[mod.sources] scpp - a selective C preprocessor

sources-request@panda.UUCP (09/20/85)

Mod.sources:  Volume 3, Issue 13
Submitted by: decvax!tektronix!tekig4!bradn


In response to the net.lang.c comments about the misuses of the preprocessor,
I offer this program that interprets selected macros in a file without
disturbing anything else.  I wrote it after trying to read Bourne's
"Algol-like" adb.

Scpp is also the most thorough conditional-code remover I've seen -- very
useful for making sense out of heavily "ifdef'ed" code like UUCP.

Scpp should run at least under 4.2BSD, SYSIII and SYSV.  Please let me know
if you have any trouble getting it running.

Brad Needham
Tektronix, Inc.
...decvax!tektronix!tekig4!bradn

-------------- cut along the dashed line -------
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	scpp.1
#	Makefile
#	lex.l
#	ctrl.c
#	interp
#	io.c
# This archive created: Thu Sep 19 12:35:36 1985
export PATH; PATH=/bin:$PATH
echo shar: extracting "'scpp.1'" '(2992 characters)'
if test -f 'scpp.1'
then
	echo shar: will not over-write existing file "'scpp.1'"
else
sed 's/^	X//' << \SHAR_EOF > 'scpp.1'
	X.TH SCPP 1 "28 September 1983"
	X.SH NAME
	Xscpp \- selective C preprocessor
	X.SH SYNOPSIS
	X.B scpp
	X[
	X.BI \-M macro
	X] [
	X.BI \-D macro
	X] [
	X.BI \-D macro=def
	X] [
	X.B \-C
	X]
	X.ti +5
	X[
	X.BI \-I incdir
	X] [
	X.I file...
	X]
	X.SH DESCRIPTION
	X.B Scpp
	Xconcatenates the input
	X.I files
	X(or reads standard-in, if no
	X.I file
	Xis given),
	Xinterprets all references to given macros,
	Xleaving the rest of the
	X.IR file "(s)"
	Xunaltered,
	Xthen writes the result to standard-out.
	XIt is helpful in removing conditionally compiled code or misleading
	Xmacros from a file.
	X.PP
	XThe
	X.I file
	Xname "\fB-\fP" refers to standard-in.
	X.PP
	XThe following options are available.
	XEach option can appear as frequently as desired.
	X.RS
	X.TP
	X.SM \-M
	XInterpret all references to the given
	X.I macro.
	X.I Macro
	Xcan be either a single macro name or a whitespace-separated list of
	Xmacro names (e.g. -MMAXINT or -M"MAXINT MININT INTWID").
	XAll occurrences of the macro and all instances of the intrinsic macro
	X\&"defined()" referring to this macro are expanded.
	XAll preprocessor directives referring to this macro (except
	X.BR #if )
	Xperform their usual function and do not appear in the output.
	X.B #if
	Xdirectives are interpreted only if their value is not dependent on macros
	Xwhich are not to be interpreted.
	X.TP
	X.SM \-D
	XDefine the
	X.I macro
	Xto have the value
	X.I def,
	Xor "1" if no
	X.I def
	Xis given.
	XUnlike the C preprocessor,
	X.B scpp
	Xdoes not implicitly define certain macros that describe the environment in
	Xwhich the code will be running (e.g. "vax" or "unix").
	X.B \-D
	Ximplies
	X.B \-M.
	X.TP
	X.SM \-C
	XPreserve comments and whitespace in interpreted macro definitions.
	XNormally, comments and leading and trailing whitespace are stripped from
	Xinterpreted macro definitions.
	X.TP
	X.SM \-I
	XAdd
	X.I incdir
	Xto the list of directories to be searched for include files.
	X.B Scpp
	Xsearches directories in the same order as the C preprocessor:
	Xif the include filename is enclosed in double-quotes ("...")
	Xrather than angle-brackets (<...>),
	Xthe directory containing the current file being processed;
	Xthe directories given by -I, left-to-right;
	Xthe standard directory, /usr/include.
	X.RE
	X.SH AUTHOR
	XBrad Needham, Tektronix, Inc.
	X.SH "SEE ALSO"
	Xcc(1).
	X.SH BUGS
	XVery long identifiers (those over 100 characters long) will crash
	X.B scpp.
	X.PP
	XBecause
	X.B scpp
	Xinterprets only the given macros, the meaning of some code will change.
	XE.g. "scpp -MBOO" of
	X.br
	X	#define BOO hello,there
	X.br
	X	#define twopar(a,b) a b
	X.br
	X	twopar(BOO,folks)
	X.br
	Xwill generate
	X.br
	X	#define twopar(a,b) a b
	X.br
	X	twopar(hello,there,folks)
	X.br
	Xcausing an argument mismatch when the output is compiled.
	X.PP
	XBecause uninterpreted "#if"s, "ifdef"s, and "ifndef"s, have no effect
	Xon the output, the following example, when processed via "scpp -MLEFT",
	Xwill generate an error message complaining about
	Xmultiple definitions of "LEFT".
	X.br
	X	#ifdef ZULU
	X.br
	X	#define LEFT 20
	X.br
	X	#else
	X.br
	X	#define LEFT 347
	X.br
	X	#endif
	X.PP
	XThe C preprocessor macros "\fB__FILE__\fP" and "\fB__LINE__\fP" have no
	Xspecial meaning to
	X.B scpp.
SHAR_EOF
if test 2992 -ne "`wc -c < 'scpp.1'`"
then
	echo shar: error transmitting "'scpp.1'" '(should have been 2992 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'Makefile'" '(1086 characters)'
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
sed 's/^	X//' << \SHAR_EOF > 'Makefile'
	X# Makefile for the selective C preprocessor, scpp.
	X#
	X# Copyright (c) 1985 by
	X# Tektronix, Incorporated Beaverton, Oregon 97077
	X# All rights reserved.
	X#
	X# Permission is hereby granted for personal, non-commercial
	X# reproduction and use of this program, provided that this
	X# notice and all copyright notices are included in any copy.
	X
	XDEFS=
	XCFLAGS= -O $(DEFS)
	XHDRS= scpp.h
	X
	XSOURCES= ctrl.c io.c lex.l parse.y scpp.c
	XOBJECTS= ctrl.o io.o lex.o parse.o scpp.o
	X
	Xall:	scpp scpp.cat
	Xscpp:	$(OBJECTS)
	X	$(CC) $(CFLAGS) -o scpp $(OBJECTS) -ll
	Xscpp.cat: scpp.1
	X	nroff -man scpp.1 >scpp.cat
	X
	Xscpp.o: scpp.c y.tab.h scpp.h
	Xctrl.o: ctrl.c y.tab.h scpp.h
	Xio.o:  io.c scpp.h
	Xlex.o: lex.c y.tab.h scpp.h
	Xparse.o: parse.c scpp.h
	X
	Xlex.c: lex.l
	X	lex lex.l
	X	sed -e '/yylex/s//xxlex/g' <lex.yy.c >lex.c
	X	rm lex.yy.c
	Xy.tab.h parse.c: parse.y
	X	yacc -d parse.y
	X	mv y.tab.c parse.c
	X
	Xclean:
	X	-rm -f lex.yy.c lex.c y.tab.c y.tab.h y.output parse.c
	X	-rm -f $(OBJECTS)
	X
	Xtags: $(SOURCES)
	X	ctags $(SOURCES)
	Xmail:
	X	shar -a scpp.1 Makefile lex.l ctrl.c interp io.c >scpp.shar1
	X	shar -a parse.y scpp.c scpp.h >scpp.shar2
SHAR_EOF
if test 1086 -ne "`wc -c < 'Makefile'`"
then
	echo shar: error transmitting "'Makefile'" '(should have been 1086 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'lex.l'" '(2334 characters)'
if test -f 'lex.l'
then
	echo shar: will not over-write existing file "'lex.l'"
else
sed 's/^	X//' << \SHAR_EOF > 'lex.l'
	X%{
	X/*
	X * scpp - selective C preprocessor
	X *  Lexical scanner
	X *
	X * Copyright (c) 1985 by
	X * Tektronix, Incorporated Beaverton, Oregon 97077
	X * All rights reserved.
	X *
	X * Permission is hereby granted for personal, non-commercial
	X * reproduction and use of this program, provided that this
	X * notice and all copyright notices are included in any copy.
	X */
	X
	X# include <stdio.h>
	X
	X# undef input
	X# undef unput
	X# define input() (*nxtin == ATTN ? nxtc() : *nxtin++)
	X# define unput(c) unc(c)
	X
	X# include "scpp.h"
	X# include "y.tab.h"
	X
	Xint lasttok = NL;	/* used to detect ^# when lex can't	*/
	X# define yield(t) lasttok = t; questr(yytext, yyleng); return(t)
	X
	X/*
	X * All input to higher levels of scpp is provided exclusively by this
	X *  lexical analyzer, xxlex().
	X * This routine is called xxlex() rather than yylex() because the "#if"
	X *  expression parser uses a slightly different lexical analyzer (which
	X *  calls xxlex()).
	X */
	X
	X%}
	X%%
	X
	X"<"	{yield(LT);}
	X"<="	{yield(LE);}
	X">"	{yield(GT);}
	X">="	{yield(GE);}
	X","	{yield(CM);}
	X"/"	{yield(DIV);}
	X"%"	{yield(MOD);}
	X"+"	{yield(PLUS);}
	X"-"	{yield(MINUS);}
	X"<<"	{yield(LS);}
	X">>"	{yield(RS);}
	X"*"	{yield(MUL);}
	X"=="	{yield(EQ);}
	X"!="	{yield(NE);}
	X"&"	{yield(AND);}
	X"|"	{yield(OR);}
	X"^"	{yield(ER);}
	X"&&"	{yield(ANDAND);}
	X"||"	{yield(OROR);}
	X"?"	{yield(QUEST);}
	X":"	{yield(COLON);}
	X"!"	{yield(NOT);}
	X"~"	{yield(COMPL);}
	X"("	{yield(LP);}
	X")"	{yield(RP);}
	X","	{yield(CM);}
	X[ \t]+	{yield(WHITE);	/* whitespace */}
	X\\\n	{/* escaped newline */
	X		if (curfile->af_raw) {
	X			curfile->af_line++;
	X		}
	X		yield(QNL);
	X	}
	X\n	{/* unescaped newline */
	X		if (curfile->af_raw) {
	X			curfile->af_line++;
	X		}
	X		yield(NL);
	X	}
	X0x[0-9a-fA-F]+[Ll]?	{yield(INT); /* hex constant */}
	X[0-9]+[Ll]?		{yield(INT); /* decimal or octal constant */}
	X[0-9]+[Ee]([+-][0-9])?[0-9]*		|
	X\.[0-9]+([Ee]([+-][0-9])?[0-9]*)?	|
	X[0-9]+\.[0-9]*([Ee]([+-][0-9])?[0-9]*)?	{yield(FLOAT); /* floating constant */}
	X[a-zA-Z_][a-zA-Z0-9_]*	{yield(IDENT); /* identifier */}
	X\'	{yield(QUOTE);}
	X\"	{yield(DQUOTE);}
	X\\	{yield(BACKS);}
	X"/*"	{yield(OPENC); /* start (open) comment */}
	X"*/"	{yield(CLOSEC);/* finish (close) comment */}
	X#	{/*
	X	  * a control line if preceeded immediately by a newline,
	X	  *  even if that newline was the result of macro interpretation.
	X	  */
	X		if (lasttok == NL) {
	X			yield(POUNDLINE);
	X		}
	X		yield(OTHER);
	X	}
	X.	{yield(OTHER);}
	X
	X%%
SHAR_EOF
if test 2334 -ne "`wc -c < 'lex.l'`"
then
	echo shar: error transmitting "'lex.l'" '(should have been 2334 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'ctrl.c'" '(19059 characters)'
if test -f 'ctrl.c'
then
	echo shar: will not over-write existing file "'ctrl.c'"
