Update to upstream version 2.11

Improved #if expression evaluator and safer modify-in-place.

Obtained from:	http://dotat.at/prog/unifdef
MFC after:	1 week
This commit is contained in:
fanf 2015-12-03 14:21:55 +00:00
parent 414f518dc1
commit 15414f06cb
3 changed files with 149 additions and 31 deletions

View File

@ -1,6 +1,6 @@
.\" Copyright (c) 1985, 1991, 1993 .\" Copyright (c) 1985, 1991, 1993
.\" The Regents of the University of California. All rights reserved. .\" The Regents of the University of California. All rights reserved.
.\" Copyright (c) 2002 - 2013 Tony Finch <dot@dotat.at>. All rights reserved. .\" Copyright (c) 2002 - 2015 Tony Finch <dot@dotat.at>. All rights reserved.
.\" .\"
.\" This code is derived from software contributed to Berkeley by .\" This code is derived from software contributed to Berkeley by
.\" Dave Yost. It was rewritten to support ANSI C by Tony Finch. .\" Dave Yost. It was rewritten to support ANSI C by Tony Finch.
@ -31,7 +31,7 @@
.\" .\"
.\" $FreeBSD$ .\" $FreeBSD$
.\" .\"
.Dd January 7, 2014 .Dd December 3, 2015
.Dt UNIFDEF 1 PRM .Dt UNIFDEF 1 PRM
.Os " " .Os " "
.Sh NAME .Sh NAME
@ -122,10 +122,13 @@ the
.Fn defined .Fn defined
operator, operator,
the operators the operators
.Ic \&! , < , > , .Ic \&! , ~ , -
.Ic <= , >= , == , != , (unary),
.Ic * , / , % , + , - ,
.Ic < , <= , > , >= , == , != , & , ^ , \&| ,
.Ic && , || , .Ic && , || ,
and parenthesized expressions. and parenthesized expressions.
Division by zero is treated as an unknown value.
A kind of A kind of
.Dq "short circuit" .Dq "short circuit"
evaluation is used for the evaluation is used for the
@ -253,6 +256,11 @@ are set to the given value.
Function-like macro definitions (with arguments) Function-like macro definitions (with arguments)
are treated as if they are set to 1. are treated as if they are set to 1.
.Pp .Pp
.Em Warning:
string literals and character constants are not parsed correctly in
.Fl f
files.
.Pp
.It Fl b .It Fl b
Replace removed lines with blank lines Replace removed lines with blank lines
instead of deleting them. instead of deleting them.
@ -325,12 +333,19 @@ It would be rude to strip them out, just as it would be for normal comments.
.Pp .Pp
.It Fl m .It Fl m
Modify one or more input files in place. Modify one or more input files in place.
If an input file is not modified,
the original is preserved instead of being overwritten with an identical copy.
.Pp .Pp
.It Fl M Ar backext .It Fl M Ar backext
Modify input files in place, and keep backups of the original files by Modify input files in place, and keep backups of the original files by
appending the appending the
.Ar backext .Ar backext
to the input filenames. to the input filenames.
A zero length
.Ar backext
behaves the same as the
.Fl m
option.
.Pp .Pp
.It Fl n .It Fl n
Add Add
@ -433,23 +448,29 @@ command line options are given.
.Sh DIAGNOSTICS .Sh DIAGNOSTICS
.Bl -item .Bl -item
.It .It
Too many levels of nesting. .Tn EOF
in comment
.It .It
Inappropriate Inappropriate
.Ic #elif , .Ic #elif ,
.Ic #else .Ic #else
or or
.Ic #endif . .Ic #endif
.It .It
Obfuscated preprocessor control line. Missing macro name in #define or #undef
.It
Obfuscated preprocessor control line
.It .It
Premature Premature
.Tn EOF .Tn EOF
(with the line number of the most recent unterminated (with the line number of the most recent unterminated
.Ic #if ) . .Ic #if )
.It .It
.Tn EOF Too many levels of nesting
in comment. .It
Unrecognized preprocessor directive
.It
Unterminated char or string literal
.El .El
.Sh SEE ALSO .Sh SEE ALSO
.Xr cpp 1 , .Xr cpp 1 ,
@ -475,6 +496,12 @@ rewrote it to support
.Sh BUGS .Sh BUGS
Expression evaluation is very limited. Expression evaluation is very limited.
.Pp .Pp
Character constants are not evaluated.
String literals and character constants in
.Fl f
definition files are ignored rather than parsed as
part of a macro's replacement tokens.
.Pp
Handling one line at a time means Handling one line at a time means
preprocessor directives split across more than one physical line preprocessor directives split across more than one physical line
(because of comments or backslash-newline) (because of comments or backslash-newline)

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2002 - 2014 Tony Finch <dot@dotat.at> * Copyright (c) 2002 - 2015 Tony Finch <dot@dotat.at>
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions * modification, are permitted provided that the following conditions
@ -46,7 +46,7 @@
#include "unifdef.h" #include "unifdef.h"
static const char copyright[] = static const char copyright[] =
"@(#) $Version: unifdef-2.10 $\n" "@(#) $Version: unifdef-2.11 $\n"
"@(#) $FreeBSD$\n" "@(#) $FreeBSD$\n"
"@(#) $Author: Tony Finch (dot@dotat.at) $\n" "@(#) $Author: Tony Finch (dot@dotat.at) $\n"
"@(#) $URL: http://dotat.at/prog/unifdef $\n" "@(#) $URL: http://dotat.at/prog/unifdef $\n"
@ -208,6 +208,7 @@ static bool firstsym; /* ditto */
static int exitmode; /* exit status mode */ static int exitmode; /* exit status mode */
static int exitstat; /* program exit status */ static int exitstat; /* program exit status */
static bool altered; /* was this file modified? */
static void addsym1(bool, bool, char *); static void addsym1(bool, bool, char *);
static void addsym2(bool, const char *, const char *); static void addsym2(bool, const char *, const char *);
@ -312,7 +313,8 @@ main(int argc, char *argv[])
break; break;
case 'M': /* modify in place and keep backup */ case 'M': /* modify in place and keep backup */
inplace = true; inplace = true;
backext = optarg; if (strlen(optarg) > 0)
backext = optarg;
break; break;
case 'n': /* add #line directive after deleted lines */ case 'n': /* add #line directive after deleted lines */
lnnum = true; lnnum = true;
@ -350,6 +352,8 @@ main(int argc, char *argv[])
errx(2, "-o cannot be used with multiple input files"); errx(2, "-o cannot be used with multiple input files");
if (argc > 1 && !inplace) if (argc > 1 && !inplace)
errx(2, "multiple input files require -m or -M"); errx(2, "multiple input files require -m or -M");
if (argc == 0 && inplace)
errx(2, "-m requires an input file");
if (argc == 0) if (argc == 0)
argc = 1; argc = 1;
if (argc == 1 && !inplace && ofilename == NULL) if (argc == 1 && !inplace && ofilename == NULL)
@ -416,7 +420,11 @@ processinout(const char *ifn, const char *ofn)
err(2, "can't rename \"%s\" to \"%s\"", ofn, backname); err(2, "can't rename \"%s\" to \"%s\"", ofn, backname);
free(backname); free(backname);
} }
if (replace(tempname, ofn) < 0) /* leave file unmodified if unifdef made no changes */
if (!altered && backext == NULL) {
if (remove(tempname) < 0)
warn("can't remove \"%s\"", tempname);
} else if (replace(tempname, ofn) < 0)
err(2, "can't rename \"%s\" to \"%s\"", tempname, ofn); err(2, "can't rename \"%s\" to \"%s\"", tempname, ofn);
free(tempname); free(tempname);
tempname = NULL; tempname = NULL;
@ -638,6 +646,7 @@ keywordedit(const char *replacement)
{ {
snprintf(keyword, tline + sizeof(tline) - keyword, snprintf(keyword, tline + sizeof(tline) - keyword,
"%s%s", replacement, newline); "%s%s", replacement, newline);
altered = true;
print(); print();
} }
static void static void
@ -700,7 +709,7 @@ flushline(bool keep)
} else { } else {
if (lnblank && fputs(newline, output) == EOF) if (lnblank && fputs(newline, output) == EOF)
closeio(); closeio();
exitstat = 1; altered = true;
delcount += 1; delcount += 1;
blankcount = 0; blankcount = 0;
} }
@ -752,6 +761,7 @@ process(void)
zerosyms = true; zerosyms = true;
newline = NULL; newline = NULL;
linenum = 0; linenum = 0;
altered = false;
while (lineval != LT_EOF) { while (lineval != LT_EOF) {
lineval = parseline(); lineval = parseline();
trans_table[ifstate[depth]][lineval](); trans_table[ifstate[depth]][lineval]();
@ -759,6 +769,7 @@ process(void)
linenum, linetype_name[lineval], linenum, linetype_name[lineval],
ifstate_name[ifstate[depth]], depth); ifstate_name[ifstate[depth]], depth);
} }
exitstat |= altered;
} }
/* /*
@ -892,6 +903,40 @@ static Linetype op_and(long *p, Linetype at, long a, Linetype bt, long b) {
return (*p = 0, LT_FALSE); return (*p = 0, LT_FALSE);
return op_strict(p, a && b, at, bt); return op_strict(p, a && b, at, bt);
} }
static Linetype op_blsh(long *p, Linetype at, long a, Linetype bt, long b) {
return op_strict(p, a << b, at, bt);
}
static Linetype op_brsh(long *p, Linetype at, long a, Linetype bt, long b) {
return op_strict(p, a >> b, at, bt);
}
static Linetype op_add(long *p, Linetype at, long a, Linetype bt, long b) {
return op_strict(p, a + b, at, bt);
}
static Linetype op_sub(long *p, Linetype at, long a, Linetype bt, long b) {
return op_strict(p, a - b, at, bt);
}
static Linetype op_mul(long *p, Linetype at, long a, Linetype bt, long b) {
return op_strict(p, a * b, at, bt);
}
static Linetype op_div(long *p, Linetype at, long a, Linetype bt, long b) {
if (bt != LT_TRUE) {
debug("eval division by zero");
return (LT_ERROR);
}
return op_strict(p, a / b, at, bt);
}
static Linetype op_mod(long *p, Linetype at, long a, Linetype bt, long b) {
return op_strict(p, a % b, at, bt);
}
static Linetype op_bor(long *p, Linetype at, long a, Linetype bt, long b) {
return op_strict(p, a | b, at, bt);
}
static Linetype op_bxor(long *p, Linetype at, long a, Linetype bt, long b) {
return op_strict(p, a ^ b, at, bt);
}
static Linetype op_band(long *p, Linetype at, long a, Linetype bt, long b) {
return op_strict(p, a & b, at, bt);
}
/* /*
* An evaluation function takes three arguments, as follows: (1) a pointer to * An evaluation function takes three arguments, as follows: (1) a pointer to
@ -915,10 +960,15 @@ static eval_fn eval_table, eval_unary;
* calls the inner function with its first argument pointing to the next * calls the inner function with its first argument pointing to the next
* element of the table. Innermost expressions have special non-table-driven * element of the table. Innermost expressions have special non-table-driven
* handling. * handling.
*
* The stop characters help with lexical analysis: an operator is not
* recognized if it is followed by one of the stop characters because
* that would make it a different operator.
*/ */
struct op { struct op {
const char *str; const char *str;
Linetype (*fn)(long *, Linetype, long, Linetype, long); Linetype (*fn)(long *, Linetype, long, Linetype, long);
const char *stop;
}; };
struct ops { struct ops {
eval_fn *inner; eval_fn *inner;
@ -927,12 +977,22 @@ struct ops {
static const struct ops eval_ops[] = { static const struct ops eval_ops[] = {
{ eval_table, { { "||", op_or } } }, { eval_table, { { "||", op_or } } },
{ eval_table, { { "&&", op_and } } }, { eval_table, { { "&&", op_and } } },
{ eval_table, { { "|", op_bor, "|" } } },
{ eval_table, { { "^", op_bxor } } },
{ eval_table, { { "&", op_band, "&" } } },
{ eval_table, { { "==", op_eq }, { eval_table, { { "==", op_eq },
{ "!=", op_ne } } }, { "!=", op_ne } } },
{ eval_unary, { { "<=", op_le }, { eval_table, { { "<=", op_le },
{ ">=", op_ge }, { ">=", op_ge },
{ "<", op_lt }, { "<", op_lt, "<=" },
{ ">", op_gt } } } { ">", op_gt, ">=" } } },
{ eval_table, { { "<<", op_blsh },
{ ">>", op_brsh } } },
{ eval_table, { { "+", op_add },
{ "-", op_sub } } },
{ eval_unary, { { "*", op_mul },
{ "/", op_div },
{ "%", op_mod } } },
}; };
/* Current operator precedence level */ /* Current operator precedence level */
@ -966,6 +1026,26 @@ eval_unary(const struct ops *ops, long *valp, const char **cpp)
*valp = !*valp; *valp = !*valp;
lt = *valp ? LT_TRUE : LT_FALSE; lt = *valp ? LT_TRUE : LT_FALSE;
} }
} else if (*cp == '~') {
debug("eval%d ~", prec(ops));
cp++;
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;
}
} else if (*cp == '-') {
debug("eval%d -", prec(ops));
cp++;
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;
}
} else if (*cp == '(') { } else if (*cp == '(') {
cp++; cp++;
debug("eval%d (", prec(ops)); debug("eval%d (", prec(ops));
@ -1040,7 +1120,7 @@ eval_table(const struct ops *ops, long *valp, const char **cpp)
{ {
const struct op *op; const struct op *op;
const char *cp; const char *cp;
long val; long val = 0;
Linetype lt, rt; Linetype lt, rt;
debug("eval%d", prec(ops)); debug("eval%d", prec(ops));
@ -1050,9 +1130,16 @@ eval_table(const struct ops *ops, long *valp, const char **cpp)
return (LT_ERROR); return (LT_ERROR);
for (;;) { for (;;) {
cp = skipcomment(cp); cp = skipcomment(cp);
for (op = ops->op; op->str != NULL; op++) for (op = ops->op; op->str != NULL; op++) {
if (strncmp(cp, op->str, strlen(op->str)) == 0) if (strncmp(cp, op->str, strlen(op->str)) == 0) {
break; /* assume only one-char operators have stop chars */
if (op->stop != NULL && cp[1] != '\0' &&
strchr(op->stop, cp[1]) != NULL)
continue;
else
break;
}
}
if (op->str == NULL) if (op->str == NULL)
break; break;
cp += strlen(op->str); cp += strlen(op->str);
@ -1123,10 +1210,14 @@ skiphash(void)
static const char * static const char *
skipline(const char *cp) skipline(const char *cp)
{ {
const char *pcp;
if (*cp != '\0') if (*cp != '\0')
linestate = LS_DIRTY; linestate = LS_DIRTY;
while (*cp != '\0') while (*cp != '\0') {
cp = skipcomment(cp + 1); cp = skipcomment(pcp = cp);
if (pcp == cp)
cp++;
}
return (cp); return (cp);
} }
@ -1202,9 +1293,9 @@ skipcomment(const char *cp)
cp += 2; cp += 2;
} else if (strncmp(cp, "\n", 1) == 0) { } else if (strncmp(cp, "\n", 1) == 0) {
if (incomment == CHAR_LITERAL) if (incomment == CHAR_LITERAL)
error("unterminated char literal"); error("Unterminated char literal");
else else
error("unterminated string literal"); error("Unterminated string literal");
} else } else
cp += 1; cp += 1;
continue; continue;
@ -1478,7 +1569,7 @@ defundef(void)
if ((cp = matchsym("define", kw)) != NULL) { if ((cp = matchsym("define", kw)) != NULL) {
sym = getsym(&cp); sym = getsym(&cp);
if (sym == NULL) if (sym == NULL)
error("missing macro name in #define"); error("Missing macro name in #define");
if (*cp == '(') { if (*cp == '(') {
val = "1"; val = "1";
} else { } else {
@ -1490,12 +1581,12 @@ defundef(void)
} else if ((cp = matchsym("undef", kw)) != NULL) { } else if ((cp = matchsym("undef", kw)) != NULL) {
sym = getsym(&cp); sym = getsym(&cp);
if (sym == NULL) if (sym == NULL)
error("missing macro name in #undef"); error("Missing macro name in #undef");
cp = skipcomment(cp); cp = skipcomment(cp);
debug("#undef"); debug("#undef");
addsym2(false, sym, NULL); addsym2(false, sym, NULL);
} else { } else {
error("unrecognized preprocessor directive"); error("Unrecognized preprocessor directive");
} }
skipline(cp); skipline(cp);
done: done:
@ -1567,5 +1658,5 @@ error(const char *msg)
warnx("%s: %d: %s (#if line %d depth %d)", warnx("%s: %d: %s (#if line %d depth %d)",
filename, linenum, msg, stifline[depth], depth); filename, linenum, msg, stifline[depth], depth);
closeio(); closeio();
errx(2, "output may be truncated"); errx(2, "Output may be truncated");
} }

View File

@ -36,7 +36,7 @@
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
/* portabiity stubs */ /* portability stubs */
#define fbinmode(fp) (fp) #define fbinmode(fp) (fp)