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 . All rights reserved. +.\" Copyright (c) 2002 - 2015 Tony Finch . 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) 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 + * Copyright (c) 2002 - 2015 Tony Finch * * 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 #include -/* portabiity stubs */ +/* portability stubs */ #define fbinmode(fp) (fp)