Browse Source

Initial revision

ceriel 37 years ago
parent
commit
846e79a2b0

+ 372 - 0
util/cpp/LLlex.c

@@ -0,0 +1,372 @@
+/* $Header$ */
+/*		    L E X I C A L   A N A L Y Z E R			*/
+
+#include	"idfsize.h"
+#include	"numsize.h"
+#include	"strsize.h"
+
+#include	<alloc.h>
+#include	"input.h"
+#include	"idf.h"
+#include	"LLlex.h"
+#include	"Lpars.h"
+#include	"class.h"
+#include	"bits.h"
+
+/* Data about the token yielded */
+struct token dot;
+
+int ReplaceMacros = 1;		/* replacing macros			*/
+int AccFileSpecifier = 0;	/* return filespecifier <...>		*/
+int AccDefined = 0;		/* accept "defined(...)"		*/
+int UnknownIdIsZero = 0;	/* interpret unknown id as integer 0	*/
+
+char *string_token();
+
+PushLex()
+{
+	DOT = 0;
+}
+
+PopLex()
+{}
+
+int
+LLlex()
+{
+	return (DOT != EOF) ? GetToken(&dot) : EOF;
+}
+
+#define BUFSIZ 1024
+
+int
+GetToken(ptok)
+	register struct token *ptok;
+{
+	char buf[BUFSIZ];
+	register int c, nch;
+
+again:	/* rescan the input after an error or replacement	*/
+	LoadChar(c);
+go_on:
+	if ((c & 0200) && c != EOI)
+		fatal("non-ascii '\\%03o' read", c & 0377);
+	switch (class(c)) {	/* detect character class	*/
+	case STNL:
+		LineNumber++;
+		return ptok->tk_symb = EOF;
+	case STSKIP:
+		goto again;
+	case STGARB:		/* garbage character			*/
+		if (c == '\\') {
+			/* a '\\' is allowed in #if/#elif expression	*/
+			LoadChar(c);
+			if (class(c) == STNL) {	/* vt , ff ?	*/
+				++LineNumber;
+				goto again;
+			}
+			PushBack();
+			c = '\\';
+		}
+		if (040 < c && c < 0177)
+			error("garbage char %c", c);
+		else
+			error("garbage char \\%03o", c);
+		goto again;
+	case STSIMP:	/* a simple character, no part of compound token*/
+		if (c == '/') { /* probably the start of comment	*/
+			LoadChar(c);
+			if (c == '*') { /* start of comment	*/
+				skipcomment();
+				goto again;
+			}
+			else {
+				PushBack();
+				c = '/';	/* restore c	*/
+			}
+		}
+		return ptok->tk_symb = c;
+	case STCOMP:	/* maybe the start of a compound token		*/
+		LoadChar(nch);			/* character lookahead	*/
+		switch (c) {
+		case '!':
+			if (nch == '=')
+				return ptok->tk_symb = NOTEQUAL;
+			PushBack();
+			return ptok->tk_symb = c;
+		case '&':
+			if (nch == '&')
+				return ptok->tk_symb = AND;
+			PushBack();
+			return ptok->tk_symb = c;
+		case '<':
+			if (AccFileSpecifier) {
+				PushBack();	/* pushback nch */
+				ptok->tk_str =
+					string_token("file specifier", '>');
+				return ptok->tk_symb = FILESPECIFIER;
+			}
+			if (nch == '<')
+				return ptok->tk_symb = LEFT;
+			if (nch == '=')
+				return ptok->tk_symb = LESSEQ;
+			PushBack();
+			return ptok->tk_symb = c;
+		case '=':
+			if (nch == '=')
+				return ptok->tk_symb = EQUAL;
+			/*	The following piece of code tries to recognise
+				old-fashioned assignment operators `=op'
+			*/
+			error("illegal character");
+			goto go_on;
+		case '>':
+			if (nch == '=')
+				return ptok->tk_symb = GREATEREQ;
+			if (nch == '>')
+				return ptok->tk_symb = RIGHT;
+			PushBack();
+			return ptok->tk_symb = c;
+		case '|':
+			if (nch == '|')
+				return ptok->tk_symb = OR;
+			PushBack();
+			return ptok->tk_symb = c;
+		}
+	case STIDF:
+	{
+		extern int idfsize;		/* ??? */
+		register char *tg = &buf[0];
+		register char *maxpos = &buf[idfsize];
+		register struct idf *idef;
+
+#define tstmac(bx)	if (!(bits[c] & bx)) goto nomac
+#define cpy		if (Unstacked) EnableMacros(); *tg++ = c
+#define load		LoadChar(c); if (!in_idf(c)) goto endidf
+
+#ifdef DOBITS
+		cpy; tstmac(bit0); load;
+		cpy; tstmac(bit1); load;
+		cpy; tstmac(bit2); load;
+		cpy; tstmac(bit3); load;
+		cpy; tstmac(bit4); load;
+		cpy; tstmac(bit5); load;
+		cpy; tstmac(bit6); load;
+		cpy; tstmac(bit7); load;
+#endif
+
+		for(;;) {
+			if (tg < maxpos) {
+				cpy;
+			}
+			load;
+		}
+	endidf:
+		PushBack();
+		*tg = '\0';	/* mark the end of the identifier */
+		if (ReplaceMacros) {
+			idef = findidf(buf);
+			if ((idef && idef->id_macro && replace(idef))) {
+				goto again;
+			}
+		}
+	nomac:
+		LoadChar(c);
+		while (in_idf(c)) {
+			if (tg < maxpos) *tg++ = c;
+			LoadChar(c);
+		}
+		PushBack();
+		*tg++ = '\0';	/* mark the end of the identifier	*/
+		if (UnknownIdIsZero) {
+			ptok->tk_val = 0;
+			return ptok->tk_symb = INTEGER;
+		}
+		ptok->tk_str = Malloc(idfsize + 1);
+		strcpy(ptok->tk_str, buf);
+		return ptok->tk_symb = IDENTIFIER;
+	}
+	case STCHAR:				/* character constant	*/
+	{
+		register int val = 0, size = 0;
+
+		LoadChar(c);
+		if (c == '\'')
+			error("character constant too short");
+		else
+		while (c != '\'') {
+			if (c == '\n') {
+				error("newline in character constant");
+				LineNumber++;
+				break;
+			}
+			if (c == '\\') {
+				LoadChar(c);
+				if (c == '\n') {
+					LoadChar(c);
+					LineNumber++;
+					continue;
+				}
+				c = quoted(c);
+			}
+			val = val*256 + c;
+			size++;
+			LoadChar(c);
+		}
+		if (size > sizeof(int))
+			error("character constant too long");
+		ptok->tk_val = val;
+		return ptok->tk_symb = INTEGER;
+	}
+	case STNUM:
+	{
+		register char *np = &buf[1];
+		register int base = 10;
+		register int vch;
+		register int val = 0;
+
+		if (c == '0') {
+			*np++ = c;
+			LoadChar(c);
+			if (c == 'x' || c == 'X') {
+				base = 16;
+				LoadChar(c);
+			}
+			else
+				base = 8;
+		}
+		while (vch = val_in_base(c, base), vch >= 0) {
+			val = val*base + vch;
+			if (np < &buf[NUMSIZE])
+				*np++ = c;
+			LoadChar(c);
+		}
+		if (c == 'l' || c == 'L')
+			LoadChar(c);
+		PushBack();
+		ptok->tk_val = val;
+		return ptok->tk_symb = INTEGER;
+	}
+	case STSTR:
+		ptok->tk_str = string_token("string", '"');
+		return ptok->tk_symb = STRING;
+	case STEOI:			/* end of text on source file	*/
+		return ptok->tk_symb = EOF;
+	default:
+		crash("Impossible character class");
+	}
+	/*NOTREACHED*/
+}
+
+skipcomment()
+{
+	register int c;
+
+	NoUnstack++;
+	LoadChar(c);
+	do {
+		while (c != '*') {
+			if (class(c) == STNL)
+				++LineNumber;
+			else
+			if (c == EOI) {
+				NoUnstack--;
+				return;
+			}
+			LoadChar(c);
+		}
+		/* Last Character seen was '*' */
+		LoadChar(c);
+	} while (c != '/');
+	NoUnstack--;
+}
+
+char *
+string_token(nm, stop_char)
+	char *nm;
+{
+	register int c;
+	register int str_size;
+	register char *str = Malloc(str_size = ISTRSIZE);
+	register int pos = 0;
+	
+	LoadChar(c);
+	while (c != stop_char) {
+		if (c == '\n') {
+			error("newline in %s", nm);
+			LineNumber++;
+			break;
+		}
+		if (c == EOI) {
+			error("end-of-file inside %s", nm);
+			break;
+		}
+		if (c == '\\') {
+			LoadChar(c);
+			if (c == '\n') {
+				LineNumber++;
+				LoadChar(c);
+				continue;
+			}
+			c = quoted(c);
+		}
+		str[pos++] = c;
+		if (pos == str_size)
+			str = Srealloc(str, str_size += RSTRSIZE);
+		LoadChar(c);
+	}
+	str[pos++] = '\0'; /* for filenames etc. */
+	return str;
+}
+
+int
+quoted(c)
+	register int c;
+{	
+	/*	quoted() replaces an escaped character sequence by the
+		character meant.
+	*/
+	/* first char after backslash already in c */
+	if (!is_oct(c)) {		/* a quoted char */
+		switch (c) {
+		case 'n':
+			c = '\n';
+			break;
+		case 't':
+			c = '\t';
+			break;
+		case 'b':
+			c = '\b';
+			break;
+		case 'r':
+			c = '\r';
+			break;
+		case 'f':
+			c = '\f';
+			break;
+		}
+	}
+	else {				/* a quoted octal */
+		register int oct = 0, cnt = 0;
+
+		do {
+			oct = oct*8 + (c-'0');
+			LoadChar(c);
+		} while (is_oct(c) && ++cnt < 3);
+		PushBack();
+		c = oct;
+	}
+	return c&0377;
+}
+
+/* provisional */
+int
+val_in_base(c, base)
+	register int c;
+{
+	return
+		is_dig(c) ? c - '0' :
+		base != 16 ? -1 :
+		is_hex(c) ? (c - 'a' + 10) & 017 :
+		-1;
+}

+ 37 - 0
util/cpp/LLlex.h

@@ -0,0 +1,37 @@
+/* D E F I N I T I O N S   F O R   T H E   L E X I C A L   A N A L Y Z E R */
+
+/*	A token from the input stream is represented by an integer,
+	called a "symbol", but it may have other information associated
+	to it.
+*/
+
+/* the structure of a token:	*/
+struct token	{
+	int tok_symb;		/* the token itself */
+	union {
+		int tok_val;		/* numeric values */
+		char *tok_str;		/* string/filespecifier */
+	} tok_data;
+};
+
+#include "file_info.h"
+
+#define tk_symb	tok_symb
+#define tk_val	tok_data.tok_val
+#define tk_str	tok_data.tok_str
+
+extern struct token dot;
+
+extern int ReplaceMacros;	/* "LLlex.c"	*/
+extern int AccFileSpecifier;	/* "LLlex.c"	*/
+extern int AccDefined;		/* "LLlex.c"	*/
+extern int UnknownIdIsZero;	/* "LLlex.c"	*/
+
+extern int NoUnstack;		/* "input.c"	*/
+extern int Unstacked;		/* "input.c"	*/
+
+extern int err_occurred;	/* "error.c"	*/
+
+#define	DOT	dot.tk_symb
+
+#define EOF	(-1)

+ 19 - 0
util/cpp/LLmessage.c

@@ -0,0 +1,19 @@
+/*		PARSER ERROR ADMINISTRATION		*/
+
+#include	"LLlex.h"
+#include	"Lpars.h"
+
+extern char *symbol2str();
+
+LLmessage(tk)	{
+	err_occurred = 1;
+	if (tk < 0)
+		error("garbage at end of line");
+	else if (tk)	{
+		error("%s missing", symbol2str(tk));
+		skipline();
+		DOT = EOF;
+	}
+	else
+		error("%s deleted", symbol2str(DOT));
+}

