Chapter 1

Introduction

This is a L^{A}TEXfile with embedded code. Process it with LGrind and its option -e.

Appendix A

lgrind.c

/∗ lgrind.c, a L^{A}TEX version of tgrind, which is a TEX version

∗ of vgrind. Used to ‘grind nice program listings’. ∗/

#ifndef lint

static char sccsid[ ] = "@(#)lgrind.c 3.4 (MPi) 27/1/97";

static char Version[ ] =

  "$Id: lgrind.c,v 1.4 91/10/01 00:36:11 gvr Exp $";

#endif

10

/∗ lgrind — general purpose “pretty printer” for use with L^{A}TEX. ∗

∗ Copyright \begin{matrix}c \\ ◯ \end{matrix} 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 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 20

∗ 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) ∗ ∗

∗ Copyright \begin{matrix}c \\ ◯ \end{matrix} 1996 by Michael Piefel,

∗ Humboldt-University at Berlin, piefelcs.tu−berlin.de  30

∗ ∗ This derivative may be freely used and copied but may not be sold

∗ without the author's written permission.  This notice must remain
* in any copy.
* Derivatives of this program and the LGrind package (including, but not
* restricted to, the documentation) may only be produced after agreement
* with the author. This notice will remain here until I give up the main-
* tenance of the package.
*

* Modifications.  40

