void doundef(struct Global *global) /* * Remove the symbol from the defined list. * Called from the #control processor. */ { int c; if (type[(c = skipws(global))] != LET) cerror(global, ERROR_ILLEGAL_UNDEF); else { scanid(global, c); /* Get name to tokenbuf */ (void) defendel(global, global->tokenbuf, TRUE); } }
/* * Remove the symbol from the defined list. * Called from the #control processor. */ void doundef() { int c; if (type[(c = skipws())] != LET) cerror("Illegal #undef argument", NULLST); else { scanid(c); /* Get name to token[] */ if (defendel(token, TRUE) == NULL) { #ifdef STRICT_UNDEF cwarn("Symbol \"%s\" not defined in #undef", token); #endif } } }
/* * 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". */ void checkparm(int c, DEFBUF* dp) { int i; char* cp; scanid(c); /* Get parm to token[] */ for (i = 0; i < nargs; i++) /* For each argument */ { if (streq(parlist[i], token)) /* If it's known */ { #ifdef SOLAR save(DEL); #endif save(i + MAC_PARM); /* Save a magic cookie */ return; /* And exit the search */ } } if (streq(dp->name, token)) /* Macro name in body? */ save(DEF_MAGIC); /* Save magic marker */ for (cp = token; *cp != EOS;) /* And save */ save(*cp++); /* The token itself */ }
INLINE FILE_LOCAL ReturnCode checkparm(struct Global *global, 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". */ int i; char *cp; ReturnCode ret=FPP_OK; scanid(global, c); /* Get parm to tokenbuf */ for (i = 0; i < global->nargs; i++) { /* For each argument */ if (streq(global->parlist[i], global->tokenbuf)) { /* If it's known */ #if OK_CONCAT if (quoting) { /* Special handling of */ ret=save(global, QUOTE_PARM); /* #formal inside defn */ if(ret) return(ret); } #endif ret=save(global, i + MAC_PARM); /* Save a magic cookie */ return(ret); /* And exit the search */ } } if (streq(dp->name, global->tokenbuf)) /* Macro name in body? */ ret=save(global, DEF_MAGIC); /* Save magic marker */ for (cp = global->tokenbuf; *cp != EOS;) /* And save */ ret=save(global, *cp++); /* The token itself */ return(ret); }
/* * 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 */ }
int control(int counter) /* * Process #control lines. Simple commands are processed inline, * while complex commands have their own subroutines. * * The counter is used to force out a newline before #line, and * #pragma commands. This prevents these commands from ending up at * the end of the previous line if cpp is invoked with the -C option. */ { register int c; register char *tp; register int hash; char *ep; c = skipws(); if (c == '\n' || c == EOF_CHAR) return (counter + 1); if (!isdigit(c)) scanid(c); /* Get #word to token[] */ else { unget(); /* Hack -- allow #123 as a */ strcpy(token, "line"); /* synonym for #line 123 */ } hash = (token[1] == EOS) ? L_nogood : (token[0] + (token[2] << 1)); switch (hash) { case L_assert: tp = "assert"; break; case L_define: tp = "define"; break; case L_elif: tp = "elif"; break; case L_else: tp = "else"; break; case L_endif: tp = "endif"; break; case L_if: tp = "if"; break; case L_ifdef: tp = "ifdef"; break; case L_ifndef: tp = "ifndef"; break; case L_include: tp = "include"; break; case L_line: tp = "line"; break; case L_pragma: tp = "pragma"; break; case L_undef: tp = "undef"; break; case L_error: tp = "error"; break; #if OSL_DEBUG_LEVEL > 1 case L_debug: tp = "debug"; break; case L_nodebug: tp = "nodebug"; break; #endif default: hash = L_nogood; case L_nogood: tp = ""; break; } if (!streq(tp, token)) hash = L_nogood; /* * hash is set to a unique value corresponding to the * control keyword (or L_nogood if we think it's nonsense). */ if (infile->fp == NULL) cwarn("Control line \"%s\" within macro expansion", token); if (!compiling) { /* Not compiling now */ switch (hash) { case L_if: /* These can't turn */ case L_ifdef: /* compilation on, but */ case L_ifndef: /* we must nest #if's */ if (++ifptr >= &ifstack[BLK_NEST]) goto if_nest_err; *ifptr = 0; /* !WAS_COMPILING */ case L_line: /* Many */ /* * Are pragma's always processed? */ case L_pragma: /* options */ case L_include: /* are uninteresting */ case L_define: /* if we */ case L_undef: /* aren't */ case L_assert: /* compiling. */ case L_error: /* BP 5.3.92, #error */ dump_line: skipnl(); /* Ignore rest of line */ return (counter + 1); } } /* * Make sure that #line and #pragma are output on a fresh line. */ if (counter > 0 && (hash == L_line || hash == L_pragma)) { PUTCHAR('\n'); counter--; } switch (hash) { case L_line: /* * Parse the line to update the line number and "progname" * field and line number for the next input line. * Set wrongline to force it out later. */ c = skipws(); workp = work; /* Save name in work */ while (c != '\n' && c != EOF_CHAR) { save(c); c = get(); } unget(); save(EOS); /* * Split #line argument into <line-number> and <name> * We subtract 1 as we want the number of the next line. */ line = atoi(work) - 1; /* Reset line number */ for (tp = work; isdigit(*tp) || type[(int)*tp] == SPA; tp++) ; /* Skip over digits */ if (*tp != EOS) { /* Got a filename, so: */ if (*tp == '"' && (ep = strrchr(tp + 1, '"')) != NULL) { tp++; /* Skip over left quote */ *ep = EOS; /* And ignore right one */ } if (infile->progname != NULL) /* Give up the old name */ free(infile->progname); /* if it's allocated. */ infile->progname = savestring(tp); } wrongline = TRUE; /* Force output later */ break; case L_include: doinclude(); break; case L_define: dodefine(); break; case L_undef: doundef(); break; case L_else: if (ifptr == &ifstack[0]) goto nest_err; else if ((*ifptr & ELSE_SEEN) != 0) goto else_seen_err; *ifptr |= ELSE_SEEN; if ((*ifptr & WAS_COMPILING) != 0) { if (compiling || (*ifptr & TRUE_SEEN) != 0) compiling = FALSE; else { compiling = TRUE; } } break; case L_elif: if (ifptr == &ifstack[0]) goto nest_err; else if ((*ifptr & ELSE_SEEN) != 0) { else_seen_err: cerror("#%s may not follow #else", token); goto dump_line; } if ((*ifptr & (WAS_COMPILING | TRUE_SEEN)) != WAS_COMPILING) { compiling = FALSE; /* Done compiling stuff */ goto dump_line; /* Skip this clause */ } doif(L_if); break; case L_if: case L_ifdef: case L_ifndef: if (++ifptr >= &ifstack[BLK_NEST]) if_nest_err: cfatal("Too many nested #%s statements", token); *ifptr = WAS_COMPILING; doif(hash); break; case L_endif: if (ifptr == &ifstack[0]) { nest_err: cerror("#%s must be in an #if", token); goto dump_line; } if (!compiling && (*ifptr & WAS_COMPILING) != 0) wrongline = TRUE; compiling = ((*ifptr & WAS_COMPILING) != 0); --ifptr; break; case L_assert: if (eval() == 0) cerror("Preprocessor assertion failure", NULLST); break; case L_pragma: /* * #pragma is provided to pass "options" to later * passes of the compiler. cpp doesn't have any yet. */ fprintf( pCppOut, "#pragma "); while ((c = get()) != '\n' && c != EOF_CHAR) cput(c); unget(); break; #if OSL_DEBUG_LEVEL > 1 case L_debug: if (debug == 0) dumpdef("debug set on"); debug++; break; case L_nodebug: debug--; break; #endif case L_error: /* BP 5.3.92, #error */ { fprintf( pCppOut, "cpp: line %u, Error directive: ", line ); while ((c = get()) != '\n' && c != EOF_CHAR) cput(c); fprintf( pCppOut, "\n" ); exit( 1 ); break; } default: /* * Undefined #control keyword. * Note: the correct behavior may be to warn and * pass the line to a subsequent compiler pass. * This would allow #asm or similar extensions. */ cerror("Illegal # command \"%s\"", token); break; } if (hash != L_include) { #if OLD_PREPROCESSOR /* * Ignore the rest of the #control line so you can write * #if foo * #endif foo */ goto dump_line; /* Take common exit */ #else if (skipws() != '\n') { cwarn("Unexpected text in #control line ignored", NULLST); skipnl(); } #endif } return (counter + 1); }
ReturnCode control( struct Global *global, int *counter ) /* Pending newline counter */ { /* * Process #control lines. Simple commands are processed inline, * while complex commands have their own subroutines. * * The counter is used to force out a newline before #line, and * #pragma commands. This prevents these commands from ending up at * the end of the previous line if cpp is invoked with the -C option. */ int c; char *tp; int hash; char *ep; ReturnCode ret; c = skipws( global ); if( c == '\n' || c == EOF_CHAR ) { (*counter)++; return(FPP_OK); } if( !isdigit(c) ) scanid( global, c ); /* Get #word to tokenbuf */ else { unget( global ); /* Hack -- allow #123 as a */ strcpy( global->tokenbuf, "line" ); /* synonym for #line 123 */ } hash = (global->tokenbuf[1] == EOS) ? L_nogood : (global->tokenbuf[0] + (global->tokenbuf[2] << 1)); switch( hash ) { case L_assert: tp = "assert"; break; case L_define: tp = "define"; break; case L_elif: tp = "elif"; break; case L_else: tp = "else"; break; case L_endif: tp = "endif"; break; case L_error: tp = "error"; break; case L_if: tp = "if"; break; case L_ifdef: tp = "ifdef"; break; case L_ifndef: tp = "ifndef"; break; case L_include: tp = "include"; break; case L_line: tp = "line"; break; case L_pragma: tp = "pragma"; break; case L_undef: tp = "undef"; break; default: hash = L_nogood; case L_nogood: tp = ""; break; } if( !streq( tp, global->tokenbuf ) ) hash = L_nogood; /* * hash is set to a unique value corresponding to the * control keyword (or L_nogood if we think it's nonsense). */ if( global->infile->fp == NULL ) cwarn( global, WARN_CONTROL_LINE_IN_MACRO, global->tokenbuf ); if( !compiling ) { /* Not compiling now */ switch( hash ) { case L_if: /* These can't turn */ case L_ifdef: /* compilation on, but */ case L_ifndef: /* we must nest #if's */ if( ++global->ifptr >= &global->ifstack[BLK_NEST] ) { cfatal( global, FATAL_TOO_MANY_NESTINGS, global->tokenbuf ); return( FPP_TOO_MANY_NESTED_STATEMENTS ); } *global->ifptr = 0; /* !WAS_COMPILING */ case L_line: /* Many */ /* * Are pragma's always processed? */ case L_pragma: /* options */ case L_include: /* are uninteresting */ case L_define: /* if we */ case L_undef: /* aren't */ case L_assert: /* compiling. */ case L_error: dump_line( global, counter ); /* Ignore rest of line */ return(FPP_OK); } } /* * Make sure that #line and #pragma are output on a fresh line. */ if( *counter > 0 && (hash == L_line || hash == L_pragma) ) { Putchar( global, '\n' ); (*counter)--; } switch( hash ) { case L_line: /* * Parse the line to update the line number and "progname" * field and line number for the next input line. * Set wrongline to force it out later. */ c = skipws( global ); global->workp = global->work; /* Save name in work */ while( c != '\n' && c != EOF_CHAR ) { if( (ret = save( global, c )) ) return(ret); c = get( global ); } unget( global ); if( (ret = save( global, EOS )) ) return(ret); /* * Split #line argument into <line-number> and <name> * We subtract 1 as we want the number of the next line. */ global->line = atoi(global->work) - 1; /* Reset line number */ for( tp = global->work; isdigit(*tp) || type[(unsigned)*tp] == SPA; tp++) ; /* Skip over digits */ if( *tp != EOS ) { /* Got a filename, so: */ if( *tp == '"' && (ep = strrchr(tp + 1, '"')) != NULL ) { tp++; /* Skip over left quote */ *ep = EOS; /* And ignore right one */ } if( global->infile->progname != NULL ) /* Give up the old name if it's allocated. */ free( global->infile->progname ); global->infile->progname = savestring( global, tp ); } global->wrongline = TRUE; /* Force output later */ break; case L_include: ret = doinclude( global ); if( ret ) return(ret); break; case L_define: ret = dodefine( global ); if( ret ) return(ret); break; case L_undef: doundef( global ); break; case L_else: if( global->ifptr == &global->ifstack[0] ) { cerror( global, ERROR_STRING_MUST_BE_IF, global->tokenbuf ); dump_line( global, counter ); return( FPP_OK ); } else if( (*global->ifptr & ELSE_SEEN) != 0 ) { cerror( global, ERROR_STRING_MAY_NOT_FOLLOW_ELSE, global->tokenbuf ); dump_line( global, counter ); return( FPP_OK ); } *global->ifptr |= ELSE_SEEN; if( (*global->ifptr & WAS_COMPILING) != 0 ) { if( compiling || (*global->ifptr & TRUE_SEEN) != 0 ) compiling = FALSE; else { compiling = TRUE; } } break; case L_elif: if( global->ifptr == &global->ifstack[0] ) { cerror( global, ERROR_STRING_MUST_BE_IF, global->tokenbuf ); dump_line( global, counter ); return( FPP_OK ); } else if( (*global->ifptr & ELSE_SEEN) != 0 ) { cerror( global, ERROR_STRING_MAY_NOT_FOLLOW_ELSE, global->tokenbuf ); dump_line( global, counter ); return( FPP_OK ); } if( (*global->ifptr & (WAS_COMPILING | TRUE_SEEN)) != WAS_COMPILING ) { compiling = FALSE; /* Done compiling stuff */ dump_line( global, counter ); /* Skip this clause */ return( FPP_OK ); } ret = doif( global, L_if ); if( ret ) return(ret); break; case L_error: cerror(global, ERROR_ERROR); break; case L_if: case L_ifdef: case L_ifndef: if( ++global->ifptr < &global->ifstack[BLK_NEST] ) { *global->ifptr = WAS_COMPILING; ret = doif( global, hash ); if( ret ) return(ret); break; } cfatal( global, FATAL_TOO_MANY_NESTINGS, global->tokenbuf ); return( FPP_TOO_MANY_NESTED_STATEMENTS ); case L_endif: if( global->ifptr == &global->ifstack[0] ) { cerror( global, ERROR_STRING_MUST_BE_IF, global->tokenbuf ); dump_line( global, counter ); return(FPP_OK); } if( !compiling && (*global->ifptr & WAS_COMPILING) != 0 ) global->wrongline = TRUE; compiling = ((*global->ifptr & WAS_COMPILING) != 0); --global->ifptr; break; case L_assert: { int result; ret = eval( global, &result ); if(ret) return(ret); if( result == 0 ) cerror( global, ERROR_PREPROC_FAILURE ); } break; case L_pragma: /* * #pragma is provided to pass "options" to later * passes of the compiler. cpp doesn't have any yet. */ Putstring( global, "#pragma " ); while( (c = get( global ) ) != '\n' && c != EOF_CHAR ) Putchar( global, c ); unget( global ); Putchar( global, '\n' ); break; default: /* * Undefined #control keyword. * Note: the correct behavior may be to warn and * pass the line to a subsequent compiler pass. * This would allow #asm or similar extensions. */ if( global->warnillegalcpp ) cwarn( global, WARN_ILLEGAL_COMMAND, global->tokenbuf ); Putchar( global, '#' ); Putstring( global, global->tokenbuf ); Putchar( global, ' ' ); while( (c = get( global ) ) != '\n' && c != EOF_CHAR ) Putchar( global, c ); unget( global ); Putchar( global, '\n' ); break; } if( hash != L_include ) { #if OLD_PREPROCESSOR /* * Ignore the rest of the #control line so you can write * #if foo * #endif foo */ dump_line( global, counter ); /* Take common exit */ return( FPP_OK ); #else if (skipws(global) != '\n') { cwarn( global, WARN_UNEXPECTED_TEXT_IGNORED ); skipnl( global ); } #endif } (*counter)++; return( FPP_OK ); }
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); }