2006-07-23 22:39:59 +04:00
/*
2011-01-18 23:12:49 +03:00
* Copyright ( c ) 2002 - 2011 Tony Finch < dot @ dotat . at >
2006-07-23 22:39:59 +04:00
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
* 1. Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
* 2. Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ` ` AS IS ' ' AND
* ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED . IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL
* DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION )
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT
* LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE .
*/
2009-11-27 18:50:30 +03:00
/*
2011-01-18 23:12:49 +03:00
* unifdef - remove ifdef ' ed lines
*
2009-11-27 18:50:30 +03:00
* This code was derived from software contributed to Berkeley by Dave Yost .
* It was rewritten to support ANSI C by Tony Finch . The original version
* of unifdef carried the 4 - clause BSD copyright licence . None of its code
* remains in this version ( though some of the names remain ) so it now
* carries a more liberal licence .
*
2006-07-23 22:39:59 +04:00
* Wishlist :
* provide an option which will append the name of the
* appropriate symbol after # else ' s and # endif ' s
* provide an option which will check symbols after
* # else ' s and # endif ' s to see that they match their
* corresponding # ifdef or # ifndef
*
2011-01-18 23:12:49 +03:00
* These require better buffer handling , which would also make
* it possible to handle all " dodgy " directives correctly .
2006-07-23 22:39:59 +04:00
*/
2011-01-18 23:12:49 +03:00
# include <sys/types.h>
# include <sys/stat.h>
2006-07-23 22:39:59 +04:00
# include <ctype.h>
# include <err.h>
2011-01-18 23:12:49 +03:00
# include <errno.h>
2006-07-23 22:39:59 +04:00
# include <stdarg.h>
# include <stdbool.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <unistd.h>
2011-01-18 23:12:49 +03:00
const char copyright [ ] =
" @(#) $Version: unifdef-2.5 $ \n "
" @(#) $Author: Tony Finch (dot@dotat.at) $ \n "
" @(#) $URL: http://dotat.at/prog/unifdef $ \n "
;
2006-07-23 22:39:59 +04:00
/* types of input lines: */
typedef enum {
LT_TRUEI , /* a true #if with ignore flag */
LT_FALSEI , /* a false #if with ignore flag */
LT_IF , /* an unknown #if */
LT_TRUE , /* a true #if */
LT_FALSE , /* a false #if */
LT_ELIF , /* an unknown #elif */
LT_ELTRUE , /* a true #elif */
LT_ELFALSE , /* a false #elif */
LT_ELSE , /* #else */
LT_ENDIF , /* #endif */
LT_DODGY , /* flag: directive is not on one line */
LT_DODGY_LAST = LT_DODGY + LT_ENDIF ,
LT_PLAIN , /* ordinary line */
LT_EOF , /* end of file */
2009-11-27 18:50:30 +03:00
LT_ERROR , /* unevaluable #if */
2006-07-23 22:39:59 +04:00
LT_COUNT
} Linetype ;
static char const * const linetype_name [ ] = {
" TRUEI " , " FALSEI " , " IF " , " TRUE " , " FALSE " ,
" ELIF " , " ELTRUE " , " ELFALSE " , " ELSE " , " ENDIF " ,
" DODGY TRUEI " , " DODGY FALSEI " ,
" DODGY IF " , " DODGY TRUE " , " DODGY FALSE " ,
" DODGY ELIF " , " DODGY ELTRUE " , " DODGY ELFALSE " ,
" DODGY ELSE " , " DODGY ENDIF " ,
2009-11-27 18:50:30 +03:00
" PLAIN " , " EOF " , " ERROR "
2006-07-23 22:39:59 +04:00
} ;
/* state of #if processing */
typedef enum {
IS_OUTSIDE ,
IS_FALSE_PREFIX , /* false #if followed by false #elifs */
IS_TRUE_PREFIX , /* first non-false #(el)if is true */
IS_PASS_MIDDLE , /* first non-false #(el)if is unknown */
IS_FALSE_MIDDLE , /* a false #elif after a pass state */
IS_TRUE_MIDDLE , /* a true #elif after a pass state */
IS_PASS_ELSE , /* an else after a pass state */
IS_FALSE_ELSE , /* an else after a true state */
IS_TRUE_ELSE , /* an else after only false states */
IS_FALSE_TRAILER , /* #elifs after a true are false */
IS_COUNT
} Ifstate ;
static char const * const ifstate_name [ ] = {
" OUTSIDE " , " FALSE_PREFIX " , " TRUE_PREFIX " ,
" PASS_MIDDLE " , " FALSE_MIDDLE " , " TRUE_MIDDLE " ,
" PASS_ELSE " , " FALSE_ELSE " , " TRUE_ELSE " ,
" FALSE_TRAILER "
} ;
/* state of comment parser */
typedef enum {
NO_COMMENT = false , /* outside a comment */
C_COMMENT , /* in a comment like this one */
CXX_COMMENT , /* between // and end of line */
STARTING_COMMENT , /* just after slash-backslash-newline */
FINISHING_COMMENT , /* star-backslash-newline in a C comment */
CHAR_LITERAL , /* inside '' */
STRING_LITERAL /* inside "" */
} Comment_state ;
static char const * const comment_name [ ] = {
" NO " , " C " , " CXX " , " STARTING " , " FINISHING " , " CHAR " , " STRING "
} ;
/* state of preprocessor line parser */
typedef enum {
LS_START , /* only space and comments on this line */
LS_HASH , /* only space, comments, and a hash */
LS_DIRTY /* this line can't be a preprocessor line */
} Line_state ;
static char const * const linestate_name [ ] = {
" START " , " HASH " , " DIRTY "
} ;
/*
* Minimum translation limits from ISO / IEC 9899 : 1999 5.2 .4 .1
*/
# define MAXDEPTH 64 /* maximum #if nesting */
# define MAXLINE 4096 /* maximum length of line */
# define MAXSYMS 4096 /* maximum number of symbols */
/*
* Sometimes when editing a keyword the replacement text is longer , so
* we leave some space at the end of the tline buffer to accommodate this .
*/
# define EDITSLOP 10
2011-01-18 23:12:49 +03:00
/*
* For temporary filenames
*/
# define TEMPLATE "unifdef.XXXXXX"
2006-07-23 22:39:59 +04:00
/*
* Globals .
*/
2009-11-27 18:50:30 +03:00
static bool compblank ; /* -B: compress blank lines */
static bool lnblank ; /* -b: blank deleted lines */
2006-07-23 22:39:59 +04:00
static bool complement ; /* -c: do the complement */
static bool debugging ; /* -d: debugging reports */
static bool iocccok ; /* -e: fewer IOCCC errors */
2009-11-27 18:50:30 +03:00
static bool strictlogic ; /* -K: keep ambiguous #ifs */
2006-07-23 22:39:59 +04:00
static bool killconsts ; /* -k: eval constant #ifs */
static bool lnnum ; /* -n: add #line directives */
static bool symlist ; /* -s: output symbol list */
2011-01-18 23:12:49 +03:00
static bool symdepth ; /* -S: output symbol depth */
2006-07-23 22:39:59 +04:00
static bool text ; /* -t: this is a text file */
static const char * symname [ MAXSYMS ] ; /* symbol name */
static const char * value [ MAXSYMS ] ; /* -Dsym=value */
static bool ignore [ MAXSYMS ] ; /* -iDsym or -iUsym */
static int nsyms ; /* number of symbols */
static FILE * input ; /* input file pointer */
static const char * filename ; /* input file name */
static int linenum ; /* current line number */
2011-01-18 23:12:49 +03:00
static FILE * output ; /* output file pointer */
static const char * ofilename ; /* output file name */
static bool overwriting ; /* output overwrites input */
static char tempname [ FILENAME_MAX ] ; /* used when overwriting */
2006-07-23 22:39:59 +04:00
static char tline [ MAXLINE + EDITSLOP ] ; /* input buffer plus space */
static char * keyword ; /* used for editing #elif's */
2011-01-18 23:12:49 +03:00
static const char * newline ; /* input file format */
static const char newline_unix [ ] = " \n " ;
static const char newline_crlf [ ] = " \r \n " ;
2006-07-23 22:39:59 +04:00
static Comment_state incomment ; /* comment parser state */
static Line_state linestate ; /* #if line parser state */
static Ifstate ifstate [ MAXDEPTH ] ; /* #if processor state */
static bool ignoring [ MAXDEPTH ] ; /* ignore comments state */
static int stifline [ MAXDEPTH ] ; /* start of current #if */
static int depth ; /* current #if nesting */
static int delcount ; /* count of deleted lines */
2009-11-27 18:50:30 +03:00
static unsigned blankcount ; /* count of blank lines */
static unsigned blankmax ; /* maximum recent blankcount */
static bool constexpr ; /* constant #if expression */
2011-01-18 23:12:49 +03:00
static bool zerosyms = true ; /* to format symdepth output */
static bool firstsym ; /* ditto */
2006-07-23 22:39:59 +04:00
static int exitstat ; /* program exit status */
static void addsym ( bool , bool , char * ) ;
2011-01-18 23:12:49 +03:00
static void closeout ( void ) ;
2006-07-23 22:39:59 +04:00
static void debug ( const char * , . . . ) ;
static void done ( void ) ;
static void error ( const char * ) ;
static int findsym ( const char * ) ;
static void flushline ( bool ) ;
2009-11-27 18:50:30 +03:00
static Linetype parseline ( void ) ;
2006-07-23 22:39:59 +04:00
static Linetype ifeval ( const char * * ) ;
static void ignoreoff ( void ) ;
static void ignoreon ( void ) ;
static void keywordedit ( const char * ) ;
static void nest ( void ) ;
static void process ( void ) ;
2009-11-27 18:50:30 +03:00
static const char * skipargs ( const char * ) ;
2006-07-23 22:39:59 +04:00
static const char * skipcomment ( const char * ) ;
static const char * skipsym ( const char * ) ;
static void state ( Ifstate ) ;
static int strlcmp ( const char * , const char * , size_t ) ;
static void unnest ( void ) ;
static void usage ( void ) ;
2011-01-18 23:12:49 +03:00
static void version ( void ) ;
2006-07-23 22:39:59 +04:00
2009-11-27 18:50:30 +03:00
# define endsym(c) (!isalnum((unsigned char)c) && c != '_')
2006-07-23 22:39:59 +04:00
/*
* The main program .
*/
int
main ( int argc , char * argv [ ] )
{
int opt ;
2011-01-18 23:12:49 +03:00
while ( ( opt = getopt ( argc , argv , " i:D:U:I:o:bBcdeKklnsStV " ) ) ! = - 1 )
2006-07-23 22:39:59 +04:00
switch ( opt ) {
case ' i ' : /* treat stuff controlled by these symbols as text */
/*
* For strict backwards - compatibility the U or D
* should be immediately after the - i but it doesn ' t
* matter much if we relax that requirement .
*/
opt = * optarg + + ;
if ( opt = = ' D ' )
addsym ( true , true , optarg ) ;
else if ( opt = = ' U ' )
addsym ( true , false , optarg ) ;
else
usage ( ) ;
break ;
case ' D ' : /* define a symbol */
addsym ( false , true , optarg ) ;
break ;
case ' U ' : /* undef a symbol */
addsym ( false , false , optarg ) ;
break ;
2011-01-18 23:12:49 +03:00
case ' I ' : /* no-op for compatibility with cpp */
2009-11-27 18:50:30 +03:00
break ;
case ' b ' : /* blank deleted lines instead of omitting them */
case ' l ' : /* backwards compatibility */
lnblank = true ;
break ;
2011-01-18 23:12:49 +03:00
case ' B ' : /* compress blank lines around removed section */
compblank = true ;
break ;
2006-07-23 22:39:59 +04:00
case ' c ' : /* treat -D as -U and vice versa */
complement = true ;
break ;
case ' d ' :
debugging = true ;
break ;
case ' e ' : /* fewer errors from dodgy lines */
iocccok = true ;
break ;
2009-11-27 18:50:30 +03:00
case ' K ' : /* keep ambiguous #ifs */
strictlogic = true ;
break ;
2006-07-23 22:39:59 +04:00
case ' k ' : /* process constant #ifs */
killconsts = true ;
break ;
case ' n ' : /* add #line directive after deleted lines */
lnnum = true ;
break ;
2011-01-18 23:12:49 +03:00
case ' o ' : /* output to a file */
ofilename = optarg ;
break ;
2006-07-23 22:39:59 +04:00
case ' s ' : /* only output list of symbols that control #ifs */
symlist = true ;
break ;
2011-01-18 23:12:49 +03:00
case ' S ' : /* list symbols with their nesting depth */
symlist = symdepth = true ;
break ;
2006-07-23 22:39:59 +04:00
case ' t ' : /* don't parse C comments */
text = true ;
break ;
2011-01-18 23:12:49 +03:00
case ' V ' : /* print version */
version ( ) ;
2006-07-23 22:39:59 +04:00
default :
usage ( ) ;
}
argc - = optind ;
argv + = optind ;
2009-11-27 18:50:30 +03:00
if ( compblank & & lnblank )
errx ( 2 , " -B and -b are mutually exclusive " ) ;
2006-07-23 22:39:59 +04:00
if ( argc > 1 ) {
errx ( 2 , " can only do one file " ) ;
} else if ( argc = = 1 & & strcmp ( * argv , " - " ) ! = 0 ) {
filename = * argv ;
2011-01-18 23:12:49 +03:00
input = fopen ( filename , " rb " ) ;
2006-07-23 22:39:59 +04:00
if ( input = = NULL )
err ( 2 , " can't open %s " , filename ) ;
} else {
filename = " [stdin] " ;
input = stdin ;
}
2011-01-18 23:12:49 +03:00
if ( ofilename = = NULL ) {
ofilename = " [stdout] " ;
output = stdout ;
} else {
struct stat ist , ost ;
if ( stat ( ofilename , & ost ) = = 0 & &
fstat ( fileno ( input ) , & ist ) = = 0 )
overwriting = ( ist . st_dev = = ost . st_dev
& & ist . st_ino = = ost . st_ino ) ;
if ( overwriting ) {
const char * dirsep ;
int ofd ;
dirsep = strrchr ( ofilename , ' / ' ) ;
if ( dirsep ! = NULL )
snprintf ( tempname , sizeof ( tempname ) ,
" %.*s/ " TEMPLATE ,
( int ) ( dirsep - ofilename ) , ofilename ) ;
else
snprintf ( tempname , sizeof ( tempname ) ,
TEMPLATE ) ;
ofd = mkstemp ( tempname ) ;
if ( ofd ! = - 1 )
output = fdopen ( ofd , " wb+ " ) ;
if ( output = = NULL )
err ( 2 , " can't create temporary file " ) ;
fchmod ( ofd , ist . st_mode & ( S_IRWXU | S_IRWXG | S_IRWXO ) ) ;
} else {
output = fopen ( ofilename , " wb " ) ;
if ( output = = NULL )
err ( 2 , " can't open %s " , ofilename ) ;
}
}
2006-07-23 22:39:59 +04:00
process ( ) ;
abort ( ) ; /* bug */
}
2011-01-18 23:12:49 +03:00
static void
version ( void )
{
const char * c = copyright ;
for ( ; ; ) {
while ( * + + c ! = ' $ ' )
if ( * c = = ' \0 ' )
exit ( 0 ) ;
while ( * + + c ! = ' $ ' )
putc ( * c , stderr ) ;
putc ( ' \n ' , stderr ) ;
}
}
2006-07-23 22:39:59 +04:00
static void
usage ( void )
{
2011-01-18 23:12:49 +03:00
fprintf ( stderr , " usage: unifdef [-bBcdeKknsStV] [-Ipath] "
2006-07-23 22:39:59 +04:00
" [-Dsym[=val]] [-Usym] [-iDsym[=val]] [-iUsym] ... [file] \n " ) ;
exit ( 2 ) ;
}
/*
* A state transition function alters the global # if processing state
* in a particular way . The table below is indexed by the current
* processing state and the type of the current line .
*
* Nesting is handled by keeping a stack of states ; some transition
* functions increase or decrease the depth . They also maintain the
* ignore state on a stack . In some complicated cases they have to
* alter the preprocessor directive , as follows .
*
* When we have processed a group that starts off with a known - false
* # if / # elif sequence ( which has therefore been deleted ) followed by a
* # elif that we don ' t understand and therefore must keep , we edit the
2018-12-01 01:45:01 +03:00
* latter into a # if to keep the nesting correct . We use memcpy ( ) to
2011-01-18 23:12:49 +03:00
* overwrite the 4 byte token " elif " with " if " without a ' \0 ' byte .
2006-07-23 22:39:59 +04:00
*
* When we find a true # elif in a group , the following block will
* always be kept and the rest of the sequence after the next # elif or
* # else will be discarded . We edit the # elif into a # else and the
* following directive to # endif since this has the desired behaviour .
*
* " Dodgy " directives are split across multiple lines , the most common
* example being a multi - line comment hanging off the right of the
* directive . We can handle them correctly only if there is no change
* from printing to dropping ( or vice versa ) caused by that directive .
* If the directive is the first of a group we have a choice between
* failing with an error , or passing it through unchanged instead of
* evaluating it . The latter is not the default to avoid questions from
* users about unifdef unexpectedly leaving behind preprocessor directives .
*/
typedef void state_fn ( void ) ;
/* report an error */
static void Eelif ( void ) { error ( " Inappropriate #elif " ) ; }
static void Eelse ( void ) { error ( " Inappropriate #else " ) ; }
static void Eendif ( void ) { error ( " Inappropriate #endif " ) ; }
static void Eeof ( void ) { error ( " Premature EOF " ) ; }
static void Eioccc ( void ) { error ( " Obfuscated preprocessor control line " ) ; }
/* plain line handling */
static void print ( void ) { flushline ( true ) ; }
static void drop ( void ) { flushline ( false ) ; }
/* output lacks group's start line */
static void Strue ( void ) { drop ( ) ; ignoreoff ( ) ; state ( IS_TRUE_PREFIX ) ; }
static void Sfalse ( void ) { drop ( ) ; ignoreoff ( ) ; state ( IS_FALSE_PREFIX ) ; }
static void Selse ( void ) { drop ( ) ; state ( IS_TRUE_ELSE ) ; }
/* print/pass this block */
static void Pelif ( void ) { print ( ) ; ignoreoff ( ) ; state ( IS_PASS_MIDDLE ) ; }
static void Pelse ( void ) { print ( ) ; state ( IS_PASS_ELSE ) ; }
static void Pendif ( void ) { print ( ) ; unnest ( ) ; }
/* discard this block */
static void Dfalse ( void ) { drop ( ) ; ignoreoff ( ) ; state ( IS_FALSE_TRAILER ) ; }
static void Delif ( void ) { drop ( ) ; ignoreoff ( ) ; state ( IS_FALSE_MIDDLE ) ; }
static void Delse ( void ) { drop ( ) ; state ( IS_FALSE_ELSE ) ; }
static void Dendif ( void ) { drop ( ) ; unnest ( ) ; }
/* first line of group */
static void Fdrop ( void ) { nest ( ) ; Dfalse ( ) ; }
static void Fpass ( void ) { nest ( ) ; Pelif ( ) ; }
static void Ftrue ( void ) { nest ( ) ; Strue ( ) ; }
static void Ffalse ( void ) { nest ( ) ; Sfalse ( ) ; }
/* variable pedantry for obfuscated lines */
static void Oiffy ( void ) { if ( ! iocccok ) Eioccc ( ) ; Fpass ( ) ; ignoreon ( ) ; }
static void Oif ( void ) { if ( ! iocccok ) Eioccc ( ) ; Fpass ( ) ; }
static void Oelif ( void ) { if ( ! iocccok ) Eioccc ( ) ; Pelif ( ) ; }
/* ignore comments in this block */
static void Idrop ( void ) { Fdrop ( ) ; ignoreon ( ) ; }
static void Itrue ( void ) { Ftrue ( ) ; ignoreon ( ) ; }
static void Ifalse ( void ) { Ffalse ( ) ; ignoreon ( ) ; }
2011-01-18 23:12:49 +03:00
/* modify this line */
2018-12-01 01:45:01 +03:00
static void Mpass ( void ) { memcpy ( keyword , " if " , 4 ) ; Pelif ( ) ; }
2011-01-18 23:12:49 +03:00
static void Mtrue ( void ) { keywordedit ( " else " ) ; state ( IS_TRUE_MIDDLE ) ; }
static void Melif ( void ) { keywordedit ( " endif " ) ; state ( IS_FALSE_TRAILER ) ; }
static void Melse ( void ) { keywordedit ( " endif " ) ; state ( IS_FALSE_ELSE ) ; }
2006-07-23 22:39:59 +04:00
static state_fn * const trans_table [ IS_COUNT ] [ LT_COUNT ] = {
/* IS_OUTSIDE */
{ Itrue , Ifalse , Fpass , Ftrue , Ffalse , Eelif , Eelif , Eelif , Eelse , Eendif ,
Oiffy , Oiffy , Fpass , Oif , Oif , Eelif , Eelif , Eelif , Eelse , Eendif ,
2009-11-27 18:50:30 +03:00
print , done , abort } ,
2006-07-23 22:39:59 +04:00
/* IS_FALSE_PREFIX */
{ Idrop , Idrop , Fdrop , Fdrop , Fdrop , Mpass , Strue , Sfalse , Selse , Dendif ,
Idrop , Idrop , Fdrop , Fdrop , Fdrop , Mpass , Eioccc , Eioccc , Eioccc , Eioccc ,
2009-11-27 18:50:30 +03:00
drop , Eeof , abort } ,
2006-07-23 22:39:59 +04:00
/* IS_TRUE_PREFIX */
{ Itrue , Ifalse , Fpass , Ftrue , Ffalse , Dfalse , Dfalse , Dfalse , Delse , Dendif ,
Oiffy , Oiffy , Fpass , Oif , Oif , Eioccc , Eioccc , Eioccc , Eioccc , Eioccc ,
2009-11-27 18:50:30 +03:00
print , Eeof , abort } ,
2006-07-23 22:39:59 +04:00
/* IS_PASS_MIDDLE */
{ Itrue , Ifalse , Fpass , Ftrue , Ffalse , Pelif , Mtrue , Delif , Pelse , Pendif ,
Oiffy , Oiffy , Fpass , Oif , Oif , Pelif , Oelif , Oelif , Pelse , Pendif ,
2009-11-27 18:50:30 +03:00
print , Eeof , abort } ,
2006-07-23 22:39:59 +04:00
/* IS_FALSE_MIDDLE */
{ Idrop , Idrop , Fdrop , Fdrop , Fdrop , Pelif , Mtrue , Delif , Pelse , Pendif ,
Idrop , Idrop , Fdrop , Fdrop , Fdrop , Eioccc , Eioccc , Eioccc , Eioccc , Eioccc ,
2009-11-27 18:50:30 +03:00
drop , Eeof , abort } ,
2006-07-23 22:39:59 +04:00
/* IS_TRUE_MIDDLE */
{ Itrue , Ifalse , Fpass , Ftrue , Ffalse , Melif , Melif , Melif , Melse , Pendif ,
Oiffy , Oiffy , Fpass , Oif , Oif , Eioccc , Eioccc , Eioccc , Eioccc , Pendif ,
2009-11-27 18:50:30 +03:00
print , Eeof , abort } ,
2006-07-23 22:39:59 +04:00
/* IS_PASS_ELSE */
{ Itrue , Ifalse , Fpass , Ftrue , Ffalse , Eelif , Eelif , Eelif , Eelse , Pendif ,
Oiffy , Oiffy , Fpass , Oif , Oif , Eelif , Eelif , Eelif , Eelse , Pendif ,
2009-11-27 18:50:30 +03:00
print , Eeof , abort } ,
2006-07-23 22:39:59 +04:00
/* IS_FALSE_ELSE */
{ Idrop , Idrop , Fdrop , Fdrop , Fdrop , Eelif , Eelif , Eelif , Eelse , Dendif ,
Idrop , Idrop , Fdrop , Fdrop , Fdrop , Eelif , Eelif , Eelif , Eelse , Eioccc ,
2009-11-27 18:50:30 +03:00
drop , Eeof , abort } ,
2006-07-23 22:39:59 +04:00
/* IS_TRUE_ELSE */
{ Itrue , Ifalse , Fpass , Ftrue , Ffalse , Eelif , Eelif , Eelif , Eelse , Dendif ,
Oiffy , Oiffy , Fpass , Oif , Oif , Eelif , Eelif , Eelif , Eelse , Eioccc ,
2009-11-27 18:50:30 +03:00
print , Eeof , abort } ,
2006-07-23 22:39:59 +04:00
/* IS_FALSE_TRAILER */
{ Idrop , Idrop , Fdrop , Fdrop , Fdrop , Dfalse , Dfalse , Dfalse , Delse , Dendif ,
Idrop , Idrop , Fdrop , Fdrop , Fdrop , Dfalse , Dfalse , Dfalse , Delse , Eioccc ,
2009-11-27 18:50:30 +03:00
drop , Eeof , abort }
2006-07-23 22:39:59 +04:00
/*TRUEI FALSEI IF TRUE FALSE ELIF ELTRUE ELFALSE ELSE ENDIF
TRUEI FALSEI IF TRUE FALSE ELIF ELTRUE ELFALSE ELSE ENDIF ( DODGY )
2009-11-27 18:50:30 +03:00
PLAIN EOF ERROR */
2006-07-23 22:39:59 +04:00
} ;
/*
* State machine utility functions
*/
static void
ignoreoff ( void )
{
if ( depth = = 0 )
abort ( ) ; /* bug */
ignoring [ depth ] = ignoring [ depth - 1 ] ;
}
static void
ignoreon ( void )
{
ignoring [ depth ] = true ;
}
static void
keywordedit ( const char * replacement )
{
2011-01-18 23:12:49 +03:00
snprintf ( keyword , tline + sizeof ( tline ) - keyword ,
" %s%s " , replacement , newline ) ;
2006-07-23 22:39:59 +04:00
print ( ) ;
}
static void
nest ( void )
{
2009-11-27 18:50:30 +03:00
if ( depth > MAXDEPTH - 1 )
abort ( ) ; /* bug */
if ( depth = = MAXDEPTH - 1 )
2006-07-23 22:39:59 +04:00
error ( " Too many levels of nesting " ) ;
2009-11-27 18:50:30 +03:00
depth + = 1 ;
2006-07-23 22:39:59 +04:00
stifline [ depth ] = linenum ;
}
static void
unnest ( void )
{
if ( depth = = 0 )
abort ( ) ; /* bug */
depth - = 1 ;
}
static void
state ( Ifstate is )
{
ifstate [ depth ] = is ;
}
/*
* Write a line to the output or not , according to command line options .
*/
static void
flushline ( bool keep )
{
if ( symlist )
return ;
if ( keep ^ complement ) {
2011-01-18 23:12:49 +03:00
bool blankline = tline [ strspn ( tline , " \t \r \n " ) ] = = ' \0 ' ;
2009-11-27 18:50:30 +03:00
if ( blankline & & compblank & & blankcount ! = blankmax ) {
delcount + = 1 ;
blankcount + = 1 ;
} else {
if ( lnnum & & delcount > 0 )
2011-01-18 23:12:49 +03:00
printf ( " #line %d%s " , linenum , newline ) ;
fputs ( tline , output ) ;
2009-11-27 18:50:30 +03:00
delcount = 0 ;
blankmax = blankcount = blankline ? blankcount + 1 : 0 ;
}
2006-07-23 22:39:59 +04:00
} else {
if ( lnblank )
2011-01-18 23:12:49 +03:00
fputs ( newline , output ) ;
2006-07-23 22:39:59 +04:00
exitstat = 1 ;
delcount + = 1 ;
2009-11-27 18:50:30 +03:00
blankcount = 0 ;
2006-07-23 22:39:59 +04:00
}
2011-01-18 23:12:49 +03:00
if ( debugging )
fflush ( output ) ;
2006-07-23 22:39:59 +04:00
}
/*
* The driver for the state machine .
*/
static void
process ( void )
{
2009-11-27 18:50:30 +03:00
/* When compressing blank lines, act as if the file
is preceded by a large number of blank lines . */
blankmax = blankcount = 1000 ;
2006-07-23 22:39:59 +04:00
for ( ; ; ) {
2011-01-18 23:12:49 +03:00
Linetype lineval = parseline ( ) ;
2006-07-23 22:39:59 +04:00
trans_table [ ifstate [ depth ] ] [ lineval ] ( ) ;
2011-01-18 23:12:49 +03:00
debug ( " process line %d %s -> %s depth %d " ,
linenum , linetype_name [ lineval ] ,
2006-07-23 22:39:59 +04:00
ifstate_name [ ifstate [ depth ] ] , depth ) ;
}
}
2011-01-18 23:12:49 +03:00
/*
* Flush the output and handle errors .
*/
static void
closeout ( void )
{
if ( symdepth & & ! zerosyms )
printf ( " \n " ) ;
if ( fclose ( output ) = = EOF ) {
warn ( " couldn't write to %s " , ofilename ) ;
if ( overwriting ) {
unlink ( tempname ) ;
errx ( 2 , " %s unchanged " , filename ) ;
} else {
exit ( 2 ) ;
}
}
}
/*
* Clean up and exit .
*/
static void
done ( void )
{
if ( incomment )
error ( " EOF in comment " ) ;
closeout ( ) ;
if ( overwriting & & rename ( tempname , ofilename ) = = - 1 ) {
warn ( " couldn't rename temporary file " ) ;
unlink ( tempname ) ;
errx ( 2 , " %s unchanged " , ofilename ) ;
}
exit ( exitstat ) ;
}
2006-07-23 22:39:59 +04:00
/*
* Parse a line and determine its type . We keep the preprocessor line
* parser state between calls in the global variable linestate , with
* help from skipcomment ( ) .
*/
static Linetype
2009-11-27 18:50:30 +03:00
parseline ( void )
2006-07-23 22:39:59 +04:00
{
const char * cp ;
int cursym ;
int kwlen ;
Linetype retval ;
Comment_state wascomment ;
2011-01-18 23:12:49 +03:00
linenum + + ;
2006-07-23 22:39:59 +04:00
if ( fgets ( tline , MAXLINE , input ) = = NULL )
return ( LT_EOF ) ;
2011-01-18 23:12:49 +03:00
if ( newline = = NULL ) {
if ( strrchr ( tline , ' \n ' ) = = strrchr ( tline , ' \r ' ) + 1 )
newline = newline_crlf ;
else
newline = newline_unix ;
}
2006-07-23 22:39:59 +04:00
retval = LT_PLAIN ;
wascomment = incomment ;
cp = skipcomment ( tline ) ;
if ( linestate = = LS_START ) {
if ( * cp = = ' # ' ) {
linestate = LS_HASH ;
2011-01-18 23:12:49 +03:00
firstsym = true ;
2006-07-23 22:39:59 +04:00
cp = skipcomment ( cp + 1 ) ;
} else if ( * cp ! = ' \0 ' )
linestate = LS_DIRTY ;
}
if ( ! incomment & & linestate = = LS_HASH ) {
keyword = tline + ( cp - tline ) ;
cp = skipsym ( cp ) ;
kwlen = cp - keyword ;
/* no way can we deal with a continuation inside a keyword */
2011-01-18 23:12:49 +03:00
if ( strncmp ( cp , " \\ \r \n " , 3 ) = = 0 | |
strncmp ( cp , " \\ \n " , 2 ) = = 0 )
2006-07-23 22:39:59 +04:00
Eioccc ( ) ;
if ( strlcmp ( " ifdef " , keyword , kwlen ) = = 0 | |
strlcmp ( " ifndef " , keyword , kwlen ) = = 0 ) {
cp = skipcomment ( cp ) ;
if ( ( cursym = findsym ( cp ) ) < 0 )
retval = LT_IF ;
else {
retval = ( keyword [ 2 ] = = ' n ' )
? LT_FALSE : LT_TRUE ;
if ( value [ cursym ] = = NULL )
retval = ( retval = = LT_TRUE )
? LT_FALSE : LT_TRUE ;
if ( ignore [ cursym ] )
retval = ( retval = = LT_TRUE )
? LT_TRUEI : LT_FALSEI ;
}
cp = skipsym ( cp ) ;
} else if ( strlcmp ( " if " , keyword , kwlen ) = = 0 )
retval = ifeval ( & cp ) ;
else if ( strlcmp ( " elif " , keyword , kwlen ) = = 0 )
retval = ifeval ( & cp ) - LT_IF + LT_ELIF ;
else if ( strlcmp ( " else " , keyword , kwlen ) = = 0 )
retval = LT_ELSE ;
else if ( strlcmp ( " endif " , keyword , kwlen ) = = 0 )
retval = LT_ENDIF ;
else {
linestate = LS_DIRTY ;
retval = LT_PLAIN ;
}
cp = skipcomment ( cp ) ;
if ( * cp ! = ' \0 ' ) {
linestate = LS_DIRTY ;
if ( retval = = LT_TRUE | | retval = = LT_FALSE | |
retval = = LT_TRUEI | | retval = = LT_FALSEI )
retval = LT_IF ;
if ( retval = = LT_ELTRUE | | retval = = LT_ELFALSE )
retval = LT_ELIF ;
}
if ( retval ! = LT_PLAIN & & ( wascomment | | incomment ) ) {
retval + = LT_DODGY ;
if ( incomment )
linestate = LS_DIRTY ;
}
2009-11-27 18:50:30 +03:00
/* skipcomment normally changes the state, except
if the last line of the file lacks a newline , or
if there is too much whitespace in a directive */
if ( linestate = = LS_HASH ) {
size_t len = cp - tline ;
if ( fgets ( tline + len , MAXLINE - len , input ) = = NULL ) {
/* append the missing newline */
2011-01-18 23:12:49 +03:00
strcpy ( tline + len , newline ) ;
cp + = strlen ( newline ) ;
2009-11-27 18:50:30 +03:00
linestate = LS_START ;
} else {
linestate = LS_DIRTY ;
}
}
2006-07-23 22:39:59 +04:00
}
if ( linestate = = LS_DIRTY ) {
while ( * cp ! = ' \0 ' )
cp = skipcomment ( cp + 1 ) ;
}
2011-01-18 23:12:49 +03:00
debug ( " parser line %d state %s comment %s line " , linenum ,
2006-07-23 22:39:59 +04:00
comment_name [ incomment ] , linestate_name [ linestate ] ) ;
return ( retval ) ;
}
/*
* These are the binary operators that are supported by the expression
2009-11-27 18:50:30 +03:00
* evaluator .
2006-07-23 22:39:59 +04:00
*/
2009-11-27 18:50:30 +03:00
static Linetype op_strict ( int * p , int v , Linetype at , Linetype bt ) {
if ( at = = LT_IF | | bt = = LT_IF ) return ( LT_IF ) ;
return ( * p = v , v ? LT_TRUE : LT_FALSE ) ;
}
static Linetype op_lt ( int * p , Linetype at , int a , Linetype bt , int b ) {
return op_strict ( p , a < b , at , bt ) ;
}
static Linetype op_gt ( int * p , Linetype at , int a , Linetype bt , int b ) {
return op_strict ( p , a > b , at , bt ) ;
}
static Linetype op_le ( int * p , Linetype at , int a , Linetype bt , int b ) {
return op_strict ( p , a < = b , at , bt ) ;
}
static Linetype op_ge ( int * p , Linetype at , int a , Linetype bt , int b ) {
return op_strict ( p , a > = b , at , bt ) ;
}
static Linetype op_eq ( int * p , Linetype at , int a , Linetype bt , int b ) {
return op_strict ( p , a = = b , at , bt ) ;
}
static Linetype op_ne ( int * p , Linetype at , int a , Linetype bt , int b ) {
return op_strict ( p , a ! = b , at , bt ) ;
}
static Linetype op_or ( int * p , Linetype at , int a , Linetype bt , int b ) {
if ( ! strictlogic & & ( at = = LT_TRUE | | bt = = LT_TRUE ) )
return ( * p = 1 , LT_TRUE ) ;
return op_strict ( p , a | | b , at , bt ) ;
}
static Linetype op_and ( int * p , Linetype at , int a , Linetype bt , int b ) {
if ( ! strictlogic & & ( at = = LT_FALSE | | bt = = LT_FALSE ) )
return ( * p = 0 , LT_FALSE ) ;
return op_strict ( p , a & & b , at , bt ) ;
}
2006-07-23 22:39:59 +04:00
/*
* An evaluation function takes three arguments , as follows : ( 1 ) a pointer to
* an element of the precedence table which lists the operators at the current
* level of precedence ; ( 2 ) a pointer to an integer which will receive the
* value of the expression ; and ( 3 ) a pointer to a char * that points to the
* expression to be evaluated and that is updated to the end of the expression
* when evaluation is complete . The function returns LT_FALSE if the value of
2009-11-27 18:50:30 +03:00
* the expression is zero , LT_TRUE if it is non - zero , LT_IF if the expression
* depends on an unknown symbol , or LT_ERROR if there is a parse failure .
2006-07-23 22:39:59 +04:00
*/
struct ops ;
typedef Linetype eval_fn ( const struct ops * , int * , const char * * ) ;
static eval_fn eval_table , eval_unary ;
/*
* The precedence table . Expressions involving binary operators are evaluated
* in a table - driven way by eval_table . When it evaluates a subexpression it
* calls the inner function with its first argument pointing to the next
* element of the table . Innermost expressions have special non - table - driven
* handling .
*/
static const struct ops {
eval_fn * inner ;
struct op {
const char * str ;
2009-11-27 18:50:30 +03:00
Linetype ( * fn ) ( int * , Linetype , int , Linetype , int ) ;
2006-07-23 22:39:59 +04:00
} op [ 5 ] ;
} eval_ops [ ] = {
{ eval_table , { { " || " , op_or } } } ,
{ eval_table , { { " && " , op_and } } } ,
{ eval_table , { { " == " , op_eq } ,
{ " != " , op_ne } } } ,
{ eval_unary , { { " <= " , op_le } ,
{ " >= " , op_ge } ,
{ " < " , op_lt } ,
{ " > " , op_gt } } }
} ;
/*
* Function for evaluating the innermost parts of expressions ,
2009-11-27 18:50:30 +03:00
* viz . ! expr ( expr ) number defined ( symbol ) symbol
* We reset the constexpr flag in the last two cases .
2006-07-23 22:39:59 +04:00
*/
static Linetype
eval_unary ( const struct ops * ops , int * valp , const char * * cpp )
{
const char * cp ;
char * ep ;
int sym ;
2009-11-27 18:50:30 +03:00
bool defparen ;
Linetype lt ;
2006-07-23 22:39:59 +04:00
cp = skipcomment ( * cpp ) ;
if ( * cp = = ' ! ' ) {
debug ( " eval%d ! " , ops - eval_ops ) ;
cp + + ;
2009-11-27 18:50:30 +03:00
lt = eval_unary ( ops , valp , & cp ) ;
if ( lt = = LT_ERROR )
return ( LT_ERROR ) ;
if ( lt ! = LT_IF ) {
* valp = ! * valp ;
lt = * valp ? LT_TRUE : LT_FALSE ;
2009-06-07 01:47:11 +04:00
}
2006-07-23 22:39:59 +04:00
} else if ( * cp = = ' ( ' ) {
cp + + ;
debug ( " eval%d ( " , ops - eval_ops ) ;
2009-11-27 18:50:30 +03:00
lt = eval_table ( eval_ops , valp , & cp ) ;
if ( lt = = LT_ERROR )
return ( LT_ERROR ) ;
2006-07-23 22:39:59 +04:00
cp = skipcomment ( cp ) ;
if ( * cp + + ! = ' ) ' )
2009-11-27 18:50:30 +03:00
return ( LT_ERROR ) ;
2006-07-23 22:39:59 +04:00
} else if ( isdigit ( ( unsigned char ) * cp ) ) {
debug ( " eval%d number " , ops - eval_ops ) ;
* valp = strtol ( cp , & ep , 0 ) ;
2009-11-27 18:50:30 +03:00
if ( ep = = cp )
return ( LT_ERROR ) ;
lt = * valp ? LT_TRUE : LT_FALSE ;
2006-07-23 22:39:59 +04:00
cp = skipsym ( cp ) ;
} else if ( strncmp ( cp , " defined " , 7 ) = = 0 & & endsym ( cp [ 7 ] ) ) {
cp = skipcomment ( cp + 7 ) ;
debug ( " eval%d defined " , ops - eval_ops ) ;
2009-11-27 18:50:30 +03:00
if ( * cp = = ' ( ' ) {
cp = skipcomment ( cp + 1 ) ;
defparen = true ;
} else {
defparen = false ;
}
2006-07-23 22:39:59 +04:00
sym = findsym ( cp ) ;
2009-11-27 18:50:30 +03:00
if ( sym < 0 ) {
lt = LT_IF ;
} else {
2009-06-07 01:47:11 +04:00
* valp = ( value [ sym ] ! = NULL ) ;
2009-11-27 18:50:30 +03:00
lt = * valp ? LT_TRUE : LT_FALSE ;
2009-06-07 01:47:11 +04:00
}
2009-11-27 18:50:30 +03:00
cp = skipsym ( cp ) ;
cp = skipcomment ( cp ) ;
if ( defparen & & * cp + + ! = ' ) ' )
return ( LT_ERROR ) ;
constexpr = false ;
2006-07-23 22:39:59 +04:00
} else if ( ! endsym ( * cp ) ) {
debug ( " eval%d symbol " , ops - eval_ops ) ;
sym = findsym ( cp ) ;
2009-11-27 18:50:30 +03:00
cp = skipsym ( cp ) ;
if ( sym < 0 ) {
lt = LT_IF ;
cp = skipargs ( cp ) ;
} else if ( value [ sym ] = = NULL ) {
2006-07-23 22:39:59 +04:00
* valp = 0 ;
2009-11-27 18:50:30 +03:00
lt = LT_FALSE ;
} else {
2006-07-23 22:39:59 +04:00
* valp = strtol ( value [ sym ] , & ep , 0 ) ;
if ( * ep ! = ' \0 ' | | ep = = value [ sym ] )
2009-11-27 18:50:30 +03:00
return ( LT_ERROR ) ;
lt = * valp ? LT_TRUE : LT_FALSE ;
cp = skipargs ( cp ) ;
2006-07-23 22:39:59 +04:00
}
2009-11-27 18:50:30 +03:00
constexpr = false ;
2006-07-23 22:39:59 +04:00
} else {
debug ( " eval%d bad expr " , ops - eval_ops ) ;
2009-11-27 18:50:30 +03:00
return ( LT_ERROR ) ;
2006-07-23 22:39:59 +04:00
}
* cpp = cp ;
debug ( " eval%d = %d " , ops - eval_ops , * valp ) ;
2009-11-27 18:50:30 +03:00
return ( lt ) ;
2006-07-23 22:39:59 +04:00
}
/*
* Table - driven evaluation of binary operators .
*/
static Linetype
eval_table ( const struct ops * ops , int * valp , const char * * cpp )
{
const struct op * op ;
const char * cp ;
int val ;
2009-11-27 18:50:30 +03:00
Linetype lt , rt ;
2006-07-23 22:39:59 +04:00
debug ( " eval%d " , ops - eval_ops ) ;
cp = * cpp ;
2009-11-27 18:50:30 +03:00
lt = ops - > inner ( ops + 1 , valp , & cp ) ;
if ( lt = = LT_ERROR )
return ( LT_ERROR ) ;
2006-07-23 22:39:59 +04:00
for ( ; ; ) {
cp = skipcomment ( cp ) ;
for ( op = ops - > op ; op - > str ! = NULL ; op + + )
if ( strncmp ( cp , op - > str , strlen ( op - > str ) ) = = 0 )
break ;
if ( op - > str = = NULL )
break ;
cp + = strlen ( op - > str ) ;
debug ( " eval%d %s " , ops - eval_ops , op - > str ) ;
2009-11-27 18:50:30 +03:00
rt = ops - > inner ( ops + 1 , & val , & cp ) ;
if ( rt = = LT_ERROR )
return ( LT_ERROR ) ;
lt = op - > fn ( valp , lt , * valp , rt , val ) ;
2006-07-23 22:39:59 +04:00
}
* cpp = cp ;
debug ( " eval%d = %d " , ops - eval_ops , * valp ) ;
2009-11-27 18:50:30 +03:00
debug ( " eval%d lt = %s " , ops - eval_ops , linetype_name [ lt ] ) ;
return ( lt ) ;
2006-07-23 22:39:59 +04:00
}
/*
* Evaluate the expression on a # if or # elif line . If we can work out
* the result we return LT_TRUE or LT_FALSE accordingly , otherwise we
* return just a generic LT_IF .
*/
static Linetype
ifeval ( const char * * cpp )
{
int ret ;
2009-11-27 18:50:30 +03:00
int val = 0 ;
2006-07-23 22:39:59 +04:00
debug ( " eval %s " , * cpp ) ;
2009-11-27 18:50:30 +03:00
constexpr = killconsts ? false : true ;
ret = eval_table ( eval_ops , & val , cpp ) ;
2006-07-23 22:39:59 +04:00
debug ( " eval = %d " , val ) ;
2009-11-27 18:50:30 +03:00
return ( constexpr ? LT_IF : ret = = LT_ERROR ? LT_IF : ret ) ;
2006-07-23 22:39:59 +04:00
}
/*
* Skip over comments , strings , and character literals and stop at the
* next character position that is not whitespace . Between calls we keep
* the comment state in the global variable incomment , and we also adjust
* the global variable linestate when we see a newline .
* XXX : doesn ' t cope with the buffer splitting inside a state transition .
*/
static const char *
skipcomment ( const char * cp )
{
if ( text | | ignoring [ depth ] ) {
for ( ; isspace ( ( unsigned char ) * cp ) ; cp + + )
if ( * cp = = ' \n ' )
linestate = LS_START ;
return ( cp ) ;
}
while ( * cp ! = ' \0 ' )
/* don't reset to LS_START after a line continuation */
2011-01-18 23:12:49 +03:00
if ( strncmp ( cp , " \\ \r \n " , 3 ) = = 0 )
cp + = 3 ;
else if ( strncmp ( cp , " \\ \n " , 2 ) = = 0 )
2006-07-23 22:39:59 +04:00
cp + = 2 ;
else switch ( incomment ) {
case NO_COMMENT :
2011-01-18 23:12:49 +03:00
if ( strncmp ( cp , " / \\ \r \n " , 4 ) = = 0 ) {
incomment = STARTING_COMMENT ;
cp + = 4 ;
} else if ( strncmp ( cp , " / \\ \n " , 3 ) = = 0 ) {
2006-07-23 22:39:59 +04:00
incomment = STARTING_COMMENT ;
cp + = 3 ;
} else if ( strncmp ( cp , " /* " , 2 ) = = 0 ) {
incomment = C_COMMENT ;
cp + = 2 ;
} else if ( strncmp ( cp , " // " , 2 ) = = 0 ) {
incomment = CXX_COMMENT ;
cp + = 2 ;
} else if ( strncmp ( cp , " \' " , 1 ) = = 0 ) {
incomment = CHAR_LITERAL ;
linestate = LS_DIRTY ;
cp + = 1 ;
} else if ( strncmp ( cp , " \" " , 1 ) = = 0 ) {
incomment = STRING_LITERAL ;
linestate = LS_DIRTY ;
cp + = 1 ;
} else if ( strncmp ( cp , " \n " , 1 ) = = 0 ) {
linestate = LS_START ;
cp + = 1 ;
2011-01-18 23:12:49 +03:00
} else if ( strchr ( " \r \t " , * cp ) ! = NULL ) {
2006-07-23 22:39:59 +04:00
cp + = 1 ;
} else
return ( cp ) ;
continue ;
case CXX_COMMENT :
if ( strncmp ( cp , " \n " , 1 ) = = 0 ) {
incomment = NO_COMMENT ;
linestate = LS_START ;
}
cp + = 1 ;
continue ;
case CHAR_LITERAL :
case STRING_LITERAL :
if ( ( incomment = = CHAR_LITERAL & & cp [ 0 ] = = ' \' ' ) | |
( incomment = = STRING_LITERAL & & cp [ 0 ] = = ' \" ' ) ) {
incomment = NO_COMMENT ;
cp + = 1 ;
} else if ( cp [ 0 ] = = ' \\ ' ) {
if ( cp [ 1 ] = = ' \0 ' )
cp + = 1 ;
else
cp + = 2 ;
} else if ( strncmp ( cp , " \n " , 1 ) = = 0 ) {
if ( incomment = = CHAR_LITERAL )
error ( " unterminated char literal " ) ;
else
error ( " unterminated string literal " ) ;
} else
cp + = 1 ;
continue ;
case C_COMMENT :
2011-01-18 23:12:49 +03:00
if ( strncmp ( cp , " * \\ \r \n " , 4 ) = = 0 ) {
incomment = FINISHING_COMMENT ;
cp + = 4 ;
} else if ( strncmp ( cp , " * \\ \n " , 3 ) = = 0 ) {
2006-07-23 22:39:59 +04:00
incomment = FINISHING_COMMENT ;
cp + = 3 ;
} else if ( strncmp ( cp , " */ " , 2 ) = = 0 ) {
incomment = NO_COMMENT ;
cp + = 2 ;
} else
cp + = 1 ;
continue ;
case STARTING_COMMENT :
if ( * cp = = ' * ' ) {
incomment = C_COMMENT ;
cp + = 1 ;
} else if ( * cp = = ' / ' ) {
incomment = CXX_COMMENT ;
cp + = 1 ;
} else {
incomment = NO_COMMENT ;
linestate = LS_DIRTY ;
}
continue ;
case FINISHING_COMMENT :
if ( * cp = = ' / ' ) {
incomment = NO_COMMENT ;
cp + = 1 ;
} else
incomment = C_COMMENT ;
continue ;
default :
abort ( ) ; /* bug */
}
return ( cp ) ;
}
2009-11-27 18:50:30 +03:00
/*
* Skip macro arguments .
*/
static const char *
skipargs ( const char * cp )
{
const char * ocp = cp ;
int level = 0 ;
cp = skipcomment ( cp ) ;
if ( * cp ! = ' ( ' )
return ( cp ) ;
do {
if ( * cp = = ' ( ' )
level + + ;
if ( * cp = = ' ) ' )
level - - ;
cp = skipcomment ( cp + 1 ) ;
} while ( level ! = 0 & & * cp ! = ' \0 ' ) ;
if ( level = = 0 )
return ( cp ) ;
else
/* Rewind and re-detect the syntax error later. */
return ( ocp ) ;
}
2006-07-23 22:39:59 +04:00
/*
* Skip over an identifier .
*/
static const char *
skipsym ( const char * cp )
{
while ( ! endsym ( * cp ) )
+ + cp ;
return ( cp ) ;
}
/*
2009-11-27 18:50:30 +03:00
* Look for the symbol in the symbol table . If it is found , we return
2006-07-23 22:39:59 +04:00
* the symbol table index , else we return - 1.
*/
static int
findsym ( const char * str )
{
const char * cp ;
int symind ;
cp = skipsym ( str ) ;
if ( cp = = str )
return ( - 1 ) ;
if ( symlist ) {
2011-01-18 23:12:49 +03:00
if ( symdepth & & firstsym )
printf ( " %s%3d " , zerosyms ? " " : " \n " , depth ) ;
firstsym = zerosyms = false ;
printf ( " %s%.*s%s " ,
symdepth ? " " : " " ,
( int ) ( cp - str ) , str ,
symdepth ? " " : " \n " ) ;
2006-07-23 22:39:59 +04:00
/* we don't care about the value of the symbol */
return ( 0 ) ;
}
for ( symind = 0 ; symind < nsyms ; + + symind ) {
if ( strlcmp ( symname [ symind ] , str , cp - str ) = = 0 ) {
debug ( " findsym %s %s " , symname [ symind ] ,
value [ symind ] ? value [ symind ] : " " ) ;
return ( symind ) ;
}
}
return ( - 1 ) ;
}
/*
* Add a symbol to the symbol table .
*/
static void
addsym ( bool ignorethis , bool definethis , char * sym )
{
int symind ;
char * val ;
symind = findsym ( sym ) ;
if ( symind < 0 ) {
if ( nsyms > = MAXSYMS )
errx ( 2 , " too many symbols " ) ;
symind = nsyms + + ;
}
symname [ symind ] = sym ;
ignore [ symind ] = ignorethis ;
val = sym + ( skipsym ( sym ) - sym ) ;
if ( definethis ) {
if ( * val = = ' = ' ) {
value [ symind ] = val + 1 ;
* val = ' \0 ' ;
} else if ( * val = = ' \0 ' )
2011-01-18 23:12:49 +03:00
value [ symind ] = " 1 " ;
2006-07-23 22:39:59 +04:00
else
usage ( ) ;
} else {
if ( * val ! = ' \0 ' )
usage ( ) ;
value [ symind ] = NULL ;
}
2011-01-18 23:12:49 +03:00
debug ( " addsym %s=%s " , symname [ symind ] ,
value [ symind ] ? value [ symind ] : " undef " ) ;
2006-07-23 22:39:59 +04:00
}
/*
* Compare s with n characters of t .
* The same as strncmp ( ) except that it checks that s [ n ] = = ' \0 ' .
*/
static int
strlcmp ( const char * s , const char * t , size_t n )
{
while ( n - - & & * t ! = ' \0 ' )
if ( * s ! = * t )
return ( ( unsigned char ) * s - ( unsigned char ) * t ) ;
else
+ + s , + + t ;
return ( ( unsigned char ) * s ) ;
}
/*
* Diagnostics .
*/
static void
debug ( const char * msg , . . . )
{
va_list ap ;
if ( debugging ) {
va_start ( ap , msg ) ;
vwarnx ( msg , ap ) ;
va_end ( ap ) ;
}
}
static void
error ( const char * msg )
{
if ( depth = = 0 )
warnx ( " %s: %d: %s " , filename , linenum , msg ) ;
else
warnx ( " %s: %d: %s (#if line %d depth %d) " ,
filename , linenum , msg , stifline [ depth ] , depth ) ;
2011-01-18 23:12:49 +03:00
closeout ( ) ;
2006-07-23 22:39:59 +04:00
errx ( 2 , " output may be truncated " ) ;
}