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