* --------------
* 10 Feb 85  Van          Written.
* 29 Mar 85  Chris Torek  (chris@@maryland):  Bug fixes for %|~|% and
*                        %|^L|% output. Most cpu-time eaters recoded
*                        to improve efficiency.
* 30 Mar 85  Chris & Van Fixed %|\C|% & %|\S|% (comment & string start
*                        indicators to really appear at the start of
*                        comments & strings.  Changes for speeded-up
*                        @expmatch()@.

*  8 Oct 87  JSL          Modified so as to compile on VMS.  On VMS, we 50

* don't use a surrounding shell script, so the

∗ options are a bit different −−− added %|−i|% to ∗ make a file to be %|\input|% in a %%\LaTeX%% ∗ environment. Changed some of the characters

∗ around −−− e.g., we always take %|∗|% in math ∗ mode (to make it centered) so we have to

∗ write %|∗|% as %|\∗|%. Etc.

∗ Jan 88 JSL %|−e|% option for embedded code.

∗ Sep 91 George V Reilly Reformated and cleaned up code, including

∗  naughtiness with NULL. Added %| |%, 60

∗ %%, %$, and %| features.

∗ Also the %<, %!, and %# features.

∗ Oct 94 Matthias Eckermann (ud311aa@sunmail.lrz-muenchen.de)

∗ fixed a little bug, added: Internal Help,

∗ Sep 95 Michael Piefel Modified for L^{A}TEX 2ε

∗ Feb 96 Michael Piefel Restructured into ANSI C ∗/

#include <stdio.h>

#include <stdlib.h>  70

#include <ctype.h> #include <string.h> #include <malloc.h> /∗ One of the following two (depending on your system) ∗/ #include <unistd.h> /∗ #include <io.h> ∗/

#include "lgrindef.h"

#include "regexp.h"

#ifndef vms  80

# include <sys/types.h> # include <sys/stat.h> # include <time.h> #else # include <types.h> # include <stat.h> #endif

#define STANDARD 0

#define ALTERNATE 1  90

#define NOTCODE 0 /∗ the three states of incode ∗/ #define INITCODE 1 #define OTHERCODE 2

#define PNAMELEN 80 /∗ length of a function/procedure name ∗/ #define PSMAX 20 /∗ size of procedure name stacking ∗/

#ifndef vms

# define OKEXIT 0  100

# define BADEXIT 1 # ifndef DEFSFILE

# define DEFSFILE "/usr/lib/lgrindef"

# endif #else # define OKEXIT 1 # define BADEXIT 0

# define DEFSFILE "TEX$INPUTS:lgrindef.src"

#endif

110

/∗ forward declarations ∗/

int getredirection(int argc, char ∗∗argv); void setlang(); void Internal Help(); void Internal Help Language List(); void readfile(FILE ∗); void putScp(char ∗); void putKcp(char ∗, char ∗, boolean);

void putVcp(char ∗, char ∗);  120

void putstr(char ∗); boolean (∗isproc)(char ∗); boolean isprocNorm(char ∗); boolean isprocC(char ∗); void outchar(int); void parsechartab(void); void parsepreamble(char ∗); void setpreambles(void); void printpreamble(char ∗);

void writeDefsfileInExe(char ∗, char ∗);  130

/∗

∗ The state variables ∗/

boolean incomm; /∗ in a comment of the primary type ∗/ boolean instr; /∗ in a string constant ∗/ boolean inchr; /∗ in a character constant ∗/ int incode; /∗ in program text within a comment ∗/

int latexcode; /∗ in program text within L^{A}TEX ∗/ 140

int latex tt; /∗ in \texttt text within L^{A}TEX ∗/ boolean use tt; /∗ use \texttt everywhere ∗/ boolean do at; /∗ pay attention to @s in L^{A}TEX ∗/ boolean do tt; /∗ pay attention to |s in L^{A}TEX ∗/ boolean nokeyw = FALSE; /∗ no keywords being flagged ∗/ boolean prccont; /∗ continue last procedure ∗/ boolean code cmnts = TRUE; /∗ Treat @, etc specially in comments ∗/ boolean code latex = TRUE; /∗ Treat @, etc specially in L^{A}TEX ∗/ #if nospaceincomment

boolean linehead;  /∗ at beginning of line or after whitespace ∗/ 150

#endif int lastout; /∗ (extended) last character to outchar ∗/ 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 ∗/
int  reclevel;  /∗ current record nesting level ∗/

char ∗fname = ""; /∗ File being read ∗/

int  lineno;  /∗ Line number in that file ∗/  160
int  linenocode;  /∗ Line number of the source code worked on ∗/

char pname[BUFFERSIZE+1]; /∗ Current procedure name ∗/

/∗

∗ The language specific globals ∗/ #ifdef NO PERM D char defsfile[200]=DEFSFILE;

#else  170

char defsbuf[200]="mPi"DEFSFILE; char ∗defsfile=&defsbuf[3]; /∗ name of language definitions file ∗/ #endif char language[PNAMELEN]="mPi"; /∗ the language indicator ∗/ char the buf[BUFFERSIZE+1]; /∗ general purpose buffer ∗/ char ∗buf = the buf + 1; /∗ buf−1 must be valid ∗/ char ∗strings; /∗ store the keywords ∗/ char ∗defs; /∗ language definitions from lgrindef ∗/ char preamble[BUFFERSIZE/4]; /∗ first preamble ∗/

char preambl2[BUFFERSIZE/4]; /∗ file preamble ∗/  180

char config[BUFFERSIZE/4]; /∗ redefinitions within lgrind-environment ∗/ char chartab[BUFFERSIZE/4]; /∗ buffer for chartab modifications ∗/ char ∗l keywds[BUFFERSIZE/2];/∗ keyword table address ∗/ char ∗l prcbeg; /∗ regular expr for procedure begin ∗/ char ∗l noproc; /∗ regexp for lines with NO procedure begin ∗/ char ∗l combeg; /∗ regexp introducing a comment ∗/ char ∗l comend; /∗ regexp ending a comment ∗/ char ∗l acmbeg; /∗ regexp introducing a comment ∗/ char ∗l acmend; /∗ regexp ending a comment ∗/

char ∗l blkbeg; /∗ regexp beginning of a block ∗/ 190

char ∗l blkend; /∗ regexp ending a block ∗/ char ∗l strbeg; /∗ regexp starting string constant ∗/ char ∗l strend; /∗ regexp ending string constant ∗/ char ∗l chrbeg; /∗ regexp starting character constant ∗/ char ∗l chrend; /∗ regexp ending character constant ∗/ char ∗l cdebeg; /∗ regexp starting prog text within comment ∗/ char s cdebeg[PNAMELEN+1]; /∗ actual string corresponding to l cdebeg ∗/ char ∗l cdeend; /∗ regexp ending prog text within comment ∗/ char ∗l texbeg; /∗ regexp starting TEX text in comment ∗/

char ∗l texend;  /∗ regexp ending TEX text in comment ∗/ 200

char ∗l txmbeg; /∗ regexp starting TEX math in comment ∗/ char ∗l txmend; /∗ regexp ending TEX math in comment ∗/ char ∗l tt beg; /∗ regexp starting verbatim text in comment ∗/ char ∗l tt end; /∗ regexp ending typewriter text in comment ∗/ char ∗l at; /∗ regexp for @ in L^{A}TEX text ∗/ char ∗l tt; /∗ regexp for | in L^{A}TEX text ∗/ char ∗l pc; /∗ regexp for % in L^{A}TEX text ∗/ char ∗l id; /∗ string containing valid identifier chars ∗/ char ∗l record; /∗ start of lexical block outside functions ∗/

char l escape;  /∗ character used to escape characters ∗/ 210

boolean l toplex; /∗ procedures only defined at top lex level ∗/ boolean l onecase; /∗ upper & lower case equivalent ∗/ boolean embed = FALSE; /∗ -e seen — do embedded code ∗/ boolean code = TRUE; /∗ Looking at code ∗/ int TabWidth = 8; /∗ Default width of a tab ∗/ /∗

∗ global variables also used by expmatch ∗/

220

extern boolean escaped; /∗ if last character was an escape ∗/ extern char ∗ sstart; /∗ start of the current string ∗/

extern int (∗re strncmp)(const char∗, const char∗, size t); /∗ function to do string compares ∗/

int main(int argc, char ∗∗argv) {

struct stat stbuf;

boolean  hseen = FALSE;  /∗ -h seen  ∗/  230
char  ∗hstring;  /∗ header string to use ∗/
char  ∗programname;  /∗ the zeroeth argument ∗/
boolean  iseen = FALSE;  /∗ -i seen  ∗/

int files done = 0;

defs=(char∗)malloc(2∗BUFFERSIZE); strings=(char∗)malloc(2∗BUFFERSIZE); argc = getredirection(argc, argv);

programname=argv[0];  240

argc−−, argv++;

if (argc==0) {

Internal Help();

return OKEXIT; }

do {

  char∗ cp = "N/A\0N/A\00000\00000:00\00000\0000000";

250

if (argc > 0) { /∗ Help-facility ∗/ if (!strcmp(argv[0], HELPOPTION)) { Internal Help();

return OKEXIT; }

if (!strcmp(argv[0], "-s")) {

Internal Help Language List();

return OKEXIT;  260
}

if (!strcmp(argv[0], "-h")) { hseen = TRUE; if (argc == 1) {

hstring = ""; argc = 0; goto rest;

}

hstring = argv[1];  270

argc−−, argv++; argc−−, argv++; if (argc > 0)

continue; goto rest;

}

/∗ take input from the standard place ∗/ if (!strcmp(argv[0], "-")) {

argc = 0;  280

goto rest; }

/∗ Process embedded text ∗/ if (!strcmp(argv[0], "-e")) {

embed = TRUE; argc−−, argv++; continue;

}

290

/∗ format for inclusion ∗/ if (!strcmp(argv[0], "-i")) {

iseen = TRUE; argc−−, argv++; continue;

}

/∗ indicate no keywords ∗/ if (!strcmp(argv[0], "-n")) {

nokeyw++;  300

argc−−, argv++; continue;

}

/∗ Don’t treat @, etc. specially in comments ∗/ if (!strcmp(argv[0], "-c")) {

code cmnts = FALSE; argc−−, argv++; continue;

}  310

/∗ Do treat @, etc. specially in comments ∗/ if (!strcmp(argv[0], "+c")) {

code cmnts = TRUE; argc−−, argv++; continue;

}

/∗ Don’t treat @, etc. specially in

∗ L^{A}TEX text (embed only) ∗/  320

if (!strcmp(argv[0], "-a")) { code latex = FALSE; argc−−, argv++; continue;

}

/∗ Do treat @, etc. specially in

∗ L^{A}TEX text (embed only) ∗/ if (!strcmp(argv[0], "+a")) {

code latex = TRUE;  330

argc−−, argv++; continue;

}

/∗ Use \texttt for all fonts ∗/ if (!strcmp(argv[0], "-t")) {

sscanf(argv[1], "%d", &TabWidth); argc−−, argv++; argc−−, argv++;

continue;  340

}

/∗ specify the language ∗/ if (!strncmp(argv[0], "-l", 2)) {

strcpy(language, argv[0]+2); argc−−, argv++; continue;

}

#ifndef NO PERM D  350

/∗ specify the language description file for permanent use ∗/ if (!strncmp(argv[0], "-d!", 3))

writeDefsfileInExe(programname, argv[1]); #endif

/∗ specify the language description file ∗/ if (!strncmp(argv[0], "-d", 2)) {

strcpy(defsfile, argv[1]);

argc−−, argv++;

argc−−, argv++;  360

continue; }

/∗ open the file for input ∗/ if (freopen(argv[0], "r", stdin) == NULL) {

perror(argv[0]); return BADEXIT;

}

fname = argv[0];  370

argc−−, argv++; }

rest:

lineno = 0; setpreambles();

/∗ get config data from lgrindef ∗/ if (tgetent(buf, "firstpreamble", defsfile)>0)

parsepreamble(preamble);  380

if (tgetent(buf, "filepreamble", defsfile)>0) parsepreamble(preambl2);

if (tgetent(buf, "configuration", defsfile)>0) parsepreamble(config);

if (tgetent(buf, "chartab", defsfile)>0)

parsechartab();

390

if (iseen && embed) {

fprintf(stderr, "-i makes no sense with -e; -e ignored\n");

embed = FALSE; }

if (!iseen && !embed) { if (files done == 0) printpreamble(preamble); printpreamble(preambl2); if (files done == 0) {

     printf("\\begin{document}\n");  400

}

printf("\\begin{lgrind}\n");

}

printpreamble(config);

if (embed)

printf("%% This document was generated automagically by lgrind.  DO NOT EDIT.\n\n");

if (iseen)

printf("%% Remember to use the lgrind style\n\n");  410

printf("\\Head{");

if (hseen) putstr(hstring); printf("}\n");

setlang();

/∗ initialize the program ∗/

incomm = instr = inchr = escaped = FALSE;  420

incode = latexcode = latex tt = NOTCODE; do at = do tt = TRUE; blklevel = 0; reclevel = 0; for (psptr = 0; psptr < PSMAX; psptr++) { pstack[psptr][0] = '\0'; plstack[psptr] = 0; } psptr = −1;

if (∗fname=='\0') stbuf.st size=0;  430

else { stat(fname, &stbuf); cp = ctime(&stbuf.st mtime); cp[7] = cp[10] = cp[16] = cp[24] = '\0';

}

if (!embed) {

printf("\\File{");

putstr(fname);

printf("}{%s}{%s}{%s}{%s}{%ld}\n", cp+20, cp+4, cp+8, cp+11, 440

stbuf.st size);

/∗ file name, mod year, mod month, mod day, mod time, size ∗/ }

code = FALSE; readfile(stdin); files done++;

if (!iseen && !embed) {

     printf("\\end{lgrind}\n");  450

}

if (code)

     fprintf(stderr, "Reached EOF within code in file %s\n", fname);

} while (argc > 0);

if (!iseen && !embed) {

  printf("\\end{document}\n");

}

return OKEXIT;  460

}

/∗

∗ Internal Help() — show help ∗/

void Internal Help() {

printf("lgrind -- general purpose \"pretty printer\" for use with LaTeX.\n");

printf("usage:  lgrind  [options] <name> ...\n");  470

printf("-e    process embedded text in a LaTeX file\n");
printf("-i    format source code for inclusion in a LaTeX document.\n");
printf("-     take input from standard input.\n");
printf("-n    don't boldface keywords.\n");
printf("-c    don't treat @, etc. specially in comments.\n");
printf("-t <width>   change tab width (default 8).\n");
printf("-h <header>  specifies text in header (default: none).\n");
printf("-d <deffile>  use another language definitions file, default:\
\n           \"%s\"\n", defsfile);

#ifndef NO PERM D  480

printf("-d! <deffile> same as above, but permanently (modifies executable)\n");

#endif

printf("-l<language>  choose the language to use.\n");
printf("-s    show a list of currently known languages.\n\n");
printf("If neither -e nor -i are specified, a complete LaTeX-file is produced.\n");

}

typedef struct langRec {

char ∗name;  490

char ∗alternatives;

struct langRec ∗next; } langRec;

void AddToLList(char ∗line, langRec ∗∗into, int ∗chainLength) {

char ∗token, ∗item, ∗alts;

if (∗line!='#' && ∗line!='\n') {

item=strdup(line);  500

if ((token=strtok(item, "|:\\\n")) == NULL) return; (∗∗into).next=(langRec∗)malloc(sizeof(langRec)); ∗into=(∗∗into).next; (∗∗into).name=strdup(token); (∗chainLength)++; if ((token=strtok(NULL, "|:\\\n")) == NULL) {

(∗∗into).alternatives="\0";

return;

}  510

(∗∗into).alternatives=strdup(token); while ((token=strtok(NULL, "|:\\\n")) != NULL) {

alts=(∗∗into).alternatives;

(∗∗into).alternatives=(char∗)malloc(

strlen((∗∗into).alternatives)+strlen(token)+1); /∗ This is extremely poor style and dangerous. I leave memory ∗ allocated here because after invocation of this help facility ∗ the program will stop anyway. In this context it is safe. ∗/

strcpy((∗∗into).alternatives, alts);  520

strcat((∗∗into).alternatives, ", "); strcat((∗∗into).alternatives, token);

} }

}

void Internal Help Language List() {

FILE ∗tf;

int check, i, llch;  530

langRec ∗lch, ∗rch, ∗currlch; /∗ lch2 was originally “language chain”, but I guess

it’s “left” as well ∗/

printf("When specifying a language case is insignificant. You can use the\n");
printf("name of the language, or, where available, one of the synonyms in\n");
printf("parantheses. Thus the following are legal and mark Tcl/Tk, Pascal\n");

printf("and Fortran input, respectively:\n");
printf("  lgrind -ltcl/tk ...\n");

printf("  lgrind -lpaSCAL ...\n");  540

printf("  lgrind -lf ...\n");
printf("The list of languages currently available in your lgrindef file:\n");

tf = fopen(defsfile, "rt"); if (tf == NULL) {

      fprintf(stderr, "cannot find lgrindef file `%s'\n", defsfile);

return; }

llch=0;  550

currlch=lch=(langRec∗)malloc(sizeof(langRec)); (∗currlch).name=""; ∗config='\n'; do {

check=0;

if (∗config=='\n' | | config[strlen(config)−2]!='\\')

check=1; if (fgets(config, BUFFERSIZE, tf)==NULL) break; if (check!=0) AddToLList(config, &currlch, &llch);

} while (strcmp((∗currlch).name, "EndOfLanguageDefinitions")!=0); 560

rch=lch; for (check=0; check<llch/2; check++)

rch=(∗rch).next; for (check=0; check<llch/2; check++) {

lch=(∗lch).next; rch=(∗rch).next; printf(" %s", (∗lch).name); if (∗((∗lch).alternatives)) printf(" (%s)", (∗lch).alternatives);

else printf("   ");  570

for (i=strlen((∗lch).name)+strlen((∗lch).alternatives); i<30; i++) putchar(' ');

if (rch!=currlch)

{ printf("%s", (∗rch).name); if (∗((∗rch).alternatives)) printf(" (%s)", (∗rch).alternatives);

}

printf("\n"); }

}  580

/∗ ∗ readfile() — read and process a file ∗/ void readfile(FILE ∗fp) {

register char ∗cp; boolean LGinline=FALSE; /∗ Doing %( – %) ∗/

char  term;  /∗ ’]’ or ’)’  ∗/  590
char  ∗atptr;  /∗ start of @ within L^{A}TEX text ∗/
char  ∗atendptr;  /∗ end of @ within L^{A}TEX text ∗/
char  ∗pcptr;  /∗ start of % within L^{A}TEX text ∗/

