mirror of
https://github.com/scratchfoundation/bgfx.git
synced 2024-11-25 09:08:22 -05:00
634 lines
20 KiB
C
634 lines
20 KiB
C
|
/******************************************************************************
|
||
|
Copyright (c) 1999 Daniel Stenberg
|
||
|
|
||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
of this software and associated documentation files (the "Software"), to deal
|
||
|
in the Software without restriction, including without limitation the rights
|
||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
|
copies of the Software, and to permit persons to whom the Software is
|
||
|
furnished to do so, subject to the following conditions:
|
||
|
|
||
|
The above copyright notice and this permission notice shall be included in
|
||
|
all copies or substantial portions of the Software.
|
||
|
|
||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||
|
SOFTWARE.
|
||
|
******************************************************************************/
|
||
|
#include <stdio.h>
|
||
|
#include <ctype.h>
|
||
|
#include "cppdef.h"
|
||
|
#include "cpp.h"
|
||
|
|
||
|
INLINE FILE_LOCAL ReturnCode checkparm(struct Global *, int, DEFBUF *, int);
|
||
|
INLINE FILE_LOCAL ReturnCode stparmscan(struct Global *, int);
|
||
|
INLINE FILE_LOCAL ReturnCode textput(struct Global *, char *);
|
||
|
FILE_LOCAL ReturnCode charput(struct Global *, int);
|
||
|
INLINE FILE_LOCAL ReturnCode expcollect(struct Global *);
|
||
|
INLINE FILE_LOCAL char *doquoting(char *, char *);
|
||
|
|
||
|
|
||
|
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; /* 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[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);
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
INLINE FILE_LOCAL
|
||
|
ReturnCode stparmscan(struct Global *global, int delim)
|
||
|
{
|
||
|
/*
|
||
|
* Normal string parameter scan.
|
||
|
*/
|
||
|
|
||
|
unsigned char *wp;
|
||
|
int i;
|
||
|
ReturnCode ret;
|
||
|
|
||
|
wp = (unsigned char *)global->workp; /* Here's where it starts */
|
||
|
ret=scanstring(global, delim, save);
|
||
|
if(ret)
|
||
|
return(ret); /* Exit on scanstring error */
|
||
|
global->workp[-1] = EOS; /* Erase trailing quote */
|
||
|
wp++; /* -> first string content byte */
|
||
|
for (i = 0; i < global->nargs; i++) {
|
||
|
if (streq(global->parlist[i], (char *)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 */
|
||
|
global->workp = (char *)wp + 1; /* Reset string end */
|
||
|
return(FPP_OK);
|
||
|
}
|
||
|
}
|
||
|
global->workp[-1] = wp[-1]; /* Nope, reset end quote. */
|
||
|
return(FPP_OK);
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
INLINE FILE_LOCAL
|
||
|
ReturnCode textput(struct Global *global, char *text)
|
||
|
{
|
||
|
/*
|
||
|
* Put the string in the parm[] buffer.
|
||
|
*/
|
||
|
|
||
|
int size;
|
||
|
|
||
|
size = strlen(text) + 1;
|
||
|
if ((global->parmp + size) >= &global->parm[NPARMWORK]) {
|
||
|
cfatal(global, FATAL_MACRO_AREA_OVERFLOW);
|
||
|
return(FPP_WORK_AREA_OVERFLOW);
|
||
|
} else {
|
||
|
strcpy(global->parmp, text);
|
||
|
global->parmp += size;
|
||
|
}
|
||
|
return(FPP_OK);
|
||
|
}
|
||
|
|
||
|
FILE_LOCAL
|
||
|
ReturnCode charput(struct Global *global, int c)
|
||
|
{
|
||
|
/*
|
||
|
* Put the byte in the parm[] buffer.
|
||
|
*/
|
||
|
|
||
|
if (global->parmp >= &global->parm[NPARMWORK]) {
|
||
|
cfatal(global, FATAL_MACRO_AREA_OVERFLOW);
|
||
|
return(FPP_WORK_AREA_OVERFLOW);
|
||
|
}
|
||
|
*global->parmp++ = c;
|
||
|
return(FPP_OK);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* M a c r o E x p a n s i o n
|
||
|
*/
|
||
|
|
||
|
ReturnCode expand(struct Global *global, 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.)
|
||
|
*/
|
||
|
int c;
|
||
|
FILEINFO *file;
|
||
|
ReturnCode ret=FPP_OK;
|
||
|
|
||
|
/*
|
||
|
* If no macro is pending, save the name of this macro
|
||
|
* for an eventual error message.
|
||
|
*/
|
||
|
if (global->recursion++ == 0)
|
||
|
global->macro = tokenp;
|
||
|
else if (global->recursion == RECURSION_LIMIT) {
|
||
|
cerror(global, ERROR_RECURSIVE_MACRO, tokenp->name, global->macro->name);
|
||
|
if (global->rec_recover) {
|
||
|
do {
|
||
|
c = get(global);
|
||
|
} while (global->infile != NULL && global->infile->fp == NULL);
|
||
|
unget(global);
|
||
|
global->recursion = 0;
|
||
|
return(FPP_OK);
|
||
|
}
|
||
|
}
|
||
|
/*
|
||
|
* Here's a macro to expand.
|
||
|
*/
|
||
|
global->nargs = 0; /* Formals counter */
|
||
|
global->parmp = global->parm; /* Setup parm buffer */
|
||
|
switch (tokenp->nargs) {
|
||
|
case (-2): /* __LINE__ */
|
||
|
if(global->infile->fp)
|
||
|
/* This is a file */
|
||
|
sprintf(global->work, "%d", global->line);
|
||
|
else
|
||
|
/* This is a macro! Find out the file line number! */
|
||
|
for (file = global->infile; file != NULL; file = file->parent) {
|
||
|
if (file->fp != NULL) {
|
||
|
sprintf(global->work, "%d", file->line);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
ret=ungetstring(global, global->work);
|
||
|
if(ret)
|
||
|
return(ret);
|
||
|
break;
|
||
|
|
||
|
case (-3): /* __FILE__ */
|
||
|
for (file = global->infile; file != NULL; file = file->parent) {
|
||
|
if (file->fp != NULL) {
|
||
|
sprintf(global->work, "\"%s\"", (file->progname != NULL)
|
||
|
? file->progname : file->filename);
|
||
|
ret=ungetstring(global, global->work);
|
||
|
if(ret)
|
||
|
return(ret);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case (-4): /* __FUNC__ */
|
||
|
sprintf(global->work, "\"%s\"", global->functionname[0]?
|
||
|
global->functionname : "<unknown function>");
|
||
|
ret=ungetstring(global, global->work);
|
||
|
if(ret)
|
||
|
return(ret);
|
||
|
break;
|
||
|
|
||
|
case (-5): /* __FUNC_LINE__ */
|
||
|
sprintf(global->work, "%d", global->funcline);
|
||
|
ret=ungetstring(global, global->work);
|
||
|
if(ret)
|
||
|
return(ret);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
/*
|
||
|
* Nothing funny about this macro.
|
||
|
*/
|
||
|
if (tokenp->nargs < 0) {
|
||
|
cfatal(global, FATAL_ILLEGAL_MACRO, tokenp->name);
|
||
|
return(FPP_ILLEGAL_MACRO);
|
||
|
}
|
||
|
while ((c = skipws(global)) == '\n') /* Look for (, skipping */
|
||
|
global->wrongline = TRUE; /* spaces and newlines */
|
||
|
if (c != '(') {
|
||
|
/*
|
||
|
* If the programmer writes
|
||
|
* #define foo() ...
|
||
|
* ...
|
||
|
* foo [no ()]
|
||
|
* just write foo to the output stream.
|
||
|
*/
|
||
|
unget(global);
|
||
|
cwarn(global, WARN_MACRO_NEEDS_ARGUMENTS, tokenp->name);
|
||
|
|
||
|
/* fputs(tokenp->name, stdout); */
|
||
|
Putstring(global, tokenp->name);
|
||
|
return(FPP_OK);
|
||
|
} else if (!(ret=expcollect(global))) { /* Collect arguments */
|
||
|
if (tokenp->nargs != global->nargs) { /* Should be an error? */
|
||
|
cwarn(global, WARN_WRONG_NUMBER_ARGUMENTS, tokenp->name);
|
||
|
}
|
||
|
} else { /* Collect arguments */
|
||
|
return(ret); /* We failed in argument colleting! */
|
||
|
}
|
||
|
case DEF_NOARGS: /* No parameters just stuffs */
|
||
|
ret=expstuff(global, tokenp->name, tokenp->repl); /* expand macro */
|
||
|
} /* nargs switch */
|
||
|
return(ret);
|
||
|
}
|
||
|
|
||
|
INLINE FILE_LOCAL
|
||
|
ReturnCode expcollect(struct Global *global)
|
||
|
{
|
||
|
/*
|
||
|
* Collect the actual parameters for this macro.
|
||
|
*/
|
||
|
|
||
|
int c;
|
||
|
int paren; /* For embedded ()'s */
|
||
|
ReturnCode ret;
|
||
|
|
||
|
for (;;) {
|
||
|
paren = 0; /* Collect next arg. */
|
||
|
while ((c = skipws(global)) == '\n')/* Skip over whitespace */
|
||
|
global->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.
|
||
|
*/
|
||
|
*global->parmp = EOS; /* Make sure terminated */
|
||
|
break; /* Exit collection loop */
|
||
|
}
|
||
|
else if (global->nargs >= LASTPARM) {
|
||
|
cfatal(global, FATAL_TOO_MANY_ARGUMENTS_EXPANSION);
|
||
|
return(FPP_TOO_MANY_ARGUMENTS);
|
||
|
}
|
||
|
global->parlist[global->nargs++] = global->parmp; /* At start of new arg */
|
||
|
for (;; c = cget(global)) { /* Collect arg's bytes */
|
||
|
if (c == EOF_CHAR) {
|
||
|
cerror(global, ERROR_EOF_IN_ARGUMENT);
|
||
|
return(FPP_EOF_IN_MACRO); /* Sorry. */
|
||
|
}
|
||
|
else if (c == '\\') { /* Quote next character */
|
||
|
charput(global, c); /* Save the \ for later */
|
||
|
charput(global, cget(global)); /* Save the next char. */
|
||
|
continue; /* And go get another */
|
||
|
}
|
||
|
else if (type[c] == QUO) { /* Start of string? */
|
||
|
ret=scanstring(global, c, (ReturnCode (*)(struct Global *, int))charput); /* Scan it off */
|
||
|
if(ret)
|
||
|
return(ret);
|
||
|
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(global); /* 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? */
|
||
|
global->wrongline = TRUE; /* We'll need a #line */
|
||
|
charput(global, c); /* Store this one */
|
||
|
} /* Collect an argument */
|
||
|
charput(global, EOS); /* Terminate argument */
|
||
|
} /* Collect all args. */
|
||
|
return(FPP_OK); /* Normal return */
|
||
|
}
|
||
|
|
||
|
|
||
|
#if OK_CONCAT
|
||
|
|
||
|
INLINE FILE_LOCAL
|
||
|
char *doquoting(char *to, char *from)
|
||
|
{
|
||
|
*to++ = '"';
|
||
|
while (*from) {
|
||
|
if (*from == '\\' || *from == '"')
|
||
|
*to++ = '\\';
|
||
|
*to++ = *from++;
|
||
|
}
|
||
|
*to++ = '"';
|
||
|
|
||
|
return to;
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
ReturnCode expstuff(struct Global *global,
|
||
|
char *MacroName,
|
||
|
char *MacroReplace)
|
||
|
{
|
||
|
/*
|
||
|
* Stuff the macro body, replacing formal parameters by actual parameters.
|
||
|
*/
|
||
|
int c; /* Current character */
|
||
|
char *inp; /* -> repl string */
|
||
|
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 */
|
||
|
ReturnCode ret;
|
||
|
#if OK_CONCAT
|
||
|
char quoting; /* Quote macro argument */
|
||
|
#endif
|
||
|
|
||
|
ret = getfile(global, NBUFF, MacroName, &file);
|
||
|
if(ret)
|
||
|
return(ret);
|
||
|
inp = MacroReplace; /* -> 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) < global->nargs) {
|
||
|
size = strlen(global->parlist[c]);
|
||
|
#if OK_CONCAT
|
||
|
if (quoting) {
|
||
|
size++;
|
||
|
size *= 2; /* worst case condition */
|
||
|
}
|
||
|
#endif
|
||
|
if ((defp + size) >= defend) {
|
||
|
cfatal(global, FATAL_OUT_OF_SPACE_IN_ARGUMENT, MacroName);
|
||
|
return(FPP_OUT_OF_SPACE_IN_MACRO_EXPANSION);
|
||
|
}
|
||
|
/*
|
||
|
* Erase the extra set of quotes.
|
||
|
*/
|
||
|
if (string_magic && defp[-1] == global->parlist[c][0]) {
|
||
|
strcpy(defp-1, global->parlist[c]);
|
||
|
defp += (size - 2);
|
||
|
}
|
||
|
#if OK_CONCAT
|
||
|
else if (quoting)
|
||
|
defp = doquoting(defp, global->parlist[c]);
|
||
|
#endif
|
||
|
else {
|
||
|
strcpy(defp, global->parlist[c]);
|
||
|
defp += size;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if (defp >= defend) {
|
||
|
cfatal(global, FATAL_OUT_OF_SPACE_IN_ARGUMENT, MacroName);
|
||
|
return(FPP_OUT_OF_SPACE_IN_MACRO_EXPANSION);
|
||
|
} else
|
||
|
*defp++ = c;
|
||
|
quoting = 0;
|
||
|
}
|
||
|
}
|
||
|
*defp = EOS;
|
||
|
return(FPP_OK);
|
||
|
}
|
||
|
|