else
sed 's/^	X//' << \SHAR_EOF > 'ctrl.c'
	X/*
	X * ctrl - interpretation of preprocessor control lines (e.g. #define...)
	X *  for the selective C preprocessor, scpp.
	X *
	X * Copyright (c) 1985 by
	X * Tektronix, Incorporated Beaverton, Oregon 97077
	X * All rights reserved.
	X *
	X * Permission is hereby granted for personal, non-commercial
	X * reproduction and use of this program, provided that this
	X * notice and all copyright notices are included in any copy.
	X */
	X
	X# include <stdio.h>
	X# include "scpp.h"
	X# include "y.tab.h"
	X
	X/*
	X * valbuf[] the buffer in which to build the value of a macro which has
	X *  parameters.
	X */
	X
	X# define VALLEN 1000		/* max # of chars in a macro value string  */
	Xchar valbuf[VALLEN];
	Xchar *valend;			/*
	X				 * always points to the null-terminator
	X				 * of the value while the value is being built
	X				 */
	X
	X/*
	X * form[] the array of formal parameters for a macro.
	X *  Each formal argument of a macro acts as a "local variable" during the
	X *  scan for the value of the macro.  The form[] array contains
	X *  pointers to each argument's slot in the symbol table (used to recognize
	X *  the formal argument in the value) and the previous value of that slot
	X *  (so that the symbol table can be restored to normal after the value
	X *  of the macro has been scanned.
	X */
	X
	Xstruct aformal {
	X	struct amacro *fm_sym;	/* symbol table slot for this formal	*/
	X	struct amacro  fm_copy;	/* copy of the old contents of that slot */
	X};
	X# define FORMSIZ 40	/* max number of parameters to a macro	*/
	Xstruct aformal form[FORMSIZ];
	Xstruct aformal *formtop; /* points to the next empty slot in form[]	*/
	X
	X/*
	X * do_xxx() - functions for processing preprocessor control lines.
	X */
	X
	Xint do_line();
	Xint do_include();
	Xint do_define();
	Xint do_undef();
	Xint do_ifdef();
	Xint do_ifndef();
	Xint do_if();
	Xint do_else();
	Xint do_endif();
	X
	X/*
	X * key - the array of preprocessor keywords.
	X */
	X
	Xstruct akeyword key[] = {
	X	{"line", do_line, 0},
	X	{"include", do_include, 0},
	X	{"define", do_define, 0},
	X	{"undef", do_undef, 0},
	X	{"ifdef", do_ifdef, 0},
	X	{"ifndef", do_ifndef, 0},
	X	{"if", do_if, 0},
	X	{"else", do_else, 0},
	X	{"endif", do_endif, 0},
	X	{0,0,0}			/* a zero ak_name marks the end of the list */
	X};
	X
	X/*
	X * ikeywords() - initialize the preprocessor keywords.
	X *  For each keyword, set its ak_sym field and act as if it has been -M'ed.
	X */
	X
	Xikeywords()
	X{
	X	struct akeyword *kp;
	X
	X
	X	for (kp = &key[0]; kp->ak_name; ++kp) {
	X		kp->ak_sym = findmac(kp->ak_name,
	X		    kp->ak_name + strlen(kp->ak_name));
	X		if (kp->ak_sym->am_name) {
	X			bomb("INTERNAL: identical keywords in key[]");
	X		}
	X
	X		kp->ak_sym->am_name = kp->ak_name;
	X		kp->ak_sym->am_npar = -1;
	X		/* leave am_val as 0 */
	X	}
	X}
	X
	X/*
	X * findkey() - find the keyword corresponding to the given symbol table entry,
	X *  returning a pointer to that keyword in key[],
	X *  or zero if no match.
	X */
	X
	Xstruct akeyword *
	Xfindkey(mac)
	Xstruct amacro *mac;
	X{
	X	struct akeyword *kp;
	X
	X	for (kp = &key[0]; kp->ak_name && kp->ak_sym != mac; ++kp)
	X		;
	X	if (!kp->ak_name) {
	X		return((struct akeyword *) 0);
	X	}
	X	return(kp);
	X}
	X
	X/*
	X * doctrl() - process a control line (a line beginning with '#').
	X */
	X
	Xint		/* returned token (NL or 0)	*/
	Xdoctrl(f)		/* process control lines */
	Xchar *f;		/* first char of this line (the '#' token) in pend[] */
	X{
	X	int tok;		/* the current token	*/
	X	struct amacro *cmd;	/* the preprocessor command (symbol table)  */
	X	struct akeyword *kp;	/* the preprocessor command (keyword table) */
	X
	X
	X	/*
	X	 * skip initial whitespace and comments (if any);
	X	 * ignore empty command lines;
	X	 * print warnings for garbled command lines;
	X	 * switch on the command name.
	X	 */
	X
	X	if ((tok = nonwhite(gintok)) == 0 || tok == NL) {
	X		return(tok);
	X	}
	X
	X	if (tok != IDENT) {
	X		warnf("undefined control");
	X		tok = endline();
	X		return(tok);
	X	}
	X
	X	cmd = findmac(curtext, nxtout);
	X	if (!cmd->am_name || !(kp = findkey(cmd))) {
	X		/* name is not a preprocessor command */
	X
	X		warnf("undefined control");
	X		tok = endline();
	X		return(tok);
	X	}
	X
	X	/*
	X	 * invoke the appropriate handler
	X	 */
	X
	X	tok = (*kp->ak_proc)(f);
	X	return(tok);
	X}
	X
	X/*
	X * do_line - parse a #line command.
	X *   #line syntax:
	X * 	#[<whitespace>]line[<whitespace>][<int>[<whitespace>]<string>
	X *     where: <int> is the new integer line number for this line,
	X *	      <string> is the new double-quote enclosed filename.
	X */
	X
	Xint
	Xdo_line(f)
	Xchar *f;
	X{
	X	int tok;
	X	char *name;		/* the new filename		*/
	X	char *src;
	X	char *dst;
	X
	X
	X	if ((tok = nonwhite(gintok)) == 0 || tok == NL) {
	X		return(tok);
	X	}
	X
	X	if (tok == INT) {
	X		if (curfile >= &filestk[0]) {
	X			curfile->af_line = inttok(curtext, nxtout);
	X		}
	X
	X		if ((tok = nonwhite(gintok)) == 0 || tok == NL) {
	X			return(tok);
	X		}
	X	}
	X	if (tok != STRING) {
	X		tok = endline();
	X		return(tok);
	X	}
	X
	X	name = savtok(curtext, nxtout);
	X	for (dst = name, src = name + 1; (*dst = *src) != '\0';
	X	  ++dst, ++src)
	X		;
	X	if (--dst <= name || *dst != '"') {
	X		free(name);
	X		tok = endline();
	X		return(tok);
	X	}
	X	*dst = '\0';
	X
	X	if (curfile >= &filestk[0]) {
	X		free(curfile->af_name);
	X		curfile->af_name = name;
	X	}
	X
	X	tok = endline();
	X	return(tok);
	X}
	X
	Xint
	Xdo_include(f)
	Xchar *f;	/* (unused because #include lines are never deleted)	*/
	X{
	X	char *ifile;		/* the (dynamically alloc'ed) filename	*/
	X	int looktype;		/* type of directory search to perform	*/
	X	int tok;		/* the current token's type		*/
	X	char *src, *dst;
	X
	X
	X	/*
	X	 * pickup the filename, scan the rest of the command line,
	X	 * then include the file.
	X	 */
	X
	X	ifile = (char *) 0;
	X	if ((tok = nonwhite(gintok)) == 0) {
	X		goto badinc;
	X	}
	X	if (tok == STRING) {
	X		/*
	X		 * the filename is enclosed in double-quotes.
	X		 * Set the search type to include the current file's directory;
	X		 * Save the filename, then remove the string delimiters from it.
	X		 */
	X
	X		looktype = PF_DOT;
	X
	X		ifile = savtok(curtext, nxtout);
	X
	X		for (dst = ifile, src = ifile + 1; (*dst = *src) != '\0';
	X		    ++dst, ++src)
	X			;
	X		if (--dst <= ifile || *dst != '"') {
	X			goto badinc;
	X		}
	X		*dst = '\0';
	X	} else if (tok == LT) {
	X		/*
	X		 * The filename is enclosed in angle-brackets.
	X		 * Set the directory search to exclude the current file's
	X		 *  directory; collect and save the filename.
	X		 */
	X
	X		looktype = PF_NODOT;
	X
	X		src = nxtout;
	X		while ((tok = gtok()) != GT && tok != NL && tok != 0)
	X			;
	X		if (tok != GT) {
	X			goto badinc;
	X		}
	X		ifile = savtok(src, curtext);
	X	} else {
	Xbadinc:
	X		bombf("bad include syntax");
	X		if (ifile) {
	X			free(ifile);
	X		}
	X		tok = endline();
	X		return(tok);
	X	}
	X
	X	tok = endline();
	X	pushfile(ifile, looktype, PF_HIDE);
	X	free(ifile);
	X	return(tok);
	X}
	X
	Xint
	Xdo_define(f)
	Xchar *f;
	X{
	X	int tok;
	X	struct amacro *mac;	/* the macro being defined		*/
	X	struct amacro *arg;	/*
	X				 * points to the slot for the current argument
	X				 * from the definition of a macro with
	X				 * parameters.
	X				 */
	X	struct amacro maccopy;	/*
	X				 * a copy of some of the info from the slot
	X				 *  for the macro being defined.  Copying the
	X				 *  slot allows perverse macro definitions
	X				 *  such as:
	X				 *    # define boo(boo)  boo()
	X				 */
	X	struct aformal *formp;	/* a temp pointer			*/
	X	int defok = TRUE;	/*
	X				 * 'ok to define the macro.' Used to prevent
	X				 * definition of a macro with parameters if
	X				 * there was a syntax error in the definition.
	X				 */
	X
	X
	X	/*
	X	 * scan for the macro name with identifier expansion turned off.
	X	 * Find the slot corresponding to the macro name.
	X	 */
	X
	X	if ((tok = nonwhite(gtok)) == 0) {
	X		return(0);
	X	}
	X	if (tok != IDENT) {
	X		warnf("illegal macro name");
	X		tok = endline();
	X		return(tok);
	X	}
	X	mac = findmac(curtext, nxtout);
	X
	X	if ((tok = gtok()) != LP) {
	X		/*
	X		 * a simple macro.  If it hasn't been -M'ed, ignore it.
	X		 * Otherwise, save the replacement text (disposing of
	X		 *  any quoted newlines, comments, and appropriate
	X		 *  whitespace), define the macro, and dispose of this line.
	X		 */
	X
	X		char *valstrt;	/* points to the macro value within pend[] */
	X		char *valstr;	/* the dynamically alloc'ed value string */
	X
	X		if (!mac->am_name) {
	X			tok = endline();
	X			return(tok);
	X		}
	X
	X		valstrt = curtext;
	X
	X		/*
	X		 * if the delimiter is whitespace, skip the first character
	X		 * of the whitespace (and any preceeding ATTN bytes).
	X		 */
	X
	X		if (tok == WHITE) {
	X			while (valstrt < nxtout && *valstrt == ATTN) {
	X				valstrt += 2;
	X			}
	X			if (valstrt < nxtout) {
	X				valstrt++;
	X			}
	X		}
	X		while (tok != NL && tok != 0) {
	X			if (tok == QNL || (!savcom && tok == COMMENT)) {
	X				(void) dispose(curtext);
	X			}
	X			tok = gtok();
	X		}
	X		if (tok == 0) {
	X			warnf("unterminated preprocessor command");
	X		} else {
	X			valstr = savtok(valstrt, curtext);
	X			if (!savcom) {
	X				stripwhite(valstr);
	X			}
	X			defmac(mac->am_name,
	X			  mac->am_name + strlen(mac->am_name),
	X			  -1 /* npar */, valstr);
	X			free(valstr);
	X		}
	X		(void) dispose(f);
	X		return(tok);
	X	}
	X
	X	/* a macro with parameters.  Copy relevant parts of it. */
	X
	X	maccopy.am_name = mac->am_name;
	X
	X	/*
	X	 * Collect the comma-separated formals of the macro.
	X	 * Temporarily define each formal of the macro, saving the old
	X	 * contents of that formal's slot in the symbol table,
	X	 * marking that symbol as -M'ed, but undefined (so that
	X	 * scanning an uninterpreted macro definition works in the
	X	 * following case:
	X	 *	scpp -MMOO
	X	 *		#define MOO lose
	X	 *		#define goo(MOO) is a MOO.
	X	 *	should generate
	X	 *		#define goo(MOO) is a MOO.
	X	 *	rather than
	X	 *		#define goo(MOO) is a lose.
	X	 */
	X
	X	formtop = &form[0];
	X	while (formtop < &form[FORMSIZ]) {
	X		if ((tok = nonwhite(gtok)) != IDENT) {
	X			break;
	X		}
	X
	X		/* process the formal argument */
	X
	X		formtop->fm_sym = findmac(curtext, nxtout);
	X		for (formp = &form[0]; formp < formtop &&
	X		  formp->fm_sym != formtop->fm_sym; ++formp)
	X			;
	X		if (formp < formtop) {
	X			warnf("duplicate formal names in macro definition");
	X		}
	X
	X		formtop->fm_copy.am_name = formtop->fm_sym->am_name;
	X		formtop->fm_copy.am_npar = formtop->fm_sym->am_npar;
	X		formtop->fm_copy.am_val = formtop->fm_sym->am_val;
	X
	X		formtop->fm_sym->am_name = savtok(curtext, nxtout);
	X		formtop->fm_sym->am_npar = -1;
	X		formtop->fm_sym->am_val = (char *) 0;
	X
	X		++formtop;
	X
	X		if ((tok = nonwhite(gtok)) != CM) {
	X			break;
	X		}
	X	}
	X	if (tok != RP) {
	X		if (formtop >= &form[FORMSIZ]) {
	X			warnf("too many formal arguments");
	X		} else {
	X			warnf("syntax error in formal arguments");
	X		}
	X		tok = endline();
	X		defok = FALSE;
	X		goto rollback;
	X	}
	X
	X	if (!maccopy.am_name) {
	X
	X		/*
	X		 * This macro is not -M'ed, so don't interpret this #define.
	X		 * scan to the end of the line.
	X		 */
	X
	X		tok = endline();
	X		defok = FALSE;
	X	} else {
	X
	X		/*
	X		 * This macro is -M'ed.  Record the number of parameters,
	X		 * then save the value of this macro,
	X		 * marking occurrences of the formal arguments.
	X		 */
	X
	X		maccopy.am_npar = formtop - &form[0];
	X
	X		valend = &valbuf[0];
	X		*valend = '\0';
	X
	X		while ((tok = gtok()) != 0 && tok != NL) {
	X			if (tok == QNL || (!savcom && tok == COMMENT)) {
	X				/* ignore the token */
	X			} else {
	X				/*
	X				 * if this token is a formal parameter name,
	X				 * add its parameter number & an ATTN byte
	X				 * to the macro value.  Otherwise add the
	X				 * token's value (less ATTN bytes) to the value.
	X				 */
	X    
	X				formp = formtop;
	X				if (tok == IDENT) {
	X				    arg = findmac(curtext, nxtout);
	X				    for (formp = &form[0]; formp < formtop &&
	X				      formp->fm_sym != arg; ++formp)
	X					    ;
	X				}
	X
	X				if (formp < formtop) {
	X				    if (valend + 2 >= &valbuf[VALLEN]) {
	X					bombf("macro value too long");
	X				    }
	X				    *valend++ = (char) ((formp - &form[0]) + 1);
	X				    *valend++ = ATTN;
	X				    *valend = '\0';
	X				} else {
	X				    if (valend + (nxtout - curtext) >=
	X				      &valbuf[VALLEN]) {
	X					bombf("macro value too long");
	X				    }
	X				    while (curtext < nxtout) {
	X					if (*curtext == ATTN) {
	X						curtext += 2;
	X					} else {
	X						*valend++ = *curtext++;
	X					}
	X				    }
	X				    *valend = '\0';
	X				}
	X			}
	X		}
	X		(void) dispose(f);
	X	}
	X
	Xrollback:
	X
	X	/*
	X	 * restore the formal parameter's original values
	X	 * (in reverse order to take care of duplicate formal parameters).
	X	 */
	X
	X	while (--formtop >= &form[0]) {
	X		free(formtop->fm_sym->am_name);
	X		formtop->fm_sym->am_name = formtop->fm_copy.am_name;
	X		formtop->fm_sym->am_npar = formtop->fm_copy.am_npar;
	X		formtop->fm_sym->am_val = formtop->fm_copy.am_val;
	X	}
	X
	X	/*
	X	 * (finally) define the macro (if there was no problem),
	X	 * stripping whitespace where appropriate.
	X	 */
	X
	X	if (defok) {
	X		if (!savcom) {
	X			stripwhite(&valbuf[0]);
	X		}
	X		defmac(maccopy.am_name,
	X		  maccopy.am_name + strlen(maccopy.am_name),
	X		  maccopy.am_npar, &valbuf[0]);
	X	}
	X	return(tok);
	X}
	X
	Xint
	Xdo_undef(f)
	Xchar *f;
	X{
	X	int tok;		/* the current token's type	*/
	X	struct amacro *mac;	/* the macro to be undefined	*/
	X	char *cp;
	X
	X
	X
	X	/*
	X	 * find the macro to be undefined (it is legal to undef an undefined
	X	 *  macro, a non "-M"ed macro, or a preprocessor keyword);
	X	 * Read the rest of the "#undef" line;
	X	 * If this macro is one of the magic preprocessor macros
	X	 * (e.g. "defined()"), it cannot be undef'ed.
	X	 * Otherwise, find the beginning of the value and free it,
	X	 *  then zero the value, undefining the macro.
	X	 * Destroy the original text of the #undef.
	X	 */
	X
	X	if ((tok = nonwhite(gtok)) != IDENT) {
	X		warnf("illegal macro name");
	X		tok = endline();
	X		return(tok);
	X	}
	X	mac = findmac(curtext, nxtout);
	X	if (!mac->am_name) {
	X		tok = endline();
	X		return(tok);
	X	}
	X	if (mac->am_val) {
	X		if (mac->am_val == &magicval) {
	X			warnf("cannot undef implicit macro");
	X			tok = endline();
	X			return(tok);
	X		}
	X		cp = mac->am_val;
	X		while (*--cp != '\0')
	X			;
	X		free(cp);
	X		mac->am_val = (char *) 0;
	X	}
	X	tok = endline();
	X	(void) dispose(f);
	X	return(tok);
	X}
	X
	Xint
	Xdo_ifdef(f)
	Xchar *f;
	X{
	X	return(ifdorn(f,TRUE));
	X}
	X
	Xint
	Xdo_ifndef(f)
	Xchar *f;
	X{
	X	return(ifdorn(f, FALSE));
	X}
	X
	X/*
	X * ifdorn() - "if defined or not defined" -- this is the common code for
	X *  ifdef and ifndef processing.
	X */
	X
	Xint
	Xifdorn(f, defed)
	Xchar *f;		/* points to the beginning of the command in pend[] */
	Xint defed;		/* "the if is true if the macro is defined"	*/
	X{
	X	int tok;		/* the current token's type	*/
	X	struct amacro *mac;	/* the macro in question	*/
	X
	X
	X	if (++curif >= &ifstk[IFSIZ]) {
	X		bombf("too many nested if's");
	X	}
	X	*curif = IF_INIF;
	X
	X	tok = nonwhite(gtok);
	X	if (tok != IDENT) {
	X		warnf("illegal macro name");
	X		tok = endline();
	X		return(tok);
	X	}
	X	mac = findmac(curtext, nxtout);
	X	tok = endline();
	X
	X	if (!mac->am_name) {
	X		return(tok);
	X	}
	X
	X	if ((mac->am_val && defed) || (!mac->am_val && !defed)) {
	X		*curif |= IF_TRUE;
	X	} else {
	X		*curif |= IF_FALSE;
	X		ift_f();
	X	}
	X
	X	(void) dispose(f);
	X	return(tok);
	X}
	X
	Xint
	Xdo_if(f)
	Xchar *f;
	X{
	X	int tok;
	X	int oldnint;		/* interp' count prior to parsing the exp */
	X	int wasraw;		/* the state of interpretation prior to parse */
	X
	X
	X	if (++curif >= &ifstk[IFSIZ]) {
	X		bombf("too many nested if's");
	X	}
	X	*curif = IF_INIF;
	X
	X	wasraw = curfile->af_raw;
	X	oldnint = ninterp;
	X
	X	expparse = TRUE;
	X	if (yyparse() != 0) {
	X		/* syntax error - don't interpret this 'if'	*/
	X
	X		*curif &= ~(IF_TRUE | IF_FALSE);
	X		tok = endline();
	X		expparse = FALSE;
	X		return(tok);
	X	}
	X	tok = endline();
	X	expparse = FALSE;
	X
	X	if (!(*curif & (IF_TRUE | IF_FALSE)) ||
	X	   (wasraw && oldnint == ninterp)) {
	X		/*
	X		 * either the truth is not known or
	X		 * no macro interpretation was performed;
	X		 * Don't interpret the #if.
	X		 */
	X
	X		*curif &= ~(IF_TRUE | IF_FALSE);
	X		return(tok);
	X	}
	X	if (*curif & IF_FALSE) {
	X		ift_f();
	X	}
	X
	X	(void) dispose(f);
	X	return(tok);
	X}
	X
	Xint
	Xdo_else(f)
	Xchar *f;
	X{
	X	int tok;
	X
	X
	X	if (curif < &ifstk[0] || !(*curif & IF_INIF)) {
	X		warnf("if-less else");
	X		tok = endline();
	X		return(tok);
	X	}
	X	tok = endline();
	X
	X	*curif &= ~IF_INIF;
	X
	X	if (*curif & IF_TRUE) {
	X		*curif &= ~IF_TRUE;
	X		*curif |= IF_FALSE;
	X		ift_f();
	X		(void) dispose(f);
	X		return(tok);
	X	}
	X	if (*curif & IF_FALSE) {
	X		*curif &= ~IF_FALSE;
	X		*curif |= IF_TRUE;
	X		iff_t();
	X		(void) dispose(f);
	X		return(tok);
	X	}
	X	/* this is the 'else' of an uninterpreted if	*/
	X	return(tok);
	X}
	X
	Xint
	Xdo_endif(f)
	Xchar *f;
	X{
	X	int tok;
	X
	X
	X	if (curif < &ifstk[0]) {
	X		warnf("if-less endif");
	X		tok = endline();
	X		return(tok);
	X	}
	X
	X	tok = endline();
	X
	X	if (!(*curif & (IF_TRUE | IF_FALSE))) {
	X		/* this is the 'endif' of an uninterpreted if */
	X		--curif;
	X		return(tok);
	X	}
	X	if (*curif & IF_FALSE) {
	X		iff_t();
	X	}
	X
	X	--curif;
	X	(void) dispose(f);
	X	return(tok);
	X}
	X
	X/*
	X * ift_f(), iff_t() - #if statement transitions which may affect output.
	X *   Ift_f() is called whenever an #if statement makes a transition from
	X *    from true (or non-existent) to false;
	X *   Iff_t() is called whenever one goes from false to true (or non-existent).
	X */
	X
	Xift_f()
	X{
	X	if (falsecnt++ == 0 && hidecnt == 0) {
	X		quec(ATTN);
	X		quec(AT_OUTOFF);
	X	}
	X}
	X
	Xiff_t()
	X{
	X	if (--falsecnt == 0 && hidecnt == 0) {
	X		quec(ATTN);
	X		quec(AT_OUTON);
	X	}
	X}
	X
	X/*
	X * stripwhite() - given a pointer to a (possibly dynamically allocated)
	X *  string which is to become the value of a macro, strip the leading
	X *  and trailing whitespace from the value.
	X */
	X
	Xstripwhite(s)
	Xchar *s;
	X{
	X	char *cp;
	X	char *nb;	/*
	X			 * points to the char beyond the last non-blank
	X			 * character in the string.
	X			 */
	X
	X	/*
	X	 * skip the initial whitespace, but don't count as whitespace a
	X	 *  parameter number which preceeds an ATTN byte.
	X	 */
	X
	X	for (cp = s; *cp == ' ' || *cp == '\t'; ++cp)
	X		;
	X	if (*cp == ATTN) {
	X		if (cp == s) {
	X			bombf("INTERNAL: ATTN at beginning of string");
	X		} else {
	X			--cp;
	X		}
	X	}
	X
	X	/*
	X	 * slide the string into its new position, noting the position of
	X	 *  the char beyond the final non-white character so that the final
	X	 *  whitespace can be eliminated.
	X	 */
	X
	X	for (nb = cp; (*s++ = *cp) != '\0'; ++cp) {
	X		if (*cp != ' ' && *cp != '\t') {
	X			nb = s;
	X		}
	X	}
	X	*nb = '\0';
	X}
	X
	X/*
	X * nonwhite() - read until the next non-white (and non-comment) token,
	X *  using the scanner provided.
	X *  This routine is used only to skip whitespace within preprocessor command
	X *   lines.
	X */
	X
	Xint			/* the non-white, non-comment token		*/
	Xnonwhite(scan)
	Xint (*scan)();		/* token scanner - either gtok() or gintok()	*/
	X{
	X	int tok;
	X
	X	while ((tok = (*scan)()) == WHITE || tok == COMMENT)
	X		;
	X	if (tok == 0) {
	X		warnf("unterminated preprocessor command");
	X	}
	X	return(tok);
	X}
	X
	X/*
	X * endline() - if not already at the end of the line, read tokens to get there.
	X *  return the final token (either NL or 0).
	X *  Used only to read the ends of preprocessor command lines.
	X *  For the benefit of uninterpreted command lines, macros are interpreted.
	X *
	X *  Endline should be called as late as possible in processing a line
	X *  so that error messages will be correlated to the offending line rather
	X *  than the following line.
	X */
	X
	Xint
	Xendline()
	X{
	X	int tok;	/* the current token	*/
	X	char *cp;	/* the current character in a backward search	*/
	X
	X
	X	/*
	X	 * if the last character read (less ATTN byte pairs)
	X	 *  was an unescaped newline, return;
	X	 * Otherwise, skip tokens until the end of the line or
	X	 *  the end of the file.
	X	 */
	X
	X	for (cp = nxtout - 1; cp >= &pend[1]; cp -= 2) {
	X		if (*(cp - 1) != ATTN) {
	X			break;
	X		}
	X	}
	X	if (cp >= &pend[0] && *cp == '\n' &&
	X	    (cp == &pend[0] || *(cp - 1) != '\\')) {
	X		/* an unescaped newline has already been read */
	X
	X		return(NL);
	X	}
	X
	X	while ((tok = gintok()) != NL) {
	X		if (tok == 0) {
	X			warnf("unterminated preprocessor command");
	X			return(tok);
	X		}
	X	}
	X	return(tok);
	X}