/∗char ∗pcendptr;∗/ /∗ end of % within L^{A}TEX text ∗/ char temp[BUFFERSIZE]; char fnamebak[100]; int linenobak;

while (fgets(buf, BUFFERSIZE, fp) != NULL) {

cp = &buf[strlen(buf)−1];  600

if (∗cp != '\n') {∗(++cp) = '\n'; ∗(++cp) = '\0'; }

lineno++; cp = buf;

#if nospaceincomment

linehead = TRUE; #endif

lastout = '\0';

if (embed) {

if (!code) {

if (buf[0] == '%' &&  610

(buf[1] == '[' | | buf[1] == '(' | | buf[1] == '#' | | buf[1] == '<' | | buf[1] == '!' | | buf[1] == '@' | | buf[1] == '|')) {

for (cp = buf + 2; ∗cp == ' ' | | ∗cp == '\t'; cp++) ;

/∗ Change the language ∗/ if (buf[1] == '#') {

if (∗cp == '\n')  620

                  fprintf(stderr, "no language seen after %%# in file\

%s at line %d\n", fname, lineno);

else { cp[strlen(cp) − 1] = '\0'; /∗ nuke the '\n' ∗/ while ((∗cp==' ') | | (∗cp=='\t')) cp++; strcpy(language, cp);

                  printf("%% switching to %s\n", language);

setlang(); }

continue;  630

}

/∗ Turn @ or | processing within L^{A}TEX

∗ text on or off. ∗/

if (buf[1] == '@' | | buf[1] == '|') { if (∗cp == '\n')

fprintf(stderr, "no setting seen after %%%c in file\

%s at line %d\n", buf[1], fname, lineno);

else {  640

int flag = (∗cp == '1' | | ∗cp == '+'); if (buf[1] == '@') do at = flag; else do tt = flag;

} continue; }

code = TRUE;  650

/∗ take input from another file or from a shell command ∗/ if (buf[1] == '<' | | buf[1] == '!') { FILE ∗fp; char source = buf[1];

cp[strlen(cp) − 1] = '\0'; /∗ nuke the '\n' ∗/ if (source == '<')

fp = fopen(cp, "r");

else /∗ source == '!' ∗/  660

fp = popen(cp, "r");

if (fp == NULL) { sprintf(temp, "%%%c on `%s' failed", source, cp); perror(temp);

} else { strcpy(temp, cp);

printf("%% start of `%s'\n", temp);

/∗ printf(“\\LGinlinefalse\\LGbegin\\lgrinde\n”); ∗/

/∗ the above was not all incorrect … ∗/  670

printf("\\LGinlinefalse");
printf("\\begin{lgrind}\n");

embed = FALSE; linenobak=lineno; strcpy(fnamebak, fname); lineno=0; strcpy(fname, temp); linenocode=0; readfile(fp); lineno=linenobak; strcpy(fname, fnamebak); embed = TRUE;

      printf("\\end{lgrind}\n");  680

      printf("%% end of `%s'\n", temp);

if (source == '<')

fclose(fp); else pclose(fp);

} code = FALSE; continue; }

690

/∗ embedded inline or displayed code ∗/ linenocode=0; if (buf[1] == '(') { LGinline = TRUE; term = ')';

printf("\\LGinlinetrue");

} else { /∗ buf[1] == '[' ∗/ LGinline = FALSE; term = ']';

printf("\\LGinlinefalse");  700

} if (buf[2] == '\n') {

printf("\\LGbegin\\lgrinde\n");

} else { buf[strlen(buf)−1] = '\0'; printf("%s\\lgrinde\n", &buf[2]); } continue;

} else if (do at &&  710

(atendptr = expmatch(buf, l at, &atptr,

(char ∗) NULL)) != NULL) {

/∗ This is a gross hack. Fix it, dammit ∗/ restart at:

/∗pcendptr =∗/ expmatch(cp, l pc, &pcptr, (char ∗) NULL);

if (pcptr == NULL | | atptr < pcptr) { putVcp(cp, atptr−1);

printf("\\LGinlinetrue\\LGbegin\\lgrinde");

cp = atendptr;  720

atendptr = expmatch(cp, l at, &atptr, (char ∗) NULL); if (atptr != NULL && (pcptr == NULL | | atptr < pcptr)) {

strncpy(temp, cp, (size t)(atptr−cp)); temp[(int)(atptr−cp)] = '\0'; cp = atendptr; linenocode++;

printf("\\L{\\LB{");

putScp(temp);

printf("}}\\egroup\\endlgrinde\\LGend{}");

atendptr = expmatch(cp, l at, &atptr, (char ∗) NULL); 730

if (atptr != NULL) goto restart at; else { fputs(cp, stdout); continue; }

} else {

/∗ multiline ∗/ } } else {

fputs(buf, stdout);  740

continue; }

}

else { fputs(buf, stdout); continue;

} } /∗ if (!code) ∗/

/∗  750

∗ We’re in embedded code. ∗/ if (buf[0] == '%') {

if (buf[1] == '*') { if (term != ']')

fprintf(stderr, "%%* only makes sense in display mode\
in file %s at line %d\n", fname, lineno);

else {

printf("\\endlgrinde\\LGend\n");

printf("\\LGinlinefalse\\LGbegin\\lgrinde[%d]\n", linenocode+1); 760

} continue;

} else if (buf[1] == term) { if (term == ')') printf("\\egroup"); if (buf[2] == '\n')

printf("\\endlgrinde\\LGend\n");

else printf("\\endlgrinde%s", &buf[2]); code = FALSE;

continue;  770

} else if (buf[1] == '=') { /∗ Send literal ∗/ fputs(&buf[2], stdout); continue;

} else if (buf[1] == '[' | | buf[1] == '('

| | buf[1] == ']' | | buf[1] == ')') { fprintf(stderr,

              "Possible nested embedded code in file %s at line %d\n",

fname, lineno);

}

}  780

/∗

∗ Inline code — suppress leading whitespace ∗/ if (LGinline) {

while (isspace(∗cp))

cp++;

} } /∗ if (embed) ∗/

790

if (∗cp == '\f') {

printf("\\NewPage\n");

cp++; if (∗cp == '\n') /∗ some people like ^Ls on their own line ∗/

continue; } prccont = FALSE; linenocode++;

printf("\\L{\\LB{");

putScp(cp);  800

if (!embed && 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 fgets() ∗/

}  810

/∗

∗ Set all of the language-dependent variables ∗/ void setlang(void) {

char ∗p, ∗cp;

int i;  820

/∗ ∗ get the language definition from the defs file ∗/

if (strcmp(language, "mPi") == 0) /∗ none yet defined ∗/ {

i = tgetent(defs, "extensions", defsfile);

if (i == 0) strcpy(language, "c");

else

{  830

cp = strrchr(fname, '.');

if (cp !=NULL)

{

p=buf; cp = tgetstr(cp+1, &p); } if (cp != NULL) strcpy(language, cp); else strcpy(language, "c");

} }

i = tgetent(defs, language, defsfile);  840

if (i == 0) { fprintf(stderr, "no entry for language %s\n", language); exit(BADEXIT);

} else if (i < 0) {

      fprintf(stderr, "cannot find lgrindef file `%s'\n", defsfile);

exit(BADEXIT); }

p = strings;

if (tgetstr("kw", &p) == NULL)  850

nokeyw = TRUE; else {

char ∗∗cpp;

cpp = l keywds; cp = strings; while (∗cp) {

while (∗cp == ' ' | | ∗cp =='\t') ∗cp++ = '\0';

if (∗cp)  860

∗cpp++ = cp; while (∗cp != ' ' && ∗cp != '\t' && ∗cp)

cp++; } ∗cpp = NULL;

}

p = buf; l prcbeg = convexp(tgetstr("pb", &p)); p = buf; l noproc = convexp(tgetstr("np", &p));

p = buf; l combeg = convexp(tgetstr("cb", &p));  870

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));

p = s cdebeg; l cdebeg = convexp(tgetstr("zb", &p));  880

p = buf; l cdeend = convexp(tgetstr("ze", &p)); p = buf; l texbeg = convexp(tgetstr("tb", &p)); p = buf; l texend = convexp(tgetstr("te", &p)); p = buf; l txmbeg = convexp(tgetstr("mb", &p)); p = buf; l txmend = convexp(tgetstr("me", &p)); p = buf; l tt beg = convexp(tgetstr("vb", &p)); p = buf; l tt end = convexp(tgetstr("ve", &p)); p = buf; l record = convexp(tgetstr("rb", &p)); p = buf; cp = tgetstr("id", &p) ;

if (cp)  890

{ l id = (char∗)malloc(strlen(cp)); strcpy(l id, cp);

} else l id = "_";

l tt = convexp("\\|"); l at = convexp("@"); l pc = convexp("%");

900

l escape = '\\'; /∗ abuse of l onecase ∗/ l onecase = tgetflag("cf"); isproc = l onecase ? isprocC : isprocNorm; l onecase = tgetflag("oc"); re strncmp = l onecase ? lc strncmp : strncmp; l toplex = tgetflag("tl");

}

910

/∗

∗ Write out a formatted line of program text ∗/ void putScp(char ∗os) {

register char ∗s = os; /∗ pointer to unmatched string ∗/ register char ∗s1; /∗ temp. string ∗/

char ∗comptr; /∗ start of a comment delimiter ∗/ 920

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 ∗cdeptr; /∗ start of prog text delim within a comment ∗/ char ∗cdeendptr; /∗ end of prog text delim within a comment ∗/

char ∗cdbptr;  /∗ start of prog text delim within a comment ∗/ 930

char ∗cdbendptr; /∗ end of prog text delim within a comment ∗/ char ∗texptr; /∗ start of TEX text delim within a comment ∗/ char ∗texendptr; /∗ end of TEX text delim within a comment ∗/ char ∗txmptr; /∗ start of TEX math delim within a comment ∗/ char ∗txmendptr; /∗ end of TEX math delim within a comment ∗/ char ∗tt ptr; /∗ start of typewriter delim within a comment ∗/ char ∗tt endptr; /∗ end of typewriter delim within a comment ∗/ char ∗blksptr; /∗ start of a lexical block start ∗/ char ∗blksendptr; /∗ end of a lexical block start ∗/

char ∗recsptr;  /∗ start of a lexical block outside functions start ∗/ 940

char ∗recsendptr; /∗ end of a lexical block outside functions start ∗/ char ∗blkeptr; /∗ start of a lexical block end ∗/ char ∗blkeendptr; /∗ end of a lexical block end ∗/

sstart = os; /∗ remember the start for expmatch() ∗/ escaped = FALSE; if (nokeyw | | incomm | | instr)

goto skip; if ((∗isproc)(s) && reclevel == 0) {

printf("\\Proc{");  950

if (strlen(pname)>30)

{ s1=pname+strlen(pname)−1; while (s1>pname && (isalnum(∗s1) | | ∗s1=='_')) s1−−; if (s1!=pname) strcpy(pname, s1+1);

} if (strlen(pname)>32) {

strcpy(pname, pname+strlen(pname)−28); printf("\\dots "); }

putstr(pname);  960

printf("}"); if (!embed && psptr < PSMAX−1) {

++psptr; strncpy(pstack[psptr], pname, PNAMELEN); pstack[psptr][PNAMELEN] = '\0'; plstack[psptr] = blklevel;

} }

skip:

do {  970

/∗ check for string, comment, blockstart, etc ∗/ if (!incomm && !instr && !inchr) {

blkeendptr = expmatch(s, l blkend, &blkeptr, (char ∗) NULL); blksendptr = expmatch(s, l blkbeg, &blksptr, (char ∗) NULL); recsendptr = expmatch(s, l record, &recsptr, (char ∗) NULL); comendptr = expmatch(s, l combeg, &comptr, (char ∗) NULL); acmendptr = expmatch(s, l acmbeg, &acmptr, (char ∗) NULL); strendptr = expmatch(s, l strbeg, &strptr, (char ∗) NULL);

chrendptr = expmatch(s, l chrbeg, &chrptr, (char ∗) NULL); 980

/∗ check for end of program text in comment ∗/ if (incode != NOTCODE) {

cdeendptr = expmatch(s, l cdeend, &cdeptr, (char ∗) NULL);

if ((cdeptr == NULL && (comptr != NULL | | acmptr != NULL))

| | (cdeptr != NULL

&& ((comptr != NULL && comptr < cdeptr)

| | ((acmptr != NULL && acmptr < cdeptr))))) {

     fprintf(stderr, "Comments may not be nested within\  990

program text within comments in file %s at\

line %d\n", fname, lineno); s = (comptr != NULL) ? comptr : acmptr; incode = NOTCODE; continue;

} /∗ look to see if there’s a \<zb> in the program text ∗/ cdbendptr = expmatch(s, l cdebeg, &cdbptr, (char ∗) NULL);

if (cdeptr != NULL  1000

&& (strptr == NULL | | cdbptr < strptr) && (chrptr == NULL | | cdbptr < chrptr) && (blksptr == NULL | | cdbptr < blksptr) && (recsptr == NULL | | cdbptr < recsptr) && (blkeptr == NULL | | cdbptr < blkeptr)) { if (cdbptr > s && cdbptr[−1] == l escape) {

putKcp(s, cdbptr−2, FALSE);

printf("\\C{}");

putKcp(s cdebeg, s cdebeg + strlen(s cdebeg) − 1, TRUE);

printf("\\CE{}");  1010

s = cdbendptr;

incode = OTHERCODE;

continue; }

}

comendptr =

expmatch(s, (comtype == STANDARD) ? l comend : l acmend,

&comptr, (char ∗) NULL);

1020

if ((cdeptr == NULL && comptr != NULL)

| | (cdeptr != NULL && (comptr != NULL && comptr < cdeptr))) {

fprintf(stderr, "Unterminated program text within comment\
in file %s at line %d\n", fname, lineno);

printf("\\C{}"); s = comptr; incode = NOTCODE; incomm = TRUE; continue;

}

1030

if (cdeendptr != NULL) {

if ((strptr == NULL | | cdeptr < strptr)

&& (chrptr == NULL | | cdeptr < chrptr) && (blksptr == NULL | | cdeptr < blksptr) && (recsptr == NULL | | cdeptr < recsptr) && (blkeptr == NULL | | cdeptr < blkeptr)) { if (incode == INITCODE && cdeptr == s) {

printf("\\C{}");

putKcp(s cdebeg, s cdebeg + strlen(s cdebeg) − 1, TRUE); 1040

} else { putKcp(s, cdeptr−1, FALSE); printf("\\C{}"); } s = cdeendptr; incode = NOTCODE; incomm = TRUE; continue;

} } else if (strptr == NULL && chrptr == NULL

&& blksptr == NULL && recsptr == NULL  1050

&& blkeptr == NULL) { cdeptr = s; s += strlen(s); putKcp(cdeptr, s−1, FALSE); incode = OTHERCODE; continue;

} /∗ else there is a string/char/block on this line ∗/

incode = OTHERCODE;

} /∗ if (incode) ∗/  1060

/∗ start of a comment? ∗/ if (comptr != NULL

&& (strptr == NULL | | comptr < strptr) && (acmptr == NULL | | comptr < acmptr) && (chrptr == NULL | | comptr < chrptr) && (blksptr == NULL | | comptr < blksptr) && (recsptr == NULL | | comptr < recsptr)

&& (blkeptr == NULL | | comptr < blkeptr)) {  1070

putKcp(s, comptr−1, FALSE); printf("\\C{}"); s = comendptr; putKcp(comptr, comendptr−1, TRUE); incomm = TRUE; comtype = STANDARD; continue;

}

/∗ start of an alternate-form comment? ∗/  1080

if (acmptr != NULL && (strptr == NULL | | acmptr < strptr) && (chrptr == NULL | | acmptr < chrptr) && (blksptr == NULL | | acmptr < blksptr) && (recsptr == NULL | | acmptr < recsptr) && (blkeptr == NULL | | acmptr < blkeptr)) { putKcp(s, acmptr−1, FALSE); printf("\\C{}"); s = acmendptr;

putKcp(acmptr, acmendptr−1, TRUE);  1090

incomm = TRUE; comtype = ALTERNATE; continue;

}

/∗ start of a string? ∗/ if (strptr != NULL

&& (chrptr == NULL | | strptr < chrptr) && (blksptr == NULL | | strptr < blksptr)

&& (recsptr == NULL | | strptr < recsptr)  1100

&& (blkeptr == NULL | | strptr < blkeptr)) { putKcp(s, strptr−1, FALSE); printf("\\S{}"); s = strendptr; putKcp(strptr, strendptr−1, FALSE); instr = TRUE; continue;

}

/∗ start of a character string? ∗/  1110

if (chrptr != NULL && (blksptr == NULL | | chrptr < blksptr) && (recsptr == NULL | | chrptr < recsptr) && (blkeptr == NULL | | chrptr < blkeptr)) { putKcp(s, chrptr−1, FALSE); printf("\\S{}"); s = chrendptr; putKcp(chrptr, chrendptr−1, FALSE); inchr = TRUE;

continue;  1120

}

/∗ end of a lexical block ∗/ if (blkeptr != NULL) {

if ((blksptr == NULL | | blkeptr < blksptr) && (recsptr == NULL | | blkeptr < recsptr)) { putKcp(s, blkeendptr − 1, FALSE); s = blkeendptr; blklevel−−;

if (reclevel) reclevel−−;  1130

else 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;  1140

}

continue; }

}

/∗ start of a lexical block ∗/ if (blksptr != NULL) {

putKcp(s, blksendptr − 1, FALSE);

s = blksendptr;

blklevel++;  1150

continue; }

/∗ start of a lexical block outside functions ∗/ if (recsptr != NULL) {

putKcp(s, recsendptr − 1, FALSE);

s = recsendptr;

blklevel++;

reclevel++;

continue;  1160

}

/∗ check for end of comment ∗/ } else if (incomm) {

cdeendptr = expmatch(s, l cdebeg, &cdeptr, (char ∗) NULL); texendptr = expmatch(s, l texbeg, &texptr, (char ∗) NULL); txmendptr = expmatch(s, l txmbeg, &txmptr, (char ∗) NULL); tt endptr = expmatch(s, l tt beg, &tt ptr, (char ∗) NULL);

1170

if (code cmnts) {

/∗ Check for program text within comment ∗/

if (cdeptr != NULL && (texptr == NULL | | cdeptr < texptr) && (tt ptr == NULL | | cdeptr < tt ptr) && (txmptr == NULL | | cdeptr < txmptr)) { putKcp(s, cdeptr−1, TRUE); printf("\\CE{}");

s = cdeendptr;  1180

incode = INITCODE; incomm = FALSE; /∗ Check for TEX text within comment ∗/ if (texptr != NULL

&& (tt ptr == NULL | | texptr < tt ptr) && (txmptr == NULL | | texptr < txmptr)) { putKcp(s, texptr−1, TRUE);

s = texendptr;  1190

if ((texendptr =

expmatch(s, l texend, &texptr, (char ∗) NULL)) != NULL) { putchar('{'); putVcp(s, texptr−1); putchar('}'); s = texendptr;

} else {

  fprintf(stderr, "LaTeX text within a comment must all be\
on one line (file %s at line %d)\n", fname, lineno);

s += strlen(s); }

continue;  1200

}

/∗ Check for typewriter text within comment ∗/ if (tt ptr != NULL

&& (txmptr == NULL | | tt ptr < txmptr)) { putKcp(s, tt ptr−1, TRUE); s = tt endptr; if ((tt endptr =

expmatch(s, l tt end, &tt ptr, (char ∗) NULL)) != NULL) {

printf("\\texttt{");  1210

putKcp(s, tt ptr−1, TRUE);

printf("}");

s = tt endptr; } else {

  fprintf(stderr, "typewriter text within a comment must all\
be on one line (file %s at line %d)\n\t%s",

fname, lineno, s);

s += strlen(s); }

continue;  1220

}

/∗ Check for TEX math within comment ∗/ if (txmptr != NULL) {

putKcp(s, txmptr−1, TRUE); s = txmendptr; if ((txmendptr =

expmatch(s, l txmend, &txmptr, (char ∗) NULL)) != NULL) { putchar('$'); putVcp(s, txmptr−1); putchar('$');

s = txmendptr;  1230

} else {

     fprintf(stderr, "TeX math within a comment must all be\
on one line (file %s at line %d)\n", fname, lineno);

s += strlen(s); } continue;

} } /∗ if (code cmnts) ∗/

if ((comendptr =  1240

expmatch(s, (comtype == STANDARD) ? l comend : l acmend, (char ∗∗) NULL, (char ∗) NULL)) != NULL) {

putKcp(s, comendptr−1, TRUE); s = comendptr; incomm = FALSE; printf("\\CE{}");

} else { comptr = s; s += strlen(s);

putKcp(comptr, s−1, TRUE);  1250

} continue;

/∗ check for end of string ∗/ } else if (instr) {

if ((strendptr =

expmatch(s, l strend, (char ∗∗) NULL, (char ∗) NULL)) != NULL) { putKcp(s, strendptr−1, TRUE); s = strendptr;

instr = FALSE;  1260

printf("\\SE{}"); } else {

strptr = s; s += strlen(s); putKcp(strptr, s−1, TRUE);

} continue;

/∗ check for end of character string ∗/

} else if (inchr) {  1270

if ((chrendptr =

expmatch(s, l chrend, (char ∗∗) NULL, (char ∗) NULL)) != NULL) { putKcp(s, chrendptr−1, TRUE); s = chrendptr; inchr = FALSE; printf("\\SE{}");

} else { chrptr = s; s += strlen(s);

putKcp(chrptr, s−1, TRUE);  1280

}

continue; }

/∗ print out the line ∗/

chrptr = s; s += strlen(s); putKcp(chrptr, s−1, FALSE);

} while (∗s);  1290

}

/∗

∗ Output a L^{A}TEX command to tab to column “col” (see the documentation ∗ for a partial explanation of the bizarre brace arrangement). ∗/ #define tabto(col) printf("}\\Tab{%d}{", col);

1300

/∗

∗ islidchr() is TRUE for things that can begin identifiers;

∗ isidchr is TRUE of identifier constituents. ∗/ #define islidchr(c)(((isalpha(c) | | (strchr(l id, c))) && c!=0)) #define isidchr(c)(((isalnum(c) | | (strchr(l id, c))) && c!=0))

/∗

∗ Get the identifier starting at s. It is assumed that s may indeed  1310

∗ start an identifier. Value is > 0 for a keyword — the count of

∗ characters in the keyword — and < 0 if not a keyword, in which

∗ case the value returned is the negative of the number of bytes in

∗ the matched identifier. ∗

∗ This function checks nokeyw and won’t check to see if the

∗ identifier found is a keyword if it is TRUE. ∗/ int getid(char ∗s)

{  1320

char ∗∗ss = l keywds; int i = 1; char ∗cp = s; int firstc = ∗s;

while (++cp, isidchr(∗cp))

i++; if (nokeyw)

return −i;

while ((cp = ∗ss++) != '\0') {  1330

if (!l onecase && firstc != ∗cp)

continue; if ((∗re strncmp)(s, cp, i) == 0 && !isidchr(cp[i]))

return i; } return −i;

}

/∗  1340

∗ Calculate the width of a string, including tabs ∗/ int width(register char ∗s, register char ∗os) {

register int i = 0, c;

while (s < os) { c = ∗s++; if (c == '\t') {

/∗ i = (i + 8) &˜ 7; ∗/  1350

i = (i + TabWidth) &˜ (TabWidth−1);

continue; } if (c < ' ')

i += 2; else

i++; } return i;

}  1360

/∗

∗ Write out a portion of the line ∗/ void putKcp(char ∗start, char ∗end, boolean nix) /∗ start Start of string to write

end End of string to write nix Don’t look for identifiers, numbers ∗/

{  1370

int i, c;

while (start <= end) { c = ∗start++; /∗

∗ take care of nice tab stops ∗/ if (c == '\t') {

while (start <= end && ∗start == '\t')

start++;  1380

tabto(width( sstart, start));

continue; }

/∗

∗ First split off numbers. We have a rather ad hoc

∗ definition: A number is a digit followed by any number

∗ of digits or letters, and periods.

∗ (Additionally a number can start with $ (e.g. hex numbers).)

∗ This produces meaningless parses — .2 is parsed as .  1390

∗ followed by the number 2 — but should produce decent

∗ results for most languages (outside of maybe FORTRAN and DCL). ∗/ if (!nix) {

if (c == '#' | | islidchr(c)) { i = getid(−−start); if (i > 0)

printf("\\K{"); else {

printf("\\V{");  1400

i = −i; } while (−−i >= 0) { c = ∗start++; outchar(c);

} putchar('}'); continue;

}

else if (isdigit(c) | | c == '$') {  1410

printf("\\N{"); do {

if (c == 'l')

                           printf("$\\ell$");

else

outchar(c);

c = ∗start++; } while (isalnum(c) | | c == '.'); putchar('}');

start−−;  1420

continue; }

}

outchar(c); }

}

/∗  1430

∗ Write out a portion of the line verbatim ∗/ void putVcp(char ∗start, char ∗end) /∗ start Start of string to write

end End of string to write ∗/ {

for ( ; start <= end; start++) {

putchar(∗start); }

}  1440

/∗

∗ Output a string, escaping special characters ∗/ void putstr(register char ∗cp) {

register int c;

if (cp == NULL)  1450

return; while ((c = ∗cp++) != '\0')

outchar(c); }

/∗ ∗ The following table converts ASCII characters to a printed

∗ representation, taking care of all the L^{A}TEX quoting. N.B.: all

∗ single-character strings are assumed to be equivalent to the  1460

∗ character for that index (i.e., printtab['c'] can’t be "f").

∗ (This is purely for efficiency hacking.) ∗

∗ Notes:

∗ Some pairs of characters are handled specially within outchar();

∗ this table contains no indication when that happens.

∗ ` is output as {`} to avoid various ligatures, such as !`.

∗ The '' and -- (and ---) ligatures are not a problem

∗ because those characters are output as macros anyway. Using {`}

