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 2 of 2) Message-ID: <925@panda.UUCP> Date: Fri, 20-Sep-85 09:24:21 EDT Article-I.D.: panda.925 Posted: Fri Sep 20 09:24:21 1985 Date-Received: Sat, 21-Sep-85 22:33:20 EDT Sender: jpn@panda.UUCP Lines: 1562 Approved: jpn@panda.UUCP 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 exp e term X%type 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 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> 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 Brought to you by Super Global Mega Corp .com