comparison man/makeinfo.c @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children ac2d302a0011
comparison
equal deleted inserted replaced
-1:000000000000 0:376386a54a3c
1 /* Makeinfo -- convert texinfo format files into info files.
2
3 Copyright (C) 1987, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
4 Copyright (C) 1994, 1995 Sun Microsystems.
5 Copyright (C) 1995 Amdahl Corporation.
6
7 This file is part of GNU Info.
8
9 Makeinfo is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY. No author or distributor accepts
11 responsibility to anyone for the consequences of using it or for
12 whether it serves any particular purpose or works at all, unless he
13 says so in writing. Refer to the GNU Emacs General Public License
14 for full details.
15
16 Everyone is granted permission to copy, modify and redistribute
17 Makeinfo, but only under the conditions described in the GNU Emacs
18 General Public License. A copy of this license is supposed to
19 have been given to you along with GNU Emacs so you can know your
20 rights and responsibilities. It should be in a file named COPYING.
21 Among other things, the copyright notice and this notice must be
22 preserved on all copies. */
23
24 /* This is Makeinfo version 1.63. If you change the version number of
25 Makeinfo, please change it here and at the lines reading:
26
27 int major_version = 1;
28 int minor_version = 63;
29
30 in the code below.
31
32 Makeinfo is authored by Brian Fox (bfox@ai.mit.edu).
33 March 1994: additions by Ben Wing (wing@netcom.com).
34 */
35
36 #if defined (HAVE_CONFIG_H)
37 #include <config.h>
38 #endif /* HAVE_CONFIG_H */
39
40 /* You can change some of the behaviour of Makeinfo by changing the
41 following defines: */
42
43 /* Define INDENT_PARAGRAPHS_IN_TABLE if you want the paragraphs which
44 appear within an @table, @ftable, or @itemize environment to have
45 standard paragraph indentation. Without this, such paragraphs have
46 no starting indentation. */
47 /* #define INDENT_PARAGRAPHS_IN_TABLE */
48
49 /* Define DEFAULT_INDENTATION_INCREMENT as an integer which is the amount
50 that @example should increase indentation by. This incremement is used
51 for all insertions which indent the enclosed text. */
52 #define DEFAULT_INDENTATION_INCREMENT 5
53
54 /* Define PARAGRAPH_START_INDENT to be the amount of indentation that
55 the first lines of paragraphs receive by default, where no other
56 value has been specified. Users can change this value on the command
57 line, with the --paragraph-indent option, or within the texinfo file,
58 with the @paragraphindent command. */
59 #define PARAGRAPH_START_INDENT 3
60
61 /* Define DEFAULT_PARAGRAPH_SPACING as the number of blank lines that you
62 wish to appear between paragraphs. A value of 1 creates a single blank
63 line between paragraphs. Paragraphs are defined by 2 or more consecutive
64 newlines in the input file (i.e., one or more blank lines). */
65 #define DEFAULT_PARAGRAPH_SPACING 1
66
67 /* Define HAVE_MACROS to enable the macro facility of TeXinfo. Using this
68 facility, users can create their own command procedures with arguments. */
69 #define HAVE_MACROS
70
71 /* **************************************************************** */
72 /* */
73 /* Include File Declarations */
74 /* */
75 /* **************************************************************** */
76
77 /* Indent #pragma so that older Cpp's don't try to parse it. */
78 #if defined (_AIX)
79 # pragma alloca
80 #endif /* _AIX */
81
82 #include <stdio.h>
83 #include <unistd.h>
84 #include <sys/types.h>
85 #include <ctype.h>
86 #include <sys/stat.h>
87 #include <pwd.h>
88 #include <errno.h>
89
90 #include <stdarg.h>
91 #include "getopt.h"
92 #if defined (HAVE_UNISTD_H)
93 #include <unistd.h>
94 #endif /* HAVE_UNISTD_H */
95
96 #if defined (VMS)
97 #include <perror.h>
98 #endif
99
100 #include <stdlib.h>
101
102 #if defined (HAVE_STRING_H)
103 #include <string.h>
104 #define HAVE_STRDUP
105 #define HAVE_STRCASECMP
106 #else
107 #include <strings.h>
108 #endif /* !HAVE_STRING_H */
109
110 #if defined (TM_IN_SYS_TIME)
111 #include <sys/time.h>
112 #else
113 #include <time.h>
114 #endif /* !TM_IN_SYS_TIME */
115
116 #if defined (HAVE_SYS_FCNTL_H)
117 #include <sys/fcntl.h>
118 #else
119 #include <fcntl.h>
120 #endif /* !HAVE_SYS_FCNTL_H */
121
122 #if defined (HAVE_SYS_FILE_H)
123 #include <sys/file.h>
124 #endif /* HAVE_SYS_FILE_H */
125
126 #if defined (__GNUC__) && !defined (alloca)
127 #define alloca __builtin_alloca
128 #else
129 #if defined(HAVE_ALLOCA_H)
130 #include <alloca.h>
131 #else /* !HAVE_ALLOCA_H */
132 #if !defined (_AIX)
133 extern char *alloca ();
134 #endif /* !_AIX */
135 #endif /* !HAVE_ALLOCA_H */
136 #endif /* !__GNUC__ */
137
138 #ifdef __SUNPRO_C
139 void *__builtin_alloca (unsigned int);
140 #endif
141
142 static void *xmalloc (unsigned int), *xrealloc (void *, unsigned int);
143 #if defined (__osf__)
144 extern void *malloc (), *realloc ();
145 #endif /* __osf__ */
146
147 static char **get_brace_args (int);
148 static int array_len (char **);
149 static void free_array (char **);
150 static void isolate_nodename (char *);
151
152 /* Non-zero means that we are currently hacking the insides of an
153 insertion which would use a fixed width font. */
154 static int in_fixed_width_font = 0;
155
156 /* Non-zero means that start_paragraph () MUST be called before we pay
157 any attention to close_paragraph () calls. */
158 int must_start_paragraph = 0;
159
160 /* Non-zero means a string is in execution, as opposed to a file. */
161 static int executing_string = 0;
162
163 #if defined (HAVE_MACROS)
164 /* If non-NULL, this is an output stream to write the full macro expansion
165 of the input text to. The resultant file is another texinfo file, but
166 missing @include, @infoinclude, @macro, and macro invocations. Instead,
167 all of the text is placed within the file. */
168 FILE *macro_expansion_output_stream = (FILE *)NULL;
169
170 /* Here is a structure used to remember input text strings and offsets
171 within them. */
172 typedef struct {
173 char *pointer; /* Pointer to the input text. */
174 int offset; /* Offset of the last character output. */
175 } ITEXT;
176
177 static ITEXT **itext_info = (ITEXT **)NULL;
178 static int itext_size = 0;
179
180 /* Non-zero means to inhibit the writing of macro expansions to the output
181 stream. This is used in special cases where the output has already been
182 written. */
183 int me_inhibit_expansion = 0;
184
185 static ITEXT *remember_itext (char *, int);
186 static void forget_itext (char *);
187 static void me_append_before_this_command (void);
188 static void append_to_expansion_output (int);
189 static void write_region_to_macro_output (char *, int, int);
190 static void maybe_write_itext (char *, int);
191 static void me_execute_string (char *);
192 #endif /* HAVE_MACROS */
193
194 /* Some systems don't declare this function in pwd.h. */
195 struct passwd *getpwnam ();
196
197
198 /* **************************************************************** */
199 /* */
200 /* Global Defines */
201 /* */
202 /* **************************************************************** */
203
204 /* Error levels */
205 #define NO_ERROR 0
206 #define SYNTAX 2
207 #define FATAL 4
208
209 /* C's standard macros don't check to make sure that the characters being
210 changed are within range. So I have to check explicitly. */
211
212 /* GNU Library doesn't have toupper(). Until GNU gets this fixed, I will
213 have to do it. */
214 #ifndef toupper
215 #define toupper(c) ((c) - 32)
216 #endif
217
218 #define coerce_to_upper(c) ((islower(c) ? toupper(c) : (c)))
219 #define coerce_to_lower(c) ((isupper(c) ? tolower(c) : (c)))
220
221 #define control_character_bit 0x40 /* %01000000, must be off. */
222 #define meta_character_bit 0x080/* %10000000, must be on. */
223 #define CTL(c) ((c) & (~control_character_bit))
224 #define UNCTL(c) coerce_to_upper(((c)|control_character_bit))
225 #define META(c) ((c) | (meta_character_bit))
226 #define UNMETA(c) ((c) & (~meta_character_bit))
227
228 #define whitespace(c) (((c) == '\t') || ((c) == ' '))
229 #define sentence_ender(c) ((c) == '.' || (c) == '?' || (c) == '!')
230 #define cr_or_whitespace(c) (((c) == '\t') || ((c) == ' ') || ((c) == '\n'))
231
232 #ifndef isletter
233 #define isletter(c) (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z'))
234 #endif
235
236 #ifndef isupper
237 #define isupper(c) ((c) >= 'A' && (c) <= 'Z')
238 #endif
239
240 #ifndef isdigit
241 #define isdigit(c) ((c) >= '0' && (c) <= '9')
242 #endif
243
244 #ifndef digit_value
245 #define digit_value(c) ((c) - '0')
246 #endif
247
248 #define member(c, s) (strchr (s, c) != NULL)
249
250 #define COMMAND_PREFIX '@'
251
252 /* Stuff for splitting large files. */
253 #define SPLIT_SIZE_THRESHOLD 70000 /* What's good enough for Stallman... */
254 #define DEFAULT_SPLIT_SIZE 50000 /* Is probably good enough for me. */
255 int splitting = 1; /* Always true for now. */
256
257 typedef void COMMAND_FUNCTION (); /* So I can say COMMAND_FUNCTION *foo; */
258
259
260 /* **************************************************************** */
261 /* */
262 /* Global Variables */
263 /* */
264 /* **************************************************************** */
265
266 /* Global pointer to argv[0]. */
267 char *progname;
268
269 /* The current input file state. */
270 char *input_filename;
271 char *input_text;
272 int size_of_input_text;
273 int input_text_offset;
274 int line_number;
275
276 #define curchar() input_text[input_text_offset]
277
278 #define command_char(c) ((!whitespace(c)) && \
279 ((c) != '\n') && \
280 ((c) != '{') && \
281 ((c) != '}') && \
282 ((c) != '='))
283
284 #define skip_whitespace() while (input_text_offset != size_of_input_text \
285 && whitespace(curchar()))\
286 input_text_offset++
287
288 #define skip_whitespace_and_newlines() \
289 do { \
290 while (input_text_offset != size_of_input_text \
291 && (whitespace (curchar ()) \
292 || (curchar () == '\n'))) \
293 { \
294 if (curchar () == '\n') \
295 line_number++; \
296 input_text_offset++; \
297 } \
298 } while (0)
299
300 /* Return non-zero if STRING is the text at input_text + input_text_offset,
301 else zero. */
302 #define looking_at(string) \
303 (strncmp (input_text + input_text_offset, string, strlen (string)) == 0)
304
305 /* And writing to the output. */
306
307 /* The output file name. */
308 char *output_filename = (char *)NULL;
309 char *pretty_output_filename;
310
311 /* Name of the output file that the user elected to pass on the command line.
312 Such a name overrides any name found with the @setfilename command. */
313 char *command_output_filename = (char *)NULL;
314
315 /* A colon separated list of directories to search for files included
316 with @include. This can be controlled with the `-I' option to makeinfo. */
317 char *include_files_path = (char *)NULL;
318
319 /* Current output stream. */
320 FILE *output_stream;
321
322 /* Position in the output file. */
323 int output_position;
324
325 /* Output paragraph buffer. */
326 unsigned char *output_paragraph;
327
328 /* Offset into OUTPUT_PARAGRAPH. */
329 int output_paragraph_offset;
330
331 /* The output paragraph "cursor" horizontal position. */
332 int output_column = 0;
333
334 /* Non-zero means output_paragraph contains text. */
335 int paragraph_is_open = 0;
336
337 #define INITIAL_PARAGRAPH_SPACE 5000
338 int paragraph_buffer_len = INITIAL_PARAGRAPH_SPACE;
339
340 /* Filling.. */
341 /* Non-zero indicates that filling will take place on long lines. */
342 int filling_enabled = 1;
343
344 /* Non-zero means that words are not to be split, even in long lines. This
345 gets changed for cm_w (). */
346 int non_splitting_words = 0;
347
348 /* Non-zero indicates that filling a line also indents the new line. */
349 int indented_fill = 0;
350
351 /* The column at which long lines are broken. */
352 int fill_column = 72;
353
354 /* The amount of indentation to apply at the start of each line. */
355 int current_indent = 0;
356
357 /* The amount of indentation to add at the starts of paragraphs.
358 0 means don't change existing indentation at paragraph starts.
359 > 0 is amount to indent new paragraphs by.
360 < 0 means indent to column zero by removing indentation if necessary.
361
362 This is normally zero, but some people prefer paragraph starts to be
363 somewhat more indented than paragraph bodies. A pretty value for
364 this is 3. */
365 int paragraph_start_indent = PARAGRAPH_START_INDENT;
366
367 /* Non-zero means that the use of paragraph_start_indent is inhibited.
368 @example uses this to line up the left columns of the example text.
369 A negative value for this variable is incremented each time it is used.
370 @noindent uses this to inhibit indentation for a single paragraph. */
371 int inhibit_paragraph_indentation = 0;
372
373 /* Indentation that is pending insertion. We have this for hacking lines
374 which look blank, but contain whitespace. We want to treat those as
375 blank lines. */
376 int pending_indent = 0;
377
378 /* The amount that indentation increases/decreases by. */
379 int default_indentation_increment = DEFAULT_INDENTATION_INCREMENT;
380
381 /* Non-zero indicates that indentation is temporarily turned off. */
382 int no_indent = 1;
383
384 /* Non-zero means forcing output text to be flushright. */
385 int force_flush_right = 0;
386
387 /* Non-zero means that the footnote style for this document was set on
388 the command line, which overrides any other settings. */
389 int footnote_style_preset = 0;
390
391 /* Non-zero means that we automatically number footnotes that have no
392 specified marker. */
393 int number_footnotes = 1;
394
395 /* The current footnote number in this node. Each time a new node is
396 started this is reset to 1. */
397 int current_footnote_number = 1;
398
399 /* Command name in the process of being hacked. */
400 char *command;
401
402 /* The index in our internal command table of the currently
403 executing command. */
404 int command_index;
405
406 /* A search string which is used to find a line defining a node. */
407 char node_search_string[] =
408 { '\n', COMMAND_PREFIX, 'n', 'o', 'd', 'e', ' ', '\0' };
409
410 /* A search string which is used to find a line defining a menu. */
411 char menu_search_string[] =
412 { '\n', COMMAND_PREFIX, 'm', 'e', 'n', 'u', '\0' };
413
414 /* A search string which is used to find the first @setfilename. */
415 char setfilename_search[] =
416 { COMMAND_PREFIX,
417 's', 'e', 't', 'f', 'i', 'l', 'e', 'n', 'a', 'm', 'e', '\0' };
418
419 /* A stack of file information records. If a new file is read in with
420 "@input", we remember the old input file state on this stack. */
421 typedef struct fstack
422 {
423 struct fstack *next;
424 char *filename;
425 char *text;
426 int size;
427 int offset;
428 int line_number;
429 } FSTACK;
430
431 FSTACK *filestack = (FSTACK *) NULL;
432
433 /* Stuff for nodes. */
434 /* The current nodes node name. */
435 char *current_node = (char *)NULL;
436
437 /* The current nodes section level. */
438 int current_section = 0;
439
440 /* The filename of the current input file. This is never freed. */
441 char *node_filename = (char *)NULL;
442
443 /* What we remember for each node. */
444 typedef struct tentry
445 {
446 struct tentry *next_ent;
447 char *node; /* name of this node. */
448 char *prev; /* name of "Prev:" for this node. */
449 char *next; /* name of "Next:" for this node. */
450 char *up; /* name of "Up:" for this node. */
451 int position; /* output file position of this node. */
452 int line_no; /* defining line in source file. */
453 char *filename; /* The file that this node was found in. */
454 int touched; /* non-zero means this node has been referenced. */
455 int flags; /* Room for growth. Right now, contains 1 bit. */
456 } TAG_ENTRY;
457
458 /* If node-a has a "Next" for node-b, but node-b has no "Prev" for node-a,
459 we turn on this flag bit in node-b's tag entry. This means that when
460 it is time to validate node-b, we don't report an additional error
461 if there was no "Prev" field. */
462 #define PREV_ERROR 0x1
463 #define NEXT_ERROR 0x2
464 #define UP_ERROR 0x4
465 #define NO_WARN 0x8
466 #define IS_TOP 0x10
467
468 TAG_ENTRY *tag_table = (TAG_ENTRY *) NULL;
469
470 #if defined (HAVE_MACROS)
471 #define ME_RECURSE 0x01
472 #define ME_QUOTE_ARG 0x02
473
474 /* Macro definitions for user-defined commands. */
475 typedef struct {
476 char *name; /* Name of the macro. */
477 char **arglist; /* Args to replace when executing. */
478 char *body; /* Macro body. */
479 char *source_file; /* File where this macro is defined. */
480 int source_lineno; /* Line number within FILENAME. */
481 int inhibited; /* Non-zero means make find_macro () fail. */
482 int flags; /* ME_RECURSE, ME_QUOTE_ARG, etc. */
483 } MACRO_DEF;
484
485 static void add_macro (char *name, char **arglist, char *body,
486 char *source_file, int source_lineno, int flags);
487 static void execute_macro (MACRO_DEF *);
488 static MACRO_DEF *find_macro (char *), *delete_macro (char *);
489 #endif /* HAVE_MACROS */
490
491 /* Menu reference, *note reference, and validation hacking. */
492
493 /* The various references that we know about. */
494 enum reftype
495 {
496 menu_reference, followed_reference
497 };
498
499 /* A structure to remember references with. A reference to a node is
500 either an entry in a menu, or a cross-reference made with [px]ref. */
501 typedef struct node_ref
502 {
503 struct node_ref *next;
504 char *node; /* Name of node referred to. */
505 char *containing_node; /* Name of node containing this reference. */
506 int line_no; /* Line number where the reference occurs. */
507 int section; /* Section level where the reference occurs. */
508 char *filename; /* Name of file where the reference occurs. */
509 enum reftype type; /* Type of reference, either menu or note. */
510 } NODE_REF;
511
512 /* The linked list of such structures. */
513 NODE_REF *node_references = (NODE_REF *) NULL;
514
515 /* Flag which tells us whether to examine menu lines or not. */
516 int in_menu = 0;
517
518 /* Non-zero means that we have seen "@top" once already. */
519 int top_node_seen = 0;
520
521 /* Non-zero means that we have seen a non-"@top" node already. */
522 int non_top_node_seen = 0;
523
524 /* Flags controlling the operation of the program. */
525
526 /* Default is to notify users of bad choices. */
527 int print_warnings = 1;
528
529 /* Default is to check node references. */
530 int validating = 1;
531
532 /* Non-zero means do not output "Node: Foo" for node separations. */
533 int no_headers = 0;
534
535 /* Number of errors that we tolerate on a given fileset. */
536 int max_error_level = 100;
537
538 /* Maximum number of references to a single node before complaining. */
539 int reference_warning_limit = 1000;
540
541 /* Non-zero means print out information about what is going on when it
542 is going on. */
543 int verbose_mode = 0;
544
545 /* Non-zero means to be relaxed about the input file. This is useful when
546 we can successfully format the input, but it doesn't strictly match our
547 somewhat pedantic ideas of correctness. Right now, it affects what
548 @table and @itemize do without arguments. */
549 int allow_lax_format = 0;
550
551 /* Count of @ifinfo commands seen. @ifinfo is handled specially
552 to allow non-hierarchical contstructions like
553
554 @ifinfo
555 @table foo
556 @end ifinfo
557 */
558 int ifinfo_count = 0;
559
560 /* The list of commands that we hack in texinfo. Each one
561 has an associated function. When the command is encountered in the
562 text, the associated function is called with START as the argument.
563 If the function expects arguments in braces, it remembers itself on
564 the stack. When the corresponding close brace is encountered, the
565 function is called with END as the argument. */
566
567 #define START 0
568 #define END 1
569
570 typedef struct brace_element
571 {
572 struct brace_element *next;
573 COMMAND_FUNCTION *proc;
574 int pos, line;
575 } BRACE_ELEMENT;
576
577 BRACE_ELEMENT *brace_stack = (BRACE_ELEMENT *) NULL;
578
579 /* Forward declarations. */
580 #if !defined (HAVE_STRDUP)
581 extern char *strdup ();
582 #endif /* HAVE_STRDUP */
583
584 static void convert_from_stream (FILE *, char *);
585 static void convert_from_file (char *);
586 static void top_defindex (char *, int);
587
588 static void insert_self (), cm_ignore_line (void);
589
590 static void
591 cm_asterisk (void), cm_dots (int), cm_bullet (int), cm_TeX (int),
592 cm_copyright (int), cm_code (int), cm_samp (int), cm_file (int),
593 cm_kbd (int), cm_key (int), cm_ctrl (int, int, int), cm_var (int, int, int),
594 cm_dfn (int, int), cm_emph (int), cm_strong (int, int), cm_cite (int, int),
595 cm_italic (int, int, int), cm_bold (int, int, int), cm_roman (int, int, int),
596 cm_title (int, int, int), cm_w (int, int, int), cm_refill (void),
597 cm_titlefont (int, int, int);
598
599 static void
600 cm_chapter (void), cm_unnumbered (void), cm_appendix (void), cm_top (void),
601 cm_section (void), cm_unnumberedsec (void), cm_appendixsec (void),
602 cm_subsection (void), cm_unnumberedsubsec (void), cm_appendixsubsec (void),
603 cm_subsubsection (void), cm_unnumberedsubsubsec (void),
604 cm_appendixsubsubsec (void), cm_heading (void), cm_chapheading (void),
605 cm_subheading (void), cm_subsubheading (void), cm_majorheading (void),
606 cm_raisesections (void), cm_lowersections (void);
607
608 /* All @defxxx commands map to cm_defun (). */
609 static void cm_defun (void);
610
611 static void
612 cm_node (void), cm_menu (void), cm_xref (int), cm_ftable (void),
613 cm_vtable (void), cm_pxref (int), cm_inforef (int), cm_quotation (void),
614 cm_display (void), cm_itemize (void), cm_enumerate (void), cm_table (void),
615 cm_itemx (void), cm_noindent (void), cm_setfilename (void), cm_br (void),
616 cm_sp (void), cm_group (void), cm_center (void), cm_include (void),
617 cm_bye (void), cm_item (void), cm_end (void), cm_infoinclude (void),
618 cm_ifinfo (void), cm_kindex (void), cm_cindex (void), cm_findex (void),
619 cm_pindex (void), cm_vindex (void), cm_tindex (void), cm_asis (void),
620 cm_synindex (void), cm_printindex (void), cm_minus (int),
621 cm_footnote (void), cm_force_abbreviated_whitespace (void),
622 cm_example (void), cm_smallexample (void), cm_lisp (void), cm_format (void),
623 cm_exdent (void), cm_defindex (void), cm_defcodeindex (void),
624 cm_sc (int, int, int), cm_result (int), cm_expansion (int), cm_equiv (int),
625 cm_print (int), cm_error (int), cm_point (int), cm_today (int),
626 cm_flushleft (void), cm_flushright (void), cm_smalllisp (void),
627 cm_math (void), cm_cartouche (void), cm_ignore_sentence_ender (void);
628
629 /* Conditionals. */
630 static void cm_set (void), cm_clear (void), cm_ifset (void), cm_ifclear (void);
631 static void cm_value (int, int, int), cm_ifeq (void);
632
633 #if defined (HAVE_MACROS)
634 /* Define a user-defined command which is simple substitution. */
635 static void cm_macro (void), cm_unmacro (void);
636 #endif /* HAVE_MACROS */
637
638 /* Options. */
639 static void cm_paragraphindent (void), cm_footnotestyle (void);
640
641 /* Internals. */
642 static void do_nothing (void);
643 static void command_name_condition (void);
644 static void insert_self (void);
645 static void misplaced_brace (void);
646 static void cm_obsolete (int arg, int start, int end);
647
648 static int error (char *, ...);
649 static int line_error (char *, ...);
650 static int warning (char *, ...);
651 static void add_word_args (char *, ...);
652 static void execute_string (char *, ...);
653
654 static void print_version_info (void);
655 static void memory_error (char *callers_name, int bytes_wanted);
656 static void usage (FILE *stream, int exit_value);
657 static void push_node_filename (void);
658 static void pop_node_filename (void);
659 static void remember_error (void);
660 static void init_internals (void);
661 static void init_paragraph (void);
662 static void reader_loop (void);
663 static void read_command (void);
664 static void init_brace_stack (void);
665 static void remember_brace (COMMAND_FUNCTION *proc);
666 static void remember_brace_1 (COMMAND_FUNCTION *proc, int position);
667 static void discard_braces (void);
668 static void add_word (char *string);
669 static void add_char (int character);
670 static void insert (int character);
671 static void flush_output (void);
672 static void close_paragraph_with_lines (int lines);
673 static void close_paragraph (void);
674 static void ignore_blank_line (void);
675 static void do_flush_right_indentation (void);
676 static void start_paragraph (void);
677 static void indent (int amount);
678 static void init_insertion_stack (void);
679 static void init_tag_table (void);
680 static void write_tag_table (void);
681 static void write_tag_table_internal (int indirect_p);
682 static void normalize_node_name (char *string);
683 static void validate_file (TAG_ENTRY *tag_tayble);
684 static void split_file (char *filename, int size);
685 static void validate_other_references (register NODE_REF *ref_list);
686 static NODE_REF *find_node_reference (char *node, register NODE_REF *ref_list);
687 static void do_enumeration (int type, char *default_string);
688 static void handle_variable (int action);
689 static void handle_variable_internal (int action, char *name);
690 static void init_indices (void);
691 static void undefindex (char *name);
692 static void defindex (char *name, int code);
693 static void gen_defindex (int code);
694 static void define_user_command (char *name, COMMAND_FUNCTION *proc,
695 int needs_braces_p);
696 static void convert_from_loaded_file (char *name);
697 static void free_pending_notes (void);
698 static void output_pending_notes (void);
699 static void free_node_references (void);
700 static int set_paragraph_indent (char *string);
701 static int set_footnote_style (char *string);
702 static int self_delimiting (int character);
703 static int search_forward (char *string, int from);
704 static int validate (char *tag, int line, char *label);
705 static void pop_and_call_brace (void);
706 static char *expand_filename (char *filename, char *input_name);
707 static char *filename_part (char *filename);
708 static char *full_pathname (char *filename);
709 static char *get_file_info_in_path (char *filename, char *path,
710 struct stat *finfo);
711 static char *glean_node_from_menu (int remember_reference);
712
713 typedef struct
714 {
715 char *name;
716 COMMAND_FUNCTION *proc;
717 int argument_in_braces;
718 } COMMAND;
719
720 /* Stuff for defining commands on the fly. */
721 COMMAND **user_command_array = (COMMAND **) NULL;
722 int user_command_array_len = 0;
723
724 #define NO_BRACE_ARGS 0
725 #define BRACE_ARGS 1
726
727 static COMMAND CommandTable[] = {
728 { "!", cm_ignore_sentence_ender, NO_BRACE_ARGS },
729 { "'", insert_self, NO_BRACE_ARGS },
730 { "*", cm_asterisk, NO_BRACE_ARGS },
731 { ".", cm_ignore_sentence_ender, NO_BRACE_ARGS },
732 { ":", cm_force_abbreviated_whitespace, NO_BRACE_ARGS },
733 { "?", cm_ignore_sentence_ender, NO_BRACE_ARGS },
734 { "|", do_nothing, NO_BRACE_ARGS },
735 { "@", insert_self, NO_BRACE_ARGS },
736 { " ", insert_self, NO_BRACE_ARGS },
737 { "\n", insert_self, NO_BRACE_ARGS },
738 { "TeX", cm_TeX, BRACE_ARGS },
739 { "`", insert_self, NO_BRACE_ARGS },
740 { "appendix", cm_appendix, NO_BRACE_ARGS },
741 { "appendixsection", cm_appendixsec, NO_BRACE_ARGS },
742 { "appendixsec", cm_appendixsec, NO_BRACE_ARGS },
743 { "appendixsubsec", cm_appendixsubsec, NO_BRACE_ARGS },
744 { "appendixsubsubsec", cm_appendixsubsubsec, NO_BRACE_ARGS },
745 { "asis", cm_asis, BRACE_ARGS },
746 { "b", cm_bold, BRACE_ARGS },
747 { "br", cm_br, NO_BRACE_ARGS },
748 { "bullet", cm_bullet, BRACE_ARGS },
749 { "bye", cm_bye, NO_BRACE_ARGS },
750 { "c", cm_ignore_line, NO_BRACE_ARGS },
751 { "cartouche", cm_cartouche, NO_BRACE_ARGS },
752 { "center", cm_center, NO_BRACE_ARGS },
753 { "chapheading", cm_chapheading, NO_BRACE_ARGS },
754 { "chapter", cm_chapter, NO_BRACE_ARGS },
755 { "cindex", cm_cindex, NO_BRACE_ARGS },
756 { "cite", cm_cite, BRACE_ARGS },
757 { "clear", cm_clear, NO_BRACE_ARGS },
758 { "code", cm_code, BRACE_ARGS },
759 { "comment", cm_ignore_line, NO_BRACE_ARGS },
760 { "contents", do_nothing, NO_BRACE_ARGS },
761 { "copyright", cm_copyright, BRACE_ARGS },
762 { "ctrl", cm_ctrl, BRACE_ARGS },
763 { "defcodeindex", cm_defcodeindex, NO_BRACE_ARGS },
764 { "defindex", cm_defindex, NO_BRACE_ARGS },
765 { "dfn", cm_dfn, BRACE_ARGS },
766
767 /* The `def' commands. */
768 { "deffn", cm_defun, NO_BRACE_ARGS },
769 { "deffnx", cm_defun, NO_BRACE_ARGS },
770 { "defun", cm_defun, NO_BRACE_ARGS },
771 { "defunx", cm_defun, NO_BRACE_ARGS },
772 { "defmac", cm_defun, NO_BRACE_ARGS },
773 { "defmacx", cm_defun, NO_BRACE_ARGS },
774 { "defspec", cm_defun, NO_BRACE_ARGS },
775 { "defspecx", cm_defun, NO_BRACE_ARGS },
776 { "defvr", cm_defun, NO_BRACE_ARGS },
777 { "defvrx", cm_defun, NO_BRACE_ARGS },
778 { "defvar", cm_defun, NO_BRACE_ARGS },
779 { "defvarx", cm_defun, NO_BRACE_ARGS },
780 { "defopt", cm_defun, NO_BRACE_ARGS },
781 { "defoptx", cm_defun, NO_BRACE_ARGS },
782 { "deftypefn", cm_defun, NO_BRACE_ARGS },
783 { "deftypefnx", cm_defun, NO_BRACE_ARGS },
784 { "deftypefun", cm_defun, NO_BRACE_ARGS },
785 { "deftypefunx", cm_defun, NO_BRACE_ARGS },
786 { "deftypevr", cm_defun, NO_BRACE_ARGS },
787 { "deftypevrx", cm_defun, NO_BRACE_ARGS },
788 { "deftypevar", cm_defun, NO_BRACE_ARGS },
789 { "deftypevarx", cm_defun, NO_BRACE_ARGS },
790 { "defcv", cm_defun, NO_BRACE_ARGS },
791 { "defcvx", cm_defun, NO_BRACE_ARGS },
792 { "defivar", cm_defun, NO_BRACE_ARGS },
793 { "defivarx", cm_defun, NO_BRACE_ARGS },
794 { "defop", cm_defun, NO_BRACE_ARGS },
795 { "defopx", cm_defun, NO_BRACE_ARGS },
796 { "defmethod", cm_defun, NO_BRACE_ARGS },
797 { "defmethodx", cm_defun, NO_BRACE_ARGS },
798 { "deftypemethod", cm_defun, NO_BRACE_ARGS },
799 { "deftypemethodx", cm_defun, NO_BRACE_ARGS },
800 { "deftp", cm_defun, NO_BRACE_ARGS },
801 { "deftpx", cm_defun, NO_BRACE_ARGS },
802 /* The end of the `def' commands. */
803
804 { "display", cm_display, NO_BRACE_ARGS },
805 { "dots", cm_dots, BRACE_ARGS },
806 { "dmn", do_nothing, BRACE_ARGS },
807 { "emph", cm_emph, BRACE_ARGS },
808 { "end", cm_end, NO_BRACE_ARGS },
809 { "enumerate", cm_enumerate, NO_BRACE_ARGS },
810 { "equiv", cm_equiv, BRACE_ARGS },
811 { "error", cm_error, BRACE_ARGS },
812 { "example", cm_example, NO_BRACE_ARGS },
813 { "exdent", cm_exdent, NO_BRACE_ARGS },
814 { "expansion", cm_expansion, BRACE_ARGS },
815 { "file", cm_file, BRACE_ARGS },
816 { "findex", cm_findex, NO_BRACE_ARGS },
817 { "finalout", do_nothing, NO_BRACE_ARGS },
818 { "flushleft", cm_flushleft, NO_BRACE_ARGS },
819 { "flushright", cm_flushright, NO_BRACE_ARGS },
820 { "format", cm_format, NO_BRACE_ARGS },
821 { "ftable", cm_ftable, NO_BRACE_ARGS },
822 { "group", cm_group, NO_BRACE_ARGS },
823 { "heading", cm_heading, NO_BRACE_ARGS },
824 { "headings", cm_ignore_line, NO_BRACE_ARGS },
825 { "i", cm_italic, BRACE_ARGS },
826 { "iappendix", cm_appendix, NO_BRACE_ARGS },
827 { "iappendixsection", cm_appendixsec, NO_BRACE_ARGS },
828 { "iappendixsec", cm_appendixsec, NO_BRACE_ARGS },
829 { "iappendixsubsec", cm_appendixsubsec, NO_BRACE_ARGS },
830 { "iappendixsubsubsec", cm_appendixsubsubsec, NO_BRACE_ARGS },
831 { "ichapter", cm_chapter, NO_BRACE_ARGS },
832 { "ifclear", cm_ifclear, NO_BRACE_ARGS },
833 { "ifeq", cm_ifeq, NO_BRACE_ARGS },
834 { "ifhtml", command_name_condition, NO_BRACE_ARGS },
835 { "ifinfo", cm_ifinfo, NO_BRACE_ARGS },
836 { "ifset", cm_ifset, NO_BRACE_ARGS },
837 { "iftex", command_name_condition, NO_BRACE_ARGS },
838 { "ignore", command_name_condition, NO_BRACE_ARGS },
839 { "include", cm_include, NO_BRACE_ARGS },
840 { "inforef", cm_inforef, BRACE_ARGS },
841 { "input", cm_include, NO_BRACE_ARGS },
842 { "isection", cm_section, NO_BRACE_ARGS },
843 { "isubsection", cm_subsection, NO_BRACE_ARGS },
844 { "isubsubsection", cm_subsubsection, NO_BRACE_ARGS },
845 { "item", cm_item, NO_BRACE_ARGS },
846 { "itemize", cm_itemize, NO_BRACE_ARGS },
847 { "itemx", cm_itemx, NO_BRACE_ARGS },
848 { "iunnumbered", cm_unnumbered, NO_BRACE_ARGS },
849 { "iunnumberedsec", cm_unnumberedsec, NO_BRACE_ARGS },
850 { "iunnumberedsubsec", cm_unnumberedsubsec, NO_BRACE_ARGS },
851 { "iunnumberedsubsubsec", cm_unnumberedsubsubsec, NO_BRACE_ARGS },
852 { "kbd", cm_kbd, BRACE_ARGS },
853 { "key", cm_key, BRACE_ARGS },
854 { "kindex", cm_kindex, NO_BRACE_ARGS },
855 { "lowersections", cm_lowersections, NO_BRACE_ARGS },
856 { "lisp", cm_lisp, NO_BRACE_ARGS },
857 #if defined (HAVE_MACROS)
858 { "macro", cm_macro, NO_BRACE_ARGS },
859 #endif
860 { "majorheading", cm_majorheading, NO_BRACE_ARGS },
861 { "math", cm_math, BRACE_ARGS },
862 { "medbreak", cm_br, NO_BRACE_ARGS },
863 { "menu", cm_menu, NO_BRACE_ARGS },
864 { "minus", cm_minus, BRACE_ARGS },
865 { "need", cm_ignore_line, NO_BRACE_ARGS },
866 { "node", cm_node, NO_BRACE_ARGS },
867 { "noindent", cm_noindent, NO_BRACE_ARGS },
868 { "nwnode", cm_node, NO_BRACE_ARGS },
869 { "overfullrule", cm_ignore_line, NO_BRACE_ARGS },
870 { "page", do_nothing, NO_BRACE_ARGS },
871 { "pindex", cm_pindex, NO_BRACE_ARGS },
872 { "point", cm_point, BRACE_ARGS },
873 { "print", cm_print, BRACE_ARGS },
874 { "printindex", cm_printindex, NO_BRACE_ARGS },
875 { "pxref", cm_pxref, BRACE_ARGS },
876 { "quotation", cm_quotation, NO_BRACE_ARGS },
877 { "r", cm_roman, BRACE_ARGS },
878 { "raisesections", cm_raisesections, NO_BRACE_ARGS },
879 { "ref", cm_xref, BRACE_ARGS },
880 { "refill", cm_refill, NO_BRACE_ARGS },
881 { "result", cm_result, BRACE_ARGS },
882 { "samp", cm_samp, BRACE_ARGS },
883 { "sc", cm_sc, BRACE_ARGS },
884 { "section", cm_section, NO_BRACE_ARGS },
885 { "set", cm_set, NO_BRACE_ARGS },
886 { "setchapternewpage", cm_ignore_line, NO_BRACE_ARGS },
887 { "setchapterstyle", cm_ignore_line, NO_BRACE_ARGS },
888 { "setfilename", cm_setfilename, NO_BRACE_ARGS },
889 { "settitle", cm_ignore_line, NO_BRACE_ARGS },
890 { "shortcontents", do_nothing, NO_BRACE_ARGS },
891 { "shorttitlepage", cm_ignore_line, NO_BRACE_ARGS },
892 { "smallbook", cm_ignore_line, NO_BRACE_ARGS },
893 { "smallbreak", cm_br, NO_BRACE_ARGS },
894 { "smallexample", cm_smallexample, NO_BRACE_ARGS },
895 { "smalllisp", cm_smalllisp, NO_BRACE_ARGS },
896 { "sp", cm_sp, NO_BRACE_ARGS },
897 { "strong", cm_strong, BRACE_ARGS },
898 { "subheading", cm_subheading, NO_BRACE_ARGS },
899 { "subsection", cm_subsection, NO_BRACE_ARGS },
900 { "subsubheading", cm_subsubheading, NO_BRACE_ARGS },
901 { "subsubsection", cm_subsubsection, NO_BRACE_ARGS },
902 { "summarycontents", do_nothing, NO_BRACE_ARGS },
903 { "syncodeindex", cm_synindex, NO_BRACE_ARGS },
904 { "synindex", cm_synindex, NO_BRACE_ARGS },
905 { "t", cm_title, BRACE_ARGS },
906 { "table", cm_table, NO_BRACE_ARGS },
907 { "tex", command_name_condition, NO_BRACE_ARGS },
908 { "tindex", cm_tindex, NO_BRACE_ARGS },
909 { "titlefont", cm_titlefont, BRACE_ARGS },
910 { "titlepage", command_name_condition, NO_BRACE_ARGS },
911 { "titlespec", command_name_condition, NO_BRACE_ARGS },
912 { "today", cm_today, BRACE_ARGS },
913 { "top", cm_top, NO_BRACE_ARGS },
914 #if defined (HAVE_MACROS)
915 { "unmacro", cm_unmacro, NO_BRACE_ARGS },
916 #endif
917 { "unnumbered", cm_unnumbered, NO_BRACE_ARGS },
918 { "unnumberedsec", cm_unnumberedsec, NO_BRACE_ARGS },
919 { "unnumberedsubsec", cm_unnumberedsubsec, NO_BRACE_ARGS },
920 { "unnumberedsubsubsec", cm_unnumberedsubsubsec, NO_BRACE_ARGS },
921 { "value", cm_value, BRACE_ARGS },
922 { "var", cm_var, BRACE_ARGS },
923 { "vindex", cm_vindex, NO_BRACE_ARGS },
924 { "vtable", cm_vtable, NO_BRACE_ARGS },
925 { "w", cm_w, BRACE_ARGS },
926 { "xref", cm_xref, BRACE_ARGS },
927 { "{", insert_self, NO_BRACE_ARGS },
928 { "}", insert_self, NO_BRACE_ARGS },
929
930 /* Some obsoleted commands. */
931 { "infotop", cm_obsolete, NO_BRACE_ARGS },
932 { "infounnumbered", cm_obsolete, NO_BRACE_ARGS },
933 { "infounnumberedsec", cm_obsolete, NO_BRACE_ARGS },
934 { "infounnumberedsubsec", cm_obsolete, NO_BRACE_ARGS },
935 { "infounnumberedsubsubsec", cm_obsolete, NO_BRACE_ARGS },
936 { "infoappendix", cm_obsolete, NO_BRACE_ARGS },
937 { "infoappendixsec", cm_obsolete, NO_BRACE_ARGS },
938 { "infoappendixsubsec", cm_obsolete, NO_BRACE_ARGS },
939 { "infoappendixsubsubsec", cm_obsolete, NO_BRACE_ARGS },
940 { "infochapter", cm_obsolete, NO_BRACE_ARGS },
941 { "infosection", cm_obsolete, NO_BRACE_ARGS },
942 { "infosubsection", cm_obsolete, NO_BRACE_ARGS },
943 { "infosubsubsection", cm_obsolete, NO_BRACE_ARGS },
944
945 /* Now @include does what this was supposed to. */
946 { "infoinclude", cm_infoinclude, NO_BRACE_ARGS },
947 { "footnote", cm_footnote, NO_BRACE_ARGS}, /* self-arg eater */
948 { "footnotestyle", cm_footnotestyle, NO_BRACE_ARGS },
949 { "paragraphindent", cm_paragraphindent, NO_BRACE_ARGS },
950
951 {(char *) NULL, (COMMAND_FUNCTION *) NULL, NO_BRACE_ARGS}};
952
953 int major_version = 1;
954 int minor_version = 63;
955
956 struct option long_options[] =
957 {
958 { "error-limit", 1, 0, 'e' }, /* formerly -el */
959 { "fill-column", 1, 0, 'f' }, /* formerly -fc */
960 { "footnote-style", 1, 0, 's' }, /* formerly -ft */
961 { "no-headers", 0, &no_headers, 1 }, /* Do not output Node: foo */
962 { "no-pointer-validate", 0, &validating, 0 }, /* formerly -nv */
963 { "no-validate", 0, &validating, 0 }, /* formerly -nv */
964 { "no-split", 0, &splitting, 0 }, /* formerly -ns */
965 { "no-warn", 0, &print_warnings, 0 }, /* formerly -nw */
966 #if defined (HAVE_MACROS)
967 { "macro-expand", 1, 0, 'E' },
968 #endif /* HAVE_MACROS */
969 { "number-footnotes", 0, &number_footnotes, 1 },
970 { "no-number-footnotes", 0, &number_footnotes, 0 },
971 { "output", 1, 0, 'o' },
972 { "paragraph-indent", 1, 0, 'p' }, /* formerly -pi */
973 { "reference-limit", 1, 0, 'r' }, /* formerly -rl */
974 { "verbose", 0, &verbose_mode, 1 }, /* formerly -verbose */
975 { "help", 0, 0, 'h' },
976 { "version", 0, 0, 'V' },
977 {NULL, 0, NULL, 0}
978 };
979
980 /* Values for calling handle_variable_internal (). */
981 #define SET 1
982 #define CLEAR 2
983 #define IFSET 3
984 #define IFCLEAR 4
985
986 /* **************************************************************** */
987 /* */
988 /* Main () Start of code */
989 /* */
990 /* **************************************************************** */
991
992 /* For each file mentioned in the command line, process it, turning
993 texinfo commands into wonderfully formatted output text. */
994 int
995 main (int argc, char **argv)
996 {
997 extern int errors_printed;
998 int c, ind;
999 int reading_from_stdin = 0;
1000
1001 /* The name of this program is the last filename in argv[0]. */
1002 progname = filename_part (argv[0]);
1003
1004 /* Parse argument flags from the input line. */
1005 while ((c = getopt_long
1006 (argc, argv,
1007 #if defined (HAVE_MACROS)
1008 "D:E:U:I:f:o:p:e:r:s:V",
1009 #else
1010 "D:U:I:f:o:p:e:r:s:V",
1011 #endif /* !HAVE_MACROS */
1012 long_options, &ind))
1013 != EOF)
1014 {
1015 if (c == 0 && long_options[ind].flag == 0)
1016 c = long_options[ind].val;
1017
1018 switch (c)
1019 {
1020 /* User specified variable to set or clear? */
1021 case 'D':
1022 case 'U':
1023 handle_variable_internal ((c == 'D') ? SET : CLEAR, optarg);
1024 break;
1025
1026 #if defined (HAVE_MACROS)
1027 /* Use specified a macro expansion output file? */
1028 case 'E':
1029 if (!macro_expansion_output_stream)
1030 {
1031 macro_expansion_output_stream = fopen (optarg, "w");
1032 if (!macro_expansion_output_stream)
1033 error ("Couldn't open macro expansion output \"%s\"", optarg);
1034 }
1035 else
1036 error ("Cannot specify more than one macro expansion output");
1037 break;
1038 #endif /* HAVE_MACROS */
1039
1040 /* User specified include file path? */
1041 case 'I':
1042 if (!include_files_path)
1043 include_files_path = strdup (".");
1044
1045 include_files_path = (char *)
1046 xrealloc (include_files_path,
1047 2 + strlen (include_files_path) + strlen (optarg));
1048 strcat (include_files_path, ":");
1049 strcat (include_files_path, optarg);
1050 break;
1051
1052 /* User specified fill_column? */
1053 case 'f':
1054 if (sscanf (optarg, "%d", &fill_column) != 1)
1055 usage (stderr, FATAL);
1056 break;
1057
1058 /* User specified output file? */
1059 case 'o':
1060 command_output_filename = strdup (optarg);
1061 break;
1062
1063 /* User specified paragraph indent (paragraph_start_index)? */
1064 case 'p':
1065 if (set_paragraph_indent (optarg) < 0)
1066 usage (stderr, FATAL);
1067 break;
1068
1069 /* User specified error level? */
1070 case 'e':
1071 if (sscanf (optarg, "%d", &max_error_level) != 1)
1072 usage (stderr, FATAL);
1073 break;
1074
1075 /* User specified reference warning limit? */
1076 case 'r':
1077 if (sscanf (optarg, "%d", &reference_warning_limit) != 1)
1078 usage (stderr, FATAL);
1079 break;
1080
1081 /* User specified footnote style? */
1082 case 's':
1083 if (set_footnote_style (optarg) < 0)
1084 usage (stderr, FATAL);
1085 footnote_style_preset = 1;
1086 break;
1087
1088 case 'h':
1089 usage (stdout, NO_ERROR);
1090 break;
1091
1092 /* User requested version info? */
1093 case 'V':
1094 print_version_info ();
1095 exit (NO_ERROR);
1096 break;
1097
1098 case '?':
1099 usage (stderr, FATAL);
1100 break;
1101 }
1102 }
1103
1104 if (optind == argc)
1105 {
1106 /* Check to see if input is a file. If so, process that. */
1107 if (!isatty (fileno (stdin)))
1108 reading_from_stdin = 1;
1109 else
1110 usage (stderr, FATAL);
1111 }
1112
1113 /* If the user has specified --no-headers, this should imply --no-split.
1114 Do that here. I think it might also imply that we should ignore the
1115 setfilename at the top of the file, but this might break some FSF things,
1116 so I will hold off on that. */
1117 if (no_headers)
1118 {
1119 splitting = 0;
1120
1121 /* If the user has not specified an output file, then use stdout by
1122 default. */
1123 if (!command_output_filename)
1124 command_output_filename = strdup ("-");
1125 }
1126
1127 if (verbose_mode)
1128 print_version_info ();
1129
1130 /* Remaining arguments are file names of texinfo files.
1131 Convert them, one by one. */
1132 if (!reading_from_stdin)
1133 {
1134 while (optind != argc)
1135 convert_from_file (argv[optind++]);
1136 }
1137 else
1138 convert_from_stream (stdin, "stdin");
1139
1140 if (errors_printed)
1141 return (SYNTAX);
1142 else
1143 return (NO_ERROR);
1144 }
1145
1146 /* Display the version info of this invocation of Makeinfo. */
1147 static void
1148 print_version_info (void)
1149 {
1150 printf ("This is GNU Makeinfo version %d.%d.\n",
1151 major_version, minor_version);
1152 }
1153
1154 /* **************************************************************** */
1155 /* */
1156 /* Generic Utilities */
1157 /* */
1158 /* **************************************************************** */
1159
1160 #if !defined (HAVE_STRDUP)
1161 char *
1162 strdup (string)
1163 char *string;
1164 {
1165 char *result;
1166
1167 result = (char *)xmalloc (1 + strlen (string));
1168 strcpy (result, string);
1169
1170 return (result);
1171 }
1172 #endif /* !HAVE_STRDUP */
1173
1174 static void
1175 memory_error (callers_name, bytes_wanted)
1176 char *callers_name;
1177 int bytes_wanted;
1178 {
1179 char printable_string[80];
1180
1181 sprintf (printable_string,
1182 "Virtual memory exhausted in %s ()! Needed %d bytes.",
1183 callers_name, bytes_wanted);
1184
1185 error (printable_string);
1186 abort ();
1187 }
1188
1189 /* Just like malloc, but kills the program in case of fatal error. */
1190 static void *
1191 xmalloc (unsigned int nbytes)
1192 {
1193 void *temp = (void *) malloc (nbytes);
1194
1195 if (nbytes && temp == (void *)NULL)
1196 memory_error ("xmalloc", nbytes);
1197
1198 return (temp);
1199 }
1200
1201 /* Like realloc (), but barfs if there isn't enough memory. */
1202 static void *
1203 xrealloc (void *pointer, unsigned int nbytes)
1204 {
1205 void *temp;
1206
1207 if (!pointer)
1208 temp = (void *)xmalloc (nbytes);
1209 else
1210 temp = (void *)realloc (pointer, nbytes);
1211
1212 if (nbytes && !temp)
1213 memory_error ("xrealloc", nbytes);
1214
1215 return (temp);
1216 }
1217
1218 /* Tell the user how to use this program.
1219 Print the message to STREAM, and then exit with EXIT_VALUE. */
1220 static void
1221 usage (FILE *stream, int exit_value)
1222 {
1223 fprintf (stream, "Usage: %s [options] texinfo-file...\n\
1224 \n\
1225 This program accepts as input files of texinfo commands and text\n\
1226 and outputs a file suitable for reading with GNU Info.\n\
1227 \n\
1228 Options:\n\
1229 `-I DIR' add DIR to the directory search list for including\n\
1230 files with the `@include' command.\n\
1231 -D VAR define a variable, as with `@set'.\n\
1232 -U VAR undefine a variable, as with `@clear'.\n\
1233 -E MACRO-OFILE process macros, and output texinfo source code for TeX.\n\
1234 --no-validate suppress node cross reference validation.\n\
1235 --no-warn suppress warning messages (errors are still output).\n\
1236 --no-split suppress the splitting of large files.\n\
1237 --no-headers suppress the output of Node: Foo headers.\n\
1238 --verbose print information about what is being done.\n\
1239 --version print the version number of Makeinfo.\n\
1240 --output FILE or -o FILE\n\
1241 specify the output file. When you specify the\n\
1242 output file in this way, any `@setfilename' in the\n\
1243 input file is ignored.\n\
1244 --paragraph-indent NUM\n\
1245 set the paragraph indent to NUM (default %d).\n\
1246 --fill-column NUM set the filling column to NUM (default %d).\n\
1247 --error-limit NUM set the error limit to NUM (default %d).\n\
1248 --reference-limit NUM\n\
1249 set the reference warning limit to NUM (default %d).\n\
1250 --footnote-style STYLE\n\
1251 set the footnote style to STYLE. STYLE should\n\
1252 either be `separate' to place footnotes in their own\n\
1253 node, or `end', to place the footnotes at the end of\n\
1254 the node in which they are defined (the default).\n\
1255 --help print this message and exit.\n\n",
1256 progname, paragraph_start_indent,
1257 fill_column, max_error_level, reference_warning_limit);
1258 exit (exit_value);
1259 }
1260
1261 #if defined (MSDOS)
1262
1263 static struct passwd *
1264 getpwnam (char *name)
1265 {
1266 return (struct passwd *) 0;
1267 }
1268
1269 #endif /* MSDOS */
1270
1271 /* **************************************************************** */
1272 /* */
1273 /* Manipulating Lists */
1274 /* */
1275 /* **************************************************************** */
1276
1277 typedef struct generic_list {
1278 struct generic_list *next;
1279 } GENERIC_LIST;
1280
1281 /* Reverse the chain of structures in LIST. Output the new head
1282 of the chain. You should always assign the output value of this
1283 function to something, or you will lose the chain. */
1284 static GENERIC_LIST *
1285 reverse_list (register GENERIC_LIST *list)
1286 {
1287 register GENERIC_LIST *next;
1288 register GENERIC_LIST *prev = (GENERIC_LIST *) NULL;
1289
1290 while (list)
1291 {
1292 next = list->next;
1293 list->next = prev;
1294 prev = list;
1295 list = next;
1296 }
1297 return (prev);
1298 }
1299
1300
1301 /* **************************************************************** */
1302 /* */
1303 /* Pushing and Popping Files */
1304 /* */
1305 /* **************************************************************** */
1306
1307 /* Find and load the file named FILENAME. Return a pointer to
1308 the loaded file, or NULL if it can't be loaded. */
1309 static char *
1310 find_and_load (char *filename)
1311 {
1312 struct stat fileinfo;
1313 long file_size;
1314 int file = -1, count = 0;
1315 char *fullpath, *result;
1316
1317 result = fullpath = (char *)NULL;
1318
1319 fullpath = get_file_info_in_path (filename, include_files_path, &fileinfo);
1320
1321 if (!fullpath)
1322 goto error_exit;
1323
1324 filename = fullpath;
1325 file_size = (long) fileinfo.st_size;
1326
1327 file = open (filename, O_RDONLY);
1328 if (file < 0)
1329 goto error_exit;
1330
1331 /* Load the file. */
1332 result = (char *)xmalloc (1 + file_size);
1333
1334 /* VMS stat lies about the st_size value. The actual number of
1335 readable bytes is always less than this value. The arcane
1336 mysteries of VMS/RMS are too much to probe, so this hack
1337 suffices to make things work.
1338
1339 BPW: same business applies under MS-DOS. */
1340 #if defined (VMS) || defined (MSDOS)
1341 while ((n = read (file, result + count, file_size)) > 0)
1342 count += n;
1343 if (n == -1)
1344 #else /* !(VMS && MSDOS) */
1345 count = file_size;
1346 if (read (file, result, file_size) != file_size)
1347 #endif /* !(VMS && MSDOS) */
1348 error_exit:
1349 {
1350 if (result)
1351 free (result);
1352
1353 if (fullpath)
1354 free (fullpath);
1355
1356 if (file != -1)
1357 close (file);
1358
1359 return ((char *) NULL);
1360 }
1361 close (file);
1362
1363 /* Set the globals to the new file. */
1364 input_text = result;
1365 size_of_input_text = count;
1366 input_filename = strdup (fullpath);
1367 node_filename = strdup (fullpath);
1368 input_text_offset = 0;
1369 line_number = 1;
1370 /* Not strictly necessary. This magic prevents read_token () from doing
1371 extra unnecessary work each time it is called (that is a lot of times).
1372 The SIZE_OF_INPUT_TEXT is one past the actual end of the text. */
1373 input_text[size_of_input_text] = '\n';
1374 return (result);
1375 }
1376
1377 /* Save the state of the current input file. */
1378 static void
1379 pushfile (void)
1380 {
1381 FSTACK *newstack = (FSTACK *) xmalloc (sizeof (FSTACK));
1382 newstack->filename = input_filename;
1383 newstack->text = input_text;
1384 newstack->size = size_of_input_text;
1385 newstack->offset = input_text_offset;
1386 newstack->line_number = line_number;
1387 newstack->next = filestack;
1388
1389 filestack = newstack;
1390 push_node_filename ();
1391 }
1392
1393 /* Make the current file globals be what is on top of the file stack. */
1394 static void
1395 popfile (void)
1396 {
1397 FSTACK *tos = filestack;
1398
1399 if (!tos)
1400 abort (); /* My fault. I wonder what I did? */
1401
1402 #if defined (HAVE_MACROS)
1403 if (macro_expansion_output_stream)
1404 {
1405 maybe_write_itext (input_text, input_text_offset);
1406 forget_itext (input_text);
1407 }
1408 #endif /* HAVE_MACROS */
1409
1410 /* Pop the stack. */
1411 filestack = filestack->next;
1412
1413 /* Make sure that commands with braces have been satisfied. */
1414 if (!executing_string)
1415 discard_braces ();
1416
1417 /* Get the top of the stack into the globals. */
1418 input_filename = tos->filename;
1419 input_text = tos->text;
1420 size_of_input_text = tos->size;
1421 input_text_offset = tos->offset;
1422 line_number = tos->line_number;
1423 free (tos);
1424
1425 /* Go back to the (now) current node. */
1426 pop_node_filename ();
1427 }
1428
1429 /* Flush all open files on the file stack. */
1430 static void
1431 flush_file_stack (void)
1432 {
1433 while (filestack)
1434 {
1435 char *fname = input_filename;
1436 char *text = input_text;
1437 popfile ();
1438 free (fname);
1439 free (text);
1440 }
1441 }
1442
1443 int node_filename_stack_index = 0;
1444 int node_filename_stack_size = 0;
1445 char **node_filename_stack = (char **)NULL;
1446
1447 static void
1448 push_node_filename (void)
1449 {
1450 if (node_filename_stack_index + 1 > node_filename_stack_size)
1451 {
1452 if (!node_filename_stack)
1453 node_filename_stack =
1454 (char **)xmalloc ((node_filename_stack_size += 10)
1455 * sizeof (char *));
1456 else
1457 node_filename_stack =
1458 (char **)xrealloc (node_filename_stack,
1459 (node_filename_stack_size + 10)
1460 * sizeof (char *));
1461 }
1462
1463 node_filename_stack[node_filename_stack_index] = node_filename;
1464 node_filename_stack_index++;
1465 }
1466
1467 static void
1468 pop_node_filename (void)
1469 {
1470 node_filename = node_filename_stack[--node_filename_stack_index];
1471 }
1472
1473 /* Return just the simple part of the filename; i.e. the
1474 filename without the path information, or extensions.
1475 This conses up a new string. */
1476 static char *
1477 filename_part (char *filename)
1478 {
1479 char *basename;
1480
1481 basename = strrchr (filename, '/');
1482 if (!basename)
1483 basename = filename;
1484 else
1485 basename++;
1486
1487 basename = strdup (basename);
1488 #if defined (REMOVE_OUTPUT_EXTENSIONS)
1489
1490 /* See if there is an extension to remove. If so, remove it. */
1491 {
1492 char *temp;
1493
1494 temp = strrchr (basename, '.');
1495 if (temp)
1496 *temp = '\0';
1497 }
1498 #endif /* REMOVE_OUTPUT_EXTENSIONS */
1499 return (basename);
1500 }
1501
1502 /* Return the pathname part of filename. This can be NULL. */
1503 static char *
1504 pathname_part (char *filename)
1505 {
1506 char *result = (char *) NULL;
1507 register int i;
1508
1509 filename = expand_filename (filename, "");
1510
1511 i = strlen (filename) - 1;
1512
1513 while (i && filename[i] != '/')
1514 i--;
1515 if (filename[i] == '/')
1516 i++;
1517
1518 if (i)
1519 {
1520 result = (char *)xmalloc (1 + i);
1521 strncpy (result, filename, i);
1522 result[i] = '\0';
1523 }
1524 free (filename);
1525 return (result);
1526 }
1527
1528 static char *
1529 filename_non_directory (char *name)
1530 {
1531 register int i;
1532
1533 for (i = strlen (name) - 1; i; i--)
1534 if (name[i] == '/')
1535 return (strdup (name + i + 1));
1536
1537 return (strdup (name));
1538 }
1539
1540 /* Return the expansion of FILENAME. */
1541 static char *
1542 expand_filename (char *filename, char *input_name)
1543 {
1544 register int i;
1545
1546 if (filename)
1547 filename = full_pathname (filename);
1548 else
1549 {
1550 filename = filename_non_directory (input_name);
1551
1552 if (!*filename)
1553 {
1554 free (filename);
1555 filename = strdup ("noname.texi");
1556 }
1557
1558 for (i = strlen (filename) - 1; i; i--)
1559 if (filename[i] == '.')
1560 break;
1561
1562 if (!i)
1563 i = strlen (filename);
1564
1565 if (i + 6 > (int) (strlen (filename)))
1566 filename = (char *)xrealloc (filename, i + 6);
1567 strcpy (filename + i, ".info");
1568 return (filename);
1569 }
1570
1571 if (filename[0] == '.' || filename[0] == '/')
1572 return (filename);
1573
1574 if (filename[0] != '/' && input_name[0] == '/')
1575 {
1576 /* Make it so that relative names work. */
1577 char *result;
1578
1579 i = strlen (input_name) - 1;
1580
1581 result = (char *)xmalloc (1 + strlen (input_name) + strlen (filename));
1582 strcpy (result, input_name);
1583
1584 while (result[i] != '/' && i)
1585 i--;
1586
1587 if (result[i] == '/')
1588 i++;
1589
1590 strcpy (&result[i], filename);
1591 free (filename);
1592 return (result);
1593 }
1594 return (filename);
1595 }
1596
1597 /* Return the full path to FILENAME. */
1598 static char *
1599 full_pathname (char *filename)
1600 {
1601 int initial_character;
1602 char *result;
1603
1604 /* No filename given? */
1605 if (!filename || !(initial_character = *filename))
1606 return (strdup (""));
1607
1608 /* Already absolute? */
1609 if ((initial_character == '/') ||
1610 ((strncmp (filename, "./", 2) == 0) ||
1611 (strncmp (filename, "../", 3) == 0)))
1612 return (strdup (filename));
1613
1614 if (initial_character != '~')
1615 {
1616 char *localdir;
1617
1618 localdir = (char *)xmalloc (1025);
1619 #if defined (HAVE_GETWD)
1620 if (!getwd (localdir))
1621 #else /* !HAVE_GETWD */
1622 if (!getcwd (localdir, 1024))
1623 #endif /* !HAVE_GETWD */
1624 {
1625 fprintf (stderr, "%s: getwd: %s, %s\n",
1626 progname, filename, localdir);
1627 exit (1);
1628 }
1629
1630 strcat (localdir, "/");
1631 strcat (localdir, filename);
1632 result = strdup (localdir);
1633 free (localdir);
1634 }
1635 else
1636 {
1637 if (filename[1] == '/')
1638 {
1639 /* Return the concatenation of the environment variable HOME
1640 and the rest of the string. */
1641 char *temp_home;
1642
1643 temp_home = (char *) getenv ("HOME");
1644 result = (char *)xmalloc (strlen (&filename[1])
1645 + 1
1646 + temp_home ? strlen (temp_home)
1647 : 0);
1648 *result = '\0';
1649
1650 if (temp_home)
1651 strcpy (result, temp_home);
1652
1653 strcat (result, &filename[1]);
1654 }
1655 else
1656 {
1657 struct passwd *user_entry;
1658 int i, c;
1659 char *username = (char *)xmalloc (257);
1660
1661 for (i = 1; (c = filename[i]); i++)
1662 {
1663 if (c == '/')
1664 break;
1665 else
1666 username[i - 1] = c;
1667 }
1668 if (c)
1669 username[i - 1] = '\0';
1670
1671 user_entry = getpwnam (username);
1672
1673 if (!user_entry)
1674 return (strdup (filename));
1675
1676 result = (char *)xmalloc (1 + strlen (user_entry->pw_dir)
1677 + strlen (&filename[i]));
1678 strcpy (result, user_entry->pw_dir);
1679 strcat (result, &filename[i]);
1680 }
1681 }
1682 return (result);
1683 }
1684
1685 static char *
1686 output_name_from_input_name (char *name)
1687 {
1688 return (expand_filename ((char *)NULL, name));
1689 }
1690
1691 /* **************************************************************** */
1692 /* */
1693 /* Error Handling */
1694 /* */
1695 /* **************************************************************** */
1696
1697 /* Number of errors encountered. */
1698 int errors_printed = 0;
1699
1700 /* Print the last error gotten from the file system. */
1701 static int
1702 fs_error (char *filename)
1703 {
1704 remember_error ();
1705 perror (filename);
1706 return (0);
1707 }
1708
1709 static int
1710 error (char *format, ...)
1711 {
1712 va_list ap;
1713
1714 remember_error ();
1715 va_start (ap, format);
1716 vfprintf (stderr, format, ap);
1717 fprintf (stderr, "\n");
1718 va_end (ap);
1719 return ((int) 0);
1720 }
1721
1722 /* Just like error (), but print the line number as well. */
1723 static int
1724 line_error (char *format, ...)
1725 {
1726 va_list ap;
1727
1728 remember_error ();
1729 va_start (ap, format);
1730 fprintf (stderr, "%s:%d: ", input_filename, line_number);
1731 vfprintf (stderr, format, ap);
1732 fprintf (stderr, ".\n");
1733 va_end (ap);
1734 return ((int) 0);
1735 }
1736
1737 static int
1738 warning (char *format, ...)
1739 {
1740 va_list ap;
1741
1742 va_start (ap, format);
1743 if (print_warnings)
1744 {
1745 fprintf (stderr, "%s:%d: Warning: ", input_filename, line_number);
1746 vfprintf (stderr, format, ap);
1747 fprintf (stderr, ".\n");
1748 }
1749 va_end (ap);
1750 return ((int) 0);
1751 }
1752
1753 /* Remember that an error has been printed. If this is the first
1754 error printed, then tell them which program is printing them.
1755 If more than max_error_level have been printed, then exit the
1756 program. */
1757 static void
1758 remember_error (void)
1759 {
1760 errors_printed++;
1761 if (max_error_level && (errors_printed > max_error_level))
1762 {
1763 fprintf (stderr, "Too many errors! Gave up.\n");
1764 flush_file_stack ();
1765 cm_bye ();
1766 exit (1);
1767 }
1768 }
1769
1770 /* **************************************************************** */
1771 /* */
1772 /* Hacking Tokens and Strings */
1773 /* */
1774 /* **************************************************************** */
1775
1776 /* Return the next token as a string pointer. We cons the
1777 string. */
1778 static char *
1779 read_token (void)
1780 {
1781 int i, character;
1782 char *result;
1783
1784 /* If the first character to be read is self-delimiting, then that
1785 is the command itself. */
1786 character = curchar ();
1787 if (self_delimiting (character))
1788 {
1789 input_text_offset++;
1790 result = strdup (" ");
1791 *result = character;
1792 return (result);
1793 }
1794
1795 for (i = 0; ((input_text_offset != size_of_input_text)
1796 && (character = curchar ())
1797 && command_char (character));
1798 i++, input_text_offset++);
1799 result = (char *)xmalloc (i + 1);
1800 memcpy (result, &input_text[input_text_offset - i], i);
1801 result[i] = '\0';
1802 return (result);
1803 }
1804
1805 /* Return non-zero if CHARACTER is self-delimiting. */
1806 static int
1807 self_delimiting (int character)
1808 {
1809 return (member (character, "{}:.@*'`,!?; \n\t"));
1810 }
1811
1812 /* Clear whitespace from the front and end of string. */
1813 static void
1814 canon_white (char *string)
1815 {
1816 int len = strlen (string);
1817 int x;
1818
1819 if (!len)
1820 return;
1821
1822 for (x = 0; x < len; x++)
1823 {
1824 if (!cr_or_whitespace (string[x]))
1825 {
1826 strcpy (string, string + x);
1827 break;
1828 }
1829 }
1830 len = strlen (string);
1831 if (len)
1832 len--;
1833 while (len > -1 && cr_or_whitespace (string[len]))
1834 len--;
1835 string[len + 1] = '\0';
1836 }
1837
1838 /* Bash STRING, replacing all whitespace with just one space. */
1839 static void
1840 fix_whitespace (char *string)
1841 {
1842 char *temp = (char *)xmalloc (strlen (string) + 1);
1843 int string_index = 0;
1844 int temp_index = 0;
1845 int c;
1846
1847 canon_white (string);
1848
1849 while (string[string_index])
1850 {
1851 c = temp[temp_index++] = string[string_index++];
1852
1853 if (c == ' ' || c == '\n' || c == '\t')
1854 {
1855 temp[temp_index - 1] = ' ';
1856 while ((c = string[string_index]) && (c == ' ' ||
1857 c == '\t' ||
1858 c == '\n'))
1859 string_index++;
1860 }
1861 }
1862 temp[temp_index] = '\0';
1863 strcpy (string, temp);
1864 free (temp);
1865 }
1866
1867 /* Discard text until the desired string is found. The string is
1868 included in the discarded text. */
1869 static void
1870 discard_until (char *string)
1871 {
1872 int temp = search_forward (string, input_text_offset);
1873
1874 int tt = (temp < 0) ? size_of_input_text : temp + strlen (string);
1875 int from = input_text_offset;
1876
1877 /* Find out what line we are on. */
1878 while (from != tt)
1879 if (input_text[from++] == '\n')
1880 line_number++;
1881
1882 if (temp < 0)
1883 {
1884 input_text_offset = size_of_input_text - strlen (string);
1885
1886 if (strcmp (string, "\n") != 0)
1887 {
1888 line_error ("Expected `%s'", string);
1889 return;
1890 }
1891 }
1892 else
1893 input_text_offset = temp;
1894
1895 input_text_offset += strlen (string);
1896 }
1897
1898 /* Read characters from the file until we are at MATCH.
1899 Place the characters read into STRING.
1900 On exit input_text_offset is after the match string.
1901 Return the offset where the string starts. */
1902 static int
1903 get_until (char *match, char **string)
1904 {
1905 int len, current_point, x, new_point, tem;
1906
1907 current_point = x = input_text_offset;
1908 new_point = search_forward (match, input_text_offset);
1909
1910 if (new_point < 0)
1911 new_point = size_of_input_text;
1912 len = new_point - current_point;
1913
1914 /* Keep track of which line number we are at. */
1915 tem = new_point + (strlen (match) - 1);
1916 while (x != tem)
1917 if (input_text[x++] == '\n')
1918 line_number++;
1919
1920 *string = (char *)xmalloc (len + 1);
1921
1922 memcpy (*string, &input_text[current_point], len);
1923 (*string)[len] = '\0';
1924
1925 /* Now leave input_text_offset in a consistent state. */
1926 input_text_offset = tem;
1927
1928 if (input_text_offset > size_of_input_text)
1929 input_text_offset = size_of_input_text;
1930
1931 return (new_point);
1932 }
1933
1934 /* Read characters from the file until we are at MATCH or end of line.
1935 Place the characters read into STRING. */
1936 static void
1937 get_until_in_line (char *match, char **string)
1938 {
1939 int real_bottom, temp;
1940
1941 real_bottom = size_of_input_text;
1942 temp = search_forward ("\n", input_text_offset);
1943
1944 if (temp < 0)
1945 temp = size_of_input_text;
1946
1947 size_of_input_text = temp;
1948 get_until (match, string);
1949 size_of_input_text = real_bottom;
1950 }
1951
1952 static void
1953 get_rest_of_line (char **string)
1954 {
1955 get_until ("\n", string);
1956 canon_white (*string);
1957
1958 if (curchar () == '\n') /* as opposed to the end of the file... */
1959 {
1960 line_number++;
1961 input_text_offset++;
1962 }
1963 }
1964
1965 /* Backup the input pointer to the previous character, keeping track
1966 of the current line number. */
1967 static void
1968 backup_input_pointer (void)
1969 {
1970 if (input_text_offset)
1971 {
1972 input_text_offset--;
1973 if (curchar () == '\n')
1974 line_number--;
1975 }
1976 }
1977
1978 /* Read characters from the file until we are at MATCH or closing brace.
1979 Place the characters read into STRING. */
1980 static void
1981 get_until_in_braces (char *match, char **string)
1982 {
1983 int i, brace = 0;
1984 int match_len = strlen (match);
1985 char *temp;
1986
1987 for (i = input_text_offset; i < size_of_input_text; i++)
1988 {
1989 if (input_text[i] == '{')
1990 brace++;
1991 else if (input_text[i] == '}')
1992 brace--;
1993 else if (input_text[i] == '\n')
1994 line_number++;
1995
1996 if (brace < 0 ||
1997 (brace == 0 && strncmp (input_text + i, match, match_len) == 0))
1998 break;
1999 }
2000
2001 match_len = i - input_text_offset;
2002 temp = (char *)xmalloc (2 + match_len);
2003 strncpy (temp, input_text + input_text_offset, match_len);
2004 temp[match_len] = '\0';
2005 input_text_offset = i;
2006 *string = temp;
2007 }
2008
2009 /* **************************************************************** */
2010 /* */
2011 /* Converting the File */
2012 /* */
2013 /* **************************************************************** */
2014
2015 /* Convert the file named by NAME. The output is saved on the file
2016 named as the argument to the @setfilename command. */
2017 static char *suffixes[] = {
2018 "",
2019 ".texinfo",
2020 ".texi",
2021 ".txinfo",
2022 (char *)NULL
2023 };
2024
2025 static void
2026 initialize_conversion (void)
2027 {
2028 init_tag_table ();
2029 init_indices ();
2030 init_internals ();
2031 init_paragraph ();
2032 }
2033
2034 /* We read in multiples of 4k, simply because it is a typical pipe size
2035 on unix systems. */
2036 #define _READ_BUFFER_GROWTH (4 * 4096)
2037
2038 /* Convert the texinfo file coming from the open stream STREAM. Assume the
2039 source of the stream is named NAME. */
2040 static void
2041 convert_from_stream (FILE *stream, char *name)
2042 {
2043 char *buffer = (char *)NULL;
2044 int buffer_offset = 0, buffer_size = 0;
2045
2046 initialize_conversion ();
2047
2048 /* Read until the end of the stream. This isn't strictly correct, since
2049 the texinfo input may end before the stream ends, but it is a quick
2050 working hueristic. */
2051 while (!feof (stream))
2052 {
2053 int count;
2054
2055 if (buffer_offset + (_READ_BUFFER_GROWTH + 1) >= buffer_size)
2056 buffer = (char *)
2057 xrealloc (buffer, (buffer_size += _READ_BUFFER_GROWTH));
2058
2059 count = fread (buffer + buffer_offset, 1, _READ_BUFFER_GROWTH, stream);
2060
2061 if (count < 0)
2062 {
2063 perror (name);
2064 exit (FATAL);
2065 }
2066
2067 buffer_offset += count;
2068 if (count == 0)
2069 break;
2070 }
2071
2072 /* Set the globals to the new file. */
2073 input_text = buffer;
2074 size_of_input_text = buffer_offset;
2075 input_filename = strdup (name);
2076 node_filename = strdup (name);
2077 input_text_offset = 0;
2078 line_number = 1;
2079
2080 /* Not strictly necessary. This magic prevents read_token () from doing
2081 extra unnecessary work each time it is called (that is a lot of times).
2082 The SIZE_OF_INPUT_TEXT is one past the actual end of the text. */
2083 input_text[size_of_input_text] = '\n';
2084
2085 convert_from_loaded_file (name);
2086 }
2087
2088 static void
2089 convert_from_file (char *name)
2090 {
2091 register int i;
2092 char *filename = (char *)xmalloc (strlen (name) + 50);
2093
2094 initialize_conversion ();
2095
2096 /* Try to load the file specified by NAME. If the file isn't found, and
2097 there is no suffix in NAME, then try NAME.texinfo, and NAME.texi. */
2098 for (i = 0; suffixes[i]; i++)
2099 {
2100 strcpy (filename, name);
2101 strcat (filename, suffixes[i]);
2102
2103 if (find_and_load (filename))
2104 break;
2105
2106 if (!suffixes[i][0] && strrchr (filename, '.'))
2107 {
2108 fs_error (filename);
2109 free (filename);
2110 return;
2111 }
2112 }
2113
2114 if (!suffixes[i])
2115 {
2116 fs_error (name);
2117 free (filename);
2118 return;
2119 }
2120
2121 input_filename = filename;
2122
2123 convert_from_loaded_file (name);
2124 }
2125
2126 static void
2127 convert_from_loaded_file (char *name)
2128 {
2129 char *real_output_filename = (char *)NULL;
2130
2131 #if defined (HAVE_MACROS)
2132 remember_itext (input_text, 0);
2133 #endif /* HAVE_MACROS */
2134
2135 /* Search this file looking for the special string which starts conversion.
2136 Once found, we may truly begin. */
2137 input_text_offset = 0;
2138 while (input_text_offset >= 0)
2139 {
2140 input_text_offset =
2141 search_forward (setfilename_search, input_text_offset);
2142
2143 if ((input_text_offset == 0) ||
2144 ((input_text_offset > 0) &&
2145 (input_text[input_text_offset -1] == '\n')))
2146 break;
2147 else if (input_text_offset > 0)
2148 input_text_offset++;
2149 }
2150
2151 if (input_text_offset < 0)
2152 {
2153 if (!command_output_filename)
2154 {
2155 #if defined (REQUIRE_SETFILENAME)
2156 error ("No `%s' found in `%s'", setfilename_search, name);
2157 goto finished;
2158 #else
2159 register int i, end_of_first_line;
2160
2161 /* Find the end of the first line in the file. */
2162 for (i = 0; i < size_of_input_text - 1; i++)
2163 if (input_text[i] == '\n')
2164 break;
2165
2166 end_of_first_line = i + 1;
2167
2168 input_text_offset = 0;
2169
2170 for (i = 0; i < end_of_first_line; i++)
2171 {
2172 if ((input_text[i] == '\\') &&
2173 (strncmp (input_text + i + 1, "include", 7) == 0))
2174 {
2175 input_text_offset = end_of_first_line;
2176 break;
2177 }
2178 }
2179 command_output_filename = output_name_from_input_name (name);
2180 #endif /* !REQUIRE_SETFILENAME */
2181 }
2182 }
2183 else
2184 input_text_offset += strlen (setfilename_search);
2185
2186 if (!command_output_filename)
2187 get_until ("\n", &output_filename);
2188 else
2189 {
2190 if (input_text_offset != -1)
2191 discard_until ("\n");
2192 else
2193 input_text_offset = 0;
2194
2195 real_output_filename = output_filename = command_output_filename;
2196 command_output_filename = (char *)NULL;
2197 }
2198
2199 canon_white (output_filename);
2200
2201 if (real_output_filename &&
2202 strcmp (real_output_filename, "-") == 0)
2203 {
2204 real_output_filename = strdup (real_output_filename);
2205 output_stream = stdout;
2206 splitting = 0; /* Cannot split when writing to stdout. */
2207 }
2208 else
2209 {
2210 if (!real_output_filename)
2211 real_output_filename = expand_filename (output_filename, name);
2212 else
2213 real_output_filename = strdup (real_output_filename);
2214
2215 output_stream = fopen (real_output_filename, "w");
2216 }
2217
2218 if (output_stream != stdout)
2219 printf ("Making info file `%s' from `%s'.\n", output_filename, name);
2220
2221 if (output_stream == NULL)
2222 {
2223 fs_error (real_output_filename);
2224 goto finished;
2225 }
2226
2227 /* Make the displayable filename from output_filename. Only the base
2228 portion of the filename need be displayed. */
2229 if (output_stream != stdout)
2230 pretty_output_filename = filename_part (output_filename);
2231 else
2232 pretty_output_filename = strdup ("stdout");
2233
2234 /* For this file only, count the number of newlines from the top of
2235 the file to here. This way, we keep track of line numbers for
2236 error reporting. Line_number starts at 1, since the user isn't
2237 zero-based. */
2238 {
2239 int temp = 0;
2240 line_number = 1;
2241 while (temp != input_text_offset)
2242 if (input_text[temp++] == '\n')
2243 line_number++;
2244 }
2245
2246 if (!no_headers)
2247 {
2248 add_word_args ("This is Info file %s, produced by Makeinfo-%d.%d from ",
2249 output_filename, major_version, minor_version);
2250 add_word_args ("the input file %s.\n", input_filename);
2251 }
2252
2253 close_paragraph ();
2254 reader_loop ();
2255
2256 finished:
2257 close_paragraph ();
2258 flush_file_stack ();
2259
2260 #if defined (HAVE_MACROS)
2261 if (macro_expansion_output_stream)
2262 fclose (macro_expansion_output_stream);
2263 #endif /* HAVE_MACROS */
2264
2265 if (output_stream != NULL)
2266 {
2267 output_pending_notes ();
2268 free_pending_notes ();
2269 if (tag_table != NULL)
2270 {
2271 tag_table = (TAG_ENTRY *) reverse_list ((GENERIC_LIST *) tag_table);
2272 if (!no_headers)
2273 write_tag_table ();
2274 }
2275
2276 if (output_stream != stdout)
2277 fclose (output_stream);
2278
2279 /* If validating, then validate the entire file right now. */
2280 if (validating)
2281 validate_file (tag_table);
2282
2283 /* This used to test && !errors_printed.
2284 But some files might have legit warnings. So split anyway. */
2285 if (splitting)
2286 split_file (real_output_filename, 0);
2287 }
2288 free (real_output_filename);
2289 }
2290
2291 static void
2292 free_and_clear (char **pointer)
2293 {
2294 if ((*pointer) != (char *) NULL)
2295 {
2296 free (*pointer);
2297 *pointer = (char *) NULL;
2298 }
2299 }
2300
2301 /* Initialize some state. */
2302 static void
2303 init_internals (void)
2304 {
2305 free_and_clear (&current_node);
2306 free_and_clear (&output_filename);
2307 free_and_clear (&command);
2308 free_and_clear (&input_filename);
2309 free_node_references ();
2310 init_insertion_stack ();
2311 init_brace_stack ();
2312 command_index = 0;
2313 in_menu = 0;
2314 top_node_seen = 0;
2315 non_top_node_seen = 0;
2316 }
2317
2318 static void
2319 init_paragraph (void)
2320 {
2321 free_and_clear ((char **) &output_paragraph);
2322 output_paragraph = (unsigned char *)xmalloc (paragraph_buffer_len);
2323 output_position = 0;
2324 output_paragraph[0] = '\0';
2325 output_paragraph_offset = 0;
2326 output_column = 0;
2327 paragraph_is_open = 0;
2328 current_indent = 0;
2329 }
2330
2331 /* Okay, we are ready to start the conversion. Call the reader on
2332 some text, and fill the text as it is output. Handle commands by
2333 remembering things like open braces and the current file position on a
2334 stack, and when the corresponding close brace is found, you can call
2335 the function with the proper arguments. */
2336 static void
2337 reader_loop (void)
2338 {
2339 int character;
2340 int done = 0;
2341 int dash_count = 0;
2342
2343 while (!done)
2344 {
2345 if (input_text_offset >= size_of_input_text)
2346 break;
2347
2348 character = curchar ();
2349
2350 if (!in_fixed_width_font &&
2351 (character == '\'' || character == '`') &&
2352 input_text[input_text_offset + 1] == character)
2353 {
2354 input_text_offset++;
2355 character = '"';
2356 }
2357
2358 if (character == '-')
2359 {
2360 dash_count++;
2361 if (dash_count == 2 && !in_fixed_width_font)
2362 {
2363 input_text_offset++;
2364 continue;
2365 }
2366 }
2367 else
2368 {
2369 dash_count = 0;
2370 }
2371
2372 /* If this is a whitespace character, then check to see if the line
2373 is blank. If so, advance to the carriage return. */
2374 if (whitespace (character))
2375 {
2376 register int i = input_text_offset + 1;
2377
2378 while (i < size_of_input_text && whitespace (input_text[i]))
2379 i++;
2380
2381 if (i == size_of_input_text || input_text[i] == '\n')
2382 {
2383 if (i == size_of_input_text)
2384 i--;
2385
2386 input_text_offset = i;
2387 character = curchar ();
2388 }
2389 }
2390
2391 if (character == '\n')
2392 {
2393 line_number++;
2394
2395 /* Check for a menu entry here, since the "escape sequence"
2396 that begins menu entrys is "\n* ". */
2397 if (in_menu && input_text_offset + 1 < size_of_input_text)
2398 {
2399 char *tem;
2400
2401 /* Note that the value of TEM is discarded, since it is
2402 gauranteed to be NULL when glean_node_from_menu () is
2403 called with a non-zero argument. */
2404 tem = glean_node_from_menu (1);
2405 }
2406 }
2407
2408 switch (character)
2409 {
2410 case COMMAND_PREFIX:
2411 read_command ();
2412 break;
2413
2414 case '{':
2415
2416 /* Special case. I'm not supposed to see this character by itself.
2417 If I do, it means there is a syntax error in the input text.
2418 Report the error here, but remember this brace on the stack so
2419 you can ignore its partner. */
2420
2421 line_error ("Misplaced `{'");
2422 remember_brace (misplaced_brace);
2423
2424 /* Don't advance input_text_offset since this happens in
2425 remember_brace ().
2426 input_text_offset++;
2427 */
2428 break;
2429
2430 case '}':
2431 pop_and_call_brace ();
2432 input_text_offset++;
2433 break;
2434
2435 default:
2436 add_char (character);
2437 input_text_offset++;
2438 }
2439 }
2440 #if defined (HAVE_MACROS)
2441 if (macro_expansion_output_stream)
2442 maybe_write_itext (input_text, input_text_offset);
2443 #endif /* HAVE_MACROS */
2444 }
2445
2446 /* Find the command corresponding to STRING. If the command
2447 is found, return a pointer to the data structure. Otherwise
2448 return (-1). */
2449 static COMMAND *
2450 get_command_entry (char *string)
2451 {
2452 register int i;
2453
2454 for (i = 0; CommandTable[i].name; i++)
2455 if (strcmp (CommandTable[i].name, string) == 0)
2456 return (&CommandTable[i]);
2457
2458 /* This command is not in our predefined command table. Perhaps
2459 it is a user defined command. */
2460 for (i = 0; i < user_command_array_len; i++)
2461 if (user_command_array[i] &&
2462 (strcmp (user_command_array[i]->name, string) == 0))
2463 return (user_command_array[i]);
2464
2465 /* Nope, we never heard of this command. */
2466 return ((COMMAND *) -1);
2467 }
2468
2469 /* input_text_offset is right at the command prefix character.
2470 Read the next token to determine what to do. */
2471 static void
2472 read_command (void)
2473 {
2474 COMMAND *entry;
2475
2476 input_text_offset++;
2477 free_and_clear (&command);
2478 command = read_token ();
2479
2480 #if defined (HAVE_MACROS)
2481 /* Check to see if this command is a macro. If so, execute it here. */
2482 {
2483 MACRO_DEF *def;
2484
2485 def = find_macro (command);
2486
2487 if (def)
2488 {
2489 /* We disallow recursive use of a macro call. Inhibit the expansion
2490 of this macro during the life of its execution. */
2491 if (!(def->flags & ME_RECURSE))
2492 def->inhibited = 1;
2493
2494 execute_macro (def);
2495
2496 if (!(def->flags & ME_RECURSE))
2497 def->inhibited = 0;
2498
2499 return;
2500 }
2501 }
2502 #endif /* HAVE_MACROS */
2503
2504 entry = get_command_entry (command);
2505
2506 if (entry == (COMMAND *)-1)
2507 {
2508 line_error ("Unknown info command `%s'", command);
2509 return;
2510 }
2511
2512 if (entry->argument_in_braces)
2513 remember_brace (entry->proc);
2514
2515 (*(entry->proc)) (START, output_paragraph_offset, 0);
2516 }
2517
2518 /* Return the string which invokes PROC; a pointer to a function. */
2519 static char *
2520 find_proc_name (COMMAND_FUNCTION *proc)
2521 {
2522 register int i;
2523
2524 for (i = 0; CommandTable[i].name; i++)
2525 if (proc == CommandTable[i].proc)
2526 return (CommandTable[i].name);
2527 return ("NO_NAME!");
2528 }
2529
2530 static void
2531 init_brace_stack (void)
2532 {
2533 brace_stack = (BRACE_ELEMENT *) NULL;
2534 }
2535
2536 static void
2537 remember_brace (COMMAND_FUNCTION *proc)
2538 {
2539 if (curchar () != '{')
2540 line_error ("%c%s expected `{..}'", COMMAND_PREFIX, command);
2541 else
2542 input_text_offset++;
2543 remember_brace_1 (proc, output_paragraph_offset);
2544 }
2545
2546 /* Remember the current output position here. Save PROC
2547 along with it so you can call it later. */
2548 static void
2549 remember_brace_1 (COMMAND_FUNCTION *proc, int position)
2550 {
2551 BRACE_ELEMENT *new = (BRACE_ELEMENT *) xmalloc (sizeof (BRACE_ELEMENT));
2552 new->next = brace_stack;
2553 new->proc = proc;
2554 new->pos = position;
2555 new->line = line_number;
2556 brace_stack = new;
2557 }
2558
2559 /* Pop the top of the brace stack, and call the associated function
2560 with the args END and POS. */
2561 static void
2562 pop_and_call_brace (void)
2563 {
2564 BRACE_ELEMENT *temp;
2565 COMMAND_FUNCTION *proc;
2566 int pos;
2567
2568 if (brace_stack == (BRACE_ELEMENT *) NULL)
2569 {
2570 line_error ("Unmatched close brace");
2571 return;
2572 }
2573
2574 pos = brace_stack->pos;
2575 proc = brace_stack->proc;
2576 temp = brace_stack->next;
2577 free (brace_stack);
2578 brace_stack = temp;
2579
2580 (*proc) (END, pos, output_paragraph_offset);
2581 }
2582
2583 /* Shift all of the markers in `brace_stack' by AMOUNT. */
2584 static void
2585 adjust_braces_following (int here, int amount)
2586 {
2587 register BRACE_ELEMENT *stack = brace_stack;
2588
2589 while (stack)
2590 {
2591 if (stack->pos >= here)
2592 stack->pos += amount;
2593 stack = stack->next;
2594 }
2595 }
2596
2597 /* You call discard_braces () when you shouldn't have any braces on the stack.
2598 I used to think that this happens for commands that don't take arguments
2599 in braces, but that was wrong because of things like @code{foo @@}. So now
2600 I only detect it at the beginning of nodes. */
2601 static void
2602 discard_braces (void)
2603 {
2604 if (!brace_stack)
2605 return;
2606
2607 while (brace_stack)
2608 {
2609 if (brace_stack->proc != misplaced_brace)
2610 {
2611 char *proc_name;
2612 int temp_line_number = line_number;
2613
2614 line_number = brace_stack->line;
2615 proc_name = find_proc_name (brace_stack->proc);
2616 line_error ("%c%s missing close brace", COMMAND_PREFIX, proc_name);
2617 line_number = temp_line_number;
2618 pop_and_call_brace ();
2619 }
2620 else
2621 {
2622 BRACE_ELEMENT *temp;
2623 temp = brace_stack->next;
2624 free (brace_stack);
2625 brace_stack = temp;
2626 }
2627 }
2628 }
2629
2630 static int
2631 get_char_len (int character)
2632 {
2633 /* Return the printed length of the character. */
2634 int len;
2635
2636 switch (character)
2637 {
2638 case '\t':
2639 len = (output_column + 8) & 0xf7;
2640 if (len > fill_column)
2641 len = fill_column - output_column;
2642 else
2643 len = len - output_column;
2644 break;
2645
2646 case '\n':
2647 len = fill_column - output_column;
2648 break;
2649
2650 default:
2651 if (character < ' ')
2652 len = 2;
2653 else
2654 len = 1;
2655 }
2656 return (len);
2657 }
2658
2659 static void
2660 add_word_args (char *format, ...)
2661 {
2662 va_list ap;
2663 char buffer[1000];
2664
2665 va_start (ap, format);
2666 vsprintf (buffer, format, ap);
2667 add_word (buffer);
2668 va_end (ap);
2669 }
2670
2671 /* Add STRING to output_paragraph. */
2672 static void
2673 add_word (char *string)
2674 {
2675 while (*string)
2676 add_char (*string++);
2677 }
2678
2679 /* Non-zero if the last character inserted has the syntax class of NEWLINE. */
2680 int last_char_was_newline = 1;
2681
2682 /* The actual last inserted character. Note that this may be something
2683 other than NEWLINE even if last_char_was_newline is 1. */
2684 int last_inserted_character = 0;
2685
2686 /* Non-zero means that a newline character has already been
2687 inserted, so close_paragraph () should insert one less. */
2688 int line_already_broken = 0;
2689
2690 /* When non-zero we have finished an insertion (see end_insertion ()) and we
2691 want to ignore false continued paragraph closings. */
2692 int insertion_paragraph_closed = 0;
2693
2694 /* Non-zero means attempt to make all of the lines have fill_column width. */
2695 int do_justification = 0;
2696
2697 /* Add the character to the current paragraph. If filling_enabled is
2698 non-zero, then do filling as well. */
2699 static void
2700 add_char (int character)
2701 {
2702 /* If we are avoiding outputting headers, and we are currently
2703 in a menu, then simply return. */
2704 if (no_headers && in_menu)
2705 return;
2706
2707 /* If we are adding a character now, then we don't have to
2708 ignore close_paragraph () calls any more. */
2709 if (must_start_paragraph && character != '\n')
2710 {
2711 must_start_paragraph = 0;
2712 line_already_broken = 0; /* The line is no longer broken. */
2713 if (current_indent > output_column)
2714 {
2715 indent (current_indent - output_column);
2716 output_column = current_indent;
2717 }
2718 }
2719
2720 if (non_splitting_words && member (character, " \t\n"))
2721 character = ' ' | 0x80;
2722
2723 insertion_paragraph_closed = 0;
2724
2725 switch (character)
2726 {
2727 case '\n':
2728 if (!filling_enabled)
2729 {
2730 insert ('\n');
2731
2732 if (force_flush_right)
2733 {
2734 close_paragraph ();
2735 /* Hack to force single blank lines out in this mode. */
2736 flush_output ();
2737 }
2738
2739 output_column = 0;
2740
2741 if (!no_indent && paragraph_is_open)
2742 indent (output_column = current_indent);
2743 break;
2744 }
2745 else /* CHARACTER is newline, and filling is enabled. */
2746 {
2747 if (sentence_ender (last_inserted_character))
2748 {
2749 insert (' ');
2750 output_column++;
2751 last_inserted_character = character;
2752 }
2753 }
2754
2755 if (last_char_was_newline)
2756 {
2757 close_paragraph ();
2758 pending_indent = 0;
2759 }
2760 else
2761 {
2762 last_char_was_newline = 1;
2763 insert (' ');
2764 output_column++;
2765 }
2766 break;
2767
2768 default:
2769 {
2770 int len = get_char_len (character);
2771 int suppress_insert = 0;
2772
2773 if ((character == ' ') && (last_char_was_newline))
2774 {
2775 if (!paragraph_is_open)
2776 {
2777 pending_indent++;
2778 return;
2779 }
2780 }
2781
2782 if (!paragraph_is_open)
2783 {
2784 start_paragraph ();
2785
2786 /* If the paragraph is supposed to be indented a certain way,
2787 then discard all of the pending whitespace. Otherwise, we
2788 let the whitespace stay. */
2789 if (!paragraph_start_indent)
2790 indent (pending_indent);
2791 pending_indent = 0;
2792 }
2793
2794 if ((output_column += len) > fill_column)
2795 {
2796 if (filling_enabled)
2797 {
2798 int temp = output_paragraph_offset;
2799 while (--temp > 0 && output_paragraph[temp] != '\n')
2800 {
2801 /* If we have found a space, we have the place to break
2802 the line. */
2803 if (output_paragraph[temp] == ' ')
2804 {
2805 /* Remove trailing whitespace from output. */
2806 while (temp && whitespace (output_paragraph[temp - 1]))
2807 temp--;
2808
2809 output_paragraph[temp++] = '\n';
2810
2811 /* We have correctly broken the line where we want
2812 to. What we don't want is spaces following where
2813 we have decided to break the line. We get rid of
2814 them. */
2815 {
2816 int t1 = temp;
2817
2818 for (;; t1++)
2819 {
2820 if (t1 == output_paragraph_offset)
2821 {
2822 if (whitespace (character))
2823 suppress_insert = 1;
2824 break;
2825 }
2826 if (!whitespace (output_paragraph[t1]))
2827 break;
2828 }
2829
2830 if (t1 != temp)
2831 {
2832 adjust_braces_following (temp, (- (t1 - temp)));
2833 strncpy ((char *) &output_paragraph[temp],
2834 (char *) &output_paragraph[t1],
2835 (output_paragraph_offset - t1));
2836 output_paragraph_offset -= (t1 - temp);
2837 }
2838 }
2839
2840 /* Filled, but now indent if that is right. */
2841 if (indented_fill && current_indent)
2842 {
2843 int buffer_len = ((output_paragraph_offset - temp)
2844 + current_indent);
2845 char *temp_buffer = (char *)xmalloc (buffer_len);
2846 int indentation = 0;
2847
2848 /* We have to shift any markers that are in
2849 front of the wrap point. */
2850 adjust_braces_following (temp, current_indent);
2851
2852 while (current_indent > 0 &&
2853 indentation != current_indent)
2854 temp_buffer[indentation++] = ' ';
2855
2856 strncpy ((char *) &temp_buffer[current_indent],
2857 (char *) &output_paragraph[temp],
2858 buffer_len - current_indent);
2859
2860 if (output_paragraph_offset + buffer_len
2861 >= paragraph_buffer_len)
2862 {
2863 unsigned char *tt = xrealloc
2864 (output_paragraph,
2865 (paragraph_buffer_len += buffer_len));
2866 output_paragraph = tt;
2867 }
2868 strncpy ((char *) &output_paragraph[temp],
2869 temp_buffer, buffer_len);
2870 output_paragraph_offset += current_indent;
2871 free (temp_buffer);
2872 }
2873 output_column = 0;
2874 while (temp < output_paragraph_offset)
2875 output_column +=
2876 get_char_len (output_paragraph[temp++]);
2877 output_column += len;
2878 break;
2879 }
2880 }
2881 }
2882 }
2883
2884 if (!suppress_insert)
2885 {
2886 insert (character);
2887 last_inserted_character = character;
2888 }
2889 last_char_was_newline = 0;
2890 line_already_broken = 0;
2891 }
2892 }
2893 }
2894
2895 /* Insert CHARACTER into OUTPUT_PARAGRAPH. */
2896 static void
2897 insert (int character)
2898 {
2899 output_paragraph[output_paragraph_offset++] = character;
2900 if (output_paragraph_offset == paragraph_buffer_len)
2901 {
2902 output_paragraph =
2903 xrealloc (output_paragraph, (paragraph_buffer_len += 100));
2904 }
2905 }
2906
2907 /* Remove upto COUNT characters of whitespace from the
2908 the current output line. If COUNT is less than zero,
2909 then remove until none left. */
2910 static void
2911 kill_self_indent (int count)
2912 {
2913 /* Handle infinite case first. */
2914 if (count < 0)
2915 {
2916 output_column = 0;
2917 while (output_paragraph_offset)
2918 {
2919 if (whitespace (output_paragraph[output_paragraph_offset - 1]))
2920 output_paragraph_offset--;
2921 else
2922 break;
2923 }
2924 }
2925 else
2926 {
2927 while (output_paragraph_offset && count--)
2928 if (whitespace (output_paragraph[output_paragraph_offset - 1]))
2929 output_paragraph_offset--;
2930 else
2931 break;
2932 }
2933 }
2934
2935 /* Non-zero means do not honor calls to flush_output (). */
2936 static int flushing_ignored = 0;
2937
2938 /* Prevent calls to flush_output () from having any effect. */
2939 static void
2940 inhibit_output_flushing (void)
2941 {
2942 flushing_ignored++;
2943 }
2944
2945 /* Allow calls to flush_output () to write the paragraph data. */
2946 static void
2947 uninhibit_output_flushing (void)
2948 {
2949 flushing_ignored--;
2950 }
2951
2952 static void
2953 flush_output (void)
2954 {
2955 register int i;
2956
2957 if (!output_paragraph_offset || flushing_ignored)
2958 return;
2959
2960 for (i = 0; i < output_paragraph_offset; i++)
2961 {
2962 if (output_paragraph[i] == (unsigned char)(' ' | 0x80) ||
2963 output_paragraph[i] == (unsigned char)('\t' | 0x80) ||
2964 output_paragraph[i] == (unsigned char)('\n' | 0x80) ||
2965 sentence_ender (UNMETA (output_paragraph[i])))
2966 output_paragraph[i] &= 0x7f;
2967 }
2968
2969 fwrite (output_paragraph, 1, output_paragraph_offset, output_stream);
2970
2971 output_position += output_paragraph_offset;
2972 output_paragraph_offset = 0;
2973 }
2974
2975 /* How to close a paragraph controlling the number of lines between
2976 this one and the last one. */
2977
2978 /* Paragraph spacing is controlled by this variable. It is the number of
2979 blank lines that you wish to appear between paragraphs. A value of
2980 1 creates a single blank line between paragraphs. */
2981 int paragraph_spacing = DEFAULT_PARAGRAPH_SPACING;
2982
2983 /* Close the current paragraph, leaving no blank lines between them. */
2984 static void
2985 close_single_paragraph (void)
2986 {
2987 close_paragraph_with_lines (0);
2988 }
2989
2990 /* Close a paragraph after an insertion has ended. */
2991 static void
2992 close_insertion_paragraph (void)
2993 {
2994 if (!insertion_paragraph_closed)
2995 {
2996 /* Close the current paragraph, breaking the line. */
2997 close_single_paragraph ();
2998
2999 /* Start a new paragraph here, inserting whatever indention is correct
3000 for the now current insertion level (one above the one that we are
3001 ending). */
3002 start_paragraph ();
3003
3004 /* Tell close_paragraph () that the previous line has already been
3005 broken, so it should insert one less newline. */
3006 line_already_broken = 1;
3007
3008 /* Let functions such as add_char () know that we have already found a
3009 newline. */
3010 ignore_blank_line ();
3011 }
3012 else
3013 {
3014 /* If the insertion paragraph is closed already, then we are seeing
3015 two `@end' commands in a row. Note that the first one we saw was
3016 handled in the first part of this if-then-else clause, and at that
3017 time start_paragraph () was called, partially to handle the proper
3018 indentation of the current line. However, the indentation level
3019 may have just changed again, so we may have to outdent the current
3020 line to the new indentation level. */
3021 if (current_indent < output_column)
3022 kill_self_indent (output_column - current_indent);
3023 }
3024
3025 insertion_paragraph_closed = 1;
3026 }
3027
3028 static void
3029 close_paragraph_with_lines (int lines)
3030 {
3031 int old_spacing = paragraph_spacing;
3032 paragraph_spacing = lines;
3033 close_paragraph ();
3034 paragraph_spacing = old_spacing;
3035 }
3036
3037 /* Close the currently open paragraph. */
3038 static void
3039 close_paragraph (void)
3040 {
3041 register int i;
3042
3043 /* The insertion paragraph is no longer closed. */
3044 insertion_paragraph_closed = 0;
3045
3046 if (paragraph_is_open && !must_start_paragraph)
3047 {
3048 register int tindex, c;
3049
3050 tindex = output_paragraph_offset;
3051
3052 /* Back up to last non-newline/space character, forcing all such
3053 subsequent characters to be newlines. This isn't strictly
3054 necessary, but a couple of functions use the presence of a newline
3055 to make decisions. */
3056 for (tindex = output_paragraph_offset - 1; tindex >= 0; --tindex)
3057 {
3058 c = output_paragraph[tindex];
3059
3060 if (c == ' '|| c == '\n')
3061 output_paragraph[tindex] = '\n';
3062 else
3063 break;
3064 }
3065
3066 /* All trailing whitespace is ignored. */
3067 output_paragraph_offset = ++tindex;
3068
3069 /* Break the line if that is appropriate. */
3070 if (paragraph_spacing >= 0)
3071 insert ('\n');
3072
3073 /* Add as many blank lines as is specified in PARAGRAPH_SPACING. */
3074 if (!force_flush_right)
3075 {
3076 for (i = 0; i < (paragraph_spacing - line_already_broken); i++)
3077 insert ('\n');
3078 }
3079
3080 /* If we are doing flush right indentation, then do it now
3081 on the paragraph (really a single line). */
3082 if (force_flush_right)
3083 do_flush_right_indentation ();
3084
3085 flush_output ();
3086 paragraph_is_open = 0;
3087 no_indent = 0;
3088 output_column = 0;
3089 }
3090 ignore_blank_line ();
3091 }
3092
3093 /* Make the last line just read look as if it were only a newline. */
3094 static void
3095 ignore_blank_line (void)
3096 {
3097 last_inserted_character = '\n';
3098 last_char_was_newline = 1;
3099 }
3100
3101 /* Align the end of the text in output_paragraph with fill_column. */
3102 static void
3103 do_flush_right_indentation (void)
3104 {
3105 char *temp;
3106 int temp_len;
3107
3108 kill_self_indent (-1);
3109
3110 if (output_paragraph[0] != '\n')
3111 {
3112 output_paragraph[output_paragraph_offset] = '\0';
3113
3114 if (output_paragraph_offset < fill_column)
3115 {
3116 register int i;
3117
3118 if (fill_column >= paragraph_buffer_len)
3119 output_paragraph =
3120 xrealloc (output_paragraph,
3121 (paragraph_buffer_len += fill_column));
3122
3123 temp_len = strlen ((char *)output_paragraph);
3124 temp = (char *)xmalloc (temp_len + 1);
3125 memcpy (temp, (char *)output_paragraph, temp_len);
3126
3127 for (i = 0; i < fill_column - output_paragraph_offset; i++)
3128 output_paragraph[i] = ' ';
3129
3130 memcpy ((char *)output_paragraph + i, temp, temp_len);
3131 free (temp);
3132 output_paragraph_offset = fill_column;
3133 }
3134 }
3135 }
3136
3137 /* Begin a new paragraph. */
3138 static void
3139 start_paragraph (void)
3140 {
3141 /* First close existing one. */
3142 if (paragraph_is_open)
3143 close_paragraph ();
3144
3145 /* In either case, the insertion paragraph is no longer closed. */
3146 insertion_paragraph_closed = 0;
3147
3148 /* However, the paragraph is open! */
3149 paragraph_is_open = 1;
3150
3151 /* If we MUST_START_PARAGRAPH, that simply means that start_paragraph ()
3152 had to be called before we would allow any other paragraph operations
3153 to have an effect. */
3154 if (!must_start_paragraph)
3155 {
3156 int amount_to_indent = 0;
3157
3158 /* If doing indentation, then insert the appropriate amount. */
3159 if (!no_indent)
3160 {
3161 if (inhibit_paragraph_indentation)
3162 {
3163 amount_to_indent = current_indent;
3164 if (inhibit_paragraph_indentation < 0)
3165 inhibit_paragraph_indentation++;
3166 }
3167 else if (paragraph_start_indent < 0)
3168 amount_to_indent = current_indent;
3169 else
3170 amount_to_indent = current_indent + paragraph_start_indent;
3171
3172 if (amount_to_indent >= output_column)
3173 {
3174 amount_to_indent -= output_column;
3175 indent (amount_to_indent);
3176 output_column += amount_to_indent;
3177 }
3178 }
3179 }
3180 else
3181 must_start_paragraph = 0;
3182 }
3183
3184 /* Insert the indentation specified by AMOUNT. */
3185 static void
3186 indent (int amount)
3187 {
3188 register BRACE_ELEMENT *elt = brace_stack;
3189
3190 /* For every START_POS saved within the brace stack which will be affected
3191 by this indentation, bump that start pos forward. */
3192 while (elt)
3193 {
3194 if (elt->pos >= output_paragraph_offset)
3195 elt->pos += amount;
3196 elt = elt->next;
3197 }
3198
3199 while (--amount >= 0)
3200 insert (' ');
3201 }
3202
3203 /* Search forward for STRING in input_text.
3204 FROM says where where to start. */
3205 static int
3206 search_forward (char *string, int from)
3207 {
3208 int len = strlen (string);
3209
3210 while (from < size_of_input_text)
3211 {
3212 if (strncmp (input_text + from, string, len) == 0)
3213 return (from);
3214 from++;
3215 }
3216 return (-1);
3217 }
3218
3219 /* Whoops, Unix doesn't have strcasecmp. */
3220
3221 /* Case independent string compare. */
3222 #if !defined (HAVE_STRCASECMP)
3223 static int
3224 strcasecmp (char *string1, char *string2)
3225 {
3226 char ch1, ch2;
3227
3228 for (;;)
3229 {
3230 ch1 = *string1++;
3231 ch2 = *string2++;
3232
3233 if (!(ch1 | ch2))
3234 return (0);
3235
3236 ch1 = coerce_to_upper (ch1);
3237 ch2 = coerce_to_upper (ch2);
3238
3239 if (ch1 != ch2)
3240 return (ch1 - ch2);
3241 }
3242 }
3243 #endif /* !HAVE_STRCASECMP */
3244
3245 enum insertion_type { menu, quotation, lisp, smalllisp, example,
3246 smallexample, display, itemize, format, enumerate, cartouche, table,
3247 ftable, vtable, group, ifinfo, flushleft, flushright, ifset, ifclear, deffn,
3248 defun, defmac, defspec, defvr, defvar, defopt, deftypefn,
3249 deftypefun, deftypevr, deftypevar, defcv, defivar, defop, defmethod,
3250 deftypemethod, deftp, bad_type };
3251
3252 char *insertion_type_names[] = { "menu", "quotation", "lisp",
3253 "smalllisp", "example", "smallexample", "display", "itemize",
3254 "format", "enumerate", "cartouche", "table", "ftable", "vtable", "group",
3255 "ifinfo", "flushleft", "flushright", "ifset", "ifclear", "deffn",
3256 "defun", "defmac", "defspec", "defvr", "defvar", "defopt",
3257 "deftypefn", "deftypefun", "deftypevr", "deftypevar", "defcv",
3258 "defivar", "defop", "defmethod", "deftypemethod", "deftp",
3259 "bad_type" };
3260
3261 int insertion_level = 0;
3262 typedef struct istack_elt
3263 {
3264 struct istack_elt *next;
3265 char *item_function;
3266 char *filename;
3267 int line_number;
3268 int filling_enabled;
3269 int indented_fill;
3270 enum insertion_type insertion;
3271 int inhibited;
3272 } INSERTION_ELT;
3273
3274 INSERTION_ELT *insertion_stack = (INSERTION_ELT *) NULL;
3275
3276 static void
3277 init_insertion_stack (void)
3278 {
3279 insertion_stack = (INSERTION_ELT *) NULL;
3280 }
3281
3282 /* Return the type of the current insertion. */
3283 static enum insertion_type
3284 current_insertion_type (void)
3285 {
3286 if (!insertion_level)
3287 return (bad_type);
3288 else
3289 return (insertion_stack->insertion);
3290 }
3291
3292 /* Return a pointer to the string which is the function to wrap around
3293 items. */
3294 static char *
3295 current_item_function (void)
3296 {
3297 register int level, done;
3298 register INSERTION_ELT *elt;
3299
3300 level = insertion_level;
3301 elt = insertion_stack;
3302 done = 0;
3303
3304 /* Skip down through the stack until we find a non-conditional insertion. */
3305 while (!done && (elt != NULL))
3306 {
3307 switch (elt->insertion)
3308 {
3309 case ifinfo:
3310 case ifset:
3311 case ifclear:
3312 case cartouche:
3313 elt = elt->next;
3314 level--;
3315 break;
3316
3317 default:
3318 done = 1;
3319 }
3320 }
3321
3322 if (!level)
3323 return ((char *) NULL);
3324 else
3325 return (elt->item_function);
3326 }
3327
3328 static char *
3329 get_item_function (void)
3330 {
3331 char *item_function;
3332 get_rest_of_line (&item_function);
3333 backup_input_pointer ();
3334 canon_white (item_function);
3335 return (item_function);
3336 }
3337
3338 /* Push the state of the current insertion on the stack. */
3339 static void
3340 push_insertion (enum insertion_type type, char *item_function)
3341 {
3342 INSERTION_ELT *new = (INSERTION_ELT *) xmalloc (sizeof (INSERTION_ELT));
3343
3344 new->item_function = item_function;
3345 new->filling_enabled = filling_enabled;
3346 new->indented_fill = indented_fill;
3347 new->insertion = type;
3348 new->line_number = line_number;
3349 new->filename = strdup (input_filename);
3350 new->inhibited = inhibit_paragraph_indentation;
3351 new->next = insertion_stack;
3352 insertion_stack = new;
3353 insertion_level++;
3354 }
3355
3356 /* Pop the value on top of the insertion stack into the
3357 global variables. */
3358 static void
3359 pop_insertion (void)
3360 {
3361 INSERTION_ELT *temp = insertion_stack;
3362
3363 if (temp == (INSERTION_ELT *) NULL)
3364 return;
3365
3366 inhibit_paragraph_indentation = temp->inhibited;
3367 filling_enabled = temp->filling_enabled;
3368 indented_fill = temp->indented_fill;
3369 free_and_clear (&(temp->item_function));
3370 free_and_clear (&(temp->filename));
3371 insertion_stack = insertion_stack->next;
3372 free (temp);
3373 insertion_level--;
3374 }
3375
3376 /* Return a pointer to the print name of this
3377 enumerated type. */
3378 static char *
3379 insertion_type_pname (enum insertion_type type)
3380 {
3381 if ((int) type < (int) bad_type)
3382 return (insertion_type_names[(int) type]);
3383 else
3384 return ("Broken-Type in insertion_type_pname");
3385 }
3386
3387 /* Return the insertion_type associated with NAME.
3388 If the type is not one of the known ones, return BAD_TYPE. */
3389 static enum insertion_type
3390 find_type_from_name (char *name)
3391 {
3392 int indecks = 0;
3393 while (indecks < (int) bad_type)
3394 {
3395 if (strcmp (name, insertion_type_names[indecks]) == 0)
3396 return (enum insertion_type) indecks;
3397 indecks++;
3398 }
3399 return (bad_type);
3400 }
3401
3402 static void
3403 do_nothing (void)
3404 {
3405 }
3406
3407 static int
3408 defun_insertion (enum insertion_type type)
3409 {
3410 return
3411 ((type == deffn)
3412 || (type == defun)
3413 || (type == defmac)
3414 || (type == defspec)
3415 || (type == defvr)
3416 || (type == defvar)
3417 || (type == defopt)
3418 || (type == deftypefn)
3419 || (type == deftypefun)
3420 || (type == deftypevr)
3421 || (type == deftypevar)
3422 || (type == defcv)
3423 || (type == defivar)
3424 || (type == defop)
3425 || (type == defmethod)
3426 || (type == deftypemethod)
3427 || (type == deftp));
3428 }
3429
3430 /* MAX_NS is the maximum nesting level for enumerations. I picked 100
3431 which seemed reasonable. This doesn't control the number of items,
3432 just the number of nested lists. */
3433 #define max_stack_depth 100
3434 #define ENUM_DIGITS 1
3435 #define ENUM_ALPHA 2
3436 typedef struct {
3437 int enumtype;
3438 int enumval;
3439 } DIGIT_ALPHA;
3440
3441 DIGIT_ALPHA enumstack[max_stack_depth];
3442 int enumstack_offset = 0;
3443 int current_enumval = 1;
3444 int current_enumtype = ENUM_DIGITS;
3445 char *enumeration_arg = (char *)NULL;
3446
3447 static void
3448 start_enumerating (int at, int type)
3449 {
3450 if ((enumstack_offset + 1) == max_stack_depth)
3451 {
3452 line_error ("Enumeration stack overflow");
3453 return;
3454 }
3455 enumstack[enumstack_offset].enumtype = current_enumtype;
3456 enumstack[enumstack_offset].enumval = current_enumval;
3457 enumstack_offset++;
3458 current_enumval = at;
3459 current_enumtype = type;
3460 }
3461
3462 static void
3463 stop_enumerating (void)
3464 {
3465 --enumstack_offset;
3466 if (enumstack_offset < 0)
3467 enumstack_offset = 0;
3468
3469 current_enumval = enumstack[enumstack_offset].enumval;
3470 current_enumtype = enumstack[enumstack_offset].enumtype;
3471 }
3472
3473 /* Place a letter or digits into the output stream. */
3474 static void
3475 enumerate_item (void)
3476 {
3477 char temp[10];
3478
3479 if (current_enumtype == ENUM_ALPHA)
3480 {
3481 if (current_enumval == ('z' + 1) || current_enumval == ('Z' + 1))
3482 {
3483 current_enumval = ((current_enumval - 1) == 'z' ? 'a' : 'A');
3484 warning ("Lettering overflow, restarting at %c", current_enumval);
3485 }
3486 sprintf (temp, "%c. ", current_enumval);
3487 }
3488 else
3489 sprintf (temp, "%d. ", current_enumval);
3490
3491 indent (output_column += (current_indent - strlen (temp)));
3492 add_word (temp);
3493 current_enumval++;
3494 }
3495
3496 /* This is where the work for all the "insertion" style
3497 commands is done. A huge switch statement handles the
3498 various setups, and generic code is on both sides. */
3499 static void
3500 begin_insertion (enum insertion_type type)
3501 {
3502 int no_discard = 0;
3503
3504 if (defun_insertion (type))
3505 {
3506 push_insertion (type, strdup (""));
3507 no_discard++;
3508 }
3509 else
3510 push_insertion (type, get_item_function ());
3511
3512 switch (type)
3513 {
3514 case menu:
3515 if (!no_headers)
3516 close_paragraph ();
3517
3518 filling_enabled = no_indent = 0;
3519 inhibit_paragraph_indentation = 1;
3520
3521 if (!no_headers)
3522 add_word ("* Menu:\n");
3523
3524 in_menu++;
3525 no_discard++;
3526 break;
3527
3528 /* I think @quotation is meant to do filling.
3529 If you don't want filling, then use @example. */
3530 case quotation:
3531 close_single_paragraph ();
3532 last_char_was_newline = no_indent = 0;
3533 indented_fill = filling_enabled = 1;
3534 inhibit_paragraph_indentation = 1;
3535 current_indent += default_indentation_increment;
3536 break;
3537
3538 case display:
3539 case example:
3540 case smallexample:
3541 case lisp:
3542 case smalllisp:
3543 /* Just like @example, but no indentation. */
3544 case format:
3545
3546 close_single_paragraph ();
3547 inhibit_paragraph_indentation = 1;
3548 in_fixed_width_font++;
3549 filling_enabled = 0;
3550 last_char_was_newline = 0;
3551
3552 if (type != format)
3553 current_indent += default_indentation_increment;
3554
3555 break;
3556
3557 case table:
3558 case ftable:
3559 case vtable:
3560 case itemize:
3561 close_single_paragraph ();
3562 current_indent += default_indentation_increment;
3563 filling_enabled = indented_fill = 1;
3564 #if defined (INDENT_PARAGRAPHS_IN_TABLE)
3565 inhibit_paragraph_indentation = 0;
3566 #else
3567 inhibit_paragraph_indentation = 1;
3568 #endif /* !INDENT_PARAGRAPHS_IN_TABLE */
3569
3570 /* Make things work for losers who forget the itemize syntax. */
3571 if (allow_lax_format && (type == itemize))
3572 {
3573 if (!(*insertion_stack->item_function))
3574 {
3575 free (insertion_stack->item_function);
3576 insertion_stack->item_function = strdup ("@bullet");
3577 insertion_stack->item_function[0] = COMMAND_PREFIX;
3578 }
3579 }
3580
3581 if (!*insertion_stack->item_function)
3582 {
3583 line_error ("%s requires an argument: the formatter for %citem",
3584 insertion_type_pname (type), COMMAND_PREFIX);
3585 }
3586 break;
3587
3588 case enumerate:
3589 close_single_paragraph ();
3590 no_indent = 0;
3591 #if defined (INDENT_PARAGRAPHS_IN_TABLE)
3592 inhibit_paragraph_indentation = 0;
3593 #else
3594 inhibit_paragraph_indentation = 1;
3595 #endif /* !INDENT_PARAGRAPHS_IN_TABLE */
3596
3597 current_indent += default_indentation_increment;
3598 filling_enabled = indented_fill = 1;
3599
3600 if (isdigit (*enumeration_arg))
3601 start_enumerating (atoi (enumeration_arg), ENUM_DIGITS);
3602 else
3603 start_enumerating (*enumeration_arg, ENUM_ALPHA);
3604 break;
3605
3606 /* Does nothing special in makeinfo. */
3607 case group:
3608 /* Only close the paragraph if we are not inside of an @example. */
3609 if (!insertion_stack->next ||
3610 insertion_stack->next->insertion != example)
3611 close_single_paragraph ();
3612 break;
3613
3614 /* Insertions that are no-ops in info, but do something in TeX. */
3615 case ifinfo:
3616 case ifset:
3617 case ifclear:
3618 case cartouche:
3619 if (in_menu)
3620 no_discard++;
3621 break;
3622
3623 case deffn:
3624 case defun:
3625 case defmac:
3626 case defspec:
3627 case defvr:
3628 case defvar:
3629 case defopt:
3630 case deftypefn:
3631 case deftypefun:
3632 case deftypevr:
3633 case deftypevar:
3634 case defcv:
3635 case defivar:
3636 case defop:
3637 case defmethod:
3638 case deftypemethod:
3639 case deftp:
3640 inhibit_paragraph_indentation = 1;
3641 filling_enabled = indented_fill = 1;
3642 current_indent += default_indentation_increment;
3643 no_indent = 0;
3644 break;
3645
3646 case flushleft:
3647 close_single_paragraph ();
3648 inhibit_paragraph_indentation = 1;
3649 filling_enabled = indented_fill = no_indent = 0;
3650 break;
3651
3652 case flushright:
3653 close_single_paragraph ();
3654 filling_enabled = indented_fill = no_indent = 0;
3655 inhibit_paragraph_indentation = 1;
3656 force_flush_right++;
3657 break;
3658
3659 default:
3660 break;
3661 }
3662
3663 if (!no_discard)
3664 discard_until ("\n");
3665 }
3666
3667 /* Try to end the insertion with the specified TYPE.
3668 TYPE, with a value of bad_type, gets translated to match
3669 the value currently on top of the stack.
3670 Otherwise, if TYPE doesn't match the top of the insertion stack,
3671 give error. */
3672 static void
3673 end_insertion (enum insertion_type type)
3674 {
3675 enum insertion_type temp_type;
3676
3677 if (!insertion_level)
3678 return;
3679
3680 temp_type = current_insertion_type ();
3681
3682 if (type == bad_type)
3683 type = temp_type;
3684
3685 if (type != temp_type)
3686 {
3687 line_error
3688 ("`%cend' expected `%s', but saw `%s'", COMMAND_PREFIX,
3689 insertion_type_pname (temp_type), insertion_type_pname (type));
3690 return;
3691 }
3692
3693 pop_insertion ();
3694
3695 switch (type)
3696 {
3697 /* Insertions which have no effect on paragraph formatting. */
3698 case ifinfo:
3699 case ifset:
3700 case ifclear:
3701 break;
3702
3703 case menu:
3704 in_menu--; /* No longer hacking menus. */
3705 if (!no_headers)
3706 close_insertion_paragraph ();
3707 break;
3708
3709 case enumerate:
3710 stop_enumerating ();
3711 close_insertion_paragraph ();
3712 current_indent -= default_indentation_increment;
3713 break;
3714
3715 case flushleft:
3716 case group:
3717 case cartouche:
3718 close_insertion_paragraph ();
3719 break;
3720
3721 case format:
3722 case display:
3723 case example:
3724 case smallexample:
3725 case lisp:
3726 case smalllisp:
3727 case quotation:
3728
3729 /* @quotation is the only one of the above without a fixed width
3730 font. */
3731 if (type != quotation)
3732 in_fixed_width_font--;
3733
3734 /* @format is the only fixed_width insertion without a change
3735 in indentation. */
3736 if (type != format)
3737 current_indent -= default_indentation_increment;
3738
3739 /* The ending of one of these insertions always marks the
3740 start of a new paragraph. */
3741 close_insertion_paragraph ();
3742 break;
3743
3744 case table:
3745 case ftable:
3746 case vtable:
3747 case itemize:
3748 current_indent -= default_indentation_increment;
3749 break;
3750
3751 case flushright:
3752 force_flush_right--;
3753 close_insertion_paragraph ();
3754 break;
3755
3756 /* Handle the @defun style insertions with a default clause. */
3757 default:
3758 current_indent -= default_indentation_increment;
3759 close_insertion_paragraph ();
3760 break;
3761 }
3762 }
3763
3764 /* Insertions cannot cross certain boundaries, such as node beginnings. In
3765 code that creates such boundaries, you should call discard_insertions ()
3766 before doing anything else. It prints the errors for you, and cleans up
3767 the insertion stack. */
3768 static void
3769 discard_insertions (void)
3770 {
3771 int real_line_number = line_number;
3772 while (insertion_stack)
3773 {
3774 if (insertion_stack->insertion == ifinfo ||
3775 insertion_stack->insertion == ifset ||
3776 insertion_stack->insertion == ifclear ||
3777 insertion_stack->insertion == cartouche)
3778 break;
3779 else
3780 {
3781 char *offender;
3782 char *current_filename;
3783
3784 current_filename = input_filename;
3785 offender = (char *)insertion_type_pname (insertion_stack->insertion);
3786 input_filename = insertion_stack->filename;
3787 line_number = insertion_stack->line_number;
3788 line_error ("This `%s' doesn't have a matching `%cend %s'", offender,
3789 COMMAND_PREFIX, offender);
3790 input_filename = current_filename;
3791 pop_insertion ();
3792 }
3793 }
3794 line_number = real_line_number;
3795 }
3796
3797 /* The actual commands themselves. */
3798
3799 /* Commands which insert themselves. */
3800 static void
3801 insert_self (void)
3802 {
3803 add_word (command);
3804 }
3805
3806 /* Force a line break in the output. */
3807 static void
3808 cm_asterisk (void)
3809 {
3810 close_single_paragraph ();
3811 #if !defined (ASTERISK_NEW_PARAGRAPH)
3812 cm_noindent ();
3813 #endif /* ASTERISK_NEW_PARAGRAPH */
3814 }
3815
3816 /* Insert ellipsis. */
3817 static void
3818 cm_dots (int arg)
3819 {
3820 if (arg == START)
3821 add_word ("...");
3822 }
3823
3824 static void
3825 cm_bullet (int arg)
3826 {
3827 if (arg == START)
3828 add_char ('*');
3829 }
3830
3831 static void
3832 cm_minus (int arg)
3833 {
3834 if (arg == START)
3835 add_char ('-');
3836 }
3837
3838 /* Insert "TeX". */
3839 static void
3840 cm_TeX (int arg)
3841 {
3842 if (arg == START)
3843 add_word ("TeX");
3844 }
3845
3846 static void
3847 cm_copyright (int arg)
3848 {
3849 if (arg == START)
3850 add_word ("(C)");
3851 }
3852
3853 #if defined (__osf__)
3854 #define LOCALTIME_CAST(x) (time_t *)(x)
3855 #else
3856 #define LOCALTIME_CAST(x) (x)
3857 #endif
3858
3859 static void
3860 cm_today (int arg)
3861 {
3862 static char * months [12] =
3863 { "January", "February", "March", "April", "May", "June", "July",
3864 "August", "September", "October", "November", "December" };
3865 if (arg == START)
3866 {
3867 long timer = time (0);
3868 struct tm *ts = localtime (LOCALTIME_CAST (&timer));
3869 add_word_args
3870 ("%d %s %d",
3871 (ts -> tm_mday),
3872 (months [ts -> tm_mon]),
3873 ((ts -> tm_year) + 1900));
3874 }
3875 }
3876
3877 static void
3878 cm_code (int arg)
3879 {
3880 extern int printing_index;
3881
3882 if (printing_index)
3883 return;
3884
3885 if (arg == START)
3886 {
3887 in_fixed_width_font++;
3888 add_char ('`');
3889 }
3890 else
3891 {
3892 add_word ("'");
3893 in_fixed_width_font--;
3894 }
3895 }
3896
3897 static void
3898 cm_samp (int arg)
3899 {
3900 cm_code (arg);
3901 }
3902
3903 static void
3904 cm_file (int arg)
3905 {
3906 cm_code (arg);
3907 }
3908
3909 static void
3910 cm_kbd (int arg)
3911 {
3912 cm_code (arg);
3913 }
3914
3915 static void
3916 cm_key (int arg)
3917 {
3918 }
3919
3920 /* Convert the character at position into CTL. */
3921 static void
3922 cm_ctrl (int arg, int start, int end)
3923 {
3924 /* Should we allow multiple character arguments? I think yes. */
3925 if (arg == END)
3926 {
3927 register int i, character;
3928 #if defined (NO_MULTIPLE_CTRL)
3929 if ((end - start) != 1)
3930 line_error ("%c%s expects a single character as an argument",
3931 COMMAND_PREFIX, command);
3932 else
3933 #endif
3934 for (i = start; i < end; i++)
3935 {
3936 character = output_paragraph[i];
3937
3938 if (isletter (character))
3939 output_paragraph[i] = CTL (coerce_to_upper (character));
3940 }
3941 }
3942 }
3943
3944 /* Small Caps in makeinfo just does all caps. */
3945 static void
3946 cm_sc (int arg, int start_pos, int end_pos)
3947 {
3948 if (arg == END)
3949 {
3950 while (start_pos < end_pos)
3951 {
3952 output_paragraph[start_pos] =
3953 coerce_to_upper (output_paragraph[start_pos]);
3954 start_pos++;
3955 }
3956 }
3957 }
3958
3959 /* @var in makeinfo just uppercases the text. */
3960 static void
3961 cm_var (int arg, int start_pos, int end_pos)
3962 {
3963 if (arg == END)
3964 {
3965 while (start_pos < end_pos)
3966 {
3967 output_paragraph[start_pos] =
3968 coerce_to_upper (output_paragraph[start_pos]);
3969 start_pos++;
3970 }
3971 }
3972 }
3973
3974 static void
3975 cm_dfn (int arg, int position)
3976 {
3977 add_char ('"');
3978 }
3979
3980 static void
3981 cm_emph (int arg)
3982 {
3983 add_char ('*');
3984 }
3985
3986 static void
3987 cm_strong (int arg, int position)
3988 {
3989 cm_emph (arg);
3990 }
3991
3992 static void
3993 cm_cite (int arg, int position)
3994 {
3995 if (arg == START)
3996 add_word ("`");
3997 else
3998 add_word ("'");
3999 }
4000
4001 /* Current text is italicized. */
4002 static void
4003 cm_italic (int arg, int start, int end)
4004 {
4005 }
4006
4007 /* Current text is highlighted. */
4008 static void
4009 cm_bold (int arg, int start, int end)
4010 {
4011 cm_italic (arg, start, end);
4012 }
4013
4014 /* Current text is in roman font. */
4015 static void
4016 cm_roman (int arg, int start, int end)
4017 {
4018 }
4019
4020 /* Current text is in roman font. */
4021 static void
4022 cm_titlefont (int arg, int start, int end)
4023 {
4024 }
4025
4026 /* Italicize titles. */
4027 static void
4028 cm_title (int arg, int start, int end)
4029 {
4030 cm_italic (arg, start, end);
4031 }
4032
4033 /* @refill is a NOP. */
4034 static void
4035 cm_refill (void)
4036 {
4037 }
4038
4039 /* Prevent the argument from being split across two lines. */
4040 static void
4041 cm_w (int arg, int start, int end)
4042 {
4043 if (arg == START)
4044 non_splitting_words++;
4045 else
4046 non_splitting_words--;
4047 }
4048
4049
4050 /* Explain that this command is obsolete, thus the user shouldn't
4051 do anything with it. */
4052 static void
4053 cm_obsolete (int arg, int start, int end)
4054 {
4055 if (arg == START)
4056 warning ("The command `%c%s' is obsolete", COMMAND_PREFIX, command);
4057 }
4058
4059 /* Insert the text following input_text_offset up to the end of the line
4060 in a new, separate paragraph. Directly underneath it, insert a
4061 line of WITH_CHAR, the same length of the inserted text. */
4062 static void
4063 insert_and_underscore (int with_char)
4064 {
4065 register int i, len;
4066 int old_no_indent, starting_pos, ending_pos;
4067 char *temp;
4068
4069 close_paragraph ();
4070 filling_enabled = indented_fill = 0;
4071 old_no_indent = no_indent;
4072 no_indent = 1;
4073
4074 #if defined (HAVE_MACROS)
4075 if (macro_expansion_output_stream)
4076 append_to_expansion_output (input_text_offset + 1);
4077 #endif /* HAVE_MACROS */
4078
4079 get_rest_of_line (&temp);
4080
4081 starting_pos = output_position + output_paragraph_offset;
4082 #if defined (HAVE_MACROS)
4083 if (macro_expansion_output_stream)
4084 {
4085 char *temp1;
4086
4087 temp1 = (char *)xmalloc (2 + strlen (temp));
4088 sprintf (temp1, "%s\n", temp);
4089 remember_itext (input_text, input_text_offset);
4090 me_execute_string (temp1);
4091 free (temp1);
4092 }
4093 else
4094 #endif /* HAVE_MACROS */
4095 execute_string ("%s\n", temp);
4096
4097 ending_pos = output_position + output_paragraph_offset;
4098 free (temp);
4099
4100 len = (ending_pos - starting_pos) - 1;
4101 for (i = 0; i < len; i++)
4102 add_char (with_char);
4103 insert ('\n');
4104 close_paragraph ();
4105 filling_enabled = 1;
4106 no_indent = old_no_indent;
4107 }
4108
4109 /* Here is a structure which associates sectioning commands with
4110 an integer, hopefully to reflect the `depth' of the current
4111 section. */
4112 struct {
4113 char *name;
4114 int level;
4115 } section_alist[] = {
4116 { "unnumberedsubsubsec", 5 },
4117 { "unnumberedsubsec", 4 },
4118 { "unnumberedsec", 3 },
4119 { "unnumbered", 2 },
4120 { "appendixsubsubsec", 5 },
4121 { "appendixsubsec", 4 },
4122 { "appendixsec", 3 },
4123 { "appendixsection", 3 },
4124 { "appendix", 2 },
4125 { "subsubsec", 5 },
4126 { "subsubsection", 5 },
4127 { "subsection", 4 },
4128 { "section", 3 },
4129 { "chapter", 2 },
4130 { "top", 1 },
4131
4132 { (char *)NULL, 0 }
4133 };
4134
4135 /* Amount to offset the name of sectioning commands to levels by. */
4136 int section_alist_offset = 0;
4137
4138 /* Shift the meaning of @section to @chapter. */
4139 static void
4140 cm_raisesections (void)
4141 {
4142 discard_until ("\n");
4143 section_alist_offset--;
4144 }
4145
4146 /* Shift the meaning of @chapter to @section. */
4147 static void
4148 cm_lowersections (void)
4149 {
4150 discard_until ("\n");
4151 section_alist_offset++;
4152 }
4153
4154 /* Return an integer which identifies the type section present in TEXT. */
4155 static int
4156 what_section (char *text)
4157 {
4158 register int i, j;
4159 char *t;
4160
4161 find_section_command:
4162 for (j = 0; text[j] && cr_or_whitespace (text[j]); j++);
4163 if (text[j] != COMMAND_PREFIX)
4164 return (-1);
4165
4166 text = text + j + 1;
4167
4168 /* We skip @c, @comment, and @?index commands. */
4169 if ((strncmp (text, "comment", strlen ("comment")) == 0) ||
4170 (text[0] == 'c' && cr_or_whitespace (text[1])) ||
4171 (strcmp (text + 1, "index") == 0))
4172 {
4173 while (*text++ != '\n');
4174 goto find_section_command;
4175 }
4176
4177 /* Handle italicized sectioning commands. */
4178 if (*text == 'i')
4179 text++;
4180
4181 for (j = 0; text[j] && !cr_or_whitespace (text[j]); j++);
4182
4183 for (i = 0; (t = section_alist[i].name); i++)
4184 {
4185 if (j == strlen (t) && strncmp (t, text, j) == 0)
4186 {
4187 int return_val;
4188
4189 return_val = (section_alist[i].level + section_alist_offset);
4190
4191 if (return_val < 0)
4192 return_val = 0;
4193 else if (return_val > 5)
4194 return_val = 5;
4195 return (return_val);
4196 }
4197 }
4198 return (-1);
4199 }
4200
4201 /* Set the level of @top to LEVEL. Return the old level of @top. */
4202 static int
4203 set_top_section_level (int level)
4204 {
4205 register int i, result = -1;
4206
4207 for (i = 0; section_alist[i].name; i++)
4208 if (strcmp (section_alist[i].name, "top") == 0)
4209 {
4210 result = section_alist[i].level;
4211 section_alist[i].level = level;
4212 break;
4213 }
4214 return (result);
4215 }
4216
4217 /* Treat this just like @unnumbered. The only difference is
4218 in node defaulting. */
4219 static void
4220 cm_top (void)
4221 {
4222 /* It is an error to have more than one @top. */
4223 if (top_node_seen)
4224 {
4225 TAG_ENTRY *tag = tag_table;
4226
4227 line_error ("There already is a node having %ctop as a section",
4228 COMMAND_PREFIX);
4229
4230 while (tag != (TAG_ENTRY *)NULL)
4231 {
4232 if ((tag->flags & IS_TOP))
4233 {
4234 int old_line_number = line_number;
4235 char *old_input_filename = input_filename;
4236
4237 line_number = tag->line_no;
4238 input_filename = tag->filename;
4239 line_error ("Here is the %ctop node", COMMAND_PREFIX);
4240 input_filename = old_input_filename;
4241 line_number = old_line_number;
4242 return;
4243 }
4244 tag = tag->next_ent;
4245 }
4246 }
4247 else
4248 {
4249 top_node_seen = 1;
4250
4251 /* It is an error to use @top before you have used @node. */
4252 if (!tag_table)
4253 {
4254 char *top_name;
4255
4256 get_rest_of_line (&top_name);
4257 free (top_name);
4258 line_error ("%ctop used before %cnode, defaulting to %s",
4259 COMMAND_PREFIX, COMMAND_PREFIX, top_name);
4260 execute_string ("@node Top, , (dir), (dir)\n@top %s\n", top_name);
4261 return;
4262 }
4263
4264 cm_unnumbered ();
4265
4266 /* The most recently defined node is the top node. */
4267 tag_table->flags |= IS_TOP;
4268
4269 /* Now set the logical hierarchical level of the Top node. */
4270 {
4271 int orig_offset = input_text_offset;
4272
4273 input_text_offset = search_forward (node_search_string, orig_offset);
4274
4275 if (input_text_offset > 0)
4276 {
4277 int this_section;
4278
4279 /* We have encountered a non-top node, so mark that one exists. */
4280 non_top_node_seen = 1;
4281
4282 /* Move to the end of this line, and find out what the
4283 sectioning command is here. */
4284 while (input_text[input_text_offset] != '\n')
4285 input_text_offset++;
4286
4287 if (input_text_offset < size_of_input_text)
4288 input_text_offset++;
4289
4290 this_section = what_section (input_text + input_text_offset);
4291
4292 /* If we found a sectioning command, then give the top section
4293 a level of this section - 1. */
4294 if (this_section != -1)
4295 set_top_section_level (this_section - 1);
4296 }
4297 input_text_offset = orig_offset;
4298 }
4299 }
4300 }
4301
4302 /* Organized by level commands. That is, "*" == chapter, "=" == section. */
4303 char *scoring_characters = "*=-.";
4304
4305 static void
4306 sectioning_underscore (char *commanned)
4307 {
4308 char character;
4309 char *temp;
4310 int level;
4311
4312 temp = (char *)xmalloc (2 + strlen (commanned));
4313 temp[0] = COMMAND_PREFIX;
4314 strcpy (&temp[1], commanned);
4315 level = what_section (temp);
4316 free (temp);
4317 level -= 2;
4318
4319 if (level < 0)
4320 level = 0;
4321
4322 character = scoring_characters[level];
4323
4324 insert_and_underscore (character);
4325 }
4326
4327 /* The remainder of the text on this line is a chapter heading. */
4328 static void
4329 cm_chapter (void)
4330 {
4331 sectioning_underscore ("chapter");
4332 }
4333
4334 /* The remainder of the text on this line is a section heading. */
4335 static void
4336 cm_section (void)
4337 {
4338 sectioning_underscore ("section");
4339 }
4340
4341 /* The remainder of the text on this line is a subsection heading. */
4342 static void
4343 cm_subsection (void)
4344 {
4345 sectioning_underscore ("subsection");
4346 }
4347
4348 /* The remainder of the text on this line is a subsubsection heading. */
4349 static void
4350 cm_subsubsection (void)
4351 {
4352 sectioning_underscore ("subsubsection");
4353 }
4354
4355 /* The remainder of the text on this line is an unnumbered heading. */
4356 static void
4357 cm_unnumbered (void)
4358 {
4359 cm_chapter ();
4360 }
4361
4362 /* The remainder of the text on this line is an unnumbered section heading. */
4363 static void
4364 cm_unnumberedsec (void)
4365 {
4366 cm_section ();
4367 }
4368
4369 /* The remainder of the text on this line is an unnumbered
4370 subsection heading. */
4371 static void
4372 cm_unnumberedsubsec (void)
4373 {
4374 cm_subsection ();
4375 }
4376
4377 /* The remainder of the text on this line is an unnumbered
4378 subsubsection heading. */
4379 static void
4380 cm_unnumberedsubsubsec (void)
4381 {
4382 cm_subsubsection ();
4383 }
4384
4385 /* The remainder of the text on this line is an appendix heading. */
4386 static void
4387 cm_appendix (void)
4388 {
4389 cm_chapter ();
4390 }
4391
4392 /* The remainder of the text on this line is an appendix section heading. */
4393 static void
4394 cm_appendixsec (void)
4395 {
4396 cm_section ();
4397 }
4398
4399 /* The remainder of the text on this line is an appendix subsection heading. */
4400 static void
4401 cm_appendixsubsec (void)
4402 {
4403 cm_subsection ();
4404 }
4405
4406 /* The remainder of the text on this line is an appendix
4407 subsubsection heading. */
4408 static void
4409 cm_appendixsubsubsec (void)
4410 {
4411 cm_subsubsection ();
4412 }
4413
4414 /* Compatibility functions substitute for chapter, section, etc. */
4415 static void
4416 cm_majorheading (void)
4417 {
4418 cm_chapheading ();
4419 }
4420
4421 static void
4422 cm_chapheading (void)
4423 {
4424 cm_chapter ();
4425 }
4426
4427 static void
4428 cm_heading (void)
4429 {
4430 cm_section ();
4431 }
4432
4433 static void
4434 cm_subheading (void)
4435 {
4436 cm_subsection ();
4437 }
4438
4439 static void
4440 cm_subsubheading (void)
4441 {
4442 cm_subsubsection ();
4443 }
4444
4445
4446 /* **************************************************************** */
4447 /* */
4448 /* Adding nodes, and making tags */
4449 /* */
4450 /* **************************************************************** */
4451
4452 /* Start a new tag table. */
4453 static void
4454 init_tag_table (void)
4455 {
4456 while (tag_table != (TAG_ENTRY *) NULL)
4457 {
4458 TAG_ENTRY *temp = tag_table;
4459 free (temp->node);
4460 free (temp->prev);
4461 free (temp->next);
4462 free (temp->up);
4463 tag_table = tag_table->next_ent;
4464 free (temp);
4465 }
4466 }
4467
4468 static void
4469 write_tag_table (void)
4470 {
4471 write_tag_table_internal (0); /* Not indirect. */
4472 }
4473
4474 static void
4475 write_tag_table_indirect (void)
4476 {
4477 write_tag_table_internal (1);
4478 }
4479
4480 /* Write out the contents of the existing tag table.
4481 INDIRECT_P says how to format the output. */
4482 static void
4483 write_tag_table_internal (int indirect_p)
4484 {
4485 TAG_ENTRY *node = tag_table;
4486 int old_indent = no_indent;
4487
4488 no_indent = 1;
4489 filling_enabled = 0;
4490 must_start_paragraph = 0;
4491 close_paragraph ();
4492
4493 if (!indirect_p)
4494 {
4495 no_indent = 1;
4496 insert ('\n');
4497 }
4498
4499 add_word_args ("\037\nTag Table:\n%s", indirect_p ? "(Indirect)\n" : "");
4500
4501 while (node != (TAG_ENTRY *) NULL)
4502 {
4503 execute_string ("Node: %s", node->node);
4504 add_word_args ("\177%d\n", node->position);
4505 node = node->next_ent;
4506 }
4507
4508 add_word ("\037\nEnd Tag Table\n");
4509 flush_output ();
4510 no_indent = old_indent;
4511 }
4512
4513 static char *
4514 get_node_token (void)
4515 {
4516 char *string;
4517
4518 get_until_in_line (",", &string);
4519
4520 if (curchar () == ',')
4521 input_text_offset++;
4522
4523 canon_white (string);
4524
4525 /* Force all versions of "top" to be "Top". */
4526 normalize_node_name (string);
4527
4528 return (string);
4529 }
4530
4531 /* Convert "top" and friends into "Top". */
4532 static void
4533 normalize_node_name (char *string)
4534 {
4535 if (strcasecmp (string, "Top") == 0)
4536 strcpy (string, "Top");
4537 }
4538
4539 /* Look up NAME in the tag table, and return the associated
4540 tag_entry. If the node is not in the table return NULL. */
4541 static TAG_ENTRY *
4542 find_node (char *name)
4543 {
4544 TAG_ENTRY *tag = tag_table;
4545
4546 while (tag != (TAG_ENTRY *) NULL)
4547 {
4548 if (strcmp (tag->node, name) == 0)
4549 return (tag);
4550 tag = tag->next_ent;
4551 }
4552 return ((TAG_ENTRY *) NULL);
4553 }
4554
4555 /* Remember NODE and associates. */
4556 static void
4557 remember_node (char *node, char *prev, char *next, char *up, int position,
4558 int line_no, int no_warn)
4559 {
4560 /* Check for existence of this tag already. */
4561 if (validating)
4562 {
4563 register TAG_ENTRY *tag = find_node (node);
4564 if (tag)
4565 {
4566 line_error ("Node `%s' multiply defined (%d is first definition)",
4567 node, tag->line_no);
4568 return;
4569 }
4570 }
4571
4572 /* First, make this the current node. */
4573 current_node = node;
4574
4575 /* Now add it to the list. */
4576 {
4577 TAG_ENTRY *new = (TAG_ENTRY *) xmalloc (sizeof (TAG_ENTRY));
4578 new->node = node;
4579 new->prev = prev;
4580 new->next = next;
4581 new->up = up;
4582 new->position = position;
4583 new->line_no = line_no;
4584 new->filename = node_filename;
4585 new->touched = 0; /* not yet referenced. */
4586 new->flags = 0;
4587 if (no_warn)
4588 new->flags |= NO_WARN;
4589 new->next_ent = tag_table;
4590 tag_table = new;
4591 }
4592 }
4593
4594 /* The order is: nodename, nextnode, prevnode, upnode.
4595 If all of the NEXT, PREV, and UP fields are empty, they are defaulted.
4596 You must follow a node command which has those fields defaulted
4597 with a sectioning command (e.g. @chapter) giving the "level" of that node.
4598 It is an error not to do so.
4599 The defaults come from the menu in this node's parent. */
4600 static void
4601 cm_node (void)
4602 {
4603 char *node, *prev, *next, *up;
4604 int new_node_pos, defaulting, this_section, no_warn = 0;
4605 extern int already_outputting_pending_notes;
4606
4607 if (strcmp (command, "nwnode") == 0)
4608 no_warn = 1;
4609
4610 /* Get rid of unmatched brace arguments from previous commands. */
4611 discard_braces ();
4612
4613 /* There also might be insertions left lying around that haven't been
4614 ended yet. Do that also. */
4615 discard_insertions ();
4616
4617 if (!already_outputting_pending_notes)
4618 {
4619 close_paragraph ();
4620 output_pending_notes ();
4621 free_pending_notes ();
4622 }
4623
4624 filling_enabled = indented_fill = 0;
4625 new_node_pos = output_position;
4626 current_footnote_number = 1;
4627
4628 #if defined (HAVE_MACROS)
4629 if (macro_expansion_output_stream)
4630 append_to_expansion_output (input_text_offset + 1);
4631 #endif /* HAVE_MACROS */
4632
4633 node = get_node_token ();
4634 next = get_node_token ();
4635 prev = get_node_token ();
4636 up = get_node_token ();
4637
4638 #if defined (HAVE_MACROS)
4639 if (macro_expansion_output_stream)
4640 remember_itext (input_text, input_text_offset);
4641 #endif /* HAVE_MACROS */
4642
4643 no_indent = 1;
4644 if (!no_headers)
4645 {
4646 add_word_args ("\037\nFile: %s, Node: ", pretty_output_filename);
4647
4648 #if defined (HAVE_MACROS)
4649 if (macro_expansion_output_stream)
4650 me_execute_string (node);
4651 else
4652 #endif /* HAVE_MACROS */
4653 execute_string ("%s", node);
4654 filling_enabled = indented_fill = 0;
4655 }
4656
4657 /* Check for defaulting of this node's next, prev, and up fields. */
4658 defaulting = ((strlen (next) == 0) &&
4659 (strlen (prev) == 0) &&
4660 (strlen (up) == 0));
4661
4662 this_section = what_section (input_text + input_text_offset);
4663
4664 /* If we are defaulting, then look at the immediately following
4665 sectioning command (error if none) to determine the node's
4666 level. Find the node that contains the menu mentioning this node
4667 that is one level up (error if not found). That node is the "Up"
4668 of this node. Default the "Next" and "Prev" from the menu. */
4669 if (defaulting)
4670 {
4671 NODE_REF *last_ref = (NODE_REF *)NULL;
4672 NODE_REF *ref = node_references;
4673
4674 if ((this_section < 0) && (strcmp (node, "Top") != 0))
4675 {
4676 char *polite_section_name = "top";
4677 int i;
4678
4679 for (i = 0; section_alist[i].name; i++)
4680 if (section_alist[i].level == current_section + 1)
4681 {
4682 polite_section_name = section_alist[i].name;
4683 break;
4684 }
4685
4686 line_error
4687 ("Node `%s' requires a sectioning command (e.g. %c%s)",
4688 node, COMMAND_PREFIX, polite_section_name);
4689 }
4690 else
4691 {
4692 if (strcmp (node, "Top") == 0)
4693 {
4694 /* Default the NEXT pointer to be the first menu item in
4695 this node, if there is a menu in this node. We have to
4696 try very hard to find the menu, as it may be obscured
4697 by execution_strings which are on the filestack. For
4698 every member of the filestack which has a FILENAME
4699 member which is identical to the current INPUT_FILENAME,
4700 search forward from that offset. */
4701 int saved_input_text_offset = input_text_offset;
4702 int saved_size_of_input_text = size_of_input_text;
4703 char *saved_input_text = input_text;
4704 FSTACK *next_file = filestack;
4705
4706 int orig_offset, orig_size;
4707
4708 /* No matter what, make this file point back at `(dir)'. */
4709 free (up); up = strdup ("(dir)");
4710
4711 while (1)
4712 {
4713 orig_offset = input_text_offset;
4714 orig_size =
4715 search_forward (node_search_string, orig_offset);
4716
4717 if (orig_size < 0)
4718 orig_size = size_of_input_text;
4719
4720 input_text_offset =
4721 search_forward (menu_search_string, orig_offset);
4722
4723 if (input_text_offset > -1)
4724 {
4725 char *nodename_from_menu = (char *)NULL;
4726
4727 input_text_offset =
4728 search_forward ("\n* ", input_text_offset);
4729
4730 if (input_text_offset != -1)
4731 nodename_from_menu = glean_node_from_menu (0);
4732
4733 if (nodename_from_menu)
4734 {
4735 free (next); next = nodename_from_menu;
4736 break;
4737 }
4738 }
4739
4740 /* We got here, so it hasn't been found yet. Try
4741 the next file on the filestack if there is one. */
4742 if (next_file &&
4743 (strcmp (next_file->filename, input_filename) == 0))
4744 {
4745 input_text = next_file->text;
4746 input_text_offset = next_file->offset;
4747 size_of_input_text = next_file->size;
4748 next_file = next_file->next;
4749 }
4750 else
4751 {
4752 /* No more input files to check. */
4753 break;
4754 }
4755 }
4756
4757 input_text = saved_input_text;
4758 input_text_offset = saved_input_text_offset;
4759 size_of_input_text = saved_size_of_input_text;
4760 }
4761 }
4762
4763 /* Fix the level of the menu references in the Top node, iff it
4764 was declared with @top, and no subsequent reference was found. */
4765 if (top_node_seen && !non_top_node_seen)
4766 {
4767 /* Then this is the first non-@top node seen. */
4768 int level;
4769
4770 level = set_top_section_level (this_section - 1);
4771 non_top_node_seen = 1;
4772
4773 while (ref)
4774 {
4775 if (ref->section == level)
4776 ref->section = this_section - 1;
4777 ref = ref->next;
4778 }
4779
4780 ref = node_references;
4781 }
4782
4783 while (ref)
4784 {
4785 if (ref->section == (this_section - 1) &&
4786 ref->type == menu_reference &&
4787 strcmp (ref->node, node) == 0)
4788 {
4789 char *containing_node = ref->containing_node;
4790
4791 free (up);
4792 up = strdup (containing_node);
4793
4794 if (last_ref &&
4795 last_ref->type == menu_reference &&
4796 (strcmp (last_ref->containing_node,
4797 containing_node) == 0))
4798 {
4799 free (next);
4800 next = strdup (last_ref->node);
4801 }
4802
4803 while ((ref->section == this_section - 1) &&
4804 (ref->next) &&
4805 (ref->next->type != menu_reference))
4806 ref = ref->next;
4807
4808 if (ref->next && ref->type == menu_reference &&
4809 (strcmp (ref->next->containing_node,
4810 containing_node) == 0))
4811 {
4812 free (prev);
4813 prev = strdup (ref->next->node);
4814 }
4815 else if (!ref->next &&
4816 strcasecmp (ref->containing_node, "Top") == 0)
4817 {
4818 free (prev);
4819 prev = strdup (ref->containing_node);
4820 }
4821 break;
4822 }
4823 last_ref = ref;
4824 ref = ref->next;
4825 }
4826 }
4827
4828 #if defined (HAVE_MACROS)
4829 /* Insert the correct args if we are expanding macros, and the node's
4830 pointers weren't defaulted. */
4831 if (macro_expansion_output_stream && !defaulting)
4832 {
4833 char *temp;
4834 int op_orig = output_paragraph_offset;
4835
4836 temp = (char *)xmalloc (3 + strlen (next));
4837 sprintf (temp, ", %s", next);
4838 me_execute_string (temp);
4839 free (temp);
4840
4841 temp = (char *)xmalloc (3 + strlen (prev));
4842 sprintf (temp, ", %s", prev);
4843 me_execute_string (temp);
4844 free (temp);
4845
4846 temp = (char *)xmalloc (4 + strlen (up));
4847 sprintf (temp, ", %s", up);
4848 me_execute_string (temp);
4849 free (temp);
4850
4851 output_paragraph_offset = op_orig;
4852 }
4853 #endif /* HAVE_MACROS */
4854
4855 if (!no_headers)
4856 {
4857 #if defined (HAVE_MACROS)
4858 if (macro_expansion_output_stream)
4859 me_inhibit_expansion++;
4860 #endif /* HAVE_MACROS */
4861
4862 if (*next)
4863 {
4864 execute_string (", Next: %s", next);
4865 filling_enabled = indented_fill = 0;
4866 }
4867
4868 if (*prev)
4869 {
4870 execute_string (", Prev: %s", prev);
4871 filling_enabled = indented_fill = 0;
4872 }
4873
4874 if (*up)
4875 {
4876 execute_string (", Up: %s", up);
4877 filling_enabled = indented_fill = 0;
4878 }
4879 #if defined (HAVE_MACROS)
4880 if (macro_expansion_output_stream)
4881 me_inhibit_expansion--;
4882 #endif /* HAVE_MACROS */
4883 }
4884
4885 close_paragraph ();
4886 no_indent = 0;
4887
4888 if (!*node)
4889 {
4890 line_error ("No node name specified for `%c%s' command",
4891 COMMAND_PREFIX, command);
4892 free (node);
4893 free (next);
4894 free (prev);
4895 free (up);
4896 }
4897 else
4898 {
4899 if (!*next) { free (next); next = (char *)NULL; }
4900 if (!*prev) { free (prev); prev = (char *)NULL; }
4901 if (!*up) { free (up); up = (char *)NULL; }
4902 remember_node (node, prev, next, up, new_node_pos, line_number, no_warn);
4903 }
4904
4905 /* Change the section only if there was a sectioning command. */
4906 if (this_section >= 0)
4907 current_section = this_section;
4908
4909 filling_enabled = 1;
4910 }
4911
4912 /* Validation of an info file.
4913 Scan through the list of tag entrys touching the Prev, Next, and Up
4914 elements of each. It is an error not to be able to touch one of them,
4915 except in the case of external node references, such as "(DIR)".
4916
4917 If the Prev is different from the Up,
4918 then the Prev node must have a Next pointing at this node.
4919
4920 Every node except Top must have an Up.
4921 The Up node must contain some sort of reference, other than a Next,
4922 to this node.
4923
4924 If the Next is different from the Next of the Up,
4925 then the Next node must have a Prev pointing at this node. */
4926 static void
4927 validate_file (TAG_ENTRY *tag_tayble)
4928 {
4929 char *old_input_filename = input_filename;
4930 TAG_ENTRY *tags = tag_tayble;
4931
4932 while (tags != (TAG_ENTRY *) NULL)
4933 {
4934 register TAG_ENTRY *temp_tag;
4935
4936 input_filename = tags->filename;
4937 line_number = tags->line_no;
4938
4939 /* If this is a "no warn" node, don't validate it in any way. */
4940 if (tags->flags & NO_WARN)
4941 {
4942 tags = tags->next_ent;
4943 continue;
4944 }
4945
4946 /* If this node has a Next, then make sure that the Next exists. */
4947 if (tags->next)
4948 {
4949 validate (tags->next, tags->line_no, "Next");
4950
4951 /* If the Next node exists, and there is no Up, then make
4952 sure that the Prev of the Next points back. */
4953 if ((temp_tag = find_node (tags->next)))
4954 {
4955 char *prev;
4956
4957 if (temp_tag->flags & NO_WARN)
4958 {
4959 /* Do nothing if we aren't supposed to issue warnings
4960 about this node. */
4961 }
4962 else
4963 {
4964 prev = temp_tag->prev;
4965 if (!prev || (strcmp (prev, tags->node) != 0))
4966 {
4967 line_error ("Node `%s''s Next field not pointed back to",
4968 tags->node);
4969 line_number = temp_tag->line_no;
4970 input_filename = temp_tag->filename;
4971 line_error
4972 ("This node (`%s') is the one with the bad `Prev'",
4973 temp_tag->node);
4974 input_filename = tags->filename;
4975 line_number = tags->line_no;
4976 temp_tag->flags |= PREV_ERROR;
4977 }
4978 }
4979 }
4980 }
4981
4982 /* Validate the Prev field if there is one, and we haven't already
4983 complained about it in some way. You don't have to have a Prev
4984 field at this stage. */
4985 if (!(tags->flags & PREV_ERROR) && tags->prev)
4986 {
4987 int valid = validate (tags->prev, tags->line_no, "Prev");
4988
4989 if (!valid)
4990 tags->flags |= PREV_ERROR;
4991 else
4992 {
4993 /* If the Prev field is not the same as the Up field,
4994 then the node pointed to by the Prev field must have
4995 a Next field which points to this node. */
4996 if (tags->up && (strcmp (tags->prev, tags->up) != 0))
4997 {
4998 temp_tag = find_node (tags->prev);
4999
5000 /* If we aren't supposed to issue warnings about the
5001 target node, do nothing. */
5002 if (!temp_tag || (temp_tag->flags & NO_WARN))
5003 {
5004 /* Do nothing. */
5005 }
5006 else
5007 {
5008 if (!temp_tag->next ||
5009 (strcmp (temp_tag->next, tags->node) != 0))
5010 {
5011 line_error
5012 ("Node `%s''s Prev field not pointed back to",
5013 tags->node);
5014 line_number = temp_tag->line_no;
5015 input_filename = temp_tag->filename;
5016 line_error
5017 ("This node (`%s') is the one with the bad `Next'",
5018 temp_tag->node);
5019 input_filename = tags->filename;
5020 line_number = tags->line_no;
5021 temp_tag->flags |= NEXT_ERROR;
5022 }
5023 }
5024 }
5025 }
5026 }
5027
5028 if (!tags->up && (strcasecmp (tags->node, "Top") != 0))
5029 line_error ("Node `%s' is missing an \"Up\" field", tags->node);
5030 else if (tags->up)
5031 {
5032 int valid = validate (tags->up, tags->line_no, "Up");
5033
5034 /* If node X has Up: Y, then warn if Y fails to have a menu item
5035 or note pointing at X, if Y isn't of the form "(Y)". */
5036 if (valid && *tags->up != '(')
5037 {
5038 NODE_REF *nref, *tref, *list;
5039
5040 tref = (NODE_REF *) NULL;
5041 list = node_references;
5042
5043 for (;;)
5044 {
5045 if (!(nref = find_node_reference (tags->node, list)))
5046 break;
5047
5048 if (strcmp (nref->containing_node, tags->up) == 0)
5049 {
5050 if (nref->type != menu_reference)
5051 {
5052 tref = nref;
5053 list = nref->next;
5054 }
5055 else
5056 break;
5057 }
5058 list = nref->next;
5059 }
5060
5061 if (!nref)
5062 {
5063 temp_tag = find_node (tags->up);
5064 line_number = temp_tag->line_no;
5065 input_filename = temp_tag->filename;
5066 if (!tref)
5067 line_error (
5068 "`%s' has an Up field of `%s', but `%s' has no menu item for `%s'",
5069 tags->node, tags->up, tags->up, tags->node);
5070 line_number = tags->line_no;
5071 input_filename = tags->filename;
5072 }
5073 }
5074 }
5075 tags = tags->next_ent;
5076 }
5077
5078 validate_other_references (node_references);
5079 /* We have told the user about the references which didn't exist.
5080 Now tell him about the nodes which aren't referenced. */
5081
5082 tags = tag_tayble;
5083 while (tags != (TAG_ENTRY *) NULL)
5084 {
5085 /* If this node is a "no warn" node, do nothing. */
5086 if (tags->flags & NO_WARN)
5087 {
5088 tags = tags->next_ent;
5089 continue;
5090 }
5091
5092 /* Special hack. If the node in question appears to have
5093 been referenced more than REFERENCE_WARNING_LIMIT times,
5094 give a warning. */
5095 if (tags->touched > reference_warning_limit)
5096 {
5097 input_filename = tags->filename;
5098 line_number = tags->line_no;
5099 warning ("Node `%s' has been referenced %d times",
5100 tags->node, tags->touched);
5101 }
5102
5103 if (tags->touched == 0)
5104 {
5105 input_filename = tags->filename;
5106 line_number = tags->line_no;
5107
5108 /* Notice that the node "Top" is special, and doesn't have to
5109 be referenced. */
5110 if (strcasecmp (tags->node, "Top") != 0)
5111 warning ("Unreferenced node `%s'", tags->node);
5112 }
5113 tags = tags->next_ent;
5114 }
5115 input_filename = old_input_filename;
5116 }
5117
5118 /* Return 1 if tag correctly validated, or 0 if not. */
5119 static int
5120 validate (char *tag, int line, char *label)
5121 {
5122 TAG_ENTRY *result;
5123
5124 /* If there isn't a tag to verify, or if the tag is in another file,
5125 then it must be okay. */
5126 if (!tag || !*tag || *tag == '(')
5127 return (1);
5128
5129 /* Otherwise, the tag must exist. */
5130 result = find_node (tag);
5131
5132 if (!result)
5133 {
5134 line_number = line;
5135 line_error (
5136 "Validation error. `%s' field points to node `%s', which doesn't exist",
5137 label, tag);
5138 return (0);
5139 }
5140 result->touched++;
5141 return (1);
5142 }
5143
5144 /* Split large output files into a series of smaller files. Each file
5145 is pointed to in the tag table, which then gets written out as the
5146 original file. The new files have the same name as the original file
5147 with a "-num" attached. SIZE is the largest number of bytes to allow
5148 in any single split file. */
5149 static void
5150 split_file (char *filename, int size)
5151 {
5152 char *root_filename, *root_pathname;
5153 char *the_file;
5154 struct stat fileinfo;
5155 long file_size;
5156 char *the_header;
5157 int header_size;
5158
5159 /* Can only do this to files with tag tables. */
5160 if (!tag_table)
5161 return;
5162
5163 if (size == 0)
5164 size = DEFAULT_SPLIT_SIZE;
5165
5166 if ((stat (filename, &fileinfo) != 0) ||
5167 (((long) fileinfo.st_size) < SPLIT_SIZE_THRESHOLD))
5168 return;
5169 file_size = (long) fileinfo.st_size;
5170
5171 the_file = find_and_load (filename);
5172 if (!the_file)
5173 return;
5174
5175 root_filename = filename_part (filename);
5176 root_pathname = pathname_part (filename);
5177
5178 if (!root_pathname)
5179 root_pathname = strdup ("");
5180
5181 /* Start splitting the file. Walk along the tag table
5182 outputting sections of the file. When we have written
5183 all of the nodes in the tag table, make the top-level
5184 pointer file, which contains indirect pointers and
5185 tags for the nodes. */
5186 {
5187 int which_file = 1;
5188 TAG_ENTRY *tags = tag_table;
5189 char *indirect_info = (char *)NULL;
5190
5191 /* Remember the `header' of this file. The first tag in the file is
5192 the bottom of the header; the top of the file is the start. */
5193 the_header = (char *)xmalloc (1 + (header_size = tags->position));
5194 memcpy (the_header, the_file, header_size);
5195
5196 while (tags)
5197 {
5198 int file_top, file_bot, limit;
5199
5200 /* Have to include the Control-_. */
5201 file_top = file_bot = tags->position;
5202 limit = file_top + size;
5203
5204 /* If the rest of this file is only one node, then
5205 that is the entire subfile. */
5206 if (!tags->next_ent)
5207 {
5208 int i = tags->position + 1;
5209 char last_char = the_file[i];
5210
5211 while (i < file_size)
5212 {
5213 if ((the_file[i] == '\037') &&
5214 ((last_char == '\n') ||
5215 (last_char == '\014')))
5216 break;
5217 else
5218 last_char = the_file[i];
5219 i++;
5220 }
5221 file_bot = i;
5222 tags = tags->next_ent;
5223 goto write_region;
5224 }
5225
5226 /* Otherwise, find the largest number of nodes that can fit in
5227 this subfile. */
5228 for (; tags; tags = tags->next_ent)
5229 {
5230 if (!tags->next_ent)
5231 {
5232 /* This entry is the last node. Search forward for the end
5233 of this node, and that is the end of this file. */
5234 int i = tags->position + 1;
5235 char last_char = the_file[i];
5236
5237 while (i < file_size)
5238 {
5239 if ((the_file[i] == '\037') &&
5240 ((last_char == '\n') ||
5241 (last_char == '\014')))
5242 break;
5243 else
5244 last_char = the_file[i];
5245 i++;
5246 }
5247 file_bot = i;
5248
5249 if (file_bot < limit)
5250 {
5251 tags = tags->next_ent;
5252 goto write_region;
5253 }
5254 else
5255 {
5256 /* Here we want to write out everything before the last
5257 node, and then write the last node out in a file
5258 by itself. */
5259 file_bot = tags->position;
5260 goto write_region;
5261 }
5262 }
5263
5264 if (tags->next_ent->position > limit)
5265 {
5266 if (tags->position == file_top)
5267 tags = tags->next_ent;
5268
5269 file_bot = tags->position;
5270
5271 write_region:
5272 {
5273 int fd;
5274 char *split_filename;
5275
5276 split_filename = (char *) xmalloc
5277 (10 + strlen (root_pathname) + strlen (root_filename));
5278 sprintf
5279 (split_filename,
5280 #ifdef MSDOS
5281 "%s%s.%d", root_pathname, root_filename, which_file);
5282 #else /* !MSDOS */
5283 "%s%s-%d", root_pathname, root_filename, which_file);
5284 #endif /* !MSDOS */
5285
5286 fd = open
5287 (split_filename, O_WRONLY | O_TRUNC | O_CREAT, 0666);
5288
5289 if ((fd < 0) ||
5290 (write (fd, the_header, header_size) != header_size) ||
5291 (write (fd, the_file + file_top, file_bot - file_top)
5292 != (file_bot - file_top)) ||
5293 ((close (fd)) < 0))
5294 {
5295 perror (split_filename);
5296 if (fd != -1)
5297 close (fd);
5298 exit (FATAL);
5299 }
5300
5301 if (!indirect_info)
5302 {
5303 indirect_info = the_file + file_top;
5304 sprintf (indirect_info, "\037\nIndirect:\n");
5305 indirect_info += strlen (indirect_info);
5306 }
5307
5308 #ifdef MSDOS
5309 sprintf (indirect_info, "%s.%d: %d\n",
5310 #else
5311 sprintf (indirect_info, "%s-%d: %d\n",
5312 #endif
5313 root_filename, which_file, file_top);
5314
5315 free (split_filename);
5316 indirect_info += strlen (indirect_info);
5317 which_file++;
5318 break;
5319 }
5320 }
5321 }
5322 }
5323
5324 /* We have sucessfully created the subfiles. Now write out the
5325 original again. We must use `output_stream', or
5326 write_tag_table_indirect () won't know where to place the output. */
5327 output_stream = fopen (filename, "w");
5328 if (!output_stream)
5329 {
5330 perror (filename);
5331 exit (FATAL);
5332 }
5333
5334 {
5335 int distance = indirect_info - the_file;
5336 fwrite (the_file, 1, distance, output_stream);
5337
5338 /* Inhibit newlines. */
5339 paragraph_is_open = 0;
5340
5341 write_tag_table_indirect ();
5342 fclose (output_stream);
5343 free (the_header);
5344 free (the_file);
5345 return;
5346 }
5347 }
5348 }
5349
5350 /* Some menu hacking. This is used to remember menu references while
5351 reading the input file. After the output file has been written, if
5352 validation is on, then we use the contents of NODE_REFERENCES as a
5353 list of nodes to validate. */
5354 static char *
5355 reftype_type_string (enum reftype type)
5356 {
5357 switch (type)
5358 {
5359 case menu_reference:
5360 return ("Menu");
5361 case followed_reference:
5362 return ("Followed-Reference");
5363 default:
5364 return ("Internal-bad-reference-type");
5365 }
5366 }
5367
5368 /* Remember this node name for later validation use. */
5369 static void
5370 remember_node_reference (char *node, int line, enum reftype type)
5371 {
5372 NODE_REF *temp = (NODE_REF *) xmalloc (sizeof (NODE_REF));
5373
5374 temp->next = node_references;
5375 temp->node = strdup (node);
5376 temp->line_no = line;
5377 temp->section = current_section;
5378 temp->type = type;
5379 temp->containing_node = (current_node ? strdup (current_node) : 0);
5380 temp->filename = node_filename;
5381
5382 node_references = temp;
5383 }
5384
5385 static void
5386 validate_other_references (register NODE_REF *ref_list)
5387 {
5388 char *old_input_filename = input_filename;
5389
5390 while (ref_list != (NODE_REF *) NULL)
5391 {
5392 input_filename = ref_list->filename;
5393 validate (ref_list->node, ref_list->line_no,
5394 reftype_type_string (ref_list->type));
5395 ref_list = ref_list->next;
5396 }
5397 input_filename = old_input_filename;
5398 }
5399
5400 /* Find NODE in REF_LIST. */
5401 static NODE_REF *
5402 find_node_reference (char *node, register NODE_REF *ref_list)
5403 {
5404 while (ref_list)
5405 {
5406 if (strcmp (node, ref_list->node) == 0)
5407 break;
5408 ref_list = ref_list->next;
5409 }
5410 return (ref_list);
5411 }
5412
5413 static void
5414 free_node_references (void)
5415 {
5416 register NODE_REF *list, *temp;
5417
5418 list = node_references;
5419
5420 while (list)
5421 {
5422 temp = list;
5423 free (list->node);
5424 free (list->containing_node);
5425 list = list->next;
5426 free (temp);
5427 }
5428 node_references = (NODE_REF *) NULL;
5429 }
5430
5431 /* This function gets called at the start of every line while inside of
5432 a menu. It checks to see if the line starts with "* ", and if so,
5433 remembers the node reference that this menu refers to.
5434 input_text_offset is at the \n just before the line start. */
5435 #define menu_starter "* "
5436 static char *
5437 glean_node_from_menu (int remember_reference)
5438 {
5439 int i, orig_offset = input_text_offset;
5440 char *nodename;
5441
5442 if (strncmp (&input_text[input_text_offset + 1],
5443 menu_starter,
5444 strlen (menu_starter)) != 0)
5445 return ((char *)NULL);
5446 else
5447 input_text_offset += strlen (menu_starter) + 1;
5448
5449 get_until_in_line (":", &nodename);
5450 if (curchar () == ':')
5451 input_text_offset++;
5452 canon_white (nodename);
5453
5454 if (curchar () == ':')
5455 goto save_node;
5456
5457 free (nodename);
5458 get_rest_of_line (&nodename);
5459
5460 /* Special hack: If the nodename follows the menu item name,
5461 then we have to read the rest of the line in order to find
5462 out what the nodename is. But we still have to read the
5463 line later, in order to process any formatting commands that
5464 might be present. So un-count the carriage return that has just
5465 been counted. */
5466 line_number--;
5467
5468 isolate_nodename (nodename);
5469
5470 save_node:
5471 input_text_offset = orig_offset;
5472 normalize_node_name (nodename);
5473 i = strlen (nodename);
5474 if (i && nodename[i - 1] == ':')
5475 nodename[i - 1] = '\0';
5476
5477 if (remember_reference)
5478 {
5479 remember_node_reference (nodename, line_number, menu_reference);
5480 free (nodename);
5481 return ((char *)NULL);
5482 }
5483 else
5484 return (nodename);
5485 }
5486
5487 static void
5488 isolate_nodename (char *nodename)
5489 {
5490 register int i, c;
5491 int paren_seen, paren;
5492
5493 if (!nodename)
5494 return;
5495
5496 canon_white (nodename);
5497 paren_seen = paren = i = 0;
5498
5499 if (*nodename == '.' || !*nodename)
5500 {
5501 *nodename = '\0';
5502 return;
5503 }
5504
5505 if (*nodename == '(')
5506 {
5507 paren++;
5508 paren_seen++;
5509 i++;
5510 }
5511
5512 for (; (c = nodename[i]); i++)
5513 {
5514 if (paren)
5515 {
5516 if (c == '(')
5517 paren++;
5518 else if (c == ')')
5519 paren--;
5520
5521 continue;
5522 }
5523
5524 /* If the character following the close paren is a space, then this
5525 node has no more characters associated with it. */
5526 if (c == '\t' ||
5527 c == '\n' ||
5528 c == ',' ||
5529 ((paren_seen && nodename[i - 1] == ')') &&
5530 (c == ' ' || c == '.')) ||
5531 (c == '.' &&
5532 ((!nodename[i + 1] ||
5533 (cr_or_whitespace (nodename[i + 1])) ||
5534 (nodename[i + 1] == ')')))))
5535 break;
5536 }
5537 nodename[i] = '\0';
5538 }
5539
5540 static void
5541 cm_menu (void)
5542 {
5543 begin_insertion (menu);
5544 }
5545
5546
5547 /* **************************************************************** */
5548 /* */
5549 /* Cross Reference Hacking */
5550 /* */
5551 /* **************************************************************** */
5552
5553 static char *
5554 get_xref_token (void)
5555 {
5556 char *string;
5557
5558 get_until_in_braces (",", &string);
5559 if (curchar () == ',')
5560 input_text_offset++;
5561 fix_whitespace (string);
5562 return (string);
5563 }
5564
5565 int px_ref_flag = 0; /* Controls initial output string. */
5566
5567 /* Make a cross reference. */
5568 static void
5569 cm_xref (int arg)
5570 {
5571 if (arg == START)
5572 {
5573 char *arg1, *arg2, *arg3, *arg4, *arg5;
5574
5575 arg1 = get_xref_token ();
5576 arg2 = get_xref_token ();
5577 arg3 = get_xref_token ();
5578 arg4 = get_xref_token ();
5579 arg5 = get_xref_token ();
5580
5581 add_word_args ("%s", px_ref_flag ? "*note " : "*Note ");
5582
5583 if (*arg5 || *arg4)
5584 {
5585 char *node_name;
5586
5587 if (!*arg2)
5588 {
5589 if (*arg3)
5590 node_name = arg3;
5591 else
5592 node_name = arg1;
5593 }
5594 else
5595 node_name = arg2;
5596
5597 execute_string ("%s: (%s)%s", node_name, arg4, arg1);
5598 return;
5599 }
5600 else
5601 remember_node_reference (arg1, line_number, followed_reference);
5602
5603 if (*arg3)
5604 {
5605 if (!*arg2)
5606 execute_string ("%s: %s", arg3, arg1);
5607 else
5608 execute_string ("%s: %s", arg2, arg1);
5609 }
5610 else
5611 {
5612 if (*arg2)
5613 execute_string ("%s: %s", arg2, arg1);
5614 else
5615 execute_string ("%s::", arg1);
5616 }
5617
5618 /* Free all of the arguments found. */
5619 if (arg1) free (arg1);
5620 if (arg2) free (arg2);
5621 if (arg3) free (arg3);
5622 if (arg4) free (arg4);
5623 if (arg5) free (arg5);
5624 }
5625 else
5626 {
5627 /* Check to make sure that the next non-whitespace character is either
5628 a period or a comma. input_text_offset is pointing at the "}" which
5629 ended the xref or pxref command. */
5630 int temp = input_text_offset + 1;
5631
5632 if (output_paragraph[output_paragraph_offset - 2] == ':' &&
5633 output_paragraph[output_paragraph_offset - 1] == ':')
5634 return;
5635 while (temp < size_of_input_text)
5636 {
5637 if (cr_or_whitespace (input_text[temp]))
5638 temp++;
5639 else
5640 {
5641 if (input_text[temp] == '.' ||
5642 input_text[temp] == ',' ||
5643 input_text[temp] == '\t')
5644 return;
5645 else
5646 {
5647 line_error (
5648 "Cross-reference must be terminated with a period or a comma");
5649 return;
5650 }
5651 }
5652 }
5653 }
5654 }
5655
5656 static void
5657 cm_pxref (int arg)
5658 {
5659 if (arg == START)
5660 {
5661 px_ref_flag++;
5662 cm_xref (arg);
5663 px_ref_flag--;
5664 }
5665 else
5666 add_char ('.');
5667 }
5668
5669 static void
5670 cm_inforef (int arg)
5671 {
5672 if (arg == START)
5673 {
5674 char *node, *pname, *file;
5675
5676 node = get_xref_token ();
5677 pname = get_xref_token ();
5678 file = get_xref_token ();
5679
5680 execute_string ("*note %s: (%s)%s", pname, file, node);
5681 }
5682 }
5683
5684 /* **************************************************************** */
5685 /* */
5686 /* Insertion Command Stubs */
5687 /* */
5688 /* **************************************************************** */
5689
5690 static void
5691 cm_quotation (void)
5692 {
5693 begin_insertion (quotation);
5694 }
5695
5696 static void
5697 cm_example (void)
5698 {
5699 begin_insertion (example);
5700 }
5701
5702 static void
5703 cm_smallexample (void)
5704 {
5705 begin_insertion (smallexample);
5706 }
5707
5708 static void
5709 cm_lisp (void)
5710 {
5711 begin_insertion (lisp);
5712 }
5713
5714 static void
5715 cm_smalllisp (void)
5716 {
5717 begin_insertion (smalllisp);
5718 }
5719
5720 /* @cartouche/@end cartouche draws box with rounded corners in
5721 TeX output. Right now, just a NOP insertion. */
5722 static void
5723 cm_cartouche (void)
5724 {
5725 begin_insertion (cartouche);
5726 }
5727
5728 static void
5729 cm_format (void)
5730 {
5731 begin_insertion (format);
5732 }
5733
5734 static void
5735 cm_display (void)
5736 {
5737 begin_insertion (display);
5738 }
5739
5740 static void
5741 cm_itemize (void)
5742 {
5743 begin_insertion (itemize);
5744 }
5745
5746 static void
5747 cm_enumerate (void)
5748 {
5749 do_enumeration (enumerate, "1");
5750 }
5751
5752 /* Start an enumeration insertion of type TYPE. If the user supplied
5753 no argument on the line, then use DEFAULT_STRING as the initial string. */
5754 static void
5755 do_enumeration (int type, char *default_string)
5756 {
5757 get_until_in_line (".", &enumeration_arg);
5758 canon_white (enumeration_arg);
5759
5760 if (!*enumeration_arg)
5761 {
5762 free (enumeration_arg);
5763 enumeration_arg = strdup (default_string);
5764 }
5765
5766 if (!isdigit (*enumeration_arg) && !isletter (*enumeration_arg))
5767 {
5768 warning ("%s requires a letter or a digit", insertion_type_pname (type));
5769
5770 switch (type)
5771 {
5772 case enumerate:
5773 default_string = "1";
5774 break;
5775 }
5776 enumeration_arg = strdup (default_string);
5777 }
5778 begin_insertion (type);
5779 }
5780
5781 static void
5782 cm_table (void)
5783 {
5784 begin_insertion (table);
5785 }
5786
5787 static void
5788 cm_ftable (void)
5789 {
5790 begin_insertion (ftable);
5791 }
5792
5793 static void
5794 cm_vtable (void)
5795 {
5796 begin_insertion (vtable);
5797 }
5798
5799 static void
5800 cm_group (void)
5801 {
5802 begin_insertion (group);
5803 }
5804
5805 static void
5806 cm_ifinfo (void)
5807 {
5808 /* begin_insertion (ifinfo); */
5809 /* BPW: @ifinfo can cross hierarchies */
5810 ifinfo_count++;
5811 if (in_menu)
5812 discard_until ("\n");
5813 }
5814
5815 /* Begin an insertion where the lines are not filled or indented. */
5816 static void
5817 cm_flushleft (void)
5818 {
5819 begin_insertion (flushleft);
5820 }
5821
5822 /* Begin an insertion where the lines are not filled, and each line is
5823 forced to the right-hand side of the page. */
5824 static void
5825 cm_flushright (void)
5826 {
5827 begin_insertion (flushright);
5828 }
5829
5830
5831 /* **************************************************************** */
5832 /* */
5833 /* Conditional Handling */
5834 /* */
5835 /* **************************************************************** */
5836
5837 /* A structure which contains `defined' variables. */
5838 typedef struct _defines {
5839 struct _defines *next;
5840 char *name;
5841 char *value;
5842 } DEFINE;
5843
5844 /* The linked list of `set' defines. */
5845 DEFINE *defines = (DEFINE *)NULL;
5846
5847 /* Add NAME to the list of `set' defines. */
5848 static void
5849 set (char *name, char *value)
5850 {
5851 DEFINE *temp;
5852
5853 for (temp = defines; temp; temp = temp->next)
5854 if (strcmp (name, temp->name) == 0)
5855 {
5856 free (temp->value);
5857 temp->value = strdup (value);
5858 return;
5859 }
5860
5861 temp = (DEFINE *)xmalloc (sizeof (DEFINE));
5862 temp->next = defines;
5863 temp->name = strdup (name);
5864 temp->value = strdup (value);
5865 defines = temp;
5866 }
5867
5868 /* Remove NAME from the list of `set' defines. */
5869 static void
5870 clear (char *name)
5871 {
5872 register DEFINE *temp, *last;
5873
5874 last = (DEFINE *)NULL;
5875 temp = defines;
5876
5877 while (temp)
5878 {
5879 if (strcmp (temp->name, name) == 0)
5880 {
5881 if (last)
5882 last->next = temp->next;
5883 else
5884 defines = temp->next;
5885
5886 free (temp->name);
5887 free (temp->value);
5888 free (temp);
5889 break;
5890 }
5891 last = temp;
5892 temp = temp->next;
5893 }
5894 }
5895
5896 /* Return the value of NAME. The return value is NULL if NAME is unset. */
5897 static char *
5898 set_p (char *name)
5899 {
5900 register DEFINE *temp;
5901
5902 for (temp = defines; temp; temp = temp->next)
5903 if (strcmp (temp->name, name) == 0)
5904 return (temp->value);
5905
5906 return ((char *)NULL);
5907 }
5908
5909 /* Conditionally parse based on the current command name. */
5910 static void
5911 command_name_condition (void)
5912 {
5913 char *discarder;
5914
5915 discarder = (char *)xmalloc (8 + strlen (command));
5916
5917 sprintf (discarder, "\n%cend %s", COMMAND_PREFIX, command);
5918 discard_until (discarder);
5919 discard_until ("\n");
5920
5921 free (discarder);
5922 }
5923
5924 /* Create a variable whose name appears as the first word on this line. */
5925 static void
5926 cm_set (void)
5927 {
5928 handle_variable (SET);
5929 }
5930
5931 /* Remove a variable whose name appears as the first word on this line. */
5932 static void
5933 cm_clear (void)
5934 {
5935 handle_variable (CLEAR);
5936 }
5937
5938 static void
5939 cm_ifset (void)
5940 {
5941 handle_variable (IFSET);
5942 }
5943
5944 static void
5945 cm_ifclear (void)
5946 {
5947 handle_variable (IFCLEAR);
5948 }
5949
5950 /* This command takes braces, but we parse the contents specially, so we
5951 don't use the standard brace popping code.
5952
5953 The syntax @ifeq{arg1, arg2, texinfo commands} performs texinfo commands
5954 if ARG1 and ARG2 caselessly string compare to the same string, otherwise,
5955 it produces no output. */
5956 static void
5957 cm_ifeq (void)
5958 {
5959 char **arglist;
5960
5961 arglist = get_brace_args (0);
5962
5963 if (arglist)
5964 {
5965 if (array_len (arglist) > 1)
5966 {
5967 if ((strcasecmp (arglist[0], arglist[1]) == 0) &&
5968 (arglist[2] != (char *)NULL))
5969 execute_string ("%s\n", arglist[2]);
5970 }
5971
5972 free_array (arglist);
5973 }
5974 }
5975
5976 static void
5977 cm_value (int arg, int start_pos, int end_pos)
5978 {
5979 if (arg == END)
5980 {
5981 char *name, *value;
5982 name = (char *)&output_paragraph[start_pos];
5983 output_paragraph[end_pos] = '\0';
5984 name = strdup (name);
5985 value = set_p (name);
5986 output_column -= end_pos - start_pos;
5987 output_paragraph_offset = start_pos;
5988
5989 if (value)
5990 execute_string ("%s", value);
5991 else
5992 add_word_args ("{No Value For \"%s\"}", name);
5993
5994 free (name);
5995 }
5996 }
5997
5998 /* Set, clear, or conditionalize based on ACTION. */
5999 static void
6000 handle_variable (int action)
6001 {
6002 char *name;
6003
6004 get_rest_of_line (&name);
6005 backup_input_pointer ();
6006 canon_white (name);
6007 handle_variable_internal (action, name);
6008 free (name);
6009 }
6010
6011 static void
6012 handle_variable_internal (int action, char *name)
6013 {
6014 char *temp;
6015 int delimiter, additional_text_present = 0;
6016
6017 /* Only the first word of NAME is a valid tag. */
6018 temp = name;
6019 delimiter = 0;
6020 while (*temp && (delimiter || !whitespace (*temp)))
6021 {
6022 /* #if defined (SET_WITH_EQUAL) */
6023 if (*temp == '"' || *temp == '\'')
6024 {
6025 if (*temp == delimiter)
6026 delimiter = 0;
6027 else
6028 delimiter = *temp;
6029 }
6030 /* #endif SET_WITH_EQUAL */
6031 temp++;
6032 }
6033
6034 if (*temp)
6035 additional_text_present++;
6036
6037 *temp = '\0';
6038
6039 if (!*name)
6040 line_error ("%c%s requires a name", COMMAND_PREFIX, command);
6041 else
6042 {
6043 switch (action)
6044 {
6045 case SET:
6046 {
6047 char *value;
6048
6049 #if defined (SET_WITH_EQUAL)
6050 /* Allow a value to be saved along with a variable. The value is
6051 the text following an `=' sign in NAME, if any is present. */
6052
6053 for (value = name; *value && *value != '='; value++);
6054
6055 if (*value)
6056 *value++ = '\0';
6057
6058 if (*value == '"' || *value == '\'')
6059 {
6060 value++;
6061 value[strlen (value) - 1] = '\0';
6062 }
6063
6064 #else /* !SET_WITH_EQUAL */
6065 /* The VALUE of NAME is the remainder of the line sans
6066 whitespace. */
6067 if (additional_text_present)
6068 {
6069 value = temp + 1;
6070 canon_white (value);
6071 }
6072 else
6073 value = "";
6074 #endif /* !SET_WITH_VALUE */
6075
6076 set (name, value);
6077 }
6078 break;
6079
6080 case CLEAR:
6081 clear (name);
6082 break;
6083
6084 case IFSET:
6085 case IFCLEAR:
6086 /* If IFSET and NAME is not set, or if IFCLEAR and NAME is set,
6087 read lines from the the file until we reach a matching
6088 "@end CONDITION". This means that we only take note of
6089 "@ifset/clear" and "@end" commands. */
6090 {
6091 char condition[8];
6092 int condition_len;
6093
6094 if (action == IFSET)
6095 strcpy (condition, "ifset");
6096 else
6097 strcpy (condition, "ifclear");
6098
6099 condition_len = strlen (condition);
6100
6101 if ((action == IFSET && !set_p (name)) ||
6102 (action == IFCLEAR && set_p (name)))
6103 {
6104 int level = 0, done = 0;
6105
6106 while (!done)
6107 {
6108 char *freeable_line, *line;
6109
6110 get_rest_of_line (&freeable_line);
6111
6112 for (line = freeable_line; whitespace (*line); line++);
6113
6114 if (*line == COMMAND_PREFIX &&
6115 (strncmp (line + 1, condition, condition_len) == 0))
6116 level++;
6117 else if (strncmp (line, "@end", 4) == 0)
6118 {
6119 char *cname = line + 4;
6120 char *temp2;
6121
6122 while (*cname && whitespace (*cname))
6123 cname++;
6124 temp2 = cname;
6125
6126 while (*temp2 && !whitespace (*temp2))
6127 temp2++;
6128 *temp2 = '\0';
6129
6130 if (strcmp (cname, condition) == 0)
6131 {
6132 if (!level)
6133 {
6134 done = 1;
6135 }
6136 else
6137 level--;
6138 }
6139 }
6140 free (freeable_line);
6141 }
6142 /* We found the end of a false @ifset/ifclear. If we are
6143 in a menu, back up over the newline that ends the ifset,
6144 since that newline may also begin the next menu entry. */
6145 break;
6146 }
6147 else
6148 {
6149 if (action == IFSET)
6150 begin_insertion (ifset);
6151 else
6152 begin_insertion (ifclear);
6153 }
6154 }
6155 break;
6156 }
6157 }
6158 }
6159
6160
6161 /* **************************************************************** */
6162 /* */
6163 /* Execution of Random Text not in file */
6164 /* */
6165 /* **************************************************************** */
6166
6167 typedef struct {
6168 char *string; /* The string buffer. */
6169 int size; /* The size of the buffer. */
6170 int in_use; /* Non-zero means string currently in use. */
6171 } EXECUTION_STRING;
6172
6173 static EXECUTION_STRING **execution_strings = (EXECUTION_STRING **)NULL;
6174 static int execution_strings_index = 0;
6175 static int execution_strings_slots = 0;
6176
6177 static EXECUTION_STRING *
6178 get_execution_string (int initial_size)
6179 {
6180 register int i = 0;
6181 EXECUTION_STRING *es = (EXECUTION_STRING *)NULL;
6182
6183 if (execution_strings)
6184 {
6185 for (i = 0; i < execution_strings_index; i++)
6186 if (execution_strings[i] && (execution_strings[i]->in_use == 0))
6187 {
6188 es = execution_strings[i];
6189 break;
6190 }
6191 }
6192
6193 if (!es)
6194 {
6195 if (execution_strings_index + 1 >= execution_strings_slots)
6196 {
6197 execution_strings = (EXECUTION_STRING **)xrealloc
6198 (execution_strings,
6199 (execution_strings_slots += 3) * sizeof (EXECUTION_STRING *));
6200 for (; i < execution_strings_slots; i++)
6201 execution_strings[i] = (EXECUTION_STRING *)NULL;
6202 }
6203
6204 execution_strings[execution_strings_index] =
6205 (EXECUTION_STRING *)xmalloc (sizeof (EXECUTION_STRING));
6206 es = execution_strings[execution_strings_index];
6207 execution_strings_index++;
6208
6209 es->size = 0;
6210 es->string = (char *)NULL;
6211 es->in_use = 0;
6212 }
6213
6214 if (initial_size > es->size)
6215 {
6216 es->string = (char *) xrealloc (es->string, initial_size);
6217 es->size = initial_size;
6218 }
6219 return (es);
6220 }
6221
6222 /* Execute the string produced by formatting the ARGs with FORMAT. This
6223 is like submitting a new file with @include. */
6224
6225 static void
6226 execute_string (char *formatte, ...)
6227 {
6228 va_list ap;
6229 EXECUTION_STRING *es;
6230 char *temp_string;
6231
6232 es = get_execution_string (4000);
6233 temp_string = es->string;
6234 es->in_use = 1;
6235
6236 va_start (ap, formatte);
6237 vsprintf (temp_string, formatte, ap);
6238 va_end (ap);
6239
6240 pushfile ();
6241 input_text_offset = 0;
6242 input_text = temp_string;
6243 input_filename = strdup (input_filename);
6244 size_of_input_text = strlen (temp_string);
6245
6246 executing_string++;
6247 reader_loop ();
6248 free (input_filename);
6249
6250 popfile ();
6251 executing_string--;
6252 es->in_use = 0;
6253 }
6254
6255 /* **************************************************************** */
6256 /* */
6257 /* @itemx, @item */
6258 /* */
6259 /* **************************************************************** */
6260
6261 static int itemx_flag = 0;
6262
6263 static void
6264 cm_itemx (void)
6265 {
6266 itemx_flag++;
6267 cm_item ();
6268 itemx_flag--;
6269 }
6270
6271 static void
6272 cm_item (void)
6273 {
6274 char *rest_of_line, *item_func;
6275
6276 /* Can only hack "@item" while inside of an insertion. */
6277 if (insertion_level)
6278 {
6279 INSERTION_ELT *stack = insertion_stack;
6280 int original_input_text_offset;
6281
6282 skip_whitespace ();
6283 original_input_text_offset = input_text_offset;
6284
6285 get_rest_of_line (&rest_of_line);
6286 canon_white (rest_of_line);
6287 item_func = current_item_function ();
6288
6289 /* Okay, do the right thing depending on which insertion function
6290 is active. */
6291
6292 switch_top:
6293 switch (stack->insertion)
6294 {
6295 case ifinfo:
6296 case ifset:
6297 case ifclear:
6298 case cartouche:
6299 stack = stack->next;
6300 if (!stack)
6301 goto no_insertion;
6302 else
6303 goto switch_top;
6304 break;
6305
6306 case menu:
6307 case quotation:
6308 case example:
6309 case smallexample:
6310 case lisp:
6311 case format:
6312 case display:
6313 case group:
6314 line_error ("The `%c%s' command is meaningless within a `@%s' block",
6315 COMMAND_PREFIX, command,
6316 insertion_type_pname (current_insertion_type ()));
6317 break;
6318
6319 case itemize:
6320 case enumerate:
6321 if (itemx_flag)
6322 {
6323 line_error ("%citemx is not meaningful inside of a `%s' block",
6324 COMMAND_PREFIX,
6325 insertion_type_pname (current_insertion_type ()));
6326 }
6327 else
6328 {
6329 start_paragraph ();
6330 kill_self_indent (-1);
6331 filling_enabled = indented_fill = 1;
6332
6333 if (current_insertion_type () == itemize)
6334 {
6335 indent (output_column = current_indent - 2);
6336
6337 /* I need some way to determine whether this command
6338 takes braces or not. I believe the user can type
6339 either "@bullet" or "@bullet{}". Of course, they
6340 can also type "o" or "#" or whatever else they want. */
6341 if (item_func && *item_func)
6342 {
6343 if (*item_func == COMMAND_PREFIX)
6344 if (item_func[strlen (item_func) - 1] != '}')
6345 execute_string ("%s{}", item_func);
6346 else
6347 execute_string ("%s", item_func);
6348 else
6349 execute_string ("%s", item_func);
6350 }
6351 insert (' ');
6352 output_column++;
6353 }
6354 else
6355 enumerate_item ();
6356
6357 /* Special hack. This makes close paragraph ignore you until
6358 the start_paragraph () function has been called. */
6359 must_start_paragraph = 1;
6360
6361 /* Ultra special hack. It appears that some people incorrectly
6362 place text directly after the @item, instead of on a new line
6363 by itself. This happens to work in TeX, so I make it work
6364 here. */
6365 if (*rest_of_line)
6366 {
6367 line_number--;
6368 input_text_offset = original_input_text_offset;
6369 }
6370 }
6371 break;
6372
6373 case table:
6374 case ftable:
6375 case vtable:
6376 {
6377 /* Get rid of extra characters. */
6378 kill_self_indent (-1);
6379
6380 /* close_paragraph () almost does what we want. The problem
6381 is when paragraph_is_open, and last_char_was_newline, and
6382 the last newline has been turned into a space, because
6383 filling_enabled. I handle it here. */
6384 if (last_char_was_newline && filling_enabled && paragraph_is_open)
6385 insert ('\n');
6386 close_paragraph ();
6387
6388 #if defined (INDENT_PARAGRAPHS_IN_TABLE)
6389 /* Indent on a new line, but back up one indentation level. */
6390 {
6391 int t;
6392
6393 t = inhibit_paragraph_indentation;
6394 inhibit_paragraph_indentation = 1;
6395 /* At this point, inserting any non-whitespace character will
6396 force the existing indentation to be output. */
6397 add_char ('i');
6398 inhibit_paragraph_indentation = t;
6399 }
6400 #else /* !INDENT_PARAGRAPHS_IN_TABLE */
6401 add_char ('i');
6402 #endif /* !INDENT_PARAGRAPHS_IN_TABLE */
6403
6404 output_paragraph_offset--;
6405 kill_self_indent (default_indentation_increment + 1);
6406
6407 /* Add item's argument to the line. */
6408 filling_enabled = 0;
6409 if (item_func && *item_func)
6410 execute_string ("%s{%s}", item_func, rest_of_line);
6411 else
6412 execute_string ("%s", rest_of_line);
6413
6414 if (current_insertion_type () == ftable)
6415 execute_string ("%cfindex %s\n", COMMAND_PREFIX, rest_of_line);
6416
6417 if (current_insertion_type () == vtable)
6418 execute_string ("%cvindex %s\n", COMMAND_PREFIX, rest_of_line);
6419
6420 /* Start a new line, and let start_paragraph ()
6421 do the indenting of it for you. */
6422 close_single_paragraph ();
6423 indented_fill = filling_enabled = 1;
6424 }
6425
6426 default:
6427 break;
6428 }
6429 free (rest_of_line);
6430 }
6431 else
6432 {
6433 no_insertion:
6434 line_error ("%c%s found outside of an insertion block",
6435 COMMAND_PREFIX, command);
6436 }
6437 }
6438
6439
6440 /* **************************************************************** */
6441 /* */
6442 /* Defun and Friends */
6443 /* */
6444 /* **************************************************************** */
6445
6446 #define DEFUN_SELF_DELIMITING(c) \
6447 (((c) == '(') \
6448 || ((c) == ')') \
6449 || ((c) == '[') \
6450 || ((c) == ']'))
6451
6452 struct token_accumulator
6453 {
6454 unsigned int length;
6455 unsigned int index;
6456 char **tokens;
6457 };
6458
6459 static void
6460 initialize_token_accumulator (struct token_accumulator *accumulator)
6461 {
6462 (accumulator->length) = 0;
6463 (accumulator->index) = 0;
6464 (accumulator->tokens) = NULL;
6465 }
6466
6467 static void
6468 accumulate_token (struct token_accumulator *accumulator, char *token)
6469 {
6470 if ((accumulator->index) >= (accumulator->length))
6471 {
6472 (accumulator->length) += 10;
6473 (accumulator->tokens) = (char **) xrealloc
6474 (accumulator->tokens, (accumulator->length * sizeof (char *)));
6475 }
6476 accumulator->tokens[accumulator->index] = token;
6477 accumulator->index += 1;
6478 }
6479
6480 static char *
6481 copy_substring (char *start, char *end)
6482 {
6483 char *result, *scan, *scan_result;
6484
6485 result = (char *) xmalloc ((end - start) + 1);
6486 scan_result = result;
6487 scan = start;
6488
6489 while (scan < end)
6490 *scan_result++ = *scan++;
6491
6492 *scan_result = '\0';
6493 return (result);
6494 }
6495
6496 /* Given `string' pointing at an open brace, skip forward and return a
6497 pointer to just past the matching close brace. */
6498 static int
6499 scan_group_in_string (char **string_pointer)
6500 {
6501 register int c;
6502 register char *scan_string;
6503 register unsigned int level = 1;
6504
6505 scan_string = (*string_pointer) + 1;
6506
6507 while (1)
6508 {
6509 if (level == 0)
6510 {
6511 (*string_pointer) = scan_string;
6512 return (1);
6513 }
6514 c = (*scan_string++);
6515 if (c == '\0')
6516 {
6517 /* Tweak line_number to compensate for fact that
6518 we gobbled the whole line before coming here. */
6519 line_number -= 1;
6520 line_error ("Missing `}' in %cdef arg", COMMAND_PREFIX);
6521 line_number += 1;
6522 (*string_pointer) = (scan_string - 1);
6523 return (0);
6524 }
6525 if (c == '{')
6526 level += 1;
6527 if (c == '}')
6528 level -= 1;
6529 }
6530 }
6531
6532 /* Return a list of tokens from the contents of `string'.
6533 Commands and brace-delimited groups count as single tokens.
6534 Contiguous whitespace characters are converted to a token
6535 consisting of a single space. */
6536 static char **
6537 args_from_string (char *string)
6538 {
6539 struct token_accumulator accumulator;
6540 register char *scan_string = string;
6541 char *token_start, *token_end;
6542
6543 initialize_token_accumulator (&accumulator);
6544
6545 while ((*scan_string) != '\0')
6546 {
6547 /* Replace arbitrary whitespace by a single space. */
6548 if (whitespace (*scan_string))
6549 {
6550 scan_string += 1;
6551 while (whitespace (*scan_string))
6552 scan_string += 1;
6553 accumulate_token ((&accumulator), (strdup (" ")));
6554 continue;
6555 }
6556
6557 /* Commands count as single tokens. */
6558 if ((*scan_string) == COMMAND_PREFIX)
6559 {
6560 token_start = scan_string;
6561 scan_string += 1;
6562 if (self_delimiting (*scan_string))
6563 scan_string += 1;
6564 else
6565 {
6566 register int c;
6567 while (1)
6568 {
6569 c = *scan_string++;
6570
6571 if ((c == '\0') || (c == '{') || (whitespace (c)))
6572 {
6573 scan_string -= 1;
6574 break;
6575 }
6576 }
6577
6578 if (*scan_string == '{')
6579 {
6580 char *s = scan_string;
6581 (void) scan_group_in_string (&s);
6582 scan_string = s;
6583 }
6584 }
6585 token_end = scan_string;
6586 }
6587
6588 /* Parentheses and brackets are self-delimiting. */
6589 else if (DEFUN_SELF_DELIMITING (*scan_string))
6590 {
6591 token_start = scan_string;
6592 scan_string += 1;
6593 token_end = scan_string;
6594 }
6595
6596 /* Open brace introduces a group that is a single token. */
6597 else if (*scan_string == '{')
6598 {
6599 char *s = scan_string;
6600 int balanced = scan_group_in_string (&s);
6601
6602 token_start = scan_string + 1;
6603 scan_string = s;
6604 token_end = balanced ? (scan_string - 1) : scan_string;
6605 }
6606
6607 /* Otherwise a token is delimited by whitespace, parentheses,
6608 brackets, or braces. A token is also ended by a command. */
6609 else
6610 {
6611 token_start = scan_string;
6612
6613 while (1)
6614 {
6615 register int c;
6616
6617 c = *scan_string++;
6618
6619 if (!c ||
6620 (whitespace (c) || DEFUN_SELF_DELIMITING (c) ||
6621 c == '{' || c == '}'))
6622 {
6623 scan_string--;
6624 break;
6625 }
6626
6627 /* If we encounter a command imbedded within a token,
6628 then end the token. */
6629 if (c == COMMAND_PREFIX)
6630 {
6631 scan_string--;
6632 break;
6633 }
6634 }
6635 token_end = scan_string;
6636 }
6637
6638 accumulate_token
6639 (&accumulator, copy_substring (token_start, token_end));
6640 }
6641 accumulate_token (&accumulator, NULL);
6642 return (accumulator.tokens);
6643 }
6644
6645 static void
6646 process_defun_args (char **defun_args, int auto_var_p)
6647 {
6648 int pending_space = 0;
6649
6650 while (1)
6651 {
6652 char *defun_arg = *defun_args++;
6653
6654 if (defun_arg == NULL)
6655 break;
6656
6657 if (defun_arg[0] == ' ')
6658 {
6659 pending_space = 1;
6660 continue;
6661 }
6662
6663 if (pending_space)
6664 {
6665 add_char (' ');
6666 pending_space = 0;
6667 }
6668
6669 if (DEFUN_SELF_DELIMITING (defun_arg[0]))
6670 add_char (defun_arg[0]);
6671 else if (defun_arg[0] == '&')
6672 add_word (defun_arg);
6673 else if (defun_arg[0] == COMMAND_PREFIX)
6674 execute_string ("%s", defun_arg);
6675 else if (auto_var_p)
6676 execute_string ("%cvar{%s}", COMMAND_PREFIX, defun_arg);
6677 else
6678 add_word (defun_arg);
6679 }
6680 }
6681
6682 static char *
6683 next_nonwhite_defun_arg (char ***arg_pointer)
6684 {
6685 char **scan = (*arg_pointer);
6686 char *arg = (*scan++);
6687
6688 if ((arg != 0) && (*arg == ' '))
6689 arg = *scan++;
6690
6691 if (arg == 0)
6692 scan -= 1;
6693
6694 *arg_pointer = scan;
6695
6696 return ((arg == 0) ? "" : arg);
6697 }
6698
6699 /* Make the defun type insertion.
6700 TYPE says which insertion this is.
6701 X_P says not to start a new insertion if non-zero. */
6702 static void
6703 defun_internal (enum insertion_type type, int x_p)
6704 {
6705 enum insertion_type base_type;
6706 char **defun_args, **scan_args;
6707 char *category, *defined_name, *type_name, *type_name2;
6708
6709 {
6710 char *line;
6711 get_rest_of_line (&line);
6712 defun_args = (args_from_string (line));
6713 free (line);
6714 }
6715
6716 scan_args = defun_args;
6717
6718 switch (type)
6719 {
6720 case defun:
6721 category = "Function";
6722 base_type = deffn;
6723 break;
6724 case defmac:
6725 category = "Macro";
6726 base_type = deffn;
6727 break;
6728 case defspec:
6729 category = "Special Form";
6730 base_type = deffn;
6731 break;
6732 case defvar:
6733 category = "Variable";
6734 base_type = defvr;
6735 break;
6736 case defopt:
6737 category = "User Option";
6738 base_type = defvr;
6739 break;
6740 case deftypefun:
6741 category = "Function";
6742 base_type = deftypefn;
6743 break;
6744 case deftypevar:
6745 category = "Variable";
6746 base_type = deftypevr;
6747 break;
6748 case defivar:
6749 category = "Instance Variable";
6750 base_type = defcv;
6751 break;
6752 case defmethod:
6753 category = "Method";
6754 base_type = defop;
6755 break;
6756 case deftypemethod:
6757 category = "Method";
6758 base_type = deftypemethod;
6759 break;
6760 default:
6761 category = next_nonwhite_defun_arg (&scan_args);
6762 base_type = type;
6763 break;
6764 }
6765
6766 if ((base_type == deftypefn)
6767 || (base_type == deftypevr)
6768 || (base_type == defcv)
6769 || (base_type == defop)
6770 || (base_type == deftypemethod))
6771 type_name = next_nonwhite_defun_arg (&scan_args);
6772
6773 if (base_type == deftypemethod)
6774 type_name2 = next_nonwhite_defun_arg (&scan_args);
6775
6776 defined_name = next_nonwhite_defun_arg (&scan_args);
6777
6778 /* This hack exists solely for the purposes of formatting the texinfo
6779 manual. I couldn't think of a better way. The token might be
6780 a simple @@ followed immediately by more text. If this is the case,
6781 then the next defun arg is part of this one, and we should concatenate
6782 them. */
6783 if (*scan_args && **scan_args && !whitespace (**scan_args) &&
6784 (strcmp (defined_name, "@@") == 0))
6785 {
6786 char *tem = (char *)xmalloc (3 + strlen (scan_args[0]));
6787
6788 sprintf (tem, "@@%s", scan_args[0]);
6789
6790 free (scan_args[0]);
6791 scan_args[0] = tem;
6792 scan_args++;
6793 defined_name = tem;
6794 }
6795
6796 if (!x_p)
6797 begin_insertion (type);
6798
6799 /* Write the definition header line.
6800 This should start at the normal indentation. */
6801 current_indent -= default_indentation_increment;
6802 start_paragraph ();
6803
6804 switch (base_type)
6805 {
6806 case deffn:
6807 case defvr:
6808 case deftp:
6809 execute_string (" -- %s: %s", category, defined_name);
6810 break;
6811 case deftypefn:
6812 case deftypevr:
6813 execute_string (" -- %s: %s %s", category, type_name, defined_name);
6814 break;
6815 case defcv:
6816 execute_string (" -- %s of %s: %s", category, type_name, defined_name);
6817 break;
6818 case defop:
6819 execute_string (" -- %s on %s: %s", category, type_name, defined_name);
6820 break;
6821 case deftypemethod:
6822 execute_string (" -- %s on %s: %s %s", category, type_name, type_name2,
6823 defined_name);
6824 break;
6825 default:
6826 break;
6827 }
6828 current_indent += default_indentation_increment;
6829
6830 /* Now process the function arguments, if any.
6831 If these carry onto the next line, they should be indented by two
6832 increments to distinguish them from the body of the definition,
6833 which is indented by one increment. */
6834 current_indent += default_indentation_increment;
6835
6836 switch (base_type)
6837 {
6838 case deffn:
6839 case defop:
6840 process_defun_args (scan_args, 1);
6841 break;
6842 case deftp:
6843 case deftypefn:
6844 case deftypemethod:
6845 process_defun_args (scan_args, 0);
6846 break;
6847 default:
6848 break;
6849 }
6850 current_indent -= default_indentation_increment;
6851 close_single_paragraph ();
6852
6853 /* Make an entry in the appropriate index. */
6854 switch (base_type)
6855 {
6856 case deffn:
6857 case deftypefn:
6858 execute_string ("%cfindex %s\n", COMMAND_PREFIX, defined_name);
6859 break;
6860 case defvr:
6861 case deftypevr:
6862 case defcv:
6863 execute_string ("%cvindex %s\n", COMMAND_PREFIX, defined_name);
6864 break;
6865 case defop:
6866 case deftypemethod:
6867 execute_string ("%cfindex %s on %s\n",
6868 COMMAND_PREFIX, defined_name, type_name);
6869 break;
6870 case deftp:
6871 execute_string ("%ctindex %s\n", COMMAND_PREFIX, defined_name);
6872 break;
6873 default:
6874 break;
6875 }
6876
6877 /* Deallocate the token list. */
6878 scan_args = defun_args;
6879 while (1)
6880 {
6881 char * arg = (*scan_args++);
6882 if (arg == NULL)
6883 break;
6884 free (arg);
6885 }
6886 free (defun_args);
6887 }
6888
6889 /* Add an entry for a function, macro, special form, variable, or option.
6890 If the name of the calling command ends in `x', then this is an extra
6891 entry included in the body of an insertion of the same type. */
6892 static void
6893 cm_defun (void)
6894 {
6895 int x_p;
6896 enum insertion_type type;
6897 char *temp = strdup (command);
6898
6899 x_p = (command[strlen (command) - 1] == 'x');
6900
6901 if (x_p)
6902 temp[strlen (temp) - 1] = '\0';
6903
6904 type = find_type_from_name (temp);
6905 free (temp);
6906
6907 /* If we are adding to an already existing insertion, then make sure
6908 that we are already in an insertion of type TYPE. */
6909 if (x_p &&
6910 (!insertion_level || insertion_stack->insertion != type))
6911 {
6912 line_error ("Must be in a `%s' insertion in order to use `%s'x",
6913 command, command);
6914 discard_until ("\n");
6915 return;
6916 }
6917
6918 defun_internal (type, x_p);
6919 }
6920
6921 /* End existing insertion block. */
6922 static void
6923 cm_end (void)
6924 {
6925 char *temp;
6926 enum insertion_type type;
6927
6928 get_rest_of_line (&temp);
6929 canon_white (temp);
6930
6931 if (strlen (temp) == 0)
6932 line_error ("`%c%s' needs something after it", COMMAND_PREFIX, command);
6933
6934 type = find_type_from_name (temp);
6935
6936 if (type == bad_type)
6937 {
6938 line_error ("Bad argument to `%s', `%s', using `%s'",
6939 command, temp, insertion_type_pname (current_insertion_type ()));
6940 }
6941
6942 /* see comment under cm_ifinfo() */
6943 if (type == ifinfo)
6944 {
6945 if (!ifinfo_count)
6946 line_error ("Unmatched `%c%s'", COMMAND_PREFIX, command);
6947 else
6948 ifinfo_count--;
6949 }
6950 else
6951 {
6952 if (!insertion_level)
6953 line_error ("Unmatched `%c%s'", COMMAND_PREFIX, command);
6954 else
6955 end_insertion (type);
6956 }
6957
6958 free (temp);
6959 }
6960
6961
6962 /* **************************************************************** */
6963 /* */
6964 /* Other Random Commands */
6965 /* */
6966 /* **************************************************************** */
6967
6968 /* This says to inhibit the indentation of the next paragraph, but
6969 not of following paragraphs. */
6970 static void
6971 cm_noindent (void)
6972 {
6973 if (!inhibit_paragraph_indentation)
6974 inhibit_paragraph_indentation = -1;
6975 }
6976
6977 /* I don't know exactly what to do with this. Should I allow
6978 someone to switch filenames in the middle of output? Since the
6979 file could be partially written, this doesn't seem to make sense.
6980 Another option: ignore it, since they don't *really* want to
6981 switch files. Finally, complain, or at least warn. */
6982 static void
6983 cm_setfilename (void)
6984 {
6985 char *filename;
6986 get_rest_of_line (&filename);
6987 /* warning ("`@%s %s' encountered and ignored", command, filename); */
6988 free (filename);
6989 }
6990
6991 static void
6992 cm_ignore_line (void)
6993 {
6994 discard_until ("\n");
6995 }
6996
6997 /* @br can be immediately followed by `{}', so we have to read those here.
6998 It should simply close the paragraph. */
6999 static void
7000 cm_br (void)
7001 {
7002 if (looking_at ("{}"))
7003 input_text_offset += 2;
7004
7005 if (curchar () == '\n')
7006 {
7007 input_text_offset++;
7008 line_number++;
7009 }
7010
7011 close_paragraph ();
7012 }
7013
7014 /* Insert the number of blank lines passed as argument. */
7015 static void
7016 cm_sp (void)
7017 {
7018 int lines;
7019 char *line;
7020
7021 get_rest_of_line (&line);
7022
7023 if (sscanf (line, "%d", &lines) != 1)
7024 {
7025 line_error ("%csp requires a positive numeric argument", COMMAND_PREFIX);
7026 }
7027 else
7028 {
7029 if (lines < 0)
7030 lines = 0;
7031
7032 while (lines--)
7033 add_char ('\n');
7034 }
7035 free (line);
7036 }
7037
7038 /* Start a new line with just this text on it.
7039 Then center the line of text.
7040 This always ends the current paragraph. */
7041 static void
7042 cm_center (void)
7043 {
7044 register int i, start, length;
7045 int fudge_factor = 1;
7046 unsigned char *line;
7047
7048 close_paragraph ();
7049 filling_enabled = indented_fill = 0;
7050 cm_noindent ();
7051 start = output_paragraph_offset;
7052 inhibit_output_flushing ();
7053 get_rest_of_line ((char **)&line);
7054 execute_string ((char *)line);
7055 free (line);
7056 uninhibit_output_flushing ();
7057
7058 i = output_paragraph_offset - 1;
7059 while (i > (start - 1) && output_paragraph[i] == '\n')
7060 i--;
7061
7062 output_paragraph_offset = ++i;
7063 length = output_paragraph_offset - start;
7064
7065 if (length < (fill_column - fudge_factor))
7066 {
7067 line = (unsigned char *)xmalloc (1 + length);
7068 memcpy (line, (char *)(output_paragraph + start), length);
7069
7070 i = (fill_column - fudge_factor - length) / 2;
7071 output_paragraph_offset = start;
7072
7073 while (i--)
7074 insert (' ');
7075
7076 for (i = 0; i < length; i++)
7077 insert (line[i]);
7078
7079 free (line);
7080 }
7081
7082 insert ('\n');
7083 close_paragraph ();
7084 filling_enabled = 1;
7085 }
7086
7087 /* Show what an expression returns. */
7088 static void
7089 cm_result (int arg)
7090 {
7091 if (arg == END)
7092 add_word ("=>");
7093 }
7094
7095 /* What an expression expands to. */
7096 static void
7097 cm_expansion (int arg)
7098 {
7099 if (arg == END)
7100 add_word ("==>");
7101 }
7102
7103 /* Indicates two expressions are equivalent. */
7104 static void
7105 cm_equiv (int arg)
7106 {
7107 if (arg == END)
7108 add_word ("==");
7109 }
7110
7111 /* What an expression may print. */
7112 static void
7113 cm_print (int arg)
7114 {
7115 if (arg == END)
7116 add_word ("-|");
7117 }
7118
7119 /* An error signaled. */
7120 static void
7121 cm_error (int arg)
7122 {
7123 if (arg == END)
7124 add_word ("error-->");
7125 }
7126
7127 /* The location of point in an example of a buffer. */
7128 static void
7129 cm_point (int arg)
7130 {
7131 if (arg == END)
7132 add_word ("-!-");
7133 }
7134
7135 /* Start a new line with just this text on it.
7136 The text is outdented one level if possible. */
7137 static void
7138 cm_exdent (void)
7139 {
7140 char *line;
7141 int i = current_indent;
7142
7143 if (current_indent)
7144 current_indent -= default_indentation_increment;
7145
7146 get_rest_of_line (&line);
7147 close_single_paragraph ();
7148 execute_string ("%s", line);
7149 current_indent = i;
7150 free (line);
7151 close_single_paragraph ();
7152 }
7153
7154 static void
7155 cm_include (void)
7156 {
7157 cm_infoinclude ();
7158 }
7159
7160 #if !defined (HAVE_STRERROR)
7161 extern char *sys_errlist[];
7162 extern int sys_nerr;
7163
7164 char *
7165 strerror (num)
7166 int num;
7167 {
7168 if (num >= sys_nerr)
7169 return ("Unknown file system error");
7170 else
7171 return (sys_errlist[num]);
7172 }
7173 #endif /* !HAVE_STRERROR */
7174
7175 /* Remember this file, and move onto the next. */
7176 static void
7177 cm_infoinclude (void)
7178 {
7179 char *filename;
7180
7181 #if defined (HAVE_MACROS)
7182 if (macro_expansion_output_stream)
7183 me_append_before_this_command ();
7184 #endif /* HAVE_MACROS */
7185
7186 close_paragraph ();
7187 get_rest_of_line (&filename);
7188
7189 #if defined (HAVE_MACROS)
7190 if (macro_expansion_output_stream)
7191 remember_itext (input_text, input_text_offset);
7192 #endif /* HAVE_MACROS */
7193
7194 pushfile ();
7195
7196 /* In verbose mode we print info about including another file. */
7197 if (verbose_mode)
7198 {
7199 register int i = 0;
7200 register FSTACK *stack = filestack;
7201
7202 for (i = 0, stack = filestack; stack; stack = stack->next, i++);
7203
7204 i *= 2;
7205
7206 printf ("%*s", i, "");
7207 printf ("%c%s %s\n", COMMAND_PREFIX, command, filename);
7208 fflush (stdout);
7209 }
7210
7211 if (!find_and_load (filename))
7212 {
7213 popfile ();
7214 line_number--;
7215
7216 /* Cannot "@include foo", in line 5 of "/wh/bar". */
7217 line_error ("`%c%s %s': %s", COMMAND_PREFIX, command, filename,
7218 strerror (errno));
7219
7220 free (filename);
7221 return;
7222 }
7223 else
7224 {
7225 #if defined (HAVE_MACROS)
7226 if (macro_expansion_output_stream)
7227 remember_itext (input_text, input_text_offset);
7228 #endif /* HAVE_MACROS */
7229 reader_loop ();
7230 }
7231 free (filename);
7232 popfile ();
7233 }
7234
7235 /* The other side of a malformed expression. */
7236 static void
7237 misplaced_brace (void)
7238 {
7239 line_error ("Misplaced `}'");
7240 }
7241
7242 /* Don't let the filling algorithm insert extra whitespace here. */
7243 static void
7244 cm_force_abbreviated_whitespace (void)
7245 {
7246 }
7247
7248 /* Do not let this character signify the end of a sentence, though
7249 if it was seen without the command prefix it normally would. We
7250 do this by turning on the 8th bit of the character. */
7251 static void
7252 cm_ignore_sentence_ender (void)
7253 {
7254 add_char (META ((*command)));
7255 }
7256
7257 /* Signals end of processing. Easy to make this happen. */
7258 static void
7259 cm_bye (void)
7260 {
7261 input_text_offset = size_of_input_text;
7262 }
7263
7264 static void
7265 cm_asis (void)
7266 {
7267 }
7268
7269 static void
7270 cm_math (void)
7271 {
7272 }
7273
7274
7275 /* **************************************************************** */
7276 /* */
7277 /* Indexing Stuff */
7278 /* */
7279 /* **************************************************************** */
7280
7281
7282 /* An index element... */
7283 typedef struct index_elt
7284 {
7285 struct index_elt *next;
7286 char *entry; /* The index entry itself. */
7287 char *node; /* The node from whence it came. */
7288 int code; /* Non-zero means add `@code{...}' when
7289 printing this element. */
7290 int defining_line; /* Line number where this entry was written. */
7291 } INDEX_ELT;
7292
7293 /* A list of short-names for each index, and the index to that index in our
7294 index array, the_indices. In addition, for each index, it is remembered
7295 whether that index is a code index or not. Code indices have @code{}
7296 inserted around the first word when they are printed with printindex. */
7297 typedef struct
7298 {
7299 char *name;
7300 int index;
7301 int code;
7302 } INDEX_ALIST;
7303
7304 INDEX_ALIST **name_index_alist = (INDEX_ALIST **) NULL;
7305
7306 /* An array of pointers. Each one is for a different index. The
7307 "synindex" command changes which array slot is pointed to by a
7308 given "index". */
7309 INDEX_ELT **the_indices = (INDEX_ELT **) NULL;
7310
7311 /* The number of defined indices. */
7312 int defined_indices = 0;
7313
7314 /* We predefine these. */
7315 #define program_index 0
7316 #define function_index 1
7317 #define concept_index 2
7318 #define variable_index 3
7319 #define datatype_index 4
7320 #define key_index 5
7321
7322 static void
7323 init_indices (void)
7324 {
7325 int i;
7326
7327 /* Create the default data structures. */
7328
7329 /* Initialize data space. */
7330 if (!the_indices)
7331 {
7332 the_indices = (INDEX_ELT **) xmalloc ((1 + defined_indices) *
7333 sizeof (INDEX_ELT *));
7334 the_indices[defined_indices] = (INDEX_ELT *) NULL;
7335
7336 name_index_alist = (INDEX_ALIST **) xmalloc ((1 + defined_indices) *
7337 sizeof (INDEX_ALIST *));
7338 name_index_alist[defined_indices] = (INDEX_ALIST *) NULL;
7339 }
7340
7341 /* If there were existing indices, get rid of them now. */
7342 for (i = 0; i < defined_indices; i++)
7343 undefindex (name_index_alist[i]->name);
7344
7345 /* Add the default indices. */
7346 top_defindex ("pg", 0);
7347 top_defindex ("fn", 1); /* "fn" is a code index. */
7348 top_defindex ("cp", 0);
7349 top_defindex ("vr", 0);
7350 top_defindex ("tp", 0);
7351 top_defindex ("ky", 0);
7352
7353 }
7354
7355 /* Find which element in the known list of indices has this name.
7356 Returns -1 if NAME isn't found. */
7357 static int
7358 find_index_offset (char *name)
7359 {
7360 register int i;
7361 for (i = 0; i < defined_indices; i++)
7362 if (name_index_alist[i] &&
7363 strcmp (name, name_index_alist[i]->name) == 0)
7364 return (name_index_alist[i]->index);
7365 return (-1);
7366 }
7367
7368 /* Return a pointer to the entry of (name . index) for this name.
7369 Return NULL if the index doesn't exist. */
7370 static INDEX_ALIST *
7371 find_index (char *name)
7372 {
7373 int offset = find_index_offset (name);
7374 if (offset > -1)
7375 return (name_index_alist[offset]);
7376 else
7377 return ((INDEX_ALIST *) NULL);
7378 }
7379
7380 /* Given an index name, return the offset in the_indices of this index,
7381 or -1 if there is no such index. */
7382 static int
7383 translate_index (char *name)
7384 {
7385 INDEX_ALIST *which = find_index (name);
7386
7387 if (which)
7388 return (which->index);
7389 else
7390 return (-1);
7391 }
7392
7393 /* Return the index list which belongs to NAME. */
7394 static INDEX_ELT *
7395 index_list (char *name)
7396 {
7397 int which = translate_index (name);
7398 if (which < 0)
7399 return ((INDEX_ELT *) -1);
7400 else
7401 return (the_indices[which]);
7402 }
7403
7404 /* Please release me, let me go... */
7405 static void
7406 free_index (INDEX_ELT *indecks)
7407 {
7408 INDEX_ELT *temp;
7409
7410 while ((temp = indecks) != (INDEX_ELT *) NULL)
7411 {
7412 free (temp->entry);
7413 free (temp->node);
7414 indecks = indecks->next;
7415 free (temp);
7416 }
7417 }
7418
7419 /* Flush an index by name. */
7420 static void
7421 undefindex (char *name)
7422 {
7423 int i;
7424 int which = find_index_offset (name);
7425
7426 if (which < 0)
7427 return;
7428
7429 i = name_index_alist[which]->index;
7430
7431 free_index (the_indices[i]);
7432 the_indices[i] = (INDEX_ELT *) NULL;
7433
7434 free (name_index_alist[which]->name);
7435 free (name_index_alist[which]);
7436 name_index_alist[which] = (INDEX_ALIST *) NULL;
7437 }
7438
7439 /* Define an index known as NAME. We assign the slot number.
7440 CODE if non-zero says to make this a code index. */
7441 static void
7442 defindex (char *name, int code)
7443 {
7444 register int i, slot;
7445
7446 /* If it already exists, flush it. */
7447 undefindex (name);
7448
7449 /* Try to find an empty slot. */
7450 slot = -1;
7451 for (i = 0; i < defined_indices; i++)
7452 if (!name_index_alist[i])
7453 {
7454 slot = i;
7455 break;
7456 }
7457
7458 if (slot < 0)
7459 {
7460 /* No such luck. Make space for another index. */
7461 slot = defined_indices;
7462 defined_indices++;
7463
7464 name_index_alist = (INDEX_ALIST **)
7465 xrealloc ((char *)name_index_alist,
7466 (1 + defined_indices) * sizeof (INDEX_ALIST *));
7467 the_indices = (INDEX_ELT **)
7468 xrealloc ((char *)the_indices,
7469 (1 + defined_indices) * sizeof (INDEX_ELT *));
7470 }
7471
7472 /* We have a slot. Start assigning. */
7473 name_index_alist[slot] = (INDEX_ALIST *) xmalloc (sizeof (INDEX_ALIST));
7474 name_index_alist[slot]->name = strdup (name);
7475 name_index_alist[slot]->index = slot;
7476 name_index_alist[slot]->code = code;
7477
7478 the_indices[slot] = (INDEX_ELT *) NULL;
7479 }
7480
7481 /* Add the arguments to the current index command to the index NAME. */
7482 static void
7483 index_add_arg (char *name)
7484 {
7485 int which;
7486 char *index_entry;
7487 INDEX_ALIST *tem;
7488
7489 tem = find_index (name);
7490
7491 which = tem ? tem->index : -1;
7492
7493 #if defined (HAVE_MACROS)
7494 if (macro_expansion_output_stream)
7495 append_to_expansion_output (input_text_offset + 1);
7496 #endif /* HAVE_MACROS */
7497
7498 get_rest_of_line (&index_entry);
7499 ignore_blank_line ();
7500
7501 #if defined (HAVE_MACROS)
7502 if (macro_expansion_output_stream)
7503 {
7504 int op_orig;
7505
7506 remember_itext (input_text, input_text_offset);
7507 op_orig = output_paragraph_offset;
7508 me_execute_string (index_entry);
7509 me_execute_string ("\n");
7510 output_paragraph_offset = op_orig;
7511 }
7512 #endif /* HAVE_MACROS */
7513
7514 if (which < 0)
7515 {
7516 line_error ("Unknown index reference `%s'", name);
7517 free (index_entry);
7518 }
7519 else
7520 {
7521 INDEX_ELT *new = (INDEX_ELT *) xmalloc (sizeof (INDEX_ELT));
7522 new->next = the_indices[which];
7523 new->entry = index_entry;
7524 new->node = current_node;
7525 new->code = tem->code;
7526 new->defining_line = line_number - 1;
7527 the_indices[which] = new;
7528 }
7529 }
7530
7531 #define INDEX_COMMAND_SUFFIX "index"
7532
7533 /* The function which user defined index commands call. */
7534 static void
7535 gen_index (void)
7536 {
7537 char *name = strdup (command);
7538 if ((int) strlen (name) >= (int) strlen ("index"))
7539 name[strlen (name) - strlen ("index")] = '\0';
7540 index_add_arg (name);
7541 free (name);
7542 }
7543
7544 static void
7545 top_defindex (char *name, int code)
7546 {
7547 char *temp;
7548
7549 temp = (char *) xmalloc (1 + strlen (name) + strlen ("index"));
7550 sprintf (temp, "%sindex", name);
7551 define_user_command (temp, gen_index, 0);
7552 defindex (name, code);
7553 free (temp);
7554 }
7555
7556 /* Define a new index command. Arg is name of index. */
7557 static void
7558 cm_defindex (void)
7559 {
7560 gen_defindex (0);
7561 }
7562
7563 static void
7564 cm_defcodeindex (void)
7565 {
7566 gen_defindex (1);
7567 }
7568
7569 static void
7570 gen_defindex (int code)
7571 {
7572 char *name;
7573 get_rest_of_line (&name);
7574
7575 if (find_index (name))
7576 {
7577 line_error ("Index `%s' already exists", name);
7578 free (name);
7579 return;
7580 }
7581 else
7582 {
7583 char *temp = (char *) alloca (1 + strlen (name) + strlen ("index"));
7584 sprintf (temp, "%sindex", name);
7585 define_user_command (temp, gen_index, 0);
7586 defindex (name, code);
7587 free (name);
7588 }
7589 }
7590
7591 /* Append LIST2 to LIST1. Return the head of the list. */
7592 static INDEX_ELT *
7593 index_append (INDEX_ELT *head, INDEX_ELT *tail)
7594 {
7595 register INDEX_ELT *t_head = head;
7596
7597 if (!t_head)
7598 return (tail);
7599
7600 while (t_head->next)
7601 t_head = t_head->next;
7602 t_head->next = tail;
7603 return (head);
7604 }
7605
7606 /* Expects 2 args, on the same line. Both are index abbreviations.
7607 Make the first one be a synonym for the second one, i.e. make the
7608 first one have the same index as the second one. */
7609 static void
7610 cm_synindex (void)
7611 {
7612 int redirector, redirectee;
7613 char *temp;
7614
7615 skip_whitespace ();
7616 get_until_in_line (" ", &temp);
7617 redirectee = find_index_offset (temp);
7618 skip_whitespace ();
7619 free_and_clear (&temp);
7620 get_until_in_line (" ", &temp);
7621 redirector = find_index_offset (temp);
7622 free (temp);
7623 if (redirector < 0 || redirectee < 0)
7624 {
7625 line_error ("Unknown index reference");
7626 }
7627 else
7628 {
7629 /* I think that we should let the user make indices synonymous to
7630 each other without any lossage of info. This means that one can
7631 say @synindex cp dt anywhere in the file, and things that used to
7632 be in cp will go into dt. */
7633 INDEX_ELT *i1 = the_indices[redirectee], *i2 = the_indices[redirector];
7634
7635 if (i1 || i2)
7636 {
7637 if (i1)
7638 the_indices[redirectee] = index_append (i1, i2);
7639 else
7640 the_indices[redirectee] = index_append (i2, i1);
7641 }
7642
7643 name_index_alist[redirectee]->index =
7644 name_index_alist[redirector]->index;
7645 }
7646 }
7647
7648 static void
7649 cm_pindex (void) /* Pinhead index. */
7650 {
7651 index_add_arg ("pg");
7652 }
7653
7654 static void
7655 cm_vindex (void) /* Variable index. */
7656 {
7657 index_add_arg ("vr");
7658 }
7659
7660 static void
7661 cm_kindex (void) /* Key index. */
7662 {
7663 index_add_arg ("ky");
7664 }
7665
7666 static void
7667 cm_cindex (void) /* Concept index. */
7668 {
7669 index_add_arg ("cp");
7670 }
7671
7672 static void
7673 cm_findex (void) /* Function index. */
7674 {
7675 index_add_arg ("fn");
7676 }
7677
7678 static void
7679 cm_tindex (void) /* Data Type index. */
7680 {
7681 index_add_arg ("tp");
7682 }
7683
7684 /* Sorting the index. */
7685 static int
7686 index_element_compare (INDEX_ELT **element1, INDEX_ELT **element2)
7687 {
7688 /* This needs to ignore leading non-text characters. */
7689 return (strcasecmp ((*element1)->entry, (*element2)->entry));
7690 }
7691
7692 /* Force all index entries to be unique. */
7693 static void
7694 make_index_entries_unique (INDEX_ELT **array, int count)
7695 {
7696 register int i, j;
7697 INDEX_ELT **copy;
7698
7699 copy = (INDEX_ELT **)xmalloc ((1 + count) * sizeof (INDEX_ELT *));
7700
7701 for (i = 0, j = 0; i < count; i++)
7702 {
7703 if ((i == (count - 1)) ||
7704 (array[i]->node != array[i + 1]->node) ||
7705 (strcasecmp (array[i]->entry, array[i + 1]->entry) != 0))
7706 copy[j++] = array[i];
7707 else
7708 {
7709 free (array[i]->entry);
7710 free (array[i]);
7711 }
7712 }
7713 copy[j] = (INDEX_ELT *)NULL;
7714
7715 /* Now COPY contains only unique entries. Duplicated entries in the
7716 original array have been freed. Replace the current array with
7717 the copy, fixing the NEXT pointers. */
7718 for (i = 0; copy[i] != (INDEX_ELT *)NULL; i++)
7719 {
7720 int counter = 1;
7721 copy[i]->next = copy[i + 1];
7722
7723 /* Fix entry names which are the same. They point to different nodes,
7724 so we make the entry name unique. */
7725 if ((copy[i + 1] != (INDEX_ELT *)NULL) &&
7726 (strcmp (copy[i]->entry, copy[i + 1]->entry) == 0))
7727 {
7728 char *new_entry_name;
7729
7730 new_entry_name = (char *)xmalloc (10 + strlen (copy[i]->entry));
7731 sprintf (new_entry_name, "%s <%d>", copy[i]->entry, counter);
7732 free (copy[i]->entry);
7733 copy[i]->entry = new_entry_name;
7734 counter++;
7735 }
7736 else
7737 counter = 1;
7738
7739 array[i] = copy[i];
7740 }
7741 array[i] = (INDEX_ELT *)NULL;
7742
7743 /* Free the storage used only by COPY. */
7744 free (copy);
7745 }
7746
7747 /* Sort the index passed in INDEX, returning an array of
7748 pointers to elements. The array is terminated with a NULL
7749 pointer. We call qsort because it's supposed to be fast.
7750 I think this looks bad. */
7751 static INDEX_ELT **
7752 sort_index (INDEX_ELT *indecks)
7753 {
7754 INDEX_ELT *temp = indecks;
7755 INDEX_ELT **array;
7756 int count = 0;
7757
7758 while (temp != (INDEX_ELT *) NULL)
7759 {
7760 count++;
7761 temp = temp->next;
7762 }
7763
7764 /* We have the length. Make an array. */
7765
7766 array = (INDEX_ELT **) xmalloc ((count + 1) * sizeof (INDEX_ELT *));
7767 count = 0;
7768 temp = indecks;
7769
7770 while (temp != (INDEX_ELT *) NULL)
7771 {
7772 array[count++] = temp;
7773 temp = temp->next;
7774 }
7775 array[count] = (INDEX_ELT *) NULL; /* terminate the array. */
7776
7777 /* Sort the array. */
7778 qsort (array, count, sizeof (INDEX_ELT *), (int (*)()) index_element_compare);
7779 make_index_entries_unique (array, count);
7780 return (array);
7781 }
7782
7783 /* Non-zero means that we are in the middle of printing an index. */
7784 int printing_index = 0;
7785
7786 /* Takes one arg, a short name of an index to print.
7787 Outputs a menu of the sorted elements of the index. */
7788 static void
7789 cm_printindex (void)
7790 {
7791 int item;
7792 INDEX_ELT *indecks;
7793 INDEX_ELT **array;
7794 char *index_name;
7795 int old_inhibitions = inhibit_paragraph_indentation;
7796 int previous_filling_enabled_value = filling_enabled;
7797
7798 close_paragraph ();
7799 get_rest_of_line (&index_name);
7800
7801 indecks = index_list (index_name);
7802 if (indecks == (INDEX_ELT *)-1)
7803 {
7804 line_error ("Unknown index name `%s'", index_name);
7805 free (index_name);
7806 return;
7807 }
7808 else
7809 free (index_name);
7810
7811 array = sort_index (indecks);
7812
7813 filling_enabled = 0;
7814 inhibit_paragraph_indentation = 1;
7815 close_paragraph ();
7816 add_word ("* Menu:\n\n");
7817
7818 printing_index = 1;
7819
7820 #if defined (HAVE_MACROS)
7821 me_inhibit_expansion++;
7822 #endif /* HAVE_MACROS */
7823
7824 for (item = 0; (indecks = array[item]); item++)
7825 {
7826 int real_line_number = line_number;
7827
7828 /* Let errors generated while making the index entry point back
7829 at the line which contains the entry. */
7830 line_number = indecks->defining_line;
7831
7832 /* If this particular entry should be printed as a "code" index,
7833 then wrap the entry with "@code{...}". */
7834 if (indecks->code)
7835 execute_string ("* %ccode{%s}: ", COMMAND_PREFIX, indecks->entry);
7836 else
7837 execute_string ("* %s: ", indecks->entry);
7838
7839 /* Pad the front of the destination nodename so that
7840 the output looks nice. */
7841 if (fill_column > 40 && output_column < 40)
7842 indent (40 - output_column);
7843
7844 execute_string ("%s.\n", indecks->node);
7845
7846 line_number = real_line_number;
7847 flush_output ();
7848 }
7849
7850 #if defined (HAVE_MACROS)
7851 me_inhibit_expansion--;
7852 #endif /* HAVE_MACROS */
7853
7854 printing_index = 0;
7855 free (array);
7856 close_single_paragraph ();
7857 filling_enabled = previous_filling_enabled_value;
7858 inhibit_paragraph_indentation = old_inhibitions;
7859 }
7860
7861
7862 /* **************************************************************** */
7863 /* */
7864 /* Making User Defined Commands */
7865 /* */
7866 /* **************************************************************** */
7867
7868 static void
7869 define_user_command (char *name, COMMAND_FUNCTION *proc, int needs_braces_p)
7870 {
7871 int slot = user_command_array_len;
7872 user_command_array_len++;
7873
7874 if (!user_command_array)
7875 user_command_array = (COMMAND **) xmalloc (1 * sizeof (COMMAND *));
7876
7877 user_command_array = (COMMAND **) xrealloc (user_command_array,
7878 (1 + user_command_array_len) *
7879 sizeof (COMMAND *));
7880
7881 user_command_array[slot] = (COMMAND *) xmalloc (sizeof (COMMAND));
7882 user_command_array[slot]->name = strdup (name);
7883 user_command_array[slot]->proc = proc;
7884 user_command_array[slot]->argument_in_braces = needs_braces_p;
7885 }
7886
7887 #if 0 /* unused */
7888 /* Make ALIAS run the named FUNCTION. Copies properties from FUNCTION. */
7889 static void
7890 define_alias (char *alias, char *function)
7891 {
7892 }
7893 #endif
7894
7895 /* Set the paragraph indentation variable to the value specified in STRING.
7896 Values can be:
7897 `asis': Don't change existing indentation.
7898 `none': Remove existing indentation.
7899 NUM: Indent NUM spaces at the starts of paragraphs.
7900 Note that if NUM is zero, we assume `none'.
7901
7902 Returns 0 if successful, or non-zero if STRING isn't one of the above. */
7903 static int
7904 set_paragraph_indent (char *string)
7905 {
7906 if (strcmp (string, "asis") == 0)
7907 paragraph_start_indent = 0;
7908 else if (strcmp (string, "none") == 0)
7909 paragraph_start_indent = -1;
7910 else
7911 {
7912 if (sscanf (string, "%d", &paragraph_start_indent) != 1)
7913 return (-1);
7914 else
7915 {
7916 if (paragraph_start_indent == 0)
7917 paragraph_start_indent = -1;
7918 }
7919 }
7920 return (0);
7921 }
7922
7923 static void
7924 cm_paragraphindent (void)
7925 {
7926 char *arg;
7927
7928 get_rest_of_line (&arg);
7929 if (set_paragraph_indent (arg) != 0)
7930 line_error ("Bad argument to %c%s", COMMAND_PREFIX, command);
7931
7932 free (arg);
7933 }
7934
7935 /* Some support for footnotes. */
7936
7937 /* Footnotes are a new construct in Info. We don't know the best method
7938 of implementing them for sure, so we present two possiblities.
7939
7940 SeparateNode:
7941 Make them look like followed references, with the reference
7942 destinations in a makeinfo manufactured node or,
7943
7944 EndNode:
7945 Make them appear at the bottom of the node that they originally
7946 appeared in. */
7947 #define SeparateNode 0
7948 #define EndNode 1
7949
7950 int footnote_style = EndNode;
7951 int first_footnote_this_node = 1;
7952 int footnote_count = 0;
7953
7954 /* Set the footnote style based on he style identifier in STRING. */
7955 static int
7956 set_footnote_style (char *string)
7957 {
7958 if ((strcasecmp (string, "separate") == 0) ||
7959 (strcasecmp (string, "MN") == 0))
7960 footnote_style = SeparateNode;
7961 else if ((strcasecmp (string, "end") == 0) ||
7962 (strcasecmp (string, "EN") == 0))
7963 footnote_style = EndNode;
7964 else
7965 return (-1);
7966
7967 return (0);
7968 }
7969
7970 static void
7971 cm_footnotestyle (void)
7972 {
7973 char *arg;
7974
7975 get_rest_of_line (&arg);
7976
7977 if (set_footnote_style (arg) != 0)
7978 line_error ("Bad argument to %c%s", COMMAND_PREFIX, command);
7979
7980 free (arg);
7981 }
7982
7983 typedef struct fn
7984 {
7985 struct fn *next;
7986 char *marker;
7987 char *note;
7988 } FN;
7989
7990 FN *pending_notes = (FN *) NULL;
7991
7992 /* A method for remembering footnotes. Note that this list gets output
7993 at the end of the current node. */
7994 static void
7995 remember_note (char *marker, char *note)
7996 {
7997 FN *temp = (FN *) xmalloc (sizeof (FN));
7998
7999 temp->marker = strdup (marker);
8000 temp->note = strdup (note);
8001 temp->next = pending_notes;
8002 pending_notes = temp;
8003 footnote_count++;
8004 }
8005
8006 /* How to get rid of existing footnotes. */
8007 static void
8008 free_pending_notes (void)
8009 {
8010 FN *temp;
8011
8012 while ((temp = pending_notes) != (FN *) NULL)
8013 {
8014 free (temp->marker);
8015 free (temp->note);
8016 pending_notes = pending_notes->next;
8017 free (temp);
8018 }
8019 first_footnote_this_node = 1;
8020 footnote_count = 0;
8021 }
8022
8023 /* What to do when you see a @footnote construct. */
8024
8025 /* Handle a "footnote".
8026 footnote *{this is a footnote}
8027 where "*" is the marker character for this note. */
8028 static void
8029 cm_footnote (void)
8030 {
8031 char *marker;
8032 char *note;
8033
8034 get_until ("{", &marker);
8035 canon_white (marker);
8036
8037 /* Read the argument in braces. */
8038 if (curchar () != '{')
8039 {
8040 line_error ("`%c%s' expected more than just `%s'. It needs something in `{...}'",
8041 COMMAND_PREFIX, command, marker);
8042 free (marker);
8043 return;
8044 }
8045 else
8046 {
8047 int braces = 1;
8048 int temp = ++input_text_offset;
8049 int len;
8050
8051 while (braces)
8052 {
8053 if (temp == size_of_input_text)
8054 {
8055 line_error ("No closing brace for footnote `%s'", marker);
8056 return;
8057 }
8058
8059 if (input_text[temp] == '{')
8060 braces++;
8061 else if (input_text[temp] == '}')
8062 braces--;
8063 else if (input_text[temp] == '\n')
8064 line_number ++;
8065
8066 temp++;
8067 }
8068
8069 len = (temp - input_text_offset) - 1;
8070 note = (char *)xmalloc (len + 1);
8071 strncpy (note, &input_text[input_text_offset], len);
8072 note[len] = '\0';
8073 input_text_offset = temp;
8074 }
8075
8076 if (!current_node || !*current_node)
8077 {
8078 line_error ("Footnote defined without parent node");
8079 free (marker);
8080 free (note);
8081 return;
8082 }
8083
8084 if (!*marker)
8085 {
8086 free (marker);
8087
8088 if (number_footnotes)
8089 {
8090 marker = (char *)xmalloc (10);
8091 sprintf (marker, "%d", current_footnote_number);
8092 current_footnote_number++;
8093 }
8094 else
8095 marker = strdup ("*");
8096 }
8097
8098 remember_note (marker, note);
8099
8100 /* Your method should at least insert MARKER. */
8101 switch (footnote_style)
8102 {
8103 case SeparateNode:
8104 add_word_args ("(%s)", marker);
8105 if (first_footnote_this_node)
8106 {
8107 char *temp_string;
8108
8109 temp_string = (char *)
8110 xmalloc ((strlen (current_node)) + (strlen ("-Footnotes")) + 1);
8111
8112 add_word_args (" (*note %s-Footnotes::)", current_node);
8113 strcpy (temp_string, current_node);
8114 strcat (temp_string, "-Footnotes");
8115 remember_node_reference (temp_string, line_number, followed_reference);
8116 free (temp_string);
8117 first_footnote_this_node = 0;
8118 }
8119 break;
8120
8121 case EndNode:
8122 add_word_args ("(%s)", marker);
8123 break;
8124
8125 default:
8126 break;
8127 }
8128 free (marker);
8129 free (note);
8130 }
8131
8132 /* Non-zero means that we are currently in the process of outputting
8133 footnotes. */
8134 int already_outputting_pending_notes = 0;
8135
8136 /* Output the footnotes. We are at the end of the current node. */
8137 static void
8138 output_pending_notes (void)
8139 {
8140 FN *footnote = pending_notes;
8141
8142 if (!pending_notes)
8143 return;
8144
8145 switch (footnote_style)
8146 {
8147
8148 case SeparateNode:
8149 {
8150 char *old_current_node = current_node;
8151 char *old_command = strdup (command);
8152
8153 already_outputting_pending_notes++;
8154 execute_string ("%cnode %s-Footnotes,,,%s\n",
8155 COMMAND_PREFIX, current_node, current_node);
8156 already_outputting_pending_notes--;
8157 current_node = old_current_node;
8158 free (command);
8159 command = old_command;
8160 }
8161 break;
8162
8163 case EndNode:
8164 close_paragraph ();
8165 in_fixed_width_font++;
8166 execute_string ("---------- Footnotes ----------\n\n");
8167 in_fixed_width_font--;
8168 break;
8169 }
8170
8171 /* Handle the footnotes in reverse order. */
8172 {
8173 FN **array = (FN **) xmalloc ((footnote_count + 1) * sizeof (FN *));
8174
8175 array[footnote_count] = (FN *) NULL;
8176
8177 while (--footnote_count > -1)
8178 {
8179 array[footnote_count] = footnote;
8180 footnote = footnote->next;
8181 }
8182
8183 filling_enabled = 1;
8184 indented_fill = 1;
8185
8186 while ((footnote = array[++footnote_count]))
8187 {
8188
8189 switch (footnote_style)
8190 {
8191 case SeparateNode:
8192 case EndNode:
8193 execute_string ("(%s) %s", footnote->marker, footnote->note);
8194 close_paragraph ();
8195 break;
8196 }
8197 }
8198 close_paragraph ();
8199 free (array);
8200 }
8201 }
8202
8203
8204 /* **************************************************************** */
8205 /* */
8206 /* User definable Macros (text substitution) */
8207 /* */
8208 /* **************************************************************** */
8209
8210 #if defined (HAVE_MACROS)
8211
8212 /* Array of macros and definitions. */
8213 MACRO_DEF **macro_list = (MACRO_DEF **)NULL;
8214
8215 int macro_list_len = 0; /* Number of elements. */
8216 int macro_list_size = 0; /* Number of slots in total. */
8217
8218 /* Return the macro definition of NAME or NULL if NAME is not defined. */
8219 static MACRO_DEF *
8220 find_macro (char *name)
8221 {
8222 register int i;
8223 register MACRO_DEF *def;
8224
8225 def = (MACRO_DEF *)NULL;
8226 for (i = 0; macro_list && (def = macro_list[i]); i++)
8227 {
8228 if ((!def->inhibited) && (strcmp (def->name, name) == 0))
8229 break;
8230 }
8231 return (def);
8232 }
8233
8234 /* Add the macro NAME with ARGLIST and BODY to the list of defined macros.
8235 SOURCE_FILE is the name of the file where this definition can be found,
8236 and SOURCE_LINENO is the line number within that file. If a macro already
8237 exists with NAME, then a warning is produced, and that previous
8238 definition is overwritten. */
8239 void
8240 add_macro (char *name, char **arglist, char *body, char *source_file,
8241 int source_lineno, int flags)
8242 {
8243 register MACRO_DEF *def;
8244
8245 def = find_macro (name);
8246
8247 if (!def)
8248 {
8249 if (macro_list_len + 2 >= macro_list_size)
8250 macro_list = (MACRO_DEF **)xrealloc
8251 (macro_list, ((macro_list_size += 10) * sizeof (MACRO_DEF *)));
8252
8253 macro_list[macro_list_len] = (MACRO_DEF *)xmalloc (sizeof (MACRO_DEF));
8254 macro_list[macro_list_len + 1] = (MACRO_DEF *)NULL;
8255
8256 def = macro_list[macro_list_len];
8257 macro_list_len += 1;
8258 def->name = name;
8259 }
8260 else
8261 {
8262 char *temp_filename = input_filename;
8263 int temp_line = line_number;
8264
8265 warning ("The macro `%s' is previously defined", name);
8266
8267 input_filename = def->source_file;
8268 line_number = def->source_lineno;
8269
8270 warning ("Here is the previous definition of `%s'", name);
8271
8272 input_filename = temp_filename;
8273 line_number = temp_line;
8274
8275 if (def->arglist)
8276 {
8277 register int i;
8278
8279 for (i = 0; def->arglist[i]; i++)
8280 free (def->arglist[i]);
8281
8282 free (def->arglist);
8283 }
8284 free (def->source_file);
8285 free (def->body);
8286 }
8287
8288 def->source_file = strdup (source_file);
8289 def->source_lineno = source_lineno;
8290 def->body = body;
8291 def->arglist = arglist;
8292 def->inhibited = 0;
8293 def->flags = flags;
8294 }
8295
8296 /* Delete the macro with name NAME. The macro is deleted from the list,
8297 but it is also returned. If there was no macro defined, NULL is
8298 returned. */
8299 static MACRO_DEF *
8300 delete_macro (char *name)
8301 {
8302 register int i;
8303 register MACRO_DEF *def;
8304
8305 def = (MACRO_DEF *)NULL;
8306
8307 for (i = 0; macro_list && (def = macro_list[i]); i++)
8308 if (strcmp (def->name, name) == 0)
8309 {
8310 memcpy (macro_list + i, macro_list + i + 1,
8311 ((macro_list_len + 1) - i) * sizeof (MACRO_DEF *));
8312 break;
8313 }
8314 return (def);
8315 }
8316
8317 /* Return the arglist on the current line. This can behave in two different
8318 ways, depending on the variable BRACES_REQUIRED_FOR_MACRO_ARGS. */
8319 int braces_required_for_macro_args = 0;
8320
8321 static char **
8322 get_macro_args (MACRO_DEF *def)
8323 {
8324 register int i;
8325 char *word;
8326
8327 /* Quickly check to see if this macro has been invoked with any arguments.
8328 If not, then don't skip any of the following whitespace. */
8329 for (i = input_text_offset; i < size_of_input_text; i++)
8330 if (!cr_or_whitespace (input_text[i]))
8331 break;
8332
8333 if (input_text[i] != '{')
8334 {
8335 if (braces_required_for_macro_args)
8336 {
8337 return ((char **)NULL);
8338 }
8339 else
8340 {
8341 /* Braces are not required to fill out the macro arguments. If
8342 this macro takes one argument, it is considered to be the
8343 remainder of the line, sans whitespace. */
8344 if (def->arglist && def->arglist[0] && !def->arglist[1])
8345 {
8346 char **arglist;
8347
8348 get_rest_of_line (&word);
8349 input_text_offset--;
8350 canon_white (word);
8351 arglist = (char **)xmalloc (2 * sizeof (char *));
8352 arglist[0] = word;
8353 arglist[1] = (char *)NULL;
8354 return (arglist);
8355 }
8356 else
8357 {
8358 /* The macro either took no arguments, or took more than
8359 one argument. In that case, it must be invoked with
8360 arguments surrounded by braces. */
8361 return ((char **)NULL);
8362 }
8363 }
8364 }
8365 return (get_brace_args (def->flags & ME_QUOTE_ARG));
8366 }
8367
8368 /* Substitute actual parameters for named parameters in body.
8369 The named parameters which appear in BODY must by surrounded
8370 reverse slashes, as in \foo\. */
8371 static char *
8372 apply (char **named, char **actuals, char *body)
8373 {
8374 register int i;
8375 int new_body_index, new_body_size;
8376 char *new_body, *text;
8377 int length_of_actuals;
8378
8379 length_of_actuals = array_len (actuals);
8380 new_body_size = strlen (body);
8381 new_body = (char *)xmalloc (1 + new_body_size);
8382
8383 /* Copy chars from BODY into NEW_BODY. */
8384 i = 0; new_body_index = 0;
8385
8386 while (1)
8387 {
8388 if (!body[i])
8389 break;
8390
8391 if (body[i] != '\\')
8392 new_body[new_body_index++] = body[i++];
8393 else
8394 {
8395 /* Snarf parameter name, check against named parameters. */
8396 char *param;
8397 int param_start, which, len;
8398
8399 param_start = ++i;
8400 while ((body[i]) && (body[i] != '\\'))
8401 i++;
8402
8403 len = i - param_start;
8404 param = (char *)xmalloc (1 + len);
8405 memcpy (param, body + param_start, len);
8406 param[len] = '\0';
8407
8408 if (body[i])
8409 i++;
8410
8411 /* Now check against named parameters. */
8412 for (which = 0; named && named[which]; which++)
8413 if (strcmp (named[which], param) == 0)
8414 break;
8415
8416 if (named[which])
8417 {
8418 if (which < length_of_actuals)
8419 text = actuals[which];
8420 else
8421 text = (char *)NULL;
8422
8423 if (!text)
8424 text = "";
8425
8426 len = strlen (text);
8427 }
8428 else
8429 {
8430 len += 2;
8431 text = (char *)xmalloc (1 + len);
8432 sprintf (text, "\\%s\\", param);
8433 }
8434
8435 if ((int) (2 + strlen (param)) < len)
8436 new_body = (char *)xrealloc
8437 (new_body, new_body_size += (1 + len));
8438
8439 free (param);
8440
8441 strcpy (new_body + new_body_index, text);
8442 new_body_index += len;
8443
8444 if (!named[which])
8445 free (text);
8446 }
8447 }
8448 new_body[new_body_index] = '\0';
8449 return (new_body);
8450 }
8451
8452 /* Execute the macro passed in DEF, a pointer to a MACRO_DEF. */
8453 static void
8454 execute_macro (MACRO_DEF *def)
8455 {
8456 char **arglist;
8457 int num_args;
8458 char *execution_string = (char *)NULL;
8459
8460 if (macro_expansion_output_stream && !me_inhibit_expansion)
8461 me_append_before_this_command ();
8462
8463 /* Find out how many arguments this macro definition takes. */
8464 num_args = array_len (def->arglist);
8465
8466 /* Gather the arguments present on the line if there are any. */
8467 arglist = get_macro_args (def);
8468
8469 if (num_args < array_len (arglist))
8470 {
8471 free_array (arglist);
8472 line_error ("Macro `%s' called with too many args", def->name);
8473 return;
8474 }
8475
8476 if (def->body)
8477 execution_string = apply (def->arglist, arglist, def->body);
8478
8479 free_array (arglist);
8480
8481 if (def->body)
8482 {
8483 if (macro_expansion_output_stream && !me_inhibit_expansion)
8484 {
8485 remember_itext (input_text, input_text_offset);
8486 me_execute_string (execution_string);
8487 }
8488 else
8489 execute_string (execution_string);
8490
8491 free (execution_string);
8492 }
8493 }
8494
8495 /* Read and remember the definition of a macro. */
8496 static void
8497 cm_macro (void)
8498 {
8499 register int i;
8500 char *name, **arglist, *body, *line;
8501 int body_size, body_index;
8502 int depth = 1;
8503 int defining_line = line_number;
8504 int flags = 0;
8505
8506 arglist = (char **)NULL;
8507 body = (char *)NULL;
8508 body_size = 0;
8509 body_index = 0;
8510
8511 if (macro_expansion_output_stream)
8512 me_append_before_this_command ();
8513
8514 skip_whitespace ();
8515
8516 /* Get the name of the macro. This is the set of characters which are
8517 not whitespace and are not `{' immediately following the @macro. */
8518 {
8519 int start = input_text_offset;
8520 int len;
8521
8522 for (i = start;
8523 (i < size_of_input_text) &&
8524 (input_text[i] != '{') &&
8525 (!cr_or_whitespace (input_text[i]));
8526 i++);
8527
8528 len = i - start;
8529 name = (char *)xmalloc (1 + len);
8530 strncpy (name, input_text + start, len);
8531 name[len] = '\0';
8532 input_text_offset = i;
8533 }
8534
8535 skip_whitespace ();
8536
8537 /* It is not required that the definition of a macro includes an arglist.
8538 If not, don't try to get the named parameters, just use a null list. */
8539 if (curchar () == '{')
8540 {
8541 int arglist_index = 0, arglist_size = 0;
8542 int gathering_words = 1;
8543 char *word = (char *)NULL;
8544 int character;
8545
8546 /* Read the words inside of the braces which determine the arglist.
8547 These words will be replaced within the body of the macro at
8548 execution time. */
8549
8550 input_text_offset++;
8551 skip_whitespace_and_newlines ();
8552
8553 while (gathering_words)
8554 {
8555 int len;
8556
8557 for (i = input_text_offset;
8558 (character = input_text[i]);
8559 i++)
8560 {
8561 switch (character)
8562 {
8563 case '\n':
8564 line_number++;
8565 case ' ':
8566 case '\t':
8567 case ',':
8568 case '}':
8569 /* Found the end of the current arglist word. Save it. */
8570 len = i - input_text_offset;
8571 word = (char *)xmalloc (1 + len);
8572 strncpy (word, input_text + input_text_offset, len);
8573 word[len] = '\0';
8574 input_text_offset = i;
8575
8576 /* Advance to the comma or close-brace that signified
8577 the end of the argument. */
8578 while ((character = curchar ())
8579 && character != ','
8580 && character != '}')
8581 input_text_offset++;
8582
8583 /* Add the word to our list of words. */
8584 if ((arglist_index + 2) >= arglist_size)
8585 arglist = (char **)xrealloc
8586 (arglist, (arglist_size += 10) * sizeof (char *));
8587
8588 arglist[arglist_index++] = word;
8589 arglist[arglist_index] = (char *)NULL;
8590 break;
8591 }
8592
8593 if (character == '}')
8594 {
8595 input_text_offset++;
8596 gathering_words = 0;
8597 break;
8598 }
8599
8600 if (character == ',')
8601 {
8602 input_text_offset++;
8603 skip_whitespace_and_newlines ();
8604 i = input_text_offset - 1;
8605 }
8606 }
8607 }
8608 }
8609
8610 /* Read the text carefully until we find an "@end macro" which
8611 matches this one. The text in between is the body of the macro. */
8612 skip_whitespace_and_newlines ();
8613
8614 while (depth)
8615 {
8616 if ((input_text_offset + 9) > size_of_input_text)
8617 {
8618 int temp_line = line_number;
8619 line_number = defining_line;
8620 line_error ("%cend macro not found", COMMAND_PREFIX);
8621 line_number = temp_line;
8622 return;
8623 }
8624
8625 get_rest_of_line (&line);
8626
8627 /* Handle commands only meaningful within a macro. */
8628 if ((*line == COMMAND_PREFIX) && (depth == 1) &&
8629 (strncmp (line + 1, "allow-recursion", 15) == 0) &&
8630 (line[16] == '\0' || whitespace (line[16])))
8631 {
8632 for (i = 16; whitespace (line[i]); i++);
8633 strcpy (line, line + i);
8634 flags |= ME_RECURSE;
8635 if (!*line)
8636 {
8637 free (line);
8638 continue;
8639 }
8640 }
8641
8642 if ((*line == COMMAND_PREFIX) && (depth == 1) &&
8643 (strncmp (line + 1, "quote-arg", 9) == 0) &&
8644 (line[10] == '\0' || whitespace (line[10])))
8645 {
8646 for (i = 16; whitespace (line[i]); i++);
8647 strcpy (line, line + i);
8648
8649 if (arglist && arglist[0] && !arglist[1])
8650 {
8651 flags |= ME_QUOTE_ARG;
8652 if (!*line)
8653 {
8654 free (line);
8655 continue;
8656 }
8657 }
8658 else
8659 {
8660 line_error ("%cquote-arg only useful when the macro takes a single argument",
8661 COMMAND_PREFIX);
8662 }
8663 }
8664
8665 if ((*line == COMMAND_PREFIX) &&
8666 (strncmp (line + 1, "macro ", 6) == 0))
8667 depth++;
8668
8669 if ((*line == COMMAND_PREFIX) &&
8670 (strncmp (line + 1, "end macro", 9) == 0))
8671 depth--;
8672
8673 if (depth)
8674 {
8675 if ((int) (body_index + strlen (line) + 3) >= body_size)
8676 body = (char *)xrealloc
8677 (body, body_size += 3 + strlen (line));
8678 strcpy (body + body_index, line);
8679 body_index += strlen (line);
8680 body[body_index++] = '\n';
8681 body[body_index] = '\0';
8682 }
8683 free (line);
8684 }
8685
8686 /* We now have the name, the arglist, and the body. However, BODY
8687 includes the final newline which preceded the `@end macro' text.
8688 Delete it. */
8689 if (body && strlen (body))
8690 body[strlen (body) - 1] = '\0';
8691
8692 add_macro (name, arglist, body, input_filename, defining_line, flags);
8693
8694 if (macro_expansion_output_stream)
8695 remember_itext (input_text, input_text_offset);
8696 }
8697
8698 static void
8699 cm_unmacro (void)
8700 {
8701 register int i;
8702 char *line, *name;
8703 MACRO_DEF *def;
8704
8705 if (macro_expansion_output_stream)
8706 me_append_before_this_command ();
8707
8708 get_rest_of_line (&line);
8709 canon_white (line);
8710
8711 for (i = 0; line[i] && !whitespace (line[i]); i++);
8712 name = (char *)xmalloc (i);
8713 strncpy (name, line, i);
8714 name[i] = '\0';
8715
8716 def = delete_macro (name);
8717
8718 if (def)
8719 {
8720 free (def->source_file);
8721 free (def->name);
8722 free (def->body);
8723
8724 if (def->arglist)
8725 {
8726 register int ii;
8727
8728 for (ii = 0; def->arglist[ii]; i++)
8729 free (def->arglist[ii]);
8730
8731 free (def->arglist);
8732 }
8733
8734 free (def);
8735 }
8736
8737 free (line);
8738 free (name);
8739
8740 if (macro_expansion_output_stream)
8741 remember_itext (input_text, input_text_offset);
8742 }
8743
8744 /* How to output sections of the input file verbatim. */
8745
8746 /* Set the value of POINTER's offset to OFFSET. */
8747 static ITEXT *
8748 remember_itext (char *pointer, int offset)
8749 {
8750 register int i;
8751 ITEXT *itext = (ITEXT *)NULL;
8752
8753 /* If we have no info, initialize a blank list. */
8754 if (!itext_info)
8755 {
8756 itext_info = (ITEXT **)xmalloc ((itext_size = 10) * sizeof (ITEXT *));
8757 for (i = 0; i < itext_size; i++)
8758 itext_info[i] = (ITEXT *)NULL;
8759 }
8760
8761 /* If the pointer is already present in the list, then set the offset. */
8762 for (i = 0; i < itext_size; i++)
8763 if ((itext_info[i] != (ITEXT *)NULL) &&
8764 (itext_info[i]->pointer == pointer))
8765 {
8766 itext = itext_info[i];
8767 itext_info[i]->offset = offset;
8768 break;
8769 }
8770
8771 if (i == itext_size)
8772 {
8773 /* Find a blank slot, (or create a new one), and remember the
8774 pointer and offset. */
8775 for (i = 0; i < itext_size; i++)
8776 if (itext_info[i] == (ITEXT *)NULL)
8777 break;
8778
8779 /* If not found, then add some slots. */
8780 if (i == itext_size)
8781 {
8782 register int j;
8783
8784 itext_info = (ITEXT **)xrealloc
8785 (itext_info, (itext_size += 10) * sizeof (ITEXT *));
8786
8787 for (j = i; j < itext_size; j++)
8788 itext_info[j] = (ITEXT *)NULL;
8789 }
8790
8791 /* Now add the pointer and the offset. */
8792 itext_info[i] = (ITEXT *)xmalloc (sizeof (ITEXT));
8793 itext_info[i]->pointer = pointer;
8794 itext_info[i]->offset = offset;
8795 itext = itext_info[i];
8796 }
8797 return (itext);
8798 }
8799
8800 /* Forget the input text associated with POINTER. */
8801 static void
8802 forget_itext (char *pointer)
8803 {
8804 register int i;
8805
8806 for (i = 0; i < itext_size; i++)
8807 if (itext_info[i] && (itext_info[i]->pointer == pointer))
8808 {
8809 free (itext_info[i]);
8810 itext_info[i] = (ITEXT *)NULL;
8811 break;
8812 }
8813 }
8814
8815 /* Append the text which appeared in input_text from the last offset to
8816 the character just before the command that we are currently executing. */
8817 static void
8818 me_append_before_this_command (void)
8819 {
8820 register int i;
8821
8822 for (i = input_text_offset; i && (input_text[i] != COMMAND_PREFIX); i--);
8823 maybe_write_itext (input_text, i);
8824 }
8825
8826 /* Similar to execute_string (), but only takes a single string argument,
8827 and remembers the input text location, etc. */
8828 static void
8829 me_execute_string (char *execution_string)
8830 {
8831 pushfile ();
8832 input_text_offset = 0;
8833 input_text = execution_string;
8834 input_filename = strdup (input_filename);
8835 size_of_input_text = strlen (execution_string);
8836
8837 remember_itext (execution_string, 0);
8838
8839 executing_string++;
8840 reader_loop ();
8841 popfile ();
8842 executing_string--;
8843 }
8844
8845 /* Append the text which appears in input_text from the last offset to
8846 the current OFFSET. */
8847 static void
8848 append_to_expansion_output (int offset)
8849 {
8850 register int i;
8851 ITEXT *itext = (ITEXT *)NULL;
8852
8853 for (i = 0; i < itext_size; i++)
8854 if (itext_info[i] && itext_info[i]->pointer == input_text)
8855 {
8856 itext = itext_info[i];
8857 break;
8858 }
8859
8860 if (!itext)
8861 itext = remember_itext (input_text, 0);
8862
8863 if (offset > itext_info[i]->offset)
8864 {
8865 write_region_to_macro_output
8866 (input_text, itext_info[i]->offset, offset);
8867 remember_itext (input_text, offset);
8868 }
8869 }
8870
8871 /* Only write this input text iff it appears in our itext list. */
8872 static void
8873 maybe_write_itext (char *pointer, int offset)
8874 {
8875 register int i;
8876 ITEXT *itext = (ITEXT *)NULL;
8877
8878 for (i = 0; i < itext_size; i++)
8879 if (itext_info[i] && (itext_info[i]->pointer == pointer))
8880 {
8881 itext = itext_info[i];
8882 break;
8883 }
8884
8885 if (itext && (itext->offset < offset))
8886 {
8887 write_region_to_macro_output (itext->pointer, itext->offset, offset);
8888 remember_itext (pointer, offset);
8889 }
8890 }
8891
8892 static void
8893 write_region_to_macro_output (char *string, int start, int end)
8894 {
8895 if (macro_expansion_output_stream)
8896 fwrite (string + start, 1, end - start, macro_expansion_output_stream);
8897 }
8898
8899 #endif /* HAVE_MACROS */
8900
8901 /* Return the length of the array in ARRAY. */
8902 static int
8903 array_len (char **array)
8904 {
8905 register int i = 0;
8906
8907 if (array)
8908 for (i = 0; array[i] != (char *)NULL; i++);
8909
8910 return (i);
8911 }
8912
8913 static void
8914 free_array (char **array)
8915 {
8916 if (array)
8917 {
8918 register int i;
8919
8920 for (i = 0; array[i] != (char *)NULL; i++)
8921 free (array[i]);
8922
8923 free (array);
8924 }
8925 }
8926
8927 /* Function is used even when we don't have macros. Although, I have
8928 to admit, it is unlikely that you would have a use for it if you
8929 aren't using macros. */
8930 static char **
8931 get_brace_args (int quote_single)
8932 {
8933 char **arglist, *word;
8934 int arglist_index, arglist_size;
8935 int character, escape_seen, start;
8936 int depth = 1;
8937
8938 /* There is an arglist in braces here, so gather the args inside of it. */
8939 skip_whitespace_and_newlines ();
8940 input_text_offset++;
8941 arglist = (char **)NULL;
8942 arglist_index = arglist_size = 0;
8943
8944 get_arg:
8945 skip_whitespace_and_newlines ();
8946 start = input_text_offset;
8947 escape_seen = 0;
8948
8949 while ((character = curchar ()))
8950 {
8951 if (character == '\\')
8952 {
8953 input_text_offset += 2;
8954 escape_seen = 1;
8955 }
8956 else if (character == '{')
8957 {
8958 depth++;
8959 input_text_offset++;
8960 }
8961 else if ((character == ',' && !quote_single) ||
8962 ((character == '}') && depth == 1))
8963 {
8964 int len = input_text_offset - start;
8965
8966 if (len || (character != '}'))
8967 {
8968 word = (char *)xmalloc (1 + len);
8969 strncpy (word, input_text + start, len);
8970 word[len] = '\0';
8971
8972 /* Clean up escaped characters. */
8973 if (escape_seen)
8974 {
8975 register int i;
8976
8977 for (i = 0; word[i]; i++)
8978 if (word[i] == '\\')
8979 memcpy (word + i, word + i + 1, strlen (word + i + 1));
8980 }
8981
8982 if (arglist_index + 2 >= arglist_size)
8983 arglist = (char **)xrealloc
8984 (arglist, (arglist_size += 10) * sizeof (char *));
8985
8986 arglist[arglist_index++] = word;
8987 arglist[arglist_index] = (char *)NULL;
8988 }
8989
8990 input_text_offset++;
8991 if (character == '}')
8992 break;
8993 else
8994 goto get_arg;
8995 }
8996 else if (character == '}')
8997 {
8998 depth--;
8999 input_text_offset++;
9000 }
9001 else
9002 input_text_offset++;
9003 }
9004 return (arglist);
9005 }
9006
9007 /* **************************************************************** */
9008 /* */
9009 /* Looking For Include Files */
9010 /* */
9011 /* **************************************************************** */
9012
9013 /* Given a string containing units of information separated by colons,
9014 return the next one pointed to by INDEX, or NULL if there are no more.
9015 Advance INDEX to the character after the colon. */
9016 static char *
9017 extract_colon_unit (char *string, int *indecks)
9018 {
9019 int i, start;
9020
9021 i = *indecks;
9022
9023 if (!string || (i >= (int) strlen (string)))
9024 return ((char *)NULL);
9025
9026 /* Each call to this routine leaves the index pointing at a colon if
9027 there is more to the path. If I is > 0, then increment past the
9028 `:'. If I is 0, then the path has a leading colon. Trailing colons
9029 are handled OK by the `else' part of the if statement; an empty
9030 string is returned in that case. */
9031 if (i && string[i] == ':')
9032 i++;
9033
9034 start = i;
9035
9036 while (string[i] && string[i] != ':') i++;
9037
9038 *indecks = i;
9039
9040 if (i == start)
9041 {
9042 if (string[i])
9043 (*indecks)++;
9044
9045 /* Return "" in the case of a trailing `:'. */
9046 return (strdup (""));
9047 }
9048 else
9049 {
9050 char *value;
9051
9052 value = (char *)xmalloc (1 + (i - start));
9053 strncpy (value, &string[start], (i - start));
9054 value [i - start] = '\0';
9055
9056 return (value);
9057 }
9058 }
9059
9060 /* Return the full pathname for FILENAME by searching along PATH.
9061 When found, return the stat () info for FILENAME in FINFO.
9062 If PATH is NULL, only the current directory is searched.
9063 If the file could not be found, return a NULL pointer. */
9064 static char *
9065 get_file_info_in_path (char *filename, char *path, struct stat *finfo)
9066 {
9067 char *dir;
9068 int result, indecks = 0;
9069
9070 if (path == (char *)NULL)
9071 path = ".";
9072
9073 /* Handle absolute pathnames. "./foo", "/foo", "../foo". */
9074 if (*filename == '/' ||
9075 (*filename == '.' &&
9076 (filename[1] == '/' ||
9077 (filename[1] == '.' && filename[2] == '/'))))
9078 {
9079 if (stat (filename, finfo) == 0)
9080 return (strdup (filename));
9081 else
9082 return ((char *)NULL);
9083 }
9084
9085 while ((dir = extract_colon_unit (path, &indecks)))
9086 {
9087 char *fullpath;
9088
9089 if (!*dir)
9090 {
9091 free (dir);
9092 dir = strdup (".");
9093 }
9094
9095 fullpath = (char *)xmalloc (2 + strlen (dir) + strlen (filename));
9096 sprintf (fullpath, "%s/%s", dir, filename);
9097 free (dir);
9098
9099 result = stat (fullpath, finfo);
9100
9101 if (result == 0)
9102 return (fullpath);
9103 else
9104 free (fullpath);
9105 }
9106 return ((char *)NULL);
9107 }