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