/* * 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). * * 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(). */ void dodefine() { int c; DEFBUF* dp; /* -> new definition */ int isredefine; /* TRUE if redefined */ char* old = NULL; /* Remember redefined */ if (type[(c = skipws())] != LET) goto bad_define; isredefine = FALSE; /* Set if redefining */ if ((dp = lookid(c)) == NULL) /* If not known now */ dp = defendel(token, 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(token); /* 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 */ while (c != EOF_CHAR && c != '\n') /* Compile macro body */ { if (c == '#') /* Token concatenation? */ { while (workp > work && type[(int)workp[-1]] == SPA) --workp; /* Erase leading spaces */ save(TOK_SEP); /* Stuff a delimiter */ c = skipws(); /* Eat whitespace */ 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); c = get(); } save(TOK_SEP); /* Delimit 2nd token */ } else { ciwarn("Strange character after # (%d.)", c); } continue; } switch (type[c]) { case LET: checkparm(c, dp); /* Might be a formal */ 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 */ stparmscan(c); 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(); } 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 OSL_DEBUG_LEVEL > 1 if (debug) dumpadef("macro definition", dp); else if (bDumpDefs) dumpadef(NULL, 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)) { #ifdef STRICT_UNDEF cerror("Redefining defined variable \"%s\"", dp->name); #else cwarn("Redefining defined variable \"%s\"", dp->name); #endif } if (old != NULL) /* We don't need the */ free(old); /* old definition now. */ } return; bad_define: cerror("#define syntax error", NULLST); inmacro = FALSE; /* Stop <newline> hack */ }
ReturnCode dodefine(struct Global *global) { /* * 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(). */ int c; DEFBUF *dp; /* -> new definition */ int isredefine; /* TRUE if redefined */ char *old = NULL; /* Remember redefined */ ReturnCode ret; #if OK_CONCAT int quoting; /* Remember we saw a # */ #endif if (type[(c = skipws(global))] != LET) { cerror(global, ERROR_DEFINE_SYNTAX); global->inmacro = FALSE; /* Stop <newline> hack */ return(FPP_OK); } isredefine = FALSE; /* Set if redefining */ if ((dp = lookid(global, c)) == NULL) { /* If not known now */ dp = defendel(global, global->tokenbuf, FALSE); /* Save the name */ if(!dp) return(FPP_OUT_OF_MEMORY); } else { /* It's known: */ isredefine = TRUE; /* Remember this fact */ old = dp->repl; /* Remember replacement */ dp->repl = NULL; /* No replacement now */ } global->parlist[0] = global->parmp = global->parm; /* Setup parm buffer */ if ((c = get(global)) == '(') { /* With arguments? */ global->nargs = 0; /* Init formals counter */ do { /* Collect formal parms */ if (global->nargs >= LASTPARM) { cfatal(global, FATAL_TOO_MANY_ARGUMENTS_MACRO); return(FPP_TOO_MANY_ARGUMENTS); } else if ((c = skipws(global)) == ')') break; /* Got them all */ else if (type[c] != LET) { /* Bad formal syntax */ cerror(global, ERROR_DEFINE_SYNTAX); global->inmacro = FALSE; /* Stop <newline> hack */ return(FPP_OK); } scanid(global, c); /* Get the formal param */ global->parlist[global->nargs++] = global->parmp; /* Save its start */ ret=textput(global, global->tokenbuf); /* Save text in parm[] */ if(ret) return(ret); } while ((c = skipws(global)) == ','); /* Get another argument */ if (c != ')') { /* Must end at ) */ cerror(global, ERROR_DEFINE_SYNTAX); global->inmacro = FALSE; /* Stop <newline> hack */ return(FPP_OK); } c = ' '; /* Will skip to body */ } else { /* * DEF_NOARGS is needed to distinguish between * "#define foo" and "#define foo()". */ global->nargs = DEF_NOARGS; /* No () parameters */ } if (type[c] == SPA) /* At whitespace? */ c = skipws(global); /* Not any more. */ global->workp = global->work; /* Replacement put here */ global->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(global)) != '#') { /* No, not really */ quoting = 1; /* Maybe quoting op. */ continue; } while (global->workp > global->work && type[(unsigned)*(global->workp - 1)] == SPA) --global->workp; /* Erase leading spaces */ if((ret=save(global, TOK_SEP))) /* Stuff a delimiter */ return(ret); c = skipws(global); /* Eat whitespace */ continue; } #endif switch (type[c]) { case LET: #if OK_CONCAT ret=checkparm(global, c, dp, quoting); /* Might be a formal */ #else ret=checkparm(c, dp); /* Might be a formal */ #endif if(ret) return(ret); break; case DIG: /* Number in mac. body */ case DOT: /* Maybe a float number */ ret=scannumber(global, c, save); /* Scan it off */ if(ret) return(ret); break; case QUO: /* String in mac. body */ ret=stparmscan(global, c); if(ret) return(ret); break; case BSH: /* Backslash */ ret=save(global, '\\'); if(ret) return(ret); if ((c = get(global)) == '\n') global->wrongline = TRUE; ret=save(global, c); if(ret) return(ret); break; case SPA: /* Absorb whitespace */ /* * Note: the "end of comment" marker is passed on * to allow comments to separate tokens. */ if (global->workp[-1] == ' ') /* Absorb multiple */ break; /* spaces */ else if (c == '\t') c = ' '; /* Normalize tabs */ /* Fall through to store character */ default: /* Other character */ ret=save(global, c); if(ret) return(ret); break; } c = get(global); quoting = 0; /* Only when immediately*/ /* preceding a formal */ } global->inmacro = FALSE; /* Stop newline hack */ unget(global); /* For control check */ if (global->workp > global->work && global->workp[-1] == ' ') /* Drop trailing blank */ global->workp--; *global->workp = EOS; /* Terminate work */ dp->repl = savestring(global, global->work); /* Save the string */ dp->nargs = global->nargs; /* Save arg count */ 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(global, ERROR_REDEFINE, dp->name); } if (old != NULL) /* We don't need the */ free(old); /* old definition now. */ } return(FPP_OK); }