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