Back to index

tetex-bin  3.0
makeinfo.c
Go to the documentation of this file.
00001 /* makeinfo -- convert Texinfo source into other formats.
00002    $Id: makeinfo.c,v 1.74 2004/12/19 17:15:42 karl Exp $
00003 
00004    Copyright (C) 1987, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
00005    2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
00006 
00007    This program is free software; you can redistribute it and/or modify
00008    it under the terms of the GNU General Public License as published by
00009    the Free Software Foundation; either version 2, or (at your option)
00010    any later version.
00011 
00012    This program is distributed in the hope that it will be useful,
00013    but WITHOUT ANY WARRANTY; without even the implied warranty of
00014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015    GNU General Public License for more details.
00016 
00017    You should have received a copy of the GNU General Public License
00018    along with this program; if not, write to the Free Software
00019    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00020 
00021    Original author of makeinfo: Brian Fox (bfox@ai.mit.edu).  */
00022 
00023 #include "system.h"
00024 #include "getopt.h"
00025 
00026 #define COMPILING_MAKEINFO
00027 #include "makeinfo.h"
00028 #include "cmds.h"
00029 #include "files.h"
00030 #include "float.h"
00031 #include "footnote.h"
00032 #include "html.h"
00033 #include "index.h"
00034 #include "insertion.h"
00035 #include "lang.h"
00036 #include "macro.h"
00037 #include "node.h"
00038 #include "sectioning.h"
00039 #include "toc.h"
00040 #include "xml.h"
00041 
00042 /* You can change some of the behavior of Makeinfo by changing the
00043    following defines: */
00044 
00045 /* Define INDENT_PARAGRAPHS_IN_TABLE if you want the paragraphs which
00046    appear within an @table, @ftable, or @itemize environment to have
00047    standard paragraph indentation.  Without this, such paragraphs have
00048    no starting indentation. */
00049 /* #define INDENT_PARAGRAPHS_IN_TABLE */
00050 
00051 /* Define PARAGRAPH_START_INDENT to be the amount of indentation that
00052    the first lines of paragraphs receive by default, where no other
00053    value has been specified.  Users can change this value on the command
00054    line, with the --paragraph-indent option, or within the texinfo file,
00055    with the @paragraphindent command. */
00056 #define PARAGRAPH_START_INDENT 3
00057 
00058 /* Define DEFAULT_PARAGRAPH_SPACING as the number of blank lines that you
00059    wish to appear between paragraphs.  A value of 1 creates a single blank
00060    line between paragraphs.  Paragraphs are defined by 2 or more consecutive
00061    newlines in the input file (i.e., one or more blank lines). */
00062 #define DEFAULT_PARAGRAPH_SPACING 1
00063 
00064 /* Global variables.  */
00065 
00066 /* The output file name. */
00067 char *output_filename = NULL;
00068 
00069 /* Name of the output file that the user elected to pass on the command line.
00070    Such a name overrides any name found with the @setfilename command. */
00071 char *command_output_filename = NULL;
00072 static char *save_command_output_filename = NULL;
00073 
00074 #define INITIAL_PARAGRAPH_SPACE 5000
00075 int paragraph_buffer_len = INITIAL_PARAGRAPH_SPACE;
00076 
00077 /* The amount of indentation to add at the starts of paragraphs.
00078    0 means don't change existing indentation at paragraph starts.
00079    > 0 is amount to indent new paragraphs by.
00080    < 0 means indent to column zero by removing indentation if necessary.
00081 
00082    This is normally zero, but some people prefer paragraph starts to be
00083    somewhat more indented than paragraph bodies.  A pretty value for
00084    this is 3. */
00085 int paragraph_start_indent = PARAGRAPH_START_INDENT;
00086 
00087 /* Indentation that is pending insertion.  We have this for hacking lines
00088    which look blank, but contain whitespace.  We want to treat those as
00089    blank lines. */
00090 int pending_indent = 0;
00091 
00092 /* The index in our internal command table of the currently
00093    executing command. */
00094 int command_index;
00095 
00096 /* A search string which is used to find the first @setfilename. */
00097 char setfilename_search[] =
00098   { COMMAND_PREFIX,
00099       's', 'e', 't', 'f', 'i', 'l', 'e', 'n', 'a', 'm', 'e', 0 };
00100 
00101 /* Values for calling handle_variable_internal (). */
00102 #define SET     1
00103 #define CLEAR   2
00104 #define IFSET   3
00105 #define IFCLEAR 4
00106 
00107 /* Flags controlling the operation of the program. */
00108 
00109 /* Default is to remove output if there were errors.  */
00110 int force = 0;
00111 
00112 /* Default is to notify users of bad choices. */
00113 int print_warnings = 1;
00114 
00115 /* Number of errors that we tolerate on a given fileset. */
00116 int max_error_level = 100;
00117 
00118 /* The actual last inserted character.  Note that this may be something
00119    other than NEWLINE even if last_char_was_newline is 1. */
00120 int last_inserted_character = 0;
00121 
00122 /* Nonzero means that a newline character has already been
00123    inserted, so close_paragraph () should insert one less. */
00124 int line_already_broken = 0;
00125 
00126 /* When nonzero we have finished an insertion (see end_insertion ()) and we
00127    want to ignore false continued paragraph closings. */
00128 int insertion_paragraph_closed = 0;
00129 
00130 /* Nonzero means attempt to make all of the lines have fill_column width. */
00131 int do_justification = 0;
00132 
00133 /* Nonzero means don't replace whitespace with &nbsp; in HTML mode.  */
00134 int in_html_elt = 0;
00135 
00136 /* Nonzero means we are inserting a block level HTML element that must not be
00137    enclosed in a <p>, such as <ul>, <ol> and <h?>.  */
00138 int in_html_block_level_elt = 0;
00139 
00140 /* True when expanding a macro definition.  */
00141 static int executing_macro = 0;
00142 
00143 /* True when we are inside a <li> block of a menu.  */
00144 static int in_menu_item = 0;
00145 
00146 typedef struct brace_element
00147 {
00148   struct brace_element *next;
00149   COMMAND_FUNCTION *proc;
00150   char *command;
00151   int pos, line;
00152   int in_fixed_width_font;
00153 } BRACE_ELEMENT;
00154 
00155 BRACE_ELEMENT *brace_stack = NULL;
00156 
00157 static void convert_from_file (char *name);
00158 static void convert_from_loaded_file (char *name);
00159 static void convert_from_stream (FILE *stream, char *name);
00160 static void do_flush_right_indentation (void);
00161 static void handle_variable (int action);
00162 static void handle_variable_internal (int action, char *name);
00163 static void init_brace_stack (void);
00164 static void init_internals (void);
00165 static void pop_and_call_brace (void);
00166 static void remember_brace (COMMAND_FUNCTION (*proc));
00167 static int end_of_sentence_p (void);
00168 
00169 void maybe_update_execution_strings (char **text, unsigned int new_len);
00170 
00171 /* Error handling.  */
00172 
00173 /* Number of errors encountered. */
00174 int errors_printed = 0;
00175 
00176 /* Remember that an error has been printed.  If more than
00177    max_error_level have been printed, then exit the program. */
00178 static void
00179 remember_error (void)
00180 {
00181   errors_printed++;
00182   if (max_error_level && (errors_printed > max_error_level))
00183     {
00184       fprintf (stderr, _("Too many errors!  Gave up.\n"));
00185       flush_file_stack ();
00186       if (errors_printed - max_error_level < 2)
00187        cm_bye ();
00188       xexit (1);
00189     }
00190 }
00191 
00192 /* Print the last error gotten from the file system. */
00193 int
00194 fs_error (char *filename)
00195 {
00196   remember_error ();
00197   perror (filename);
00198   return 0;
00199 }
00200 
00201 /* Print an error message, and return false. */
00202 void
00203 #if defined (VA_FPRINTF) && __STDC__
00204 error (const char *format, ...)
00205 #else
00206 error (format, va_alist)
00207      const char *format;
00208      va_dcl
00209 #endif
00210 {
00211 #ifdef VA_FPRINTF
00212   va_list ap;
00213 #endif
00214 
00215   remember_error ();
00216 
00217   VA_START (ap, format);
00218 #ifdef VA_FPRINTF
00219   VA_FPRINTF (stderr, format, ap);
00220 #else
00221   fprintf (stderr, format, a1, a2, a3, a4, a5, a6, a7, a8);
00222 #endif /* not VA_FPRINTF */
00223   va_end (ap);
00224 
00225   putc ('\n', stderr);
00226 }
00227 
00228 /* Just like error (), but print the input file and line number as well. */
00229 void
00230 #if defined (VA_FPRINTF) && __STDC__
00231 file_line_error (char *infile, int lno, const char *format, ...)
00232 #else
00233 file_line_error (infile, lno, format, va_alist)
00234    char *infile;
00235    int lno;
00236    const char *format;
00237    va_dcl
00238 #endif
00239 {
00240 #ifdef VA_FPRINTF
00241   va_list ap;
00242 #endif
00243 
00244   remember_error ();
00245   fprintf (stderr, "%s:%d: ", infile, lno);
00246 
00247   VA_START (ap, format);
00248 #ifdef VA_FPRINTF
00249   VA_FPRINTF (stderr, format, ap);
00250 #else
00251   fprintf (stderr, format, a1, a2, a3, a4, a5, a6, a7, a8);
00252 #endif /* not VA_FPRINTF */
00253   va_end (ap);
00254 
00255   fprintf (stderr, ".\n");
00256 }
00257 
00258 /* Just like file_line_error (), but take the input file and the line
00259    number from global variables. */
00260 void
00261 #if defined (VA_FPRINTF) && __STDC__
00262 line_error (const char *format, ...)
00263 #else
00264 line_error (format, va_alist)
00265    const char *format;
00266    va_dcl
00267 #endif
00268 {
00269 #ifdef VA_FPRINTF
00270   va_list ap;
00271 #endif
00272 
00273   remember_error ();
00274   fprintf (stderr, "%s:%d: ", input_filename, line_number);
00275 
00276   VA_START (ap, format);
00277 #ifdef VA_FPRINTF
00278   VA_FPRINTF (stderr, format, ap);
00279 #else
00280   fprintf (stderr, format, a1, a2, a3, a4, a5, a6, a7, a8);
00281 #endif /* not VA_FPRINTF */
00282   va_end (ap);
00283 
00284   fprintf (stderr, ".\n");
00285 }
00286 
00287 void
00288 #if defined (VA_FPRINTF) && __STDC__
00289 warning (const char *format, ...)
00290 #else
00291 warning (format, va_alist)
00292      const char *format;
00293      va_dcl
00294 #endif
00295 {
00296 #ifdef VA_FPRINTF
00297   va_list ap;
00298 #endif
00299 
00300   if (print_warnings)
00301     {
00302       fprintf (stderr, _("%s:%d: warning: "), input_filename, line_number);
00303 
00304       VA_START (ap, format);
00305 #ifdef VA_FPRINTF
00306       VA_FPRINTF (stderr, format, ap);
00307 #else
00308       fprintf (stderr, format, a1, a2, a3, a4, a5, a6, a7, a8);
00309 #endif /* not VA_FPRINTF */
00310       va_end (ap);
00311 
00312       fprintf (stderr, ".\n");
00313     }
00314 }
00315 
00316 
00317 /* The other side of a malformed expression. */
00318 static void
00319 misplaced_brace (void)
00320 {
00321   line_error (_("Misplaced %c"), '}');
00322 }
00323 
00324 /* Main.  */
00325 
00326 /* Display the version info of this invocation of Makeinfo. */
00327 static void
00328 print_version_info (void)
00329 {
00330   printf ("makeinfo (GNU %s) %s\n", PACKAGE, VERSION);
00331 }
00332 
00333 /* If EXIT_VALUE is zero, print the full usage message to stdout.
00334    Otherwise, just say to use --help for more info.
00335    Then exit with EXIT_VALUE. */
00336 static void
00337 usage (int exit_value)
00338 {
00339   if (exit_value != 0)
00340     fprintf (stderr, _("Try `%s --help' for more information.\n"), progname);
00341   else
00342   {
00343     printf (_("Usage: %s [OPTION]... TEXINFO-FILE...\n"), progname);
00344     puts ("");
00345 
00346     puts (_("\
00347 Translate Texinfo source documentation to various other formats, by default\n\
00348 Info files suitable for reading online with Emacs or standalone GNU Info.\n"));
00349 
00350     printf (_("\
00351 General options:\n\
00352       --error-limit=NUM       quit after NUM errors (default %d).\n\
00353       --force                 preserve output even if errors.\n\
00354       --help                  display this help and exit.\n\
00355       --no-validate           suppress node cross-reference validation.\n\
00356       --no-warn               suppress warnings (but not errors).\n\
00357       --reference-limit=NUM   warn about at most NUM references (default %d).\n\
00358   -v, --verbose               explain what is being done.\n\
00359       --version               display version information and exit.\n"),
00360             max_error_level, reference_warning_limit);
00361     puts ("");
00362 
00363      /* xgettext: no-wrap */
00364     puts (_("\
00365 Output format selection (default is to produce Info):\n\
00366       --docbook             output Docbook XML rather than Info.\n\
00367       --html                output HTML rather than Info.\n\
00368       --xml                 output Texinfo XML rather than Info.\n\
00369       --plaintext           output plain text rather than Info.\n\
00370 "));
00371 
00372     puts (_("\
00373 General output options:\n\
00374   -E, --macro-expand FILE   output macro-expanded source to FILE.\n\
00375                             ignoring any @setfilename.\n\
00376       --no-headers          suppress node separators, Node: lines, and menus\n\
00377                               from Info output (thus producing plain text)\n\
00378                               or from HTML (thus producing shorter output);\n\
00379                               also, write to standard output by default.\n\
00380       --no-split            suppress splitting of Info or HTML output,\n\
00381                             generate only one output file.\n\
00382       --number-sections     output chapter and sectioning numbers.\n\
00383   -o, --output=FILE         output to FILE (directory if split HTML),\n\
00384 "));
00385 
00386     printf (_("\
00387 Options for Info and plain text:\n\
00388       --enable-encoding       output accented and special characters in\n\
00389                                 Info output based on @documentencoding.\n\
00390       --fill-column=NUM       break Info lines at NUM characters (default %d).\n\
00391       --footnote-style=STYLE  output footnotes in Info according to STYLE:\n\
00392                                 `separate' to put them in their own node;\n\
00393                                 `end' to put them at the end of the node\n\
00394                                   in which they are defined (default).\n\
00395       --paragraph-indent=VAL  indent Info paragraphs by VAL spaces (default %d).\n\
00396                                 If VAL is `none', do not indent; if VAL is\n\
00397                                 `asis', preserve existing indentation.\n\
00398       --split-size=NUM        split Info files at size NUM (default %d).\n"),
00399              fill_column, paragraph_start_indent,
00400              DEFAULT_SPLIT_SIZE);
00401     puts ("");
00402 
00403     puts (_("\
00404 Options for HTML:\n\
00405       --css-include=FILE        include FILE in HTML <style> output;\n\
00406                                   read stdin if FILE is -.\n\
00407 "));
00408 
00409     printf (_("\
00410 Options for XML and Docbook:\n\
00411       --output-indent=VAL       indent XML elements by VAL spaces (default %d).\n\
00412                                   If VAL is 0, ignorable whitespace is dropped.\n\
00413 "), xml_indentation_increment);
00414     puts ("");
00415 
00416     puts (_("\
00417 Input file options:\n\
00418       --commands-in-node-names  allow @ commands in node names.\n\
00419   -D VAR                        define the variable VAR, as with @set.\n\
00420   -I DIR                        append DIR to the @include search path.\n\
00421   -P DIR                        prepend DIR to the @include search path.\n\
00422   -U VAR                        undefine the variable VAR, as with @clear.\n\
00423 "));
00424 
00425     puts (_("\
00426 Conditional processing in input:\n\
00427   --ifdocbook       process @ifdocbook and @docbook even if\n\
00428                       not generating Docbook.\n\
00429   --ifhtml          process @ifhtml and @html even if not generating HTML.\n\
00430   --ifinfo          process @ifinfo even if not generating Info.\n\
00431   --ifplaintext     process @ifplaintext even if not generating plain text.\n\
00432   --iftex           process @iftex and @tex; implies --no-split.\n\
00433   --ifxml           process @ifxml and @xml.\n\
00434   --no-ifdocbook    do not process @ifdocbook and @docbook text.\n\
00435   --no-ifhtml       do not process @ifhtml and @html text.\n\
00436   --no-ifinfo       do not process @ifinfo text.\n\
00437   --no-ifplaintext  do not process @ifplaintext text.\n\
00438   --no-iftex        do not process @iftex and @tex text.\n\
00439   --no-ifxml        do not process @ifxml and @xml text.\n\
00440 \n\
00441   Also, for the --no-ifFORMAT options, do process @ifnotFORMAT text.\n\
00442 "));
00443 
00444     puts (_("\
00445   The defaults for the @if... conditionals depend on the output format:\n\
00446   if generating HTML, --ifhtml is on and the others are off;\n\
00447   if generating Info, --ifinfo is on and the others are off;\n\
00448   if generating plain text, --ifplaintext is on and the others are off;\n\
00449   if generating XML, --ifxml is on and the others are off.\n\
00450 "));
00451 
00452     fputs (_("\
00453 Examples:\n\
00454   makeinfo foo.texi                     write Info to foo's @setfilename\n\
00455   makeinfo --html foo.texi              write HTML to @setfilename\n\
00456   makeinfo --xml foo.texi               write Texinfo XML to @setfilename\n\
00457   makeinfo --docbook foo.texi           write DocBook XML to @setfilename\n\
00458   makeinfo --no-headers foo.texi        write plain text to standard output\n\
00459 \n\
00460   makeinfo --html --no-headers foo.texi write html without node lines, menus\n\
00461   makeinfo --number-sections foo.texi   write Info with numbered sections\n\
00462   makeinfo --no-split foo.texi          write one Info file however big\n\
00463 "), stdout);
00464 
00465     puts (_("\n\
00466 Email bug reports to bug-texinfo@gnu.org,\n\
00467 general questions and discussion to help-texinfo@gnu.org.\n\
00468 Texinfo home page: http://www.gnu.org/software/texinfo/"));
00469 
00470   } /* end of full help */
00471 
00472   xexit (exit_value);
00473 }
00474 
00475 struct option long_options[] =
00476 {
00477   { "commands-in-node-names", 0, &expensive_validation, 1 },
00478   { "css-include", 1, 0, 'C' },
00479   { "docbook", 0, 0, 'd' },
00480   { "enable-encoding", 0, &enable_encoding, 1 },
00481   { "error-limit", 1, 0, 'e' },
00482   { "fill-column", 1, 0, 'f' },
00483   { "footnote-style", 1, 0, 's' },
00484   { "force", 0, &force, 1 },
00485   { "help", 0, 0, 'h' },
00486   { "html", 0, 0, 'w' },
00487   { "ifdocbook", 0, &process_docbook, 1 },
00488   { "ifhtml", 0, &process_html, 1 },
00489   { "ifinfo", 0, &process_info, 1 },
00490   { "ifplaintext", 0, &process_plaintext, 1 },
00491   { "iftex", 0, &process_tex, 1 },
00492   { "ifxml", 0, &process_xml, 1 },
00493   { "macro-expand", 1, 0, 'E' },
00494   { "no-headers", 0, &no_headers, 1 },
00495   { "no-ifdocbook", 0, &process_docbook, 0 },
00496   { "no-ifhtml", 0, &process_html, 0 },
00497   { "no-ifinfo", 0, &process_info, 0 },
00498   { "no-ifplaintext", 0, &process_plaintext, 0 },
00499   { "no-iftex", 0, &process_tex, 0 },
00500   { "no-ifxml", 0, &process_xml, 0 },
00501   { "no-number-footnotes", 0, &number_footnotes, 0 },
00502   { "no-number-sections", 0, &number_sections, 0 },
00503   { "no-pointer-validate", 0, &validating, 0 },
00504   { "no-split", 0, &splitting, 0 },
00505   { "no-validate", 0, &validating, 0 },
00506   { "no-warn", 0, &print_warnings, 0 },
00507   { "number-footnotes", 0, &number_footnotes, 1 },
00508   { "number-sections", 0, &number_sections, 1 },
00509   { "output", 1, 0, 'o' },
00510   { "output-indent", 1, 0, 'i' },
00511   { "paragraph-indent", 1, 0, 'p' },
00512   { "plaintext", 0, 0, 't' },
00513   { "reference-limit", 1, 0, 'r' },
00514   { "split-size", 1, 0, 'S'},
00515   { "verbose", 0, &verbose_mode, 1 },
00516   { "version", 0, 0, 'V' },
00517   { "xml", 0, 0, 'x' },
00518   {NULL, 0, NULL, 0}
00519 };
00520 
00521 /* We use handle_variable_internal for -D and -U, and it depends on
00522    execute_string, which depends on input_filename, which is not defined
00523    while we are handling options. :-\  So we save these defines in this
00524    struct, and handle them later.  */
00525 typedef struct command_line_define
00526 {
00527   struct command_line_define *next;
00528   int action;
00529   char *define;
00530 } COMMAND_LINE_DEFINE;
00531 
00532 static COMMAND_LINE_DEFINE *command_line_defines = NULL;
00533 
00534 /* For each file mentioned in the command line, process it, turning
00535    Texinfo commands into wonderfully formatted output text. */
00536 int
00537 main (int argc, char **argv)
00538 {
00539   int c, ind;
00540   int reading_from_stdin = 0;
00541 
00542 #ifdef HAVE_SETLOCALE
00543   /* Do not use LC_ALL, because LC_NUMERIC screws up the scanf parsing
00544      of the argument to @multicolumn.  */
00545   setlocale (LC_TIME, "");
00546 #ifdef LC_MESSAGES /* ultrix */
00547   setlocale (LC_MESSAGES, "");
00548 #endif
00549   setlocale (LC_CTYPE, "");
00550   setlocale (LC_COLLATE, "");
00551 #endif
00552 
00553 #ifdef ENABLE_NLS
00554   /* Set the text message domain.  */
00555   bindtextdomain (PACKAGE, LOCALEDIR);
00556   textdomain (PACKAGE);
00557 #endif
00558 
00559   /* If TEXINFO_OUTPUT_FORMAT envvar is set, use it to set default output.
00560      Can be overridden with one of the output options.  */
00561   if (getenv ("TEXINFO_OUTPUT_FORMAT") != NULL)
00562     {
00563       if (STREQ (getenv ("TEXINFO_OUTPUT_FORMAT"), "docbook"))
00564         {
00565           splitting = 0;
00566           html = 0;
00567           docbook = 1;
00568           xml = 1;
00569           process_docbook = 1;
00570         }
00571       else if (STREQ (getenv ("TEXINFO_OUTPUT_FORMAT"), "html"))
00572         {
00573           html = 1;
00574           docbook = 0;
00575           xml = 0;
00576           process_html = 1;
00577         }
00578       else if (STREQ (getenv ("TEXINFO_OUTPUT_FORMAT"), "info"))
00579         {
00580           html = 0;
00581           docbook = 0;
00582           xml = 0;
00583         }
00584       else if (STREQ (getenv ("TEXINFO_OUTPUT_FORMAT"), "plaintext"))
00585         {
00586           splitting = 0;
00587           no_headers = 1;
00588           html = 0;
00589           docbook = 0;
00590           xml = 0;
00591           process_plaintext = 1;
00592         }
00593       else if (STREQ (getenv ("TEXINFO_OUTPUT_FORMAT"), "xml"))
00594         {
00595           splitting = 0;
00596           html = 0;
00597           docbook = 0;
00598           xml = 1;
00599           process_xml = 1;
00600         }
00601       else
00602         fprintf (stderr,
00603             _("%s: Ignoring unrecognized TEXINFO_OUTPUT_FORMAT value `%s'.\n"),
00604                  progname, getenv ("TEXINFO_OUTPUT_FORMAT"));
00605     }
00606 
00607   /* Parse argument flags from the input line. */
00608   while ((c = getopt_long (argc, argv, "D:de:E:f:hI:i:o:p:P:r:s:t:U:vV:wx",
00609                            long_options, &ind)) != EOF)
00610     {
00611       if (c == 0 && long_options[ind].flag == 0)
00612         c = long_options[ind].val;
00613 
00614       switch (c)
00615         {
00616         case 'C':  /* --css-include */
00617           css_include = xstrdup (optarg);
00618           break;
00619 
00620         case 'D':
00621         case 'U':
00622           /* User specified variable to set or clear. */
00623           if (xml && !docbook)
00624             {
00625               COMMAND_LINE_DEFINE *new = xmalloc (sizeof (COMMAND_LINE_DEFINE));
00626               new->action = (c == 'D') ? SET : CLEAR;
00627               new->define = xstrdup (optarg);
00628               new->next = command_line_defines;
00629               command_line_defines = new;
00630             }
00631           else
00632             handle_variable_internal ((c == 'D' ? SET : CLEAR), optarg);
00633           break;
00634 
00635         case 'd': /* --docbook */
00636           splitting = 0;
00637           xml = 1;
00638           docbook = 1;
00639           html = 0;
00640          process_docbook = 1;
00641           break;
00642 
00643         case 'e': /* --error-limit */
00644           if (sscanf (optarg, "%d", &max_error_level) != 1)
00645             {
00646               fprintf (stderr,
00647                       _("%s: %s arg must be numeric, not `%s'.\n"),
00648                       progname, "--error-limit", optarg);
00649               usage (1);
00650             }
00651           break;
00652 
00653         case 'E': /* --macro-expand */
00654           if (!macro_expansion_output_stream)
00655             {
00656               macro_expansion_filename = optarg;
00657               macro_expansion_output_stream
00658                 = strcmp (optarg, "-") == 0 ? stdout : fopen (optarg, "w");
00659               if (!macro_expansion_output_stream)
00660                 error (_("%s: could not open macro expansion output `%s'"),
00661                        progname, optarg);
00662             }
00663           else
00664             fprintf (stderr,
00665                      _("%s: ignoring second macro expansion output `%s'.\n"),
00666                      progname, optarg);
00667           break;
00668 
00669         case 'f': /* --fill-column */
00670           if (sscanf (optarg, "%d", &fill_column) != 1)
00671             {
00672               fprintf (stderr,
00673                        _("%s: %s arg must be numeric, not `%s'.\n"),
00674                        progname, "--fill-column", optarg);
00675               usage (1);
00676             }
00677           break;
00678 
00679         case 'h': /* --help */
00680           usage (0);
00681           break;
00682 
00683         case 'I':
00684           /* Append user-specified dir to include file path. */
00685           append_to_include_path (optarg);
00686           break;
00687 
00688         case 'i':
00689           if (sscanf (optarg, "%d", &xml_indentation_increment) != 1)
00690             {
00691               fprintf (stderr,
00692                      _("%s: %s arg must be numeric, not `%s'.\n"),
00693                      progname, "--output-indent", optarg);
00694               usage (1);
00695             }
00696           break;
00697 
00698         case 'o': /* --output */
00699           command_output_filename = xstrdup (optarg);
00700           save_command_output_filename = command_output_filename;
00701           break;
00702 
00703         case 'p': /* --paragraph-indent */
00704           if (set_paragraph_indent (optarg) < 0)
00705             {
00706               fprintf (stderr,
00707    _("%s: --paragraph-indent arg must be numeric/`none'/`asis', not `%s'.\n"),
00708                        progname, optarg);
00709               usage (1);
00710             }
00711           break;
00712 
00713         case 'P':
00714           /* Prepend user-specified include dir to include path. */
00715           prepend_to_include_path (optarg);
00716           break;
00717 
00718         case 'r': /* --reference-limit */
00719           if (sscanf (optarg, "%d", &reference_warning_limit) != 1)
00720             {
00721               fprintf (stderr,
00722                      _("%s: %s arg must be numeric, not `%s'.\n"),
00723                      progname, "--reference-limit", optarg);
00724               usage (1);
00725             }
00726           break;
00727 
00728         case 's': /* --footnote-style */
00729           if (set_footnote_style (optarg) < 0)
00730             {
00731               fprintf (stderr,
00732         _("%s: --footnote-style arg must be `separate' or `end', not `%s'.\n"),
00733                        progname, optarg);
00734               usage (1);
00735             }
00736           footnote_style_preset = 1;
00737           break;
00738 
00739         case 'S': /* --split-size */
00740           if (sscanf (optarg, "%d", &split_size) != 1)
00741             {
00742               fprintf (stderr,
00743                      _("%s: %s arg must be numeric, not `%s'.\n"),
00744                      progname, "--split-size", optarg);
00745               usage (1);
00746             }
00747           break;
00748 
00749         case 't': /* --plaintext */
00750           splitting = 0;
00751           no_headers = 1;
00752           html = 0;
00753           docbook = 0;
00754           xml = 0;
00755           process_plaintext = 1;
00756           break;
00757 
00758         case 'v':
00759           verbose_mode++;
00760           break;
00761 
00762         case 'V': /* --version */
00763           print_version_info ();
00764           puts ("");
00765           puts ("Copyright (C) 2004 Free Software Foundation, Inc.");
00766           printf (_("There is NO warranty.  You may redistribute this software\n\
00767 under the terms of the GNU General Public License.\n\
00768 For more information about these matters, see the files named COPYING.\n"));
00769           xexit (0);
00770           break;
00771 
00772         case 'w': /* --html */
00773           xml = 0;
00774           docbook = 0;
00775           html = 1;
00776           process_html = 1;
00777           break;
00778 
00779         case 'x': /* --xml */
00780           splitting = 0;
00781           html = 0;
00782           docbook = 0;
00783           xml = 1;
00784           process_xml = 1;
00785           break;
00786 
00787         case '?':
00788           usage (1);
00789           break;
00790         }
00791     }
00792 
00793   if (macro_expansion_output_stream)
00794     validating = 0;
00795 
00796   if (!validating)
00797     expensive_validation = 0;
00798 
00799   if (optind == argc)
00800     {
00801       /* Check to see if input is a file.  If so, process that. */
00802       if (!isatty (fileno (stdin)))
00803         reading_from_stdin = 1;
00804       else
00805         {
00806           fprintf (stderr, _("%s: missing file argument.\n"), progname);
00807           usage (1);
00808         }
00809     }
00810 
00811   if (no_headers)
00812     {
00813       /* If the user did not specify an output file, use stdout. */
00814       if (!command_output_filename)
00815         command_output_filename = xstrdup ("-");
00816 
00817       if (html && splitting && !STREQ (command_output_filename, "-"))
00818         { /* --no-headers --no-split --html indicates confusion. */
00819           fprintf (stderr,
00820                   "%s: can't split --html output to `%s' with --no-headers.\n",
00821                    progname, command_output_filename);
00822           usage (1);
00823         }
00824 
00825       /* --no-headers implies --no-split.  */
00826       splitting = 0;
00827     }
00828 
00829   if (process_info == -1)
00830     { /* no explicit --[no-]ifinfo option, so we'll do @ifinfo
00831          if we're generating info or (for compatibility) plain text.  */
00832       process_info = !html && !xml;
00833     }
00834 
00835   if (process_plaintext == -1)
00836     { /* no explicit --[no-]ifplaintext option, so we'll do @ifplaintext
00837          if we're generating plain text.  */
00838       process_plaintext = no_headers && !html && !xml;
00839     }
00840 
00841   if (verbose_mode)
00842     print_version_info ();
00843 
00844   /* Remaining arguments are file names of texinfo files.
00845      Convert them, one by one. */
00846   if (!reading_from_stdin)
00847     {
00848       while (optind != argc)
00849         convert_from_file (argv[optind++]);
00850     }
00851   else
00852     convert_from_stream (stdin, "stdin");
00853 
00854   xexit (errors_printed ? 2 : 0);
00855   return 0; /* Avoid bogus warnings.  */
00856 }
00857 
00858 /* Hacking tokens and strings.  */
00859 
00860 /* Return the next token as a string pointer.  We cons the string.  This
00861    `token' means simply a command name.  */
00862 
00863 /* = is so @alias works.  ^ and _ are so macros can be used in math mode
00864    without a space following.  Possibly we should simply allow alpha, to
00865    be compatible with TeX.  */
00866 #define COMMAND_CHAR(c) (!cr_or_whitespace(c) \
00867                          && (c) != '{' \
00868                          && (c) != '}' \
00869                          && (c) != '=' \
00870                          && (c) != '_' \
00871                          && (c) != '^' \
00872                          )
00873 
00874 static char *
00875 read_token (void)
00876 {
00877   int i, character;
00878   char *result;
00879 
00880   /* If the first character to be read is self-delimiting, then that
00881      is the command itself. */
00882   character = curchar ();
00883   if (self_delimiting (character))
00884     {
00885       input_text_offset++;
00886 
00887       if (character == '\n')
00888         line_number++;
00889 
00890       result = xstrdup (" ");
00891       *result = character;
00892       return result;
00893     }
00894 
00895   for (i = 0; ((input_text_offset != input_text_length)
00896                && (character = curchar ())
00897                && COMMAND_CHAR (character));
00898        i++, input_text_offset++);
00899   result = xmalloc (i + 1);
00900   memcpy (result, &input_text[input_text_offset - i], i);
00901   result[i] = 0;
00902   return result;
00903 }
00904 
00905 /* Return nonzero if CHARACTER is self-delimiting. */
00906 int
00907 self_delimiting (int character)
00908 {
00909   /* @; and @\ are not Texinfo commands, but they are listed here
00910      anyway.  I don't know why.  --karl, 10aug96.  */
00911   return strchr ("~{|}`^\\@?=;:./-,*\'\" !\n\t", character) != NULL;
00912 }
00913 
00914 /* Clear whitespace from the front and end of string. */
00915 void
00916 canon_white (char *string)
00917 {
00918   char *p = string;
00919   unsigned len;
00920 
00921   if (!*p)
00922     return;
00923 
00924   do
00925     {
00926       if (!cr_or_whitespace (*p))
00927        break;
00928       ++p;
00929     }
00930   while (*p);
00931 
00932   len = strlen (p);
00933   while (len && cr_or_whitespace (p[len-1]))
00934     --len;
00935 
00936   if (p != string)
00937     memmove (string, p, len);
00938 
00939   string[len] = 0;
00940 }
00941 
00942 /* Bash STRING, replacing all whitespace with just one space. */
00943 void
00944 fix_whitespace (char *string)
00945 {
00946   char *temp = xmalloc (strlen (string) + 1);
00947   int string_index = 0;
00948   int temp_index = 0;
00949   int c;
00950 
00951   canon_white (string);
00952 
00953   while (string[string_index])
00954     {
00955       c = temp[temp_index++] = string[string_index++];
00956 
00957       if (c == ' ' || c == '\n' || c == '\t')
00958         {
00959           temp[temp_index - 1] = ' ';
00960           while ((c = string[string_index]) && (c == ' ' ||
00961                                                 c == '\t' ||
00962                                                 c == '\n'))
00963             string_index++;
00964         }
00965     }
00966   temp[temp_index] = 0;
00967   strcpy (string, temp);
00968   free (temp);
00969 }
00970 
00971 /* Discard text until the desired string is found.  The string is
00972    included in the discarded text. */
00973 void
00974 discard_until (char *string)
00975 {
00976   int temp = search_forward (string, input_text_offset);
00977 
00978   int tt = (temp < 0) ? input_text_length : temp + strlen (string);
00979   int from = input_text_offset;
00980 
00981   /* Find out what line we are on. */
00982   while (from != tt)
00983     if (input_text[from++] == '\n')
00984       line_number++;
00985 
00986   if (temp < 0)
00987     {
00988       /* not found, move current position to end of string */
00989       input_text_offset = input_text_length;
00990       if (strcmp (string, "\n") != 0)
00991         { /* Give a more descriptive feedback, if we are looking for ``@end ''
00992              during macro execution.  That means someone used a multiline
00993              command as an argument to, say, @section ... style commands.  */
00994           char *end_block = xmalloc (8);
00995           sprintf (end_block, "\n%cend ", COMMAND_PREFIX);
00996           if (executing_string && strstr (string, end_block))
00997             line_error (_("Multiline command %c%s used improperly"), 
00998                 COMMAND_PREFIX, command);
00999           else
01000             line_error (_("Expected `%s'"), string);
01001           free (end_block);
01002           return;
01003         }
01004     }
01005   else
01006     /* found, move current position to after the found string */
01007     input_text_offset = temp + strlen (string);
01008 }
01009 
01010 /* Read characters from the file until we are at MATCH.
01011    Place the characters read into STRING.
01012    On exit input_text_offset is after the match string.
01013    Return the offset where the string starts. */
01014 int
01015 get_until (char *match, char **string)
01016 {
01017   int len, current_point, x, new_point, tem;
01018 
01019   current_point = x = input_text_offset;
01020   new_point = search_forward (match, input_text_offset);
01021 
01022   if (new_point < 0)
01023     new_point = input_text_length;
01024   len = new_point - current_point;
01025 
01026   /* Keep track of which line number we are at. */
01027   tem = new_point + (strlen (match) - 1);
01028   while (x != tem)
01029     if (input_text[x++] == '\n')
01030       line_number++;
01031 
01032   *string = xmalloc (len + 1);
01033 
01034   memcpy (*string, &input_text[current_point], len);
01035   (*string)[len] = 0;
01036 
01037   /* Now leave input_text_offset in a consistent state. */
01038   input_text_offset = tem;
01039 
01040   if (input_text_offset > input_text_length)
01041     input_text_offset = input_text_length;
01042 
01043   return new_point;
01044 }
01045 
01046 /* Replace input_text[FROM .. TO] with its expansion.  */
01047 void
01048 replace_with_expansion (int from, int *to)
01049 {
01050   char *xp;
01051   unsigned xp_len, new_len;
01052   char *old_input = input_text;
01053   unsigned raw_len = *to - from;
01054   char *str;
01055 
01056   /* The rest of the code here moves large buffers, so let's
01057      not waste time if the input cannot possibly expand
01058      into anything.  Unfortunately, we cannot avoid expansion
01059      when we see things like @code etc., even if they only
01060      asked for expansion of macros, since any Texinfo command
01061      can be potentially redefined with a macro.  */
01062   if (only_macro_expansion &&
01063       memchr (input_text + from, COMMAND_PREFIX, raw_len) == 0)
01064     return;
01065 
01066   /* Get original string from input.  */
01067   str = xmalloc (raw_len + 1);
01068   memcpy (str, input_text + from, raw_len);
01069   str[raw_len] = 0;
01070 
01071   /* We are going to relocate input_text, so we had better output
01072      pending portion of input_text now, before the pointer changes.  */
01073   if (macro_expansion_output_stream && !executing_string
01074       && !me_inhibit_expansion)
01075     append_to_expansion_output (from);
01076 
01077   /* Expand it.  */
01078   xp = expansion (str, 0);
01079   xp_len = strlen (xp);
01080   free (str);
01081 
01082   /* Plunk the expansion into the middle of `input_text' --
01083      which is terminated by a newline, not a null.  Avoid
01084      expensive move of the rest of the input if the expansion
01085      has the same length as the original string.  */
01086   if (xp_len != raw_len)
01087     {
01088       new_len = from + xp_len + input_text_length - *to + 1;
01089       if (executing_string)
01090         { /* If we are in execute_string, we might need to update
01091              the relevant element in the execution_strings[] array,
01092              since it could have to be relocated from under our
01093              feet.  (input_text is reallocated here as well, if needed.)  */
01094           maybe_update_execution_strings (&input_text, new_len);
01095         }
01096       else if (new_len > input_text_length + 1)
01097         /* Don't bother to realloc if we have enough space.  */
01098         input_text = xrealloc (input_text, new_len);
01099 
01100       memmove (input_text + from + xp_len,
01101                input_text + *to, input_text_length - *to + 1);
01102 
01103       *to += xp_len - raw_len;
01104       /* Since we change input_text_length here, the comparison above
01105          isn't really valid, but it seems the worst that might happen is
01106          an extra xrealloc or two, so let's not worry.  */
01107       input_text_length += xp_len - raw_len;
01108     }
01109   memcpy (input_text + from, xp, xp_len);
01110   free (xp);
01111 
01112   /* Synchronize the macro-expansion pointers with our new input_text.  */
01113   if (input_text != old_input)
01114     forget_itext (old_input);
01115   if (macro_expansion_output_stream && !executing_string)
01116     remember_itext (input_text, from);
01117 }
01118 
01119 /* Read characters from the file until we are at MATCH or end of line.
01120    Place the characters read into STRING.  If EXPAND is nonzero,
01121    expand the text before looking for MATCH for those cases where
01122    MATCH might be produced by some macro.  */
01123 void
01124 get_until_in_line (int expand, char *match, char **string)
01125 {
01126   int real_bottom = input_text_length;
01127   int limit = search_forward ("\n", input_text_offset);
01128   if (limit < 0)
01129     limit = input_text_length;
01130 
01131   /* Replace input_text[input_text_offset .. limit-1] with its expansion.
01132      This allows the node names and menu entries themselves to be
01133      constructed via a macro, as in:
01134         @macro foo{p, q}
01135         Together: \p\ & \q\.
01136         @end macro
01137 
01138         @node @foo{A,B}, next, prev, top
01139 
01140      Otherwise, the `,' separating the macro args A and B is taken as
01141      the node argument separator, so the node name is `@foo{A'.  This
01142      expansion is only necessary on the first call, since we expand the
01143      whole line then.  */
01144   if (expand)
01145     {
01146       replace_with_expansion (input_text_offset, &limit);
01147     }
01148 
01149   real_bottom = input_text_length;
01150   input_text_length = limit;
01151   get_until (match, string);
01152   input_text_length = real_bottom;
01153 }
01154 
01155 void
01156 get_rest_of_line (int expand, char **string)
01157 {
01158   xml_no_para ++;
01159   if (expand)
01160     {
01161       char *tem;
01162 
01163       /* Don't expand non-macros in input, since we want them
01164          intact in the macro-expanded output.  */
01165       only_macro_expansion++;
01166       get_until_in_line (1, "\n", &tem);
01167       only_macro_expansion--;
01168       *string = expansion (tem, 0);
01169       free (tem);
01170     }
01171   else
01172     get_until_in_line (0, "\n", string);
01173 
01174   canon_white (*string);
01175 
01176   if (curchar () == '\n')       /* as opposed to the end of the file... */
01177     {
01178       line_number++;
01179       input_text_offset++;
01180     }
01181   xml_no_para --;
01182 }
01183 
01184 /* Backup the input pointer to the previous character, keeping track
01185    of the current line number. */
01186 void
01187 backup_input_pointer (void)
01188 {
01189   if (input_text_offset)
01190     {
01191       input_text_offset--;
01192       if (curchar () == '\n')
01193         line_number--;
01194     }
01195 }
01196 
01197 /* Read characters from the file until we are at MATCH or closing brace.
01198    Place the characters read into STRING.  */
01199 void
01200 get_until_in_braces (char *match, char **string)
01201 {
01202   char *temp;
01203   int i, brace = 0;
01204   int match_len = strlen (match);
01205 
01206   for (i = input_text_offset; i < input_text_length; i++)
01207     {
01208       if (i < input_text_length - 1 && input_text[i] == '@')
01209         {
01210           i++;                  /* skip commands like @, and @{ */
01211           continue;
01212         }
01213       else if (input_text[i] == '{')
01214         brace++;
01215       else if (input_text[i] == '}')
01216         {
01217           brace--;
01218           /* If looking for a brace, don't stop at the interior brace,
01219              like after "baz" in "@foo{something @bar{baz} more}".  */
01220           if (brace == 0)
01221             continue;
01222         }
01223       else if (input_text[i] == '\n')
01224         line_number++;
01225 
01226       if (brace < 0 ||
01227           (brace == 0 && strncmp (input_text + i, match, match_len) == 0))
01228         break;
01229     }
01230 
01231   match_len = i - input_text_offset;
01232   temp = xmalloc (2 + match_len);
01233   memcpy (temp, input_text + input_text_offset, match_len);
01234   temp[match_len] = 0;
01235   input_text_offset = i;
01236   *string = temp;
01237 }
01238 
01239 
01240 
01241 /* Converting a file.  */
01242 
01243 /* Convert the file named by NAME.  The output is saved on the file
01244    named as the argument to the @setfilename command. */
01245 static char *suffixes[] = {
01246   /* ".txi" is checked first so that on 8+3 DOS filesystems, if they
01247      have "texinfo.txi" and "texinfo.tex" in the same directory, the
01248      former is used rather than the latter, due to file name truncation.  */
01249   ".txi",
01250   ".texinfo",
01251   ".texi",
01252   ".txinfo",
01253   "",
01254   NULL
01255 };
01256 
01257 static void
01258 initialize_conversion (void)
01259 {
01260   init_tag_table ();
01261   init_indices ();
01262   init_internals ();
01263   init_paragraph ();
01264 
01265   /* This is used for splitting the output file and for doing section
01266      headings.  It was previously initialized in `init_paragraph', but its
01267      use there loses with the `init_paragraph' calls done by the
01268      multitable code; the tag indices get reset to zero.  */
01269   output_position = 0;
01270 }
01271 
01272 /* Reverse the chain of structures in LIST.  Output the new head
01273    of the chain.  You should always assign the output value of this
01274    function to something, or you will lose the chain. */
01275 GENERIC_LIST *
01276 reverse_list (GENERIC_LIST *list)
01277 {
01278   GENERIC_LIST *next;
01279   GENERIC_LIST *prev = NULL;
01280 
01281   while (list)
01282     {
01283       next = list->next;
01284       list->next = prev;
01285       prev = list;
01286       list = next;
01287     }
01288   return prev;
01289 }
01290 
01291 /* We read in multiples of 4k, simply because it is a typical pipe size
01292    on unix systems. */
01293 #define READ_BUFFER_GROWTH (4 * 4096)
01294 
01295 /* Convert the Texinfo file coming from the open stream STREAM.  Assume the
01296    source of the stream is named NAME. */
01297 static void
01298 convert_from_stream (FILE *stream, char *name)
01299 {
01300   char *buffer = NULL;
01301   int buffer_offset = 0, buffer_size = 0;
01302 
01303   initialize_conversion ();
01304 
01305   /* Read until the end of the stream.  This isn't strictly correct, since
01306      the texinfo input may end before the stream ends, but it is a quick
01307      working hueristic. */
01308   while (!feof (stream))
01309     {
01310       int count;
01311 
01312       if (buffer_offset + (READ_BUFFER_GROWTH + 1) >= buffer_size)
01313         buffer = (char *)
01314           xrealloc (buffer, (buffer_size += READ_BUFFER_GROWTH));
01315 
01316       count = fread (buffer + buffer_offset, 1, READ_BUFFER_GROWTH, stream);
01317 
01318       if (count < 0)
01319         {
01320           perror (name);
01321           xexit (1);
01322         }
01323 
01324       buffer_offset += count;
01325       if (count == 0)
01326         break;
01327     }
01328 
01329   /* Set the globals to the new file. */
01330   input_text = buffer;
01331   input_text_length = buffer_offset;
01332   input_filename = xstrdup (name);
01333   node_filename = xstrdup (name);
01334   input_text_offset = 0;
01335   line_number = 1;
01336 
01337   /* Not strictly necessary.  This magic prevents read_token () from doing
01338      extra unnecessary work each time it is called (that is a lot of times).
01339      The INPUT_TEXT_LENGTH is one past the actual end of the text. */
01340   input_text[input_text_length] = '\n';
01341 
01342   convert_from_loaded_file (name);
01343 }
01344 
01345 static void
01346 convert_from_file (char *name)
01347 {
01348   int i;
01349   char *filename = xmalloc (strlen (name) + 50);
01350 
01351   /* Prepend file directory to the search path, so relative links work.  */
01352   prepend_to_include_path (pathname_part (name));
01353 
01354   initialize_conversion ();
01355 
01356   /* Try to load the file specified by NAME, concatenated with our
01357      various suffixes.  Prefer files like `makeinfo.texi' to
01358      `makeinfo'.  */
01359   for (i = 0; suffixes[i]; i++)
01360     {
01361       strcpy (filename, name);
01362       strcat (filename, suffixes[i]);
01363 
01364       if (find_and_load (filename, 1))
01365         break;
01366 
01367       if (!suffixes[i][0] && strrchr (filename, '.'))
01368         {
01369           fs_error (filename);
01370           free (filename);
01371           return;
01372         }
01373     }
01374 
01375   if (!suffixes[i])
01376     {
01377       fs_error (name);
01378       free (filename);
01379       return;
01380     }
01381 
01382   input_filename = filename;
01383 
01384   convert_from_loaded_file (name);
01385 
01386   /* Pop the prepended path, so multiple filenames in the
01387      command line do not screw each others include paths.  */
01388   pop_path_from_include_path ();
01389 }
01390 
01391 static int
01392 create_html_directory (char *dir, int can_remove_file)
01393 {
01394   struct stat st;
01395 
01396   /* Already exists.  */
01397   if (stat (dir, &st) == 0)
01398     {
01399       /* And it's a directory, so silently reuse it.  */
01400       if (S_ISDIR (st.st_mode))
01401         return 1;
01402       /* Not a directory, so move it out of the way if we are allowed.  */
01403       else if (can_remove_file)
01404         {
01405           if (unlink (dir) != 0)
01406             return 0;
01407         }
01408       else
01409         return 0;
01410     }
01411 
01412   if (mkdir (dir, 0777) == 0)
01413     /* Success!  */
01414     return 1;
01415   else
01416     return 0;
01417 }
01418 
01419 /* Given OUTPUT_FILENAME == ``/foo/bar/baz.html'', return
01420    "/foo/bar/baz/baz.html".  This routine is called only if html && splitting.
01421 
01422   Split html output goes into the subdirectory of the toplevel
01423   filename, without extension.  For example:
01424       @setfilename foo.info
01425   produces output in files foo/index.html, foo/second-node.html, ...
01426 
01427   But if the user said -o foo.whatever on the cmd line, then use
01428   foo.whatever unchanged.  */
01429 
01430 static char *
01431 insert_toplevel_subdirectory (char *output_filename)
01432 {
01433   static const char index_name[] = "index.html";
01434   char *dir, *subdir, *base, *basename, *p;
01435   char buf[PATH_MAX];
01436   const int index_len = sizeof (index_name) - 1;
01437 
01438   strcpy (buf, output_filename);
01439   dir = pathname_part (buf);   /* directory of output_filename */
01440   base = filename_part (buf);  /* strips suffix, too */
01441   basename = xstrdup (base);   /* remember real @setfilename name */
01442   p = dir + strlen (dir) - 1;
01443   if (p > dir && IS_SLASH (*p))
01444     *p = 0;
01445   p = strrchr (base, '.');
01446   if (p)
01447     *p = 0;
01448 
01449   /* Split html output goes into subdirectory of toplevel name. */
01450   if (save_command_output_filename
01451       && STREQ (output_filename, save_command_output_filename))
01452     subdir = basename;  /* from user, use unchanged */
01453   else
01454     subdir = base;      /* implicit, omit suffix */
01455 
01456   free (output_filename);
01457   output_filename = xmalloc (strlen (dir) + 1
01458                              + strlen (basename) + 1
01459                              + index_len
01460                              + 1);
01461   strcpy (output_filename, dir);
01462   if (strlen (dir))
01463     strcat (output_filename, "/");
01464   strcat (output_filename, subdir);
01465 
01466   /* First try, do not remove existing file.  */
01467   if (!create_html_directory (output_filename, 0))
01468     {
01469       /* That failed, try subdir name with .html.
01470          Remove it if it exists.  */
01471       strcpy (output_filename, dir);
01472       if (strlen (dir))
01473         strcat (output_filename, "/");
01474       strcat (output_filename, basename);
01475 
01476       if (!create_html_directory (output_filename, 1))
01477         {
01478           /* Last try failed too :-\  */
01479           line_error (_("Can't create directory `%s': %s"),
01480               output_filename, strerror (errno));
01481           xexit (1);
01482         }
01483     }
01484 
01485   strcat (output_filename, "/");
01486   strcat (output_filename, index_name);
01487   return output_filename;
01488 }
01489 
01490 /* FIXME: this is way too hairy */
01491 static void
01492 convert_from_loaded_file (char *name)
01493 {
01494   char *real_output_filename = NULL;
01495 
01496   remember_itext (input_text, 0);
01497 
01498   input_text_offset = 0;
01499 
01500   /* Avoid the `\input texinfo' line in HTML output (assuming it starts
01501      the file).  */
01502   if (looking_at ("\\input"))
01503     discard_until ("\n");
01504 
01505   /* Search this file looking for the special string which starts conversion.
01506      Once found, we may truly begin. */
01507   while (input_text_offset >= 0)
01508     {
01509       input_text_offset =
01510         search_forward (setfilename_search, input_text_offset);
01511 
01512       if (input_text_offset == 0
01513           || (input_text_offset > 0
01514               && input_text[input_text_offset -1] == '\n'))
01515         break;
01516       else if (input_text_offset > 0)
01517         input_text_offset++;
01518     }
01519 
01520   if (input_text_offset < 0)
01521     {
01522       if (!command_output_filename)
01523         {
01524 #if defined (REQUIRE_SETFILENAME)
01525           error (_("No `%s' found in `%s'"), setfilename_search, name);
01526           goto finished;
01527 #else
01528           command_output_filename = output_name_from_input_name (name);
01529 #endif /* !REQUIRE_SETFILENAME */
01530         }
01531 
01532       {
01533         int i, end_of_first_line;
01534 
01535         /* Find the end of the first line in the file. */
01536         for (i = 0; i < input_text_length - 1; i++)
01537           if (input_text[i] == '\n')
01538             break;
01539 
01540         end_of_first_line = i + 1;
01541 
01542         for (i = 0; i < end_of_first_line; i++)
01543           {
01544             if ((input_text[i] == '\\') &&
01545                 (strncmp (input_text + i + 1, "input", 5) == 0))
01546               {
01547                 input_text_offset = i;
01548                 break;
01549               }
01550           }
01551       }
01552     }
01553   else
01554     input_text_offset += strlen (setfilename_search);
01555 
01556   if (!command_output_filename)
01557     {
01558       get_until ("\n", &output_filename); /* read rest of line */
01559       if (html || xml)
01560         { /* Change any extension to .html or .xml.  */
01561           char *html_name, *directory_part, *basename_part, *temp;
01562 
01563           canon_white (output_filename);
01564           directory_part = pathname_part (output_filename);
01565 
01566           basename_part = filename_part (output_filename);
01567 
01568           /* Zap any existing extension.  */
01569           temp = strrchr (basename_part, '.');
01570           if (temp)
01571             *temp = 0;
01572 
01573           /* Construct new filename.  */
01574           html_name = xmalloc (strlen (directory_part)
01575                                + strlen (basename_part) + 6);
01576           strcpy (html_name, directory_part);
01577           strcat (html_name, basename_part);
01578           strcat (html_name, html ? ".html" : ".xml");
01579 
01580           /* Replace name from @setfilename with the html name.  */
01581           free (output_filename);
01582           output_filename = html_name;
01583         }
01584     }
01585   else
01586     {
01587       if (input_text_offset != -1)
01588         discard_until ("\n");
01589       else
01590         input_text_offset = 0;
01591 
01592       real_output_filename = output_filename = command_output_filename;
01593       command_output_filename = NULL;  /* for included files or whatever */
01594     }
01595 
01596   canon_white (output_filename);
01597   toplevel_output_filename = xstrdup (output_filename);
01598 
01599   if (real_output_filename && strcmp (real_output_filename, "-") == 0)
01600     {
01601       if (macro_expansion_filename
01602           && strcmp (macro_expansion_filename, "-") == 0)
01603         {
01604           fprintf (stderr,
01605   _("%s: Skipping macro expansion to stdout as Info output is going there.\n"),
01606                    progname);
01607           macro_expansion_output_stream = NULL;
01608         }
01609       real_output_filename = xstrdup (real_output_filename);
01610       output_stream = stdout;
01611       splitting = 0;            /* Cannot split when writing to stdout. */
01612     }
01613   else
01614     {
01615       if (html && splitting)
01616         {
01617           if (FILENAME_CMP (output_filename, NULL_DEVICE) == 0
01618               || FILENAME_CMP (output_filename, ALSO_NULL_DEVICE) == 0)
01619             splitting = 0;
01620           else
01621             output_filename = insert_toplevel_subdirectory (output_filename);
01622           real_output_filename = xstrdup (output_filename);
01623         }
01624       else if (!real_output_filename)
01625         real_output_filename = expand_filename (output_filename, name);
01626       else
01627         real_output_filename = xstrdup (real_output_filename);
01628 
01629       output_stream = fopen (real_output_filename, "w");
01630     }
01631 
01632   set_current_output_filename (real_output_filename);
01633 
01634   if (xml && !docbook)
01635     xml_begin_document (filename_part (output_filename));
01636 
01637   if (verbose_mode)
01638     printf (_("Making %s file `%s' from `%s'.\n"),
01639             no_headers ? "text"
01640             : html ? "HTML"
01641             : xml ? "XML"
01642             : "info",
01643             output_filename, input_filename);
01644 
01645   if (output_stream == NULL)
01646     {
01647       fs_error (real_output_filename);
01648       goto finished;
01649     }
01650 
01651   /* Make the displayable filename from output_filename.  Only the base
01652      portion of the filename need be displayed. */
01653   flush_output ();              /* in case there was no @bye */
01654   if (output_stream != stdout)
01655     pretty_output_filename = filename_part (output_filename);
01656   else
01657     pretty_output_filename = xstrdup ("stdout");
01658 
01659   /* For this file only, count the number of newlines from the top of
01660      the file to here.  This way, we keep track of line numbers for
01661      error reporting.  Line_number starts at 1, since the user isn't
01662      zero-based. */
01663   {
01664     int temp = 0;
01665     line_number = 1;
01666     while (temp != input_text_offset)
01667       if (input_text[temp++] == '\n')
01668         line_number++;
01669   }
01670 
01671   /* html fixxme: should output this as trailer on first page.  */
01672   if (!no_headers && !html && !xml)
01673     add_word_args (_("This is %s, produced by makeinfo version %s from %s.\n"),
01674                    output_filename, VERSION, input_filename);
01675 
01676   close_paragraph ();
01677 
01678   if (xml && !docbook)
01679     {
01680       /* Just before the real main loop, let's handle the defines.  */
01681       COMMAND_LINE_DEFINE *temp;
01682 
01683       for (temp = command_line_defines; temp; temp = temp->next)
01684         {
01685           handle_variable_internal (temp->action, temp->define);
01686           free(temp->define);
01687         }
01688     }
01689 
01690   reader_loop ();
01691   if (xml)
01692     xml_end_document ();
01693 
01694 
01695 finished:
01696   discard_insertions (0);
01697   close_paragraph ();
01698   flush_file_stack ();
01699 
01700   if (macro_expansion_output_stream)
01701     {
01702       fclose (macro_expansion_output_stream);
01703       if (errors_printed && !force
01704           && strcmp (macro_expansion_filename, "-") != 0
01705           && FILENAME_CMP (macro_expansion_filename, NULL_DEVICE) != 0
01706           && FILENAME_CMP (macro_expansion_filename, ALSO_NULL_DEVICE) != 0)
01707         {
01708           fprintf (stderr,
01709 _("%s: Removing macro output file `%s' due to errors; use --force to preserve.\n"),
01710                    progname, macro_expansion_filename);
01711           if (unlink (macro_expansion_filename) < 0)
01712             perror (macro_expansion_filename);
01713         }
01714     }
01715 
01716   if (output_stream)
01717     {
01718       output_pending_notes ();
01719 
01720       if (html)
01721         {
01722           no_indent = 1;
01723           start_paragraph ();
01724           add_word ("</body></html>\n");
01725           close_paragraph ();
01726         }
01727 
01728       /* maybe we want local variables in info output.  */
01729       {
01730         char *trailer = info_trailer ();
01731        if (!xml && !docbook && trailer)
01732           {
01733             if (html)
01734               insert_string ("<!--");
01735             insert_string (trailer);
01736             free (trailer);
01737             if (html)
01738               insert_string ("\n-->\n");
01739           }
01740       }
01741 
01742       /* Write stuff makeinfo generates after @bye, ie. info_trailer.  */
01743       flush_output ();
01744 
01745       if (output_stream != stdout)
01746         fclose (output_stream);
01747 
01748       /* If validating, then validate the entire file right now. */
01749       if (validating)
01750         validate_file (tag_table);
01751 
01752       handle_delayed_writes ();
01753 
01754       if (tag_table)
01755         {
01756           tag_table = (TAG_ENTRY *) reverse_list ((GENERIC_LIST *) tag_table);
01757           if (!no_headers && !html && !STREQ (current_output_filename, "-"))
01758             write_tag_table (real_output_filename);
01759         }
01760 
01761       if (splitting && !html && (!errors_printed || force))
01762         {
01763           clean_old_split_files (real_output_filename);
01764           split_file (real_output_filename, split_size);
01765         }
01766       else if (errors_printed
01767                && !force
01768                && strcmp (real_output_filename, "-") != 0
01769                && FILENAME_CMP (real_output_filename, NULL_DEVICE) != 0
01770                && FILENAME_CMP (real_output_filename, ALSO_NULL_DEVICE) != 0)
01771         { /* If there were errors, and no --force, remove the output.  */
01772           fprintf (stderr,
01773   _("%s: Removing output file `%s' due to errors; use --force to preserve.\n"),
01774                    progname, real_output_filename);
01775           if (unlink (real_output_filename) < 0)
01776             perror (real_output_filename);
01777         }
01778     }
01779   free (real_output_filename);
01780 }
01781 
01782 /* If enable_encoding is set and @documentencoding is used, return a
01783    Local Variables section (as a malloc-ed string) so that Emacs'
01784    locale features can work.  Else return NULL.  */
01785 char *
01786 info_trailer (void)
01787 {
01788   char *encoding;
01789 
01790   if (!enable_encoding)
01791     return NULL;
01792 
01793   encoding = current_document_encoding ();
01794 
01795   if (encoding && *encoding)
01796     {
01797 #define LV_FMT "\n\037\nLocal Variables:\ncoding: %s\nEnd:\n"
01798       char *lv = xmalloc (sizeof (LV_FMT) + strlen (encoding));
01799       sprintf (lv, LV_FMT, encoding);
01800       free (encoding);
01801       return lv;
01802     }
01803 
01804   free (encoding);
01805   return NULL;
01806 }
01807 
01808 void
01809 free_and_clear (char **pointer)
01810 {
01811   if (*pointer)
01812     {
01813       free (*pointer);
01814       *pointer = NULL;
01815     }
01816 }
01817 
01818  /* Initialize some state. */
01819 static void
01820 init_internals (void)
01821 {
01822   free_and_clear (&output_filename);
01823   free_and_clear (&command);
01824   free_and_clear (&input_filename);
01825   free_node_references ();
01826   free_node_node_references ();
01827   toc_free ();
01828   init_insertion_stack ();
01829   init_brace_stack ();
01830   current_node = NULL; /* sometimes already freed */
01831   command_index = 0;
01832   in_menu = 0;
01833   in_detailmenu = 0;
01834   top_node_seen = 0;
01835   non_top_node_seen = 0;
01836   node_number = -1;
01837 }
01838 
01839 void
01840 init_paragraph (void)
01841 {
01842   free (output_paragraph);
01843   output_paragraph = xmalloc (paragraph_buffer_len);
01844   output_paragraph[0] = 0;
01845   output_paragraph_offset = 0;
01846   output_column = 0;
01847   paragraph_is_open = 0;
01848   current_indent = 0;
01849   meta_char_pos = 0;
01850 }
01851 
01852 /* This is called from `reader_loop' when we are at the * beginning a
01853    menu line.  */
01854 
01855 static void
01856 handle_menu_entry (void)
01857 {
01858   char *tem;
01859 
01860   /* Ugh, glean_node_from_menu wants to read the * itself.  */
01861   input_text_offset--;
01862 
01863   /* Find node name in menu entry and save it in references list for
01864      later validation.  Use followed_reference type for detailmenu
01865      references since we don't want to use them for default node pointers.  */
01866   tem = glean_node_from_menu (1, in_detailmenu
01867                                  ? followed_reference : menu_reference);
01868 
01869   if (html && tem)
01870     { /* Start a menu item with the cleaned-up line.  Put an anchor
01871          around the start text (before `:' or the node name). */
01872       char *string;
01873 
01874       discard_until ("* ");
01875 
01876       /* The line number was already incremented in reader_loop when we
01877          saw the newline, and discard_until has now incremented again.  */
01878       line_number--;
01879 
01880       if (had_menu_commentary)
01881         {
01882           add_html_block_elt ("<ul class=\"menu\">\n");
01883           had_menu_commentary = 0;
01884           in_paragraph = 0;
01885         }
01886 
01887       if (in_paragraph)
01888         {
01889           add_html_block_elt ("</p>\n");
01890           add_html_block_elt ("<ul class=\"menu\">\n");
01891           in_paragraph = 0;
01892         }
01893 
01894       in_menu_item = 1;
01895 
01896       add_html_block_elt ("<li><a");
01897       if (next_menu_item_number <= 9)
01898         {
01899           add_word(" accesskey=");
01900           add_word_args("\"%d\"", next_menu_item_number);
01901           next_menu_item_number++;
01902         }
01903       add_word (" href=\"");
01904       string = expansion (tem, 0);
01905       add_anchor_name (string, 1);
01906       add_word ("\">");
01907       free (string);
01908 
01909       /* The menu item may use macros, so expand them now.  */
01910       only_macro_expansion++;
01911       get_until_in_line (1, ":", &string);
01912       only_macro_expansion--;
01913       execute_string ("%s", string); /* get escaping done */
01914       free (string);
01915 
01916       add_word ("</a>");
01917 
01918       if (looking_at ("::"))
01919         discard_until (":");
01920       else
01921         { /* discard the node name */
01922           get_until_in_line (0, ".", &string);
01923           free (string);
01924         }
01925       input_text_offset++;      /* discard the second colon or the period */
01926 
01927       /* Insert a colon only if there is a description of this menu item.  */
01928       {
01929         int save_input_text_offset = input_text_offset;
01930         int save_line_number = line_number;
01931         char *test_string;
01932         get_rest_of_line (0, &test_string);
01933         if (strlen (test_string) > 0)
01934           add_word (": ");
01935         input_text_offset = save_input_text_offset;
01936         line_number = save_line_number;
01937       }
01938     }
01939   else if (xml && tem)
01940     {
01941       xml_start_menu_entry (tem);
01942     }
01943   else if (tem)
01944     { /* For Info output, we can just use the input and the main case in
01945          reader_loop where we output what comes in.  Just move off the *
01946          so the next time through reader_loop we don't end up back here.  */
01947       add_char ('*');
01948       input_text_offset += 2; /* undo the pointer back-up above.  */
01949     }
01950 
01951   if (tem)
01952     free (tem);
01953 }
01954 
01955 /* Find the command corresponding to STRING.  If the command is found,
01956    return a pointer to the data structure.  Otherwise return -1.  */
01957 static COMMAND *
01958 get_command_entry (char *string)
01959 {
01960   int i;
01961 
01962   for (i = 0; command_table[i].name; i++)
01963     if (strcmp (command_table[i].name, string) == 0)
01964       return &command_table[i];
01965 
01966   /* This command is not in our predefined command table.  Perhaps
01967      it is a user defined command. */
01968   for (i = 0; i < user_command_array_len; i++)
01969     if (user_command_array[i] &&
01970         (strcmp (user_command_array[i]->name, string) == 0))
01971       return user_command_array[i];
01972 
01973   /* We never heard of this command. */
01974   return (COMMAND *) -1;
01975 }
01976 
01977 /* input_text_offset is right at the command prefix character.
01978    Read the next token to determine what to do.  Return zero
01979    if there's no known command or macro after the prefix character.  */
01980 static int
01981 read_command (void)
01982 {
01983   COMMAND *entry;
01984   int old_text_offset = input_text_offset++;
01985 
01986   free_and_clear (&command);
01987   command = read_token ();
01988 
01989   /* Check to see if this command is a macro.  If so, execute it here. */
01990   {
01991     MACRO_DEF *def;
01992 
01993     def = find_macro (command);
01994 
01995     if (def)
01996       {
01997         /* We disallow recursive use of a macro call.  Inhibit the expansion
01998            of this macro during the life of its execution. */
01999         if (!(def->flags & ME_RECURSE))
02000           def->inhibited = 1;
02001 
02002         executing_macro++;
02003         execute_macro (def);
02004         executing_macro--;
02005 
02006         if (!(def->flags & ME_RECURSE))
02007           def->inhibited = 0;
02008 
02009         return 1;
02010       }
02011   }
02012 
02013   if (only_macro_expansion)
02014     {
02015       /* Back up to the place where we were called, so the
02016          caller will have a chance to process this non-macro.  */
02017       input_text_offset = old_text_offset;
02018       return 0;
02019     }
02020 
02021   /* Perform alias expansion */
02022   command = alias_expand (command);
02023 
02024   if (enclosure_command (command))
02025     {
02026       remember_brace (enclosure_expand);
02027       enclosure_expand (START, output_paragraph_offset, 0);
02028       return 0;
02029     }
02030 
02031   entry = get_command_entry (command);
02032   if (entry == (COMMAND *)-1)
02033     {
02034       line_error (_("Unknown command `%s'"), command);
02035       return 0;
02036     }
02037 
02038   if (entry->argument_in_braces == BRACE_ARGS)
02039     remember_brace (entry->proc);
02040   else if (entry->argument_in_braces == MAYBE_BRACE_ARGS)
02041     {
02042       if (curchar () == '{')
02043         remember_brace (entry->proc);
02044       else
02045         { /* No braces, so arg is next char.  */
02046           int ch;
02047           int saved_offset = output_paragraph_offset;
02048           (*(entry->proc)) (START, output_paragraph_offset, 0);
02049 
02050           /* Possibilities left for the next character: @ (error), }
02051              (error), whitespace (skip) anything else (normal char).  */
02052           skip_whitespace ();
02053           ch = curchar ();
02054           if (ch == '@')
02055             {
02056            line_error (_("Use braces to give a command as an argument to @%s"),
02057                entry->name);
02058               return 0;
02059             }
02060           else if (ch == '}')
02061             {
02062               /* Our caller will give the error message, because this }
02063                  won't match anything.  */
02064               return 0;
02065             }
02066 
02067           add_char (ch);
02068           input_text_offset++;
02069           (*(entry->proc)) (END, saved_offset, output_paragraph_offset);
02070           return 1;
02071         }
02072     }
02073 
02074   /* Get here if we have BRACE_ARGS, NO_BRACE_ARGS, or MAYBE_BRACE_ARGS
02075      with braces.  */
02076   (*(entry->proc)) (START, output_paragraph_offset, 0);
02077   return 1;
02078 }
02079 
02080 /* Okay, we are ready to start the conversion.  Call the reader on
02081    some text, and fill the text as it is output.  Handle commands by
02082    remembering things like open braces and the current file position on a
02083    stack, and when the corresponding close brace is found, you can call
02084    the function with the proper arguments.  Although the filling isn't
02085    necessary for HTML, it should do no harm.  */
02086 void
02087 reader_loop (void)
02088 {
02089   int character;
02090   int done = 0;
02091 
02092   while (!done)
02093     {
02094       if (input_text_offset >= input_text_length)
02095         break;
02096 
02097       character = curchar ();
02098 
02099       /* If only_macro_expansion, only handle macros and leave
02100          everything else intact.  */
02101       if (!only_macro_expansion && !in_fixed_width_font
02102           && ((!html && !xml) || escape_html)
02103           && (character == '\'' || character == '`')
02104           && input_text[input_text_offset + 1] == character)
02105         {
02106           if (html)
02107             {
02108               input_text_offset += 2;
02109               add_word (character == '`' ? "&ldquo;" : "&rdquo;");
02110               continue;
02111             }
02112           else if (xml)
02113             {
02114               input_text_offset += 2;
02115               xml_insert_entity (character == '`' ? "ldquo" : "rdquo");
02116               continue;
02117             }
02118           else
02119             {
02120               input_text_offset++;
02121               character = '"';
02122             }
02123         }
02124 
02125       /* Convert --- to --.  */
02126       if (!only_macro_expansion && character == '-' && !in_fixed_width_font
02127           && ((!html && !xml) || escape_html))
02128         {
02129           int dash_count = 0;
02130 
02131           /* Get the number of consequtive dashes.  */
02132           while (input_text[input_text_offset] == '-')
02133             {
02134               dash_count++;
02135               input_text_offset++;
02136             }
02137 
02138           /* Eat one dash.  */
02139           dash_count--;
02140 
02141           if (html || xml)
02142             {
02143               if (dash_count == 0)
02144                 add_char ('-');
02145               else
02146                 while (dash_count > 0)
02147                   {
02148                     if (dash_count >= 2)
02149                       {
02150                         if (html)
02151                           add_word ("&mdash;");
02152                         else
02153                           xml_insert_entity ("mdash");
02154                         dash_count -= 2;
02155                       }
02156                     else if (dash_count >= 1)
02157                       {
02158                         if (html)
02159                           add_word ("&ndash;");
02160                         else
02161                           xml_insert_entity ("ndash");
02162                         dash_count--;
02163                       }
02164                   }
02165             }
02166           else
02167             {
02168               add_char ('-');
02169               while (--dash_count > 0)
02170                 add_char ('-');
02171             }
02172 
02173           continue;
02174         }
02175 
02176       /* If this is a whitespace character, then check to see if the line
02177          is blank.  If so, advance to the carriage return. */
02178       if (!only_macro_expansion && whitespace (character))
02179         {
02180           int i = input_text_offset + 1;
02181 
02182           while (i < input_text_length && whitespace (input_text[i]))
02183             i++;
02184 
02185           if (i == input_text_length || input_text[i] == '\n')
02186             {
02187               if (i == input_text_length)
02188                 i--;
02189 
02190               input_text_offset = i;
02191               character = curchar ();
02192             }
02193         }
02194 
02195       if (character == '\n')
02196         line_number++;
02197 
02198       switch (character)
02199         {
02200         case '*': /* perhaps we are at a menu */
02201           /* We used to check for this in the \n case but an @c in a
02202              menu swallows its newline, so check here instead.  */
02203           if (!only_macro_expansion && in_menu
02204               && input_text_offset + 1 < input_text_length
02205               && input_text[input_text_offset-1] == '\n')
02206             handle_menu_entry ();
02207           else
02208             { /* Duplicate code from below, but not worth twisting the
02209                  fallthroughs to get down there.  */
02210               add_char (character);
02211               input_text_offset++;
02212             }
02213           break;
02214 
02215         /* Escapes for HTML unless we're outputting raw HTML.  Do
02216            this always, even if SGML rules don't require it since
02217            that's easier and safer for non-conforming browsers. */
02218         case '&':
02219           if (html && escape_html)
02220             add_word ("&amp;");
02221           else
02222             add_char (character);
02223           input_text_offset++;
02224           break;
02225 
02226         case '<':
02227           if (html && escape_html)
02228             add_word ("&lt;");
02229           else if (xml && escape_html)
02230             xml_insert_entity ("lt");
02231           else
02232             add_char (character);
02233           input_text_offset++;
02234           break;
02235 
02236         case '>':
02237           if (html && escape_html)
02238             add_word ("&gt;");
02239           else if (xml && escape_html)
02240             xml_insert_entity ("gt");
02241           else
02242             add_char (character);
02243           input_text_offset++;
02244           break;
02245 
02246         case COMMAND_PREFIX: /* @ */
02247           if (read_command () || !only_macro_expansion)
02248             break;
02249 
02250         /* FALLTHROUGH (usually) */
02251         case '{':
02252           /* Special case.  We're not supposed to see this character by itself.
02253              If we do, it means there is a syntax error in the input text.
02254              Report the error here, but remember this brace on the stack so
02255              we can ignore its partner. */
02256           if (!only_macro_expansion)
02257             {
02258               if (command && !STREQ (command, "math"))
02259                 {
02260                   line_error (_("Misplaced %c"), '{');
02261                   remember_brace (misplaced_brace);
02262                 }
02263               else
02264                 /* We don't mind `extra' braces inside @math.  */
02265                 remember_brace (cm_no_op);
02266               /* remember_brace advances input_text_offset.  */
02267               break;
02268             }
02269 
02270         /* FALLTHROUGH (usually) */
02271         case '}':
02272           if (!only_macro_expansion)
02273             {
02274               pop_and_call_brace ();
02275               input_text_offset++;
02276               break;
02277             }
02278 
02279         /* FALLTHROUGH (usually) */
02280         default:
02281           add_char (character);
02282           input_text_offset++;
02283         }
02284     }
02285   if (macro_expansion_output_stream && !only_macro_expansion)
02286     maybe_write_itext (input_text, input_text_offset);
02287 }
02288 
02289 static void
02290 init_brace_stack (void)
02291 {
02292   brace_stack = NULL;
02293 }
02294 
02295 /* Remember the current output position here.  Save PROC
02296    along with it so you can call it later. */
02297 static void
02298 remember_brace_1 (COMMAND_FUNCTION (*proc), int position)
02299 {
02300   BRACE_ELEMENT *new = xmalloc (sizeof (BRACE_ELEMENT));
02301   new->next = brace_stack;
02302   new->proc = proc;
02303   new->command = command ? xstrdup (command) : "";
02304   new->pos = position;
02305   new->line = line_number;
02306   new->in_fixed_width_font = in_fixed_width_font;
02307   brace_stack = new;
02308 }
02309 
02310 static void
02311 remember_brace (COMMAND_FUNCTION (*proc))
02312 {
02313   if (curchar () != '{')
02314     line_error (_("%c%s expected braces"), COMMAND_PREFIX, command);
02315   else
02316     input_text_offset++;
02317   remember_brace_1 (proc, output_paragraph_offset);
02318 }
02319 
02320 /* Pop the top of the brace stack, and call the associated function
02321    with the args END and POS. */
02322 static void
02323 pop_and_call_brace (void)
02324 {
02325   if (brace_stack == NULL)
02326     {
02327       line_error (_("Unmatched }"));
02328       return;
02329     }
02330 
02331   {
02332     BRACE_ELEMENT *temp;
02333 
02334     int pos = brace_stack->pos;
02335     COMMAND_FUNCTION *proc = brace_stack->proc;
02336     in_fixed_width_font = brace_stack->in_fixed_width_font;
02337 
02338     /* Reset current command, so the proc can know who it is.  This is
02339        used in cm_accent.  */
02340     command = brace_stack->command;
02341 
02342     temp = brace_stack->next;
02343     free (brace_stack);
02344     brace_stack = temp;
02345 
02346     (*proc) (END, pos, output_paragraph_offset);
02347   }
02348 }
02349 
02350 /* Shift all of the markers in `brace_stack' by AMOUNT. */
02351 static void
02352 adjust_braces_following (int here, int amount)
02353 {
02354   BRACE_ELEMENT *stack = brace_stack;
02355 
02356   while (stack)
02357     {
02358       if (stack->pos >= here)
02359         stack->pos += amount;
02360       stack = stack->next;
02361     }
02362 }
02363 
02364 /* Return the string which invokes PROC; a pointer to a function.
02365    Always returns the first function in the command table if more than
02366    one matches PROC.  */
02367 static const char *
02368 find_proc_name (COMMAND_FUNCTION (*proc))
02369 {
02370   int i;
02371 
02372   for (i = 0; command_table[i].name; i++)
02373     if (proc == command_table[i].proc)
02374       return command_table[i].name;
02375   return _("NO_NAME!");
02376 }
02377 
02378 /* You call discard_braces () when you shouldn't have any braces on the stack.
02379    I used to think that this happens for commands that don't take arguments
02380    in braces, but that was wrong because of things like @code{foo @@}.  So now
02381    I only detect it at the beginning of nodes. */
02382 void
02383 discard_braces (void)
02384 {
02385   if (!brace_stack)
02386     return;
02387 
02388   while (brace_stack)
02389     {
02390       if (brace_stack->proc != misplaced_brace)
02391         {
02392           const char *proc_name;
02393 
02394           proc_name = find_proc_name (brace_stack->proc);
02395           file_line_error (input_filename, brace_stack->line,
02396                            _("%c%s missing close brace"), COMMAND_PREFIX,
02397                            proc_name);
02398           pop_and_call_brace ();
02399         }
02400       else
02401         {
02402           BRACE_ELEMENT *temp;
02403           temp = brace_stack->next;
02404           free (brace_stack);
02405           brace_stack = temp;
02406         }
02407     }
02408 }
02409 
02410 static int
02411 get_char_len (int character)
02412 {
02413   /* Return the printed length of the character. */
02414   int len;
02415 
02416   switch (character)
02417     {
02418     case '\t':
02419       len = (output_column + 8) & 0xf7;
02420       if (len > fill_column)
02421         len = fill_column - output_column;
02422       else
02423         len = len - output_column;
02424       break;
02425 
02426     case '\n':
02427       len = fill_column - output_column;
02428       break;
02429 
02430     default:
02431       /* ASCII control characters appear as two characters in the output
02432          (e.g., ^A).  But characters with the high bit set are just one
02433          on suitable terminals, so don't count them as two for line
02434          breaking purposes.  */
02435       if (0 <= character && character < ' ')
02436         len = 2;
02437       else
02438         len = 1;
02439     }
02440   return len;
02441 }
02442 
02443 void
02444 #if defined (VA_FPRINTF) && __STDC__
02445 add_word_args (const char *format, ...)
02446 #else
02447 add_word_args (format, va_alist)
02448     const char *format;
02449     va_dcl
02450 #endif
02451 {
02452   char buffer[2000]; /* xx no fixed limits */
02453 #ifdef VA_FPRINTF
02454   va_list ap;
02455 #endif
02456 
02457   VA_START (ap, format);
02458 #ifdef VA_SPRINTF
02459   VA_SPRINTF (buffer, format, ap);
02460 #else
02461   sprintf (buffer, format, a1, a2, a3, a4, a5, a6, a7, a8);
02462 #endif /* not VA_SPRINTF */
02463   va_end (ap);
02464   add_word (buffer);
02465 }
02466 
02467 /* Add STRING to output_paragraph. */
02468 void
02469 add_word (char *string)
02470 {
02471   while (*string)
02472     add_char (*string++);
02473 }
02474 
02475 /* Like add_word, but inhibits conversion of whitespace into &nbsp;.
02476    Use this to output HTML directives with embedded blanks, to make
02477    them @w-safe.  */
02478 void
02479 add_html_elt (char *string)
02480 {
02481   in_html_elt++;
02482   add_word (string);
02483   in_html_elt--;
02484 }
02485 
02486 /* These two functions below, add_html_block_elt and add_html_block_elt_args,
02487    are mixtures of add_html_elt and add_word_args.  They inform makeinfo that
02488    the current HTML element being inserted should not be enclosed in a <p>
02489    element.  */
02490 void
02491 add_html_block_elt (char *string)
02492 {
02493   in_html_block_level_elt++;
02494   add_word (string);
02495   in_html_block_level_elt--;
02496 }
02497 
02498 void
02499 #if defined (VA_FPRINTF) && __STDC__
02500 add_html_block_elt_args (const char *format, ...)
02501 #else
02502 add_html_block_elt_args (format, va_alist)
02503     const char *format;
02504     va_dcl
02505 #endif
02506 {
02507   char buffer[2000]; /* xx no fixed limits */
02508 #ifdef VA_FPRINTF
02509   va_list ap;
02510 #endif
02511 
02512   VA_START (ap, format);
02513 #ifdef VA_SPRINTF
02514   VA_SPRINTF (buffer, format, ap);
02515 #else
02516   sprintf (buffer, format, a1, a2, a3, a4, a5, a6, a7, a8);
02517 #endif /* not VA_SPRINTF */
02518   va_end (ap);
02519   add_html_block_elt (buffer);
02520 }
02521 
02522 /* Here is another awful kludge, used in add_char.  Ordinarily, macro
02523    expansions take place in the body of the document, and therefore we
02524    should html_output_head when we see one.  But there's an exception: a
02525    macro call might take place within @copying, and that does not start
02526    the real output, even though we fully expand the copying text.
02527 
02528    So we need to be able to check if we are defining the @copying text.
02529    We do this by looking back through the insertion stack.  */
02530 static int
02531 defining_copying (void)
02532 {
02533   INSERTION_ELT *i;
02534   for (i = insertion_stack; i; i = i->next)
02535     {
02536       if (i->insertion == copying)
02537         return 1;
02538     }
02539   return 0;
02540 }
02541 
02542 
02543 /* Add the character to the current paragraph.  If filling_enabled is
02544    nonzero, then do filling as well. */
02545 void
02546 add_char (int character)
02547 {
02548   if (xml)
02549     {
02550       xml_add_char (character);
02551       return;
02552     }
02553 
02554   /* If we are avoiding outputting headers, and we are currently
02555      in a menu, then simply return.  But if we're only expanding macros,
02556      then we're being called from glean_node_from_menu to try to
02557      remember a menu reference, and we need that so we can do defaulting.  */
02558   if (no_headers && !only_macro_expansion && (in_menu || in_detailmenu))
02559     return;
02560 
02561   /* If we are adding a character now, then we don't have to
02562      ignore close_paragraph () calls any more. */
02563   if (must_start_paragraph && character != '\n')
02564     {
02565       must_start_paragraph = 0;
02566       line_already_broken = 0;  /* The line is no longer broken. */
02567       if (current_indent > output_column)
02568         {
02569           indent (current_indent - output_column);
02570           output_column = current_indent;
02571         }
02572     }
02573 
02574   if (non_splitting_words
02575       && !(html && in_html_elt)
02576       && strchr (" \t\n", character))
02577     {
02578       if (html || docbook)
02579         { /* Seems cleaner to use &nbsp; than an 8-bit char.  */
02580           int saved_escape_html = escape_html;
02581           escape_html = 0;
02582           add_word ("&nbsp");
02583           escape_html = saved_escape_html;
02584           character = ';';
02585         }
02586       else
02587         character = META (' '); /* unmeta-d in flush_output */
02588     }
02589 
02590   insertion_paragraph_closed = 0;
02591 
02592   switch (character)
02593     {
02594     case '\n':
02595       if (!filling_enabled && !(html && (in_menu || in_detailmenu)))
02596         {
02597           insert ('\n');
02598 
02599           if (force_flush_right)
02600             {
02601               close_paragraph ();
02602               /* Hack to force single blank lines out in this mode. */
02603               flush_output ();
02604             }
02605 
02606           output_column = 0;
02607 
02608           if (!no_indent && paragraph_is_open)
02609             indent (output_column = current_indent);
02610           break;
02611         }
02612       else if (end_of_sentence_p ())
02613         /* CHARACTER is newline, and filling is enabled. */
02614         {
02615           insert (' ');
02616           output_column++;
02617           last_inserted_character = character;
02618         }
02619 
02620       if (last_char_was_newline)
02621         {
02622           if (html)
02623             last_char_was_newline++;
02624           close_paragraph ();
02625           pending_indent = 0;
02626         }
02627       else
02628         {
02629           last_char_was_newline = 1;
02630           if (html)
02631             insert ('\n');
02632           else
02633             insert (' ');
02634           output_column++;
02635         }
02636       break;
02637 
02638     default: /* not at newline */
02639       {
02640         int len = get_char_len (character);
02641         int suppress_insert = 0;
02642 
02643         if ((character == ' ') && (last_char_was_newline))
02644           {
02645             if (!paragraph_is_open)
02646               {
02647                 pending_indent++;
02648                 return;
02649               }
02650           }
02651 
02652         /* This is sad, but it seems desirable to not force any
02653            particular order on the front matter commands.  This way,
02654            the document can do @settitle, @documentlanguage, etc, in
02655            any order and with any omissions, and we'll still output
02656            the html <head> `just in time'.  */
02657         if ((executing_macro || !executing_string)
02658             && !only_macro_expansion
02659             && html && !html_output_head_p && !defining_copying ())
02660           html_output_head ();
02661 
02662         if (!paragraph_is_open)
02663           {
02664             start_paragraph ();
02665             /* If the paragraph is supposed to be indented a certain
02666                way, then discard all of the pending whitespace.
02667                Otherwise, we let the whitespace stay. */
02668             if (!paragraph_start_indent)
02669               indent (pending_indent);
02670             pending_indent = 0;
02671 
02672             /* This check for in_html_block_level_elt prevents <p> from being
02673                inserted when we already have html markup starting a paragraph,
02674                as with <ul> and <h1> and the like.  */
02675             if (html && !in_html_block_level_elt)
02676               {
02677                 if ((in_menu || in_detailmenu) && in_menu_item)
02678                   {
02679                     insert_string ("</li></ul>\n");
02680                     in_menu_item = 0;
02681                   }
02682                 insert_string ("<p>");
02683                 in_paragraph = 1;
02684                 adjust_braces_following (0, 3); /* adjust for <p> */
02685               }
02686           }
02687 
02688         output_column += len;
02689         if (output_column > fill_column)
02690           {
02691             if (filling_enabled && !html)
02692               {
02693                 int temp = output_paragraph_offset;
02694                 while (--temp > 0 && output_paragraph[temp] != '\n')
02695                   {
02696                     /* If we have found a space, we have the place to break
02697                        the line. */
02698                     if (output_paragraph[temp] == ' ')
02699                       {
02700                         /* Remove trailing whitespace from output. */
02701                         while (temp && whitespace (output_paragraph[temp - 1]))
02702                           temp--;
02703 
02704                         /* If we went back all the way to the newline of the
02705                            preceding line, it probably means that the word we
02706                            are adding is itself wider than the space that the
02707                            indentation and the fill_column let us use.  In
02708                            that case, do NOT insert another newline, since it
02709                            won't help.  Just indent to current_indent and
02710                            leave it alone, since that's the most we can do.  */
02711                         if (temp && output_paragraph[temp - 1] != '\n')
02712                           output_paragraph[temp++] = '\n';
02713 
02714                         /* We have correctly broken the line where we want
02715                            to.  What we don't want is spaces following where
02716                            we have decided to break the line.  We get rid of
02717                            them. */
02718                         {
02719                           int t1 = temp;
02720 
02721                           for (;; t1++)
02722                             {
02723                               if (t1 == output_paragraph_offset)
02724                                 {
02725                                   if (whitespace (character))
02726                                     suppress_insert = 1;
02727                                   break;
02728                                 }
02729                               if (!whitespace (output_paragraph[t1]))
02730                                 break;
02731                             }
02732 
02733                           if (t1 != temp)
02734                             {
02735                               adjust_braces_following (temp, (- (t1 - temp)));
02736                               memmove (&output_paragraph[temp],
02737                                        &output_paragraph[t1],
02738                                        output_paragraph_offset - t1);
02739                               output_paragraph_offset -= (t1 - temp);
02740                             }
02741                         }
02742 
02743                         /* Filled, but now indent if that is right. */
02744                         if (indented_fill && current_indent > 0)
02745                           {
02746                             int buffer_len = ((output_paragraph_offset - temp)
02747                                               + current_indent);
02748                             char *temp_buffer = xmalloc (buffer_len);
02749                             int indentation = 0;
02750 
02751                             /* We have to shift any markers that are in
02752                                front of the wrap point. */
02753                             adjust_braces_following (temp, current_indent);
02754 
02755                             while (current_indent > 0 &&
02756                                    indentation != current_indent)
02757                               temp_buffer[indentation++] = ' ';
02758 
02759                             memcpy ((char *) &temp_buffer[current_indent],
02760                                      (char *) &output_paragraph[temp],
02761                                      buffer_len - current_indent);
02762 
02763                             if (output_paragraph_offset + buffer_len
02764                                 >= paragraph_buffer_len)
02765                               {
02766                                 unsigned char *tt = xrealloc
02767                                   (output_paragraph,
02768                                    (paragraph_buffer_len += buffer_len));
02769                                 output_paragraph = tt;
02770                               }
02771                             memcpy ((char *) &output_paragraph[temp],
02772                                      temp_buffer, buffer_len);
02773                             output_paragraph_offset += current_indent;
02774                             free (temp_buffer);
02775                           }
02776                         output_column = 0;
02777                         while (temp < output_paragraph_offset)
02778                           output_column +=
02779                             get_char_len (output_paragraph[temp++]);
02780                         output_column += len;
02781                         break;
02782                       }
02783                   }
02784               }
02785           }
02786 
02787         if (!suppress_insert)
02788           {
02789             insert (character);
02790             last_inserted_character = character;
02791           }
02792         last_char_was_newline = 0;
02793         line_already_broken = 0;
02794       }
02795     }
02796 }
02797 
02798 /* Add a character and store its position in meta_char_pos.  */
02799 void
02800 add_meta_char (int character)
02801 {
02802   meta_char_pos = output_paragraph_offset;
02803   add_char (character);
02804 }
02805 
02806 /* Insert CHARACTER into `output_paragraph'. */
02807 void
02808 insert (int character)
02809 {
02810   /* We don't want to strip trailing whitespace in multitables.  Otherwise
02811      horizontal separators confuse the font locking in Info mode in Emacs,
02812      because it looks like a @subsection.  Adding a trailing space to those
02813      lines fixes it.  */
02814   if (character == '\n' && !html && !xml && !multitable_active)
02815     {
02816       while (output_paragraph_offset
02817             && whitespace (output_paragraph[output_paragraph_offset-1]))
02818        output_paragraph_offset--;
02819     }
02820 
02821   output_paragraph[output_paragraph_offset++] = character;
02822   if (output_paragraph_offset == paragraph_buffer_len)
02823     {
02824       output_paragraph =
02825         xrealloc (output_paragraph, (paragraph_buffer_len += 100));
02826     }
02827 }
02828 
02829 /* Insert the null-terminated string STRING into `output_paragraph'.  */
02830 void
02831 insert_string (const char *string)
02832 {
02833   while (*string)
02834     insert (*string++);
02835 }
02836 
02837 
02838 /* Sentences might have these characters after the period (or whatever).  */
02839 #define POST_SENTENCE(c) ((c) == ')' || (c) == '\'' || (c) == '"' \
02840                           || (c) == ']')
02841 
02842 /* Return true if at an end-of-sentence character, possibly followed by
02843    post-sentence punctuation to ignore.  */
02844 static int
02845 end_of_sentence_p (void)
02846 {
02847   int loc = output_paragraph_offset - 1;
02848 
02849   /* If nothing has been output, don't check output_paragraph[-1].  */
02850   if (loc < 0)
02851     return 0;
02852 
02853   /* A post-sentence character that is at meta_char_pos is not really
02854      a post-sentence character; it was produced by a markup such as
02855      @samp.  We don't want the period inside @samp to be treated as a
02856      sentence ender. */
02857   while (loc > 0
02858          && loc != meta_char_pos && POST_SENTENCE (output_paragraph[loc]))
02859     loc--;
02860   return loc != meta_char_pos && sentence_ender (output_paragraph[loc]);
02861 }
02862 
02863 
02864 /* Remove upto COUNT characters of whitespace from the
02865    the current output line.  If COUNT is less than zero,
02866    then remove until none left. */
02867 void
02868 kill_self_indent (int count)
02869 {
02870   /* Handle infinite case first. */
02871   if (count < 0)
02872     {
02873       output_column = 0;
02874       while (output_paragraph_offset)
02875         {
02876           if (whitespace (output_paragraph[output_paragraph_offset - 1]))
02877             output_paragraph_offset--;
02878           else
02879             break;
02880         }
02881     }
02882   else
02883     {
02884       while (output_paragraph_offset && count--)
02885         if (whitespace (output_paragraph[output_paragraph_offset - 1]))
02886           output_paragraph_offset--;
02887         else
02888           break;
02889     }
02890 }
02891 
02892 /* Nonzero means do not honor calls to flush_output (). */
02893 static int flushing_ignored = 0;
02894 
02895 /* Prevent calls to flush_output () from having any effect. */
02896 void
02897 inhibit_output_flushing (void)
02898 {
02899   flushing_ignored++;
02900 }
02901 
02902 /* Allow calls to flush_output () to write the paragraph data. */
02903 void
02904 uninhibit_output_flushing (void)
02905 {
02906   flushing_ignored--;
02907 }
02908 
02909 void
02910 flush_output (void)
02911 {
02912   int i;
02913 
02914   if (!output_paragraph_offset || flushing_ignored)
02915     return;
02916 
02917   for (i = 0; i < output_paragraph_offset; i++)
02918     {
02919       if (output_paragraph[i] == '\n')
02920         {
02921           output_line_number++;
02922           node_line_number++;
02923         }
02924 
02925       /* If we turned on the 8th bit for a space inside @w, turn it
02926          back off for output.  This might be problematic, since the
02927          0x80 character may be used in 8-bit character sets.  Sigh.
02928          In any case, don't do this for HTML, since the nbsp character
02929          is valid input and must be passed along to the browser.  */
02930       if (!html && (output_paragraph[i] & meta_character_bit))
02931         {
02932           int temp = UNMETA (output_paragraph[i]);
02933           if (temp == ' ')
02934             output_paragraph[i] &= 0x7f;
02935         }
02936     }
02937 
02938   fwrite (output_paragraph, 1, output_paragraph_offset, output_stream);
02939 
02940   output_position += output_paragraph_offset;
02941   output_paragraph_offset = 0;
02942   meta_char_pos = 0;
02943 }
02944 
02945 /* How to close a paragraph controlling the number of lines between
02946    this one and the last one. */
02947 
02948 /* Paragraph spacing is controlled by this variable.  It is the number of
02949    blank lines that you wish to appear between paragraphs.  A value of
02950    1 creates a single blank line between paragraphs. */
02951 int paragraph_spacing = DEFAULT_PARAGRAPH_SPACING;
02952 
02953 static void
02954 close_paragraph_with_lines (int lines)
02955 {
02956   int old_spacing = paragraph_spacing;
02957   paragraph_spacing = lines;
02958   close_paragraph ();
02959   paragraph_spacing = old_spacing;
02960 }
02961 
02962 /* Close the current paragraph, leaving no blank lines between them. */
02963 void
02964 close_single_paragraph (void)
02965 {
02966   close_paragraph_with_lines (0);
02967 }
02968 
02969 /* Close a paragraph after an insertion has ended. */
02970 void
02971 close_insertion_paragraph (void)
02972 {
02973   if (!insertion_paragraph_closed)
02974     {
02975       /* Close the current paragraph, breaking the line. */
02976       close_single_paragraph ();
02977 
02978       /* Start a new paragraph, with the correct indentation for the now
02979          current insertion level (one above the one that we are ending). */
02980       start_paragraph ();
02981 
02982       /* Tell `close_paragraph' that the previous line has already been
02983          broken, so it should insert one less newline. */
02984       line_already_broken = 1;
02985 
02986       /* Tell functions such as `add_char' we've already found a newline. */
02987       ignore_blank_line ();
02988     }
02989   else
02990     {
02991       /* If the insertion paragraph is closed already, then we are seeing
02992          two `@end' commands in a row.  Note that the first one we saw was
02993          handled in the first part of this if-then-else clause, and at that
02994          time `start_paragraph' was called, partially to handle the proper
02995          indentation of the current line.  However, the indentation level
02996          may have just changed again, so we may have to outdent the current
02997          line to the new indentation level. */
02998       if (current_indent < output_column)
02999         kill_self_indent (output_column - current_indent);
03000     }
03001 
03002   insertion_paragraph_closed = 1;
03003 }
03004 
03005 /* Close the currently open paragraph. */
03006 void
03007 close_paragraph (void)
03008 {
03009   int i;
03010 
03011   /* We don't need these newlines in XML and Docbook outputs for
03012      paragraph seperation.  We have <para> element for that.  */
03013   if (xml)
03014     return;
03015 
03016   /* The insertion paragraph is no longer closed. */
03017   insertion_paragraph_closed = 0;
03018 
03019   if (paragraph_is_open && !must_start_paragraph)
03020     {
03021       int tindex = output_paragraph_offset;
03022 
03023       /* Back up to last non-newline/space character, forcing all such
03024          subsequent characters to be newlines.  This isn't strictly
03025          necessary, but a couple of functions use the presence of a newline
03026          to make decisions. */
03027       for (tindex = output_paragraph_offset - 1; tindex >= 0; --tindex)
03028         {
03029           int c = output_paragraph[tindex];
03030 
03031           if (c == ' '|| c == '\n')
03032             output_paragraph[tindex] = '\n';
03033           else
03034             break;
03035         }
03036 
03037       /* All trailing whitespace is ignored. */
03038       output_paragraph_offset = ++tindex;
03039 
03040       /* Break the line if that is appropriate. */
03041       if (paragraph_spacing >= 0)
03042         insert ('\n');
03043 
03044       /* Add as many blank lines as is specified in `paragraph_spacing'. */
03045       if (!force_flush_right)
03046         {
03047           for (i = 0; i < (paragraph_spacing - line_already_broken); i++)
03048             {
03049               insert ('\n');
03050               /* Don't need anything extra for HTML in usual case of no
03051                  extra paragraph spacing.  */
03052               if (html && i > 0)
03053                 insert_string ("<br>");
03054             }
03055         }
03056 
03057       /* If we are doing flush right indentation, then do it now
03058          on the paragraph (really a single line). */
03059       if (force_flush_right)
03060         do_flush_right_indentation ();
03061 
03062       flush_output ();
03063       paragraph_is_open = 0;
03064       no_indent = 0;
03065       output_column = 0;
03066     }
03067 
03068   ignore_blank_line ();
03069 }
03070 
03071 /* Make the last line just read look as if it were only a newline. */
03072 void
03073 ignore_blank_line (void)
03074 {
03075   last_inserted_character = '\n';
03076   last_char_was_newline = 1;
03077 }
03078 
03079 /* Align the end of the text in output_paragraph with fill_column. */
03080 static void
03081 do_flush_right_indentation (void)
03082 {
03083   char *temp;
03084   int temp_len;
03085 
03086   kill_self_indent (-1);
03087 
03088   if (output_paragraph[0] != '\n')
03089     {
03090       output_paragraph[output_paragraph_offset] = 0;
03091 
03092       if (output_paragraph_offset < fill_column)
03093         {
03094           int i;
03095 
03096           if (fill_column >= paragraph_buffer_len)
03097             output_paragraph =
03098               xrealloc (output_paragraph,
03099                         (paragraph_buffer_len += fill_column));
03100 
03101           temp_len = strlen ((char *)output_paragraph);
03102           temp = xmalloc (temp_len + 1);
03103           memcpy (temp, (char *)output_paragraph, temp_len);
03104 
03105           for (i = 0; i < fill_column - output_paragraph_offset; i++)
03106             output_paragraph[i] = ' ';
03107 
03108           memcpy ((char *)output_paragraph + i, temp, temp_len);
03109           free (temp);
03110           output_paragraph_offset = fill_column;
03111           adjust_braces_following (0, i);
03112         }
03113     }
03114 }
03115 
03116 /* Begin a new paragraph. */
03117 void
03118 start_paragraph (void)
03119 {
03120   /* First close existing one. */
03121   if (paragraph_is_open)
03122     close_paragraph ();
03123 
03124   /* In either case, the insertion paragraph is no longer closed. */
03125   insertion_paragraph_closed = 0;
03126 
03127   /* However, the paragraph is open! */
03128   paragraph_is_open = 1;
03129 
03130   /* If we MUST_START_PARAGRAPH, that simply means that start_paragraph ()
03131      had to be called before we would allow any other paragraph operations
03132      to have an effect. */
03133   if (!must_start_paragraph)
03134     {
03135       int amount_to_indent = 0;
03136 
03137       /* If doing indentation, then insert the appropriate amount. */
03138       if (!no_indent)
03139         {
03140           if (inhibit_paragraph_indentation)
03141             {
03142               amount_to_indent = current_indent;
03143               if (inhibit_paragraph_indentation < 0)
03144                 inhibit_paragraph_indentation++;
03145             }
03146           else if (paragraph_start_indent < 0)
03147             amount_to_indent = current_indent;
03148           else
03149             amount_to_indent = current_indent + paragraph_start_indent;
03150 
03151           if (amount_to_indent >= output_column)
03152             {
03153               amount_to_indent -= output_column;
03154               indent (amount_to_indent);
03155               output_column += amount_to_indent;
03156             }
03157         }
03158     }
03159   else
03160     must_start_paragraph = 0;
03161 }
03162 
03163 /* Insert the indentation specified by AMOUNT. */
03164 void
03165 indent (int amount)
03166 {
03167   /* For every START_POS saved within the brace stack which will be affected
03168      by this indentation, bump that start pos forward. */
03169   adjust_braces_following (output_paragraph_offset, amount);
03170 
03171   while (--amount >= 0)
03172     insert (' ');
03173 }
03174 
03175 /* Search forward for STRING in input_text.
03176    FROM says where where to start. */
03177 int
03178 search_forward (char *string, int from)
03179 {
03180   int len = strlen (string);
03181 
03182   while (from < input_text_length)
03183     {
03184       if (strncmp (input_text + from, string, len) == 0)
03185         return from;
03186       from++;
03187     }
03188   return -1;
03189 }
03190 
03191 /* search_forward until n characters.  */
03192 int
03193 search_forward_until_pos (char *string, int from, int end_pos)
03194 {
03195   int save_input_text_length = input_text_length;
03196   input_text_length = end_pos;
03197 
03198   from = search_forward (string, from);
03199 
03200   input_text_length = save_input_text_length;
03201 
03202   return from;
03203 }
03204 
03205 /* Return next non-whitespace and non-cr character.  */
03206 int
03207 next_nonwhitespace_character (void)
03208 {
03209   /* First check the current input_text.  Start from the next char because
03210      we already have input_text[input_text_offset] in ``current''.  */
03211   int pos = input_text_offset + 1;
03212 
03213   while (pos < input_text_length)
03214     {
03215       if (!cr_or_whitespace(input_text[pos]))
03216         return input_text[pos];
03217       pos++;
03218     }
03219 
03220   { /* Can't find a valid character, so go through filestack
03221        in case we are doing @include or expanding a macro.  */
03222     FSTACK *tos = filestack;
03223 
03224     while (tos)
03225       {
03226         int tmp_input_text_length = filestack->size;
03227         int tmp_input_text_offset = filestack->offset;
03228         char *tmp_input_text = filestack->text;
03229 
03230         while (tmp_input_text_offset < tmp_input_text_length)
03231           {
03232             if (!cr_or_whitespace(tmp_input_text[tmp_input_text_offset]))
03233               return tmp_input_text[tmp_input_text_offset];
03234             tmp_input_text_offset++;
03235           }
03236 
03237         tos = tos->next;
03238       }
03239   }
03240 
03241   return -1;
03242 }
03243 
03244 /* An external image is a reference, kind of.  The parsing is (not
03245    coincidentally) similar, anyway.  */
03246 void
03247 cm_image (int arg)
03248 {
03249   char *name_arg, *w_arg, *h_arg, *alt_arg, *ext_arg;
03250 
03251   if (arg == END)
03252     return;
03253 
03254   name_arg = get_xref_token (1); /* expands all macros in image */
03255   w_arg = get_xref_token (0);
03256   h_arg = get_xref_token (0);
03257   alt_arg = get_xref_token (1); /* expands all macros in alt text */
03258   ext_arg = get_xref_token (0);
03259 
03260   if (*name_arg)
03261     {
03262       struct stat file_info;
03263       char *pathname = NULL;
03264       char *fullname = xmalloc (strlen (name_arg)
03265                        + (ext_arg && *ext_arg ? strlen (ext_arg) + 1: 4) + 1);
03266 
03267       if (ext_arg && *ext_arg)
03268         {
03269           sprintf (fullname, "%s%s", name_arg, ext_arg);
03270           if (access (fullname, R_OK) != 0)
03271             pathname = get_file_info_in_path (fullname, include_files_path,
03272                                               &file_info);
03273 
03274          if (pathname == NULL)
03275            {
03276              /* Backwards compatibility (4.6 <= version < 4.7):
03277                try prefixing @image's EXTENSION parameter with a period. */
03278              sprintf (fullname, "%s.%s", name_arg, ext_arg);
03279              if (access (fullname, R_OK) != 0)
03280               pathname = get_file_info_in_path (fullname, include_files_path,
03281                                             &file_info);
03282            }
03283         }
03284       else
03285         {
03286           sprintf (fullname, "%s.png", name_arg);
03287           if (access (fullname, R_OK) != 0) {
03288             pathname = get_file_info_in_path (fullname,
03289                                               include_files_path, &file_info);
03290             if (pathname == NULL) {
03291               sprintf (fullname, "%s.jpg", name_arg);
03292               if (access (fullname, R_OK) != 0) {
03293                 sprintf (fullname, "%s.gif", name_arg);
03294                 if (access (fullname, R_OK) != 0) {
03295                   pathname = get_file_info_in_path (fullname,
03296                                                include_files_path, &file_info);
03297                 }
03298               }
03299             }
03300           }
03301         }
03302 
03303       if (html)
03304         {
03305           int image_in_div = 0;
03306 
03307           if (pathname == NULL && access (fullname, R_OK) != 0)
03308             {
03309               line_error(_("@image file `%s' (for HTML) not readable: %s"),
03310                              fullname, strerror (errno));
03311               return;
03312             }
03313           if (pathname != NULL && access (pathname, R_OK) != 0)
03314             {
03315               line_error (_("No such file `%s'"),
03316                           fullname);
03317               return;
03318             }
03319 
03320           if (!paragraph_is_open)
03321             {
03322               add_html_block_elt ("<div class=\"block-image\">");
03323               image_in_div = 1;
03324             }
03325 
03326           add_html_elt ("<img src=");
03327           add_word_args ("\"%s\"", fullname);
03328           add_html_elt (" alt=");
03329           add_word_args ("\"%s\">", 
03330               escape_string (*alt_arg ? text_expansion (alt_arg) : fullname));
03331 
03332           if (image_in_div)
03333             add_html_block_elt ("</div>");
03334         }
03335       else if (xml && docbook)
03336         xml_insert_docbook_image (name_arg);
03337       else if (xml)
03338         {
03339           extern int xml_in_para;
03340           extern int xml_no_para;
03341           int elt = xml_in_para ? INLINEIMAGE : IMAGE;
03342 
03343           if (!xml_in_para)
03344             xml_no_para++;
03345 
03346           xml_insert_element_with_attribute (elt,
03347               START, "width=\"%s\" height=\"%s\" name=\"%s\" extension=\"%s\"",
03348               w_arg, h_arg, name_arg, ext_arg);
03349           xml_insert_element (IMAGEALTTEXT, START);
03350           execute_string ("%s", alt_arg);
03351           xml_insert_element (IMAGEALTTEXT, END);
03352           xml_insert_element (elt, END);
03353 
03354           if (!xml_in_para)
03355             xml_no_para--;
03356         }
03357       else
03358         { /* Try to open foo.EXT or foo.txt.  */
03359           FILE *image_file;
03360           char *txtpath = NULL;
03361           char *txtname = xmalloc (strlen (name_arg)
03362                                    + (ext_arg && *ext_arg
03363                                       ? strlen (ext_arg) : 4) + 1);
03364           strcpy (txtname, name_arg);
03365           strcat (txtname, ".txt");
03366           image_file = fopen (txtname, "r");
03367           if (image_file == NULL)
03368             {
03369               txtpath = get_file_info_in_path (txtname,
03370                                                include_files_path, &file_info);
03371               if (txtpath != NULL)
03372                 image_file = fopen (txtpath, "r");
03373             }
03374 
03375           if (image_file != NULL
03376               || access (fullname, R_OK) == 0
03377               || (pathname != NULL && access (pathname, R_OK) == 0))
03378             {
03379               int ch;
03380               int save_inhibit_indentation = inhibit_paragraph_indentation;
03381               int save_filling_enabled = filling_enabled;
03382               int image_in_brackets = paragraph_is_open;
03383 
03384               /* Write magic ^@^H[image ...^@^H] cookie in the info file, if
03385                  there's an accompanying bitmap.  Otherwise just include the
03386                  text image.  In the plaintext output, always include the text
03387                  image without the magic cookie.  */
03388               int use_magic_cookie = !no_headers
03389                 && access (fullname, R_OK) == 0 && !STREQ (fullname, txtname);
03390 
03391               inhibit_paragraph_indentation = 1;
03392               filling_enabled = 0;
03393               last_char_was_newline = 0;
03394 
03395               if (use_magic_cookie)
03396                 {
03397                   add_char ('\0');
03398                   add_word ("\010[image");
03399 
03400                   if (access (fullname, R_OK) == 0
03401                       || (pathname != NULL && access (pathname, R_OK) == 0))
03402                     add_word_args (" src=\"%s\"", fullname);
03403 
03404                   if (*alt_arg)
03405                     add_word_args (" alt=\"%s\"", alt_arg);
03406                 }
03407 
03408               if (image_file != NULL)
03409                 {
03410                   if (use_magic_cookie)
03411                     add_word (" text=\"");
03412 
03413                   if (image_in_brackets)
03414                     add_char ('[');
03415 
03416                   /* Maybe we need to remove the final newline if the image
03417                      file is only one line to allow in-line images.  On the
03418                      other hand, they could just make the file without a
03419                      final newline.  */
03420                   while ((ch = getc (image_file)) != EOF)
03421                     {
03422                       if (use_magic_cookie && (ch == '"' || ch == '\\'))
03423                         add_char ('\\');
03424                       add_char (ch);
03425                     }
03426 
03427                   if (image_in_brackets)
03428                     add_char (']');
03429                   
03430                   if (use_magic_cookie)
03431                     add_char ('"');
03432 
03433                   if (fclose (image_file) != 0)
03434                     perror (txtname);
03435                 }
03436 
03437               if (use_magic_cookie)
03438                 {
03439                   add_char ('\0');
03440                   add_word ("\010]");
03441                 }
03442 
03443               inhibit_paragraph_indentation = save_inhibit_indentation;
03444               filling_enabled = save_filling_enabled;
03445             }
03446           else
03447             warning (_("@image file `%s' (for text) unreadable: %s"),
03448                         txtname, strerror (errno));
03449         }
03450 
03451       free (fullname);
03452       if (pathname)
03453         free (pathname);
03454     }
03455   else
03456     line_error (_("@image missing filename argument"));
03457 
03458   if (name_arg)
03459     free (name_arg);
03460   if (w_arg)
03461     free (w_arg);
03462   if (h_arg)
03463     free (h_arg);
03464   if (alt_arg)
03465     free (alt_arg);
03466   if (ext_arg)
03467     free (ext_arg);
03468 }
03469 
03470 /* Conditionals.  */
03471 
03472 /* A structure which contains `defined' variables. */
03473 typedef struct defines {
03474   struct defines *next;
03475   char *name;
03476   char *value;
03477 } DEFINE;
03478 
03479 /* The linked list of `set' defines. */
03480 DEFINE *defines = NULL;
03481 
03482 /* Add NAME to the list of `set' defines. */
03483 static void
03484 set (char *name, char *value)
03485 {
03486   DEFINE *temp;
03487 
03488   for (temp = defines; temp; temp = temp->next)
03489     if (strcmp (name, temp->name) == 0)
03490       {
03491         free (temp->value);
03492         temp->value = xstrdup (value);
03493         return;
03494       }
03495 
03496   temp = xmalloc (sizeof (DEFINE));
03497   temp->next = defines;
03498   temp->name = xstrdup (name);
03499   temp->value = xstrdup (value);
03500   defines = temp;
03501 
03502   if (xml && !docbook)
03503     {
03504       xml_insert_element_with_attribute (SETVALUE, START, "name=\"%s\"", name);
03505       execute_string ("%s", value);
03506       xml_insert_element (SETVALUE, END);
03507     }
03508 }
03509 
03510 /* Remove NAME from the list of `set' defines. */
03511 static void
03512 clear (char *name)
03513 {
03514   DEFINE *temp, *last;
03515 
03516   last = NULL;
03517   temp = defines;
03518 
03519   while (temp)
03520     {
03521       if (strcmp (temp->name, name) == 0)
03522         {
03523           if (last)
03524             last->next = temp->next;
03525           else
03526             defines = temp->next;
03527 
03528           free (temp->name);
03529           free (temp->value);
03530           free (temp);
03531           break;
03532         }
03533       last = temp;
03534       temp = temp->next;
03535     }
03536 
03537   if (xml && !docbook)
03538     {
03539       xml_insert_element_with_attribute (CLEARVALUE, START, "name=\"%s\"", name);
03540       xml_insert_element (CLEARVALUE, END);
03541     }
03542 }
03543 
03544 /* Return the value of NAME.  The return value is NULL if NAME is unset. */
03545 static char *
03546 set_p (char *name)
03547 {
03548   DEFINE *temp;
03549 
03550   for (temp = defines; temp; temp = temp->next)
03551     if (strcmp (temp->name, name) == 0)
03552       return temp->value;
03553 
03554   return NULL;
03555 }
03556 
03557 /* Create a variable whose name appears as the first word on this line. */
03558 void
03559 cm_set (void)
03560 {
03561   handle_variable (SET);
03562 }
03563 
03564 /* Remove a variable whose name appears as the first word on this line. */
03565 void
03566 cm_clear (void)
03567 {
03568   handle_variable (CLEAR);
03569 }
03570 
03571 void
03572 cm_ifset (void)
03573 {
03574   handle_variable (IFSET);
03575 }
03576 
03577 void
03578 cm_ifclear (void)
03579 {
03580   handle_variable (IFCLEAR);
03581 }
03582 
03583 /* This command takes braces, but we parse the contents specially, so we
03584    don't use the standard brace popping code.
03585 
03586    The syntax @ifeq{arg1, arg2, texinfo-commands} performs texinfo-commands
03587    if ARG1 and ARG2 caselessly string compare to the same string, otherwise,
03588    it produces no output. */
03589 void
03590 cm_ifeq (void)
03591 {
03592   char **arglist;
03593 
03594   arglist = get_brace_args (0);
03595 
03596   if (arglist)
03597     {
03598       if (array_len (arglist) > 1)
03599         {
03600           if ((strcasecmp (arglist[0], arglist[1]) == 0) &&
03601               (arglist[2]))
03602             execute_string ("%s\n", arglist[2]);
03603         }
03604 
03605       free_array (arglist);
03606     }
03607 }
03608 
03609 void
03610 cm_value (int arg, int start_pos, int end_pos)
03611 {
03612   static int value_level = 0, saved_meta_pos = -1;
03613 
03614   /* xml_add_char() skips any content inside menus when output format is
03615      Docbook, so @value{} is no use there.  Also start_pos and end_pos does not
03616      get updated, causing name to be empty string.  So just return.  */
03617    if (docbook && in_menu)
03618      return;
03619 
03620   /* All the text after @value{ upto the matching } will eventually
03621      disappear from output_paragraph, when this function is called
03622      with ARG == END.  If the text produced until then sets
03623      meta_char_pos, we will need to restore it to the value it had
03624      before @value was seen.  So we need to save the previous value
03625      of meta_char_pos here.  */
03626   if (arg == START)
03627     {
03628       /* If we are already inside some outer @value, don't overwrite
03629          the value saved in saved_meta_pos.  */
03630       if (!value_level)
03631         saved_meta_pos = meta_char_pos;
03632       value_level++;
03633       /* While the argument of @value is processed, we need to inhibit
03634          textual transformations like "--" into "-", since @set didn't
03635          do that when it grabbed the name of the variable.  */
03636       in_fixed_width_font++;
03637     }
03638   else
03639     {
03640       char *name = (char *) &output_paragraph[start_pos];
03641       char *value;
03642       output_paragraph[end_pos] = 0;
03643       name = xstrdup (name);
03644       value = set_p (name);
03645       output_column -= end_pos - start_pos;
03646       output_paragraph_offset = start_pos;
03647 
03648       /* Restore the previous value of meta_char_pos if the stuff
03649          inside this @value{} moved it.  */
03650       if (saved_meta_pos == -1) /* can't happen inside @value{} */
03651         abort ();
03652       if (value_level == 1
03653           && meta_char_pos >= start_pos && meta_char_pos < end_pos)
03654         {
03655           meta_char_pos = saved_meta_pos;
03656           saved_meta_pos = -1;
03657         }
03658       value_level--;
03659       /* No need to decrement in_fixed_width_font, since before
03660          we are called with arg == END, the reader loop already
03661          popped the brace stack, which restored in_fixed_width_font,
03662          among other things.  */
03663 
03664       if (value)
03665        {
03666          /* We need to get past the closing brace since the value may
03667             expand to a context-sensitive macro (e.g. @xref) and produce
03668             spurious warnings */
03669          input_text_offset++; 
03670          execute_string ("%s", value);
03671          input_text_offset--;
03672        }
03673       else
03674        {
03675           warning (_("undefined flag: %s"), name);
03676           add_word_args (_("{No value for `%s'}"), name);
03677        }
03678 
03679       free (name);
03680     }
03681 }
03682 
03683 /* Set, clear, or conditionalize based on ACTION. */
03684 static void
03685 handle_variable (int action)
03686 {
03687   char *name;
03688 
03689   get_rest_of_line (0, &name);
03690   /* If we hit the end of text in get_rest_of_line, backing up
03691      input pointer will cause the last character of the last line
03692      be pushed back onto the input, which is wrong.  */
03693   if (input_text_offset < input_text_length)
03694     backup_input_pointer ();
03695   handle_variable_internal (action, name);
03696   free (name);
03697 }
03698 
03699 static void
03700 handle_variable_internal (int action, char *name)
03701 {
03702   char *temp;
03703   int delimiter, additional_text_present = 0;
03704 
03705   /* Only the first word of NAME is a valid tag. */
03706   temp = name;
03707   delimiter = 0;
03708   while (*temp && (delimiter || !whitespace (*temp)))
03709     {
03710 /* #if defined (SET_WITH_EQUAL) */
03711       if (*temp == '"' || *temp == '\'')
03712         {
03713           if (*temp == delimiter)
03714             delimiter = 0;
03715           else
03716             delimiter = *temp;
03717         }
03718 /* #endif SET_WITH_EQUAL */
03719       temp++;
03720     }
03721 
03722   if (*temp)
03723     additional_text_present++;
03724 
03725   *temp = 0;
03726 
03727   if (!*name)
03728     line_error (_("%c%s requires a name"), COMMAND_PREFIX, command);
03729   else
03730     {
03731       switch (action)
03732         {
03733         case SET:
03734           {
03735             char *value;
03736 
03737 #if defined (SET_WITH_EQUAL)
03738             /* Allow a value to be saved along with a variable.  The value is
03739                the text following an `=' sign in NAME, if any is present. */
03740 
03741             for (value = name; *value && *value != '='; value++);
03742 
03743             if (*value)
03744               *value++ = 0;
03745 
03746             if (*value == '"' || *value == '\'')
03747               {
03748                 value++;
03749                 value[strlen (value) - 1] = 0;
03750               }
03751 
03752 #else /* !SET_WITH_EQUAL */
03753             /* The VALUE of NAME is the remainder of the line sans
03754                whitespace. */
03755             if (additional_text_present)
03756               {
03757                 value = temp + 1;
03758                 canon_white (value);
03759               }
03760             else
03761               value = "";
03762 #endif /* !SET_WITH_VALUE */
03763 
03764             set (name, value);
03765           }
03766           break;
03767 
03768         case CLEAR:
03769           clear (name);
03770           break;
03771 
03772         case IFSET:
03773         case IFCLEAR:
03774           /* If IFSET and NAME is not set, or if IFCLEAR and NAME is set,
03775              read lines from the the file until we reach a matching
03776              "@end CONDITION".  This means that we only take note of
03777              "@ifset/clear" and "@end" commands. */
03778           {
03779             char condition[8];
03780             int condition_len;
03781             int orig_line_number = line_number;
03782 
03783             if (action == IFSET)
03784               strcpy (condition, "ifset");
03785             else
03786               strcpy (condition, "ifclear");
03787 
03788             condition_len = strlen (condition);
03789 
03790           if ((action == IFSET && !set_p (name))
03791               || (action == IFCLEAR && set_p (name)))
03792             {
03793               int level = 0, done = 0;
03794 
03795               while (!done && input_text_offset < input_text_length)
03796                 {
03797                   char *freeable_line, *line;
03798 
03799                   get_rest_of_line (0, &freeable_line);
03800 
03801                   for (line = freeable_line; whitespace (*line); line++);
03802 
03803                   if (*line == COMMAND_PREFIX &&
03804                       (strncmp (line + 1, condition, condition_len) == 0))
03805                     level++;
03806                   else if (strncmp (line, "@end", 4) == 0)
03807                     {
03808                       char *cname = line + 4;
03809                       char *temp;
03810 
03811                       while (*cname && whitespace (*cname))
03812                         cname++;
03813                       temp = cname;
03814 
03815                       while (*temp && !whitespace (*temp))
03816                         temp++;
03817                       *temp = 0;
03818 
03819                       if (strcmp (cname, condition) == 0)
03820                         {
03821                           if (!level)
03822                             {
03823                               done = 1;
03824                             }
03825                           else
03826                             level--;
03827                         }
03828                     }
03829                   free (freeable_line);
03830                 }
03831 
03832               if (!done)
03833                 file_line_error (input_filename, orig_line_number,
03834                                  _("Reached eof before matching @end %s"),
03835                                  condition);
03836 
03837               /* We found the end of a false @ifset/ifclear.  If we are
03838                  in a menu, back up over the newline that ends the ifset,
03839                  since that newline may also begin the next menu entry. */
03840               break;
03841             }
03842           else
03843             {
03844               if (action == IFSET)
03845                 begin_insertion (ifset);
03846               else
03847                 begin_insertion (ifclear);
03848             }
03849           }
03850           break;
03851         }
03852     }
03853 }
03854 
03855 /* Execution of random text not in file. */
03856 typedef struct {
03857   char *string;                 /* The string buffer. */
03858   int size;                     /* The size of the buffer. */
03859   int in_use;                   /* Nonzero means string currently in use. */
03860 } EXECUTION_STRING;
03861 
03862 static EXECUTION_STRING **execution_strings = NULL;
03863 static int execution_strings_index = 0;
03864 static int execution_strings_slots = 0;
03865 
03866 static EXECUTION_STRING *
03867 get_execution_string (int initial_size)
03868 {
03869   int i = 0;
03870   EXECUTION_STRING *es = NULL;
03871 
03872   if (execution_strings)
03873     {
03874       for (i = 0; i < execution_strings_index; i++)
03875         if (execution_strings[i] && (execution_strings[i]->in_use == 0))
03876           {
03877             es = execution_strings[i];
03878             break;
03879           }
03880     }
03881 
03882   if (!es)
03883     {
03884       if (execution_strings_index + 1 >= execution_strings_slots)
03885         {
03886           execution_strings = xrealloc
03887             (execution_strings,
03888              (execution_strings_slots += 3) * sizeof (EXECUTION_STRING *));
03889           for (; i < execution_strings_slots; i++)
03890             execution_strings[i] = NULL;
03891         }
03892 
03893       execution_strings[execution_strings_index] =
03894         xmalloc (sizeof (EXECUTION_STRING));
03895       es = execution_strings[execution_strings_index];
03896       execution_strings_index++;
03897 
03898       es->size = 0;
03899       es->string = NULL;
03900       es->in_use = 0;
03901     }
03902 
03903   if (initial_size > es->size)
03904     {
03905       es->string = xrealloc (es->string, initial_size);
03906       es->size = initial_size;
03907     }
03908   return es;
03909 }
03910 
03911 /* Given a pointer to TEXT and its desired length NEW_LEN, find TEXT's
03912    entry in the execution_strings[] array and change the .STRING and
03913    .SIZE members of that entry as appropriate.  */
03914 void
03915 maybe_update_execution_strings (char **text, unsigned int new_len)
03916 {
03917   int i = 0;
03918 
03919   if (execution_strings)
03920     {
03921       for (i = 0; i < execution_strings_index; i++)
03922         if (execution_strings[i] && (execution_strings[i]->in_use == 1) &&
03923             execution_strings[i]->string == *text)
03924           {
03925             /* Don't ever shrink the string storage in execution_strings[]!
03926                execute_string assumes that it is always big enough to store
03927                every possible execution_string, and will break if that's
03928                not true.  So we only enlarge the string storage if the
03929                current size isn't big enough.  */
03930             if (execution_strings[i]->size < new_len)
03931               {
03932                 execution_strings[i]->string =
03933                   *text = xrealloc (*text, new_len + 1);
03934                 execution_strings[i]->size = new_len + 1;
03935               }
03936             return;
03937           }
03938     }
03939   /* We should *never* end up here, since if we are inside
03940      execute_string, TEXT is always in execution_strings[].  */
03941   abort ();
03942 }
03943 
03944 /* FIXME: this is an arbitrary limit.  */
03945 #define EXECUTE_STRING_MAX 16*1024
03946 
03947 /* Execute the string produced by formatting the ARGs with FORMAT.  This
03948    is like submitting a new file with @include. */
03949 void
03950 #if defined (VA_FPRINTF) && __STDC__
03951 execute_string (char *format, ...)
03952 #else
03953 execute_string (format, va_alist)
03954     char *format;
03955     va_dcl
03956 #endif
03957 {
03958   EXECUTION_STRING *es;
03959   char *temp_string, *temp_input_filename;
03960 #ifdef VA_FPRINTF
03961   va_list ap;
03962 #endif
03963   int insertion_level_at_start = insertion_level;
03964 
03965   es = get_execution_string (EXECUTE_STRING_MAX);
03966   temp_string = es->string;
03967   es->in_use = 1;
03968 
03969   VA_START (ap, format);
03970 #ifdef VA_SPRINTF
03971   VA_SPRINTF (temp_string, format, ap);
03972 #else
03973   sprintf (temp_string, format, a1, a2, a3, a4, a5, a6, a7, a8);
03974 #endif /* not VA_SPRINTF */
03975   va_end (ap);
03976 
03977   pushfile ();
03978   input_text_offset = 0;
03979   input_text = temp_string;
03980   input_text_length = strlen (temp_string);
03981   input_filename = xstrdup (input_filename);
03982   temp_input_filename = input_filename;
03983 
03984   executing_string++;
03985   reader_loop ();
03986 
03987   /* If insertion stack level changes during execution, that means a multiline
03988      command is used inside braces or @section ... kind of commands.  */
03989   if (insertion_level_at_start != insertion_level && !executing_macro)
03990     {
03991       line_error (_("Multiline command %c%s used improperly"),
03992           COMMAND_PREFIX,
03993           command);
03994       /* We also need to keep insertion_level intact to make sure warnings are
03995          issued for @end ... command.  */
03996       while (insertion_level > insertion_level_at_start)
03997         pop_insertion ();
03998     }
03999 
04000   popfile ();
04001   executing_string--;
04002   es->in_use = 0;
04003   free (temp_input_filename);
04004 }
04005 
04006 
04007 /* Return what would be output for STR (in newly-malloced memory), i.e.,
04008    expand Texinfo commands according to the current output format.  If
04009    IMPLICIT_CODE is set, expand @code{STR}.  This is generally used for
04010    short texts; filling, indentation, and html escapes are disabled.  */
04011 
04012 char *
04013 expansion (char *str, int implicit_code)
04014 {
04015   return maybe_escaped_expansion (str, implicit_code, 0);
04016 }
04017 
04018 
04019 /* Do HTML escapes according to DO_HTML_ESCAPE.  Needed in
04020    cm_printindex, q.v.  */
04021 
04022 char *
04023 maybe_escaped_expansion (char *str, int implicit_code, int do_html_escape)
04024 {
04025   char *result;
04026 
04027   /* Inhibit indentation and filling, so that extra newlines
04028      are not added to the expansion.  (This is undesirable if
04029      we write the expanded text to macro_expansion_output_stream.)  */
04030   int saved_filling_enabled = filling_enabled;
04031   int saved_indented_fill = indented_fill;
04032   int saved_no_indent = no_indent;
04033   int saved_escape_html = escape_html;
04034 
04035   filling_enabled = 0;
04036   indented_fill = 0;
04037   no_indent = 1;
04038   escape_html = do_html_escape;
04039 
04040   result = full_expansion (str, implicit_code);
04041 
04042   filling_enabled = saved_filling_enabled;
04043   indented_fill = saved_indented_fill;
04044   no_indent = saved_no_indent;
04045   escape_html = saved_escape_html;
04046 
04047   return result;
04048 }
04049 
04050 
04051 /* Expand STR (or @code{STR} if IMPLICIT_CODE is nonzero).  No change to
04052    any formatting parameters -- filling, indentation, html escapes,
04053    etc., are not reset.  Always returned in new memory.  */
04054 
04055 char *
04056 full_expansion (char *str, int implicit_code)
04057 {
04058   int length;
04059   char *result;
04060 
04061   /* Inhibit any real output.  */
04062   int start = output_paragraph_offset;
04063   int saved_paragraph_is_open = paragraph_is_open;
04064   int saved_output_column = output_column;
04065 
04066   /* More output state to save.  */
04067   int saved_meta_pos = meta_char_pos;
04068   int saved_last_char = last_inserted_character;
04069   int saved_last_nl = last_char_was_newline;
04070 
04071   /* If we are called in the middle of processing a command, we need
04072      to dup and save the global variable `command' (which holds the
04073      name of this command), since the recursive reader loop will free
04074      it from under our feet if it finds any macros in STR.  */
04075   char *saved_command = command ? xstrdup (command) : NULL;
04076 
04077   inhibit_output_flushing ();
04078   paragraph_is_open = 1;
04079   if (strlen (str) > (implicit_code
04080                       ? EXECUTE_STRING_MAX - 1 - sizeof("@code{}")
04081                       : EXECUTE_STRING_MAX - 1))
04082     line_error (_("`%.40s...' is too long for expansion; not expanded"), str);
04083   else
04084     execute_string (implicit_code ? "@code{%s}" : "%s", str);
04085   uninhibit_output_flushing ();
04086 
04087   /* Copy the expansion from the buffer.  */
04088   length = output_paragraph_offset - start;
04089   result = xmalloc (1 + length);
04090   memcpy (result, (char *) (output_paragraph + start), length);
04091   result[length] = 0;
04092 
04093   /* Pretend it never happened.  */
04094   free_and_clear (&command);
04095   command = saved_command;
04096 
04097   output_paragraph_offset = start;
04098   paragraph_is_open = saved_paragraph_is_open;
04099   output_column = saved_output_column;
04100 
04101   meta_char_pos = saved_meta_pos;
04102   last_inserted_character = saved_last_char;
04103   last_char_was_newline = saved_last_nl;
04104 
04105   return result;
04106 }
04107 
04108 
04109 /* Return text (info) expansion of STR no matter what the current output
04110    format is.  */
04111 
04112 char *
04113 text_expansion (char *str)
04114 {
04115   char *ret;
04116   int save_html = html;
04117   int save_xml = xml;
04118   int save_docbook = docbook;
04119 
04120   html = 0;
04121   xml = 0;
04122   docbook = 0;
04123   ret = expansion (str, 0);
04124   html = save_html;
04125   xml = save_xml;
04126   docbook = save_docbook;
04127 
04128   return ret;
04129 }
04130 
04131 
04132 /* Set the paragraph indentation variable to the value specified in STRING.
04133    Values can be:
04134      `asis': Don't change existing indentation.
04135      `none': Remove existing indentation.
04136         NUM: Indent NUM spaces at the starts of paragraphs.
04137              If NUM is zero, we assume `none'.
04138    Returns 0 if successful, or nonzero if STRING isn't one of the above. */
04139 int
04140 set_paragraph_indent (char *string)
04141 {
04142   if (strcmp (string, "asis") == 0 || strcmp (string, _("asis")) == 0)
04143     paragraph_start_indent = 0;
04144   else if (strcmp (string, "none") == 0 || strcmp (string, _("none")) == 0)
04145     paragraph_start_indent = -1;
04146   else
04147     {
04148       if (sscanf (string, "%d", &paragraph_start_indent) != 1)
04149         return -1;
04150       else
04151         {
04152           if (paragraph_start_indent == 0)
04153             paragraph_start_indent = -1;
04154         }
04155     }
04156   return 0;
04157 }