+ 164 - 0
util/cpp/Makefile

@@ -0,0 +1,164 @@
+# MAKEFILE FOR (STAND_ALONE) CEM PREPROCESSOR
+
+EMHOME=../..
+MODULES=$(EMHOME)/modules
+MODULESLIB=$(MODULES)/lib
+BIN=$(EMHOME)/lib
+
+# Some paths
+
+# Libraries
+SYSLIB = $(MODULESLIB)/libsystem.a
+STRLIB = $(MODULESLIB)/libstring.a
+PRTLIB = $(MODULESLIB)/libprint.a
+ALLOCLIB = $(MODULESLIB)/liballoc.a
+ASSERTLIB = $(MODULESLIB)/libassert.a
+MALLOC = $(MODULESLIB)/malloc.o
+LIBS = $(PRTLIB) $(STRLIB) $(ALLOCLIB) $(MALLOC) $(ASSERTLIB) $(SYSLIB)
+LIB_INCLUDES = -I$(MODULES)/h -I$(MODULES)/pkg
+
+# Where to install the preprocessor
+CEMPP = $(BIN)/cpp
+
+# What C compiler to use and how
+CC = cc
+COPTIONS =
+LFLAGS = -n
+
+# What parser generator to use and how
+GEN = $(EMHOME)/bin/LLgen
+GENOPTIONS =
+
+# Special #defines during compilation
+CDEFS =	$(LIB_INCLUDES)
+CFLAGS = $(CDEFS) $(COPTIONS) -O#	# we cannot pass the COPTIONS to lint!
+
+# Grammar files and their objects
+LSRC =	tokenfile.g expression.g
+LCSRC = tokenfile.c expression.c Lpars.c
+LOBJ =	tokenfile.o expression.o Lpars.o
+
+# Objects of hand-written C files
+CSRC =	LLlex.c LLmessage.c ch7bin.c ch7mon.c domacro.c \
+	error.c idf.c init.c input.c main.c options.c \
+	preprocess.c replace.c scan.c skip.c tokenname.c next.c expr.c
+COBJ =	LLlex.o LLmessage.o ch7bin.o ch7mon.o domacro.o \
+	error.o idf.o init.o input.o main.o options.o \
+	preprocess.o replace.o scan.o skip.o tokenname.o next.o expr.o
+
+PRFILES = Makefile Parameters \
+	make.hfiles make.tokcase make.tokfile LLlex.h bits.h file_info.h \
+	idf.h input.h interface.h macro.h \
+	class.h chtab.c char.tab expression.g $(CSRC)
+
+# Objects of other generated C files
+GOBJ =	char.o symbol2str.o
+
+# generated source files
+GSRC =	char.c symbol2str.c
+
+# .h files generated by `make hfiles'; PLEASE KEEP THIS UP-TO-DATE!
+GHSRC =	errout.h idfsize.h ifdepth.h lapbuf.h \
+	maxincl.h nparams.h numsize.h obufsize.h \
+	parbufsize.h pathlength.h strsize.h textsize.h \
+	botch_free.h debug.h inputtype.h dobits.h
+
+# Other generated files, for 'make clean' only
+GENERATED = tokenfile.g Lpars.h LLfiles LL.output lint.out \
+	Xref hfiles cfiles charoffset.h
+
+all:	cc
+
+cc:	hfiles LLfiles
+	make "EMHOME="$(EMHOME) cpp
+
+hfiles: Parameters
+	./make.hfiles Parameters
+	@touch hfiles
+
+LLfiles: $(LSRC)
+	$(GEN) $(GENOPTIONS) $(LSRC)
+	@touch LLfiles
+
+tokenfile.g:	tokenname.c make.tokfile
+	<tokenname.c ./make.tokfile >tokenfile.g
+
+symbol2str.c:	tokenname.c make.tokcase
+	<tokenname.c ./make.tokcase >symbol2str.c
+
+char.c:	char.tab chtab
+	chtab -fchar.tab > char.c
+
+charoffset.h:	chtab char.tab
+	chtab -fchar.tab > /dev/null
+
+chtab:	chtab.o
+	$(CC) -o chtab chtab.o
+
+# Objects needed for 'cpp'
+OBJ =	$(COBJ) $(LOBJ) $(GOBJ)
+SRC =	$(CSRC) $(LCSRC) $(GSRC)
+
+cpp:	$(OBJ) Makefile
+	$(CC) $(COPTIONS) $(LFLAGS) $(OBJ) $(LIBS) -o cpp 
+	size cpp
+
+cfiles: hfiles LLfiles $(GSRC)
+	@touch cfiles
+
+install: all
+	cp cpp $(CEMPP)
+
+cmp:	all
+	cmp cpp $(CEMPP)
+
+pr: 
+	@pr $(PRFILES)
+
+opr:
+	make pr | opr
+
+tags:	cfiles
+	ctags $(SRC)
+
+depend:	cfiles
+	sed '/^#AUTOAUTO/,$$d' Makefile >Makefile.new
+	echo '#AUTOAUTOAUTOAUTOAUTOAUTOAUTOAUTOAUTOAUTO' >>Makefile.new
+	$(EMHOME)/bin/mkdep $(SRC) | \
+		sed 's/\.c:/.o:/' >>Makefile.new
+	mv Makefile Makefile.old
+	mv Makefile.new Makefile
+	
+xref:
+	ctags -x `grep "\.[ch]" Files`|sed "s/).*/)/">Xref
+	
+lint:	cfiles
+	lint -bx $(CDEFS) $(SRC) >lint.out
+
+clean:
+	rm -f $(LCSRC) $(OBJ) $(GENERATED) $(GSRC) $(GHSRC) chtab.o chtab
+
+#AUTOAUTOAUTOAUTOAUTOAUTOAUTOAUTOAUTOAUTO
+LLlex.o: LLlex.h Lpars.h bits.h charoffset.h class.h dobits.h file_info.h idf.h idfsize.h input.h inputtype.h numsize.h strsize.h
+LLmessage.o: LLlex.h Lpars.h file_info.h
+ch7bin.o: Lpars.h
+ch7mon.o: Lpars.h
+domacro.o: LLlex.h Lpars.h bits.h botch_free.h charoffset.h class.h debug.h dobits.h file_info.h idf.h idfsize.h ifdepth.h input.h inputtype.h interface.h macro.h nparams.h parbufsize.h textsize.h
+error.o: LLlex.h errout.h file_info.h
+idf.o: idf.h
+init.o: charoffset.h class.h idf.h interface.h macro.h
+input.o: file_info.h input.h inputtype.h
+main.o: file_info.h idfsize.h
+options.o: charoffset.h class.h idf.h idfsize.h macro.h maxincl.h
+preprocess.o: LLlex.h bits.h charoffset.h class.h dobits.h file_info.h idf.h idfsize.h input.h inputtype.h maxincl.h obufsize.h
+replace.o: LLlex.h charoffset.h class.h debug.h file_info.h idf.h input.h inputtype.h interface.h macro.h pathlength.h textsize.h
+scan.o: charoffset.h class.h idf.h input.h inputtype.h interface.h lapbuf.h macro.h nparams.h
+skip.o: LLlex.h charoffset.h class.h file_info.h input.h inputtype.h
+tokenname.o: LLlex.h Lpars.h file_info.h idf.h
+next.o: debug.h
+expr.o: Lpars.h
+tokenfile.o: Lpars.h
+expression.o: LLlex.h Lpars.h file_info.h
+Lpars.o: Lpars.h
+char.o: charoffset.h class.h
+symbol2str.o: Lpars.h

+ 71 - 0
util/cpp/Parameters

@@ -0,0 +1,71 @@
+!File: pathlength.h
+#define PATHLENGTH	1024	/* max. length of path to file		*/
+
+
+!File: errout.h
+#define	ERROUT		STDERR	/* file pointer for writing messages	*/
+#define	MAXERR_LINE	5	/* maximum number of error messages given
+					on the same input line.		*/
+
+
+!File: idfsize.h
+#define	IDFSIZE	64	/* maximum significant length of an identifier	*/
+
+
+!File: numsize.h
+#define	NUMSIZE	256	/* maximum length of a numeric constant		*/
+
+
+!File: nparams.h
+#define	NPARAMS 32	/* maximum number of parameters of macros	*/
+
+
+!File: ifdepth.h
+#define	IFDEPTH	256	/* maximum number of nested if-constructions	*/
+
+
+!File: maxincl.h
+#define	MAXINCL	16	/* maximum number of #include directories	*/
+
+
+!File: lapbuf.h
+#define	LAPBUF	4096	/* size of macro actual parameter buffer	*/
+
+
+!File: strsize.h
+#define ISTRSIZE	32	/* minimum number of bytes allocated for
+					storing a string		*/
+#define RSTRSIZE	8	/* step size in enlarging the memory for
+					the storage of a string		*/
+
+
+!File: botch_free.h
+#undef BOTCH_FREE	1	/* botch freed memory, as a check	*/
+
+
+!File: debug.h
+#undef DEBUG		1	/* perform various self-tests		*/
+
+
+!File: parbufsize.h
+#define PARBUFSIZE	1024
+
+
+!File: textsize.h
+#define ITEXTSIZE	32	/* 1st piece of memory for repl. text	*/
+#define RTEXTSIZE	32	/* stepsize for enlarging repl.text	*/
+
+
+!File: inputtype.h
+#undef INP_READ_IN_ONE	1	/* read input file in one.  */
+				/* If defined, we cannot read from a pipe */
+
+
+!File: obufsize.h
+#define OBUFSIZE	8192	/* output buffer size */
+
+
+!File: dobits.h
+#define DOBITS		1	/* use trick to reduce symboltable accesses */
+
+

+ 13 - 0
util/cpp/bits.h

@@ -0,0 +1,13 @@
+#include "dobits.h"
+#ifdef DOBITS
+#define bit0	0x01
+#define bit1	0x02
+#define bit2	0x04
+#define bit3	0x08
+#define bit4	0x10
+#define bit5	0x20
+#define bit6	0x40
+#define bit7	0x80
+
+extern char bits[];
+#endif

+ 74 - 0
util/cpp/ch7bin.c

@@ -0,0 +1,74 @@
+/* EVALUATION OF BINARY OPERATORS */
+
+#include	"Lpars.h"
+
+ch7bin(pval, oper, val)
+	register int *pval, val;
+	int oper;
+{
+	switch (oper)	{
+	case '%':
+		if (val == 0)
+			error("% by 0");
+		else
+			*pval = *pval % val;
+		break;
+	case '/':
+		if (val == 0)
+			error("/ by 0");
+		else
+			*pval = *pval / val;
+		break;
+	case '*':
+		*pval = *pval * val;
+		break;
+	case '+':
+		*pval = *pval + val;
+		break;
+	case '-':
+		*pval = *pval - val;
+		break;
+	case LEFT:
+		*pval = *pval << val;
+		break;
+	case RIGHT:
+		*pval = *pval >> val;
+		break;
+	case '<':
+		*pval = (*pval < val);
+		break;
+	case '>':
+		*pval = (*pval > val);
+		break;
+	case LESSEQ:
+		*pval = (*pval <= val);
+		break;
+	case GREATEREQ:
+		*pval = (*pval >= val);
+		break;
+	case EQUAL:
+		*pval = (*pval == val);
+		break;
+	case NOTEQUAL:
+		*pval = (*pval != val);
+		break;
+	case '&':
+		*pval = *pval & val;
+		break;
+	case '^':
+		*pval = *pval ^ val;
+		break;
+	case '|':
+		*pval = *pval | val;
+		break;
+	case AND:
+		*pval = (*pval && val);
+		break;
+	case OR:
+		*pval = (*pval || val);
+		break;
+	case ',':
+		*pval = val;
+		break;
+	}
+}

