summaryrefslogtreecommitdiff
path: root/support/tgrind/sysv
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /support/tgrind/sysv
Initial commit
Diffstat (limited to 'support/tgrind/sysv')
-rw-r--r--support/tgrind/sysv/makefile30
-rw-r--r--support/tgrind/sysv/regexp.c595
-rw-r--r--support/tgrind/sysv/retest.c69
-rw-r--r--support/tgrind/sysv/tfontedpr.c620
-rw-r--r--support/tgrind/sysv/tgrind.sh99
-rw-r--r--support/tgrind/sysv/tgrindmac.tex139
6 files changed, 1552 insertions, 0 deletions
diff --git a/support/tgrind/sysv/makefile b/support/tgrind/sysv/makefile
new file mode 100644
index 0000000000..84fa4f0165
--- /dev/null
+++ b/support/tgrind/sysv/makefile
@@ -0,0 +1,30 @@
+# @(#)Makefile 4.1 (Berkeley) 10/19/82
+#
+DESTDIR=/usr/local
+DEFSFILE=/usr/local/lib/vgrindefs
+TEXINPUTS=/usr/lib/tex/macros
+
+CFLAGS=-g
+SOURCES=tfontedpr.c vgrindefs.c regexp.c retest.c
+CMDS=tfontedpr retest
+OBJS=retest.o regexp.o tfontedpr.o vgrindefs.o
+
+all: $(CMDS)
+
+tfontedpr: tfontedpr.o vgrindefs.o regexp.o
+ cc ${CFLAGS} -o tfontedpr tfontedpr.o vgrindefs.o regexp.o
+
+tfontedpr.o: tfontedpr.c
+ cc ${CFLAGS} -DDEFSFILE=\"${DEFSFILE}\" -c tfontedpr.c
+
+retest: retest.o regexp.o
+ cc ${CFLAGS} -o retest retest.o regexp.o
+
+install: all
+ install -s tfontedpr ${DESTDIR}/lib/tfontedpr
+ install -c tgrind.sh ${DESTDIR}/bin/tgrind
+ install -c tgrindmac.tex ${TEXINPUTS}/tgrindmac.tex
+ install -c vgrindefs.src ${DEFSFILE}
+
+clean:
+ rm -f ${CMDS} ${OBJS}
diff --git a/support/tgrind/sysv/regexp.c b/support/tgrind/sysv/regexp.c
new file mode 100644
index 0000000000..ba7ab74eeb
--- /dev/null
+++ b/support/tgrind/sysv/regexp.c
@@ -0,0 +1,595 @@
+#ifndef lint
+static char *sccsid="@(#)regexp.c 1.2 (LBL) 4/12/85";
+#endif
+/*
+ * regular expression matching routines for tgrind/tfontedpr.
+ *
+ * These routines were written by Dave Presotto (I think) for vgrind.
+ * Minor mods & attempts to improve performance by Van Jacobson (van@lbl-rtsg)
+ * and Chris Torek (chris@maryland).
+ *
+ * Modifications.
+ * --------------
+ * 30Mar85 Van & Chris Changed expmatch to return pointer to start of what
+ * was matched in addition to pointer to match end.
+ * Several changes to improve performance (too numerous
+ * to mention).
+ * 11Dec84 Dave Presotto Written.
+ */
+
+#include <ctype.h>
+
+typedef int boolean;
+#define TRUE 1
+#define FALSE 0
+#define NIL 0
+
+#define makelower(c) (isupper((c)) ? tolower((c)) : (c))
+
+int (*re_strncmp)(); /* function used by expmatch to compare
+ * strings. The caller should make it point to
+ * strncmp if case is significant &
+ * lc_strncmp otherwise.
+ */
+
+/* lc_strncmp - like strncmp except that we convert the
+ * first string to lower case before comparing.
+ */
+
+lc_strncmp(s1, s2, len)
+ register char *s1,*s2;
+ register int len;
+{
+ while (len-- > 0)
+ if (*s2 - makelower(*s1))
+ return(1);
+ else
+ s2++, s1++;
+
+ return(0);
+}
+
+/* The following routine converts an irregular expression to
+ * internal format.
+ *
+ * Either meta symbols (\a \d or \p) or character strings or
+ * operations ( alternation or perenthesizing ) can be
+ * specified. Each starts with a descriptor byte. The descriptor
+ * byte has STR set for strings, META set for meta symbols
+ * and OPER set for operations.
+ * The descriptor byte can also have the OPT bit set if the object
+ * defined is optional. Also ALT can be set to indicate an alternation.
+ *
+ * For metasymbols the byte following the descriptor byte identities
+ * the meta symbol (containing an ascii 'a', 'd', 'p', '|', or '('). For
+ * strings the byte after the descriptor is a character count for
+ * the string:
+ *
+ * meta symbols := descriptor
+ * symbol
+ *
+ * strings := descriptor
+ * character count
+ * the string
+ *
+ * operatins := descriptor
+ * symbol
+ * character count
+ */
+
+/*
+ * handy macros for accessing parts of match blocks
+ */
+#define MSYM(A) (*(A+1)) /* symbol in a meta symbol block */
+#define MNEXT(A) (A+2) /* character following a metasymbol block */
+
+#define OSYM(A) (*(A+1)) /* symbol in an operation block */
+#define OCNT(A) (*(A+2)) /* character count */
+#define ONEXT(A) (A+3) /* next character after the operation */
+#define OPTR(A) (A+*(A+2)) /* place pointed to by the operator */
+
+#define SCNT(A) (*(A+1)) /* byte count of a string */
+#define SSTR(A) (A+2) /* address of the string */
+#define SNEXT(A) (A+2+*(A+1)) /* character following the string */
+
+/*
+ * bit flags in the descriptor
+ */
+#define OPT 1
+#define STR 2
+#define META 4
+#define ALT 8
+#define OPER 16
+
+char *ure; /* pointer current position in unconverted exp */
+char *ccre; /* pointer to current position in converted exp*/
+char *malloc();
+
+char *
+convexp(re)
+ char *re; /* unconverted irregular expression */
+{
+ register char *cre; /* pointer to converted regular expression */
+
+ /* allocate room for the converted expression */
+ if (re == NIL)
+ return (NIL);
+ if (*re == '\0')
+ return (NIL);
+ cre = malloc (4 * strlen(re) + 3);
+ ccre = cre;
+ ure = re;
+
+ /* start the conversion with a \a */
+ *cre = META | OPT;
+ MSYM(cre) = 'a';
+ ccre = MNEXT(cre);
+
+ /* start the conversion (its recursive) */
+ expconv ();
+ *ccre = 0;
+ return (cre);
+}
+
+expconv()
+{
+ register char *cs; /* pointer to current symbol in converted exp */
+ register char c; /* character being processed */
+ register char *acs; /* pinter to last alternate */
+ register int temp;
+
+ /* let the conversion begin */
+ acs = NIL;
+ cs = NIL;
+ while (*ure != NIL) {
+ switch (c = *ure++) {
+
+ case '\\':
+ switch (c = *ure++) {
+
+ /* escaped characters are just characters */
+ default:
+ if (cs == NIL || (*cs & STR) == 0) {
+ cs = ccre;
+ *cs = STR;
+ SCNT(cs) = 1;
+ ccre += 2;
+ } else
+ SCNT(cs)++;
+ *ccre++ = c;
+ break;
+
+ /* normal(?) metacharacters */
+ case 'a':
+ case 'd':
+ case 'e':
+ case 'p':
+ if (acs != NIL && acs != cs) {
+ do {
+ temp = OCNT(acs);
+ OCNT(acs) = ccre - acs;
+ acs -= temp;
+ } while (temp != 0);
+ acs = NIL;
+ }
+ cs = ccre;
+ *cs = META;
+ MSYM(cs) = c;
+ ccre = MNEXT(cs);
+ break;
+ }
+ break;
+
+ /* just put the symbol in */
+ case '^':
+ case '$':
+ if (acs != NIL && acs != cs) {
+ do {
+ temp = OCNT(acs);
+ OCNT(acs) = ccre - acs;
+ acs -= temp;
+ } while (temp != 0);
+ acs = NIL;
+ }
+ cs = ccre;
+ *cs = META;
+ MSYM(cs) = c;
+ ccre = MNEXT(cs);
+ break;
+
+ /* mark the last match sequence as optional */
+ case '?':
+ if (cs)
+ *cs = *cs | OPT;
+ break;
+
+ /* recurse and define a subexpression */
+ case '(':
+ if (acs != NIL && acs != cs) {
+ do {
+ temp = OCNT(acs);
+ OCNT(acs) = ccre - acs;
+ acs -= temp;
+ } while (temp != 0);
+ acs = NIL;
+ }
+ cs = ccre;
+ *cs = OPER;
+ OSYM(cs) = '(';
+ ccre = ONEXT(cs);
+ expconv ();
+ OCNT(cs) = ccre - cs; /* offset to next symbol */
+ break;
+
+ /* return from a recursion */
+ case ')':
+ if (acs != NIL) {
+ do {
+ temp = OCNT(acs);
+ OCNT(acs) = ccre - acs;
+ acs -= temp;
+ } while (temp != 0);
+ acs = NIL;
+ }
+ cs = ccre;
+ *cs = META;
+ MSYM(cs) = c;
+ ccre = MNEXT(cs);
+ return;
+
+ /* mark the last match sequence as having an alternate */
+ /* the third byte will contain an offset to jump over the */
+ /* alternate match in case the first did not fail */
+ case '|':
+ if (acs != NIL && acs != cs)
+ OCNT(ccre) = ccre - acs; /* make a back pointer */
+ else
+ OCNT(ccre) = 0;
+ *cs |= ALT;
+ cs = ccre;
+ *cs = OPER;
+ OSYM(cs) = '|';
+ ccre = ONEXT(cs);
+ acs = cs; /* remember that the pointer is to be filles */
+ break;
+
+ /* if its not a metasymbol just build a scharacter string */
+ default:
+ if (cs == NIL || (*cs & STR) == 0) {
+ cs = ccre;
+ *cs = STR;
+ SCNT(cs) = 1;
+ ccre = SSTR(cs);
+ } else
+ SCNT(cs)++;
+ *ccre++ = c;
+ break;
+ }
+ }
+ if (acs != NIL) {
+ do {
+ temp = OCNT(acs);
+ OCNT(acs) = ccre - acs;
+ acs -= temp;
+ } while (temp != 0);
+ acs = NIL;
+ }
+ return;
+}
+/* end of convertre */
+
+
+/*
+ * The following routine recognises an irregular expresion
+ * with the following special characters:
+ *
+ * \? - means last match was optional
+ * \a - matches any number of characters
+ * \d - matches any number of spaces and tabs
+ * \p - matches any number of alphanumeric
+ * characters. The
+ * characters matched will be copied into
+ * the area pointed to by 'name'.
+ * \| - alternation
+ * \( \) - grouping used mostly for alternation and
+ * optionality
+ *
+ * The irregular expression must be translated to internal form
+ * prior to calling this routine
+ *
+ * The value returned is the pointer to the last character matched.
+ * If strtptr is non-null, a pointer to the start of the string matched
+ * (excluding \a matches) will be returned at *strtptr.
+ * If mstring is non-null, the string matched by a \p will be copied
+ * into mstring.
+ */
+
+boolean _escaped; /* true if we are currently _escaped */
+char *_regstart; /* start of string */
+
+char *
+expmatch (s, re, strtptr, mstring)
+ register char *s; /* string to check for a match in */
+ register char *re; /* a converted irregular expression */
+ char **strtptr; /* where to put ptr to start of match */
+ char *mstring; /* where to put whatever matches a \p */
+{
+ register char *cs; /* the current symbol */
+ register char *ptr, *s1; /* temporary pointer */
+ register char c; /* temporary */
+ boolean matched; /* a temporary boolean */
+
+ /* initial conditions */
+ if ( strtptr )
+ *strtptr = NIL;
+ if (re == NIL)
+ return (NIL);
+ cs = re;
+ matched = FALSE;
+
+ /* loop till expression string is exhausted (or at least pretty tired) */
+ while (*cs) {
+ switch (*cs & (OPER | STR | META)) {
+
+ /* try to match a string */
+ case STR:
+ matched = !((*re_strncmp)(s, SSTR(cs), SCNT(cs)));
+ if (matched) {
+
+ /* hoorah it matches */
+ s += SCNT(cs);
+ cs = SNEXT(cs);
+ } else if (*cs & ALT) {
+
+ /* alternation, skip to next expression */
+ cs = SNEXT(cs);
+ } else if (*cs & OPT) {
+
+ /* the match is optional */
+ cs = SNEXT(cs);
+ matched = 1; /* indicate a successful match */
+ } else {
+
+ /* no match, error return */
+ return (NIL);
+ }
+ break;
+
+ /* an operator, do something fancy */
+ case OPER:
+ switch (OSYM(cs)) {
+
+ /* this is an alternation */
+ case '|':
+ if (matched)
+
+ /* last thing in the alternation was a match, skip ahead */
+ cs = OPTR(cs);
+ else
+
+ /* no match, keep trying */
+ cs = ONEXT(cs);
+ break;
+
+ /* this is a grouping, recurse */
+ case '(':
+ ptr = expmatch (s, ONEXT(cs), strtptr, mstring);
+ if (ptr != NIL) {
+
+ /* the subexpression matched */
+ matched = 1;
+ s = ptr;
+ } else if (*cs & ALT) {
+
+ /* alternation, skip to next expression */
+ matched = 0;
+ } else if (*cs & OPT) {
+
+ /* the match is optional */
+ matched = 1; /* indicate a successful match */
+ } else {
+
+ /* no match, error return */
+ return (NIL);
+ }
+ cs = OPTR(cs);
+ break;
+ }
+ break;
+
+ /* try to match a metasymbol */
+ case META:
+ switch (MSYM(cs)) {
+
+ /* try to match anything and remember what was matched */
+ case 'p':
+ /*
+ * This is really the same as trying the match the
+ * remaining parts of the expression to any subset
+ * of the string.
+ */
+ s1 = s;
+ do {
+ ptr = expmatch (s1, MNEXT(cs), strtptr, mstring);
+ if (ptr != NIL && s1 != s) {
+
+ /* we have a match, remember the match */
+ if ( mstring ) {
+ strncpy (mstring, s, s1 - s);
+ mstring[s1 - s] = '\0';
+ }
+ return (ptr);
+
+ } else if (ptr != NIL && (*cs & OPT)) {
+
+ /* \p was optional so no match is ok */
+ return (ptr);
+
+ } else if (ptr != NIL) {
+
+ /* not optional and we still matched */
+ return (NIL);
+ }
+ if (!isalnum(*s1) && *s1 != '_')
+ return (NIL);
+ if (*s1 == '\\')
+ _escaped = _escaped ? FALSE : TRUE;
+ else
+ _escaped = FALSE;
+ } while (*s1++);
+ return (NIL);
+
+ /* try to match anything */
+ case 'a':
+ /*
+ * This is really the same as trying the match the
+ * remaining parts of the expression to any subset
+ * of the string.
+ */
+ s1 = s;
+ do {
+ /*
+ * Hack for an important special case: if the next thing
+ * in the pattern is a string, just gobble characters until
+ * we find something that matches that string (this saves
+ * the cost of a recursive call on expmatch while scanning
+ * for the start of comments or strings). Since many
+ * patterns end with a string, we also treat that as a
+ * special case.
+ */
+ if( *(ptr=MNEXT(cs)) == STR ) {
+ c = *SSTR(ptr);
+ while( *s1 && *s1 != c )
+ s1++;
+
+ if ( *s1 == 0 )
+ return(NIL);
+
+ if ( SNEXT(ptr) == 0 && (s1 != s || *cs & OPT)) {
+ /* next item is a string, it's the last item and
+ * the \a match is ok - just loop to try & match
+ * the string.
+ */
+ if ( strtptr )
+ *strtptr = s1;
+
+ cs = ptr;
+ s = s1;
+ break;
+ }
+ }
+ ptr = expmatch (s1, MNEXT(cs), strtptr, mstring);
+ if (ptr != NIL && (s1 != s || *cs & OPT)) {
+
+ /* we have a match */
+ if ( strtptr )
+ *strtptr = s1;
+
+ return(ptr);
+
+ } else if (ptr != NIL) {
+
+ /* not optional and we still matched */
+ return (NIL);
+ }
+ if (*s1 == '\\')
+ _escaped = _escaped ? FALSE : TRUE;
+ else
+ _escaped = FALSE;
+ } while (*s1++);
+ return (NIL);
+
+ /* fail if we are currently _escaped */
+ case 'e':
+ if (_escaped)
+ return(NIL);
+ cs = MNEXT(cs);
+ break;
+
+ /* match any number of tabs and spaces */
+ case 'd':
+ ptr = s;
+ while (*s == ' ' || *s == '\t')
+ s++;
+ if (s != ptr || s == _regstart) {
+
+ /* match, be happy */
+ matched = 1;
+ cs = MNEXT(cs);
+ } else if (*s == '\n' || *s == '\0') {
+
+ /* match, be happy */
+ matched = 1;
+ cs = MNEXT(cs);
+ } else if (*cs & ALT) {
+
+ /* try the next part */
+ matched = 0;
+ cs = MNEXT(cs);
+ } else if (*cs & OPT) {
+
+ /* doesn't matter */
+ matched = 1;
+ cs = MNEXT(cs);
+ } else
+
+ /* no match, error return */
+ return (NIL);
+ break;
+
+ /* check for end of line */
+ case '$':
+ if (*s == '\0' || *s == '\n') {
+
+ /* match, be happy */
+ s++;
+ matched = 1;
+ cs = MNEXT(cs);
+ } else if (*cs & ALT) {
+
+ /* try the next part */
+ matched = 0;
+ cs = MNEXT(cs);
+ } else if (*cs & OPT) {
+
+ /* doesn't matter */
+ matched = 1;
+ cs = MNEXT(cs);
+ } else
+
+ /* no match, error return */
+ return (NIL);
+ break;
+
+ /* check for start of line */
+ case '^':
+ if (s == _regstart) {
+
+ /* match, be happy */
+ matched = 1;
+ cs = MNEXT(cs);
+ } else if (*cs & ALT) {
+
+ /* try the next part */
+ matched = 0;
+ cs = MNEXT(cs);
+ } else if (*cs & OPT) {
+
+ /* doesn't matter */
+ matched = 1;
+ cs = MNEXT(cs);
+ } else
+
+ /* no match, error return */
+ return (NIL);
+ break;
+
+ /* end of a subexpression, return success */
+ case ')':
+ return (s);
+ }
+ break;
+ }
+ }
+ return (s);
+}
diff --git a/support/tgrind/sysv/retest.c b/support/tgrind/sysv/retest.c
new file mode 100644
index 0000000000..098d06eae8
--- /dev/null
+++ b/support/tgrind/sysv/retest.c
@@ -0,0 +1,69 @@
+#ifndef lint
+static char *sccsid="@(#)retest.c 1.1 (LBL) 3/29/85";
+#endif
+
+#include <ctype.h>
+
+int l_onecase = 0;
+char * _regstart;
+char * _escaped;
+char * convexp();
+char * expmatch();
+int (*re_strncmp)();
+main()
+{
+ char reg[132];
+ char *ireg;
+ char str[132];
+ char *match;
+ char matstr[132];
+ char c;
+ int strncmp();
+
+ re_strncmp = strncmp;
+ while (1) {
+ printf ("\nexpr: ");
+ scanf ("%s", reg);
+ ireg = convexp(reg);
+ match = ireg;
+ while(*match) {
+ switch (*match) {
+
+ case '\\':
+ case '(':
+ case ')':
+ case '|':
+ printf ("%c", *match);
+ break;
+
+ default:
+ if (isalnum(*match))
+ printf("%c", *match);
+ else
+ printf ("<%03o>", *match);
+ break;
+ }
+ match++;
+ }
+ printf("\n");
+ getchar();
+ while(1) {
+ printf ("string: ");
+ match = str;
+ while ((c = getchar()) != '\n')
+ *match++ = c;
+ *match = 0;
+ if (str[0] == '#')
+ break;
+ matstr[0] = 0;
+ _regstart = str;
+ _escaped = 0;
+ match = expmatch (str, ireg, matstr);
+ if (match == 0)
+ printf ("FAILED\n");
+ else
+ printf ("match\nmatstr = %s\n", matstr);
+ }
+
+ }
+}
diff --git a/support/tgrind/sysv/tfontedpr.c b/support/tgrind/sysv/tfontedpr.c
new file mode 100644
index 0000000000..9ed861e3fd
--- /dev/null
+++ b/support/tgrind/sysv/tfontedpr.c
@@ -0,0 +1,620 @@
+#ifndef lint
+static char sccsid[] = "@(#)@(#)tfontedpr.c 1.3 (LBL) 4/12/85";
+#endif
+
+/* tfontedpr - general purpose "pretty printer" for use with TeX.
+ *
+ * Copyright (C) 1985 by Van Jacobson, Lawrence Berkeley Laboratory.
+ * This program may be freely used and copied but may not be sold
+ * without the author's written permission. This notice must remain
+ * in any copy or derivative.
+ *
+ * This program is used as part of the "tgrind" shell script. It
+ * converts program source file(s) to TeX input files.
+ *
+ * This program is an adaptation of "vfontedpr" v4.2 (12/11/84) from
+ * the 4.2bsd Unix distribution. Vfontedpr was written by Dave
+ * Presotto (based on an earlier program of the same name written by
+ * Bill Joy).
+ *
+ * I would welcome comments, enhancements, bug fixes, etc. Please
+ * mail them to:
+ * van@lbl-rtsg.arpa (from arpanet, milnet, csnet, etc.)
+ * ..!ucbvax!lbl-csam!van (from Usenet/UUCP)
+ *
+ * Modifications.
+ * --------------
+ * 30Mar85 Chris & Van: Fixed "\C" & "\S" (comment & string start indicators)
+ * to really appear at the start of comments & strings.
+ * Changes for speeded-up expmatch.
+ * 29Mar85 Chris Torek (chris@maryland): Bug fixes for '~' and '^L'
+ * output. Most cpu-time eaters recoded to improve
+ * efficiency.
+ * 10Feb85 Van Written.
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#define boolean int
+#define TRUE 1
+#define FALSE 0
+#define NIL 0
+#define STANDARD 0
+#define ALTERNATE 1
+
+#define STRLEN 10 /* length of strings introducing things */
+#define PNAMELEN 80 /* length of a function/procedure name */
+#define PSMAX 20 /* size of procedure name stacking */
+
+/* regular expression routines */
+
+char *expmatch(); /* match a string to an expression */
+char *convexp(); /* convert expression to internal form */
+char *tgetstr();
+
+boolean isproc();
+
+
+char *ctime();
+
+/*
+ * The state variables
+ */
+
+boolean incomm; /* in a comment of the primary type */
+boolean instr; /* in a string constant */
+boolean inchr; /* in a string constant */
+boolean nokeyw = FALSE; /* no keywords being flagged */
+boolean prccont; /* continue last procedure */
+int comtype; /* type of comment */
+int psptr; /* the stack index of the current procedure */
+char pstack[PSMAX][PNAMELEN+1]; /* the procedure name stack */
+int plstack[PSMAX]; /* the procedure nesting level stack */
+int blklevel; /* current nesting level */
+char *defsfile = DEFSFILE; /* name of language definitions file */
+char pname[BUFSIZ+1];
+
+/*
+ * The language specific globals
+ */
+
+char *language = "c"; /* the language indicator */
+char *l_keywds[BUFSIZ/2]; /* keyword table address */
+char *l_prcbeg; /* regular expr for procedure begin */
+char *l_combeg; /* string introducing a comment */
+char *l_comend; /* string ending a comment */
+char *l_acmbeg; /* string introducing a comment */
+char *l_acmend; /* string ending a comment */
+char *l_blkbeg; /* string begining of a block */
+char *l_blkend; /* string ending a block */
+char *l_strbeg; /* delimiter for string constant */
+char *l_strend; /* delimiter for string constant */
+char *l_chrbeg; /* delimiter for character constant */
+char *l_chrend; /* delimiter for character constant */
+char l_escape; /* character used to escape characters */
+boolean l_toplex; /* procedures only defined at top lex level */
+boolean l_onecase; /* upper & lower case equivalent */
+
+/*
+ * global variables also used by expmatch
+ */
+extern boolean _escaped; /* if last character was an escape */
+extern char *_regstart; /* start of the current string */
+
+int (*re_strncmp)(); /* function to do string compares */
+extern int strncmp();
+extern int lc_strncmp();
+
+/*
+ * The following table converts ASCII characters to a printed
+ * representation, taking care of all the TeX quoting. N.B.: all
+ * single-character strings are assumed to be equivalent to the
+ * character for that index (i.e., printtab['c'] can't be "f").
+ * (This is purely for efficiency hacking.)
+ */
+char *printtab[128] = {
+ "\0x", "\\^A", "\\^B", "\\^C", "\\^D", "\\^E", "\\^F", "\\^G",
+ "\\^H", "\t", "}}\n", "\\^K", "\0x", "\\^M", "\\^N", "\\^O",
+ "\\^P", "\\^Q", "\\^R", "\\^S", "\\^T", "\\^U", "\\^V", "\\^W",
+ "\\^X", "\\^Y", "\\^Z", "\\^[", "\\^\\!","\\^]", "\\^\\^","\\^_",
+ " ", "!", "\\\"", "\\#", "\\$", "\\%", "\\&", "\\'",
+ "(", ")", "*", "+", ",", "\\-", ".", "\\/",
+ "0", "1", "2", "3", "4", "5", "6", "7",
+ "8", "9", ":", ";", "\\<", "=", "\\>", "?",
+ "@", "A", "B", "C", "D", "E", "F", "G",
+ "H", "I", "J", "K", "L", "M", "N", "O",
+ "P", "Q", "R", "S", "T", "U", "V", "W",
+ "X", "Y", "Z", "[", "\\!", "]", "\\^", "\\_",
+ "`", "a", "b", "c", "d", "e", "f", "g",
+ "h", "i", "j", "k", "l", "m", "n", "o",
+ "p", "q", "r", "s", "t", "u", "v", "w",
+ "x", "y", "z", "\\{", "\\|", "\\}", "\\~", "\\^?",
+};
+
+/* Output a character, with translation. Avoid side effects with this
+ macro! */
+#define outchar(c) (printtab[c][1] ? printf("%s", printtab[c]) : putchar(c))
+
+/*
+ * Output a TeX command to tab to column "col" (see tgrindmac.tex for a
+ * partial explanation of the bizarre brace arrangement).
+ */
+#define tabto(col) printf("}\\Tab{%d}{", col);
+
+main(argc, argv)
+ int argc;
+ register char *argv[];
+{
+ char *fname = "", *p;
+ struct stat stbuf;
+ char buf[BUFSIZ];
+ char strings[2 * BUFSIZ];
+ char defs[2 * BUFSIZ];
+
+ printf("\\input tgrindmac\n");
+ argc--, argv++;
+ do {
+ register char *cp;
+ register int i;
+
+ if (argc > 0) {
+ if (!strcmp(argv[0], "-h")) {
+ if (argc == 1) {
+ printf("\\Head{}\n");
+ argc = 0;
+ goto rest;
+ }
+ printf("\\Head{");
+ putstr( argv[1] );
+ printf( "}\n" );
+ argc--, argv++;
+ argc--, argv++;
+ if (argc > 0)
+ continue;
+ goto rest;
+ }
+
+ /* take input from the standard place */
+ if (!strcmp(argv[0], "-")) {
+ argc = 0;
+ goto rest;
+ }
+
+ /* indicate no keywords */
+ if (!strcmp(argv[0], "-n")) {
+ nokeyw++;
+ argc--, argv++;
+ continue;
+ }
+
+ /* specify the language */
+ if (!strncmp(argv[0], "-l", 2)) {
+ language = argv[0]+2;
+ argc--, argv++;
+ continue;
+ }
+
+ /* specify the language description file */
+ if (!strncmp(argv[0], "-d", 2)) {
+ defsfile = argv[1];
+ argc--, argv++;
+ argc--, argv++;
+ continue;
+ }
+
+ /* open the file for input */
+ if (freopen(argv[0], "r", stdin) == NULL) {
+ perror(argv[0]);
+ exit(1);
+ }
+
+ fname = argv[0];
+ argc--, argv++;
+ }
+ rest:
+
+ /*
+ * get the language definition from the defs file
+ */
+ i = tgetent (defs, language, defsfile);
+ if (i == 0) {
+ fprintf (stderr, "no entry for language %s\n", language);
+ exit (0);
+ } else if (i < 0) {
+ fprintf (stderr, "cannot find vgrindefs file %s\n", defsfile);
+ exit (0);
+ }
+ p = strings;
+ if (tgetstr ("kw", &p) == NIL)
+ nokeyw = TRUE;
+ else {
+ char **cpp;
+
+ cpp = l_keywds;
+ cp = strings;
+ while (*cp) {
+ while (*cp == ' ' || *cp =='\t')
+ *cp++ = NULL;
+ if (*cp)
+ *cpp++ = cp;
+ while (*cp != ' ' && *cp != '\t' && *cp)
+ cp++;
+ }
+ *cpp = NIL;
+ }
+ p = buf;
+ l_prcbeg = convexp (tgetstr ("pb", &p));
+ p = buf;
+ l_combeg = convexp (tgetstr ("cb", &p));
+ p = buf;
+ l_comend = convexp (tgetstr ("ce", &p));
+ p = buf;
+ l_acmbeg = convexp (tgetstr ("ab", &p));
+ p = buf;
+ l_acmend = convexp (tgetstr ("ae", &p));
+ p = buf;
+ l_strbeg = convexp (tgetstr ("sb", &p));
+ p = buf;
+ l_strend = convexp (tgetstr ("se", &p));
+ p = buf;
+ l_blkbeg = convexp (tgetstr ("bb", &p));
+ p = buf;
+ l_blkend = convexp (tgetstr ("be", &p));
+ p = buf;
+ l_chrbeg = convexp (tgetstr ("lb", &p));
+ p = buf;
+ l_chrend = convexp (tgetstr ("le", &p));
+ l_escape = '\\';
+ l_onecase = tgetflag ("oc");
+ if ( l_onecase )
+ re_strncmp = lc_strncmp;
+ else
+ re_strncmp = strncmp;
+ l_toplex = tgetflag ("tl");
+
+ /* initialize the program */
+
+ incomm = FALSE;
+ instr = FALSE;
+ inchr = FALSE;
+ _escaped = FALSE;
+ blklevel = 0;
+ for (psptr=0; psptr<PSMAX; psptr++) {
+ pstack[psptr][0] = NULL;
+ plstack[psptr] = 0;
+ }
+ psptr = -1;
+ fstat(fileno(stdin), &stbuf);
+ cp = ctime(&stbuf.st_mtime);
+ cp[10] = '\0';
+ cp[16] = '\0';
+ cp[24] = '\0';
+ printf("\\File{");
+ putstr( fname );
+ printf("},{%s},{%s %s}\n", cp+11, cp+4, cp+20);
+
+ /*
+ * MAIN LOOP!!!
+ */
+ while (fgets(buf, sizeof buf, stdin) != NULL) {
+ cp = buf;
+ if (*cp == '\f') {
+ printf("\\NewPage\n");
+ cp++;
+ if (*cp == '\n')/* some people like ^Ls on their own line */
+ continue;
+ }
+ prccont = FALSE;
+ printf("\\L{\\LB{");
+ putScp(cp);
+ if (prccont && (psptr >= 0)) {
+ printf("\\ProcCont{");
+ putstr( pstack[psptr] );
+ printf("}");
+ }
+#ifdef DEBUG
+ printf ("com %o str %o chr %o ptr %d\n", incomm, instr, inchr, psptr);
+#endif
+ }
+ } while (argc > 0);
+ printf("\\vfill\\eject\\end\n");
+ exit(0);
+}
+
+#define isidchr(c) (isalnum(c) || (c) == '_')
+
+putScp(os)
+ char *os;
+{
+ register char *s = os; /* pointer to unmatched string */
+ register char *s1; /* temp. string */
+ char *comptr; /* start of a comment delimiter */
+ char *comendptr; /* end of a comment delimiter */
+ char *acmptr; /* start of an alt. comment delimiter */
+ char *acmendptr; /* end of an alt. comment delimiter */
+ char *strptr; /* start of a string delimiter */
+ char *strendptr; /* end of a string delimiter */
+ char *chrptr; /* start of a char. const delimiter */
+ char *chrendptr; /* end of a char. const delimiter */
+ char *blksptr; /* start of a lexical block start */
+ char *blksendptr; /* end of a lexical block start */
+ char *blkeptr; /* start of a lexical block end */
+ char *blkeendptr; /* end of a lexical block end */
+
+ _regstart = os; /* remember the start for expmatch */
+ _escaped = FALSE;
+ if (nokeyw || incomm || instr)
+ goto skip;
+ if (isproc(s)) {
+ printf("\\Proc{");
+ putstr(pname);
+ printf("}");
+ if (psptr < PSMAX) {
+ ++psptr;
+ strncpy (pstack[psptr], pname, PNAMELEN);
+ pstack[psptr][PNAMELEN] = NULL;
+ plstack[psptr] = blklevel;
+ }
+ }
+skip:
+ do {
+ /* check for string, comment, blockstart, etc */
+ if (!incomm && !instr && !inchr) {
+
+ blkeendptr = expmatch (s, l_blkend, &blkeptr, NIL);
+ blksendptr = expmatch (s, l_blkbeg, &blksptr, NIL);
+ comendptr = expmatch (s, l_combeg, &comptr, NIL);
+ acmendptr = expmatch (s, l_acmbeg, &acmptr, NIL);
+ strendptr = expmatch (s, l_strbeg, &strptr, NIL);
+ chrendptr = expmatch (s, l_chrbeg, &chrptr, NIL);
+
+ /* start of a comment? */
+ if (comptr != NIL
+ && (strptr == NIL || comptr < strptr)
+ && (acmptr == NIL || comptr < acmptr)
+ && (chrptr == NIL || comptr < chrptr)
+ && (blksptr == NIL || comptr < blksptr)
+ && (blkeptr == NIL || comptr < blkeptr)) {
+ putKcp (s, comptr-1, FALSE);
+ printf("\\C{}");
+ s = comendptr;
+ putKcp (comptr, comendptr-1, FALSE);
+ incomm = TRUE;
+ comtype = STANDARD;
+ continue;
+ }
+
+ /* start of an alternate-form comment? */
+ if (acmptr != NIL
+ && (strptr == NIL || acmptr < strptr)
+ && (chrptr == NIL || acmptr < chrptr)
+ && (blksptr == NIL || acmptr < blksptr)
+ && (blkeptr == NIL || acmptr < blkeptr)) {
+ putKcp (s, acmptr-1, FALSE);
+ printf("\\C{}");
+ s = acmendptr;
+ putKcp (acmptr, acmendptr, FALSE);
+ incomm = TRUE;
+ comtype = ALTERNATE;
+ continue;
+ }
+
+ /* start of a string? */
+ if (strptr != NIL
+ && (chrptr == NIL || strptr < chrptr)
+ && (blksptr == NIL || strptr < blksptr)
+ && (blkeptr == NIL || strptr < blkeptr)) {
+ putKcp (s, strptr-1, FALSE);
+ printf("\\S{}");
+ s = strendptr;
+ putKcp (strptr,strendptr-1, FALSE);
+ instr = TRUE;
+ continue;
+ }
+
+ /* start of a character string? */
+ if (chrptr != NIL
+ && (blksptr == NIL || chrptr < blksptr)
+ && (blkeptr == NIL || chrptr < blkeptr)) {
+ putKcp (s, chrptr-1, FALSE);
+ printf("\\S{}");
+ s = chrendptr;
+ putKcp (chrptr, chrendptr-1, FALSE);
+ inchr = TRUE;
+ continue;
+ }
+
+ /* end of a lexical block */
+ if (blkeptr != NIL) {
+ if (blksptr == NIL || blkeptr < blksptr) {
+ putKcp (s, blkeendptr - 1, FALSE);
+ s = blkeendptr;
+ blklevel--;
+ if (psptr >= 0 && plstack[psptr] >= blklevel) {
+
+ /* end of current procedure */
+ blklevel = plstack[psptr];
+
+ /* see if we should print the last proc name */
+ if (--psptr >= 0)
+ prccont = TRUE;
+ else
+ psptr = -1;
+ }
+ continue;
+ }
+ }
+
+ /* start of a lexical block */
+ if (blksptr != NIL) {
+ putKcp (s, blksendptr - 1, FALSE);
+ s = blksendptr;
+ blklevel++;
+ continue;
+ }
+
+ /* check for end of comment */
+ } else if (incomm) {
+ if ((comendptr = expmatch( s,
+ comtype==STANDARD? l_comend : l_acmend,
+ NIL, NIL)) != NIL) {
+ putKcp (s, comendptr-1, TRUE);
+ s = comendptr;
+ incomm = FALSE;
+ printf("\\CE{}");
+ } else {
+ comptr = s;
+ s += strlen(s);
+ putKcp (comptr, s-1, TRUE);
+ }
+ continue;
+
+ /* check for end of string */
+ } else if (instr) {
+ if ((strendptr = expmatch (s, l_strend, NIL, NIL)) != NIL) {
+ putKcp (s, strendptr-1, TRUE);
+ s = strendptr;
+ instr = FALSE;
+ printf("\\SE{}");
+ } else {
+ strptr = s;
+ s += strlen(s);
+ putKcp (strptr, s-1, TRUE);
+ }
+ continue;
+
+ /* check for end of character string */
+ } else if (inchr) {
+ if ((chrendptr = expmatch (s, l_chrend, NIL, NIL)) != NIL) {
+ putKcp (s, chrendptr-1, TRUE);
+ s = chrendptr;
+ inchr = FALSE;
+ printf("\\SE{}");
+ } else {
+ chrptr = s;
+ s += strlen(s);
+ putKcp (chrptr, s-1, TRUE);
+ }
+ continue;
+ }
+
+ /* print out the line */
+ chrptr = s;
+ s += strlen(s);
+ putKcp (chrptr, s-1, FALSE);
+
+ } while (*s);
+}
+
+putKcp(start, end, nix)
+ register char *start; /* start of string to write */
+ register char *end; /* end of string to write */
+ register boolean nix; /* true if we should force nokeyw */
+{
+ register int i, c;
+
+ if (nokeyw)
+ nix = TRUE;
+
+ while (start <= end) {
+ c = *start++;
+ /* take care of nice tab stops */
+ if (c == '\t') {
+ while (start <= end && *start == '\t')
+ start++;
+ tabto(width(_regstart, start));
+ continue;
+ }
+ if (!nix && (isidchr(c) ||
+ c == '#' || c == '%' || c == '!' || c == '$')) {
+ /* potential keyword */
+ start--;
+ if (start == _regstart || !isidchr(start[-1])) {
+ i = iskw(start);
+ if (i > 0) {
+ printf("\\K{");
+ while (--i >= 0) {
+ c = *start++;
+ outchar(c);
+ }
+ putchar('}');
+ continue;
+ }
+ }
+ start++;
+ }
+ outchar(c);
+ }
+}
+
+
+width(s, os)
+ register char *s, *os;
+{
+ register int i = 0, c;
+
+ while (s < os) {
+ c = *s++;
+ if (c == '\t') {
+ i = (i + 8) &~ 7;
+ continue;
+ }
+ if (c < ' ')
+ i += 2;
+ else
+ i++;
+ }
+ return (i);
+}
+
+/* output a string, escaping special characters */
+putstr(cp)
+ register char *cp;
+{
+ register int c;
+
+ if (cp == NULL)
+ return;
+ while ((c = *cp++) != 0)
+ outchar(c);
+}
+
+/*
+ * look for a process beginning on this line
+ */
+boolean
+isproc(s)
+ char *s;
+{
+ pname[0] = NULL;
+ if ((!l_toplex || blklevel == 0)
+ && expmatch(s, l_prcbeg, NIL, pname) != NIL)
+ return (TRUE);
+ return (FALSE);
+}
+
+
+/* iskw - check to see if the next word is a keyword
+ */
+
+iskw(s)
+ register char *s;
+{
+ register char **ss = l_keywds;
+ register int i = 1;
+ register char *cp = s;
+ register int firstc = *s;
+
+ while (++cp, isidchr(*cp))
+ i++;
+ while (cp = *ss++) {
+ if (!l_onecase && firstc != *cp)
+ continue;
+ if ((*re_strncmp)(s, cp, i) == 0 && !isidchr(cp[i]))
+ return (i);
+ }
+ return (0);
+}
diff --git a/support/tgrind/sysv/tgrind.sh b/support/tgrind/sysv/tgrind.sh
new file mode 100644
index 0000000000..a5cf61097f
--- /dev/null
+++ b/support/tgrind/sysv/tgrind.sh
@@ -0,0 +1,99 @@
+#! /bin/sh
+# Script to grind nice program listings using TeX.
+#
+# written Feb, 1985 by Van Jacobson, Lawrence Berkeley Laboratory (adapted
+# from the 4.2bsd "vgrind" script).
+#
+# Translated to Bourne Shell, March 1987, Lou Salkind, New York University
+#
+# Since TeX output handling is site dependent, you'll have to edit this
+# file to get output to your local typesetting device(s). Our site uses
+# the flags "-v" (versatec output), "-q" (qms output) and "-o" (keep dvi file)
+# to route output. Put something appropriate to your site at the "PUT OUTPUT
+# HANDLING..." comment at the end of this script. If you've already dealt
+# with this in your local tex command, just change the -v/q/k (or whatever)
+# cases in the first "switch" to set variable "texoptions" appropriately.
+#
+b=/usr/local/lib/tfontedpr
+tex=/usr/local/bin/tex
+options=
+texoptions=
+files=
+head=""
+format=""
+output="dvi"
+outputfile=tgrind.dvi
+expecting=filename
+
+for A do
+ case $A in
+
+ -d)
+ expecting=doptions ;;
+
+ -f)
+ format="Y" ;;
+
+ -h)
+ expecting=head ;;
+
+ -o)
+ output=dvi
+ expecting=outputfile ;;
+
+# some sample devices...
+ -v)
+ output="ver" ;;
+
+ -q)
+ output="qms" ;;
+
+ -*)
+ options="$options $A" ;;
+
+ *)
+ case $expecting in
+ outputfile)
+ outputfile="$A" ;;
+ head)
+ head="$A" ;;
+ doptions)
+ options="$options -d $A" ;;
+ filename)
+ files="$files $A" ;;
+ esac
+ expecting=filename ;;
+ esac
+done
+
+if [ "$format" = "Y" ]; then
+ if [ "$head" != "" ]; then
+ $b $options -h "$head" $files
+ else
+ $b $options $files
+ fi
+ exit 0
+fi
+
+trap 'rm -f tgrnd$$.tex tgrnd$$.dvi tgrnd$$.log' 0 2 3 15
+
+if [ "$head" != "" ]; then
+ $b $options -h "$head" $files >tgrnd$$.tex
+else
+ $b $options $files >tgrnd$$.tex
+fi
+$tex $texoptions tgrnd$$.tex
+
+# PUT OUTPUT HANDLING COMMANDS HERE.
+case $output in
+ver)
+ ;;
+qms)
+ ;;
+dvi)
+# if [ `expr $outputfile : '/*'` -eq 0 ]; then
+# outputfile=$mydir/$outputfile
+# fi
+ mv tgrnd$$.dvi $outputfile
+ ;;
+esac
diff --git a/support/tgrind/sysv/tgrindmac.tex b/support/tgrind/sysv/tgrindmac.tex
new file mode 100644
index 0000000000..c09b7c83cd
--- /dev/null
+++ b/support/tgrind/sysv/tgrindmac.tex
@@ -0,0 +1,139 @@
+% @(#)tgrindmac.tex 1.4 (LBL) 3/30/85
+% Macros for TeX "tgrind" (a TeX equivalent of 4bsd "vgrind").
+%
+% Copyright (C) 1985 by Van Jacobson, Lawrence Berkeley Laboratory.
+% This program may be freely used and copied but may not be sold
+% without the author's written permission. This notice must remain
+% in any copy or derivative.
+%
+% Please send improvements, bug fixes, comments, etc., to
+% van@lbl-rtsg.arpa
+% ...ucbvax!lbl-csam!van
+%
+% Modifications.
+% --------------
+% 10Feb85, vj Written.
+% 23Mar85, rf Substitute ambx10 for amb10
+% 29Mar85, Chris Torek: Use tt font for all characters in strings.
+% Print decent quotes in comments rather than use tt
+% font quotes. Show filename (to terminal & log file)
+% at start of each new file.
+% 30Mar85, vj Fixed bug in tabbing.
+
+\font\sevenrm=cmr7 % font for right margin line numbers
+\font\twelvebf=cmbx10 scaled \magstep1 % font for page headers
+\font\forteenrm=cmr10 scaled \magstep2 % font for right margin proc names
+
+% tfontedpr outputs a "\Head{Hdr text}" if you give it the "-h" flag.
+% We remember the text in "\Header" so it can be included in the
+% head line.
+\def\Head#1{\def\Header{#1}}
+\def\Header{\null}
+
+% We get a "\File{Filename},{Last Mod Time},{Last Mod Date}" at the start of
+% each new file. We remember this stuff for inclusion in the page head & foot.
+% We reset the page number & current line number to 0 and output a null
+% mark to let the output routine know we're starting a new file.
+% We set up the \headline & \footline token lists inside the File macro to
+% save remembering the filename & mod time with yet other macros.
+\def\File#1,#2,#3{\vfill\eject\mark{\empty}
+\global\linecount=0\linenext=9\pageno=1\message{#1}
+\headline={\twelvebf\Header\hfil
+\edef\a{\topmark}\edef\b{\botmark}\edef\c{\firstmark}
+\ifx\c\empty\botmark\else
+\ifx\a\empty\botmark\else
+\ifx\b\empty\topmark\else
+\ifx\a\b\topmark\else\topmark--\botmark\fi
+\fi\fi\fi(#1)}
+\footline={\it{}#2 #3\hfil{}Page \folio{} of #1}}
+
+% There's a "\Proc{Proc Name}" at the start of each procedure. If
+% the language definition allows nested procedures (e.g., pascal), there
+% will be a "\ProcCont{Proc Name}" at the end of each inner procedure.
+% (In this case, "proc name" is the name of the outer procedure. I.e.,
+% ProcCont marks the continuation of "proc name").
+\def\Proc#1{\global\def\Procname{#1}\global\setbox\procbox=\hbox{\forteenrm #1}}
+\def\ProcCont#1{\global\def\Procname{#1}
+\global\setbox\procbox=\hbox{\forteenrm$\ldots$#1}}
+\newbox\procbox
+\def\Procname{\null}
+
+% Each formfeed in the input is replaced by a "\NewPage" macro. If
+% you really want a page break here, define this as "\vfill\eject".
+\def\NewPage{\filbreak\bigskip}
+
+% Each line of the program text is enclosed by a "\L{...}". We turn
+% each line into an hbox of size hsize. If we saw a procedure name somewhere
+% in the line (i.e., "procbox" is not null), we right justify "procbox"
+% on the line. Every 10 lines we output a small, right justified line number.
+\def\L#1{\filbreak\hbox to \hsize{\CF\strut\global\advance\linecount by1
+#1\hss\ifvoid\procbox\linebox\else\box\procbox\mark{\Procname}\fi}}
+
+\newcount\linecount \linecount=0
+\newcount\linenext \linenext=9
+\def\linebox{\ifnum\linecount>\linenext\global\advance\linenext by10
+\hbox{\sevenrm\the\linecount}\fi}
+
+
+% The following weirdness is to deal with tabs. "Pieces" of a line
+% between tabs are output as "\LB{...}". E.g., a line with a tab at
+% column 16 would be output as "\LB{xxx}\Tab{16}\LB{yyy}". (Actually, to
+% reduce the number of characters in the .tex file the \Tab macro
+% supplies the 2nd & subsequent \LB's.) We accumulate the LB stuff in an
+% hbox. When we see a Tab, we grab this hbox (using "\lastbox") and turn
+% it into a box that extends to the tab position. We stash this box in
+% "\linesofar" & use "\everyhbox" to get \linesofar concatenated onto the
+% front of the next piece of the line. (There must be a better way of
+% doing tabs [cf., the Plain.tex tab macros] but I'm not not enough of a
+% TeX wizard to come up with it. Suggestions would be appreciated.)
+
+\def\LB{\CF\hbox}
+\newbox\linesofar\setbox\linesofar=\null
+\everyhbox={\box\linesofar}
+\newdimen\TBwid
+\def\Tab#1{\setbox\tbox=\lastbox\TBwid=1\wd\tbox\advance\TBwid by 1\ts
+\ifdim\TBwid>#1\ts
+\setbox\linesofar=\hbox{\box\tbox\space}\else
+\setbox\linesofar=\hbox to #1\ts{\box\tbox\hfil}\fi\LB}
+
+% A normal space is too thin for code listings. We make spaces & tabs
+% be in "\ts" units (which are the width of a "0" in the current font).
+\newdimen\ts
+\newbox\tbox
+\setbox\tbox=\hbox{0} \ts=1\wd\tbox \setbox\tbox=\hbox{\hskip 1\ts}
+\def\space{\hskip 1\ts\relax}
+
+% Font changing stuff for keywords, comments & strings. We put keywords
+% in boldface, comments in text-italic & strings in typewriter. Since
+% we're usually changing the font inside of a \LB macro, we remember the
+% current font in \CF & stick a \CF at the start of each new box.
+% Also, the characters " and ' behave differently in comments than in
+% code, and others behave differently in strings than in code.
+\newif\ifcomment\newif\ifstring
+\let\CF=\rm
+\def\K#1{{\bf #1}} % Keyword
+\def\C{\it\global\let\CF=\it\global\commenttrue\relax} % Comment Start
+\def\CE{\rm\global\let\CF=\rm\global\commentfalse\relax}% Comment End
+\def\S{\tt\global\let\CF=\tt\global\stringtrue\relax} % String Start
+\def\SE{\rm\global\let\CF=\rm\global\stringfalse\relax} % String End
+
+% Special characters.
+\def\{{\ifmmode\lbrace\else\ifstring{\char'173}\else$\lbrace$\fi\fi}
+\def\}{\ifmmode\rbrace\else\ifstring{\char'175}\else$\rbrace$\fi\fi}
+\def\!{\ifmmode\backslash\else\ifstring{\char'134}\else$\backslash$\fi\fi}
+\def\|{\ifmmode|\else\ifstring{\char'174}\else$|$\fi\fi}
+\def\<{\ifmmode<\else\ifstring<\else$<$\fi\fi}
+\def\>{\ifmmode>\else\ifstring>\else$>$\fi\fi}
+\def\/{\ifmmode/\else\ifstring/\else$/$\fi\fi}
+\def\-{\ifmmode-\else\ifstring-\else$-$\fi\fi}
+\def\_{\ifstring{\char'137}\else\underbar{\ }\fi}
+\def\&{{\char'046}}
+\def\#{{\char'043}}
+\def\%{{\char'045}}
+\def\~{{\char'176}}
+\def\"{\ifcomment''\else{\tt\char'042}\fi}
+\def\'{\ifcomment'\else{\tt\char'047}\fi}
+\def\^{{\char'136}}
+\def\${{\rm\char'044}}
+
+\raggedright\obeyspaces\let =\space%