diff options
author | Theodore Tso <tytso@mit.edu> | 1993-06-03 12:31:48 +0000 |
---|---|---|
committer | Theodore Tso <tytso@mit.edu> | 1993-06-03 12:31:48 +0000 |
commit | acbed92e113f54d33789d427e697a23a0f07ab64 (patch) | |
tree | 164a1dbc44306b6d896eb46165623e44223c37a9 | |
parent | df53bcea92bd141193e4d63cd1cccdab138579b7 (diff) | |
download | krb5-acbed92e113f54d33789d427e697a23a0f07ab64.tar.gz krb5-acbed92e113f54d33789d427e697a23a0f07ab64.tar.xz krb5-acbed92e113f54d33789d427e697a23a0f07ab64.zip |
Initial revision
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@2610 dc483132-0cff-0310-8789-dd5450dbe970
47 files changed, 6763 insertions, 0 deletions
diff --git a/src/util/Imakefile b/src/util/Imakefile new file mode 100644 index 000000000..61b6de9ed --- /dev/null +++ b/src/util/Imakefile @@ -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 index 000000000..747184c6d --- /dev/null +++ b/src/util/et/Imakefile @@ -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 index 000000000..804343013 --- /dev/null +++ b/src/util/et/Makefile.in @@ -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 index 000000000..ee4375b02 --- /dev/null +++ b/src/util/et/com_err.3 @@ -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 index 000000000..2f4b26612 --- /dev/null +++ b/src/util/et/com_err.texinfo @@ -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#=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 index 000000000..f17a278bd --- /dev/null +++ b/src/util/et/compile_et.1 @@ -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 index 000000000..fdd249e83 --- /dev/null +++ b/src/util/et/compile_et.sh @@ -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 index 000000000..e3de35c87 --- /dev/null +++ b/src/util/et/config_script @@ -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 index 000000000..b65532d8f --- /dev/null +++ b/src/util/et/error_table.y @@ -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 index 000000000..4c7b77f0e --- /dev/null +++ b/src/util/et/test1.et @@ -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 index 000000000..55ad74ead --- /dev/null +++ b/src/util/et/test2.et @@ -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 index 000000000..838160c98 --- /dev/null +++ b/src/util/et/texinfo.tex @@ -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\}\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 `\=\other +\catcode `\=\other +\catcode `\^^C=\other +\catcode `\^^D=\other +\catcode `\^^E=\other +\catcode `\^^F=\other +\catcode `\^^G=\other +\catcode `\^^H=\other +\catcode `\=\other +\catcode `\^^L=\other +\catcode `\=\other +\catcode `\=\other +\catcode `\=\other +\catcode `\=\other +\catcode `\=\other +\catcode `\=\other +\catcode `\=\other +\catcode `\=\other +\catcode `\=\other +\catcode `\=\other +\catcode `\=\other +\catcode `\=\other +\catcode `\=\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 index 000000000..94f0fb582 --- /dev/null +++ b/src/util/et/vfprintf.c @@ -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 index 000000000..adfe36da7 --- /dev/null +++ b/src/util/ss/Imakefile @@ -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 index 000000000..7a5b3c891 --- /dev/null +++ b/src/util/ss/Makefile.in @@ -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 index 000000000..2f413a8af --- /dev/null +++ b/src/util/ss/cmd_tbl.lex.l @@ -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 index 000000000..e3de35c87 --- /dev/null +++ b/src/util/ss/config_script @@ -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 index 000000000..e0d157224 --- /dev/null +++ b/src/util/ss/copyright.h @@ -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 index 000000000..6ad5dfcec --- /dev/null +++ b/src/util/ss/ct.y @@ -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 index 000000000..872f6e007 --- /dev/null +++ b/src/util/ss/ct_c.awk @@ -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 index 000000000..8d6452bee --- /dev/null +++ b/src/util/ss/ct_c.sed @@ -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 index 000000000..dd6341c0c --- /dev/null +++ b/src/util/ss/data.c @@ -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 index 000000000..3b7165a72 --- /dev/null +++ b/src/util/ss/error.c @@ -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 index 000000000..9442f3392 --- /dev/null +++ b/src/util/ss/execute_cmd.c @@ -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 index 000000000..ad3b90b1f --- /dev/null +++ b/src/util/ss/help.c @@ -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 index 000000000..678bbcd87 --- /dev/null +++ b/src/util/ss/invocation.c @@ -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 index 000000000..b8aee87d2 --- /dev/null +++ b/src/util/ss/list_rqs.c @@ -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 index 000000000..a15495309 --- /dev/null +++ b/src/util/ss/listen.c @@ -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 index 000000000..ffcfc380c --- /dev/null +++ b/src/util/ss/mit-sipb-copyright.h @@ -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 index 000000000..af04ec083 --- /dev/null +++ b/src/util/ss/mk_cmds.c @@ -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 index 000000000..eda3888a1 --- /dev/null +++ b/src/util/ss/mk_cmds.sh @@ -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 index 000000000..dd648b01a --- /dev/null +++ b/src/util/ss/options.c @@ -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 index 000000000..b951fa638 --- /dev/null +++ b/src/util/ss/pager.c @@ -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 index 000000000..e1dec3151 --- /dev/null +++ b/src/util/ss/parse.c @@ -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 index 000000000..bc95d8223 --- /dev/null +++ b/src/util/ss/prompt.c @@ -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 index 000000000..8dcb5837a --- /dev/null +++ b/src/util/ss/request_tbl.c @@ -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 index 000000000..bfe69f120 --- /dev/null +++ b/src/util/ss/requests.c @@ -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 index 000000000..ca3197d8b --- /dev/null +++ b/src/util/ss/ss.h @@ -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 index 000000000..80e9dfa44 --- /dev/null +++ b/src/util/ss/ss_err.et @@ -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 index 000000000..4b9ea23c3 --- /dev/null +++ b/src/util/ss/ss_internal.h @@ -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 index 000000000..500288a02 --- /dev/null +++ b/src/util/ss/std_rqs.ct @@ -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 index 000000000..a7c7c5dab --- /dev/null +++ b/src/util/ss/test_ss.c @@ -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 index 000000000..3520601b7 --- /dev/null +++ b/src/util/ss/utils.c @@ -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 index 000000000..0e312276b --- /dev/null +++ b/src/util/unifdef/Imakefile @@ -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 index 000000000..0d766bb92 --- /dev/null +++ b/src/util/unifdef/Makefile @@ -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 index 000000000..90e043a47 --- /dev/null +++ b/src/util/unifdef/unifdef.1 @@ -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 index 000000000..399825ec7 --- /dev/null +++ b/src/util/unifdef/unifdef.c @@ -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; +} |