Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Posting-Version: version B 2.10.2 9/3/84; site panda.UUCP Path: utzoo!decvax!bellcore!petrus!sabre!zeta!epsilon!gamma!ulysses!allegra!mit-eddie!think!harvard!talcott!panda!sources-request From: sources-request@panda.UUCP Newsgroups: mod.sources Subject: scpp - a selective C preprocessor (Part 1 of 2) Message-ID: <924@panda.UUCP> Date: Fri, 20-Sep-85 09:23:04 EDT Article-I.D.: panda.924 Posted: Fri Sep 20 09:23:04 1985 Date-Received: Sat, 21-Sep-85 22:23:19 EDT Sender: jpn@panda.UUCP Lines: 1770 Approved: jpn@panda.UUCP 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.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 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 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 * #[]line[][[] X * where: is the new integer line number for this line, X * 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 .......\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 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 Brought to you by Super Global Mega Corp .com