move pcc into /usr/old
[unix-history] / usr / src / usr.bin / unifdef / unifdef.c
CommitLineData
dc533d04
KB
1/*
2 * Copyright (c) 1985 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Dave Yost.
7 *
6ecf3d85 8 * %sccs.include.redist.c%
dc533d04
KB
9 */
10
476fcd16 11#ifndef lint
dc533d04
KB
12char copyright[] =
13"@(#) Copyright (c) 1985 The Regents of the University of California.\n\
14 All rights reserved.\n";
15#endif /* not lint */
16
17#ifndef lint
6ecf3d85 18static char sccsid[] = "@(#)unifdef.c 4.7 (Berkeley) %G%";
dc533d04 19#endif /* not lint */
ba2c33c2 20
476fcd16
SL
21/*
22 * unifdef - remove ifdef'ed lines
f42d2b57
KB
23 *
24 * Warning: will not work correctly if input contains null characters.
25 *
26 * Wishlist:
27 * provide an option which will append the name of the
28 * appropriate symbol after #else's and #endif's
29 * provide an option which will check symbols after
30 * #else's and #endif's to see that they match their
31 * corresponding #ifdef or #ifndef
476fcd16
SL
32 */
33
ba2c33c2
KM
34#include <stdio.h>
35#include <ctype.h>
dc533d04 36
ba2c33c2
KM
37#define BSS
38FILE *input;
39#ifndef YES
40#define YES 1
41#define NO 0
f42d2b57
KB
42#endif/*YES */
43typedef int Bool;
ba2c33c2
KM
44
45char *progname BSS;
46char *filename BSS;
47char text BSS; /* -t option in effect: this is a text file */
48char lnblank BSS; /* -l option in effect: blank deleted lines */
49char complement BSS; /* -c option in effect: complement the operation */
f42d2b57 50
ba2c33c2 51#define MAXSYMS 100
f42d2b57
KB
52char *symname[MAXSYMS] BSS; /* symbol name */
53char true[MAXSYMS] BSS; /* -Dsym */
54char ignore[MAXSYMS] BSS; /* -iDsym or -iUsym */
55char insym[MAXSYMS] BSS; /* state: false, inactive, true */
56#define SYM_INACTIVE 0 /* symbol is currently inactive */
57#define SYM_FALSE 1 /* symbol is currently false */
58#define SYM_TRUE 2 /* symbol is currently true */
59
ba2c33c2 60char nsyms BSS;
f42d2b57
KB
61char incomment BSS; /* inside C comment */
62
63#define QUOTE_NONE 0
64#define QUOTE_SINGLE 1
65#define QUOTE_DOUBLE 2
66char inquote BSS; /* inside single or double quotes */
67
ba2c33c2
KM
68int exitstat BSS;
69char *skipcomment ();
70char *skipquote ();
71
72main (argc, argv)
73int argc;
74char **argv;
75{
76 char **curarg;
77 register char *cp;
78 register char *cp1;
79 char ignorethis;
80
81 progname = argv[0][0] ? argv[0] : "unifdef";
82
83 for (curarg = &argv[1]; --argc > 0; curarg++) {
84 if (*(cp1 = cp = *curarg) != '-')
85 break;
86 if (*++cp1 == 'i') {
87 ignorethis = YES;
88 cp1++;
f42d2b57 89 } else
ba2c33c2 90 ignorethis = NO;
535a99db
SL
91 if ( ( *cp1 == 'D'
92 || *cp1 == 'U'
ba2c33c2
KM
93 )
94 && cp1[1] != '\0'
95 ) {
f42d2b57
KB
96 register int symind;
97
98 if ((symind = findsym (&cp1[1])) < 0) {
99 if (nsyms >= MAXSYMS) {
100 prname ();
101 fprintf (stderr, "too many symbols.\n");
102 exit (2);
103 }
104 symind = nsyms++;
105 symname[symind] = &cp1[1];
106 insym[symind] = SYM_INACTIVE;
ba2c33c2 107 }
f42d2b57
KB
108 ignore[symind] = ignorethis;
109 true[symind] = *cp1 == 'D' ? YES : NO;
110 } else if (ignorethis)
ba2c33c2
KM
111 goto unrec;
112 else if (strcmp (&cp[1], "t") == 0)
113 text = YES;
114 else if (strcmp (&cp[1], "l") == 0)
115 lnblank = YES;
116 else if (strcmp (&cp[1], "c") == 0)
117 complement = YES;
118 else {
119 unrec:
120 prname ();
121 fprintf (stderr, "unrecognized option: %s\n", cp);
122 goto usage;
123 }
124 }
125 if (nsyms == 0) {
126 usage:
127 fprintf (stderr, "\
f42d2b57
KB
128Usage: %s [-l] [-t] [-c] [[-Dsym] [-Usym] [-iDsym] [-iUsym]]... [file]\n\
129 At least one arg from [-D -U -iD -iU] is required\n", progname);
ba2c33c2
KM
130 exit (2);
131 }
132
133 if (argc > 1) {
134 prname ();
135 fprintf (stderr, "can only do one file.\n");
f42d2b57 136 } else if (argc == 1) {
ba2c33c2
KM
137 filename = *curarg;
138 if ((input = fopen (filename, "r")) != NULL) {
139 pfile();
f42d2b57
KB
140 (void) fclose (input);
141 } else {
ba2c33c2 142 prname ();
f42d2b57 143 fprintf (stderr, "can't open ");
6404bb88 144 perror(*curarg);
ba2c33c2 145 }
f42d2b57 146 } else {
ba2c33c2
KM
147 filename = "[stdin]";
148 input = stdin;
149 pfile();
150 }
151
f42d2b57 152 (void) fflush (stdout);
ba2c33c2
KM
153 exit (exitstat);
154}
155
156/* types of input lines: */
f42d2b57
KB
157typedef int Linetype;
158#define LT_PLAIN 0 /* ordinary line */
159#define LT_TRUE 1 /* a true #ifdef of a symbol known to us */
160#define LT_FALSE 2 /* a false #ifdef of a symbol known to us */
161#define LT_OTHER 3 /* an #ifdef of a symbol not known to us */
162#define LT_IF 4 /* an #ifdef of a symbol not known to us */
163#define LT_ELSE 5 /* #else */
164#define LT_ENDIF 6 /* #endif */
165#define LT_LEOF 7 /* end of file */
166extern Linetype checkline ();
167
168typedef int Reject_level;
169Reject_level reject BSS; /* 0 or 1: pass thru; 1 or 2: ignore comments */
170#define REJ_NO 0
171#define REJ_IGNORE 1
172#define REJ_YES 2
173
ba2c33c2
KM
174int linenum BSS; /* current line number */
175int stqcline BSS; /* start of current coment or quote */
176char *errs[] = {
177#define NO_ERR 0
178 "",
179#define END_ERR 1
180 "",
181#define ELSE_ERR 2
182 "Inappropriate else",
183#define ENDIF_ERR 3
184 "Inappropriate endif",
185#define IEOF_ERR 4
186 "Premature EOF in ifdef",
187#define CEOF_ERR 5
188 "Premature EOF in comment",
189#define Q1EOF_ERR 6
190 "Premature EOF in quoted character",
191#define Q2EOF_ERR 7
192 "Premature EOF in quoted string"
193};
194
f42d2b57
KB
195/* States for inif arg to doif */
196#define IN_NONE 0
197#define IN_IF 1
198#define IN_ELSE 2
199
ba2c33c2
KM
200pfile ()
201{
f42d2b57
KB
202 reject = REJ_NO;
203 (void) doif (-1, IN_NONE, reject, 0);
ba2c33c2
KM
204 return;
205}
206
f42d2b57 207int
ba2c33c2
KM
208doif (thissym, inif, prevreject, depth)
209register int thissym; /* index of the symbol who was last ifdef'ed */
210int inif; /* YES or NO we are inside an ifdef */
f42d2b57 211Reject_level prevreject;/* previous value of reject */
ba2c33c2
KM
212int depth; /* depth of ifdef's */
213{
f42d2b57
KB
214 register Linetype lineval;
215 register Reject_level thisreject;
216 int doret; /* tmp return value of doif */
ba2c33c2
KM
217 int cursym; /* index of the symbol returned by checkline */
218 int stline; /* line number when called this time */
219
220 stline = linenum;
221 for (;;) {
222 switch (lineval = checkline (&cursym)) {
f42d2b57 223 case LT_PLAIN:
ba2c33c2
KM
224 flushline (YES);
225 break;
226
f42d2b57
KB
227 case LT_TRUE:
228 case LT_FALSE:
ba2c33c2 229 thisreject = reject;
f42d2b57
KB
230 if (lineval == LT_TRUE)
231 insym[cursym] = SYM_TRUE;
ba2c33c2 232 else {
f42d2b57
KB
233 if (reject != REJ_YES)
234 reject = ignore[cursym] ? REJ_IGNORE : REJ_YES;
235 insym[cursym] = SYM_FALSE;
ba2c33c2
KM
236 }
237 if (ignore[cursym])
238 flushline (YES);
239 else {
240 exitstat = 1;
241 flushline (NO);
242 }
f42d2b57 243 if ((doret = doif (cursym, IN_IF, thisreject, depth + 1)) != NO_ERR)
ba2c33c2 244 return error (doret, stline, depth);
f42d2b57 245 break;
ba2c33c2 246
f42d2b57
KB
247 case LT_IF:
248 case LT_OTHER:
ba2c33c2 249 flushline (YES);
f42d2b57 250 if ((doret = doif (-1, IN_IF, reject, depth + 1)) != NO_ERR)
ba2c33c2
KM
251 return error (doret, stline, depth);
252 break;
253
f42d2b57
KB
254 case LT_ELSE:
255 if (inif != IN_IF)
ba2c33c2 256 return error (ELSE_ERR, linenum, depth);
f42d2b57 257 inif = IN_ELSE;
ba2c33c2 258 if (thissym >= 0) {
f42d2b57
KB
259 if (insym[thissym] == SYM_TRUE) {
260 reject = ignore[thissym] ? REJ_IGNORE : REJ_YES;
261 insym[thissym] = SYM_FALSE;
262 } else { /* (insym[thissym] == SYM_FALSE) */
ba2c33c2 263 reject = prevreject;
f42d2b57
KB
264 insym[thissym] = SYM_TRUE;
265 }
ba2c33c2
KM
266 if (!ignore[thissym]) {
267 flushline (NO);
268 break;
269 }
270 }
271 flushline (YES);
272 break;
273
f42d2b57
KB
274 case LT_ENDIF:
275 if (inif == IN_NONE)
ba2c33c2
KM
276 return error (ENDIF_ERR, linenum, depth);
277 if (thissym >= 0) {
f42d2b57 278 insym[thissym] = SYM_INACTIVE;
ba2c33c2
KM
279 reject = prevreject;
280 if (!ignore[thissym]) {
281 flushline (NO);
282 return NO_ERR;
283 }
284 }
285 flushline (YES);
286 return NO_ERR;
287
f42d2b57 288 case LT_LEOF: {
ba2c33c2
KM
289 int err;
290 err = incomment
291 ? CEOF_ERR
f42d2b57 292 : inquote == QUOTE_SINGLE
ba2c33c2 293 ? Q1EOF_ERR
f42d2b57 294 : inquote == QUOTE_DOUBLE
ba2c33c2
KM
295 ? Q2EOF_ERR
296 : NO_ERR;
f42d2b57 297 if (inif != IN_NONE) {
ba2c33c2 298 if (err != NO_ERR)
f42d2b57 299 (void) error (err, stqcline, depth);
ba2c33c2 300 return error (IEOF_ERR, stline, depth);
f42d2b57 301 } else if (err != NO_ERR)
ba2c33c2
KM
302 return error (err, stqcline, depth);
303 else
304 return NO_ERR;
305 }
306 }
307 }
308}
309
310#define endsym(c) (!isalpha (c) && !isdigit (c) && c != '_')
311
312#define MAXLINE 256
313char tline[MAXLINE] BSS;
314
f42d2b57 315Linetype
ba2c33c2 316checkline (cursym)
f42d2b57 317int *cursym; /* if LT_TRUE or LT_FALSE returned, set this to sym index */
ba2c33c2
KM
318{
319 register char *cp;
320 register char *symp;
ba2c33c2 321 char *scp;
f42d2b57 322 Linetype retval;
ba2c33c2
KM
323# define KWSIZE 8
324 char keyword[KWSIZE];
325
326 linenum++;
327 if (getlin (tline, sizeof tline, input, NO) == EOF)
f42d2b57 328 return LT_LEOF;
ba2c33c2 329
f42d2b57 330 retval = LT_PLAIN;
ba2c33c2
KM
331 if ( *(cp = tline) != '#'
332 || incomment
f42d2b57
KB
333 || inquote == QUOTE_SINGLE
334 || inquote == QUOTE_DOUBLE
ba2c33c2
KM
335 )
336 goto eol;
337
338 cp = skipcomment (++cp);
339 symp = keyword;
340 while (!endsym (*cp)) {
341 *symp = *cp++;
342 if (++symp >= &keyword[KWSIZE])
343 goto eol;
344 }
345 *symp = '\0';
346
347 if (strcmp (keyword, "ifdef") == 0) {
348 retval = YES;
349 goto ifdef;
f42d2b57 350 } else if (strcmp (keyword, "ifndef") == 0) {
ba2c33c2
KM
351 retval = NO;
352 ifdef:
353 scp = cp = skipcomment (++cp);
354 if (incomment) {
f42d2b57 355 retval = LT_PLAIN;
ba2c33c2
KM
356 goto eol;
357 }
f42d2b57
KB
358 {
359 int symind;
360
361 if ((symind = findsym (scp)) >= 0)
362 retval = (retval ^ true[*cursym = symind])
363 ? LT_FALSE : LT_TRUE;
364 else
365 retval = LT_OTHER;
ba2c33c2 366 }
f42d2b57
KB
367 } else if (strcmp (keyword, "if") == 0)
368 retval = LT_IF;
ba2c33c2 369 else if (strcmp (keyword, "else") == 0)
f42d2b57 370 retval = LT_ELSE;
ba2c33c2 371 else if (strcmp (keyword, "endif") == 0)
f42d2b57 372 retval = LT_ENDIF;
ba2c33c2
KM
373
374 eol:
f42d2b57 375 if (!text && reject != REJ_IGNORE)
ba2c33c2
KM
376 for (; *cp; ) {
377 if (incomment)
378 cp = skipcomment (cp);
f42d2b57
KB
379 else if (inquote == QUOTE_SINGLE)
380 cp = skipquote (cp, QUOTE_SINGLE);
381 else if (inquote == QUOTE_DOUBLE)
382 cp = skipquote (cp, QUOTE_DOUBLE);
ba2c33c2
KM
383 else if (*cp == '/' && cp[1] == '*')
384 cp = skipcomment (cp);
385 else if (*cp == '\'')
f42d2b57 386 cp = skipquote (cp, QUOTE_SINGLE);
ba2c33c2 387 else if (*cp == '"')
f42d2b57 388 cp = skipquote (cp, QUOTE_DOUBLE);
ba2c33c2
KM
389 else
390 cp++;
391 }
392 return retval;
393}
394
f42d2b57
KB
395/*
396 * Skip over comments and stop at the next charaacter
397 * position that is not whitespace.
398 */
ba2c33c2
KM
399char *
400skipcomment (cp)
401register char *cp;
402{
403 if (incomment)
404 goto inside;
405 for (;; cp++) {
f42d2b57
KB
406 while (*cp == ' ' || *cp == '\t')
407 cp++;
ba2c33c2 408 if (text)
f42d2b57 409 return cp;
ba2c33c2
KM
410 if ( cp[0] != '/'
411 || cp[1] != '*'
412 )
f42d2b57 413 return cp;
ba2c33c2
KM
414 cp += 2;
415 if (!incomment) {
416 incomment = YES;
417 stqcline = linenum;
418 }
419 inside:
420 for (;;) {
421 for (; *cp != '*'; cp++)
422 if (*cp == '\0')
423 return cp;
f42d2b57
KB
424 if (*++cp == '/') {
425 incomment = NO;
ba2c33c2 426 break;
f42d2b57 427 }
ba2c33c2 428 }
ba2c33c2
KM
429 }
430}
431
f42d2b57
KB
432/*
433 * Skip over a quoted string or character and stop at the next charaacter
434 * position that is not whitespace.
435 */
ba2c33c2
KM
436char *
437skipquote (cp, type)
438register char *cp;
439register int type;
440{
441 register char qchar;
442
f42d2b57 443 qchar = type == QUOTE_SINGLE ? '\'' : '"';
ba2c33c2 444
f42d2b57 445 if (inquote == type)
ba2c33c2
KM
446 goto inside;
447 for (;; cp++) {
448 if (*cp != qchar)
449 return cp;
450 cp++;
f42d2b57
KB
451 inquote = type;
452 stqcline = linenum;
ba2c33c2
KM
453 inside:
454 for (; ; cp++) {
455 if (*cp == qchar)
456 break;
457 if ( *cp == '\0'
f42d2b57 458 || *cp == '\\' && *++cp == '\0'
ba2c33c2
KM
459 )
460 return cp;
461 }
f42d2b57 462 inquote = QUOTE_NONE;
ba2c33c2
KM
463 }
464}
465
466/*
f42d2b57
KB
467 * findsym - look for the symbol in the symbol table.
468 * if found, return symbol table index,
469 * else return -1.
470 */
471int
472findsym (str)
473char *str;
474{
475 register char *cp;
476 register char *symp;
477 register int symind;
478 register char chr;
479
480 for (symind = 0; symind < nsyms; ++symind) {
481 if (insym[symind] == SYM_INACTIVE) {
482 for ( symp = symname[symind], cp = str
483 ; *symp && *cp == *symp
484 ; cp++, symp++
485 )
486 continue;
487 chr = *cp;
488 if (*symp == '\0' && endsym (chr))
489 return symind;
490 }
491 }
492 return -1;
493}
494
ba2c33c2 495/*
f42d2b57
KB
496 * getlin - expands tabs if asked for
497 * and (if compiled in) treats form-feed as an end-of-line
498 */
499int
ba2c33c2
KM
500getlin (line, maxline, inp, expandtabs)
501register char *line;
502int maxline;
503FILE *inp;
504int expandtabs;
505{
506 int tmp;
507 register int num;
508 register int chr;
f42d2b57 509#ifdef FFSPECIAL
ba2c33c2
KM
510 static char havechar = NO; /* have leftover char from last time */
511 static char svchar BSS;
f42d2b57 512#endif/*FFSPECIAL */
ba2c33c2
KM
513
514 num = 0;
f42d2b57 515#ifdef FFSPECIAL
ba2c33c2
KM
516 if (havechar) {
517 havechar = NO;
518 chr = svchar;
519 goto ent;
520 }
f42d2b57 521#endif/*FFSPECIAL */
ba2c33c2 522 while (num + 8 < maxline) { /* leave room for tab */
f42d2b57 523 chr = getc (inp);
ba2c33c2 524 if (isprint (chr)) {
f42d2b57 525#ifdef FFSPECIAL
ba2c33c2 526 ent:
f42d2b57 527#endif/*FFSPECIAL */
ba2c33c2
KM
528 *line++ = chr;
529 num++;
f42d2b57 530 } else
ba2c33c2
KM
531 switch (chr) {
532 case EOF:
533 return EOF;
534
535 case '\t':
536 if (expandtabs) {
537 num += tmp = 8 - (num & 7);
538 do
539 *line++ = ' ';
540 while (--tmp);
541 break;
f42d2b57
KB
542 }
543 default:
544 *line++ = chr;
545 num++;
ba2c33c2
KM
546 break;
547
548 case '\n':
f42d2b57
KB
549 *line = '\n';
550 num++;
551 goto end;
552
553#ifdef FFSPECIAL
ba2c33c2
KM
554 case '\f':
555 if (++num == 1)
556 *line = '\f';
557 else {
558 *line = '\n';
559 havechar = YES;
f42d2b57
KB
560 svchar = chr;
561 }
562 goto end;
563#endif/*FFSPECIAL */
ba2c33c2
KM
564 }
565 }
566 end:
567 *++line = '\0';
568 return num;
569}
570
571flushline (keep)
f42d2b57 572Bool keep;
ba2c33c2 573{
f42d2b57
KB
574 if ((keep && reject != REJ_YES) ^ complement) {
575 register char *line = tline;
576 register FILE *out = stdout;
577 register char chr;
578
579 while (chr = *line++)
580 putc (chr, out);
581 } else if (lnblank)
582 putc ('\n', stdout);
ba2c33c2
KM
583 return;
584}
585
586prname ()
587{
588 fprintf (stderr, "%s: ", progname);
589 return;
590}
591
f42d2b57 592int
ba2c33c2 593error (err, line, depth)
f42d2b57
KB
594int err; /* type of error & index into error string array */
595int line; /* line number */
596int depth; /* how many ifdefs we are inside */
ba2c33c2
KM
597{
598 if (err == END_ERR)
599 return err;
600
601 prname ();
602
603#ifndef TESTING
f42d2b57
KB
604 fprintf (stderr, "Error in %s line %d: %s.\n", filename, line, errs[err]);
605#else/* TESTING */
606 fprintf (stderr, "Error in %s line %d: %s. ", filename, line, errs[err]);
ba2c33c2 607 fprintf (stderr, "ifdef depth: %d\n", depth);
f42d2b57 608#endif/*TESTING */
ba2c33c2
KM
609
610 exitstat = 2;
611 return depth > 1 ? IEOF_ERR : END_ERR;
612}