∗ is sufficient since nothing we put out will ever be given to the  1470

∗ hyphenation algorithms anyway. ∗/

char ∗printtab[256] = {

"\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", "\\^[", "\\^\\!","\\^]", "\\^\\^","\\^\\_",
" ",  "!",  "\\3", "\\#", "\\$", "\\%", "\\&", "\\4",

"(",  ")",  "\\*", "+",  ",",  "\\-", ".",  "\\1", 1480

"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",  "[",  "\\2", "]",  "\\5", "\\_",
"{`}", "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",  "\\{", "\\|", "\\}", "\\~", "\\^?", 1490

"\200", "\201", "\202", "\203", "\204", "\205", "\206", "\207",
"\210", "\211", "\212", "\213", "\214", "\215", "\216", "\217",
"\220", "\221", "\222", "\223", "\224", "\225", "\226", "\227",
"\230", "\231", "\232", "\233", "\234", "\235", "\236", "\237",
"\240", "\241", "\242", "\243", "\244", "\245", "\246", "\247",
"\250", "\251", "\252", "\253", "\254", "\255", "\256", "\257",
"\260", "\261", "\262", "\263", "\264", "\265", "\266", "\267",
"\270", "\271", "\272", "\273", "\274", "\275", "\276", "\277",
"\300", "\301", "\302", "\303", "\304", "\305", "\306", "\307",

"\310", "\311", "\312", "\313", "\314", "\315", "\316", "\317", 1500

"\320", "\321", "\322", "\323", "\324", "\325", "\326", "\327",
"\330", "\331", "\332", "\333", "\334", "\335", "\336", "\337",
"\340", "\341", "\342", "\343", "\344", "\345", "\346", "\347",

"\350", "\351", "\352", "\353", "\354", "\355", "\356", "\357",
"\360", "\361", "\362", "\363", "\364", "\365", "\366", "\367",
"\370", "\371", "\372", "\373", "\374", "\375", "\376", "\377",

};

