xref: /illumos-gate/usr/src/cmd/sgs/unifdef/common/unifdef.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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
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
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
205 pfile(void)
206 {
207 	reject = 0;
208 	(void) doif(-1, NO, reject, 0);
209 }
210 
211 static int
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
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 *
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 *
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
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
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
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
577 prname(void)
578 {
579 	(void) fprintf(stderr, "%s: ", progname);
580 }
581 
582 
583 static int
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 *
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