+ 19 - 0
util/cpp/ch7mon.c

@@ -0,0 +1,19 @@
+/* EVALUATION OF MONADIC OPERATORS */
+
+#include	"Lpars.h"
+
+ch7mon(oper, pval)
+	register int *pval;
+{
+	switch (oper)	{
+	case '~':
+		*pval = ~(*pval);
+		break;
+	case '-':
+		*pval = -(*pval);
+		break;
+	case '!':
+		*pval = !(*pval);
+		break;
+	}
+}

+ 36 - 0
util/cpp/char.tab

@@ -0,0 +1,36 @@
+%
+%	CHARACTER CLASSES
+%
+% some general settings:
+%F	%s,
+%
+%	START OF TOKEN
+%
+%C
+STGARB:\000-\377
+STSKIP:\r \t
+STNL:\n\f\013
+STCOMP:!&<=>|
+STSIMP:-%()+*,/:?^~
+STCHAR:'
+STIDF:a-zA-Z_
+STNUM:0-9
+STSTR:"
+STEOI:\200
+%T/* character classes */
+%T#include "class.h"
+%Tchar tkclass[] = {
+%p
+%T};
+%
+%	other kinds of classes
+%
+%C
+_D_|_H_|_I_|_O_:0-7
+_D_|_H_|_I_:89
+_H_|_I_:a-fA-F
+_I_:g-zG-Z_
+%Tchar tk2class[] = {
+%F	%s,
+%p
+%T};

+ 316 - 0
util/cpp/chtab.c

@@ -0,0 +1,316 @@
+/*
+	chtab - character table generator 
+
+	Author: Erik Baalbergen (..tjalk!erikb)
+	Modified by Ceriel Jacobs
+*/
+
+#include <stdio.h> 
+
+static char *RcsId = "$Header$";
+
+#define MAXBUF	256
+#define TABSIZE 257
+#define COMCOM	'-'
+#define FILECOM	'%'
+
+int InputForm = 'c';
+char OutputForm[MAXBUF] = "%s,\n";
+char *Table[TABSIZE];
+char *Name;
+char *ProgCall;
+int signedch = 0;
+char *OutputName;
+char *chroffsetfile = "charoffset.h";
+
+main(argc, argv)
+	char *argv[];
+{
+	char c = 0200;
+	int i = c;
+
+	if (i < 0) signedch = 1;
+
+	ProgCall = *argv++;
+	argc--;
+	while (argc-- > 0) {
+		if (**argv == COMCOM) {
+			option(*argv++);
+		}
+		else {
+			process(*argv++, InputForm);
+		}
+	}
+	MkCharIndex();
+}
+
+MkCharIndex()
+{
+	FILE *fp;
+
+	if ((fp = fopen(chroffsetfile, "w")) == NULL) {
+		fprintf(stderr, "%s: cannot write file %s\n", ProgCall, chroffsetfile);
+		exit(1);
+	}
+	if (signedch) {
+		fputs("#define CharOffset	128\n");
+	}
+	else	fputs("#define CharOffset	0\n");
+	fclose(fp);
+}
+
+char *
+Salloc(s)
+	char *s;
+{
+	char *malloc();
+	char *ns = malloc(strlen(s) + 1);
+
+	if (ns) {
+		strcpy(ns, s);
+	}
+	return ns;
+}
+
+option(str)
+	char *str;
+{
+	/*	note that *str indicates the source of the option:
+		either COMCOM (from command line) or FILECOM (from a file).
+	*/
+	switch (*++str) {
+
+	case ' ':	/* command */
+	case '\t':
+	case '\0':
+		break;
+	case 'I':
+		InputForm = *++str;
+		break;
+	case 'f':
+		if (*++str == '\0') {
+			fprintf(stderr, "%s: -f: name expected\n", ProgCall);
+			exit(1);
+		}
+		DoFile(str);
+		break;
+	case 'F':
+		sprintf(OutputForm, "%s\n", ++str);
+		break;
+	case 'T':
+		printf("%s\n", ++str);
+		break;
+	case 'p':
+		PrintTable();
+		break;
+	case 'C':
+		ClearTable();
+		break;
+	case 'H':
+		if (*++str == '\0') {
+			fprintf(stderr, "%s: -H: name expected\n", ProgCall);
+		}
+		else	chroffsetfile = ++str;
+		break;
+	default:
+		fprintf(stderr, "%s: bad option -%s\n", ProgCall, str);
+	}
+}
+
+ClearTable()
+{
+	register i;
+
+	for (i = 0; i < TABSIZE; i++) {
+		Table[i] = 0;
+	}
+}
+
+PrintTable()
+{
+	register i;
+
+	for (i = 0; i < TABSIZE; i++) {
+		if (Table[i]) {
+			printf(OutputForm, Table[i]);
+		}
+		else {
+			printf(OutputForm, "0");
+		}
+	}
+}
+
+process(str, format)
+	char *str;
+{
+	char *cstr = str;
+	char *Name = cstr;	/* overwrite original string!	*/
+
+	/* strip of the entry name
+	*/
+	while (*str && *str != ':') {
+		if (*str == '\\') {
+			++str;
+		}
+		*cstr++ = *str++;
+	}
+
+	if (*str != ':') {
+		fprintf(stderr, "%s: bad specification: \"%s\", ignored\n",
+			ProgCall, Name);
+		return 0;
+	}
+	*cstr = '\0';
+	str++;
+
+	switch (format) {
+
+	case 'c':
+		return c_proc(str, Name);
+	default:
+		fprintf(stderr, "%s: bad input format\n", ProgCall);
+	}
+	return 0;
+}
+
+c_proc(str, Name)
+	char *str;
+	char *Name;
+{
+	int ch, ch2;
+	int quoted();
+	char *name = Salloc(Name);
+
+	while (*str)	{
+		if (*str == '\\')	{
+			ch = quoted(&str);
+		}
+		else	{
+			ch = *str++;
+		}
+		if (*str == '-')	{
+			if (*++str == '\\')	{
+				ch2 = quoted(&str);
+			}
+			else	{
+				if (ch2 = *str++);
+				else str--;
+			}
+			if (ch > ch2) {
+				fprintf(stderr, "%s: bad range\n", ProgCall);
+				return 0;
+			}
+#define ind(X)	(signedch?(X>=128?X-128:X+128):X)
+			while (ch <= ch2) {
+				Table[ind(ch)] = name;
+				ch++;
+			}
+		}
+		else	{
+			if (ch >= 0 && ch <= 255)
+				Table[ind(ch)] = name;
+		}
+	}
+	Table[256] = Table[0];
+	return 1;
+}
+
+int
+quoted(pstr)
+	char **pstr;
+{
+	register int ch;
+	register int i;
+	register char *str = *pstr;
+
+	if ((*++str >= '0') && (*str <= '9'))	{
+		ch = 0;
+		for (i = 0; i < 3; i++)	{
+			ch = 8 * ch + (*str - '0');
+			if (*++str < '0' || *str > '9')
+				break;
+		}
+	}
+	else	{
+		switch (*str++)	{
+
+		case 'n':
+			ch = '\n';
+			break;
+		case 't':
+			ch = '\t';
+			break;
+		case 'b':
+			ch = '\b';
+			break;
+		case 'r':
+			ch = '\r';
+			break;
+		case 'f':
+			ch = '\f';
+			break;
+		default :
+			ch = *str;
+		}
+	}
+	*pstr = str;
+	return ch & 0377;
+}
+
+int
+stoi(str)
+	char *str;
+{
+	register i = 0;
+
+	while (*str >= '0' && *str <= '9') {
+		i = i * 10 + *str++ - '0';
+	}
+	return i;
+}
+
+char *
+getline(s, n, fp)
+	char *s;
+	FILE *fp;
+{
+	register c = getc(fp);
+	char *str = s;
+
+	while (n--) {
+		if (c == EOF) {
+			return NULL;
+		}
+		else
+		if (c == '\n') {
+			*str++ = '\0';
+			return s;
+		}
+		*str++ = c;
+		c = getc(fp);
+	}
+	s[n - 1] = '\0';
+	return s;
+}
+
+#define BUFSIZE 1024
+
+DoFile(name)
+	char *name;
+{
+	char text[BUFSIZE];
+	FILE *fp;
+
+	if ((fp = fopen(name, "r")) == NULL) {
+		fprintf(stderr, "%s: cannot read file %s\n", ProgCall, name);
+		exit(1);
+	}
+	while (getline(text, BUFSIZE, fp) != NULL) {
+		if (text[0] == FILECOM) {
+			option(text);
+		}
+		else {
+			process(text, InputForm);
+		}
+	}
+}

+ 41 - 0
util/cpp/class.h

@@ -0,0 +1,41 @@
+/* U S E   O F   C H A R A C T E R   C L A S S E S */
+
+/*	As a starter, chars are divided into classes, according to which
+	token they can be the start of.
+	At present such a class number is supposed to fit in 4 bits.
+*/
+
+#include "charoffset.h"
+
+#define	class(ch)	((tkclass+CharOffset)[ch])
+
+/*	Being the start of a token is, fortunately, a mutual exclusive
+	property, so, as there are less than 16 classes they can be
+	packed in 4 bits.
+*/
+
+#define	STSKIP	0	/* spaces and so on: skipped characters		*/
+#define	STNL	1	/* newline character(s): update linenumber etc.	*/
+#define	STGARB	2	/* garbage ascii character: not allowed		*/
+#define	STSIMP	3	/* this character can occur as token		*/
+#define	STCOMP	4	/* this one can start a compound token		*/
+#define	STIDF	5	/* being the initial character of an identifier	*/
+#define	STCHAR	6	/* the starter of a character constant		*/
+#define	STSTR	7	/* the starter of a string			*/
+#define	STNUM	8	/* the starter of a numeric constant		*/
+#define	STEOI	9	/* End-Of-Information mark			*/
+
+/*	But occurring inside a token is not, so we need 1 bit for each
+	class.
+*/
+#define _I_	01
+#define _O_	02
+#define _D_	04
+#define _H_	010
+
+#define	in_idf(ch)	((tk2class+CharOffset)[ch] & _I_)
+#define	is_oct(ch)	((tk2class+CharOffset)[ch] & _O_)
+#define	is_dig(ch)	((tk2class+CharOffset)[ch] & _D_)
+#define	is_hex(ch)	((tk2class+CharOffset)[ch] & _H_)
+
+extern char tkclass[], tk2class[];

+ 693 - 0
util/cpp/domacro.c

