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