home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / gnu / lucid / lemacs-19.6 / man / makeinfo.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-06-29  |  122.4 KB  |  5,286 lines

  1. /* Makeinfo -- convert texinfo format files into info files
  2.    Copyright (C) 1987 Free Software Foundation, Inc.
  3.  
  4. This file is part of GNU Info.
  5.  
  6. GNU Info is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 1, or (at your option)
  9. any later version.
  10.  
  11. GNU Info is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with GNU Info; see the file COPYING.  If not, write to
  18. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  19.  
  20. /* **************************************************************** */
  21. /*                                    */
  22. /*            Include File Declarations               */
  23. /*                                    */
  24. /* **************************************************************** */
  25.  
  26. #include <stdio.h>
  27. #include <sys/types.h>
  28. #include <sys/stat.h>
  29. #include <ctype.h>
  30. #include <pwd.h>
  31. #include <errno.h>
  32. #include "getopt.h"
  33.  
  34. #if defined (VMS)
  35. #include <perror.h>
  36. #endif
  37.  
  38. #if defined (SYSV) || defined (VMS)
  39. #include <string.h>
  40. #else
  41. #include <strings.h>
  42. #endif
  43.  
  44. #include <fcntl.h>
  45. #include <sys/file.h>
  46.  
  47. #if defined (SYSV)
  48. #define bcopy(source, dest, count) memcpy (dest, source, count)
  49. #endif
  50.  
  51. #if defined (__GNUC__)
  52. #define alloca __builtin_alloca
  53. #else
  54. #if defined (sparc)
  55. #include <alloca.h>
  56. #else
  57. extern char *alloca ();
  58. #endif
  59. #endif
  60.  
  61. /* Forward declarations. */
  62. char *xmalloc (), *xrealloc ();
  63. extern int in_fixed_width_font;
  64.  
  65. /* Some systems don't declare this function in pwd.h. */
  66. struct passwd *getpwnam ();
  67.  
  68.  
  69. /* **************************************************************** */
  70. /*                                    */
  71. /*                  Global Defines                  */
  72. /*                                    */
  73. /* **************************************************************** */
  74.  
  75. /* Error levels */
  76. #define NO_ERROR 0
  77. #define SYNTAX     2
  78. #define FATAL     4
  79.  
  80. /* Boolean values. */
  81. #define true  1
  82. #define false 0
  83. typedef int boolean;
  84.  
  85. /* How to allocate permanent storage for STRING. */
  86. #define savestring(x) \
  87.   ((char *)strcpy (xmalloc (1 + ((x) ? strlen (x) : 0)), (x) ? (x) : ""))
  88.  
  89. /* C's standard macros don't check to make sure that the characters being
  90.    changed are within range.  So I have to check explicitly. */
  91.  
  92. /* GNU Library doesn't have toupper().  Until GNU gets this fixed, I will
  93.    have to do it. */
  94. #ifndef toupper
  95. #define toupper(c) ((c) - 32)
  96. #endif
  97.  
  98. #define coerce_to_upper(c) ((islower(c) ? toupper(c) : (c)))
  99. #define coerce_to_lower(c) ((isupper(c) ? tolower(c) : (c)))
  100.  
  101. #define control_character_bit 0x40 /* %01000000, must be off. */
  102. #define meta_character_bit 0x080/* %10000000, must be on.  */
  103. #define CTL(c) ((c) & (~control_character_bit))
  104. #define UNCTL(c) coerce_to_upper(((c)|control_character_bit))
  105. #define META(c) ((c) | (meta_character_bit))
  106. #define UNMETA(c) ((c) & (~meta_character_bit))
  107.  
  108. #define whitespace(c) (((c) == '\t') || ((c) == ' '))
  109. #define sentence_ender(c) ((c) == '.' || (c) == '?' || (c) == '!')
  110. #define cr_or_whitespace(c) (((c) == '\t') || ((c) == ' ') || ((c) == '\n'))
  111. #define member(c, s) (index (s, c) != NULL)
  112.  
  113. #define COMMAND_PREFIX '@'
  114.  
  115. /* Stuff for splitting large files. */
  116. #define SPLIT_SIZE_THRESHOLD 70000    /* What's good enough for Stallman... */
  117. #define DEFAULT_SPLIT_SIZE 50000/* Is probably good enough for me. */
  118. boolean splitting = true;    /* Always true for now. */
  119.  
  120. typedef int FUNCTION ();    /* So I can say FUNCTION *foo; */
  121.  
  122.  
  123. /* **************************************************************** */
  124. /*                                    */
  125. /*                Global Variables                */
  126. /*                                    */
  127. /* **************************************************************** */
  128.  
  129. /* Global pointer to argv[0]. */
  130. char *progname;
  131.  
  132. /* The current input file state. */
  133. char *input_filename;
  134. char *input_text;
  135. int size_of_input_text;
  136. int input_text_offset;
  137. int line_number;
  138.  
  139. #define curchar() input_text[input_text_offset]
  140.  
  141. #define command_char(c) ((!whitespace(c)) && \
  142.               ((c) != '\n') && \
  143.               ((c) != '{'))
  144. #define skip_whitespace() while (input_text_offset != size_of_input_text \
  145.                  && whitespace(curchar()))\
  146.   input_text_offset++
  147.  
  148. /* And writing to the output. */
  149.  
  150. /* The output file name. */
  151. char *output_filename, *pretty_output_filename;
  152.  
  153. /* Current output stream. */
  154. FILE *output_stream;
  155.  
  156. /* Position in the output file. */
  157. int output_position;
  158.  
  159. /* Output paragraph buffer. */
  160. char *output_paragraph;
  161.  
  162. /* Offset into OUTPUT_PARAGRAPH. */
  163. int output_paragraph_offset;
  164.  
  165. /* The output paragraph "cursor" horizontal position. */
  166. int output_column = 0;
  167.  
  168. /* non-zero means output_paragraph contains text. */
  169. boolean paragraph_is_open = false;
  170.  
  171. #define INITIAL_PARAGRAPH_SPACE 5000
  172. int paragraph_buffer_len = INITIAL_PARAGRAPH_SPACE;
  173.  
  174. /* Filling.. */
  175. /* True indicates that filling will take place on long lines. */
  176. boolean filling_enabled = true;
  177.  
  178. /* Non-zero means that words are not to be split, even in long lines.  This
  179.    gets changed for cm_w (). */
  180. int non_splitting_words = 0;
  181.  
  182. /* True indicates that filling a line also indents the new line. */
  183. boolean indented_fill = false;
  184.  
  185. /* The column at which long lines are broken. */
  186. int fill_column = 72;
  187.  
  188. /* The amount of indentation to apply at the start of each line. */
  189. int current_indent = 0;
  190.  
  191. /* The amount of indentation to add at the starts of paragraphs.
  192.    0 means don't change existing indentation at paragraph starts.
  193.    > 0 is amount to indent new paragraphs by.
  194.    < 0 means indent to column zero by removing indentation if necessary.
  195.  
  196.    This is normally zero, but some people prefer paragraph starts to be
  197.    somewhat more indented than paragraph bodies.  A pretty value for
  198.    this is 3. */
  199. int paragraph_start_indent = 3;
  200.  
  201. /* Non-zero means that the use of paragraph_start_indent is inhibited.
  202.    @example uses this to line up the left columns of the example text. */
  203. int inhibit_paragraph_indentation = 0;
  204.  
  205. /* Indentation that is pending insertion.  We have this for hacking lines
  206.    which look blank, but contain whitespace.  We want to treat those as
  207.    blank lines. */
  208. int pending_indent = 0;
  209.  
  210. /* The amount that indentation increases/decreases by. */
  211. int default_indentation_increment = 5;
  212.  
  213. /* True indicates that indentation is temporarily turned off. */
  214. boolean no_indent = true;
  215.  
  216. /* Command name in the process of being hacked. */
  217. char *command;
  218.  
  219. /* The index in our internal command table of the currently
  220.    executing command. */
  221. int command_index;
  222.  
  223. /* A stack of file information records.  If a new file is read in with
  224.    "@input", we remember the old input file state on this stack. */
  225. typedef struct fstack
  226. {
  227.   struct fstack *next;
  228.   char *filename;
  229.   char *text;
  230.   int size;
  231.   int offset;
  232.   int line_number;
  233. } FSTACK;
  234.  
  235. FSTACK *filestack = (FSTACK *) NULL;
  236.  
  237. /* Stuff for nodes. */
  238. /* The current nodes node name */
  239. char *current_node;
  240.  
  241. /* The current nodes section level. */
  242. int current_section = 0;
  243.  
  244. /* The filename of the current input file.  This is never freed. */
  245. char *node_filename = (char *)NULL;
  246.  
  247. /* What we remember for each node. */
  248. typedef struct tentry
  249. {
  250.   struct tentry *next_ent;
  251.   char *node;        /* name of this node. */
  252.   char *prev;        /* name of "Prev:" for this node. */
  253.   char *next;        /* name of "Next:" for this node. */
  254.   char *up;        /* name of "Up:" for this node.   */
  255.   int position;        /* output file position of this node. */
  256.   int line_no;        /* defining line in source file. */
  257.   char *filename;    /* The file that this node was found in. */
  258.   int touched;        /* non-zero means this node has been referenced. */
  259.   int flags;        /* Room for growth.  Right now, contains 1 bit. */
  260. } TAG_ENTRY;
  261.  
  262. /* If node-a has a "Next" for node-b, but node-b has no "Prev" for node-a,
  263.    we turn on this flag bit in node-b's tag entry.  This means that when
  264.    it is time to validate node-b, we don't report an additional error
  265.    if there was no "Prev" field. */
  266. #define PREV_ERROR 0x1
  267. #define NEXT_ERROR 0x2
  268. #define UP_ERROR   0x4
  269. #define NO_WARN       0x8
  270.  
  271. TAG_ENTRY *tag_table = (TAG_ENTRY *) NULL;
  272.  
  273. /* Menu reference, *note reference, and validation hacking. */
  274.  
  275. /* The various references that we know about. */
  276. enum reftype
  277. {
  278.   menu_reference, followed_reference
  279. };
  280.  
  281. /* A structure to remember references with.  A reference to a node is
  282.    either an entry in a menu, or a cross-reference made with [px]ref. */
  283. typedef struct node_ref
  284. {
  285.   struct node_ref *next;
  286.   char *node;            /* Name of node referred to. */
  287.   char *containing_node;    /* Name of node containing this reference. */
  288.   int line_no;            /* Line number where the reference occurs. */
  289.   int section;            /* Section level where the reference occurs. */
  290.   char *filename;        /* Name of file where the reference occurs. */
  291.   enum reftype type;        /* Type of reference, either menu or note. */
  292. } NODE_REF;
  293.  
  294. /* The linked list of such structures. */
  295. NODE_REF *node_references = (NODE_REF *) NULL;
  296.  
  297. /* Flag which tells us whether to examine menu lines or not. */
  298. int in_menu = 0;
  299.  
  300. /* Flags controlling the operation of the program. */
  301.  
  302. /* Default is to notify users of bad choices. */
  303. boolean print_warnings = true;
  304.  
  305. /* Default is to check node references. */
  306. boolean validating = true;
  307.  
  308. /* Number of errors that we tolerate on a given fileset. */
  309. int max_error_level = 100;
  310.  
  311. /* Maximum number of references to a single node before complaining. */
  312. int reference_warning_limit = 1000;
  313.  
  314. /* Non-zero means print out information about what is going on when it
  315.    is going on. */
  316. int verbose_mode = 0;
  317.  
  318. /* The list of commands that we hack in texinfo.  Each one
  319.    has an associated function.  When the command is encountered in the
  320.    text, the associated function is called with START as the argument.
  321.    If the function expects arguments in braces, it remembers itself on
  322.    the stack.  When the corresponding close brace is encountered, the
  323.    function is called with END as the argument. */
  324.  
  325. #define START 0
  326. #define END 1
  327.  
  328. typedef struct brace_element
  329. {
  330.   struct brace_element *next;
  331.   FUNCTION *proc;
  332.   int pos, line;
  333. } BRACE_ELEMENT;
  334.  
  335. BRACE_ELEMENT *brace_stack = (BRACE_ELEMENT *) NULL;
  336.  
  337. /* Forward declarations. */
  338.  
  339. int
  340. insert_self (), cm_tex (), cm_asterisk (), cm_dots (), cm_bullet (),
  341. cm_TeX (), cm_copyright (), cm_code (), cm_samp (), cm_file (), cm_kbd (),
  342. cm_key (), cm_ctrl (), cm_var (), cm_dfn (), cm_emph (), cm_strong (),
  343. cm_cite (), cm_italic (), cm_bold (), cm_roman (), cm_title (), cm_w (),
  344. cm_refill ();
  345.  
  346. int
  347. cm_chapter (), cm_unnumbered (), cm_appendix (),
  348. cm_section (), cm_unnumberedsec (), cm_appendixsec (),
  349. cm_subsection (), cm_unnumberedsubsec (), cm_appendixsubsec (),
  350. cm_subsubsection (), cm_unnumberedsubsubsec (), cm_appendixsubsubsec (),
  351. cm_heading (), cm_chapheading (), cm_subheading (), cm_subsubheading (),
  352. cm_majorheading ();
  353.  
  354. /* All @defxxx commands map to cm_defun (). */
  355. int
  356. cm_defun ();
  357.  
  358. int
  359. cm_node (), cm_menu (), cm_xref (),
  360. cm_pxref (), cm_inforef (), cm_quotation (), cm_display (), cm_itemize (),
  361. cm_enumerate (), cm_table (), cm_itemx (), cm_noindent (), cm_setfilename (),
  362. cm_comment (), cm_ignore (), cm_br (), cm_sp (), cm_page (), cm_group (),
  363. cm_need (), cm_center (), cm_include (), cm_bye (), cm_item (), cm_end (),
  364. cm_infoinclude (), cm_ifinfo (), cm_iftex (), cm_titlepage (),
  365. cm_titlespec (),cm_kindex (), cm_cindex (), cm_findex (), cm_pindex (),
  366. cm_vindex (), cm_tindex (), cm_asis (), cm_synindex (), cm_settitle (),
  367. cm_setchapternewpage (), cm_printindex (), cm_minus (), cm_footnote (),
  368. cm_force_abbreviated_whitespace (), cm_force_sentence_end (), cm_example (),
  369. cm_smallexample (), cm_lisp (), cm_format (), cm_exdent (), cm_defindex (),
  370. cm_defcodeindex (), cm_sc (), cm_result (), cm_expansion (), cm_equiv (),
  371. cm_print (), cm_error (), cm_point (), cm_smallbook ();
  372.  
  373. int do_nothing ();
  374. int misplaced_brace (), cm_obsolete ();
  375.  
  376. typedef struct
  377. {
  378.   char *name;
  379.   FUNCTION *proc;
  380.   boolean argument_in_braces;
  381. } COMMAND;
  382.  
  383. /* Stuff for defining commands on the fly. */
  384. COMMAND **user_command_array = (COMMAND **) NULL;
  385. int user_command_array_len = 0;
  386.  
  387. static COMMAND CommandTable[] = {
  388.   {"!", cm_force_sentence_end, false},
  389.   {"'", insert_self, false},
  390.   {"*", cm_asterisk, false},
  391.   {".", cm_force_sentence_end, false},
  392.   {":", cm_force_abbreviated_whitespace, false},
  393.   {"?", cm_force_sentence_end, false},
  394.   {"@", insert_self, false},
  395.   {" ", insert_self, false},
  396.   {"\n", insert_self, false},
  397.   {"TeX", cm_TeX, true},
  398.   {"`", insert_self, false},
  399.   {"appendix", cm_appendix, false},
  400.   {"appendixsec", cm_appendixsec, false},
  401.   {"appendixsubsec", cm_appendixsubsec, false},
  402.   {"appendixsubsubsec", cm_appendixsubsubsec, false},
  403.   {"asis", cm_asis, true},
  404.   {"b", cm_bold, true},
  405.   {"br", cm_br, false},
  406.   {"bullet", cm_bullet, true},
  407.   {"bye", cm_bye, false},
  408.   {"c", cm_comment, false},
  409.   {"center", cm_center, false},
  410.   {"chapheading", cm_chapheading, false},
  411.   {"chapter", cm_chapter, false},
  412.   {"cindex", cm_cindex, false},
  413.   {"cite", cm_cite, true},
  414.   {"code", cm_code, true},
  415.   {"comment", cm_comment, false},
  416.   {"contents", do_nothing, false},
  417.   {"copyright", cm_copyright, true},
  418.   {"ctrl", cm_ctrl, true},
  419.   {"defcodeindex", cm_defcodeindex, false},
  420.   {"defindex", cm_defindex, false},
  421.   {"dfn", cm_dfn, true},
  422.  
  423. /* The `def' commands. */
  424.   {"defun", cm_defun, false},
  425.   {"defunx", cm_defun, false},
  426.   {"defvar", cm_defun, false},
  427.   {"defvarx", cm_defun, false},
  428.   {"defopt", cm_defun, false},
  429.   {"defoptx", cm_defun, false},
  430.   {"deffn", cm_defun, false},
  431.   {"deffnx", cm_defun, false},
  432.   {"defspec", cm_defun, false},
  433.   {"defspecx", cm_defun, false},
  434.   {"defmac", cm_defun, false},
  435.   {"defmacx", cm_defun, false},
  436. /* The end of the `def' commands. */
  437.  
  438.   {"display", cm_display, false},
  439.   {"dots", cm_dots, true},
  440.   {"emph", cm_emph, true},
  441.   {"end", cm_end, false},
  442.   {"enumerate", cm_enumerate, false},
  443.   {"equiv", cm_equiv, true},
  444.   {"error", cm_error, true},
  445.   {"example", cm_example, false},
  446.   {"exdent", cm_exdent, false},
  447.   {"expansion", cm_expansion, true},
  448.   {"file", cm_file, true},
  449.   {"findex", cm_findex, false},
  450.   {"format", cm_format, false},
  451.   {"group", cm_group, false},
  452.   {"heading", cm_heading, false},
  453.   {"i", cm_italic, true},
  454.   {"iappendix", cm_appendix, false},
  455.   {"iappendixsec", cm_appendixsec, false},
  456.   {"iappendixsubsec", cm_appendixsubsec, false},
  457.   {"iappendixsubsubsec", cm_appendixsubsubsec, false},
  458.   {"ichapter", cm_chapter, false},
  459.   {"ifinfo", cm_ifinfo, false},
  460.   {"iftex", cm_iftex, false},
  461.   {"ignore", cm_ignore, false},
  462.   {"include", cm_include, false},
  463.   {"inforef", cm_inforef, true},
  464.   {"input", cm_include, false},
  465.   {"isection", cm_section, false},
  466.   {"isubsection", cm_subsection, false},
  467.   {"isubsubsection", cm_subsubsection, false},
  468.   {"item", cm_item, false},
  469.   {"itemize", cm_itemize, false},
  470.   {"itemx", cm_itemx, false},
  471.   {"iunnumbered", cm_unnumbered, false},
  472.   {"iunnumberedsec", cm_unnumberedsec, false},
  473.   {"iunnumberedsubsec", cm_unnumberedsubsec, false},
  474.   {"iunnumberedsubsubsec", cm_unnumberedsubsubsec, false},
  475.   {"kbd", cm_kbd, true},
  476.   {"key", cm_key, true},
  477.   {"kindex", cm_kindex, false},
  478.   {"lisp", cm_lisp, false},
  479.   {"majorheading", cm_majorheading, false},
  480.   {"menu", cm_menu},
  481.   {"minus", cm_minus, true},
  482.   {"need", cm_need, false},
  483.   {"node", cm_node, false},
  484.   {"noindent", cm_noindent, false},
  485.   {"page", do_nothing, false},
  486.   {"pindex", cm_pindex, false},
  487.   {"point", cm_point, true},
  488.   {"print", cm_print, true},
  489.   {"printindex", cm_printindex, false},
  490.   {"pxref", cm_pxref, true},
  491.   {"quotation", cm_quotation, false},
  492.   {"r", cm_roman, true},
  493.   {"ref", cm_xref, true},
  494.   {"refill", cm_refill, false},
  495.   {"result", cm_result, true},
  496.   {"samp", cm_samp, true},
  497.   {"sc", cm_sc, true},
  498.   {"section", cm_section, false},
  499.   {"setchapternewpage", cm_setchapternewpage, false},
  500.   {"setfilename", cm_setfilename, false},
  501.   {"settitle", cm_settitle, false},
  502.   {"smallexample", cm_smallexample, false},
  503.   {"smallbook", cm_smallbook, false},
  504.   {"sp", cm_sp, false},
  505.   {"strong", cm_strong, true},
  506.   {"subheading", cm_subheading, false},
  507.   {"subsection", cm_subsection, false},
  508.   {"subsubheading", cm_subsubheading, false},
  509.   {"subsubsection", cm_subsubsection, false},
  510.   {"summarycontents", do_nothing, false},
  511.   {"syncodeindex", cm_synindex, false},
  512.   {"synindex", cm_synindex, false},
  513.   {"t", cm_title, true},
  514.   {"table", cm_table, false},
  515.   {"tex", cm_tex, false},
  516.   {"tindex", cm_tindex, false},
  517.   {"titlepage", cm_titlepage, false},
  518.   {"titlespec", cm_titlespec, false},
  519.   {"unnumbered", cm_unnumbered, false},
  520.   {"unnumberedsec", cm_unnumberedsec, false},
  521.   {"unnumberedsubsec", cm_unnumberedsubsec, false},
  522.   {"unnumberedsubsubsec", cm_unnumberedsubsubsec, false},
  523.   {"var", cm_var, true},
  524.   {"vindex", cm_vindex, false},
  525.   {"w", cm_w, true},
  526.   {"xref", cm_xref, true},
  527.   {"{", insert_self, false},
  528.   {"}", insert_self, false},
  529.  
  530.   /* Now @include does what this was supposed to. */
  531.   {"infoinclude", cm_infoinclude, false},
  532.   {"footnote", cm_footnote, false}, /* self-arg eater */
  533.  
  534.   {(char *) NULL, (FUNCTION *) NULL}, false};
  535.  
  536. /* Non-zero means we are running inside of Emacs. */
  537. int in_emacs = 0;
  538.  
  539. #ifndef MAKEINFO_MAJOR
  540. #define MAKEINFO_MAJOR 1
  541. #endif
  542.  
  543. #ifndef MAKEINFO_MINOR
  544. #define MAKEINFO_MINOR 0
  545. #endif
  546.  
  547. int major_version = MAKEINFO_MAJOR;
  548. int minor_version = MAKEINFO_MINOR;
  549.  
  550. struct option long_options[] =
  551. {
  552.   { "no-validate", 0, &validating, false },    /* formerly -nv */
  553.   { "no-warn", 0, &print_warnings, false },    /* formerly -nw */
  554.   { "no-split", 0, &splitting, false },        /* formerly -ns */
  555.   { "verbose", 0, &verbose_mode, 1 },        /* formerly -verbose */
  556.   { "fill-column", 1, 0, 'f' },            /* formerly -fc */
  557.   { "paragraph-indent", 1, 0, 'p' },        /* formerly -pi */
  558.   { "error-limit", 1, 0, 'e' },            /* formerly -el */
  559.   { "reference-limit", 1, 0, 'r' },        /* formerly -rl */
  560.   { "footnote-style", 1, 0, 's' },        /* formerly -ft */
  561.   { "version", 0, 0, 'V' },
  562.   {NULL, 0, NULL, 0}
  563. };
  564.   
  565. /* **************************************************************** */
  566. /*                                    */
  567. /*            Main ()  Start of code              */
  568. /*                                        */
  569. /* **************************************************************** */
  570.  
  571. /* For each file mentioned in the command line, process it, turning
  572.    texinfo commands into wonderfully formatted output text. */
  573. main (argc, argv)
  574.      int argc;
  575.      char **argv;
  576. {
  577.   char *t = (char *) getenv ("EMACS");
  578.   int c;
  579.   int ind;
  580.  
  581.   progname = argv[0];
  582.  
  583.   if (t && strcmp (t, "t") == 0)
  584.     in_emacs++;
  585.  
  586.   /* Parse argument flags from the input line. */
  587.   while ((c = getopt_long (argc, argv, "", long_options, &ind)) != EOF)
  588.     {
  589.       if (c == 0 && long_options[ind].flag == 0)
  590.     c = long_options[ind].val;
  591.       switch (c)
  592.     {
  593.     case 'f':
  594.       /* user specified fill_column? */
  595.       if (sscanf (optarg, "%d", &fill_column) != 1)
  596.         usage ();
  597.       break;
  598.  
  599.     case 'p':
  600.       /* User specified paragraph indent (paragraph_start_index)? */
  601.       if (sscanf (optarg, "%d", ¶graph_start_indent) != 1)
  602.         usage ();
  603.       break;
  604.  
  605.     case 'e':
  606.       /* User specified error level? */
  607.       if (sscanf (optarg, "%d", &max_error_level) != 1)
  608.         usage ();
  609.       break;
  610.  
  611.     case 'r':
  612.       /* User specified reference warning limit? */
  613.       if (sscanf (optarg, "%d", &reference_warning_limit) != 1)
  614.         usage ();
  615.       break;
  616.  
  617.     case 's':
  618.       /* User specified footnote style? */
  619.       set_footnote_style (optarg);
  620.       break;
  621.  
  622.     case 'V':        /* Use requested version info? */
  623.       fprintf (stderr, "Makeinfo verison %d.%d.\n",
  624.            major_version, minor_version);
  625.       exit (NO_ERROR);
  626.       break;
  627.  
  628.     case '?':
  629.       usage ();
  630.     }
  631.     }
  632.  
  633.   if (optind == argc)
  634.     usage ();
  635.  
  636.   /* Remaining arguments are file names of texinfo files.
  637.      Convert them, one by one. */
  638.   while (optind != argc)
  639.     convert (argv[optind++]);
  640.  
  641.   exit (NO_ERROR);
  642. }
  643.  
  644.  
  645. /* **************************************************************** */
  646. /*                                    */
  647. /*            Generic Utilities                */
  648. /*                                    */
  649. /* **************************************************************** */
  650.  
  651. /* Just like malloc, but kills the program in case of fatal error. */
  652. char *
  653. xmalloc (nbytes)
  654.      int nbytes;
  655. {
  656.   char *temp = (char *) malloc (nbytes);
  657.   if (temp == (char *) NULL)
  658.     {
  659.       error ("Virtual memory exhausted! Needed %d bytes.", nbytes);
  660.       exit (FATAL);
  661.     }
  662.   return (temp);
  663. }
  664.  
  665. /* Like realloc (), but barfs if there isn't enough memory. */
  666. char *
  667. xrealloc (pointer, nbytes)
  668.      char *pointer;
  669.      int nbytes;
  670. {
  671.   pointer = (char *) realloc (pointer, nbytes);
  672.   if (!pointer)
  673.     {
  674.       error ("Virtual memory exhausted in realloc ().");
  675.       abort ();
  676.     }
  677.   return (pointer);
  678. }
  679.  
  680. /* Tell the user how to use this program. */
  681. usage ()
  682. {
  683.   fprintf (stderr, "Usage: %s [options] texinfo-file...\n\
  684. \n\
  685. This program accepts as input files of texinfo commands and text\n\
  686. and outputs a file suitable for reading with GNU Info.\n\
  687. \n\
  688. The options are:\n\
  689. `+no-validate' to suppress node cross reference validation.\n\
  690. `+no-warn' to suppress warning messages (errors are still output).\n\
  691. `+no-split' to suppress the splitting of large files.\n\
  692. `+verbose' to print information about what is being done.\n\
  693. `+version' to print the version number of Makeinfo.\n\
  694. `+paragraph-indent NUM' to set the paragraph indent to NUM (default %d).\n\
  695. `+fill-column NUM' to set the filling column to NUM (default %d).\n\
  696. `+error-limit NUM' to set the error limit to NUM (default %d).\n\
  697. `+reference-limit NUM' to set the reference warning limit to NUM (default %d).\n\
  698. `+footnote-style STYLE' to set the footnote style to STYLE.  STYLE should\n\
  699.   either be `MN' for `make node', or `BN' for `bottom node'.\n\n",
  700.        progname, paragraph_start_indent,
  701.        fill_column, max_error_level, reference_warning_limit);
  702.   exit (FATAL);
  703. }
  704.  
  705. /* **************************************************************** */
  706. /*                                    */
  707. /*            Manipulating Lists                  */
  708. /*                                        */
  709. /* **************************************************************** */
  710.  
  711. typedef struct generic_list
  712. {
  713.   struct generic_list *next;
  714. }            GENERIC_LIST;
  715.  
  716. /* Reverse the chain of structures in LIST.  Output the new head
  717.    of the chain.  You should always assign the output value of this
  718.    function to something, or you will lose the chain. */
  719. GENERIC_LIST *
  720. reverse_list (list)
  721.      register GENERIC_LIST *list;
  722. {
  723.   register GENERIC_LIST *next;
  724.   register GENERIC_LIST *prev = (GENERIC_LIST *) NULL;
  725.  
  726.   while (list)
  727.     {
  728.       next = list->next;
  729.       list->next = prev;
  730.       prev = list;
  731.       list = next;
  732.     }
  733.   return (prev);
  734. }
  735.  
  736.  
  737. /* **************************************************************** */
  738. /*                                    */
  739. /*            Pushing and Popping Files               */
  740. /*                                    */
  741. /* **************************************************************** */
  742.  
  743. /* Find and load the file named FILENAME.  Return a pointer to
  744.    the loaded file, or NULL if it can't be loaded. */
  745. char *
  746. find_and_load (filename)
  747.      char *filename;
  748. {
  749.   struct stat fileinfo;
  750.   int file, n, i, count = 0;
  751.   char *result = (char *) NULL;
  752.  
  753.   if (stat (filename, &fileinfo) != 0)
  754.     goto error_exit;
  755.  
  756.   file = open (filename, O_RDONLY);
  757.   if (file < 0)
  758.     goto error_exit;
  759.  
  760.   /* Load the file. */
  761.   result = xmalloc (fileinfo.st_size);
  762.  
  763.   /* VMS stat lies about the st_size value.  The actual number of
  764.      readable bytes is always less than this value.  The arcane
  765.      mysteries of VMS/RMS are too much to probe, so this hack
  766.     suffices to make things work. */
  767. #if defined (VMS)
  768.   while ((n = read (file, result+count, fileinfo.st_size)) > 0)
  769.     count += n;
  770.   if (n == -1)
  771. #else
  772.     count = fileinfo.st_size;
  773.     if (read (file, result, fileinfo.st_size) != fileinfo.st_size)
  774. #endif
  775.   error_exit:
  776.     {
  777.       if (result)
  778.     free (result);
  779.       if (file != -1)
  780.     close (file);
  781.       return ((char *) NULL);
  782.     }
  783.   close (file);
  784.  
  785.   /* Set the globals to the new file. */
  786.   input_text = result;
  787.   size_of_input_text = fileinfo.st_size;
  788.   input_filename = savestring (filename);
  789.   node_filename = savestring (filename);
  790.   input_text_offset = 0;
  791.   line_number = 1;
  792.   return (result);
  793. }
  794.  
  795. /* Save the state of the current input file. */
  796. pushfile ()
  797. {
  798.   FSTACK *newstack = (FSTACK *) xmalloc (sizeof (FSTACK));
  799.   newstack->filename = input_filename;
  800.   newstack->text = input_text;
  801.   newstack->size = size_of_input_text;
  802.   newstack->offset = input_text_offset;
  803.   newstack->line_number = line_number;
  804.   newstack->next = filestack;
  805.  
  806.   filestack = newstack;
  807.   push_node_filename ();
  808. }
  809.  
  810. /* Make the current file globals be what is on top of the file stack. */
  811. popfile ()
  812. {
  813.   extern int executing_string;
  814.   FSTACK *temp = filestack;
  815.  
  816.   if (!filestack)
  817.     abort ();            /* My fault.  I wonder what I did? */
  818.  
  819.   /* Make sure that commands with braces have been satisfied. */
  820.   if (!executing_string)
  821.     discard_braces ();
  822.  
  823.   /* Get the top of the stack into the globals. */
  824.   input_filename = filestack->filename;
  825.   input_text = filestack->text;
  826.   size_of_input_text = filestack->size;
  827.   input_text_offset = filestack->offset;
  828.   line_number = filestack->line_number;
  829.  
  830.   /* Pop the stack. */
  831.   filestack = filestack->next;
  832.   free (temp);
  833.   pop_node_filename ();
  834. }
  835.  
  836. /* Flush all open files on the file stack. */
  837. flush_file_stack ()
  838. {
  839.   while (filestack)
  840.     {
  841.       free (input_filename);
  842.       free (input_text);
  843.       popfile ();
  844.     }
  845. }
  846.  
  847. int node_filename_stack_index = 0;
  848. int node_filename_stack_size = 0;
  849. char **node_filename_stack = (char **)NULL;
  850.  
  851. push_node_filename ()
  852. {
  853.   if (node_filename_stack_index + 1 > node_filename_stack_size)
  854.     {
  855.       if (!node_filename_stack)
  856.     node_filename_stack =
  857.       (char **)xmalloc ((node_filename_stack_size += 10)
  858.                 * sizeof (char *));
  859.       else
  860.     node_filename_stack =
  861.       (char **)xrealloc (node_filename_stack,
  862.                  (node_filename_stack_size + 10)
  863.                  * sizeof (char *));
  864.     }
  865.  
  866.   node_filename_stack[node_filename_stack_index] = node_filename;
  867.   node_filename_stack_index++;
  868. }
  869.  
  870. pop_node_filename ()
  871. {
  872.   node_filename = node_filename_stack[--node_filename_stack_index];
  873. }
  874.  
  875. /* Return just the simple part of the filename; i.e. the
  876.    filename without the path information, or extensions.
  877.    This conses up a new string. */
  878. char *
  879. filename_part (filename)
  880.      char *filename;
  881. {
  882.   register int i = strlen (filename) - 1;
  883.  
  884.   while (i && filename[i] != '/')
  885.     i--;
  886.   if (filename[i] == '/')
  887.     i++;
  888.  
  889. #ifdef REMOVE_OUTPUT_EXTENSIONS
  890.   result = savestring (&filename[i]);
  891.  
  892.   /* See if there is an extension to remove.  If so, remove it. */
  893.   if (rindex (result, '.'))
  894.     *(rindex (result, '.')) = '\0';
  895.   return (result);
  896. #else
  897.   return (savestring (&filename[i]));
  898. #endif /* REMOVE_OUTPUT_EXTENSIONS */
  899. }
  900.  
  901. /* Return the pathname part of filename.  This can be NULL. */
  902. char *
  903. pathname_part (filename)
  904.      char *filename;
  905. {
  906.   char *expand_filename ();
  907.   char *result = (char *) NULL;
  908.   register int i;
  909.  
  910.   filename = expand_filename (filename, "");
  911.  
  912.   i = strlen (filename) - 1;
  913.  
  914.   while (i && filename[i] != '/')
  915.     i--;
  916.   if (filename[i] == '/')
  917.     i++;
  918.  
  919.   if (i)
  920.     {
  921.       result = xmalloc (1 + i);
  922.       strncpy (result, filename, i);
  923.       result[i] = '\0';
  924.     }
  925.   free (filename);
  926.   return (result);
  927. }
  928.  
  929. /* Return the expansion of FILENAME. */
  930. char *
  931. expand_filename (filename, input_name)
  932.      char *filename, *input_name;
  933. {
  934.   char *full_pathname ();
  935.   filename = full_pathname (filename);
  936.  
  937.   if (filename[0] == '.')
  938.     return (filename);
  939.  
  940.   if (filename[0] != '/' && input_name[0] == '/')
  941.     {
  942.       /* Make it so that relative names work. */
  943.       char *result = xmalloc (1 + strlen (input_name)
  944.                   + strlen (filename));
  945.       int i = strlen (input_name) - 1;
  946.  
  947.       strcpy (result, input_name);
  948.       while (result[i] != '/' && i)
  949.     i--;
  950.       if (result[i] == '/')
  951.     i++;
  952.       strcpy (&result[i], filename);
  953.       free (filename);
  954.       return (result);
  955.     }
  956.   return (filename);
  957. }
  958.  
  959. /* Return the full path to FILENAME. */
  960. char *
  961. full_pathname (filename)
  962.      char *filename;
  963. {
  964.   int initial_character;
  965.  
  966.   if (filename && (initial_character = *filename))
  967.     {
  968.       if (initial_character == '/')
  969.     return (savestring (filename));
  970.       if (initial_character != '~')
  971.     {
  972.       return (savestring (filename));
  973.     }
  974.       else
  975.     {
  976.       if (filename[1] == '/')
  977.         {
  978.           /* Return the concatenation of HOME and the rest of the string. */
  979.           char *temp_home = (char *) getenv ("HOME");
  980.           char *temp_name = xmalloc (strlen (&filename[2])
  981.                      + 1
  982.                      + temp_home ? strlen (temp_home)
  983.                      : 0);
  984.           if (temp_home)
  985.         strcpy (temp_name, temp_home);
  986.           strcat (temp_name, &filename[2]);
  987.           return (temp_name);
  988.         }
  989.       else
  990.         {
  991.           struct passwd *user_entry;
  992.           int i, c;
  993.           char *username = xmalloc (257);
  994.           char *temp_name;
  995.  
  996.           for (i = 1; c = filename[i]; i++)
  997.         {
  998.           if (c == '/')
  999.             break;
  1000.           else
  1001.             username[i - 1] = c;
  1002.         }
  1003.           if (c)
  1004.         username[i - 1] = '\0';
  1005.  
  1006.           user_entry = getpwnam (username);
  1007.  
  1008.           if (!user_entry)
  1009.         return (savestring (filename));
  1010.  
  1011.           temp_name = xmalloc (1 + strlen (user_entry->pw_dir)
  1012.                    + strlen (&filename[i]));
  1013.           strcpy (temp_name, user_entry->pw_dir);
  1014.           strcat (temp_name, &filename[i]);
  1015.           return (temp_name);
  1016.         }
  1017.     }
  1018.     }
  1019.   else
  1020.     {
  1021.       return (savestring (filename));
  1022.     }
  1023. }
  1024.  
  1025. /* **************************************************************** */
  1026. /*                                    */
  1027. /*            Error Handling                    */
  1028. /*                                    */
  1029. /* **************************************************************** */
  1030.  
  1031. /* Number of errors encountered. */
  1032. int errors_printed = 0;
  1033.  
  1034. /* Print the last error gotten from the file system. */
  1035. fs_error (filename)
  1036.      char *filename;
  1037. {
  1038.   perror (filename);
  1039.   return ((int) false);
  1040. }
  1041.  
  1042. /* Print an error message, and return false. */
  1043. error (format, arg1, arg2, arg3, arg4, arg5)
  1044.      char *format;
  1045. {
  1046.   remember_error ();
  1047.   fprintf (stderr, format, arg1, arg2, arg3, arg4, arg5);
  1048.   fprintf (stderr, "\n");
  1049.   return ((int) false);
  1050. }
  1051.  
  1052. /* Just like error (), but print the line number as well. */
  1053. line_error (format, arg1, arg2, arg3, arg4, arg5)
  1054.      char *format;
  1055. {
  1056.   remember_error ();
  1057.   fprintf (stderr, "%s:%d: ", input_filename, line_number);
  1058.   fprintf (stderr, format, arg1, arg2, arg3, arg4, arg5);
  1059.   fprintf (stderr, ".\n");
  1060.   return ((int) false);
  1061. }
  1062.  
  1063. warning (format, arg1, arg2, arg3, arg4, arg5)
  1064.      char *format;
  1065. {
  1066.   if (print_warnings)
  1067.     {
  1068.       fprintf (stderr, "%s:%d: Warning: ", input_filename, line_number);
  1069.       fprintf (stderr, format, arg1, arg2, arg3, arg4, arg5);
  1070.       fprintf (stderr, ".\n");
  1071.     }
  1072.   return ((int) false);
  1073. }
  1074.  
  1075. /* Remember that an error has been printed.  If this is the first
  1076.    error printed, then tell them which program is printing them.
  1077.    If more than max_error_level have been printed, then exit the
  1078.    program. */
  1079. remember_error ()
  1080. {
  1081.   errors_printed++;
  1082.   if (max_error_level && (errors_printed > max_error_level))
  1083.     {
  1084.       fprintf (stderr, "Too many errors!  Gave up.");
  1085.       flush_file_stack ();
  1086.       cm_bye ();
  1087.     }
  1088. }
  1089.  
  1090.  
  1091. /* **************************************************************** */
  1092. /*                                    */
  1093. /*            Hacking Tokens and Strings            */
  1094. /*                                    */
  1095. /* **************************************************************** */
  1096.  
  1097. /* Return the next token as a string pointer.  We cons the
  1098.    string. */
  1099. char *
  1100. read_token ()
  1101. {
  1102.   int i, character;
  1103.   char *result;
  1104.  
  1105.   /* Hack special case.  If the first character to be read is
  1106.      self-delimiting, then that is the command itself. */
  1107.  
  1108.   character = curchar ();
  1109.   if (self_delimiting (character))
  1110.     {
  1111.       input_text_offset++;
  1112.       result = savestring (" ");
  1113.       *result = character;
  1114.       return (result);
  1115.     }
  1116.  
  1117.   for (i = 0; ((input_text_offset != size_of_input_text)
  1118.            && (character = curchar ())
  1119.            && command_char (character));
  1120.        i++, input_text_offset++);
  1121.   result = xmalloc (i + 1);
  1122.   strncpy (result, &input_text[input_text_offset - i], i);
  1123.   result[i] = '\0';
  1124.   return (result);
  1125. }
  1126.  
  1127. /* Return TRUE if CHARACTER is self-delimiting. */
  1128. boolean
  1129. self_delimiting (character)
  1130.      int character;
  1131. {
  1132.   return (member (character, "{}:.@*'`,!?; \n"));
  1133. }
  1134.  
  1135. /* Clear whitespace from the front and end of string. */
  1136. canon_white (string)
  1137.      char *string;
  1138. {
  1139.   int len = strlen (string);
  1140.   int x;
  1141.  
  1142.   if (!len)
  1143.     return;
  1144.  
  1145.   for (x = 0; x < len; x++)
  1146.     {
  1147.       if (!whitespace (string[x]))
  1148.     {
  1149.       strcpy (string, string + x);
  1150.       break;
  1151.     }
  1152.     }
  1153.   len = strlen (string);
  1154.   if (len)
  1155.     len--;
  1156.   while (len > -1 && cr_or_whitespace (string[len]))
  1157.     len--;
  1158.   string[len + 1] = '\0';
  1159. }
  1160.  
  1161. /* Bash STRING, replacing all whitespace with just one space. */
  1162. fix_whitespace (string)
  1163.      char *string;
  1164. {
  1165.   char *temp = xmalloc (strlen (string) + 1);
  1166.   int string_index = 0;
  1167.   int temp_index = 0;
  1168.   int c;
  1169.  
  1170.   canon_white (string);
  1171.  
  1172.   while (string[string_index])
  1173.     {
  1174.       c = temp[temp_index++] = string[string_index++];
  1175.  
  1176.       if (c == ' ' || c == '\n' || c == '\t')
  1177.     {
  1178.       temp[temp_index - 1] = ' ';
  1179.       while ((c = string[string_index]) && (c == ' ' ||
  1180.                         c == '\t' ||
  1181.                         c == '\n'))
  1182.         string_index++;
  1183.     }
  1184.     }
  1185.   temp[temp_index] = '\0';
  1186.   strcpy (string, temp);
  1187.   free (temp);
  1188. }
  1189.  
  1190. /* Discard text until the desired string is found.  The string is
  1191.    included in the discarded text. */
  1192. discard_until (string)
  1193.      char *string;
  1194. {
  1195.   int temp = search_forward (string, input_text_offset);
  1196.  
  1197.   int tt = (temp < 0) ? size_of_input_text : temp + strlen (string);
  1198.   int from = input_text_offset;
  1199.  
  1200.   /* Find out what line we are on. */
  1201.   while (from != tt)
  1202.     if (input_text[from++] == '\n')
  1203.       line_number++;
  1204.  
  1205.   if (temp < 0)
  1206.     {
  1207.       input_text_offset = size_of_input_text - strlen (string);
  1208.  
  1209.       if (strcmp (string, "\n") != 0)
  1210.     {
  1211.       line_error ("Expected `%s'", string);
  1212.       return;
  1213.     }
  1214.     }
  1215.   else
  1216.     input_text_offset = temp;
  1217.  
  1218.   input_text_offset += strlen (string);
  1219. }
  1220.  
  1221. /* Read characters from the file until we are at MATCH.
  1222.    Place the characters read into STRING.
  1223.    On exit input_text_offset is after the match string.
  1224.    Return the length of STRING. */
  1225. get_until (match, string)
  1226.      char *match, **string;
  1227. {
  1228.   int len;
  1229.   int current_point = input_text_offset;
  1230.   int x = current_point;
  1231.   int new_point = search_forward (match, input_text_offset);
  1232.  
  1233.   if (new_point < 0)
  1234.     new_point = size_of_input_text;
  1235.   len = new_point - current_point;
  1236.  
  1237.   /* Keep track of which line number we are at. */
  1238.   while (x != new_point)
  1239.     if (input_text[x++] == '\n')
  1240.       line_number++;
  1241.  
  1242.   *string = xmalloc (len + 1);
  1243.  
  1244.   strncpy (*string, &input_text[current_point], len);
  1245.   (*string)[len] = '\0';
  1246.  
  1247.   /* Now leave input_text_offset in a consistent state. */
  1248.   input_text_offset = new_point + (strlen (match) - 1);
  1249.   if (input_text_offset > size_of_input_text)
  1250.     input_text_offset = size_of_input_text;
  1251. }
  1252.  
  1253. /* Read characters from the file until we are at MATCH or end of line.
  1254.    Place the characters read into STRING.  */
  1255. get_until_in_line (match, string)
  1256.      char *match, **string;
  1257. {
  1258.   int real_bottom = size_of_input_text;
  1259.   int temp = search_forward ("\n", input_text_offset);
  1260.   if (temp < 0)
  1261.     temp = size_of_input_text;
  1262.   size_of_input_text = temp;
  1263.   get_until (match, string);
  1264.   size_of_input_text = real_bottom;
  1265. }
  1266.  
  1267. get_rest_of_line (string)
  1268.      char **string;
  1269. {
  1270.   get_until ("\n", string);
  1271.   canon_white (*string);
  1272.   if (curchar () == '\n')
  1273.     {                /* as opposed to the end of the file... */
  1274.       line_number++;
  1275.       input_text_offset++;
  1276.     }
  1277. }
  1278.  
  1279. /* Read characters from the file until we are at MATCH or closing brace.
  1280.    Place the characters read into STRING.  */
  1281. get_until_in_braces (match, string)
  1282.      char *match, **string;
  1283. {
  1284.   int i, brace = 0;
  1285.   int match_len = strlen (match);
  1286.   char *temp;
  1287.  
  1288.   for (i = input_text_offset; i < size_of_input_text; i++)
  1289.     {
  1290.       if (input_text[i] == '{')
  1291.     brace++;
  1292.       if (input_text[i] == '}')
  1293.     brace--;
  1294.       if (input_text[i] == '\n')
  1295.     line_number++;
  1296.       if (brace < 0 ||
  1297.       (brace == 0 && strncmp (input_text + i, match, match_len) == 0))
  1298.     break;
  1299.     }
  1300.   match_len = i - input_text_offset;
  1301.   temp = xmalloc (2 + match_len);
  1302.   strncpy (temp, input_text + input_text_offset, match_len);
  1303.   temp[match_len] = '\0';
  1304.   input_text_offset = i;
  1305.   *string = temp;
  1306. }
  1307.  
  1308. /* **************************************************************** */
  1309. /*                                    */
  1310. /*            Converting the File                 */
  1311. /*                                    */
  1312. /* **************************************************************** */
  1313.  
  1314. /* Convert the file named by NAME.  The output is saved on the file
  1315.    named as the argument to the @setfilename command. */
  1316. convert (name)
  1317.      char *name;
  1318. {
  1319.   char *real_output_filename, *expand_filename (), *filename_part ();
  1320.   init_tag_table ();
  1321.   init_indices ();
  1322.   init_internals ();
  1323.   init_paragraph ();
  1324.  
  1325.   if (!find_and_load (name))
  1326.     {
  1327.       /* For some reason, the file couldn't be loaded.  Print a message
  1328.      to that affect, and split. */
  1329.       fs_error (name);
  1330.       return;
  1331.     }
  1332.   else
  1333.     input_filename = savestring (name);
  1334.  
  1335.   /* Search this file looking for the special string which starts conversion.
  1336.      Once found, we may truly begin. */
  1337.  
  1338.   input_text_offset = search_forward ("@setfilename", 0);
  1339.   if (input_text_offset < 0)
  1340.     {
  1341.       error ("No `@setfilename' found in `%s'", name);
  1342.       goto finished;
  1343.     }
  1344.   else
  1345.     input_text_offset += strlen ("@setfilename");
  1346.  
  1347.   get_until ("\n", &output_filename);    /* no braces expected. */
  1348.   canon_white (output_filename);
  1349.  
  1350.   printf ("Making info file `%s' from `%s'.\n", output_filename, name);
  1351.   real_output_filename = expand_filename (output_filename, name);
  1352.   output_stream = fopen (real_output_filename, "w");
  1353.   if (output_stream == NULL)
  1354.     {
  1355.       fs_error (real_output_filename);
  1356.       goto finished;
  1357.     }
  1358.  
  1359.   /* Make the displayable filename from output_filename.  Only the root
  1360.      portion of the filename need be displayed. */
  1361.   pretty_output_filename = filename_part (output_filename);
  1362.  
  1363.   /* For this file only, count the number of newlines from the top of
  1364.      the file to here.  This way, we keep track of line numbers for
  1365.      error reporting.  Line_number starts at 1, since the user isn't
  1366.      zero-based. */
  1367.   {
  1368.     int temp = 0;
  1369.     line_number = 1;
  1370.     while (temp != input_text_offset)
  1371.       if (input_text[temp++] == '\n')
  1372.     line_number++;
  1373.   }
  1374.  
  1375.   add_word_args ("Info file %s, produced by Makeinfo, -*- Text -*-\n\
  1376. from input file %s.\n", output_filename, input_filename);
  1377.   close_paragraph ();
  1378.  
  1379.   reader_loop ();
  1380.  
  1381. finished:
  1382.   close_paragraph ();
  1383.   flush_file_stack ();
  1384.   if (output_stream != NULL)
  1385.     {
  1386.       output_pending_notes ();
  1387.       free_pending_notes ();
  1388.       if (tag_table != NULL)
  1389.     {
  1390.       tag_table = (TAG_ENTRY *) reverse_list (tag_table);
  1391.       write_tag_table ();
  1392.     }
  1393.  
  1394.       fclose (output_stream);
  1395.  
  1396.       /* If validating, then validate the entire file right now. */
  1397.       if (validating)
  1398.     validate_file (real_output_filename, tag_table);
  1399.  
  1400.       /* This used to test  && !errors_printed.
  1401.      But some files might have legit warnings.  So split anyway.  */
  1402.       if (splitting)
  1403.     split_file (real_output_filename, 0);
  1404.     }
  1405. }
  1406.  
  1407. free_and_clear (pointer)
  1408.      char **pointer;
  1409. {
  1410.   if ((*pointer) != (char *) NULL)
  1411.     {
  1412.       free (*pointer);
  1413.       *pointer = (char *) NULL;
  1414.     }
  1415. }
  1416.  
  1417.  /* Initialize some state. */
  1418. init_internals ()
  1419. {
  1420.   free_and_clear (¤t_node);
  1421.   free_and_clear (&output_filename);
  1422.   free_and_clear (&command);
  1423.   free_and_clear (&input_filename);
  1424.   free_node_references ();
  1425.   init_insertion_stack ();
  1426.   init_brace_stack ();
  1427.   command_index = 0;
  1428.   in_menu = 0;
  1429. }
  1430.  
  1431. init_paragraph ()
  1432. {
  1433.   free_and_clear (&output_paragraph);
  1434.   output_paragraph = xmalloc (paragraph_buffer_len);
  1435.   output_position = 0;
  1436.   output_paragraph[0] = '\0';
  1437.   output_paragraph_offset = 0;
  1438.   output_column = 0;
  1439.   paragraph_is_open = false;
  1440.   current_indent = 0;
  1441. }
  1442.  
  1443. /* Okay, we are ready to start the conversion.  Call the reader on
  1444.    some text, and fill the text as it is output.  Handle commands by
  1445.    remembering things like open braces and the current file position on a
  1446.    stack, and when the corresponding close brace is found, you can call
  1447.    the function with the proper arguments. */
  1448. reader_loop ()
  1449. {
  1450.   int character;
  1451.   boolean done = false;
  1452.   int dash_count = 0;
  1453.  
  1454.   while (!done)
  1455.     {
  1456.       if (input_text_offset >= size_of_input_text)
  1457.     {
  1458.       if (filestack)
  1459.         {
  1460.           free (input_filename);
  1461.           free (input_text);
  1462.           popfile ();
  1463.         }
  1464.       else
  1465.         break;
  1466.     }
  1467.       character = curchar ();
  1468.  
  1469.       if (!in_fixed_width_font &&
  1470.       (character == '\'' || character == '`') &&
  1471.       input_text[input_text_offset + 1] == character)
  1472.     {
  1473.       input_text_offset++;
  1474.       character = '"';
  1475.     }
  1476.  
  1477.       if (character == '-')
  1478.     {
  1479.       dash_count++;
  1480.       if (dash_count == 3 && !in_fixed_width_font)
  1481.         {
  1482.           input_text_offset++;
  1483.           continue;
  1484.         }
  1485.     }
  1486.       else
  1487.     {
  1488.       dash_count = 0;
  1489.     }
  1490.  
  1491.       if (character == '\n')
  1492.     {
  1493.       line_number++;
  1494.       if (in_menu && input_text_offset + 1 < size_of_input_text)
  1495.         {
  1496.           glean_node_from_menu ();
  1497.         }
  1498.  
  1499.       /* If the following line is all whitespace, advance to the carriage
  1500.          return on it. */
  1501.       {
  1502.         register int i = input_text_offset + 1;
  1503.         
  1504.         while (i < size_of_input_text && whitespace (input_text[i]))
  1505.           i++;
  1506.         
  1507.             if (i == size_of_input_text || input_text[i] == '\n')
  1508.           input_text_offset = i - 1;
  1509.       }
  1510.     }
  1511.       
  1512.       switch (character)
  1513.     {
  1514.     case COMMAND_PREFIX:
  1515.       read_command ();
  1516.       if (strcmp (command, "bye") == 0)
  1517.         {
  1518.           done = true;
  1519.           continue;
  1520.         }
  1521.       break;
  1522.  
  1523.     case '{':
  1524.  
  1525.       /* Special case.  I'm not supposed to see this character by itself.
  1526.          If I do, it means there is a syntax error in the input text.
  1527.          Report the error here, but remember this brace on the stack so
  1528.          you can ignore its partner. */
  1529.  
  1530.       line_error ("Misplaced `{'");
  1531.       remember_brace (misplaced_brace);
  1532.  
  1533.       /* Don't advance input_text_offset since this happens in
  1534.          remember_brace ().
  1535.          input_text_offset++;
  1536.            */
  1537.       break;
  1538.  
  1539.     case '}':
  1540.       pop_and_call_brace ();
  1541.       input_text_offset++;
  1542.       break;
  1543.  
  1544.     default:
  1545.       add_char (character);
  1546.       input_text_offset++;
  1547.     }
  1548.     }
  1549. }
  1550.  
  1551. /* Find the command corresponding to STRING.  If the command
  1552.    is found, return a pointer to the data structure.  Otherwise
  1553.    return (-1). */
  1554. COMMAND *
  1555. get_command_entry (string)
  1556.      char *string;
  1557. {
  1558.   register int i;
  1559.  
  1560.   for (i = 0; CommandTable[i].name; i++)
  1561.     if (strcmp (CommandTable[i].name, string) == 0)
  1562.       return (&CommandTable[i]);
  1563.  
  1564.   /* This command is not in our predefined command table.  Perhaps
  1565.      it is a user defined command. */
  1566.   for (i = 0; i < user_command_array_len; i++)
  1567.     if (user_command_array[i] &&
  1568.     (strcmp (user_command_array[i]->name, string) == 0))
  1569.       return (user_command_array[i]);
  1570.  
  1571.   /* Nope, we never heard of this command. */
  1572.   return ((COMMAND *) - 1);
  1573. }
  1574.  
  1575. /* input_text_offset is right at the command prefix character.
  1576.    Read the next token to determine what to do. */
  1577. read_command ()
  1578. {
  1579.   COMMAND *entry;
  1580.   input_text_offset++;
  1581.   free_and_clear (&command);
  1582.   command = read_token ();
  1583.  
  1584.   entry = get_command_entry (command);
  1585.   if ((int) entry < 0)
  1586.     {
  1587.       line_error ("Unknown info command `%s'", command);
  1588.       return;
  1589.     }
  1590.  
  1591.   if (entry->argument_in_braces)
  1592.     remember_brace (entry->proc);
  1593.  
  1594.   (*(entry->proc)) (START);
  1595. }
  1596.  
  1597. /* Return the string which invokes PROC; a pointer to a function. */
  1598. char *
  1599. find_proc_name (proc)
  1600.      FUNCTION *proc;
  1601. {
  1602.   register int i;
  1603.  
  1604.   for (i = 0; CommandTable[i].name; i++)
  1605.     if (proc == CommandTable[i].proc)
  1606.       return (CommandTable[i].name);
  1607.   return ("NO_NAME!");
  1608. }
  1609.  
  1610. init_brace_stack ()
  1611. {
  1612.   brace_stack = (BRACE_ELEMENT *) NULL;
  1613. }
  1614.  
  1615. remember_brace (proc)
  1616.      FUNCTION *proc;
  1617. {
  1618.   if (curchar () != '{')
  1619.     line_error ("@%s expected `{..}'", command);
  1620.   else
  1621.     input_text_offset++;
  1622.   remember_brace_1 (proc, output_paragraph_offset);
  1623. }
  1624.  
  1625. /* Remember the current output position here.  Save PROC
  1626.    along with it so you can call it later. */
  1627. remember_brace_1 (proc, position)
  1628.      FUNCTION *proc;
  1629.      int position;
  1630. {
  1631.   BRACE_ELEMENT *new = (BRACE_ELEMENT *) xmalloc (sizeof (BRACE_ELEMENT));
  1632.   new->next = brace_stack;
  1633.   new->proc = proc;
  1634.   new->pos = position;
  1635.   new->line = line_number;
  1636.   brace_stack = new;
  1637. }
  1638.  
  1639. /* Pop the top of the brace stack, and call the associated function
  1640.    with the args END and POS. */
  1641. pop_and_call_brace ()
  1642. {
  1643.   BRACE_ELEMENT *temp;
  1644.   FUNCTION *proc;
  1645.   int pos;
  1646.  
  1647.   if (brace_stack == (BRACE_ELEMENT *) NULL)
  1648.     return (line_error ("Unmatched close bracket"));
  1649.  
  1650.   pos = brace_stack->pos;
  1651.   proc = brace_stack->proc;
  1652.   temp = brace_stack->next;
  1653.   free (brace_stack);
  1654.   brace_stack = temp;
  1655.  
  1656.   return ((*proc) (END, pos, output_paragraph_offset));
  1657. }
  1658.  
  1659. /* You call discard_braces () when you shouldn't have any braces on the stack.
  1660.    I used to think that this happens for commands that don't take arguments
  1661.    in braces, but that was wrong because of things like @code{foo @@}.  So now
  1662.    I only detect it at the beginning of nodes. */
  1663. discard_braces ()
  1664. {
  1665.   int temp_line_number = line_number;
  1666.   char *proc_name;
  1667.  
  1668.   if (!brace_stack)
  1669.     return;
  1670.  
  1671.   while (brace_stack)
  1672.     {
  1673.       line_number = brace_stack->line;
  1674.       proc_name = find_proc_name (brace_stack->proc);
  1675.       line_error ("@%s missing close brace", proc_name);
  1676.       line_number = temp_line_number;
  1677.       pop_and_call_brace ();
  1678.     }
  1679. }
  1680.  
  1681. get_char_len (character)
  1682.      int character;
  1683. {
  1684.   /* Return the printed length of the character. */
  1685.   int len;
  1686.  
  1687.   switch (character)
  1688.     {
  1689.     case '\t':
  1690.       len = (output_column + 8) & 0xf7;
  1691.       if (len > fill_column)
  1692.     len = fill_column - output_column;
  1693.       else
  1694.     len = len - output_column;
  1695.       break;
  1696.  
  1697.     case '\n':
  1698.       len = fill_column - output_column;
  1699.       break;
  1700.  
  1701.     default:
  1702.       if (character < ' ')
  1703.     len = 2;
  1704.       else
  1705.     len = 1;
  1706.     }
  1707.   return (len);
  1708. }
  1709.  
  1710. add_word_args (format, arg1, arg2, arg3, arg4, arg5)
  1711.      char *format;
  1712. {
  1713.   char buffer[1000];
  1714.   sprintf (buffer, format, arg1, arg2, arg3, arg4, arg5);
  1715.   add_word (buffer);
  1716. }
  1717.  
  1718. /* Add STRING to output_paragraph. */
  1719. add_word (string)
  1720.      char *string;
  1721. {
  1722.   while (*string)
  1723.     add_char (*string++);
  1724. }
  1725.  
  1726. boolean last_char_was_newline = true;
  1727. int last_inserted_character = 0;
  1728.  
  1729. /* Add the character to the current paragraph.  If filling_enabled is
  1730.    true, then do filling as well. */
  1731. add_char (character)
  1732.      int character;
  1733. {
  1734.   extern int must_start_paragraph;
  1735.  
  1736.   /* If we are adding a character now, then we don't have to
  1737.      ignore close_paragraph () calls any more. */
  1738.   if (must_start_paragraph)
  1739.     {
  1740.       must_start_paragraph = 0;
  1741.       if (current_indent > output_column)
  1742.     {
  1743.       indent (current_indent - output_column);
  1744.       output_column = current_indent;
  1745.     }
  1746.     }
  1747.  
  1748.   if (non_splitting_words && member (character, " \t\n"))
  1749.     character = ' ' | 0x80;
  1750.  
  1751.   switch (character)
  1752.     {
  1753.  
  1754.     case '\n':
  1755.       if (!filling_enabled)
  1756.     {
  1757.       insert ('\n');
  1758.  
  1759.       /* Should I be flushing output here? * /
  1760.           flush_output (); */
  1761.  
  1762.       output_column = 0;
  1763.       if (!no_indent)
  1764.         indent (output_column = current_indent);
  1765.       break;
  1766.     }
  1767.       else
  1768.     {
  1769.       if (sentence_ender (last_inserted_character))
  1770.         {
  1771.           insert (' ');
  1772.           output_column++;
  1773.           last_inserted_character = character;
  1774.         }
  1775.     }
  1776.  
  1777.       if (last_char_was_newline)
  1778.     {
  1779.       close_paragraph ();
  1780.       pending_indent = 0;
  1781.     }
  1782.       else
  1783.     {
  1784.       last_char_was_newline = true;
  1785.       insert (' ');
  1786.       output_column++;
  1787.     }
  1788.       break;
  1789.  
  1790.     default:
  1791.       {
  1792.     int len = get_char_len (character);
  1793.     if ((character == ' ') && (last_char_was_newline))
  1794.       {
  1795.         if (!paragraph_is_open)
  1796.           {
  1797.         pending_indent++;
  1798.         return;
  1799.           }
  1800.       }
  1801.     if (!paragraph_is_open)
  1802.       {
  1803.         start_paragraph ();
  1804.  
  1805.         /* If the paragraph is supposed to be indented a certain way,
  1806.            then discard all of the pending whitespace.  Otherwise, we
  1807.            let the whitespace stay. */
  1808.         if (!paragraph_start_indent)
  1809.           indent (pending_indent);
  1810.         pending_indent = 0;
  1811.       }
  1812.     if ((output_column += len) >= fill_column)
  1813.       {
  1814.         if (filling_enabled)
  1815.           {
  1816.         int temp = output_paragraph_offset - 1;
  1817.         while (temp > 0 && output_paragraph[--temp] != '\n')
  1818.           {
  1819.             if (output_paragraph[temp] == ' ')
  1820.               {
  1821.             output_paragraph[temp++] = '\n';
  1822.  
  1823.             /* We have correctly broken the line where we want
  1824.                to.  What we don't want is spaces following where
  1825.                we have decided to break the line.  We get rid of
  1826.                them. */
  1827.             {
  1828.               int t1 = temp;
  1829.               while (t1 < output_paragraph_offset
  1830.                  && whitespace (output_paragraph[t1]))
  1831.                 t1++;
  1832.  
  1833.               if (t1 != temp)
  1834.                 {
  1835.                   strncpy (&output_paragraph[temp],
  1836.                        &output_paragraph[t1],
  1837.                        (output_paragraph_offset - t1));
  1838.                   output_paragraph_offset -= (t1 - temp);
  1839.                 }
  1840.             }
  1841.  
  1842.             /* Filled, but now indent if that is right. */
  1843.             if (indented_fill && current_indent)
  1844.               {
  1845.                 int buffer_len = ((output_paragraph_offset - temp)
  1846.                           + current_indent);
  1847.                 char *temp_buffer = xmalloc (buffer_len);
  1848.                 int indentation = 0;
  1849.  
  1850.                 /* We have to shift any markers that are in
  1851.                    front of the wrap point. */
  1852.                 {
  1853.                   register BRACE_ELEMENT *stack = brace_stack;
  1854.  
  1855.                   while (stack)
  1856.                 {
  1857.                   if (stack->pos > temp)
  1858.                     stack->pos += current_indent;
  1859.                   stack = stack->next;
  1860.                 }
  1861.                 }
  1862.  
  1863.                 while (indentation != current_indent)
  1864.                   temp_buffer[indentation++] = ' ';
  1865.  
  1866.                 strncpy (&temp_buffer[current_indent],
  1867.                      &output_paragraph[temp],
  1868.                      buffer_len - current_indent);
  1869.  
  1870.                 if (output_paragraph_offset + buffer_len
  1871.                 >= paragraph_buffer_len)
  1872.                   {
  1873.                 char *tt =
  1874.                 (char *) xrealloc (output_paragraph,
  1875.                       (paragraph_buffer_len += buffer_len));
  1876.                 output_paragraph = tt;
  1877.                   }
  1878.                 strncpy (&output_paragraph[temp], temp_buffer, buffer_len);
  1879.                 output_paragraph_offset += current_indent;
  1880.                 free (temp_buffer);
  1881.               }
  1882.             output_column = 0;
  1883.             while (temp != output_paragraph_offset)
  1884.               output_column += get_char_len (output_paragraph[temp++]);
  1885.             output_column += len;
  1886.             break;
  1887.               }
  1888.           }
  1889.           }
  1890.       }
  1891.     insert (character);
  1892.     last_char_was_newline = false;
  1893.     last_inserted_character = character;
  1894.       }
  1895.     }
  1896. }
  1897.  
  1898. /* Insert CHARACTER into OUTPUT_PARAGRAPH. */
  1899. insert (character)
  1900.      int character;
  1901. {
  1902.   output_paragraph[output_paragraph_offset++] = character;
  1903.   if (output_paragraph_offset == paragraph_buffer_len)
  1904.     {
  1905.       output_paragraph =
  1906.     (char *) xrealloc (output_paragraph,
  1907.               (paragraph_buffer_len += 100));
  1908.     }
  1909. }
  1910.  
  1911. /* Remove upto COUNT characters of whitespace from the
  1912.    the current output line.  If COUNT is less than zero,
  1913.    then remove until none left. */
  1914. kill_self_indent (count)
  1915.      int count;
  1916. {
  1917.   /* Handle infinite case first. */
  1918.   if (count < 0)
  1919.     {
  1920.       output_column = 0;
  1921.       while (output_paragraph_offset)
  1922.     {
  1923.       if (whitespace (output_paragraph[output_paragraph_offset - 1]))
  1924.         output_paragraph_offset--;
  1925.       else
  1926.         break;
  1927.     }
  1928.     }
  1929.   else
  1930.     {
  1931.       while (output_paragraph_offset && count--)
  1932.     if (whitespace (output_paragraph[output_paragraph_offset - 1]))
  1933.       output_paragraph_offset--;
  1934.     else
  1935.       break;
  1936.     }
  1937. }
  1938.  
  1939. flush_output ()
  1940. {
  1941.   register int i;
  1942.  
  1943.   if (!output_paragraph_offset)
  1944.     return;
  1945.   for (i = 0; i < output_paragraph_offset; i++)
  1946.     output_paragraph[i] &= 0x7f;
  1947.  
  1948.   fwrite (output_paragraph, 1, output_paragraph_offset, output_stream);
  1949.   output_position += output_paragraph_offset;
  1950.   output_paragraph_offset = 0;
  1951. }
  1952.  
  1953. /* How to close a paragraph controlling the number of lines between
  1954.    this one and the last one. */
  1955.  
  1956. /* Paragraph spacing is controlled by this variable.  It is the number of
  1957.    blank lines that you wish to appear between paragraphs.  A value of
  1958.    1 creates a single blank line between paragraphs. */
  1959. int paragraph_spacing = 1;
  1960.  
  1961.  
  1962. /* Close the current paragraph, leaving no blank lines between them. */
  1963. close_single_paragraph ()
  1964. {
  1965.   close_paragraph_with_lines (0);
  1966. }
  1967.  
  1968. close_paragraph_with_lines (lines)
  1969.      int lines;
  1970. {
  1971.   int old_spacing = paragraph_spacing;
  1972.   paragraph_spacing = lines;
  1973.   close_paragraph ();
  1974.   paragraph_spacing = old_spacing;
  1975. }
  1976.  
  1977. /* Non-zero means that start_paragraph () MUST be called before we pay
  1978.    any attention to close_paragraph () calls. */
  1979. int must_start_paragraph = 0;
  1980.  
  1981. /* Close the currently open paragraph. */
  1982. close_paragraph ()
  1983. {
  1984.   if (paragraph_is_open && !must_start_paragraph)
  1985.     {
  1986.       /* Gobble up blank lines that are extra... */
  1987.       register int tindex = output_paragraph_offset;
  1988.       register int c;
  1989.       while (tindex &&
  1990.          ((c = output_paragraph[tindex - 1]) == ' ' || c == '\n'))
  1991.     output_paragraph[--tindex] = '\n';
  1992.  
  1993.       output_paragraph_offset = tindex;
  1994.  
  1995.       insert ('\n');
  1996.       {
  1997.     register int i;
  1998.     for (i = 0; i < paragraph_spacing; i++)
  1999.       insert ('\n');
  2000.       }
  2001.       flush_output ();
  2002.       paragraph_is_open = false;
  2003.       no_indent = false;
  2004.     }
  2005.   last_char_was_newline = true;
  2006. }
  2007.  
  2008. /* Begin a new paragraph. */
  2009. start_paragraph ()
  2010. {
  2011.   close_paragraph ();        /* First close existing one. */
  2012.  
  2013.   paragraph_is_open = true;
  2014.  
  2015.   if (!must_start_paragraph)
  2016.     {
  2017.       output_column = 0;
  2018.  
  2019.       /* If doing indentation, then insert the appropriate amount. */
  2020.       if (!no_indent)
  2021.     {
  2022.       if (inhibit_paragraph_indentation || paragraph_start_indent < 0)
  2023.         output_column = current_indent;
  2024.       else
  2025.         output_column = current_indent + paragraph_start_indent;
  2026.  
  2027.       indent (output_column);
  2028.     }
  2029.     }
  2030.   else
  2031.     must_start_paragraph = 0;
  2032. }
  2033.  
  2034. /* Insert the indentation specified by AMOUNT. */
  2035. indent (amount)
  2036.      int amount;
  2037. {
  2038.   while (--amount >= 0)
  2039.     insert (' ');
  2040. }
  2041.  
  2042. /* Search forward for STRING in input_text.
  2043.    FROM says where where to start. */
  2044. search_forward (string, from)
  2045.      char *string;
  2046.      int from;
  2047. {
  2048.   int len = strlen (string);
  2049.  
  2050.   while (from < size_of_input_text)
  2051.     {
  2052.       if (strnicmp (input_text + from, string, len) == 0)
  2053.     return (from);
  2054.       from++;
  2055.     }
  2056.   return (-1);
  2057. }
  2058.  
  2059. /* Whoops, Unix doesn't have stricmp, or strnicmp. */
  2060.  
  2061. /* Case independent string compare. */
  2062. stricmp (string1, string2)
  2063.      char *string1, *string2;
  2064. {
  2065.   char ch1, ch2;
  2066.  
  2067.   for (;;)
  2068.     {
  2069.       ch1 = *string1++;
  2070.       ch2 = *string2++;
  2071.       if (!(ch1 | ch2))
  2072.     return (0);
  2073.  
  2074.       ch1 = coerce_to_upper (ch1);
  2075.       ch2 = coerce_to_upper (ch2);
  2076.  
  2077.       if (ch1 != ch2)
  2078.     return (1);
  2079.     }
  2080. }
  2081.  
  2082. /* Compare at most COUNT characters from string1 to string2.  Case
  2083.    doesn't matter. */
  2084. strnicmp (string1, string2, count)
  2085.      char *string1, *string2;
  2086. {
  2087.   char ch1, ch2;
  2088.  
  2089.   while (count)
  2090.     {
  2091.       ch1 = *string1++;
  2092.       ch2 = *string2++;
  2093.       if (coerce_to_upper (ch1) == coerce_to_upper (ch2))
  2094.     count--;
  2095.       else
  2096.     break;
  2097.     }
  2098.   return (count);
  2099. }
  2100.  
  2101. enum insertion_type
  2102. {
  2103.   menu, quotation, lisp, example, smallexample, display,
  2104.   itemize, format, enumerate, table, group, ifinfo,
  2105.   defun, defvar, defopt, deffn, defspec, defmac,
  2106.   bad_type
  2107. };
  2108.  
  2109. char *insertion_type_names[] = {
  2110.       "menu", "quotation", "lisp", "example", "smallexample", "display",
  2111.       "itemize", "format", "enumerate", "table", "group", "ifinfo",
  2112.       "defun", "defvar", "defopt", "deffn", "defspec", "defmac",
  2113. };
  2114.  
  2115. int insertion_level = 0;
  2116. typedef struct istack_elt
  2117. {
  2118.   struct istack_elt *next;
  2119.   char *item_function;
  2120.   int line_number;
  2121.   int filling_enabled;
  2122.   int indented_fill;
  2123.   enum insertion_type insertion;
  2124.   int inhibited;
  2125. } INSERTION_ELT;
  2126.  
  2127. INSERTION_ELT *insertion_stack = (INSERTION_ELT *) NULL;
  2128.  
  2129. init_insertion_stack ()
  2130. {
  2131.   insertion_stack = (INSERTION_ELT *) NULL;
  2132. }
  2133.  
  2134. /* Return the type of the current insertion. */
  2135. enum insertion_type
  2136. current_insertion_type ()
  2137. {
  2138.   if (!insertion_level)
  2139.     return (bad_type);
  2140.   else
  2141.     return (insertion_stack->insertion);
  2142. }
  2143.  
  2144. /* Return a pointer to the string which is the function
  2145.    to wrap around items. */
  2146. char *
  2147. current_item_function ()
  2148. {
  2149.   if (!insertion_level)
  2150.     return ((char *) NULL);
  2151.   else
  2152.     return (insertion_stack->item_function);
  2153. }
  2154.  
  2155. char *
  2156. get_item_function ()
  2157. {
  2158.   char *item_function;
  2159.   get_until ("\n", &item_function);
  2160.   canon_white (item_function);
  2161.   return (item_function);
  2162. }
  2163.  
  2164.  /* Push the state of the current insertion on the stack. */
  2165. push_insertion (type, item_function)
  2166.      enum insertion_type type;
  2167.      char *item_function;
  2168. {
  2169.   INSERTION_ELT *new = (INSERTION_ELT *) xmalloc (sizeof (INSERTION_ELT));
  2170.  
  2171.   new->item_function = item_function;
  2172.   new->filling_enabled = filling_enabled;
  2173.   new->indented_fill = indented_fill;
  2174.   new->insertion = type;
  2175.   new->line_number = line_number;
  2176.   new->inhibited = inhibit_paragraph_indentation;
  2177.   new->next = insertion_stack;
  2178.   insertion_stack = new;
  2179.   insertion_level++;
  2180. }
  2181.  
  2182.  /* Pop the value on top of the insertion stack into the
  2183.     global variables. */
  2184. pop_insertion ()
  2185. {
  2186.   INSERTION_ELT *temp = insertion_stack;
  2187.   if (temp == (INSERTION_ELT *) NULL)
  2188.     return;
  2189.   inhibit_paragraph_indentation = temp->inhibited;
  2190.   filling_enabled = insertion_stack->filling_enabled;
  2191.   indented_fill = insertion_stack->indented_fill;
  2192.   free_and_clear (&(temp->item_function));
  2193.   insertion_stack = insertion_stack->next;
  2194.   free (temp);
  2195.   insertion_level--;
  2196. }
  2197.  
  2198.  /* Return a pointer to the print name of this
  2199.     enumerated type. */
  2200. char *
  2201. insertion_type_pname (type)
  2202.      enum insertion_type type;
  2203. {
  2204.   if ((int) type < (int) bad_type)
  2205.     return (insertion_type_names[(int) type]);
  2206.   else
  2207.     return ("Broken-Type in insertion_type_pname");
  2208. }
  2209.  
  2210. /* Return the insertion_type associated with NAME.
  2211.    If the type is not one of the known ones, return BAD_TYPE. */
  2212. enum insertion_type
  2213. find_type_from_name (name)
  2214.      char *name;
  2215. {
  2216.   int index = 0;
  2217.   while (index < (int) bad_type)
  2218.     {
  2219.       if (stricmp (name, insertion_type_names[index]) == 0)
  2220.     return (enum insertion_type) index;
  2221.       index++;
  2222.     }
  2223.   return (bad_type);
  2224. }
  2225.  
  2226. do_nothing ()
  2227. {
  2228. }
  2229.  
  2230. int
  2231. defun_insertion (type)
  2232.      enum insertion_type type;
  2233. {
  2234.   return (type == defun ||
  2235.       type == defvar ||
  2236.       type == defopt ||
  2237.       type == deffn ||
  2238.       type == defspec ||
  2239.       type == defmac);
  2240. }
  2241.  
  2242. /* Non-zero means that we are currently hacking the insides of an
  2243.    insertion which would use a fixed width font. */
  2244. int in_fixed_width_font = 0;
  2245.  
  2246. /* This is where the work for all the "insertion" style
  2247.    commands is done.  A huge switch statement handles the
  2248.    various setups, and generic code is on both sides. */
  2249. begin_insertion (type)
  2250.      enum insertion_type type;
  2251. {
  2252.   int no_discard = 0;
  2253.  
  2254.   close_paragraph ();
  2255.  
  2256.   if (defun_insertion (type))
  2257.     {
  2258.       push_insertion (type, savestring (""));
  2259.       no_discard = 1;
  2260.     }
  2261.   else
  2262.     push_insertion (type, get_item_function ());
  2263.  
  2264.   filling_enabled = false;    /* the general case for insertions. */
  2265.   inhibit_paragraph_indentation = 1;
  2266.   no_indent = false;
  2267.  
  2268.   switch (type)
  2269.     {
  2270.     case menu:
  2271.       add_word ("* Menu:\n");
  2272.       in_menu++;
  2273.       discard_until ("\n");
  2274.       input_text_offset--;
  2275.       /* discard_until () has already counted the newline.  Discount it. */
  2276.       line_number--;
  2277.       return;
  2278.  
  2279.       /* I think @quotation is meant to do filling.
  2280.      If you don't want filling, then use @example. */
  2281.     case quotation:
  2282.       last_char_was_newline = 0;
  2283.       indented_fill = filling_enabled = true;
  2284.       current_indent += default_indentation_increment;
  2285.       break;
  2286.  
  2287.       /* Just like @example, but no indentation. */
  2288.     case format:
  2289.       in_fixed_width_font++;
  2290.       break;
  2291.  
  2292.     case display:
  2293.     case example:
  2294.     case smallexample:
  2295.     case lisp:
  2296.       last_char_was_newline = 0;
  2297.       current_indent += default_indentation_increment;
  2298.       in_fixed_width_font++;
  2299.       break;
  2300.  
  2301.     case table:
  2302.     case itemize:
  2303.       current_indent += default_indentation_increment;
  2304.       filling_enabled = indented_fill = true;
  2305.  
  2306.       /* Make things work for losers who forget the itemize syntax. */
  2307.       if (type == itemize)
  2308.     {
  2309.       if (!(*insertion_stack->item_function))
  2310.         {
  2311.           free (insertion_stack->item_function);
  2312.           insertion_stack->item_function = savestring ("*");
  2313.         }
  2314.     }
  2315.       break;
  2316.  
  2317.     case enumerate:
  2318.       inhibit_paragraph_indentation = 0;
  2319.       current_indent += default_indentation_increment;
  2320.       start_numbering (1);
  2321.       filling_enabled = indented_fill = true;
  2322.       break;
  2323.  
  2324.     case group:
  2325.       inhibit_paragraph_indentation = 0;
  2326.       break;
  2327.  
  2328.     case ifinfo:
  2329.       /* Undo whatever we just did.  This is a no-op. */
  2330.       inhibit_paragraph_indentation = 0;
  2331.       filling_enabled = insertion_stack->filling_enabled;
  2332.       indented_fill = insertion_stack->indented_fill;
  2333.       break;
  2334.  
  2335.     case defun:
  2336.     case defvar:
  2337.     case defopt:
  2338.     case deffn:
  2339.     case defspec:
  2340.     case defmac:
  2341.       filling_enabled = indented_fill = true;
  2342.       current_indent += default_indentation_increment;
  2343.       break;
  2344.     }
  2345.   if (!no_discard)
  2346.     discard_until ("\n");
  2347. }
  2348.  
  2349. /* Try to end the quotation with the specified type.
  2350.    Like begin_insertion (), this uses a giant switch statement as
  2351.    well.  A big difference is that you *must* pass a valid type to
  2352.    this function, and a value of bad_type gets translated to match
  2353.    the value currently on top of the stack.  If, however, the value
  2354.    passed is a valid type, and it doesn't match the top of the
  2355.    stack, then we produce an error.  Should fix this, somewhat
  2356.    unclean. */
  2357. end_insertion (type)
  2358.      enum insertion_type type;
  2359. {
  2360.   enum insertion_type temp_type;
  2361.  
  2362.   if (!insertion_level)
  2363.     return;
  2364.  
  2365.   temp_type = current_insertion_type ();
  2366.   if (type == bad_type)
  2367.     type = temp_type;
  2368.  
  2369.   if (type != temp_type)
  2370.     {
  2371.       line_error ("Expected `%s', but saw `%s'.  Token unread",
  2372.          insertion_type_pname (temp_type), insertion_type_pname (type));
  2373.       return;
  2374.     }
  2375.   pop_insertion ();
  2376.  
  2377.   switch (type)
  2378.     {
  2379.  
  2380.     case menu:
  2381.       in_menu--;        /* no longer hacking menus. */
  2382.       break;
  2383.  
  2384.     case enumerate:
  2385.       stop_numbering ();
  2386.       current_indent -= default_indentation_increment;
  2387.       break;
  2388.  
  2389.     case group:
  2390.     case ifinfo:
  2391.       break;
  2392.  
  2393.     case format:
  2394.     case example:
  2395.     case smallexample:
  2396.     case display:
  2397.     case lisp:
  2398.       in_fixed_width_font--;
  2399.       current_indent -= default_indentation_increment;
  2400.       break;
  2401.  
  2402.     default:
  2403.       current_indent -= default_indentation_increment;
  2404.       break;
  2405.     }
  2406.   close_paragraph ();
  2407. }
  2408.  
  2409. /* Insertions cannot cross certain boundaries, such as node beginnings.  In
  2410.    code that creates such boundaries, you should call discard_insertions ()
  2411.    before doing anything else.  It prints the errors for you, and cleans up
  2412.    the insertion stack. */
  2413. discard_insertions ()
  2414. {
  2415.   int real_line_number = line_number;
  2416.   while (insertion_stack)
  2417.     {
  2418.       if (insertion_stack->insertion == ifinfo)
  2419.     break;
  2420.       else
  2421.     {
  2422.       char *offender = (char *) insertion_type_pname (insertion_stack->insertion);
  2423.  
  2424.       line_number = insertion_stack->line_number;
  2425.       line_error ("This `%s' doesn't have a matching `%cend %s'", offender,
  2426.               COMMAND_PREFIX, offender);
  2427.       pop_insertion ();
  2428.     }
  2429.     }
  2430.   line_number = real_line_number;
  2431. }
  2432.  
  2433. /* MAX_NS is the maximum nesting level for enumerations.  I picked 100
  2434.    which seemed reasonable.  This doesn't control the number of items,
  2435.    just the number of nested lists. */
  2436. #define max_ns 100
  2437. int number_stack[max_ns];
  2438. int number_offset = 0;
  2439. int the_current_number = 0;
  2440.  
  2441. start_numbering (at_number)
  2442. {
  2443.   if (number_offset + 1 == max_ns)
  2444.     {
  2445.       line_error ("Enumeration stack overflow");
  2446.       return;
  2447.     }
  2448.   number_stack[number_offset++] = the_current_number;
  2449.   the_current_number = at_number;
  2450. }
  2451.  
  2452. stop_numbering ()
  2453. {
  2454.   the_current_number = number_stack[--number_offset];
  2455.   if (number_offset < 0)
  2456.     number_offset = 0;
  2457. }
  2458.  
  2459.  /* Place a number into the output stream. */
  2460. number_item ()
  2461. {
  2462.   char temp[10];
  2463.   sprintf (temp, "%d. ", the_current_number);
  2464.   indent (output_column += (current_indent - strlen (temp)));
  2465.   add_word (temp);
  2466.   the_current_number++;
  2467. }
  2468.  
  2469. /* The actual commands themselves. */
  2470.  
  2471. /* Commands which insert themselves. */
  2472. insert_self ()
  2473. {
  2474.   add_word (command);
  2475. }
  2476.  
  2477. /* Force line break */
  2478. cm_asterisk ()
  2479. {
  2480.   /* Force a line break in the output. */
  2481.   insert ('\n');
  2482.   indent (output_column = current_indent);
  2483. }
  2484.  
  2485. /* Insert ellipsis. */
  2486. cm_dots (arg)
  2487.      int arg;
  2488. {
  2489.   if (arg == START)
  2490.     add_word ("...");
  2491. }
  2492.  
  2493. cm_bullet (arg)
  2494.      int arg;
  2495. {
  2496.   if (arg == START)
  2497.     add_char ('*');
  2498. }
  2499.  
  2500. cm_minus (arg)
  2501.      int arg;
  2502. {
  2503.   if (arg == START)
  2504.     add_char ('-');
  2505. }
  2506.  
  2507. /* Insert "TeX". */
  2508. cm_TeX (arg)
  2509.      int arg;
  2510. {
  2511.   if (arg == START)
  2512.     add_word ("TeX");
  2513. }
  2514.  
  2515. cm_copyright (arg)
  2516.      int arg;
  2517. {
  2518.   if (arg == START)
  2519.     add_word ("(C)");
  2520. }
  2521.  
  2522. cm_code (arg)
  2523.      int arg;
  2524. {
  2525.   extern int printing_index;
  2526.  
  2527.   if (printing_index)
  2528.     return;
  2529.  
  2530.   if (arg == START)
  2531.     add_char ('`');
  2532.   else
  2533.     add_word ("'");
  2534. }
  2535.  
  2536. cm_samp (arg)
  2537.      int arg;
  2538. {
  2539.   cm_code (arg);
  2540. }
  2541.  
  2542. cm_file (arg)
  2543.      int arg;
  2544. {
  2545.   cm_code (arg);
  2546. }
  2547.  
  2548. cm_kbd (arg)
  2549.      int arg;
  2550. {
  2551.   cm_code (arg);
  2552. }
  2553.  
  2554. cm_key (arg)
  2555.      int arg;
  2556. {
  2557. }
  2558.  
  2559.  /* Convert the character at position-1 into CTL. */
  2560. cm_ctrl (arg, position)
  2561.      int arg, position;
  2562. {
  2563.   if (arg == END)
  2564.     output_paragraph[position - 1] = CTL (output_paragraph[position - 1]);
  2565. }
  2566.  
  2567. /* Small Caps in makeinfo just does all caps. */
  2568. cm_sc (arg, start_pos, end_pos)
  2569.      int arg, start_pos, end_pos;
  2570. {
  2571.   if (arg == END)
  2572.     {
  2573.       while (start_pos < end_pos)
  2574.     {
  2575.       output_paragraph[start_pos] =
  2576.         coerce_to_upper (output_paragraph[start_pos]);
  2577.       start_pos++;
  2578.     }
  2579.     }
  2580. }
  2581.  
  2582. /* @var in makeinfo just uppercases the text. */
  2583. cm_var (arg, start_pos, end_pos)
  2584.      int arg, start_pos, end_pos;
  2585. {
  2586.   if (arg == END)
  2587.     {
  2588.       while (start_pos < end_pos)
  2589.     {
  2590.       output_paragraph[start_pos] =
  2591.         coerce_to_upper (output_paragraph[start_pos]);
  2592.       start_pos++;
  2593.     }
  2594.     }
  2595. }
  2596.  
  2597. cm_dfn (arg, position)
  2598.      int arg, position;
  2599. {
  2600.   add_char ('"');
  2601. }
  2602.  
  2603. cm_emph (arg)
  2604.      int arg;
  2605. {
  2606.   add_char ('*');
  2607. }
  2608.  
  2609. cm_strong (arg, position)
  2610.      int arg, position;
  2611. {
  2612.   cm_emph (arg);
  2613. }
  2614.  
  2615. cm_cite (arg, position)
  2616.      int arg, position;
  2617. {
  2618.   if (arg == START)
  2619.     add_word ("``");
  2620.   else
  2621.     add_word ("''");
  2622. }
  2623.  
  2624. cm_italic (arg)
  2625. {
  2626. }
  2627.  
  2628. cm_bold (arg)
  2629. {
  2630.   cm_italic (arg);
  2631. }
  2632.  
  2633. cm_roman (arg)
  2634. {
  2635. }
  2636.  
  2637. cm_title (arg)
  2638. {
  2639.   cm_italic (arg);
  2640. }
  2641.  
  2642. cm_refill ()
  2643. {
  2644. }
  2645.  
  2646. /* Prevent the argument from being split across two lines. */
  2647. cm_w (arg)
  2648.      int arg;
  2649. {
  2650.   if (arg == START)
  2651.     non_splitting_words++;
  2652.   else
  2653.     non_splitting_words--;
  2654. }
  2655.  
  2656.  
  2657. /* Explain that this command is obsolete, thus the user shouldn't
  2658.    do anything with it. */
  2659. cm_obsolete (arg)
  2660. {
  2661.   if (arg == START)
  2662.     warning ("The command `@%s' is obsolete", command);
  2663. }
  2664.  
  2665. /* Insert the text following input_text_offset up to the end of the line
  2666.    in a new, separate paragraph.  Directly underneath it, insert a
  2667.    line of WITH_CHAR, the same length of the inserted text. */
  2668. insert_and_underscore (with_char)
  2669.      int with_char;
  2670. {
  2671.   int len, i, old_no_indent;
  2672.   char *temp;
  2673.  
  2674.   close_paragraph ();
  2675.   filling_enabled =  indented_fill = false;
  2676.   old_no_indent = no_indent;
  2677.   no_indent = true;
  2678.   get_rest_of_line (&temp);
  2679.  
  2680.   len = output_position;
  2681.   execute_string ("%s\n", temp);
  2682.   free (temp);
  2683.  
  2684.   len = ((output_position + output_paragraph_offset) - 1) - len;
  2685.   for (i = 0; i < len; i++)
  2686.     add_char (with_char);
  2687.   insert ('\n');
  2688.   close_paragraph ();
  2689.   filling_enabled = true;
  2690.   no_indent = old_no_indent;
  2691. }
  2692.  
  2693. cm_chapter ()
  2694. {
  2695.   insert_and_underscore ('*');
  2696. }
  2697.  
  2698. cm_section ()
  2699. {
  2700.   insert_and_underscore ('=');
  2701. }
  2702.  
  2703. cm_subsection ()
  2704. {
  2705.   insert_and_underscore ('-');
  2706. }
  2707.  
  2708. cm_subsubsection ()
  2709. {
  2710.   insert_and_underscore ('.');
  2711. }
  2712.  
  2713. cm_unnumbered ()
  2714. {
  2715.   cm_chapter ();
  2716. }
  2717.  
  2718. cm_unnumberedsec ()
  2719. {
  2720.   cm_section ();
  2721. }
  2722.  
  2723. cm_unnumberedsubsec ()
  2724. {
  2725.   cm_subsection ();
  2726. }
  2727.  
  2728. cm_unnumberedsubsubsec ()
  2729. {
  2730.   cm_subsubsection ();
  2731. }
  2732.  
  2733. cm_appendix ()
  2734. {
  2735.   cm_chapter ();
  2736. }
  2737.  
  2738. cm_appendixsec ()
  2739. {
  2740.   cm_section ();
  2741. }
  2742.  
  2743. cm_appendixsubsec ()
  2744. {
  2745.   cm_subsection ();
  2746. }
  2747.  
  2748. cm_appendixsubsubsec ()
  2749. {
  2750.   cm_subsubsection ();
  2751. }
  2752.  
  2753. cm_majorheading ()
  2754. {
  2755.   cm_chapheading ();
  2756. }
  2757.  
  2758. cm_chapheading ()
  2759. {
  2760.   cm_chapter ();
  2761. }
  2762.  
  2763. cm_heading ()
  2764. {
  2765.   cm_section ();
  2766. }
  2767.  
  2768. cm_subheading ()
  2769. {
  2770.   cm_subsection ();
  2771. }
  2772.  
  2773. cm_subsubheading ()
  2774. {
  2775.   cm_subsubsection ();
  2776. }
  2777.  
  2778.  
  2779. /* **************************************************************** */
  2780. /*                                    */
  2781. /*           Adding nodes, and making tags            */
  2782. /*                                    */
  2783. /* **************************************************************** */
  2784.  
  2785. /* Start a new tag table. */
  2786. init_tag_table ()
  2787. {
  2788.   while (tag_table != (TAG_ENTRY *) NULL)
  2789.     {
  2790.       TAG_ENTRY *temp = tag_table;
  2791.       free (temp->node);
  2792.       free (temp->prev);
  2793.       free (temp->next);
  2794.       free (temp->up);
  2795.       tag_table = tag_table->next_ent;
  2796.       free (temp);
  2797.     }
  2798. }
  2799.  
  2800. write_tag_table ()
  2801. {
  2802.   return (write_tag_table_internal (false));    /* Not indirect. */
  2803. }
  2804.  
  2805. write_tag_table_indirect ()
  2806. {
  2807.   return (write_tag_table_internal (true));
  2808. }
  2809.  
  2810. /* Write out the contents of the existing tag table.
  2811.    INDIRECT_P says how to format the output. */
  2812. write_tag_table_internal (indirect_p)
  2813.      boolean indirect_p;
  2814. {
  2815.   TAG_ENTRY *node = tag_table;
  2816.  
  2817.   filling_enabled = false;
  2818.   must_start_paragraph = 0;
  2819.   close_paragraph ();
  2820.   if (!indirect_p)
  2821.     insert ('\n');
  2822.   add_word_args ("\037\nTag Table:\n%s", indirect_p ? "(Indirect)\n" : "");
  2823.  
  2824.   while (node != (TAG_ENTRY *) NULL)
  2825.     {
  2826.       add_word_args ("Node: %s\177%d\n", node->node, node->position);
  2827.       node = node->next_ent;
  2828.     }
  2829.   add_word ("\037\nEnd Tag Table\n");
  2830.   flush_output ();
  2831. }
  2832.  
  2833. char *
  2834. get_node_token ()
  2835. {
  2836.   char *string;
  2837.  
  2838.   get_until_in_line (",", &string);
  2839.  
  2840.   if (curchar () == ',')
  2841.     input_text_offset++;
  2842.  
  2843.   canon_white (string);
  2844.  
  2845.   /* Allow things like @@nodename. */
  2846.   normalize_node_name (string);
  2847.  
  2848.   return (string);
  2849. }
  2850.  
  2851. /* Given a node name in STRING, remove double @ signs, replacing them
  2852.    with just one. */
  2853. normalize_node_name (string)
  2854.      char *string;
  2855. {
  2856.   register int i, l = strlen (string);
  2857.  
  2858.   for (i = 0; i < l; i++)
  2859.     {
  2860.       if (string[i] == '@' && string[i + 1] == '@')
  2861.     {
  2862.       strncpy (string + i, string + i + 1, l - i);
  2863.       l--;
  2864.     }
  2865.     }
  2866. }
  2867.  
  2868. /* Look up NAME in the tag table, and return the associated
  2869.    tag_entry.  If the node is not in the table return NULL. */
  2870. TAG_ENTRY *
  2871. find_node (name)
  2872.      char *name;
  2873. {
  2874.   TAG_ENTRY *tag = tag_table;
  2875.  
  2876.   while (tag != (TAG_ENTRY *) NULL)
  2877.     {
  2878.       if (stricmp (tag->node, name) == 0)
  2879.     return (tag);
  2880.       tag = tag->next_ent;
  2881.     }
  2882.   return ((TAG_ENTRY *) NULL);
  2883. }
  2884.  
  2885. /* Remember NODE and associates. */
  2886. remember_node (node, prev, next, up, position, line_no, no_warn)
  2887.      char *node, *prev, *next, *up;
  2888.      int position, line_no, no_warn;
  2889. {
  2890.   /* Check for existence of this tag already. */
  2891.   if (validating)
  2892.     {
  2893.       register TAG_ENTRY *tag = find_node (node);
  2894.       if (tag)
  2895.     {
  2896.       line_error ("Node `%s' multiply defined (%d is first definition)",
  2897.               node, tag->line_no);
  2898.       return;
  2899.     }
  2900.     }
  2901.  
  2902.   /* First, make this the current node. */
  2903.   current_node = node;
  2904.  
  2905.   /* Now add it to the list. */
  2906.   {
  2907.     TAG_ENTRY *new = (TAG_ENTRY *) xmalloc (sizeof (TAG_ENTRY));
  2908.     new->node = node;
  2909.     new->prev = prev;
  2910.     new->next = next;
  2911.     new->up = up;
  2912.     new->position = position;
  2913.     new->line_no = line_no;
  2914.     new->filename = node_filename;
  2915.     new->touched = 0;        /* not yet referenced. */
  2916.     new->flags = 0;
  2917.     if (no_warn)
  2918.       new->flags |= NO_WARN;
  2919.     new->next_ent = tag_table;
  2920.     tag_table = new;
  2921.   }
  2922. }
  2923.  
  2924. /* Here is a structure which associates sectioning commands with
  2925.    an integer, hopefully to reflect the `depth' of the current
  2926.    section. */
  2927. struct {
  2928.   char *name;
  2929.   int level;
  2930. } section_alist[] = {
  2931.   { "chapter", 1 },
  2932.   { "section", 2},
  2933.   { "subsec", 3},
  2934.   { "subsubsec", 4},
  2935.   { "unnumbered", 1},
  2936.   { "unnumberedsec", 2},
  2937.   { "unnumberedsubsec", 3},
  2938.   { "unnumberedsubsubsec", 4},
  2939.   { "appendix", 1},
  2940.   { "appendixsec", 2},
  2941.   { "appendixsubsec", 3},
  2942.   { "appendixsubsubsec", 4},
  2943.   { (char *)NULL, 0 }
  2944. };
  2945.  
  2946. /* Return an integer which identifies the type section present in TEXT. */
  2947. int
  2948. what_section (text)
  2949.      char *text;
  2950. {
  2951.   int i, j;
  2952.   char *t;
  2953.  
  2954.   for (j = 0; text[j] && whitespace (text[j]) || text[j] == '\n'; j++);
  2955.   if (text[j] != '@')
  2956.     return (-1);
  2957.  
  2958.   text = text + j + 1;
  2959.  
  2960.   /* Handle italicized sectioning commands. */
  2961.   if (*text == 'i')
  2962.     text++;
  2963.  
  2964.   for (i = 0; t = section_alist[i].name; i++)
  2965.     {
  2966.       if (strncmp (t, text, strlen (t)) == 0)
  2967.     return (section_alist[i].level);
  2968.     }
  2969.   return (-1);
  2970. }
  2971.  
  2972. /* The order is: nodename, nextnode, prevnode, upnode.
  2973.    The next, prev, and up fields can be defaulted.
  2974.    You must follow a node command which has those fields
  2975.    defaulted with a sectioning command (e.g. @chapter) giving
  2976.    the "level" of that node.  It is an error not to do so.
  2977.    The defaults come from the menu in this nodes parent. */
  2978. cm_node ()
  2979. {
  2980.   char *node, *prev, *next, *up;
  2981.   int new_node_pos, defaulting, this_section, no_warn = 0;
  2982.   extern int already_outputting_pending_notes;
  2983.  
  2984.   if (strcmp (command, "nwnode") == 0)
  2985.     no_warn = 1;
  2986.  
  2987.   /* Get rid of unmatched brace arguments from previous commands. */
  2988.   discard_braces ();
  2989.  
  2990.   /* There also might be insertions left lying around that haven't been
  2991.      ended yet.  Do that also. */
  2992.   discard_insertions ();
  2993.  
  2994.   if (!already_outputting_pending_notes)
  2995.     {
  2996.       close_paragraph ();
  2997.       output_pending_notes ();
  2998.       free_pending_notes ();
  2999.     }
  3000.  
  3001.   filling_enabled = indented_fill = false;
  3002.   new_node_pos = output_position + 1;
  3003.  
  3004.   node = get_node_token ();
  3005.   next = get_node_token ();
  3006.   prev = get_node_token ();
  3007.   up = get_node_token ();
  3008.  
  3009.   this_section = what_section (input_text + input_text_offset);
  3010.  
  3011.   /* ??? The first \n in the following string shouldn't be there, but I have
  3012.      to revamp the @example & @group things so that they always leave a \n
  3013.      as the last character output.  Until that time, this is the only way
  3014.      I can produce reliable output. */
  3015.   no_indent = true;
  3016.   add_word_args ("\n\037\nFile: %s,  Node: %s", pretty_output_filename, node);
  3017.  
  3018.   /* Check for defaulting of this node's next, prev, and up fields. */
  3019.   defaulting = ((strlen (next) == 0) &&
  3020.         (strlen (prev) == 0) &&
  3021.         (strlen (up) == 0));
  3022.  
  3023.   /* If we are defaulting, then look at the immediately following
  3024.      sectioning command (error if none) to determine the node's
  3025.      level.  Find the node that contains the menu mentioning this node
  3026.      that is one level up (error if not found).  That node is the "Up"
  3027.      of this node.  Default the "Next" and "Prev" from the menu. */
  3028.   if (defaulting)
  3029.     {
  3030.       NODE_REF *last_ref = (NODE_REF *)NULL;
  3031.       NODE_REF *ref = node_references;
  3032.  
  3033.       if (this_section < 0)
  3034.     {
  3035.       char *polite_section_name = "chapter";
  3036.       int i;
  3037.  
  3038.       for (i = 0; section_alist[i].name; i++)
  3039.         if (section_alist[i].level == current_section + 1)
  3040.           {
  3041.         polite_section_name = section_alist[i].name;
  3042.         break;
  3043.           }
  3044.  
  3045.       line_error
  3046.         ("Node `%s' requires a sectioning command (e.g. @%s)",
  3047.          node, polite_section_name);
  3048.     }
  3049.       else
  3050.     {
  3051.       while (ref)
  3052.         {
  3053.           if (ref->section == (this_section - 1) &&
  3054.           ref->type == menu_reference &&
  3055.           stricmp (ref->node, node) == 0)
  3056.         {
  3057.           free (up);
  3058.           up = savestring (ref->containing_node);
  3059.  
  3060.           if (last_ref &&
  3061.               strcmp
  3062.               (last_ref->containing_node, ref->containing_node) == 0)
  3063.             {
  3064.               free (next);
  3065.               next = savestring (last_ref->node);
  3066.             }
  3067.  
  3068.           if (ref->next &&
  3069.               strcmp
  3070.               (ref->next->containing_node, ref->containing_node) == 0)
  3071.             {
  3072.               free (prev);
  3073.               prev = savestring (ref->next->node);
  3074.             }
  3075.           break;
  3076.         }
  3077.           last_ref = ref;
  3078.           ref = ref->next;
  3079.         }
  3080.     }
  3081.     }
  3082.  
  3083.   if (*next)
  3084.     add_word_args (",  Next: %s", next);
  3085.   
  3086.   if (*prev)
  3087.     add_word_args (",  Prev: %s", prev);
  3088.  
  3089.   if (*up)
  3090.     add_word_args (",  Up: %s", up);
  3091.  
  3092.   insert ('\n');
  3093.   close_paragraph ();
  3094.   no_indent = false;
  3095.  
  3096.   if (!*node)
  3097.     {
  3098.       line_error ("No node name specified for `@%s' command", command);
  3099.       free (node);
  3100.       free (next);
  3101.       free (prev);
  3102.       free (up);
  3103.     }
  3104.   else
  3105.     {
  3106.       if (!*next) { free (next); next = (char *)NULL; }
  3107.       if (!*prev) { free (prev); prev = (char *)NULL; }
  3108.       if (!*up) { free (up); up = (char *)NULL; }
  3109.       remember_node (node, prev, next, up, new_node_pos, line_number, no_warn);
  3110.     }
  3111.  
  3112.   /* Change the section only if there was a sectioning command. */
  3113.   if (this_section >= 0)
  3114.     current_section = this_section;
  3115.  
  3116.   filling_enabled = true;
  3117. }
  3118.  
  3119. /* Validation of an info file.
  3120.    Scan through the list of tag entrys touching the Prev, Next, and Up
  3121.    elements of each.  It is an error not to be able to touch one of them,
  3122.    except in the case of external node references, such as "(DIR)".
  3123.  
  3124.    If the Prev is different from the Up,
  3125.    then the Prev node must have a Next pointing at this node.
  3126.  
  3127.    Every node except Top must have an Up.
  3128.    The Up node must contain some sort of reference, other than a Next,
  3129.    to this node.
  3130.  
  3131.    If the Next is different from the Next of the Up,
  3132.    then the Next node must have a Prev pointing at this node. */
  3133. validate_file (filename, tag_table)
  3134.      char *filename;
  3135.      TAG_ENTRY *tag_table;
  3136. {
  3137.   char *old_input_filename = input_filename;
  3138.   TAG_ENTRY *tags = tag_table;
  3139.  
  3140.   while (tags != (TAG_ENTRY *) NULL)
  3141.     {
  3142.       register TAG_ENTRY *temp_tag;
  3143.  
  3144.       input_filename = tags->filename;
  3145.       line_number = tags->line_no;
  3146.  
  3147.       /* If this node has a Next, then make sure that the Next exists. */
  3148.       if (tags->next)
  3149.     {
  3150.       validate (tags->next, tags->line_no, "Next");
  3151.  
  3152.       /* If the Next node exists, and there is no Up, then make
  3153.          sure that the Prev of the Next points back. */
  3154.       if (temp_tag = find_node (tags->next))
  3155.         {
  3156.           char *prev = temp_tag->prev;
  3157.           if (!prev || (stricmp (prev, tags->node) != 0))
  3158.         {
  3159.           line_error
  3160.             ("Node `%s''s Next field not pointed back to", tags->node);
  3161.           line_number = temp_tag->line_no;
  3162.           input_filename = temp_tag->filename;
  3163.           line_error
  3164.             ("This node (`%s') is the one with the bad `Prev'",
  3165.              temp_tag->node);
  3166.           input_filename = tags->filename;
  3167.           line_number = tags->line_no;
  3168.           temp_tag->flags |= PREV_ERROR;
  3169.         }
  3170.         }
  3171.     }
  3172.  
  3173.       /* Validate the Prev field if there is one, and we haven't already
  3174.      complained about it in some way.  You don't have to have a Prev
  3175.      field at this stage. */
  3176.       if (!(tags->flags & PREV_ERROR) && tags->prev)
  3177.     {
  3178.       int valid = validate (tags->prev, tags->line_no, "Prev");
  3179.  
  3180.       if (!valid)
  3181.         tags->flags |= PREV_ERROR;
  3182.       else
  3183.         {
  3184.           /* If the Prev field is not the same as the Up field,
  3185.          then the node pointed to by the Prev field must have
  3186.          a Next field which points to this node. */
  3187.           if (tags->up && (stricmp (tags->prev, tags->up) != 0))
  3188.         {
  3189.           temp_tag = find_node (tags->prev);
  3190.           if (!temp_tag->next ||
  3191.               (stricmp (temp_tag->next, tags->node) != 0))
  3192.             {
  3193.               line_error ("Node `%s''s Prev field not pointed back to",
  3194.                   tags->node);
  3195.               line_number = temp_tag->line_no;
  3196.               input_filename = temp_tag->filename;
  3197.               line_error
  3198.             ("This node (`%s') is the one with the bad `Next'",
  3199.              temp_tag->node);
  3200.               input_filename = tags->filename;
  3201.               line_number = tags->line_no;
  3202.               temp_tag->flags |= NEXT_ERROR;
  3203.             }
  3204.         }
  3205.         }
  3206.     }
  3207.  
  3208.       if (!tags->up && (stricmp (tags->node, "Top") != 0))
  3209.     line_error ("Node `%s' is missing an \"Up\" field", tags->node);
  3210.       else if (tags->up)
  3211.     {
  3212.       int valid = validate (tags->up, tags->line_no, "Up");
  3213.  
  3214.       /* If node X has Up: Y, then warn if Y fails to have a menu item
  3215.          or note pointing at X, if Y isn't of the form "(Y)". */
  3216.       if (valid && *tags->up != '(')
  3217.         {
  3218.           NODE_REF *nref, *tref, *list;
  3219.           NODE_REF *find_node_reference ();
  3220.  
  3221.           tref = (NODE_REF *) NULL;
  3222.           list = node_references;
  3223.  
  3224.           for (;;)
  3225.         {
  3226.           if (!(nref = find_node_reference (tags->node, list)))
  3227.             break;
  3228.  
  3229.           if (stricmp (nref->containing_node, tags->up) == 0)
  3230.             {
  3231.               if (nref->type != menu_reference)
  3232.             {
  3233.               tref = nref;
  3234.               list = nref->next;
  3235.             }
  3236.               else
  3237.             break;
  3238.             }
  3239.           list = nref->next;
  3240.         }
  3241.  
  3242.           if (!nref)
  3243.         {
  3244.           temp_tag = find_node (tags->up);
  3245.           line_number = temp_tag->line_no;
  3246.           filename = temp_tag->filename;
  3247.           if (!tref)
  3248.             line_error ("`%s' has an Up field of `%s', but `%s' has no menu item for `%s'",
  3249.                 tags->node, tags->up, tags->up, tags->node);
  3250.           line_number = tags->line_no;
  3251.           filename = tags->filename;
  3252.         }
  3253.         }
  3254.     }
  3255.       tags = tags->next_ent;
  3256.     }
  3257.  
  3258.   validate_other_references (node_references);
  3259.   /* We have told the user about the references which didn't exist.
  3260.      Now tell him about the nodes which aren't referenced. */
  3261.  
  3262.   tags = tag_table;
  3263.   while (tags != (TAG_ENTRY *) NULL)
  3264.     {
  3265.       /* Special hack.  If the node in question appears to have
  3266.          been referenced more than REFERENCE_WARNING_LIMIT times,
  3267.          give a warning. */
  3268.       if (tags->touched > reference_warning_limit)
  3269.     {
  3270.       input_filename = tags->filename;
  3271.       line_number = tags->line_no;
  3272.       warning ("Node `%s' has been referenced %d times",
  3273.            tags->node, tags->touched);
  3274.     }
  3275.  
  3276.       if (tags->touched == 0)
  3277.     {
  3278.       input_filename = tags->filename;
  3279.       line_number = tags->line_no;
  3280.  
  3281.       /* Notice that the node "Top" is special, and doesn't have to
  3282.          be referenced. */
  3283.       if (stricmp (tags->node, "Top") != 0)
  3284.         warning ("Unreferenced node `%s'", tags->node);
  3285.     }
  3286.       tags = tags->next_ent;
  3287.     }
  3288.   input_filename = old_input_filename;
  3289. }
  3290.  
  3291. /* Return 1 if tag correctly validated, or 0 if not. */
  3292. validate (tag, line, label)
  3293.      char *tag;
  3294.      int line;
  3295.      char *label;
  3296. {
  3297.   TAG_ENTRY *result;
  3298.  
  3299.   /* If there isn't a tag to verify, or if the tag is in another file,
  3300.      then it must be okay. */
  3301.   if (!tag || !*tag || *tag == '(')
  3302.     return (1);
  3303.  
  3304.   /* Otherwise, the tag must exist. */
  3305.   result = find_node (tag);
  3306.  
  3307.   if (!result)
  3308.     {
  3309.       line_number = line;
  3310.       line_error ("Validation error.  `%s' field points to node `%s', which doesn't exist",
  3311.           label, tag);
  3312.       return (0);
  3313.     }
  3314.   result->touched++;
  3315.   return (1);
  3316. }
  3317.  
  3318. /* Split large output files into a series of smaller files.  Each file
  3319.    is pointed to in the tag table, which then gets written out as the
  3320.    original file.  The new files have the same name as the original file
  3321.    with a "-num" attached.  SIZE is the largest number of bytes to allow
  3322.    in any single split file. */
  3323. split_file (filename, size)
  3324.      char *filename;
  3325.      int size;
  3326. {
  3327.   char *root_filename, *root_pathname;
  3328.   char *the_file, *filename_part ();
  3329.   struct stat fileinfo;
  3330.   char *the_header;
  3331.   int header_size;
  3332.  
  3333.   /* Can only do this to files with tag tables. */
  3334.   if (!tag_table)
  3335.     return;
  3336.  
  3337.   if (size == 0)
  3338.     size = DEFAULT_SPLIT_SIZE;
  3339.  
  3340.   if ((stat (filename, &fileinfo) != 0) ||
  3341.       (fileinfo.st_size < SPLIT_SIZE_THRESHOLD))
  3342.     return;
  3343.  
  3344.   the_file = find_and_load (filename);
  3345.   if (!the_file)
  3346.     return;
  3347.  
  3348.   root_filename = filename_part (filename);
  3349.   root_pathname = pathname_part (filename);
  3350.  
  3351.   if (!root_pathname)
  3352.     root_pathname = savestring ("");
  3353.  
  3354.   /* Start splitting the file.  Walk along the tag table
  3355.      outputting sections of the file.  When we have written
  3356.      all of the nodes in the tag table, make the top-level
  3357.      pointer file, which contains indirect pointers and
  3358.      tags for the nodes. */
  3359.   {
  3360.     int which_file = 1;
  3361.     TAG_ENTRY *tags = tag_table;
  3362.     char *indirect_info = (char *)NULL;
  3363.  
  3364.     /* Remember the `header' of this file.  The first tag in the file is
  3365.        the bottom of the header; the top of the file is the start. */
  3366.     the_header = xmalloc (1 + (header_size = (tags->position - 2)));
  3367.     bcopy (the_file, the_header, header_size);
  3368.  
  3369.     while (tags)
  3370.       {
  3371.     int file_top, file_bot, limit;
  3372.  
  3373.     /* Have to include the Control-_. */
  3374.     file_top = file_bot = tags->position - 2;
  3375.     limit = file_top + size;
  3376.  
  3377.     /* If the rest of this file is only one node, then
  3378.        that is the entire subfile. */
  3379.     if (!tags->next_ent)
  3380.       {
  3381.         int i = tags->position + 1;
  3382.         char last_char = the_file[i];
  3383.  
  3384.         while (i < fileinfo.st_size)
  3385.           {
  3386.         if ((the_file[i] == '\037') &&
  3387.             ((last_char == '\n') ||
  3388.              (last_char == '\014')))
  3389.           break;
  3390.         else
  3391.           last_char = the_file[i];
  3392.         i++;
  3393.           }
  3394.         file_bot = i;
  3395.         tags = tags->next_ent;
  3396.         goto write_region;
  3397.       }
  3398.  
  3399.     /* Otherwise, find the largest number of nodes that can fit in
  3400.        this subfile. */
  3401.     for (; tags; tags = tags->next_ent)
  3402.       {
  3403.         if (!tags->next_ent)
  3404.           {
  3405.         /* This entry is the last node.  Search forward for the end
  3406.                of this node, and that is the end of this file. */
  3407.         int i = tags->position + 1;
  3408.         char last_char = the_file[i];
  3409.  
  3410.         while (i < fileinfo.st_size)
  3411.           {
  3412.             if ((the_file[i] == '\037') &&
  3413.             ((last_char == '\n') ||
  3414.              (last_char == '\014')))
  3415.               break;
  3416.             else
  3417.               last_char = the_file[i];
  3418.             i++;
  3419.           }
  3420.         file_bot = i;
  3421.  
  3422.         if (file_bot < limit)
  3423.           {
  3424.             tags = tags->next_ent;
  3425.             goto write_region;
  3426.           }
  3427.         else
  3428.           {
  3429.             /* Here we want to write out everything before the last
  3430.                node, and then write the last node out in a file
  3431.                by itself. */
  3432.             file_bot = tags->position;
  3433.             goto write_region;
  3434.           }
  3435.           }
  3436.  
  3437.         if (tags->next_ent->position > limit)
  3438.           {
  3439.         if ((tags->position) - 2 == file_top)
  3440.           tags = tags->next_ent;
  3441.         file_bot = tags->position;
  3442.           write_region:
  3443.         {
  3444.           int fd;
  3445.           char *split_file = xmalloc (10 + strlen (root_pathname)
  3446.                           + strlen (root_filename));
  3447.           sprintf (split_file,
  3448.                "%s%s-%d", root_pathname, root_filename, which_file);
  3449.  
  3450.           if (((fd = open (split_file, O_WRONLY | O_TRUNC | O_CREAT, 0666)) < 0)
  3451.               || (write (fd, the_header, header_size) != header_size)
  3452.               || (write (fd, the_file + file_top, file_bot - file_top)
  3453.               != (file_bot - file_top))
  3454.               || ((close (fd)) < 0))
  3455.             {
  3456.               perror (split_file);
  3457.               close (fd);
  3458.               exit (FATAL);
  3459.             }
  3460.  
  3461.           if (!indirect_info)
  3462.             {
  3463.               indirect_info = the_file + file_top;
  3464.               sprintf (indirect_info, "\037\nIndirect:\n");
  3465.               indirect_info += strlen (indirect_info);
  3466.             }
  3467.  
  3468.           sprintf (indirect_info, "%s-%d: %d\n",
  3469.                root_filename, which_file, file_top);
  3470.  
  3471.           free (split_file);
  3472.           indirect_info += strlen (indirect_info);
  3473.           which_file++;
  3474.           break;
  3475.         }
  3476.           }
  3477.       }
  3478.       }
  3479.  
  3480.     /* We have sucessfully created the subfiles.  Now write out the
  3481.        original again.  We must use `output_stream', or
  3482.        write_tag_table_indirect () won't know where to place the output. */
  3483.     output_stream = fopen (filename, "w");
  3484.     if (!output_stream)
  3485.       {
  3486.     perror (filename);
  3487.     exit (FATAL);
  3488.       }
  3489.  
  3490.     {
  3491.       int distance = indirect_info - the_file;
  3492.       fwrite (the_file, 1, distance, output_stream);
  3493.  
  3494.       /* Inhibit newlines. */
  3495.       paragraph_is_open = false;
  3496.  
  3497.       write_tag_table_indirect ();
  3498.       fclose (output_stream);
  3499.       free (the_header);
  3500.       free (the_file);
  3501.       return;
  3502.     }
  3503.   }
  3504. }
  3505.  
  3506. /* Some menu hacking.  This is used to remember menu references while
  3507.    reading the input file.  After the output file has been written, if
  3508.    validation is on, then we use the contents of NODE_REFERENCES as a
  3509.    list of nodes to validate. */
  3510. char *
  3511. reftype_type_string (type)
  3512.      enum reftype type;
  3513. {
  3514.   switch (type)
  3515.     {
  3516.     case menu_reference:
  3517.       return ("Menu");
  3518.     case followed_reference:
  3519.       return ("Followed-Reference");
  3520.     default:
  3521.       return ("Internal-bad-reference-type");
  3522.     }
  3523. }
  3524.  
  3525. /* Remember this node name for later validation use. */
  3526. remember_node_reference (node, line, type)
  3527.      char *node;
  3528.      int line;
  3529.      enum reftype type;
  3530. {
  3531.   NODE_REF *temp = (NODE_REF *) xmalloc (sizeof (NODE_REF));
  3532.  
  3533.   temp->next = node_references;
  3534.   temp->node = savestring (node);
  3535.   temp->line_no = line;
  3536.   temp->section = current_section;
  3537.   temp->type = type;
  3538.   temp->containing_node = savestring (current_node);
  3539.   temp->filename = node_filename;
  3540.  
  3541.   node_references = temp;
  3542. }
  3543.  
  3544. validate_other_references (ref_list)
  3545.      register NODE_REF *ref_list;
  3546. {
  3547.   char *old_input_filename = input_filename;
  3548.  
  3549.   while (ref_list != (NODE_REF *) NULL)
  3550.     {
  3551.       input_filename = ref_list->filename;
  3552.       validate (ref_list->node, ref_list->line_no,
  3553.         reftype_type_string (ref_list->type));
  3554.       ref_list = ref_list->next;
  3555.     }
  3556.   input_filename = old_input_filename;
  3557. }
  3558.  
  3559. /* Find NODE in REF_LIST. */
  3560. NODE_REF *
  3561. find_node_reference (node, ref_list)
  3562.      char *node;
  3563.      register NODE_REF *ref_list;
  3564. {
  3565.   while (ref_list)
  3566.     {
  3567.       if (stricmp (node, ref_list->node) == 0)
  3568.     break;
  3569.       ref_list = ref_list->next;
  3570.     }
  3571.   return (ref_list);
  3572. }
  3573.  
  3574. free_node_references ()
  3575. {
  3576.   register NODE_REF *list, *temp;
  3577.  
  3578.   list = node_references;
  3579.  
  3580.   while (list)
  3581.     {
  3582.       temp = list;
  3583.       free (list->node);
  3584.       free (list->containing_node);
  3585.       list = list->next;
  3586.       free (temp);
  3587.     }
  3588.   node_references = (NODE_REF *) NULL;
  3589. }
  3590.  
  3591. #define menu_starter "* "
  3592. glean_node_from_menu ()
  3593. {
  3594.   /* This function gets called at the start of every line while inside of
  3595.      a menu.  It checks to see if the line starts with "* ", and if so,
  3596.      remembers the node reference that this menu refers to.
  3597.  
  3598.      input_text_offset is at the \n just before the line start. */
  3599.  
  3600.   int i, orig_offset = input_text_offset;
  3601.   char *nodename;
  3602.  
  3603.   if (strncmp (&input_text[input_text_offset + 1],
  3604.            menu_starter,
  3605.            strlen (menu_starter)) != 0)
  3606.     return;
  3607.   else
  3608.     input_text_offset += strlen (menu_starter) + 1;
  3609.  
  3610.   get_until_in_line (":", &nodename);
  3611.   if (curchar () == ':')
  3612.     input_text_offset++;
  3613.   canon_white (nodename);
  3614.  
  3615.   if (curchar () == ':')
  3616.     goto save_node;
  3617.   free (nodename);
  3618.   get_rest_of_line (&nodename);
  3619.  
  3620.   /* Special hack: If the nodename follows the menu item name,
  3621.      then we have to read the rest of the line in order to find
  3622.      out what the nodename is.  But we still have to read the
  3623.      line later, in order to process any formatting commands that
  3624.      might be present.  So un-count the carriage return that has just
  3625.      been counted. */
  3626.   line_number--;
  3627.  
  3628.   canon_white (nodename);
  3629.   for (i = 0; i < strlen (nodename); i++)
  3630.     {
  3631.       if (nodename[i] == '\t' ||
  3632.       nodename[i] == '.' ||
  3633.       nodename[i] == ',')
  3634.     {
  3635.       nodename[i] = '\0';
  3636.       break;
  3637.     }
  3638.     }
  3639. save_node:
  3640.   normalize_node_name (nodename);
  3641.   i = strlen (nodename);
  3642.   if (i && nodename[i - 1] == ':')
  3643.     nodename[i - 1] = '\0';
  3644.  
  3645.   remember_node_reference (nodename, line_number, menu_reference);
  3646.   free (nodename);
  3647.   input_text_offset = orig_offset;
  3648. }
  3649.  
  3650. cm_menu ()
  3651. {
  3652.   begin_insertion (menu);
  3653. }
  3654.  
  3655.  
  3656. /* **************************************************************** */
  3657. /*                                    */
  3658. /*            Cross Reference Hacking                */
  3659. /*                                    */
  3660. /* **************************************************************** */
  3661.  
  3662. char *
  3663. get_xref_token ()
  3664. {
  3665.   char *string;
  3666.  
  3667.   get_until_in_braces (",", &string);
  3668.   if (curchar () == ',')
  3669.     input_text_offset++;
  3670.   fix_whitespace (string);
  3671.   normalize_node_name (string);
  3672.   return (string);
  3673. }
  3674.  
  3675. int px_ref_flag = 0;        /* Controls initial output string. */
  3676.  
  3677. /* Make a cross reference. */
  3678. cm_xref (arg)
  3679. {
  3680.   if (arg == START)
  3681.     {
  3682.       char *arg1, *arg2, *arg3, *arg4, *arg5;
  3683.  
  3684.       arg1 = get_xref_token ();
  3685.       arg2 = get_xref_token ();
  3686.       arg3 = get_xref_token ();
  3687.       arg4 = get_xref_token ();
  3688.       arg5 = get_xref_token ();
  3689.  
  3690.       add_word_args ("%s", px_ref_flag ? "*note " : "*Note ");
  3691.  
  3692.       if (*arg5 || *arg4)
  3693.     {
  3694.       char *node_name;
  3695.  
  3696.       if (!*arg2)
  3697.         node_name = arg1;
  3698.       else
  3699.         node_name = arg2;
  3700.  
  3701.       add_word_args ("%s: (%s)%s", arg2, arg4, arg1);
  3702.       return;
  3703.     }
  3704.       else
  3705.     remember_node_reference (arg1, line_number, followed_reference);
  3706.  
  3707.       if (*arg3)
  3708.     {
  3709.       if (!*arg2)
  3710.         {
  3711.           add_word_args ("%s: %s", arg3, arg1);
  3712.         }
  3713.       else
  3714.         {
  3715.           add_word_args ("%s: %s", arg2, arg1);
  3716.         }
  3717.       return;
  3718.     }
  3719.  
  3720.       if (*arg2)
  3721.     {
  3722.       execute_string ("%s", arg2);
  3723.       add_word_args (": %s", arg1);
  3724.     }
  3725.       else
  3726.     {
  3727.       add_word_args ("%s::", arg1);
  3728.     }
  3729.  
  3730.     }
  3731.   else
  3732.     {
  3733.  
  3734.       /* Check to make sure that the next non-whitespace character is either
  3735.          a period or a comma. input_text_offset is pointing at the "}" which
  3736.          ended the xref or pxref command. */
  3737.  
  3738.       int temp = input_text_offset + 1;
  3739.  
  3740.       if (output_paragraph[output_paragraph_offset - 2] == ':' &&
  3741.       output_paragraph[output_paragraph_offset - 1] == ':')
  3742.     return;
  3743.       while (temp < size_of_input_text)
  3744.     {
  3745.       if (cr_or_whitespace (input_text[temp]))
  3746.         temp++;
  3747.       else
  3748.         {
  3749.           if (input_text[temp] == '.' ||
  3750.           input_text[temp] == ',' ||
  3751.           input_text[temp] == '\t')
  3752.         return;
  3753.           else
  3754.         {
  3755.           line_error ("Cross-reference must be terminated with a period or a comma");
  3756.           return;
  3757.         }
  3758.         }
  3759.     }
  3760.     }
  3761. }
  3762.  
  3763. cm_pxref (arg)
  3764.      int arg;
  3765. {
  3766.   if (arg == START)
  3767.     {
  3768.       px_ref_flag++;
  3769.       cm_xref (arg);
  3770.       px_ref_flag--;
  3771.     }
  3772.   else
  3773.     add_char ('.');
  3774. }
  3775.  
  3776. cm_inforef (arg)
  3777.      int arg;
  3778. {
  3779.   if (arg == START)
  3780.     {
  3781.       char *node, *pname, *file;
  3782.  
  3783.       node = get_xref_token ();
  3784.       pname = get_xref_token ();
  3785.       file = get_xref_token ();
  3786.  
  3787.       add_word_args ("*note %s: (%s)%s", pname, file, node);
  3788.     }
  3789. }
  3790.  
  3791. /* **************************************************************** */
  3792. /*                                    */
  3793. /*            Insertion Command Stubs                */
  3794. /*                                    */
  3795. /* **************************************************************** */
  3796.  
  3797. cm_quotation ()
  3798. {
  3799.   begin_insertion (quotation);
  3800. }
  3801.  
  3802. cm_example ()
  3803. {
  3804.   begin_insertion (example);
  3805. }
  3806.  
  3807. cm_smallexample ()
  3808. {
  3809.   begin_insertion (smallexample);
  3810. }
  3811.  
  3812. cm_lisp ()
  3813. {
  3814.   begin_insertion (lisp);
  3815. }
  3816.  
  3817. cm_format ()
  3818. {
  3819.   begin_insertion (format);
  3820. }
  3821.  
  3822. cm_display ()
  3823. {
  3824.   begin_insertion (display);
  3825. }
  3826.  
  3827. cm_itemize ()
  3828. {
  3829.   begin_insertion (itemize);
  3830. }
  3831.  
  3832. cm_enumerate ()
  3833. {
  3834.   begin_insertion (enumerate);
  3835. }
  3836.  
  3837. cm_table ()
  3838. {
  3839.   begin_insertion (table);
  3840. }
  3841.  
  3842. cm_group ()
  3843. {
  3844.   begin_insertion (group);
  3845. }
  3846.  
  3847. cm_ifinfo ()
  3848. {
  3849.   begin_insertion (ifinfo);
  3850. }
  3851.  
  3852. cm_tex ()
  3853. {
  3854.   discard_until ("\n@end tex");
  3855.   discard_until ("\n");
  3856. }
  3857.  
  3858. cm_iftex ()
  3859. {
  3860.   discard_until ("\n@end iftex");
  3861.   discard_until ("\n");
  3862. }
  3863.  
  3864. cm_titlespec ()
  3865. {
  3866.   discard_until ("\n@end titlespec");
  3867.   discard_until ("\n");
  3868. }
  3869.  
  3870. cm_titlepage ()
  3871. {
  3872.   discard_until ("\n@end titlepage");
  3873.   discard_until ("\n");
  3874. }
  3875.  
  3876. cm_ignore ()
  3877. {
  3878.   discard_until ("\n@end ignore");
  3879.   discard_until ("\n");
  3880. }
  3881.  
  3882. /* **************************************************************** */
  3883. /*                                    */
  3884. /*            @itemx, @item                    */
  3885. /*                                    */
  3886. /* **************************************************************** */
  3887.  
  3888. /* Non-zero means a string is in execution, as opposed to a file. */
  3889. int executing_string = 0;
  3890.  
  3891. /* Execute the string produced by formatting the ARGs with FORMAT.  This
  3892.    is like submitting a new file with @include. */
  3893. execute_string (format, arg1, arg2, arg3, arg4, arg5)
  3894.      char *format;
  3895. {
  3896.   static char temp_string[4000];
  3897.   sprintf (temp_string, format, arg1, arg2, arg3, arg4, arg5);
  3898.   strcat (temp_string, "@bye\n");
  3899.   pushfile ();
  3900.   input_text_offset = 0;
  3901.   input_text = temp_string;
  3902.   input_filename = savestring (input_filename);
  3903.   size_of_input_text = strlen (temp_string);
  3904.  
  3905.   executing_string++;
  3906.   reader_loop ();
  3907.  
  3908.   popfile ();
  3909.   executing_string--;
  3910.  
  3911.   free_and_clear (&command);
  3912.   command = savestring ("not bye");
  3913. }
  3914.  
  3915. int itemx_flag = 0;
  3916.  
  3917. cm_itemx ()
  3918. {
  3919.   itemx_flag++;
  3920.   cm_item ();
  3921.   itemx_flag--;
  3922. }
  3923.  
  3924.  
  3925. cm_item ()
  3926. {
  3927.   char *rest_of_line, *item_func;
  3928.  
  3929.   /* Can only hack "@item" while inside of an insertion. */
  3930.   if (insertion_level)
  3931.     {
  3932.       get_until ("\n", &rest_of_line);
  3933.       canon_white (rest_of_line);
  3934.       item_func = current_item_function ();
  3935.  
  3936.       /* Okay, do the right thing depending on which insertion function
  3937.      is active. */
  3938.  
  3939.       switch (current_insertion_type ())
  3940.     {
  3941.     case menu:
  3942.     case quotation:
  3943.     case example:
  3944.     case smallexample:
  3945.     case lisp:
  3946.     case format:
  3947.     case display:
  3948.     case group:
  3949.     case ifinfo:
  3950.       line_error ("The `@%s' command is meaningless within a `@%s' block",
  3951.               command,
  3952.               insertion_type_pname (current_insertion_type ()));
  3953.       break;
  3954.  
  3955.     case itemize:
  3956.     case enumerate:
  3957.       if (itemx_flag)
  3958.         {
  3959.           line_error ("@itemx is not meaningful inside of a `%s' block",
  3960.               insertion_type_pname (current_insertion_type ()));
  3961.         }
  3962.       else
  3963.         {
  3964.           start_paragraph ();
  3965.           kill_self_indent (-1);
  3966.           discard_until ("\n");
  3967.           filling_enabled = indented_fill = true;
  3968.  
  3969.           if (current_insertion_type () == itemize)
  3970.         {
  3971.           indent (output_column = current_indent - 2);
  3972.  
  3973.           /* I need some way to determine whether this command
  3974.              takes braces or not.  I believe the user can type
  3975.              either "@bullet" or "@bullet{}".  Of course, they
  3976.              can also type "o" or "#" or whatever else they want. */
  3977.           if (item_func && *item_func)
  3978.             {
  3979.               if (*item_func == '@')
  3980.             if (item_func[strlen (item_func) - 1] != '}')
  3981.               execute_string ("%s{}", item_func);
  3982.             else
  3983.               execute_string ("%s", item_func);
  3984.               else
  3985.             execute_string ("%s", item_func);
  3986.             }
  3987.           insert (' ');
  3988.           output_column++;
  3989.         }
  3990.           else
  3991.         number_item ();
  3992.  
  3993.           /* Special hack.  This makes close paragraph ignore you until
  3994.          the start_paragraph () function has been called. */
  3995.           must_start_paragraph = 1;
  3996.         }
  3997.       break;
  3998.  
  3999.     case table:
  4000.       {
  4001.         /* Get rid of extra characters. */
  4002.         kill_self_indent (-1);
  4003.  
  4004.         /* close_paragraph () almost does what we want.  The problem
  4005.            is when paragraph_is_open, and last_char_was_newline, and
  4006.            the last newline has been turned into a space, because
  4007.            filling_enabled. I handle it here. */
  4008.         if (last_char_was_newline && filling_enabled && paragraph_is_open)
  4009.           insert ('\n');
  4010.         close_paragraph ();
  4011.  
  4012.         /* Indent on a new line, but back up one indentation level. */
  4013.         /* force existing indentation. */
  4014.         add_char ('i');
  4015.         output_paragraph_offset--;
  4016.         kill_self_indent (default_indentation_increment + 1);
  4017.  
  4018.         /* Add item's argument to the line. */
  4019.         filling_enabled = false;
  4020.         if (!item_func && !(*item_func))
  4021.           execute_string ("%s", rest_of_line);
  4022.         else
  4023.           execute_string ("%s{%s}", item_func, rest_of_line);
  4024.  
  4025.         /* Start a new line, and let start_paragraph ()
  4026.            do the indenting of it for you. */
  4027.         close_single_paragraph ();
  4028.         indented_fill = filling_enabled = true;
  4029.       }
  4030.     }
  4031.       free (rest_of_line);
  4032.     }
  4033.   else
  4034.     line_error ("@%s found outside of an insertion block", command);
  4035. }
  4036.  
  4037.  
  4038. /* **************************************************************** */
  4039. /*                                    */
  4040. /*            Defun and Friends                   */
  4041. /*                                    */
  4042. /* **************************************************************** */
  4043.  
  4044. /* The list of args that were passed to the def**** command. */
  4045. char **defun_args = (char **)NULL;
  4046.  
  4047. /* An alist mapping defun insertion types to the text that we use
  4048.    to describe them. */
  4049. struct {
  4050.   enum insertion_type type;
  4051.   char *title;
  4052. } type_title_alist[] = {
  4053.   { defun, "Function" },
  4054.   { defmac, "Macro" },
  4055.   { defspec, "Special form" },
  4056.   { defopt, "Option" },
  4057.   { deffn, (char *)NULL },
  4058.   { defvar, "Variable" },
  4059.   { (enum insertion_type)0, (char *)NULL }
  4060. };
  4061.  
  4062. /* Return the title string for this type of defun. */
  4063. char *
  4064. defun_title (type)
  4065.      enum insertion_type type;
  4066. {
  4067.   register int i;
  4068.  
  4069.   for (i = 0; type_title_alist[i].type || type_title_alist[i].title; i++)
  4070.     if (type_title_alist[i].type == type)
  4071.       return (type_title_alist[i].title);
  4072.   return (char *)NULL;
  4073. }
  4074.  
  4075. /* Return a list of words from the contents of STRING.
  4076.    You can group words with braces. */
  4077. char **
  4078. args_from_string (string)
  4079.      char *string;
  4080. {
  4081.   char **result = (char **) NULL;
  4082.   register int i, start, result_index, size;
  4083.   int len, skip_til_brace = 0;
  4084.  
  4085.   i = result_index = size = 0;
  4086.  
  4087.   /* Get a token, and stuff it into RESULT.  The tokens are split
  4088.      at spaces or tabs. */
  4089.   while (string[i])
  4090.     {
  4091.       /* Skip leading whitespace. */
  4092.       for (; string[i] && whitespace (string[i]); i++);
  4093.  
  4094.       start = i;
  4095.  
  4096.       if (!string[i])
  4097.     return (result);
  4098.  
  4099.       /* If this is a command which takes it's argument in braces, then
  4100.      gobble the whole thing. */
  4101.       if (string[i] == COMMAND_PREFIX)
  4102.     {
  4103.       register int j;
  4104.       for (j = i; string[j] &&
  4105.            !whitespace (string[j]) &&
  4106.            string[j] != '{'; j++);
  4107.  
  4108.       if (string[j] == '{')
  4109.         {
  4110.           while (string[j] && string[j] != '}')
  4111.         j++;
  4112.  
  4113.           if (string[j])
  4114.         j++;
  4115.  
  4116.           i = j;
  4117.           goto add_arg;
  4118.         }
  4119.     }
  4120.       
  4121.       if (string[i] == '{' && !whitespace (string[i + 1]))
  4122.     {
  4123.       skip_til_brace = 1;
  4124.       start = ++i;
  4125.     }
  4126.  
  4127.       /* Skip until whitespace or close brace. */
  4128.       while (string[i] &&
  4129.          ((skip_til_brace && string[i] != '}') ||
  4130.           (!skip_til_brace && !whitespace (string[i]))))
  4131.     i++;
  4132.  
  4133.     add_arg:
  4134.       len = i - start;
  4135.       if (result_index + 2 >= size)
  4136.     {
  4137.       if (!size)
  4138.         result = (char **) xmalloc ((size = 10) * (sizeof (char *)));
  4139.       else
  4140.         result =
  4141.           (char **) xrealloc (result, ((size += 10) * (sizeof (char *))));
  4142.     }
  4143.       result[result_index] = (char *) xmalloc (1 + len);
  4144.       strncpy (result[result_index], string + start, len);
  4145.       result[result_index][len] = '\0';
  4146.       result_index++;
  4147.       result[result_index] = (char *) NULL;
  4148.  
  4149.       if (skip_til_brace)
  4150.     {
  4151.       skip_til_brace = 0;
  4152.       if (string[i])
  4153.         i++;
  4154.     }
  4155.     }
  4156.  
  4157.   return (result);
  4158. }
  4159.  
  4160. get_defun_args ()
  4161. {
  4162.   register int i;
  4163.   char *line;
  4164.  
  4165.   get_rest_of_line (&line);
  4166.  
  4167.   if (defun_args)
  4168.     {
  4169.       for (i = 0; defun_args[i]; i++)
  4170.     free (defun_args[i]);
  4171.       free (defun_args);
  4172.     }
  4173.  
  4174.   defun_args = args_from_string (line);
  4175.   free (line);
  4176. }
  4177.  
  4178. insert_defun_arg (string, where)
  4179.      char *string;
  4180.      int where;
  4181. {
  4182.   register int i;
  4183.  
  4184.   for (i = 0; defun_args[i]; i++);
  4185.   defun_args = (char **)xrealloc (defun_args, (i + 2) * sizeof (char *));
  4186.   defun_args[i + 1] = (char *)NULL;
  4187.  
  4188.   for (; i != where; --i)
  4189.     defun_args[i] = defun_args[i - 1];
  4190.  
  4191.   defun_args[i] = savestring (string);
  4192. }
  4193.  
  4194. /* Make the defun type insertion.
  4195.    TYPE says which insertion this is.
  4196.    TITLE is the string to describe the object being described, or NULL
  4197.    for no title string.
  4198.    X_P says not to start a new insertion if non-zero. */
  4199. defun_internal (type, title, x_p)
  4200.      enum insertion_type type;
  4201.      char *title;
  4202.      int x_p;
  4203. {
  4204.   register int i = 0;
  4205.   char *type_name, *func_name;
  4206.   int old_no_indent = no_indent;
  4207.  
  4208.   get_defun_args ();
  4209.  
  4210.   if (title)
  4211.     insert_defun_arg (title, 0);
  4212.  
  4213.   if (defun_args[0])
  4214.     {
  4215.       type_name = defun_args[0];
  4216.       i++;
  4217.  
  4218.       if (defun_args[1])
  4219.     {
  4220.       func_name = defun_args[1];
  4221.       i++;
  4222.     }
  4223.       else
  4224.     func_name = "";
  4225.     }
  4226.   else
  4227.     type_name = "";
  4228.  
  4229.   no_indent = true;
  4230.   start_paragraph ();
  4231.   execute_string (" * %s: %s", type_name, func_name);
  4232.   no_indent = old_no_indent;
  4233.  
  4234.   for (; defun_args[i]; i++)
  4235.     {
  4236.       if (*defun_args[i] == '&')
  4237.     add_word_args (" %s", defun_args[i]);
  4238.       else
  4239.     execute_string (" @var{%s}", defun_args[i]);
  4240.     }
  4241.   add_char ('\n');
  4242.   if (type == defvar || type == defopt)
  4243.     execute_string ("@vindex %s\n", func_name);
  4244.   else
  4245.     execute_string ("@findex %s\n", func_name);
  4246.  
  4247.   if (!x_p)
  4248.     begin_insertion (type);
  4249.   else
  4250.     start_paragraph ();
  4251. }
  4252.  
  4253. /* Add an entry for a function, macro, special form, variable, or option.
  4254.    If the name of the calling command ends in `x', then this is an extra
  4255.    entry included in the body of an insertion of the same type. */
  4256. cm_defun ()
  4257. {
  4258.   int x_p;
  4259.   enum insertion_type type;
  4260.   char *title, *temp = savestring (command);
  4261.  
  4262.   x_p = (command[strlen (command) - 1] == 'x');
  4263.  
  4264.   if (x_p)
  4265.     temp[strlen (temp) - 1] = '\0';
  4266.  
  4267.   type = find_type_from_name (temp);
  4268.   free (temp);
  4269.  
  4270.   /* If we are adding to an already existing insertion, then make sure
  4271.      that we are already in an insertion of type TYPE. */
  4272.   if (x_p &&
  4273.       (!insertion_level || insertion_stack->insertion != type))
  4274.     {
  4275.       line_error ("Must be in a `%s' insertion in order to use `%s'x",
  4276.           command, command);
  4277.       return;
  4278.     }
  4279.  
  4280.   title = defun_title (type);
  4281.   defun_internal (type, title, x_p);
  4282. }
  4283.  
  4284. /* End existing insertion block. */
  4285. cm_end ()
  4286. {
  4287.   char *temp;
  4288.   enum insertion_type type;
  4289.  
  4290.   if (!insertion_level)
  4291.     {
  4292.       line_error ("Unmatched `@%s'", command);
  4293.       return;
  4294.     }
  4295.   get_rest_of_line (&temp);
  4296.   canon_white (temp);
  4297.  
  4298.   if (strlen (temp) == 0)
  4299.     line_error ("`@%s' needs something after it", command);
  4300.   type = find_type_from_name (temp);
  4301.   if (type == bad_type)
  4302.     {
  4303.       line_error ("Bad argument to `%s', `%s', using `%s'",
  4304.        command, temp, insertion_type_pname (current_insertion_type ()));
  4305.     }
  4306.   end_insertion (type);
  4307.   free (temp);
  4308. }
  4309.  
  4310.  
  4311. /* **************************************************************** */
  4312. /*                                    */
  4313. /*            Other Random Commands                   */
  4314. /*                                    */
  4315. /* **************************************************************** */
  4316.  
  4317. /* noindent () used to do something valueable, but it didn't follow the
  4318.    spec for noindent in the texinfo manual.  Now it does nothing, which,
  4319.    in the case of makeinfo, is correct. */
  4320. cm_noindent ()
  4321. {
  4322. /*  no_indent = true;
  4323.   indented_fill = false; */
  4324. }
  4325.  
  4326. /* I don't know exactly what to do with this.  Should I allow
  4327.    someone to switch filenames in the middle of output?  Since the
  4328.    file could be partially written, this doesn't seem to make sense.
  4329.    Another option: ignore it, since they don't *really* want to
  4330.    switch files.  Finally, complain, or at least warn. */
  4331. cm_setfilename ()
  4332. {
  4333.   char *filename;
  4334.   get_rest_of_line (&filename);
  4335.   /* warning ("`@%s %s' encountered and ignored", command, filename); */
  4336.   free (filename);
  4337. }
  4338.  
  4339. cm_comment ()
  4340. {
  4341.   discard_until ("\n");
  4342. }
  4343.  
  4344. cm_br ()
  4345. {
  4346.   close_paragraph ();
  4347. }
  4348.  
  4349.  /* Insert the number of blank lines passed as argument. */
  4350. cm_sp ()
  4351. {
  4352.   int lines;
  4353.   char *line;
  4354.  
  4355. /*  close_paragraph (); */
  4356.   get_rest_of_line (&line);
  4357.  
  4358.   sscanf (line, "%d", &lines);
  4359.   while (lines--)
  4360.     add_char ('\n');
  4361.   free (line);
  4362. }
  4363.  
  4364. cm_settitle ()
  4365. {
  4366.   discard_until ("\n");
  4367. }
  4368.  
  4369. cm_need ()
  4370. {
  4371. }
  4372.  
  4373. /* Start a new line with just this text on it.
  4374.    Then center the line of text.
  4375.    This always ends the current paragraph. */
  4376. cm_center ()
  4377. {
  4378.   char *line;
  4379.  
  4380.   close_paragraph ();
  4381.   filling_enabled = indented_fill = false;
  4382.  
  4383.   get_rest_of_line (&line);
  4384.  
  4385.   if (strlen (line) < fill_column)
  4386.     {
  4387.       int i = (fill_column - strlen (line)) / 2;
  4388.       while (i--)
  4389.     insert (' ');
  4390.     }
  4391.   execute_string (line);
  4392.   free (line);
  4393.   insert ('\n');
  4394.   close_paragraph ();
  4395.   filling_enabled = true;
  4396. }
  4397.  
  4398. /* Show what an expression returns. */
  4399. cm_result (arg)
  4400.      int arg;
  4401. {
  4402.   if (arg == END)
  4403.     add_word ("=>");
  4404. }
  4405.  
  4406. /* What an expression expands to. */
  4407. cm_expansion (arg)
  4408.      int arg;
  4409. {
  4410.   if (arg == END)
  4411.     add_word ("==>");
  4412. }
  4413.  
  4414. /* Indicates two expressions are equivalent. */
  4415. cm_equiv (arg)
  4416.      int arg;
  4417. {
  4418.   if (arg == END)
  4419.     add_word ("==");
  4420. }
  4421.  
  4422. /* What an expression may print. */
  4423. cm_print (arg)
  4424.      int arg;
  4425. {
  4426.   if (arg == END)
  4427.     add_word ("-|");
  4428. }
  4429.  
  4430. /* An error signaled. */
  4431. cm_error (arg)
  4432.      int arg;
  4433. {
  4434.   if (arg == END)
  4435.     add_word ("error-->");
  4436. }
  4437.  
  4438. /* The location of point in an example of a buffer. */
  4439. cm_point (arg)
  4440.      int arg;
  4441. {
  4442.   if (arg == END)
  4443.     add_word ("-!-");
  4444. }
  4445.  
  4446. /* Start a new line with just this text on it.
  4447.    The text is outdented one level if possible. */
  4448. cm_exdent ()
  4449. {
  4450.   char *line;
  4451.   int i = current_indent;
  4452.  
  4453.   if (current_indent)
  4454.     current_indent -= default_indentation_increment;
  4455.  
  4456.   get_rest_of_line (&line);
  4457.   close_single_paragraph ();
  4458.   add_word_args ("%s", line);
  4459.   current_indent = i;
  4460.   free (line);
  4461.   close_single_paragraph ();
  4462. }
  4463.  
  4464. cm_include ()
  4465. {
  4466.   cm_infoinclude ();
  4467. }
  4468.  
  4469. /* Remember this file, and move onto the next. */
  4470. cm_infoinclude ()
  4471. {
  4472.   char *filename;
  4473.  
  4474.   close_paragraph ();
  4475.   get_rest_of_line (&filename);
  4476.   pushfile ();
  4477.  
  4478.   /* In verbose mode we print info about including another file. */
  4479.   if (verbose_mode)
  4480.     {
  4481.       register int i = 0;
  4482.       register FSTACK *stack = filestack;
  4483.  
  4484.       for (i = 0, stack = filestack; stack; stack = stack->next, i++);
  4485.  
  4486.       i *= 2;
  4487.  
  4488.       printf ("%*s", i, "");
  4489.       printf ("%c%s %s\n", COMMAND_PREFIX, command, filename);
  4490.       fflush (stdout);
  4491.     }
  4492.  
  4493.   if (!find_and_load (filename))
  4494.     {
  4495.       extern char *sys_errlist[];
  4496.       extern int errno, sys_nerr;
  4497.       popfile ();
  4498.  
  4499.       /* Cannot "@include foo", in line 5 of "/wh/bar". */
  4500.       line_error ("`%c%s %s': %s", COMMAND_PREFIX, command, filename,
  4501.           ((errno < sys_nerr) ?
  4502.            sys_errlist[errno] : "Unknown file system error"));
  4503.     }
  4504.   free (filename);
  4505. }
  4506.  
  4507. /* The other side of a malformed expression. */
  4508. misplaced_brace ()
  4509. {
  4510.   line_error ("Misplaced `}'");
  4511. }
  4512.  
  4513. /* Don't let the filling algorithm insert extra whitespace here. */
  4514. cm_force_abbreviated_whitespace ()
  4515. {
  4516. }
  4517.  
  4518. /* Make the output paragraph end the sentence here, even though it
  4519.    looks like it shouldn't.  This also inserts the character which
  4520.    invoked it. */
  4521. cm_force_sentence_end ()
  4522. {
  4523.   add_char (META ((*command)));
  4524. }
  4525.  
  4526. /* Signals end of processing.  Easy to make this happen. */
  4527. cm_bye ()
  4528. {
  4529.   input_text_offset = size_of_input_text;
  4530. }
  4531.  
  4532. cm_asis ()
  4533. {
  4534. }
  4535.  
  4536. cm_setchapternewpage ()
  4537. {
  4538.   discard_until ("\n");
  4539. }
  4540.  
  4541. cm_smallbook ()
  4542. {
  4543.   discard_until ("\n");
  4544. }
  4545.  
  4546. /* **************************************************************** */
  4547. /*                                    */
  4548. /*            Indexing Stuff                    */
  4549. /*                                    */
  4550. /* **************************************************************** */
  4551.  
  4552.  
  4553. /* An index element... */
  4554. typedef struct index_elt
  4555. {
  4556.   struct index_elt *next;
  4557.   char *entry;            /* The index entry itself. */
  4558.   char *node;            /* The node from whence it came. */
  4559.   int code;            /* Non-zero means add `@code{...}' when
  4560.                    printing this element. */
  4561. } INDEX_ELT;
  4562.  
  4563. /* A list of short-names for each index, and the index to that index in our
  4564.    index array, the_indices.  In addition, for each index, it is remembered
  4565.    whether that index is a code index or not.  Code indices have @code{}
  4566.    inserted around the first word when they are printed with printindex. */
  4567. typedef struct
  4568. {
  4569.   char *name;
  4570.   int index;
  4571.   int code;
  4572. } INDEX_ALIST;
  4573.  
  4574. INDEX_ALIST **name_index_alist = (INDEX_ALIST **) NULL;
  4575.  
  4576. /* An array of pointers.  Each one is for a different index.  The
  4577.    "synindex" command changes which array slot is pointed to by a
  4578.    given "index". */
  4579. INDEX_ELT **the_indices = (INDEX_ELT **) NULL;
  4580.  
  4581. /* The number of defined indices. */
  4582. int defined_indices = 0;
  4583.  
  4584. /* We predefine these. */
  4585. #define program_index 0
  4586. #define function_index 1
  4587. #define concept_index 2
  4588. #define variable_index 3
  4589. #define datatype_index 4
  4590. #define key_index 5
  4591.  
  4592. init_indices ()
  4593. {
  4594.   int i;
  4595.  
  4596.   /* Create the default data structures. */
  4597.  
  4598.   /* Initialize data space. */
  4599.   if (!the_indices)
  4600.     {
  4601.       the_indices = (INDEX_ELT **) xmalloc ((1 + defined_indices) *
  4602.                         sizeof (INDEX_ELT *));
  4603.       the_indices[defined_indices] = (INDEX_ELT *) NULL;
  4604.  
  4605.       name_index_alist = (INDEX_ALIST **) xmalloc ((1 + defined_indices) *
  4606.                            sizeof (INDEX_ALIST *));
  4607.       name_index_alist[defined_indices] = (INDEX_ALIST *) NULL;
  4608.     }
  4609.  
  4610.   /* If there were existing indices, get rid of them now. */
  4611.   for (i = 0; i < defined_indices; i++)
  4612.     undefindex (name_index_alist[i]->name);
  4613.  
  4614.   /* Add the default indices. */
  4615.   defindex ("pg", 0);
  4616.   defindex ("fn", 1);        /* "fn" is a code index.  */
  4617.   defindex ("cp", 0);
  4618.   defindex ("vr", 0);
  4619.   defindex ("tp", 0);
  4620.   defindex ("ky", 0);
  4621.  
  4622. }
  4623.  
  4624. /* Find which element in the known list of indices has this name.
  4625.    Returns -1 if NAME isn't found. */
  4626. int
  4627. find_index_offset (name)
  4628.      char *name;
  4629. {
  4630.   register int i;
  4631.   for (i = 0; i < defined_indices; i++)
  4632.     if (name_index_alist[i] &&
  4633.     stricmp (name, name_index_alist[i]->name) == 0)
  4634.       return (name_index_alist[i]->index);
  4635.   return (-1);
  4636. }
  4637.  
  4638. /* Return a pointer to the entry of (name . index) for this name.
  4639.    Return NULL if the index doesn't exist. */
  4640. INDEX_ALIST *
  4641. find_index (name)
  4642.      char *name;
  4643. {
  4644.   int offset = find_index_offset (name);
  4645.   if (offset > -1)
  4646.     return (name_index_alist[offset]);
  4647.   else
  4648.     return ((INDEX_ALIST *) NULL);
  4649. }
  4650.  
  4651. /* Given an index name, return the offset in the_indices of this index,
  4652.    or -1 if there is no such index. */
  4653. translate_index (name)
  4654.      char *name;
  4655. {
  4656.   INDEX_ALIST *which = find_index (name);
  4657.  
  4658.   if (which)
  4659.     return (which->index);
  4660.   else
  4661.     return (-1);
  4662. }
  4663.  
  4664. /* Return the index list which belongs to NAME. */
  4665. INDEX_ELT *
  4666. index_list (name)
  4667.      char *name;
  4668. {
  4669.   int which = translate_index (name);
  4670.   if (which < 0)
  4671.     return ((INDEX_ELT *) - 1);
  4672.   else
  4673.     return (the_indices[which]);
  4674. }
  4675.  
  4676. /* Please release me, let me go... */
  4677. free_index (index)
  4678.      INDEX_ELT *index;
  4679. {
  4680.   INDEX_ELT *temp;
  4681.  
  4682.   while ((temp = index) != (INDEX_ELT *) NULL)
  4683.     {
  4684.       free (temp->entry);
  4685.       free (temp->node);
  4686.       index = index->next;
  4687.       free (temp);
  4688.     }
  4689. }
  4690.  
  4691. /* Flush an index by name. */
  4692. undefindex (name)
  4693.      char *name;
  4694. {
  4695.   int i;
  4696.   int which = find_index_offset (name);
  4697.  
  4698.   if (which < 0)
  4699.     return (which);
  4700.  
  4701.   i = name_index_alist[which]->index;
  4702.  
  4703.  
  4704.   free_index (the_indices[i]);
  4705.   the_indices[i] = (INDEX_ELT *) NULL;
  4706.  
  4707.   free (name_index_alist[which]->name);
  4708.   free (name_index_alist[which]);
  4709.   name_index_alist[which] = (INDEX_ALIST *) NULL;
  4710. }
  4711.  
  4712. /* Define an index known as NAME.  We assign the slot number.
  4713.    CODE if non-zero says to make this a code index. */
  4714. defindex (name, code)
  4715.      char *name;
  4716.      int code;
  4717. {
  4718.   register int i, slot;
  4719.  
  4720.   /* If it already exists, flush it. */
  4721.   undefindex (name);
  4722.  
  4723.   /* Try to find an empty slot. */
  4724.   slot = -1;
  4725.   for (i = 0; i < defined_indices; i++)
  4726.     if (!name_index_alist[i])
  4727.       {
  4728.     slot = i;
  4729.     break;
  4730.       }
  4731.  
  4732.   if (slot < 0)
  4733.     {
  4734.       /* No such luck.  Make space for another index. */
  4735.       slot = defined_indices;
  4736.       defined_indices++;
  4737.  
  4738.       name_index_alist = (INDEX_ALIST **) xrealloc (name_index_alist,
  4739.                             (1 + defined_indices)
  4740.                           * sizeof (INDEX_ALIST *));
  4741.       the_indices = (INDEX_ELT **) xrealloc (the_indices,
  4742.                          (1 + defined_indices)
  4743.                          * sizeof (INDEX_ELT *));
  4744.     }
  4745.  
  4746.   /* We have a slot.  Start assigning. */
  4747.   name_index_alist[slot] = (INDEX_ALIST *) xmalloc (sizeof (INDEX_ALIST));
  4748.   name_index_alist[slot]->name = savestring (name);
  4749.   name_index_alist[slot]->index = slot;
  4750.   name_index_alist[slot]->code = code;
  4751.  
  4752.   the_indices[slot] = (INDEX_ELT *) NULL;
  4753. }
  4754.  
  4755. /* Add the arguments to the current index command to the index NAME. */
  4756. index_add_arg (name)
  4757.      char *name;
  4758. {
  4759.   int which;
  4760.   char *index_entry;
  4761.   INDEX_ALIST *tem;
  4762.  
  4763.   tem = find_index (name);
  4764.  
  4765.   which = tem ? tem->index : -1;
  4766.  
  4767.   /* close_paragraph (); */
  4768.   get_rest_of_line (&index_entry);
  4769.  
  4770.   if (which < 0)
  4771.     {
  4772.       line_error ("Unknown index reference `%s'", name);
  4773.       free (index_entry);
  4774.     }
  4775.   else
  4776.     {
  4777.       INDEX_ELT *new = (INDEX_ELT *) xmalloc (sizeof (INDEX_ELT));
  4778.       new->next = the_indices[which];
  4779.       new->entry = index_entry;
  4780.       new->node = current_node;
  4781.       new->code = tem->code;
  4782.       the_indices[which] = new;
  4783.     }
  4784. }
  4785.  
  4786. #define INDEX_COMMAND_SUFFIX "index"
  4787.  
  4788. /* The function which user defined index commands call. */
  4789. gen_index ()
  4790. {
  4791.   char *name = savestring (command);
  4792.   if (strlen (name) >= strlen ("index"))
  4793.     name[strlen (name) - strlen ("index")] = '\0';
  4794.   index_add_arg (name);
  4795.   free (name);
  4796. }
  4797.  
  4798. /* Define a new index command.  Arg is name of index. */
  4799. cm_defindex ()
  4800. {
  4801.   gen_defindex (0);
  4802. }
  4803.  
  4804. cm_defcodeindex ()
  4805. {
  4806.   gen_defindex (1);
  4807. }
  4808.  
  4809. gen_defindex (code)
  4810.      int code;
  4811. {
  4812.   char *name;
  4813.   get_rest_of_line (&name);
  4814.  
  4815.   if (find_index (name))
  4816.     {
  4817.       line_error ("Index `%s' already exists", name);
  4818.       free (name);
  4819.       return;
  4820.     }
  4821.   else
  4822.     {
  4823.       char *temp = (char *) alloca (1 + strlen (name) + strlen ("index"));
  4824.       sprintf (temp, "%sindex", name);
  4825.       define_user_command (temp, gen_index, 0);
  4826.       defindex (name, code);
  4827.       free (name);
  4828.     }
  4829. }
  4830.  
  4831. /* Append LIST2 to LIST1.  Return the head of the list. */
  4832. INDEX_ELT *
  4833. index_append (head, tail)
  4834.      INDEX_ELT *head, *tail;
  4835. {
  4836.   register INDEX_ELT *t_head = head;
  4837.  
  4838.   if (!t_head)
  4839.     return (tail);
  4840.  
  4841.   while (t_head->next)
  4842.     t_head = t_head->next;
  4843.   t_head->next = tail;
  4844.   return (head);
  4845. }
  4846.  
  4847. /* Expects 2 args, on the same line.  Both are index abbreviations.
  4848.    Make the first one be a synonym for the second one, i.e. make the
  4849.    first one have the same index as the second one. */
  4850. cm_synindex ()
  4851. {
  4852.   int redirector, redirectee;
  4853.   char *temp;
  4854.  
  4855.   skip_whitespace ();
  4856.   get_until_in_line (" ", &temp);
  4857.   redirectee = find_index_offset (temp);
  4858.   skip_whitespace ();
  4859.   free_and_clear (&temp);
  4860.   get_until_in_line (" ", &temp);
  4861.   redirector = find_index_offset (temp);
  4862.   free (temp);
  4863.   if (redirector < 0 || redirectee < 0)
  4864.     {
  4865.       line_error ("Unknown index reference");
  4866.     }
  4867.   else
  4868.     {
  4869.       /* I think that we should let the user make indices synonymous to
  4870.          each other without any lossage of info.  This means that one can
  4871.          say @synindex cp dt anywhere in the file, and things that used to
  4872.          be in cp will go into dt. */
  4873.       INDEX_ELT *i1 = the_indices[redirectee], *i2 = the_indices[redirector];
  4874.  
  4875.       if (i1 || i2)
  4876.     {
  4877.       if (i1)
  4878.         the_indices[redirectee] = index_append (i1, i2);
  4879.       else
  4880.         the_indices[redirectee] = index_append (i2, i1);
  4881.     }
  4882.  
  4883.       name_index_alist[redirectee]->index =
  4884.     name_index_alist[redirector]->index;
  4885.     }
  4886. }
  4887.  
  4888. cm_pindex ()            /* Pinhead index. */
  4889. {
  4890.   index_add_arg ("pg");
  4891. }
  4892.  
  4893. cm_vindex ()            /* variable index */
  4894. {
  4895.   index_add_arg ("vr");
  4896. }
  4897.  
  4898. cm_kindex ()            /* key index */
  4899. {
  4900.   index_add_arg ("ky");
  4901. }
  4902.  
  4903. cm_cindex ()            /* concept index */
  4904. {
  4905.   index_add_arg ("cp");
  4906. }
  4907.  
  4908. cm_findex ()            /* function index */
  4909. {
  4910.   index_add_arg ("fn");
  4911. }
  4912.  
  4913. cm_tindex ()            /* data type index */
  4914. {
  4915.   index_add_arg ("tp");
  4916. }
  4917.  
  4918. /* Sorting the index. */
  4919. index_element_compare (element1, element2)
  4920.      INDEX_ELT **element1, **element2;
  4921. {
  4922.   /* This needs to ignore leading non-text characters. */
  4923.   return (strcmp ((*element1)->entry, (*element2)->entry));
  4924. }
  4925.  
  4926. /* Sort the index passed in INDEX, returning an array of
  4927.    pointers to elements.  The array is terminated with a NULL
  4928.    pointer.  We call qsort because it's supposed to be fast.
  4929.    I think this looks bad. */
  4930. INDEX_ELT **
  4931. sort_index (index)
  4932.      INDEX_ELT *index;
  4933. {
  4934.   INDEX_ELT *temp = index;
  4935.   INDEX_ELT **array;
  4936.   int count = 0;
  4937.  
  4938.   while (temp != (INDEX_ELT *) NULL)
  4939.     {
  4940.       count++;
  4941.       temp = temp->next;
  4942.     }
  4943.  
  4944.   /* We have the length.  Make an array. */
  4945.  
  4946.   array = (INDEX_ELT **) xmalloc ((count + 1) * sizeof (INDEX_ELT *));
  4947.   count = 0;
  4948.   temp = index;
  4949.  
  4950.   while (temp != (INDEX_ELT *) NULL)
  4951.     {
  4952.       array[count++] = temp;
  4953.       temp = temp->next;
  4954.     }
  4955.   array[count] = (INDEX_ELT *) NULL;    /* terminate the array. */
  4956.  
  4957.   /* Sort the array. */
  4958.   qsort (array, count, sizeof (INDEX_ELT *), index_element_compare);
  4959.  
  4960.   return (array);
  4961. }
  4962.  
  4963. /* Non-zero means that we are in the middle of printing an index. */
  4964. int printing_index = 0;
  4965.  
  4966. /* Takes one arg, a short name of an index to print.
  4967.    Outputs a menu of the sorted elements of the index. */
  4968. cm_printindex ()
  4969. {
  4970.   int item;
  4971.   INDEX_ELT *index;
  4972.   INDEX_ELT **array;
  4973.   char *index_name;
  4974.   int old_inhibitions = inhibit_paragraph_indentation;
  4975.   boolean previous_filling_enabled_value = filling_enabled;
  4976.  
  4977.   close_paragraph ();
  4978.   get_rest_of_line (&index_name);
  4979.  
  4980.   index = index_list (index_name);
  4981.   if ((int) index < 0)
  4982.     {
  4983.       line_error ("Unknown index name `%s'", index_name);
  4984.       free (index_name);
  4985.       return;
  4986.     }
  4987.   else
  4988.     free (index_name);
  4989.  
  4990.   array = sort_index (index);
  4991.  
  4992.   filling_enabled = false;
  4993.   inhibit_paragraph_indentation = 1;
  4994.   close_paragraph ();
  4995.   add_word ("* Menu:\n\n");
  4996.  
  4997.   printing_index = 1;
  4998.   for (item = 0; (index = array[item]); item++)
  4999.     {
  5000.       /* If this particular entry should be printed as a "code" index,
  5001.      then wrap the entry with "@code{...}". */
  5002.       if (index->code)
  5003.     execute_string ("* @code{%s}: %s.\n", index->entry, index->node);
  5004.       else
  5005.     execute_string ("* %s: %s.\n", index->entry, index->node);
  5006.       flush_output ();
  5007.     }
  5008.   printing_index = 0;
  5009.   free (array);
  5010.   close_paragraph ();
  5011.   filling_enabled = previous_filling_enabled_value;
  5012.   inhibit_paragraph_indentation = old_inhibitions;
  5013. }
  5014.  
  5015.  
  5016. /* **************************************************************** */
  5017. /*                                    */
  5018. /*            Making User Defined Commands            */
  5019. /*                                    */
  5020. /* **************************************************************** */
  5021.  
  5022. define_user_command (name, proc, needs_braces_p)
  5023.      char *name;
  5024.      FUNCTION *proc;
  5025.      int needs_braces_p;
  5026. {
  5027.   int slot = user_command_array_len;
  5028.   user_command_array_len++;
  5029.  
  5030.   if (!user_command_array)
  5031.     user_command_array = (COMMAND **) xmalloc (1 * sizeof (COMMAND *));
  5032.  
  5033.   user_command_array = (COMMAND **) xrealloc (user_command_array,
  5034.                           (1 + user_command_array_len) *
  5035.                           sizeof (COMMAND *));
  5036.  
  5037.   user_command_array[slot] = (COMMAND *) xmalloc (sizeof (COMMAND));
  5038.   user_command_array[slot]->name = savestring (name);
  5039.   user_command_array[slot]->proc = proc;
  5040.   user_command_array[slot]->argument_in_braces = needs_braces_p;
  5041. }
  5042.  
  5043. /* Make ALIAS run the named FUNCTION.  Copies properties from FUNCTION. */
  5044. define_alias (alias, function)
  5045.      char *alias, *function;
  5046. {
  5047. }
  5048.  
  5049. /* Some support for footnotes. */
  5050.  
  5051. /* Footnotes are a new construct in Info.  We don't know the best method
  5052.    of implementing them for sure, so we present two possiblities.
  5053.  
  5054. MN   1) Make them look like followed references, with the reference
  5055.         destinations in a makeinfo manufactured node or,
  5056.  
  5057. BN   2) Make them appear at the bottom of the node that they originally
  5058.         appeared in.
  5059. */
  5060.  
  5061. #define MN 0
  5062. #define BN 1
  5063.  
  5064. int footnote_style = MN;
  5065. boolean first_footnote_this_node = true;
  5066. int footnote_count = 0;
  5067.  
  5068. /* Set the footnote style based on he style identifier in STRING. */
  5069. set_footnote_style (string)
  5070.      char *string;
  5071. {
  5072.   if (stricmp (string, "MN") == 0)
  5073.     {
  5074.       footnote_style = MN;
  5075.       return;
  5076.     }
  5077.  
  5078.   if (stricmp (string, "BN") == 0)
  5079.     {
  5080.       footnote_style = BN;
  5081.       return;
  5082.     }
  5083. }
  5084.  
  5085. typedef struct fn
  5086. {
  5087.   struct fn *next;
  5088.   char *marker;
  5089.   char *note;
  5090. }  FN;
  5091.  
  5092. FN *pending_notes = (FN *) NULL;
  5093.  
  5094. /* A method for remembering footnotes.  Note that this list gets output
  5095.    at the end of the current node. */
  5096. remember_note (marker, note)
  5097.      char *marker, *note;
  5098. {
  5099.   FN *temp = (FN *) xmalloc (sizeof (FN));
  5100.  
  5101.   temp->marker = savestring (marker);
  5102.   temp->note = savestring (note);
  5103.   temp->next = pending_notes;
  5104.   pending_notes = temp;
  5105.   footnote_count++;
  5106. }
  5107.  
  5108. /* How to get rid of existing footnotes. */
  5109. free_pending_notes ()
  5110. {
  5111.   FN *temp;
  5112.  
  5113.   while ((temp = pending_notes) != (FN *) NULL)
  5114.     {
  5115.       free (temp->marker);
  5116.       free (temp->note);
  5117.       pending_notes = pending_notes->next;
  5118.       free (temp);
  5119.     }
  5120.   first_footnote_this_node = true;
  5121.   footnote_count = 0;
  5122. }
  5123.  
  5124. /* What to do when you see a @footnote construct. */
  5125.  
  5126.  /* Handle a "footnote".
  5127.     footnote *{this is a footnote}
  5128.     where "*" is the marker character for this note. */
  5129. cm_footnote ()
  5130. {
  5131.   char *marker;
  5132.   char *note;
  5133.  
  5134.   get_until ("{", &marker);
  5135.   canon_white (marker);
  5136.  
  5137.   /* Read the argument in braces. */
  5138.   if (curchar () != '{')
  5139.     {
  5140.       line_error ("`@%s' expected more than just `%s'.  It needs something in `{...}'", command, marker);
  5141.       free (marker);
  5142.       return;
  5143.     }
  5144.   else
  5145.     {
  5146.       int braces = 1;
  5147.       int temp = ++input_text_offset;
  5148.       int len;
  5149.  
  5150.       while (braces)
  5151.     {
  5152.       if (temp == size_of_input_text)
  5153.         {
  5154.           line_error ("No closing brace for footnote `%s'", marker);
  5155.           return;
  5156.         }
  5157.       if (input_text[temp] == '{')
  5158.         braces++;
  5159.       else if (input_text[temp] == '}')
  5160.         braces--;
  5161.       temp++;
  5162.     }
  5163.  
  5164.       len = (temp - input_text_offset) - 1;
  5165.       note = xmalloc (len + 1);
  5166.       strncpy (note, &input_text[input_text_offset], len);
  5167.       note[len] = '\0';
  5168.       input_text_offset = temp;
  5169.     }
  5170.  
  5171.   if (!current_node || !*current_node)
  5172.     {
  5173.       line_error ("Footnote defined without parent node");
  5174.       free (marker);
  5175.       free (note);
  5176.       return;
  5177.     }
  5178.  
  5179.   remember_note (marker, note);
  5180.  
  5181.   switch (footnote_style)
  5182.     {                /* your method should at least insert marker. */
  5183.  
  5184.     case MN:
  5185.       add_word_args ("(%s)", marker);
  5186.       if (first_footnote_this_node)
  5187.     {
  5188.       char *temp_string = xmalloc ((strlen (current_node))
  5189.                        + (strlen ("-Footnotes")) + 1);
  5190.       add_word_args (" (*note %s-Footnotes::)", current_node);
  5191.       strcpy (temp_string, current_node);
  5192.       strcat (temp_string, "-Footnotes");
  5193.       remember_node_reference (temp_string, line_number, followed_reference);
  5194.       free (temp_string);
  5195.       first_footnote_this_node = false;
  5196.     }
  5197.       break;
  5198.  
  5199.     case BN:
  5200.       add_word_args ("(%s)", marker);
  5201.       break;
  5202.  
  5203.     default:
  5204.       break;
  5205.     }
  5206.   free (marker);
  5207.   free (note);
  5208. }
  5209.  
  5210. /* Non-zero means that we are currently in the process of outputting
  5211.    footnotes. */
  5212. int already_outputting_pending_notes = 0;
  5213.  
  5214. /* Output the footnotes.  We are at the end of the current node. */
  5215. output_pending_notes ()
  5216. {
  5217.   FN *footnote = pending_notes;
  5218.  
  5219.   if (!pending_notes)
  5220.     return;
  5221.  
  5222.   switch (footnote_style)
  5223.     {
  5224.  
  5225.     case MN:
  5226.       {
  5227.     char *old_current_node = current_node;
  5228.     char *old_command = savestring (command);
  5229.  
  5230.     already_outputting_pending_notes++;
  5231.     execute_string ("@node %s-Footnotes,,,%s\n", current_node, current_node);
  5232.     already_outputting_pending_notes--;
  5233.     current_node = old_current_node;
  5234.     free (command);
  5235.     command = old_command;
  5236.       }
  5237.       break;
  5238.  
  5239.     case BN:
  5240.       close_paragraph ();
  5241.       in_fixed_width_font++;
  5242.       execute_string ("---------- Footnotes ----------\n\n");
  5243.       in_fixed_width_font--;
  5244.       break;
  5245.     }
  5246.  
  5247.   /* Handle the footnotes in reverse order. */
  5248.   {
  5249.     FN **array = (FN **) xmalloc ((footnote_count + 1) * sizeof (FN *));
  5250.  
  5251.     array[footnote_count] = (FN *) NULL;
  5252.  
  5253.     while (--footnote_count > -1)
  5254.       {
  5255.     array[footnote_count] = footnote;
  5256.     footnote = footnote->next;
  5257.       }
  5258.  
  5259.     filling_enabled = true;
  5260.     indented_fill = true;
  5261.  
  5262.     while (footnote = array[++footnote_count])
  5263.       {
  5264.  
  5265.     switch (footnote_style)
  5266.       {
  5267.  
  5268.       case MN:
  5269.       case BN:
  5270.         execute_string ("(%s)  %s", footnote->marker, footnote->note);
  5271.         close_paragraph ();
  5272.         break;
  5273.       }
  5274.       }
  5275.     close_paragraph ();
  5276.     free (array);
  5277.   }
  5278. }
  5279.  
  5280. /*
  5281.  * Local variables:
  5282.  * compile-command: "gcc -g -Bstatic -o makeinfo makeinfo.c getopt.c"
  5283.  * end:
  5284.  */
  5285.  
  5286.