@@ -0,0 +1,693 @@
+/* $Header$ */
+/* PREPROCESSOR: CONTROLLINE INTERPRETER */
+
+#include	"interface.h"
+#include	<em_arith.h>
+#include	"LLlex.h"
+#include	"Lpars.h"
+#include	"debug.h"
+#include	"idf.h"
+#include	"input.h"
+
+#include	"ifdepth.h"	
+#include	"botch_free.h"	
+#include	"nparams.h"	
+#include	"parbufsize.h"	
+#include	"textsize.h"	
+#include	"idfsize.h"	
+#include	<assert.h>
+#include	<alloc.h>
+#include	"class.h"
+#include	"macro.h"
+#include	"bits.h"
+
+IMPORT char *inctable[];	/* list of include directories		*/
+IMPORT char *getwdir();
+PRIVATE char ifstack[IFDEPTH];	/* if-stack: the content of an entry is	*/
+				/* 1 if a corresponding ELSE has been	*/
+				/* encountered.				*/
+PRIVATE int nestlevel = -1;	/* initially no nesting level.		*/
+
+PRIVATE char *
+GetIdentifier()
+{
+	/*	returns a pointer to the descriptor of the identifier that is
+		read from the input stream. A null-pointer is returned if
+		the input does not contain an identifier.
+		The substitution of macros is disabled.
+	*/
+	int tok;
+	struct token tk;
+
+	ReplaceMacros = 0;
+	tok = GetToken(&tk);
+	ReplaceMacros = 1;
+	return tok == IDENTIFIER ? tk.tk_str : (char *)0;
+}
+
+/*	domacro() is the control line interpreter. The '#' has already
+	been read by the lexical analyzer by which domacro() is called.
+	The token appearing directly after the '#' is obtained by calling
+	the basic lexical analyzing function GetToken() and is interpreted
+	to perform the action belonging to that token.
+	An error message is produced when the token is not recognized,
+	i.e. it is not one of "define" .. "undef" , integer or newline.
+*/
+EXPORT
+domacro()
+{
+	struct token tk;	/* the token itself			*/
+	register struct idf *id;
+
+	switch(GetToken(&tk)) {		/* select control line action	*/
+	case IDENTIFIER:		/* is it a macro keyword?	*/
+		id = findidf(tk.tk_str);
+		if (!id) {
+			error("%s: unknown control", tk.tk_str);
+			PushBack();
+			skipline();
+			free(tk.tk_str);
+			break;
+		}
+		free(tk.tk_str);
+		switch (id->id_resmac) {
+		case K_DEFINE:				/* "define"	*/
+			do_define();
+			break;
+		case K_ELIF:				/* "elif"	*/
+			do_elif();
+			break;
+		case K_ELSE:				/* "else"	*/
+			do_else();
+			break;
+		case K_ENDIF:				/* "endif"	*/
+			do_endif();
+			break;
+		case K_IF:				/* "if"		*/
+			do_if();
+			break;
+		case K_IFDEF:				/* "ifdef"	*/
+			do_ifdef(1);
+			break;
+		case K_IFNDEF:				/* "ifndef"	*/
+			do_ifdef(0);
+			break;
+		case K_INCLUDE:				/* "include"	*/
+			do_include();
+			break;
+		case K_LINE:				/* "line"	*/
+			/*	set LineNumber and FileName according to
+				the arguments.
+			*/
+			if (GetToken(&tk) != INTEGER) {
+				error("#line without linenumber");
+				PushBack();
+				skipline();
+			}
+			else
+				do_line((unsigned int)tk.tk_val);
+			break;
+		case K_UNDEF:				/* "undef"	*/
+			do_undef();
+			break;
+		default:
+			/* invalid word seen after the '#'	*/
+			error("%s: unknown control", id->id_text);
+			PushBack();
+			skipline();
+		}
+		break;
+	case INTEGER:		/* # <integer> [<filespecifier>]?	*/
+		do_line((unsigned int)tk.tk_val);
+		break;
+	case EOI:	/* only `#' on this line: do nothing, ignore	*/
+		break;
+	default:	/* invalid token following '#'		*/
+		error("illegal # line");
+		PushBack();
+		skipline();
+	}
+}
+
+PRIVATE
+skip_block()
+{
+	/*	skip_block() skips the input from
+		1)	a false #if, #ifdef, #ifndef or #elif until the
+			corresponding #elif (resulting in true), #else or
+			#endif is read.
+		2)	a #else corresponding to a true #if, #ifdef,
+			#ifndef or #elif until the corresponding #endif is
+			seen.
+	*/
+	register int ch;
+	register int skiplevel = nestlevel; /* current nesting level	*/
+	struct token tk;
+	struct idf *id;
+
+	NoUnstack++;
+	for (;;) {
+		LoadChar(ch);	/* read first character after newline	*/
+		if (ch != '#') {
+			if (ch == EOI) {
+				NoUnstack--;
+				return;
+			}
+			PushBack();
+			skipline();
+			continue;
+		}
+		if (GetToken(&tk) != IDENTIFIER) {
+			PushBack();
+			skipline();
+			continue;
+		}
+		/*	an IDENTIFIER: look for #if, #ifdef and #ifndef
+			without interpreting them.
+			Interpret #else, #elif and #endif if they occur
+			on the same level.
+		*/
+		id = findidf(tk.tk_str);
+		free(tk.tk_str);
+		switch(id->id_resmac) {
+		case K_IF:
+		case K_IFDEF:
+		case K_IFNDEF:
+			push_if();
+			continue;
+		case K_ELIF:
+			if (nestlevel == skiplevel) {
+				nestlevel--;
+				push_if();
+				if (ifexpr()) {
+					NoUnstack--;
+					return;
+				}
+			}
+			break;
+		case K_ELSE:
+			++(ifstack[nestlevel]);
+			if (nestlevel == skiplevel) {
+				PushBack();
+				skipline();
+				NoUnstack--;
+				return;
+			}
+			break;
+		case K_ENDIF:
+			assert(nestlevel >= 0);
+			if (nestlevel == skiplevel) {
+				PushBack();
+				skipline();
+				nestlevel--;
+				NoUnstack--;
+				return;
+			}
+			nestlevel--;
+			break;
+		}
+	}
+}
+
+PRIVATE
+ifexpr()
+{
+	/*	ifexpr() returns whether the restricted constant
+		expression following #if or #elif evaluates to true.  This
+		is done by calling the LLgen generated subparser for
+		constant expressions.  The result of this expression will
+		be given in the extern long variable "ifval".
+	*/
+	IMPORT arith ifval;
+	int errors = err_occurred;
+
+	ifval = (arith)0;
+	AccDefined = 1;
+	UnknownIdIsZero = 1;
+	PushLex();	/* NEW parser */
+	If_expr();	/* invoke constant expression parser	*/
+	PopLex();	/* OLD parser */
+	AccDefined = 0;
+	UnknownIdIsZero = 0;
+	return (errors == err_occurred) && (ifval != (arith)0);
+}
+
+PRIVATE
+do_include()
+{
+	/*	do_include() performs the inclusion of a file.
+	*/
+	char *filenm;
+	char *result;
+	int tok;
+	struct token tk;
+
+	AccFileSpecifier = 1;
+	if (((tok = GetToken(&tk)) == FILESPECIFIER) || tok == STRING)
+		filenm = tk.tk_str;
+	else {
+		error("bad include syntax");
+		filenm = (char *)0;
+	}
+	AccFileSpecifier = 0;
+	PushBack();
+	skipline();
+	inctable[0] = WorkingDir;
+	if (filenm) {
+		if (!InsertFile(filenm, &inctable[tok==FILESPECIFIER],&result)){
+			error("cannot find include file \"%s\"", filenm);
+		}
+		else {
+			WorkingDir = getwdir(result);
+			FileName = result;
+			LineNumber = 1;
+		}
+	}
+}
+
+PRIVATE
+do_define()
+{
+	/*	do_define() interprets a #define control line.
+	*/
+	char *str;
+	int nformals = -1;	/* keep track of the number of formals	*/
+	char *formals[NPARAMS];	/* pointers to the names of the formals	*/
+	char parbuf[PARBUFSIZE];		/* names of formals	*/
+	char *repl_text;	/* start of the replacement text	*/
+	int length;		/* length of the replacement text	*/
+	register ch;
+	char *get_text();
+
+	/* read the #defined macro's name	*/
+	if (!(str = GetIdentifier())) {
+		error("#define: illegal macro name");
+		PushBack();
+		skipline();
+		return;
+	}
+	/*	there is a formal parameter list if the identifier is
+		followed immediately by a '('. 
+	*/
+	LoadChar(ch);
+	if (ch == '(') {
+		if ((nformals = getparams(formals, parbuf)) == -1) {
+			PushBack();
+			skipline();
+			return;	/* an error occurred	*/
+		}
+		LoadChar(ch);
+	}
+	/* read the replacement text if there is any			*/
+	ch = skipspaces(ch);	/* find first character of the text	*/
+	assert(ch != EOI);
+	if (class(ch) == STNL) {
+		/*	Treat `#define something' as `#define something ""'
+		*/
+		repl_text = "";
+		length = 0;
+	}
+	else {
+		PushBack();
+		repl_text = get_text((nformals > 0) ? formals : 0, &length);
+	}
+	macro_def(str2idf(str, 0), repl_text, nformals, length, NOFLAG);
+	LineNumber++;
+}
+
+PRIVATE
+push_if()
+{
+	if (nestlevel >= IFDEPTH)
+		fatal("too many nested #if/#ifdef/#ifndef");
+	else
+		ifstack[++nestlevel] = 0;
+}
+
+PRIVATE
+do_elif()
+{
+	if (nestlevel < 0 || (ifstack[nestlevel])) {
+		error("#elif without corresponding #if");
+		PushBack();
+		skipline();
+	}
+	else { /* restart at this level as if a #if is detected.  */
+		nestlevel--;
+		push_if();
+		skip_block();
+	}
+}
+
+PRIVATE
+do_else()
+{
+	PushBack();
+	skipline();
+	if (nestlevel < 0 || (ifstack[nestlevel]))
+		error("#else without corresponding #if");
+	else {	/* mark this level as else-d		*/
+		++(ifstack[nestlevel]);
+		skip_block();
+	}
+}
+
+PRIVATE
+do_endif()
+{
+	PushBack();
+	skipline();
+	if (nestlevel-- < 0)
+		error("#endif without corresponding #if");
+}
+
+PRIVATE
+do_if()
+{
+	push_if();
+	if (!ifexpr())	/* a false #if/#elif expression */
+		skip_block();
+}
+
+PRIVATE
+do_ifdef(how)
+{
+	register struct idf *id;
+	char *str;
+
+	/*	how == 1 : ifdef; how == 0 : ifndef
+	*/
+	push_if();
+	str = GetIdentifier();
+	if (!str) {
+		error("illegal #ifdef construction");
+		id = 0;
+	}
+	else	{
+		id = findidf(str);
+		free(str);
+	}
+
+	/* The next test is a shorthand for:
+		(how && !id->id_macro) || (!how && id->id_macro)
+	*/
+	if (how ^ (id && id->id_macro != 0))
+		skip_block();
+	else {
+		PushBack();
+		skipline();
+	}
+}
+
+PRIVATE
+do_undef()
+{
+	register struct idf *id;
+	register char *str = GetIdentifier();
+
+	/* Forget a macro definition.	*/
+	if (str) {
+		if (id = findidf(str)) {
+			if (id->id_macro) { /* forget the macro */
+				free_macro(id->id_macro);
+				id->id_macro = (struct macro *) 0;
+			} /* else: don't complain */
+		}
+		free(str);
+	}
+	else
+		error("illegal #undef construction");
+	PushBack();
+	skipline();
+}
+
+PRIVATE
+do_line(l)
+	unsigned int l;
+{
+	struct token tk;
+
+	LineNumber = l - 1;	/* the number of the next input line */
+	if (GetToken(&tk) == STRING)	/* is there a filespecifier? */
+		FileName = tk.tk_str;
+	PushBack();
+	skipline();
+}
+
+PRIVATE int
+getparams(buf, parbuf)
+	char *buf[];
+	char parbuf[];
+{
+	/*	getparams() reads the formal parameter list of a macro
+		definition.
+		The number of parameters is returned.
+		As a formal parameter list is expected when calling this
+		routine, -1 is returned if an error is detected, for
+		example:
+			#define one(1), where 1 is not an identifier.
+		Note that the '(' has already been eaten.
+		The names of the formal parameters are stored into parbuf.
+	*/
+	register char **pbuf = &buf[0];
+	register int c;
+	register char *ptr = &parbuf[0];
+	register char **pbuf2;
+
+	LoadChar(c);
+	c = skipspaces(c);
+	if (c == ')') {		/* no parameters: #define name()	*/
+		*pbuf = (char *) 0;
+		return 0;
+	}
+	for (;;) {		/* eat the formal parameter list	*/
+		if (class(c) != STIDF) {	/* not an identifier	*/
+			error("#define: bad formal parameter");
+			return -1;
+		}
+		*pbuf = ptr;	/* name of the formal	*/
+		*ptr++ = c;
+		if (ptr >= &parbuf[PARBUFSIZE])
+			fatal("formal parameter buffer overflow");
+		do {			/* eat the identifier name	*/
+			LoadChar(c);
+			*ptr++ = c;
+			if (ptr >= &parbuf[PARBUFSIZE])
+				fatal("formal parameter buffer overflow");
+		} while (in_idf(c));
+		*(ptr - 1) = '\0';	/* mark end of the name		*/
+
+		/*	Check if this formal parameter is already used.
+			Usually, macros do not have many parameters, so ...
+		*/
+		for (pbuf2 = pbuf - 1; pbuf2 >= &buf[0]; pbuf2--) {
+			if (!strcmp(*pbuf2, *pbuf)) {
+				warning("formal parameter \"%s\" already used",
+					*pbuf);
+			}
+		}
+
+		pbuf++;
+		c = skipspaces(c);
+		if (c == ')') {	/* end of the formal parameter list	*/
+			*pbuf = (char *) 0;
+			return pbuf - buf;
+		}
+		if (c != ',') {
+			error("#define: bad formal parameter list");
+			return -1;
+		}
+		LoadChar(c);
+		c = skipspaces(c);
+	}
+	/*NOTREACHED*/
+}
+
+EXPORT
+macro_def(id, text, nformals, length, flags)
+	register struct idf *id;
+	char *text;
+{
+	/*	macro_def() puts the contents and information of a macro
+		definition into a structure and stores it into the symbol
+		table entry belonging to the name of the macro.
+		A warning is given if the definition overwrites another.
+	*/
+	register struct macro *newdef = id->id_macro;
+	
+	if (newdef) {		/* is there a redefinition?	*/
+		if (macroeq(newdef->mc_text, text))
+			return;
+		warning("redefine \"%s\"", id->id_text);
+	}
+	else {
+#ifdef DOBITS
+		register char *p = id->id_text;
+#define setbit(bx)	if (!*p) goto go_on; bits[*p++] |= (bx)
+		setbit(bit0);
+		setbit(bit1);
+		setbit(bit2);
+		setbit(bit3);
+		setbit(bit4);
+		setbit(bit5);
+		setbit(bit6);
+		setbit(bit7);
+		
+	go_on:
+#endif
+		id->id_macro = newdef = new_macro();
+	}
+	newdef->mc_text = text;		/* replacement text	*/
+	newdef->mc_nps  = nformals;	/* nr of formals	*/
+	newdef->mc_length = length;	/* length of repl. text	*/
+	newdef->mc_flag = flags;	/* special flags	*/
+	newdef->mc_count = 0;
+}
+
+PRIVATE int
+find_name(nm, index)
+	char *nm, *index[];
+{
+	/*	find_name() returns the index of "nm" in the namelist
+		"index" if it can be found there.  0 is returned if it is
+		not there.
+	*/
+	register char **ip = &index[0];
+
+	while (*ip)
+		if (strcmp(nm, *ip++) == 0)
+			return ip - &index[0];
+	/* arrived here, nm is not in the name list.	*/
+	return 0;
+}
+
+PRIVATE char *
+get_text(formals, length)
+	char *formals[];
+	int *length;
+{
+	/*	get_text() copies the replacement text of a macro
+		definition with zero, one or more parameters, thereby
+		substituting each formal parameter by a special character
+		(non-ascii: 0200 & (order-number in the formal parameter
+		list)) in order to substitute this character later by the
+		actual parameter.  The replacement text is copied into
+		itself because the copied text will contain fewer or the
+		same amount of characters.  The length of the replacement
+		text is returned.
+
+		Implementation:
+		finite automaton : we are only interested in
+		identifiers, because they might be replaced by some actual
+		parameter.  Other tokens will not be seen as such.
+	*/
+	register int c;
+	register int text_size;
+	char *text = Malloc(text_size = ITEXTSIZE);
+	register int pos = 0;
+
+	LoadChar(c);
+
+	while ((c != EOI) && (class(c) != STNL)) {
+		if (c == '\\') {	/* check for "\\\n"	*/
+			LoadChar(c);
+			if (c == '\n') {
+				/*	More than one line is used for the
+					replacement text.
+					Replace "\\\n" by " ".
+				*/
+				text[pos++] = ' ';
+				++LineNumber;
+				LoadChar(c);
+			}
+			else
+				text[pos++] = '\\';
+			if (pos == text_size)
+				text = Srealloc(text, text_size += RTEXTSIZE);
+		}
+		else
+		if ( c == '/') {
+			LoadChar(c);
+			if (c == '*') {
+				skipcomment();
+				text[pos++] = ' ';
+				LoadChar(c);
+			}
+			else
+				text[pos++] = '/';
+			if (pos == text_size)
+				text = Srealloc(text, text_size += RTEXTSIZE);
+		}
+		else
+		if (formals && class(c) == STIDF) {
+			char id_buf[IDFSIZE + 1];
+			register id_size = 0;
+			register n;
+
+			/* read identifier: it may be a formal parameter */
+			id_buf[id_size++] = c;
+			do {
+				LoadChar(c);
+				if (id_size <= IDFSIZE)
+					id_buf[id_size++] = c;
+			} while (in_idf(c));
+			id_buf[--id_size] = '\0';
+			if (n = find_name(id_buf, formals)) {
+				/* construct the formal parameter mark	*/
+				text[pos++] = FORMALP | (char) n;
+				if (pos == text_size)
+					text = Srealloc(text,
+						text_size += RTEXTSIZE);
+			}
+			else {
+				register char *ptr = &id_buf[0];
+
+				while (pos + id_size >= text_size)
+					text = Srealloc(text,
+						text_size += RTEXTSIZE);
+				while (text[pos++] = *ptr++) ;
+				pos--;
+			}
+		}
+		else {
+			text[pos++] = c;
+			if (pos == text_size)
+				text = Srealloc(text, text_size += RTEXTSIZE);
+			LoadChar(c);
+		}
+	}
+	text[pos++] = '\0';
+	*length = pos - 1;
+	return text;
+}
+
+#define	BLANK(ch)	((ch == ' ') || (ch == '\t'))
+
+/*	macroeq() decides whether two macro replacement texts are
+	identical.  This version compares the texts, which occur
+	as strings, without taking care of the leading and trailing
+	blanks (spaces and tabs).
+*/
+PRIVATE
+macroeq(s, t)
+	register char *s, *t;
+{
+	
+	/* skip leading spaces	*/
+	while (BLANK(*s)) s++;
+	while (BLANK(*t)) t++;
+	/* first non-blank encountered in both strings	*/
+	/* The actual comparison loop:			*/
+	while (*s && *s == *t)
+		s++, t++;
+	/* two cases are possible when arrived here:	*/
+	if (*s == '\0')	{	/* *s == '\0'		*/
+		while (BLANK(*t)) t++;
+		return *t == '\0';
+	}
+	else	{		/* *s != *t		*/
+		while (BLANK(*s)) s++;
+		while (BLANK(*t)) t++;
+		return (*s == '\0') && (*t == '\0');
+	}
+}

