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