1 /*	$NetBSD: makeinfo.c,v 1.2 2016/01/14 00:34:53 christos Exp $	*/
2 
3 /* makeinfo -- convert Texinfo source into other formats.
4    Id: makeinfo.c,v 1.74 2004/12/19 17:15:42 karl Exp
5 
6    Copyright (C) 1987, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
7    2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
8 
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2, or (at your option)
12    any later version.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 
23    Original author of makeinfo: Brian Fox (bfox@ai.mit.edu).  */
24 
25 #include "system.h"
26 #include "getopt.h"
27 
28 #define COMPILING_MAKEINFO
29 #include "makeinfo.h"
30 #include "cmds.h"
31 #include "files.h"
32 #include "float.h"
33 #include "footnote.h"
34 #include "html.h"
35 #include "index.h"
36 #include "insertion.h"
37 #include "lang.h"
38 #include "macro.h"
39 #include "node.h"
40 #include "sectioning.h"
41 #include "toc.h"
42 #include "xml.h"
43 
44 /* You can change some of the behavior of Makeinfo by changing the
45    following defines: */
46 
47 /* Define INDENT_PARAGRAPHS_IN_TABLE if you want the paragraphs which
48    appear within an @table, @ftable, or @itemize environment to have
49    standard paragraph indentation.  Without this, such paragraphs have
50    no starting indentation. */
51 /* #define INDENT_PARAGRAPHS_IN_TABLE */
52 
53 /* Define PARAGRAPH_START_INDENT to be the amount of indentation that
54    the first lines of paragraphs receive by default, where no other
55    value has been specified.  Users can change this value on the command
56    line, with the --paragraph-indent option, or within the texinfo file,
57    with the @paragraphindent command. */
58 #define PARAGRAPH_START_INDENT 3
59 
60 /* Define DEFAULT_PARAGRAPH_SPACING as the number of blank lines that you
61    wish to appear between paragraphs.  A value of 1 creates a single blank
62    line between paragraphs.  Paragraphs are defined by 2 or more consecutive
63    newlines in the input file (i.e., one or more blank lines). */
64 #define DEFAULT_PARAGRAPH_SPACING 1
65 
66 /* Global variables.  */
67 
68 /* The output file name. */
69 char *output_filename = NULL;
70 
71 /* Name of the output file that the user elected to pass on the command line.
72    Such a name overrides any name found with the @setfilename command. */
73 char *command_output_filename = NULL;
74 static char *save_command_output_filename = NULL;
75 
76 #define INITIAL_PARAGRAPH_SPACE 5000
77 int paragraph_buffer_len = INITIAL_PARAGRAPH_SPACE;
78 
79 /* The amount of indentation to add at the starts of paragraphs.
80    0 means don't change existing indentation at paragraph starts.
81    > 0 is amount to indent new paragraphs by.
82    < 0 means indent to column zero by removing indentation if necessary.
83 
84    This is normally zero, but some people prefer paragraph starts to be
85    somewhat more indented than paragraph bodies.  A pretty value for
86    this is 3. */
87 int paragraph_start_indent = PARAGRAPH_START_INDENT;
88 
89 /* Indentation that is pending insertion.  We have this for hacking lines
90    which look blank, but contain whitespace.  We want to treat those as
91    blank lines. */
92 int pending_indent = 0;
93 
94 /* The index in our internal command table of the currently
95    executing command. */
96 int command_index;
97 
98 /* A search string which is used to find the first @setfilename. */
99 char setfilename_search[] =
100   { COMMAND_PREFIX,
101       's', 'e', 't', 'f', 'i', 'l', 'e', 'n', 'a', 'm', 'e', 0 };
102 
103 /* Values for calling handle_variable_internal (). */
104 #define SET     1
105 #define CLEAR   2
106 #define IFSET   3
107 #define IFCLEAR 4
108 
109 /* Flags controlling the operation of the program. */
110 
111 /* Default is to remove output if there were errors.  */
112 int force = 0;
113 
114 /* Default is to notify users of bad choices. */
115 int print_warnings = 1;
116 
117 /* Number of errors that we tolerate on a given fileset. */
118 int max_error_level = 100;
119 
120 /* The actual last inserted character.  Note that this may be something
121    other than NEWLINE even if last_char_was_newline is 1. */
122 int last_inserted_character = 0;
123 
124 /* Nonzero means that a newline character has already been
125    inserted, so close_paragraph () should insert one less. */
126 int line_already_broken = 0;
127 
128 /* When nonzero we have finished an insertion (see end_insertion ()) and we
129    want to ignore false continued paragraph closings. */
130 int insertion_paragraph_closed = 0;
131 
132 /* Nonzero means attempt to make all of the lines have fill_column width. */
133 int do_justification = 0;
134 
135 /* Nonzero means don't replace whitespace with &nbsp; in HTML mode.  */
136 int in_html_elt = 0;
137 
138 /* Nonzero means we are inserting a block level HTML element that must not be
139    enclosed in a <p>, such as <ul>, <ol> and <h?>.  */
140 int in_html_block_level_elt = 0;
141 
142 /* True when expanding a macro definition.  */
143 static int executing_macro = 0;
144 
145 /* True when we are inside a <li> block of a menu.  */
146 static int in_menu_item = 0;
147 
148 typedef struct brace_element
149 {
150   struct brace_element *next;
151   COMMAND_FUNCTION *proc;
152   char *command;
153   int pos, line;
154   int in_fixed_width_font;
155 } BRACE_ELEMENT;
156 
157 BRACE_ELEMENT *brace_stack = NULL;
158 
159 static void convert_from_file (char *name);
160 static void convert_from_loaded_file (char *name);
161 static void convert_from_stream (FILE *stream, char *name);
162 static void do_flush_right_indentation (void);
163 static void handle_variable (int action);
164 static void handle_variable_internal (int action, char *name);
165 static void init_brace_stack (void);
166 static void init_internals (void);
167 static void pop_and_call_brace (void);
168 static void remember_brace (COMMAND_FUNCTION (*proc));
169 static int end_of_sentence_p (void);
170 
171 void maybe_update_execution_strings (char **text, unsigned int new_len);
172 
173 /* Error handling.  */
174 
175 /* Number of errors encountered. */
176 int errors_printed = 0;
177 
178 /* Remember that an error has been printed.  If more than
179    max_error_level have been printed, then exit the program. */
180 static void
remember_error(void)181 remember_error (void)
182 {
183   errors_printed++;
184   if (max_error_level && (errors_printed > max_error_level))
185     {
186       fprintf (stderr, _("Too many errors!  Gave up.\n"));
187       flush_file_stack ();
188       if (errors_printed - max_error_level < 2)
189 	cm_bye ();
190       xexit (1);
191     }
192 }
193 
194 /* Print the last error gotten from the file system. */
195 int
fs_error(char * filename)196 fs_error (char *filename)
197 {
198   remember_error ();
199   perror (filename);
200   return 0;
201 }
202 
203 /* Print an error message, and return false. */
204 void
205 #if defined (VA_FPRINTF) && __STDC__
error(const char * format,...)206 error (const char *format, ...)
207 #else
208 error (format, va_alist)
209      const char *format;
210      va_dcl
211 #endif
212 {
213 #ifdef VA_FPRINTF
214   va_list ap;
215 #endif
216 
217   remember_error ();
218 
219   VA_START (ap, format);
220 #ifdef VA_FPRINTF
221   VA_FPRINTF (stderr, format, ap);
222 #else
223   fprintf (stderr, format, a1, a2, a3, a4, a5, a6, a7, a8);
224 #endif /* not VA_FPRINTF */
225   va_end (ap);
226 
227   putc ('\n', stderr);
228 }
229 
230 /* Just like error (), but print the input file and line number as well. */
231 void
232 #if defined (VA_FPRINTF) && __STDC__
file_line_error(char * infile,int lno,const char * format,...)233 file_line_error (char *infile, int lno, const char *format, ...)
234 #else
235 file_line_error (infile, lno, format, va_alist)
236    char *infile;
237    int lno;
238    const char *format;
239    va_dcl
240 #endif
241 {
242 #ifdef VA_FPRINTF
243   va_list ap;
244 #endif
245 
246   remember_error ();
247   fprintf (stderr, "%s:%d: ", infile, lno);
248 
249   VA_START (ap, format);
250 #ifdef VA_FPRINTF
251   VA_FPRINTF (stderr, format, ap);
252 #else
253   fprintf (stderr, format, a1, a2, a3, a4, a5, a6, a7, a8);
254 #endif /* not VA_FPRINTF */
255   va_end (ap);
256 
257   fprintf (stderr, ".\n");
258 }
259 
260 /* Just like file_line_error (), but take the input file and the line
261    number from global variables. */
262 void
263 #if defined (VA_FPRINTF) && __STDC__
line_error(const char * format,...)264 line_error (const char *format, ...)
265 #else
266 line_error (format, va_alist)
267    const char *format;
268    va_dcl
269 #endif
270 {
271 #ifdef VA_FPRINTF
272   va_list ap;
273 #endif
274 
275   remember_error ();
276   fprintf (stderr, "%s:%d: ", input_filename, line_number);
277 
278   VA_START (ap, format);
279 #ifdef VA_FPRINTF
280   VA_FPRINTF (stderr, format, ap);
281 #else
282   fprintf (stderr, format, a1, a2, a3, a4, a5, a6, a7, a8);
283 #endif /* not VA_FPRINTF */
284   va_end (ap);
285 
286   fprintf (stderr, ".\n");
287 }
288 
289 void
290 #if defined (VA_FPRINTF) && __STDC__
warning(const char * format,...)291 warning (const char *format, ...)
292 #else
293 warning (format, va_alist)
294      const char *format;
295      va_dcl
296 #endif
297 {
298 #ifdef VA_FPRINTF
299   va_list ap;
300 #endif
301 
302   if (print_warnings)
303     {
304       fprintf (stderr, _("%s:%d: warning: "), input_filename, line_number);
305 
306       VA_START (ap, format);
307 #ifdef VA_FPRINTF
308       VA_FPRINTF (stderr, format, ap);
309 #else
310       fprintf (stderr, format, a1, a2, a3, a4, a5, a6, a7, a8);
311 #endif /* not VA_FPRINTF */
312       va_end (ap);
313 
314       fprintf (stderr, ".\n");
315     }
316 }
317 
318 
319 /* The other side of a malformed expression. */
320 static void
misplaced_brace(void)321 misplaced_brace (void)
322 {
323   line_error (_("Misplaced %c"), '}');
324 }
325 
326 /* Main.  */
327 
328 /* Display the version info of this invocation of Makeinfo. */
329 static void
print_version_info(void)330 print_version_info (void)
331 {
332   printf ("makeinfo (GNU %s) %s\n", PACKAGE, VERSION);
333 }
334 
335 /* If EXIT_VALUE is zero, print the full usage message to stdout.
336    Otherwise, just say to use --help for more info.
337    Then exit with EXIT_VALUE. */
338 static void
usage(int exit_value)339 usage (int exit_value)
340 {
341   if (exit_value != 0)
342     fprintf (stderr, _("Try `%s --help' for more information.\n"), progname);
343   else
344   {
345     printf (_("Usage: %s [OPTION]... TEXINFO-FILE...\n"), progname);
346     puts ("");
347 
348     puts (_("\
349 Translate Texinfo source documentation to various other formats, by default\n\
350 Info files suitable for reading online with Emacs or standalone GNU Info.\n"));
351 
352     printf (_("\
353 General options:\n\
354       --error-limit=NUM       quit after NUM errors (default %d).\n\
355       --force                 preserve output even if errors.\n\
356       --help                  display this help and exit.\n\
357       --no-validate           suppress node cross-reference validation.\n\
358       --no-warn               suppress warnings (but not errors).\n\
359       --reference-limit=NUM   warn about at most NUM references (default %d).\n\
360   -v, --verbose               explain what is being done.\n\
361       --version               display version information and exit.\n"),
362             max_error_level, reference_warning_limit);
363     puts ("");
364 
365      /* xgettext: no-wrap */
366     puts (_("\
367 Output format selection (default is to produce Info):\n\
368       --docbook             output Docbook XML rather than Info.\n\
369       --html                output HTML rather than Info.\n\
370       --xml                 output Texinfo XML rather than Info.\n\
371       --plaintext           output plain text rather than Info.\n\
372 "));
373 
374     puts (_("\
375 General output options:\n\
376   -E, --macro-expand FILE   output macro-expanded source to FILE.\n\
377                             ignoring any @setfilename.\n\
378       --no-headers          suppress node separators, Node: lines, and menus\n\
379                               from Info output (thus producing plain text)\n\
380                               or from HTML (thus producing shorter output);\n\
381                               also, write to standard output by default.\n\
382       --no-split            suppress splitting of Info or HTML output,\n\
383                             generate only one output file.\n\
384       --no-version-headers  suppress header with makeinfo version and\n\
385                             source path.\n\
386       --number-sections     output chapter and sectioning numbers.\n\
387   -o, --output=FILE         output to FILE (directory if split HTML),\n\
388 "));
389 
390     printf (_("\
391 Options for Info and plain text:\n\
392       --enable-encoding       output accented and special characters in\n\
393                                 Info output based on @documentencoding.\n\
394       --fill-column=NUM       break Info lines at NUM characters (default %d).\n\
395       --footnote-style=STYLE  output footnotes in Info according to STYLE:\n\
396                                 `separate' to put them in their own node;\n\
397                                 `end' to put them at the end of the node\n\
398                                   in which they are defined (default).\n\
399       --paragraph-indent=VAL  indent Info paragraphs by VAL spaces (default %d).\n\
400                                 If VAL is `none', do not indent; if VAL is\n\
401                                 `asis', preserve existing indentation.\n\
402       --split-size=NUM        split Info files at size NUM (default %d).\n"),
403              fill_column, paragraph_start_indent,
404              DEFAULT_SPLIT_SIZE);
405     puts ("");
406 
407     puts (_("\
408 Options for HTML:\n\
409       --css-include=FILE        include FILE in HTML <style> output;\n\
410                                   read stdin if FILE is -.\n\
411 "));
412 
413     printf (_("\
414 Options for XML and Docbook:\n\
415       --output-indent=VAL       indent XML elements by VAL spaces (default %d).\n\
416                                   If VAL is 0, ignorable whitespace is dropped.\n\
417 "), xml_indentation_increment);
418     puts ("");
419 
420     puts (_("\
421 Input file options:\n\
422       --commands-in-node-names  allow @ commands in node names.\n\
423   -D VAR                        define the variable VAR, as with @set.\n\
424   -I DIR                        append DIR to the @include search path.\n\
425   -P DIR                        prepend DIR to the @include search path.\n\
426   -U VAR                        undefine the variable VAR, as with @clear.\n\
427 "));
428 
429     puts (_("\
430 Conditional processing in input:\n\
431   --ifdocbook       process @ifdocbook and @docbook even if\n\
432                       not generating Docbook.\n\
433   --ifhtml          process @ifhtml and @html even if not generating HTML.\n\
434   --ifinfo          process @ifinfo even if not generating Info.\n\
435   --ifplaintext     process @ifplaintext even if not generating plain text.\n\
436   --iftex           process @iftex and @tex; implies --no-split.\n\
437   --ifxml           process @ifxml and @xml.\n\
438   --no-ifdocbook    do not process @ifdocbook and @docbook text.\n\
439   --no-ifhtml       do not process @ifhtml and @html text.\n\
440   --no-ifinfo       do not process @ifinfo text.\n\
441   --no-ifplaintext  do not process @ifplaintext text.\n\
442   --no-iftex        do not process @iftex and @tex text.\n\
443   --no-ifxml        do not process @ifxml and @xml text.\n\
444 \n\
445   Also, for the --no-ifFORMAT options, do process @ifnotFORMAT text.\n\
446 "));
447 
448     puts (_("\
449   The defaults for the @if... conditionals depend on the output format:\n\
450   if generating HTML, --ifhtml is on and the others are off;\n\
451   if generating Info, --ifinfo is on and the others are off;\n\
452   if generating plain text, --ifplaintext is on and the others are off;\n\
453   if generating XML, --ifxml is on and the others are off.\n\
454 "));
455 
456     fputs (_("\
457 Examples:\n\
458   makeinfo foo.texi                     write Info to foo's @setfilename\n\
459   makeinfo --html foo.texi              write HTML to @setfilename\n\
460   makeinfo --xml foo.texi               write Texinfo XML to @setfilename\n\
461   makeinfo --docbook foo.texi           write DocBook XML to @setfilename\n\
462   makeinfo --no-headers foo.texi        write plain text to standard output\n\
463 \n\
464   makeinfo --html --no-headers foo.texi write html without node lines, menus\n\
465   makeinfo --number-sections foo.texi   write Info with numbered sections\n\
466   makeinfo --no-split foo.texi          write one Info file however big\n\
467 "), stdout);
468 
469     puts (_("\n\
470 Email bug reports to bug-texinfo@gnu.org,\n\
471 general questions and discussion to help-texinfo@gnu.org.\n\
472 Texinfo home page: http://www.gnu.org/software/texinfo/"));
473 
474   } /* end of full help */
475 
476   xexit (exit_value);
477 }
478 
479 struct option long_options[] =
480 {
481   { "commands-in-node-names", 0, &expensive_validation, 1 },
482   { "css-include", 1, 0, 'C' },
483   { "docbook", 0, 0, 'd' },
484   { "enable-encoding", 0, &enable_encoding, 1 },
485   { "error-limit", 1, 0, 'e' },
486   { "fill-column", 1, 0, 'f' },
487   { "footnote-style", 1, 0, 's' },
488   { "force", 0, &force, 1 },
489   { "help", 0, 0, 'h' },
490   { "html", 0, 0, 'w' },
491   { "ifdocbook", 0, &process_docbook, 1 },
492   { "ifhtml", 0, &process_html, 1 },
493   { "ifinfo", 0, &process_info, 1 },
494   { "ifplaintext", 0, &process_plaintext, 1 },
495   { "iftex", 0, &process_tex, 1 },
496   { "ifxml", 0, &process_xml, 1 },
497   { "macro-expand", 1, 0, 'E' },
498   { "no-headers", 0, &no_headers, 1 },
499   { "no-ifdocbook", 0, &process_docbook, 0 },
500   { "no-ifhtml", 0, &process_html, 0 },
501   { "no-ifinfo", 0, &process_info, 0 },
502   { "no-ifplaintext", 0, &process_plaintext, 0 },
503   { "no-iftex", 0, &process_tex, 0 },
504   { "no-ifxml", 0, &process_xml, 0 },
505   { "no-number-footnotes", 0, &number_footnotes, 0 },
506   { "no-number-sections", 0, &number_sections, 0 },
507   { "no-pointer-validate", 0, &validating, 0 },
508   { "no-split", 0, &splitting, 0 },
509   { "no-validate", 0, &validating, 0 },
510   { "no-version-header", 0, &no_version_header, 1 },
511   { "no-warn", 0, &print_warnings, 0 },
512   { "number-footnotes", 0, &number_footnotes, 1 },
513   { "number-sections", 0, &number_sections, 1 },
514   { "output", 1, 0, 'o' },
515   { "output-indent", 1, 0, 'i' },
516   { "paragraph-indent", 1, 0, 'p' },
517   { "plaintext", 0, 0, 't' },
518   { "reference-limit", 1, 0, 'r' },
519   { "split-size", 1, 0, 'S'},
520   { "verbose", 0, &verbose_mode, 1 },
521   { "version", 0, 0, 'V' },
522   { "xml", 0, 0, 'x' },
523   {NULL, 0, NULL, 0}
524 };
525 
526 /* We use handle_variable_internal for -D and -U, and it depends on
527    execute_string, which depends on input_filename, which is not defined
528    while we are handling options. :-\  So we save these defines in this
529    struct, and handle them later.  */
530 typedef struct command_line_define
531 {
532   struct command_line_define *next;
533   int action;
534   char *define;
535 } COMMAND_LINE_DEFINE;
536 
537 static COMMAND_LINE_DEFINE *command_line_defines = NULL;
538 
539 /* For each file mentioned in the command line, process it, turning
540    Texinfo commands into wonderfully formatted output text. */
541 int
main(int argc,char ** argv)542 main (int argc, char **argv)
543 {
544   int c, ind;
545   int reading_from_stdin = 0;
546 
547 #ifdef HAVE_SETLOCALE
548   /* Do not use LC_ALL, because LC_NUMERIC screws up the scanf parsing
549      of the argument to @multicolumn.  */
550   setlocale (LC_TIME, "");
551 #ifdef LC_MESSAGES /* ultrix */
552   setlocale (LC_MESSAGES, "");
553 #endif
554   setlocale (LC_CTYPE, "");
555   setlocale (LC_COLLATE, "");
556 #endif
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 && !no_version_header)
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 = xmalloc (strlen (name_arg)
3367                                    + (ext_arg && *ext_arg
3368                                       ? strlen (ext_arg) : 4) + 1);
3369           strcpy (txtname, name_arg);
3370           strcat (txtname, ".txt");
3371           image_file = fopen (txtname, "r");
3372           if (image_file == NULL)
3373             {
3374               txtpath = get_file_info_in_path (txtname,
3375                                                include_files_path, &file_info);
3376               if (txtpath != NULL)
3377                 image_file = fopen (txtpath, "r");
3378             }
3379 
3380           if (image_file != NULL
3381               || access (fullname, R_OK) == 0
3382               || (pathname != NULL && access (pathname, R_OK) == 0))
3383             {
3384               int ch;
3385               int save_inhibit_indentation = inhibit_paragraph_indentation;
3386               int save_filling_enabled = filling_enabled;
3387               int image_in_brackets = paragraph_is_open;
3388 
3389               /* Write magic ^@^H[image ...^@^H] cookie in the info file, if
3390                  there's an accompanying bitmap.  Otherwise just include the
3391                  text image.  In the plaintext output, always include the text
3392                  image without the magic cookie.  */
3393               int use_magic_cookie = !no_headers
3394                 && access (fullname, R_OK) == 0 && !STREQ (fullname, txtname);
3395 
3396               inhibit_paragraph_indentation = 1;
3397               filling_enabled = 0;
3398               last_char_was_newline = 0;
3399 
3400               if (use_magic_cookie)
3401                 {
3402                   add_char ('\0');
3403                   add_word ("\010[image");
3404 
3405                   if (access (fullname, R_OK) == 0
3406                       || (pathname != NULL && access (pathname, R_OK) == 0))
3407                     add_word_args (" src=\"%s\"", fullname);
3408 
3409                   if (*alt_arg)
3410                     add_word_args (" alt=\"%s\"", alt_arg);
3411                 }
3412 
3413               if (image_file != NULL)
3414                 {
3415                   if (use_magic_cookie)
3416                     add_word (" text=\"");
3417 
3418                   if (image_in_brackets)
3419                     add_char ('[');
3420 
3421                   /* Maybe we need to remove the final newline if the image
3422                      file is only one line to allow in-line images.  On the
3423                      other hand, they could just make the file without a
3424                      final newline.  */
3425                   while ((ch = getc (image_file)) != EOF)
3426                     {
3427                       if (use_magic_cookie && (ch == '"' || ch == '\\'))
3428                         add_char ('\\');
3429                       add_char (ch);
3430                     }
3431 
3432                   if (image_in_brackets)
3433                     add_char (']');
3434 
3435                   if (use_magic_cookie)
3436                     add_char ('"');
3437 
3438                   if (fclose (image_file) != 0)
3439                     perror (txtname);
3440                 }
3441 
3442               if (use_magic_cookie)
3443                 {
3444                   add_char ('\0');
3445                   add_word ("\010]");
3446                 }
3447 
3448               inhibit_paragraph_indentation = save_inhibit_indentation;
3449               filling_enabled = save_filling_enabled;
3450             }
3451           else
3452             warning (_("@image file `%s' (for text) unreadable: %s"),
3453                         txtname, strerror (errno));
3454         }
3455 
3456       free (fullname);
3457       if (pathname)
3458         free (pathname);
3459     }
3460   else
3461     line_error (_("@image missing filename argument"));
3462 
3463   if (name_arg)
3464     free (name_arg);
3465   if (w_arg)
3466     free (w_arg);
3467   if (h_arg)
3468     free (h_arg);
3469   if (alt_arg)
3470     free (alt_arg);
3471   if (ext_arg)
3472     free (ext_arg);
3473 }
3474 
3475 /* Conditionals.  */
3476 
3477 /* A structure which contains `defined' variables. */
3478 typedef struct defines {
3479   struct defines *next;
3480   char *name;
3481   char *value;
3482 } DEFINE;
3483 
3484 /* The linked list of `set' defines. */
3485 DEFINE *defines = NULL;
3486 
3487 /* Add NAME to the list of `set' defines. */
3488 static void
set(char * name,char * value)3489 set (char *name, char *value)
3490 {
3491   DEFINE *temp;
3492 
3493   for (temp = defines; temp; temp = temp->next)
3494     if (strcmp (name, temp->name) == 0)
3495       {
3496         free (temp->value);
3497         temp->value = xstrdup (value);
3498         return;
3499       }
3500 
3501   temp = xmalloc (sizeof (DEFINE));
3502   temp->next = defines;
3503   temp->name = xstrdup (name);
3504   temp->value = xstrdup (value);
3505   defines = temp;
3506 
3507   if (xml && !docbook)
3508     {
3509       xml_insert_element_with_attribute (SETVALUE, START, "name=\"%s\"", name);
3510       execute_string ("%s", value);
3511       xml_insert_element (SETVALUE, END);
3512     }
3513 }
3514 
3515 /* Remove NAME from the list of `set' defines. */
3516 static void
clear(char * name)3517 clear (char *name)
3518 {
3519   DEFINE *temp, *last;
3520 
3521   last = NULL;
3522   temp = defines;
3523 
3524   while (temp)
3525     {
3526       if (strcmp (temp->name, name) == 0)
3527         {
3528           if (last)
3529             last->next = temp->next;
3530           else
3531             defines = temp->next;
3532 
3533           free (temp->name);
3534           free (temp->value);
3535           free (temp);
3536           break;
3537         }
3538       last = temp;
3539       temp = temp->next;
3540     }
3541 
3542   if (xml && !docbook)
3543     {
3544       xml_insert_element_with_attribute (CLEARVALUE, START, "name=\"%s\"", name);
3545       xml_insert_element (CLEARVALUE, END);
3546     }
3547 }
3548 
3549 /* Return the value of NAME.  The return value is NULL if NAME is unset. */
3550 static char *
set_p(char * name)3551 set_p (char *name)
3552 {
3553   DEFINE *temp;
3554 
3555   for (temp = defines; temp; temp = temp->next)
3556     if (strcmp (temp->name, name) == 0)
3557       return temp->value;
3558 
3559   return NULL;
3560 }
3561 
3562 /* Create a variable whose name appears as the first word on this line. */
3563 void
cm_set(void)3564 cm_set (void)
3565 {
3566   handle_variable (SET);
3567 }
3568 
3569 /* Remove a variable whose name appears as the first word on this line. */
3570 void
cm_clear(void)3571 cm_clear (void)
3572 {
3573   handle_variable (CLEAR);
3574 }
3575 
3576 void
cm_ifset(void)3577 cm_ifset (void)
3578 {
3579   handle_variable (IFSET);
3580 }
3581 
3582 void
cm_ifclear(void)3583 cm_ifclear (void)
3584 {
3585   handle_variable (IFCLEAR);
3586 }
3587 
3588 /* This command takes braces, but we parse the contents specially, so we
3589    don't use the standard brace popping code.
3590 
3591    The syntax @ifeq{arg1, arg2, texinfo-commands} performs texinfo-commands
3592    if ARG1 and ARG2 caselessly string compare to the same string, otherwise,
3593    it produces no output. */
3594 void
cm_ifeq(void)3595 cm_ifeq (void)
3596 {
3597   char **arglist;
3598 
3599   arglist = get_brace_args (0);
3600 
3601   if (arglist)
3602     {
3603       if (array_len (arglist) > 1)
3604         {
3605           if ((strcasecmp (arglist[0], arglist[1]) == 0) &&
3606               (arglist[2]))
3607             execute_string ("%s\n", arglist[2]);
3608         }
3609 
3610       free_array (arglist);
3611     }
3612 }
3613 
3614 void
cm_value(int arg,int start_pos,int end_pos)3615 cm_value (int arg, int start_pos, int end_pos)
3616 {
3617   static int value_level = 0, saved_meta_pos = -1;
3618 
3619   /* xml_add_char() skips any content inside menus when output format is
3620      Docbook, so @value{} is no use there.  Also start_pos and end_pos does not
3621      get updated, causing name to be empty string.  So just return.  */
3622    if (docbook && in_menu)
3623      return;
3624 
3625   /* All the text after @value{ upto the matching } will eventually
3626      disappear from output_paragraph, when this function is called
3627      with ARG == END.  If the text produced until then sets
3628      meta_char_pos, we will need to restore it to the value it had
3629      before @value was seen.  So we need to save the previous value
3630      of meta_char_pos here.  */
3631   if (arg == START)
3632     {
3633       /* If we are already inside some outer @value, don't overwrite
3634          the value saved in saved_meta_pos.  */
3635       if (!value_level)
3636         saved_meta_pos = meta_char_pos;
3637       value_level++;
3638       /* While the argument of @value is processed, we need to inhibit
3639          textual transformations like "--" into "-", since @set didn't
3640          do that when it grabbed the name of the variable.  */
3641       in_fixed_width_font++;
3642     }
3643   else
3644     {
3645       char *name = (char *) &output_paragraph[start_pos];
3646       char *value;
3647       output_paragraph[end_pos] = 0;
3648       name = xstrdup (name);
3649       value = set_p (name);
3650       output_column -= end_pos - start_pos;
3651       output_paragraph_offset = start_pos;
3652 
3653       /* Restore the previous value of meta_char_pos if the stuff
3654          inside this @value{} moved it.  */
3655       if (saved_meta_pos == -1) /* can't happen inside @value{} */
3656         abort ();
3657       if (value_level == 1
3658           && meta_char_pos >= start_pos && meta_char_pos < end_pos)
3659         {
3660           meta_char_pos = saved_meta_pos;
3661           saved_meta_pos = -1;
3662         }
3663       value_level--;
3664       /* No need to decrement in_fixed_width_font, since before
3665          we are called with arg == END, the reader loop already
3666          popped the brace stack, which restored in_fixed_width_font,
3667          among other things.  */
3668 
3669       if (value)
3670 	{
3671 	  /* We need to get past the closing brace since the value may
3672 	     expand to a context-sensitive macro (e.g. @xref) and produce
3673 	     spurious warnings */
3674 	  input_text_offset++;
3675 	  execute_string ("%s", value);
3676 	  input_text_offset--;
3677 	}
3678       else
3679 	{
3680           warning (_("undefined flag: %s"), name);
3681           add_word_args (_("{No value for `%s'}"), name);
3682 	}
3683 
3684       free (name);
3685     }
3686 }
3687 
3688 /* Set, clear, or conditionalize based on ACTION. */
3689 static void
handle_variable(int action)3690 handle_variable (int action)
3691 {
3692   char *name;
3693 
3694   get_rest_of_line (0, &name);
3695   /* If we hit the end of text in get_rest_of_line, backing up
3696      input pointer will cause the last character of the last line
3697      be pushed back onto the input, which is wrong.  */
3698   if (input_text_offset < input_text_length)
3699     backup_input_pointer ();
3700   handle_variable_internal (action, name);
3701   free (name);
3702 }
3703 
3704 static void
handle_variable_internal(int action,char * name)3705 handle_variable_internal (int action, char *name)
3706 {
3707   char *temp;
3708   int delimiter, additional_text_present = 0;
3709 
3710   /* Only the first word of NAME is a valid tag. */
3711   temp = name;
3712   delimiter = 0;
3713   while (*temp && (delimiter || !whitespace (*temp)))
3714     {
3715 /* #if defined (SET_WITH_EQUAL) */
3716       if (*temp == '"' || *temp == '\'')
3717         {
3718           if (*temp == delimiter)
3719             delimiter = 0;
3720           else
3721             delimiter = *temp;
3722         }
3723 /* #endif SET_WITH_EQUAL */
3724       temp++;
3725     }
3726 
3727   if (*temp)
3728     additional_text_present++;
3729 
3730   *temp = 0;
3731 
3732   if (!*name)
3733     line_error (_("%c%s requires a name"), COMMAND_PREFIX, command);
3734   else
3735     {
3736       switch (action)
3737         {
3738         case SET:
3739           {
3740             char *value;
3741 
3742 #if defined (SET_WITH_EQUAL)
3743             /* Allow a value to be saved along with a variable.  The value is
3744                the text following an `=' sign in NAME, if any is present. */
3745 
3746             for (value = name; *value && *value != '='; value++);
3747 
3748             if (*value)
3749               *value++ = 0;
3750 
3751             if (*value == '"' || *value == '\'')
3752               {
3753                 value++;
3754                 value[strlen (value) - 1] = 0;
3755               }
3756 
3757 #else /* !SET_WITH_EQUAL */
3758             /* The VALUE of NAME is the remainder of the line sans
3759                whitespace. */
3760             if (additional_text_present)
3761               {
3762                 value = temp + 1;
3763                 canon_white (value);
3764               }
3765             else
3766               value = "";
3767 #endif /* !SET_WITH_VALUE */
3768 
3769             set (name, value);
3770           }
3771           break;
3772 
3773         case CLEAR:
3774           clear (name);
3775           break;
3776 
3777         case IFSET:
3778         case IFCLEAR:
3779           /* If IFSET and NAME is not set, or if IFCLEAR and NAME is set,
3780              read lines from the the file until we reach a matching
3781              "@end CONDITION".  This means that we only take note of
3782              "@ifset/clear" and "@end" commands. */
3783           {
3784             char condition[8];
3785             int condition_len;
3786             int orig_line_number = line_number;
3787 
3788             if (action == IFSET)
3789               strcpy (condition, "ifset");
3790             else
3791               strcpy (condition, "ifclear");
3792 
3793             condition_len = strlen (condition);
3794 
3795           if ((action == IFSET && !set_p (name))
3796               || (action == IFCLEAR && set_p (name)))
3797             {
3798               int level = 0, done = 0;
3799 
3800               while (!done && input_text_offset < input_text_length)
3801                 {
3802                   char *freeable_line, *line;
3803 
3804                   get_rest_of_line (0, &freeable_line);
3805 
3806                   for (line = freeable_line; whitespace (*line); line++);
3807 
3808                   if (*line == COMMAND_PREFIX &&
3809                       (strncmp (line + 1, condition, condition_len) == 0))
3810                     level++;
3811                   else if (strncmp (line, "@end", 4) == 0)
3812                     {
3813                       char *cname = line + 4;
3814                       char *temp;
3815 
3816                       while (*cname && whitespace (*cname))
3817                         cname++;
3818                       temp = cname;
3819 
3820                       while (*temp && !whitespace (*temp))
3821                         temp++;
3822                       *temp = 0;
3823 
3824                       if (strcmp (cname, condition) == 0)
3825                         {
3826                           if (!level)
3827                             {
3828                               done = 1;
3829                             }
3830                           else
3831                             level--;
3832                         }
3833                     }
3834                   free (freeable_line);
3835                 }
3836 
3837               if (!done)
3838                 file_line_error (input_filename, orig_line_number,
3839                                  _("Reached eof before matching @end %s"),
3840                                  condition);
3841 
3842               /* We found the end of a false @ifset/ifclear.  If we are
3843                  in a menu, back up over the newline that ends the ifset,
3844                  since that newline may also begin the next menu entry. */
3845               break;
3846             }
3847           else
3848             {
3849               if (action == IFSET)
3850                 begin_insertion (ifset);
3851               else
3852                 begin_insertion (ifclear);
3853             }
3854           }
3855           break;
3856         }
3857     }
3858 }
3859 
3860 /* Execution of random text not in file. */
3861 typedef struct {
3862   char *string;                 /* The string buffer. */
3863   int size;                     /* The size of the buffer. */
3864   int in_use;                   /* Nonzero means string currently in use. */
3865 } EXECUTION_STRING;
3866 
3867 static EXECUTION_STRING **execution_strings = NULL;
3868 static int execution_strings_index = 0;
3869 static int execution_strings_slots = 0;
3870 
3871 static EXECUTION_STRING *
get_execution_string(int initial_size)3872 get_execution_string (int initial_size)
3873 {
3874   int i = 0;
3875   EXECUTION_STRING *es = NULL;
3876 
3877   if (execution_strings)
3878     {
3879       for (i = 0; i < execution_strings_index; i++)
3880         if (execution_strings[i] && (execution_strings[i]->in_use == 0))
3881           {
3882             es = execution_strings[i];
3883             break;
3884           }
3885     }
3886 
3887   if (!es)
3888     {
3889       if (execution_strings_index + 1 >= execution_strings_slots)
3890         {
3891           execution_strings = xrealloc
3892             (execution_strings,
3893              (execution_strings_slots += 3) * sizeof (EXECUTION_STRING *));
3894           for (; i < execution_strings_slots; i++)
3895             execution_strings[i] = NULL;
3896         }
3897 
3898       execution_strings[execution_strings_index] =
3899         xmalloc (sizeof (EXECUTION_STRING));
3900       es = execution_strings[execution_strings_index];
3901       execution_strings_index++;
3902 
3903       es->size = 0;
3904       es->string = NULL;
3905       es->in_use = 0;
3906     }
3907 
3908   if (initial_size > es->size)
3909     {
3910       es->string = xrealloc (es->string, initial_size);
3911       es->size = initial_size;
3912     }
3913   return es;
3914 }
3915 
3916 /* Given a pointer to TEXT and its desired length NEW_LEN, find TEXT's
3917    entry in the execution_strings[] array and change the .STRING and
3918    .SIZE members of that entry as appropriate.  */
3919 void
maybe_update_execution_strings(char ** text,unsigned int new_len)3920 maybe_update_execution_strings (char **text, unsigned int new_len)
3921 {
3922   int i = 0;
3923 
3924   if (execution_strings)
3925     {
3926       for (i = 0; i < execution_strings_index; i++)
3927         if (execution_strings[i] && (execution_strings[i]->in_use == 1) &&
3928             execution_strings[i]->string == *text)
3929           {
3930             /* Don't ever shrink the string storage in execution_strings[]!
3931                execute_string assumes that it is always big enough to store
3932                every possible execution_string, and will break if that's
3933                not true.  So we only enlarge the string storage if the
3934                current size isn't big enough.  */
3935             if (execution_strings[i]->size < new_len)
3936               {
3937                 execution_strings[i]->string =
3938                   *text = xrealloc (*text, new_len + 1);
3939                 execution_strings[i]->size = new_len + 1;
3940               }
3941             return;
3942           }
3943     }
3944   /* We should *never* end up here, since if we are inside
3945      execute_string, TEXT is always in execution_strings[].  */
3946   abort ();
3947 }
3948 
3949 /* FIXME: this is an arbitrary limit.  */
3950 #define EXECUTE_STRING_MAX 16*1024
3951 
3952 /* Execute the string produced by formatting the ARGs with FORMAT.  This
3953    is like submitting a new file with @include. */
3954 void
3955 #if defined (VA_FPRINTF) && __STDC__
execute_string(char * format,...)3956 execute_string (char *format, ...)
3957 #else
3958 execute_string (format, va_alist)
3959     char *format;
3960     va_dcl
3961 #endif
3962 {
3963   EXECUTION_STRING *es;
3964   char *temp_string, *temp_input_filename;
3965 #ifdef VA_FPRINTF
3966   va_list ap;
3967 #endif
3968   int insertion_level_at_start = insertion_level;
3969 
3970   es = get_execution_string (EXECUTE_STRING_MAX);
3971   temp_string = es->string;
3972   es->in_use = 1;
3973 
3974   VA_START (ap, format);
3975 #ifdef VA_SPRINTF
3976   VA_SPRINTF (temp_string, format, ap);
3977 #else
3978   sprintf (temp_string, format, a1, a2, a3, a4, a5, a6, a7, a8);
3979 #endif /* not VA_SPRINTF */
3980   va_end (ap);
3981 
3982   pushfile ();
3983   input_text_offset = 0;
3984   input_text = temp_string;
3985   input_text_length = strlen (temp_string);
3986   input_filename = xstrdup (input_filename);
3987   temp_input_filename = input_filename;
3988 
3989   executing_string++;
3990   reader_loop ();
3991 
3992   /* If insertion stack level changes during execution, that means a multiline
3993      command is used inside braces or @section ... kind of commands.  */
3994   if (insertion_level_at_start != insertion_level && !executing_macro)
3995     {
3996       line_error (_("Multiline command %c%s used improperly"),
3997           COMMAND_PREFIX,
3998           command);
3999       /* We also need to keep insertion_level intact to make sure warnings are
4000          issued for @end ... command.  */
4001       while (insertion_level > insertion_level_at_start)
4002         pop_insertion ();
4003     }
4004 
4005   popfile ();
4006   executing_string--;
4007   es->in_use = 0;
4008   free (temp_input_filename);
4009 }
4010 
4011 
4012 /* Return what would be output for STR (in newly-malloced memory), i.e.,
4013    expand Texinfo commands according to the current output format.  If
4014    IMPLICIT_CODE is set, expand @code{STR}.  This is generally used for
4015    short texts; filling, indentation, and html escapes are disabled.  */
4016 
4017 char *
expansion(char * str,int implicit_code)4018 expansion (char *str, int implicit_code)
4019 {
4020   return maybe_escaped_expansion (str, implicit_code, 0);
4021 }
4022 
4023 
4024 /* Do HTML escapes according to DO_HTML_ESCAPE.  Needed in
4025    cm_printindex, q.v.  */
4026 
4027 char *
maybe_escaped_expansion(char * str,int implicit_code,int do_html_escape)4028 maybe_escaped_expansion (char *str, int implicit_code, int do_html_escape)
4029 {
4030   char *result;
4031 
4032   /* Inhibit indentation and filling, so that extra newlines
4033      are not added to the expansion.  (This is undesirable if
4034      we write the expanded text to macro_expansion_output_stream.)  */
4035   int saved_filling_enabled = filling_enabled;
4036   int saved_indented_fill = indented_fill;
4037   int saved_no_indent = no_indent;
4038   int saved_escape_html = escape_html;
4039 
4040   filling_enabled = 0;
4041   indented_fill = 0;
4042   no_indent = 1;
4043   escape_html = do_html_escape;
4044 
4045   result = full_expansion (str, implicit_code);
4046 
4047   filling_enabled = saved_filling_enabled;
4048   indented_fill = saved_indented_fill;
4049   no_indent = saved_no_indent;
4050   escape_html = saved_escape_html;
4051 
4052   return result;
4053 }
4054 
4055 
4056 /* Expand STR (or @code{STR} if IMPLICIT_CODE is nonzero).  No change to
4057    any formatting parameters -- filling, indentation, html escapes,
4058    etc., are not reset.  Always returned in new memory.  */
4059 
4060 char *
full_expansion(char * str,int implicit_code)4061 full_expansion (char *str, int implicit_code)
4062 {
4063   int length;
4064   char *result;
4065 
4066   /* Inhibit any real output.  */
4067   int start = output_paragraph_offset;
4068   int saved_paragraph_is_open = paragraph_is_open;
4069   int saved_output_column = output_column;
4070 
4071   /* More output state to save.  */
4072   int saved_meta_pos = meta_char_pos;
4073   int saved_last_char = last_inserted_character;
4074   int saved_last_nl = last_char_was_newline;
4075 
4076   /* If we are called in the middle of processing a command, we need
4077      to dup and save the global variable `command' (which holds the
4078      name of this command), since the recursive reader loop will free
4079      it from under our feet if it finds any macros in STR.  */
4080   char *saved_command = command ? xstrdup (command) : NULL;
4081 
4082   inhibit_output_flushing ();
4083   paragraph_is_open = 1;
4084   if (strlen (str) > (implicit_code
4085                       ? EXECUTE_STRING_MAX - 1 - sizeof("@code{}")
4086                       : EXECUTE_STRING_MAX - 1))
4087     line_error (_("`%.40s...' is too long for expansion; not expanded"), str);
4088   else
4089     execute_string (implicit_code ? "@code{%s}" : "%s", str);
4090   uninhibit_output_flushing ();
4091 
4092   /* Copy the expansion from the buffer.  */
4093   length = output_paragraph_offset - start;
4094   result = xmalloc (1 + length);
4095   memcpy (result, (char *) (output_paragraph + start), length);
4096   result[length] = 0;
4097 
4098   /* Pretend it never happened.  */
4099   free_and_clear (&command);
4100   command = saved_command;
4101 
4102   output_paragraph_offset = start;
4103   paragraph_is_open = saved_paragraph_is_open;
4104   output_column = saved_output_column;
4105 
4106   meta_char_pos = saved_meta_pos;
4107   last_inserted_character = saved_last_char;
4108   last_char_was_newline = saved_last_nl;
4109 
4110   return result;
4111 }
4112 
4113 
4114 /* Return text (info) expansion of STR no matter what the current output
4115    format is.  */
4116 
4117 char *
text_expansion(char * str)4118 text_expansion (char *str)
4119 {
4120   char *ret;
4121   int save_html = html;
4122   int save_xml = xml;
4123   int save_docbook = docbook;
4124 
4125   html = 0;
4126   xml = 0;
4127   docbook = 0;
4128   ret = expansion (str, 0);
4129   html = save_html;
4130   xml = save_xml;
4131   docbook = save_docbook;
4132 
4133   return ret;
4134 }
4135 
4136 
4137 /* Set the paragraph indentation variable to the value specified in STRING.
4138    Values can be:
4139      `asis': Don't change existing indentation.
4140      `none': Remove existing indentation.
4141         NUM: Indent NUM spaces at the starts of paragraphs.
4142              If NUM is zero, we assume `none'.
4143    Returns 0 if successful, or nonzero if STRING isn't one of the above. */
4144 int
set_paragraph_indent(char * string)4145 set_paragraph_indent (char *string)
4146 {
4147   if (strcmp (string, "asis") == 0 || strcmp (string, _("asis")) == 0)
4148     paragraph_start_indent = 0;
4149   else if (strcmp (string, "none") == 0 || strcmp (string, _("none")) == 0)
4150     paragraph_start_indent = -1;
4151   else
4152     {
4153       if (sscanf (string, "%d", &paragraph_start_indent) != 1)
4154         return -1;
4155       else
4156         {
4157           if (paragraph_start_indent == 0)
4158             paragraph_start_indent = -1;
4159         }
4160     }
4161   return 0;
4162 }
4163