summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGreg McGary <greg@mcgary.org>1997-04-18 06:43:32 +0000
committerGreg McGary <greg@mcgary.org>1997-04-18 06:43:32 +0000
commit916418ea1284e6aa64f50eba077e48ced5944acc (patch)
tree8e519e3a48ae80c8c17da3ba5acccd91530b22ff
parenta560adff07afe7c1f35e4585e0694e89c842b245 (diff)
downloadidutils-916418ea1284e6aa64f50eba077e48ced5944acc.tar.gz
idutils-916418ea1284e6aa64f50eba077e48ced5944acc.tar.bz2
idutils-916418ea1284e6aa64f50eba077e48ced5944acc.zip
Initial revision
-rw-r--r--doc/texinfo.tex4558
-rw-r--r--lib/memcpy.c16
-rw-r--r--lib/memset.c29
-rw-r--r--lib/strcspn.c43
-rw-r--r--lib/strpbrk.c39
-rw-r--r--lib/strspn.c44
-rw-r--r--lib/strstr.c116
-rw-r--r--lib/xalloca.h47
-rw-r--r--lib/xdirent.h53
-rw-r--r--lib/xfnmatch.h34
-rw-r--r--lib/xobstack.h29
-rw-r--r--lib/xstddef.h32
-rw-r--r--lib/xstdlib.h28
-rw-r--r--lib/xstring.h44
-rw-r--r--lib/xsysstat.h70
-rw-r--r--lib/xunistd.h44
-rw-r--r--libidu/Makefile.am22
-rw-r--r--libidu/Makefile.in320
-rw-r--r--libidu/dynvec.c59
-rw-r--r--libidu/dynvec.h35
-rw-r--r--libidu/fnprint.c138
-rw-r--r--libidu/hash.c294
-rw-r--r--libidu/hash.h144
-rw-r--r--libidu/id-lang.map88
-rw-r--r--libidu/idfile.c137
-rw-r--r--libidu/idfile.h223
-rw-r--r--libidu/idread.c280
-rw-r--r--libidu/idwrite.c169
-rw-r--r--libidu/scanners.c1212
-rw-r--r--libidu/scanners.h70
-rw-r--r--libidu/tokflags.h35
-rw-r--r--libidu/walker.c1130
-rw-r--r--libidu/xnls.h41
-rw-r--r--lisp/Makefile.am10
-rw-r--r--lisp/Makefile.in160
-rwxr-xr-xlisp/elisp-comp42
-rw-r--r--lisp/id-utils.el63
-rw-r--r--src/fnid.c192
-rw-r--r--src/xtokid.c214
-rw-r--r--testsuite/Makefile.am8
-rw-r--r--testsuite/Makefile.in159
-rwxr-xr-xtestsuite/consistency63
42 files changed, 10534 insertions, 0 deletions
diff --git a/doc/texinfo.tex b/doc/texinfo.tex
new file mode 100644
index 0000000..7489021
--- /dev/null
+++ b/doc/texinfo.tex
@@ -0,0 +1,4558 @@
+%% TeX macros to handle texinfo files
+
+% Copyright (C) 1985, 86, 88, 90, 91, 92, 93,
+% 94, 95, 1996 Free Software Foundation, Inc.
+
+%This texinfo.tex file 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 texinfo.tex file 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 texinfo.tex file; see the file COPYING. If not, write
+%to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+%Boston, MA 02111-1307, USA.
+
+
+%In other words, you are welcome to use, share and improve this program.
+%You are forbidden to forbid anyone else to use, share and improve
+%what you give them. Help stamp out software-hoarding!
+
+
+% Send bug reports to bug-texinfo@prep.ai.mit.edu.
+% Please include a *precise* test case in each bug report.
+
+
+% Make it possible to create a .fmt file just by loading this file:
+% if the underlying format is not loaded, start by loading it now.
+% Added by gildea November 1993.
+\expandafter\ifx\csname fmtname\endcsname\relax\input plain\fi
+
+% This automatically updates the version number based on RCS.
+\def\deftexinfoversion$#1: #2 ${\def\texinfoversion{#2}}
+\deftexinfoversion$Revision$
+\message{Loading texinfo package [Version \texinfoversion]:}
+
+% If in a .fmt file, print the version number
+% and turn on active characters that we couldn't do earlier because
+% they might have appeared in the input file name.
+\everyjob{\message{[Texinfo version \texinfoversion]}\message{}
+ \catcode`+=\active \catcode`\_=\active}
+
+% Save some parts of plain tex whose names we will redefine.
+
+\let\ptextilde=\~
+\let\ptexlbrace=\{
+\let\ptexrbrace=\}
+\let\ptexdots=\dots
+\let\ptexdot=\.
+\let\ptexstar=\*
+\let\ptexend=\end
+\let\ptexbullet=\bullet
+\let\ptexb=\b
+\let\ptexc=\c
+\let\ptexi=\i
+\let\ptext=\t
+\let\ptexl=\l
+\let\ptexL=\L
+
+% Be sure we're in horizontal mode when doing a tie, since we make space
+% equivalent to this in @example-like environments. Otherwise, a space
+% at the beginning of a line will start with \penalty -- and
+% since \penalty is valid in vertical mode, we'd end up putting the
+% penalty on the vertical list instead of in the new paragraph.
+{\catcode`@ = 11
+ % Avoid using \@M directly, because that causes trouble
+ % if the definition is written into an index file.
+ \global\let\tiepenalty = \@M
+ \gdef\tie{\leavevmode\penalty\tiepenalty\ }
+}
+\let\~ = \tie % And make it available as @~.
+
+\message{Basics,}
+\chardef\other=12
+
+% If this character appears in an error message or help string, it
+% starts a new line in the output.
+\newlinechar = `^^J
+
+% Set up fixed words for English.
+\ifx\putwordChapter\undefined{\gdef\putwordChapter{Chapter}}\fi%
+\def\putwordInfo{Info}%
+\ifx\putwordSee\undefined{\gdef\putwordSee{See}}\fi%
+\ifx\putwordsee\undefined{\gdef\putwordsee{see}}\fi%
+\ifx\putwordfile\undefined{\gdef\putwordfile{file}}\fi%
+\ifx\putwordpage\undefined{\gdef\putwordpage{page}}\fi%
+\ifx\putwordsection\undefined{\gdef\putwordsection{section}}\fi%
+\ifx\putwordSection\undefined{\gdef\putwordSection{Section}}\fi%
+\ifx\putwordTableofContents\undefined{\gdef\putwordTableofContents{Table of Contents}}\fi%
+\ifx\putwordShortContents\undefined{\gdef\putwordShortContents{Short Contents}}\fi%
+\ifx\putwordAppendix\undefined{\gdef\putwordAppendix{Appendix}}\fi%
+
+% Ignore a token.
+%
+\def\gobble#1{}
+
+\hyphenation{ap-pen-dix}
+\hyphenation{mini-buf-fer mini-buf-fers}
+\hyphenation{eshell}
+
+% Margin to add to right of even pages, to left of odd pages.
+\newdimen \bindingoffset \bindingoffset=0pt
+\newdimen \normaloffset \normaloffset=\hoffset
+\newdimen\pagewidth \newdimen\pageheight
+\pagewidth=\hsize \pageheight=\vsize
+
+% Sometimes it is convenient to have everything in the transcript file
+% and nothing on the terminal. We don't just call \tracingall here,
+% since that produces some useless output on the terminal.
+%
+\def\gloggingall{\begingroup \globaldefs = 1 \loggingall \endgroup}%
+\def\loggingall{\tracingcommands2 \tracingstats2
+ \tracingpages1 \tracingoutput1 \tracinglostchars1
+ \tracingmacros2 \tracingparagraphs1 \tracingrestores1
+ \showboxbreadth\maxdimen\showboxdepth\maxdimen
+}%
+
+%---------------------Begin change-----------------------
+%
+%%%% For @cropmarks command.
+% Dimensions to add cropmarks at corners Added by P. A. MacKay, 12 Nov. 1986
+%
+\newdimen\cornerlong \newdimen\cornerthick
+\newdimen \topandbottommargin
+\newdimen \outerhsize \newdimen \outervsize
+\cornerlong=1pc\cornerthick=.3pt % These set size of cropmarks
+\outerhsize=7in
+%\outervsize=9.5in
+% Alternative @smallbook page size is 9.25in
+\outervsize=9.25in
+\topandbottommargin=.75in
+%
+%---------------------End change-----------------------
+
+% \onepageout takes a vbox as an argument. Note that \pagecontents
+% does insertions itself, but you have to call it yourself.
+\chardef\PAGE=255 \output={\onepageout{\pagecontents\PAGE}}
+\def\onepageout#1{\hoffset=\normaloffset
+\ifodd\pageno \advance\hoffset by \bindingoffset
+\else \advance\hoffset by -\bindingoffset\fi
+{\escapechar=`\\\relax % makes sure backslash is used in output files.
+\shipout\vbox{{\let\hsize=\pagewidth \makeheadline} \pagebody{#1}%
+{\let\hsize=\pagewidth \makefootline}}}%
+\advancepageno \ifnum\outputpenalty>-20000 \else\dosupereject\fi}
+
+%%%% For @cropmarks command %%%%
+
+% Here is a modification of the main output routine for Near East Publications
+% This provides right-angle cropmarks at all four corners.
+% The contents of the page are centerlined into the cropmarks,
+% and any desired binding offset is added as an \hskip on either
+% site of the centerlined box. (P. A. MacKay, 12 November, 1986)
+%
+\def\croppageout#1{\hoffset=0pt % make sure this doesn't mess things up
+{\escapechar=`\\\relax % makes sure backslash is used in output files.
+ \shipout
+ \vbox to \outervsize{\hsize=\outerhsize
+ \vbox{\line{\ewtop\hfill\ewtop}}
+ \nointerlineskip
+ \line{\vbox{\moveleft\cornerthick\nstop}
+ \hfill
+ \vbox{\moveright\cornerthick\nstop}}
+ \vskip \topandbottommargin
+ \centerline{\ifodd\pageno\hskip\bindingoffset\fi
+ \vbox{
+ {\let\hsize=\pagewidth \makeheadline}
+ \pagebody{#1}
+ {\let\hsize=\pagewidth \makefootline}}
+ \ifodd\pageno\else\hskip\bindingoffset\fi}
+ \vskip \topandbottommargin plus1fill minus1fill
+ \boxmaxdepth\cornerthick
+ \line{\vbox{\moveleft\cornerthick\nsbot}
+ \hfill
+ \vbox{\moveright\cornerthick\nsbot}}
+ \nointerlineskip
+ \vbox{\line{\ewbot\hfill\ewbot}}
+ }}
+ \advancepageno
+ \ifnum\outputpenalty>-20000 \else\dosupereject\fi}
+%
+% Do @cropmarks to get crop marks
+\def\cropmarks{\let\onepageout=\croppageout }
+
+\newinsert\margin \dimen\margin=\maxdimen
+
+\def\pagebody#1{\vbox to\pageheight{\boxmaxdepth=\maxdepth #1}}
+{\catcode`\@ =11
+\gdef\pagecontents#1{\ifvoid\topins\else\unvbox\topins\fi
+% marginal hacks, juha@viisa.uucp (Juha Takala)
+\ifvoid\margin\else % marginal info is present
+ \rlap{\kern\hsize\vbox to\z@{\kern1pt\box\margin \vss}}\fi
+\dimen@=\dp#1 \unvbox#1
+\ifvoid\footins\else\vskip\skip\footins\footnoterule \unvbox\footins\fi
+\ifr@ggedbottom \kern-\dimen@ \vfil \fi}
+}
+
+%
+% Here are the rules for the cropmarks. Note that they are
+% offset so that the space between them is truly \outerhsize or \outervsize
+% (P. A. MacKay, 12 November, 1986)
+%
+\def\ewtop{\vrule height\cornerthick depth0pt width\cornerlong}
+\def\nstop{\vbox
+ {\hrule height\cornerthick depth\cornerlong width\cornerthick}}
+\def\ewbot{\vrule height0pt depth\cornerthick width\cornerlong}
+\def\nsbot{\vbox
+ {\hrule height\cornerlong depth\cornerthick width\cornerthick}}
+
+% Parse an argument, then pass it to #1. The argument is the rest of
+% the input line (except we remove a trailing comment). #1 should be a
+% macro which expects an ordinary undelimited TeX argument.
+%
+\def\parsearg#1{%
+ \let\next = #1%
+ \begingroup
+ \obeylines
+ \futurelet\temp\parseargx
+}
+
+% If the next token is an obeyed space (from an @example environment or
+% the like), remove it and recurse. Otherwise, we're done.
+\def\parseargx{%
+ % \obeyedspace is defined far below, after the definition of \sepspaces.
+ \ifx\obeyedspace\temp
+ \expandafter\parseargdiscardspace
+ \else
+ \expandafter\parseargline
+ \fi
+}
+
+% Remove a single space (as the delimiter token to the macro call).
+{\obeyspaces %
+ \gdef\parseargdiscardspace {\futurelet\temp\parseargx}}
+
+{\obeylines %
+ \gdef\parseargline#1^^M{%
+ \endgroup % End of the group started in \parsearg.
+ %
+ % First remove any @c comment, then any @comment.
+ % Result of each macro is put in \toks0.
+ \argremovec #1\c\relax %
+ \expandafter\argremovecomment \the\toks0 \comment\relax %
+ %
+ % Call the caller's macro, saved as \next in \parsearg.
+ \expandafter\next\expandafter{\the\toks0}%
+ }%
+}
+
+% Since all \c{,omment} does is throw away the argument, we can let TeX
+% do that for us. The \relax here is matched by the \relax in the call
+% in \parseargline; it could be more or less anything, its purpose is
+% just to delimit the argument to the \c.
+\def\argremovec#1\c#2\relax{\toks0 = {#1}}
+\def\argremovecomment#1\comment#2\relax{\toks0 = {#1}}
+
+% \argremovec{,omment} might leave us with trailing spaces, though; e.g.,
+% @end itemize @c foo
+% will have two active spaces as part of the argument with the
+% `itemize'. Here we remove all active spaces from #1, and assign the
+% result to \toks0.
+%
+% This loses if there are any *other* active characters besides spaces
+% in the argument -- _ ^ +, for example -- since they get expanded.
+% Fortunately, Texinfo does not define any such commands. (If it ever
+% does, the catcode of the characters in questionwill have to be changed
+% here.) But this means we cannot call \removeactivespaces as part of
+% \argremovec{,omment}, since @c uses \parsearg, and thus the argument
+% that \parsearg gets might well have any character at all in it.
+%
+\def\removeactivespaces#1{%
+ \begingroup
+ \ignoreactivespaces
+ \edef\temp{#1}%
+ \global\toks0 = \expandafter{\temp}%
+ \endgroup
+}
+
+% Change the active space to expand to nothing.
+%
+\begingroup
+ \obeyspaces
+ \gdef\ignoreactivespaces{\obeyspaces\let =\empty}
+\endgroup
+
+
+\def\flushcr{\ifx\par\lisppar \def\next##1{}\else \let\next=\relax \fi \next}
+
+%% These are used to keep @begin/@end levels from running away
+%% Call \inENV within environments (after a \begingroup)
+\newif\ifENV \ENVfalse \def\inENV{\ifENV\relax\else\ENVtrue\fi}
+\def\ENVcheck{%
+\ifENV\errmessage{Still within an environment. Type Return to continue.}
+\endgroup\fi} % This is not perfect, but it should reduce lossage
+
+% @begin foo is the same as @foo, for now.
+\newhelp\EMsimple{Type <Return> to continue.}
+
+\outer\def\begin{\parsearg\beginxxx}
+
+\def\beginxxx #1{%
+\expandafter\ifx\csname #1\endcsname\relax
+{\errhelp=\EMsimple \errmessage{Undefined command @begin #1}}\else
+\csname #1\endcsname\fi}
+
+% @end foo executes the definition of \Efoo.
+%
+\def\end{\parsearg\endxxx}
+\def\endxxx #1{%
+ \removeactivespaces{#1}%
+ \edef\endthing{\the\toks0}%
+ %
+ \expandafter\ifx\csname E\endthing\endcsname\relax
+ \expandafter\ifx\csname \endthing\endcsname\relax
+ % There's no \foo, i.e., no ``environment'' foo.
+ \errhelp = \EMsimple
+ \errmessage{Undefined command `@end \endthing'}%
+ \else
+ \unmatchedenderror\endthing
+ \fi
+ \else
+ % Everything's ok; the right environment has been started.
+ \csname E\endthing\endcsname
+ \fi
+}
+
+% There is an environment #1, but it hasn't been started. Give an error.
+%
+\def\unmatchedenderror#1{%
+ \errhelp = \EMsimple
+ \errmessage{This `@end #1' doesn't have a matching `@#1'}%
+}
+
+% Define the control sequence \E#1 to give an unmatched @end error.
+%
+\def\defineunmatchedend#1{%
+ \expandafter\def\csname E#1\endcsname{\unmatchedenderror{#1}}%
+}
+
+
+% Single-spacing is done by various environments (specifically, in
+% \nonfillstart and \quotations).
+\newskip\singlespaceskip \singlespaceskip = 12.5pt
+\def\singlespace{%
+ % Why was this kern here? It messes up equalizing space above and below
+ % environments. --karl, 6may93
+ %{\advance \baselineskip by -\singlespaceskip
+ %\kern \baselineskip}%
+ \setleading \singlespaceskip
+}
+
+%% Simple single-character @ commands
+
+% @@ prints an @
+% Kludge this until the fonts are right (grr).
+\def\@{{\tt \char '100}}
+
+% This is turned off because it was never documented
+% and you can use @w{...} around a quote to suppress ligatures.
+%% Define @` and @' to be the same as ` and '
+%% but suppressing ligatures.
+%\def\`{{`}}
+%\def\'{{'}}
+
+% Used to generate quoted braces.
+
+\def\mylbrace {{\tt \char '173}}
+\def\myrbrace {{\tt \char '175}}
+\let\{=\mylbrace
+\let\}=\myrbrace
+
+% @: forces normal size whitespace following.
+\def\:{\spacefactor=1000 }
+
+% @* forces a line break.
+\def\*{\hfil\break\hbox{}\ignorespaces}
+
+% @. is an end-of-sentence period.
+\def\.{.\spacefactor=3000 }
+
+% @enddots{} is an end-of-sentence ellipsis.
+\gdef\enddots{$\mathinner{\ldotp\ldotp\ldotp\ldotp}$\spacefactor=3000}
+
+% @! is an end-of-sentence bang.
+\gdef\!{!\spacefactor=3000 }
+
+% @? is an end-of-sentence query.
+\gdef\?{?\spacefactor=3000 }
+
+% @w prevents a word break. Without the \leavevmode, @w at the
+% beginning of a paragraph, when TeX is still in vertical mode, would
+% produce a whole line of output instead of starting the paragraph.
+\def\w#1{\leavevmode\hbox{#1}}
+
+% @group ... @end group forces ... to be all on one page, by enclosing
+% it in a TeX vbox. We use \vtop instead of \vbox to construct the box
+% to keep its height that of a normal line. According to the rules for
+% \topskip (p.114 of the TeXbook), the glue inserted is
+% max (\topskip - \ht (first item), 0). If that height is large,
+% therefore, no glue is inserted, and the space between the headline and
+% the text is small, which looks bad.
+%
+\def\group{\begingroup
+ \ifnum\catcode13=\active \else
+ \errhelp = \groupinvalidhelp
+ \errmessage{@group invalid in context where filling is enabled}%
+ \fi
+ %
+ % The \vtop we start below produces a box with normal height and large
+ % depth; thus, TeX puts \baselineskip glue before it, and (when the
+ % next line of text is done) \lineskip glue after it. (See p.82 of
+ % the TeXbook.) Thus, space below is not quite equal to space
+ % above. But it's pretty close.
+ \def\Egroup{%
+ \egroup % End the \vtop.
+ \endgroup % End the \group.
+ }%
+ %
+ \vtop\bgroup
+ % We have to put a strut on the last line in case the @group is in
+ % the midst of an example, rather than completely enclosing it.
+ % Otherwise, the interline space between the last line of the group
+ % and the first line afterwards is too small. But we can't put the
+ % strut in \Egroup, since there it would be on a line by itself.
+ % Hence this just inserts a strut at the beginning of each line.
+ \everypar = {\strut}%
+ %
+ % Since we have a strut on every line, we don't need any of TeX's
+ % normal interline spacing.
+ \offinterlineskip
+ %
+ % OK, but now we have to do something about blank
+ % lines in the input in @example-like environments, which normally
+ % just turn into \lisppar, which will insert no space now that we've
+ % turned off the interline space. Simplest is to make them be an
+ % empty paragraph.
+ \ifx\par\lisppar
+ \edef\par{\leavevmode \par}%
+ %
+ % Reset ^^M's definition to new definition of \par.
+ \obeylines
+ \fi
+ %
+ % Do @comment since we are called inside an environment such as
+ % @example, where each end-of-line in the input causes an
+ % end-of-line in the output. We don't want the end-of-line after
+ % the `@group' to put extra space in the output. Since @group
+ % should appear on a line by itself (according to the Texinfo
+ % manual), we don't worry about eating any user text.
+ \comment
+}
+%
+% TeX puts in an \escapechar (i.e., `@') at the beginning of the help
+% message, so this ends up printing `@group can only ...'.
+%
+\newhelp\groupinvalidhelp{%
+group can only be used in environments such as @example,^^J%
+where each line of input produces a line of output.}
+
+% @need space-in-mils
+% forces a page break if there is not space-in-mils remaining.
+
+\newdimen\mil \mil=0.001in
+
+\def\need{\parsearg\needx}
+
+% Old definition--didn't work.
+%\def\needx #1{\par %
+%% This method tries to make TeX break the page naturally
+%% if the depth of the box does not fit.
+%{\baselineskip=0pt%
+%\vtop to #1\mil{\vfil}\kern -#1\mil\penalty 10000
+%\prevdepth=-1000pt
+%}}
+
+\def\needx#1{%
+ % Go into vertical mode, so we don't make a big box in the middle of a
+ % paragraph.
+ \par
+ %
+ % Don't add any leading before our big empty box, but allow a page
+ % break, since the best break might be right here.
+ \allowbreak
+ \nointerlineskip
+ \vtop to #1\mil{\vfil}%
+ %
+ % TeX does not even consider page breaks if a penalty added to the
+ % main vertical list is 10000 or more. But in order to see if the
+ % empty box we just added fits on the page, we must make it consider
+ % page breaks. On the other hand, we don't want to actually break the
+ % page after the empty box. So we use a penalty of 9999.
+ %
+ % There is an extremely small chance that TeX will actually break the
+ % page at this \penalty, if there are no other feasible breakpoints in
+ % sight. (If the user is using lots of big @group commands, which
+ % almost-but-not-quite fill up a page, TeX will have a hard time doing
+ % good page breaking, for example.) However, I could not construct an
+ % example where a page broke at this \penalty; if it happens in a real
+ % document, then we can reconsider our strategy.
+ \penalty9999
+ %
+ % Back up by the size of the box, whether we did a page break or not.
+ \kern -#1\mil
+ %
+ % Do not allow a page break right after this kern.
+ \nobreak
+}
+
+% @br forces paragraph break
+
+\let\br = \par
+
+% @dots{} output some dots
+
+\def\dots{$\ldots$}
+
+% @page forces the start of a new page
+
+\def\page{\par\vfill\supereject}
+
+% @exdent text....
+% outputs text on separate line in roman font, starting at standard page margin
+
+% This records the amount of indent in the innermost environment.
+% That's how much \exdent should take out.
+\newskip\exdentamount
+
+% This defn is used inside fill environments such as @defun.
+\def\exdent{\parsearg\exdentyyy}
+\def\exdentyyy #1{{\hfil\break\hbox{\kern -\exdentamount{\rm#1}}\hfil\break}}
+
+% This defn is used inside nofill environments such as @example.
+\def\nofillexdent{\parsearg\nofillexdentyyy}
+\def\nofillexdentyyy #1{{\advance \leftskip by -\exdentamount
+\leftline{\hskip\leftskip{\rm#1}}}}
+
+% @inmargin{TEXT} puts TEXT in the margin next to the current paragraph.
+
+\def\inmargin#1{%
+\strut\vadjust{\nobreak\kern-\strutdepth
+ \vtop to \strutdepth{\baselineskip\strutdepth\vss
+ \llap{\rightskip=\inmarginspacing \vbox{\noindent #1}}\null}}}
+\newskip\inmarginspacing \inmarginspacing=1cm
+\def\strutdepth{\dp\strutbox}
+
+%\hbox{{\rm#1}}\hfil\break}}
+
+% @include file insert text of that file as input.
+% Allow normal characters that we make active in the argument (a file name).
+\def\include{\begingroup
+ \catcode`\\=12
+ \catcode`~=12
+ \catcode`^=12
+ \catcode`_=12
+ \catcode`|=12
+ \catcode`<=12
+ \catcode`>=12
+ \catcode`+=12
+ \parsearg\includezzz}
+% Restore active chars for included file.
+\def\includezzz#1{\endgroup\begingroup
+ % Read the included file in a group so nested @include's work.
+ \def\thisfile{#1}%
+ \input\thisfile
+\endgroup}
+
+\def\thisfile{}
+
+% @center line outputs that line, centered
+
+\def\center{\parsearg\centerzzz}
+\def\centerzzz #1{{\advance\hsize by -\leftskip
+\advance\hsize by -\rightskip
+\centerline{#1}}}
+
+% @sp n outputs n lines of vertical space
+
+\def\sp{\parsearg\spxxx}
+\def\spxxx #1{\par \vskip #1\baselineskip}
+
+% @comment ...line which is ignored...
+% @c is the same as @comment
+% @ignore ... @end ignore is another way to write a comment
+
+\def\comment{\catcode 64=\other \catcode 123=\other \catcode 125=\other%
+\parsearg \commentxxx}
+
+\def\commentxxx #1{\catcode 64=0 \catcode 123=1 \catcode 125=2 }
+
+\let\c=\comment
+
+% Prevent errors for section commands.
+% Used in @ignore and in failing conditionals.
+\def\ignoresections{%
+\let\chapter=\relax
+\let\unnumbered=\relax
+\let\top=\relax
+\let\unnumberedsec=\relax
+\let\unnumberedsection=\relax
+\let\unnumberedsubsec=\relax
+\let\unnumberedsubsection=\relax
+\let\unnumberedsubsubsec=\relax
+\let\unnumberedsubsubsection=\relax
+\let\section=\relax
+\let\subsec=\relax
+\let\subsubsec=\relax
+\let\subsection=\relax
+\let\subsubsection=\relax
+\let\appendix=\relax
+\let\appendixsec=\relax
+\let\appendixsection=\relax
+\let\appendixsubsec=\relax
+\let\appendixsubsection=\relax
+\let\appendixsubsubsec=\relax
+\let\appendixsubsubsection=\relax
+\let\contents=\relax
+\let\smallbook=\relax
+\let\titlepage=\relax
+}
+
+% Used in nested conditionals, where we have to parse the Texinfo source
+% and so want to turn off most commands, in case they are used
+% incorrectly.
+%
+\def\ignoremorecommands{%
+ \let\defcv = \relax
+ \let\deffn = \relax
+ \let\deffnx = \relax
+ \let\defindex = \relax
+ \let\defivar = \relax
+ \let\defmac = \relax
+ \let\defmethod = \relax
+ \let\defop = \relax
+ \let\defopt = \relax
+ \let\defspec = \relax
+ \let\deftp = \relax
+ \let\deftypefn = \relax
+ \let\deftypefun = \relax
+ \let\deftypevar = \relax
+ \let\deftypevr = \relax
+ \let\defun = \relax
+ \let\defvar = \relax
+ \let\defvr = \relax
+ \let\ref = \relax
+ \let\xref = \relax
+ \let\printindex = \relax
+ \let\pxref = \relax
+ \let\settitle = \relax
+ \let\setchapternewpage = \relax
+ \let\setchapterstyle = \relax
+ \let\everyheading = \relax
+ \let\evenheading = \relax
+ \let\oddheading = \relax
+ \let\everyfooting = \relax
+ \let\evenfooting = \relax
+ \let\oddfooting = \relax
+ \let\headings = \relax
+ \let\include = \relax
+ \let\lowersections = \relax
+ \let\down = \relax
+ \let\raisesections = \relax
+ \let\up = \relax
+ \let\set = \relax
+ \let\clear = \relax
+ \let\item = \relax
+ \let\message = \relax
+}
+
+% Ignore @ignore ... @end ignore.
+%
+\def\ignore{\doignore{ignore}}
+
+% Also ignore @ifinfo, @ifhtml, @html, @menu, and @direntry text.
+%
+\def\ifinfo{\doignore{ifinfo}}
+\def\ifhtml{\doignore{ifhtml}}
+\def\html{\doignore{html}}
+\def\menu{\doignore{menu}}
+\def\direntry{\doignore{direntry}}
+
+% @dircategory CATEGORY -- specify a category of the dir file
+% which this file should belong to. Ignore this in TeX.
+
+\def\dircategory{\comment}
+
+% Ignore text until a line `@end #1'.
+%
+\def\doignore#1{\begingroup
+ % Don't complain about control sequences we have declared \outer.
+ \ignoresections
+ %
+ % Define a command to swallow text until we reach `@end #1'.
+ \long\def\doignoretext##1\end #1{\enddoignore}%
+ %
+ % Make sure that spaces turn into tokens that match what \doignoretext wants.
+ \catcode32 = 10
+ %
+ % And now expand that command.
+ \doignoretext
+}
+
+% What we do to finish off ignored text.
+%
+\def\enddoignore{\endgroup\ignorespaces}%
+
+\newif\ifwarnedobs\warnedobsfalse
+\def\obstexwarn{%
+ \ifwarnedobs\relax\else
+ % We need to warn folks that they may have trouble with TeX 3.0.
+ % This uses \immediate\write16 rather than \message to get newlines.
+ \immediate\write16{}
+ \immediate\write16{***WARNING*** for users of Unix TeX 3.0!}
+ \immediate\write16{This manual trips a bug in TeX version 3.0 (tex hangs).}
+ \immediate\write16{If you are running another version of TeX, relax.}
+ \immediate\write16{If you are running Unix TeX 3.0, kill this TeX process.}
+ \immediate\write16{ Then upgrade your TeX installation if you can.}
+ \immediate\write16{If you are stuck with version 3.0, run the}
+ \immediate\write16{ script ``tex3patch'' from the Texinfo distribution}
+ \immediate\write16{ to use a workaround.}
+ \immediate\write16{}
+ \global\warnedobstrue
+ \fi
+}
+
+% **In TeX 3.0, setting text in \nullfont hangs tex. For a
+% workaround (which requires the file ``dummy.tfm'' to be installed),
+% uncomment the following line:
+%%%%%\font\nullfont=dummy\let\obstexwarn=\relax
+
+% Ignore text, except that we keep track of conditional commands for
+% purposes of nesting, up to an `@end #1' command.
+%
+\def\nestedignore#1{%
+ \obstexwarn
+ % We must actually expand the ignored text to look for the @end
+ % command, so that nested ignore constructs work. Thus, we put the
+ % text into a \vbox and then do nothing with the result. To minimize
+ % the change of memory overflow, we follow the approach outlined on
+ % page 401 of the TeXbook: make the current font be a dummy font.
+ %
+ \setbox0 = \vbox\bgroup
+ % Don't complain about control sequences we have declared \outer.
+ \ignoresections
+ %
+ % Define `@end #1' to end the box, which will in turn undefine the
+ % @end command again.
+ \expandafter\def\csname E#1\endcsname{\egroup\ignorespaces}%
+ %
+ % We are going to be parsing Texinfo commands. Most cause no
+ % trouble when they are used incorrectly, but some commands do
+ % complicated argument parsing or otherwise get confused, so we
+ % undefine them.
+ %
+ % We can't do anything about stray @-signs, unfortunately;
+ % they'll produce `undefined control sequence' errors.
+ \ignoremorecommands
+ %
+ % Set the current font to be \nullfont, a TeX primitive, and define
+ % all the font commands to also use \nullfont. We don't use
+ % dummy.tfm, as suggested in the TeXbook, because not all sites
+ % might have that installed. Therefore, math mode will still
+ % produce output, but that should be an extremely small amount of
+ % stuff compared to the main input.
+ %
+ \nullfont
+ \let\tenrm = \nullfont \let\tenit = \nullfont \let\tensl = \nullfont
+ \let\tenbf = \nullfont \let\tentt = \nullfont \let\smallcaps = \nullfont
+ \let\tensf = \nullfont
+ % Similarly for index fonts (mostly for their use in
+ % smallexample)
+ \let\indrm = \nullfont \let\indit = \nullfont \let\indsl = \nullfont
+ \let\indbf = \nullfont \let\indtt = \nullfont \let\indsc = \nullfont
+ \let\indsf = \nullfont
+ %
+ % Don't complain when characters are missing from the fonts.
+ \tracinglostchars = 0
+ %
+ % Don't bother to do space factor calculations.
+ \frenchspacing
+ %
+ % Don't report underfull hboxes.
+ \hbadness = 10000
+ %
+ % Do minimal line-breaking.
+ \pretolerance = 10000
+ %
+ % Do not execute instructions in @tex
+ \def\tex{\doignore{tex}}
+}
+
+% @set VAR sets the variable VAR to an empty value.
+% @set VAR REST-OF-LINE sets VAR to the value REST-OF-LINE.
+%
+% Since we want to separate VAR from REST-OF-LINE (which might be
+% empty), we can't just use \parsearg; we have to insert a space of our
+% own to delimit the rest of the line, and then take it out again if we
+% didn't need it. Make sure the catcode of space is correct to avoid
+% losing inside @example, for instance.
+%
+\def\set{\begingroup\catcode` =10 \parsearg\setxxx}
+\def\setxxx#1{\setyyy#1 \endsetyyy}
+\def\setyyy#1 #2\endsetyyy{%
+ \def\temp{#2}%
+ \ifx\temp\empty \global\expandafter\let\csname SET#1\endcsname = \empty
+ \else \setzzz{#1}#2\endsetzzz % Remove the trailing space \setxxx inserted.
+ \fi
+ \endgroup
+}
+% Can't use \xdef to pre-expand #2 and save some time, since \temp or
+% \next or other control sequences that we've defined might get us into
+% an infinite loop. Consider `@set foo @cite{bar}'.
+\def\setzzz#1#2 \endsetzzz{\expandafter\gdef\csname SET#1\endcsname{#2}}
+
+% @clear VAR clears (i.e., unsets) the variable VAR.
+%
+\def\clear{\parsearg\clearxxx}
+\def\clearxxx#1{\global\expandafter\let\csname SET#1\endcsname=\relax}
+
+% @value{foo} gets the text saved in variable foo.
+%
+\def\value#1{\expandafter
+ \ifx\csname SET#1\endcsname\relax
+ {\{No value for ``#1''\}}
+ \else \csname SET#1\endcsname \fi}
+
+% @ifset VAR ... @end ifset reads the `...' iff VAR has been defined
+% with @set.
+%
+\def\ifset{\parsearg\ifsetxxx}
+\def\ifsetxxx #1{%
+ \expandafter\ifx\csname SET#1\endcsname\relax
+ \expandafter\ifsetfail
+ \else
+ \expandafter\ifsetsucceed
+ \fi
+}
+\def\ifsetsucceed{\conditionalsucceed{ifset}}
+\def\ifsetfail{\nestedignore{ifset}}
+\defineunmatchedend{ifset}
+
+% @ifclear VAR ... @end ifclear reads the `...' iff VAR has never been
+% defined with @set, or has been undefined with @clear.
+%
+\def\ifclear{\parsearg\ifclearxxx}
+\def\ifclearxxx #1{%
+ \expandafter\ifx\csname SET#1\endcsname\relax
+ \expandafter\ifclearsucceed
+ \else
+ \expandafter\ifclearfail
+ \fi
+}
+\def\ifclearsucceed{\conditionalsucceed{ifclear}}
+\def\ifclearfail{\nestedignore{ifclear}}
+\defineunmatchedend{ifclear}
+
+% @iftex always succeeds; we read the text following, through @end
+% iftex). But `@end iftex' should be valid only after an @iftex.
+%
+\def\iftex{\conditionalsucceed{iftex}}
+\defineunmatchedend{iftex}
+
+% We can't just want to start a group at @iftex (for example) and end it
+% at @end iftex, since then @set commands inside the conditional have no
+% effect (they'd get reverted at the end of the group). So we must
+% define \Eiftex to redefine itself to be its previous value. (We can't
+% just define it to fail again with an ``unmatched end'' error, since
+% the @ifset might be nested.)
+%
+\def\conditionalsucceed#1{%
+ \edef\temp{%
+ % Remember the current value of \E#1.
+ \let\nece{prevE#1} = \nece{E#1}%
+ %
+ % At the `@end #1', redefine \E#1 to be its previous value.
+ \def\nece{E#1}{\let\nece{E#1} = \nece{prevE#1}}%
+ }%
+ \temp
+}
+
+% We need to expand lots of \csname's, but we don't want to expand the
+% control sequences after we've constructed them.
+%
+\def\nece#1{\expandafter\noexpand\csname#1\endcsname}
+
+% @asis just yields its argument. Used with @table, for example.
+%
+\def\asis#1{#1}
+
+% @math means output in math mode.
+% We don't use $'s directly in the definition of \math because control
+% sequences like \math are expanded when the toc file is written. Then,
+% we read the toc file back, the $'s will be normal characters (as they
+% should be, according to the definition of Texinfo). So we must use a
+% control sequence to switch into and out of math mode.
+%
+% This isn't quite enough for @math to work properly in indices, but it
+% seems unlikely it will ever be needed there.
+%
+\let\implicitmath = $
+\def\math#1{\implicitmath #1\implicitmath}
+
+% @bullet and @minus need the same treatment as @math, just above.
+\def\bullet{\implicitmath\ptexbullet\implicitmath}
+\def\minus{\implicitmath-\implicitmath}
+
+\def\node{\ENVcheck\parsearg\nodezzz}
+\def\nodezzz#1{\nodexxx [#1,]}
+\def\nodexxx[#1,#2]{\gdef\lastnode{#1}}
+\let\nwnode=\node
+\let\lastnode=\relax
+
+\def\donoderef{\ifx\lastnode\relax\else
+\expandafter\expandafter\expandafter\setref{\lastnode}\fi
+\global\let\lastnode=\relax}
+
+\def\unnumbnoderef{\ifx\lastnode\relax\else
+\expandafter\expandafter\expandafter\unnumbsetref{\lastnode}\fi
+\global\let\lastnode=\relax}
+
+\def\appendixnoderef{\ifx\lastnode\relax\else
+\expandafter\expandafter\expandafter\appendixsetref{\lastnode}\fi
+\global\let\lastnode=\relax}
+
+\let\refill=\relax
+
+% @setfilename is done at the beginning of every texinfo file.
+% So open here the files we need to have open while reading the input.
+% This makes it possible to make a .fmt file for texinfo.
+\def\setfilename{%
+ \readauxfile
+ \opencontents
+ \openindices
+ \fixbackslash % Turn off hack to swallow `\input texinfo'.
+ \global\let\setfilename=\comment % Ignore extra @setfilename cmds.
+ \comment % Ignore the actual filename.
+}
+
+\outer\def\bye{\pagealignmacro\tracingstats=1\ptexend}
+
+\def\inforef #1{\inforefzzz #1,,,,**}
+\def\inforefzzz #1,#2,#3,#4**{\putwordSee{} \putwordInfo{} \putwordfile{} \file{\ignorespaces #3{}},
+ node \samp{\ignorespaces#1{}}}
+
+\def\macro#1{\begingroup\ignoresections\catcode`\#=6\def\macrotemp{#1}\parsearg\macroxxx}
+\def\macroxxx#1#2 \end macro{%
+\expandafter\gdef\macrotemp#1{#2}%
+\endgroup}
+
+%\def\linemacro#1{\begingroup\ignoresections\catcode`\#=6\def\macrotemp{#1}\parsearg\linemacroxxx}
+%\def\linemacroxxx#1#2 \end linemacro{%
+%\let\parsearg=\relax
+%\edef\macrotempx{\csname M\butfirst\expandafter\string\macrotemp\endcsname}%
+%\expandafter\xdef\macrotemp{\parsearg\macrotempx}%
+%\expandafter\gdef\macrotempx#1{#2}%
+%\endgroup}
+
+%\def\butfirst#1{}
+
+\message{fonts,}
+
+% Font-change commands.
+
+% Texinfo supports the sans serif font style, which plain TeX does not.
+% So we set up a \sf analogous to plain's \rm, etc.
+\newfam\sffam
+\def\sf{\fam=\sffam \tensf}
+\let\li = \sf % Sometimes we call it \li, not \sf.
+
+% We don't need math for this one.
+\def\ttsl{\tenttsl}
+
+%% Try out Computer Modern fonts at \magstephalf
+\let\mainmagstep=\magstephalf
+
+% Set the font macro #1 to the font named #2, adding on the
+% specified font prefix (normally `cm').
+% #3 is the font's design size, #4 is a scale factor
+\def\setfont#1#2#3#4{\font#1=\fontprefix#2#3 scaled #4}
+
+% Use cm as the default font prefix.
+% To specify the font prefix, you must define \fontprefix
+% before you read in texinfo.tex.
+\ifx\fontprefix\undefined
+\def\fontprefix{cm}
+\fi
+% Support font families that don't use the same naming scheme as CM.
+\def\rmshape{r}
+\def\rmbshape{bx} %where the normal face is bold
+\def\bfshape{b}
+\def\bxshape{bx}
+\def\ttshape{tt}
+\def\ttbshape{tt}
+\def\ttslshape{sltt}
+\def\itshape{ti}
+\def\itbshape{bxti}
+\def\slshape{sl}
+\def\slbshape{bxsl}
+\def\sfshape{ss}
+\def\sfbshape{ss}
+\def\scshape{csc}
+\def\scbshape{csc}
+
+\ifx\bigger\relax
+\let\mainmagstep=\magstep1
+\setfont\textrm\rmshape{12}{1000}
+\setfont\texttt\ttshape{12}{1000}
+\else
+\setfont\textrm\rmshape{10}{\mainmagstep}
+\setfont\texttt\ttshape{10}{\mainmagstep}
+\fi
+% Instead of cmb10, you many want to use cmbx10.
+% cmbx10 is a prettier font on its own, but cmb10
+% looks better when embedded in a line with cmr10.
+\setfont\textbf\bfshape{10}{\mainmagstep}
+\setfont\textit\itshape{10}{\mainmagstep}
+\setfont\textsl\slshape{10}{\mainmagstep}
+\setfont\textsf\sfshape{10}{\mainmagstep}
+\setfont\textsc\scshape{10}{\mainmagstep}
+\setfont\textttsl\ttslshape{10}{\mainmagstep}
+\font\texti=cmmi10 scaled \mainmagstep
+\font\textsy=cmsy10 scaled \mainmagstep
+
+% A few fonts for @defun, etc.
+\setfont\defbf\bxshape{10}{\magstep1} %was 1314
+\setfont\deftt\ttshape{10}{\magstep1}
+\def\df{\let\tentt=\deftt \let\tenbf = \defbf \bf}
+
+% Fonts for indices and small examples.
+% We actually use the slanted font rather than the italic,
+% because texinfo normally uses the slanted fonts for that.
+% Do not make many font distinctions in general in the index, since they
+% aren't very useful.
+\setfont\ninett\ttshape{9}{1000}
+\setfont\indrm\rmshape{9}{1000}
+\setfont\indit\slshape{9}{1000}
+\let\indsl=\indit
+\let\indtt=\ninett
+\let\indttsl=\ninett
+\let\indsf=\indrm
+\let\indbf=\indrm
+\setfont\indsc\scshape{10}{900}
+\font\indi=cmmi9
+\font\indsy=cmsy9
+
+% Fonts for headings
+\setfont\chaprm\rmbshape{12}{\magstep2}
+\setfont\chapit\itbshape{10}{\magstep3}
+\setfont\chapsl\slbshape{10}{\magstep3}
+\setfont\chaptt\ttbshape{12}{\magstep2}
+\setfont\chapttsl\ttslshape{10}{\magstep3}
+\setfont\chapsf\sfbshape{12}{\magstep2}
+\let\chapbf=\chaprm
+\setfont\chapsc\scbshape{10}{\magstep3}
+\font\chapi=cmmi12 scaled \magstep2
+\font\chapsy=cmsy10 scaled \magstep3
+
+\setfont\secrm\rmbshape{12}{\magstep1}
+\setfont\secit\itbshape{10}{\magstep2}
+\setfont\secsl\slbshape{10}{\magstep2}
+\setfont\sectt\ttbshape{12}{\magstep1}
+\setfont\secttsl\ttslshape{10}{\magstep2}
+\setfont\secsf\sfbshape{12}{\magstep1}
+\let\secbf\secrm
+\setfont\secsc\scbshape{10}{\magstep2}
+\font\seci=cmmi12 scaled \magstep1
+\font\secsy=cmsy10 scaled \magstep2
+
+% \setfont\ssecrm\bxshape{10}{\magstep1} % This size an font looked bad.
+% \setfont\ssecit\itshape{10}{\magstep1} % The letters were too crowded.
+% \setfont\ssecsl\slshape{10}{\magstep1}
+% \setfont\ssectt\ttshape{10}{\magstep1}
+% \setfont\ssecsf\sfshape{10}{\magstep1}
+
+%\setfont\ssecrm\bfshape{10}{1315} % Note the use of cmb rather than cmbx.
+%\setfont\ssecit\itshape{10}{1315} % Also, the size is a little larger than
+%\setfont\ssecsl\slshape{10}{1315} % being scaled magstep1.
+%\setfont\ssectt\ttshape{10}{1315}
+%\setfont\ssecsf\sfshape{10}{1315}
+
+%\let\ssecbf=\ssecrm
+
+\setfont\ssecrm\rmbshape{12}{\magstephalf}
+\setfont\ssecit\itbshape{10}{1315}
+\setfont\ssecsl\slbshape{10}{1315}
+\setfont\ssectt\ttbshape{12}{\magstephalf}
+\setfont\ssecttsl\ttslshape{10}{\magstep1}
+\setfont\ssecsf\sfbshape{12}{\magstephalf}
+\let\ssecbf\ssecrm
+\setfont\ssecsc\scbshape{10}{\magstep1}
+\font\sseci=cmmi12 scaled \magstephalf
+\font\ssecsy=cmsy10 scaled \magstep1
+% The smallcaps and symbol fonts should actually be scaled \magstep1.5,
+% but that is not a standard magnification.
+
+% Fonts for title page:
+\setfont\titlerm\rmbshape{12}{\magstep3}
+\let\authorrm = \secrm
+
+% In order for the font changes to affect most math symbols and letters,
+% we have to define the \textfont of the standard families. Since
+% texinfo doesn't allow for producing subscripts and superscripts, we
+% don't bother to reset \scriptfont and \scriptscriptfont (which would
+% also require loading a lot more fonts).
+%
+\def\resetmathfonts{%
+ \textfont0 = \tenrm \textfont1 = \teni \textfont2 = \tensy
+ \textfont\itfam = \tenit \textfont\slfam = \tensl \textfont\bffam = \tenbf
+ \textfont\ttfam = \tentt \textfont\sffam = \tensf
+}
+
+
+% The font-changing commands redefine the meanings of \tenSTYLE, instead
+% of just \STYLE. We do this so that font changes will continue to work
+% in math mode, where it is the current \fam that is relevant in most
+% cases, not the current font. Plain TeX does \def\bf{\fam=\bffam
+% \tenbf}, for example. By redefining \tenbf, we obviate the need to
+% redefine \bf itself.
+\def\textfonts{%
+ \let\tenrm=\textrm \let\tenit=\textit \let\tensl=\textsl
+ \let\tenbf=\textbf \let\tentt=\texttt \let\smallcaps=\textsc
+ \let\tensf=\textsf \let\teni=\texti \let\tensy=\textsy \let\tenttsl=\textttsl
+ \resetmathfonts}
+\def\chapfonts{%
+ \let\tenrm=\chaprm \let\tenit=\chapit \let\tensl=\chapsl
+ \let\tenbf=\chapbf \let\tentt=\chaptt \let\smallcaps=\chapsc
+ \let\tensf=\chapsf \let\teni=\chapi \let\tensy=\chapsy \let\tenttsl=\chapttsl
+ \resetmathfonts}
+\def\secfonts{%
+ \let\tenrm=\secrm \let\tenit=\secit \let\tensl=\secsl
+ \let\tenbf=\secbf \let\tentt=\sectt \let\smallcaps=\secsc
+ \let\tensf=\secsf \let\teni=\seci \let\tensy=\secsy \let\tenttsl=\secttsl
+ \resetmathfonts}
+\def\subsecfonts{%
+ \let\tenrm=\ssecrm \let\tenit=\ssecit \let\tensl=\ssecsl
+ \let\tenbf=\ssecbf \let\tentt=\ssectt \let\smallcaps=\ssecsc
+ \let\tensf=\ssecsf \let\teni=\sseci \let\tensy=\ssecsy \let\tenttsl=\ssecttsl
+ \resetmathfonts}
+\def\indexfonts{%
+ \let\tenrm=\indrm \let\tenit=\indit \let\tensl=\indsl
+ \let\tenbf=\indbf \let\tentt=\indtt \let\smallcaps=\indsc
+ \let\tensf=\indsf \let\teni=\indi \let\tensy=\indsy \let\tenttsl=\indttsl
+ \resetmathfonts}
+
+% Set up the default fonts, so we can use them for creating boxes.
+%
+\textfonts
+
+% Count depth in font-changes, for error checks
+\newcount\fontdepth \fontdepth=0
+
+% Fonts for short table of contents.
+\setfont\shortcontrm\rmshape{12}{1000}
+\setfont\shortcontbf\bxshape{12}{1000}
+\setfont\shortcontsl\slshape{12}{1000}
+
+%% Add scribe-like font environments, plus @l for inline lisp (usually sans
+%% serif) and @ii for TeX italic
+
+% \smartitalic{ARG} outputs arg in italics, followed by an italic correction
+% unless the following character is such as not to need one.
+\def\smartitalicx{\ifx\next,\else\ifx\next-\else\ifx\next.\else\/\fi\fi\fi}
+\def\smartitalic#1{{\sl #1}\futurelet\next\smartitalicx}
+
+\let\i=\smartitalic
+\let\var=\smartitalic
+\let\dfn=\smartitalic
+\let\emph=\smartitalic
+\let\cite=\smartitalic
+
+\def\b#1{{\bf #1}}
+\let\strong=\b
+
+% We can't just use \exhyphenpenalty, because that only has effect at
+% the end of a paragraph. Restore normal hyphenation at the end of the
+% group within which \nohyphenation is presumably called.
+%
+\def\nohyphenation{\hyphenchar\font = -1 \aftergroup\restorehyphenation}
+\def\restorehyphenation{\hyphenchar\font = `- }
+
+\def\t#1{%
+ {\tt \rawbackslash \frenchspacing #1}%
+ \null
+}
+\let\ttfont=\t
+\def\samp #1{`\tclose{#1}'\null}
+\def\key #1{{\ttsl \nohyphenation \uppercase{#1}}\null}
+\def\ctrl #1{{\tt \rawbackslash \hat}#1}
+
+\let\file=\samp
+\let\url=\samp % perhaps include a hypertex \special eventually
+
+% @code is a modification of @t,
+% which makes spaces the same size as normal in the surrounding text.
+\def\tclose#1{%
+ {%
+ % Change normal interword space to be same as for the current font.
+ \spaceskip = \fontdimen2\font
+ %
+ % Switch to typewriter.
+ \tt
+ %
+ % But `\ ' produces the large typewriter interword space.
+ \def\ {{\spaceskip = 0pt{} }}%
+ %
+ % Turn off hyphenation.
+ \nohyphenation
+ %
+ \rawbackslash
+ \frenchspacing
+ #1%
+ }%
+ \null
+}
+
+% We *must* turn on hyphenation at `-' and `_' in \code.
+% Otherwise, it is too hard to avoid overfull hboxes
+% in the Emacs manual, the Library manual, etc.
+
+% Unfortunately, TeX uses one parameter (\hyphenchar) to control
+% both hyphenation at - and hyphenation within words.
+% We must therefore turn them both off (\tclose does that)
+% and arrange explicitly to hyphenate an a dash.
+% -- rms.
+{
+\catcode`\-=\active
+\catcode`\_=\active
+\global\def\code{\begingroup \catcode`\-=\active \let-\codedash \catcode`\_=\active \let_\codeunder \codex}
+% The following is used by \doprintindex to insure that long function names
+% wrap around. It is necessary for - and _ to be active before the index is
+% read from the file, as \entry parses the arguments long before \code is
+% ever called. -- mycroft
+\global\def\indexbreaks{\catcode`\-=\active \let-\realdash \catcode`\_=\active \let_\realunder}
+}
+
+\def\realdash{-}
+\def\realunder{_}
+\def\codedash{-\discretionary{}{}{}}
+\def\codeunder{\normalunderscore\discretionary{}{}{}}
+\def\codex #1{\tclose{#1}\endgroup}
+
+%\let\exp=\tclose %Was temporary
+
+% @kbd is like @code, except that if the argument is just one @key command,
+% then @kbd has no effect.
+%
+\def\xkey{\key}
+\def\kbdfoo#1#2#3\par{\def\one{#1}\def\three{#3}\def\threex{??}%
+\ifx\one\xkey\ifx\threex\three \key{#2}%
+\else{\tclose{\ttsl\look}}\fi
+\else{\tclose{\ttsl\look}}\fi}
+
+% Check if we are currently using a typewriter font. Since all the
+% Computer Modern typewriter fonts have zero interword stretch (and
+% shrink), and it is reasonable to expect all typewriter fonts to have
+% this property, we can check that font parameter.
+%
+\def\ifmonospace{\ifdim\fontdimen3\font=0pt }
+
+% Typeset a dimension, e.g., `in' or `pt'. The only reason for the
+% argument is to make the input look right: @dmn{pt} instead of
+% @dmn{}pt.
+%
+\def\dmn#1{\thinspace #1}
+
+\def\kbd#1{\def\look{#1}\expandafter\kbdfoo\look??\par}
+
+\def\l#1{{\li #1}\null} %
+
+\def\r#1{{\rm #1}} % roman font
+% Use of \lowercase was suggested.
+\def\sc#1{{\smallcaps#1}} % smallcaps font
+\def\ii#1{{\it #1}} % italic font
+
+\message{page headings,}
+
+\newskip\titlepagetopglue \titlepagetopglue = 1.5in
+\newskip\titlepagebottomglue \titlepagebottomglue = 2pc
+
+% First the title page. Must do @settitle before @titlepage.
+\def\titlefont#1{{\titlerm #1}}
+
+\newif\ifseenauthor
+\newif\iffinishedtitlepage
+
+\def\shorttitlepage{\parsearg\shorttitlepagezzz}
+\def\shorttitlepagezzz #1{\begingroup\hbox{}\vskip 1.5in \chaprm \centerline{#1}%
+ \endgroup\page\hbox{}\page}
+
+\def\titlepage{\begingroup \parindent=0pt \textfonts
+ \let\subtitlerm=\tenrm
+% I deinstalled the following change because \cmr12 is undefined.
+% This change was not in the ChangeLog anyway. --rms.
+% \let\subtitlerm=\cmr12
+ \def\subtitlefont{\subtitlerm \normalbaselineskip = 13pt \normalbaselines}%
+ %
+ \def\authorfont{\authorrm \normalbaselineskip = 16pt \normalbaselines}%
+ %
+ % Leave some space at the very top of the page.
+ \vglue\titlepagetopglue
+ %
+ % Now you can print the title using @title.
+ \def\title{\parsearg\titlezzz}%
+ \def\titlezzz##1{\leftline{\titlefont{##1}}
+ % print a rule at the page bottom also.
+ \finishedtitlepagefalse
+ \vskip4pt \hrule height 4pt width \hsize \vskip4pt}%
+ % No rule at page bottom unless we print one at the top with @title.
+ \finishedtitlepagetrue
+ %
+ % Now you can put text using @subtitle.
+ \def\subtitle{\parsearg\subtitlezzz}%
+ \def\subtitlezzz##1{{\subtitlefont \rightline{##1}}}%
+ %
+ % @author should come last, but may come many times.
+ \def\author{\parsearg\authorzzz}%
+ \def\authorzzz##1{\ifseenauthor\else\vskip 0pt plus 1filll\seenauthortrue\fi
+ {\authorfont \leftline{##1}}}%
+ %
+ % Most title ``pages'' are actually two pages long, with space
+ % at the top of the second. We don't want the ragged left on the second.
+ \let\oldpage = \page
+ \def\page{%
+ \iffinishedtitlepage\else
+ \finishtitlepage
+ \fi
+ \oldpage
+ \let\page = \oldpage
+ \hbox{}}%
+% \def\page{\oldpage \hbox{}}
+}
+
+\def\Etitlepage{%
+ \iffinishedtitlepage\else
+ \finishtitlepage
+ \fi
+ % It is important to do the page break before ending the group,
+ % because the headline and footline are only empty inside the group.
+ % If we use the new definition of \page, we always get a blank page
+ % after the title page, which we certainly don't want.
+ \oldpage
+ \endgroup
+ \HEADINGSon
+}
+
+\def\finishtitlepage{%
+ \vskip4pt \hrule height 2pt width \hsize
+ \vskip\titlepagebottomglue
+ \finishedtitlepagetrue
+}
+
+%%% Set up page headings and footings.
+
+\let\thispage=\folio
+
+\newtoks \evenheadline % Token sequence for heading line of even pages
+\newtoks \oddheadline % Token sequence for heading line of odd pages
+\newtoks \evenfootline % Token sequence for footing line of even pages
+\newtoks \oddfootline % Token sequence for footing line of odd pages
+
+% Now make Tex use those variables
+\headline={{\textfonts\rm \ifodd\pageno \the\oddheadline
+ \else \the\evenheadline \fi}}
+\footline={{\textfonts\rm \ifodd\pageno \the\oddfootline
+ \else \the\evenfootline \fi}\HEADINGShook}
+\let\HEADINGShook=\relax
+
+% Commands to set those variables.
+% For example, this is what @headings on does
+% @evenheading @thistitle|@thispage|@thischapter
+% @oddheading @thischapter|@thispage|@thistitle
+% @evenfooting @thisfile||
+% @oddfooting ||@thisfile
+
+\def\evenheading{\parsearg\evenheadingxxx}
+\def\oddheading{\parsearg\oddheadingxxx}
+\def\everyheading{\parsearg\everyheadingxxx}
+
+\def\evenfooting{\parsearg\evenfootingxxx}
+\def\oddfooting{\parsearg\oddfootingxxx}
+\def\everyfooting{\parsearg\everyfootingxxx}
+
+{\catcode`\@=0 %
+
+\gdef\evenheadingxxx #1{\evenheadingyyy #1@|@|@|@|\finish}
+\gdef\evenheadingyyy #1@|#2@|#3@|#4\finish{%
+\global\evenheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+
+\gdef\oddheadingxxx #1{\oddheadingyyy #1@|@|@|@|\finish}
+\gdef\oddheadingyyy #1@|#2@|#3@|#4\finish{%
+\global\oddheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+
+\gdef\everyheadingxxx #1{\everyheadingyyy #1@|@|@|@|\finish}
+\gdef\everyheadingyyy #1@|#2@|#3@|#4\finish{%
+\global\evenheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}
+\global\oddheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+
+\gdef\evenfootingxxx #1{\evenfootingyyy #1@|@|@|@|\finish}
+\gdef\evenfootingyyy #1@|#2@|#3@|#4\finish{%
+\global\evenfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+
+\gdef\oddfootingxxx #1{\oddfootingyyy #1@|@|@|@|\finish}
+\gdef\oddfootingyyy #1@|#2@|#3@|#4\finish{%
+\global\oddfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+
+\gdef\everyfootingxxx #1{\everyfootingyyy #1@|@|@|@|\finish}
+\gdef\everyfootingyyy #1@|#2@|#3@|#4\finish{%
+\global\evenfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}
+\global\oddfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+%
+}% unbind the catcode of @.
+
+% @headings double turns headings on for double-sided printing.
+% @headings single turns headings on for single-sided printing.
+% @headings off turns them off.
+% @headings on same as @headings double, retained for compatibility.
+% @headings after turns on double-sided headings after this page.
+% @headings doubleafter turns on double-sided headings after this page.
+% @headings singleafter turns on single-sided headings after this page.
+% By default, they are off.
+
+\def\headings #1 {\csname HEADINGS#1\endcsname}
+
+\def\HEADINGSoff{
+\global\evenheadline={\hfil} \global\evenfootline={\hfil}
+\global\oddheadline={\hfil} \global\oddfootline={\hfil}}
+\HEADINGSoff
+% When we turn headings on, set the page number to 1.
+% For double-sided printing, put current file name in lower left corner,
+% chapter name on inside top of right hand pages, document
+% title on inside top of left hand pages, and page numbers on outside top
+% edge of all pages.
+\def\HEADINGSdouble{
+%\pagealignmacro
+\global\pageno=1
+\global\evenfootline={\hfil}
+\global\oddfootline={\hfil}
+\global\evenheadline={\line{\folio\hfil\thistitle}}
+\global\oddheadline={\line{\thischapter\hfil\folio}}
+}
+% For single-sided printing, chapter title goes across top left of page,
+% page number on top right.
+\def\HEADINGSsingle{
+%\pagealignmacro
+\global\pageno=1
+\global\evenfootline={\hfil}
+\global\oddfootline={\hfil}
+\global\evenheadline={\line{\thischapter\hfil\folio}}
+\global\oddheadline={\line{\thischapter\hfil\folio}}
+}
+\def\HEADINGSon{\HEADINGSdouble}
+
+\def\HEADINGSafter{\let\HEADINGShook=\HEADINGSdoublex}
+\let\HEADINGSdoubleafter=\HEADINGSafter
+\def\HEADINGSdoublex{%
+\global\evenfootline={\hfil}
+\global\oddfootline={\hfil}
+\global\evenheadline={\line{\folio\hfil\thistitle}}
+\global\oddheadline={\line{\thischapter\hfil\folio}}
+}
+
+\def\HEADINGSsingleafter{\let\HEADINGShook=\HEADINGSsinglex}
+\def\HEADINGSsinglex{%
+\global\evenfootline={\hfil}
+\global\oddfootline={\hfil}
+\global\evenheadline={\line{\thischapter\hfil\folio}}
+\global\oddheadline={\line{\thischapter\hfil\folio}}
+}
+
+% Subroutines used in generating headings
+% Produces Day Month Year style of output.
+\def\today{\number\day\space
+\ifcase\month\or
+January\or February\or March\or April\or May\or June\or
+July\or August\or September\or October\or November\or December\fi
+\space\number\year}
+
+% Use this if you want the Month Day, Year style of output.
+%\def\today{\ifcase\month\or
+%January\or February\or March\or April\or May\or June\or
+%July\or August\or September\or October\or November\or December\fi
+%\space\number\day, \number\year}
+
+% @settitle line... specifies the title of the document, for headings
+% It generates no output of its own
+
+\def\thistitle{No Title}
+\def\settitle{\parsearg\settitlezzz}
+\def\settitlezzz #1{\gdef\thistitle{#1}}
+
+\message{tables,}
+
+% @tabs -- simple alignment
+
+% These don't work. For one thing, \+ is defined as outer.
+% So these macros cannot even be defined.
+
+%\def\tabs{\parsearg\tabszzz}
+%\def\tabszzz #1{\settabs\+#1\cr}
+%\def\tabline{\parsearg\tablinezzz}
+%\def\tablinezzz #1{\+#1\cr}
+%\def\&{&}
+
+% Tables -- @table, @ftable, @vtable, @item(x), @kitem(x), @xitem(x).
+
+% default indentation of table text
+\newdimen\tableindent \tableindent=.8in
+% default indentation of @itemize and @enumerate text
+\newdimen\itemindent \itemindent=.3in
+% margin between end of table item and start of table text.
+\newdimen\itemmargin \itemmargin=.1in
+
+% used internally for \itemindent minus \itemmargin
+\newdimen\itemmax
+
+% Note @table, @vtable, and @vtable define @item, @itemx, etc., with
+% these defs.
+% They also define \itemindex
+% to index the item name in whatever manner is desired (perhaps none).
+
+\newif\ifitemxneedsnegativevskip
+
+\def\itemxpar{\par\ifitemxneedsnegativevskip\nobreak\vskip-\parskip\nobreak\fi}
+
+\def\internalBitem{\smallbreak \parsearg\itemzzz}
+\def\internalBitemx{\itemxpar \parsearg\itemzzz}
+
+\def\internalBxitem "#1"{\def\xitemsubtopix{#1} \smallbreak \parsearg\xitemzzz}
+\def\internalBxitemx "#1"{\def\xitemsubtopix{#1} \itemxpar \parsearg\xitemzzz}
+
+\def\internalBkitem{\smallbreak \parsearg\kitemzzz}
+\def\internalBkitemx{\itemxpar \parsearg\kitemzzz}
+
+\def\kitemzzz #1{\dosubind {kw}{\code{#1}}{for {\bf \lastfunction}}%
+ \itemzzz {#1}}
+
+\def\xitemzzz #1{\dosubind {kw}{\code{#1}}{for {\bf \xitemsubtopic}}%
+ \itemzzz {#1}}
+
+\def\itemzzz #1{\begingroup %
+ \advance\hsize by -\rightskip
+ \advance\hsize by -\tableindent
+ \setbox0=\hbox{\itemfont{#1}}%
+ \itemindex{#1}%
+ \nobreak % This prevents a break before @itemx.
+ %
+ % Be sure we are not still in the middle of a paragraph.
+ %{\parskip = 0in
+ %\par
+ %}%
+ %
+ % If the item text does not fit in the space we have, put it on a line
+ % by itself, and do not allow a page break either before or after that
+ % line. We do not start a paragraph here because then if the next
+ % command is, e.g., @kindex, the whatsit would get put into the
+ % horizontal list on a line by itself, resulting in extra blank space.
+ \ifdim \wd0>\itemmax
+ %
+ % Make this a paragraph so we get the \parskip glue and wrapping,
+ % but leave it ragged-right.
+ \begingroup
+ \advance\leftskip by-\tableindent
+ \advance\hsize by\tableindent
+ \advance\rightskip by0pt plus1fil
+ \leavevmode\unhbox0\par
+ \endgroup
+ %
+ % We're going to be starting a paragraph, but we don't want the
+ % \parskip glue -- logically it's part of the @item we just started.
+ \nobreak \vskip-\parskip
+ %
+ % Stop a page break at the \parskip glue coming up. Unfortunately
+ % we can't prevent a possible page break at the following
+ % \baselineskip glue.
+ \nobreak
+ \endgroup
+ \itemxneedsnegativevskipfalse
+ \else
+ % The item text fits into the space. Start a paragraph, so that the
+ % following text (if any) will end up on the same line. Since that
+ % text will be indented by \tableindent, we make the item text be in
+ % a zero-width box.
+ \noindent
+ \rlap{\hskip -\tableindent\box0}\ignorespaces%
+ \endgroup%
+ \itemxneedsnegativevskiptrue%
+ \fi
+}
+
+\def\item{\errmessage{@item while not in a table}}
+\def\itemx{\errmessage{@itemx while not in a table}}
+\def\kitem{\errmessage{@kitem while not in a table}}
+\def\kitemx{\errmessage{@kitemx while not in a table}}
+\def\xitem{\errmessage{@xitem while not in a table}}
+\def\xitemx{\errmessage{@xitemx while not in a table}}
+
+%% Contains a kludge to get @end[description] to work
+\def\description{\tablez{\dontindex}{1}{}{}{}{}}
+
+\def\table{\begingroup\inENV\obeylines\obeyspaces\tablex}
+{\obeylines\obeyspaces%
+\gdef\tablex #1^^M{%
+\tabley\dontindex#1 \endtabley}}
+
+\def\ftable{\begingroup\inENV\obeylines\obeyspaces\ftablex}
+{\obeylines\obeyspaces%
+\gdef\ftablex #1^^M{%
+\tabley\fnitemindex#1 \endtabley
+\def\Eftable{\endgraf\afterenvbreak\endgroup}%
+\let\Etable=\relax}}
+
+\def\vtable{\begingroup\inENV\obeylines\obeyspaces\vtablex}
+{\obeylines\obeyspaces%
+\gdef\vtablex #1^^M{%
+\tabley\vritemindex#1 \endtabley
+\def\Evtable{\endgraf\afterenvbreak\endgroup}%
+\let\Etable=\relax}}
+
+\def\dontindex #1{}
+\def\fnitemindex #1{\doind {fn}{\code{#1}}}%
+\def\vritemindex #1{\doind {vr}{\code{#1}}}%
+
+{\obeyspaces %
+\gdef\tabley#1#2 #3 #4 #5 #6 #7\endtabley{\endgroup%
+\tablez{#1}{#2}{#3}{#4}{#5}{#6}}}
+
+\def\tablez #1#2#3#4#5#6{%
+\aboveenvbreak %
+\begingroup %
+\def\Edescription{\Etable}% Necessary kludge.
+\let\itemindex=#1%
+\ifnum 0#3>0 \advance \leftskip by #3\mil \fi %
+\ifnum 0#4>0 \tableindent=#4\mil \fi %
+\ifnum 0#5>0 \advance \rightskip by #5\mil \fi %
+\def\itemfont{#2}%
+\itemmax=\tableindent %
+\advance \itemmax by -\itemmargin %
+\advance \leftskip by \tableindent %
+\exdentamount=\tableindent
+\parindent = 0pt
+\parskip = \smallskipamount
+\ifdim \parskip=0pt \parskip=2pt \fi%
+\def\Etable{\endgraf\afterenvbreak\endgroup}%
+\let\item = \internalBitem %
+\let\itemx = \internalBitemx %
+\let\kitem = \internalBkitem %
+\let\kitemx = \internalBkitemx %
+\let\xitem = \internalBxitem %
+\let\xitemx = \internalBxitemx %
+}
+
+% This is the counter used by @enumerate, which is really @itemize
+
+\newcount \itemno
+
+\def\itemize{\parsearg\itemizezzz}
+
+\def\itemizezzz #1{%
+ \begingroup % ended by the @end itemsize
+ \itemizey {#1}{\Eitemize}
+}
+
+\def\itemizey #1#2{%
+\aboveenvbreak %
+\itemmax=\itemindent %
+\advance \itemmax by -\itemmargin %
+\advance \leftskip by \itemindent %
+\exdentamount=\itemindent
+\parindent = 0pt %
+\parskip = \smallskipamount %
+\ifdim \parskip=0pt \parskip=2pt \fi%
+\def#2{\endgraf\afterenvbreak\endgroup}%
+\def\itemcontents{#1}%
+\let\item=\itemizeitem}
+
+% Set sfcode to normal for the chars that usually have another value.
+% These are `.?!:;,'
+\def\frenchspacing{\sfcode46=1000 \sfcode63=1000 \sfcode33=1000
+ \sfcode58=1000 \sfcode59=1000 \sfcode44=1000 }
+
+% \splitoff TOKENS\endmark defines \first to be the first token in
+% TOKENS, and \rest to be the remainder.
+%
+\def\splitoff#1#2\endmark{\def\first{#1}\def\rest{#2}}%
+
+% Allow an optional argument of an uppercase letter, lowercase letter,
+% or number, to specify the first label in the enumerated list. No
+% argument is the same as `1'.
+%
+\def\enumerate{\parsearg\enumeratezzz}
+\def\enumeratezzz #1{\enumeratey #1 \endenumeratey}
+\def\enumeratey #1 #2\endenumeratey{%
+ \begingroup % ended by the @end enumerate
+ %
+ % If we were given no argument, pretend we were given `1'.
+ \def\thearg{#1}%
+ \ifx\thearg\empty \def\thearg{1}\fi
+ %
+ % Detect if the argument is a single token. If so, it might be a
+ % letter. Otherwise, the only valid thing it can be is a number.
+ % (We will always have one token, because of the test we just made.
+ % This is a good thing, since \splitoff doesn't work given nothing at
+ % all -- the first parameter is undelimited.)
+ \expandafter\splitoff\thearg\endmark
+ \ifx\rest\empty
+ % Only one token in the argument. It could still be anything.
+ % A ``lowercase letter'' is one whose \lccode is nonzero.
+ % An ``uppercase letter'' is one whose \lccode is both nonzero, and
+ % not equal to itself.
+ % Otherwise, we assume it's a number.
+ %
+ % We need the \relax at the end of the \ifnum lines to stop TeX from
+ % continuing to look for a <number>.
+ %
+ \ifnum\lccode\expandafter`\thearg=0\relax
+ \numericenumerate % a number (we hope)
+ \else
+ % It's a letter.
+ \ifnum\lccode\expandafter`\thearg=\expandafter`\thearg\relax
+ \lowercaseenumerate % lowercase letter
+ \else
+ \uppercaseenumerate % uppercase letter
+ \fi
+ \fi
+ \else
+ % Multiple tokens in the argument. We hope it's a number.
+ \numericenumerate
+ \fi
+}
+
+% An @enumerate whose labels are integers. The starting integer is
+% given in \thearg.
+%
+\def\numericenumerate{%
+ \itemno = \thearg
+ \startenumeration{\the\itemno}%
+}
+
+% The starting (lowercase) letter is in \thearg.
+\def\lowercaseenumerate{%
+ \itemno = \expandafter`\thearg
+ \startenumeration{%
+ % Be sure we're not beyond the end of the alphabet.
+ \ifnum\itemno=0
+ \errmessage{No more lowercase letters in @enumerate; get a bigger
+ alphabet}%
+ \fi
+ \char\lccode\itemno
+ }%
+}
+
+% The starting (uppercase) letter is in \thearg.
+\def\uppercaseenumerate{%
+ \itemno = \expandafter`\thearg
+ \startenumeration{%
+ % Be sure we're not beyond the end of the alphabet.
+ \ifnum\itemno=0
+ \errmessage{No more uppercase letters in @enumerate; get a bigger
+ alphabet}
+ \fi
+ \char\uccode\itemno
+ }%
+}
+
+% Call itemizey, adding a period to the first argument and supplying the
+% common last two arguments. Also subtract one from the initial value in
+% \itemno, since @item increments \itemno.
+%
+\def\startenumeration#1{%
+ \advance\itemno by -1
+ \itemizey{#1.}\Eenumerate\flushcr
+}
+
+% @alphaenumerate and @capsenumerate are abbreviations for giving an arg
+% to @enumerate.
+%
+\def\alphaenumerate{\enumerate{a}}
+\def\capsenumerate{\enumerate{A}}
+\def\Ealphaenumerate{\Eenumerate}
+\def\Ecapsenumerate{\Eenumerate}
+
+% Definition of @item while inside @itemize.
+
+\def\itemizeitem{%
+\advance\itemno by 1
+{\let\par=\endgraf \smallbreak}%
+\ifhmode \errmessage{\in hmode at itemizeitem}\fi
+{\parskip=0in \hskip 0pt
+\hbox to 0pt{\hss \itemcontents\hskip \itemmargin}%
+\vadjust{\penalty 1200}}%
+\flushcr}
+
+% @multitable macros
+% Amy Hendrickson, 8/18/94, 3/6/96
+%
+% @multitable ... @end multitable will make as many columns as desired.
+% Contents of each column will wrap at width given in preamble. Width
+% can be specified either with sample text given in a template line,
+% or in percent of \hsize, the current width of text on page.
+
+% Table can continue over pages but will only break between lines.
+
+% To make preamble:
+%
+% Either define widths of columns in terms of percent of \hsize:
+% @multitable @columnfractions .25 .3 .45
+% @item ...
+%
+% Numbers following @columnfractions are the percent of the total
+% current hsize to be used for each column. You may use as many
+% columns as desired.
+
+% Or use a template:
+% @multitable {Column 1 template} {Column 2 template} {Column 3 template}
+% @item ...
+% using the widest term desired in each column.
+%
+% For those who want to use more than one line's worth of words in
+% the preamble, break the line within one argument and it
+% will parse correctly, i.e.,
+%
+% @multitable {Column 1 template} {Column 2 template} {Column 3
+% template}
+% Not:
+% @multitable {Column 1 template} {Column 2 template}
+% {Column 3 template}
+
+% Each new table line starts with @item, each subsequent new column
+% starts with @tab. Empty columns may be produced by supplying @tab's
+% with nothing between them for as many times as empty columns are needed,
+% ie, @tab@tab@tab will produce two empty columns.
+
+% @item, @tab, @multicolumn or @endmulticolumn do not need to be on their
+% own lines, but it will not hurt if they are.
+
+% Sample multitable:
+
+% @multitable {Column 1 template} {Column 2 template} {Column 3 template}
+% @item first col stuff @tab second col stuff @tab third col
+% @item
+% first col stuff
+% @tab
+% second col stuff
+% @tab
+% third col
+% @item first col stuff @tab second col stuff
+% @tab Many paragraphs of text may be used in any column.
+%
+% They will wrap at the width determined by the template.
+% @item@tab@tab This will be in third column.
+% @end multitable
+
+% Default dimensions may be reset by user.
+% @multitableparskip is vertical space between paragraphs in table.
+% @multitableparindent is paragraph indent in table.
+% @multitablecolmargin is horizontal space to be left between columns.
+% @multitablelinespace is space to leave between table items;
+% 0 means it depends on current normal line spacing.
+
+%%%%
+% Dimensions
+
+\newskip\multitableparskip
+\newskip\multitableparindent
+\newdimen\multitablecolspace
+\newskip\multitablelinespace
+\multitableparskip=0pt
+\multitableparindent=6pt
+\multitablecolspace=12pt
+\multitablelinespace=0pt
+
+%%%%
+% Macros used to set up halign preamble:
+\let\endsetuptable\relax
+\def\xendsetuptable{\endsetuptable}
+\let\columnfractions\relax
+\def\xcolumnfractions{\columnfractions}
+\newif\ifsetpercent
+
+%% 2/1/96, to allow fractions to be given with more than one digit.
+\def\pickupwholefraction#1 {\global\advance\colcount by1 %
+\expandafter\xdef\csname col\the\colcount\endcsname{.#1\hsize}%
+\setuptable}
+
+\newcount\colcount
+\def\setuptable#1{\def\firstarg{#1}%
+\ifx\firstarg\xendsetuptable\let\go\relax%
+\else
+ \ifx\firstarg\xcolumnfractions\global\setpercenttrue%
+ \else
+ \ifsetpercent
+ \let\go\pickupwholefraction % In this case arg of setuptable
+ % is the decimal point before the
+ % number given in percent of hsize.
+ % We don't need this so we don't use it.
+ \else
+ \global\advance\colcount by1
+ \setbox0=\hbox{#1}%
+ \expandafter\xdef\csname col\the\colcount\endcsname{\the\wd0}%
+ \fi%
+ \fi%
+\ifx\go\pickupwholefraction\else\let\go\setuptable\fi%
+\fi\go}
+
+%%%%
+% multitable syntax
+\def\tab{&\hskip1sp\relax} % 2/2/96
+ % tiny skip here makes sure this column space is
+ % maintained, even if it is never used.
+
+
+%%%%
+% @multitable ... @end multitable definitions:
+
+\def\multitable{\parsearg\dotable}
+
+\def\dotable#1{\bgroup
+\let\item\cr
+\tolerance=9500
+\hbadness=9500
+\setmultitablespacing
+\parskip=\multitableparskip
+\parindent=\multitableparindent
+\overfullrule=0pt
+\global\colcount=0\relax%
+\def\Emultitable{\global\setpercentfalse\global\everycr{}\cr\egroup\egroup}%
+ % To parse everything between @multitable and @item :
+\setuptable#1 \endsetuptable
+ % Need to reset this to 0 after \setuptable.
+\global\colcount=0\relax%
+ %
+ % This preamble sets up a generic column definition, which will
+ % be used as many times as user calls for columns.
+ % \vtop will set a single line and will also let text wrap and
+ % continue for many paragraphs if desired.
+\halign\bgroup&\global\advance\colcount by 1\relax%
+\vtop{\hsize=\expandafter\csname col\the\colcount\endcsname
+ % In order to keep entries from bumping into each other
+ % we will add a \leftskip of \multitablecolspace to all columns after
+ % the first one.
+ % If a template has been used, we will add \multitablecolspace
+ % to the width of each template entry.
+ % If user has set preamble in terms of percent of \hsize
+ % we will use that dimension as the width of the column, and
+ % the \leftskip will keep entries from bumping into each other.
+ % Table will start at left margin and final column will justify at
+ % right margin.
+\ifnum\colcount=1
+\else
+ \ifsetpercent
+ \else
+ % If user has <not> set preamble in terms of percent of \hsize
+ % we will advance \hsize by \multitablecolspace
+ \advance\hsize by \multitablecolspace
+ \fi
+ % In either case we will make \leftskip=\multitablecolspace:
+\leftskip=\multitablecolspace
+\fi
+\noindent##%\par
+%\vskip\multitablelinespace
+}\cr%
+ % \everycr will reset column counter, \colcount, at the end of
+ % each line. Every column entry will cause \colcount to advance by one.
+ % The table preamble
+ % looks at the current \colcount to find the correct column width.
+\global\everycr{\noalign{\nointerlineskip\vskip\multitablelinespace
+\filbreak%% keeps underfull box messages off when table breaks over pages.
+\global\colcount=0\relax}}}
+
+\def\setmultitablespacing{% test to see if user has set \multitablelinespace.
+% If so, do nothing. If not, give it an appropriate dimension based on
+% current baselineskip.
+\ifdim\multitablelinespace=0pt
+\setbox0=\vbox{X}\global\multitablelinespace=\the\baselineskip
+\global\advance\multitablelinespace by-\ht0\fi
+%% Test to see if parskip is larger than space between lines of
+%% table. If not, do nothing.
+%% If so, set to same dimension as multitablelinespace.
+\ifdim\multitableparskip>\multitablelinespace
+\global\multitableparskip=\multitablelinespace
+\global\advance\multitableparskip-7pt %% to keep parskip somewhat smaller
+ %% than skip between lines in the table.
+\fi%
+\ifdim\multitableparskip=0pt
+\global\multitableparskip=\multitablelinespace
+\global\advance\multitableparskip-7pt %% to keep parskip somewhat smaller
+ %% than skip between lines in the table.
+\fi}
+\message{indexing,}
+% Index generation facilities
+
+% Define \newwrite to be identical to plain tex's \newwrite
+% except not \outer, so it can be used within \newindex.
+{\catcode`\@=11
+\gdef\newwrite{\alloc@7\write\chardef\sixt@@n}}
+
+% \newindex {foo} defines an index named foo.
+% It automatically defines \fooindex such that
+% \fooindex ...rest of line... puts an entry in the index foo.
+% It also defines \fooindfile to be the number of the output channel for
+% the file that accumulates this index. The file's extension is foo.
+% The name of an index should be no more than 2 characters long
+% for the sake of vms.
+
+\def\newindex #1{
+\expandafter\newwrite \csname#1indfile\endcsname% Define number for output file
+\openout \csname#1indfile\endcsname \jobname.#1 % Open the file
+\expandafter\xdef\csname#1index\endcsname{% % Define \xxxindex
+\noexpand\doindex {#1}}
+}
+
+% @defindex foo == \newindex{foo}
+
+\def\defindex{\parsearg\newindex}
+
+% Define @defcodeindex, like @defindex except put all entries in @code.
+
+\def\newcodeindex #1{
+\expandafter\newwrite \csname#1indfile\endcsname% Define number for output file
+\openout \csname#1indfile\endcsname \jobname.#1 % Open the file
+\expandafter\xdef\csname#1index\endcsname{% % Define \xxxindex
+\noexpand\docodeindex {#1}}
+}
+
+\def\defcodeindex{\parsearg\newcodeindex}
+
+% @synindex foo bar makes index foo feed into index bar.
+% Do this instead of @defindex foo if you don't want it as a separate index.
+\def\synindex #1 #2 {%
+\expandafter\let\expandafter\synindexfoo\expandafter=\csname#2indfile\endcsname
+\expandafter\let\csname#1indfile\endcsname=\synindexfoo
+\expandafter\xdef\csname#1index\endcsname{% % Define \xxxindex
+\noexpand\doindex {#2}}%
+}
+
+% @syncodeindex foo bar similar, but put all entries made for index foo
+% inside @code.
+\def\syncodeindex #1 #2 {%
+\expandafter\let\expandafter\synindexfoo\expandafter=\csname#2indfile\endcsname
+\expandafter\let\csname#1indfile\endcsname=\synindexfoo
+\expandafter\xdef\csname#1index\endcsname{% % Define \xxxindex
+\noexpand\docodeindex {#2}}%
+}
+
+% Define \doindex, the driver for all \fooindex macros.
+% Argument #1 is generated by the calling \fooindex macro,
+% and it is "foo", the name of the index.
+
+% \doindex just uses \parsearg; it calls \doind for the actual work.
+% This is because \doind is more useful to call from other macros.
+
+% There is also \dosubind {index}{topic}{subtopic}
+% which makes an entry in a two-level index such as the operation index.
+
+\def\doindex#1{\edef\indexname{#1}\parsearg\singleindexer}
+\def\singleindexer #1{\doind{\indexname}{#1}}
+
+% like the previous two, but they put @code around the argument.
+\def\docodeindex#1{\edef\indexname{#1}\parsearg\singlecodeindexer}
+\def\singlecodeindexer #1{\doind{\indexname}{\code{#1}}}
+
+\def\indexdummies{%
+% Take care of the plain tex accent commands.
+\def\"{\realbackslash "}%
+\def\`{\realbackslash `}%
+\def\'{\realbackslash '}%
+\def\^{\realbackslash ^}%
+\def\~{\realbackslash ~}%
+\def\={\realbackslash =}%
+\def\b{\realbackslash b}%
+\def\c{\realbackslash c}%
+\def\d{\realbackslash d}%
+\def\u{\realbackslash u}%
+\def\v{\realbackslash v}%
+\def\H{\realbackslash H}%
+% Take care of the plain tex special European modified letters.
+\def\oe{\realbackslash oe}%
+\def\ae{\realbackslash ae}%
+\def\aa{\realbackslash aa}%
+\def\OE{\realbackslash OE}%
+\def\AE{\realbackslash AE}%
+\def\AA{\realbackslash AA}%
+\def\o{\realbackslash o}%
+\def\O{\realbackslash O}%
+\def\l{\realbackslash l}%
+\def\L{\realbackslash L}%
+\def\ss{\realbackslash ss}%
+% Take care of texinfo commands likely to appear in an index entry.
+\def\_{{\realbackslash _}}%
+\def\w{\realbackslash w }%
+\def\bf{\realbackslash bf }%
+\def\rm{\realbackslash rm }%
+\def\sl{\realbackslash sl }%
+\def\sf{\realbackslash sf}%
+\def\tt{\realbackslash tt}%
+\def\gtr{\realbackslash gtr}%
+\def\less{\realbackslash less}%
+\def\hat{\realbackslash hat}%
+\def\char{\realbackslash char}%
+\def\TeX{\realbackslash TeX}%
+\def\dots{\realbackslash dots }%
+\def\copyright{\realbackslash copyright }%
+\def\tclose##1{\realbackslash tclose {##1}}%
+\def\code##1{\realbackslash code {##1}}%
+\def\samp##1{\realbackslash samp {##1}}%
+\def\t##1{\realbackslash r {##1}}%
+\def\r##1{\realbackslash r {##1}}%
+\def\i##1{\realbackslash i {##1}}%
+\def\b##1{\realbackslash b {##1}}%
+\def\cite##1{\realbackslash cite {##1}}%
+\def\key##1{\realbackslash key {##1}}%
+\def\file##1{\realbackslash file {##1}}%
+\def\var##1{\realbackslash var {##1}}%
+\def\kbd##1{\realbackslash kbd {##1}}%
+\def\dfn##1{\realbackslash dfn {##1}}%
+\def\emph##1{\realbackslash emph {##1}}%
+\unsepspaces
+}
+
+% If an index command is used in an @example environment, any spaces
+% therein should become regular spaces in the raw index file, not the
+% expansion of \tie (\\leavevmode \penalty \@M \ ).
+{\obeyspaces
+ \gdef\unsepspaces{\obeyspaces\let =\space}}
+
+% \indexnofonts no-ops all font-change commands.
+% This is used when outputting the strings to sort the index by.
+\def\indexdummyfont#1{#1}
+\def\indexdummytex{TeX}
+\def\indexdummydots{...}
+
+\def\indexnofonts{%
+% Just ignore accents.
+\let\"=\indexdummyfont
+\let\`=\indexdummyfont
+\let\'=\indexdummyfont
+\let\^=\indexdummyfont
+\let\~=\indexdummyfont
+\let\==\indexdummyfont
+\let\b=\indexdummyfont
+\let\c=\indexdummyfont
+\let\d=\indexdummyfont
+\let\u=\indexdummyfont
+\let\v=\indexdummyfont
+\let\H=\indexdummyfont
+% Take care of the plain tex special European modified letters.
+\def\oe{oe}%
+\def\ae{ae}%
+\def\aa{aa}%
+\def\OE{OE}%
+\def\AE{AE}%
+\def\AA{AA}%
+\def\o{o}%
+\def\O{O}%
+\def\l{l}%
+\def\L{L}%
+\def\ss{ss}%
+\let\w=\indexdummyfont
+\let\t=\indexdummyfont
+\let\r=\indexdummyfont
+\let\i=\indexdummyfont
+\let\b=\indexdummyfont
+\let\emph=\indexdummyfont
+\let\strong=\indexdummyfont
+\let\cite=\indexdummyfont
+\let\sc=\indexdummyfont
+%Don't no-op \tt, since it isn't a user-level command
+% and is used in the definitions of the active chars like <, >, |...
+%\let\tt=\indexdummyfont
+\let\tclose=\indexdummyfont
+\let\code=\indexdummyfont
+\let\file=\indexdummyfont
+\let\samp=\indexdummyfont
+\let\kbd=\indexdummyfont
+\let\key=\indexdummyfont
+\let\var=\indexdummyfont
+\let\TeX=\indexdummytex
+\let\dots=\indexdummydots
+}
+
+% To define \realbackslash, we must make \ not be an escape.
+% We must first make another character (@) an escape
+% so we do not become unable to do a definition.
+
+{\catcode`\@=0 \catcode`\\=\other
+@gdef@realbackslash{\}}
+
+\let\indexbackslash=0 %overridden during \printindex.
+
+\let\SETmarginindex=\relax %initialize!
+% workhorse for all \fooindexes
+% #1 is name of index, #2 is stuff to put there
+\def\doind #1#2{%
+% Put the index entry in the margin if desired.
+\ifx\SETmarginindex\relax\else%
+\insert\margin{\hbox{\vrule height8pt depth3pt width0pt #2}}%
+\fi%
+{\count10=\lastpenalty %
+{\indexdummies % Must do this here, since \bf, etc expand at this stage
+\escapechar=`\\%
+{\let\folio=0% Expand all macros now EXCEPT \folio
+\def\rawbackslashxx{\indexbackslash}% \indexbackslash isn't defined now
+% so it will be output as is; and it will print as backslash in the indx.
+%
+% Now process the index-string once, with all font commands turned off,
+% to get the string to sort the index by.
+{\indexnofonts
+\xdef\temp1{#2}%
+}%
+% Now produce the complete index entry. We process the index-string again,
+% this time with font commands expanded, to get what to print in the index.
+\edef\temp{%
+\write \csname#1indfile\endcsname{%
+\realbackslash entry {\temp1}{\folio}{#2}}}%
+\temp }%
+}\penalty\count10}}
+
+\def\dosubind #1#2#3{%
+{\count10=\lastpenalty %
+{\indexdummies % Must do this here, since \bf, etc expand at this stage
+\escapechar=`\\%
+{\let\folio=0%
+\def\rawbackslashxx{\indexbackslash}%
+%
+% Now process the index-string once, with all font commands turned off,
+% to get the string to sort the index by.
+{\indexnofonts
+\xdef\temp1{#2 #3}%
+}%
+% Now produce the complete index entry. We process the index-string again,
+% this time with font commands expanded, to get what to print in the index.
+\edef\temp{%
+\write \csname#1indfile\endcsname{%
+\realbackslash entry {\temp1}{\folio}{#2}{#3}}}%
+\temp }%
+}\penalty\count10}}
+
+% The index entry written in the file actually looks like
+% \entry {sortstring}{page}{topic}
+% or
+% \entry {sortstring}{page}{topic}{subtopic}
+% The texindex program reads in these files and writes files
+% containing these kinds of lines:
+% \initial {c}
+% before the first topic whose initial is c
+% \entry {topic}{pagelist}
+% for a topic that is used without subtopics
+% \primary {topic}
+% for the beginning of a topic that is used with subtopics
+% \secondary {subtopic}{pagelist}
+% for each subtopic.
+
+% Define the user-accessible indexing commands
+% @findex, @vindex, @kindex, @cindex.
+
+\def\findex {\fnindex}
+\def\kindex {\kyindex}
+\def\cindex {\cpindex}
+\def\vindex {\vrindex}
+\def\tindex {\tpindex}
+\def\pindex {\pgindex}
+
+\def\cindexsub {\begingroup\obeylines\cindexsub}
+{\obeylines %
+\gdef\cindexsub "#1" #2^^M{\endgroup %
+\dosubind{cp}{#2}{#1}}}
+
+% Define the macros used in formatting output of the sorted index material.
+
+% This is what you call to cause a particular index to get printed.
+% Write
+% @unnumbered Function Index
+% @printindex fn
+
+\def\printindex{\parsearg\doprintindex}
+
+\def\doprintindex#1{%
+ \tex
+ \dobreak \chapheadingskip {10000}
+ \catcode`\%=\other\catcode`\&=\other\catcode`\#=\other
+ \catcode`\$=\other
+ \catcode`\~=\other
+ \indexbreaks
+ %
+ % The following don't help, since the chars were translated
+ % when the raw index was written, and their fonts were discarded
+ % due to \indexnofonts.
+ %\catcode`\"=\active
+ %\catcode`\^=\active
+ %\catcode`\_=\active
+ %\catcode`\|=\active
+ %\catcode`\<=\active
+ %\catcode`\>=\active
+ % %
+ \def\indexbackslash{\rawbackslashxx}
+ \indexfonts\rm \tolerance=9500 \advance\baselineskip -1pt
+ \begindoublecolumns
+ %
+ % See if the index file exists and is nonempty.
+ \openin 1 \jobname.#1s
+ \ifeof 1
+ % \enddoublecolumns gets confused if there is no text in the index,
+ % and it loses the chapter title and the aux file entries for the
+ % index. The easiest way to prevent this problem is to make sure
+ % there is some text.
+ (Index is nonexistent)
+ \else
+ %
+ % If the index file exists but is empty, then \openin leaves \ifeof
+ % false. We have to make TeX try to read something from the file, so
+ % it can discover if there is anything in it.
+ \read 1 to \temp
+ \ifeof 1
+ (Index is empty)
+ \else
+ \input \jobname.#1s
+ \fi
+ \fi
+ \closein 1
+ \enddoublecolumns
+ \Etex
+}
+
+% These macros are used by the sorted index file itself.
+% Change them to control the appearance of the index.
+
+% Same as \bigskipamount except no shrink.
+% \balancecolumns gets confused if there is any shrink.
+\newskip\initialskipamount \initialskipamount 12pt plus4pt
+
+\def\initial #1{%
+{\let\tentt=\sectt \let\tt=\sectt \let\sf=\sectt
+\ifdim\lastskip<\initialskipamount
+\removelastskip \penalty-200 \vskip \initialskipamount\fi
+\line{\secbf#1\hfill}\kern 2pt\penalty10000}}
+
+% This typesets a paragraph consisting of #1, dot leaders, and then #2
+% flush to the right margin. It is used for index and table of contents
+% entries. The paragraph is indented by \leftskip.
+%
+\def\entry #1#2{\begingroup
+ %
+ % Start a new paragraph if necessary, so our assignments below can't
+ % affect previous text.
+ \par
+ %
+ % Do not fill out the last line with white space.
+ \parfillskip = 0in
+ %
+ % No extra space above this paragraph.
+ \parskip = 0in
+ %
+ % Do not prefer a separate line ending with a hyphen to fewer lines.
+ \finalhyphendemerits = 0
+ %
+ % \hangindent is only relevant when the entry text and page number
+ % don't both fit on one line. In that case, bob suggests starting the
+ % dots pretty far over on the line. Unfortunately, a large
+ % indentation looks wrong when the entry text itself is broken across
+ % lines. So we use a small indentation and put up with long leaders.
+ %
+ % \hangafter is reset to 1 (which is the value we want) at the start
+ % of each paragraph, so we need not do anything with that.
+ \hangindent=2em
+ %
+ % When the entry text needs to be broken, just fill out the first line
+ % with blank space.
+ \rightskip = 0pt plus1fil
+ %
+ % Start a ``paragraph'' for the index entry so the line breaking
+ % parameters we've set above will have an effect.
+ \noindent
+ %
+ % Insert the text of the index entry. TeX will do line-breaking on it.
+ #1%
+ % The following is kludged to not output a line of dots in the index if
+ % there are no page numbers. The next person who breaks this will be
+ % cursed by a Unix daemon.
+ \def\tempa{{\rm }}%
+ \def\tempb{#2}%
+ \edef\tempc{\tempa}%
+ \edef\tempd{\tempb}%
+ \ifx\tempc\tempd\ \else%
+ %
+ % If we must, put the page number on a line of its own, and fill out
+ % this line with blank space. (The \hfil is overwhelmed with the
+ % fill leaders glue in \indexdotfill if the page number does fit.)
+ \hfil\penalty50
+ \null\nobreak\indexdotfill % Have leaders before the page number.
+ %
+ % The `\ ' here is removed by the implicit \unskip that TeX does as
+ % part of (the primitive) \par. Without it, a spurious underfull
+ % \hbox ensues.
+ \ #2% The page number ends the paragraph.
+ \fi%
+ \par
+\endgroup}
+
+% Like \dotfill except takes at least 1 em.
+\def\indexdotfill{\cleaders
+ \hbox{$\mathsurround=0pt \mkern1.5mu ${\it .}$ \mkern1.5mu$}\hskip 1em plus 1fill}
+
+\def\primary #1{\line{#1\hfil}}
+
+\newskip\secondaryindent \secondaryindent=0.5cm
+
+\def\secondary #1#2{
+{\parfillskip=0in \parskip=0in
+\hangindent =1in \hangafter=1
+\noindent\hskip\secondaryindent\hbox{#1}\indexdotfill #2\par
+}}
+
+%% Define two-column mode, which is used in indexes.
+%% Adapted from the TeXbook, page 416.
+\catcode `\@=11
+
+\newbox\partialpage
+
+\newdimen\doublecolumnhsize
+
+\def\begindoublecolumns{\begingroup
+ % Grab any single-column material above us.
+ \output = {\global\setbox\partialpage
+ =\vbox{\unvbox255\kern -\topskip \kern \baselineskip}}%
+ \eject
+ %
+ % Now switch to the double-column output routine.
+ \output={\doublecolumnout}%
+ %
+ % Change the page size parameters. We could do this once outside this
+ % routine, in each of @smallbook, @afourpaper, and the default 8.5x11
+ % format, but then we repeat the same computation. Repeating a couple
+ % of assignments once per index is clearly meaningless for the
+ % execution time, so we may as well do it once.
+ %
+ % First we halve the line length, less a little for the gutter between
+ % the columns. We compute the gutter based on the line length, so it
+ % changes automatically with the paper format. The magic constant
+ % below is chosen so that the gutter has the same value (well, +- <
+ % 1pt) as it did when we hard-coded it.
+ %
+ % We put the result in a separate register, \doublecolumhsize, so we
+ % can restore it in \pagesofar, after \hsize itself has (potentially)
+ % been clobbered.
+ %
+ \doublecolumnhsize = \hsize
+ \advance\doublecolumnhsize by -.04154\hsize
+ \divide\doublecolumnhsize by 2
+ \hsize = \doublecolumnhsize
+ %
+ % Double the \vsize as well. (We don't need a separate register here,
+ % since nobody clobbers \vsize.)
+ \vsize = 2\vsize
+ \doublecolumnpagegoal
+}
+
+\def\enddoublecolumns{\eject \endgroup \pagegoal=\vsize \unvbox\partialpage}
+
+\def\doublecolumnsplit{\splittopskip=\topskip \splitmaxdepth=\maxdepth
+ \global\dimen@=\pageheight \global\advance\dimen@ by-\ht\partialpage
+ \global\setbox1=\vsplit255 to\dimen@ \global\setbox0=\vbox{\unvbox1}
+ \global\setbox3=\vsplit255 to\dimen@ \global\setbox2=\vbox{\unvbox3}
+ \ifdim\ht0>\dimen@ \setbox255=\vbox{\unvbox0\unvbox2} \global\setbox255=\copy5 \fi
+ \ifdim\ht2>\dimen@ \setbox255=\vbox{\unvbox0\unvbox2} \global\setbox255=\copy5 \fi
+}
+\def\doublecolumnpagegoal{%
+ \dimen@=\vsize \advance\dimen@ by-2\ht\partialpage \global\pagegoal=\dimen@
+}
+\def\pagesofar{\unvbox\partialpage %
+ \hsize=\doublecolumnhsize % have to restore this since output routine
+ \wd0=\hsize \wd2=\hsize \hbox to\pagewidth{\box0\hfil\box2}}
+\def\doublecolumnout{%
+ \setbox5=\copy255
+ {\vbadness=10000 \doublecolumnsplit}
+ \ifvbox255
+ \setbox0=\vtop to\dimen@{\unvbox0}
+ \setbox2=\vtop to\dimen@{\unvbox2}
+ \onepageout\pagesofar \unvbox255 \penalty\outputpenalty
+ \else
+ \setbox0=\vbox{\unvbox5}
+ \ifvbox0
+ \dimen@=\ht0 \advance\dimen@ by\topskip \advance\dimen@ by-\baselineskip
+ \divide\dimen@ by2 \splittopskip=\topskip \splitmaxdepth=\maxdepth
+ {\vbadness=10000
+ \loop \global\setbox5=\copy0
+ \setbox1=\vsplit5 to\dimen@
+ \setbox3=\vsplit5 to\dimen@
+ \ifvbox5 \global\advance\dimen@ by1pt \repeat
+ \setbox0=\vbox to\dimen@{\unvbox1}
+ \setbox2=\vbox to\dimen@{\unvbox3}
+ \global\setbox\partialpage=\vbox{\pagesofar}
+ \doublecolumnpagegoal
+ }
+ \fi
+ \fi
+}
+
+\catcode `\@=\other
+\message{sectioning,}
+% Define chapters, sections, etc.
+
+\newcount \chapno
+\newcount \secno \secno=0
+\newcount \subsecno \subsecno=0
+\newcount \subsubsecno \subsubsecno=0
+
+% This counter is funny since it counts through charcodes of letters A, B, ...
+\newcount \appendixno \appendixno = `\@
+\def\appendixletter{\char\the\appendixno}
+
+\newwrite \contentsfile
+% This is called from \setfilename.
+\def\opencontents{\openout \contentsfile = \jobname.toc}
+
+% Each @chapter defines this as the name of the chapter.
+% page headings and footings can use it. @section does likewise
+
+\def\thischapter{} \def\thissection{}
+\def\seccheck#1{\if \pageno<0 %
+\errmessage{@#1 not allowed after generating table of contents}\fi
+%
+}
+
+\def\chapternofonts{%
+\let\rawbackslash=\relax%
+\let\frenchspacing=\relax%
+\def\result{\realbackslash result}
+\def\equiv{\realbackslash equiv}
+\def\expansion{\realbackslash expansion}
+\def\print{\realbackslash print}
+\def\TeX{\realbackslash TeX}
+\def\dots{\realbackslash dots}
+\def\copyright{\realbackslash copyright}
+\def\tt{\realbackslash tt}
+\def\bf{\realbackslash bf }
+\def\w{\realbackslash w}
+\def\less{\realbackslash less}
+\def\gtr{\realbackslash gtr}
+\def\hat{\realbackslash hat}
+\def\char{\realbackslash char}
+\def\tclose##1{\realbackslash tclose {##1}}
+\def\code##1{\realbackslash code {##1}}
+\def\samp##1{\realbackslash samp {##1}}
+\def\r##1{\realbackslash r {##1}}
+\def\b##1{\realbackslash b {##1}}
+\def\key##1{\realbackslash key {##1}}
+\def\file##1{\realbackslash file {##1}}
+\def\kbd##1{\realbackslash kbd {##1}}
+% These are redefined because @smartitalic wouldn't work inside xdef.
+\def\i##1{\realbackslash i {##1}}
+\def\cite##1{\realbackslash cite {##1}}
+\def\var##1{\realbackslash var {##1}}
+\def\emph##1{\realbackslash emph {##1}}
+\def\dfn##1{\realbackslash dfn {##1}}
+}
+
+\newcount\absseclevel % used to calculate proper heading level
+\newcount\secbase\secbase=0 % @raise/lowersections modify this count
+
+% @raisesections: treat @section as chapter, @subsection as section, etc.
+\def\raisesections{\global\advance\secbase by -1}
+\let\up=\raisesections % original BFox name
+
+% @lowersections: treat @chapter as section, @section as subsection, etc.
+\def\lowersections{\global\advance\secbase by 1}
+\let\down=\lowersections % original BFox name
+
+% Choose a numbered-heading macro
+% #1 is heading level if unmodified by @raisesections or @lowersections
+% #2 is text for heading
+\def\numhead#1#2{\absseclevel=\secbase\advance\absseclevel by #1
+\ifcase\absseclevel
+ \chapterzzz{#2}
+\or
+ \seczzz{#2}
+\or
+ \numberedsubseczzz{#2}
+\or
+ \numberedsubsubseczzz{#2}
+\else
+ \ifnum \absseclevel<0
+ \chapterzzz{#2}
+ \else
+ \numberedsubsubseczzz{#2}
+ \fi
+\fi
+}
+
+% like \numhead, but chooses appendix heading levels
+\def\apphead#1#2{\absseclevel=\secbase\advance\absseclevel by #1
+\ifcase\absseclevel
+ \appendixzzz{#2}
+\or
+ \appendixsectionzzz{#2}
+\or
+ \appendixsubseczzz{#2}
+\or
+ \appendixsubsubseczzz{#2}
+\else
+ \ifnum \absseclevel<0
+ \appendixzzz{#2}
+ \else
+ \appendixsubsubseczzz{#2}
+ \fi
+\fi
+}
+
+% like \numhead, but chooses numberless heading levels
+\def\unnmhead#1#2{\absseclevel=\secbase\advance\absseclevel by #1
+\ifcase\absseclevel
+ \unnumberedzzz{#2}
+\or
+ \unnumberedseczzz{#2}
+\or
+ \unnumberedsubseczzz{#2}
+\or
+ \unnumberedsubsubseczzz{#2}
+\else
+ \ifnum \absseclevel<0
+ \unnumberedzzz{#2}
+ \else
+ \unnumberedsubsubseczzz{#2}
+ \fi
+\fi
+}
+
+
+\def\thischaptername{No Chapter Title}
+\outer\def\chapter{\parsearg\chapteryyy}
+\def\chapteryyy #1{\numhead0{#1}} % normally numhead0 calls chapterzzz
+\def\chapterzzz #1{\seccheck{chapter}%
+\secno=0 \subsecno=0 \subsubsecno=0
+\global\advance \chapno by 1 \message{\putwordChapter \the\chapno}%
+\chapmacro {#1}{\the\chapno}%
+\gdef\thissection{#1}%
+\gdef\thischaptername{#1}%
+% We don't substitute the actual chapter name into \thischapter
+% because we don't want its macros evaluated now.
+\xdef\thischapter{\putwordChapter{} \the\chapno: \noexpand\thischaptername}%
+{\chapternofonts%
+\edef\temp{{\realbackslash chapentry {#1}{\the\chapno}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\donoderef %
+\global\let\section = \numberedsec
+\global\let\subsection = \numberedsubsec
+\global\let\subsubsection = \numberedsubsubsec
+}}
+
+\outer\def\appendix{\parsearg\appendixyyy}
+\def\appendixyyy #1{\apphead0{#1}} % normally apphead0 calls appendixzzz
+\def\appendixzzz #1{\seccheck{appendix}%
+\secno=0 \subsecno=0 \subsubsecno=0
+\global\advance \appendixno by 1 \message{Appendix \appendixletter}%
+\chapmacro {#1}{\putwordAppendix{} \appendixletter}%
+\gdef\thissection{#1}%
+\gdef\thischaptername{#1}%
+\xdef\thischapter{\putwordAppendix{} \appendixletter: \noexpand\thischaptername}%
+{\chapternofonts%
+\edef\temp{{\realbackslash chapentry
+ {#1}{\putwordAppendix{} \appendixletter}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\appendixnoderef %
+\global\let\section = \appendixsec
+\global\let\subsection = \appendixsubsec
+\global\let\subsubsection = \appendixsubsubsec
+}}
+
+\outer\def\top{\parsearg\unnumberedyyy}
+\outer\def\unnumbered{\parsearg\unnumberedyyy}
+\def\unnumberedyyy #1{\unnmhead0{#1}} % normally unnmhead0 calls unnumberedzzz
+\def\unnumberedzzz #1{\seccheck{unnumbered}%
+\secno=0 \subsecno=0 \subsubsecno=0
+%
+% This used to be simply \message{#1}, but TeX fully expands the
+% argument to \message. Therefore, if #1 contained @-commands, TeX
+% expanded them. For example, in `@unnumbered The @cite{Book}', TeX
+% expanded @cite (which turns out to cause errors because \cite is meant
+% to be executed, not expanded).
+%
+% Anyway, we don't want the fully-expanded definition of @cite to appear
+% as a result of the \message, we just want `@cite' itself. We use
+% \the<toks register> to achieve this: TeX expands \the<toks> only once,
+% simply yielding the contents of the <toks register>.
+\toks0 = {#1}\message{(\the\toks0)}%
+%
+\unnumbchapmacro {#1}%
+\gdef\thischapter{#1}\gdef\thissection{#1}%
+{\chapternofonts%
+\edef\temp{{\realbackslash unnumbchapentry {#1}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\unnumbnoderef %
+\global\let\section = \unnumberedsec
+\global\let\subsection = \unnumberedsubsec
+\global\let\subsubsection = \unnumberedsubsubsec
+}}
+
+\outer\def\numberedsec{\parsearg\secyyy}
+\def\secyyy #1{\numhead1{#1}} % normally calls seczzz
+\def\seczzz #1{\seccheck{section}%
+\subsecno=0 \subsubsecno=0 \global\advance \secno by 1 %
+\gdef\thissection{#1}\secheading {#1}{\the\chapno}{\the\secno}%
+{\chapternofonts%
+\edef\temp{{\realbackslash secentry %
+{#1}{\the\chapno}{\the\secno}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\donoderef %
+\penalty 10000 %
+}}
+
+\outer\def\appendixsection{\parsearg\appendixsecyyy}
+\outer\def\appendixsec{\parsearg\appendixsecyyy}
+\def\appendixsecyyy #1{\apphead1{#1}} % normally calls appendixsectionzzz
+\def\appendixsectionzzz #1{\seccheck{appendixsection}%
+\subsecno=0 \subsubsecno=0 \global\advance \secno by 1 %
+\gdef\thissection{#1}\secheading {#1}{\appendixletter}{\the\secno}%
+{\chapternofonts%
+\edef\temp{{\realbackslash secentry %
+{#1}{\appendixletter}{\the\secno}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\appendixnoderef %
+\penalty 10000 %
+}}
+
+\outer\def\unnumberedsec{\parsearg\unnumberedsecyyy}
+\def\unnumberedsecyyy #1{\unnmhead1{#1}} % normally calls unnumberedseczzz
+\def\unnumberedseczzz #1{\seccheck{unnumberedsec}%
+\plainsecheading {#1}\gdef\thissection{#1}%
+{\chapternofonts%
+\edef\temp{{\realbackslash unnumbsecentry{#1}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\unnumbnoderef %
+\penalty 10000 %
+}}
+
+\outer\def\numberedsubsec{\parsearg\numberedsubsecyyy}
+\def\numberedsubsecyyy #1{\numhead2{#1}} % normally calls numberedsubseczzz
+\def\numberedsubseczzz #1{\seccheck{subsection}%
+\gdef\thissection{#1}\subsubsecno=0 \global\advance \subsecno by 1 %
+\subsecheading {#1}{\the\chapno}{\the\secno}{\the\subsecno}%
+{\chapternofonts%
+\edef\temp{{\realbackslash subsecentry %
+{#1}{\the\chapno}{\the\secno}{\the\subsecno}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\donoderef %
+\penalty 10000 %
+}}
+
+\outer\def\appendixsubsec{\parsearg\appendixsubsecyyy}
+\def\appendixsubsecyyy #1{\apphead2{#1}} % normally calls appendixsubseczzz
+\def\appendixsubseczzz #1{\seccheck{appendixsubsec}%
+\gdef\thissection{#1}\subsubsecno=0 \global\advance \subsecno by 1 %
+\subsecheading {#1}{\appendixletter}{\the\secno}{\the\subsecno}%
+{\chapternofonts%
+\edef\temp{{\realbackslash subsecentry %
+{#1}{\appendixletter}{\the\secno}{\the\subsecno}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\appendixnoderef %
+\penalty 10000 %
+}}
+
+\outer\def\unnumberedsubsec{\parsearg\unnumberedsubsecyyy}
+\def\unnumberedsubsecyyy #1{\unnmhead2{#1}} %normally calls unnumberedsubseczzz
+\def\unnumberedsubseczzz #1{\seccheck{unnumberedsubsec}%
+\plainsecheading {#1}\gdef\thissection{#1}%
+{\chapternofonts%
+\edef\temp{{\realbackslash unnumbsubsecentry{#1}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\unnumbnoderef %
+\penalty 10000 %
+}}
+
+\outer\def\numberedsubsubsec{\parsearg\numberedsubsubsecyyy}
+\def\numberedsubsubsecyyy #1{\numhead3{#1}} % normally numberedsubsubseczzz
+\def\numberedsubsubseczzz #1{\seccheck{subsubsection}%
+\gdef\thissection{#1}\global\advance \subsubsecno by 1 %
+\subsubsecheading {#1}
+ {\the\chapno}{\the\secno}{\the\subsecno}{\the\subsubsecno}%
+{\chapternofonts%
+\edef\temp{{\realbackslash subsubsecentry %
+ {#1}
+ {\the\chapno}{\the\secno}{\the\subsecno}{\the\subsubsecno}
+ {\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\donoderef %
+\penalty 10000 %
+}}
+
+\outer\def\appendixsubsubsec{\parsearg\appendixsubsubsecyyy}
+\def\appendixsubsubsecyyy #1{\apphead3{#1}} % normally appendixsubsubseczzz
+\def\appendixsubsubseczzz #1{\seccheck{appendixsubsubsec}%
+\gdef\thissection{#1}\global\advance \subsubsecno by 1 %
+\subsubsecheading {#1}
+ {\appendixletter}{\the\secno}{\the\subsecno}{\the\subsubsecno}%
+{\chapternofonts%
+\edef\temp{{\realbackslash subsubsecentry{#1}%
+ {\appendixletter}
+ {\the\secno}{\the\subsecno}{\the\subsubsecno}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\appendixnoderef %
+\penalty 10000 %
+}}
+
+\outer\def\unnumberedsubsubsec{\parsearg\unnumberedsubsubsecyyy}
+\def\unnumberedsubsubsecyyy #1{\unnmhead3{#1}} %normally unnumberedsubsubseczzz
+\def\unnumberedsubsubseczzz #1{\seccheck{unnumberedsubsubsec}%
+\plainsecheading {#1}\gdef\thissection{#1}%
+{\chapternofonts%
+\edef\temp{{\realbackslash unnumbsubsubsecentry{#1}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\unnumbnoderef %
+\penalty 10000 %
+}}
+
+% These are variants which are not "outer", so they can appear in @ifinfo.
+% Actually, they should now be obsolete; ordinary section commands should work.
+\def\infotop{\parsearg\unnumberedzzz}
+\def\infounnumbered{\parsearg\unnumberedzzz}
+\def\infounnumberedsec{\parsearg\unnumberedseczzz}
+\def\infounnumberedsubsec{\parsearg\unnumberedsubseczzz}
+\def\infounnumberedsubsubsec{\parsearg\unnumberedsubsubseczzz}
+
+\def\infoappendix{\parsearg\appendixzzz}
+\def\infoappendixsec{\parsearg\appendixseczzz}
+\def\infoappendixsubsec{\parsearg\appendixsubseczzz}
+\def\infoappendixsubsubsec{\parsearg\appendixsubsubseczzz}
+
+\def\infochapter{\parsearg\chapterzzz}
+\def\infosection{\parsearg\sectionzzz}
+\def\infosubsection{\parsearg\subsectionzzz}
+\def\infosubsubsection{\parsearg\subsubsectionzzz}
+
+% These macros control what the section commands do, according
+% to what kind of chapter we are in (ordinary, appendix, or unnumbered).
+% Define them by default for a numbered chapter.
+\global\let\section = \numberedsec
+\global\let\subsection = \numberedsubsec
+\global\let\subsubsection = \numberedsubsubsec
+
+% Define @majorheading, @heading and @subheading
+
+% NOTE on use of \vbox for chapter headings, section headings, and
+% such:
+% 1) We use \vbox rather than the earlier \line to permit
+% overlong headings to fold.
+% 2) \hyphenpenalty is set to 10000 because hyphenation in a
+% heading is obnoxious; this forbids it.
+% 3) Likewise, headings look best if no \parindent is used, and
+% if justification is not attempted. Hence \raggedright.
+
+
+\def\majorheading{\parsearg\majorheadingzzz}
+\def\majorheadingzzz #1{%
+{\advance\chapheadingskip by 10pt \chapbreak }%
+{\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
+ \parindent=0pt\raggedright
+ \rm #1\hfill}}\bigskip \par\penalty 200}
+
+\def\chapheading{\parsearg\chapheadingzzz}
+\def\chapheadingzzz #1{\chapbreak %
+{\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
+ \parindent=0pt\raggedright
+ \rm #1\hfill}}\bigskip \par\penalty 200}
+
+\def\heading{\parsearg\secheadingi}
+
+\def\subheading{\parsearg\subsecheadingi}
+
+\def\subsubheading{\parsearg\subsubsecheadingi}
+
+% These macros generate a chapter, section, etc. heading only
+% (including whitespace, linebreaking, etc. around it),
+% given all the information in convenient, parsed form.
+
+%%% Args are the skip and penalty (usually negative)
+\def\dobreak#1#2{\par\ifdim\lastskip<#1\removelastskip\penalty#2\vskip#1\fi}
+
+\def\setchapterstyle #1 {\csname CHAPF#1\endcsname}
+
+%%% Define plain chapter starts, and page on/off switching for it
+% Parameter controlling skip before chapter headings (if needed)
+
+\newskip \chapheadingskip \chapheadingskip = 30pt plus 8pt minus 4pt
+
+\def\chapbreak{\dobreak \chapheadingskip {-4000}}
+\def\chappager{\par\vfill\supereject}
+\def\chapoddpage{\chappager \ifodd\pageno \else \hbox to 0pt{} \chappager\fi}
+
+\def\setchapternewpage #1 {\csname CHAPPAG#1\endcsname}
+
+\def\CHAPPAGoff{
+\global\let\pchapsepmacro=\chapbreak
+\global\let\pagealignmacro=\chappager}
+
+\def\CHAPPAGon{
+\global\let\pchapsepmacro=\chappager
+\global\let\pagealignmacro=\chappager
+\global\def\HEADINGSon{\HEADINGSsingle}}
+
+\def\CHAPPAGodd{
+\global\let\pchapsepmacro=\chapoddpage
+\global\let\pagealignmacro=\chapoddpage
+\global\def\HEADINGSon{\HEADINGSdouble}}
+
+\CHAPPAGon
+
+\def\CHAPFplain{
+\global\let\chapmacro=\chfplain
+\global\let\unnumbchapmacro=\unnchfplain}
+
+\def\chfplain #1#2{%
+ \pchapsepmacro
+ {%
+ \chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
+ \parindent=0pt\raggedright
+ \rm #2\enspace #1}%
+ }%
+ \bigskip
+ \penalty5000
+}
+
+\def\unnchfplain #1{%
+\pchapsepmacro %
+{\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
+ \parindent=0pt\raggedright
+ \rm #1\hfill}}\bigskip \par\penalty 10000 %
+}
+\CHAPFplain % The default
+
+\def\unnchfopen #1{%
+\chapoddpage {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
+ \parindent=0pt\raggedright
+ \rm #1\hfill}}\bigskip \par\penalty 10000 %
+}
+
+\def\chfopen #1#2{\chapoddpage {\chapfonts
+\vbox to 3in{\vfil \hbox to\hsize{\hfil #2} \hbox to\hsize{\hfil #1} \vfil}}%
+\par\penalty 5000 %
+}
+
+\def\CHAPFopen{
+\global\let\chapmacro=\chfopen
+\global\let\unnumbchapmacro=\unnchfopen}
+
+% Parameter controlling skip before section headings.
+
+\newskip \subsecheadingskip \subsecheadingskip = 17pt plus 8pt minus 4pt
+\def\subsecheadingbreak{\dobreak \subsecheadingskip {-500}}
+
+\newskip \secheadingskip \secheadingskip = 21pt plus 8pt minus 4pt
+\def\secheadingbreak{\dobreak \secheadingskip {-1000}}
+
+% @paragraphindent is defined for the Info formatting commands only.
+\let\paragraphindent=\comment
+
+% Section fonts are the base font at magstep2, which produces
+% a size a bit more than 14 points in the default situation.
+
+\def\secheading #1#2#3{\secheadingi {#2.#3\enspace #1}}
+\def\plainsecheading #1{\secheadingi {#1}}
+\def\secheadingi #1{{\advance \secheadingskip by \parskip %
+\secheadingbreak}%
+{\secfonts \vbox{\hyphenpenalty=10000\tolerance=5000
+ \parindent=0pt\raggedright
+ \rm #1\hfill}}%
+\ifdim \parskip<10pt \kern 10pt\kern -\parskip\fi \penalty 10000 }
+
+
+% Subsection fonts are the base font at magstep1,
+% which produces a size of 12 points.
+
+\def\subsecheading #1#2#3#4{\subsecheadingi {#2.#3.#4\enspace #1}}
+\def\subsecheadingi #1{{\advance \subsecheadingskip by \parskip %
+\subsecheadingbreak}%
+{\subsecfonts \vbox{\hyphenpenalty=10000\tolerance=5000
+ \parindent=0pt\raggedright
+ \rm #1\hfill}}%
+\ifdim \parskip<10pt \kern 10pt\kern -\parskip\fi \penalty 10000 }
+
+\def\subsubsecfonts{\subsecfonts} % Maybe this should change:
+ % Perhaps make sssec fonts scaled
+ % magstep half
+\def\subsubsecheading #1#2#3#4#5{\subsubsecheadingi {#2.#3.#4.#5\enspace #1}}
+\def\subsubsecheadingi #1{{\advance \subsecheadingskip by \parskip %
+\subsecheadingbreak}%
+{\subsubsecfonts \vbox{\hyphenpenalty=10000\tolerance=5000
+ \parindent=0pt\raggedright
+ \rm #1\hfill}}%
+\ifdim \parskip<10pt \kern 10pt\kern -\parskip\fi \penalty 10000}
+
+
+\message{toc printing,}
+
+% Finish up the main text and prepare to read what we've written
+% to \contentsfile.
+
+\newskip\contentsrightmargin \contentsrightmargin=1in
+\def\startcontents#1{%
+ \pagealignmacro
+ \immediate\closeout \contentsfile
+ \ifnum \pageno>0
+ \pageno = -1 % Request roman numbered pages.
+ \fi
+ % Don't need to put `Contents' or `Short Contents' in the headline.
+ % It is abundantly clear what they are.
+ \unnumbchapmacro{#1}\def\thischapter{}%
+ \begingroup % Set up to handle contents files properly.
+ \catcode`\\=0 \catcode`\{=1 \catcode`\}=2 \catcode`\@=11
+ \catcode`\^=7 % to see ^^e4 as \"a etc. juha@piuha.ydi.vtt.fi
+ \raggedbottom % Worry more about breakpoints than the bottom.
+ \advance\hsize by -\contentsrightmargin % Don't use the full line length.
+}
+
+
+% Normal (long) toc.
+\outer\def\contents{%
+ \startcontents{\putwordTableofContents}%
+ \input \jobname.toc
+ \endgroup
+ \vfill \eject
+}
+
+% And just the chapters.
+\outer\def\summarycontents{%
+ \startcontents{\putwordShortContents}%
+ %
+ \let\chapentry = \shortchapentry
+ \let\unnumbchapentry = \shortunnumberedentry
+ % We want a true roman here for the page numbers.
+ \secfonts
+ \let\rm=\shortcontrm \let\bf=\shortcontbf \let\sl=\shortcontsl
+ \rm
+ \advance\baselineskip by 1pt % Open it up a little.
+ \def\secentry ##1##2##3##4{}
+ \def\unnumbsecentry ##1##2{}
+ \def\subsecentry ##1##2##3##4##5{}
+ \def\unnumbsubsecentry ##1##2{}
+ \def\subsubsecentry ##1##2##3##4##5##6{}
+ \def\unnumbsubsubsecentry ##1##2{}
+ \input \jobname.toc
+ \endgroup
+ \vfill \eject
+}
+\let\shortcontents = \summarycontents
+
+% These macros generate individual entries in the table of contents.
+% The first argument is the chapter or section name.
+% The last argument is the page number.
+% The arguments in between are the chapter number, section number, ...
+
+% Chapter-level things, for both the long and short contents.
+\def\chapentry#1#2#3{\dochapentry{#2\labelspace#1}{#3}}
+
+% See comments in \dochapentry re vbox and related settings
+\def\shortchapentry#1#2#3{%
+ \tocentry{\shortchaplabel{#2}\labelspace #1}{\doshortpageno{#3}}%
+}
+
+% Typeset the label for a chapter or appendix for the short contents.
+% The arg is, e.g. `Appendix A' for an appendix, or `3' for a chapter.
+% We could simplify the code here by writing out an \appendixentry
+% command in the toc file for appendices, instead of using \chapentry
+% for both, but it doesn't seem worth it.
+\setbox0 = \hbox{\shortcontrm \putwordAppendix }
+\newdimen\shortappendixwidth \shortappendixwidth = \wd0
+
+\def\shortchaplabel#1{%
+ % We typeset #1 in a box of constant width, regardless of the text of
+ % #1, so the chapter titles will come out aligned.
+ \setbox0 = \hbox{#1}%
+ \dimen0 = \ifdim\wd0 > \shortappendixwidth \shortappendixwidth \else 0pt \fi
+ %
+ % This space should be plenty, since a single number is .5em, and the
+ % widest letter (M) is 1em, at least in the Computer Modern fonts.
+ % (This space doesn't include the extra space that gets added after
+ % the label; that gets put in in \shortchapentry above.)
+ \advance\dimen0 by 1.1em
+ \hbox to \dimen0{#1\hfil}%
+}
+
+\def\unnumbchapentry#1#2{\dochapentry{#1}{#2}}
+\def\shortunnumberedentry#1#2{\tocentry{#1}{\doshortpageno{#2}}}
+
+% Sections.
+\def\secentry#1#2#3#4{\dosecentry{#2.#3\labelspace#1}{#4}}
+\def\unnumbsecentry#1#2{\dosecentry{#1}{#2}}
+
+% Subsections.
+\def\subsecentry#1#2#3#4#5{\dosubsecentry{#2.#3.#4\labelspace#1}{#5}}
+\def\unnumbsubsecentry#1#2{\dosubsecentry{#1}{#2}}
+
+% And subsubsections.
+\def\subsubsecentry#1#2#3#4#5#6{%
+ \dosubsubsecentry{#2.#3.#4.#5\labelspace#1}{#6}}
+\def\unnumbsubsubsecentry#1#2{\dosubsubsecentry{#1}{#2}}
+
+
+% This parameter controls the indentation of the various levels.
+\newdimen\tocindent \tocindent = 3pc
+
+% Now for the actual typesetting. In all these, #1 is the text and #2 is the
+% page number.
+%
+% If the toc has to be broken over pages, we would want to be at chapters
+% if at all possible; hence the \penalty.
+\def\dochapentry#1#2{%
+ \penalty-300 \vskip\baselineskip
+ \begingroup
+ \chapentryfonts
+ \tocentry{#1}{\dopageno{#2}}%
+ \endgroup
+ \nobreak\vskip .25\baselineskip
+}
+
+\def\dosecentry#1#2{\begingroup
+ \secentryfonts \leftskip=\tocindent
+ \tocentry{#1}{\dopageno{#2}}%
+\endgroup}
+
+\def\dosubsecentry#1#2{\begingroup
+ \subsecentryfonts \leftskip=2\tocindent
+ \tocentry{#1}{\dopageno{#2}}%
+\endgroup}
+
+\def\dosubsubsecentry#1#2{\begingroup
+ \subsubsecentryfonts \leftskip=3\tocindent
+ \tocentry{#1}{\dopageno{#2}}%
+\endgroup}
+
+% Final typesetting of a toc entry; we use the same \entry macro as for
+% the index entries, but we want to suppress hyphenation here. (We
+% can't do that in the \entry macro, since index entries might consist
+% of hyphenated-identifiers-that-do-not-fit-on-a-line-and-nothing-else.)
+%
+% \turnoffactive is for the sake of @" used for umlauts.
+\def\tocentry#1#2{\begingroup
+ \hyphenpenalty = 10000
+ \entry{\turnoffactive #1}{\turnoffactive #2}%
+\endgroup}
+
+% Space between chapter (or whatever) number and the title.
+\def\labelspace{\hskip1em \relax}
+
+\def\dopageno#1{{\rm #1}}
+\def\doshortpageno#1{{\rm #1}}
+
+\def\chapentryfonts{\secfonts \rm}
+\def\secentryfonts{\textfonts}
+\let\subsecentryfonts = \textfonts
+\let\subsubsecentryfonts = \textfonts
+
+
+\message{environments,}
+
+% Since these characters are used in examples, it should be an even number of
+% \tt widths. Each \tt character is 1en, so two makes it 1em.
+% Furthermore, these definitions must come after we define our fonts.
+\newbox\dblarrowbox \newbox\longdblarrowbox
+\newbox\pushcharbox \newbox\bullbox
+\newbox\equivbox \newbox\errorbox
+
+\let\ptexequiv = \equiv
+
+%{\tentt
+%\global\setbox\dblarrowbox = \hbox to 1em{\hfil$\Rightarrow$\hfil}
+%\global\setbox\longdblarrowbox = \hbox to 1em{\hfil$\mapsto$\hfil}
+%\global\setbox\pushcharbox = \hbox to 1em{\hfil$\dashv$\hfil}
+%\global\setbox\equivbox = \hbox to 1em{\hfil$\ptexequiv$\hfil}
+% Adapted from the manmac format (p.420 of TeXbook)
+%\global\setbox\bullbox = \hbox to 1em{\kern.15em\vrule height .75ex width .85ex
+% depth .1ex\hfil}
+%}
+
+\def\point{$\star$}
+
+\def\result{\leavevmode\raise.15ex\hbox to 1em{\hfil$\Rightarrow$\hfil}}
+\def\expansion{\leavevmode\raise.1ex\hbox to 1em{\hfil$\mapsto$\hfil}}
+\def\print{\leavevmode\lower.1ex\hbox to 1em{\hfil$\dashv$\hfil}}
+
+\def\equiv{\leavevmode\lower.1ex\hbox to 1em{\hfil$\ptexequiv$\hfil}}
+
+% Adapted from the TeXbook's \boxit.
+{\tentt \global\dimen0 = 3em}% Width of the box.
+\dimen2 = .55pt % Thickness of rules
+% The text. (`r' is open on the right, `e' somewhat less so on the left.)
+\setbox0 = \hbox{\kern-.75pt \tensf error\kern-1.5pt}
+
+\global\setbox\errorbox=\hbox to \dimen0{\hfil
+ \hsize = \dimen0 \advance\hsize by -5.8pt % Space to left+right.
+ \advance\hsize by -2\dimen2 % Rules.
+ \vbox{
+ \hrule height\dimen2
+ \hbox{\vrule width\dimen2 \kern3pt % Space to left of text.
+ \vtop{\kern2.4pt \box0 \kern2.4pt}% Space above/below.
+ \kern3pt\vrule width\dimen2}% Space to right.
+ \hrule height\dimen2}
+ \hfil}
+
+% The @error{} command.
+\def\error{\leavevmode\lower.7ex\copy\errorbox}
+
+% @tex ... @end tex escapes into raw Tex temporarily.
+% One exception: @ is still an escape character, so that @end tex works.
+% But \@ or @@ will get a plain tex @ character.
+
+\def\tex{\begingroup
+\catcode `\\=0 \catcode `\{=1 \catcode `\}=2
+\catcode `\$=3 \catcode `\&=4 \catcode `\#=6
+\catcode `\^=7 \catcode `\_=8 \catcode `\~=13 \let~=\tie
+\catcode `\%=14
+\catcode 43=12
+\catcode`\"=12
+\catcode`\==12
+\catcode`\|=12
+\catcode`\<=12
+\catcode`\>=12
+\escapechar=`\\
+%
+\let\~=\ptextilde
+\let\{=\ptexlbrace
+\let\}=\ptexrbrace
+\let\.=\ptexdot
+\let\*=\ptexstar
+\let\dots=\ptexdots
+\def\@{@}%
+\let\bullet=\ptexbullet
+\let\b=\ptexb \let\c=\ptexc \let\i=\ptexi \let\t=\ptext \let\l=\ptexl
+\let\L=\ptexL
+%
+\let\Etex=\endgroup}
+
+% Define @lisp ... @endlisp.
+% @lisp does a \begingroup so it can rebind things,
+% including the definition of @endlisp (which normally is erroneous).
+
+% Amount to narrow the margins by for @lisp.
+\newskip\lispnarrowing \lispnarrowing=0.4in
+
+% This is the definition that ^^M gets inside @lisp, @example, and other
+% such environments. \null is better than a space, since it doesn't
+% have any width.
+\def\lisppar{\null\endgraf}
+
+% Make each space character in the input produce a normal interword
+% space in the output. Don't allow a line break at this space, as this
+% is used only in environments like @example, where each line of input
+% should produce a line of output anyway.
+%
+{\obeyspaces %
+\gdef\sepspaces{\obeyspaces\let =\tie}}
+
+% Define \obeyedspace to be our active space, whatever it is. This is
+% for use in \parsearg.
+{\sepspaces%
+\global\let\obeyedspace= }
+
+% This space is always present above and below environments.
+\newskip\envskipamount \envskipamount = 0pt
+
+% Make spacing and below environment symmetrical. We use \parskip here
+% to help in doing that, since in @example-like environments \parskip
+% is reset to zero; thus the \afterenvbreak inserts no space -- but the
+% start of the next paragraph will insert \parskip
+%
+\def\aboveenvbreak{{\advance\envskipamount by \parskip
+\endgraf \ifdim\lastskip<\envskipamount
+\removelastskip \penalty-50 \vskip\envskipamount \fi}}
+
+\let\afterenvbreak = \aboveenvbreak
+
+% \nonarrowing is a flag. If "set", @lisp etc don't narrow margins.
+\let\nonarrowing=\relax
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% \cartouche: draw rectangle w/rounded corners around argument
+\font\circle=lcircle10
+\newdimen\circthick
+\newdimen\cartouter\newdimen\cartinner
+\newskip\normbskip\newskip\normpskip\newskip\normlskip
+\circthick=\fontdimen8\circle
+%
+\def\ctl{{\circle\char'013\hskip -6pt}}% 6pt from pl file: 1/2charwidth
+\def\ctr{{\hskip 6pt\circle\char'010}}
+\def\cbl{{\circle\char'012\hskip -6pt}}
+\def\cbr{{\hskip 6pt\circle\char'011}}
+\def\carttop{\hbox to \cartouter{\hskip\lskip
+ \ctl\leaders\hrule height\circthick\hfil\ctr
+ \hskip\rskip}}
+\def\cartbot{\hbox to \cartouter{\hskip\lskip
+ \cbl\leaders\hrule height\circthick\hfil\cbr
+ \hskip\rskip}}
+%
+\newskip\lskip\newskip\rskip
+
+\long\def\cartouche{%
+\begingroup
+ \lskip=\leftskip \rskip=\rightskip
+ \leftskip=0pt\rightskip=0pt %we want these *outside*.
+ \cartinner=\hsize \advance\cartinner by-\lskip
+ \advance\cartinner by-\rskip
+ \cartouter=\hsize
+ \advance\cartouter by 18pt % allow for 3pt kerns on either
+% side, and for 6pt waste from
+% each corner char
+ \normbskip=\baselineskip \normpskip=\parskip \normlskip=\lineskip
+ % Flag to tell @lisp, etc., not to narrow margin.
+ \let\nonarrowing=\comment
+ \vbox\bgroup
+ \baselineskip=0pt\parskip=0pt\lineskip=0pt
+ \carttop
+ \hbox\bgroup
+ \hskip\lskip
+ \vrule\kern3pt
+ \vbox\bgroup
+ \hsize=\cartinner
+ \kern3pt
+ \begingroup
+ \baselineskip=\normbskip
+ \lineskip=\normlskip
+ \parskip=\normpskip
+ \vskip -\parskip
+\def\Ecartouche{%
+ \endgroup
+ \kern3pt
+ \egroup
+ \kern3pt\vrule
+ \hskip\rskip
+ \egroup
+ \cartbot
+ \egroup
+\endgroup
+}}
+
+
+% This macro is called at the beginning of all the @example variants,
+% inside a group.
+\def\nonfillstart{%
+ \aboveenvbreak
+ \inENV % This group ends at the end of the body
+ \hfuzz = 12pt % Don't be fussy
+ \sepspaces % Make spaces be word-separators rather than space tokens.
+ \singlespace
+ \let\par = \lisppar % don't ignore blank lines
+ \obeylines % each line of input is a line of output
+ \parskip = 0pt
+ \parindent = 0pt
+ \emergencystretch = 0pt % don't try to avoid overfull boxes
+ % @cartouche defines \nonarrowing to inhibit narrowing
+ % at next level down.
+ \ifx\nonarrowing\relax
+ \advance \leftskip by \lispnarrowing
+ \exdentamount=\lispnarrowing
+ \let\exdent=\nofillexdent
+ \let\nonarrowing=\relax
+ \fi
+}
+
+% To ending an @example-like environment, we first end the paragraph
+% (via \afterenvbreak's vertical glue), and then the group. That way we
+% keep the zero \parskip that the environments set -- \parskip glue
+% will be inserted at the beginning of the next paragraph in the
+% document, after the environment.
+%
+\def\nonfillfinish{\afterenvbreak\endgroup}%
+
+% This macro is
+\def\lisp{\begingroup
+ \nonfillstart
+ \let\Elisp = \nonfillfinish
+ \tt
+ \rawbackslash % have \ input char produce \ char from current font
+ \gobble
+}
+
+% Define the \E... control sequence only if we are inside the
+% environment, so the error checking in \end will work.
+%
+% We must call \lisp last in the definition, since it reads the
+% return following the @example (or whatever) command.
+%
+\def\example{\begingroup \def\Eexample{\nonfillfinish\endgroup}\lisp}
+\def\smallexample{\begingroup \def\Esmallexample{\nonfillfinish\endgroup}\lisp}
+\def\smalllisp{\begingroup \def\Esmalllisp{\nonfillfinish\endgroup}\lisp}
+
+% @smallexample and @smalllisp. This is not used unless the @smallbook
+% command is given. Originally contributed by Pavel@xerox.
+%
+\def\smalllispx{\begingroup
+ \nonfillstart
+ \let\Esmalllisp = \nonfillfinish
+ \let\Esmallexample = \nonfillfinish
+ %
+ % Smaller interline space and fonts for small examples.
+ \setleading{10pt}%
+ \indexfonts \tt
+ \rawbackslash % make \ output the \ character from the current font (tt)
+ \gobble
+}
+
+% This is @display; same as @lisp except use roman font.
+%
+\def\display{\begingroup
+ \nonfillstart
+ \let\Edisplay = \nonfillfinish
+ \gobble
+}
+
+% This is @format; same as @display except don't narrow margins.
+%
+\def\format{\begingroup
+ \let\nonarrowing = t
+ \nonfillstart
+ \let\Eformat = \nonfillfinish
+ \gobble
+}
+
+% @flushleft (same as @format) and @flushright.
+%
+\def\flushleft{\begingroup
+ \let\nonarrowing = t
+ \nonfillstart
+ \let\Eflushleft = \nonfillfinish
+ \gobble
+}
+\def\flushright{\begingroup
+ \let\nonarrowing = t
+ \nonfillstart
+ \let\Eflushright = \nonfillfinish
+ \advance\leftskip by 0pt plus 1fill
+ \gobble}
+
+% @quotation does normal linebreaking (hence we can't use \nonfillstart)
+% and narrows the margins.
+%
+\def\quotation{%
+ \begingroup\inENV %This group ends at the end of the @quotation body
+ {\parskip=0pt \aboveenvbreak}% because \aboveenvbreak inserts \parskip
+ \singlespace
+ \parindent=0pt
+ % We have retained a nonzero parskip for the environment, since we're
+ % doing normal filling. So to avoid extra space below the environment...
+ \def\Equotation{\parskip = 0pt \nonfillfinish}%
+ %
+ % @cartouche defines \nonarrowing to inhibit narrowing at next level down.
+ \ifx\nonarrowing\relax
+ \advance\leftskip by \lispnarrowing
+ \advance\rightskip by \lispnarrowing
+ \exdentamount = \lispnarrowing
+ \let\nonarrowing = \relax
+ \fi
+}
+
+\message{defuns,}
+% Define formatter for defuns
+% First, allow user to change definition object font (\df) internally
+\def\setdeffont #1 {\csname DEF#1\endcsname}
+
+\newskip\defbodyindent \defbodyindent=.4in
+\newskip\defargsindent \defargsindent=50pt
+\newskip\deftypemargin \deftypemargin=12pt
+\newskip\deflastargmargin \deflastargmargin=18pt
+
+\newcount\parencount
+% define \functionparens, which makes ( and ) and & do special things.
+% \functionparens affects the group it is contained in.
+\def\activeparens{%
+\catcode`\(=\active \catcode`\)=\active \catcode`\&=\active
+\catcode`\[=\active \catcode`\]=\active}
+
+% Make control sequences which act like normal parenthesis chars.
+\let\lparen = ( \let\rparen = )
+
+{\activeparens % Now, smart parens don't turn on until &foo (see \amprm)
+
+% Be sure that we always have a definition for `(', etc. For example,
+% if the fn name has parens in it, \boldbrax will not be in effect yet,
+% so TeX would otherwise complain about undefined control sequence.
+\global\let(=\lparen \global\let)=\rparen
+\global\let[=\lbrack \global\let]=\rbrack
+
+\gdef\functionparens{\boldbrax\let&=\amprm\parencount=0 }
+\gdef\boldbrax{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb}
+% This is used to turn on special parens
+% but make & act ordinary (given that it's active).
+\gdef\boldbraxnoamp{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb\let&=\ampnr}
+
+% Definitions of (, ) and & used in args for functions.
+% This is the definition of ( outside of all parentheses.
+\gdef\oprm#1 {{\rm\char`\(}#1 \bf \let(=\opnested %
+\global\advance\parencount by 1 }
+%
+% This is the definition of ( when already inside a level of parens.
+\gdef\opnested{\char`\(\global\advance\parencount by 1 }
+%
+\gdef\clrm{% Print a paren in roman if it is taking us back to depth of 0.
+% also in that case restore the outer-level definition of (.
+\ifnum \parencount=1 {\rm \char `\)}\sl \let(=\oprm \else \char `\) \fi
+\global\advance \parencount by -1 }
+% If we encounter &foo, then turn on ()-hacking afterwards
+\gdef\amprm#1 {{\rm\&#1}\let(=\oprm \let)=\clrm\ }
+%
+\gdef\normalparens{\boldbrax\let&=\ampnr}
+} % End of definition inside \activeparens
+%% These parens (in \boldbrax) actually are a little bolder than the
+%% contained text. This is especially needed for [ and ]
+\def\opnr{{\sf\char`\(}} \def\clnr{{\sf\char`\)}} \def\ampnr{\&}
+\def\lbrb{{\bf\char`\[}} \def\rbrb{{\bf\char`\]}}
+
+% First, defname, which formats the header line itself.
+% #1 should be the function name.
+% #2 should be the type of definition, such as "Function".
+
+\def\defname #1#2{%
+% Get the values of \leftskip and \rightskip as they were
+% outside the @def...
+\dimen2=\leftskip
+\advance\dimen2 by -\defbodyindent
+\dimen3=\rightskip
+\advance\dimen3 by -\defbodyindent
+\noindent %
+\setbox0=\hbox{\hskip \deflastargmargin{\rm #2}\hskip \deftypemargin}%
+\dimen0=\hsize \advance \dimen0 by -\wd0 % compute size for first line
+\dimen1=\hsize \advance \dimen1 by -\defargsindent %size for continuations
+\parshape 2 0in \dimen0 \defargsindent \dimen1 %
+% Now output arg 2 ("Function" or some such)
+% ending at \deftypemargin from the right margin,
+% but stuck inside a box of width 0 so it does not interfere with linebreaking
+{% Adjust \hsize to exclude the ambient margins,
+% so that \rightline will obey them.
+\advance \hsize by -\dimen2 \advance \hsize by -\dimen3
+\rlap{\rightline{{\rm #2}\hskip \deftypemargin}}}%
+% Make all lines underfull and no complaints:
+\tolerance=10000 \hbadness=10000
+\advance\leftskip by -\defbodyindent
+\exdentamount=\defbodyindent
+{\df #1}\enskip % Generate function name
+}
+
+% Actually process the body of a definition
+% #1 should be the terminating control sequence, such as \Edefun.
+% #2 should be the "another name" control sequence, such as \defunx.
+% #3 should be the control sequence that actually processes the header,
+% such as \defunheader.
+
+\def\defparsebody #1#2#3{\begingroup\inENV% Environment for definitionbody
+\medbreak %
+% Define the end token that this defining construct specifies
+% so that it will exit this group.
+\def#1{\endgraf\endgroup\medbreak}%
+\def#2{\begingroup\obeylines\activeparens\spacesplit#3}%
+\parindent=0in
+\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent
+\exdentamount=\defbodyindent
+\begingroup %
+\catcode 61=\active % 61 is `='
+\obeylines\activeparens\spacesplit#3}
+
+\def\defmethparsebody #1#2#3#4 {\begingroup\inENV %
+\medbreak %
+% Define the end token that this defining construct specifies
+% so that it will exit this group.
+\def#1{\endgraf\endgroup\medbreak}%
+\def#2##1 {\begingroup\obeylines\activeparens\spacesplit{#3{##1}}}%
+\parindent=0in
+\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent
+\exdentamount=\defbodyindent
+\begingroup\obeylines\activeparens\spacesplit{#3{#4}}}
+
+\def\defopparsebody #1#2#3#4#5 {\begingroup\inENV %
+\medbreak %
+% Define the end token that this defining construct specifies
+% so that it will exit this group.
+\def#1{\endgraf\endgroup\medbreak}%
+\def#2##1 ##2 {\def#4{##1}%
+\begingroup\obeylines\activeparens\spacesplit{#3{##2}}}%
+\parindent=0in
+\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent
+\exdentamount=\defbodyindent
+\begingroup\obeylines\activeparens\spacesplit{#3{#5}}}
+
+% These parsing functions are similar to the preceding ones
+% except that they do not make parens into active characters.
+% These are used for "variables" since they have no arguments.
+
+\def\defvarparsebody #1#2#3{\begingroup\inENV% Environment for definitionbody
+\medbreak %
+% Define the end token that this defining construct specifies
+% so that it will exit this group.
+\def#1{\endgraf\endgroup\medbreak}%
+\def#2{\begingroup\obeylines\spacesplit#3}%
+\parindent=0in
+\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent
+\exdentamount=\defbodyindent
+\begingroup %
+\catcode 61=\active %
+\obeylines\spacesplit#3}
+
+% This is used for \def{tp,vr}parsebody. It could probably be used for
+% some of the others, too, with some judicious conditionals.
+%
+\def\parsebodycommon#1#2#3{%
+ \begingroup\inENV %
+ \medbreak %
+ % Define the end token that this defining construct specifies
+ % so that it will exit this group.
+ \def#1{\endgraf\endgroup\medbreak}%
+ \def#2##1 {\begingroup\obeylines\spacesplit{#3{##1}}}%
+ \parindent=0in
+ \advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent
+ \exdentamount=\defbodyindent
+ \begingroup\obeylines
+}
+
+\def\defvrparsebody#1#2#3#4 {%
+ \parsebodycommon{#1}{#2}{#3}%
+ \spacesplit{#3{#4}}%
+}
+
+% This loses on `@deftp {Data Type} {struct termios}' -- it thinks the
+% type is just `struct', because we lose the braces in `{struct
+% termios}' when \spacesplit reads its undelimited argument. Sigh.
+% \let\deftpparsebody=\defvrparsebody
+%
+% So, to get around this, we put \empty in with the type name. That
+% way, TeX won't find exactly `{...}' as an undelimited argument, and
+% won't strip off the braces.
+%
+\def\deftpparsebody #1#2#3#4 {%
+ \parsebodycommon{#1}{#2}{#3}%
+ \spacesplit{\parsetpheaderline{#3{#4}}}\empty
+}
+
+% Fine, but then we have to eventually remove the \empty *and* the
+% braces (if any). That's what this does, putting the result in \tptemp.
+%
+\def\removeemptybraces\empty#1\relax{\def\tptemp{#1}}%
+
+% After \spacesplit has done its work, this is called -- #1 is the final
+% thing to call, #2 the type name (which starts with \empty), and #3
+% (which might be empty) the arguments.
+%
+\def\parsetpheaderline#1#2#3{%
+ \removeemptybraces#2\relax
+ #1{\tptemp}{#3}%
+}%
+
+\def\defopvarparsebody #1#2#3#4#5 {\begingroup\inENV %
+\medbreak %
+% Define the end token that this defining construct specifies
+% so that it will exit this group.
+\def#1{\endgraf\endgroup\medbreak}%
+\def#2##1 ##2 {\def#4{##1}%
+\begingroup\obeylines\spacesplit{#3{##2}}}%
+\parindent=0in
+\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent
+\exdentamount=\defbodyindent
+\begingroup\obeylines\spacesplit{#3{#5}}}
+
+% Split up #2 at the first space token.
+% call #1 with two arguments:
+% the first is all of #2 before the space token,
+% the second is all of #2 after that space token.
+% If #2 contains no space token, all of it is passed as the first arg
+% and the second is passed as empty.
+
+{\obeylines
+\gdef\spacesplit#1#2^^M{\endgroup\spacesplitfoo{#1}#2 \relax\spacesplitfoo}%
+\long\gdef\spacesplitfoo#1#2 #3#4\spacesplitfoo{%
+\ifx\relax #3%
+#1{#2}{}\else #1{#2}{#3#4}\fi}}
+
+% So much for the things common to all kinds of definitions.
+
+% Define @defun.
+
+% First, define the processing that is wanted for arguments of \defun
+% Use this to expand the args and terminate the paragraph they make up
+
+\def\defunargs #1{\functionparens \sl
+% Expand, preventing hyphenation at `-' chars.
+% Note that groups don't affect changes in \hyphenchar.
+\hyphenchar\tensl=0
+#1%
+\hyphenchar\tensl=45
+\ifnum\parencount=0 \else \errmessage{unbalanced parens in @def arguments}\fi%
+\interlinepenalty=10000
+\advance\rightskip by 0pt plus 1fil
+\endgraf\penalty 10000\vskip -\parskip\penalty 10000%
+}
+
+\def\deftypefunargs #1{%
+% Expand, preventing hyphenation at `-' chars.
+% Note that groups don't affect changes in \hyphenchar.
+% Use \boldbraxnoamp, not \functionparens, so that & is not special.
+\boldbraxnoamp
+\tclose{#1}% avoid \code because of side effects on active chars
+\interlinepenalty=10000
+\advance\rightskip by 0pt plus 1fil
+\endgraf\penalty 10000\vskip -\parskip\penalty 10000%
+}
+
+% Do complete processing of one @defun or @defunx line already parsed.
+
+% @deffn Command forward-char nchars
+
+\def\deffn{\defmethparsebody\Edeffn\deffnx\deffnheader}
+
+\def\deffnheader #1#2#3{\doind {fn}{\code{#2}}%
+\begingroup\defname {#2}{#1}\defunargs{#3}\endgroup %
+\catcode 61=\other % Turn off change made in \defparsebody
+}
+
+% @defun == @deffn Function
+
+\def\defun{\defparsebody\Edefun\defunx\defunheader}
+
+\def\defunheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index
+\begingroup\defname {#1}{Function}%
+\defunargs {#2}\endgroup %
+\catcode 61=\other % Turn off change made in \defparsebody
+}
+
+% @deftypefun int foobar (int @var{foo}, float @var{bar})
+
+\def\deftypefun{\defparsebody\Edeftypefun\deftypefunx\deftypefunheader}
+
+% #1 is the data type. #2 is the name and args.
+\def\deftypefunheader #1#2{\deftypefunheaderx{#1}#2 \relax}
+% #1 is the data type, #2 the name, #3 the args.
+\def\deftypefunheaderx #1#2 #3\relax{%
+\doind {fn}{\code{#2}}% Make entry in function index
+\begingroup\defname {\defheaderxcond#1\relax$$$#2}{Function}%
+\deftypefunargs {#3}\endgroup %
+\catcode 61=\other % Turn off change made in \defparsebody
+}
+
+% @deftypefn {Library Function} int foobar (int @var{foo}, float @var{bar})
+
+\def\deftypefn{\defmethparsebody\Edeftypefn\deftypefnx\deftypefnheader}
+
+% \defheaderxcond#1\relax$$$
+% puts #1 in @code, followed by a space, but does nothing if #1 is null.
+\def\defheaderxcond#1#2$$${\ifx#1\relax\else\code{#1#2} \fi}
+
+% #1 is the classification. #2 is the data type. #3 is the name and args.
+\def\deftypefnheader #1#2#3{\deftypefnheaderx{#1}{#2}#3 \relax}
+% #1 is the classification, #2 the data type, #3 the name, #4 the args.
+\def\deftypefnheaderx #1#2#3 #4\relax{%
+\doind {fn}{\code{#3}}% Make entry in function index
+\begingroup
+\normalparens % notably, turn off `&' magic, which prevents
+% at least some C++ text from working
+\defname {\defheaderxcond#2\relax$$$#3}{#1}%
+\deftypefunargs {#4}\endgroup %
+\catcode 61=\other % Turn off change made in \defparsebody
+}
+
+% @defmac == @deffn Macro
+
+\def\defmac{\defparsebody\Edefmac\defmacx\defmacheader}
+
+\def\defmacheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index
+\begingroup\defname {#1}{Macro}%
+\defunargs {#2}\endgroup %
+\catcode 61=\other % Turn off change made in \defparsebody
+}
+
+% @defspec == @deffn Special Form
+
+\def\defspec{\defparsebody\Edefspec\defspecx\defspecheader}
+
+\def\defspecheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index
+\begingroup\defname {#1}{Special Form}%
+\defunargs {#2}\endgroup %
+\catcode 61=\other % Turn off change made in \defparsebody
+}
+
+% This definition is run if you use @defunx
+% anywhere other than immediately after a @defun or @defunx.
+
+\def\deffnx #1 {\errmessage{@deffnx in invalid context}}
+\def\defunx #1 {\errmessage{@defunx in invalid context}}
+\def\defmacx #1 {\errmessage{@defmacx in invalid context}}
+\def\defspecx #1 {\errmessage{@defspecx in invalid context}}
+\def\deftypefnx #1 {\errmessage{@deftypefnx in invalid context}}
+\def\deftypeunx #1 {\errmessage{@deftypeunx in invalid context}}
+
+% @defmethod, and so on
+
+% @defop {Funny Method} foo-class frobnicate argument
+
+\def\defop #1 {\def\defoptype{#1}%
+\defopparsebody\Edefop\defopx\defopheader\defoptype}
+
+\def\defopheader #1#2#3{%
+\dosubind {fn}{\code{#2}}{on #1}% Make entry in function index
+\begingroup\defname {#2}{\defoptype{} on #1}%
+\defunargs {#3}\endgroup %
+}
+
+% @defmethod == @defop Method
+
+\def\defmethod{\defmethparsebody\Edefmethod\defmethodx\defmethodheader}
+
+\def\defmethodheader #1#2#3{%
+\dosubind {fn}{\code{#2}}{on #1}% entry in function index
+\begingroup\defname {#2}{Method on #1}%
+\defunargs {#3}\endgroup %
+}
+
+% @defcv {Class Option} foo-class foo-flag
+
+\def\defcv #1 {\def\defcvtype{#1}%
+\defopvarparsebody\Edefcv\defcvx\defcvarheader\defcvtype}
+
+\def\defcvarheader #1#2#3{%
+\dosubind {vr}{\code{#2}}{of #1}% Make entry in var index
+\begingroup\defname {#2}{\defcvtype{} of #1}%
+\defvarargs {#3}\endgroup %
+}
+
+% @defivar == @defcv {Instance Variable}
+
+\def\defivar{\defvrparsebody\Edefivar\defivarx\defivarheader}
+
+\def\defivarheader #1#2#3{%
+\dosubind {vr}{\code{#2}}{of #1}% Make entry in var index
+\begingroup\defname {#2}{Instance Variable of #1}%
+\defvarargs {#3}\endgroup %
+}
+
+% These definitions are run if you use @defmethodx, etc.,
+% anywhere other than immediately after a @defmethod, etc.
+
+\def\defopx #1 {\errmessage{@defopx in invalid context}}
+\def\defmethodx #1 {\errmessage{@defmethodx in invalid context}}
+\def\defcvx #1 {\errmessage{@defcvx in invalid context}}
+\def\defivarx #1 {\errmessage{@defivarx in invalid context}}
+
+% Now @defvar
+
+% First, define the processing that is wanted for arguments of @defvar.
+% This is actually simple: just print them in roman.
+% This must expand the args and terminate the paragraph they make up
+\def\defvarargs #1{\normalparens #1%
+\interlinepenalty=10000
+\endgraf\penalty 10000\vskip -\parskip\penalty 10000}
+
+% @defvr Counter foo-count
+
+\def\defvr{\defvrparsebody\Edefvr\defvrx\defvrheader}
+
+\def\defvrheader #1#2#3{\doind {vr}{\code{#2}}%
+\begingroup\defname {#2}{#1}\defvarargs{#3}\endgroup}
+
+% @defvar == @defvr Variable
+
+\def\defvar{\defvarparsebody\Edefvar\defvarx\defvarheader}
+
+\def\defvarheader #1#2{\doind {vr}{\code{#1}}% Make entry in var index
+\begingroup\defname {#1}{Variable}%
+\defvarargs {#2}\endgroup %
+}
+
+% @defopt == @defvr {User Option}
+
+\def\defopt{\defvarparsebody\Edefopt\defoptx\defoptheader}
+
+\def\defoptheader #1#2{\doind {vr}{\code{#1}}% Make entry in var index
+\begingroup\defname {#1}{User Option}%
+\defvarargs {#2}\endgroup %
+}
+
+% @deftypevar int foobar
+
+\def\deftypevar{\defvarparsebody\Edeftypevar\deftypevarx\deftypevarheader}
+
+% #1 is the data type. #2 is the name.
+\def\deftypevarheader #1#2{%
+\doind {vr}{\code{#2}}% Make entry in variables index
+\begingroup\defname {\defheaderxcond#1\relax$$$#2}{Variable}%
+\interlinepenalty=10000
+\endgraf\penalty 10000\vskip -\parskip\penalty 10000
+\endgroup}
+
+% @deftypevr {Global Flag} int enable
+
+\def\deftypevr{\defvrparsebody\Edeftypevr\deftypevrx\deftypevrheader}
+
+\def\deftypevrheader #1#2#3{\doind {vr}{\code{#3}}%
+\begingroup\defname {\defheaderxcond#2\relax$$$#3}{#1}
+\interlinepenalty=10000
+\endgraf\penalty 10000\vskip -\parskip\penalty 10000
+\endgroup}
+
+% This definition is run if you use @defvarx
+% anywhere other than immediately after a @defvar or @defvarx.
+
+\def\defvrx #1 {\errmessage{@defvrx in invalid context}}
+\def\defvarx #1 {\errmessage{@defvarx in invalid context}}
+\def\defoptx #1 {\errmessage{@defoptx in invalid context}}
+\def\deftypevarx #1 {\errmessage{@deftypevarx in invalid context}}
+\def\deftypevrx #1 {\errmessage{@deftypevrx in invalid context}}
+
+% Now define @deftp
+% Args are printed in bold, a slight difference from @defvar.
+
+\def\deftpargs #1{\bf \defvarargs{#1}}
+
+% @deftp Class window height width ...
+
+\def\deftp{\deftpparsebody\Edeftp\deftpx\deftpheader}
+
+\def\deftpheader #1#2#3{\doind {tp}{\code{#2}}%
+\begingroup\defname {#2}{#1}\deftpargs{#3}\endgroup}
+
+% This definition is run if you use @deftpx, etc
+% anywhere other than immediately after a @deftp, etc.
+
+\def\deftpx #1 {\errmessage{@deftpx in invalid context}}
+
+\message{cross reference,}
+% Define cross-reference macros
+\newwrite \auxfile
+
+\newif\ifhavexrefs % True if xref values are known.
+\newif\ifwarnedxrefs % True if we warned once that they aren't known.
+
+% \setref{foo} defines a cross-reference point named foo.
+
+\def\setref#1{%
+\dosetq{#1-title}{Ytitle}%
+\dosetq{#1-pg}{Ypagenumber}%
+\dosetq{#1-snt}{Ysectionnumberandtype}}
+
+\def\unnumbsetref#1{%
+\dosetq{#1-title}{Ytitle}%
+\dosetq{#1-pg}{Ypagenumber}%
+\dosetq{#1-snt}{Ynothing}}
+
+\def\appendixsetref#1{%
+\dosetq{#1-title}{Ytitle}%
+\dosetq{#1-pg}{Ypagenumber}%
+\dosetq{#1-snt}{Yappendixletterandtype}}
+
+% \xref, \pxref, and \ref generate cross-references to specified points.
+% For \xrefX, #1 is the node name, #2 the name of the Info
+% cross-reference, #3 the printed node name, #4 the name of the Info
+% file, #5 the name of the printed manual. All but the node name can be
+% omitted.
+%
+\def\pxref#1{\putwordsee{} \xrefX[#1,,,,,,,]}
+\def\xref#1{\putwordSee{} \xrefX[#1,,,,,,,]}
+\def\ref#1{\xrefX[#1,,,,,,,]}
+\def\xrefX[#1,#2,#3,#4,#5,#6]{\begingroup
+ \def\printedmanual{\ignorespaces #5}%
+ \def\printednodename{\ignorespaces #3}%
+ \setbox1=\hbox{\printedmanual}%
+ \setbox0=\hbox{\printednodename}%
+ \ifdim \wd0 = 0pt
+ % No printed node name was explicitly given.
+ \expandafter\ifx\csname SETxref-automatic-section-title\endcsname\relax
+ % Use the node name inside the square brackets.
+ \def\printednodename{\ignorespaces #1}%
+ \else
+ % Use the actual chapter/section title appear inside
+ % the square brackets. Use the real section title if we have it.
+ \ifdim \wd1>0pt%
+ % It is in another manual, so we don't have it.
+ \def\printednodename{\ignorespaces #1}%
+ \else
+ \ifhavexrefs
+ % We know the real title if we have the xref values.
+ \def\printednodename{\refx{#1-title}{}}%
+ \else
+ % Otherwise just copy the Info node name.
+ \def\printednodename{\ignorespaces #1}%
+ \fi%
+ \fi
+ \fi
+ \fi
+ %
+ % If we use \unhbox0 and \unhbox1 to print the node names, TeX does not
+ % insert empty discretionaries after hyphens, which means that it will
+ % not find a line break at a hyphen in a node names. Since some manuals
+ % are best written with fairly long node names, containing hyphens, this
+ % is a loss. Therefore, we give the text of the node name again, so it
+ % is as if TeX is seeing it for the first time.
+ \ifdim \wd1 > 0pt
+ \putwordsection{} ``\printednodename'' in \cite{\printedmanual}%
+ \else
+ % _ (for example) has to be the character _ for the purposes of the
+ % control sequence corresponding to the node, but it has to expand
+ % into the usual \leavevmode...\vrule stuff for purposes of
+ % printing. So we \turnoffactive for the \refx-snt, back on for the
+ % printing, back off for the \refx-pg.
+ {\turnoffactive \refx{#1-snt}{}}%
+ \space [\printednodename],\space
+ \turnoffactive \putwordpage\tie\refx{#1-pg}{}%
+ \fi
+\endgroup}
+
+% \dosetq is the interface for calls from other macros
+
+% Use \turnoffactive so that punctuation chars such as underscore
+% work in node names.
+\def\dosetq #1#2{{\let\folio=0 \turnoffactive \auxhat%
+\edef\next{\write\auxfile{\internalsetq {#1}{#2}}}%
+\next}}
+
+% \internalsetq {foo}{page} expands into
+% CHARACTERS 'xrdef {foo}{...expansion of \Ypage...}
+% When the aux file is read, ' is the escape character
+
+\def\internalsetq #1#2{'xrdef {#1}{\csname #2\endcsname}}
+
+% Things to be expanded by \internalsetq
+
+\def\Ypagenumber{\folio}
+
+\def\Ytitle{\thissection}
+
+\def\Ynothing{}
+
+\def\Ysectionnumberandtype{%
+\ifnum\secno=0 \putwordChapter\xreftie\the\chapno %
+\else \ifnum \subsecno=0 \putwordSection\xreftie\the\chapno.\the\secno %
+\else \ifnum \subsubsecno=0 %
+\putwordSection\xreftie\the\chapno.\the\secno.\the\subsecno %
+\else %
+\putwordSection\xreftie\the\chapno.\the\secno.\the\subsecno.\the\subsubsecno %
+\fi \fi \fi }
+
+\def\Yappendixletterandtype{%
+\ifnum\secno=0 \putwordAppendix\xreftie'char\the\appendixno{}%
+\else \ifnum \subsecno=0 \putwordSection\xreftie'char\the\appendixno.\the\secno %
+\else \ifnum \subsubsecno=0 %
+\putwordSection\xreftie'char\the\appendixno.\the\secno.\the\subsecno %
+\else %
+\putwordSection\xreftie'char\the\appendixno.\the\secno.\the\subsecno.\the\subsubsecno %
+\fi \fi \fi }
+
+\gdef\xreftie{'tie}
+
+% Use TeX 3.0's \inputlineno to get the line number, for better error
+% messages, but if we're using an old version of TeX, don't do anything.
+%
+\ifx\inputlineno\thisisundefined
+ \let\linenumber = \empty % Non-3.0.
+\else
+ \def\linenumber{\the\inputlineno:\space}
+\fi
+
+% Define \refx{NAME}{SUFFIX} to reference a cross-reference string named NAME.
+% If its value is nonempty, SUFFIX is output afterward.
+
+\def\refx#1#2{%
+ \expandafter\ifx\csname X#1\endcsname\relax
+ % If not defined, say something at least.
+ $\langle$un\-de\-fined$\rangle$%
+ \ifhavexrefs
+ \message{\linenumber Undefined cross reference `#1'.}%
+ \else
+ \ifwarnedxrefs\else
+ \global\warnedxrefstrue
+ \message{Cross reference values unknown; you must run TeX again.}%
+ \fi
+ \fi
+ \else
+ % It's defined, so just use it.
+ \csname X#1\endcsname
+ \fi
+ #2% Output the suffix in any case.
+}
+
+% Read the last existing aux file, if any. No error if none exists.
+
+% This is the macro invoked by entries in the aux file.
+\def\xrdef #1#2{
+{\catcode`\'=\other\expandafter \gdef \csname X#1\endcsname {#2}}}
+
+\def\readauxfile{%
+\begingroup
+\catcode `\^^@=\other
+\catcode `\=\other
+\catcode `\=\other
+\catcode `\^^C=\other
+\catcode `\^^D=\other
+\catcode `\^^E=\other
+\catcode `\^^F=\other
+\catcode `\^^G=\other
+\catcode `\^^H=\other
+\catcode `\ =\other
+\catcode `\^^L=\other
+\catcode `\=\other
+\catcode `\=\other
+\catcode `\=\other
+\catcode `\=\other
+\catcode `\=\other
+\catcode `\=\other
+\catcode `\=\other
+\catcode `\=\other
+\catcode `\=\other
+\catcode `\=\other
+\catcode `\=\other
+\catcode `\=\other
+\catcode 26=\other
+\catcode `\^^[=\other
+\catcode `\^^\=\other
+\catcode `\^^]=\other
+\catcode `\^^^=\other
+\catcode `\^^_=\other
+\catcode `\@=\other
+\catcode `\^=\other
+\catcode `\~=\other
+\catcode `\[=\other
+\catcode `\]=\other
+\catcode`\"=\other
+\catcode`\_=\other
+\catcode`\|=\other
+\catcode`\<=\other
+\catcode`\>=\other
+\catcode `\$=\other
+\catcode `\#=\other
+\catcode `\&=\other
+% `\+ does not work, so use 43.
+\catcode 43=\other
+% Make the characters 128-255 be printing characters
+{%
+ \count 1=128
+ \def\loop{%
+ \catcode\count 1=\other
+ \advance\count 1 by 1
+ \ifnum \count 1<256 \loop \fi
+ }%
+}%
+% the aux file uses ' as the escape.
+% Turn off \ as an escape so we do not lose on
+% entries which were dumped with control sequences in their names.
+% For example, 'xrdef {$\leq $-fun}{page ...} made by @defun ^^
+% Reference to such entries still does not work the way one would wish,
+% but at least they do not bomb out when the aux file is read in.
+\catcode `\{=1 \catcode `\}=2
+\catcode `\%=\other
+\catcode `\'=0
+\catcode`\^=7 % to make ^^e4 etc usable in xref tags
+\catcode `\\=\other
+\openin 1 \jobname.aux
+\ifeof 1 \else \closein 1 \input \jobname.aux \global\havexrefstrue
+\global\warnedobstrue
+\fi
+% Open the new aux file. Tex will close it automatically at exit.
+\openout \auxfile=\jobname.aux
+\endgroup}
+
+
+% Footnotes.
+
+\newcount \footnoteno
+
+% The trailing space in the following definition for supereject is
+% vital for proper filling; pages come out unaligned when you do a
+% pagealignmacro call if that space before the closing brace is
+% removed.
+\def\supereject{\par\penalty -20000\footnoteno =0 }
+
+% @footnotestyle is meaningful for info output only..
+\let\footnotestyle=\comment
+
+\let\ptexfootnote=\footnote
+
+{\catcode `\@=11
+%
+% Auto-number footnotes. Otherwise like plain.
+\gdef\footnote{%
+ \global\advance\footnoteno by \@ne
+ \edef\thisfootno{$^{\the\footnoteno}$}%
+ %
+ % In case the footnote comes at the end of a sentence, preserve the
+ % extra spacing after we do the footnote number.
+ \let\@sf\empty
+ \ifhmode\edef\@sf{\spacefactor\the\spacefactor}\/\fi
+ %
+ % Remove inadvertent blank space before typesetting the footnote number.
+ \unskip
+ \thisfootno\@sf
+ \footnotezzz
+}%
+
+% Don't bother with the trickery in plain.tex to not require the
+% footnote text as a parameter. Our footnotes don't need to be so general.
+%
+\long\gdef\footnotezzz#1{\insert\footins{%
+ % We want to typeset this text as a normal paragraph, even if the
+ % footnote reference occurs in (for example) a display environment.
+ % So reset some parameters.
+ \interlinepenalty\interfootnotelinepenalty
+ \splittopskip\ht\strutbox % top baseline for broken footnotes
+ \splitmaxdepth\dp\strutbox
+ \floatingpenalty\@MM
+ \leftskip\z@skip
+ \rightskip\z@skip
+ \spaceskip\z@skip
+ \xspaceskip\z@skip
+ \parindent\defaultparindent
+ %
+ % Hang the footnote text off the number.
+ \hang
+ \textindent{\thisfootno}%
+ %
+ % Don't crash into the line above the footnote text. Since this
+ % expands into a box, it must come within the paragraph, lest it
+ % provide a place where TeX can split the footnote.
+ \footstrut
+ #1\strut}%
+}
+
+}%end \catcode `\@=11
+
+% Set the baselineskip to #1, and the lineskip and strut size
+% correspondingly. There is no deep meaning behind these magic numbers
+% used as factors; they just match (closely enough) what Knuth defined.
+%
+\def\lineskipfactor{.08333}
+\def\strutheightpercent{.70833}
+\def\strutdepthpercent {.29167}
+%
+\def\setleading#1{%
+ \normalbaselineskip = #1\relax
+ \normallineskip = \lineskipfactor\normalbaselineskip
+ \normalbaselines
+ \setbox\strutbox =\hbox{%
+ \vrule width0pt height\strutheightpercent\baselineskip
+ depth \strutdepthpercent \baselineskip
+ }%
+}
+
+% @| inserts a changebar to the left of the current line. It should
+% surround any changed text. This approach does *not* work if the
+% change spans more than two lines of output. To handle that, we would
+% have adopt a much more difficult approach (putting marks into the main
+% vertical list for the beginning and end of each change).
+%
+\def\|{%
+ % \vadjust can only be used in horizontal mode.
+ \leavevmode
+ %
+ % Append this vertical mode material after the current line in the output.
+ \vadjust{%
+ % We want to insert a rule with the height and depth of the current
+ % leading; that is exactly what \strutbox is supposed to record.
+ \vskip-\baselineskip
+ %
+ % \vadjust-items are inserted at the left edge of the type. So
+ % the \llap here moves out into the left-hand margin.
+ \llap{%
+ %
+ % For a thicker or thinner bar, change the `1pt'.
+ \vrule height\baselineskip width1pt
+ %
+ % This is the space between the bar and the text.
+ \hskip 12pt
+ }%
+ }%
+}
+
+% For a final copy, take out the rectangles
+% that mark overfull boxes (in case you have decided
+% that the text looks ok even though it passes the margin).
+%
+\def\finalout{\overfullrule=0pt}
+
+
+% End of control word definitions.
+
+\message{and turning on texinfo input format.}
+
+\def\openindices{%
+ \newindex{cp}%
+ \newcodeindex{fn}%
+ \newcodeindex{vr}%
+ \newcodeindex{tp}%
+ \newcodeindex{ky}%
+ \newcodeindex{pg}%
+}
+
+% Set some numeric style parameters, for 8.5 x 11 format.
+
+%\hsize = 6.5in
+\newdimen\defaultparindent \defaultparindent = 15pt
+\parindent = \defaultparindent
+\parskip 18pt plus 1pt
+\setleading{15pt}
+\advance\topskip by 1.2cm
+
+% Prevent underfull vbox error messages.
+\vbadness=10000
+
+% Following George Bush, just get rid of widows and orphans.
+\widowpenalty=10000
+\clubpenalty=10000
+
+% Use TeX 3.0's \emergencystretch to help line breaking, but if we're
+% using an old version of TeX, don't do anything. We want the amount of
+% stretch added to depend on the line length, hence the dependence on
+% \hsize. This makes it come to about 9pt for the 8.5x11 format.
+%
+\ifx\emergencystretch\thisisundefined
+ % Allow us to assign to \emergencystretch anyway.
+ \def\emergencystretch{\dimen0}%
+\else
+ \emergencystretch = \hsize
+ \divide\emergencystretch by 45
+\fi
+
+% Use @smallbook to reset parameters for 7x9.5 format (or else 7x9.25)
+\def\smallbook{
+
+% These values for secheadingskip and subsecheadingskip are
+% experiments. RJC 7 Aug 1992
+\global\secheadingskip = 17pt plus 6pt minus 3pt
+\global\subsecheadingskip = 14pt plus 6pt minus 3pt
+
+\global\lispnarrowing = 0.3in
+\setleading{12pt}
+\advance\topskip by -1cm
+\global\parskip 3pt plus 1pt
+\global\hsize = 5in
+\global\vsize=7.5in
+\global\tolerance=700
+\global\hfuzz=1pt
+\global\contentsrightmargin=0pt
+\global\deftypemargin=0pt
+\global\defbodyindent=.5cm
+
+\global\pagewidth=\hsize
+\global\pageheight=\vsize
+
+\global\let\smalllisp=\smalllispx
+\global\let\smallexample=\smalllispx
+\global\def\Esmallexample{\Esmalllisp}
+}
+
+% Use @afourpaper to print on European A4 paper.
+\def\afourpaper{
+\global\tolerance=700
+\global\hfuzz=1pt
+\setleading{12pt}
+\global\parskip 15pt plus 1pt
+
+\global\vsize= 53\baselineskip
+\advance\vsize by \topskip
+%\global\hsize= 5.85in % A4 wide 10pt
+\global\hsize= 6.5in
+\global\outerhsize=\hsize
+\global\advance\outerhsize by 0.5in
+\global\outervsize=\vsize
+\global\advance\outervsize by 0.6in
+
+\global\pagewidth=\hsize
+\global\pageheight=\vsize
+}
+
+% Allow control of the text dimensions. Parameters in order: textheight;
+% textwidth; voffset; hoffset; binding offset; topskip.
+% All require a dimension;
+% header is additional; added length extends the bottom of the page.
+
+\def\changepagesizes#1#2#3#4#5#6{
+ \global\vsize= #1
+ \global\topskip= #6
+ \advance\vsize by \topskip
+ \global\voffset= #3
+ \global\hsize= #2
+ \global\outerhsize=\hsize
+ \global\advance\outerhsize by 0.5in
+ \global\outervsize=\vsize
+ \global\advance\outervsize by 0.6in
+ \global\pagewidth=\hsize
+ \global\pageheight=\vsize
+ \global\normaloffset= #4
+ \global\bindingoffset= #5}
+
+% A specific text layout, 24x15cm overall, intended for A4 paper. Top margin
+% 29mm, hence bottom margin 28mm, nominal side margin 3cm.
+\def\afourlatex
+ {\global\tolerance=700
+ \global\hfuzz=1pt
+ \setleading{12pt}
+ \global\parskip 15pt plus 1pt
+ \advance\baselineskip by 1.6pt
+ \changepagesizes{237mm}{150mm}{3.6mm}{3.6mm}{3mm}{7mm}
+ }
+
+% Use @afourwide to print on European A4 paper in wide format.
+\def\afourwide{\afourpaper
+\changepagesizes{9.5in}{6.5in}{\hoffset}{\normaloffset}{\bindingoffset}{7mm}}
+
+% Define macros to output various characters with catcode for normal text.
+\catcode`\"=\other
+\catcode`\~=\other
+\catcode`\^=\other
+\catcode`\_=\other
+\catcode`\|=\other
+\catcode`\<=\other
+\catcode`\>=\other
+\catcode`\+=\other
+\def\normaldoublequote{"}
+\def\normaltilde{~}
+\def\normalcaret{^}
+\def\normalunderscore{_}
+\def\normalverticalbar{|}
+\def\normalless{<}
+\def\normalgreater{>}
+\def\normalplus{+}
+
+% This macro is used to make a character print one way in ttfont
+% where it can probably just be output, and another way in other fonts,
+% where something hairier probably needs to be done.
+%
+% #1 is what to print if we are indeed using \tt; #2 is what to print
+% otherwise. Since all the Computer Modern typewriter fonts have zero
+% interword stretch (and shrink), and it is reasonable to expect all
+% typewriter fonts to have this, we can check that font parameter.
+%
+\def\ifusingtt#1#2{\ifdim \fontdimen3\the\font=0pt #1\else #2\fi}
+
+% Turn off all special characters except @
+% (and those which the user can use as if they were ordinary).
+% Most of these we simply print from the \tt font, but for some, we can
+% use math or other variants that look better in normal text.
+
+\catcode`\"=\active
+\def\activedoublequote{{\tt \char '042}}
+\let"=\activedoublequote
+\catcode`\~=\active
+\def~{{\tt \char '176}}
+\chardef\hat=`\^
+\catcode`\^=\active
+\def\auxhat{\def^{'hat}}
+\def^{{\tt \hat}}
+
+\catcode`\_=\active
+\def_{\ifusingtt\normalunderscore\_}
+% Subroutine for the previous macro.
+\def\_{\leavevmode \kern.06em \vbox{\hrule width.3em height.1ex}}
+
+\catcode`\|=\active
+\def|{{\tt \char '174}}
+\chardef \less=`\<
+\catcode`\<=\active
+\def<{{\tt \less}}
+\chardef \gtr=`\>
+\catcode`\>=\active
+\def>{{\tt \gtr}}
+\catcode`\+=\active
+\def+{{\tt \char 43}}
+%\catcode 27=\active
+%\def^^[{$\diamondsuit$}
+
+% Set up an active definition for =, but don't enable it most of the time.
+{\catcode`\==\active
+\global\def={{\tt \char 61}}}
+
+\catcode`+=\active
+\catcode`\_=\active
+
+% If a .fmt file is being used, characters that might appear in a file
+% name cannot be active until we have parsed the command line.
+% So turn them off again, and have \everyjob (or @setfilename) turn them on.
+% \otherifyactive is called near the end of this file.
+\def\otherifyactive{\catcode`+=\other \catcode`\_=\other}
+
+\catcode`\@=0
+
+% \rawbackslashxx output one backslash character in current font
+\global\chardef\rawbackslashxx=`\\
+%{\catcode`\\=\other
+%@gdef@rawbackslashxx{\}}
+
+% \rawbackslash redefines \ as input to do \rawbackslashxx.
+{\catcode`\\=\active
+@gdef@rawbackslash{@let\=@rawbackslashxx }}
+
+% \normalbackslash outputs one backslash in fixed width font.
+\def\normalbackslash{{\tt\rawbackslashxx}}
+
+% Say @foo, not \foo, in error messages.
+\escapechar=`\@
+
+% \catcode 17=0 % Define control-q
+\catcode`\\=\active
+
+% Used sometimes to turn off (effectively) the active characters
+% even after parsing them.
+@def@turnoffactive{@let"=@normaldoublequote
+@let\=@realbackslash
+@let~=@normaltilde
+@let^=@normalcaret
+@let_=@normalunderscore
+@let|=@normalverticalbar
+@let<=@normalless
+@let>=@normalgreater
+@let+=@normalplus}
+
+@def@normalturnoffactive{@let"=@normaldoublequote
+@let\=@normalbackslash
+@let~=@normaltilde
+@let^=@normalcaret
+@let_=@normalunderscore
+@let|=@normalverticalbar
+@let<=@normalless
+@let>=@normalgreater
+@let+=@normalplus}
+
+% Make _ and + \other characters, temporarily.
+% This is canceled by @fixbackslash.
+@otherifyactive
+
+% If a .fmt file is being used, we don't want the `\input texinfo' to show up.
+% That is what \eatinput is for; after that, the `\' should revert to printing
+% a backslash.
+%
+@gdef@eatinput input texinfo{@fixbackslash}
+@global@let\ = @eatinput
+
+% On the other hand, perhaps the file did not have a `\input texinfo'. Then
+% the first `\{ in the file would cause an error. This macro tries to fix
+% that, assuming it is called before the first `\' could plausibly occur.
+% Also back turn on active characters that might appear in the input
+% file name, in case not using a pre-dumped format.
+%
+@gdef@fixbackslash{@ifx\@eatinput @let\ = @normalbackslash @fi
+ @catcode`+=@active @catcode`@_=@active}
+
+%% These look ok in all fonts, so just make them not special. The @rm below
+%% makes sure that the current font starts out as the newly loaded cmr10
+@catcode`@$=@other @catcode`@%=@other @catcode`@&=@other @catcode`@#=@other
+
+@textfonts
+@rm
+
+@c Local variables:
+@c page-delimiter: "^\\\\message"
+@c End:
diff --git a/lib/memcpy.c b/lib/memcpy.c
new file mode 100644
index 0000000..dba7d56
--- /dev/null
+++ b/lib/memcpy.c
@@ -0,0 +1,16 @@
+/* Copy LEN bytes starting at SRCADDR to DESTADDR. Result undefined
+ if the source overlaps with the destination.
+ Return DESTADDR. */
+
+char *
+memcpy (destaddr, srcaddr, len)
+ char *destaddr;
+ const char *srcaddr;
+ int len;
+{
+ char *dest = destaddr;
+
+ while (len-- > 0)
+ *destaddr++ = *srcaddr++;
+ return dest;
+}
diff --git a/lib/memset.c b/lib/memset.c
new file mode 100644
index 0000000..0e819f2
--- /dev/null
+++ b/lib/memset.c
@@ -0,0 +1,29 @@
+/* memset.c -- set an area of memory to a given value
+ Copyright (C) 1991 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+char *
+memset (str, c, len)
+ char *str;
+ int c;
+ unsigned len;
+{
+ register char *st = str;
+
+ while (len-- > 0)
+ *st++ = c;
+ return str;
+}
diff --git a/lib/strcspn.c b/lib/strcspn.c
new file mode 100644
index 0000000..a17357e
--- /dev/null
+++ b/lib/strcspn.c
@@ -0,0 +1,43 @@
+/* Copyright (C) 1995 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. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef HAVE_STRING_H
+# include <string.h>
+#else
+# include <strings.h>
+#endif
+
+/* Return the length of the maximum inital segment of S
+ which contains no characters from REJECT. */
+size_t
+strcspn (s, reject)
+ register const char *s;
+ register const char *reject;
+{
+ register size_t count = 0;
+
+ while (*s != '\0')
+ if (strchr (reject, *s++) == NULL)
+ ++count;
+ else
+ return count;
+
+ return count;
+}
diff --git a/lib/strpbrk.c b/lib/strpbrk.c
new file mode 100644
index 0000000..3e60201
--- /dev/null
+++ b/lib/strpbrk.c
@@ -0,0 +1,39 @@
+/* Copyright (C) 1991, 1994 Free Software Foundation, Inc.
+ NOTE: The canonical source of this file is maintained with the GNU C Library.
+ Bugs can be reported to bug-glibc@prep.ai.mit.edu.
+
+ 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, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Find the first ocurrence in S of any character in ACCEPT. */
+char *
+strpbrk (s, accept)
+ register const char *s;
+ register const char *accept;
+{
+ while (*s != '\0')
+ {
+ const char *a = accept;
+ while (*a != '\0')
+ if (*a++ == *s)
+ return (char *) s;
+ ++s;
+ }
+
+ return 0;
+}
diff --git a/lib/strspn.c b/lib/strspn.c
new file mode 100644
index 0000000..89b45ad
--- /dev/null
+++ b/lib/strspn.c
@@ -0,0 +1,44 @@
+/* Copyright (C) 1991 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include <ansidecl.h>
+#include <string.h>
+
+
+/* Return the length of the maximum initial segment
+ of S which contains only characters in ACCEPT. */
+size_t
+DEFUN(strspn, (s, accept), CONST char *s AND CONST char *accept)
+{
+ register CONST char *p;
+ register CONST char *a;
+ register size_t count = 0;
+
+ for (p = s; *p != '\0'; ++p)
+ {
+ for (a = accept; *a != '\0'; ++a)
+ if (*p == *a)
+ break;
+ if (*a == '\0')
+ return count;
+ else
+ ++count;
+ }
+
+ return count;
+}
diff --git a/lib/strstr.c b/lib/strstr.c
new file mode 100644
index 0000000..cdee621
--- /dev/null
+++ b/lib/strstr.c
@@ -0,0 +1,116 @@
+/* Copyright (C) 1994 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+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. */
+
+/*
+ * My personal strstr() implementation that beats most other algorithms.
+ * Until someone tells me otherwise, I assume that this is the
+ * fastest implementation of strstr() in C.
+ * I deliberately chose not to comment it. You should have at least
+ * as much fun trying to understand it, as I had to write it :-).
+ *
+ * Stephen R. van den Berg, berg@pool.informatik.rwth-aachen.de */
+
+#include <string.h>
+#include <sys/types.h>
+
+typedef unsigned chartype;
+
+char *
+strstr (phaystack, pneedle)
+ const char *phaystack;
+ const char *pneedle;
+{
+ register const unsigned char *haystack, *needle;
+ register chartype b, c;
+
+ haystack = (const unsigned char *) phaystack;
+ needle = (const unsigned char *) pneedle;
+
+ b = *needle;
+ if (b != '\0')
+ {
+ haystack--; /* possible ANSI violation */
+ do
+ {
+ c = *++haystack;
+ if (c == '\0')
+ goto ret0;
+ }
+ while (c != b);
+
+ c = *++needle;
+ if (c == '\0')
+ goto foundneedle;
+ ++needle;
+ goto jin;
+
+ for (;;)
+ {
+ register chartype a;
+ register const unsigned char *rhaystack, *rneedle;
+
+ do
+ {
+ a = *++haystack;
+ if (a == '\0')
+ goto ret0;
+ if (a == b)
+ break;
+ a = *++haystack;
+ if (a == '\0')
+ goto ret0;
+shloop: }
+ while (a != b);
+
+jin: a = *++haystack;
+ if (a == '\0')
+ goto ret0;
+
+ if (a != c)
+ goto shloop;
+
+ rhaystack = haystack-- + 1;
+ rneedle = needle;
+ a = *rneedle;
+
+ if (*rhaystack == a)
+ do
+ {
+ if (a == '\0')
+ goto foundneedle;
+ ++rhaystack;
+ a = *++needle;
+ if (*rhaystack != a)
+ break;
+ if (a == '\0')
+ goto foundneedle;
+ ++rhaystack;
+ a = *++needle;
+ }
+ while (*rhaystack == a);
+
+ needle = rneedle; /* took the register-poor aproach */
+
+ if (a == '\0')
+ break;
+ }
+ }
+foundneedle:
+ return (char*) haystack;
+ret0:
+ return 0;
+}
diff --git a/lib/xalloca.h b/lib/xalloca.h
new file mode 100644
index 0000000..b28632a
--- /dev/null
+++ b/lib/xalloca.h
@@ -0,0 +1,47 @@
+/* xalloca.h -- alloca declarations wrapper
+ Copyright (C) 1996 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.
+*/
+
+#ifndef _xalloca_h_
+#define _xalloca_h_ 1
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifndef alloca
+# ifdef __GNUC__
+# define alloca __builtin_alloca
+# define HAVE_ALLOCA 1
+# else
+# if defined HAVE_ALLOCA_H || defined _LIBC
+# include <alloca.h>
+# else
+# ifdef _AIX
+ #pragma alloca
+# else
+# ifndef alloca
+char *alloca ();
+# endif
+# endif
+# endif
+# endif
+#endif
+
+#define ALLOCA(t, n) ((t *) alloca (sizeof (t) * (n)))
+
+#endif /* _xalloca_h_ */
diff --git a/lib/xdirent.h b/lib/xdirent.h
new file mode 100644
index 0000000..0420fdf
--- /dev/null
+++ b/lib/xdirent.h
@@ -0,0 +1,53 @@
+/* xdirent.h -- dirent declarations wrapper
+ Copyright (C) 1996 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.
+*/
+
+#ifndef _xdirent_h_
+#define _xdirent_h_
+
+#if HAVE_DIRENT_H
+# include <dirent.h>
+#endif
+#if HAVE_NDIR_H
+# include <ndir.h>
+#endif
+#if HAVE_SYS_DIR_H
+# include <sys/dir.h>
+#endif
+#if HAVE_SYS_NDIR_H
+# include <sys/ndir.h>
+#endif
+
+/* Interpret `HAVE_LINK' as meaning `UN*X style' directory structure
+ (e.g., A single root called `/', with `/' separating links), and
+ !HAVE_LINK as `DOS|OS/2|Windows style' (e.g., Multiple root volues
+ named `x:', with `\' separating links). */
+
+#if HAVE_LINK
+# define IS_ABSOLUTE(_dir_) ((_dir_)[0] == '/')
+# define SLASH_STRING "/"
+# define SLASH_CHAR '/'
+# define DOT_DOT_SLASH "../"
+#else
+/* NEEDSWORK: prefer forward-slashes as a user-configurable option. */
+# define IS_ABSOLUTE(_dir_) ((_dir_)[1] == ':')
+# define SLASH_STRING "\\/"
+# define SLASH_CHAR '\\'
+# define DOT_DOT_SLASH "..\\"
+#endif
+
+#endif /* not _xdirent_h_ */
diff --git a/lib/xfnmatch.h b/lib/xfnmatch.h
new file mode 100644
index 0000000..a8ed2e0
--- /dev/null
+++ b/lib/xfnmatch.h
@@ -0,0 +1,34 @@
+/* xfnmatch.h -- fnmatch declarations wrapper
+ Copyright (C) 1996 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.
+*/
+
+#ifndef _xfnmatch_h_
+#define _xfnmatch_h_
+
+#include <fnmatch.h>
+
+#ifndef FNM_FILE_NAME
+# define FNM_FILE_NAME FNM_PATHNAME
+#endif
+
+#if HAVE_LINK
+# define MAYBE_FNM_CASEFOLD 0
+#else
+# define MAYBE_FNM_CASEFOLD FNM_CASEFOLD
+#endif
+
+#endif /* not _xfnmatch_h_ */
diff --git a/lib/xobstack.h b/lib/xobstack.h
new file mode 100644
index 0000000..38281cb
--- /dev/null
+++ b/lib/xobstack.h
@@ -0,0 +1,29 @@
+/* xobstack.h -- obstack declarations wrapper
+ Copyright (C) 1996 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.
+*/
+
+#ifndef _xobstack_h_
+#define _xobstack_h_
+
+#include <obstack.h>
+#include "xmalloc.h"
+
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+#define OBSTACK_ALLOC(obs, t, n) ((t *)obstack_alloc ((obs), (n)*sizeof(t)))
+
+#endif /* not _xobstack_h_ */
diff --git a/lib/xstddef.h b/lib/xstddef.h
new file mode 100644
index 0000000..e05c339
--- /dev/null
+++ b/lib/xstddef.h
@@ -0,0 +1,32 @@
+/* xstddef.h -- stddef declarations wrapper
+ Copyright (C) 1996 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.
+*/
+
+#ifndef _xstddef_h_
+#define _xstddef_h_
+
+#if HAVE_STDDEF_H
+# include <stddef.h>
+#endif
+
+#ifndef offsetof
+# define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+
+#define cardinalityof(ARRAY) (sizeof (ARRAY) / sizeof ((ARRAY)[0]))
+
+#endif /* not _xstddef_h_ */
diff --git a/lib/xstdlib.h b/lib/xstdlib.h
new file mode 100644
index 0000000..99335e7
--- /dev/null
+++ b/lib/xstdlib.h
@@ -0,0 +1,28 @@
+/* xstdlib.h -- stdlib declarations wrapper
+ Copyright (C) 1996 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.
+*/
+
+#ifndef _xstdlib_h_
+#define _xstdlib_h_ 1
+
+#if defined STDC_HEADERS || defined _LIBC || defined HAVE_STDLIB_H
+# include <stdlib.h>
+#else
+char *getenv ();
+#endif
+
+#endif /* _xstdlib_h_ */
diff --git a/lib/xstring.h b/lib/xstring.h
new file mode 100644
index 0000000..c6f79e0
--- /dev/null
+++ b/lib/xstring.h
@@ -0,0 +1,44 @@
+/* xstring.h - string declarations wrapper wrapper
+ Copyright (C) 1986, 1995, 1996 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. */
+
+#ifndef _xstring_h_
+#define _xstring_h_
+
+#define dirname sidestep_dirname_declaration
+#define basename sidestep_basename_declaration
+#ifdef HAVE_STRING_H
+# if !STDC_HEADERS && HAVE_MEMORY_H
+# include <memory.h>
+# endif
+# include <string.h>
+#else
+# include <strings.h>
+char *memchr ();
+#endif
+#undef dirname
+#undef basename
+
+#define strequ(s1, s2) (strcmp ((s1), (s2)) == 0)
+#define strnequ(s1, s2, n) (strncmp ((s1), (s2), (n)) == 0)
+
+#if !HAVE_STRNDUP
+extern char *strndup __P((char const *, size_t n));
+#endif
+extern char *basename __P((char const *));
+extern char *dirname __P((char const *));
+
+#endif /* not _xstring_h_ */
diff --git a/lib/xsysstat.h b/lib/xsysstat.h
new file mode 100644
index 0000000..8c96541
--- /dev/null
+++ b/lib/xsysstat.h
@@ -0,0 +1,70 @@
+/* xsysstat.h -- sys/stat declarations wrapper
+ Copyright (C) 1996 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. */
+
+#ifndef _xsysstat_h_
+#define _xsysstat_h_
+
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#if HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+
+#ifdef STAT_MACROS_BROKEN
+#undef S_ISBLK
+#undef S_ISCHR
+#undef S_ISDIR
+#undef S_ISFIFO
+#undef S_ISLNK
+#undef S_ISMPB
+#undef S_ISMPC
+#undef S_ISNWK
+#undef S_ISREG
+#undef S_ISSOCK
+#endif /* STAT_MACROS_BROKEN. */
+
+#if !defined(S_ISBLK) && defined(S_IFBLK)
+#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
+#endif
+#if !defined(S_ISCHR) && defined(S_IFCHR)
+#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
+#endif
+#if !defined(S_ISDIR) && defined(S_IFDIR)
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#endif
+#if !defined(S_ISREG) && defined(S_IFREG)
+#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#endif
+#if !defined(S_ISFIFO) && defined(S_IFIFO)
+#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
+#endif
+#if !defined(S_ISLNK) && defined(S_IFLNK)
+#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
+#endif
+#if !defined(S_ISSOCK) && defined(S_IFSOCK)
+#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
+#endif
+#if !defined(S_ISMPB) && defined(S_IFMPB) /* V7 */
+#define S_ISMPB(m) (((m) & S_IFMT) == S_IFMPB)
+#define S_ISMPC(m) (((m) & S_IFMT) == S_IFMPC)
+#endif
+#if !defined(S_ISNWK) && defined(S_IFNWK) /* HP/UX */
+#define S_ISNWK(m) (((m) & S_IFMT) == S_IFNWK)
+#endif
+
+#endif /* not _xsysstat_h_ */
diff --git a/lib/xunistd.h b/lib/xunistd.h
new file mode 100644
index 0000000..bfd3a97
--- /dev/null
+++ b/lib/xunistd.h
@@ -0,0 +1,44 @@
+/* xunistd.h -- unistd declarations wrapper
+ Copyright (C) 1996 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.
+*/
+
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#define sbrk sidestep_sbrk_declaration
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#undef sbrk
+void *sbrk ();
+
+#ifndef _POSIX_VERSION
+off_t lseek ();
+#endif
+
+#ifndef STDIN_FILENO
+#define STDIN_FILENO 0
+#endif
+
+#ifndef STDOUT_FILENO
+#define STDOUT_FILENO 1
+#endif
+
+#ifndef STDERR_FILENO
+#define STDERR_FILENO 2
+#endif
diff --git a/libidu/Makefile.am b/libidu/Makefile.am
new file mode 100644
index 0000000..5fde198
--- /dev/null
+++ b/libidu/Makefile.am
@@ -0,0 +1,22 @@
+## Process this file with automake to create Makefile.in
+
+AUTOMAKE_OPTIONS = ansi2knr
+
+noinst_LIBRARIES = idu
+
+idu_SOURCES = dynvec.c hash.c idfile.c idread.c idwrite.c fnprint.c \
+ scanners.c walker.c
+
+idudir = $(prefix)/share
+idu_DATA = id-lang.map
+
+noinst_HEADERS = dynvec.h hash.h idfile.h scanners.h tokflags.h xnls.h
+
+INCLUDES = -I. -I$(srcdir) \
+ -I../lib -I$(top_srcdir)/lib \
+ -I../intl -I$(top_srcdir)/intl \
+ -I.. -I$(top_srcdir)
+DEFS = -DLOCALEDIR=\"$(localedir)\" @DEFS@
+
+ansi2knr.c: $(top_srcdir)/src/ansi2knr.c
+ ln $(top_srcdir)/src/ansi2knr.c .
diff --git a/libidu/Makefile.in b/libidu/Makefile.in
new file mode 100644
index 0000000..10c086f
--- /dev/null
+++ b/libidu/Makefile.in
@@ -0,0 +1,320 @@
+# Makefile.in generated automatically by automake 1.0 from Makefile.am
+
+# Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+
+
+SHELL = /bin/sh
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+bindir = @bindir@
+sbindir = @sbindir@
+libexecdir = @libexecdir@
+datadir = @datadir@
+sysconfdir = @sysconfdir@
+sharedstatedir = @sharedstatedir@
+localstatedir = @localstatedir@
+libdir = @libdir@
+infodir = @infodir@
+mandir = @mandir@
+includedir = @includedir@
+oldincludedir = /usr/include
+
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+
+top_builddir = ..
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+transform = @program_transform_name@
+
+AUTOMAKE_OPTIONS = ansi2knr
+
+noinst_LIBRARIES = idu
+
+idu_SOURCES = dynvec.c hash.c idfile.c idread.c idwrite.c fnprint.c \
+ scanners.c walker.c
+
+idudir = $(prefix)/share
+idu_DATA = id-lang.map
+
+noinst_HEADERS = dynvec.h hash.h idfile.h scanners.h tokflags.h xnls.h
+
+INCLUDES = -I. -I$(srcdir) \
+ -I../lib -I$(top_srcdir)/lib \
+ -I../intl -I$(top_srcdir)/intl \
+ -I.. -I$(top_srcdir)
+DEFS = -DLOCALEDIR=\"$(localedir)\" @DEFS@
+mkinstalldirs = $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = ../config.h
+LIBRARIES = $(noinst_LIBRARIES)
+
+noinst_LIBFILES = libidu.a
+
+CC = @CC@
+LEX = @LEX@
+YACC = @YACC@
+CPPFLAGS = @CPPFLAGS@
+CFLAGS = @CFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBS = @LIBS@
+
+COMPILE = $(CC) -c $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS)
+LINK = $(CC) $(LDFLAGS) -o $@
+
+ANSI2KNR = @ANSI2KNR@
+o = .@U@o
+idu_LIBADD =
+idu_OBJECTS = dynvec$o hash$o idfile$o idread$o idwrite$o fnprint$o \
+scanners$o walker$o
+EXTRA_idu_SOURCES =
+LIBFILES = libidu.a
+AR = ar
+RANLIB = @RANLIB@
+DATA = $(idu_DATA)
+
+HEADERS = $(noinst_HEADERS)
+
+DIST_COMMON = Makefile.am Makefile.in
+
+
+PACKAGE = @PACKAGE@
+VERSION = @VERSION@
+
+DISTFILES = $(DIST_COMMON) $(SOURCES) $(BUILT_SOURCES) $(HEADERS) \
+ $(TEXINFOS) $(INFOS) $(MANS) $(EXTRA_DIST) $(DATA)
+DEP_DISTFILES = $(DIST_COMMON) $(SOURCES) $(BUILT_SOURCES) $(HEADERS) \
+ $(TEXINFOS) $(INFO_DEPS) $(MANS) $(EXTRA_DIST) $(DATA)
+
+TAR = tar
+SOURCES = $(idu_SOURCES)
+OBJECTS = $(idu_OBJECTS)
+
+default: all
+
+
+$(srcdir)/Makefile.in: @MAINT@Makefile.am $(top_srcdir)/configure.in
+ cd $(top_srcdir) && automake $(subdir)/Makefile
+
+Makefile: $(top_builddir)/config.status Makefile.in
+ cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+
+mostlyclean-noinstLIBRARIES:
+
+clean-noinstLIBRARIES:
+ rm -f $(noinst_LIBFILES)
+
+distclean-noinstLIBRARIES:
+
+maintainer-clean-noinstLIBRARIES:
+
+.c.o:
+ $(COMPILE) $<
+
+mostlyclean-compile:
+ rm -f *.o core
+
+clean-compile:
+
+distclean-compile:
+ rm -f *.tab.c
+
+maintainer-clean-compile:
+
+.c._c:
+ $(ANSI2KNR) $< > $*.tmp && mv $*.tmp $@
+
+._c._o:
+ @echo $(COMPILE) $<
+ @rm -f _$*.c
+ @ln $< _$*.c && $(COMPILE) _$*.c && mv _$*.o $@ && rm _$*.c
+
+.c._o:
+ $(ANSI2KNR) $< > $*.tmp && mv $*.tmp $*._c
+ @echo $(COMPILE) $*._c
+ @rm -f _$*.c
+ @ln $*._c _$*.c && $(COMPILE) _$*.c && mv _$*.o $@ && rm _$*.c
+
+ansi2knr: ansi2knr.o
+ $(LINK) ansi2knr.o $(LIBS)
+
+$(OBJECTS): $(ANSI2KNR)
+ansi2knr.o: $(CONFIG_HEADER)
+
+mostlyclean-kr:
+ rm -f *._o *._c _*.c _*.o
+
+clean-kr:
+
+distclean-kr:
+ rm -f ansi2knr
+
+maintainer-clean-kr:
+$(idu_OBJECTS): ../config.h
+
+libidu.a: $(idu_OBJECTS) $(idu_LIBADD)
+ rm -f libidu.a
+ $(AR) cru libidu.a $(idu_OBJECTS) $(idu_LIBADD)
+ $(RANLIB) libidu.a
+
+install-iduDATA: $(idu_DATA)
+ $(mkinstalldirs) $(idudir)
+ list="$(idu_DATA)"; for p in $$list; do \
+ if test -f $(srcdir)/$$p; then \
+ $(INSTALL_DATA) $(srcdir)/$$p $(idudir)/$$p; \
+ else if test -f $$p; then \
+ $(INSTALL_DATA) $$p $(idudir)/$$p; \
+ fi; fi; \
+ done
+
+uninstall-iduDATA:
+ list="$(idu_DATA)"; for p in $$list; do \
+ rm -f $(idudir)/$$p; \
+ done
+
+ID: $(HEADERS) $(SOURCES)
+ here=`pwd` && cd $(srcdir) && mkid -f$$here/ID $(SOURCES) $(HEADERS)
+
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES)
+ here=`pwd` && cd $(srcdir) && etags $(ETAGS_ARGS) $(SOURCES) $(HEADERS) -o $$here/TAGS
+
+mostlyclean-tags:
+
+clean-tags:
+
+distclean-tags:
+ rm -f TAGS ID
+
+maintainer-clean-tags:
+
+subdir = libidu
+distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
+distdir: $(DEP_DISTFILES)
+ @for file in `cd $(srcdir) && echo $(DISTFILES)`; do \
+ test -f $(distdir)/$$file \
+ || ln $(srcdir)/$$file $(distdir)/$$file 2> /dev/null \
+ || cp -p $(srcdir)/$$file $(distdir)/$$file; \
+ done
+dynvec$o: ../libidu/dynvec.c ../libidu/dynvec.h \
+ ../lib/xmalloc.h ../lib/xstring.h
+fnprint$o: ../libidu/fnprint.c ../lib/xstdlib.h \
+ ../lib/xalloca.h ../lib/xunistd.h ../libidu/idfile.h \
+ ../lib/xobstack.h ../lib/xmalloc.h ../lib/xstring.h \
+ ../libidu/hash.h ../libidu/dynvec.h ../libidu/tokflags.h \
+ ../libidu/xnls.h ../lib/pathmax.h ../lib/error.h
+hash$o: ../libidu/hash.c ../lib/xstdlib.h ../libidu/hash.h \
+ ../libidu/xnls.h ../lib/xmalloc.h ../lib/xstring.h \
+ ../lib/error.h
+idfile$o: ../libidu/idfile.c ../lib/xstdlib.h \
+ ../lib/xstring.h ../lib/xsysstat.h ../libidu/xnls.h \
+ ../lib/xobstack.h ../lib/xmalloc.h ../libidu/idfile.h \
+ ../libidu/hash.h ../libidu/dynvec.h ../libidu/tokflags.h \
+ ../lib/error.h
+idread$o: ../libidu/idread.c ../libidu/idfile.h \
+ ../lib/xobstack.h ../lib/xmalloc.h ../lib/xstring.h \
+ ../libidu/hash.h ../libidu/dynvec.h ../libidu/tokflags.h \
+ ../lib/xstddef.h ../lib/error.h ../libidu/xnls.h
+idwrite$o: ../libidu/idwrite.c ../libidu/idfile.h \
+ ../lib/xobstack.h ../lib/xmalloc.h ../lib/xstring.h \
+ ../libidu/hash.h ../libidu/dynvec.h ../libidu/tokflags.h \
+ ../libidu/xnls.h ../lib/error.h
+scanners$o: ../libidu/scanners.c ../lib/xstdlib.h \
+ ../lib/xstddef.h ../lib/xunistd.h ../lib/xsysstat.h \
+ ../lib/xstring.h ../lib/xmalloc.h ../libidu/xnls.h \
+ ../lib/error.h ../libidu/scanners.h ../lib/xobstack.h \
+ ../libidu/tokflags.h
+walker$o: ../libidu/walker.c ../lib/xsysstat.h \
+ ../lib/xstdlib.h ../lib/xstddef.h ../lib/xunistd.h \
+ ../lib/xstring.h ../lib/xfnmatch.h ../lib/xdirent.h \
+ ../libidu/xnls.h ../libidu/idfile.h ../lib/xobstack.h \
+ ../lib/xmalloc.h ../libidu/hash.h ../libidu/dynvec.h \
+ ../libidu/tokflags.h ../lib/error.h ../libidu/scanners.h \
+ ../lib/pathmax.h ../lib/xalloca.h
+
+info:
+
+dvi:
+
+check: all
+
+installcheck:
+
+install-exec:
+
+install-data: install-iduDATA
+
+install: install-exec install-data all
+ @:
+
+uninstall: uninstall-iduDATA
+
+all: $(LIBFILES) $(DATA) $(HEADERS) Makefile
+
+install-strip:
+ $(MAKE) INSTALL_PROGRAM='$(INSTALL_PROGRAM) -s' install
+installdirs:
+ $(mkinstalldirs) $(idudir)
+
+
+mostlyclean-generic:
+ test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES)
+
+clean-generic:
+ test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ rm -f Makefile $(DISTCLEANFILES)
+ rm -f config.cache config.log $(CONFIG_HEADER) stamp-h
+
+maintainer-clean-generic:
+ test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+ test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+mostlyclean: mostlyclean-noinstLIBRARIES mostlyclean-compile \
+ mostlyclean-kr mostlyclean-tags mostlyclean-generic
+
+clean: clean-noinstLIBRARIES clean-compile clean-kr clean-tags \
+ clean-generic mostlyclean
+
+distclean: distclean-noinstLIBRARIES distclean-compile distclean-kr \
+ distclean-tags distclean-generic clean
+ rm -f config.status
+
+maintainer-clean: maintainer-clean-noinstLIBRARIES \
+ maintainer-clean-compile maintainer-clean-kr \
+ maintainer-clean-tags maintainer-clean-generic \
+ distclean
+ @echo "This command is intended for maintainers to use;"
+ @echo "it deletes files that may require special tools to rebuild."
+
+.PHONY: default mostlyclean-noinstLIBRARIES distclean-noinstLIBRARIES \
+clean-noinstLIBRARIES maintainer-clean-noinstLIBRARIES \
+mostlyclean-compile distclean-compile clean-compile \
+maintainer-clean-compile mostlyclean-kr distclean-kr clean-kr \
+maintainer-clean-kr uninstall-iduDATA install-iduDATA tags \
+mostlyclean-tags distclean-tags clean-tags maintainer-clean-tags \
+distdir info dvi check installcheck install-exec install-data install \
+uninstall all installdirs mostlyclean-generic distclean-generic \
+clean-generic maintainer-clean-generic clean mostlyclean distclean \
+maintainer-clean
+
+
+ansi2knr.c: $(top_srcdir)/src/ansi2knr.c
+ ln $(top_srcdir)/src/ansi2knr.c .
+.SUFFIXES:
+.SUFFIXES: .c .o ._c ._o
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/libidu/dynvec.c b/libidu/dynvec.c
new file mode 100644
index 0000000..29844b0
--- /dev/null
+++ b/libidu/dynvec.c
@@ -0,0 +1,59 @@
+/* dynvec.c -- dynamically growable vectors
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Greg McGary <gkm@gnu.ai.mit.edu>
+
+ 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.
+*/
+
+#include <config.h>
+#include "dynvec.h"
+#include "xmalloc.h"
+
+struct dynvec *
+make_dynvec (int n)
+{
+ struct dynvec *dv = MALLOC (struct dynvec, 1);
+ dv->dv_vec = MALLOC (void *, n);
+ dv->dv_capacity = n;
+ dv->dv_fill = 0;
+ return dv;
+}
+
+void
+dynvec_free (struct dynvec *dv)
+{
+ free (dv->dv_vec);
+ free (dv);
+}
+
+void
+dynvec_freeze (struct dynvec *dv)
+{
+ if (dv->dv_fill == dv->dv_capacity)
+ return;
+ dv->dv_capacity = dv->dv_fill;
+ dv->dv_vec = REALLOC (dv->dv_vec, void *, dv->dv_capacity);
+}
+
+void
+dynvec_append (struct dynvec *dv, void *element)
+{
+ if (dv->dv_fill == dv->dv_capacity)
+ {
+ dv->dv_capacity *= 2;
+ dv->dv_vec = REALLOC (dv->dv_vec, void *, dv->dv_capacity);
+ }
+ dv->dv_vec[dv->dv_fill++] = element;
+}
diff --git a/libidu/dynvec.h b/libidu/dynvec.h
new file mode 100644
index 0000000..55fc0c2
--- /dev/null
+++ b/libidu/dynvec.h
@@ -0,0 +1,35 @@
+/* dynvec.h -- declarations for dynamically growable vectors
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Greg McGary <gkm@gnu.ai.mit.edu>
+
+ 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.
+*/
+
+#ifndef _dynvec_h_
+#define _dynvec_h_
+
+struct dynvec
+{
+ void **dv_vec;
+ int dv_capacity;
+ int dv_fill;
+};
+
+struct dynvec *make_dynvec __P((int n));
+void dynvec_free __P((struct dynvec *dv));
+void dynvec_freeze __P((struct dynvec *dv));
+void dynvec_append __P((struct dynvec *dv, void *element));
+
+#endif /* not _dynvec_h_ */
diff --git a/libidu/fnprint.c b/libidu/fnprint.c
new file mode 100644
index 0000000..ea5ea88
--- /dev/null
+++ b/libidu/fnprint.c
@@ -0,0 +1,138 @@
+/* fnprint.c -- print a list of file names
+ Copyright (C) 1986, 1995, 1996 Free Software Foundation, Inc.
+ Written by Greg McGary <gkm@gnu.ai.mit.edu>
+
+ 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. */
+
+#include <config.h>
+#include <stdio.h>
+#include "xstdlib.h"
+#include "xalloca.h"
+#include "xunistd.h"
+#include "idfile.h"
+#include "xstring.h"
+#include "xnls.h"
+#include "pathmax.h"
+#include "error.h"
+
+char const *root_name __P((char const *path));
+char const *suff_name __P((char const *path));
+int common_prefix_suffix __P((struct file_link const *flink_1, struct file_link const *flink_2));
+
+extern void usage __P((void));
+extern struct file_link *cw_dlink;
+
+/* Return the file name with the suffix stripped off. */
+
+char const *
+root_name (char const *file_name)
+{
+ static char file_name_buffer[BUFSIZ];
+ char const *dot = strrchr (file_name, '.');
+
+ if (dot)
+ {
+ int length = dot - file_name;
+ strncpy (file_name_buffer, file_name, length);
+ file_name_buffer[length] = '\0';
+ }
+ else
+ strcpy (file_name_buffer, file_name);
+ return file_name_buffer;
+}
+
+/* Return the suffix, including the dot, or an empty string if there
+ is no suffix. */
+
+char const *
+suff_name (char const *file_name)
+{
+ char const *dot = strrchr (file_name, '.');
+ return dot ? dot : "";
+}
+
+/* common_prefix_suffix returns non-zero if two file names have a
+ fully common directory prefix and a common suffix (i.e., they're
+ eligible for coalescing with brace notation. */
+
+int
+common_prefix_suffix (struct file_link const *flink_1, struct file_link const *flink_2)
+{
+ return (flink_1->fl_parent == flink_2->fl_parent
+ && strequ (suff_name (flink_1->fl_name), suff_name (flink_2->fl_name)));
+}
+
+void
+print_filenames (struct file_link **flinkv, enum separator_style separator_style)
+{
+ struct file_link const *arg;
+ struct file_link const *dlink;
+ int brace_is_open = 0;
+
+ while (*flinkv)
+ {
+ arg = *flinkv++;
+ if (*flinkv && (separator_style == ss_braces)
+ && common_prefix_suffix (arg, *flinkv))
+ {
+ if (brace_is_open)
+ printf (",%s", root_name (arg->fl_name));
+ else
+ {
+ dlink = arg->fl_parent;
+ if (dlink && dlink != cw_dlink)
+ {
+ char *file_name = ALLOCA (char, PATH_MAX);
+ maybe_relative_file_name (file_name, dlink, cw_dlink);
+ fputs (file_name, stdout);
+ putchar ('/');
+ }
+ printf ("{%s", root_name (arg->fl_name));
+ }
+ brace_is_open = 1;
+ }
+ else
+ {
+ if (brace_is_open)
+ printf (",%s}%s", root_name (arg->fl_name), suff_name (arg->fl_name));
+ else
+ {
+ char *file_name = ALLOCA (char, PATH_MAX);
+ maybe_relative_file_name (file_name, arg, cw_dlink);
+ fputs (file_name, stdout);
+ }
+ brace_is_open = 0;
+ if (*flinkv)
+ {
+ if (separator_style == ss_newline)
+ putchar ('\n');
+ else
+ putchar (' ');
+ }
+ }
+ }
+ putchar ('\n');
+}
+
+enum separator_style
+parse_separator_style (char const *arg)
+{
+ MAYBE_RETURN_PREFIX_MATCH (arg, "braces", ss_braces);
+ MAYBE_RETURN_PREFIX_MATCH (arg, "space", ss_space);
+ MAYBE_RETURN_PREFIX_MATCH (arg, "newline", ss_newline);
+ error (0, 0, _("invalid `--separator' style: `%s'"), arg);
+ usage ();
+ return ss_bogus;
+}
diff --git a/libidu/hash.c b/libidu/hash.c
new file mode 100644
index 0000000..cda26a0
--- /dev/null
+++ b/libidu/hash.c
@@ -0,0 +1,294 @@
+/* hash.c -- hash table maintenance
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Greg McGary <gkm@gnu.ai.mit.edu>
+
+ 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.
+*/
+
+#include <config.h>
+#include <stdio.h>
+#include "xstdlib.h"
+#include "hash.h"
+#include "xnls.h"
+#include "xmalloc.h"
+#include "error.h"
+
+static void hash_rehash __P((struct hash_table* ht));
+static unsigned long round_up_2 __P((unsigned long rough));
+
+/* Implement double hashing with open addressing. The table size is
+ always a power of two. The secondary (`increment') hash function
+ is forced to return an odd-value, in order to be relatively prime
+ to the table size. This guarantees that the increment can
+ potentially hit every slot in the table during collision
+ resolution. */
+
+void *hash_deleted_item = &hash_deleted_item;
+
+/* Force the table size to be a power of two, possibly rounding up the
+ given size. */
+
+void
+hash_init (struct hash_table* ht, unsigned long size,
+ hash_func_t hash_1, hash_func_t hash_2, hash_cmp_func_t hash_cmp)
+{
+ ht->ht_size = round_up_2 (size);
+ if (ht->ht_size > (128 * 1024)) /* prevent size from getting out of hand */
+ ht->ht_size /= 2;
+ ht->ht_vec = (void**) CALLOC (struct token *, ht->ht_size);
+ if (ht->ht_vec == 0)
+ error (1, 0, _("can't allocate %ld bytes for hash table: memory exhausted"),
+ ht->ht_size * sizeof(struct token *));
+ ht->ht_capacity = ht->ht_size * 15 / 16; /* 93.75% loading factor */
+ ht->ht_fill = 0;
+ ht->ht_collisions = 0;
+ ht->ht_lookups = 0;
+ ht->ht_rehashes = 0;
+ ht->ht_hash_1 = hash_1;
+ ht->ht_hash_2 = hash_2;
+ ht->ht_compare = hash_cmp;
+}
+
+/* Load an array of items into `ht'. */
+
+void
+hash_load (struct hash_table* ht, void *item_table, unsigned long cardinality, unsigned long size)
+{
+ char *items = (char *) item_table;
+ while (cardinality--)
+ {
+ hash_insert (ht, items);
+ items += size;
+ }
+}
+
+/* Returns the address of the table slot matching `key'. If `key' is
+ not found, return the address of an empty slot suitable for
+ inserting `key'. The caller is responsible for incrementing
+ ht_fill on insertion. */
+
+void **
+hash_find_slot (struct hash_table* ht, void const *key)
+{
+ void **slot;
+ void **deleted_slot = 0;
+ unsigned int hash_2 = 0;
+ unsigned int hash_1 = (*ht->ht_hash_1) (key);
+
+ ht->ht_lookups++;
+ for (;;)
+ {
+ hash_1 %= ht->ht_size;
+ slot = &ht->ht_vec[hash_1];
+
+ if (*slot == 0)
+ return slot;
+ if (*slot == hash_deleted_item)
+ {
+ if (deleted_slot == 0)
+ deleted_slot = slot;
+ }
+ else
+ {
+ if (key == *slot)
+ return slot;
+ if ((*ht->ht_compare) (key, *slot) == 0)
+ return slot;
+ ht->ht_collisions++;
+ }
+ if (!hash_2)
+ hash_2 = (*ht->ht_hash_2) (key) | 1;
+ hash_1 += hash_2;
+ }
+}
+
+void *
+hash_find_item (struct hash_table* ht, void const *key)
+{
+ void **slot = hash_find_slot (ht, key);
+ return ((HASH_VACANT (*slot)) ? 0 : *slot);
+}
+
+void *
+hash_insert (struct hash_table* ht, void *item)
+{
+ void **slot = hash_find_slot (ht, item);
+ return hash_insert_at (ht, item, slot);
+}
+
+void *
+hash_insert_at (struct hash_table* ht, void *item, void const *slot)
+{
+ void *old_item = *(void **) slot;
+ if (HASH_VACANT (old_item))
+ {
+ ht->ht_fill++;
+ old_item = item;
+ }
+ *(void const **) slot = item;
+ if (ht->ht_fill >= ht->ht_capacity)
+ hash_rehash (ht);
+ return old_item;
+}
+
+void *
+hash_delete (struct hash_table* ht, void const *item)
+{
+ void **slot = hash_find_slot (ht, item);
+ return hash_delete_at (ht, slot);
+}
+
+void *
+hash_delete_at (struct hash_table* ht, void const *slot)
+{
+ void *item = *(void **) slot;
+ if (!HASH_VACANT (item))
+ {
+ *(void const **) slot = hash_deleted_item;
+ ht->ht_fill--;
+ return item;
+ }
+ else
+ return 0;
+}
+
+void
+hash_free_items (struct hash_table* ht)
+{
+ void **vec = ht->ht_vec;
+ void **end = &vec[ht->ht_size];
+ for (; vec < end; vec++)
+ {
+ void *item = *vec;
+ if (!HASH_VACANT (item))
+ free (item);
+ *vec = 0;
+ }
+ ht->ht_fill = 0;
+}
+
+void
+hash_delete_items (struct hash_table* ht)
+{
+ void **vec = ht->ht_vec;
+ void **end = &vec[ht->ht_size];
+ for (; vec < end; vec++)
+ *vec = 0;
+ ht->ht_fill = 0;
+ ht->ht_collisions = 0;
+ ht->ht_lookups = 0;
+ ht->ht_rehashes = 0;
+}
+
+void
+hash_free (struct hash_table* ht, int free_items)
+{
+ if (free_items)
+ hash_free_items (ht);
+ free (ht->ht_vec);
+ ht->ht_vec = 0;
+ ht->ht_fill = 0;
+ ht->ht_capacity = 0;
+}
+
+void
+hash_map (struct hash_table *ht, hash_map_func_t map)
+{
+ void **slot;
+ void **end = &ht->ht_vec[ht->ht_size];
+
+ for (slot = ht->ht_vec; slot < end; slot++)
+ {
+ if (!HASH_VACANT (*slot))
+ (*map) (*slot);
+ }
+}
+
+/* Double the size of the hash table in the event of overflow... */
+
+static void
+hash_rehash (struct hash_table* ht)
+{
+ unsigned long old_ht_size = ht->ht_size;
+ void **old_vec = ht->ht_vec;
+ void **ovp;
+ void **slot;
+
+ ht->ht_size *= 2;
+ ht->ht_rehashes++;
+ ht->ht_capacity = ht->ht_size - (ht->ht_size >> 4);
+ ht->ht_vec = (void **) CALLOC (struct token *, ht->ht_size);
+
+ for (ovp = old_vec; ovp < &old_vec[old_ht_size]; ovp++)
+ {
+ if (*ovp == 0)
+ continue;
+ slot = hash_find_slot (ht, *ovp);
+ *slot = *ovp;
+ }
+ free (old_vec);
+}
+
+void
+hash_print_stats (struct hash_table *ht, FILE *out_FILE)
+{
+ fprintf (out_FILE, _("Load=%ld/%ld=%.0f%%, "), ht->ht_fill, ht->ht_size,
+ 100.0 * (double) ht->ht_fill / (double) ht->ht_size);
+ fprintf (out_FILE, _("Rehash=%d, "), ht->ht_rehashes);
+ fprintf (out_FILE, _("Collisions=%ld/%ld=%.0f%%"), ht->ht_collisions, ht->ht_lookups,
+ (ht->ht_lookups
+ ? (100.0 * (double) ht->ht_collisions / (double) ht->ht_lookups)
+ : 0));
+}
+
+/* Dump all items into a NULL-terminated vector. Use the
+ user-supplied vector, or malloc one. */
+
+void**
+hash_dump (struct hash_table *ht, void **vector_0, qsort_cmp_t compare)
+{
+ void **vector;
+ void **slot;
+ void **end = &ht->ht_vec[ht->ht_size];
+
+ if (vector_0 == 0)
+ vector_0 = MALLOC (void *, ht->ht_fill + 1);
+ vector = vector_0;
+
+ for (slot = ht->ht_vec; slot < end; slot++)
+ if (!HASH_VACANT (*slot))
+ *vector++ = *slot;
+ *vector = 0;
+
+ if (compare)
+ qsort (vector_0, ht->ht_fill, sizeof (void *), compare);
+ return vector_0;
+}
+
+/* Round a given number up to the nearest power of 2. */
+
+static unsigned long
+round_up_2 (unsigned long rough)
+{
+ int round;
+
+ round = 1;
+ while (rough)
+ {
+ round <<= 1;
+ rough >>= 1;
+ }
+ return round;
+}
diff --git a/libidu/hash.h b/libidu/hash.h
new file mode 100644
index 0000000..5661893
--- /dev/null
+++ b/libidu/hash.h
@@ -0,0 +1,144 @@
+/* hash.h -- decls for hash table
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Greg McGary <gkm@gnu.ai.mit.edu>
+
+ 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. */
+
+#ifndef _hash_h_
+#define _hash_h_
+
+#include <stdio.h>
+
+typedef unsigned long (*hash_func_t) __P((void const *key));
+typedef int (*hash_cmp_func_t) __P((void const *x, void const *y));
+typedef void (*hash_map_func_t) __P((void const *item));
+
+struct hash_table
+{
+ void **ht_vec;
+ unsigned long ht_size; /* total number of slots (power of 2) */
+ unsigned long ht_capacity; /* usable slots, limited by loading-factor */
+ unsigned long ht_fill; /* items in table */
+ unsigned long ht_collisions; /* # of failed calls to comparison function */
+ unsigned long ht_lookups; /* # of queries */
+ unsigned int ht_rehashes; /* # of times we've expanded table */
+ hash_func_t ht_hash_1; /* primary hash function */
+ hash_func_t ht_hash_2; /* secondary hash function */
+ hash_cmp_func_t ht_compare; /* comparison function */
+};
+
+typedef int (*qsort_cmp_t) __P((void const *, void const *));
+
+void hash_init __P((struct hash_table *ht, unsigned long size,
+ hash_func_t hash_1, hash_func_t hash_2, hash_cmp_func_t hash_cmp));
+void hash_load __P((struct hash_table *ht, void *item_table,
+ unsigned long cardinality, unsigned long size));
+void **hash_find_slot __P((struct hash_table *ht, void const *key));
+void *hash_find_item __P((struct hash_table *ht, void const *key));
+void *hash_insert __P((struct hash_table *ht, void *item));
+void *hash_insert_at __P((struct hash_table *ht, void *item, void const *slot));
+void *hash_delete __P((struct hash_table *ht, void const *item));
+void *hash_delete_at __P((struct hash_table *ht, void const *slot));
+void hash_delete_items __P((struct hash_table *ht));
+void hash_free_items __P((struct hash_table *ht));
+void hash_free __P((struct hash_table *ht, int free_items));
+void hash_map __P((struct hash_table *ht, hash_map_func_t map));
+void hash_print_stats __P((struct hash_table *ht, FILE *out_FILE));
+void **hash_dump __P((struct hash_table *ht, void **vector_0, qsort_cmp_t compare));
+
+extern void *hash_deleted_item;
+#define HASH_VACANT(item) ((item) == 0 || (void *) (item) == hash_deleted_item)
+
+
+/* hash and comparison macros for string keys. */
+
+#define STRING_HASH_1(_key_, _result_) { \
+ unsigned char const *kk = (unsigned char const *) (_key_) - 1; \
+ while (*++kk) \
+ (_result_) += (*kk << (kk[1] & 0xf)); \
+} while (0)
+#define return_STRING_HASH_1(_key_) do { \
+ unsigned long result = 0; \
+ STRING_HASH_1 ((_key_), result); \
+ return result; \
+} while (0)
+
+#define STRING_HASH_2(_key_, _result_) do { \
+ unsigned char const *kk = (unsigned char const *) (_key_) - 1; \
+ while (*++kk) \
+ (_result_) += (*kk << (kk[1] & 0x7)); \
+} while (0)
+#define return_STRING_HASH_2(_key_) do { \
+ unsigned long result = 0; \
+ STRING_HASH_2 ((_key_), result); \
+ return result; \
+} while (0)
+
+#define STRING_COMPARE(_x_, _y_, _result_) do { \
+ unsigned char const *xx = (unsigned char const *) (_x_) - 1; \
+ unsigned char const *yy = (unsigned char const *) (_y_) - 1; \
+ do { \
+ if (*++xx == '\0') { \
+ yy++; \
+ break; \
+ } \
+ } while (*xx == *++yy); \
+ (_result_) = *xx - *yy; \
+} while (0)
+#define return_STRING_COMPARE(_x_, _y_) do { \
+ int result; \
+ STRING_COMPARE (_x_, _y_, result); \
+ return result; \
+} while (0)
+
+/* hash and comparison macros for integer keys. */
+
+#define INTEGER_HASH_1(_key_, _result_) do { \
+ (_result_) += ((unsigned long)(_key_)); \
+} while (0)
+#define return_INTEGER_HASH_1(_key_) do { \
+ unsigned long result = 0; \
+ INTEGER_HASH_1 ((_key_), result); \
+ return result; \
+} while (0)
+
+#define INTEGER_HASH_2(_key_, _result_) do { \
+ (_result_) += ~((unsigned long)(_key_)); \
+} while (0)
+#define return_INTEGER_HASH_2(_key_) do { \
+ unsigned long result = 0; \
+ INTEGER_HASH_2 ((_key_), result); \
+ return result; \
+} while (0)
+
+#define INTEGER_COMPARE(_x_, _y_, _result_) do { \
+ (_result_) = _x_ - _y_; \
+} while (0)
+#define return_INTEGER_COMPARE(_x_, _y_) do { \
+ int result; \
+ INTEGER_COMPARE (_x_, _y_, result); \
+ return result; \
+} while (0)
+
+/* hash and comparison macros for address keys. */
+
+#define ADDRESS_HASH_1(_key_, _result_) INTEGER_HASH_1 (((unsigned long)(_key_)) >> 3, (_result_))
+#define ADDRESS_HASH_2(_key_, _result_) INTEGER_HASH_2 (((unsigned long)(_key_)) >> 3, (_result_))
+#define ADDRESS_COMPARE(_x_, _y_, _result_) INTEGER_COMPARE ((_x_), (_y_), (_result_))
+#define return_ADDRESS_HASH_1(_key_) return_INTEGER_HASH_1 (((unsigned long)(_key_)) >> 3)
+#define return_ADDRESS_HASH_2(_key_) return_INTEGER_HASH_2 (((unsigned long)(_key_)) >> 3)
+#define return_ADDRESS_COMPARE(_x_, _y_) return_INTEGER_COMPARE ((_x_), (_y_))
+
+#endif /* not _hash_h_ */
diff --git a/libidu/id-lang.map b/libidu/id-lang.map
new file mode 100644
index 0000000..e230cb6
--- /dev/null
+++ b/libidu/id-lang.map
@@ -0,0 +1,88 @@
+# Welcome to the mkid language mapper.
+#
+# The format of each line is:
+#
+# <pattern> <language> [options]
+#
+# Filenames are matched top-to-bottom against the patterns, and the
+# first match is chosen. The special language `IGNORE' means that
+# this file should be ignored by mkid. The options are
+# language-specific command-line options to mkid.
+#
+# If a file name doesn't match any pattern, it is assigned the default
+# language. The default language may be specified here with the
+# special pattern `**', or overridden from the mkid command-line with
+# the `--default-lang=LANG' option.
+#
+# The special pattern `***' means to include the named file that
+# immediately follows. If no file is named, then the default system
+# language mapper file (i.e., this file) is included.
+
+# Default language
+** IGNORE # Although this is listed first,
+ # the default language pattern is
+ # logically matched last.
+
+# Backup files
+*~ IGNORE
+*.bak IGNORE
+*.bk[0-9] IGNORE
+
+# SCCS files
+[sp].* IGNORE
+
+# C dependencies created by automake
+*/.deps/* IGNORE
+
+*.h C
+*.h.in C
+*.H C++
+*.hh C++
+*.hpp C++
+*.hxx C++
+
+*.l C
+*.lex C
+*.y C
+*.yacc C
+
+*.c C
+*.C C++
+*.cc C++
+*.cpp C++
+*.cxx C++
+
+ChangeLog* Cdoc
+
+*.[sS] asm --comment=;
+*.asm asm --comment=;
+
+# [nt]roff
+*.[0-9] roff
+*.ms roff
+*.me roff
+*.mm roff
+
+*.tex TeX
+*.ltx TeX
+*.texi texinfo
+*.texinfo texinfo
+
+# portable object (i18n)
+*.po po
+
+*.el elisp
+
+*.am make
+Makefile make
+Makefile.* make
+
+*.doc text
+*.txt text
+
+*.m4 m4
+
+*.pl perl
+
+*.gz FILTER gzip -d <%s
+*.Z FILTER gzip -d <%s
diff --git a/libidu/idfile.c b/libidu/idfile.c
new file mode 100644
index 0000000..e377145
--- /dev/null
+++ b/libidu/idfile.c
@@ -0,0 +1,137 @@
+/* idfile.c -- read & write mkid database file header
+ Copyright (C) 1986, 1995, 1996 Free Software Foundation, Inc.
+ Written by Greg McGary <gkm@gnu.ai.mit.edu>
+
+ 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.
+*/
+
+#include <config.h>
+#include <stdio.h>
+#include <errno.h>
+#include "xstdlib.h"
+#include "xstring.h"
+#include "xsysstat.h"
+#include "xnls.h"
+#include "xobstack.h"
+#include "idfile.h"
+#include "error.h"
+
+int io_size __P((FILE *, void *, unsigned int size, int));
+
+
+/****************************************************************************/
+
+/* Discover the name of the ID file. If ARG is NULL, consult $IDPATH.
+ If $IDPATH is undefined, default to "ID". If the candidate file
+ name is relative, search successive ancestor directories until the
+ file is found or we reach the root. If we find it, return the
+ relative file name, otherwise return NULL. */
+
+char const *
+locate_id_file_name (char const *arg)
+{
+ static char file_name_buffer[BUFSIZ];
+ char *buf = file_name_buffer;
+ char *id_path = 0;
+ struct stat rootb;
+ struct stat statb;
+
+ if (arg == 0)
+ {
+ id_path = getenv ("IDPATH");
+ if (id_path)
+ {
+ id_path = strdup (id_path);
+ arg = strtok (id_path, ":");
+ /* FIXME: handle multiple ID file names */
+ }
+ }
+ if (arg == 0)
+ arg = DEFAULT_ID_FILE_NAME;
+
+ /* if we got absolute name, just use it. */
+ if (arg[0] == '/')
+ return arg;
+ /* if the name we were give exists, don't bother searching */
+ if (stat (arg, &statb) == 0)
+ return arg;
+ /* search up the tree until we find a directory where this
+ * relative file name is visible.
+ * (or we run out of tree to search by hitting root).
+ */
+
+ if (stat ("/", &rootb) != 0)
+ return NULL;
+ do
+ {
+ strcpy (buf, "../");
+ buf += 3;
+ strcpy (buf, arg);
+ if (stat (file_name_buffer, &statb) == 0)
+ return file_name_buffer;
+ *buf = '\0';
+ if (stat (file_name_buffer, &statb) != 0)
+ return NULL;
+ }
+ while (!((statb.st_ino == rootb.st_ino) ||
+ (statb.st_dev == rootb.st_dev)));
+ return NULL;
+}
+
+
+/****************************************************************************/
+
+int
+sizeof_idhead ()
+{
+ return io_idhead (0, io_size, 0);
+}
+
+int
+io_size (FILE *ignore_FILE, void *ignore_addr, unsigned int size, int io_type)
+{
+ if (io_type == IO_TYPE_STR)
+ error (0, 0, _("can't determine the io_size of a string!"));
+ return size;
+}
+
+/* The sizes of the fields must be hard-coded. They aren't
+ necessarily the sizes of the struct members, because some
+ architectures don't have any way to declare 4-byte integers
+ (e.g., Cray) */
+
+int
+io_idhead (FILE *fp, io_func_t iof, struct idhead *idhp)
+{
+ unsigned int size = 0;
+ unsigned char pad = 0;
+ if (fp)
+ fseek (fp, 0L, 0);
+ size += iof (fp, idhp->idh_magic, 2, IO_TYPE_FIX);
+ size += iof (fp, &pad, 1, IO_TYPE_FIX);
+ size += iof (fp, &idhp->idh_version, 1, IO_TYPE_FIX);
+ size += iof (fp, &idhp->idh_flags, 2, IO_TYPE_INT);
+ size += iof (fp, &idhp->idh_file_links, 4, IO_TYPE_INT);
+ size += iof (fp, &idhp->idh_files, 4, IO_TYPE_INT);
+ size += iof (fp, &idhp->idh_tokens, 4, IO_TYPE_INT);
+ size += iof (fp, &idhp->idh_buf_size, 4, IO_TYPE_INT);
+ size += iof (fp, &idhp->idh_vec_size, 4, IO_TYPE_INT);
+ size += iof (fp, &idhp->idh_tokens_offset, 4, IO_TYPE_INT);
+ size += iof (fp, &idhp->idh_flinks_offset, 4, IO_TYPE_INT);
+ size += iof (fp, &idhp->idh_end_offset, 4, IO_TYPE_INT);
+ size += iof (fp, &idhp->idh_max_link, 2, IO_TYPE_INT);
+ size += iof (fp, &idhp->idh_max_path, 2, IO_TYPE_INT);
+ return size;
+}
diff --git a/libidu/idfile.h b/libidu/idfile.h
new file mode 100644
index 0000000..fb108a8
--- /dev/null
+++ b/libidu/idfile.h
@@ -0,0 +1,223 @@
+/* idfile.h -- decls for ID file header and constituent file names
+ Copyright (C) 1986, 1995, 1996 Free Software Foundation, Inc.
+ Written by Greg McGary <gkm@gnu.ai.mit.edu>
+
+ 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.
+*/
+
+#ifndef _idfile_h_
+#define _idfile_h_ 1
+
+#include <config.h>
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <stdio.h>
+#include "xobstack.h"
+#include "hash.h"
+#include "dynvec.h"
+#include "tokflags.h"
+
+
+/****************************************************************************/
+
+/* The ID file header is the nexus of all ID file information. This
+ is an in-core structure, only some of which is read/written to disk. */
+
+struct idhead
+{
+ unsigned char idh_magic[2];
+#define IDH_MAGIC_0 ('I'|0x80)
+#define IDH_MAGIC_1 ('D'|0x80)
+ unsigned char idh_version;
+#define IDH_VERSION 4
+ unsigned short idh_flags;
+#define IDH_COUNTS (1<<0) /* include occurrence counts for each token */
+#define IDH_FOLLOW_SL (1<<1) /* follow symlinks to directories */
+#define IDH_COMMENTS (1<<2) /* include tokens found in comments */
+#define IDH_LOCALS (1<<3) /* include names of formal params & local vars */
+#define IDH_DECL_DEFN_USE (1<<4) /* include decl/defn/use info */
+#define IDH_L_R_VALUE (1<<5) /* include lvalue/rvalue info */
+#define IDH_CALL_ER_EE (1<<6) /* include caller/callee relationship info */
+ unsigned long idh_file_links; /* total # of file links */
+ unsigned long idh_files; /* total # of constituent source files */
+ unsigned long idh_tokens; /* total # of constituent tokens */
+ /* idh_*_size: max buffer-sizes for ID file reading programs */
+ unsigned long idh_buf_size; /* # of bytes in longest entry */
+ unsigned long idh_vec_size; /* # of hits in longest entry */
+ /* idh_*_offset: ID file offsets for start of various sections */
+ long idh_tokens_offset; /* constituent tokens section */
+ long idh_flinks_offset; /* constituent file & directory names section */
+ long idh_end_offset; /* end of tokens section */
+ unsigned short idh_max_link; /* longest file name component */
+ unsigned short idh_max_path; /* largest # of file name components */
+
+ /* The following are run-time variables and are not stored on disk */
+ char const *idh_file_name;
+ struct hash_table idh_member_file_table;
+ struct hash_table idh_file_link_table;
+ struct obstack idh_member_file_obstack;
+ struct obstack idh_file_link_obstack;
+#if HAVE_LINK
+ struct hash_table idh_dev_ino_table; /* for detecting file name aliases */
+ struct obstack idh_dev_ino_obstack;
+#endif
+ FILE *idh_FILE;
+};
+
+/* idhead input/output definitions */
+
+#define IO_TYPE_INT 0 /* integer */
+#define IO_TYPE_STR 1 /* NUL terminated string */
+#define IO_TYPE_FIX 2 /* fix-sized */
+
+
+/****************************************************************************/
+
+/* A file_link represents a single component (file or directory) in a
+ file name. It has a name, a parent file_link and some flags. */
+
+struct file_link
+{
+ union {
+ struct file_link *u_parent;
+#define fl_parent fl_u.u_parent
+ unsigned long u_index;
+#define fl_index fl_u.u_index
+#define FL_PARENT_INDEX_BYTES 3
+#define IS_ROOT_FILE_LINK(flink) ((flink)->fl_parent == (flink))
+ } fl_u;
+ unsigned char fl_flags;
+#define FL_CMD_LINE_ARG (1<<0)
+#define FL_USED (1<<1)
+#define FL_MEMBER (1<<2) /* has a corresponding member_file entry */
+#define FL_SCAN_ME (1<<3)
+#define FL_SYM_LINK (1<<4)
+#define FL_TYPE_MASK (FL_TYPE_DIR|FL_TYPE_FILE)
+# define FL_TYPE_DIR (1<<5)
+# define FL_IS_DIR(_f_) (((_f_) & FL_TYPE_MASK) == FL_TYPE_DIR)
+# define FL_TYPE_FILE (1<<6)
+# define FL_IS_FILE(_f_) (((_f_) & FL_TYPE_MASK) == FL_TYPE_FILE)
+#define FL_PRUNE (1<<7)
+ char fl_name[1];
+};
+
+/* A member_file represents a source file that is treated by mkid. */
+
+struct member_file
+{
+ struct file_link *mf_link;
+ struct lang_args const *mf_lang_args;
+ short mf_index; /* order in ID file */
+};
+
+#if HAVE_LINK
+
+/* On systems that support multiple names for a single file (via hard
+ and/or soft links), dev_ino records information needed to detect
+ such aliasing. */
+
+struct dev_ino
+{
+ dev_t di_dev;
+ ino_t di_ino;
+ struct file_link *di_link;
+};
+
+extern struct hash_table dev_ino_table;
+
+#endif /* HAVE_LINK */
+
+
+/******************************************************************************/
+/* token flags (struct token is defined in scanners.h) */
+
+#define token_string(buf) (buf)
+extern unsigned int token_flags __P((char const *buf));
+extern unsigned short token_count __P((char const *buf));
+extern unsigned char const *token_hits_addr __P((char const *buf));
+
+#define MAYBE_RETURN_PREFIX_MATCH(arg, str, val) do { \
+ char const *_s_ = (str); \
+ if (strstr (_s_, (arg)) == _s_) \
+ return (val); \
+ } while (0)
+
+enum separator_style
+{
+ ss_bogus,
+ ss_contextual,
+ ss_braces,
+ ss_space,
+ ss_newline
+};
+
+#ifndef DEFAULT_SEPARATOR_STYLE
+#define DEFAULT_SEPARATOR_STYLE ss_braces
+#endif
+
+typedef int (*io_func_t) __P((FILE *, void *, unsigned int, int));
+
+extern struct file_link **read_id_file __P((char const *id_file_name, struct idhead *idhp));
+extern struct file_link **maybe_read_id_file __P((char const *id_file_name, struct idhead *idhp));
+extern int read_idhead __P((struct idhead *idhp));
+extern int write_idhead __P((struct idhead *idhp));
+extern int sizeof_idhead __P((void));
+struct file_link *init_walker __P((struct idhead *idhp));
+extern void init_idh_obstacks __P((struct idhead *idhp));
+extern void init_idh_tables __P((struct idhead *idhp));
+
+extern int io_write __P((FILE *output_FILE, void *addr, unsigned int size, int io_type));
+extern int io_read __P((FILE *input_FILE, void *addr, unsigned int size, int io_type));
+extern int io_idhead __P((FILE *fp, io_func_t iof, struct idhead *idhp));
+
+extern struct file_link *get_current_dir_link __P((void));
+extern struct file_link **deserialize_file_links __P((struct idhead *idhp));
+extern void serialize_file_links __P((struct idhead *idhp));
+
+extern void mark_member_file_links __P((struct idhead *idhp));
+extern int member_file_qsort_compare __P((void const *x, void const *y));
+extern struct file_link *parse_file_name __P((char *file_name,
+ struct file_link *relative_dir_link));
+extern void print_filenames __P((struct file_link **flinkv,
+ enum separator_style separator_style));
+extern enum separator_style parse_separator_style __P((char const *arg));
+
+extern void walk_flink __P((struct file_link *flink, struct dynvec *sub_dirs_vec));
+extern int chdir_to_link __P((struct file_link* dir_link));
+void prune_file_names __P((char *str, struct file_link *from_link));
+char **vectorize_string __P((char *string, char *delimiter_class));
+void include_languages __P((char *lang_names));
+void exclude_languages __P((char *lang_names));
+
+extern char *absolute_file_name __P((char *buffer, struct file_link const *flink));
+extern char *maybe_relative_file_name __P((char *buffer, struct file_link const *to_link,
+ struct file_link const *from_link));
+extern char const *locate_id_file_name __P((char const *arg));
+
+extern int tree8_count_levels __P((unsigned int cardinality));
+extern int gets_past_00 __P((char *tok, FILE *input_FILE));
+extern int skip_past_00 __P((FILE *input_FILE));
+
+extern int links_depth __P((struct file_link const *flink));
+#if HAVE_LINK
+extern struct member_file *find_member_file __P((struct file_link const *flink));
+#endif
+
+extern struct idhead idh;
+
+#define DEFAULT_ID_FILE_NAME "ID"
+
+#endif /* not _idfile_h_ */
diff --git a/libidu/idread.c b/libidu/idread.c
new file mode 100644
index 0000000..c725f9f
--- /dev/null
+++ b/libidu/idread.c
@@ -0,0 +1,280 @@
+/* idread.c -- functions to read ID database files
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Written by Greg McGary <gkm@gnu.ai.mit.edu>
+
+ 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.
+*/
+
+#include <config.h>
+#include <stdio.h>
+#include "idfile.h"
+#include "xstddef.h"
+#include "hash.h"
+#include "error.h"
+#include "xobstack.h"
+#include "xmalloc.h"
+#include "xnls.h"
+
+int fgets0 __P((char *buf0, int size, FILE *in_FILE));
+
+
+/****************************************************************************/
+
+/* read_id_file opens the ID file, reads header fields into idh,
+ verifies the magic number and version, and reads the constituent
+ file names. Any errors are considered fatal and cause an exit. */
+
+struct file_link **
+read_id_file (char const *id_file_name, struct idhead *idhp)
+{
+ struct file_link **flinkv = maybe_read_id_file (id_file_name, idhp);
+ if (flinkv)
+ return flinkv;
+ error (1, errno, _("can't open `%s'"), id_file_name);
+ return NULL;
+}
+
+/* maybe_read_id_file does everything that read_id_file does, but is
+ tolerant of errors opening the ID file, returning NULL in this case
+ (this is called from mkid where an ID might or might not already
+ exist). All other errors are considered fatal. */
+
+struct file_link **
+maybe_read_id_file (char const *id_file_name, struct idhead *idhp)
+{
+ obstack_init (&idhp->idh_file_link_obstack);
+ idhp->idh_FILE = fopen (id_file_name, "r");
+ if (idhp->idh_FILE == 0)
+ return 0;
+
+ read_idhead (idhp);
+ if (idhp->idh_magic[0] != IDH_MAGIC_0 || idhp->idh_magic[1] != IDH_MAGIC_1)
+ error (1, 0, _("`%s' is not an ID file! (bad magic #)"), id_file_name);
+ if (idhp->idh_version != IDH_VERSION)
+ error (1, 0, _("`%s' is version %d, but I only grok version %d"),
+ id_file_name, idhp->idh_version, IDH_VERSION);
+
+ fseek (idhp->idh_FILE, idhp->idh_flinks_offset, 0);
+ return deserialize_file_links (idhp);
+}
+
+
+/****************************************************************************/
+
+/* Read and reconstruct a serialized file_link hierarchy. */
+
+struct file_link **
+deserialize_file_links (struct idhead *idhp)
+{
+ struct file_link **flinks_0 = MALLOC (struct file_link *, idhp->idh_file_links);
+ struct file_link **flinks = flinks_0;
+ struct file_link **members_0 = MALLOC (struct file_link *, idhp->idh_files + 1);
+ struct file_link **members = members_0;
+ struct file_link *flink;
+ struct file_link **slot;
+ int i;
+
+ for (i = 0; i < idhp->idh_file_links; i++)
+ {
+ unsigned long parent_index;
+ int c;
+
+ obstack_blank (&idhp->idh_file_link_obstack, offsetof (struct file_link, fl_name));
+ if (obstack_room (&idhp->idh_file_link_obstack) >= idhp->idh_max_link)
+ do
+ {
+ c = getc (idhp->idh_FILE);
+ obstack_1grow_fast (&idhp->idh_file_link_obstack, c);
+ }
+ while (c);
+ else
+ do
+ {
+ c = getc (idhp->idh_FILE);
+ obstack_1grow (&idhp->idh_file_link_obstack, c);
+ }
+ while (c);
+ flink = (struct file_link *) obstack_finish (&idhp->idh_file_link_obstack);
+ *flinks = flink;
+ io_read (idhp->idh_FILE, &flink->fl_flags, sizeof (flink->fl_flags), IO_TYPE_INT);
+ io_read (idhp->idh_FILE, &parent_index, FL_PARENT_INDEX_BYTES, IO_TYPE_INT);
+ flink->fl_parent = flinks_0[parent_index];
+ slot = (struct file_link **) hash_find_slot (&idhp->idh_file_link_table, flink);
+ if (HASH_VACANT (*slot))
+ hash_insert_at (&idhp->idh_file_link_table, flink, slot);
+ else
+ {
+ obstack_free (&idhp->idh_file_link_obstack, flink);
+ (*slot)->fl_flags = flink->fl_flags;
+ flink = *flinks = *slot;
+ }
+ flinks++;
+ if (flink->fl_flags & FL_MEMBER)
+ *members++ = flink;
+ }
+ free (flinks_0);
+ *members = 0;
+ return members_0;
+}
+
+
+/****************************************************************************/
+
+int
+read_idhead (struct idhead *idhp)
+{
+ return io_idhead (idhp->idh_FILE, io_read, idhp);
+}
+
+/* This is like fgets(3s), except that lines are delimited by NULs
+ rather than newlines. Also, we return the number of characters
+ read rather than the address of buf0. */
+
+int
+fgets0 (char *buf0, int size, FILE * in_FILE)
+{
+ char *buf;
+ int c;
+ char *end;
+
+ buf = buf0;
+ end = &buf[size];
+ while ((c = getc (in_FILE)) > 0 && buf < end)
+ *buf++ = c;
+ *buf = '\0';
+ return (buf - buf0);
+}
+
+int
+io_read (FILE *input_FILE, void *addr, unsigned int size, int io_type)
+{
+ if (io_type == IO_TYPE_INT || size == 1)
+ {
+ switch (size)
+ {
+ case 4:
+ *(unsigned long *)addr = getc (input_FILE);
+ *(unsigned long *)addr += getc (input_FILE) << 010;
+ *(unsigned long *)addr += getc (input_FILE) << 020;
+ *(unsigned long *)addr += getc (input_FILE) << 030;
+ break;
+ case 3:
+ *(unsigned long *)addr = getc (input_FILE);
+ *(unsigned long *)addr += getc (input_FILE) << 010;
+ *(unsigned long *)addr += getc (input_FILE) << 020;
+ break;
+ case 2:
+ *(unsigned short *)addr = getc (input_FILE);
+ *(unsigned short *)addr += getc (input_FILE) << 010;
+ break;
+ case 1:
+ *(unsigned char *)addr = getc (input_FILE);
+ break;
+ default:
+ error (1, 0, _("unsupported size in io_read (): %d"), size);
+ }
+ }
+ else if (io_type == IO_TYPE_STR)
+ fgets0 (addr, size, input_FILE);
+ else if (io_type == IO_TYPE_FIX)
+ fread (addr, size, 1, input_FILE);
+ else
+ error (0, 0, _("unknown I/O type: %d"), io_type);
+ return size;
+}
+
+
+/****************************************************************************/
+
+unsigned int
+token_flags (char const *buf)
+{
+ return *(unsigned char const *)&buf[strlen (buf) + 1];
+}
+
+#define TOK_COUNT_ADDR(buf) ((unsigned char const *)(TOK_FLAGS_ADDR (buf) + 1))
+#define TOK_HITS_ADDR(buf) ((unsigned char const *)(TOK_COUNT_ADDR (buf) + 2))
+
+unsigned short
+token_count (char const *buf)
+{
+ unsigned char const *flags = (unsigned char const *)&buf[strlen (buf) + 1];
+ unsigned char const *addr = flags + 1;
+ unsigned short count = *addr;
+ if (*flags & TOK_SHORT_COUNT)
+ count += (*++addr << 8);
+ return count;
+}
+
+unsigned char const *
+token_hits_addr (char const *buf)
+{
+ unsigned char const *flags = (unsigned char const *)&buf[strlen (buf) + 1];
+ unsigned char const *addr = flags + 2;
+ if (*flags & TOK_SHORT_COUNT)
+ addr++;
+ return addr;
+}
+
+
+
+/****************************************************************************/
+
+int
+tree8_count_levels (unsigned int cardinality)
+{
+ int levels = 1;
+ cardinality--;
+ while (cardinality >>= 3)
+ ++levels;
+ return levels;
+}
+
+int
+gets_past_00 (char *tok, FILE *input_FILE)
+{
+ int got = 0;
+ int c;
+ do
+ {
+ do
+ {
+ got++;
+ c = getc (input_FILE);
+ *tok++ = c;
+ }
+ while (c > 0);
+ got++;
+ c = getc (input_FILE);
+ *tok++ = c;
+ }
+ while (c > 0);
+ return got - 2;
+}
+
+int
+skip_past_00 (FILE *input_FILE)
+{
+ int skipped = 0;
+ do
+ {
+ do
+ skipped++;
+ while (getc (input_FILE) > 0);
+ skipped++;
+ }
+ while (getc (input_FILE) > 0);
+ return skipped;
+}
diff --git a/libidu/idwrite.c b/libidu/idwrite.c
new file mode 100644
index 0000000..6257302
--- /dev/null
+++ b/libidu/idwrite.c
@@ -0,0 +1,169 @@
+/* idwrite.c -- functions to write ID database files
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Written by Greg McGary <gkm@gnu.ai.mit.edu>
+
+ 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.
+*/
+
+#include <config.h>
+#include "idfile.h"
+#include "hash.h"
+#include "xobstack.h"
+#include "xmalloc.h"
+#include "xnls.h"
+#include "error.h"
+
+int file_link_qsort_compare __P((void const *x, void const *y));
+
+
+/****************************************************************************/
+/* Serialize and write a file_link hierarchy. */
+
+void
+serialize_file_links (struct idhead *idhp)
+{
+ struct file_link **flinks_0;
+ struct file_link **flinks;
+ struct file_link **end;
+ struct file_link **parents_0;
+ struct file_link **parents;
+ unsigned long parent_index = 0;
+ int max_link = 0;
+
+ flinks_0 = (struct file_link **) hash_dump (&idhp->idh_file_link_table,
+ 0, file_link_qsort_compare);
+ end = &flinks_0[idhp->idh_file_link_table.ht_fill];
+ parents = parents_0 = MALLOC (struct file_link *, idhp->idh_file_link_table.ht_fill);
+ for (flinks = flinks_0; flinks < end; flinks++)
+ {
+ struct file_link *flink = *flinks;
+ int name_length;
+
+ if (!(flink->fl_flags & FL_USED))
+ break;
+ name_length = strlen (flink->fl_name);
+ if (name_length > max_link)
+ max_link = name_length;
+ io_write (idhp->idh_FILE, flink->fl_name, 0, IO_TYPE_STR);
+ io_write (idhp->idh_FILE, &flink->fl_flags, sizeof (flink->fl_flags), IO_TYPE_INT);
+ io_write (idhp->idh_FILE, (IS_ROOT_FILE_LINK (flink)
+ ? &parent_index : &flink->fl_parent->fl_index),
+ FL_PARENT_INDEX_BYTES, IO_TYPE_INT);
+ *parents++ = flink->fl_parent; /* save parent link before clobbering */
+ flink->fl_index = parent_index++;
+ }
+ /* restore parent links */
+ for ((flinks = flinks_0), (parents = parents_0); flinks < end; flinks++)
+ {
+ struct file_link *flink = *flinks;
+ if (!(flink->fl_flags & FL_USED))
+ break;
+ flink->fl_parent = *parents++;
+ }
+ free (parents_0);
+ free (flinks_0);
+ idhp->idh_max_link = max_link + 1;
+ idhp->idh_file_links = parent_index;
+ idhp->idh_files = idhp->idh_member_file_table.ht_fill;
+}
+
+/* Collation sequence:
+ - Used before unused.
+ - Among used: breadth-first (dirs before files, parent dirs before children)
+ - Among files: collate by mf_index. */
+
+int
+file_link_qsort_compare (void const *x, void const *y)
+{
+ struct file_link const *flx = *(struct file_link const *const *) x;
+ struct file_link const *fly = *(struct file_link const *const *) y;
+ unsigned int x_flags = flx->fl_flags;
+ unsigned int y_flags = fly->fl_flags;
+ int result;
+
+ result = (y_flags & FL_USED) - (x_flags & FL_USED);
+ if (result)
+ return result;
+ if (!(x_flags & FL_USED)) /* If neither link is used, we don't care... */
+ return 0;
+ result = (y_flags & FL_TYPE_DIR) - (x_flags & FL_TYPE_DIR);
+ if (result)
+ return result;
+ result = (y_flags & FL_TYPE_MASK) - (x_flags & FL_TYPE_MASK);
+ if (result)
+ return result;
+ if (FL_IS_FILE (x_flags))
+ {
+ struct member_file *x_member = find_member_file (flx);
+ struct member_file *y_member = find_member_file (fly);
+ return x_member->mf_index - y_member->mf_index;
+ }
+ else
+ {
+ int x_depth = links_depth (flx);
+ int y_depth = links_depth (fly);
+ return (x_depth - y_depth);
+ }
+}
+
+
+/****************************************************************************/
+
+int
+write_idhead (struct idhead *idhp)
+{
+ return io_idhead (idhp->idh_FILE, io_write, idhp);
+}
+
+int
+io_write (FILE *output_FILE, void *addr, unsigned int size, int io_type)
+{
+ if (io_type == IO_TYPE_INT || size == 1)
+ {
+ switch (size)
+ {
+ case 4:
+ putc (*(unsigned long *)addr, output_FILE);
+ putc (*(unsigned long *)addr >> 010, output_FILE);
+ putc (*(unsigned long *)addr >> 020, output_FILE);
+ putc (*(unsigned long *)addr >> 030, output_FILE);
+ break;
+ case 3:
+ putc (*(unsigned long *)addr, output_FILE);
+ putc (*(unsigned long *)addr >> 010, output_FILE);
+ putc (*(unsigned long *)addr >> 020, output_FILE);
+ break;
+ case 2:
+ putc (*(unsigned short *)addr, output_FILE);
+ putc (*(unsigned short *)addr >> 010, output_FILE);
+ break;
+ case 1:
+ putc (*(unsigned char *)addr, output_FILE);
+ break;
+ default:
+ error (1, 0, _("unsupported size in io_write (): %d"), size);
+ }
+ }
+ else if (io_type == IO_TYPE_STR)
+ {
+ fputs (addr, output_FILE);
+ putc ('\0', output_FILE);
+ }
+ else if (io_type == IO_TYPE_FIX)
+ fwrite (addr, size, 1, output_FILE);
+ else
+ error (0, 0, _("unknown I/O type: %d"), io_type);
+ return size;
+}
diff --git a/libidu/scanners.c b/libidu/scanners.c
new file mode 100644
index 0000000..af48c9f
--- /dev/null
+++ b/libidu/scanners.c
@@ -0,0 +1,1212 @@
+/* scanners.c -- file & directory name manipulations
+ Copyright (C) 1986, 1995, 1996 Free Software Foundation, Inc.
+ Written by Greg McGary <gkm@gnu.ai.mit.edu>
+
+ 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. */
+
+#include <config.h>
+#include <stdio.h>
+#include <ctype.h>
+#if HAVE_FCNTL_H
+# include <fcntl.h>
+#endif
+#include <getopt.h>
+#include "xstdlib.h"
+#include "xstddef.h"
+#include "xunistd.h"
+#include "xsysstat.h"
+#include "xstring.h"
+#include "xmalloc.h"
+#include "xnls.h"
+#include "error.h"
+#include "scanners.h"
+#include "tokflags.h"
+
+#define DEBUG(args) /* printf args */
+
+struct obstack lang_args_obstack;
+struct lang_args *lang_args_default = 0;
+struct lang_args *lang_args_list = 0;
+struct obstack tokens_obstack;
+
+extern void usage __P((void));
+extern char *program_name;
+
+/****************************************************************************/
+
+struct lang_args **parse_language_map_file __P((char const *file_name, struct lang_args **next_ptr));
+char *read_language_map_file __P((char const *file_name));
+void tokenize_args_string __P((char *args_string, int *argcp, char ***argvp));
+
+static struct token *get_token_c __P((FILE *in_FILE, void const *args, int *flags));
+static void *parse_args_c __P((char **argv, int argc));
+static void help_me_c __P((void));
+
+static struct token *get_token_asm __P((FILE *in_FILE, void const *args, int *flags));
+static void *parse_args_asm __P((char **argv, int argc));
+static void help_me_asm __P((void));
+
+static struct token *get_token_text __P((FILE *in_FILE, void const *args, int *flags));
+static void *parse_args_text __P((char **argv, int argc));
+static void help_me_text __P((void));
+
+struct language languages_0[] =
+{
+ { "C", parse_args_c, get_token_c, help_me_c },
+ { "asm", parse_args_asm, get_token_asm, help_me_asm },
+ { "text", parse_args_text, get_token_text, help_me_text },
+};
+struct language const *languages_N = &languages_0[cardinalityof (languages_0)];
+
+void
+language_help_me (void)
+{
+ struct language *lang;
+ for (lang = languages_0; lang < languages_N; lang++)
+ {
+ putchar ('\n');
+ (*lang->lg_help_me) ();
+ }
+}
+
+void
+language_save_arg (char *arg)
+{
+ static char horizontal_space[] = " \t";
+ char *lang_name = strtok (arg, ":");
+ struct language *lang = get_language (lang_name);
+
+ if (lang == 0)
+ {
+ error (0, 0, _("unrecognized language: `%s'"), lang_name);
+ usage ();
+ }
+ if (lang->lg_argc == 0)
+ lang->lg_argv[lang->lg_argc++] = program_name;
+ lang->lg_argv[lang->lg_argc++] = strtok (0, horizontal_space);
+}
+
+void
+language_getopt (void)
+{
+ struct language *lang;
+
+ for (lang = languages_0; lang < languages_N; lang++)
+ if (lang->lg_argc)
+ lang->lg_parse_args (lang->lg_argv, lang->lg_argc);
+}
+
+struct language *
+get_language (char const *lang_name)
+{
+ struct language *lang;
+
+ for (lang = languages_0; lang < languages_N; lang++)
+ if (strequ (lang_name, lang->lg_name))
+ {
+ DEBUG (("lang=%s", lang_name));
+ return lang;
+ }
+ DEBUG (("!lang=%s", lang_name));
+ return 0;
+}
+
+/****************************************************************************/
+
+int lang_args_index = 0;
+
+void
+set_default_language (char const *lang_name)
+{
+}
+
+void
+parse_language_map (char const *file_name)
+{
+ if (obstack_init (&lang_args_obstack) == 0)
+ error (1, 0, _("can't allocate language args obstack: memory exhausted"));
+ if (file_name == 0)
+ file_name = LANGUAGE_MAP_FILE;
+ parse_language_map_file (file_name, &lang_args_list);
+}
+
+struct lang_args **
+parse_language_map_file (char const *file_name, struct lang_args **next_ptr)
+{
+ static char white_space[] = " \t\r\n\v\f";
+ static char horizontal_space[] = " \t";
+ static char vertical_space[] = "\r\n\v\f";
+ char *lang_map_buffer;
+ char *lmp;
+
+ lmp = lang_map_buffer = read_language_map_file (file_name);
+ for (;;)
+ {
+ struct lang_args *new_args;
+ struct language const *lang;
+ int pattern_size;
+ char *lang_name;
+ int space;
+
+ /* Skip leading white space and full-line comments */
+ while (*lmp)
+ {
+ lmp += strspn (lmp, white_space);
+ if (*lmp != '#')
+ break;
+ lmp += strcspn (lmp, vertical_space);
+ }
+ if (*lmp == '\0')
+ break;
+
+ pattern_size = strcspn (lmp, white_space);
+ if (pattern_size == 3 && strnequ (lmp, "***", 3))
+ {
+ lmp += pattern_size;
+ lmp += strspn (lmp, horizontal_space);
+ if (isspace (*lmp))
+ next_ptr = parse_language_map_file (LANGUAGE_MAP_FILE, next_ptr);
+ else
+ {
+ char *end = lmp + strcspn (lmp, white_space);
+ *end = '\0';
+ next_ptr = parse_language_map_file (lmp, next_ptr);
+ lmp = end + 1;
+ }
+ continue;
+ }
+
+ new_args = OBSTACK_ALLOC (&lang_args_obstack, struct lang_args, 1);
+ if (new_args == 0)
+ error (1, 0, _("can't allocate language args: memory exhausted"));
+ new_args->la_pattern = obstack_copy0 (&lang_args_obstack, lmp, pattern_size);
+ new_args->la_args_string = 0;
+ new_args->la_next = 0;
+ lmp += pattern_size;
+ lmp += strspn (lmp, horizontal_space);
+ if (isspace (*lmp))
+ {
+ error (0, 0, _("language name expected following `%s' in file `%s'"),
+ new_args->la_pattern, file_name);
+ obstack_free (&lang_args_obstack, new_args);
+ continue;
+ }
+ lang_name = lmp;
+ lmp += strcspn (lmp, white_space);
+ space = *lmp;
+ *lmp++ = '\0';
+ lmp += strspn (lmp, horizontal_space);
+ lang = new_args->la_language = get_language (lang_name);
+
+ if (*lmp == '#')
+ lmp += strcspn (lmp, vertical_space);
+ else if (!isspace (*lmp) && (space == ' ' || space == '\t'))
+ {
+ int args_size = strcspn (lmp, vertical_space);
+ new_args->la_args_string = obstack_copy0 (&lang_args_obstack, lmp, args_size);
+ lmp += args_size;
+ }
+ new_args->la_args_digested = (lang
+ ? lang->lg_parse_args (&new_args->la_args_string, 0)
+ : 0);
+ if (pattern_size == 2 && strnequ (new_args->la_pattern, "**", 2))
+ {
+ if (lang_args_default)
+ {
+ obstack_free (&lang_args_obstack, new_args);
+ continue;
+ }
+ lang_args_default = new_args;
+ DEBUG ((", <default>"));
+ }
+ else
+ {
+ new_args->la_index = lang_args_index++;
+ *next_ptr = new_args;
+ next_ptr = &new_args->la_next;
+ }
+ DEBUG ((", pat=%s\n", new_args->la_pattern));
+ }
+ free (lang_map_buffer);
+ return next_ptr;
+}
+
+char *
+read_language_map_file (char const *file_name)
+{
+ int map_fd;
+ char *lang_map_buffer;
+ struct stat st;
+ int bytes;
+
+ map_fd = open (file_name, O_RDONLY);
+ if (map_fd < 0)
+ error (1, errno, _("can't open language map file `%s'"), file_name);
+ if (fstat (map_fd, &st) < 0)
+ error (1, errno, _("can't get size of map file `%s'"), file_name);
+
+ lang_map_buffer = MALLOC (char, st.st_size + 2);
+ if (lang_map_buffer == 0)
+ error (1, 0, _("can't allocate language args: memory exhausted"));
+ lang_map_buffer[st.st_size] = '\n';
+ lang_map_buffer[st.st_size+1] = '\0';
+
+ bytes = read (map_fd, lang_map_buffer, st.st_size);
+ if (bytes < 0)
+ error (1, errno, _("can't read language map file `%s'"), file_name);
+ /* FIXME: handle interrupted & partial reads */
+ if (bytes != st.st_size)
+ error (1, errno, _("can't read entire language map file `%s'"), file_name);
+
+ close (map_fd);
+ return lang_map_buffer;
+}
+
+/****************************************************************************/
+
+void
+tokenize_args_string (char *args_string, int *argcp, char ***argvp)
+{
+ static char horizontal_space[] = " \t";
+ char **argv_0 = MALLOC (char *, strlen (args_string) / 2);
+ char **argv = argv_0;
+ char *arg;
+
+ *argv++ = program_name;
+ arg = strtok (args_string, horizontal_space);
+ while (arg)
+ {
+ *argv++ = arg;
+ arg = strtok (0, horizontal_space);
+ }
+ *argcp = argv - argv_0;
+ *argvp = REALLOC (argv_0, char *, *argcp);
+}
+
+static void
+set_ushort_ctype (unsigned short *ctype, char const *chars, int type)
+{
+ unsigned short *rct = &ctype[1];
+
+ while (*chars)
+ rct[*chars++] |= type;
+}
+
+static void
+clear_ushort_ctype (unsigned short *ctype, char const *chars, int type)
+{
+ unsigned short *rct = &ctype[1];
+
+ while (*chars)
+ rct[*chars++] &= ~type;
+}
+
+static void
+set_uchar_ctype (unsigned char *ctype, char const *chars, int type)
+{
+ unsigned char *rct = &ctype[1];
+
+ while (*chars)
+ rct[*chars++] |= type;
+}
+
+static void
+clear_uchar_ctype (unsigned char *ctype, char const *chars, int type)
+{
+ unsigned char *rct = &ctype[1];
+
+ while (*chars)
+ rct[*chars++] &= ~type;
+}
+
+/*************** C & C++ ****************************************************/
+
+#define I1 0x0001 /* 1st char of an identifier [a-zA-Z_] */
+#define DG 0x0002 /* decimal digit [0-9] */
+#define NM 0x0004 /* extra chars in a hex or long number [a-fA-FxXlL] */
+#define C1 0x0008 /* C comment introduction char: / */
+#define C2 0x0010 /* C comment termination char: * */
+#define Q1 0x0020 /* single quote: ' */
+#define Q2 0x0040 /* double quote: " */
+#define ES 0x0080 /* escape char: \ */
+#define NL 0x0100 /* newline: \n */
+#define EF 0x0200 /* EOF */
+#define SK 0x0400 /* Make these chars valid for names within strings */
+#define VH 0x0800 /* VHIL comment introduction char: # */
+#define WS 0x1000 /* White space characters */
+
+/* character class membership macros: */
+
+#define ISDIGIT(c) ((rct)[c] & (DG)) /* digit */
+#define ISNUMBER(c) ((rct)[c] & (DG|NM)) /* legal in a number */
+#define ISEOF(c) ((rct)[c] & (EF)) /* EOF */
+#define ISID1ST(c) ((rct)[c] & (I1)) /* 1st char of an identifier */
+#define ISIDREST(c) ((rct)[c] & (I1|DG)) /* rest of an identifier */
+#define ISSTRKEEP(c) ((rct)[c] & (I1|DG|SK)) /* keep contents of string */
+#define ISSPACE(c) ((rct)[c] & (WS)) /* white space character */
+
+/* The `BORING' classes should be skipped over until something
+ interesting comes along... */
+
+#define ISBORING(c) (!((rct)[c] & (EF|NL|I1|DG|Q1|Q2|C1|VH))) /* fluff */
+#define ISCBORING(c) (!((rct)[c] & (EF|C2))) /* comment fluff */
+#define ISCCBORING(c) (!((rct)[c] & (EF|NL))) /* C++ // comment fluff */
+#define ISQ1BORING(c) (!((rct)[c] & (EF|NL|Q1|ES))) /* char const fluff */
+#define ISQ2BORING(c) (!((rct)[c] & (EF|NL|Q2|ES))) /* quoted str fluff */
+
+static unsigned short ctype_c[257] =
+{
+ EF,
+/* 0 1 2 3 4 5 6 7 */
+/* ----- ----- ----- ----- ----- ----- ----- ----- */
+/*000*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*010*/ 0, 0, NL, 0, 0, 0, 0, 0,
+/*020*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*030*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*040*/ 0, 0, Q2, 0, 0, 0, 0, Q1,
+/*050*/ 0, 0, C2, 0, 0, 0, 0, C1,
+/*060*/ DG, DG, DG, DG, DG, DG, DG, DG,
+/*070*/ DG, DG, 0, 0, 0, 0, 0, 0,
+/*100*/ 0, I1|NM, I1|NM, I1|NM, I1|NM, I1|NM, I1|NM, I1,
+/*110*/ I1, I1, I1, I1, I1|NM, I1, I1, I1,
+/*120*/ I1, I1, I1, I1, I1, I1, I1, I1,
+/*130*/ I1|NM, I1, I1, 0, ES, 0, 0, I1,
+/*140*/ 0, I1|NM, I1|NM, I1|NM, I1|NM, I1|NM, I1|NM, I1,
+/*150*/ I1, I1, I1, I1, I1|NM, I1, I1, I1,
+/*160*/ I1, I1, I1, I1, I1, I1, I1, I1,
+/*170*/ I1|NM, I1, I1, 0, 0, 0, 0, 0,
+ /* FIXME: latin-1 */
+};
+
+struct args_c
+{
+ int strip_underscore;
+ unsigned short *ctype;
+};
+
+static struct args_c args_c = { 0, ctype_c };
+
+static struct option const long_options_c[] =
+{
+ { "keep", required_argument, 0, 'k' },
+ { "ignore", required_argument, 0, 'i' },
+ { "strip-underscore", no_argument, 0, 'u' },
+ { 0 }
+};
+
+static void
+help_me_c (void)
+{
+ printf (_("\
+C language:\n\
+ -k,--keep=CHARS Allow CHARS in single-token strings, keep the result\n\
+ -i,--ignore=CHARS Allow CHARS in single-token strings, toss the result\n\
+ -u,--strip-underscore Strip a leading underscore from single-token strings\n\
+"));
+}
+
+static void *
+parse_args_c (char **argv, int argc)
+{
+ char *tmp_string = 0;
+ struct args_c *args;
+
+ if (argv == 0 || *argv == 0)
+ return &args_c;
+
+ if (argc)
+ args = &args_c;
+ else
+ {
+ tmp_string = strdup (*argv);
+ tokenize_args_string (tmp_string, &argc, &argv);
+ args = MALLOC (struct args_c, 1);
+ args->strip_underscore = 0;
+ args->ctype = ctype_c;
+ }
+
+ optind = 0;
+ for (;;)
+ {
+ int optc = getopt_long (argc, argv, "k:i:u",
+ long_options_c, (int *) 0);
+ if (optc < 0)
+ break;
+ if ((optc == 'k' || optc == 'i') && args->ctype == ctype_c)
+ args->ctype = CLONE (ctype_c, unsigned short, cardinalityof (ctype_c));
+ switch (optc)
+ {
+ case 'k':
+ set_ushort_ctype (args->ctype, optarg, SK);
+ break;
+
+ case 'i':
+ clear_ushort_ctype (args->ctype, optarg, SK);
+ break;
+
+ case 'u':
+ args->strip_underscore = 1;
+ break;
+
+ default:
+ usage ();
+ }
+ }
+ if (tmp_string)
+ {
+ free (argv);
+ free (tmp_string);
+ }
+ return args;
+}
+
+
+/* Grab the next identifier from the C source file. This state
+ machine is built for speed, not elegance. */
+
+static struct token *
+get_token_c (FILE *in_FILE, void const *args, int *flags)
+{
+#define ARGS ((struct args_c const *) args)
+ static int new_line = 1;
+ unsigned short const *rct = &ARGS->ctype[1];
+ char id_0[BUFSIZ];
+ char *id = id_0;
+ int c;
+
+ obstack_blank (&tokens_obstack, offsetof (struct token, tok_name));
+
+top:
+ c = getc (in_FILE);
+ if (new_line)
+ {
+ new_line = 0;
+ if (c != '#')
+ goto next;
+ c = getc (in_FILE);
+ while (ISBORING (c))
+ c = getc (in_FILE);
+ if (!ISID1ST (c))
+ goto next;
+ id = id_0;
+ *id++ = c;
+ while (ISIDREST (c = getc (in_FILE)))
+ *id++ = c;
+ *id = '\0';
+ if (strequ (id_0, "include"))
+ {
+ while (c == ' ' || c == '\t')
+ c = getc (in_FILE);
+ if (c == '\n')
+ {
+ new_line = 1;
+ goto top;
+ }
+ id = id_0;
+ if (c == '"')
+ {
+ c = getc (in_FILE);
+ while (c != '\n' && c != EOF && c != '"')
+ {
+ *id++ = c;
+ c = getc (in_FILE);
+ }
+ *flags = TOK_STRING;
+ }
+ else if (c == '<')
+ {
+ c = getc (in_FILE);
+ while (c != '\n' && c != EOF && c != '>')
+ {
+ *id++ = c;
+ c = getc (in_FILE);
+ }
+ *flags = TOK_STRING;
+ }
+ else if (ISID1ST (c))
+ {
+ *id++ = c;
+ while (ISIDREST (c = getc (in_FILE)))
+ *id++ = c;
+ *flags = TOK_NAME;
+ }
+ else
+ {
+ while (c != '\n' && c != EOF)
+ c = getc (in_FILE);
+ new_line = 1;
+ goto top;
+ }
+ while (c != '\n' && c != EOF)
+ c = getc (in_FILE);
+ new_line = 1;
+ obstack_grow0 (&tokens_obstack, id_0, id - id_0);
+ return (struct token *) obstack_finish (&tokens_obstack);
+ }
+ if (strnequ (id_0, "if", 2)
+ || strequ (id_0, "define")
+ || strequ (id_0, "elif") /* ansi C */
+ || strequ (id_0, "undef"))
+ goto next;
+ while ((c != '\n') && (c != EOF))
+ c = getc (in_FILE);
+ new_line = 1;
+ goto top;
+ }
+
+next:
+ while (ISBORING (c))
+ c = getc (in_FILE);
+
+ switch (c)
+ {
+ case '"':
+ id = id_0;
+ *id++ = c = getc (in_FILE);
+ for (;;)
+ {
+ while (ISQ2BORING (c))
+ *id++ = c = getc (in_FILE);
+ if (c == '\\')
+ {
+ *id++ = c = getc (in_FILE);
+ continue;
+ }
+ else if (c != '"')
+ goto next;
+ break;
+ }
+ *--id = '\0';
+ id = id_0;
+ while (ISSTRKEEP (*id))
+ id++;
+ if (*id || id == id_0)
+ {
+ c = getc (in_FILE);
+ goto next;
+ }
+ *flags = TOK_STRING;
+ if (ARGS->strip_underscore && id_0[0] == '_' && id_0[1])
+ obstack_grow0 (&tokens_obstack, id_0 + 1, id - id_0 - 1);
+ else
+ obstack_grow0 (&tokens_obstack, id_0, id - id_0);
+ return (struct token *) obstack_finish (&tokens_obstack);
+
+ case '\'':
+ c = getc (in_FILE);
+ for (;;)
+ {
+ while (ISQ1BORING (c))
+ c = getc (in_FILE);
+ if (c == '\\')
+ {
+ c = getc (in_FILE);
+ continue;
+ }
+ else if (c == '\'')
+ c = getc (in_FILE);
+ goto next;
+ }
+
+ case '/':
+ c = getc (in_FILE);
+ if (c == '/')
+ { /* Cope with C++ comment */
+ while (ISCCBORING (c))
+ c = getc (in_FILE);
+ new_line = 1;
+ goto top;
+ }
+ else if (c != '*')
+ goto next;
+ c = getc (in_FILE);
+ for (;;)
+ {
+ while (ISCBORING (c))
+ c = getc (in_FILE);
+ c = getc (in_FILE);
+ if (c == '/')
+ {
+ c = getc (in_FILE);
+ goto next;
+ }
+ else if (ISEOF (c))
+ {
+ new_line = 1;
+ obstack_free (&tokens_obstack, obstack_finish (&tokens_obstack));
+ return 0;
+ }
+ }
+
+ case '\n':
+ new_line = 1;
+ goto top;
+
+ default:
+ if (ISEOF (c))
+ {
+ new_line = 1;
+ obstack_free (&tokens_obstack, obstack_finish (&tokens_obstack));
+ return 0;
+ }
+ id = id_0;
+ *id++ = c;
+ if (ISID1ST (c))
+ {
+ *flags = TOK_NAME;
+ while (ISIDREST (c = getc (in_FILE)))
+ *id++ = c;
+ }
+ else if (ISDIGIT (c))
+ {
+ *flags = TOK_NUMBER;
+ while (ISNUMBER (c = getc (in_FILE)))
+ *id++ = c;
+ }
+ else
+ {
+ if (isprint (c))
+ fprintf (stderr, _("junk: `%c'"), c);
+ else
+ fprintf (stderr, _("junk: `\\%03o'"), c);
+ }
+ ungetc (c, in_FILE);
+ *flags |= TOK_LITERAL;
+ obstack_grow0 (&tokens_obstack, id_0, id - id_0);
+ return (struct token *) obstack_finish (&tokens_obstack);
+ }
+#undef ARGS
+}
+
+#undef I1
+#undef DG
+#undef NM
+#undef C1
+#undef C2
+#undef Q1
+#undef Q2
+#undef ES
+#undef NL
+#undef EF
+#undef SK
+#undef VH
+#undef WS
+#undef ISDIGIT
+#undef ISNUMBER
+#undef ISEOF
+#undef ISID1ST
+#undef ISIDREST
+#undef ISSTRKEEP
+#undef ISSPACE
+#undef ISBORING
+#undef ISCBORING
+#undef ISCCBORING
+#undef ISQ1BORING
+#undef ISQ2BORING
+
+/*************** Assembly ***************************************************/
+
+#define I1 0x01 /* 1st char of an identifier [a-zA-Z_] */
+#define NM 0x02 /* digit [0-9a-fA-FxX] */
+#define NL 0x04 /* newline: \n */
+#define CM 0x08 /* assembler comment char: usually # or | */
+#define IG 0x10 /* ignore `identifiers' with these chars in them */
+#define C1 0x20 /* C comment introduction char: / */
+#define C2 0x40 /* C comment termination char: * */
+#define EF 0x80 /* EOF */
+
+/* Assembly Language character classes */
+#define ISID1ST(c) ((rct)[c] & (I1))
+#define ISIDREST(c) ((rct)[c] & (I1|NM))
+#define ISNUMBER(c) ((rct)[c] & (NM))
+#define ISEOF(c) ((rct)[c] & (EF))
+#define ISCOMMENT(c) ((rct)[c] & (CM))
+#define ISBORING(c) (!((rct)[c] & (EF|NL|I1|NM|CM|C1)))
+#define ISCBORING(c) (!((rct)[c] & (EF|NL)))
+#define ISCCBORING(c) (!((rct)[c] & (EF|C2)))
+#define ISIGNORE(c) ((rct)[c] & (IG))
+
+static unsigned char ctype_asm[257] =
+{
+ EF,
+/* 0 1 2 3 4 5 6 7 */
+/* ----- ----- ----- ----- ----- ----- ----- ----- */
+/*000*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*010*/ 0, 0, NL, 0, 0, 0, 0, 0,
+/*020*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*030*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*040*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*050*/ 0, 0, C2, 0, 0, 0, 0, C1,
+/*060*/ NM, NM, NM, NM, NM, NM, NM, NM,
+/*070*/ NM, NM, 0, 0, 0, 0, 0, 0,
+/*100*/ 0, I1|NM, I1|NM, I1|NM, I1|NM, I1|NM, I1|NM, I1,
+/*110*/ I1, I1, I1, I1, I1|NM, I1, I1, I1,
+/*120*/ I1, I1, I1, I1, I1, I1, I1, I1,
+/*130*/ I1|NM, I1, I1, 0, 0, 0, 0, I1,
+/*140*/ 0, I1|NM, I1|NM, I1|NM, I1|NM, I1|NM, I1|NM, I1,
+/*150*/ I1, I1, I1, I1, I1|NM, I1, I1, I1,
+/*160*/ I1, I1, I1, I1, I1, I1, I1, I1,
+/*170*/ I1|NM, I1, I1, 0, 0, 0, 0, 0,
+
+};
+
+struct args_asm
+{
+ int handle_cpp;
+ int strip_underscore;
+ unsigned char *ctype;
+};
+
+static struct args_asm args_asm = { 1, 0, ctype_asm };
+
+static struct option const long_options_asm[] =
+{
+ { "comment", required_argument, 0, 'c' },
+ { "keep", required_argument, 0, 'k' },
+ { "ignore", required_argument, 0, 'i' },
+ { "strip-underscore", no_argument, 0, 'u' },
+ { "no-cpp", no_argument, 0, 'p' },
+ { 0 }
+};
+
+static void
+help_me_asm (void)
+{
+ printf (_("\
+Assembly language:\n\
+ -c,--comment=CHARS Any of CHARS starts a comment until end-of-line\n\
+ -k,--keep=CHARS Allow CHARS in tokens, and keep the result\n\
+ -i,--ignore=CHARS Allow CHARS in tokens, and toss the result\n\
+ -u,--strip-underscore Strip a leading underscore from tokens\n\
+ -n,--no-cpp Don't handle C pre-processor directives\n\
+"));
+}
+
+static void *
+parse_args_asm (char **argv, int argc)
+{
+ char *tmp_string = 0;
+ struct args_asm *args;
+
+ if (argv == 0 || *argv == 0)
+ return &args_asm;
+
+ if (argc)
+ args = &args_asm;
+ else
+ {
+ tmp_string = strdup (*argv);
+ tokenize_args_string (tmp_string, &argc, &argv);
+ args = MALLOC (struct args_asm, 1);
+ args->strip_underscore = 0;
+ args->ctype = ctype_asm;
+ args->handle_cpp = 1;
+ }
+
+ optind = 0;
+ for (;;)
+ {
+ int optc = getopt_long (argc, argv, "c:k:i:un",
+ long_options_asm, (int *) 0);
+ if (optc < 0)
+ break;
+ if ((optc == 'k' || optc == 'i' || optc == 'c')
+ && args->ctype == ctype_asm)
+ args->ctype = CLONE (ctype_asm, unsigned char, cardinalityof (ctype_asm));
+ switch (optc)
+ {
+ case 'c':
+ set_uchar_ctype (args->ctype, optarg, CM);
+ break;
+
+ case 'k':
+ set_uchar_ctype (args->ctype, optarg, I1);
+ break;
+
+ case 'i':
+ set_uchar_ctype (args->ctype, optarg, I1 | IG);
+ break;
+
+ case 'u':
+ args->strip_underscore = 1;
+ break;
+
+ case 'n':
+ args->handle_cpp = 0;
+ break;
+
+ default:
+ usage ();
+ }
+ }
+ if (tmp_string)
+ {
+ free (argv);
+ free (tmp_string);
+ }
+ return args;
+}
+
+/* Grab the next identifier the assembly language source file. This
+ state machine is built for speed, not elegance. */
+
+static struct token *
+get_token_asm (FILE *in_FILE, void const *args, int *flags)
+{
+#define ARGS ((struct args_asm const *) args)
+ static int new_line = 1;
+ unsigned char const *rct = &ARGS->ctype[1];
+ char id_0[BUFSIZ];
+ char *id = id_0;
+ int c;
+
+ obstack_blank (&tokens_obstack, offsetof (struct token, tok_name));
+
+top:
+ c = getc (in_FILE);
+ if (ARGS->handle_cpp > 0 && new_line)
+ {
+ new_line = 0;
+ if (c != '#')
+ goto next;
+ while (ISBORING (c))
+ c = getc (in_FILE);
+ if (!ISID1ST (c))
+ goto next;
+ id = id_0;
+ *id++ = c;
+ while (ISIDREST (c = getc (in_FILE)))
+ *id++ = c;
+ *id = '\0';
+ if (strequ (id_0, "include"))
+ {
+ while (c != '"' && c != '<')
+ c = getc (in_FILE);
+ id = id_0;
+ *id++ = c = getc (in_FILE);
+ while ((c = getc (in_FILE)) != '"' && c != '>')
+ *id++ = c;
+ *flags = TOK_STRING;
+ obstack_grow0 (&tokens_obstack, id_0, id - id_0);
+ return (struct token *) obstack_finish (&tokens_obstack);
+ }
+ if (strnequ (id_0, "if", 2)
+ || strequ (id_0, "define")
+ || strequ (id_0, "undef"))
+ goto next;
+ while (c != '\n')
+ c = getc (in_FILE);
+ new_line = 1;
+ goto top;
+ }
+
+next:
+ while (ISBORING (c))
+ c = getc (in_FILE);
+
+ if (ISCOMMENT (c))
+ {
+ while (ISCBORING (c))
+ c = getc (in_FILE);
+ new_line = 1;
+ }
+
+ if (ISEOF (c))
+ {
+ new_line = 1;
+ obstack_free (&tokens_obstack, obstack_finish (&tokens_obstack));
+ return 0;
+ }
+
+ if (c == '\n')
+ {
+ new_line = 1;
+ goto top;
+ }
+
+ if (c == '/')
+ {
+ if ((c = getc (in_FILE)) != '*')
+ goto next;
+ c = getc (in_FILE);
+ for (;;)
+ {
+ while (ISCCBORING (c))
+ c = getc (in_FILE);
+ c = getc (in_FILE);
+ if (c == '/')
+ {
+ c = getc (in_FILE);
+ break;
+ }
+ else if (ISEOF (c))
+ {
+ new_line = 1;
+ obstack_free (&tokens_obstack, obstack_finish (&tokens_obstack));
+ return 0;
+ }
+ }
+ goto next;
+ }
+
+ id = id_0;
+ if (ARGS->strip_underscore && c == '_' && !ISID1ST (c = getc (in_FILE)))
+ {
+ obstack_grow0 (&tokens_obstack, "_", 1);
+ return (struct token *) obstack_finish (&tokens_obstack);
+ }
+ *id++ = c;
+ if (ISID1ST (c))
+ {
+ *flags = TOK_NAME;
+ while (ISIDREST (c = getc (in_FILE)))
+ *id++ = c;
+ }
+ else if (ISNUMBER (c))
+ {
+ *flags = TOK_NUMBER;
+ while (ISNUMBER (c = getc (in_FILE)))
+ *id++ = c;
+ }
+ else
+ {
+ if (isprint (c))
+ fprintf (stderr, _("junk: `%c'"), c);
+ else
+ fprintf (stderr, _("junk: `\\%03o'"), c);
+ goto next;
+ }
+
+ *id = '\0';
+ for (id = id_0; *id; id++)
+ if (ISIGNORE (*id))
+ goto next;
+ ungetc (c, in_FILE);
+ *flags |= TOK_LITERAL;
+ obstack_grow0 (&tokens_obstack, id_0, id - id_0);
+ return (struct token *) obstack_finish (&tokens_obstack);
+#undef ARGS
+}
+
+#undef I1
+#undef NM
+#undef NL
+#undef CM
+#undef IG
+#undef C1
+#undef C2
+#undef EF
+#undef ISID1ST
+#undef ISIDREST
+#undef ISNUMBER
+#undef ISEOF
+#undef ISCOMMENT
+#undef ISBORING
+#undef ISCBORING
+#undef ISCCBORING
+#undef ISIGNORE
+
+/*************** Text *******************************************************/
+
+#define I1 0x01 /* 1st char of an identifier [a-zA-Z_] */
+#define NM 0x02 /* digit [0-9a-fA-FxX] */
+#define SQ 0x04 /* squeeze these out (.,',-) */
+#define EF 0x80 /* EOF */
+
+/* Text character classes */
+#define ISID1ST(c) ((rct)[c] & (I1))
+#define ISIDREST(c) ((rct)[c] & (I1|NM|SQ))
+#define ISNUMBER(c) ((rct)[c] & (NM))
+#define ISEOF(c) ((rct)[c] & (EF))
+#define ISBORING(c) (!((rct)[c] & (I1|NM|EF)))
+#define ISIDSQUEEZE(c) ((rct)[c] & (SQ))
+
+static unsigned char ctype_text[257] =
+{
+ EF,
+/* 0 1 2 3 4 5 6 7 */
+/* ----- ----- ----- ----- ----- ----- ----- ----- */
+/*000*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*010*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*020*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*030*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*040*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*050*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*060*/ NM, NM, NM, NM, NM, NM, NM, NM,
+/*070*/ NM, NM, 0, 0, 0, 0, 0, 0,
+/*100*/ 0, I1|NM, I1|NM, I1|NM, I1|NM, I1|NM, I1|NM, I1,
+/*110*/ I1, I1, I1, I1, I1|NM, I1, I1, I1,
+/*120*/ I1, I1, I1, I1, I1, I1, I1, I1,
+/*130*/ I1|NM, I1, I1, 0, 0, 0, 0, I1,
+/*140*/ 0, I1|NM, I1|NM, I1|NM, I1|NM, I1|NM, I1|NM, I1,
+/*150*/ I1, I1, I1, I1, I1|NM, I1, I1, I1,
+/*160*/ I1, I1, I1, I1, I1, I1, I1, I1,
+/*170*/ I1|NM, I1, I1, 0, 0, 0, 0, 0,
+/*200*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*210*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*220*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*230*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*240*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*250*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*260*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*270*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*300*/ I1, I1, I1, I1, I1, I1, I1, I1,
+/*310*/ I1, I1, I1, I1, I1, I1, I1, I1,
+/*320*/ I1, I1, I1, I1, I1, I1, I1, 0,
+/*330*/ I1, I1, I1, I1, I1, I1, I1, I1,
+/*340*/ I1, I1, I1, I1, I1, I1, I1, I1,
+/*350*/ I1, I1, I1, I1, I1, I1, I1, I1,
+/*360*/ I1, I1, I1, I1, I1, I1, I1, 0,
+/*370*/ I1, I1, I1, I1, I1, I1, I1, I1,
+};
+
+struct args_text
+{
+ unsigned char *ctype;
+};
+
+static struct args_text args_text = { ctype_text };
+
+static struct option const long_options_text[] =
+{
+ { "include", required_argument, 0, 'i' },
+ { "exclude", required_argument, 0, 'x' },
+ { 0 }
+};
+
+static void
+help_me_text (void)
+{
+ printf (_("\
+Text language:\n\
+ -i,--include=CHAR-CLASS Treat characters of CHAR-CLASS as token constituents\n\
+ -x,--exclude=CHAR-CLASS Treat characters of CHAR-CLASS as token delimiters\n\
+"));
+}
+
+static void *
+parse_args_text (char **argv, int argc)
+{
+ char *tmp_string = 0;
+ struct args_text *args;
+
+ if (argv == 0 || *argv == 0)
+ return &args_text;
+
+ if (argc)
+ args = &args_text;
+ else
+ {
+ tmp_string = strdup (*argv);
+ tokenize_args_string (tmp_string, &argc, &argv);
+ args = MALLOC (struct args_text, 1);
+ args->ctype = ctype_text;
+ }
+
+ optind = 0;
+ for (;;)
+ {
+ int optc = getopt_long (argc, argv, "i:x:",
+ long_options_text, (int *) 0);
+ if (optc < 0)
+ break;
+ if ((optc == 'k' || optc == 'i') && args->ctype == ctype_text)
+ args->ctype = CLONE (ctype_text, unsigned char, cardinalityof (ctype_text));
+ switch (optc)
+ {
+ case 'i':
+ set_uchar_ctype (args->ctype, optarg, I1);
+ break;
+
+ case 'x':
+ clear_uchar_ctype (args->ctype, optarg, I1);
+ break;
+
+ default:
+ usage ();
+ }
+ }
+ if (tmp_string)
+ {
+ free (argv);
+ free (tmp_string);
+ }
+ return args;
+}
+
+/* Grab the next identifier the text source file. This state machine
+ is built for speed, not elegance. */
+
+static struct token *
+get_token_text (FILE *in_FILE, void const *args, int *flags)
+{
+#define ARGS ((struct args_text const *) args)
+ static char id_0[BUFSIZ];
+ unsigned char const *rct = &ARGS->ctype[1];
+ int c;
+ char *id = id_0;
+
+ obstack_blank (&tokens_obstack, offsetof (struct token, tok_name));
+
+top:
+ c = getc (in_FILE);
+ while (ISBORING (c))
+ c = getc (in_FILE);
+ if (ISEOF (c))
+ {
+ obstack_free (&tokens_obstack, obstack_finish (&tokens_obstack));
+ return 0;
+ }
+ id = id_0;
+ *id++ = c;
+ if (ISID1ST (c))
+ {
+ *flags = TOK_NAME;
+ while (ISIDREST (c = getc (in_FILE)))
+ if (!ISIDSQUEEZE (c))
+ *id++ = c;
+ }
+ else if (ISNUMBER (c))
+ {
+ *flags = TOK_NUMBER;
+ while (ISNUMBER (c = getc (in_FILE)))
+ *id++ = c;
+ }
+ else
+ {
+ if (isprint (c))
+ fprintf (stderr, _("junk: `%c'"), c);
+ else
+ fprintf (stderr, _("junk: `\\%03o'"), c);
+ goto top;
+ }
+
+ ungetc (c, in_FILE);
+ *flags |= TOK_LITERAL;
+ obstack_grow0 (&tokens_obstack, id_0, id - id_0);
+ return (struct token *) obstack_finish (&tokens_obstack);
+#undef ARGS
+}
+
+#undef I1
+#undef NM
+#undef SQ
+#undef EF
+#undef ISID1ST
+#undef ISIDREST
+#undef ISNUMBER
+#undef ISEOF
+#undef ISBORING
+#undef ISIDSQUEEZE
diff --git a/libidu/scanners.h b/libidu/scanners.h
new file mode 100644
index 0000000..ca95310
--- /dev/null
+++ b/libidu/scanners.h
@@ -0,0 +1,70 @@
+/* scanners.h -- defs for interface to scanners.c
+ Copyright (C) 1986, 1995, 1996 Free Software Foundation, Inc.
+ Written by Greg McGary <gkm@gnu.ai.mit.edu>
+
+ 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. */
+
+#ifndef _scanners_h_
+#define _scanners_h_
+
+#include "xobstack.h"
+
+#define MAX_LEVELS 5 /* log_8 of the max # of files: log_8 (32768) == 5 */
+
+struct token
+{
+ unsigned short tok_count;
+ unsigned char tok_flags;
+ unsigned char tok_hits[MAX_LEVELS];
+ char tok_name[1];
+};
+
+typedef struct token *(*get_token_func_t) __P((FILE *in_FILE, void const *args, int *flags));
+typedef void *(*parse_args_func_t) __P((char **argv, int argc));
+typedef void (*help_me_func_t) __P((void));
+
+struct language
+{
+ char const *lg_name;
+ parse_args_func_t lg_parse_args;
+ get_token_func_t lg_get_token;
+ help_me_func_t lg_help_me;
+ int lg_argc;
+ char *lg_argv[16];
+};
+
+struct lang_args
+{
+ struct language const *la_language;
+ char const *la_pattern; /* fnmatch(3) pattern */
+ char *la_args_string; /* human-readable scanner args */
+ void const *la_args_digested; /* pre-parsed scanner args */
+ int la_index;
+ struct lang_args *la_next;
+};
+
+extern void language_help_me __P((void));
+extern void language_getopt __P((void));
+extern void language_save_arg __P((char *arg));
+extern struct language *get_language __P((char const *lang_name));
+extern void parse_language_map __P((char const *file_name));
+extern void set_default_language __P((char const *lang_name));
+
+extern struct lang_args *lang_args_default;
+extern struct lang_args *lang_args_list;
+
+extern struct obstack tokens_obstack;
+
+#endif /* not _scanners_h_ */
diff --git a/libidu/tokflags.h b/libidu/tokflags.h
new file mode 100644
index 0000000..a246ec5
--- /dev/null
+++ b/libidu/tokflags.h
@@ -0,0 +1,35 @@
+/* tokflags.h -- decls for per-token flags
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Written by Greg McGary <gkm@gnu.ai.mit.edu>
+
+ 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.
+*/
+
+#ifndef _tokflags_h_
+#define _tokflags_h_
+
+#define TOK_VECTOR 0x01 /* 1 = hits are stored as a vector
+ 0 = hits are stored as a 8-way tree of bits
+ mkid chooses whichever is more compact.
+ vector is more compact for tokens with few hits */
+#define TOK_NUMBER 0x02 /* occurs as a number */
+#define TOK_NAME 0x04 /* occurs as a name */
+#define TOK_STRING 0x08 /* occurs in a string */
+#define TOK_LITERAL 0x10 /* occurs as a literal */
+#define TOK_COMMENT 0x20 /* occurs in a comment */
+#define TOK_UNUSED_1 0x40
+#define TOK_SHORT_COUNT 0x80 /* count is two bytes */
+
+#endif /* not _tokflags_h_ */
diff --git a/libidu/walker.c b/libidu/walker.c
new file mode 100644
index 0000000..0ba2d89
--- /dev/null
+++ b/libidu/walker.c
@@ -0,0 +1,1130 @@
+/* walker.c -- nifty file-tree walker
+ Copyright (C) 1986, 1995, 1996 Free Software Foundation, Inc.
+ Written by Greg McGary <gkm@gnu.ai.mit.edu>
+
+ 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.
+*/
+
+#include <config.h>
+#include "xsysstat.h"
+#include <stdio.h>
+#include "xstdlib.h"
+#include "xstddef.h"
+#include "xunistd.h"
+#include "xstring.h"
+#include "xfnmatch.h"
+#include "xdirent.h"
+#include "xnls.h"
+#include "idfile.h"
+#include "error.h"
+#include "xmalloc.h"
+#include "dynvec.h"
+#include "scanners.h"
+#include "pathmax.h"
+#include "xalloca.h"
+
+int walk_dir __P((struct file_link *dir_link));
+struct member_file *get_member_file __P((struct file_link *flink));
+struct lang_args *get_lang_args __P((struct file_link const *flink));
+int walk_sub_dirs __P((struct dynvec *sub_dirs_vec));
+void reparent_children __P((struct file_link *dlink, struct file_link *slink));
+int classify_link __P((struct file_link *flink, struct stat *stp));
+struct file_link *get_link_from_dirent __P((struct dirent *dirent, struct file_link *parent));
+struct file_link *make_link_from_dirent __P((struct dirent *dirent, struct file_link *parent));
+struct file_link *get_link_from_string __P((char const *name, struct file_link *parent));
+struct file_link *make_link_from_string __P((char const *name, struct file_link *parent));
+int lang_wanted __P((char const *lang_name));
+char **append_strings_to_vector __P((char **vector_0, char *string, char *delimiter_class));
+int vector_length __P((char **vector));
+int string_in_vector __P((char const *string, char **vector));
+static int same_as_dot __P((char const *cwd));
+struct file_link const **fill_link_vector __P((struct file_link const **vec_buf, struct file_link const *flink));
+struct file_link const **fill_link_vector_1 __P((struct file_link const **vec_buf, struct file_link const *flink));
+char *fill_dot_dots __P((char *buf, int levels));
+static char *absolute_file_name_1 __P((char *buffer, struct file_link const *flink));
+unsigned long member_file_hash_1 __P((void const *key));
+unsigned long member_file_hash_2 __P((void const *key));
+int member_file_hash_compare __P((void const *x, void const *y));
+unsigned long file_link_hash_1 __P((void const *key));
+unsigned long file_link_hash_2 __P((void const *key));
+int file_link_hash_compare __P((void const *x, void const *y));
+unsigned long dev_ino_hash_1 __P((void const *key));
+unsigned long dev_ino_hash_2 __P((void const *key));
+int dev_ino_hash_compare __P((void const *x, void const *y));
+int symlink_ancestry __P((struct file_link *flink));
+
+#if HAVE_LINK
+struct file_link *find_alias_link __P((struct file_link *flink, struct stat *stp));
+struct member_file *maybe_get_member_file __P((struct file_link *flink, struct stat *stp));
+#endif
+
+#define IS_DOT(s) ((s)[0] == '.' && (s)[1] == '\0')
+#define IS_DOT_DOT(s) ((s)[0] == '.' && (s)[1] == '.' && (s)[2] == '\0')
+#define IS_DOT_or_DOT_DOT(s) \
+ (((s)[0] == '.') && (((s)[1] == '\0') || ((s)[1] == '.' && (s)[2] == '\0')))
+
+static struct file_link *current_dir_link = 0;
+
+static char white_space[] = " \t\r\n\v\f";
+
+char* xgetcwd __P((void));
+
+
+/****************************************************************************/
+/* Walk the file-system tree rooted at `dir_link', looking for files
+ that are eligible for scanning. */
+
+int
+walk_dir (struct file_link *dir_link)
+{
+ int scannable_files;
+ struct dynvec *sub_dirs_vec;
+ DIR *dirp;
+
+ if (!chdir_to_link (dir_link))
+ return 0;
+ dirp = opendir (".");
+ if (dirp == 0)
+ {
+ char *file_name = ALLOCA (char, PATH_MAX);
+ absolute_file_name (file_name, dir_link);
+ error (0, errno, _("can't read directory `%s' (`.' from `%s')"), file_name, xgetcwd ());
+ return 0;
+ }
+ sub_dirs_vec = make_dynvec (32);
+ scannable_files = 0;
+ for (;;)
+ {
+ struct file_link *flink;
+ struct dirent *dirent = readdir (dirp);
+
+ if (dirent == 0)
+ break;
+ if (IS_DOT_or_DOT_DOT (dirent->d_name))
+ continue;
+
+ flink = get_link_from_dirent (dirent, dir_link);
+ if (!(flink->fl_flags & FL_PRUNE))
+ walk_flink (flink, sub_dirs_vec);
+ }
+ closedir (dirp);
+
+ scannable_files += walk_sub_dirs (sub_dirs_vec);
+ dynvec_free (sub_dirs_vec);
+ return scannable_files;
+}
+
+/* Walk the directories found by walk_dir, calling walk_dir
+ recursively for each directory. */
+
+int
+walk_sub_dirs (struct dynvec *sub_dirs_vec)
+{
+ struct file_link **sub_dirs;
+ struct file_link **sub_dirs_end;
+ int total_scannable_files = 0;
+
+ dynvec_freeze (sub_dirs_vec);
+ sub_dirs_end = (struct file_link **)
+ &sub_dirs_vec->dv_vec[sub_dirs_vec->dv_fill];
+ sub_dirs = (struct file_link **) sub_dirs_vec->dv_vec;
+ for ( ; sub_dirs < sub_dirs_end; sub_dirs++)
+ {
+ struct file_link *sub_dir_link = *sub_dirs;
+ int scannable_files = walk_dir (sub_dir_link);
+ if (scannable_files)
+ total_scannable_files += scannable_files;
+ }
+ return total_scannable_files;
+}
+
+void
+walk_flink (struct file_link *flink, struct dynvec *sub_dirs_vec)
+{
+ struct stat st;
+ unsigned int old_flags;
+ unsigned int new_flags;
+
+ new_flags = classify_link (flink, &st);
+ if (new_flags == 0)
+ return;
+
+ old_flags = flink->fl_flags;
+ if ((old_flags & FL_TYPE_MASK)
+ && (old_flags & FL_TYPE_MASK) != (new_flags & FL_TYPE_MASK))
+ {
+ char *file_name = ALLOCA (char, PATH_MAX);
+ absolute_file_name (file_name, flink);
+ error (0, 0, _("notice: `%s' was a %s, but is now a %s!"), file_name,
+ (FL_IS_FILE (old_flags) ? _("file") : _("directory")),
+ (FL_IS_FILE (new_flags) ? _("file") : _("directory")));
+ }
+
+
+ flink->fl_flags = (old_flags & ~(FL_TYPE_MASK|FL_SYM_LINK)) | new_flags;
+ if (FL_IS_DIR (new_flags))
+ {
+ struct file_link *alias_link = find_alias_link (flink, &st);
+
+ if (alias_link)
+ {
+ if (!(new_flags & FL_SYM_LINK))
+ reparent_children (flink, alias_link);
+ }
+ else if (sub_dirs_vec == 0)
+ walk_dir (flink);
+ else
+ dynvec_append (sub_dirs_vec, flink);
+ }
+ else
+ {
+ struct member_file *member;
+#if HAVE_LINK
+ member = maybe_get_member_file (flink, &st);
+#else
+ member = get_member_file (flink);
+#endif
+ if (member == 0)
+ return;
+ }
+}
+
+/* Take child file_link nodes from a symlinked directory and give them
+ to a hard linked directory. This is something of a pain since a
+ file_link's parent node is part of its hash-table key. We must
+ search the entire hash-table for the children. With each child, we
+ must delete it, reset its parent link, then reinsert. */
+
+void
+reparent_children (struct file_link *dlink, struct file_link *slink)
+{
+ void **slot = idh.idh_file_link_table.ht_vec;
+ void **end = &idh.idh_file_link_table.ht_vec[idh.idh_file_link_table.ht_size];
+
+ for ( ; slot < end; slot++)
+ {
+ if (!HASH_VACANT (*slot))
+ {
+ struct file_link *child = (struct file_link *) *slot;
+ if (child->fl_parent == slink)
+ {
+ void **new_slot;
+ *slot = hash_deleted_item;
+ child->fl_parent = dlink;
+ new_slot = hash_find_slot (&idh.idh_file_link_table, child);
+ *new_slot = child;
+ }
+ }
+ }
+}
+
+
+/****************************************************************************/
+/* Separate the wheat from the chaff. Mark those file_links that are
+ components in member files. */
+
+void
+mark_member_file_links (struct idhead *idhp)
+{
+ struct member_file **members_0
+ = (struct member_file **) hash_dump (&idhp->idh_member_file_table,
+ 0, member_file_qsort_compare);
+ struct member_file **end = &members_0[idhp->idh_member_file_table.ht_fill];
+ struct member_file **members;
+ int new_index = 0;
+
+ for (members = members_0; members < end; members++)
+ {
+ struct member_file *member = *members;
+ struct file_link *flink;
+ member->mf_index = new_index++;
+ for (flink = member->mf_link;
+ !(flink->fl_flags & FL_USED); flink = flink->fl_parent)
+ flink->fl_flags |= FL_USED;
+ }
+ free (members_0);
+}
+
+
+#if HAVE_LINK
+
+/****************************************************************************/
+/* Return a `member_file' for this `flink' *if* the filename matches
+ some scan pattern, and no alias for the file takes precedence ([1]
+ hard-links dominate symbolic-links; [2] for two hard-links: first
+ come, first served). */
+
+struct member_file *
+maybe_get_member_file (struct file_link *flink, struct stat *stp)
+{
+ struct file_link *alias_link;
+ struct member_file *member;
+ struct member_file *alias_member = 0;
+
+ member = get_member_file (flink);
+ alias_link = find_alias_link (flink, stp);
+ if (alias_link)
+ alias_member = find_member_file (alias_link);
+
+ if (member && alias_member)
+ {
+ int ancestry = symlink_ancestry (flink);
+ int alias_ancestry = symlink_ancestry (alias_link);
+ if (member->mf_lang_args != alias_member->mf_lang_args)
+ {
+ char *file_name = ALLOCA (char, PATH_MAX);
+ char *alias_file_name = ALLOCA (char, PATH_MAX);
+ absolute_file_name (file_name, flink);
+ absolute_file_name (alias_file_name, alias_link);
+ error (0, 0, _("warning: `%s' and `%s' are the same file, but yield different scans!"),
+ file_name, alias_file_name);
+ }
+ else if (alias_ancestry > ancestry)
+ {
+ hash_delete (&idh.idh_member_file_table, member);
+ member->mf_link->fl_flags &= ~FL_MEMBER;
+ return 0;
+ }
+ else
+ {
+ hash_delete (&idh.idh_member_file_table, alias_member);
+ alias_member->mf_link->fl_flags &= ~FL_MEMBER;
+ }
+ }
+ return member;
+}
+
+/* Return a previously registered alias for `flink', if any. */
+
+struct file_link *
+find_alias_link (struct file_link *flink, struct stat *stp)
+{
+ struct dev_ino *dev_ino;
+ struct dev_ino **slot;
+
+ dev_ino = (struct dev_ino *) obstack_alloc (&idh.idh_dev_ino_obstack, sizeof (struct dev_ino));
+ dev_ino->di_dev = stp->st_dev;
+ dev_ino->di_ino = stp->st_ino;
+ slot = (struct dev_ino **) hash_find_slot (&idh.idh_dev_ino_table, dev_ino);
+ if (HASH_VACANT (*slot))
+ {
+ dev_ino->di_link = flink;
+ hash_insert_at (&idh.idh_dev_ino_table, dev_ino, slot);
+ return 0;
+ }
+ else
+ {
+ obstack_free (&idh.idh_dev_ino_obstack, dev_ino);
+ return (*slot)->di_link;
+ }
+}
+
+/* Return the distance from `flink' to a symbolic-link ancestor
+ directory. PATH_MAX is considered an infinite distance (e.g.,
+ there are no symlinks between `flink' and the root). */
+
+int
+symlink_ancestry (struct file_link *flink)
+{
+ int ancestry = 0;
+ while (!IS_ROOT_FILE_LINK (flink))
+ {
+ if (flink->fl_flags & FL_SYM_LINK)
+ return ancestry;
+ ancestry++;
+ flink = flink->fl_parent;
+ }
+ return PATH_MAX;
+}
+
+#endif /* HAVE_LINK */
+
+struct member_file *
+get_member_file (struct file_link *flink)
+{
+ struct member_file *member;
+ struct member_file **slot;
+ struct lang_args const *args;
+
+ args = get_lang_args (flink);
+ if (args == 0)
+ return 0;
+ if (!lang_wanted (args->la_language->lg_name))
+ return 0;
+
+ member = (struct member_file *) obstack_alloc (&idh.idh_member_file_obstack,
+ sizeof (struct member_file));
+ member->mf_link = flink;
+ slot = (struct member_file **) hash_find_slot (&idh.idh_member_file_table, member);
+ if (HASH_VACANT (*slot))
+ {
+ member->mf_index = -1;
+ hash_insert_at (&idh.idh_member_file_table, member, slot);
+ flink->fl_flags |= FL_MEMBER;
+ }
+ else
+ {
+ obstack_free (&idh.idh_member_file_obstack, member);
+#if 0
+ if (member->mf_lang_args != args)
+ {
+ char *file_name = ALLOCA (char, PATH_MAX);
+ absolute_file_name (file_name, flink);
+ error (0, 0, _("notice: scan parameters changed for `%s'"), file_name);
+ member->mf_old_index = -1;
+ }
+#endif
+ member = *slot;
+ }
+ member->mf_lang_args = args;
+ return *slot;
+}
+
+struct member_file *
+find_member_file (struct file_link const *flink)
+{
+ struct member_file key;
+ struct member_file **slot;
+
+ key.mf_link = (struct file_link *) flink;
+ slot = (struct member_file **) hash_find_slot (&idh.idh_member_file_table, &key);
+ if (HASH_VACANT (*slot))
+ return 0;
+ return *slot;
+}
+
+/* March down the list of lang_args, and return the first one whose
+ pattern matches FLINK. Return the matching lang_args, if a
+ scanner exists for that language, otherwise return 0. */
+
+struct lang_args *
+get_lang_args (struct file_link const *flink)
+{
+ struct lang_args *args = lang_args_list;
+
+ while (args)
+ {
+ if (strchr (args->la_pattern, SLASH_CHAR))
+ {
+ char *file_name = ALLOCA (char, PATH_MAX);
+ absolute_file_name (file_name, flink);
+ if (fnmatch (args->la_pattern, file_name, MAYBE_FNM_CASEFOLD | FNM_FILE_NAME) == 0)
+ return (args->la_language ? args : 0);
+ }
+ else
+ {
+ if (fnmatch (args->la_pattern, flink->fl_name, MAYBE_FNM_CASEFOLD) == 0)
+ return (args->la_language ? args : 0);
+ }
+ args = args->la_next;
+ }
+ return ((lang_args_default && lang_args_default->la_language)
+ ? lang_args_default : 0);
+}
+
+
+/****************************************************************************/
+
+static char **langs_included;
+static char **langs_excluded;
+
+int
+lang_wanted (char const *lang_name)
+{
+ if (langs_excluded)
+ return !string_in_vector (lang_name, langs_excluded);
+ else if (langs_included)
+ return string_in_vector (lang_name, langs_included);
+ else
+ return 1;
+}
+
+void
+include_languages (char *lang_names)
+{
+ if (langs_excluded)
+ error (1, 0, "can't mix --include and --exclude options");
+ langs_included = append_strings_to_vector (langs_included, lang_names, white_space);
+}
+
+void
+exclude_languages (char *lang_names)
+{
+ if (langs_excluded)
+ error (1, 0, "can't mix --include and --exclude options");
+ langs_excluded = append_strings_to_vector (langs_excluded, lang_names, white_space);
+}
+
+char **
+append_strings_to_vector (char **vector_0, char *string, char *delimiter_class)
+{
+ char **vector;
+ if (vector_0)
+ {
+ int length = vector_length (vector_0);
+ vector_0 = REALLOC (vector_0, char *,
+ length + 2 + strlen (string) / 2);
+ vector = &vector_0[length];
+ }
+ else
+ vector = vector_0 = MALLOC (char *, 2 + strlen (string) / 2);
+
+ *vector = strtok (string, delimiter_class);
+ while (*vector)
+ *++vector = strtok (0, delimiter_class);
+ return REALLOC (vector_0, char *, ++vector - vector_0);
+}
+
+int
+vector_length (char **vector)
+{
+ int length = 0;
+ while (*vector++)
+ length++;
+ return length;
+}
+
+int
+string_in_vector (char const *string, char **vector)
+{
+ while (*vector)
+ if (strequ (string, *vector++))
+ return 1;
+ return 0;
+}
+
+
+/****************************************************************************/
+/* Convert a file name string to an absolute chain of `file_link's. */
+
+struct file_link *
+parse_file_name (char *file_name, struct file_link *relative_dir_link)
+{
+ struct file_link *flink;
+ char **links_0;
+ char **links;
+
+ if (IS_ABSOLUTE (file_name))
+ {
+#if HAVE_LINK
+ flink = get_link_from_string (SLASH_STRING, 0);
+#else
+ flink = 0;
+#endif
+ }
+ else if (relative_dir_link)
+ flink = relative_dir_link;
+ else if (current_dir_link)
+ flink = current_dir_link;
+ else
+ flink = get_current_dir_link ();
+
+ links = links_0 = vectorize_string (file_name, SLASH_STRING);
+ while (*links)
+ {
+ char const* link_name = *links++;
+ if (*link_name == '\0' || IS_DOT (link_name))
+ ;
+ else if (IS_DOT_DOT (link_name))
+ flink = flink->fl_parent;
+ else
+ {
+ struct stat st;
+ flink = get_link_from_string (link_name, flink);
+ if (!flink->fl_flags)
+ {
+ flink->fl_flags = classify_link (flink, &st);
+ if (!flink->fl_flags)
+ return 0;
+ }
+ }
+ }
+ free (links_0);
+ return flink;
+}
+
+/* Return an absolute chain of `file_link's representing the current
+ working directory. */
+
+struct file_link *
+get_current_dir_link (void)
+{
+ struct file_link *dir_link;
+ char *cwd_0;
+ char *cwd;
+ char *xcwd = 0;
+ char **links_0;
+ char **links;
+
+ if (current_dir_link)
+ return current_dir_link;
+
+ cwd_0 = getenv ("PWD");
+ if (cwd_0)
+ cwd_0 = strdup (cwd_0);
+ if (!same_as_dot (cwd_0))
+ cwd_0 = xcwd = xgetcwd ();
+ if (cwd_0 == 0)
+ error (1, errno, _("can't get working directory"));
+ cwd = cwd_0;
+#if HAVE_LINK
+ dir_link = get_link_from_string (SLASH_STRING, 0);
+ dir_link->fl_flags = (dir_link->fl_flags & ~FL_TYPE_MASK) | FL_TYPE_DIR;
+#else
+ dir_link = 0;
+#endif
+ links = links_0 = vectorize_string (cwd, SLASH_STRING);
+ while (*links)
+ {
+ struct stat st;
+ char const* link_name = *links++;
+ dir_link = get_link_from_string (link_name, dir_link);
+ if (!dir_link->fl_flags)
+ dir_link->fl_flags = classify_link (dir_link, &st);
+ }
+ chdir_to_link (dir_link);
+ free (links_0);
+ if (xcwd)
+ free (xcwd);
+ current_dir_link = dir_link;
+ return dir_link;
+}
+
+static int
+same_as_dot (char const *cwd)
+{
+ struct stat cwd_st;
+ struct stat dot_st;
+
+ if (cwd == 0 || *cwd != '/'
+ || stat (cwd, &cwd_st) < 0
+ || stat (".", &dot_st) < 0)
+ return 0;
+ return ((cwd_st.st_ino == dot_st.st_ino) && (cwd_st.st_dev == dot_st.st_dev));
+}
+
+/* Change the working directory to the directory represented by
+ `dir_link'. Choose the shortest path to the destination based on
+ the cached value of the current directory. */
+
+int
+chdir_to_link (struct file_link *dir_link)
+{
+ char *to_dir_name = ALLOCA (char, PATH_MAX);
+
+ if (current_dir_link == dir_link)
+ return 1;
+
+ if (current_dir_link)
+ maybe_relative_file_name (to_dir_name, dir_link, current_dir_link);
+ else
+ absolute_file_name (to_dir_name, dir_link);
+ if (chdir (to_dir_name) < 0)
+ {
+ if (IS_ABSOLUTE (to_dir_name))
+ error (0, errno, _("can't chdir to `%s'"), to_dir_name);
+ else
+ {
+ char *from_dir_name = ALLOCA (char, PATH_MAX);
+ absolute_file_name (from_dir_name, current_dir_link);
+ error (0, errno, _("can't chdir to `%s' from `%s'"), to_dir_name, from_dir_name);
+ }
+ return 0;
+ }
+ else
+ {
+ current_dir_link = dir_link;
+ return 1;
+ }
+}
+
+char **
+vectorize_string (char *string, char *delimiter_class)
+{
+ char **vector_0 = MALLOC (char *, 2 + strlen (string) / 2);
+ char **vector = vector_0;
+
+ *vector = strtok (string, delimiter_class);
+ while (*vector)
+ *++vector = strtok (0, delimiter_class);
+ return REALLOC (vector_0, char *, ++vector - vector_0);
+}
+
+void
+prune_file_names (char *str, struct file_link *from_link)
+{
+ char **file_names_0 = vectorize_string (str, white_space);
+ char **file_names = file_names_0;
+
+ while (*file_names)
+ {
+ struct file_link *flink = parse_file_name (*file_names++, from_link);
+ if (flink)
+ flink->fl_flags |= FL_PRUNE;
+ }
+ free (file_names_0);
+}
+
+
+/****************************************************************************/
+/* Gather information about the link at `flink'. If it's a good
+ file or directory, return its mod-time and type. */
+
+int
+classify_link (struct file_link *flink, struct stat *stp)
+{
+ unsigned int flags = 0;
+
+ if (!chdir_to_link (flink->fl_parent))
+ return 0;
+
+#ifdef S_IFLNK
+ if (lstat (flink->fl_name, stp) < 0)
+ {
+ error (0, errno, _("can't lstat `%s' from `%s'"), flink->fl_name, xgetcwd ());
+ return 0;
+ }
+ if (S_ISLNK (stp->st_mode))
+ {
+#endif
+ if (stat (flink->fl_name, stp) < 0)
+ {
+ error (0, errno, _("can't stat `%s' from `%s'"), flink->fl_name, xgetcwd ());
+ return 0;
+ }
+#ifdef S_IFLNK
+ flags |= FL_SYM_LINK;
+ }
+#endif
+ if (S_ISDIR (stp->st_mode))
+ flags |= FL_TYPE_DIR;
+ else if (S_ISREG (stp->st_mode))
+ flags |= FL_TYPE_FILE;
+ else
+ return 0;
+ return flags;
+}
+
+
+/****************************************************************************/
+/* Retrieve an existing flink; or if none exists, create one. */
+
+struct file_link *
+get_link_from_dirent (struct dirent *dirent, struct file_link *parent)
+{
+ struct file_link **slot;
+ struct file_link *new_link;
+
+ new_link = make_link_from_dirent (dirent, parent);
+ slot = (struct file_link **) hash_find_slot (&idh.idh_file_link_table, new_link);
+ if (HASH_VACANT (*slot))
+ hash_insert_at (&idh.idh_file_link_table, new_link, slot);
+ else
+ obstack_free (&idh.idh_file_link_obstack, new_link);
+ return *slot;
+}
+
+struct file_link *
+get_link_from_string (char const *name, struct file_link *parent)
+{
+ struct file_link **slot;
+ struct file_link *new_link;
+
+ new_link = make_link_from_string (name, parent);
+ slot = (struct file_link **) hash_find_slot (&idh.idh_file_link_table, new_link);
+ if (HASH_VACANT (*slot))
+ hash_insert_at (&idh.idh_file_link_table, new_link, slot);
+ else
+ obstack_free (&idh.idh_file_link_obstack, new_link);
+ return *slot;
+}
+
+struct file_link *
+make_link_from_dirent (struct dirent* dirent, struct file_link *parent)
+{
+ struct file_link *flink;
+
+ flink = (struct file_link *) obstack_alloc (&idh.idh_file_link_obstack,
+ sizeof (struct file_link) + strlen (dirent->d_name));
+ strcpy (flink->fl_name, dirent->d_name);
+ flink->fl_parent = parent ? parent : flink;
+ flink->fl_flags = 0;
+
+ return flink;
+}
+
+struct file_link *
+make_link_from_string (char const* name, struct file_link *parent)
+{
+ struct file_link *flink;
+
+ flink = (struct file_link *) obstack_alloc (&idh.idh_file_link_obstack,
+ sizeof (struct file_link) + strlen (name));
+ strcpy (flink->fl_name, name);
+ flink->fl_parent = parent ? parent : flink;
+ flink->fl_flags = 0;
+
+ return flink;
+}
+
+
+/****************************************************************************/
+/* Convert a `file_link' chain to a vector of component `file_link's,
+ with the root at [0]. Return a pointer beyond the final component. */
+
+struct file_link const **
+fill_link_vector (struct file_link const **vec_buf, struct file_link const *flink)
+{
+ vec_buf = fill_link_vector_1 (vec_buf, flink);
+ *vec_buf = 0;
+ return vec_buf;
+}
+
+struct file_link const **
+fill_link_vector_1 (struct file_link const **vec_buf, struct file_link const *flink)
+{
+ if (!IS_ROOT_FILE_LINK (flink))
+ vec_buf = fill_link_vector_1 (vec_buf, flink->fl_parent);
+ *vec_buf++ = flink;
+ return vec_buf;
+}
+
+
+/****************************************************************************/
+/* Fill BUF_0 with a path to TO_LINK. If a relative path from
+ FROM_LINK is possible (i.e., no intervening symbolic-links) and
+ shorter, return the relative path; otherwise, return an absolute
+ path. */
+
+char *
+maybe_relative_file_name (char *buf_0, struct file_link const *to_link, struct file_link const *from_link)
+{
+ struct file_link const **to_link_vec_0 = ALLOCA (struct file_link const *, PATH_MAX/2);
+ struct file_link const **from_link_vec_0 = ALLOCA (struct file_link const *, PATH_MAX/2);
+ struct file_link const **to_link_vec = to_link_vec_0;
+ struct file_link const **from_link_vec = from_link_vec_0;
+ struct file_link const **from_link_end;
+ struct file_link const **from_links;
+ int alloc_me = (buf_0 == 0);
+ char *buf;
+ int levels;
+
+ if (from_link == 0)
+ from_link = current_dir_link;
+
+ if (alloc_me)
+ buf_0 = MALLOC (char, PATH_MAX);
+
+ /* Optimize common cases. */
+ if (to_link == from_link)
+ strcpy (buf_0, ".");
+ else if (to_link->fl_parent == from_link)
+ strcpy (buf_0, to_link->fl_name);
+ else if (from_link->fl_flags & FL_SYM_LINK)
+ absolute_file_name (buf_0, to_link);
+ else if (to_link == from_link->fl_parent)
+ strcpy (buf_0, "..");
+ else if (to_link->fl_parent == from_link->fl_parent)
+ {
+ strcpy (buf_0, DOT_DOT_SLASH);
+ strcpy (&buf_0[3], to_link->fl_name);
+ }
+ else
+ {
+ from_link_end = fill_link_vector (from_link_vec, from_link);
+ fill_link_vector (to_link_vec, to_link);
+ while (*to_link_vec == *from_link_vec)
+ {
+ if (*to_link_vec == 0)
+ {
+ strcpy (buf_0, ".");
+ goto out;
+ }
+ to_link_vec++;
+ from_link_vec++;
+ }
+ levels = from_link_end - from_link_vec;
+ if (levels >= (from_link_vec - from_link_vec_0))
+ {
+ absolute_file_name (buf_0, to_link);
+ goto out;
+ }
+ for (from_links = from_link_vec; *from_links; from_links++)
+ if ((*from_links)->fl_flags & FL_SYM_LINK)
+ {
+ absolute_file_name (buf_0, to_link);
+ goto out;
+ }
+ buf = fill_dot_dots (buf_0, levels);
+ if (*to_link_vec == 0)
+ *--buf = '\0';
+ else
+ {
+ do
+ {
+ strcpy (buf, (*to_link_vec)->fl_name);
+ buf += strlen (buf);
+ if ((*to_link_vec)->fl_name[0] != SLASH_CHAR && *++to_link_vec)
+ *buf++ = SLASH_CHAR;
+ }
+ while (*to_link_vec);
+ }
+ }
+out:
+ if (alloc_me)
+ buf_0 = REALLOC (buf_0, char, 1 + strlen (buf_0));
+ return buf_0;
+}
+
+/* Fill `buf' with sequences of "../" in order to ascend so many
+ `levels' in a directory tree. */
+
+char *
+fill_dot_dots (char *buf, int levels)
+{
+ while (levels--)
+ {
+ strcpy (buf, DOT_DOT_SLASH);
+ buf += 3;
+ }
+ return buf;
+}
+
+
+/****************************************************************************/
+/* Fill `buffer' with the absolute path to `flink'. */
+
+char *
+absolute_file_name (char *buf_0, struct file_link const *flink)
+{
+ char *end;
+ int alloc_me = (buf_0 == 0);
+
+ if (alloc_me)
+ buf_0 = MALLOC (char, PATH_MAX);
+ end = absolute_file_name_1 (buf_0, flink);
+ /* Clip the trailing slash. */
+#if HAVE_LINK
+ if (end > &buf_0[1])
+ end--;
+#else
+ if (end > &buf_0[3])
+ end--;
+#endif
+ *end++ = '\0';
+ if (alloc_me)
+ buf_0 = REALLOC (buf_0, char, end - buf_0);
+ return buf_0;
+}
+
+static char *
+absolute_file_name_1 (char *buf, struct file_link const *flink)
+{
+ char *end;
+ if (IS_ROOT_FILE_LINK (flink))
+ end = buf;
+ else
+ end = absolute_file_name_1 (buf, flink->fl_parent);
+ strcpy (end, flink->fl_name);
+ if (*end == SLASH_CHAR)
+ end++;
+ else
+ {
+ end += strlen (end);
+ *end++ = SLASH_CHAR;
+ }
+ return end;
+}
+
+
+/****************************************************************************/
+/* Hash stuff for `struct member_file'. */
+
+unsigned long
+member_file_hash_1 (void const *key)
+{
+ return_ADDRESS_HASH_1 (((struct member_file const *) key)->mf_link);
+}
+
+unsigned long
+member_file_hash_2 (void const *key)
+{
+ return_ADDRESS_HASH_2 (((struct member_file const *) key)->mf_link);
+}
+
+int
+member_file_hash_compare (void const *x, void const *y)
+{
+ return_ADDRESS_COMPARE (((struct member_file const *) x)->mf_link,
+ ((struct member_file const *) y)->mf_link);
+}
+
+/* Collating sequence:
+ - language.map index
+ - mf_link: breadth-first, alphabetical */
+
+int
+member_file_qsort_compare (void const *x, void const *y)
+{
+ struct member_file const *mfx = *(struct member_file const *const *) x;
+ struct member_file const *mfy = *(struct member_file const *const *) y;
+ int result;
+
+ INTEGER_COMPARE (mfx->mf_lang_args->la_index, mfy->mf_lang_args->la_index, result);
+ if (result)
+ return result;
+ else
+ {
+ struct file_link const *flx = mfx->mf_link;
+ struct file_link const *fly = mfy->mf_link;
+ if (flx->fl_parent == fly->fl_parent)
+ return strcmp (flx->fl_name, fly->fl_name);
+ result = (links_depth (flx) - links_depth (fly));
+ if (result)
+ return result;
+ while (flx->fl_parent != fly->fl_parent)
+ {
+ flx = flx->fl_parent;
+ fly = fly->fl_parent;
+ }
+ return strcmp (flx->fl_name, fly->fl_name);
+ }
+}
+
+/****************************************************************************/
+/* Hash stuff for `struct file_link'. */
+
+unsigned long
+file_link_hash_1 (void const *key)
+{
+ unsigned long result = 0;
+ struct file_link const *parent = (IS_ROOT_FILE_LINK (((struct file_link const *) key))
+ ? 0 : ((struct file_link const *) key)->fl_parent);
+ STRING_HASH_1 (((struct file_link const *) key)->fl_name, result);
+ ADDRESS_HASH_1 (parent, result);
+ return result;
+}
+
+unsigned long
+file_link_hash_2 (void const *key)
+{
+ unsigned long result = 0;
+ struct file_link const *parent = (IS_ROOT_FILE_LINK (((struct file_link const *) key))
+ ? 0 : ((struct file_link const *) key)->fl_parent);
+ STRING_HASH_2 (((struct file_link const *) key)->fl_name, result);
+ ADDRESS_HASH_2 (parent, result);
+ return result;
+}
+
+int
+file_link_hash_compare (void const *x, void const *y)
+{
+ int result;
+ struct file_link const *x_parent = (IS_ROOT_FILE_LINK (((struct file_link const *) x))
+ ? 0 : ((struct file_link const *) x)->fl_parent);
+ struct file_link const *y_parent = (IS_ROOT_FILE_LINK (((struct file_link const *) y))
+ ? 0 : ((struct file_link const *) y)->fl_parent);
+ ADDRESS_COMPARE (x_parent, y_parent, result);
+ if (result)
+ return result;
+ STRING_COMPARE (((struct file_link const *) x)->fl_name,
+ ((struct file_link const *) y)->fl_name, result);
+ return result;
+}
+
+/* Count directory components between flink and its root. */
+
+int
+links_depth (struct file_link const *flink)
+{
+ int depth = 0;
+ while (!IS_ROOT_FILE_LINK (flink))
+ {
+ depth++;
+ flink = flink->fl_parent;
+ }
+ return depth;
+}
+
+#if HAVE_LINK
+
+/****************************************************************************/
+/* Hash stuff for `struct dev_ino'. */
+
+unsigned long
+dev_ino_hash_1 (void const *key)
+{
+ unsigned long result = 0;
+ INTEGER_HASH_1 (((struct dev_ino const *) key)->di_dev, result);
+ INTEGER_HASH_1 (((struct dev_ino const *) key)->di_ino, result);
+ return result;
+}
+
+unsigned long
+dev_ino_hash_2 (void const *key)
+{
+ unsigned long result = 0;
+ INTEGER_HASH_2 (((struct dev_ino const *) key)->di_dev, result);
+ INTEGER_HASH_2 (((struct dev_ino const *) key)->di_ino, result);
+ return result;
+}
+
+int
+dev_ino_hash_compare (void const *x, void const *y)
+{
+ int result;
+ INTEGER_COMPARE (((struct dev_ino const *) x)->di_ino,
+ ((struct dev_ino const *) y)->di_ino, result);
+ if (result)
+ return result;
+ INTEGER_COMPARE (((struct dev_ino const *) x)->di_dev,
+ ((struct dev_ino const *) y)->di_dev, result);
+ return result;
+}
+
+#endif
+
+/*******************************************************************/
+
+struct file_link *
+init_walker (struct idhead *idhp)
+{
+ init_idh_obstacks (idhp);
+ init_idh_tables (idhp);
+ return get_current_dir_link ();
+}
+
+void
+init_idh_obstacks (struct idhead *idhp)
+{
+ obstack_init (&idhp->idh_member_file_obstack);
+ obstack_init (&idhp->idh_file_link_obstack);
+#if HAVE_LINK
+ obstack_init (&idhp->idh_dev_ino_obstack);
+#endif
+}
+
+void
+init_idh_tables (struct idhead *idhp)
+{
+ hash_init (&idhp->idh_member_file_table, 16*1024,
+ member_file_hash_1, member_file_hash_2, member_file_hash_compare);
+ hash_init (&idhp->idh_file_link_table, 16*1024,
+ file_link_hash_1, file_link_hash_2, file_link_hash_compare);
+#if HAVE_LINK
+ hash_init (&idhp->idh_dev_ino_table, 16*1024,
+ dev_ino_hash_1, dev_ino_hash_2, dev_ino_hash_compare);
+#endif
+}
diff --git a/libidu/xnls.h b/libidu/xnls.h
new file mode 100644
index 0000000..f7f5b45
--- /dev/null
+++ b/libidu/xnls.h
@@ -0,0 +1,41 @@
+/* xnls.h -- NLS declarations
+ Copyright (C) 1996 Free Software Foundation, Inc.
+ Written by Greg McGary <gkm@gnu.ai.mit.edu>
+
+ 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.
+*/
+
+#ifndef _i18n_h_
+#define _i18n_h_
+
+/* Take care of NLS matters. */
+
+#if HAVE_LOCALE_H
+# include <locale.h>
+#endif
+#if !HAVE_SETLOCALE
+# define setlocale(Category, Locale) /* empty */
+#endif
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(Text) gettext (Text)
+#else
+# define bindtextdomain(Domain, Directory) /* empty */
+# define textdomain(Domain) /* empty */
+# define _(Text) Text
+#endif
+
+#endif /* not _i18n_h_ */
diff --git a/lisp/Makefile.am b/lisp/Makefile.am
new file mode 100644
index 0000000..54c22d0
--- /dev/null
+++ b/lisp/Makefile.am
@@ -0,0 +1,10 @@
+## Process this file with automake to create Makefile.in
+
+EXTRA_DIST = id-utils.el elisp-comp
+elc_SCRIPTS = id-utils.elc
+elcdir = @LISPDIR@
+SUFFIXES = .el .elc
+
+.el.elc:
+ @echo "WARNING: Warnings can be ignored. :-)"
+ $(SHELL) $(srcdir)/elisp-comp $<
diff --git a/lisp/Makefile.in b/lisp/Makefile.in
new file mode 100644
index 0000000..48d5568
--- /dev/null
+++ b/lisp/Makefile.in
@@ -0,0 +1,160 @@
+# Makefile.in generated automatically by automake 1.0 from Makefile.am
+
+# Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+
+
+SHELL = /bin/sh
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+bindir = @bindir@
+sbindir = @sbindir@
+libexecdir = @libexecdir@
+datadir = @datadir@
+sysconfdir = @sysconfdir@
+sharedstatedir = @sharedstatedir@
+localstatedir = @localstatedir@
+libdir = @libdir@
+infodir = @infodir@
+mandir = @mandir@
+includedir = @includedir@
+oldincludedir = /usr/include
+
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+
+top_builddir = ..
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+transform = @program_transform_name@
+
+EXTRA_DIST = id-utils.el elisp-comp
+elc_SCRIPTS = id-utils.elc
+elcdir = @LISPDIR@
+SUFFIXES = .el .elc
+mkinstalldirs = $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = ../config.h
+SCRIPTS = $(elc_SCRIPTS)
+
+DIST_COMMON = Makefile.am Makefile.in
+
+
+PACKAGE = @PACKAGE@
+VERSION = @VERSION@
+
+DISTFILES = $(DIST_COMMON) $(SOURCES) $(BUILT_SOURCES) $(HEADERS) \
+ $(TEXINFOS) $(INFOS) $(MANS) $(EXTRA_DIST) $(DATA)
+DEP_DISTFILES = $(DIST_COMMON) $(SOURCES) $(BUILT_SOURCES) $(HEADERS) \
+ $(TEXINFOS) $(INFO_DEPS) $(MANS) $(EXTRA_DIST) $(DATA)
+
+TAR = tar
+default: all
+
+
+$(srcdir)/Makefile.in: @MAINT@Makefile.am $(top_srcdir)/configure.in
+ cd $(top_srcdir) && automake $(subdir)/Makefile
+
+Makefile: $(top_builddir)/config.status Makefile.in
+ cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+
+install-elcSCRIPTS: $(elc_SCRIPTS)
+ $(mkinstalldirs) $(elcdir)
+ list="$(elc_SCRIPTS)"; for p in $$list; do \
+ if test -f $$p; then \
+ $(INSTALL_SCRIPT) $$p $(elcdir)/`echo $$p|sed '$(transform)'`; \
+ else if test -f $(srcdir)/$$p; then \
+ $(INSTALL_SCRIPT) $(srcdir)/$$p \
+ $(elcdir)/`echo $$p|sed '$(transform)'`; \
+ else :; fi; fi; \
+ done
+
+uninstall-elcSCRIPTS:
+ list="$(elc_SCRIPTS)"; for p in $$list; do \
+ rm -f $(elcdir)/`echo $$p|sed '$(transform)'`; \
+ done
+tags: TAGS
+TAGS:
+
+
+subdir = lisp
+distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
+distdir: $(DEP_DISTFILES)
+ @for file in `cd $(srcdir) && echo $(DISTFILES)`; do \
+ test -f $(distdir)/$$file \
+ || ln $(srcdir)/$$file $(distdir)/$$file 2> /dev/null \
+ || cp -p $(srcdir)/$$file $(distdir)/$$file; \
+ done
+info:
+
+dvi:
+
+check: all
+
+installcheck:
+
+install-exec:
+
+install-data: install-elcSCRIPTS
+
+install: install-exec install-data all
+ @:
+
+uninstall: uninstall-elcSCRIPTS
+
+all: $(SCRIPTS) Makefile
+
+install-strip:
+ $(MAKE) INSTALL_PROGRAM='$(INSTALL_PROGRAM) -s' install
+installdirs:
+ $(mkinstalldirs) $(elcdir)
+
+
+mostlyclean-generic:
+ test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES)
+
+clean-generic:
+ test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ rm -f Makefile $(DISTCLEANFILES)
+ rm -f config.cache config.log $(CONFIG_HEADER) stamp-h
+
+maintainer-clean-generic:
+ test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+ test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+mostlyclean: mostlyclean-generic
+
+clean: clean-generic mostlyclean
+
+distclean: distclean-generic clean
+ rm -f config.status
+
+maintainer-clean: maintainer-clean-generic distclean
+ @echo "This command is intended for maintainers to use;"
+ @echo "it deletes files that may require special tools to rebuild."
+
+.PHONY: default uninstall-elcSCRIPTS install-elcSCRIPTS tags distdir \
+info dvi check installcheck install-exec install-data install uninstall \
+all installdirs mostlyclean-generic distclean-generic clean-generic \
+maintainer-clean-generic clean mostlyclean distclean maintainer-clean
+
+
+.el.elc:
+ @echo "WARNING: Warnings can be ignored. :-)"
+ $(SHELL) $(srcdir)/elisp-comp $<
+.SUFFIXES:
+.SUFFIXES: $(SUFFIXES)
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/lisp/elisp-comp b/lisp/elisp-comp
new file mode 100755
index 0000000..d9adbfe
--- /dev/null
+++ b/lisp/elisp-comp
@@ -0,0 +1,42 @@
+#!/bin/sh
+# Copyright (C) 1995 Free Software Foundation, Inc.
+# François Pinard <pinard@iro.umontreal.ca>, 1995.
+#
+# 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.
+
+# This script byte-compiles all `.el' files which are part of its
+# arguments, using GNU Emacs, and put the resulting `.elc' files into
+# the current directory, so disregarding the original directories used
+# in `.el' arguments.
+#
+# This script manages in such a way that all Emacs LISP files to
+# be compiled are made visible between themselves, in the event
+# they require or load-library one another.
+
+if test $# = 0; then
+ echo 1>&2 "No files given to $0"
+else
+ tempdir=elc.$$
+ mkdir $tempdir
+ cp $* $tempdir
+ cd $tempdir
+
+ echo "(setq load-path (cons nil load-path))" > script
+ emacs -batch -l script -f batch-byte-compile *.el
+ mv *.elc ..
+
+ cd ..
+ rm -fr $tempdir
+fi
diff --git a/lisp/id-utils.el b/lisp/id-utils.el
new file mode 100644
index 0000000..09c7f9b
--- /dev/null
+++ b/lisp/id-utils.el
@@ -0,0 +1,63 @@
+;;; id-utils.el -- emacs interface to `lid -R grep', a.k.a. `gid'
+;;; Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+;;; Greg McGary <gkm@gnu.ai.mit.edu>.
+
+;; This file is part of GNU id-utils.
+
+;; GNU id-utils 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.
+
+;; GNU id-utils 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 GNU Emacs; see the file COPYING. If not, write to the
+;; Free Software Foundation, 59 Temple Place - Suite 330, Boston,
+;; MA 02111-1307, USA.
+
+;;; This package provides the tools meant to help editing PO files,
+;;; as documented in the GNU id-utils user's manual. See this manual
+;;; for user documentation, which is not repeated here.
+
+;;; To install, merely put this file somewhere GNU Emacs will find it,
+;;; then add the following lines to your .emacs file:
+;;;
+;;; (autoload 'gid "id-utils")
+;;;
+;;; You may also adjust some customizations variables, below, by defining
+;;; them in your .emacs file.
+
+(require 'compile)
+(provide 'id-utils)
+
+(defvar gid-command "gid" "The command run by the gid function.")
+
+(defun gid (args)
+ "Run gid, with user-specified ARGS, and collect output in a buffer.
+While gid runs asynchronously, you can use the \\[next-error] command to
+find the text that gid hits refer to. The command actually run is
+defined by the gid-command variable."
+ (interactive (list (read-input
+ (concat "Run " gid-command " (with args): ") (word-around-point))))
+ (let (compile-command
+ (compilation-error-regexp-alist grep-regexp-alist)
+ (compilation-buffer-name-function '(lambda (mode)
+ (concat "*" gid-command " " args "*"))))
+ ;; For portability to v19, use compile rather than compile-internal.
+ (compile (concat gid-command " " args))))
+
+(defun word-around-point ()
+ "Return the word around the point as a string."
+ (save-excursion
+ (if (not (eobp))
+ (forward-char 1))
+ (forward-word -1)
+ (forward-word 1)
+ (forward-sexp -1)
+ (buffer-substring (point) (progn
+ (forward-sexp 1)
+ (point)))))
diff --git a/src/fnid.c b/src/fnid.c
new file mode 100644
index 0000000..309480a
--- /dev/null
+++ b/src/fnid.c
@@ -0,0 +1,192 @@
+/* fnid.c -- report which files constitute an ID database
+ Copyright (C) 1996 Free Software Foundation, Inc.
+ Written by Greg McGary <gkm@gnu.ai.mit.edu>
+
+ 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. */
+
+#include <config.h>
+#include <stdio.h>
+#include <getopt.h>
+#include "xfnmatch.h"
+#include "xstring.h"
+#include "xmalloc.h"
+#include "xnls.h"
+#include "idfile.h"
+#include "pathmax.h"
+#include "error.h"
+#include "xalloca.h"
+
+void scan_files __P((struct idhead *idhp));
+void scan_member_file __P((struct member_file const *member));
+void usage __P((void));
+
+char const *program_name;
+int show_version = 0;
+int show_help = 0;
+struct idhead idh;
+struct file_link *cw_dlink;
+int no_id_flag = 1;
+
+/* How shall we separate file names? */
+
+enum separator_style separator_style = ss_contextual;
+
+void
+usage (void)
+{
+ fprintf (stderr, _("Try `%s --help' for more information.\n"),
+ program_name);
+ exit (1);
+}
+
+static struct option const long_options[] =
+{
+ { "file", required_argument, 0, 'f' },
+ { "separator", required_argument, 0, 'S' },
+ { "help", no_argument, &show_help, 1 },
+ { "version", no_argument, &show_version, 1 },
+ { 0 }
+};
+
+static void
+help_me (void)
+{
+ printf (_("\
+Usage: %s [OPTION]... [PATTERN]...\n\
+"), program_name);
+
+ printf (_("\
+Print constituent file names that match PATTERN,\n\
+using shell-style wildcards.\n\
+ -f, --file=FILE file name of ID database\n\
+ -S, --separator=STYLE STYLE is one of `braces', `space' or `newline'\n\
+ --help display this help and exit\n\
+ --version output version information and exit\n\
+"));
+ exit (0);
+}
+
+int
+main (int argc, char **argv)
+{
+ program_name = argv[0];
+ idh.idh_file_name = 0;
+
+ /* Set locale according to user's wishes. */
+ setlocale (LC_ALL, "");
+
+ /* Tell program which translations to use and where to find. */
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+
+ for (;;)
+ {
+ int optc = getopt_long (argc, argv, "f:S:",
+ long_options, (int *) 0);
+ if (optc < 0)
+ break;
+ switch (optc)
+ {
+ case 0:
+ break;
+
+ case 'f':
+ idh.idh_file_name = optarg;
+ break;
+
+ case 'S':
+ separator_style = parse_separator_style (optarg);
+ break;
+
+ default:
+ usage ();
+ }
+ }
+
+ if (show_version)
+ {
+ printf ("%s - %s\n", program_name, PACKAGE_VERSION);
+ exit (0);
+ }
+
+ if (show_help)
+ help_me ();
+
+ if (separator_style == ss_contextual)
+ {
+ if (isatty (STDOUT_FILENO))
+ separator_style = DEFAULT_SEPARATOR_STYLE;
+ else
+ separator_style = ss_newline;
+ }
+
+ argc -= optind;
+ argv += optind;
+ if (argc == 0)
+ {
+ static char *star = (char *) "*";
+ argc = 1;
+ argv = &star;
+ }
+
+ /* Look for the ID database up the tree */
+ idh.idh_file_name = locate_id_file_name (idh.idh_file_name);
+ if (idh.idh_file_name == 0)
+ error (1, errno, _("can't locate `ID'"));
+
+ init_idh_obstacks (&idh);
+ init_idh_tables (&idh);
+
+ cw_dlink = get_current_dir_link ();
+ {
+ struct file_link **members = read_id_file (idh.idh_file_name, &idh);
+ struct file_link **members_N = &members[idh.idh_files];
+ struct file_link **flinkv_0 = MALLOC (struct file_link *, idh.idh_files + 1);
+ struct file_link **flinkv = flinkv_0;
+ char **patv_0 = MALLOC (char *, argc * 2);
+ char **patv_N;
+ char **patv = patv_0;
+
+ for ( ; argc; argc--, argv++)
+ {
+ char *arg = *argv;
+ *patv++ = arg;
+ if (*arg != '*' && *arg != '/')
+ {
+ char *pat = MALLOC (char, strlen (arg) + 2);
+ sprintf (pat, "*/%s", arg);
+ *patv++ = pat;
+ }
+ }
+ patv_N = patv;
+
+ for ( ; members < members_N; members++)
+ {
+ char *file_name = ALLOCA (char, PATH_MAX);
+ maybe_relative_file_name (file_name, *members, cw_dlink);
+ for (patv = patv_0; patv < patv_N; patv++)
+ {
+ if (fnmatch (*patv, file_name, MAYBE_FNM_CASEFOLD) == 0)
+ {
+ *flinkv++ = *members;
+ break;
+ }
+ }
+ }
+ *flinkv = 0;
+ print_filenames (flinkv_0, separator_style);
+ }
+ return 0;
+}
diff --git a/src/xtokid.c b/src/xtokid.c
new file mode 100644
index 0000000..c48834c
--- /dev/null
+++ b/src/xtokid.c
@@ -0,0 +1,214 @@
+/* idx.c -- simple interface for testing scanners scanners
+ Copyright (C) 1986, 1995, 1996 Free Software Foundation, Inc.
+ Written by Greg McGary <gkm@gnu.ai.mit.edu>
+
+ 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. */
+
+#include <config.h>
+#include <stdio.h>
+#include <getopt.h>
+#include "xnls.h"
+#include "scanners.h"
+#include "idfile.h"
+#include "pathmax.h"
+#include "error.h"
+
+void scan_files __P((struct idhead *idhp));
+void scan_member_file __P((struct member_file const *member));
+void usage __P((void));
+
+char const *program_name;
+char *lang_map_file_name = 0;
+int show_version = 0;
+int show_help = 0;
+struct idhead idh;
+struct file_link *cw_dlink;
+
+void
+usage (void)
+{
+ fprintf (stderr, _("Try `%s --help' for more information.\n"),
+ program_name);
+ exit (1);
+}
+
+static struct option const long_options[] =
+{
+ { "include", required_argument, 0, 'i' },
+ { "exclude", required_argument, 0, 'x' },
+ { "lang-option", required_argument, 0, 'l' },
+ { "lang-map", required_argument, 0, 'm' },
+ { "default-lang", required_argument, 0, 'd' },
+ { "prune", required_argument, 0, 'p' },
+ { "help", no_argument, &show_help, 1 },
+ { "version", no_argument, &show_version, 1 },
+ { 0 }
+};
+
+static void
+help_me (void)
+{
+ printf (_("\
+Usage: %s [OPTION]... [FILE]...\n\
+"), program_name);
+
+ printf (_("\
+Print all tokens found in a source file.\n\
+ -i, --include=LANGS include languages in LANGS (default: \"C C++ asm\")\n\
+ -x, --exclude=LANGS exclude languages in LANGS\n\
+ -l, --lang-option=L:OPT pass OPT as a default for language L (see below)\n\
+ -m, --lang-map=MAPFILE use MAPFILE to map file names onto source language\n\
+ -d, --default-lang=LANG make LANG the default source language\n\
+ -p, --prune=NAMES exclude the named files and/or directories\n\
+ --help display this help and exit\n\
+ --version output version information and exit\n\
+\n\
+The following arguments apply to the language-specific scanners:\n\
+"));
+ language_help_me ();
+ exit (0);
+}
+
+int
+main (int argc, char **argv)
+{
+ program_name = argv[0];
+
+ /* Set locale according to user's wishes. */
+ setlocale (LC_ALL, "");
+
+ /* Tell program which translations to use and where to find. */
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+
+ for (;;)
+ {
+ int optc = getopt_long (argc, argv, "i:x:l:m:d:p:",
+ long_options, (int *) 0);
+ if (optc < 0)
+ break;
+ switch (optc)
+ {
+ case 0:
+ break;
+
+ case 'i':
+ include_languages (optarg);
+ break;
+
+ case 'x':
+ exclude_languages (optarg);
+ break;
+
+ case 'l':
+ language_save_arg (optarg);
+ break;
+
+ case 'm':
+ lang_map_file_name = optarg;
+ break;
+
+ case 'd':
+ set_default_language (optarg);
+ break;
+
+ case 'p':
+ if (cw_dlink == 0)
+ cw_dlink = init_walker (&idh);
+ prune_file_names (optarg, cw_dlink);
+ break;
+
+ default:
+ usage ();
+ }
+ }
+
+ if (show_version)
+ {
+ printf ("%s - %s\n", program_name, PACKAGE_VERSION);
+ exit (0);
+ }
+
+ if (show_help)
+ help_me ();
+
+ argc -= optind;
+ argv += optind;
+ if (argc == 0)
+ {
+ static char *dot = (char *) ".";
+ argc = 1;
+ argv = &dot;
+ }
+
+ language_getopt ();
+ if (cw_dlink == 0)
+ cw_dlink = init_walker (&idh);
+ parse_language_map (lang_map_file_name);
+
+ while (argc--)
+ {
+ struct file_link *flink = parse_file_name (*argv++, cw_dlink);
+ if (flink)
+ walk_flink (flink, 0);
+ }
+ mark_member_file_links (&idh);
+ obstack_init (&tokens_obstack);
+ scan_files (&idh);
+
+ return 0;
+}
+
+void
+scan_files (struct idhead *idhp)
+{
+ struct member_file **members_0
+ = (struct member_file **) hash_dump (&idhp->idh_member_file_table,
+ 0, member_file_qsort_compare);
+ struct member_file **end = &members_0[idhp->idh_member_file_table.ht_fill];
+ struct member_file **members;
+
+ for (members = members_0; members < end; members++)
+ scan_member_file (*members);
+ free (members_0);
+}
+
+void
+scan_member_file (struct member_file const *member)
+{
+ struct lang_args const *lang_args = member->mf_lang_args;
+ struct language const *lang = lang_args->la_language;
+ get_token_func_t get_token = lang->lg_get_token;
+ struct file_link *flink = member->mf_link;
+ FILE *source_FILE;
+
+ chdir_to_link (flink->fl_parent);
+ source_FILE = fopen (flink->fl_name, "r");
+ if (source_FILE)
+ {
+ void const *args = lang_args->la_args_digested;
+ int flags;
+ struct token *token;
+
+ while ((token = (*get_token) (source_FILE, args, &flags)) != NULL)
+ {
+ puts (token->tok_name);
+ obstack_free (&tokens_obstack, token);
+ }
+ fclose (source_FILE);
+ }
+ else
+ error (0, errno, _("can't open `%s'"), flink->fl_name);
+}
diff --git a/testsuite/Makefile.am b/testsuite/Makefile.am
new file mode 100644
index 0000000..938a21d
--- /dev/null
+++ b/testsuite/Makefile.am
@@ -0,0 +1,8 @@
+## Process this file with automake to create Makefile.in
+
+TESTS = consistency
+
+EXTRA_DIST = $(TESTS)
+
+consistency-real: all
+ $(SHELL) $(srcdir)/consistency $(top_srcdir) $(top_builddir)/src
diff --git a/testsuite/Makefile.in b/testsuite/Makefile.in
new file mode 100644
index 0000000..3c763f6
--- /dev/null
+++ b/testsuite/Makefile.in
@@ -0,0 +1,159 @@
+# Makefile.in generated automatically by automake 1.0 from Makefile.am
+
+# Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+
+
+SHELL = /bin/sh
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+bindir = @bindir@
+sbindir = @sbindir@
+libexecdir = @libexecdir@
+datadir = @datadir@
+sysconfdir = @sysconfdir@
+sharedstatedir = @sharedstatedir@
+localstatedir = @localstatedir@
+libdir = @libdir@
+infodir = @infodir@
+mandir = @mandir@
+includedir = @includedir@
+oldincludedir = /usr/include
+
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+
+top_builddir = ..
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+transform = @program_transform_name@
+
+TESTS = consistency
+
+EXTRA_DIST = $(TESTS)
+mkinstalldirs = $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = ../config.h
+DIST_COMMON = Makefile.am Makefile.in
+
+
+PACKAGE = @PACKAGE@
+VERSION = @VERSION@
+
+DISTFILES = $(DIST_COMMON) $(SOURCES) $(BUILT_SOURCES) $(HEADERS) \
+ $(TEXINFOS) $(INFOS) $(MANS) $(EXTRA_DIST) $(DATA)
+DEP_DISTFILES = $(DIST_COMMON) $(SOURCES) $(BUILT_SOURCES) $(HEADERS) \
+ $(TEXINFOS) $(INFO_DEPS) $(MANS) $(EXTRA_DIST) $(DATA)
+
+TAR = tar
+default: all
+
+
+$(srcdir)/Makefile.in: @MAINT@Makefile.am $(top_srcdir)/configure.in
+ cd $(top_srcdir) && automake $(subdir)/Makefile
+
+Makefile: $(top_builddir)/config.status Makefile.in
+ cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+tags: TAGS
+TAGS:
+
+
+subdir = testsuite
+distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
+distdir: $(DEP_DISTFILES)
+ @for file in `cd $(srcdir) && echo $(DISTFILES)`; do \
+ test -f $(distdir)/$$file \
+ || ln $(srcdir)/$$file $(distdir)/$$file 2> /dev/null \
+ || cp -p $(srcdir)/$$file $(distdir)/$$file; \
+ done
+check-TESTS: $(TESTS)
+ @failed=0; all=0; \
+ srcdir=$(srcdir); export srcdir; \
+ for tst in $(TESTS); do \
+ all=`expr $$all + 1`; \
+ if test -f $$tst; then dir=.; \
+ else dir="$(srcdir)"; fi; \
+ if $$dir/$$tst; then \
+ echo "PASS: $$tst"; \
+ else \
+ failed=`expr $$failed + 1`; \
+ echo "FAIL: $$tst"; \
+ fi; \
+ done; \
+ if test "$$failed" -eq 0; then \
+ echo "========================"; \
+ echo "All $$all tests passed"; \
+ echo "========================"; \
+ else \
+ echo "$$failed of $$all tests failed"; \
+ fi
+info:
+
+dvi:
+
+check: all check-TESTS
+
+installcheck:
+
+install-exec:
+
+install-data:
+
+install: install-exec install-data all
+ @:
+
+uninstall:
+
+all: Makefile
+
+install-strip:
+ $(MAKE) INSTALL_PROGRAM='$(INSTALL_PROGRAM) -s' install
+installdirs:
+
+
+mostlyclean-generic:
+ test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES)
+
+clean-generic:
+ test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ rm -f Makefile $(DISTCLEANFILES)
+ rm -f config.cache config.log $(CONFIG_HEADER) stamp-h
+
+maintainer-clean-generic:
+ test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+ test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+mostlyclean: mostlyclean-generic
+
+clean: clean-generic mostlyclean
+
+distclean: distclean-generic clean
+ rm -f config.status
+
+maintainer-clean: maintainer-clean-generic distclean
+ @echo "This command is intended for maintainers to use;"
+ @echo "it deletes files that may require special tools to rebuild."
+
+.PHONY: default tags distdir check-TESTS info dvi check installcheck \
+install-exec install-data install uninstall all installdirs \
+mostlyclean-generic distclean-generic clean-generic \
+maintainer-clean-generic clean mostlyclean distclean maintainer-clean
+
+
+consistency-real: all
+ $(SHELL) $(srcdir)/consistency $(top_srcdir) $(top_builddir)/src
+.SUFFIXES:
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/testsuite/consistency b/testsuite/consistency
new file mode 100755
index 0000000..d375b2e
--- /dev/null
+++ b/testsuite/consistency
@@ -0,0 +1,63 @@
+#!/bin/sh
+
+export LANG; LANG=C
+
+case $# in
+0) exec make consistency-real;;
+esac
+
+srcdir=$1
+bindir=$2
+libdir=$srcdir/libidu
+
+m_flag="-m $libdir/id-lang.map"
+
+errors=
+
+cmd="$bindir/mkid $m_flag $srcdir"
+echo $cmd
+if eval $cmd; then
+ files="`$bindir/fnid || exit 1`"
+
+ if $bindir/xtokid $m_flag $files |sort -u >ID.xti &&
+ $bindir/lid |sed -e 's/[ ].*//' |sort -u >ID.lid &&
+ cmp -s ID.xti ID.lid
+ then
+ rm -f ID.xti ID.lid
+ echo "Good. xtokid and lid agree."
+ else
+ mv ID.xti ID.bad-xti
+ mv ID.fid ID.bad-fid
+ 1>&2 echo "Oops! xtokid and lid disagree--look in ID.bad-{xtokid,lid}"
+ errors=t
+ fi
+
+ xtokid_file=$$.xti
+ fid_file=$$.fid
+
+ for file in $files
+ do
+ if $bindir/fid $file >$fid_file &&
+ $bindir/xtokid $m_flag $file |sort -u >$xtokid_file &&
+ cmp -s $xtokid_file $fid_file;
+ then
+ echo "Good. xtokid and fid agree for $file"
+ else
+ 1>&2 echo "Oops! xtokid and fid disagree for $file--look in $file.bad-{xtokid,fid}"
+ mv $xtokid_file $file.bad-xti
+ mv $fid_file $file.bad-fid
+ errors=t
+ fi
+ done
+ rm -f $xtokid_file $fid_file
+else
+ 1>&2 echo "Oops! mkid failed."
+ errors=t
+fi
+
+case x$errors in
+xt) echo "Some checks failed."
+ exit 1;;
+*) echo "All checks successful."
+ exit 0;;
+esac