1510

#define EMDASH (−1) /∗

∗ Output one character, fixed up as required. Since there is no call-back

∗ to tell outchar() to flush what it has stored, it must always produce

∗ some output — it can’t simply retain state. This makes certain

∗ translations impossible, but we can live without them for now. … ∗/ void outchar(int c) {

#if nospaceincomment  1520

if ((linehead | | !incomm) && c == ' ') { putchar('_'); goto fin;

} linehead = FALSE;

#else

if (c == ' ') { putchar('_'); goto fin;

}  1530

#endif switch (lastout) { case '.': case '|':

if (c == lastout)

printf("\\,"); break;

case ' ': #if 0 /∗ gvr hates this trick ∗/

if (incomm && c == '-') {  1540

printf("---");

c = EMDASH; /∗ For future cleverness… ∗/ goto fin;

} #endif

break; case '[':

if (c == ']')

printf("\\,");

break;  1550

case '-':

if (c == '>')

printf("\\!"); break;

case '<':

if (c == '-')

printf("\\!"); break;

}

if (incomm && c == '-') {  1560

putchar('-'); goto fin;

} printtab[(unsigned char)c][1] ?

printf("%s", printtab[(unsigned char)c]) : putchar(c); fin:

lastout = c; }

1570

/∗

∗ Redefine Chartab from lgrindef file ∗/ void parsechartab(void) {

char ∗i=buf, ∗j=chartab, c; int charnum=0;

while (∗i++!=':') {}  1580

while ((c=∗i++)!='\0') { if (c==':') continue; charnum=(c>='0' && c<='9') ? (int)c−48 : tolower(c)−87; charnum=charnum << 4; c=∗i++; charnum+=(c>='0' && c<='9') ? (int)c−48 : tolower(c)−87; i++;

printtab[charnum]=j; while (∗i!=':')

if (∗i=='\\') {  1590

switch (∗++i) { case ':': ∗j++=':'; break; case '\\': ∗j++='\\'; break;

}; i++; } else ∗j++=∗i++;

∗j++='\0'; }

}

