/* * Copyright (c) 2002 - 2005 Tony Finch <dot@dotat.at>. All rights reserved. * * This code is 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 following copyright notice. None of its code remains * in this version (though some of the names remain). * * Copyright (c) 1985, 1993 * The Regents of the University of California. All rights reserved. * * 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. */ #include <sys/cdefs.h> #ifndef lint#if 0static const char copyright[] ="@(#) Copyright (c) 1985, 1993\n\ The Regents of the University of California. All rights reserved.\n";#endif#ifdef __IDSTRING__IDSTRING(Berkeley, "@(#)unifdef.c 8.1 (Berkeley) 6/6/93");__IDSTRING(NetBSD, "$NetBSD: unifdef.c,v 1.8 2000/07/03 02:51:36 matt Exp $");__IDSTRING(dotat, "$dotat: things/unifdef.c,v 1.171 2005/03/08 12:38:48 fanf2 Exp $");#endif#endif /* not lint */#ifdef __FBSDID__FBSDID("$FreeBSD: /repoman/r/ncvs/src/usr.bin/unifdef/unifdef.c,v 1.20 2005/05/21 09:55:09 ru Exp $");#endif /* * unifdef - remove ifdef'ed lines * * 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 * * The first two items above require better buffer handling, which would * also make it possible to handle all "dodgy" directives correctly. */ #include <ctype.h>#include <err.h>#include <stdarg.h>#include <stdbool.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h> size_t strlcpy(char *dst, const char *src, size_t siz); /* 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_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"}; /* 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 /* * Globals. */ static bool complement; /* -c: do the complement */static bool debugging; /* -d: debugging reports */static bool iocccok; /* -e: fewer IOCCC errors */static bool killconsts; /* -k: eval constant #ifs */static bool lnblank; /* -l: blank deleted lines */static bool lnnum; /* -n: add #line directives */static bool symlist; /* -s: output symbol list */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 char tline[MAXLINE+EDITSLOP];/* input buffer plus space */static char *keyword; /* used for editing #elif's */ 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 bool keepthis; /* don't delete constant #if */ static int exitstat; /* program exit status */ static void addsym(bool, bool, char *);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 getline(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 *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); #define endsym(c) (!isalpha((unsigned char)c) && !isdigit((unsigned char)c) && c != '_') /* * The main program. */intmain(int argc, char *argv[]){ int opt; while ((opt = getopt(argc, argv, "i:D:U:I:cdeklnst")) != -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 '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': /* process constant #ifs */ killconsts = true; break; case 'l': /* blank deleted lines instead of omitting them */ lnblank = true; break; case 'n': /* add #line directive after deleted lines */ lnnum = true; break; case 's': /* only output list of symbols that control #ifs */ symlist = true; break; case 't': /* don't parse C comments */ text = true; break; default: usage(); } argc -= optind; argv += optind; if (argc > 1) { errx(2, "can only do one file"); } else if (argc == 1 && strcmp(*argv, "-") != 0) { filename = *argv; input = fopen(filename, "r"); if (input == NULL) err(2, "can't open %s", filename); } else { filename = "[stdin]"; input = stdin; } process(); abort(); /* bug */} static voidusage(void){ fprintf(stderr, "usage: unifdef [-cdeklnst] [-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. * * 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(); }/* edit this line */static void Mpass (void) { strncpy(keyword, "if ", 4); Pelif(); }static void Mtrue (void) { keywordedit("else\n"); state(IS_TRUE_MIDDLE); }static void Melif (void) { keywordedit("endif\n"); state(IS_FALSE_TRAILER); }static void Melse (void) { keywordedit("endif\n"); 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 },/* 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 },/* 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 },/* 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 },/* 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 },/* 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 },/* 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 },/* 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 },/* 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 },/* 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 }/*TRUEI FALSEI IF TRUE FALSE ELIF ELTRUE ELFALSE ELSE ENDIF TRUEI FALSEI IF TRUE FALSE ELIF ELTRUE ELFALSE ELSE ENDIF (DODGY) PLAIN EOF */}; /* * State machine utility functions */static voiddone(void){ if (incomment) error("EOF in comment"); exit(exitstat);}static voidignoreoff(void){ if (depth == 0) abort(); /* bug */ ignoring[depth] = ignoring[depth-1];}static voidignoreon(void){ ignoring[depth] = true;}static voidkeywordedit(const char *replacement){ size_t size = tline + sizeof(tline) - keyword; char *dst = keyword; const char *src = replacement; if (size != 0) { while ((--size != 0) && (*src != '\0')) *dst++ = *src++; *dst = '\0'; } print();}static voidnest(void){ depth += 1; if (depth >= MAXDEPTH) error("Too many levels of nesting"); stifline[depth] = linenum;}static voidunnest(void){ if (depth == 0) abort(); /* bug */ depth -= 1;}static voidstate(Ifstate is){ ifstate[depth] = is;} /* * Write a line to the output or not, according to command line options. */static voidflushline(bool keep){ if (symlist) return; if (keep ^ complement) { if (lnnum && delcount > 0) printf("#line %d\n", linenum); fputs(tline, stdout); delcount = 0; } else { if (lnblank) putc('\n', stdout); exitstat = 1;Lines 1–500 of 1005. Load more
/* * Copyright (c) 2002 - 2005 Tony Finch <dot@dotat.at>. All rights reserved. * * This code is 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 following copyright notice. None of its code remains * in this version (though some of the names remain). * * Copyright (c) 1985, 1993 * The Regents of the University of California. All rights reserved. * * 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. */ #include <sys/cdefs.h> #ifndef lint #if 0 static const char copyright[] = "@(#) Copyright (c) 1985, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif #ifdef __IDSTRING __IDSTRING(Berkeley, "@(#)unifdef.c 8.1 (Berkeley) 6/6/93"); __IDSTRING(NetBSD, "$NetBSD: unifdef.c,v 1.8 2000/07/03 02:51:36 matt Exp $"); __IDSTRING(dotat, "$dotat: things/unifdef.c,v 1.171 2005/03/08 12:38:48 fanf2 Exp $"); #endif #endif /* not lint */ #ifdef __FBSDID __FBSDID("$FreeBSD: /repoman/r/ncvs/src/usr.bin/unifdef/unifdef.c,v 1.20 2005/05/21 09:55:09 ru Exp $"); #endif /* * unifdef - remove ifdef'ed lines * * 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 * * The first two items above require better buffer handling, which would * also make it possible to handle all "dodgy" directives correctly. */ #include <ctype.h> #include <err.h> #include <stdarg.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> size_t strlcpy(char *dst, const char *src, size_t siz); /* 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_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" }; /* 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 /* * Globals. */ static bool complement; /* -c: do the complement */ static bool debugging; /* -d: debugging reports */ static bool iocccok; /* -e: fewer IOCCC errors */ static bool killconsts; /* -k: eval constant #ifs */ static bool lnblank; /* -l: blank deleted lines */ static bool lnnum; /* -n: add #line directives */ static bool symlist; /* -s: output symbol list */ 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 char tline[MAXLINE+EDITSLOP];/* input buffer plus space */ static char *keyword; /* used for editing #elif's */ 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 bool keepthis; /* don't delete constant #if */ static int exitstat; /* program exit status */ static void addsym(bool, bool, char *); 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 getline(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 *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); #define endsym(c) (!isalpha((unsigned char)c) && !isdigit((unsigned char)c) && c != '_') /* * The main program. */ int main(int argc, char *argv[]) { int opt; while ((opt = getopt(argc, argv, "i:D:U:I:cdeklnst")) != -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 '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': /* process constant #ifs */ killconsts = true; break; case 'l': /* blank deleted lines instead of omitting them */ lnblank = true; break; case 'n': /* add #line directive after deleted lines */ lnnum = true; break; case 's': /* only output list of symbols that control #ifs */ symlist = true; break; case 't': /* don't parse C comments */ text = true; break; default: usage(); } argc -= optind; argv += optind; if (argc > 1) { errx(2, "can only do one file"); } else if (argc == 1 && strcmp(*argv, "-") != 0) { filename = *argv; input = fopen(filename, "r"); if (input == NULL) err(2, "can't open %s", filename); } else { filename = "[stdin]"; input = stdin; } process(); abort(); /* bug */ } static void usage(void) { fprintf(stderr, "usage: unifdef [-cdeklnst] [-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. * * 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(); } /* edit this line */ static void Mpass (void) { strncpy(keyword, "if ", 4); Pelif(); } static void Mtrue (void) { keywordedit("else\n"); state(IS_TRUE_MIDDLE); } static void Melif (void) { keywordedit("endif\n"); state(IS_FALSE_TRAILER); } static void Melse (void) { keywordedit("endif\n"); 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 }, /* 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 }, /* 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 }, /* 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 }, /* 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 }, /* 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 }, /* 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 }, /* 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 }, /* 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 }, /* 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 } /*TRUEI FALSEI IF TRUE FALSE ELIF ELTRUE ELFALSE ELSE ENDIF TRUEI FALSEI IF TRUE FALSE ELIF ELTRUE ELFALSE ELSE ENDIF (DODGY) PLAIN EOF */ }; /* * State machine utility functions */ static void done(void) { if (incomment) error("EOF in comment"); exit(exitstat); } 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) { size_t size = tline + sizeof(tline) - keyword; char *dst = keyword; const char *src = replacement; if (size != 0) { while ((--size != 0) && (*src != '\0')) *dst++ = *src++; *dst = '\0'; } print(); } static void nest(void) { depth += 1; if (depth >= MAXDEPTH) error("Too many levels of nesting"); 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) { if (lnnum && delcount > 0) printf("#line %d\n", linenum); fputs(tline, stdout); delcount = 0; } else { if (lnblank) putc('\n', stdout); exitstat = 1;