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
This commit is contained in:
Tony Finch 2013-03-28 20:33:07 +00:00
parent 03a9b7c085
commit 31f5980bd9
4 changed files with 418 additions and 218 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 - 2010 Tony Finch <dot@dotat.at>. All rights reserved.
.\" Copyright (c) 2002 - 2013 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,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

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002 - 2011 Tony Finch <dot@dotat.at>
* Copyright (c) 2002 - 2013 Tony Finch <dot@dotat.at>
*
* 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 <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#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.
*/

50
usr.bin/unifdef/unifdef.h Normal file
View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2012 Tony Finch <dot@dotat.at>
*
* 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 <sys/stat.h>
#include <ctype.h>
#include <err.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/* 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"));
}

View File

@ -2,7 +2,7 @@
#
# unifdefall: remove all the #if's from a source file
#
# Copyright (c) 2002 - 2010 Tony Finch <dot@dotat.at>
# Copyright (c) 2002 - 2013 Tony Finch <dot@dotat.at>
# Copyright (c) 2009 - 2010 Jonathan Nieder <jrnieder@gmail.com>
#
# Redistribution and use in source and binary forms, with or without