summaryrefslogtreecommitdiff
path: root/gnu/usr.bin/texinfo/makeinfo
diff options
context:
space:
mode:
authorNiklas Hallqvist <niklas@cvs.openbsd.org>1995-12-22 16:47:20 +0000
committerNiklas Hallqvist <niklas@cvs.openbsd.org>1995-12-22 16:47:20 +0000
commitec18f98a8dd00dbc9bdc71858b395712564182bf (patch)
treef31db27a0d475ab3bb7d62153ec5f12c4d691b2e /gnu/usr.bin/texinfo/makeinfo
parent0ea8ce66e7dba7c3b169b0e15553a973df9e3570 (diff)
Import of texinfo-3.6 from FSF
Diffstat (limited to 'gnu/usr.bin/texinfo/makeinfo')
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/Makefile.in111
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/macro.texi177
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/macros/example.texi224
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/macros/html.texi269
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/macros/multifmt.texi41
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/macros/res-samp.texi32
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/macros/resume.texi64
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/macros/simpledoc.texi135
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/makeinfo.c9258
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/makeinfo.texi295
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/multiformat.texi40
11 files changed, 10646 insertions, 0 deletions
diff --git a/gnu/usr.bin/texinfo/makeinfo/Makefile.in b/gnu/usr.bin/texinfo/makeinfo/Makefile.in
new file mode 100644
index 00000000000..dd691215b41
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/Makefile.in
@@ -0,0 +1,111 @@
+# Makefile for GNU makeinfo. -*- Indented-Text -*-
+# Copyright (C) 1993 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#### Start of system configuration section. ####
+
+srcdir = @srcdir@
+VPATH = $(srcdir):$(common)
+
+common = $(srcdir)/../libtxi
+
+CC = @CC@
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+
+LN = ln
+RM = rm -f
+TAR = tar
+MKDIR = mkdir
+COMPRESS= compress
+
+DEFS = @DEFS@
+LIBS = -L../libtxi -ltxi @LIBS@
+LOADLIBES = $(LIBS)
+
+SHELL = /bin/sh
+
+CFLAGS = @CFLAGS@
+LDFLAGS = @LDFLAGS@
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+bindir = $(exec_prefix)/bin
+# Prefix for each installed program, normally empty or `g'.
+binprefix =
+libdir = $(prefix)/lib
+# Prefix for each installed man page, normally empty or `g'.
+manprefix =
+mandir = $(prefix)/man/man1
+manext = 1
+infodir = $(prefix)/info
+
+#### End of system configuration section. ####
+
+SRCS = makeinfo.c
+OBJS = makeinfo.o
+
+PROGS = makeinfo
+
+all: $(PROGS) makeinfo.info
+sub-all: all
+
+.c.o:
+ $(CC) -c $(CPPFLAGS) -I. -I$(srcdir) -I$(common) $(DEFS) $(CFLAGS) $<
+
+makeinfo: $(OBJS) ../libtxi/libtxi.a
+ $(CC) $(LDFLAGS) -o makeinfo $(OBJS) $(LOADLIBES)
+
+../libtxi/libtxi.a:
+ (cd ../libtxi; $(MAKE) $(MFLAGS) libtxi.a)
+
+makeinfo.o: makeinfo.c $(common)/getopt.h
+
+makeinfo.info: ./makeinfo makeinfo.texi macro.texi
+ ./makeinfo --no-split -I$(srcdir) makeinfo.texi
+
+# makeinfo.texi: ./makeinfo makeinfo.mki
+# ./makeinfo -E makeinfo.texi -I$(srcdir) makeinfo.mki
+
+install: all
+ for f in $(PROGS); do $(INSTALL_PROGRAM) $$f $(bindir)/$(binprefix)$$f; done
+ -d=$(srcdir); test -f ./makeinfo.info && d=.; $(INSTALL_DATA) $$d/makeinfo.info $(infodir)/makeinfo.info
+
+uninstall:
+ for f in $(PROGS); do rm -f $(bindir)/$(binprefix)$$f; done
+ rm -f $(infodir)/makeinfo.info
+
+TAGS: $(SRCS)
+ etags $(SRCS)
+
+clean:
+ rm -f *.o a.out core core.* $(PROGS)
+
+mostlyclean: clean
+
+distclean: clean
+ rm -f TAGS Makefile config.status *.info */*.info
+
+realclean: distclean
+
+Makefile: Makefile.in ../config.status
+ cd ..; sh config.status
+
+# Prevent GNU make v3 from overflowing arg limit on SysV.
+.NOEXPORT:
+
+# eof
diff --git a/gnu/usr.bin/texinfo/makeinfo/macro.texi b/gnu/usr.bin/texinfo/makeinfo/macro.texi
new file mode 100644
index 00000000000..8a3fe802392
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/macro.texi
@@ -0,0 +1,177 @@
+@c This file is included in makeinfo.texi.
+@c
+@ifinfo
+@comment Here are some useful examples of the macro facility.
+
+@c Simply insert the right version of the texinfo name.
+@macro texinfo{}
+TeXinfo
+@end macro
+
+@macro dfn{text}
+@dfn{\text\}
+@cpindex \text\
+@end macro
+
+@c Define a macro which expands to a pretty version of the name of the
+@c Makeinfo program.
+@macro makeinfo{}
+@code{Makeinfo}
+@end macro
+
+@c Define a macro which is used to define other macros. This one makes
+@c a macro which creates a node and gives it a sectioning command. Note
+@c that the created macro uses the original definition within the
+@c expansion text. This takes advantage of the non-recursion feature of
+@c macro execution.
+@macro node_define{orig-name}
+@macro \orig-name\{title}
+@node \title\
+@\orig-name\ \title\
+@end macro
+@end macro
+
+@c Now actually define a new set of sectioning commands.
+@node_define {chapter}
+@node_define {section}
+@node_define {subsection}
+@end ifinfo
+
+@chapter The Macro Facility
+
+This chapter describes the new macro facility.
+
+A @dfn{macro} is a command that you define in terms of other commands.
+It doesn't exist as a @texinfo{} command until you define it as part of
+the input file to @makeinfo{}. Once the command exists, it behaves much
+as any other @texinfo{} command. Macros are a useful way to ease the
+details and tedium of writing a `correct' info file. The following
+sections explain how to write and invoke macros.
+
+@menu
+* How to Use Macros in @texinfo{}::
+ How to use the macro facility.
+
+* Using Macros Recursively::
+ How to write a macro which does (or doesn't) recurse.
+
+* Using @texinfo{} Macros As Arguments::
+ Passing a macro as an argument.
+@end menu
+
+@section How to Use Macros in @texinfo{}
+
+Using macros in @texinfo{} is easy. First you define the macro. After
+that, the macro command is available as a normal @texinfo{} command.
+Here is what a definition looks like:
+
+@example
+@@macro @var{name}@{@var{arg1}, @var{@dots{}} @var{argn}@}
+@var{@texinfo{} commands@dots{}}
+@@end macro
+@end example
+
+The arguments that you specify that the macro takes are expanded with
+the actual parameters used when calling the macro if they are seen
+surrounded by backslashes. For example, here is a definition of
+@code{@@codeitem}, a macro which can be used wherever @code{@@item} can
+be used, but which surrounds its argument with @code{@@code@{@dots{}@}}.
+
+@example
+@@macro codeitem@{item@}
+@@item @@code@{\item\@}
+@@end macro
+@end example
+
+When the macro is expanded, all of the text between the @code{@@macro}
+and @code{@@end macro} is inserted into the document at the expansion
+point, with the actual parameters substituted for the named parameters.
+So, a call to the above macro might look like:
+
+@example
+@@codeitem@{Foo@}
+@end example
+
+and @makeinfo{} would execute the following code:
+
+@example
+@@item @@code@{Foo@}
+@end example
+
+A special case is made for macros which only take a single argument, and
+which are invoked without any brace characters (i.e.,
+@samp{@{}@dots{}@samp{@}}) surrounding an argument; the rest of the line
+is supplied as is as the sole argument to the macro. This special case
+allows one to redefine some standard @texinfo{} commands without
+modifying the input file. Along with the non-recursive action of macro
+invocation, one can easily redefine the sectioning commands to also
+provide index entries:
+
+@example
+@@macro chapter@{name@}
+@@chapter \name\
+@@findex \name\
+@@end macro
+@end example
+
+Thus, the text:
+
+@example
+@@chapter strlen
+@end example
+
+will expand to:
+
+@example
+@@chapter strlen
+@@findex strlen
+@end example
+
+@section Using Macros Recursively
+
+Normally, while a particular macro is executing, any call to that macro
+will be seen as a call to a builtin @texinfo{} command. This allows one
+to redefine a builtin @texinfo{} command as a macro, and then use that
+command within the definition of the macro itself. For example, one
+might wish to make sure that whereever a term was defined with
+@code{@@dfn@{@dots{}@}}, the location of the definition would appear
+in the concept index for the manual. Here is a macro which redefines
+@code{@@dfn} to do just that:
+
+@example
+@@macro dfn@{text@}
+@@dfn@{\text\@}
+@@cpindex \text\
+@@end macro
+@end example
+
+Note that we used the builtin @texinfo{} command @code{@@dfn} within our
+overriding macro definition.
+
+This behaviour itself can be overridden for macro execution by writing a
+special @dfn{macro control command} in the definition of the macro. The
+command is considered special because it doesn't affect the output text
+directly, rather, it affects the way in which the macro is defined. One
+such special command is @code{@@allow-recursion}.
+
+@example
+@@macro silly@{arg@}
+@@allow-recursion
+\arg\
+@@end macro
+@end example
+
+Now @code{@@silly} is a macro that can be used within a call to itself:
+
+@example
+This text @@silly@{@@silly@{some text@}@} is ``some text''.
+@end example
+
+@section Using @texinfo{} Macros As Arguments
+
+@printindex cp
+How to use @texinfo{} macros as arguments to other @texinfo{} macros.
+
+@bye
+
+
diff --git a/gnu/usr.bin/texinfo/makeinfo/macros/example.texi b/gnu/usr.bin/texinfo/makeinfo/macros/example.texi
new file mode 100644
index 00000000000..d3554ff3ddc
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/macros/example.texi
@@ -0,0 +1,224 @@
+\input texinfo @c -*-texinfo-*-
+@comment %**start of header
+@setfilename example.info
+@set VERSION 1.58
+@paragraphindent none
+@comment %**end of header
+
+@include simpledoc.texi
+
+@document {@makeinfo{}, Brian J. Fox,
+This file is an extract from the @cite{@texinfo{}} manual.@*
+It documents @makeinfo{}\, a program that converts @texinfo{} files into
+Info files.
+}
+
+@menu
+* What is @makeinfo{}?::
+* Controlling Paragraph Formats::
+* Command Line Options::
+* Pointer Validation::
+@end menu
+
+@section What is @makeinfo{}?
+
+@iftex
+This file documents the use of the @code{makeinfo} program, versions
+@value{VERSION} and later. It is an extract from the @cite{TeXinfo} manual.
+@end iftex
+
+@makeinfo{} is a program for converting @dfn{@texinfo{}} files into
+@dfn{@Info{}} files. @texinfo{} is a documentation system that uses a
+single source file to produce both on-line information and printed output.
+
+You can read the on-line information using @Info{}; type @code{info} to
+learn about @Info{}.
+@ifinfo
+@xref{Top, Texinfo, Overview of Texinfo, texinfo, Texinfo},
+@end ifinfo
+@iftex
+See the @cite{TeXinfo} manual,
+@end iftex
+to learn about the TeXinfo documentation system.
+
+@section Controlling Paragraph Formats
+
+In general, @makeinfo{} @dfn{fills} the paragraphs that it outputs
+to an @Info{} file. Filling is the process of breaking and connecting
+lines so that lines are the same length as or shorter than the number
+specified as the fill column. Lines are broken between words. With
+@makeinfo{}, you can control:
+
+@itemize @bullet
+@item
+The width of each paragraph (the @dfn{fill-column}).
+@item
+The amount of indentation that the first line of
+each paragraph receives (the @dfn{paragraph-indentation}).
+@end itemize
+
+@section Command Line Options
+
+The following command line options are available for @makeinfo{}.
+
+@need 100
+@table @code
+@item -D @var{var}
+Cause @var{var} to be defined. This is equivalent to
+@code{@@set @var{var}} in the Texinfo file.
+
+@need 150
+@item --error-limit @var{limit}
+Set the maximum number of errors that @makeinfo{} will report
+before exiting (on the assumption that continuing would be useless).
+The default number of errors that can be reported before
+@makeinfo{} gives up is 100.@refill
+
+@need 150
+@item --fill-column @var{width}
+Specify the maximum number of columns in a line; this is the right-hand
+edge of a line. Paragraphs that are filled will be filled to this
+width. The default value for @code{fill-column} is 72.
+
+@item --footnote-style @var{style}
+Set the footnote style to @var{style}, either @samp{end} for the end
+node style or @samp{separate} for the separate node style. The value
+set by this option overrides the value set in a Texinfo file by an
+@code{@@footnotestyle} command. When the footnote style is
+@samp{separate}, @makeinfo{} makes a new node containing the
+footnotes found in the current node. When the footnote style is
+@samp{end}, @makeinfo{} places the footnote references at the end
+of the current node.
+
+@need 150
+@item -I @var{dir}
+Add @code{dir} to the directory search list for finding files that are
+included using the @code{@@include} command. By default,
+@makeinfo{} searches only the current directory.
+
+@need 150
+@item --no-headers
+Do not include menus or node lines in the output. This results in an
+@sc{ascii} file that you cannot read in Info since it does not contain
+the requisite nodes or menus; but you can print such a file in a
+single, typewriter-like font and produce acceptable output.
+
+@need 150
+@item --no-split
+Suppress the splitting stage of @makeinfo{}. Normally, large
+output files (where the size is greater than 70k bytes) are split into
+smaller subfiles, each one approximately 50k bytes. If you specify
+@samp{--no-split}, @makeinfo{} will not split up the output
+file.
+
+@need 100
+@item --no-pointer-validate
+@item --no-validate
+Suppress the pointer-validation phase of @makeinfo{}. Normally,
+after a Texinfo file is processed, some consistency checks are made to
+ensure that cross references can be resolved, etc.
+@xref{Pointer Validation}.
+
+@need 150
+@item --no-warn
+Suppress the output of warning messages. This does @emph{not}
+suppress the output of error messages, only warnings. You might
+want this if the file you are creating has examples of Texinfo cross
+references within it, and the nodes that are referenced do not actually
+exist.
+
+@item --no-number-footnotes
+Supress automatic footnote numbering. By default, @makeinfo{}
+numbers each footnote sequentially in a single node, resetting the
+current footnote number to 1 at the start of each node.
+
+@need 150
+@item --output @var{file}
+@itemx -o @var{file}
+Specify that the output should be directed to @var{file} and not to the
+file name specified in the @code{@@setfilename} command found in the Texinfo
+source. @var{file} can be the special token @samp{-}, which specifies
+standard output.
+
+@need 150
+@item --paragraph-indent @var{indent}
+Set the paragraph indentation style to @var{indent}. The value set by
+this option overrides the value set in a Texinfo file by an
+@code{@@paragraphindent} command. The value of @var{indent} is
+interpreted as follows:
+
+@itemize @bullet
+@item
+If the value of @var{indent} is @samp{asis}, do not change the
+existing indentation at the starts of paragraphs.
+
+@item
+If the value of @var{indent} is zero, delete any existing
+indentation.
+
+@item
+If the value of @var{indent} is greater than zero, indent each
+paragraph by that number of spaces.
+@end itemize
+
+@need 100
+@item --reference-limit @var{limit}
+Set the value of the number of references to a node that
+@makeinfo{} will make without reporting a warning. If a node has more
+than this number of references in it, @makeinfo{} will make the
+references but also report a warning.
+
+@need 150
+@item -U @var{var}
+Cause @var{var} to be undefined. This is equivalent to
+@code{@@clear @var{var}} in the Texinfo file.
+
+@need 100
+@item --verbose
+Cause @makeinfo{} to display messages saying what it is doing.
+Normally, @makeinfo{} only outputs messages if there are errors or
+warnings.
+
+@need 100
+@item --version
+Report the version number of this copy of @makeinfo{}.
+@end table
+
+@section Pointer Validation
+@cindex Pointer validation with @makeinfo{}
+@cindex Validation of pointers
+
+If you do not suppress pointer-validation (by using the
+@samp{--no-pointer-validation} option), @makeinfo{}
+will check the validity of the final Info file. Mostly,
+this means ensuring that nodes you have referenced
+really exist. Here is a complete list of what is
+checked:
+
+@enumerate
+@item
+If a `Next', `Previous', or `Up' node reference is a reference to a
+node in the current file and is not an external reference such as to
+@file{(dir)}, then the referenced node must exist.
+
+@item
+In every node, if the `Previous' node is different from the `Up' node,
+then the `Previous' node must also be pointed to by a `Next' node.
+
+@item
+Every node except the `Top' node must have an `Up' pointer.
+
+@item
+The node referenced by an `Up' pointer must contain a reference to the
+current node in some manner other than through a `Next' reference.
+This includes menu entries and cross references.
+
+@item
+If the `Next' reference of a node is not the same as the `Next' reference
+of the `Up' reference, then the node referenced by the `Next' pointer
+must have a `Previous' pointer that points back to the current node.
+This rule allows the last node in a section to point to the first node
+of the next chapter.
+@end enumerate
+
+@bye
diff --git a/gnu/usr.bin/texinfo/makeinfo/macros/html.texi b/gnu/usr.bin/texinfo/makeinfo/macros/html.texi
new file mode 100644
index 00000000000..60760825c68
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/macros/html.texi
@@ -0,0 +1,269 @@
+@c html.texi: -*- Texinfo -*- Macros which support HTML output.
+
+@c Copyright (c) 1995 Brian Fox (bfox@ai.mit.edu)
+@c Author: Brian J. Fox (bfox@ai.mit.edu) Sat Apr 1 20:30:54 1995.
+@c
+@c I didn't want to write this myself, because I wanted some HTML wizard
+@c to get everything exactly right. However, rms continues to believe
+@c that the macro system is not a good idea. I couldn't disagree more,
+@c so I am writing this as an example of how useful such macros can be.
+
+@macro html
+@set html
+<html>
+@end macro
+
+@c
+@c The first step is to define the macros which really only have meaning
+@c when producing output for HTML.
+
+@c
+@c @anchor{Brian Fox, http://www.ua.com/users/bfox/}
+@c
+@macro anchor{text, link}
+@ifset html
+<a href="\link\">\text\</a>
+@end ifset
+@ifclear html
+\text\
+@end ifclear
+@end macro
+
+@macro pre{}
+@ifset html
+<pre>
+@end ifset
+@end macro
+
+@macro endpre{}
+@ifset html
+</pre>
+@end ifset
+@end macro
+
+@macro TeX
+@ifset html
+<i>T</i>e<i>X</i>
+@end ifset
+@ifclear html
+@TeX{}
+@end ifclear
+@end macro
+
+@macro paragraph{}
+@ifset html
+<p>
+@end ifset
+@end macro
+
+@c
+@c @email{bfox@@ai.mit.edu}
+@c
+@macro email{address}
+@anchor{mailto:\address\, \address\}
+@end macro
+
+@c
+@c Redefine the TeXinfo commands which have direct HTML counterparts.
+@c
+
+@macro html-define-0arg{command, html-insertion}
+@macro \command\
+@ifset html
+\html-insertion\
+@end ifset
+@ifclear html
+@\command\
+@end ifclear
+@end macro
+@end macro
+
+@macro html-define-1arg{command, html-insertion}
+@macro \command\{arg}
+@ifset html
+\html-insertion\
+@end ifset
+@ifclear html
+@\command\{\arg\}
+@end ifclear
+@end macro
+@end macro
+
+@macro html-define-line{command, html-insertion}
+@macro \command\{line}
+@ifset html
+\html-insertion\
+@end ifset
+@ifclear html
+@\command\ \line\
+@end ifclear
+@end macro
+@end macro
+
+@html-define-0arg{*, <br>}
+@html-define-1arg{b, <b>\\arg\\</b>}
+@html-define-1arg{code, <tt><b>\\arg\\</b></tt>}
+@html-define-line{itemize, <ul>}
+@html-define-line{item,<p><li>}
+@html-define-line{heading,<h1>\\line\\</h1>}
+@html-define-0arg{bye, </html>}
+
+@c
+@c Define into nothing the macros which do nothing in html.
+@c
+@html-define-line{group,}
+
+@c
+@c Define a macro which is used to define other macros. This one makes
+@c a macro which creates an HTML header line. No sectioning commands
+@c are used. This takes advantage of the non-recursion feature of
+@c macro execution.
+@macro node_define{orig-name, header-style}
+@macro \orig-name\{title}
+@ifset html
+@node \title\
+<a name="\title\"><\header-style\>\title\</\header-style\></a>
+@end ifset
+@ifclear html
+@\orig-name\ \title\
+@end ifclear
+@end macro
+@end macro
+
+@c
+@c The same as NODE_DEFINE, but italicized.
+@macro inode_define{orig-name, header-style}
+@macro \orig-name\{title}
+@ifset html
+@node \title\
+<a name="\title\"><\header-style\><i>\title\</i></\header-style\></a>
+@end ifset
+@ifclear html
+@\orig-name\ \title\
+@end ifclear
+@end macro
+@end macro
+
+@c Ignore @node commands.
+@html-define-line{node,}
+
+@c Here is a special one for "@top".
+@macro top{title}
+@end macro
+
+@c Now actually define a new set of sectioning commands.
+@node_define {appendix, h1}
+@node_define {appendixsec, h2}
+@node_define {appendixsubsec, h3}
+@node_define {appendixsubsubsec, h4}
+@node_define {chapter, h1}
+@node_define {section, h2}
+@node_define {subsection, h3}
+@node_define {subsubsec, h4}
+@node_define {unnumbered, h1}
+@node_define {unnumberedsec, h2}
+@node_define {unnumberedsubsec, h3}
+@node_define {unnumberedsubsubsec, h4}
+
+@c The italicized analogues.
+@inode_define {iappendix, h1}
+@inode_define {iappendixsec, h2}
+@inode_define {iappendixsubsec, h3}
+@inode_define {iappendixsubsubsec, h4}
+@inode_define {ichapter, h1}
+@inode_define {isection, h2}
+@inode_define {isubsection, h3}
+@inode_define {isubsubsec, h4}
+@inode_define {iunnumbered, h1}
+@inode_define {iunnumberedsec, h2}
+@inode_define {iunnumberedsubsec, h3}
+@inode_define {iunnumberedsubsubsec, h4}
+
+@c Manual starter:
+@c
+@c Pass arguments of TITLE, AUTHOR, and a short DESCRIPTION.
+@c Immediately following, insert the Top node's menu.
+@c
+@c Typical usage:
+@c
+@c @document{Makeinfo, Brian J. Fox, This file documents the use of the
+@c @code{makeinfo} program\, versions 1.61 and later.}
+@c
+@c @menu
+@c * What is @makeinfo{}?::
+@c @end menu
+@macro document{title, author, description}
+@ifinfo
+\description\
+
+Copyright @copyright{} 1995 \author\
+Copyright @copyright{} 1995 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to process this file through TeX and print the
+results, provided the printed document carries copying permission
+notice identical to this one except for the removal of this paragraph
+(this paragraph not being relevant to the printed manual).
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the entire
+resulting derived work is distributed under the terms of a permission
+notice identical to this one.
+
+Permission is granted to copy and distribute translations of this manual
+into another language, under the above conditions for modified versions,
+except that this permission notice may be stated in a translation approved
+by the copyright holders.
+@end ifinfo
+
+@titlepage
+@title \title\
+@author \author\
+
+@page
+@vskip 0pt plus 1filll
+Copyright @copyright{} 1995 \author\
+Copyright @copyright{} 1995 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the entire
+resulting derived work is distributed under the terms of a permission
+notice identical to this one.
+
+Permission is granted to copy and distribute translations of this manual
+into another language, under the above conditions for modified versions,
+except that this permission notice may be stated in a translation approved
+by the copyright holders.
+@end titlepage
+
+@top{\title\}
+
+\description\
+@end macro
+
+@html-define-line{end,
+@ifeq{"\\line\\"\, "ifinfo"\, @end ifinfo}
+@ifeq{"\\line\\"\, "ifset"\, @end ifset}
+@ifeq{"\\line\\"\, "ifclear"\, @end ifclear}
+@ifeq{"\\line\\"\, "cartouche"\, @end cartouche}
+@ifeq{"\\line\\"\, "menu"\, @end menu}
+@ifeq{"\\line\\"\, "itemize"\, </ul>}
+@ifeq{"\\line\\"\, "enumerate"\, </ul>}
+@ifeq{"\\line\\"\, "table"\, </ul>}
+@ifeq{"\\line\\"\, "ftable"\, </ul>}
+@ifeq{"\\line\\"\, "vtable"\, </ul>}
+@ifeq{"\\line\\"\, "menu"\, xxx}
+@ifeq{"\\line\\"\, "quotation"\, </pre>}
+@ifeq{"\\line\\"\, "example"\, </tt></pre>}
+@ifeq{"\\line\\"\, "smallexample"\, </tt></pre>}
+@ifeq{"\\line\\"\, "lisp"\, </tt></pre>}
+@ifeq{"\\line\\"\, "format"\, </tt></pre>}
+@ifeq{"\\line\\"\, "display"\, </tt></pre>}
+@ifeq{"\\line\\"\, "group"}}
diff --git a/gnu/usr.bin/texinfo/makeinfo/macros/multifmt.texi b/gnu/usr.bin/texinfo/makeinfo/macros/multifmt.texi
new file mode 100644
index 00000000000..0f2eb32a585
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/macros/multifmt.texi
@@ -0,0 +1,41 @@
+@c multiformat.texi: -*- Texinfo -*- \input texinfo.tex
+
+@c Copyright (c) 1995 Universal Access, Inc
+@c Author: Brian J. Fox (bfox@ua.com) Sun Apr 2 07:56:23 1995.
+@setfilename multiformat.info
+
+@include html.texi
+
+@ifset html
+@html
+@end ifset
+
+@node First Node, First Section, (dir), (dir)
+@chapter First Chapter
+Here is some text that belongs in the first chapter. Nothing very
+exciting happens here, but this is enough text to span a couple of
+lines, and we feel that is important.
+@paragraph
+
+This is the second paragraph of the first chapter. Note that the
+formatting commands in @code{HTML} seem to do the right thing, as do the
+commands when invoked in @code{Texinfo} mode and in @TeX{}.
+
+@node First Section, , First Node, First Node
+@isection First Section
+
+Here is some text in the first section of the first chapter. We are
+trying very hard to examine the output here to see exactly how proper it
+is. If I wasn't so tired, we could probably see it already.
+@paragraph
+
+Here is a list of items:
+@paragraph
+
+@itemize @bullet
+@item Here is the first item.
+@item Here is the second item.
+@end itemize
+
+@bye
+
diff --git a/gnu/usr.bin/texinfo/makeinfo/macros/res-samp.texi b/gnu/usr.bin/texinfo/makeinfo/macros/res-samp.texi
new file mode 100644
index 00000000000..5b4e869e0c3
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/macros/res-samp.texi
@@ -0,0 +1,32 @@
+\input texinfo.tex
+@setfilename resume-example.info
+
+@include resume.texi
+
+@name Brian J. Fox
+@email bfox@@ai.mit.edu
+@street 116 Barranca Ave, Ste. B
+@city Santa Barbara
+@state CA
+@zip 93109
+@phone (805) 564-2192
+
+@resume
+
+@block{EDUCATION}
+@entry{
+12/11/59,
+12/11/63,
+My Mom's House,
+Learning at home with my mother.,
+This was the most learning I ever did.}
+
+@entry{12/11/63, 12/11/77, Brookline\, MA, Learning in the public school system.}
+@entry{12/11/78, 12/11/81, Santa Barbara\, CA, Learning in life
+experience\, and three months at Santa Barbara City College.}
+
+@block{WORK EXPERIENCE}
+@entry{12/11/59, 12/11/75, Mom's house, Various and sundry tasks\,
+including washing dishes and clothes\, and toilet training.}
+@entry{3 months ago, present, Terrapin\, Inc., hacking up Unix systems\, breaking @code{LOGO} worlds\, terrorizing surrounding neighborhood.}
+@bye
diff --git a/gnu/usr.bin/texinfo/makeinfo/macros/resume.texi b/gnu/usr.bin/texinfo/makeinfo/macros/resume.texi
new file mode 100644
index 00000000000..a4dc5d04be9
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/macros/resume.texi
@@ -0,0 +1,64 @@
+@c
+@c Reusme writing macros. Produce a very specific format.
+@c
+
+@c A macro which creates a macro. The resultant macro can be called to
+@c set a variable which has the same name as the macro. Use
+@c @value{name} to get the value set in @name{}.
+@macro make-var-macro{macro-name}
+@macro \macro-name\{value}
+@quote-arg
+@set \macro-name\ \value\
+@end macro
+@end macro
+
+@make-var-macro{name}
+@make-var-macro{street}
+@make-var-macro{city}
+@make-var-macro{state}
+@make-var-macro{zip}
+@make-var-macro{phone}
+@make-var-macro{email}
+
+@c Give all of the above variable/macros a null value to start.
+@name
+@street
+@city
+@state
+@zip
+@phone
+@email
+
+@c A typical heading for a resume block is a non-indented line.
+@macro block{title}
+@paragraphindent none
+@comment @noindent
+@heading \title\
+@end macro
+
+@c A typical entry in a resume has a from-date, a to-date, a location,
+@c a job title, and a longer descrition body.
+
+@macro entry{from-date, to-date, where, what, body}
+@paragraphindent 8
+@b{\where\: \what\ (\from-date\ --- \to-date\)}
+@paragraphindent 3
+
+\body\
+@paragraphindent none
+@end macro
+
+@macro address{}
+@value{name}@*
+@value{street}@*
+@value{city}, @value{state}@*
+@value{zip}@*
+@value{phone}
+@end macro
+
+@macro resume{}
+@center @value{name}@*
+@center @value{street}@*
+@center @value{city}, @value{state} @value{zip}@*
+@center @value{email}
+@end macro
diff --git a/gnu/usr.bin/texinfo/makeinfo/macros/simpledoc.texi b/gnu/usr.bin/texinfo/makeinfo/macros/simpledoc.texi
new file mode 100644
index 00000000000..576cb9b8e41
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/macros/simpledoc.texi
@@ -0,0 +1,135 @@
+
+@comment Here are some useful examples of the macro facility.
+
+@c Simply insert the right version of the texinfo name.
+@macro texinfo{}
+TeXinfo
+@end macro
+
+@c Define a macro which expands to a pretty version of the name of the
+@c Makeinfo program.
+@macro makeinfo{}
+@code{Makeinfo}
+@end macro
+
+@c Simple insert the right version of the Info name.
+@macro Info{}
+@code{Info}
+@end macro
+
+@c Define a macro which is used to define other macros. This one makes
+@c a macro which creates a node and gives it a sectioning command. Note
+@c that the created macro uses the original definition within the
+@c expansion text. This takes advantage of the non-recursion feature of
+@c macro execution.
+@macro node_define{orig-name}
+@macro \orig-name\{title}
+@node \title\
+@\orig-name\ \title\
+@end macro
+@end macro
+
+@c Here is a special one for "@top", causing the node name to be "Top",
+@c but leaving the section name as the specified title.
+@macro top{title}
+@node Top
+@top \title\
+@end macro
+
+@c Now actually define a new set of sectioning commands.
+@node_define {appendix}
+@node_define {appendixsec}
+@node_define {appendixsubsec}
+@node_define {appendixsubsubsec}
+@node_define {chapter}
+@node_define {section}
+@node_define {subsection}
+@node_define {subsubsec}
+@node_define {unnumbered}
+@node_define {unnumberedsec}
+@node_define {unnumberedsubsec}
+@node_define {unnumberedsubsubsec}
+
+@c The italicized analogues.
+@node_define {iappendix}
+@node_define {iappendixsec}
+@node_define {iappendixsubsec}
+@node_define {iappendixsubsubsec}
+@node_define {ichapter}
+@node_define {isection}
+@node_define {isubsection}
+@node_define {isubsubsec}
+@node_define {iunnumbered}
+@node_define {iunnumberedsec}
+@node_define {iunnumberedsubsec}
+@node_define {iunnumberedsubsubsec}
+
+@c Manual starter:
+@c
+@c Pass arguments of TITLE, AUTHOR, and a short DESCRIPTION.
+@c Immediately following, insert the Top node's menu.
+@c
+@c Typical usage:
+@c
+@c @document{Makeinfo, Brian J. Fox, This file documents the use of the
+@c @code{makeinfo} program\, versions 1.58 and later.}
+@c
+@c @menu
+@c * What is @makeinfo{}?::
+@c @end menu
+
+@macro document{title, author, description}
+@ifinfo
+\description\
+
+Copyright @copyright{} 1994 \author\
+Copyright @copyright{} 1994 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to process this file through TeX and print the
+results, provided the printed document carries copying permission
+notice identical to this one except for the removal of this paragraph
+(this paragraph not being relevant to the printed manual).
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the entire
+resulting derived work is distributed under the terms of a permission
+notice identical to this one.
+
+Permission is granted to copy and distribute translations of this manual
+into another language, under the above conditions for modified versions,
+except that this permission notice may be stated in a translation approved
+by the copyright holders.
+@end ifinfo
+
+@titlepage
+@title \title\
+@author \author\
+
+@page
+@vskip 0pt plus 1filll
+Copyright @copyright{} 1994 \author\
+Copyright @copyright{} 1994 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the entire
+resulting derived work is distributed under the terms of a permission
+notice identical to this one.
+
+Permission is granted to copy and distribute translations of this manual
+into another language, under the above conditions for modified versions,
+except that this permission notice may be stated in a translation approved
+by the copyright holders.
+@end titlepage
+
+@top{\title\}
+
+\description\
+@end macro
diff --git a/gnu/usr.bin/texinfo/makeinfo/makeinfo.c b/gnu/usr.bin/texinfo/makeinfo/makeinfo.c
new file mode 100644
index 00000000000..b3a0d5ff32c
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/makeinfo.c
@@ -0,0 +1,9258 @@
+/* Makeinfo -- convert texinfo format files into info files.
+
+ Copyright (C) 1987, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
+
+ 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). */
+
+/* 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 <sys/types.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <errno.h>
+
+#if defined (HAVE_VARARGS_H)
+#include <varargs.h>
+#endif /* HAVE_VARARGS_H */
+#include "getopt.h"
+
+#if defined (HAVE_UNISTD_H)
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+#if defined (VMS)
+#include <perror.h>
+#endif
+
+#if defined (HAVE_STRING_H)
+#include <string.h>
+#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__)
+#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__ */
+
+void *xmalloc (), *xrealloc ();
+#if defined (__osf__)
+extern void *malloc (), *realloc ();
+#endif /* __osf__ */
+
+char **get_brace_args ();
+int array_len ();
+void free_array ();
+static void isolate_nodename ();
+
+/* 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;
+
+ITEXT *remember_itext ();
+void forget_itext (), me_append_before_this_command ();
+void append_to_expansion_output (), write_region_to_macro_output ();
+void maybe_write_itext (), me_execute_string ();
+#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;
+
+void add_macro (), execute_macro ();
+MACRO_DEF *find_macro (), *delete_macro ();
+#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;
+
+/* 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 */
+
+void print_version_info ();
+void usage ();
+void push_node_filename (), pop_node_filename ();
+void remember_error ();
+void convert_from_stream (), convert_from_file (), convert_from_loaded_file ();
+void init_internals (), init_paragraph (), init_brace_stack ();
+void init_insertion_stack (), init_indices ();
+void init_tag_table (), write_tag_table (), write_tag_table_internal ();
+void validate_file (), validate_other_references (), split_file ();
+void free_node_references (), do_enumeration (), handle_variable ();
+void handle_variable_internal ();
+void execute_string ();
+void normalize_node_name ();
+void undefindex (), top_defindex (), gen_defindex ();
+void define_user_command ();
+void free_pending_notes (), output_pending_notes ();
+
+void reader_loop (), read_command ();
+void remember_brace (), remember_brace_1 ();
+void pop_and_call_brace (), discard_braces ();
+void add_word_args (), add_word (), add_char (), insert (), flush_output ();
+void close_paragraph_with_lines (), close_paragraph ();
+void ignore_blank_line ();
+void do_flush_right_indentation ();
+void start_paragraph (), indent ();
+
+void insert_self (), cm_ignore_line ();
+
+void
+ cm_asterisk (), cm_dots (), cm_bullet (), cm_TeX (),
+ cm_copyright (), cm_code (), cm_samp (), cm_file (), cm_kbd (),
+ cm_key (), cm_ctrl (), cm_var (), cm_dfn (), cm_emph (), cm_strong (),
+ cm_cite (), cm_italic (), cm_bold (), cm_roman (), cm_title (), cm_w (),
+ cm_refill (), cm_titlefont ();
+
+void
+ cm_chapter (), cm_unnumbered (), cm_appendix (), cm_top (),
+ cm_section (), cm_unnumberedsec (), cm_appendixsec (),
+ cm_subsection (), cm_unnumberedsubsec (), cm_appendixsubsec (),
+ cm_subsubsection (), cm_unnumberedsubsubsec (), cm_appendixsubsubsec (),
+ cm_heading (), cm_chapheading (), cm_subheading (), cm_subsubheading (),
+ cm_majorheading (), cm_raisesections (), cm_lowersections ();
+
+/* All @defxxx commands map to cm_defun (). */
+void cm_defun ();
+
+void
+ cm_node (), cm_menu (), cm_xref (), cm_ftable (), cm_vtable (), cm_pxref (),
+ cm_inforef (), cm_quotation (), cm_display (), cm_itemize (),
+ cm_enumerate (), cm_table (), cm_itemx (), cm_noindent (), cm_setfilename (),
+ cm_br (), cm_sp (), cm_page (), cm_group (), cm_center (), cm_include (),
+ cm_bye (), cm_item (), cm_end (), cm_infoinclude (), cm_ifinfo (),
+ cm_kindex (), cm_cindex (), cm_findex (), cm_pindex (), cm_vindex (),
+ cm_tindex (), cm_asis (), cm_synindex (), cm_printindex (), cm_minus (),
+ cm_footnote (), cm_force_abbreviated_whitespace (), cm_example (),
+ cm_smallexample (), cm_lisp (), cm_format (), cm_exdent (), cm_defindex (),
+ cm_defcodeindex (), cm_sc (), cm_result (), cm_expansion (), cm_equiv (),
+ cm_print (), cm_error (), cm_point (), cm_today (), cm_flushleft (),
+ cm_flushright (), cm_smalllisp (), cm_finalout (), cm_math (),
+ cm_cartouche (), cm_ignore_sentence_ender ();
+
+/* Conditionals. */
+void cm_set (), cm_clear (), cm_ifset (), cm_ifclear ();
+void cm_value (), cm_ifeq ();
+
+#if defined (HAVE_MACROS)
+/* Define a user-defined command which is simple substitution. */
+void cm_macro (), cm_unmacro ();
+#endif /* HAVE_MACROS */
+
+/* Options. */
+void cm_paragraphindent (), cm_footnotestyle ();
+
+/* Internals. */
+void do_nothing (), command_name_condition ();
+void misplaced_brace (), cm_obsolete ();
+
+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 (argc, argv)
+ int argc;
+ char **argv;
+{
+ extern int errors_printed;
+ char *filename_part ();
+ 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. */
+void
+print_version_info ()
+{
+ 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. */
+void *
+xmalloc (nbytes)
+ 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. */
+void *
+xrealloc (pointer, nbytes)
+ 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. */
+void
+usage (stream, exit_value)
+ 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);
+}
+
+/* **************************************************************** */
+/* */
+/* 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. */
+GENERIC_LIST *
+reverse_list (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. */
+char *
+find_and_load (filename)
+ char *filename;
+{
+ struct stat fileinfo;
+ long file_size;
+ int file = -1, n, i, count = 0;
+ char *fullpath, *result, *get_file_info_in_path ();
+
+ 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. */
+#if defined (VMS)
+ while ((n = read (file, result + count, file_size)) > 0)
+ count += n;
+ if (n == -1)
+#else /* !VMS */
+ count = file_size;
+ if (read (file, result, file_size) != file_size)
+#endif /* !VMS */
+ 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. */
+void
+pushfile ()
+{
+ 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. */
+void
+popfile ()
+{
+ 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. */
+void
+flush_file_stack ()
+{
+ 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;
+
+void
+push_node_filename ()
+{
+ 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++;
+}
+
+void
+pop_node_filename ()
+{
+ 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. */
+char *
+filename_part (filename)
+ 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. */
+char *
+pathname_part (filename)
+ char *filename;
+{
+ char *expand_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);
+}
+
+char *
+filename_non_directory (name)
+ 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. */
+char *
+expand_filename (filename, input_name)
+ char *filename, *input_name;
+{
+ register int i;
+ char *full_pathname ();
+
+ 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 > (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. */
+char *
+full_pathname (filename)
+ 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_GETCWD)
+ if (!getcwd (localdir, 1024))
+#else /* !HAVE_GETCWD */
+ if (!getwd (localdir))
+#endif /* !HAVE_GETCWD */
+ {
+ 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);
+}
+
+char *
+output_name_from_input_name (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. */
+int
+fs_error (filename)
+ char *filename;
+{
+ remember_error ();
+ perror (filename);
+ return (0);
+}
+
+/* Print an error message, and return false. */
+#if defined (HAVE_VARARGS_H) && defined (HAVE_VFPRINTF)
+
+int
+error (va_alist)
+ va_dcl
+{
+ char *format;
+ va_list args;
+
+ remember_error ();
+ va_start (args);
+ format = va_arg (args, char *);
+ vfprintf (stderr, format, args);
+ va_end (args);
+ fprintf (stderr, "\n");
+}
+
+/* Just like error (), but print the line number as well. */
+int
+line_error (va_alist)
+ va_dcl
+{
+ char *format;
+ va_list args;
+
+ remember_error ();
+ va_start (args);
+ format = va_arg (args, char *);
+ fprintf (stderr, "%s:%d: ", input_filename, line_number);
+ vfprintf (stderr, format, args);
+ fprintf (stderr, ".\n");
+ va_end (args);
+ return ((int) 0);
+}
+
+int
+warning (va_alist)
+ va_dcl
+{
+ char *format;
+ va_list args;
+
+ va_start (args);
+ format = va_arg (args, char *);
+ if (print_warnings)
+ {
+ fprintf (stderr, "%s:%d: Warning: ", input_filename, line_number);
+ vfprintf (stderr, format, args);
+ fprintf (stderr, ".\n");
+ }
+ va_end (args);
+ return ((int) 0);
+}
+
+#else /* !(HAVE_VARARGS_H && HAVE_VFPRINTF) */
+
+int
+error (format, arg1, arg2, arg3, arg4, arg5)
+ char *format;
+{
+ remember_error ();
+ fprintf (stderr, format, arg1, arg2, arg3, arg4, arg5);
+ fprintf (stderr, "\n");
+ return ((int) 0);
+}
+
+/* Just like error (), but print the line number as well. */
+int
+line_error (format, arg1, arg2, arg3, arg4, arg5)
+ char *format;
+{
+ remember_error ();
+ fprintf (stderr, "%s:%d: ", input_filename, line_number);
+ fprintf (stderr, format, arg1, arg2, arg3, arg4, arg5);
+ fprintf (stderr, ".\n");
+ return ((int) 0);
+}
+
+int
+warning (format, arg1, arg2, arg3, arg4, arg5)
+ char *format;
+{
+ if (print_warnings)
+ {
+ fprintf (stderr, "%s:%d: Warning: ", input_filename, line_number);
+ fprintf (stderr, format, arg1, arg2, arg3, arg4, arg5);
+ fprintf (stderr, ".\n");
+ }
+ return ((int) 0);
+}
+
+#endif /* !(HAVE_VARARGS_H && HAVE_VFPRINTF) */
+
+/* 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. */
+void
+remember_error ()
+{
+ 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. */
+char *
+read_token ()
+{
+ 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. */
+int
+self_delimiting (character)
+ int character;
+{
+ return (member (character, "{}:.@*'`,!?; \n\t"));
+}
+
+/* Clear whitespace from the front and end of string. */
+void
+canon_white (string)
+ 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. */
+void
+fix_whitespace (string)
+ 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. */
+void
+discard_until (string)
+ 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. */
+int
+get_until (match, string)
+ char *match, **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. */
+void
+get_until_in_line (match, string)
+ char *match, **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;
+}
+
+void
+get_rest_of_line (string)
+ 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. */
+void
+backup_input_pointer ()
+{
+ 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. */
+void
+get_until_in_braces (match, string)
+ char *match, **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
+};
+
+void
+initialize_conversion ()
+{
+ 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. */
+void
+convert_from_stream (stream, name)
+ 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);
+}
+
+void
+convert_from_file (name)
+ 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);
+}
+
+void
+convert_from_loaded_file (name)
+ char *name;
+{
+ char *expand_filename (), *filename_part ();
+ 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 (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);
+}
+
+void
+free_and_clear (pointer)
+ char **pointer;
+{
+ if ((*pointer) != (char *) NULL)
+ {
+ free (*pointer);
+ *pointer = (char *) NULL;
+ }
+}
+
+ /* Initialize some state. */
+void
+init_internals ()
+{
+ 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;
+}
+
+void
+init_paragraph ()
+{
+ free_and_clear (&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. */
+void
+reader_loop ()
+{
+ 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 *glean_node_from_menu (), *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). */
+COMMAND *
+get_command_entry (string)
+ 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. */
+void
+read_command ()
+{
+ 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. */
+char *
+find_proc_name (proc)
+ 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!");
+}
+
+void
+init_brace_stack ()
+{
+ brace_stack = (BRACE_ELEMENT *) NULL;
+}
+
+void
+remember_brace (proc)
+ 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. */
+void
+remember_brace_1 (proc, position)
+ 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. */
+void
+pop_and_call_brace ()
+{
+ 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. */
+void
+adjust_braces_following (here, amount)
+ int here, 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. */
+void
+discard_braces ()
+{
+ 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;
+ }
+ }
+}
+
+int
+get_char_len (character)
+ 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);
+}
+
+#if defined (HAVE_VARARGS_H) && defined (HAVE_VSPRINTF)
+
+void
+add_word_args (va_alist)
+ va_dcl
+{
+ char buffer[1000];
+ char *format;
+ va_list args;
+
+ va_start (args);
+ format = va_arg (args, char *);
+ vsprintf (buffer, format, args);
+ va_end (args);
+ add_word (buffer);
+}
+
+#else /* !(HAVE_VARARGS_H && HAVE_VSPRINTF) */
+
+void
+add_word_args (format, arg1, arg2, arg3, arg4, arg5)
+ char *format;
+{
+ char buffer[1000];
+ sprintf (buffer, format, arg1, arg2, arg3, arg4, arg5);
+ add_word (buffer);
+}
+
+#endif /* !(HAVE_VARARGS_H && HAVE_VSPRINTF) */
+
+/* Add STRING to output_paragraph. */
+void
+add_word (string)
+ 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. */
+void
+add_char (character)
+ 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. */
+void
+insert (character)
+ 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. */
+void
+kill_self_indent (count)
+ 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. */
+void
+inhibit_output_flushing ()
+{
+ flushing_ignored++;
+}
+
+/* Allow calls to flush_output () to write the paragraph data. */
+void
+uninhibit_output_flushing ()
+{
+ flushing_ignored--;
+}
+
+void
+flush_output ()
+{
+ 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. */
+void
+close_single_paragraph ()
+{
+ close_paragraph_with_lines (0);
+}
+
+/* Close a paragraph after an insertion has ended. */
+void
+close_insertion_paragraph ()
+{
+ 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;
+}
+
+void
+close_paragraph_with_lines (lines)
+ int lines;
+{
+ int old_spacing = paragraph_spacing;
+ paragraph_spacing = lines;
+ close_paragraph ();
+ paragraph_spacing = old_spacing;
+}
+
+/* Close the currently open paragraph. */
+void
+close_paragraph ()
+{
+ 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. */
+void
+ignore_blank_line ()
+{
+ last_inserted_character = '\n';
+ last_char_was_newline = 1;
+}
+
+/* Align the end of the text in output_paragraph with fill_column. */
+void
+do_flush_right_indentation ()
+{
+ 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. */
+void
+start_paragraph ()
+{
+ /* 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. */
+void
+indent (amount)
+ 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. */
+int
+search_forward (string, from)
+ 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)
+int
+strcasecmp (string1, string2)
+ char *string1, *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;
+
+void
+init_insertion_stack ()
+{
+ insertion_stack = (INSERTION_ELT *) NULL;
+}
+
+/* Return the type of the current insertion. */
+enum insertion_type
+current_insertion_type ()
+{
+ 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. */
+char *
+current_item_function ()
+{
+ 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);
+}
+
+char *
+get_item_function ()
+{
+ 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. */
+void
+push_insertion (type, item_function)
+ 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. */
+void
+pop_insertion ()
+{
+ 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. */
+char *
+insertion_type_pname (type)
+ 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. */
+enum insertion_type
+find_type_from_name (name)
+ char *name;
+{
+ int index = 0;
+ while (index < (int) bad_type)
+ {
+ if (strcmp (name, insertion_type_names[index]) == 0)
+ return (enum insertion_type) index;
+ index++;
+ }
+ return (bad_type);
+}
+
+void
+do_nothing ()
+{
+}
+
+int
+defun_insertion (type)
+ 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;
+
+void
+start_enumerating (at, type)
+ int at, 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;
+}
+
+void
+stop_enumerating ()
+{
+ --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. */
+void
+enumerate_item ()
+{
+ 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. */
+void
+begin_insertion (type)
+ 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;
+ }
+
+ 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. */
+void
+end_insertion (type)
+ 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. */
+void
+discard_insertions ()
+{
+ 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. */
+void
+insert_self ()
+{
+ add_word (command);
+}
+
+/* Force a line break in the output. */
+void
+cm_asterisk ()
+{
+ close_single_paragraph ();
+#if !defined (ASTERISK_NEW_PARAGRAPH)
+ cm_noindent ();
+#endif /* ASTERISK_NEW_PARAGRAPH */
+}
+
+/* Insert ellipsis. */
+void
+cm_dots (arg)
+ int arg;
+{
+ if (arg == START)
+ add_word ("...");
+}
+
+void
+cm_bullet (arg)
+ int arg;
+{
+ if (arg == START)
+ add_char ('*');
+}
+
+void
+cm_minus (arg)
+ int arg;
+{
+ if (arg == START)
+ add_char ('-');
+}
+
+/* Insert "TeX". */
+void
+cm_TeX (arg)
+ int arg;
+{
+ if (arg == START)
+ add_word ("TeX");
+}
+
+void
+cm_copyright (arg)
+ 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
+
+void
+cm_today (arg)
+ 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));
+ }
+}
+
+void
+cm_code (arg)
+ 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--;
+ }
+}
+
+void
+cm_samp (arg)
+ int arg;
+{
+ cm_code (arg);
+}
+
+void
+cm_file (arg)
+ int arg;
+{
+ cm_code (arg);
+}
+
+void
+cm_kbd (arg)
+ int arg;
+{
+ cm_code (arg);
+}
+
+void
+cm_key (arg)
+ int arg;
+{
+}
+
+/* Convert the character at position into CTL. */
+void
+cm_ctrl (arg, start, end)
+ int arg, start, 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. */
+void
+cm_sc (arg, start_pos, end_pos)
+ int arg, start_pos, 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. */
+void
+cm_var (arg, start_pos, end_pos)
+ int arg, start_pos, end_pos;
+{
+ if (arg == END)
+ {
+ while (start_pos < end_pos)
+ {
+ output_paragraph[start_pos] =
+ coerce_to_upper (output_paragraph[start_pos]);
+ start_pos++;
+ }
+ }
+}
+
+void
+cm_dfn (arg, position)
+ int arg, position;
+{
+ add_char ('"');
+}
+
+void
+cm_emph (arg)
+ int arg;
+{
+ add_char ('*');
+}
+
+void
+cm_strong (arg, position)
+ int arg, position;
+{
+ cm_emph (arg);
+}
+
+void
+cm_cite (arg, position)
+ int arg, position;
+{
+ if (arg == START)
+ add_word ("`");
+ else
+ add_word ("'");
+}
+
+/* Current text is italicized. */
+void
+cm_italic (arg, start, end)
+ int arg, start, end;
+{
+}
+
+/* Current text is highlighted. */
+void
+cm_bold (arg, start, end)
+ int arg, start, end;
+{
+ cm_italic (arg);
+}
+
+/* Current text is in roman font. */
+void
+cm_roman (arg, start, end)
+ int arg, start, end;
+{
+}
+
+/* Current text is in roman font. */
+void
+cm_titlefont (arg, start, end)
+ int arg, start, end;
+{
+}
+
+/* Italicize titles. */
+void
+cm_title (arg, start, end)
+ int arg, start, end;
+{
+ cm_italic (arg);
+}
+
+/* @refill is a NOP. */
+void
+cm_refill ()
+{
+}
+
+/* Prevent the argument from being split across two lines. */
+void
+cm_w (arg, start, end)
+ int arg, start, 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. */
+void
+cm_obsolete (arg, start, end)
+ int arg, start, 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. */
+void
+insert_and_underscore (with_char)
+ 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. */
+void
+cm_raisesections ()
+{
+ discard_until ("\n");
+ section_alist_offset--;
+}
+
+/* Shift the meaning of @chapter to @section. */
+void
+cm_lowersections ()
+{
+ discard_until ("\n");
+ section_alist_offset++;
+}
+
+/* Return an integer which identifies the type section present in TEXT. */
+int
+what_section (text)
+ 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. */
+int
+set_top_section_level (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. */
+void
+cm_top ()
+{
+ /* 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 = "*=-.";
+
+void
+sectioning_underscore (command)
+ char *command;
+{
+ char character;
+ char *temp;
+ int level;
+
+ temp = (char *)xmalloc (2 + strlen (command));
+ temp[0] = COMMAND_PREFIX;
+ strcpy (&temp[1], command);
+ 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. */
+void
+cm_chapter ()
+{
+ sectioning_underscore ("chapter");
+}
+
+/* The remainder of the text on this line is a section heading. */
+void
+cm_section ()
+{
+ sectioning_underscore ("section");
+}
+
+/* The remainder of the text on this line is a subsection heading. */
+void
+cm_subsection ()
+{
+ sectioning_underscore ("subsection");
+}
+
+/* The remainder of the text on this line is a subsubsection heading. */
+void
+cm_subsubsection ()
+{
+ sectioning_underscore ("subsubsection");
+}
+
+/* The remainder of the text on this line is an unnumbered heading. */
+void
+cm_unnumbered ()
+{
+ cm_chapter ();
+}
+
+/* The remainder of the text on this line is an unnumbered section heading. */
+void
+cm_unnumberedsec ()
+{
+ cm_section ();
+}
+
+/* The remainder of the text on this line is an unnumbered
+ subsection heading. */
+void
+cm_unnumberedsubsec ()
+{
+ cm_subsection ();
+}
+
+/* The remainder of the text on this line is an unnumbered
+ subsubsection heading. */
+void
+cm_unnumberedsubsubsec ()
+{
+ cm_subsubsection ();
+}
+
+/* The remainder of the text on this line is an appendix heading. */
+void
+cm_appendix ()
+{
+ cm_chapter ();
+}
+
+/* The remainder of the text on this line is an appendix section heading. */
+void
+cm_appendixsec ()
+{
+ cm_section ();
+}
+
+/* The remainder of the text on this line is an appendix subsection heading. */
+void
+cm_appendixsubsec ()
+{
+ cm_subsection ();
+}
+
+/* The remainder of the text on this line is an appendix
+ subsubsection heading. */
+void
+cm_appendixsubsubsec ()
+{
+ cm_subsubsection ();
+}
+
+/* Compatibility functions substitute for chapter, section, etc. */
+void
+cm_majorheading ()
+{
+ cm_chapheading ();
+}
+
+void
+cm_chapheading ()
+{
+ cm_chapter ();
+}
+
+void
+cm_heading ()
+{
+ cm_section ();
+}
+
+void
+cm_subheading ()
+{
+ cm_subsection ();
+}
+
+void
+cm_subsubheading ()
+{
+ cm_subsubsection ();
+}
+
+
+/* **************************************************************** */
+/* */
+/* Adding nodes, and making tags */
+/* */
+/* **************************************************************** */
+
+/* Start a new tag table. */
+void
+init_tag_table ()
+{
+ 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);
+ }
+}
+
+void
+write_tag_table ()
+{
+ write_tag_table_internal (0); /* Not indirect. */
+}
+
+void
+write_tag_table_indirect ()
+{
+ write_tag_table_internal (1);
+}
+
+/* Write out the contents of the existing tag table.
+ INDIRECT_P says how to format the output. */
+void
+write_tag_table_internal (indirect_p)
+ 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;
+}
+
+char *
+get_node_token ()
+{
+ 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". */
+void
+normalize_node_name (string)
+ 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. */
+TAG_ENTRY *
+find_node (name)
+ 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. */
+void
+remember_node (node, prev, next, up, position, line_no, no_warn)
+ char *node, *prev, *next, *up;
+ int position, line_no, 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. */
+void
+cm_node ()
+{
+ 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;
+ char *glean_node_from_menu ();
+
+ /* 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. */
+void
+validate_file (tag_table)
+ TAG_ENTRY *tag_table;
+{
+ char *old_input_filename = input_filename;
+ TAG_ENTRY *tags = tag_table;
+
+ 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;
+ NODE_REF *find_node_reference ();
+
+ 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_table;
+ 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. */
+int
+validate (tag, line, label)
+ 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. */
+void
+split_file (filename, size)
+ char *filename;
+ int size;
+{
+ char *root_filename, *root_pathname;
+ char *the_file, *filename_part ();
+ 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,
+ "%s%s-%d", root_pathname, root_filename, which_file);
+
+ 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);
+ }
+
+ sprintf (indirect_info, "%s-%d: %d\n",
+ 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. */
+char *
+reftype_type_string (type)
+ 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. */
+void
+remember_node_reference (node, line, type)
+ 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 = strdup (current_node);
+ temp->filename = node_filename;
+
+ node_references = temp;
+}
+
+void
+validate_other_references (ref_list)
+ 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. */
+NODE_REF *
+find_node_reference (node, ref_list)
+ 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);
+}
+
+void
+free_node_references ()
+{
+ 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 "* "
+char *
+glean_node_from_menu (remember_reference)
+ 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 (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';
+}
+
+void
+cm_menu ()
+{
+ begin_insertion (menu);
+}
+
+
+/* **************************************************************** */
+/* */
+/* Cross Reference Hacking */
+/* */
+/* **************************************************************** */
+
+char *
+get_xref_token ()
+{
+ 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. */
+void
+cm_xref (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;
+ }
+ }
+ }
+ }
+}
+
+void
+cm_pxref (arg)
+ int arg;
+{
+ if (arg == START)
+ {
+ px_ref_flag++;
+ cm_xref (arg);
+ px_ref_flag--;
+ }
+ else
+ add_char ('.');
+}
+
+void
+cm_inforef (arg)
+ 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 */
+/* */
+/* **************************************************************** */
+
+void
+cm_quotation ()
+{
+ begin_insertion (quotation);
+}
+
+void
+cm_example ()
+{
+ begin_insertion (example);
+}
+
+void
+cm_smallexample ()
+{
+ begin_insertion (smallexample);
+}
+
+void
+cm_lisp ()
+{
+ begin_insertion (lisp);
+}
+
+void
+cm_smalllisp ()
+{
+ begin_insertion (smalllisp);
+}
+
+/* @cartouche/@end cartouche draws box with rounded corners in
+ TeX output. Right now, just a NOP insertion. */
+void
+cm_cartouche ()
+{
+ begin_insertion (cartouche);
+}
+
+void
+cm_format ()
+{
+ begin_insertion (format);
+}
+
+void
+cm_display ()
+{
+ begin_insertion (display);
+}
+
+void
+cm_itemize ()
+{
+ begin_insertion (itemize);
+}
+
+void
+cm_enumerate ()
+{
+ 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. */
+void
+do_enumeration (type, default_string)
+ 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);
+}
+
+void
+cm_table ()
+{
+ begin_insertion (table);
+}
+
+void
+cm_ftable ()
+{
+ begin_insertion (ftable);
+}
+
+void
+cm_vtable ()
+{
+ begin_insertion (vtable);
+}
+
+void
+cm_group ()
+{
+ begin_insertion (group);
+}
+
+void
+cm_ifinfo ()
+{
+ begin_insertion (ifinfo);
+}
+
+/* Begin an insertion where the lines are not filled or indented. */
+void
+cm_flushleft ()
+{
+ 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. */
+void
+cm_flushright ()
+{
+ 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. */
+void
+set (name, value)
+ 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. */
+void
+clear (name)
+ 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. */
+char *
+set_p (name)
+ 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. */
+void
+command_name_condition ()
+{
+ 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. */
+void
+cm_set ()
+{
+ handle_variable (SET);
+}
+
+/* Remove a variable whose name appears as the first word on this line. */
+void
+cm_clear ()
+{
+ handle_variable (CLEAR);
+}
+
+void
+cm_ifset ()
+{
+ handle_variable (IFSET);
+}
+
+void
+cm_ifclear ()
+{
+ 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. */
+void
+cm_ifeq ()
+{
+ register int i;
+ 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);
+ }
+}
+
+void
+cm_value (arg, start_pos, end_pos)
+ int arg, start_pos, 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. */
+void
+handle_variable (action)
+ int action;
+{
+ char *name;
+
+ get_rest_of_line (&name);
+ backup_input_pointer ();
+ canon_white (name);
+ handle_variable_internal (action, name);
+ free (name);
+}
+
+void
+handle_variable_internal (action, name)
+ 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 *temp;
+
+ while (*cname && whitespace (*cname))
+ cname++;
+ temp = cname;
+
+ while (*temp && !whitespace (*temp))
+ temp++;
+ *temp = '\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;
+
+EXECUTION_STRING *
+get_execution_string (initial_size)
+ 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. */
+#if defined (HAVE_VARARGS_H) && defined (HAVE_VSPRINTF)
+void
+execute_string (va_alist)
+ va_dcl
+{
+ EXECUTION_STRING *es;
+ char *temp_string;
+ char *format;
+ va_list args;
+
+ es = get_execution_string (4000);
+ temp_string = es->string;
+ es->in_use = 1;
+
+ va_start (args);
+ format = va_arg (args, char *);
+ vsprintf (temp_string, format, args);
+ va_end (args);
+
+#else /* !(HAVE_VARARGS_H && HAVE_VSPRINTF) */
+
+void
+execute_string (format, arg1, arg2, arg3, arg4, arg5)
+ char *format;
+{
+ EXECUTION_STRING *es;
+ char *temp_string;
+
+ es = get_execution_string (4000);
+ temp_string = es->string;
+ es->in_use = 1;
+
+ sprintf (temp_string, format, arg1, arg2, arg3, arg4, arg5);
+
+#endif /* !(HAVE_VARARGS_H && HAVE_VSPRINTF) */
+
+ 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;
+
+void
+cm_itemx ()
+{
+ itemx_flag++;
+ cm_item ();
+ itemx_flag--;
+}
+
+void
+cm_item ()
+{
+ 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;
+ }
+ }
+ 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;
+};
+
+void
+initialize_token_accumulator (accumulator)
+ struct token_accumulator *accumulator;
+{
+ (accumulator->length) = 0;
+ (accumulator->index) = 0;
+ (accumulator->tokens) = NULL;
+}
+
+void
+accumulate_token (accumulator, 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;
+}
+
+char *
+copy_substring (start, end)
+ 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. */
+int
+scan_group_in_string (string_pointer)
+ 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. */
+char **
+args_from_string (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);
+}
+
+void
+process_defun_args (defun_args, auto_var_p)
+ 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);
+ }
+}
+
+char *
+next_nonwhite_defun_arg (arg_pointer)
+ 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. */
+void
+defun_internal (type, x_p)
+ 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;
+ }
+ 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;
+ }
+ 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;
+ }
+
+ /* 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. */
+void
+cm_defun ()
+{
+ 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. */
+void
+cm_end ()
+{
+ char *temp;
+ enum insertion_type type;
+
+ if (!insertion_level)
+ {
+ line_error ("Unmatched `%c%s'", COMMAND_PREFIX, command);
+ return;
+ }
+
+ 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 ()));
+ }
+ end_insertion (type);
+ free (temp);
+}
+
+
+/* **************************************************************** */
+/* */
+/* Other Random Commands */
+/* */
+/* **************************************************************** */
+
+/* This says to inhibit the indentation of the next paragraph, but
+ not of following paragraphs. */
+void
+cm_noindent ()
+{
+ 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. */
+void
+cm_setfilename ()
+{
+ char *filename;
+ get_rest_of_line (&filename);
+ /* warning ("`@%s %s' encountered and ignored", command, filename); */
+ free (filename);
+}
+
+void
+cm_ignore_line ()
+{
+ discard_until ("\n");
+}
+
+/* @br can be immediately followed by `{}', so we have to read those here.
+ It should simply close the paragraph. */
+void
+cm_br ()
+{
+ 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. */
+void
+cm_sp ()
+{
+ 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. */
+void
+cm_center ()
+{
+ 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. */
+void
+cm_result (arg)
+ int arg;
+{
+ if (arg == END)
+ add_word ("=>");
+}
+
+/* What an expression expands to. */
+void
+cm_expansion (arg)
+ int arg;
+{
+ if (arg == END)
+ add_word ("==>");
+}
+
+/* Indicates two expressions are equivalent. */
+void
+cm_equiv (arg)
+ int arg;
+{
+ if (arg == END)
+ add_word ("==");
+}
+
+/* What an expression may print. */
+void
+cm_print (arg)
+ int arg;
+{
+ if (arg == END)
+ add_word ("-|");
+}
+
+/* An error signaled. */
+void
+cm_error (arg)
+ int arg;
+{
+ if (arg == END)
+ add_word ("error-->");
+}
+
+/* The location of point in an example of a buffer. */
+void
+cm_point (arg)
+ 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. */
+void
+cm_exdent ()
+{
+ 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 ();
+}
+
+void
+cm_include ()
+{
+ 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. */
+void
+cm_infoinclude ()
+{
+ 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))
+ {
+ extern int errno;
+
+ 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. */
+void
+misplaced_brace ()
+{
+ line_error ("Misplaced `}'");
+}
+
+/* Don't let the filling algorithm insert extra whitespace here. */
+void
+cm_force_abbreviated_whitespace ()
+{
+}
+
+/* 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. */
+void
+cm_ignore_sentence_ender ()
+{
+ add_char (META ((*command)));
+}
+
+/* Signals end of processing. Easy to make this happen. */
+void
+cm_bye ()
+{
+ input_text_offset = size_of_input_text;
+}
+
+void
+cm_asis ()
+{
+}
+
+void
+cm_math ()
+{
+}
+
+
+/* **************************************************************** */
+/* */
+/* 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
+
+void
+init_indices ()
+{
+ 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. */
+int
+find_index_offset (name)
+ 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. */
+INDEX_ALIST *
+find_index (name)
+ 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. */
+int
+translate_index (name)
+ char *name;
+{
+ INDEX_ALIST *which = find_index (name);
+
+ if (which)
+ return (which->index);
+ else
+ return (-1);
+}
+
+/* Return the index list which belongs to NAME. */
+INDEX_ELT *
+index_list (name)
+ 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... */
+void
+free_index (index)
+ INDEX_ELT *index;
+{
+ INDEX_ELT *temp;
+
+ while ((temp = index) != (INDEX_ELT *) NULL)
+ {
+ free (temp->entry);
+ free (temp->node);
+ index = index->next;
+ free (temp);
+ }
+}
+
+/* Flush an index by name. */
+void
+undefindex (name)
+ 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. */
+void
+defindex (name, code)
+ 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. */
+void
+index_add_arg (name)
+ 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. */
+void
+gen_index ()
+{
+ char *name = strdup (command);
+ if (strlen (name) >= strlen ("index"))
+ name[strlen (name) - strlen ("index")] = '\0';
+ index_add_arg (name);
+ free (name);
+}
+
+void
+top_defindex (name, code)
+ 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. */
+void
+cm_defindex ()
+{
+ gen_defindex (0);
+}
+
+void
+cm_defcodeindex ()
+{
+ gen_defindex (1);
+}
+
+void
+gen_defindex (code)
+ 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. */
+INDEX_ELT *
+index_append (head, tail)
+ INDEX_ELT *head, *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. */
+void
+cm_synindex ()
+{
+ 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;
+ }
+}
+
+void
+cm_pindex () /* Pinhead index. */
+{
+ index_add_arg ("pg");
+}
+
+void
+cm_vindex () /* Variable index. */
+{
+ index_add_arg ("vr");
+}
+
+void
+cm_kindex () /* Key index. */
+{
+ index_add_arg ("ky");
+}
+
+void
+cm_cindex () /* Concept index. */
+{
+ index_add_arg ("cp");
+}
+
+void
+cm_findex () /* Function index. */
+{
+ index_add_arg ("fn");
+}
+
+void
+cm_tindex () /* Data Type index. */
+{
+ index_add_arg ("tp");
+}
+
+/* Sorting the index. */
+int
+index_element_compare (element1, element2)
+ INDEX_ELT **element1, **element2;
+{
+ /* This needs to ignore leading non-text characters. */
+ return (strcasecmp ((*element1)->entry, (*element2)->entry));
+}
+
+/* Force all index entries to be unique. */
+void
+make_index_entries_unique (array, count)
+ 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. */
+INDEX_ELT **
+sort_index (index)
+ INDEX_ELT *index;
+{
+ INDEX_ELT *temp = index;
+ 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 = index;
+
+ 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 *), 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. */
+void
+cm_printindex ()
+{
+ int item;
+ INDEX_ELT *index;
+ 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);
+
+ index = index_list (index_name);
+ if (index == (INDEX_ELT *)-1)
+ {
+ line_error ("Unknown index name `%s'", index_name);
+ free (index_name);
+ return;
+ }
+ else
+ free (index_name);
+
+ array = sort_index (index);
+
+ 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; (index = 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 = index->defining_line;
+
+ /* If this particular entry should be printed as a "code" index,
+ then wrap the entry with "@code{...}". */
+ if (index->code)
+ execute_string ("* %ccode{%s}: ", COMMAND_PREFIX, index->entry);
+ else
+ execute_string ("* %s: ", index->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", index->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 */
+/* */
+/* **************************************************************** */
+
+void
+define_user_command (name, proc, needs_braces_p)
+ 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;
+}
+
+/* Make ALIAS run the named FUNCTION. Copies properties from FUNCTION. */
+void
+define_alias (alias, function)
+ char *alias, *function;
+{
+}
+
+/* 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. */
+int
+set_paragraph_indent (string)
+ 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);
+}
+
+void
+cm_paragraphindent ()
+{
+ 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. */
+int
+set_footnote_style (string)
+ 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);
+}
+
+void
+cm_footnotestyle ()
+{
+ 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. */
+void
+remember_note (marker, note)
+ char *marker, *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. */
+void
+free_pending_notes ()
+{
+ 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. */
+void
+cm_footnote ()
+{
+ 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. */
+void
+output_pending_notes ()
+{
+ 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. */
+MACRO_DEF *
+find_macro (name)
+ 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 (name, arglist, body, source_file, source_lineno, flags)
+ char *name;
+ char **arglist;
+ char *body;
+ char *source_file;
+ int source_lineno, 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. */
+MACRO_DEF *
+delete_macro (name)
+ 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;
+
+char **
+get_macro_args (def)
+ 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\. */
+char *
+apply (named, actuals, body)
+ char **named, **actuals, *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 ((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. */
+void
+execute_macro (def)
+ MACRO_DEF *def;
+{
+ register int i;
+ 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. */
+void
+cm_macro ()
+{
+ 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 ((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);
+}
+
+void
+cm_unmacro ()
+{
+ 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 i;
+
+ for (i = 0; def->arglist[i]; i++)
+ free (def->arglist[i]);
+
+ 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. */
+ITEXT *
+remember_itext (pointer, offset)
+ 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. */
+void
+forget_itext (pointer)
+ 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. */
+void
+me_append_before_this_command ()
+{
+ 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. */
+void
+me_execute_string (execution_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. */
+void
+append_to_expansion_output (offset)
+ 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. */
+void
+maybe_write_itext (pointer, offset)
+ 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);
+ }
+}
+
+void
+write_region_to_macro_output (string, start, end)
+ char *string;
+ int start, 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. */
+int
+array_len (array)
+ char **array;
+{
+ register int i = 0;
+
+ if (array)
+ for (i = 0; array[i] != (char *)NULL; i++);
+
+ return (i);
+}
+
+void
+free_array (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. */
+char **
+get_brace_args (quote_single)
+ 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. */
+char *
+extract_colon_unit (string, index)
+ char *string;
+ int *index;
+{
+ int i, start;
+
+ i = *index;
+
+ if (!string || (i >= 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++;
+
+ *index = i;
+
+ if (i == start)
+ {
+ if (string[i])
+ (*index)++;
+
+ /* 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. */
+char *
+get_file_info_in_path (filename, path, finfo)
+ char *filename, *path;
+ struct stat *finfo;
+{
+ char *dir;
+ int result, index = 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, &index))
+ {
+ 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);
+}
diff --git a/gnu/usr.bin/texinfo/makeinfo/makeinfo.texi b/gnu/usr.bin/texinfo/makeinfo/makeinfo.texi
new file mode 100644
index 00000000000..0299fea7398
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/makeinfo.texi
@@ -0,0 +1,295 @@
+\input texinfo @c -*-texinfo-*-
+@comment %**start of header
+@setfilename makeinfo.info
+@set VERSION 1.61
+@paragraphindent none
+@comment %**start of header
+
+@ifinfo
+This file is an extract from the @cite{Texinfo} manual.@*
+It documents @code{makeinfo}, a program that converts Texinfo
+files into Info files.
+
+Copyright (C) 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+@ignore
+Permission is granted to process this file through TeX and print the
+results, provided the printed document carries copying permission
+notice identical to this one except for the removal of this paragraph
+(this paragraph not being relevant to the printed manual).
+
+@end ignore
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the entire
+resulting derived work is distributed under the terms of a permission
+notice identical to this one.
+
+Permission is granted to copy and distribute translations of this manual
+into another language, under the above conditions for modified versions,
+except that this permission notice may be stated in a translation approved
+by the Free Software Foundation.
+@end ifinfo
+
+@titlepage
+@title GNU Makeinfo
+@author Brian J. Fox and Robert J. Chassell
+
+@page
+@vskip 0pt plus 1filll
+Copyright @copyright{} 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the entire
+resulting derived work is distributed under the terms of a permission
+notice identical to this one.
+
+Permission is granted to copy and distribute translations of this manual
+into another language, under the above conditions for modified versions,
+except that this permission notice may be stated in a translation approved
+by the Free Software Foundation.
+@end titlepage
+
+@node Top, ,(dir), (dir)
+@chapter What is @code{makeinfo}?
+
+@iftex
+This file documents the use of the @code{makeinfo} program, versions
+@value{VERSION} and later. It is an extract from the @cite{TeXinfo} manual.
+@end iftex
+
+@code{makeinfo} is a program for converting @dfn{TeXinfo} files into @dfn{Info}
+files. TeXinfo is a documentation system that uses a single source file to
+produce both on-line information and printed output.
+
+You can read the on-line information using Info; type @code{info} to
+learn about Info.
+@ifinfo
+@xref{Top, Texinfo, Overview of Texinfo, texinfo, Texinfo},
+@end ifinfo
+@iftex
+See the @cite{TeXinfo} manual,
+@end iftex
+to learn about the TeXinfo documentation system.
+
+@menu
+* Formatting Control:: Controlling the width of lines, paragraph
+ indentation, and other similar formatting.
+
+* Options:: Command line options which control the
+ behaviour of Makeinfo.
+
+* Pointer Validation:: How Makeinfo can help you to track node
+ references through complex Texinfo files.
+
+* The Macro Facility:: Makeinfo allows the use of @dfn{macros}.
+
+* Index:: Index of Concepts.
+@end menu
+
+@node Formatting Control
+@section Controlling Paragraph Formats
+
+Without any special options, @code{makeinfo} @dfn{fills} the paragraphs that
+it outputs to an Info file. Filling is the process of breaking and connecting
+lines so that lines are the same length as or shorter than the number
+specified as the fill column. Lines are broken between words. With
+@code{makeinfo}, you can control:
+
+@itemize @bullet
+@item
+The width of each paragraph (the @dfn{fill-column}).
+@item
+The amount of indentation that the first line of
+each paragraph receives (the @dfn{paragraph-indentation}).
+@end itemize
+
+@node Options
+@section Command Line Options
+
+The following command line options are available for @code{makeinfo}.
+
+@need 100
+@table @code
+@item -D @var{var}
+Cause @var{var} to be defined. This is equivalent to
+@code{@@set @var{var}} in the Texinfo file.
+
+@need 150
+@item --error-limit @var{limit}
+Set the maximum number of errors that @code{makeinfo} will report
+before exiting (on the assumption that continuing would be useless).
+The default number of errors that can be reported before
+@code{makeinfo} gives up is 100.@refill
+
+@need 150
+@item --fill-column @var{width}
+Specify the maximum number of columns in a line; this is the right-hand
+edge of a line. Paragraphs that are filled will be filled to this
+width. The default value for @code{fill-column} is 72.
+@refill
+
+@item --footnote-style @var{style}
+Set the footnote style to @var{style}, either @samp{end} for the end
+node style or @samp{separate} for the separate node style. The value
+set by this option overrides the value set in a Texinfo file by an
+@code{@@footnotestyle} command. When the footnote style is
+@samp{separate}, @code{makeinfo} makes a new node containing the
+footnotes found in the current node. When the footnote style is
+@samp{end}, @code{makeinfo} places the footnote references at the end
+of the current node.@refill
+
+@need 150
+@item -I @var{dir}
+Add @code{dir} to the directory search list for finding files that are
+included using the @code{@@include} command. By default,
+@code{makeinfo} searches only the current directory.
+
+@need 150
+@item --no-headers
+Do not include menus or node lines in the output. This results in an
+@sc{ascii} file that you cannot read in Info since it does not contain
+the requisite nodes or menus; but you can print such a file in a
+single, typewriter-like font and produce acceptable output.
+
+@need 150
+@item --no-split
+Suppress the splitting stage of @code{makeinfo}. Normally, large
+output files (where the size is greater than 70k bytes) are split into
+smaller subfiles, each one approximately 50k bytes. If you specify
+@samp{--no-split}, @code{makeinfo} will not split up the output
+file.@refill
+
+@need 100
+@item --no-pointer-validate
+@item --no-validate
+Suppress the pointer-validation phase of @code{makeinfo}. Normally,
+after a Texinfo file is processed, some consistency checks are made to
+ensure that cross references can be resolved, etc.
+@xref{Pointer Validation}.@refill
+
+@need 150
+@item --no-warn
+Suppress the output of warning messages. This does @emph{not}
+suppress the output of error messages, only warnings. You might
+want this if the file you are creating has examples of Texinfo cross
+references within it, and the nodes that are referenced do not actually
+exist.@refill
+
+@item --no-number-footnotes
+Supress automatic footnote numbering. By default, @code{makeinfo}
+numbers each footnote sequentially in a single node, resetting the
+current footnote number to 1 at the start of each node.
+
+@need 150
+@item --output @var{file}
+@itemx -o @var{file}
+Specify that the output should be directed to @var{file} and not to the
+file name specified in the @code{@@setfilename} command found in the Texinfo
+source. @var{file} can be the special token @samp{-}, which specifies
+standard output.
+
+@need 150
+@item --paragraph-indent @var{indent}
+Set the paragraph indentation style to @var{indent}. The value set by
+this option overrides the value set in a Texinfo file by an
+@code{@@paragraphindent} command. The value of @var{indent} is
+interpreted as follows:@refill
+
+@itemize @bullet
+@item
+If the value of @var{indent} is @samp{asis}, do not change the
+existing indentation at the starts of paragraphs.@refill
+
+@item
+If the value of @var{indent} is zero, delete any existing
+indentation.@refill
+
+@item
+If the value of @var{indent} is greater than zero, indent each
+paragraph by that number of spaces.@refill
+@end itemize
+
+@need 100
+@item --reference-limit @var{limit}
+Set the value of the number of references to a node that
+@code{makeinfo} will make without reporting a warning. If a node has more
+than this number of references in it, @code{makeinfo} will make the
+references but also report a warning.@refill
+
+@need 150
+@item -U @var{var}
+Cause @var{var} to be undefined. This is equivalent to
+@code{@@clear @var{var}} in the Texinfo file.
+
+@need 100
+@item --verbose
+Cause @code{makeinfo} to display messages saying what it is doing.
+Normally, @code{makeinfo} only outputs messages if there are errors or
+warnings.@refill
+
+@need 100
+@item --version
+Report the version number of this copy of @code{makeinfo}.@refill
+
+@item --help
+Show a summary of the commend line arguments to @code{makeinfo}.
+@end table
+
+@node Pointer Validation
+@section Pointer Validation
+@cindex Pointer validation with @code{makeinfo}
+@cindex Validation of pointers
+
+If you do not suppress pointer-validation (by using the
+@samp{--no-pointer-validation} option), @code{makeinfo}
+will check the validity of the final Info file. Mostly,
+this means ensuring that nodes you have referenced
+really exist. Here is a complete list of what is
+checked:@refill
+
+@enumerate
+@item
+If a `Next', `Previous', or `Up' node reference is a reference to a
+node in the current file and is not an external reference such as to
+@file{(dir)}, then the referenced node must exist.@refill
+
+@item
+In every node, if the `Previous' node is different from the `Up' node,
+then the `Previous' node must also be pointed to by a `Next' node.@refill
+
+@item
+Every node except the `Top' node must have an `Up' pointer.@refill
+
+@item
+The node referenced by an `Up' pointer must contain a reference to the
+current node in some manner other than through a `Next' reference.
+This includes menu entries and cross references.@refill
+
+@item
+If the `Next' reference of a node is not the same as the `Next' reference
+of the `Up' reference, then the node referenced by the `Next' pointer
+must have a `Previous' pointer that points back to the current node.
+This rule allows the last node in a section to point to the first node
+of the next chapter.@refill
+@end enumerate
+
+@lowersections
+@include macro.texi
+@raisesections
+
+@lowersections
+@node Index
+@appendix Index
+@printindex cp
+@raisesections
+
+@contents
+@bye
diff --git a/gnu/usr.bin/texinfo/makeinfo/multiformat.texi b/gnu/usr.bin/texinfo/makeinfo/multiformat.texi
new file mode 100644
index 00000000000..0c6c467dc0d
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/multiformat.texi
@@ -0,0 +1,40 @@
+@c multiformat.texi: -*- Texinfo -*- \input texinfo.tex
+
+@c Copyright (c) 1995 Universal Access, Inc
+@c Author: Brian J. Fox (bfox@ua.com) Sun Apr 2 07:56:23 1995.
+@setfilename multiformat.info
+
+@include html.texi
+
+@ifset html
+@html
+@end ifset
+
+@node First Node, First Section, (dir), (dir)
+@chapter First Chapter
+Here is some text that belongs in the first chapter. Nothing very
+exciting happens here, but this is enough text to span a couple of
+lines, and we feel that is important.
+@paragraph
+
+This is the second paragraph of the first chapter. Note that the
+formatting commands in @code{HTML} seem to do the right thing, as do the
+commands when invoked in @code{Texinfo} mode and in @TeX{}.
+
+@node First Section, , First Node, First Node
+@isection First Section
+
+Here is some text in the first section of the first chapter. We are
+trying very hard to examine the output here to see exactly how proper it
+is. If I wasn't so tired, we could probably see it already.
+@paragraph
+
+Here is a list of items:
+@paragraph
+
+@itemize @bullet
+@item Here is the first item.
+@item Here is the second item.
+@end itemize
+
+@bye