aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTony Finch <fanf@FreeBSD.org>2015-12-03 14:21:55 +0000
committerTony Finch <fanf@FreeBSD.org>2015-12-03 14:21:55 +0000
commitef4eec8a6d3d8fe6303ec3dc47b4bbbe5f8c14f6 (patch)
treeedb1dc975e6f10b0e2e294bcacc26bc597386956
parentaacc26e3297e30ceb69e0b142f74d76c7aafcb13 (diff)
downloadsrc-ef4eec8a6d3d8fe6303ec3dc47b4bbbe5f8c14f6.tar.gz
src-ef4eec8a6d3d8fe6303ec3dc47b4bbbe5f8c14f6.zip
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
Notes
Notes: svn path=/head/; revision=291696
-rw-r--r--usr.bin/unifdef/unifdef.147
-rw-r--r--usr.bin/unifdef/unifdef.c131
-rw-r--r--usr.bin/unifdef/unifdef.h2
3 files changed, 149 insertions, 31 deletions
diff --git a/usr.bin/unifdef/unifdef.1 b/usr.bin/unifdef/unifdef.1
index 24a9205d8a76..430f743b6b4f 100644
--- a/usr.bin/unifdef/unifdef.1
+++ b/usr.bin/unifdef/unifdef.1
@@ -1,6 +1,6 @@
.\" Copyright (c) 1985, 1991, 1993
.\" 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
.\" Dave Yost. It was rewritten to support ANSI C by Tony Finch.
@@ -31,7 +31,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd January 7, 2014
+.Dd December 3, 2015
.Dt UNIFDEF 1 PRM
.Os " "
.Sh NAME
@@ -122,10 +122,13 @@ the
.Fn defined
operator,
the operators
-.Ic \&! , < , > ,
-.Ic <= , >= , == , != ,
+.Ic \&! , ~ , -
+(unary),
+.Ic * , / , % , + , - ,
+.Ic < , <= , > , >= , == , != , & , ^ , \&| ,
.Ic && , || ,
and parenthesized expressions.
+Division by zero is treated as an unknown value.
A kind of
.Dq "short circuit"
evaluation is used for the
@@ -253,6 +256,11 @@ are set to the given value.
Function-like macro definitions (with arguments)
are treated as if they are set to 1.
.Pp
+.Em Warning:
+string literals and character constants are not parsed correctly in
+.Fl f
+files.
+.Pp
.It Fl b
Replace removed lines with blank lines
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
.It Fl m
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
.It Fl M Ar backext
Modify input files in place, and keep backups of the original files by
appending the
.Ar backext
to the input filenames.
+A zero length
+.Ar backext
+behaves the same as the
+.Fl m
+option.
.Pp
.It Fl n
Add
@@ -433,23 +448,29 @@ command line options are given.
.Sh DIAGNOSTICS
.Bl -item
.It
-Too many levels of nesting.
+.Tn EOF
+in comment
.It
Inappropriate
.Ic #elif ,
.Ic #else
or
-.Ic #endif .
+.Ic #endif
+.It
+Missing macro name in #define or #undef
.It
-Obfuscated preprocessor control line.
+Obfuscated preprocessor control line
.It
Premature
.Tn EOF
(with the line number of the most recent unterminated
-.Ic #if ) .
+.Ic #if )
.It
-.Tn EOF
-in comment.
+Too many levels of nesting
+.It
+Unrecognized preprocessor directive
+.It
+Unterminated char or string literal
.El
.Sh SEE ALSO
.Xr cpp 1 ,
@@ -475,6 +496,12 @@ rewrote it to support
.Sh BUGS
Expression evaluation is very limited.
.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
preprocessor directives split across more than one physical line
(because of comments or backslash-newline)
diff --git a/usr.bin/unifdef/unifdef.c b/usr.bin/unifdef/unifdef.c
index fd378a14c1eb..2e0182a34539 100644
--- a/usr.bin/unifdef/unifdef.c
+++ b/usr.bin/unifdef/unifdef.c
@@ -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
* modification, are permitted provided that the following conditions
@@ -46,7 +46,7 @@
#include "unifdef.h"
static const char copyright[] =
- "@(#) $Version: unifdef-2.10 $\n"
+ "@(#) $Version: unifdef-2.11 $\n"
"@(#) $FreeBSD$\n"
"@(#) $Author: Tony Finch (dot@dotat.at) $\n"
"@(#) $URL: http://dotat.at/prog/unifdef $\n"
@@ -208,6 +208,7 @@ static bool firstsym; /* ditto */
static int exitmode; /* exit status mode */
static int exitstat; /* program exit status */
+static bool altered; /* was this file modified? */
static void addsym1(bool, bool, char *);
static void addsym2(bool, const char *, const char *);
@@ -312,7 +313,8 @@ main(int argc, char *argv[])
break;
case 'M': /* modify in place and keep backup */
inplace = true;
- backext = optarg;
+ if (strlen(optarg) > 0)
+ backext = optarg;
break;
case 'n': /* add #line directive after deleted lines */
lnnum = true;
@@ -350,6 +352,8 @@ main(int argc, char *argv[])
errx(2, "-o cannot be used with multiple input files");
if (argc > 1 && !inplace)
errx(2, "multiple input files require -m or -M");
+ if (argc == 0 && inplace)
+ errx(2, "-m requires an input file");
if (argc == 0)
argc = 1;
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);
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);
free(tempname);
tempname = NULL;
@@ -638,6 +646,7 @@ keywordedit(const char *replacement)
{
snprintf(keyword, tline + sizeof(tline) - keyword,
"%s%s", replacement, newline);
+ altered = true;
print();
}
static void
@@ -700,7 +709,7 @@ flushline(bool keep)
} else {
if (lnblank && fputs(newline, output) == EOF)
closeio();
- exitstat = 1;
+ altered = true;
delcount += 1;
blankcount = 0;
}
@@ -752,6 +761,7 @@ process(void)
zerosyms = true;
newline = NULL;
linenum = 0;
+ altered = false;
while (lineval != LT_EOF) {
lineval = parseline();
trans_table[ifstate[depth]][lineval]();
@@ -759,6 +769,7 @@ process(void)
linenum, linetype_name[lineval],
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 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
@@ -915,10 +960,15 @@ static eval_fn eval_table, eval_unary;
* calls the inner function with its first argument pointing to the next
* element of the table. Innermost expressions have special non-table-driven
* 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 {
const char *str;
Linetype (*fn)(long *, Linetype, long, Linetype, long);
+ const char *stop;
};
struct ops {
eval_fn *inner;
@@ -927,12 +977,22 @@ struct ops {
static const struct ops eval_ops[] = {
{ eval_table, { { "||", op_or } } },
{ eval_table, { { "&&", op_and } } },
+ { eval_table, { { "|", op_bor, "|" } } },
+ { eval_table, { { "^", op_bxor } } },
+ { eval_table, { { "&", op_band, "&" } } },
{ eval_table, { { "==", op_eq },
{ "!=", op_ne } } },
- { eval_unary, { { "<=", op_le },
+ { eval_table, { { "<=", op_le },
{ ">=", op_ge },
- { "<", op_lt },
- { ">", op_gt } } }
+ { "<", op_lt, "<=" },
+ { ">", 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 */
@@ -966,6 +1026,26 @@ eval_unary(const struct ops *ops, long *valp, const char **cpp)
*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 == '-') {
+ 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 == '(') {
cp++;
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 char *cp;
- long val;
+ long val = 0;
Linetype lt, rt;
debug("eval%d", prec(ops));
@@ -1050,9 +1130,16 @@ eval_table(const struct ops *ops, long *valp, const char **cpp)
return (LT_ERROR);
for (;;) {
cp = skipcomment(cp);
- for (op = ops->op; op->str != NULL; op++)
- if (strncmp(cp, op->str, strlen(op->str)) == 0)
- break;
+ for (op = ops->op; op->str != NULL; op++) {
+ if (strncmp(cp, op->str, strlen(op->str)) == 0) {
+ /* 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)
break;
cp += strlen(op->str);
@@ -1123,10 +1210,14 @@ skiphash(void)
static const char *
skipline(const char *cp)
{
+ const char *pcp;
if (*cp != '\0')
linestate = LS_DIRTY;
- while (*cp != '\0')
- cp = skipcomment(cp + 1);
+ while (*cp != '\0') {
+ cp = skipcomment(pcp = cp);
+ if (pcp == cp)
+ cp++;
+ }
return (cp);
}
@@ -1202,9 +1293,9 @@ skipcomment(const char *cp)
cp += 2;
} else if (strncmp(cp, "\n", 1) == 0) {
if (incomment == CHAR_LITERAL)
- error("unterminated char literal");
+ error("Unterminated char literal");
else
- error("unterminated string literal");
+ error("Unterminated string literal");
} else
cp += 1;
continue;
@@ -1478,7 +1569,7 @@ defundef(void)
if ((cp = matchsym("define", kw)) != NULL) {
sym = getsym(&cp);
if (sym == NULL)
- error("missing macro name in #define");
+ error("Missing macro name in #define");
if (*cp == '(') {
val = "1";
} else {
@@ -1490,12 +1581,12 @@ defundef(void)
} else if ((cp = matchsym("undef", kw)) != NULL) {
sym = getsym(&cp);
if (sym == NULL)
- error("missing macro name in #undef");
+ error("Missing macro name in #undef");
cp = skipcomment(cp);
debug("#undef");
addsym2(false, sym, NULL);
} else {
- error("unrecognized preprocessor directive");
+ error("Unrecognized preprocessor directive");
}
skipline(cp);
done:
@@ -1567,5 +1658,5 @@ error(const char *msg)
warnx("%s: %d: %s (#if line %d depth %d)",
filename, linenum, msg, stifline[depth], depth);
closeio();
- errx(2, "output may be truncated");
+ errx(2, "Output may be truncated");
}
diff --git a/usr.bin/unifdef/unifdef.h b/usr.bin/unifdef/unifdef.h
index e2e0bd8f3b17..6e58e4f2e262 100644
--- a/usr.bin/unifdef/unifdef.h
+++ b/usr.bin/unifdef/unifdef.h
@@ -36,7 +36,7 @@
#include <string.h>
#include <unistd.h>
-/* portabiity stubs */
+/* portability stubs */
#define fbinmode(fp) (fp)