Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

unifdef: update to upstream version 2.5

Fix a long-standing cpp compatibility bug. The -DFOO argument
(without an explicit value) should define FOO to 1 not to the empty
string.

Add a -o option to support overwriting a file in place, and a -S
option to list the nesting depth of symbols. Include line numbers
in debugging output. Support CRLF newlines.

Signed-off-by: Tony Finch <dot@dotat.at>
Signed-off-by: Michal Marek <mmarek@suse.cz>

authored by

Tony Finch and committed by
Michal Marek
3cbea436 c56eb8fb

+184 -63
+184 -63
scripts/unifdef.c
··· 1 1 /* 2 - * Copyright (c) 2002 - 2009 Tony Finch <dot@dotat.at> 2 + * Copyright (c) 2002 - 2011 Tony Finch <dot@dotat.at> 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without 5 5 * modification, are permitted provided that the following conditions ··· 24 24 */ 25 25 26 26 /* 27 + * unifdef - remove ifdef'ed lines 28 + * 27 29 * This code was derived from software contributed to Berkeley by Dave Yost. 28 30 * It was rewritten to support ANSI C by Tony Finch. The original version 29 31 * of unifdef carried the 4-clause BSD copyright licence. None of its code 30 32 * remains in this version (though some of the names remain) so it now 31 33 * carries a more liberal licence. 32 - * 33 - * The latest version is available from http://dotat.at/prog/unifdef 34 - */ 35 - 36 - static const char * const copyright[] = { 37 - "@(#) Copyright (c) 2002 - 2009 Tony Finch <dot@dotat.at>\n", 38 - "$dotat: unifdef/unifdef.c,v 1.190 2009/11/27 17:21:26 fanf2 Exp $", 39 - }; 40 - 41 - /* 42 - * unifdef - remove ifdef'ed lines 43 34 * 44 35 * Wishlist: 45 36 * provide an option which will append the name of the ··· 39 48 * #else's and #endif's to see that they match their 40 49 * corresponding #ifdef or #ifndef 41 50 * 42 - * The first two items above require better buffer handling, which would 43 - * also make it possible to handle all "dodgy" directives correctly. 51 + * These require better buffer handling, which would also make 52 + * it possible to handle all "dodgy" directives correctly. 44 53 */ 54 + 55 + #include <sys/types.h> 56 + #include <sys/stat.h> 45 57 46 58 #include <ctype.h> 47 59 #include <err.h> 60 + #include <errno.h> 48 61 #include <stdarg.h> 49 62 #include <stdbool.h> 50 63 #include <stdio.h> 51 64 #include <stdlib.h> 52 65 #include <string.h> 53 66 #include <unistd.h> 67 + 68 + const char copyright[] = 69 + "@(#) $Version: unifdef-2.5 $\n" 70 + "@(#) $Author: Tony Finch (dot@dotat.at) $\n" 71 + "@(#) $URL: http://dotat.at/prog/unifdef $\n" 72 + ; 54 73 55 74 /* types of input lines: */ 56 75 typedef enum { ··· 154 153 #define EDITSLOP 10 155 154 156 155 /* 156 + * For temporary filenames 157 + */ 158 + #define TEMPLATE "unifdef.XXXXXX" 159 + 160 + /* 157 161 * Globals. 158 162 */ 159 163 ··· 171 165 static bool killconsts; /* -k: eval constant #ifs */ 172 166 static bool lnnum; /* -n: add #line directives */ 173 167 static bool symlist; /* -s: output symbol list */ 168 + static bool symdepth; /* -S: output symbol depth */ 174 169 static bool text; /* -t: this is a text file */ 175 170 176 171 static const char *symname[MAXSYMS]; /* symbol name */ ··· 182 175 static FILE *input; /* input file pointer */ 183 176 static const char *filename; /* input file name */ 184 177 static int linenum; /* current line number */ 178 + static FILE *output; /* output file pointer */ 179 + static const char *ofilename; /* output file name */ 180 + static bool overwriting; /* output overwrites input */ 181 + static char tempname[FILENAME_MAX]; /* used when overwriting */ 185 182 186 183 static char tline[MAXLINE+EDITSLOP];/* input buffer plus space */ 187 184 static char *keyword; /* used for editing #elif's */ 185 + 186 + static const char *newline; /* input file format */ 187 + static const char newline_unix[] = "\n"; 188 + static const char newline_crlf[] = "\r\n"; 188 189 189 190 static Comment_state incomment; /* comment parser state */ 190 191 static Line_state linestate; /* #if line parser state */ ··· 204 189 static unsigned blankcount; /* count of blank lines */ 205 190 static unsigned blankmax; /* maximum recent blankcount */ 206 191 static bool constexpr; /* constant #if expression */ 192 + static bool zerosyms = true; /* to format symdepth output */ 193 + static bool firstsym; /* ditto */ 207 194 208 195 static int exitstat; /* program exit status */ 209 196 210 197 static void addsym(bool, bool, char *); 198 + static void closeout(void); 211 199 static void debug(const char *, ...); 212 200 static void done(void); 213 201 static void error(const char *); ··· 230 212 static int strlcmp(const char *, const char *, size_t); 231 213 static void unnest(void); 232 214 static void usage(void); 215 + static void version(void); 233 216 234 217 #define endsym(c) (!isalnum((unsigned char)c) && c != '_') 235 218 ··· 242 223 { 243 224 int opt; 244 225 245 - while ((opt = getopt(argc, argv, "i:D:U:I:BbcdeKklnst")) != -1) 226 + while ((opt = getopt(argc, argv, "i:D:U:I:o:bBcdeKklnsStV")) != -1) 246 227 switch (opt) { 247 228 case 'i': /* treat stuff controlled by these symbols as text */ 248 229 /* ··· 264 245 case 'U': /* undef a symbol */ 265 246 addsym(false, false, optarg); 266 247 break; 267 - case 'I': 268 - /* no-op for compatibility with cpp */ 269 - break; 270 - case 'B': /* compress blank lines around removed section */ 271 - compblank = true; 248 + case 'I': /* no-op for compatibility with cpp */ 272 249 break; 273 250 case 'b': /* blank deleted lines instead of omitting them */ 274 251 case 'l': /* backwards compatibility */ 275 252 lnblank = true; 253 + break; 254 + case 'B': /* compress blank lines around removed section */ 255 + compblank = true; 276 256 break; 277 257 case 'c': /* treat -D as -U and vice versa */ 278 258 complement = true; ··· 291 273 case 'n': /* add #line directive after deleted lines */ 292 274 lnnum = true; 293 275 break; 276 + case 'o': /* output to a file */ 277 + ofilename = optarg; 278 + break; 294 279 case 's': /* only output list of symbols that control #ifs */ 295 280 symlist = true; 281 + break; 282 + case 'S': /* list symbols with their nesting depth */ 283 + symlist = symdepth = true; 296 284 break; 297 285 case 't': /* don't parse C comments */ 298 286 text = true; 299 287 break; 288 + case 'V': /* print version */ 289 + version(); 300 290 default: 301 291 usage(); 302 292 } ··· 316 290 errx(2, "can only do one file"); 317 291 } else if (argc == 1 && strcmp(*argv, "-") != 0) { 318 292 filename = *argv; 319 - input = fopen(filename, "r"); 293 + input = fopen(filename, "rb"); 320 294 if (input == NULL) 321 295 err(2, "can't open %s", filename); 322 296 } else { 323 297 filename = "[stdin]"; 324 298 input = stdin; 325 299 } 300 + if (ofilename == NULL) { 301 + ofilename = "[stdout]"; 302 + output = stdout; 303 + } else { 304 + struct stat ist, ost; 305 + if (stat(ofilename, &ost) == 0 && 306 + fstat(fileno(input), &ist) == 0) 307 + overwriting = (ist.st_dev == ost.st_dev 308 + && ist.st_ino == ost.st_ino); 309 + if (overwriting) { 310 + const char *dirsep; 311 + int ofd; 312 + 313 + dirsep = strrchr(ofilename, '/'); 314 + if (dirsep != NULL) 315 + snprintf(tempname, sizeof(tempname), 316 + "%.*s/" TEMPLATE, 317 + (int)(dirsep - ofilename), ofilename); 318 + else 319 + snprintf(tempname, sizeof(tempname), 320 + TEMPLATE); 321 + ofd = mkstemp(tempname); 322 + if (ofd != -1) 323 + output = fdopen(ofd, "wb+"); 324 + if (output == NULL) 325 + err(2, "can't create temporary file"); 326 + fchmod(ofd, ist.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)); 327 + } else { 328 + output = fopen(ofilename, "wb"); 329 + if (output == NULL) 330 + err(2, "can't open %s", ofilename); 331 + } 332 + } 326 333 process(); 327 334 abort(); /* bug */ 328 335 } 329 336 330 337 static void 338 + version(void) 339 + { 340 + const char *c = copyright; 341 + for (;;) { 342 + while (*++c != '$') 343 + if (*c == '\0') 344 + exit(0); 345 + while (*++c != '$') 346 + putc(*c, stderr); 347 + putc('\n', stderr); 348 + } 349 + } 350 + 351 + static void 331 352 usage(void) 332 353 { 333 - fprintf(stderr, "usage: unifdef [-BbcdeKknst] [-Ipath]" 354 + fprintf(stderr, "usage: unifdef [-bBcdeKknsStV] [-Ipath]" 334 355 " [-Dsym[=val]] [-Usym] [-iDsym[=val]] [-iUsym] ... [file]\n"); 335 356 exit(2); 336 357 } ··· 395 322 * When we have processed a group that starts off with a known-false 396 323 * #if/#elif sequence (which has therefore been deleted) followed by a 397 324 * #elif that we don't understand and therefore must keep, we edit the 398 - * latter into a #if to keep the nesting correct. 325 + * latter into a #if to keep the nesting correct. We use strncpy() to 326 + * overwrite the 4 byte token "elif" with "if " without a '\0' byte. 399 327 * 400 328 * When we find a true #elif in a group, the following block will 401 329 * always be kept and the rest of the sequence after the next #elif or ··· 449 375 static void Idrop (void) { Fdrop(); ignoreon(); } 450 376 static void Itrue (void) { Ftrue(); ignoreon(); } 451 377 static void Ifalse(void) { Ffalse(); ignoreon(); } 452 - /* edit this line */ 378 + /* modify this line */ 453 379 static void Mpass (void) { strncpy(keyword, "if ", 4); Pelif(); } 454 - static void Mtrue (void) { keywordedit("else\n"); state(IS_TRUE_MIDDLE); } 455 - static void Melif (void) { keywordedit("endif\n"); state(IS_FALSE_TRAILER); } 456 - static void Melse (void) { keywordedit("endif\n"); state(IS_FALSE_ELSE); } 380 + static void Mtrue (void) { keywordedit("else"); state(IS_TRUE_MIDDLE); } 381 + static void Melif (void) { keywordedit("endif"); state(IS_FALSE_TRAILER); } 382 + static void Melse (void) { keywordedit("endif"); state(IS_FALSE_ELSE); } 457 383 458 384 static state_fn * const trans_table[IS_COUNT][LT_COUNT] = { 459 385 /* IS_OUTSIDE */ ··· 505 431 * State machine utility functions 506 432 */ 507 433 static void 508 - done(void) 509 - { 510 - if (incomment) 511 - error("EOF in comment"); 512 - exit(exitstat); 513 - } 514 - static void 515 434 ignoreoff(void) 516 435 { 517 436 if (depth == 0) ··· 519 452 static void 520 453 keywordedit(const char *replacement) 521 454 { 522 - size_t size = tline + sizeof(tline) - keyword; 523 - char *dst = keyword; 524 - const char *src = replacement; 525 - if (size != 0) { 526 - while ((--size != 0) && (*src != '\0')) 527 - *dst++ = *src++; 528 - *dst = '\0'; 529 - } 455 + snprintf(keyword, tline + sizeof(tline) - keyword, 456 + "%s%s", replacement, newline); 530 457 print(); 531 458 } 532 459 static void ··· 555 494 if (symlist) 556 495 return; 557 496 if (keep ^ complement) { 558 - bool blankline = tline[strspn(tline, " \t\n")] == '\0'; 497 + bool blankline = tline[strspn(tline, " \t\r\n")] == '\0'; 559 498 if (blankline && compblank && blankcount != blankmax) { 560 499 delcount += 1; 561 500 blankcount += 1; 562 501 } else { 563 502 if (lnnum && delcount > 0) 564 - printf("#line %d\n", linenum); 565 - fputs(tline, stdout); 503 + printf("#line %d%s", linenum, newline); 504 + fputs(tline, output); 566 505 delcount = 0; 567 506 blankmax = blankcount = blankline ? blankcount + 1 : 0; 568 507 } 569 508 } else { 570 509 if (lnblank) 571 - putc('\n', stdout); 510 + fputs(newline, output); 572 511 exitstat = 1; 573 512 delcount += 1; 574 513 blankcount = 0; 575 514 } 515 + if (debugging) 516 + fflush(output); 576 517 } 577 518 578 519 /* ··· 583 520 static void 584 521 process(void) 585 522 { 586 - Linetype lineval; 587 - 588 523 /* When compressing blank lines, act as if the file 589 524 is preceded by a large number of blank lines. */ 590 525 blankmax = blankcount = 1000; 591 526 for (;;) { 592 - linenum++; 593 - lineval = parseline(); 527 + Linetype lineval = parseline(); 594 528 trans_table[ifstate[depth]][lineval](); 595 - debug("process %s -> %s depth %d", 596 - linetype_name[lineval], 529 + debug("process line %d %s -> %s depth %d", 530 + linenum, linetype_name[lineval], 597 531 ifstate_name[ifstate[depth]], depth); 598 532 } 533 + } 534 + 535 + /* 536 + * Flush the output and handle errors. 537 + */ 538 + static void 539 + closeout(void) 540 + { 541 + if (symdepth && !zerosyms) 542 + printf("\n"); 543 + if (fclose(output) == EOF) { 544 + warn("couldn't write to %s", ofilename); 545 + if (overwriting) { 546 + unlink(tempname); 547 + errx(2, "%s unchanged", filename); 548 + } else { 549 + exit(2); 550 + } 551 + } 552 + } 553 + 554 + /* 555 + * Clean up and exit. 556 + */ 557 + static void 558 + done(void) 559 + { 560 + if (incomment) 561 + error("EOF in comment"); 562 + closeout(); 563 + if (overwriting && rename(tempname, ofilename) == -1) { 564 + warn("couldn't rename temporary file"); 565 + unlink(tempname); 566 + errx(2, "%s unchanged", ofilename); 567 + } 568 + exit(exitstat); 599 569 } 600 570 601 571 /* ··· 645 549 Linetype retval; 646 550 Comment_state wascomment; 647 551 552 + linenum++; 648 553 if (fgets(tline, MAXLINE, input) == NULL) 649 554 return (LT_EOF); 555 + if (newline == NULL) { 556 + if (strrchr(tline, '\n') == strrchr(tline, '\r') + 1) 557 + newline = newline_crlf; 558 + else 559 + newline = newline_unix; 560 + } 650 561 retval = LT_PLAIN; 651 562 wascomment = incomment; 652 563 cp = skipcomment(tline); 653 564 if (linestate == LS_START) { 654 565 if (*cp == '#') { 655 566 linestate = LS_HASH; 567 + firstsym = true; 656 568 cp = skipcomment(cp + 1); 657 569 } else if (*cp != '\0') 658 570 linestate = LS_DIRTY; ··· 670 566 cp = skipsym(cp); 671 567 kwlen = cp - keyword; 672 568 /* no way can we deal with a continuation inside a keyword */ 673 - if (strncmp(cp, "\\\n", 2) == 0) 569 + if (strncmp(cp, "\\\r\n", 3) == 0 || 570 + strncmp(cp, "\\\n", 2) == 0) 674 571 Eioccc(); 675 572 if (strlcmp("ifdef", keyword, kwlen) == 0 || 676 573 strlcmp("ifndef", keyword, kwlen) == 0) { ··· 722 617 size_t len = cp - tline; 723 618 if (fgets(tline + len, MAXLINE - len, input) == NULL) { 724 619 /* append the missing newline */ 725 - tline[len+0] = '\n'; 726 - tline[len+1] = '\0'; 727 - cp++; 620 + strcpy(tline + len, newline); 621 + cp += strlen(newline); 728 622 linestate = LS_START; 729 623 } else { 730 624 linestate = LS_DIRTY; ··· 734 630 while (*cp != '\0') 735 631 cp = skipcomment(cp + 1); 736 632 } 737 - debug("parser %s comment %s line", 633 + debug("parser line %d state %s comment %s line", linenum, 738 634 comment_name[incomment], linestate_name[linestate]); 739 635 return (retval); 740 636 } ··· 979 875 } 980 876 while (*cp != '\0') 981 877 /* don't reset to LS_START after a line continuation */ 982 - if (strncmp(cp, "\\\n", 2) == 0) 878 + if (strncmp(cp, "\\\r\n", 3) == 0) 879 + cp += 3; 880 + else if (strncmp(cp, "\\\n", 2) == 0) 983 881 cp += 2; 984 882 else switch (incomment) { 985 883 case NO_COMMENT: 986 - if (strncmp(cp, "/\\\n", 3) == 0) { 884 + if (strncmp(cp, "/\\\r\n", 4) == 0) { 885 + incomment = STARTING_COMMENT; 886 + cp += 4; 887 + } else if (strncmp(cp, "/\\\n", 3) == 0) { 987 888 incomment = STARTING_COMMENT; 988 889 cp += 3; 989 890 } else if (strncmp(cp, "/*", 2) == 0) { ··· 1008 899 } else if (strncmp(cp, "\n", 1) == 0) { 1009 900 linestate = LS_START; 1010 901 cp += 1; 1011 - } else if (strchr(" \t", *cp) != NULL) { 902 + } else if (strchr(" \r\t", *cp) != NULL) { 1012 903 cp += 1; 1013 904 } else 1014 905 return (cp); ··· 1040 931 cp += 1; 1041 932 continue; 1042 933 case C_COMMENT: 1043 - if (strncmp(cp, "*\\\n", 3) == 0) { 934 + if (strncmp(cp, "*\\\r\n", 4) == 0) { 935 + incomment = FINISHING_COMMENT; 936 + cp += 4; 937 + } else if (strncmp(cp, "*\\\n", 3) == 0) { 1044 938 incomment = FINISHING_COMMENT; 1045 939 cp += 3; 1046 940 } else if (strncmp(cp, "*/", 2) == 0) { ··· 1127 1015 if (cp == str) 1128 1016 return (-1); 1129 1017 if (symlist) { 1130 - printf("%.*s\n", (int)(cp-str), str); 1018 + if (symdepth && firstsym) 1019 + printf("%s%3d", zerosyms ? "" : "\n", depth); 1020 + firstsym = zerosyms = false; 1021 + printf("%s%.*s%s", 1022 + symdepth ? " " : "", 1023 + (int)(cp-str), str, 1024 + symdepth ? "" : "\n"); 1131 1025 /* we don't care about the value of the symbol */ 1132 1026 return (0); 1133 1027 } ··· 1170 1052 value[symind] = val+1; 1171 1053 *val = '\0'; 1172 1054 } else if (*val == '\0') 1173 - value[symind] = ""; 1055 + value[symind] = "1"; 1174 1056 else 1175 1057 usage(); 1176 1058 } else { ··· 1178 1060 usage(); 1179 1061 value[symind] = NULL; 1180 1062 } 1063 + debug("addsym %s=%s", symname[symind], 1064 + value[symind] ? value[symind] : "undef"); 1181 1065 } 1182 1066 1183 1067 /* ··· 1220 1100 else 1221 1101 warnx("%s: %d: %s (#if line %d depth %d)", 1222 1102 filename, linenum, msg, stifline[depth], depth); 1103 + closeout(); 1223 1104 errx(2, "output may be truncated"); 1224 1105 }