+ 59 - 0
util/cpp/error.c

@@ -0,0 +1,59 @@
+/*	E R R O R   A N D  D I A G N O S T I C   R O U T I N E S	*/
+
+#include	<system.h>
+
+#include	"errout.h"
+#include	"LLlex.h"
+
+/*	This file contains the (non-portable) error-message and diagnostic
+	functions.  Beware, they are called with a variable number of
+	arguments!
+*/
+
+int err_occurred;
+
+err_hdr(s)
+	char *s;
+{
+	fprint(ERROUT, "\"%s\", line %d: %s", FileName, LineNumber, s);
+}
+
+/*VARARGS1*/
+error(fmt, args)
+	char *fmt;
+{
+	err_hdr("");
+	doprnt(ERROUT, fmt, &args);
+	fprint(ERROUT, "\n");
+}
+
+/*VARARGS1*/
+warning(fmt, args)
+	char *fmt;
+{
+	err_hdr("warning ");
+	doprnt(ERROUT, fmt, &args);
+	fprint(ERROUT, "\n");
+}
+
+/*VARARGS1*/
+crash(fmt, args)
+	char *fmt;
+	int args;
+{
+	err_hdr("crash ");
+	doprnt(ERROUT, fmt, &args);
+	fprint(ERROUT, "\n");
+	sys_stop(S_ABORT);
+}
+
+/*VARARGS1*/
+fatal(fmt, args)
+	char *fmt;
+	int args;
+{
+	err_hdr("fatal ");
+	doprnt(ERROUT, fmt, &args);
+	fprint(ERROUT, "\n");
+	sys_stop(S_EXIT);
+}

+ 53 - 0
util/cpp/expr.c

@@ -0,0 +1,53 @@
+/* OPERATOR HANDLING */
+
+#include	"Lpars.h"
+
+int
+rank_of(oper)
+	int oper;
+{
+	/*	The rank of the operator oper is returned.
+	*/
+	switch (oper)	{
+	default:
+		return 0;
+	case '(':
+		return 1;
+	case '!':
+		return 2;
+	case '*':
+	case '/':
+	case '%':
+		return 3;
+	case '+':
+	case '-':
+		return 4;
+	case LEFT:
+	case RIGHT:
+		return 5;
+	case '<':
+	case '>':
+	case LESSEQ:
+	case GREATEREQ:
+		return 6;
+	case EQUAL:
+	case NOTEQUAL:
+		return 7;
+	case '&':
+		return 8;
+	case '^':
+		return 9;
+	case '|':
+		return 10;
+	case AND:
+		return 11;
+	case OR:
+		return 12;
+	case '?':
+	case ':':
+		return 13;
+	case ',':
+		return 15;
+	}
+	/*NOTREACHED*/
+}

+ 124 - 0
util/cpp/expression.g

@@ -0,0 +1,124 @@
+/*	EXPRESSION SYNTAX PARSER	*/
+
+%lexical	LLlex;
+%start		If_expr, if_expression;
+
+{
+#include	"LLlex.h"
+
+extern int ifval;
+}
+
+if_expression
+:
+	constant_expression(&ifval)
+;
+
+/* 7.1 */
+primary(int *pval;)
+:
+	constant(pval)
+|
+	'(' expression(pval) ')'
+;
+
+unary(int *pval;)
+	{int oper;}
+:
+	unop(&oper)
+	unary(pval)
+	{ ch7mon(oper, pval); }
+|
+	primary(pval)
+;
+
+binary_expression(int maxrank; int *pval;)
+	{int oper; int val1;}
+:
+	unary(pval)
+	[%while (rank_of(DOT) <= maxrank)
+		binop(&oper)
+		binary_expression(rank_of(oper)-1, &val1)
+		{
+			ch7bin(pval, oper, val1);
+		}
+	]*
+;
+
+/* 7.13 */
+conditional_expression(int *pval;)
+	{int val1 = 0, val2 = 0;}
+:
+	/* allow all binary operators */
+	binary_expression(rank_of('?') - 1, pval)
+	[	'?'
+		expression(&val1)
+		':'
+		assignment_expression(&val2)
+		{ *pval = (*pval ? val1 : val2); }
+	]?
+;
+
+/* 7.14 */
+assignment_expression(int *pval;)
+:
+	conditional_expression(pval)
+;
+
+/* 7.15 */
+expression(int *pval;)
+	{int val1;}
+:
+	assignment_expression(pval)
+	[	','
+		assignment_expression(&val1)
+		{
+			ch7bin(pval, ',', val1);
+		}
+	]*
+;
+
+unop(int *oper;) :
+	[ '-' | '!' | '~' ]
+	{*oper = DOT;}
+;
+
+multop:
+	'*' | '/' | '%'
+;
+
+addop:
+	'+' | '-'
+;
+
+shiftop:
+	LEFT | RIGHT
+;
+
+relop:
+	'<' | '>' | LESSEQ | GREATEREQ
+;
+
+eqop:
+	EQUAL | NOTEQUAL
+;
+
+arithop:
+	multop | addop | shiftop
+|
+	'&' | '^' | '|'
+;
+
+binop(int *oper;) :
+	[ arithop | relop | eqop | AND | OR ]
+	{*oper = DOT;}
+;
+
+constant(int *pval;) :
+	INTEGER
+	{*pval = dot.tk_val;}
+;
+
+constant_expression (int *pval;) :
+	assignment_expression(pval)
+;

