svn commit: r291696 - head/usr.bin/unifdef

Tony Finch fanf at FreeBSD.org
Thu Dec 3 14:21:57 UTC 2015


Author: fanf
Date: Thu Dec  3 14:21:55 2015
New Revision: 291696
URL: https://svnweb.freebsd.org/changeset/base/291696

Log:
  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

Modified:
  head/usr.bin/unifdef/unifdef.1
  head/usr.bin/unifdef/unifdef.c
  head/usr.bin/unifdef/unifdef.h

Modified: head/usr.bin/unifdef/unifdef.1
==============================================================================
--- head/usr.bin/unifdef/unifdef.1	Thu Dec  3 14:17:28 2015	(r291695)
+++ head/usr.bin/unifdef/unifdef.1	Thu Dec  3 14:21:55 2015	(r291696)
@@ -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 at dotat.at>.  All rights reserved.
+.\" Copyright (c) 2002 - 2015 Tony Finch <dot at 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
 .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)

Modified: head/usr.bin/unifdef/unifdef.c
==============================================================================
--- head/usr.bin/unifdef/unifdef.c	Thu Dec  3 14:17:28 2015	(r291695)
+++ head/usr.bin/unifdef/unifdef.c	Thu Dec  3 14:21:55 2015	(r291696)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002 - 2014 Tony Finch <dot at dotat.at>
+ * Copyright (c) 2002 - 2015 Tony Finch <dot at 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 at dotat.at) $\n"
     "@(#) $URL: http://dotat.at/prog/unifdef $\n"
@@ -208,6 +208,7 @@ static bool             firstsym;		/* di
 
 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
 			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
 		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 = !*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 *
 {
 	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 *
 		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");
 }

Modified: head/usr.bin/unifdef/unifdef.h
==============================================================================
--- head/usr.bin/unifdef/unifdef.h	Thu Dec  3 14:17:28 2015	(r291695)
+++ head/usr.bin/unifdef/unifdef.h	Thu Dec  3 14:21:55 2015	(r291696)
@@ -36,7 +36,7 @@
 #include <string.h>
 #include <unistd.h>
 
-/* portabiity stubs */
+/* portability stubs */
 
 #define fbinmode(fp) (fp)
 


More information about the svn-src-all mailing list