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