+ 13 - 0
util/cpp/file_info.h

@@ -0,0 +1,13 @@
+/* F I L E   I N F O R M A T I O N   S T R U C T U R E */
+
+struct file_info {
+	unsigned int	fil_lino;
+	char		*fil_name;
+	char		*fil_wdir;
+};
+
+#define LineNumber	finfo.fil_lino
+#define FileName	finfo.fil_name
+#define WorkingDir	finfo.fil_wdir
+
+extern struct file_info finfo;	/* input.c */

+ 2 - 0
util/cpp/idf.c

@@ -0,0 +1,2 @@
+#include "idf.h"
+#include <idf_pkg.body>

+ 11 - 0
util/cpp/idf.h

@@ -0,0 +1,11 @@
+struct id_usr {
+	struct macro *idu_macro;
+	int idu_resmac;
+};
+
+#define IDF_TYPE struct id_usr
+#define IDF_HSIZE 6
+#define id_macro id_user.idu_macro
+#define id_resmac id_user.idu_resmac
+
+#include <idf_pkg.spec>

+ 72 - 0
util/cpp/init.c

@@ -0,0 +1,72 @@
+/* PREPROCESSOR: INITIALIZATION ROUTINES */
+
+#include	<system.h>
+#include	<alloc.h>
+#include	"class.h"
+#include	"macro.h"
+#include	"idf.h"
+#include	"interface.h"
+
+PRIVATE struct mkey	{
+	char *mk_reserved;
+	int mk_key;
+} mkey[] =	{
+	{"define",	K_DEFINE},
+	{"elif",	K_ELIF},
+	{"else",	K_ELSE},
+	{"endif",	K_ENDIF},
+	{"if",		K_IF},
+	{"ifdef",	K_IFDEF},
+	{"ifndef",	K_IFNDEF},
+	{"include",	K_INCLUDE},
+	{"line",	K_LINE},
+	{"undef",	K_UNDEF},
+	{0,		K_UNKNOWN}
+};
+
+char *strcpy();
+
+EXPORT
+init_pp()
+{
+	long clock, sys_time();
+	static char date[30];
+	char *ctime();
+
+	/*	Initialise the control line keywords (if, include, define, etc)
+		Although the lexical analyzer treats them as identifiers, the
+		control line handler can recognize them as keywords by the
+		id_resmac field of the identifier.
+	*/
+	{
+		register struct mkey *mk = &mkey[0];
+
+		while (mk->mk_reserved)	{
+			struct idf *idf = str2idf(mk->mk_reserved, 0);
+			
+			if (idf->id_resmac)
+				fatal("maximum identifier length insufficient");
+			idf->id_resmac = mk->mk_key;
+			mk++;
+		}
+	}
+
+	/*	Initialize __DATE__, __FILE__ and __LINE__ macro
+		definitions.
+	*/
+	/* __DATE__	*/
+	clock = sys_time();
+	strcpy(&date[1], ctime(&clock));
+	date[26] = '\0';		/* zap nl	*/
+	date[0] = date[25] = '"';
+	macro_def(str2idf("__DATE__", 0), date, -1, 26, NOFLAG);
+
+	/* __LINE__	*/
+	macro_def(str2idf("__LINE__", 0), "0", -1, 1, FUNC);
+
+	/* __FILE__	*/
+	macro_def(str2idf("__FILE__", 0), "", -1, 1, FUNC);
+
+	/* defined(??) */
+	macro_def(str2idf("defined", 0), "", 1, 1, FUNC);
+}

+ 47 - 0
util/cpp/input.c

@@ -0,0 +1,47 @@
+#include "file_info.h"
+#include "input.h"
+#define INP_TYPE	struct file_info
+#define INP_VAR		finfo
+struct file_info	finfo;
+#include <inp_pkg.body>
+
+char *
+getwdir(fn)
+	char *fn;
+{
+	register char *p;
+	char *strrindex();
+
+	p = strrindex(fn, '/');
+	while (p && *(p + 1) == '\0') {	/* remove trailing /'s */
+		*p = '\0';
+		p = strrindex(fn, '/');
+	}
+
+	if (fn[0] == '\0' || (fn[0] == '/' && p == &fn[0])) /* absolute path */
+		return "";
+	else
+	if (p) {
+		*p = '\0';
+		fn = Salloc(fn, p - &fn[0] + 1);
+		*p = '/';
+		return fn;
+	}
+	else return ".";
+}
+
+int	NoUnstack;
+int	Unstacked;
+
+AtEoIT()
+{
+	if (NoUnstack) error("unexpected EOF");
+	DoUnstack();
+	return 0;
+}
+
+AtEoIF()
+{
+	if (NoUnstack) error("unexpected EOF");
+	return 0;
+}

+ 2 - 0
util/cpp/input.h

@@ -0,0 +1,2 @@
+#include "inputtype.h"
+#include <inp_pkg.spec>

+ 3 - 0
util/cpp/interface.h

@@ -0,0 +1,3 @@
+#define PRIVATE	
+#define IMPORT extern
+#define EXPORT

+ 52 - 0
util/cpp/macro.h

@@ -0,0 +1,52 @@
+/* PREPROCESSOR: DEFINITION OF MACRO DESCRIPTOR */
+
+/*	The flags of the mc_flag field of the macro structure. Note that
+	these flags can be set simultaneously.
+*/
+#define NOFLAG		0		/* no special flags	*/
+#define	FUNC		01		/* function attached    */
+#define NOREPLACE	02		/* don't replace	*/
+
+#define	FORMALP 0200	/* mask for creating macro formal parameter	*/
+
+/*	The macro descriptor is very simple, except the fact that the
+	mc_text, which points to the replacement text, contains the
+	non-ascii characters \201, \202, etc, indicating the position of a
+	formal parameter in this text.
+*/
+struct macro	{
+	struct macro *next;
+	char *	mc_text;	/* the replacement text		*/
+	int	mc_nps;		/* number of formal parameters	*/
+	int	mc_length;	/* length of replacement text	*/
+	int	mc_count;	/* # of "concurrent" invocations*/
+	char	mc_flag;	/* marking this macro		*/
+};
+
+
+/* allocation definitions of struct macro */
+extern char *st_alloc();
+extern struct macro *h_macro;
+#ifdef DEBUG
+extern int cnt_macro;
+extern char *std_alloc();
+#define	new_macro() ((struct macro *) std_alloc((char **)&h_macro, sizeof(struct macro), 20, &cnt_macro))
+#else
+#define	new_macro() ((struct macro *) st_alloc((char **)&h_macro, sizeof(struct macro), 20))
+#endif
+#define	free_macro(p) st_free(p, &h_macro, sizeof(struct macro))
+
+
+/* `token' numbers of keywords of command-line processor
+*/
+#define	K_UNKNOWN	0
+#define	K_DEFINE	1
+#define	K_ELIF		2
+#define	K_ELSE		3
+#define	K_ENDIF		4
+#define	K_IF		5
+#define	K_IFDEF		6
+#define	K_IFNDEF	7
+#define	K_INCLUDE	8
+#define	K_LINE		9
+#define	K_UNDEF		10

+ 65 - 0
util/cpp/main.c

@@ -0,0 +1,65 @@
+/* MAIN PROGRAM */
+
+#include "file_info.h"
+#include "idfsize.h"
+
+extern struct tokenname tkidf[], tkkey[];
+extern char *symbol2str();
+extern char *inctable[];
+extern int err_occurred;
+int idfsize = IDFSIZE;
+
+int ifval;
+
+char *prog_name;
+
+main(argc, argv)
+	char *argv[];
+{
+	/* parse and interpret the command line options	*/
+	prog_name = argv[0];
+
+	init_idf();
+	init_pp();	/* initialise the preprocessor macros	*/
+
+	/*	Note: source file "-" indicates that the source is supplied
+		as standard input.  This is only allowed if INP_READ_IN_ONE is
+		not defined!
+	*/
+	while (argc > 1 && *argv[1] == '-' && argv[1][1] != '\0')	{
+		char *par = &argv[1][1];
+
+		if (*par == '-')
+			par++;
+		do_option(par);
+		argc--, argv++;
+	}
+	compile(argc - 1, &argv[1]);
+	return err_occurred;
+}
+
+compile(argc, argv)
+	char *argv[];
+{
+	register char *source = 0;
+	char *dummy;
+
+	switch (argc) {
+	case 1:
+		source = argv[0];
+		FileName = source;
+		break;
+	case 0:
+		FileName = "";
+		break;
+	default:
+		fatal("use: %s [options] [source]", prog_name);
+		break;
+	}
+	WorkingDir = inctable[0];
+
+	if (!InsertFile(source, (char **) 0, &dummy)) /* read the source file	*/
+		fatal("%s: no source file %s\n", prog_name, 
+			source ? source : "stdin");
+	preprocess(source);
+}

+ 35 - 0
util/cpp/make.hfiles

@@ -0,0 +1,35 @@
+: Update Files from database
+
+PATH=/bin:/usr/bin
+
+case $# in
+1) ;;
+*)	echo use: $0 file >&2
+	exit 1
+esac
+
+(
+IFCOMMAND="if (<\$FN) 2>/dev/null;\
+	then	if cmp -s \$FN \$TMP;\
+		then	rm \$TMP;\
+		else	mv \$TMP \$FN;\
+			echo update \$FN;\
+		fi;\
+	else	mv \$TMP \$FN;\
+		echo create \$FN;\
+	fi"
+echo 'TMP=.uf$$'
+echo 'FN=$TMP'
+echo 'cat >$TMP <<\!EOF!'
+sed -n '/^!File:/,${
+/^$/d
+/^!File:[	 ]*\(.*\)$/s@@!EOF!\
+'"$IFCOMMAND"'\
+FN=\1\
+cat >$TMP <<\\!EOF!@
+p
+}' $1
+echo '!EOF!'
+echo $IFCOMMAND
+) |
+sh

+ 34 - 0
util/cpp/make.tokcase

