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
43FILE *input;
44#ifndef YES
45#define	YES 1
46#define	NO  0
47#endif
48
49char *progname;
50char *filename;
51char text;		/* -t option in effect: this is a text file */
52char lnblank;		/* -l option in effect: blank deleted lines */
53char complement;	/* -c option in effect: complement the operation */
54#define	MAXSYMS 100
55char true[MAXSYMS];
56char ignore[MAXSYMS];
57char *sym[MAXSYMS];
58signed char insym[MAXSYMS];
59#define	KWSIZE 8
60char buf[KWSIZE];
61char nsyms;
62char incomment;
63#define	QUOTE1 0
64#define	QUOTE2 1
65char inquote[2];
66int exitstat;
67
68static char *skipcomment(char *cp);
69static char *skipquote(char *cp, int type);
70static char *nextsym(char *p);
71static int doif(int thissym, int inif, int prevreject, int depth);
72static void pfile(void);
73static int getlin(char *line, int maxline, FILE *inp, int expandtabs);
74static void prname(void);
75static void flushline(int keep);
76static int checkline(int *cursym);
77static int error(int err, int line, int depth);
78static void putlin(char *line, FILE *fio);
79
80static void
81usage(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
91int
92main(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 {
136unrec:
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 */
179int reject;    /* 0 or 1: pass thru; 1 or 2: ignore comments */
180int linenum;    /* current line number */
181int stqcline;   /* start of current comment or quote */
182
183char *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
202static void
203pfile(void)
204{
205	reject = 0;
206	(void) doif(-1, NO, reject, 0);
207}
208
209static int
210doif(
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
313char tline[MAXLINE];
314
315static int
316checkline(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;
362ifdef:
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
393eol:
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 */
418static char *
419skipcomment(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		}
435inside:
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 */
452static char *
453skipquote(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		}
469inside:
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 */
485static int
486getlin(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
508ent:
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	}
549end:
550	*++line = '\0';
551	return (num);
552}
553
554static void
555flushline(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 */
566static void
567putlin(char *line, FILE *fio)
568{
569	char chr;
570
571	while (chr = *line++)
572		(void) putc(chr, fio);
573}
574
575static void
576prname(void)
577{
578	(void) fprintf(stderr, "%s: ", progname);
579}
580
581
582static int
583error(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 */
606char *
607nextsym(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