]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/texinfo/makeinfo/makeinfo.c
This commit was generated by cvs2svn to compensate for changes in r91684,
[FreeBSD/FreeBSD.git] / contrib / texinfo / makeinfo / makeinfo.c
1 /* makeinfo -- convert Texinfo source into other formats.
2    $Id: makeinfo.c,v 1.171 1999/09/19 15:24:44 karl Exp $
3
4    Copyright (C) 1987, 92, 93, 94, 95, 96, 97, 98, 99
5    Free Software Foundation, Inc.
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2, or (at your option)
10    any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20
21    Makeinfo was authored by Brian Fox (bfox@ai.mit.edu). */
22
23 #include "system.h"
24 #include "getopt.h"
25
26 #define COMPILING_MAKEINFO
27 #include "makeinfo.h"
28 #include "cmds.h"
29 #include "files.h"
30 #include "footnote.h"
31 #include "html.h"
32 #include "index.h"
33 #include "insertion.h"
34 #include "macro.h"
35 #include "node.h"
36 #include "toc.h"
37
38 /* We'd like to take advantage of _doprnt if it's around, a la error.c,
39    but then we'd have no VA_SPRINTF.  */
40 #if HAVE_VPRINTF
41 # if __STDC__
42 #  include <stdarg.h>
43 #  define VA_START(args, lastarg) va_start(args, lastarg)
44 # else
45 #  include <varargs.h>
46 #  define VA_START(args, lastarg) va_start(args)
47 # endif
48 # define VA_FPRINTF(file, fmt, ap) vfprintf (file, fmt, ap)
49 # define VA_SPRINTF(str, fmt, ap) vsprintf (str, fmt, ap)
50 #else /* not HAVE_VPRINTF */
51 # define VA_START(args, lastarg)
52 # define va_alist a1, a2, a3, a4, a5, a6, a7, a8
53 # define va_dcl char *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8;
54 # define va_end(args)
55 #endif
56
57 /* DJGPP supports /dev/null, which is okay for Unix aficionados,
58    shell scripts and Makefiles, but interactive DOS die-hards
59    would probably want to have NUL as well.  */
60 #ifdef __DJGPP__
61 # define ALSO_NULL_DEVICE  "NUL"
62 #else
63 # define ALSO_NULL_DEVICE  ""
64 #endif
65
66 /* You can change some of the behavior of Makeinfo by changing the
67    following defines: */
68
69 /* Define INDENT_PARAGRAPHS_IN_TABLE if you want the paragraphs which
70    appear within an @table, @ftable, or @itemize environment to have
71    standard paragraph indentation.  Without this, such paragraphs have
72    no starting indentation. */
73 /* #define INDENT_PARAGRAPHS_IN_TABLE */
74
75 /* Define PARAGRAPH_START_INDENT to be the amount of indentation that
76    the first lines of paragraphs receive by default, where no other
77    value has been specified.  Users can change this value on the command
78    line, with the --paragraph-indent option, or within the texinfo file,
79    with the @paragraphindent command. */
80 #define PARAGRAPH_START_INDENT 3
81
82 /* Define DEFAULT_PARAGRAPH_SPACING as the number of blank lines that you
83    wish to appear between paragraphs.  A value of 1 creates a single blank
84    line between paragraphs.  Paragraphs are defined by 2 or more consecutive
85    newlines in the input file (i.e., one or more blank lines). */
86 #define DEFAULT_PARAGRAPH_SPACING 1
87 \f
88 /* Global variables.  */
89
90 /* The output file name. */
91 char *output_filename = NULL;
92
93 /* Name of the output file that the user elected to pass on the command line.
94    Such a name overrides any name found with the @setfilename command. */
95 char *command_output_filename = NULL;
96
97 /* Flags which control initial output string for xrefs. */
98 int px_ref_flag = 0;
99 int ref_flag = 0;
100
101 #define INITIAL_PARAGRAPH_SPACE 5000
102 int paragraph_buffer_len = INITIAL_PARAGRAPH_SPACE;
103
104 /* The amount of indentation to add at the starts of paragraphs.
105    0 means don't change existing indentation at paragraph starts.
106    > 0 is amount to indent new paragraphs by.
107    < 0 means indent to column zero by removing indentation if necessary.
108
109    This is normally zero, but some people prefer paragraph starts to be
110    somewhat more indented than paragraph bodies.  A pretty value for
111    this is 3. */
112 int paragraph_start_indent = PARAGRAPH_START_INDENT;
113
114 /* Indentation that is pending insertion.  We have this for hacking lines
115    which look blank, but contain whitespace.  We want to treat those as
116    blank lines. */
117 int pending_indent = 0;
118
119 /* The index in our internal command table of the currently
120    executing command. */
121 int command_index;
122
123 /* A search string which is used to find the first @setfilename. */
124 char setfilename_search[] =
125   { COMMAND_PREFIX,
126       's', 'e', 't', 'f', 'i', 'l', 'e', 'n', 'a', 'm', 'e', 0 };
127
128 /* Values for calling handle_variable_internal (). */
129 #define SET     1
130 #define CLEAR   2
131 #define IFSET   3
132 #define IFCLEAR 4
133
134 /* Flags controlling the operation of the program. */
135
136 /* Default is to remove output if there were errors.  */
137 int force = 0;
138
139 /* Default is to notify users of bad choices. */
140 int print_warnings = 1;
141
142 /* Number of errors that we tolerate on a given fileset. */
143 int max_error_level = 100;
144
145 /* The actual last inserted character.  Note that this may be something
146    other than NEWLINE even if last_char_was_newline is 1. */
147 int last_inserted_character = 0;
148
149 /* Nonzero means that a newline character has already been
150    inserted, so close_paragraph () should insert one less. */
151 int line_already_broken = 0;
152
153 /* When nonzero we have finished an insertion (see end_insertion ()) and we
154    want to ignore false continued paragraph closings. */
155 int insertion_paragraph_closed = 0;
156
157 /* Nonzero means attempt to make all of the lines have fill_column width. */
158 int do_justification = 0;
159
160 typedef struct brace_element
161 {
162   struct brace_element *next;
163   COMMAND_FUNCTION *proc;
164   char *command;
165   int pos, line;
166   int in_fixed_width_font;
167 } BRACE_ELEMENT;
168
169 BRACE_ELEMENT *brace_stack = NULL;
170
171 extern void do_multitable (), end_multitable ();
172
173 void push_node_filename (), pop_node_filename ();
174 void remember_error ();
175 void convert_from_stream (), convert_from_file (), convert_from_loaded_file ();
176 void init_internals (), init_paragraph (), init_brace_stack ();
177 void init_insertion_stack (), init_indices ();
178 void init_tag_table (), write_tag_table (), write_tag_table_internal ();
179 void validate_file (), validate_other_references (), split_file ();
180 void free_node_references (), handle_variable ();
181 void handle_variable_internal ();
182 void normalize_node_name ();
183 void add_anchor_name ();
184 void free_node_node_references (), remember_node_node_reference ();
185
186 char **get_brace_args ();
187 int array_len ();
188 void free_array ();
189 static int end_of_sentence_p ();
190 static void isolate_nodename ();
191 void reader_loop ();
192 void remember_brace (), remember_brace_1 ();
193 void pop_and_call_brace (), discard_braces ();
194 void add_word (), add_char (), insert (), flush_output ();
195 void insert_string ();
196 void close_paragraph ();
197 void ignore_blank_line ();
198 void do_flush_right_indentation (), discard_insertions ();
199 void start_paragraph (), indent ();
200 void inhibit_output_flushing (), uninhibit_output_flushing ();
201 int set_paragraph_indent ();
202 int self_delimiting (), search_forward ();
203 int multitable_item (), number_of_node ();
204 extern void add_link (), add_escaped_anchor_name ();
205
206 void me_execute_string_keep_state ();
207 void maybe_update_execution_strings ();
208
209 extern char *escape_string ();
210 extern void insert_html_tag ();
211 extern void sectioning_html ();
212 extern void add_link ();
213
214 #if defined (VA_FPRINTF) && __STDC__
215 /* Unfortunately we must use prototypes if we are to use <stdarg.h>.  */
216 void add_word_args (char *, ...);
217 void execute_string (char *, ...);
218 #else
219 void add_word_args ();
220 void execute_string ();
221 #endif /* no prototypes */
222 \f
223 /* Error handling.  */
224
225 /* Number of errors encountered. */
226 int errors_printed = 0;
227
228 /* Print the last error gotten from the file system. */
229 int
230 fs_error (filename)
231      char *filename;
232 {
233   remember_error ();
234   perror (filename);
235   return 0;
236 }
237
238 /* Print an error message, and return false. */
239 void
240 #if defined (VA_FPRINTF) && __STDC__
241 error (char *format, ...)
242 #else
243 error (format, va_alist)
244      char *format;
245      va_dcl
246 #endif
247 {
248 #ifdef VA_FPRINTF
249   va_list ap;
250 #endif
251
252   remember_error ();
253
254   VA_START (ap, format);
255 #ifdef VA_FPRINTF
256   VA_FPRINTF (stderr, format, ap);
257 #else
258   fprintf (stderr, format, a1, a2, a3, a4, a5, a6, a7, a8);
259 #endif /* not VA_FPRINTF */
260   va_end (ap);
261
262   putc ('\n', stderr);
263 }
264
265 /* Just like error (), but print the line number as well. */
266 void
267 #if defined (VA_FPRINTF) && __STDC__
268 line_error (char *format, ...)
269 #else
270 line_error (format, va_alist)
271    char *format;
272    va_dcl
273 #endif
274 {
275 #ifdef VA_FPRINTF
276   va_list ap;
277 #endif
278
279   remember_error ();
280   fprintf (stderr, "%s:%d: ", input_filename, line_number);
281
282   VA_START (ap, format);
283 #ifdef VA_FPRINTF
284   VA_FPRINTF (stderr, format, ap);
285 #else
286   fprintf (stderr, format, a1, a2, a3, a4, a5, a6, a7, a8);
287 #endif /* not VA_FPRINTF */
288   va_end (ap);
289
290   fprintf (stderr, ".\n");
291 }
292
293 void
294 #if defined (VA_FPRINTF) && __STDC__
295 warning (char *format, ...)
296 #else
297 warning (format, va_alist)
298      char *format;
299      va_dcl
300 #endif
301 {
302 #ifdef VA_FPRINTF
303   va_list ap;
304 #endif
305
306   if (print_warnings)
307     {
308       fprintf (stderr, _("%s:%d: warning: "), input_filename, line_number);
309
310       VA_START (ap, format);
311 #ifdef VA_FPRINTF
312       VA_FPRINTF (stderr, format, ap);
313 #else
314       fprintf (stderr, format, a1, a2, a3, a4, a5, a6, a7, a8);
315 #endif /* not VA_FPRINTF */
316       va_end (ap);
317
318       fprintf (stderr, ".\n");
319     }
320 }
321
322
323 /* Remember that an error has been printed.  If more than
324    max_error_level have been printed, then exit the program. */
325 void
326 remember_error ()
327 {
328   errors_printed++;
329   if (max_error_level && (errors_printed > max_error_level))
330     {
331       fprintf (stderr, _("Too many errors!  Gave up.\n"));
332       flush_file_stack ();
333       cm_bye ();
334       xexit (1);
335     }
336 }
337
338 /* The other side of a malformed expression. */
339 void
340 misplaced_brace ()
341 {
342   line_error (_("Misplaced %c"), '}');
343 }
344 \f
345 /* Main.  */
346
347 /* Display the version info of this invocation of Makeinfo. */
348 static void
349 print_version_info ()
350 {
351   printf ("makeinfo (GNU %s) %s\n", PACKAGE, VERSION);
352 }
353
354 /* If EXIT_VALUE is zero, print the full usage message to stdout.
355    Otherwise, just say to use --help for more info.
356    Then exit with EXIT_VALUE. */
357 static void
358 usage (exit_value)
359      int exit_value;
360 {
361   if (exit_value != 0)
362     fprintf (stderr, _("Try `%s --help' for more information.\n"), progname);
363   else
364   {
365     printf (_("Usage: %s [OPTION]... TEXINFO-FILE...\n\
366 \n\
367 Translate Texinfo source documentation to various other formats:\n\
368 Info files suitable for reading online with Emacs or standalone GNU Info\n\
369 (by default); plain text (with --no-headers); or HTML (with --html).\n\
370 \n\
371 Options:\n\
372  --commands-in-node-names  allow @ commands in node names.\n\
373  -D VAR                    define a variable, as with @set.\n\
374  -E, --macro-expand FILE   output macro-expanded source to FILE.\n\
375  --error-limit=NUM         quit after NUM errors (default %d).\n\
376  --fill-column=NUM         break Info lines at NUM characters (default %d).\n\
377  --footnote-style=STYLE    output footnotes according to STYLE:\n\
378                             `separate' to place footnotes in their own node,\n\
379                             `end' to place the footnotes at the end of the\n\
380                              node in which they are defined (the default).\n\
381  --force                   preserve output even if errors.\n\
382  --help                    display this help and exit.\n\
383  --html                    output HTML rather than Info format;\n\
384  -I DIR                    append DIR to the @include search path.\n\
385  --ifhtml                  process @ifhtml and @html text even when not\n\
386                              generating HTML.\n\
387  --ifinfo                  process @ifinfo text even when generating HTML.\n\
388  --iftex                   process @iftex and @tex text.\n\
389                              implies --no-split.\n"),
390             progname, max_error_level, fill_column);
391     printf (_("\
392  --no-headers              suppress Info node separators and Node: lines and\n\
393                              write to standard output without --output.\n\
394  --no-ifhtml               do not process @ifhtml and @html text.\n\
395  --no-ifinfo               do not process @ifinfo text.\n\
396  --no-iftex                do not process @iftex and @tex text.\n\
397  --no-split                suppress splitting of large Info output files or\n\
398                            generation of one HTML file per node.\n\
399  --no-validate             suppress node cross-reference validation.\n\
400  --no-warn                 suppress warnings (but not errors).\n\
401  --number-sections         include chapter, section, etc. numbers in output.\n\
402  -o, --output=FILE         output to FILE, ignoring any @setfilename.\n\
403  -P DIR                    prepend DIR to the @include search path.\n\
404  --paragraph-indent=VAL    indent Info paragraphs by VAL spaces (default %d).\n\
405                              if VAL is `none', do not indent;\n\
406                              if VAL is `asis', preserve existing indentation.\n\
407  --reference-limit=NUM     warn about at most NUM references (default %d).\n\
408  -U VAR                    undefine a variable, as with @clear.\n\
409  -v, --verbose             explain what is being done.\n\
410  --version                 display version information and exit.\n\
411 "),
412             paragraph_start_indent, reference_warning_limit);
413   }
414
415   puts (_("\n\
416 The defaults for the @if... conditionals depend on the output format:\n\
417 if generating HTML, --ifhtml is on and the others are off;\n\
418 if generating Info or plain text, --ifinfo is on and the others are off.\n\
419 \n\
420 Examples:\n\
421   makeinfo foo.texi                    write Info to foo's @setfilename\n\
422   makeinfo --html foo.texi             write HTML to foo's @setfilename\n\
423   makeinfo --no-headers -o - foo.texi  write plain text to standard output\n\
424   makeinfo --number-sections foo.texi  write Info with numbered sections\n\
425   makeinfo --no-split foo.texi         write one Info file however big\n\
426 \n\
427 Email bug reports to bug-texinfo@gnu.org,\n\
428 general questions and discussion to help-texinfo@gnu.org."));
429   xexit (exit_value);
430 }
431
432 struct option long_options[] =
433 {
434   { "commands-in-node-names", 0, &expensive_validation, 1 },
435   { "error-limit", 1, 0, 'e' },
436   { "fill-column", 1, 0, 'f' },
437   { "footnote-style", 1, 0, 's' },
438   { "force", 0, &force, 1 },
439   { "help", 0, 0, 'h' },
440   { "html", 0, 0, 'w' },
441   { "ifhtml", 0, &process_html, 1 },
442   { "ifinfo", 0, &process_info, 1 },
443   { "iftex", 0, &process_tex, 1 },
444   { "macro-expand", 1, 0, 'E' },
445   { "no-headers", 0, &no_headers, 1 },
446   { "no-ifhtml", 0, &process_html, 0 },
447   { "no-ifinfo", 0, &process_info, 0 },
448   { "no-iftex", 0, &process_tex, 0 },
449   { "no-number-footnotes", 0, &number_footnotes, 0 },
450   { "no-number-sections", 0, &number_sections, 0 },
451   { "no-pointer-validate", 0, &validating, 0 },
452   { "no-split", 0, &splitting, 0 },
453   { "no-validate", 0, &validating, 0 },
454   { "no-warn", 0, &print_warnings, 0 },
455   { "number-footnotes", 0, &number_footnotes, 1 },
456   { "number-sections", 0, &number_sections, 1 },
457   { "output", 1, 0, 'o' },
458   { "paragraph-indent", 1, 0, 'p' },
459   { "reference-limit", 1, 0, 'r' },
460   { "verbose", 0, &verbose_mode, 1 },
461   { "version", 0, 0, 'V' },
462   {NULL, 0, NULL, 0}
463 };
464
465 /* For each file mentioned in the command line, process it, turning
466    Texinfo commands into wonderfully formatted output text. */
467 int
468 main (argc, argv)
469      int argc;
470      char **argv;
471 {
472   extern int errors_printed;
473   int c, ind;
474   int reading_from_stdin = 0;
475
476 #ifdef HAVE_SETLOCALE
477   /* Do not use LC_ALL, because LC_NUMERIC screws up the scanf parsing
478      of the argument to @multicolumn.  */
479   setlocale (LC_TIME, "");
480   setlocale (LC_MESSAGES, "");
481   setlocale (LC_CTYPE, "");
482   setlocale (LC_COLLATE, "");
483 #endif
484
485   /* Set the text message domain.  */
486   bindtextdomain (PACKAGE, LOCALEDIR);
487   textdomain (PACKAGE);
488
489   /* Parse argument flags from the input line. */
490   while ((c = getopt_long (argc, argv, "D:e:E:f:hI:o:p:P:r:s:U:vV:w",
491                            long_options, &ind)) != EOF)
492     {
493       if (c == 0 && long_options[ind].flag == 0)
494         c = long_options[ind].val;
495
496       switch (c)
497         {
498         case 'D':
499         case 'U':
500           /* User specified variable to set or clear. */
501           handle_variable_internal ((c == 'D') ? SET : CLEAR, optarg);
502           break;
503
504         case 'e': /* --error-limit */
505           if (sscanf (optarg, "%d", &max_error_level) != 1)
506             {
507               fprintf (stderr,
508                       _("%s: %s arg must be numeric, not `%s'.\n"),
509                       "--error-limit", progname, optarg);
510               usage (stderr, 1);
511             }
512           break;
513
514         case 'E': /* --macro-expand */
515           if (!macro_expansion_output_stream)
516             {
517               macro_expansion_filename = optarg;
518               macro_expansion_output_stream
519                 = strcmp (optarg, "-") == 0 ? stdout : fopen (optarg, "w");
520               if (!macro_expansion_output_stream)
521                 error (_("Couldn't open macro expansion output `%s'"), optarg);
522             }
523           else
524             error (_("Cannot specify more than one macro expansion output"));
525           break;
526
527         case 'f': /* --fill-column */
528           if (sscanf (optarg, "%d", &fill_column) != 1)
529             {
530               fprintf (stderr,
531                        _("%s: %s arg must be numeric, not `%s'.\n"),
532                        "--fill-column", progname, optarg);
533               usage (1);
534             }
535           break;
536
537         case 'h': /* --help */
538           usage (0);
539           break;
540
541         case 'I':
542           /* Append user-specified dir to include file path. */
543           if (!include_files_path)
544             include_files_path = xstrdup (".");
545
546           include_files_path = (char *)
547             xrealloc (include_files_path,
548                       2 + strlen (include_files_path) + strlen (optarg));
549           strcat (include_files_path, PATH_SEP);
550           strcat (include_files_path, optarg);
551           break;
552
553         case 'o': /* --output */
554           command_output_filename = xstrdup (optarg);
555           break;
556
557         case 'p': /* --paragraph-indent */
558           if (set_paragraph_indent (optarg) < 0)
559             {
560               fprintf (stderr,
561    _("%s: --paragraph-indent arg must be numeric/`none'/`asis', not `%s'.\n"),
562                        progname, optarg);
563               usage (1);
564             }
565           break;
566
567         case 'P':
568           /* Prepend user-specified include dir to include path. */
569           if (!include_files_path)
570             {
571               include_files_path = xstrdup (optarg);
572               include_files_path = xrealloc (include_files_path,
573                            strlen (include_files_path) + 3); /* 3 for ":.\0" */
574               strcat (strcat (include_files_path, PATH_SEP), ".");
575             }
576           else
577             {
578               char *tmp = xstrdup (include_files_path);
579               include_files_path = xrealloc (include_files_path,
580           strlen (include_files_path) + strlen (optarg) + 2); /* 2 for ":\0" */
581               strcpy (include_files_path, optarg);
582               strcat (include_files_path, ":");
583               strcat (include_files_path, tmp);
584               free (tmp);
585             }
586           break;
587
588         case 'r': /* --reference-limit */
589           if (sscanf (optarg, "%d", &reference_warning_limit) != 1)
590             {
591               fprintf (stderr,
592                      _("%s: %s arg must be numeric, not `%s'.\n"),
593                      "--reference-limit", progname, optarg);
594               usage (1);
595             }
596           break;
597
598         case 's': /* --footnote-style */
599           if (set_footnote_style (optarg) < 0)
600             {
601               fprintf (stderr,
602           _("%s: --footnote-style arg must be `separate' or `end', not `%s'.\n"),
603                        progname, optarg);
604               usage (1);
605             }
606           footnote_style_preset = 1;
607           break;
608
609         case 'v':
610           verbose_mode++;
611           break;
612
613         case 'V': /* --version */
614           print_version_info ();
615           puts ("");
616           printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
617 There is NO warranty.  You may redistribute this software\n\
618 under the terms of the GNU General Public License.\n\
619 For more information about these matters, see the files named COPYING.\n"),
620                   "1999");
621           exit (0);
622           break;
623
624         case 'w': /* --html */
625           html = 1;
626           process_html = 1;
627           process_info = 0;
628           splitting = 0; /* too complicated for now */
629           break;
630
631         case '?':
632           usage (1);
633           break;
634         }
635     }
636
637   if (!validating)
638     expensive_validation = 0;
639
640   if (optind == argc)
641     {
642       /* Check to see if input is a file.  If so, process that. */
643       if (!isatty (fileno (stdin)))
644         reading_from_stdin = 1;
645       else
646         {
647           fprintf (stderr, _("%s: missing file argument.\n"), progname);
648           usage (1);
649         }
650     }
651
652   if (no_headers)
653     {
654       if (html && splitting)
655         { /* --no-headers --no-split --html indicates confusion. */
656           fprintf (stderr,
657                    "%s: --no-headers conflicts with --no-split for --html.\n",
658                    progname);
659           usage (1);
660         }
661
662       /* --no-headers implies --no-split.  */
663       splitting = 0;
664
665       /* If the user did not specify an output file, use stdout. */
666       if (!command_output_filename)
667         command_output_filename = xstrdup ("-");
668     }
669
670   if (verbose_mode)
671     print_version_info ();
672
673   /* Remaining arguments are file names of texinfo files.
674      Convert them, one by one. */
675   if (!reading_from_stdin)
676     {
677       while (optind != argc)
678         convert_from_file (argv[optind++]);
679     }
680   else
681     convert_from_stream (stdin, "stdin");
682
683   return errors_printed ? 2 : 0;
684 }
685
686 \f
687 /* Hacking tokens and strings.  */
688
689 /* Return the next token as a string pointer.  We cons the string. */
690 char *
691 read_token ()
692 {
693   int i, character;
694   char *result;
695
696   /* If the first character to be read is self-delimiting, then that
697      is the command itself. */
698   character = curchar ();
699   if (self_delimiting (character))
700     {
701       input_text_offset++;
702
703       if (character == '\n')
704         line_number++;
705
706       result = xstrdup (" ");
707       *result = character;
708       return result;
709     }
710
711   for (i = 0; ((input_text_offset != input_text_length)
712                && (character = curchar ())
713                && command_char (character));
714        i++, input_text_offset++);
715   result = xmalloc (i + 1);
716   memcpy (result, &input_text[input_text_offset - i], i);
717   result[i] = 0;
718   return result;
719 }
720
721 /* Return nonzero if CHARACTER is self-delimiting. */
722 int
723 self_delimiting (character)
724      int character;
725 {
726   /* @; and @\ are not Texinfo commands, but they are listed here
727      anyway.  I don't know why.  --karl, 10aug96.  */
728   return strchr ("~{|}`^\\@?=;:.-,*\'\" !\n\t", character) != NULL;
729 }
730
731 /* Clear whitespace from the front and end of string. */
732 void
733 canon_white (string)
734      char *string;
735 {
736   int len = strlen (string);
737   int x;
738
739   if (!len)
740     return;
741
742   for (x = 0; x < len; x++)
743     {
744       if (!cr_or_whitespace (string[x]))
745         {
746           strcpy (string, string + x);
747           break;
748         }
749     }
750   len = strlen (string);
751   if (len)
752     len--;
753   while (len > -1 && cr_or_whitespace (string[len]))
754     len--;
755   string[len + 1] = 0;
756 }
757
758 /* Bash STRING, replacing all whitespace with just one space. */
759 void
760 fix_whitespace (string)
761      char *string;
762 {
763   char *temp = xmalloc (strlen (string) + 1);
764   int string_index = 0;
765   int temp_index = 0;
766   int c;
767
768   canon_white (string);
769
770   while (string[string_index])
771     {
772       c = temp[temp_index++] = string[string_index++];
773
774       if (c == ' ' || c == '\n' || c == '\t')
775         {
776           temp[temp_index - 1] = ' ';
777           while ((c = string[string_index]) && (c == ' ' ||
778                                                 c == '\t' ||
779                                                 c == '\n'))
780             string_index++;
781         }
782     }
783   temp[temp_index] = 0;
784   strcpy (string, temp);
785   free (temp);
786 }
787
788 /* Discard text until the desired string is found.  The string is
789    included in the discarded text. */
790 void
791 discard_until (string)
792      char *string;
793 {
794   int temp = search_forward (string, input_text_offset);
795
796   int tt = (temp < 0) ? input_text_length : temp + strlen (string);
797   int from = input_text_offset;
798
799   /* Find out what line we are on. */
800   while (from != tt)
801     if (input_text[from++] == '\n')
802       line_number++;
803
804   if (temp < 0)
805     {
806       input_text_offset = input_text_length - strlen (string);
807
808       if (strcmp (string, "\n") != 0)
809         {
810           line_error (_("Expected `%s'"), string);
811           return;
812         }
813     }
814   else
815     input_text_offset = temp;
816
817   input_text_offset += strlen (string);
818 }
819
820 /* Read characters from the file until we are at MATCH.
821    Place the characters read into STRING.
822    On exit input_text_offset is after the match string.
823    Return the offset where the string starts. */
824 int
825 get_until (match, string)
826      char *match, **string;
827 {
828   int len, current_point, x, new_point, tem;
829
830   current_point = x = input_text_offset;
831   new_point = search_forward (match, input_text_offset);
832
833   if (new_point < 0)
834     new_point = input_text_length;
835   len = new_point - current_point;
836
837   /* Keep track of which line number we are at. */
838   tem = new_point + (strlen (match) - 1);
839   while (x != tem)
840     if (input_text[x++] == '\n')
841       line_number++;
842
843   *string = xmalloc (len + 1);
844
845   memcpy (*string, &input_text[current_point], len);
846   (*string)[len] = 0;
847
848   /* Now leave input_text_offset in a consistent state. */
849   input_text_offset = tem;
850
851   if (input_text_offset > input_text_length)
852     input_text_offset = input_text_length;
853
854   return new_point;
855 }
856
857 /* Replace input_text[FROM .. TO] with its expansion.  */
858 void
859 replace_with_expansion (from, to)
860      int from, *to;
861 {
862   char *xp;
863   unsigned xp_len, new_len;
864   char *old_input = input_text;
865   unsigned raw_len = *to - from;
866   char *str;
867
868   /* The rest of the code here moves large buffers, so let's
869      not waste time if the input cannot possibly expand
870      into anything.  Unfortunately, we cannot avoid expansion
871      when we see things like @code etc., even if they only
872      asked for expansion of macros, since any Texinfo command
873      can be potentially redefined with a macro.  */
874   if (only_macro_expansion &&
875       memchr (input_text + from, COMMAND_PREFIX, raw_len) == 0)
876     return;
877
878   /* Get original string from input.  */
879   str = xmalloc (raw_len + 1);
880   memcpy (str, input_text + from, raw_len);
881   str[raw_len] = 0;
882
883   /* We are going to relocate input_text, so we had better output
884      pending portion of input_text now, before the pointer changes.  */
885   if (macro_expansion_output_stream && !executing_string
886       && !me_inhibit_expansion)
887     append_to_expansion_output (from);
888
889   /* Expand it.  */
890   xp = expansion (str, 0);
891   xp_len = strlen (xp);
892   free (str);
893
894   /* Plunk the expansion into the middle of `input_text' --
895      which is terminated by a newline, not a null.  Avoid
896      expensive move of the rest of the input if the expansion
897      has the same length as the original string.  */
898   if (xp_len != raw_len)
899     {
900       new_len = from + xp_len + input_text_length - *to + 1;
901       if (executing_string)
902         { /* If we are in execute_string, we might need to update
903              the relevant element in the execution_strings[] array,
904              since it could have to be relocated from under our
905              feet.  (input_text is reallocated here as well, if needed.)  */
906           maybe_update_execution_strings (&input_text, new_len);
907         }
908       else if (new_len > input_text_length + 1)
909         /* Don't bother to realloc if we have enough space.  */
910         input_text = xrealloc (input_text, new_len);
911
912       memmove (input_text + from + xp_len,
913                input_text + *to, input_text_length - *to + 1);
914
915       *to += xp_len - raw_len;
916       /* Since we change input_text_length here, the comparison above
917          isn't really valid, but it seems the worst that might happen is
918          an extra xrealloc or two, so let's not worry.  */
919       input_text_length += xp_len - raw_len;
920     }
921   memcpy (input_text + from, xp, xp_len);
922   free (xp);
923
924   /* Synchronize the macro-expansion pointers with our new input_text.  */
925   if (input_text != old_input)
926     forget_itext (old_input);
927   if (macro_expansion_output_stream && !executing_string)
928     remember_itext (input_text, from);
929 }
930
931 /* Read characters from the file until we are at MATCH or end of line.
932    Place the characters read into STRING.  If EXPAND is nonzero,
933    expand the text before looking for MATCH for those cases where
934    MATCH might be produced by some macro.  */
935 void
936 get_until_in_line (expand, match, string)
937      int expand;
938      char *match, **string;
939 {
940   int real_bottom = input_text_length;
941   int limit = search_forward ("\n", input_text_offset);
942   if (limit < 0)
943     limit = input_text_length;
944
945   /* Replace input_text[input_text_offset .. limit-1] with its expansion.
946      This allows the node names and menu entries themselves to be
947      constructed via a macro, as in:
948         @macro foo{p, q}
949         Together: \p\ & \q\.
950         @end macro
951
952         @node @foo{A,B}, next, prev, top
953
954      Otherwise, the `,' separating the macro args A and B is taken as
955      the node argument separator, so the node name is `@foo{A'.  This
956      expansion is only necessary on the first call, since we expand the
957      whole line then.  */
958   if (expand)
959     {
960       replace_with_expansion (input_text_offset, &limit);
961     }
962
963   real_bottom = input_text_length;
964   input_text_length = limit;
965   get_until (match, string);
966   input_text_length = real_bottom;
967 }
968
969 void
970 get_rest_of_line (expand, string)
971      int expand;
972      char **string;
973 {
974   if (expand)
975     {
976       char *tem;
977
978       /* Don't expand non-macros in input, since we want them
979          intact in the macro-expanded output.  */
980       only_macro_expansion++;
981       get_until_in_line (1, "\n", &tem);
982       only_macro_expansion--;
983       *string = expansion (tem, 0);
984       free (tem);
985     }
986   else
987     get_until_in_line (0, "\n", string);
988
989   canon_white (*string);
990
991   if (curchar () == '\n')       /* as opposed to the end of the file... */
992     {
993       line_number++;
994       input_text_offset++;
995     }
996 }
997
998 /* Backup the input pointer to the previous character, keeping track
999    of the current line number. */
1000 void
1001 backup_input_pointer ()
1002 {
1003   if (input_text_offset)
1004     {
1005       input_text_offset--;
1006       if (curchar () == '\n')
1007         line_number--;
1008     }
1009 }
1010
1011 /* Read characters from the file until we are at MATCH or closing brace.
1012    Place the characters read into STRING.  */
1013 void
1014 get_until_in_braces (match, string)
1015      char *match, **string;
1016 {
1017   char *temp;
1018   int i, brace = 0;
1019   int match_len = strlen (match);
1020
1021   for (i = input_text_offset; i < input_text_length; i++)
1022     {
1023       if (input_text[i] == '{')
1024         brace++;
1025       else if (input_text[i] == '}')
1026         brace--;
1027       else if (input_text[i] == '\n')
1028         line_number++;
1029
1030       if (brace < 0 ||
1031           (brace == 0 && strncmp (input_text + i, match, match_len) == 0))
1032         break;
1033     }
1034
1035   match_len = i - input_text_offset;
1036   temp = xmalloc (2 + match_len);
1037   memcpy (temp, input_text + input_text_offset, match_len);
1038   temp[match_len] = 0;
1039   input_text_offset = i;
1040   *string = temp;
1041 }
1042 \f
1043 /* Converting a file.  */
1044
1045 /* Convert the file named by NAME.  The output is saved on the file
1046    named as the argument to the @setfilename command. */
1047 static char *suffixes[] = {
1048   /* ".txi" is checked first so that on 8+3 DOS filesystems, if they
1049      have "texinfo.txi" and "texinfo.tex" in the same directory, the
1050      former is used rather than the latter, due to file name truncation.  */
1051   ".txi",
1052   ".texinfo",
1053   ".texi",
1054   ".txinfo",
1055   "",
1056   NULL
1057 };
1058
1059 void
1060 initialize_conversion ()
1061 {
1062   init_tag_table ();
1063   init_indices ();
1064   init_internals ();
1065   init_paragraph ();
1066
1067   /* This is used for splitting the output file and for doing section
1068      headings.  It was previously initialized in `init_paragraph', but its
1069      use there loses with the `init_paragraph' calls done by the
1070      multitable code; the tag indices get reset to zero.  */
1071   output_position = 0;
1072 }
1073
1074 typedef struct generic_list {
1075   struct generic_list *next;
1076 } GENERIC_LIST;
1077
1078 /* Reverse the chain of structures in LIST.  Output the new head
1079    of the chain.  You should always assign the output value of this
1080    function to something, or you will lose the chain. */
1081 GENERIC_LIST *
1082 reverse_list (list)
1083      GENERIC_LIST *list;
1084 {
1085   GENERIC_LIST *next;
1086   GENERIC_LIST *prev = NULL;
1087
1088   while (list)
1089     {
1090       next = list->next;
1091       list->next = prev;
1092       prev = list;
1093       list = next;
1094     }
1095   return prev;
1096 }
1097
1098 /* We read in multiples of 4k, simply because it is a typical pipe size
1099    on unix systems. */
1100 #define READ_BUFFER_GROWTH (4 * 4096)
1101
1102 /* Convert the Texinfo file coming from the open stream STREAM.  Assume the
1103    source of the stream is named NAME. */
1104 void
1105 convert_from_stream (stream, name)
1106      FILE *stream;
1107      char *name;
1108 {
1109   char *buffer = NULL;
1110   int buffer_offset = 0, buffer_size = 0;
1111
1112   initialize_conversion ();
1113
1114   /* Read until the end of the stream.  This isn't strictly correct, since
1115      the texinfo input may end before the stream ends, but it is a quick
1116      working hueristic. */
1117   while (!feof (stream))
1118     {
1119       int count;
1120
1121       if (buffer_offset + (READ_BUFFER_GROWTH + 1) >= buffer_size)
1122         buffer = (char *)
1123           xrealloc (buffer, (buffer_size += READ_BUFFER_GROWTH));
1124
1125       count = fread (buffer + buffer_offset, 1, READ_BUFFER_GROWTH, stream);
1126
1127       if (count < 0)
1128         {
1129           perror (name);
1130           xexit (1);
1131         }
1132
1133       buffer_offset += count;
1134       if (count == 0)
1135         break;
1136     }
1137
1138   /* Set the globals to the new file. */
1139   input_text = buffer;
1140   input_text_length = buffer_offset;
1141   input_filename = xstrdup (name);
1142   node_filename = xstrdup (name);
1143   input_text_offset = 0;
1144   line_number = 1;
1145
1146   /* Not strictly necessary.  This magic prevents read_token () from doing
1147      extra unnecessary work each time it is called (that is a lot of times).
1148      The INPUT_TEXT_LENGTH is one past the actual end of the text. */
1149   input_text[input_text_length] = '\n';
1150
1151   convert_from_loaded_file (name);
1152 }
1153
1154 void
1155 convert_from_file (name)
1156      char *name;
1157 {
1158   int i;
1159   char *filename = xmalloc (strlen (name) + 50);
1160
1161   initialize_conversion ();
1162
1163   /* Try to load the file specified by NAME, concatenated with our
1164      various suffixes.  Prefer files like `makeinfo.texi' to
1165      `makeinfo'.  */
1166   for (i = 0; suffixes[i]; i++)
1167     {
1168       strcpy (filename, name);
1169       strcat (filename, suffixes[i]);
1170
1171       if (find_and_load (filename))
1172         break;
1173
1174       if (!suffixes[i][0] && strrchr (filename, '.'))
1175         {
1176           fs_error (filename);
1177           free (filename);
1178           return;
1179         }
1180     }
1181
1182   if (!suffixes[i])
1183     {
1184       fs_error (name);
1185       free (filename);
1186       return;
1187     }
1188
1189   input_filename = filename;
1190
1191   convert_from_loaded_file (name);
1192 }
1193
1194 void
1195 convert_from_loaded_file (name)
1196      char *name;
1197 {
1198   char *real_output_filename = NULL;
1199
1200   remember_itext (input_text, 0);
1201
1202   input_text_offset = 0;
1203
1204   /* Avoid the `\input texinfo' line in HTML output (assuming it starts
1205      the file).  */
1206   if (looking_at ("\\input"))
1207     discard_until ("\n");
1208
1209   /* Search this file looking for the special string which starts conversion.
1210      Once found, we may truly begin. */
1211   while (input_text_offset >= 0)
1212     {
1213       input_text_offset =
1214         search_forward (setfilename_search, input_text_offset);
1215
1216       if (input_text_offset == 0
1217           || (input_text_offset > 0
1218               && input_text[input_text_offset -1] == '\n'))
1219         break;
1220       else if (input_text_offset > 0)
1221         input_text_offset++;
1222     }
1223
1224   if (input_text_offset < 0)
1225     {
1226       if (!command_output_filename)
1227         {
1228 #if defined (REQUIRE_SETFILENAME)
1229           error (_("No `%s' found in `%s'"), setfilename_search, name);
1230           goto finished;
1231 #else
1232           command_output_filename = output_name_from_input_name (name);
1233 #endif /* !REQUIRE_SETFILENAME */
1234         }
1235  
1236       {
1237         int i, end_of_first_line;
1238
1239         /* Find the end of the first line in the file. */
1240         for (i = 0; i < input_text_length - 1; i++)
1241           if (input_text[i] == '\n')
1242             break;
1243
1244         end_of_first_line = i + 1;
1245
1246         for (i = 0; i < end_of_first_line; i++)
1247           {
1248             if ((input_text[i] == '\\') &&
1249                 (strncmp (input_text + i + 1, "input", 5) == 0))
1250               {
1251                 input_text_offset = i;
1252                 break;
1253               }
1254           }
1255       }
1256     }
1257   else
1258     input_text_offset += strlen (setfilename_search);
1259
1260   if (!command_output_filename)
1261     {
1262       get_until ("\n", &output_filename); /* read rest of line */
1263       if (html)
1264         { /* Change any extension to .html.  */
1265           char *html_name, *directory_part, *basename_part, *temp;
1266
1267           canon_white (output_filename);
1268           directory_part = pathname_part (output_filename);
1269           basename_part = filename_part (output_filename);
1270
1271           /* Zap any existing extension.  */
1272           temp = strrchr (basename_part, '.');
1273           if (temp)
1274             *temp = 0;
1275
1276           /* Construct new filename.  */
1277           html_name = xmalloc (strlen (directory_part)
1278                                + strlen (basename_part) + 6);
1279           strcpy (html_name, directory_part);
1280           strcat (html_name, basename_part);
1281           strcat (html_name, ".html");
1282
1283           /* Replace name from @setfilename with the html name.  */
1284           free (output_filename);
1285           output_filename = html_name;
1286         }
1287     }
1288   else
1289     {
1290       if (input_text_offset != -1)
1291         discard_until ("\n");
1292       else
1293         input_text_offset = 0;
1294
1295       real_output_filename = output_filename = command_output_filename;
1296       command_output_filename = NULL;
1297     }
1298
1299   canon_white (output_filename);
1300
1301   if (real_output_filename && strcmp (real_output_filename, "-") == 0)
1302     {
1303       if (macro_expansion_filename
1304           && strcmp (macro_expansion_filename, "-") == 0)
1305         {
1306           fprintf (stderr, _("%s: Skipping macro expansion to stdout as Info output is going there.\n"),
1307                    progname);
1308           macro_expansion_output_stream = NULL;
1309         }
1310       real_output_filename = xstrdup (real_output_filename);
1311       output_stream = stdout;
1312       splitting = 0;            /* Cannot split when writing to stdout. */
1313     }
1314   else
1315     {
1316       if (!real_output_filename)
1317         real_output_filename = expand_filename (output_filename, name);
1318       else
1319         real_output_filename = xstrdup (real_output_filename);
1320
1321       output_stream = fopen (real_output_filename, "w");
1322     }
1323
1324   set_current_output_filename (real_output_filename);
1325
1326   if (verbose_mode)
1327     printf (_("Making %s file `%s' from `%s'.\n"),
1328             no_headers ? "text" : (html ? "HTML" : "info"),
1329             output_filename, input_filename);
1330
1331   if (output_stream == NULL)
1332     {
1333       fs_error (real_output_filename);
1334       goto finished;
1335     }
1336
1337   /* Make the displayable filename from output_filename.  Only the base
1338      portion of the filename need be displayed. */
1339   if (output_stream != stdout)
1340     pretty_output_filename = filename_part (output_filename);
1341   else
1342     pretty_output_filename = xstrdup ("stdout");
1343
1344   /* For this file only, count the number of newlines from the top of
1345      the file to here.  This way, we keep track of line numbers for
1346      error reporting.  Line_number starts at 1, since the user isn't
1347      zero-based. */
1348   {
1349     int temp = 0;
1350     line_number = 1;
1351     while (temp != input_text_offset)
1352       if (input_text[temp++] == '\n')
1353         line_number++;
1354   }
1355
1356   /* html fixxme: should output this as trailer on first page.  */
1357   if (!no_headers && !html)
1358     add_word_args (_("This is %s, produced by makeinfo version %s from %s.\n"),
1359                    output_filename, VERSION, input_filename);
1360
1361   close_paragraph ();
1362   reader_loop ();
1363
1364 finished:
1365   discard_insertions (0);
1366   close_paragraph ();
1367   flush_file_stack ();
1368
1369   if (macro_expansion_output_stream)
1370     {
1371       fclose (macro_expansion_output_stream);
1372       if (errors_printed && !force
1373           && strcmp (macro_expansion_filename, "-") != 0
1374           && FILENAME_CMP (macro_expansion_filename, NULL_DEVICE) != 0
1375           && FILENAME_CMP (macro_expansion_filename, ALSO_NULL_DEVICE) != 0)
1376         {
1377           fprintf (stderr, _("%s: Removing macro output file `%s' due to errors; use --force to preserve.\n"),
1378                    progname, macro_expansion_filename);
1379           if (unlink (macro_expansion_filename) < 0)
1380             perror (macro_expansion_filename);
1381         }
1382     }
1383
1384   if (output_stream)
1385     {
1386       output_pending_notes ();
1387       if (tag_table)
1388         {
1389           tag_table = (TAG_ENTRY *) reverse_list (tag_table);
1390           if (!no_headers && !html)
1391             write_tag_table ();
1392         }
1393
1394       if (html)
1395         {
1396           start_paragraph ();
1397           add_word ("</body></html>\n");
1398           close_paragraph ();
1399         }
1400
1401       if (output_stream != stdout)
1402         fclose (output_stream);
1403
1404       /* If validating, then validate the entire file right now. */
1405       if (validating)
1406         validate_file (tag_table);
1407
1408       /* If we need to output the table of contents, do it now.  */
1409       if (contents_filename || shortcontents_filename)
1410         toc_update ();
1411
1412       if (splitting && !html && (!errors_printed || force))
1413         split_file (real_output_filename, 0);
1414       else if (errors_printed
1415                && !force
1416                && strcmp (real_output_filename, "-") != 0
1417                && FILENAME_CMP (real_output_filename, NULL_DEVICE) != 0
1418                && FILENAME_CMP (real_output_filename, ALSO_NULL_DEVICE) != 0)
1419         { /* If there were errors, and no --force, remove the output.  */
1420           fprintf (stderr, _("%s: Removing output file `%s' due to errors; use --force to preserve.\n"),
1421                    progname, real_output_filename);
1422           if (unlink (real_output_filename) < 0)
1423             perror (real_output_filename);
1424         }
1425     }
1426   free (real_output_filename);
1427 }
1428
1429 void
1430 free_and_clear (pointer)
1431      char **pointer;
1432 {
1433   if (*pointer)
1434     {
1435       free (*pointer);
1436       *pointer = NULL;
1437     }
1438 }
1439
1440  /* Initialize some state. */
1441 void
1442 init_internals ()
1443 {
1444   free_and_clear (&output_filename);
1445   free_and_clear (&command);
1446   free_and_clear (&input_filename);
1447   free_node_references ();
1448   free_node_node_references ();
1449   toc_free ();
1450   init_insertion_stack ();
1451   init_brace_stack ();
1452   current_node = NULL; /* sometimes already freed */
1453   command_index = 0;
1454   in_menu = 0;
1455   in_detailmenu = 0;
1456   top_node_seen = 0;
1457   non_top_node_seen = 0;
1458   node_number = -1;
1459 }
1460
1461 void
1462 init_paragraph ()
1463 {
1464   free_and_clear (&output_paragraph);
1465   output_paragraph = xmalloc (paragraph_buffer_len);
1466   output_paragraph[0] = 0;
1467   output_paragraph_offset = 0;
1468   output_column = 0;
1469   paragraph_is_open = 0;
1470   current_indent = 0;
1471   meta_char_pos = 0;
1472 }
1473 \f
1474 /* This is called from `reader_loop' when we are at the * beginning a
1475    menu line.  */
1476
1477 static void
1478 handle_menu_entry ()
1479 {
1480   char *tem;
1481   
1482   /* Ugh, glean_node_from_menu wants to read the * itself.  */
1483   input_text_offset--;
1484   
1485   /* Find node name in menu entry and save it in references list for
1486      later validation.  Use followed_reference type for detailmenu
1487      references since we don't want to use them for default node pointers.  */
1488   tem = glean_node_from_menu (1, in_detailmenu
1489                                  ? followed_reference : menu_reference);
1490
1491   if (html && tem)
1492     { /* Start a menu item with the cleaned-up line.  Put an anchor
1493          around the start text (before `:' or the node name). */
1494       char *string;
1495
1496       discard_until ("* ");
1497
1498       /* The line number was already incremented in reader_loop when we
1499          saw the newline, and discard_until has now incremented again.  */
1500       line_number--;
1501
1502       if (had_menu_commentary)
1503         {
1504           add_word ("<ul>\n");
1505           had_menu_commentary = 0;
1506           in_paragraph = 0;
1507         }
1508       else if (!in_paragraph && !paragraph_is_open)
1509         {
1510           add_word ("<p>\n");
1511           in_paragraph = 1;
1512         }
1513       
1514       if (in_paragraph)
1515         {
1516           add_word ("</p>");
1517           in_paragraph = 0;
1518         }
1519
1520       add_word ("<li><a href=\"");
1521       string = expansion (tem, 0);
1522       add_anchor_name (string, 1);
1523       add_word ("\">");
1524       free (string);
1525
1526       /* The menu item may use macros, so expand them now.  */
1527       only_macro_expansion++;
1528       get_until_in_line (1, ":", &string);
1529       only_macro_expansion--;
1530       execute_string ("%s", string); /* get escaping done */
1531       free (string);
1532
1533       add_word ("</a>");
1534
1535       if (looking_at ("::"))
1536         discard_until (":");
1537       else
1538         { /* discard the node name */
1539           get_until_in_line (0, ".", &string);
1540           free (string);
1541         }
1542       input_text_offset++;      /* discard the second colon or the period */
1543       add_word (": ");
1544     }
1545   else if (tem)
1546     { /* For Info output, we can just use the input and the main case in
1547          reader_loop where we output what comes in.  Just move off the *
1548          so the next time through reader_loop we don't end up back here.  */
1549       add_char ('*');
1550       input_text_offset += 2; /* undo the pointer back-up above.  */
1551     }
1552
1553   if (tem)
1554     free (tem);
1555 }
1556 \f
1557 /* Find the command corresponding to STRING.  If the command is found,
1558    return a pointer to the data structure.  Otherwise return -1.  */
1559 static COMMAND *
1560 get_command_entry (string)
1561      char *string;
1562 {
1563   int i;
1564
1565   for (i = 0; command_table[i].name; i++)
1566     if (strcmp (command_table[i].name, string) == 0)
1567       return &command_table[i];
1568
1569   /* This command is not in our predefined command table.  Perhaps
1570      it is a user defined command. */
1571   for (i = 0; i < user_command_array_len; i++)
1572     if (user_command_array[i] &&
1573         (strcmp (user_command_array[i]->name, string) == 0))
1574       return user_command_array[i];
1575
1576   /* We never heard of this command. */
1577   return (COMMAND *) -1;
1578 }
1579 \f
1580 /* input_text_offset is right at the command prefix character.
1581    Read the next token to determine what to do.  Return zero
1582    if there's no known command or macro after the prefix character.  */
1583 static int
1584 read_command ()
1585 {
1586   COMMAND *entry;
1587   int old_text_offset = input_text_offset++;
1588
1589   free_and_clear (&command);
1590   command = read_token ();
1591
1592   /* Check to see if this command is a macro.  If so, execute it here. */
1593   {
1594     MACRO_DEF *def;
1595
1596     def = find_macro (command);
1597
1598     if (def)
1599       {
1600         /* We disallow recursive use of a macro call.  Inhibit the expansion
1601            of this macro during the life of its execution. */
1602         if (!(def->flags & ME_RECURSE))
1603           def->inhibited = 1;
1604
1605         execute_macro (def);
1606
1607         if (!(def->flags & ME_RECURSE))
1608           def->inhibited = 0;
1609
1610         return 1;
1611       }
1612     }
1613
1614   if (only_macro_expansion)
1615     {
1616       /* Back up to the place where we were called, so the
1617          caller will have a chance to process this non-macro.  */
1618       input_text_offset = old_text_offset;
1619       return 0;
1620     }
1621
1622   /* Perform alias expansion */
1623   command = alias_expand (command);
1624
1625   if (enclosure_command (command))
1626     {
1627       remember_brace (enclosure_expand);
1628       enclosure_expand (START, output_paragraph_offset, 0);
1629       return 0;
1630     }
1631
1632   entry = get_command_entry (command);
1633   if (entry == (COMMAND *)-1)
1634     {
1635       line_error (_("Unknown command `%s'"), command);
1636       return 0;
1637     }
1638
1639   if (entry->argument_in_braces == BRACE_ARGS)
1640     remember_brace (entry->proc);
1641   else if (entry->argument_in_braces == MAYBE_BRACE_ARGS)
1642     {
1643       if (curchar () == '{')
1644         remember_brace (entry->proc);
1645       else
1646         { /* No braces, so arg is next char.  */
1647           int ch;
1648           int saved_offset = output_paragraph_offset;
1649           (*(entry->proc)) (START, output_paragraph_offset, 0);
1650
1651           /* Possibilities left for the next character: @ (error), }
1652              (error), whitespace (skip) anything else (normal char).  */
1653           skip_whitespace ();
1654           ch = curchar ();
1655           if (ch == '@')
1656             {
1657            line_error (_("Use braces to give a command as an argument to @%s"),
1658                entry->name);
1659               return 0;
1660             }
1661           else if (ch == '}')
1662             {
1663               /* Our caller will give the error message, because this }
1664                  won't match anything.  */
1665               return 0;
1666             }
1667
1668           add_char (ch);
1669           input_text_offset++;
1670           (*(entry->proc)) (END, saved_offset, output_paragraph_offset);
1671           return 1;
1672         }
1673     }
1674
1675   /* Get here if we have BRACE_ARGS, NO_BRACE_ARGS, or MAYBE_BRACE_ARGS
1676      with braces.  */
1677   (*(entry->proc)) (START, output_paragraph_offset, 0);
1678   return 1;
1679 }
1680
1681 /* Okay, we are ready to start the conversion.  Call the reader on
1682    some text, and fill the text as it is output.  Handle commands by
1683    remembering things like open braces and the current file position on a
1684    stack, and when the corresponding close brace is found, you can call
1685    the function with the proper arguments.  Although the filling isn't
1686    necessary for HTML, it should do no harm.  */
1687 void
1688 reader_loop ()
1689 {
1690   int character;
1691   int done = 0;
1692   int dash_count = 0;
1693
1694   while (!done)
1695     {
1696       if (input_text_offset >= input_text_length)
1697         break;
1698
1699       character = curchar ();
1700
1701       /* If only_macro_expansion, only handle macros and leave
1702          everything else intact.  */
1703       if (!only_macro_expansion && !in_fixed_width_font
1704           && (character == '\'' || character == '`')
1705           && input_text[input_text_offset + 1] == character)
1706         {
1707           input_text_offset++;
1708           character = '"'; /* html fixxme */
1709         }
1710
1711       /* Convert --- to --.  */
1712       if (!only_macro_expansion && character == '-')
1713         {
1714           dash_count++;
1715           if (dash_count == 2 && !in_fixed_width_font)
1716             {
1717               input_text_offset++;
1718               continue;
1719             }
1720         }
1721       else if (dash_count > 0)
1722         dash_count = 0;
1723
1724       /* If this is a whitespace character, then check to see if the line
1725          is blank.  If so, advance to the carriage return. */
1726       if (!only_macro_expansion && whitespace (character))
1727         {
1728           int i = input_text_offset + 1;
1729
1730           while (i < input_text_length && whitespace (input_text[i]))
1731             i++;
1732
1733           if (i == input_text_length || input_text[i] == '\n')
1734             {
1735               if (i == input_text_length)
1736                 i--;
1737
1738               input_text_offset = i;
1739               character = curchar ();
1740             }
1741         }
1742
1743       if (character == '\n')
1744         line_number++;
1745
1746       switch (character)
1747         {
1748         case '*': /* perhaps we are at a menu */
1749           /* We used to check for this in the \n case but an @c in a
1750              menu swallows its newline, so check here instead.  */
1751           if (!only_macro_expansion && in_menu
1752               && input_text_offset + 1 < input_text_length
1753               && input_text[input_text_offset-1] == '\n')
1754             handle_menu_entry ();
1755           else
1756             { /* Duplicate code from below, but not worth twisting the
1757                  fallthroughs to get down there.  */
1758               add_char (character);
1759               input_text_offset++;
1760             }
1761           break;
1762         
1763         /* Escapes for HTML unless we're outputting raw HTML.  Do
1764            this always, even if SGML rules don't require it since
1765            that's easier and safer for non-conforming browsers. */
1766         case '&':
1767           if (html && escape_html)
1768             add_word ("&amp;");
1769           else
1770             add_char (character);
1771           input_text_offset++;
1772           break;
1773
1774         case '<':
1775           if (html && escape_html)
1776             add_word ("&lt;");
1777           else
1778             add_char (character);
1779           input_text_offset++;
1780           break;
1781
1782         case '>':
1783           if (html && escape_html)
1784             add_word ("&gt;");
1785           else
1786             add_char (character);
1787           input_text_offset++;
1788           break;
1789
1790         case COMMAND_PREFIX: /* @ */
1791           if (read_command () || !only_macro_expansion)
1792             break;
1793
1794         /* FALLTHROUGH (usually) */
1795         case '{':
1796           /* Special case.  We're not supposed to see this character by itself.
1797              If we do, it means there is a syntax error in the input text.
1798              Report the error here, but remember this brace on the stack so
1799              we can ignore its partner. */
1800           if (!only_macro_expansion)
1801             {
1802               line_error (_("Misplaced %c"), '{');
1803               remember_brace (misplaced_brace);
1804               /* remember_brace advances input_text_offset.  */
1805               break;
1806             }
1807
1808         /* FALLTHROUGH (usually) */
1809         case '}':
1810           if (!only_macro_expansion)
1811             {
1812               pop_and_call_brace ();
1813               input_text_offset++;
1814               break;
1815             }
1816
1817         /* FALLTHROUGH (usually) */
1818         default:
1819           add_char (character);
1820           input_text_offset++;
1821         }
1822     }
1823   if (macro_expansion_output_stream && !only_macro_expansion)
1824     maybe_write_itext (input_text, input_text_offset);
1825 }
1826 \f
1827 void
1828 init_brace_stack ()
1829 {
1830   brace_stack = NULL;
1831 }
1832
1833 void
1834 remember_brace (proc)
1835      COMMAND_FUNCTION *proc;
1836 {
1837   if (curchar () != '{')
1838     line_error (_("%c%s expected `{...}'"), COMMAND_PREFIX, command);
1839   else
1840     input_text_offset++;
1841   remember_brace_1 (proc, output_paragraph_offset);
1842 }
1843
1844 /* Remember the current output position here.  Save PROC
1845    along with it so you can call it later. */
1846 void
1847 remember_brace_1 (proc, position)
1848      COMMAND_FUNCTION *proc;
1849      int position;
1850 {
1851   BRACE_ELEMENT *new = xmalloc (sizeof (BRACE_ELEMENT));
1852   new->next = brace_stack;
1853   new->proc = proc;
1854   new->command = xstrdup (command);
1855   new->pos = position;
1856   new->line = line_number;
1857   new->in_fixed_width_font = in_fixed_width_font;
1858   brace_stack = new;
1859 }
1860
1861 /* Pop the top of the brace stack, and call the associated function
1862    with the args END and POS. */
1863 void
1864 pop_and_call_brace ()
1865 {
1866   if (brace_stack == NULL)
1867     {
1868       line_error (_("Unmatched }"));
1869       return;
1870     }
1871
1872   {
1873     BRACE_ELEMENT *temp;
1874
1875     int pos = brace_stack->pos;
1876     COMMAND_FUNCTION *proc = brace_stack->proc;
1877     in_fixed_width_font = brace_stack->in_fixed_width_font;
1878
1879     /* Reset current command, so the proc can know who it is.  This is
1880        used in cm_accent.  */
1881     command = brace_stack->command;
1882
1883     temp = brace_stack->next;
1884     free (brace_stack);
1885     brace_stack = temp;
1886
1887     (*proc) (END, pos, output_paragraph_offset);
1888   }
1889 }
1890
1891 /* Shift all of the markers in `brace_stack' by AMOUNT. */
1892 void
1893 adjust_braces_following (here, amount)
1894      int here, amount;
1895 {
1896   BRACE_ELEMENT *stack = brace_stack;
1897
1898   while (stack)
1899     {
1900       if (stack->pos >= here)
1901         stack->pos += amount;
1902       stack = stack->next;
1903     }
1904 }
1905
1906 /* Return the string which invokes PROC; a pointer to a function.
1907    Always returns the first function in the command table if more than
1908    one matches PROC.  */
1909 static char *
1910 find_proc_name (proc)
1911      COMMAND_FUNCTION *proc;
1912 {
1913   int i;
1914
1915   for (i = 0; command_table[i].name; i++)
1916     if (proc == command_table[i].proc)
1917       return command_table[i].name;
1918   return _("NO_NAME!");
1919 }
1920
1921 /* You call discard_braces () when you shouldn't have any braces on the stack.
1922    I used to think that this happens for commands that don't take arguments
1923    in braces, but that was wrong because of things like @code{foo @@}.  So now
1924    I only detect it at the beginning of nodes. */
1925 void
1926 discard_braces ()
1927 {
1928   if (!brace_stack)
1929     return;
1930
1931   while (brace_stack)
1932     {
1933       if (brace_stack->proc != misplaced_brace)
1934         {
1935           char *proc_name;
1936           int temp_line_number = line_number;
1937
1938           line_number = brace_stack->line;
1939           proc_name = find_proc_name (brace_stack->proc);
1940           line_error (_("%c%s missing close brace"), COMMAND_PREFIX, proc_name);
1941           line_number = temp_line_number;
1942           pop_and_call_brace ();
1943         }
1944       else
1945         {
1946           BRACE_ELEMENT *temp;
1947           temp = brace_stack->next;
1948           free (brace_stack);
1949           brace_stack = temp;
1950         }
1951     }
1952 }
1953
1954 int
1955 get_char_len (character)
1956      int character;
1957 {
1958   /* Return the printed length of the character. */
1959   int len;
1960
1961   switch (character)
1962     {
1963     case '\t':
1964       len = (output_column + 8) & 0xf7;
1965       if (len > fill_column)
1966         len = fill_column - output_column;
1967       else
1968         len = len - output_column;
1969       break;
1970
1971     case '\n':
1972       len = fill_column - output_column;
1973       break;
1974
1975     default:
1976       /* ASCII control characters appear as two characters in the output
1977          (e.g., ^A).  But characters with the high bit set are just one
1978          on suitable terminals, so don't count them as two for line
1979          breaking purposes.  */
1980       if (0 <= character && character < ' ')
1981         len = 2;
1982       else
1983         len = 1;
1984     }
1985   return len;
1986 }
1987 \f
1988 void
1989 #if defined (VA_FPRINTF) && __STDC__
1990 add_word_args (char *format, ...)
1991 #else
1992 add_word_args (format, va_alist)
1993     char *format;
1994     va_dcl
1995 #endif
1996 {
1997   char buffer[2000]; /* xx no fixed limits */
1998 #ifdef VA_FPRINTF
1999   va_list ap;
2000 #endif
2001
2002   VA_START (ap, format);
2003 #ifdef VA_SPRINTF
2004   VA_SPRINTF (buffer, format, ap);
2005 #else
2006   sprintf (buffer, format, a1, a2, a3, a4, a5, a6, a7, a8);
2007 #endif /* not VA_SPRINTF */
2008   va_end (ap);
2009   add_word (buffer);
2010 }
2011
2012 /* Add STRING to output_paragraph. */
2013 void
2014 add_word (string)
2015      char *string;
2016 {
2017   while (*string)
2018     add_char (*string++);
2019 }
2020
2021 /* Add the character to the current paragraph.  If filling_enabled is
2022    nonzero, then do filling as well. */
2023 void
2024 add_char (character)
2025      int character;
2026 {
2027   /* If we are avoiding outputting headers, and we are currently
2028      in a menu, then simply return.  But if we're only expanding macros,
2029      then we're being called from glean_node_from_menu to try to
2030      remember a menu reference, and we need that so we can do defaulting.  */
2031   if (no_headers && !only_macro_expansion && (in_menu || in_detailmenu))
2032     return;
2033
2034   /* If we are adding a character now, then we don't have to
2035      ignore close_paragraph () calls any more. */
2036   if (must_start_paragraph && character != '\n')
2037     {
2038       must_start_paragraph = 0;
2039       line_already_broken = 0;  /* The line is no longer broken. */
2040       if (current_indent > output_column)
2041         {
2042           indent (current_indent - output_column);
2043           output_column = current_indent;
2044         }
2045     }
2046
2047   if (non_splitting_words && strchr (" \t\n", character))
2048     {
2049       if (html)
2050         { /* Seems cleaner to use &nbsp; than an 8-bit char.  */
2051           add_word ("&nbsp");
2052           character = ';';
2053         }
2054       else
2055         character = META (' '); /* unmeta-d in flush_output */
2056     }
2057
2058   insertion_paragraph_closed = 0;
2059
2060   switch (character)
2061     {
2062     case '\n':
2063       if (!filling_enabled && ! (html && (in_menu || in_detailmenu)))
2064         {
2065           insert ('\n');
2066
2067           if (force_flush_right)
2068             {
2069               close_paragraph ();
2070               /* Hack to force single blank lines out in this mode. */
2071               flush_output ();
2072             }
2073
2074           output_column = 0;
2075
2076           if (!no_indent && paragraph_is_open)
2077             indent (output_column = current_indent);
2078           break;
2079         }
2080       else if (end_of_sentence_p ())
2081         /* CHARACTER is newline, and filling is enabled. */
2082         {
2083           insert (' ');
2084           output_column++;
2085           last_inserted_character = character;
2086         }
2087
2088       if (last_char_was_newline)
2089         {
2090           if (html)
2091             last_char_was_newline++;
2092           close_paragraph ();
2093           pending_indent = 0;
2094         }
2095       else
2096         {
2097           last_char_was_newline = 1;
2098           if (html)
2099             insert ('\n');
2100           else
2101             insert (' ');
2102           output_column++;
2103         }
2104       break;
2105
2106     default: /* not at newline */
2107       {
2108         int len = get_char_len (character);
2109         int suppress_insert = 0;
2110
2111         if ((character == ' ') && (last_char_was_newline))
2112           {
2113             if (!paragraph_is_open)
2114               {
2115                 pending_indent++;
2116                 return;
2117               }
2118           }
2119
2120         if (!paragraph_is_open)
2121           {
2122             start_paragraph ();
2123             /* If the paragraph is supposed to be indented a certain
2124                way, then discard all of the pending whitespace.
2125                Otherwise, we let the whitespace stay. */
2126             if (!paragraph_start_indent)
2127               indent (pending_indent);
2128             pending_indent = 0;
2129
2130             /* This horrible kludge of checking for a < prevents <p>
2131                from being inserted when we already have html markup
2132                starting a paragraph, as with <ul> and <h1> and the like.  */
2133             if (html && escape_html && character != '<'
2134                 && (!in_fixed_width_font || in_menu || in_detailmenu))
2135               {
2136                 insert_string ("<p>");
2137                 in_paragraph = 1;
2138                 adjust_braces_following (0, 3); /* adjust for <p> */
2139               }
2140           }
2141
2142         output_column += len;
2143         if (output_column > fill_column)
2144           {
2145             if (filling_enabled && !html)
2146               {
2147                 int temp = output_paragraph_offset;
2148                 while (--temp > 0 && output_paragraph[temp] != '\n')
2149                   {
2150                     /* If we have found a space, we have the place to break
2151                        the line. */
2152                     if (output_paragraph[temp] == ' ')
2153                       {
2154                         /* Remove trailing whitespace from output. */
2155                         while (temp && whitespace (output_paragraph[temp - 1]))
2156                           temp--;
2157
2158                         /* If we went back all the way to the newline of the
2159                            preceding line, it probably means that the word we
2160                            are adding is itself wider than the space that the
2161                            indentation and the fill_column let us use.  In
2162                            that case, do NOT insert another newline, since it
2163                            won't help.  Just indent to current_indent and
2164                            leave it alone, since that's the most we can do.  */
2165                         if (temp && output_paragraph[temp - 1] != '\n')
2166                           output_paragraph[temp++] = '\n';
2167
2168                         /* We have correctly broken the line where we want
2169                            to.  What we don't want is spaces following where
2170                            we have decided to break the line.  We get rid of
2171                            them. */
2172                         {
2173                           int t1 = temp;
2174
2175                           for (;; t1++)
2176                             {
2177                               if (t1 == output_paragraph_offset)
2178                                 {
2179                                   if (whitespace (character))
2180                                     suppress_insert = 1;
2181                                   break;
2182                                 }
2183                               if (!whitespace (output_paragraph[t1]))
2184                                 break;
2185                             }
2186
2187                           if (t1 != temp)
2188                             {
2189                               adjust_braces_following (temp, (- (t1 - temp)));
2190                               strncpy ((char *) &output_paragraph[temp],
2191                                        (char *) &output_paragraph[t1],
2192                                        (output_paragraph_offset - t1));
2193                               output_paragraph_offset -= (t1 - temp);
2194                             }
2195                         }
2196
2197                         /* Filled, but now indent if that is right. */
2198                         if (indented_fill && current_indent > 0)
2199                           {
2200                             int buffer_len = ((output_paragraph_offset - temp)
2201                                               + current_indent);
2202                             char *temp_buffer = xmalloc (buffer_len);
2203                             int indentation = 0;
2204
2205                             /* We have to shift any markers that are in
2206                                front of the wrap point. */
2207                             adjust_braces_following (temp, current_indent);
2208
2209                             while (current_indent > 0 &&
2210                                    indentation != current_indent)
2211                               temp_buffer[indentation++] = ' ';
2212
2213                             memcpy ((char *) &temp_buffer[current_indent],
2214                                      (char *) &output_paragraph[temp],
2215                                      buffer_len - current_indent);
2216
2217                             if (output_paragraph_offset + buffer_len
2218                                 >= paragraph_buffer_len)
2219                               {
2220                                 unsigned char *tt = xrealloc
2221                                   (output_paragraph,
2222                                    (paragraph_buffer_len += buffer_len));
2223                                 output_paragraph = tt;
2224                               }
2225                             memcpy ((char *) &output_paragraph[temp],
2226                                      temp_buffer, buffer_len);
2227                             output_paragraph_offset += current_indent;
2228                             free (temp_buffer);
2229                           }
2230                         output_column = 0;
2231                         while (temp < output_paragraph_offset)
2232                           output_column +=
2233                             get_char_len (output_paragraph[temp++]);
2234                         output_column += len;
2235                         break;
2236                       }
2237                   }
2238               }
2239           }
2240
2241         if (!suppress_insert)
2242           {
2243             insert (character);
2244             last_inserted_character = character;
2245           }
2246         last_char_was_newline = 0;
2247         line_already_broken = 0;
2248       }
2249     }
2250 }
2251
2252 /* Add a character and store its position in meta_char_pos.  */
2253 void
2254 add_meta_char (character)
2255      int character;
2256 {
2257   meta_char_pos = output_paragraph_offset;
2258   add_char (character);
2259 }
2260
2261 /* Insert CHARACTER into `output_paragraph'. */
2262 void
2263 insert (character)
2264      int character;
2265 {
2266   /* This is sad, but it seems desirable to not force any particular
2267      order on the front matter commands.  This way, the document can do
2268      @settitle, @documentlanguage, etc, in any order and with any
2269      omissions, and we'll still output the html <head> `just in time'.  */
2270   if (!executing_string && html && !html_output_head_p)
2271     html_output_head ();
2272     
2273   output_paragraph[output_paragraph_offset++] = character;
2274   if (output_paragraph_offset == paragraph_buffer_len)
2275     {
2276       output_paragraph =
2277         xrealloc (output_paragraph, (paragraph_buffer_len += 100));
2278     }
2279 }
2280
2281 /* Insert the null-terminated string STRING into `output_paragraph'.  */
2282 void
2283 insert_string (string)
2284      char *string;
2285 {
2286   while (*string)
2287     insert (*string++);
2288 }
2289
2290
2291 /* Sentences might have these characters after the period (or whatever).  */
2292 #define POST_SENTENCE(c) ((c) == ')' || (c) == '\'' || (c) == '"' \
2293                           || (c) == ']')
2294
2295 /* Return true if at an end-of-sentence character, possibly followed by
2296    post-sentence punctuation to ignore.  */
2297 static int
2298 end_of_sentence_p ()
2299 {
2300   int loc = output_paragraph_offset - 1;
2301
2302   /* If nothing has been output, don't check output_paragraph[-1].  */
2303   if (loc < 0)
2304     return 0;
2305
2306   /* A post-sentence character that is at meta_char_pos is not really
2307      a post-sentence character; it was produced by a markup such as
2308      @samp.  We don't want the period inside @samp to be treated as a
2309      sentence ender. */
2310   while (loc > 0
2311          && loc != meta_char_pos && POST_SENTENCE (output_paragraph[loc]))
2312     loc--;
2313   return loc != meta_char_pos && sentence_ender (output_paragraph[loc]);
2314 }
2315
2316
2317 /* Remove upto COUNT characters of whitespace from the
2318    the current output line.  If COUNT is less than zero,
2319    then remove until none left. */
2320 void
2321 kill_self_indent (count)
2322      int count;
2323 {
2324   /* Handle infinite case first. */
2325   if (count < 0)
2326     {
2327       output_column = 0;
2328       while (output_paragraph_offset)
2329         {
2330           if (whitespace (output_paragraph[output_paragraph_offset - 1]))
2331             output_paragraph_offset--;
2332           else
2333             break;
2334         }
2335     }
2336   else
2337     {
2338       while (output_paragraph_offset && count--)
2339         if (whitespace (output_paragraph[output_paragraph_offset - 1]))
2340           output_paragraph_offset--;
2341         else
2342           break;
2343     }
2344 }
2345
2346 /* Nonzero means do not honor calls to flush_output (). */
2347 static int flushing_ignored = 0;
2348
2349 /* Prevent calls to flush_output () from having any effect. */
2350 void
2351 inhibit_output_flushing ()
2352 {
2353   flushing_ignored++;
2354 }
2355
2356 /* Allow calls to flush_output () to write the paragraph data. */
2357 void
2358 uninhibit_output_flushing ()
2359 {
2360   flushing_ignored--;
2361 }
2362
2363 void
2364 flush_output ()
2365 {
2366   int i;
2367
2368   if (!output_paragraph_offset || flushing_ignored)
2369     return;
2370
2371   for (i = 0; i < output_paragraph_offset; i++)
2372     {
2373       /* If we turned on the 8th bit for a space inside @w, turn it
2374          back off for output.  This might be problematic, since the
2375          0x80 character may be used in 8-bit character sets.  Sigh.
2376          In any case, don't do this for HTML, since the nbsp character
2377          is valid input and must be passed along to the browser.  */
2378       if (!html && (output_paragraph[i] & meta_character_bit))
2379         {
2380           int temp = UNMETA (output_paragraph[i]);
2381           if (temp == ' ')
2382             output_paragraph[i] &= 0x7f;
2383         }
2384     }
2385
2386   fwrite (output_paragraph, 1, output_paragraph_offset, output_stream);
2387
2388   output_position += output_paragraph_offset;
2389   output_paragraph_offset = 0;
2390   meta_char_pos = 0;
2391 }
2392
2393 /* How to close a paragraph controlling the number of lines between
2394    this one and the last one. */
2395
2396 /* Paragraph spacing is controlled by this variable.  It is the number of
2397    blank lines that you wish to appear between paragraphs.  A value of
2398    1 creates a single blank line between paragraphs. */
2399 int paragraph_spacing = DEFAULT_PARAGRAPH_SPACING;
2400
2401 static void
2402 close_paragraph_with_lines (lines)
2403      int lines;
2404 {
2405   int old_spacing = paragraph_spacing;
2406   paragraph_spacing = lines;
2407   close_paragraph ();
2408   paragraph_spacing = old_spacing;
2409 }
2410
2411 /* Close the current paragraph, leaving no blank lines between them. */
2412 void
2413 close_single_paragraph ()
2414 {
2415   close_paragraph_with_lines (0);
2416 }
2417
2418 /* Close a paragraph after an insertion has ended. */
2419 void
2420 close_insertion_paragraph ()
2421 {
2422   if (!insertion_paragraph_closed)
2423     {
2424       /* Close the current paragraph, breaking the line. */
2425       close_single_paragraph ();
2426
2427       /* Start a new paragraph, with the correct indentation for the now
2428          current insertion level (one above the one that we are ending). */
2429       start_paragraph ();
2430
2431       /* Tell `close_paragraph' that the previous line has already been
2432          broken, so it should insert one less newline. */
2433       line_already_broken = 1;
2434
2435       /* Tell functions such as `add_char' we've already found a newline. */
2436       ignore_blank_line ();
2437     }
2438   else
2439     {
2440       /* If the insertion paragraph is closed already, then we are seeing
2441          two `@end' commands in a row.  Note that the first one we saw was
2442          handled in the first part of this if-then-else clause, and at that
2443          time `start_paragraph' was called, partially to handle the proper
2444          indentation of the current line.  However, the indentation level
2445          may have just changed again, so we may have to outdent the current
2446          line to the new indentation level. */
2447       if (current_indent < output_column)
2448         kill_self_indent (output_column - current_indent);
2449     }
2450
2451   insertion_paragraph_closed = 1;
2452 }
2453
2454 /* Close the currently open paragraph. */
2455 void
2456 close_paragraph ()
2457 {
2458   int i;
2459
2460   /* The insertion paragraph is no longer closed. */
2461   insertion_paragraph_closed = 0;
2462
2463   if (paragraph_is_open && !must_start_paragraph)
2464     {
2465       int tindex, c;
2466
2467       tindex = output_paragraph_offset;
2468
2469       /* Back up to last non-newline/space character, forcing all such
2470          subsequent characters to be newlines.  This isn't strictly
2471          necessary, but a couple of functions use the presence of a newline
2472          to make decisions. */
2473       for (tindex = output_paragraph_offset - 1; tindex >= 0; --tindex)
2474         {
2475           c = output_paragraph[tindex];
2476
2477           if (c == ' '|| c == '\n')
2478             output_paragraph[tindex] = '\n';
2479           else
2480             break;
2481         }
2482
2483       /* All trailing whitespace is ignored. */
2484       output_paragraph_offset = ++tindex;
2485
2486       /* Break the line if that is appropriate. */
2487       if (paragraph_spacing >= 0)
2488         insert ('\n');
2489
2490       /* Add as many blank lines as is specified in `paragraph_spacing'. */
2491       if (!force_flush_right)
2492         {
2493           for (i = 0; i < (paragraph_spacing - line_already_broken); i++)
2494             {
2495               insert ('\n');
2496               /* Don't need anything extra for HTML in usual case of no
2497                  extra paragraph spacing.  */
2498               if (html && i > 0)
2499                 insert_string ("<br>");
2500             }
2501         }
2502
2503       /* If we are doing flush right indentation, then do it now
2504          on the paragraph (really a single line). */
2505       if (force_flush_right)
2506         do_flush_right_indentation ();
2507
2508       flush_output ();
2509       paragraph_is_open = 0;
2510       no_indent = 0;
2511       output_column = 0;
2512     }
2513
2514   ignore_blank_line ();
2515 }
2516
2517 /* Make the last line just read look as if it were only a newline. */
2518 void
2519 ignore_blank_line ()
2520 {
2521   last_inserted_character = '\n';
2522   last_char_was_newline = 1;
2523 }
2524
2525 /* Align the end of the text in output_paragraph with fill_column. */
2526 void
2527 do_flush_right_indentation ()
2528 {
2529   char *temp;
2530   int temp_len;
2531
2532   kill_self_indent (-1);
2533
2534   if (output_paragraph[0] != '\n')
2535     {
2536       output_paragraph[output_paragraph_offset] = 0;
2537
2538       if (output_paragraph_offset < fill_column)
2539         {
2540           int i;
2541
2542           if (fill_column >= paragraph_buffer_len)
2543             output_paragraph =
2544               xrealloc (output_paragraph,
2545                         (paragraph_buffer_len += fill_column));
2546
2547           temp_len = strlen ((char *)output_paragraph);
2548           temp = xmalloc (temp_len + 1);
2549           memcpy (temp, (char *)output_paragraph, temp_len);
2550
2551           for (i = 0; i < fill_column - output_paragraph_offset; i++)
2552             output_paragraph[i] = ' ';
2553
2554           memcpy ((char *)output_paragraph + i, temp, temp_len);
2555           free (temp);
2556           output_paragraph_offset = fill_column;
2557           adjust_braces_following (0, i);
2558         }
2559     }
2560 }
2561
2562 /* Begin a new paragraph. */
2563 void
2564 start_paragraph ()
2565 {
2566   /* First close existing one. */
2567   if (paragraph_is_open)
2568     close_paragraph ();
2569
2570   /* In either case, the insertion paragraph is no longer closed. */
2571   insertion_paragraph_closed = 0;
2572
2573   /* However, the paragraph is open! */
2574   paragraph_is_open = 1;
2575
2576   /* If we MUST_START_PARAGRAPH, that simply means that start_paragraph ()
2577      had to be called before we would allow any other paragraph operations
2578      to have an effect. */
2579   if (!must_start_paragraph)
2580     {
2581       int amount_to_indent = 0;
2582
2583       /* If doing indentation, then insert the appropriate amount. */
2584       if (!no_indent)
2585         {
2586           if (inhibit_paragraph_indentation)
2587             {
2588               amount_to_indent = current_indent;
2589               if (inhibit_paragraph_indentation < 0)
2590                 inhibit_paragraph_indentation++;
2591             }
2592           else if (paragraph_start_indent < 0)
2593             amount_to_indent = current_indent;
2594           else
2595             amount_to_indent = current_indent + paragraph_start_indent;
2596
2597           if (amount_to_indent >= output_column)
2598             {
2599               amount_to_indent -= output_column;
2600               indent (amount_to_indent);
2601               output_column += amount_to_indent;
2602             }
2603         }
2604     }
2605   else
2606     must_start_paragraph = 0;
2607 }
2608
2609 /* Insert the indentation specified by AMOUNT. */
2610 void
2611 indent (amount)
2612      int amount;
2613 {
2614   if (html)
2615     return;
2616
2617   /* For every START_POS saved within the brace stack which will be affected
2618      by this indentation, bump that start pos forward. */
2619   adjust_braces_following (output_paragraph_offset, amount);
2620
2621   while (--amount >= 0)
2622     insert (' ');
2623 }
2624
2625 /* Search forward for STRING in input_text.
2626    FROM says where where to start. */
2627 int
2628 search_forward (string, from)
2629      char *string;
2630      int from;
2631 {
2632   int len = strlen (string);
2633
2634   while (from < input_text_length)
2635     {
2636       if (strncmp (input_text + from, string, len) == 0)
2637         return from;
2638       from++;
2639     }
2640   return -1;
2641 }
2642 \f
2643 /* Cross references.  */
2644
2645 /* Return next comma-delimited argument, but do not cross a close-brace
2646    boundary.  Clean up whitespace, too.  If EXPAND is nonzero, replace
2647    the entire brace-delimited argument list with its expansion before
2648    looking for the next comma.  */
2649 char *
2650 get_xref_token (expand)
2651      int expand;
2652 {
2653   char *string;
2654
2655   if (expand)
2656     {
2657       int old_offset = input_text_offset;
2658       int old_lineno = line_number;
2659
2660       get_until_in_braces ("}", &string);
2661       if (curchar () == '}')    /* as opposed to end of text */
2662         input_text_offset++;
2663       if (input_text_offset > old_offset)
2664         {
2665           int limit = input_text_offset;
2666
2667           input_text_offset = old_offset;
2668           line_number = old_lineno;
2669           only_macro_expansion++;
2670           replace_with_expansion (input_text_offset, &limit);
2671           only_macro_expansion--;
2672         }
2673       free (string);
2674     }
2675
2676   get_until_in_braces (",", &string);
2677   if (curchar () == ',')
2678     input_text_offset++;
2679   fix_whitespace (string);
2680   return string;
2681 }
2682
2683 /* NOTE: If you wonder why the HTML output is produced with such a
2684    peculiar mix of calls to add_word and execute_string, here's the
2685    reason.  get_xref_token (1) expands all macros in a reference, but
2686    any other commands, like @value, @@, etc., are left intact.  To
2687    expand them, we need to run the arguments through execute_string.
2688    However, characters like <, &, > and others cannot be let into
2689    execute_string, because they will be escaped.  See the mess?  */
2690
2691 /* Make a cross reference. */
2692 void
2693 cm_xref (arg)
2694 {
2695   if (arg == START)
2696     {
2697       char *arg1 = get_xref_token (1); /* expands all macros in xref */
2698       char *arg2 = get_xref_token (0);
2699       char *arg3 = get_xref_token (0);
2700       char *arg4 = get_xref_token (0);
2701       char *arg5 = get_xref_token (0);
2702       char *tem;
2703
2704       if (html)
2705         {
2706           if (!ref_flag)
2707             add_word_args ("%s", px_ref_flag ? _("see ") : _("See "));
2708         }
2709       else
2710         add_word_args ("%s", px_ref_flag ? "*note " : "*Note ");
2711
2712       if (*arg5 || *arg4)
2713         {
2714           char *node_name;
2715
2716           if (!*arg2)
2717             {
2718               if (*arg3)
2719                 node_name = arg3;
2720               else
2721                 node_name = arg1;
2722             }
2723           else
2724             node_name = arg2;
2725
2726           if (html)
2727             {
2728               /* html fixxme: revisit this; external node name not
2729                  much use to us with numbered nodes. */
2730               add_word ("<a href=");
2731               execute_string ("\"%s.html#", arg4);
2732               /* Do not collapse -- to -, etc., in references.  */
2733               in_fixed_width_font++;
2734               tem = expansion (node_name, 0);
2735               in_fixed_width_font--;
2736               add_escaped_anchor_name (tem);
2737               free (tem);
2738               add_word ("\">");
2739               execute_string ("%s", arg1);
2740               add_word ("</a>");
2741             }
2742           else
2743             {
2744               execute_string ("%s:", node_name);
2745               in_fixed_width_font++;
2746               execute_string (" (%s)%s%s", arg4, arg1, px_ref_flag ? "." : "");
2747               in_fixed_width_font--;
2748             }
2749
2750           /* Free all of the arguments found. */
2751           if (arg1) free (arg1);
2752           if (arg2) free (arg2);
2753           if (arg3) free (arg3);
2754           if (arg4) free (arg4);
2755           if (arg5) free (arg5);
2756           return;
2757         }
2758       else
2759         remember_node_reference (arg1, line_number, followed_reference);
2760
2761       if (*arg3)
2762         {
2763           if (html)
2764             {
2765               add_word ("<a href=\"");
2766               in_fixed_width_font++;
2767               tem = expansion (arg1, 0);
2768               in_fixed_width_font--;
2769               add_anchor_name (tem, 1);
2770               free (tem);
2771               add_word ("\">");
2772               execute_string ("%s", *arg2 ? arg2 : arg3);
2773               add_word ("</a>");
2774             }
2775           else
2776             {
2777               execute_string ("%s:", *arg2 ? arg2 : arg3);
2778               in_fixed_width_font++;
2779               execute_string (" %s%s", arg1, px_ref_flag ? "." : "");
2780               in_fixed_width_font--;
2781             }
2782         }
2783       else
2784         {
2785           if (html)
2786             {
2787               add_word ("<a href=\"");
2788               in_fixed_width_font++;
2789               tem = expansion (arg1, 0);
2790               in_fixed_width_font--;
2791               add_anchor_name (tem, 1);
2792               free (tem);
2793               add_word ("\">");
2794               execute_string ("%s", *arg2 ? arg2 : arg1);
2795               add_word ("</a>");
2796             }
2797           else
2798             {
2799               if (*arg2)
2800                 {
2801                   execute_string ("%s:", arg2);
2802                   in_fixed_width_font++;
2803                   execute_string (" %s%s", arg1, px_ref_flag ? "." : "");
2804                   in_fixed_width_font--;
2805                 }
2806               else
2807                 {
2808                   in_fixed_width_font++;
2809                   execute_string ("%s::", arg1);
2810                   in_fixed_width_font--;
2811                 }
2812             }
2813         }
2814
2815       /* Free all of the arguments found. */
2816       if (arg1) free (arg1);
2817       if (arg2) free (arg2);
2818       if (arg3) free (arg3);
2819       if (arg4) free (arg4);
2820       if (arg5) free (arg5);
2821     }
2822   else
2823     { /* Check to make sure that the next non-whitespace character is
2824          valid to follow an xref (so info readers can find the node
2825          names).  `input_text_offset' is pointing at the "}" which ended
2826          the xref or ref command. */
2827       int temp;
2828
2829       for (temp = input_text_offset + 1; temp < input_text_length; )
2830         {
2831           if (cr_or_whitespace (input_text[temp]))
2832             temp++;
2833           else
2834             {
2835               if (input_text[temp] != '.' && input_text[temp] != ',')
2836                 warning (_("`.' or `,' must follow cross reference, not %c"),
2837                          input_text[temp]);
2838               break;
2839             }
2840         }
2841     }
2842 }
2843
2844 void
2845 cm_pxref (arg)
2846      int arg;
2847 {
2848   if (arg == START)
2849     {
2850       px_ref_flag++;
2851       cm_xref (arg);
2852       px_ref_flag--;
2853     }
2854   /* Note that cm_xref isn't called with arg == END, which disables
2855      the code near the end of cm_xref that checks for `.' or `,'
2856      after the cross-reference.  This is because @pxref{} generates
2857      the required character itself, when needed.  */
2858 }
2859
2860 void
2861 cm_ref (arg)
2862      int arg;
2863 {
2864   if (arg == START)
2865     {
2866       ref_flag++;
2867       cm_xref (arg);
2868       ref_flag--;
2869     }
2870 }
2871
2872 void
2873 cm_inforef (arg)
2874      int arg;
2875 {
2876   if (arg == START)
2877     {
2878       char *node = get_xref_token (1); /* expands all macros in inforef */
2879       char *pname = get_xref_token (0);
2880       char *file = get_xref_token (0);
2881
2882       if (html)
2883         {
2884           add_word (_("see "));
2885           /* html fixxme: revisit this */
2886           add_word ("<a href=");
2887           execute_string ("\"%s.html\"", file);
2888           add_word (">");
2889           execute_string ("%s", pname);
2890           add_word ("</a>");
2891         }
2892       else
2893         {
2894           if (*pname)
2895             execute_string ("*note %s: (%s)%s", pname, file, node);
2896           else
2897             execute_string ("*note (%s)%s::", file, node);
2898         }
2899
2900       free (node);
2901       free (pname);
2902       free (file);
2903     }
2904 }
2905
2906 /* A URL reference.  */
2907 void
2908 cm_uref (arg)
2909      int arg;
2910 {
2911   if (arg == START)
2912     {
2913       extern int printing_index;
2914       char *url  = get_xref_token (1); /* expands all macros in uref */
2915       char *desc = get_xref_token (0);
2916       char *replacement = get_xref_token (0);
2917
2918       if (html)
2919         { /* never need to show the url */
2920           add_word ("<a href=");
2921           /* don't collapse `--' etc. in the url */
2922           in_fixed_width_font++;
2923           execute_string ("\"%s\"", url);
2924           in_fixed_width_font--;
2925           add_word (">");
2926           execute_string ("%s", *replacement ? replacement
2927                                 : (*desc ? desc : url));
2928           add_word ("</a>");
2929         }
2930       else if (*replacement) /* do not show the url */
2931         execute_string ("%s", replacement);
2932       else if (*desc)        /* show both text and url */
2933         {
2934           execute_string ("%s ", desc);
2935           in_fixed_width_font++;
2936           execute_string ("(%s)", url);
2937           in_fixed_width_font--;
2938         }
2939       else /* no text at all, so have the url to show */
2940         {
2941           in_fixed_width_font++;
2942           execute_string ("%s%s%s",
2943                           printing_index ? "" : "`",
2944                           url,
2945                           printing_index ? "" : "'");
2946           in_fixed_width_font--;
2947         }
2948       if (url)
2949         free (url);
2950       if (desc)
2951         free (desc);
2952       if (replacement)
2953         free (replacement);
2954     }
2955 }
2956
2957 /* An email reference.  */
2958 void
2959 cm_email (arg)
2960      int arg;
2961 {
2962   if (arg == START)
2963     {
2964       char *addr = get_xref_token (1); /* expands all macros in email */
2965       char *name = get_xref_token (0);
2966
2967       if (html)
2968         {
2969           add_word ("<a href=");
2970           /* don't collapse `--' etc. in the address */
2971           in_fixed_width_font++;
2972           execute_string ("\"mailto:%s\"", addr);
2973           in_fixed_width_font--;
2974           add_word (">");
2975           execute_string ("%s", *name ? name : addr);
2976           add_word ("</a>");
2977         }
2978       else
2979         {
2980           execute_string ("%s%s", name, *name ? " "  : "");
2981           in_fixed_width_font++;
2982           execute_string ("<%s>", addr);
2983           in_fixed_width_font--;
2984         }
2985
2986       if (addr)
2987         free (addr);
2988       if (name)
2989         free (name);
2990     }
2991 }
2992
2993 /* An external image is a reference, kind of.  The parsing is (not
2994    coincidentally) similar, anyway.  */
2995 void
2996 cm_image (arg)
2997      int arg;
2998 {
2999   char *name_arg, *rest;
3000
3001   if (arg == END)
3002     return;
3003
3004   name_arg = get_xref_token (1); /* expands all macros in image */
3005   /* We don't (yet) care about any other args, but read them so they
3006      don't end up in the text.  */
3007   rest = get_xref_token (0);
3008   if (rest)
3009     free (rest);
3010   rest = get_xref_token (0);
3011   if (rest)
3012     free (rest);
3013
3014   if (*name_arg)
3015     {
3016       char *fullname = xmalloc (strlen (name_arg) + 4 + 1);
3017
3018       if (html)
3019         { /* fixxme It would be nice to insert more useful alt text.  */
3020           sprintf (fullname, "%s.png", name_arg);
3021           if (access (fullname, R_OK) != 0)
3022             {
3023               sprintf (fullname, "%s.jpg", name_arg);
3024               if (access (fullname, R_OK) != 0)
3025                 {
3026                   line_error (_("No .png or .jpg for `%s'"), name_arg);
3027                   return;
3028                 }
3029           }
3030
3031           add_word_args ("<img src=\"%s\" alt=\"%s\">", fullname, fullname);
3032         }
3033       else
3034         { /* Try to open foo.txt.  */
3035           FILE *image_file;
3036           strcpy (fullname, name_arg);
3037           strcat (fullname, ".txt");
3038           image_file = fopen (fullname, "r");
3039           if (image_file)
3040             {
3041               int ch;
3042               int save_inhibit_indentation = inhibit_paragraph_indentation;
3043               int save_filling_enabled = filling_enabled;
3044
3045               inhibit_paragraph_indentation = 1;
3046               filling_enabled = 0;
3047               last_char_was_newline = 0;
3048
3049               /* Maybe we need to remove the final newline if the image
3050                  file is only one line to allow in-line images.  On the
3051                  other hand, they could just make the file without a
3052                  final newline.  */
3053               while ((ch = getc (image_file)) != EOF)
3054                 add_char (ch);
3055
3056               inhibit_paragraph_indentation = save_inhibit_indentation;
3057               filling_enabled = save_filling_enabled;
3058
3059               if (fclose (image_file) != 0)
3060                 perror (fullname);
3061             }
3062           else
3063             warning (_("@image file `%s' unreadable: %s"), fullname,
3064                        strerror (errno));
3065         }
3066
3067       free (fullname);
3068     }
3069   else
3070     line_error (_("@image missing filename argument"));
3071
3072   if (name_arg)
3073     free (name_arg);
3074 }
3075 \f
3076 /* Conditionals.  */
3077
3078 /* A structure which contains `defined' variables. */
3079 typedef struct defines {
3080   struct defines *next;
3081   char *name;
3082   char *value;
3083 } DEFINE;
3084
3085 /* The linked list of `set' defines. */
3086 DEFINE *defines = NULL;
3087
3088 /* Add NAME to the list of `set' defines. */
3089 void
3090 set (name, value)
3091      char *name;
3092      char *value;
3093 {
3094   DEFINE *temp;
3095
3096   for (temp = defines; temp; temp = temp->next)
3097     if (strcmp (name, temp->name) == 0)
3098       {
3099         free (temp->value);
3100         temp->value = xstrdup (value);
3101         return;
3102       }
3103
3104   temp = xmalloc (sizeof (DEFINE));
3105   temp->next = defines;
3106   temp->name = xstrdup (name);
3107   temp->value = xstrdup (value);
3108   defines = temp;
3109 }
3110
3111 /* Remove NAME from the list of `set' defines. */
3112 void
3113 clear (name)
3114      char *name;
3115 {
3116   DEFINE *temp, *last;
3117
3118   last = NULL;
3119   temp = defines;
3120
3121   while (temp)
3122     {
3123       if (strcmp (temp->name, name) == 0)
3124         {
3125           if (last)
3126             last->next = temp->next;
3127           else
3128             defines = temp->next;
3129
3130           free (temp->name);
3131           free (temp->value);
3132           free (temp);
3133           break;
3134         }
3135       last = temp;
3136       temp = temp->next;
3137     }
3138 }
3139
3140 /* Return the value of NAME.  The return value is NULL if NAME is unset. */
3141 char *
3142 set_p (name)
3143      char *name;
3144 {
3145   DEFINE *temp;
3146
3147   for (temp = defines; temp; temp = temp->next)
3148     if (strcmp (temp->name, name) == 0)
3149       return temp->value;
3150
3151   return NULL;
3152 }
3153
3154 /* Create a variable whose name appears as the first word on this line. */
3155 void
3156 cm_set ()
3157 {
3158   handle_variable (SET);
3159 }
3160
3161 /* Remove a variable whose name appears as the first word on this line. */
3162 void
3163 cm_clear ()
3164 {
3165   handle_variable (CLEAR);
3166 }
3167
3168 void
3169 cm_ifset ()
3170 {
3171   handle_variable (IFSET);
3172 }
3173
3174 void
3175 cm_ifclear ()
3176 {
3177   handle_variable (IFCLEAR);
3178 }
3179
3180 /* This command takes braces, but we parse the contents specially, so we
3181    don't use the standard brace popping code.
3182
3183    The syntax @ifeq{arg1, arg2, texinfo-commands} performs texinfo-commands
3184    if ARG1 and ARG2 caselessly string compare to the same string, otherwise,
3185    it produces no output. */
3186 void
3187 cm_ifeq ()
3188 {
3189   char **arglist;
3190
3191   arglist = get_brace_args (0);
3192
3193   if (arglist)
3194     {
3195       if (array_len (arglist) > 1)
3196         {
3197           if ((strcasecmp (arglist[0], arglist[1]) == 0) &&
3198               (arglist[2]))
3199             execute_string ("%s\n", arglist[2]);
3200         }
3201
3202       free_array (arglist);
3203     }
3204 }
3205
3206 void
3207 cm_value (arg, start_pos, end_pos)
3208      int arg, start_pos, end_pos;
3209 {
3210   static int value_level = 0, saved_meta_pos = -1;
3211
3212   /* All the text after @value{ upto the matching } will eventually
3213      disappear from output_paragraph, when this function is called
3214      with ARG == END.  If the text produced until then sets
3215      meta_char_pos, we will need to restore it to the value it had
3216      before @value was seen.  So we need to save the previous value
3217      of meta_char_pos here.  */
3218   if (arg == START)
3219     {
3220       /* If we are already inside some outer @value, don't overwrite
3221          the value saved in saved_meta_pos.  */
3222       if (!value_level)
3223         saved_meta_pos = meta_char_pos;
3224       value_level++;
3225       /* While the argument of @value is processed, we need to inhibit
3226          textual transformations like "--" into "-", since @set didn't
3227          do that when it grabbed the name of the variable.  */
3228       in_fixed_width_font++;
3229     }
3230   else
3231     {
3232       char *name = (char *) &output_paragraph[start_pos];
3233       char *value;
3234       output_paragraph[end_pos] = 0;
3235       name = xstrdup (name);
3236       value = set_p (name);
3237       output_column -= end_pos - start_pos;
3238       output_paragraph_offset = start_pos;
3239
3240       /* Restore the previous value of meta_char_pos if the stuff
3241          inside this @value{} moved it.  */
3242       if (saved_meta_pos == -1) /* can't happen inside @value{} */
3243         abort ();
3244       if (value_level == 1
3245           && meta_char_pos >= start_pos && meta_char_pos < end_pos)
3246         {
3247           meta_char_pos = saved_meta_pos;
3248           saved_meta_pos = -1;
3249         }
3250       value_level--;
3251       /* No need to decrement in_fixed_width_font, since before
3252          we are called with arg == END, the reader loop already
3253          popped the brace stack, which restored in_fixed_width_font,
3254          among other things.  */
3255
3256       if (value)
3257         execute_string ("%s", value);
3258       else
3259         add_word_args (_("{No value for `%s'}"), name);
3260
3261       free (name);
3262     }
3263 }
3264
3265 /* Set, clear, or conditionalize based on ACTION. */
3266 void
3267 handle_variable (action)
3268      int action;
3269 {
3270   char *name;
3271
3272   get_rest_of_line (0, &name);
3273   /* If we hit the end of text in get_rest_of_line, backing up
3274      input pointer will cause the last character of the last line
3275      be pushed back onto the input, which is wrong.  */
3276   if (input_text_offset < input_text_length)
3277     backup_input_pointer ();
3278   handle_variable_internal (action, name);
3279   free (name);
3280 }
3281
3282 void
3283 handle_variable_internal (action, name)
3284      int action;
3285      char *name;
3286 {
3287   char *temp;
3288   int delimiter, additional_text_present = 0;
3289
3290   /* Only the first word of NAME is a valid tag. */
3291   temp = name;
3292   delimiter = 0;
3293   while (*temp && (delimiter || !whitespace (*temp)))
3294     {
3295 /* #if defined (SET_WITH_EQUAL) */
3296       if (*temp == '"' || *temp == '\'')
3297         {
3298           if (*temp == delimiter)
3299             delimiter = 0;
3300           else
3301             delimiter = *temp;
3302         }
3303 /* #endif SET_WITH_EQUAL */
3304       temp++;
3305     }
3306
3307   if (*temp)
3308     additional_text_present++;
3309
3310   *temp = 0;
3311
3312   if (!*name)
3313     line_error (_("%c%s requires a name"), COMMAND_PREFIX, command);
3314   else
3315     {
3316       switch (action)
3317         {
3318         case SET:
3319           {
3320             char *value;
3321
3322 #if defined (SET_WITH_EQUAL)
3323             /* Allow a value to be saved along with a variable.  The value is
3324                the text following an `=' sign in NAME, if any is present. */
3325
3326             for (value = name; *value && *value != '='; value++);
3327
3328             if (*value)
3329               *value++ = 0;
3330
3331             if (*value == '"' || *value == '\'')
3332               {
3333                 value++;
3334                 value[strlen (value) - 1] = 0;
3335               }
3336
3337 #else /* !SET_WITH_EQUAL */
3338             /* The VALUE of NAME is the remainder of the line sans
3339                whitespace. */
3340             if (additional_text_present)
3341               {
3342                 value = temp + 1;
3343                 canon_white (value);
3344               }
3345             else
3346               value = "";
3347 #endif /* !SET_WITH_VALUE */
3348
3349             set (name, value);
3350           }
3351           break;
3352
3353         case CLEAR:
3354           clear (name);
3355           break;
3356
3357         case IFSET:
3358         case IFCLEAR:
3359           /* If IFSET and NAME is not set, or if IFCLEAR and NAME is set,
3360              read lines from the the file until we reach a matching
3361              "@end CONDITION".  This means that we only take note of
3362              "@ifset/clear" and "@end" commands. */
3363           {
3364             char condition[8];
3365             int condition_len;
3366             int orig_line_number = line_number;
3367
3368             if (action == IFSET)
3369               strcpy (condition, "ifset");
3370             else
3371               strcpy (condition, "ifclear");
3372
3373             condition_len = strlen (condition);
3374
3375           if ((action == IFSET && !set_p (name))
3376               || (action == IFCLEAR && set_p (name)))
3377             {
3378               int level = 0, done = 0;
3379
3380               while (!done && input_text_offset < input_text_length)
3381                 {
3382                   char *freeable_line, *line;
3383
3384                   get_rest_of_line (0, &freeable_line);
3385
3386                   for (line = freeable_line; whitespace (*line); line++);
3387
3388                   if (*line == COMMAND_PREFIX &&
3389                       (strncmp (line + 1, condition, condition_len) == 0))
3390                     level++;
3391                   else if (strncmp (line, "@end", 4) == 0)
3392                     {
3393                       char *cname = line + 4;
3394                       char *temp;
3395
3396                       while (*cname && whitespace (*cname))
3397                         cname++;
3398                       temp = cname;
3399
3400                       while (*temp && !whitespace (*temp))
3401                         temp++;
3402                       *temp = 0;
3403
3404                       if (strcmp (cname, condition) == 0)
3405                         {
3406                           if (!level)
3407                             {
3408                               done = 1;
3409                             }
3410                           else
3411                             level--;
3412                         }
3413                     }
3414                   free (freeable_line);
3415                 }
3416
3417               if (!done)
3418                 {
3419                   int save = line_number;
3420                   line_number = orig_line_number;
3421                   line_error (_("Reached eof before matching @end %s"),
3422                               condition);
3423                   line_number = save;
3424                 }
3425
3426               /* We found the end of a false @ifset/ifclear.  If we are
3427                  in a menu, back up over the newline that ends the ifset,
3428                  since that newline may also begin the next menu entry. */
3429               break;
3430             }
3431           else
3432             {
3433               if (action == IFSET)
3434                 begin_insertion (ifset);
3435               else
3436                 begin_insertion (ifclear);
3437             }
3438           }
3439           break;
3440         }
3441     }
3442 }
3443 \f
3444 /* Execution of random text not in file. */
3445
3446 typedef struct {
3447   char *string;                 /* The string buffer. */
3448   int size;                     /* The size of the buffer. */
3449   int in_use;                   /* Nonzero means string currently in use. */
3450 } EXECUTION_STRING;
3451
3452 static EXECUTION_STRING **execution_strings = NULL;
3453 static int execution_strings_index = 0;
3454 static int execution_strings_slots = 0;
3455
3456 EXECUTION_STRING *
3457 get_execution_string (initial_size)
3458      int initial_size;
3459 {
3460   int i = 0;
3461   EXECUTION_STRING *es = NULL;
3462
3463   if (execution_strings)
3464     {
3465       for (i = 0; i < execution_strings_index; i++)
3466         if (execution_strings[i] && (execution_strings[i]->in_use == 0))
3467           {
3468             es = execution_strings[i];
3469             break;
3470           }
3471     }
3472
3473   if (!es)
3474     {
3475       if (execution_strings_index + 1 >= execution_strings_slots)
3476         {
3477           execution_strings = xrealloc
3478             (execution_strings,
3479              (execution_strings_slots += 3) * sizeof (EXECUTION_STRING *));
3480           for (; i < execution_strings_slots; i++)
3481             execution_strings[i] = NULL;
3482         }
3483
3484       execution_strings[execution_strings_index] =
3485         xmalloc (sizeof (EXECUTION_STRING));
3486       es = execution_strings[execution_strings_index];
3487       execution_strings_index++;
3488
3489       es->size = 0;
3490       es->string = NULL;
3491       es->in_use = 0;
3492     }
3493
3494   if (initial_size > es->size)
3495     {
3496       es->string = xrealloc (es->string, initial_size);
3497       es->size = initial_size;
3498     }
3499   return es;
3500 }
3501
3502 /* Given a pointer to TEXT and its desired length NEW_LEN, find TEXT's
3503    entry in the execution_strings[] array and change the .STRING and
3504    .SIZE members of that entry as appropriate.  */
3505 void
3506 maybe_update_execution_strings (text, new_len)
3507      char **text;
3508      unsigned new_len;
3509 {
3510   int i = 0;
3511
3512   if (execution_strings)
3513     {
3514       for (i = 0; i < execution_strings_index; i++)
3515         if (execution_strings[i] && (execution_strings[i]->in_use == 1) &&
3516             execution_strings[i]->string == *text)
3517           {
3518             /* Don't ever shrink the string storage in execution_strings[]!
3519                execute_string assumes that it is always big enough to store
3520                every possible execution_string, and will break if that's
3521                not true.  So we only enlarge the string storage if the
3522                current size isn't big enough.  */
3523             if (execution_strings[i]->size < new_len)
3524               {
3525                 execution_strings[i]->string =
3526                   *text = xrealloc (*text, new_len + 1);
3527                 execution_strings[i]->size = new_len + 1;
3528               }
3529             return;
3530           }
3531     }
3532   /* We should *never* end up here, since if we are inside
3533      execute_string, TEXT is always in execution_strings[].  */
3534   abort ();
3535 }
3536
3537 /* Execute the string produced by formatting the ARGs with FORMAT.  This
3538    is like submitting a new file with @include. */
3539 void
3540 #if defined (VA_FPRINTF) && __STDC__
3541 execute_string (char *format, ...)
3542 #else
3543 execute_string (format, va_alist)
3544     char *format;
3545     va_dcl
3546 #endif
3547 {
3548   EXECUTION_STRING *es;
3549   char *temp_string;
3550 #ifdef VA_FPRINTF
3551   va_list ap;
3552 #endif
3553
3554   es = get_execution_string (4000);
3555   temp_string = es->string;
3556   es->in_use = 1;
3557
3558   VA_START (ap, format);
3559 #ifdef VA_SPRINTF
3560   VA_SPRINTF (temp_string, format, ap);
3561 #else
3562   sprintf (temp_string, format, a1, a2, a3, a4, a5, a6, a7, a8);
3563 #endif /* not VA_SPRINTF */
3564   va_end (ap);
3565
3566   pushfile ();
3567   input_text_offset = 0;
3568   input_text = temp_string;
3569   input_filename = xstrdup (input_filename);
3570   input_text_length = strlen (temp_string);
3571
3572   executing_string++;
3573   reader_loop ();
3574   free (input_filename);
3575
3576   popfile ();
3577   executing_string--;
3578   es->in_use = 0;
3579 }
3580
3581
3582 /* Return what would be output for STR (in newly-malloced memory), i.e.,
3583    expand Texinfo commands.  If IMPLICIT_CODE is set, expand @code{STR}.  */
3584
3585 char *
3586 expansion (str, implicit_code)
3587     char *str;
3588     int implicit_code;
3589 {
3590   int length;
3591   char *result;
3592
3593   /* Inhibit any real output.  */
3594   int start = output_paragraph_offset;
3595   int saved_paragraph_is_open = paragraph_is_open;
3596   int saved_output_column = output_column;
3597
3598   /* Inhibit indentation and filling, so that extra newlines
3599      are not added to the expansion.  (This is undesirable if
3600      we write the expanded text to macro_expansion_output_stream.)  */
3601   int saved_filling_enabled = filling_enabled;
3602   int saved_indented_fill = indented_fill;
3603   int saved_no_indent = no_indent;
3604   int saved_escape_html = escape_html;
3605   int saved_meta_pos = meta_char_pos;
3606   int saved_last_char = last_inserted_character;
3607   int saved_last_nl = last_char_was_newline;
3608
3609   /* If we are called in the middle of processing a command, we need
3610      to dup and save the global variable `command' (which holds the
3611      name of this command), since the recursive reader loop will free
3612      it from under our feet if it finds any macros in STR.  */
3613   char *saved_command = command ? xstrdup (command) : NULL;
3614
3615   filling_enabled = 0;
3616   indented_fill = 0;
3617   no_indent = 1;
3618   escape_html = 0;
3619
3620   inhibit_output_flushing ();
3621   paragraph_is_open = 1;
3622   execute_string (implicit_code ? "@code{%s}" : "%s", str);
3623   uninhibit_output_flushing ();
3624
3625   /* Copy the expansion from the buffer.  */
3626   length = output_paragraph_offset - start;
3627   result = xmalloc (1 + length);
3628   memcpy (result, (char *) (output_paragraph + start), length);
3629   result[length] = 0;
3630
3631   /* Pretend it never happened.  */
3632   free_and_clear (&command);
3633   command = saved_command;
3634   output_paragraph_offset = start;
3635   paragraph_is_open = saved_paragraph_is_open;
3636   output_column = saved_output_column;
3637   filling_enabled = saved_filling_enabled;
3638   indented_fill = saved_indented_fill;
3639   no_indent = saved_no_indent;
3640   escape_html = saved_escape_html;
3641   meta_char_pos = saved_meta_pos;
3642   last_inserted_character = saved_last_char;
3643   last_char_was_newline = saved_last_nl;
3644
3645   return result;
3646 }
3647
3648
3649 /* Return text (info) expansion of STR no matter what the current output
3650    format is.  */
3651
3652 char *
3653 text_expansion (str)
3654     char *str;
3655 {
3656   char *ret;
3657   int save_html = html;
3658   
3659   html = 0;
3660   ret = expansion (str, 0);
3661   html = save_html;
3662   
3663   return ret;
3664 }
3665
3666 \f
3667 /* Set the paragraph indentation variable to the value specified in STRING.
3668    Values can be:
3669      `asis': Don't change existing indentation.
3670      `none': Remove existing indentation.
3671         NUM: Indent NUM spaces at the starts of paragraphs.
3672              If NUM is zero, we assume `none'.
3673    Returns 0 if successful, or nonzero if STRING isn't one of the above. */
3674 int
3675 set_paragraph_indent (string)
3676      char *string;
3677 {
3678   if (strcmp (string, "asis") == 0 || strcmp (string, _("asis")) == 0)
3679     paragraph_start_indent = 0;
3680   else if (strcmp (string, "none") == 0 || strcmp (string, _("none")) == 0)
3681     paragraph_start_indent = -1;
3682   else
3683     {
3684       if (sscanf (string, "%d", &paragraph_start_indent) != 1)
3685         return -1;
3686       else
3687         {
3688           if (paragraph_start_indent == 0)
3689             paragraph_start_indent = -1;
3690         }
3691     }
3692   return 0;
3693 }