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:
parent
aacc26e329
commit
ef4eec8a6d
@ -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
|
||||
Obfuscated preprocessor control line.
|
||||
Missing macro name in #define or #undef
|
||||
.It
|
||||
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)
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -36,7 +36,7 @@
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* portabiity stubs */
|
||||
/* portability stubs */
|
||||
|
||||
#define fbinmode(fp) (fp)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user