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