xref: /illumos-gate/usr/src/cmd/sgs/unifdef/common/unifdef.c (revision 1e8199757379047b30cc13c6aed3a0d25c7a035a)
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