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 <tedu@stanford.edu>,
    including some whitespace fixes and removal of strcpy()
    (and not including excessively strict KNF enforcement).

* Fix some typos and terminological inconsistencies.
This commit is contained in:
Tony Finch 2003-01-20 12:41:41 +00:00
parent 8ec22a9363
commit 0723ff0c6a
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=109567
3 changed files with 148 additions and 59 deletions

View File

@ -6,5 +6,6 @@ MAINTAINER= fanf@FreeBSD.org
PROG= unifdef
SCRIPTS=unifdefall.sh
MLINKS= unifdef.1 unifdefall.1
WARNS?= 5
.include <bsd.prog.mk>

View File

@ -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

View File

@ -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 <ctype.h>
@ -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"
};
@ -147,12 +157,19 @@ static char const * const linestate_name[] = {
#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 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");
}