2012-04-03 20:30:07 -07:00
/******************************************************************************
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 */
2013-04-07 23:53:58 -07:00
char * old = NULL ; /* Remember redefined */
2012-04-03 20:30:07 -07:00
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() " .
*/
2015-09-01 16:44:11 -07:00
global - > nargs = DEF_NOARGS ; /* No () parameters */
2012-04-03 20:30:07 -07:00
}
if ( type [ c ] = = SPA ) /* At whitespace? */
c = skipws ( global ) ; /* Not any more. */
2015-09-01 16:44:11 -07:00
global - > workp = global - > work ; /* Replacement put here */
global - > inmacro = TRUE ; /* Keep \<newline> now */
quoting = 0 ; /* No # seen yet. */
2012-04-03 20:30:07 -07:00
while ( c ! = EOF_CHAR & & c ! = ' \n ' ) { /* Compile macro body */
# if OK_CONCAT
if ( c = = ' # ' ) { /* Token concatenation? */
if ( ( c = get ( global ) ) ! = ' # ' ) { /* No, not really */
2015-09-01 16:44:11 -07:00
quoting = 1 ; /* Maybe quoting op. */
continue ;
2012-04-03 20:30:07 -07:00
}
2013-04-07 23:53:58 -07:00
while ( global - > workp > global - > work & & type [ ( unsigned ) * ( global - > workp - 1 ) ] = = SPA )
2015-09-01 16:44:11 -07:00
- - global - > workp ; /* Erase leading spaces */
// if ((ret=save(global, TOK_SEP))) /* Stuff a delimiter */
// return(ret);
2012-04-03 20:30:07 -07:00
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 .
*/
2013-03-20 21:44:17 -07:00
size_t size ;
2012-04-03 20:30:07 -07:00
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 */
2013-03-20 21:44:17 -07:00
size_t size ; /* Actual parm. size */
2012-04-03 20:30:07 -07:00
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 ) ;
}