Initial revision
authorTheodore Tso <tytso@mit.edu>
Thu, 3 Jun 1993 12:31:48 +0000 (12:31 +0000)
committerTheodore Tso <tytso@mit.edu>
Thu, 3 Jun 1993 12:31:48 +0000 (12:31 +0000)
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@2610 dc483132-0cff-0310-8789-dd5450dbe970

47 files changed:
src/util/Imakefile [new file with mode: 0644]
src/util/et/Imakefile [new file with mode: 0644]
src/util/et/Makefile.in [new file with mode: 0644]
src/util/et/com_err.3 [new file with mode: 0644]
src/util/et/com_err.texinfo [new file with mode: 0644]
src/util/et/compile_et.1 [new file with mode: 0644]
src/util/et/compile_et.sh [new file with mode: 0644]
src/util/et/config_script [new file with mode: 0644]
src/util/et/error_table.y [new file with mode: 0644]
src/util/et/test1.et [new file with mode: 0644]
src/util/et/test2.et [new file with mode: 0644]
src/util/et/texinfo.tex [new file with mode: 0644]
src/util/et/vfprintf.c [new file with mode: 0644]
src/util/ss/Imakefile [new file with mode: 0644]
src/util/ss/Makefile.in [new file with mode: 0644]
src/util/ss/cmd_tbl.lex.l [new file with mode: 0644]
src/util/ss/config_script [new file with mode: 0644]
src/util/ss/copyright.h [new file with mode: 0644]
src/util/ss/ct.y [new file with mode: 0644]
src/util/ss/ct_c.awk [new file with mode: 0644]
src/util/ss/ct_c.sed [new file with mode: 0644]
src/util/ss/data.c [new file with mode: 0644]
src/util/ss/error.c [new file with mode: 0644]
src/util/ss/execute_cmd.c [new file with mode: 0644]
src/util/ss/help.c [new file with mode: 0644]
src/util/ss/invocation.c [new file with mode: 0644]
src/util/ss/list_rqs.c [new file with mode: 0644]
src/util/ss/listen.c [new file with mode: 0644]
src/util/ss/mit-sipb-copyright.h [new file with mode: 0644]
src/util/ss/mk_cmds.c [new file with mode: 0644]
src/util/ss/mk_cmds.sh [new file with mode: 0644]
src/util/ss/options.c [new file with mode: 0644]
src/util/ss/pager.c [new file with mode: 0644]
src/util/ss/parse.c [new file with mode: 0644]
src/util/ss/prompt.c [new file with mode: 0644]
src/util/ss/request_tbl.c [new file with mode: 0644]
src/util/ss/requests.c [new file with mode: 0644]
src/util/ss/ss.h [new file with mode: 0644]
src/util/ss/ss_err.et [new file with mode: 0644]
src/util/ss/ss_internal.h [new file with mode: 0644]
src/util/ss/std_rqs.ct [new file with mode: 0644]
src/util/ss/test_ss.c [new file with mode: 0644]
src/util/ss/utils.c [new file with mode: 0644]
src/util/unifdef/Imakefile [new file with mode: 0644]
src/util/unifdef/Makefile [new file with mode: 0644]
src/util/unifdef/unifdef.1 [new file with mode: 0644]
src/util/unifdef/unifdef.c [new file with mode: 0644]

