Amiga-Request@cs.odu.edu (Amiga Sources/Binaries Moderator) (01/18/90)
Submitted-by: Olaf 'Rhialto' Seibert <U211344@HNYKUN11.BITNET>
Posting-number: Volume 90, Issue 024
Archive-name: unix/cpp/part03
#!/bin/sh
# This is a shell archive. Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file". To overwrite existing
# files, type "sh file -c". You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g.. If this archive is complete, you
# will see the following message at the end:
# "End of archive 3 (of 5)."
# Contents: Cpp4.c cpp5.c
# Wrapped by tadguy@xanth on Wed Jan 17 19:17:35 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'Cpp4.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'Cpp4.c'\"
else
echo shar: Extracting \"'Cpp4.c'\" \(19642 characters\)
sed "s/^X//" >'Cpp4.c' <<'END_OF_FILE'
X/*
X * C P P 4 . C
X * M a c r o D e f i n i t i o n s
X *
X * Edit History
X * 31-Aug-84 MM USENET net.sources release
X * 04-Oct-84 MM __LINE__ and __FILE__ must call ungetstring()
X * so they work correctly with token concatenation.
X * Added string formal recognition.
X * 25-Oct-84 MM "Short-circuit" evaluate #if's so that we
X * don't print unnecessary error messages for
X * #if !defined(FOO) && FOO != 0 && 10 / FOO ...
X * 31-Oct-84 ado/MM Added token concatenation
X * 6-Nov-84 MM Split off eval stuff
X * 21-Oct-85 RMS Rename `token' to `tokenbuf'.
X * In doundef, don't complain if arg already not defined.
X * 14-Mar-86 FNF Incorporate macro based C debugging package.
X * Port to Commodore AMIGA.
X * 21-Aug-88 Ois Changed concatenation operator to ##. Changed hand-
X * ling of tokens following ##. Added new meaning of #.
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include "cppdef.h"
X#include "cpp.h"
X/*
X * parm[], parmp, and parlist[] are used to store #define() argument
X * lists. nargs contains the actual number of parameters stored.
X */
Xstatic char parm[NPARMWORK + 1]; /* define param work buffer */
Xstatic char *parmp; /* Free space in parm */
Xstatic char *parlist[LASTPARM]; /* -> start of each parameter */
Xstatic int nargs; /* Parameters for this macro */
X
Xdodefine()
X/*
X * Called from control when a #define is scanned. This module
X * parses formal parameters and the replacement string. When
X * the formal parameter name is encountered in the replacement
X * string, it is replaced by a character in the range 128 to
X * 128+NPARAM (this allows up to 32 parameters within the
X * Dec Multinational range). If cpp is ported to an EBCDIC
X * machine, you will have to make other arrangements.
X *
X * There is some special case code to distinguish
X * #define foo bar
X * from #define foo() bar
X *
X * Also, we make sure that
X * #define foo foo
X * expands to "foo" but doesn't put cpp into an infinite loop.
X *
X * A warning message is printed if you redefine a symbol to a
X * different text. I.e,
X * #define foo 123
X * #define foo 123
X * is ok, but
X * #define foo 123
X * #define foo +123
X * is not.
X *
X * The following subroutines are called from define():
X * checkparm called when a token is scanned. It checks through the
X * array of formal parameters. If a match is found, the
X * token is replaced by a control byte which will be used
X * to locate the parameter when the macro is expanded.
X * textput puts a string in the macro work area (parm[]), updating
X * parmp to point to the first free byte in parm[].
X * textput() tests for work buffer overflow.
X * charput puts a single character in the macro work area (parm[])
X * in a manner analogous to textput().
X */
X{
X register int c;
X register DEFBUF *dp; /* -> new definition */
X int isredefine; /* TRUE if redefined */
X char *old; /* Remember redefined */
X#if OK_CONCAT
X register int quoting; /* Remember we saw a # */
X#endif
X extern int save(); /* Save char in work[] */
X
X DBUG_ENTER ("dodefine");
X if (type[(c = skipws())] != LET)
X goto bad_define;
X isredefine = FALSE; /* Set if redefining */
X if ((dp = lookid(c)) == NULL) /* If not known now */
X dp = defendel(tokenbuf, FALSE); /* Save the name */
X else { /* It's known: */
X isredefine = TRUE; /* Remember this fact */
X old = dp->repl; /* Remember replacement */
X dp->repl = NULL; /* No replacement now */
X }
X parlist[0] = parmp = parm; /* Setup parm buffer */
X if ((c = get()) == '(') { /* With arguments? */
X nargs = 0; /* Init formals counter */
X do { /* Collect formal parms */
X if (nargs >= LASTPARM)
X cfatal("Too many arguments for macro", NULLST);
X else if ((c = skipws()) == ')')
X break; /* Got them all */
X else if (type[c] != LET) /* Bad formal syntax */
X goto bad_define;
X scanid(c); /* Get the formal param */
X parlist[nargs++] = parmp; /* Save its start */
X textput(tokenbuf); /* Save text in parm[] */
X } while ((c = skipws()) == ','); /* Get another argument */
X if (c != ')') /* Must end at ) */
X goto bad_define;
X c = ' '; /* Will skip to body */
X }
X else {
X /*
X * DEF_NOARGS is needed to distinguish between
X * "#define foo" and "#define foo()".
X */
X nargs = DEF_NOARGS; /* No () parameters */
X }
X if (type[c] == SPA) /* At whitespace? */
X c = skipws(); /* Not any more. */
X workp = work; /* Replacement put here */
X inmacro = TRUE; /* Keep \<newline> now */
X quoting = 0; /* No # seen yet. */
X while (c != EOF_CHAR && c != '\n') { /* Compile macro body */
X#if OK_CONCAT
X if (c == '#') { /* Token concatenation? */
X if ((c = get()) != '#') { /* No, not really */
X quoting = 1; /* Maybe quoting op. */
X continue;
X }
X while (workp > work && type[workp[-1]] == SPA)
X --workp; /* Erase leading spaces */
X save(TOK_SEP); /* Stuff a delimiter */
X c = skipws(); /* Eat whitespace */
X#if 0
X if (type[c] == LET) /* Another token here? */
X ; /* Stuff it normally */
X else if (type[c] == DIG) { /* Digit string after? */
X while (type[c] == DIG) { /* Stuff the digits */
X save(c); /* Note
X c = get();
X }
X save(TOK_SEP); /* Delimit 2nd token */
X }
X else {
X ciwarn("Strange character after ## (%d.)", c);
X }
X#endif
X continue;
X }
X#endif
X switch (type[c]) {
X case LET:
X#if OK_CONCAT
X checkparm(c, dp, quoting); /* Might be a formal */
X#else
X checkparm(c, dp); /* Might be a formal */
X#endif
X break;
X
X case DIG: /* Number in mac. body */
X case DOT: /* Maybe a float number */
X scannumber(c, save); /* Scan it off */
X break;
X
X case QUO: /* String in mac. body */
X#if STRING_FORMAL
X stparmscan(c, dp); /* Do string magic */
X#else
X stparmscan(c);
X#endif
X break;
X
X case BSH: /* Backslash */
X save('\\');
X if ((c = get()) == '\n')
X wrongline = TRUE;
X save(c);
X break;
X
X case SPA: /* Absorb whitespace */
X /*
X * Note: the "end of comment" marker is passed on
X * to allow comments to separate tokens.
X */
X if (workp[-1] == ' ') /* Absorb multiple */
X break; /* spaces */
X else if (c == '\t')
X c = ' '; /* Normalize tabs */
X /* Fall through to store character */
X default: /* Other character */
X save(c);
X break;
X }
X c = get();
X quoting = 0; /* Only when immediately*/
X /* preceding a formal */
X }
X inmacro = FALSE; /* Stop newline hack */
X unget(); /* For control check */
X if (workp > work && workp[-1] == ' ') /* Drop trailing blank */
X workp--;
X *workp = EOS; /* Terminate work */
X dp->repl = savestring(work); /* Save the string */
X dp->nargs = nargs; /* Save arg count */
X#if DEBUG
X if (debug)
X dumpadef("macro definition", dp);
X#endif
X if (isredefine) { /* Error if redefined */
X if ((old != NULL && dp->repl != NULL && !streq(old, dp->repl))
X || (old == NULL && dp->repl != NULL)
X || (old != NULL && dp->repl == NULL)) {
X cerror("Redefining defined variable \"%s\"", dp->name);
X }
X if (old != NULL) /* We don't need the */
X free(old); /* old definition now. */
X }
X DBUG_VOID_RETURN;
X
Xbad_define:
X cerror("#define syntax error", NULLST);
X inmacro = FALSE; /* Stop <newline> hack */
X DBUG_VOID_RETURN;
X}
X
Xcheckparm(c, dp, quoting)
Xregister int c;
XDEFBUF *dp;
Xint quoting; /* Preceded by a # ? */
X/*
X * Replace this param if it's defined. Note that the macro name is a
X * possible replacement token. We stuff DEF_MAGIC in front of the token
X * which is treated as a LETTER by the token scanner and eaten by
X * the output routine. This prevents the macro expander from
X * looping if someone writes "#define foo foo".
X */
X{
X register int i;
X register char *cp;
X
X DBUG_ENTER ("checkparm");
X scanid(c); /* Get parm to tokenbuf */
X for (i = 0; i < nargs; i++) { /* For each argument */
X if (streq(parlist[i], tokenbuf)) { /* If it's known */
X#if OK_CONCAT
X if (quoting) /* Special handling of */
X save(QUOTE_PARM); /* #formal inside defn */
X#endif
X save(i + MAC_PARM); /* Save a magic cookie */
X DBUG_VOID_RETURN; /* And exit the search */
X }
X }
X if (streq(dp->name, tokenbuf)) /* Macro name in body? */
X save(DEF_MAGIC); /* Save magic marker */
X for (cp = tokenbuf; *cp != EOS;) /* And save */
X save(*cp++); /* The token itself */
X DBUG_VOID_RETURN;
X}
X
X#if STRING_FORMAL
Xstparmscan(delim, dp)
Xint delim;
Xregister DEFBUF *dp;
X/*
X * Scan the string (starting with the given delimiter).
X * The token is replaced if it is the only text in this string or
X * character constant. The algorithm follows checkparm() above.
X * Note that scanstring() has approved of the string.
X */
X{
X register int c;
X
X DBUG_ENTER ("stparmscan");
X /*
X * Warning -- this code hasn't been tested for a while.
X * It exists only to preserve compatibility with earlier
X * implementations of cpp. It is not part of the Draft
X * ANSI Standard C language.
X */
X save(delim);
X instring = TRUE;
X while ((c = get()) != delim
X && c != '\n'
X && c != EOF_CHAR) {
X if (type[c] == LET) /* Maybe formal parm */
X checkparm(c, dp, 0);
X else {
X save(c);
X if (c == '\\')
X save(get());
X }
X }
X instring = FALSE;
X if (c != delim)
X cerror("Unterminated string in macro body", NULLST);
X save(c);
X DBUG_VOID_RETURN;
X}
X#else
Xstparmscan(delim)
Xint delim;
X/*
X * Normal string parameter scan.
X */
X{
X register char *wp;
X register int i;
X extern int save();
X
X DBUG_ENTER ("stparmscan");
X wp = workp; /* Here's where it starts */
X if (!scanstring(delim, save))
X DBUG_VOID_RETURN; /* Exit on scanstring error */
X workp[-1] = EOS; /* Erase trailing quote */
X wp++; /* -> first string content byte */
X for (i = 0; i < nargs; i++) {
X if (streq(parlist[i], wp)) {
X *wp++ = MAC_PARM + PAR_MAC; /* Stuff a magic marker */
X *wp++ = (i + MAC_PARM); /* Make a formal marker */
X *wp = wp[-3]; /* Add on closing quote */
X workp = wp + 1; /* Reset string end */
X DBUG_VOID_RETURN;
X }
X }
X workp[-1] = wp[-1]; /* Nope, reset end quote. */
X DBUG_VOID_RETURN;
X}
X#endif
X
Xdoundef()
X/*
X * Remove the symbol from the defined list.
X * Called from the #control processor.
X */
X{
X register int c;
X
X DBUG_ENTER ("doundef");
X if (type[(c = skipws())] != LET)
X cerror("Illegal #undef argument", NULLST);
X else
X {
X scanid(c); /* Get name to tokenbuf */
X (void) defendel(tokenbuf, TRUE);
X }
X DBUG_VOID_RETURN;
X}
X
Xtextput(text)
Xchar *text;
X/*
X * Put the string in the parm[] buffer.
X */
X{
X register int size;
X
X DBUG_ENTER ("textput");
X size = strlen(text) + 1;
X if ((parmp + size) >= &parm[NPARMWORK])
X cfatal("Macro work area overflow", NULLST);
X else {
X strcpy(parmp, text);
X parmp += size;
X }
X DBUG_VOID_RETURN;
X}
X
Xcharput(c)
Xregister int c;
X/*
X * Put the byte in the parm[] buffer.
X */
X{
X if (parmp >= &parm[NPARMWORK])
X cfatal("Macro work area overflow", NULLST);
X else {
X *parmp++ = c;
X }
X}
X
X/*
X * M a c r o E x p a n s i o n
X */
X
Xstatic DEFBUF *macro; /* Catches start of infinite macro */
X
Xexpand(tokenp)
Xregister DEFBUF *tokenp;
X/*
X * Expand a macro. Called from the cpp mainline routine (via subroutine
X * macroid()) when a token is found in the symbol table. It calls
X * expcollect() to parse actual parameters, checking for the correct number.
X * It then creates a "file" containing a single line containing the
X * macro with actual parameters inserted appropriately. This is
X * "pushed back" onto the input stream. (When the get() routine runs
X * off the end of the macro line, it will dismiss the macro itself.)
X */
X{
X register int c;
X register FILEINFO *file;
X extern FILEINFO *getfile();
X
X DBUG_ENTER ("expand");
X#if DEBUG
X if (debug)
X dumpadef("expand entry", tokenp);
X#endif
X /*
X * If no macro is pending, save the name of this macro
X * for an eventual error message.
X */
X if (recursion++ == 0)
X macro = tokenp;
X else if (recursion == RECURSION_LIMIT) {
X cerror("Recursive macro definition of \"%s\"", tokenp->name);
X fprintf(stderr, "(Defined by \"%s\")\n", macro->name);
X if (rec_recover) {
X do {
X c = get();
X } while (infile != NULL && infile->fp == NULL);
X unget();
X recursion = 0;
X DBUG_VOID_RETURN;
X }
X }
X /*
X * Here's a macro to expand.
X */
X nargs = 0; /* Formals counter */
X parmp = parm; /* Setup parm buffer */
X switch (tokenp->nargs) {
X case (-2): /* __LINE__ */
X sprintf(work, "%d", line);
X ungetstring(work);
X break;
X
X case (-3): /* __FILE__ */
X for (file = infile; file != NULL; file = file->parent) {
X if (file->fp != NULL) {
X sprintf(work, "\"%s\"", (file->progname != NULL)
X ? file->progname : file->filename);
X ungetstring(work);
X break;
X }
X }
X break;
X
X default:
X /*
X * Nothing funny about this macro.
X */
X if (tokenp->nargs < 0)
X cfatal("Bug: Illegal __ macro \"%s\"", tokenp->name);
X while ((c = skipws()) == '\n') /* Look for (, skipping */
X wrongline = TRUE; /* spaces and newlines */
X if (c != '(') {
X /*
X * If the programmer writes
X * #define foo() ...
X * ...
X * foo [no ()]
X * just write foo to the output stream.
X */
X unget();
X cwarn("Macro \"%s\" needs arguments", tokenp->name);
X fputs(tokenp->name, stdout);
X DBUG_VOID_RETURN;
X }
X else if (expcollect()) { /* Collect arguments */
X if (tokenp->nargs != nargs) { /* Should be an error? */
X cwarn("Wrong number of macro arguments for \"%s\"",
X tokenp->name);
X }
X#if DEBUG
X if (debug)
X dumpparm("expand");
X#endif
X } /* Collect arguments */
X case DEF_NOARGS: /* No parameters just stuffs */
X expstuff(tokenp); /* Do actual parameters */
X } /* nargs switch */
X DBUG_VOID_RETURN;
X}
X
XFILE_LOCAL int
Xexpcollect()
X/*
X * Collect the actual parameters for this macro. TRUE if ok.
X */
X{
X register int c;
X register int paren; /* For embedded ()'s */
X extern int charput();
X
X DBUG_ENTER ("expcollect");
X for (;;) {
X paren = 0; /* Collect next arg. */
X while ((c = skipws()) == '\n') /* Skip over whitespace */
X wrongline = TRUE; /* and newlines. */
X if (c == ')') { /* At end of all args? */
X /*
X * Note that there is a guard byte in parm[]
X * so we don't have to check for overflow here.
X */
X *parmp = EOS; /* Make sure terminated */
X break; /* Exit collection loop */
X }
X else if (nargs >= LASTPARM)
X cfatal("Too many arguments in macro expansion", NULLST);
X parlist[nargs++] = parmp; /* At start of new arg */
X for (;; c = cget()) { /* Collect arg's bytes */
X if (c == EOF_CHAR) {
X cerror("end of file within macro argument", NULLST);
X DBUG_RETURN (FALSE); /* Sorry. */
X }
X else if (c == '\\') { /* Quote next character */
X charput(c); /* Save the \ for later */
X charput(cget()); /* Save the next char. */
X continue; /* And go get another */
X }
X else if (type[c] == QUO) { /* Start of string? */
X scanstring(c, charput); /* Scan it off */
X continue; /* Go get next char */
X }
X else if (c == '(') /* Worry about balance */
X paren++; /* To know about commas */
X else if (c == ')') { /* Other side too */
X if (paren == 0) { /* At the end? */
X unget(); /* Look at it later */
X break; /* Exit arg getter. */
X }
X paren--; /* More to come. */
X }
X else if (c == ',' && paren == 0) /* Comma delimits args */
X break;
X else if (c == '\n') /* Newline inside arg? */
X wrongline = TRUE; /* We'll need a #line */
X charput(c); /* Store this one */
X } /* Collect an argument */
X charput(EOS); /* Terminate argument */
X#if DEBUG
X if (debug)
X printf("parm[%d] = \"%s\"\n", nargs, parlist[nargs - 1]);
X#endif
X } /* Collect all args. */
X DBUG_RETURN (TRUE); /* Normal return */
X}
X
X
X#if OK_CONCAT
X
XFILE_LOCAL
Xchar *doquoting(to, from)
Xregister char *to;
Xregister char *from;
X{
X *to++ = '"';
X while (*from) {
X if (*from == '\\' || *from == '"')
X *to++ = '\\';
X *to++ = *from++;
X }
X *to++ = '"';
X
X return to;
X}
X
X#endif
X
XFILE_LOCAL
Xexpstuff(tokenp)
XDEFBUF *tokenp; /* Current macro being expanded */
X/*
X * Stuff the macro body, replacing formal parameters by actual parameters.
X */
X{
X register int c; /* Current character */
X register char *inp; /* -> repl string */
X register char *defp; /* -> macro output buff */
X int size; /* Actual parm. size */
X char *defend; /* -> output buff end */
X int string_magic; /* String formal hack */
X FILEINFO *file; /* Funny #include */
X#if OK_CONCAT
X register char quoting; /* Quote macro argument */
X#endif
X extern FILEINFO *getfile();
X
X DBUG_ENTER ("expstuff");
X file = getfile(NBUFF, tokenp->name);
X inp = tokenp->repl; /* -> macro replacement */
X defp = file->buffer; /* -> output buffer */
X defend = defp + (NBUFF - 1); /* Note its end */
X if (inp != NULL) {
X quoting = 0;
X while ((c = (*inp++ & 0xFF)) != EOS) {
X#if OK_CONCAT
X if (c == QUOTE_PARM) { /* Special token for # */
X quoting = 1; /* set flag, for later */
X continue; /* Get next character */
X }
X#endif
X if (c >= MAC_PARM && c <= (MAC_PARM + PAR_MAC)) {
X string_magic = (c == (MAC_PARM + PAR_MAC));
X if (string_magic)
X c = (*inp++ & 0xFF);
X /*
X * Replace formal parameter by actual parameter string.
X */
X if ((c -= MAC_PARM) < nargs) {
X size = strlen(parlist[c]);
X#if OK_CONCAT
X if (quoting) {
X size++;
X size *= 2; /* worst case condition */
X }
X#endif
X if ((defp + size) >= defend)
X goto nospace;
X /*
X * Erase the extra set of quotes.
X */
X if (string_magic && defp[-1] == parlist[c][0]) {
X strcpy(defp-1, parlist[c]);
X defp += (size - 2);
X }
X#if OK_CONCAT
X else if (quoting)
X defp = doquoting(defp, parlist[c]);
X#endif
X else {
X strcpy(defp, parlist[c]);
X defp += size;
X }
X }
X }
X else if (defp >= defend) {
Xnospace: cfatal("Out of space in macro \"%s\" arg expansion",
X tokenp->name);
X }
X else {
X *defp++ = c;
X }
X quoting = 0;
X }
X }
X *defp = EOS;
X#if DEBUG
X if (debug > 1)
X printf("macroline: \"%s\"\n", file->buffer);
X#endif
X DBUG_VOID_RETURN;
X}
X
X#if DEBUG
Xdumpparm(why)
Xchar *why;
X/*
X * Dump parameter list.
X */
X{
X register int i;
X
X DBUG_ENTER ("dumpparm");
X printf("dump of %d parameters (%d bytes total) %s\n",
X nargs, parmp - parm, why);
X for (i = 0; i < nargs; i++) {
X printf("parm[%d] (%d) = \"%s\"\n",
X i + 1, strlen(parlist[i]), parlist[i]);
X }
X DBUG_VOID_RETURN;
X}
X#endif
END_OF_FILE
if test 19642 -ne `wc -c <'Cpp4.c'`; then
echo shar: \"'Cpp4.c'\" unpacked with wrong size!
fi
# end of 'Cpp4.c'
fi
if test -f 'cpp5.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'cpp5.c'\"
else
echo shar: Extracting \"'cpp5.c'\" \(23086 characters\)
sed "s/^X//" >'cpp5.c' <<'END_OF_FILE'
X/*
X * C P P 5 . C
X * E x p r e s s i o n E v a l u a t i o n
X *
X * Edit History
X * 31-Aug-84 MM USENET net.sources release
X * 04-Oct-84 MM __LINE__ and __FILE__ must call ungetstring()
X * so they work correctly with token concatenation.
X * Added string formal recognition.
X * 25-Oct-84 MM "Short-circuit" evaluate #if's so that we
X * don't print unnecessary error messages for
X * #if !defined(FOO) && FOO != 0 && 10 / FOO ...
X * 31-Oct-84 ado/MM Added token concatenation
X * 6-Nov-84 MM Split from #define stuff, added sizeof stuff
X * 19-Nov-84 ado #if error returns TRUE for (sigh) compatibility
X * 21-Oct-85 RMS Rename `token' to `tokenbuf'
X * 23-Oct-85 RMS Treat undefined symbols as having value zero.
X * 14-Mar-86 FNF Incorporate macro based C debugging package.
X * Port to Commodore Amiga.
X * 20-Aug-88 Ois Conditionally compile sizeof stuff.
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include "cppdef.h"
X#include "cpp.h"
X
X/*
X * Evaluate an #if expression.
X */
X
Xstatic char *opname[] = { /* For debug and error messages */
X"end of expression", "val", "id",
X "+", "-", "*", "/", "%",
X "<<", ">>", "&", "|", "^",
X "==", "!=", "<", "<=", ">=", ">",
X "&&", "||", "?", ":", ",",
X "unary +", "unary -", "~", "!", "(", ")", "(none)",
X};
X
X/*
X * opdope[] has the operator precedence:
X * Bits
X * 7 Unused (so the value is always positive)
X * 6-2 Precedence (000x .. 017x)
X * 1-0 Binary op. flags:
X * 01 The binop flag should be set/cleared when this op is seen.
X * 10 The new value of the binop flag.
X * Note: Expected, New binop
X * constant 0 1 Binop, end, or ) should follow constants
X * End of line 1 0 End may not be preceeded by an operator
X * binary 1 0 Binary op follows a value, value follows.
X * unary 0 0 Unary op doesn't follow a value, value follows
X * ( 0 0 Doesn't follow value, value or unop follows
X * ) 1 1 Follows value. Op follows.
X */
X
Xstatic char opdope[OP_MAX] = {
X 0001, /* End of expression */
X 0002, /* Digit */
X 0000, /* Letter (identifier) */
X 0141, 0141, 0151, 0151, 0151, /* ADD, SUB, MUL, DIV, MOD */
X 0131, 0131, 0101, 0071, 0071, /* ASL, ASR, AND, OR, XOR */
X 0111, 0111, 0121, 0121, 0121, 0121, /* EQ, NE, LT, LE, GE, GT */
X 0061, 0051, 0041, 0041, 0031, /* ANA, ORO, QUE, COL, CMA */
X/*
X * Unary op's follow
X */
X 0160, 0160, 0160, 0160, /* NEG, PLU, COM, NOT */
X 0170, 0013, 0023, /* LPA, RPA, END */
X};
X/*
X * OP_QUE and OP_RPA have alternate precedences:
X */
X#define OP_RPA_PREC 0013
X#define OP_QUE_PREC 0034
X
X/*
X * S_ANDOR and S_QUEST signal "short-circuit" boolean evaluation, so that
X * #if FOO != 0 && 10 / FOO ...
X * doesn't generate an error message. They are stored in optab.skip.
X */
X#define S_ANDOR 2
X#define S_QUEST 1
X
Xtypedef struct optab {
X char op; /* Operator */
X char prec; /* Its precedence */
X char skip; /* Short-circuit: TRUE to skip */
X} OPTAB;
Xstatic int evalue; /* Current value from evallex() */
X
X#ifdef nomacargs
XFILE_LOCAL int
Xisbinary(op)
Xregister int op;
X{
X return (op >= FIRST_BINOP && op <= LAST_BINOP);
X}
X
XFILE_LOCAL int
Xisunary(op)
Xregister int op;
X{
X return (op >= FIRST_UNOP && op <= LAST_UNOP);
X}
X#else
X#define isbinary(op) (op >= FIRST_BINOP && op <= LAST_BINOP)
X#define isunary(op) (op >= FIRST_UNOP && op <= LAST_UNOP)
X#endif
X
X/*
X * The following definitions are used to specify basic variable sizes.
X */
X
X#if OK_SIZEOF
X
X#ifndef S_CHAR
X#define S_CHAR (sizeof (char))
X#endif
X#ifndef S_SINT
X#ifdef manx /* Aztec/Manx C does not like "short int" */
X#define S_SINT (sizeof (short))
X#else
X#define S_SINT (sizeof (short int))
X#endif
X#endif
X#ifndef S_INT
X#define S_INT (sizeof (int))
X#endif
X#ifndef S_LINT
X#define S_LINT (sizeof (long int))
X#endif
X#ifndef S_FLOAT
X#define S_FLOAT (sizeof (float))
X#endif
X#ifndef S_DOUBLE
X#define S_DOUBLE (sizeof (double))
X#endif
X#ifndef S_PCHAR
X#define S_PCHAR (sizeof (char *))
X#endif
X#ifndef S_PSINT
X#ifdef manx /* Aztec/Manx C does not like "short int" */
X#define S_PSINT (sizeof (short *))
X#else
X#define S_PSINT (sizeof (short int *))
X#endif
X#endif
X#ifndef S_PINT
X#define S_PINT (sizeof (int *))
X#endif
X#ifndef S_PLINT
X#define S_PLINT (sizeof (long int *))
X#endif
X#ifndef S_PFLOAT
X#define S_PFLOAT (sizeof (float *))
X#endif
X#ifndef S_PDOUBLE
X#define S_PDOUBLE (sizeof (double *))
X#endif
X#ifndef S_PFPTR
X#define S_PFPTR (sizeof (int (*)()))
X#endif
X
X
Xtypedef struct types {
X short type; /* This is the bit if */
X char *name; /* this is the token word */
X} TYPES;
X
Xstatic TYPES basic_types[] = {
X { T_CHAR, "char", },
X { T_INT, "int", },
X { T_FLOAT, "float", },
X { T_DOUBLE, "double", },
X { T_SHORT, "short", },
X { T_LONG, "long", },
X { T_SIGNED, "signed", },
X { T_UNSIGNED, "unsigned", },
X { 0, NULL, }, /* Signal end */
X};
X
X/*
X * Test_table[] is used to test for illegal combinations.
X */
Xstatic short test_table[] = {
X T_FLOAT | T_DOUBLE | T_LONG | T_SHORT,
X T_FLOAT | T_DOUBLE | T_CHAR | T_INT,
X T_FLOAT | T_DOUBLE | T_SIGNED | T_UNSIGNED,
X T_LONG | T_SHORT | T_CHAR,
X 0 /* end marker */
X};
X
X/*
X * The order of this table is important -- it is also referenced by
X * the command line processor to allow run-time overriding of the
X * built-in size values. The order must not be changed:
X * char, short, int, long, float, double (func pointer)
X */
XSIZES size_table[] = {
X { T_CHAR, S_CHAR, S_PCHAR }, /* char */
X { T_SHORT, S_SINT, S_PSINT }, /* short int */
X { T_INT, S_INT, S_PINT }, /* int */
X { T_LONG, S_LINT, S_PLINT }, /* long */
X { T_FLOAT, S_FLOAT, S_PFLOAT }, /* float */
X { T_DOUBLE, S_DOUBLE, S_PDOUBLE }, /* double */
X { T_FPTR, 0, S_PFPTR }, /* int (*()) */
X { 0, 0, 0 }, /* End of table */
X};
X
X#endif /* OK_SIZEOF */
X
Xint
Xeval()
X/*
X * Evaluate an expression. Straight-forward operator precedence.
X * This is called from control() on encountering an #if statement.
X * It calls the following routines:
X * evallex Lexical analyser -- returns the type and value of
X * the next input token.
X * evaleval Evaluate the current operator, given the values on
X * the value stack. Returns a pointer to the (new)
X * value stack.
X * For compatiblity with older cpp's, this return returns 1 (TRUE)
X * if a syntax error is detected.
X */
X{
X register int op; /* Current operator */
X register int *valp; /* -> value vector */
X register OPTAB *opp; /* Operator stack */
X int prec; /* Op precedence */
X int binop; /* Set if binary op. needed */
X int op1; /* Operand from stack */
X int skip; /* For short-circuit testing */
X int value[NEXP]; /* Value stack */
X OPTAB opstack[NEXP]; /* Operand stack */
X extern int *evaleval(); /* Does actual evaluation */
X
X DBUG_ENTER ("eval");
X valp = value;
X opp = opstack;
X opp->op = OP_END; /* Mark bottom of stack */
X opp->prec = opdope[OP_END]; /* And its precedence */
X opp->skip = 0; /* Not skipping now */
X binop = 0;
Xagain: ;
X#ifdef DEBUG_EVAL
X printf("In #if at again: skip = %d, binop = %d, line is: %s",
X opp->skip, binop, infile->bptr);
X#endif
X if ((op = evallex(opp->skip)) == OP_SUB && binop == 0)
X op = OP_NEG; /* Unary minus */
X else if (op == OP_ADD && binop == 0)
X op = OP_PLU; /* Unary plus */
X else if (op == OP_FAIL)
X DBUG_RETURN (1); /* Error in evallex */
X#ifdef DEBUG_EVAL
X printf("op = %s, opdope = %03o, binop = %d, skip = %d\n",
X opname[op], opdope[op], binop, opp->skip);
X#endif
X if (op == DIG) { /* Value? */
X if (binop != 0) {
X cerror("misplaced constant in #if", NULLST);
X DBUG_RETURN (1);
X }
X else if (valp >= &value[NEXP-1]) {
X cerror("#if value stack overflow", NULLST);
X DBUG_RETURN (1);
X }
X else {
X#ifdef DEBUG_EVAL
X printf("pushing %d onto value stack[%d]\n",
X evalue, valp - value);
X#endif
X *valp++ = evalue;
X binop = 1;
X }
X goto again;
X }
X else if (op > OP_END) {
X cerror("Illegal #if line", NULLST);
X DBUG_RETURN (1);
X }
X prec = opdope[op];
X if (binop != (prec & 1)) {
X cerror("Operator %s in incorrect context", opname[op]);
X DBUG_RETURN (1);
X }
X binop = (prec & 2) >> 1;
X for (;;) {
X#ifdef DEBUG_EVAL
X printf("op %s, prec %d., stacked op %s, prec %d, skip %d\n",
X opname[op], prec, opname[opp->op], opp->prec, opp->skip);
X#endif
X if (prec > opp->prec) {
X if (op == OP_LPA)
X prec = OP_RPA_PREC;
X else if (op == OP_QUE)
X prec = OP_QUE_PREC;
X op1 = opp->skip; /* Save skip for test */
X /*
X * Push operator onto op. stack.
X */
X opp++;
X if (opp >= &opstack[NEXP]) {
X cerror("expression stack overflow at op \"%s\"",
X opname[op]);
X DBUG_RETURN (1);
X }
X opp->op = op;
X opp->prec = prec;
X skip = (valp[-1] != 0); /* Short-circuit tester */
X /*
X * Do the short-circuit stuff here. Short-circuiting
X * stops automagically when operators are evaluated.
X */
X if ((op == OP_ANA && !skip)
X || (op == OP_ORO && skip))
X opp->skip = S_ANDOR; /* And/or skip starts */
X else if (op == OP_QUE) /* Start of ?: operator */
X opp->skip = (op1 & S_ANDOR) | ((!skip) ? S_QUEST : 0);
X else if (op == OP_COL) { /* : inverts S_QUEST */
X opp->skip = (op1 & S_ANDOR)
X | (((op1 & S_QUEST) != 0) ? 0 : S_QUEST);
X }
X else { /* Other ops leave */
X opp->skip = op1; /* skipping unchanged. */
X }
X#ifdef DEBUG_EVAL
X printf("stacking %s, valp[-1] == %d at %s",
X opname[op], valp[-1], infile->bptr);
X dumpstack(opstack, opp, value, valp);
X#endif
X goto again;
X }
X /*
X * Pop operator from op. stack and evaluate it.
X * End of stack and '(' are specials.
X */
X skip = opp->skip; /* Remember skip value */
X switch ((op1 = opp->op)) { /* Look at stacked op */
X case OP_END: /* Stack end marker */
X if (op == OP_EOE)
X DBUG_RETURN (valp[-1]); /* Finished ok. */
X goto again; /* Read another op. */
X
X case OP_LPA: /* ( on stack */
X if (op != OP_RPA) { /* Matches ) on input */
X cerror("unbalanced paren's, op is \"%s\"", opname[op]);
X DBUG_RETURN (1);
X }
X opp--; /* Unstack it */
X /* goto again; -- Fall through */
X
X case OP_QUE:
X goto again; /* Evaluate true expr. */
X
X case OP_COL: /* : on stack. */
X opp--; /* Unstack : */
X if (opp->op != OP_QUE) { /* Matches ? on stack? */
X cerror("Misplaced '?' or ':', previous operator is %s",
X opname[opp->op]);
X DBUG_RETURN (1);
X }
X /*
X * Evaluate op1.
X */
X default: /* Others: */
X opp--; /* Unstack the operator */
X#ifdef DEBUG_EVAL
X printf("Stack before evaluation of %s\n", opname[op1]);
X dumpstack(opstack, opp, value, valp);
X#endif
X valp = evaleval(valp, op1, skip);
X#ifdef DEBUG_EVAL
X printf("Stack after evaluation\n");
X dumpstack(opstack, opp, value, valp);
X#endif
X } /* op1 switch end */
X } /* Stack unwind loop */
X}
X
XFILE_LOCAL int
Xevallex(skip)
Xint skip; /* TRUE if short-circuit evaluation */
X/*
X * Return next eval operator or value. Called from eval(). It
X * calls a special-purpose routines for 'char' strings and
X * numeric values:
X * evalchar called to evaluate 'x'
X * evalnum called to evaluate numbers.
X */
X{
X register int c, c1, t;
X
X DBUG_ENTER ("evallex");
Xagain: do { /* Collect the token */
X c = skipws();
X if ((c = macroid(c)) == EOF_CHAR || c == '\n') {
X unget();
X DBUG_RETURN (OP_EOE); /* End of expression */
X }
X } while ((t = type[c]) == LET && catenate());
X if (t == INV) { /* Total nonsense */
X if (!skip) {
X if (isascii(c) && isprint(c))
X cierror("illegal character '%c' in #if", c);
X else
X cierror("illegal character (%d decimal) in #if", c);
X }
X DBUG_RETURN (OP_FAIL);
X }
X else if (t == QUO) { /* ' or " */
X if (c == '\'') { /* Character constant */
X evalue = evalchar(skip); /* Somewhat messy */
X#ifdef DEBUG_EVAL
X printf("evalchar returns %d.\n", evalue);
X#endif
X DBUG_RETURN (DIG); /* Return a value */
X }
X cerror("Can't use a string in an #if", NULLST);
X DBUG_RETURN (OP_FAIL);
X }
X else if (t == LET) { /* ID must be a macro */
X if (streq(tokenbuf, "defined")) { /* Or defined name */
X c1 = c = skipws();
X if (c == '(') /* Allow defined(name) */
X c = skipws();
X if (type[c] == LET) {
X evalue = (lookid(c) != NULL);
X if (c1 != '(' /* Need to balance */
X || skipws() == ')') /* Did we balance? */
X DBUG_RETURN (DIG); /* Parsed ok */
X }
X cerror("Bad #if ... defined() syntax", NULLST);
X DBUG_RETURN (OP_FAIL);
X }
X#if OK_SIZEOF
X else if (streq(tokenbuf, "sizeof")) /* New sizeof hackery */
X DBUG_RETURN (dosizeof()); /* Gets own routine */
X#endif
X evalue = 0;
X DBUG_RETURN (DIG);
X }
X else if (t == DIG) { /* Numbers are harder */
X evalue = evalnum(c);
X#ifdef DEBUG_EVAL
X printf("evalnum returns %d.\n", evalue);
X#endif
X }
X else if (strchr("!=<>&|\\", c) != NULL) {
X /*
X * Process a possible multi-byte lexeme.
X */
X c1 = cget(); /* Peek at next char */
X switch (c) {
X case '!':
X if (c1 == '=')
X DBUG_RETURN (OP_NE);
X break;
X
X case '=':
X if (c1 != '=') { /* Can't say a=b in #if */
X unget();
X cerror("= not allowed in #if", NULLST);
X DBUG_RETURN (OP_FAIL);
X }
X DBUG_RETURN (OP_EQ);
X
X case '>':
X case '<':
X if (c1 == c)
X DBUG_RETURN ((c == '<') ? OP_ASL : OP_ASR);
X else if (c1 == '=')
X DBUG_RETURN ((c == '<') ? OP_LE : OP_GE);
X break;
X
X case '|':
X case '&':
X if (c1 == c)
X DBUG_RETURN ((c == '|') ? OP_ORO : OP_ANA);
X break;
X
X case '\\':
X if (c1 == '\n') /* Multi-line if */
X goto again;
X cerror("Unexpected \\ in #if", NULLST);
X DBUG_RETURN (OP_FAIL);
X }
X unget();
X }
X DBUG_RETURN (t);
X}
X
X#if OK_SIZEOF
X
XFILE_LOCAL int
Xdosizeof()
X/*
X * Process the sizeof (basic type) operation in an #if string.
X * Sets evalue to the size and returns
X * DIG success
X * OP_FAIL bad parse or something.
X */
X{
X register int c;
X register TYPES *tp;
X register SIZES *sizp;
X register short *testp;
X short typecode;
X
X DBUG_ENTER ("dosizeof");
X if ((c = skipws()) != '(')
X goto nogood;
X /*
X * Scan off the tokens.
X */
X typecode = 0;
X while ((c = skipws())) {
X if ((c = macroid(c)) == EOF_CHAR || c == '\n')
X goto nogood; /* End of line is a bug */
X else if (c == '(') { /* thing (*)() func ptr */
X if (skipws() == '*'
X && skipws() == ')') { /* We found (*) */
X if (skipws() != '(') /* Let () be optional */
X unget();
X else if (skipws() != ')')
X goto nogood;
X typecode |= T_FPTR; /* Function pointer */
X }
X else { /* Junk is a bug */
X goto nogood;
X }
X }
X else if (type[c] != LET) /* Exit if not a type */
X break;
X else if (!catenate()) { /* Maybe combine tokens */
X /*
X * Look for this unexpandable token in basic_types.
X * The code accepts "int long" as well as "long int"
X * which is a minor bug as bugs go (and one shared with
X * a lot of C compilers).
X */
X for (tp = basic_types; tp->name != NULLST; tp++) {
X if (streq(tokenbuf, tp->name))
X break;
X }
X if (tp->name == NULLST) {
X cerror("#if sizeof, unknown type \"%s\"", tokenbuf);
X DBUG_RETURN (OP_FAIL);
X }
X typecode |= tp->type; /* Or in the type bit */
X }
X }
X /*
X * We are at the end of the type scan. Chew off '*' if necessary.
X */
X if (c == '*') {
X typecode |= T_PTR;
X c = skipws();
X }
X if (c == ')') { /* Last syntax check */
X for (testp = test_table; *testp != 0; testp++) {
X if (!bittest(typecode & *testp)) {
X cerror("#if ... sizeof: illegal type combination", NULLST);
X DBUG_RETURN (OP_FAIL);
X }
X }
X /*
X * We assume that all function pointers are the same size:
X * sizeof (int (*)()) == sizeof (float (*)())
X * We assume that signed and unsigned don't change the size:
X * sizeof (signed int) == (sizeof unsigned int)
X */
X if ((typecode & T_FPTR) != 0) /* Function pointer */
X typecode = T_FPTR | T_PTR;
X else { /* Var or var * datum */
X typecode &= ~(T_SIGNED | T_UNSIGNED);
X if ((typecode & (T_SHORT | T_LONG)) != 0)
X typecode &= ~T_INT;
X }
X if ((typecode & ~T_PTR) == 0) {
X cerror("#if sizeof() error, no type specified", NULLST);
X DBUG_RETURN (OP_FAIL);
X }
X /*
X * Exactly one bit (and possibly T_PTR) may be set.
X */
X for (sizp = size_table; sizp->bits != 0; sizp++) {
X if ((typecode & ~T_PTR) == sizp->bits) {
X evalue = ((typecode & T_PTR) != 0)
X ? sizp->psize : sizp->size;
X DBUG_RETURN (DIG);
X }
X } /* We shouldn't fail */
X cierror("#if ... sizeof: bug, unknown type code 0x%x", typecode);
X DBUG_RETURN (OP_FAIL);
X }
X
Xnogood: unget();
X cerror("#if ... sizeof() syntax error", NULLST);
X DBUG_RETURN (OP_FAIL);
X}
X
XFILE_LOCAL int
Xbittest(value)
X/*
X * TRUE if value is zero or exactly one bit is set in value.
X */
X{
X#if (4096 & ~(-4096)) == 0
X return ((value & ~(-value)) == 0);
X#else
X /*
X * Do it the hard way (for non 2's complement machines)
X */
X return (value == 0 || value ^ (value - 1) == (value * 2 - 1));
X#endif
X}
X
X#endif /* OK_SIZEOF */
X
XFILE_LOCAL int
Xevalnum(c)
Xregister int c;
X/*
X * Expand number for #if lexical analysis. Note: evalnum recognizes
X * the unsigned suffix, but only returns a signed int value.
X */
X{
X register int value;
X register int base;
X register int c1;
X
X DBUG_ENTER ("evalnum");
X if (c != '0')
X base = 10;
X else if ((c = cget()) == 'x' || c == 'X') {
X base = 16;
X c = cget();
X }
X else base = 8;
X value = 0;
X for (;;) {
X c1 = c;
X if (isascii(c) && isupper(c1))
X c1 = tolower(c1);
X if (c1 >= 'a')
X c1 -= ('a' - 10);
X else c1 -= '0';
X if (c1 < 0 || c1 >= base)
X break;
X value *= base;
X value += c1;
X c = cget();
X }
X if (c == 'u' || c == 'U') /* Unsigned nonsense */
X c = cget();
X unget();
X DBUG_RETURN (value);
X}
X
XFILE_LOCAL int
Xevalchar(skip)
Xint skip; /* TRUE if short-circuit evaluation */
X/*
X * Get a character constant
X */
X{
X register int c;
X register int value;
X register int count;
X
X DBUG_ENTER ("evalchar");
X instring = TRUE;
X if ((c = cget()) == '\\') {
X switch ((c = cget())) {
X case 'a': /* New in Standard */
X#if ('a' == '\a' || '\a' == ALERT)
X value = ALERT; /* Use predefined value */
X#else
X value = '\a'; /* Use compiler's value */
X#endif
X break;
X
X case 'b':
X value = '\b';
X break;
X
X case 'f':
X value = '\f';
X break;
X
X case 'n':
X value = '\n';
X break;
X
X case 'r':
X value = '\r';
X break;
X
X case 't':
X value = '\t';
X break;
X
X case 'v': /* New in Standard */
X#if ('v' == '\v' || '\v' == VT)
X value = VT; /* Use predefined value */
X#else
X value = '\v'; /* Use compiler's value */
X#endif
X break;
X
X case 'x': /* '\xFF' */
X count = 3;
X value = 0;
X while ((((c = get()) >= '0' && c <= '9')
X || (c >= 'a' && c <= 'f')
X || (c >= 'A' && c <= 'F'))
X && (--count >= 0)) {
X value *= 16;
X value += (c <= '9') ? (c - '0') : ((c & 0xF) + 9);
X }
X unget();
X break;
X
X default:
X if (c >= '0' && c <= '7') {
X count = 3;
X value = 0;
X while (c >= '0' && c <= '7' && --count >= 0) {
X value *= 8;
X value += (c - '0');
X c = get();
X }
X unget();
X }
X else value = c;
X break;
X }
X }
X else if (c == '\'')
X value = 0;
X else value = c;
X /*
X * We warn on multi-byte constants and try to hack
X * (big|little)endian machines.
X */
X#if BIG_ENDIAN
X count = 0;
X#endif
X while ((c = get()) != '\'' && c != EOF_CHAR && c != '\n') {
X if (!skip)
X ciwarn("multi-byte constant '%c' isn't portable", c);
X#if BIG_ENDIAN
X count += BITS_CHAR;
X value += (c << count);
X#else
X value <<= BITS_CHAR;
X value += c;
X#endif
X }
X instring = FALSE;
X DBUG_RETURN (value);
X}
X
XFILE_LOCAL int *
Xevaleval(valp, op, skip)
Xregister int *valp;
Xint op;
Xint skip; /* TRUE if short-circuit evaluation */
X/*
X * Apply the argument operator to the data on the value stack.
X * One or two values are popped from the value stack and the result
X * is pushed onto the value stack.
X *
X * OP_COL is a special case.
X *
X * evaleval() returns the new pointer to the top of the value stack.
X */
X{
X register int v1, v2;
X
X DBUG_ENTER ("evaleval");
X if (isbinary(op))
X v2 = *--valp;
X v1 = *--valp;
X#ifdef DEBUG_EVAL
X printf("%s op %s", (isbinary(op)) ? "binary" : "unary",
X opname[op]);
X if (isbinary(op))
X printf(", v2 = %d.", v2);
X printf(", v1 = %d.\n", v1);
X#endif
X switch (op) {
X case OP_EOE:
X break;
X
X case OP_ADD:
X v1 += v2;
X break;
X
X case OP_SUB:
X v1 -= v2;
X break;
X
X case OP_MUL:
X v1 *= v2;
X break;
X
X case OP_DIV:
X case OP_MOD:
X if (v2 == 0) {
X if (!skip) {
X cwarn("%s by zero in #if, zero result assumed",
X (op == OP_DIV) ? "divide" : "mod");
X }
X v1 = 0;
X }
X else if (op == OP_DIV)
X v1 /= v2;
X else
X v1 %= v2;
X break;
X
X case OP_ASL:
X v1 <<= v2;
X break;
X
X case OP_ASR:
X v1 >>= v2;
X break;
X
X case OP_AND:
X v1 &= v2;
X break;
X
X case OP_OR:
X v1 |= v2;
X break;
X
X case OP_XOR:
X v1 ^= v2;
X break;
X
X case OP_EQ:
X v1 = (v1 == v2);
X break;
X
X case OP_NE:
X v1 = (v1 != v2);
X break;
X
X case OP_LT:
X v1 = (v1 < v2);
X break;
X
X case OP_LE:
X v1 = (v1 <= v2);
X break;
X
X case OP_GE:
X v1 = (v1 >= v2);
X break;
X
X case OP_GT:
X v1 = (v1 > v2);
X break;
X
X case OP_ANA:
X v1 = (v1 && v2);
X break;
X
X case OP_ORO:
X v1 = (v1 || v2);
X break;
X
X case OP_COL:
X /*
X * v1 has the "true" value, v2 the "false" value.
X * The top of the value stack has the test.
X */
X v1 = (*--valp) ? v1 : v2;
X break;
X
X case OP_NEG:
X v1 = (-v1);
X break;
X
X case OP_PLU:
X break;
X
X case OP_COM:
X v1 = ~v1;
X break;
X
X case OP_NOT:
X v1 = !v1;
X break;
X
X default:
X cierror("#if bug, operand = %d.", op);
X v1 = 0;
X }
X *valp++ = v1;
X DBUG_RETURN (valp);
X}
X
X#ifdef DEBUG_EVAL
Xdumpstack(opstack, opp, value, valp)
XOPTAB opstack[NEXP]; /* Operand stack */
Xregister OPTAB *opp; /* Operator stack */
Xint value[NEXP]; /* Value stack */
Xregister int *valp; /* -> value vector */
X{
X DBUG_ENTER ("dumpstack");
X printf("index op prec skip name -- op stack at %s", infile->bptr);
X while (opp > opstack) {
X printf(" [%2d] %2d %03o %d %s\n", opp - opstack,
X opp->op, opp->prec, opp->skip, opname[opp->op]);
X opp--;
X }
X while (--valp >= value) {
X printf("value[%d] = %d\n", (valp - value), *valp);
X }
X DBUG_VOID_RETURN;
X}
X#endif
X
END_OF_FILE
if test 23086 -ne `wc -c <'cpp5.c'`; then
echo shar: \"'cpp5.c'\" unpacked with wrong size!
fi
# end of 'cpp5.c'
fi
echo shar: End of archive 3 \(of 5\).
cp /dev/null ark3isdone
MISSING=""
for I in 1 2 3 4 5 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked all 5 archives.
rm -f ark[1-9]isdone
else
echo You still need to unpack the following archives:
echo " " ${MISSING}
fi
## End of shell archive.
exit 0
--
Submissions to comp.sources.amiga and comp.binaries.amiga should be sent to:
amiga@cs.odu.edu
or amiga@xanth.cs.odu.edu ( obsolescent mailers may need this address )
or ...!uunet!xanth!amiga ( very obsolescent mailers need this address )
Comments, questions, and suggestions s should be addressed to ``amiga-request''
(only use ``amiga'' for submissions) at the above addresses.