unifdef.c revision 103906
1/*
2 * Copyright (c) 1985, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Dave Yost. Support for #if and #elif was added by Tony Finch.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#include <sys/cdefs.h>
38
39#ifndef lint
40static const char copyright[] =
41"@(#) Copyright (c) 1985, 1993\n\
42	The Regents of the University of California.  All rights reserved.\n";
43
44#ifdef __IDSTRING
45__IDSTRING(Berkeley, "@(#)unifdef.c	8.1 (Berkeley) 6/6/93");
46__IDSTRING(NetBSD, "$NetBSD: unifdef.c,v 1.8 2000/07/03 02:51:36 matt Exp $");
47__IDSTRING(dotat, "$dotat: things/unifdef.c,v 1.75 2002/09/24 19:16:29 fanf2 Exp $");
48#endif
49#ifdef __FBSDID
50__FBSDID("$FreeBSD: head/usr.bin/unifdef/unifdef.c 103906 2002-09-24 19:27:44Z fanf $");
51#endif
52#endif
53
54/*
55 * unifdef - remove ifdef'ed lines
56 *
57 *  Warning: will not work correctly if input contains nul characters.
58 *
59 *  Wishlist:
60 *      provide an option which will append the name of the
61 *        appropriate symbol after #else's and #endif's
62 *      provide an option which will check symbols after
63 *        #else's and #endif's to see that they match their
64 *        corresponding #ifdef or #ifndef
65 *      generate #line directives in place of deleted code
66 */
67
68#include <ctype.h>
69#include <err.h>
70#include <stdarg.h>
71#include <stdbool.h>
72#include <stdio.h>
73#include <stdlib.h>
74#include <string.h>
75#include <unistd.h>
76
77/* types of input lines: */
78typedef enum {
79	LT_PLAIN,		/* ordinary line */
80	LT_TRUE,		/* a true #if */
81	LT_FALSE,		/* a false #if */
82	LT_ELTRUE,		/* a true #elif */
83	LT_ELFALSE,		/* a false #elif */
84	LT_IF,			/* an unknown #if */
85	LT_ELIF,		/* an unknown #elif */
86	LT_ELSE,		/* #else */
87	LT_ENDIF,		/* #endif */
88	LT_EOF			/* end of file */
89} Linetype;
90
91typedef enum {		/* 0 or 1: pass thru; 1 or 2: ignore comments */
92	REJ_NO,
93	REJ_IGNORE,
94	REJ_YES
95} Reject_level;
96
97typedef enum {
98	NO_COMMENT = false,
99	C_COMMENT,
100	CXX_COMMENT
101} Comment_state;
102
103typedef enum {
104	QUOTE_NONE = false,
105	QUOTE_SINGLE,
106	QUOTE_DOUBLE
107} Quote_state;
108
109const char *const errs[] = {
110#define NO_ERR      0
111	"",
112#define END_ERR     1
113	"",
114#define ELIF_ERR    2
115	"Inappropriate elif",
116#define ELSE_ERR    3
117	"Inappropriate else",
118#define ENDIF_ERR   4
119	"Inappropriate endif",
120#define IEOF_ERR    5
121	"Premature EOF in ifdef",
122#define CEOF_ERR    6
123	"Premature EOF in comment",
124#define Q1EOF_ERR   7
125	"Premature EOF in quoted character",
126#define Q2EOF_ERR   8
127	"Premature EOF in quoted string"
128};
129
130/*
131 * These are the operators that are supported by the expression evaluator.
132 */
133static int op_lt(int a, int b) { return a < b; }
134static int op_gt(int a, int b) { return a > b; }
135static int op_le(int a, int b) { return a <= b; }
136static int op_ge(int a, int b) { return a >= b; }
137static int op_eq(int a, int b) { return a == b; }
138static int op_ne(int a, int b) { return a != b; }
139static int op_or(int a, int b) { return a || b; }
140static int op_and(int a, int b) { return a && b; }
141
142struct ops;
143
144/*
145 * An evaluation function takes three arguments, as follows: (1) a pointer to
146 * an element of the precedence table which lists the operators at the current
147 * level of precedence; (2) a pointer to an integer which will receive the
148 * value of the expression; and (3) a pointer to a char* that points to the
149 * expression to be evaluated and that is updated to the end of the expression
150 * when evaluation is complete. The function returns LT_FALSE if the value of
151 * the expression is zero, LT_TRUE if it is non-zero, or LT_IF if the
152 * expression could not be evaluated.
153 */
154typedef Linetype eval_fn(struct ops *, int *, const char **);
155
156eval_fn eval_table, eval_unary;
157
158/*
159 * The precedence table. Expressions involving binary operators are evaluated
160 * in a table-driven way by eval_table. When it evaluates a subexpression it
161 * calls the inner function with its first argument pointing to the next
162 * element of the table. Innermost expressions have special non-table-driven
163 * handling.
164 */
165struct ops {
166	eval_fn *inner;
167	struct op {
168		const char *str;
169		int (*fn)(int, int);
170	} op[5];
171} eval_ops[] = {
172	{ eval_table, { { "||", op_or } } },
173	{ eval_table, { { "&&", op_and } } },
174	{ eval_table, { { "==", op_eq },
175			{ "!=", op_ne } } },
176	{ eval_unary, { { "<=", op_le },
177			{ ">=", op_ge },
178			{ "<", op_lt },
179			{ ">", op_gt } } }
180};
181
182FILE           *input;
183const char     *filename;
184int             linenum;	/* current line number */
185int             stifline;	/* start of current #if */
186int             stqcline;	/* start of current coment or quote */
187bool            keepthis;	/* ignore this #if's value 'cause it's const */
188
189#define MAXLINE 1024
190#define KWSIZE 8
191/* tline has extra space so that it isn't overflowed when editing #elifs */
192char    tline[MAXLINE+KWSIZE];	/* input buffer */
193char   *keyword;        	/* used for editing #elif's */
194
195bool            complement;	/* -c option in effect: do the complement */
196bool            debugging;	/* -d option in effect: debugging reports */
197bool            killconsts;	/* -k option in effect: eval constant #ifs */
198bool            lnblank;	/* -l option in effect: blank deleted lines */
199bool            symlist;	/* -s option in effect: output symbol list */
200bool            text;		/* -t option in effect: this is a text file */
201
202int             exitstat;	/* program exit status */
203
204#define MAXSYMS 1000
205const char     *symname[MAXSYMS];	/* symbol name */
206const char     *value[MAXSYMS];		/* -Dsym=value */
207bool            ignore[MAXSYMS];	/* -iDsym or -iUsym */
208
209int             nsyms = 1;	/* symbol 0 is used for tracking #ifs */
210
211Reject_level    reject;		/* what kind of filtering we are doing */
212Comment_state   incomment;	/* inside C comment */
213Quote_state     inquote;	/* inside single or double quotes */
214
215Linetype        checkline(int *);
216void            debug(const char *, ...);
217Linetype        process(int);
218void            doif(int, Linetype, bool);
219void            elif2if(void);
220void            elif2endif(void);
221void            error(int, int);
222void            addsym(bool, bool, char *);
223int             findsym(const char *);
224void            flushline(bool);
225int             getline(char *, int, FILE *, bool);
226Linetype        ifeval(const char **);
227int             main(int, char **);
228const char     *skipcomment(const char *);
229const char     *skipquote(const char *, Quote_state);
230const char     *skipsym(const char *);
231void            usage(void);
232
233#define endsym(c) (!isalpha((unsigned char)c) && !isdigit((unsigned char)c) && c != '_')
234
235int
236main(int argc, char *argv[])
237{
238	int opt;
239
240	while ((opt = getopt(argc, argv, "i:D:U:I:cdklst")) != -1)
241		switch (opt) {
242		case 'i': /* treat stuff controlled by these symbols as text */
243			/*
244			 * For strict backwards-compatibility the U or D
245			 * should be immediately after the -i but it doesn't
246			 * matter much if we relax that requirement.
247			 */
248			opt = *optarg++;
249			if (opt == 'D')
250				addsym(true, true, optarg);
251			else if (opt == 'U')
252				addsym(true, false, optarg);
253			else
254				usage();
255			break;
256		case 'D': /* define a symbol */
257			addsym(false, true, optarg);
258			break;
259		case 'U': /* undef a symbol */
260			addsym(false, false, optarg);
261			break;
262		case 'I':
263			/* ignore for compatibility with cpp */
264			break;
265		case 'c': /* treat -D as -U and vice versa */
266			complement = true;
267			break;
268		case 'k': /* process constant #ifs */
269			killconsts = true;
270			break;
271		case 'd':
272			debugging = true;
273			break;
274		case 'l': /* blank deleted lines instead of omitting them */
275			lnblank = true;
276			break;
277		case 's': /* only output list of symbols that control #ifs */
278			symlist = true;
279			break;
280		case 't': /* don't parse C comments or strings */
281			text = true;
282			break;
283		default:
284			usage();
285		}
286	argc -= optind;
287	argv += optind;
288	if (nsyms == 1 && !symlist) {
289		warnx("must -D or -U at least one symbol");
290		usage();
291	}
292	if (argc > 1) {
293		errx(2, "can only do one file");
294	} else if (argc == 1 && strcmp(*argv, "-") != 0) {
295		filename = *argv;
296		if ((input = fopen(filename, "r")) != NULL) {
297			(void) process(0);
298			(void) fclose(input);
299		} else
300			err(2, "can't open %s", *argv);
301	} else {
302		filename = "[stdin]";
303		input = stdin;
304		(void) process(0);
305	}
306
307	exit(exitstat);
308}
309
310void
311usage(void)
312{
313	fprintf (stderr, "usage: %s",
314"unifdef [-cdklst] [[-Dsym[=val]] [-Usym] [-iDsym[=val]] [-iUsym]] ... [file]\n");
315	exit (2);
316}
317
318/*
319 * This function processes #if lines and alters the pass-through
320 * state accordingly. All the complicated state transition suff is
321 * dealt with in this function, as well as checking that the
322 * #if/#elif/#else/#endif lines happen in the correct order. Lines
323 * between #if lines are handled by a recursive call to process().
324 */
325void
326doif(int depth, Linetype lineval, bool ignoring)
327{
328	Reject_level savereject;
329	bool active;
330	bool donetrue;
331	bool inelse;
332	int saveline;
333
334	debug("#if line %d code %d depth %d",
335	    linenum, lineval, depth);
336	saveline = stifline;
337	stifline = linenum;
338	savereject = reject;
339	inelse = false;
340	donetrue = false;
341	if (lineval == LT_IF || reject != REJ_NO) {
342		active = false;
343		ignoring = false;
344		flushline(true);
345	} else if (ignoring) {
346		active = false;
347		flushline(true);
348		if (lineval == LT_FALSE)
349			reject = REJ_IGNORE;
350		else
351			donetrue = true;
352	} else {
353		active = true;
354		flushline(false);
355		if (lineval == LT_FALSE)
356			reject = REJ_YES;
357		else
358			donetrue = true;
359	}
360	debug("active %d ignore %d", active, ignoring);
361	for (;;) {
362		switch (lineval = process(depth)) {
363		case LT_ELIF:
364			debug("#elif start %d line %d code %d depth %d",
365			    stifline, linenum, lineval, depth);
366			if (inelse)
367				error(ELIF_ERR, depth);
368			donetrue = false;
369			reject = savereject;
370			if (active) {
371				active = false;
372				elif2if();
373				flushline(true);
374			} else {
375				ignoring = false;
376				flushline(true);
377			}
378			debug("active %d ignore %d", active, ignoring);
379			break;
380		case LT_ELTRUE:
381		case LT_ELFALSE:
382			debug("#elif start %d line %d code %d depth %d",
383			    stifline, linenum, lineval, depth);
384			if (inelse)
385				error(ELIF_ERR, depth);
386			if (active) {
387				flushline(false);
388			} else {
389				ignoring = false;
390				active = true;
391				elif2endif();
392				flushline(true);
393			}
394			if (lineval == LT_ELFALSE)
395				reject = REJ_YES;
396			else {
397				reject = REJ_NO;
398				donetrue = true;
399			}
400			debug("active %d ignore %d", active, ignoring);
401			break;
402		case LT_ELSE:
403			debug("#else start %d line %d code %d depth %d",
404			    stifline, linenum, lineval, depth);
405			if (inelse)
406				error(ELSE_ERR, depth);
407			if (active) {
408				flushline(false);
409				if (reject == REJ_YES && !donetrue)
410					reject = REJ_NO;
411				else
412					reject = REJ_YES;
413			} else {
414				flushline(true);
415				if (ignoring) {
416					if (reject == REJ_IGNORE)
417						reject = REJ_NO;
418					else
419						reject = REJ_IGNORE;
420				}
421			}
422			inelse = true;
423			debug("active %d ignore %d", active, ignoring);
424			break;
425		case LT_ENDIF:
426			debug("#endif start %d line %d code %d depth %d",
427			    stifline, linenum, lineval, depth);
428			if (active)
429				flushline(false);
430			else
431				flushline(true);
432			reject = savereject;
433			stifline = saveline;
434			return;
435		default:
436			/* bug */
437			abort();
438		}
439	}
440}
441
442/*
443 * The main file processing routine. This function deals with passing
444 * through normal non-#if lines, correct nesting of #if sections, and
445 * checking that things terminate correctly at the end of file. The
446 * complicated stuff is delegated to doif().
447 */
448Linetype
449process(int depth)
450{
451	Linetype lineval;
452	int cursym;
453
454	for (;;) {
455		linenum++;
456		if (getline(tline, MAXLINE, input, false) == EOF) {
457			if (incomment)
458				error(CEOF_ERR, depth);
459			if (inquote == QUOTE_SINGLE)
460				error(Q1EOF_ERR, depth);
461			if (inquote == QUOTE_DOUBLE)
462				error(Q2EOF_ERR, depth);
463			if (depth != 0)
464				error(IEOF_ERR, depth);
465			return LT_EOF;
466		}
467		switch (lineval = checkline(&cursym)) {
468		case LT_PLAIN:
469			flushline(true);
470			break;
471		case LT_IF:
472		case LT_TRUE:
473		case LT_FALSE:
474			doif(depth + 1, lineval, ignore[cursym]);
475			break;
476		case LT_ELIF:
477		case LT_ELTRUE:
478		case LT_ELFALSE:
479		case LT_ELSE:
480		case LT_ENDIF:
481			if (depth != 0)
482				return lineval;
483			if (lineval == LT_ENDIF)
484				error(ENDIF_ERR, depth);
485			if (lineval == LT_ELSE)
486				error(ELSE_ERR, depth);
487			error(ELIF_ERR, depth);
488		default:
489			/* bug */
490			abort();
491		}
492	}
493}
494
495/*
496 * Parse a line and determine its type.
497 */
498Linetype
499checkline(int *cursym)
500{
501	const char *cp;
502	char *symp;
503	Linetype retval;
504	char kw[KWSIZE];
505
506	retval = LT_PLAIN;
507	cp = skipcomment(tline);
508	if (*cp != '#'
509	    || incomment
510	    || inquote == QUOTE_SINGLE
511	    || inquote == QUOTE_DOUBLE
512	    )
513		goto eol;
514
515	cp = skipcomment(++cp);
516	keyword = (char *)cp;
517	symp = kw;
518	while (!endsym(*cp)) {
519		*symp = *cp++;
520		if (++symp >= &kw[KWSIZE])
521			goto eol;
522	}
523	*symp = '\0';
524
525	if (strcmp(kw, "ifdef") == 0) {
526		retval = LT_TRUE;
527		goto ifdef;
528	} else if (strcmp(kw, "ifndef") == 0) {
529		retval = LT_FALSE;
530	ifdef:
531		cp = skipcomment(++cp);
532		if (incomment) {
533			retval = LT_PLAIN;
534			goto eol;
535		}
536		if ((*cursym = findsym(cp)) == 0)
537			retval = LT_IF;
538		else if (value[*cursym] == NULL)
539			retval = (retval == LT_TRUE)
540			    ? LT_FALSE : LT_TRUE;
541	} else if (strcmp(kw, "if") == 0) {
542		retval = ifeval(&cp);
543		cp = skipcomment(cp);
544		if (*cp != '\n' || keepthis)
545			retval = LT_IF;
546		*cursym = 0;
547	} else if (strcmp(kw, "elif") == 0) {
548		retval = ifeval(&cp);
549		cp = skipcomment(cp);
550		if (*cp != '\n' || keepthis)
551			retval = LT_ELIF;
552		if (retval == LT_IF)
553			retval = LT_ELIF;
554		if (retval == LT_TRUE)
555			retval = LT_ELTRUE;
556		if (retval == LT_FALSE)
557			retval = LT_ELFALSE;
558		*cursym = 0;
559	} else if (strcmp(kw, "else") == 0)
560		retval = LT_ELSE;
561	else if (strcmp(kw, "endif") == 0)
562		retval = LT_ENDIF;
563
564eol:
565	if (!text && reject != REJ_IGNORE)
566		for (; *cp;) {
567			if (incomment)
568				cp = skipcomment(cp);
569			else if (inquote == QUOTE_SINGLE)
570				cp = skipquote(cp, QUOTE_SINGLE);
571			else if (inquote == QUOTE_DOUBLE)
572				cp = skipquote(cp, QUOTE_DOUBLE);
573			else if (*cp == '/' && (cp[1] == '*' || cp[1] == '/'))
574				cp = skipcomment(cp);
575			else if (*cp == '\'')
576				cp = skipquote(cp, QUOTE_SINGLE);
577			else if (*cp == '"')
578				cp = skipquote(cp, QUOTE_DOUBLE);
579			else
580				cp++;
581		}
582	return retval;
583}
584
585/*
586 * Turn a #elif line into a #if. This function is used when we are
587 * processing a #if/#elif/#else/#endif sequence that starts off with a
588 * #if that we understand (and therefore it has been deleted) which is
589 * followed by a #elif that we don't understand and therefore must be
590 * kept. We turn it into a #if to keep the nesting correct.
591 */
592void
593elif2if(void)
594{
595	strncpy(keyword, "if  ", 4);
596}
597
598/*
599 * Turn a #elif line into a #endif. This is used in the opposite
600 * situation to elif2if, i.e. a #if that we don't understand is
601 * followed by a #elif that we do; rather than deleting the #elif (as
602 * we would for a #if) we turn it into a #endif to keep the nesting
603 * correct.
604 */
605void
606elif2endif(void)
607{
608	strcpy(keyword, "endif\n");
609}
610
611/*
612 * Function for evaluating the innermost parts of expressions,
613 * viz. !expr (expr) defined(symbol) symbol number
614 * We reset the keepthis flag when we find a non-constant subexpression.
615 */
616Linetype
617eval_unary(struct ops *ops, int *valp, const char **cpp)
618{
619	const char *cp;
620	char *ep;
621	int sym;
622
623	cp = skipcomment(*cpp);
624	if(*cp == '!') {
625		debug("eval%d !", ops - eval_ops);
626		cp++;
627		if (eval_unary(ops, valp, &cp) == LT_IF)
628			return LT_IF;
629		*valp = !*valp;
630	} else if (*cp == '(') {
631		cp++;
632		debug("eval%d (", ops - eval_ops);
633		if (eval_table(eval_ops, valp, &cp) == LT_IF)
634			return LT_IF;
635		cp = skipcomment(cp);
636		if (*cp++ != ')')
637			return LT_IF;
638	} else if (isdigit((unsigned char)*cp)) {
639		debug("eval%d number", ops - eval_ops);
640		*valp = strtol(cp, &ep, 0);
641		cp = skipsym(cp);
642	} else if (strncmp(cp, "defined", 7) == 0 && endsym(cp[7])) {
643		cp = skipcomment(cp+7);
644		debug("eval%d defined", ops - eval_ops);
645		if (*cp++ != '(')
646			return LT_IF;
647		cp = skipcomment(cp);
648		sym = findsym(cp);
649		if (sym == 0 && !symlist)
650			return LT_IF;
651		*valp = (value[sym] != NULL);
652		cp = skipsym(cp);
653		cp = skipcomment(cp);
654		if (*cp++ != ')')
655			return LT_IF;
656		keepthis = false;
657	} else if (!endsym(*cp)) {
658		debug("eval%d symbol", ops - eval_ops);
659		sym = findsym(cp);
660		if (sym == 0 && !symlist)
661			return LT_IF;
662		if (value[sym] == NULL)
663			*valp = 0;
664		else {
665			*valp = strtol(value[sym], &ep, 0);
666			if (*ep != '\0' || ep == value[sym])
667				return LT_IF;
668		}
669		cp = skipsym(cp);
670		keepthis = false;
671	} else
672		return LT_IF;
673
674	*cpp = cp;
675	debug("eval%d = %d", ops - eval_ops, *valp);
676	return *valp ? LT_TRUE : LT_FALSE;
677}
678
679/*
680 * Table-driven evaluation of binary operators.
681 */
682Linetype
683eval_table(struct ops *ops, int *valp, const char **cpp)
684{
685	const char *cp;
686	struct op *op;
687	int val;
688
689	debug("eval%d", ops - eval_ops);
690	cp = *cpp;
691	if (ops->inner(ops+1, valp, &cp) == LT_IF)
692		return LT_IF;
693	for (;;) {
694		cp = skipcomment(cp);
695		for (op = ops->op; op->str != NULL; op++)
696			if (strncmp(cp, op->str, strlen(op->str)) == 0)
697				break;
698		if (op->str == NULL)
699			break;
700		cp += strlen(op->str);
701		debug("eval%d %s", ops - eval_ops, op->str);
702		if (ops->inner(ops+1, &val, &cp) == LT_IF)
703			return LT_IF;
704		*valp = op->fn(*valp, val);
705	}
706
707	*cpp = cp;
708	debug("eval%d = %d", ops - eval_ops, *valp);
709	return *valp ? LT_TRUE : LT_FALSE;
710}
711
712/*
713 * Evaluate the expression on a #if or #elif line. If we can work out
714 * the result we return LT_TRUE or LT_FALSE accordingly, otherwise we
715 * return just a generic LT_IF. If the expression is constant and
716 * we are not processing constant #ifs then the keepthis flag is true.
717 */
718Linetype
719ifeval(const char **cpp)
720{
721	int val;
722	debug("eval %s", *cpp);
723	keepthis = killconsts ? false : true;
724	return eval_table(eval_ops, &val, cpp);
725}
726
727/*
728 * Skip over comments and stop at the next character position that is
729 * not whitespace.
730 */
731const char *
732skipcomment(const char *cp)
733{
734	if (incomment)
735		goto inside;
736	for (;; cp++) {
737		while (*cp == ' ' || *cp == '\t')
738			cp++;
739		if (text)
740			return cp;
741		if (cp[0] != '/')
742			return cp;
743
744		if (cp[1] == '*') {
745			if (!incomment) {
746				incomment = C_COMMENT;
747				stqcline = linenum;
748			}
749		} else if (cp[1] == '/') {
750			if (!incomment) {
751				incomment = CXX_COMMENT;
752				stqcline = linenum;
753			}
754		} else
755			return cp;
756
757		cp += 2;
758inside:
759		if (incomment == C_COMMENT) {
760			for (;;) {
761				for (; *cp != '*'; cp++)
762					if (*cp == '\0')
763						return cp;
764				if (*++cp == '/') {
765					incomment = NO_COMMENT;
766					break;
767				}
768			}
769		}
770		else if (incomment == CXX_COMMENT) {
771			for (; *cp != '\n'; cp++)
772				if (*cp == '\0')
773					return cp;
774			incomment = NO_COMMENT;
775		}
776	}
777}
778
779/*
780 * Skip over a quoted string or character and stop at the next charaacter
781 * position that is not whitespace.
782 */
783const char *
784skipquote(const char *cp, Quote_state type)
785{
786	char qchar;
787
788	qchar = type == QUOTE_SINGLE ? '\'' : '"';
789
790	if (inquote == type)
791		goto inside;
792	for (;; cp++) {
793		if (*cp != qchar)
794			return cp;
795		cp++;
796		inquote = type;
797		stqcline = linenum;
798inside:
799		for (;; cp++) {
800			if (*cp == qchar)
801				break;
802			if (*cp == '\0' || (*cp == '\\' && *++cp == '\0'))
803				return cp;
804		}
805		inquote = QUOTE_NONE;
806	}
807}
808
809/*
810 * Skip over an identifier.
811 */
812const char *
813skipsym(const char *cp)
814{
815	while (!endsym(*cp))
816		++cp;
817	return cp;
818}
819
820/*
821 * Look for the symbol in the symbol table. If is is found, we return
822 * the symbol table index, else we return 0.
823 */
824int
825findsym(const char *str)
826{
827	const char *cp;
828	const char *symp;
829	int symind;
830
831	if (symlist) {
832		for (cp = str; !endsym(*cp); cp++)
833			continue;
834		printf("%.*s\n", cp-str, str);
835	}
836	for (symind = 1; symind < nsyms; ++symind) {
837		for (cp = str, symp = symname[symind]
838		    ; *cp && *symp && *cp == *symp
839		    ; cp++, symp++
840		    )
841			continue;
842		if (*symp == '\0' && endsym(*cp)) {
843			debug("findsym %s %s", symname[symind],
844			    value[symind] ? value[symind] : "");
845			return symind;
846		}
847	}
848	return 0;
849}
850
851/*
852 * Add a symbol to the symbol table.
853 */
854void
855addsym(bool ignorethis, bool definethis, char *sym)
856{
857	int symind;
858	char *val;
859
860	symind = findsym(sym);
861	if (symind == 0) {
862		if (nsyms >= MAXSYMS)
863			errx(2, "too many symbols");
864		symind = nsyms++;
865	}
866	symname[symind] = sym;
867	ignore[symind] = ignorethis;
868	val = (char *)skipsym(sym);
869	if (definethis) {
870		if (*val == '=') {
871			value[symind] = val+1;
872			*val = '\0';
873		} else if (*val == '\0')
874			value[symind] = "";
875		else
876			usage();
877	} else {
878		if (*val != '\0')
879			usage();
880		value[symind] = NULL;
881	}
882}
883
884/*
885 * Read a line from the input and expand tabs if requested and (if
886 * compiled in) treats form-feed as an end-of-line.
887 */
888int
889getline(char *line, int maxline, FILE *inp, bool expandtabs)
890{
891	int tmp;
892	int num;
893	int chr;
894#ifdef  FFSPECIAL
895	static bool havechar = false;	/* have leftover char from last time */
896	static char svchar;
897#endif	/* FFSPECIAL */
898
899	num = 0;
900#ifdef  FFSPECIAL
901	if (havechar) {
902		havechar = false;
903		chr = svchar;
904		goto ent;
905	}
906#endif	/* FFSPECIAL */
907	while (num + 8 < maxline) {	/* leave room for tab */
908		chr = getc(inp);
909		if (isprint(chr)) {
910#ifdef  FFSPECIAL
911	ent:
912#endif	/* FFSPECIAL */
913			*line++ = chr;
914			num++;
915		} else
916			switch (chr) {
917			case EOF:
918				return EOF;
919
920			case '\t':
921				if (expandtabs) {
922					num += tmp = 8 - (num & 7);
923					do
924						*line++ = ' ';
925					while (--tmp);
926					break;
927				}
928			default:
929				*line++ = chr;
930				num++;
931				break;
932
933			case '\n':
934				*line = '\n';
935				num++;
936				goto end;
937
938#ifdef  FFSPECIAL
939			case '\f':
940				if (++num == 1)
941					*line = '\f';
942				else {
943					*line = '\n';
944					havechar = true;
945					svchar = chr;
946				}
947				goto end;
948#endif	/* FFSPECIAL */
949			}
950	}
951end:
952	*++line = '\0';
953	return num;
954}
955
956/*
957 * Write a line to the output or not, according to the current
958 * filtering state.
959 */
960void
961flushline(bool keep)
962{
963	if (symlist)
964		return;
965	if ((keep && reject != REJ_YES) ^ complement)
966		fputs(tline, stdout);
967	else if (lnblank)
968		putc('\n', stdout);
969	return;
970}
971
972void
973debug(const char *msg, ...)
974{
975	va_list ap;
976
977	if (debugging) {
978		va_start(ap, msg);
979		vwarnx(msg, ap);
980		va_end(ap);
981	}
982}
983
984void
985error(int code, int depth)
986{
987	if (incomment || inquote)
988		errx(2, "error in %s line %d: %s (#if depth %d)",
989		    filename, stqcline, errs[code], depth);
990	else
991		errx(2, "error in %s line %d: %s"
992		    " (#if depth %d start line %d)",
993		    filename, linenum, errs[code], depth, stifline);
994}
995