@@ -0,0 +1,34 @@
+cat <<'--EOT--'
+#include "Lpars.h"
+
+char *
+symbol2str(tok)
+	int tok;
+{
+	static char buf[2] = { '\0', '\0' };
+
+	if (040 <= tok && tok < 0177) {
+		buf[0] = tok;
+		buf[1] = '\0';
+		return buf;
+	}
+	switch (tok) {
+--EOT--
+sed '
+/{[A-Z]/!d
+s/.*{\(.*\),.*\(".*"\).*$/	case \1 :\
+		return \2;/
+'
+cat <<'--EOT--'
+	case '\n':
+	case '\f':
+	case '\v':
+	case '\r':
+	case '\t':
+		buf[0] = tok;
+		return buf;
+	default:
+		return "bad token";
+	}
+}
+--EOT--

+ 6 - 0
util/cpp/make.tokfile

@@ -0,0 +1,6 @@
+sed '
+/{[A-Z]/!d
+s/.*{//
+s/,.*//
+s/.*/%token	&;/
+'

+ 5 - 0
util/cpp/next.c

@@ -0,0 +1,5 @@
+#include "debug.h"
+struct macro *h_macro = 0;
+#ifdef DEBUG
+int cnt_macro = 0;
+#endif

+ 119 - 0
util/cpp/options.c

@@ -0,0 +1,119 @@
+/* USER-OPTION HANDLING */
+
+#include	<alloc.h>
+#include	"idfsize.h"
+#include	"maxincl.h"
+#include	"class.h"
+#include	"macro.h"
+#include	"idf.h"
+
+char options[128];			/* one for every char	*/
+int inc_pos = 1;			/* place where next -I goes */
+char *inctable[MAXINCL] = {		/* list for includes	*/
+	".",
+	"/usr/include",
+	0
+};
+
+extern int idfsize;
+int txt2int();
+
+do_option(text)
+	char *text;
+{
+	switch(*text++)	{
+	case '-':
+		options[*text] = 1;
+		break;
+	default:
+		error("illegal option: %c", text[-1]);
+		break;
+	case 'C' :	/* comment output		*/
+		options['C'] = 1;
+		break;
+	case 'D' :	/* -Dname :	predefine name		*/
+	{
+		register char *cp = text, *name, *mactext;
+
+		if (class(*cp) != STIDF)	{
+			error("identifier missing in -D%s", text);
+			break;
+		}
+		name = cp;
+		while (*cp && in_idf(*cp))
+			++cp;
+		if (!*cp)			/* -Dname */
+			mactext = "1";
+		else
+		if (*cp == '=')	{		/* -Dname=text	*/
+			*cp++ = '\0';		/* end of name	*/
+			mactext = cp;
+		}
+		else	{			/* -Dname?? */
+			error("malformed option -D%s", text);
+			break;
+		}
+		macro_def(str2idf(name, 0), mactext, -1, strlen(mactext), NOFLAG);
+		break;
+	}
+	case 'I' :	/* -Ipath : insert "path" into include list	*/
+		if (*text)	{
+			register int i = inc_pos++;
+			register char *new = text;
+			
+			while (new)	{
+				register char *tmp = inctable[i];
+				
+				inctable[i++] = new;
+				if (i == MAXINCL)
+					fatal("too many -I options");
+				new = tmp;
+			}
+		}
+		else	inctable[inc_pos] = 0;
+		break;
+	case 'M':	/* maximum identifier length */
+		idfsize = txt2int(&text);
+		if (*text)
+			error("malformed -M option");
+		if (idfsize > IDFSIZE) {
+			warning("maximum identifier length is %d", IDFSIZE);
+			idfsize = IDFSIZE;
+		}
+		if (idfsize < 8) {
+			warning("minimum identifier length is 8");
+			idfsize = 8;
+		}
+		break;
+	case 'P' :	/* run preprocessor stand-alone, without #'s	*/
+		options['P'] = 1;
+		break;
+	case 'U' :	/* -Uname :	undefine predefined	*/
+		if (*text)	{
+			register struct idf *idef = findidf(text);
+
+			if (idef && idef->id_macro) {
+				free_macro(idef->id_macro);
+				idef->id_macro = (struct macro *) 0;
+			}
+		}
+		break;
+	}
+}
+
+int
+txt2int(tp)
+	char **tp;
+{
+	/*	the integer pointed to by *tp is read, while increasing
+		*tp; the resulting value is yielded.
+	*/
+	register int val = 0;
+	register int ch;
+	
+	while (ch = **tp, ch >= '0' && ch <= '9')	{
+		val = val * 10 + ch - '0';
+		(*tp)++;
+	}
+	return val;
+}

+ 209 - 0
util/cpp/preprocess.c

@@ -0,0 +1,209 @@
+/* PREPROCESSOR DRIVER */
+
+#include <system.h>
+#include "input.h"
+#include "maxincl.h"
+#include "obufsize.h"
+#include "LLlex.h"
+#include "class.h"
+#include "idf.h"
+#include "idfsize.h"
+#include "bits.h"
+
+char _obuf[OBUFSIZE];
+
+#ifdef DOBITS
+char bits[128];
+#endif
+
+preprocess(fn)
+	char *fn;
+{
+	register int c;
+	register char *op = _obuf;
+	register char *ob = &_obuf[OBUFSIZE];
+	int lineno = 0;
+	extern char options[];
+
+#define flush(X)	(sys_write(STDOUT,_obuf,X), op = _obuf)
+#define echo(ch) 	(op != ob || flush(OBUFSIZE), *op++ = (ch))
+#define newline()	echo('\n')
+
+	for (;;) {
+		LineNumber++;
+		lineno++;
+		LoadChar(c);
+		while (c == '#') {
+			domacro();
+			lineno++;
+			newline();
+			LoadChar(c);
+		}
+		if (! options['P'] &&
+		     (lineno != LineNumber || fn != FileName)) {
+			char Xbuf[256];
+			register char *p = Xbuf;
+
+			fn = FileName;
+			lineno = LineNumber;
+			sprint(p, "#line %d \"%s\"\n", LineNumber,
+							FileName);
+			while (*p) echo(*p++);
+		}
+		for (;;) {
+			if (c & 0200)  {
+				if (c == EOI) {
+					flush(op-_obuf);
+					return;
+				}
+				fatal("non-ascii character read");
+			}
+			if (c == '/') {
+				LoadChar(c);
+				if (c == '*') {
+					NoUnstack++;
+					if (options['C']) {
+						echo('/');
+						echo('*');
+					}
+					for (;;) {
+						LoadChar(c);
+						if (class(c) == STNL) {
+							++LineNumber;
+							++lineno;
+							echo(c);
+						}
+						else if (c == EOI) {
+							flush(op - _obuf);
+							return;
+						}
+						else if (c == '*') {
+							if (options['C']) echo(c);
+							LoadChar(c);
+							if (c == '/') {
+								if (options['C']) echo(c);
+								break;
+							}
+							else {
+								PushBack();
+							}
+						}
+						else if (options['C']) echo(c);
+					}
+					NoUnstack--;
+					LoadChar(c);
+					continue;
+				}
+				echo('/');
+				continue;
+			}
+			switch(class(c)) {
+			case STNL:
+				echo(c);
+				break;
+			case STSTR:
+			case STCHAR: {
+				register int stopc = c;
+	
+				do {
+					echo(c);
+					LoadChar(c);
+					if (c == '\\') {
+						echo(c);
+						LoadChar(c);
+					}
+					if (c == '\n') {
+						++LineNumber;
+						lineno++;
+					}
+					else if (c == EOI) {
+						flush(op-_obuf);
+						return;
+					}
+				}
+				while (c != stopc);
+				echo(c);
+				LoadChar(c);
+				continue;
+				}
+			case STNUM:
+#define getdec(c)	do { echo(c); LoadChar(c);} while (is_dig(c))
+#define gethex(c)	do { echo(c); LoadChar(c);} while (is_hex(c))
+
+				if (c != '0') {
+					getdec(c);
+					if (c == '.') getdec(c);
+					if (c == 'e') {
+						echo(c);
+						LoadChar(c);
+						if (c == '+' || c == '-') {
+							echo(c);
+							LoadChar(c);
+						}
+						if (is_dig(c)) getdec(c);
+					}
+				}
+				else {
+					echo(c);
+					LoadChar(c);
+					if (c == 'x' || c == 'X') gethex(c);
+					else if (is_dig(c)) getdec(c);
+				}
+				continue;
+			    case STIDF: {
+				extern int idfsize;		/* ??? */
+				char buf[IDFSIZE + 1];
+				register char *tg = &buf[0];
+				register char *maxpos = &buf[idfsize];
+				register struct idf *idef;
+
+#define tstmac(bx)	if (!(bits[c] & bx)) goto nomac
+#define cpy		if (Unstacked) EnableMacros(); *tg++ = c
+#define load		LoadChar(c); if (!in_idf(c)) goto endidf
+
+#ifdef DOBITS
+				cpy; tstmac(bit0); load;
+				cpy; tstmac(bit1); load;
+				cpy; tstmac(bit2); load;
+				cpy; tstmac(bit3); load;
+				cpy; tstmac(bit4); load;
+				cpy; tstmac(bit5); load;
+				cpy; tstmac(bit6); load;
+				cpy; tstmac(bit7); load;
+#endif
+
+				for(;;) {
+					if (tg < maxpos) {
+						cpy;
+					}
+					load;
+				}
+			endidf:
+				PushBack();
+				*tg = '\0';	/* mark the end of the identifier */
+				idef = findidf(buf);
+				if ((idef && idef->id_macro && replace(idef))) {
+					LoadChar(c);
+					continue;
+				}
+			nomac:
+				*tg = 0;
+				tg = buf;
+				while (*tg) echo(*tg++);
+				LoadChar(c);
+				while (in_idf(c)) {
+					echo(c);
+					LoadChar(c);
+				}
+				continue;
+				}
+			    default:
+			    	echo(c);
+			    	LoadChar(c);
+			    	continue;
+			}
+			break;
+		}
+	}
+	/*NOTREACHED*/
+}

+ 196 - 0
util/cpp/replace.c

@@ -0,0 +1,196 @@
+/* PREPROCESSOR: MACRO-TEXT REPLACEMENT ROUTINES */
+
+#include	"debug.h"	/* UF */
+#include	"pathlength.h"	/* UF */
+#include	"textsize.h"	/* UF */
+
+#include	<alloc.h>
+#include	<em_arith.h>
+#include	<assert.h>
+#include	"idf.h"
+#include	"input.h"
+#include	"macro.h"
+#include	"LLlex.h"
+#include	"class.h"
+#include	"interface.h"
+
+char *strcpy(), *strcat();
+char *long2str();
+
+PRIVATE struct macro	*ReplaceList;	/* list of currently active macros */
+
+EXPORT int
+replace(idef)
+	register struct idf *idef;
+{
+	/*	replace() is called by the lexical analyzer to perform
+		macro replacement.  "idef" is the description of the
+		identifier which leads to the replacement.  If the
+		optional actual parameters of the macro are OK, the text
+		of the macro is prepared to serve as an input buffer,
+		which is pushed onto the input stack.
+		replace() returns 1 if the replacement succeeded and 0 if
+		some error has occurred.
+	*/
+	register struct macro *mac = idef->id_macro;
+	register char c;
+	char **actpars, **getactuals();
+	char *reptext, *macro2buffer();
+	int size;
+
+	if (mac->mc_flag & NOREPLACE) {
+		warning("macro %s is recursive", idef->id_text);
+		return 0;
+	}
+	if (mac->mc_nps != -1) {	/* with parameter list	*/
+		if (mac->mc_flag & FUNC) {
+					/* must be "defined".
+					   Unfortunately, the next assertion
+					   will not compile ...
+			assert( ! strcmp("defined", idef->id_text));
+					*/
+			if (! AccDefined)
+				return 0;
+		}
+		if (++mac->mc_count > 100) {
+			/* 100 must be some number in Parameters */
+			warning("macro %s is assumed recursive",
+				    idef->id_text);
+			return 0;
+		}
+		LoadChar(c);
+		c = skipspaces(c);
+		if (c != '(') {		/* no replacement if no ()	*/
+			error("macro %s needs arguments",
+				idef->id_text);
+			PushBack();
+			return 0;
+		}
+		actpars = getactuals(idef);	/* get act.param. list	*/
+		if (mac->mc_flag & FUNC) {
+			struct idf *param = findidf(*actpars);
+
+			if (param && param->id_macro) 
+				reptext = "1";
+			else
+				reptext = "0";
+			InsertText(reptext, 1);
+			mac->next = ReplaceList;
+			ReplaceList = mac;
+			return 1;
+		}
+	}
+	if (mac->mc_flag & FUNC) /* this macro leads to special action	*/
+		macro_func(idef);
+	if (mac->mc_nps <= 0) {
+		mac->mc_flag |= NOREPLACE;
+		reptext = mac->mc_text;
+		size = mac->mc_length;
+	}
+	else reptext = macro2buffer(idef, actpars, &size); /* create input buffer */
+	InsertText(reptext, size);
+	mac->next = ReplaceList;
+	ReplaceList = mac;
+	return 1;
+}
+
+char FilNamBuf[PATHLENGTH];
+
+PRIVATE
+macro_func(idef)
+	register struct idf *idef;
+{
+	/*	macro_func() performs the special actions needed with some
+		macros.  These macros are __FILE__ and __LINE__ which
+		replacement texts must be evaluated at the time they are
+		used.
+	*/
+	register struct macro *mac = idef->id_macro;
+
+	switch (idef->id_text[2]) { /* This switch is very blunt... */
+	case 'F' :			/* __FILE__	*/
+		mac->mc_length = strlen(FileName) + 2;
+		mac->mc_text = FilNamBuf;
+		mac->mc_text[0] = '"';
+		strcpy(&(mac->mc_text[1]), FileName);
+		strcat(mac->mc_text, "\"");
+		break;
+	case 'L' :			/* __LINE__	*/
+	{
+		mac->mc_text = long2str((long) LineNumber, 10);
+		mac->mc_length = strlen(mac->mc_text);
+		break;
+	}
+	default :
+		crash("(macro_func)");
+	}
+}
+
+PRIVATE char *
+macro2buffer(idef, actpars, siztext)
+	struct idf *idef;
+	char **actpars;
+	int *siztext;
+{
+	/*	Macro2buffer() turns the macro replacement text, as it is
+		stored, into an input buffer, while each occurrence of the
+		non-ascii formal parameter mark is replaced by its
+		corresponding actual parameter specified in the actual
+		parameter list actpars.  A pointer to the beginning of the
+		constructed text is returned, while *siztext is filled
+		with its length.
+		If there are no parameters, this function behaves
+		the same as strcpy().
+	*/
+	register int size = idef->id_macro->mc_length + ITEXTSIZE;
+	register char *text = Malloc(size);
+	register int pos = 0;
+	register char *ptr = idef->id_macro->mc_text;
+
+	while (*ptr) {
+		if (*ptr & FORMALP) {	/* non-asc formal param. mark	*/
+			register int n = *ptr++ & 0177;
+			register char *p;
+
+			assert(n != 0);
+			/*	copy the text of the actual parameter
+				into the replacement text
+			*/
+			for (p = actpars[n - 1]; p && *p; p++) {
+				text[pos++] = *p;
+				if (pos == size)
+					text = Srealloc(text, size += RTEXTSIZE);
+			}
+		}
+		else {
+			text[pos++] = *ptr++;
+			if (pos == size)
+				text = Srealloc(text, size += RTEXTSIZE);
+		}
+	}
+	text[pos] = '\0';
+	*siztext = pos;
+	return text;
+}
+
+EXPORT
+DoUnstack()
+{
+	Unstacked++;
+}
+
+EXPORT
+EnableMacros()
+{
+	register struct macro *p = ReplaceList;
+
+	assert(Unstacked > 0);
+	while (Unstacked > 0) {
+		assert(p != 0);
+		p->mc_flag &= ~NOREPLACE;
+		p->mc_count = 0;
+		p = p->next;
+		Unstacked--;
+	}
+	ReplaceList = p;
+}

+ 224 - 0
util/cpp/scan.c

@@ -0,0 +1,224 @@
+/* PREPROCESSOR: SCANNER FOR THE ACTUAL PARAMETERS OF MACROS	*/
+
+/*	This file contains the function getactuals() which scans an actual
+	parameter list and splits it up into a list of strings, each one
+	representing an actual parameter.
+*/
+
+#include	"lapbuf.h"	/* UF */
+#include	"nparams.h"	/* UF */
+
+#include	"input.h"
+#include	"class.h"
+#include	"idf.h"
+#include	"macro.h"
+#include	"interface.h"
+
+#define	EOS		'\0'
+#define	overflow()	(fatal("actual parameter buffer overflow"))
+
+PRIVATE char apbuf[LAPBUF]; /* temporary storage for actual parameters	*/
+PRIVATE char *actparams[NPARAMS]; /* pointers to the text of the actuals */
+PRIVATE char *aptr;	/* pointer to last inserted character in apbuf	*/
+
+#define	copy(ch)	((aptr < &apbuf[LAPBUF]) ? (*aptr++ = ch) : overflow())
+
+PRIVATE int nr_of_params;	/* number of actuals read until now	*/
+
+PRIVATE char **
+getactuals(idef)
+	struct idf *idef;
+{
+	/*	getactuals() collects the actual parameters and turns them
+		into a list of strings, a pointer to which is returned.
+	*/
+	register acnt = idef->id_macro->mc_nps;
+
+	nr_of_params = 0;
+	actparams[0] = aptr = &apbuf[0];
+	copyact('(', ')', 0);	/* read the actual parameters	*/
+	copy(EOS);		/* mark the end of it all	*/
+
+	if (!nr_of_params++)	{		/* 0 or 1 parameter	*/
+		/* there could be a ( <spaces, comment, ...> )
+		*/
+		register char *p = actparams[0];
+
+		while ((class(*p) == STSKIP) || (*p == '\n')) {
+				++p;
+		}
+
+		if (!*p) {	/* the case () : 0 parameters	*/
+			nr_of_params--;
+		}
+	}
+
+	if (nr_of_params != acnt)	{
+		/*	argument mismatch: too many or too few
+			actual parameters.
+		*/
+		error("argument mismatch, %s", idef->id_text);
+
+		while (++nr_of_params < acnt) {
+			/*	too few paraeters: remaining actuals are ""
+			*/
+			actparams[nr_of_params] = (char *) 0;
+		}
+	}
+
+	return actparams;
+}
+
+PRIVATE
+copyact(ch1, ch2, level)
+	char ch1, ch2;
+	int level;
+{
+	/*	copyact() is taken from Ceriel Jacobs' LLgen, with
+		permission.  Its task is to build a list of actuals
+		parameters, which list is surrounded by '(' and ')' and in
+		which the parameters are separated by ',' if there are
+		more than 1. The balancing of '(',')' and '[',']' and
+		'{','}' is taken care of by calling this function
+		recursively. At each level, copyact() reads the input,
+		upto the corresponding closing bracket.
+
+		Opening bracket is ch1, closing bracket is ch2. If
+		level != 0, copy opening and closing parameters too.
+	*/
+	register int ch;		/* Current char */
+	register int match;		/* used to read strings */
+
+	if (level) {
+		copy(ch1);
+	}
+
+	for (;;)	{
+		LoadChar(ch);
+
+		if (ch == ch2)	{
+			if (level) {
+				copy(ch);
+			}
+			return;
+		}
+
+		switch(ch)	{
+
+		case ')':
+		case '}':
+		case ']':
+			error("unbalanced parenthesis");
+			break;
+
+		case '(':
+			copyact('(', ')', level+1);
+			break;
+
+		case '{':
+			/*	example:
+					#define declare(v, t)	t v
+					declare(v, union{int i, j; float r;});
+			*/
+			copyact('{', '}', level+1);
+			break;
+
+		case '[':
+			copyact('[', ']', level+1);
+			break;
+
+		case '\n':
+			LoadChar(ch);
+			while (ch == '#')	{
+				/*	This piece of code needs some
+					explanation: consider the call of
+					the macro defined as:
+						#define sum(b,c) (b + c)
+					in the following form:
+						sum(
+						#include my_phone_number
+						,2)
+					in which case the include must be
+					interpreted as such.
+				*/
+				domacro();	/* has read nl, vt or ff */
+				LoadChar(ch);
+				/* Loop, for another control line */
+			}
+
+			PushBack();
+			copy('\n');
+			break;
+
+		case '/':
+			LoadChar(ch);
+
+			if (ch == '*')	{	/* skip comment	*/
+				skipcomment();
+				continue;
+			}
+
+			PushBack();
+			copy('/');
+			break;
+
+		case ',':
+			if (!level)	{
+				/* next parameter encountered */
+				copy(EOS);
+
+				if (++nr_of_params >= NPARAMS) {
+					fatal("(getact) too many actuals");
+				}
+
+				actparams[nr_of_params] = aptr;
+			}
+			else	{
+				copy(ch);
+			}
+			break;
+
+		case '\'':
+		case '"' :
+			/*	watch out for brackets in strings, they do
+				not count !
+			*/
+			match = ch;
+			copy(ch);
+			LoadChar(ch);
+			while (ch != EOI)	{
+				if (ch == match) {
+					break;
+				}
+
+				if (ch == '\\')	{
+					copy(ch);
+					LoadChar(ch);
+				}
+				else
+				if (ch == '\n')	{
+					error("newline in string");
+					copy(match);
+					break;
+				}
+
+				copy(ch);
+				LoadChar(ch);
+			}
+
+			if (ch == match)	{
+				copy(ch);
+				break;
+			}
+			/* Fall through */
+
+		case EOI :
+			error("unterminated macro call");
+			return;
+
+		default:
+			copy(ch);
+			break;
+		}
+	}
+}

+ 64 - 0
util/cpp/skip.c

@@ -0,0 +1,64 @@
+/* $Header$ */
+/* PREPROCESSOR: INPUT SKIP FUNCTIONS */
+
+#include	"LLlex.h"
+#include	"class.h"
+#include	"input.h"
+
+int
+skipspaces(ch)
+	register int ch;
+{
+	/*	skipspaces() skips any white space and returns the first
+		non-space character.
+	*/
+	for (;;) {
+		while (class(ch) == STSKIP)
+			LoadChar(ch);
+		/* How about "\\\n"?????????	*/
+		if (ch == '/') {
+			LoadChar(ch);
+			if (ch == '*') {
+				skipcomment();
+				LoadChar(ch);
+			}
+			else	{
+				PushBack();
+				return '/';
+			}
+		}
+		else
+			return ch;
+	}
+}
+
+skipline()
+{
+	/*	skipline() skips all characters until a newline character
+		is seen, not escaped by a '\\'.
+		Any comment is skipped.
+	*/
+	register int c;
+
+	LoadChar(c);
+	while (class(c) != STNL && c != EOI) {
+		if (c == '\\') {
+			LoadChar(c);
+			if (class(c) == STNL)
+				++LineNumber;
+		}
+		if (c == '/') {
+			LoadChar(c);
+			if (c == '*')
+				skipcomment();
+			else
+				continue;
+		}
+		LoadChar(c);
+	}
+	++LineNumber;
+	if (c == EOI) {		/* garbage input...		*/
+		error("unexpected EOF while skipping text");
+		PushBack();
+	}
+}

+ 51 - 0
util/cpp/tokenname.c

@@ -0,0 +1,51 @@
+/* $Header$ */
+/* TOKEN NAME DEFINITIONS */
+
+#include	"idf.h"
+#include	"LLlex.h"
+#include	"Lpars.h"
+
+struct tokenname	{	/*	Used for defining the name of a
+					token as identified by its symbol
+				*/
+	int tn_symbol;
+	char *tn_name;
+};
+
+/*	To centralize the declaration of %tokens, their presence in this
+	file is taken as their declaration. The Makefile will produce
+	a grammar file (tokenfile.g) from this file.
+	Moreover, rather than looking up a symbol in all these lists
+	to find its printable name, a fast version of symbol2str() is
+	generated from these tables.
+	Consequenty some of these tables are not referenced explicitly
+	in the C text any more.  To save space and to avoid lint confusion,
+	these have been made pseudo-invisible by #ifdefs.
+*/
+
+#ifdef	____
+struct tokenname tkspec[] =	{	/* the names of the special tokens */
+	{IDENTIFIER, "identifier"},
+	{STRING, "string"},
+	{FILESPECIFIER, "filespecifier"},
+	{INTEGER, "integer"},
+	{0, ""}
+};
+
+struct tokenname tkcomp[] =	{	/* names of the composite tokens */
+	{NOTEQUAL, "!="},
+	{AND, "&&"},
+	{LEFT, "<<"},
+	{LESSEQ, "<="},
+	{EQUAL, "=="},
+	{GREATEREQ, ">="},
+	{RIGHT, ">>"},
+	{OR, "||"},
+	{0, ""}
+};
+
+struct tokenname tkfunny[] =	{	/* internal keywords */
+	{ERRONEOUS, "erroneous"},
+	{0, ""}
+};
+#endif	____