1600

/∗

∗ Look for a procedure beginning on this line ∗/ boolean isprocNorm(char ∗s) {

pname[0] = '\0'; if ((!l toplex | | blklevel == 0)

&& expmatch(s, l prcbeg, (char ∗∗) NULL, pname) != NULL

&& expmatch(s, l noproc, (char ∗∗) NULL, (char ∗) NULL) == NULL) 1610

return TRUE; return FALSE;

}

/∗ Special version of the above for C functions ∗/ boolean isprocC(char ∗s) {

char cheat[BUFFERSIZE]; char ∗j=s, ∗i=cheat;

boolean comm=FALSE, string=FALSE, schar=FALSE; 1620

int brack=0;

if (blklevel!=0) return FALSE; while (∗j) {

if (∗j=='"' && !comm && !schar) string=(string+1)%2;

else if (∗j=='\'' && !comm && !string) schar=(schar+1)%2;

else if (∗j=='\\' && !comm) j++;

else if (!comm && !string && !schar && ∗j=='{') brack++;

else if (!comm && !string && !schar && ∗j=='}') brack−−;  1630

else if (!comm && !string && !schar && ∗j=='#') break;

else if (!comm && !string && !schar && ∗j=='/' && ∗(j+1)=='/') break; else if (!comm && !string && !schar && ∗j=='/' && ∗(j+1)=='*') comm=TRUE; else if ( !string && !schar && ∗j=='*' && ∗(j+1)=='/')

{comm=FALSE; j++;} else if (!brack && !comm && !string && !schar)

∗i++=∗j; j++;

}

∗i = '\0';  1640

return isprocNorm(cheat); }

/∗

∗ getredirection() is intended to aid in porting C programs

∗ to VMS (Vax-11 C) which does not support > and <

∗ I/O redirection. With suitable modification, it may

∗ useful for other portability problems as well.  1650

∗

∗ Modified, 24-Jan-86 by Jerry Leichter

∗ When creating a new output file, force the maximum record size to ∗ 512; otherwise, it ends up as 0 (though the C I/O system won’t write ∗ a record longer than 512 bytes anyway) which will cause problems if ∗ the file is later opened for APPEND — if the maximum record size ∗ is 0, C will use the length of the longest record written to the file ∗ for its buffer! ∗/

1660

#ifdef vms # include <stdio.h> # include <errno.h>

int getredirection(argc, argv)

int argc;

char ∗∗argv; /∗

∗ Process VMS redirection args. Exit if any error is seen.  1670

∗ If getredirection() processes an argument, it is erased

∗ from the vector. getredirection() returns a new argc value. ∗

∗ Warning: do not try to simplify the code for VMS. The code

∗ presupposes that getredirection() is called before any data is

∗ read from stdin or written to stdout. ∗

∗ Normal usage is as follows: ∗

∗  main(argc, argv)  1680
∗  int  argc;
∗  char  ∗argv[ ];

∗ {

∗ argc = getredirection(argc, argv);

∗ } ∗/ { register char ∗ap; /∗ Argument pointer ∗/

int  i;  /∗ argv[ ] index ∗/
int  j;  /∗ Output index ∗/  1690

int file; /∗ File descriptor ∗/

for (j = i = 1; i < argc; i++) { /∗ Do all arguments ∗/ switch (∗(ap = argv[i])) { case '<': /∗ <file ∗/

if (freopen(++ap, "r", stdin) == NULL) { perror(ap); /∗ Can’t find file ∗/ exit(errno); /∗ Is a fatal error ∗/

}

break;  1700

case '>': /∗ >file or >>file ∗/

if (∗++ap == '>') { /∗ >>file ∗/ /∗

∗ If the file exists, and is writable by us,

∗ call freopen() to append to the file (using the

∗ file’s current attributes). Otherwise, create

∗ a new file with “vanilla” attributes as if

∗ the argument was given as >filename.

∗ access(name, 2) is TRUE if we can write on  1710

∗ the specified file.

∗/

if (access(++ap, 2) == 0) {

if (freopen(ap, "a", stdout) != NULL)

break; /∗ Exit case statement ∗/ perror(ap); /∗ Error, can’t append ∗/ exit(errno); /∗ After access test ∗/

} /∗ If file accessable ∗/ }

/∗  1720

∗ On VMS, we want to create the file using “standard”

∗ record attributes. create(…) creates the file

∗ using the caller’s default protection mask and

∗ “variable length, implied carriage return”

∗ attributes. dup2() associates the file with stdout. ∗/ if ((file = creat(ap, 0, "rat=cr", "rfm=var", "mrs=512")) == −1

| | dup2(file, fileno(stdout)) == −1) {

perror(ap); /∗ Can’t create file ∗/

exit(errno);  /∗ is a fatal error  ∗/  1730
}  /∗ If > creation  ∗/
break;  /∗ Exit case test  ∗/

default:

argv[j++] = ap; /∗ Not a redirector ∗/ break; /∗ Exit case test ∗/

} } /∗ For all arguments ∗/ argv[j] = NULL; /∗ Terminate argv[ ] ∗/

return j; /∗ Return new argc ∗/ 1740

}

#else /∗ !vms ∗/

int getredirection(int argc, char ∗∗argv) /∗

∗ Dummy routine.

∗/ {

return argc;  1750

}

#endif /∗ !vms ∗/

/∗

∗ Parses the lgrindef file for a preamble ∗/ void parsepreamble(char ∗destination) {

char ∗i=buf, ∗j=destination;  1760
while (∗i++!=':') {}
while (∗i) {
if (∗i=='\\') {

switch (∗++i) { case 'n': ∗j++='\n'; break; case 't': ∗j++='\t'; break; case 'f': ∗j++='\f'; break; case '\\': ∗j++='\\'; break;

        default: fprintf(stderr, "Bad preamble entry: incorrect '\\'\n"); 1770

}; i++;

} else ∗j++=∗i++;

     ∗j='\0';

}

}

/∗

∗ Sets the default preambles ∗/

void setpreambles(void)  1780

