From d707a4320db2fa86d34e3eb17eb2780b92c9930e Mon Sep 17 00:00:00 2001 From: fanf Date: Mon, 20 Jan 2003 12:41:41 +0000 Subject: Sync with upstream again: * Be less strict about multi-line preprocessor directives (e.g. those with comments hanging off the right-hand end) since they're more of a problem in practise than I expected. Prompted by phk. * Fix the handling of "ignore" symbols. * Style pedantry from OpenBSD and Ted Unangst , including some whitespace fixes and removal of strcpy() (and not including excessively strict KNF enforcement). * Fix some typos and terminological inconsistencies. --- usr.bin/unifdef/Makefile | 1 + usr.bin/unifdef/unifdef.1 | 24 +++++- usr.bin/unifdef/unifdef.c | 182 ++++++++++++++++++++++++++++++++-------------- 3 files changed, 148 insertions(+), 59 deletions(-) (limited to 'usr.bin') diff --git a/usr.bin/unifdef/Makefile b/usr.bin/unifdef/Makefile index e3817d1..de6c53a 100644 --- a/usr.bin/unifdef/Makefile +++ b/usr.bin/unifdef/Makefile @@ -6,5 +6,6 @@ MAINTAINER= fanf@FreeBSD.org PROG= unifdef SCRIPTS=unifdefall.sh MLINKS= unifdef.1 unifdefall.1 +WARNS?= 5 .include diff --git a/usr.bin/unifdef/unifdef.1 b/usr.bin/unifdef/unifdef.1 index 6bc771c..fd76e14 100644 --- a/usr.bin/unifdef/unifdef.1 +++ b/usr.bin/unifdef/unifdef.1 @@ -33,7 +33,7 @@ .\" SUCH DAMAGE. .\" .\" @(#)unifdef.1 8.2 (Berkeley) 4/1/94 -.\" $dotat: things/unifdef.1,v 1.40 2002/12/13 11:33:34 fanf2 Exp $ +.\" $dotat: things/unifdef.1,v 1.43 2003/01/20 11:36:12 fanf2 Exp $ .\" $FreeBSD$ .\" .Dd September 24, 2002 @@ -44,7 +44,7 @@ .Nd remove preprocessor conditionals from code .Sh SYNOPSIS .Nm -.Op Fl cklst +.Op Fl ceklst .Oo .Fl I Ns Ar path .Fl D Ns Ar sym Ns Op = Ns Ar val @@ -157,6 +157,24 @@ is complemented, i.e., the lines that would have been removed or blanked are retained and vice versa. .Pp +.It Fl e +Because +.Nm +processes its input one line at a time, +it cannot remove preprocessor directives that span more than one line. +The most common example of this is a directive with a multi-line +comment hanging off its right hand end. +By default, +if +.Nm +has to process such a directive, +it will complain that the line is too obfuscated. +The +.Fl e +option changes the behaviour so that, +where possible, +such lines are left unprocessed instead of reporting an error. +.Pp .It Fl k Process .Ic #if @@ -285,7 +303,7 @@ Expression evaluation is very limited. .Pp Preprocessor control lines split across more than one physical line (because of comments or backslash-newline) -cannot be handled. +cannot be handled in every situation. .Pp Trigraphs are not recognized. .Pp diff --git a/usr.bin/unifdef/unifdef.c b/usr.bin/unifdef/unifdef.c index 01d4e9e..66f65d8 100644 --- a/usr.bin/unifdef/unifdef.c +++ b/usr.bin/unifdef/unifdef.c @@ -44,7 +44,7 @@ static const char copyright[] = #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.133 2003/01/17 19:04:36 fanf2 Exp $"); +__IDSTRING(dotat, "$dotat: things/unifdef.c,v 1.148 2003/01/20 12:05:41 fanf2 Exp $"); #endif #ifdef __FBSDID __FBSDID("$FreeBSD$"); @@ -61,6 +61,9 @@ __FBSDID("$FreeBSD$"); * #else's and #endif's to see that they match their * corresponding #ifdef or #ifndef * generate #line directives in place of deleted code + * + * The first two items above require better buffer handling, which would + * also make it possible to handle all "dodgy" directives correctly. */ #include @@ -74,7 +77,6 @@ __FBSDID("$FreeBSD$"); /* types of input lines: */ typedef enum { - LT_PLAIN, /* ordinary line */ LT_TRUEI, /* a true #if with ignore flag */ LT_FALSEI, /* a false #if with ignore flag */ LT_IF, /* an unknown #if */ @@ -85,13 +87,21 @@ typedef enum { 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[] = { - "PLAIN", "TRUEI", "FALSEI", "IF", "TRUE", "FALSE", - "ELIF", "ELTRUE", "ELFALSE", "ELSE", "ENDIF", "EOF" + "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 */ @@ -111,8 +121,8 @@ typedef enum { static char const * const ifstate_name[] = { "OUTSIDE", "FALSE_PREFIX", "TRUE_PREFIX", - "PASS_MIDDLE", "FALSE_MIDDLE", "TRUE_MIDDLE", - "PASS_ELSE", "FALSE_ELSE", "TRUE_ELSE", + "PASS_MIDDLE", "FALSE_MIDDLE", "TRUE_MIDDLE", + "PASS_ELSE", "FALSE_ELSE", "TRUE_ELSE", "FALSE_TRAILER" }; @@ -148,11 +158,18 @@ static char const * const linestate_name[] = { #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 symlist; /* -s: output symbol list */ @@ -167,7 +184,7 @@ static FILE *input; /* input file pointer */ static const char *filename; /* input file name */ static int linenum; /* current line number */ -static char tline[MAXLINE+10]; /* input buffer plus space */ +static char tline[MAXLINE+EDITSLOP];/* input buffer plus space */ static char *keyword; /* used for editing #elif's */ static Comment_state incomment; /* comment parser state */ @@ -187,13 +204,15 @@ 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 unignore(void); static void usage(void); #define endsym(c) (!isalpha((unsigned char)c) && !isdigit((unsigned char)c) && c != '_') @@ -206,7 +225,7 @@ main(int argc, char *argv[]) { int opt; - while ((opt = getopt(argc, argv, "i:D:U:I:cdklst")) != -1) + while ((opt = getopt(argc, argv, "i:D:U:I:cdeklst")) != -1) switch (opt) { case 'i': /* treat stuff controlled by these symbols as text */ /* @@ -237,6 +256,9 @@ main(int argc, char *argv[]) case 'd': debugging = true; break; + case 'e': /* fewer errors from dodgy lines */ + iocccok = true; + break; case 'k': /* process constant #ifs */ killconsts = true; break; @@ -279,9 +301,9 @@ main(int argc, char *argv[]) static void usage(void) { - fprintf(stderr, "usage: %s", -"unifdef [-cdklst] [[-Dsym[=val]] [-Usym] [-iDsym[=val]] [-iUsym]] ... [file]\n"); - exit (2); + fprintf(stderr, "usage: unifdef [-cdeklst]" + " [[-Dsym[=val]] [-Usym] [-iDsym[=val]] [-iUsym]] ... [file]\n"); + exit(2); } /* @@ -291,19 +313,28 @@ usage(void) * indicate that processing is complete. * * Nesting is handled by keeping a stack of states; some transition - * functions increase or decrease the depth. They also maintin the + * 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 turn the + * #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 change the #elif to #else and the + * #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); @@ -317,65 +348,103 @@ static void Eioccc(void) { error("Obfuscated preprocessor control line"); } static void print (void) { flushline(true); } static void drop (void) { flushline(false); } /* output lacks group's start line */ -static void Strue (void) { drop(); unignore(); state(IS_TRUE_PREFIX); } -static void Sfalse(void) { drop(); unignore(); state(IS_FALSE_PREFIX); } -static void Selse (void) { drop(); state(IS_TRUE_ELSE); } +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(); unignore(); state(IS_PASS_MIDDLE); } -static void Pelse (void) { print(); state(IS_PASS_ELSE); } +static void Pelif (void) { print(); ignoreoff(); state(IS_PASS_MIDDLE); } +static void Pelse (void) { print(); state(IS_PASS_ELSE); } static void Pendif(void) { print(); --depth; } /* discard this block */ -static void Dfalse(void) { drop(); unignore(); state(IS_FALSE_TRAILER); } -static void Delif (void) { drop(); unignore(); state(IS_FALSE_MIDDLE); } -static void Delse (void) { drop(); state(IS_FALSE_ELSE); } +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(); --depth; } /* 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) Fpass(); else Eioccc(); ignoreon(); } +static void Oif (void) { if (iocccok) Fpass(); else Eioccc(); } +static void Oelif (void) { if (iocccok) Pelif(); else Eioccc(); } /* ignore comments in this block */ -static void Idrop (void) { Fdrop(); ignore[depth] = true; } -static void Itrue (void) { Ftrue(); ignore[depth] = true; } -static void Ifalse(void) { Ffalse(); ignore[depth] = true; } -/* modify this line */ -static void -Mpass (void) { strncpy(keyword, "if ", 4); Pelif(); } -static void -Mtrue (void) { strcpy(keyword, "else\n"); print(); state(IS_TRUE_MIDDLE); } -static void -Melif (void) { strcpy(keyword, "endif\n"); print(); state(IS_FALSE_TRAILER); } -static void -Melse (void) { strcpy(keyword, "endif\n"); print(); state(IS_FALSE_ELSE); } +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 */ -{print,Itrue,Ifalse,Fpass,Ftrue,Ffalse,Eelif, Eelif, Eelif, Eelse,Eendif,NULL}, +{ Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Eelif, Eelif, Eelse, Eendif, + Oiffy, Oiffy, Fpass, Oif, Oif, Eelif, Eelif, Eelif, Eelse, Eendif, + print, NULL }, /* IS_FALSE_PREFIX */ -{drop, Idrop,Idrop, Fdrop,Fdrop,Fdrop, Mpass, Strue, Sfalse,Selse,Dendif,Eeof}, +{ 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 */ -{print,Itrue,Ifalse,Fpass,Ftrue,Ffalse,Dfalse,Dfalse,Dfalse,Delse,Dendif,Eeof}, +{ 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 */ -{print,Itrue,Ifalse,Fpass,Ftrue,Ffalse,Pelif, Mtrue, Delif, Pelse,Pendif,Eeof}, +{ 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 */ -{drop, Idrop,Idrop, Fdrop,Fdrop,Fdrop, Pelif, Mtrue, Delif, Pelse,Pendif,Eeof}, +{ 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 */ -{print,Itrue,Ifalse,Fpass,Ftrue,Ffalse,Melif, Melif, Melif, Melse,Pendif,Eeof}, +{ 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 */ -{print,Itrue,Ifalse,Fpass,Ftrue,Ffalse,Eelif, Eelif, Eelif, Eelse,Pendif,Eeof}, +{ 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 */ -{drop, Idrop,Idrop, Fdrop,Fdrop,Fdrop, Eelif, Eelif, Eelif, Eelse,Dendif,Eeof}, +{ 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 */ -{print,Itrue,Ifalse,Fpass,Ftrue,Ffalse,Eelif, Eelif, Eelif, Eelse,Dendif,Eeof}, +{ 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 */ -{drop, Idrop,Idrop, Fdrop,Fdrop,Fdrop, Dfalse,Dfalse,Dfalse,Delse,Dendif,Eeof} -/*PLAIN TRUEI FALSEI IF TRUE FALSE ELIF ELTRUE ELFALSE ELSE ENDIF EOF*/ +{ 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 +ignoreoff(void) +{ + ignoring[depth] = ignoring[depth-1]; +} +static void +ignoreon(void) +{ + ignoring[depth] = true; +} +static void +keywordedit(const char *replacement) +{ + strlcpy(keyword, replacement, tline + sizeof(tline) - keyword); + print(); +} +static void nest(void) { depth += 1; @@ -388,11 +457,6 @@ state(Ifstate is) { ifstate[depth] = is; } -static void -unignore(void) -{ - ignore[depth] = ignore[depth-1]; -} /* * Write a line to the output or not, according to command line options. @@ -464,6 +528,7 @@ getline(void) keyword = tline + (cp - tline); cp = skipsym(cp); kwlen = cp - keyword; + /* no way can we deal with a continuation inside a keyword */ if (strncmp(cp, "\\\n", 2) == 0) Eioccc(); if (strlcmp("ifdef", keyword, kwlen) == 0 || @@ -503,8 +568,12 @@ getline(void) if (retval == LT_ELTRUE || retval == LT_ELFALSE) retval = LT_ELIF; } - if (retval != LT_PLAIN && (wascomment || incomment)) - Eioccc(); + if (retval != LT_PLAIN && (wascomment || incomment)) { + retval += LT_DODGY; + if (incomment) + linestate = LS_DIRTY; + } + /* skipcomment should have changed the state */ if (linestate == LS_HASH) abort(); /* bug */ } @@ -582,7 +651,7 @@ eval_unary(const struct ops *ops, int *valp, const char **cpp) int sym; cp = skipcomment(*cpp); - if(*cp == '!') { + if (*cp == '!') { debug("eval%d !", ops - eval_ops); cp++; if (eval_unary(ops, valp, &cp) == LT_IF) @@ -869,8 +938,9 @@ static void error(const char *msg) { if (depth == 0) - errx(2, "%s: %d: %s", filename, linenum, msg); + warnx("%s: %d: %s", filename, linenum, msg); else - errx(2, "%s: %d: %s (#if line %d depth %d)", + warnx("%s: %d: %s (#if line %d depth %d)", filename, linenum, msg, stifline[depth], depth); + errx(2, "output may be truncated"); } -- cgit v1.1