[comp.sources.amiga] v90i024: cpp - a c preprocessor with some ANSI features, Part03/05

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.