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.