diff man/makeinfo.c @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children ac2d302a0011
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/man/makeinfo.c	Mon Aug 13 08:45:50 2007 +0200
@@ -0,0 +1,9107 @@
+/* Makeinfo -- convert texinfo format files into info files.
+
+   Copyright (C) 1987, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
+   Copyright (C) 1994, 1995 Sun Microsystems.
+   Copyright (C) 1995 Amdahl Corporation.
+
+   This file is part of GNU Info.
+
+   Makeinfo is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY.  No author or distributor accepts
+   responsibility to anyone for the consequences of using it or for
+   whether it serves any particular purpose or works at all, unless he
+   says so in writing.  Refer to the GNU Emacs General Public License
+   for full details.
+
+   Everyone is granted permission to copy, modify and redistribute
+   Makeinfo, but only under the conditions described in the GNU Emacs
+   General Public License.   A copy of this license is supposed to
+   have been given to you along with GNU Emacs so you can know your
+   rights and responsibilities.  It should be in a file named COPYING.
+   Among other things, the copyright notice and this notice must be
+   preserved on all copies.  */
+
+/* This is Makeinfo version 1.63.  If you change the version number of
+   Makeinfo, please change it here and at the lines reading:
+
+    int major_version = 1;
+    int minor_version = 63;
+
+   in the code below.
+
+   Makeinfo is authored by Brian Fox (bfox@ai.mit.edu).
+   March 1994: additions by Ben Wing (wing@netcom.com).
+*/
+
+#if defined (HAVE_CONFIG_H)
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+/* You can change some of the behaviour of Makeinfo by changing the
+   following defines: */
+
+/* Define INDENT_PARAGRAPHS_IN_TABLE if you want the paragraphs which
+   appear within an @table, @ftable, or @itemize environment to have
+   standard paragraph indentation.  Without this, such paragraphs have
+   no starting indentation. */
+/* #define INDENT_PARAGRAPHS_IN_TABLE */
+
+/* Define DEFAULT_INDENTATION_INCREMENT as an integer which is the amount
+   that @example should increase indentation by.  This incremement is used
+   for all insertions which indent the enclosed text. */
+#define DEFAULT_INDENTATION_INCREMENT 5
+
+/* Define PARAGRAPH_START_INDENT to be the amount of indentation that
+   the first lines of paragraphs receive by default, where no other
+   value has been specified.  Users can change this value on the command
+   line, with the --paragraph-indent option, or within the texinfo file,
+   with the @paragraphindent command. */
+#define PARAGRAPH_START_INDENT 3
+
+/* Define DEFAULT_PARAGRAPH_SPACING as the number of blank lines that you
+   wish to appear between paragraphs.  A value of 1 creates a single blank
+   line between paragraphs.  Paragraphs are defined by 2 or more consecutive
+   newlines in the input file (i.e., one or more blank lines). */
+#define DEFAULT_PARAGRAPH_SPACING 1
+
+/* Define HAVE_MACROS to enable the macro facility of TeXinfo.  Using this
+   facility, users can create their own command procedures with arguments. */
+#define HAVE_MACROS
+
+/* **************************************************************** */
+/*								    */
+/*			Include File Declarations       	    */
+/*								    */
+/* **************************************************************** */
+
+/* Indent #pragma so that older Cpp's don't try to parse it. */
+#if defined (_AIX)
+ # pragma alloca
+#endif /* _AIX */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <errno.h>
+
+#include <stdarg.h>
+#include "getopt.h"
+#if defined (HAVE_UNISTD_H)
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+#if defined (VMS)
+#include <perror.h>
+#endif
+
+#include <stdlib.h>
+
+#if defined (HAVE_STRING_H)
+#include <string.h>
+#define HAVE_STRDUP
+#define HAVE_STRCASECMP
+#else
+#include <strings.h>
+#endif /* !HAVE_STRING_H */
+
+#if defined (TM_IN_SYS_TIME)
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif /* !TM_IN_SYS_TIME */
+
+#if defined (HAVE_SYS_FCNTL_H)
+#include <sys/fcntl.h>
+#else
+#include <fcntl.h>
+#endif /* !HAVE_SYS_FCNTL_H */
+
+#if defined (HAVE_SYS_FILE_H)
+#include <sys/file.h>
+#endif /* HAVE_SYS_FILE_H */
+
+#if defined (__GNUC__) && !defined (alloca)
+#define alloca __builtin_alloca
+#else
+#if defined(HAVE_ALLOCA_H)
+#include <alloca.h>
+#else /* !HAVE_ALLOCA_H */
+#if !defined (_AIX)
+extern char *alloca ();
+#endif /* !_AIX */
+#endif /* !HAVE_ALLOCA_H */
+#endif /* !__GNUC__ */
+
+#ifdef __SUNPRO_C
+void *__builtin_alloca (unsigned int);
+#endif
+
+static void *xmalloc (unsigned int), *xrealloc (void *, unsigned int);
+#if defined (__osf__)
+extern void *malloc (), *realloc ();
+#endif /* __osf__ */
+
+static char **get_brace_args (int);
+static int array_len (char **);
+static void free_array (char **);
+static void isolate_nodename (char *);
+
+/* Non-zero means that we are currently hacking the insides of an
+   insertion which would use a fixed width font. */
+static int in_fixed_width_font = 0;
+
+/* Non-zero means that start_paragraph () MUST be called before we pay
+   any attention to close_paragraph () calls. */
+int must_start_paragraph = 0;
+
+/* Non-zero means a string is in execution, as opposed to a file. */
+static int executing_string = 0;
+
+#if defined (HAVE_MACROS)
+/* If non-NULL, this is an output stream to write the full macro expansion
+   of the input text to.  The resultant file is another texinfo file, but
+   missing @include, @infoinclude, @macro, and macro invocations.  Instead,
+   all of the text is placed within the file. */
+FILE *macro_expansion_output_stream = (FILE *)NULL;
+
+/* Here is a structure used to remember input text strings and offsets
+   within them. */
+typedef struct {
+  char *pointer;		/* Pointer to the input text. */
+  int offset;			/* Offset of the last character output. */
+} ITEXT;
+
+static ITEXT **itext_info = (ITEXT **)NULL;
+static int itext_size = 0;
+
+/* Non-zero means to inhibit the writing of macro expansions to the output
+   stream.  This is used in special cases where the output has already been
+   written. */
+int me_inhibit_expansion = 0;
+
+static ITEXT *remember_itext (char *, int);
+static void forget_itext (char *);
+static void me_append_before_this_command (void);
+static void append_to_expansion_output (int);
+static void write_region_to_macro_output (char *, int, int);
+static void maybe_write_itext (char *, int);
+static void me_execute_string (char *);
+#endif /* HAVE_MACROS */
+
+/* Some systems don't declare this function in pwd.h. */
+struct passwd *getpwnam ();
+
+
+/* **************************************************************** */
+/*								    */
+/*			      Global Defines  			    */
+/*								    */
+/* **************************************************************** */
+
+/* Error levels */
+#define NO_ERROR 0
+#define SYNTAX	 2
+#define FATAL	 4
+
+/* C's standard macros don't check to make sure that the characters being
+   changed are within range.  So I have to check explicitly. */
+
+/* GNU Library doesn't have toupper().  Until GNU gets this fixed, I will
+   have to do it. */
+#ifndef toupper
+#define toupper(c) ((c) - 32)
+#endif
+
+#define coerce_to_upper(c) ((islower(c) ? toupper(c) : (c)))
+#define coerce_to_lower(c) ((isupper(c) ? tolower(c) : (c)))
+
+#define control_character_bit 0x40 /* %01000000, must be off. */
+#define meta_character_bit 0x080/* %10000000, must be on.  */
+#define CTL(c) ((c) & (~control_character_bit))
+#define UNCTL(c) coerce_to_upper(((c)|control_character_bit))
+#define META(c) ((c) | (meta_character_bit))
+#define UNMETA(c) ((c) & (~meta_character_bit))
+
+#define whitespace(c) (((c) == '\t') || ((c) == ' '))
+#define sentence_ender(c) ((c) == '.' || (c) == '?' || (c) == '!')
+#define cr_or_whitespace(c) (((c) == '\t') || ((c) == ' ') || ((c) == '\n'))
+
+#ifndef isletter
+#define isletter(c) (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z'))
+#endif
+
+#ifndef isupper
+#define isupper(c) ((c) >= 'A' && (c) <= 'Z')
+#endif
+
+#ifndef isdigit
+#define isdigit(c)  ((c) >= '0' && (c) <= '9')
+#endif
+
+#ifndef digit_value
+#define digit_value(c) ((c) - '0')
+#endif
+
+#define member(c, s) (strchr (s, c) != NULL)
+
+#define COMMAND_PREFIX '@'
+
+/* Stuff for splitting large files. */
+#define SPLIT_SIZE_THRESHOLD 70000  /* What's good enough for Stallman... */
+#define DEFAULT_SPLIT_SIZE 50000    /* Is probably good enough for me. */
+int splitting = 1;		    /* Always true for now. */
+
+typedef void COMMAND_FUNCTION (); /* So I can say COMMAND_FUNCTION *foo; */
+
+
+/* **************************************************************** */
+/*								    */
+/*			    Global Variables			    */
+/*								    */
+/* **************************************************************** */
+
+/* Global pointer to argv[0]. */
+char *progname;
+
+/* The current input file state. */
+char *input_filename;
+char *input_text;
+int size_of_input_text;
+int input_text_offset;
+int line_number;
+
+#define curchar() input_text[input_text_offset]
+
+#define command_char(c) ((!whitespace(c)) && \
+			 ((c) != '\n') && \
+			 ((c) != '{') && \
+			 ((c) != '}') && \
+			 ((c) != '='))
+
+#define skip_whitespace() while (input_text_offset != size_of_input_text \
+				 && whitespace(curchar()))\
+  input_text_offset++
+
+#define skip_whitespace_and_newlines() \
+  do { \
+   while (input_text_offset != size_of_input_text \
+	  && (whitespace (curchar ()) \
+	      || (curchar () == '\n'))) \
+      { \
+	 if (curchar () == '\n') \
+	   line_number++; \
+	 input_text_offset++; \
+      } \
+   } while (0)
+
+/* Return non-zero if STRING is the text at input_text + input_text_offset,
+   else zero. */
+#define looking_at(string) \
+  (strncmp (input_text + input_text_offset, string, strlen (string)) == 0)
+
+/* And writing to the output. */
+
+/* The output file name. */
+char *output_filename = (char *)NULL;
+char *pretty_output_filename;
+
+/* Name of the output file that the user elected to pass on the command line.
+   Such a name overrides any name found with the @setfilename command. */
+char *command_output_filename = (char *)NULL;
+
+/* A colon separated list of directories to search for files included
+   with @include.  This can be controlled with the `-I' option to makeinfo. */
+char *include_files_path = (char *)NULL;
+
+/* Current output stream. */
+FILE *output_stream;
+
+/* Position in the output file. */
+int output_position;
+
+/* Output paragraph buffer. */
+unsigned char *output_paragraph;
+
+/* Offset into OUTPUT_PARAGRAPH. */
+int output_paragraph_offset;
+
+/* The output paragraph "cursor" horizontal position. */
+int output_column = 0;
+
+/* Non-zero means output_paragraph contains text. */
+int paragraph_is_open = 0;
+
+#define INITIAL_PARAGRAPH_SPACE 5000
+int paragraph_buffer_len = INITIAL_PARAGRAPH_SPACE;
+
+/* Filling.. */
+/* Non-zero indicates that filling will take place on long lines. */
+int filling_enabled = 1;
+
+/* Non-zero means that words are not to be split, even in long lines.  This
+   gets changed for cm_w (). */
+int non_splitting_words = 0;
+
+/* Non-zero indicates that filling a line also indents the new line. */
+int indented_fill = 0;
+
+/* The column at which long lines are broken. */
+int fill_column = 72;
+
+/* The amount of indentation to apply at the start of each line. */
+int current_indent = 0;
+
+/* The amount of indentation to add at the starts of paragraphs.
+   0 means don't change existing indentation at paragraph starts.
+   > 0 is amount to indent new paragraphs by.
+   < 0 means indent to column zero by removing indentation if necessary.
+
+   This is normally zero, but some people prefer paragraph starts to be
+   somewhat more indented than paragraph bodies.  A pretty value for
+   this is 3. */
+int paragraph_start_indent = PARAGRAPH_START_INDENT;
+
+/* Non-zero means that the use of paragraph_start_indent is inhibited.
+   @example uses this to line up the left columns of the example text.
+   A negative value for this variable is incremented each time it is used.
+   @noindent uses this to inhibit indentation for a single paragraph.  */
+int inhibit_paragraph_indentation = 0;
+
+/* Indentation that is pending insertion.  We have this for hacking lines
+   which look blank, but contain whitespace.  We want to treat those as
+   blank lines. */
+int pending_indent = 0;
+
+/* The amount that indentation increases/decreases by. */
+int default_indentation_increment = DEFAULT_INDENTATION_INCREMENT;
+
+/* Non-zero indicates that indentation is temporarily turned off. */
+int no_indent = 1;
+
+/* Non-zero means forcing output text to be flushright. */
+int force_flush_right = 0;
+
+/* Non-zero means that the footnote style for this document was set on
+   the command line, which overrides any other settings. */
+int footnote_style_preset = 0;
+
+/* Non-zero means that we automatically number footnotes that have no
+   specified marker. */
+int number_footnotes = 1;
+
+/* The current footnote number in this node.  Each time a new node is
+   started this is reset to 1. */
+int current_footnote_number = 1;
+
+/* Command name in the process of being hacked. */
+char *command;
+
+/* The index in our internal command table of the currently
+   executing command. */
+int command_index;
+
+/* A search string which is used to find a line defining a node. */
+char node_search_string[] =
+  { '\n', COMMAND_PREFIX, 'n', 'o', 'd', 'e', ' ', '\0' };
+
+/* A search string which is used to find a line defining a menu. */
+char menu_search_string[] =
+  { '\n', COMMAND_PREFIX, 'm', 'e', 'n', 'u', '\0' };
+
+/* A search string which is used to find the first @setfilename. */
+char setfilename_search[] =
+  { COMMAND_PREFIX,
+      's', 'e', 't', 'f', 'i', 'l', 'e', 'n', 'a', 'm', 'e', '\0' };
+
+/* A stack of file information records.  If a new file is read in with
+   "@input", we remember the old input file state on this stack. */
+typedef struct fstack
+{
+  struct fstack *next;
+  char *filename;
+  char *text;
+  int size;
+  int offset;
+  int line_number;
+} FSTACK;
+
+FSTACK *filestack = (FSTACK *) NULL;
+
+/* Stuff for nodes. */
+/* The current nodes node name. */
+char *current_node = (char *)NULL;
+
+/* The current nodes section level. */
+int current_section = 0;
+
+/* The filename of the current input file.  This is never freed. */
+char *node_filename = (char *)NULL;
+
+/* What we remember for each node. */
+typedef struct tentry
+{
+  struct tentry *next_ent;
+  char *node;		/* name of this node. */
+  char *prev;		/* name of "Prev:" for this node. */
+  char *next;		/* name of "Next:" for this node. */
+  char *up;		/* name of "Up:" for this node.   */
+  int position;		/* output file position of this node. */
+  int line_no;		/* defining line in source file. */
+  char *filename;	/* The file that this node was found in. */
+  int touched;		/* non-zero means this node has been referenced. */
+  int flags;		/* Room for growth.  Right now, contains 1 bit. */
+} TAG_ENTRY;
+
+/* If node-a has a "Next" for node-b, but node-b has no "Prev" for node-a,
+   we turn on this flag bit in node-b's tag entry.  This means that when
+   it is time to validate node-b, we don't report an additional error
+   if there was no "Prev" field. */
+#define PREV_ERROR 0x1
+#define NEXT_ERROR 0x2
+#define UP_ERROR   0x4
+#define NO_WARN	   0x8
+#define IS_TOP 	   0x10
+
+TAG_ENTRY *tag_table = (TAG_ENTRY *) NULL;
+
+#if defined (HAVE_MACROS)
+#define ME_RECURSE	0x01
+#define ME_QUOTE_ARG	0x02
+
+/* Macro definitions for user-defined commands. */
+typedef struct {
+  char *name;			/* Name of the macro. */
+  char **arglist;		/* Args to replace when executing. */
+  char *body;			/* Macro body. */
+  char *source_file;		/* File where this macro is defined. */
+  int source_lineno;		/* Line number within FILENAME. */
+  int inhibited;		/* Non-zero means make find_macro () fail. */
+  int flags;			/* ME_RECURSE, ME_QUOTE_ARG, etc. */
+} MACRO_DEF;
+
+static void add_macro (char *name, char **arglist, char *body,
+		       char *source_file, int source_lineno, int flags);
+static void execute_macro (MACRO_DEF *);
+static MACRO_DEF *find_macro (char *), *delete_macro (char *);
+#endif /* HAVE_MACROS */
+
+/* Menu reference, *note reference, and validation hacking. */
+
+/* The various references that we know about. */
+enum reftype
+{
+  menu_reference, followed_reference
+};
+
+/* A structure to remember references with.  A reference to a node is
+   either an entry in a menu, or a cross-reference made with [px]ref. */
+typedef struct node_ref
+{
+  struct node_ref *next;
+  char *node;			/* Name of node referred to. */
+  char *containing_node;	/* Name of node containing this reference. */
+  int line_no;			/* Line number where the reference occurs. */
+  int section;			/* Section level where the reference occurs. */
+  char *filename;		/* Name of file where the reference occurs. */
+  enum reftype type;		/* Type of reference, either menu or note. */
+} NODE_REF;
+
+/* The linked list of such structures. */
+NODE_REF *node_references = (NODE_REF *) NULL;
+
+/* Flag which tells us whether to examine menu lines or not. */
+int in_menu = 0;
+
+/* Non-zero means that we have seen "@top" once already. */
+int top_node_seen = 0;
+
+/* Non-zero means that we have seen a non-"@top" node already. */
+int non_top_node_seen = 0;
+
+/* Flags controlling the operation of the program. */
+
+/* Default is to notify users of bad choices. */
+int print_warnings = 1;
+
+/* Default is to check node references. */
+int validating = 1;
+
+/* Non-zero means do not output "Node: Foo" for node separations. */
+int no_headers = 0;
+
+/* Number of errors that we tolerate on a given fileset. */
+int max_error_level = 100;
+
+/* Maximum number of references to a single node before complaining. */
+int reference_warning_limit = 1000;
+
+/* Non-zero means print out information about what is going on when it
+   is going on. */
+int verbose_mode = 0;
+
+/* Non-zero means to be relaxed about the input file.  This is useful when
+   we can successfully format the input, but it doesn't strictly match our
+   somewhat pedantic ideas of correctness.  Right now, it affects what
+   @table and @itemize do without arguments. */
+int allow_lax_format = 0;
+
+/* Count of @ifinfo commands seen.  @ifinfo is handled specially
+   to allow non-hierarchical contstructions like
+
+   @ifinfo
+   @table foo
+   @end ifinfo
+*/
+int ifinfo_count = 0;
+
+/* The list of commands that we hack in texinfo.  Each one
+   has an associated function.  When the command is encountered in the
+   text, the associated function is called with START as the argument.
+   If the function expects arguments in braces, it remembers itself on
+   the stack.  When the corresponding close brace is encountered, the
+   function is called with END as the argument. */
+
+#define START 0
+#define END 1
+
+typedef struct brace_element
+{
+  struct brace_element *next;
+  COMMAND_FUNCTION *proc;
+  int pos, line;
+} BRACE_ELEMENT;
+
+BRACE_ELEMENT *brace_stack = (BRACE_ELEMENT *) NULL;
+
+/* Forward declarations. */
+#if !defined (HAVE_STRDUP)
+extern char *strdup ();
+#endif /* HAVE_STRDUP */
+
+static void convert_from_stream (FILE *, char *);
+static void convert_from_file (char *);
+static void top_defindex (char *, int);
+
+static void insert_self (), cm_ignore_line (void);
+
+static void
+  cm_asterisk (void), cm_dots (int), cm_bullet (int), cm_TeX (int),
+  cm_copyright (int), cm_code (int), cm_samp (int), cm_file (int),
+  cm_kbd (int), cm_key (int), cm_ctrl (int, int, int), cm_var (int, int, int),
+  cm_dfn (int, int), cm_emph (int), cm_strong (int, int), cm_cite (int, int),
+  cm_italic (int, int, int), cm_bold (int, int, int), cm_roman (int, int, int),
+  cm_title (int, int, int), cm_w (int, int, int), cm_refill (void),
+  cm_titlefont (int, int, int);
+
+static void
+  cm_chapter (void), cm_unnumbered (void), cm_appendix (void), cm_top (void),
+  cm_section (void), cm_unnumberedsec (void), cm_appendixsec (void),
+  cm_subsection (void), cm_unnumberedsubsec (void), cm_appendixsubsec (void),
+  cm_subsubsection (void), cm_unnumberedsubsubsec (void),
+  cm_appendixsubsubsec (void), cm_heading (void), cm_chapheading (void),
+  cm_subheading (void), cm_subsubheading (void), cm_majorheading (void),
+  cm_raisesections (void), cm_lowersections (void);
+
+/* All @defxxx commands map to cm_defun (). */
+static void cm_defun (void);
+
+static void
+  cm_node (void), cm_menu (void), cm_xref (int), cm_ftable (void),
+  cm_vtable (void), cm_pxref (int), cm_inforef (int), cm_quotation (void),
+  cm_display (void), cm_itemize (void), cm_enumerate (void), cm_table (void),
+  cm_itemx (void), cm_noindent (void), cm_setfilename (void), cm_br (void),
+  cm_sp (void), cm_group (void), cm_center (void), cm_include (void),
+  cm_bye (void), cm_item (void), cm_end (void), cm_infoinclude (void),
+  cm_ifinfo (void), cm_kindex (void), cm_cindex (void), cm_findex (void),
+  cm_pindex (void), cm_vindex (void), cm_tindex (void), cm_asis (void),
+  cm_synindex (void), cm_printindex (void), cm_minus (int),
+  cm_footnote (void), cm_force_abbreviated_whitespace (void),
+  cm_example (void), cm_smallexample (void), cm_lisp (void), cm_format (void),
+  cm_exdent (void), cm_defindex (void), cm_defcodeindex (void),
+  cm_sc (int, int, int), cm_result (int), cm_expansion (int), cm_equiv (int),
+  cm_print (int), cm_error (int), cm_point (int), cm_today (int),
+  cm_flushleft (void), cm_flushright (void), cm_smalllisp (void),
+  cm_math (void), cm_cartouche (void), cm_ignore_sentence_ender (void);
+
+/* Conditionals. */
+static void cm_set (void), cm_clear (void), cm_ifset (void), cm_ifclear (void);
+static void cm_value (int, int, int), cm_ifeq (void);
+
+#if defined (HAVE_MACROS)
+/* Define a user-defined command which is simple substitution. */
+static void cm_macro (void), cm_unmacro (void);
+#endif /* HAVE_MACROS */
+
+/* Options. */
+static void cm_paragraphindent (void), cm_footnotestyle (void);
+
+/* Internals. */
+static void do_nothing (void);
+static void command_name_condition (void);
+static void insert_self (void);
+static void misplaced_brace (void);
+static void cm_obsolete (int arg, int start, int end);
+
+static int error (char *, ...);
+static int line_error (char *, ...);
+static int warning (char *, ...);
+static void add_word_args (char *, ...);
+static void execute_string (char *, ...);
+
+static void print_version_info (void);
+static void memory_error (char *callers_name, int bytes_wanted);
+static void usage (FILE *stream, int exit_value);
+static void push_node_filename (void);
+static void pop_node_filename (void);
+static void remember_error (void);
+static void init_internals (void);
+static void init_paragraph (void);
+static void reader_loop (void);
+static void read_command (void);
+static void init_brace_stack (void);
+static void remember_brace (COMMAND_FUNCTION *proc);
+static void remember_brace_1 (COMMAND_FUNCTION *proc, int position);
+static void discard_braces (void);
+static void add_word (char *string);
+static void add_char (int character);
+static void insert (int character);
+static void flush_output (void);
+static void close_paragraph_with_lines (int lines);
+static void close_paragraph (void);
+static void ignore_blank_line (void);
+static void do_flush_right_indentation (void);
+static void start_paragraph (void);
+static void indent (int amount);
+static void init_insertion_stack (void);
+static void init_tag_table (void);
+static void write_tag_table (void);
+static void write_tag_table_internal (int indirect_p);
+static void normalize_node_name (char *string);
+static void validate_file (TAG_ENTRY *tag_tayble);
+static void split_file (char *filename, int size);
+static void validate_other_references (register NODE_REF *ref_list);
+static NODE_REF *find_node_reference (char *node, register NODE_REF *ref_list);
+static void do_enumeration (int type, char *default_string);
+static void handle_variable (int action);
+static void handle_variable_internal (int action, char *name);
+static void init_indices (void);
+static void undefindex (char *name);
+static void defindex (char *name, int code);
+static void gen_defindex (int code);
+static void define_user_command (char *name, COMMAND_FUNCTION *proc,
+				 int needs_braces_p);
+static void convert_from_loaded_file (char *name);
+static void free_pending_notes (void);
+static void output_pending_notes (void);
+static void free_node_references (void);
+static int set_paragraph_indent (char *string);
+static int set_footnote_style (char *string);
+static int self_delimiting (int character);
+static int search_forward (char *string, int from);
+static int validate (char *tag, int line, char *label);
+static void pop_and_call_brace (void);
+static char *expand_filename (char *filename, char *input_name);
+static char *filename_part (char *filename);
+static char *full_pathname (char *filename);
+static char *get_file_info_in_path (char *filename, char *path,
+				    struct stat *finfo);
+static char *glean_node_from_menu (int remember_reference);
+
+typedef struct
+{
+  char *name;
+  COMMAND_FUNCTION *proc;
+  int argument_in_braces;
+} COMMAND;
+
+/* Stuff for defining commands on the fly. */
+COMMAND **user_command_array = (COMMAND **) NULL;
+int user_command_array_len = 0;
+
+#define NO_BRACE_ARGS 0
+#define BRACE_ARGS 1
+
+static COMMAND CommandTable[] = {
+  { "!", cm_ignore_sentence_ender, NO_BRACE_ARGS },
+  { "'", insert_self, NO_BRACE_ARGS },
+  { "*", cm_asterisk, NO_BRACE_ARGS },
+  { ".", cm_ignore_sentence_ender, NO_BRACE_ARGS },
+  { ":", cm_force_abbreviated_whitespace, NO_BRACE_ARGS },
+  { "?", cm_ignore_sentence_ender, NO_BRACE_ARGS },
+  { "|", do_nothing, NO_BRACE_ARGS },
+  { "@", insert_self, NO_BRACE_ARGS },
+  { " ", insert_self, NO_BRACE_ARGS },
+  { "\n", insert_self, NO_BRACE_ARGS },
+  { "TeX", cm_TeX, BRACE_ARGS },
+  { "`", insert_self, NO_BRACE_ARGS },
+  { "appendix", cm_appendix, NO_BRACE_ARGS },
+  { "appendixsection", cm_appendixsec, NO_BRACE_ARGS },
+  { "appendixsec", cm_appendixsec, NO_BRACE_ARGS },
+  { "appendixsubsec", cm_appendixsubsec, NO_BRACE_ARGS },
+  { "appendixsubsubsec", cm_appendixsubsubsec, NO_BRACE_ARGS },
+  { "asis", cm_asis, BRACE_ARGS },
+  { "b", cm_bold, BRACE_ARGS },
+  { "br", cm_br, NO_BRACE_ARGS },
+  { "bullet", cm_bullet, BRACE_ARGS },
+  { "bye", cm_bye, NO_BRACE_ARGS },
+  { "c", cm_ignore_line, NO_BRACE_ARGS },
+  { "cartouche", cm_cartouche, NO_BRACE_ARGS },
+  { "center", cm_center, NO_BRACE_ARGS },
+  { "chapheading", cm_chapheading, NO_BRACE_ARGS },
+  { "chapter", cm_chapter, NO_BRACE_ARGS },
+  { "cindex", cm_cindex, NO_BRACE_ARGS },
+  { "cite", cm_cite, BRACE_ARGS },
+  { "clear", cm_clear, NO_BRACE_ARGS },
+  { "code", cm_code, BRACE_ARGS },
+  { "comment", cm_ignore_line, NO_BRACE_ARGS },
+  { "contents", do_nothing, NO_BRACE_ARGS },
+  { "copyright", cm_copyright, BRACE_ARGS },
+  { "ctrl", cm_ctrl, BRACE_ARGS },
+  { "defcodeindex", cm_defcodeindex, NO_BRACE_ARGS },
+  { "defindex", cm_defindex, NO_BRACE_ARGS },
+  { "dfn", cm_dfn, BRACE_ARGS },
+
+/* The `def' commands. */
+  { "deffn", cm_defun, NO_BRACE_ARGS },
+  { "deffnx", cm_defun, NO_BRACE_ARGS },
+  { "defun", cm_defun, NO_BRACE_ARGS },
+  { "defunx", cm_defun, NO_BRACE_ARGS },
+  { "defmac", cm_defun, NO_BRACE_ARGS },
+  { "defmacx", cm_defun, NO_BRACE_ARGS },
+  { "defspec", cm_defun, NO_BRACE_ARGS },
+  { "defspecx", cm_defun, NO_BRACE_ARGS },
+  { "defvr", cm_defun, NO_BRACE_ARGS },
+  { "defvrx", cm_defun, NO_BRACE_ARGS },
+  { "defvar", cm_defun, NO_BRACE_ARGS },
+  { "defvarx", cm_defun, NO_BRACE_ARGS },
+  { "defopt", cm_defun, NO_BRACE_ARGS },
+  { "defoptx", cm_defun, NO_BRACE_ARGS },
+  { "deftypefn", cm_defun, NO_BRACE_ARGS },
+  { "deftypefnx", cm_defun, NO_BRACE_ARGS },
+  { "deftypefun", cm_defun, NO_BRACE_ARGS },
+  { "deftypefunx", cm_defun, NO_BRACE_ARGS },
+  { "deftypevr", cm_defun, NO_BRACE_ARGS },
+  { "deftypevrx", cm_defun, NO_BRACE_ARGS },
+  { "deftypevar", cm_defun, NO_BRACE_ARGS },
+  { "deftypevarx", cm_defun, NO_BRACE_ARGS },
+  { "defcv", cm_defun, NO_BRACE_ARGS },
+  { "defcvx", cm_defun, NO_BRACE_ARGS },
+  { "defivar", cm_defun, NO_BRACE_ARGS },
+  { "defivarx", cm_defun, NO_BRACE_ARGS },
+  { "defop", cm_defun, NO_BRACE_ARGS },
+  { "defopx", cm_defun, NO_BRACE_ARGS },
+  { "defmethod", cm_defun, NO_BRACE_ARGS },
+  { "defmethodx", cm_defun, NO_BRACE_ARGS },
+  { "deftypemethod", cm_defun, NO_BRACE_ARGS },
+  { "deftypemethodx", cm_defun, NO_BRACE_ARGS },
+  { "deftp", cm_defun, NO_BRACE_ARGS },
+  { "deftpx", cm_defun, NO_BRACE_ARGS },
+/* The end of the `def' commands. */
+
+  { "display", cm_display, NO_BRACE_ARGS },
+  { "dots", cm_dots, BRACE_ARGS },
+  { "dmn", do_nothing, BRACE_ARGS },
+  { "emph", cm_emph, BRACE_ARGS },
+  { "end", cm_end, NO_BRACE_ARGS },
+  { "enumerate", cm_enumerate, NO_BRACE_ARGS },
+  { "equiv", cm_equiv, BRACE_ARGS },
+  { "error", cm_error, BRACE_ARGS },
+  { "example", cm_example, NO_BRACE_ARGS },
+  { "exdent", cm_exdent, NO_BRACE_ARGS },
+  { "expansion", cm_expansion, BRACE_ARGS },
+  { "file", cm_file, BRACE_ARGS },
+  { "findex", cm_findex, NO_BRACE_ARGS },
+  { "finalout", do_nothing, NO_BRACE_ARGS },
+  { "flushleft", cm_flushleft, NO_BRACE_ARGS },
+  { "flushright", cm_flushright, NO_BRACE_ARGS },
+  { "format", cm_format, NO_BRACE_ARGS },
+  { "ftable", cm_ftable, NO_BRACE_ARGS },
+  { "group", cm_group, NO_BRACE_ARGS },
+  { "heading", cm_heading, NO_BRACE_ARGS },
+  { "headings", cm_ignore_line, NO_BRACE_ARGS },
+  { "i", cm_italic, BRACE_ARGS },
+  { "iappendix", cm_appendix, NO_BRACE_ARGS },
+  { "iappendixsection", cm_appendixsec, NO_BRACE_ARGS },
+  { "iappendixsec", cm_appendixsec, NO_BRACE_ARGS },
+  { "iappendixsubsec", cm_appendixsubsec, NO_BRACE_ARGS },
+  { "iappendixsubsubsec", cm_appendixsubsubsec, NO_BRACE_ARGS },
+  { "ichapter", cm_chapter, NO_BRACE_ARGS },
+  { "ifclear", cm_ifclear, NO_BRACE_ARGS },
+  { "ifeq", cm_ifeq, NO_BRACE_ARGS },
+  { "ifhtml", command_name_condition, NO_BRACE_ARGS },
+  { "ifinfo", cm_ifinfo, NO_BRACE_ARGS },
+  { "ifset", cm_ifset, NO_BRACE_ARGS },
+  { "iftex", command_name_condition, NO_BRACE_ARGS },
+  { "ignore", command_name_condition, NO_BRACE_ARGS },
+  { "include", cm_include, NO_BRACE_ARGS },
+  { "inforef", cm_inforef, BRACE_ARGS },
+  { "input", cm_include, NO_BRACE_ARGS },
+  { "isection", cm_section, NO_BRACE_ARGS },
+  { "isubsection", cm_subsection, NO_BRACE_ARGS },
+  { "isubsubsection", cm_subsubsection, NO_BRACE_ARGS },
+  { "item", cm_item, NO_BRACE_ARGS },
+  { "itemize", cm_itemize, NO_BRACE_ARGS },
+  { "itemx", cm_itemx, NO_BRACE_ARGS },
+  { "iunnumbered", cm_unnumbered, NO_BRACE_ARGS },
+  { "iunnumberedsec", cm_unnumberedsec, NO_BRACE_ARGS },
+  { "iunnumberedsubsec", cm_unnumberedsubsec, NO_BRACE_ARGS },
+  { "iunnumberedsubsubsec", cm_unnumberedsubsubsec, NO_BRACE_ARGS },
+  { "kbd", cm_kbd, BRACE_ARGS },
+  { "key", cm_key, BRACE_ARGS },
+  { "kindex", cm_kindex, NO_BRACE_ARGS },
+  { "lowersections", cm_lowersections, NO_BRACE_ARGS },
+  { "lisp", cm_lisp, NO_BRACE_ARGS },
+#if defined (HAVE_MACROS)
+  { "macro", cm_macro, NO_BRACE_ARGS },
+#endif
+  { "majorheading", cm_majorheading, NO_BRACE_ARGS },
+  { "math", cm_math, BRACE_ARGS },
+  { "medbreak", cm_br, NO_BRACE_ARGS },
+  { "menu", cm_menu, NO_BRACE_ARGS },
+  { "minus", cm_minus, BRACE_ARGS },
+  { "need", cm_ignore_line, NO_BRACE_ARGS },
+  { "node", cm_node, NO_BRACE_ARGS },
+  { "noindent", cm_noindent, NO_BRACE_ARGS },
+  { "nwnode", cm_node, NO_BRACE_ARGS },
+  { "overfullrule", cm_ignore_line, NO_BRACE_ARGS },
+  { "page", do_nothing, NO_BRACE_ARGS },
+  { "pindex", cm_pindex, NO_BRACE_ARGS },
+  { "point", cm_point, BRACE_ARGS },
+  { "print", cm_print, BRACE_ARGS },
+  { "printindex", cm_printindex, NO_BRACE_ARGS },
+  { "pxref", cm_pxref, BRACE_ARGS },
+  { "quotation", cm_quotation, NO_BRACE_ARGS },
+  { "r", cm_roman, BRACE_ARGS },
+  { "raisesections", cm_raisesections, NO_BRACE_ARGS },
+  { "ref", cm_xref, BRACE_ARGS },
+  { "refill", cm_refill, NO_BRACE_ARGS },
+  { "result", cm_result, BRACE_ARGS },
+  { "samp", cm_samp, BRACE_ARGS },
+  { "sc", cm_sc, BRACE_ARGS },
+  { "section", cm_section, NO_BRACE_ARGS },
+  { "set", cm_set, NO_BRACE_ARGS },
+  { "setchapternewpage", cm_ignore_line, NO_BRACE_ARGS },
+  { "setchapterstyle", cm_ignore_line, NO_BRACE_ARGS },
+  { "setfilename", cm_setfilename, NO_BRACE_ARGS },
+  { "settitle", cm_ignore_line, NO_BRACE_ARGS },
+  { "shortcontents", do_nothing, NO_BRACE_ARGS },
+  { "shorttitlepage", cm_ignore_line, NO_BRACE_ARGS },
+  { "smallbook", cm_ignore_line, NO_BRACE_ARGS },
+  { "smallbreak", cm_br, NO_BRACE_ARGS },
+  { "smallexample", cm_smallexample, NO_BRACE_ARGS },
+  { "smalllisp", cm_smalllisp, NO_BRACE_ARGS },
+  { "sp", cm_sp, NO_BRACE_ARGS },
+  { "strong", cm_strong, BRACE_ARGS },
+  { "subheading", cm_subheading, NO_BRACE_ARGS },
+  { "subsection", cm_subsection, NO_BRACE_ARGS },
+  { "subsubheading", cm_subsubheading, NO_BRACE_ARGS },
+  { "subsubsection", cm_subsubsection, NO_BRACE_ARGS },
+  { "summarycontents", do_nothing, NO_BRACE_ARGS },
+  { "syncodeindex", cm_synindex, NO_BRACE_ARGS },
+  { "synindex", cm_synindex, NO_BRACE_ARGS },
+  { "t", cm_title, BRACE_ARGS },
+  { "table", cm_table, NO_BRACE_ARGS },
+  { "tex", command_name_condition, NO_BRACE_ARGS },
+  { "tindex", cm_tindex, NO_BRACE_ARGS },
+  { "titlefont", cm_titlefont, BRACE_ARGS },
+  { "titlepage", command_name_condition, NO_BRACE_ARGS },
+  { "titlespec", command_name_condition, NO_BRACE_ARGS },
+  { "today", cm_today, BRACE_ARGS },
+  { "top", cm_top, NO_BRACE_ARGS  },
+#if defined (HAVE_MACROS)
+  { "unmacro", cm_unmacro, NO_BRACE_ARGS },
+#endif
+  { "unnumbered", cm_unnumbered, NO_BRACE_ARGS },
+  { "unnumberedsec", cm_unnumberedsec, NO_BRACE_ARGS },
+  { "unnumberedsubsec", cm_unnumberedsubsec, NO_BRACE_ARGS },
+  { "unnumberedsubsubsec", cm_unnumberedsubsubsec, NO_BRACE_ARGS },
+  { "value", cm_value, BRACE_ARGS },
+  { "var", cm_var, BRACE_ARGS },
+  { "vindex", cm_vindex, NO_BRACE_ARGS },
+  { "vtable", cm_vtable, NO_BRACE_ARGS },
+  { "w", cm_w, BRACE_ARGS },
+  { "xref", cm_xref, BRACE_ARGS },
+  { "{", insert_self, NO_BRACE_ARGS },
+  { "}", insert_self, NO_BRACE_ARGS },
+
+  /* Some obsoleted commands. */
+  { "infotop", cm_obsolete, NO_BRACE_ARGS },
+  { "infounnumbered", cm_obsolete, NO_BRACE_ARGS },
+  { "infounnumberedsec", cm_obsolete, NO_BRACE_ARGS },
+  { "infounnumberedsubsec", cm_obsolete, NO_BRACE_ARGS },
+  { "infounnumberedsubsubsec", cm_obsolete, NO_BRACE_ARGS },
+  { "infoappendix", cm_obsolete, NO_BRACE_ARGS },
+  { "infoappendixsec", cm_obsolete, NO_BRACE_ARGS },
+  { "infoappendixsubsec", cm_obsolete, NO_BRACE_ARGS },
+  { "infoappendixsubsubsec", cm_obsolete, NO_BRACE_ARGS },
+  { "infochapter", cm_obsolete, NO_BRACE_ARGS },
+  { "infosection", cm_obsolete, NO_BRACE_ARGS },
+  { "infosubsection", cm_obsolete, NO_BRACE_ARGS },
+  { "infosubsubsection", cm_obsolete, NO_BRACE_ARGS },
+
+  /* Now @include does what this was supposed to. */
+  { "infoinclude", cm_infoinclude, NO_BRACE_ARGS },
+  { "footnote", cm_footnote, NO_BRACE_ARGS}, /* self-arg eater */
+  { "footnotestyle", cm_footnotestyle, NO_BRACE_ARGS },
+  { "paragraphindent", cm_paragraphindent, NO_BRACE_ARGS },
+
+  {(char *) NULL, (COMMAND_FUNCTION *) NULL, NO_BRACE_ARGS}};
+
+int major_version = 1;
+int minor_version = 63;
+
+struct option long_options[] =
+{
+  { "error-limit", 1, 0, 'e' },			/* formerly -el */
+  { "fill-column", 1, 0, 'f' },			/* formerly -fc */
+  { "footnote-style", 1, 0, 's' },		/* formerly -ft */
+  { "no-headers", 0, &no_headers, 1 },		/* Do not output Node: foo */
+  { "no-pointer-validate", 0, &validating, 0 }, /* formerly -nv */
+  { "no-validate", 0, &validating, 0 },		/* formerly -nv */
+  { "no-split", 0, &splitting, 0 },		/* formerly -ns */
+  { "no-warn", 0, &print_warnings, 0 },		/* formerly -nw */
+#if defined (HAVE_MACROS)
+  { "macro-expand", 1, 0, 'E' },
+#endif /* HAVE_MACROS */
+  { "number-footnotes", 0, &number_footnotes, 1 },
+  { "no-number-footnotes", 0, &number_footnotes, 0 },
+  { "output", 1, 0, 'o' },
+  { "paragraph-indent", 1, 0, 'p' },		/* formerly -pi */
+  { "reference-limit", 1, 0, 'r' },		/* formerly -rl */
+  { "verbose", 0, &verbose_mode, 1 },		/* formerly -verbose */
+  { "help", 0, 0, 'h' },
+  { "version", 0, 0, 'V' },
+  {NULL, 0, NULL, 0}
+};
+
+/* Values for calling handle_variable_internal (). */
+#define SET	1
+#define CLEAR	2
+#define IFSET	3
+#define IFCLEAR	4
+
+/* **************************************************************** */
+/*								    */
+/*			Main ()  Start of code  		    */
+/*					        		    */
+/* **************************************************************** */
+
+/* For each file mentioned in the command line, process it, turning
+   texinfo commands into wonderfully formatted output text. */
+int
+main (int argc, char **argv)
+{
+  extern int errors_printed;
+  int c, ind;
+  int reading_from_stdin = 0;
+
+  /* The name of this program is the last filename in argv[0]. */
+  progname = filename_part (argv[0]);
+
+  /* Parse argument flags from the input line. */
+  while ((c = getopt_long
+	  (argc, argv,
+#if defined (HAVE_MACROS)
+	   "D:E:U:I:f:o:p:e:r:s:V",
+#else
+	   "D:U:I:f:o:p:e:r:s:V",
+#endif /* !HAVE_MACROS */
+	   long_options, &ind))
+	 != EOF)
+    {
+      if (c == 0 && long_options[ind].flag == 0)
+	c = long_options[ind].val;
+
+      switch (c)
+	{
+	  /* User specified variable to set or clear? */
+	case 'D':
+	case 'U':
+	  handle_variable_internal ((c == 'D') ? SET : CLEAR, optarg);
+	  break;
+
+#if defined (HAVE_MACROS)
+	  /* Use specified a macro expansion output file? */
+	case 'E':
+	  if (!macro_expansion_output_stream)
+	    {
+	      macro_expansion_output_stream = fopen (optarg, "w");
+	      if (!macro_expansion_output_stream)
+		error ("Couldn't open macro expansion output \"%s\"", optarg);
+	    }
+	  else
+	    error ("Cannot specify more than one macro expansion output");
+	  break;
+#endif /* HAVE_MACROS */
+
+	  /* User specified include file path? */
+	case 'I':
+	  if (!include_files_path)
+	    include_files_path = strdup (".");
+
+	  include_files_path = (char *)
+	    xrealloc (include_files_path,
+		      2 + strlen (include_files_path) + strlen (optarg));
+	  strcat (include_files_path, ":");
+	  strcat (include_files_path, optarg);
+	  break;
+
+	  /* User specified fill_column? */
+	case 'f':
+	  if (sscanf (optarg, "%d", &fill_column) != 1)
+	    usage (stderr, FATAL);
+	  break;
+
+	  /* User specified output file? */
+	case 'o':
+	  command_output_filename = strdup (optarg);
+	  break;
+
+	  /* User specified paragraph indent (paragraph_start_index)? */
+	case 'p':
+	  if (set_paragraph_indent (optarg) < 0)
+	    usage (stderr, FATAL);
+	  break;
+
+	  /* User specified error level? */
+	case 'e':
+	  if (sscanf (optarg, "%d", &max_error_level) != 1)
+	    usage (stderr, FATAL);
+	  break;
+
+	  /* User specified reference warning limit? */
+	case 'r':
+	  if (sscanf (optarg, "%d", &reference_warning_limit) != 1)
+	    usage (stderr, FATAL);
+	  break;
+
+	  /* User specified footnote style? */
+	case 's':
+	  if (set_footnote_style (optarg) < 0)
+	    usage (stderr, FATAL);
+	  footnote_style_preset = 1;
+	  break;
+
+	case 'h':
+	  usage (stdout, NO_ERROR);
+	  break;
+
+	  /* User requested version info? */
+	case 'V':
+	  print_version_info ();
+	  exit (NO_ERROR);
+	  break;
+
+	case '?':
+	  usage (stderr, FATAL);
+	  break;
+	}
+    }
+
+  if (optind == argc)
+    {
+      /* Check to see if input is a file.  If so, process that. */
+      if (!isatty (fileno (stdin)))
+	reading_from_stdin = 1;
+      else
+	usage (stderr, FATAL);
+    }
+
+  /* If the user has specified --no-headers, this should imply --no-split.
+     Do that here.  I think it might also imply that we should ignore the
+     setfilename at the top of the file, but this might break some FSF things,
+     so I will hold off on that. */
+  if (no_headers)
+    {
+      splitting = 0;
+
+      /* If the user has not specified an output file, then use stdout by
+	 default. */
+      if (!command_output_filename)
+	command_output_filename = strdup ("-");
+    }
+
+  if (verbose_mode)
+    print_version_info ();
+
+  /* Remaining arguments are file names of texinfo files.
+     Convert them, one by one. */
+  if (!reading_from_stdin)
+    {
+      while (optind != argc)
+	convert_from_file (argv[optind++]);
+    }
+  else
+    convert_from_stream (stdin, "stdin");
+
+  if (errors_printed)
+    return (SYNTAX);
+  else
+    return (NO_ERROR);
+}
+
+/* Display the version info of this invocation of Makeinfo. */
+static void
+print_version_info (void)
+{
+  printf ("This is GNU Makeinfo version %d.%d.\n",
+	  major_version, minor_version);
+}
+
+/* **************************************************************** */
+/*								    */
+/*			Generic Utilities			    */
+/*								    */
+/* **************************************************************** */
+
+#if !defined (HAVE_STRDUP)
+char *
+strdup (string)
+     char *string;
+{
+  char *result;
+
+  result = (char *)xmalloc (1 + strlen (string));
+  strcpy (result, string);
+
+  return (result);
+}
+#endif /* !HAVE_STRDUP */
+
+static void
+memory_error (callers_name, bytes_wanted)
+     char *callers_name;
+     int bytes_wanted;
+{
+  char printable_string[80];
+
+  sprintf (printable_string,
+	   "Virtual memory exhausted in %s ()!  Needed %d bytes.",
+	   callers_name, bytes_wanted);
+
+  error (printable_string);
+  abort ();
+}
+
+/* Just like malloc, but kills the program in case of fatal error. */
+static void *
+xmalloc (unsigned int nbytes)
+{
+  void *temp = (void *) malloc (nbytes);
+
+  if (nbytes && temp == (void *)NULL)
+    memory_error ("xmalloc", nbytes);
+
+  return (temp);
+}
+
+/* Like realloc (), but barfs if there isn't enough memory. */
+static void *
+xrealloc (void *pointer, unsigned int nbytes)
+{
+  void *temp;
+
+  if (!pointer)
+    temp = (void *)xmalloc (nbytes);
+  else
+    temp = (void *)realloc (pointer, nbytes);
+
+  if (nbytes && !temp)
+    memory_error ("xrealloc", nbytes);
+
+  return (temp);
+}
+
+/* Tell the user how to use this program.
+   Print the message to STREAM, and then exit with EXIT_VALUE. */
+static void
+usage (FILE *stream, int exit_value)
+{
+  fprintf (stream, "Usage: %s [options] texinfo-file...\n\
+\n\
+This program accepts as input files of texinfo commands and text\n\
+and outputs a file suitable for reading with GNU Info.\n\
+\n\
+Options:\n\
+`-I DIR'              add DIR to the directory search list for including\n\
+                      files with the `@include' command.\n\
+-D VAR                define a variable, as with `@set'.\n\
+-U VAR                undefine a variable, as with `@clear'.\n\
+-E MACRO-OFILE	    process macros, and output texinfo source code for TeX.\n\
+--no-validate         suppress node cross reference validation.\n\
+--no-warn             suppress warning messages (errors are still output).\n\
+--no-split            suppress the splitting of large files.\n\
+--no-headers          suppress the output of Node: Foo headers.\n\
+--verbose             print information about what is being done.\n\
+--version             print the version number of Makeinfo.\n\
+--output FILE or -o FILE\n\
+                      specify the output file.  When you specify the\n\
+                      output file in this way, any `@setfilename' in the\n\
+                      input file is ignored.\n\
+--paragraph-indent NUM\n\
+                      set the paragraph indent to NUM (default %d).\n\
+--fill-column NUM     set the filling column to NUM (default %d).\n\
+--error-limit NUM     set the error limit to NUM (default %d).\n\
+--reference-limit NUM\n\
+                      set the reference warning limit to NUM (default %d).\n\
+--footnote-style STYLE\n\
+                      set the footnote style to STYLE.  STYLE should\n\
+                      either be `separate' to place footnotes in their own\n\
+                      node, or `end', to place the footnotes at the end of\n\
+                      the node in which they are defined (the default).\n\
+--help                print this message and exit.\n\n",
+	   progname, paragraph_start_indent,
+	   fill_column, max_error_level, reference_warning_limit);
+  exit (exit_value);
+}
+
+#if defined (MSDOS)
+
+static struct passwd *
+getpwnam (char *name)
+{
+  return (struct passwd *) 0;
+}
+
+#endif /* MSDOS */
+
+/* **************************************************************** */
+/*								    */
+/*			Manipulating Lists      		    */
+/*					        		    */
+/* **************************************************************** */
+
+typedef struct generic_list {
+  struct generic_list *next;
+} GENERIC_LIST;
+
+/* Reverse the chain of structures in LIST.  Output the new head
+   of the chain.  You should always assign the output value of this
+   function to something, or you will lose the chain. */
+static GENERIC_LIST *
+reverse_list (register GENERIC_LIST *list)
+{
+  register GENERIC_LIST *next;
+  register GENERIC_LIST *prev = (GENERIC_LIST *) NULL;
+
+  while (list)
+    {
+      next = list->next;
+      list->next = prev;
+      prev = list;
+      list = next;
+    }
+  return (prev);
+}
+
+
+/* **************************************************************** */
+/*								    */
+/*			Pushing and Popping Files       	    */
+/*								    */
+/* **************************************************************** */
+
+/* Find and load the file named FILENAME.  Return a pointer to
+   the loaded file, or NULL if it can't be loaded. */
+static char *
+find_and_load (char *filename)
+{
+  struct stat fileinfo;
+  long file_size;
+  int file = -1, count = 0;
+  char *fullpath, *result;
+
+  result = fullpath = (char *)NULL;
+
+  fullpath = get_file_info_in_path (filename, include_files_path, &fileinfo);
+
+  if (!fullpath)
+    goto error_exit;
+
+  filename = fullpath;
+  file_size = (long) fileinfo.st_size;
+
+  file = open (filename, O_RDONLY);
+  if (file < 0)
+    goto error_exit;
+
+  /* Load the file. */
+  result = (char *)xmalloc (1 + file_size);
+
+  /* VMS stat lies about the st_size value.  The actual number of
+     readable bytes is always less than this value.  The arcane
+     mysteries of VMS/RMS are too much to probe, so this hack
+     suffices to make things work.
+    
+     BPW: same business applies under MS-DOS.  */
+#if defined (VMS) || defined (MSDOS)
+  while ((n = read (file, result + count, file_size)) > 0)
+    count += n;
+  if (n == -1)
+#else /* !(VMS && MSDOS) */
+    count = file_size;
+    if (read (file, result, file_size) != file_size)
+#endif /* !(VMS && MSDOS) */
+  error_exit:
+    {
+      if (result)
+	free (result);
+
+      if (fullpath)
+	free (fullpath);
+
+      if (file != -1)
+	close (file);
+
+      return ((char *) NULL);
+    }
+  close (file);
+
+  /* Set the globals to the new file. */
+  input_text = result;
+  size_of_input_text = count;
+  input_filename = strdup (fullpath);
+  node_filename = strdup (fullpath);
+  input_text_offset = 0;
+  line_number = 1;
+  /* Not strictly necessary.  This magic prevents read_token () from doing
+     extra unnecessary work each time it is called (that is a lot of times).
+     The SIZE_OF_INPUT_TEXT is one past the actual end of the text. */
+  input_text[size_of_input_text] = '\n';
+  return (result);
+}
+
+/* Save the state of the current input file. */
+static void
+pushfile (void)
+{
+  FSTACK *newstack = (FSTACK *) xmalloc (sizeof (FSTACK));
+  newstack->filename = input_filename;
+  newstack->text = input_text;
+  newstack->size = size_of_input_text;
+  newstack->offset = input_text_offset;
+  newstack->line_number = line_number;
+  newstack->next = filestack;
+
+  filestack = newstack;
+  push_node_filename ();
+}
+
+/* Make the current file globals be what is on top of the file stack. */
+static void
+popfile (void)
+{
+  FSTACK *tos = filestack;
+
+  if (!tos)
+    abort ();			/* My fault.  I wonder what I did? */
+
+#if defined (HAVE_MACROS)
+  if (macro_expansion_output_stream)
+    {
+      maybe_write_itext (input_text, input_text_offset);
+      forget_itext (input_text);
+    }
+#endif /* HAVE_MACROS */
+
+  /* Pop the stack. */
+  filestack = filestack->next;
+
+  /* Make sure that commands with braces have been satisfied. */
+  if (!executing_string)
+    discard_braces ();
+
+  /* Get the top of the stack into the globals. */
+  input_filename = tos->filename;
+  input_text = tos->text;
+  size_of_input_text = tos->size;
+  input_text_offset = tos->offset;
+  line_number = tos->line_number;
+  free (tos);
+
+  /* Go back to the (now) current node. */
+  pop_node_filename ();
+}
+
+/* Flush all open files on the file stack. */
+static void
+flush_file_stack (void)
+{
+  while (filestack)
+    {
+      char *fname = input_filename;
+      char *text = input_text;
+      popfile ();
+      free (fname);
+      free (text);
+    }
+}
+
+int node_filename_stack_index = 0;
+int node_filename_stack_size = 0;
+char **node_filename_stack = (char **)NULL;
+
+static void
+push_node_filename (void)
+{
+  if (node_filename_stack_index + 1 > node_filename_stack_size)
+    {
+      if (!node_filename_stack)
+	node_filename_stack =
+	  (char **)xmalloc ((node_filename_stack_size += 10)
+			    * sizeof (char *));
+      else
+	node_filename_stack =
+	  (char **)xrealloc (node_filename_stack,
+			     (node_filename_stack_size + 10)
+			     * sizeof (char *));
+    }
+
+  node_filename_stack[node_filename_stack_index] = node_filename;
+  node_filename_stack_index++;
+}
+
+static void
+pop_node_filename (void)
+{
+  node_filename = node_filename_stack[--node_filename_stack_index];
+}
+
+/* Return just the simple part of the filename; i.e. the
+   filename without the path information, or extensions.
+   This conses up a new string. */
+static char *
+filename_part (char *filename)
+{
+  char *basename;
+
+  basename = strrchr (filename, '/');
+  if (!basename)
+    basename = filename;
+  else
+    basename++;
+
+  basename = strdup (basename);
+#if defined (REMOVE_OUTPUT_EXTENSIONS)
+
+  /* See if there is an extension to remove.  If so, remove it. */
+  {
+    char *temp;
+
+    temp = strrchr (basename, '.');
+    if (temp)
+      *temp = '\0';
+  }
+#endif /* REMOVE_OUTPUT_EXTENSIONS */
+  return (basename);
+}
+
+/* Return the pathname part of filename.  This can be NULL. */
+static char *
+pathname_part (char *filename)
+{
+  char *result = (char *) NULL;
+  register int i;
+
+  filename = expand_filename (filename, "");
+
+  i = strlen (filename) - 1;
+
+  while (i && filename[i] != '/')
+    i--;
+  if (filename[i] == '/')
+    i++;
+
+  if (i)
+    {
+      result = (char *)xmalloc (1 + i);
+      strncpy (result, filename, i);
+      result[i] = '\0';
+    }
+  free (filename);
+  return (result);
+}
+
+static char *
+filename_non_directory (char *name)
+{
+  register int i;
+
+  for (i = strlen (name) - 1; i; i--)
+    if (name[i] == '/')
+      return (strdup (name + i + 1));
+
+  return (strdup (name));
+}
+
+/* Return the expansion of FILENAME. */
+static char *
+expand_filename (char *filename, char *input_name)
+{
+  register int i;
+
+  if (filename)
+    filename = full_pathname (filename);
+  else
+    {
+      filename = filename_non_directory (input_name);
+
+      if (!*filename)
+	{
+	  free (filename);
+	  filename = strdup ("noname.texi");
+	}
+
+      for (i = strlen (filename) - 1; i; i--)
+	if (filename[i] == '.')
+	  break;
+
+      if (!i)
+	i = strlen (filename);
+
+      if (i + 6 > (int) (strlen (filename)))
+	filename = (char *)xrealloc (filename, i + 6);
+      strcpy (filename + i, ".info");
+      return (filename);
+    }
+	
+  if (filename[0] == '.' || filename[0] == '/')
+    return (filename);
+
+  if (filename[0] != '/' && input_name[0] == '/')
+    {
+      /* Make it so that relative names work. */
+      char *result;
+      
+      i = strlen (input_name) - 1;
+
+      result = (char *)xmalloc (1 + strlen (input_name) + strlen (filename));
+      strcpy (result, input_name);
+
+      while (result[i] != '/' && i)
+	i--;
+
+      if (result[i] == '/')
+	i++;
+
+      strcpy (&result[i], filename);
+      free (filename);
+      return (result);
+    }
+  return (filename);
+}
+
+/* Return the full path to FILENAME. */
+static char *
+full_pathname (char *filename)
+{
+  int initial_character;
+  char *result;
+
+  /* No filename given? */
+  if (!filename || !(initial_character = *filename))
+    return (strdup (""));
+  
+  /* Already absolute? */
+  if ((initial_character == '/') ||
+      ((strncmp (filename, "./", 2) == 0) ||
+       (strncmp (filename, "../", 3) == 0)))
+    return (strdup (filename));
+
+  if (initial_character != '~')
+    {
+      char *localdir;
+
+      localdir = (char *)xmalloc (1025);
+#if defined (HAVE_GETWD)
+      if (!getwd (localdir))
+#else  /*  !HAVE_GETWD */
+      if (!getcwd (localdir, 1024))
+#endif /* !HAVE_GETWD */
+	  {
+	    fprintf (stderr, "%s: getwd: %s, %s\n",
+		     progname, filename, localdir);
+	    exit (1);
+	  }
+
+      strcat (localdir, "/");
+      strcat (localdir, filename);
+      result = strdup (localdir);
+      free (localdir);
+    }
+  else
+    {
+      if (filename[1] == '/')
+	{
+	  /* Return the concatenation of the environment variable HOME
+	     and the rest of the string. */
+	  char *temp_home;
+
+	  temp_home = (char *) getenv ("HOME");
+	  result = (char *)xmalloc (strlen (&filename[1])
+				    + 1
+				    + temp_home ? strlen (temp_home)
+				    : 0);
+	  *result = '\0';
+
+	  if (temp_home)
+	    strcpy (result, temp_home);
+
+	  strcat (result, &filename[1]);
+	}
+      else
+	{
+	  struct passwd *user_entry;
+	  int i, c;
+	  char *username = (char *)xmalloc (257);
+
+	  for (i = 1; (c = filename[i]); i++)
+	    {
+	      if (c == '/')
+		break;
+	      else
+		username[i - 1] = c;
+	    }
+	  if (c)
+	    username[i - 1] = '\0';
+
+	  user_entry = getpwnam (username);
+
+	  if (!user_entry)
+	    return (strdup (filename));
+
+	  result = (char *)xmalloc (1 + strlen (user_entry->pw_dir)
+				    + strlen (&filename[i]));
+	  strcpy (result, user_entry->pw_dir);
+	  strcat (result, &filename[i]);
+	}
+    }
+  return (result);
+}
+
+static char *
+output_name_from_input_name (char *name)
+{
+  return (expand_filename ((char *)NULL, name));
+}
+
+/* **************************************************************** */
+/*								    */
+/*			Error Handling				    */
+/*								    */
+/* **************************************************************** */
+
+/* Number of errors encountered. */
+int errors_printed = 0;
+
+/* Print the last error gotten from the file system. */
+static int
+fs_error (char *filename)
+{
+  remember_error ();
+  perror (filename);
+  return (0);
+}
+
+static int
+error (char *format, ...)
+{
+  va_list ap;
+
+  remember_error ();
+  va_start (ap, format);
+  vfprintf (stderr, format, ap);
+  fprintf (stderr, "\n");
+  va_end (ap);
+  return ((int) 0);
+}
+
+/* Just like error (), but print the line number as well. */
+static int
+line_error (char *format, ...)
+{
+  va_list ap;
+
+  remember_error ();
+  va_start (ap, format);
+  fprintf (stderr, "%s:%d: ", input_filename, line_number);
+  vfprintf (stderr, format, ap);
+  fprintf (stderr, ".\n");
+  va_end (ap);
+  return ((int) 0);
+}
+
+static int
+warning (char *format, ...)
+{
+  va_list ap;
+
+  va_start (ap, format);
+  if (print_warnings)
+    {
+      fprintf (stderr, "%s:%d: Warning: ", input_filename, line_number);
+      vfprintf (stderr, format, ap);
+      fprintf (stderr, ".\n");
+    }
+  va_end (ap);
+  return ((int) 0);
+}
+
+/* Remember that an error has been printed.  If this is the first
+   error printed, then tell them which program is printing them.
+   If more than max_error_level have been printed, then exit the
+   program. */
+static void
+remember_error (void)
+{
+  errors_printed++;
+  if (max_error_level && (errors_printed > max_error_level))
+    {
+      fprintf (stderr, "Too many errors!  Gave up.\n");
+      flush_file_stack ();
+      cm_bye ();
+      exit (1);
+    }
+}
+
+/* **************************************************************** */
+/*								    */
+/*			Hacking Tokens and Strings		    */
+/*								    */
+/* **************************************************************** */
+
+/* Return the next token as a string pointer.  We cons the
+   string. */
+static char *
+read_token (void)
+{
+  int i, character;
+  char *result;
+
+  /* If the first character to be read is self-delimiting, then that
+     is the command itself. */
+  character = curchar ();
+  if (self_delimiting (character))
+    {
+      input_text_offset++;
+      result = strdup (" ");
+      *result = character;
+      return (result);
+    }
+
+  for (i = 0; ((input_text_offset != size_of_input_text)
+	       && (character = curchar ())
+	       && command_char (character));
+       i++, input_text_offset++);
+  result = (char *)xmalloc (i + 1);
+  memcpy (result, &input_text[input_text_offset - i], i);
+  result[i] = '\0';
+  return (result);
+}
+
+/* Return non-zero if CHARACTER is self-delimiting. */
+static int
+self_delimiting (int character)
+{
+  return (member (character, "{}:.@*'`,!?; \n\t"));
+}
+
+/* Clear whitespace from the front and end of string. */
+static void
+canon_white (char *string)
+{
+  int len = strlen (string);
+  int x;
+
+  if (!len)
+    return;
+
+  for (x = 0; x < len; x++)
+    {
+      if (!cr_or_whitespace (string[x]))
+	{
+	  strcpy (string, string + x);
+	  break;
+	}
+    }
+  len = strlen (string);
+  if (len)
+    len--;
+  while (len > -1 && cr_or_whitespace (string[len]))
+    len--;
+  string[len + 1] = '\0';
+}
+
+/* Bash STRING, replacing all whitespace with just one space. */
+static void
+fix_whitespace (char *string)
+{
+  char *temp = (char *)xmalloc (strlen (string) + 1);
+  int string_index = 0;
+  int temp_index = 0;
+  int c;
+
+  canon_white (string);
+
+  while (string[string_index])
+    {
+      c = temp[temp_index++] = string[string_index++];
+
+      if (c == ' ' || c == '\n' || c == '\t')
+	{
+	  temp[temp_index - 1] = ' ';
+	  while ((c = string[string_index]) && (c == ' ' ||
+						c == '\t' ||
+						c == '\n'))
+	    string_index++;
+	}
+    }
+  temp[temp_index] = '\0';
+  strcpy (string, temp);
+  free (temp);
+}
+
+/* Discard text until the desired string is found.  The string is
+   included in the discarded text. */
+static void
+discard_until (char *string)
+{
+  int temp = search_forward (string, input_text_offset);
+
+  int tt = (temp < 0) ? size_of_input_text : temp + strlen (string);
+  int from = input_text_offset;
+
+  /* Find out what line we are on. */
+  while (from != tt)
+    if (input_text[from++] == '\n')
+      line_number++;
+
+  if (temp < 0)
+    {
+      input_text_offset = size_of_input_text - strlen (string);
+
+      if (strcmp (string, "\n") != 0)
+	{
+	  line_error ("Expected `%s'", string);
+	  return;
+	}
+    }
+  else
+    input_text_offset = temp;
+
+  input_text_offset += strlen (string);
+}
+
+/* Read characters from the file until we are at MATCH.
+   Place the characters read into STRING.
+   On exit input_text_offset is after the match string.
+   Return the offset where the string starts. */
+static int
+get_until (char *match, char **string)
+{
+  int len, current_point, x, new_point, tem;
+
+  current_point = x = input_text_offset;
+  new_point = search_forward (match, input_text_offset);
+
+  if (new_point < 0)
+    new_point = size_of_input_text;
+  len = new_point - current_point;
+
+  /* Keep track of which line number we are at. */
+  tem = new_point + (strlen (match) - 1);
+  while (x != tem)
+    if (input_text[x++] == '\n')
+      line_number++;
+
+  *string = (char *)xmalloc (len + 1);
+
+  memcpy (*string, &input_text[current_point], len);
+  (*string)[len] = '\0';
+
+  /* Now leave input_text_offset in a consistent state. */
+  input_text_offset = tem;
+
+  if (input_text_offset > size_of_input_text)
+    input_text_offset = size_of_input_text;
+
+  return (new_point);
+}
+
+/* Read characters from the file until we are at MATCH or end of line.
+   Place the characters read into STRING.  */
+static void
+get_until_in_line (char *match, char **string)
+{
+  int real_bottom, temp;
+
+  real_bottom = size_of_input_text;
+  temp = search_forward ("\n", input_text_offset);
+
+  if (temp < 0)
+    temp = size_of_input_text;
+
+  size_of_input_text = temp;
+  get_until (match, string);
+  size_of_input_text = real_bottom;
+}
+
+static void
+get_rest_of_line (char **string)
+{
+  get_until ("\n", string);
+  canon_white (*string);
+
+  if (curchar () == '\n')	/* as opposed to the end of the file... */
+    {
+      line_number++;
+      input_text_offset++;
+    }
+}
+
+/* Backup the input pointer to the previous character, keeping track
+   of the current line number. */
+static void
+backup_input_pointer (void)
+{
+  if (input_text_offset)
+    {
+      input_text_offset--;
+      if (curchar () == '\n')
+	line_number--;
+    }
+}
+
+/* Read characters from the file until we are at MATCH or closing brace.
+   Place the characters read into STRING.  */
+static void
+get_until_in_braces (char *match, char **string)
+{
+  int i, brace = 0;
+  int match_len = strlen (match);
+  char *temp;
+
+  for (i = input_text_offset; i < size_of_input_text; i++)
+    {
+      if (input_text[i] == '{')
+	brace++;
+      else if (input_text[i] == '}')
+	brace--;
+      else if (input_text[i] == '\n')
+	line_number++;
+
+      if (brace < 0 ||
+	  (brace == 0 && strncmp (input_text + i, match, match_len) == 0))
+	break;
+    }
+
+  match_len = i - input_text_offset;
+  temp = (char *)xmalloc (2 + match_len);
+  strncpy (temp, input_text + input_text_offset, match_len);
+  temp[match_len] = '\0';
+  input_text_offset = i;
+  *string = temp;
+}
+
+/* **************************************************************** */
+/*								    */
+/*			Converting the File     		    */
+/*								    */
+/* **************************************************************** */
+
+/* Convert the file named by NAME.  The output is saved on the file
+   named as the argument to the @setfilename command. */
+static char *suffixes[] = {
+  "",
+  ".texinfo",
+  ".texi",
+  ".txinfo",
+  (char *)NULL
+};
+
+static void
+initialize_conversion (void)
+{
+  init_tag_table ();
+  init_indices ();
+  init_internals ();
+  init_paragraph ();
+}
+
+  /* We read in multiples of 4k, simply because it is a typical pipe size
+     on unix systems. */
+#define _READ_BUFFER_GROWTH (4 * 4096)
+
+/* Convert the texinfo file coming from the open stream STREAM.  Assume the
+   source of the stream is named NAME. */
+static void
+convert_from_stream (FILE *stream, char *name)
+{
+  char *buffer = (char *)NULL;
+  int buffer_offset = 0, buffer_size = 0;
+
+  initialize_conversion ();
+
+  /* Read until the end of the stream.  This isn't strictly correct, since
+     the texinfo input may end before the stream ends, but it is a quick
+     working hueristic. */
+  while (!feof (stream))
+    {
+      int count;
+
+      if (buffer_offset + (_READ_BUFFER_GROWTH + 1) >= buffer_size)
+	buffer = (char *)
+	  xrealloc (buffer, (buffer_size += _READ_BUFFER_GROWTH));
+
+      count = fread (buffer + buffer_offset, 1, _READ_BUFFER_GROWTH, stream);
+
+      if (count < 0)
+	{
+	  perror (name);
+	  exit (FATAL);
+	}
+
+      buffer_offset += count;
+      if (count == 0)
+	break;
+    }
+
+  /* Set the globals to the new file. */
+  input_text = buffer;
+  size_of_input_text = buffer_offset;
+  input_filename = strdup (name);
+  node_filename = strdup (name);
+  input_text_offset = 0;
+  line_number = 1;
+
+  /* Not strictly necessary.  This magic prevents read_token () from doing
+     extra unnecessary work each time it is called (that is a lot of times).
+     The SIZE_OF_INPUT_TEXT is one past the actual end of the text. */
+  input_text[size_of_input_text] = '\n';
+
+  convert_from_loaded_file (name);
+}
+
+static void
+convert_from_file (char *name)
+{
+  register int i;
+  char *filename = (char *)xmalloc (strlen (name) + 50);
+
+  initialize_conversion ();
+
+  /* Try to load the file specified by NAME.  If the file isn't found, and
+     there is no suffix in NAME, then try NAME.texinfo, and NAME.texi. */
+  for (i = 0; suffixes[i]; i++)
+    {
+      strcpy (filename, name);
+      strcat (filename, suffixes[i]);
+
+      if (find_and_load (filename))
+	break;
+
+      if (!suffixes[i][0] && strrchr (filename, '.'))
+	{
+	  fs_error (filename);
+	  free (filename);
+	  return;
+	}
+    }
+
+  if (!suffixes[i])
+    {
+      fs_error (name);
+      free (filename);
+      return;
+    }
+
+  input_filename = filename;
+
+  convert_from_loaded_file (name);
+}
+  
+static void
+convert_from_loaded_file (char *name)
+{
+  char *real_output_filename = (char *)NULL;
+
+#if defined (HAVE_MACROS)
+  remember_itext (input_text, 0);
+#endif /* HAVE_MACROS */
+
+  /* Search this file looking for the special string which starts conversion.
+     Once found, we may truly begin. */
+  input_text_offset = 0;
+  while (input_text_offset >= 0)
+    {
+      input_text_offset =
+	search_forward (setfilename_search, input_text_offset);
+
+      if ((input_text_offset == 0) ||
+	  ((input_text_offset > 0) &&
+	   (input_text[input_text_offset -1] == '\n')))
+	break;
+      else if (input_text_offset > 0)
+	input_text_offset++;
+    }
+
+  if (input_text_offset < 0)
+    {
+      if (!command_output_filename)
+	{
+#if defined (REQUIRE_SETFILENAME)
+	  error ("No `%s' found in `%s'", setfilename_search, name);
+	  goto finished;
+#else
+	  register int i, end_of_first_line;
+
+	  /* Find the end of the first line in the file. */
+	  for (i = 0; i < size_of_input_text - 1; i++)
+	    if (input_text[i] == '\n')
+	      break;
+
+	  end_of_first_line = i + 1;
+
+	  input_text_offset = 0;
+
+	  for (i = 0; i < end_of_first_line; i++)
+	    {
+	      if ((input_text[i] == '\\') &&
+		  (strncmp (input_text + i + 1, "include", 7) == 0))
+		{
+		  input_text_offset = end_of_first_line;
+		  break;
+		}
+	    }
+	  command_output_filename = output_name_from_input_name (name);
+#endif /* !REQUIRE_SETFILENAME */
+	}
+    }
+  else
+    input_text_offset += strlen (setfilename_search);
+
+  if (!command_output_filename)
+    get_until ("\n", &output_filename);
+  else
+    {
+      if (input_text_offset != -1)
+	discard_until ("\n");
+      else
+	input_text_offset = 0;
+
+      real_output_filename = output_filename = command_output_filename;
+      command_output_filename = (char *)NULL;
+    }
+
+  canon_white (output_filename);
+
+  if (real_output_filename &&
+      strcmp (real_output_filename, "-") == 0)
+    {
+      real_output_filename = strdup (real_output_filename);
+      output_stream = stdout;
+      splitting = 0;		/* Cannot split when writing to stdout. */
+    }
+  else
+    {
+      if (!real_output_filename)
+	real_output_filename = expand_filename (output_filename, name);
+      else
+	real_output_filename = strdup (real_output_filename);
+
+      output_stream = fopen (real_output_filename, "w");
+    }
+
+  if (output_stream != stdout)
+    printf ("Making info file `%s' from `%s'.\n", output_filename, name);
+
+  if (output_stream == NULL)
+    {
+      fs_error (real_output_filename);
+      goto finished;
+    }
+
+  /* Make the displayable filename from output_filename.  Only the base
+     portion of the filename need be displayed. */
+  if (output_stream != stdout)
+    pretty_output_filename = filename_part (output_filename);
+  else
+    pretty_output_filename = strdup ("stdout");
+
+  /* For this file only, count the number of newlines from the top of
+     the file to here.  This way, we keep track of line numbers for
+     error reporting.  Line_number starts at 1, since the user isn't
+     zero-based. */
+  {
+    int temp = 0;
+    line_number = 1;
+    while (temp != input_text_offset)
+      if (input_text[temp++] == '\n')
+	line_number++;
+  }
+
+  if (!no_headers)
+    {
+      add_word_args ("This is Info file %s, produced by Makeinfo-%d.%d from ",
+		     output_filename, major_version, minor_version);
+      add_word_args ("the input file %s.\n", input_filename);
+    }
+
+  close_paragraph ();
+  reader_loop ();
+
+finished:
+  close_paragraph ();
+  flush_file_stack ();
+
+#if defined (HAVE_MACROS)
+  if (macro_expansion_output_stream)
+    fclose (macro_expansion_output_stream);
+#endif /* HAVE_MACROS */
+
+  if (output_stream != NULL)
+    {
+      output_pending_notes ();
+      free_pending_notes ();
+      if (tag_table != NULL)
+	{
+	  tag_table = (TAG_ENTRY *) reverse_list ((GENERIC_LIST *) tag_table);
+	  if (!no_headers)
+	    write_tag_table ();
+	}
+
+      if (output_stream != stdout)
+	fclose (output_stream);
+
+      /* If validating, then validate the entire file right now. */
+      if (validating)
+	validate_file (tag_table);
+
+      /* This used to test  && !errors_printed.
+	 But some files might have legit warnings.  So split anyway.  */
+      if (splitting)
+	split_file (real_output_filename, 0);
+    }
+  free (real_output_filename);
+}
+
+static void
+free_and_clear (char **pointer)
+{
+  if ((*pointer) != (char *) NULL)
+    {
+      free (*pointer);
+      *pointer = (char *) NULL;
+    }
+}
+
+ /* Initialize some state. */
+static void
+init_internals (void)
+{
+  free_and_clear (&current_node);
+  free_and_clear (&output_filename);
+  free_and_clear (&command);
+  free_and_clear (&input_filename);
+  free_node_references ();
+  init_insertion_stack ();
+  init_brace_stack ();
+  command_index = 0;
+  in_menu = 0;
+  top_node_seen = 0;
+  non_top_node_seen = 0;
+}
+
+static void
+init_paragraph (void)
+{
+  free_and_clear ((char **) &output_paragraph);
+  output_paragraph = (unsigned char *)xmalloc (paragraph_buffer_len);
+  output_position = 0;
+  output_paragraph[0] = '\0';
+  output_paragraph_offset = 0;
+  output_column = 0;
+  paragraph_is_open = 0;
+  current_indent = 0;
+}
+
+/* Okay, we are ready to start the conversion.  Call the reader on
+   some text, and fill the text as it is output.  Handle commands by
+   remembering things like open braces and the current file position on a
+   stack, and when the corresponding close brace is found, you can call
+   the function with the proper arguments. */
+static void
+reader_loop (void)
+{
+  int character;
+  int done = 0;
+  int dash_count = 0;
+
+  while (!done)
+    {
+      if (input_text_offset >= size_of_input_text)
+	break;
+
+      character = curchar ();
+
+      if (!in_fixed_width_font &&
+	  (character == '\'' || character == '`') &&
+	  input_text[input_text_offset + 1] == character)
+	{
+	  input_text_offset++;
+	  character = '"';
+	}
+
+      if (character == '-')
+	{
+	  dash_count++;
+	  if (dash_count == 2 && !in_fixed_width_font)
+	    {
+	      input_text_offset++;
+	      continue;
+	    }
+	}
+      else
+	{
+	  dash_count = 0;
+	}
+
+      /* If this is a whitespace character, then check to see if the line
+	 is blank.  If so, advance to the carriage return. */
+      if (whitespace (character))
+	{
+	  register int i = input_text_offset + 1;
+
+	  while (i < size_of_input_text && whitespace (input_text[i]))
+	    i++;
+
+	  if (i == size_of_input_text || input_text[i] == '\n')
+	    {
+	      if (i == size_of_input_text)
+		i--;
+
+	      input_text_offset = i;
+	      character = curchar ();
+	    }
+	}
+
+      if (character == '\n')
+	{
+	  line_number++;
+
+	  /* Check for a menu entry here, since the "escape sequence"
+	     that begins menu entrys is "\n* ". */
+	  if (in_menu && input_text_offset + 1 < size_of_input_text)
+	    {
+	      char *tem;
+
+	      /* Note that the value of TEM is discarded, since it is
+		 gauranteed to be NULL when glean_node_from_menu () is
+		 called with a non-zero argument. */
+	      tem = glean_node_from_menu (1);
+	    }
+	}
+
+      switch (character)
+	{
+	case COMMAND_PREFIX:
+	  read_command ();
+	  break;
+
+	case '{':
+
+	  /* Special case.  I'm not supposed to see this character by itself.
+	     If I do, it means there is a syntax error in the input text.
+	     Report the error here, but remember this brace on the stack so
+	     you can ignore its partner. */
+
+	  line_error ("Misplaced `{'");
+	  remember_brace (misplaced_brace);
+
+	  /* Don't advance input_text_offset since this happens in
+	     remember_brace ().
+	     input_text_offset++;
+           */
+	  break;
+
+	case '}':
+	  pop_and_call_brace ();
+	  input_text_offset++;
+	  break;
+
+	default:
+	  add_char (character);
+	  input_text_offset++;
+	}
+    }
+#if defined (HAVE_MACROS)
+  if (macro_expansion_output_stream)
+    maybe_write_itext (input_text, input_text_offset);
+#endif /* HAVE_MACROS */
+}
+
+/* Find the command corresponding to STRING.  If the command
+   is found, return a pointer to the data structure.  Otherwise
+   return (-1). */
+static COMMAND *
+get_command_entry (char *string)
+{
+  register int i;
+
+  for (i = 0; CommandTable[i].name; i++)
+    if (strcmp (CommandTable[i].name, string) == 0)
+      return (&CommandTable[i]);
+
+  /* This command is not in our predefined command table.  Perhaps
+     it is a user defined command. */
+  for (i = 0; i < user_command_array_len; i++)
+    if (user_command_array[i] &&
+	(strcmp (user_command_array[i]->name, string) == 0))
+      return (user_command_array[i]);
+
+  /* Nope, we never heard of this command. */
+  return ((COMMAND *) -1);
+}
+
+/* input_text_offset is right at the command prefix character.
+   Read the next token to determine what to do. */
+static void
+read_command (void)
+{
+  COMMAND *entry;
+
+  input_text_offset++;
+  free_and_clear (&command);
+  command = read_token ();
+
+#if defined (HAVE_MACROS)
+  /* Check to see if this command is a macro.  If so, execute it here. */
+  {
+    MACRO_DEF *def;
+
+    def = find_macro (command);
+
+    if (def)
+      {
+	/* We disallow recursive use of a macro call.  Inhibit the expansion
+	   of this macro during the life of its execution. */
+	if (!(def->flags & ME_RECURSE))
+	  def->inhibited = 1;
+
+	execute_macro (def);
+
+	if (!(def->flags & ME_RECURSE))
+	  def->inhibited = 0;
+
+	return;
+      }
+    }
+#endif /* HAVE_MACROS */
+
+  entry = get_command_entry (command);
+
+  if (entry == (COMMAND *)-1)
+    {
+      line_error ("Unknown info command `%s'", command);
+      return;
+    }
+
+  if (entry->argument_in_braces)
+    remember_brace (entry->proc);
+
+  (*(entry->proc)) (START, output_paragraph_offset, 0);
+}
+
+/* Return the string which invokes PROC; a pointer to a function. */
+static char *
+find_proc_name (COMMAND_FUNCTION *proc)
+{
+  register int i;
+
+  for (i = 0; CommandTable[i].name; i++)
+    if (proc == CommandTable[i].proc)
+      return (CommandTable[i].name);
+  return ("NO_NAME!");
+}
+
+static void
+init_brace_stack (void)
+{
+  brace_stack = (BRACE_ELEMENT *) NULL;
+}
+
+static void
+remember_brace (COMMAND_FUNCTION *proc)
+{
+  if (curchar () != '{')
+    line_error ("%c%s expected `{..}'", COMMAND_PREFIX, command);
+  else
+    input_text_offset++;
+  remember_brace_1 (proc, output_paragraph_offset);
+}
+
+/* Remember the current output position here.  Save PROC
+   along with it so you can call it later. */
+static void
+remember_brace_1 (COMMAND_FUNCTION *proc, int position)
+{
+  BRACE_ELEMENT *new = (BRACE_ELEMENT *) xmalloc (sizeof (BRACE_ELEMENT));
+  new->next = brace_stack;
+  new->proc = proc;
+  new->pos = position;
+  new->line = line_number;
+  brace_stack = new;
+}
+
+/* Pop the top of the brace stack, and call the associated function
+   with the args END and POS. */
+static void
+pop_and_call_brace (void)
+{
+  BRACE_ELEMENT *temp;
+  COMMAND_FUNCTION *proc;
+  int pos;
+
+  if (brace_stack == (BRACE_ELEMENT *) NULL)
+    {
+      line_error ("Unmatched close brace");
+      return;
+    }
+
+  pos = brace_stack->pos;
+  proc = brace_stack->proc;
+  temp = brace_stack->next;
+  free (brace_stack);
+  brace_stack = temp;
+
+  (*proc) (END, pos, output_paragraph_offset);
+}
+
+/* Shift all of the markers in `brace_stack' by AMOUNT. */
+static void
+adjust_braces_following (int here, int amount)
+{
+  register BRACE_ELEMENT *stack = brace_stack;
+
+  while (stack)
+    {
+      if (stack->pos >= here)
+	stack->pos += amount;
+      stack = stack->next;
+    }
+}
+
+/* You call discard_braces () when you shouldn't have any braces on the stack.
+   I used to think that this happens for commands that don't take arguments
+   in braces, but that was wrong because of things like @code{foo @@}.  So now
+   I only detect it at the beginning of nodes. */
+static void
+discard_braces (void)
+{
+  if (!brace_stack)
+    return;
+
+  while (brace_stack)
+    {
+      if (brace_stack->proc != misplaced_brace)
+	{
+	  char *proc_name;
+	  int temp_line_number = line_number;
+
+	  line_number = brace_stack->line;
+	  proc_name = find_proc_name (brace_stack->proc);
+	  line_error ("%c%s missing close brace", COMMAND_PREFIX, proc_name);
+	  line_number = temp_line_number;
+	  pop_and_call_brace ();
+	}
+      else
+	{
+	  BRACE_ELEMENT *temp;
+	  temp = brace_stack->next;
+	  free (brace_stack);
+	  brace_stack = temp;
+	}
+    }
+}
+
+static int
+get_char_len (int character)
+{
+  /* Return the printed length of the character. */
+  int len;
+
+  switch (character)
+    {
+    case '\t':
+      len = (output_column + 8) & 0xf7;
+      if (len > fill_column)
+	len = fill_column - output_column;
+      else
+	len = len - output_column;
+      break;
+
+    case '\n':
+      len = fill_column - output_column;
+      break;
+
+    default:
+      if (character < ' ')
+	len = 2;
+      else
+	len = 1;
+    }
+  return (len);
+}
+
+static void
+add_word_args (char *format, ...)
+{
+  va_list ap;
+  char buffer[1000];
+
+  va_start (ap, format);
+  vsprintf (buffer, format, ap);
+  add_word (buffer);
+  va_end (ap);
+}
+
+/* Add STRING to output_paragraph. */
+static void
+add_word (char *string)
+{
+  while (*string)
+    add_char (*string++);
+}
+
+/* Non-zero if the last character inserted has the syntax class of NEWLINE. */
+int last_char_was_newline = 1;
+
+/* The actual last inserted character.  Note that this may be something
+   other than NEWLINE even if last_char_was_newline is 1. */
+int last_inserted_character = 0;
+
+/* Non-zero means that a newline character has already been
+   inserted, so close_paragraph () should insert one less. */
+int line_already_broken = 0;
+
+/* When non-zero we have finished an insertion (see end_insertion ()) and we
+   want to ignore false continued paragraph closings. */
+int insertion_paragraph_closed = 0;
+
+/* Non-zero means attempt to make all of the lines have fill_column width. */
+int do_justification = 0;
+
+/* Add the character to the current paragraph.  If filling_enabled is
+   non-zero, then do filling as well. */
+static void
+add_char (int character)
+{
+  /* If we are avoiding outputting headers, and we are currently
+     in a menu, then simply return. */
+  if (no_headers && in_menu)
+    return;
+
+  /* If we are adding a character now, then we don't have to
+     ignore close_paragraph () calls any more. */
+  if (must_start_paragraph && character != '\n')
+    {
+      must_start_paragraph = 0;
+      line_already_broken = 0;	/* The line is no longer broken. */
+      if (current_indent > output_column)
+	{
+	  indent (current_indent - output_column);
+	  output_column = current_indent;
+	}
+    }
+
+  if (non_splitting_words && member (character, " \t\n"))
+    character = ' ' | 0x80;
+
+  insertion_paragraph_closed = 0;
+
+  switch (character)
+    {
+    case '\n':
+      if (!filling_enabled)
+	{
+	  insert ('\n');
+
+	  if (force_flush_right)
+	    {
+	      close_paragraph ();
+	      /* Hack to force single blank lines out in this mode. */
+	      flush_output ();
+	    }
+
+	  output_column = 0;
+
+	  if (!no_indent && paragraph_is_open)
+	    indent (output_column = current_indent);
+	  break;
+	}
+      else /* CHARACTER is newline, and filling is enabled. */
+	{
+	  if (sentence_ender (last_inserted_character))
+	    {
+	      insert (' ');
+	      output_column++;
+	      last_inserted_character = character;
+	    }
+	}
+
+      if (last_char_was_newline)
+	{
+	  close_paragraph ();
+	  pending_indent = 0;
+	}
+      else
+	{
+	  last_char_was_newline = 1;
+	  insert (' ');
+	  output_column++;
+	}
+      break;
+
+    default:
+      {
+	int len = get_char_len (character);
+	int suppress_insert = 0;
+
+	if ((character == ' ') && (last_char_was_newline))
+	  {
+	    if (!paragraph_is_open)
+	      {
+		pending_indent++;
+		return;
+	      }
+	  }
+
+	if (!paragraph_is_open)
+	  {
+	    start_paragraph ();
+
+	    /* If the paragraph is supposed to be indented a certain way,
+	       then discard all of the pending whitespace.  Otherwise, we
+	       let the whitespace stay. */
+	    if (!paragraph_start_indent)
+	      indent (pending_indent);
+	    pending_indent = 0;
+	  }
+
+	if ((output_column += len) > fill_column)
+	  {
+	    if (filling_enabled)
+	      {
+		int temp = output_paragraph_offset;
+		while (--temp > 0 && output_paragraph[temp] != '\n')
+		  {
+		    /* If we have found a space, we have the place to break
+		       the line. */
+		    if (output_paragraph[temp] == ' ')
+		      {
+			/* Remove trailing whitespace from output. */
+			while (temp && whitespace (output_paragraph[temp - 1]))
+			  temp--;
+
+			output_paragraph[temp++] = '\n';
+
+			/* We have correctly broken the line where we want
+			   to.  What we don't want is spaces following where
+			   we have decided to break the line.  We get rid of
+			   them. */
+			{
+			  int t1 = temp;
+
+			  for (;; t1++)
+			    {
+			      if (t1 == output_paragraph_offset)
+				{
+				  if (whitespace (character))
+				    suppress_insert = 1;
+				  break;
+				}
+			      if (!whitespace (output_paragraph[t1]))
+				break;
+			    }
+
+			  if (t1 != temp)
+			    {
+			      adjust_braces_following (temp, (- (t1 - temp)));
+			      strncpy ((char *) &output_paragraph[temp],
+				       (char *) &output_paragraph[t1],
+				       (output_paragraph_offset - t1));
+			      output_paragraph_offset -= (t1 - temp);
+			    }
+			}
+
+			/* Filled, but now indent if that is right. */
+			if (indented_fill && current_indent)
+			  {
+			    int buffer_len = ((output_paragraph_offset - temp)
+					      + current_indent);
+			    char *temp_buffer = (char *)xmalloc (buffer_len);
+			    int indentation = 0;
+
+			    /* We have to shift any markers that are in
+			       front of the wrap point. */
+			    adjust_braces_following (temp, current_indent);
+
+			    while (current_indent > 0 &&
+				   indentation != current_indent)
+			      temp_buffer[indentation++] = ' ';
+
+			    strncpy ((char *) &temp_buffer[current_indent],
+				     (char *) &output_paragraph[temp],
+				     buffer_len - current_indent);
+
+			    if (output_paragraph_offset + buffer_len
+				>= paragraph_buffer_len)
+			      {
+				unsigned char *tt = xrealloc
+				  (output_paragraph,
+				   (paragraph_buffer_len += buffer_len));
+				output_paragraph = tt;
+			      }
+			    strncpy ((char *) &output_paragraph[temp],
+				     temp_buffer, buffer_len);
+			    output_paragraph_offset += current_indent;
+			    free (temp_buffer);
+			  }
+			output_column = 0;
+			while (temp < output_paragraph_offset)
+			  output_column +=
+			    get_char_len (output_paragraph[temp++]);
+			output_column += len;
+			break;
+		      }
+		  }
+	      }
+	  }
+
+	if (!suppress_insert)
+	  {
+	    insert (character);
+	    last_inserted_character = character;
+	  }
+	last_char_was_newline = 0;
+	line_already_broken = 0;
+      }
+    }
+}
+
+/* Insert CHARACTER into OUTPUT_PARAGRAPH. */
+static void
+insert (int character)
+{
+  output_paragraph[output_paragraph_offset++] = character;
+  if (output_paragraph_offset == paragraph_buffer_len)
+    {
+      output_paragraph =
+	xrealloc (output_paragraph, (paragraph_buffer_len += 100));
+    }
+}
+
+/* Remove upto COUNT characters of whitespace from the
+   the current output line.  If COUNT is less than zero,
+   then remove until none left. */
+static void
+kill_self_indent (int count)
+{
+  /* Handle infinite case first. */
+  if (count < 0)
+    {
+      output_column = 0;
+      while (output_paragraph_offset)
+	{
+	  if (whitespace (output_paragraph[output_paragraph_offset - 1]))
+	    output_paragraph_offset--;
+	  else
+	    break;
+	}
+    }
+  else
+    {
+      while (output_paragraph_offset && count--)
+	if (whitespace (output_paragraph[output_paragraph_offset - 1]))
+	  output_paragraph_offset--;
+	else
+	  break;
+    }
+}
+
+/* Non-zero means do not honor calls to flush_output (). */
+static int flushing_ignored = 0;
+
+/* Prevent calls to flush_output () from having any effect. */
+static void
+inhibit_output_flushing (void)
+{
+  flushing_ignored++;
+}
+
+/* Allow calls to flush_output () to write the paragraph data. */
+static void
+uninhibit_output_flushing (void)
+{
+  flushing_ignored--;
+}
+
+static void
+flush_output (void)
+{
+  register int i;
+
+  if (!output_paragraph_offset || flushing_ignored)
+    return;
+
+  for (i = 0; i < output_paragraph_offset; i++)
+    {
+      if (output_paragraph[i] == (unsigned char)(' ' | 0x80) ||
+	  output_paragraph[i] == (unsigned char)('\t' | 0x80) ||
+	  output_paragraph[i] == (unsigned char)('\n' | 0x80) ||
+	  sentence_ender (UNMETA (output_paragraph[i])))
+	output_paragraph[i] &= 0x7f;
+    }
+
+  fwrite (output_paragraph, 1, output_paragraph_offset, output_stream);
+
+  output_position += output_paragraph_offset;
+  output_paragraph_offset = 0;
+}
+
+/* How to close a paragraph controlling the number of lines between
+   this one and the last one. */
+
+/* Paragraph spacing is controlled by this variable.  It is the number of
+   blank lines that you wish to appear between paragraphs.  A value of
+   1 creates a single blank line between paragraphs. */
+int paragraph_spacing = DEFAULT_PARAGRAPH_SPACING;
+
+/* Close the current paragraph, leaving no blank lines between them. */
+static void
+close_single_paragraph (void)
+{
+  close_paragraph_with_lines (0);
+}
+
+/* Close a paragraph after an insertion has ended. */
+static void
+close_insertion_paragraph (void)
+{
+  if (!insertion_paragraph_closed)
+    {
+      /* Close the current paragraph, breaking the line. */
+      close_single_paragraph ();
+
+      /* Start a new paragraph here, inserting whatever indention is correct
+	 for the now current insertion level (one above the one that we are
+	 ending). */
+      start_paragraph ();
+
+      /* Tell close_paragraph () that the previous line has already been
+	 broken, so it should insert one less newline. */
+      line_already_broken = 1;
+
+      /* Let functions such as add_char () know that we have already found a
+	 newline. */
+      ignore_blank_line ();
+    }
+  else
+    {
+      /* If the insertion paragraph is closed already, then we are seeing
+	 two `@end' commands in a row.  Note that the first one we saw was
+	 handled in the first part of this if-then-else clause, and at that
+	 time start_paragraph () was called, partially to handle the proper
+	 indentation of the current line.  However, the indentation level
+	 may have just changed again, so we may have to outdent the current
+	 line to the new indentation level. */
+      if (current_indent < output_column)
+	kill_self_indent (output_column - current_indent);
+    }
+
+  insertion_paragraph_closed = 1;
+}
+
+static void
+close_paragraph_with_lines (int lines)
+{
+  int old_spacing = paragraph_spacing;
+  paragraph_spacing = lines;
+  close_paragraph ();
+  paragraph_spacing = old_spacing;
+}
+
+/* Close the currently open paragraph. */
+static void
+close_paragraph (void)
+{
+  register int i;
+
+  /* The insertion paragraph is no longer closed. */
+  insertion_paragraph_closed = 0;
+
+  if (paragraph_is_open && !must_start_paragraph)
+    {
+      register int tindex, c;
+
+      tindex = output_paragraph_offset;
+
+      /* Back up to last non-newline/space character, forcing all such
+	 subsequent characters to be newlines.  This isn't strictly
+	 necessary, but a couple of functions use the presence of a newline
+	 to make decisions. */
+      for (tindex = output_paragraph_offset - 1; tindex >= 0; --tindex)
+	{
+	  c = output_paragraph[tindex];
+
+	  if (c == ' '|| c == '\n')
+	    output_paragraph[tindex] = '\n';
+	  else
+	    break;
+	}
+
+      /* All trailing whitespace is ignored. */
+      output_paragraph_offset = ++tindex;
+
+      /* Break the line if that is appropriate. */
+      if (paragraph_spacing >= 0)
+	insert ('\n');
+
+      /* Add as many blank lines as is specified in PARAGRAPH_SPACING. */
+      if (!force_flush_right)
+	{
+	  for (i = 0; i < (paragraph_spacing - line_already_broken); i++)
+	    insert ('\n');
+	}
+
+      /* If we are doing flush right indentation, then do it now
+	 on the paragraph (really a single line). */
+      if (force_flush_right)
+	do_flush_right_indentation ();
+
+      flush_output ();
+      paragraph_is_open = 0;
+      no_indent = 0;
+      output_column = 0;
+    }
+  ignore_blank_line ();
+}
+
+/* Make the last line just read look as if it were only a newline. */
+static void
+ignore_blank_line (void)
+{
+  last_inserted_character = '\n';
+  last_char_was_newline = 1;
+}
+
+/* Align the end of the text in output_paragraph with fill_column. */
+static void
+do_flush_right_indentation (void)
+{
+  char *temp;
+  int temp_len;
+
+  kill_self_indent (-1);
+
+  if (output_paragraph[0] != '\n')
+    {
+      output_paragraph[output_paragraph_offset] = '\0';
+
+      if (output_paragraph_offset < fill_column)
+	{
+	  register int i;
+
+	  if (fill_column >= paragraph_buffer_len)
+	    output_paragraph =
+	      xrealloc (output_paragraph,
+			(paragraph_buffer_len += fill_column));
+
+	  temp_len = strlen ((char *)output_paragraph);
+	  temp = (char *)xmalloc (temp_len + 1);
+	  memcpy (temp, (char *)output_paragraph, temp_len);
+
+	  for (i = 0; i < fill_column - output_paragraph_offset; i++)
+	    output_paragraph[i] = ' ';
+
+	  memcpy ((char *)output_paragraph + i, temp, temp_len);
+	  free (temp);
+	  output_paragraph_offset = fill_column;
+	}
+    }
+}
+
+/* Begin a new paragraph. */
+static void
+start_paragraph (void)
+{
+  /* First close existing one. */
+  if (paragraph_is_open)
+    close_paragraph ();
+
+  /* In either case, the insertion paragraph is no longer closed. */
+  insertion_paragraph_closed = 0;
+
+  /* However, the paragraph is open! */
+  paragraph_is_open = 1;
+
+  /* If we MUST_START_PARAGRAPH, that simply means that start_paragraph ()
+     had to be called before we would allow any other paragraph operations
+     to have an effect. */
+  if (!must_start_paragraph)
+    {
+      int amount_to_indent = 0;
+
+      /* If doing indentation, then insert the appropriate amount. */
+      if (!no_indent)
+	{
+	  if (inhibit_paragraph_indentation)
+	    {
+	      amount_to_indent = current_indent;
+	      if (inhibit_paragraph_indentation < 0)
+		inhibit_paragraph_indentation++;
+	    }
+	  else if (paragraph_start_indent < 0)
+	    amount_to_indent = current_indent;
+	  else
+	    amount_to_indent = current_indent + paragraph_start_indent;
+
+	  if (amount_to_indent >= output_column)
+	    {
+	      amount_to_indent -= output_column;
+	      indent (amount_to_indent);
+	      output_column += amount_to_indent;
+	    }
+	}
+    }
+  else
+    must_start_paragraph = 0;
+}
+
+/* Insert the indentation specified by AMOUNT. */
+static void
+indent (int amount)
+{
+  register BRACE_ELEMENT *elt = brace_stack;
+
+  /* For every START_POS saved within the brace stack which will be affected
+     by this indentation, bump that start pos forward. */
+  while (elt)
+    {
+      if (elt->pos >= output_paragraph_offset)
+	elt->pos += amount;
+      elt = elt->next;
+    }
+
+  while (--amount >= 0)
+    insert (' ');
+}
+
+/* Search forward for STRING in input_text.
+   FROM says where where to start. */
+static int
+search_forward (char *string, int from)
+{
+  int len = strlen (string);
+
+  while (from < size_of_input_text)
+    {
+      if (strncmp (input_text + from, string, len) == 0)
+	return (from);
+      from++;
+    }
+  return (-1);
+}
+
+/* Whoops, Unix doesn't have strcasecmp. */
+
+/* Case independent string compare. */
+#if !defined (HAVE_STRCASECMP)
+static int
+strcasecmp (char *string1, char *string2)
+{
+  char ch1, ch2;
+
+  for (;;)
+    {
+      ch1 = *string1++;
+      ch2 = *string2++;
+
+      if (!(ch1 | ch2))
+	return (0);
+
+      ch1 = coerce_to_upper (ch1);
+      ch2 = coerce_to_upper (ch2);
+
+      if (ch1 != ch2)
+	return (ch1 - ch2);
+    }
+}
+#endif /* !HAVE_STRCASECMP */
+
+enum insertion_type { menu, quotation, lisp, smalllisp, example,
+  smallexample, display, itemize, format, enumerate, cartouche, table,
+  ftable, vtable, group, ifinfo, flushleft, flushright, ifset, ifclear, deffn,
+  defun, defmac, defspec, defvr, defvar, defopt, deftypefn,
+  deftypefun, deftypevr, deftypevar, defcv, defivar, defop, defmethod,
+  deftypemethod, deftp, bad_type };
+
+char *insertion_type_names[] = { "menu", "quotation", "lisp",
+  "smalllisp", "example", "smallexample", "display", "itemize",
+  "format", "enumerate", "cartouche", "table", "ftable", "vtable", "group",
+  "ifinfo", "flushleft", "flushright", "ifset", "ifclear", "deffn",
+  "defun", "defmac", "defspec", "defvr", "defvar", "defopt",
+  "deftypefn", "deftypefun", "deftypevr", "deftypevar", "defcv",
+  "defivar", "defop", "defmethod", "deftypemethod", "deftp",
+  "bad_type" };
+
+int insertion_level = 0;
+typedef struct istack_elt
+{
+  struct istack_elt *next;
+  char *item_function;
+  char *filename;
+  int line_number;
+  int filling_enabled;
+  int indented_fill;
+  enum insertion_type insertion;
+  int inhibited;
+} INSERTION_ELT;
+
+INSERTION_ELT *insertion_stack = (INSERTION_ELT *) NULL;
+
+static void
+init_insertion_stack (void)
+{
+  insertion_stack = (INSERTION_ELT *) NULL;
+}
+
+/* Return the type of the current insertion. */
+static enum insertion_type
+current_insertion_type (void)
+{
+  if (!insertion_level)
+    return (bad_type);
+  else
+    return (insertion_stack->insertion);
+}
+
+/* Return a pointer to the string which is the function to wrap around
+   items. */
+static char *
+current_item_function (void)
+{
+  register int level, done;
+  register INSERTION_ELT *elt;
+
+  level = insertion_level;
+  elt = insertion_stack;
+  done = 0;
+
+  /* Skip down through the stack until we find a non-conditional insertion. */
+  while (!done && (elt != NULL))
+    {
+      switch (elt->insertion)
+	{
+	case ifinfo:
+	case ifset:
+	case ifclear:
+	case cartouche:
+	  elt = elt->next;
+	  level--;
+	  break;
+
+	default:
+	  done = 1;
+	}
+    }
+
+  if (!level)
+    return ((char *) NULL);
+  else
+    return (elt->item_function);
+}
+
+static char *
+get_item_function (void)
+{
+  char *item_function;
+  get_rest_of_line (&item_function);
+  backup_input_pointer ();
+  canon_white (item_function);
+  return (item_function);
+}
+
+ /* Push the state of the current insertion on the stack. */
+static void
+push_insertion (enum insertion_type type, char *item_function)
+{
+  INSERTION_ELT *new = (INSERTION_ELT *) xmalloc (sizeof (INSERTION_ELT));
+
+  new->item_function = item_function;
+  new->filling_enabled = filling_enabled;
+  new->indented_fill = indented_fill;
+  new->insertion = type;
+  new->line_number = line_number;
+  new->filename = strdup (input_filename);
+  new->inhibited = inhibit_paragraph_indentation;
+  new->next = insertion_stack;
+  insertion_stack = new;
+  insertion_level++;
+}
+
+ /* Pop the value on top of the insertion stack into the
+    global variables. */
+static void
+pop_insertion (void)
+{
+  INSERTION_ELT *temp = insertion_stack;
+
+  if (temp == (INSERTION_ELT *) NULL)
+    return;
+
+  inhibit_paragraph_indentation = temp->inhibited;
+  filling_enabled = temp->filling_enabled;
+  indented_fill = temp->indented_fill;
+  free_and_clear (&(temp->item_function));
+  free_and_clear (&(temp->filename));
+  insertion_stack = insertion_stack->next;
+  free (temp);
+  insertion_level--;
+}
+
+ /* Return a pointer to the print name of this
+    enumerated type. */
+static char *
+insertion_type_pname (enum insertion_type type)
+{
+  if ((int) type < (int) bad_type)
+    return (insertion_type_names[(int) type]);
+  else
+    return ("Broken-Type in insertion_type_pname");
+}
+
+/* Return the insertion_type associated with NAME.
+   If the type is not one of the known ones, return BAD_TYPE. */
+static enum insertion_type
+find_type_from_name (char *name)
+{
+  int indecks = 0;
+  while (indecks < (int) bad_type)
+    {
+      if (strcmp (name, insertion_type_names[indecks]) == 0)
+	return (enum insertion_type) indecks;
+      indecks++;
+    }
+  return (bad_type);
+}
+
+static void
+do_nothing (void)
+{
+}
+
+static int
+defun_insertion (enum insertion_type type)
+{
+  return
+    ((type == deffn)
+     || (type == defun)
+     || (type == defmac)
+     || (type == defspec)
+     || (type == defvr)
+     || (type == defvar)
+     || (type == defopt)
+     || (type == deftypefn)
+     || (type == deftypefun)
+     || (type == deftypevr)
+     || (type == deftypevar)
+     || (type == defcv)
+     || (type == defivar)
+     || (type == defop)
+     || (type == defmethod)
+     || (type == deftypemethod)
+     || (type == deftp));
+}
+
+/* MAX_NS is the maximum nesting level for enumerations.  I picked 100
+   which seemed reasonable.  This doesn't control the number of items,
+   just the number of nested lists. */
+#define max_stack_depth 100
+#define ENUM_DIGITS 1
+#define ENUM_ALPHA  2
+typedef struct {
+  int enumtype;
+  int enumval;
+} DIGIT_ALPHA;
+
+DIGIT_ALPHA enumstack[max_stack_depth];
+int enumstack_offset = 0;
+int current_enumval = 1;
+int current_enumtype = ENUM_DIGITS;
+char *enumeration_arg = (char *)NULL;
+
+static void
+start_enumerating (int at, int type)
+{
+  if ((enumstack_offset + 1) == max_stack_depth)
+    {
+      line_error ("Enumeration stack overflow");
+      return;
+    }
+  enumstack[enumstack_offset].enumtype = current_enumtype;
+  enumstack[enumstack_offset].enumval = current_enumval;
+  enumstack_offset++;
+  current_enumval = at;
+  current_enumtype = type;
+}
+
+static void
+stop_enumerating (void)
+{
+  --enumstack_offset;
+  if (enumstack_offset < 0)
+    enumstack_offset = 0;
+
+  current_enumval = enumstack[enumstack_offset].enumval;
+  current_enumtype = enumstack[enumstack_offset].enumtype;
+}
+
+/* Place a letter or digits into the output stream. */
+static void
+enumerate_item (void)
+{
+  char temp[10];
+
+  if (current_enumtype == ENUM_ALPHA)
+    {
+      if (current_enumval == ('z' + 1) || current_enumval == ('Z' + 1))
+	{
+	  current_enumval = ((current_enumval - 1) == 'z' ? 'a' : 'A');
+	  warning ("Lettering overflow, restarting at %c", current_enumval);
+	}
+      sprintf (temp, "%c. ", current_enumval);
+    }
+  else
+    sprintf (temp, "%d. ", current_enumval);
+
+  indent (output_column += (current_indent - strlen (temp)));
+  add_word (temp);
+  current_enumval++;
+}
+
+/* This is where the work for all the "insertion" style
+   commands is done.  A huge switch statement handles the
+   various setups, and generic code is on both sides. */
+static void
+begin_insertion (enum insertion_type type)
+{
+  int no_discard = 0;
+
+  if (defun_insertion (type))
+    {
+      push_insertion (type, strdup (""));
+      no_discard++;
+    }
+  else
+    push_insertion (type, get_item_function ());
+
+  switch (type)
+    {
+    case menu:
+      if (!no_headers)
+	close_paragraph ();
+
+      filling_enabled = no_indent = 0;
+      inhibit_paragraph_indentation = 1;
+
+      if (!no_headers)
+	add_word ("* Menu:\n");
+
+      in_menu++;
+      no_discard++;
+      break;
+
+      /* I think @quotation is meant to do filling.
+	 If you don't want filling, then use @example. */
+    case quotation:
+      close_single_paragraph ();
+      last_char_was_newline = no_indent = 0;
+      indented_fill = filling_enabled = 1;
+      inhibit_paragraph_indentation = 1;
+      current_indent += default_indentation_increment;
+      break;
+
+    case display:
+    case example:
+    case smallexample:
+    case lisp:
+    case smalllisp:
+      /* Just like @example, but no indentation. */
+    case format:
+
+      close_single_paragraph ();
+      inhibit_paragraph_indentation = 1;
+      in_fixed_width_font++;
+      filling_enabled = 0;
+      last_char_was_newline = 0;
+
+      if (type != format)
+	current_indent += default_indentation_increment;
+
+      break;
+
+    case table:
+    case ftable:
+    case vtable:
+    case itemize:
+      close_single_paragraph ();
+      current_indent += default_indentation_increment;
+      filling_enabled = indented_fill = 1;
+#if defined (INDENT_PARAGRAPHS_IN_TABLE)
+      inhibit_paragraph_indentation = 0;
+#else
+      inhibit_paragraph_indentation = 1;
+#endif /* !INDENT_PARAGRAPHS_IN_TABLE */
+
+      /* Make things work for losers who forget the itemize syntax. */
+      if (allow_lax_format && (type == itemize))
+	{
+	  if (!(*insertion_stack->item_function))
+	    {
+	      free (insertion_stack->item_function);
+	      insertion_stack->item_function = strdup ("@bullet");
+	      insertion_stack->item_function[0] = COMMAND_PREFIX;
+	    }
+	}
+
+      if (!*insertion_stack->item_function)
+	{
+	  line_error ("%s requires an argument: the formatter for %citem",
+		      insertion_type_pname (type), COMMAND_PREFIX);
+	}
+      break;
+
+    case enumerate:
+      close_single_paragraph ();
+      no_indent = 0;
+#if defined (INDENT_PARAGRAPHS_IN_TABLE)
+      inhibit_paragraph_indentation = 0;
+#else
+      inhibit_paragraph_indentation = 1;
+#endif /* !INDENT_PARAGRAPHS_IN_TABLE */
+
+      current_indent += default_indentation_increment;
+      filling_enabled = indented_fill = 1;
+
+      if (isdigit (*enumeration_arg))
+	start_enumerating (atoi (enumeration_arg), ENUM_DIGITS);
+      else
+	start_enumerating (*enumeration_arg, ENUM_ALPHA);
+      break;
+
+      /* Does nothing special in makeinfo. */
+    case group:
+      /* Only close the paragraph if we are not inside of an @example. */
+      if (!insertion_stack->next ||
+	  insertion_stack->next->insertion != example)
+	close_single_paragraph ();
+      break;
+
+      /* Insertions that are no-ops in info, but do something in TeX. */
+    case ifinfo:
+    case ifset:
+    case ifclear:
+    case cartouche:
+      if (in_menu)
+	no_discard++;
+      break;
+
+    case deffn:
+    case defun:
+    case defmac:
+    case defspec:
+    case defvr:
+    case defvar:
+    case defopt:
+    case deftypefn:
+    case deftypefun:
+    case deftypevr:
+    case deftypevar:
+    case defcv:
+    case defivar:
+    case defop:
+    case defmethod:
+    case deftypemethod:
+    case deftp:
+      inhibit_paragraph_indentation = 1;
+      filling_enabled = indented_fill = 1;
+      current_indent += default_indentation_increment;
+      no_indent = 0;
+      break;
+
+    case flushleft:
+      close_single_paragraph ();
+      inhibit_paragraph_indentation = 1;
+      filling_enabled = indented_fill = no_indent = 0;
+      break;
+
+    case flushright:
+      close_single_paragraph ();
+      filling_enabled = indented_fill = no_indent = 0;
+      inhibit_paragraph_indentation = 1;
+      force_flush_right++;
+      break;
+
+    default:
+      break;
+    }
+
+  if (!no_discard)
+    discard_until ("\n");
+}
+
+/* Try to end the insertion with the specified TYPE.
+   TYPE, with a value of bad_type,  gets translated to match
+   the value currently on top of the stack.
+   Otherwise, if TYPE doesn't match the top of the insertion stack,
+   give error. */
+static void
+end_insertion (enum insertion_type type)
+{
+  enum insertion_type temp_type;
+
+  if (!insertion_level)
+    return;
+
+  temp_type = current_insertion_type ();
+
+  if (type == bad_type)
+    type = temp_type;
+
+  if (type != temp_type)
+    {
+      line_error
+	("`%cend' expected `%s', but saw `%s'", COMMAND_PREFIX,
+	 insertion_type_pname (temp_type), insertion_type_pname (type));
+      return;
+    }
+
+  pop_insertion ();
+
+  switch (type)
+    {
+      /* Insertions which have no effect on paragraph formatting. */
+    case ifinfo:
+    case ifset:
+    case ifclear:
+      break;
+
+    case menu:
+      in_menu--;		/* No longer hacking menus. */
+      if (!no_headers)
+	close_insertion_paragraph ();
+      break;
+
+    case enumerate:
+      stop_enumerating ();
+      close_insertion_paragraph ();
+      current_indent -= default_indentation_increment;
+      break;
+
+    case flushleft:
+    case group:
+    case cartouche:
+      close_insertion_paragraph ();
+      break;
+
+    case format:
+    case display:
+    case example:
+    case smallexample:
+    case lisp:
+    case smalllisp:
+    case quotation:
+
+      /* @quotation is the only one of the above without a fixed width
+	 font. */
+      if (type != quotation)
+	in_fixed_width_font--;
+
+      /* @format is the only fixed_width insertion without a change
+	 in indentation. */
+      if (type != format)
+	current_indent -= default_indentation_increment;
+
+      /* The ending of one of these insertions always marks the
+	 start of a new paragraph. */
+      close_insertion_paragraph ();
+      break;
+
+    case table:
+    case ftable:
+    case vtable:
+    case itemize:
+      current_indent -= default_indentation_increment;
+      break;
+
+    case flushright:
+      force_flush_right--;
+      close_insertion_paragraph ();
+      break;
+
+      /* Handle the @defun style insertions with a default clause. */
+    default:
+      current_indent -= default_indentation_increment;
+      close_insertion_paragraph ();
+      break;
+    }
+}
+
+/* Insertions cannot cross certain boundaries, such as node beginnings.  In
+   code that creates such boundaries, you should call discard_insertions ()
+   before doing anything else.  It prints the errors for you, and cleans up
+   the insertion stack. */
+static void
+discard_insertions (void)
+{
+  int real_line_number = line_number;
+  while (insertion_stack)
+    {
+      if (insertion_stack->insertion == ifinfo ||
+	  insertion_stack->insertion == ifset ||
+	  insertion_stack->insertion == ifclear ||
+	  insertion_stack->insertion == cartouche)
+	break;
+      else
+	{
+	  char *offender;
+	  char *current_filename;
+
+	  current_filename = input_filename;
+	  offender = (char *)insertion_type_pname (insertion_stack->insertion);
+	  input_filename = insertion_stack->filename;
+	  line_number = insertion_stack->line_number;
+	  line_error ("This `%s' doesn't have a matching `%cend %s'", offender,
+		      COMMAND_PREFIX, offender);
+	  input_filename = current_filename;
+	  pop_insertion ();
+	}
+    }
+  line_number = real_line_number;
+}
+
+/* The actual commands themselves. */
+
+/* Commands which insert themselves. */
+static void
+insert_self (void)
+{
+  add_word (command);
+}
+
+/* Force a line break in the output. */
+static void
+cm_asterisk (void)
+{
+  close_single_paragraph ();
+#if !defined (ASTERISK_NEW_PARAGRAPH)
+  cm_noindent ();
+#endif /* ASTERISK_NEW_PARAGRAPH */
+}
+
+/* Insert ellipsis. */
+static void
+cm_dots (int arg)
+{
+  if (arg == START)
+    add_word ("...");
+}
+
+static void
+cm_bullet (int arg)
+{
+  if (arg == START)
+    add_char ('*');
+}
+
+static void
+cm_minus (int arg)
+{
+  if (arg == START)
+    add_char ('-');
+}
+
+/* Insert "TeX". */
+static void
+cm_TeX (int arg)
+{
+  if (arg == START)
+    add_word ("TeX");
+}
+
+static void
+cm_copyright (int arg)
+{
+  if (arg == START)
+    add_word ("(C)");
+}
+
+#if defined (__osf__)
+#define LOCALTIME_CAST(x) (time_t *)(x)
+#else
+#define LOCALTIME_CAST(x) (x)
+#endif
+
+static void
+cm_today (int arg)
+{
+  static char * months [12] =
+    { "January", "February", "March", "April", "May", "June", "July",
+	"August", "September", "October", "November", "December" };
+  if (arg == START)
+    {
+      long timer = time (0);
+      struct tm *ts = localtime (LOCALTIME_CAST (&timer));
+      add_word_args
+	("%d %s %d",
+	 (ts -> tm_mday),
+	 (months [ts -> tm_mon]),
+	 ((ts -> tm_year) + 1900));
+    }
+}
+
+static void
+cm_code (int arg)
+{
+  extern int printing_index;
+
+  if (printing_index)
+    return;
+
+  if (arg == START)
+    {
+      in_fixed_width_font++;
+      add_char ('`');
+    }
+  else
+    {
+      add_word ("'");
+      in_fixed_width_font--;
+    }
+}
+
+static void
+cm_samp (int arg)
+{
+  cm_code (arg);
+}
+
+static void
+cm_file (int arg)
+{
+  cm_code (arg);
+}
+
+static void
+cm_kbd (int arg)
+{
+  cm_code (arg);
+}
+
+static void
+cm_key (int arg)
+{
+}
+
+/* Convert the character at position into CTL. */
+static void
+cm_ctrl (int arg, int start, int end)
+{
+  /* Should we allow multiple character arguments?  I think yes. */
+  if (arg == END)
+    {
+      register int i, character;
+#if defined (NO_MULTIPLE_CTRL)
+      if ((end - start) != 1)
+	line_error ("%c%s expects a single character as an argument",
+		    COMMAND_PREFIX, command);
+      else
+#endif
+	for (i = start; i < end; i++)
+	  {
+	    character = output_paragraph[i];
+
+	    if (isletter (character))
+	      output_paragraph[i] = CTL (coerce_to_upper (character));
+	  }
+    }
+}
+
+/* Small Caps in makeinfo just does all caps. */
+static void
+cm_sc (int arg, int start_pos, int end_pos)
+{
+  if (arg == END)
+    {
+      while (start_pos < end_pos)
+	{
+	  output_paragraph[start_pos] =
+	    coerce_to_upper (output_paragraph[start_pos]);
+	  start_pos++;
+	}
+    }
+}
+
+/* @var in makeinfo just uppercases the text. */
+static void
+cm_var (int arg, int start_pos, int end_pos)
+{
+  if (arg == END)
+    {
+      while (start_pos < end_pos)
+	{
+	  output_paragraph[start_pos] =
+	    coerce_to_upper (output_paragraph[start_pos]);
+	  start_pos++;
+	}
+    }
+}
+
+static void
+cm_dfn (int arg, int position)
+{
+  add_char ('"');
+}
+
+static void
+cm_emph (int arg)
+{
+  add_char ('*');
+}
+
+static void
+cm_strong (int arg, int position)
+{
+  cm_emph (arg);
+}
+
+static void
+cm_cite (int arg, int position)
+{
+  if (arg == START)
+    add_word ("`");
+  else
+    add_word ("'");
+}
+
+/* Current text is italicized. */
+static void
+cm_italic (int arg, int start, int end)
+{
+}
+
+/* Current text is highlighted. */
+static void
+cm_bold (int arg, int start, int end)
+{
+  cm_italic (arg, start, end);
+}
+
+/* Current text is in roman font. */
+static void
+cm_roman (int arg, int start, int end)
+{
+}
+
+/* Current text is in roman font. */
+static void
+cm_titlefont (int arg, int start, int end)
+{
+}
+
+/* Italicize titles. */
+static void
+cm_title (int arg, int start, int end)
+{
+  cm_italic (arg, start, end);
+}
+
+/* @refill is a NOP. */
+static void
+cm_refill (void)
+{
+}
+
+/* Prevent the argument from being split across two lines. */
+static void
+cm_w (int arg, int start, int end)
+{
+  if (arg == START)
+    non_splitting_words++;
+  else
+    non_splitting_words--;
+}
+
+
+/* Explain that this command is obsolete, thus the user shouldn't
+   do anything with it. */
+static void
+cm_obsolete (int arg, int start, int end)
+{
+  if (arg == START)
+    warning ("The command `%c%s' is obsolete", COMMAND_PREFIX, command);
+}
+
+/* Insert the text following input_text_offset up to the end of the line
+   in a new, separate paragraph.  Directly underneath it, insert a
+   line of WITH_CHAR, the same length of the inserted text. */
+static void
+insert_and_underscore (int with_char)
+{
+  register int i, len;
+  int old_no_indent, starting_pos, ending_pos;
+  char *temp;
+
+  close_paragraph ();
+  filling_enabled =  indented_fill = 0;
+  old_no_indent = no_indent;
+  no_indent = 1;
+
+#if defined (HAVE_MACROS)
+  if (macro_expansion_output_stream)
+    append_to_expansion_output (input_text_offset + 1);
+#endif /* HAVE_MACROS */
+
+  get_rest_of_line (&temp);
+
+  starting_pos = output_position + output_paragraph_offset;
+#if defined (HAVE_MACROS)
+  if (macro_expansion_output_stream)
+    {
+      char *temp1;
+
+      temp1 = (char *)xmalloc (2 + strlen (temp));
+      sprintf (temp1, "%s\n", temp);
+      remember_itext (input_text, input_text_offset);
+      me_execute_string (temp1);
+      free (temp1);
+    }
+  else
+#endif /* HAVE_MACROS */
+  execute_string ("%s\n", temp);
+
+  ending_pos = output_position + output_paragraph_offset;
+  free (temp);
+
+  len = (ending_pos - starting_pos) - 1;
+  for (i = 0; i < len; i++)
+    add_char (with_char);
+  insert ('\n');
+  close_paragraph ();
+  filling_enabled = 1;
+  no_indent = old_no_indent;
+}
+
+/* Here is a structure which associates sectioning commands with
+   an integer, hopefully to reflect the `depth' of the current
+   section. */
+struct {
+  char *name;
+  int level;
+} section_alist[] = {
+  { "unnumberedsubsubsec", 5 },
+  { "unnumberedsubsec", 4 },
+  { "unnumberedsec", 3 },
+  { "unnumbered", 2 },
+  { "appendixsubsubsec", 5 },
+  { "appendixsubsec", 4 },
+  { "appendixsec", 3 },
+  { "appendixsection", 3 },
+  { "appendix", 2 },
+  { "subsubsec", 5 },
+  { "subsubsection", 5 },
+  { "subsection", 4 },
+  { "section", 3 },
+  { "chapter", 2 },
+  { "top", 1 },
+
+  { (char *)NULL, 0 }
+};
+
+/* Amount to offset the name of sectioning commands to levels by. */
+int section_alist_offset = 0;
+
+/* Shift the meaning of @section to @chapter. */
+static void
+cm_raisesections (void)
+{
+  discard_until ("\n");
+  section_alist_offset--;
+}
+
+/* Shift the meaning of @chapter to @section. */
+static void
+cm_lowersections (void)
+{
+  discard_until ("\n");
+  section_alist_offset++;
+}
+
+/* Return an integer which identifies the type section present in TEXT. */
+static int
+what_section (char *text)
+{
+  register int i, j;
+  char *t;
+
+ find_section_command:
+  for (j = 0; text[j] && cr_or_whitespace (text[j]); j++);
+  if (text[j] != COMMAND_PREFIX)
+    return (-1);
+
+  text = text + j + 1;
+
+  /* We skip @c, @comment, and @?index commands. */
+  if ((strncmp (text, "comment", strlen ("comment")) == 0) ||
+      (text[0] == 'c' && cr_or_whitespace (text[1])) ||
+      (strcmp (text + 1, "index") == 0))
+    {
+      while (*text++ != '\n');
+      goto find_section_command;
+    }
+
+  /* Handle italicized sectioning commands. */
+  if (*text == 'i')
+    text++;
+
+  for (j = 0; text[j] && !cr_or_whitespace (text[j]); j++);
+
+  for (i = 0; (t = section_alist[i].name); i++)
+    {
+      if (j == strlen (t) && strncmp (t, text, j) == 0)
+	{
+	  int return_val;
+
+	  return_val = (section_alist[i].level + section_alist_offset);
+
+	  if (return_val < 0)
+	    return_val = 0;
+	  else if (return_val > 5)
+	    return_val = 5;
+	  return (return_val);
+	}
+    }
+  return (-1);
+}
+
+/* Set the level of @top to LEVEL.  Return the old level of @top. */
+static int
+set_top_section_level (int level)
+{
+  register int i, result = -1;
+
+  for (i = 0; section_alist[i].name; i++)
+    if (strcmp (section_alist[i].name, "top") == 0)
+      {
+	result = section_alist[i].level;
+	section_alist[i].level = level;
+	break;
+      }
+  return (result);
+}
+
+/* Treat this just like @unnumbered.  The only difference is
+   in node defaulting. */
+static void
+cm_top (void)
+{
+  /* It is an error to have more than one @top. */
+  if (top_node_seen)
+    {
+      TAG_ENTRY *tag = tag_table;
+
+      line_error ("There already is a node having %ctop as a section",
+		  COMMAND_PREFIX);
+
+      while (tag != (TAG_ENTRY *)NULL)
+	{
+	  if ((tag->flags & IS_TOP))
+	    {
+	      int old_line_number = line_number;
+	      char *old_input_filename = input_filename;
+
+	      line_number = tag->line_no;
+	      input_filename = tag->filename;
+	      line_error ("Here is the %ctop node", COMMAND_PREFIX);
+	      input_filename = old_input_filename;
+	      line_number = old_line_number;
+	      return;
+	    }
+	  tag = tag->next_ent;
+	}
+    }
+  else
+    {
+      top_node_seen = 1;
+
+      /* It is an error to use @top before you have used @node. */
+      if (!tag_table)
+	{
+	  char *top_name;
+
+	  get_rest_of_line (&top_name);
+	  free (top_name);
+	  line_error ("%ctop used before %cnode, defaulting to %s",
+		      COMMAND_PREFIX, COMMAND_PREFIX, top_name);
+	  execute_string ("@node Top, , (dir), (dir)\n@top %s\n", top_name);
+	  return;
+	}
+
+      cm_unnumbered ();
+
+      /* The most recently defined node is the top node. */
+      tag_table->flags |= IS_TOP;
+
+      /* Now set the logical hierarchical level of the Top node. */
+      {
+	int orig_offset = input_text_offset;
+
+	input_text_offset = search_forward (node_search_string, orig_offset);
+
+	if (input_text_offset > 0)
+	  {
+	    int this_section;
+
+	    /* We have encountered a non-top node, so mark that one exists. */
+	    non_top_node_seen = 1;
+
+	    /* Move to the end of this line, and find out what the
+	       sectioning command is here. */
+	    while (input_text[input_text_offset] != '\n')
+	      input_text_offset++;
+
+	    if (input_text_offset < size_of_input_text)
+	      input_text_offset++;
+
+	    this_section = what_section (input_text + input_text_offset);
+
+	    /* If we found a sectioning command, then give the top section
+	       a level of this section - 1. */
+	    if (this_section != -1)
+	      set_top_section_level (this_section - 1);
+	  }
+	input_text_offset = orig_offset;
+      }
+    }
+}
+
+/* Organized by level commands.  That is, "*" == chapter, "=" == section. */
+char *scoring_characters = "*=-.";
+
+static void
+sectioning_underscore (char *commanned)
+{
+  char character;
+  char *temp;
+  int level;
+
+  temp = (char *)xmalloc (2 + strlen (commanned));
+  temp[0] = COMMAND_PREFIX;
+  strcpy (&temp[1], commanned);
+  level = what_section (temp);
+  free (temp);
+  level -= 2;
+
+  if (level < 0)
+    level = 0;
+
+  character = scoring_characters[level];
+
+  insert_and_underscore (character);
+}
+
+/* The remainder of the text on this line is a chapter heading. */
+static void
+cm_chapter (void)
+{
+  sectioning_underscore ("chapter");
+}
+
+/* The remainder of the text on this line is a section heading. */
+static void
+cm_section (void)
+{
+  sectioning_underscore ("section");
+}
+
+/* The remainder of the text on this line is a subsection heading. */
+static void
+cm_subsection (void)
+{
+  sectioning_underscore ("subsection");
+}
+
+/* The remainder of the text on this line is a subsubsection heading. */
+static void
+cm_subsubsection (void)
+{
+  sectioning_underscore ("subsubsection");
+}
+
+/* The remainder of the text on this line is an unnumbered heading. */
+static void
+cm_unnumbered (void)
+{
+  cm_chapter ();
+}
+
+/* The remainder of the text on this line is an unnumbered section heading. */
+static void
+cm_unnumberedsec (void)
+{
+  cm_section ();
+}
+
+/* The remainder of the text on this line is an unnumbered
+   subsection heading. */
+static void
+cm_unnumberedsubsec (void)
+{
+  cm_subsection ();
+}
+
+/* The remainder of the text on this line is an unnumbered
+   subsubsection heading. */
+static void
+cm_unnumberedsubsubsec (void)
+{
+  cm_subsubsection ();
+}
+
+/* The remainder of the text on this line is an appendix heading. */
+static void
+cm_appendix (void)
+{
+  cm_chapter ();
+}
+
+/* The remainder of the text on this line is an appendix section heading. */
+static void
+cm_appendixsec (void)
+{
+  cm_section ();
+}
+
+/* The remainder of the text on this line is an appendix subsection heading. */
+static void
+cm_appendixsubsec (void)
+{
+  cm_subsection ();
+}
+
+/* The remainder of the text on this line is an appendix
+   subsubsection heading. */
+static void
+cm_appendixsubsubsec (void)
+{
+  cm_subsubsection ();
+}
+
+/* Compatibility functions substitute for chapter, section, etc. */
+static void
+cm_majorheading (void)
+{
+  cm_chapheading ();
+}
+
+static void
+cm_chapheading (void)
+{
+  cm_chapter ();
+}
+
+static void
+cm_heading (void)
+{
+  cm_section ();
+}
+
+static void
+cm_subheading (void)
+{
+  cm_subsection ();
+}
+
+static void
+cm_subsubheading (void)
+{
+  cm_subsubsection ();
+}
+
+
+/* **************************************************************** */
+/*								    */
+/*		   Adding nodes, and making tags		    */
+/*								    */
+/* **************************************************************** */
+
+/* Start a new tag table. */
+static void
+init_tag_table (void)
+{
+  while (tag_table != (TAG_ENTRY *) NULL)
+    {
+      TAG_ENTRY *temp = tag_table;
+      free (temp->node);
+      free (temp->prev);
+      free (temp->next);
+      free (temp->up);
+      tag_table = tag_table->next_ent;
+      free (temp);
+    }
+}
+
+static void
+write_tag_table (void)
+{
+  write_tag_table_internal (0);	/* Not indirect. */
+}
+
+static void
+write_tag_table_indirect (void)
+{
+  write_tag_table_internal (1);
+}
+
+/* Write out the contents of the existing tag table.
+   INDIRECT_P says how to format the output. */
+static void
+write_tag_table_internal (int indirect_p)
+{
+  TAG_ENTRY *node = tag_table;
+  int old_indent = no_indent;
+
+  no_indent = 1;
+  filling_enabled = 0;
+  must_start_paragraph = 0;
+  close_paragraph ();
+
+  if (!indirect_p)
+    {
+      no_indent = 1;
+      insert ('\n');
+    }
+
+  add_word_args ("\037\nTag Table:\n%s", indirect_p ? "(Indirect)\n" : "");
+
+  while (node != (TAG_ENTRY *) NULL)
+    {
+      execute_string ("Node: %s", node->node);
+      add_word_args ("\177%d\n", node->position);
+      node = node->next_ent;
+    }
+
+  add_word ("\037\nEnd Tag Table\n");
+  flush_output ();
+  no_indent = old_indent;
+}
+
+static char *
+get_node_token (void)
+{
+  char *string;
+
+  get_until_in_line (",", &string);
+
+  if (curchar () == ',')
+    input_text_offset++;
+
+  canon_white (string);
+
+  /* Force all versions of "top" to be "Top". */
+  normalize_node_name (string);
+
+  return (string);
+}
+
+/* Convert "top" and friends into "Top". */
+static void
+normalize_node_name (char *string)
+{
+  if (strcasecmp (string, "Top") == 0)
+    strcpy (string, "Top");
+}
+
+/* Look up NAME in the tag table, and return the associated
+   tag_entry.  If the node is not in the table return NULL. */
+static TAG_ENTRY *
+find_node (char *name)
+{
+  TAG_ENTRY *tag = tag_table;
+
+  while (tag != (TAG_ENTRY *) NULL)
+    {
+      if (strcmp (tag->node, name) == 0)
+	return (tag);
+      tag = tag->next_ent;
+    }
+  return ((TAG_ENTRY *) NULL);
+}
+
+/* Remember NODE and associates. */
+static void
+remember_node (char *node, char *prev, char *next, char *up, int position,
+	       int line_no, int no_warn)
+{
+  /* Check for existence of this tag already. */
+  if (validating)
+    {
+      register TAG_ENTRY *tag = find_node (node);
+      if (tag)
+	{
+	  line_error ("Node `%s' multiply defined (%d is first definition)",
+		      node, tag->line_no);
+	  return;
+	}
+    }
+
+  /* First, make this the current node. */
+  current_node = node;
+
+  /* Now add it to the list. */
+  {
+    TAG_ENTRY *new = (TAG_ENTRY *) xmalloc (sizeof (TAG_ENTRY));
+    new->node = node;
+    new->prev = prev;
+    new->next = next;
+    new->up = up;
+    new->position = position;
+    new->line_no = line_no;
+    new->filename = node_filename;
+    new->touched = 0;		/* not yet referenced. */
+    new->flags = 0;
+    if (no_warn)
+      new->flags |= NO_WARN;
+    new->next_ent = tag_table;
+    tag_table = new;
+  }
+}
+
+/* The order is: nodename, nextnode, prevnode, upnode.
+   If all of the NEXT, PREV, and UP fields are empty, they are defaulted.
+   You must follow a node command which has those fields defaulted
+   with a sectioning command (e.g. @chapter) giving the "level" of that node.
+   It is an error not to do so.
+   The defaults come from the menu in this node's parent. */
+static void
+cm_node (void)
+{
+  char *node, *prev, *next, *up;
+  int new_node_pos, defaulting, this_section, no_warn = 0;
+  extern int already_outputting_pending_notes;
+
+  if (strcmp (command, "nwnode") == 0)
+    no_warn = 1;
+
+  /* Get rid of unmatched brace arguments from previous commands. */
+  discard_braces ();
+
+  /* There also might be insertions left lying around that haven't been
+     ended yet.  Do that also. */
+  discard_insertions ();
+
+  if (!already_outputting_pending_notes)
+    {
+      close_paragraph ();
+      output_pending_notes ();
+      free_pending_notes ();
+    }
+
+  filling_enabled = indented_fill = 0;
+  new_node_pos = output_position;
+  current_footnote_number = 1;
+
+#if defined (HAVE_MACROS)
+  if (macro_expansion_output_stream)
+    append_to_expansion_output (input_text_offset + 1);
+#endif /* HAVE_MACROS */
+
+  node = get_node_token ();
+  next = get_node_token ();
+  prev = get_node_token ();
+  up = get_node_token ();
+
+#if defined (HAVE_MACROS)
+  if (macro_expansion_output_stream)
+    remember_itext (input_text, input_text_offset);
+#endif /* HAVE_MACROS */
+
+  no_indent = 1;
+  if (!no_headers)
+    {
+      add_word_args ("\037\nFile: %s,  Node: ", pretty_output_filename);
+
+#if defined (HAVE_MACROS)
+      if (macro_expansion_output_stream)
+	me_execute_string (node);
+      else
+#endif /* HAVE_MACROS */
+      execute_string ("%s", node);
+      filling_enabled = indented_fill = 0;
+    }
+
+  /* Check for defaulting of this node's next, prev, and up fields. */
+  defaulting = ((strlen (next) == 0) &&
+		(strlen (prev) == 0) &&
+		(strlen (up) == 0));
+
+  this_section = what_section (input_text + input_text_offset);
+
+  /* If we are defaulting, then look at the immediately following
+     sectioning command (error if none) to determine the node's
+     level.  Find the node that contains the menu mentioning this node
+     that is one level up (error if not found).  That node is the "Up"
+     of this node.  Default the "Next" and "Prev" from the menu. */
+  if (defaulting)
+    {
+      NODE_REF *last_ref = (NODE_REF *)NULL;
+      NODE_REF *ref = node_references;
+
+      if ((this_section < 0) && (strcmp (node, "Top") != 0))
+	{
+	  char *polite_section_name = "top";
+	  int i;
+
+	  for (i = 0; section_alist[i].name; i++)
+	    if (section_alist[i].level == current_section + 1)
+	      {
+		polite_section_name = section_alist[i].name;
+		break;
+	      }
+
+	  line_error
+	    ("Node `%s' requires a sectioning command (e.g. %c%s)",
+	     node, COMMAND_PREFIX, polite_section_name);
+	}
+      else
+	{
+	  if (strcmp (node, "Top") == 0)
+	    {
+	      /* Default the NEXT pointer to be the first menu item in
+		 this node, if there is a menu in this node.  We have to
+		 try very hard to find the menu, as it may be obscured
+		 by execution_strings which are on the filestack.  For
+		 every member of the filestack which has a FILENAME
+		 member which is identical to the current INPUT_FILENAME,
+		 search forward from that offset. */
+	      int saved_input_text_offset = input_text_offset;
+	      int saved_size_of_input_text = size_of_input_text;
+	      char *saved_input_text = input_text;
+	      FSTACK *next_file = filestack;
+
+	      int orig_offset, orig_size;
+
+	      /* No matter what, make this file point back at `(dir)'. */
+	      free (up);   up = strdup ("(dir)");
+
+	      while (1)
+		{
+		  orig_offset = input_text_offset;
+		  orig_size =
+		    search_forward (node_search_string, orig_offset);
+
+		  if (orig_size < 0)
+		    orig_size = size_of_input_text;
+
+		  input_text_offset =
+		    search_forward (menu_search_string, orig_offset);
+
+		  if (input_text_offset > -1)
+		    {
+		      char *nodename_from_menu = (char *)NULL;
+
+		      input_text_offset =
+			search_forward ("\n* ", input_text_offset);
+
+		      if (input_text_offset != -1)
+			nodename_from_menu = glean_node_from_menu (0);
+
+		      if (nodename_from_menu)
+			{
+			  free (next); next = nodename_from_menu;
+			  break;
+			}
+		    }
+
+		  /* We got here, so it hasn't been found yet.  Try
+		     the next file on the filestack if there is one. */
+		  if (next_file &&
+		      (strcmp (next_file->filename, input_filename) == 0))
+		    {
+		      input_text = next_file->text;
+		      input_text_offset = next_file->offset;
+		      size_of_input_text = next_file->size;
+		      next_file = next_file->next;
+		    }
+		  else
+		    {
+		      /* No more input files to check. */
+		      break;
+		    }
+		}
+
+	      input_text = saved_input_text;
+	      input_text_offset = saved_input_text_offset;
+	      size_of_input_text = saved_size_of_input_text;
+	    }
+	}
+
+      /* Fix the level of the menu references in the Top node, iff it
+	 was declared with @top, and no subsequent reference was found. */
+      if (top_node_seen && !non_top_node_seen)
+	{
+	  /* Then this is the first non-@top node seen. */
+	  int level;
+
+	  level = set_top_section_level (this_section - 1);
+	  non_top_node_seen = 1;
+
+	  while (ref)
+	    {
+	      if (ref->section == level)
+		ref->section = this_section - 1;
+	      ref = ref->next;
+	    }
+
+	  ref = node_references;
+	}
+
+      while (ref)
+	{
+	  if (ref->section == (this_section - 1) &&
+	      ref->type == menu_reference &&
+	      strcmp (ref->node, node) == 0)
+	    {
+	      char *containing_node = ref->containing_node;
+
+	      free (up);
+	      up = strdup (containing_node);
+
+	      if (last_ref &&
+		  last_ref->type == menu_reference &&
+		  (strcmp (last_ref->containing_node,
+			   containing_node) == 0))
+		{
+		  free (next);
+		  next = strdup (last_ref->node);
+		}
+
+	      while ((ref->section == this_section - 1) &&
+		     (ref->next) &&
+		     (ref->next->type != menu_reference))
+		ref = ref->next;
+
+	      if (ref->next && ref->type == menu_reference &&
+		  (strcmp (ref->next->containing_node,
+			   containing_node) == 0))
+		{
+		  free (prev);
+		  prev = strdup (ref->next->node);
+		}
+	      else if (!ref->next &&
+		       strcasecmp (ref->containing_node, "Top") == 0)
+		{
+		  free (prev);
+		  prev = strdup (ref->containing_node);
+		}
+	      break;
+	    }
+	  last_ref = ref;
+	  ref = ref->next;
+	}
+    }
+
+#if defined (HAVE_MACROS)
+  /* Insert the correct args if we are expanding macros, and the node's
+     pointers weren't defaulted. */
+  if (macro_expansion_output_stream && !defaulting)
+    {
+      char *temp;
+      int op_orig = output_paragraph_offset;
+
+      temp = (char *)xmalloc (3 + strlen (next));
+      sprintf (temp, ", %s", next);
+      me_execute_string (temp);
+      free (temp);
+
+      temp = (char *)xmalloc (3 + strlen (prev));
+      sprintf (temp, ", %s", prev);
+      me_execute_string (temp);
+      free (temp);
+
+      temp = (char *)xmalloc (4 + strlen (up));
+      sprintf (temp, ", %s", up);
+      me_execute_string (temp);
+      free (temp);
+
+      output_paragraph_offset = op_orig;
+    }
+#endif /* HAVE_MACROS */
+
+  if (!no_headers)
+    {
+#if defined (HAVE_MACROS)
+      if (macro_expansion_output_stream)
+	me_inhibit_expansion++;
+#endif /* HAVE_MACROS */
+
+      if (*next)
+	{
+	  execute_string (",  Next: %s", next);
+	  filling_enabled = indented_fill = 0;
+	}
+
+      if (*prev)
+	{
+	  execute_string (",  Prev: %s", prev);
+	  filling_enabled = indented_fill = 0;
+	}
+
+      if (*up)
+	{
+	  execute_string (",  Up: %s", up);
+	  filling_enabled = indented_fill = 0;
+	}
+#if defined (HAVE_MACROS)
+      if (macro_expansion_output_stream)
+	me_inhibit_expansion--;
+#endif /* HAVE_MACROS */
+    }
+
+  close_paragraph ();
+  no_indent = 0;
+
+  if (!*node)
+    {
+      line_error ("No node name specified for `%c%s' command",
+		  COMMAND_PREFIX, command);
+      free (node);
+      free (next);
+      free (prev);
+      free (up);
+    }
+  else
+    {
+      if (!*next) { free (next); next = (char *)NULL; }
+      if (!*prev) { free (prev); prev = (char *)NULL; }
+      if (!*up) { free (up); up = (char *)NULL; }
+      remember_node (node, prev, next, up, new_node_pos, line_number, no_warn);
+    }
+
+  /* Change the section only if there was a sectioning command. */
+  if (this_section >= 0)
+    current_section = this_section;
+
+  filling_enabled = 1;
+}
+
+/* Validation of an info file.
+   Scan through the list of tag entrys touching the Prev, Next, and Up
+   elements of each.  It is an error not to be able to touch one of them,
+   except in the case of external node references, such as "(DIR)".
+
+   If the Prev is different from the Up,
+   then the Prev node must have a Next pointing at this node.
+
+   Every node except Top must have an Up.
+   The Up node must contain some sort of reference, other than a Next,
+   to this node.
+
+   If the Next is different from the Next of the Up,
+   then the Next node must have a Prev pointing at this node. */
+static void
+validate_file (TAG_ENTRY *tag_tayble)
+{
+  char *old_input_filename = input_filename;
+  TAG_ENTRY *tags = tag_tayble;
+
+  while (tags != (TAG_ENTRY *) NULL)
+    {
+      register TAG_ENTRY *temp_tag;
+
+      input_filename = tags->filename;
+      line_number = tags->line_no;
+
+      /* If this is a "no warn" node, don't validate it in any way. */
+      if (tags->flags & NO_WARN)
+	{
+	  tags = tags->next_ent;
+	  continue;
+	}
+
+      /* If this node has a Next, then make sure that the Next exists. */
+      if (tags->next)
+	{
+	  validate (tags->next, tags->line_no, "Next");
+
+	  /* If the Next node exists, and there is no Up, then make
+	     sure that the Prev of the Next points back. */
+	  if ((temp_tag = find_node (tags->next)))
+	    {
+	      char *prev;
+
+	      if (temp_tag->flags & NO_WARN)
+		{
+		  /* Do nothing if we aren't supposed to issue warnings
+		     about this node. */
+		}
+	      else
+		{
+		  prev = temp_tag->prev;
+		  if (!prev || (strcmp (prev, tags->node) != 0))
+		    {
+		      line_error ("Node `%s''s Next field not pointed back to",
+				  tags->node);
+		      line_number = temp_tag->line_no;
+		      input_filename = temp_tag->filename;
+		      line_error
+			("This node (`%s') is the one with the bad `Prev'",
+			 temp_tag->node);
+		      input_filename = tags->filename;
+		      line_number = tags->line_no;
+		      temp_tag->flags |= PREV_ERROR;
+		    }
+		}
+	    }
+	}
+
+      /* Validate the Prev field if there is one, and we haven't already
+	 complained about it in some way.  You don't have to have a Prev
+	 field at this stage. */
+      if (!(tags->flags & PREV_ERROR) && tags->prev)
+	{
+	  int valid = validate (tags->prev, tags->line_no, "Prev");
+
+	  if (!valid)
+	    tags->flags |= PREV_ERROR;
+	  else
+	    {
+	      /* If the Prev field is not the same as the Up field,
+		 then the node pointed to by the Prev field must have
+		 a Next field which points to this node. */
+	      if (tags->up && (strcmp (tags->prev, tags->up) != 0))
+		{
+		  temp_tag = find_node (tags->prev);
+
+		  /* If we aren't supposed to issue warnings about the
+		     target node, do nothing. */
+		  if (!temp_tag || (temp_tag->flags & NO_WARN))
+		    {
+		      /* Do nothing. */
+		    }
+		  else
+		    {
+		      if (!temp_tag->next ||
+			  (strcmp (temp_tag->next, tags->node) != 0))
+			{
+			  line_error
+			    ("Node `%s''s Prev field not pointed back to",
+			     tags->node);
+			  line_number = temp_tag->line_no;
+			  input_filename = temp_tag->filename;
+			  line_error
+			    ("This node (`%s') is the one with the bad `Next'",
+			     temp_tag->node);
+			  input_filename = tags->filename;
+			  line_number = tags->line_no;
+			  temp_tag->flags |= NEXT_ERROR;
+			}
+		    }
+		}
+	    }
+	}
+
+      if (!tags->up && (strcasecmp (tags->node, "Top") != 0))
+	line_error ("Node `%s' is missing an \"Up\" field", tags->node);
+      else if (tags->up)
+	{
+	  int valid = validate (tags->up, tags->line_no, "Up");
+
+	  /* If node X has Up: Y, then warn if Y fails to have a menu item
+	     or note pointing at X, if Y isn't of the form "(Y)". */
+	  if (valid && *tags->up != '(')
+	    {
+	      NODE_REF *nref, *tref, *list;
+
+	      tref = (NODE_REF *) NULL;
+	      list = node_references;
+
+	      for (;;)
+		{
+		  if (!(nref = find_node_reference (tags->node, list)))
+		    break;
+
+		  if (strcmp (nref->containing_node, tags->up) == 0)
+		    {
+		      if (nref->type != menu_reference)
+			{
+			  tref = nref;
+			  list = nref->next;
+			}
+		      else
+			break;
+		    }
+		  list = nref->next;
+		}
+
+	      if (!nref)
+		{
+		  temp_tag = find_node (tags->up);
+		  line_number = temp_tag->line_no;
+		  input_filename = temp_tag->filename;
+		  if (!tref)
+		    line_error (
+"`%s' has an Up field of `%s', but `%s' has no menu item for `%s'",
+				tags->node, tags->up, tags->up, tags->node);
+		  line_number = tags->line_no;
+		  input_filename = tags->filename;
+		}
+	    }
+	}
+      tags = tags->next_ent;
+    }
+
+  validate_other_references (node_references);
+  /* We have told the user about the references which didn't exist.
+     Now tell him about the nodes which aren't referenced. */
+
+  tags = tag_tayble;
+  while (tags != (TAG_ENTRY *) NULL)
+    {
+      /* If this node is a "no warn" node, do nothing. */
+      if (tags->flags & NO_WARN)
+	{
+	  tags = tags->next_ent;
+	  continue;
+	}
+
+      /* Special hack.  If the node in question appears to have
+         been referenced more than REFERENCE_WARNING_LIMIT times,
+         give a warning. */
+      if (tags->touched > reference_warning_limit)
+	{
+	  input_filename = tags->filename;
+	  line_number = tags->line_no;
+	  warning ("Node `%s' has been referenced %d times",
+		   tags->node, tags->touched);
+	}
+
+      if (tags->touched == 0)
+	{
+	  input_filename = tags->filename;
+	  line_number = tags->line_no;
+
+	  /* Notice that the node "Top" is special, and doesn't have to
+	     be referenced. */
+	  if (strcasecmp (tags->node, "Top") != 0)
+	    warning ("Unreferenced node `%s'", tags->node);
+	}
+      tags = tags->next_ent;
+    }
+  input_filename = old_input_filename;
+}
+
+/* Return 1 if tag correctly validated, or 0 if not. */
+static int
+validate (char *tag, int line, char *label)
+{
+  TAG_ENTRY *result;
+
+  /* If there isn't a tag to verify, or if the tag is in another file,
+     then it must be okay. */
+  if (!tag || !*tag || *tag == '(')
+    return (1);
+
+  /* Otherwise, the tag must exist. */
+  result = find_node (tag);
+
+  if (!result)
+    {
+      line_number = line;
+      line_error (
+"Validation error.  `%s' field points to node `%s', which doesn't exist",
+		  label, tag);
+      return (0);
+    }
+  result->touched++;
+  return (1);
+}
+
+/* Split large output files into a series of smaller files.  Each file
+   is pointed to in the tag table, which then gets written out as the
+   original file.  The new files have the same name as the original file
+   with a "-num" attached.  SIZE is the largest number of bytes to allow
+   in any single split file. */
+static void
+split_file (char *filename, int size)
+{
+  char *root_filename, *root_pathname;
+  char *the_file;
+  struct stat fileinfo;
+  long file_size;
+  char *the_header;
+  int header_size;
+
+  /* Can only do this to files with tag tables. */
+  if (!tag_table)
+    return;
+
+  if (size == 0)
+    size = DEFAULT_SPLIT_SIZE;
+
+  if ((stat (filename, &fileinfo) != 0) ||
+      (((long) fileinfo.st_size) < SPLIT_SIZE_THRESHOLD))
+    return;
+  file_size = (long) fileinfo.st_size;
+
+  the_file = find_and_load (filename);
+  if (!the_file)
+    return;
+
+  root_filename = filename_part (filename);
+  root_pathname = pathname_part (filename);
+
+  if (!root_pathname)
+    root_pathname = strdup ("");
+
+  /* Start splitting the file.  Walk along the tag table
+     outputting sections of the file.  When we have written
+     all of the nodes in the tag table, make the top-level
+     pointer file, which contains indirect pointers and
+     tags for the nodes. */
+  {
+    int which_file = 1;
+    TAG_ENTRY *tags = tag_table;
+    char *indirect_info = (char *)NULL;
+
+    /* Remember the `header' of this file.  The first tag in the file is
+       the bottom of the header; the top of the file is the start. */
+    the_header = (char *)xmalloc (1 + (header_size = tags->position));
+    memcpy (the_header, the_file, header_size);
+
+    while (tags)
+      {
+	int file_top, file_bot, limit;
+
+	/* Have to include the Control-_. */
+	file_top = file_bot = tags->position;
+	limit = file_top + size;
+
+	/* If the rest of this file is only one node, then
+	   that is the entire subfile. */
+	if (!tags->next_ent)
+	  {
+	    int i = tags->position + 1;
+	    char last_char = the_file[i];
+
+	    while (i < file_size)
+	      {
+		if ((the_file[i] == '\037') &&
+		    ((last_char == '\n') ||
+		     (last_char == '\014')))
+		  break;
+		else
+		  last_char = the_file[i];
+		i++;
+	      }
+	    file_bot = i;
+	    tags = tags->next_ent;
+	    goto write_region;
+	  }
+
+	/* Otherwise, find the largest number of nodes that can fit in
+	   this subfile. */
+	for (; tags; tags = tags->next_ent)
+	  {
+	    if (!tags->next_ent)
+	      {
+		/* This entry is the last node.  Search forward for the end
+	           of this node, and that is the end of this file. */
+		int i = tags->position + 1;
+		char last_char = the_file[i];
+
+		while (i < file_size)
+		  {
+		    if ((the_file[i] == '\037') &&
+			((last_char == '\n') ||
+			 (last_char == '\014')))
+		      break;
+		    else
+		      last_char = the_file[i];
+		    i++;
+		  }
+		file_bot = i;
+
+		if (file_bot < limit)
+		  {
+		    tags = tags->next_ent;
+		    goto write_region;
+		  }
+		else
+		  {
+		    /* Here we want to write out everything before the last
+		       node, and then write the last node out in a file
+		       by itself. */
+		    file_bot = tags->position;
+		    goto write_region;
+		  }
+	      }
+
+	    if (tags->next_ent->position > limit)
+	      {
+		if (tags->position == file_top)
+		  tags = tags->next_ent;
+
+		file_bot = tags->position;
+
+	      write_region:
+		{
+		  int fd;
+		  char *split_filename;
+
+		  split_filename = (char *) xmalloc
+		    (10 + strlen (root_pathname) + strlen (root_filename));
+		  sprintf
+		    (split_filename,
+#ifdef MSDOS
+		     "%s%s.%d", root_pathname, root_filename, which_file);
+#else /* !MSDOS */
+		     "%s%s-%d", root_pathname, root_filename, which_file);
+#endif /* !MSDOS */
+
+		  fd = open
+		    (split_filename, O_WRONLY | O_TRUNC | O_CREAT, 0666);
+
+		  if ((fd < 0) ||
+		      (write (fd, the_header, header_size) != header_size) ||
+		      (write (fd, the_file + file_top, file_bot - file_top)
+		       != (file_bot - file_top)) ||
+		      ((close (fd)) < 0))
+		    {
+		      perror (split_filename);
+		      if (fd != -1)
+			close (fd);
+		      exit (FATAL);
+		    }
+
+		  if (!indirect_info)
+		    {
+		      indirect_info = the_file + file_top;
+		      sprintf (indirect_info, "\037\nIndirect:\n");
+		      indirect_info += strlen (indirect_info);
+		    }
+
+#ifdef MSDOS
+		  sprintf (indirect_info, "%s.%d: %d\n",
+#else
+		  sprintf (indirect_info, "%s-%d: %d\n",
+#endif
+			   root_filename, which_file, file_top);
+
+		  free (split_filename);
+		  indirect_info += strlen (indirect_info);
+		  which_file++;
+		  break;
+		}
+	      }
+	  }
+      }
+
+    /* We have sucessfully created the subfiles.  Now write out the
+       original again.  We must use `output_stream', or
+       write_tag_table_indirect () won't know where to place the output. */
+    output_stream = fopen (filename, "w");
+    if (!output_stream)
+      {
+	perror (filename);
+	exit (FATAL);
+      }
+
+    {
+      int distance = indirect_info - the_file;
+      fwrite (the_file, 1, distance, output_stream);
+
+      /* Inhibit newlines. */
+      paragraph_is_open = 0;
+
+      write_tag_table_indirect ();
+      fclose (output_stream);
+      free (the_header);
+      free (the_file);
+      return;
+    }
+  }
+}
+
+/* Some menu hacking.  This is used to remember menu references while
+   reading the input file.  After the output file has been written, if
+   validation is on, then we use the contents of NODE_REFERENCES as a
+   list of nodes to validate. */
+static char *
+reftype_type_string (enum reftype type)
+{
+  switch (type)
+    {
+    case menu_reference:
+      return ("Menu");
+    case followed_reference:
+      return ("Followed-Reference");
+    default:
+      return ("Internal-bad-reference-type");
+    }
+}
+
+/* Remember this node name for later validation use. */
+static void
+remember_node_reference (char *node, int line, enum reftype type)
+{
+  NODE_REF *temp = (NODE_REF *) xmalloc (sizeof (NODE_REF));
+
+  temp->next = node_references;
+  temp->node = strdup (node);
+  temp->line_no = line;
+  temp->section = current_section;
+  temp->type = type;
+  temp->containing_node = (current_node ? strdup (current_node) : 0);
+  temp->filename = node_filename;
+
+  node_references = temp;
+}
+
+static void
+validate_other_references (register NODE_REF *ref_list)
+{
+  char *old_input_filename = input_filename;
+
+  while (ref_list != (NODE_REF *) NULL)
+    {
+      input_filename = ref_list->filename;
+      validate (ref_list->node, ref_list->line_no,
+		reftype_type_string (ref_list->type));
+      ref_list = ref_list->next;
+    }
+  input_filename = old_input_filename;
+}
+
+/* Find NODE in REF_LIST. */
+static NODE_REF *
+find_node_reference (char *node, register NODE_REF *ref_list)
+{
+  while (ref_list)
+    {
+      if (strcmp (node, ref_list->node) == 0)
+	break;
+      ref_list = ref_list->next;
+    }
+  return (ref_list);
+}
+
+static void
+free_node_references (void)
+{
+  register NODE_REF *list, *temp;
+
+  list = node_references;
+
+  while (list)
+    {
+      temp = list;
+      free (list->node);
+      free (list->containing_node);
+      list = list->next;
+      free (temp);
+    }
+  node_references = (NODE_REF *) NULL;
+}
+
+  /* This function gets called at the start of every line while inside of
+     a menu.  It checks to see if the line starts with "* ", and if so,
+     remembers the node reference that this menu refers to.
+     input_text_offset is at the \n just before the line start. */
+#define menu_starter "* "
+static char *
+glean_node_from_menu (int remember_reference)
+{
+  int i, orig_offset = input_text_offset;
+  char *nodename;
+
+  if (strncmp (&input_text[input_text_offset + 1],
+	       menu_starter,
+	       strlen (menu_starter)) != 0)
+    return ((char *)NULL);
+  else
+    input_text_offset += strlen (menu_starter) + 1;
+
+  get_until_in_line (":", &nodename);
+  if (curchar () == ':')
+    input_text_offset++;
+  canon_white (nodename);
+
+  if (curchar () == ':')
+    goto save_node;
+
+  free (nodename);
+  get_rest_of_line (&nodename);
+
+  /* Special hack: If the nodename follows the menu item name,
+     then we have to read the rest of the line in order to find
+     out what the nodename is.  But we still have to read the
+     line later, in order to process any formatting commands that
+     might be present.  So un-count the carriage return that has just
+     been counted. */
+  line_number--;
+
+  isolate_nodename (nodename);
+
+save_node:
+  input_text_offset = orig_offset;
+  normalize_node_name (nodename);
+  i = strlen (nodename);
+  if (i && nodename[i - 1] == ':')
+    nodename[i - 1] = '\0';
+
+  if (remember_reference)
+    {
+      remember_node_reference (nodename, line_number, menu_reference);
+      free (nodename);
+      return ((char *)NULL);
+    }
+  else
+    return (nodename);
+}
+
+static void
+isolate_nodename (char *nodename)
+{
+  register int i, c;
+  int paren_seen, paren;
+
+  if (!nodename)
+    return;
+
+  canon_white (nodename);
+  paren_seen = paren = i = 0;
+
+  if (*nodename == '.' || !*nodename)
+    {
+      *nodename = '\0';
+      return;
+    }
+
+  if (*nodename == '(')
+    {
+      paren++;
+      paren_seen++;
+      i++;
+    }
+
+  for (; (c = nodename[i]); i++)
+    {
+      if (paren)
+	{
+	  if (c == '(')
+	    paren++;
+	  else if (c == ')')
+	    paren--;
+
+	  continue;
+	}
+
+      /* If the character following the close paren is a space, then this
+	 node has no more characters associated with it. */
+      if (c == '\t' ||
+	  c == '\n' ||
+	  c == ','  ||
+	  ((paren_seen && nodename[i - 1] == ')') &&
+	   (c == ' ' || c == '.')) ||
+	  (c == '.' &&
+	   ((!nodename[i + 1] ||
+	     (cr_or_whitespace (nodename[i + 1])) ||
+	     (nodename[i + 1] == ')')))))
+	break;
+    }
+  nodename[i] = '\0';
+}
+
+static void
+cm_menu (void)
+{
+  begin_insertion (menu);
+}
+
+
+/* **************************************************************** */
+/*								    */
+/*			Cross Reference Hacking			    */
+/*								    */
+/* **************************************************************** */
+
+static char *
+get_xref_token (void)
+{
+  char *string;
+
+  get_until_in_braces (",", &string);
+  if (curchar () == ',')
+    input_text_offset++;
+  fix_whitespace (string);
+  return (string);
+}
+
+int px_ref_flag = 0;		/* Controls initial output string. */
+
+/* Make a cross reference. */
+static void
+cm_xref (int arg)
+{
+  if (arg == START)
+    {
+      char *arg1, *arg2, *arg3, *arg4, *arg5;
+
+      arg1 = get_xref_token ();
+      arg2 = get_xref_token ();
+      arg3 = get_xref_token ();
+      arg4 = get_xref_token ();
+      arg5 = get_xref_token ();
+
+      add_word_args ("%s", px_ref_flag ? "*note " : "*Note ");
+
+      if (*arg5 || *arg4)
+	{
+	  char *node_name;
+
+	  if (!*arg2)
+	    {
+	      if (*arg3)
+		node_name = arg3;
+	      else
+		node_name = arg1;
+	    }
+	  else
+	    node_name = arg2;
+
+	  execute_string ("%s: (%s)%s", node_name, arg4, arg1);
+	  return;
+	}
+      else
+	remember_node_reference (arg1, line_number, followed_reference);
+
+      if (*arg3)
+	{
+	  if (!*arg2)
+	    execute_string ("%s: %s", arg3, arg1);
+	  else
+	    execute_string ("%s: %s", arg2, arg1);
+	}
+      else
+	{
+	  if (*arg2)
+	    execute_string ("%s: %s", arg2, arg1);
+	  else
+	    execute_string ("%s::", arg1);
+	}
+
+      /* Free all of the arguments found. */
+      if (arg1) free (arg1);
+      if (arg2) free (arg2);
+      if (arg3) free (arg3);
+      if (arg4) free (arg4);
+      if (arg5) free (arg5);
+    }
+  else
+    {
+      /* Check to make sure that the next non-whitespace character is either
+         a period or a comma. input_text_offset is pointing at the "}" which
+         ended the xref or pxref command. */
+      int temp = input_text_offset + 1;
+
+      if (output_paragraph[output_paragraph_offset - 2] == ':' &&
+	  output_paragraph[output_paragraph_offset - 1] == ':')
+	return;
+      while (temp < size_of_input_text)
+	{
+	  if (cr_or_whitespace (input_text[temp]))
+	    temp++;
+	  else
+	    {
+	      if (input_text[temp] == '.' ||
+		  input_text[temp] == ',' ||
+		  input_text[temp] == '\t')
+		return;
+	      else
+		{
+		  line_error (
+		"Cross-reference must be terminated with a period or a comma");
+		  return;
+		}
+	    }
+	}
+    }
+}
+
+static void
+cm_pxref (int arg)
+{
+  if (arg == START)
+    {
+      px_ref_flag++;
+      cm_xref (arg);
+      px_ref_flag--;
+    }
+  else
+    add_char ('.');
+}
+
+static void
+cm_inforef (int arg)
+{
+  if (arg == START)
+    {
+      char *node, *pname, *file;
+
+      node = get_xref_token ();
+      pname = get_xref_token ();
+      file = get_xref_token ();
+
+      execute_string ("*note %s: (%s)%s", pname, file, node);
+    }
+}
+
+/* **************************************************************** */
+/*								    */
+/*			Insertion Command Stubs			    */
+/*								    */
+/* **************************************************************** */
+
+static void
+cm_quotation (void)
+{
+  begin_insertion (quotation);
+}
+
+static void
+cm_example (void)
+{
+  begin_insertion (example);
+}
+
+static void
+cm_smallexample (void)
+{
+  begin_insertion (smallexample);
+}
+
+static void
+cm_lisp (void)
+{
+  begin_insertion (lisp);
+}
+
+static void
+cm_smalllisp (void)
+{
+  begin_insertion (smalllisp);
+}
+
+/* @cartouche/@end cartouche draws box with rounded corners in
+   TeX output.  Right now, just a NOP insertion. */
+static void
+cm_cartouche (void)
+{
+  begin_insertion (cartouche);
+}
+
+static void
+cm_format (void)
+{
+  begin_insertion (format);
+}
+
+static void
+cm_display (void)
+{
+  begin_insertion (display);
+}
+
+static void
+cm_itemize (void)
+{
+  begin_insertion (itemize);
+}
+
+static void
+cm_enumerate (void)
+{
+  do_enumeration (enumerate, "1");
+}
+
+/* Start an enumeration insertion of type TYPE.  If the user supplied
+   no argument on the line, then use DEFAULT_STRING as the initial string. */
+static void
+do_enumeration (int type, char *default_string)
+{
+  get_until_in_line (".", &enumeration_arg);
+  canon_white (enumeration_arg);
+
+  if (!*enumeration_arg)
+    {
+      free (enumeration_arg);
+      enumeration_arg = strdup (default_string);
+    }
+
+  if (!isdigit (*enumeration_arg) && !isletter (*enumeration_arg))
+    {
+      warning ("%s requires a letter or a digit", insertion_type_pname (type));
+
+      switch (type)
+	{
+	case enumerate:
+	  default_string = "1";
+	  break;
+	}
+      enumeration_arg = strdup (default_string);
+    }
+  begin_insertion (type);
+}
+
+static void
+cm_table (void)
+{
+  begin_insertion (table);
+}
+
+static void
+cm_ftable (void)
+{
+  begin_insertion (ftable);
+}
+
+static void
+cm_vtable (void)
+{
+  begin_insertion (vtable);
+}
+
+static void
+cm_group (void)
+{
+  begin_insertion (group);
+}
+
+static void
+cm_ifinfo (void)
+{
+  /* begin_insertion (ifinfo); */
+  /* BPW: @ifinfo can cross hierarchies */
+  ifinfo_count++;
+  if (in_menu)
+    discard_until ("\n");  
+}
+
+/* Begin an insertion where the lines are not filled or indented. */
+static void
+cm_flushleft (void)
+{
+  begin_insertion (flushleft);
+}
+
+/* Begin an insertion where the lines are not filled, and each line is
+   forced to the right-hand side of the page. */
+static void
+cm_flushright (void)
+{
+  begin_insertion (flushright);
+}
+
+
+/* **************************************************************** */
+/*								    */
+/*			  Conditional Handling			    */
+/*								    */
+/* **************************************************************** */
+
+/* A structure which contains `defined' variables. */
+typedef struct _defines {
+  struct _defines *next;
+  char *name;
+  char *value;
+} DEFINE;
+
+/* The linked list of `set' defines. */
+DEFINE *defines = (DEFINE *)NULL;
+
+/* Add NAME to the list of `set' defines. */
+static void
+set (char *name, char *value)
+{
+  DEFINE *temp;
+
+  for (temp = defines; temp; temp = temp->next)
+    if (strcmp (name, temp->name) == 0)
+      {
+	free (temp->value);
+	temp->value = strdup (value);
+	return;
+      }
+
+  temp = (DEFINE *)xmalloc (sizeof (DEFINE));
+  temp->next = defines;
+  temp->name = strdup (name);
+  temp->value = strdup (value);
+  defines = temp;
+}
+
+/* Remove NAME from the list of `set' defines. */
+static void
+clear (char *name)
+{
+  register DEFINE *temp, *last;
+
+  last = (DEFINE *)NULL;
+  temp = defines;
+
+  while (temp)
+    {
+      if (strcmp (temp->name, name) == 0)
+	{
+	  if (last)
+	    last->next = temp->next;
+	  else
+	    defines = temp->next;
+
+	  free (temp->name);
+	  free (temp->value);
+	  free (temp);
+	  break;
+	}
+      last = temp;
+      temp = temp->next;
+    }
+}
+
+/* Return the value of NAME.  The return value is NULL if NAME is unset. */
+static char *
+set_p (char *name)
+{
+  register DEFINE *temp;
+
+  for (temp = defines; temp; temp = temp->next)
+    if (strcmp (temp->name, name) == 0)
+      return (temp->value);
+
+  return ((char *)NULL);
+}
+
+/* Conditionally parse based on the current command name. */
+static void
+command_name_condition (void)
+{
+  char *discarder;
+
+  discarder = (char *)xmalloc (8 + strlen (command));
+
+  sprintf (discarder, "\n%cend %s", COMMAND_PREFIX, command);
+  discard_until (discarder);
+  discard_until ("\n");
+
+  free (discarder);
+}
+
+/* Create a variable whose name appears as the first word on this line. */
+static void
+cm_set (void)
+{
+  handle_variable (SET);
+}
+
+/* Remove a variable whose name appears as the first word on this line. */
+static void
+cm_clear (void)
+{
+  handle_variable (CLEAR);
+}
+
+static void
+cm_ifset (void)
+{
+  handle_variable (IFSET);
+}
+
+static void
+cm_ifclear (void)
+{
+  handle_variable (IFCLEAR);
+}
+
+/* This command takes braces, but we parse the contents specially, so we
+   don't use the standard brace popping code.
+
+   The syntax @ifeq{arg1, arg2, texinfo commands} performs texinfo commands
+   if ARG1 and ARG2 caselessly string compare to the same string, otherwise,
+   it produces no output. */
+static void
+cm_ifeq (void)
+{
+  char **arglist;
+
+  arglist = get_brace_args (0);
+
+  if (arglist)
+    {
+      if (array_len (arglist) > 1)
+	{
+	  if ((strcasecmp (arglist[0], arglist[1]) == 0) &&
+	      (arglist[2] != (char *)NULL))
+	    execute_string ("%s\n", arglist[2]);
+	}
+
+      free_array (arglist);
+    }
+}
+
+static void
+cm_value (int arg, int start_pos, int end_pos)
+{
+  if (arg == END)
+    {
+      char *name, *value;
+      name = (char *)&output_paragraph[start_pos];
+      output_paragraph[end_pos] = '\0';
+      name = strdup (name);
+      value = set_p (name);
+      output_column -= end_pos - start_pos;
+      output_paragraph_offset = start_pos;
+
+      if (value)
+        execute_string ("%s", value);
+      else
+	add_word_args ("{No Value For \"%s\"}", name);
+
+      free (name);
+    }
+}
+
+/* Set, clear, or conditionalize based on ACTION. */
+static void
+handle_variable (int action)
+{
+  char *name;
+
+  get_rest_of_line (&name);
+  backup_input_pointer ();
+  canon_white (name);
+  handle_variable_internal (action, name);
+  free (name);
+}
+
+static void
+handle_variable_internal (int action, char *name)
+{
+  char *temp;
+  int delimiter, additional_text_present = 0;
+
+  /* Only the first word of NAME is a valid tag. */
+  temp = name;
+  delimiter = 0;
+  while (*temp && (delimiter || !whitespace (*temp)))
+    {
+/* #if defined (SET_WITH_EQUAL) */
+      if (*temp == '"' || *temp == '\'')
+	{
+	  if (*temp == delimiter)
+	    delimiter = 0;
+	  else
+	    delimiter = *temp;
+	}
+/* #endif SET_WITH_EQUAL */
+      temp++;
+    }
+
+  if (*temp)
+    additional_text_present++;
+
+  *temp = '\0';
+
+  if (!*name)
+    line_error ("%c%s requires a name", COMMAND_PREFIX, command);
+  else
+    {
+      switch (action)
+	{
+	case SET:
+	  {
+	    char *value;
+
+#if defined (SET_WITH_EQUAL)
+	    /* Allow a value to be saved along with a variable.  The value is
+	       the text following an `=' sign in NAME, if any is present. */
+
+	    for (value = name; *value && *value != '='; value++);
+
+	    if (*value)
+	      *value++ = '\0';
+
+	    if (*value == '"' || *value == '\'')
+	      {
+		value++;
+		value[strlen (value) - 1] = '\0';
+	      }
+
+#else /* !SET_WITH_EQUAL */
+	    /* The VALUE of NAME is the remainder of the line sans
+	       whitespace. */
+	    if (additional_text_present)
+	      {
+		value = temp + 1;
+		canon_white (value);
+	      }
+	    else
+	      value = "";
+#endif /* !SET_WITH_VALUE */
+
+	    set (name, value);
+	  }
+	  break;
+
+	case CLEAR:
+	  clear (name);
+	  break;
+
+	case IFSET:
+	case IFCLEAR:
+	  /* If IFSET and NAME is not set, or if IFCLEAR and NAME is set,
+	     read lines from the the file until we reach a matching
+	     "@end CONDITION".  This means that we only take note of
+	     "@ifset/clear" and "@end" commands. */
+	  {
+	    char condition[8];
+	    int condition_len;
+
+	    if (action == IFSET)
+	      strcpy (condition, "ifset");
+	    else
+	      strcpy (condition, "ifclear");
+
+	    condition_len = strlen (condition);
+
+	  if ((action == IFSET && !set_p (name)) ||
+	      (action == IFCLEAR && set_p (name)))
+	    {
+	      int level = 0, done = 0;
+
+	      while (!done)
+		{
+		  char *freeable_line, *line;
+
+		  get_rest_of_line (&freeable_line);
+
+		  for (line = freeable_line; whitespace (*line); line++);
+
+		  if (*line == COMMAND_PREFIX &&
+		      (strncmp (line + 1, condition, condition_len) == 0))
+		    level++;
+		  else if (strncmp (line, "@end", 4) == 0)
+		    {
+		      char *cname = line + 4;
+		      char *temp2;
+
+		      while (*cname && whitespace (*cname))
+			cname++;
+		      temp2 = cname;
+
+		      while (*temp2 && !whitespace (*temp2))
+			temp2++;
+		      *temp2 = '\0';
+
+		      if (strcmp (cname, condition) == 0)
+			{
+			  if (!level)
+			    {
+			      done = 1;
+			    }
+			  else
+			    level--;
+			}
+		    }
+		  free (freeable_line);
+		}
+	      /* We found the end of a false @ifset/ifclear.  If we are
+		 in a menu, back up over the newline that ends the ifset,
+		 since that newline may also begin the next menu entry. */
+	      break;
+	    }
+	  else
+	    {
+	      if (action == IFSET)
+		begin_insertion (ifset);
+	      else
+		begin_insertion (ifclear);
+	    }
+	  }
+	  break;
+	}
+    }
+}
+
+
+/* **************************************************************** */
+/*								    */
+/*		    Execution of Random Text not in file	    */
+/*								    */
+/* **************************************************************** */
+
+typedef struct {
+  char *string;			/* The string buffer. */
+  int size;			/* The size of the buffer. */
+  int in_use;			/* Non-zero means string currently in use. */
+} EXECUTION_STRING;
+
+static EXECUTION_STRING **execution_strings = (EXECUTION_STRING **)NULL;
+static int execution_strings_index = 0;
+static int execution_strings_slots = 0;
+
+static EXECUTION_STRING *
+get_execution_string (int initial_size)
+{
+  register int i = 0;
+  EXECUTION_STRING *es = (EXECUTION_STRING *)NULL;
+
+  if (execution_strings)
+    {
+      for (i = 0; i < execution_strings_index; i++)
+	if (execution_strings[i] && (execution_strings[i]->in_use == 0))
+	  {
+	    es = execution_strings[i];
+	    break;
+	  }
+    }
+
+  if (!es)
+    {
+      if (execution_strings_index + 1 >= execution_strings_slots)
+	{
+	  execution_strings = (EXECUTION_STRING **)xrealloc
+	    (execution_strings,
+	     (execution_strings_slots += 3) * sizeof (EXECUTION_STRING *));
+	  for (; i < execution_strings_slots; i++)
+	    execution_strings[i] = (EXECUTION_STRING *)NULL;
+	}
+
+      execution_strings[execution_strings_index] =
+	(EXECUTION_STRING *)xmalloc (sizeof (EXECUTION_STRING));
+      es = execution_strings[execution_strings_index];
+      execution_strings_index++;
+
+      es->size = 0;
+      es->string = (char *)NULL;
+      es->in_use = 0;
+    }
+
+  if (initial_size > es->size)
+    {
+      es->string = (char *) xrealloc (es->string, initial_size);
+      es->size = initial_size;
+    }
+  return (es);
+}
+
+/* Execute the string produced by formatting the ARGs with FORMAT.  This
+   is like submitting a new file with @include. */
+
+static void
+execute_string (char *formatte, ...)
+{
+  va_list ap;
+  EXECUTION_STRING *es;
+  char *temp_string;
+
+  es = get_execution_string (4000);
+  temp_string = es->string;
+  es->in_use = 1;
+
+  va_start (ap, formatte);
+  vsprintf (temp_string, formatte, ap);
+  va_end (ap);
+
+  pushfile ();
+  input_text_offset = 0;
+  input_text = temp_string;
+  input_filename = strdup (input_filename);
+  size_of_input_text = strlen (temp_string);
+
+  executing_string++;
+  reader_loop ();
+  free (input_filename);
+
+  popfile ();
+  executing_string--;
+  es->in_use = 0;
+}
+
+/* **************************************************************** */
+/*								    */
+/*			@itemx, @item				    */
+/*								    */
+/* **************************************************************** */
+
+static int itemx_flag = 0;
+
+static void
+cm_itemx (void)
+{
+  itemx_flag++;
+  cm_item ();
+  itemx_flag--;
+}
+
+static void
+cm_item (void)
+{
+  char *rest_of_line, *item_func;
+
+  /* Can only hack "@item" while inside of an insertion. */
+  if (insertion_level)
+    {
+      INSERTION_ELT *stack = insertion_stack;
+      int original_input_text_offset;
+
+      skip_whitespace ();
+      original_input_text_offset = input_text_offset;
+
+      get_rest_of_line (&rest_of_line);
+      canon_white (rest_of_line);
+      item_func = current_item_function ();
+
+      /* Okay, do the right thing depending on which insertion function
+	 is active. */
+
+    switch_top:
+      switch (stack->insertion)
+	{
+	case ifinfo:
+	case ifset:
+	case ifclear:
+	case cartouche:
+	  stack = stack->next;
+	  if (!stack)
+	    goto no_insertion;
+	  else
+	    goto switch_top;
+	  break;
+
+	case menu:
+	case quotation:
+	case example:
+	case smallexample:
+	case lisp:
+	case format:
+	case display:
+	case group:
+	  line_error ("The `%c%s' command is meaningless within a `@%s' block",
+		      COMMAND_PREFIX, command,
+		      insertion_type_pname (current_insertion_type ()));
+	  break;
+
+	case itemize:
+	case enumerate:
+	  if (itemx_flag)
+	    {
+	      line_error ("%citemx is not meaningful inside of a `%s' block",
+			  COMMAND_PREFIX,
+			  insertion_type_pname (current_insertion_type ()));
+	    }
+	  else
+	    {
+	      start_paragraph ();
+	      kill_self_indent (-1);
+	      filling_enabled = indented_fill = 1;
+
+	      if (current_insertion_type () == itemize)
+		{
+		  indent (output_column = current_indent - 2);
+
+		  /* I need some way to determine whether this command
+		     takes braces or not.  I believe the user can type
+		     either "@bullet" or "@bullet{}".  Of course, they
+		     can also type "o" or "#" or whatever else they want. */
+		  if (item_func && *item_func)
+		    {
+		      if (*item_func == COMMAND_PREFIX)
+			if (item_func[strlen (item_func) - 1] != '}')
+			  execute_string ("%s{}", item_func);
+			else
+			  execute_string ("%s", item_func);
+		      else
+			execute_string ("%s", item_func);
+		    }
+		  insert (' ');
+		  output_column++;
+		}
+	      else
+		enumerate_item ();
+
+	      /* Special hack.  This makes close paragraph ignore you until
+		 the start_paragraph () function has been called. */
+	      must_start_paragraph = 1;
+
+	      /* Ultra special hack.  It appears that some people incorrectly
+		 place text directly after the @item, instead of on a new line
+		 by itself.  This happens to work in TeX, so I make it work
+		 here. */
+	      if (*rest_of_line)
+		{
+		  line_number--;
+		  input_text_offset = original_input_text_offset;
+		}
+	    }
+	  break;
+
+	case table:
+	case ftable:
+	case vtable:
+	  {
+	    /* Get rid of extra characters. */
+	    kill_self_indent (-1);
+
+	    /* close_paragraph () almost does what we want.  The problem
+	       is when paragraph_is_open, and last_char_was_newline, and
+	       the last newline has been turned into a space, because
+	       filling_enabled. I handle it here. */
+	    if (last_char_was_newline && filling_enabled && paragraph_is_open)
+	      insert ('\n');
+	    close_paragraph ();
+
+#if defined (INDENT_PARAGRAPHS_IN_TABLE)
+	    /* Indent on a new line, but back up one indentation level. */
+	    {
+	      int t;
+
+	      t = inhibit_paragraph_indentation;
+	      inhibit_paragraph_indentation = 1;
+	      /* At this point, inserting any non-whitespace character will
+		 force the existing indentation to be output. */
+	      add_char ('i');
+	      inhibit_paragraph_indentation = t;
+	    }
+#else /* !INDENT_PARAGRAPHS_IN_TABLE */
+	    add_char ('i');
+#endif /* !INDENT_PARAGRAPHS_IN_TABLE */
+
+	    output_paragraph_offset--;
+	    kill_self_indent (default_indentation_increment + 1);
+
+	    /* Add item's argument to the line. */
+	    filling_enabled = 0;
+	    if (item_func && *item_func)
+ 	      execute_string ("%s{%s}", item_func, rest_of_line);
+ 	    else
+ 	      execute_string ("%s", rest_of_line);
+
+	    if (current_insertion_type () == ftable)
+	      execute_string ("%cfindex %s\n", COMMAND_PREFIX, rest_of_line);
+
+	    if (current_insertion_type () == vtable)
+	      execute_string ("%cvindex %s\n", COMMAND_PREFIX, rest_of_line);
+
+	    /* Start a new line, and let start_paragraph ()
+	       do the indenting of it for you. */
+	    close_single_paragraph ();
+	    indented_fill = filling_enabled = 1;
+	  }
+
+	default:
+	  break;
+	}
+      free (rest_of_line);
+    }
+  else
+    {
+    no_insertion:
+      line_error ("%c%s found outside of an insertion block",
+		  COMMAND_PREFIX, command);
+    }
+}
+
+
+/* **************************************************************** */
+/*								    */
+/*			Defun and Friends       		    */
+/*								    */
+/* **************************************************************** */
+
+#define DEFUN_SELF_DELIMITING(c)					\
+  (((c) == '(')								\
+   || ((c) == ')')							\
+   || ((c) == '[')							\
+   || ((c) == ']'))
+
+struct token_accumulator
+{
+  unsigned int length;
+  unsigned int index;
+  char **tokens;
+};
+
+static void
+initialize_token_accumulator (struct token_accumulator *accumulator)
+{
+  (accumulator->length) = 0;
+  (accumulator->index) = 0;
+  (accumulator->tokens) = NULL;
+}
+
+static void
+accumulate_token (struct token_accumulator *accumulator, char *token)
+{
+  if ((accumulator->index) >= (accumulator->length))
+    {
+      (accumulator->length) += 10;
+      (accumulator->tokens) = (char **) xrealloc
+	(accumulator->tokens, (accumulator->length * sizeof (char *)));
+    }
+  accumulator->tokens[accumulator->index] = token;
+  accumulator->index += 1;
+}
+
+static char *
+copy_substring (char *start, char *end)
+{
+  char *result, *scan, *scan_result;
+
+  result = (char *) xmalloc ((end - start) + 1);
+  scan_result = result;
+  scan = start;
+
+  while (scan < end)
+    *scan_result++ = *scan++;
+
+  *scan_result = '\0';
+  return (result);
+}
+
+/* Given `string' pointing at an open brace, skip forward and return a
+   pointer to just past the matching close brace. */
+static int
+scan_group_in_string (char **string_pointer)
+{
+  register int c;
+  register char *scan_string;
+  register unsigned int level = 1;
+
+  scan_string = (*string_pointer) + 1;
+
+  while (1)
+    {
+      if (level == 0)
+	{
+	  (*string_pointer) = scan_string;
+	  return (1);
+	}
+      c = (*scan_string++);
+      if (c == '\0')
+	{
+	  /* Tweak line_number to compensate for fact that
+	     we gobbled the whole line before coming here. */
+	  line_number -= 1;
+	  line_error ("Missing `}' in %cdef arg", COMMAND_PREFIX);
+	  line_number += 1;
+	  (*string_pointer) = (scan_string - 1);
+	  return (0);
+	}
+      if (c == '{')
+	level += 1;
+      if (c == '}')
+	level -= 1;
+    }
+}
+
+/* Return a list of tokens from the contents of `string'.
+   Commands and brace-delimited groups count as single tokens.
+   Contiguous whitespace characters are converted to a token
+   consisting of a single space. */
+static char **
+args_from_string (char *string)
+{
+  struct token_accumulator accumulator;
+  register char *scan_string = string;
+  char *token_start, *token_end;
+
+  initialize_token_accumulator (&accumulator);
+
+  while ((*scan_string) != '\0')
+    {
+      /* Replace arbitrary whitespace by a single space. */
+      if (whitespace (*scan_string))
+	{
+	  scan_string += 1;
+	  while (whitespace (*scan_string))
+	    scan_string += 1;
+	  accumulate_token ((&accumulator), (strdup (" ")));
+	  continue;
+	}
+
+      /* Commands count as single tokens. */
+      if ((*scan_string) == COMMAND_PREFIX)
+	{
+	  token_start = scan_string;
+	  scan_string += 1;
+	  if (self_delimiting (*scan_string))
+	    scan_string += 1;
+	  else
+	    {
+	      register int c;
+	      while (1)
+		{
+		  c = *scan_string++;
+
+ 		  if ((c == '\0') || (c == '{') || (whitespace (c)))
+		    {
+		      scan_string -= 1;
+		      break;
+		    }
+		}
+
+	      if (*scan_string == '{')
+		{
+		  char *s = scan_string;
+		  (void) scan_group_in_string (&s);
+		  scan_string = s;
+		}
+	    }
+	  token_end = scan_string;
+	}
+
+      /* Parentheses and brackets are self-delimiting. */
+      else if (DEFUN_SELF_DELIMITING (*scan_string))
+	{
+	  token_start = scan_string;
+	  scan_string += 1;
+	  token_end = scan_string;
+	}
+
+      /* Open brace introduces a group that is a single token. */
+      else if (*scan_string == '{')
+	{
+	  char *s = scan_string;
+	  int balanced = scan_group_in_string (&s);
+
+	  token_start = scan_string + 1;
+	  scan_string = s;
+	  token_end = balanced ? (scan_string - 1) : scan_string;
+	}
+
+      /* Otherwise a token is delimited by whitespace, parentheses,
+	 brackets, or braces.  A token is also ended by a command. */
+      else
+	{
+	  token_start = scan_string;
+
+	  while (1)
+	    {
+	      register int c;
+
+	      c = *scan_string++;
+
+	      if (!c ||
+		  (whitespace (c) || DEFUN_SELF_DELIMITING (c) ||
+		   c == '{' || c == '}'))
+		{
+		  scan_string--;
+		  break;
+		}
+
+	      /* If we encounter a command imbedded within a token,
+		 then end the token. */
+	      if (c == COMMAND_PREFIX)
+		{
+		  scan_string--;
+		  break;
+		}
+	    }
+	  token_end = scan_string;
+	}
+
+      accumulate_token
+	(&accumulator, copy_substring (token_start, token_end));
+    }
+  accumulate_token (&accumulator, NULL);
+  return (accumulator.tokens);
+}
+
+static void
+process_defun_args (char **defun_args, int auto_var_p)
+{
+  int pending_space = 0;
+
+  while (1)
+    {
+      char *defun_arg = *defun_args++;
+
+      if (defun_arg == NULL)
+	break;
+
+      if (defun_arg[0] == ' ')
+	{
+	  pending_space = 1;
+	  continue;
+	}
+
+      if (pending_space)
+	{
+	  add_char (' ');
+	  pending_space = 0;
+	}
+
+      if (DEFUN_SELF_DELIMITING (defun_arg[0]))
+	add_char (defun_arg[0]);
+      else if (defun_arg[0] == '&')
+	add_word (defun_arg);
+      else if (defun_arg[0] == COMMAND_PREFIX)
+	execute_string ("%s", defun_arg);
+      else if (auto_var_p)
+	execute_string ("%cvar{%s}", COMMAND_PREFIX, defun_arg);
+      else
+	add_word (defun_arg);
+    }
+}
+
+static char *
+next_nonwhite_defun_arg (char ***arg_pointer)
+{
+  char **scan = (*arg_pointer);
+  char *arg = (*scan++);
+
+  if ((arg != 0) && (*arg == ' '))
+    arg = *scan++;
+
+  if (arg == 0)
+    scan -= 1;
+
+  *arg_pointer = scan;
+
+  return ((arg == 0) ? "" : arg);
+}
+
+/* Make the defun type insertion.
+   TYPE says which insertion this is.
+   X_P says not to start a new insertion if non-zero. */
+static void
+defun_internal (enum insertion_type type, int x_p)
+{
+  enum insertion_type base_type;
+  char **defun_args, **scan_args;
+  char *category, *defined_name, *type_name, *type_name2;
+
+  {
+    char *line;
+    get_rest_of_line (&line);
+    defun_args = (args_from_string (line));
+    free (line);
+  }
+
+  scan_args = defun_args;
+
+  switch (type)
+    {
+    case defun:
+      category = "Function";
+      base_type = deffn;
+      break;
+    case defmac:
+      category = "Macro";
+      base_type = deffn;
+      break;
+    case defspec:
+      category = "Special Form";
+      base_type = deffn;
+      break;
+    case defvar:
+      category = "Variable";
+      base_type = defvr;
+      break;
+    case defopt:
+      category = "User Option";
+      base_type = defvr;
+      break;
+    case deftypefun:
+      category = "Function";
+      base_type = deftypefn;
+      break;
+    case deftypevar:
+      category = "Variable";
+      base_type = deftypevr;
+      break;
+    case defivar:
+      category = "Instance Variable";
+      base_type = defcv;
+      break;
+    case defmethod:
+      category = "Method";
+      base_type = defop;
+      break;
+    case deftypemethod:
+      category = "Method";
+      base_type = deftypemethod;
+      break;
+    default:
+      category = next_nonwhite_defun_arg (&scan_args);
+      base_type = type;
+      break;
+    }
+
+  if ((base_type == deftypefn)
+      || (base_type == deftypevr)
+      || (base_type == defcv)
+      || (base_type == defop)
+      || (base_type == deftypemethod))
+    type_name = next_nonwhite_defun_arg (&scan_args);
+
+  if (base_type == deftypemethod)
+    type_name2 = next_nonwhite_defun_arg (&scan_args);
+
+  defined_name = next_nonwhite_defun_arg (&scan_args);
+
+  /* This hack exists solely for the purposes of formatting the texinfo
+     manual.  I couldn't think of a better way.  The token might be
+     a simple @@ followed immediately by more text.  If this is the case,
+     then the next defun arg is part of this one, and we should concatenate
+     them. */
+  if (*scan_args && **scan_args && !whitespace (**scan_args) &&
+      (strcmp (defined_name, "@@") == 0))
+    {
+      char *tem = (char *)xmalloc (3 + strlen (scan_args[0]));
+
+      sprintf (tem, "@@%s", scan_args[0]);
+
+      free (scan_args[0]);
+      scan_args[0] = tem;
+      scan_args++;
+      defined_name = tem;
+    }
+
+  if (!x_p)
+    begin_insertion (type);
+
+  /* Write the definition header line.
+     This should start at the normal indentation.  */
+  current_indent -= default_indentation_increment;
+  start_paragraph ();
+
+  switch (base_type)
+    {
+    case deffn:
+    case defvr:
+    case deftp:
+      execute_string (" -- %s: %s", category, defined_name);
+      break;
+    case deftypefn:
+    case deftypevr:
+      execute_string (" -- %s: %s %s", category, type_name, defined_name);
+      break;
+    case defcv:
+      execute_string (" -- %s of %s: %s", category, type_name, defined_name);
+      break;
+    case defop:
+      execute_string (" -- %s on %s: %s", category, type_name, defined_name);
+      break;
+    case deftypemethod:
+      execute_string (" -- %s on %s: %s %s", category, type_name, type_name2,
+		      defined_name);
+      break;
+    default:
+      break;
+    }
+  current_indent += default_indentation_increment;
+
+  /* Now process the function arguments, if any.
+     If these carry onto the next line, they should be indented by two
+     increments to distinguish them from the body of the definition,
+     which is indented by one increment.  */
+  current_indent += default_indentation_increment;
+
+  switch (base_type)
+    {
+    case deffn:
+    case defop:
+      process_defun_args (scan_args, 1);
+      break;
+    case deftp:
+    case deftypefn:
+    case deftypemethod:
+      process_defun_args (scan_args, 0);
+      break;
+    default:
+      break;
+    }
+  current_indent -= default_indentation_increment;
+  close_single_paragraph ();
+
+  /* Make an entry in the appropriate index. */
+  switch (base_type)
+    {
+    case deffn:
+    case deftypefn:
+      execute_string ("%cfindex %s\n", COMMAND_PREFIX, defined_name);
+      break;
+    case defvr:
+    case deftypevr:
+    case defcv:
+      execute_string ("%cvindex %s\n", COMMAND_PREFIX, defined_name);
+      break;
+    case defop:
+    case deftypemethod:
+      execute_string ("%cfindex %s on %s\n",
+		      COMMAND_PREFIX, defined_name, type_name);
+      break;
+    case deftp:
+      execute_string ("%ctindex %s\n", COMMAND_PREFIX, defined_name);
+      break;
+    default:
+      break;
+    }
+
+  /* Deallocate the token list. */
+  scan_args = defun_args;
+  while (1)
+    {
+      char * arg = (*scan_args++);
+      if (arg == NULL)
+	break;
+      free (arg);
+    }
+  free (defun_args);
+}
+
+/* Add an entry for a function, macro, special form, variable, or option.
+   If the name of the calling command ends in `x', then this is an extra
+   entry included in the body of an insertion of the same type. */
+static void
+cm_defun (void)
+{
+  int x_p;
+  enum insertion_type type;
+  char *temp = strdup (command);
+
+  x_p = (command[strlen (command) - 1] == 'x');
+
+  if (x_p)
+    temp[strlen (temp) - 1] = '\0';
+
+  type = find_type_from_name (temp);
+  free (temp);
+
+  /* If we are adding to an already existing insertion, then make sure
+     that we are already in an insertion of type TYPE. */
+  if (x_p &&
+      (!insertion_level || insertion_stack->insertion != type))
+    {
+      line_error ("Must be in a `%s' insertion in order to use `%s'x",
+		  command, command);
+      discard_until ("\n");
+      return;
+    }
+
+  defun_internal (type, x_p);
+}
+
+/* End existing insertion block. */
+static void
+cm_end (void)
+{
+  char *temp;
+  enum insertion_type type;
+
+  get_rest_of_line (&temp);
+  canon_white (temp);
+
+  if (strlen (temp) == 0)
+    line_error ("`%c%s' needs something after it", COMMAND_PREFIX, command);
+
+  type = find_type_from_name (temp);
+
+  if (type == bad_type)
+    {
+      line_error ("Bad argument to `%s', `%s', using `%s'",
+	   command, temp, insertion_type_pname (current_insertion_type ()));
+    }
+
+  /* see comment under cm_ifinfo() */
+  if (type == ifinfo)
+    {
+      if (!ifinfo_count)
+	line_error ("Unmatched `%c%s'", COMMAND_PREFIX, command);
+      else
+	ifinfo_count--;
+    }
+  else
+    {
+      if (!insertion_level)
+	line_error ("Unmatched `%c%s'", COMMAND_PREFIX, command);
+      else
+	end_insertion (type);
+    }
+
+  free (temp);
+}
+
+
+/* **************************************************************** */
+/*								    */
+/*			Other Random Commands		   	    */
+/*								    */
+/* **************************************************************** */
+
+/* This says to inhibit the indentation of the next paragraph, but
+   not of following paragraphs.  */
+static void
+cm_noindent (void)
+{
+  if (!inhibit_paragraph_indentation)
+    inhibit_paragraph_indentation = -1;
+}
+
+/* I don't know exactly what to do with this.  Should I allow
+   someone to switch filenames in the middle of output?  Since the
+   file could be partially written, this doesn't seem to make sense.
+   Another option: ignore it, since they don't *really* want to
+   switch files.  Finally, complain, or at least warn. */
+static void
+cm_setfilename (void)
+{
+  char *filename;
+  get_rest_of_line (&filename);
+  /* warning ("`@%s %s' encountered and ignored", command, filename); */
+  free (filename);
+}
+
+static void
+cm_ignore_line (void)
+{
+  discard_until ("\n");
+}
+
+/* @br can be immediately followed by `{}', so we have to read those here.
+   It should simply close the paragraph. */
+static void
+cm_br (void)
+{
+  if (looking_at ("{}"))
+    input_text_offset += 2;
+
+  if (curchar () == '\n')
+    {
+      input_text_offset++;
+      line_number++;
+    }
+
+  close_paragraph ();
+}
+
+ /* Insert the number of blank lines passed as argument. */
+static void
+cm_sp (void)
+{
+  int lines;
+  char *line;
+
+  get_rest_of_line (&line);
+
+  if (sscanf (line, "%d", &lines) != 1)
+    {
+      line_error ("%csp requires a positive numeric argument", COMMAND_PREFIX);
+    }
+  else
+    {
+      if (lines < 0)
+	lines = 0;
+
+      while (lines--)
+	add_char ('\n');
+    }
+  free (line);
+}
+
+/* Start a new line with just this text on it.
+   Then center the line of text.
+   This always ends the current paragraph. */
+static void
+cm_center (void)
+{
+  register int i, start, length;
+  int fudge_factor = 1;
+  unsigned char *line;
+
+  close_paragraph ();
+  filling_enabled = indented_fill = 0;
+  cm_noindent ();
+  start = output_paragraph_offset;
+  inhibit_output_flushing ();
+  get_rest_of_line ((char **)&line);
+  execute_string ((char *)line);
+  free (line);
+  uninhibit_output_flushing ();
+
+  i = output_paragraph_offset - 1;
+  while (i > (start - 1) && output_paragraph[i] == '\n')
+	i--;
+
+  output_paragraph_offset = ++i;
+  length = output_paragraph_offset - start;
+
+  if (length < (fill_column - fudge_factor))
+    {
+      line = (unsigned char *)xmalloc (1 + length);
+      memcpy (line, (char *)(output_paragraph + start), length);
+
+      i = (fill_column - fudge_factor - length) / 2;
+      output_paragraph_offset = start;
+
+      while (i--)
+	insert (' ');
+
+      for (i = 0; i < length; i++)
+	insert (line[i]);
+
+      free (line);
+    }
+
+  insert ('\n');
+  close_paragraph ();
+  filling_enabled = 1;
+}
+
+/* Show what an expression returns. */
+static void
+cm_result (int arg)
+{
+  if (arg == END)
+    add_word ("=>");
+}
+
+/* What an expression expands to. */
+static void
+cm_expansion (int arg)
+{
+  if (arg == END)
+    add_word ("==>");
+}
+
+/* Indicates two expressions are equivalent. */
+static void
+cm_equiv (int arg)
+{
+  if (arg == END)
+    add_word ("==");
+}
+
+/* What an expression may print. */
+static void
+cm_print (int arg)
+{
+  if (arg == END)
+    add_word ("-|");
+}
+
+/* An error signaled. */
+static void
+cm_error (int arg)
+{
+  if (arg == END)
+    add_word ("error-->");
+}
+
+/* The location of point in an example of a buffer. */
+static void
+cm_point (int arg)
+{
+  if (arg == END)
+    add_word ("-!-");
+}
+
+/* Start a new line with just this text on it.
+   The text is outdented one level if possible. */
+static void
+cm_exdent (void)
+{
+  char *line;
+  int i = current_indent;
+
+  if (current_indent)
+    current_indent -= default_indentation_increment;
+
+  get_rest_of_line (&line);
+  close_single_paragraph ();
+  execute_string ("%s", line);
+  current_indent = i;
+  free (line);
+  close_single_paragraph ();
+}
+
+static void
+cm_include (void)
+{
+  cm_infoinclude ();
+}
+
+#if !defined (HAVE_STRERROR)
+extern char *sys_errlist[];
+extern int sys_nerr;
+
+char *
+strerror (num)
+     int num;
+{
+  if (num >= sys_nerr)
+    return ("Unknown file system error");
+  else
+    return (sys_errlist[num]);
+}
+#endif /* !HAVE_STRERROR */
+
+/* Remember this file, and move onto the next. */
+static void
+cm_infoinclude (void)
+{
+  char *filename;
+
+#if defined (HAVE_MACROS)
+  if (macro_expansion_output_stream)
+    me_append_before_this_command ();
+#endif /* HAVE_MACROS */
+
+  close_paragraph ();
+  get_rest_of_line (&filename);
+
+#if defined (HAVE_MACROS)
+  if (macro_expansion_output_stream)
+    remember_itext (input_text, input_text_offset);
+#endif /* HAVE_MACROS */
+
+  pushfile ();
+
+  /* In verbose mode we print info about including another file. */
+  if (verbose_mode)
+    {
+      register int i = 0;
+      register FSTACK *stack = filestack;
+
+      for (i = 0, stack = filestack; stack; stack = stack->next, i++);
+
+      i *= 2;
+
+      printf ("%*s", i, "");
+      printf ("%c%s %s\n", COMMAND_PREFIX, command, filename);
+      fflush (stdout);
+    }
+
+  if (!find_and_load (filename))
+    {
+      popfile ();
+      line_number--;
+
+      /* Cannot "@include foo", in line 5 of "/wh/bar". */
+      line_error ("`%c%s %s': %s", COMMAND_PREFIX, command, filename,
+		  strerror (errno));
+
+      free (filename);
+      return;
+    }
+  else
+    {
+#if defined (HAVE_MACROS)
+      if (macro_expansion_output_stream)
+	remember_itext (input_text, input_text_offset);
+#endif /* HAVE_MACROS */
+      reader_loop ();
+    }
+  free (filename);
+  popfile ();
+}
+
+/* The other side of a malformed expression. */
+static void
+misplaced_brace (void)
+{
+  line_error ("Misplaced `}'");
+}
+
+/* Don't let the filling algorithm insert extra whitespace here. */
+static void
+cm_force_abbreviated_whitespace (void)
+{
+}
+
+/* Do not let this character signify the end of a sentence, though
+   if it was seen without the command prefix it normally would.  We
+   do this by turning on the 8th bit of the character. */
+static void
+cm_ignore_sentence_ender (void)
+{
+  add_char (META ((*command)));
+}
+
+/* Signals end of processing.  Easy to make this happen. */
+static void
+cm_bye (void)
+{
+  input_text_offset = size_of_input_text;
+}
+
+static void
+cm_asis (void)
+{
+}
+
+static void
+cm_math (void)
+{
+}
+
+
+/* **************************************************************** */
+/*								    */
+/*			Indexing Stuff				    */
+/*								    */
+/* **************************************************************** */
+
+
+/* An index element... */
+typedef struct index_elt
+{
+  struct index_elt *next;
+  char *entry;			/* The index entry itself. */
+  char *node;			/* The node from whence it came. */
+  int code;			/* Non-zero means add `@code{...}' when
+				   printing this element. */
+  int defining_line;		/* Line number where this entry was written. */
+} INDEX_ELT;
+
+/* A list of short-names for each index, and the index to that index in our
+   index array, the_indices.  In addition, for each index, it is remembered
+   whether that index is a code index or not.  Code indices have @code{}
+   inserted around the first word when they are printed with printindex. */
+typedef struct
+{
+  char *name;
+  int index;
+  int code;
+} INDEX_ALIST;
+
+INDEX_ALIST **name_index_alist = (INDEX_ALIST **) NULL;
+
+/* An array of pointers.  Each one is for a different index.  The
+   "synindex" command changes which array slot is pointed to by a
+   given "index". */
+INDEX_ELT **the_indices = (INDEX_ELT **) NULL;
+
+/* The number of defined indices. */
+int defined_indices = 0;
+
+/* We predefine these. */
+#define program_index 0
+#define function_index 1
+#define concept_index 2
+#define variable_index 3
+#define datatype_index 4
+#define key_index 5
+
+static void
+init_indices (void)
+{
+  int i;
+
+  /* Create the default data structures. */
+
+  /* Initialize data space. */
+  if (!the_indices)
+    {
+      the_indices = (INDEX_ELT **) xmalloc ((1 + defined_indices) *
+					    sizeof (INDEX_ELT *));
+      the_indices[defined_indices] = (INDEX_ELT *) NULL;
+
+      name_index_alist = (INDEX_ALIST **) xmalloc ((1 + defined_indices) *
+						   sizeof (INDEX_ALIST *));
+      name_index_alist[defined_indices] = (INDEX_ALIST *) NULL;
+    }
+
+  /* If there were existing indices, get rid of them now. */
+  for (i = 0; i < defined_indices; i++)
+    undefindex (name_index_alist[i]->name);
+
+  /* Add the default indices. */
+  top_defindex ("pg", 0);
+  top_defindex ("fn", 1);		/* "fn" is a code index.  */
+  top_defindex ("cp", 0);
+  top_defindex ("vr", 0);
+  top_defindex ("tp", 0);
+  top_defindex ("ky", 0);
+
+}
+
+/* Find which element in the known list of indices has this name.
+   Returns -1 if NAME isn't found. */
+static int
+find_index_offset (char *name)
+{
+  register int i;
+  for (i = 0; i < defined_indices; i++)
+    if (name_index_alist[i] &&
+	strcmp (name, name_index_alist[i]->name) == 0)
+      return (name_index_alist[i]->index);
+  return (-1);
+}
+
+/* Return a pointer to the entry of (name . index) for this name.
+   Return NULL if the index doesn't exist. */
+static INDEX_ALIST *
+find_index (char *name)
+{
+  int offset = find_index_offset (name);
+  if (offset > -1)
+    return (name_index_alist[offset]);
+  else
+    return ((INDEX_ALIST *) NULL);
+}
+
+/* Given an index name, return the offset in the_indices of this index,
+   or -1 if there is no such index. */
+static int
+translate_index (char *name)
+{
+  INDEX_ALIST *which = find_index (name);
+
+  if (which)
+    return (which->index);
+  else
+    return (-1);
+}
+
+/* Return the index list which belongs to NAME. */
+static INDEX_ELT *
+index_list (char *name)
+{
+  int which = translate_index (name);
+  if (which < 0)
+    return ((INDEX_ELT *) -1);
+  else
+    return (the_indices[which]);
+}
+
+/* Please release me, let me go... */
+static void
+free_index (INDEX_ELT *indecks)
+{
+  INDEX_ELT *temp;
+
+  while ((temp = indecks) != (INDEX_ELT *) NULL)
+    {
+      free (temp->entry);
+      free (temp->node);
+      indecks = indecks->next;
+      free (temp);
+    }
+}
+
+/* Flush an index by name. */
+static void
+undefindex (char *name)
+{
+  int i;
+  int which = find_index_offset (name);
+
+  if (which < 0)
+    return;
+
+  i = name_index_alist[which]->index;
+
+  free_index (the_indices[i]);
+  the_indices[i] = (INDEX_ELT *) NULL;
+
+  free (name_index_alist[which]->name);
+  free (name_index_alist[which]);
+  name_index_alist[which] = (INDEX_ALIST *) NULL;
+}
+
+/* Define an index known as NAME.  We assign the slot number.
+   CODE if non-zero says to make this a code index. */
+static void
+defindex (char *name, int code)
+{
+  register int i, slot;
+
+  /* If it already exists, flush it. */
+  undefindex (name);
+
+  /* Try to find an empty slot. */
+  slot = -1;
+  for (i = 0; i < defined_indices; i++)
+    if (!name_index_alist[i])
+      {
+	slot = i;
+	break;
+      }
+
+  if (slot < 0)
+    {
+      /* No such luck.  Make space for another index. */
+      slot = defined_indices;
+      defined_indices++;
+
+      name_index_alist = (INDEX_ALIST **)
+	xrealloc ((char *)name_index_alist,
+		  (1 + defined_indices) * sizeof (INDEX_ALIST *));
+      the_indices = (INDEX_ELT **)
+	xrealloc ((char *)the_indices,
+		  (1 + defined_indices) * sizeof (INDEX_ELT *));
+    }
+
+  /* We have a slot.  Start assigning. */
+  name_index_alist[slot] = (INDEX_ALIST *) xmalloc (sizeof (INDEX_ALIST));
+  name_index_alist[slot]->name = strdup (name);
+  name_index_alist[slot]->index = slot;
+  name_index_alist[slot]->code = code;
+
+  the_indices[slot] = (INDEX_ELT *) NULL;
+}
+
+/* Add the arguments to the current index command to the index NAME. */
+static void
+index_add_arg (char *name)
+{
+  int which;
+  char *index_entry;
+  INDEX_ALIST *tem;
+
+  tem = find_index (name);
+
+  which = tem ? tem->index : -1;
+
+#if defined (HAVE_MACROS)
+  if (macro_expansion_output_stream)
+    append_to_expansion_output (input_text_offset + 1);
+#endif /* HAVE_MACROS */
+
+  get_rest_of_line (&index_entry);
+  ignore_blank_line ();
+
+#if defined (HAVE_MACROS)
+  if (macro_expansion_output_stream)
+    {
+      int op_orig;
+
+      remember_itext (input_text, input_text_offset);
+      op_orig = output_paragraph_offset;
+      me_execute_string (index_entry);
+      me_execute_string ("\n");
+      output_paragraph_offset = op_orig;
+    }
+#endif /* HAVE_MACROS */
+
+  if (which < 0)
+    {
+      line_error ("Unknown index reference `%s'", name);
+      free (index_entry);
+    }
+  else
+    {
+      INDEX_ELT *new = (INDEX_ELT *) xmalloc (sizeof (INDEX_ELT));
+      new->next = the_indices[which];
+      new->entry = index_entry;
+      new->node = current_node;
+      new->code = tem->code;
+      new->defining_line = line_number - 1;
+      the_indices[which] = new;
+    }
+}
+
+#define INDEX_COMMAND_SUFFIX "index"
+
+/* The function which user defined index commands call. */
+static void
+gen_index (void)
+{
+  char *name = strdup (command);
+  if ((int) strlen (name) >= (int) strlen ("index"))
+    name[strlen (name) - strlen ("index")] = '\0';
+  index_add_arg (name);
+  free (name);
+}
+
+static void
+top_defindex (char *name, int code)
+{
+  char *temp;
+
+  temp = (char *) xmalloc (1 + strlen (name) + strlen ("index"));
+  sprintf (temp, "%sindex", name);
+  define_user_command (temp, gen_index, 0);
+  defindex (name, code);
+  free (temp);
+}
+
+/* Define a new index command.  Arg is name of index. */
+static void
+cm_defindex (void)
+{
+  gen_defindex (0);
+}
+
+static void
+cm_defcodeindex (void)
+{
+  gen_defindex (1);
+}
+
+static void
+gen_defindex (int code)
+{
+  char *name;
+  get_rest_of_line (&name);
+
+  if (find_index (name))
+    {
+      line_error ("Index `%s' already exists", name);
+      free (name);
+      return;
+    }
+  else
+    {
+      char *temp = (char *) alloca (1 + strlen (name) + strlen ("index"));
+      sprintf (temp, "%sindex", name);
+      define_user_command (temp, gen_index, 0);
+      defindex (name, code);
+      free (name);
+    }
+}
+
+/* Append LIST2 to LIST1.  Return the head of the list. */
+static INDEX_ELT *
+index_append (INDEX_ELT *head, INDEX_ELT *tail)
+{
+  register INDEX_ELT *t_head = head;
+
+  if (!t_head)
+    return (tail);
+
+  while (t_head->next)
+    t_head = t_head->next;
+  t_head->next = tail;
+  return (head);
+}
+
+/* Expects 2 args, on the same line.  Both are index abbreviations.
+   Make the first one be a synonym for the second one, i.e. make the
+   first one have the same index as the second one. */
+static void
+cm_synindex (void)
+{
+  int redirector, redirectee;
+  char *temp;
+
+  skip_whitespace ();
+  get_until_in_line (" ", &temp);
+  redirectee = find_index_offset (temp);
+  skip_whitespace ();
+  free_and_clear (&temp);
+  get_until_in_line (" ", &temp);
+  redirector = find_index_offset (temp);
+  free (temp);
+  if (redirector < 0 || redirectee < 0)
+    {
+      line_error ("Unknown index reference");
+    }
+  else
+    {
+      /* I think that we should let the user make indices synonymous to
+         each other without any lossage of info.  This means that one can
+         say @synindex cp dt anywhere in the file, and things that used to
+         be in cp will go into dt. */
+      INDEX_ELT *i1 = the_indices[redirectee], *i2 = the_indices[redirector];
+
+      if (i1 || i2)
+	{
+	  if (i1)
+	    the_indices[redirectee] = index_append (i1, i2);
+	  else
+	    the_indices[redirectee] = index_append (i2, i1);
+	}
+
+      name_index_alist[redirectee]->index =
+	name_index_alist[redirector]->index;
+    }
+}
+
+static void
+cm_pindex (void)			/* Pinhead index. */
+{
+  index_add_arg ("pg");
+}
+
+static void
+cm_vindex (void)			/* Variable index. */
+{
+  index_add_arg ("vr");
+}
+
+static void
+cm_kindex (void)			/* Key index. */
+{
+  index_add_arg ("ky");
+}
+
+static void
+cm_cindex (void)			/* Concept index. */
+{
+  index_add_arg ("cp");
+}
+
+static void
+cm_findex (void)			/* Function index. */
+{
+  index_add_arg ("fn");
+}
+
+static void
+cm_tindex (void)			/* Data Type index. */
+{
+  index_add_arg ("tp");
+}
+
+/* Sorting the index. */
+static int
+index_element_compare (INDEX_ELT **element1, INDEX_ELT **element2)
+{
+  /* This needs to ignore leading non-text characters. */
+  return (strcasecmp ((*element1)->entry, (*element2)->entry));
+}
+
+/* Force all index entries to be unique. */
+static void
+make_index_entries_unique (INDEX_ELT **array, int count)
+{
+  register int i, j;
+  INDEX_ELT **copy;
+
+  copy = (INDEX_ELT **)xmalloc ((1 + count) * sizeof (INDEX_ELT *));
+
+  for (i = 0, j = 0; i < count; i++)
+    {
+      if ((i == (count - 1)) ||
+	  (array[i]->node != array[i + 1]->node) ||
+	  (strcasecmp (array[i]->entry, array[i + 1]->entry) != 0))
+	copy[j++] = array[i];
+      else
+	{
+	  free (array[i]->entry);
+	  free (array[i]);
+	}
+    }
+  copy[j] = (INDEX_ELT *)NULL;
+
+  /* Now COPY contains only unique entries.  Duplicated entries in the
+     original array have been freed.  Replace the current array with
+     the copy, fixing the NEXT pointers. */
+  for (i = 0; copy[i] != (INDEX_ELT *)NULL; i++)
+    {
+      int counter = 1;
+      copy[i]->next = copy[i + 1];
+
+      /* Fix entry names which are the same.  They point to different nodes,
+	 so we make the entry name unique. */
+      if ((copy[i + 1] != (INDEX_ELT *)NULL) &&
+	  (strcmp (copy[i]->entry, copy[i + 1]->entry) == 0))
+	{
+	  char *new_entry_name;
+
+	  new_entry_name = (char *)xmalloc (10 + strlen (copy[i]->entry));
+	  sprintf (new_entry_name, "%s <%d>", copy[i]->entry, counter);
+	  free (copy[i]->entry);
+	  copy[i]->entry = new_entry_name;
+	  counter++;
+	}
+      else
+	counter = 1;
+
+      array[i] = copy[i];
+    }
+  array[i] = (INDEX_ELT *)NULL;
+
+  /* Free the storage used only by COPY. */
+  free (copy);
+}
+
+/* Sort the index passed in INDEX, returning an array of
+   pointers to elements.  The array is terminated with a NULL
+   pointer.  We call qsort because it's supposed to be fast.
+   I think this looks bad. */
+static INDEX_ELT **
+sort_index (INDEX_ELT *indecks)
+{
+  INDEX_ELT *temp = indecks;
+  INDEX_ELT **array;
+  int count = 0;
+
+  while (temp != (INDEX_ELT *) NULL)
+    {
+      count++;
+      temp = temp->next;
+    }
+
+  /* We have the length.  Make an array. */
+
+  array = (INDEX_ELT **) xmalloc ((count + 1) * sizeof (INDEX_ELT *));
+  count = 0;
+  temp = indecks;
+
+  while (temp != (INDEX_ELT *) NULL)
+    {
+      array[count++] = temp;
+      temp = temp->next;
+    }
+  array[count] = (INDEX_ELT *) NULL;	/* terminate the array. */
+
+  /* Sort the array. */
+  qsort (array, count, sizeof (INDEX_ELT *), (int (*)()) index_element_compare);
+  make_index_entries_unique (array, count);
+  return (array);
+}
+
+/* Non-zero means that we are in the middle of printing an index. */
+int printing_index = 0;
+
+/* Takes one arg, a short name of an index to print.
+   Outputs a menu of the sorted elements of the index. */
+static void
+cm_printindex (void)
+{
+  int item;
+  INDEX_ELT *indecks;
+  INDEX_ELT **array;
+  char *index_name;
+  int old_inhibitions = inhibit_paragraph_indentation;
+  int previous_filling_enabled_value = filling_enabled;
+
+  close_paragraph ();
+  get_rest_of_line (&index_name);
+
+  indecks = index_list (index_name);
+  if (indecks == (INDEX_ELT *)-1)
+    {
+      line_error ("Unknown index name `%s'", index_name);
+      free (index_name);
+      return;
+    }
+  else
+    free (index_name);
+
+  array = sort_index (indecks);
+
+  filling_enabled = 0;
+  inhibit_paragraph_indentation = 1;
+  close_paragraph ();
+  add_word ("* Menu:\n\n");
+
+  printing_index = 1;
+
+#if defined (HAVE_MACROS)
+  me_inhibit_expansion++;
+#endif /* HAVE_MACROS */
+
+  for (item = 0; (indecks = array[item]); item++)
+    {
+      int real_line_number = line_number;
+
+      /* Let errors generated while making the index entry point back
+	 at the line which contains the entry. */
+      line_number = indecks->defining_line;
+
+      /* If this particular entry should be printed as a "code" index,
+	 then wrap the entry with "@code{...}". */
+      if (indecks->code)
+	execute_string ("* %ccode{%s}: ", COMMAND_PREFIX, indecks->entry);
+      else
+	execute_string ("* %s: ", indecks->entry);
+
+      /* Pad the front of the destination nodename so that
+	 the output looks nice. */
+      if (fill_column > 40 && output_column < 40)
+	indent (40 - output_column);
+
+      execute_string ("%s.\n", indecks->node);
+
+      line_number = real_line_number;
+      flush_output ();
+    }
+
+#if defined (HAVE_MACROS)
+      me_inhibit_expansion--;
+#endif /* HAVE_MACROS */
+
+  printing_index = 0;
+  free (array);
+  close_single_paragraph ();
+  filling_enabled = previous_filling_enabled_value;
+  inhibit_paragraph_indentation = old_inhibitions;
+}
+
+
+/* **************************************************************** */
+/*								    */
+/*		    Making User Defined Commands		    */
+/*								    */
+/* **************************************************************** */
+
+static void
+define_user_command (char *name, COMMAND_FUNCTION *proc, int needs_braces_p)
+{
+  int slot = user_command_array_len;
+  user_command_array_len++;
+
+  if (!user_command_array)
+    user_command_array = (COMMAND **) xmalloc (1 * sizeof (COMMAND *));
+
+  user_command_array = (COMMAND **) xrealloc (user_command_array,
+					      (1 + user_command_array_len) *
+					      sizeof (COMMAND *));
+
+  user_command_array[slot] = (COMMAND *) xmalloc (sizeof (COMMAND));
+  user_command_array[slot]->name = strdup (name);
+  user_command_array[slot]->proc = proc;
+  user_command_array[slot]->argument_in_braces = needs_braces_p;
+}
+
+#if 0 /* unused */
+/* Make ALIAS run the named FUNCTION.  Copies properties from FUNCTION. */
+static void
+define_alias (char *alias, char *function)
+{
+}
+#endif
+
+/* Set the paragraph indentation variable to the value specified in STRING.
+   Values can be:
+   `asis': Don't change existing indentation.
+   `none': Remove existing indentation.
+      NUM: Indent NUM spaces at the starts of paragraphs.
+           Note that if NUM is zero, we assume `none'.
+
+   Returns 0 if successful, or non-zero if STRING isn't one of the above. */
+static int
+set_paragraph_indent (char *string)
+{
+  if (strcmp (string, "asis") == 0)
+    paragraph_start_indent = 0;
+  else if (strcmp (string, "none") == 0)
+    paragraph_start_indent = -1;
+  else
+    {
+      if (sscanf (string, "%d", &paragraph_start_indent) != 1)
+	return (-1);
+      else
+	{
+	  if (paragraph_start_indent == 0)
+	    paragraph_start_indent = -1;
+	}
+    }
+  return (0);
+}
+
+static void
+cm_paragraphindent (void)
+{
+  char *arg;
+
+  get_rest_of_line (&arg);
+  if (set_paragraph_indent (arg) != 0)
+    line_error ("Bad argument to %c%s", COMMAND_PREFIX, command);
+
+  free (arg);
+}
+
+/* Some support for footnotes. */
+
+/* Footnotes are a new construct in Info.  We don't know the best method
+   of implementing them for sure, so we present two possiblities.
+
+   SeparateNode:
+	Make them look like followed references, with the reference
+	destinations in a makeinfo manufactured node or,
+
+   EndNode:
+	Make them appear at the bottom of the node that they originally
+	appeared in. */
+#define SeparateNode 0
+#define EndNode 1
+
+int footnote_style = EndNode;
+int first_footnote_this_node = 1;
+int footnote_count = 0;
+
+/* Set the footnote style based on he style identifier in STRING. */
+static int
+set_footnote_style (char *string)
+{
+  if ((strcasecmp (string, "separate") == 0) ||
+      (strcasecmp (string, "MN") == 0))
+    footnote_style = SeparateNode;
+  else if ((strcasecmp (string, "end") == 0) ||
+	   (strcasecmp (string, "EN") == 0))
+    footnote_style = EndNode;
+  else
+    return (-1);
+
+ return (0);
+}
+
+static void
+cm_footnotestyle (void)
+{
+  char *arg;
+
+  get_rest_of_line (&arg);
+
+  if (set_footnote_style (arg) != 0)
+    line_error ("Bad argument to %c%s", COMMAND_PREFIX, command);
+
+  free (arg);
+}
+
+typedef struct fn
+{
+  struct fn *next;
+  char *marker;
+  char *note;
+}  FN;
+
+FN *pending_notes = (FN *) NULL;
+
+/* A method for remembering footnotes.  Note that this list gets output
+   at the end of the current node. */
+static void
+remember_note (char *marker, char *note)
+{
+  FN *temp = (FN *) xmalloc (sizeof (FN));
+
+  temp->marker = strdup (marker);
+  temp->note = strdup (note);
+  temp->next = pending_notes;
+  pending_notes = temp;
+  footnote_count++;
+}
+
+/* How to get rid of existing footnotes. */
+static void
+free_pending_notes (void)
+{
+  FN *temp;
+
+  while ((temp = pending_notes) != (FN *) NULL)
+    {
+      free (temp->marker);
+      free (temp->note);
+      pending_notes = pending_notes->next;
+      free (temp);
+    }
+  first_footnote_this_node = 1;
+  footnote_count = 0;
+}
+
+/* What to do when you see a @footnote construct. */
+
+ /* Handle a "footnote".
+    footnote *{this is a footnote}
+    where "*" is the marker character for this note. */
+static void
+cm_footnote (void)
+{
+  char *marker;
+  char *note;
+
+  get_until ("{", &marker);
+  canon_white (marker);
+
+  /* Read the argument in braces. */
+  if (curchar () != '{')
+    {
+      line_error ("`%c%s' expected more than just `%s'.  It needs something in `{...}'",
+		  COMMAND_PREFIX, command, marker);
+      free (marker);
+      return;
+    }
+  else
+    {
+      int braces = 1;
+      int temp = ++input_text_offset;
+      int len;
+
+      while (braces)
+	{
+	  if (temp == size_of_input_text)
+	    {
+	      line_error ("No closing brace for footnote `%s'", marker);
+	      return;
+	    }
+
+	  if (input_text[temp] == '{')
+	    braces++;
+	  else if (input_text[temp] == '}')
+	    braces--;
+	  else if (input_text[temp] == '\n')
+	    line_number ++;
+
+	  temp++;
+	}
+
+      len = (temp - input_text_offset) - 1;
+      note = (char *)xmalloc (len + 1);
+      strncpy (note, &input_text[input_text_offset], len);
+      note[len] = '\0';
+      input_text_offset = temp;
+    }
+
+  if (!current_node || !*current_node)
+    {
+      line_error ("Footnote defined without parent node");
+      free (marker);
+      free (note);
+      return;
+    }
+
+  if (!*marker)
+    {
+      free (marker);
+
+      if (number_footnotes)
+	{
+	  marker = (char *)xmalloc (10);
+	  sprintf (marker, "%d", current_footnote_number);
+	  current_footnote_number++;
+	}
+      else
+	marker = strdup ("*");
+    }
+
+  remember_note (marker, note);
+
+  /* Your method should at least insert MARKER. */
+  switch (footnote_style)
+    {
+    case SeparateNode:
+      add_word_args ("(%s)", marker);
+      if (first_footnote_this_node)
+	{
+	  char *temp_string;
+
+	  temp_string = (char *)
+	    xmalloc ((strlen (current_node)) + (strlen ("-Footnotes")) + 1);
+
+	  add_word_args (" (*note %s-Footnotes::)", current_node);
+	  strcpy (temp_string, current_node);
+	  strcat (temp_string, "-Footnotes");
+	  remember_node_reference (temp_string, line_number, followed_reference);
+	  free (temp_string);
+	  first_footnote_this_node = 0;
+	}
+      break;
+
+    case EndNode:
+      add_word_args ("(%s)", marker);
+      break;
+
+    default:
+      break;
+    }
+  free (marker);
+  free (note);
+}
+
+/* Non-zero means that we are currently in the process of outputting
+   footnotes. */
+int already_outputting_pending_notes = 0;
+
+/* Output the footnotes.  We are at the end of the current node. */
+static void
+output_pending_notes (void)
+{
+  FN *footnote = pending_notes;
+
+  if (!pending_notes)
+    return;
+
+  switch (footnote_style)
+    {
+
+    case SeparateNode:
+      {
+	char *old_current_node = current_node;
+	char *old_command = strdup (command);
+
+	already_outputting_pending_notes++;
+	execute_string ("%cnode %s-Footnotes,,,%s\n",
+			COMMAND_PREFIX, current_node, current_node);
+	already_outputting_pending_notes--;
+	current_node = old_current_node;
+	free (command);
+	command = old_command;
+      }
+      break;
+
+    case EndNode:
+      close_paragraph ();
+      in_fixed_width_font++;
+      execute_string ("---------- Footnotes ----------\n\n");
+      in_fixed_width_font--;
+      break;
+    }
+
+  /* Handle the footnotes in reverse order. */
+  {
+    FN **array = (FN **) xmalloc ((footnote_count + 1) * sizeof (FN *));
+
+    array[footnote_count] = (FN *) NULL;
+
+    while (--footnote_count > -1)
+      {
+	array[footnote_count] = footnote;
+	footnote = footnote->next;
+      }
+
+    filling_enabled = 1;
+    indented_fill = 1;
+
+    while ((footnote = array[++footnote_count]))
+      {
+
+	switch (footnote_style)
+	  {
+	  case SeparateNode:
+	  case EndNode:
+	    execute_string ("(%s)  %s", footnote->marker, footnote->note);
+	    close_paragraph ();
+	    break;
+	  }
+      }
+    close_paragraph ();
+    free (array);
+  }
+}
+
+
+/* **************************************************************** */
+/*                                                                  */
+/*              User definable Macros (text substitution)	    */
+/*                                                                  */
+/* **************************************************************** */
+
+#if defined (HAVE_MACROS)
+
+/* Array of macros and definitions. */
+MACRO_DEF **macro_list = (MACRO_DEF **)NULL;
+
+int macro_list_len = 0;		/* Number of elements. */
+int macro_list_size = 0;	/* Number of slots in total. */
+
+/* Return the macro definition of NAME or NULL if NAME is not defined. */
+static MACRO_DEF *
+find_macro (char *name)
+{
+  register int i;
+  register MACRO_DEF *def;
+
+  def = (MACRO_DEF *)NULL;
+  for (i = 0; macro_list && (def = macro_list[i]); i++)
+    {
+      if ((!def->inhibited) && (strcmp (def->name, name) == 0))
+	break;
+    }
+  return (def);
+}
+
+/* Add the macro NAME with ARGLIST and BODY to the list of defined macros.
+   SOURCE_FILE is the name of the file where this definition can be found,
+   and SOURCE_LINENO is the line number within that file.  If a macro already
+   exists with NAME, then a warning is produced, and that previous
+   definition is overwritten. */
+void
+add_macro (char *name, char **arglist, char *body, char *source_file,
+           int source_lineno, int flags)
+{
+  register MACRO_DEF *def;
+
+  def = find_macro (name);
+
+  if (!def)
+    {
+      if (macro_list_len + 2 >= macro_list_size)
+	macro_list = (MACRO_DEF **)xrealloc
+	  (macro_list, ((macro_list_size += 10) * sizeof (MACRO_DEF *)));
+
+      macro_list[macro_list_len] = (MACRO_DEF *)xmalloc (sizeof (MACRO_DEF));
+      macro_list[macro_list_len + 1] = (MACRO_DEF *)NULL;
+
+      def = macro_list[macro_list_len];
+      macro_list_len += 1;
+      def->name = name;
+    }
+  else
+    {
+      char *temp_filename = input_filename;
+      int temp_line = line_number;
+
+      warning ("The macro `%s' is previously defined", name);
+
+      input_filename = def->source_file;
+      line_number = def->source_lineno;
+
+      warning ("Here is the previous definition of `%s'", name);
+
+      input_filename = temp_filename;
+      line_number = temp_line;
+
+      if (def->arglist)
+	{
+	  register int i;
+
+	  for (i = 0; def->arglist[i]; i++)
+	    free (def->arglist[i]);
+
+	  free (def->arglist);
+	}
+      free (def->source_file);
+      free (def->body);
+    }
+
+  def->source_file = strdup (source_file);
+  def->source_lineno = source_lineno;
+  def->body = body;
+  def->arglist = arglist;
+  def->inhibited = 0;
+  def->flags = flags;
+}
+
+/* Delete the macro with name NAME.  The macro is deleted from the list,
+   but it is also returned.  If there was no macro defined, NULL is
+   returned. */
+static MACRO_DEF *
+delete_macro (char *name)
+{
+  register int i;
+  register MACRO_DEF *def;
+
+  def = (MACRO_DEF *)NULL;
+
+  for (i = 0; macro_list && (def = macro_list[i]); i++)
+    if (strcmp (def->name, name) == 0)
+      {
+	memcpy (macro_list + i, macro_list + i + 1,
+	       ((macro_list_len + 1) - i) * sizeof (MACRO_DEF *));
+	break;
+      }
+  return (def);
+}
+
+/* Return the arglist on the current line.  This can behave in two different
+   ways, depending on the variable BRACES_REQUIRED_FOR_MACRO_ARGS. */
+int braces_required_for_macro_args = 0;
+
+static char **
+get_macro_args (MACRO_DEF *def)
+{
+  register int i;
+  char *word;
+
+  /* Quickly check to see if this macro has been invoked with any arguments.
+     If not, then don't skip any of the following whitespace. */
+  for (i = input_text_offset; i < size_of_input_text; i++)
+    if (!cr_or_whitespace (input_text[i]))
+      break;
+
+  if (input_text[i] != '{')
+    {
+      if (braces_required_for_macro_args)
+	{
+	  return ((char **)NULL);
+	}
+      else
+	{
+	  /* Braces are not required to fill out the macro arguments.  If
+	     this macro takes one argument, it is considered to be the
+	     remainder of the line, sans whitespace. */
+	  if (def->arglist && def->arglist[0] && !def->arglist[1])
+	    {
+	      char **arglist;
+
+	      get_rest_of_line (&word);
+	      input_text_offset--;
+	      canon_white (word);
+	      arglist = (char **)xmalloc (2 * sizeof (char *));
+	      arglist[0] = word;
+	      arglist[1] = (char *)NULL;
+	      return (arglist);
+	    }
+	  else
+	    {
+	      /* The macro either took no arguments, or took more than
+		 one argument.  In that case, it must be invoked with
+		 arguments surrounded by braces. */
+	      return ((char **)NULL);
+	    }
+	}
+    }
+  return (get_brace_args (def->flags & ME_QUOTE_ARG));
+}
+
+/* Substitute actual parameters for named parameters in body.
+   The named parameters which appear in BODY must by surrounded
+   reverse slashes, as in \foo\. */
+static char *
+apply (char **named, char **actuals, char *body)
+{
+  register int i;
+  int new_body_index, new_body_size;
+  char *new_body, *text;
+  int length_of_actuals;
+
+  length_of_actuals = array_len (actuals);
+  new_body_size = strlen (body);
+  new_body = (char *)xmalloc (1 + new_body_size);
+
+  /* Copy chars from BODY into NEW_BODY. */
+  i = 0; new_body_index = 0;
+
+  while (1)
+    {
+      if (!body[i])
+	break;
+
+      if (body[i] != '\\')
+	new_body[new_body_index++] = body[i++];
+      else
+	{
+	  /* Snarf parameter name, check against named parameters. */
+	  char *param;
+	  int param_start, which, len;
+
+	  param_start = ++i;
+	  while ((body[i]) && (body[i] != '\\'))
+	    i++;
+
+	  len = i - param_start;
+	  param = (char *)xmalloc (1 + len);
+	  memcpy (param, body + param_start, len);
+	  param[len] = '\0';
+
+	  if (body[i])
+	    i++;
+
+	  /* Now check against named parameters. */
+	  for (which = 0; named && named[which]; which++)
+	    if (strcmp (named[which], param) == 0)
+	      break;
+
+	  if (named[which])
+	    {
+	      if (which < length_of_actuals)
+		text = actuals[which];
+	      else
+		text = (char *)NULL;
+
+	      if (!text)
+		text = "";
+
+	      len = strlen (text);
+	    }
+	  else
+	    {
+	      len += 2;
+	      text = (char *)xmalloc (1 + len);
+	      sprintf (text, "\\%s\\", param);
+	    }
+
+	  if ((int) (2 + strlen (param)) < len)
+	    new_body = (char *)xrealloc
+	      (new_body, new_body_size += (1 + len));
+
+	  free (param);
+
+	  strcpy (new_body + new_body_index, text);
+	  new_body_index += len;
+
+	  if (!named[which])
+	    free (text);
+	}
+    }
+  new_body[new_body_index] = '\0';
+  return (new_body);
+}
+
+/* Execute the macro passed in DEF, a pointer to a MACRO_DEF.  */
+static void
+execute_macro (MACRO_DEF *def)
+{
+  char **arglist;
+  int num_args;
+  char *execution_string = (char *)NULL;
+
+  if (macro_expansion_output_stream && !me_inhibit_expansion)
+    me_append_before_this_command ();
+
+  /* Find out how many arguments this macro definition takes. */
+  num_args = array_len (def->arglist);
+
+  /* Gather the arguments present on the line if there are any. */
+  arglist = get_macro_args (def);
+
+  if (num_args < array_len (arglist))
+    {
+      free_array (arglist);
+      line_error ("Macro `%s' called with too many args", def->name);
+      return;
+    }
+
+  if (def->body)
+    execution_string = apply (def->arglist, arglist, def->body);
+
+  free_array (arglist);
+
+  if (def->body)
+    {
+      if (macro_expansion_output_stream && !me_inhibit_expansion)
+	{
+	  remember_itext (input_text, input_text_offset);
+	  me_execute_string (execution_string);
+	}
+      else
+	execute_string (execution_string);
+
+      free (execution_string);
+    }
+}
+
+/* Read and remember the definition of a macro. */
+static void
+cm_macro (void)
+{
+  register int i;
+  char *name, **arglist, *body, *line;
+  int body_size, body_index;
+  int depth = 1;
+  int defining_line = line_number;
+  int flags = 0;
+
+  arglist = (char **)NULL;
+  body = (char *)NULL;
+  body_size = 0;
+  body_index = 0;
+
+  if (macro_expansion_output_stream)
+    me_append_before_this_command ();
+
+  skip_whitespace ();
+
+  /* Get the name of the macro.  This is the set of characters which are
+     not whitespace and are not `{' immediately following the @macro. */
+  {
+    int start = input_text_offset;
+    int len;
+
+    for (i = start;
+	 (i < size_of_input_text) &&
+	 (input_text[i] != '{') &&
+	 (!cr_or_whitespace (input_text[i]));
+	 i++);
+
+    len = i - start;
+    name = (char *)xmalloc (1 + len);
+    strncpy (name, input_text + start, len);
+    name[len] = '\0';
+    input_text_offset = i;
+  }
+
+  skip_whitespace ();
+
+  /* It is not required that the definition of a macro includes an arglist.
+     If not, don't try to get the named parameters, just use a null list. */
+  if (curchar () == '{')
+    {
+      int arglist_index = 0, arglist_size = 0;
+      int gathering_words = 1;
+      char *word = (char *)NULL;
+      int character;
+
+      /* Read the words inside of the braces which determine the arglist.
+	 These words will be replaced within the body of the macro at
+	 execution time. */
+
+      input_text_offset++;
+      skip_whitespace_and_newlines ();
+
+      while (gathering_words)
+	{
+	  int len;
+
+	  for (i = input_text_offset;
+	       (character = input_text[i]);
+	       i++)
+	    {
+	      switch (character)
+		{
+		case '\n':
+		  line_number++;
+		case ' ':
+		case '\t':
+		case ',':
+		case '}':
+		  /* Found the end of the current arglist word.  Save it. */
+		  len = i - input_text_offset;
+		  word = (char *)xmalloc (1 + len);
+		  strncpy (word, input_text + input_text_offset, len);
+		  word[len] = '\0';
+		  input_text_offset = i;
+
+		  /* Advance to the comma or close-brace that signified
+		     the end of the argument. */
+		  while ((character = curchar ())
+			 && character != ','
+			 && character != '}')
+		    input_text_offset++;
+
+		  /* Add the word to our list of words. */
+		  if ((arglist_index + 2) >= arglist_size)
+		    arglist = (char **)xrealloc
+		      (arglist, (arglist_size += 10) * sizeof (char *));
+
+		  arglist[arglist_index++] = word;
+		  arglist[arglist_index] = (char *)NULL;
+		  break;
+		}
+
+	      if (character == '}')
+		{
+		  input_text_offset++;
+		  gathering_words = 0;
+		  break;
+		}
+
+	      if (character == ',')
+		{
+		  input_text_offset++;
+		  skip_whitespace_and_newlines ();
+		  i = input_text_offset - 1;
+		}
+	    }
+	}
+    }
+
+  /* Read the text carefully until we find an "@end macro" which
+     matches this one.  The text in between is the body of the macro. */
+  skip_whitespace_and_newlines ();
+
+  while (depth)
+    {
+      if ((input_text_offset + 9) > size_of_input_text)
+	{
+	  int temp_line = line_number;
+	  line_number = defining_line;
+	  line_error ("%cend macro not found", COMMAND_PREFIX);
+	  line_number = temp_line;
+	  return;
+	}
+
+      get_rest_of_line (&line);
+
+      /* Handle commands only meaningful within a macro. */
+      if ((*line == COMMAND_PREFIX) && (depth == 1) &&
+	  (strncmp (line + 1, "allow-recursion", 15) == 0) &&
+	  (line[16] == '\0' || whitespace (line[16])))
+	{
+	  for (i = 16; whitespace (line[i]); i++);
+	  strcpy (line, line + i);
+	  flags |= ME_RECURSE;
+	  if (!*line)
+	    {
+	      free (line);
+	      continue;
+	    }
+	}
+
+      if ((*line == COMMAND_PREFIX) && (depth == 1) &&
+	  (strncmp (line + 1, "quote-arg", 9) == 0) &&
+	  (line[10] == '\0' || whitespace (line[10])))
+	{
+	  for (i = 16; whitespace (line[i]); i++);
+	  strcpy (line, line + i);
+
+	  if (arglist && arglist[0] && !arglist[1])
+	    {
+	      flags |= ME_QUOTE_ARG;
+	      if (!*line)
+		{
+		  free (line);
+		  continue;
+		}
+	    }
+	  else
+	    {
+	      line_error ("%cquote-arg only useful when the macro takes a single argument",
+			  COMMAND_PREFIX);
+	    }
+	}
+
+      if ((*line == COMMAND_PREFIX) &&
+	  (strncmp (line + 1, "macro ", 6) == 0))
+	depth++;
+
+      if ((*line == COMMAND_PREFIX) &&
+	  (strncmp (line + 1, "end macro", 9) == 0))
+	depth--;
+
+      if (depth)
+	{
+	  if ((int) (body_index + strlen (line) + 3) >= body_size)
+	    body = (char *)xrealloc
+	      (body, body_size += 3 + strlen (line));
+	  strcpy (body + body_index, line);
+	  body_index += strlen (line);
+	  body[body_index++] = '\n';
+	  body[body_index] = '\0';
+	}
+      free (line);
+    }
+
+  /* We now have the name, the arglist, and the body.  However, BODY
+     includes the final newline which preceded the `@end macro' text.
+     Delete it. */
+  if (body && strlen (body))
+    body[strlen (body) - 1] = '\0';
+
+  add_macro (name, arglist, body, input_filename, defining_line, flags);
+
+  if (macro_expansion_output_stream)
+    remember_itext (input_text, input_text_offset);
+}
+
+static void
+cm_unmacro (void)
+{
+  register int i;
+  char *line, *name;
+  MACRO_DEF *def;
+
+  if (macro_expansion_output_stream)
+    me_append_before_this_command ();
+
+  get_rest_of_line (&line);
+  canon_white (line);
+
+  for (i = 0; line[i] && !whitespace (line[i]); i++);
+  name = (char *)xmalloc (i);
+  strncpy (name, line, i);
+  name[i] = '\0';
+
+  def = delete_macro (name);
+
+  if (def)
+    {
+      free (def->source_file);
+      free (def->name);
+      free (def->body);
+
+      if (def->arglist)
+	{
+	  register int ii;
+
+	  for (ii = 0; def->arglist[ii]; i++)
+	    free (def->arglist[ii]);
+
+	  free (def->arglist);
+	}
+
+      free (def);
+    }
+
+  free (line);
+  free (name);
+
+  if (macro_expansion_output_stream)
+    remember_itext (input_text, input_text_offset);
+}
+
+/* How to output sections of the input file verbatim. */
+
+/* Set the value of POINTER's offset to OFFSET. */
+static ITEXT *
+remember_itext (char *pointer, int offset)
+{
+  register int i;
+  ITEXT *itext = (ITEXT *)NULL;
+
+  /* If we have no info, initialize a blank list. */
+  if (!itext_info)
+    {
+      itext_info = (ITEXT **)xmalloc ((itext_size = 10) * sizeof (ITEXT *));
+      for (i = 0; i < itext_size; i++)
+	itext_info[i] = (ITEXT *)NULL;
+    }
+
+  /* If the pointer is already present in the list, then set the offset. */
+  for (i = 0; i < itext_size; i++)
+    if ((itext_info[i] != (ITEXT *)NULL) &&
+	(itext_info[i]->pointer == pointer))
+      {
+	itext = itext_info[i];
+	itext_info[i]->offset = offset;
+	break;
+      }
+
+  if (i == itext_size)
+    {
+      /* Find a blank slot, (or create a new one), and remember the
+	 pointer and offset. */
+      for (i = 0; i < itext_size; i++)
+	if (itext_info[i] == (ITEXT *)NULL)
+	  break;
+
+      /* If not found, then add some slots. */
+      if (i == itext_size)
+	{
+	  register int j;
+
+	  itext_info = (ITEXT **)xrealloc
+	    (itext_info, (itext_size += 10) * sizeof (ITEXT *));
+
+	  for (j = i; j < itext_size; j++)
+	    itext_info[j] = (ITEXT *)NULL;
+	}
+
+      /* Now add the pointer and the offset. */
+      itext_info[i] = (ITEXT *)xmalloc (sizeof (ITEXT));
+      itext_info[i]->pointer = pointer;
+      itext_info[i]->offset = offset;
+      itext = itext_info[i];
+    }
+  return (itext);
+}
+
+/* Forget the input text associated with POINTER. */
+static void
+forget_itext (char *pointer)
+{
+  register int i;
+
+  for (i = 0; i < itext_size; i++)
+    if (itext_info[i] && (itext_info[i]->pointer == pointer))
+      {
+	free (itext_info[i]);
+	itext_info[i] = (ITEXT *)NULL;
+	break;
+      }
+}
+
+/* Append the text which appeared in input_text from the last offset to
+   the character just before the command that we are currently executing. */
+static void
+me_append_before_this_command (void)
+{
+  register int i;
+
+  for (i = input_text_offset; i && (input_text[i] != COMMAND_PREFIX); i--);
+  maybe_write_itext (input_text, i);
+}
+
+/* Similar to execute_string (), but only takes a single string argument,
+   and remembers the input text location, etc. */
+static void
+me_execute_string (char *execution_string)
+{
+  pushfile ();
+  input_text_offset = 0;
+  input_text = execution_string;
+  input_filename = strdup (input_filename);
+  size_of_input_text = strlen (execution_string);
+
+  remember_itext (execution_string, 0);
+
+  executing_string++;
+  reader_loop ();
+  popfile ();
+  executing_string--;
+}
+
+/* Append the text which appears in input_text from the last offset to
+   the current OFFSET. */
+static void
+append_to_expansion_output (int offset)
+{
+  register int i;
+  ITEXT *itext = (ITEXT *)NULL;
+
+  for (i = 0; i < itext_size; i++)
+    if (itext_info[i] && itext_info[i]->pointer == input_text)
+      {
+	itext = itext_info[i];
+	break;
+      }
+
+  if (!itext)
+    itext = remember_itext (input_text, 0);
+
+  if (offset > itext_info[i]->offset)
+    {
+      write_region_to_macro_output
+	(input_text, itext_info[i]->offset, offset);
+      remember_itext (input_text, offset);
+    }
+}
+
+/* Only write this input text iff it appears in our itext list. */
+static void
+maybe_write_itext (char *pointer, int offset)
+{
+  register int i;
+  ITEXT *itext = (ITEXT *)NULL;
+
+  for (i = 0; i < itext_size; i++)
+    if (itext_info[i] && (itext_info[i]->pointer == pointer))
+      {
+	itext = itext_info[i];
+	break;
+      }
+
+  if (itext && (itext->offset < offset))
+    {
+      write_region_to_macro_output (itext->pointer, itext->offset, offset);
+      remember_itext (pointer, offset);
+    }
+}
+
+static void
+write_region_to_macro_output (char *string, int start, int end)
+{
+  if (macro_expansion_output_stream)
+    fwrite (string + start, 1, end - start, macro_expansion_output_stream);
+}
+
+#endif /* HAVE_MACROS */
+
+/* Return the length of the array in ARRAY. */
+static int
+array_len (char **array)
+{
+  register int i = 0;
+
+  if (array)
+    for (i = 0; array[i] != (char *)NULL; i++);
+
+  return (i);
+}
+
+static void
+free_array (char **array)
+{
+  if (array)
+    {
+      register int i;
+
+      for (i = 0; array[i] != (char *)NULL; i++)
+	free (array[i]);
+
+      free (array);
+    }
+}
+
+/* Function is used even when we don't have macros.  Although, I have
+   to admit, it is unlikely that you would have a use for it if you
+   aren't using macros. */
+static char **
+get_brace_args (int quote_single)
+{
+  char **arglist, *word;
+  int arglist_index, arglist_size;
+  int character, escape_seen, start;
+  int depth = 1;
+
+  /* There is an arglist in braces here, so gather the args inside of it. */
+  skip_whitespace_and_newlines ();
+  input_text_offset++;
+  arglist = (char **)NULL;
+  arglist_index = arglist_size = 0;
+
+ get_arg:
+  skip_whitespace_and_newlines ();
+  start = input_text_offset;
+  escape_seen = 0;
+
+  while ((character = curchar ()))
+    {
+      if (character == '\\')
+	{
+	  input_text_offset += 2;
+	  escape_seen = 1;
+	}
+      else if (character == '{')
+	{
+	  depth++;
+	  input_text_offset++;
+	}
+      else if ((character == ',' && !quote_single) ||
+	       ((character == '}') && depth == 1))
+	{
+	  int len = input_text_offset - start;
+
+	  if (len || (character != '}'))
+	    {
+	      word = (char *)xmalloc (1 + len);
+	      strncpy (word, input_text + start, len);
+	      word[len] = '\0';
+
+	      /* Clean up escaped characters. */
+	      if (escape_seen)
+		{
+		  register int i;
+
+		  for (i = 0; word[i]; i++)
+		    if (word[i] == '\\')
+		      memcpy (word + i, word + i + 1, strlen (word + i + 1));
+		}
+
+	      if (arglist_index + 2 >= arglist_size)
+		arglist = (char **)xrealloc
+		  (arglist, (arglist_size += 10) * sizeof (char *));
+
+	      arglist[arglist_index++] = word;
+	      arglist[arglist_index] = (char *)NULL;
+	    }
+
+	  input_text_offset++;
+	  if (character == '}')
+	    break;
+	  else
+	    goto get_arg;
+	}
+      else if (character == '}')
+	{
+	  depth--;
+	  input_text_offset++;
+	}
+      else
+	input_text_offset++;
+    }
+  return (arglist);
+}
+
+/* **************************************************************** */
+/*                                                                  */
+/*                  Looking For Include Files                       */
+/*                                                                  */
+/* **************************************************************** */
+
+/* Given a string containing units of information separated by colons,
+   return the next one pointed to by INDEX, or NULL if there are no more.
+   Advance INDEX to the character after the colon. */
+static char *
+extract_colon_unit (char *string, int *indecks)
+{
+  int i, start;
+
+  i = *indecks;
+
+  if (!string || (i >= (int) strlen (string)))
+    return ((char *)NULL);
+
+  /* Each call to this routine leaves the index pointing at a colon if
+     there is more to the path.  If I is > 0, then increment past the
+     `:'.  If I is 0, then the path has a leading colon.  Trailing colons
+     are handled OK by the `else' part of the if statement; an empty
+     string is returned in that case. */
+  if (i && string[i] == ':')
+    i++;
+
+  start = i;
+
+  while (string[i] && string[i] != ':') i++;
+
+  *indecks = i;
+
+  if (i == start)
+    {
+      if (string[i])
+	(*indecks)++;
+
+      /* Return "" in the case of a trailing `:'. */
+      return (strdup (""));
+    }
+  else
+    {
+      char *value;
+
+      value = (char *)xmalloc (1 + (i - start));
+      strncpy (value, &string[start], (i - start));
+      value [i - start] = '\0';
+
+      return (value);
+    }
+}
+
+/* Return the full pathname for FILENAME by searching along PATH.
+   When found, return the stat () info for FILENAME in FINFO.
+   If PATH is NULL, only the current directory is searched.
+   If the file could not be found, return a NULL pointer. */
+static char *
+get_file_info_in_path (char *filename, char *path, struct stat *finfo)
+{
+  char *dir;
+  int result, indecks = 0;
+
+  if (path == (char *)NULL)
+    path = ".";
+
+  /* Handle absolute pathnames. "./foo", "/foo", "../foo". */
+  if (*filename == '/' ||
+      (*filename == '.' &&
+       (filename[1] == '/' ||
+	(filename[1] == '.' && filename[2] == '/'))))
+    {
+      if (stat (filename, finfo) == 0)
+	return (strdup (filename));
+      else
+	return ((char *)NULL);
+    }
+
+  while ((dir = extract_colon_unit (path, &indecks)))
+    {
+      char *fullpath;
+
+      if (!*dir)
+	{
+	  free (dir);
+	  dir = strdup (".");
+	}
+
+      fullpath = (char *)xmalloc (2 + strlen (dir) + strlen (filename));
+      sprintf (fullpath, "%s/%s", dir, filename);
+      free (dir);
+
+      result = stat (fullpath, finfo);
+
+      if (result == 0)
+	return (fullpath);
+      else
+	free (fullpath);
+    }
+  return ((char *)NULL);
+}