1225 lines · 34.8 KB · C
RawDownload
/* * Copyright (c) 2002 - 2011 Tony Finch <dot@dotat.at> * * 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. */ /* * unifdef - remove ifdef'ed lines * * 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. * *  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 * *   These require better buffer handling, which would also make *   it possible to handle all "dodgy" directives correctly. */ #include <sys/types.h>#include <sys/stat.h> #include <ctype.h>#include <err.h>#include <errno.h>#include <stdarg.h>#include <stdbool.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h> const char copyright[] =    "@(#) $Version: unifdef-2.5 $\n"    "@(#) $Author: Tony Finch (dot@dotat.at) $\n"    "@(#) $URL: http://dotat.at/prog/unifdef $\n"; /* 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 */	LT_ERROR,		/* unevaluable #if */	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",	"PLAIN", "EOF", "ERROR"}; /* 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 /* * For temporary filenames */#define TEMPLATE        "unifdef.XXXXXX" /* * Globals. */ static bool             compblank;		/* -B: compress blank lines */static bool             lnblank;		/* -b: blank deleted lines */static bool             complement;		/* -c: do the complement */static bool             debugging;		/* -d: debugging reports */static bool             iocccok;		/* -e: fewer IOCCC errors */static bool             strictlogic;		/* -K: keep ambiguous #ifs */static bool             killconsts;		/* -k: eval constant #ifs */static bool             lnnum;			/* -n: add #line directives */static bool             symlist;		/* -s: output symbol list */static bool             symdepth;		/* -S: output symbol depth */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 */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 */ static char             tline[MAXLINE+EDITSLOP];/* input buffer plus space */static char            *keyword;		/* used for editing #elif's */ static const char      *newline;		/* input file format */static const char       newline_unix[] = "\n";static const char       newline_crlf[] = "\r\n"; 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 */static unsigned         blankcount;		/* count of blank lines */static unsigned         blankmax;		/* maximum recent blankcount */static bool             constexpression;	/* constant #if expression */static bool             zerosyms = true;	/* to format symdepth output */static bool             firstsym;		/* ditto */ static int              exitstat;		/* program exit status */ static void             addsym(bool, bool, char *);static void             closeout(void);static void             debug(const char *, ...);static void             done(void);static void             error(const char *);static int              findsym(const char *);static void             flushline(bool);static Linetype         parseline(void);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);static const char      *skipargs(const char *);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);static void             version(void); #define endsym(c) (!isalnum((unsigned char)c) && c != '_') /* * The main program. */intmain(int argc, char *argv[]){	int opt; 	while ((opt = getopt(argc, argv, "i:D:U:I:o:bBcdeKklnsStV")) != -1)		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;		case 'I': /* no-op for compatibility with cpp */			break;		case 'b': /* blank deleted lines instead of omitting them */		case 'l': /* backwards compatibility */			lnblank = true;			break;		case 'B': /* compress blank lines around removed section */			compblank = true;			break;		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;		case 'K': /* keep ambiguous #ifs */			strictlogic = true;			break;		case 'k': /* process constant #ifs */			killconsts = true;			break;		case 'n': /* add #line directive after deleted lines */			lnnum = true;			break;		case 'o': /* output to a file */			ofilename = optarg;			break;		case 's': /* only output list of symbols that control #ifs */			symlist = true;			break;		case 'S': /* list symbols with their nesting depth */			symlist = symdepth = true;			break;		case 't': /* don't parse C comments */			text = true;			break;		case 'V': /* print version */			version();		default:			usage();		}	argc -= optind;	argv += optind;	if (compblank && lnblank)		errx(2, "-B and -b are mutually exclusive");	if (argc > 1) {		errx(2, "can only do one file");	} else if (argc == 1 && strcmp(*argv, "-") != 0) {		filename = *argv;		input = fopen(filename, "rb");		if (input == NULL)			err(2, "can't open %s", filename);	} else {		filename = "[stdin]";		input = stdin;	}	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);		}	}	process();	abort(); /* bug */} static voidversion(void){	const char *c = copyright;	for (;;) {		while (*++c != '$')			if (*c == '\0')				exit(0);		while (*++c != '$')			putc(*c, stderr);		putc('\n', stderr);	}} static voidusage(void){	fprintf(stderr, "usage: unifdef [-bBcdeKknsStV] [-Ipath]"	    " [-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 * latter into a #if to keep the nesting correct. We use memcpy() to * overwrite the 4 byte token "elif" with "if  " without a '\0' byte. * * 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(); }/* modify this line */static void Mpass (void) { memcpy(keyword, "if  ", 4); Pelif(); }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); } 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,  print, done,  abort },/* IS_FALSE_PREFIX */{ Idrop, Idrop, Fdrop, Fdrop, Fdrop, Mpass, Strue, Sfalse,Selse, Dendif,  Idrop, Idrop, Fdrop, Fdrop, Fdrop, Mpass, Eioccc,Eioccc,Eioccc,Eioccc,  drop,  Eeof,  abort },/* IS_TRUE_PREFIX */{ Itrue, Ifalse,Fpass, Ftrue, Ffalse,Dfalse,Dfalse,Dfalse,Delse, Dendif,  Oiffy, Oiffy, Fpass, Oif,   Oif,   Eioccc,Eioccc,Eioccc,Eioccc,Eioccc,  print, Eeof,  abort },/* IS_PASS_MIDDLE */{ Itrue, Ifalse,Fpass, Ftrue, Ffalse,Pelif, Mtrue, Delif, Pelse, Pendif,  Oiffy, Oiffy, Fpass, Oif,   Oif,   Pelif, Oelif, Oelif, Pelse, Pendif,  print, Eeof,  abort },/* IS_FALSE_MIDDLE */{ Idrop, Idrop, Fdrop, Fdrop, Fdrop, Pelif, Mtrue, Delif, Pelse, Pendif,  Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eioccc,Eioccc,Eioccc,Eioccc,Eioccc,  drop,  Eeof,  abort },/* IS_TRUE_MIDDLE */{ Itrue, Ifalse,Fpass, Ftrue, Ffalse,Melif, Melif, Melif, Melse, Pendif,  Oiffy, Oiffy, Fpass, Oif,   Oif,   Eioccc,Eioccc,Eioccc,Eioccc,Pendif,  print, Eeof,  abort },/* IS_PASS_ELSE */{ Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Eelif, Eelif, Eelse, Pendif,  Oiffy, Oiffy, Fpass, Oif,   Oif,   Eelif, Eelif, Eelif, Eelse, Pendif,  print, Eeof,  abort },/* IS_FALSE_ELSE */{ Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eelif, Eelif, Eelif, Eelse, Dendif,  Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eelif, Eelif, Eelif, Eelse, Eioccc,  drop,  Eeof,  abort },/* IS_TRUE_ELSE */{ Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Eelif, Eelif, Eelse, Dendif,  Oiffy, Oiffy, Fpass, Oif,   Oif,   Eelif, Eelif, Eelif, Eelse, Eioccc,  print, Eeof,  abort },/* IS_FALSE_TRAILER */{ Idrop, Idrop, Fdrop, Fdrop, Fdrop, Dfalse,Dfalse,Dfalse,Delse, Dendif,  Idrop, Idrop, Fdrop, Fdrop, Fdrop, Dfalse,Dfalse,Dfalse,Delse, Eioccc,  drop,  Eeof,  abort }/*TRUEI  FALSEI IF     TRUE   FALSE  ELIF   ELTRUE ELFALSE ELSE  ENDIF  TRUEI  FALSEI IF     TRUE   FALSE  ELIF   ELTRUE ELFALSE ELSE  ENDIF (DODGY)

Lines 1–500 of 1225.