156164Sru/* makeinfo -- convert Texinfo source into other formats.
2146520Sru   $Id: makeinfo.c,v 1.74 2004/12/19 17:15:42 karl Exp $
321495Sjmacd
4116530Sru   Copyright (C) 1987, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
5146520Sru   2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
621495Sjmacd
721495Sjmacd   This program is free software; you can redistribute it and/or modify
821495Sjmacd   it under the terms of the GNU General Public License as published by
921495Sjmacd   the Free Software Foundation; either version 2, or (at your option)
1021495Sjmacd   any later version.
1121495Sjmacd
1221495Sjmacd   This program is distributed in the hope that it will be useful,
1321495Sjmacd   but WITHOUT ANY WARRANTY; without even the implied warranty of
1421495Sjmacd   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1521495Sjmacd   GNU General Public License for more details.
1621495Sjmacd
1721495Sjmacd   You should have received a copy of the GNU General Public License
1821495Sjmacd   along with this program; if not, write to the Free Software
1921495Sjmacd   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
2021495Sjmacd
21146520Sru   Original author of makeinfo: Brian Fox (bfox@ai.mit.edu).  */
2221495Sjmacd
2342664Smarkm#include "system.h"
2442664Smarkm#include "getopt.h"
2542664Smarkm
2656164Sru#define COMPILING_MAKEINFO
2756164Sru#include "makeinfo.h"
2856164Sru#include "cmds.h"
2956164Sru#include "files.h"
30146520Sru#include "float.h"
3156164Sru#include "footnote.h"
3256164Sru#include "html.h"
3356164Sru#include "index.h"
3456164Sru#include "insertion.h"
35116530Sru#include "lang.h"
3656164Sru#include "macro.h"
3756164Sru#include "node.h"
38146520Sru#include "sectioning.h"
3956164Sru#include "toc.h"
4093142Sru#include "xml.h"
4142664Smarkm
4242664Smarkm/* You can change some of the behavior of Makeinfo by changing the
4321495Sjmacd   following defines: */
4421495Sjmacd
4521495Sjmacd/* Define INDENT_PARAGRAPHS_IN_TABLE if you want the paragraphs which
4621495Sjmacd   appear within an @table, @ftable, or @itemize environment to have
4721495Sjmacd   standard paragraph indentation.  Without this, such paragraphs have
4821495Sjmacd   no starting indentation. */
4921495Sjmacd/* #define INDENT_PARAGRAPHS_IN_TABLE */
5021495Sjmacd
5121495Sjmacd/* Define PARAGRAPH_START_INDENT to be the amount of indentation that
5221495Sjmacd   the first lines of paragraphs receive by default, where no other
5321495Sjmacd   value has been specified.  Users can change this value on the command
5421495Sjmacd   line, with the --paragraph-indent option, or within the texinfo file,
5521495Sjmacd   with the @paragraphindent command. */
5621495Sjmacd#define PARAGRAPH_START_INDENT 3
5721495Sjmacd
5821495Sjmacd/* Define DEFAULT_PARAGRAPH_SPACING as the number of blank lines that you
5921495Sjmacd   wish to appear between paragraphs.  A value of 1 creates a single blank
6021495Sjmacd   line between paragraphs.  Paragraphs are defined by 2 or more consecutive
6121495Sjmacd   newlines in the input file (i.e., one or more blank lines). */
6221495Sjmacd#define DEFAULT_PARAGRAPH_SPACING 1
6321495Sjmacd
6456164Sru/* Global variables.  */
6521495Sjmacd
6621495Sjmacd/* The output file name. */
6756164Sruchar *output_filename = NULL;
6821495Sjmacd
6921495Sjmacd/* Name of the output file that the user elected to pass on the command line.
7021495Sjmacd   Such a name overrides any name found with the @setfilename command. */
7156164Sruchar *command_output_filename = NULL;
72100516Srustatic char *save_command_output_filename = NULL;
7321495Sjmacd
7421495Sjmacd#define INITIAL_PARAGRAPH_SPACE 5000
7521495Sjmacdint paragraph_buffer_len = INITIAL_PARAGRAPH_SPACE;
7621495Sjmacd
7721495Sjmacd/* The amount of indentation to add at the starts of paragraphs.
7821495Sjmacd   0 means don't change existing indentation at paragraph starts.
7921495Sjmacd   > 0 is amount to indent new paragraphs by.
8021495Sjmacd   < 0 means indent to column zero by removing indentation if necessary.
8121495Sjmacd
8221495Sjmacd   This is normally zero, but some people prefer paragraph starts to be
8321495Sjmacd   somewhat more indented than paragraph bodies.  A pretty value for
8421495Sjmacd   this is 3. */
8521495Sjmacdint paragraph_start_indent = PARAGRAPH_START_INDENT;
8621495Sjmacd
8721495Sjmacd/* Indentation that is pending insertion.  We have this for hacking lines
8821495Sjmacd   which look blank, but contain whitespace.  We want to treat those as
8921495Sjmacd   blank lines. */
9021495Sjmacdint pending_indent = 0;
9121495Sjmacd
9221495Sjmacd/* The index in our internal command table of the currently
9321495Sjmacd   executing command. */
9421495Sjmacdint command_index;
9521495Sjmacd
9621495Sjmacd/* A search string which is used to find the first @setfilename. */
9721495Sjmacdchar setfilename_search[] =
9821495Sjmacd  { COMMAND_PREFIX,
9942664Smarkm      's', 'e', 't', 'f', 'i', 'l', 'e', 'n', 'a', 'm', 'e', 0 };
10021495Sjmacd
10142664Smarkm/* Values for calling handle_variable_internal (). */
10242664Smarkm#define SET     1
10342664Smarkm#define CLEAR   2
10442664Smarkm#define IFSET   3
10542664Smarkm#define IFCLEAR 4
10642664Smarkm
10721495Sjmacd/* Flags controlling the operation of the program. */
10821495Sjmacd
10942664Smarkm/* Default is to remove output if there were errors.  */
11042664Smarkmint force = 0;
11142664Smarkm
11221495Sjmacd/* Default is to notify users of bad choices. */
11321495Sjmacdint print_warnings = 1;
11421495Sjmacd
11521495Sjmacd/* Number of errors that we tolerate on a given fileset. */
11621495Sjmacdint max_error_level = 100;
11721495Sjmacd
11856164Sru/* The actual last inserted character.  Note that this may be something
11956164Sru   other than NEWLINE even if last_char_was_newline is 1. */
12056164Sruint last_inserted_character = 0;
12121495Sjmacd
12256164Sru/* Nonzero means that a newline character has already been
12356164Sru   inserted, so close_paragraph () should insert one less. */
12456164Sruint line_already_broken = 0;
12521495Sjmacd
12656164Sru/* When nonzero we have finished an insertion (see end_insertion ()) and we
12756164Sru   want to ignore false continued paragraph closings. */
12856164Sruint insertion_paragraph_closed = 0;
12921495Sjmacd
13056164Sru/* Nonzero means attempt to make all of the lines have fill_column width. */
13156164Sruint do_justification = 0;
13221495Sjmacd
13393142Sru/* Nonzero means don't replace whitespace with &nbsp; in HTML mode.  */
13493142Sruint in_html_elt = 0;
13593142Sru
136146520Sru/* Nonzero means we are inserting a block level HTML element that must not be
137146520Sru   enclosed in a <p>, such as <ul>, <ol> and <h?>.  */
138146520Sruint in_html_block_level_elt = 0;
139146520Sru
140114477Sru/* True when expanding a macro definition.  */
141114477Srustatic int executing_macro = 0;
142114477Sru
143146520Sru/* True when we are inside a <li> block of a menu.  */
144146520Srustatic int in_menu_item = 0;
145146520Sru
14621495Sjmacdtypedef struct brace_element
14721495Sjmacd{
14821495Sjmacd  struct brace_element *next;
14921495Sjmacd  COMMAND_FUNCTION *proc;
15056164Sru  char *command;
15121495Sjmacd  int pos, line;
15221495Sjmacd  int in_fixed_width_font;
15321495Sjmacd} BRACE_ELEMENT;
15421495Sjmacd
15556164SruBRACE_ELEMENT *brace_stack = NULL;
15621495Sjmacd
157146520Srustatic void convert_from_file (char *name);
158146520Srustatic void convert_from_loaded_file (char *name);
159146520Srustatic void convert_from_stream (FILE *stream, char *name);
160146520Srustatic void do_flush_right_indentation (void);
161146520Srustatic void handle_variable (int action);
162146520Srustatic void handle_variable_internal (int action, char *name);
163146520Srustatic void init_brace_stack (void);
164146520Srustatic void init_internals (void);
165146520Srustatic void pop_and_call_brace (void);
166146520Srustatic void remember_brace (COMMAND_FUNCTION (*proc));
167146520Srustatic int end_of_sentence_p (void);
16821495Sjmacd
169146520Sruvoid maybe_update_execution_strings (char **text, unsigned int new_len);
17021495Sjmacd
17156164Sru/* Error handling.  */
17221495Sjmacd
17342664Smarkm/* Number of errors encountered. */
17442664Smarkmint errors_printed = 0;
17542664Smarkm
176146520Sru/* Remember that an error has been printed.  If more than
177146520Sru   max_error_level have been printed, then exit the program. */
178146520Srustatic void
179146520Sruremember_error (void)
180146520Sru{
181146520Sru  errors_printed++;
182146520Sru  if (max_error_level && (errors_printed > max_error_level))
183146520Sru    {
184146520Sru      fprintf (stderr, _("Too many errors!  Gave up.\n"));
185146520Sru      flush_file_stack ();
186146520Sru      if (errors_printed - max_error_level < 2)
187146520Sru	cm_bye ();
188146520Sru      xexit (1);
189146520Sru    }
190146520Sru}
191146520Sru
19242664Smarkm/* Print the last error gotten from the file system. */
19342664Smarkmint
194146520Srufs_error (char *filename)
19542664Smarkm{
19642664Smarkm  remember_error ();
19742664Smarkm  perror (filename);
19856164Sru  return 0;
19942664Smarkm}
20042664Smarkm
20142664Smarkm/* Print an error message, and return false. */
20242664Smarkmvoid
20342664Smarkm#if defined (VA_FPRINTF) && __STDC__
204116530Sruerror (const char *format, ...)
20542664Smarkm#else
20642664Smarkmerror (format, va_alist)
207116530Sru     const char *format;
20842664Smarkm     va_dcl
20942664Smarkm#endif
21042664Smarkm{
21142664Smarkm#ifdef VA_FPRINTF
21242664Smarkm  va_list ap;
21342664Smarkm#endif
21442664Smarkm
21542664Smarkm  remember_error ();
21642664Smarkm
21742664Smarkm  VA_START (ap, format);
21842664Smarkm#ifdef VA_FPRINTF
21942664Smarkm  VA_FPRINTF (stderr, format, ap);
22042664Smarkm#else
22142664Smarkm  fprintf (stderr, format, a1, a2, a3, a4, a5, a6, a7, a8);
22242664Smarkm#endif /* not VA_FPRINTF */
22342664Smarkm  va_end (ap);
22442664Smarkm
22542664Smarkm  putc ('\n', stderr);
22642664Smarkm}
22742664Smarkm
22893142Sru/* Just like error (), but print the input file and line number as well. */
22942664Smarkmvoid
23042664Smarkm#if defined (VA_FPRINTF) && __STDC__
231116530Srufile_line_error (char *infile, int lno, const char *format, ...)
23293142Sru#else
23393142Srufile_line_error (infile, lno, format, va_alist)
23493142Sru   char *infile;
23593142Sru   int lno;
236116530Sru   const char *format;
23793142Sru   va_dcl
23893142Sru#endif
23993142Sru{
24093142Sru#ifdef VA_FPRINTF
24193142Sru  va_list ap;
24293142Sru#endif
24393142Sru
24493142Sru  remember_error ();
24593142Sru  fprintf (stderr, "%s:%d: ", infile, lno);
24693142Sru
24793142Sru  VA_START (ap, format);
24893142Sru#ifdef VA_FPRINTF
24993142Sru  VA_FPRINTF (stderr, format, ap);
25093142Sru#else
25193142Sru  fprintf (stderr, format, a1, a2, a3, a4, a5, a6, a7, a8);
25293142Sru#endif /* not VA_FPRINTF */
25393142Sru  va_end (ap);
25493142Sru
25593142Sru  fprintf (stderr, ".\n");
25693142Sru}
25793142Sru
25893142Sru/* Just like file_line_error (), but take the input file and the line
25993142Sru   number from global variables. */
26093142Sruvoid
26193142Sru#if defined (VA_FPRINTF) && __STDC__
262116530Sruline_error (const char *format, ...)
26342664Smarkm#else
26442664Smarkmline_error (format, va_alist)
265116530Sru   const char *format;
26642664Smarkm   va_dcl
26742664Smarkm#endif
26842664Smarkm{
26942664Smarkm#ifdef VA_FPRINTF
27042664Smarkm  va_list ap;
27142664Smarkm#endif
27242664Smarkm
27342664Smarkm  remember_error ();
27442664Smarkm  fprintf (stderr, "%s:%d: ", input_filename, line_number);
27542664Smarkm
27642664Smarkm  VA_START (ap, format);
27742664Smarkm#ifdef VA_FPRINTF
27842664Smarkm  VA_FPRINTF (stderr, format, ap);
27942664Smarkm#else
28042664Smarkm  fprintf (stderr, format, a1, a2, a3, a4, a5, a6, a7, a8);
28142664Smarkm#endif /* not VA_FPRINTF */
28242664Smarkm  va_end (ap);
28342664Smarkm
28442664Smarkm  fprintf (stderr, ".\n");
28542664Smarkm}
28642664Smarkm
28742664Smarkmvoid
28842664Smarkm#if defined (VA_FPRINTF) && __STDC__
289116530Sruwarning (const char *format, ...)
29042664Smarkm#else
29142664Smarkmwarning (format, va_alist)
292116530Sru     const char *format;
29342664Smarkm     va_dcl
29442664Smarkm#endif
29542664Smarkm{
29642664Smarkm#ifdef VA_FPRINTF
29742664Smarkm  va_list ap;
29842664Smarkm#endif
29942664Smarkm
30042664Smarkm  if (print_warnings)
30142664Smarkm    {
30242664Smarkm      fprintf (stderr, _("%s:%d: warning: "), input_filename, line_number);
30342664Smarkm
30442664Smarkm      VA_START (ap, format);
30542664Smarkm#ifdef VA_FPRINTF
30642664Smarkm      VA_FPRINTF (stderr, format, ap);
30742664Smarkm#else
30842664Smarkm      fprintf (stderr, format, a1, a2, a3, a4, a5, a6, a7, a8);
30942664Smarkm#endif /* not VA_FPRINTF */
31042664Smarkm      va_end (ap);
31142664Smarkm
31242664Smarkm      fprintf (stderr, ".\n");
31342664Smarkm    }
31442664Smarkm}
31542664Smarkm
31642664Smarkm
31756164Sru/* The other side of a malformed expression. */
318146520Srustatic void
319146520Srumisplaced_brace (void)
32056164Sru{
32156164Sru  line_error (_("Misplaced %c"), '}');
32256164Sru}
32342664Smarkm
32456164Sru/* Main.  */
32521495Sjmacd
32656164Sru/* Display the version info of this invocation of Makeinfo. */
32756164Srustatic void
328146520Sruprint_version_info (void)
32956164Sru{
33056164Sru  printf ("makeinfo (GNU %s) %s\n", PACKAGE, VERSION);
33156164Sru}
33256164Sru
33356164Sru/* If EXIT_VALUE is zero, print the full usage message to stdout.
33456164Sru   Otherwise, just say to use --help for more info.
33556164Sru   Then exit with EXIT_VALUE. */
33656164Srustatic void
337146520Sruusage (int exit_value)
33856164Sru{
33956164Sru  if (exit_value != 0)
34056164Sru    fprintf (stderr, _("Try `%s --help' for more information.\n"), progname);
34156164Sru  else
34256164Sru  {
34393142Sru    printf (_("Usage: %s [OPTION]... TEXINFO-FILE...\n"), progname);
344116530Sru    puts ("");
345100516Sru
34693142Sru    puts (_("\
34793142SruTranslate Texinfo source documentation to various other formats, by default\n\
34893142SruInfo files suitable for reading online with Emacs or standalone GNU Info.\n"));
34993142Sru
35056164Sru    printf (_("\
35193142SruGeneral options:\n\
35293142Sru      --error-limit=NUM       quit after NUM errors (default %d).\n\
35393142Sru      --force                 preserve output even if errors.\n\
35493142Sru      --help                  display this help and exit.\n\
35593142Sru      --no-validate           suppress node cross-reference validation.\n\
35693142Sru      --no-warn               suppress warnings (but not errors).\n\
35793142Sru      --reference-limit=NUM   warn about at most NUM references (default %d).\n\
35893142Sru  -v, --verbose               explain what is being done.\n\
35993142Sru      --version               display version information and exit.\n"),
36093142Sru            max_error_level, reference_warning_limit);
361146520Sru    puts ("");
36293142Sru
36393142Sru     /* xgettext: no-wrap */
364114477Sru    puts (_("\
36593142SruOutput format selection (default is to produce Info):\n\
366146520Sru      --docbook             output Docbook XML rather than Info.\n\
36793142Sru      --html                output HTML rather than Info.\n\
368114477Sru      --xml                 output Texinfo XML rather than Info.\n\
369146520Sru      --plaintext           output plain text rather than Info.\n\
37093142Sru"));
37193142Sru
372114477Sru    puts (_("\
37393142SruGeneral output options:\n\
37493142Sru  -E, --macro-expand FILE   output macro-expanded source to FILE.\n\
37593142Sru                            ignoring any @setfilename.\n\
376100516Sru      --no-headers          suppress node separators, Node: lines, and menus\n\
377100516Sru                              from Info output (thus producing plain text)\n\
378100516Sru                              or from HTML (thus producing shorter output);\n\
379100516Sru                              also, write to standard output by default.\n\
38093142Sru      --no-split            suppress splitting of Info or HTML output,\n\
38193142Sru                            generate only one output file.\n\
38293142Sru      --number-sections     output chapter and sectioning numbers.\n\
38393142Sru  -o, --output=FILE         output to FILE (directory if split HTML),\n\
38493142Sru"));
38593142Sru
386114477Sru    printf (_("\
38793142SruOptions for Info and plain text:\n\
38893142Sru      --enable-encoding       output accented and special characters in\n\
38993142Sru                                Info output based on @documentencoding.\n\
39093142Sru      --fill-column=NUM       break Info lines at NUM characters (default %d).\n\
39193142Sru      --footnote-style=STYLE  output footnotes in Info according to STYLE:\n\
39293142Sru                                `separate' to put them in their own node;\n\
39393142Sru                                `end' to put them at the end of the node\n\
39493142Sru                                  in which they are defined (default).\n\
39593142Sru      --paragraph-indent=VAL  indent Info paragraphs by VAL spaces (default %d).\n\
39693142Sru                                If VAL is `none', do not indent; if VAL is\n\
39793142Sru                                `asis', preserve existing indentation.\n\
39893142Sru      --split-size=NUM        split Info files at size NUM (default %d).\n"),
39993142Sru             fill_column, paragraph_start_indent,
40093142Sru             DEFAULT_SPLIT_SIZE);
401146520Sru    puts ("");
402114477Sru
403114477Sru    puts (_("\
404116530SruOptions for HTML:\n\
405116530Sru      --css-include=FILE        include FILE in HTML <style> output;\n\
406116530Sru                                  read stdin if FILE is -.\n\
407116530Sru"));
408116530Sru
409146520Sru    printf (_("\
410146520SruOptions for XML and Docbook:\n\
411146520Sru      --output-indent=VAL       indent XML elements by VAL spaces (default %d).\n\
412146520Sru                                  If VAL is 0, ignorable whitespace is dropped.\n\
413146520Sru"), xml_indentation_increment);
414146520Sru    puts ("");
415146520Sru
416116530Sru    puts (_("\
41793142SruInput file options:\n\
418116530Sru      --commands-in-node-names  allow @ commands in node names.\n\
419116530Sru  -D VAR                        define the variable VAR, as with @set.\n\
420116530Sru  -I DIR                        append DIR to the @include search path.\n\
421116530Sru  -P DIR                        prepend DIR to the @include search path.\n\
422116530Sru  -U VAR                        undefine the variable VAR, as with @clear.\n\
42393142Sru"));
424100516Sru
425114477Sru    puts (_("\
42693142SruConditional processing in input:\n\
427146520Sru  --ifdocbook       process @ifdocbook and @docbook even if\n\
428146520Sru                      not generating Docbook.\n\
429100516Sru  --ifhtml          process @ifhtml and @html even if not generating HTML.\n\
430100516Sru  --ifinfo          process @ifinfo even if not generating Info.\n\
431100516Sru  --ifplaintext     process @ifplaintext even if not generating plain text.\n\
432100516Sru  --iftex           process @iftex and @tex; implies --no-split.\n\
433114477Sru  --ifxml           process @ifxml and @xml.\n\
434146520Sru  --no-ifdocbook    do not process @ifdocbook and @docbook text.\n\
435100516Sru  --no-ifhtml       do not process @ifhtml and @html text.\n\
436100516Sru  --no-ifinfo       do not process @ifinfo text.\n\
437100516Sru  --no-ifplaintext  do not process @ifplaintext text.\n\
438100516Sru  --no-iftex        do not process @iftex and @tex text.\n\
439114477Sru  --no-ifxml        do not process @ifxml and @xml text.\n\
440146520Sru\n\
441146520Sru  Also, for the --no-ifFORMAT options, do process @ifnotFORMAT text.\n\
44293142Sru"));
44393142Sru
444114477Sru    puts (_("\
44593142Sru  The defaults for the @if... conditionals depend on the output format:\n\
44693142Sru  if generating HTML, --ifhtml is on and the others are off;\n\
447100516Sru  if generating Info, --ifinfo is on and the others are off;\n\
448100516Sru  if generating plain text, --ifplaintext is on and the others are off;\n\
449116530Sru  if generating XML, --ifxml is on and the others are off.\n\
45093142Sru"));
45193142Sru
452114477Sru    fputs (_("\
45356164SruExamples:\n\
45493142Sru  makeinfo foo.texi                     write Info to foo's @setfilename\n\
455100516Sru  makeinfo --html foo.texi              write HTML to @setfilename\n\
456114477Sru  makeinfo --xml foo.texi               write Texinfo XML to @setfilename\n\
457100516Sru  makeinfo --docbook foo.texi           write DocBook XML to @setfilename\n\
458100516Sru  makeinfo --no-headers foo.texi        write plain text to standard output\n\
459100516Sru\n\
460100516Sru  makeinfo --html --no-headers foo.texi write html without node lines, menus\n\
46193142Sru  makeinfo --number-sections foo.texi   write Info with numbered sections\n\
46293142Sru  makeinfo --no-split foo.texi          write one Info file however big\n\
463100516Sru"), stdout);
46493142Sru
465114477Sru    puts (_("\n\
46656164SruEmail bug reports to bug-texinfo@gnu.org,\n\
467100516Srugeneral questions and discussion to help-texinfo@gnu.org.\n\
468100516SruTexinfo home page: http://www.gnu.org/software/texinfo/"));
469100516Sru
470114477Sru  } /* end of full help */
471114477Sru
47256164Sru  xexit (exit_value);
47356164Sru}
47456164Sru
47556164Srustruct option long_options[] =
47656164Sru{
47756164Sru  { "commands-in-node-names", 0, &expensive_validation, 1 },
478116530Sru  { "css-include", 1, 0, 'C' },
47993142Sru  { "docbook", 0, 0, 'd' },
48093142Sru  { "enable-encoding", 0, &enable_encoding, 1 },
48156164Sru  { "error-limit", 1, 0, 'e' },
48256164Sru  { "fill-column", 1, 0, 'f' },
48356164Sru  { "footnote-style", 1, 0, 's' },
48456164Sru  { "force", 0, &force, 1 },
48556164Sru  { "help", 0, 0, 'h' },
48656164Sru  { "html", 0, 0, 'w' },
487146520Sru  { "ifdocbook", 0, &process_docbook, 1 },
48856164Sru  { "ifhtml", 0, &process_html, 1 },
48956164Sru  { "ifinfo", 0, &process_info, 1 },
490100516Sru  { "ifplaintext", 0, &process_plaintext, 1 },
49156164Sru  { "iftex", 0, &process_tex, 1 },
492114477Sru  { "ifxml", 0, &process_xml, 1 },
49356164Sru  { "macro-expand", 1, 0, 'E' },
49456164Sru  { "no-headers", 0, &no_headers, 1 },
495146520Sru  { "no-ifdocbook", 0, &process_docbook, 0 },
49656164Sru  { "no-ifhtml", 0, &process_html, 0 },
49756164Sru  { "no-ifinfo", 0, &process_info, 0 },
498100516Sru  { "no-ifplaintext", 0, &process_plaintext, 0 },
49956164Sru  { "no-iftex", 0, &process_tex, 0 },
500114477Sru  { "no-ifxml", 0, &process_xml, 0 },
50156164Sru  { "no-number-footnotes", 0, &number_footnotes, 0 },
50256164Sru  { "no-number-sections", 0, &number_sections, 0 },
50356164Sru  { "no-pointer-validate", 0, &validating, 0 },
50456164Sru  { "no-split", 0, &splitting, 0 },
50556164Sru  { "no-validate", 0, &validating, 0 },
50656164Sru  { "no-warn", 0, &print_warnings, 0 },
50756164Sru  { "number-footnotes", 0, &number_footnotes, 1 },
50856164Sru  { "number-sections", 0, &number_sections, 1 },
50956164Sru  { "output", 1, 0, 'o' },
510146520Sru  { "output-indent", 1, 0, 'i' },
51156164Sru  { "paragraph-indent", 1, 0, 'p' },
512146520Sru  { "plaintext", 0, 0, 't' },
51356164Sru  { "reference-limit", 1, 0, 'r' },
51493142Sru  { "split-size", 1, 0, 'S'},
51556164Sru  { "verbose", 0, &verbose_mode, 1 },
51656164Sru  { "version", 0, 0, 'V' },
51793142Sru  { "xml", 0, 0, 'x' },
51856164Sru  {NULL, 0, NULL, 0}
51956164Sru};
52056164Sru
521146520Sru/* We use handle_variable_internal for -D and -U, and it depends on
522146520Sru   execute_string, which depends on input_filename, which is not defined
523146520Sru   while we are handling options. :-\  So we save these defines in this
524146520Sru   struct, and handle them later.  */
525146520Srutypedef struct command_line_define
526146520Sru{
527146520Sru  struct command_line_define *next;
528146520Sru  int action;
529146520Sru  char *define;
530146520Sru} COMMAND_LINE_DEFINE;
531146520Sru
532146520Srustatic COMMAND_LINE_DEFINE *command_line_defines = NULL;
533146520Sru
53421495Sjmacd/* For each file mentioned in the command line, process it, turning
53521495Sjmacd   Texinfo commands into wonderfully formatted output text. */
53621495Sjmacdint
537146520Srumain (int argc, char **argv)
53821495Sjmacd{
53921495Sjmacd  int c, ind;
54021495Sjmacd  int reading_from_stdin = 0;
54121495Sjmacd
54242664Smarkm#ifdef HAVE_SETLOCALE
54342664Smarkm  /* Do not use LC_ALL, because LC_NUMERIC screws up the scanf parsing
54442664Smarkm     of the argument to @multicolumn.  */
54542664Smarkm  setlocale (LC_TIME, "");
546146520Sru#ifdef LC_MESSAGES /* ultrix */
54742664Smarkm  setlocale (LC_MESSAGES, "");
548146520Sru#endif
54956164Sru  setlocale (LC_CTYPE, "");
55056164Sru  setlocale (LC_COLLATE, "");
55142664Smarkm#endif
55242664Smarkm
553146520Sru#ifdef ENABLE_NLS
55442664Smarkm  /* Set the text message domain.  */
55542664Smarkm  bindtextdomain (PACKAGE, LOCALEDIR);
55642664Smarkm  textdomain (PACKAGE);
557146520Sru#endif
55842664Smarkm
559146520Sru  /* If TEXINFO_OUTPUT_FORMAT envvar is set, use it to set default output.
560146520Sru     Can be overridden with one of the output options.  */
561146520Sru  if (getenv ("TEXINFO_OUTPUT_FORMAT") != NULL)
562146520Sru    {
563146520Sru      if (STREQ (getenv ("TEXINFO_OUTPUT_FORMAT"), "docbook"))
564146520Sru        {
565146520Sru          splitting = 0;
566146520Sru          html = 0;
567146520Sru          docbook = 1;
568146520Sru          xml = 1;
569146520Sru          process_docbook = 1;
570146520Sru        }
571146520Sru      else if (STREQ (getenv ("TEXINFO_OUTPUT_FORMAT"), "html"))
572146520Sru        {
573146520Sru          html = 1;
574146520Sru          docbook = 0;
575146520Sru          xml = 0;
576146520Sru          process_html = 1;
577146520Sru        }
578146520Sru      else if (STREQ (getenv ("TEXINFO_OUTPUT_FORMAT"), "info"))
579146520Sru        {
580146520Sru          html = 0;
581146520Sru          docbook = 0;
582146520Sru          xml = 0;
583146520Sru        }
584146520Sru      else if (STREQ (getenv ("TEXINFO_OUTPUT_FORMAT"), "plaintext"))
585146520Sru        {
586146520Sru          splitting = 0;
587146520Sru          no_headers = 1;
588146520Sru          html = 0;
589146520Sru          docbook = 0;
590146520Sru          xml = 0;
591146520Sru          process_plaintext = 1;
592146520Sru        }
593146520Sru      else if (STREQ (getenv ("TEXINFO_OUTPUT_FORMAT"), "xml"))
594146520Sru        {
595146520Sru          splitting = 0;
596146520Sru          html = 0;
597146520Sru          docbook = 0;
598146520Sru          xml = 1;
599146520Sru          process_xml = 1;
600146520Sru        }
601146520Sru      else
602146520Sru        fprintf (stderr,
603146520Sru            _("%s: Ignoring unrecognized TEXINFO_OUTPUT_FORMAT value `%s'.\n"),
604146520Sru                 progname, getenv ("TEXINFO_OUTPUT_FORMAT"));
605146520Sru    }
606146520Sru
60721495Sjmacd  /* Parse argument flags from the input line. */
608146520Sru  while ((c = getopt_long (argc, argv, "D:de:E:f:hI:i:o:p:P:r:s:t:U:vV:wx",
60942664Smarkm                           long_options, &ind)) != EOF)
61021495Sjmacd    {
61121495Sjmacd      if (c == 0 && long_options[ind].flag == 0)
61242664Smarkm        c = long_options[ind].val;
61321495Sjmacd
61421495Sjmacd      switch (c)
61542664Smarkm        {
616116530Sru        case 'C':  /* --css-include */
617116530Sru          css_include = xstrdup (optarg);
618116530Sru          break;
619116530Sru
62042664Smarkm        case 'D':
62142664Smarkm        case 'U':
62242664Smarkm          /* User specified variable to set or clear. */
623146520Sru          if (xml && !docbook)
624146520Sru            {
625146520Sru              COMMAND_LINE_DEFINE *new = xmalloc (sizeof (COMMAND_LINE_DEFINE));
626146520Sru              new->action = (c == 'D') ? SET : CLEAR;
627146520Sru              new->define = xstrdup (optarg);
628146520Sru              new->next = command_line_defines;
629146520Sru              command_line_defines = new;
630146520Sru            }
631146520Sru          else
632146520Sru            handle_variable_internal ((c == 'D' ? SET : CLEAR), optarg);
63342664Smarkm          break;
63421495Sjmacd
63593142Sru        case 'd': /* --docbook */
63693142Sru          splitting = 0;
637114477Sru          xml = 1;
638114477Sru          docbook = 1;
639146520Sru          html = 0;
640146520Sru	  process_docbook = 1;
64193142Sru          break;
64293142Sru
64356164Sru        case 'e': /* --error-limit */
64442664Smarkm          if (sscanf (optarg, "%d", &max_error_level) != 1)
64542664Smarkm            {
64642664Smarkm              fprintf (stderr,
64742664Smarkm                      _("%s: %s arg must be numeric, not `%s'.\n"),
648146520Sru                      progname, "--error-limit", optarg);
64993142Sru              usage (1);
65042664Smarkm            }
65142664Smarkm          break;
65221495Sjmacd
65356164Sru        case 'E': /* --macro-expand */
65442664Smarkm          if (!macro_expansion_output_stream)
65542664Smarkm            {
65642664Smarkm              macro_expansion_filename = optarg;
65742664Smarkm              macro_expansion_output_stream
65842664Smarkm                = strcmp (optarg, "-") == 0 ? stdout : fopen (optarg, "w");
65942664Smarkm              if (!macro_expansion_output_stream)
660146520Sru                error (_("%s: could not open macro expansion output `%s'"),
661146520Sru                       progname, optarg);
66242664Smarkm            }
66342664Smarkm          else
664146520Sru            fprintf (stderr,
665146520Sru                     _("%s: ignoring second macro expansion output `%s'.\n"),
666146520Sru                     progname, optarg);
66742664Smarkm          break;
66821495Sjmacd
66956164Sru        case 'f': /* --fill-column */
67042664Smarkm          if (sscanf (optarg, "%d", &fill_column) != 1)
67142664Smarkm            {
67242664Smarkm              fprintf (stderr,
67356164Sru                       _("%s: %s arg must be numeric, not `%s'.\n"),
674146520Sru                       progname, "--fill-column", optarg);
67556164Sru              usage (1);
67642664Smarkm            }
67742664Smarkm          break;
67821495Sjmacd
67956164Sru        case 'h': /* --help */
68056164Sru          usage (0);
68142664Smarkm          break;
68221495Sjmacd
68342664Smarkm        case 'I':
68442664Smarkm          /* Append user-specified dir to include file path. */
685146520Sru          append_to_include_path (optarg);
686146520Sru          break;
68721495Sjmacd
688146520Sru        case 'i':
689146520Sru          if (sscanf (optarg, "%d", &xml_indentation_increment) != 1)
690146520Sru            {
691146520Sru              fprintf (stderr,
692146520Sru                     _("%s: %s arg must be numeric, not `%s'.\n"),
693146520Sru                     progname, "--output-indent", optarg);
694146520Sru              usage (1);
695146520Sru            }
69642664Smarkm          break;
69721495Sjmacd
69856164Sru        case 'o': /* --output */
69942664Smarkm          command_output_filename = xstrdup (optarg);
700100516Sru          save_command_output_filename = command_output_filename;
70142664Smarkm          break;
70221495Sjmacd
70356164Sru        case 'p': /* --paragraph-indent */
70442664Smarkm          if (set_paragraph_indent (optarg) < 0)
70542664Smarkm            {
70642664Smarkm              fprintf (stderr,
70756164Sru   _("%s: --paragraph-indent arg must be numeric/`none'/`asis', not `%s'.\n"),
70821495Sjmacd                       progname, optarg);
70956164Sru              usage (1);
71042664Smarkm            }
71142664Smarkm          break;
71221495Sjmacd
71342664Smarkm        case 'P':
71442664Smarkm          /* Prepend user-specified include dir to include path. */
715146520Sru          prepend_to_include_path (optarg);
71642664Smarkm          break;
71756164Sru
71856164Sru        case 'r': /* --reference-limit */
71942664Smarkm          if (sscanf (optarg, "%d", &reference_warning_limit) != 1)
72042664Smarkm            {
72142664Smarkm              fprintf (stderr,
72256164Sru                     _("%s: %s arg must be numeric, not `%s'.\n"),
723146520Sru                     progname, "--reference-limit", optarg);
72456164Sru              usage (1);
72542664Smarkm            }
72642664Smarkm          break;
72742664Smarkm
72856164Sru        case 's': /* --footnote-style */
72942664Smarkm          if (set_footnote_style (optarg) < 0)
73042664Smarkm            {
73142664Smarkm              fprintf (stderr,
732146520Sru        _("%s: --footnote-style arg must be `separate' or `end', not `%s'.\n"),
73321495Sjmacd                       progname, optarg);
73456164Sru              usage (1);
73542664Smarkm            }
73642664Smarkm          footnote_style_preset = 1;
73742664Smarkm          break;
73821495Sjmacd
739114477Sru        case 'S': /* --split-size */
74093142Sru          if (sscanf (optarg, "%d", &split_size) != 1)
74193142Sru            {
74293142Sru              fprintf (stderr,
74393142Sru                     _("%s: %s arg must be numeric, not `%s'.\n"),
744146520Sru                     progname, "--split-size", optarg);
74593142Sru              usage (1);
74693142Sru            }
747114477Sru          break;
74893142Sru
749146520Sru        case 't': /* --plaintext */
750146520Sru          splitting = 0;
751146520Sru          no_headers = 1;
752146520Sru          html = 0;
753146520Sru          docbook = 0;
754146520Sru          xml = 0;
755146520Sru          process_plaintext = 1;
756146520Sru          break;
757146520Sru
75856164Sru        case 'v':
75956164Sru          verbose_mode++;
76056164Sru          break;
76156164Sru
76256164Sru        case 'V': /* --version */
76342664Smarkm          print_version_info ();
76456164Sru          puts ("");
765146520Sru          puts ("Copyright (C) 2004 Free Software Foundation, Inc.");
766146520Sru          printf (_("There is NO warranty.  You may redistribute this software\n\
76721495Sjmacdunder the terms of the GNU General Public License.\n\
768146520SruFor more information about these matters, see the files named COPYING.\n"));
769114477Sru          xexit (0);
77042664Smarkm          break;
77121495Sjmacd
77256164Sru        case 'w': /* --html */
773146520Sru          xml = 0;
774146520Sru          docbook = 0;
77556164Sru          html = 1;
77656164Sru          process_html = 1;
77756164Sru          break;
77856164Sru
77993142Sru        case 'x': /* --xml */
78093142Sru          splitting = 0;
781146520Sru          html = 0;
782146520Sru          docbook = 0;
783114477Sru          xml = 1;
784114477Sru          process_xml = 1;
78593142Sru          break;
786116530Sru
78742664Smarkm        case '?':
78856164Sru          usage (1);
78942664Smarkm          break;
79042664Smarkm        }
79121495Sjmacd    }
79221495Sjmacd
793146520Sru  if (macro_expansion_output_stream)
794146520Sru    validating = 0;
795146520Sru
79656164Sru  if (!validating)
79756164Sru    expensive_validation = 0;
79856164Sru
79921495Sjmacd  if (optind == argc)
80021495Sjmacd    {
80121495Sjmacd      /* Check to see if input is a file.  If so, process that. */
80221495Sjmacd      if (!isatty (fileno (stdin)))
80342664Smarkm        reading_from_stdin = 1;
80421495Sjmacd      else
80521495Sjmacd        {
80642664Smarkm          fprintf (stderr, _("%s: missing file argument.\n"), progname);
80756164Sru          usage (1);
80821495Sjmacd        }
80921495Sjmacd    }
81021495Sjmacd
81121495Sjmacd  if (no_headers)
81221495Sjmacd    {
813146520Sru      /* If the user did not specify an output file, use stdout. */
814146520Sru      if (!command_output_filename)
815146520Sru        command_output_filename = xstrdup ("-");
816146520Sru
817116530Sru      if (html && splitting && !STREQ (command_output_filename, "-"))
81856164Sru        { /* --no-headers --no-split --html indicates confusion. */
81956164Sru          fprintf (stderr,
820116530Sru                  "%s: can't split --html output to `%s' with --no-headers.\n",
821116530Sru                   progname, command_output_filename);
82256164Sru          usage (1);
82356164Sru        }
82456164Sru
82556164Sru      /* --no-headers implies --no-split.  */
82621495Sjmacd      splitting = 0;
82721495Sjmacd    }
828116530Sru
829100516Sru  if (process_info == -1)
830100516Sru    { /* no explicit --[no-]ifinfo option, so we'll do @ifinfo
831100516Sru         if we're generating info or (for compatibility) plain text.  */
832100516Sru      process_info = !html && !xml;
833100516Sru    }
83421495Sjmacd
835100516Sru  if (process_plaintext == -1)
836100516Sru    { /* no explicit --[no-]ifplaintext option, so we'll do @ifplaintext
837100516Sru         if we're generating plain text.  */
838100516Sru      process_plaintext = no_headers && !html && !xml;
839100516Sru    }
840116530Sru
84121495Sjmacd  if (verbose_mode)
84221495Sjmacd    print_version_info ();
84321495Sjmacd
84421495Sjmacd  /* Remaining arguments are file names of texinfo files.
84521495Sjmacd     Convert them, one by one. */
84621495Sjmacd  if (!reading_from_stdin)
84721495Sjmacd    {
84821495Sjmacd      while (optind != argc)
84942664Smarkm        convert_from_file (argv[optind++]);
85021495Sjmacd    }
85121495Sjmacd  else
85221495Sjmacd    convert_from_stream (stdin, "stdin");
85321495Sjmacd
854116530Sru  xexit (errors_printed ? 2 : 0);
855116530Sru  return 0; /* Avoid bogus warnings.  */
85621495Sjmacd}
85721495Sjmacd
85856164Sru/* Hacking tokens and strings.  */
85921495Sjmacd
860114477Sru/* Return the next token as a string pointer.  We cons the string.  This
861114477Sru   `token' means simply a command name.  */
862114477Sru
863114477Sru/* = is so @alias works.  ^ and _ are so macros can be used in math mode
864114477Sru   without a space following.  Possibly we should simply allow alpha, to
865114477Sru   be compatible with TeX.  */
866114477Sru#define COMMAND_CHAR(c) (!cr_or_whitespace(c) \
867114477Sru                         && (c) != '{' \
868114477Sru                         && (c) != '}' \
869114477Sru                         && (c) != '=' \
870114477Sru                         && (c) != '_' \
871114477Sru                         && (c) != '^' \
872114477Sru                         )
873114477Sru
874146520Srustatic char *
875146520Sruread_token (void)
87621495Sjmacd{
87721495Sjmacd  int i, character;
87821495Sjmacd  char *result;
87921495Sjmacd
88021495Sjmacd  /* If the first character to be read is self-delimiting, then that
88121495Sjmacd     is the command itself. */
88221495Sjmacd  character = curchar ();
88321495Sjmacd  if (self_delimiting (character))
88421495Sjmacd    {
88521495Sjmacd      input_text_offset++;
88621495Sjmacd
88721495Sjmacd      if (character == '\n')
88842664Smarkm        line_number++;
88921495Sjmacd
89042664Smarkm      result = xstrdup (" ");
89121495Sjmacd      *result = character;
89256164Sru      return result;
89321495Sjmacd    }
89421495Sjmacd
89556164Sru  for (i = 0; ((input_text_offset != input_text_length)
89642664Smarkm               && (character = curchar ())
897114477Sru               && COMMAND_CHAR (character));
89821495Sjmacd       i++, input_text_offset++);
89956164Sru  result = xmalloc (i + 1);
90021495Sjmacd  memcpy (result, &input_text[input_text_offset - i], i);
90142664Smarkm  result[i] = 0;
90256164Sru  return result;
90321495Sjmacd}
90421495Sjmacd
90542664Smarkm/* Return nonzero if CHARACTER is self-delimiting. */
90621495Sjmacdint
907146520Sruself_delimiting (int character)
90821495Sjmacd{
90921495Sjmacd  /* @; and @\ are not Texinfo commands, but they are listed here
91021495Sjmacd     anyway.  I don't know why.  --karl, 10aug96.  */
911116530Sru  return strchr ("~{|}`^\\@?=;:./-,*\'\" !\n\t", character) != NULL;
91221495Sjmacd}
91321495Sjmacd
91421495Sjmacd/* Clear whitespace from the front and end of string. */
91521495Sjmacdvoid
916146520Srucanon_white (char *string)
91721495Sjmacd{
918146520Sru  char *p = string;
919146520Sru  unsigned len;
92021495Sjmacd
921146520Sru  if (!*p)
92221495Sjmacd    return;
92321495Sjmacd
924146520Sru  do
92521495Sjmacd    {
926146520Sru      if (!cr_or_whitespace (*p))
927146520Sru	break;
928146520Sru      ++p;
92921495Sjmacd    }
930146520Sru  while (*p);
931146520Sru
932146520Sru  len = strlen (p);
933146520Sru  while (len && cr_or_whitespace (p[len-1]))
934146520Sru    --len;
935146520Sru
936146520Sru  if (p != string)
937146520Sru    memmove (string, p, len);
938146520Sru
939146520Sru  string[len] = 0;
94021495Sjmacd}
94121495Sjmacd
94221495Sjmacd/* Bash STRING, replacing all whitespace with just one space. */
94321495Sjmacdvoid
944146520Srufix_whitespace (char *string)
94521495Sjmacd{
94656164Sru  char *temp = xmalloc (strlen (string) + 1);
94721495Sjmacd  int string_index = 0;
94821495Sjmacd  int temp_index = 0;
94921495Sjmacd  int c;
95021495Sjmacd
95121495Sjmacd  canon_white (string);
95221495Sjmacd
95321495Sjmacd  while (string[string_index])
95421495Sjmacd    {
95521495Sjmacd      c = temp[temp_index++] = string[string_index++];
95621495Sjmacd
95721495Sjmacd      if (c == ' ' || c == '\n' || c == '\t')
95842664Smarkm        {
95942664Smarkm          temp[temp_index - 1] = ' ';
96042664Smarkm          while ((c = string[string_index]) && (c == ' ' ||
96142664Smarkm                                                c == '\t' ||
96242664Smarkm                                                c == '\n'))
96342664Smarkm            string_index++;
96442664Smarkm        }
96521495Sjmacd    }
96642664Smarkm  temp[temp_index] = 0;
96721495Sjmacd  strcpy (string, temp);
96821495Sjmacd  free (temp);
96921495Sjmacd}
97021495Sjmacd
97121495Sjmacd/* Discard text until the desired string is found.  The string is
97221495Sjmacd   included in the discarded text. */
97321495Sjmacdvoid
974146520Srudiscard_until (char *string)
97521495Sjmacd{
97621495Sjmacd  int temp = search_forward (string, input_text_offset);
97721495Sjmacd
97856164Sru  int tt = (temp < 0) ? input_text_length : temp + strlen (string);
97921495Sjmacd  int from = input_text_offset;
98021495Sjmacd
98121495Sjmacd  /* Find out what line we are on. */
98221495Sjmacd  while (from != tt)
98321495Sjmacd    if (input_text[from++] == '\n')
98421495Sjmacd      line_number++;
98521495Sjmacd
98621495Sjmacd  if (temp < 0)
98721495Sjmacd    {
988146520Sru      /* not found, move current position to end of string */
989146520Sru      input_text_offset = input_text_length;
99021495Sjmacd      if (strcmp (string, "\n") != 0)
991146520Sru        { /* Give a more descriptive feedback, if we are looking for ``@end ''
992146520Sru             during macro execution.  That means someone used a multiline
993146520Sru             command as an argument to, say, @section ... style commands.  */
994146520Sru          char *end_block = xmalloc (8);
995146520Sru          sprintf (end_block, "\n%cend ", COMMAND_PREFIX);
996146520Sru          if (executing_string && strstr (string, end_block))
997146520Sru            line_error (_("Multiline command %c%s used improperly"),
998146520Sru                COMMAND_PREFIX, command);
999146520Sru          else
1000146520Sru            line_error (_("Expected `%s'"), string);
1001146520Sru          free (end_block);
100242664Smarkm          return;
100342664Smarkm        }
100421495Sjmacd    }
100521495Sjmacd  else
1006146520Sru    /* found, move current position to after the found string */
1007146520Sru    input_text_offset = temp + strlen (string);
100821495Sjmacd}
100921495Sjmacd
101021495Sjmacd/* Read characters from the file until we are at MATCH.
101121495Sjmacd   Place the characters read into STRING.
101221495Sjmacd   On exit input_text_offset is after the match string.
101321495Sjmacd   Return the offset where the string starts. */
101421495Sjmacdint
1015146520Sruget_until (char *match, char **string)
101621495Sjmacd{
101721495Sjmacd  int len, current_point, x, new_point, tem;
101821495Sjmacd
101921495Sjmacd  current_point = x = input_text_offset;
102021495Sjmacd  new_point = search_forward (match, input_text_offset);
102121495Sjmacd
102221495Sjmacd  if (new_point < 0)
102356164Sru    new_point = input_text_length;
102421495Sjmacd  len = new_point - current_point;
102521495Sjmacd
102621495Sjmacd  /* Keep track of which line number we are at. */
102721495Sjmacd  tem = new_point + (strlen (match) - 1);
102821495Sjmacd  while (x != tem)
102921495Sjmacd    if (input_text[x++] == '\n')
103021495Sjmacd      line_number++;
103121495Sjmacd
103256164Sru  *string = xmalloc (len + 1);
103321495Sjmacd
103421495Sjmacd  memcpy (*string, &input_text[current_point], len);
103542664Smarkm  (*string)[len] = 0;
103621495Sjmacd
103721495Sjmacd  /* Now leave input_text_offset in a consistent state. */
103821495Sjmacd  input_text_offset = tem;
103921495Sjmacd
104056164Sru  if (input_text_offset > input_text_length)
104156164Sru    input_text_offset = input_text_length;
104221495Sjmacd
104356164Sru  return new_point;
104421495Sjmacd}
104521495Sjmacd
104656164Sru/* Replace input_text[FROM .. TO] with its expansion.  */
104756164Sruvoid
1048146520Srureplace_with_expansion (int from, int *to)
104956164Sru{
105056164Sru  char *xp;
105156164Sru  unsigned xp_len, new_len;
105256164Sru  char *old_input = input_text;
105356164Sru  unsigned raw_len = *to - from;
105456164Sru  char *str;
105556164Sru
105656164Sru  /* The rest of the code here moves large buffers, so let's
105756164Sru     not waste time if the input cannot possibly expand
105856164Sru     into anything.  Unfortunately, we cannot avoid expansion
105956164Sru     when we see things like @code etc., even if they only
106056164Sru     asked for expansion of macros, since any Texinfo command
106156164Sru     can be potentially redefined with a macro.  */
106256164Sru  if (only_macro_expansion &&
106356164Sru      memchr (input_text + from, COMMAND_PREFIX, raw_len) == 0)
106456164Sru    return;
106556164Sru
106656164Sru  /* Get original string from input.  */
106756164Sru  str = xmalloc (raw_len + 1);
106856164Sru  memcpy (str, input_text + from, raw_len);
106956164Sru  str[raw_len] = 0;
107056164Sru
107156164Sru  /* We are going to relocate input_text, so we had better output
107256164Sru     pending portion of input_text now, before the pointer changes.  */
107356164Sru  if (macro_expansion_output_stream && !executing_string
107456164Sru      && !me_inhibit_expansion)
107556164Sru    append_to_expansion_output (from);
107656164Sru
107756164Sru  /* Expand it.  */
107856164Sru  xp = expansion (str, 0);
107956164Sru  xp_len = strlen (xp);
108056164Sru  free (str);
108156164Sru
108256164Sru  /* Plunk the expansion into the middle of `input_text' --
108356164Sru     which is terminated by a newline, not a null.  Avoid
108456164Sru     expensive move of the rest of the input if the expansion
108556164Sru     has the same length as the original string.  */
108656164Sru  if (xp_len != raw_len)
108756164Sru    {
108856164Sru      new_len = from + xp_len + input_text_length - *to + 1;
108956164Sru      if (executing_string)
109056164Sru        { /* If we are in execute_string, we might need to update
109156164Sru             the relevant element in the execution_strings[] array,
109256164Sru             since it could have to be relocated from under our
109356164Sru             feet.  (input_text is reallocated here as well, if needed.)  */
109456164Sru          maybe_update_execution_strings (&input_text, new_len);
109556164Sru        }
109656164Sru      else if (new_len > input_text_length + 1)
109756164Sru        /* Don't bother to realloc if we have enough space.  */
109856164Sru        input_text = xrealloc (input_text, new_len);
109956164Sru
110056164Sru      memmove (input_text + from + xp_len,
110156164Sru               input_text + *to, input_text_length - *to + 1);
110256164Sru
110356164Sru      *to += xp_len - raw_len;
110456164Sru      /* Since we change input_text_length here, the comparison above
110556164Sru         isn't really valid, but it seems the worst that might happen is
110656164Sru         an extra xrealloc or two, so let's not worry.  */
110756164Sru      input_text_length += xp_len - raw_len;
110856164Sru    }
110956164Sru  memcpy (input_text + from, xp, xp_len);
111056164Sru  free (xp);
111156164Sru
111256164Sru  /* Synchronize the macro-expansion pointers with our new input_text.  */
111356164Sru  if (input_text != old_input)
111456164Sru    forget_itext (old_input);
111556164Sru  if (macro_expansion_output_stream && !executing_string)
111656164Sru    remember_itext (input_text, from);
111756164Sru}
111856164Sru
111921495Sjmacd/* Read characters from the file until we are at MATCH or end of line.
112056164Sru   Place the characters read into STRING.  If EXPAND is nonzero,
112156164Sru   expand the text before looking for MATCH for those cases where
112256164Sru   MATCH might be produced by some macro.  */
112321495Sjmacdvoid
1124146520Sruget_until_in_line (int expand, char *match, char **string)
112521495Sjmacd{
112656164Sru  int real_bottom = input_text_length;
112742664Smarkm  int limit = search_forward ("\n", input_text_offset);
112842664Smarkm  if (limit < 0)
112956164Sru    limit = input_text_length;
113021495Sjmacd
113156164Sru  /* Replace input_text[input_text_offset .. limit-1] with its expansion.
113256164Sru     This allows the node names and menu entries themselves to be
113356164Sru     constructed via a macro, as in:
113442664Smarkm        @macro foo{p, q}
113542664Smarkm        Together: \p\ & \q\.
113642664Smarkm        @end macro
113721495Sjmacd
113842664Smarkm        @node @foo{A,B}, next, prev, top
113956164Sru
114042664Smarkm     Otherwise, the `,' separating the macro args A and B is taken as
114142664Smarkm     the node argument separator, so the node name is `@foo{A'.  This
114242664Smarkm     expansion is only necessary on the first call, since we expand the
114356164Sru     whole line then.  */
114456164Sru  if (expand)
114542664Smarkm    {
114656164Sru      replace_with_expansion (input_text_offset, &limit);
114742664Smarkm    }
114842664Smarkm
114956164Sru  real_bottom = input_text_length;
115056164Sru  input_text_length = limit;
115121495Sjmacd  get_until (match, string);
115256164Sru  input_text_length = real_bottom;
115321495Sjmacd}
115421495Sjmacd
115521495Sjmacdvoid
1156146520Sruget_rest_of_line (int expand, char **string)
115721495Sjmacd{
115893142Sru  xml_no_para ++;
115956164Sru  if (expand)
116056164Sru    {
116156164Sru      char *tem;
116256164Sru
116356164Sru      /* Don't expand non-macros in input, since we want them
1164114477Sru         intact in the macro-expanded output.  */
116556164Sru      only_macro_expansion++;
116656164Sru      get_until_in_line (1, "\n", &tem);
116756164Sru      only_macro_expansion--;
116856164Sru      *string = expansion (tem, 0);
116956164Sru      free (tem);
117056164Sru    }
117156164Sru  else
117256164Sru    get_until_in_line (0, "\n", string);
117356164Sru
117421495Sjmacd  canon_white (*string);
117521495Sjmacd
117642664Smarkm  if (curchar () == '\n')       /* as opposed to the end of the file... */
117721495Sjmacd    {
117821495Sjmacd      line_number++;
117921495Sjmacd      input_text_offset++;
118021495Sjmacd    }
118193142Sru  xml_no_para --;
118221495Sjmacd}
118321495Sjmacd
118421495Sjmacd/* Backup the input pointer to the previous character, keeping track
118521495Sjmacd   of the current line number. */
118621495Sjmacdvoid
1187146520Srubackup_input_pointer (void)
118821495Sjmacd{
118921495Sjmacd  if (input_text_offset)
119021495Sjmacd    {
119121495Sjmacd      input_text_offset--;
119221495Sjmacd      if (curchar () == '\n')
119342664Smarkm        line_number--;
119421495Sjmacd    }
119521495Sjmacd}
119621495Sjmacd
119721495Sjmacd/* Read characters from the file until we are at MATCH or closing brace.
119821495Sjmacd   Place the characters read into STRING.  */
119921495Sjmacdvoid
1200146520Sruget_until_in_braces (char *match, char **string)
120121495Sjmacd{
120242664Smarkm  char *temp;
120321495Sjmacd  int i, brace = 0;
120421495Sjmacd  int match_len = strlen (match);
120521495Sjmacd
120656164Sru  for (i = input_text_offset; i < input_text_length; i++)
120721495Sjmacd    {
120893142Sru      if (i < input_text_length - 1 && input_text[i] == '@')
120993142Sru        {
1210114477Sru          i++;                  /* skip commands like @, and @{ */
121193142Sru          continue;
121293142Sru        }
121393142Sru      else if (input_text[i] == '{')
121442664Smarkm        brace++;
121521495Sjmacd      else if (input_text[i] == '}')
121693142Sru        {
121793142Sru          brace--;
121893142Sru          /* If looking for a brace, don't stop at the interior brace,
121993142Sru             like after "baz" in "@foo{something @bar{baz} more}".  */
122093142Sru          if (brace == 0)
122193142Sru            continue;
122293142Sru        }
122321495Sjmacd      else if (input_text[i] == '\n')
122442664Smarkm        line_number++;
122521495Sjmacd
122621495Sjmacd      if (brace < 0 ||
122742664Smarkm          (brace == 0 && strncmp (input_text + i, match, match_len) == 0))
122842664Smarkm        break;
122921495Sjmacd    }
123021495Sjmacd
123121495Sjmacd  match_len = i - input_text_offset;
123256164Sru  temp = xmalloc (2 + match_len);
123356164Sru  memcpy (temp, input_text + input_text_offset, match_len);
123442664Smarkm  temp[match_len] = 0;
123521495Sjmacd  input_text_offset = i;
123621495Sjmacd  *string = temp;
123721495Sjmacd}
1238116530Sru
1239116530Sru
124021495Sjmacd
124156164Sru/* Converting a file.  */
124221495Sjmacd
124321495Sjmacd/* Convert the file named by NAME.  The output is saved on the file
124421495Sjmacd   named as the argument to the @setfilename command. */
124521495Sjmacdstatic char *suffixes[] = {
124656164Sru  /* ".txi" is checked first so that on 8+3 DOS filesystems, if they
124756164Sru     have "texinfo.txi" and "texinfo.tex" in the same directory, the
124856164Sru     former is used rather than the latter, due to file name truncation.  */
124956164Sru  ".txi",
125021495Sjmacd  ".texinfo",
125121495Sjmacd  ".texi",
125221495Sjmacd  ".txinfo",
125321495Sjmacd  "",
125456164Sru  NULL
125521495Sjmacd};
125621495Sjmacd
1257146520Srustatic void
1258146520Sruinitialize_conversion (void)
125921495Sjmacd{
126021495Sjmacd  init_tag_table ();
126121495Sjmacd  init_indices ();
126221495Sjmacd  init_internals ();
126321495Sjmacd  init_paragraph ();
126421495Sjmacd
126521495Sjmacd  /* This is used for splitting the output file and for doing section
126621495Sjmacd     headings.  It was previously initialized in `init_paragraph', but its
126721495Sjmacd     use there loses with the `init_paragraph' calls done by the
126821495Sjmacd     multitable code; the tag indices get reset to zero.  */
126921495Sjmacd  output_position = 0;
127021495Sjmacd}
127121495Sjmacd
127256164Sru/* Reverse the chain of structures in LIST.  Output the new head
127356164Sru   of the chain.  You should always assign the output value of this
127456164Sru   function to something, or you will lose the chain. */
127556164SruGENERIC_LIST *
1276146520Srureverse_list (GENERIC_LIST *list)
127756164Sru{
127856164Sru  GENERIC_LIST *next;
127956164Sru  GENERIC_LIST *prev = NULL;
128056164Sru
128156164Sru  while (list)
128256164Sru    {
128356164Sru      next = list->next;
128456164Sru      list->next = prev;
128556164Sru      prev = list;
128656164Sru      list = next;
128756164Sru    }
128856164Sru  return prev;
128956164Sru}
129056164Sru
129121495Sjmacd/* We read in multiples of 4k, simply because it is a typical pipe size
129221495Sjmacd   on unix systems. */
129321495Sjmacd#define READ_BUFFER_GROWTH (4 * 4096)
129421495Sjmacd
129542664Smarkm/* Convert the Texinfo file coming from the open stream STREAM.  Assume the
129621495Sjmacd   source of the stream is named NAME. */
1297146520Srustatic void
1298146520Sruconvert_from_stream (FILE *stream, char *name)
129921495Sjmacd{
130056164Sru  char *buffer = NULL;
130121495Sjmacd  int buffer_offset = 0, buffer_size = 0;
130221495Sjmacd
130321495Sjmacd  initialize_conversion ();
130421495Sjmacd
130521495Sjmacd  /* Read until the end of the stream.  This isn't strictly correct, since
130621495Sjmacd     the texinfo input may end before the stream ends, but it is a quick
130721495Sjmacd     working hueristic. */
130821495Sjmacd  while (!feof (stream))
130921495Sjmacd    {
131021495Sjmacd      int count;
131121495Sjmacd
131221495Sjmacd      if (buffer_offset + (READ_BUFFER_GROWTH + 1) >= buffer_size)
131342664Smarkm        buffer = (char *)
131442664Smarkm          xrealloc (buffer, (buffer_size += READ_BUFFER_GROWTH));
131521495Sjmacd
131621495Sjmacd      count = fread (buffer + buffer_offset, 1, READ_BUFFER_GROWTH, stream);
131721495Sjmacd
131821495Sjmacd      if (count < 0)
131942664Smarkm        {
132042664Smarkm          perror (name);
132156164Sru          xexit (1);
132242664Smarkm        }
132321495Sjmacd
132421495Sjmacd      buffer_offset += count;
132521495Sjmacd      if (count == 0)
132642664Smarkm        break;
132721495Sjmacd    }
132821495Sjmacd
132921495Sjmacd  /* Set the globals to the new file. */
133021495Sjmacd  input_text = buffer;
133156164Sru  input_text_length = buffer_offset;
133242664Smarkm  input_filename = xstrdup (name);
133342664Smarkm  node_filename = xstrdup (name);
133421495Sjmacd  input_text_offset = 0;
133521495Sjmacd  line_number = 1;
133621495Sjmacd
133721495Sjmacd  /* Not strictly necessary.  This magic prevents read_token () from doing
133821495Sjmacd     extra unnecessary work each time it is called (that is a lot of times).
133956164Sru     The INPUT_TEXT_LENGTH is one past the actual end of the text. */
134056164Sru  input_text[input_text_length] = '\n';
134121495Sjmacd
134221495Sjmacd  convert_from_loaded_file (name);
134321495Sjmacd}
134421495Sjmacd
1345146520Srustatic void
1346146520Sruconvert_from_file (char *name)
134721495Sjmacd{
134856164Sru  int i;
134956164Sru  char *filename = xmalloc (strlen (name) + 50);
135021495Sjmacd
1351146520Sru  /* Prepend file directory to the search path, so relative links work.  */
1352146520Sru  prepend_to_include_path (pathname_part (name));
1353146520Sru
135421495Sjmacd  initialize_conversion ();
135521495Sjmacd
135621495Sjmacd  /* Try to load the file specified by NAME, concatenated with our
135721495Sjmacd     various suffixes.  Prefer files like `makeinfo.texi' to
135821495Sjmacd     `makeinfo'.  */
135921495Sjmacd  for (i = 0; suffixes[i]; i++)
136021495Sjmacd    {
136121495Sjmacd      strcpy (filename, name);
136221495Sjmacd      strcat (filename, suffixes[i]);
136321495Sjmacd
1364146520Sru      if (find_and_load (filename, 1))
136542664Smarkm        break;
136621495Sjmacd
136721495Sjmacd      if (!suffixes[i][0] && strrchr (filename, '.'))
136842664Smarkm        {
136942664Smarkm          fs_error (filename);
137042664Smarkm          free (filename);
137142664Smarkm          return;
137242664Smarkm        }
137321495Sjmacd    }
137421495Sjmacd
137521495Sjmacd  if (!suffixes[i])
137621495Sjmacd    {
137721495Sjmacd      fs_error (name);
137821495Sjmacd      free (filename);
137921495Sjmacd      return;
138021495Sjmacd    }
138121495Sjmacd
138221495Sjmacd  input_filename = filename;
138321495Sjmacd
138421495Sjmacd  convert_from_loaded_file (name);
1385146520Sru
1386146520Sru  /* Pop the prepended path, so multiple filenames in the
1387146520Sru     command line do not screw each others include paths.  */
1388146520Sru  pop_path_from_include_path ();
138921495Sjmacd}
139056164Sru
1391146520Srustatic int
1392146520Srucreate_html_directory (char *dir, int can_remove_file)
1393146520Sru{
1394146520Sru  struct stat st;
1395146520Sru
1396146520Sru  /* Already exists.  */
1397146520Sru  if (stat (dir, &st) == 0)
1398146520Sru    {
1399146520Sru      /* And it's a directory, so silently reuse it.  */
1400146520Sru      if (S_ISDIR (st.st_mode))
1401146520Sru        return 1;
1402146520Sru      /* Not a directory, so move it out of the way if we are allowed.  */
1403146520Sru      else if (can_remove_file)
1404146520Sru        {
1405146520Sru          if (unlink (dir) != 0)
1406146520Sru            return 0;
1407146520Sru        }
1408146520Sru      else
1409146520Sru        return 0;
1410146520Sru    }
1411146520Sru
1412146520Sru  if (mkdir (dir, 0777) == 0)
1413146520Sru    /* Success!  */
1414146520Sru    return 1;
1415146520Sru  else
1416146520Sru    return 0;
1417146520Sru}
1418146520Sru
141993142Sru/* Given OUTPUT_FILENAME == ``/foo/bar/baz.html'', return
1420100516Sru   "/foo/bar/baz/baz.html".  This routine is called only if html && splitting.
1421116530Sru
142293142Sru  Split html output goes into the subdirectory of the toplevel
142393142Sru  filename, without extension.  For example:
142493142Sru      @setfilename foo.info
1425100516Sru  produces output in files foo/index.html, foo/second-node.html, ...
1426116530Sru
1427100516Sru  But if the user said -o foo.whatever on the cmd line, then use
1428100516Sru  foo.whatever unchanged.  */
142993142Sru
143093142Srustatic char *
1431146520Sruinsert_toplevel_subdirectory (char *output_filename)
143293142Sru{
1433116530Sru  static const char index_name[] = "index.html";
143493142Sru  char *dir, *subdir, *base, *basename, *p;
143593142Sru  char buf[PATH_MAX];
143693142Sru  const int index_len = sizeof (index_name) - 1;
143793142Sru
143893142Sru  strcpy (buf, output_filename);
1439116530Sru  dir = pathname_part (buf);   /* directory of output_filename */
1440116530Sru  base = filename_part (buf);  /* strips suffix, too */
1441116530Sru  basename = xstrdup (base);   /* remember real @setfilename name */
144293142Sru  p = dir + strlen (dir) - 1;
144393142Sru  if (p > dir && IS_SLASH (*p))
144493142Sru    *p = 0;
144593142Sru  p = strrchr (base, '.');
144693142Sru  if (p)
144793142Sru    *p = 0;
1448100516Sru
144993142Sru  /* Split html output goes into subdirectory of toplevel name. */
1450116530Sru  if (save_command_output_filename
1451116530Sru      && STREQ (output_filename, save_command_output_filename))
1452116530Sru    subdir = basename;  /* from user, use unchanged */
1453116530Sru  else
1454116530Sru    subdir = base;      /* implicit, omit suffix */
145593142Sru
145693142Sru  free (output_filename);
1457100516Sru  output_filename = xmalloc (strlen (dir) + 1
1458114477Sru                             + strlen (basename) + 1
1459114477Sru                             + index_len
1460114477Sru                             + 1);
146193142Sru  strcpy (output_filename, dir);
146293142Sru  if (strlen (dir))
146393142Sru    strcat (output_filename, "/");
146493142Sru  strcat (output_filename, subdir);
1465146520Sru
1466146520Sru  /* First try, do not remove existing file.  */
1467146520Sru  if (!create_html_directory (output_filename, 0))
1468146520Sru    {
1469146520Sru      /* That failed, try subdir name with .html.
1470146520Sru         Remove it if it exists.  */
147193142Sru      strcpy (output_filename, dir);
147293142Sru      if (strlen (dir))
147393142Sru        strcat (output_filename, "/");
147493142Sru      strcat (output_filename, basename);
1475146520Sru
1476146520Sru      if (!create_html_directory (output_filename, 1))
1477114477Sru        {
1478146520Sru          /* Last try failed too :-\  */
147993142Sru          line_error (_("Can't create directory `%s': %s"),
1480146520Sru              output_filename, strerror (errno));
1481114477Sru          xexit (1);
148293142Sru        }
148393142Sru    }
1484146520Sru
1485146520Sru  strcat (output_filename, "/");
148693142Sru  strcat (output_filename, index_name);
148793142Sru  return output_filename;
148893142Sru}
148993142Sru
149093142Sru/* FIXME: this is way too hairy */
1491146520Srustatic void
1492146520Sruconvert_from_loaded_file (char *name)
149321495Sjmacd{
149456164Sru  char *real_output_filename = NULL;
149521495Sjmacd
149621495Sjmacd  remember_itext (input_text, 0);
149721495Sjmacd
149856164Sru  input_text_offset = 0;
149956164Sru
150056164Sru  /* Avoid the `\input texinfo' line in HTML output (assuming it starts
150156164Sru     the file).  */
150256164Sru  if (looking_at ("\\input"))
150356164Sru    discard_until ("\n");
150456164Sru
150521495Sjmacd  /* Search this file looking for the special string which starts conversion.
150621495Sjmacd     Once found, we may truly begin. */
150721495Sjmacd  while (input_text_offset >= 0)
150821495Sjmacd    {
150921495Sjmacd      input_text_offset =
151042664Smarkm        search_forward (setfilename_search, input_text_offset);
151121495Sjmacd
151256164Sru      if (input_text_offset == 0
151356164Sru          || (input_text_offset > 0
151456164Sru              && input_text[input_text_offset -1] == '\n'))
151542664Smarkm        break;
151621495Sjmacd      else if (input_text_offset > 0)
151742664Smarkm        input_text_offset++;
151821495Sjmacd    }
151921495Sjmacd
152021495Sjmacd  if (input_text_offset < 0)
152121495Sjmacd    {
152221495Sjmacd      if (!command_output_filename)
152342664Smarkm        {
152421495Sjmacd#if defined (REQUIRE_SETFILENAME)
152542664Smarkm          error (_("No `%s' found in `%s'"), setfilename_search, name);
152642664Smarkm          goto finished;
152721495Sjmacd#else
152856164Sru          command_output_filename = output_name_from_input_name (name);
152956164Sru#endif /* !REQUIRE_SETFILENAME */
153056164Sru        }
1531116530Sru
153256164Sru      {
153356164Sru        int i, end_of_first_line;
153421495Sjmacd
153556164Sru        /* Find the end of the first line in the file. */
153656164Sru        for (i = 0; i < input_text_length - 1; i++)
153756164Sru          if (input_text[i] == '\n')
153856164Sru            break;
153921495Sjmacd
154056164Sru        end_of_first_line = i + 1;
154121495Sjmacd
154256164Sru        for (i = 0; i < end_of_first_line; i++)
154356164Sru          {
154456164Sru            if ((input_text[i] == '\\') &&
154556164Sru                (strncmp (input_text + i + 1, "input", 5) == 0))
154656164Sru              {
154756164Sru                input_text_offset = i;
154856164Sru                break;
154956164Sru              }
155056164Sru          }
155156164Sru      }
155221495Sjmacd    }
155321495Sjmacd  else
155421495Sjmacd    input_text_offset += strlen (setfilename_search);
155521495Sjmacd
155621495Sjmacd  if (!command_output_filename)
155756164Sru    {
155856164Sru      get_until ("\n", &output_filename); /* read rest of line */
155993142Sru      if (html || xml)
156093142Sru        { /* Change any extension to .html or .xml.  */
156156164Sru          char *html_name, *directory_part, *basename_part, *temp;
156256164Sru
156356164Sru          canon_white (output_filename);
156456164Sru          directory_part = pathname_part (output_filename);
156593142Sru
156656164Sru          basename_part = filename_part (output_filename);
156756164Sru
156856164Sru          /* Zap any existing extension.  */
156956164Sru          temp = strrchr (basename_part, '.');
157056164Sru          if (temp)
157156164Sru            *temp = 0;
157256164Sru
157356164Sru          /* Construct new filename.  */
157456164Sru          html_name = xmalloc (strlen (directory_part)
157556164Sru                               + strlen (basename_part) + 6);
157656164Sru          strcpy (html_name, directory_part);
157756164Sru          strcat (html_name, basename_part);
157893142Sru          strcat (html_name, html ? ".html" : ".xml");
157956164Sru
158056164Sru          /* Replace name from @setfilename with the html name.  */
158156164Sru          free (output_filename);
158256164Sru          output_filename = html_name;
158356164Sru        }
158456164Sru    }
158521495Sjmacd  else
158621495Sjmacd    {
158721495Sjmacd      if (input_text_offset != -1)
158842664Smarkm        discard_until ("\n");
158921495Sjmacd      else
159042664Smarkm        input_text_offset = 0;
159121495Sjmacd
159221495Sjmacd      real_output_filename = output_filename = command_output_filename;
1593100516Sru      command_output_filename = NULL;  /* for included files or whatever */
159421495Sjmacd    }
159521495Sjmacd
159621495Sjmacd  canon_white (output_filename);
159793142Sru  toplevel_output_filename = xstrdup (output_filename);
159821495Sjmacd
159942664Smarkm  if (real_output_filename && strcmp (real_output_filename, "-") == 0)
160021495Sjmacd    {
160142664Smarkm      if (macro_expansion_filename
160242664Smarkm          && strcmp (macro_expansion_filename, "-") == 0)
160342664Smarkm        {
160493142Sru          fprintf (stderr,
160593142Sru  _("%s: Skipping macro expansion to stdout as Info output is going there.\n"),
160642664Smarkm                   progname);
160742664Smarkm          macro_expansion_output_stream = NULL;
160842664Smarkm        }
160942664Smarkm      real_output_filename = xstrdup (real_output_filename);
161021495Sjmacd      output_stream = stdout;
161142664Smarkm      splitting = 0;            /* Cannot split when writing to stdout. */
161221495Sjmacd    }
161321495Sjmacd  else
161421495Sjmacd    {
161593142Sru      if (html && splitting)
161693142Sru        {
1617114477Sru          if (FILENAME_CMP (output_filename, NULL_DEVICE) == 0
1618114477Sru              || FILENAME_CMP (output_filename, ALSO_NULL_DEVICE) == 0)
1619114477Sru            splitting = 0;
1620114477Sru          else
1621114477Sru            output_filename = insert_toplevel_subdirectory (output_filename);
162293142Sru          real_output_filename = xstrdup (output_filename);
162393142Sru        }
162493142Sru      else if (!real_output_filename)
162542664Smarkm        real_output_filename = expand_filename (output_filename, name);
162621495Sjmacd      else
162742664Smarkm        real_output_filename = xstrdup (real_output_filename);
162821495Sjmacd
162921495Sjmacd      output_stream = fopen (real_output_filename, "w");
163021495Sjmacd    }
163121495Sjmacd
163256164Sru  set_current_output_filename (real_output_filename);
163356164Sru
1634146520Sru  if (xml && !docbook)
1635146520Sru    xml_begin_document (filename_part (output_filename));
1636146520Sru
163756164Sru  if (verbose_mode)
163842664Smarkm    printf (_("Making %s file `%s' from `%s'.\n"),
163993142Sru            no_headers ? "text"
164093142Sru            : html ? "HTML"
164193142Sru            : xml ? "XML"
164293142Sru            : "info",
164356164Sru            output_filename, input_filename);
164421495Sjmacd
164521495Sjmacd  if (output_stream == NULL)
164621495Sjmacd    {
164721495Sjmacd      fs_error (real_output_filename);
164821495Sjmacd      goto finished;
164921495Sjmacd    }
165021495Sjmacd
165121495Sjmacd  /* Make the displayable filename from output_filename.  Only the base
165221495Sjmacd     portion of the filename need be displayed. */
1653114477Sru  flush_output ();              /* in case there was no @bye */
165421495Sjmacd  if (output_stream != stdout)
165521495Sjmacd    pretty_output_filename = filename_part (output_filename);
165621495Sjmacd  else
165742664Smarkm    pretty_output_filename = xstrdup ("stdout");
165821495Sjmacd
165921495Sjmacd  /* For this file only, count the number of newlines from the top of
166021495Sjmacd     the file to here.  This way, we keep track of line numbers for
166121495Sjmacd     error reporting.  Line_number starts at 1, since the user isn't
166221495Sjmacd     zero-based. */
166321495Sjmacd  {
166421495Sjmacd    int temp = 0;
166521495Sjmacd    line_number = 1;
166621495Sjmacd    while (temp != input_text_offset)
166721495Sjmacd      if (input_text[temp++] == '\n')
166842664Smarkm        line_number++;
166921495Sjmacd  }
167021495Sjmacd
167156164Sru  /* html fixxme: should output this as trailer on first page.  */
167293142Sru  if (!no_headers && !html && !xml)
167356164Sru    add_word_args (_("This is %s, produced by makeinfo version %s from %s.\n"),
167456164Sru                   output_filename, VERSION, input_filename);
167521495Sjmacd
167621495Sjmacd  close_paragraph ();
1677146520Sru
1678146520Sru  if (xml && !docbook)
1679146520Sru    {
1680146520Sru      /* Just before the real main loop, let's handle the defines.  */
1681146520Sru      COMMAND_LINE_DEFINE *temp;
1682146520Sru
1683146520Sru      for (temp = command_line_defines; temp; temp = temp->next)
1684146520Sru        {
1685146520Sru          handle_variable_internal (temp->action, temp->define);
1686146520Sru          free(temp->define);
1687146520Sru        }
1688146520Sru    }
1689146520Sru
169021495Sjmacd  reader_loop ();
169193142Sru  if (xml)
169293142Sru    xml_end_document ();
169321495Sjmacd
1694116530Sru
169521495Sjmacdfinished:
169642664Smarkm  discard_insertions (0);
169721495Sjmacd  close_paragraph ();
169821495Sjmacd  flush_file_stack ();
169921495Sjmacd
170021495Sjmacd  if (macro_expansion_output_stream)
170142664Smarkm    {
170242664Smarkm      fclose (macro_expansion_output_stream);
170342664Smarkm      if (errors_printed && !force
170442664Smarkm          && strcmp (macro_expansion_filename, "-") != 0
170556164Sru          && FILENAME_CMP (macro_expansion_filename, NULL_DEVICE) != 0
170656164Sru          && FILENAME_CMP (macro_expansion_filename, ALSO_NULL_DEVICE) != 0)
170742664Smarkm        {
1708146520Sru          fprintf (stderr,
1709146520Sru_("%s: Removing macro output file `%s' due to errors; use --force to preserve.\n"),
171042664Smarkm                   progname, macro_expansion_filename);
171142664Smarkm          if (unlink (macro_expansion_filename) < 0)
171242664Smarkm            perror (macro_expansion_filename);
171342664Smarkm        }
171442664Smarkm    }
171521495Sjmacd
171642664Smarkm  if (output_stream)
171721495Sjmacd    {
171821495Sjmacd      output_pending_notes ();
171921495Sjmacd
172056164Sru      if (html)
172156164Sru        {
1722146520Sru          no_indent = 1;
172356164Sru          start_paragraph ();
172456164Sru          add_word ("</body></html>\n");
172556164Sru          close_paragraph ();
172656164Sru        }
172756164Sru
1728116530Sru      /* maybe we want local variables in info output.  */
1729116530Sru      {
1730116530Sru        char *trailer = info_trailer ();
1731146520Sru	if (!xml && !docbook && trailer)
1732116530Sru          {
1733146520Sru            if (html)
1734146520Sru              insert_string ("<!--");
1735116530Sru            insert_string (trailer);
1736116530Sru            free (trailer);
1737146520Sru            if (html)
1738146520Sru              insert_string ("\n-->\n");
1739116530Sru          }
1740116530Sru      }
1741116530Sru
1742146520Sru      /* Write stuff makeinfo generates after @bye, ie. info_trailer.  */
1743146520Sru      flush_output ();
1744116530Sru
174521495Sjmacd      if (output_stream != stdout)
174642664Smarkm        fclose (output_stream);
174721495Sjmacd
174821495Sjmacd      /* If validating, then validate the entire file right now. */
174921495Sjmacd      if (validating)
175042664Smarkm        validate_file (tag_table);
175121495Sjmacd
1752146520Sru      handle_delayed_writes ();
175356164Sru
1754146520Sru      if (tag_table)
1755146520Sru        {
1756146520Sru          tag_table = (TAG_ENTRY *) reverse_list ((GENERIC_LIST *) tag_table);
1757146520Sru          if (!no_headers && !html && !STREQ (current_output_filename, "-"))
1758146520Sru            write_tag_table (real_output_filename);
1759146520Sru        }
1760146520Sru
176156164Sru      if (splitting && !html && (!errors_printed || force))
1762146520Sru        {
1763146520Sru          clean_old_split_files (real_output_filename);
1764146520Sru          split_file (real_output_filename, split_size);
1765146520Sru        }
176656164Sru      else if (errors_printed
176756164Sru               && !force
176842664Smarkm               && strcmp (real_output_filename, "-") != 0
176956164Sru               && FILENAME_CMP (real_output_filename, NULL_DEVICE) != 0
177056164Sru               && FILENAME_CMP (real_output_filename, ALSO_NULL_DEVICE) != 0)
177142664Smarkm        { /* If there were errors, and no --force, remove the output.  */
1772146520Sru          fprintf (stderr,
1773146520Sru  _("%s: Removing output file `%s' due to errors; use --force to preserve.\n"),
177442664Smarkm                   progname, real_output_filename);
177542664Smarkm          if (unlink (real_output_filename) < 0)
177642664Smarkm            perror (real_output_filename);
177742664Smarkm        }
177821495Sjmacd    }
177921495Sjmacd  free (real_output_filename);
178021495Sjmacd}
1781116530Sru
1782146520Sru/* If enable_encoding is set and @documentencoding is used, return a
1783146520Sru   Local Variables section (as a malloc-ed string) so that Emacs'
1784146520Sru   locale features can work.  Else return NULL.  */
1785116530Sruchar *
1786146520Sruinfo_trailer (void)
1787116530Sru{
1788146520Sru  char *encoding;
1789146520Sru
1790146520Sru  if (!enable_encoding)
1791116530Sru    return NULL;
1792116530Sru
1793146520Sru  encoding = current_document_encoding ();
1794146520Sru
1795146520Sru  if (encoding && *encoding)
1796146520Sru    {
1797116530Sru#define LV_FMT "\n\037\nLocal Variables:\ncoding: %s\nEnd:\n"
1798146520Sru      char *lv = xmalloc (sizeof (LV_FMT) + strlen (encoding));
1799146520Sru      sprintf (lv, LV_FMT, encoding);
1800146520Sru      free (encoding);
1801146520Sru      return lv;
1802146520Sru    }
1803146520Sru
1804146520Sru  free (encoding);
1805146520Sru  return NULL;
1806116530Sru}
1807116530Sru
180821495Sjmacdvoid
1809146520Srufree_and_clear (char **pointer)
181021495Sjmacd{
181142664Smarkm  if (*pointer)
181221495Sjmacd    {
181321495Sjmacd      free (*pointer);
181456164Sru      *pointer = NULL;
181521495Sjmacd    }
181621495Sjmacd}
181721495Sjmacd
181821495Sjmacd /* Initialize some state. */
1819146520Srustatic void
1820146520Sruinit_internals (void)
182121495Sjmacd{
182221495Sjmacd  free_and_clear (&output_filename);
182321495Sjmacd  free_and_clear (&command);
182421495Sjmacd  free_and_clear (&input_filename);
182521495Sjmacd  free_node_references ();
182656164Sru  free_node_node_references ();
182756164Sru  toc_free ();
182821495Sjmacd  init_insertion_stack ();
182921495Sjmacd  init_brace_stack ();
183042664Smarkm  current_node = NULL; /* sometimes already freed */
183121495Sjmacd  command_index = 0;
183221495Sjmacd  in_menu = 0;
183321495Sjmacd  in_detailmenu = 0;
183421495Sjmacd  top_node_seen = 0;
183521495Sjmacd  non_top_node_seen = 0;
183656164Sru  node_number = -1;
183721495Sjmacd}
183821495Sjmacd
183921495Sjmacdvoid
1840146520Sruinit_paragraph (void)
184121495Sjmacd{
1842146520Sru  free (output_paragraph);
184356164Sru  output_paragraph = xmalloc (paragraph_buffer_len);
184442664Smarkm  output_paragraph[0] = 0;
184521495Sjmacd  output_paragraph_offset = 0;
184621495Sjmacd  output_column = 0;
184721495Sjmacd  paragraph_is_open = 0;
184821495Sjmacd  current_indent = 0;
184956164Sru  meta_char_pos = 0;
185021495Sjmacd}
185156164Sru
185256164Sru/* This is called from `reader_loop' when we are at the * beginning a
185356164Sru   menu line.  */
185421495Sjmacd
185556164Srustatic void
1856146520Sruhandle_menu_entry (void)
185721495Sjmacd{
185856164Sru  char *tem;
1859116530Sru
186056164Sru  /* Ugh, glean_node_from_menu wants to read the * itself.  */
186156164Sru  input_text_offset--;
1862116530Sru
186356164Sru  /* Find node name in menu entry and save it in references list for
186456164Sru     later validation.  Use followed_reference type for detailmenu
186556164Sru     references since we don't want to use them for default node pointers.  */
186656164Sru  tem = glean_node_from_menu (1, in_detailmenu
186756164Sru                                 ? followed_reference : menu_reference);
186821495Sjmacd
186956164Sru  if (html && tem)
187056164Sru    { /* Start a menu item with the cleaned-up line.  Put an anchor
187156164Sru         around the start text (before `:' or the node name). */
187256164Sru      char *string;
187321495Sjmacd
187456164Sru      discard_until ("* ");
187521495Sjmacd
187656164Sru      /* The line number was already incremented in reader_loop when we
187756164Sru         saw the newline, and discard_until has now incremented again.  */
187856164Sru      line_number--;
187921495Sjmacd
188056164Sru      if (had_menu_commentary)
188142664Smarkm        {
1882146520Sru          add_html_block_elt ("<ul class=\"menu\">\n");
188356164Sru          had_menu_commentary = 0;
188456164Sru          in_paragraph = 0;
188542664Smarkm        }
1886116530Sru
188756164Sru      if (in_paragraph)
188842664Smarkm        {
1889146520Sru          add_html_block_elt ("</p>\n");
1890146520Sru          add_html_block_elt ("<ul class=\"menu\">\n");
189156164Sru          in_paragraph = 0;
189242664Smarkm        }
189321495Sjmacd
1894146520Sru      in_menu_item = 1;
1895146520Sru
1896146520Sru      add_html_block_elt ("<li><a");
1897100516Sru      if (next_menu_item_number <= 9)
1898114477Sru        {
1899114477Sru          add_word(" accesskey=");
1900114477Sru          add_word_args("\"%d\"", next_menu_item_number);
1901114477Sru          next_menu_item_number++;
1902114477Sru        }
1903100516Sru      add_word (" href=\"");
190456164Sru      string = expansion (tem, 0);
190556164Sru      add_anchor_name (string, 1);
190656164Sru      add_word ("\">");
190756164Sru      free (string);
190821495Sjmacd
190956164Sru      /* The menu item may use macros, so expand them now.  */
191056164Sru      only_macro_expansion++;
191156164Sru      get_until_in_line (1, ":", &string);
191256164Sru      only_macro_expansion--;
191356164Sru      execute_string ("%s", string); /* get escaping done */
191456164Sru      free (string);
191521495Sjmacd
191656164Sru      add_word ("</a>");
191721495Sjmacd
191856164Sru      if (looking_at ("::"))
191956164Sru        discard_until (":");
192056164Sru      else
192156164Sru        { /* discard the node name */
192256164Sru          get_until_in_line (0, ".", &string);
192356164Sru          free (string);
192442664Smarkm        }
1925114477Sru      input_text_offset++;      /* discard the second colon or the period */
1926146520Sru
1927146520Sru      /* Insert a colon only if there is a description of this menu item.  */
1928146520Sru      {
1929146520Sru        int save_input_text_offset = input_text_offset;
1930146520Sru        int save_line_number = line_number;
1931146520Sru        char *test_string;
1932146520Sru        get_rest_of_line (0, &test_string);
1933146520Sru        if (strlen (test_string) > 0)
1934146520Sru          add_word (": ");
1935146520Sru        input_text_offset = save_input_text_offset;
1936146520Sru        line_number = save_line_number;
1937146520Sru      }
193821495Sjmacd    }
193993142Sru  else if (xml && tem)
1940116530Sru    {
194193142Sru      xml_start_menu_entry (tem);
194293142Sru    }
194356164Sru  else if (tem)
194456164Sru    { /* For Info output, we can just use the input and the main case in
194556164Sru         reader_loop where we output what comes in.  Just move off the *
194656164Sru         so the next time through reader_loop we don't end up back here.  */
194756164Sru      add_char ('*');
194856164Sru      input_text_offset += 2; /* undo the pointer back-up above.  */
194956164Sru    }
195056164Sru
195156164Sru  if (tem)
195256164Sru    free (tem);
195321495Sjmacd}
195456164Sru
195556164Sru/* Find the command corresponding to STRING.  If the command is found,
195656164Sru   return a pointer to the data structure.  Otherwise return -1.  */
195756164Srustatic COMMAND *
1958146520Sruget_command_entry (char *string)
195921495Sjmacd{
196056164Sru  int i;
196121495Sjmacd
196242664Smarkm  for (i = 0; command_table[i].name; i++)
196342664Smarkm    if (strcmp (command_table[i].name, string) == 0)
196456164Sru      return &command_table[i];
196521495Sjmacd
196621495Sjmacd  /* This command is not in our predefined command table.  Perhaps
196721495Sjmacd     it is a user defined command. */
196821495Sjmacd  for (i = 0; i < user_command_array_len; i++)
196921495Sjmacd    if (user_command_array[i] &&
197042664Smarkm        (strcmp (user_command_array[i]->name, string) == 0))
197156164Sru      return user_command_array[i];
197221495Sjmacd
197342664Smarkm  /* We never heard of this command. */
197456164Sru  return (COMMAND *) -1;
197521495Sjmacd}
197656164Sru
197721495Sjmacd/* input_text_offset is right at the command prefix character.
197856164Sru   Read the next token to determine what to do.  Return zero
197956164Sru   if there's no known command or macro after the prefix character.  */
198056164Srustatic int
1981146520Sruread_command (void)
198221495Sjmacd{
198321495Sjmacd  COMMAND *entry;
198456164Sru  int old_text_offset = input_text_offset++;
198521495Sjmacd
198621495Sjmacd  free_and_clear (&command);
198721495Sjmacd  command = read_token ();
198821495Sjmacd
198921495Sjmacd  /* Check to see if this command is a macro.  If so, execute it here. */
199021495Sjmacd  {
199121495Sjmacd    MACRO_DEF *def;
199221495Sjmacd
199321495Sjmacd    def = find_macro (command);
199421495Sjmacd
199521495Sjmacd    if (def)
199621495Sjmacd      {
199742664Smarkm        /* We disallow recursive use of a macro call.  Inhibit the expansion
199842664Smarkm           of this macro during the life of its execution. */
199942664Smarkm        if (!(def->flags & ME_RECURSE))
200042664Smarkm          def->inhibited = 1;
200121495Sjmacd
2002114477Sru        executing_macro++;
200342664Smarkm        execute_macro (def);
2004114477Sru        executing_macro--;
200521495Sjmacd
200642664Smarkm        if (!(def->flags & ME_RECURSE))
200742664Smarkm          def->inhibited = 0;
200821495Sjmacd
200956164Sru        return 1;
201021495Sjmacd      }
2011146520Sru  }
201221495Sjmacd
201356164Sru  if (only_macro_expansion)
201456164Sru    {
201556164Sru      /* Back up to the place where we were called, so the
201656164Sru         caller will have a chance to process this non-macro.  */
201756164Sru      input_text_offset = old_text_offset;
201856164Sru      return 0;
201956164Sru    }
202056164Sru
202156164Sru  /* Perform alias expansion */
202256164Sru  command = alias_expand (command);
202356164Sru
202456164Sru  if (enclosure_command (command))
202556164Sru    {
202656164Sru      remember_brace (enclosure_expand);
202756164Sru      enclosure_expand (START, output_paragraph_offset, 0);
202856164Sru      return 0;
202956164Sru    }
203056164Sru
203121495Sjmacd  entry = get_command_entry (command);
203221495Sjmacd  if (entry == (COMMAND *)-1)
203321495Sjmacd    {
203442664Smarkm      line_error (_("Unknown command `%s'"), command);
203556164Sru      return 0;
203621495Sjmacd    }
203721495Sjmacd
203856164Sru  if (entry->argument_in_braces == BRACE_ARGS)
203921495Sjmacd    remember_brace (entry->proc);
204056164Sru  else if (entry->argument_in_braces == MAYBE_BRACE_ARGS)
204156164Sru    {
204256164Sru      if (curchar () == '{')
204356164Sru        remember_brace (entry->proc);
204456164Sru      else
204556164Sru        { /* No braces, so arg is next char.  */
204656164Sru          int ch;
204756164Sru          int saved_offset = output_paragraph_offset;
204856164Sru          (*(entry->proc)) (START, output_paragraph_offset, 0);
204921495Sjmacd
205056164Sru          /* Possibilities left for the next character: @ (error), }
205156164Sru             (error), whitespace (skip) anything else (normal char).  */
205256164Sru          skip_whitespace ();
205356164Sru          ch = curchar ();
205456164Sru          if (ch == '@')
205556164Sru            {
205656164Sru           line_error (_("Use braces to give a command as an argument to @%s"),
205756164Sru               entry->name);
205856164Sru              return 0;
205956164Sru            }
206056164Sru          else if (ch == '}')
206156164Sru            {
206256164Sru              /* Our caller will give the error message, because this }
206356164Sru                 won't match anything.  */
206456164Sru              return 0;
206556164Sru            }
206656164Sru
206756164Sru          add_char (ch);
206856164Sru          input_text_offset++;
206956164Sru          (*(entry->proc)) (END, saved_offset, output_paragraph_offset);
207056164Sru          return 1;
207156164Sru        }
207256164Sru    }
207356164Sru
207456164Sru  /* Get here if we have BRACE_ARGS, NO_BRACE_ARGS, or MAYBE_BRACE_ARGS
207556164Sru     with braces.  */
207621495Sjmacd  (*(entry->proc)) (START, output_paragraph_offset, 0);
207756164Sru  return 1;
207821495Sjmacd}
207921495Sjmacd
208056164Sru/* Okay, we are ready to start the conversion.  Call the reader on
208156164Sru   some text, and fill the text as it is output.  Handle commands by
208256164Sru   remembering things like open braces and the current file position on a
208356164Sru   stack, and when the corresponding close brace is found, you can call
208456164Sru   the function with the proper arguments.  Although the filling isn't
208556164Sru   necessary for HTML, it should do no harm.  */
208656164Sruvoid
2087146520Srureader_loop (void)
208821495Sjmacd{
208956164Sru  int character;
209056164Sru  int done = 0;
209121495Sjmacd
209256164Sru  while (!done)
209356164Sru    {
209456164Sru      if (input_text_offset >= input_text_length)
209556164Sru        break;
209656164Sru
209756164Sru      character = curchar ();
209856164Sru
209956164Sru      /* If only_macro_expansion, only handle macros and leave
210056164Sru         everything else intact.  */
210156164Sru      if (!only_macro_expansion && !in_fixed_width_font
2102146520Sru          && ((!html && !xml) || escape_html)
210356164Sru          && (character == '\'' || character == '`')
210456164Sru          && input_text[input_text_offset + 1] == character)
210556164Sru        {
2106146520Sru          if (html)
2107146520Sru            {
2108146520Sru              input_text_offset += 2;
2109146520Sru              add_word (character == '`' ? "&ldquo;" : "&rdquo;");
2110146520Sru              continue;
2111146520Sru            }
2112146520Sru          else if (xml)
2113146520Sru            {
2114146520Sru              input_text_offset += 2;
2115146520Sru              xml_insert_entity (character == '`' ? "ldquo" : "rdquo");
2116146520Sru              continue;
2117146520Sru            }
2118146520Sru          else
2119146520Sru            {
2120146520Sru              input_text_offset++;
2121146520Sru              character = '"';
2122146520Sru            }
212356164Sru        }
212456164Sru
212556164Sru      /* Convert --- to --.  */
2126146520Sru      if (!only_macro_expansion && character == '-' && !in_fixed_width_font
2127146520Sru          && ((!html && !xml) || escape_html))
212856164Sru        {
2129146520Sru          int dash_count = 0;
2130146520Sru
2131146520Sru          /* Get the number of consequtive dashes.  */
2132146520Sru          while (input_text[input_text_offset] == '-')
213356164Sru            {
2134146520Sru              dash_count++;
213556164Sru              input_text_offset++;
213656164Sru            }
2137146520Sru
2138146520Sru          /* Eat one dash.  */
2139146520Sru          dash_count--;
2140146520Sru
2141146520Sru          if (html || xml)
2142146520Sru            {
2143146520Sru              if (dash_count == 0)
2144146520Sru                add_char ('-');
2145146520Sru              else
2146146520Sru                while (dash_count > 0)
2147146520Sru                  {
2148146520Sru                    if (dash_count >= 2)
2149146520Sru                      {
2150146520Sru                        if (html)
2151146520Sru                          add_word ("&mdash;");
2152146520Sru                        else
2153146520Sru                          xml_insert_entity ("mdash");
2154146520Sru                        dash_count -= 2;
2155146520Sru                      }
2156146520Sru                    else if (dash_count >= 1)
2157146520Sru                      {
2158146520Sru                        if (html)
2159146520Sru                          add_word ("&ndash;");
2160146520Sru                        else
2161146520Sru                          xml_insert_entity ("ndash");
2162146520Sru                        dash_count--;
2163146520Sru                      }
2164146520Sru                  }
2165146520Sru            }
2166146520Sru          else
2167146520Sru            {
2168146520Sru              add_char ('-');
2169146520Sru              while (--dash_count > 0)
2170146520Sru                add_char ('-');
2171146520Sru            }
2172146520Sru
2173146520Sru          continue;
217456164Sru        }
217556164Sru
217656164Sru      /* If this is a whitespace character, then check to see if the line
217756164Sru         is blank.  If so, advance to the carriage return. */
217856164Sru      if (!only_macro_expansion && whitespace (character))
217956164Sru        {
218056164Sru          int i = input_text_offset + 1;
218156164Sru
218256164Sru          while (i < input_text_length && whitespace (input_text[i]))
218356164Sru            i++;
218456164Sru
218556164Sru          if (i == input_text_length || input_text[i] == '\n')
218656164Sru            {
218756164Sru              if (i == input_text_length)
218856164Sru                i--;
218956164Sru
219056164Sru              input_text_offset = i;
219156164Sru              character = curchar ();
219256164Sru            }
219356164Sru        }
219456164Sru
219556164Sru      if (character == '\n')
219656164Sru        line_number++;
219756164Sru
219856164Sru      switch (character)
219956164Sru        {
220056164Sru        case '*': /* perhaps we are at a menu */
220156164Sru          /* We used to check for this in the \n case but an @c in a
220256164Sru             menu swallows its newline, so check here instead.  */
220356164Sru          if (!only_macro_expansion && in_menu
220456164Sru              && input_text_offset + 1 < input_text_length
220556164Sru              && input_text[input_text_offset-1] == '\n')
220656164Sru            handle_menu_entry ();
220756164Sru          else
220856164Sru            { /* Duplicate code from below, but not worth twisting the
220956164Sru                 fallthroughs to get down there.  */
221056164Sru              add_char (character);
221156164Sru              input_text_offset++;
221256164Sru            }
221356164Sru          break;
2214116530Sru
221556164Sru        /* Escapes for HTML unless we're outputting raw HTML.  Do
221656164Sru           this always, even if SGML rules don't require it since
221756164Sru           that's easier and safer for non-conforming browsers. */
221856164Sru        case '&':
221956164Sru          if (html && escape_html)
222056164Sru            add_word ("&amp;");
222156164Sru          else
222256164Sru            add_char (character);
222356164Sru          input_text_offset++;
222456164Sru          break;
222556164Sru
222656164Sru        case '<':
222756164Sru          if (html && escape_html)
222856164Sru            add_word ("&lt;");
2229114477Sru          else if (xml && escape_html)
2230114477Sru            xml_insert_entity ("lt");
223156164Sru          else
223256164Sru            add_char (character);
223356164Sru          input_text_offset++;
223456164Sru          break;
223556164Sru
223656164Sru        case '>':
223756164Sru          if (html && escape_html)
223856164Sru            add_word ("&gt;");
2239114477Sru          else if (xml && escape_html)
2240114477Sru            xml_insert_entity ("gt");
224156164Sru          else
224256164Sru            add_char (character);
224356164Sru          input_text_offset++;
224456164Sru          break;
224556164Sru
224656164Sru        case COMMAND_PREFIX: /* @ */
224756164Sru          if (read_command () || !only_macro_expansion)
224856164Sru            break;
224956164Sru
225056164Sru        /* FALLTHROUGH (usually) */
225156164Sru        case '{':
225256164Sru          /* Special case.  We're not supposed to see this character by itself.
225356164Sru             If we do, it means there is a syntax error in the input text.
225456164Sru             Report the error here, but remember this brace on the stack so
225556164Sru             we can ignore its partner. */
225656164Sru          if (!only_macro_expansion)
225756164Sru            {
2258114477Sru              if (command && !STREQ (command, "math"))
225993142Sru                {
226093142Sru                  line_error (_("Misplaced %c"), '{');
226193142Sru                  remember_brace (misplaced_brace);
226293142Sru                }
226393142Sru              else
2264146520Sru                /* We don't mind `extra' braces inside @math.  */
2265146520Sru                remember_brace (cm_no_op);
226656164Sru              /* remember_brace advances input_text_offset.  */
226756164Sru              break;
226856164Sru            }
226956164Sru
227056164Sru        /* FALLTHROUGH (usually) */
227156164Sru        case '}':
227256164Sru          if (!only_macro_expansion)
227356164Sru            {
227456164Sru              pop_and_call_brace ();
227556164Sru              input_text_offset++;
227656164Sru              break;
227756164Sru            }
227856164Sru
227956164Sru        /* FALLTHROUGH (usually) */
228056164Sru        default:
228156164Sru          add_char (character);
228256164Sru          input_text_offset++;
228356164Sru        }
228456164Sru    }
228556164Sru  if (macro_expansion_output_stream && !only_macro_expansion)
228656164Sru    maybe_write_itext (input_text, input_text_offset);
228721495Sjmacd}
228856164Sru
2289146520Srustatic void
2290146520Sruinit_brace_stack (void)
229121495Sjmacd{
229256164Sru  brace_stack = NULL;
229321495Sjmacd}
229421495Sjmacd
229521495Sjmacd/* Remember the current output position here.  Save PROC
229621495Sjmacd   along with it so you can call it later. */
2297146520Srustatic void
2298146520Sruremember_brace_1 (COMMAND_FUNCTION (*proc), int position)
229921495Sjmacd{
230056164Sru  BRACE_ELEMENT *new = xmalloc (sizeof (BRACE_ELEMENT));
230121495Sjmacd  new->next = brace_stack;
230221495Sjmacd  new->proc = proc;
230393142Sru  new->command = command ? xstrdup (command) : "";
230421495Sjmacd  new->pos = position;
230521495Sjmacd  new->line = line_number;
230621495Sjmacd  new->in_fixed_width_font = in_fixed_width_font;
230721495Sjmacd  brace_stack = new;
230821495Sjmacd}
230921495Sjmacd
2310146520Srustatic void
2311146520Sruremember_brace (COMMAND_FUNCTION (*proc))
2312146520Sru{
2313146520Sru  if (curchar () != '{')
2314146520Sru    line_error (_("%c%s expected braces"), COMMAND_PREFIX, command);
2315146520Sru  else
2316146520Sru    input_text_offset++;
2317146520Sru  remember_brace_1 (proc, output_paragraph_offset);
2318146520Sru}
2319146520Sru
232021495Sjmacd/* Pop the top of the brace stack, and call the associated function
232121495Sjmacd   with the args END and POS. */
2322146520Srustatic void
2323146520Srupop_and_call_brace (void)
232421495Sjmacd{
232556164Sru  if (brace_stack == NULL)
232621495Sjmacd    {
232742664Smarkm      line_error (_("Unmatched }"));
232821495Sjmacd      return;
232921495Sjmacd    }
233021495Sjmacd
233156164Sru  {
233256164Sru    BRACE_ELEMENT *temp;
233321495Sjmacd
233456164Sru    int pos = brace_stack->pos;
233556164Sru    COMMAND_FUNCTION *proc = brace_stack->proc;
233656164Sru    in_fixed_width_font = brace_stack->in_fixed_width_font;
233756164Sru
233856164Sru    /* Reset current command, so the proc can know who it is.  This is
233956164Sru       used in cm_accent.  */
234056164Sru    command = brace_stack->command;
234156164Sru
234256164Sru    temp = brace_stack->next;
234356164Sru    free (brace_stack);
234456164Sru    brace_stack = temp;
234556164Sru
234656164Sru    (*proc) (END, pos, output_paragraph_offset);
234756164Sru  }
234821495Sjmacd}
234921495Sjmacd
235021495Sjmacd/* Shift all of the markers in `brace_stack' by AMOUNT. */
2351146520Srustatic void
2352146520Sruadjust_braces_following (int here, int amount)
235321495Sjmacd{
235456164Sru  BRACE_ELEMENT *stack = brace_stack;
235521495Sjmacd
235621495Sjmacd  while (stack)
235721495Sjmacd    {
235821495Sjmacd      if (stack->pos >= here)
235942664Smarkm        stack->pos += amount;
236021495Sjmacd      stack = stack->next;
236121495Sjmacd    }
236221495Sjmacd}
236321495Sjmacd
236456164Sru/* Return the string which invokes PROC; a pointer to a function.
236556164Sru   Always returns the first function in the command table if more than
236656164Sru   one matches PROC.  */
2367116530Srustatic const char *
2368146520Srufind_proc_name (COMMAND_FUNCTION (*proc))
236956164Sru{
237056164Sru  int i;
237156164Sru
237256164Sru  for (i = 0; command_table[i].name; i++)
237356164Sru    if (proc == command_table[i].proc)
237456164Sru      return command_table[i].name;
237556164Sru  return _("NO_NAME!");
237656164Sru}
237756164Sru
237821495Sjmacd/* You call discard_braces () when you shouldn't have any braces on the stack.
237921495Sjmacd   I used to think that this happens for commands that don't take arguments
238021495Sjmacd   in braces, but that was wrong because of things like @code{foo @@}.  So now
238121495Sjmacd   I only detect it at the beginning of nodes. */
238221495Sjmacdvoid
2383146520Srudiscard_braces (void)
238421495Sjmacd{
238521495Sjmacd  if (!brace_stack)
238621495Sjmacd    return;
238721495Sjmacd
238821495Sjmacd  while (brace_stack)
238921495Sjmacd    {
239021495Sjmacd      if (brace_stack->proc != misplaced_brace)
239142664Smarkm        {
2392116530Sru          const char *proc_name;
239321495Sjmacd
239442664Smarkm          proc_name = find_proc_name (brace_stack->proc);
239593142Sru          file_line_error (input_filename, brace_stack->line,
2396114477Sru                           _("%c%s missing close brace"), COMMAND_PREFIX,
2397114477Sru                           proc_name);
239842664Smarkm          pop_and_call_brace ();
239942664Smarkm        }
240021495Sjmacd      else
240142664Smarkm        {
240242664Smarkm          BRACE_ELEMENT *temp;
240342664Smarkm          temp = brace_stack->next;
240442664Smarkm          free (brace_stack);
240542664Smarkm          brace_stack = temp;
240642664Smarkm        }
240721495Sjmacd    }
240821495Sjmacd}
240921495Sjmacd
2410146520Srustatic int
2411146520Sruget_char_len (int character)
241221495Sjmacd{
241321495Sjmacd  /* Return the printed length of the character. */
241421495Sjmacd  int len;
241521495Sjmacd
241621495Sjmacd  switch (character)
241721495Sjmacd    {
241821495Sjmacd    case '\t':
241921495Sjmacd      len = (output_column + 8) & 0xf7;
242021495Sjmacd      if (len > fill_column)
242142664Smarkm        len = fill_column - output_column;
242221495Sjmacd      else
242342664Smarkm        len = len - output_column;
242421495Sjmacd      break;
242521495Sjmacd
242621495Sjmacd    case '\n':
242721495Sjmacd      len = fill_column - output_column;
242821495Sjmacd      break;
242921495Sjmacd
243021495Sjmacd    default:
243142664Smarkm      /* ASCII control characters appear as two characters in the output
243242664Smarkm         (e.g., ^A).  But characters with the high bit set are just one
243342664Smarkm         on suitable terminals, so don't count them as two for line
243442664Smarkm         breaking purposes.  */
243542664Smarkm      if (0 <= character && character < ' ')
243642664Smarkm        len = 2;
243721495Sjmacd      else
243842664Smarkm        len = 1;
243921495Sjmacd    }
244056164Sru  return len;
244121495Sjmacd}
244256164Sru
244321495Sjmacdvoid
244442664Smarkm#if defined (VA_FPRINTF) && __STDC__
2445116530Sruadd_word_args (const char *format, ...)
244642664Smarkm#else
244742664Smarkmadd_word_args (format, va_alist)
2448116530Sru    const char *format;
244942664Smarkm    va_dcl
245042664Smarkm#endif
245121495Sjmacd{
245256164Sru  char buffer[2000]; /* xx no fixed limits */
245342664Smarkm#ifdef VA_FPRINTF
245442664Smarkm  va_list ap;
245542664Smarkm#endif
245621495Sjmacd
245742664Smarkm  VA_START (ap, format);
245842664Smarkm#ifdef VA_SPRINTF
245942664Smarkm  VA_SPRINTF (buffer, format, ap);
246042664Smarkm#else
246156164Sru  sprintf (buffer, format, a1, a2, a3, a4, a5, a6, a7, a8);
246242664Smarkm#endif /* not VA_SPRINTF */
246342664Smarkm  va_end (ap);
246421495Sjmacd  add_word (buffer);
246521495Sjmacd}
246621495Sjmacd
246721495Sjmacd/* Add STRING to output_paragraph. */
246821495Sjmacdvoid
2469146520Sruadd_word (char *string)
247021495Sjmacd{
247121495Sjmacd  while (*string)
247221495Sjmacd    add_char (*string++);
247321495Sjmacd}
247421495Sjmacd
247593142Sru/* Like add_word, but inhibits conversion of whitespace into &nbsp;.
247693142Sru   Use this to output HTML directives with embedded blanks, to make
247793142Sru   them @w-safe.  */
247893142Sruvoid
2479146520Sruadd_html_elt (char *string)
248093142Sru{
248193142Sru  in_html_elt++;
248293142Sru  add_word (string);
248393142Sru  in_html_elt--;
248493142Sru}
248593142Sru
2486146520Sru/* These two functions below, add_html_block_elt and add_html_block_elt_args,
2487146520Sru   are mixtures of add_html_elt and add_word_args.  They inform makeinfo that
2488146520Sru   the current HTML element being inserted should not be enclosed in a <p>
2489146520Sru   element.  */
2490146520Sruvoid
2491146520Sruadd_html_block_elt (char *string)
2492146520Sru{
2493146520Sru  in_html_block_level_elt++;
2494146520Sru  add_word (string);
2495146520Sru  in_html_block_level_elt--;
2496146520Sru}
2497146520Sru
2498146520Sruvoid
2499146520Sru#if defined (VA_FPRINTF) && __STDC__
2500146520Sruadd_html_block_elt_args (const char *format, ...)
2501146520Sru#else
2502146520Sruadd_html_block_elt_args (format, va_alist)
2503146520Sru    const char *format;
2504146520Sru    va_dcl
2505146520Sru#endif
2506146520Sru{
2507146520Sru  char buffer[2000]; /* xx no fixed limits */
2508146520Sru#ifdef VA_FPRINTF
2509146520Sru  va_list ap;
2510146520Sru#endif
2511146520Sru
2512146520Sru  VA_START (ap, format);
2513146520Sru#ifdef VA_SPRINTF
2514146520Sru  VA_SPRINTF (buffer, format, ap);
2515146520Sru#else
2516146520Sru  sprintf (buffer, format, a1, a2, a3, a4, a5, a6, a7, a8);
2517146520Sru#endif /* not VA_SPRINTF */
2518146520Sru  va_end (ap);
2519146520Sru  add_html_block_elt (buffer);
2520146520Sru}
2521146520Sru
2522116530Sru/* Here is another awful kludge, used in add_char.  Ordinarily, macro
2523116530Sru   expansions take place in the body of the document, and therefore we
2524116530Sru   should html_output_head when we see one.  But there's an exception: a
2525116530Sru   macro call might take place within @copying, and that does not start
2526116530Sru   the real output, even though we fully expand the copying text.
2527116530Sru
2528116530Sru   So we need to be able to check if we are defining the @copying text.
2529116530Sru   We do this by looking back through the insertion stack.  */
2530116530Srustatic int
2531146520Srudefining_copying (void)
2532116530Sru{
2533116530Sru  INSERTION_ELT *i;
2534116530Sru  for (i = insertion_stack; i; i = i->next)
2535116530Sru    {
2536116530Sru      if (i->insertion == copying)
2537116530Sru        return 1;
2538116530Sru    }
2539116530Sru  return 0;
2540116530Sru}
2541116530Sru
2542116530Sru
254321495Sjmacd/* Add the character to the current paragraph.  If filling_enabled is
254442664Smarkm   nonzero, then do filling as well. */
254521495Sjmacdvoid
2546146520Sruadd_char (int character)
254721495Sjmacd{
254893142Sru  if (xml)
254993142Sru    {
255093142Sru      xml_add_char (character);
255193142Sru      return;
255293142Sru    }
255393142Sru
255421495Sjmacd  /* If we are avoiding outputting headers, and we are currently
255556164Sru     in a menu, then simply return.  But if we're only expanding macros,
255656164Sru     then we're being called from glean_node_from_menu to try to
255756164Sru     remember a menu reference, and we need that so we can do defaulting.  */
255856164Sru  if (no_headers && !only_macro_expansion && (in_menu || in_detailmenu))
255921495Sjmacd    return;
256021495Sjmacd
256121495Sjmacd  /* If we are adding a character now, then we don't have to
256221495Sjmacd     ignore close_paragraph () calls any more. */
256321495Sjmacd  if (must_start_paragraph && character != '\n')
256421495Sjmacd    {
256521495Sjmacd      must_start_paragraph = 0;
256642664Smarkm      line_already_broken = 0;  /* The line is no longer broken. */
256721495Sjmacd      if (current_indent > output_column)
256842664Smarkm        {
256942664Smarkm          indent (current_indent - output_column);
257042664Smarkm          output_column = current_indent;
257142664Smarkm        }
257221495Sjmacd    }
257321495Sjmacd
257493142Sru  if (non_splitting_words
257593142Sru      && !(html && in_html_elt)
257693142Sru      && strchr (" \t\n", character))
257756164Sru    {
257893142Sru      if (html || docbook)
257956164Sru        { /* Seems cleaner to use &nbsp; than an 8-bit char.  */
2580116530Sru          int saved_escape_html = escape_html;
2581116530Sru          escape_html = 0;
258256164Sru          add_word ("&nbsp");
2583116530Sru          escape_html = saved_escape_html;
258456164Sru          character = ';';
258556164Sru        }
258656164Sru      else
258756164Sru        character = META (' '); /* unmeta-d in flush_output */
258856164Sru    }
258921495Sjmacd
259021495Sjmacd  insertion_paragraph_closed = 0;
259121495Sjmacd
259221495Sjmacd  switch (character)
259321495Sjmacd    {
259421495Sjmacd    case '\n':
2595114477Sru      if (!filling_enabled && !(html && (in_menu || in_detailmenu)))
259642664Smarkm        {
259742664Smarkm          insert ('\n');
259821495Sjmacd
259942664Smarkm          if (force_flush_right)
260042664Smarkm            {
260142664Smarkm              close_paragraph ();
260242664Smarkm              /* Hack to force single blank lines out in this mode. */
260342664Smarkm              flush_output ();
260442664Smarkm            }
260521495Sjmacd
260642664Smarkm          output_column = 0;
260721495Sjmacd
260842664Smarkm          if (!no_indent && paragraph_is_open)
260942664Smarkm            indent (output_column = current_indent);
261042664Smarkm          break;
261142664Smarkm        }
261256164Sru      else if (end_of_sentence_p ())
261356164Sru        /* CHARACTER is newline, and filling is enabled. */
261442664Smarkm        {
261556164Sru          insert (' ');
261656164Sru          output_column++;
261756164Sru          last_inserted_character = character;
261842664Smarkm        }
261921495Sjmacd
262021495Sjmacd      if (last_char_was_newline)
262142664Smarkm        {
262256164Sru          if (html)
262356164Sru            last_char_was_newline++;
262442664Smarkm          close_paragraph ();
262542664Smarkm          pending_indent = 0;
262642664Smarkm        }
262721495Sjmacd      else
262842664Smarkm        {
262942664Smarkm          last_char_was_newline = 1;
263056164Sru          if (html)
263156164Sru            insert ('\n');
263256164Sru          else
263356164Sru            insert (' ');
263442664Smarkm          output_column++;
263542664Smarkm        }
263621495Sjmacd      break;
263721495Sjmacd
263856164Sru    default: /* not at newline */
263921495Sjmacd      {
264042664Smarkm        int len = get_char_len (character);
264142664Smarkm        int suppress_insert = 0;
264221495Sjmacd
264342664Smarkm        if ((character == ' ') && (last_char_was_newline))
264442664Smarkm          {
264542664Smarkm            if (!paragraph_is_open)
264642664Smarkm              {
264742664Smarkm                pending_indent++;
264842664Smarkm                return;
264942664Smarkm              }
265042664Smarkm          }
265121495Sjmacd
2652114477Sru        /* This is sad, but it seems desirable to not force any
2653114477Sru           particular order on the front matter commands.  This way,
2654114477Sru           the document can do @settitle, @documentlanguage, etc, in
2655114477Sru           any order and with any omissions, and we'll still output
2656114477Sru           the html <head> `just in time'.  */
2657114477Sru        if ((executing_macro || !executing_string)
2658146520Sru            && !only_macro_expansion
2659116530Sru            && html && !html_output_head_p && !defining_copying ())
2660114477Sru          html_output_head ();
266193142Sru
266242664Smarkm        if (!paragraph_is_open)
266342664Smarkm          {
266442664Smarkm            start_paragraph ();
266556164Sru            /* If the paragraph is supposed to be indented a certain
266656164Sru               way, then discard all of the pending whitespace.
266756164Sru               Otherwise, we let the whitespace stay. */
266842664Smarkm            if (!paragraph_start_indent)
266942664Smarkm              indent (pending_indent);
267042664Smarkm            pending_indent = 0;
267156164Sru
2672146520Sru            /* This check for in_html_block_level_elt prevents <p> from being
2673146520Sru               inserted when we already have html markup starting a paragraph,
2674146520Sru               as with <ul> and <h1> and the like.  */
2675146520Sru            if (html && !in_html_block_level_elt)
267656164Sru              {
2677146520Sru                if ((in_menu || in_detailmenu) && in_menu_item)
2678146520Sru                  {
2679146520Sru                    insert_string ("</li></ul>\n");
2680146520Sru                    in_menu_item = 0;
2681146520Sru                  }
268256164Sru                insert_string ("<p>");
268356164Sru                in_paragraph = 1;
268456164Sru                adjust_braces_following (0, 3); /* adjust for <p> */
268556164Sru              }
268642664Smarkm          }
268721495Sjmacd
268856164Sru        output_column += len;
268956164Sru        if (output_column > fill_column)
269042664Smarkm          {
269156164Sru            if (filling_enabled && !html)
269242664Smarkm              {
269342664Smarkm                int temp = output_paragraph_offset;
269442664Smarkm                while (--temp > 0 && output_paragraph[temp] != '\n')
269542664Smarkm                  {
269642664Smarkm                    /* If we have found a space, we have the place to break
269742664Smarkm                       the line. */
269842664Smarkm                    if (output_paragraph[temp] == ' ')
269942664Smarkm                      {
270042664Smarkm                        /* Remove trailing whitespace from output. */
270142664Smarkm                        while (temp && whitespace (output_paragraph[temp - 1]))
270242664Smarkm                          temp--;
270321495Sjmacd
270456164Sru                        /* If we went back all the way to the newline of the
270556164Sru                           preceding line, it probably means that the word we
270656164Sru                           are adding is itself wider than the space that the
270756164Sru                           indentation and the fill_column let us use.  In
270856164Sru                           that case, do NOT insert another newline, since it
270956164Sru                           won't help.  Just indent to current_indent and
271056164Sru                           leave it alone, since that's the most we can do.  */
271156164Sru                        if (temp && output_paragraph[temp - 1] != '\n')
271256164Sru                          output_paragraph[temp++] = '\n';
271321495Sjmacd
271442664Smarkm                        /* We have correctly broken the line where we want
271542664Smarkm                           to.  What we don't want is spaces following where
271642664Smarkm                           we have decided to break the line.  We get rid of
271742664Smarkm                           them. */
271842664Smarkm                        {
271942664Smarkm                          int t1 = temp;
272021495Sjmacd
272142664Smarkm                          for (;; t1++)
272242664Smarkm                            {
272342664Smarkm                              if (t1 == output_paragraph_offset)
272442664Smarkm                                {
272542664Smarkm                                  if (whitespace (character))
272642664Smarkm                                    suppress_insert = 1;
272742664Smarkm                                  break;
272842664Smarkm                                }
272942664Smarkm                              if (!whitespace (output_paragraph[t1]))
273042664Smarkm                                break;
273142664Smarkm                            }
273221495Sjmacd
273342664Smarkm                          if (t1 != temp)
273442664Smarkm                            {
273542664Smarkm                              adjust_braces_following (temp, (- (t1 - temp)));
2736146520Sru                              memmove (&output_paragraph[temp],
2737146520Sru                                       &output_paragraph[t1],
2738146520Sru                                       output_paragraph_offset - t1);
273942664Smarkm                              output_paragraph_offset -= (t1 - temp);
274042664Smarkm                            }
274142664Smarkm                        }
274221495Sjmacd
274342664Smarkm                        /* Filled, but now indent if that is right. */
274456164Sru                        if (indented_fill && current_indent > 0)
274542664Smarkm                          {
274642664Smarkm                            int buffer_len = ((output_paragraph_offset - temp)
274742664Smarkm                                              + current_indent);
274856164Sru                            char *temp_buffer = xmalloc (buffer_len);
274942664Smarkm                            int indentation = 0;
275021495Sjmacd
275142664Smarkm                            /* We have to shift any markers that are in
275242664Smarkm                               front of the wrap point. */
275342664Smarkm                            adjust_braces_following (temp, current_indent);
275421495Sjmacd
275542664Smarkm                            while (current_indent > 0 &&
275642664Smarkm                                   indentation != current_indent)
275742664Smarkm                              temp_buffer[indentation++] = ' ';
275821495Sjmacd
275956164Sru                            memcpy ((char *) &temp_buffer[current_indent],
276042664Smarkm                                     (char *) &output_paragraph[temp],
276142664Smarkm                                     buffer_len - current_indent);
276221495Sjmacd
276342664Smarkm                            if (output_paragraph_offset + buffer_len
276442664Smarkm                                >= paragraph_buffer_len)
276542664Smarkm                              {
276642664Smarkm                                unsigned char *tt = xrealloc
276742664Smarkm                                  (output_paragraph,
276842664Smarkm                                   (paragraph_buffer_len += buffer_len));
276942664Smarkm                                output_paragraph = tt;
277042664Smarkm                              }
277156164Sru                            memcpy ((char *) &output_paragraph[temp],
277242664Smarkm                                     temp_buffer, buffer_len);
277342664Smarkm                            output_paragraph_offset += current_indent;
277442664Smarkm                            free (temp_buffer);
277542664Smarkm                          }
277642664Smarkm                        output_column = 0;
277742664Smarkm                        while (temp < output_paragraph_offset)
277842664Smarkm                          output_column +=
277942664Smarkm                            get_char_len (output_paragraph[temp++]);
278042664Smarkm                        output_column += len;
278142664Smarkm                        break;
278242664Smarkm                      }
278342664Smarkm                  }
278442664Smarkm              }
278542664Smarkm          }
278621495Sjmacd
278742664Smarkm        if (!suppress_insert)
278842664Smarkm          {
278942664Smarkm            insert (character);
279042664Smarkm            last_inserted_character = character;
279142664Smarkm          }
279242664Smarkm        last_char_was_newline = 0;
279342664Smarkm        line_already_broken = 0;
279421495Sjmacd      }
279521495Sjmacd    }
279621495Sjmacd}
279721495Sjmacd
279856164Sru/* Add a character and store its position in meta_char_pos.  */
279956164Sruvoid
2800146520Sruadd_meta_char (int character)
280156164Sru{
280256164Sru  meta_char_pos = output_paragraph_offset;
280356164Sru  add_char (character);
280456164Sru}
280556164Sru
280621495Sjmacd/* Insert CHARACTER into `output_paragraph'. */
280721495Sjmacdvoid
2808146520Sruinsert (int character)
280921495Sjmacd{
2810146520Sru  /* We don't want to strip trailing whitespace in multitables.  Otherwise
2811146520Sru     horizontal separators confuse the font locking in Info mode in Emacs,
2812146520Sru     because it looks like a @subsection.  Adding a trailing space to those
2813146520Sru     lines fixes it.  */
2814146520Sru  if (character == '\n' && !html && !xml && !multitable_active)
2815146520Sru    {
2816146520Sru      while (output_paragraph_offset
2817146520Sru	     && whitespace (output_paragraph[output_paragraph_offset-1]))
2818146520Sru	output_paragraph_offset--;
2819146520Sru    }
2820146520Sru
282121495Sjmacd  output_paragraph[output_paragraph_offset++] = character;
282221495Sjmacd  if (output_paragraph_offset == paragraph_buffer_len)
282321495Sjmacd    {
282421495Sjmacd      output_paragraph =
282542664Smarkm        xrealloc (output_paragraph, (paragraph_buffer_len += 100));
282621495Sjmacd    }
282721495Sjmacd}
282821495Sjmacd
282921495Sjmacd/* Insert the null-terminated string STRING into `output_paragraph'.  */
283021495Sjmacdvoid
2831146520Sruinsert_string (const char *string)
283221495Sjmacd{
283321495Sjmacd  while (*string)
283421495Sjmacd    insert (*string++);
283521495Sjmacd}
283621495Sjmacd
283742664Smarkm
283842664Smarkm/* Sentences might have these characters after the period (or whatever).  */
283956164Sru#define POST_SENTENCE(c) ((c) == ')' || (c) == '\'' || (c) == '"' \
284042664Smarkm                          || (c) == ']')
284142664Smarkm
284242664Smarkm/* Return true if at an end-of-sentence character, possibly followed by
284342664Smarkm   post-sentence punctuation to ignore.  */
284442664Smarkmstatic int
2845146520Sruend_of_sentence_p (void)
284642664Smarkm{
284742664Smarkm  int loc = output_paragraph_offset - 1;
284856164Sru
284956164Sru  /* If nothing has been output, don't check output_paragraph[-1].  */
285056164Sru  if (loc < 0)
285156164Sru    return 0;
285256164Sru
285356164Sru  /* A post-sentence character that is at meta_char_pos is not really
285456164Sru     a post-sentence character; it was produced by a markup such as
285556164Sru     @samp.  We don't want the period inside @samp to be treated as a
285656164Sru     sentence ender. */
285756164Sru  while (loc > 0
285856164Sru         && loc != meta_char_pos && POST_SENTENCE (output_paragraph[loc]))
285942664Smarkm    loc--;
286056164Sru  return loc != meta_char_pos && sentence_ender (output_paragraph[loc]);
286142664Smarkm}
286242664Smarkm
286342664Smarkm
286421495Sjmacd/* Remove upto COUNT characters of whitespace from the
286521495Sjmacd   the current output line.  If COUNT is less than zero,
286621495Sjmacd   then remove until none left. */
286721495Sjmacdvoid
2868146520Srukill_self_indent (int count)
286921495Sjmacd{
287021495Sjmacd  /* Handle infinite case first. */
287121495Sjmacd  if (count < 0)
287221495Sjmacd    {
287321495Sjmacd      output_column = 0;
287421495Sjmacd      while (output_paragraph_offset)
287542664Smarkm        {
287642664Smarkm          if (whitespace (output_paragraph[output_paragraph_offset - 1]))
287742664Smarkm            output_paragraph_offset--;
287842664Smarkm          else
287942664Smarkm            break;
288042664Smarkm        }
288121495Sjmacd    }
288221495Sjmacd  else
288321495Sjmacd    {
288421495Sjmacd      while (output_paragraph_offset && count--)
288542664Smarkm        if (whitespace (output_paragraph[output_paragraph_offset - 1]))
288642664Smarkm          output_paragraph_offset--;
288742664Smarkm        else
288842664Smarkm          break;
288921495Sjmacd    }
289021495Sjmacd}
289121495Sjmacd
289242664Smarkm/* Nonzero means do not honor calls to flush_output (). */
289321495Sjmacdstatic int flushing_ignored = 0;
289421495Sjmacd
289521495Sjmacd/* Prevent calls to flush_output () from having any effect. */
289621495Sjmacdvoid
2897146520Sruinhibit_output_flushing (void)
289821495Sjmacd{
289921495Sjmacd  flushing_ignored++;
290021495Sjmacd}
290121495Sjmacd
290221495Sjmacd/* Allow calls to flush_output () to write the paragraph data. */
290321495Sjmacdvoid
2904146520Sruuninhibit_output_flushing (void)
290521495Sjmacd{
290621495Sjmacd  flushing_ignored--;
290721495Sjmacd}
290821495Sjmacd
290921495Sjmacdvoid
2910146520Sruflush_output (void)
291121495Sjmacd{
291256164Sru  int i;
291321495Sjmacd
291421495Sjmacd  if (!output_paragraph_offset || flushing_ignored)
291521495Sjmacd    return;
291621495Sjmacd
291721495Sjmacd  for (i = 0; i < output_paragraph_offset; i++)
291821495Sjmacd    {
2919146520Sru      if (output_paragraph[i] == '\n')
2920146520Sru        {
2921146520Sru          output_line_number++;
2922146520Sru          node_line_number++;
2923146520Sru        }
2924146520Sru
292556164Sru      /* If we turned on the 8th bit for a space inside @w, turn it
292656164Sru         back off for output.  This might be problematic, since the
292756164Sru         0x80 character may be used in 8-bit character sets.  Sigh.
292856164Sru         In any case, don't do this for HTML, since the nbsp character
292956164Sru         is valid input and must be passed along to the browser.  */
293056164Sru      if (!html && (output_paragraph[i] & meta_character_bit))
293121495Sjmacd        {
293221495Sjmacd          int temp = UNMETA (output_paragraph[i]);
293321495Sjmacd          if (temp == ' ')
293442664Smarkm            output_paragraph[i] &= 0x7f;
293521495Sjmacd        }
293621495Sjmacd    }
293721495Sjmacd
293821495Sjmacd  fwrite (output_paragraph, 1, output_paragraph_offset, output_stream);
293921495Sjmacd
294021495Sjmacd  output_position += output_paragraph_offset;
294121495Sjmacd  output_paragraph_offset = 0;
294256164Sru  meta_char_pos = 0;
294321495Sjmacd}
294421495Sjmacd
294521495Sjmacd/* How to close a paragraph controlling the number of lines between
294621495Sjmacd   this one and the last one. */
294721495Sjmacd
294821495Sjmacd/* Paragraph spacing is controlled by this variable.  It is the number of
294921495Sjmacd   blank lines that you wish to appear between paragraphs.  A value of
295021495Sjmacd   1 creates a single blank line between paragraphs. */
295121495Sjmacdint paragraph_spacing = DEFAULT_PARAGRAPH_SPACING;
295221495Sjmacd
295356164Srustatic void
2954146520Sruclose_paragraph_with_lines (int lines)
295556164Sru{
295656164Sru  int old_spacing = paragraph_spacing;
295756164Sru  paragraph_spacing = lines;
295856164Sru  close_paragraph ();
295956164Sru  paragraph_spacing = old_spacing;
296056164Sru}
296156164Sru
296221495Sjmacd/* Close the current paragraph, leaving no blank lines between them. */
296321495Sjmacdvoid
2964146520Sruclose_single_paragraph (void)
296521495Sjmacd{
296621495Sjmacd  close_paragraph_with_lines (0);
296721495Sjmacd}
296821495Sjmacd
296921495Sjmacd/* Close a paragraph after an insertion has ended. */
297021495Sjmacdvoid
2971146520Sruclose_insertion_paragraph (void)
297221495Sjmacd{
297321495Sjmacd  if (!insertion_paragraph_closed)
297421495Sjmacd    {
297521495Sjmacd      /* Close the current paragraph, breaking the line. */
297621495Sjmacd      close_single_paragraph ();
297721495Sjmacd
297842664Smarkm      /* Start a new paragraph, with the correct indentation for the now
297942664Smarkm         current insertion level (one above the one that we are ending). */
298021495Sjmacd      start_paragraph ();
298121495Sjmacd
298242664Smarkm      /* Tell `close_paragraph' that the previous line has already been
298342664Smarkm         broken, so it should insert one less newline. */
298421495Sjmacd      line_already_broken = 1;
298521495Sjmacd
298642664Smarkm      /* Tell functions such as `add_char' we've already found a newline. */
298721495Sjmacd      ignore_blank_line ();
298821495Sjmacd    }
298921495Sjmacd  else
299021495Sjmacd    {
299121495Sjmacd      /* If the insertion paragraph is closed already, then we are seeing
299242664Smarkm         two `@end' commands in a row.  Note that the first one we saw was
299342664Smarkm         handled in the first part of this if-then-else clause, and at that
299442664Smarkm         time `start_paragraph' was called, partially to handle the proper
299542664Smarkm         indentation of the current line.  However, the indentation level
299642664Smarkm         may have just changed again, so we may have to outdent the current
299742664Smarkm         line to the new indentation level. */
299821495Sjmacd      if (current_indent < output_column)
299942664Smarkm        kill_self_indent (output_column - current_indent);
300021495Sjmacd    }
300121495Sjmacd
300221495Sjmacd  insertion_paragraph_closed = 1;
300321495Sjmacd}
300421495Sjmacd
300521495Sjmacd/* Close the currently open paragraph. */
300621495Sjmacdvoid
3007146520Sruclose_paragraph (void)
300821495Sjmacd{
300956164Sru  int i;
301021495Sjmacd
3011146520Sru  /* We don't need these newlines in XML and Docbook outputs for
3012146520Sru     paragraph seperation.  We have <para> element for that.  */
3013146520Sru  if (xml)
3014146520Sru    return;
3015146520Sru
301621495Sjmacd  /* The insertion paragraph is no longer closed. */
301721495Sjmacd  insertion_paragraph_closed = 0;
301821495Sjmacd
301921495Sjmacd  if (paragraph_is_open && !must_start_paragraph)
302021495Sjmacd    {
3021146520Sru      int tindex = output_paragraph_offset;
302221495Sjmacd
302321495Sjmacd      /* Back up to last non-newline/space character, forcing all such
302442664Smarkm         subsequent characters to be newlines.  This isn't strictly
302542664Smarkm         necessary, but a couple of functions use the presence of a newline
302642664Smarkm         to make decisions. */
302721495Sjmacd      for (tindex = output_paragraph_offset - 1; tindex >= 0; --tindex)
302842664Smarkm        {
3029146520Sru          int c = output_paragraph[tindex];
303021495Sjmacd
303142664Smarkm          if (c == ' '|| c == '\n')
303242664Smarkm            output_paragraph[tindex] = '\n';
303342664Smarkm          else
303442664Smarkm            break;
303542664Smarkm        }
303621495Sjmacd
303721495Sjmacd      /* All trailing whitespace is ignored. */
303821495Sjmacd      output_paragraph_offset = ++tindex;
303921495Sjmacd
304021495Sjmacd      /* Break the line if that is appropriate. */
304121495Sjmacd      if (paragraph_spacing >= 0)
304242664Smarkm        insert ('\n');
304321495Sjmacd
304442664Smarkm      /* Add as many blank lines as is specified in `paragraph_spacing'. */
304521495Sjmacd      if (!force_flush_right)
304642664Smarkm        {
304742664Smarkm          for (i = 0; i < (paragraph_spacing - line_already_broken); i++)
304856164Sru            {
304956164Sru              insert ('\n');
305056164Sru              /* Don't need anything extra for HTML in usual case of no
305156164Sru                 extra paragraph spacing.  */
305256164Sru              if (html && i > 0)
305356164Sru                insert_string ("<br>");
305456164Sru            }
305542664Smarkm        }
305621495Sjmacd
305721495Sjmacd      /* If we are doing flush right indentation, then do it now
305842664Smarkm         on the paragraph (really a single line). */
305921495Sjmacd      if (force_flush_right)
306042664Smarkm        do_flush_right_indentation ();
306121495Sjmacd
306221495Sjmacd      flush_output ();
306321495Sjmacd      paragraph_is_open = 0;
306421495Sjmacd      no_indent = 0;
306521495Sjmacd      output_column = 0;
306621495Sjmacd    }
306756164Sru
306821495Sjmacd  ignore_blank_line ();
306921495Sjmacd}
307021495Sjmacd
307121495Sjmacd/* Make the last line just read look as if it were only a newline. */
307221495Sjmacdvoid
3073146520Sruignore_blank_line (void)
307421495Sjmacd{
307521495Sjmacd  last_inserted_character = '\n';
307621495Sjmacd  last_char_was_newline = 1;
307721495Sjmacd}
307821495Sjmacd
307921495Sjmacd/* Align the end of the text in output_paragraph with fill_column. */
3080146520Srustatic void
3081146520Srudo_flush_right_indentation (void)
308221495Sjmacd{
308321495Sjmacd  char *temp;
308421495Sjmacd  int temp_len;
308521495Sjmacd
308621495Sjmacd  kill_self_indent (-1);
308721495Sjmacd
308821495Sjmacd  if (output_paragraph[0] != '\n')
308921495Sjmacd    {
309042664Smarkm      output_paragraph[output_paragraph_offset] = 0;
309121495Sjmacd
309221495Sjmacd      if (output_paragraph_offset < fill_column)
309342664Smarkm        {
309456164Sru          int i;
309521495Sjmacd
309642664Smarkm          if (fill_column >= paragraph_buffer_len)
309742664Smarkm            output_paragraph =
309842664Smarkm              xrealloc (output_paragraph,
309942664Smarkm                        (paragraph_buffer_len += fill_column));
310021495Sjmacd
310142664Smarkm          temp_len = strlen ((char *)output_paragraph);
310256164Sru          temp = xmalloc (temp_len + 1);
310342664Smarkm          memcpy (temp, (char *)output_paragraph, temp_len);
310421495Sjmacd
310542664Smarkm          for (i = 0; i < fill_column - output_paragraph_offset; i++)
310642664Smarkm            output_paragraph[i] = ' ';
310721495Sjmacd
310842664Smarkm          memcpy ((char *)output_paragraph + i, temp, temp_len);
310942664Smarkm          free (temp);
311042664Smarkm          output_paragraph_offset = fill_column;
311156164Sru          adjust_braces_following (0, i);
311242664Smarkm        }
311321495Sjmacd    }
311421495Sjmacd}
311521495Sjmacd
311621495Sjmacd/* Begin a new paragraph. */
311721495Sjmacdvoid
3118146520Srustart_paragraph (void)
311921495Sjmacd{
312021495Sjmacd  /* First close existing one. */
312121495Sjmacd  if (paragraph_is_open)
312221495Sjmacd    close_paragraph ();
312321495Sjmacd
312421495Sjmacd  /* In either case, the insertion paragraph is no longer closed. */
312521495Sjmacd  insertion_paragraph_closed = 0;
312621495Sjmacd
312721495Sjmacd  /* However, the paragraph is open! */
312821495Sjmacd  paragraph_is_open = 1;
312921495Sjmacd
313021495Sjmacd  /* If we MUST_START_PARAGRAPH, that simply means that start_paragraph ()
313121495Sjmacd     had to be called before we would allow any other paragraph operations
313221495Sjmacd     to have an effect. */
313321495Sjmacd  if (!must_start_paragraph)
313421495Sjmacd    {
313521495Sjmacd      int amount_to_indent = 0;
313621495Sjmacd
313721495Sjmacd      /* If doing indentation, then insert the appropriate amount. */
313821495Sjmacd      if (!no_indent)
313942664Smarkm        {
314042664Smarkm          if (inhibit_paragraph_indentation)
314142664Smarkm            {
314242664Smarkm              amount_to_indent = current_indent;
314342664Smarkm              if (inhibit_paragraph_indentation < 0)
314442664Smarkm                inhibit_paragraph_indentation++;
314542664Smarkm            }
314642664Smarkm          else if (paragraph_start_indent < 0)
314742664Smarkm            amount_to_indent = current_indent;
314842664Smarkm          else
314942664Smarkm            amount_to_indent = current_indent + paragraph_start_indent;
315021495Sjmacd
315142664Smarkm          if (amount_to_indent >= output_column)
315242664Smarkm            {
315342664Smarkm              amount_to_indent -= output_column;
315442664Smarkm              indent (amount_to_indent);
315542664Smarkm              output_column += amount_to_indent;
315642664Smarkm            }
315742664Smarkm        }
315821495Sjmacd    }
315921495Sjmacd  else
316021495Sjmacd    must_start_paragraph = 0;
316121495Sjmacd}
316221495Sjmacd
316321495Sjmacd/* Insert the indentation specified by AMOUNT. */
316421495Sjmacdvoid
3165146520Sruindent (int amount)
316621495Sjmacd{
316721495Sjmacd  /* For every START_POS saved within the brace stack which will be affected
316821495Sjmacd     by this indentation, bump that start pos forward. */
316956164Sru  adjust_braces_following (output_paragraph_offset, amount);
317021495Sjmacd
317121495Sjmacd  while (--amount >= 0)
317221495Sjmacd    insert (' ');
317321495Sjmacd}
317421495Sjmacd
317521495Sjmacd/* Search forward for STRING in input_text.
317621495Sjmacd   FROM says where where to start. */
317721495Sjmacdint
3178146520Srusearch_forward (char *string, int from)
317921495Sjmacd{
318021495Sjmacd  int len = strlen (string);
318121495Sjmacd
318256164Sru  while (from < input_text_length)
318321495Sjmacd    {
318421495Sjmacd      if (strncmp (input_text + from, string, len) == 0)
318556164Sru        return from;
318621495Sjmacd      from++;
318721495Sjmacd    }
318856164Sru  return -1;
318921495Sjmacd}
319021495Sjmacd
3191146520Sru/* search_forward until n characters.  */
3192146520Sruint
3193146520Srusearch_forward_until_pos (char *string, int from, int end_pos)
319421495Sjmacd{
3195146520Sru  int save_input_text_length = input_text_length;
3196146520Sru  input_text_length = end_pos;
319721495Sjmacd
3198146520Sru  from = search_forward (string, from);
3199114477Sru
3200146520Sru  input_text_length = save_input_text_length;
320121495Sjmacd
3202146520Sru  return from;
320321495Sjmacd}
320421495Sjmacd
3205146520Sru/* Return next non-whitespace and non-cr character.  */
3206146520Sruint
3207146520Srunext_nonwhitespace_character (void)
320821495Sjmacd{
3209146520Sru  /* First check the current input_text.  Start from the next char because
3210146520Sru     we already have input_text[input_text_offset] in ``current''.  */
3211146520Sru  int pos = input_text_offset + 1;
321221495Sjmacd
3213146520Sru  while (pos < input_text_length)
321421495Sjmacd    {
3215146520Sru      if (!cr_or_whitespace(input_text[pos]))
3216146520Sru        return input_text[pos];
3217146520Sru      pos++;
321821495Sjmacd    }
321921495Sjmacd
3220146520Sru  { /* Can't find a valid character, so go through filestack
3221146520Sru       in case we are doing @include or expanding a macro.  */
3222146520Sru    FSTACK *tos = filestack;
322356164Sru
3224146520Sru    while (tos)
3225146520Sru      {
3226146520Sru        int tmp_input_text_length = filestack->size;
3227146520Sru        int tmp_input_text_offset = filestack->offset;
3228146520Sru        char *tmp_input_text = filestack->text;
322921495Sjmacd
3230146520Sru        while (tmp_input_text_offset < tmp_input_text_length)
3231146520Sru          {
3232146520Sru            if (!cr_or_whitespace(tmp_input_text[tmp_input_text_offset]))
3233146520Sru              return tmp_input_text[tmp_input_text_offset];
3234146520Sru            tmp_input_text_offset++;
3235146520Sru          }
323693142Sru
3237146520Sru        tos = tos->next;
3238146520Sru      }
3239146520Sru  }
324093142Sru
3241146520Sru  return -1;
324221495Sjmacd}
3243146520Sru
324442664Smarkm/* An external image is a reference, kind of.  The parsing is (not
324542664Smarkm   coincidentally) similar, anyway.  */
324642664Smarkmvoid
3247146520Srucm_image (int arg)
324842664Smarkm{
3249114477Sru  char *name_arg, *w_arg, *h_arg, *alt_arg, *ext_arg;
325056164Sru
325156164Sru  if (arg == END)
325256164Sru    return;
325356164Sru
325456164Sru  name_arg = get_xref_token (1); /* expands all macros in image */
3255114477Sru  w_arg = get_xref_token (0);
3256114477Sru  h_arg = get_xref_token (0);
325793142Sru  alt_arg = get_xref_token (1); /* expands all macros in alt text */
325893142Sru  ext_arg = get_xref_token (0);
325956164Sru
326056164Sru  if (*name_arg)
326142664Smarkm    {
3262116530Sru      struct stat file_info;
3263116530Sru      char *pathname = NULL;
326493142Sru      char *fullname = xmalloc (strlen (name_arg)
3265146520Sru                       + (ext_arg && *ext_arg ? strlen (ext_arg) + 1: 4) + 1);
326656164Sru
3267116530Sru      if (ext_arg && *ext_arg)
326893142Sru        {
3269146520Sru          sprintf (fullname, "%s%s", name_arg, ext_arg);
3270116530Sru          if (access (fullname, R_OK) != 0)
3271116530Sru            pathname = get_file_info_in_path (fullname, include_files_path,
3272116530Sru                                              &file_info);
3273146520Sru
3274146520Sru	  if (pathname == NULL)
3275146520Sru	    {
3276146520Sru	      /* Backwards compatibility (4.6 <= version < 4.7):
3277146520Sru		 try prefixing @image's EXTENSION parameter with a period. */
3278146520Sru	      sprintf (fullname, "%s.%s", name_arg, ext_arg);
3279146520Sru	      if (access (fullname, R_OK) != 0)
3280146520Sru		pathname = get_file_info_in_path (fullname, include_files_path,
3281146520Sru						  &file_info);
3282146520Sru	    }
3283116530Sru        }
3284116530Sru      else
3285116530Sru        {
3286116530Sru          sprintf (fullname, "%s.png", name_arg);
3287146520Sru          if (access (fullname, R_OK) != 0) {
3288146520Sru            pathname = get_file_info_in_path (fullname,
3289146520Sru                                              include_files_path, &file_info);
3290146520Sru            if (pathname == NULL) {
3291146520Sru              sprintf (fullname, "%s.jpg", name_arg);
3292146520Sru              if (access (fullname, R_OK) != 0) {
3293146520Sru                sprintf (fullname, "%s.gif", name_arg);
3294146520Sru                if (access (fullname, R_OK) != 0) {
3295146520Sru                  pathname = get_file_info_in_path (fullname,
3296116530Sru                                               include_files_path, &file_info);
3297114477Sru                }
3298146520Sru              }
3299114477Sru            }
3300146520Sru          }
3301116530Sru        }
3302116530Sru
3303116530Sru      if (html)
3304116530Sru        {
3305146520Sru          int image_in_div = 0;
3306146520Sru
3307116530Sru          if (pathname == NULL && access (fullname, R_OK) != 0)
3308114477Sru            {
3309116530Sru              line_error(_("@image file `%s' (for HTML) not readable: %s"),
3310116530Sru                             fullname, strerror (errno));
3311116530Sru              return;
3312116530Sru            }
3313116530Sru          if (pathname != NULL && access (pathname, R_OK) != 0)
331456164Sru            {
3315116530Sru              line_error (_("No such file `%s'"),
3316116530Sru                          fullname);
3317116530Sru              return;
3318114477Sru            }
331956164Sru
3320146520Sru          if (!paragraph_is_open)
3321146520Sru            {
3322146520Sru              add_html_block_elt ("<div class=\"block-image\">");
3323146520Sru              image_in_div = 1;
3324146520Sru            }
3325146520Sru
3326114477Sru          add_html_elt ("<img src=");
332793142Sru          add_word_args ("\"%s\"", fullname);
3328114477Sru          add_html_elt (" alt=");
3329146520Sru          add_word_args ("\"%s\">",
3330146520Sru              escape_string (*alt_arg ? text_expansion (alt_arg) : fullname));
3331146520Sru
3332146520Sru          if (image_in_div)
3333146520Sru            add_html_block_elt ("</div>");
333456164Sru        }
333593142Sru      else if (xml && docbook)
3336114477Sru        xml_insert_docbook_image (name_arg);
333793142Sru      else if (xml)
3338114477Sru        {
3339146520Sru          extern int xml_in_para;
3340146520Sru          extern int xml_no_para;
3341146520Sru          int elt = xml_in_para ? INLINEIMAGE : IMAGE;
3342146520Sru
3343146520Sru          if (!xml_in_para)
3344146520Sru            xml_no_para++;
3345146520Sru
3346146520Sru          xml_insert_element_with_attribute (elt,
3347146520Sru              START, "width=\"%s\" height=\"%s\" name=\"%s\" extension=\"%s\"",
3348146520Sru              w_arg, h_arg, name_arg, ext_arg);
3349146520Sru          xml_insert_element (IMAGEALTTEXT, START);
3350146520Sru          execute_string ("%s", alt_arg);
3351146520Sru          xml_insert_element (IMAGEALTTEXT, END);
3352146520Sru          xml_insert_element (elt, END);
3353146520Sru
3354146520Sru          if (!xml_in_para)
3355146520Sru            xml_no_para--;
3356114477Sru        }
335756164Sru      else
3358116530Sru        { /* Try to open foo.EXT or foo.txt.  */
335942664Smarkm          FILE *image_file;
3360116530Sru          char *txtpath = NULL;
3361116530Sru          char *txtname = xmalloc (strlen (name_arg)
3362116530Sru                                   + (ext_arg && *ext_arg
3363146520Sru                                      ? strlen (ext_arg) : 4) + 1);
3364116530Sru          strcpy (txtname, name_arg);
3365116530Sru          strcat (txtname, ".txt");
3366116530Sru          image_file = fopen (txtname, "r");
3367116530Sru          if (image_file == NULL)
336842664Smarkm            {
3369116530Sru              txtpath = get_file_info_in_path (txtname,
3370116530Sru                                               include_files_path, &file_info);
3371116530Sru              if (txtpath != NULL)
3372116530Sru                image_file = fopen (txtpath, "r");
3373116530Sru            }
3374116530Sru
3375116530Sru          if (image_file != NULL
3376116530Sru              || access (fullname, R_OK) == 0
3377116530Sru              || (pathname != NULL && access (pathname, R_OK) == 0))
3378116530Sru            {
337942664Smarkm              int ch;
338042664Smarkm              int save_inhibit_indentation = inhibit_paragraph_indentation;
338142664Smarkm              int save_filling_enabled = filling_enabled;
3382146520Sru              int image_in_brackets = paragraph_is_open;
338356164Sru
3384146520Sru              /* Write magic ^@^H[image ...^@^H] cookie in the info file, if
3385146520Sru                 there's an accompanying bitmap.  Otherwise just include the
3386146520Sru                 text image.  In the plaintext output, always include the text
3387146520Sru                 image without the magic cookie.  */
3388146520Sru              int use_magic_cookie = !no_headers
3389146520Sru                && access (fullname, R_OK) == 0 && !STREQ (fullname, txtname);
3390146520Sru
339142664Smarkm              inhibit_paragraph_indentation = 1;
339242664Smarkm              filling_enabled = 0;
339342664Smarkm              last_char_was_newline = 0;
339456164Sru
3395146520Sru              if (use_magic_cookie)
3396146520Sru                {
3397146520Sru                  add_char ('\0');
3398146520Sru                  add_word ("\010[image");
339956164Sru
3400146520Sru                  if (access (fullname, R_OK) == 0
3401146520Sru                      || (pathname != NULL && access (pathname, R_OK) == 0))
3402146520Sru                    add_word_args (" src=\"%s\"", fullname);
3403116530Sru
3404146520Sru                  if (*alt_arg)
3405146520Sru                    add_word_args (" alt=\"%s\"", alt_arg);
3406146520Sru                }
3407116530Sru
3408116530Sru              if (image_file != NULL)
3409116530Sru                {
3410146520Sru                  if (use_magic_cookie)
3411146520Sru                    add_word (" text=\"");
3412146520Sru
3413146520Sru                  if (image_in_brackets)
3414146520Sru                    add_char ('[');
3415146520Sru
3416116530Sru                  /* Maybe we need to remove the final newline if the image
3417116530Sru                     file is only one line to allow in-line images.  On the
3418116530Sru                     other hand, they could just make the file without a
3419116530Sru                     final newline.  */
3420116530Sru                  while ((ch = getc (image_file)) != EOF)
3421116530Sru                    {
3422146520Sru                      if (use_magic_cookie && (ch == '"' || ch == '\\'))
3423116530Sru                        add_char ('\\');
3424116530Sru                      add_char (ch);
3425116530Sru                    }
3426116530Sru
3427146520Sru                  if (image_in_brackets)
3428146520Sru                    add_char (']');
3429146520Sru
3430146520Sru                  if (use_magic_cookie)
3431146520Sru                    add_char ('"');
3432146520Sru
3433116530Sru                  if (fclose (image_file) != 0)
3434116530Sru                    perror (txtname);
3435116530Sru                }
3436116530Sru
3437146520Sru              if (use_magic_cookie)
3438146520Sru                {
3439146520Sru                  add_char ('\0');
3440146520Sru                  add_word ("\010]");
3441146520Sru                }
3442146520Sru
344342664Smarkm              inhibit_paragraph_indentation = save_inhibit_indentation;
344442664Smarkm              filling_enabled = save_filling_enabled;
344542664Smarkm            }
344642664Smarkm          else
3447146520Sru            warning (_("@image file `%s' (for text) unreadable: %s"),
3448116530Sru                        txtname, strerror (errno));
344942664Smarkm        }
345042664Smarkm
345156164Sru      free (fullname);
3452116530Sru      if (pathname)
3453116530Sru        free (pathname);
345442664Smarkm    }
345521495Sjmacd  else
345656164Sru    line_error (_("@image missing filename argument"));
345721495Sjmacd
345856164Sru  if (name_arg)
345956164Sru    free (name_arg);
3460114477Sru  if (w_arg)
3461114477Sru    free (w_arg);
3462114477Sru  if (h_arg)
3463114477Sru    free (h_arg);
346493142Sru  if (alt_arg)
346593142Sru    free (alt_arg);
346693142Sru  if (ext_arg)
346793142Sru    free (ext_arg);
346821495Sjmacd}
346921495Sjmacd
347056164Sru/* Conditionals.  */
347121495Sjmacd
347221495Sjmacd/* A structure which contains `defined' variables. */
347321495Sjmacdtypedef struct defines {
347421495Sjmacd  struct defines *next;
347521495Sjmacd  char *name;
347621495Sjmacd  char *value;
347721495Sjmacd} DEFINE;
347821495Sjmacd
347921495Sjmacd/* The linked list of `set' defines. */
348056164SruDEFINE *defines = NULL;
348121495Sjmacd
348221495Sjmacd/* Add NAME to the list of `set' defines. */
3483146520Srustatic void
3484146520Sruset (char *name, char *value)
348521495Sjmacd{
348621495Sjmacd  DEFINE *temp;
348721495Sjmacd
348821495Sjmacd  for (temp = defines; temp; temp = temp->next)
348921495Sjmacd    if (strcmp (name, temp->name) == 0)
349021495Sjmacd      {
349142664Smarkm        free (temp->value);
349242664Smarkm        temp->value = xstrdup (value);
349342664Smarkm        return;
349421495Sjmacd      }
349521495Sjmacd
349656164Sru  temp = xmalloc (sizeof (DEFINE));
349721495Sjmacd  temp->next = defines;
349842664Smarkm  temp->name = xstrdup (name);
349942664Smarkm  temp->value = xstrdup (value);
350021495Sjmacd  defines = temp;
3501146520Sru
3502146520Sru  if (xml && !docbook)
3503146520Sru    {
3504146520Sru      xml_insert_element_with_attribute (SETVALUE, START, "name=\"%s\"", name);
3505146520Sru      execute_string ("%s", value);
3506146520Sru      xml_insert_element (SETVALUE, END);
3507146520Sru    }
350821495Sjmacd}
350921495Sjmacd
351021495Sjmacd/* Remove NAME from the list of `set' defines. */
3511146520Srustatic void
3512146520Sruclear (char *name)
351321495Sjmacd{
351456164Sru  DEFINE *temp, *last;
351521495Sjmacd
351656164Sru  last = NULL;
351721495Sjmacd  temp = defines;
351821495Sjmacd
351921495Sjmacd  while (temp)
352021495Sjmacd    {
352121495Sjmacd      if (strcmp (temp->name, name) == 0)
352242664Smarkm        {
352342664Smarkm          if (last)
352442664Smarkm            last->next = temp->next;
352542664Smarkm          else
352642664Smarkm            defines = temp->next;
352721495Sjmacd
352842664Smarkm          free (temp->name);
352942664Smarkm          free (temp->value);
353042664Smarkm          free (temp);
353142664Smarkm          break;
353242664Smarkm        }
353321495Sjmacd      last = temp;
353421495Sjmacd      temp = temp->next;
353521495Sjmacd    }
3536146520Sru
3537146520Sru  if (xml && !docbook)
3538146520Sru    {
3539146520Sru      xml_insert_element_with_attribute (CLEARVALUE, START, "name=\"%s\"", name);
3540146520Sru      xml_insert_element (CLEARVALUE, END);
3541146520Sru    }
354221495Sjmacd}
354321495Sjmacd
354421495Sjmacd/* Return the value of NAME.  The return value is NULL if NAME is unset. */
3545146520Srustatic char *
3546146520Sruset_p (char *name)
354721495Sjmacd{
354856164Sru  DEFINE *temp;
354921495Sjmacd
355021495Sjmacd  for (temp = defines; temp; temp = temp->next)
355121495Sjmacd    if (strcmp (temp->name, name) == 0)
355256164Sru      return temp->value;
355321495Sjmacd
355456164Sru  return NULL;
355521495Sjmacd}
355621495Sjmacd
355721495Sjmacd/* Create a variable whose name appears as the first word on this line. */
355821495Sjmacdvoid
3559146520Srucm_set (void)
356021495Sjmacd{
356121495Sjmacd  handle_variable (SET);
356221495Sjmacd}
356321495Sjmacd
356421495Sjmacd/* Remove a variable whose name appears as the first word on this line. */
356521495Sjmacdvoid
3566146520Srucm_clear (void)
356721495Sjmacd{
356821495Sjmacd  handle_variable (CLEAR);
356921495Sjmacd}
357021495Sjmacd
357121495Sjmacdvoid
3572146520Srucm_ifset (void)
357321495Sjmacd{
357421495Sjmacd  handle_variable (IFSET);
357521495Sjmacd}
357621495Sjmacd
357721495Sjmacdvoid
3578146520Srucm_ifclear (void)
357921495Sjmacd{
358021495Sjmacd  handle_variable (IFCLEAR);
358121495Sjmacd}
358221495Sjmacd
358321495Sjmacd/* This command takes braces, but we parse the contents specially, so we
358421495Sjmacd   don't use the standard brace popping code.
358521495Sjmacd
358621495Sjmacd   The syntax @ifeq{arg1, arg2, texinfo-commands} performs texinfo-commands
358721495Sjmacd   if ARG1 and ARG2 caselessly string compare to the same string, otherwise,
358821495Sjmacd   it produces no output. */
358921495Sjmacdvoid
3590146520Srucm_ifeq (void)
359121495Sjmacd{
359221495Sjmacd  char **arglist;
359321495Sjmacd
359421495Sjmacd  arglist = get_brace_args (0);
359521495Sjmacd
359621495Sjmacd  if (arglist)
359721495Sjmacd    {
359821495Sjmacd      if (array_len (arglist) > 1)
359942664Smarkm        {
360042664Smarkm          if ((strcasecmp (arglist[0], arglist[1]) == 0) &&
360156164Sru              (arglist[2]))
360242664Smarkm            execute_string ("%s\n", arglist[2]);
360342664Smarkm        }
360421495Sjmacd
360521495Sjmacd      free_array (arglist);
360621495Sjmacd    }
360721495Sjmacd}
360821495Sjmacd
360921495Sjmacdvoid
3610146520Srucm_value (int arg, int start_pos, int end_pos)
361121495Sjmacd{
361256164Sru  static int value_level = 0, saved_meta_pos = -1;
361356164Sru
3614146520Sru  /* xml_add_char() skips any content inside menus when output format is
3615146520Sru     Docbook, so @value{} is no use there.  Also start_pos and end_pos does not
3616146520Sru     get updated, causing name to be empty string.  So just return.  */
3617146520Sru   if (docbook && in_menu)
3618146520Sru     return;
3619146520Sru
362056164Sru  /* All the text after @value{ upto the matching } will eventually
362156164Sru     disappear from output_paragraph, when this function is called
362256164Sru     with ARG == END.  If the text produced until then sets
362356164Sru     meta_char_pos, we will need to restore it to the value it had
362456164Sru     before @value was seen.  So we need to save the previous value
362556164Sru     of meta_char_pos here.  */
362656164Sru  if (arg == START)
362721495Sjmacd    {
362856164Sru      /* If we are already inside some outer @value, don't overwrite
362956164Sru         the value saved in saved_meta_pos.  */
363056164Sru      if (!value_level)
363156164Sru        saved_meta_pos = meta_char_pos;
363256164Sru      value_level++;
363356164Sru      /* While the argument of @value is processed, we need to inhibit
3634114477Sru         textual transformations like "--" into "-", since @set didn't
3635114477Sru         do that when it grabbed the name of the variable.  */
363656164Sru      in_fixed_width_font++;
363756164Sru    }
363856164Sru  else
363956164Sru    {
364042664Smarkm      char *name = (char *) &output_paragraph[start_pos];
364142664Smarkm      char *value;
364242664Smarkm      output_paragraph[end_pos] = 0;
364342664Smarkm      name = xstrdup (name);
364421495Sjmacd      value = set_p (name);
364521495Sjmacd      output_column -= end_pos - start_pos;
364621495Sjmacd      output_paragraph_offset = start_pos;
364721495Sjmacd
364856164Sru      /* Restore the previous value of meta_char_pos if the stuff
364956164Sru         inside this @value{} moved it.  */
365056164Sru      if (saved_meta_pos == -1) /* can't happen inside @value{} */
365156164Sru        abort ();
365256164Sru      if (value_level == 1
365356164Sru          && meta_char_pos >= start_pos && meta_char_pos < end_pos)
365456164Sru        {
365556164Sru          meta_char_pos = saved_meta_pos;
365656164Sru          saved_meta_pos = -1;
365756164Sru        }
365856164Sru      value_level--;
365956164Sru      /* No need to decrement in_fixed_width_font, since before
3660114477Sru         we are called with arg == END, the reader loop already
3661114477Sru         popped the brace stack, which restored in_fixed_width_font,
3662114477Sru         among other things.  */
366356164Sru
366421495Sjmacd      if (value)
3665146520Sru	{
3666146520Sru	  /* We need to get past the closing brace since the value may
3667146520Sru	     expand to a context-sensitive macro (e.g. @xref) and produce
3668146520Sru	     spurious warnings */
3669146520Sru	  input_text_offset++;
3670146520Sru	  execute_string ("%s", value);
3671146520Sru	  input_text_offset--;
3672146520Sru	}
367321495Sjmacd      else
3674116530Sru	{
3675146520Sru          warning (_("undefined flag: %s"), name);
3676146520Sru          add_word_args (_("{No value for `%s'}"), name);
3677116530Sru	}
367821495Sjmacd
367921495Sjmacd      free (name);
368021495Sjmacd    }
368121495Sjmacd}
368221495Sjmacd
368321495Sjmacd/* Set, clear, or conditionalize based on ACTION. */
3684146520Srustatic void
3685146520Sruhandle_variable (int action)
368621495Sjmacd{
368721495Sjmacd  char *name;
368821495Sjmacd
368956164Sru  get_rest_of_line (0, &name);
369056164Sru  /* If we hit the end of text in get_rest_of_line, backing up
369156164Sru     input pointer will cause the last character of the last line
369256164Sru     be pushed back onto the input, which is wrong.  */
369356164Sru  if (input_text_offset < input_text_length)
369456164Sru    backup_input_pointer ();
369521495Sjmacd  handle_variable_internal (action, name);
369621495Sjmacd  free (name);
369721495Sjmacd}
369821495Sjmacd
3699146520Srustatic void
3700146520Sruhandle_variable_internal (int action, char *name)
370121495Sjmacd{
370221495Sjmacd  char *temp;
370321495Sjmacd  int delimiter, additional_text_present = 0;
370421495Sjmacd
370521495Sjmacd  /* Only the first word of NAME is a valid tag. */
370621495Sjmacd  temp = name;
370721495Sjmacd  delimiter = 0;
370821495Sjmacd  while (*temp && (delimiter || !whitespace (*temp)))
370921495Sjmacd    {
371021495Sjmacd/* #if defined (SET_WITH_EQUAL) */
371121495Sjmacd      if (*temp == '"' || *temp == '\'')
371242664Smarkm        {
371342664Smarkm          if (*temp == delimiter)
371442664Smarkm            delimiter = 0;
371542664Smarkm          else
371642664Smarkm            delimiter = *temp;
371742664Smarkm        }
371821495Sjmacd/* #endif SET_WITH_EQUAL */
371921495Sjmacd      temp++;
372021495Sjmacd    }
372121495Sjmacd
372221495Sjmacd  if (*temp)
372321495Sjmacd    additional_text_present++;
372421495Sjmacd
372542664Smarkm  *temp = 0;
372621495Sjmacd
372721495Sjmacd  if (!*name)
372842664Smarkm    line_error (_("%c%s requires a name"), COMMAND_PREFIX, command);
372921495Sjmacd  else
373021495Sjmacd    {
373121495Sjmacd      switch (action)
373242664Smarkm        {
373342664Smarkm        case SET:
373442664Smarkm          {
373542664Smarkm            char *value;
373621495Sjmacd
373721495Sjmacd#if defined (SET_WITH_EQUAL)
373842664Smarkm            /* Allow a value to be saved along with a variable.  The value is
373942664Smarkm               the text following an `=' sign in NAME, if any is present. */
374021495Sjmacd
374142664Smarkm            for (value = name; *value && *value != '='; value++);
374221495Sjmacd
374342664Smarkm            if (*value)
374442664Smarkm              *value++ = 0;
374521495Sjmacd
374642664Smarkm            if (*value == '"' || *value == '\'')
374742664Smarkm              {
374842664Smarkm                value++;
374942664Smarkm                value[strlen (value) - 1] = 0;
375042664Smarkm              }
375121495Sjmacd
375221495Sjmacd#else /* !SET_WITH_EQUAL */
375342664Smarkm            /* The VALUE of NAME is the remainder of the line sans
375442664Smarkm               whitespace. */
375542664Smarkm            if (additional_text_present)
375642664Smarkm              {
375742664Smarkm                value = temp + 1;
375842664Smarkm                canon_white (value);
375942664Smarkm              }
376042664Smarkm            else
376142664Smarkm              value = "";
376221495Sjmacd#endif /* !SET_WITH_VALUE */
376321495Sjmacd
376442664Smarkm            set (name, value);
376542664Smarkm          }
376642664Smarkm          break;
376721495Sjmacd
376842664Smarkm        case CLEAR:
376942664Smarkm          clear (name);
377042664Smarkm          break;
377121495Sjmacd
377242664Smarkm        case IFSET:
377342664Smarkm        case IFCLEAR:
377442664Smarkm          /* If IFSET and NAME is not set, or if IFCLEAR and NAME is set,
377542664Smarkm             read lines from the the file until we reach a matching
377642664Smarkm             "@end CONDITION".  This means that we only take note of
377742664Smarkm             "@ifset/clear" and "@end" commands. */
377842664Smarkm          {
377942664Smarkm            char condition[8];
378042664Smarkm            int condition_len;
378142664Smarkm            int orig_line_number = line_number;
378221495Sjmacd
378342664Smarkm            if (action == IFSET)
378442664Smarkm              strcpy (condition, "ifset");
378542664Smarkm            else
378642664Smarkm              strcpy (condition, "ifclear");
378721495Sjmacd
378842664Smarkm            condition_len = strlen (condition);
378921495Sjmacd
379042664Smarkm          if ((action == IFSET && !set_p (name))
379142664Smarkm              || (action == IFCLEAR && set_p (name)))
379242664Smarkm            {
379342664Smarkm              int level = 0, done = 0;
379421495Sjmacd
379556164Sru              while (!done && input_text_offset < input_text_length)
379642664Smarkm                {
379742664Smarkm                  char *freeable_line, *line;
379821495Sjmacd
379956164Sru                  get_rest_of_line (0, &freeable_line);
380021495Sjmacd
380142664Smarkm                  for (line = freeable_line; whitespace (*line); line++);
380221495Sjmacd
380342664Smarkm                  if (*line == COMMAND_PREFIX &&
380442664Smarkm                      (strncmp (line + 1, condition, condition_len) == 0))
380542664Smarkm                    level++;
380642664Smarkm                  else if (strncmp (line, "@end", 4) == 0)
380742664Smarkm                    {
380842664Smarkm                      char *cname = line + 4;
380942664Smarkm                      char *temp;
381021495Sjmacd
381142664Smarkm                      while (*cname && whitespace (*cname))
381242664Smarkm                        cname++;
381342664Smarkm                      temp = cname;
381421495Sjmacd
381542664Smarkm                      while (*temp && !whitespace (*temp))
381642664Smarkm                        temp++;
381742664Smarkm                      *temp = 0;
381821495Sjmacd
381942664Smarkm                      if (strcmp (cname, condition) == 0)
382042664Smarkm                        {
382142664Smarkm                          if (!level)
382242664Smarkm                            {
382342664Smarkm                              done = 1;
382442664Smarkm                            }
382542664Smarkm                          else
382642664Smarkm                            level--;
382742664Smarkm                        }
382842664Smarkm                    }
382942664Smarkm                  free (freeable_line);
383042664Smarkm                }
383156164Sru
383242664Smarkm              if (!done)
3833114477Sru                file_line_error (input_filename, orig_line_number,
3834114477Sru                                 _("Reached eof before matching @end %s"),
3835114477Sru                                 condition);
383656164Sru
383742664Smarkm              /* We found the end of a false @ifset/ifclear.  If we are
383842664Smarkm                 in a menu, back up over the newline that ends the ifset,
383942664Smarkm                 since that newline may also begin the next menu entry. */
384042664Smarkm              break;
384142664Smarkm            }
384242664Smarkm          else
384342664Smarkm            {
384442664Smarkm              if (action == IFSET)
384542664Smarkm                begin_insertion (ifset);
384642664Smarkm              else
384742664Smarkm                begin_insertion (ifclear);
384842664Smarkm            }
384942664Smarkm          }
385042664Smarkm          break;
385142664Smarkm        }
385221495Sjmacd    }
385321495Sjmacd}
385421495Sjmacd
385521495Sjmacd/* Execution of random text not in file. */
385621495Sjmacdtypedef struct {
385742664Smarkm  char *string;                 /* The string buffer. */
385842664Smarkm  int size;                     /* The size of the buffer. */
385942664Smarkm  int in_use;                   /* Nonzero means string currently in use. */
386021495Sjmacd} EXECUTION_STRING;
386121495Sjmacd
386256164Srustatic EXECUTION_STRING **execution_strings = NULL;
386321495Sjmacdstatic int execution_strings_index = 0;
386421495Sjmacdstatic int execution_strings_slots = 0;
386521495Sjmacd
3866146520Srustatic EXECUTION_STRING *
3867146520Sruget_execution_string (int initial_size)
386821495Sjmacd{
386956164Sru  int i = 0;
387056164Sru  EXECUTION_STRING *es = NULL;
387121495Sjmacd
387221495Sjmacd  if (execution_strings)
387321495Sjmacd    {
387421495Sjmacd      for (i = 0; i < execution_strings_index; i++)
387542664Smarkm        if (execution_strings[i] && (execution_strings[i]->in_use == 0))
387642664Smarkm          {
387742664Smarkm            es = execution_strings[i];
387842664Smarkm            break;
387942664Smarkm          }
388021495Sjmacd    }
388121495Sjmacd
388221495Sjmacd  if (!es)
388321495Sjmacd    {
388421495Sjmacd      if (execution_strings_index + 1 >= execution_strings_slots)
388542664Smarkm        {
388656164Sru          execution_strings = xrealloc
388742664Smarkm            (execution_strings,
388842664Smarkm             (execution_strings_slots += 3) * sizeof (EXECUTION_STRING *));
388942664Smarkm          for (; i < execution_strings_slots; i++)
389056164Sru            execution_strings[i] = NULL;
389142664Smarkm        }
389221495Sjmacd
389321495Sjmacd      execution_strings[execution_strings_index] =
389456164Sru        xmalloc (sizeof (EXECUTION_STRING));
389521495Sjmacd      es = execution_strings[execution_strings_index];
389621495Sjmacd      execution_strings_index++;
389721495Sjmacd
389821495Sjmacd      es->size = 0;
389956164Sru      es->string = NULL;
390021495Sjmacd      es->in_use = 0;
390121495Sjmacd    }
390221495Sjmacd
390321495Sjmacd  if (initial_size > es->size)
390421495Sjmacd    {
390556164Sru      es->string = xrealloc (es->string, initial_size);
390621495Sjmacd      es->size = initial_size;
390721495Sjmacd    }
390856164Sru  return es;
390921495Sjmacd}
391021495Sjmacd
391156164Sru/* Given a pointer to TEXT and its desired length NEW_LEN, find TEXT's
391256164Sru   entry in the execution_strings[] array and change the .STRING and
391356164Sru   .SIZE members of that entry as appropriate.  */
391456164Sruvoid
3915146520Srumaybe_update_execution_strings (char **text, unsigned int new_len)
391656164Sru{
391756164Sru  int i = 0;
391856164Sru
391956164Sru  if (execution_strings)
392056164Sru    {
392156164Sru      for (i = 0; i < execution_strings_index; i++)
392256164Sru        if (execution_strings[i] && (execution_strings[i]->in_use == 1) &&
392356164Sru            execution_strings[i]->string == *text)
392456164Sru          {
392556164Sru            /* Don't ever shrink the string storage in execution_strings[]!
392656164Sru               execute_string assumes that it is always big enough to store
392756164Sru               every possible execution_string, and will break if that's
392856164Sru               not true.  So we only enlarge the string storage if the
392956164Sru               current size isn't big enough.  */
393056164Sru            if (execution_strings[i]->size < new_len)
393156164Sru              {
393256164Sru                execution_strings[i]->string =
393356164Sru                  *text = xrealloc (*text, new_len + 1);
393456164Sru                execution_strings[i]->size = new_len + 1;
393556164Sru              }
393656164Sru            return;
393756164Sru          }
393856164Sru    }
393956164Sru  /* We should *never* end up here, since if we are inside
394056164Sru     execute_string, TEXT is always in execution_strings[].  */
394156164Sru  abort ();
394256164Sru}
394356164Sru
3944100516Sru/* FIXME: this is an arbitrary limit.  */
3945100516Sru#define EXECUTE_STRING_MAX 16*1024
3946100516Sru
394721495Sjmacd/* Execute the string produced by formatting the ARGs with FORMAT.  This
394821495Sjmacd   is like submitting a new file with @include. */
394921495Sjmacdvoid
395042664Smarkm#if defined (VA_FPRINTF) && __STDC__
395142664Smarkmexecute_string (char *format, ...)
395242664Smarkm#else
395342664Smarkmexecute_string (format, va_alist)
395442664Smarkm    char *format;
395542664Smarkm    va_dcl
395642664Smarkm#endif
395721495Sjmacd{
395821495Sjmacd  EXECUTION_STRING *es;
3959146520Sru  char *temp_string, *temp_input_filename;
396042664Smarkm#ifdef VA_FPRINTF
396142664Smarkm  va_list ap;
396242664Smarkm#endif
3963146520Sru  int insertion_level_at_start = insertion_level;
396421495Sjmacd
3965100516Sru  es = get_execution_string (EXECUTE_STRING_MAX);
396621495Sjmacd  temp_string = es->string;
396721495Sjmacd  es->in_use = 1;
396821495Sjmacd
396942664Smarkm  VA_START (ap, format);
397042664Smarkm#ifdef VA_SPRINTF
397142664Smarkm  VA_SPRINTF (temp_string, format, ap);
397242664Smarkm#else
397342664Smarkm  sprintf (temp_string, format, a1, a2, a3, a4, a5, a6, a7, a8);
397442664Smarkm#endif /* not VA_SPRINTF */
397542664Smarkm  va_end (ap);
397621495Sjmacd
397721495Sjmacd  pushfile ();
397821495Sjmacd  input_text_offset = 0;
397921495Sjmacd  input_text = temp_string;
3980146520Sru  input_text_length = strlen (temp_string);
398142664Smarkm  input_filename = xstrdup (input_filename);
3982146520Sru  temp_input_filename = input_filename;
398321495Sjmacd
398421495Sjmacd  executing_string++;
398521495Sjmacd  reader_loop ();
398621495Sjmacd
3987146520Sru  /* If insertion stack level changes during execution, that means a multiline
3988146520Sru     command is used inside braces or @section ... kind of commands.  */
3989146520Sru  if (insertion_level_at_start != insertion_level && !executing_macro)
3990146520Sru    {
3991146520Sru      line_error (_("Multiline command %c%s used improperly"),
3992146520Sru          COMMAND_PREFIX,
3993146520Sru          command);
3994146520Sru      /* We also need to keep insertion_level intact to make sure warnings are
3995146520Sru         issued for @end ... command.  */
3996146520Sru      while (insertion_level > insertion_level_at_start)
3997146520Sru        pop_insertion ();
3998146520Sru    }
3999146520Sru
400021495Sjmacd  popfile ();
400121495Sjmacd  executing_string--;
400221495Sjmacd  es->in_use = 0;
4003146520Sru  free (temp_input_filename);
400421495Sjmacd}
400521495Sjmacd
400621495Sjmacd
400756164Sru/* Return what would be output for STR (in newly-malloced memory), i.e.,
4008146520Sru   expand Texinfo commands according to the current output format.  If
4009146520Sru   IMPLICIT_CODE is set, expand @code{STR}.  This is generally used for
4010146520Sru   short texts; filling, indentation, and html escapes are disabled.  */
401121495Sjmacd
401221495Sjmacdchar *
4013146520Sruexpansion (char *str, int implicit_code)
401421495Sjmacd{
4015146520Sru  return maybe_escaped_expansion (str, implicit_code, 0);
4016146520Sru}
4017146520Sru
4018146520Sru
4019146520Sru/* Do HTML escapes according to DO_HTML_ESCAPE.  Needed in
4020146520Sru   cm_printindex, q.v.  */
4021146520Sru
4022146520Sruchar *
4023146520Srumaybe_escaped_expansion (char *str, int implicit_code, int do_html_escape)
4024146520Sru{
4025100516Sru  char *result;
4026116530Sru
4027100516Sru  /* Inhibit indentation and filling, so that extra newlines
4028100516Sru     are not added to the expansion.  (This is undesirable if
4029100516Sru     we write the expanded text to macro_expansion_output_stream.)  */
4030100516Sru  int saved_filling_enabled = filling_enabled;
4031100516Sru  int saved_indented_fill = indented_fill;
4032100516Sru  int saved_no_indent = no_indent;
4033100516Sru  int saved_escape_html = escape_html;
4034100516Sru
4035100516Sru  filling_enabled = 0;
4036100516Sru  indented_fill = 0;
4037100516Sru  no_indent = 1;
4038146520Sru  escape_html = do_html_escape;
4039116530Sru
4040100516Sru  result = full_expansion (str, implicit_code);
4041100516Sru
4042100516Sru  filling_enabled = saved_filling_enabled;
4043100516Sru  indented_fill = saved_indented_fill;
4044100516Sru  no_indent = saved_no_indent;
4045116530Sru  escape_html = saved_escape_html;
4046116530Sru
4047100516Sru  return result;
4048100516Sru}
4049100516Sru
4050100516Sru
4051100516Sru/* Expand STR (or @code{STR} if IMPLICIT_CODE is nonzero).  No change to
4052100516Sru   any formatting parameters -- filling, indentation, html escapes,
4053146520Sru   etc., are not reset.  Always returned in new memory.  */
4054100516Sru
4055100516Sruchar *
4056146520Srufull_expansion (char *str, int implicit_code)
4057100516Sru{
405821495Sjmacd  int length;
405921495Sjmacd  char *result;
406021495Sjmacd
406121495Sjmacd  /* Inhibit any real output.  */
406221495Sjmacd  int start = output_paragraph_offset;
406321495Sjmacd  int saved_paragraph_is_open = paragraph_is_open;
406456164Sru  int saved_output_column = output_column;
406521495Sjmacd
4066100516Sru  /* More output state to save.  */
406756164Sru  int saved_meta_pos = meta_char_pos;
406856164Sru  int saved_last_char = last_inserted_character;
406956164Sru  int saved_last_nl = last_char_was_newline;
407056164Sru
407156164Sru  /* If we are called in the middle of processing a command, we need
407256164Sru     to dup and save the global variable `command' (which holds the
407356164Sru     name of this command), since the recursive reader loop will free
407456164Sru     it from under our feet if it finds any macros in STR.  */
407556164Sru  char *saved_command = command ? xstrdup (command) : NULL;
407656164Sru
407721495Sjmacd  inhibit_output_flushing ();
407842664Smarkm  paragraph_is_open = 1;
4079100516Sru  if (strlen (str) > (implicit_code
4080114477Sru                      ? EXECUTE_STRING_MAX - 1 - sizeof("@code{}")
4081114477Sru                      : EXECUTE_STRING_MAX - 1))
4082100516Sru    line_error (_("`%.40s...' is too long for expansion; not expanded"), str);
4083100516Sru  else
4084100516Sru    execute_string (implicit_code ? "@code{%s}" : "%s", str);
408521495Sjmacd  uninhibit_output_flushing ();
408621495Sjmacd
408721495Sjmacd  /* Copy the expansion from the buffer.  */
408821495Sjmacd  length = output_paragraph_offset - start;
408921495Sjmacd  result = xmalloc (1 + length);
409021495Sjmacd  memcpy (result, (char *) (output_paragraph + start), length);
409121495Sjmacd  result[length] = 0;
409256164Sru
409321495Sjmacd  /* Pretend it never happened.  */
409456164Sru  free_and_clear (&command);
409556164Sru  command = saved_command;
4096100516Sru
409721495Sjmacd  output_paragraph_offset = start;
409821495Sjmacd  paragraph_is_open = saved_paragraph_is_open;
409956164Sru  output_column = saved_output_column;
4100100516Sru
410156164Sru  meta_char_pos = saved_meta_pos;
410256164Sru  last_inserted_character = saved_last_char;
410356164Sru  last_char_was_newline = saved_last_nl;
410421495Sjmacd
410521495Sjmacd  return result;
410621495Sjmacd}
410721495Sjmacd
410821495Sjmacd
410956164Sru/* Return text (info) expansion of STR no matter what the current output
411056164Sru   format is.  */
411121495Sjmacd
411221495Sjmacdchar *
4113146520Srutext_expansion (char *str)
411421495Sjmacd{
411556164Sru  char *ret;
411656164Sru  int save_html = html;
4117100516Sru  int save_xml = xml;
4118146520Sru  int save_docbook = docbook;
4119116530Sru
412056164Sru  html = 0;
4121100516Sru  xml = 0;
4122146520Sru  docbook = 0;
412356164Sru  ret = expansion (str, 0);
412456164Sru  html = save_html;
4125100516Sru  xml = save_xml;
4126146520Sru  docbook = save_docbook;
4127116530Sru
412856164Sru  return ret;
412921495Sjmacd}
413021495Sjmacd
413121495Sjmacd
413242664Smarkm/* Set the paragraph indentation variable to the value specified in STRING.
413342664Smarkm   Values can be:
413442664Smarkm     `asis': Don't change existing indentation.
413542664Smarkm     `none': Remove existing indentation.
413642664Smarkm        NUM: Indent NUM spaces at the starts of paragraphs.
413742664Smarkm             If NUM is zero, we assume `none'.
413842664Smarkm   Returns 0 if successful, or nonzero if STRING isn't one of the above. */
413942664Smarkmint
4140146520Sruset_paragraph_indent (char *string)
414142664Smarkm{
414242664Smarkm  if (strcmp (string, "asis") == 0 || strcmp (string, _("asis")) == 0)
414342664Smarkm    paragraph_start_indent = 0;
414442664Smarkm  else if (strcmp (string, "none") == 0 || strcmp (string, _("none")) == 0)
414542664Smarkm    paragraph_start_indent = -1;
414642664Smarkm  else
414742664Smarkm    {
414842664Smarkm      if (sscanf (string, "%d", &paragraph_start_indent) != 1)
414956164Sru        return -1;
415042664Smarkm      else
415142664Smarkm        {
415242664Smarkm          if (paragraph_start_indent == 0)
415342664Smarkm            paragraph_start_indent = -1;
415442664Smarkm        }
415542664Smarkm    }
415656164Sru  return 0;
415742664Smarkm}
4158