From 31f5980bd9badb3694af7b0abfe66b6b08f43f9f Mon Sep 17 00:00:00 2001 From: Tony Finch Date: Thu, 28 Mar 2013 20:33:07 +0000 Subject: [PATCH] Update to upstream version 2.7 The most notable new feature is support for processing multiple files in one invocation. There is also support for more make-friendly exit statuses. The most notable bug fix is #line directives now include the input file name. Obtained from: http://dotat.at/prog/unifdef --- usr.bin/unifdef/unifdef.1 | 200 ++++++++++-------- usr.bin/unifdef/unifdef.c | 384 ++++++++++++++++++++++------------ usr.bin/unifdef/unifdef.h | 50 +++++ usr.bin/unifdef/unifdefall.sh | 2 +- 4 files changed, 418 insertions(+), 218 deletions(-) create mode 100644 usr.bin/unifdef/unifdef.h diff --git a/usr.bin/unifdef/unifdef.1 b/usr.bin/unifdef/unifdef.1 index 6497d36bd080..d362eaf197e4 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 - 2010 Tony Finch . All rights reserved. +.\" Copyright (c) 2002 - 2013 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,23 +31,23 @@ .\" .\" $FreeBSD$ .\" -.Dd March 11, 2010 -.Dt UNIFDEF 1 -.Os +.Dd February 21, 2012 +.Dt UNIFDEF 1 PRM +.Os " " .Sh NAME .Nm unifdef , unifdefall .Nd remove preprocessor conditionals from code .Sh SYNOPSIS .Nm -.Op Fl bBcdeKknsStV +.Op Fl bBcdehKkmnsStV .Op Fl I Ns Ar path -.Op Fl D Ns Ar sym Ns Op = Ns Ar val -.Op Fl U Ns Ar sym -.Op Fl iD Ns Ar sym Ns Op = Ns Ar val -.Op Fl iU Ns Ar sym +.Op Fl [i]D Ns Ar sym Ns Op = Ns Ar val +.Op Fl [i]U Ns Ar sym .Ar ... +.Op Fl x Bro Ar 012 Brc +.Op Fl M Ar backext .Op Fl o Ar outfile -.Op Ar infile +.Op Ar infile ... .Nm unifdefall .Op Fl I Ns Ar path .Ar ... @@ -66,7 +66,8 @@ while otherwise leaving the file alone. The .Nm utility acts on -.Ic #if , #ifdef , #ifndef , #elif , #else , +.Ic #if , #ifdef , #ifndef , +.Ic #elif , #else , and .Ic #endif lines. @@ -114,7 +115,9 @@ the .Fn defined operator, the operators -.Ic \&! , < , > , <= , >= , == , != , && , || , +.Ic \&! , < , > , +.Ic <= , >= , == , != , +.Ic && , || , and parenthesized expressions. A kind of .Dq "short circuit" @@ -181,6 +184,18 @@ Specify that a symbol is undefined. If the same symbol appears in more than one argument, the last occurrence dominates. .Pp +.It Fl iD Ns Ar sym Ns Op = Ns Ar val +.It Fl iU Ns Ar sym +C strings, comments, +and line continuations +are ignored within +.Ic #ifdef +and +.Ic #ifndef +blocks +controlled by symbols +specified with these options. +.Pp .It Fl b Replace removed lines with blank lines instead of deleting them. @@ -195,35 +210,39 @@ Mutually exclusive with the option. .Pp .It Fl c -If the -.Fl c -flag is specified, -then the operation of -.Nm -is complemented, -i.e., the lines that would have been removed or blanked +Complement, +i.e., lines that would have been removed or blanked are retained and vice versa. .Pp .It Fl d Turn on printing of debugging messages. .Pp .It Fl e -Because -.Nm -processes its input one line at a time, -it cannot remove preprocessor directives that span more than one line. -The most common example of this is a directive with a multi-line -comment hanging off its right hand end. By default, -if .Nm -has to process such a directive, -it will complain that the line is too obfuscated. +will report an error if it needs to remove +a preprocessor directive that spans more than one line, +for example, if it has a multi-line +comment hanging off its right hand end. The .Fl e -option changes the behaviour so that, -where possible, -such lines are left unprocessed instead of reporting an error. +flag makes it ignore the line instead. +.Pp +.It Fl h +Print help. +.Pp +.It Fl I Ns Ar path +Specifies to +.Nm unifdefall +an additional place to look for +.Ic #include +files. +This option is ignored by +.Nm +for compatibility with +.Xr cpp 1 +and to simplify the implementation of +.Nm unifdefall . .Pp .It Fl K Always treat the result of @@ -247,6 +266,15 @@ because they typically start and are used as a kind of comment to sketch out future or past development. 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. +.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. +.Pp .It Fl n Add .Li #line @@ -257,17 +285,10 @@ line numbers in the input file. .It Fl o Ar outfile Write output to the file .Ar outfile -instead of the standard output. -If -.Ar outfile -is the same as the input file, -the output is written to a temporary file -which is renamed into place when -.Nm -completes successfully. +instead of the standard output when processing a single file. .Pp .It Fl s -Instead of processing the input file as usual, +Instead of processing an input file as usual, this option causes .Nm to produce a list of symbols that appear in expressions @@ -290,63 +311,40 @@ This is useful for working out the number of possible combinations of interdependent defined/undefined symbols. .Pp .It Fl t -Disables parsing for C comments +Disables parsing for C strings, comments, and line continuations, which is useful for plain text. -.Pp -.It Fl iD Ns Ar sym Ns Op = Ns Ar val -.It Fl iU Ns Ar sym -Ignore -.Ic #ifdef Ns s . -If your C code uses -.Ic #ifdef Ns s -to delimit non-C lines, -such as comments -or code which is under construction, -then you must tell -.Nm -which symbols are used for that purpose so that it will not try to parse -comments -and line continuations -inside those -.Ic #ifdef Ns s . -You can specify ignored symbols with -.Fl iD Ns Ar sym Ns Oo = Ns Ar val Oc +This is a blanket version of the +.Fl iD and -.Fl iU Ns Ar sym -similar to -.Fl D Ns Ar sym Ns Op = Ns Ar val -and -.Fl U Ns Ar sym -above. -.Pp -.It Fl I Ns Ar path -Specifies to -.Nm unifdefall -an additional place to look for -.Ic #include -files. -This option is ignored by -.Nm -for compatibility with -.Xr cpp 1 -and to simplify the implementation of -.Nm unifdefall . +.Fl iU +flags. .Pp .It Fl V Print version details. +.Pp +.It Fl x Bro Ar 012 Brc +Set exit status mode to zero, one, or two. +See the +.Sx EXIT STATUS +section below for details. .El .Pp The .Nm -utility copies its output to -.Em stdout -and will take its input from +utility takes its input from .Em stdin -if no +if there are no .Ar file -argument is given. +arguments. +You must use the +.Fl m +or +.Fl M +options if there are multiple input files. +You can specify inut from stdin or output to stdout with +.Ql - . .Pp The .Nm @@ -355,10 +353,35 @@ utility works nicely with the option of .Xr diff 1 . .Sh EXIT STATUS -The +In normal usage the .Nm -utility exits 0 if the output is an exact copy of the input, -1 if not, and 2 if in trouble. +utility's exit status depends on the mode set using the +.Fl x +option. +.Pp +If the exit mode is zero (the default) then +.Nm +exits with status 0 if the output is an exact copy of the input, +or with status 1 if the output differs. +.Pp +If the exit mode is one, +.Nm +exits with status 1 if the output is unmodified +or 0 if it differs. +.Pp +If the exit mode is two, +.Nm +exits with status zero in both cases. +.Pp +In all exit modes, +.Nm +exits with status 2 if there is an error. +.Pp +The exit status is 0 if the +.Fl h +or +.Fl V +command line options are given. .Sh DIAGNOSTICS .Bl -item .It @@ -400,7 +423,8 @@ rewrote it to support .Sh BUGS Expression evaluation is very limited. .Pp -Preprocessor control lines split across more than one physical line +Handling one line at a time means +preprocessor directives split across more than one physical line (because of comments or backslash-newline) cannot be handled in every situation. .Pp diff --git a/usr.bin/unifdef/unifdef.c b/usr.bin/unifdef/unifdef.c index 8f7ed0fd1c27..22f81efa00be 100644 --- a/usr.bin/unifdef/unifdef.c +++ b/usr.bin/unifdef/unifdef.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002 - 2011 Tony Finch + * Copyright (c) 2002 - 2013 Tony Finch * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -43,21 +43,10 @@ * it possible to handle all "dodgy" directives correctly. */ -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "unifdef.h" static const char copyright[] = - "@(#) $Version: unifdef-2.5.6.21f1388 $\n" + "@(#) $Version: unifdef-2.7 $\n" "@(#) $FreeBSD$\n" "@(#) $Author: Tony Finch (dot@dotat.at) $\n" "@(#) $URL: http://dotat.at/prog/unifdef $\n" @@ -93,6 +82,9 @@ static char const * const linetype_name[] = { "PLAIN", "EOF", "ERROR" }; +#define linetype_if2elif(lt) ((Linetype)(lt - LT_IF + LT_ELIF)) +#define linetype_2dodgy(lt) ((Linetype)(lt + LT_DODGY)) + /* state of #if processing */ typedef enum { IS_OUTSIDE, @@ -154,11 +146,6 @@ static char const * const linestate_name[] = { */ #define EDITSLOP 10 -/* - * For temporary filenames - */ -#define TEMPLATE "unifdef.XXXXXX" - /* * Globals. */ @@ -167,6 +154,7 @@ static bool compblank; /* -B: compress blank lines */ static bool lnblank; /* -b: blank deleted lines */ static bool complement; /* -c: do the complement */ static bool debugging; /* -d: debugging reports */ +static bool inplace; /* -m: modify in place */ static bool iocccok; /* -e: fewer IOCCC errors */ static bool strictlogic; /* -K: keep ambiguous #ifs */ static bool killconsts; /* -k: eval constant #ifs */ @@ -183,10 +171,11 @@ static int nsyms; /* number of symbols */ static FILE *input; /* input file pointer */ static const char *filename; /* input file name */ static int linenum; /* current line number */ +static const char *linefile; /* file name for #line */ static FILE *output; /* output file pointer */ static const char *ofilename; /* output file name */ -static bool overwriting; /* output overwrites input */ -static char tempname[FILENAME_MAX]; /* used when overwriting */ +static const char *backext; /* backup extension */ +static char *tempname; /* avoid splatting input */ static char tline[MAXLINE+EDITSLOP];/* input buffer plus space */ static char *keyword; /* used for editing #elif's */ @@ -205,25 +194,31 @@ static int delcount; /* count of deleted lines */ static unsigned blankcount; /* count of blank lines */ static unsigned blankmax; /* maximum recent blankcount */ static bool constexpr; /* constant #if expression */ -static bool zerosyms = true; /* to format symdepth output */ +static bool zerosyms; /* to format symdepth output */ static bool firstsym; /* ditto */ +static int exitmode; /* exit status mode */ static int exitstat; /* program exit status */ static void addsym(bool, bool, char *); +static char *astrcat(const char *, const char *); +static void cleantemp(void); static void closeout(void); static void debug(const char *, ...); static void done(void); static void error(const char *); static int findsym(const char *); static void flushline(bool); -static Linetype parseline(void); +static void hashline(void); +static void help(void); static Linetype ifeval(const char **); static void ignoreoff(void); static void ignoreon(void); static void keywordedit(const char *); static void nest(void); +static Linetype parseline(void); static void process(void); +static void processinout(const char *, const char *); static const char *skipargs(const char *); static const char *skipcomment(const char *); static const char *skipsym(const char *); @@ -243,7 +238,7 @@ main(int argc, char *argv[]) { int opt; - while ((opt = getopt(argc, argv, "i:D:U:I:o:bBcdeKklnsStV")) != -1) + while ((opt = getopt(argc, argv, "i:D:U:I:M:o:x:bBcdehKklmnsStV")) != -1) switch (opt) { case 'i': /* treat stuff controlled by these symbols as text */ /* @@ -283,12 +278,22 @@ main(int argc, char *argv[]) case 'e': /* fewer errors from dodgy lines */ iocccok = true; break; + case 'h': + help(); + break; case 'K': /* keep ambiguous #ifs */ strictlogic = true; break; case 'k': /* process constant #ifs */ killconsts = true; break; + case 'm': /* modify in place */ + inplace = true; + break; + case 'M': /* modify in place and keep backup */ + inplace = true; + backext = optarg; + break; case 'n': /* add #line directive after deleted lines */ lnnum = true; break; @@ -304,8 +309,14 @@ main(int argc, char *argv[]) case 't': /* don't parse C comments */ text = true; break; - case 'V': /* print version */ + case 'V': version(); + break; + case 'x': + exitmode = atoi(optarg); + if(exitmode < 0 || exitmode > 2) + usage(); + break; default: usage(); } @@ -313,54 +324,97 @@ main(int argc, char *argv[]) argv += optind; if (compblank && lnblank) errx(2, "-B and -b are mutually exclusive"); - if (argc > 1) { - errx(2, "can only do one file"); - } else if (argc == 1 && strcmp(*argv, "-") != 0) { - filename = *argv; - input = fopen(filename, "rb"); - if (input == NULL) - err(2, "can't open %s", filename); - } else { - filename = "[stdin]"; - input = stdin; - } - if (ofilename == NULL) { - ofilename = "[stdout]"; - output = stdout; - } else { - struct stat ist, ost; - if (stat(ofilename, &ost) == 0 && - fstat(fileno(input), &ist) == 0) - overwriting = (ist.st_dev == ost.st_dev - && ist.st_ino == ost.st_ino); - if (overwriting) { - const char *dirsep; - int ofd; + if (symlist && (ofilename != NULL || inplace || argc > 1)) + errx(2, "-s only works with one input file"); + if (argc > 1 && ofilename != NULL) + 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) + argc = 1; + if (argc == 1 && !inplace && ofilename == NULL) + ofilename = "-"; - dirsep = strrchr(ofilename, '/'); - if (dirsep != NULL) - snprintf(tempname, sizeof(tempname), - "%.*s/" TEMPLATE, - (int)(dirsep - ofilename), ofilename); - else - snprintf(tempname, sizeof(tempname), - TEMPLATE); - ofd = mkstemp(tempname); - if (ofd != -1) - output = fdopen(ofd, "wb+"); - if (output == NULL) - err(2, "can't create temporary file"); - fchmod(ofd, ist.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)); - } else { - output = fopen(ofilename, "wb"); - if (output == NULL) - err(2, "can't open %s", ofilename); - } + atexit(cleantemp); + if (ofilename != NULL) + processinout(*argv, ofilename); + else while (argc-- > 0) { + processinout(*argv, *argv); + argv++; + } + switch(exitmode) { + case(0): exit(exitstat); + case(1): exit(!exitstat); + case(2): exit(0); + default: abort(); /* bug */ } - process(); - abort(); /* bug */ } +/* + * File logistics. + */ +static void +processinout(const char *ifn, const char *ofn) +{ + struct stat st; + + if (ifn == NULL || strcmp(ifn, "-") == 0) { + filename = "[stdin]"; + linefile = NULL; + input = fbinmode(stdin); + } else { + filename = ifn; + linefile = ifn; + input = fopen(ifn, "rb"); + if (input == NULL) + err(2, "can't open %s", ifn); + } + if (strcmp(ofn, "-") == 0) { + output = fbinmode(stdout); + process(); + return; + } + if (stat(ofn, &st) < 0) { + output = fopen(ofn, "wb"); + if (output == NULL) + err(2, "can't create %s", ofn); + process(); + return; + } + + tempname = astrcat(ofn, ".XXXXXX"); + output = mktempmode(tempname, st.st_mode); + if (output == NULL) + err(2, "can't create %s", tempname); + + process(); + + if (backext != NULL) { + char *backname = astrcat(ofn, backext); + if (rename(ofn, backname) < 0) + err(2, "can't rename \"%s\" to \"%s%s\"", ofn, ofn, backext); + free(backname); + } + if (rename(tempname, ofn) < 0) + err(2, "can't rename \"%s\" to \"%s\"", tempname, ofn); + free(tempname); + tempname = NULL; +} + +/* + * For cleaning up if there is an error. + */ +static void +cleantemp(void) +{ + if (tempname != NULL) + remove(tempname); +} + +/* + * Self-identification functions. + */ + static void version(void) { @@ -375,14 +429,54 @@ version(void) } } +static void +synopsis(FILE *fp) +{ + fprintf(fp, + "usage: unifdef [-bBcdehKkmnsStV] [-x{012}] [-Mext] [-opath] \\\n" + " [-[i]Dsym[=val]] [-[i]Usym] ... [file] ...\n"); +} + static void usage(void) { - fprintf(stderr, "usage: unifdef [-bBcdeKknsStV] [-Ipath]" - " [-Dsym[=val]] [-Usym] [-iDsym[=val]] [-iUsym] ... [file]\n"); + synopsis(stderr); exit(2); } +static void +help(void) +{ + synopsis(stdout); + printf( + " -Dsym=val define preprocessor symbol with given value\n" + " -Dsym define preprocessor symbol with value 1\n" + " -Usym preprocessor symbol is undefined\n" + " -iDsym=val \\ ignore C strings and comments\n" + " -iDsym ) in sections controlled by these\n" + " -iUsym / preprocessor symbols\n" + " -b blank lines instead of deleting them\n" + " -B compress blank lines around deleted section\n" + " -c complement (invert) keep vs. delete\n" + " -d debugging mode\n" + " -e ignore multiline preprocessor directives\n" + " -h print help\n" + " -Ipath extra include file path (ignored)\n" + " -K disable && and || short-circuiting\n" + " -k process constant #if expressions\n" + " -Mext modify in place and keep backups\n" + " -m modify input files in place\n" + " -n add #line directives to output\n" + " -opath output file name\n" + " -S list #if control symbols with nesting\n" + " -s list #if control symbols\n" + " -t ignore C strings and comments\n" + " -V print version\n" + " -x{012} exit status mode\n" + ); + exit(0); +} + /* * A state transition function alters the global #if processing state * in a particular way. The table below is indexed by the current @@ -396,7 +490,7 @@ usage(void) * When we have processed a group that starts off with a known-false * #if/#elif sequence (which has therefore been deleted) followed by a * #elif that we don't understand and therefore must keep, we edit the - * latter into a #if to keep the nesting correct. We use strncpy() to + * latter into a #if to keep the nesting correct. We use memcpy() to * overwrite the 4 byte token "elif" with "if " without a '\0' byte. * * When we find a true #elif in a group, the following block will @@ -451,7 +545,7 @@ static void Idrop (void) { Fdrop(); ignoreon(); } static void Itrue (void) { Ftrue(); ignoreon(); } static void Ifalse(void) { Ffalse(); ignoreon(); } /* modify this line */ -static void Mpass (void) { strncpy(keyword, "if ", 4); Pelif(); } +static void Mpass (void) { memcpy(keyword, "if ", 4); Pelif(); } static void Mtrue (void) { keywordedit("else"); state(IS_TRUE_MIDDLE); } static void Melif (void) { keywordedit("endif"); state(IS_FALSE_TRAILER); } static void Melse (void) { keywordedit("endif"); state(IS_FALSE_ELSE); } @@ -547,6 +641,18 @@ state(Ifstate is) ifstate[depth] = is; } +/* + * The last state transition function. When this is called, + * lineval == LT_EOF, so the process() loop will terminate. + */ +static void +done(void) +{ + if (incomment) + error("EOF in comment"); + closeout(); +} + /* * Write a line to the output or not, according to command line options. * If writing fails, closeout() will print the error and exit. @@ -562,9 +668,8 @@ flushline(bool keep) delcount += 1; blankcount += 1; } else { - if (lnnum && delcount > 0 && - fprintf(output, "#line %d%s", linenum, newline) < 0) - closeout(); + if (lnnum && delcount > 0) + hashline(); if (fputs(tline, output) == EOF) closeout(); delcount = 0; @@ -582,21 +687,20 @@ flushline(bool keep) } /* - * The driver for the state machine. + * Format of #line directives depends on whether we know the input filename. */ static void -process(void) +hashline(void) { - /* When compressing blank lines, act as if the file - is preceded by a large number of blank lines. */ - blankmax = blankcount = 1000; - for (;;) { - Linetype lineval = parseline(); - trans_table[ifstate[depth]][lineval](); - debug("process line %d %s -> %s depth %d", - linenum, linetype_name[lineval], - ifstate_name[ifstate[depth]], depth); - } + int e; + + if (linefile == NULL) + e = fprintf(output, "#line %d%s", linenum, newline); + else + e = fprintf(output, "#line %d \"%s\"%s", + linenum, linefile, newline); + if (e < 0) + closeout(); } /* @@ -605,34 +709,31 @@ process(void) static void closeout(void) { + /* Tidy up after findsym(). */ if (symdepth && !zerosyms) printf("\n"); - if (ferror(output) || fclose(output) == EOF) { - if (overwriting) { - warn("couldn't write to temporary file"); - unlink(tempname); - errx(2, "%s unchanged", ofilename); - } else { - err(2, "couldn't write to %s", ofilename); - } - } + if (ferror(output) || fclose(output) == EOF) + err(2, "%s: can't write to output", filename); } /* - * Clean up and exit. + * The driver for the state machine. */ static void -done(void) +process(void) { - if (incomment) - error("EOF in comment"); - closeout(); - if (overwriting && rename(tempname, ofilename) == -1) { - warn("couldn't rename temporary file"); - unlink(tempname); - errx(2, "%s unchanged", ofilename); + Linetype lineval = LT_PLAIN; + /* When compressing blank lines, act as if the file + is preceded by a large number of blank lines. */ + blankmax = blankcount = 1000; + zerosyms = true; + while (lineval != LT_EOF) { + lineval = parseline(); + trans_table[ifstate[depth]][lineval](); + debug("process line %d %s -> %s depth %d", + linenum, linetype_name[lineval], + ifstate_name[ifstate[depth]], depth); } - exit(exitstat); } /* @@ -652,7 +753,7 @@ parseline(void) linenum++; if (fgets(tline, MAXLINE, input) == NULL) { if (ferror(input)) - error(strerror(errno)); + err(2, "can't read %s", filename); else return (LT_EOF); } @@ -700,7 +801,7 @@ parseline(void) } else if (strlcmp("if", keyword, kwlen) == 0) retval = ifeval(&cp); else if (strlcmp("elif", keyword, kwlen) == 0) - retval = ifeval(&cp) - LT_IF + LT_ELIF; + retval = linetype_if2elif(ifeval(&cp)); else if (strlcmp("else", keyword, kwlen) == 0) retval = LT_ELSE; else if (strlcmp("endif", keyword, kwlen) == 0) @@ -719,7 +820,7 @@ parseline(void) retval = LT_ELIF; } if (retval != LT_PLAIN && (wascomment || incomment)) { - retval += LT_DODGY; + retval = linetype_2dodgy(retval); if (incomment) linestate = LS_DIRTY; } @@ -730,7 +831,7 @@ parseline(void) size_t len = cp - tline; if (fgets(tline + len, MAXLINE - len, input) == NULL) { if (ferror(input)) - error(strerror(errno)); + err(2, "can't read %s", filename); /* append the missing newline at eof */ strcpy(tline + len, newline); cp += strlen(newline); @@ -809,13 +910,15 @@ static eval_fn eval_table, eval_unary; * element of the table. Innermost expressions have special non-table-driven * handling. */ -static const struct ops { +struct op { + const char *str; + Linetype (*fn)(int *, Linetype, int, Linetype, int); +}; +struct ops { eval_fn *inner; - struct op { - const char *str; - Linetype (*fn)(int *, Linetype, int, Linetype, int); - } op[5]; -} eval_ops[] = { + struct op op[5]; +}; +static const struct ops eval_ops[] = { { eval_table, { { "||", op_or } } }, { eval_table, { { "&&", op_and } } }, { eval_table, { { "==", op_eq }, @@ -826,6 +929,12 @@ static const struct ops { { ">", op_gt } } } }; +/* Current operator precedence level */ +static int prec(const struct ops *ops) +{ + return (ops - eval_ops); +} + /* * Function for evaluating the innermost parts of expressions, * viz. !expr (expr) number defined(symbol) symbol @@ -842,7 +951,7 @@ eval_unary(const struct ops *ops, int *valp, const char **cpp) cp = skipcomment(*cpp); if (*cp == '!') { - debug("eval%d !", ops - eval_ops); + debug("eval%d !", prec(ops)); cp++; lt = eval_unary(ops, valp, &cp); if (lt == LT_ERROR) @@ -853,7 +962,7 @@ eval_unary(const struct ops *ops, int *valp, const char **cpp) } } else if (*cp == '(') { cp++; - debug("eval%d (", ops - eval_ops); + debug("eval%d (", prec(ops)); lt = eval_table(eval_ops, valp, &cp); if (lt == LT_ERROR) return (LT_ERROR); @@ -861,7 +970,7 @@ eval_unary(const struct ops *ops, int *valp, const char **cpp) if (*cp++ != ')') return (LT_ERROR); } else if (isdigit((unsigned char)*cp)) { - debug("eval%d number", ops - eval_ops); + debug("eval%d number", prec(ops)); *valp = strtol(cp, &ep, 0); if (ep == cp) return (LT_ERROR); @@ -869,7 +978,7 @@ eval_unary(const struct ops *ops, int *valp, const char **cpp) cp = skipsym(cp); } else if (strncmp(cp, "defined", 7) == 0 && endsym(cp[7])) { cp = skipcomment(cp+7); - debug("eval%d defined", ops - eval_ops); + debug("eval%d defined", prec(ops)); if (*cp == '(') { cp = skipcomment(cp+1); defparen = true; @@ -889,7 +998,7 @@ eval_unary(const struct ops *ops, int *valp, const char **cpp) return (LT_ERROR); constexpr = false; } else if (!endsym(*cp)) { - debug("eval%d symbol", ops - eval_ops); + debug("eval%d symbol", prec(ops)); sym = findsym(cp); cp = skipsym(cp); if (sym < 0) { @@ -907,12 +1016,12 @@ eval_unary(const struct ops *ops, int *valp, const char **cpp) } constexpr = false; } else { - debug("eval%d bad expr", ops - eval_ops); + debug("eval%d bad expr", prec(ops)); return (LT_ERROR); } *cpp = cp; - debug("eval%d = %d", ops - eval_ops, *valp); + debug("eval%d = %d", prec(ops), *valp); return (lt); } @@ -927,7 +1036,7 @@ eval_table(const struct ops *ops, int *valp, const char **cpp) int val; Linetype lt, rt; - debug("eval%d", ops - eval_ops); + debug("eval%d", prec(ops)); cp = *cpp; lt = ops->inner(ops+1, valp, &cp); if (lt == LT_ERROR) @@ -940,7 +1049,7 @@ eval_table(const struct ops *ops, int *valp, const char **cpp) if (op->str == NULL) break; cp += strlen(op->str); - debug("eval%d %s", ops - eval_ops, op->str); + debug("eval%d %s", prec(ops), op->str); rt = ops->inner(ops+1, &val, &cp); if (rt == LT_ERROR) return (LT_ERROR); @@ -948,8 +1057,8 @@ eval_table(const struct ops *ops, int *valp, const char **cpp) } *cpp = cp; - debug("eval%d = %d", ops - eval_ops, *valp); - debug("eval%d lt = %s", ops - eval_ops, linetype_name[lt]); + debug("eval%d = %d", prec(ops), *valp); + debug("eval%d lt = %s", prec(ops), linetype_name[lt]); return (lt); } @@ -961,7 +1070,7 @@ eval_table(const struct ops *ops, int *valp, const char **cpp) static Linetype ifeval(const char **cpp) { - int ret; + Linetype ret; int val = 0; debug("eval %s", *cpp); @@ -1207,6 +1316,23 @@ strlcmp(const char *s, const char *t, size_t n) return ((unsigned char)*s); } +/* + * Concatenate two strings into new memory, checking for failure. + */ +static char * +astrcat(const char *s1, const char *s2) +{ + char *s; + int len; + + len = 1 + snprintf(NULL, 0, "%s%s", s1, s2); + s = (char *)malloc(len); + if (s == NULL) + err(2, "malloc"); + snprintf(s, len, "%s%s", s1, s2); + return (s); +} + /* * Diagnostics. */ diff --git a/usr.bin/unifdef/unifdef.h b/usr.bin/unifdef/unifdef.h new file mode 100644 index 000000000000..8837b68bf6fd --- /dev/null +++ b/usr.bin/unifdef/unifdef.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2012 Tony Finch + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* portabiity stubs */ + +#define fbinmode(fp) (fp) + +static FILE * +mktempmode(char *tmp, int mode) +{ + int fd = mkstemp(tmp); + if (fd < 0) return (NULL); + fchmod(fd, mode & (S_IRWXU|S_IRWXG|S_IRWXO)); + return (fdopen(fd, "wb")); +} diff --git a/usr.bin/unifdef/unifdefall.sh b/usr.bin/unifdef/unifdefall.sh index 36442b934c87..6c8614b53932 100644 --- a/usr.bin/unifdef/unifdefall.sh +++ b/usr.bin/unifdef/unifdefall.sh @@ -2,7 +2,7 @@ # # unifdefall: remove all the #if's from a source file # -# Copyright (c) 2002 - 2010 Tony Finch +# Copyright (c) 2002 - 2013 Tony Finch # Copyright (c) 2009 - 2010 Jonathan Nieder # # Redistribution and use in source and binary forms, with or without