diff --git a/src/util/Imakefile b/src/util/Imakefile
new file mode 100644 (file)
index 0000000..61b6de9
--- /dev/null
@@ -0,0 +1,29 @@
+#      $Source$
+#      $Author$
+#      $Id$
+#
+#  Copyright 1990,1991 by the Massachusetts Institute of Technology.
+#  All Rights Reserved.
+# 
+# Export of this software from the United States of America is assumed
+#   to require a specific license from the United States Government.
+#   It is the responsibility of any person or organization contemplating
+#   export to obtain such a license before exporting.
+# 
+# WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+# distribute this software and its documentation for any purpose and
+# without fee is hereby granted, provided that the above copyright
+# notice appear in all copies and that both that copyright notice and
+# this permission notice appear in supporting documentation, and that
+# the name of M.I.T. not be used in advertising or publicity pertaining
+# to distribution of the software without specific, written prior
+# permission.  M.I.T. makes no representations about the suitability of
+# this software for any purpose.  It is provided "as is" without express
+# or implied warranty.
+# 
+# 
+#define IHaveSubdirs
+#define PassCDebugFlags
+
+SUBDIRS=unifdef et ss
+MakeSubdirs($(SUBDIRS))
diff --git a/src/util/et/Imakefile b/src/util/et/Imakefile
new file mode 100644 (file)
index 0000000..747184c
--- /dev/null
@@ -0,0 +1,54 @@
+#      $Source$
+#      $Author$
+#      $Id$
+#
+#  Copyright 1993 by the Massachusetts Institute of Technology.
+#  All Rights Reserved.
+# 
+# Export of this software from the United States of America is assumed
+#   to require a specific license from the United States Government.
+#   It is the responsibility of any person or organization contemplating
+#   export to obtain such a license before exporting.
+# 
+# WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+# distribute this software and its documentation for any purpose and
+# without fee is hereby granted, provided that the above copyright
+# notice appear in all copies and that both that copyright notice and
+# this permission notice appear in supporting documentation, and that
+# the name of M.I.T. not be used in advertising or publicity pertaining
+# to distribution of the software without specific, written prior
+# permission.  M.I.T. makes no representations about the suitability of
+# this software for any purpose.  It is provided "as is" without express
+# or implied warranty.
+# 
+# 
+NormalLibraryObjectRule()
+
+#ifdef HasVfprintf
+VPOBJS=
+VPSRCS=
+#else
+VPOBJS=vfprintf.o 
+VPSRCS=$(SRCDIR)vfprintf.c
+#endif
+
+OBJS=  error_message.o et_name.o init_et.o com_err.o $(VPOBJS)
+SRCS=  error_message.c et_name.c init_et.c com_err.c $(VPSRCS)
+#ifndef LinuxArchitecture
+DEFINES= -DNEED_SYS_ERRLIST
+#endif
+
+Krb5LibraryTarget(com_err,$(OBJS))
+
+compile_et: compile_et.sh
+       ./config_script compile_et.sh $(AWK) > compile_et
+       chmod +x compile_et
+
+all:: compile_et
+
+includes:: compile_et
+       -$(RM) -f $(TOP)/include/com_err.h
+       $(LN) ../$(CURRENT_DIR)/com_err.h $(TOP)/include
+
+clean::
+       $(RM) compile_et
diff --git a/src/util/et/Makefile.in b/src/util/et/Makefile.in
new file mode 100644 (file)
index 0000000..8043430
--- /dev/null
@@ -0,0 +1,109 @@
+#### insert configury here
+BUILDTOP=../..
+LINTFLAGS=-uhvb 
+LINTFILES= error_message.c et_name.c init_et.c com_err.c
+LIBOBJS= error_message.o et_name.o init_et.o com_err.o
+# for et_lex.lex.c include in error_table.y
+LOCALINCLUDE=-I. 
+
+FILES= Makefile et_name.c error_message.c compile_et.c \
+               et_lex.lex.l error_table.y init_et.c \
+               com_err.c com_err.h \
+               error_table.h mit-sipb-copyright.h \
+               test_et.c test1.et test2.et \
+               compiler.h internal.h \
+               com_err.texinfo texinfo.tex
+CFILES=        compile_et.c error_table.c error_message.c et_name.c \
+       init_et.c com_err.c
+
+SRCS=$(CFILES)
+
+#
+# what to build...
+#
+
+#
+# rules
+#
+error_table.o: et_lex.lex.c
+
+#
+# real entries...
+#
+
+all:: compile_et
+
+compile_et: compile_et.o error_table.o
+       $(CC) $(CFLAGS) -o $@ compile_et.o error_table.o $(LEXLIB) $(BSDLIB)
+       
+install::
+       $(INSTALLPROG) compile_et ${DESTDIR}$(PROGDIR)/compile_et
+
+clean::
+       $(RM) compile_et compile_et.o error_table.o
+
+depend:: 
+
+install:: com_err.h 
+       $(INSTALLFILE) $(srcdir)/com_err.h  $(DESTDIR)$(INCLDIR)/com_err.h
+
+install:: mit-sipb-copyright.h
+       $(INSTALLFILE) $(srcdir)/mit-sipb-copyright.h $(DESTDIR)$(INCLDIR)/mit-sipb-copyright.h
+
+install:: com_err.3
+       $(INSTALLFILE) $(srcdir)/com_err.3 ${DESTDIR}${MANDIR}/man3/com_err.3
+
+install:: compile_et.1
+       $(INSTALLFILE) $(srcdir)/compile_et.1 ${DESTDIR}${MANDIR}/man1/compile_et.1
+
+
+## install_library_target(com_err,$(LIBOBJS),$(LINTFILES),)
+all:: libcom_err.a
+
+libcom_err.a: $(LIBOBJS)
+       $(RM) $@.bak
+       -$(MV) $@ $@.bak
+       $(ARCHIVE) $@ $(LIBOBJS)
+       $(RANLIB) $@
+
+clean::
+       $(RM) libcom_err.a
+       $(RM) $(LIBOBJS)
+
+install::
+       $(INSTALLLIB) libcom_err.a $(DESTDIR)$(LIBDIR)/libcom_err.a
+       $(CHMOD) 644 $(DESTDIR)$(LIBDIR)/libcom_err.a
+       $(RANLIB)    $(DESTDIR)$(LIBDIR)/libcom_err.a
+       $(CHMOD) 444 $(DESTDIR)$(LIBDIR)/libcom_err.a
+## 
+
+clean::
+       rm -f *~ \#* *.bak \
+               *.otl *.aux *.toc *.PS *.dvi *.x9700 *.ps \
+               *.cp *.fn *.ky *.log *.pg *.tp *.vr \
+               *.o profiled/?*.o libcom_err.a libcom_err_p.a \
+               com_err.o compile_et \
+               et.ar TAGS y.tab.c lex.yy.c error_table.c \
+               et_lex.lex.c \
+               test1.h test1.c test2.h test2.c test_et \
+               eddep makedep *.ln
+
+
+
+com_err.ps : com_err.dvi
+com_err.dvi: com_err.texinfo
+
+libcom_err.o:  $(LIBOBJS)
+       ld -r -s -o libcom_err.o $(LIBOBJS)
+       chmod -x libcom_err.o
+
+
+archive:       et.tar
+
+TAGS:  et_name.c error_message.c compile_et.c error_table.c \
+               lex.yy.c init_et.c
+       etags et_name.c error_message.c compile_et.c \
+               error_table.c init_et.c
+
+depend::  et_lex.lex.c
+
diff --git a/src/util/et/com_err.3 b/src/util/et/com_err.3
new file mode 100644 (file)
index 0000000..ee4375b
--- /dev/null
@@ -0,0 +1,96 @@
+.\" Copyright (c) 1988 Massachusetts Institute of Technology,
+.\" Student Information Processing Board.  All rights reserved.
+.\"
+.\" $Header$
+.\"
+.TH COM_ERR 3 "22 Nov 1988" SIPB
+.SH NAME
+com_err \- common error display routine
+.SH SYNOPSIS
+.nf
+ #include <com_err.h>
+.PP
+void com_err (whoami, code, format, ...);
+       const char *whoami;
+       long code;
+       const char *format;
+.PP
+proc = set_com_err_hook (proc);
+.fi
+void (*
+.I proc
+) (const char *, long, const char *, va_list);
+.nf
+.PP
+proc = reset_com_err_hook ();
+.PP
+void initialize_XXXX_error_table ();
+.fi
+.SH DESCRIPTION
+.I Com_err
+displays an error message on the standard error stream
+.I stderr
+(see
+.IR stdio (3S))
+composed of the
+.I whoami
+string, which should specify the program name or some subportion of
+a program, followed by an error message generated from the
+.I code
+value (derived from
+.IR compile_et (1)),
+and a string produced using the
+.I format
+string and any following arguments, in the same style as
+.IR fprintf (3).
+
+The behavior of
+.I com_err
+can be modified using
+.I set_com_err_hook;
+this defines a procedure which is called with the arguments passed to
+.I com_err,
+instead of the default internal procedure which sends the formatted
+text to error output.  Thus the error messages from a program can all
+easily be diverted to another form of diagnostic logging, such as
+.IR syslog (3).
+.I Reset_com_err_hook
+may be used to restore the behavior of
+.I com_err
+to its default form.  Both procedures return the previous ``hook''
+value.  These ``hook'' procedures must have the declaration given for
+.I proc
+above in the synopsis.
+
+The
+.I initialize_XXXX_error_table
+routine is generated mechanically by
+.IR compile_et (1)
+from a source file containing names and associated strings.  Each
+table has a name of up to four characters, which is used in place of
+the
+.B XXXX
+in the name of the routine.  These routines should be called before
+any of the corresponding error codes are used, so that the
+.I com_err
+library will recognize error codes from these tables when they are
+used.
+
+The
+.B com_err.h
+header file should be included in any source file that uses routines
+from the
+.I com_err
+library; executable files must be linked using
+.I ``-lcom_err''
+in order to cause the
+.I com_err
+library to be included.
+
+.\" .IR for manual entries
+.\" .PP for paragraph breaks
+
+.SH "SEE ALSO"
+compile_et (1), syslog (3).
+
+Ken Raeburn, "A Common Error Description Library for UNIX".
diff --git a/src/util/et/com_err.texinfo b/src/util/et/com_err.texinfo
new file mode 100644 (file)
index 0000000..2f4b266
--- /dev/null
@@ -0,0 +1,554 @@
+\input texinfo @c -*-texinfo-*-
+
+@c $Header$
+@c $Source$
+@c $Locker$
+
+@c Note that although this source file is in texinfo format (more
+@c or less), it is not yet suitable for turning into an ``info''
+@c file.  Sorry, maybe next time.
+@c
+@c In order to produce hardcopy documentation from a texinfo file,
+@c run ``tex com_err.texinfo'' which will load in texinfo.tex,
+@c provided in this distribution.  (texinfo.tex is from the Free
+@c Software Foundation, and is under different copyright restrictions
+@c from the rest of this package.)
+
+@ifinfo
+@barfo
+@end ifinfo
+
+@iftex
+@tolerance 10000
+
+@c Mutate section headers...
+@begingroup
+  @catcode\11#=6
+  @gdef@secheading#1#2#3{@secheadingi {#3@enspace #1}}
+@endgroup
+@end iftex
+
+@setfilename com_err
+@settitle A Common Error Description Library for UNIX
+
+@ifinfo
+This file documents the use of the Common Error Description library.
+
+Copyright (C) 1987, 1988 Student Information Processing Board of the
+Massachusetts Institute of Technology.
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted, provided
+that the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the names of M.I.T. and the M.I.T. S.I.P.B. not be
+used in advertising or publicity pertaining to distribution of the software
+without specific, written prior permission.  M.I.T. and the M.I.T. S.I.P.B.
+make no representations about the suitability of this software for any
+purpose.  It is provided "as is" without express or implied warranty.
+
+Note that the file texinfo.tex, provided with this distribution, is from
+the Free Software Foundation, and is under different copyright restrictions
+from the remainder of this package.
+
+@end ifinfo
+
+@ignore
+Permission is granted to process this file through Tex and print the
+results, provided the printed document carries copying permission
+notice identical to this one except for the removal of this paragraph
+(this paragraph not being relevant to the printed manual).
+
+@end ignore
+
+@setchapternewpage odd
+
+@titlepage
+@center @titlefont{A Common Error Description}
+@center @titlefont{Library for UNIX}
+@sp 2
+@center Ken Raeburn
+@center Bill Sommerfeld
+@sp 1
+@center MIT Student Information Processing Board
+@sp 3
+@center last updated 1 January 1989
+@center for version 1.2
+@center ***DRAFT COPY ONLY***
+
+@vskip 2in
+
+@center @b{Abstract}
+
+UNIX has always had a clean and simple system call interface, with a
+standard set of error codes passed between the kernel and user
+programs.  Unfortunately, the same cannot be said of many of the
+libraries layered on top of the primitives provided by the kernel.
+Typically, each one has used a different style of indicating errors to
+their callers, leading to a total hodgepodge of error handling, and
+considerable amounts of work for the programmer.  This paper describes
+a library and associated utilities which allows a more uniform way for
+libraries to return errors to their callers, and for programs to
+describe errors and exceptional conditions to their users.
+
+@page
+@vskip 0pt plus 1filll
+
+Copyright @copyright{} 1987, 1988 by the Student Information Processing
+Board of the Massachusetts Institute of Technology.
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted, provided
+that the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the names of M.I.T. and the M.I.T. S.I.P.B. not be
+used in advertising or publicity pertaining to distribution of the software
+without specific, written prior permission.  M.I.T. and the M.I.T. S.I.P.B.
+make no representations about the suitability of this software for any
+purpose.  It is provided "as is" without express or implied warranty.
+
+Note that the file texinfo.tex, provided with this distribution, is from
+the Free Software Foundation, and is under different copyright restrictions
+from the remainder of this package.
+
+@end titlepage
+
+@ifinfo
+@c should put a menu here someday....
+@end ifinfo
+
+@page
+
+@section Why com_err?
+
+In building application software packages, a programmer often has to
+deal with a number of libraries, each of which can use a different
+error-reporting mechanism.  Sometimes one of two values is returned,
+indicating simply SUCCESS or FAILURE, with no description of errors
+encountered.  Sometimes it is an index into a table of text strings,
+where the name of the table used is dependent on the library being
+used when the error is generated; since each table starts numbering at
+0 or 1, additional information as to the source of the error code is
+needed to determine which table to look at.  Sometimes no text messages are
+supplied at all, and the programmer must supply them at any point at which
+he may wish to report error conditions.
+Often, a global variable is assigned some value describing the error, but
+the programmer has to know in each case whether to look at @code{errno},
+@code{h_errno}, the return value from @code{hes_err()}, or whatever other
+variables or routines are specified.
+And what happens if something
+in the procedure of
+examining or reporting the error changes the same variable?
+
+The package we have developed is an attempt to present a common
+error-handling mechanism to manipulate the most common form of error code
+in a fashion that does not have the problems listed above.
+
+A list of up to 256 text messages is supplied to a translator we have
+written, along with the three- to four-character ``name'' of the error
+table.  The library using this error table need only call a routine
+generated from this error-table source to make the table ``known'' to the
+com_err library, and any error code the library generates can be converted
+to the corresponding error message.  There is also a default format for
+error codes accidentally returned before making the table known, which is
+of the form @samp{unknown code foo 32}, where @samp{foo} would be the name
+of the table.
+
+@section Error codes
+
+Error codes themselves are 32 bit (signed) integers, of which the high
+order 24 bits are an identifier of which error table the error code is
+from, and the low order 8 bits are a sequential error number within
+the table.  An error code may thus be easily decomposed into its component
+parts.  Only the lowest 32 bits of an error code are considered significant
+on systems which support wider values.
+
+Error table 0 is defined to match the UNIX system call error table
+(@code{sys_errlist}); this allows @code{errno} values to be used directly
+in the library (assuming that @code{errno} is of a type with the same width
+as @t{long}).  Other error table numbers are formed by compacting together
+the first four characters of the error table name.  The mapping between
+characters in the name and numeric values in the error code are defined in
+a system-independent fashion, so that two systems that can pass integral
+values between them can reliably pass error codes without loss of meaning;
+this should work even if the character sets used are not the same.
+(However, if this is to be done, error table 0 should be avoided, since the
+local system call error tables may differ.)
+
+Any variable which is to contain an error code should be declared @t{long}.
+The draft proposed American National Standard for C (as of May, 1988)
+requires that @t{long} variables be at least 32 bits; any system which does
+not support 32-bit @t{long} values cannot make use of this package (nor
+much other software that assumes an ANSI-C environment base) without
+significant effort.
+
+@section Error table source file
+
+The error table source file begins with the declaration of the table name,
+as
+
+@example
+error_table @var{tablename}
+@end example
+
+Individual error codes are
+specified with
+
+@example
+error_code @var{ERROR_NAME}, @var{"text message"}
+@end example
+
+where @samp{ec} can also be used as a short form of @samp{error_code}.  To
+indicate the end of the table, use @samp{end}.  Thus, a (short) sample
+error table might be:
+
+@example
+
+        error_table     dsc
+
+        error_code      DSC_DUP_MTG_NAME,
+                        "Meeting already exists"
+
+        ec              DSC_BAD_PATH,
+                        "A bad meeting pathname was given"
+
+        ec              DSC_BAD_MODES,
+                        "Invalid mode for this access control list"
+
+        end
+
+@end example
+
+@section The error-table compiler
+
+The error table compiler is named @code{compile_et}.  It takes one
+argument, the pathname of a file (ending in @samp{.et}, e.g.,
+@samp{dsc_err.et}) containing an error table source file.  It parses the
+error table, and generates two output files -- a C header file
+(@samp{discuss_err.h}) which contains definitions of the numerical values
+of the error codes defined in the error table, and a C source file which
+should be compiled and linked with the executable.  The header file must be
+included in the source of a module which wishes to reference the error
+codes defined; the object module generated from the C code may be linked in
+to a program which wishes to use the printed forms of the error codes.
+
+This translator accepts a @kbd{-language @var{lang}} argument, which
+determines for which language (or language variant) the output should be
+written.  At the moment, @var{lang} is currently limited to @kbd{ANSI-C}
+and @kbd{K&R-C}, and some abbreviated forms of each.  Eventually, this will
+be extended to include some support for C++.  The default is currently
+@kbd{K&R-C}, though the generated sources will have ANSI-C code
+conditionalized on the symbol @t{__STDC__}.
+
+@section Run-time support routines
+
+Any source file which uses the routines supplied with or produced by the
+com_err package should include the header file @file{<com_err.h>}.  It
+contains declarations and definitions which may be needed on some systems.
+(Some functions cannot be referenced properly without the return type
+declarations in this file.  Some functions may work properly on most
+architectures even without the header file, but relying on this is not
+recommended.)
+
+The run-time support routines and variables provided via this package
+include the following:
+
+@example
+void initialize_@var{xxxx}_error_table (void);
+@end example
+
+One of these routines is built by the error compiler for each error table.
+It makes the @var{xxxx} error table ``known'' to the error reporting
+system.  By convention, this routine should be called in the initialization
+routine of the @var{xxxx} library.  If the library has no initialization
+routine, some combination of routines which form the core of the library
+should ensure that this routine is called.  It is not advised to leave it
+the caller to make this call.
+
+There is no harm in calling this routine more than once.
+
+@example
+#define ERROR_TABLE_BASE_@var{xxxx} @var{nnnnn}L
+@end example
+
+This symbol contains the value of the first error code entry in the
+specified table.
+This rarely needs be used by the
+programmer.
+
+@example
+const char *error_message (long code);
+@end example
+
+This routine returns the character string error message associated
+with @code{code}; if this is associated with an unknown error table, or
+if the code is associated with a known error table but the code is not
+in the table, a string of the form @samp{Unknown code @var{xxxx nn}} is
+returned, where @var{xxxx} is the error table name produced by
+reversing the compaction performed on the error table number implied
+by that error code, and @var{nn} is the offset from that base value.
+
+Although this routine is available for use when needed, its use should be
+left to circumstances which render @code{com_err} (below) unusable.
+
+@example
+void com_err (const char *whoami,  /* module reporting error */
+              long code,           /* error code */
+              const char *format,  /* format for additional detail */
+              ...);                /*  (extra parameters) */
+@end example
+
+This routine provides an alternate way to print error messages to
+standard error; it allows the error message to be passed in as a
+parameter, rather than in an external variable.  @emph{Provide grammatical
+context for ``message.''}
+
+If @var{format} is @code{(char *)NULL}, the formatted message will not be
+printed.  @var{format} may not be omitted.
+
+@example
+#include <stdarg.h>
+
+void com_err_va (const char *whoami,
+                 long code,
+                 const char *format,
+                 va_list args);
+@end example
+
+This routine provides an interface, equivalent to @code{com_err} above,
+which may be used by higher-level variadic functions (functions which
+accept variable numbers of arguments).
+
+@example
+#include <stdarg.h>
+
+void (*set_com_err_hook (void (*proc) ())) ();
+
+void (*@var{proc}) (const char *whoami, long code, va_list args);
+
+void reset_com_err_hook ();
+@end example
+
+These two routines allow a routine to be dynamically substituted for
+@samp{com_err}.  After @samp{set_com_err_hook} has been called,
+calls to @samp{com_err} will turn into calls to the new hook routine.
+@samp{reset_com_err_hook} turns off this hook.  This may intended to
+be used in daemons (to use a routine which calls @var{syslog(3)}), or
+in a window system application (which could pop up a dialogue box).
+
+If a program is to be used in an environment in which simply printing
+messages to the @code{stderr} stream would be inappropriate (such as in a
+daemon program which runs without a terminal attached),
+@code{set_com_err_hook} may be used to redirect output from @code{com_err}.
+The following is an example of an error handler which uses @var{syslog(3)}
+as supplied in BSD 4.3:
+
+@example
+#include <stdio.h>
+#include <stdarg.h>
+#include <syslog.h>
+
+/* extern openlog (const char * name, int logopt, int facility); */
+/* extern syslog (int priority, char * message, ...); */
+
+void hook (const char * whoami, long code,
+           const char * format, va_list args)
+@{
+    char buffer[BUFSIZ];
+    static int initialized = 0;
+    if (!initialized) @{
+        openlog (whoami,
+                 LOG_NOWAIT|LOG_CONS|LOG_PID|LOG_NDELAY,
+                 LOG_DAEMON);
+        initialized = 1;
+    @}
+    vsprintf (buffer, format, args);
+    syslog (LOG_ERR, "%s %s", error_message (code), buffer);
+@}
+@end example
+
+After making the call
+@code{set_com_err_hook (hook);},
+any calls to @code{com_err} will result in messages being sent to the
+@var{syslogd} daemon for logging.
+The name of the program, @samp{whoami}, is supplied to the
+@samp{openlog()} call, and the message is formatted into a buffer and
+passed to @code{syslog}.
+
+Note that since the extra arguments to @code{com_err} are passed by
+reference via the @code{va_list} value @code{args}, the hook routine may
+place any form of interpretation on them, including ignoring them.  For
+consistency, @code{printf}-style interpretation is suggested, via
+@code{vsprintf} (or @code{_doprnt} on BSD systems without full support for
+the ANSI C library).
+
+@section Coding Conventions
+
+The following conventions are just some general stylistic conventions
+to follow when writing robust libraries and programs.  Conventions
+similar to this are generally followed inside the UNIX kernel and most
+routines in the Multics operating system.  In general, a routine
+either succeeds (returning a zero error code, and doing some side
+effects in the process), or it fails, doing minimal side effects; in
+any event, any invariant which the library assumes must be maintained.
+
+In general, it is not in the domain of non user-interface library
+routines to write error messages to the user's terminal, or halt the
+process.  Such forms of ``error handling'' should be reserved for
+failures of internal invariants and consistancy checks only, as it
+provides the user of the library no way to clean up for himself in the
+event of total failure.
+
+Library routines which can fail should be set up to return an error
+code.  This should usually be done as the return value of the
+function; if this is not acceptable, the routine should return a
+``null'' value, and put the error code into a parameter passed by
+reference.
+
+Routines which use the first style of interface can be used from
+user-interface levels of a program as follows:
+
+@example
+@{
+    if ((code = initialize_world(getuid(), random())) != 0) @{
+        com_err("demo", code,
+                "when trying to initialize world");
+        exit(1);
+    @}
+    if ((database = open_database("my_secrets", &code))==NULL) @{
+        com_err("demo", code,
+                "while opening my_secrets");
+        exit(1);
+    @}
+@}
+@end example
+
+A caller which fails to check the return status is in error.  It is
+possible to look for code which ignores error returns by using lint;
+look for error messages of the form ``foobar returns value which is
+sometimes ignored'' or ``foobar returns value which is always
+ignored.''
+
+Since libraries may be built out of other libraries, it is often necessary
+for the success of one routine to depend on another.  When a lower level
+routine returns an error code, the middle level routine has a few possible
+options.  It can simply return the error code to its caller after doing
+some form of cleanup, it can substitute one of its own, or it can take
+corrective action of its own and continue normally.  For instance, a
+library routine which makes a ``connect'' system call to make a network
+connection may reflect the system error code @code{ECONNREFUSED}
+(Connection refused) to its caller, or it may return a ``server not
+available, try again later,'' or it may try a different server.
+
+Cleanup which is typically necessary may include, but not be limited
+to, freeing allocated memory which will not be needed any more,
+unlocking concurrancy locks, dropping reference counts, closing file
+descriptors, or otherwise undoing anything which the procedure did up
+to this point.  When there are a lot of things which can go wrong, it
+is generally good to write one block of error-handling code which is
+branched to, using a goto, in the event of failure.  A common source
+of errors in UNIX programs is failing to close file descriptors on
+error returns; this leaves a number of ``zombied'' file descriptors
+open, which eventually causes the process to run out of file
+descriptors and fall over.
+
+@example
+@{
+    FILE *f1=NULL, *f2=NULL, *f3=NULL;
+    int status = 0;
+
+    if ( (f1 = fopen(FILE1, "r")) == NULL) @{
+        status = errno;
+        goto error;
+    @}
+
+    /*
+     * Crunch for a while
+     */
+
+    if ( (f2 = fopen(FILE2, "w")) == NULL) @{
+        status = errno;
+        goto error;
+    @}
+
+    if ( (f3 = fopen(FILE3, "a+")) == NULL) @{
+        status = errno;
+            goto error;
+    @}
+
+    /*
+     * Do more processing.
+     */
+    fclose(f1);
+    fclose(f2);
+    fclose(f3);
+    return 0;
+
+error:
+    if (f1) fclose(f1);
+    if (f2) fclose(f2);
+    if (f3) fclose(f3);
+    return status;
+@}
+@end example
+
+@section Building and Installation
+
+The distribution of this package will probably be done as a compressed
+``tar''-format file available via anonymous FTP from SIPB.MIT.EDU.
+Retrieve @samp{pub/com_err.tar.Z} and extract the contents.  A subdirectory
+@t{profiled} should be created to hold objects compiled for profiling.
+Running ``make all'' should then be sufficient to build the library and
+error-table compiler.  The files @samp{libcom_err.a},
+@samp{libcom_err_p.a}, @samp{com_err.h}, and @samp{compile_et} should be
+installed for use; @samp{com_err.3} and @samp{compile_et.1} can also be
+installed as manual pages.
+
+Potential problems:
+
+@itemize @bullet
+
+@item Use of @code{strcasecmp}, a routine provided in BSD for
+case-insensitive string comparisons.  If an equivalent routine is
+available, you can modify @code{CFLAGS} in the makefile to define
+@code{strcasecmp} to the name of that routine.
+
+@item Compilers that defined @code{__STDC__} without providing the header
+file @code{<stdarg.h>}.  One such example is Metaware's High ``C''
+compiler, as provided at Project Athena on the IBM RT/PC workstation; if
+@code{__HIGHC__} is defined, it is assumed that @code{<stdarg.h>} is not
+available, and therefore @code{<varargs.h>} must be used.  If the symbol
+@code{VARARGS} is defined (e.g., in the makefile), @code{<varargs.h>} will
+be used.
+
+@item If your linker rejects symbols that are simultaneously defined in two
+library files, edit @samp{Makefile} to remove @samp{perror.c} from the
+library.  This file contains a version of @var{perror(3)} which calls
+@code{com_err} instead of calling @code{write} directly.
+
+@end itemize
+
+As I do not have access to non-BSD systems, there are probably
+bugs present that may interfere with building or using this package on
+other systems.  If they are reported to me, they can probably be fixed for
+the next version.
+
+@section Bug Reports
+
+Please send any comments or bug reports to the principal author: Ken
+Raeburn, @t{Raeburn@@Athena.MIT.EDU}.
+
+@section Acknowledgements
+
+I would like to thank: Bill Sommerfeld, for his help with some of this
+documentation, and catching some of the bugs the first time around;
+Honeywell Information Systems, for not killing off the @emph{Multics}
+operating system before I had an opportunity to use it; Honeywell's
+customers, who persuaded them not to do so, for a while; Ted Anderson of
+CMU, for catching some problems before version 1.2 left the nest; Stan
+Zanarotti and several others of MIT's Student Information Processing Board,
+for getting us started with ``discuss,'' for which this package was
+originally written; and everyone I've talked into --- I mean, asked to read
+this document and the ``man'' pages.
+
+@bye
diff --git a/src/util/et/compile_et.1 b/src/util/et/compile_et.1
new file mode 100644 (file)
index 0000000..f17a278
--- /dev/null
@@ -0,0 +1,79 @@
+.\" Copyright (c) 1988 Massachusetts Institute of Technology,
+.\" Student Information Processing Board.  All rights reserved.
+.\"
+.\" $Header$
+.\"
+.TH COMPILE_ET 1 "22 Nov 1988" SIPB
+.SH NAME
+compile_et \- error table compiler
+.SH SYNOPSIS
+.B compile_et
+file
+.SH DESCRIPTION
+.B Compile_et
+converts a table listing error-code names and associated messages into
+a C source file suitable for use with the
+.IR com_err (3)
+library.
+
+The source file name must end with a suffix of ``.et''; the file
+consists of a declaration supplying the name (up to four characters
+long) of the error-code table:
+
+.B error_table
+.I name
+
+followed by up to 256 entries of the form:
+
+.B error_code
+.I name,
+"
+.I string
+"
+
+and a final
+
+.B end
+
+to indicate the end of the table.
+
+The name of the table is used to construct the name of a subroutine
+.I initialize_XXXX_error_table
+which must be called in order for the
+.I com_err
+library to recognize the error table.
+
+The various error codes defined are assigned sequentially increasing
+numbers (starting with a large number computed as a hash function of
+the name of the table); thus for compatibility it is suggested that
+new codes be added only to the end of an existing table, and that no
+codes be removed from tables.
+
+The names defined in the table are placed into a C header file with
+preprocessor directives defining them as integer constants of up to
+32 bits in magnitude.
+
+A C source file is also generated which should be compiled and linked
+with the object files which reference these error codes; it contains
+the text of the messages and the initialization subroutine.  Both C
+files have names derived from that of the original source file, with
+the ``.et'' suffix replaced by ``.c'' and ``.h''.
+
+A ``#'' in the source file is treated as a comment character, and all
+remaining text to the end of the source line will be ignored.
+
+.SH BUGS
+
+Since
+.B compile_et
+uses a very simple parser based on
+.IR yacc (1),
+its error recovery leaves much to be desired.
+
+.\" .IR for manual entries
+.\" .PP for paragraph breaks
+
+.SH "SEE ALSO"
+com_err (3).
+
+Ken Raeburn, "A Common Error Description Library for UNIX".
diff --git a/src/util/et/compile_et.sh b/src/util/et/compile_et.sh
new file mode 100644 (file)
index 0000000..fdd249e
--- /dev/null
@@ -0,0 +1,11 @@
+#!/bin/sh
+#
+#
+AWK=@AWK@
+DIR=@DIR@
+
+ROOT=`echo $1 | sed -e s/.et$//`
+BASE=`basename $ROOT`
+
+$AWK -f ${DIR}/et_h.awk outfile=${BASE}.h $ROOT.et
+$AWK -f ${DIR}/et_c.awk outfile=${BASE}.c $ROOT.et
diff --git a/src/util/et/config_script b/src/util/et/config_script
new file mode 100644 (file)
index 0000000..e3de35c
--- /dev/null
@@ -0,0 +1,25 @@
+#!/bin/sh
+#
+# This program takes a shell script and configures for the following
+# variables:   @DIR@
+#              @AWK@
+#              @SED@
+#
+# Usage: config_script <filename> [<awk>] [<sed>]
+#
+
+FILE=$1
+AWK=$2
+SED=$3
+
+# Grr.... not all Unix's have the dirname command
+TMP=`echo  $1 | sed -e 's;[^/]*$;;' -e 's/^$/./'`
+DIR=`cd ${TMP}; pwd`
+
+if test "${AWK}x" = "x" ; then
+       AWK=awk
+fi
+if test "${SED}x" = "x" ; then
+       SED=sed
+fi
+sed -e "s;@DIR@;${DIR};" -e "s;@AWK@;${AWK};" -e "s;@SED@;${SED};" $FILE
diff --git a/src/util/et/error_table.y b/src/util/et/error_table.y
new file mode 100644 (file)
index 0000000..b65532d
--- /dev/null
@@ -0,0 +1,234 @@
+%{
+#include <stdio.h>
+char *str_concat(), *ds(), *quote();
+#ifndef __STDC__
+char *malloc(), *realloc();  
+#endif
+char *current_token = (char *)NULL;
+extern char *table_name;
+%}
+%union {
+       char *dynstr;
+}
+
+%token ERROR_TABLE ERROR_CODE_ENTRY END
+%token <dynstr> STRING QUOTED_STRING
+%type <dynstr> ec_name description table_id
+%{
+%}
+%start error_table
+%%
+
+error_table    :       ERROR_TABLE table_id error_codes END
+                       { table_name = ds($2);
+                         current_token = table_name;
+                         put_ecs(); }
+               ;
+
+table_id       :       STRING
+                       { current_token = $1;
+                         set_table_num($1);
+                         $$ = $1; }
+               ;
+
+error_codes    :       error_codes ec_entry
+               |       ec_entry
+               ;
+
+ec_entry       :       ERROR_CODE_ENTRY ec_name ',' description
+                       { add_ec($2, $4);
+                         free($2);
+                         free($4); }
+               |       ERROR_CODE_ENTRY ec_name '=' STRING ',' description
+                       { add_ec_val($2, $4, $6);
+                         free($2);
+                         free($4);
+                         free($6);
+                       }
+               ;
+
+ec_name                :       STRING
+                       { $$ = ds($1);
+                         current_token = $$; }
+               ;
+
+description    :       QUOTED_STRING
+                       { $$ = ds($1);
+                         current_token = $$; }
+               ;
+
+%%
+/*
+ *
+ * Copyright 1986, 1987 by the MIT Student Information Processing Board
+ *
+ * For copyright info, see mit-sipb-copyright.h.
+ */
+
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include "internal.h"
+#include "error_table.h"
+#include "mit-sipb-copyright.h"
+
+#ifndef        lint
+static char const rcsid_error_table_y[] =
+    "$Header$";
+#endif
+
+extern FILE *hfile, *cfile;
+
+static long gensym_n = 0;
+char *
+gensym(x)
+       char const *x;
+{
+       char *symbol;
+       if (!gensym_n) {
+               struct timeval tv;
+               struct timezone tzp;
+               gettimeofday(&tv, &tzp);
+               gensym_n = (tv.tv_sec%10000)*100 + tv.tv_usec/10000;
+       }
+       symbol = malloc(32 * sizeof(char));
+       gensym_n++;
+       sprintf(symbol, "et%ld", gensym_n);
+       return(symbol);
+}
+
+char *
+ds(string)
+       char const *string;
+{
+       char *rv;
+       rv = malloc(strlen(string)+1);
+       strcpy(rv, string);
+       return(rv);
+}
+
+char *
+quote(string)
+       char const *string;
+{
+       char *rv;
+       rv = malloc(strlen(string)+3);
+       strcpy(rv, "\"");
+       strcat(rv, string);
+       strcat(rv, "\"");
+       return(rv);
+}
+
+long table_number;
+int current = 0;
+char **error_codes = (char **)NULL;
+
+add_ec(name, description)
+       char const *name, *description;
+{
+       fprintf(cfile, "\t\"%s\",\n", description);
+       if (error_codes == (char **)NULL) {
+               error_codes = (char **)malloc(sizeof(char *));
+               *error_codes = (char *)NULL;
+       }
+       error_codes = (char **)realloc((char *)error_codes,
+                                      (current + 2)*sizeof(char *));
+       error_codes[current++] = ds(name);
+       error_codes[current] = (char *)NULL;
+}
+
+add_ec_val(name, val, description)
+       char const *name, *val, *description;
+{
+       const int ncurrent = atoi(val);
+       if (ncurrent < current) {
+               printf("Error code %s (%d) out of order", name,
+                      current);
+               return;
+       }
+      
+       while (ncurrent > current)
+            fputs("\t(char *)NULL,\n", cfile), current++;
+       
+       fprintf(cfile, "\t\"%s\",\n", description);
+       if (error_codes == (char **)NULL) {
+               error_codes = (char **)malloc(sizeof(char *));
+               *error_codes = (char *)NULL;
+       }
+       error_codes = (char **)realloc((char *)error_codes,
+                                      (current + 2)*sizeof(char *));
+       error_codes[current++] = ds(name);
+       error_codes[current] = (char *)NULL;
+} 
+
+put_ecs()
+{
+       int i;
+       for (i = 0; i < current; i++) {
+            if (error_codes[i] != (char *)NULL)
+                 fprintf(hfile, "#define %-40s (%ldL)\n",
+                         error_codes[i], table_number + i);
+       }
+}
+
+/*
+ * char_to_num -- maps letters and numbers into a small numbering space
+ *     uppercase ->  1-26
+ *     lowercase -> 27-52
+ *     digits    -> 53-62
+ *     underscore-> 63
+ */
+
+static const char char_set[] =
+       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_";
+
+int char_to_num(c)
+       char c;
+{
+       const char *where;
+       int diff;
+
+       where = strchr (char_set, c);
+       if (where) {
+               diff = where - char_set + 1;
+               assert (diff < (1 << ERRCODE_RANGE));
+               return diff;
+       }
+       else if (isprint (c))
+               fprintf (stderr,
+                        "Illegal character `%c' in error table name\n",
+                        c);
+       else
+               fprintf (stderr,
+                        "Illegal character %03o in error table name\n",
+                        c);
+       exit (1);
+}
+
+set_table_num(string)
+       char *string;
+{
+       if (char_to_num (string[0]) > char_to_num ('z')) {
+               fprintf (stderr, "%s%s%s%s",
+                        "First character of error table name must be ",
+                        "a letter; name ``",
+                        string, "'' rejected\n");
+               exit (1);
+       }
+       if (strlen(string) > 4) {
+               fprintf(stderr, "Table name %s too long, truncated ",
+                       string);
+               string[4] = '\0';
+               fprintf(stderr, "to %s\n", string);
+       }
+       while (*string != '\0') {
+               table_number = (table_number << BITS_PER_CHAR)
+                       + char_to_num(*string);
+               string++;
+       }
+       table_number = table_number << ERRCODE_RANGE;
+}
+
+#include "et_lex.lex.c"
diff --git a/src/util/et/test1.et b/src/util/et/test1.et
new file mode 100644 (file)
index 0000000..4c7b77f
--- /dev/null
@@ -0,0 +1,69 @@
+       error_table     krb
+
+       error_code      KRB_MK_AP_TKFIL,
+                       "Can't read ticket file"
+
+       ec              KRB_MK_AP_NOTKT,
+                       "Can't find ticket or TGT"
+
+       ec              KRB_MK_AP_TGTEXP,
+                       "TGT expired"
+
+       ec              KRB_RD_AP_UNDEC,
+                       "Can't decode authenticator"
+
+       ec              KRB_RD_AP_EXP,
+                       "Ticket expired"
+
+       ec              KRB_RD_AP_REPEAT,
+                       "Repeated request"
+
+       ec              KRB_RD_AP_NOT_US,
+                       "The ticket isn't for us"
+
+       ec              KRB_RD_AP_INCON,
+                       "Request is inconsistent"
+
+       ec              KRB_RD_AP_TIME,
+                       "Delta-T too big"
+
+       ec              KRB_RD_AP_BADD,
+                       "Incorrect net address"
+
+       ec              KRB_RD_AP_VERSION,
+                       "Protocol version mismatch"
+
+       ec              KRB_RD_AP_MSG_TYPE,
+                       "Invalid message type"
+
+       ec              KRB_RD_AP_MODIFIED,
+                       "Message stream modified"
+
+       ec              KRB_RD_AP_ORDER,
+                       "Message out of order"
+
+       ec              KRB_RD_AP_UNAUTHOR,
+                       "Unauthorized request"
+
+       ec              KRB_GT_PW_NULL,
+                       "Current password is null"
+
+       ec              KRB_GT_PW_BADPW,
+                       "Incorrect current password"
+
+       ec              KRB_GT_PW_PROT,
+                       "Protocol error"
+
+       ec              KRB_GT_PW_KDCERR,
+                       "Error returned by KDC"
+
+       ec              KRB_GT_PW_NULLTKT,
+                       "Null ticket returned by KDC"
+
+       ec              KRB_SKDC_RETRY,
+                       "Retry count exceeded"
+
+       ec              KRB_SKDC_CANT,
+                       "Can't send request"
+
+       end
diff --git a/src/util/et/test2.et b/src/util/et/test2.et
new file mode 100644 (file)
index 0000000..55ad74e
--- /dev/null
@@ -0,0 +1,9 @@
+       error_table     quux
+
+       ec      FOO_ERR, "foo"
+
+       ec      BAR_ERR, "bar"
+
+       ec      BAZ_ERR, "meow"
+
+       end
diff --git a/src/util/et/texinfo.tex b/src/util/et/texinfo.tex
new file mode 100644 (file)
index 0000000..838160c
--- /dev/null
@@ -0,0 +1,2077 @@
+%% TeX macros to handle texinfo files
+
+%   Copyright (C) 1985, 1986, 1988 Richard M. Stallman
+
+%                     NO WARRANTY
+
+%  BECAUSE THIS PROGRAM IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY
+%NO WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW.  EXCEPT
+%WHEN OTHERWISE STATED IN WRITING, FREE SOFTWARE FOUNDATION, INC,
+%RICHARD M. STALLMAN AND/OR OTHER PARTIES PROVIDE THIS PROGRAM "AS IS"
+%WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
+%BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+%FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY
+%AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE PROGRAM PROVE
+%DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
+%CORRECTION.
+
+% IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL RICHARD M.
+%STALLMAN, THE FREE SOFTWARE FOUNDATION, INC., AND/OR ANY OTHER PARTY
+%WHO MAY MODIFY AND REDISTRIBUTE THIS PROGRAM AS PERMITTED BELOW, BE
+%LIABLE TO YOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR
+%OTHER SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+%USE OR INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR
+%DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR
+%A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) THIS
+%PROGRAM, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
+%DAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY.
+
+%              GENERAL PUBLIC LICENSE TO COPY
+
+%  1. You may copy and distribute verbatim copies of this source file
+%as you receive it, in any medium, provided that you conspicuously
+%and appropriately publish on each copy a valid copyright notice
+%"Copyright (C) 1986 Richard M. Stallman"; and include
+%following the copyright notice a verbatim copy of the above disclaimer
+%of warranty and of this License.
+
+%  2. You may modify your copy or copies of this source file or
+%any portion of it, and copy and distribute such modifications under
+%the terms of Paragraph 1 above, provided that you also do the following:
+
+%    a) cause the modified files to carry prominent notices stating
+%    that you changed the files and the date of any change; and
+
+%    b) cause the whole of any work that you distribute or publish,
+%    that in whole or in part contains or is a derivative of this
+%    program or any part thereof, to be licensed at no charge to all
+%    third parties on terms identical to those contained in this
+%    License Agreement (except that you may choose to grant more extensive
+%    warranty protection to some or all third parties, at your option).
+
+%    c) You may charge a distribution fee for the physical act of
+%    transferring a copy, and you may at your option offer warranty
+%    protection in exchange for a fee.
+
+%Mere aggregation of another unrelated program with this program (or its
+%derivative) on a volume of a storage or distribution medium does not bring
+%the other program under the scope of these terms.
+
+%  3. You may copy and distribute this program (or a portion or derivative
+%of it, under Paragraph 2) in object code or executable form under the terms
+%of Paragraphs 1 and 2 above provided that you also do one of the following:
+
+%    a) accompany it with the complete corresponding machine-readable
+%    source code, which must be distributed under the terms of
+%    Paragraphs 1 and 2 above; or,
+
+%    b) accompany it with a written offer, valid for at least three
+%    years, to give any third party free (except for a nominal
+%    shipping charge) a complete machine-readable copy of the
+%    corresponding source code, to be distributed under the terms of
+%    Paragraphs 1 and 2 above; or,
+
+%    c) accompany it with the information you received as to where the
+%    corresponding source code may be obtained.  (This alternative is
+%    allowed only for noncommercial distribution and only if you
+%    received the program in object code or executable form alone.)
+
+%For an executable file, complete source code means all the source code for
+%all modules it contains; but, as a special exception, it need not include
+%source code for modules which are standard libraries that accompany the
+%operating system on which the executable file runs.
+
+%  4. You may not copy, sublicense, distribute or transfer this program
+%except as expressly provided under this License Agreement.  Any attempt
+%otherwise to copy, sublicense, distribute or transfer this program is void and
+%your rights to use the program under this License agreement shall be
+%automatically terminated.  However, parties who have received computer
+%software programs from you with this License Agreement will not have
+%their licenses terminated so long as such parties remain in full compliance.
+
+%  5. If you wish to incorporate parts of this program into other free
+%programs whose distribution conditions are different, write to the Free
+%Software Foundation at 675 Mass Ave, Cambridge, MA 02139.  We have not yet
+%worked out a simple rule that can be stated here, but we will often permit
+%this.  We will be guided by the two goals of preserving the free status of
+%all derivatives of our free software and of promoting the sharing and reuse of
+%software.
+
+%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!
+
+\def\texinfoversion{1.18}
+\message{Loading texinfo package [Version \texinfoversion]:}
+\message{}
+
+% Save some parts of plain tex whose names we will redefine.
+
+\let\ptexlbrace=\{
+\let\ptexrbrace=\}
+\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
+
+\def\tie{\penalty 10000\ }     % Save plain tex definition of ~.
+
+\message{Basics,}
+\chardef\other=12
+
+\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
+
+%---------------------Begin change-----------------------
+%
+% 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
+\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
+\shipout\vbox{{\let\hsize=\pagewidth \makeheadline} \pagebody{#1}%
+ {\let\hsize=\pagewidth \makefootline}}
+\advancepageno \ifnum\outputpenalty>-20000 \else\dosupereject\fi}
+
+
+% 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
+                \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 }
+
+\def\pagebody#1{\vbox to\pageheight{\boxmaxdepth=\maxdepth #1}}
+{\catcode`\@ =11
+\gdef\pagecontents#1{\ifvoid\topins\else\unvbox\topins\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 can be delimited with [...] or with "..." or braces
+% or it can be a whole line.
+% #1 should be a macro which expects
+% an ordinary undelimited TeX argument.
+
+\def\parsearg #1{\let\next=#1\begingroup\obeylines\futurelet\temp\parseargx}
+
+\def\parseargx{%
+\ifx \obeyedspace\temp \aftergroup\parseargdiscardspace \else%
+\aftergroup \parseargline %
+\fi \endgroup}
+
+{\obeyspaces %
+\gdef\parseargdiscardspace {\begingroup\obeylines\futurelet\temp\parseargx}}
+
+\gdef\obeyedspace{\ }
+
+\def\parseargline{\begingroup \obeylines \parsearglinex}
+{\obeylines %
+\gdef\parsearglinex #1^^M{\endgroup \next {#1}}}
+
+\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.
+%% foo can be delimited by doublequotes or brackets.
+
+\def\end{\parsearg\endxxx}
+
+\def\endxxx #1{%
+\expandafter\ifx\csname E#1\endcsname\relax
+\expandafter\ifx\csname #1\endcsname\relax
+\errmessage{Undefined command @end #1}\else
+\errorE{#1}\fi\fi
+\csname E#1\endcsname}
+\def\errorE#1{
+{\errhelp=\EMsimple \errmessage{@end #1 not within #1 environment}}}
+
+% Single-spacing is done by various environments.
+
+\newskip\singlespaceskip \singlespaceskip = \baselineskip
+\def\singlespace{%
+{\advance \baselineskip by -\singlespaceskip
+\kern \baselineskip}%
+\baselineskip=\singlespaceskip
+}
+
+%% Simple single-character @ commands
+
+% @@ prints an @
+% Kludge this until the fonts are right (grr).
+\def\@{{\sf \char '100}}
+
+% 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}
+
+% @. is an end-of-sentence period.
+\def\.{.\spacefactor=3000 }
+
+% @w prevents a word break
+\def\w #1{\hbox{#1}}
+
+% @group ... @end group  forces ... to be all on one page.
+
+\def\group{\begingroup% \inENV ???
+\def \Egroup{\egroup\endgroup}
+\vbox\bgroup}
+
+% @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
+
+\def\exdent{\errmessage{@exdent in filled text}}
+  % @lisp, etc, define \exdent locally from \internalexdent
+
+{\obeyspaces
+\gdef\internalexdent{\parsearg\exdentzzz}}
+
+\def\exdentzzz #1{{\advance \leftskip by -\lispnarrowing
+\advance \hsize by -\leftskip
+\advance \hsize by -\rightskip
+\leftline{{\rm#1}}}}
+
+% @include file    insert text of that file as input.
+
+\def\include{\parsearg\includezzz}
+\def\includezzz #1{{\def\thisfile{#1}\input #1
+}}
+
+\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{\parsearg \commentxxx}
+
+\def\commentxxx #1{}
+
+\let\c=\comment
+
+\long\def\ignore #1\end ignore{}
+
+\outer\def\ifset{\parsearg\ifsetxxx}
+
+\def\ifsetxxx #1#2\end ifset{%
+\expandafter\ifx\csname IF#1\endcsname\relax \else #2\fi}
+
+\outer\def\ifclear{\parsearg\ifclearxxx}
+
+\def\ifclearxxx #1#2\end ifclear{%
+\expandafter\ifx\csname IF#1\endcsname\relax #2\fi}
+
+% Some texinfo constructs that are trivial in tex
+
+\def\iftex{}
+\def\Eiftex{}
+\long\def\ifinfo #1\end ifinfo{}
+\long\def\menu #1\end menu{}
+\def\asis#1{#1}
+
+\def\node{\parsearg\nodezzz}
+\def\nodezzz#1{\nodexxx [#1,]}
+\def\nodexxx[#1,#2]{\gdef\lastnode{#1}}
+\let\lastnode=\relax
+
+\def\donoderef{\ifx\lastnode\relax\else
+\expandafter\expandafter\expandafter\setref{\lastnode}\fi
+\let\lastnode=\relax}
+
+\def\unnumbnoderef{\ifx\lastnode\relax\else
+\expandafter\expandafter\expandafter\unnumbsetref{\lastnode}\fi
+\let\lastnode=\relax}
+
+\let\refill=\relax
+
+\let\setfilename=\comment
+
+\def\inforef #1{\inforefzzz #1,,,,**}
+\def\inforefzzz #1,#2,#3,#4**{See Info file \file{\losespace#3{}}, node `\losespace#1{}'}
+\def\losespace #1{#1}
+
+\message{fonts,}
+
+% Font-change commands.
+
+%% Try out Computer Modern fonts at \magstephalf
+\font\tenrm=cmr10 scaled \magstephalf
+\font\tentt=cmtt10 scaled \magstephalf
+% 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.
+\font\tenbf=cmb10 scaled \magstephalf 
+\font\tenit=cmti10 scaled \magstephalf
+\font\tensl=cmsl10 scaled \magstephalf
+\font\tensf=cmss10 scaled \magstephalf
+\def\li{\sf}
+\font\tensc=cmcsc10 scaled \magstephalf
+
+% Fonts for @defun, etc.
+\font\defbf=cmbx10 scaled \magstep1 %was 1314
+\let\deftt=\tentt
+\def\df{\let\tt=\deftt \defbf}
+
+% Font for title
+\font\titlerm = cmbx10 scaled \magstep5
+
+% Fonts for indices
+\font\indit=cmti9 \font\indrm=cmr9
+\def\indbf{\indrm} \def\indsl{\indit}
+\def\indexfonts{\let\it=\indit \let\sl=\indsl \let\bf=\indbf \let\rm=\indrm}
+
+% Fonts for headings
+\font\chaprm=cmbx10 scaled \magstep3
+\font\chapit=cmti10 scaled \magstep3
+\font\chapsl=cmsl10 scaled \magstep3
+\font\chaptt=cmtt10 scaled \magstep3
+\font\chapsf=cmss10 scaled \magstep3
+\let\chapbf=\chaprm
+
+\font\secrm=cmbx10 scaled \magstep2
+\font\secit=cmti10 scaled \magstep2
+\font\secsl=cmsl10 scaled \magstep2
+\font\sectt=cmtt10 scaled \magstep2
+\font\secsf=cmss10 scaled \magstep2
+\let\secbf=\secrm
+
+\font\ssecrm=cmbx10 scaled \magstep1
+\font\ssecit=cmti10 scaled \magstep1
+\font\ssecsl=cmsl10 scaled \magstep1
+\font\ssectt=cmtt10 scaled \magstep1
+\font\ssecsf=cmss10 scaled \magstep1
+\let\ssecbf=\ssecrm
+
+\def\textfonts{\let\rm=\tenrm\let\it=\tenit\let\sl=\tensl\let\bf=\tenbf%
+\let\sc=\tensc\let\sf=\tensf}
+\def\chapfonts{\let\rm=\chaprm\let\it=\chapit\let\sl=\chapsl\let\bf=\chapbf\let\tt=\chaptt\let\sf=\chapsf}
+\def\secfonts{\let\rm=\secrm\let\it=\secit\let\sl=\secsl\let\bf=\secbf\let\tt=\sectt\let\sf=\secsf}
+\def\subsecfonts{\let\rm=\ssecrm\let\it=\ssecit\let\sl=\ssecsl\let\bf=\ssecbf\let\tt=\ssectt\let\sf=\ssecsf}
+% Count depth in font-changes, for error checks
+\newcount\fontdepth \fontdepth=0
+
+%% Add scribe-like font environments, plus @l for inline lisp (usually sans
+%% serif) and @ii for TeX italic
+
+\def\i#1{{\sl #1}}
+\let\var=\i
+\let\dfn=\i
+\let\emph=\i
+\let\cite=\i
+
+\def\b#1{{\bf #1}}
+\let\strong=\b
+
+\def\t#1{{\tt \rawbackslash \frenchspacing #1}\null}
+\let\ttfont = \t
+\let\kbd=\t
+\let\code=\t
+\def\samp #1{`{\tt \rawbackslash \frenchspacing #1}'\null}
+\def\key #1{{\tt \uppercase{#1}}\null}
+\def\ctrl #1{{\tt \rawbackslash \hat}#1}
+
+\let\file=\samp
+
+\def\l#1{{\li #1}\null}
+
+\def\r#1{{\rm #1}}
+\def\s#1{{\sc #1}}
+\def\ii#1{{\it #1}}
+
+\def\titlefont#1{{\titlerm #1}}
+
+\def\titlepage{\begingroup \parindent=0pt \hbox{}%
+\let\oldpage=\page
+\def\page{\oldpage \hbox{}}}
+
+\def\Etitlepage{\endgroup\page\HEADINGSon}
+
+% Make altmode in file print out right
+
+\catcode `\^^[=\active \def^^[{$\diamondsuit$}
+
+\message{page headings,}
+
+%%% 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}}
+
+% 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 on   turns them on.
+% @headings off  turns them off.
+% 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,
+% Put current file name in lower left corner,
+% Put 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\HEADINGSon{
+\pagealignmacro
+\global\pageno=1
+\global\evenfootline={\hfil}
+\global\oddfootline={\hfil}
+\global\evenheadline={\line{\folio\hfil\thistitle}}
+\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,}
+
+% Tables -- @table, @ftable, @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 and @ftable define @item, @itemx, etc., with these defs.
+% They also define \itemindex
+% to index the item name in whatever manner is desired (perhaps none).
+
+\def\internalBitem{\smallbreak \parsearg\itemzzz}
+\def\internalBitemx{\par \parsearg\itemzzz}
+
+\def\internalBxitem "#1"{\def\xitemsubtopix{#1} \smallbreak \parsearg\xitemzzz}
+\def\internalBxitemx "#1"{\def\xitemsubtopix{#1} \par \parsearg\xitemzzz}
+
+\def\internalBkitem{\smallbreak \parsearg\kitemzzz}
+\def\internalBkitemx{\par \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 -\leftskip %
+\setbox0=\hbox{\itemfont{#1}}%
+\itemindex{#1}%
+\parskip=0in %
+\noindent %
+\ifdim \wd0>\itemmax %
+\vadjust{\penalty 10000}%
+\hbox to \hsize{\hskip -\tableindent\box0\hss}\ %
+\else %
+\hbox to 0pt{\hskip -\tableindent\box0\hss}%
+\fi %
+\endgroup %
+}
+
+\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\dontindex #1{}
+\def\fnitemindex #1{\doind {fn}{\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}% Neccessary 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 %
+\parindent = 0pt
+\parskip = \smallskipamount
+\ifdim \parskip=0pt \parskip=2pt \fi%
+\def\Etable{\endgraf\endgroup\afterenvbreak}%
+\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{\itemizey {#1}{\Eitemize}}
+
+\def\itemizey #1#2{%
+\aboveenvbreak %
+\begingroup %
+\itemno = 0 %
+\itemmax=\itemindent %
+\advance \itemmax by -\itemmargin %
+\advance \leftskip by \itemindent %
+\parindent = 0pt
+\parskip = \smallskipamount
+\ifdim \parskip=0pt \parskip=2pt \fi%
+\def#2{\endgraf\endgroup\afterenvbreak}%
+\def\itemcontents{#1}%
+\let\item=\itemizeitem}
+
+\def\bullet{$\ptexbullet$}
+\def\minus{$-$}
+
+\def\enumerate{\itemizey{\the\itemno.}\Eenumerate\flushcr}
+
+% 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 300}}%
+\flushcr}
+
+\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\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\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{%
+\def\bf{\realbackslash bf }%
+\def\rm{\realbackslash rm }%
+\def\sl{\realbackslash sl }%
+\def\dots{\realbackslash dots }%
+\def\copyright{\realbackslash copyright }%
+}
+
+% \indexnofonts no-ops all font-change commands.
+% This is used when outputting the strings to sort the index by.
+\def\indexdummyfont#1{#1}
+\def\indexnofonts{%
+\let\code=\indexdummyfont
+\let\samp=\indexdummyfont
+\let\kbd=\indexdummyfont
+\let\key=\indexdummyfont
+\let\var=\indexdummyfont
+}
+
+% 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.
+
+\def\doind #1#2{%
+{\indexdummies % Must do this here, since \bf, etc expand at this stage
+\count10=\lastpenalty %
+\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{%
+{\indexdummies % Must do this here, since \bf, etc expand at this stage
+\count10=\lastpenalty %
+\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 %
+\catcode`\%=\other\catcode`\&=\other\catcode`\#=\other
+\catcode`\$=\other\catcode`\_=\other
+\catcode`\~=\other
+\def\indexbackslash{\rawbackslashxx}
+\indexfonts\rm \tolerance=9500 \advance\baselineskip -1pt
+\begindoublecolumns
+\openin 1 \jobname.#1s
+\ifeof 1 \else \closein 1 \input \jobname.#1s
+\fi
+\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
+
+\outer\def\initial #1{%
+{\let\tentt=\sectt \let\sf=\sectt
+\ifdim\lastskip<\initialskipamount
+\removelastskip \penalty-200 \vskip \initialskipamount\fi
+\line{\secbf#1\hfill}\kern 2pt\penalty3000}}
+
+\outer\def\entry #1#2{
+{\parfillskip=0in \parskip=0in \parindent=0in
+\hangindent=1in \hangafter=1%
+\noindent\hbox{#1}\leaders\Dotsbox\hskip 0pt plus 1filll #2\par
+}}
+
+\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}\leaders\Dotsbox\hskip 0pt plus 1filll#2\par
+}}
+
+%% Define two-column mode, which is used in indexes.
+%% Adapted from the TeXBook, page 416
+\catcode `\@=11
+
+\newbox\partialpage
+
+\newdimen\doublecolumnhsize  \doublecolumnhsize = 3.11in
+\newdimen\doublecolumnvsize  \doublecolumnvsize = 19.1in
+
+\def\begindoublecolumns{\begingroup
+  \output={\global\setbox\partialpage=\vbox{\unvbox255\kern -\topskip \kern \baselineskip}}\eject
+  \output={\doublecolumnout} \hsize=\doublecolumnhsize \vsize=\doublecolumnvsize}
+\def\enddoublecolumns{\output={\balancecolumns}\eject
+  \endgroup \pagegoal=\vsize}
+
+\def\doublecolumnout{\splittopskip=\topskip \splitmaxdepth=\maxdepth
+  \dimen@=\pageheight \advance\dimen@ by-\ht\partialpage
+  \setbox0=\vsplit255 to\dimen@ \setbox2=\vsplit255 to\dimen@
+  \onepageout\pagesofar \unvbox255 \penalty\outputpenalty}
+\def\pagesofar{\unvbox\partialpage %
+  \hsize=\doublecolumnhsize % have to restore this since output routine
+%            changes it to set cropmarks (P. A. MacKay, 12 Nov. 1986)
+  \wd0=\hsize \wd2=\hsize \hbox to\pagewidth{\box0\hfil\box2}}
+\def\balancecolumns{\setbox0=\vbox{\unvbox255} \dimen@=\ht0
+  \advance\dimen@ by\topskip \advance\dimen@ by-\baselineskip
+  \divide\dimen@ by2 \splittopskip=\topskip
+  {\vbadness=10000 \loop \global\setbox3=\copy0
+    \global\setbox1=\vsplit3 to\dimen@
+    \ifdim\ht3>\dimen@ \global\advance\dimen@ by1pt \repeat}
+  \setbox0=\vbox to\dimen@{\unvbox1}  \setbox2=\vbox to\dimen@{\unvbox3}
+  \pagesofar}
+
+\catcode `\@=\other
+\message{sectioning,}
+% Define chapters, sections, etc.
+
+\newcount \chapno
+\newcount \secno
+\newcount \subsecno
+\newcount \subsubsecno
+
+% This counter is funny since it counts through charcodes of letters A, B, ...
+\newcount \appendixno  \appendixno = `\@
+\def\appendixletter{\char\the\appendixno}
+
+\newwrite \contentsfile
+\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
+%
+}
+
+\outer\def\chapter{\parsearg\chapterzzz}
+\def\chapterzzz #1{\seccheck{chapter}%
+\secno=0 \subsecno=0 \subsubsecno=0 \global\advance \chapno by 1 \message{Chapter \the\chapno}%
+\chapmacro {#1}{\the\chapno}%
+\gdef\thissection{#1}\gdef\thischapter{#1}%
+\let\rawbackslash=\relax%
+\let\frenchspacing=\relax%
+\edef\temp{{\realbackslash chapentry {#1}{\the\chapno}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp  %
+\donoderef %
+}
+
+\outer\def\appendix{\parsearg\appendixzzz}
+\def\appendixzzz #1{\seccheck{appendix}%
+\secno=0 \subsecno=0 \subsubsecno=0 \global\advance \appendixno by 1 \message{Appendix \appendixletter}%
+\chapmacro {#1}{Appendix \appendixletter}%
+\gdef\thischapter{#1}\gdef\thissection{#1}%
+\let\rawbackslash=\relax%
+\let\frenchspacing=\relax%
+\edef\temp{{\realbackslash chapentry {#1}{Appendix \appendixletter}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp  %
+\unnumbnoderef %
+}
+
+\outer\def\unnumbered{\parsearg\unnumberedzzz}
+\def\unnumberedzzz #1{\seccheck{unnumbered}%
+\secno=0 \subsecno=0 \subsubsecno=0 \message{(#1)}
+\unnumbchapmacro {#1}%
+\gdef\thischapter{#1}\gdef\thissection{#1}%
+\let\rawbackslash=\relax%
+\let\frenchspacing=\relax%
+\edef\temp{{\realbackslash unnumbchapentry {#1}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp  %
+\unnumbnoderef %
+}
+
+\outer\def\section{\parsearg\sectionzzz}
+\def\sectionzzz #1{\seccheck{section}%
+\subsecno=0 \subsubsecno=0 \global\advance \secno by 1 %
+\gdef\thissection{#1}\secheading {#1}{\the\chapno}{\the\secno}%
+\let\rawbackslash=\relax%
+\let\frenchspacing=\relax%
+\edef\temp{{\realbackslash secentry %
+{#1}{\the\chapno}{\the\secno}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\donoderef %
+\penalty 10000 %
+}
+
+\outer\def\appendixsection{\parsearg\appendixsectionzzz}
+\outer\def\appendixsec{\parsearg\appendixsectionzzz}
+\def\appendixsectionzzz #1{\seccheck{appendixsection}%
+\subsecno=0 \subsubsecno=0 \global\advance \secno by 1 %
+\gdef\thissection{#1}\secheading {#1}{\appendixletter}{\the\secno}%
+\let\rawbackslash=\relax%
+\let\frenchspacing=\relax%
+\edef\temp{{\realbackslash secentry %
+{#1}{\appendixletter}{\the\secno}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\unnumbnoderef %
+\penalty 10000 %
+}
+
+\outer\def\unnumberedsec{\parsearg\unnumberedseczzz}
+\def\unnumberedseczzz #1{\seccheck{unnumberedsec}%
+\plainsecheading {#1}\gdef\thissection{#1}%
+\let\rawbackslash=\relax%
+\let\frenchspacing=\relax%
+\edef\temp{{\realbackslash unnumbsecentry{#1}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\unnumbnoderef %
+\penalty 10000 %
+}
+
+\outer\def\subsection{\parsearg\subsectionzzz}
+\def\subsectionzzz #1{\seccheck{subsection}%
+\gdef\thissection{#1}\subsubsecno=0 \global\advance \subsecno by 1 %
+\subsecheading {#1}{\the\chapno}{\the\secno}{\the\subsecno}%
+\let\rawbackslash=\relax%
+\let\frenchspacing=\relax%
+\edef\temp{{\realbackslash subsecentry %
+{#1}{\the\chapno}{\the\secno}{\the\subsecno}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\donoderef %
+\penalty 10000 %
+}
+
+\outer\def\appendixsubsec{\parsearg\appendixsubseczzz}
+\def\appendixsubseczzz #1{\seccheck{appendixsubsec}%
+\gdef\thissection{#1}\subsubsecno=0 \global\advance \subsecno by 1 %
+\subsecheading {#1}{\appendixletter}{\the\secno}{\the\subsecno}%
+\let\rawbackslash=\relax%
+\let\frenchspacing=\relax%
+\edef\temp{{\realbackslash subsecentry %
+{#1}{\appendixletter}{\the\secno}{\the\subsecno}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\unnumbnoderef %
+\penalty 10000 %
+}
+
+\outer\def\unnumberedsubsec{\parsearg\unnumberedsubseczzz}
+\def\unnumberedsubseczzz #1{\seccheck{unnumberedsubsec}%
+\plainsecheading {#1}\gdef\thissection{#1}%
+\let\rawbackslash=\relax%
+\let\frenchspacing=\relax%
+\edef\temp{{\realbackslash unnumbsubsecentry{#1}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\unnumbnoderef %
+\penalty 10000 %
+}
+
+\outer\def\subsubsection{\parsearg\subsubsectionzzz}
+\def\subsubsectionzzz #1{\seccheck{subsubsection}%
+\gdef\thissection{#1}\global\advance \subsubsecno by 1 %
+\subsubsecheading {#1}{\the\chapno}{\the\secno}{\the\subsecno}{\the\subsubsecno}%
+\let\rawbackslash=\relax%
+\let\frenchspacing=\relax%
+\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\appendixsubsubseczzz}
+\def\appendixsubsubseczzz #1{\seccheck{appendixsubsubsec}%
+\gdef\thissection{#1}\global\advance \subsubsecno by 1 %
+\subsubsecheading {#1}{\appendixletter}{\the\secno}{\the\subsecno}{\the\subsubsecno}%
+\let\rawbackslash=\relax%
+\let\frenchspacing=\relax%
+\edef\temp{{\realbackslash subsubsecentry{#1}%
+{\appendixletter}{\the\secno}{\the\subsecno}{\the\subsubsecno}{\noexpand\folio}}}%\
+\escapechar=`\\%
+\write \contentsfile \temp %
+\unnumbnoderef %
+\penalty 10000 %
+}
+
+\outer\def\unnumberedsubsubsec{\parsearg\unnumberedsubsubseczzz}
+\def\unnumberedsubsubseczzz #1{\seccheck{unnumberedsubsubsec}%
+\plainsecheading {#1}\gdef\thissection{#1}%
+\let\rawbackslash=\relax%
+\let\frenchspacing=\relax%
+\edef\temp{{\realbackslash unnumbsubsubsecentry{#1}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\unnumbnoderef %
+\penalty 10000 %
+}
+
+% Define @majorheading, @heading and @subheading
+
+\outer\def\majorheading #1{%
+{\advance\chapheadingskip by 10pt \chapbreak }%
+{\chapfonts \line{\chaprm #1\hfill}}\bigskip \par\penalty 200}
+
+\outer\def\chapheading #1{\chapbreak %
+{\chapfonts \line{\chaprm #1\hfill}}\bigskip \par\penalty 200}
+
+\let\heading=\secheadingi
+\let\subheading=\subsecheadingi
+\let\subsubheading=\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}
+
+\def\CHAPPAGodd{
+\global\let\pchapsepmacro=\chapoddpage
+\global\let\pagealignmacro=\chapoddpage}
+
+\CHAPPAGon
+
+\def\CHAPFplain{
+\global\let\chapmacro=\chfplain
+\global\let\unnumbchapmacro=\unnchfplain}
+
+\def\chfplain #1#2{%
+\pchapsepmacro %
+{\chapfonts \line{\chaprm #2.\enspace #1\hfill}}\bigskip \par\penalty 5000 %
+}
+
+\def\unnchfplain #1{%
+\pchapsepmacro %
+{\chapfonts \line{\chaprm #1\hfill}}\bigskip \par\penalty 10000 %
+}
+\CHAPFplain % The default
+
+\def\unnchfopen #1{%
+\chapoddpage {\chapfonts \line{\chaprm #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}}
+
+\def\secheading #1#2#3{\secheadingi {#2.#3\enspace #1}}
+\def\plainsecheading #1{\secheadingi {#1}}
+\def\secheadingi #1{{\advance \secheadingskip by \parskip %
+\secheadingbreak}%
+{\secfonts \line{\secrm #1\hfill}}%
+\ifdim \parskip<10pt \kern 10pt\kern -\parskip\fi \penalty 10000 }
+
+\def\subsecheading #1#2#3#4{{\advance \subsecheadingskip by \parskip %
+\subsecheadingbreak}%
+{\secfonts \line{\secrm#2.#3.#4\enspace #1\hfill}}%
+\ifdim \parskip<10pt \kern 10pt\kern -\parskip\fi \penalty 10000 }
+
+\def\subsubsecfonts{\subsecfonts} % Maybe this should change
+
+\def\subsubsecheading #1#2#3#4#5{{\advance \subsecheadingskip by \parskip %
+\subsecheadingbreak}%
+{\secfonts \line{\secrm#2.#3.#4.#5\enspace #1\hfill}}%
+\ifdim \parskip<10pt \kern 10pt\kern -\parskip\fi \penalty 10000}
+
+\message{toc printing,}
+
+\def\Dotsbox{\hbox to 1em{\hss.\hss}} % Used by index macros
+
+\def\finishcontents{%
+\ifnum\pageno>0 %
+\pagealignmacro %
+\immediate\closeout \contentsfile%
+\pageno=-1             % Request roman numbered pages
+\fi}
+
+\outer\def\contents{%
+\finishcontents %
+\unnumbchapmacro{Table of Contents}
+\def\thischapter{Table of Contents}
+{\catcode`\\=0
+\catcode`\{=1          % Set up to handle contents files properly
+\catcode`\}=2
+\catcode`\@=11
+\input \jobname.toc
+}
+\vfill \eject}
+
+\outer\def\summarycontents{%
+\finishcontents %
+\unnumbchapmacro{Summary Table of Contents}
+\def\thischapter{Summary Table of Contents}
+{\catcode`\\=0
+\catcode`\{=1          % Set up to handle contents files properly
+\catcode`\}=2
+\catcode`\@=11
+\def\smallbreak{}
+\def\secentry ##1##2##3##4{}
+\def\subsecentry ##1##2##3##4##5{}
+\def\subsubsecentry ##1##2##3##4##5##6{}
+\def\unnumbsecentry ##1##2{}
+\def\unnumbsubsecentry ##1##2{}
+\def\unnumbsubsubsecentry ##1##2{}
+\let\medbreak=\smallbreak
+\input \jobname.toc
+}
+\vfill \eject}
+
+\outer\def\bye{\pagealignmacro\tracingstats=1\ptexend}
+
+% 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, ...
+
+\def\chapentry #1#2#3{%
+\medbreak
+\line{#2.\space#1\leaders\hbox to 1em{\hss.\hss}\hfill #3}
+}
+
+\def\unnumbchapentry #1#2{%
+\medbreak
+\line{#1\leaders\Dotsbox\hfill #2}
+}
+
+\def\secentry #1#2#3#4{%
+\line{\enspace\enspace#2.#3\space#1\leaders\Dotsbox\hfill#4}
+}
+
+\def\unnumbsecentry #1#2{%
+\line{\enspace\enspace#1\leaders\Dotsbox\hfill #2}
+}
+
+\def\subsecentry #1#2#3#4#5{%
+\line{\enspace\enspace\enspace\enspace
+#2.#3.#4\space#1\leaders\Dotsbox\hfill #5}
+}
+
+\def\unnumbsubsecentry #1#2{%
+\line{\enspace\enspace\enspace\enspace#1\leaders\Dotsbox\hfill #2}
+}
+
+\def\subsubsecentry #1#2#3#4#5#6{%
+\line{\enspace\enspace\enspace\enspace\enspace\enspace
+#2.#3.#4.#5\space#1\leaders\Dotsbox\hfill #6}
+}
+
+\def\unnumbsubsubsecentry #1#2{%
+\line{\enspace\enspace\enspace\enspace\enspace\enspace#1\leaders\Dotsbox\hfill #2}
+}
+
+\message{environments,}
+
+% @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`\"=12
+\catcode`\|=12
+\catcode`\<=12
+\catcode`\>=12
+\escapechar=`\\
+%
+\let\{=\ptexlbrace
+\let\}=\ptexrbrace
+\let\.=\ptexdot
+\let\*=\ptexstar
+\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
+% phr: changed space to \null, to avoid overfull hbox problems.
+{\obeyspaces%
+\gdef\lisppar{\null\endgraf}}
+
+% Cause \obeyspaces to make each Space cause a word-separation
+% rather than the default which is that it acts punctuation.
+% This is because space in tt font looks funny.
+{\obeyspaces %
+\gdef\sepspaces{\def {\ }}}
+
+\newskip\aboveenvskipamount \aboveenvskipamount= 0pt
+\def\aboveenvbreak{{\advance\aboveenvskipamount by \parskip
+\endgraf \ifdim\lastskip<\aboveenvskipamount
+\removelastskip \penalty-50 \vskip\aboveenvskipamount \fi}}
+
+\def\afterenvbreak{\endgraf \ifdim\lastskip<\aboveenvskipamount
+\removelastskip \penalty-50 \vskip\aboveenvskipamount \fi}
+
+\def\lisp{\aboveenvbreak\begingroup\inENV %This group ends at the end of the @lisp body
+\hfuzz=12truept % Don't be fussy
+% Make spaces be word-separators rather than space tokens.
+\sepspaces %
+% Single space lines
+\singlespace %
+% The following causes blank lines not to be ignored
+% by adding a space to the end of each line.
+\let\par=\lisppar
+\def\Elisp{\endgroup\afterenvbreak}%
+\parskip=0pt \advance \rightskip by \lispnarrowing 
+\advance \leftskip by \lispnarrowing
+\parindent=0pt
+\let\exdent=\internalexdent
+\obeyspaces \obeylines \tt \rawbackslash
+\def\next##1{}\next}
+
+
+\let\example=\lisp
+\def\Eexample{\Elisp}
+
+\let\smallexample=\lisp
+\def\Esmallexample{\Elisp}
+
+% Macro for 9 pt. examples, necessary to print with 5" lines.
+% From Pavel@xerox.  This is not really used unless the
+% @smallbook command is given.
+
+\def\smalllispx{\aboveenvbreak\begingroup\inENV
+%                      This group ends at the end of the @lisp body
+\hfuzz=12truept % Don't be fussy
+% Make spaces be word-separators rather than space tokens.
+\sepspaces %
+% Single space lines
+\singlespace %
+% The following causes blank lines not to be ignored
+% by adding a space to the end of each line.
+\let\par=\lisppar
+\def\Esmalllisp{\endgroup\afterenvbreak}%
+\parskip=0pt \advance \rightskip by \lispnarrowing 
+\advance \leftskip by \lispnarrowing
+\parindent=0pt
+\let\exdent=\internalexdent
+\obeyspaces \obeylines \ninett \rawbackslash
+\def\next##1{}\next}
+
+% This is @display; same as @lisp except use roman font.
+
+\def\display{\begingroup\inENV %This group ends at the end of the @display body
+\aboveenvbreak
+% Make spaces be word-separators rather than space tokens.
+\sepspaces %
+% Single space lines
+\singlespace %
+% The following causes blank lines not to be ignored
+% by adding a space to the end of each line.
+\let\par=\lisppar
+\def\Edisplay{\endgroup\afterenvbreak}%
+\parskip=0pt \advance \rightskip by \lispnarrowing 
+\advance \leftskip by \lispnarrowing
+\parindent=0pt
+\let\exdent=\internalexdent
+\obeyspaces \obeylines
+\def\next##1{}\next}
+
+% This is @format; same as @lisp except use roman font and don't narrow margins
+
+\def\format{\begingroup\inENV %This group ends at the end of the @format body
+\aboveenvbreak
+% Make spaces be word-separators rather than space tokens.
+\sepspaces %
+\singlespace %
+% The following causes blank lines not to be ignored
+% by adding a space to the end of each line.
+\let\par=\lisppar
+\def\Eformat{\endgroup\afterenvbreak}
+\parskip=0pt \parindent=0pt
+\obeyspaces \obeylines
+\def\next##1{}\next}
+
+% @flushleft and @flushright
+
+\def\flushleft{\begingroup\inENV %This group ends at the end of the @format body
+\aboveenvbreak
+% Make spaces be word-separators rather than space tokens.
+\sepspaces %
+% The following causes blank lines not to be ignored
+% by adding a space to the end of each line.
+% This also causes @ to work when the directive name
+% is terminated by end of line.
+\let\par=\lisppar
+\def\Eflushleft{\endgroup\afterenvbreak}%
+\parskip=0pt \parindent=0pt
+\obeyspaces \obeylines
+\def\next##1{}\next}
+
+\def\flushright{\begingroup\inENV %This group ends at the end of the @format body
+\aboveenvbreak
+% Make spaces be word-separators rather than space tokens.
+\sepspaces %
+% The following causes blank lines not to be ignored
+% by adding a space to the end of each line.
+% This also causes @ to work when the directive name
+% is terminated by end of line.
+\let\par=\lisppar
+\def\Eflushright{\endgroup\afterenvbreak}%
+\parskip=0pt \parindent=0pt
+\advance \leftskip by 0pt plus 1fill
+\obeyspaces \obeylines
+\def\next##1{}\next}
+
+% @quotation - narrow the margins.
+
+\def\quotation{\begingroup\inENV %This group ends at the end of the @quotation body
+{\parskip=0pt  % because we will skip by \parskip too, later
+\aboveenvbreak}%
+\singlespace
+\parindent=0pt
+\def\Equotation{\par\endgroup\afterenvbreak}%
+\advance \rightskip by \lispnarrowing 
+\advance \leftskip by \lispnarrowing}
+
+\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=36pt
+\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}
+{\activeparens % Now, smart parens don't turn on until &foo (see \amprm)
+\gdef\functionparens{\boldbrax\let&=\amprm\parencount=0 }
+\gdef\boldbrax{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb}
+
+% 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{{\tt\char`\[}} \def\rbrb{{\tt\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{%
+\leftskip = 0in  %
+\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
+\rlap{\rightline{{\rm #2}\hskip \deftypemargin}}%
+\tolerance=10000 \hbadness=10000    % Make all lines underfull and no complaints
+{\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 \leftskip=\defbodyindent %
+\begingroup\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 \leftskip=\defbodyindent %
+\begingroup\obeylines\activeparens\spacesplit{#3{#4}}}
+
+% 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 #1%
+\ifnum\parencount=0 \else \errmessage{unbalanced parens in @def arguments}\fi%
+\interlinepenalty=10000
+\endgraf\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}
+
+% @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 %
+}
+
+% @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 %
+}
+
+% @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 %
+}
+
+% 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}}
+
+% @defmethod, and so on
+
+% @defop {Funny Method} foo-class frobnicate argument
+
+\def\defop #1 {\def\defoptype{#1}%
+\defmethparsebody\Edefop\defopx\defopheader}
+
+\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}{Operation on #1}%
+\defunargs {#3}\endgroup %
+}
+
+% @defcv {Class Option} foo-class foo-flag
+
+\def\defcv #1 {\def\defcvtype{#1}%
+\defmethparsebody\Edefcv\defcvx\defcvheader}
+
+\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{\defmethparsebody\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\vskip -\parskip \penalty 10000}
+
+% @defvr Counter foo-count
+
+\def\defvr{\defmethparsebody\Edefvr\defvrx\defvrheader}
+
+\def\defvrheader #1#2#3{\doind {vr}{\code{#2}}%
+\begingroup\defname {#2}{#1}\defvarargs{#3}\endgroup}
+
+% @defvar == @defvr Variable
+
+\def\defvar{\defparsebody\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{\defparsebody\Edefopt\defoptx\defoptheader}
+
+\def\defoptheader #1#2{\doind {vr}{\code{#1}}% Make entry in var index
+\begingroup\defname {#1}{User Option}%
+\defvarargs {#2}\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}}
+
+% 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{\defmethparsebody\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
+
+% \setref{foo} defines a cross-reference point named foo.
+
+\def\setref#1{%
+\dosetq{#1-pg}{Ypagenumber}%
+\dosetq{#1-snt}{Ysectionnumberandtype}}
+
+\def\unnumbsetref#1{%
+\dosetq{#1-pg}{Ypagenumber}%
+\dosetq{#1-snt}{Ynothing}}
+
+% \xref and \pxref generate cross references to specified points.
+
+\def\pxref #1{see \xrefX [#1,,,,,,,]}
+\def\xref #1{See \xrefX [#1,,,,,,,]}
+\def\xrefX [#1,#2,#3,#4,#5,#6]{%
+\setbox1=\hbox{\i{\losespace#5{}}}%
+\setbox0=\hbox{\losespace#3{}}%
+\ifdim \wd0 =0pt \setbox0=\hbox{\losespace#1{}}\fi%
+\ifdim \wd1 >0pt%
+section \unhbox0{} in \unhbox1%
+\else%
+\refx{#1-snt} [\unhbox0], page\tie \refx{#1-pg}%
+\fi }
+
+% \dosetq is the interface for calls from other macros
+
+\def\dosetq #1#2{{\let\folio=0%
+\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\Ynothing{}
+
+\def\Ysectionnumberandtype{%
+\ifnum\secno=0 chapter\xreftie\the\chapno %
+\else \ifnum \subsecno=0 section\xreftie\the\chapno.\the\secno %
+\else \ifnum \subsubsecno=0 %
+section\xreftie\the\chapno.\the\secno.\the\subsecno %
+\else %
+section\xreftie\the\chapno.\the\secno.\the\subsecno.\the\subsubsecno %
+\fi \fi \fi }
+
+\gdef\xreftie{'tie}
+
+% Define @refx to reference a specific cross-reference string.
+
+\def\refx#1{%
+{%
+\expandafter\ifx\csname X#1\endcsname\relax
+% If not defined, say something at least.
+\expandafter\gdef\csname X#1\endcsname {$<$undefined$>$}%
+\message {WARNING: Cross-reference "#1" used but not yet defined}%
+\message {}%
+\fi %
+\csname X#1\endcsname %It's defined, so just use it.
+}}
+
+% 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}}}
+
+{
+\catcode `\^^@=\other
+\catcode `\\ 1=\other
+\catcode `\\ 2=\other
+\catcode `\^^C=\other
+\catcode `\^^D=\other
+\catcode `\^^E=\other
+\catcode `\^^F=\other
+\catcode `\^^G=\other
+\catcode `\^^H=\other
+\catcode `\\v=\other
+\catcode `\^^L=\other
+\catcode `\\ e=\other
+\catcode `\\ f=\other
+\catcode `\\10=\other
+\catcode `\\11=\other
+\catcode `\\12=\other
+\catcode `\\13=\other
+\catcode `\\14=\other
+\catcode `\\15=\other
+\catcode `\\16=\other
+\catcode `\\17=\other
+\catcode `\\18=\other
+\catcode `\\19=\other
+\catcode `\\1a=\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
+
+% 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 `\\=\other
+
+'openin 1 'jobname.aux
+'ifeof 1 'else 'closein 1 'input 'jobname.aux
+'fi
+}
+
+% Open the new aux file.  Tex will close it automatically at exit.
+
+\openout \auxfile=\jobname.aux
+
+% Footnotes.
+
+\newcount \footnoteno
+
+\def\supereject{\par\penalty -20000\footnoteno =0 }
+
+\let\ptexfootnote=\footnote
+
+{\catcode `\@=11
+\gdef\footnote{\global\advance \footnoteno by \@ne
+\edef\thisfootno{$^{\the\footnoteno}$}%
+\let\@sf\empty
+\ifhmode\edef\@sf{\spacefactor\the\spacefactor}\/\fi
+\thisfootno\@sf\parsearg\footnotezzz}
+
+\gdef\footnotezzz #1{\insert\footins{
+\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
+\footstrut\hang\textindent{\thisfootno}#1\strut}}
+
+}%end \catcode `\@=11
+
+% End of control word definitions.
+
+\message{and turning on texinfo input format.}
+
+\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
+\parindent 15pt
+\parskip 18pt plus 1pt
+\baselineskip 15pt
+\advance\topskip by 1.2cm
+
+% Prevent underfull vbox error messages.
+\vbadness=10000
+
+% Use @smallbook to reset parameters for 7x9.5 format
+\def\smallbook{
+\global\lispnarrowing = 0.3in
+\global\baselineskip 12pt
+\global\parskip 3pt plus 1pt
+\global\hsize = 5in
+\global\doublecolumnhsize=2.4in \global\doublecolumnvsize=15.0in
+\global\vsize=7.5in
+\global\tolerance=700
+\global\hfuzz=1pt
+
+\global\pagewidth=\hsize
+\global\pageheight=\vsize
+\global\font\ninett=cmtt9
+
+\global\let\smalllisp=\smalllispx
+\global\let\smallexample=\smalllispx
+\global\def\Esmallexample{\Esmalllisp}
+}
+
+%% 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}
+
+% Turn off all special characters except @
+% (and those which the user can use as if they were ordinary)
+% Define certain chars to be always in tt font.
+
+\catcode`\"=\active
+\def\activedoublequote{{\tt \char '042}}
+\let"=\activedoublequote
+\catcode`\~=\active
+\def~{{\tt \char '176}}
+\chardef\hat=`\^
+\catcode`\^=\active
+\def^{{\tt \hat}}
+\catcode`\_=\active
+\def_{{\tt \char '137}}
+\catcode`\|=\active
+\def|{{\tt \char '174}}
+\chardef \less=`\<
+\catcode`\<=\active
+\def<{{\tt \less}}
+\chardef \gtr=`\>
+\catcode`\>=\active
+\def>{{\tt \gtr}}
+
+\catcode`\@=0
+
+% \rawbackslashxx output one backslash character in current font
+{\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=`\@
+
+%% 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
+
+\catcode 17=0   @c Define control-q
+\catcode`\\=\active
+@let\=@normalbackslash
+
+@textfonts
+@rm
diff --git a/src/util/et/vfprintf.c b/src/util/et/vfprintf.c
new file mode 100644 (file)
index 0000000..94f0fb5
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)vfprintf.c 5.2 (Berkeley) 6/27/88";
+#endif /* LIBC_SCCS and not lint */
+
+#include <stdio.h>
+#include <varargs.h>
+
+int
+vfprintf(iop, fmt, ap)
+       FILE *iop;
+       char *fmt;
+       va_list ap;
+{
+       int len;
+       char localbuf[BUFSIZ];
+
+       if (iop->_flag & _IONBF) {
+               iop->_flag &= ~_IONBF;
+               iop->_ptr = iop->_base = localbuf;
+               len = _doprnt(fmt, ap, iop);
+               (void) fflush(iop);
+               iop->_flag |= _IONBF;
+               iop->_base = NULL;
+               iop->_bufsiz = 0;
+               iop->_cnt = 0;
+       } else
+               len = _doprnt(fmt, ap, iop);
+
+       return (ferror(iop) ? EOF : len);
+}
diff --git a/src/util/ss/Imakefile b/src/util/ss/Imakefile
new file mode 100644 (file)
index 0000000..adfe36d
--- /dev/null
@@ -0,0 +1,68 @@
+#      $Source$
+#      $Author$
+#      $Id$
+#
+#  Copyright 1990 by the Massachusetts Institute of Technology.
+#  All Rights Reserved.
+# 
+# Export of this software from the United States of America is assumed
+#   to require a specific license from the United States Government.
+#   It is the responsibility of any person or organization contemplating
+#   export to obtain such a license before exporting.
+# 
+# WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+# distribute this software and its documentation for any purpose and
+# without fee is hereby granted, provided that the above copyright
+# notice appear in all copies and that both that copyright notice and
+# this permission notice appear in supporting documentation, and that
+# the name of M.I.T. not be used in advertising or publicity pertaining
+# to distribution of the software without specific, written prior
+# permission.  M.I.T. makes no representations about the suitability of
+# this software for any purpose.  It is provided "as is" without express
+# or implied warranty.
+# 
+# 
+NormalLibraryObjectRule()
+ErrorTableObjectRule()
+
+OBJS=  ss_err.o \
+       std_rqs.o \
+       invocation.o help.o \
+       execute_cmd.o listen.o parse.o error.o prompt.o \
+       request_tbl.o list_rqs.o pager.o requests.o \
+       data.o
+
+SRCS=  invocation.c help.c \
+       execute_cmd.c listen.c parse.c error.c prompt.c \
+       request_tbl.c list_rqs.c pager.c requests.c \
+       data.c
+
+INCLUDE=-I$(TOP)/include -I. -I.. -I../et
+
+Krb5LibraryTarget(ss,$(OBJS))
+
+mk_cmds: mk_cmds.sh
+       ./config_script mk_cmds.sh $(AWK) > mk_cmds
+       chmod +x mk_cmds
+
+SpecialObjectRule(ss_err.o,ss_err.c,)
+SpecialObjectRule(std_rqs.o,std_rqs.c,)
+
+std_rqs.c: std_rqs.ct mk_cmds
+       ./mk_cmds std_rqs.ct
+
+all:: mk_cmds
+
+includes:: mk_cmds ss_err.h
+       -$(RM) -rf $(TOP)/include/ss
+       mkdir $(TOP)/include/ss
+       $(LN) ../../$(CURRENT_DIR)/ss.h $(TOP)/include/ss
+       $(LN) ../../$(CURRENT_DIR)/mit-sipb-copyright.h $(TOP)/include/ss
+       $(LN) ../../$(CURRENT_DIR)/copyright.h $(TOP)/include/ss
+       $(LN) ../../$(CURRENT_DIR)/ss_err.h $(TOP)/include/ss
+       $(LN) ../../$(CURRENT_DIR)/ss_internal.h $(TOP)/include/ss
+
+clean::
+       $(RM) mk_cmds ss_err.c ss_err.h
+
+
diff --git a/src/util/ss/Makefile.in b/src/util/ss/Makefile.in
new file mode 100644 (file)
index 0000000..7a5b3c8
--- /dev/null
@@ -0,0 +1,140 @@
+#### insert configury here
+# flags
+BUILDTOP=../..
+
+# hard coded srcdir/.. is so that ss/ss.h works
+
+# hard coded .. is so that ss/ss_err.h works
+# hard coded ../et is so com_err.h works
+# CFLAGS= -I${INCDIR} -I. -I.. -I../et -g
+LOCALINCLUDE= -I. -I$(srcdir)/.. -I$(srcdir)/../et -I..
+
+# for the library
+
+LIB=   libss.a
+
+# with ss_err.o first, ss_err.h should get rebuilt first too.  should not
+# be relying on this, though.
+OBJS=  ss_err.o \
+       std_rqs.o \
+       invocation.o help.o \
+       execute_cmd.o listen.o parse.o error.o prompt.o \
+       request_tbl.o list_rqs.o pager.o requests.o \
+       data.o
+
+SRCS=  invocation.c help.c \
+       execute_cmd.c listen.c parse.c error.c prompt.c \
+       request_tbl.c list_rqs.c pager.c requests.c \
+       data.c  \
+       ss_err.h
+# ss_err.h here, so that make depend catches it.
+
+CODE= $(SRCS) $(MKCMDSFILES)
+
+MKCMDSOBJS=    mk_cmds.o utils.o options.o ct.tab.o cmd_tbl.lex.o
+
+MKCMDSFILES=   mk_cmds.c utils.c options.c ct.y cmd_tbl.lex.l
+
+MKCMDSCSRCS=   mk_cmds.c utils.c options.c ct.tab.c cmd_tbl.lex.c
+
+
+HFILES=        ss.h ss_internal.h copyright.h
+
+# for 'tags' and dependencies
+
+CFILES=        $(SRCS) $(MKCMDSCSRCS) test_ss.c
+
+# for building archives
+
+FILES= $(SRCS) $(MKCMDSFILES) $(HFILES) \
+       ss_err.et std_rqs.ct Makefile \
+       test_ss.c ss mit-sipb-copyright.h copyright.h
+
+#
+# stuff to build
+#
+
+all::  mk_cmds libss.a # libss_p.a lint
+
+dist:  archives
+
+install:: all
+       $(INSTALLLIB) libss.a ${DESTDIR}$(LIBDIR)/libss.a
+       $(RANLIB) ${DESTDIR}$(LIBDIR)/libss.a
+       @rm -rf ${DESTDIR}$(INCLDIR)/ss
+       @mkdir ${DESTDIR}$(INCLDIR)/ss
+
+
+
+install:: $(HFILES) copyright.h
+       for i in $(HFILES) copyright.h; do \
+               $(INSTALLFILE) $(srcdir)/$$i ${DESTDIR}$(INCLDIR)/ss/$$i; \
+       done
+
+install:: copyright.h
+       $(INSTALLFILE) $(srcdir)/copyright.h ${DESTDIR}$(INCLDIR)/ss/mit-sipb-copyright.h
+
+std_rqs.c: std_rqs.ct
+
+ss_err.h: ss_err.et
+
+ss_err.c: ss_err.et
+
+clean::
+       $(RM) ss_err.o ss_err.c ss_err.h
+
+depend:: ss_err.h
+
+ct.tab.c ct.tab.h: ct.y
+       rm -f ct.tab.* y.*
+       yacc -d $(srcdir)/ct.y
+       mv -f y.tab.c ct.tab.c
+       mv -f y.tab.h ct.tab.h
+
+# install_library_target(ss,$(OBJS),$(SRCS),)
+all:: libss.a
+
+libss.a: $(OBJS)
+       $(RM) $@.bak
+       -$(MV) $@ $@.bak
+       $(ARCHIVE) $@ $(OBJS)
+       $(RANLIB) $@
+
+clean::
+       $(RM) libss.a
+       $(RM) $(OBJS)
+
+install::
+       $(INSTALLLIB) libss.a $(DESTDIR)$(LIBDIR)/libss.a
+       $(CHMOD) 644 $(DESTDIR)$(LIBDIR)/libss.a
+       $(RANLIB)    $(DESTDIR)$(LIBDIR)/libss.a
+       $(CHMOD) 444 $(DESTDIR)$(LIBDIR)/libss.a
+## 
+
+
+libss.o:       $(OBJS)
+       $(LD) -r -s -o $@ $(OBJS)
+       $(CHMOD) -x $@
+
+# program(mk_cmds,$(MKCMDSOBJS), , LEXLIB BSDLIB,$(PROGDIR)) 
+all:: mk_cmds
+
+mk_cmds: $(MKCMDSOBJS)
+       $(CC) $(CFLAGS) -o $@ $(MKCMDSOBJS) $(LEXLIB) $(BSDLIB)
+
+install::
+       $(INSTALLPROG) mk_cmds ${DESTDIR}$(PROGDIR)/mk_cmds
+
+clean::
+       $(RM) mk_cmds $(MKCMDSOBJS)
+
+# 
+
+clean::
+       rm -f *.o *~ \#* *.bak core \
+               ss_err.h ct.tab.c ct.tab.h cmd_tbl.lex.c \
+               lex.yy.c y.tab.c \
+               libss.a libss_p.a llib-lss.ln mk_cmds \
+               ss.ar ss.tar \
+               TAGS test_ss
+
diff --git a/src/util/ss/cmd_tbl.lex.l b/src/util/ss/cmd_tbl.lex.l
new file mode 100644 (file)
index 0000000..2f413a8
--- /dev/null
@@ -0,0 +1,78 @@
+N      [0-9]
+PC     [^\"]
+AN      [A-Z_a-z0-9]
+%%
+
+command_table  return l_command_table();
+request                return l_request();
+unimplemented  return l_unimplemented();
+end            return l_end();
+
+[\t\n ]                ;
+
+\"{PC}*\"      return l_quoted_string();
+
+{AN}*          return l_string();
+
+#.*\n          ;
+
+.              return (*yytext);
+%%
+/*
+ * User-subroutines section.
+ *
+ * Have to put all this stuff here so that the include file
+ * from YACC output can be included, since LEX doesn't allow
+ * an include file before the code it generates for the above
+ * rules.
+ *
+ * Copyright 1987 by MIT Student Information Processing Board.
+ *
+ * For copyright info, see mit-sipb-copyright.h.
+ */
+#include <string.h>
+#include "ct.tab.h"
+#include "mit-sipb-copyright.h"
+
+extern char *last_token;
+
+static l_command_table()
+{
+     last_token = "command_table";
+     return COMMAND_TABLE;
+}
+
+static l_request()
+{
+     last_token = "request";
+     return REQUEST;
+}
+
+static l_unimplemented()
+{
+     last_token = "unimplemented";
+     return UNIMPLEMENTED;
+}
+
+static l_end()
+{
+     last_token = "end";
+     return END;
+}
+
+static l_quoted_string()
+{
+     register char *p;
+     yylval.dynstr = strdup(yytext+1);
+     if (p=strrchr(yylval.dynstr, '"'))
+         *p='\0';
+     last_token = strdup(yylval.dynstr);
+     return STRING;
+}
+
+static l_string()
+{
+     yylval.dynstr = strdup(yytext);
+     last_token = strdup(yylval.dynstr);
+     return STRING;
+}
diff --git a/src/util/ss/config_script b/src/util/ss/config_script
new file mode 100644 (file)
index 0000000..e3de35c
--- /dev/null
@@ -0,0 +1,25 @@
+#!/bin/sh
+#
+# This program takes a shell script and configures for the following
+# variables:   @DIR@
+#              @AWK@
+#              @SED@
+#
+# Usage: config_script <filename> [<awk>] [<sed>]
+#
+
+FILE=$1
+AWK=$2
+SED=$3
+
+# Grr.... not all Unix's have the dirname command
+TMP=`echo  $1 | sed -e 's;[^/]*$;;' -e 's/^$/./'`
+DIR=`cd ${TMP}; pwd`
+
+if test "${AWK}x" = "x" ; then
+       AWK=awk
+fi
+if test "${SED}x" = "x" ; then
+       SED=sed
+fi
+sed -e "s;@DIR@;${DIR};" -e "s;@AWK@;${AWK};" -e "s;@SED@;${SED};" $FILE
diff --git a/src/util/ss/copyright.h b/src/util/ss/copyright.h
new file mode 100644 (file)
index 0000000..e0d1572
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+
+Copyright 1987, 1989 by the Student Information Processing Board
+       of the Massachusetts Institute of Technology
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is
+hereby granted, provided that the above copyright notice
+appear in all copies and that both that copyright notice and
+this permission notice appear in supporting documentation,
+and that the names of M.I.T. and the M.I.T. S.I.P.B. not be
+used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+M.I.T. and the M.I.T. S.I.P.B. make no representations about
+the suitability of this software for any purpose.  It is
+provided "as is" without express or implied warranty.
+
+*/
+
diff --git a/src/util/ss/ct.y b/src/util/ss/ct.y
new file mode 100644 (file)
index 0000000..6ad5dfc
--- /dev/null
@@ -0,0 +1,77 @@
+%{
+/*
+ * Copyright 1987 by MIT Student Information Processing Board
+ *
+ * For copyright info, see mit-sipb-copyright.h.
+ */
+#include <stdio.h>
+#include "mit-sipb-copyright.h"
+
+char *str_concat3(), *generate_rqte(), *quote();
+long flag_value();
+char *last_token = (char *)NULL;
+FILE *output_file;
+long gensym_n = 0;
+
+%}
+%union {
+       char *dynstr;
+       long flags;
+}
+
+%token COMMAND_TABLE REQUEST UNKNOWN UNIMPLEMENTED END
+%token <dynstr> STRING
+%token <dynstr> FLAGNAME
+%type <dynstr> namelist header request_list
+%type <dynstr> request_entry
+%type <flags> flag_list options
+%left OPTIONS
+%{
+#include "ss.h"
+%}
+%start command_table
+%%
+command_table :        header request_list END ';'
+               { write_ct($1, $2); }
+       ;
+
+header :       COMMAND_TABLE STRING ';'
+               { $$ = $2; }
+       ;
+
+request_list : request_list request_entry
+               { $$ = str_concat3($1, $2, ""); }
+       |
+               { $$ = ""; }
+       ;
+
+request_entry :        REQUEST STRING ',' STRING ',' namelist ',' options ';'
+               { $$ = generate_rqte($2, quote($4), $6, $8); }
+       |       REQUEST STRING ',' STRING ',' namelist ';'
+               { $$ = generate_rqte($2, quote($4), $6, 0); }
+       |       UNKNOWN namelist ';'
+               { $$ = generate_rqte("ss_unknown_request",
+                                       (char *)NULL, $2, 0); }
+       |       UNIMPLEMENTED STRING ',' STRING ',' namelist ';'
+               { $$ = generate_rqte("ss_unimplemented", quote($4), $6, 3); }
+       ;
+
+options        :       '(' flag_list ')'
+               { $$ = $2; }
+       |       '(' ')'
+               { $$ = 0; }
+       ;
+
+flag_list :    flag_list ',' STRING
+               { $$ = $1 | flag_val($3); }
+       |       STRING
+               { $$ = flag_val($1); }
+       ;
+
+namelist:      STRING
+               { $$ = quote(strdup($1)); }
+       |       namelist ',' STRING
+               { $$ = str_concat3($1, quote($3), ",\n    "); }
+       ;
+
+%%
diff --git a/src/util/ss/ct_c.awk b/src/util/ss/ct_c.awk
new file mode 100644 (file)
index 0000000..872f6e0
--- /dev/null
@@ -0,0 +1,77 @@
+/^command_table / {
+       cmdtbl = $2;
+       printf "/* %s.c - automatically generated from %s.ct */\n", \
+               rootname, rootname > outfile
+       print "#include <ss/ss.h>" > outfile
+       print "" >outfile
+       print "#ifndef __STDC__" > outfile
+       print "#define const" > outfile
+       print "#endif" > outfile
+       print "" > outfile
+}
+       
+/^BOR$/ {
+       cmdnum++
+       options = 0
+       cmdtab = ""
+       printf "static char const * const ssu%05d[] = {\n", cmdnum > outfile
+}
+
+/^sub/ {
+       subr = substr($0, 6, length($0)-5)
+}
+
+/^hlp/ {
+       help = substr($0, 6, length($0)-5)
+}
+
+/^cmd/ {
+       cmd = substr($0, 6, length($0)-5)
+       printf "%s\"%s\",\n", cmdtab, cmd > outfile
+       cmdtab = "    "
+}
+
+/^opt/ {
+       opt = substr($0, 6, length($0)-5)
+       if (opt == "dont_list") {
+               options += 1
+       }
+       if (opt == "dont_summarize") {
+               options += 2
+       }
+}
+
+/^EOR/ {
+       print "    (char const *)0" > outfile
+       print "};" > outfile 
+       printf "extern void %s __SS_PROTO;\n", subr > outfile
+       subr_tab[cmdnum] = subr
+       options_tab[cmdnum] = options
+       help_tab[cmdnum] = help
+}
+
+/^[0-9]/ {
+       linenum = $1;
+}
+
+/^ERROR/ {
+       error = substr($0, 8, length($0)-7)
+       printf "Error in line %d: %s\n", linenum, error
+       print "#__ERROR_IN_FILE__" > outfile
+}
+
+END {
+       printf "static ss_request_entry ssu%05d[] = {\n", cmdnum+1 > outfile
+       for (i=1; i <= cmdnum; i++) {
+               printf "    { ssu%05d,\n", i > outfile
+               printf "      %s,\n", subr_tab[i] > outfile
+               printf "      \"%s\",\n", help_tab[i] > outfile
+               printf "      %d },\n", options_tab[i] > outfile
+       }
+       print "    { 0, 0, 0, 0 }" > outfile
+       print "};" > outfile
+       print "" > outfile
+       printf "ss_request_table %s = { 2, ssu%05d };\n", \
+               cmdtbl, cmdnum+1 > outfile
+}
+
diff --git a/src/util/ss/ct_c.sed b/src/util/ss/ct_c.sed
new file mode 100644 (file)
index 0000000..8d6452b
--- /dev/null
@@ -0,0 +1,160 @@
+#
+# This script parses a command_table file into something which is a bit 
+# easier for an awk script to understand.
+#
+# Input syntax: a .ct file
+#
+# Output syntax:
+# (for the command_table line)
+#      command_table  <command_table>
+#
+#(for each request definition)
+#      BOR
+#      sub: <subroutine name>
+#      hlp: <help text>
+#      cmd: <command>
+#      opt: <option>
+#      EOR
+# (there may be more than one 'cmd' or 'opt' line
+#
+# A number sent to the output represents a parse error --- it will be 
+# followed by the next line which will have the form:
+#      ERROR: <error text>
+#
+# The design of this output syntax is such that it should be easy for
+# an awk script to parse.
+
+#
+# The first section of this script is just to cannoicalize the file.  
+# It removes comments, and puts each command_table request onto a single
+# line
+#
+:FIRST
+y/     / /
+s/^ *//
+s/#.*$//
+/; *$/!{
+N
+y/     / /
+s/\n */ /
+bFIRST
+}
+s/, */, /g
+#
+# Now we take care of some syntatic sugar.....
+#
+/^unimplemented/ {
+       s/^unimplemented [A-Za-z_0-9]*/request ss_unimplemented/
+       s/;/, (dont_list, dont_summarize);/
+}
+/^unknown/ {
+       s/^unknown /request ss_unknown, "", /
+}
+#
+# Dispatch based on the keyword....  illegal keywords are prefixed by ERROR:
+# and are handled by the awk script.
+#
+/^command_table /bCMD
+/^request /bREQUEST
+/^end;/bEND
+s/ .*//
+s/^/ERROR: unknown keyword: /
+=
+b
+#
+# Handle the command_table keyword
+#
+:CMD
+s/;$//
+p
+d
+b
+#
+# Handle the request keyword --- this is the heart of the sed script.
+# 
+:REQUEST
+s/^request *//
+h
+i\
+BOR
+# First, parse out the subroutine name
+s/^/sub: /
+s/,.*//
+p
+# Next, parse out the help message, being careful to handle a quoted string
+g
+s/^[^,]*, *//
+h
+/^"/ {
+       s/^"//
+       s/".*//
+       x
+       s/^"[^"]*", *//
+       x
+       b EMITHLP
+}
+s/[^a-zA-Z0-9].*//
+x
+s/[a-zA-Z0-9]*, *//
+x
+:EMITHLP
+s/^/hlp: /
+p
+# Next take care of the command names
+:CMDLIST
+g
+/^(/b OPTIONS
+/^;/b EOR
+/^"/ {
+       s/^"//
+       s/".*//
+       x
+       s/^"[^"]*"//
+       s/, *//
+       x
+       b EMITREQ
+}
+s/[^A-Za-z_0-9].*//
+x
+s/[A-Za-z_0-9]*//
+s/, *//
+x
+:EMITREQ
+s/^/cmd: /
+p
+b CMDLIST
+#
+# Here we parse the list of options.
+#
+: OPTIONS
+g
+s/^(//
+h
+: OPTLIST
+/^)/ b EOR
+/^[^A-Za-z_0-9]/ {
+       =
+       c\
+ERROR: parse error in options list
+}
+s/[^A-Za-z_0-9].*//
+x
+s/[A-Za-z_0-9]*//
+s/, *//
+x
+s/^/opt: /
+p
+g
+b OPTLIST
+: EOR
+c\
+EOR\
+
+d
+b
+#
+# Handle the end keyword --- it's basically ignored.
+#
+:END
+d
+b
diff --git a/src/util/ss/data.c b/src/util/ss/data.c
new file mode 100644 (file)
index 0000000..dd6341c
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright 1987, 1988, 1989 Massachusetts Institute of Technology
+ * (Student Information Processing Board)
+ *
+ * For copyright info, see copyright.h.
+ */
+
+#include <stdio.h>
+#include "ss_internal.h"
+#include "copyright.h"
+
+const static char copyright[] =
+    "Copyright 1987, 1988, 1989 by the Massachusetts Institute of Technology";
+
+ss_data **_ss_table = (ss_data **)NULL;
+char *_ss_pager_name = (char *)NULL;
diff --git a/src/util/ss/error.c b/src/util/ss/error.c
new file mode 100644 (file)
index 0000000..3b7165a
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright 1987, 1988, 1989 by MIT Student Information Processing
+ * Board
+ *
+ * For copyright information, see copyright.h.
+ */
+
+#include <stdio.h>
+
+/*
+ * I'm assuming that com_err.h includes varargs.h, which it does
+ * (right now).  There really ought to be a way for me to include the
+ * file without worrying about whether com_err.h includes it or not,
+ * but varargs.h doesn't define anything that I can use as a flag, and
+ * gcc will lose if I try to include it twice and redefine stuff.
+ */
+#if !defined(__STDC__) || !defined(ibm032) || !defined(NeXT)
+#define ss_error ss_error_external
+#endif
+
+#include "copyright.h"
+#include <com_err.h>
+#include "ss_internal.h"
+
+#ifdef _STDARG_H_
+#define STDARG
+#endif
+
+#ifdef _STDARG_H
+#define STDARG
+#endif
+
+#ifndef __STDC__
+/* we didn't get it in com_err.h if it wasn't STDC. */
+#ifndef STDARG
+/* and we don't need it, either, if we're using stdarg.h... */
+#include <varargs.h>
+#endif
+#endif
+  
+#undef ss_error
+
+char * ss_name(sci_idx)
+    int sci_idx;
+{
+    register char *ret_val;
+    register ss_data *infop;
+    
+    infop = ss_info(sci_idx);
+    if (infop->current_request == (char const *)NULL) {
+       ret_val = malloc((unsigned)
+                        (strlen(infop->subsystem_name)+1)
+                        * sizeof(char));
+       if (ret_val == (char *)NULL)
+           return((char *)NULL);
+       strcpy(ret_val, infop->subsystem_name);
+       return(ret_val);
+    }
+    else {
+       register char *cp;
+       register char const *cp1;
+       ret_val = malloc((unsigned)sizeof(char) * 
+                        (strlen(infop->subsystem_name)+
+                         strlen(infop->current_request)+
+                         4));
+       cp = ret_val;
+       cp1 = infop->subsystem_name;
+       while (*cp1)
+           *cp++ = *cp1++;
+       *cp++ = ' ';
+       *cp++ = '(';
+       cp1 = infop->current_request;
+       while (*cp1)
+           *cp++ = *cp1++;
+       *cp++ = ')';
+       *cp = '\0';
+       return(ret_val);
+    }
+}
+
+#ifdef STDARG
+void ss_error (int sci_idx, long code, const char * fmt, ...)
+#else
+void ss_error (va_alist)
+    va_dcl
+#endif
+{
+    register char const *whoami;
+    va_list pvar;
+#ifndef STDARG
+    int sci_idx;
+    long code;
+    char * fmt;
+    va_start (pvar);
+    sci_idx = va_arg (pvar, int);
+    code = va_arg (pvar, long);
+    fmt = va_arg (pvar, char *);
+#else
+    va_start (pvar, fmt);
+#endif
+    whoami = ss_name (sci_idx);
+    com_err_va (whoami, code, fmt, pvar);
+    free (whoami);
+    va_end(pvar);
+}
+
+void ss_perror (sci_idx, code, msg) /* for compatibility */
+    int sci_idx;
+    long code;
+    char const *msg;
+{
+    ss_error (sci_idx, code, "%s", msg);
+}
diff --git a/src/util/ss/execute_cmd.c b/src/util/ss/execute_cmd.c
new file mode 100644 (file)
index 0000000..9442f33
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * Copyright 1987, 1988, 1989 by Massachusetts Institute of Technology
+ *
+ * For copyright info, see copyright.h.
+ */
+
+#include "ss_internal.h"
+#include "copyright.h"
+#include <stdio.h>
+
+#ifndef lint
+static char const rcsid[] =
+    "$Header$";
+#endif
+
+/*
+ * get_request(tbl, idx)
+ *
+ * Function:
+ *      Gets the idx'th request from the request table pointed to
+ *      by tbl.
+ * Arguments:
+ *      tbl (ss_request_table *)
+ *              pointer to request table
+ *      idx (int)
+ *              index into table
+ * Returns:
+ *      (ss_request_entry *)
+ *              pointer to request table entry
+ * Notes:
+ *      Has been replaced by a macro.
+ */
+
+#ifdef __SABER__
+/* sigh.  saber won't deal with pointer-to-const-struct */
+static struct _ss_request_entry * get_request (tbl, idx)
+    ss_request_table * tbl;
+    int idx;
+{
+    struct _ss_request_table *tbl1 = (struct _ss_request_table *) tbl;
+    struct _ss_request_entry *e = (struct _ss_request_entry *) tbl1->requests;
+    return e + idx;
+}
+#else
+#define get_request(tbl,idx)    ((tbl) -> requests + (idx))
+#endif
+
+/*
+ * check_request_table(rqtbl, argc, argv, sci_idx)
+ *
+ * Function:
+ *      If the command string in argv[0] is in the request table, execute
+ *      the commands and return error code 0.  Otherwise, return error
+ *      code ss_et_command_not_found.
+ * Arguments:
+ *      rqtbl (ss_request_table *)
+ *              pointer to request table
+ *      argc (int)
+ *              number of elements in argv[]
+ *      argv (char *[])
+ *              argument string array
+ *      sci_idx (int)
+ *              ss-internal index for subsystem control info structure
+ * Returns:
+ *      (int)
+ *              zero if command found, ss_et_command_not_found otherwise
+ * Notes:
+ */
+
+static int check_request_table (rqtbl, argc, argv, sci_idx)
+    register ss_request_table *rqtbl;
+    int argc;
+    char *argv[];
+    int sci_idx;
+{
+#ifdef __SABER__
+    struct _ss_request_entry *request;
+#else
+    register ss_request_entry *request;
+#endif
+    register ss_data *info;
+    register char const * const * name;
+    char *string = argv[0];
+    int i;
+
+    info = ss_info(sci_idx);
+    info->argc = argc;
+    info->argv = argv;
+    for (i = 0; (request = get_request(rqtbl, i))->command_names; i++) {
+       for (name = request->command_names; *name; name++)
+           if (!strcmp(*name, string)) {
+               info->current_request = request->command_names[0];
+               (request->function)(argc, (const char *const *) argv,
+                                   sci_idx,info->info_ptr);
+               info->current_request = (char *)NULL;
+               return(0);
+           }
+    }
+    return(SS_ET_COMMAND_NOT_FOUND);
+}
+
+/*
+ * really_execute_command(sci_idx, argc, argv)
+ *
+ * Function:
+ *      Fills in the argc, argv values in the subsystem entry and
+ *      call the appropriate routine.
+ * Arguments:
+ *      sci_idx (int)
+ *              ss-internal index for subsystem control info structure
+ *      argc (int)
+ *              number of arguments in argument list
+ *      argv (char **[])
+ *              pointer to parsed argument list (may be reallocated
+ *              on abbrev expansion)
+ *
+ * Returns:
+ *      (int)
+ *              Zero if successful, ss_et_command_not_found otherwise.
+ * Notes:
+ */
+
+static int really_execute_command (sci_idx, argc, argv)
+    int sci_idx;
+    int argc;
+    char **argv[];
+{
+    register ss_request_table **rqtbl;
+    register ss_data *info;
+
+    info = ss_info(sci_idx);
+
+    for (rqtbl = info->rqt_tables; *rqtbl; rqtbl++) {
+        if (check_request_table (*rqtbl, argc, *argv, sci_idx) == 0)
+            return(0);
+    }
+    return(SS_ET_COMMAND_NOT_FOUND);
+}
+
+/*
+ * ss_execute_command(sci_idx, argv)
+ *
+ * Function:
+ *     Executes a parsed command list within the subsystem.
+ * Arguments:
+ *     sci_idx (int)
+ *             ss-internal index for subsystem control info structure
+ *     argv (char *[])
+ *             parsed argument list
+ * Returns:
+ *     (int)
+ *             Zero if successful, ss_et_command_not_found otherwise.
+ * Notes:
+ */
+
+ss_execute_command(sci_idx, argv)
+       int sci_idx;
+       register char *argv[];
+{
+       register int i, argc;
+       char **argp;
+
+       argc = 0;
+       for (argp = argv; *argp; argp++)
+               argc++;
+       argp = (char **)malloc((argc+1)*sizeof(char *));
+       for (i = 0; i <= argc; i++)
+               argp[i] = argv[i];
+       i = really_execute_command(sci_idx, argc, &argp);
+       free(argp);
+       return(i);
+}
+
+/*
+ * ss_execute_line(sci_idx, line_ptr)
+ *
+ * Function:
+ *      Parses and executes a command line within a subsystem.
+ * Arguments:
+ *      sci_idx (int)
+ *              ss-internal index for subsystem control info structure
+ *      line_ptr (char *)
+ *              Pointer to command line to be parsed.
+ * Returns:
+ *      (int)
+ *             Error code.
+ * Notes:
+ */
+
+int ss_execute_line (sci_idx, line_ptr)
+    int sci_idx;
+    char *line_ptr;
+{
+    char **argv;
+    int argc;
+
+    /* flush leading whitespace */
+    while (line_ptr[0] == ' ' || line_ptr[0] == '\t')
+        line_ptr++;
+
+    /* check if it should be sent to operating system for execution */
+    if (*line_ptr == '!') {
+        if (ss_info(sci_idx)->flags.escape_disabled)
+            return SS_ET_ESCAPE_DISABLED;
+        else {
+            line_ptr++;
+            system(line_ptr);
+           return 0;
+        }
+    }
+
+    /* parse it */
+    argv = ss_parse(sci_idx, line_ptr, &argc);
+    if (argc == 0)
+        return 0;
+
+    /* look it up in the request tables, execute if found */
+    return really_execute_command (sci_idx, argc, &argv);
+}
diff --git a/src/util/ss/help.c b/src/util/ss/help.c
new file mode 100644 (file)
index 0000000..ad3b90b
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * Copyright 1987, 1988 by MIT Student Information Processing Board
+ *
+ * For copyright info, see copyright.h.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#ifdef NEED_SYS_FCNTL_H
+/* just for O_* */
+#include <sys/fcntl.h>
+#endif
+#include <sys/wait.h>
+#include "ss_internal.h"
+#include "copyright.h"
+
+extern int errno;
+
+void ss_help (argc, argv, sci_idx, info_ptr)
+    int argc;
+    char const * const *argv;
+    int sci_idx;
+    pointer info_ptr;
+{
+    char buffer[MAXPATHLEN];
+    char const *request_name;
+    int code;
+    int fd, child;
+    register int idx;
+    register ss_data *info;
+
+    request_name = ss_current_request(sci_idx, &code);
+    if (code != 0) {
+       ss_perror(sci_idx, code, "");
+       return;         /* no ss_abort_line, if invalid invocation */
+    }
+    if (argc == 1) {
+       ss_list_requests(argc, argv, sci_idx, info_ptr);
+       return;
+    }
+    else if (argc != 2) {
+       /* should do something better than this */
+       sprintf(buffer, "usage:\n\t%s [topic|command]\nor\t%s\n",
+               request_name, request_name);
+       ss_perror(sci_idx, 0, buffer);
+       return;
+    }
+    info = ss_info(sci_idx);
+    if (info->info_dirs == (char **)NULL) {
+       ss_perror(sci_idx, SS_ET_NO_INFO_DIR, (char *)NULL);
+       return;
+    }
+    if (info->info_dirs[0] == (char *)NULL) {
+       ss_perror(sci_idx, SS_ET_NO_INFO_DIR, (char *)NULL);
+       return;
+    }
+    for (idx = 0; info->info_dirs[idx] != (char *)NULL; idx++) {
+       (void) strcpy(buffer, info->info_dirs[idx]);
+       (void) strcat(buffer, "/");
+       (void) strcat(buffer, argv[1]);
+       (void) strcat(buffer, ".info");
+       if ((fd = open(&buffer[0], O_RDONLY)) >= 0) goto got_it;
+    }
+    if ((fd = open(&buffer[0], O_RDONLY)) < 0) {
+       char buf[MAXPATHLEN];
+       strcpy(buf, "No info found for ");
+       strcat(buf, argv[1]);
+       ss_perror(sci_idx, 0, buf);
+       return;
+    }
+got_it:
+    switch (child = fork()) {
+    case -1:
+       ss_perror(sci_idx, errno, "Can't fork for pager");
+       return;
+    case 0:
+       (void) dup2(fd, 0); /* put file on stdin */
+       ss_page_stdin();
+    default:
+       (void) close(fd); /* what can we do if it fails? */
+       while (wait((union wait *)NULL) != child) {
+           /* do nothing if wrong pid */
+       };
+    }
+}
+
+#ifndef USE_DIRENT_H
+#include <sys/dir.h>
+#else
+#include <dirent.h>
+#endif
+
+void ss_add_info_dir(sci_idx, info_dir, code_ptr)
+    int sci_idx;
+    char *info_dir;
+    int *code_ptr;
+{
+    register ss_data *info;
+    DIR *d;
+    int n_dirs;
+    register char **dirs;
+
+    info = ss_info(sci_idx);
+    if (info_dir == NULL && *info_dir) {
+       *code_ptr = SS_ET_NO_INFO_DIR;
+       return;
+    }
+    if ((d = opendir(info_dir)) == (DIR *)NULL) {
+       *code_ptr = errno;
+       return;
+    }
+    closedir(d);
+    dirs = info->info_dirs;
+    for (n_dirs = 0; dirs[n_dirs] != (char *)NULL; n_dirs++)
+       ;               /* get number of non-NULL dir entries */
+    dirs = (char **)realloc((char *)dirs,
+                           (unsigned)(n_dirs + 2)*sizeof(char *));
+    if (dirs == (char **)NULL) {
+       info->info_dirs = (char **)NULL;
+       *code_ptr = errno;
+       return;
+    }
+    info->info_dirs = dirs;
+    dirs[n_dirs + 1] = (char *)NULL;
+    dirs[n_dirs] = malloc((unsigned)strlen(info_dir)+1);
+    strcpy(dirs[n_dirs], info_dir);
+    *code_ptr = 0;
+}
+
+void ss_delete_info_dir(sci_idx, info_dir, code_ptr)
+    int sci_idx;
+    char *info_dir;
+    int *code_ptr;
+{
+    register char **i_d;
+    register char **info_dirs;
+
+    info_dirs = ss_info(sci_idx)->info_dirs;
+    for (i_d = info_dirs; *i_d; i_d++) {
+       if (!strcmp(*i_d, info_dir)) {
+           while (*i_d) {
+               *i_d = *(i_d+1);
+               i_d++;
+           }
+           *code_ptr = 0;
+           return;
+       }
+    }
+    *code_ptr = SS_ET_NO_INFO_DIR;
+}
diff --git a/src/util/ss/invocation.c b/src/util/ss/invocation.c
new file mode 100644 (file)
index 0000000..678bbcd
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright 1987, 1988 by MIT Student Information Processing Board
+ *
+ * For copyright information, see copyright.h.
+ */
+#include "ss_internal.h"
+#include "copyright.h"
+#define        size    sizeof(ss_data *)
+
+#ifndef lint
+static char const rcsid[] =
+    "$Header$";
+#endif
+
+int ss_create_invocation(subsystem_name, version_string, info_ptr,
+                        request_table_ptr, code_ptr)
+       char *subsystem_name, *version_string;
+       char *info_ptr;
+       ss_request_table *request_table_ptr;
+       int *code_ptr;
+{
+       register int sci_idx;
+       register ss_data *new_table;
+       register ss_data **table;
+
+       *code_ptr = 0;
+       table = _ss_table;
+       new_table = (ss_data *) malloc(sizeof(ss_data));
+
+       if (table == (ss_data **) NULL) {
+               table = (ss_data **) malloc(2 * size);
+               table[0] = table[1] = (ss_data *)NULL;
+       }
+       initialize_ss_error_table ();
+
+       for (sci_idx = 1; table[sci_idx] != (ss_data *)NULL; sci_idx++)
+               ;
+       table = (ss_data **) realloc((char *)table,
+                                    ((unsigned)sci_idx+2)*size);
+       table[sci_idx+1] = (ss_data *) NULL;
+       table[sci_idx] = new_table;
+
+       new_table->subsystem_name = subsystem_name;
+       new_table->subsystem_version = version_string;
+       new_table->argv = (char **)NULL;
+       new_table->current_request = (char *)NULL;
+       new_table->info_dirs = (char **)malloc(sizeof(char *));
+       *new_table->info_dirs = (char *)NULL;
+       new_table->info_ptr = info_ptr;
+       new_table->prompt = malloc((unsigned)strlen(subsystem_name)+4);
+       strcpy(new_table->prompt, subsystem_name);
+       strcat(new_table->prompt, ":  ");
+#ifdef silly
+       new_table->abbrev_info = ss_abbrev_initialize("/etc/passwd", code_ptr);
+#else
+       new_table->abbrev_info = NULL;
+#endif
+       new_table->flags.escape_disabled = 0;
+       new_table->flags.abbrevs_disabled = 0;
+       new_table->rqt_tables =
+               (ss_request_table **) calloc(2, sizeof(ss_request_table *));
+       *(new_table->rqt_tables) = request_table_ptr;
+       *(new_table->rqt_tables+1) = (ss_request_table *) NULL;
+       _ss_table = table;
+       return(sci_idx);
+}
+
+void
+ss_delete_invocation(sci_idx)
+       int sci_idx;
+{
+       register ss_data *t;
+       int ignored_code;
+
+       t = ss_info(sci_idx);
+       free(t->prompt);
+       free((char *)t->rqt_tables);
+       while(t->info_dirs[0] != (char *)NULL)
+               ss_delete_info_dir(sci_idx, t->info_dirs[0], &ignored_code);
+       free((char *)t->info_dirs);
+       free((char *)t);
+}
diff --git a/src/util/ss/list_rqs.c b/src/util/ss/list_rqs.c
new file mode 100644 (file)
index 0000000..b8aee87
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright 1987, 1988 by MIT Student Information Processing Board
+ *
+ * For copyright information, see copyright.h.
+ */
+#include "copyright.h"
+#include "ss_internal.h"
+#include <signal.h>
+#include <setjmp.h>
+#include <sys/wait.h>
+
+#ifdef lint     /* "lint returns a value which is sometimes ignored" */
+#define DONT_USE(x)     x=x;
+#else /* !lint */
+#define DONT_USE(x)     ;
+#endif /* lint */
+
+static char const twentyfive_spaces[26] =
+    "                         ";
+static char const NL[2] = "\n";
+
+ss_list_requests(argc, argv, sci_idx, info_ptr)
+    int argc;
+    char **argv;
+    int sci_idx;
+    pointer info_ptr;
+{
+    register ss_request_entry *entry;
+    register char const * const *name;
+    register int spacing;
+    register ss_request_table **table;
+
+    char buffer[BUFSIZ];
+    FILE *output;
+    int fd;
+    int mask;
+    int (*func)();
+#ifndef WAIT_USES_INT
+    union wait waitb;
+#else
+    int waitb;
+#endif
+
+    DONT_USE(argc);
+    DONT_USE(argv);
+
+    mask = sigblock(sigmask(SIGINT));
+    func = signal(SIGINT, SIG_IGN);
+    fd = ss_pager_create();
+    output = fdopen(fd, "w");
+    sigsetmask(mask);
+
+    fprintf (output, "Available %s requests:\n\n",
+            ss_info (sci_idx) -> subsystem_name);
+
+    for (table = ss_info(sci_idx)->rqt_tables; *table; table++) {
+        entry = (*table)->requests;
+        for (; entry->command_names; entry++) {
+            spacing = -2;
+            buffer[0] = '\0';
+            if (entry->flags & SS_OPT_DONT_LIST)
+                continue;
+            for (name = entry->command_names; *name; name++) {
+                register int len = strlen(*name);
+                strncat(buffer, *name, len);
+                spacing += len + 2;
+                if (name[1]) {
+                    strcat(buffer, ", ");
+                }
+            }
+            if (spacing > 23) {
+                strcat(buffer, NL);
+                fputs(buffer, output);
+                spacing = 0;
+                buffer[0] = '\0';
+            }
+            strncat(buffer, twentyfive_spaces, 25-spacing);
+            strcat(buffer, entry->info_string);
+            strcat(buffer, NL);
+            fputs(buffer, output);
+        }
+    }
+    fclose(output);
+#ifndef NO_FORK
+    wait(&waitb);
+#endif
+    (void) signal(SIGINT, func);
+}
diff --git a/src/util/ss/listen.c b/src/util/ss/listen.c
new file mode 100644 (file)
index 0000000..a154953
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * Listener loop for subsystem library libss.a.
+ *
+ *     $Header$
+ *     $Locker$
+ * 
+ * Copyright 1987, 1988 by MIT Student Information Processing Board
+ *
+ * For copyright information, see copyright.h.
+ */
+
+#include "copyright.h"
+#include "ss_internal.h"
+#include <stdio.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <sys/param.h>
+#ifdef BSD
+#include <sgtty.h>
+#endif
+
+#ifndef        lint
+static char const rcs_id[] =
+    "$Header$";
+#endif
+
+static ss_data *current_info;
+static jmp_buf listen_jmpb;
+
+static int print_prompt()
+{
+#ifdef BSD
+    /* put input into a reasonable mode */
+    struct sgttyb ttyb;
+    if (ioctl(fileno(stdin), TIOCGETP, &ttyb) != -1) {
+       if (ttyb.sg_flags & (CBREAK|RAW)) {
+           ttyb.sg_flags &= ~(CBREAK|RAW);
+           (void) ioctl(0, TIOCSETP, &ttyb);
+       }
+    }
+#endif
+    (void) fputs(current_info->prompt, stdout);
+    (void) fflush(stdout);
+}
+
+static int listen_int_handler()
+{
+    putc('\n', stdout);
+    longjmp(listen_jmpb, 1);
+}
+
+int ss_listen (sci_idx)
+    int sci_idx;
+{
+    register char *cp;
+    register int (*sig_cont)();
+    register ss_data *info;
+    int (*sig_int)(), (*old_sig_cont)();
+    char input[BUFSIZ];
+    char buffer[BUFSIZ];
+    char *end = buffer;
+    int mask;
+    int code;
+    jmp_buf old_jmpb;
+    ss_data *old_info = current_info;
+    
+    current_info = info = ss_info(sci_idx);
+    sig_cont = (int (*)())0;
+    info->abort = 0;
+    mask = sigblock(sigmask(SIGINT));
+    memcpy(old_jmpb, listen_jmpb, sizeof(jmp_buf));
+    sig_int = signal(SIGINT, listen_int_handler);
+    setjmp(listen_jmpb);
+    (void) sigsetmask(mask);
+    while(!info->abort) {
+       print_prompt();
+       *end = '\0';
+       old_sig_cont = sig_cont;
+       sig_cont = signal(SIGCONT, print_prompt);
+       if (sig_cont == print_prompt)
+           sig_cont = old_sig_cont;
+       if (fgets(input, BUFSIZ, stdin) != input) {
+           code = SS_ET_EOF;
+           goto egress;
+       }
+       cp = strchr(input, '\n');
+       if (cp) {
+           *cp = '\0';
+           if (cp == input)
+               continue;
+       }
+       (void) signal(SIGCONT, sig_cont);
+       for (end = input; *end; end++)
+           ;
+
+       code = ss_execute_line (sci_idx, input);
+       if (code == SS_ET_COMMAND_NOT_FOUND) {
+           register char *c = input;
+           while (*c == ' ' || *c == '\t')
+               c++;
+           cp = strchr (c, ' ');
+           if (cp)
+               *cp = '\0';
+           cp = strchr (c, '\t');
+           if (cp)
+               *cp = '\0';
+           ss_error (sci_idx, 0,
+                   "Unknown request \"%s\".  Type \"?\" for a request list.",
+                      c);
+       }
+    }
+    code = 0;
+egress:
+    (void) signal(SIGINT, sig_int);
+    memcpy(listen_jmpb, old_jmpb, sizeof(jmp_buf));
+    current_info = old_info;
+    return code;
+}
+
+void ss_abort_subsystem(sci_idx, code)
+    int sci_idx;
+    int code;
+{
+    ss_info(sci_idx)->abort = 1;
+    ss_info(sci_idx)->exit_status = code;
+    
+}
+
+int ss_quit(argc, argv, sci_idx, infop)
+    int argc;
+    char **argv;
+    int sci_idx;
+    pointer infop;
+{
+    ss_abort_subsystem(sci_idx, 0);
+}
diff --git a/src/util/ss/mit-sipb-copyright.h b/src/util/ss/mit-sipb-copyright.h
new file mode 100644 (file)
index 0000000..ffcfc38
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+
+Copyright 1987 by the Student Information Processing Board
+       of the Massachusetts Institute of Technology
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is
+hereby granted, provided that the above copyright notice
+appear in all copies and that both that copyright notice and
+this permission notice appear in supporting documentation,
+and that the names of M.I.T. and the M.I.T. S.I.P.B. not be
+used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+M.I.T. and the M.I.T. S.I.P.B. make no representations about
+the suitability of this software for any purpose.  It is
+provided "as is" without express or implied warranty.
+
+*/
+
diff --git a/src/util/ss/mk_cmds.c b/src/util/ss/mk_cmds.c
new file mode 100644 (file)
index 0000000..af04ec0
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * make_commands.c
+ *
+ * $Header$
+ * $Locker$
+ *
+ * Copyright 1987, 1988 by MIT Student Information Processing Board
+ *
+ * For copyright information, see copyright.h.
+ */
+
+#include "copyright.h"
+#include <stdio.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <string.h>
+#include "ss_internal.h"
+
+static const char copyright[] =
+    "Copyright 1987 by MIT Student Information Processing Board";
+
+extern pointer malloc PROTOTYPE((unsigned));
+extern char *last_token;
+extern FILE *output_file;
+
+extern FILE *yyin, *yyout;
+extern int yylineno;
+
+int main(argc, argv)
+    int argc;
+    char **argv;
+{
+    char c_file[MAXPATHLEN];
+    int result;
+    char *path, *p;
+
+    if (argc != 2) {
+       fputs("Usage: ", stderr);
+       fputs(argv[0], stderr);
+       fputs("cmdtbl.ct\n", stderr);
+       exit(1);
+    }
+
+    path = malloc(strlen(argv[1])+4); /* extra space to add ".ct" */
+    strcpy(path, argv[1]);
+    p = strrchr(path, '/');
+    if (p == (char *)NULL)
+       p = path;
+    else
+       p++;
+    p = strrchr(p, '.');
+    if (p == (char *)NULL || strcmp(p, ".ct"))
+       strcat(path, ".ct");
+    yyin = fopen(path, "r");
+    if (!yyin) {
+       perror(path);
+       exit(1);
+    }
+
+    p = strrchr(path, '.');
+    *p = '\0';
+    strcpy(c_file, path);
+    strcat(c_file, ".c");
+    *p = '.';
+
+    output_file = fopen(c_file, "w+");
+    if (!output_file) {
+       perror(c_file);
+       exit(1);
+    }
+
+    fputs("/* ", output_file);
+    fputs(c_file, output_file);
+    fputs(" - automatically generated from ", output_file);
+    fputs(path, output_file);
+    fputs(" */\n", output_file);
+    fputs("#include <ss/ss.h>\n\n", output_file);
+    fputs("#ifndef __STDC__\n#define const\n#endif\n\n", output_file);
+    /* parse it */
+    result = yyparse();
+    /* put file descriptors back where they belong */
+    fclose(yyin);              /* bye bye input file */
+    fclose(output_file);       /* bye bye output file */
+
+    return result;
+}
+
+yyerror(s)
+    char *s;
+{
+    fputs(s, stderr);
+#ifdef NO_YYLINENO
+    fprintf(stderr, "\nLast token was '%s'\n", last_token);
+#else
+    fprintf(stderr, "\nLine %d; last token was '%s'\n",
+           yylineno, last_token);
+#endif
+}
diff --git a/src/util/ss/mk_cmds.sh b/src/util/ss/mk_cmds.sh
new file mode 100644 (file)
index 0000000..eda3888
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/sh
+#
+#
+
+DIR=@DIR@
+AWK=@AWK@
+SED=@SED@
+
+FILE=$1
+ROOT=`echo $1 | sed -e s/.ct$//`
+BASE=`basename $ROOT`
+TMP=ct$$.c
+
+${SED} -f ${DIR}/ct_c.sed  ${FILE} \
+       | ${AWK} -f ${DIR}/ct_c.awk rootname=${ROOT} outfile=${TMP} -
+
+if grep "^#__ERROR_IN_FILE" ${TMP} > /dev/null; then
+       rm ${TMP}
+       exit 1
+else
+       mv ${TMP} ${BASE}.c
+       exit 0
+fi
diff --git a/src/util/ss/options.c b/src/util/ss/options.c
new file mode 100644 (file)
index 0000000..dd648b0
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright 1987, 1988 by MIT Student Information Processing Board
+ *
+ * For copyright information, see copyright.h.
+ */
+#include "copyright.h"
+#include <stdio.h>
+#include "ss.h"
+
+struct option {
+     char *text;
+     long value;
+};
+
+static struct option options[] = {
+     { "dont_list", SS_OPT_DONT_LIST },
+     { "^list", SS_OPT_DONT_LIST },
+     { "dont_summarize", SS_OPT_DONT_SUMMARIZE },
+     { "^summarize", SS_OPT_DONT_SUMMARIZE },
+     { (char *)NULL, 0 }
+};
+
+long
+flag_val(string)
+     register char *string;
+{
+     register struct option *opt;
+     for (opt = options; opt->text; opt++)
+         if (!strcmp(opt->text, string))
+              return(opt->value);
+     return(0);
+}
diff --git a/src/util/ss/pager.c b/src/util/ss/pager.c
new file mode 100644 (file)
index 0000000..b951fa6
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Pager: Routines to create a "more" running out of a particular file
+ * descriptor.
+ *
+ * Copyright 1987, 1988 by MIT Student Information Processing Board
+ *
+ * For copyright information, see copyright.h.
+ */
+
+#include "ss_internal.h"
+#include "copyright.h"
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <signal.h>
+
+static char MORE[] = "more";
+extern char *_ss_pager_name;
+extern char *getenv();
+extern int errno;
+
+/*
+ * this needs a *lot* of work....
+ *
+ * run in same process
+ * handle SIGINT sensibly
+ * allow finer control -- put-page-break-here
+ */
+void ss_page_stdin();
+
+#ifndef NO_FORK
+int ss_pager_create() 
+{
+       int filedes[2];
+     
+       if (pipe(filedes) != 0)
+               return(-1);
+
+       switch(fork()) {
+       case -1:
+               return(-1);
+       case 0:
+               /*
+                * Child; dup read half to 0, close all but 0, 1, and 2
+                */
+               if (dup2(filedes[0], 0) == -1)
+                       exit(1);
+               ss_page_stdin();
+       default:
+               /*
+                * Parent:  close "read" side of pipe, return
+                * "write" side.
+                */
+               (void) close(filedes[0]);
+               return(filedes[1]);
+       }
+}
+#else /* don't fork */
+int ss_pager_create()
+{
+    int fd;
+    fd = open("/dev/tty", O_WRONLY, 0);
+    return fd;
+}
+#endif
+
+void ss_page_stdin()
+{
+       int i;
+       for (i = 3; i < 32; i++)
+               (void) close(i);
+       (void) signal(SIGINT, SIG_DFL);
+       {
+               int mask = sigblock(0);
+               mask &= ~sigmask(SIGINT);
+               sigsetmask(mask);
+       }
+       if (_ss_pager_name == (char *)NULL) {
+               if ((_ss_pager_name = getenv("PAGER")) == (char *)NULL)
+                       _ss_pager_name = MORE;
+       }
+       (void) execlp(_ss_pager_name, _ss_pager_name, (char *) NULL);
+       {
+               /* minimal recovery if pager program isn't found */
+               char buf[80];
+               register int n;
+               while ((n = read(0, buf, 80)) > 0)
+                       write(1, buf, n);
+       }
+       exit(errno);
+}
diff --git a/src/util/ss/parse.c b/src/util/ss/parse.c
new file mode 100644 (file)
index 0000000..e1dec31
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Copyright 1987, 1988 by MIT Student Information Processing Board
+ *
+ * For copyright info, see copyright.h.
+ */
+
+#include "ss_internal.h"
+#include "copyright.h"
+
+#ifndef lint
+static char const rcsid[] =
+    "$Header$";
+#endif
+
+enum parse_mode { WHITESPACE, TOKEN, QUOTED_STRING };
+
+/*
+ * parse(line_ptr, argc_ptr)
+ *
+ * Function:
+ *      Parses line, dividing at whitespace, into tokens, returns
+ *      the "argc" and "argv" values.
+ * Arguments:
+ *      line_ptr (char *)
+ *              Pointer to text string to be parsed.
+ *      argc_ptr (int *)
+ *              Where to put the "argc" (number of tokens) value.
+ * Returns:
+ *      argv (char **)
+ *              Series of pointers to parsed tokens.
+ */
+
+#define NEW_ARGV(old,n) (char **)realloc((char *)old,\
+                                        (unsigned)(n+2)*sizeof(char*))
+
+char **ss_parse (sci_idx, line_ptr, argc_ptr)
+    int sci_idx;
+    register char *line_ptr;
+    int *argc_ptr;
+{
+    register char **argv, *cp;
+    register int argc;
+    register enum parse_mode parse_mode;
+
+    argv = (char **) malloc (sizeof(char *));
+    if (argv == (char **)NULL) {
+       ss_error(sci_idx, errno, "Can't allocate storage");
+       *argc_ptr = 0;
+       return(argv);
+    }
+    *argv = (char *)NULL;
+
+    argc = 0;
+
+    parse_mode = WHITESPACE;   /* flushing whitespace */
+    cp = line_ptr;             /* cp is for output */
+    while (1) {
+#ifdef DEBUG
+       {
+           printf ("character `%c', mode %d\n", *line_ptr, parse_mode);
+       }
+#endif
+       while (parse_mode == WHITESPACE) {
+           if (*line_ptr == '\0')
+               goto end_of_line;
+           if (*line_ptr == ' ' || *line_ptr == '\t') {
+               line_ptr++;
+               continue;
+           }
+           if (*line_ptr == '"') {
+               /* go to quoted-string mode */
+               parse_mode = QUOTED_STRING;
+               cp = line_ptr++;
+               argv = NEW_ARGV (argv, argc);
+               argv[argc++] = cp;
+               argv[argc] = NULL;
+           }
+           else {
+               /* random-token mode */
+               parse_mode = TOKEN;
+               cp = line_ptr;
+               argv = NEW_ARGV (argv, argc);
+               argv[argc++] = line_ptr;
+               argv[argc] = NULL;
+           }
+       }
+       while (parse_mode == TOKEN) {
+           if (*line_ptr == '\0') {
+               *cp++ = '\0';
+               goto end_of_line;
+           }
+           else if (*line_ptr == ' ' || *line_ptr == '\t') {
+               *cp++ = '\0';
+               line_ptr++;
+               parse_mode = WHITESPACE;
+           }
+           else if (*line_ptr == '"') {
+               line_ptr++;
+               parse_mode = QUOTED_STRING;
+           }
+           else {
+               *cp++ = *line_ptr++;
+           }
+       }
+       while (parse_mode == QUOTED_STRING) {
+           if (*line_ptr == '\0') {
+               ss_error (sci_idx, 0,
+                         "Unbalanced quotes in command line");
+               free (argv);
+               *argc_ptr = 0;
+               return NULL;
+           }
+           else if (*line_ptr == '"') {
+               if (*++line_ptr == '"') {
+                   *cp++ = '"';
+                   line_ptr++;
+               }
+               else {
+                   parse_mode = TOKEN;
+               }
+           }
+           else {
+               *cp++ = *line_ptr++;
+           }
+       }
+    }
+end_of_line:
+    *argc_ptr = argc;
+#ifdef DEBUG
+    {
+       int i;
+       printf ("argc = %d\n", argc);
+       for (i = 0; i <= argc; i++)
+           printf ("\targv[%2d] = `%s'\n", i,
+                   argv[i] ? argv[i] : "<NULL>");
+    }
+#endif
+    return(argv);
+}
diff --git a/src/util/ss/prompt.c b/src/util/ss/prompt.c
new file mode 100644 (file)
index 0000000..bc95d82
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * prompt.c: Routines for retrieving and setting a prompt.
+ *
+ * $Header$
+ * $Locker$
+ *
+ * Copyright 1987, 1988 by MIT Student Information Processing Board
+ *
+ * For copyright information, see copyright.h.
+ */
+
+#include "copyright.h"
+#include <stdio.h>
+#include "ss_internal.h"
+
+static const char rcsid[] =
+    "$Header$";
+
+ss_set_prompt(sci_idx, new_prompt)
+     int sci_idx;
+     char *new_prompt;
+{
+     ss_info(sci_idx)->prompt = new_prompt;
+}
+
+char *
+ss_get_prompt(sci_idx)
+     int sci_idx;
+{
+     return(ss_info(sci_idx)->prompt);
+}
diff --git a/src/util/ss/request_tbl.c b/src/util/ss/request_tbl.c
new file mode 100644 (file)
index 0000000..8dcb583
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright 1987, 1988 by MIT Student Information Processing Board
+ *
+ * For copyright information, see copyright.h.
+ */
+
+#include "copyright.h"
+#include "ss_internal.h"
+
+#define ssrt ss_request_table  /* for some readable code... */
+
+ss_add_request_table(sci_idx, rqtbl_ptr, position, code_ptr)
+       int sci_idx;
+       ssrt *rqtbl_ptr;
+       int position;           /* 1 -> becomes second... */
+       int *code_ptr;
+{
+       register ss_data *info;
+       register int i, size;
+
+       info = ss_info(sci_idx);
+       for (size=0; info->rqt_tables[size] != (ssrt *)NULL; size++)
+               ;
+       /* size == C subscript of NULL == #elements */
+       size += 2;              /* new element, and NULL */
+       info->rqt_tables = (ssrt **)realloc((char *)info->rqt_tables,
+                                           (unsigned)size*sizeof(ssrt));
+       if (info->rqt_tables == (ssrt **)NULL) {
+               *code_ptr = errno;
+               return;
+       }
+       if (position > size - 2)
+               position = size - 2;
+
+       if (size > 1)
+               for (i = size - 2; i >= position; i--)
+                       info->rqt_tables[i+1] = info->rqt_tables[i];
+
+       info->rqt_tables[position] = rqtbl_ptr;
+       info->rqt_tables[size-1] = (ssrt *)NULL;
+       *code_ptr = 0;
+}
+
+ss_delete_request_table(sci_idx, rqtbl_ptr, code_ptr)
+     int sci_idx;
+     ssrt *rqtbl_ptr;
+     int *code_ptr;
+{
+     register ss_data *info;
+     register ssrt **rt1, **rt2;
+
+     *code_ptr = SS_ET_TABLE_NOT_FOUND;
+     info = ss_info(sci_idx);
+     rt1 = info->rqt_tables;
+     for (rt2 = rt1; *rt1; rt1++) {
+         if (*rt1 != rqtbl_ptr) {
+              *rt2++ = *rt1;
+              *code_ptr = 0;
+         }
+     }
+     *rt2 = (ssrt *)NULL;
+     return;
+}
diff --git a/src/util/ss/requests.c b/src/util/ss/requests.c
new file mode 100644 (file)
index 0000000..bfe69f1
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Various minor routines...
+ *
+ * Copyright 1987, 1988, 1989 by MIT
+ *
+ * For copyright information, see mit-sipb-copyright.h.
+ */
+
+#include "mit-sipb-copyright.h"
+#include <stdio.h>
+#include "ss_internal.h"
+
+#define        DECLARE(name)   name(argc,argv,sci_idx)int argc,sci_idx;char **argv;
+
+/*
+ * ss_self_identify -- assigned by default to the "." request
+ */
+DECLARE(ss_self_identify)
+{
+     register ss_data *info = ss_info(sci_idx);
+     printf("%s version %s\n", info->subsystem_name,
+           info->subsystem_version);
+}
+
+/*
+ * ss_subsystem_name -- print name of subsystem
+ */
+DECLARE(ss_subsystem_name)
+{
+     printf("%s\n", ss_info(sci_idx)->subsystem_name);
+}
+
+/*
+ * ss_subsystem_version -- print version of subsystem
+ */
+DECLARE(ss_subsystem_version)
+{
+     printf("%s\n", ss_info(sci_idx)->subsystem_version);
+}
+
+/*
+ * ss_unimplemented -- routine not implemented (should be
+ * set up as (dont_list,dont_summarize))
+ */
+DECLARE(ss_unimplemented)
+{
+     ss_perror(sci_idx, SS_ET_UNIMPLEMENTED, "");
+}
diff --git a/src/util/ss/ss.h b/src/util/ss/ss.h
new file mode 100644 (file)
index 0000000..ca3197d
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright 1987, 1988 by MIT Student Information Processing Board
+ *
+ * For copyright information, see mit-sipb-copyright.h.
+ */
+
+#ifndef _ss_h
+#define _ss_h __FILE__
+
+#include <ss/mit-sipb-copyright.h>
+#include <ss/ss_err.h>
+
+extern int errno;
+
+#ifdef __STDC__
+#define __SS_CONST const
+#define __SS_PROTO (int, const char * const *, int, void *)
+#else
+#define __SS_CONST
+#define __SS_PROTO ()
+#endif
+
+typedef __SS_CONST struct _ss_request_entry {
+    __SS_CONST char * __SS_CONST *command_names; /* whatever */
+    void (* __SS_CONST function) __SS_PROTO; /* foo */
+    __SS_CONST char * __SS_CONST info_string;  /* NULL */
+    int flags;                 /* 0 */
+} ss_request_entry;
+
+typedef __SS_CONST struct _ss_request_table {
+    int version;
+    ss_request_entry *requests;
+} ss_request_table;
+
+#define SS_RQT_TBL_V2  2
+
+typedef struct _ss_rp_options {        /* DEFAULT VALUES */
+    int version;               /* SS_RP_V1 */
+    void (*unknown) __SS_PROTO;        /* call for unknown command */
+    int allow_suspend;
+    int catch_int;
+} ss_rp_options;
+
+#define SS_RP_V1 1
+
+#define SS_OPT_DONT_LIST       0x0001
+#define SS_OPT_DONT_SUMMARIZE  0x0002
+
+void ss_help __SS_PROTO;
+char *ss_current_request();
+char *ss_name();
+#ifdef __STDC__
+void ss_error (int, long, char const *, ...);
+void ss_perror (int, long, char const *);
+#else
+void ss_error ();
+void ss_perror ();
+#endif
+void ss_abort_subsystem();
+extern ss_request_table ss_std_requests;
+#endif /* _ss_h */
diff --git a/src/util/ss/ss_err.et b/src/util/ss/ss_err.et
new file mode 100644 (file)
index 0000000..80e9dfa
--- /dev/null
@@ -0,0 +1,39 @@
+       error_table ss
+
+ec     SS_ET_SUBSYSTEM_ABORTED,
+       "Subsystem aborted"
+
+ec     SS_ET_VERSION_MISMATCH,
+       "Version mismatch"
+
+ec     SS_ET_NULL_INV,
+       "No current invocation"
+
+ec     SS_ET_NO_INFO_DIR,
+       "No info directory"
+
+ec     SS_ET_COMMAND_NOT_FOUND,
+       "Command not found"
+
+ec     SS_ET_LINE_ABORTED,
+       "Command line aborted"
+
+ec     SS_ET_EOF,
+       "End-of-file reached"
+
+ec     SS_ET_PERMISSION_DENIED,
+       "Permission denied"
+
+ec     SS_ET_TABLE_NOT_FOUND,
+       "Request table not found"
+
+ec     SS_ET_NO_HELP_FILE,
+       "No info available"
+
+ec     SS_ET_ESCAPE_DISABLED,
+       "Shell escapes are disabled"
+
+ec     SS_ET_UNIMPLEMENTED,
+       "Sorry, this request is not yet implemented"
+
+       end
diff --git a/src/util/ss/ss_internal.h b/src/util/ss/ss_internal.h
new file mode 100644 (file)
index 0000000..4b9ea23
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright 1987, 1988 by MIT Student Information Processing Board
+ *
+ * For copyright information, see copyright.h.
+ */
+
+#ifndef _ss_ss_internal_h
+#define _ss_ss_internal_h __FILE__
+#include <stdio.h>
+#include <string.h>
+
+#ifdef __STDC__
+
+#define PROTOTYPE(p) p
+typedef void * pointer;
+
+#else
+
+#define const
+#define volatile
+#define PROTOTYPE(p) ()
+typedef char * pointer;
+
+#endif /* not __STDC__ */
+
+#include "ss.h"
+
+#if defined(__GNUC__)
+#define LOCAL_ALLOC(x) __builtin_alloca(x)
+#define LOCAL_FREE(x)
+#else
+#if defined(vax)
+#define LOCAL_ALLOC(x) alloca(x)
+#define LOCAL_FREE(x)
+extern pointer alloca PROTOTYPE((unsigned));
+#else
+#if defined(__HIGHC__) /* Barf! */
+pragma on(alloca);
+#define LOCAL_ALLOC(x) alloca(x)
+#define LOCAL_FREE(x)
+extern pointer alloca PROTOTYPE((unsigned));
+#else
+/* no alloca? */
+#define LOCAL_ALLOC(x) malloc(x)
+#define LOCAL_FREE(x) free(x)
+#endif
+#endif
+#endif                         /* LOCAL_ALLOC stuff */
+
+typedef char BOOL;
+
+typedef struct _ss_abbrev_entry {
+    char *name;                        /* abbrev name */
+    char **abbrev;             /* new tokens to insert */
+    unsigned int beginning_of_line : 1;
+} ss_abbrev_entry;
+
+typedef struct _ss_abbrev_list {
+    int n_abbrevs;
+    ss_abbrev_entry *first_abbrev;
+} ss_abbrev_list;
+
+typedef struct {
+/*    char *path; */
+    ss_abbrev_list abbrevs[127];
+} ss_abbrev_info;
+
+typedef struct _ss_data {      /* init values */
+    /* this subsystem */
+    char *subsystem_name;
+    char *subsystem_version;
+    /* current request info */
+    int argc;
+    char **argv;               /* arg list */
+    char const *current_request; /* primary name */
+    /* info directory for 'help' */
+    char **info_dirs;
+    /* to be extracted by subroutines */
+    pointer info_ptr;          /* (void *) NULL */
+    /* for ss_listen processing */
+    char *prompt;
+    ss_request_table **rqt_tables;
+    ss_abbrev_info *abbrev_info;
+    struct {
+       unsigned int  escape_disabled : 1,
+                     abbrevs_disabled : 1;
+    } flags;
+    /* to get out */
+    int abort;                 /* exit subsystem */
+    int exit_status;
+} ss_data;
+
+#define CURRENT_SS_VERSION 1
+
+#define        ss_info(sci_idx)        (_ss_table[sci_idx])
+#define        ss_current_request(sci_idx,code_ptr)    \
+     (*code_ptr=0,ss_info(sci_idx)->current_request)
+void ss_unknown_function();
+void ss_delete_info_dir();
+int ss_execute_line();
+char **ss_parse();
+ss_abbrev_info *ss_abbrev_initialize PROTOTYPE((char *, int *));
+void ss_page_stdin();
+
+extern ss_data **_ss_table;
+extern char *ss_et_msgs[];
+
+extern pointer malloc PROTOTYPE((unsigned));
+extern pointer realloc PROTOTYPE((pointer, unsigned));
+extern pointer calloc PROTOTYPE((unsigned, unsigned));
+
+#ifdef USE_SIGPROCMASK
+/* fake sigmask, sigblock, sigsetmask */
+#include <signal.h>
+#define sigmask(x) (1L<<(x)-1)
+#define sigsetmask(x) sigprocmask(SIG_SETMASK,&x,NULL)
+static int _fake_sigstore;
+#define sigblock(x) (_fake_sigstore=x,sigprocmask(SIG_BLOCK,&_fake_sigstore,0))
+#endif
+#endif /* _ss_internal_h */
diff --git a/src/util/ss/std_rqs.ct b/src/util/ss/std_rqs.ct
new file mode 100644 (file)
index 0000000..500288a
--- /dev/null
@@ -0,0 +1,46 @@
+       command_table   ss_std_requests;
+
+       request ss_self_identify, "Identify the subsystem.",
+               ".",
+               (dont_list, dont_summarize);
+
+       request ss_help, "Display info on command or topic.",
+               help;
+
+       unimplemented
+               ss_list_help,
+               "List topics for which help is available.",
+               list_help, lh;
+
+       request ss_list_requests, "List available commands.",
+               list_requests, lr, "?";
+
+       request ss_quit, "Leave the subsystem.",
+               quit, q;
+
+       unimplemented
+               ss_abbrev,
+               "Enable/disable abbreviation processing of request lines.",
+               abbrev, ab;
+
+       unimplemented
+               ss_execute,
+               "Execute a UNIX command line.",
+               execute, e;
+
+       unimplemented
+               ss_summarize_requests,
+               "Produce a list of the most commonly used requests.",
+               "?";
+               
+       request ss_subsystem_name,
+               "Return the name of this subsystem.",
+               subsystem_name,
+               (dont_list);
+
+       request ss_subsystem_version,
+               "Return the version of this subsystem.",
+               subsystem_version,
+               (dont_list);
+
+       end;
diff --git a/src/util/ss/test_ss.c b/src/util/ss/test_ss.c
new file mode 100644 (file)
index 0000000..a7c7c5d
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ *------------------------------------------------------------------
+ *
+ * $Source$
+ * $Revision$
+ * $Date$
+ * $State$
+ * $Author$
+ * $Locker$
+ *
+ * $Log$
+ * Revision 1.1  1993/06/03 12:31:25  tytso
+ * Initial revision
+ *
+ * Revision 1.1  1991/12/21  16:41:47  eichin
+ * Initial revision
+ *
+ * Revision 1.1  1991/12/21  11:13:39  eichin
+ * Initial revision
+ *
+ * Revision 1.2  89/01/25  07:52:27  raeburn
+ * *** empty log message ***
+ * 
+ * Revision 1.1  88/01/23  15:50:26  raeburn
+ * Initial revision
+ *
+ *
+ *------------------------------------------------------------------
+ */
+
+#ifndef lint
+static char const rcsid_test_c[] =
+    "$Header$";
+#endif /* lint */
+
+#include <stdio.h>
+#include "ss.h"
+
+extern ss_request_table test_cmds;
+
+#define TRUE 1
+#define FALSE 0
+
+static char def_subsystem_name[5] = "test";
+static char version [4] = "1.0";
+extern void ss_listen();
+
+int main(argc, argv)
+    int argc;
+    char **argv;
+{
+    int code;
+    char *argv0 = argv[0];
+    char *initial_request = (char *)NULL;
+    int quit = FALSE;  /* quit after processing request */
+    int sci_idx;
+    char *subsystem_name;
+
+    subsystem_name = def_subsystem_name;
+
+    for (; *argv; ++argv, --argc) {
+       printf("checking arg: %s\n", *argv);
+       if (!strcmp(*argv, "-prompt")) {
+           if (argc == 1) {
+               fprintf(stderr,
+                       "No argument supplied with -prompt\n");
+               exit(1);
+           }
+           argc--; argv++;
+           subsystem_name = *argv;
+       }
+       else if (!strcmp(*argv, "-request") || !strcmp(*argv, "-rq")) {
+           if (argc == 1) {
+               fprintf(stderr,
+                       "No string supplied with -request.\n");
+               exit(1);
+           }
+           argc--; argv++;
+           initial_request = *argv;
+       }
+       else if (!strcmp(*argv, "-quit"))
+           quit = TRUE;
+       else if (!strcmp(*argv, "-no_quit"))
+           quit = FALSE;
+       else if (**argv == '-') {
+           fprintf(stderr, "Unknown control argument %s\n",
+                   *argv);
+           fprintf(stderr,
+       "Usage: %s [gateway] [ -prompt name ] [ -request name ] [ -quit ]\n",
+                   argv0);
+           exit(1);
+       }
+    }
+
+    sci_idx = ss_create_invocation(subsystem_name, version,
+                                  (char *)NULL, &test_cmds, &code);
+    if (code) {
+       ss_perror(sci_idx, code, "creating invocation");
+       exit(1);
+    }
+
+    (void) ss_add_request_table (sci_idx, &ss_std_requests, 1, &code);
+    if (code) {
+       ss_perror (sci_idx, code, "adding standard requests");
+       exit (1);
+    }
+
+    if (!quit)
+       printf("test version %s.  Type '?' for a list of commands.\n\n",
+              version);
+
+    if (initial_request != (char *)NULL) {
+       code = ss_execute_line(sci_idx, initial_request);
+       if (code != 0)
+           ss_perror(sci_idx, code, initial_request);
+    }
+    if (!quit || code)
+       (void) ss_listen (sci_idx, &code);
+    exit(0);
+}
+
+
+void test_cmd (argc, argv)
+    int argc;
+    char **argv;
+{
+    while (++argv, --argc)
+       fputs(*argv, stdout);
+    putchar ('\n');
+}
diff --git a/src/util/ss/utils.c b/src/util/ss/utils.c
new file mode 100644 (file)
index 0000000..3520601
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Copyright 1987, 1988 by MIT Student Information Processing Board
+ *
+ * For copyright information, see copyright.h.
+ */
+
+#include <string.h>
+#include "copyright.h"
+#include "ss_internal.h"       /* includes stdio and string */
+
+extern FILE *output_file;
+
+char *gensym(), *str_concat3(), *quote();
+extern long gensym_n;
+
+void write_ct(hdr, rql)
+    char const *hdr, *rql;
+{
+    char *sym;
+    sym = gensym("ssu");
+    fputs("static ss_request_entry ", output_file);
+    fputs(sym, output_file);
+    fputs("[] = {\n", output_file);
+    fputs(rql, output_file);
+    fputs("    { 0, 0, 0, 0 }\n", output_file);
+    fputs("};\n\nss_request_table ", output_file);
+    fputs(hdr, output_file);
+    fprintf(output_file, " = { %d, ", SS_RQT_TBL_V2);
+    fputs(sym, output_file);
+    fputs(" };\n", output_file);
+}
+
+char * generate_cmds_string(cmds)
+    char const *cmds;
+{
+    char * var_name = gensym("ssu");
+    fputs("static char const * const ", output_file);
+    fputs(var_name, output_file);
+    fputs("[] = {\n", output_file);
+    fputs(cmds, output_file);
+    fputs(",\n    (char const *)0\n};\n", output_file);
+    return(var_name);
+}
+
+void generate_function_definition(func)
+    char const *func;
+{
+    fputs("extern void ", output_file);
+    fputs(func, output_file);
+    fputs(" __SS_PROTO;\n", output_file);
+}
+
+char * generate_rqte(func_name, info_string, cmds, options)
+    char const *func_name;
+    char const *info_string;
+    char const *cmds;
+    int options;
+{
+    int size;
+    char *string, *var_name, numbuf[16];
+    var_name = generate_cmds_string(cmds);
+    generate_function_definition(func_name);
+    size = 6;          /* "    { " */
+    size += strlen(var_name)+7; /* "quux, " */
+    size += strlen(func_name)+7; /* "foo, " */
+    size += strlen(info_string)+9; /* "\"Info!\", " */
+    sprintf(numbuf, "%d", options);
+    size += strlen(numbuf);
+    size += 4;         /* " }," + NL */
+    string = malloc(size * sizeof(char *));
+    strcpy(string, "    { ");
+    strcat(string, var_name);
+    strcat(string, ",\n      ");
+    strcat(string, func_name);
+    strcat(string, ",\n      ");
+    strcat(string, info_string);
+    strcat(string, ",\n      ");
+    strcat(string, numbuf);
+    strcat(string, " },\n");
+    return(string);
+}
+
+char *
+gensym(name)
+       char *name;
+{
+       char *symbol;
+
+       symbol = malloc((strlen(name)+6) * sizeof(char));
+       gensym_n++;
+       sprintf(symbol, "%s%05ld", name, gensym_n);
+       return(symbol);
+}
+
+/* concatenate three strings and return the result */
+char *str_concat3(a, b, c)
+       register char *a, *b, *c;
+{
+       char *result;
+       int size_a = strlen(a);
+       int size_b = strlen(b);
+       int size_c = strlen(c);
+
+       result = malloc((size_a + size_b + size_c + 2)*sizeof(char));
+       strcpy(result, a);
+       strcpy(&result[size_a], c);
+       strcpy(&result[size_a+size_c], b);
+       return(result);
+}
+
+/* return copy of string enclosed in double-quotes */
+char *quote(string)
+       register char *string;
+{
+       register char *result;
+       int len;
+       len = strlen(string)+1;
+       result = malloc(len+2);
+       result[0] = '"';
+       strncpy(&result[1], string, len-1);
+       result[len] = '"';
+       result[len+1] = '\0';
+       return(result);
+}
+
+#ifndef HAS_STRDUP
+/* make duplicate of string and return pointer */
+char *strdup(s)
+       register char *s;
+{
+       register int len = strlen(s) + 1;
+       register char *new;
+       new = malloc(len);
+       strncpy(new, s, len);
+       return(new);
+}
+#endif
diff --git a/src/util/unifdef/Imakefile b/src/util/unifdef/Imakefile
new file mode 100644 (file)
index 0000000..0e31227
--- /dev/null
@@ -0,0 +1,34 @@
+#      $Source$
+#      $Author$
+#      $Id$
+#
+#  Copyright 1990 by the Massachusetts Institute of Technology.
+#  All Rights Reserved.
+# 
+# Export of this software from the United States of America is assumed
+#   to require a specific license from the United States Government.
+#   It is the responsibility of any person or organization contemplating
+#   export to obtain such a license before exporting.
+# 
+# WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+# distribute this software and its documentation for any purpose and
+# without fee is hereby granted, provided that the above copyright
+# notice appear in all copies and that both that copyright notice and
+# this permission notice appear in supporting documentation, and that
+# the name of M.I.T. not be used in advertising or publicity pertaining
+# to distribution of the software without specific, written prior
+# permission.  M.I.T. makes no representations about the suitability of
+# this software for any purpose.  It is provided "as is" without express
+# or implied warranty.
+# 
+# 
+
+SimpleTestProgramTarget(unifdef)
+
+DependTarget()
+
+#
+# We need to actually build the program during the includes stage.
+#
+
+includes:: unifdef
diff --git a/src/util/unifdef/Makefile b/src/util/unifdef/Makefile
new file mode 100644 (file)
index 0000000..0d766bb
--- /dev/null
@@ -0,0 +1,5 @@
+#      @(#)Makefile    5.3 (Berkeley) 5/11/90
+
+PROG=  unifdef
+
+.include <bsd.prog.mk>
diff --git a/src/util/unifdef/unifdef.1 b/src/util/unifdef/unifdef.1
new file mode 100644 (file)
index 0000000..90e043a
--- /dev/null
@@ -0,0 +1,166 @@
+.\" Copyright (c) 1985, 1991 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Dave Yost.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"    This product includes software developed by the University of
+.\"    California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"     @(#)unifdef.1  6.5 (Berkeley) 4/23/91
+.\"
+.Dd April 23, 1991
+.Dt UNIFDEF 1
+.Os BSD 4.3
+.Sh NAME
+.Nm unifdef
+.Nd remove ifdef'ed lines
+.Sh SYNOPSIS
+.Nm unifdef
+.Oo
+.Fl t l c
+.Fl D Ns Ar sym
+.Fl U Ns Ar sym
+.Fl iD Ns Ar sym
+.Fl iD Ns Ar sym
+.Oc
+.Ar ...
+.Op Ar file
+.Sh DESCRIPTION
+.Nm Unifdef
+is useful for removing ifdef'ed lines
+from a file while otherwise leaving the file alone.
+.Nm Unifdef
+acts on
+#ifdef, #ifndef, #else, and #endif lines,
+and it knows only enough about C
+to know when one of these is inactive
+because it is inside
+a comment,
+or a single or double quote.
+Parsing for quotes is very simplistic:
+when it finds an open quote,
+it ignores everything (except escaped quotes)
+until it finds a close quote, and
+it will not complain if it gets
+to the end of a line and finds no backslash for continuation.
+.Pp
+Available options:
+.Bl -tag -width Ds -compact
+.It Fl D Ns Ar sym
+.It Fl U Ns Ar sym
+Specify which symbols to define or undefine.
+and the lines inside those ifdefs will be copied to the output or removed as
+appropriate.
+The ifdef, ifndef, else, and endif lines associated with
+.Ar sym
+will also be removed.
+Ifdefs involving symbols you don't specify
+and ``#if'' control lines
+are untouched and copied out
+along with their associated
+ifdef, else, and endif lines.
+If an ifdef X occurs nested inside another ifdef X, then the
+inside ifdef is treated as if it were an unrecognized symbol.
+If the same symbol appears in more than one argument,
+the last occurrence dominates.
+.Pp
+.It Fl c
+If the
+.Fl c
+flag is specified,
+then the operation of
+.Nm unifdef
+is complemented,
+i.e. the lines that would have been removed or blanked
+are retained and vice versa.
+.Pp
+.It Fl l
+Replace removed lines with blank lines
+instead of deleting them.
+.It Fl t
+Disables parsing for
+C comments and quotes useful for plain text
+(not C code).
+.Pp
+.It Fl iD Ns Ar sym
+.It Fl iU Ns Ar sym
+Ignore ifdefs.
+If your C code uses ifdefs to delimit non-C lines,
+such as comments
+or code which is under construction,
+then you must tell
+.Nm unifdef
+which symbols are used for that purpose so that it won't try to parse
+for quotes and comments
+inside those ifdefs.
+One specifies ignored ifdefs with
+.Fl iD Ns Ar sym
+and
+.Fl iU Ns Ar sym
+similar to
+.Fl D Ns Ar sym
+and
+.Fl U Ns Ar sym
+above.
+.El
+.Pp
+.Nm Unifdef
+copies its output to
+.Em stdout
+and will take its input from
+.Em stdin
+if no
+.Ar file
+argument is given.
+.Pp
+.Nm Unifdef
+works nicely with the
+.Fl D Ns Ar sym
+option added to
+.Xr diff 1
+as of the 4.1 Berkeley Software Distribution.
+.Sh SEE ALSO
+.Xr diff 1
+.Sh DIAGNOSTICS
+Inappropriate else or endif.
+.br
+Premature
+.Tn EOF
+with line numbers of the unterminated #ifdefs.
+.Pp
+Exit status is 0 if output is exact copy of input, 1 if not, 2 if trouble.
+.Sh BUGS
+Should try to deal with ``#if'' lines.
+.Pp
+Doesn't work correctly if input contains null characters.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.3 .
diff --git a/src/util/unifdef/unifdef.c b/src/util/unifdef/unifdef.c
new file mode 100644 (file)
index 0000000..399825e
--- /dev/null
@@ -0,0 +1,638 @@
+/*
+ * Copyright (c) 1985 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Dave Yost.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1985 The Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)unifdef.c  4.7 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+/*
+ * unifdef - remove ifdef'ed lines
+ *
+ *  Warning: will not work correctly if input contains null characters.
+ *
+ *  Wishlist:
+ *      provide an option which will append the name of the
+ *        appropriate symbol after #else's and #endif's
+ *      provide an option which will check symbols after
+ *        #else's and #endif's to see that they match their
+ *        corresponding #ifdef or #ifndef
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+
+#define BSS
+FILE *input;
+#ifndef YES
+#define YES 1
+#define NO  0
+#endif/*YES */
+typedef int Bool;
+
+char *progname BSS;
+char *filename BSS;
+char text BSS;          /* -t option in effect: this is a text file */
+char lnblank BSS;       /* -l option in effect: blank deleted lines */
+char complement BSS;    /* -c option in effect: complement the operation */
+
+#define MAXSYMS 100
+char *symname[MAXSYMS] BSS; /* symbol name */
+char true[MAXSYMS] BSS;     /* -Dsym */
+char ignore[MAXSYMS] BSS;   /* -iDsym or -iUsym */
+char insym[MAXSYMS] BSS;    /* state: false, inactive, true */
+#define SYM_INACTIVE 0      /* symbol is currently inactive */
+#define SYM_FALSE    1      /* symbol is currently false */
+#define SYM_TRUE     2      /* symbol is currently true  */
+
+char nsyms BSS;
+char incomment BSS;         /* inside C comment */
+
+#define QUOTE_NONE   0
+#define QUOTE_SINGLE 1
+#define QUOTE_DOUBLE 2
+char inquote BSS;           /* inside single or double quotes */
+
+int exitstat BSS;
+char *skipcomment ();
+char *skipquote ();
+
+main (argc, argv)
+int argc;
+char **argv;
+{
+    char **curarg;
+    register char *cp;
+    register char *cp1;
+    char ignorethis;
+
+    progname = argv[0][0] ? argv[0] : "unifdef";
+
+    for (curarg = &argv[1]; --argc > 0; curarg++) {
+       if (*(cp1 = cp = *curarg) != '-')
+           break;
+       if (*++cp1 == 'i') {
+           ignorethis = YES;
+           cp1++;
+       } else
+           ignorethis = NO;
+       if (   (   *cp1 == 'D'
+               || *cp1 == 'U'
+              )
+           && cp1[1] != '\0'
+          ) {
+           register int symind;
+
+           if ((symind = findsym (&cp1[1])) < 0) {
+               if (nsyms >= MAXSYMS) {
+                   prname ();
+                   fprintf (stderr, "too many symbols.\n");
+                   exit (2);
+               }
+               symind = nsyms++;
+               symname[symind] = &cp1[1];
+               insym[symind] = SYM_INACTIVE;
+           }
+           ignore[symind] = ignorethis;
+           true[symind] = *cp1 == 'D' ? YES : NO;
+       } else if (ignorethis)
+           goto unrec;
+       else if (strcmp (&cp[1], "t") == 0)
+           text = YES;
+       else if (strcmp (&cp[1], "l") == 0)
+           lnblank = YES;
+       else if (strcmp (&cp[1], "c") == 0)
+           complement = YES;
+       else {
+ unrec:
+           prname ();
+           fprintf (stderr, "unrecognized option: %s\n", cp);
+           goto usage;
+       }
+    }
+    if (nsyms == 0) {
+ usage:
+       fprintf (stderr, "\
+Usage: %s [-l] [-t] [-c] [[-Dsym] [-Usym] [-iDsym] [-iUsym]]... [file]\n\
+    At least one arg from [-D -U -iD -iU] is required\n", progname);
+       exit (2);
+    }
+
+    if (argc > 1) {
+       prname ();
+       fprintf (stderr, "can only do one file.\n");
+    } else if (argc == 1) {
+       filename = *curarg;
+       if ((input = fopen (filename, "r")) != NULL) {
+           pfile();
+           (void) fclose (input);
+       } else {
+           prname ();
+           fprintf (stderr, "can't open ");
+           perror(*curarg);
+       }
+    } else {
+       filename = "[stdin]";
+       input = stdin;
+       pfile();
+    }
+
+    (void) fflush (stdout);
+    exit (exitstat);
+}
+
+/* types of input lines: */
+typedef int Linetype;
+#define LT_PLAIN       0   /* ordinary line */
+#define LT_TRUE        1   /* a true  #ifdef of a symbol known to us */
+#define LT_FALSE       2   /* a false #ifdef of a symbol known to us */
+#define LT_OTHER       3   /* an #ifdef of a symbol not known to us */
+#define LT_IF          4   /* an #ifdef of a symbol not known to us */
+#define LT_ELSE        5   /* #else */
+#define LT_ENDIF       6   /* #endif */
+#define LT_LEOF        7   /* end of file */
+extern Linetype checkline ();
+
+typedef int Reject_level;
+Reject_level reject BSS;    /* 0 or 1: pass thru; 1 or 2: ignore comments */
+#define REJ_NO          0
+#define REJ_IGNORE      1
+#define REJ_YES         2
+
+int linenum BSS;    /* current line number */
+int stqcline BSS;   /* start of current coment or quote */
+char *errs[] = {
+#define NO_ERR      0
+                       "",
+#define END_ERR     1
+                       "",
+#define ELSE_ERR    2
+                       "Inappropriate else",
+#define ENDIF_ERR   3
+                       "Inappropriate endif",
+#define IEOF_ERR    4
+                       "Premature EOF in ifdef",
+#define CEOF_ERR    5
+                       "Premature EOF in comment",
+#define Q1EOF_ERR   6
+                       "Premature EOF in quoted character",
+#define Q2EOF_ERR   7
+                       "Premature EOF in quoted string"
+};
+
+/* States for inif arg to doif */
+#define IN_NONE 0
+#define IN_IF   1
+#define IN_ELSE 2
+
+pfile ()
+{
+    reject = REJ_NO;
+    (void) doif (-1, IN_NONE, reject, 0);
+    return;
+}
+
+int
+doif (thissym, inif, prevreject, depth)
+register int thissym;   /* index of the symbol who was last ifdef'ed */
+int inif;               /* YES or NO we are inside an ifdef */
+Reject_level prevreject;/* previous value of reject */
+int depth;              /* depth of ifdef's */
+{
+    register Linetype lineval;
+    register Reject_level thisreject;
+    int doret;          /* tmp return value of doif */
+    int cursym;         /* index of the symbol returned by checkline */
+    int stline;         /* line number when called this time */
+
+    stline = linenum;
+    for (;;) {
+       switch (lineval = checkline (&cursym)) {
+       case LT_PLAIN:
+           flushline (YES);
+           break;
+
+       case LT_TRUE:
+       case LT_FALSE:
+           thisreject = reject;
+           if (lineval == LT_TRUE)
+               insym[cursym] = SYM_TRUE;
+           else {
+               if (reject != REJ_YES)
+                   reject = ignore[cursym] ? REJ_IGNORE : REJ_YES;
+               insym[cursym] = SYM_FALSE;
+           }
+           if (ignore[cursym])
+               flushline (YES);
+           else {
+               exitstat = 1;
+               flushline (NO);
+           }
+           if ((doret = doif (cursym, IN_IF, thisreject, depth + 1)) != NO_ERR)
+               return error (doret, stline, depth);
+           break;
+
+       case LT_IF:
+       case LT_OTHER:
+           flushline (YES);
+           if ((doret = doif (-1, IN_IF, reject, depth + 1)) != NO_ERR)
+               return error (doret, stline, depth);
+           break;
+
+       case LT_ELSE:
+           if (inif != IN_IF)
+               return error (ELSE_ERR, linenum, depth);
+           inif = IN_ELSE;
+           if (thissym >= 0) {
+               if (insym[thissym] == SYM_TRUE) {
+                   reject = ignore[thissym] ? REJ_IGNORE : REJ_YES;
+                   insym[thissym] = SYM_FALSE;
+               } else { /* (insym[thissym] == SYM_FALSE) */
+                   reject = prevreject;
+                   insym[thissym] = SYM_TRUE;
+               }
+               if (!ignore[thissym]) {
+                   flushline (NO);
+                   break;
+               }
+           }
+           flushline (YES);
+           break;
+
+       case LT_ENDIF:
+           if (inif == IN_NONE)
+               return error (ENDIF_ERR, linenum, depth);
+           if (thissym >= 0) {
+               insym[thissym] = SYM_INACTIVE;
+               reject = prevreject;
+               if (!ignore[thissym]) {
+                   flushline (NO);
+                   return NO_ERR;
+               }
+           }
+           flushline (YES);
+           return NO_ERR;
+
+       case LT_LEOF: {
+           int err;
+           err =   incomment
+                 ? CEOF_ERR
+                 : inquote == QUOTE_SINGLE
+                 ? Q1EOF_ERR
+                 : inquote == QUOTE_DOUBLE
+                 ? Q2EOF_ERR
+                 : NO_ERR;
+           if (inif != IN_NONE) {
+               if (err != NO_ERR)
+                   (void) error (err, stqcline, depth);
+               return error (IEOF_ERR, stline, depth);
+           } else if (err != NO_ERR)
+               return error (err, stqcline, depth);
+           else
+               return NO_ERR;
+           }
+       }
+    }
+}
+
+#define endsym(c) (!isalpha (c) && !isdigit (c) && c != '_')
+
+#define MAXLINE 256
+char tline[MAXLINE] BSS;
+
+Linetype
+checkline (cursym)
+int *cursym;    /* if LT_TRUE or LT_FALSE returned, set this to sym index */
+{
+    register char *cp;
+    register char *symp;
+    char *scp;
+    Linetype retval;
+#   define KWSIZE 8
+    char keyword[KWSIZE];
+
+    linenum++;
+    if (getlin (tline, sizeof tline, input, NO) == EOF)
+       return LT_LEOF;
+
+    retval = LT_PLAIN;
+    if (   *(cp = tline) != '#'
+       || incomment
+       || inquote == QUOTE_SINGLE
+       || inquote == QUOTE_DOUBLE
+       )
+       goto eol;
+
+    cp = skipcomment (++cp);
+    symp = keyword;
+    while (!endsym (*cp)) {
+       *symp = *cp++;
+       if (++symp >= &keyword[KWSIZE])
+           goto eol;
+    }
+    *symp = '\0';
+
+    if (strcmp (keyword, "ifdef") == 0) {
+       retval = YES;
+       goto ifdef;
+    } else if (strcmp (keyword, "ifndef") == 0) {
+       retval = NO;
+ ifdef:
+       scp = cp = skipcomment (++cp);
+       if (incomment) {
+           retval = LT_PLAIN;
+           goto eol;
+       }
+       {
+           int symind;
+
+           if ((symind = findsym (scp)) >= 0)
+               retval = (retval ^ true[*cursym = symind])
+                        ? LT_FALSE : LT_TRUE;
+           else
+               retval = LT_OTHER;
+       }
+    } else if (strcmp (keyword, "if") == 0)
+       retval = LT_IF;
+    else if (strcmp (keyword, "else") == 0)
+       retval = LT_ELSE;
+    else if (strcmp (keyword, "endif") == 0)
+       retval = LT_ENDIF;
+
+ eol:
+    if (!text && reject != REJ_IGNORE)
+       for (; *cp; ) {
+           if (incomment)
+               cp = skipcomment (cp);
+           else if (inquote == QUOTE_SINGLE)
+               cp = skipquote (cp, QUOTE_SINGLE);
+           else if (inquote == QUOTE_DOUBLE)
+               cp = skipquote (cp, QUOTE_DOUBLE);
+           else if (*cp == '/' && cp[1] == '*')
+               cp = skipcomment (cp);
+           else if (*cp == '\'')
+               cp = skipquote (cp, QUOTE_SINGLE);
+           else if (*cp == '"')
+               cp = skipquote (cp, QUOTE_DOUBLE);
+           else
+               cp++;
+       }
+    return retval;
+}
+
+/*
+ *  Skip over comments and stop at the next charaacter
+ *  position that is not whitespace.
+ */
+char *
+skipcomment (cp)
+register char *cp;
+{
+    if (incomment)
+       goto inside;
+    for (;; cp++) {
+       while (*cp == ' ' || *cp == '\t')
+           cp++;
+       if (text)
+           return cp;
+       if (   cp[0] != '/'
+           || cp[1] != '*'
+          )
+           return cp;
+       cp += 2;
+       if (!incomment) {
+           incomment = YES;
+           stqcline = linenum;
+       }
+ inside:
+       for (;;) {
+           for (; *cp != '*'; cp++)
+               if (*cp == '\0')
+                   return cp;
+           if (*++cp == '/') {
+               incomment = NO;
+               break;
+           }
+       }
+    }
+}
+
+/*
+ *  Skip over a quoted string or character and stop at the next charaacter
+ *  position that is not whitespace.
+ */
+char *
+skipquote (cp, type)
+register char *cp;
+register int type;
+{
+    register char qchar;
+
+    qchar = type == QUOTE_SINGLE ? '\'' : '"';
+
+    if (inquote == type)
+       goto inside;
+    for (;; cp++) {
+       if (*cp != qchar)
+           return cp;
+       cp++;
+       inquote = type;
+       stqcline = linenum;
+ inside:
+       for (; ; cp++) {
+           if (*cp == qchar)
+               break;
+           if (   *cp == '\0'
+               || *cp == '\\' && *++cp == '\0'
+              )
+               return cp;
+       }
+       inquote = QUOTE_NONE;
+    }
+}
+
+/*
+ *  findsym - look for the symbol in the symbol table.
+ *            if found, return symbol table index,
+ *            else return -1.
+ */
+int
+findsym (str)
+char *str;
+{
+    register char *cp;
+    register char *symp;
+    register int symind;
+    register char chr;
+
+    for (symind = 0; symind < nsyms; ++symind) {
+       if (insym[symind] == SYM_INACTIVE) {
+           for ( symp = symname[symind], cp = str
+               ; *symp && *cp == *symp
+               ; cp++, symp++
+               )
+               continue;
+           chr = *cp;
+           if (*symp == '\0' && endsym (chr))
+               return symind;
+       }
+    }
+    return -1;
+}
+
+/*
+ *   getlin - expands tabs if asked for
+ *            and (if compiled in) treats form-feed as an end-of-line
+ */
+int
+getlin (line, maxline, inp, expandtabs)
+register char *line;
+int maxline;
+FILE *inp;
+int expandtabs;
+{
+    int tmp;
+    register int num;
+    register int chr;
+#ifdef  FFSPECIAL
+    static char havechar = NO;  /* have leftover char from last time */
+    static char svchar BSS;
+#endif/*FFSPECIAL */
+
+    num = 0;
+#ifdef  FFSPECIAL
+    if (havechar) {
+       havechar = NO;
+       chr = svchar;
+       goto ent;
+    }
+#endif/*FFSPECIAL */
+    while (num + 8 < maxline) {   /* leave room for tab */
+       chr = getc (inp);
+       if (isprint (chr)) {
+#ifdef  FFSPECIAL
+ ent:
+#endif/*FFSPECIAL */
+           *line++ = chr;
+           num++;
+       } else
+           switch (chr) {
+           case EOF:
+               return EOF;
+
+           case '\t':
+               if (expandtabs) {
+                   num += tmp = 8 - (num & 7);
+                   do
+                       *line++ = ' ';
+                   while (--tmp);
+                   break;
+               }
+           default:
+               *line++ = chr;
+               num++;
+               break;
+
+           case '\n':
+               *line = '\n';
+               num++;
+               goto end;
+
+#ifdef  FFSPECIAL
+           case '\f':
+               if (++num == 1)
+                   *line = '\f';
+               else {
+                   *line = '\n';
+                   havechar = YES;
+                   svchar = chr;
+               }
+               goto end;
+#endif/*FFSPECIAL */
+           }
+    }
+ end:
+    *++line = '\0';
+    return num;
+}
+
+flushline (keep)
+Bool keep;
+{
+    if ((keep && reject != REJ_YES) ^ complement) {
+       register char *line = tline;
+       register FILE *out = stdout;
+       register char chr;
+
+       while (chr = *line++)
+           putc (chr, out);
+    } else if (lnblank)
+       putc ('\n', stdout);
+    return;
+}
+
+prname ()
+{
+    fprintf (stderr, "%s: ", progname);
+    return;
+}
+
+int
+error (err, line, depth)
+int err;        /* type of error & index into error string array */
+int line;       /* line number */
+int depth;      /* how many ifdefs we are inside */
+{
+    if (err == END_ERR)
+       return err;
+
+    prname ();
+
+#ifndef TESTING
+    fprintf (stderr, "Error in %s line %d: %s.\n", filename, line, errs[err]);
+#else/* TESTING */
+    fprintf (stderr, "Error in %s line %d: %s. ", filename, line, errs[err]);
+    fprintf (stderr, "ifdef depth: %d\n", depth);
+#endif/*TESTING */
+
+    exitstat = 2;
+    return depth > 1 ? IEOF_ERR : END_ERR;
+}