{

strcpy(preamble,

"\\documentclass[a4paper]{article}\n\
\\usepackage[procnames,noindent]{lgrind}\n\
\\usepackage{fancyhdr}\n\
\\pagestyle{fancy}\n");

strcpy(preambl2,

"\\lhead[\\fancyplain{}{\\bf\\thepage}]\
{\\fancyplain{}{\\bf \f}}\n\

\\rhead[\\fancyplain{}{\\bf \f}]\  1790

{\\fancyplain{}{\\bf\\thepage}}\n\
\\cfoot{}\n");

strcpy(config,""); }

/∗

∗ prints a preamble, substituting \f with the current

∗ file’s name (sorry, no more form feeds) ∗/

void printpreamble(char ∗string)  1800

{ char ∗i; while (∗string)

if (∗string=='\f') { i=fname−1;

while (∗(++i)) outchar(∗i); string++;

} else putchar(∗string++);

}  1810

#ifndef NO PERM D /∗

∗ try to modify the executable file to store the new lgrindef-file ∗/ void writeDefsfileInExe(char ∗progname, char ∗newdefsfile) {

char ∗buffer, ∗i; FILE ∗progfile;

long pos=0; 1820

int ret;

boolean found=FALSE;

buffer = (char∗) malloc(30000);

/∗ So small? Why in chunks? Well, it’s gotta run on machines with

only limited access to their resources (you know, the IBM PC) ∗/

if (access(progname, 6)==0 && buffer) /∗ read and write permission? ∗/ {

progfile=fopen(progname, "r+b");  1830

fread(buffer, 1, 200, progfile); do {

ret=fread(buffer+200, 1, 29500, progfile); for(i=buffer; i<buffer+ret; i++) if (∗i=='m' && ∗(i+1)=='P' && ∗(i+2)=='i'

&& strcmp(defsfile, i+3)==0) { pos+=i−buffer; found=TRUE;

}

memcpy(buffer, buffer+29500, 200);  1840

pos+=29500; } while (ret==29500 && !found); if (found) {

fseek(progfile, pos−29500+3, 0); fwrite(newdefsfile, strlen(newdefsfile)+1, 1, progfile);

} fclose(progfile);

} free(buffer);

}  1850

#endif

Appendix B

regexp.c

B.1 Header

/∗ Created 19 February 1996 ∗/ typedef int boolean; #define TRUE 1 #define FALSE 0 int lc strncmp(const char ∗s1, const char ∗s2, size t len); char ∗convexp(char ∗re); char ∗expmatch(register char ∗s, register char ∗re, char ∗∗strtptr, char ∗mstring);

B.2 Code

#ifndef lint

static char ∗sccsid="@(#)regexp.c 1.2 (LBL) 4/12/85";

static char Version[ ] =

  "$Id: regexp.c,v 1.2 91/10/01 00:30:20 gvr Exp $";

#endif

/∗

∗ Regular expression matching routines for lgrind/tgrind/tfontedpr. ∗

∗ These routines were written by Dave Presotto (I think) for vgrind.  10

∗ Minor mods & attempts to improve performance by Van Jacobson

∗ (van@lbl-rtsg) and Chris Torek (chris@maryland). ∗

∗ Modifications.

∗ ————–

∗ Sep 91 George V Reilly Fixed up some bogus uses of NIL and NULL.

∗ 30 Mar 85 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). 20

∗ 11 Dec 84 Dave Presotto Written. ∗/

#include <stdio.h> #include <malloc.h> #include <ctype.h> #include <string.h>

#include "regexp.h"

30

#define makelower(c) (isupper((c)) ? tolower((c)) : (c))

extern char ∗l id; static void expconv(void); /∗ forward declaration ∗/

int (∗re strncmp)(const char ∗, const char ∗, size t); /∗ function used by expmatch() to compare

∗ strings. The caller should make it point to

∗ strncmp() if case is significant &

∗ lc strncmp() otherwise.  40

∗/

/∗ lc strncmp() — like strncmp() except that we convert the

∗ first string to lower case before comparing. ∗/ int lc strncmp(register const char ∗s1, register const char ∗s2, register size t len) {

while (len−− > 0)

if (∗s2 − makelower(∗s1))  50

return 1; else

s2++, s1++;

return 0; }

/∗ The following routine converts an irregular expression to

∗ internal format.  60

∗

∗ Either meta symbols (\a, \d, or \p) or character strings

∗ or operations (alternation or parenthesizing) 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  70

∗ 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 80

∗

∗ operations := descriptor

∗ symbol

∗ character count ∗/

/∗

∗ handy macros for accessing parts of match blocks

∗/  90

#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 ∗/  100

#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  110

#define OPER 16

char ∗ure; /∗ pointer current position in unconverted exp ∗/ char ∗ccre; /∗ pointer to current position in converted exp ∗/

/∗

∗ Convert an irregular expression to the internal form

∗/  120

char ∗convexp(char ∗re) /∗ re unconverted irregular expression ∗/ {

register char ∗cre; /∗ pointer to converted regular expression ∗/

/∗ allocate room for the converted expression ∗/ if (re == NULL)

return NULL; if (∗re == '\0')

return NULL;  130

cre = (char∗)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 (it’s recursive) ∗/  140

expconv(); ∗ccre = 0; return cre;

}

/∗

∗ Actually do the conversion ∗/

static void expconv(void)  150

{ register char ∗cs; /∗ pointer to current symbol in converted exp ∗/ register char c; /∗ character being processed ∗/ register char ∗acs; /∗ pointer to last alternate ∗/ register int temp; /∗ let the conversion begin ∗/ acs = NULL; cs = NULL;

while (∗ure != '\0') {  160

switch (c = ∗ure++) {

case '\\': switch (c = ∗ure++) {

/∗ escaped characters are just characters ∗/ default:

if (cs == NULL | | (∗cs & STR) == 0) { cs = ccre;

∗cs = STR;  170

SCNT(cs) = 1;

ccre += 2; } else

SCNT(cs)++; ∗ccre++ = c; break;

/∗ normal(?) metacharacters ∗/ case 'a':

case 'd':  180

case 'e': case 'p':

if (acs != NULL && acs != cs) { do { temp = OCNT(acs); OCNT(acs) = ccre − acs; acs −= temp; } while (temp != 0); acs = NULL;

}  190

cs = ccre; ∗cs = META; MSYM(cs) = c;

ccre = MNEXT(cs); break;

} break;

/∗ just put the symbol in ∗/

case '^':  200

case '$':

if (acs != NULL && acs != cs) { do {

temp = OCNT(acs);

OCNT(acs) = ccre − acs; acs −= temp;

} while (temp != 0);

acs = NULL; }

cs = ccre;  210

∗cs = META; MSYM(cs) = c; ccre = MNEXT(cs); break;

/∗ mark the last match sequence as optional ∗/ case '?':

if (cs)

∗cs = ∗cs | OPT;
break;  220

/∗ recurse and define a subexpression ∗/ case '(':

if (acs != NULL && acs != cs) {

do { temp = OCNT(acs); OCNT(acs) = ccre − acs; acs −= temp;

} while (temp != 0);

acs = NULL;  230

} cs = ccre; ∗cs = OPER; OSYM(cs) = '('; ccre = ONEXT(cs); expconv(); OCNT(cs) = ccre − cs; /∗ offset to next symbol ∗/ break;

/∗ return from a recursion ∗/  240

case ')':

if (acs != NULL) {

do { temp = OCNT(acs); OCNT(acs) = ccre − acs; acs −= temp;

} while (temp != 0);

acs = NULL; }

cs = ccre;  250

∗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 != NULL && acs != cs)  260

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 filled ∗/

break;  270

/∗ if it’s not a metasymbol, just build a character string ∗/ default:

if (cs == NULL | | (∗cs & STR) == 0) { cs = ccre; ∗cs = STR; SCNT(cs) = 1; ccre = SSTR(cs);

} else

SCNT(cs)++;  280

∗ccre++ = c; break;

} } if (acs != NULL) {

do { temp = OCNT(acs); OCNT(acs) = ccre − acs; acs −= temp;

} while (temp != 0);  290

acs = NULL; } return;

} /∗ end of expconv() ∗/ /∗

∗ The following routine recognises an irregular expression

∗ with the following special characters:

∗  300
∗  \?  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 mstring.

∗  \|  alternation
∗  \( \)  grouping used mostly for alternation and

∗ optionality

∗  310

∗ 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. ∗/

320

boolean escaped; /∗ true if we are currently escaped ∗/ char ∗ sstart; /∗ start of string. Set in putScp(). ∗/

char ∗expmatch( 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 ∗/

{  330

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 = '\0'; if (re == NULL)

return NULL;  340

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)));  350

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) {  360

/∗ the match is optional ∗/ cs = SNEXT(cs); matched = 1; /∗ indicate a successful match ∗/

} else {

/∗ no match: error return ∗/ return NULL;

}

break;  370

/∗ 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 ∗/ 380

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);  390

if (ptr != NULL) {

/∗ the subexpression matched ∗/

matched = 1;

s = ptr; } else if (∗cs & ALT) {

/∗ alternation: skip to next expression ∗/

matched = 0;

} else if (∗cs & OPT) {  400

/∗ the match is optional ∗/

matched = 1; /∗ indicate a successful match ∗/ } else {

/∗ no match: error return ∗/

return NULL; } cs = OPTR(cs);

break;  410

} break;

/∗ try to match a metasymbol ∗/ case META:

switch (MSYM(cs)) {

/∗ try to match anything and remember what was matched ∗/ case 'p':

/∗  420

∗ 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 != NULL && s1 != s) {

/∗ we have a match: remember the match ∗/  430

if (mstring) { strncpy(mstring, s, (size t)(s1 − s));

mstring[(int)(s1 − s)] = '\0'; } return ptr;

} else if (ptr != NULL && (∗cs & OPT)) {

/∗ \p was optional, so no match is ok ∗/

return ptr;  440

} else if (ptr != NULL) {

/∗ not optional and we still matched ∗/ return NULL;

} if (!isalnum(∗s1) && !strchr(l id, ∗s1))

return NULL; if (∗s1 == '\\')

escaped = escaped ? FALSE : TRUE;  450

else

escaped = FALSE; } while (∗s1++); return NULL;

/∗ try to match anything ∗/ case 'a':

/∗

∗ This is really the same as trying the match the

∗ remaining parts of the expression to any subset  460

∗ 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  470

∗ 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 NULL;  480

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;  490

s = s1;

break; }

} ptr = expmatch(s1, MNEXT(cs), strtptr, mstring); if (ptr != NULL && (s1 != s | | ∗cs & OPT)) {

/∗ we have a match ∗/ if (strtptr)

∗strtptr = s1;  500

return ptr;

} else if (ptr != NULL) {

/∗ not optional and we still matched ∗/ return NULL;

} if (∗s1 == '\\')

escaped = escaped ? FALSE : TRUE;  510

else

escaped = FALSE; } while (∗s1++); return NULL;

/∗ fail if we are currently escaped ∗/ case 'e':

if ( escaped)

return NULL;

cs = MNEXT(cs);  520

break;

/∗ match any number of tabs and spaces ∗/ case 'd': ptr = s; while (∗s == ' ' | | ∗s == '\t')

s++;

