home *** CD-ROM | disk | FTP | other *** search
- /*
- * C P P 4 . C
- * M a c r o D e f i n i t i o n s
- *
- * Edit History
- * 31-Aug-84 MM USENET net.sources release
- * 04-Oct-84 MM __LINE__ and __FILE__ must call ungetstring()
- * so they work correctly with token concatenation.
- * Added string formal recognition.
- * 25-Oct-84 MM "Short-circuit" evaluate #if's so that we
- * don't print unnecessary error messages for
- * #if !defined(FOO) && FOO != 0 && 10 / FOO ...
- * 31-Oct-84 ado/MM Added token concatenation
- * 6-Nov-84 MM Split off eval stuff
- * 21-Oct-85 RMS Rename `token' to `tokenbuf'.
- * In doundef, don't complain if arg already not defined.
- * 14-Mar-86 FNF Incorporate macro based C debugging package.
- * Port to Commodore AMIGA.
- * 21-Aug-88 Ois Changed concatenation operator to ##. Changed hand-
- * ling of tokens following ##. Added new meaning of #.
- */
-
- #include <stdio.h>
- #include <ctype.h>
- #include "cppdef.h"
- #include "cpp.h"
- /*
- * parm[], parmp, and parlist[] are used to store #define() argument
- * lists. nargs contains the actual number of parameters stored.
- */
- static char parm[NPARMWORK + 1]; /* define param work buffer */
- static char *parmp; /* Free space in parm */
- static char *parlist[LASTPARM]; /* -> start of each parameter */
- static int nargs; /* Parameters for this macro */
-
- dodefine()
- /*
- * Called from control when a #define is scanned. This module
- * parses formal parameters and the replacement string. When
- * the formal parameter name is encountered in the replacement
- * string, it is replaced by a character in the range 128 to
- * 128+NPARAM (this allows up to 32 parameters within the
- * Dec Multinational range). If cpp is ported to an EBCDIC
- * machine, you will have to make other arrangements.
- *
- * There is some special case code to distinguish
- * #define foo bar
- * from #define foo() bar
- *
- * Also, we make sure that
- * #define foo foo
- * expands to "foo" but doesn't put cpp into an infinite loop.
- *
- * A warning message is printed if you redefine a symbol to a
- * different text. I.e,
- * #define foo 123
- * #define foo 123
- * is ok, but
- * #define foo 123
- * #define foo +123
- * is not.
- *
- * The following subroutines are called from define():
- * checkparm called when a token is scanned. It checks through the
- * array of formal parameters. If a match is found, the
- * token is replaced by a control byte which will be used
- * to locate the parameter when the macro is expanded.
- * textput puts a string in the macro work area (parm[]), updating
- * parmp to point to the first free byte in parm[].
- * textput() tests for work buffer overflow.
- * charput puts a single character in the macro work area (parm[])
- * in a manner analogous to textput().
- */
- {
- register int c;
- register DEFBUF *dp; /* -> new definition */
- int isredefine; /* TRUE if redefined */
- char *old; /* Remember redefined */
- #if OK_CONCAT
- register int quoting; /* Remember we saw a # */
- #endif
- extern int save(); /* Save char in work[] */
-
- DBUG_ENTER ("dodefine");
- if (type[(c = skipws())] != LET)
- goto bad_define;
- isredefine = FALSE; /* Set if redefining */
- if ((dp = lookid(c)) == NULL) /* If not known now */
- dp = defendel(tokenbuf, FALSE); /* Save the name */
- else { /* It's known: */
- isredefine = TRUE; /* Remember this fact */
- old = dp->repl; /* Remember replacement */
- dp->repl = NULL; /* No replacement now */
- }
- parlist[0] = parmp = parm; /* Setup parm buffer */
- if ((c = get()) == '(') { /* With arguments? */
- nargs = 0; /* Init formals counter */
- do { /* Collect formal parms */
- if (nargs >= LASTPARM)
- cfatal("Too many arguments for macro", NULLST);
- else if ((c = skipws()) == ')')
- break; /* Got them all */
- else if (type[c] != LET) /* Bad formal syntax */
- goto bad_define;
- scanid(c); /* Get the formal param */
- parlist[nargs++] = parmp; /* Save its start */
- textput(tokenbuf); /* Save text in parm[] */
- } while ((c = skipws()) == ','); /* Get another argument */
- if (c != ')') /* Must end at ) */
- goto bad_define;
- c = ' '; /* Will skip to body */
- }
- else {
- /*
- * DEF_NOARGS is needed to distinguish between
- * "#define foo" and "#define foo()".
- */
- nargs = DEF_NOARGS; /* No () parameters */
- }
- if (type[c] == SPA) /* At whitespace? */
- c = skipws(); /* Not any more. */
- workp = work; /* Replacement put here */
- inmacro = TRUE; /* Keep \<newline> now */
- quoting = 0; /* No # seen yet. */
- while (c != EOF_CHAR && c != '\n') { /* Compile macro body */
- #if OK_CONCAT
- if (c == '#') { /* Token concatenation? */
- if ((c = get()) != '#') { /* No, not really */
- quoting = 1; /* Maybe quoting op. */
- continue;
- }
- while (workp > work && type[workp[-1]] == SPA)
- --workp; /* Erase leading spaces */
- save(TOK_SEP); /* Stuff a delimiter */
- c = skipws(); /* Eat whitespace */
- #if 0
- if (type[c] == LET) /* Another token here? */
- ; /* Stuff it normally */
- else if (type[c] == DIG) { /* Digit string after? */
- while (type[c] == DIG) { /* Stuff the digits */
- save(c); /* Note
- c = get();
- }
- save(TOK_SEP); /* Delimit 2nd token */
- }
- else {
- ciwarn("Strange character after ## (%d.)", c);
- }
- #endif
- continue;
- }
- #endif
- switch (type[c]) {
- case LET:
- #if OK_CONCAT
- checkparm(c, dp, quoting); /* Might be a formal */
- #else
- checkparm(c, dp); /* Might be a formal */
- #endif
- break;
-
- case DIG: /* Number in mac. body */
- case DOT: /* Maybe a float number */
- scannumber(c, save); /* Scan it off */
- break;
-
- case QUO: /* String in mac. body */
- #if STRING_FORMAL
- stparmscan(c, dp); /* Do string magic */
- #else
- stparmscan(c);
- #endif
- break;
-
- case BSH: /* Backslash */
- save('\\');
- if ((c = get()) == '\n')
- wrongline = TRUE;
- save(c);
- break;
-
- case SPA: /* Absorb whitespace */
- /*
- * Note: the "end of comment" marker is passed on
- * to allow comments to separate tokens.
- */
- if (workp[-1] == ' ') /* Absorb multiple */
- break; /* spaces */
- else if (c == '\t')
- c = ' '; /* Normalize tabs */
- /* Fall through to store character */
- default: /* Other character */
- save(c);
- break;
- }
- c = get();
- quoting = 0; /* Only when immediately*/
- /* preceding a formal */
- }
- inmacro = FALSE; /* Stop newline hack */
- unget(); /* For control check */
- if (workp > work && workp[-1] == ' ') /* Drop trailing blank */
- workp--;
- *workp = EOS; /* Terminate work */
- dp->repl = savestring(work); /* Save the string */
- dp->nargs = nargs; /* Save arg count */
- #if DEBUG
- if (debug)
- dumpadef("macro definition", dp);
- #endif
- if (isredefine) { /* Error if redefined */
- if ((old != NULL && dp->repl != NULL && !streq(old, dp->repl))
- || (old == NULL && dp->repl != NULL)
- || (old != NULL && dp->repl == NULL)) {
- cerror("Redefining defined variable \"%s\"", dp->name);
- }
- if (old != NULL) /* We don't need the */
- free(old); /* old definition now. */
- }
- DBUG_VOID_RETURN;
-
- bad_define:
- cerror("#define syntax error", NULLST);
- inmacro = FALSE; /* Stop <newline> hack */
- DBUG_VOID_RETURN;
- }
-
- checkparm(c, dp, quoting)
- register int c;
- DEFBUF *dp;
- int quoting; /* Preceded by a # ? */
- /*
- * Replace this param if it's defined. Note that the macro name is a
- * possible replacement token. We stuff DEF_MAGIC in front of the token
- * which is treated as a LETTER by the token scanner and eaten by
- * the output routine. This prevents the macro expander from
- * looping if someone writes "#define foo foo".
- */
- {
- register int i;
- register char *cp;
-
- DBUG_ENTER ("checkparm");
- scanid(c); /* Get parm to tokenbuf */
- for (i = 0; i < nargs; i++) { /* For each argument */
- if (streq(parlist[i], tokenbuf)) { /* If it's known */
- #if OK_CONCAT
- if (quoting) /* Special handling of */
- save(QUOTE_PARM); /* #formal inside defn */
- #endif
- save(i + MAC_PARM); /* Save a magic cookie */
- DBUG_VOID_RETURN; /* And exit the search */
- }
- }
- if (streq(dp->name, tokenbuf)) /* Macro name in body? */
- save(DEF_MAGIC); /* Save magic marker */
- for (cp = tokenbuf; *cp != EOS;) /* And save */
- save(*cp++); /* The token itself */
- DBUG_VOID_RETURN;
- }
-
- #if STRING_FORMAL
- stparmscan(delim, dp)
- int delim;
- register DEFBUF *dp;
- /*
- * Scan the string (starting with the given delimiter).
- * The token is replaced if it is the only text in this string or
- * character constant. The algorithm follows checkparm() above.
- * Note that scanstring() has approved of the string.
- */
- {
- register int c;
-
- DBUG_ENTER ("stparmscan");
- /*
- * Warning -- this code hasn't been tested for a while.
- * It exists only to preserve compatibility with earlier
- * implementations of cpp. It is not part of the Draft
- * ANSI Standard C language.
- */
- save(delim);
- instring = TRUE;
- while ((c = get()) != delim
- && c != '\n'
- && c != EOF_CHAR) {
- if (type[c] == LET) /* Maybe formal parm */
- checkparm(c, dp, 0);
- else {
- save(c);
- if (c == '\\')
- save(get());
- }
- }
- instring = FALSE;
- if (c != delim)
- cerror("Unterminated string in macro body", NULLST);
- save(c);
- DBUG_VOID_RETURN;
- }
- #else
- stparmscan(delim)
- int delim;
- /*
- * Normal string parameter scan.
- */
- {
- register char *wp;
- register int i;
- extern int save();
-
- DBUG_ENTER ("stparmscan");
- wp = workp; /* Here's where it starts */
- if (!scanstring(delim, save))
- DBUG_VOID_RETURN; /* Exit on scanstring error */
- workp[-1] = EOS; /* Erase trailing quote */
- wp++; /* -> first string content byte */
- for (i = 0; i < nargs; i++) {
- if (streq(parlist[i], wp)) {
- *wp++ = MAC_PARM + PAR_MAC; /* Stuff a magic marker */
- *wp++ = (i + MAC_PARM); /* Make a formal marker */
- *wp = wp[-3]; /* Add on closing quote */
- workp = wp + 1; /* Reset string end */
- DBUG_VOID_RETURN;
- }
- }
- workp[-1] = wp[-1]; /* Nope, reset end quote. */
- DBUG_VOID_RETURN;
- }
- #endif
-
- doundef()
- /*
- * Remove the symbol from the defined list.
- * Called from the #control processor.
- */
- {
- register int c;
-
- DBUG_ENTER ("doundef");
- if (type[(c = skipws())] != LET)
- cerror("Illegal #undef argument", NULLST);
- else
- {
- scanid(c); /* Get name to tokenbuf */
- (void) defendel(tokenbuf, TRUE);
- }
- DBUG_VOID_RETURN;
- }
-
- textput(text)
- char *text;
- /*
- * Put the string in the parm[] buffer.
- */
- {
- register int size;
-
- DBUG_ENTER ("textput");
- size = strlen(text) + 1;
- if ((parmp + size) >= &parm[NPARMWORK])
- cfatal("Macro work area overflow", NULLST);
- else {
- strcpy(parmp, text);
- parmp += size;
- }
- DBUG_VOID_RETURN;
- }
-
- charput(c)
- register int c;
- /*
- * Put the byte in the parm[] buffer.
- */
- {
- if (parmp >= &parm[NPARMWORK])
- cfatal("Macro work area overflow", NULLST);
- else {
- *parmp++ = c;
- }
- }
-
- /*
- * M a c r o E x p a n s i o n
- */
-
- static DEFBUF *macro; /* Catches start of infinite macro */
-
- expand(tokenp)
- register DEFBUF *tokenp;
- /*
- * Expand a macro. Called from the cpp mainline routine (via subroutine
- * macroid()) when a token is found in the symbol table. It calls
- * expcollect() to parse actual parameters, checking for the correct number.
- * It then creates a "file" containing a single line containing the
- * macro with actual parameters inserted appropriately. This is
- * "pushed back" onto the input stream. (When the get() routine runs
- * off the end of the macro line, it will dismiss the macro itself.)
- */
- {
- register int c;
- register FILEINFO *file;
- extern FILEINFO *getfile();
-
- DBUG_ENTER ("expand");
- #if DEBUG
- if (debug)
- dumpadef("expand entry", tokenp);
- #endif
- /*
- * If no macro is pending, save the name of this macro
- * for an eventual error message.
- */
- if (recursion++ == 0)
- macro = tokenp;
- else if (recursion == RECURSION_LIMIT) {
- cerror("Recursive macro definition of \"%s\"", tokenp->name);
- fprintf(stderr, "(Defined by \"%s\")\n", macro->name);
- if (rec_recover) {
- do {
- c = get();
- } while (infile != NULL && infile->fp == NULL);
- unget();
- recursion = 0;
- DBUG_VOID_RETURN;
- }
- }
- /*
- * Here's a macro to expand.
- */
- nargs = 0; /* Formals counter */
- parmp = parm; /* Setup parm buffer */
- switch (tokenp->nargs) {
- case (-2): /* __LINE__ */
- sprintf(work, "%d", line);
- ungetstring(work);
- break;
-
- case (-3): /* __FILE__ */
- for (file = infile; file != NULL; file = file->parent) {
- if (file->fp != NULL) {
- sprintf(work, "\"%s\"", (file->progname != NULL)
- ? file->progname : file->filename);
- ungetstring(work);
- break;
- }
- }
- break;
-
- default:
- /*
- * Nothing funny about this macro.
- */
- if (tokenp->nargs < 0)
- cfatal("Bug: Illegal __ macro \"%s\"", tokenp->name);
- while ((c = skipws()) == '\n') /* Look for (, skipping */
- wrongline = TRUE; /* spaces and newlines */
- if (c != '(') {
- /*
- * If the programmer writes
- * #define foo() ...
- * ...
- * foo [no ()]
- * just write foo to the output stream.
- */
- unget();
- cwarn("Macro \"%s\" needs arguments", tokenp->name);
- fputs(tokenp->name, stdout);
- DBUG_VOID_RETURN;
- }
- else if (expcollect()) { /* Collect arguments */
- if (tokenp->nargs != nargs) { /* Should be an error? */
- cwarn("Wrong number of macro arguments for \"%s\"",
- tokenp->name);
- }
- #if DEBUG
- if (debug)
- dumpparm("expand");
- #endif
- } /* Collect arguments */
- case DEF_NOARGS: /* No parameters just stuffs */
- expstuff(tokenp); /* Do actual parameters */
- } /* nargs switch */
- DBUG_VOID_RETURN;
- }
-
- FILE_LOCAL int
- expcollect()
- /*
- * Collect the actual parameters for this macro. TRUE if ok.
- */
- {
- register int c;
- register int paren; /* For embedded ()'s */
- extern int charput();
-
- DBUG_ENTER ("expcollect");
- for (;;) {
- paren = 0; /* Collect next arg. */
- while ((c = skipws()) == '\n') /* Skip over whitespace */
- wrongline = TRUE; /* and newlines. */
- if (c == ')') { /* At end of all args? */
- /*
- * Note that there is a guard byte in parm[]
- * so we don't have to check for overflow here.
- */
- *parmp = EOS; /* Make sure terminated */
- break; /* Exit collection loop */
- }
- else if (nargs >= LASTPARM)
- cfatal("Too many arguments in macro expansion", NULLST);
- parlist[nargs++] = parmp; /* At start of new arg */
- for (;; c = cget()) { /* Collect arg's bytes */
- if (c == EOF_CHAR) {
- cerror("end of file within macro argument", NULLST);
- DBUG_RETURN (FALSE); /* Sorry. */
- }
- else if (c == '\\') { /* Quote next character */
- charput(c); /* Save the \ for later */
- charput(cget()); /* Save the next char. */
- continue; /* And go get another */
- }
- else if (type[c] == QUO) { /* Start of string? */
- scanstring(c, charput); /* Scan it off */
- continue; /* Go get next char */
- }
- else if (c == '(') /* Worry about balance */
- paren++; /* To know about commas */
- else if (c == ')') { /* Other side too */
- if (paren == 0) { /* At the end? */
- unget(); /* Look at it later */
- break; /* Exit arg getter. */
- }
- paren--; /* More to come. */
- }
- else if (c == ',' && paren == 0) /* Comma delimits args */
- break;
- else if (c == '\n') /* Newline inside arg? */
- wrongline = TRUE; /* We'll need a #line */
- charput(c); /* Store this one */
- } /* Collect an argument */
- charput(EOS); /* Terminate argument */
- #if DEBUG
- if (debug)
- printf("parm[%d] = \"%s\"\n", nargs, parlist[nargs - 1]);
- #endif
- } /* Collect all args. */
- DBUG_RETURN (TRUE); /* Normal return */
- }
-
-
- #if OK_CONCAT
-
- FILE_LOCAL
- char *doquoting(to, from)
- register char *to;
- register char *from;
- {
- *to++ = '"';
- while (*from) {
- if (*from == '\\' || *from == '"')
- *to++ = '\\';
- *to++ = *from++;
- }
- *to++ = '"';
-
- return to;
- }
-
- #endif
-
- FILE_LOCAL
- expstuff(tokenp)
- DEFBUF *tokenp; /* Current macro being expanded */
- /*
- * Stuff the macro body, replacing formal parameters by actual parameters.
- */
- {
- register int c; /* Current character */
- register char *inp; /* -> repl string */
- register char *defp; /* -> macro output buff */
- int size; /* Actual parm. size */
- char *defend; /* -> output buff end */
- int string_magic; /* String formal hack */
- FILEINFO *file; /* Funny #include */
- #if OK_CONCAT
- register char quoting; /* Quote macro argument */
- #endif
- extern FILEINFO *getfile();
-
- DBUG_ENTER ("expstuff");
- file = getfile(NBUFF, tokenp->name);
- inp = tokenp->repl; /* -> macro replacement */
- defp = file->buffer; /* -> output buffer */
- defend = defp + (NBUFF - 1); /* Note its end */
- if (inp != NULL) {
- quoting = 0;
- while ((c = (*inp++ & 0xFF)) != EOS) {
- #if OK_CONCAT
- if (c == QUOTE_PARM) { /* Special token for # */
- quoting = 1; /* set flag, for later */
- continue; /* Get next character */
- }
- #endif
- if (c >= MAC_PARM && c <= (MAC_PARM + PAR_MAC)) {
- string_magic = (c == (MAC_PARM + PAR_MAC));
- if (string_magic)
- c = (*inp++ & 0xFF);
- /*
- * Replace formal parameter by actual parameter string.
- */
- if ((c -= MAC_PARM) < nargs) {
- size = strlen(parlist[c]);
- #if OK_CONCAT
- if (quoting) {
- size++;
- size *= 2; /* worst case condition */
- }
- #endif
- if ((defp + size) >= defend)
- goto nospace;
- /*
- * Erase the extra set of quotes.
- */
- if (string_magic && defp[-1] == parlist[c][0]) {
- strcpy(defp-1, parlist[c]);
- defp += (size - 2);
- }
- #if OK_CONCAT
- else if (quoting)
- defp = doquoting(defp, parlist[c]);
- #endif
- else {
- strcpy(defp, parlist[c]);
- defp += size;
- }
- }
- }
- else if (defp >= defend) {
- nospace: cfatal("Out of space in macro \"%s\" arg expansion",
- tokenp->name);
- }
- else {
- *defp++ = c;
- }
- quoting = 0;
- }
- }
- *defp = EOS;
- #if DEBUG
- if (debug > 1)
- printf("macroline: \"%s\"\n", file->buffer);
- #endif
- DBUG_VOID_RETURN;
- }
-
- #if DEBUG
- dumpparm(why)
- char *why;
- /*
- * Dump parameter list.
- */
- {
- register int i;
-
- DBUG_ENTER ("dumpparm");
- printf("dump of %d parameters (%d bytes total) %s\n",
- nargs, parmp - parm, why);
- for (i = 0; i < nargs; i++) {
- printf("parm[%d] (%d) = \"%s\"\n",
- i + 1, strlen(parlist[i]), parlist[i]);
- }
- DBUG_VOID_RETURN;
- }
- #endif
-