SHAR_EOF
if test 19059 -ne "`wc -c < 'ctrl.c'`"
then
	echo shar: error transmitting "'ctrl.c'" '(should have been 19059 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'interp'" '(1986 characters)'
if test -f 'interp'
then
	echo shar: will not over-write existing file "'interp'"
else
sed 's/^	X//' << \SHAR_EOF > 'interp'
	XInterpretation rules:
	XText is, by default, interpreted.  The following chart shows what sections
	Xof preprocessor directives are not interpreted (i.e. no macro interpretation,
	Xno preprocessor line interpretation).  Note, however that the lexical analyzer
	Xis still used -- this prevents the recognition of floating point numbers as
	Xan instance of a formal parameter named, for example, "e4".
	X
	X(\n is an unescaped newline -- escaped newlines are recognized)
	X
	X# line n ..............\n
	X
	X# line n filename .....\n
	X			  (the filename is treated as garbage)
	X# line n "filename"....\n
	X			  (the filename is not interp'ed 'cause it's tween "")
	X			  (#line lines are no-ops)
	X# include "file".......\n
	X                	  (again, "file" is in quotes & is not interpreted)
	X# include <file>.......\n
	X	   -----
	X# define macro replacement-text\n
	X	------------------------- (if 'macro' is one to interpret)
	X        ------			  (if 'macro' is not one to interpret --
	X				   also, the line is a no-op in this case)
	X# define macro( f1 , f2 , f3 ...) replacement-text with formals\n
	X	---------------------------------------------------------
	X	------------------------- (if 'macro' is not one to be interpreted)
	X  (the above case is strange in that the formal parameters in the replacement
	X   text must be protected against interpretation)
	X   [remember to allow comments in the formals]
	X# undef macro .........\n  (a no-op if 'macro' is not to be interpreted)
	X       ------
	X# ifdef macro .........\n  (also a no-op if 'macro is not -M'ed, except that
	X       ------		    there must be a matching #endif or #else)
	X# ifndef macro ........\n  (same note as above)
	X        ------
	X# else.................\n
	X
	X# endif................\n
	X
	X# if expression\n  (if the truth of the expression is known, this is
	X                    interpreted.  Otherwise it is not)
	X#\n
	X
	X
	XSpecial case: text is not interpreted (but preprocessor lines are interpreted)
	X during the scan for the actual parameters of a macro with parameters.
SHAR_EOF
if test 1986 -ne "`wc -c < 'interp'`"
then
	echo shar: error transmitting "'interp'" '(should have been 1986 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'io.c'" '(8707 characters)'
if test -f 'io.c'
then
	echo shar: will not over-write existing file "'io.c'"
else
sed 's/^	X//' << \SHAR_EOF > 'io.c'
	X/*
	X * io.c - input and output primitives for the selective C preprocessor, scpp.
	X *
	X * Copyright (c) 1985 by
	X * Tektronix, Incorporated Beaverton, Oregon 97077
	X * All rights reserved.
	X *
	X * Permission is hereby granted for personal, non-commercial
	X * reproduction and use of this program, provided that this
	X * notice and all copyright notices are included in any copy.
	X */
	X
	X# include <stdio.h>
	X# include "scpp.h"
	X
	X# define STDINPUT 0	/* file descriptor of stdin		*/
	X# define BSIZE	512	/*
	X			 * # of bytes per read -- controls how quickly
	X			 * istk[] is consumed.
	X			 */
	Xint dooutput = 1;	/* "actually write data" rather than tossing it	*/
	X
	X/*
	X * nxtc() - return the next character from the input stream.
	X *   Nxtc() is used only by lex.
	X */
	X
	Xchar
	Xnxtc()
	X{
	X	char ch;
	X	int readcnt;
	X
	X
	X	while ((ch = *nxtin++) == ATTN) {
	X		switch (ch = *nxtin++) {
	X		case AT_EPUSH:	/* end of pushback (interpreted text)	*/
	X			curfile->af_raw = TRUE;
	X			break;
	X		case AT_EBLK:	/* end of block	*/
	X
	X			/*
	X			 * The current block is exhausted.
	X			 * Mark the end of the new block
	X			 * then read in the new block (if there's space)
	X			 * and adjust the top of stack.
	X			 */
	X
	X			unc(AT_EBLK);
	X			unc(ATTN);
	X			if (nxtin < &istk[BSIZE]) {
	X				over();
	X			}
	X			nxtin -= BSIZE;
	X			readcnt = read(curfile->af_fd, nxtin, BSIZE);
	X			if (readcnt < 0) {
	X				bombf("read error");
	X			}
	X			if (readcnt > 0) {
	X				if (readcnt < BSIZE) {
	X					/* slide the new data into place */
	X
	X					register char *src, *dst;
	X
	X					for (dst = nxtin + BSIZE,
	X					     src = nxtin + readcnt;
	X					     src > nxtin; *--dst = *--src)
	X						;
	X					nxtin = dst;
	X				}
	X				break;
	X			}
	X
	X			/*
	X			 * The current file is exhausted.
	X			 * Pop the nonexistent block and the ATTN bytes
	X			 *  from the input stack;
	X			 * Turn on the output if necessary;
	X			 * Close and pop the file.
	X			 */
	X
	X			nxtin += BSIZE + 2;
	X			if (curfile->af_hide) {
	X				if (--hidecnt == 0 && falsecnt == 0) {
	X					quec(ATTN);
	X					quec(AT_OUTON);
	X				}
	X			}
	X			if (curfile->af_fd != STDINPUT) {
	X				close(curfile->af_fd);
	X			}
	X			free(curfile->af_name);
	X			if (--curfile >= &filestk[0]){
	X				break;
	X			}
	X
	X			/*
	X			 * no more current files remain - open the next
	X			 * file to be processed (if there is one),
	X			 * or pushback the EOF character and an ATTN
	X			 * so that further nxtc() calls return EOF.
	X			 */
	X
	X			if (*nxtfile == (char *) 0) {
	X				unc(AT_EBLK);
	X				unc(ATTN);
	X				unc('\0');
	X				break;
	X			}
	X			pushfile(*nxtfile++, PF_NOLOOK, PF_NOHIDE);
	X			break;
	X
	X		default:
	X			bombf("illegal character in input: 0x%x", ATTN);
	X		}
	X	}
	X
	X	return(ch);
	X}
	X
	X/*
	X * untok() - push back the most recent token (less ATTN bytes)
	X *  from the output stream into the input stream.
	X */
	X
	Xuntok()
	X{
	X	char *cp;
	X
	X	for (cp = nxtout - 1; cp >= curtext; --cp) {
	X		if (cp > curtext && *(cp - 1) == ATTN) {
	X			--cp;
	X		} else {
	X			if (*cp == '\n' && curfile->af_raw) {
	X				curfile->af_line--;
	X			}
	X			unc(*cp);
	X		}
	X	}
	X	nxtout = dispose(curtext);
	X}
	X
	X/*
	X * pushmac() - push the given macro value back into the input stream.
	X *  Used to expand a macro.
	X * pushmac() is passed a pointer to the END of a string to be pushed
	X *  (some part of a macro's replacement text).  Pushmac() pushes the string
	X *  backwards onto the input stack until it comes to a null-terminator or
	X *  an ATTN byte.  It returns a pointer to the terminator.
	X */
	X
	Xchar *
	Xpushmac(v)
	Xchar *v;	/* points to a null-terminator or other ignored byte	*/
	X{
	X	if (curfile->af_raw) {
	X		unc(AT_EPUSH);
	X		unc(ATTN);
	X		curfile->af_raw = FALSE;
	X	}
	X	while (*--v != '\0' && *v != ATTN) {
	X		if (nxtin-- < &istk[0]) {
	X			over();
	X		}
	X		*nxtin = *v;
	X	}
	X	return(v);
	X}
	X
	X/*
	X * pushfile() - effectively push the given file into the input stream.
	X *  Used to include a file.
	X */
	X
	Xpushfile(name, itype, hide)
	Xchar *name;
	Xint itype;
	X{
	X#define PNLEN 257
	X	char pname[PNLEN];
	X	char *cp;
	X	char **dp;
	X	struct afile *ip;
	X	char *rindex();
	X	char *malloc();
	X
	X
	X	if (++curfile >= &filestk[FILESIZ]) {
	X		--curfile;
	X		warnf("too many nested include files.  skipping `%s'", name);
	X		return;
	X	}
	X
	X	/*
	X	 * if the name is to be opened with no modification, do that.
	X	 * If the directory of the current file is to be searched, do that.
	X	 * Search each directory in the list for the file.
	X	 */
	X
	X	if (name[0] == '/' || itype == PF_NOLOOK) {
	X		(void) strcpy(pname, name);
	X		if (strcmp(name, "-") == 0) {
	X			curfile->af_fd = STDINPUT;
	X		} else {
	X			curfile->af_fd = open(pname, 0);
	X		}
	X	} else {
	X		curfile->af_fd = -1;
	X		if (itype == PF_DOT) {
	X			(void) strcpy(pname, (curfile - 1)->af_name);
	X			if ((cp = rindex(pname, '/'))) {
	X				++cp;
	X			} else {
	X				cp = &pname[0];
	X			}
	X			if (cp + strlen(name) >= &pname[PNLEN]) {
	X				--curfile;
	X				bombf("name too long `%s%s'", pname, name);
	X			}
	X			(void) strcpy(cp, name);
	X			curfile->af_fd = open(pname, 0);
	X		}
	X		for (dp = &dirlist[0]; *dp && curfile->af_fd < 0; dp++) {
	X			cp = &pname[0] + strlen(*dp);
	X			if (cp >= &pname[PNLEN]) {
	X				--curfile;
	X				bombf("name too long `%s'", *dp);
	X			}
	X			(void) strcpy(pname, *dp);
	X			if (cp > &pname[0] && *(cp - 1) != '/') {
	X				*cp++ = '/';
	X				*cp = '\0';
	X			}
	X			if (cp + strlen(name) >= &pname[PNLEN]) {
	X				--curfile;
	X				bombf("name too long `%s%s'", pname, name);
	X			}
	X			(void) strcpy(cp, name);
	X			curfile->af_fd = open(pname, 0);
	X		}
	X	}
	X	if (curfile->af_fd < 0) {
	X		--curfile;
	X		warnf("cannot find%s file `%s'",
	X		  curfile > &filestk[0] ? " include" : "", name);
	X		return;
	X	}
	X
	X	/*
	X	 * the file is open.
	X	 * See if this is a recursive include.
	X	 */
	X
	X	for (ip = &filestk[0]; ip < curfile; ip++) {
	X		if (strcmp(ip->af_name, pname) == 0) {
	X			close(curfile->af_fd);
	X			--curfile;
	X			warnf("skipping recursive inclusion of `%s'", pname);
	X			return;
	X		}
	X	}
	X
	X	/*
	X	 * fill in the rest of the afile structure.
	X	 */
	X
	X	if (!(curfile->af_name = malloc((unsigned) strlen(pname) + 1))) {
	X		--curfile;
	X		bombf("out of memory");
	X	}
	X	(void) strcpy(curfile->af_name, pname);
	X	curfile->af_line = 1;
	X	curfile->af_raw = TRUE;
	X	curfile->af_hide = hide;
	X	if (hide) {
	X		if (hidecnt++ == 0 && falsecnt == 0) {
	X			quec(ATTN);
	X			quec(AT_OUTOFF);
	X		}
	X	}
	X	unc(AT_EBLK);
	X	unc(ATTN);
	X
	X#undef PNLEN
	X}
	X
	X/*
	X * quec() - move a character to the output queue, pend[]
	X */
	X
	Xquec(c)
	Xchar c;
	X{
	X	*nxtout = c;
	X	if (++nxtout >= &pend[PENDSIZ]) {
	X		bombf("too much forward search");
	X	}
	X}
	X
	X/*
	X * questr() - move the null-terminated string to the output queue, pend[]
	X *  Used only by xxlex().
	X */
	X
	Xquestr(s, len)
	Xregister char *s;
	Xint len;		/* length (in bytes) of the string to be moved */
	X{
	X	register char *d = nxtout;
	X
	X
	X	if (d + len < &pend[PENDSIZ]) {
	X		while (*d++ = *s++)
	X			;
	X		nxtout += len;
	X	} else {
	X		bombf("too much forward search");
	X	}
	X}
	X
	X/*
	X * writepend() - write pending data to the output file, scanning for
	X *  output control characters.  Called only by the macro outpend().
	X */
	X
	Xwritepend()
	X{
	X	char *cp;
	X
	X	for (cp = &pend[0]; cp < nxtout; cp++) {
	X		if (*cp != ATTN) {
	X			if (dooutput) {
	X				putchar(*cp);
	X			}
	X		} else {
	X			switch(*++cp) {
	X			case AT_OUTON:
	X				dooutput = TRUE;
	X				break;
	X			case AT_OUTOFF:
	X				dooutput = FALSE;
	X				break;
	X			default:
	X				bombf("INTERNAL illegal character in output: 0x%x", ATTN);
	X			}
	X		}
	X	}
	X	nxtout = &pend[0];
	X}
	X
	X/*
	X * dispose() - dispose of pending output.
	X *  output from the given point to nxtout is discarded, output control ATTN's
	X *  are not discarded.
	X */
	X
	Xchar *		/* returns the new end of the buffer (nxttok)	*/
	Xdispose(f)
	Xchar *f;
	X{
	X	char *cp;
	X
	X	for (cp = f; cp < nxtout; cp++) {
	X		if (*cp == ATTN) {
	X			/* copy the ATTN byte and the following code	*/
	X
	X			*f++ = *cp++;
	X			*f++ = *cp;
	X		}
	X	}
	X	nxtout = f;
	X	return(f);
	X}
	X
	X/*
	X * warnf - print a file-specific error and continue;
	X */
	X
	X/*VARARGS1*/
	Xwarnf(s, x1, x2, x3, x4, x5, x6, x7, x8)
	Xchar *s;
	Xint x1, x2, x3, x4, x5, x6, x7, x8;
	X{
	X	if (curfile >= &filestk[0]) {
	X		fprintf(stderr, "\"%s\", line %d: ",
	X		  curfile->af_name, curfile->af_line);
	X	}
	X	fprintf(stderr, s, x1, x2, x3, x4, x5, x6, x7, x8);
	X	fprintf(stderr, "\n");
	X	sawerror = TRUE;
	X}
	X
	X/*
	X * bombf - print a file-specific error and exit.
	X */
	X
	X/*VARARGS1*/
	Xbombf(s, x1, x2, x3, x4, x5, x6, x7, x8)
	Xchar *s;
	Xint x1, x2, x3, x4, x5, x6, x7, x8;
	X{
	X	warnf(s, x1, x2, x3, x4, x5, x6, x7, x8);
	X	exit(1);
	X}
	X
	X/*
	X * warn - print a non-file-specific error and continue.
	X */
	X
	X/*VARARGS1*/
	Xwarn(s, x1, x2, x3, x4, x5, x6, x7, x8)
	Xchar *s;
	Xint x1, x2, x3, x4, x5, x6, x7, x8;
	X{
	X	fprintf(stderr, s, x1, x2, x3, x4, x5, x6, x7, x8);
	X	fprintf(stderr, "\n");
	X	sawerror = TRUE;
	X}
	X
	X/*
	X * bomb - print a non-file-specific error and exit.
	X */
	X
	X/*VARARGS1*/
	Xbomb(s, x1, x2, x3, x4, x5, x6, x7, x8)
	Xchar *s;
	Xint x1, x2, x3, x4, x5, x6, x7, x8;
	X{
	X	fprintf(stderr, s, x1, x2, x3, x4, x5, x6, x7, x8);
	X	exit(1);
	X}
	X
	X/*
	X * over() - input pushback overflow
	X */
	X
	Xover()
	X{
	X	bombf("too much pushback");
	X}
SHAR_EOF
if test 8707 -ne "`wc -c < 'io.c'`"
then
	echo shar: error transmitting "'io.c'" '(should have been 8707 characters)'
fi
fi # end of overwriting check
#	End of shell archive
exit 0

sources-request@panda.UUCP (09/20/85)

Mod.sources:  Volume 3, Issue 14
Submitted by: decvax!tektronix!tekig4!bradn


This is the second half of scpp.  The first half contains a brief explanation
of the purpose of the program.

Brad Needham
Tektronix, Inc.
...decvax!tektronix!tekig4!bradn

-------- cut along the dashed line -----
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	parse.y
#	scpp.c
#	scpp.h
# This archive created: Thu Sep 19 12:35:40 1985
export PATH; PATH=/bin:$PATH
echo shar: extracting "'parse.y'" '(10067 characters)'
if test -f 'parse.y'
then
	echo shar: will not over-write existing file "'parse.y'"
else
sed 's/^	X//' << \SHAR_EOF > 'parse.y'
	X/*
	X * parse.y - #if parser for the selective C preprocessor, scpp.
	X *
	X * Copyright (c) 1985 by
	X * Tektronix, Incorporated Beaverton, Oregon 97077
	X * All rights reserved.
	X *
	X * Permission is hereby granted for personal, non-commercial
	X * reproduction and use of this program, provided that this
	X * notice and all copyright notices are included in any copy.
	X */
	X
	X%term	MUL		/* *		*/
	X%term	DIV		/* /		*/
	X%term	MOD		/* %		*/
	X%term	PLUS		/* +		*/
	X%term	MINUS		/* -		*/
	X%term	LS		/* <<		*/
	X%term	RS		/* >>		*/
	X%term	AND		/* &		*/
	X%term	OR		/* |		*/
	X%term	ER		/* ^		*/
	X%term	LT		/* <		*/
	X%term	LE		/* <=		*/
	X%term	GT		/* >		*/
	X%term	GE		/* >=		*/
	X%term	EQ		/* ==		*/
	X%term	NE		/* !=		*/
	X%term	ANDAND		/* &&		*/
	X%term	OROR		/* ||		*/
	X%term	CM		/* , (comma)	*/
	X%term	QUEST		/* ?		*/
	X%term	COLON		/* :		*/
	X%term	NOT		/* !		*/
	X%term	COMPL		/* ~		*/
	X%term	LP		/* (		*/
	X%term	RP		/* )		*/
	X%term	INT		/* an integer	*/
	X%term	FLOAT		/* a float	*/
	X%term	IDENT		/* a identifier	*/
	X%term	QUOTE		/* ' (apostrophe) */
	X%term	DQUOTE		/* "		*/
	X%term	BACKS		/* \ (backslash) */
	X%term	OPENC		/* open comment sequence */
	X%term	CLOSEC		/* close comment sequence */
	X%term	WHITE		/* whitespace	*/
	X%term	NL		/* newline	*/
	X%term	QNL		/* escaped (quoted) newline	*/
	X%term	COMMENT		/* a comment	*/
	X%term	OTHER		/* anything else */
	X%term	STRING		/* a double-quote enclosed string constant	*/
	X%term	CHARS		/* a single-quote enclosed char constant	*/
	X%term	POUNDLINE	/*
	X			 * The initial '#' of a preprocessor directive
	X			 * (as opposed to a normal '#', which is of type OTHER).
	X			 */
	X%term	DEFMAC		/* an uninterpreted 'defined(x)' invocation	*/
	X
	X%left	CM
	X%right	QUEST COLON
	X%left	OROR
	X%left	ANDAND
	X%left	OR
	X%left	ER
	X%left	AND
	X%left	EQ NE
	X%left	LT LE GE GT
	X%left	LS RS
	X%left	PLUS MINUS
	X%left	MUL DIV MOD
	X%right	NOT COMPL
	X%left	LP
	X
	X%union {
	X	int intval;		/* yacc stack entries	*/
	X	struct anode *lexval;	/* everything in this file	*/
	X}
	X
	X%type <lexval>	exp e term
	X%type <lexval> MUL DIV MOD PLUS MINUS LS RS AND OR ER LT LE GT GE EQ NE
	X		ANDAND OROR CM QUEST COLON NOT COMPL LP RP INT FLOAT IDENT
	X		QUOTE DQUOTE BACKS OPENC CLOSEC WHITE NL QNL COMMENT OTHER
	X		STRING CHARS POUNDLINE DEFMAC
	X
	X%{
	X# include "scpp.h"
	X
	X/*
	X * struct anode - the structure used to pass strings.
	X *  Allocated by mknode();
	X *  Deallocated by freenode().
	X * The string described will be in pend[] and is NOT NULL-TERMINATED.
	X */
	X
	Xstruct anode {
	X	int an_val;	/*
	X			 * lexical (token) value of this string.
	X			 * A value of 0 == this node is free.
	X			 */
	X	int an_ifval;	/* integer result of this expression */
	X};
	X
	X# define NODESIZ 100	/* max number of nodes in a #if expresssion	*/
	Xstruct anode nodepool[NODESIZ];
	X
	Xstruct anode *mknode();
	X
	X# define NIL ((struct anode *) 0)
	X%}
	X
	X%start exp
	X%%
	Xexp:	e
	X		{
	X			/*
	X			 * If the expression can be evaluated, set the result
	X			 */
	X
	X			if ($1->an_val == INT) {
	X				*curif |= $1->an_ifval != 0 ?
	X				  IF_TRUE : IF_FALSE;
	X			}
	X			freenode($1);
	X		}
	X	;
	Xe:	  e MUL e
	X		{
	X			$1->an_ifval = $1->an_ifval * $3->an_ifval;
	X		binop:
	X			$$ = $1;
	X			if ($1->an_val == INT && $3->an_val == INT) {
	X				$$->an_val == INT;
	X			} else {
	X				$$->an_val = OTHER;
	X			}
	X			freenode($2);
	X			freenode($3);
	X		}
	X	| e DIV e
	X		{
	X		  if ($3->an_ifval == 0 && $3->an_val == INT) {
	X			$3->an_val = OTHER;
	X			warnf("division by zero in #if");
	X		  } else {
	X			$1->an_ifval = $1->an_ifval / $3->an_ifval;
	X		  }
	X		  goto binop;
	X		}
	X	| e MOD e
	X		{
	X		  if ($3->an_ifval == 0 && $3->an_val == INT) {
	X			$3->an_val = OTHER;
	X			warnf("mod by zero in #if");
	X		  } else {
	X			$1->an_ifval = $1->an_ifval % $3->an_ifval;
	X		  }
	X		  goto binop;
	X		}
	X	| e PLUS e
	X		{$1->an_ifval = $1->an_ifval + $3->an_ifval; goto binop;}
	X	| e MINUS e
	X		{$1->an_ifval = $1->an_ifval - $3->an_ifval; goto binop;}
	X	| e LS e
	X		{$1->an_ifval = $1->an_ifval << $3->an_ifval; goto binop;}
	X	| e RS e
	X		{$1->an_ifval = $1->an_ifval >> $3->an_ifval; goto binop;}
	X	| e LT e
	X		{$1->an_ifval = $1->an_ifval < $3->an_ifval; goto binop;}
	X	| e GT e
	X		{$1->an_ifval = $1->an_ifval > $3->an_ifval; goto binop;}
	X	| e LE e
	X		{$1->an_ifval = $1->an_ifval <= $3->an_ifval; goto binop;}
	X	| e GE e
	X		{$1->an_ifval = $1->an_ifval >= $3->an_ifval; goto binop;}
	X	| e EQ e
	X		{$1->an_ifval = $1->an_ifval == $3->an_ifval; goto binop;}
	X	| e NE e
	X		{$1->an_ifval = $1->an_ifval != $3->an_ifval; goto binop;}
	X	| e AND e
	X		{$1->an_ifval = $1->an_ifval & $3->an_ifval; goto binop;}
	X	| e ER e
	X		{$1->an_ifval = $1->an_ifval ^ $3->an_ifval; goto binop;}
	X	| e OR e
	X		{$1->an_ifval = $1->an_ifval | $3->an_ifval; goto binop;}
	X	| e ANDAND e
	X		{
	X			/*
	X			 * since this is a logical AND, its value
	X			 *  is known if either subexpression is false.
	X			 */
	X
	X			$$ = $1;
	X			if ($1->an_val == INT && $3->an_val == INT) {
	X				/* both subexpressions are known */
	X				$$->an_ifval = $1->an_ifval && $3->an_ifval;
	X			} else {
	X				if (($1->an_val == INT && !$1->an_ifval) ||
	X				    ($3->an_val == INT && !$3->an_ifval)) {
	X					$$->an_val = INT;
	X					$$->an_ifval = FALSE;
	X				} else {
	X					$$->an_val = OTHER;
	X				}
	X			}
	X			freenode($2); freenode($3);
	X		}
	X	| e OROR e
	X		{
	X			/*
	X			 * since this is a logical OR, its value
	X			 *  is known if either subexpression is true.
	X			 */
	X
	X			$$ = $1;
	X			if ($1->an_val == INT && $3->an_val == INT) {
	X				/* both subexpressions are known */
	X				$$->an_ifval = $1->an_ifval || $3->an_ifval;
	X			} else {
	X				if (($1->an_val == INT && $1->an_ifval) ||
	X				    ($3->an_val == INT && $3->an_ifval)) {
	X					$$->an_val = INT;
	X					$$->an_ifval = TRUE;
	X				} else {
	X					$$->an_val = OTHER;
	X				}
	X			}
	X			freenode($2); freenode($3);
	X		}
	X	| e QUEST e COLON e
	X		{
	X			/*
	X			 * since this is an IF-ELSE, its value is known
	X			 * in some cases even if one subexpression is unknown.
	X			 */
	X
	X			$$ = $1;
	X			if ($1->an_val == INT) {
	X				if ($1->an_ifval) {
	X					$$->an_val = $3->an_val;
	X					$$->an_ifval = $3->an_ifval;
	X				} else {
	X					$$->an_val = $5->an_val;
	X					$$->an_ifval = $5->an_ifval;
	X				}
	X			} else {
	X				$$->an_val = OTHER;
	X			}
	X			freenode($2); freenode($3); freenode($4);
	X			freenode($5);
	X		}
	X	| e CM e
	X		{
	X			/*
	X			 * since this is a comma operator, the value of
	X			 * the first expression is irrelevant.
	X			 */
	X
	X			$$ = $3;
	X			freenode($1);
	X			freenode($2);
	X		}
	X	| term
	X		{$$ = $1;}
	X	;
	Xterm:
	X	  MINUS term
	X		{
	X			$2->an_ifval = -($2->an_ifval);
	X		unop:
	X			$$ = $2;
	X			freenode($1);
	X		}
	X	| NOT term
	X		{$2->an_ifval = !($2->an_ifval); goto unop;}
	X	| COMPL term
	X		{$2->an_ifval = ~($2->an_ifval); goto unop;}
	X	| LP e RP
	X		{
	X			$$ = $2;
	X			freenode($1); freenode($3);
	X		}
	X	| INT
	X		{$$= $1;}
	X	| IDENT
	X		{/* an uninterpreted macro */ $$ = $1;}
	X	| DEFMAC
	X		{/* an uninterpreted 'defined(x)' invocation */ $$ = $1;}
	X	;
	X%%
	X
	Xyyerror(s)
	Xchar *s;
	X{
	X	struct anode *anp;
	X
	X	/* free all nodes */
	X
	X	for (anp = &nodepool[0]; anp < &nodepool[NODESIZ]; anp++) {
	X		anp->an_val = 0;
	X	}
	X	warnf("syntax error in #if");
	X}
	X
	X/*
	X * yylex() - the lexical analyzer for #if statements.
	X *  yylex() reads from the stream of interpreted macros, skipping
	X *  insignificant tokens, then sets yylval appropriately and returns
	X *  the token number of the token.
	X */
	X
	Xint
	Xyylex()
	X{
	X	int tok;
	X
	X
	X	/*
	X	 * Skip whitespace, quoted newlines, and interpreted preprocessor
	X	 * directives;
	X	 * End-of-file or an unquoted newline marks the end of the parse;
	X	 * calculate the value of integers and character constants.
	X	 */
	X
	X	if (!(yylval.lexval = mknode())) {
	X		return(0);
	X	}
	X
	X	while ((tok = gintok()) == WHITE || tok == COMMENT || tok == QNL)
	X		;
	X
	X	if (tok == 0 || tok == NL) {
	X		freenode(yylval.lexval);
	X		yylval.lexval = NIL;
	X		return(0);
	X	}
	X
	X	yylval.lexval->an_val = tok;
	X	if (tok == INT) {
	X		yylval.lexval->an_ifval = inttok(curtext, nxtout);
	X	} else if (tok == CHARS) {
	X		yylval.lexval->an_val = INT;
	X		yylval.lexval->an_ifval = chartok(curtext, nxtout);
	X	}
	X	return(yylval.lexval->an_val);
	X}
	X
	X/*
	X * inttok - convert integer token.
	X *  Given the bounds of a token of type INT, return the value of that integer.
	X */
	X
	Xint
	Xinttok(s, e)
	Xchar *s, *e;
	X{
	X	char *str;	/* points to a (dynamically alloc'ed) copy of the tok */
	X	char *cp;
	X	int base;	/* the radix of this integer			*/
	X	int value;	/* the value to return				*/
	X	int digit;	/* the value of the current digit		*/
	X
	X	/*
	X	 * get a copy of the token (to remove ATTN bytes and null-terminate
	X	 *  the string), and find out what the number base is.
	X	 */
	X
	X	str = savtok(s, e);
	X	cp = str;
	X	if (*cp != '0') {
	X		base = 10;
	X	} else {
	X		if (*cp && (*++cp == 'x' || *cp == 'X')) {
	X			++cp;
	X			base = 16;
	X		} else {
	X			base = 8;
	X		}
	X	}
	X
	X	/*
	X	 * convert the string
	X	 */
	X
	X	value = 0;
	X	for (;*cp; ++cp) {
	X		if (*cp >= '0' && *cp <= '7') {
	X			digit = (int)(*cp - '0');
	X		} else if (*cp >= '8' && *cp <= '9' && base >= 10) {
	X			digit = (int)(*cp - '0');
	X		} else if (*cp >= 'a' && *cp <= 'f' && base == 16) {
	X			digit = (int)(*cp - 'a') + 10;
	X		} else if (*cp >= 'A' && *cp <= 'F' && base == 16) {
	X			digit = (int)(*cp - 'A') + 10;
	X		} else {
	X			break;
	X		}
	X		value = value * base + digit;
	X	}
	X
	X	free(str);
	X	return(value);
	X}
	X
	X/*
	X * chartok() - convert a character constant token.
	X *  given the bounds of a character constant, return the integer value
	X *   of that character constant.
	X */
	X 
	Xint
	Xchartok(s, e)
	Xchar *s, *e;
	X{
	X	char *str;	/* (dynamically alloc'ed) copy of the token	*/
	X	char *cp;
	X	int value;	/* value to return				*/
	X	int cnt;
	X
	X
	X	str = savtok(s, e);
	X
	X	cp = str + 1;
	X	if (*cp != '\\') {
	X		value = (int) *cp;
	X	} else if (*++cp == 'n') {
	X		value = (int) '\n';
	X	} else if (*cp == 't') {
	X		value = (int) '\t';
	X	} else if (*cp == 'b') {
	X		value = (int) '\b';
	X/*--read the book to find out the other chars supported--*/
	X	} else if (*cp >= '0' && *cp <= '7') {
	X		for (value = 0, cnt = 3; cnt >= 1 && *cp >= '0' && *cp <= '7';
	X		  --cnt, ++cp) {
	X			value = value * 8 + (int)(*cp - '0');
	X		}
	X	} else {
	X		value = (int) *cp;
	X	}
	X
	X	free(str);
	X	return(value);
	X}
	X
	Xstruct anode *
	Xmknode()
	X{
	X	struct anode *anp;
	X
	X	for (anp = &nodepool[0];
	X	  anp < &nodepool[NODESIZ] && anp->an_val != 0; anp++)
	X		;
	X	if (anp >= &nodepool[NODESIZ]) {
	X		warnf("#if expression too complex");
	X		return(NIL);
	X	}
	X	anp->an_val = OTHER;
	X	return(anp);
	X}
	X
	Xfreenode(n)
	Xstruct anode *n;
	X{
	X	n->an_val = 0;
	X}
SHAR_EOF
if test 10067 -ne "`wc -c < 'parse.y'`"
then
	echo shar: error transmitting "'parse.y'" '(should have been 10067 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'scpp.c'" '(14405 characters)'
if test -f 'scpp.c'
then
	echo shar: will not over-write existing file "'scpp.c'"
else
sed 's/^	X//' << \SHAR_EOF > 'scpp.c'
	X/*
	X * scpp.c - main processing for the selective C preprocessor, scpp.
	X *
	X * Copyright (c) 1985 by
	X * Tektronix, Incorporated Beaverton, Oregon 97077
	X * All rights reserved.
	X *
	X * Permission is hereby granted for personal, non-commercial
	X * reproduction and use of this program, provided that this
	X * notice and all copyright notices are included in any copy.
	X */
	X
	X#define VARS
	X# include <stdio.h>
	X# include "scpp.h"
	X# include "y.tab.h"
	X
	X/*
	X * actual[] - the array of actual parameters of the macro currently being
	X *  interpreted.
	X */
	X
	Xstruct anactual {
	X	char *aa_val;	/*
	X			 * the value of this actual (a pointer to the null-
	X			 * terminator.  see amacro.am_val in scpp.h).
	X			 */
	X	char *aa_mem;	/*
	X			 * points to the beginning of the aa_val string.
	X			 * Used to later free the value's memory.
	X			 */
	X};
	X#define ACTSIZ MAXPARMS
	Xstruct anactual actual[ACTSIZ];
	Xstruct anactual *actp;	/* the next available slot in actual[] */
	X
	X
	X
	Xmain(argc, argv)
	Xint argc;
	Xchar **argv;
	X{
	X	int tok;	/* current token's value	*/
	X	char *cp;
	X	char *ep;
	X	char **dp;	/* where within dirlist to put the next directory */
	X	struct amacro *np;
	X	char *name;	/* name of the current macro	*/
	X	char *val;	/* value of the current macro	*/
	X	char *defmagic = "defined";	/* name of the 'defined()' macro */
	X	struct amacro *magmac;	/* (temp) slot for the magic macro */
	X
	X	/*
	X	 * init all the global structures
	X	 */
	X
	X	nxtout = &pend[0];
	X	curfile = &filestk[-1];
	X	nxtin = &istk[ISTKSIZ];
	X	curif = &ifstk[-1];
	X	
	X	nxtfile = &catlist[0];
	X	dp = &dirlist[0];
	X
	X	/*
	X	 * setup the keyword symbols and the special macro, 'defined()'.
	X	 */
	X
	X	ikeywords();
	X	magmac = findmac(defmagic, defmagic + strlen(defmagic));
	X	if (magmac->am_name) {
	X		bomb("INTERNAL: 'defined()' macro slot in use");
	X	}
	X	magmac->am_name = defmagic;
	X	magmac->am_npar = 1;
	X	magmac->am_val = &magicval;
	X
	X	while (++argv, --argc > 0) {
	X		cp = *argv;
	X		if (*cp == '-' && *(cp + 1) != '\0') {
	X			switch(*++cp) {
	X			case 'C':
	X				savcom = TRUE;
	X				break;
	X			case 'I':
	X				*dp++ = cp + 1;
	X				break;
	X			case 'M':
	X				/*
	X				 * for each name in the list of whitespace-
	X				 *  separated macro names,
	X				 * Setup a slot for that macro, but leave it
	X				 *  undefined.
	X				 */
	X
	X				while (*cp) {
	X					while (*++cp == ' ' || *cp == '\t' ||
	X					    *cp == '\n')
	X						;
	X					if (*cp == '\0') {
	X						break;
	X					}
	X					for (name = cp; *cp != '\0' &&
	X					  *cp != ' ' && *cp != '\t' &&
	X					  *cp != '\n'; ++cp)
	X						;
	X
	X					np = findmac(name, cp);
	X					if (np->am_name == (char *) 0) {
	X					    np->am_name = savtok(name, cp);
	X						np->am_npar = -1;
	X					}
	X					/* am_val is left as zero */
	X				}
	X				break;
	X			case 'D':
	X				for (name = ++cp; *cp != '\0' && *cp != '=';
	X				  ++cp)
	X					;
	X				if (name == cp) {
	X					warn("missing macro name in `%s'",
	X					  name - 2);
	X					break;
	X				}
	X
	X				if (*cp == '\0') {
	X					/*
	X					 * macro name with no definition.
	X					 * Define the name with no parameters
	X					 *  and with a value of "1".
	X					 */
	X
	X					defmac(name, cp, -1, "1");
	X				} else {
	X					/* macro + definition */
	X
	X					for (*cp++ = '\0', val = cp;
	X					  *cp != '\0'; ++cp)
	X						;
	X					defmac(name, name + strlen(name),
	X					  -1, val);
	X				}
	X				break;
	X			default:
	X				bomb("unknown switch `%c'", *cp);
	X			}
	X		} else {
	X			*nxtfile++ = cp;
	X		}
	X	}
	X
	X	if (nxtfile == &catlist[0]) {
	X		*nxtfile++ = "-";
	X	}
	X	*nxtfile = (char *) 0;
	X	nxtfile = &catlist[0];
	X
	X	*dp++ = "/usr/include";
	X	*dp = (char *) 0;
	X
	X	/*
	X	 * prime the input stack and go,
	X	 * interpreting preprocessor directives along the way.
	X	 */
	X
	X	pushfile(*nxtfile++, PF_NOLOOK, PF_NOHIDE);
	X	do {
	X		tok = gintok();
	X		if (tok == POUNDLINE) {
	X			tok = doctrl(curtext);
	X		}
	X		outpend();	/* even the 0 token needs to be flushed.
	X				 * Otherwise, incomplete comments at the end
	X				 * of the file would be destroyed.
	X				 */
	X	} while (tok != 0);
	X	writepend();		/* flush trailing output	*/
	X
	X	if (curif >= &ifstk[0]) {
	X		warnf("missing endif");
	X	}
	X
	X	exit(sawerror ? 1 : 0);
	X}
	X
	Xint
	Xgintok()	/* get a token, interpreting macro's	*/
	X{
	X	int tok;		/* the current token's value	*/
	X	struct amacro *mac;	/* the current macro		*/
	X	struct amacro *defsym;	/* the macro being checked for 'defined()' */
	X	char *mactext;		/*
	X				 * the start of the invocation of a macro
	X				 * which has parameters.
	X				 */
	X	char *start;		/* the start of the current parameter	*/
	X	int nest;		/*
	X				 * current nesting level of parentheses.
	X				 * used to avoid misinterpreting commas within
	X				 * nested parens as parameter separators.
	X				 */
	X	char *defident;		/*
	X				 * The IDENT parameter for the magic macro,
	X				 * 'defined()' (dynamically alloc'ed).
	X				 * If gintok() is interpreting the magic macro,
	X				 * this variable is marked so that, during the
	X				 * parameter parsing, the first IDENT is saved
	X				 * here.
	X				 */
	X	int parmgripe;		/*
	X				 * "an error message about parameters of
	X				 * this macro has already been printed."
	X				 */
	X	int i;			/* an actual-parameter index	*/
	X	char *cp;		/* a temp pointer		*/
	X
	X	/*
	X	 * special macro values (see scpp.h: struct amacro, field am_val):
	X	 *  noval == a null macro value;
	X	 *  oneval == a macro value of '1';
	X	 *  zeroval == a macro value of '0';
	X	 */
	X
	X	static char nv[2] = {'\0', '\0'};
	X	static char *noval = &nv[1];
	X	static char ov[3] = {'\0', '1', '\0'};
	X	static char *oneval = &ov[2];
	X	static char zv[3] = {'\0', '0', '\0'};
	X	static char *zeroval = &zv[2];
	X
	X
	X	tok = OTHER;
	X	while (tok != DEFMAC && (tok = gtok()) != 0) {
	X		if (tok == QUOTE || tok == DQUOTE) {
	X			tok = gstrtok(tok);
	X		}
	X		if (tok != IDENT) {
	X			return(tok);
	X		}
	X
	X		if ((mac = findmac(curtext, nxtout))->am_name == (char *) 0 ||
	X		    mac->am_val == (char *) 0) {
	X			/* there is no macro by this name currently defined */
	X
	X			return(tok);
	X		}
	X
	X		/*
	X		 * tally this interpretation
	X		 */
	X
	X		++ninterp;
	X
	X		if (mac->am_npar < 0) {
	X			/*
	X			 * the macro has no formal parameters.
	X			 * pushback the replacement text and continue.
	X			 */
	X
	X			(void) dispose(curtext);
	X			(void) pushmac(mac->am_val);
	X			continue;
	X		}
	X
	X		/* this is a macro with formals */
	X
	X		/*
	X		 * save the starting-point of the macro's text.
	X		 * Used for later disposal.  The text is not disposed
	X		 * here in case the macro is a 'defined()' of some non--M'ed
	X		 * macro.
	X		 */
	X
	X		mactext = curtext;
	X
	X		/*
	X		 * collect the comma-separated actual parameters of the macro,
	X		 * ignoring commas within pairs of parens or within strings.
	X		 */
	X
	X		parmgripe = FALSE;
	X		actp = &actual[0];
	X		nest = 0;
	X		if (mac->am_val == &magicval) {
	X			defident = &magicval;
	X		} else {
	X			defident = (char *) 0;
	X		}
	X
	X		if ((tok = nonwhite(gtok)) != LP) {
	X			warnf("missing parenthesis in macro");
	X			parmgripe = TRUE;
	X	
	X			/* pushback the erroneous token	*/
	X			untok();
	X		} else {
	X			do {
	X				/* collect one parameter */
	X
	X				start = nxtout;
	X				while ((tok = gtok())) {
	X					if (tok == CM && nest == 0) {
	X						break;
	X					} else if (tok == RP) {
	X						if (nest > 0) {
	X							--nest;
	X						} else if (nest == 0) {
	X							break;
	X						}
	X					} else if (tok == LP) {
	X						++nest;
	X					} else if (tok == QUOTE ||
	X					  tok == DQUOTE) {
	X						tok = gstrtok(tok);
	X					} else if (tok == IDENT &&
	X					  defident == &magicval) {
	X						defident =
	X						  savtok(curtext, nxtout);
	X					}
	X				}
	X
	X				/*
	X				 * Warn about too many parameters, otherwise,
	X				 * store the parameter in the format of
	X				 * a macro value.
	X				 */
	X
	X				if ((actp - &actual[0]) >= mac->am_npar) {
	X					if (!parmgripe) {
	X					  warnf("macro parameter mismatch");
	X					  parmgripe = TRUE;
	X					}
	X				} else {
	X					cp = savtok(start - 1, curtext);
	X					*cp = '\0';
	X					actp->aa_mem = cp;
	X					while (*++cp)
	X						;
	X					actp->aa_val = cp;
	X					++actp;
	X				}
	X			} while (tok == CM);
	X			if (tok != RP) {
	X				if (!parmgripe) {
	X				  warnf("missing parenthesis in macro");
	X				  parmgripe = TRUE;
	X				}
	X			}
	X		}
	X
	X		/*
	X		 * If there are too few actual parameters, fill out the
	X		 * list with null values.
	X		 */
	X
	X		while (actp - &actual[0] < mac->am_npar) {
	X			if (!parmgripe) {
	X				warnf("parameter mismatch");
	X				parmgripe = TRUE;
	X			}
	X			actp->aa_val = noval;
	X			actp->aa_mem = (char *) 0;
	X			++actp;
	X		}
	X
	X		/*
	X		 * replace the macro invocation with the value of the macro,
	X		 *  replacing formal arguments with the corresponding actual.
	X		 */
	X
	X		if ((cp = mac->am_val) == &magicval) {
	X			/*
	X			 * This is the magic macro, "defined(x)".
	X			 * Interpret only if the parameter is a -M'ed
	X			 *  macro and we are currently parsing a
	X			 *  #if expression.
	X			 * Lookup the parameter (if any);
	X			 * If the parameter is -M'ed, pushback a '1' or '0',
	X			 * depending on whether the macro is defined.
	X			 */
	X
	X			defsym = findmac(defident, defident + strlen(defident));
	X			if (!defsym->am_name || !expparse) {
	X				/*
	X				 * Leave the invocation of defined() untouched.
	X				 */
	X
	X				curtext = mactext;
	X				tok = DEFMAC;
	X			} else {
	X				(void) dispose(mactext);
	X				if (defsym->am_val) {
	X					(void) pushmac(oneval);
	X				} else {
	X					(void) pushmac(zeroval);
	X				}
	X			}
	X			free(defident);
	X		} else {
	X			(void) dispose(mactext);
	X			while (*(cp = pushmac(cp)) == ATTN) {
	X				i = (int) (*--cp) - 1;
	X				if (i < 0 || i >= mac->am_npar) {
	X					warnf(
	X"INTERNAL: parameter number %d out of bounds", i);
	X				} else {
	X					(void) pushmac(actual[i].aa_val);
	X				}
	X			}
	X		}
	X
	X		/*
	X		 * free the actual parameters.
	X		 */
	X
	X		while (--actp >= &actual[0]) {
	X			if (actp->aa_mem) {
	X				free(actp->aa_mem);
	X			}
	X		}
	X	}
	X	return(tok);
	X}
	X
	X/*
	X * gtok() - get a token without interpreting macros or preprocessor directives.
	X *  This is the low-level lexical analyzer.  It exists only because Lex's
	X *  analyzer chokes on long comments.
	X */
	X
	Xint
	Xgtok()
	X{
	X	int tok;
	X
	X
	X	curtext = nxtout;
	X	tok = xxlex();
	X	if (tok == OPENC) {
	X		while ((tok = xxlex()) != CLOSEC) {
	X			if (tok == 0) {
	X				warnf("unterminated comment");
	X				return(0);
	X			}
	X		}
	X		tok = COMMENT;
	X	}
	X	return(tok);
	X}
	X
	X/*
	X * gstrtok - get a string token.  Given the token which starts a string
	X *  or character constant (I.E. QUOTE or DQUOTE), collect the string token
	X *  as if it had been recognised by the lexical analyzer as a single token.
	X */
	X
	Xint
	Xgstrtok(tok)
	Xint tok;		/* token which started the quoted string	*/
	X{
	X	int tok2;		/* the next token's value	*/
	X	char *qstrt;		/* start of a string in pend[]	*/
	X
	X	/*
	X	 * collect the string without interpreting
	X	 * macros.  Allow \' and \" within strings.
	X	 * Newline or EOF terminate strings.
	X	 * Save and restore curtext so that on returning,
	X	 * curtext points to the beginning of the token.
	X	 */
	X
	X	qstrt = curtext;
	X	while ((tok2 = gtok()) != tok) {
	X		if (tok2 == 0) {
	X			/* unterminated quote	*/
	X			curtext = qstrt;
	X			return(0);
	X		}
	X		if (tok2 == NL) {
	X			/* unterminated quote. pushback the newline	*/
	X
	X			untok();
	X			break;
	X		}
	X		if (tok2 == BACKS) {
	X			if (gtok() == 0) {
	X				/* unterminated quote */
	X				curtext = qstrt;
	X				return(0);
	X			}
	X		}
	X	}
	X	curtext = qstrt;
	X	return(tok == DQUOTE ? STRING : CHARS);
	X}
	X
	X/*
	X * findmac - find a macro
	X *  given the bounds of what might be a macro name (possibly containing ATTN
	X *   bytes), return a pointer to the symbol table slot
	X *  corresponding to that name.
	X */
	X
	Xstruct amacro *
	Xfindmac(name, last)
	Xchar *name;	/* points to the beginning of the name.			*/
	Xchar *last;	/* points to the char beyond the end of the name	*/
	X{
	X	/*
	X	 * hash the first 8 chars of the name (less ATTN bytes) into an index;
	X	 * Use that index as a starting point for a linear search
	X	 *  for either the matching slot or an empty slot.
	X	 */
	X
	X	int idx;
	X	char *cp;
	X	char *tp;
	X	int cnt;
	X	struct amacro *np, *start;
	X
	X
	X	for (idx = 0, cp = name, cnt = 0; cp < last && cnt < 8; ++cp) {
	X		if (*cp == ATTN) {
	X			++cp;
	X		} else {
	X			idx += (int) *cp++ & 0xff;
	X			++cnt;
	X		}
	X	}
	X	start = np = &sym[idx % SYMSIZ];
	X
	X	while (np->am_name) {
	X		/*
	X		 * compare the token at 'name' with the macro's name,
	X		 * skipping ATTN bytes and their associated codes.
	X		 */
	X
	X		for (tp = name, cp = np->am_name; tp < last; ++tp) {
	X			if (*tp == ATTN) {
	X				++tp;
	X				continue;
	X			}
	X			if (*tp != *cp++) {
	X				break;
	X			}
	X		}
	X		if (tp == last) {
	X			/* the names match */
	X			break;
	X		}
	X
	X		if (++np >= &sym[SYMSIZ]) {
	X			np = &sym[0];
	X		}
	X		if (np == start) {
	X			bombf("symbol table overflow");
	X		}
	X	}
	X	return(np);
	X}
	X
	X/*
	X * defmac - define a macro
	X */
	X
	Xdefmac(name, end, npar, val)
	Xchar *name;		/* the start of the macro's name		*/
	Xchar *end;		/* points to one char beyond the end of the name */
	Xint npar;		/* # of parameters (-1 == none)			*/
	Xchar *val;		/* the beginning of the value string		*/
	X{
	X	char *cp;
	X	struct amacro *np;
	X	struct akeyword *kp;
	X	char *malloc();
	X
	X
	X	/*
	X	 * find the slot for the macro and give it a name if this is the
	X	 * first occurrence of this name.
	X	 */
	X
	X	np = findmac(name, end);
	X	if (!np->am_name) {
	X		np->am_name = savtok(name, end);
	X	} else {
	X		/*
	X		 * Don't allow preprocessor keywords to be defined.
	X		 */
	X
	X		if ((kp = findkey(np)) != (struct akeyword *) 0) {
	X			warnf("redeclaration of keyword \"%s\"", kp->ak_name);
	X			return;
	X		}
	X
	X		/*
	X		 * if the macro is currently defined (I.E. has a value),
	X		 *  reject redefinitions of magic macros.
	X		 * compare the new and old values.
	X		 * If the value or number of parameters differs,
	X		 *  print a warning and destroy the old value.
	X		 * If they are the same, do nothing (return).
	X		 */
	X
	X		if (np->am_val) {
	X			if (np->am_val == &magicval) {
	X				warnf("cannot redefine implicit macro");
	X				return;
	X			}
	X			cp = np->am_val;
	X			while (*--cp)
	X				;
	X			if (np->am_npar == npar && strcmp(cp + 1, val) == 0) {
	X				return;
	X			}
	X
	X			warnf("redeclaration of \"%s\"", np->am_name);
	X			free(cp);
	X		}
	X	}
	X
	X	/*
	X	 * Set the new value and number of parameters.
	X	 * Put a null introduction on the value;
	X	 * Remember that am_val points to the *end* of the value.
	X	 */
	X
	X	np->am_npar = npar;
	X
	X	if (!(cp = malloc((unsigned) strlen(val) + 2))) {
	X		bombf("out of memory");
	X	}
	X	*cp++ = '\0';
	X	strcpy(cp, val);
	X	np->am_val = cp + strlen(cp);
	X}
	X
	X/*
	X * savtok - given the limits of a token string,
	X *  copy that string (less ATTN bytes) into a dynamically allocated buffer
	X *  then return the buffer.
	X */
	X
	Xchar *
	Xsavtok(s, e)
	Xchar *s;	/* first char of token			*/
	Xchar *e;	/* points beyond the last char of token	*/
	X{
	X	char *name;	/* the text of the token -- the value to return	*/
	X	char *cp;
	X	char *malloc();
	X
	X	if (!(name = malloc(e - s + 1))) {
	X		bombf("out of memory");
	X	}
	X
	X	for (cp = name; s < e; ++s) {
	X		if (*s == ATTN) {
	X			++s;
	X		} else {
	X			*cp++ = *s;
	X		}
	X	}
	X	*cp = '\0';
	X
	X	return(name);
	X}
SHAR_EOF
if test 14405 -ne "`wc -c < 'scpp.c'`"
then
	echo shar: error transmitting "'scpp.c'" '(should have been 14405 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'scpp.h'" '(10614 characters)'
if test -f 'scpp.h'
then
	echo shar: will not over-write existing file "'scpp.h'"
else
sed 's/^	X//' << \SHAR_EOF > 'scpp.h'
	X
	X/*
	X * scpp.h - common declarations for the selective C preprocessor, scpp.
	X *
	X * Copyright (c) 1985 by
	X * Tektronix, Incorporated Beaverton, Oregon 97077
	X * All rights reserved.
	X *
	X * Permission is hereby granted for personal, non-commercial
	X * reproduction and use of this program, provided that this
	X * notice and all copyright notices are included in any copy.
	X */
	X
	X# define TRUE	1
	X# define FALSE	0
	X
	X/*
	X * sawerror - "some error was processed" If true, scpp exits non-zero.
	X * Set by the error printout routines, examined when exiting.
	X */
	X#ifdef VARS
	Xint sawerror;
	X#else
	Xextern int sawerror;
	X#endif
	X
	X# define BSIZE	512	/*
	X			 * # of bytes per read -- controls how quickly
	X			 * istk[] is consumed.
	X			 */
	X
	X/*
	X * PENDSIZ is a tunable parameter -- it is the largest number of characters
	X *  which can be waiting to be output.  This number sets a limit on:
	X *  1) the longest comment;
	X *  2) the largest invocation of a macro with parameters, i.e. the number
	X *    of characters between the '(' and the ')'.
	X *  3) the longest preprocessor control line, e.g. #define....
	X * PENDSIZ also controls the input stack size, ISTK.
	X *
	X * Pend[] is the pending output buffer.
	X *
	X * Nxtout points to where within pend[] to put the next token scanned.
	X * Nxtout is advanced by questr() and quec(),
	X *  the primitives for putting stuff in pend[],
	X * and is moved backward by dispose() and outpend(),
	X *  the primitives for getting stuff out of pend[].
	X *
	X * Curtext points to the start of the text within pend[] of
	X * the current token.  Set by gtok() and gintok().
	X * For anyone who uses gtok() or gintok() to get
	X *  a token, the limits of the text of the resultant
	X *  token are curtext and nxtout. (be aware that the
	X *  text of anything in pend[] may contain imbedded
	X *  ATTN bytes.)
	X */
	X
	X# define PENDSIZ 8000
	X# define PENDHIGH 512	/* highwater mark for flushing pend[]	*/
	X#ifdef VARS
	Xchar pend[PENDSIZ];
	Xchar *nxtout;
	Xchar *curtext;
	X#else
	Xextern char pend[];
	Xextern char *nxtout;
	Xchar *curtext;
	X#endif
	Xextern char *dispose();
	X#define outpend() (nxtout < &pend[PENDHIGH] ? 0 : writepend())
	X
	X/*
	X * filestk - the stack containing the state of the current file
	X */
	X
	Xstruct afile {
	X	int	af_fd;		/* the open file's file-descriptor	*/
	X	char	*af_name;	/* the name of the file (dynamic alloc)	*/
	X	int	af_line;	/* the current line in the file		*/
	X	int	af_raw;		/*
	X				 * "scanning unprocessed data rather than
	X				 *  pushed-back data".
	X				 * Used to count input lines.
	X				 * Also used to prevent
	X				 *  interpretation of "#if" expressions whose
	X				 *  truth or falsehood does not depend on
	X				 *  interpreting macros (e.g. #if '\377' > 0). 
	X				 */
	X	int	af_hide;	/*
	X				 * "do not output anything for this file."
	X				 * This file is the result of an uninterpreted
	X				 * "# include".
	X				 */
	X};
	X
	X#define FILESIZ 11	/* max # of include files + 1 (the original file) */
	X#ifdef VARS
	Xstruct afile filestk[FILESIZ];
	Xstruct afile *curfile;	/* the current file.  Initially = &filestk[-1]	*/
	X#else
	Xextern struct afile filestk[];
	Xextern struct afile *curfile;
	X#endif
	X
	X/*
	X * ISTKSIZ is the size of the input/pushback stack.
	X *  It contains up to one block of data for each pending file plus
	X *  one pending token.
	X * The input stack grows down from istk[ISTKSIZ - 1].
	X *
	X * Nxtin points to the next char to read from istk[].
	X * Characters are popped from the stack by nxtc()
	X * and are pushed back on the stack by unc() and
	X * pushmac().
	X */
	X
	X# define ISTKSIZ (FILESIZ * BSIZE + PENDSIZ)
	X#ifdef VARS
	Xchar istk[ISTKSIZ];
	Xchar *nxtin;
	X#else
	Xextern char istk[];
	Xextern char *nxtin;
	X#endif
	Xextern char nxtc();
	Xextern char *pushmac();
	X#define unc(c) (nxtin-- < &istk[0] ? over() : (*nxtin = c))
	X
	X/*
	X * ATTN appears in the input stack to notify nxtc() of some condition,
	X *  in the output queue to notify dispose() or outpend() of some condition,
	X *  or in the value of a macro to notify gintok() of some condition.
	X * ATTN means that the next byte contains a control code.
	X * Input control codes are:
	X *  AT_EPUSH	- end of pushed-back data.  what follows has not been
	X *		 scanned before.
	X *  AT_EBLK	- end of block.  read another block from the current file.
	X * Output control codes are:
	X *  AT_OUTOFF	- disable further output.
	X *  AT_OUTON	- enable output.
	X * Macro value control codes are formal parameter numbers and are not defined.
	X *
	X * note: to avoid breaking string operations and newline recognition,
	X *  do not add an ATTN control code which has a value of '\0', '\\', or '\n'.
	X */
	X
	X#define ATTN		'\376'	/* this char must not appear in any file */
	X#define AT_EPUSH	'\001'
	X#define AT_EBLK		'\002'
	X
	X#define AT_OUTOFF	'\006'
	X#define AT_OUTON	'\007'
	X
	X/*
	X * Ninterp - number of interpretations.  Incremented each time
	X *  gintok() interprets a macro.  Since there is no
	X *  overflow detection, ninterp can be used only to
	X *  see if some interpretation took place -- not to
	X *  count the interpretations (e.g. "oldnint != ninterp"
	X *  works, but "cnt = ninterp - oldnint" may fail).
	X * Used in conjunction with af_raw to prevent
	X *  interpretation of  #if's which are always true
	X *  or false without any macro interpretation (e.g.
	X *  "#if '\377' > 0").
	X */
	X
	X#ifdef VARS
	Xint ninterp;
	X#else
	Xextern int ninterp;
	X#endif
	X
	X/*
	X * Falsecnt - number of currently false #if's;
	X * Hidecnt  - current number of uninterpreted #include's.
	X * Collectively, these variables are used to determine when
	X *  to enable or disable output.
	X */
	X
	X#ifdef VARS
	Xint falsecnt;
	Xint hidecnt;
	X#else
	Xextern int falsecnt;
	Xextern int hidecnt;
	X#endif
	X
	X/*
	X * ifstk[] contains flags describing the state of all currently active #if's.
	X * curif points to the currently active #if within the stack.
	X * The stack grows upward, starting at ifstk[-1].
	X */
	X
	X#define IF_INIF		'\001'	/* "in the 'if' clause rather than 'else'" */
	X#define IF_TRUE		'\002'	/* "this if is currently true"		   */
	X#define IF_FALSE	'\004'	/* "this if is currently false"		   */
	X	/* uninterpreted #if statements are neither true nor false.	   */
	X#define IFSIZ	100		/* maximum number of nested #if's	   */
	X#ifdef VARS
	Xchar ifstk[IFSIZ];
	Xchar *curif;
	X#else
	Xextern char ifstk[];
	Xextern char *curif;
	X#endif
	X
	X/*
	X * expparse - "currently parsing a #if expression".
	X *  Used to prevent interpretation of the macro "defined()" outside
	X *  #if expressions.
	X */
	X
	X#ifdef VARS
	Xint expparse;
	X#else
	Xextern int expparse;
	X#endif
	X
	X/*
	X * the next set of definitions are values of parameters to pushfile().
	X *  PF_NOLOOK	- the filename was given on the command line.  Don't
	X *		 search any directories for it.
	X *  PF_NODOT	- the include filename was enclosed in '<' and '>'.
	X *		 Do not search the current directory (dot) for the it.
	X *  PF_DOT	- the include filename was enclosed in double-quotes.
	X *
	X *  PF_HIDE	- the file is not to be interpreted (I.e. is an include file).
	X *		 Do not output anything while processing this file.
	X *  PF_NOHIDE	- the file is to be interpreted.
	X */
	X
	X# define PF_NOLOOK	(-1)
	X# define PF_NODOT	0
	X# define PF_DOT		1
	X
	X# define PF_HIDE	TRUE
	X# define PF_NOHIDE	FALSE
	X
	X/*
	X * savcom - "save comments and whitespace"
	X *  If false, comments and leading and trailing whitespace are removed
	X *   from interpreted macro definitions.
	X */
	X
	X#ifdef VARS
	Xint savcom;
	X#else
	Xextern int savcom;
	X#endif
	X
	X/*
	X * catlist - the list of files to process; I.E. the filenames from
	X *  the command line.  A zero pointer marks the end of the list.
	X * nxtfile - points to the next element of catlist[] to be processed.
	X */
	X
	X# define CLSIZ		100
	X#ifdef VARS
	Xchar *catlist[CLSIZ];
	Xchar **nxtfile;
	X#else
	Xextern char *catlist[];
	Xextern char **nxtfile;
	X#endif
	X
	X/*
	X * dirlist - the list of directories to search for an include file.
	X *  I.E. all the -I directories from the command line + /usr/include.
	X *  (the search of the current directory of the file is handled separately.)
	X *  A zero pointer marks the end of the list.
	X */
	X
	X#define DLSIZ		100
	X#ifdef VARS
	Xchar *dirlist[DLSIZ];
	X#else
	Xextern char *dirlist[];
	X#endif
	X
	X/*
	X * The symbol table.  All macros are stored in this table.
	X */
	X
	Xstruct amacro {
	X	char *am_name;	/*
	X			 * the name of this macro (dynamically allocated).
	X			 * An am_name value of 0 means this slot is empty.
	X			 * All macros to be interpreted are allocated slots
	X			 * before any files are scanned.  #define and #undef
	X			 * do not allocate or free symbol-table slots.
	X			 */
	X	int am_npar;	/* number of parameters.  -1 == no parameters.	*/
	X	char *am_val;	/*
	X			 * the value (replacement text) of the macro.
	X			 * (dynamically allocated.)
	X			 * An am_val value of 0 means that this macro is not
	X			 * currently defined.
	X			 *
	X			 * am_val points to the null-terminator of the
	X			 * replacement text.  The replacement text is to be
	X			 * read backwards from (am_val - 1) until a null-
	X			 * terminator is found at the other end.
	X			 * An ATTN byte followed (well, preceeded if scanning
	X			 *  forward) by a one-byte integer parameter number
	X			 *  is replaced when expanding this macro by the
	X			 *  corresponding actual parameter.
	X			 * To avoid breaking string operations on val strings,
	X			 * parameter numbers begin at 1 rather than 0
	X			 *
	X			 * A visual example may help:
	X			 *   #define goop(name) hello there name people
	X			 *  results in a sym[] slot containing:
	X			 *
	X			 *  am_name:-------------|
	X			 *			 V
	X			 *			 goop\0
	X			 *  am_npar: 1
	X			 *  am_val:----------------------------|
	X			 *				       V
	X			 *	\0hello there <1><ATTN> people\0
	X			 */
	X};
	X
	X#define SYMSIZ	1001
	X#ifdef VARS
	Xstruct amacro sym[SYMSIZ];
	X#else
	Xextern struct amacro sym[];
	X#endif
	X
	Xextern struct amacro *findmac();
	Xextern char *savtok();
	Xextern int gintok();
	Xextern int gtok();
	X
	X/*
	X * magicval - This (uninitialized) character is used to
	X *  recognize special macro's (e.g. "defined()").
	X * An am_val field of &magicval marks a macro
	X *  as special -- it cannot be undef'ed or redefined,
	X *  and macro expansion in gintok() recognizes it.
	X */
	X
	X#ifdef VARS
	Xchar magicval;
	X#else
	Xextern char magicval;
	X#endif
	X
	X#define MAXPARMS 40	/* max number of formal parameters to a macro	*/
	X
	X/*
	X * the keyword structure - one of these describes each preprocessor keyword.
	X * see ctrl.c for the keyword array, key[].
	X */
	X
	Xstruct akeyword {
	X	char *ak_name;		/* name of this keyword (used to set ak_sym) */
	X	int (*ak_proc)();	/* procedure to interpret this directive     */
	X	struct amacro *ak_sym;	/*
	X				 * pointer to the symbol table slot for this
	X				 * keyword.  Used to recognise the keyword.
	X				 * All keywords in this list are effectively
	X				 * "-M"ed when scpp is invoked.  They are
	X				 * never defined.
	X				 *   This field is initialized at runtime.
	X				 */
	X};
	Xextern struct akeyword *findkey();
	Xextern char *strcpy();
SHAR_EOF
if test 10614 -ne "`wc -c < 'scpp.h'`"
then
	echo shar: error transmitting "'scpp.h'" '(should have been 10614 characters)'
fi
fi # end of overwriting check
#	End of shell archive
exit 0