scpp - a selective C preprocessor (Part 2 of 2)
sources-request at panda.UUCP
sources-request at panda.UUCP
Fri Sep 20 23:24:21 AEST 1985
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
More information about the Mod.sources
mailing list