home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / texinfo-3.7-src.tgz / tar.out / fsf / texinfo / makeinfo / makeinfo.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  259KB  |  10,471 lines

  1. /* Makeinfo -- convert texinfo format files into info files.
  2.  
  3.    Copyright (C) 1987, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
  4.  
  5.    This file is part of GNU Info.
  6.  
  7.    Makeinfo is distributed in the hope that it will be useful,
  8.    but WITHOUT ANY WARRANTY.  No author or distributor accepts
  9.    responsibility to anyone for the consequences of using it or for
  10.    whether it serves any particular purpose or works at all, unless he
  11.    says so in writing.  Refer to the GNU Emacs General Public License
  12.    for full details.
  13.  
  14.    Everyone is granted permission to copy, modify and redistribute
  15.    Makeinfo, but only under the conditions described in the GNU Emacs
  16.    General Public License.   A copy of this license is supposed to
  17.    have been given to you along with GNU Emacs so you can know your
  18.    rights and responsibilities.  It should be in a file named COPYING.
  19.    Among other things, the copyright notice and this notice must be
  20.    preserved on all copies.  */
  21.  
  22. /* This is Makeinfo version 1.64.  If you change the version number of
  23.    Makeinfo, please change it here and at the lines reading:
  24.  
  25.     int major_version = 1;
  26.     int minor_version = 64;
  27.  
  28.    in the code below.
  29.  
  30.    Makeinfo is authored by Brian Fox (bfox@ai.mit.edu). */
  31.  
  32. /* You can change some of the behaviour of Makeinfo by changing the
  33.    following defines: */
  34.  
  35. /* Define INDENT_PARAGRAPHS_IN_TABLE if you want the paragraphs which
  36.    appear within an @table, @ftable, or @itemize environment to have
  37.    standard paragraph indentation.  Without this, such paragraphs have
  38.    no starting indentation. */
  39. /* #define INDENT_PARAGRAPHS_IN_TABLE */
  40.  
  41. /* Define DEFAULT_INDENTATION_INCREMENT as an integer which is the amount
  42.    that @example should increase indentation by.  This incremement is used
  43.    for all insertions which indent the enclosed text. */
  44. #define DEFAULT_INDENTATION_INCREMENT 5
  45.  
  46. /* Define PARAGRAPH_START_INDENT to be the amount of indentation that
  47.    the first lines of paragraphs receive by default, where no other
  48.    value has been specified.  Users can change this value on the command
  49.    line, with the --paragraph-indent option, or within the texinfo file,
  50.    with the @paragraphindent command. */
  51. #define PARAGRAPH_START_INDENT 3
  52.  
  53. /* Define DEFAULT_PARAGRAPH_SPACING as the number of blank lines that you
  54.    wish to appear between paragraphs.  A value of 1 creates a single blank
  55.    line between paragraphs.  Paragraphs are defined by 2 or more consecutive
  56.    newlines in the input file (i.e., one or more blank lines). */
  57. #define DEFAULT_PARAGRAPH_SPACING 1
  58.  
  59. /* Define HAVE_MACROS to enable the macro facility of TeXinfo.  Using this
  60.    facility, users can create their own command procedures with arguments. */
  61. #define HAVE_MACROS
  62.  
  63. /* **************************************************************** */
  64. /*                                    */
  65. /*            Include File Declarations               */
  66. /*                                    */
  67. /* **************************************************************** */
  68.  
  69. /* Indent #pragma so that older Cpp's don't try to parse it. */
  70. #if defined (_AIX)
  71.  # pragma alloca
  72. #endif /* _AIX */
  73.  
  74. #include <stdio.h>
  75. #include <sys/types.h>
  76. #include <ctype.h>
  77. #include <sys/stat.h>
  78. #if !defined(__amigaos__)
  79. #include <pwd.h>
  80. #endif /* __amigaos__ */
  81. #include <errno.h>
  82.  
  83. #if defined (HAVE_VARARGS_H)
  84. #include <varargs.h>
  85. #endif /* HAVE_VARARGS_H */
  86. #include "getopt.h"
  87.  
  88. #if defined (HAVE_UNISTD_H)
  89. #include <unistd.h>
  90. #endif /* HAVE_UNISTD_H */
  91.  
  92. #if defined (VMS)
  93. #include <perror.h>
  94. #endif
  95.  
  96. #if defined (HAVE_STRING_H)
  97. #include <string.h>
  98. #else
  99. #include <strings.h>
  100. #endif /* !HAVE_STRING_H */
  101.  
  102. #if defined (TM_IN_SYS_TIME)
  103. #include <sys/time.h>
  104. #else
  105. #include <time.h>
  106. #endif /* !TM_IN_SYS_TIME */
  107.  
  108. #if defined (HAVE_SYS_FCNTL_H)
  109. #include <sys/fcntl.h>
  110. #else
  111. #include <fcntl.h>
  112. #endif /* !HAVE_SYS_FCNTL_H */
  113.  
  114. #if defined (HAVE_SYS_FILE_H)
  115. #include <sys/file.h>
  116. #endif /* HAVE_SYS_FILE_H */
  117.  
  118. #if defined (__GNUC__)
  119. #define alloca __builtin_alloca
  120. #else
  121. #if defined(HAVE_ALLOCA_H)
  122. #include <alloca.h>
  123. #else /* !HAVE_ALLOCA_H */
  124. #if !defined (_AIX)
  125. extern char *alloca ();
  126. #endif /* !_AIX */
  127. #endif /* !HAVE_ALLOCA_H */
  128. #endif /* !__GNUC__ */
  129.  
  130. void *xmalloc (), *xrealloc ();
  131. #if defined (__osf__)
  132. extern void *malloc (), *realloc ();
  133. #endif /* __osf__ */
  134.  
  135. char **get_brace_args ();
  136. int array_len ();
  137. void free_array ();
  138. static void isolate_nodename ();
  139.  
  140. #if !defined (HAVE_MEMMOVE)
  141. #  define memmove(dst, src, len) bcopy (src, dst, len)
  142. #endif
  143.  
  144. /* Non-zero means that we are currently hacking the insides of an
  145.    insertion which would use a fixed width font. */
  146. static int in_fixed_width_font = 0;
  147.  
  148. /* Non-zero means that start_paragraph () MUST be called before we pay
  149.    any attention to close_paragraph () calls. */
  150. int must_start_paragraph = 0;
  151.  
  152. /* Non-zero means a string is in execution, as opposed to a file. */
  153. static int executing_string = 0;
  154.  
  155. #if defined (HAVE_MACROS)
  156. /* If non-NULL, this is an output stream to write the full macro expansion
  157.    of the input text to.  The resultant file is another texinfo file, but
  158.    missing @include, @infoinclude, @macro, and macro invocations.  Instead,
  159.    all of the text is placed within the file. */
  160. FILE *macro_expansion_output_stream = (FILE *)NULL;
  161.  
  162. /* Here is a structure used to remember input text strings and offsets
  163.    within them. */
  164. typedef struct {
  165.   char *pointer;        /* Pointer to the input text. */
  166.   int offset;            /* Offset of the last character output. */
  167. } ITEXT;
  168.  
  169. static ITEXT **itext_info = (ITEXT **)NULL;
  170. static int itext_size = 0;
  171.  
  172. /* Non-zero means to inhibit the writing of macro expansions to the output
  173.    stream.  This is used in special cases where the output has already been
  174.    written. */
  175. int me_inhibit_expansion = 0;
  176.  
  177. ITEXT *remember_itext ();
  178. void forget_itext (), me_append_before_this_command ();
  179. void append_to_expansion_output (), write_region_to_macro_output ();
  180. void maybe_write_itext (), me_execute_string ();
  181. #endif /* HAVE_MACROS */
  182.  
  183. /* Some systems don't declare this function in pwd.h. */
  184. struct passwd *getpwnam ();
  185.  
  186.  
  187. /* **************************************************************** */
  188. /*                                    */
  189. /*                  Global Defines                  */
  190. /*                                    */
  191. /* **************************************************************** */
  192.  
  193. /* Error levels */
  194. #define NO_ERROR 0
  195. #define SYNTAX     2
  196. #define FATAL     4
  197.  
  198. /* C's standard macros don't check to make sure that the characters being
  199.    changed are within range.  So I have to check explicitly. */
  200.  
  201. /* GNU Library doesn't have toupper().  Until GNU gets this fixed, I will
  202.    have to do it. */
  203. #ifndef toupper
  204. #define toupper(c) ((c) - 32)
  205. #endif
  206.  
  207. #define coerce_to_upper(c) ((islower(c) ? toupper(c) : (c)))
  208. #define coerce_to_lower(c) ((isupper(c) ? tolower(c) : (c)))
  209.  
  210. #define control_character_bit 0x40 /* %01000000, must be off. */
  211. #define meta_character_bit 0x080/* %10000000, must be on.  */
  212. #define CTL(c) ((c) & (~control_character_bit))
  213. #define UNCTL(c) coerce_to_upper(((c)|control_character_bit))
  214. #define META(c) ((c) | (meta_character_bit))
  215. #define UNMETA(c) ((c) & (~meta_character_bit))
  216.  
  217. #define whitespace(c) (((c) == '\t') || ((c) == ' '))
  218. #define sentence_ender(c) ((c) == '.' || (c) == '?' || (c) == '!')
  219. #define cr_or_whitespace(c) (((c) == '\t') || ((c) == ' ') || ((c) == '\n'))
  220.  
  221. #ifndef isletter
  222. #define isletter(c) (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z'))
  223. #endif
  224.  
  225. #ifndef isupper
  226. #define isupper(c) ((c) >= 'A' && (c) <= 'Z')
  227. #endif
  228.  
  229. #ifndef isdigit
  230. #define isdigit(c)  ((c) >= '0' && (c) <= '9')
  231. #endif
  232.  
  233. #ifndef digit_value
  234. #define digit_value(c) ((c) - '0')
  235. #endif
  236.  
  237. #define member(c, s) (strchr (s, c) != NULL)
  238.  
  239. #define COMMAND_PREFIX '@'
  240.  
  241. /* Stuff for splitting large files. */
  242. #define SPLIT_SIZE_THRESHOLD 70000  /* What's good enough for Stallman... */
  243. #define DEFAULT_SPLIT_SIZE 50000    /* Is probably good enough for me. */
  244. int splitting = 1;            /* Always true for now. */
  245.  
  246. typedef void COMMAND_FUNCTION (); /* So I can say COMMAND_FUNCTION *foo; */
  247.  
  248.  
  249. /* **************************************************************** */
  250. /*                                    */
  251. /*                Global Variables                */
  252. /*                                    */
  253. /* **************************************************************** */
  254.  
  255. /* Global pointer to argv[0]. */
  256. char *progname;
  257.  
  258. /* The current input file state. */
  259. char *input_filename;
  260. char *input_text;
  261. int size_of_input_text;
  262. int input_text_offset;
  263. int line_number;
  264.  
  265. #define curchar() input_text[input_text_offset]
  266.  
  267. #define command_char(c) ((!whitespace(c)) && \
  268.              ((c) != '\n') && \
  269.              ((c) != '{') && \
  270.              ((c) != '}') && \
  271.              ((c) != '='))
  272.  
  273. #define skip_whitespace() while (input_text_offset != size_of_input_text \
  274.                  && whitespace(curchar()))\
  275.   input_text_offset++
  276.  
  277. #define skip_whitespace_and_newlines() \
  278.   do { \
  279.    while (input_text_offset != size_of_input_text \
  280.       && (whitespace (curchar ()) \
  281.           || (curchar () == '\n'))) \
  282.       { \
  283.      if (curchar () == '\n') \
  284.        line_number++; \
  285.      input_text_offset++; \
  286.       } \
  287.    } while (0)
  288.  
  289. /* Return non-zero if STRING is the text at input_text + input_text_offset,
  290.    else zero. */
  291. #define looking_at(string) \
  292.   (strncmp (input_text + input_text_offset, string, strlen (string)) == 0)
  293.  
  294. /* And writing to the output. */
  295.  
  296. /* The output file name. */
  297. char *output_filename = (char *)NULL;
  298. char *pretty_output_filename;
  299.  
  300. /* Name of the output file that the user elected to pass on the command line.
  301.    Such a name overrides any name found with the @setfilename command. */
  302. char *command_output_filename = (char *)NULL;
  303.  
  304. /* A colon separated list of directories to search for files included
  305.    with @include.  This can be controlled with the `-I' option to makeinfo. */
  306. char *include_files_path = (char *)NULL;
  307.  
  308. /* Current output stream. */
  309. FILE *output_stream;
  310.  
  311. /* Position in the output file. */
  312. int output_position;
  313.  
  314. /* Output paragraph buffer. */
  315. unsigned char *output_paragraph;
  316.  
  317. /* Offset into OUTPUT_PARAGRAPH. */
  318. int output_paragraph_offset;
  319.  
  320. /* The output paragraph "cursor" horizontal position. */
  321. int output_column = 0;
  322.  
  323. /* Non-zero means output_paragraph contains text. */
  324. int paragraph_is_open = 0;
  325.  
  326. #define INITIAL_PARAGRAPH_SPACE 5000
  327. int paragraph_buffer_len = INITIAL_PARAGRAPH_SPACE;
  328.  
  329. /* Filling.. */
  330. /* Non-zero indicates that filling will take place on long lines. */
  331. int filling_enabled = 1;
  332.  
  333. /* Non-zero means that words are not to be split, even in long lines.  This
  334.    gets changed for cm_w (). */
  335. int non_splitting_words = 0;
  336.  
  337. /* Non-zero indicates that filling a line also indents the new line. */
  338. int indented_fill = 0;
  339.  
  340. /* The column at which long lines are broken. */
  341. int fill_column = 72;
  342.  
  343. /* The amount of indentation to apply at the start of each line. */
  344. int current_indent = 0;
  345.  
  346. /* The amount of indentation to add at the starts of paragraphs.
  347.    0 means don't change existing indentation at paragraph starts.
  348.    > 0 is amount to indent new paragraphs by.
  349.    < 0 means indent to column zero by removing indentation if necessary.
  350.  
  351.    This is normally zero, but some people prefer paragraph starts to be
  352.    somewhat more indented than paragraph bodies.  A pretty value for
  353.    this is 3. */
  354. int paragraph_start_indent = PARAGRAPH_START_INDENT;
  355.  
  356. /* Non-zero means that the use of paragraph_start_indent is inhibited.
  357.    @example uses this to line up the left columns of the example text.
  358.    A negative value for this variable is incremented each time it is used.
  359.    @noindent uses this to inhibit indentation for a single paragraph.  */
  360. int inhibit_paragraph_indentation = 0;
  361.  
  362. /* Indentation that is pending insertion.  We have this for hacking lines
  363.    which look blank, but contain whitespace.  We want to treat those as
  364.    blank lines. */
  365. int pending_indent = 0;
  366.  
  367. /* The amount that indentation increases/decreases by. */
  368. int default_indentation_increment = DEFAULT_INDENTATION_INCREMENT;
  369.  
  370. /* Non-zero indicates that indentation is temporarily turned off. */
  371. int no_indent = 1;
  372.  
  373. /* Non-zero means forcing output text to be flushright. */
  374. int force_flush_right = 0;
  375.  
  376. /* Non-zero means that the footnote style for this document was set on
  377.    the command line, which overrides any other settings. */
  378. int footnote_style_preset = 0;
  379.  
  380. /* Non-zero means that we automatically number footnotes that have no
  381.    specified marker. */
  382. int number_footnotes = 1;
  383.  
  384. /* The current footnote number in this node.  Each time a new node is
  385.    started this is reset to 1. */
  386. int current_footnote_number = 1;
  387.  
  388. /* Command name in the process of being hacked. */
  389. char *command;
  390.  
  391. /* The index in our internal command table of the currently
  392.    executing command. */
  393. int command_index;
  394.  
  395. /* A search string which is used to find a line defining a node. */
  396. char node_search_string[] =
  397.   { '\n', COMMAND_PREFIX, 'n', 'o', 'd', 'e', ' ', '\0' };
  398.  
  399. /* A search string which is used to find a line defining a menu. */
  400. char menu_search_string[] =
  401.   { '\n', COMMAND_PREFIX, 'm', 'e', 'n', 'u', '\0' };
  402.  
  403. /* A search string which is used to find the first @setfilename. */
  404. char setfilename_search[] =
  405.   { COMMAND_PREFIX,
  406.       's', 'e', 't', 'f', 'i', 'l', 'e', 'n', 'a', 'm', 'e', '\0' };
  407.  
  408. /* A stack of file information records.  If a new file is read in with
  409.    "@input", we remember the old input file state on this stack. */
  410. typedef struct fstack
  411. {
  412.   struct fstack *next;
  413.   char *filename;
  414.   char *text;
  415.   int size;
  416.   int offset;
  417.   int line_number;
  418. } FSTACK;
  419.  
  420. FSTACK *filestack = (FSTACK *) NULL;
  421.  
  422. /* Stuff for nodes. */
  423. /* The current nodes node name. */
  424. char *current_node = (char *)NULL;
  425.  
  426. /* The current nodes section level. */
  427. int current_section = 0;
  428.  
  429. /* The filename of the current input file.  This is never freed. */
  430. char *node_filename = (char *)NULL;
  431.  
  432. /* What we remember for each node. */
  433. typedef struct tentry
  434. {
  435.   struct tentry *next_ent;
  436.   char *node;        /* name of this node. */
  437.   char *prev;        /* name of "Prev:" for this node. */
  438.   char *next;        /* name of "Next:" for this node. */
  439.   char *up;        /* name of "Up:" for this node.   */
  440.   int position;        /* output file position of this node. */
  441.   int line_no;        /* defining line in source file. */
  442.   char *filename;    /* The file that this node was found in. */
  443.   int touched;        /* non-zero means this node has been referenced. */
  444.   int flags;        /* Room for growth.  Right now, contains 1 bit. */
  445. } TAG_ENTRY;
  446.  
  447. /* If node-a has a "Next" for node-b, but node-b has no "Prev" for node-a,
  448.    we turn on this flag bit in node-b's tag entry.  This means that when
  449.    it is time to validate node-b, we don't report an additional error
  450.    if there was no "Prev" field. */
  451. #define PREV_ERROR 0x1
  452. #define NEXT_ERROR 0x2
  453. #define UP_ERROR   0x4
  454. #define NO_WARN       0x8
  455. #define IS_TOP        0x10
  456.  
  457. TAG_ENTRY *tag_table = (TAG_ENTRY *) NULL;
  458.  
  459. #if defined (HAVE_MACROS)
  460. #define ME_RECURSE    0x01
  461. #define ME_QUOTE_ARG    0x02
  462.  
  463. /* Macro definitions for user-defined commands. */
  464. typedef struct {
  465.   char *name;            /* Name of the macro. */
  466.   char **arglist;        /* Args to replace when executing. */
  467.   char *body;            /* Macro body. */
  468.   char *source_file;        /* File where this macro is defined. */
  469.   int source_lineno;        /* Line number within FILENAME. */
  470.   int inhibited;        /* Non-zero means make find_macro () fail. */
  471.   int flags;            /* ME_RECURSE, ME_QUOTE_ARG, etc. */
  472. } MACRO_DEF;
  473.  
  474. void add_macro (), execute_macro ();
  475. MACRO_DEF *find_macro (), *delete_macro ();
  476. #endif /* HAVE_MACROS */
  477.  
  478. /* Menu reference, *note reference, and validation hacking. */
  479.  
  480. /* The various references that we know about. */
  481. enum reftype
  482. {
  483.   menu_reference, followed_reference
  484. };
  485.  
  486. /* A structure to remember references with.  A reference to a node is
  487.    either an entry in a menu, or a cross-reference made with [px]ref. */
  488. typedef struct node_ref
  489. {
  490.   struct node_ref *next;
  491.   char *node;            /* Name of node referred to. */
  492.   char *containing_node;    /* Name of node containing this reference. */
  493.   int line_no;            /* Line number where the reference occurs. */
  494.   int section;            /* Section level where the reference occurs. */
  495.   char *filename;        /* Name of file where the reference occurs. */
  496.   enum reftype type;        /* Type of reference, either menu or note. */
  497. } NODE_REF;
  498.  
  499. /* The linked list of such structures. */
  500. NODE_REF *node_references = (NODE_REF *) NULL;
  501.  
  502. /* Flag which tells us whether to examine menu lines or not. */
  503. int in_menu = 0;
  504.  
  505. /* Non-zero means that we have seen "@top" once already. */
  506. int top_node_seen = 0;
  507.  
  508. /* Non-zero means that we have seen a non-"@top" node already. */
  509. int non_top_node_seen = 0;
  510.  
  511. /* Flags controlling the operation of the program. */
  512.  
  513. /* Default is to notify users of bad choices. */
  514. int print_warnings = 1;
  515.  
  516. /* Default is to check node references. */
  517. int validating = 1;
  518.  
  519. /* Non-zero means do not output "Node: Foo" for node separations. */
  520. int no_headers = 0;
  521.  
  522. /* Number of errors that we tolerate on a given fileset. */
  523. int max_error_level = 100;
  524.  
  525. /* Maximum number of references to a single node before complaining. */
  526. int reference_warning_limit = 1000;
  527.  
  528. /* Non-zero means print out information about what is going on when it
  529.    is going on. */
  530. int verbose_mode = 0;
  531.  
  532. /* Non-zero means to be relaxed about the input file.  This is useful when
  533.    we can successfully format the input, but it doesn't strictly match our
  534.    somewhat pedantic ideas of correctness.  Right now, it affects what
  535.    @table and @itemize do without arguments. */
  536. int allow_lax_format = 0;
  537.  
  538. /* Non-zero means converting to AmigaGuide hypertext format. */
  539. int amiga_guide = 0;
  540.  
  541. /* Non-zero means converting to AmigaGuide V34 hypertext format. */
  542. int amiga_guide_34 = 0;
  543.  
  544. /* Non-zero means converting to AmigaGuide V39 hypertext format. */
  545. int amiga_guide_39 = 0;
  546.  
  547. /* Non-zero means converting to AmigaGuide V40 hypertext format. */
  548. int amiga_guide_40 = 0;
  549.  
  550. /* Default length of an index button in an AmigaGuide. */
  551. int amiga_index_button_length = 40;
  552.  
  553. /* Default length of a menu button in an AmigaGuide. */
  554. int amiga_menu_button_length = 25;
  555.  
  556. /* Non-zero means we are writing an AmigaGuide button. */
  557. int in_amiga_guide_button = 0;
  558.  
  559. /* Non-zero means we are writing title. */
  560. int in_amiga_guide_title = 0;
  561.  
  562. /* Number of chars we have to hide when writing titles */
  563. int amiga_hide_title_chars = 0;
  564.  
  565. /* Prototype for attribute setting function */
  566. void amiga_set_attributes (char *);
  567.  
  568. #define AMIGA_CODE_ON "@{b}"
  569. #define AMIGA_CODE_OFF "@{ub}"
  570. #define AMIGA_BOLD_ON "@{b}"
  571. #define AMIGA_BOLD_OFF "@{ub}"
  572. #define AMIGA_VAR_ON "@{i}"
  573. #define AMIGA_VAR_OFF "@{ui}"
  574. #define AMIGA_EMPH_ON "@{i}"
  575. #define AMIGA_EMPH_OFF "@{ui}"
  576. #define AMIGA_ITALIC_ON "@{i}"
  577. #define AMIGA_ITALIC_OFF "@{ui}"
  578.   
  579. /* Prototype for a function which returns a string of
  580.    spaces with the length of the index/menu text length */
  581. char * amiga_button_text_length (char *, int);
  582.  
  583. /* The list of commands that we hack in texinfo.  Each one
  584.    has an associated function.  When the command is encountered in the
  585.    text, the associated function is called with START as the argument.
  586.    If the function expects arguments in braces, it remembers itself on
  587.    the stack.  When the corresponding close brace is encountered, the
  588.    function is called with END as the argument. */
  589.  
  590. #define START 0
  591. #define END 1
  592.  
  593. typedef struct brace_element
  594. {
  595.   struct brace_element *next;
  596.   COMMAND_FUNCTION *proc;
  597.   int pos, line;
  598. } BRACE_ELEMENT;
  599.  
  600. BRACE_ELEMENT *brace_stack = (BRACE_ELEMENT *) NULL;
  601.  
  602. /* Forward declarations. */
  603. #if !defined (HAVE_STRDUP)
  604. extern char *strdup ();
  605. #endif /* HAVE_STRDUP */
  606.  
  607. void print_version_info ();
  608. void usage ();
  609. void push_node_filename (), pop_node_filename ();
  610. void remember_error ();
  611. void convert_from_stream (), convert_from_file (), convert_from_loaded_file ();
  612. void init_internals (), init_paragraph (), init_brace_stack ();
  613. void init_insertion_stack (), init_indices ();
  614. void init_tag_table (), write_tag_table (), write_tag_table_internal ();
  615. void validate_file (), validate_other_references (), split_file ();
  616. void free_node_references (), do_enumeration (), handle_variable ();
  617. void handle_variable_internal ();
  618. void execute_string ();
  619. void normalize_node_name ();
  620. void undefindex (), top_defindex (), gen_defindex ();
  621. void define_user_command ();
  622. void free_pending_notes (), output_pending_notes ();
  623.  
  624. void reader_loop (), read_command ();
  625. void remember_brace (), remember_brace_1 ();
  626. void pop_and_call_brace (), discard_braces ();
  627. void add_word_args (), add_word (), add_char (), insert (), flush_output ();
  628. void close_paragraph_with_lines (), close_paragraph ();
  629. void ignore_blank_line ();
  630. void do_flush_right_indentation ();
  631. void start_paragraph (), indent ();
  632.  
  633. void insert_self (), cm_ignore_line ();
  634.  
  635. void
  636.   cm_asterisk (), cm_dots (), cm_bullet (), cm_TeX (),
  637.   cm_copyright (), cm_code (), cm_samp (), cm_file (), cm_kbd (),
  638.   cm_key (), cm_ctrl (), cm_var (), cm_dfn (), cm_emph (), cm_strong (),
  639.   cm_cite (), cm_italic (), cm_bold (), cm_roman (), cm_title (), cm_w (),
  640.   cm_refill (), cm_titlefont ();
  641.  
  642. void
  643.   cm_chapter (), cm_unnumbered (), cm_appendix (), cm_top (),
  644.   cm_section (), cm_unnumberedsec (), cm_appendixsec (),
  645.   cm_subsection (), cm_unnumberedsubsec (), cm_appendixsubsec (),
  646.   cm_subsubsection (), cm_unnumberedsubsubsec (), cm_appendixsubsubsec (),
  647.   cm_heading (), cm_chapheading (), cm_subheading (), cm_subsubheading (),
  648.   cm_majorheading (), cm_raisesections (), cm_lowersections ();
  649.  
  650. /* All @defxxx commands map to cm_defun (). */
  651. void cm_defun ();
  652.  
  653. void
  654.   cm_node (), cm_menu (), cm_xref (), cm_ftable (), cm_vtable (), cm_pxref (),
  655.   cm_inforef (), cm_quotation (), cm_display (), cm_itemize (),
  656.   cm_enumerate (), cm_table (), cm_itemx (), cm_noindent (), cm_setfilename (),
  657.   cm_br (), cm_sp (), cm_page (), cm_group (), cm_center (), cm_include (),
  658.   cm_bye (), cm_item (), cm_end (), cm_infoinclude (), cm_ifinfo (),
  659.   cm_kindex (), cm_cindex (), cm_findex (), cm_pindex (), cm_vindex (),
  660.   cm_tindex (), cm_asis (), cm_synindex (), cm_printindex (), cm_minus (),
  661.   cm_footnote (), cm_force_abbreviated_whitespace (), cm_example (),
  662.   cm_smallexample (), cm_lisp (), cm_format (), cm_exdent (), cm_defindex (),
  663.   cm_defcodeindex (), cm_sc (), cm_result (), cm_expansion (), cm_equiv (),
  664.   cm_print (), cm_error (), cm_point (), cm_today (), cm_flushleft (),
  665.   cm_flushright (), cm_smalllisp (), cm_finalout (), cm_math (),
  666.   cm_cartouche (), cm_ignore_sentence_ender ();
  667.  
  668. /* Conditionals. */
  669. void cm_set (), cm_clear (), cm_ifset (), cm_ifclear ();
  670. void cm_value (), cm_ifeq ();
  671.  
  672. #if defined (HAVE_MACROS)
  673. /* Define a user-defined command which is simple substitution. */
  674. void cm_macro (), cm_unmacro ();
  675. #endif /* HAVE_MACROS */
  676.  
  677. /* Options. */
  678. void cm_paragraphindent (), cm_footnotestyle ();
  679.  
  680. /* Internals. */
  681. void do_nothing (), command_name_condition ();
  682. void misplaced_brace (), cm_obsolete ();
  683.  
  684. typedef struct
  685. {
  686.   char *name;
  687.   COMMAND_FUNCTION *proc;
  688.   int argument_in_braces;
  689. } COMMAND;
  690.  
  691. /* Stuff for defining commands on the fly. */
  692. COMMAND **user_command_array = (COMMAND **) NULL;
  693. int user_command_array_len = 0;
  694.  
  695. #define NO_BRACE_ARGS 0
  696. #define BRACE_ARGS 1
  697.  
  698. static COMMAND CommandTable[] = {
  699.   { "!", cm_ignore_sentence_ender, NO_BRACE_ARGS },
  700.   { "'", insert_self, NO_BRACE_ARGS },
  701.   { "*", cm_asterisk, NO_BRACE_ARGS },
  702.   { ".", cm_ignore_sentence_ender, NO_BRACE_ARGS },
  703.   { ":", cm_force_abbreviated_whitespace, NO_BRACE_ARGS },
  704.   { "?", cm_ignore_sentence_ender, NO_BRACE_ARGS },
  705.   { "|", do_nothing, NO_BRACE_ARGS },
  706.   { "@", insert_self, NO_BRACE_ARGS },
  707.   { " ", insert_self, NO_BRACE_ARGS },
  708.   { "\n", insert_self, NO_BRACE_ARGS },
  709.   { "TeX", cm_TeX, BRACE_ARGS },
  710.   { "`", insert_self, NO_BRACE_ARGS },
  711.   { "appendix", cm_appendix, NO_BRACE_ARGS },
  712.   { "appendixsection", cm_appendixsec, NO_BRACE_ARGS },
  713.   { "appendixsec", cm_appendixsec, NO_BRACE_ARGS },
  714.   { "appendixsubsec", cm_appendixsubsec, NO_BRACE_ARGS },
  715.   { "appendixsubsubsec", cm_appendixsubsubsec, NO_BRACE_ARGS },
  716.   { "asis", cm_asis, BRACE_ARGS },
  717.   { "b", cm_bold, BRACE_ARGS },
  718.   { "br", cm_br, NO_BRACE_ARGS },
  719.   { "bullet", cm_bullet, BRACE_ARGS },
  720.   { "bye", cm_bye, NO_BRACE_ARGS },
  721.   { "c", cm_ignore_line, NO_BRACE_ARGS },
  722.   { "cartouche", cm_cartouche, NO_BRACE_ARGS },
  723.   { "center", cm_center, NO_BRACE_ARGS },
  724.   { "chapheading", cm_chapheading, NO_BRACE_ARGS },
  725.   { "chapter", cm_chapter, NO_BRACE_ARGS },
  726.   { "cindex", cm_cindex, NO_BRACE_ARGS },
  727.   { "cite", cm_cite, BRACE_ARGS },
  728.   { "clear", cm_clear, NO_BRACE_ARGS },
  729.   { "code", cm_code, BRACE_ARGS },
  730.   { "comment", cm_ignore_line, NO_BRACE_ARGS },
  731.   { "contents", do_nothing, NO_BRACE_ARGS },
  732.   { "copyright", cm_copyright, BRACE_ARGS },
  733.   { "ctrl", cm_ctrl, BRACE_ARGS },
  734.   { "defcodeindex", cm_defcodeindex, NO_BRACE_ARGS },
  735.   { "defindex", cm_defindex, NO_BRACE_ARGS },
  736.   { "dfn", cm_dfn, BRACE_ARGS },
  737.  
  738. /* The `def' commands. */
  739.   { "deffn", cm_defun, NO_BRACE_ARGS },
  740.   { "deffnx", cm_defun, NO_BRACE_ARGS },
  741.   { "defun", cm_defun, NO_BRACE_ARGS },
  742.   { "defunx", cm_defun, NO_BRACE_ARGS },
  743.   { "defmac", cm_defun, NO_BRACE_ARGS },
  744.   { "defmacx", cm_defun, NO_BRACE_ARGS },
  745.   { "defspec", cm_defun, NO_BRACE_ARGS },
  746.   { "defspecx", cm_defun, NO_BRACE_ARGS },
  747.   { "defvr", cm_defun, NO_BRACE_ARGS },
  748.   { "defvrx", cm_defun, NO_BRACE_ARGS },
  749.   { "defvar", cm_defun, NO_BRACE_ARGS },
  750.   { "defvarx", cm_defun, NO_BRACE_ARGS },
  751.   { "defopt", cm_defun, NO_BRACE_ARGS },
  752.   { "defoptx", cm_defun, NO_BRACE_ARGS },
  753.   { "deftypefn", cm_defun, NO_BRACE_ARGS },
  754.   { "deftypefnx", cm_defun, NO_BRACE_ARGS },
  755.   { "deftypefun", cm_defun, NO_BRACE_ARGS },
  756.   { "deftypefunx", cm_defun, NO_BRACE_ARGS },
  757.   { "deftypevr", cm_defun, NO_BRACE_ARGS },
  758.   { "deftypevrx", cm_defun, NO_BRACE_ARGS },
  759.   { "deftypevar", cm_defun, NO_BRACE_ARGS },
  760.   { "deftypevarx", cm_defun, NO_BRACE_ARGS },
  761.   { "defcv", cm_defun, NO_BRACE_ARGS },
  762.   { "defcvx", cm_defun, NO_BRACE_ARGS },
  763.   { "defivar", cm_defun, NO_BRACE_ARGS },
  764.   { "defivarx", cm_defun, NO_BRACE_ARGS },
  765.   { "defop", cm_defun, NO_BRACE_ARGS },
  766.   { "defopx", cm_defun, NO_BRACE_ARGS },
  767.   { "defmethod", cm_defun, NO_BRACE_ARGS },
  768.   { "defmethodx", cm_defun, NO_BRACE_ARGS },
  769.   { "deftypemethod", cm_defun, NO_BRACE_ARGS },
  770.   { "deftypemethodx", cm_defun, NO_BRACE_ARGS },
  771.   { "deftp", cm_defun, NO_BRACE_ARGS },
  772.   { "deftpx", cm_defun, NO_BRACE_ARGS },
  773. /* The end of the `def' commands. */
  774.  
  775.   { "display", cm_display, NO_BRACE_ARGS },
  776.   { "dots", cm_dots, BRACE_ARGS },
  777.   { "dmn", do_nothing, BRACE_ARGS },
  778.   { "emph", cm_emph, BRACE_ARGS },
  779.   { "end", cm_end, NO_BRACE_ARGS },
  780.   { "enumerate", cm_enumerate, NO_BRACE_ARGS },
  781.   { "equiv", cm_equiv, BRACE_ARGS },
  782.   { "error", cm_error, BRACE_ARGS },
  783.   { "example", cm_example, NO_BRACE_ARGS },
  784.   { "exdent", cm_exdent, NO_BRACE_ARGS },
  785.   { "expansion", cm_expansion, BRACE_ARGS },
  786.   { "file", cm_file, BRACE_ARGS },
  787.   { "findex", cm_findex, NO_BRACE_ARGS },
  788.   { "finalout", do_nothing, NO_BRACE_ARGS },
  789.   { "flushleft", cm_flushleft, NO_BRACE_ARGS },
  790.   { "flushright", cm_flushright, NO_BRACE_ARGS },
  791.   { "format", cm_format, NO_BRACE_ARGS },
  792.   { "ftable", cm_ftable, NO_BRACE_ARGS },
  793.   { "group", cm_group, NO_BRACE_ARGS },
  794.   { "heading", cm_heading, NO_BRACE_ARGS },
  795.   { "headings", cm_ignore_line, NO_BRACE_ARGS },
  796.   { "i", cm_italic, BRACE_ARGS },
  797.   { "iappendix", cm_appendix, NO_BRACE_ARGS },
  798.   { "iappendixsection", cm_appendixsec, NO_BRACE_ARGS },
  799.   { "iappendixsec", cm_appendixsec, NO_BRACE_ARGS },
  800.   { "iappendixsubsec", cm_appendixsubsec, NO_BRACE_ARGS },
  801.   { "iappendixsubsubsec", cm_appendixsubsubsec, NO_BRACE_ARGS },
  802.   { "ichapter", cm_chapter, NO_BRACE_ARGS },
  803.   { "ifclear", cm_ifclear, NO_BRACE_ARGS },
  804.   { "ifeq", cm_ifeq, NO_BRACE_ARGS },
  805.   { "ifhtml", command_name_condition, NO_BRACE_ARGS },
  806.   { "ifinfo", cm_ifinfo, NO_BRACE_ARGS },
  807.   { "ifset", cm_ifset, NO_BRACE_ARGS },
  808.   { "iftex", command_name_condition, NO_BRACE_ARGS },
  809.   { "ignore", command_name_condition, NO_BRACE_ARGS },
  810.   { "include", cm_include, NO_BRACE_ARGS },
  811.   { "inforef", cm_inforef, BRACE_ARGS },
  812.   { "input", cm_include, NO_BRACE_ARGS },
  813.   { "isection", cm_section, NO_BRACE_ARGS },
  814.   { "isubsection", cm_subsection, NO_BRACE_ARGS },
  815.   { "isubsubsection", cm_subsubsection, NO_BRACE_ARGS },
  816.   { "item", cm_item, NO_BRACE_ARGS },
  817.   { "itemize", cm_itemize, NO_BRACE_ARGS },
  818.   { "itemx", cm_itemx, NO_BRACE_ARGS },
  819.   { "iunnumbered", cm_unnumbered, NO_BRACE_ARGS },
  820.   { "iunnumberedsec", cm_unnumberedsec, NO_BRACE_ARGS },
  821.   { "iunnumberedsubsec", cm_unnumberedsubsec, NO_BRACE_ARGS },
  822.   { "iunnumberedsubsubsec", cm_unnumberedsubsubsec, NO_BRACE_ARGS },
  823.   { "kbd", cm_kbd, BRACE_ARGS },
  824.   { "key", cm_key, BRACE_ARGS },
  825.   { "kindex", cm_kindex, NO_BRACE_ARGS },
  826.   { "lowersections", cm_lowersections, NO_BRACE_ARGS },
  827.   { "lisp", cm_lisp, NO_BRACE_ARGS },
  828. #if defined (HAVE_MACROS)
  829.   { "macro", cm_macro, NO_BRACE_ARGS },
  830. #endif
  831.   { "majorheading", cm_majorheading, NO_BRACE_ARGS },
  832.   { "math", cm_math, BRACE_ARGS },
  833.   { "medbreak", cm_br, NO_BRACE_ARGS },
  834.   { "menu", cm_menu, NO_BRACE_ARGS },
  835.   { "minus", cm_minus, BRACE_ARGS },
  836.   { "need", cm_ignore_line, NO_BRACE_ARGS },
  837.   { "node", cm_node, NO_BRACE_ARGS },
  838.   { "noindent", cm_noindent, NO_BRACE_ARGS },
  839.   { "nwnode", cm_node, NO_BRACE_ARGS },
  840.   { "overfullrule", cm_ignore_line, NO_BRACE_ARGS },
  841.   { "page", do_nothing, NO_BRACE_ARGS },
  842.   { "pindex", cm_pindex, NO_BRACE_ARGS },
  843.   { "point", cm_point, BRACE_ARGS },
  844.   { "print", cm_print, BRACE_ARGS },
  845.   { "printindex", cm_printindex, NO_BRACE_ARGS },
  846.   { "pxref", cm_pxref, BRACE_ARGS },
  847.   { "quotation", cm_quotation, NO_BRACE_ARGS },
  848.   { "r", cm_roman, BRACE_ARGS },
  849.   { "raisesections", cm_raisesections, NO_BRACE_ARGS },
  850.   { "ref", cm_xref, BRACE_ARGS },
  851.   { "refill", cm_refill, NO_BRACE_ARGS },
  852.   { "result", cm_result, BRACE_ARGS },
  853.   { "samp", cm_samp, BRACE_ARGS },
  854.   { "sc", cm_sc, BRACE_ARGS },
  855.   { "section", cm_section, NO_BRACE_ARGS },
  856.   { "set", cm_set, NO_BRACE_ARGS },
  857.   { "setchapternewpage", cm_ignore_line, NO_BRACE_ARGS },
  858.   { "setchapterstyle", cm_ignore_line, NO_BRACE_ARGS },
  859.   { "setfilename", cm_setfilename, NO_BRACE_ARGS },
  860.   { "settitle", cm_ignore_line, NO_BRACE_ARGS },
  861.   { "shortcontents", do_nothing, NO_BRACE_ARGS },
  862.   { "shorttitlepage", cm_ignore_line, NO_BRACE_ARGS },
  863.   { "smallbook", cm_ignore_line, NO_BRACE_ARGS },
  864.   { "smallbreak", cm_br, NO_BRACE_ARGS },
  865.   { "smallexample", cm_smallexample, NO_BRACE_ARGS },
  866.   { "smalllisp", cm_smalllisp, NO_BRACE_ARGS },
  867.   { "sp", cm_sp, NO_BRACE_ARGS },
  868.   { "strong", cm_strong, BRACE_ARGS },
  869.   { "subheading", cm_subheading, NO_BRACE_ARGS },
  870.   { "subsection", cm_subsection, NO_BRACE_ARGS },
  871.   { "subsubheading", cm_subsubheading, NO_BRACE_ARGS },
  872.   { "subsubsection", cm_subsubsection, NO_BRACE_ARGS },
  873.   { "summarycontents", do_nothing, NO_BRACE_ARGS },
  874.   { "syncodeindex", cm_synindex, NO_BRACE_ARGS },
  875.   { "synindex", cm_synindex, NO_BRACE_ARGS },
  876.   { "t", cm_title, BRACE_ARGS },
  877.   { "table", cm_table, NO_BRACE_ARGS },
  878.   { "tex", command_name_condition, NO_BRACE_ARGS },
  879.   { "tindex", cm_tindex, NO_BRACE_ARGS },
  880.   { "titlefont", cm_titlefont, BRACE_ARGS },
  881.   { "titlepage", command_name_condition, NO_BRACE_ARGS },
  882.   { "titlespec", command_name_condition, NO_BRACE_ARGS },
  883.   { "today", cm_today, BRACE_ARGS },
  884.   { "top", cm_top, NO_BRACE_ARGS  },
  885. #if defined (HAVE_MACROS)
  886.   { "unmacro", cm_unmacro, NO_BRACE_ARGS },
  887. #endif
  888.   { "unnumbered", cm_unnumbered, NO_BRACE_ARGS },
  889.   { "unnumberedsec", cm_unnumberedsec, NO_BRACE_ARGS },
  890.   { "unnumberedsubsec", cm_unnumberedsubsec, NO_BRACE_ARGS },
  891.   { "unnumberedsubsubsec", cm_unnumberedsubsubsec, NO_BRACE_ARGS },
  892.   { "value", cm_value, BRACE_ARGS },
  893.   { "var", cm_var, BRACE_ARGS },
  894.   { "vindex", cm_vindex, NO_BRACE_ARGS },
  895.   { "vtable", cm_vtable, NO_BRACE_ARGS },
  896.   { "w", cm_w, BRACE_ARGS },
  897.   { "xref", cm_xref, BRACE_ARGS },
  898.   { "{", insert_self, NO_BRACE_ARGS },
  899.   { "}", insert_self, NO_BRACE_ARGS },
  900.  
  901.   /* Some obsoleted commands. */
  902.   { "infotop", cm_obsolete, NO_BRACE_ARGS },
  903.   { "infounnumbered", cm_obsolete, NO_BRACE_ARGS },
  904.   { "infounnumberedsec", cm_obsolete, NO_BRACE_ARGS },
  905.   { "infounnumberedsubsec", cm_obsolete, NO_BRACE_ARGS },
  906.   { "infounnumberedsubsubsec", cm_obsolete, NO_BRACE_ARGS },
  907.   { "infoappendix", cm_obsolete, NO_BRACE_ARGS },
  908.   { "infoappendixsec", cm_obsolete, NO_BRACE_ARGS },
  909.   { "infoappendixsubsec", cm_obsolete, NO_BRACE_ARGS },
  910.   { "infoappendixsubsubsec", cm_obsolete, NO_BRACE_ARGS },
  911.   { "infochapter", cm_obsolete, NO_BRACE_ARGS },
  912.   { "infosection", cm_obsolete, NO_BRACE_ARGS },
  913.   { "infosubsection", cm_obsolete, NO_BRACE_ARGS },
  914.   { "infosubsubsection", cm_obsolete, NO_BRACE_ARGS },
  915.  
  916.   /* Now @include does what this was supposed to. */
  917.   { "infoinclude", cm_infoinclude, NO_BRACE_ARGS },
  918.   { "footnote", cm_footnote, NO_BRACE_ARGS}, /* self-arg eater */
  919.   { "footnotestyle", cm_footnotestyle, NO_BRACE_ARGS },
  920.   { "paragraphindent", cm_paragraphindent, NO_BRACE_ARGS },
  921.  
  922.   {(char *) NULL, (COMMAND_FUNCTION *) NULL}, NO_BRACE_ARGS};
  923.  
  924. int major_version = 1;
  925. int minor_version = 64;
  926.  
  927. struct option long_options[] =
  928. {
  929.   { "error-limit", 1, 0, 'e' },            /* formerly -el */
  930.   { "fill-column", 1, 0, 'f' },            /* formerly -fc */
  931.   { "footnote-style", 1, 0, 's' },        /* formerly -ft */
  932.   { "no-headers", 0, &no_headers, 1 },        /* Do not output Node: foo */
  933.   { "no-pointer-validate", 0, &validating, 0 }, /* formerly -nv */
  934.   { "no-validate", 0, &validating, 0 },        /* formerly -nv */
  935.   { "no-split", 0, &splitting, 0 },        /* formerly -ns */
  936.   { "no-warn", 0, &print_warnings, 0 },        /* formerly -nw */
  937. #if defined (HAVE_MACROS)
  938.   { "macro-expand", 1, 0, 'E' },
  939. #endif /* HAVE_MACROS */
  940.   { "number-footnotes", 0, &number_footnotes, 1 },
  941.   { "no-number-footnotes", 0, &number_footnotes, 0 },
  942.   { "output", 1, 0, 'o' },
  943.   { "paragraph-indent", 1, 0, 'p' },        /* formerly -pi */
  944.   { "reference-limit", 1, 0, 'r' },        /* formerly -rl */
  945.   { "verbose", 0, &verbose_mode, 1 },        /* formerly -verbose */
  946.   { "help", 0, 0, 'h' },
  947.   { "version", 0, 0, 'V' },
  948.   { "amiga", 0, &amiga_guide_34, 1 },           /* convert to AmigaGuide. */
  949.   { "amiga-39", 0, &amiga_guide_39, 1 },        /* convert to AmigaGuide V39 */
  950.   { "amiga-40", 0, &amiga_guide_40, 1 },        /* convert to AmigaGuide V40 */
  951.   { "index-button-length", 1, 0, 'i'},           /* set default index button length */
  952.   { "menu-button-length", 1, 0, 'm'},            /* set default menu button length */
  953.   {NULL, 0, NULL, 0}
  954. };
  955.  
  956. /* Values for calling handle_variable_internal (). */
  957. #define SET    1
  958. #define CLEAR    2
  959. #define IFSET    3
  960. #define IFCLEAR    4
  961.  
  962. /* **************************************************************** */
  963. /*                                    */
  964. /*            Main ()  Start of code              */
  965. /*                                        */
  966. /* **************************************************************** */
  967.  
  968. /* For each file mentioned in the command line, process it, turning
  969.    texinfo commands into wonderfully formatted output text. */
  970. int
  971. main (argc, argv)
  972.      int argc;
  973.      char **argv;
  974. {
  975.   extern int errors_printed;
  976.   char *filename_part ();
  977.   int c, ind;
  978.   int reading_from_stdin = 0;
  979.  
  980.   /* The name of this program is the last filename in argv[0]. */
  981.   progname = filename_part (argv[0]);
  982.  
  983.   /* Parse argument flags from the input line. */
  984.   while ((c = getopt_long
  985.       (argc, argv,
  986. #if defined (HAVE_MACROS)
  987.            "D:E:U:I:f:o:p:e:r:s:V:i:m",
  988. #else
  989.            "D:U:I:f:o:p:e:r:s:V:i:m",
  990. #endif /* !HAVE_MACROS */
  991.        long_options, &ind))
  992.      != EOF)
  993.     {
  994.       if (c == 0 && long_options[ind].flag == 0)
  995.     c = long_options[ind].val;
  996.  
  997.       switch (c)
  998.     {
  999.       /* User specified variable to set or clear? */
  1000.     case 'D':
  1001.     case 'U':
  1002.       handle_variable_internal ((c == 'D') ? SET : CLEAR, optarg);
  1003.       break;
  1004.  
  1005. #if defined (HAVE_MACROS)
  1006.       /* Use specified a macro expansion output file? */
  1007.     case 'E':
  1008.       if (!macro_expansion_output_stream)
  1009.         {
  1010.           macro_expansion_output_stream = fopen (optarg, "w");
  1011.           if (!macro_expansion_output_stream)
  1012.         error ("Couldn't open macro expansion output \"%s\"", optarg);
  1013.         }
  1014.       else
  1015.         error ("Cannot specify more than one macro expansion output");
  1016.       break;
  1017. #endif /* HAVE_MACROS */
  1018.  
  1019.       /* User specified include file path? */
  1020.     case 'I':
  1021.       if (!include_files_path)
  1022.         include_files_path = strdup (".");
  1023.  
  1024.       include_files_path = (char *)
  1025.         xrealloc (include_files_path,
  1026.               2 + strlen (include_files_path) + strlen (optarg));
  1027.       strcat (include_files_path, ":");
  1028.       strcat (include_files_path, optarg);
  1029.       break;
  1030.  
  1031.       /* User specified fill_column? */
  1032.     case 'f':
  1033.       if (sscanf (optarg, "%d", &fill_column) != 1)
  1034.         usage (stderr, FATAL);
  1035.       break;
  1036.  
  1037.       /* User specified output file? */
  1038.     case 'o':
  1039.       command_output_filename = strdup (optarg);
  1040.       break;
  1041.  
  1042.       /* User specified paragraph indent (paragraph_start_index)? */
  1043.     case 'p':
  1044.       if (set_paragraph_indent (optarg) < 0)
  1045.         usage (stderr, FATAL);
  1046.       break;
  1047.  
  1048.       /* User specified error level? */
  1049.     case 'e':
  1050.       if (sscanf (optarg, "%d", &max_error_level) != 1)
  1051.         usage (stderr, FATAL);
  1052.       break;
  1053.  
  1054.       /* User specified reference warning limit? */
  1055.     case 'r':
  1056.       if (sscanf (optarg, "%d", &reference_warning_limit) != 1)
  1057.         usage (stderr, FATAL);
  1058.       break;
  1059.  
  1060.       /* User specified footnote style? */
  1061.     case 's':
  1062.       if (set_footnote_style (optarg) < 0)
  1063.         usage (stderr, FATAL);
  1064.       footnote_style_preset = 1;
  1065.       break;
  1066.  
  1067.     case 'h':
  1068.       usage (stdout, NO_ERROR);
  1069.       break;
  1070.  
  1071.       /* User requested version info? */
  1072.     case 'V':
  1073.       print_version_info ();
  1074.       exit (NO_ERROR);
  1075.       break;
  1076.  
  1077.           /* User specified default index button length? */          
  1078.         case 'i':
  1079.           if (sscanf (optarg, "%d", &amiga_index_button_length) != 1)
  1080.             usage (stderr, FATAL);
  1081.           break;
  1082.  
  1083.           /* User specified default menu button length? */          
  1084.         case 'm':
  1085.           if (sscanf (optarg, "%d", &amiga_menu_button_length) != 1)
  1086.             usage (stderr, FATAL);
  1087.           break;          
  1088.  
  1089.     case '?':
  1090.       usage (stderr, FATAL);
  1091.       break;
  1092.     }
  1093.     }
  1094.  
  1095.   if (optind == argc)
  1096.     {
  1097.       /* Check to see if input is a file.  If so, process that. */
  1098.       if (!isatty (fileno (stdin)))
  1099.     reading_from_stdin = 1;
  1100.       else
  1101.     usage (stderr, FATAL);
  1102.     }
  1103.  
  1104.   /* If the user has specified --no-headers, this should imply --no-split.
  1105.      Do that here.  I think it might also imply that we should ignore the
  1106.      setfilename at the top of the file, but this might break some FSF things,
  1107.      so I will hold off on that. */
  1108.   if (no_headers)
  1109.     {
  1110.       splitting = 0;
  1111.  
  1112.       /* If the user has not specified an output file, then use stdout by
  1113.      default. */
  1114.       if (!command_output_filename)
  1115.     command_output_filename = strdup ("-");
  1116.     }
  1117.  
  1118.   if (verbose_mode)
  1119.     print_version_info ();
  1120.  
  1121.   /* if --amiga option is specified, set the amiga_guide variable */
  1122.   if (amiga_guide_34)
  1123.     amiga_guide = 1;
  1124.  
  1125.   /* if --amiga-39 option is specified, set the amiga_guide variable */
  1126.   if (amiga_guide_39)
  1127.     amiga_guide = 1;
  1128.     
  1129.   /* if --amiga-40 option is specified, set the amiga_guide variable 
  1130.      and the amiga_guide_39 variable */
  1131.   if (amiga_guide_40)
  1132.     {
  1133.       amiga_guide = 1;
  1134.       amiga_guide_39 = 1;
  1135.     }
  1136.  
  1137.   /* if --amiga option is specified, do not split large files */
  1138.   if (amiga_guide)
  1139.      splitting=0;
  1140.  
  1141.   /* Remaining arguments are file names of texinfo files.
  1142.      Convert them, one by one. */
  1143.   if (!reading_from_stdin)
  1144.     {
  1145.       while (optind != argc)
  1146.     convert_from_file (argv[optind++]);
  1147.     }
  1148.   else
  1149.     convert_from_stream (stdin, "stdin");
  1150.  
  1151.   if (errors_printed)
  1152.     return (SYNTAX);
  1153.   else
  1154.     return (NO_ERROR);
  1155. }
  1156.  
  1157. /* Display the version info of this invocation of Makeinfo. */
  1158. void
  1159. print_version_info ()
  1160. {
  1161.   printf ("This is GNU Makeinfo version %d.%d, from texinfo-3.7.\n",
  1162.       major_version, minor_version);
  1163. }
  1164.  
  1165. /* **************************************************************** */
  1166. /*                                    */
  1167. /*            Generic Utilities                */
  1168. /*                                    */
  1169. /* **************************************************************** */
  1170.  
  1171. #if !defined (HAVE_STRDUP)
  1172. char *
  1173. strdup (string)
  1174.      char *string;
  1175. {
  1176.   char *result;
  1177.  
  1178.   result = (char *)xmalloc (1 + strlen (string));
  1179.   strcpy (result, string);
  1180.  
  1181.   return (result);
  1182. }
  1183. #endif /* !HAVE_STRDUP */
  1184.  
  1185. static void
  1186. memory_error (callers_name, bytes_wanted)
  1187.      char *callers_name;
  1188.      int bytes_wanted;
  1189. {
  1190.   char printable_string[80];
  1191.  
  1192.   sprintf (printable_string,
  1193.        "Virtual memory exhausted in %s ()!  Needed %d bytes.",
  1194.        callers_name, bytes_wanted);
  1195.  
  1196.   error (printable_string);
  1197.   abort ();
  1198. }
  1199.  
  1200. /* Just like malloc, but kills the program in case of fatal error. */
  1201. void *
  1202. xmalloc (nbytes)
  1203.      unsigned int nbytes;
  1204. {
  1205.   void *temp = (void *) malloc (nbytes);
  1206.  
  1207.   if (nbytes && temp == (void *)NULL)
  1208.     memory_error ("xmalloc", nbytes);
  1209.  
  1210.   return (temp);
  1211. }
  1212.  
  1213. /* Like realloc (), but barfs if there isn't enough memory. */
  1214. void *
  1215. xrealloc (pointer, nbytes)
  1216.      void *pointer;
  1217.      unsigned int nbytes;
  1218. {
  1219.   void *temp;
  1220.  
  1221.   if (!pointer)
  1222.     temp = (void *)xmalloc (nbytes);
  1223.   else
  1224.     temp = (void *)realloc (pointer, nbytes);
  1225.  
  1226.   if (nbytes && !temp)
  1227.     memory_error ("xrealloc", nbytes);
  1228.  
  1229.   return (temp);
  1230. }
  1231.  
  1232. /* Tell the user how to use this program.
  1233.    Print the message to STREAM, and then exit with EXIT_VALUE. */
  1234. void
  1235. usage (stream, exit_value)
  1236.      FILE *stream;
  1237.      int exit_value;
  1238. {
  1239.   fprintf (stream, "Usage: %s [options] texinfo-file...\n\
  1240. \n\
  1241. This program accepts as input files of texinfo commands and text\n\
  1242. and outputs a file suitable for reading with GNU Info or outputs\n\
  1243. a file in AmigaGuide® hypertext format.\n\
  1244. \n\
  1245. Options:\n\
  1246. `-I DIR'              add DIR to the directory search list for including\n\
  1247.                       files with the `@include' command.\n\
  1248. -D VAR                define a variable, as with `@set'.\n\
  1249. -U VAR                undefine a variable, as with `@clear'.\n\
  1250. -E MACRO-OFILE        process macros, and output texinfo source code for TeX.\n\
  1251. --no-validate         suppress node cross reference validation.\n\
  1252. --no-warn             suppress warning messages (errors are still output).\n\
  1253. --no-split            suppress the splitting of large files.\n\
  1254. --no-headers          suppress the output of Node: Foo headers.\n\
  1255. --verbose             print information about what is being done.\n\
  1256. --version             print the version number of Makeinfo.\n\
  1257. --output FILE or -o FILE\n\
  1258.                       specify the output file.  When you specify the\n\
  1259.                       output file in this way, any `@setfilename' in the\n\
  1260.                       input file is ignored.\n\
  1261. --paragraph-indent NUM\n\
  1262.                       set the paragraph indent to NUM (default %d).\n\
  1263. --fill-column NUM     set the filling column to NUM (default %d).\n\
  1264. --error-limit NUM     set the error limit to NUM (default %d).\n\
  1265. --reference-limit NUM\n\
  1266.                       set the reference warning limit to NUM (default %d).\n\
  1267. --footnote-style STYLE\n\
  1268.                       set the footnote style to STYLE.  STYLE should\n\
  1269.                       either be `separate' to place footnotes in their own\n\
  1270.                       node, or `end', to place the footnotes at the end of\n\
  1271.                       the node in which they are defined (the default).\n\
  1272. --amiga               to convert to AmigaGuide® V34 hypertext format \n\
  1273. --amiga-39            to convert to AmigaGuide® V39 hypertext format \n\
  1274. --amiga-40            to convert to AmigaGuide® V40 hypertext format \n\
  1275. --index-button-length NUM\n\
  1276.                       set the minimum index button length to NUM (default %d).\n\
  1277. --menu-button-length NUM\n\
  1278.                       set the minimum menu button length to NUM (default %d).\n\n\
  1279. --help                print this message and exit.\n\n",
  1280.        progname, paragraph_start_indent,
  1281.            fill_column, max_error_level, reference_warning_limit,
  1282.            amiga_index_button_length, amiga_menu_button_length);
  1283.   exit (exit_value);
  1284. }
  1285.  
  1286. /* **************************************************************** */
  1287. /*                                    */
  1288. /*            Manipulating Lists                  */
  1289. /*                                        */
  1290. /* **************************************************************** */
  1291.  
  1292. typedef struct generic_list {
  1293.   struct generic_list *next;
  1294. } GENERIC_LIST;
  1295.  
  1296. /* Reverse the chain of structures in LIST.  Output the new head
  1297.    of the chain.  You should always assign the output value of this
  1298.    function to something, or you will lose the chain. */
  1299. GENERIC_LIST *
  1300. reverse_list (list)
  1301.      register GENERIC_LIST *list;
  1302. {
  1303.   register GENERIC_LIST *next;
  1304.   register GENERIC_LIST *prev = (GENERIC_LIST *) NULL;
  1305.  
  1306.   while (list)
  1307.     {
  1308.       next = list->next;
  1309.       list->next = prev;
  1310.       prev = list;
  1311.       list = next;
  1312.     }
  1313.   return (prev);
  1314. }
  1315.  
  1316.  
  1317. /* **************************************************************** */
  1318. /*                                    */
  1319. /*            Pushing and Popping Files               */
  1320. /*                                    */
  1321. /* **************************************************************** */
  1322.  
  1323. /* Find and load the file named FILENAME.  Return a pointer to
  1324.    the loaded file, or NULL if it can't be loaded. */
  1325. char *
  1326. find_and_load (filename)
  1327.      char *filename;
  1328. {
  1329.   struct stat fileinfo;
  1330.   long file_size;
  1331.   int file = -1, n, i, count = 0;
  1332.   char *fullpath, *result, *get_file_info_in_path ();
  1333.  
  1334.   result = fullpath = (char *)NULL;
  1335.  
  1336.   fullpath = get_file_info_in_path (filename, include_files_path, &fileinfo);
  1337.  
  1338.   if (!fullpath)
  1339.     goto error_exit;
  1340.  
  1341.   filename = fullpath;
  1342.   file_size = (long) fileinfo.st_size;
  1343.  
  1344.   file = open (filename, O_RDONLY);
  1345.   if (file < 0)
  1346.     goto error_exit;
  1347.  
  1348.   /* Load the file. */
  1349.   result = (char *)xmalloc (1 + file_size);
  1350.  
  1351.   /* VMS stat lies about the st_size value.  The actual number of
  1352.      readable bytes is always less than this value.  The arcane
  1353.      mysteries of VMS/RMS are too much to probe, so this hack
  1354.     suffices to make things work. */
  1355. #if defined (VMS)
  1356.   while ((n = read (file, result + count, file_size)) > 0)
  1357.     count += n;
  1358.   if (n == -1)
  1359. #else /* !VMS */
  1360.     count = file_size;
  1361.     if (read (file, result, file_size) != file_size)
  1362. #endif /* !VMS */
  1363.   error_exit:
  1364.     {
  1365.       if (result)
  1366.     free (result);
  1367.  
  1368.       if (fullpath)
  1369.     free (fullpath);
  1370.  
  1371.       if (file != -1)
  1372.     close (file);
  1373.  
  1374.       return ((char *) NULL);
  1375.     }
  1376.   close (file);
  1377.  
  1378.   /* Set the globals to the new file. */
  1379.   input_text = result;
  1380.   size_of_input_text = count;
  1381.   input_filename = fullpath;
  1382.   node_filename = strdup (fullpath);
  1383.   input_text_offset = 0;
  1384.   line_number = 1;
  1385.   /* Not strictly necessary.  This magic prevents read_token () from doing
  1386.      extra unnecessary work each time it is called (that is a lot of times).
  1387.      The SIZE_OF_INPUT_TEXT is one past the actual end of the text. */
  1388.   input_text[size_of_input_text] = '\n';
  1389.   return (result);
  1390. }
  1391.  
  1392. /* Save the state of the current input file. */
  1393. void
  1394. pushfile ()
  1395. {
  1396.   FSTACK *newstack = (FSTACK *) xmalloc (sizeof (FSTACK));
  1397.   newstack->filename = input_filename;
  1398.   newstack->text = input_text;
  1399.   newstack->size = size_of_input_text;
  1400.   newstack->offset = input_text_offset;
  1401.   newstack->line_number = line_number;
  1402.   newstack->next = filestack;
  1403.  
  1404.   filestack = newstack;
  1405.   push_node_filename ();
  1406. }
  1407.  
  1408. /* Make the current file globals be what is on top of the file stack. */
  1409. void
  1410. popfile ()
  1411. {
  1412.   FSTACK *tos = filestack;
  1413.  
  1414.   if (!tos)
  1415.     abort ();            /* My fault.  I wonder what I did? */
  1416.  
  1417. #if defined (HAVE_MACROS)
  1418.   if (macro_expansion_output_stream)
  1419.     {
  1420.       maybe_write_itext (input_text, input_text_offset);
  1421.       forget_itext (input_text);
  1422.     }
  1423. #endif /* HAVE_MACROS */
  1424.  
  1425.   /* Pop the stack. */
  1426.   filestack = filestack->next;
  1427.  
  1428.   /* Make sure that commands with braces have been satisfied. */
  1429.   if (!executing_string)
  1430.     discard_braces ();
  1431.  
  1432.   /* Get the top of the stack into the globals. */
  1433.   input_filename = tos->filename;
  1434.   input_text = tos->text;
  1435.   size_of_input_text = tos->size;
  1436.   input_text_offset = tos->offset;
  1437.   line_number = tos->line_number;
  1438.   free (tos);
  1439.  
  1440.   /* Go back to the (now) current node. */
  1441.   pop_node_filename ();
  1442. }
  1443.  
  1444. /* Flush all open files on the file stack. */
  1445. void
  1446. flush_file_stack ()
  1447. {
  1448.   while (filestack)
  1449.     {
  1450.       char *fname = input_filename;
  1451.       char *text = input_text;
  1452.       popfile ();
  1453.       free (fname);
  1454.       free (text);
  1455.     }
  1456. }
  1457.  
  1458. int node_filename_stack_index = 0;
  1459. int node_filename_stack_size = 0;
  1460. char **node_filename_stack = (char **)NULL;
  1461.  
  1462. void
  1463. push_node_filename ()
  1464. {
  1465.   if (node_filename_stack_index + 1 > node_filename_stack_size)
  1466.     {
  1467.       if (!node_filename_stack)
  1468.     node_filename_stack =
  1469.       (char **)xmalloc ((node_filename_stack_size += 10)
  1470.                 * sizeof (char *));
  1471.       else
  1472.     node_filename_stack =
  1473.       (char **)xrealloc (node_filename_stack,
  1474.                  (node_filename_stack_size + 10)
  1475.                  * sizeof (char *));
  1476.     }
  1477.  
  1478.   node_filename_stack[node_filename_stack_index] = node_filename;
  1479.   node_filename_stack_index++;
  1480. }
  1481.  
  1482. void
  1483. pop_node_filename ()
  1484. {
  1485.   node_filename = node_filename_stack[--node_filename_stack_index];
  1486. }
  1487.  
  1488. /* Return just the simple part of the filename; i.e. the
  1489.    filename without the path information, or extensions.
  1490.    This conses up a new string. */
  1491. char *
  1492. filename_part (filename)
  1493.      char *filename;
  1494. {
  1495.   char *basename=strrchr (filename, '/'),
  1496.        *basename2=strrchr (filename, ':');
  1497.  
  1498.   basename = (int)basename > (int)basename2 ? basename : basename2;
  1499.   if (!basename)
  1500.     basename = filename;
  1501.   else
  1502.     basename++;
  1503.  
  1504.   basename = strdup (basename);
  1505. #if defined (REMOVE_OUTPUT_EXTENSIONS)
  1506.  
  1507.   /* See if there is an extension to remove.  If so, remove it. */
  1508.   {
  1509.     char *temp;
  1510.  
  1511.     temp = strrchr (basename, '.');
  1512.     if (temp)
  1513.       *temp = '\0';
  1514.   }
  1515. #endif /* REMOVE_OUTPUT_EXTENSIONS */
  1516.   return (basename);
  1517. }
  1518.  
  1519. /* Return the pathname part of filename.  This can be NULL. */
  1520. char *
  1521. pathname_part (filename)
  1522.      char *filename;
  1523. {
  1524.   char *expand_filename ();
  1525.   char *result = (char *) NULL;
  1526.   register int i;
  1527.  
  1528.   filename = expand_filename (filename, "");
  1529.  
  1530.   i = strlen (filename) - 1;
  1531.  
  1532. #if !defined (__amigaos__)
  1533.   while (i && filename[i] != '/')
  1534.     i--;
  1535.   if (filename[i] == '/')
  1536.     i++;
  1537. #else /* __amigaos__ */
  1538.   while (i && (filename[i] != '/') && (filename[i] != ':'))
  1539.     i--;
  1540.   if ((filename[i] == '/') || (filename[i] == ':'))
  1541.    i++;
  1542.  
  1543. #endif /* __amigaos__ */
  1544.  
  1545.   if (i)
  1546.     {
  1547.       result = (char *)xmalloc (1 + i);
  1548.       strncpy (result, filename, i);
  1549.       result[i] = '\0';
  1550.     }
  1551. /*  free (filename); Bug fix: a function might still reference
  1552.                               "filename" after calling this
  1553.                               function                    -kdp */
  1554.   return (result);
  1555. }
  1556.  
  1557. char *
  1558. filename_non_directory (name)
  1559.      char *name;
  1560. {
  1561.   register int i;
  1562.  
  1563.   for (i = strlen (name) - 1; i; i--)
  1564.     if (name[i] == '/')
  1565.       return (strdup (name + i + 1));
  1566.  
  1567.   return (strdup (name));
  1568. }
  1569.  
  1570. /* Return the expansion of FILENAME. */
  1571. char *
  1572. expand_filename (filename, input_name)
  1573.      char *filename, *input_name;
  1574. {
  1575.   char *full_pathname ();
  1576.  
  1577. #if !defined(__amigaos__)
  1578.   register int i;
  1579.  
  1580.   if (filename)
  1581.     filename = full_pathname (filename);
  1582.   else
  1583.     {
  1584.       filename = filename_non_directory (input_name);
  1585.  
  1586.       if (!*filename)
  1587.     {
  1588.       free (filename);
  1589.       filename = strdup ("noname.texi");
  1590.     }
  1591.  
  1592.       for (i = strlen (filename) - 1; i; i--)
  1593.     if (filename[i] == '.')
  1594.       break;
  1595.  
  1596.       if (!i)
  1597.     i = strlen (filename);
  1598.  
  1599.       if (i + 6 > (strlen (filename)))
  1600.     filename = (char *)xrealloc (filename, i + 6);
  1601.       strcpy (filename + i, ".info");
  1602.       return (filename);
  1603.     }
  1604.     
  1605.   if (filename[0] == '.' || filename[0] == '/')
  1606.     return (filename);
  1607.  
  1608.   if (filename[0] != '/' && input_name[0] == '/')
  1609.     {
  1610.       /* Make it so that relative names work. */
  1611.       char *result;
  1612.       
  1613.       i = strlen (input_name) - 1;
  1614.  
  1615.       result = (char *)xmalloc (1 + strlen (input_name) + strlen (filename));
  1616.       strcpy (result, input_name);
  1617.  
  1618.       while (result[i] != '/' && i)
  1619.     i--;
  1620.  
  1621.       if (result[i] == '/')
  1622.     i++;
  1623.  
  1624.       strcpy (&result[i], filename);
  1625.       free (filename);
  1626.       return (result);
  1627.     }
  1628.   return (filename);
  1629. #else /* __amigaos__ */
  1630.  
  1631.   register int i = 0;
  1632.   char *result;
  1633.  
  1634.   filename = full_pathname (filename);
  1635.  
  1636.   while(filename[i] && filename[i] != ':')
  1637.     i++;
  1638.  
  1639.   if (!*input_name || filename[i] == ':') return(filename);
  1640.  
  1641.   i = strlen (input_name) - 1;
  1642.  
  1643.   result = (char *)xmalloc (1 + strlen (input_name) + strlen (filename));
  1644.   strcpy (result, input_name);
  1645.  
  1646.   while (i && result[i] != '/' && result[i] != ':')
  1647.     i--;
  1648.   if (result[i] == '/' || result[i] == ':')
  1649.     i++;
  1650.  
  1651.   strcpy (&result[i], filename);
  1652. /* Bug Fix : free (filename);   -kdp */
  1653.   return (result);
  1654.  
  1655. #endif /* __amigaos__ */
  1656. }
  1657.  
  1658. /* Return the full path to FILENAME. */
  1659. char *
  1660. full_pathname (filename)
  1661.      char *filename;
  1662. {
  1663. #if !defined(__amigaos__)
  1664.   int initial_character;
  1665.   char *result;
  1666.  
  1667.   /* No filename given? */
  1668.   if (!filename || !(initial_character = *filename))
  1669.     return (strdup (""));
  1670.   
  1671.   /* Already absolute? */
  1672.   if ((initial_character == '/') ||
  1673.       ((strncmp (filename, "./", 2) == 0) ||
  1674.        (strncmp (filename, "../", 3) == 0)))
  1675.     return (strdup (filename));
  1676.  
  1677.   if (initial_character != '~')
  1678.     {
  1679.       char *localdir;
  1680.  
  1681.       localdir = (char *)xmalloc (1025);
  1682. #if defined (HAVE_GETCWD)
  1683.       if (!getcwd (localdir, 1024))
  1684. #else  /*  !HAVE_GETCWD */
  1685.     if (!getwd (localdir))
  1686. #endif /* !HAVE_GETCWD */
  1687.       {
  1688.         fprintf (stderr, "%s: getwd: %s, %s\n",
  1689.              progname, filename, localdir);
  1690.         exit (1);
  1691.       }
  1692.  
  1693.       strcat (localdir, "/");
  1694.       strcat (localdir, filename);
  1695.       result = strdup (localdir);
  1696.       free (localdir);
  1697.     }
  1698.   else
  1699.     {
  1700.       if (filename[1] == '/')
  1701.     {
  1702.       /* Return the concatenation of the environment variable HOME
  1703.          and the rest of the string. */
  1704.       char *temp_home;
  1705.  
  1706.       temp_home = (char *) getenv ("HOME");
  1707.       result = (char *)xmalloc (strlen (&filename[1])
  1708.                     + 1
  1709.                     + temp_home ? strlen (temp_home)
  1710.                     : 0);
  1711.       *result = '\0';
  1712.  
  1713.       if (temp_home)
  1714.         strcpy (result, temp_home);
  1715.  
  1716.       strcat (result, &filename[1]);
  1717.     }
  1718.       else
  1719.     {
  1720.       struct passwd *user_entry;
  1721.       int i, c;
  1722.       char *username = (char *)xmalloc (257);
  1723.  
  1724.       for (i = 1; c = filename[i]; i++)
  1725.         {
  1726.           if (c == '/')
  1727.         break;
  1728.           else
  1729.         username[i - 1] = c;
  1730.         }
  1731.       if (c)
  1732.         username[i - 1] = '\0';
  1733.  
  1734.       user_entry = getpwnam (username);
  1735.  
  1736.       if (!user_entry)
  1737.         return (strdup (filename));
  1738.  
  1739.       result = (char *)xmalloc (1 + strlen (user_entry->pw_dir)
  1740.                     + strlen (&filename[i]));
  1741.       strcpy (result, user_entry->pw_dir);
  1742.       strcat (result, &filename[i]);
  1743.     }
  1744.     }
  1745.   return (result);
  1746. #else /* __amigaos__ */
  1747.     return (filename);
  1748. #endif /* __amigaos__ */
  1749. }
  1750.  
  1751. char *
  1752. output_name_from_input_name (name)
  1753.      char *name;
  1754. {
  1755.   return (expand_filename ((char *)NULL, name));
  1756. }
  1757.  
  1758. /* **************************************************************** */
  1759. /*                                    */
  1760. /*            Error Handling                    */
  1761. /*                                    */
  1762. /* **************************************************************** */
  1763.  
  1764. /* Number of errors encountered. */
  1765. int errors_printed = 0;
  1766.  
  1767. /* Print the last error gotten from the file system. */
  1768. int
  1769. fs_error (filename)
  1770.      char *filename;
  1771. {
  1772.   remember_error ();
  1773.   perror (filename);
  1774.   return (0);
  1775. }
  1776.  
  1777. /* Print an error message, and return false. */
  1778. #if defined (HAVE_VARARGS_H) && defined (HAVE_VFPRINTF)
  1779.  
  1780. int
  1781. error (va_alist)
  1782.      va_dcl
  1783. {
  1784.   char *format;
  1785.   va_list args;
  1786.  
  1787.   remember_error ();
  1788.   va_start (args);
  1789.   format = va_arg (args, char *);
  1790.   vfprintf (stderr, format, args);
  1791.   va_end (args);
  1792.   fprintf (stderr, "\n");
  1793. }
  1794.  
  1795. /* Just like error (), but print the line number as well. */
  1796. int
  1797. line_error (va_alist)
  1798.      va_dcl
  1799. {
  1800.   char *format;
  1801.   va_list args;
  1802.  
  1803.   remember_error ();
  1804.   va_start (args);
  1805.   format = va_arg (args, char *);
  1806.   fprintf (stderr, "%s:%d: ", input_filename, line_number);
  1807.   vfprintf (stderr, format, args);
  1808.   fprintf (stderr, ".\n");
  1809.   va_end (args);
  1810.   return ((int) 0);
  1811. }
  1812.  
  1813. int
  1814. warning (va_alist)
  1815.      va_dcl
  1816. {
  1817.   char *format;
  1818.   va_list args;
  1819.  
  1820.   va_start (args);
  1821.   format = va_arg (args, char *);
  1822.   if (print_warnings)
  1823.     {
  1824.       fprintf (stderr, "%s:%d: Warning: ", input_filename, line_number);
  1825.       vfprintf (stderr, format, args);
  1826.       fprintf (stderr, ".\n");
  1827.     }
  1828.   va_end (args);
  1829.   return ((int) 0);
  1830. }
  1831.  
  1832. #else /* !(HAVE_VARARGS_H && HAVE_VFPRINTF) */
  1833.  
  1834. int
  1835. error (format, arg1, arg2, arg3, arg4, arg5)
  1836.      char *format;
  1837. {
  1838.   remember_error ();
  1839.   fprintf (stderr, format, arg1, arg2, arg3, arg4, arg5);
  1840.   fprintf (stderr, "\n");
  1841.   return ((int) 0);
  1842. }
  1843.  
  1844. /* Just like error (), but print the line number as well. */
  1845. int
  1846. line_error (format, arg1, arg2, arg3, arg4, arg5)
  1847.      char *format;
  1848. {
  1849.   remember_error ();
  1850.   fprintf (stderr, "%s:%d: ", input_filename, line_number);
  1851.   fprintf (stderr, format, arg1, arg2, arg3, arg4, arg5);
  1852.   fprintf (stderr, ".\n");
  1853.   return ((int) 0);
  1854. }
  1855.  
  1856. int
  1857. warning (format, arg1, arg2, arg3, arg4, arg5)
  1858.      char *format;
  1859. {
  1860.   if (print_warnings)
  1861.     {
  1862.       fprintf (stderr, "%s:%d: Warning: ", input_filename, line_number);
  1863.       fprintf (stderr, format, arg1, arg2, arg3, arg4, arg5);
  1864.       fprintf (stderr, ".\n");
  1865.     }
  1866.   return ((int) 0);
  1867. }
  1868.  
  1869. #endif /* !(HAVE_VARARGS_H && HAVE_VFPRINTF) */
  1870.  
  1871. /* Remember that an error has been printed.  If this is the first
  1872.    error printed, then tell them which program is printing them.
  1873.    If more than max_error_level have been printed, then exit the
  1874.    program. */
  1875. void
  1876. remember_error ()
  1877. {
  1878.   errors_printed++;
  1879.   if (max_error_level && (errors_printed > max_error_level))
  1880.     {
  1881.       fprintf (stderr, "Too many errors!  Gave up.\n");
  1882.       flush_file_stack ();
  1883.       cm_bye ();
  1884.       exit (1);
  1885.     }
  1886. }
  1887.  
  1888. /* **************************************************************** */
  1889. /*                                    */
  1890. /*            Hacking Tokens and Strings            */
  1891. /*                                    */
  1892. /* **************************************************************** */
  1893.  
  1894. /* Return the next token as a string pointer.  We cons the
  1895.    string. */
  1896. char *
  1897. read_token ()
  1898. {
  1899.   int i, character;
  1900.   char *result;
  1901.  
  1902.   /* If the first character to be read is self-delimiting, then that
  1903.      is the command itself. */
  1904.   character = curchar ();
  1905.   if (self_delimiting (character))
  1906.     {
  1907.       input_text_offset++;
  1908.  
  1909.       if (character == '\n')
  1910.     line_number++;
  1911.  
  1912.       result = strdup (" ");
  1913.       *result = character;
  1914.       return (result);
  1915.     }
  1916.  
  1917.   for (i = 0; ((input_text_offset != size_of_input_text)
  1918.            && (character = curchar ())
  1919.            && command_char (character));
  1920.        i++, input_text_offset++);
  1921.   result = (char *)xmalloc (i + 1);
  1922.   memcpy (result, &input_text[input_text_offset - i], i);
  1923.   result[i] = '\0';
  1924.   return (result);
  1925. }
  1926.  
  1927. /* Return non-zero if CHARACTER is self-delimiting. */
  1928. int
  1929. self_delimiting (character)
  1930.      int character;
  1931. {
  1932.   return (member (character, "{}:.@*'`,!?; \n\t"));
  1933. }
  1934.  
  1935. /* Clear whitespace from the front and end of string. */
  1936. void
  1937. canon_white (string)
  1938.      char *string;
  1939. {
  1940.   int len = strlen (string);
  1941.   int x;
  1942.  
  1943.   if (!len)
  1944.     return;
  1945.  
  1946.   for (x = 0; x < len; x++)
  1947.     {
  1948.       if (!cr_or_whitespace (string[x]))
  1949.     {
  1950.       strcpy (string, string + x);
  1951.       break;
  1952.     }
  1953.     }
  1954.   len = strlen (string);
  1955.   if (len)
  1956.     len--;
  1957.   while (len > -1 && cr_or_whitespace (string[len]))
  1958.     len--;
  1959.   string[len + 1] = '\0';
  1960. }
  1961.  
  1962. /* Bash STRING, replacing all whitespace with just one space. */
  1963. void
  1964. fix_whitespace (string)
  1965.      char *string;
  1966. {
  1967.   char *temp = (char *)xmalloc (strlen (string) + 1);
  1968.   int string_index = 0;
  1969.   int temp_index = 0;
  1970.   int c;
  1971.  
  1972.   canon_white (string);
  1973.  
  1974.   while (string[string_index])
  1975.     {
  1976.       c = temp[temp_index++] = string[string_index++];
  1977.  
  1978.       if (c == ' ' || c == '\n' || c == '\t')
  1979.     {
  1980.       temp[temp_index - 1] = ' ';
  1981.       while ((c = string[string_index]) && (c == ' ' ||
  1982.                         c == '\t' ||
  1983.                         c == '\n'))
  1984.         string_index++;
  1985.     }
  1986.     }
  1987.   temp[temp_index] = '\0';
  1988.   strcpy (string, temp);
  1989.   free (temp);
  1990. }
  1991.  
  1992. /* Discard text until the desired string is found.  The string is
  1993.    included in the discarded text. */
  1994. void
  1995. discard_until (string)
  1996.      char *string;
  1997. {
  1998.   int temp = search_forward (string, input_text_offset);
  1999.  
  2000.   int tt = (temp < 0) ? size_of_input_text : temp + strlen (string);
  2001.   int from = input_text_offset;
  2002.  
  2003.   /* Find out what line we are on. */
  2004.   while (from != tt)
  2005.     if (input_text[from++] == '\n')
  2006.       line_number++;
  2007.  
  2008.   if (temp < 0)
  2009.     {
  2010.       input_text_offset = size_of_input_text - strlen (string);
  2011.  
  2012.       if (strcmp (string, "\n") != 0)
  2013.     {
  2014.       line_error ("Expected `%s'", string);
  2015.       return;
  2016.     }
  2017.     }
  2018.   else
  2019.     input_text_offset = temp;
  2020.  
  2021.   input_text_offset += strlen (string);
  2022. }
  2023.  
  2024. /* Read characters from the file until we are at MATCH.
  2025.    Place the characters read into STRING.
  2026.    On exit input_text_offset is after the match string.
  2027.    Return the offset where the string starts. */
  2028. int
  2029. get_until (match, string)
  2030.      char *match, **string;
  2031. {
  2032.   int len, current_point, x, new_point, tem;
  2033.  
  2034.   current_point = x = input_text_offset;
  2035.   new_point = search_forward (match, input_text_offset);
  2036.  
  2037.   if (new_point < 0)
  2038.     new_point = size_of_input_text;
  2039.   len = new_point - current_point;
  2040.  
  2041.   /* Keep track of which line number we are at. */
  2042.   tem = new_point + (strlen (match) - 1);
  2043.   while (x != tem)
  2044.     if (input_text[x++] == '\n')
  2045.       line_number++;
  2046.  
  2047.   *string = (char *)xmalloc (len + 1);
  2048.  
  2049.   memcpy (*string, &input_text[current_point], len);
  2050.   (*string)[len] = '\0';
  2051.  
  2052.   /* Now leave input_text_offset in a consistent state. */
  2053.   input_text_offset = tem;
  2054.  
  2055.   if (input_text_offset > size_of_input_text)
  2056.     input_text_offset = size_of_input_text;
  2057.  
  2058.   return (new_point);
  2059. }
  2060.  
  2061. /* Read characters from the file until we are at MATCH or end of line.
  2062.    Place the characters read into STRING.  */
  2063. void
  2064. get_until_in_line (match, string)
  2065.      char *match, **string;
  2066. {
  2067.   int real_bottom, temp;
  2068.  
  2069.   real_bottom = size_of_input_text;
  2070.   temp = search_forward ("\n", input_text_offset);
  2071.  
  2072.   if (temp < 0)
  2073.     temp = size_of_input_text;
  2074.  
  2075.   size_of_input_text = temp;
  2076.   get_until (match, string);
  2077.   size_of_input_text = real_bottom;
  2078. }
  2079.  
  2080. void
  2081. get_rest_of_line (string)
  2082.      char **string;
  2083. {
  2084.   get_until ("\n", string);
  2085.   canon_white (*string);
  2086.  
  2087.   if (curchar () == '\n')    /* as opposed to the end of the file... */
  2088.     {
  2089.       line_number++;
  2090.       input_text_offset++;
  2091.     }
  2092. }
  2093.  
  2094. /* Backup the input pointer to the previous character, keeping track
  2095.    of the current line number. */
  2096. void
  2097. backup_input_pointer ()
  2098. {
  2099.   if (input_text_offset)
  2100.     {
  2101.       input_text_offset--;
  2102.       if (curchar () == '\n')
  2103.     line_number--;
  2104.     }
  2105. }
  2106.  
  2107. /* Read characters from the file until we are at MATCH or closing brace.
  2108.    Place the characters read into STRING.  */
  2109. void
  2110. get_until_in_braces (match, string)
  2111.      char *match, **string;
  2112. {
  2113.   int i, brace = 0;
  2114.   int match_len = strlen (match);
  2115.   char *temp;
  2116.  
  2117.   for (i = input_text_offset; i < size_of_input_text; i++)
  2118.     {
  2119.       if (input_text[i] == '{')
  2120.     brace++;
  2121.       else if (input_text[i] == '}')
  2122.     brace--;
  2123.       else if (input_text[i] == '\n')
  2124.     line_number++;
  2125.  
  2126.       if (brace < 0 ||
  2127.       (brace == 0 && strncmp (input_text + i, match, match_len) == 0))
  2128.     break;
  2129.     }
  2130.  
  2131.   match_len = i - input_text_offset;
  2132.   temp = (char *)xmalloc (2 + match_len);
  2133.   strncpy (temp, input_text + input_text_offset, match_len);
  2134.   temp[match_len] = '\0';
  2135.   input_text_offset = i;
  2136.   *string = temp;
  2137. }
  2138.  
  2139. /* **************************************************************** */
  2140. /*                                    */
  2141. /*            Converting the File                 */
  2142. /*                                    */
  2143. /* **************************************************************** */
  2144.  
  2145. /* Convert the file named by NAME.  The output is saved on the file
  2146.    named as the argument to the @setfilename command. */
  2147. static char *suffixes[] = {
  2148.   "",
  2149.   ".texinfo",
  2150.   ".texi",
  2151.   ".txinfo",
  2152.   (char *)NULL
  2153. };
  2154.  
  2155. void
  2156. initialize_conversion ()
  2157. {
  2158.   init_tag_table ();
  2159.   init_indices ();
  2160.   init_internals ();
  2161.   init_paragraph ();
  2162. }
  2163.  
  2164.   /* We read in multiples of 4k, simply because it is a typical pipe size
  2165.      on unix systems. */
  2166. #define _READ_BUFFER_GROWTH (4 * 4096)
  2167.  
  2168. /* Convert the texinfo file coming from the open stream STREAM.  Assume the
  2169.    source of the stream is named NAME. */
  2170. void
  2171. convert_from_stream (stream, name)
  2172.      FILE *stream;
  2173.      char *name;
  2174. {
  2175.   char *buffer = (char *)NULL;
  2176.   int buffer_offset = 0, buffer_size = 0;
  2177.  
  2178.   initialize_conversion ();
  2179.  
  2180.   /* Read until the end of the stream.  This isn't strictly correct, since
  2181.      the texinfo input may end before the stream ends, but it is a quick
  2182.      working hueristic. */
  2183.   while (!feof (stream))
  2184.     {
  2185.       int count;
  2186.  
  2187.       if (buffer_offset + (_READ_BUFFER_GROWTH + 1) >= buffer_size)
  2188.     buffer = (char *)
  2189.       xrealloc (buffer, (buffer_size += _READ_BUFFER_GROWTH));
  2190.  
  2191.       count = fread (buffer + buffer_offset, 1, _READ_BUFFER_GROWTH, stream);
  2192.  
  2193.       if (count < 0)
  2194.     {
  2195.       perror (name);
  2196.       exit (FATAL);
  2197.     }
  2198.  
  2199.       buffer_offset += count;
  2200.       if (count == 0)
  2201.     break;
  2202.     }
  2203.  
  2204.   /* Set the globals to the new file. */
  2205.   input_text = buffer;
  2206.   size_of_input_text = buffer_offset;
  2207.   input_filename = strdup (name);
  2208.   node_filename = strdup (name);
  2209.   input_text_offset = 0;
  2210.   line_number = 1;
  2211.  
  2212.   /* Not strictly necessary.  This magic prevents read_token () from doing
  2213.      extra unnecessary work each time it is called (that is a lot of times).
  2214.      The SIZE_OF_INPUT_TEXT is one past the actual end of the text. */
  2215.   input_text[size_of_input_text] = '\n';
  2216.  
  2217.   convert_from_loaded_file (name);
  2218. }
  2219.  
  2220. void
  2221. convert_from_file (name)
  2222.      char *name;
  2223. {
  2224.   register int i;
  2225.   char *filename = (char *)xmalloc (strlen (name) + 50);
  2226.  
  2227.   initialize_conversion ();
  2228.  
  2229.   /* Try to load the file specified by NAME.  If the file isn't found, and
  2230.      there is no suffix in NAME, then try NAME.texinfo, and NAME.texi. */
  2231.   for (i = 0; suffixes[i]; i++)
  2232.     {
  2233.       strcpy (filename, name);
  2234.       strcat (filename, suffixes[i]);
  2235.  
  2236.       if (find_and_load (filename))
  2237.     break;
  2238.  
  2239.       if (!suffixes[i][0] && strrchr (filename, '.'))
  2240.     {
  2241.       fs_error (filename);
  2242.       free (filename);
  2243.       return;
  2244.     }
  2245.     }
  2246.  
  2247.   if (!suffixes[i])
  2248.     {
  2249.       fs_error (name);
  2250.       free (filename);
  2251.       return;
  2252.     }
  2253.  
  2254.   input_filename = filename;
  2255.  
  2256.   convert_from_loaded_file (name);
  2257. }
  2258.   
  2259. void
  2260. convert_from_loaded_file (name)
  2261.      char *name;
  2262. {
  2263.   char *expand_filename (), *filename_part ();
  2264.   char *real_output_filename = (char *)NULL;
  2265.  
  2266. #if defined (HAVE_MACROS)
  2267.   remember_itext (input_text, 0);
  2268. #endif /* HAVE_MACROS */
  2269.  
  2270.   /* Search this file looking for the special string which starts conversion.
  2271.      Once found, we may truly begin. */
  2272.   input_text_offset = 0;
  2273.   while (input_text_offset >= 0)
  2274.     {
  2275.       input_text_offset =
  2276.     search_forward (setfilename_search, input_text_offset);
  2277.  
  2278.       if ((input_text_offset == 0) ||
  2279.       ((input_text_offset > 0) &&
  2280.        (input_text[input_text_offset -1] == '\n')))
  2281.     break;
  2282.       else if (input_text_offset > 0)
  2283.     input_text_offset++;
  2284.     }
  2285.  
  2286.   if (input_text_offset < 0)
  2287.     {
  2288.       if (!command_output_filename)
  2289.     {
  2290. #if defined (REQUIRE_SETFILENAME)
  2291.       error ("No `%s' found in `%s'", setfilename_search, name);
  2292.       goto finished;
  2293. #else
  2294.       register int i, end_of_first_line;
  2295.  
  2296.       /* Find the end of the first line in the file. */
  2297.       for (i = 0; i < size_of_input_text - 1; i++)
  2298.         if (input_text[i] == '\n')
  2299.           break;
  2300.  
  2301.       end_of_first_line = i + 1;
  2302.  
  2303.       input_text_offset = 0;
  2304.  
  2305.       for (i = 0; i < end_of_first_line; i++)
  2306.         {
  2307.           if ((input_text[i] == '\\') &&
  2308.           (strncmp (input_text + i + 1, "include", 7) == 0))
  2309.         {
  2310.           input_text_offset = end_of_first_line;
  2311.           break;
  2312.         }
  2313.         }
  2314.       command_output_filename = output_name_from_input_name (name);
  2315. #endif /* !REQUIRE_SETFILENAME */
  2316.     }
  2317.     }
  2318.   else
  2319.     input_text_offset += strlen (setfilename_search);
  2320.  
  2321.   if (!command_output_filename)
  2322.     get_until ("\n", &output_filename);
  2323.   else
  2324.     {
  2325.       if (input_text_offset != -1)
  2326.     discard_until ("\n");
  2327.       else
  2328.     input_text_offset = 0;
  2329.  
  2330.       real_output_filename = output_filename = command_output_filename;
  2331.       command_output_filename = (char *)NULL;
  2332.     }
  2333.  
  2334.   canon_white (output_filename);
  2335.  
  2336.   if (real_output_filename &&
  2337.       strcmp (real_output_filename, "-") == 0)
  2338.     {
  2339.       real_output_filename = strdup (real_output_filename);
  2340.       output_stream = stdout;
  2341.       splitting = 0;        /* Cannot split when writing to stdout. */
  2342.     }
  2343.   else
  2344.     {
  2345.       if (!real_output_filename)
  2346.     real_output_filename = expand_filename (output_filename, name);
  2347.       else
  2348.     real_output_filename = strdup (real_output_filename);
  2349.  
  2350.       output_stream = fopen (real_output_filename, "w");
  2351.     }
  2352.  
  2353.   if (output_stream != stdout)
  2354.     {
  2355.       if (amiga_guide) 
  2356.         {
  2357.           if (no_headers)
  2358.             printf ("Making ASCII file `%s' from `%s'.\n", output_filename, name);
  2359.           else
  2360.             {
  2361.               if (amiga_guide_40)
  2362.                 printf ("Making AmigaGuide® V40 file `%s' from `%s'.\n", output_filename, name);
  2363.               else if (amiga_guide_39)
  2364.                 printf ("Making AmigaGuide® V39 file `%s' from `%s'.\n", output_filename, name);
  2365.               else if (amiga_guide_34)
  2366.                 printf ("Making AmigaGuide® V34 file `%s' from `%s'.\n", output_filename, name);
  2367.             }
  2368.         }
  2369.       else
  2370.     printf ("Making info file `%s' from `%s'.\n", output_filename, name);
  2371.     }
  2372.  
  2373.   if (output_stream == NULL)
  2374.     {
  2375.       fs_error (real_output_filename);
  2376.       goto finished;
  2377.     }
  2378.  
  2379.   /* Make the displayable filename from output_filename.  Only the base
  2380.      portion of the filename need be displayed. */
  2381.   if (output_stream != stdout)
  2382.     pretty_output_filename = filename_part (output_filename);
  2383.   else
  2384.     pretty_output_filename = strdup ("stdout");
  2385.  
  2386.   /* For this file only, count the number of newlines from the top of
  2387.      the file to here.  This way, we keep track of line numbers for
  2388.      error reporting.  Line_number starts at 1, since the user isn't
  2389.      zero-based. */
  2390.   {
  2391.     int temp = 0;
  2392.     line_number = 1;
  2393.     while (temp != input_text_offset)
  2394.       if (input_text[temp++] == '\n')
  2395.     line_number++;
  2396.   }
  2397.  
  2398.   if (!no_headers)
  2399.     {
  2400.       if (amiga_guide)
  2401.         {
  2402.           int old_paragraph_indentation = inhibit_paragraph_indentation;
  2403.           int old_filling_enabled = filling_enabled;
  2404.  
  2405.           inhibit_paragraph_indentation=1;
  2406.           filling_enabled = 0;
  2407.  
  2408.           add_word_args ("@database %s\n",output_filename);
  2409.           add_word_args ("\n@Master %s\n", input_filename);
  2410.           add_word_args ("\n@Width %d\n",fill_column);
  2411.  
  2412.           add_word_args ("\n\nThis is the AmigaGuide®  file %s, produced by Makeinfo-%d.%d from ",
  2413.                      output_filename, major_version, minor_version);
  2414.           add_word_args ("\nthe input file %s.\n\n", input_filename);
  2415.  
  2416.           inhibit_paragraph_indentation = old_paragraph_indentation;
  2417.           filling_enabled = old_filling_enabled;
  2418.         }
  2419.       else
  2420.         {
  2421.       add_word_args ("This is Info file %s, produced by Makeinfo-%d.%d from ",
  2422.              output_filename, major_version, minor_version);
  2423.       add_word_args ("the input file %s.\n", input_filename);
  2424.     }
  2425.     }
  2426.  
  2427.   close_paragraph ();
  2428.   reader_loop ();
  2429.  
  2430. finished:
  2431.   close_paragraph ();
  2432.   flush_file_stack ();
  2433.  
  2434. #if defined (HAVE_MACROS)
  2435.   if (macro_expansion_output_stream)
  2436.     fclose (macro_expansion_output_stream);
  2437. #endif /* HAVE_MACROS */
  2438.  
  2439.   if (output_stream != NULL)
  2440.     {
  2441.       output_pending_notes ();
  2442.       free_pending_notes ();
  2443.       if (tag_table != NULL)
  2444.     {
  2445.       tag_table = (TAG_ENTRY *) reverse_list (tag_table);
  2446.           if (!no_headers && !amiga_guide)
  2447.         write_tag_table ();
  2448.     }
  2449.  
  2450.       if (amiga_guide && !no_headers)
  2451.         {
  2452.           inhibit_paragraph_indentation = 1;
  2453.           add_word("\n\n@EndNode\n\n");
  2454.         }
  2455.  
  2456.       if (output_stream != stdout)
  2457.     fclose (output_stream);
  2458.  
  2459.       /* If validating, then validate the entire file right now. */
  2460.       if (validating)
  2461.     validate_file (tag_table);
  2462.  
  2463.       /* This used to test  && !errors_printed.
  2464.      But some files might have legit warnings.  So split anyway.  */
  2465.       if (splitting)
  2466.     split_file (real_output_filename, 0);
  2467.     }
  2468. /*  free (real_output_filename); BUG Fix -kdp */
  2469. }
  2470.  
  2471. void
  2472. free_and_clear (pointer)
  2473.      char **pointer;
  2474. {
  2475.   if ((*pointer) != (char *) NULL)
  2476.     {
  2477.       free (*pointer);
  2478.       *pointer = (char *) NULL;
  2479.     }
  2480. }
  2481.  
  2482.  /* Initialize some state. */
  2483. void
  2484. init_internals ()
  2485. {
  2486.   free_and_clear (¤t_node);
  2487.   free_and_clear (&output_filename);
  2488.   free_and_clear (&command);
  2489.   free_and_clear (&input_filename);
  2490.   free_node_references ();
  2491.   init_insertion_stack ();
  2492.   init_brace_stack ();
  2493.   command_index = 0;
  2494.   in_menu = 0;
  2495.   top_node_seen = 0;
  2496.   non_top_node_seen = 0;
  2497. }
  2498.  
  2499. void
  2500. init_paragraph ()
  2501. {
  2502.   free_and_clear (&output_paragraph);
  2503.   output_paragraph = (unsigned char *)xmalloc (paragraph_buffer_len);
  2504.   output_position = 0;
  2505.   output_paragraph[0] = '\0';
  2506.   output_paragraph_offset = 0;
  2507.   output_column = 0;
  2508.   paragraph_is_open = 0;
  2509.   current_indent = 0;
  2510. }
  2511.  
  2512. /* Okay, we are ready to start the conversion.  Call the reader on
  2513.    some text, and fill the text as it is output.  Handle commands by
  2514.    remembering things like open braces and the current file position on a
  2515.    stack, and when the corresponding close brace is found, you can call
  2516.    the function with the proper arguments. */
  2517. void
  2518. reader_loop ()
  2519. {
  2520.   int character;
  2521.   int done = 0;
  2522.   int dash_count = 0;
  2523.  
  2524.   while (!done)
  2525.     {
  2526.       if (input_text_offset >= size_of_input_text)
  2527.     break;
  2528.  
  2529.       character = curchar ();
  2530.  
  2531.       if (!in_fixed_width_font &&
  2532.       (character == '\'' || character == '`') &&
  2533.       input_text[input_text_offset + 1] == character)
  2534.     {
  2535.       input_text_offset++;
  2536.       character = '"';
  2537.     }
  2538.  
  2539.       if (character == '-')
  2540.     {
  2541.       dash_count++;
  2542.       if (dash_count == 2 && !in_fixed_width_font)
  2543.         {
  2544.           input_text_offset++;
  2545.           continue;
  2546.         }
  2547.     }
  2548.       else
  2549.     {
  2550.       dash_count = 0;
  2551.     }
  2552.  
  2553.       /* If this is a whitespace character, then check to see if the line
  2554.      is blank.  If so, advance to the carriage return. */
  2555.       if (whitespace (character))
  2556.     {
  2557.       register int i = input_text_offset + 1;
  2558.  
  2559.       while (i < size_of_input_text && whitespace (input_text[i]))
  2560.         i++;
  2561.  
  2562.       if (i == size_of_input_text || input_text[i] == '\n')
  2563.         {
  2564.           if (i == size_of_input_text)
  2565.         i--;
  2566.  
  2567.           input_text_offset = i;
  2568.           character = curchar ();
  2569.         }
  2570.     }
  2571.  
  2572.       if (character == '\n')
  2573.     {
  2574.       line_number++;
  2575.  
  2576.       /* Check for a menu entry here, since the "escape sequence"
  2577.          that begins menu entrys is "\n* ". */
  2578.       if (in_menu && input_text_offset + 1 < size_of_input_text)
  2579.         {
  2580.           char *glean_node_from_menu (), *tem;
  2581.               int amiga_guide_convert_menu ();
  2582.  
  2583.           /* Note that the value of TEM is discarded, since it is
  2584.          gauranteed to be NULL when glean_node_from_menu () is
  2585.          called with a non-zero argument. */
  2586.           tem = glean_node_from_menu (1);
  2587.  
  2588.               if (amiga_guide && !no_headers)
  2589.                 {
  2590.                   if (amiga_guide_convert_menu())
  2591.                     continue;
  2592.                 }
  2593.         }
  2594.     }
  2595.  
  2596.       switch (character)
  2597.     {
  2598.     case COMMAND_PREFIX:
  2599.       read_command ();
  2600.       break;
  2601.  
  2602.     case '{':
  2603.  
  2604.       /* Special case.  I'm not supposed to see this character by itself.
  2605.          If I do, it means there is a syntax error in the input text.
  2606.          Report the error here, but remember this brace on the stack so
  2607.          you can ignore its partner. */
  2608.  
  2609.       line_error ("Misplaced `{'");
  2610.       remember_brace (misplaced_brace);
  2611.  
  2612.       /* Don't advance input_text_offset since this happens in
  2613.          remember_brace ().
  2614.          input_text_offset++;
  2615.            */
  2616.       break;
  2617.  
  2618.     case '}':
  2619.       pop_and_call_brace ();
  2620.       input_text_offset++;
  2621.       break;
  2622.  
  2623.     default:
  2624.       add_char (character);
  2625.       input_text_offset++;
  2626.     }
  2627.     }
  2628. #if defined (HAVE_MACROS)
  2629.   if (macro_expansion_output_stream)
  2630.     maybe_write_itext (input_text, input_text_offset);
  2631. #endif /* HAVE_MACROS */
  2632. }
  2633.  
  2634. /* Find the command corresponding to STRING.  If the command
  2635.    is found, return a pointer to the data structure.  Otherwise
  2636.    return (-1). */
  2637. COMMAND *
  2638. get_command_entry (string)
  2639.      char *string;
  2640. {
  2641.   register int i;
  2642.  
  2643.   for (i = 0; CommandTable[i].name; i++)
  2644.     if (strcmp (CommandTable[i].name, string) == 0)
  2645.       return (&CommandTable[i]);
  2646.  
  2647.   /* This command is not in our predefined command table.  Perhaps
  2648.      it is a user defined command. */
  2649.   for (i = 0; i < user_command_array_len; i++)
  2650.     if (user_command_array[i] &&
  2651.     (strcmp (user_command_array[i]->name, string) == 0))
  2652.       return (user_command_array[i]);
  2653.  
  2654.   /* Nope, we never heard of this command. */
  2655.   return ((COMMAND *) -1);
  2656. }
  2657.  
  2658. /* input_text_offset is right at the command prefix character.
  2659.    Read the next token to determine what to do. */
  2660. void
  2661. read_command ()
  2662. {
  2663.   COMMAND *entry;
  2664.  
  2665.   input_text_offset++;
  2666.   free_and_clear (&command);
  2667.   command = read_token ();
  2668.  
  2669. #if defined (HAVE_MACROS)
  2670.   /* Check to see if this command is a macro.  If so, execute it here. */
  2671.   {
  2672.     MACRO_DEF *def;
  2673.  
  2674.     def = find_macro (command);
  2675.  
  2676.     if (def)
  2677.       {
  2678.     /* We disallow recursive use of a macro call.  Inhibit the expansion
  2679.        of this macro during the life of its execution. */
  2680.     if (!(def->flags & ME_RECURSE))
  2681.       def->inhibited = 1;
  2682.  
  2683.     execute_macro (def);
  2684.  
  2685.     if (!(def->flags & ME_RECURSE))
  2686.       def->inhibited = 0;
  2687.  
  2688.     return;
  2689.       }
  2690.     }
  2691. #endif /* HAVE_MACROS */
  2692.  
  2693.   entry = get_command_entry (command);
  2694.  
  2695.   if (entry == (COMMAND *)-1)
  2696.     {
  2697.       line_error ("Unknown info command `%s'", command);
  2698.       return;
  2699.     }
  2700.  
  2701.   if (entry->argument_in_braces)
  2702.     remember_brace (entry->proc);
  2703.  
  2704.   (*(entry->proc)) (START, output_paragraph_offset, 0);
  2705. }
  2706.  
  2707. /* Return the string which invokes PROC; a pointer to a function. */
  2708. char *
  2709. find_proc_name (proc)
  2710.      COMMAND_FUNCTION *proc;
  2711. {
  2712.   register int i;
  2713.  
  2714.   for (i = 0; CommandTable[i].name; i++)
  2715.     if (proc == CommandTable[i].proc)
  2716.       return (CommandTable[i].name);
  2717.   return ("NO_NAME!");
  2718. }
  2719.  
  2720. void
  2721. init_brace_stack ()
  2722. {
  2723.   brace_stack = (BRACE_ELEMENT *) NULL;
  2724. }
  2725.  
  2726. void
  2727. remember_brace (proc)
  2728.      COMMAND_FUNCTION *proc;
  2729. {
  2730.   if (curchar () != '{')
  2731.     line_error ("%c%s expected `{..}'", COMMAND_PREFIX, command);
  2732.   else
  2733.     input_text_offset++;
  2734.   remember_brace_1 (proc, output_paragraph_offset);
  2735. }
  2736.  
  2737. /* Remember the current output position here.  Save PROC
  2738.    along with it so you can call it later. */
  2739. void
  2740. remember_brace_1 (proc, position)
  2741.      COMMAND_FUNCTION *proc;
  2742.      int position;
  2743. {
  2744.   BRACE_ELEMENT *new = (BRACE_ELEMENT *) xmalloc (sizeof (BRACE_ELEMENT));
  2745.   new->next = brace_stack;
  2746.   new->proc = proc;
  2747.   new->pos = position;
  2748.   new->line = line_number;
  2749.   brace_stack = new;
  2750. }
  2751.  
  2752. /* Pop the top of the brace stack, and call the associated function
  2753.    with the args END and POS. */
  2754. void
  2755. pop_and_call_brace ()
  2756. {
  2757.   BRACE_ELEMENT *temp;
  2758.   COMMAND_FUNCTION *proc;
  2759.   int pos;
  2760.  
  2761.   if (brace_stack == (BRACE_ELEMENT *) NULL)
  2762.     {
  2763.       line_error ("Unmatched close brace");
  2764.       return;
  2765.     }
  2766.  
  2767.   pos = brace_stack->pos;
  2768.   proc = brace_stack->proc;
  2769.   temp = brace_stack->next;
  2770.   free (brace_stack);
  2771.   brace_stack = temp;
  2772.  
  2773.   (*proc) (END, pos, output_paragraph_offset);
  2774. }
  2775.  
  2776. /* Shift all of the markers in `brace_stack' by AMOUNT. */
  2777. void
  2778. adjust_braces_following (here, amount)
  2779.      int here, amount;
  2780. {
  2781.   register BRACE_ELEMENT *stack = brace_stack;
  2782.  
  2783.   while (stack)
  2784.     {
  2785.       if (stack->pos >= here)
  2786.     stack->pos += amount;
  2787.       stack = stack->next;
  2788.     }
  2789. }
  2790.  
  2791. /* You call discard_braces () when you shouldn't have any braces on the stack.
  2792.    I used to think that this happens for commands that don't take arguments
  2793.    in braces, but that was wrong because of things like @code{foo @@}.  So now
  2794.    I only detect it at the beginning of nodes. */
  2795. void
  2796. discard_braces ()
  2797. {
  2798.   if (!brace_stack)
  2799.     return;
  2800.  
  2801.   while (brace_stack)
  2802.     {
  2803.       if (brace_stack->proc != misplaced_brace)
  2804.     {
  2805.       char *proc_name;
  2806.       int temp_line_number = line_number;
  2807.  
  2808.       line_number = brace_stack->line;
  2809.       proc_name = find_proc_name (brace_stack->proc);
  2810.       line_error ("%c%s missing close brace", COMMAND_PREFIX, proc_name);
  2811.       line_number = temp_line_number;
  2812.       pop_and_call_brace ();
  2813.     }
  2814.       else
  2815.     {
  2816.       BRACE_ELEMENT *temp;
  2817.       temp = brace_stack->next;
  2818.       free (brace_stack);
  2819.       brace_stack = temp;
  2820.     }
  2821.     }
  2822. }
  2823.  
  2824. int
  2825. get_char_len (character)
  2826.      int character;
  2827. {
  2828.   /* Return the printed length of the character. */
  2829.   int len;
  2830.  
  2831.   switch (character)
  2832.     {
  2833.     case '\t':
  2834.       len = (output_column + 8) & 0xf7;
  2835.       if (len > fill_column)
  2836.     len = fill_column - output_column;
  2837.       else
  2838.     len = len - output_column;
  2839.       break;
  2840.  
  2841.     case '\n':
  2842.       len = fill_column - output_column;
  2843.       break;
  2844.  
  2845.     case '\\':
  2846.       if (amiga_guide_40) /* We have to write "\\" to get "\" */
  2847.         len = 2;
  2848.       else
  2849.         len = 1;
  2850.  
  2851.     default:
  2852.       if (character < ' ')
  2853.     len = 2;
  2854.       else
  2855.     len = 1;
  2856.     }
  2857.   return (len);
  2858. }
  2859.  
  2860. #if defined (HAVE_VARARGS_H) && defined (HAVE_VSPRINTF)
  2861.  
  2862. void
  2863. add_word_args (va_alist)
  2864.      va_dcl
  2865. {
  2866.   char buffer[1000];
  2867.   char *format;
  2868.   va_list args;
  2869.  
  2870.   va_start (args);
  2871.   format = va_arg (args, char *);
  2872.   vsprintf (buffer, format, args);
  2873.   va_end (args);
  2874.   add_word (buffer);
  2875. }
  2876.  
  2877. #else /* !(HAVE_VARARGS_H && HAVE_VSPRINTF) */
  2878.  
  2879. void
  2880. add_word_args (format, arg1, arg2, arg3, arg4, arg5)
  2881.      char *format;
  2882. {
  2883.   char buffer[1000];
  2884.   sprintf (buffer, format, arg1, arg2, arg3, arg4, arg5);
  2885.   add_word (buffer);
  2886. }
  2887.  
  2888. #endif /* !(HAVE_VARARGS_H && HAVE_VSPRINTF) */
  2889.  
  2890. /* Add STRING to output_paragraph. */
  2891. void
  2892. add_word (string)
  2893.      char *string;
  2894. {
  2895.   while (*string)
  2896.     add_char (*string++);
  2897. }
  2898.  
  2899. /* Non-zero if the last character inserted has the syntax class of NEWLINE. */
  2900. int last_char_was_newline = 1;
  2901.  
  2902. /* The actual last inserted character.  Note that this may be something
  2903.    other than NEWLINE even if last_char_was_newline is 1. */
  2904. int last_inserted_character = 0;
  2905.  
  2906. /* Non-zero means that a newline character has already been
  2907.    inserted, so close_paragraph () should insert one less. */
  2908. int line_already_broken = 0;
  2909.  
  2910. /* AmigaGuide --- Set to the number of hidden chars in the last @Node */
  2911. int amiga_guide_hidden_chars = 0;
  2912.  
  2913. /* When non-zero we have finished an insertion (see end_insertion ()) and we
  2914.    want to ignore false continued paragraph closings. */
  2915. int insertion_paragraph_closed = 0;
  2916.  
  2917. /* Non-zero means attempt to make all of the lines have fill_column width. */
  2918. int do_justification = 0;
  2919.  
  2920. /* Add the character to the current paragraph.  If filling_enabled is
  2921.    non-zero, then do filling as well. */
  2922. void
  2923. add_char (character)
  2924.      int character;
  2925. {
  2926.   /* If we are avoiding outputting headers, and we are currently
  2927.      in a menu, then simply return. */
  2928.   if (no_headers && in_menu)
  2929.     return;
  2930.  
  2931.   /* If we are adding a character now, then we don't have to
  2932.      ignore close_paragraph () calls any more. */
  2933.   if (must_start_paragraph && character != '\n')
  2934.     {
  2935.       must_start_paragraph = 0;
  2936.       line_already_broken = 0;    /* The line is no longer broken. */
  2937.       if ((current_indent > output_column) && (!amiga_guide || !in_amiga_guide_button))
  2938.     {
  2939.       indent (current_indent - output_column);
  2940.       output_column = current_indent;
  2941.     }
  2942.     }
  2943.  
  2944.   if (non_splitting_words && member (character, " \t\n"))
  2945.     character = ' ' | 0x80;
  2946.  
  2947.   insertion_paragraph_closed = 0;
  2948.  
  2949.   switch (character)
  2950.     {
  2951.     case '\n':
  2952.       if (!filling_enabled)
  2953.     {
  2954.       insert ('\n');
  2955.  
  2956.       if (force_flush_right)
  2957.         {
  2958.           close_paragraph ();
  2959.           /* Hack to force single blank lines out in this mode. */
  2960.           flush_output ();
  2961.         }
  2962.  
  2963.       output_column = 0;
  2964.  
  2965.           if ((!no_indent && paragraph_is_open) && (!amiga_guide || !in_amiga_guide_button))
  2966.         indent (output_column = current_indent);
  2967.       break;
  2968.     }
  2969.       else /* CHARACTER is newline, and filling is enabled. */
  2970.     {
  2971.       if (sentence_ender (last_inserted_character))
  2972.         {
  2973.           insert (' ');
  2974.           output_column++;
  2975.           last_inserted_character = character;
  2976.         }
  2977.     }
  2978.  
  2979.       if (last_char_was_newline)
  2980.     {
  2981.       close_paragraph ();
  2982.       pending_indent = 0;
  2983.     }
  2984.       else
  2985.     {
  2986.       last_char_was_newline = 1;
  2987.       insert (' ');
  2988.       output_column++;
  2989.     }
  2990.       break;
  2991.  
  2992.     default:
  2993.       {
  2994.     int len = get_char_len (character);
  2995.     int suppress_insert = 0;
  2996.  
  2997.     if ((character == ' ') && (last_char_was_newline))
  2998.       {
  2999.         if (!paragraph_is_open)
  3000.           {
  3001.         pending_indent++;
  3002.         return;
  3003.           }
  3004.       }
  3005.  
  3006.     if (!paragraph_is_open)
  3007.       {
  3008.         start_paragraph ();
  3009.  
  3010.         /* If the paragraph is supposed to be indented a certain way,
  3011.            then discard all of the pending whitespace.  Otherwise, we
  3012.            let the whitespace stay. */
  3013.             if (!paragraph_start_indent && (!amiga_guide || !in_amiga_guide_button))
  3014.           indent (pending_indent);
  3015.         pending_indent = 0;
  3016.       }
  3017.  
  3018.     if ((output_column += len) > fill_column)
  3019.       {
  3020.         if (filling_enabled)
  3021.           {
  3022.         int temp = output_paragraph_offset;
  3023.         while (--temp > 0 && output_paragraph[temp] != '\n')
  3024.           {
  3025.             /* If we have found a space, we have the place to break
  3026.                the line. */
  3027.             if (output_paragraph[temp] == ' ')
  3028.               {
  3029.             /* Remove trailing whitespace from output. */
  3030.             while (temp && whitespace (output_paragraph[temp - 1]))
  3031.               temp--;
  3032.  
  3033.             output_paragraph[temp++] = '\n';
  3034.  
  3035.             /* We have correctly broken the line where we want
  3036.                to.  What we don't want is spaces following where
  3037.                we have decided to break the line.  We get rid of
  3038.                them. */
  3039.             {
  3040.               int t1 = temp;
  3041.  
  3042.               for (;; t1++)
  3043.                 {
  3044.                   if (t1 == output_paragraph_offset)
  3045.                 {
  3046.                   if (whitespace (character))
  3047.                     suppress_insert = 1;
  3048.                   break;
  3049.                 }
  3050.                   if (!whitespace (output_paragraph[t1]))
  3051.                 break;
  3052.                 }
  3053.  
  3054.               if (t1 != temp)
  3055.                 {
  3056.                   adjust_braces_following (temp, (- (t1 - temp)));
  3057.                   strncpy ((char *) &output_paragraph[temp],
  3058.                        (char *) &output_paragraph[t1],
  3059.                        (output_paragraph_offset - t1));
  3060.                   output_paragraph_offset -= (t1 - temp);
  3061.                 }
  3062.             }
  3063.  
  3064.             /* Filled, but now indent if that is right. */
  3065.             if (indented_fill && current_indent)
  3066.               {
  3067.                 int buffer_len = ((output_paragraph_offset - temp)
  3068.                           + current_indent);
  3069.                 char *temp_buffer = (char *)xmalloc (buffer_len);
  3070.                 int indentation = 0;
  3071.  
  3072.                 /* We have to shift any markers that are in
  3073.                    front of the wrap point. */
  3074.                 adjust_braces_following (temp, current_indent);
  3075.  
  3076.                 while (current_indent > 0 &&
  3077.                    indentation != current_indent)
  3078.                   temp_buffer[indentation++] = ' ';
  3079.  
  3080.                 strncpy ((char *) &temp_buffer[current_indent],
  3081.                      (char *) &output_paragraph[temp],
  3082.                      buffer_len - current_indent);
  3083.  
  3084.                 if (output_paragraph_offset + buffer_len
  3085.                 >= paragraph_buffer_len)
  3086.                   {
  3087.                 unsigned char *tt = xrealloc
  3088.                   (output_paragraph,
  3089.                    (paragraph_buffer_len += buffer_len));
  3090.                 output_paragraph = tt;
  3091.                   }
  3092.                 strncpy ((char *) &output_paragraph[temp],
  3093.                      temp_buffer, buffer_len);
  3094.                 output_paragraph_offset += current_indent;
  3095.                 free (temp_buffer);
  3096.               }
  3097.             output_column = 0;
  3098.                         if (amiga_guide && !strncmp("@{\"", &output_paragraph[temp+current_indent], 3))
  3099.                           output_column = -amiga_guide_hidden_chars;
  3100.             while (temp < output_paragraph_offset)
  3101.               output_column +=
  3102.                 get_char_len (output_paragraph[temp++]);
  3103.             output_column += len;
  3104.             break;
  3105.               }
  3106.           }
  3107.           }
  3108.       }
  3109.  
  3110.     if (!suppress_insert)
  3111.       {
  3112.         insert (character);
  3113.         last_inserted_character = character;
  3114.       }
  3115.     last_char_was_newline = 0;
  3116.     line_already_broken = 0;
  3117.       }
  3118.     }
  3119. }
  3120.  
  3121. /* Insert CHARACTER into OUTPUT_PARAGRAPH. */
  3122. void
  3123. insert (character)
  3124.      int character;
  3125. {
  3126.   if (amiga_guide_40 && character == '\\')
  3127.     {
  3128.       output_paragraph[output_paragraph_offset++] = character;
  3129.       if (output_paragraph_offset == paragraph_buffer_len)
  3130.         {
  3131.           output_paragraph =
  3132.             xrealloc (output_paragraph, (paragraph_buffer_len += 100));
  3133.         }
  3134.       output_paragraph[output_paragraph_offset++] = character;
  3135.     }
  3136.   else
  3137.     output_paragraph[output_paragraph_offset++] = character;
  3138.   
  3139.   if (output_paragraph_offset == paragraph_buffer_len)
  3140.     {
  3141.       output_paragraph =
  3142.     xrealloc (output_paragraph, (paragraph_buffer_len += 100));
  3143.     }
  3144. }
  3145.  
  3146. /* Remove upto COUNT characters of whitespace from the
  3147.    the current output line.  If COUNT is less than zero,
  3148.    then remove until none left. */
  3149. void
  3150. kill_self_indent (count)
  3151.      int count;
  3152. {
  3153.   /* Handle infinite case first. */
  3154.   if (count < 0)
  3155.     {
  3156.       output_column = 0;
  3157.       while (output_paragraph_offset)
  3158.     {
  3159.       if (whitespace (output_paragraph[output_paragraph_offset - 1]))
  3160.         output_paragraph_offset--;
  3161.       else
  3162.         break;
  3163.     }
  3164.     }
  3165.   else
  3166.     {
  3167.       while (output_paragraph_offset && count--)
  3168.     if (whitespace (output_paragraph[output_paragraph_offset - 1]))
  3169.       output_paragraph_offset--;
  3170.     else
  3171.       break;
  3172.     }
  3173. }
  3174.  
  3175. /* Non-zero means do not honor calls to flush_output (). */
  3176. static int flushing_ignored = 0;
  3177.  
  3178. /* Prevent calls to flush_output () from having any effect. */
  3179. void
  3180. inhibit_output_flushing ()
  3181. {
  3182.   flushing_ignored++;
  3183. }
  3184.  
  3185. /* Allow calls to flush_output () to write the paragraph data. */
  3186. void
  3187. uninhibit_output_flushing ()
  3188. {
  3189.   flushing_ignored--;
  3190. }
  3191.  
  3192. void
  3193. flush_output ()
  3194. {
  3195.   register int i;
  3196.  
  3197.   if (!output_paragraph_offset || flushing_ignored)
  3198.     return;
  3199.  
  3200.   for (i = 0; i < output_paragraph_offset; i++)
  3201.     {
  3202.       if (amiga_guide)
  3203.         {
  3204.           if (output_paragraph[i] == (unsigned char) (' ' | 0x80))
  3205.             output_paragraph[i] &= 0x7f;
  3206.           if (sentence_ender (UNMETA (output_paragraph[i]) + 32))
  3207.             output_paragraph[i] = (output_paragraph[i] & 0x7f) + 32;
  3208.         }
  3209.       else 
  3210.         {  
  3211.           if (output_paragraph[i] == (unsigned char)(' ' | 0x80))
  3212.         output_paragraph[i] &= 0x7f;
  3213.         }
  3214.     }
  3215.  
  3216.   fwrite (output_paragraph, 1, output_paragraph_offset, output_stream);
  3217.  
  3218.   output_position += output_paragraph_offset;
  3219.   output_paragraph_offset = 0;
  3220. }
  3221.  
  3222. /* How to close a paragraph controlling the number of lines between
  3223.    this one and the last one. */
  3224.  
  3225. /* Paragraph spacing is controlled by this variable.  It is the number of
  3226.    blank lines that you wish to appear between paragraphs.  A value of
  3227.    1 creates a single blank line between paragraphs. */
  3228. int paragraph_spacing = DEFAULT_PARAGRAPH_SPACING;
  3229.  
  3230. /* Close the current paragraph, leaving no blank lines between them. */
  3231. void
  3232. close_single_paragraph ()
  3233. {
  3234.   close_paragraph_with_lines (0);
  3235. }
  3236.  
  3237. /* Close a paragraph after an insertion has ended. */
  3238. void
  3239. close_insertion_paragraph ()
  3240. {
  3241.   if (!insertion_paragraph_closed)
  3242.     {
  3243.       /* Close the current paragraph, breaking the line. */
  3244.       close_single_paragraph ();
  3245.  
  3246.       /* Start a new paragraph here, inserting whatever indention is correct
  3247.      for the now current insertion level (one above the one that we are
  3248.      ending). */
  3249.       start_paragraph ();
  3250.  
  3251.       /* Tell close_paragraph () that the previous line has already been
  3252.      broken, so it should insert one less newline. */
  3253.       line_already_broken = 1;
  3254.  
  3255.       /* Let functions such as add_char () know that we have already found a
  3256.      newline. */
  3257.       ignore_blank_line ();
  3258.     }
  3259.   else
  3260.     {
  3261.       /* If the insertion paragraph is closed already, then we are seeing
  3262.      two `@end' commands in a row.  Note that the first one we saw was
  3263.      handled in the first part of this if-then-else clause, and at that
  3264.      time start_paragraph () was called, partially to handle the proper
  3265.      indentation of the current line.  However, the indentation level
  3266.      may have just changed again, so we may have to outdent the current
  3267.      line to the new indentation level. */
  3268.       if (current_indent < output_column)
  3269.     kill_self_indent (output_column - current_indent);
  3270.     }
  3271.  
  3272.   insertion_paragraph_closed = 1;
  3273. }
  3274.  
  3275. void
  3276. close_paragraph_with_lines (lines)
  3277.      int lines;
  3278. {
  3279.   int old_spacing = paragraph_spacing;
  3280.   paragraph_spacing = lines;
  3281.   close_paragraph ();
  3282.   paragraph_spacing = old_spacing;
  3283. }
  3284.  
  3285. /* Close the currently open paragraph. */
  3286. void
  3287. close_paragraph ()
  3288. {
  3289.   register int i;
  3290.  
  3291.   /* The insertion paragraph is no longer closed. */
  3292.   insertion_paragraph_closed = 0;
  3293.  
  3294.   if (paragraph_is_open && !must_start_paragraph)
  3295.     {
  3296.       register int tindex, c;
  3297.  
  3298.       tindex = output_paragraph_offset;
  3299.  
  3300.       /* Back up to last non-newline/space character, forcing all such
  3301.      subsequent characters to be newlines.  This isn't strictly
  3302.      necessary, but a couple of functions use the presence of a newline
  3303.      to make decisions. */
  3304.       for (tindex = output_paragraph_offset - 1; tindex >= 0; --tindex)
  3305.     {
  3306.       c = output_paragraph[tindex];
  3307.  
  3308.       if (c == ' '|| c == '\n')
  3309.         output_paragraph[tindex] = '\n';
  3310.       else
  3311.         break;
  3312.     }
  3313.  
  3314.       /* All trailing whitespace is ignored. */
  3315.       output_paragraph_offset = ++tindex;
  3316.  
  3317.       /* Break the line if that is appropriate. */
  3318.       if (paragraph_spacing >= 0)
  3319.     insert ('\n');
  3320.  
  3321.       /* Add as many blank lines as is specified in PARAGRAPH_SPACING. */
  3322.       if (!force_flush_right)
  3323.     {
  3324.       for (i = 0; i < (paragraph_spacing - line_already_broken); i++)
  3325.         insert ('\n');
  3326.     }
  3327.  
  3328.       /* If we are doing flush right indentation, then do it now
  3329.      on the paragraph (really a single line). */
  3330.       if (force_flush_right)
  3331.     do_flush_right_indentation ();
  3332.  
  3333.       flush_output ();
  3334.       paragraph_is_open = 0;
  3335.       no_indent = 0;
  3336.       output_column = 0;
  3337.     }
  3338.   ignore_blank_line ();
  3339. }
  3340.  
  3341. /* Make the last line just read look as if it were only a newline. */
  3342. void
  3343. ignore_blank_line ()
  3344. {
  3345.   last_inserted_character = '\n';
  3346.   last_char_was_newline = 1;
  3347. }
  3348.  
  3349. /* Align the end of the text in output_paragraph with fill_column. */
  3350. void
  3351. do_flush_right_indentation ()
  3352. {
  3353.   char *temp;
  3354.   int temp_len;
  3355.  
  3356.   kill_self_indent (-1);
  3357.  
  3358.   if (output_paragraph[0] != '\n')
  3359.     {
  3360.       output_paragraph[output_paragraph_offset] = '\0';
  3361.  
  3362.       if (output_paragraph_offset < fill_column)
  3363.     {
  3364.       register int i;
  3365.  
  3366.       if (fill_column >= paragraph_buffer_len)
  3367.         output_paragraph =
  3368.           xrealloc (output_paragraph,
  3369.             (paragraph_buffer_len += fill_column));
  3370.  
  3371.       temp_len = strlen ((char *)output_paragraph);
  3372.       temp = (char *)xmalloc (temp_len + 1);
  3373.       memcpy (temp, (char *)output_paragraph, temp_len);
  3374.  
  3375.       for (i = 0; i < fill_column - output_paragraph_offset; i++)
  3376.         output_paragraph[i] = ' ';
  3377.  
  3378.       memcpy ((char *)output_paragraph + i, temp, temp_len);
  3379.       free (temp);
  3380.       output_paragraph_offset = fill_column;
  3381.     }
  3382.     }
  3383. }
  3384.  
  3385. /* Begin a new paragraph. */
  3386. void
  3387. start_paragraph ()
  3388. {
  3389.   /* First close existing one. */
  3390.   if (paragraph_is_open)
  3391.     close_paragraph ();
  3392.  
  3393.   /* In either case, the insertion paragraph is no longer closed. */
  3394.   insertion_paragraph_closed = 0;
  3395.  
  3396.   /* However, the paragraph is open! */
  3397.   paragraph_is_open = 1;
  3398.  
  3399.   /* If we MUST_START_PARAGRAPH, that simply means that start_paragraph ()
  3400.      had to be called before we would allow any other paragraph operations
  3401.      to have an effect. */
  3402.   if (!must_start_paragraph)
  3403.     {
  3404.       int amount_to_indent = 0;
  3405.  
  3406.       /* If doing indentation, then insert the appropriate amount. */
  3407.       if (!no_indent && (!amiga_guide || !in_amiga_guide_button))
  3408.     {
  3409.       if (inhibit_paragraph_indentation)
  3410.         {
  3411.           amount_to_indent = current_indent;
  3412.           if (inhibit_paragraph_indentation < 0)
  3413.         inhibit_paragraph_indentation++;
  3414.         }
  3415.       else if (paragraph_start_indent < 0)
  3416.         amount_to_indent = current_indent;
  3417.       else
  3418.         amount_to_indent = current_indent + paragraph_start_indent;
  3419.  
  3420.       if (amount_to_indent >= output_column)
  3421.         {
  3422.           amount_to_indent -= output_column;
  3423.           indent (amount_to_indent);
  3424.           output_column += amount_to_indent;
  3425.         }
  3426.     }
  3427.     }
  3428.   else
  3429.     must_start_paragraph = 0;
  3430. }
  3431.  
  3432. /* Insert the indentation specified by AMOUNT. */
  3433. void
  3434. indent (amount)
  3435.      int amount;
  3436. {
  3437.   register BRACE_ELEMENT *elt = brace_stack;
  3438.  
  3439.   /* For every START_POS saved within the brace stack which will be affected
  3440.      by this indentation, bump that start pos forward. */
  3441.   while (elt)
  3442.     {
  3443.       if (elt->pos >= output_paragraph_offset)
  3444.     elt->pos += amount;
  3445.       elt = elt->next;
  3446.     }
  3447.  
  3448.   while (--amount >= 0)
  3449.     insert (' ');
  3450. }
  3451.  
  3452. /* Search forward for STRING in input_text.
  3453.    FROM says where where to start. */
  3454. int
  3455. search_forward (string, from)
  3456.      char *string;
  3457.      int from;
  3458. {
  3459.   int len = strlen (string);
  3460.  
  3461.   while (from < size_of_input_text)
  3462.     {
  3463.       if (strncmp (input_text + from, string, len) == 0)
  3464.     return (from);
  3465.       from++;
  3466.     }
  3467.   return (-1);
  3468. }
  3469.  
  3470. /* Whoops, Unix doesn't have strcasecmp. */
  3471.  
  3472. /* Case independent string compare. */
  3473. #if !defined (HAVE_STRCASECMP)
  3474. int
  3475. strcasecmp (string1, string2)
  3476.      char *string1, *string2;
  3477. {
  3478.   char ch1, ch2;
  3479.  
  3480.   for (;;)
  3481.     {
  3482.       ch1 = *string1++;
  3483.       ch2 = *string2++;
  3484.  
  3485.       if (!(ch1 | ch2))
  3486.     return (0);
  3487.  
  3488.       ch1 = coerce_to_upper (ch1);
  3489.       ch2 = coerce_to_upper (ch2);
  3490.  
  3491.       if (ch1 != ch2)
  3492.     return (ch1 - ch2);
  3493.     }
  3494. }
  3495. #endif /* !HAVE_STRCASECMP */
  3496.  
  3497. enum insertion_type { menu, quotation, lisp, smalllisp, example,
  3498.   smallexample, display, itemize, format, enumerate, cartouche, table,
  3499.   ftable, vtable, group, ifinfo, flushleft, flushright, ifset, ifclear, deffn,
  3500.   defun, defmac, defspec, defvr, defvar, defopt, deftypefn,
  3501.   deftypefun, deftypevr, deftypevar, defcv, defivar, defop, defmethod,
  3502.   deftypemethod, deftp, bad_type };
  3503.  
  3504. char *insertion_type_names[] = { "menu", "quotation", "lisp",
  3505.   "smalllisp", "example", "smallexample", "display", "itemize",
  3506.   "format", "enumerate", "cartouche", "table", "ftable", "vtable", "group",
  3507.   "ifinfo", "flushleft", "flushright", "ifset", "ifclear", "deffn",
  3508.   "defun", "defmac", "defspec", "defvr", "defvar", "defopt",
  3509.   "deftypefn", "deftypefun", "deftypevr", "deftypevar", "defcv",
  3510.   "defivar", "defop", "defmethod", "deftypemethod", "deftp",
  3511.   "bad_type" };
  3512.  
  3513. int insertion_level = 0;
  3514. typedef struct istack_elt
  3515. {
  3516.   struct istack_elt *next;
  3517.   char *item_function;
  3518.   char *filename;
  3519.   int line_number;
  3520.   int filling_enabled;
  3521.   int indented_fill;
  3522.   enum insertion_type insertion;
  3523.   int inhibited;
  3524. } INSERTION_ELT;
  3525.  
  3526. INSERTION_ELT *insertion_stack = (INSERTION_ELT *) NULL;
  3527.  
  3528. void
  3529. init_insertion_stack ()
  3530. {
  3531.   insertion_stack = (INSERTION_ELT *) NULL;
  3532. }
  3533.  
  3534. /* Return the type of the current insertion. */
  3535. enum insertion_type
  3536. current_insertion_type ()
  3537. {
  3538.   if (!insertion_level)
  3539.     return (bad_type);
  3540.   else
  3541.     return (insertion_stack->insertion);
  3542. }
  3543.  
  3544. /* Return a pointer to the string which is the function to wrap around
  3545.    items. */
  3546. char *
  3547. current_item_function ()
  3548. {
  3549.   register int level, done;
  3550.   register INSERTION_ELT *elt;
  3551.  
  3552.   level = insertion_level;
  3553.   elt = insertion_stack;
  3554.   done = 0;
  3555.  
  3556.   /* Skip down through the stack until we find a non-conditional insertion. */
  3557.   while (!done && (elt != NULL))
  3558.     {
  3559.       switch (elt->insertion)
  3560.     {
  3561.     case ifinfo:
  3562.     case ifset:
  3563.     case ifclear:
  3564.     case cartouche:
  3565.       elt = elt->next;
  3566.       level--;
  3567.       break;
  3568.  
  3569.     default:
  3570.       done = 1;
  3571.     }
  3572.     }
  3573.  
  3574.   if (!level)
  3575.     return ((char *) NULL);
  3576.   else
  3577.     return (elt->item_function);
  3578. }
  3579.  
  3580. char *
  3581. get_item_function ()
  3582. {
  3583.   char *item_function;
  3584.   get_rest_of_line (&item_function);
  3585.   backup_input_pointer ();
  3586.   canon_white (item_function);
  3587.   return (item_function);
  3588. }
  3589.  
  3590.  /* Push the state of the current insertion on the stack. */
  3591. void
  3592. push_insertion (type, item_function)
  3593.      enum insertion_type type;
  3594.      char *item_function;
  3595. {
  3596.   INSERTION_ELT *new = (INSERTION_ELT *) xmalloc (sizeof (INSERTION_ELT));
  3597.  
  3598.   new->item_function = item_function;
  3599.   new->filling_enabled = filling_enabled;
  3600.   new->indented_fill = indented_fill;
  3601.   new->insertion = type;
  3602.   new->line_number = line_number;
  3603.   new->filename = strdup (input_filename);
  3604.   new->inhibited = inhibit_paragraph_indentation;
  3605.   new->next = insertion_stack;
  3606.   insertion_stack = new;
  3607.   insertion_level++;
  3608. }
  3609.  
  3610.  /* Pop the value on top of the insertion stack into the
  3611.     global variables. */
  3612. void
  3613. pop_insertion ()
  3614. {
  3615.   INSERTION_ELT *temp = insertion_stack;
  3616.  
  3617.   if (temp == (INSERTION_ELT *) NULL)
  3618.     return;
  3619.  
  3620.   inhibit_paragraph_indentation = temp->inhibited;
  3621.   filling_enabled = temp->filling_enabled;
  3622.   indented_fill = temp->indented_fill;
  3623.   free_and_clear (&(temp->item_function));
  3624.   free_and_clear (&(temp->filename));
  3625.   insertion_stack = insertion_stack->next;
  3626.   free (temp);
  3627.   insertion_level--;
  3628. }
  3629.  
  3630.  /* Return a pointer to the print name of this
  3631.     enumerated type. */
  3632. char *
  3633. insertion_type_pname (type)
  3634.      enum insertion_type type;
  3635. {
  3636.   if ((int) type < (int) bad_type)
  3637.     return (insertion_type_names[(int) type]);
  3638.   else
  3639.     return ("Broken-Type in insertion_type_pname");
  3640. }
  3641.  
  3642. /* Return the insertion_type associated with NAME.
  3643.    If the type is not one of the known ones, return BAD_TYPE. */
  3644. enum insertion_type
  3645. find_type_from_name (name)
  3646.      char *name;
  3647. {
  3648.   int index = 0;
  3649.   while (index < (int) bad_type)
  3650.     {
  3651.       if (strcmp (name, insertion_type_names[index]) == 0)
  3652.     return (enum insertion_type) index;
  3653.       index++;
  3654.     }
  3655.   return (bad_type);
  3656. }
  3657.  
  3658. void
  3659. do_nothing ()
  3660. {
  3661. }
  3662.  
  3663. int
  3664. defun_insertion (type)
  3665.      enum insertion_type type;
  3666. {
  3667.   return
  3668.     ((type == deffn)
  3669.      || (type == defun)
  3670.      || (type == defmac)
  3671.      || (type == defspec)
  3672.      || (type == defvr)
  3673.      || (type == defvar)
  3674.      || (type == defopt)
  3675.      || (type == deftypefn)
  3676.      || (type == deftypefun)
  3677.      || (type == deftypevr)
  3678.      || (type == deftypevar)
  3679.      || (type == defcv)
  3680.      || (type == defivar)
  3681.      || (type == defop)
  3682.      || (type == defmethod)
  3683.      || (type == deftypemethod)
  3684.      || (type == deftp));
  3685. }
  3686.  
  3687. /* MAX_NS is the maximum nesting level for enumerations.  I picked 100
  3688.    which seemed reasonable.  This doesn't control the number of items,
  3689.    just the number of nested lists. */
  3690. #define max_stack_depth 100
  3691. #define ENUM_DIGITS 1
  3692. #define ENUM_ALPHA  2
  3693. typedef struct {
  3694.   int enumtype;
  3695.   int enumval;
  3696. } DIGIT_ALPHA;
  3697.  
  3698. DIGIT_ALPHA enumstack[max_stack_depth];
  3699. int enumstack_offset = 0;
  3700. int current_enumval = 1;
  3701. int current_enumtype = ENUM_DIGITS;
  3702. char *enumeration_arg = (char *)NULL;
  3703.  
  3704. void
  3705. start_enumerating (at, type)
  3706.      int at, type;
  3707. {
  3708.   if ((enumstack_offset + 1) == max_stack_depth)
  3709.     {
  3710.       line_error ("Enumeration stack overflow");
  3711.       return;
  3712.     }
  3713.   enumstack[enumstack_offset].enumtype = current_enumtype;
  3714.   enumstack[enumstack_offset].enumval = current_enumval;
  3715.   enumstack_offset++;
  3716.   current_enumval = at;
  3717.   current_enumtype = type;
  3718. }
  3719.  
  3720. void
  3721. stop_enumerating ()
  3722. {
  3723.   --enumstack_offset;
  3724.   if (enumstack_offset < 0)
  3725.     enumstack_offset = 0;
  3726.  
  3727.   current_enumval = enumstack[enumstack_offset].enumval;
  3728.   current_enumtype = enumstack[enumstack_offset].enumtype;
  3729. }
  3730.  
  3731. /* Place a letter or digits into the output stream. */
  3732. void
  3733. enumerate_item ()
  3734. {
  3735.   char temp[10];
  3736.  
  3737.   if (current_enumtype == ENUM_ALPHA)
  3738.     {
  3739.       if (current_enumval == ('z' + 1) || current_enumval == ('Z' + 1))
  3740.     {
  3741.       current_enumval = ((current_enumval - 1) == 'z' ? 'a' : 'A');
  3742.       warning ("Lettering overflow, restarting at %c", current_enumval);
  3743.     }
  3744.       sprintf (temp, "%c. ", current_enumval);
  3745.     }
  3746.   else
  3747.     sprintf (temp, "%d. ", current_enumval);
  3748.  
  3749.   indent (output_column += (current_indent - strlen (temp)));
  3750.   add_word (temp);
  3751.   current_enumval++;
  3752. }
  3753.  
  3754. /* This is where the work for all the "insertion" style
  3755.    commands is done.  A huge switch statement handles the
  3756.    various setups, and generic code is on both sides. */
  3757. void
  3758. begin_insertion (type)
  3759.      enum insertion_type type;
  3760. {
  3761.   int no_discard = 0;
  3762.  
  3763.   if (defun_insertion (type))
  3764.     {
  3765.       push_insertion (type, strdup (""));
  3766.       no_discard++;
  3767.     }
  3768.   else
  3769.     push_insertion (type, get_item_function ());
  3770.  
  3771.   switch (type)
  3772.     {
  3773.     case menu:
  3774.       if (!no_headers)
  3775.     close_paragraph ();
  3776.  
  3777.       filling_enabled = no_indent = 0;
  3778.       inhibit_paragraph_indentation = 1;
  3779.  
  3780.       if (!no_headers && !amiga_guide)
  3781.     add_word ("* Menu:\n");
  3782.  
  3783.       in_menu++;
  3784.       no_discard++;
  3785.       break;
  3786.  
  3787.       /* I think @quotation is meant to do filling.
  3788.      If you don't want filling, then use @example. */
  3789.     case quotation:
  3790.       close_single_paragraph ();
  3791.       last_char_was_newline = no_indent = 0;
  3792.       indented_fill = filling_enabled = 1;
  3793.       inhibit_paragraph_indentation = 1;
  3794.       current_indent += default_indentation_increment;
  3795.       break;
  3796.  
  3797.     case display:
  3798.     case example:
  3799.     case smallexample:
  3800.     case lisp:
  3801.     case smalllisp:
  3802.       /* Just like @example, but no indentation. */
  3803.     case format:
  3804.  
  3805.       close_single_paragraph ();
  3806.       inhibit_paragraph_indentation = 1;
  3807.       in_fixed_width_font++;
  3808.       filling_enabled = 0;
  3809.       last_char_was_newline = 0;
  3810.  
  3811.       if (type != format)
  3812.     current_indent += default_indentation_increment;
  3813.  
  3814.       break;
  3815.  
  3816.     case table:
  3817.     case ftable:
  3818.     case vtable:
  3819.     case itemize:
  3820.       close_single_paragraph ();
  3821.       current_indent += default_indentation_increment;
  3822.       filling_enabled = indented_fill = 1;
  3823. #if defined (INDENT_PARAGRAPHS_IN_TABLE)
  3824.       inhibit_paragraph_indentation = 0;
  3825. #else
  3826.       inhibit_paragraph_indentation = 1;
  3827. #endif /* !INDENT_PARAGRAPHS_IN_TABLE */
  3828.  
  3829.       /* Make things work for losers who forget the itemize syntax. */
  3830.       if (allow_lax_format && (type == itemize))
  3831.     {
  3832.       if (!(*insertion_stack->item_function))
  3833.         {
  3834.           free (insertion_stack->item_function);
  3835.           insertion_stack->item_function = strdup ("@bullet");
  3836.           insertion_stack->item_function[0] = COMMAND_PREFIX;
  3837.         }
  3838.     }
  3839.  
  3840.       if (!*insertion_stack->item_function)
  3841.     {
  3842.       line_error ("%s requires an argument: the formatter for %citem",
  3843.               insertion_type_pname (type), COMMAND_PREFIX);
  3844.     }
  3845.       break;
  3846.  
  3847.     case enumerate:
  3848.       close_single_paragraph ();
  3849.       no_indent = 0;
  3850. #if defined (INDENT_PARAGRAPHS_IN_TABLE)
  3851.       inhibit_paragraph_indentation = 0;
  3852. #else
  3853.       inhibit_paragraph_indentation = 1;
  3854. #endif /* !INDENT_PARAGRAPHS_IN_TABLE */
  3855.  
  3856.       current_indent += default_indentation_increment;
  3857.       filling_enabled = indented_fill = 1;
  3858.  
  3859.       if (isdigit (*enumeration_arg))
  3860.     start_enumerating (atoi (enumeration_arg), ENUM_DIGITS);
  3861.       else
  3862.     start_enumerating (*enumeration_arg, ENUM_ALPHA);
  3863.       break;
  3864.  
  3865.       /* Does nothing special in makeinfo. */
  3866.     case group:
  3867.       /* Only close the paragraph if we are not inside of an @example. */
  3868.       if (!insertion_stack->next ||
  3869.       insertion_stack->next->insertion != example)
  3870.     close_single_paragraph ();
  3871.       break;
  3872.  
  3873.       /* Insertions that are no-ops in info, but do something in TeX. */
  3874.     case ifinfo:
  3875.     case ifset:
  3876.     case ifclear:
  3877.     case cartouche:
  3878.       if (in_menu)
  3879.     no_discard++;
  3880.       break;
  3881.  
  3882.     case deffn:
  3883.     case defun:
  3884.     case defmac:
  3885.     case defspec:
  3886.     case defvr:
  3887.     case defvar:
  3888.     case defopt:
  3889.     case deftypefn:
  3890.     case deftypefun:
  3891.     case deftypevr:
  3892.     case deftypevar:
  3893.     case defcv:
  3894.     case defivar:
  3895.     case defop:
  3896.     case defmethod:
  3897.     case deftypemethod:
  3898.     case deftp:
  3899.       inhibit_paragraph_indentation = 1;
  3900.       filling_enabled = indented_fill = 1;
  3901.       current_indent += default_indentation_increment;
  3902.       no_indent = 0;
  3903.       break;
  3904.  
  3905.     case flushleft:
  3906.       close_single_paragraph ();
  3907.       inhibit_paragraph_indentation = 1;
  3908.       filling_enabled = indented_fill = no_indent = 0;
  3909.       break;
  3910.  
  3911.     case flushright:
  3912.       close_single_paragraph ();
  3913.       filling_enabled = indented_fill = no_indent = 0;
  3914.       inhibit_paragraph_indentation = 1;
  3915.       force_flush_right++;
  3916.       break;
  3917.     }
  3918.  
  3919.   if (!no_discard)
  3920.     discard_until ("\n");
  3921. }
  3922.  
  3923. /* Try to end the insertion with the specified TYPE.
  3924.    TYPE, with a value of bad_type,  gets translated to match
  3925.    the value currently on top of the stack.
  3926.    Otherwise, if TYPE doesn't match the top of the insertion stack,
  3927.    give error. */
  3928. void
  3929. end_insertion (type)
  3930.      enum insertion_type type;
  3931. {
  3932.   enum insertion_type temp_type;
  3933.  
  3934.   if (!insertion_level)
  3935.     return;
  3936.  
  3937.   temp_type = current_insertion_type ();
  3938.  
  3939.   if (type == bad_type)
  3940.     type = temp_type;
  3941.  
  3942.   if (type != temp_type)
  3943.     {
  3944.       line_error
  3945.     ("`%cend' expected `%s', but saw `%s'", COMMAND_PREFIX,
  3946.      insertion_type_pname (temp_type), insertion_type_pname (type));
  3947.       return;
  3948.     }
  3949.  
  3950.   pop_insertion ();
  3951.  
  3952.   switch (type)
  3953.     {
  3954.       /* Insertions which have no effect on paragraph formatting. */
  3955.     case ifinfo:
  3956.     case ifset:
  3957.     case ifclear:
  3958.       break;
  3959.  
  3960.     case menu:
  3961.       in_menu--;        /* No longer hacking menus. */
  3962.       if (!no_headers)
  3963.     close_insertion_paragraph ();
  3964.       break;
  3965.  
  3966.     case enumerate:
  3967.       stop_enumerating ();
  3968.       close_insertion_paragraph ();
  3969.       current_indent -= default_indentation_increment;
  3970.       break;
  3971.  
  3972.     case flushleft:
  3973.     case group:
  3974.     case cartouche:
  3975.       close_insertion_paragraph ();
  3976.       break;
  3977.  
  3978.     case format:
  3979.     case display:
  3980.     case example:
  3981.     case smallexample:
  3982.     case lisp:
  3983.     case smalllisp:
  3984.     case quotation:
  3985.  
  3986.       /* @quotation is the only one of the above without a fixed width
  3987.      font. */
  3988.       if (type != quotation)
  3989.     in_fixed_width_font--;
  3990.  
  3991.       /* @format is the only fixed_width insertion without a change
  3992.      in indentation. */
  3993.       if (type != format)
  3994.     current_indent -= default_indentation_increment;
  3995.  
  3996.       /* The ending of one of these insertions always marks the
  3997.      start of a new paragraph. */
  3998.       close_insertion_paragraph ();
  3999.       break;
  4000.  
  4001.     case table:
  4002.     case ftable:
  4003.     case vtable:
  4004.     case itemize:
  4005.       current_indent -= default_indentation_increment;
  4006.       break;
  4007.  
  4008.     case flushright:
  4009.       force_flush_right--;
  4010.       close_insertion_paragraph ();
  4011.       break;
  4012.  
  4013.       /* Handle the @defun style insertions with a default clause. */
  4014.     default:
  4015.       current_indent -= default_indentation_increment;
  4016.       close_insertion_paragraph ();
  4017.       break;
  4018.     }
  4019. }
  4020.  
  4021. /* Insertions cannot cross certain boundaries, such as node beginnings.  In
  4022.    code that creates such boundaries, you should call discard_insertions ()
  4023.    before doing anything else.  It prints the errors for you, and cleans up
  4024.    the insertion stack. */
  4025. void
  4026. discard_insertions ()
  4027. {
  4028.   int real_line_number = line_number;
  4029.   while (insertion_stack)
  4030.     {
  4031.       if (insertion_stack->insertion == ifinfo ||
  4032.       insertion_stack->insertion == ifset ||
  4033.       insertion_stack->insertion == ifclear)
  4034.     break;
  4035.       else
  4036.     {
  4037.       char *offender;
  4038.       char *current_filename;
  4039.  
  4040.       current_filename = input_filename;
  4041.       offender = (char *)insertion_type_pname (insertion_stack->insertion);
  4042.       input_filename = insertion_stack->filename;
  4043.       line_number = insertion_stack->line_number;
  4044.       line_error ("This `%s' doesn't have a matching `%cend %s'", offender,
  4045.               COMMAND_PREFIX, offender);
  4046.       input_filename = current_filename;
  4047.       pop_insertion ();
  4048.     }
  4049.     }
  4050.   line_number = real_line_number;
  4051. }
  4052.  
  4053. /* The actual commands themselves. */
  4054.  
  4055. /* Commands which insert themselves. */
  4056. void
  4057. insert_self ()
  4058. {
  4059.   add_word (command);
  4060. }
  4061.  
  4062. /* Force a line break in the output. */
  4063. void
  4064. cm_asterisk ()
  4065. {
  4066.   close_single_paragraph ();
  4067. #if !defined (ASTERISK_NEW_PARAGRAPH)
  4068.   cm_noindent ();
  4069. #endif /* ASTERISK_NEW_PARAGRAPH */
  4070. }
  4071.  
  4072. /* Insert ellipsis. */
  4073. void
  4074. cm_dots (arg)
  4075.      int arg;
  4076. {
  4077.   if (arg == START)
  4078.     add_word ("...");
  4079. }
  4080.  
  4081. void
  4082. cm_bullet (arg)
  4083.      int arg;
  4084. {
  4085.   if (arg == START)
  4086.     {
  4087.        if (amiga_guide_39 && (!in_amiga_guide_button) && !no_headers)
  4088.          {
  4089.            output_column -= (amiga_guide_hidden_chars = strlen ("@{b}") + strlen ("@{ub}"));
  4090.            add_word ("@{b}*@{ub}");
  4091.          }
  4092.        else
  4093.           add_char ('*');
  4094.     }
  4095. }
  4096.  
  4097. void
  4098. cm_minus (arg)
  4099.      int arg;
  4100. {
  4101.   if (arg == START)
  4102.     add_char ('-');
  4103. }
  4104.  
  4105. /* Insert "TeX". */
  4106. void
  4107. cm_TeX (arg)
  4108.      int arg;
  4109. {
  4110.   if (arg == START)
  4111.     add_word ("TeX");
  4112. }
  4113.  
  4114. void
  4115. cm_copyright (arg)
  4116.      int arg;
  4117. {
  4118.   if (arg == START)
  4119.     add_word ("(C)");
  4120. }
  4121.  
  4122. #if defined (__osf__)
  4123. #define LOCALTIME_CAST(x) (time_t *)(x)
  4124. #else
  4125. #define LOCALTIME_CAST(x) (x)
  4126. #endif
  4127.  
  4128. void
  4129. cm_today (arg)
  4130.      int arg;
  4131. {
  4132.   static char * months [12] =
  4133.     { "January", "February", "March", "April", "May", "June", "July",
  4134.     "August", "September", "October", "November", "December" };
  4135.   if (arg == START)
  4136.     {
  4137.       long timer = time (0);
  4138.       struct tm *ts = localtime (LOCALTIME_CAST (&timer));
  4139.       add_word_args
  4140.     ("%d %s %d",
  4141.      (ts -> tm_mday),
  4142.      (months [ts -> tm_mon]),
  4143.      ((ts -> tm_year) + 1900));
  4144.     }
  4145. }
  4146.  
  4147. void
  4148. cm_code (arg)
  4149.      int arg;
  4150. {
  4151.   extern int printing_index;
  4152.  
  4153.   /* If we are printing an index, or we are in an AmigaGuide button,
  4154.      simply return */
  4155.   if (printing_index)
  4156.     return;
  4157.  
  4158.   if (arg == START)
  4159.     {
  4160.       if (amiga_guide && (!amiga_guide_34))
  4161.         {
  4162.           in_fixed_width_font++;  /* Added  -kdp- */
  4163.           amiga_set_attributes (AMIGA_BOLD_ON);
  4164.         }
  4165.       else
  4166.         {
  4167.           in_fixed_width_font++;
  4168.           add_char ('`');
  4169.     }
  4170.     }
  4171.   else
  4172.     {
  4173.       if (amiga_guide && (!amiga_guide_34))
  4174.         {
  4175.           amiga_set_attributes (AMIGA_BOLD_OFF);
  4176.           in_fixed_width_font--;  /* Added  -kdp- */
  4177.         }
  4178.       else
  4179.         {
  4180.           add_word ("'");
  4181.           in_fixed_width_font--;
  4182.         }
  4183.     }
  4184. }
  4185.  
  4186. void
  4187. cm_samp (arg)
  4188.      int arg;
  4189. {
  4190.   cm_code (arg);
  4191. }
  4192.  
  4193. void
  4194. cm_file (arg)
  4195.      int arg;
  4196. {
  4197.   cm_code (arg);
  4198. }
  4199.  
  4200. void
  4201. cm_kbd (arg)
  4202.      int arg;
  4203. {
  4204.   cm_code (arg);
  4205. }
  4206.  
  4207. void
  4208. cm_key (arg)
  4209.      int arg;
  4210. {
  4211. }
  4212.  
  4213. /* Convert the character at position into CTL. */
  4214. void
  4215. cm_ctrl (arg, start, end)
  4216.      int arg, start, end;
  4217. {
  4218.   /* Should we allow multiple character arguments?  I think yes. */
  4219.   if (arg == END)
  4220.     {
  4221.       register int i, character;
  4222. #if defined (NO_MULTIPLE_CTRL)
  4223.       if ((end - start) != 1)
  4224.     line_error ("%c%s expects a single character as an argument",
  4225.             COMMAND_PREFIX, command);
  4226.       else
  4227. #endif
  4228.     for (i = start; i < end; i++)
  4229.       {
  4230.         character = output_paragraph[i];
  4231.  
  4232.         if (isletter (character))
  4233.           output_paragraph[i] = CTL (coerce_to_upper (character));
  4234.       }
  4235.     }
  4236. }
  4237.  
  4238. /* Small Caps in makeinfo just does all caps. */
  4239. void
  4240. cm_sc (arg, start_pos, end_pos)
  4241.      int arg, start_pos, end_pos;
  4242. {
  4243.   extern int printing_index;
  4244.   
  4245.   if (!amiga_guide || amiga_guide_34)
  4246.     {
  4247.       if (arg == END)
  4248.         {
  4249.           while (start_pos < end_pos)
  4250.             {
  4251.            output_paragraph[start_pos] =
  4252.              coerce_to_upper (output_paragraph[start_pos]);
  4253.            start_pos++;
  4254.         }
  4255.         }
  4256.      }
  4257.   else
  4258.     {
  4259.       /* If we are printing an index, or we are in an AmigaGuide button,
  4260.          simply return */
  4261.       if (printing_index)
  4262.         return;
  4263.       if (arg == START)
  4264.         amiga_set_attributes (AMIGA_VAR_ON);
  4265.       if (arg == END)
  4266.         amiga_set_attributes (AMIGA_VAR_OFF);
  4267.     }
  4268. }
  4269.  
  4270. /* @var in makeinfo just uppercases the text. */
  4271. void
  4272. cm_var (arg, start_pos, end_pos)
  4273.      int arg, start_pos, end_pos;
  4274. {
  4275.   if (arg == END)
  4276.     {
  4277.       while (start_pos < end_pos)
  4278.     {
  4279.       output_paragraph[start_pos] =
  4280.         coerce_to_upper (output_paragraph[start_pos]);
  4281.       start_pos++;
  4282.     }
  4283.     }
  4284. }
  4285.  
  4286. void
  4287. cm_dfn (arg, position)
  4288.      int arg, position;
  4289. {
  4290.   extern int printing_index;
  4291.   
  4292.   if (!amiga_guide || amiga_guide_34)
  4293.     add_char ('"');
  4294.   else
  4295.     {
  4296.       /* If we are printing an index, or we are in an AmigaGuide button,
  4297.          simply return */
  4298.       if (printing_index)
  4299.         return;
  4300.       if (arg == START)
  4301.         amiga_set_attributes (AMIGA_VAR_ON);
  4302.       if (arg == END)
  4303.         amiga_set_attributes (AMIGA_VAR_OFF);
  4304.     }
  4305. }
  4306.  
  4307. void
  4308. cm_emph (arg)
  4309.      int arg;
  4310. {
  4311.   if (!amiga_guide || amiga_guide_34)
  4312.     add_char ('*');
  4313.   else
  4314.     {
  4315.       if (arg == START)
  4316.         amiga_set_attributes (AMIGA_EMPH_ON);
  4317.       if (arg == END)
  4318.         amiga_set_attributes (AMIGA_EMPH_OFF);
  4319.     }
  4320.  
  4321. }
  4322.  
  4323. void
  4324. cm_strong (arg, position)
  4325.      int arg, position;
  4326. {
  4327.   cm_emph (arg);
  4328. }
  4329.  
  4330. void
  4331. cm_cite (arg, position)
  4332.      int arg, position;
  4333. {
  4334.   if (arg == START)
  4335.     add_word ("`");
  4336.   else
  4337.     add_word ("'");
  4338. }
  4339.  
  4340. /* Current text is italicized. */
  4341. void
  4342. cm_italic (arg, start, end)
  4343.      int arg, start, end;
  4344. {
  4345.   extern int printing_index;
  4346.  
  4347.   if (amiga_guide && (!amiga_guide_34))
  4348.     {
  4349.       if (arg == START)
  4350.         amiga_set_attributes (AMIGA_ITALIC_ON);
  4351.       if (arg == END)
  4352.         amiga_set_attributes (AMIGA_ITALIC_OFF);
  4353.     }
  4354. }
  4355.  
  4356. /* Current text is highlighted. */
  4357. void
  4358. cm_bold (arg, start, end)
  4359.      int arg, start, end;
  4360. {
  4361.   cm_italic (arg);
  4362. }
  4363.  
  4364. /* Current text is in roman font. */
  4365. void
  4366. cm_roman (arg, start, end)
  4367.      int arg, start, end;
  4368. {
  4369. }
  4370.  
  4371. /* Current text is in roman font. */
  4372. void
  4373. cm_titlefont (arg, start, end)
  4374.      int arg, start, end;
  4375. {
  4376. }
  4377.  
  4378. /* Italicize titles. */
  4379. void
  4380. cm_title (arg, start, end)
  4381.      int arg, start, end;
  4382. {
  4383.   cm_italic (arg);
  4384. }
  4385.  
  4386. /* @refill is a NOP. */
  4387. void
  4388. cm_refill ()
  4389. {
  4390. }
  4391.  
  4392. /* Prevent the argument from being split across two lines. */
  4393. void
  4394. cm_w (arg, start, end)
  4395.      int arg, start, end;
  4396. {
  4397.   if (arg == START)
  4398.     non_splitting_words++;
  4399.   else
  4400.     non_splitting_words--;
  4401. }
  4402.  
  4403.  
  4404. /* Explain that this command is obsolete, thus the user shouldn't
  4405.    do anything with it. */
  4406. void
  4407. cm_obsolete (arg, start, end)
  4408.      int arg, start, end;
  4409. {
  4410.   if (arg == START)
  4411.     warning ("The command `%c%s' is obsolete", COMMAND_PREFIX, command);
  4412. }
  4413.  
  4414. /* Insert the text following input_text_offset up to the end of the line
  4415.    in a new, separate paragraph.  Directly underneath it, insert a
  4416.    line of WITH_CHAR, the same length of the inserted text. */
  4417. void
  4418. insert_and_underscore (with_char)
  4419.      int with_char;
  4420. {
  4421.   register int i, len;
  4422.   int old_no_indent, starting_pos, ending_pos;
  4423.   char *temp;
  4424.  
  4425.   close_paragraph ();
  4426.   filling_enabled =  indented_fill = 0;
  4427.   old_no_indent = no_indent;
  4428.   no_indent = 1;
  4429.  
  4430. #if defined (HAVE_MACROS)
  4431.   if (macro_expansion_output_stream)
  4432.     append_to_expansion_output (input_text_offset + 1);
  4433. #endif /* HAVE_MACROS */
  4434.  
  4435.   get_rest_of_line (&temp);
  4436.  
  4437.   starting_pos = output_position + output_paragraph_offset;
  4438. #if defined (HAVE_MACROS)
  4439.   if (macro_expansion_output_stream)
  4440.     {
  4441.       char *temp1;
  4442.  
  4443.       temp1 = (char *)xmalloc (2 + strlen (temp));
  4444.       sprintf (temp1, "%s\n", temp);
  4445.       remember_itext (input_text, input_text_offset);
  4446.       me_execute_string (temp1);
  4447.       free (temp1);
  4448.     }
  4449.   else
  4450. #endif /* HAVE_MACROS */
  4451.   {
  4452.   in_amiga_guide_title = 1;
  4453.   execute_string ("%s\n", temp);
  4454.   in_amiga_guide_title = 0;
  4455.   }
  4456.  
  4457.   ending_pos = output_position + output_paragraph_offset;
  4458.   free (temp);
  4459.  
  4460.   len = (ending_pos - starting_pos) - 1;
  4461.   for (i = 0; i < (len - amiga_hide_title_chars) ; i++)
  4462.     add_char (with_char);
  4463.   insert ('\n');
  4464.   close_paragraph ();
  4465.   amiga_hide_title_chars = 0;
  4466.   filling_enabled = 1;
  4467.   no_indent = old_no_indent;
  4468. }
  4469.  
  4470. /* Here is a structure which associates sectioning commands with
  4471.    an integer, hopefully to reflect the `depth' of the current
  4472.    section. */
  4473. struct {
  4474.   char *name;
  4475.   int level;
  4476. } section_alist[] = {
  4477.   { "unnumberedsubsubsec", 5 },
  4478.   { "unnumberedsubsec", 4 },
  4479.   { "unnumberedsec", 3 },
  4480.   { "unnumbered", 2 },
  4481.   { "appendixsubsubsec", 5 },
  4482.   { "appendixsubsec", 4 },
  4483.   { "appendixsec", 3 },
  4484.   { "appendixsection", 3 },
  4485.   { "appendix", 2 },
  4486.   { "subsubsec", 5 },
  4487.   { "subsubsection", 5 },
  4488.   { "subsection", 4 },
  4489.   { "section", 3 },
  4490.   { "chapter", 2 },
  4491.   { "top", 1 },
  4492.  
  4493.   { (char *)NULL, 0 }
  4494. };
  4495.  
  4496. /* Amount to offset the name of sectioning commands to levels by. */
  4497. int section_alist_offset = 0;
  4498.  
  4499. /* Shift the meaning of @section to @chapter. */
  4500. void
  4501. cm_raisesections ()
  4502. {
  4503.   discard_until ("\n");
  4504.   section_alist_offset--;
  4505. }
  4506.  
  4507. /* Shift the meaning of @chapter to @section. */
  4508. void
  4509. cm_lowersections ()
  4510. {
  4511.   discard_until ("\n");
  4512.   section_alist_offset++;
  4513. }
  4514.  
  4515. /* Return an integer which identifies the type section present in TEXT. */
  4516. int
  4517. what_section (text)
  4518.      char *text;
  4519. {
  4520.   register int i, j;
  4521.   char *t;
  4522.  
  4523.  find_section_command:
  4524.   for (j = 0; text[j] && cr_or_whitespace (text[j]); j++);
  4525.   if (text[j] != COMMAND_PREFIX)
  4526.     return (-1);
  4527.  
  4528.   text = text + j + 1;
  4529.  
  4530.   /* We skip @c, @comment, and @?index commands. */
  4531.   if ((strncmp (text, "comment", strlen ("comment")) == 0) ||
  4532.       (text[0] == 'c' && cr_or_whitespace (text[1])) ||
  4533.       (strncmp (text  + 1, "index", strlen("index")) == 0))
  4534. /* Bug Fix : changed 
  4535.                strcmp (text +1, "index") 
  4536.              to
  4537.                strncmp (text +1, "index", strlen("index")) 
  4538.    -kdp  */
  4539.     {
  4540.       while (*text++ != '\n');
  4541.       goto find_section_command;
  4542.     }
  4543.  
  4544.   /* Handle italicized sectioning commands. */
  4545.   if (*text == 'i')
  4546.     text++;
  4547.  
  4548.   for (j = 0; text[j] && !cr_or_whitespace (text[j]); j++);
  4549.  
  4550.   for (i = 0; t = section_alist[i].name; i++)
  4551.     {
  4552.       if (j == strlen (t) && strncmp (t, text, j) == 0)
  4553.     {
  4554.       int return_val;
  4555.  
  4556.       return_val = (section_alist[i].level + section_alist_offset);
  4557.  
  4558.       if (return_val < 0)
  4559.         return_val = 0;
  4560.       else if (return_val > 5)
  4561.         return_val = 5;
  4562.       return (return_val);
  4563.     }
  4564.     }
  4565.   return (-1);
  4566. }
  4567.  
  4568. /* Set the level of @top to LEVEL.  Return the old level of @top. */
  4569. int
  4570. set_top_section_level (level)
  4571.      int level;
  4572. {
  4573.   register int i, result = -1;
  4574.  
  4575.   for (i = 0; section_alist[i].name; i++)
  4576.     if (strcmp (section_alist[i].name, "top") == 0)
  4577.       {
  4578.     result = section_alist[i].level;
  4579.     section_alist[i].level = level;
  4580.     break;
  4581.       }
  4582.   return (result);
  4583. }
  4584.  
  4585. /* Treat this just like @unnumbered.  The only difference is
  4586.    in node defaulting. */
  4587. void
  4588. cm_top ()
  4589. {
  4590.   /* It is an error to have more than one @top. */
  4591.   if (top_node_seen)
  4592.     {
  4593.       TAG_ENTRY *tag = tag_table;
  4594.  
  4595.       line_error ("There already is a node having %ctop as a section",
  4596.           COMMAND_PREFIX);
  4597.  
  4598.       while (tag != (TAG_ENTRY *)NULL)
  4599.     {
  4600.       if ((tag->flags & IS_TOP))
  4601.         {
  4602.           int old_line_number = line_number;
  4603.           char *old_input_filename = input_filename;
  4604.  
  4605.           line_number = tag->line_no;
  4606.           input_filename = tag->filename;
  4607.           line_error ("Here is the %ctop node", COMMAND_PREFIX);
  4608.           input_filename = old_input_filename;
  4609.           line_number = old_line_number;
  4610.           return;
  4611.         }
  4612.       tag = tag->next_ent;
  4613.     }
  4614.     }
  4615.   else
  4616.     {
  4617.       top_node_seen = 1;
  4618.  
  4619.       /* It is an error to use @top before you have used @node. */
  4620.       if (!tag_table)
  4621.     {
  4622.       char *top_name;
  4623.  
  4624.       get_rest_of_line (&top_name);
  4625.       free (top_name);
  4626.       line_error ("%ctop used before %cnode, defaulting to %s",
  4627.               COMMAND_PREFIX, COMMAND_PREFIX, top_name);
  4628.       execute_string ("@node Top, , (dir), (dir)\n@top %s\n", top_name);
  4629.       return;
  4630.     }
  4631.  
  4632.       cm_unnumbered ();
  4633.  
  4634.       /* The most recently defined node is the top node. */
  4635.       tag_table->flags |= IS_TOP;
  4636.  
  4637.       /* Now set the logical hierarchical level of the Top node. */
  4638.       {
  4639.     int orig_offset = input_text_offset;
  4640.  
  4641.     input_text_offset = search_forward (node_search_string, orig_offset);
  4642.  
  4643.     if (input_text_offset > 0)
  4644.       {
  4645.         int this_section;
  4646.  
  4647.         /* We have encountered a non-top node, so mark that one exists. */
  4648.         non_top_node_seen = 1;
  4649.  
  4650.         /* Move to the end of this line, and find out what the
  4651.            sectioning command is here. */
  4652.         while (input_text[input_text_offset] != '\n')
  4653.           input_text_offset++;
  4654.  
  4655.         if (input_text_offset < size_of_input_text)
  4656.           input_text_offset++;
  4657.  
  4658.         this_section = what_section (input_text + input_text_offset);
  4659.  
  4660.         /* If we found a sectioning command, then give the top section
  4661.            a level of this section - 1. */
  4662.         if (this_section != -1)
  4663.           set_top_section_level (this_section - 1);
  4664.       }
  4665.     input_text_offset = orig_offset;
  4666.       }
  4667.     }
  4668. }
  4669.  
  4670. /* Organized by level commands.  That is, "*" == chapter, "=" == section. */
  4671. char *scoring_characters = "*=-.";
  4672.  
  4673. void
  4674. sectioning_underscore (command)
  4675.      char *command;
  4676. {
  4677.   char character;
  4678.   char *temp;
  4679.   int level;
  4680.  
  4681.   temp = (char *)xmalloc (2 + strlen (command));
  4682.   temp[0] = COMMAND_PREFIX;
  4683.   strcpy (&temp[1], command);
  4684.   level = what_section (temp);
  4685.   free (temp);
  4686.   level -= 2;
  4687.  
  4688.   if (level < 0)
  4689.     level = 0;
  4690.  
  4691.   character = scoring_characters[level];
  4692.  
  4693.   insert_and_underscore (character);
  4694. }
  4695.  
  4696. /* The remainder of the text on this line is a chapter heading. */
  4697. void
  4698. cm_chapter ()
  4699. {
  4700.   sectioning_underscore ("chapter");
  4701. }
  4702.  
  4703. /* The remainder of the text on this line is a section heading. */
  4704. void
  4705. cm_section ()
  4706. {
  4707.   sectioning_underscore ("section");
  4708. }
  4709.  
  4710. /* The remainder of the text on this line is a subsection heading. */
  4711. void
  4712. cm_subsection ()
  4713. {
  4714.   sectioning_underscore ("subsection");
  4715. }
  4716.  
  4717. /* The remainder of the text on this line is a subsubsection heading. */
  4718. void
  4719. cm_subsubsection ()
  4720. {
  4721.   sectioning_underscore ("subsubsection");
  4722. }
  4723.  
  4724. /* The remainder of the text on this line is an unnumbered heading. */
  4725. void
  4726. cm_unnumbered ()
  4727. {
  4728.   cm_chapter ();
  4729. }
  4730.  
  4731. /* The remainder of the text on this line is an unnumbered section heading. */
  4732. void
  4733. cm_unnumberedsec ()
  4734. {
  4735.   cm_section ();
  4736. }
  4737.  
  4738. /* The remainder of the text on this line is an unnumbered
  4739.    subsection heading. */
  4740. void
  4741. cm_unnumberedsubsec ()
  4742. {
  4743.   cm_subsection ();
  4744. }
  4745.  
  4746. /* The remainder of the text on this line is an unnumbered
  4747.    subsubsection heading. */
  4748. void
  4749. cm_unnumberedsubsubsec ()
  4750. {
  4751.   cm_subsubsection ();
  4752. }
  4753.  
  4754. /* The remainder of the text on this line is an appendix heading. */
  4755. void
  4756. cm_appendix ()
  4757. {
  4758.   cm_chapter ();
  4759. }
  4760.  
  4761. /* The remainder of the text on this line is an appendix section heading. */
  4762. void
  4763. cm_appendixsec ()
  4764. {
  4765.   cm_section ();
  4766. }
  4767.  
  4768. /* The remainder of the text on this line is an appendix subsection heading. */
  4769. void
  4770. cm_appendixsubsec ()
  4771. {
  4772.   cm_subsection ();
  4773. }
  4774.  
  4775. /* The remainder of the text on this line is an appendix
  4776.    subsubsection heading. */
  4777. void
  4778. cm_appendixsubsubsec ()
  4779. {
  4780.   cm_subsubsection ();
  4781. }
  4782.  
  4783. /* Compatibility functions substitute for chapter, section, etc. */
  4784. void
  4785. cm_majorheading ()
  4786. {
  4787.   cm_chapheading ();
  4788. }
  4789.  
  4790. void
  4791. cm_chapheading ()
  4792. {
  4793.   cm_chapter ();
  4794. }
  4795.  
  4796. void
  4797. cm_heading ()
  4798. {
  4799.   cm_section ();
  4800. }
  4801.  
  4802. void
  4803. cm_subheading ()
  4804. {
  4805.   cm_subsection ();
  4806. }
  4807.  
  4808. void
  4809. cm_subsubheading ()
  4810. {
  4811.   cm_subsubsection ();
  4812. }
  4813.  
  4814.  
  4815. /* **************************************************************** */
  4816. /*                                    */
  4817. /*           Adding nodes, and making tags            */
  4818. /*                                    */
  4819. /* **************************************************************** */
  4820.  
  4821. /* Start a new tag table. */
  4822. void
  4823. init_tag_table ()
  4824. {
  4825.   while (tag_table != (TAG_ENTRY *) NULL)
  4826.     {
  4827.       TAG_ENTRY *temp = tag_table;
  4828.       free (temp->node);
  4829.       free (temp->prev);
  4830.       free (temp->next);
  4831.       free (temp->up);
  4832.       tag_table = tag_table->next_ent;
  4833.       free (temp);
  4834.     }
  4835. }
  4836.  
  4837. void
  4838. write_tag_table ()
  4839. {
  4840.   write_tag_table_internal (0);    /* Not indirect. */
  4841. }
  4842.  
  4843. void
  4844. write_tag_table_indirect ()
  4845. {
  4846.   write_tag_table_internal (1);
  4847. }
  4848.  
  4849. /* Write out the contents of the existing tag table.
  4850.    INDIRECT_P says how to format the output. */
  4851. void
  4852. write_tag_table_internal (indirect_p)
  4853.      int indirect_p;
  4854. {
  4855.   TAG_ENTRY *node = tag_table;
  4856.   int old_indent = no_indent;
  4857.  
  4858.   no_indent = 1;
  4859.   filling_enabled = 0;
  4860.   must_start_paragraph = 0;
  4861.   close_paragraph ();
  4862.  
  4863.   if (!indirect_p)
  4864.     {
  4865.       no_indent = 1;
  4866.       insert ('\n');
  4867.     }
  4868.  
  4869.   add_word_args ("\037\nTag Table:\n%s", indirect_p ? "(Indirect)\n" : "");
  4870.  
  4871.   while (node != (TAG_ENTRY *) NULL)
  4872.     {
  4873.       execute_string ("Node: %s", node->node);
  4874.       add_word_args ("\177%d\n", node->position);
  4875.       node = node->next_ent;
  4876.     }
  4877.  
  4878.   add_word ("\037\nEnd Tag Table\n");
  4879.   flush_output ();
  4880.   no_indent = old_indent;
  4881. }
  4882.  
  4883. char *
  4884. get_node_token ()
  4885. {
  4886.   char *string;
  4887.  
  4888.   get_until_in_line (",", &string);
  4889.  
  4890.   if (curchar () == ',')
  4891.     input_text_offset++;
  4892.  
  4893.   canon_white (string);
  4894.  
  4895.   /* Force all versions of "top" to be "Top". */
  4896.   normalize_node_name (string);
  4897.  
  4898.   return (string);
  4899. }
  4900.  
  4901. /* Convert "top" and friends into "Top". */
  4902. void
  4903. normalize_node_name (string)
  4904.      char *string;
  4905. {
  4906.   register int i, l = strlen (string);
  4907.  
  4908.   for (i = 0; i < l; i++)
  4909.     {
  4910.       if (string[i] == '@' && string[i + 1] == '@')
  4911.         {
  4912.           strncpy (string + i, string + i + 1, l - i);
  4913.           l--;
  4914.         }
  4915.       else if (amiga_guide)
  4916.         {
  4917.           if (string[i] == '/')
  4918.             string[i] = '-';
  4919.           else if (string[i] == '\"')
  4920.             string[i] = '`';
  4921.           else if (amiga_guide && !no_headers)
  4922.             {
  4923.               char *set_p (char *);
  4924.   
  4925.               if (set_p ("amiga_convert_nodes"))
  4926.                 if (string[i] == ' ')
  4927.                   string[i] = '_';
  4928.             }
  4929.         }
  4930.     }
  4931.  
  4932.   if (strcasecmp (string, "Top") == 0)
  4933.     strcpy (string, "Top");
  4934. }
  4935.  
  4936. /* Look up NAME in the tag table, and return the associated
  4937.    tag_entry.  If the node is not in the table return NULL. */
  4938. TAG_ENTRY *
  4939. find_node (name)
  4940.      char *name;
  4941. {
  4942.   TAG_ENTRY *tag = tag_table;
  4943.  
  4944.   while (tag != (TAG_ENTRY *) NULL)
  4945.     {
  4946.       if (strcmp (tag->node, name) == 0)
  4947.     return (tag);
  4948.       tag = tag->next_ent;
  4949.     }
  4950.   return ((TAG_ENTRY *) NULL);
  4951. }
  4952.  
  4953. /* Remember NODE and associates. */
  4954. void
  4955. remember_node (node, prev, next, up, position, line_no, no_warn)
  4956.      char *node, *prev, *next, *up;
  4957.      int position, line_no, no_warn;
  4958. {
  4959.   /* Check for existence of this tag already. */
  4960.   if (validating)
  4961.     {
  4962.       register TAG_ENTRY *tag = find_node (node);
  4963.       if (tag)
  4964.     {
  4965.       line_error ("Node `%s' multiply defined (%d is first definition)",
  4966.               node, tag->line_no);
  4967.       return;
  4968.     }
  4969.     }
  4970.  
  4971.   /* First, make this the current node. */
  4972.   current_node = node;
  4973.  
  4974.   /* Now add it to the list. */
  4975.   {
  4976.     TAG_ENTRY *new = (TAG_ENTRY *) xmalloc (sizeof (TAG_ENTRY));
  4977.     new->node = node;
  4978.     new->prev = prev;
  4979.     new->next = next;
  4980.     new->up = up;
  4981.     new->position = position;
  4982.     new->line_no = line_no;
  4983.     new->filename = node_filename;
  4984.     new->touched = 0;        /* not yet referenced. */
  4985.     new->flags = 0;
  4986.     if (no_warn)
  4987.       new->flags |= NO_WARN;
  4988.     new->next_ent = tag_table;
  4989.     tag_table = new;
  4990.   }
  4991. }
  4992.  
  4993. /* The order is: nodename, nextnode, prevnode, upnode.
  4994.    If all of the NEXT, PREV, and UP fields are empty, they are defaulted.
  4995.    You must follow a node command which has those fields defaulted
  4996.    with a sectioning command (e.g. @chapter) giving the "level" of that node.
  4997.    It is an error not to do so.
  4998.    The defaults come from the menu in this node's parent. */
  4999. void
  5000. cm_node ()
  5001. {
  5002.   char *node, *prev, *next, *up;
  5003.   int new_node_pos, defaulting, this_section, no_warn = 0;
  5004.   static amiga_guide_first_node = 1;
  5005.   extern int already_outputting_pending_notes;
  5006.  
  5007.   if (strcmp (command, "nwnode") == 0)
  5008.     no_warn = 1;
  5009.  
  5010.   /* Get rid of unmatched brace arguments from previous commands. */
  5011.   discard_braces ();
  5012.  
  5013.   /* There also might be insertions left lying around that haven't been
  5014.      ended yet.  Do that also. */
  5015.   discard_insertions ();
  5016.  
  5017.   if (!already_outputting_pending_notes)
  5018.     {
  5019.       close_paragraph ();
  5020.       output_pending_notes ();
  5021.       free_pending_notes ();
  5022.     }
  5023.  
  5024.   filling_enabled = indented_fill = 0;
  5025.   new_node_pos = output_position;
  5026.   current_footnote_number = 1;
  5027.  
  5028. #if defined (HAVE_MACROS)
  5029.   if (macro_expansion_output_stream)
  5030.     append_to_expansion_output (input_text_offset + 1);
  5031. #endif /* HAVE_MACROS */
  5032.  
  5033.   node = get_node_token ();
  5034.   next = get_node_token ();
  5035.   prev = get_node_token ();
  5036.   up = get_node_token ();
  5037.  
  5038. #if defined (HAVE_MACROS)
  5039.   if (macro_expansion_output_stream)
  5040.     remember_itext (input_text, input_text_offset);
  5041. #endif /* HAVE_MACROS */
  5042.  
  5043.   no_indent = 1;
  5044.   if (!no_headers)
  5045.     {
  5046.       if (amiga_guide)
  5047.          /* if amiga_guide_first_node is set to 1, then we are
  5048.             considering the first node of the database. */
  5049.         {
  5050.           if (amiga_guide_first_node)
  5051.             {
  5052.               add_word_args ("\n@Node Main \"%s\"", output_filename);
  5053.               amiga_guide_first_node = 0;
  5054.             }
  5055.           else
  5056.             {
  5057.               add_word_args("\n@EndNode\n\n");
  5058. #if defined (HAVE_MACROS)
  5059.               if (macro_expansion_output_stream)
  5060.                 {
  5061.                    add_word_args ("@Node \"");
  5062.                    me_execute_string (node);
  5063.                    add_word_args ("\" \"%s/", pretty_output_filename);
  5064.                    me_execute_string (node);
  5065.                    add_word_args ("\"");
  5066.                  }
  5067.               else
  5068. #endif /* HAVE_MACROS */
  5069.                  {
  5070.                    add_word_args ("@Node ");
  5071.                    execute_string ("\"%s\" \"%s/%s\"",node,pretty_output_filename,node);
  5072.                  }
  5073.             }
  5074.         }
  5075.       else
  5076.         {
  5077.            add_word_args ("\037\nFile: %s,  Node: ", pretty_output_filename);
  5078.  
  5079. #if defined (HAVE_MACROS)
  5080.            if (macro_expansion_output_stream)
  5081.              me_execute_string (node);
  5082.            else
  5083. #endif /* HAVE_MACROS */
  5084.              execute_string ("%s", node);
  5085.            filling_enabled = indented_fill = 0;
  5086.         }
  5087.     }
  5088.  
  5089.   /* Check for defaulting of this node's next, prev, and up fields. */
  5090.   defaulting = ((strlen (next) == 0) &&
  5091.         (strlen (prev) == 0) &&
  5092.         (strlen (up) == 0));
  5093.  
  5094.   this_section = what_section (input_text + input_text_offset);
  5095.  
  5096.   /* If we are defaulting, then look at the immediately following
  5097.      sectioning command (error if none) to determine the node's
  5098.      level.  Find the node that contains the menu mentioning this node
  5099.      that is one level up (error if not found).  That node is the "Up"
  5100.      of this node.  Default the "Next" and "Prev" from the menu. */
  5101.   if (defaulting)
  5102.     {
  5103.       NODE_REF *last_ref = (NODE_REF *)NULL;
  5104.       NODE_REF *ref = node_references;
  5105.  
  5106.       if ((this_section < 0) && (strcmp (node, "Top") != 0))
  5107.     {
  5108.       char *polite_section_name = "top";
  5109.       int i;
  5110.  
  5111.       for (i = 0; section_alist[i].name; i++)
  5112.         if (section_alist[i].level == current_section + 1)
  5113.           {
  5114.         polite_section_name = section_alist[i].name;
  5115.         break;
  5116.           }
  5117.  
  5118.       line_error
  5119.         ("Node `%s' requires a sectioning command (e.g. %c%s)",
  5120.          node, COMMAND_PREFIX, polite_section_name);
  5121.     }
  5122.       else
  5123.     {
  5124.       if (strcmp (node, "Top") == 0)
  5125.         {
  5126.           /* Default the NEXT pointer to be the first menu item in
  5127.          this node, if there is a menu in this node.  We have to
  5128.          try very hard to find the menu, as it may be obscured
  5129.          by execution_strings which are on the filestack.  For
  5130.          every member of the filestack which has a FILENAME
  5131.          member which is identical to the current INPUT_FILENAME,
  5132.          search forward from that offset. */
  5133.           int saved_input_text_offset = input_text_offset;
  5134.           int saved_size_of_input_text = size_of_input_text;
  5135.           char *saved_input_text = input_text;
  5136.           FSTACK *next_file = filestack;
  5137.  
  5138.           int orig_offset, orig_size;
  5139.           char *glean_node_from_menu ();
  5140.  
  5141.           /* No matter what, make this file point back at `(dir)'. */
  5142.           free (up);   up = strdup ("(dir)");
  5143.  
  5144.           while (1)
  5145.         {
  5146.           orig_offset = input_text_offset;
  5147.           orig_size =
  5148.             search_forward (node_search_string, orig_offset);
  5149.  
  5150.           if (orig_size < 0)
  5151.             orig_size = size_of_input_text;
  5152.  
  5153.           input_text_offset =
  5154.             search_forward (menu_search_string, orig_offset);
  5155.  
  5156.           if (input_text_offset > -1)
  5157.             {
  5158.               char *nodename_from_menu = (char *)NULL;
  5159.  
  5160.               input_text_offset =
  5161.             search_forward ("\n* ", input_text_offset);
  5162.  
  5163.               if (input_text_offset != -1)
  5164.             nodename_from_menu = glean_node_from_menu (0);
  5165.  
  5166.               if (nodename_from_menu)
  5167.             {
  5168.               free (next); next = nodename_from_menu;
  5169.               break;
  5170.             }
  5171.             }
  5172.  
  5173.           /* We got here, so it hasn't been found yet.  Try
  5174.              the next file on the filestack if there is one. */
  5175.           if (next_file &&
  5176.               (strcmp (next_file->filename, input_filename) == 0))
  5177.             {
  5178.               input_text = next_file->text;
  5179.               input_text_offset = next_file->offset;
  5180.               size_of_input_text = next_file->size;
  5181.               next_file = next_file->next;
  5182.             }
  5183.           else
  5184.             {
  5185.               /* No more input files to check. */
  5186.               break;
  5187.             }
  5188.         }
  5189.  
  5190.           input_text = saved_input_text;
  5191.           input_text_offset = saved_input_text_offset;
  5192.           size_of_input_text = saved_size_of_input_text;
  5193.         }
  5194.     }
  5195.  
  5196.       /* Fix the level of the menu references in the Top node, iff it
  5197.      was declared with @top, and no subsequent reference was found. */
  5198.       if (top_node_seen && !non_top_node_seen)
  5199.     {
  5200.       /* Then this is the first non-@top node seen. */
  5201.       int level;
  5202.  
  5203.       level = set_top_section_level (this_section - 1);
  5204.       non_top_node_seen = 1;
  5205.  
  5206.       while (ref)
  5207.         {
  5208.           if (ref->section == level)
  5209.         ref->section = this_section - 1;
  5210.           ref = ref->next;
  5211.         }
  5212.  
  5213.       ref = node_references;
  5214.     }
  5215.  
  5216.       while (ref)
  5217.     {
  5218.       if (ref->section == (this_section - 1) &&
  5219.           ref->type == menu_reference &&
  5220.           strcmp (ref->node, node) == 0)
  5221.         {
  5222.           char *containing_node = ref->containing_node;
  5223.  
  5224.           free (up);
  5225.           up = strdup (containing_node);
  5226.  
  5227.           if (last_ref &&
  5228.           last_ref->type == menu_reference &&
  5229.           (strcmp (last_ref->containing_node,
  5230.                containing_node) == 0))
  5231.         {
  5232.           free (next);
  5233.           next = strdup (last_ref->node);
  5234.         }
  5235.  
  5236.           while ((ref->section == this_section - 1) &&
  5237.              (ref->next) &&
  5238.              (ref->next->type != menu_reference))
  5239.         ref = ref->next;
  5240.  
  5241.           if (ref->next && ref->type == menu_reference &&
  5242.           (strcmp (ref->next->containing_node,
  5243.                containing_node) == 0))
  5244.         {
  5245.           free (prev);
  5246.           prev = strdup (ref->next->node);
  5247.         }
  5248.           else if (!ref->next &&
  5249.                strcasecmp (ref->containing_node, "Top") == 0)
  5250.         {
  5251.           free (prev);
  5252.           prev = strdup (ref->containing_node);
  5253.         }
  5254.           break;
  5255.         }
  5256.       last_ref = ref;
  5257.       ref = ref->next;
  5258.     }
  5259.     }
  5260.  
  5261. #if defined (HAVE_MACROS)
  5262.   /* Insert the correct args if we are expanding macros, and the node's
  5263.      pointers weren't defaulted. */
  5264.   if (macro_expansion_output_stream && !defaulting)
  5265.     {
  5266.       char *temp;
  5267.       int op_orig = output_paragraph_offset;
  5268.  
  5269.       temp = (char *)xmalloc (3 + strlen (next));
  5270.       sprintf (temp, ", %s", next);
  5271.       me_execute_string (temp);
  5272.       free (temp);
  5273.  
  5274.       temp = (char *)xmalloc (3 + strlen (prev));
  5275.       sprintf (temp, ", %s", prev);
  5276.       me_execute_string (temp);
  5277.       free (temp);
  5278.  
  5279.       temp = (char *)xmalloc (4 + strlen (up));
  5280.       sprintf (temp, ", %s", up);
  5281.       me_execute_string (temp);
  5282.       free (temp);
  5283.  
  5284.       output_paragraph_offset = op_orig;
  5285.     }
  5286. #endif /* HAVE_MACROS */
  5287.  
  5288.   if (!no_headers)
  5289.     {
  5290. #if defined (HAVE_MACROS)
  5291.       if (macro_expansion_output_stream)
  5292.     me_inhibit_expansion++;
  5293. #endif /* HAVE_MACROS */
  5294.  
  5295.       if (amiga_guide)
  5296.         {
  5297.           if (*next)
  5298.             {
  5299.               if (strcmp (next, "Top") == 0)
  5300.                 add_word_args ("\n@Next \"Main\"");
  5301.               else
  5302.                 {
  5303.                   add_word_args ("\n@Next ");
  5304.                   execute_string ("\"%s\"", next);
  5305.                 }
  5306.               filling_enabled = indented_fill = 0;
  5307.             }
  5308.           if (*prev)
  5309.             {
  5310.               if (strcmp (prev, "(DIR)") != 0)
  5311.                 {
  5312.                   if (strcmp (prev, "Top") == 0)
  5313.                     add_word_args ("\n@Prev \"Main\"");
  5314.                   else
  5315.                     {
  5316.                       add_word_args ("\n@Prev ");
  5317.                       execute_string ("\"%s\"", prev);
  5318.                     }
  5319.                   filling_enabled = indented_fill = 0;
  5320.                 }
  5321.             }
  5322.           if (*up)
  5323.             {
  5324.               if (strcmp (up, "(DIR)") != 0)
  5325.                 {
  5326.                   if (strcmp (up, "Top") == 0)
  5327.                     add_word_args ("\n@Toc \"Main\"");
  5328.                   else
  5329.                     {
  5330.                       add_word_args ("\n@Toc ");
  5331.                       execute_string ("\"%s\"", up);
  5332.                     }
  5333.                   filling_enabled = indented_fill = 0;
  5334.                 }
  5335.             }
  5336.  
  5337.         }
  5338.       else
  5339.         {
  5340.       if (*next)
  5341.     {
  5342.       execute_string (",  Next: %s", next);
  5343.       filling_enabled = indented_fill = 0;
  5344.     }
  5345.  
  5346.       if (*prev)
  5347.     {
  5348.       execute_string (",  Prev: %s", prev);
  5349.       filling_enabled = indented_fill = 0;
  5350.     }
  5351.  
  5352.       if (*up)
  5353.     {
  5354.       execute_string (",  Up: %s", up);
  5355.       filling_enabled = indented_fill = 0;
  5356.     }
  5357.          }
  5358. #if defined (HAVE_MACROS)
  5359.       if (macro_expansion_output_stream)
  5360.     me_inhibit_expansion--;
  5361. #endif /* HAVE_MACROS */
  5362.     }
  5363.  
  5364.   close_paragraph ();
  5365.   no_indent = 0;
  5366.  
  5367.   if (!*node)
  5368.     {
  5369.       line_error ("No node name specified for `%c%s' command",
  5370.           COMMAND_PREFIX, command);
  5371.       free (node);
  5372.       free (next);
  5373.       free (prev);
  5374.       free (up);
  5375.     }
  5376.   else
  5377.     {
  5378.       if (!*next) { free (next); next = (char *)NULL; }
  5379.       if (!*prev) { free (prev); prev = (char *)NULL; }
  5380.       if (!*up) { free (up); up = (char *)NULL; }
  5381.       remember_node (node, prev, next, up, new_node_pos, line_number, no_warn);
  5382.     }
  5383.  
  5384.   /* Change the section only if there was a sectioning command. */
  5385.   if (this_section >= 0)
  5386.     current_section = this_section;
  5387.  
  5388.   filling_enabled = 1;
  5389. }
  5390.  
  5391. /* Validation of an info file.
  5392.    Scan through the list of tag entrys touching the Prev, Next, and Up
  5393.    elements of each.  It is an error not to be able to touch one of them,
  5394.    except in the case of external node references, such as "(DIR)".
  5395.  
  5396.    If the Prev is different from the Up,
  5397.    then the Prev node must have a Next pointing at this node.
  5398.  
  5399.    Every node except Top must have an Up.
  5400.    The Up node must contain some sort of reference, other than a Next,
  5401.    to this node.
  5402.  
  5403.    If the Next is different from the Next of the Up,
  5404.    then the Next node must have a Prev pointing at this node. */
  5405. void
  5406. validate_file (tag_table)
  5407.      TAG_ENTRY *tag_table;
  5408. {
  5409.   char *old_input_filename = input_filename;
  5410.   TAG_ENTRY *tags = tag_table;
  5411.  
  5412.   while (tags != (TAG_ENTRY *) NULL)
  5413.     {
  5414.       register TAG_ENTRY *temp_tag;
  5415.  
  5416.       input_filename = tags->filename;
  5417.       line_number = tags->line_no;
  5418.  
  5419.       /* If this is a "no warn" node, don't validate it in any way. */
  5420.       if (tags->flags & NO_WARN)
  5421.     {
  5422.       tags = tags->next_ent;
  5423.       continue;
  5424.     }
  5425.  
  5426.       /* If this node has a Next, then make sure that the Next exists. */
  5427.       if (tags->next)
  5428.     {
  5429.       validate (tags->next, tags->line_no, "Next");
  5430.  
  5431.       /* If the Next node exists, and there is no Up, then make
  5432.          sure that the Prev of the Next points back. */
  5433.       if (temp_tag = find_node (tags->next))
  5434.         {
  5435.           char *prev;
  5436.  
  5437.           if (temp_tag->flags & NO_WARN)
  5438.         {
  5439.           /* Do nothing if we aren't supposed to issue warnings
  5440.              about this node. */
  5441.         }
  5442.           else
  5443.         {
  5444.           prev = temp_tag->prev;
  5445.           if (!prev || (strcmp (prev, tags->node) != 0))
  5446.             {
  5447.               line_error ("Node `%s''s Next field not pointed back to",
  5448.                   tags->node);
  5449.               line_number = temp_tag->line_no;
  5450.               input_filename = temp_tag->filename;
  5451.               line_error
  5452.             ("This node (`%s') is the one with the bad `Prev'",
  5453.              temp_tag->node);
  5454.               input_filename = tags->filename;
  5455.               line_number = tags->line_no;
  5456.               temp_tag->flags |= PREV_ERROR;
  5457.             }
  5458.         }
  5459.         }
  5460.     }
  5461.  
  5462.       /* Validate the Prev field if there is one, and we haven't already
  5463.      complained about it in some way.  You don't have to have a Prev
  5464.      field at this stage. */
  5465.       if (!(tags->flags & PREV_ERROR) && tags->prev)
  5466.     {
  5467.       int valid = validate (tags->prev, tags->line_no, "Prev");
  5468.  
  5469.       if (!valid)
  5470.         tags->flags |= PREV_ERROR;
  5471.       else
  5472.         {
  5473.           /* If the Prev field is not the same as the Up field,
  5474.          then the node pointed to by the Prev field must have
  5475.          a Next field which points to this node. */
  5476.           if (tags->up && (strcmp (tags->prev, tags->up) != 0))
  5477.         {
  5478.           temp_tag = find_node (tags->prev);
  5479.  
  5480.           /* If we aren't supposed to issue warnings about the
  5481.              target node, do nothing. */
  5482.           if (!temp_tag || (temp_tag->flags & NO_WARN))
  5483.             {
  5484.               /* Do nothing. */
  5485.             }
  5486.           else
  5487.             {
  5488.               if (!temp_tag->next ||
  5489.               (strcmp (temp_tag->next, tags->node) != 0))
  5490.             {
  5491.               line_error
  5492.                 ("Node `%s''s Prev field not pointed back to",
  5493.                  tags->node);
  5494.               line_number = temp_tag->line_no;
  5495.               input_filename = temp_tag->filename;
  5496.               line_error
  5497.                 ("This node (`%s') is the one with the bad `Next'",
  5498.                  temp_tag->node);
  5499.               input_filename = tags->filename;
  5500.               line_number = tags->line_no;
  5501.               temp_tag->flags |= NEXT_ERROR;
  5502.             }
  5503.             }
  5504.         }
  5505.         }
  5506.     }
  5507.  
  5508.       if (!tags->up && (strcasecmp (tags->node, "Top") != 0))
  5509.     line_error ("Node `%s' is missing an \"Up\" field", tags->node);
  5510.       else if (tags->up)
  5511.     {
  5512.       int valid = validate (tags->up, tags->line_no, "Up");
  5513.  
  5514.       /* If node X has Up: Y, then warn if Y fails to have a menu item
  5515.          or note pointing at X, if Y isn't of the form "(Y)". */
  5516.       if (valid && *tags->up != '(')
  5517.         {
  5518.           NODE_REF *nref, *tref, *list;
  5519.           NODE_REF *find_node_reference ();
  5520.  
  5521.           tref = (NODE_REF *) NULL;
  5522.           list = node_references;
  5523.  
  5524.           for (;;)
  5525.         {
  5526.           if (!(nref = find_node_reference (tags->node, list)))
  5527.             break;
  5528.  
  5529.           if (strcmp (nref->containing_node, tags->up) == 0)
  5530.             {
  5531.               if (nref->type != menu_reference)
  5532.             {
  5533.               tref = nref;
  5534.               list = nref->next;
  5535.             }
  5536.               else
  5537.             break;
  5538.             }
  5539.           list = nref->next;
  5540.         }
  5541.  
  5542.           if (!nref)
  5543.         {
  5544.           temp_tag = find_node (tags->up);
  5545.           line_number = temp_tag->line_no;
  5546.           input_filename = temp_tag->filename;
  5547.           if (!tref)
  5548.             line_error (
  5549. "`%s' has an Up field of `%s', but `%s' has no menu item for `%s'",
  5550.                 tags->node, tags->up, tags->up, tags->node);
  5551.           line_number = tags->line_no;
  5552.           input_filename = tags->filename;
  5553.         }
  5554.         }
  5555.     }
  5556.       tags = tags->next_ent;
  5557.     }
  5558.  
  5559.   validate_other_references (node_references);
  5560.   /* We have told the user about the references which didn't exist.
  5561.      Now tell him about the nodes which aren't referenced. */
  5562.  
  5563.   tags = tag_table;
  5564.   while (tags != (TAG_ENTRY *) NULL)
  5565.     {
  5566.       /* If this node is a "no warn" node, do nothing. */
  5567.       if (tags->flags & NO_WARN)
  5568.     {
  5569.       tags = tags->next_ent;
  5570.       continue;
  5571.     }
  5572.  
  5573.       /* Special hack.  If the node in question appears to have
  5574.          been referenced more than REFERENCE_WARNING_LIMIT times,
  5575.          give a warning. */
  5576.       if (tags->touched > reference_warning_limit)
  5577.     {
  5578.       input_filename = tags->filename;
  5579.       line_number = tags->line_no;
  5580.       warning ("Node `%s' has been referenced %d times",
  5581.            tags->node, tags->touched);
  5582.     }
  5583.  
  5584.       if (tags->touched == 0)
  5585.     {
  5586.       input_filename = tags->filename;
  5587.       line_number = tags->line_no;
  5588.  
  5589.       /* Notice that the node "Top" is special, and doesn't have to
  5590.          be referenced. */
  5591.       if (strcasecmp (tags->node, "Top") != 0)
  5592.         warning ("Unreferenced node `%s'", tags->node);
  5593.     }
  5594.       tags = tags->next_ent;
  5595.     }
  5596.   input_filename = old_input_filename;
  5597. }
  5598.  
  5599. /* Return 1 if tag correctly validated, or 0 if not. */
  5600. int
  5601. validate (tag, line, label)
  5602.      char *tag;
  5603.      int line;
  5604.      char *label;
  5605. {
  5606.   TAG_ENTRY *result;
  5607.  
  5608.   /* If there isn't a tag to verify, or if the tag is in another file,
  5609.      then it must be okay. */
  5610.   if (!tag || !*tag || *tag == '(')
  5611.     return (1);
  5612.  
  5613.   /* Otherwise, the tag must exist. */
  5614.   result = find_node (tag);
  5615.  
  5616.   if (!result)
  5617.     {
  5618.       line_number = line;
  5619.       line_error (
  5620. "Validation error.  `%s' field points to node `%s', which doesn't exist",
  5621.           label, tag);
  5622.       return (0);
  5623.     }
  5624.   result->touched++;
  5625.   return (1);
  5626. }
  5627.  
  5628. /* Split large output files into a series of smaller files.  Each file
  5629.    is pointed to in the tag table, which then gets written out as the
  5630.    original file.  The new files have the same name as the original file
  5631.    with a "-num" attached.  SIZE is the largest number of bytes to allow
  5632.    in any single split file. */
  5633. void
  5634. split_file (filename, size)
  5635.      char *filename;
  5636.      int size;
  5637. {
  5638.   char *root_filename, *root_pathname;
  5639.   char *the_file, *filename_part ();
  5640.   struct stat fileinfo;
  5641.   long file_size;
  5642.   char *the_header;
  5643.   int header_size;
  5644.  
  5645.   /* Can only do this to files with tag tables. */
  5646.   if (!tag_table)
  5647.     return;
  5648.  
  5649.   if (size == 0)
  5650.     size = DEFAULT_SPLIT_SIZE;
  5651.  
  5652.   if ((stat (filename, &fileinfo) != 0) ||
  5653.       (((long) fileinfo.st_size) < SPLIT_SIZE_THRESHOLD))
  5654.     return;
  5655.   file_size = (long) fileinfo.st_size;
  5656.  
  5657.   the_file = find_and_load (filename);
  5658.   if (!the_file)
  5659.     return;
  5660.  
  5661.   root_filename = filename_part (filename);
  5662.   root_pathname = pathname_part (filename);
  5663.  
  5664.   if (!root_pathname)
  5665.     root_pathname = strdup ("");
  5666.  
  5667.   /* Start splitting the file.  Walk along the tag table
  5668.      outputting sections of the file.  When we have written
  5669.      all of the nodes in the tag table, make the top-level
  5670.      pointer file, which contains indirect pointers and
  5671.      tags for the nodes. */
  5672.   {
  5673.     int which_file = 1;
  5674.     TAG_ENTRY *tags = tag_table;
  5675.     char *indirect_info = (char *)NULL;
  5676.  
  5677.     /* Remember the `header' of this file.  The first tag in the file is
  5678.        the bottom of the header; the top of the file is the start. */
  5679.     the_header = (char *)xmalloc (1 + (header_size = tags->position));
  5680.     memcpy (the_header, the_file, header_size);
  5681.  
  5682.     while (tags)
  5683.       {
  5684.     int file_top, file_bot, limit;
  5685.  
  5686.     /* Have to include the Control-_. */
  5687.     file_top = file_bot = tags->position;
  5688.     limit = file_top + size;
  5689.  
  5690.     /* If the rest of this file is only one node, then
  5691.        that is the entire subfile. */
  5692.     if (!tags->next_ent)
  5693.       {
  5694.         int i = tags->position + 1;
  5695.         char last_char = the_file[i];
  5696.  
  5697.         while (i < file_size)
  5698.           {
  5699.         if ((the_file[i] == '\037') &&
  5700.             ((last_char == '\n') ||
  5701.              (last_char == '\014')))
  5702.           break;
  5703.         else
  5704.           last_char = the_file[i];
  5705.         i++;
  5706.           }
  5707.         file_bot = i;
  5708.         tags = tags->next_ent;
  5709.         goto write_region;
  5710.       }
  5711.  
  5712.     /* Otherwise, find the largest number of nodes that can fit in
  5713.        this subfile. */
  5714.     for (; tags; tags = tags->next_ent)
  5715.       {
  5716.         if (!tags->next_ent)
  5717.           {
  5718.         /* This entry is the last node.  Search forward for the end
  5719.                of this node, and that is the end of this file. */
  5720.         int i = tags->position + 1;
  5721.         char last_char = the_file[i];
  5722.  
  5723.         while (i < file_size)
  5724.           {
  5725.             if ((the_file[i] == '\037') &&
  5726.             ((last_char == '\n') ||
  5727.              (last_char == '\014')))
  5728.               break;
  5729.             else
  5730.               last_char = the_file[i];
  5731.             i++;
  5732.           }
  5733.         file_bot = i;
  5734.  
  5735.         if (file_bot < limit)
  5736.           {
  5737.             tags = tags->next_ent;
  5738.             goto write_region;
  5739.           }
  5740.         else
  5741.           {
  5742.             /* Here we want to write out everything before the last
  5743.                node, and then write the last node out in a file
  5744.                by itself. */
  5745.             file_bot = tags->position;
  5746.             goto write_region;
  5747.           }
  5748.           }
  5749.  
  5750.         if (tags->next_ent->position > limit)
  5751.           {
  5752.         if (tags->position == file_top)
  5753.           tags = tags->next_ent;
  5754.  
  5755.         file_bot = tags->position;
  5756.  
  5757.           write_region:
  5758.         {
  5759.           int fd;
  5760.           char *split_filename;
  5761.  
  5762.           split_filename = (char *) xmalloc
  5763.             (10 + strlen (root_pathname) + strlen (root_filename));
  5764.           sprintf
  5765.             (split_filename,
  5766.              "%s%s-%d", root_pathname, root_filename, which_file);
  5767.  
  5768.           fd = open
  5769.             (split_filename, O_WRONLY | O_TRUNC | O_CREAT, 0666);
  5770.  
  5771.           if ((fd < 0) ||
  5772.               (write (fd, the_header, header_size) != header_size) ||
  5773.               (write (fd, the_file + file_top, file_bot - file_top)
  5774.                != (file_bot - file_top)) ||
  5775.               ((close (fd)) < 0))
  5776.             {
  5777.               perror (split_filename);
  5778.               if (fd != -1)
  5779.             close (fd);
  5780.               exit (FATAL);
  5781.             }
  5782.  
  5783.           if (!indirect_info)
  5784.             {
  5785.               indirect_info = the_file + file_top;
  5786.               sprintf (indirect_info, "\037\nIndirect:\n");
  5787.               indirect_info += strlen (indirect_info);
  5788.             }
  5789.  
  5790.           sprintf (indirect_info, "%s-%d: %d\n",
  5791.                root_filename, which_file, file_top);
  5792.  
  5793.           free (split_filename);
  5794.           indirect_info += strlen (indirect_info);
  5795.           which_file++;
  5796.           break;
  5797.         }
  5798.           }
  5799.       }
  5800.       }
  5801.  
  5802.     /* We have sucessfully created the subfiles.  Now write out the
  5803.        original again.  We must use `output_stream', or
  5804.        write_tag_table_indirect () won't know where to place the output. */
  5805.     output_stream = fopen (filename, "w");
  5806.     if (!output_stream)
  5807.       {
  5808.     perror (filename);
  5809.     exit (FATAL);
  5810.       }
  5811.  
  5812.     {
  5813.       int distance = indirect_info - the_file;
  5814.       fwrite (the_file, 1, distance, output_stream);
  5815.  
  5816.       /* Inhibit newlines. */
  5817.       paragraph_is_open = 0;
  5818.  
  5819.       write_tag_table_indirect ();
  5820.       fclose (output_stream);
  5821.       free (the_header);
  5822.       free (the_file);
  5823.       return;
  5824.     }
  5825.   }
  5826. }
  5827.  
  5828. /* Some menu hacking.  This is used to remember menu references while
  5829.    reading the input file.  After the output file has been written, if
  5830.    validation is on, then we use the contents of NODE_REFERENCES as a
  5831.    list of nodes to validate. */
  5832. char *
  5833. reftype_type_string (type)
  5834.      enum reftype type;
  5835. {
  5836.   switch (type)
  5837.     {
  5838.     case menu_reference:
  5839.       return ("Menu");
  5840.     case followed_reference:
  5841.       return ("Followed-Reference");
  5842.     default:
  5843.       return ("Internal-bad-reference-type");
  5844.     }
  5845. }
  5846.  
  5847. /* Remember this node name for later validation use. */
  5848. void
  5849. remember_node_reference (node, line, type)
  5850.      char *node;
  5851.      int line;
  5852.      enum reftype type;
  5853. {
  5854.   NODE_REF *temp = (NODE_REF *) xmalloc (sizeof (NODE_REF));
  5855.  
  5856.   temp->next = node_references;
  5857.   temp->node = strdup (node);
  5858.   temp->line_no = line;
  5859.   temp->section = current_section;
  5860.   temp->type = type;
  5861.   temp->containing_node = strdup (current_node);
  5862.   temp->filename = node_filename;
  5863.  
  5864.   node_references = temp;
  5865. }
  5866.  
  5867. void
  5868. validate_other_references (ref_list)
  5869.      register NODE_REF *ref_list;
  5870. {
  5871.   char *old_input_filename = input_filename;
  5872.  
  5873.   while (ref_list != (NODE_REF *) NULL)
  5874.     {
  5875.       input_filename = ref_list->filename;
  5876.       validate (ref_list->node, ref_list->line_no,
  5877.         reftype_type_string (ref_list->type));
  5878.       ref_list = ref_list->next;
  5879.     }
  5880.   input_filename = old_input_filename;
  5881. }
  5882.  
  5883. /* Find NODE in REF_LIST. */
  5884. NODE_REF *
  5885. find_node_reference (node, ref_list)
  5886.      char *node;
  5887.      register NODE_REF *ref_list;
  5888. {
  5889.   while (ref_list)
  5890.     {
  5891.       if (strcmp (node, ref_list->node) == 0)
  5892.     break;
  5893.       ref_list = ref_list->next;
  5894.     }
  5895.   return (ref_list);
  5896. }
  5897.  
  5898. void
  5899. free_node_references ()
  5900. {
  5901.   register NODE_REF *list, *temp;
  5902.  
  5903.   list = node_references;
  5904.  
  5905.   while (list)
  5906.     {
  5907.       temp = list;
  5908.       free (list->node);
  5909.       free (list->containing_node);
  5910.       list = list->next;
  5911.       free (temp);
  5912.     }
  5913.   node_references = (NODE_REF *) NULL;
  5914. }
  5915.  
  5916.   /* This function gets called at the start of every line while inside of
  5917.      a menu.  It checks to see if the line starts with "* ", and if so,
  5918.      remembers the node reference that this menu refers to.
  5919.      input_text_offset is at the \n just before the line start. */
  5920. #define menu_starter "* "
  5921. char *
  5922. glean_node_from_menu (remember_reference)
  5923.      int remember_reference;
  5924. {
  5925.   int i, orig_offset = input_text_offset;
  5926.   char *nodename;
  5927.  
  5928.   if (strncmp (&input_text[input_text_offset + 1],
  5929.            menu_starter,
  5930.            strlen (menu_starter)) != 0)
  5931.     return ((char *)NULL);
  5932.   else
  5933.     input_text_offset += strlen (menu_starter) + 1;
  5934.  
  5935.   get_until_in_line (":", &nodename);
  5936.   if (curchar () == ':')
  5937.     input_text_offset++;
  5938.   canon_white (nodename);
  5939.  
  5940.   if (curchar () == ':')
  5941.     goto save_node;
  5942.  
  5943.   free (nodename);
  5944.   get_rest_of_line (&nodename);
  5945.  
  5946.   /* Special hack: If the nodename follows the menu item name,
  5947.      then we have to read the rest of the line in order to find
  5948.      out what the nodename is.  But we still have to read the
  5949.      line later, in order to process any formatting commands that
  5950.      might be present.  So un-count the carriage return that has just
  5951.      been counted. */
  5952.   line_number--;
  5953.  
  5954.   isolate_nodename (nodename);
  5955.  
  5956. save_node:
  5957.   input_text_offset = orig_offset;
  5958.   normalize_node_name (nodename);
  5959.   i = strlen (nodename);
  5960.   if (i && nodename[i - 1] == ':')
  5961.     nodename[i - 1] = '\0';
  5962.  
  5963.   if (remember_reference)
  5964.     {
  5965.       remember_node_reference (nodename, line_number, menu_reference);
  5966.       free (nodename);
  5967.       return ((char *)NULL);
  5968.     }
  5969.   else
  5970.     return (nodename);
  5971. }
  5972.  
  5973. static void
  5974. isolate_nodename (nodename)
  5975.      char *nodename;
  5976. {
  5977.   register int i, c;
  5978.   int paren_seen, paren;
  5979.  
  5980.   if (!nodename)
  5981.     return;
  5982.  
  5983.   canon_white (nodename);
  5984.   paren_seen = paren = i = 0;
  5985.  
  5986.   if (*nodename == '.' || !*nodename)
  5987.     {
  5988.       *nodename = '\0';
  5989.       return;
  5990.     }
  5991.  
  5992.   if (*nodename == '(')
  5993.     {
  5994.       paren++;
  5995.       paren_seen++;
  5996.       i++;
  5997.     }
  5998.  
  5999.   for (; c = nodename[i]; i++)
  6000.     {
  6001.       if (paren)
  6002.     {
  6003.       if (c == '(')
  6004.         paren++;
  6005.       else if (c == ')')
  6006.         paren--;
  6007.  
  6008.       continue;
  6009.     }
  6010.  
  6011.       /* If the character following the close paren is a space, then this
  6012.      node has no more characters associated with it. */
  6013.       if (c == '\t' ||
  6014.       c == '\n' ||
  6015.       c == ','  ||
  6016.       ((paren_seen && nodename[i - 1] == ')') &&
  6017.        (c == ' ' || c == '.')) ||
  6018.       (c == '.' &&
  6019.        ((!nodename[i + 1] ||
  6020.          (cr_or_whitespace (nodename[i + 1])) ||
  6021.          (nodename[i + 1] == ')')))))
  6022.     break;
  6023.     }
  6024.   nodename[i] = '\0';
  6025. }
  6026.  
  6027. int
  6028. amiga_guide_convert_menu ()
  6029. {
  6030.   int i;
  6031.   char *gadgetname;
  6032.   int orig_offset;
  6033.  
  6034.   if (strncmp (&input_text[input_text_offset + 1],
  6035.                menu_starter,
  6036.                strlen (menu_starter)) != 0)
  6037.     return(0);
  6038.   else
  6039.     input_text_offset += strlen (menu_starter) + 1;
  6040.  
  6041.   orig_offset = input_text_offset;
  6042.   get_until_in_line (":", &gadgetname);
  6043.   if (curchar () == ':')
  6044.     input_text_offset++;
  6045.  
  6046.   if (curchar () == ':')
  6047.     {
  6048.       char * spaces = NULL;
  6049.       
  6050.       input_text_offset++;
  6051.       canon_white (gadgetname);
  6052.       normalize_node_name (gadgetname);
  6053.       in_amiga_guide_button = 1;
  6054.       
  6055.       spaces = amiga_button_text_length (gadgetname, amiga_menu_button_length);
  6056.  
  6057.       output_column -= (strlen("@{\"\" Link \"\"}") +
  6058.                         strlen(gadgetname));
  6059.       execute_string ("\n@w{ @@@{\" %s%s \" Link \"%s\"@} }   ", gadgetname, spaces, gadgetname);
  6060.       in_amiga_guide_button = 0;
  6061.       free(spaces);
  6062.       /* remove all space after node name */
  6063.       while (curchar () == ' ' || curchar () == '\t')
  6064.         input_text_offset++;
  6065.     }
  6066.   else
  6067.     {
  6068.       char *set_p(char *);
  6069.  
  6070.       char *nodename = NULL;
  6071.       char *newnodename = NULL;
  6072.       char *infopos = NULL;
  6073.       int nodestart;
  6074.  
  6075.       nodestart = input_text_offset;
  6076.       get_until_in_line (":", &newnodename);
  6077.       /* now change ".info)" to ".guide)" in newnodename */
  6078.       if ((infopos = strstr (newnodename, ".info)")) != NULL)
  6079.         {
  6080.       (*infopos) = '\0';
  6081.       nodename = (char *)xmalloc (strlen (newnodename)+8+1);
  6082.           strcpy (nodename, newnodename);
  6083.           strncat (nodename, ".guide)", strlen(".guide)"));
  6084.     }
  6085.       else
  6086.         nodename = strdup (newnodename);
  6087.  
  6088.       canon_white (nodename);
  6089.       normalize_node_name (nodename);
  6090.       
  6091.       if (*nodename == '(')
  6092.         {
  6093.           char * spaces = NULL;
  6094.           
  6095.           in_amiga_guide_button = 1;
  6096.           spaces = amiga_button_text_length (gadgetname, amiga_menu_button_length);
  6097.           
  6098.           for(i=1; i<strlen(nodename); i++)
  6099.             if (nodename[i] == ')') break;
  6100.  
  6101.           if (i == strlen(nodename)) return(0);
  6102.  
  6103.           nodename[i] = '\0';
  6104.  
  6105.           output_column -= (strlen("@{\"\" Link \"/\"}") +
  6106.                             strlen(nodename+1));
  6107.           execute_string ("\n@w{ @@@{\" %s%s \" Link \"%s/Main\"@} }   ", gadgetname, spaces, nodename+1);
  6108.           in_amiga_guide_button = 0;
  6109.           free(spaces);
  6110.         
  6111.           /* remove all space after node name */
  6112.           while (curchar () == ' ' || curchar () == '\t')
  6113.             input_text_offset++;
  6114.         }
  6115.       else /* there is a ":" in a nodename */
  6116.         {
  6117.           char * spaces = NULL;
  6118.       int new_offset;
  6119.           
  6120.       free (nodename);
  6121.       free (gadgetname);
  6122.       input_text_offset = orig_offset;
  6123.       get_until_in_line (":", &nodename);
  6124.       free (nodename);
  6125.       input_text_offset += 2; /* skip ": " */ 
  6126.  
  6127.       /* nodename is the part after the ":" */
  6128.       get_until_in_line (".", &nodename);
  6129.       new_offset = input_text_offset+1;
  6130.       canon_white (nodename);
  6131.           normalize_node_name (nodename);
  6132.  
  6133.       /* gadgetname is everything */
  6134.       input_text_offset = orig_offset;
  6135.       get_until_in_line (".", &gadgetname);
  6136.       canon_white (gadgetname);
  6137.           normalize_node_name (gadgetname);
  6138.  
  6139.       input_text_offset = new_offset;      
  6140.           in_amiga_guide_button = 1;
  6141.           spaces = amiga_button_text_length (gadgetname, amiga_menu_button_length);
  6142.           output_column -= (strlen("@{\"\" Link \"\"}") +
  6143.                             strlen(nodename));
  6144.           execute_string ("\n@w{ @@@{\" %s%s \" Link \"%s\"@} }   ", gadgetname, spaces, nodename);
  6145.           in_amiga_guide_button = 0;
  6146.           free(spaces);
  6147.           /* remove all space after node name */
  6148.           while (curchar () == ' ' || curchar () == '\t')
  6149.             input_text_offset++;
  6150.         }
  6151.  
  6152. /*      if (set_p("AmigaGuide-expand-menus"))
  6153.         {
  6154.           input_text_offset = nodestart;
  6155.         }
  6156.       else
  6157.         {
  6158.           for(i=0; i<input_text_offset-nodestart; i++) insert(' '); 
  6159.           input_text_offset++; 
  6160.         } */
  6161.  
  6162.       free(nodename);
  6163.       free(newnodename);
  6164.     }
  6165.  
  6166.   free(gadgetname);
  6167.   return(1);
  6168. }
  6169.  
  6170.  
  6171. void
  6172. cm_menu ()
  6173. {
  6174.   if (current_node == (char *)NULL)
  6175.     {
  6176.       warning ("%cmenu seen before a node has been defined", COMMAND_PREFIX);
  6177.       warning ("Creating `TOP' node.");
  6178.       execute_string ("@node Top");
  6179.     }
  6180.   begin_insertion (menu);
  6181. }
  6182.  
  6183. /* **************************************************************** */
  6184. /*                                    */
  6185. /*            Cross Reference Hacking                */
  6186. /*                                    */
  6187. /* **************************************************************** */
  6188.  
  6189. char *
  6190. get_xref_token ()
  6191. {
  6192.   char *string;
  6193.  
  6194.   get_until_in_braces (",", &string);
  6195.   if (curchar () == ',')
  6196.     input_text_offset++;
  6197.   fix_whitespace (string);
  6198.   return (string);
  6199. }
  6200.  
  6201. int px_ref_flag = 0;            /* Controls initial output string. */
  6202. int ref_flag = 0;               /* Controls initial output string, too. */
  6203.  
  6204. /* Make a cross reference. */
  6205. void
  6206. cm_xref (arg)
  6207. {
  6208.   static int one_argument = 0;
  6209.   
  6210.   if (arg == START)
  6211.     {
  6212.       char *arg1, *arg2, *arg3, *arg4, *arg5;
  6213.  
  6214.       arg1 = get_xref_token ();
  6215.       arg2 = get_xref_token ();
  6216.       arg3 = get_xref_token ();
  6217.       arg4 = get_xref_token ();
  6218.       arg5 = get_xref_token ();
  6219.  
  6220.       if (amiga_guide)
  6221.         {
  6222.       normalize_node_name(arg1);
  6223.       one_argument = 0;    
  6224.           if (!ref_flag)
  6225.             {
  6226.                char *xrefstring;
  6227.                char *Xrefstring;
  6228.                
  6229.                char *set_p (char *);
  6230.  
  6231.                if ((xrefstring = set_p ("xrefstring")) &&
  6232.                    (Xrefstring = set_p ("Xrefstring")))
  6233.                  {
  6234.                    add_word_args ("%s ", px_ref_flag ? xrefstring : Xrefstring);
  6235.                  }
  6236.                else
  6237.                  add_word_args("%s", px_ref_flag ? "see " : "See ");
  6238.             }
  6239.         }
  6240.       else
  6241.         add_word_args ("%s", px_ref_flag ? "*note " : "*Note ");
  6242.  
  6243.       if (*arg5 || *arg4)
  6244.     {
  6245.       char *node_name;
  6246.  
  6247.       if (!*arg2)
  6248.         {
  6249.           if (*arg3)
  6250.         node_name = arg3;
  6251.           else
  6252.         node_name = arg1;
  6253.         }
  6254.       else
  6255.         node_name = arg2;
  6256.  
  6257.           if (amiga_guide)
  6258.             {
  6259.               if (no_headers)
  6260.                 {
  6261.                   execute_string ("%s/%s", arg4, node_name);
  6262.                 }
  6263.               else
  6264.                 {
  6265.                   in_amiga_guide_button = 1;
  6266.                   output_column -= (amiga_guide_hidden_chars = strlen("@{\"\" Link \"/\"}") +
  6267.                                     strlen(arg4)+strlen(arg1));
  6268.                   canon_white(node_name);
  6269.                   execute_string ("@w{@@@{\"%s\" Link \"%s/%s\"@}}", node_name, arg4, arg1);
  6270.                   in_amiga_guide_button = 0;
  6271.                 }
  6272.             }
  6273.           else
  6274.           execute_string ("%s: (%s)%s", node_name, arg4, arg1);
  6275.         
  6276.       /* Free all of the arguments found. */
  6277.       if (arg1) free (arg1);
  6278.       if (arg2) free (arg2);
  6279.       if (arg3) free (arg3);
  6280.       if (arg4) free (arg4);
  6281.       if (arg5) free (arg5);
  6282.       return;
  6283.     }
  6284.       else
  6285.     remember_node_reference (arg1, line_number, followed_reference);
  6286.  
  6287.       if (*arg3)
  6288.     {
  6289.           if (amiga_guide)
  6290.             {
  6291.               if (no_headers)
  6292.                 {
  6293.                   execute_string (arg1);
  6294.                 }
  6295.               else
  6296.                 {
  6297.                   in_amiga_guide_button = 1;
  6298.  
  6299.                   if (!*arg2)
  6300.                     {
  6301.                       output_column -= (amiga_guide_hidden_chars = strlen("@{\"\" Link \"\"}") +
  6302.                                         +strlen(arg1));
  6303.                       canon_white(arg3);
  6304.                       execute_string ("@w{@@@{\"%s\" Link \"%s\"@}}", arg3, arg1);
  6305.                     }
  6306.                   else
  6307.                     {
  6308.                       output_column -= (amiga_guide_hidden_chars = strlen("@{\"\" Link \"\"}") +
  6309.                                         +strlen(arg1));
  6310.                       canon_white(arg2);
  6311.                       execute_string ("@w{@@@{\"%s\" Link \"%s\"@}}", arg2, arg1);
  6312.                     }
  6313.                   in_amiga_guide_button = 0;
  6314.                 }
  6315.             }
  6316.           else
  6317.             {
  6318.           if (!*arg2)
  6319.               execute_string ("%s: %s", arg3, arg1);
  6320.           else
  6321.             execute_string ("%s: %s", arg2, arg1);
  6322.             }
  6323.         }
  6324.       else
  6325.         {
  6326.           if (amiga_guide)
  6327.             {
  6328.               if (no_headers)
  6329.                 {
  6330.                    execute_string (arg1);
  6331.                 }
  6332.               else
  6333.                 {
  6334.                   in_amiga_guide_button = 1;
  6335.  
  6336.                   if (*arg2)
  6337.                     {
  6338.                        output_column -= (amiga_guide_hidden_chars = strlen("@{\"\" Link \"\"}") +
  6339.                                          +strlen(arg1));
  6340.                        canon_white(arg2);
  6341.                        execute_string ("@w{@@@{\"%s\" Link \"%s\"@}}", arg2, arg1);
  6342.                     }
  6343.                   else
  6344.                     {
  6345.                        output_column -= (amiga_guide_hidden_chars = strlen("@{\"\" Link \"\"}") +
  6346.                                          +strlen(arg1));
  6347.                        canon_white(arg1);
  6348.                        execute_string ("@w{@@@{\"%s\" Link \"%s\"@}}", arg1, arg1);
  6349.                one_argument = 1;
  6350.                     }
  6351.                   in_amiga_guide_button = 0;
  6352.                 }
  6353.             }
  6354.           else
  6355.         {
  6356.           if (*arg2)
  6357.             execute_string ("%s: %s", arg2, arg1);
  6358.           else
  6359.             execute_string ("%s::", arg1);
  6360.         }
  6361.         }
  6362.  
  6363.       /* Free all of the arguments found. */
  6364.       if (arg1) free (arg1);
  6365.       if (arg2) free (arg2);
  6366.       if (arg3) free (arg3);
  6367.       if (arg4) free (arg4);
  6368.       if (arg5) free (arg5);
  6369.     }
  6370.   else
  6371.     {
  6372.       /* Check to make sure that the next non-whitespace character is either
  6373.          a period or a comma. input_text_offset is pointing at the "}" which
  6374.          ended the xref or pxref command. */
  6375.       int temp = input_text_offset + 1;
  6376.       
  6377.       if (amiga_guide && one_argument)
  6378.         {
  6379.       return;
  6380.         }
  6381.       else if (!amiga_guide)
  6382.         if (output_paragraph[output_paragraph_offset - 2] == ':' &&
  6383.         output_paragraph[output_paragraph_offset - 1] == ':')
  6384.       return;
  6385.       while (temp < size_of_input_text)
  6386.     {
  6387.       if (cr_or_whitespace (input_text[temp]))
  6388.         temp++;
  6389.       else
  6390.         {
  6391.           if (input_text[temp] == '.' ||
  6392.           input_text[temp] == ',' ||
  6393.           input_text[temp] == '\t')
  6394.         return;
  6395.           else
  6396.         {
  6397.           line_error (
  6398.         "Cross-reference must be terminated with a period or a comma");
  6399.           return;
  6400.         }
  6401.         }
  6402.     }
  6403.     }
  6404. }
  6405.  
  6406. void
  6407. cm_pxref (arg)
  6408.      int arg;
  6409. {
  6410.   if (arg == START)
  6411.     {
  6412.       px_ref_flag++;
  6413.       cm_xref (arg);
  6414.       px_ref_flag--;
  6415.     }
  6416.   else if (!amiga_guide)
  6417.     {
  6418.       add_char ('.');
  6419.     }
  6420. }
  6421.  
  6422. void
  6423. cm_inforef (arg)
  6424.      int arg;
  6425. {
  6426.   if (arg == START)
  6427.     {
  6428.       char *node, *pname, *file;
  6429.  
  6430.       node = get_xref_token ();
  6431.       pname = get_xref_token ();
  6432.       file = get_xref_token ();
  6433.       if (amiga_guide)
  6434.         {
  6435.           add_word("See ");
  6436.  
  6437.           if (no_headers)
  6438.             {
  6439.               execute_string ("%s/%s", file, node);
  6440.             }
  6441.           else
  6442.             {
  6443.               in_amiga_guide_button = 1;
  6444.               output_column -= (amiga_guide_hidden_chars = strlen("@{\"\" Link \"/\"}") +
  6445.                                 strlen(file)+strlen(node));
  6446.               canon_white(pname);
  6447.               execute_string ("@w{@@@{\"%s\" Link \"%s/%s\"@}}", pname, file, node);
  6448.               in_amiga_guide_button = 0;
  6449.             }
  6450.         }
  6451.       else
  6452.         execute_string ("*note %s: (%s)%s", pname, file, node);
  6453.     }
  6454. }
  6455.  
  6456. /* **************************************************************** */
  6457. /*                                    */
  6458. /*            Insertion Command Stubs                */
  6459. /*                                    */
  6460. /* **************************************************************** */
  6461.  
  6462. void
  6463. cm_quotation ()
  6464. {
  6465.   begin_insertion (quotation);
  6466. }
  6467.  
  6468. void
  6469. cm_example ()
  6470. {
  6471.   begin_insertion (example);
  6472. }
  6473.  
  6474. void
  6475. cm_smallexample ()
  6476. {
  6477.   begin_insertion (smallexample);
  6478. }
  6479.  
  6480. void
  6481. cm_lisp ()
  6482. {
  6483.   begin_insertion (lisp);
  6484. }
  6485.  
  6486. void
  6487. cm_smalllisp ()
  6488. {
  6489.   begin_insertion (smalllisp);
  6490. }
  6491.  
  6492. /* @cartouche/@end cartouche draws box with rounded corners in
  6493.    TeX output.  Right now, just a NOP insertion. */
  6494. void
  6495. cm_cartouche ()
  6496. {
  6497.   begin_insertion (cartouche);
  6498. }
  6499.  
  6500. void
  6501. cm_format ()
  6502. {
  6503.   begin_insertion (format);
  6504. }
  6505.  
  6506. void
  6507. cm_display ()
  6508. {
  6509.   begin_insertion (display);
  6510. }
  6511.  
  6512. void
  6513. cm_itemize ()
  6514. {
  6515.   begin_insertion (itemize);
  6516. }
  6517.  
  6518. void
  6519. cm_enumerate ()
  6520. {
  6521.   do_enumeration (enumerate, "1");
  6522. }
  6523.  
  6524. /* Start an enumeration insertion of type TYPE.  If the user supplied
  6525.    no argument on the line, then use DEFAULT_STRING as the initial string. */
  6526. void
  6527. do_enumeration (type, default_string)
  6528.      int type;
  6529.      char *default_string;
  6530. {
  6531.   get_until_in_line (".", &enumeration_arg);
  6532.   canon_white (enumeration_arg);
  6533.  
  6534.   if (!*enumeration_arg)
  6535.     {
  6536.       free (enumeration_arg);
  6537.       enumeration_arg = strdup (default_string);
  6538.     }
  6539.  
  6540.   if (!isdigit (*enumeration_arg) && !isletter (*enumeration_arg))
  6541.     {
  6542.       warning ("%s requires a letter or a digit", insertion_type_pname (type));
  6543.  
  6544.       switch (type)
  6545.     {
  6546.     case enumerate:
  6547.       default_string = "1";
  6548.       break;
  6549.     }
  6550.       enumeration_arg = strdup (default_string);
  6551.     }
  6552.   begin_insertion (type);
  6553. }
  6554.  
  6555. void
  6556. cm_table ()
  6557. {
  6558.   begin_insertion (table);
  6559. }
  6560.  
  6561. void
  6562. cm_ftable ()
  6563. {
  6564.   begin_insertion (ftable);
  6565. }
  6566.  
  6567. void
  6568. cm_vtable ()
  6569. {
  6570.   begin_insertion (vtable);
  6571. }
  6572.  
  6573. void
  6574. cm_group ()
  6575. {
  6576.   begin_insertion (group);
  6577. }
  6578.  
  6579. void
  6580. cm_ifinfo ()
  6581. {
  6582.   begin_insertion (ifinfo);
  6583. }
  6584.  
  6585. /* Begin an insertion where the lines are not filled or indented. */
  6586. void
  6587. cm_flushleft ()
  6588. {
  6589.   begin_insertion (flushleft);
  6590. }
  6591.  
  6592. /* Begin an insertion where the lines are not filled, and each line is
  6593.    forced to the right-hand side of the page. */
  6594. void
  6595. cm_flushright ()
  6596. {
  6597.   begin_insertion (flushright);
  6598. }
  6599.  
  6600.  
  6601. /* **************************************************************** */
  6602. /*                                    */
  6603. /*              Conditional Handling                */
  6604. /*                                    */
  6605. /* **************************************************************** */
  6606.  
  6607. /* A structure which contains `defined' variables. */
  6608. typedef struct _defines {
  6609.   struct _defines *next;
  6610.   char *name;
  6611.   char *value;
  6612. } DEFINE;
  6613.  
  6614. /* The linked list of `set' defines. */
  6615. DEFINE *defines = (DEFINE *)NULL;
  6616.  
  6617. /* Add NAME to the list of `set' defines. */
  6618. void
  6619. set (name, value)
  6620.      char *name;
  6621.      char *value;
  6622. {
  6623.   DEFINE *temp;
  6624.  
  6625.   for (temp = defines; temp; temp = temp->next)
  6626.     if (strcmp (name, temp->name) == 0)
  6627.       {
  6628.     free (temp->value);
  6629.     temp->value = strdup (value);
  6630.     return;
  6631.       }
  6632.  
  6633.   temp = (DEFINE *)xmalloc (sizeof (DEFINE));
  6634.   temp->next = defines;
  6635.   temp->name = strdup (name);
  6636.   temp->value = strdup (value);
  6637.   defines = temp;
  6638. }
  6639.  
  6640. /* Remove NAME from the list of `set' defines. */
  6641. void
  6642. clear (name)
  6643.      char *name;
  6644. {
  6645.   register DEFINE *temp, *last;
  6646.  
  6647.   last = (DEFINE *)NULL;
  6648.   temp = defines;
  6649.  
  6650.   while (temp)
  6651.     {
  6652.       if (strcmp (temp->name, name) == 0)
  6653.     {
  6654.       if (last)
  6655.         last->next = temp->next;
  6656.       else
  6657.         defines = temp->next;
  6658.  
  6659.       free (temp->name);
  6660.       free (temp->value);
  6661.       free (temp);
  6662.       break;
  6663.     }
  6664.       last = temp;
  6665.       temp = temp->next;
  6666.     }
  6667. }
  6668.  
  6669. /* Return the value of NAME.  The return value is NULL if NAME is unset. */
  6670. char *
  6671. set_p (name)
  6672.      char *name;
  6673. {
  6674.   register DEFINE *temp;
  6675.  
  6676.   for (temp = defines; temp; temp = temp->next)
  6677.     if (strcmp (temp->name, name) == 0)
  6678.       return (temp->value);
  6679.  
  6680.   return ((char *)NULL);
  6681. }
  6682.  
  6683. /* Conditionally parse based on the current command name. */
  6684. void
  6685. command_name_condition ()
  6686. {
  6687.   char *discarder;
  6688.  
  6689.   discarder = (char *)xmalloc (8 + strlen (command));
  6690.  
  6691.   sprintf (discarder, "\n%cend %s", COMMAND_PREFIX, command);
  6692.   discard_until (discarder);
  6693.   discard_until ("\n");
  6694.  
  6695.   free (discarder);
  6696. }
  6697.  
  6698. /* Create a variable whose name appears as the first word on this line. */
  6699. void
  6700. cm_set ()
  6701. {
  6702.   handle_variable (SET);
  6703. }
  6704.  
  6705. /* Remove a variable whose name appears as the first word on this line. */
  6706. void
  6707. cm_clear ()
  6708. {
  6709.   handle_variable (CLEAR);
  6710. }
  6711.  
  6712. void
  6713. cm_ifset ()
  6714. {
  6715.   handle_variable (IFSET);
  6716. }
  6717.  
  6718. void
  6719. cm_ifclear ()
  6720. {
  6721.   handle_variable (IFCLEAR);
  6722. }
  6723.  
  6724. /* This command takes braces, but we parse the contents specially, so we
  6725.    don't use the standard brace popping code.
  6726.  
  6727.    The syntax @ifeq{arg1, arg2, texinfo commands} performs texinfo commands
  6728.    if ARG1 and ARG2 caselessly string compare to the same string, otherwise,
  6729.    it produces no output. */
  6730. void
  6731. cm_ifeq ()
  6732. {
  6733.   register int i;
  6734.   char **arglist;
  6735.  
  6736.   arglist = get_brace_args (0);
  6737.  
  6738.   if (arglist)
  6739.     {
  6740.       if (array_len (arglist) > 1)
  6741.     {
  6742.       if ((strcasecmp (arglist[0], arglist[1]) == 0) &&
  6743.           (arglist[2] != (char *)NULL))
  6744.         execute_string ("%s\n", arglist[2]);
  6745.     }
  6746.  
  6747.       free_array (arglist);
  6748.     }
  6749. }
  6750.  
  6751. void
  6752. cm_value (arg, start_pos, end_pos)
  6753.      int arg, start_pos, end_pos;
  6754. {
  6755.   if (arg == END)
  6756.     {
  6757.       char *name, *value;
  6758.       name = (char *)&output_paragraph[start_pos];
  6759.       output_paragraph[end_pos] = '\0';
  6760.       name = strdup (name);
  6761.       value = set_p (name);
  6762.       output_column -= end_pos - start_pos;
  6763.       output_paragraph_offset = start_pos;
  6764.  
  6765.       if (value)
  6766.         execute_string ("%s", value);
  6767.       else
  6768.     add_word_args ("{No Value For \"%s\"}", name);
  6769.  
  6770.       free (name);
  6771.     }
  6772. }
  6773.  
  6774. /* Set, clear, or conditionalize based on ACTION. */
  6775. void
  6776. handle_variable (action)
  6777.      int action;
  6778. {
  6779.   char *name;
  6780.  
  6781.   get_rest_of_line (&name);
  6782.   backup_input_pointer ();
  6783.   canon_white (name);
  6784.   handle_variable_internal (action, name);
  6785.   free (name);
  6786. }
  6787.  
  6788. void
  6789. handle_variable_internal (action, name)
  6790.      int action;
  6791.      char *name;
  6792. {
  6793.   char *temp;
  6794.   int delimiter, additional_text_present = 0;
  6795.  
  6796.   /* Only the first word of NAME is a valid tag. */
  6797.   temp = name;
  6798.   delimiter = 0;
  6799.   while (*temp && (delimiter || !whitespace (*temp)))
  6800.     {
  6801. /* #if defined (SET_WITH_EQUAL) */
  6802.       if (*temp == '"' || *temp == '\'')
  6803.     {
  6804.       if (*temp == delimiter)
  6805.         delimiter = 0;
  6806.       else
  6807.         delimiter = *temp;
  6808.     }
  6809. /* #endif SET_WITH_EQUAL */
  6810.       temp++;
  6811.     }
  6812.  
  6813.   if (*temp)
  6814.     additional_text_present++;
  6815.  
  6816.   *temp = '\0';
  6817.  
  6818.   if (!*name)
  6819.     line_error ("%c%s requires a name", COMMAND_PREFIX, command);
  6820.   else
  6821.     {
  6822.       switch (action)
  6823.     {
  6824.     case SET:
  6825.       {
  6826.         char *value;
  6827.  
  6828. #if defined (SET_WITH_EQUAL)
  6829.         /* Allow a value to be saved along with a variable.  The value is
  6830.            the text following an `=' sign in NAME, if any is present. */
  6831.  
  6832.         for (value = name; *value && *value != '='; value++);
  6833.  
  6834.         if (*value)
  6835.           *value++ = '\0';
  6836.  
  6837.         if (*value == '"' || *value == '\'')
  6838.           {
  6839.         value++;
  6840.         value[strlen (value) - 1] = '\0';
  6841.           }
  6842.  
  6843. #else /* !SET_WITH_EQUAL */
  6844.         /* The VALUE of NAME is the remainder of the line sans
  6845.            whitespace. */
  6846.         if (additional_text_present)
  6847.           {
  6848.         value = temp + 1;
  6849.         canon_white (value);
  6850.           }
  6851.         else
  6852.           value = "";
  6853. #endif /* !SET_WITH_VALUE */
  6854.  
  6855.         set (name, value);
  6856.       }
  6857.       break;
  6858.  
  6859.     case CLEAR:
  6860.       clear (name);
  6861.       break;
  6862.  
  6863.     case IFSET:
  6864.     case IFCLEAR:
  6865.       /* If IFSET and NAME is not set, or if IFCLEAR and NAME is set,
  6866.          read lines from the the file until we reach a matching
  6867.          "@end CONDITION".  This means that we only take note of
  6868.          "@ifset/clear" and "@end" commands. */
  6869.       {
  6870.         char condition[8];
  6871.         int condition_len;
  6872.  
  6873.         if (action == IFSET)
  6874.           strcpy (condition, "ifset");
  6875.         else
  6876.           strcpy (condition, "ifclear");
  6877.  
  6878.         condition_len = strlen (condition);
  6879.  
  6880.       if ((action == IFSET && !set_p (name)) ||
  6881.           (action == IFCLEAR && set_p (name)))
  6882.         {
  6883.           int level = 0, done = 0;
  6884.  
  6885.           while (!done)
  6886.         {
  6887.           char *freeable_line, *line;
  6888.  
  6889.           get_rest_of_line (&freeable_line);
  6890.  
  6891.           for (line = freeable_line; whitespace (*line); line++);
  6892.  
  6893.           if (*line == COMMAND_PREFIX &&
  6894.               (strncmp (line + 1, condition, condition_len) == 0))
  6895.             level++;
  6896.           else if (strncmp (line, "@end", 4) == 0)
  6897.             {
  6898.               char *cname = line + 4;
  6899.               char *temp;
  6900.  
  6901.               while (*cname && whitespace (*cname))
  6902.             cname++;
  6903.               temp = cname;
  6904.  
  6905.               while (*temp && !whitespace (*temp))
  6906.             temp++;
  6907.               *temp = '\0';
  6908.  
  6909.               if (strcmp (cname, condition) == 0)
  6910.             {
  6911.               if (!level)
  6912.                 {
  6913.                   done = 1;
  6914.                 }
  6915.               else
  6916.                 level--;
  6917.             }
  6918.             }
  6919.           free (freeable_line);
  6920.         }
  6921.           /* We found the end of a false @ifset/ifclear.  If we are
  6922.          in a menu, back up over the newline that ends the ifset,
  6923.          since that newline may also begin the next menu entry. */
  6924.           break;
  6925.         }
  6926.       else
  6927.         {
  6928.           if (action == IFSET)
  6929.         begin_insertion (ifset);
  6930.           else
  6931.         begin_insertion (ifclear);
  6932.         }
  6933.       }
  6934.       break;
  6935.     }
  6936.     }
  6937. }
  6938.  
  6939.  
  6940. /* **************************************************************** */
  6941. /*                                    */
  6942. /*            Execution of Random Text not in file        */
  6943. /*                                    */
  6944. /* **************************************************************** */
  6945.  
  6946. typedef struct {
  6947.   char *string;            /* The string buffer. */
  6948.   int size;            /* The size of the buffer. */
  6949.   int in_use;            /* Non-zero means string currently in use. */
  6950. } EXECUTION_STRING;
  6951.  
  6952. static EXECUTION_STRING **execution_strings = (EXECUTION_STRING **)NULL;
  6953. static int execution_strings_index = 0;
  6954. static int execution_strings_slots = 0;
  6955.  
  6956. EXECUTION_STRING *
  6957. get_execution_string (initial_size)
  6958.      int initial_size;
  6959. {
  6960.   register int i = 0;
  6961.   EXECUTION_STRING *es = (EXECUTION_STRING *)NULL;
  6962.  
  6963.   if (execution_strings)
  6964.     {
  6965.       for (i = 0; i < execution_strings_index; i++)
  6966.     if (execution_strings[i] && (execution_strings[i]->in_use == 0))
  6967.       {
  6968.         es = execution_strings[i];
  6969.         break;
  6970.       }
  6971.     }
  6972.  
  6973.   if (!es)
  6974.     {
  6975.       if (execution_strings_index + 1 >= execution_strings_slots)
  6976.     {
  6977.       execution_strings = (EXECUTION_STRING **)xrealloc
  6978.         (execution_strings,
  6979.          (execution_strings_slots += 3) * sizeof (EXECUTION_STRING *));
  6980.       for (; i < execution_strings_slots; i++)
  6981.         execution_strings[i] = (EXECUTION_STRING *)NULL;
  6982.     }
  6983.  
  6984.       execution_strings[execution_strings_index] =
  6985.     (EXECUTION_STRING *)xmalloc (sizeof (EXECUTION_STRING));
  6986.       es = execution_strings[execution_strings_index];
  6987.       execution_strings_index++;
  6988.  
  6989.       es->size = 0;
  6990.       es->string = (char *)NULL;
  6991.       es->in_use = 0;
  6992.     }
  6993.  
  6994.   if (initial_size > es->size)
  6995.     {
  6996.       es->string = (char *) xrealloc (es->string, initial_size);
  6997.       es->size = initial_size;
  6998.     }
  6999.   return (es);
  7000. }
  7001.  
  7002. /* Execute the string produced by formatting the ARGs with FORMAT.  This
  7003.    is like submitting a new file with @include. */
  7004. #if defined (HAVE_VARARGS_H) && defined (HAVE_VSPRINTF)
  7005. void
  7006. execute_string (va_alist)
  7007.      va_dcl
  7008. {
  7009.   EXECUTION_STRING *es;
  7010.   char *temp_string;
  7011.   char *format;
  7012.   va_list args;
  7013.  
  7014.   es = get_execution_string (4000);
  7015.   temp_string = es->string;
  7016.   es->in_use = 1;
  7017.  
  7018.   va_start (args);
  7019.   format = va_arg (args, char *);
  7020.   vsprintf (temp_string, format, args);
  7021.   va_end (args);
  7022.  
  7023. #else /* !(HAVE_VARARGS_H && HAVE_VSPRINTF) */
  7024.  
  7025. void
  7026. execute_string (format, arg1, arg2, arg3, arg4, arg5)
  7027.      char *format;
  7028. {
  7029.   EXECUTION_STRING *es;
  7030.   char *temp_string;
  7031.  
  7032.   es = get_execution_string (4000);
  7033.   temp_string = es->string;
  7034.   es->in_use = 1;
  7035.  
  7036.   sprintf (temp_string, format, arg1, arg2, arg3, arg4, arg5);
  7037.  
  7038. #endif /* !(HAVE_VARARGS_H && HAVE_VSPRINTF) */
  7039.  
  7040.   pushfile ();
  7041.   input_text_offset = 0;
  7042.   input_text = temp_string;
  7043.   input_filename = strdup (input_filename);
  7044.   size_of_input_text = strlen (temp_string);
  7045.  
  7046.   executing_string++;
  7047.   reader_loop ();
  7048.   free (input_filename);
  7049.  
  7050.   popfile ();
  7051.   executing_string--;
  7052.   es->in_use = 0;
  7053. }
  7054.  
  7055. /* **************************************************************** */
  7056. /*                                    */
  7057. /*            @itemx, @item                    */
  7058. /*                                    */
  7059. /* **************************************************************** */
  7060.  
  7061. static int itemx_flag = 0;
  7062.  
  7063. void
  7064. cm_itemx ()
  7065. {
  7066.   itemx_flag++;
  7067.   cm_item ();
  7068.   itemx_flag--;
  7069. }
  7070.  
  7071. void
  7072. cm_item ()
  7073. {
  7074.   char *rest_of_line, *item_func;
  7075.  
  7076.   /* Can only hack "@item" while inside of an insertion. */
  7077.   if (insertion_level)
  7078.     {
  7079.       INSERTION_ELT *stack = insertion_stack;
  7080.       int original_input_text_offset;
  7081.  
  7082.       skip_whitespace ();
  7083.       original_input_text_offset = input_text_offset;
  7084.  
  7085.       get_rest_of_line (&rest_of_line);
  7086.       canon_white (rest_of_line);
  7087.       item_func = current_item_function ();
  7088.  
  7089.       /* Okay, do the right thing depending on which insertion function
  7090.      is active. */
  7091.  
  7092.     switch_top:
  7093.       switch (stack->insertion)
  7094.     {
  7095.     case ifinfo:
  7096.     case ifset:
  7097.     case ifclear:
  7098.     case cartouche:
  7099.       stack = stack->next;
  7100.       if (!stack)
  7101.         goto no_insertion;
  7102.       else
  7103.         goto switch_top;
  7104.       break;
  7105.  
  7106.     case menu:
  7107.     case quotation:
  7108.     case example:
  7109.     case smallexample:
  7110.     case lisp:
  7111.     case format:
  7112.     case display:
  7113.     case group:
  7114.       line_error ("The `%c%s' command is meaningless within a `@%s' block",
  7115.               COMMAND_PREFIX, command,
  7116.               insertion_type_pname (current_insertion_type ()));
  7117.       break;
  7118.  
  7119.     case itemize:
  7120.     case enumerate:
  7121.       if (itemx_flag)
  7122.         {
  7123.           line_error ("%citemx is not meaningful inside of a `%s' block",
  7124.               COMMAND_PREFIX,
  7125.               insertion_type_pname (current_insertion_type ()));
  7126.         }
  7127.       else
  7128.         {
  7129.           start_paragraph ();
  7130.           kill_self_indent (-1);
  7131.           filling_enabled = indented_fill = 1;
  7132.  
  7133.           if (current_insertion_type () == itemize)
  7134.         {
  7135.           indent (output_column = current_indent - 2);
  7136.  
  7137.           /* I need some way to determine whether this command
  7138.              takes braces or not.  I believe the user can type
  7139.              either "@bullet" or "@bullet{}".  Of course, they
  7140.              can also type "o" or "#" or whatever else they want. */
  7141.           if (item_func && *item_func)
  7142.             {
  7143.               if (*item_func == COMMAND_PREFIX)
  7144.             if (item_func[strlen (item_func) - 1] != '}')
  7145.               execute_string ("%s{}", item_func);
  7146.             else
  7147.               execute_string ("%s", item_func);
  7148.               else
  7149.             execute_string ("%s", item_func);
  7150.             }
  7151.           insert (' ');
  7152.           output_column++;
  7153.         }
  7154.           else
  7155.         enumerate_item ();
  7156.  
  7157.           /* Special hack.  This makes close paragraph ignore you until
  7158.          the start_paragraph () function has been called. */
  7159.           must_start_paragraph = 1;
  7160.  
  7161.           /* Ultra special hack.  It appears that some people incorrectly
  7162.          place text directly after the @item, instead of on a new line
  7163.          by itself.  This happens to work in TeX, so I make it work
  7164.          here. */
  7165.           if (*rest_of_line)
  7166.         {
  7167.           line_number--;
  7168.           input_text_offset = original_input_text_offset;
  7169.         }
  7170.         }
  7171.       break;
  7172.  
  7173.     case table:
  7174.     case ftable:
  7175.     case vtable:
  7176.       {
  7177.         /* Get rid of extra characters. */
  7178.         kill_self_indent (-1);
  7179.  
  7180.         /* close_paragraph () almost does what we want.  The problem
  7181.            is when paragraph_is_open, and last_char_was_newline, and
  7182.            the last newline has been turned into a space, because
  7183.            filling_enabled. I handle it here. */
  7184.         if (last_char_was_newline && filling_enabled && paragraph_is_open)
  7185.           insert ('\n');
  7186.         close_paragraph ();
  7187.  
  7188. #if defined (INDENT_PARAGRAPHS_IN_TABLE)
  7189.         /* Indent on a new line, but back up one indentation level. */
  7190.         {
  7191.           int t;
  7192.  
  7193.           t = inhibit_paragraph_indentation;
  7194.           inhibit_paragraph_indentation = 1;
  7195.           /* At this point, inserting any non-whitespace character will
  7196.          force the existing indentation to be output. */
  7197.           add_char ('i');
  7198.           inhibit_paragraph_indentation = t;
  7199.         }
  7200. #else /* !INDENT_PARAGRAPHS_IN_TABLE */
  7201.         add_char ('i');
  7202. #endif /* !INDENT_PARAGRAPHS_IN_TABLE */
  7203.  
  7204.         output_paragraph_offset--;
  7205.         kill_self_indent (default_indentation_increment + 1);
  7206.  
  7207.         /* Add item's argument to the line. */
  7208.         filling_enabled = 0;
  7209.         if (item_func && *item_func)
  7210.            execute_string ("%s{%s}", item_func, rest_of_line);
  7211.          else
  7212.            execute_string ("%s", rest_of_line);
  7213.  
  7214.         if (current_insertion_type () == ftable)
  7215.           execute_string ("%cfindex %s\n", COMMAND_PREFIX, rest_of_line);
  7216.  
  7217.         if (current_insertion_type () == vtable)
  7218.           execute_string ("%cvindex %s\n", COMMAND_PREFIX, rest_of_line);
  7219.  
  7220.         /* Start a new line, and let start_paragraph ()
  7221.            do the indenting of it for you. */
  7222.         close_single_paragraph ();
  7223.         indented_fill = filling_enabled = 1;
  7224.       }
  7225.     }
  7226.       free (rest_of_line);
  7227.     }
  7228.   else
  7229.     {
  7230.     no_insertion:
  7231.       line_error ("%c%s found outside of an insertion block",
  7232.           COMMAND_PREFIX, command);
  7233.     }
  7234. }
  7235.  
  7236.  
  7237. /* **************************************************************** */
  7238. /*                                    */
  7239. /*            Defun and Friends                   */
  7240. /*                                    */
  7241. /* **************************************************************** */
  7242.  
  7243. #define DEFUN_SELF_DELIMITING(c)                    \
  7244.   (((c) == '(')                                \
  7245.    || ((c) == ')')                            \
  7246.    || ((c) == '[')                            \
  7247.    || ((c) == ']'))
  7248.  
  7249. struct token_accumulator
  7250. {
  7251.   unsigned int length;
  7252.   unsigned int index;
  7253.   char **tokens;
  7254. };
  7255.  
  7256. void
  7257. initialize_token_accumulator (accumulator)
  7258.      struct token_accumulator *accumulator;
  7259. {
  7260.   (accumulator->length) = 0;
  7261.   (accumulator->index) = 0;
  7262.   (accumulator->tokens) = NULL;
  7263. }
  7264.  
  7265. void
  7266. accumulate_token (accumulator, token)
  7267.      struct token_accumulator *accumulator;
  7268.      char *token;
  7269. {
  7270.   if ((accumulator->index) >= (accumulator->length))
  7271.     {
  7272.       (accumulator->length) += 10;
  7273.       (accumulator->tokens) = (char **) xrealloc
  7274.     (accumulator->tokens, (accumulator->length * sizeof (char *)));
  7275.     }
  7276.   accumulator->tokens[accumulator->index] = token;
  7277.   accumulator->index += 1;
  7278. }
  7279.  
  7280. char *
  7281. copy_substring (start, end)
  7282.      char *start;
  7283.      char *end;
  7284. {
  7285.   char *result, *scan, *scan_result;
  7286.  
  7287.   result = (char *) xmalloc ((end - start) + 1);
  7288.   scan_result = result;
  7289.   scan = start;
  7290.  
  7291.   while (scan < end)
  7292.     *scan_result++ = *scan++;
  7293.  
  7294.   *scan_result = '\0';
  7295.   return (result);
  7296. }
  7297.  
  7298. /* Given `string' pointing at an open brace, skip forward and return a
  7299.    pointer to just past the matching close brace. */
  7300. int
  7301. scan_group_in_string (string_pointer)
  7302.      char **string_pointer;
  7303. {
  7304.   register int c;
  7305.   register char *scan_string;
  7306.   register unsigned int level = 1;
  7307.  
  7308.   scan_string = (*string_pointer) + 1;
  7309.  
  7310.   while (1)
  7311.     {
  7312.       if (level == 0)
  7313.     {
  7314.       (*string_pointer) = scan_string;
  7315.       return (1);
  7316.     }
  7317.       c = (*scan_string++);
  7318.       if (c == '\0')
  7319.     {
  7320.       /* Tweak line_number to compensate for fact that
  7321.          we gobbled the whole line before coming here. */
  7322.       line_number -= 1;
  7323.       line_error ("Missing `}' in %cdef arg", COMMAND_PREFIX);
  7324.       line_number += 1;
  7325.       (*string_pointer) = (scan_string - 1);
  7326.       return (0);
  7327.     }
  7328.       if (c == '{')
  7329.     level += 1;
  7330.       if (c == '}')
  7331.     level -= 1;
  7332.     }
  7333. }
  7334.  
  7335. /* Return a list of tokens from the contents of `string'.
  7336.    Commands and brace-delimited groups count as single tokens.
  7337.    Contiguous whitespace characters are converted to a token
  7338.    consisting of a single space. */
  7339. char **
  7340. args_from_string (string)
  7341.      char *string;
  7342. {
  7343.   struct token_accumulator accumulator;
  7344.   register char *scan_string = string;
  7345.   char *token_start, *token_end;
  7346.  
  7347.   initialize_token_accumulator (&accumulator);
  7348.  
  7349.   while ((*scan_string) != '\0')
  7350.     {
  7351.       /* Replace arbitrary whitespace by a single space. */
  7352.       if (whitespace (*scan_string))
  7353.     {
  7354.       scan_string += 1;
  7355.       while (whitespace (*scan_string))
  7356.         scan_string += 1;
  7357.       accumulate_token ((&accumulator), (strdup (" ")));
  7358.       continue;
  7359.     }
  7360.  
  7361.       /* Commands count as single tokens. */
  7362.       if ((*scan_string) == COMMAND_PREFIX)
  7363.     {
  7364.       token_start = scan_string;
  7365.       scan_string += 1;
  7366.       if (self_delimiting (*scan_string))
  7367.         scan_string += 1;
  7368.       else
  7369.         {
  7370.           register int c;
  7371.           while (1)
  7372.         {
  7373.           c = *scan_string++;
  7374.  
  7375.            if ((c == '\0') || (c == '{') || (whitespace (c)))
  7376.             {
  7377.               scan_string -= 1;
  7378.               break;
  7379.             }
  7380.         }
  7381.  
  7382.           if (*scan_string == '{')
  7383.         {
  7384.           char *s = scan_string;
  7385.           (void) scan_group_in_string (&s);
  7386.           scan_string = s;
  7387.         }
  7388.         }
  7389.       token_end = scan_string;
  7390.     }
  7391.  
  7392.       /* Parentheses and brackets are self-delimiting. */
  7393.       else if (DEFUN_SELF_DELIMITING (*scan_string))
  7394.     {
  7395.       token_start = scan_string;
  7396.       scan_string += 1;
  7397.       token_end = scan_string;
  7398.     }
  7399.  
  7400.       /* Open brace introduces a group that is a single token. */
  7401.       else if (*scan_string == '{')
  7402.     {
  7403.       char *s = scan_string;
  7404.       int balanced = scan_group_in_string (&s);
  7405.  
  7406.       token_start = scan_string + 1;
  7407.       scan_string = s;
  7408.       token_end = balanced ? (scan_string - 1) : scan_string;
  7409.     }
  7410.  
  7411.       /* Otherwise a token is delimited by whitespace, parentheses,
  7412.      brackets, or braces.  A token is also ended by a command. */
  7413.       else
  7414.     {
  7415.       token_start = scan_string;
  7416.  
  7417.       while (1)
  7418.         {
  7419.           register int c;
  7420.  
  7421.           c = *scan_string++;
  7422.  
  7423.           if (!c ||
  7424.           (whitespace (c) || DEFUN_SELF_DELIMITING (c) ||
  7425.            c == '{' || c == '}'))
  7426.         {
  7427.           scan_string--;
  7428.           break;
  7429.         }
  7430.  
  7431.           /* If we encounter a command imbedded within a token,
  7432.          then end the token. */
  7433.           if (c == COMMAND_PREFIX)
  7434.         {
  7435.           scan_string--;
  7436.           break;
  7437.         }
  7438.         }
  7439.       token_end = scan_string;
  7440.     }
  7441.  
  7442.       accumulate_token
  7443.     (&accumulator, copy_substring (token_start, token_end));
  7444.     }
  7445.   accumulate_token (&accumulator, NULL);
  7446.   return (accumulator.tokens);
  7447. }
  7448.  
  7449. void
  7450. process_defun_args (defun_args, auto_var_p)
  7451.      char **defun_args;
  7452.      int auto_var_p;
  7453. {
  7454.   int pending_space = 0;
  7455.  
  7456.   while (1)
  7457.     {
  7458.       char *defun_arg = *defun_args++;
  7459.  
  7460.       if (defun_arg == NULL)
  7461.     break;
  7462.  
  7463.       if (defun_arg[0] == ' ')
  7464.     {
  7465.       pending_space = 1;
  7466.       continue;
  7467.     }
  7468.  
  7469.       if (pending_space)
  7470.     {
  7471.       add_char (' ');
  7472.       pending_space = 0;
  7473.     }
  7474.  
  7475.       if (DEFUN_SELF_DELIMITING (defun_arg[0]))
  7476.     add_char (defun_arg[0]);
  7477.       else if (defun_arg[0] == '&')
  7478.     add_word (defun_arg);
  7479.       else if (defun_arg[0] == COMMAND_PREFIX)
  7480.     execute_string ("%s", defun_arg);
  7481.       else if (auto_var_p)
  7482.     execute_string ("%cvar{%s}", COMMAND_PREFIX, defun_arg);
  7483.       else
  7484.     add_word (defun_arg);
  7485.     }
  7486. }
  7487.  
  7488. char *
  7489. next_nonwhite_defun_arg (arg_pointer)
  7490.      char ***arg_pointer;
  7491. {
  7492.   char **scan = (*arg_pointer);
  7493.   char *arg = (*scan++);
  7494.  
  7495.   if ((arg != 0) && (*arg == ' '))
  7496.     arg = *scan++;
  7497.  
  7498.   if (arg == 0)
  7499.     scan -= 1;
  7500.  
  7501.   *arg_pointer = scan;
  7502.  
  7503.   return ((arg == 0) ? "" : arg);
  7504. }
  7505.  
  7506. /* Make the defun type insertion.
  7507.    TYPE says which insertion this is.
  7508.    X_P says not to start a new insertion if non-zero. */
  7509. void
  7510. defun_internal (type, x_p)
  7511.      enum insertion_type type;
  7512.      int x_p;
  7513. {
  7514.   enum insertion_type base_type;
  7515.   char **defun_args, **scan_args;
  7516.   char *category, *defined_name, *type_name, *type_name2;
  7517.  
  7518.   {
  7519.     char *line;
  7520.     get_rest_of_line (&line);
  7521.     defun_args = (args_from_string (line));
  7522.     free (line);
  7523.   }
  7524.  
  7525.   scan_args = defun_args;
  7526.  
  7527.   switch (type)
  7528.     {
  7529.     case defun:
  7530.       category = "Function";
  7531.       base_type = deffn;
  7532.       break;
  7533.     case defmac:
  7534.       category = "Macro";
  7535.       base_type = deffn;
  7536.       break;
  7537.     case defspec:
  7538.       category = "Special Form";
  7539.       base_type = deffn;
  7540.       break;
  7541.     case defvar:
  7542.       category = "Variable";
  7543.       base_type = defvr;
  7544.       break;
  7545.     case defopt:
  7546.       category = "User Option";
  7547.       base_type = defvr;
  7548.       break;
  7549.     case deftypefun:
  7550.       category = "Function";
  7551.       base_type = deftypefn;
  7552.       break;
  7553.     case deftypevar:
  7554.       category = "Variable";
  7555.       base_type = deftypevr;
  7556.       break;
  7557.     case defivar:
  7558.       category = "Instance Variable";
  7559.       base_type = defcv;
  7560.       break;
  7561.     case defmethod:
  7562.       category = "Method";
  7563.       base_type = defop;
  7564.       break;
  7565.     case deftypemethod:
  7566.       category = "Method";
  7567.       base_type = deftypemethod;
  7568.       break;
  7569.     default:
  7570.       category = next_nonwhite_defun_arg (&scan_args);
  7571.       base_type = type;
  7572.       break;
  7573.     }
  7574.  
  7575.   if ((base_type == deftypefn)
  7576.       || (base_type == deftypevr)
  7577.       || (base_type == defcv)
  7578.       || (base_type == defop)
  7579.       || (base_type == deftypemethod))
  7580.     type_name = next_nonwhite_defun_arg (&scan_args);
  7581.  
  7582.   if (base_type == deftypemethod)
  7583.     type_name2 = next_nonwhite_defun_arg (&scan_args);
  7584.  
  7585.   defined_name = next_nonwhite_defun_arg (&scan_args);
  7586.  
  7587.   /* This hack exists solely for the purposes of formatting the texinfo
  7588.      manual.  I couldn't think of a better way.  The token might be
  7589.      a simple @@ followed immediately by more text.  If this is the case,
  7590.      then the next defun arg is part of this one, and we should concatenate
  7591.      them. */
  7592.   if (*scan_args && **scan_args && !whitespace (**scan_args) &&
  7593.       (strcmp (defined_name, "@@") == 0))
  7594.     {
  7595.       char *tem = (char *)xmalloc (3 + strlen (scan_args[0]));
  7596.  
  7597.       sprintf (tem, "@@%s", scan_args[0]);
  7598.  
  7599.       free (scan_args[0]);
  7600.       scan_args[0] = tem;
  7601.       scan_args++;
  7602.       defined_name = tem;
  7603.     }
  7604.  
  7605.   if (!x_p)
  7606.     begin_insertion (type);
  7607.  
  7608.   /* Write the definition header line.
  7609.      This should start at the normal indentation.  */
  7610.   current_indent -= default_indentation_increment;
  7611.   start_paragraph ();
  7612.  
  7613.   switch (base_type)
  7614.     {
  7615.     case deffn:
  7616.     case defvr:
  7617.     case deftp:
  7618.       execute_string (" -- %s: %s", category, defined_name);
  7619.       break;
  7620.     case deftypefn:
  7621.     case deftypevr:
  7622.       execute_string (" -- %s: %s %s", category, type_name, defined_name);
  7623.       break;
  7624.     case defcv:
  7625.       execute_string (" -- %s of %s: %s", category, type_name, defined_name);
  7626.       break;
  7627.     case defop:
  7628.       execute_string (" -- %s on %s: %s", category, type_name, defined_name);
  7629.       break;
  7630.     case deftypemethod:
  7631.       execute_string (" -- %s on %s: %s %s", category, type_name, type_name2,
  7632.               defined_name);
  7633.       break;
  7634.     }
  7635.   current_indent += default_indentation_increment;
  7636.  
  7637.   /* Now process the function arguments, if any.
  7638.      If these carry onto the next line, they should be indented by two
  7639.      increments to distinguish them from the body of the definition,
  7640.      which is indented by one increment.  */
  7641.   current_indent += default_indentation_increment;
  7642.  
  7643.   switch (base_type)
  7644.     {
  7645.     case deffn:
  7646.     case defop:
  7647.       process_defun_args (scan_args, 1);
  7648.       break;
  7649.     case deftp:
  7650.     case deftypefn:
  7651.     case deftypemethod:
  7652.       process_defun_args (scan_args, 0);
  7653.       break;
  7654.     }
  7655.   current_indent -= default_indentation_increment;
  7656.   close_single_paragraph ();
  7657.  
  7658.   /* Make an entry in the appropriate index. */
  7659.   switch (base_type)
  7660.     {
  7661.     case deffn:
  7662.     case deftypefn:
  7663.       execute_string ("%cfindex %s\n", COMMAND_PREFIX, defined_name);
  7664.       break;
  7665.     case defvr:
  7666.     case deftypevr:
  7667.     case defcv:
  7668.       execute_string ("%cvindex %s\n", COMMAND_PREFIX, defined_name);
  7669.       break;
  7670.     case defop:
  7671.     case deftypemethod:
  7672.       execute_string ("%cfindex %s on %s\n",
  7673.               COMMAND_PREFIX, defined_name, type_name);
  7674.       break;
  7675.     case deftp:
  7676.       execute_string ("%ctindex %s\n", COMMAND_PREFIX, defined_name);
  7677.       break;
  7678.     }
  7679.  
  7680.   /* Deallocate the token list. */
  7681.   scan_args = defun_args;
  7682.   while (1)
  7683.     {
  7684.       char * arg = (*scan_args++);
  7685.       if (arg == NULL)
  7686.     break;
  7687.       free (arg);
  7688.     }
  7689.   free (defun_args);
  7690. }
  7691.  
  7692. /* Add an entry for a function, macro, special form, variable, or option.
  7693.    If the name of the calling command ends in `x', then this is an extra
  7694.    entry included in the body of an insertion of the same type. */
  7695. void
  7696. cm_defun ()
  7697. {
  7698.   int x_p;
  7699.   enum insertion_type type;
  7700.   char *temp = strdup (command);
  7701.  
  7702.   x_p = (command[strlen (command) - 1] == 'x');
  7703.  
  7704.   if (x_p)
  7705.     temp[strlen (temp) - 1] = '\0';
  7706.  
  7707.   type = find_type_from_name (temp);
  7708.   free (temp);
  7709.  
  7710.   /* If we are adding to an already existing insertion, then make sure
  7711.      that we are already in an insertion of type TYPE. */
  7712.   if (x_p &&
  7713.       (!insertion_level || insertion_stack->insertion != type))
  7714.     {
  7715.       line_error ("Must be in a `%s' insertion in order to use `%s'x",
  7716.           command, command);
  7717.       discard_until ("\n");
  7718.       return;
  7719.     }
  7720.  
  7721.   defun_internal (type, x_p);
  7722. }
  7723.  
  7724. /* End existing insertion block. */
  7725. void
  7726. cm_end ()
  7727. {
  7728.   char *temp;
  7729.   enum insertion_type type;
  7730.  
  7731.   if (!insertion_level)
  7732.     {
  7733.       line_error ("Unmatched `%c%s'", COMMAND_PREFIX, command);
  7734.       return;
  7735.     }
  7736.  
  7737.   get_rest_of_line (&temp);
  7738.   canon_white (temp);
  7739.  
  7740.   if (strlen (temp) == 0)
  7741.     line_error ("`%c%s' needs something after it", COMMAND_PREFIX, command);
  7742.  
  7743.   type = find_type_from_name (temp);
  7744.  
  7745.   if (type == bad_type)
  7746.     {
  7747.       line_error ("Bad argument to `%s', `%s', using `%s'",
  7748.        command, temp, insertion_type_pname (current_insertion_type ()));
  7749.     }
  7750.   end_insertion (type);
  7751.   free (temp);
  7752. }
  7753.  
  7754.  
  7755. /* **************************************************************** */
  7756. /*                                    */
  7757. /*            Other Random Commands                   */
  7758. /*                                    */
  7759. /* **************************************************************** */
  7760.  
  7761. /* This says to inhibit the indentation of the next paragraph, but
  7762.    not of following paragraphs.  */
  7763. void
  7764. cm_noindent ()
  7765. {
  7766.   if (!inhibit_paragraph_indentation)
  7767.     inhibit_paragraph_indentation = -1;
  7768. }
  7769.  
  7770. /* I don't know exactly what to do with this.  Should I allow
  7771.    someone to switch filenames in the middle of output?  Since the
  7772.    file could be partially written, this doesn't seem to make sense.
  7773.    Another option: ignore it, since they don't *really* want to
  7774.    switch files.  Finally, complain, or at least warn. */
  7775. void
  7776. cm_setfilename ()
  7777. {
  7778.   char *filename;
  7779.   get_rest_of_line (&filename);
  7780.   /* warning ("`@%s %s' encountered and ignored", command, filename); */
  7781.   free (filename);
  7782. }
  7783.  
  7784. void
  7785. cm_ignore_line ()
  7786. {
  7787.   discard_until ("\n");
  7788. }
  7789.  
  7790. /* @br can be immediately followed by `{}', so we have to read those here.
  7791.    It should simply close the paragraph. */
  7792. void
  7793. cm_br ()
  7794. {
  7795.   if (looking_at ("{}"))
  7796.     input_text_offset += 2;
  7797.  
  7798.   if (curchar () == '\n')
  7799.     {
  7800.       input_text_offset++;
  7801.       line_number++;
  7802.     }
  7803.  
  7804.   close_paragraph ();
  7805. }
  7806.  
  7807.  /* Insert the number of blank lines passed as argument. */
  7808. void
  7809. cm_sp ()
  7810. {
  7811.   int lines;
  7812.   char *line;
  7813.  
  7814.   get_rest_of_line (&line);
  7815.  
  7816.   if (sscanf (line, "%d", &lines) != 1)
  7817.     {
  7818.       line_error ("%csp requires a positive numeric argument", COMMAND_PREFIX);
  7819.     }
  7820.   else
  7821.     {
  7822.       if (lines < 0)
  7823.     lines = 0;
  7824.  
  7825.       while (lines--)
  7826.     add_char ('\n');
  7827.     }
  7828.   free (line);
  7829. }
  7830.  
  7831. /* Start a new line with just this text on it.
  7832.    Then center the line of text.
  7833.    This always ends the current paragraph. */
  7834. void
  7835. cm_center ()
  7836. {
  7837.   register int i, start, length;
  7838.   int fudge_factor = 1;
  7839.   unsigned char *line;
  7840.  
  7841.   close_paragraph ();
  7842.   filling_enabled = indented_fill = 0;
  7843.   cm_noindent ();
  7844.   start = output_paragraph_offset;
  7845.   inhibit_output_flushing ();
  7846.   get_rest_of_line ((char **)&line);
  7847.   execute_string ("%s", (char *)line);
  7848.   free (line);
  7849.   uninhibit_output_flushing ();
  7850.  
  7851.   i = output_paragraph_offset - 1;
  7852.   while (i > (start - 1) && output_paragraph[i] == '\n')
  7853.     i--;
  7854.  
  7855.   output_paragraph_offset = ++i;
  7856.   length = output_paragraph_offset - start;
  7857.  
  7858.   if (length < (fill_column - fudge_factor))
  7859.     {
  7860.       line = (unsigned char *)xmalloc (1 + length);
  7861.       memcpy (line, (char *)(output_paragraph + start), length);
  7862.  
  7863.       i = (fill_column - fudge_factor - length) / 2;
  7864.       output_paragraph_offset = start;
  7865.  
  7866.       while (i--)
  7867.     insert (' ');
  7868.  
  7869.       for (i = 0; i < length; i++)
  7870.     insert (line[i]);
  7871.  
  7872.       free (line);
  7873.     }
  7874.  
  7875.   insert ('\n');
  7876.   close_paragraph ();
  7877.   filling_enabled = 1;
  7878. }
  7879.  
  7880. /* Show what an expression returns. */
  7881. void
  7882. cm_result (arg)
  7883.      int arg;
  7884. {
  7885.   if (arg == END)
  7886.     add_word ("=>");
  7887. }
  7888.  
  7889. /* What an expression expands to. */
  7890. void
  7891. cm_expansion (arg)
  7892.      int arg;
  7893. {
  7894.   if (arg == END)
  7895.     add_word ("==>");
  7896. }
  7897.  
  7898. /* Indicates two expressions are equivalent. */
  7899. void
  7900. cm_equiv (arg)
  7901.      int arg;
  7902. {
  7903.   if (arg == END)
  7904.     add_word ("==");
  7905. }
  7906.  
  7907. /* What an expression may print. */
  7908. void
  7909. cm_print (arg)
  7910.      int arg;
  7911. {
  7912.   if (arg == END)
  7913.     add_word ("-|");
  7914. }
  7915.  
  7916. /* An error signaled. */
  7917. void
  7918. cm_error (arg)
  7919.      int arg;
  7920. {
  7921.   if (arg == END)
  7922.     add_word ("error-->");
  7923. }
  7924.  
  7925. /* The location of point in an example of a buffer. */
  7926. void
  7927. cm_point (arg)
  7928.      int arg;
  7929. {
  7930.   if (arg == END)
  7931.     add_word ("-!-");
  7932. }
  7933.  
  7934. /* Start a new line with just this text on it.
  7935.    The text is outdented one level if possible. */
  7936. void
  7937. cm_exdent ()
  7938. {
  7939.   char *line;
  7940.   int i = current_indent;
  7941.  
  7942.   if (current_indent)
  7943.     current_indent -= default_indentation_increment;
  7944.  
  7945.   get_rest_of_line (&line);
  7946.   close_single_paragraph ();
  7947.   execute_string ("%s", line);
  7948.   current_indent = i;
  7949.   free (line);
  7950.   close_single_paragraph ();
  7951. }
  7952.  
  7953. void
  7954. cm_include ()
  7955. {
  7956.   cm_infoinclude ();
  7957. }
  7958.  
  7959. #if !defined (HAVE_STRERROR)
  7960. extern char *sys_errlist[];
  7961. extern int sys_nerr;
  7962.  
  7963. char *
  7964. strerror (num)
  7965.      int num;
  7966. {
  7967.   if (num >= sys_nerr)
  7968.     return ("Unknown file system error");
  7969.   else
  7970.     return (sys_errlist[num]);
  7971. }
  7972. #endif /* !HAVE_STRERROR */
  7973.  
  7974. /* Remember this file, and move onto the next. */
  7975. void
  7976. cm_infoinclude ()
  7977. {
  7978.   char *filename;
  7979.  
  7980. #if defined (HAVE_MACROS)
  7981.   if (macro_expansion_output_stream)
  7982.     me_append_before_this_command ();
  7983. #endif /* HAVE_MACROS */
  7984.  
  7985.   close_paragraph ();
  7986.   get_rest_of_line (&filename);
  7987.  
  7988. #if defined (HAVE_MACROS)
  7989.   if (macro_expansion_output_stream)
  7990.     remember_itext (input_text, input_text_offset);
  7991. #endif /* HAVE_MACROS */
  7992.  
  7993.   pushfile ();
  7994.  
  7995.   /* In verbose mode we print info about including another file. */
  7996.   if (verbose_mode)
  7997.     {
  7998.       register int i = 0;
  7999.       register FSTACK *stack = filestack;
  8000.  
  8001.       for (i = 0, stack = filestack; stack; stack = stack->next, i++);
  8002.  
  8003.       i *= 2;
  8004.  
  8005.       printf ("%*s", i, "");
  8006.       printf ("%c%s %s\n", COMMAND_PREFIX, command, filename);
  8007.       fflush (stdout);
  8008.     }
  8009.  
  8010.   if (!find_and_load (filename))
  8011.     {
  8012.       extern int errno;
  8013.  
  8014.       popfile ();
  8015.       line_number--;
  8016.  
  8017.       /* Cannot "@include foo", in line 5 of "/wh/bar". */
  8018.       line_error ("`%c%s %s': %s", COMMAND_PREFIX, command, filename,
  8019.           strerror (errno));
  8020.  
  8021.       free (filename);
  8022.       return;
  8023.     }
  8024.   else
  8025.     {
  8026. #if defined (HAVE_MACROS)
  8027.       if (macro_expansion_output_stream)
  8028.     remember_itext (input_text, input_text_offset);
  8029. #endif /* HAVE_MACROS */
  8030.       reader_loop ();
  8031.     }
  8032.   free (filename);
  8033.   popfile ();
  8034. }
  8035.  
  8036. /* The other side of a malformed expression. */
  8037. void
  8038. misplaced_brace ()
  8039. {
  8040.   line_error ("Misplaced `}'");
  8041. }
  8042.  
  8043. /* Don't let the filling algorithm insert extra whitespace here. */
  8044. void
  8045. cm_force_abbreviated_whitespace ()
  8046. {
  8047. }
  8048.  
  8049. /* Do not let this character signify the end of a sentence, though
  8050.    if it was seen without the command prefix it normally would.  We
  8051.    do this by turning on the 8th bit of the character. */
  8052. void
  8053. cm_ignore_sentence_ender ()
  8054. {
  8055.   if (amiga_guide) add_char (META ((*command - 32)));
  8056.   else add_char (META ((*command)));
  8057. }
  8058.  
  8059. /* Signals end of processing.  Easy to make this happen. */
  8060. void
  8061. cm_bye ()
  8062. {
  8063.   input_text_offset = size_of_input_text;
  8064. }
  8065.  
  8066. void
  8067. cm_asis ()
  8068. {
  8069. }
  8070.  
  8071. void
  8072. cm_math ()
  8073. {
  8074. }
  8075.  
  8076.  
  8077. /* **************************************************************** */
  8078. /*                                    */
  8079. /*            Indexing Stuff                    */
  8080. /*                                    */
  8081. /* **************************************************************** */
  8082.  
  8083.  
  8084. /* An index element... */
  8085. typedef struct index_elt
  8086. {
  8087.   struct index_elt *next;
  8088.   char *entry;            /* The index entry itself. */
  8089.   char *node;            /* The node from whence it came. */
  8090.   int code;            /* Non-zero means add `@code{...}' when
  8091.                    printing this element. */
  8092.   int defining_line;        /* Line number where this entry was written. */
  8093. } INDEX_ELT;
  8094.  
  8095. /* A list of short-names for each index, and the index to that index in our
  8096.    index array, the_indices.  In addition, for each index, it is remembered
  8097.    whether that index is a code index or not.  Code indices have @code{}
  8098.    inserted around the first word when they are printed with printindex. */
  8099. typedef struct
  8100. {
  8101.   char *name;
  8102.   int index;
  8103.   int code;
  8104. } INDEX_ALIST;
  8105.  
  8106. INDEX_ALIST **name_index_alist = (INDEX_ALIST **) NULL;
  8107.  
  8108. /* An array of pointers.  Each one is for a different index.  The
  8109.    "synindex" command changes which array slot is pointed to by a
  8110.    given "index". */
  8111. INDEX_ELT **the_indices = (INDEX_ELT **) NULL;
  8112.  
  8113. /* The number of defined indices. */
  8114. int defined_indices = 0;
  8115.  
  8116. /* We predefine these. */
  8117. #define program_index 0
  8118. #define function_index 1
  8119. #define concept_index 2
  8120. #define variable_index 3
  8121. #define datatype_index 4
  8122. #define key_index 5
  8123.  
  8124. void
  8125. init_indices ()
  8126. {
  8127.   int i;
  8128.  
  8129.   /* Create the default data structures. */
  8130.  
  8131.   /* Initialize data space. */
  8132.   if (!the_indices)
  8133.     {
  8134.       the_indices = (INDEX_ELT **) xmalloc ((1 + defined_indices) *
  8135.                         sizeof (INDEX_ELT *));
  8136.       the_indices[defined_indices] = (INDEX_ELT *) NULL;
  8137.  
  8138.       name_index_alist = (INDEX_ALIST **) xmalloc ((1 + defined_indices) *
  8139.                            sizeof (INDEX_ALIST *));
  8140.       name_index_alist[defined_indices] = (INDEX_ALIST *) NULL;
  8141.     }
  8142.  
  8143.   /* If there were existing indices, get rid of them now. */
  8144.   for (i = 0; i < defined_indices; i++)
  8145.     undefindex (name_index_alist[i]->name);
  8146.  
  8147.   /* Add the default indices. */
  8148.   top_defindex ("pg", 0);
  8149.   top_defindex ("fn", 1);        /* "fn" is a code index.  */
  8150.   top_defindex ("cp", 0);
  8151.   top_defindex ("vr", 0);
  8152.   top_defindex ("tp", 0);
  8153.   top_defindex ("ky", 0);
  8154.  
  8155. }
  8156.  
  8157. /* Find which element in the known list of indices has this name.
  8158.    Returns -1 if NAME isn't found. */
  8159. int
  8160. find_index_offset (name)
  8161.      char *name;
  8162. {
  8163.   register int i;
  8164.   for (i = 0; i < defined_indices; i++)
  8165.     if (name_index_alist[i] &&
  8166.     strcmp (name, name_index_alist[i]->name) == 0)
  8167.       return (name_index_alist[i]->index);
  8168.   return (-1);
  8169. }
  8170.  
  8171. /* Return a pointer to the entry of (name . index) for this name.
  8172.    Return NULL if the index doesn't exist. */
  8173. INDEX_ALIST *
  8174. find_index (name)
  8175.      char *name;
  8176. {
  8177.   int offset = find_index_offset (name);
  8178.   if (offset > -1)
  8179.     return (name_index_alist[offset]);
  8180.   else
  8181.     return ((INDEX_ALIST *) NULL);
  8182. }
  8183.  
  8184. /* Given an index name, return the offset in the_indices of this index,
  8185.    or -1 if there is no such index. */
  8186. int
  8187. translate_index (name)
  8188.      char *name;
  8189. {
  8190.   INDEX_ALIST *which = find_index (name);
  8191.  
  8192.   if (which)
  8193.     return (which->index);
  8194.   else
  8195.     return (-1);
  8196. }
  8197.  
  8198. /* Return the index list which belongs to NAME. */
  8199. INDEX_ELT *
  8200. index_list (name)
  8201.      char *name;
  8202. {
  8203.   int which = translate_index (name);
  8204.   if (which < 0)
  8205.     return ((INDEX_ELT *) -1);
  8206.   else
  8207.     return (the_indices[which]);
  8208. }
  8209.  
  8210. /* Please release me, let me go... */
  8211. void
  8212. free_index (index)
  8213.      INDEX_ELT *index;
  8214. {
  8215.   INDEX_ELT *temp;
  8216.  
  8217.   while ((temp = index) != (INDEX_ELT *) NULL)
  8218.     {
  8219.       free (temp->entry);
  8220.       free (temp->node);
  8221.       index = index->next;
  8222.       free (temp);
  8223.     }
  8224. }
  8225.  
  8226. /* Flush an index by name. */
  8227. void
  8228. undefindex (name)
  8229.      char *name;
  8230. {
  8231.   int i;
  8232.   int which = find_index_offset (name);
  8233.  
  8234.   if (which < 0)
  8235.     return;
  8236.  
  8237.   i = name_index_alist[which]->index;
  8238.  
  8239.   free_index (the_indices[i]);
  8240.   the_indices[i] = (INDEX_ELT *) NULL;
  8241.  
  8242.   free (name_index_alist[which]->name);
  8243.   free (name_index_alist[which]);
  8244.   name_index_alist[which] = (INDEX_ALIST *) NULL;
  8245. }
  8246.  
  8247. /* Define an index known as NAME.  We assign the slot number.
  8248.    CODE if non-zero says to make this a code index. */
  8249. void
  8250. defindex (name, code)
  8251.      char *name;
  8252.      int code;
  8253. {
  8254.   register int i, slot;
  8255.  
  8256.   /* If it already exists, flush it. */
  8257.   undefindex (name);
  8258.  
  8259.   /* Try to find an empty slot. */
  8260.   slot = -1;
  8261.   for (i = 0; i < defined_indices; i++)
  8262.     if (!name_index_alist[i])
  8263.       {
  8264.     slot = i;
  8265.     break;
  8266.       }
  8267.  
  8268.   if (slot < 0)
  8269.     {
  8270.       /* No such luck.  Make space for another index. */
  8271.       slot = defined_indices;
  8272.       defined_indices++;
  8273.  
  8274.       name_index_alist = (INDEX_ALIST **)
  8275.     xrealloc ((char *)name_index_alist,
  8276.           (1 + defined_indices) * sizeof (INDEX_ALIST *));
  8277.       the_indices = (INDEX_ELT **)
  8278.     xrealloc ((char *)the_indices,
  8279.           (1 + defined_indices) * sizeof (INDEX_ELT *));
  8280.     }
  8281.  
  8282.   /* We have a slot.  Start assigning. */
  8283.   name_index_alist[slot] = (INDEX_ALIST *) xmalloc (sizeof (INDEX_ALIST));
  8284.   name_index_alist[slot]->name = strdup (name);
  8285.   name_index_alist[slot]->index = slot;
  8286.   name_index_alist[slot]->code = code;
  8287.  
  8288.   the_indices[slot] = (INDEX_ELT *) NULL;
  8289. }
  8290.  
  8291. /* Add the arguments to the current index command to the index NAME. */
  8292. void
  8293. index_add_arg (name)
  8294.      char *name;
  8295. {
  8296.   int which;
  8297.   char *index_entry;
  8298.   INDEX_ALIST *tem;
  8299.  
  8300.   tem = find_index (name);
  8301.  
  8302.   which = tem ? tem->index : -1;
  8303.  
  8304. #if defined (HAVE_MACROS)
  8305.   if (macro_expansion_output_stream)
  8306.     append_to_expansion_output (input_text_offset + 1);
  8307. #endif /* HAVE_MACROS */
  8308.  
  8309.   get_rest_of_line (&index_entry);
  8310.   ignore_blank_line ();
  8311.  
  8312. #if defined (HAVE_MACROS)
  8313.   if (macro_expansion_output_stream)
  8314.     {
  8315.       int op_orig;
  8316.  
  8317.       remember_itext (input_text, input_text_offset);
  8318.       op_orig = output_paragraph_offset;
  8319.       me_execute_string (index_entry);
  8320.       me_execute_string ("\n");
  8321.       output_paragraph_offset = op_orig;
  8322.     }
  8323. #endif /* HAVE_MACROS */
  8324.  
  8325.   if (which < 0)
  8326.     {
  8327.       line_error ("Unknown index reference `%s'", name);
  8328.       free (index_entry);
  8329.     }
  8330.   else
  8331.     {
  8332.       INDEX_ELT *new = (INDEX_ELT *) xmalloc (sizeof (INDEX_ELT));
  8333.       new->next = the_indices[which];
  8334.       new->entry = index_entry;
  8335.       new->node = current_node;
  8336.       new->code = tem->code;
  8337.       new->defining_line = line_number - 1;
  8338.       the_indices[which] = new;
  8339.     }
  8340. }
  8341.  
  8342. #define INDEX_COMMAND_SUFFIX "index"
  8343.  
  8344. /* The function which user defined index commands call. */
  8345. void
  8346. gen_index ()
  8347. {
  8348.   char *name = strdup (command);
  8349.   if (strlen (name) >= strlen ("index"))
  8350.     name[strlen (name) - strlen ("index")] = '\0';
  8351.   index_add_arg (name);
  8352.   free (name);
  8353. }
  8354.  
  8355. void
  8356. top_defindex (name, code)
  8357.      char *name;
  8358.      int code;
  8359. {
  8360.   char *temp;
  8361.  
  8362.   temp = (char *) xmalloc (1 + strlen (name) + strlen ("index"));
  8363.   sprintf (temp, "%sindex", name);
  8364.   define_user_command (temp, gen_index, 0);
  8365.   defindex (name, code);
  8366.   free (temp);
  8367. }
  8368.  
  8369. /* Define a new index command.  Arg is name of index. */
  8370. void
  8371. cm_defindex ()
  8372. {
  8373.   gen_defindex (0);
  8374. }
  8375.  
  8376. void
  8377. cm_defcodeindex ()
  8378. {
  8379.   gen_defindex (1);
  8380. }
  8381.  
  8382. void
  8383. gen_defindex (code)
  8384.      int code;
  8385. {
  8386.   char *name;
  8387.   get_rest_of_line (&name);
  8388.  
  8389.   if (find_index (name))
  8390.     {
  8391.       line_error ("Index `%s' already exists", name);
  8392.       free (name);
  8393.       return;
  8394.     }
  8395.   else
  8396.     {
  8397.       char *temp = (char *) alloca (1 + strlen (name) + strlen ("index"));
  8398.       sprintf (temp, "%sindex", name);
  8399.       define_user_command (temp, gen_index, 0);
  8400.       defindex (name, code);
  8401.       free (name);
  8402.     }
  8403. }
  8404.  
  8405. /* Append LIST2 to LIST1.  Return the head of the list. */
  8406. INDEX_ELT *
  8407. index_append (head, tail)
  8408.      INDEX_ELT *head, *tail;
  8409. {
  8410.   register INDEX_ELT *t_head = head;
  8411.  
  8412.   if (!t_head)
  8413.     return (tail);
  8414.  
  8415.   while (t_head->next)
  8416.     t_head = t_head->next;
  8417.   t_head->next = tail;
  8418.   return (head);
  8419. }
  8420.  
  8421. /* Expects 2 args, on the same line.  Both are index abbreviations.
  8422.    Make the first one be a synonym for the second one, i.e. make the
  8423.    first one have the same index as the second one. */
  8424. void
  8425. cm_synindex ()
  8426. {
  8427.   int redirector, redirectee;
  8428.   char *temp;
  8429.  
  8430.   skip_whitespace ();
  8431.   get_until_in_line (" ", &temp);
  8432.   redirectee = find_index_offset (temp);
  8433.   skip_whitespace ();
  8434.   free_and_clear (&temp);
  8435.   get_until_in_line (" ", &temp);
  8436.   redirector = find_index_offset (temp);
  8437.   free (temp);
  8438.   if (redirector < 0 || redirectee < 0)
  8439.     {
  8440.       line_error ("Unknown index reference");
  8441.     }
  8442.   else
  8443.     {
  8444.       /* I think that we should let the user make indices synonymous to
  8445.          each other without any lossage of info.  This means that one can
  8446.          say @synindex cp dt anywhere in the file, and things that used to
  8447.          be in cp will go into dt. */
  8448.       INDEX_ELT *i1 = the_indices[redirectee], *i2 = the_indices[redirector];
  8449.  
  8450.       if (i1 || i2)
  8451.     {
  8452.       if (i1)
  8453.         the_indices[redirectee] = index_append (i1, i2);
  8454.       else
  8455.         the_indices[redirectee] = index_append (i2, i1);
  8456.     }
  8457.  
  8458.       name_index_alist[redirectee]->index =
  8459.     name_index_alist[redirector]->index;
  8460.     }
  8461. }
  8462.  
  8463. void
  8464. cm_pindex ()            /* Pinhead index. */
  8465. {
  8466.   index_add_arg ("pg");
  8467. }
  8468.  
  8469. void
  8470. cm_vindex ()            /* Variable index. */
  8471. {
  8472.   index_add_arg ("vr");
  8473. }
  8474.  
  8475. void
  8476. cm_kindex ()            /* Key index. */
  8477. {
  8478.   index_add_arg ("ky");
  8479. }
  8480.  
  8481. void
  8482. cm_cindex ()            /* Concept index. */
  8483. {
  8484.   index_add_arg ("cp");
  8485. }
  8486.  
  8487. void
  8488. cm_findex ()            /* Function index. */
  8489. {
  8490.   index_add_arg ("fn");
  8491. }
  8492.  
  8493. void
  8494. cm_tindex ()            /* Data Type index. */
  8495. {
  8496.   index_add_arg ("tp");
  8497. }
  8498.  
  8499. /* Sorting the index. */
  8500. int
  8501. index_element_compare (element1, element2)
  8502.      INDEX_ELT **element1, **element2;
  8503. {
  8504.   /* This needs to ignore leading non-text characters. */
  8505.   return (strcasecmp ((*element1)->entry, (*element2)->entry));
  8506. }
  8507.  
  8508. /* Force all index entries to be unique. */
  8509. void
  8510. make_index_entries_unique (array, count)
  8511.      INDEX_ELT **array;
  8512.      int count;
  8513. {
  8514.   register int i, j;
  8515.   INDEX_ELT **copy;
  8516.   int counter = 1;
  8517.  
  8518.   copy = (INDEX_ELT **)xmalloc ((1 + count) * sizeof (INDEX_ELT *));
  8519.  
  8520.   for (i = 0, j = 0; i < count; i++)
  8521.     {
  8522.       if ((i == (count - 1)) ||
  8523.       (array[i]->node != array[i + 1]->node) ||
  8524.       (strcasecmp (array[i]->entry, array[i + 1]->entry) != 0))
  8525.     copy[j++] = array[i];
  8526.       else
  8527.     {
  8528.       free (array[i]->entry);
  8529.       free (array[i]);
  8530.     }
  8531.     }
  8532.   copy[j] = (INDEX_ELT *)NULL;
  8533.  
  8534.   /* Now COPY contains only unique entries.  Duplicated entries in the
  8535.      original array have been freed.  Replace the current array with
  8536.      the copy, fixing the NEXT pointers. */
  8537.   for (i = 0; copy[i] != (INDEX_ELT *)NULL; i++)
  8538.     {
  8539.  
  8540.       copy[i]->next = copy[i + 1];
  8541.  
  8542.       /* Fix entry names which are the same.  They point to different nodes,
  8543.      so we make the entry name unique. */
  8544.       if ((copy[i + 1] != (INDEX_ELT *)NULL) &&
  8545.       (strcmp (copy[i]->entry, copy[i + 1]->entry) == 0))
  8546.     {
  8547.       char *new_entry_name;
  8548.  
  8549.       new_entry_name = (char *)xmalloc (10 + strlen (copy[i]->entry));
  8550.       sprintf (new_entry_name, "%s <%d>", copy[i]->entry, counter);
  8551.       free (copy[i]->entry);
  8552.       copy[i]->entry = new_entry_name;
  8553.       counter++;
  8554.     }
  8555.       else
  8556.     counter = 1;
  8557.  
  8558.       array[i] = copy[i];
  8559.     }
  8560.   array[i] = (INDEX_ELT *)NULL;
  8561.  
  8562.   /* Free the storage used only by COPY. */
  8563.   free (copy);
  8564. }
  8565.  
  8566. /* Sort the index passed in INDEX, returning an array of
  8567.    pointers to elements.  The array is terminated with a NULL
  8568.    pointer.  We call qsort because it's supposed to be fast.
  8569.    I think this looks bad. */
  8570. INDEX_ELT **
  8571. sort_index (index)
  8572.      INDEX_ELT *index;
  8573. {
  8574.   INDEX_ELT *temp = index;
  8575.   INDEX_ELT **array;
  8576.   int count = 0;
  8577.  
  8578.   while (temp != (INDEX_ELT *) NULL)
  8579.     {
  8580.       count++;
  8581.       temp = temp->next;
  8582.     }
  8583.  
  8584.   /* We have the length.  Make an array. */
  8585.  
  8586.   array = (INDEX_ELT **) xmalloc ((count + 1) * sizeof (INDEX_ELT *));
  8587.   count = 0;
  8588.   temp = index;
  8589.  
  8590.   while (temp != (INDEX_ELT *) NULL)
  8591.     {
  8592.       array[count++] = temp;
  8593.       temp = temp->next;
  8594.     }
  8595.   array[count] = (INDEX_ELT *) NULL;    /* terminate the array. */
  8596.  
  8597.   /* Sort the array. */
  8598.   qsort (array, count, sizeof (INDEX_ELT *), index_element_compare);
  8599.   make_index_entries_unique (array, count);
  8600.   return (array);
  8601. }
  8602.  
  8603. /* Non-zero means that we are in the middle of printing an index. */
  8604. int printing_index = 0;
  8605.  
  8606. /* Takes one arg, a short name of an index to print.
  8607.    Outputs a menu of the sorted elements of the index. */
  8608. void
  8609. cm_printindex ()
  8610. {
  8611.   int item;
  8612.   INDEX_ELT *index;
  8613.   INDEX_ELT **array;
  8614.   char *index_name;
  8615.   int old_inhibitions = inhibit_paragraph_indentation;
  8616.   int previous_filling_enabled_value = filling_enabled;
  8617.  
  8618.   close_paragraph ();
  8619.   get_rest_of_line (&index_name);
  8620.  
  8621.   index = index_list (index_name);
  8622.   if (index == (INDEX_ELT *)-1)
  8623.     {
  8624.       line_error ("Unknown index name `%s'", index_name);
  8625.       free (index_name);
  8626.       return;
  8627.     }
  8628.   else
  8629.     {
  8630.       if (amiga_guide && !no_headers && (strcmp (index_name, "cp") == 0))
  8631.         {
  8632.           inhibit_paragraph_indentation = 1;
  8633.           add_word_args ("\n@Index \"%s\"", current_node);
  8634.           inhibit_paragraph_indentation = old_inhibitions;
  8635.         }
  8636.       free (index_name);
  8637.     }
  8638.  
  8639.   array = sort_index (index);
  8640.  
  8641.   filling_enabled = 0;
  8642.   inhibit_paragraph_indentation = 1;
  8643.   close_paragraph ();
  8644.  
  8645.   if (amiga_guide)
  8646.     {
  8647.       add_word ("\x80");
  8648.       output_paragraph_offset--;
  8649.       add_word ("\n\n");
  8650.     }
  8651.   else
  8652.     add_word ("* Menu:\n\n");
  8653.  
  8654.   printing_index = 1;
  8655.  
  8656. #if defined (HAVE_MACROS)
  8657.   me_inhibit_expansion++;
  8658. #endif /* HAVE_MACROS */
  8659.  
  8660.   for (item = 0; (index = array[item]); item++)
  8661.     {
  8662.       int real_line_number = line_number;
  8663.  
  8664.       /* Let errors generated while making the index entry point back
  8665.      at the line which contains the entry. */
  8666.       line_number = index->defining_line;
  8667.  
  8668.       if (amiga_guide)
  8669.         {
  8670.           /* If this particular entry should be printed as a "code" index,
  8671.              then wrap the entry with "@code{...}". */
  8672.  
  8673.           if (no_headers)
  8674.             {
  8675.               if (index->code)
  8676.                 {
  8677.                   execute_string (" `%s'", index->entry);
  8678.                 }
  8679.               else
  8680.                 {
  8681.                   execute_string (" %s", index->entry);
  8682.                 }
  8683.             }
  8684.           else
  8685.             {
  8686.               in_amiga_guide_button = 1;
  8687.               if (index->code)
  8688.                 {
  8689.                   char * spaces;
  8690.                   
  8691.                   spaces = amiga_button_text_length (index->entry, amiga_index_button_length);
  8692.                      
  8693.                   output_column -= (amiga_guide_hidden_chars = strlen("@{\"\" Link \"\"}") +
  8694.                                     strlen(index->node));
  8695.                   execute_string ("@w{ @@@{\" @code{%s}%s \" Link \"%s\"@} }", index->entry, spaces, index->node);
  8696.                   free(spaces);
  8697.                 }
  8698.               else
  8699.                 {
  8700.                   char * spaces;
  8701.                   
  8702.                   spaces = amiga_button_text_length (index->entry, amiga_index_button_length);
  8703.  
  8704.                   output_column -= (amiga_guide_hidden_chars = strlen("@{\"\" Link \"\"}") +
  8705.                                     strlen(index->node));
  8706.                   execute_string ("@w{ @@@{\" %s%s \" Link \"%s\"@} }", index->entry, spaces, index->node);
  8707.                   free(spaces);
  8708.                 }
  8709.               in_amiga_guide_button = 0;
  8710.             }
  8711.  
  8712.           execute_string ("  %s\n", index->node);
  8713.         }
  8714.       else
  8715.         {
  8716.           /* If this particular entry should be printed as a "code" index,
  8717.          then wrap the entry with "@code{...}". */
  8718.           if (index->code)
  8719.         execute_string ("* %ccode{%s}: ", COMMAND_PREFIX, index->entry);
  8720.           else
  8721.         execute_string ("* %s: ", index->entry);
  8722.  
  8723.           /* Pad the front of the destination nodename so that
  8724.          the output looks nice. */
  8725.           if (fill_column > 40 && output_column < 40)
  8726.         indent (40 - output_column);
  8727.  
  8728.           execute_string ("%s.\n", index->node);
  8729.         }
  8730.  
  8731.       line_number = real_line_number;
  8732.       flush_output ();
  8733.     }
  8734.  
  8735. #if defined (HAVE_MACROS)
  8736.   me_inhibit_expansion--;
  8737. #endif /* HAVE_MACROS */
  8738.  
  8739.   printing_index = 0;
  8740.   free (array);
  8741.   close_single_paragraph ();
  8742.   filling_enabled = previous_filling_enabled_value;
  8743.   inhibit_paragraph_indentation = old_inhibitions;
  8744. }
  8745.  
  8746.  
  8747. /* **************************************************************** */
  8748. /*                                    */
  8749. /*            Making User Defined Commands            */
  8750. /*                                    */
  8751. /* **************************************************************** */
  8752.  
  8753. void
  8754. define_user_command (name, proc, needs_braces_p)
  8755.      char *name;
  8756.      COMMAND_FUNCTION *proc;
  8757.      int needs_braces_p;
  8758. {
  8759.   int slot = user_command_array_len;
  8760.   user_command_array_len++;
  8761.  
  8762.   if (!user_command_array)
  8763.     user_command_array = (COMMAND **) xmalloc (1 * sizeof (COMMAND *));
  8764.  
  8765.   user_command_array = (COMMAND **) xrealloc (user_command_array,
  8766.                           (1 + user_command_array_len) *
  8767.                           sizeof (COMMAND *));
  8768.  
  8769.   user_command_array[slot] = (COMMAND *) xmalloc (sizeof (COMMAND));
  8770.   user_command_array[slot]->name = strdup (name);
  8771.   user_command_array[slot]->proc = proc;
  8772.   user_command_array[slot]->argument_in_braces = needs_braces_p;
  8773. }
  8774.  
  8775. /* Make ALIAS run the named FUNCTION.  Copies properties from FUNCTION. */
  8776. void
  8777. define_alias (alias, function)
  8778.      char *alias, *function;
  8779. {
  8780. }
  8781.  
  8782. /* Set the paragraph indentation variable to the value specified in STRING.
  8783.    Values can be:
  8784.    `asis': Don't change existing indentation.
  8785.    `none': Remove existing indentation.
  8786.       NUM: Indent NUM spaces at the starts of paragraphs.
  8787.            Note that if NUM is zero, we assume `none'.
  8788.  
  8789.    Returns 0 if successful, or non-zero if STRING isn't one of the above. */
  8790. int
  8791. set_paragraph_indent (string)
  8792.      char *string;
  8793. {
  8794.   if (strcmp (string, "asis") == 0)
  8795.     paragraph_start_indent = 0;
  8796.   else if (strcmp (string, "none") == 0)
  8797.     paragraph_start_indent = -1;
  8798.   else
  8799.     {
  8800.       if (sscanf (string, "%d", ¶graph_start_indent) != 1)
  8801.     return (-1);
  8802.       else
  8803.     {
  8804.       if (paragraph_start_indent == 0)
  8805.         paragraph_start_indent = -1;
  8806.     }
  8807.     }
  8808.   return (0);
  8809. }
  8810.  
  8811. void
  8812. cm_paragraphindent ()
  8813. {
  8814.   char *arg;
  8815.  
  8816.   get_rest_of_line (&arg);
  8817.   if (set_paragraph_indent (arg) != 0)
  8818.     line_error ("Bad argument to %c%s", COMMAND_PREFIX, command);
  8819.  
  8820.   free (arg);
  8821. }
  8822.  
  8823. /* Some support for footnotes. */
  8824.  
  8825. /* Footnotes are a new construct in Info.  We don't know the best method
  8826.    of implementing them for sure, so we present two possiblities.
  8827.  
  8828.    SeparateNode:
  8829.     Make them look like followed references, with the reference
  8830.     destinations in a makeinfo manufactured node or,
  8831.  
  8832.    EndNode:
  8833.     Make them appear at the bottom of the node that they originally
  8834.     appeared in. */
  8835. #define SeparateNode 0
  8836. #define EndNode 1
  8837.  
  8838. int footnote_style = EndNode;
  8839. int first_footnote_this_node = 1;
  8840. int footnote_count = 0;
  8841. int footnote_style_changed = 0;
  8842.  
  8843. /* Set the footnote style based on he style identifier in STRING. */
  8844. int
  8845. set_footnote_style (string)
  8846.      char *string;
  8847. {
  8848.   if (!footnote_style_changed) /* BUG Fix: command line style has 
  8849.                                   higher priority -kdp */
  8850.   {
  8851.   if ((strcasecmp (string, "separate") == 0) ||
  8852.       (strcasecmp (string, "MN") == 0))
  8853.     footnote_style = SeparateNode;
  8854.   else if ((strcasecmp (string, "end") == 0) ||
  8855.        (strcasecmp (string, "EN") == 0))
  8856.     footnote_style = EndNode;
  8857.   else
  8858.     return (-1);
  8859.   }
  8860.   footnote_style_changed = 1;
  8861.  
  8862.  return (0);
  8863. }
  8864.  
  8865. void
  8866. cm_footnotestyle ()
  8867. {
  8868.   char *arg;
  8869.  
  8870.   get_rest_of_line (&arg);
  8871.  
  8872.   if (set_footnote_style (arg) != 0)
  8873.     line_error ("Bad argument to %c%s", COMMAND_PREFIX, command);
  8874.  
  8875.   free (arg);
  8876. }
  8877.  
  8878. typedef struct fn
  8879. {
  8880.   struct fn *next;
  8881.   char *marker;
  8882.   char *note;
  8883. }  FN;
  8884.  
  8885. FN *pending_notes = (FN *) NULL;
  8886.  
  8887. /* A method for remembering footnotes.  Note that this list gets output
  8888.    at the end of the current node. */
  8889. void
  8890. remember_note (marker, note)
  8891.      char *marker, *note;
  8892. {
  8893.   FN *temp = (FN *) xmalloc (sizeof (FN));
  8894.  
  8895.   temp->marker = strdup (marker);
  8896.   temp->note = strdup (note);
  8897.   temp->next = pending_notes;
  8898.   pending_notes = temp;
  8899.   footnote_count++;
  8900. }
  8901.  
  8902. /* How to get rid of existing footnotes. */
  8903. void
  8904. free_pending_notes ()
  8905. {
  8906.   FN *temp;
  8907.  
  8908.   while ((temp = pending_notes) != (FN *) NULL)
  8909.     {
  8910.       free (temp->marker);
  8911.       free (temp->note);
  8912.       pending_notes = pending_notes->next;
  8913.       free (temp);
  8914.     }
  8915.   first_footnote_this_node = 1;
  8916.   footnote_count = 0;
  8917. }
  8918.  
  8919. /* What to do when you see a @footnote construct. */
  8920.  
  8921.  /* Handle a "footnote".
  8922.     footnote *{this is a footnote}
  8923.     where "*" is the marker character for this note. */
  8924. void
  8925. cm_footnote ()
  8926. {
  8927.   char *marker;
  8928.   char *note;
  8929.  
  8930.   get_until ("{", &marker);
  8931.   canon_white (marker);
  8932.  
  8933.   /* Read the argument in braces. */
  8934.   if (curchar () != '{')
  8935.     {
  8936.       line_error ("`%c%s' expected more than just `%s'.  It needs something in `{...}'",
  8937.           COMMAND_PREFIX, command, marker);
  8938.       free (marker);
  8939.       return;
  8940.     }
  8941.   else
  8942.     {
  8943.       int braces = 1;
  8944.       int temp = ++input_text_offset;
  8945.       int len;
  8946.  
  8947.       while (braces)
  8948.     {
  8949.       if (temp == size_of_input_text)
  8950.         {
  8951.           line_error ("No closing brace for footnote `%s'", marker);
  8952.           return;
  8953.         }
  8954.  
  8955.       if (input_text[temp] == '{')
  8956.         braces++;
  8957.       else if (input_text[temp] == '}')
  8958.         braces--;
  8959.       else if (input_text[temp] == '\n')
  8960.         line_number ++;
  8961.  
  8962.       temp++;
  8963.     }
  8964.  
  8965.       len = (temp - input_text_offset) - 1;
  8966.       note = (char *)xmalloc (len + 1);
  8967.       strncpy (note, &input_text[input_text_offset], len);
  8968.       note[len] = '\0';
  8969.       input_text_offset = temp;
  8970.     }
  8971.  
  8972.   if (!current_node || !*current_node)
  8973.     {
  8974.       line_error ("Footnote defined without parent node");
  8975.       free (marker);
  8976.       free (note);
  8977.       return;
  8978.     }
  8979.  
  8980.   if (!*marker)
  8981.     {
  8982.       free (marker);
  8983.  
  8984.       if (number_footnotes)
  8985.     {
  8986.       marker = (char *)xmalloc (10);
  8987.       sprintf (marker, "%d", current_footnote_number);
  8988.       current_footnote_number++;
  8989.     }
  8990.       else
  8991.     marker = strdup ("*");
  8992.     }
  8993.  
  8994.   remember_note (marker, note);
  8995.  
  8996.   /* Your method should at least insert MARKER. */
  8997.   switch (footnote_style)
  8998.     {
  8999.     case SeparateNode:
  9000.       add_word_args ("(%s)", marker);
  9001.       if (first_footnote_this_node)
  9002.     {
  9003.       char *temp_string;
  9004.  
  9005.       temp_string = (char *)
  9006.         xmalloc ((strlen (current_node)) + (strlen ("-Footnotes")) + 1);
  9007.  
  9008.           if (amiga_guide)
  9009.             {
  9010.               if (no_headers)
  9011.                 {
  9012.                    char *Footnotestring;
  9013.  
  9014.                    if ((Footnotestring = set_p ("Footnotestring")))
  9015.                      execute_string ("%s-%s", Footnotestring, current_node);
  9016.                    else
  9017.                     execute_string ("%s-Footnotes", current_node);
  9018.                 }
  9019.               else
  9020.                 {
  9021.                    char *Footnotestring;
  9022.  
  9023.                    in_amiga_guide_button = 1;
  9024.                    output_column -= (amiga_guide_hidden_chars = strlen("@{\"\" Link \"-Footnotes\"}")
  9025.                                     +strlen(current_node));
  9026.                    if ((Footnotestring = set_p ("Footnotestring")))
  9027.                      execute_string ("@w{@@@{\"%s-%s\" Link \"%s-%s\"@}}", current_node,
  9028.                                      Footnotestring, current_node, Footnotestring);
  9029.                    else
  9030.                      execute_string ("@w{@@@{\"%s-Footnotes\" Link \"%s-Footnotes\"@}}", current_node, current_node);
  9031.                   in_amiga_guide_button = 0;
  9032.                 }
  9033.             }
  9034.           else
  9035.             {
  9036.           add_word_args (" (*note %s-Footnotes::)", current_node);
  9037.             }
  9038.                         
  9039.       strcpy (temp_string, current_node);
  9040.       strcat (temp_string, "-Footnotes");
  9041.       
  9042.           if (amiga_guide) normalize_node_name(temp_string);
  9043.           
  9044.       remember_node_reference (temp_string, line_number, followed_reference);
  9045.       free (temp_string);
  9046.       first_footnote_this_node = 0;
  9047.     }
  9048.       break;
  9049.  
  9050.     case EndNode:
  9051.       add_word_args ("(%s)", marker);
  9052.       break;
  9053.  
  9054.     default:
  9055.       break;
  9056.     }
  9057.   free (marker);
  9058.   free (note);
  9059. }
  9060.  
  9061. /* Non-zero means that we are currently in the process of outputting
  9062.    footnotes. */
  9063. int already_outputting_pending_notes = 0;
  9064.  
  9065. /* Output the footnotes.  We are at the end of the current node. */
  9066. void
  9067. output_pending_notes ()
  9068. {
  9069.   FN *footnote = pending_notes;
  9070.   char *Footnotestring; 
  9071.  
  9072.   if (!pending_notes)
  9073.     return;
  9074.  
  9075.   switch (footnote_style)
  9076.     {
  9077.  
  9078.     case SeparateNode:
  9079.       {
  9080.     char *old_current_node = current_node;
  9081.     char *old_command = strdup (command);
  9082.  
  9083.     already_outputting_pending_notes++;
  9084.         if (amiga_guide && (Footnotestring = set_p ("Footnotestring")))
  9085.           execute_string ("@node %s-%s,,,%s\n", current_node,
  9086.                           Footnotestring, current_node);
  9087.         else
  9088.       execute_string ("%cnode %s-Footnotes,,,%s\n",
  9089.                 COMMAND_PREFIX, current_node, current_node);
  9090.     already_outputting_pending_notes--;
  9091.     current_node = old_current_node;
  9092.     free (command);
  9093.     command = old_command;
  9094.       }
  9095.       break;
  9096.  
  9097.     case EndNode:
  9098.       close_paragraph ();
  9099.       in_fixed_width_font++;
  9100.       if (amiga_guide && (Footnotestring = set_p ("Footnotestring")))
  9101.         execute_string ("---------- %s ----------\n\n", Footnotestring);
  9102.       else
  9103.         execute_string ("---------- Footnotes ----------\n\n");
  9104.       in_fixed_width_font--;
  9105.       break;
  9106.     }
  9107.  
  9108.   /* Handle the footnotes in reverse order. */
  9109.   {
  9110.     FN **array = (FN **) xmalloc ((footnote_count + 1) * sizeof (FN *));
  9111.  
  9112.     array[footnote_count] = (FN *) NULL;
  9113.  
  9114.     while (--footnote_count > -1)
  9115.       {
  9116.     array[footnote_count] = footnote;
  9117.     footnote = footnote->next;
  9118.       }
  9119.  
  9120.     filling_enabled = 1;
  9121.     indented_fill = 1;
  9122.  
  9123.     while (footnote = array[++footnote_count])
  9124.       {
  9125.  
  9126.     switch (footnote_style)
  9127.       {
  9128.       case SeparateNode:
  9129.       case EndNode:
  9130.         execute_string ("(%s)  %s", footnote->marker, footnote->note);
  9131.         close_paragraph ();
  9132.         break;
  9133.       }
  9134.       }
  9135.     close_paragraph ();
  9136.     free (array);
  9137.   }
  9138. }
  9139.  
  9140.  
  9141. /* **************************************************************** */
  9142. /*                                                                  */
  9143. /*              User definable Macros (text substitution)        */
  9144. /*                                                                  */
  9145. /* **************************************************************** */
  9146.  
  9147. #if defined (HAVE_MACROS)
  9148.  
  9149. /* Array of macros and definitions. */
  9150. MACRO_DEF **macro_list = (MACRO_DEF **)NULL;
  9151.  
  9152. int macro_list_len = 0;        /* Number of elements. */
  9153. int macro_list_size = 0;    /* Number of slots in total. */
  9154.  
  9155. /* Return the macro definition of NAME or NULL if NAME is not defined. */
  9156. MACRO_DEF *
  9157. find_macro (name)
  9158.      char *name;
  9159. {
  9160.   register int i;
  9161.   register MACRO_DEF *def;
  9162.  
  9163.   def = (MACRO_DEF *)NULL;
  9164.   for (i = 0; macro_list && (def = macro_list[i]); i++)
  9165.     {
  9166.       if ((!def->inhibited) && (strcmp (def->name, name) == 0))
  9167.     break;
  9168.     }
  9169.   return (def);
  9170. }
  9171.  
  9172. /* Add the macro NAME with ARGLIST and BODY to the list of defined macros.
  9173.    SOURCE_FILE is the name of the file where this definition can be found,
  9174.    and SOURCE_LINENO is the line number within that file.  If a macro already
  9175.    exists with NAME, then a warning is produced, and that previous
  9176.    definition is overwritten. */
  9177. void
  9178. add_macro (name, arglist, body, source_file, source_lineno, flags)
  9179.      char *name;
  9180.      char **arglist;
  9181.      char *body;
  9182.      char *source_file;
  9183.      int source_lineno, flags;
  9184. {
  9185.   register MACRO_DEF *def;
  9186.  
  9187.   def = find_macro (name);
  9188.  
  9189.   if (!def)
  9190.     {
  9191.       if (macro_list_len + 2 >= macro_list_size)
  9192.     macro_list = (MACRO_DEF **)xrealloc
  9193.       (macro_list, ((macro_list_size += 10) * sizeof (MACRO_DEF *)));
  9194.  
  9195.       macro_list[macro_list_len] = (MACRO_DEF *)xmalloc (sizeof (MACRO_DEF));
  9196.       macro_list[macro_list_len + 1] = (MACRO_DEF *)NULL;
  9197.  
  9198.       def = macro_list[macro_list_len];
  9199.       macro_list_len += 1;
  9200.       def->name = name;
  9201.     }
  9202.   else
  9203.     {
  9204.       char *temp_filename = input_filename;
  9205.       int temp_line = line_number;
  9206.  
  9207.       warning ("The macro `%s' is previously defined", name);
  9208.  
  9209.       input_filename = def->source_file;
  9210.       line_number = def->source_lineno;
  9211.  
  9212.       warning ("Here is the previous definition of `%s'", name);
  9213.  
  9214.       input_filename = temp_filename;
  9215.       line_number = temp_line;
  9216.  
  9217.       if (def->arglist)
  9218.     {
  9219.       register int i;
  9220.  
  9221.       for (i = 0; def->arglist[i]; i++)
  9222.         free (def->arglist[i]);
  9223.  
  9224.       free (def->arglist);
  9225.     }
  9226.       free (def->source_file);
  9227.       free (def->body);
  9228.     }
  9229.  
  9230.   def->source_file = strdup (source_file);
  9231.   def->source_lineno = source_lineno;
  9232.   def->body = body;
  9233.   def->arglist = arglist;
  9234.   def->inhibited = 0;
  9235.   def->flags = flags;
  9236. }
  9237.  
  9238. /* Delete the macro with name NAME.  The macro is deleted from the list,
  9239.    but it is also returned.  If there was no macro defined, NULL is
  9240.    returned. */
  9241. MACRO_DEF *
  9242. delete_macro (name)
  9243.      char *name;
  9244. {
  9245.   register int i;
  9246.   register MACRO_DEF *def;
  9247.  
  9248.   def = (MACRO_DEF *)NULL;
  9249.  
  9250.   for (i = 0; macro_list && (def = macro_list[i]); i++)
  9251.     if (strcmp (def->name, name) == 0)
  9252.       {
  9253.     memmove (macro_list + i, macro_list + i + 1,
  9254.            ((macro_list_len + 1) - i) * sizeof (MACRO_DEF *));
  9255.     break;
  9256.       }
  9257.   return (def);
  9258. }
  9259.  
  9260. /* Return the arglist on the current line.  This can behave in two different
  9261.    ways, depending on the variable BRACES_REQUIRED_FOR_MACRO_ARGS. */
  9262. int braces_required_for_macro_args = 0;
  9263.  
  9264. char **
  9265. get_macro_args (def)
  9266.      MACRO_DEF *def;
  9267. {
  9268.   register int i;
  9269.   char *word;
  9270.  
  9271.   /* Quickly check to see if this macro has been invoked with any arguments.
  9272.      If not, then don't skip any of the following whitespace. */
  9273.   for (i = input_text_offset; i < size_of_input_text; i++)
  9274.     if (!cr_or_whitespace (input_text[i]))
  9275.       break;
  9276.  
  9277.   if (input_text[i] != '{')
  9278.     {
  9279.       if (braces_required_for_macro_args)
  9280.     {
  9281.       return ((char **)NULL);
  9282.     }
  9283.       else
  9284.     {
  9285.       /* Braces are not required to fill out the macro arguments.  If
  9286.          this macro takes one argument, it is considered to be the
  9287.          remainder of the line, sans whitespace. */
  9288.       if (def->arglist && def->arglist[0] && !def->arglist[1])
  9289.         {
  9290.           char **arglist;
  9291.  
  9292.           get_rest_of_line (&word);
  9293.           if (input_text[input_text_offset - 1] == '\n')
  9294.         input_text_offset--;
  9295.           /* canon_white (word); */
  9296.           arglist = (char **)xmalloc (2 * sizeof (char *));
  9297.           arglist[0] = word;
  9298.           arglist[1] = (char *)NULL;
  9299.           return (arglist);
  9300.         }
  9301.       else
  9302.         {
  9303.           /* The macro either took no arguments, or took more than
  9304.          one argument.  In that case, it must be invoked with
  9305.          arguments surrounded by braces. */
  9306.           return ((char **)NULL);
  9307.         }
  9308.     }
  9309.     }
  9310.   return (get_brace_args (def->flags & ME_QUOTE_ARG));
  9311. }
  9312.  
  9313. /* Substitute actual parameters for named parameters in body.
  9314.    The named parameters which appear in BODY must by surrounded
  9315.    reverse slashes, as in \foo\. */
  9316. char *
  9317. apply (named, actuals, body)
  9318.      char **named, **actuals, *body;
  9319. {
  9320.   register int i;
  9321.   int new_body_index, new_body_size;
  9322.   char *new_body, *text;
  9323.   int length_of_actuals;
  9324.  
  9325.   length_of_actuals = array_len (actuals);
  9326.   new_body_size = strlen (body);
  9327.   new_body = (char *)xmalloc (1 + new_body_size);
  9328.  
  9329.   /* Copy chars from BODY into NEW_BODY. */
  9330.   i = 0; new_body_index = 0;
  9331.  
  9332.   while (1)
  9333.     {
  9334.       if (!body[i])
  9335.     break;
  9336.  
  9337.       if (body[i] != '\\')
  9338.     new_body[new_body_index++] = body[i++];
  9339.       else
  9340.     {
  9341.       /* Snarf parameter name, check against named parameters. */
  9342.       char *param;
  9343.       int param_start, which, len;
  9344.  
  9345.       param_start = ++i;
  9346.       while ((body[i]) && (body[i] != '\\'))
  9347.         i++;
  9348.  
  9349.       len = i - param_start;
  9350.       param = (char *)xmalloc (1 + len);
  9351.       memcpy (param, body + param_start, len);
  9352.       param[len] = '\0';
  9353.  
  9354.       if (body[i])
  9355.         i++;
  9356.  
  9357.       /* Now check against named parameters. */
  9358.       for (which = 0; named && named[which]; which++)
  9359.         if (strcmp (named[which], param) == 0)
  9360.           break;
  9361.  
  9362.       if (named[which])
  9363.         {
  9364.           if (which < length_of_actuals)
  9365.         text = actuals[which];
  9366.           else
  9367.         text = (char *)NULL;
  9368.  
  9369.           if (!text)
  9370.         text = "";
  9371.  
  9372.           len = strlen (text);
  9373.         }
  9374.       else
  9375.         {
  9376.           len += 2;
  9377.           text = (char *)xmalloc (1 + len);
  9378.           sprintf (text, "\\%s\\", param);
  9379.         }
  9380.  
  9381.       if ((2 + strlen (param)) < len)
  9382.         new_body = (char *)xrealloc
  9383.           (new_body, new_body_size += (1 + len));
  9384.  
  9385.       free (param);
  9386.  
  9387.       strcpy (new_body + new_body_index, text);
  9388.       new_body_index += len;
  9389.  
  9390.       if (!named[which])
  9391.         free (text);
  9392.     }
  9393.     }
  9394.   new_body[new_body_index] = '\0';
  9395.   return (new_body);
  9396. }
  9397.  
  9398. /* Execute the macro passed in DEF, a pointer to a MACRO_DEF.  */
  9399. void
  9400. execute_macro (def)
  9401.      MACRO_DEF *def;
  9402. {
  9403.   register int i;
  9404.   char **arglist;
  9405.   int num_args;
  9406.   char *execution_string = (char *)NULL;
  9407.  
  9408.   if (macro_expansion_output_stream && !me_inhibit_expansion)
  9409.     me_append_before_this_command ();
  9410.  
  9411.   /* Find out how many arguments this macro definition takes. */
  9412.   num_args = array_len (def->arglist);
  9413.  
  9414.   /* Gather the arguments present on the line if there are any. */
  9415.   arglist = get_macro_args (def);
  9416.  
  9417.   if (num_args < array_len (arglist))
  9418.     {
  9419.       free_array (arglist);
  9420.       line_error ("Macro `%s' called with too many args", def->name);
  9421.       return;
  9422.     }
  9423.  
  9424.   if (def->body)
  9425.     execution_string = apply (def->arglist, arglist, def->body);
  9426.  
  9427.   free_array (arglist);
  9428.  
  9429.   if (def->body)
  9430.     {
  9431.       if (macro_expansion_output_stream && !me_inhibit_expansion)
  9432.     {
  9433.       remember_itext (input_text, input_text_offset);
  9434.       me_execute_string (execution_string);
  9435.     }
  9436.       else
  9437.     execute_string ("%s", execution_string);
  9438.  
  9439.       free (execution_string);
  9440.     }
  9441. }
  9442.  
  9443. /* Read and remember the definition of a macro. */
  9444. void
  9445. cm_macro ()
  9446. {
  9447.   register int i;
  9448.   char *name, **arglist, *body, *line;
  9449.   int body_size, body_index;
  9450.   int depth = 1;
  9451.   int defining_line = line_number;
  9452.   int flags = 0;
  9453.  
  9454.   arglist = (char **)NULL;
  9455.   body = (char *)NULL;
  9456.   body_size = 0;
  9457.   body_index = 0;
  9458.  
  9459.   if (macro_expansion_output_stream)
  9460.     me_append_before_this_command ();
  9461.  
  9462.   skip_whitespace ();
  9463.  
  9464.   /* Get the name of the macro.  This is the set of characters which are
  9465.      not whitespace and are not `{' immediately following the @macro. */
  9466.   {
  9467.     int start = input_text_offset;
  9468.     int len;
  9469.  
  9470.     for (i = start;
  9471.      (i < size_of_input_text) &&
  9472.      (input_text[i] != '{') &&
  9473.      (!cr_or_whitespace (input_text[i]));
  9474.      i++);
  9475.  
  9476.     len = i - start;
  9477.     name = (char *)xmalloc (1 + len);
  9478.     strncpy (name, input_text + start, len);
  9479.     name[len] = '\0';
  9480.     input_text_offset = i;
  9481.   }
  9482.  
  9483.   skip_whitespace ();
  9484.  
  9485.   /* It is not required that the definition of a macro includes an arglist.
  9486.      If not, don't try to get the named parameters, just use a null list. */
  9487.   if (curchar () == '{')
  9488.     {
  9489.       int arglist_index = 0, arglist_size = 0;
  9490.       int gathering_words = 1;
  9491.       char *word = (char *)NULL;
  9492.       int character;
  9493.  
  9494.       /* Read the words inside of the braces which determine the arglist.
  9495.      These words will be replaced within the body of the macro at
  9496.      execution time. */
  9497.  
  9498.       input_text_offset++;
  9499.       skip_whitespace_and_newlines ();
  9500.  
  9501.       while (gathering_words)
  9502.     {
  9503.       int len;
  9504.  
  9505.       for (i = input_text_offset;
  9506.            character = input_text[i];
  9507.            i++)
  9508.         {
  9509.           switch (character)
  9510.         {
  9511.         case '\n':
  9512.           line_number++;
  9513.         case ' ':
  9514.         case '\t':
  9515.         case ',':
  9516.         case '}':
  9517.           /* Found the end of the current arglist word.  Save it. */
  9518.           len = i - input_text_offset;
  9519.           word = (char *)xmalloc (1 + len);
  9520.           strncpy (word, input_text + input_text_offset, len);
  9521.           word[len] = '\0';
  9522.           input_text_offset = i;
  9523.  
  9524.           /* Advance to the comma or close-brace that signified
  9525.              the end of the argument. */
  9526.           while ((character = curchar ())
  9527.              && character != ','
  9528.              && character != '}')
  9529.             {
  9530.               input_text_offset++;
  9531.               if (character == '\n')
  9532.             line_number++;
  9533.             }
  9534.  
  9535.           /* Add the word to our list of words. */
  9536.           if ((arglist_index + 2) >= arglist_size)
  9537.             arglist = (char **)xrealloc
  9538.               (arglist, (arglist_size += 10) * sizeof (char *));
  9539.  
  9540.           arglist[arglist_index++] = word;
  9541.           arglist[arglist_index] = (char *)NULL;
  9542.           break;
  9543.         }
  9544.  
  9545.           if (character == '}')
  9546.         {
  9547.           input_text_offset++;
  9548.           gathering_words = 0;
  9549.           break;
  9550.         }
  9551.  
  9552.           if (character == ',')
  9553.         {
  9554.           input_text_offset++;
  9555.           skip_whitespace_and_newlines ();
  9556.           i = input_text_offset - 1;
  9557.         }
  9558.         }
  9559.     }
  9560.     }
  9561.  
  9562.   /* Read the text carefully until we find an "@end macro" which
  9563.      matches this one.  The text in between is the body of the macro. */
  9564.   skip_whitespace_and_newlines ();
  9565.  
  9566.   while (depth)
  9567.     {
  9568.       if ((input_text_offset + 9) > size_of_input_text)
  9569.     {
  9570.       int temp_line = line_number;
  9571.       line_number = defining_line;
  9572.       line_error ("%cend macro not found", COMMAND_PREFIX);
  9573.       line_number = temp_line;
  9574.       return;
  9575.     }
  9576.  
  9577.       get_rest_of_line (&line);
  9578.  
  9579.       /* Handle commands only meaningful within a macro. */
  9580.       if ((*line == COMMAND_PREFIX) && (depth == 1) &&
  9581.       (strncmp (line + 1, "allow-recursion", 15) == 0) &&
  9582.       (line[16] == '\0' || whitespace (line[16])))
  9583.     {
  9584.       for (i = 16; whitespace (line[i]); i++);
  9585.       strcpy (line, line + i);
  9586.       flags |= ME_RECURSE;
  9587.       if (!*line)
  9588.         {
  9589.           free (line);
  9590.           continue;
  9591.         }
  9592.     }
  9593.  
  9594.       if ((*line == COMMAND_PREFIX) && (depth == 1) &&
  9595.       (strncmp (line + 1, "quote-arg", 9) == 0) &&
  9596.       (line[10] == '\0' || whitespace (line[10])))
  9597.     {
  9598.       for (i = 10; whitespace (line[i]); i++);
  9599.       strcpy (line, line + i);
  9600.  
  9601.       if (arglist && arglist[0] && !arglist[1])
  9602.         {
  9603.           flags |= ME_QUOTE_ARG;
  9604.           if (!*line)
  9605.         {
  9606.           free (line);
  9607.           continue;
  9608.         }
  9609.         }
  9610.       else
  9611.         {
  9612.           line_error ("%cquote-arg only useful when the macro takes a single argument",
  9613.               COMMAND_PREFIX);
  9614.         }
  9615.     }
  9616.  
  9617.       if ((*line == COMMAND_PREFIX) &&
  9618.       (strncmp (line + 1, "macro ", 6) == 0))
  9619.     depth++;
  9620.  
  9621.       if ((*line == COMMAND_PREFIX) &&
  9622.       (strncmp (line + 1, "end macro", 9) == 0))
  9623.     depth--;
  9624.  
  9625.       if (depth)
  9626.     {
  9627.       if ((body_index + strlen (line) + 3) >= body_size)
  9628.         body = (char *)xrealloc
  9629.           (body, body_size += 3 + strlen (line));
  9630.       strcpy (body + body_index, line);
  9631.       body_index += strlen (line);
  9632.       body[body_index++] = '\n';
  9633.       body[body_index] = '\0';
  9634.     }
  9635.       free (line);
  9636.     }
  9637.  
  9638.   /* We now have the name, the arglist, and the body.  However, BODY
  9639.      includes the final newline which preceded the `@end macro' text.
  9640.      Delete it. */
  9641.   if (body && strlen (body))
  9642.     body[strlen (body) - 1] = '\0';
  9643.  
  9644.   add_macro (name, arglist, body, input_filename, defining_line, flags);
  9645.  
  9646.   if (macro_expansion_output_stream)
  9647.     remember_itext (input_text, input_text_offset);
  9648. }
  9649.  
  9650. void
  9651. cm_unmacro ()
  9652. {
  9653.   register int i;
  9654.   char *line, *name;
  9655.   MACRO_DEF *def;
  9656.  
  9657.   if (macro_expansion_output_stream)
  9658.     me_append_before_this_command ();
  9659.  
  9660.   get_rest_of_line (&line);
  9661.   canon_white (line);
  9662.  
  9663.   for (i = 0; line[i] && !whitespace (line[i]); i++);
  9664.   name = (char *)xmalloc (i);
  9665.   strncpy (name, line, i);
  9666.   name[i] = '\0';
  9667.  
  9668.   def = delete_macro (name);
  9669.  
  9670.   if (def)
  9671.     {
  9672.       free (def->source_file);
  9673.       free (def->name);
  9674.       free (def->body);
  9675.  
  9676.       if (def->arglist)
  9677.     {
  9678.       register int i;
  9679.  
  9680.       for (i = 0; def->arglist[i]; i++)
  9681.         free (def->arglist[i]);
  9682.  
  9683.       free (def->arglist);
  9684.     }
  9685.  
  9686.       free (def);
  9687.     }
  9688.  
  9689.   free (line);
  9690.   free (name);
  9691.  
  9692.   if (macro_expansion_output_stream)
  9693.     remember_itext (input_text, input_text_offset);
  9694. }
  9695.  
  9696. /* How to output sections of the input file verbatim. */
  9697.  
  9698. /* Set the value of POINTER's offset to OFFSET. */
  9699. ITEXT *
  9700. remember_itext (pointer, offset)
  9701.      char *pointer;
  9702.      int offset;
  9703. {
  9704.   register int i;
  9705.   ITEXT *itext = (ITEXT *)NULL;
  9706.  
  9707.   /* If we have no info, initialize a blank list. */
  9708.   if (!itext_info)
  9709.     {
  9710.       itext_info = (ITEXT **)xmalloc ((itext_size = 10) * sizeof (ITEXT *));
  9711.       for (i = 0; i < itext_size; i++)
  9712.     itext_info[i] = (ITEXT *)NULL;
  9713.     }
  9714.  
  9715.   /* If the pointer is already present in the list, then set the offset. */
  9716.   for (i = 0; i < itext_size; i++)
  9717.     if ((itext_info[i] != (ITEXT *)NULL) &&
  9718.     (itext_info[i]->pointer == pointer))
  9719.       {
  9720.     itext = itext_info[i];
  9721.     itext_info[i]->offset = offset;
  9722.     break;
  9723.       }
  9724.  
  9725.   if (i == itext_size)
  9726.     {
  9727.       /* Find a blank slot, (or create a new one), and remember the
  9728.      pointer and offset. */
  9729.       for (i = 0; i < itext_size; i++)
  9730.     if (itext_info[i] == (ITEXT *)NULL)
  9731.       break;
  9732.  
  9733.       /* If not found, then add some slots. */
  9734.       if (i == itext_size)
  9735.     {
  9736.       register int j;
  9737.  
  9738.       itext_info = (ITEXT **)xrealloc
  9739.         (itext_info, (itext_size += 10) * sizeof (ITEXT *));
  9740.  
  9741.       for (j = i; j < itext_size; j++)
  9742.         itext_info[j] = (ITEXT *)NULL;
  9743.     }
  9744.  
  9745.       /* Now add the pointer and the offset. */
  9746.       itext_info[i] = (ITEXT *)xmalloc (sizeof (ITEXT));
  9747.       itext_info[i]->pointer = pointer;
  9748.       itext_info[i]->offset = offset;
  9749.       itext = itext_info[i];
  9750.     }
  9751.   return (itext);
  9752. }
  9753.  
  9754. /* Forget the input text associated with POINTER. */
  9755. void
  9756. forget_itext (pointer)
  9757.      char *pointer;
  9758. {
  9759.   register int i;
  9760.  
  9761.   for (i = 0; i < itext_size; i++)
  9762.     if (itext_info[i] && (itext_info[i]->pointer == pointer))
  9763.       {
  9764.     free (itext_info[i]);
  9765.     itext_info[i] = (ITEXT *)NULL;
  9766.     break;
  9767.       }
  9768. }
  9769.  
  9770. /* Append the text which appeared in input_text from the last offset to
  9771.    the character just before the command that we are currently executing. */
  9772. void
  9773. me_append_before_this_command ()
  9774. {
  9775.   register int i;
  9776.  
  9777.   for (i = input_text_offset; i && (input_text[i] != COMMAND_PREFIX); i--);
  9778.   maybe_write_itext (input_text, i);
  9779. }
  9780.  
  9781. /* Similar to execute_string (), but only takes a single string argument,
  9782.    and remembers the input text location, etc. */
  9783. void
  9784. me_execute_string (execution_string)
  9785.      char *execution_string;
  9786. {
  9787.   pushfile ();
  9788.   input_text_offset = 0;
  9789.   input_text = execution_string;
  9790.   input_filename = strdup (input_filename);
  9791.   size_of_input_text = strlen (execution_string);
  9792.  
  9793.   remember_itext (execution_string, 0);
  9794.  
  9795.   executing_string++;
  9796.   reader_loop ();
  9797.   popfile ();
  9798.   executing_string--;
  9799. }
  9800.  
  9801. /* Append the text which appears in input_text from the last offset to
  9802.    the current OFFSET. */
  9803. void
  9804. append_to_expansion_output (offset)
  9805.      int offset;
  9806. {
  9807.   register int i;
  9808.   ITEXT *itext = (ITEXT *)NULL;
  9809.  
  9810.   for (i = 0; i < itext_size; i++)
  9811.     if (itext_info[i] && itext_info[i]->pointer == input_text)
  9812.       {
  9813.     itext = itext_info[i];
  9814.     break;
  9815.       }
  9816.  
  9817.   if (!itext)
  9818.     itext = remember_itext (input_text, 0);
  9819.  
  9820.   if (offset > itext->offset)
  9821.     {
  9822.       write_region_to_macro_output
  9823.     (input_text, itext->offset, offset);
  9824.       remember_itext (input_text, offset);
  9825.     }
  9826. }
  9827.  
  9828. /* Only write this input text iff it appears in our itext list. */
  9829. void
  9830. maybe_write_itext (pointer, offset)
  9831.      char *pointer;
  9832.      int offset;
  9833. {
  9834.   register int i;
  9835.   ITEXT *itext = (ITEXT *)NULL;
  9836.  
  9837.   for (i = 0; i < itext_size; i++)
  9838.     if (itext_info[i] && (itext_info[i]->pointer == pointer))
  9839.       {
  9840.     itext = itext_info[i];
  9841.     break;
  9842.       }
  9843.  
  9844.   if (itext && (itext->offset < offset))
  9845.     {
  9846.       write_region_to_macro_output (itext->pointer, itext->offset, offset);
  9847.       remember_itext (pointer, offset);
  9848.     }
  9849. }
  9850.  
  9851. void
  9852. write_region_to_macro_output (string, start, end)
  9853.      char *string;
  9854.      int start, end;
  9855. {
  9856.   if (macro_expansion_output_stream)
  9857.     fwrite (string + start, 1, end - start, macro_expansion_output_stream);
  9858. }
  9859.  
  9860. #endif /* HAVE_MACROS */
  9861.  
  9862. /* Return the length of the array in ARRAY. */
  9863. int
  9864. array_len (array)
  9865.      char **array;
  9866. {
  9867.   register int i = 0;
  9868.  
  9869.   if (array)
  9870.     for (i = 0; array[i] != (char *)NULL; i++);
  9871.  
  9872.   return (i);
  9873. }
  9874.  
  9875. void
  9876. free_array (array)
  9877.      char **array;
  9878. {
  9879.   if (array)
  9880.     {
  9881.       register int i;
  9882.  
  9883.       for (i = 0; array[i] != (char *)NULL; i++)
  9884.     free (array[i]);
  9885.  
  9886.       free (array);
  9887.     }
  9888. }
  9889.  
  9890. /* Function is used even when we don't have macros.  Although, I have
  9891.    to admit, it is unlikely that you would have a use for it if you
  9892.    aren't using macros. */
  9893. char **
  9894. get_brace_args (quote_single)
  9895.      int quote_single;
  9896. {
  9897.   char **arglist, *word;
  9898.   int arglist_index, arglist_size;
  9899.   int character, escape_seen, start;
  9900.   int depth = 1;
  9901.  
  9902.   /* There is an arglist in braces here, so gather the args inside of it. */
  9903.   skip_whitespace_and_newlines ();
  9904.   input_text_offset++;
  9905.   arglist = (char **)NULL;
  9906.   arglist_index = arglist_size = 0;
  9907.  
  9908.  get_arg:
  9909.   skip_whitespace_and_newlines ();
  9910.   start = input_text_offset;
  9911.   escape_seen = 0;
  9912.  
  9913.   while (character = curchar ())
  9914.     {
  9915.       if (character == '\\')
  9916.     {
  9917.       input_text_offset += 2;
  9918.       escape_seen = 1;
  9919.     }
  9920.       else if (character == '{')
  9921.     {
  9922.       depth++;
  9923.       input_text_offset++;
  9924.     }
  9925.       else if ((character == ',' && !quote_single) ||
  9926.            ((character == '}') && depth == 1))
  9927.     {
  9928.       int len = input_text_offset - start;
  9929.  
  9930.       if (len || (character != '}'))
  9931.         {
  9932.           word = (char *)xmalloc (1 + len);
  9933.           strncpy (word, input_text + start, len);
  9934.           word[len] = '\0';
  9935.  
  9936.           /* Clean up escaped characters. */
  9937.           if (escape_seen)
  9938.         {
  9939.           register int i;
  9940.  
  9941.           for (i = 0; word[i]; i++)
  9942.             if (word[i] == '\\')
  9943.               memmove (word + i, word + i + 1,
  9944.                    1 + strlen (word + i + 1));
  9945.         }
  9946.  
  9947.           if (arglist_index + 2 >= arglist_size)
  9948.         arglist = (char **)xrealloc
  9949.           (arglist, (arglist_size += 10) * sizeof (char *));
  9950.  
  9951.           arglist[arglist_index++] = word;
  9952.           arglist[arglist_index] = (char *)NULL;
  9953.         }
  9954.  
  9955.       input_text_offset++;
  9956.       if (character == '}')
  9957.         break;
  9958.       else
  9959.         goto get_arg;
  9960.     }
  9961.       else if (character == '}')
  9962.     {
  9963.       depth--;
  9964.       input_text_offset++;
  9965.     }
  9966.       else
  9967.     {
  9968.       input_text_offset++;
  9969.       if (character == '\n') line_number++;
  9970.     }
  9971.     }
  9972.   return (arglist);
  9973. }
  9974.  
  9975. /* **************************************************************** */
  9976. /*                                                                  */
  9977. /*                  Looking For Include Files                       */
  9978. /*                                                                  */
  9979. /* **************************************************************** */
  9980.  
  9981. /* Given a string containing units of information separated by colons,
  9982.    return the next one pointed to by INDEX, or NULL if there are no more.
  9983.    Advance INDEX to the character after the colon. */
  9984. char *
  9985. extract_colon_unit (string, index)
  9986.      char *string;
  9987.      int *index;
  9988. {
  9989.   int i, start;
  9990.  
  9991.   i = *index;
  9992.  
  9993.   if (!string || (i >= strlen (string)))
  9994.     return ((char *)NULL);
  9995.  
  9996.   /* Each call to this routine leaves the index pointing at a colon if
  9997.      there is more to the path.  If I is > 0, then increment past the
  9998.      `:'.  If I is 0, then the path has a leading colon.  Trailing colons
  9999.      are handled OK by the `else' part of the if statement; an empty
  10000.      string is returned in that case. */
  10001.   if (i && string[i] == ':')
  10002.     i++;
  10003.  
  10004.   start = i;
  10005.  
  10006.   while (string[i] && string[i] != ':') i++;
  10007.  
  10008.   *index = i;
  10009.  
  10010.   if (i == start)
  10011.     {
  10012.       if (string[i])
  10013.     (*index)++;
  10014.  
  10015.       /* Return "" in the case of a trailing `:'. */
  10016.       return (strdup (""));
  10017.     }
  10018.   else
  10019.     {
  10020.       char *value;
  10021.  
  10022.       value = (char *)xmalloc (1 + (i - start));
  10023.       strncpy (value, &string[start], (i - start));
  10024.       value [i - start] = '\0';
  10025.  
  10026.       return (value);
  10027.     }
  10028. }
  10029.  
  10030. /* Return the full pathname for FILENAME by searching along PATH.
  10031.    When found, return the stat () info for FILENAME in FINFO.
  10032.    If PATH is NULL, only the current directory is searched.
  10033.    If the file could not be found, return a NULL pointer. */
  10034. char *
  10035. get_file_info_in_path (filename, path, finfo)
  10036.      char *filename, *path;
  10037.      struct stat *finfo;
  10038. {
  10039.   char *dir;
  10040.   int result, index = 0;
  10041.  
  10042.   if (path == (char *)NULL)
  10043.     path = ".";
  10044.  
  10045. #if !defined(__amigaos__)
  10046.   /* Handle absolute pathnames. "./foo", "/foo", "../foo". */
  10047.   if (*filename == '/' ||
  10048.       (*filename == '.' &&
  10049.        (filename[1] == '/' ||
  10050.     (filename[1] == '.' && filename[2] == '/'))))
  10051.     {
  10052.       if (stat (filename, finfo) == 0)
  10053.     return (strdup (filename));
  10054.       else
  10055.     return ((char *)NULL);
  10056.     }
  10057. #endif
  10058.  
  10059.   while (dir = extract_colon_unit (path, &index))
  10060.     {
  10061.       char *fullpath;
  10062.  
  10063.       if (!*dir)
  10064.     {
  10065.       free (dir);
  10066.       dir = strdup (".");
  10067.     }
  10068.  
  10069.       fullpath = (char *)xmalloc (2 + strlen (dir) + strlen (filename));
  10070.  
  10071. #if !defined(__amigaos__)
  10072.       sprintf (fullpath, "%s/%s", dir, filename);
  10073. #else /* __amigaos__ */
  10074.       if (strcmp (dir, ".")) sprintf (fullpath, "%s/%s", dir, filename);
  10075.       else sprintf (fullpath, "%s", filename);
  10076. #endif /* __amigaos__ */
  10077.       free (dir);
  10078.  
  10079.       result = stat (fullpath, finfo);
  10080.  
  10081.       if (result == 0)
  10082.     return (fullpath);
  10083.       else
  10084.     free (fullpath);
  10085.     }
  10086.   return ((char *)NULL);
  10087. }
  10088.  
  10089. void
  10090. amiga_set_attributes (char *attr_type)
  10091. {
  10092.   int after_attr = 0;
  10093.   if (in_amiga_guide_button || printing_index || !amiga_guide_39)
  10094.     return;
  10095.  
  10096.   if (no_headers)
  10097.     {
  10098.       int after_attr = 0;
  10099.  
  10100.       if (output_column > 4)
  10101.         after_attr = 1;
  10102.       else
  10103.         output_column -= (amiga_guide_hidden_chars = 4);
  10104.  
  10105.       if (strcmp (attr_type, "@{b}") == 0)
  10106.         {
  10107.           /* Write Bold on */
  10108.           add_word ("\x1B[1m");
  10109.         }
  10110.       if (strcmp (attr_type, "@{i}") == 0)
  10111.         {
  10112.           /* Write Italic on */
  10113.           add_word ("\x1B[3m");
  10114.         }
  10115.       if ((strcmp (attr_type, "@{ub}") == 0) ||
  10116.           (strcmp (attr_type, "@{ui}") == 0))
  10117.         {
  10118.           /* Write Bold or italic off */
  10119.           add_word ("\x1B[0m");
  10120.         }
  10121.       if (after_attr)
  10122.         output_column -= (amiga_guide_hidden_chars = 4);
  10123.       after_attr = 0;
  10124.       if (in_amiga_guide_title)
  10125.         amiga_hide_title_chars += 4;
  10126.     }
  10127.   else if (amiga_guide_39)
  10128.     {
  10129.       if (output_column > strlen (attr_type))
  10130.         output_column -= (amiga_guide_hidden_chars = strlen (attr_type));
  10131.       else
  10132.         after_attr = 1;
  10133.  
  10134.       if (input_text[input_text_offset - 1] == '\\')
  10135.         add_word (" ");
  10136.       /* current_indent is the current indentation */
  10137.       add_word_args ("%s", attr_type);
  10138.  
  10139.       if ((input_text[input_text_offset + 1]) == '@')
  10140.         add_word (" ");
  10141.  
  10142.       if (after_attr)
  10143.         {
  10144.           output_column -= (amiga_guide_hidden_chars = strlen (attr_type));
  10145.           after_attr = 0;
  10146.         }
  10147.  
  10148.       if (in_amiga_guide_title)
  10149.         amiga_hide_title_chars += strlen (attr_type);
  10150.     }
  10151. }
  10152.  
  10153. /* **************************************************************** */
  10154. /*                                                                  */
  10155. /*        Calculate the index and menu button text length.          */
  10156. /*                                                                  */
  10157. /* **************************************************************** */
  10158.  
  10159. /* Modified version of execute_macro () but now doesn't write anything
  10160.    to the output file */
  10161.    
  10162. /* Execute the macro passed in DEF, a pointer to a MACRO_DEF.  */
  10163. int
  10164. execute_macro_return_length (def)
  10165.      MACRO_DEF *def;
  10166. {
  10167.   register int i;
  10168.   char **arglist;
  10169.   int num_args;
  10170.   char *execution_string = (char *)NULL;
  10171.   int length = 0;
  10172.  
  10173.   if (macro_expansion_output_stream && !me_inhibit_expansion)
  10174.     me_append_before_this_command ();
  10175.  
  10176.   /* Find out how many arguments this macro definition takes. */
  10177.   num_args = array_len (def->arglist);
  10178.  
  10179.   /* Gather the arguments present on the line if there are any. */
  10180.   arglist = get_macro_args (def);
  10181.  
  10182.   if (num_args < array_len (arglist))
  10183.     {
  10184.       free_array (arglist);
  10185.       line_error ("Macro `%s' called with too many args", def->name);
  10186.       return;
  10187.     }
  10188.  
  10189.   if (def->body)
  10190.     execution_string = apply (def->arglist, arglist, def->body);
  10191.  
  10192.   free_array (arglist);
  10193.  
  10194.   if (def->body)
  10195.     {
  10196.       if (macro_expansion_output_stream && !me_inhibit_expansion)
  10197.         {
  10198.            char * temp_string = strdup(execution_string);
  10199.            int command_string_length(void);
  10200.              
  10201.            remember_itext (input_text, input_text_offset);
  10202.            pushfile ();
  10203.            input_text_offset = 0;
  10204.            input_text = temp_string;
  10205.            input_filename = strdup (input_filename);
  10206.            size_of_input_text = strlen (temp_string);
  10207.            remember_itext (temp_string, 0);
  10208.            length += command_string_length ();
  10209.            popfile ();
  10210.         }
  10211.       else
  10212.         {
  10213.            char * temp_string = strdup(execution_string);
  10214.            int i = 0;
  10215.            int command_string_length(void);
  10216.                  
  10217.            pushfile ();
  10218.            input_text_offset = 0;
  10219.            input_text = temp_string;
  10220.            input_filename = strdup (input_filename);
  10221.            size_of_input_text = strlen (temp_string);
  10222.            remember_itext (temp_string, 0);
  10223.            length += command_string_length ();
  10224.            popfile ();
  10225.         }
  10226.  
  10227.       free (execution_string);
  10228.     }
  10229.   return length;
  10230. }
  10231.  
  10232. /* Modified version of read_command (), it doesn't write characters to
  10233.    the output file */
  10234.    
  10235. /* input_text_offset is right at the command prefix character.
  10236.    Read the next token to determine what to do. */
  10237. int
  10238. read_command_return_length ()
  10239. {
  10240.   COMMAND *entry;
  10241.   int length = 0;
  10242.  
  10243.   input_text_offset++;
  10244.   free_and_clear (&command);
  10245.   command = read_token ();
  10246.  
  10247. #if defined (HAVE_MACROS)
  10248.   /* Check to see if this command is a macro.  If so, execute it here. */
  10249.   {
  10250.     MACRO_DEF *def;
  10251.  
  10252.     def = find_macro (command);
  10253.  
  10254.     if (def)
  10255.       {
  10256.         /* We disallow recursive use of a macro call.  Inhibit the expansion
  10257.            of this macro during the life of its execution. */
  10258.         if (!(def->flags & ME_RECURSE))
  10259.           def->inhibited = 1;
  10260.  
  10261.         length += execute_macro_return_length (def);
  10262.  
  10263.         if (!(def->flags & ME_RECURSE))
  10264.           def->inhibited = 0;
  10265.  
  10266.         return length;
  10267.       }
  10268.     }
  10269. #endif /* HAVE_MACROS */
  10270.  
  10271.   entry = get_command_entry (command);
  10272.  
  10273.   if (entry == (COMMAND *)-1)
  10274.     {
  10275.       line_error ("Unknown info command `%s'", command);
  10276.       return;
  10277.     }
  10278.  
  10279.   if (entry->name) 
  10280.     {
  10281.       /* We have to count the number of characters the
  10282.          command will output. */
  10283.       if (strncmp(entry->name,"dots", 4) == 0)
  10284.         length += 3;
  10285.       else if (strncmp(entry->name,"bullet", 3) == 0)
  10286.         length += 1;
  10287.       else if (strncmp(entry->name,"minus", 3) == 0)
  10288.         length += 1;
  10289.       else if (strncmp(entry->name,"TeX", 3) == 0)
  10290.         length += 3;
  10291.       else if (strncmp(entry->name,"copyright", 9) == 0)
  10292.         length += 3;
  10293.       else if (strncmp(entry->name,"cite", 4) == 0)
  10294.         length += 1;
  10295.     }
  10296.   if (entry->argument_in_braces) 
  10297.     remember_brace (entry->proc);
  10298.     
  10299.   return length;
  10300. }
  10301.  
  10302. /* Modified version of pop_and_call_brace, this one _doesn't_ call
  10303.    the associated function, because the function wasn't called in
  10304.    read_command_return_length */
  10305.  
  10306. void
  10307. pop_and_call_brace_length ()
  10308. {
  10309.   BRACE_ELEMENT *temp;
  10310.   COMMAND_FUNCTION *proc;
  10311.   int pos;
  10312.  
  10313.   if (brace_stack == (BRACE_ELEMENT *) NULL)
  10314.     {
  10315.       line_error ("Unmatched close brace");
  10316.       return;
  10317.     }
  10318.  
  10319.   pos = brace_stack->pos;
  10320.   proc = brace_stack->proc;
  10321.   temp = brace_stack->next;
  10322.   free (brace_stack);
  10323.   brace_stack = temp;
  10324.  
  10325. }
  10326.  
  10327. /* Modified version of reader_loop (), it doesn't write anything to the
  10328.    output file. */
  10329.  
  10330. /* Okay, we are ready to count the number of characters.  Call the reader on
  10331.    some text, and count the number of characters.  Handle commands by
  10332.    remembering things like open braces and the current file position on a
  10333.    stack, and when the corresponding close brace is found, you can call
  10334.    the function with the proper arguments. */
  10335.  
  10336. int
  10337. command_string_length ()
  10338. {
  10339.   int character;
  10340.   int done = 0;
  10341.   int dash_count = 0;
  10342.   int length = 0;
  10343.  
  10344.   while (!done)
  10345.     {
  10346.       if (input_text_offset >= size_of_input_text)
  10347.         break;
  10348.  
  10349.       character = curchar ();
  10350.  
  10351.       if (!in_fixed_width_font &&
  10352.           (character == '\'' || character == '`') &&
  10353.           input_text[input_text_offset + 1] == character)
  10354.         {
  10355.           input_text_offset++;
  10356.           length++;
  10357.           character = '"';
  10358.         }
  10359.  
  10360.       if (character == '-')
  10361.         {
  10362.           dash_count++;
  10363.           if (dash_count == 2 && !in_fixed_width_font)
  10364.             {
  10365.               input_text_offset++;
  10366.               continue;
  10367.             }
  10368.         }
  10369.       else
  10370.         {
  10371.           dash_count = 0;
  10372.         }
  10373.  
  10374.       /* If this is a whitespace character, then check to see if the line
  10375.          is blank.  If so, advance to the carriage return. */
  10376.       if (whitespace (character))
  10377.         {
  10378.           register int i = input_text_offset + 1;
  10379.  
  10380.           while (i < size_of_input_text && whitespace (input_text[i]))
  10381.             i++;
  10382.  
  10383.           if (i == size_of_input_text || input_text[i] == '\n')
  10384.             {
  10385.               if (i == size_of_input_text)
  10386.                 i--;
  10387.  
  10388.               input_text_offset = i;
  10389.               character = curchar ();
  10390.             }
  10391.         }
  10392.  
  10393.       switch (character)
  10394.         {
  10395.         case COMMAND_PREFIX:
  10396.           if (input_text[input_text_offset+1] == COMMAND_PREFIX)
  10397.             length++; /* it isn't a command, count the @ too */
  10398.           length += read_command_return_length ();
  10399.           break;
  10400.  
  10401.         case '{':
  10402.  
  10403.           /* Special case.  I'm not supposed to see this character by itself.
  10404.              If I do, it means there is a syntax error in the input text.
  10405.              Report the error here, but remember this brace on the stack so
  10406.              you can ignore its partner. */
  10407.  
  10408.           line_error ("Misplaced `{'");
  10409.           remember_brace (misplaced_brace);
  10410.  
  10411.           /* Don't advance input_text_offset since this happens in
  10412.              remember_brace ().
  10413.              input_text_offset++;
  10414.            */
  10415.           break;
  10416.  
  10417.         case '}':
  10418.           pop_and_call_brace_length ();
  10419.           input_text_offset++;
  10420.           break;
  10421.  
  10422.         default:
  10423.           length++;
  10424.           input_text_offset++;
  10425.         }
  10426.     }
  10427.     return length;
  10428. }
  10429.  
  10430. /* The following code is for counting the needed spaces to make all index/menu buttons 
  10431.    of equal length. Because there can be commands in "string" we need to execute
  10432.    these commands first. To accomplish this we call a slightly modified version of
  10433.    reader_loop () now called command_string_length () which doesn't write character
  10434.    to the outputfile. */
  10435. char *
  10436. amiga_button_text_length (string, length)
  10437.     char * string;
  10438.     int length;
  10439. {
  10440.   int number_of_spaces = 0;
  10441.   char * spaces = NULL;
  10442.   char * temp_string = strdup(string);
  10443.   int i = 0;
  10444.                   
  10445.   pushfile ();
  10446.   input_text_offset = 0;
  10447.   input_text = temp_string;
  10448.   input_filename = strdup (input_filename);
  10449.   size_of_input_text = strlen (temp_string);
  10450.   remember_itext (temp_string, 0);
  10451.   number_of_spaces = length-command_string_length ();
  10452.   popfile ();
  10453.  
  10454.   if (number_of_spaces > 0)
  10455.     {
  10456.        spaces = (char *)xmalloc(number_of_spaces+1);
  10457.        spaces[number_of_spaces] = '\0';
  10458.        for (i=0; i<number_of_spaces; i++) 
  10459.          spaces[i] = ' ';
  10460.     }
  10461.   else
  10462.     {
  10463.        number_of_spaces = 0;
  10464.        spaces = (char *)xmalloc(1);
  10465.        spaces[0] = '\0';
  10466.     }
  10467.  
  10468.   return spaces;
  10469. }
  10470.  
  10471.