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:
Tony Finch 2015-12-03 14:21:55 +00:00
parent aacc26e329
commit ef4eec8a6d
3 changed files with 149 additions and 31 deletions

View File

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

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
* 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");
}

View File

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