blob: 11a451b8b0b6fc7113b20b313f66cf889688a27f [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
#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 */
void InitCpp4()
{
int i;
for( i = 0; i < NPARMWORK; i++ )
parm[ i ] = 0;
for( i = 0; i < LASTPARM; i++ )
parlist[ i ] = NULL;
nargs = 0;
}
void 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 = 0; /* 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 OK_CONCAT
#if COMMENT_INVISIBLE
if (c == COM_SEP) { /* Token concatenation? */
save(TOK_SEP); /* Stuff a delimiter */
c = get();
#else
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 */
#endif
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 {
#if ! COMMENT_INVISIBLE
ciwarn("Strange character after # (%d.)", c);
#endif
}
continue;
}
#endif
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 */
#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();
}
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 */
}
void checkparm(int c, DEFBUF* dp)
/*
* 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;
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 */
}
#if STRING_FORMAL
void 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;
/*
* 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);
else {
save(c);
if (c == '\\')
save(get());
}
}
instring = FALSE;
if (c != delim)
cerror("Unterminated string in macro body", NULLST);
save(c);
}
#else
void stparmscan(int delim)
/*
* Normal string parameter scan.
*/
{
register char *wp;
register int i;
wp = workp; /* Here's where it starts */
if (!scanstring(delim, save))
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)) {
#ifdef SOLAR
*wp++ = DEL;
*wp++ = MAC_PARM + PAR_MAC; /* Stuff a magic marker */
*wp++ = (char)(i + MAC_PARM); /* Make a formal marker */
*wp = wp[-4]; /* Add on closing quote */
workp = wp + 1; /* Reset string end */
#else
*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 */
#endif
return;
}
}
workp[-1] = wp[-1]; /* Nope, reset end quote. */
}
#endif
void doundef()
/*
* Remove the symbol from the defined list.
* Called from the #control processor.
*/
{
register 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
}
}
}
void textput(char* text)
/*
* Put the string in the parm[] buffer.
*/
{
register int size;
size = strlen(text) + 1;
if ((parmp + size) >= &parm[NPARMWORK])
cfatal("Macro work area overflow", NULLST);
else {
strcpy(parmp, text);
parmp += size;
}
}
void charput(int c)
/*
* Put the byte in the parm[] buffer.
*/
{
if (parmp >= &parm[NPARMWORK])
cfatal("Macro work area overflow", NULLST);
else {
*parmp++ = (char)c;
}
}
/*
* M a c r o E x p a n s i o n
*/
static DEFBUF *macro; /* Catches start of infinite macro */
void expand(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;
#ifndef ZTC /* BP */
extern FILEINFO *getfile();
#endif
#if OSL_DEBUG_LEVEL > 1
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;
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, pCppOut );
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 OSL_DEBUG_LEVEL > 1
if (debug)
dumpparm("expand");
#endif
} /* Collect arguments */
case DEF_NOARGS: /* No parameters just stuffs */
expstuff(tokenp); /* Do actual parameters */
} /* nargs switch */
}
FILE_LOCAL int
expcollect()
/*
* Collect the actual parameters for this macro. TRUE if ok.
*/
{
register int c;
register int paren; /* For embedded ()'s */
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);
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 OSL_DEBUG_LEVEL > 1
if (debug)
fprintf( pCppOut, "parm[%d] = \"%s\"\n", nargs, parlist[nargs - 1]);
#endif
} /* Collect all args. */
return (TRUE); /* Normal return */
}
FILE_LOCAL
void expstuff(DEFBUF* tokenp)
/*
* 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 */
#ifndef ZTC /* BP */
extern FILEINFO *getfile();
#endif
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) {
while ((c = (*inp++ & 0xFF)) != EOS) {
#ifdef SOLAR
if (c == DEL) {
c = (*inp++ & 0xFF);
#else
if (c >= MAC_PARM && c <= (MAC_PARM + PAR_MAC)) {
#endif
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 ((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);
}
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++ = (char)c;
}
}
}
*defp = EOS;
#if OSL_DEBUG_LEVEL > 1
if (debug > 1)
fprintf( pCppOut, "macroline: \"%s\"\n", file->buffer);
#endif
}
#if OSL_DEBUG_LEVEL > 1
void dumpparm(char* why)
/*
* Dump parameter list.
*/
{
register int i;
fprintf( pCppOut, "dump of %d parameters (%d bytes total) %s\n",
nargs, parmp - parm, why);
for (i = 0; i < nargs; i++) {
fprintf( pCppOut, "parm[%d] (%d) = \"%s\"\n",
i + 1, (int)strlen(parlist[i]), parlist[i]);
}
}
#endif