if (s != ptr | | s == sstart) {

530

/∗ 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) {

540

/∗ try the next part ∗/ matched = 0; cs = MNEXT(cs);

} else if (∗cs & OPT) {

/∗ doesn’t matter ∗/ matched = 1; cs = MNEXT(cs);

} else

550

/∗ no match: error return ∗/ return NULL;

break;

/∗ check for end of line ∗/ case '$':

if (∗s == '\0' | | ∗s == '\n') {

/∗ match: be happy ∗/  560

s++; matched = 1; cs = MNEXT(cs);

} else if (∗cs & ALT) {

/∗ try the next part ∗/ matched = 0; cs = MNEXT(cs);

} else if (∗cs & OPT) {

570

/∗ doesn’t matter ∗/ matched = 1; cs = MNEXT(cs);

} else

/∗ no match: error return ∗/ return NULL;

break;

/∗ check for start of line ∗/  580

case '^': if (s == sstart) {

/∗ match: be happy ∗/ matched = 1; cs = MNEXT(cs);

} else if (∗cs & ALT) {

/∗ try the next part ∗/

matched = 0;  590

cs = MNEXT(cs); } else if (∗cs & OPT) {

/∗ doesn’t matter ∗/ matched = 1; cs = MNEXT(cs);

} else

/∗ no match: error return ∗/

return NULL;  600

break;

/∗ end of a subexpression: return success ∗/ case ')':

return s; } break;

} }

return s;  610

}

Appendix C

lgrindef.c

C.1 Header

/∗ Created 19 February 1996 ∗/ #define BUFFERSIZE 8192 int tgetent(char ∗bp, char ∗name, char ∗file); int tgetnum(char ∗id); int tgetflag(char ∗id); char ∗tgetstr(char ∗id, char ∗∗area);

C.2 Code

#ifndef lint

static char sccsid[ ] = "@(#)lgrindef.c 4.3 (Berkeley) 12/11/84";

static char Version[ ] =

  "$Id: lgrindef.c,v 1.2 91/10/01 00:29:08 gvr Exp $";

#endif

/∗ Copyright (c) 1979 Regents of the University of California ∗/

#define MAXHOP 32 /∗ max number of tc= indirections ∗/

10

#include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <string.h>

#include "lgrindef.h"

/∗

∗ grindcap — routines for dealing with the language definitions data base ∗ (code stolen almost totally from termcap)

∗  20

∗ BUG: Should use a “last” pointer in tbuf, so that searching

∗ for capabilities alphabetically would not be a n^{2}/2

∗ process when large numbers of capabilities are given.

∗ Note: If we add a last pointer now we will screw up the

∗ tc capability. We really should compile termcap. ∗

∗ Essentially all the work here is scanning and decoding escapes

∗ in string capabilities. We don’t use stdio because the editor

∗ doesn’t, and because living w/o it is not hard.

∗/  30

static char ∗tbuf; static char ∗filename; static int hopcount = 0; /∗ detect infinite loops in termcap ∗/

static char ∗tskip(char ∗); static char ∗tdecode(char ∗, char ∗∗);

/∗

∗ tnchktc: check the last entry, see if it’s tc=xxx. If so,  40

∗ recursively find xxx and append that entry (minus the names)

∗ to take the place of the tc=xxx entry. This allows termcap

∗ entries to say “like an HP2621 but doesn’t turn on the labels”.

∗ Note that this works because of the left to right scan. ∗/ static int tnchktc(void) {

register char ∗p, ∗q;

char tcname[16]; /∗ name of similar terminal ∗/

char tcbuf[BUFFERSIZE];  50

char ∗holdtbuf = tbuf;

int l;

p = tbuf + strlen(tbuf) − 2; /∗ before the last colon ∗/

while (∗−−p != ':')

if (p < tbuf) {

/∗ Not very informative. Wrong BTW (for the configs). Shut up. ∗/ /∗ write(2, “Bad lgrind entry\n”, 18); ∗/

return 0;

}  60

p++;

/∗ p now points to beginning of last field ∗/

if (p[0] != 't' | | p[1] != 'c')

return 1; strcpy(tcname,p+3); q = tcname; while (q && ∗q != ':')

q++;

∗q = '\0';

if (++hopcount > MAXHOP) {  70

  fprintf(stderr, "Infinite tc= loop\n");

return 0; } if (tgetent(tcbuf, tcname, filename) != 1)

return 0; for (q = tcbuf; ∗q != ':'; q++)

; l = (int)(p − holdtbuf + strlen(q)); if (l > BUFFERSIZE) {

  fprintf(stderr, "LGrind entry too long\n");  80

q[(int)(BUFFERSIZE − (p−tbuf))] = '\0'; } strcpy(p, q+1); tbuf = holdtbuf; return 1;

}

/∗

∗ tnamatch deals with name matching. The first field of the termcap 90

∗ entry is a sequence of names separated by |’s, so we compare

∗ against each such name. The normal : terminator after the last

∗ name (before the first field) stops us. ∗/ static int tnamatch(char ∗np) {

register char ∗Np, ∗Bp;

Bp = tbuf;

if (∗Bp == '#')  100

return 0; for (;;) {

for (Np = np; ∗Np && toupper(∗Bp) == toupper(∗Np); Bp++, Np++)

continue;

if (∗Np == '\0' && (∗Bp == '|' | | ∗Bp == ':' | | ∗Bp == '\0'))

return 1;

while (∗Bp != '\0' && ∗Bp != ':' && ∗Bp != '|')

Bp++;

if (∗Bp == '\0' | | ∗Bp == ':')

return 0;  110

Bp++;

} }

/∗

∗ Skip to the next field. Notice that this is very dumb, not

∗ knowing about \: escapes or any such. If necessary, :’s can

∗ be put into the termcap file in octal.

∗/  120

static char ∗tskip(register char ∗bp) {

while (∗bp && ∗bp != ':')

bp++; if (∗bp == ':')

bp++; return bp;

}

130

/∗

∗ Return the (numeric) option id.

∗ Numeric options look like

∗  li#80

∗ i.e. the option string is separated from the numeric value by

∗ a # character. If the option is not found we return −1.

∗ Note that we handle octal numbers beginning with 0. ∗/ int tgetnum(char ∗id)

{  140

register int i, base;

register char ∗bp = tbuf;

for (;;) { bp = tskip(bp); if (∗bp == '\0') return −1;

if (∗bp++ != id[0] | | ∗bp == '\0' | | ∗bp++ != id[1])

continue;

if (∗bp == '@')  150

return −1; if (∗bp != '#') continue;

bp++; base = 10; if (∗bp == '0') base = 8; i = 0; while (isdigit(∗bp))

i ∗= base, i += ∗bp++ − '0';  160

return i; }

}

/∗

∗ Handle a flag option.

∗ Flag options are given “naked”, i.e. followed by a : or the end

∗ of the buffer. Return 1 if we find the option, or 0 if it is  170

∗ not given. ∗/ int tgetflag(char ∗id) {

register char ∗bp = tbuf;

for (;;) { bp = tskip(bp); if (!∗bp)

return 0;  180

if (∗bp++ == id[0] && ∗bp != 0 && ∗bp++ == id[1]) {

if (!∗bp | | ∗bp == ':')

return 1; else if (∗bp == '@')

return 0; }

} }

190

/∗

∗ Get a string valued option.

∗ These are given as

∗  cl=^Z

∗ Much decoding is done on the strings, and the strings are

∗ placed in area, which is a ref parameter which is updated.

∗ No checking on area overflow. ∗/ char ∗tgetstr(char ∗id, char ∗∗area)

{  200

register char ∗bp = tbuf, ∗i; register eq; for (;;) {

bp = tskip(bp); if (!∗bp)

return 0; eq=1; for (i=id; ∗i;)

if (∗i++!=∗bp++) { eq=0; break; }  210

if (!eq)

continue; if (∗bp == '@')

return 0; if (∗bp != '=')

continue; bp++; return tdecode(bp, area);

}

}  220

/∗

∗ tdecode does the grunge work to decode the

∗ string capability escapes. ∗/ static char ∗tdecode(register char ∗str, char ∗∗area) {

register char ∗cp;

register char c;  /∗ 30.7.96 – tryal [sic] ∗/  230

cp = ∗area; while ((c = ∗str++) != 0) {

if (c == ':' && ∗(cp−1) != '\\')

break; ∗cp++ = c;

} ∗cp++ = '\0'; str = ∗area;

∗area = cp;  240

return str; }

/∗

∗ Get an entry for terminal name in buffer bp,

∗ from the termcap file. Parse is very rudimentary;

∗ we just notice escaped newlines. ∗/ int tgetent(char ∗bp, char ∗name, char ∗file)

{  250

register char ∗cp; register char c; /∗ 30.7.96 – tryal [sic] ∗/ register int i = 0, cnt = 0; char ibuf[BUFFERSIZE]; FILE ∗tf;

tbuf = bp; tf = 0; filename = file;

tf = fopen(filename, "rt");  260

if (tf == NULL)

return −1; for (;;) {

cp = bp;

for (;;) {

if (i == cnt) { cnt = fread(ibuf, 1, BUFFERSIZE, tf); if (cnt <= 0) { fclose(tf);

return 0;  270

} i = 0;

}

c = ibuf[i++]; if (c == '\n') {

if (cp > bp && cp[−1] == '\\'){ cp−−; continue;

}

break;  280

}

if (cp >= bp+BUFFERSIZE) {

          fprintf(stderr, "LGrind entry too long\n");

break; } else

∗cp++ = c; } ∗cp = '\0';

/∗  290

∗ The real work for the match.

∗/

if (tnamatch(name)) {

fclose(tf);

return tnchktc(); } } }

64