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