46 #include <sys/types.h>
60 "@(#) $Version: unifdef-2.5 $\n"
62 "@(#) $URL: http://dotat.at/prog/unifdef $\n"
85 static char const *
const linetype_name[] = {
86 "TRUEI",
"FALSEI",
"IF",
"TRUE",
"FALSE",
87 "ELIF",
"ELTRUE",
"ELFALSE",
"ELSE",
"ENDIF",
88 "DODGY TRUEI",
"DODGY FALSEI",
89 "DODGY IF",
"DODGY TRUE",
"DODGY FALSE",
90 "DODGY ELIF",
"DODGY ELTRUE",
"DODGY ELFALSE",
91 "DODGY ELSE",
"DODGY ENDIF",
92 "PLAIN",
"EOF",
"ERROR"
110 static char const *
const ifstate_name[] = {
111 "OUTSIDE",
"FALSE_PREFIX",
"TRUE_PREFIX",
112 "PASS_MIDDLE",
"FALSE_MIDDLE",
"TRUE_MIDDLE",
113 "PASS_ELSE",
"FALSE_ELSE",
"TRUE_ELSE",
128 static char const *
const comment_name[] = {
129 "NO",
"C",
"CXX",
"STARTING",
"FINISHING",
"CHAR",
"STRING"
139 static char const *
const linestate_name[] = {
140 "START",
"HASH",
"DIRTY"
159 #define TEMPLATE "unifdef.XXXXXX"
165 static bool compblank;
167 static bool complement;
168 static bool debugging;
170 static bool strictlogic;
171 static bool killconsts;
174 static bool symdepth;
177 static const char *symname[
MAXSYMS];
186 static const char *ofilename;
187 static bool overwriting;
188 static char tempname[FILENAME_MAX];
191 static char *keyword;
193 static const char *newline;
194 static const char newline_unix[] =
"\n";
195 static const char newline_crlf[] =
"\r\n";
204 static unsigned blankcount;
205 static unsigned blankmax;
206 static bool constexpr;
207 static bool zerosyms =
true;
208 static bool firstsym;
212 static void addsym(
bool,
bool,
char *);
213 static void closeout(
void);
214 static void debug(
const char *, ...);
215 static void done(
void);
216 static void error(
const char *);
217 static int findsym(
const char *);
218 static void flushline(
bool);
220 static Linetype ifeval(
const char **);
221 static void ignoreoff(
void);
222 static void ignoreon(
void);
223 static void keywordedit(
const char *);
224 static void nest(
void);
225 static void process(
void);
226 static const char *skipargs(
const char *);
227 static const char *skipcomment(
const char *);
228 static const char *skipsym(
const char *);
230 static int strlcmp(
const char *,
const char *,
size_t);
231 static void unnest(
void);
232 static void usage(
void);
235 #define endsym(c) (!isalnum((unsigned char)c) && c != '_')
245 while ((opt = getopt(argc, argv,
"i:D:U:I:o:bBcdeKklnsStV")) != -1)
255 addsym(
true,
true, optarg);
257 addsym(
true,
false, optarg);
262 addsym(
false,
true, optarg);
265 addsym(
false,
false, optarg);
301 symlist = symdepth =
true;
313 if (compblank && lnblank)
314 errx(2,
"-B and -b are mutually exclusive");
316 errx(2,
"can only do one file");
317 }
else if (argc == 1 &&
strcmp(*argv,
"-") != 0) {
326 if (ofilename ==
NULL) {
327 ofilename =
"[stdout]";
330 struct stat ist, ost;
331 if (
stat(ofilename, &ost) == 0 &&
332 fstat(fileno(
input), &ist) == 0)
339 dirsep =
strrchr(ofilename,
'/');
341 snprintf(tempname,
sizeof(tempname),
343 (
int)(dirsep - ofilename), ofilename);
345 snprintf(tempname,
sizeof(tempname),
347 ofd = mkstemp(tempname);
349 output = fdopen(ofd,
"wb+");
351 err(2,
"can't create temporary file");
354 output = fopen(ofilename,
"wb");
356 err(2,
"can't open %s", ofilename);
380 fprintf(stderr,
"usage: unifdef [-bBcdeKknsStV] [-Ipath]"
381 " [-Dsym[=val]] [-Usym] [-iDsym[=val]] [-iUsym] ... [file]\n");
418 static void Eelif (
void) {
error(
"Inappropriate #elif"); }
419 static void Eelse (
void) {
error(
"Inappropriate #else"); }
420 static void Eendif(
void) {
error(
"Inappropriate #endif"); }
421 static void Eeof (
void) {
error(
"Premature EOF"); }
422 static void Eioccc(
void) {
error(
"Obfuscated preprocessor control line"); }
424 static void print (
void) { flushline(
true); }
425 static void drop (
void) { flushline(
false); }
433 static void Pendif(
void) { print(); unnest(); }
438 static void Dendif(
void) { drop(); unnest(); }
440 static void Fdrop (
void) { nest(); Dfalse(); }
441 static void Fpass (
void) { nest(); Pelif(); }
442 static void Ftrue (
void) { nest(); Strue(); }
443 static void Ffalse(
void) { nest(); Sfalse(); }
445 static void Oiffy (
void) {
if (!iocccok) Eioccc(); Fpass(); ignoreon(); }
446 static void Oif (
void) {
if (!iocccok) Eioccc(); Fpass(); }
447 static void Oelif (
void) {
if (!iocccok) Eioccc(); Pelif(); }
449 static void Idrop (
void) { Fdrop(); ignoreon(); }
450 static void Itrue (
void) { Ftrue(); ignoreon(); }
451 static void Ifalse(
void) { Ffalse(); ignoreon(); }
453 static void Mpass (
void) {
strncpy(keyword,
"if ", 4); Pelif(); }
460 { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Eelif, Eelif, Eelse, Eendif,
461 Oiffy, Oiffy, Fpass, Oif, Oif, Eelif, Eelif, Eelif, Eelse, Eendif,
464 { Idrop, Idrop, Fdrop, Fdrop, Fdrop, Mpass, Strue, Sfalse,Selse, Dendif,
465 Idrop, Idrop, Fdrop, Fdrop, Fdrop, Mpass, Eioccc,Eioccc,Eioccc,Eioccc,
468 { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Dfalse,Dfalse,Dfalse,Delse, Dendif,
469 Oiffy, Oiffy, Fpass, Oif, Oif, Eioccc,Eioccc,Eioccc,Eioccc,Eioccc,
470 print, Eeof,
abort },
472 { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Pelif, Mtrue, Delif, Pelse, Pendif,
473 Oiffy, Oiffy, Fpass, Oif, Oif, Pelif, Oelif, Oelif, Pelse, Pendif,
474 print, Eeof,
abort },
476 { Idrop, Idrop, Fdrop, Fdrop, Fdrop, Pelif, Mtrue, Delif, Pelse, Pendif,
477 Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eioccc,Eioccc,Eioccc,Eioccc,Eioccc,
480 { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Melif, Melif, Melif, Melse, Pendif,
481 Oiffy, Oiffy, Fpass, Oif, Oif, Eioccc,Eioccc,Eioccc,Eioccc,Pendif,
482 print, Eeof,
abort },
484 { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Eelif, Eelif, Eelse, Pendif,
485 Oiffy, Oiffy, Fpass, Oif, Oif, Eelif, Eelif, Eelif, Eelse, Pendif,
486 print, Eeof,
abort },
488 { Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eelif, Eelif, Eelif, Eelse, Dendif,
489 Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eelif, Eelif, Eelif, Eelse, Eioccc,
492 { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Eelif, Eelif, Eelse, Dendif,
493 Oiffy, Oiffy, Fpass, Oif, Oif, Eelif, Eelif, Eelif, Eelse, Eioccc,
494 print, Eeof,
abort },
496 { Idrop, Idrop, Fdrop, Fdrop, Fdrop, Dfalse,Dfalse,Dfalse,Delse, Dendif,
497 Idrop, Idrop, Fdrop, Fdrop, Fdrop, Dfalse,Dfalse,Dfalse,Delse, Eioccc,
517 ignoring[
depth] =
true;
520 keywordedit(
const char *replacement)
522 snprintf(keyword, tline +
sizeof(tline) - keyword,
523 "%s%s", replacement, newline);
532 error(
"Too many levels of nesting");
534 stifline[
depth] = linenum;
557 if (keep ^ complement) {
558 bool blankline = tline[
strspn(tline,
" \t\r\n")] ==
'\0';
559 if (blankline && compblank && blankcount != blankmax) {
563 if (lnnum && delcount > 0)
564 printf(
"#line %d%s", linenum, newline);
565 fputs(tline, output);
567 blankmax = blankcount = blankline ? blankcount + 1 : 0;
571 fputs(newline, output);
588 blankmax = blankcount = 1000;
591 trans_table[ifstate[
depth]][lineval]();
592 debug(
"process line %d %s -> %s depth %d",
593 linenum, linetype_name[lineval],
594 ifstate_name[ifstate[
depth]], depth);
604 if (symdepth && !zerosyms)
606 if (fclose(output) ==
EOF) {
607 warn(
"couldn't write to %s", ofilename);
624 error(
"EOF in comment");
626 if (overwriting && rename(tempname, ofilename) == -1) {
627 warn(
"couldn't rename temporary file");
629 errx(2,
"%s unchanged", ofilename);
651 if (newline ==
NULL) {
653 newline = newline_crlf;
655 newline = newline_unix;
658 wascomment = incomment;
659 cp = skipcomment(tline);
664 cp = skipcomment(cp + 1);
665 }
else if (*cp !=
'\0')
668 if (!incomment && linestate ==
LS_HASH) {
669 keyword = tline + (cp - tline);
671 kwlen = cp - keyword;
673 if (
strncmp(cp,
"\\\r\n", 3) == 0 ||
676 if (strlcmp(
"ifdef", keyword, kwlen) == 0 ||
677 strlcmp(
"ifndef", keyword, kwlen) == 0) {
678 cp = skipcomment(cp);
679 if ((cursym = findsym(cp)) < 0)
682 retval = (keyword[2] ==
'n')
692 }
else if (strlcmp(
"if", keyword, kwlen) == 0)
693 retval = ifeval(&cp);
694 else if (strlcmp(
"elif", keyword, kwlen) == 0)
696 else if (strlcmp(
"else", keyword, kwlen) == 0)
698 else if (strlcmp(
"endif", keyword, kwlen) == 0)
704 cp = skipcomment(cp);
713 if (retval !=
LT_PLAIN && (wascomment || incomment)) {
722 size_t len = cp - tline;
725 strcpy(tline + len, newline);
735 cp = skipcomment(cp + 1);
737 debug(
"parser line %d state %s comment %s line", linenum,
738 comment_name[incomment], linestate_name[linestate]);
751 return op_strict(p, a < b, at, bt);
754 return op_strict(p, a > b, at, bt);
757 return op_strict(p, a <= b, at, bt);
760 return op_strict(p, a >= b, at, bt);
763 return op_strict(p, a == b, at, bt);
766 return op_strict(p, a != b, at, bt);
771 return op_strict(p, a || b, at, bt);
776 return op_strict(p, a && b, at, bt);
793 static eval_fn eval_table, eval_unary;
802 static const struct ops {
809 { eval_table, { {
"||", op_or } } },
810 { eval_table, { {
"&&", op_and } } },
811 { eval_table, { {
"==", op_eq },
813 { eval_unary, { {
"<=", op_le },
825 eval_unary(
const struct ops *ops,
int *valp,
const char **
cpp)
833 cp = skipcomment(*cpp);
835 debug(
"eval%d !", ops - eval_ops);
837 lt = eval_unary(ops, valp, &cp);
844 }
else if (*cp ==
'(') {
846 debug(
"eval%d (", ops - eval_ops);
847 lt = eval_table(eval_ops, valp, &cp);
850 cp = skipcomment(cp);
853 }
else if (
isdigit((
unsigned char)*cp)) {
854 debug(
"eval%d number", ops - eval_ops);
855 *valp = strtol(cp, &ep, 0);
860 }
else if (
strncmp(cp,
"defined", 7) == 0 &&
endsym(cp[7])) {
861 cp = skipcomment(cp+7);
862 debug(
"eval%d defined", ops - eval_ops);
864 cp = skipcomment(cp+1);
877 cp = skipcomment(cp);
878 if (defparen && *cp++ !=
')')
881 }
else if (!
endsym(*cp)) {
882 debug(
"eval%d symbol", ops - eval_ops);
892 *valp = strtol(
value[sym], &ep, 0);
893 if (*ep !=
'\0' || ep ==
value[sym])
900 debug(
"eval%d bad expr", ops - eval_ops);
905 debug(
"eval%d = %d", ops - eval_ops, *valp);
913 eval_table(
const struct ops *ops,
int *valp,
const char **cpp)
920 debug(
"eval%d", ops - eval_ops);
922 lt = ops->inner(ops+1, valp, &cp);
926 cp = skipcomment(cp);
927 for (op = ops->op; op->str !=
NULL; op++)
933 debug(
"eval%d %s", ops - eval_ops, op->str);
934 rt = ops->inner(ops+1, &val, &cp);
937 lt = op->
fn(valp, lt, *valp, rt, val);
941 debug(
"eval%d = %d", ops - eval_ops, *valp);
942 debug(
"eval%d lt = %s", ops - eval_ops, linetype_name[lt]);
952 ifeval(
const char **cpp)
957 debug(
"eval %s", *cpp);
958 constexpr = killconsts ?
false :
true;
959 ret = eval_table(eval_ops, &val, cpp);
960 debug(
"eval = %d", val);
972 skipcomment(
const char *cp)
975 for (;
isspace((
unsigned char)*cp); cp++)
982 if (
strncmp(cp,
"\\\r\n", 3) == 0)
984 else if (
strncmp(cp,
"\\\n", 2) == 0)
986 else switch (incomment) {
988 if (
strncmp(cp,
"/\\\r\n", 4) == 0) {
991 }
else if (
strncmp(cp,
"/\\\n", 3) == 0) {
994 }
else if (
strncmp(cp,
"/*", 2) == 0) {
997 }
else if (
strncmp(cp,
"//", 2) == 0) {
1000 }
else if (
strncmp(cp,
"\'", 1) == 0) {
1004 }
else if (
strncmp(cp,
"\"", 1) == 0) {
1008 }
else if (
strncmp(cp,
"\n", 1) == 0) {
1017 if (
strncmp(cp,
"\n", 1) == 0) {
1029 }
else if (cp[0] ==
'\\') {
1034 }
else if (
strncmp(cp,
"\n", 1) == 0) {
1036 error(
"unterminated char literal");
1038 error(
"unterminated string literal");
1043 if (
strncmp(cp,
"*\\\r\n", 4) == 0) {
1046 }
else if (
strncmp(cp,
"*\\\n", 3) == 0) {
1049 }
else if (
strncmp(cp,
"*/", 2) == 0) {
1059 }
else if (*cp ==
'/') {
1084 skipargs(
const char *cp)
1086 const char *ocp =
cp;
1088 cp = skipcomment(cp);
1096 cp = skipcomment(cp+1);
1097 }
while (level != 0 && *cp !=
'\0');
1109 skipsym(
const char *cp)
1121 findsym(
const char *
str)
1130 if (symdepth && firstsym)
1131 printf(
"%s%3d", zerosyms ?
"" :
"\n", depth);
1132 firstsym = zerosyms =
false;
1134 symdepth ?
" " :
"",
1136 symdepth ?
"" :
"\n");
1140 for (symind = 0; symind < nsyms; ++symind) {
1141 if (strlcmp(symname[symind], str, cp-str) == 0) {
1142 debug(
"findsym %s %s", symname[symind],
1154 addsym(
bool ignorethis,
bool definethis,
char *sym)
1159 symind = findsym(sym);
1162 errx(2,
"too many symbols");
1165 symname[symind] = sym;
1166 ignore[symind] = ignorethis;
1167 val = sym + (skipsym(sym) - sym);
1170 value[symind] = val+1;
1172 }
else if (*val ==
'\0')
1173 value[symind] =
"1";
1181 debug(
"addsym %s=%s", symname[symind],
1190 strlcmp(
const char *
s,
const char *
t,
size_t n)
1192 while (n-- && *t !=
'\0')
1194 return ((
unsigned char)*s - (
unsigned char)*t);
1197 return ((
unsigned char)*s);
1216 error(
const char *msg)
1219 warnx(
"%s: %d: %s",
filename, linenum, msg);
1221 warnx(
"%s: %d: %s (#if line %d depth %d)",
1222 filename, linenum, msg, stifline[depth], depth);
1224 errx(2,
"output may be truncated");