1/*
2 * Copyright (c) 2002 - 2013 Tony Finch <dot@dotat.at>
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26/*
27 * unifdef - remove ifdef'ed lines
28 *
29 * This code was derived from software contributed to Berkeley by Dave Yost.
30 * It was rewritten to support ANSI C by Tony Finch. The original version
31 * of unifdef carried the 4-clause BSD copyright licence. None of its code
32 * remains in this version (though some of the names remain) so it now
33 * carries a more liberal licence.
34 *
35 *  Wishlist:
36 *      provide an option which will append the name of the
37 *        appropriate symbol after #else's and #endif's
38 *      provide an option which will check symbols after
39 *        #else's and #endif's to see that they match their
40 *        corresponding #ifdef or #ifndef
41 *
42 *   These require better buffer handling, which would also make
43 *   it possible to handle all "dodgy" directives correctly.
44 */
45
46#include "unifdef.h"
47
48static const char copyright[] =
49    "@(#) $Version: unifdef-2.7 $\n"
50    "@(#) $FreeBSD$\n"
51    "@(#) $Author: Tony Finch (dot@dotat.at) $\n"
52    "@(#) $URL: http://dotat.at/prog/unifdef $\n"
53;
54
55/* types of input lines: */
56typedef enum {
57	LT_TRUEI,		/* a true #if with ignore flag */
58	LT_FALSEI,		/* a false #if with ignore flag */
59	LT_IF,			/* an unknown #if */
60	LT_TRUE,		/* a true #if */
61	LT_FALSE,		/* a false #if */
62	LT_ELIF,		/* an unknown #elif */
63	LT_ELTRUE,		/* a true #elif */
64	LT_ELFALSE,		/* a false #elif */
65	LT_ELSE,		/* #else */
66	LT_ENDIF,		/* #endif */
67	LT_DODGY,		/* flag: directive is not on one line */
68	LT_DODGY_LAST = LT_DODGY + LT_ENDIF,
69	LT_PLAIN,		/* ordinary line */
70	LT_EOF,			/* end of file */
71	LT_ERROR,		/* unevaluable #if */
72	LT_COUNT
73} Linetype;
74
75static char const * const linetype_name[] = {
76	"TRUEI", "FALSEI", "IF", "TRUE", "FALSE",
77	"ELIF", "ELTRUE", "ELFALSE", "ELSE", "ENDIF",
78	"DODGY TRUEI", "DODGY FALSEI",
79	"DODGY IF", "DODGY TRUE", "DODGY FALSE",
80	"DODGY ELIF", "DODGY ELTRUE", "DODGY ELFALSE",
81	"DODGY ELSE", "DODGY ENDIF",
82	"PLAIN", "EOF", "ERROR"
83};
84
85#define linetype_if2elif(lt) ((Linetype)(lt - LT_IF + LT_ELIF))
86#define linetype_2dodgy(lt) ((Linetype)(lt + LT_DODGY))
87
88/* state of #if processing */
89typedef enum {
90	IS_OUTSIDE,
91	IS_FALSE_PREFIX,	/* false #if followed by false #elifs */
92	IS_TRUE_PREFIX,		/* first non-false #(el)if is true */
93	IS_PASS_MIDDLE,		/* first non-false #(el)if is unknown */
94	IS_FALSE_MIDDLE,	/* a false #elif after a pass state */
95	IS_TRUE_MIDDLE,		/* a true #elif after a pass state */
96	IS_PASS_ELSE,		/* an else after a pass state */
97	IS_FALSE_ELSE,		/* an else after a true state */
98	IS_TRUE_ELSE,		/* an else after only false states */
99	IS_FALSE_TRAILER,	/* #elifs after a true are false */
100	IS_COUNT
101} Ifstate;
102
103static char const * const ifstate_name[] = {
104	"OUTSIDE", "FALSE_PREFIX", "TRUE_PREFIX",
105	"PASS_MIDDLE", "FALSE_MIDDLE", "TRUE_MIDDLE",
106	"PASS_ELSE", "FALSE_ELSE", "TRUE_ELSE",
107	"FALSE_TRAILER"
108};
109
110/* state of comment parser */
111typedef enum {
112	NO_COMMENT = false,	/* outside a comment */
113	C_COMMENT,		/* in a comment like this one */
114	CXX_COMMENT,		/* between // and end of line */
115	STARTING_COMMENT,	/* just after slash-backslash-newline */
116	FINISHING_COMMENT,	/* star-backslash-newline in a C comment */
117	CHAR_LITERAL,		/* inside '' */
118	STRING_LITERAL		/* inside "" */
119} Comment_state;
120
121static char const * const comment_name[] = {
122	"NO", "C", "CXX", "STARTING", "FINISHING", "CHAR", "STRING"
123};
124
125/* state of preprocessor line parser */
126typedef enum {
127	LS_START,		/* only space and comments on this line */
128	LS_HASH,		/* only space, comments, and a hash */
129	LS_DIRTY		/* this line can't be a preprocessor line */
130} Line_state;
131
132static char const * const linestate_name[] = {
133	"START", "HASH", "DIRTY"
134};
135
136/*
137 * Minimum translation limits from ISO/IEC 9899:1999 5.2.4.1
138 */
139#define	MAXDEPTH        64			/* maximum #if nesting */
140#define	MAXLINE         4096			/* maximum length of line */
141#define	MAXSYMS         4096			/* maximum number of symbols */
142
143/*
144 * Sometimes when editing a keyword the replacement text is longer, so
145 * we leave some space at the end of the tline buffer to accommodate this.
146 */
147#define	EDITSLOP        10
148
149/*
150 * Globals.
151 */
152
153static bool             compblank;		/* -B: compress blank lines */
154static bool             lnblank;		/* -b: blank deleted lines */
155static bool             complement;		/* -c: do the complement */
156static bool             debugging;		/* -d: debugging reports */
157static bool             inplace;		/* -m: modify in place */
158static bool             iocccok;		/* -e: fewer IOCCC errors */
159static bool             strictlogic;		/* -K: keep ambiguous #ifs */
160static bool             killconsts;		/* -k: eval constant #ifs */
161static bool             lnnum;			/* -n: add #line directives */
162static bool             symlist;		/* -s: output symbol list */
163static bool             symdepth;		/* -S: output symbol depth */
164static bool             text;			/* -t: this is a text file */
165
166static const char      *symname[MAXSYMS];	/* symbol name */
167static const char      *value[MAXSYMS];		/* -Dsym=value */
168static bool             ignore[MAXSYMS];	/* -iDsym or -iUsym */
169static int              nsyms;			/* number of symbols */
170
171static FILE            *input;			/* input file pointer */
172static const char      *filename;		/* input file name */
173static int              linenum;		/* current line number */
174static const char      *linefile;		/* file name for #line */
175static FILE            *output;			/* output file pointer */
176static const char      *ofilename;		/* output file name */
177static const char      *backext;		/* backup extension */
178static char            *tempname;		/* avoid splatting input */
179
180static char             tline[MAXLINE+EDITSLOP];/* input buffer plus space */
181static char            *keyword;		/* used for editing #elif's */
182
183static const char      *newline;		/* input file format */
184static const char       newline_unix[] = "\n";
185static const char       newline_crlf[] = "\r\n";
186
187static Comment_state    incomment;		/* comment parser state */
188static Line_state       linestate;		/* #if line parser state */
189static Ifstate          ifstate[MAXDEPTH];	/* #if processor state */
190static bool             ignoring[MAXDEPTH];	/* ignore comments state */
191static int              stifline[MAXDEPTH];	/* start of current #if */
192static int              depth;			/* current #if nesting */
193static int              delcount;		/* count of deleted lines */
194static unsigned         blankcount;		/* count of blank lines */
195static unsigned         blankmax;		/* maximum recent blankcount */
196static bool             constexpr;		/* constant #if expression */
197static bool             zerosyms;		/* to format symdepth output */
198static bool             firstsym;		/* ditto */
199
200static int              exitmode;		/* exit status mode */
201static int              exitstat;		/* program exit status */
202
203static void             addsym(bool, bool, char *);
204static char            *astrcat(const char *, const char *);
205static void             cleantemp(void);
206static void             closeout(void);
207static void             debug(const char *, ...);
208static void             done(void);
209static void             error(const char *);
210static int              findsym(const char *);
211static void             flushline(bool);
212static void             hashline(void);
213static void             help(void);
214static Linetype         ifeval(const char **);
215static void             ignoreoff(void);
216static void             ignoreon(void);
217static void             keywordedit(const char *);
218static void             nest(void);
219static Linetype         parseline(void);
220static void             process(void);
221static void             processinout(const char *, const char *);
222static const char      *skipargs(const char *);
223static const char      *skipcomment(const char *);
224static const char      *skipsym(const char *);
225static void             state(Ifstate);
226static int              strlcmp(const char *, const char *, size_t);
227static void             unnest(void);
228static void             usage(void);
229static void             version(void);
230
231#define endsym(c) (!isalnum((unsigned char)c) && c != '_')
232
233/*
234 * The main program.
235 */
236int
237main(int argc, char *argv[])
238{
239	int opt;
240
241	while ((opt = getopt(argc, argv, "i:D:U:I:M:o:x:bBcdehKklmnsStV")) != -1)
242		switch (opt) {
243		case 'i': /* treat stuff controlled by these symbols as text */
244			/*
245			 * For strict backwards-compatibility the U or D
246			 * should be immediately after the -i but it doesn't
247			 * matter much if we relax that requirement.
248			 */
249			opt = *optarg++;
250			if (opt == 'D')
251				addsym(true, true, optarg);
252			else if (opt == 'U')
253				addsym(true, false, optarg);
254			else
255				usage();
256			break;
257		case 'D': /* define a symbol */
258			addsym(false, true, optarg);
259			break;
260		case 'U': /* undef a symbol */
261			addsym(false, false, optarg);
262			break;
263		case 'I': /* no-op for compatibility with cpp */
264			break;
265		case 'b': /* blank deleted lines instead of omitting them */
266		case 'l': /* backwards compatibility */
267			lnblank = true;
268			break;
269		case 'B': /* compress blank lines around removed section */
270			compblank = true;
271			break;
272		case 'c': /* treat -D as -U and vice versa */
273			complement = true;
274			break;
275		case 'd':
276			debugging = true;
277			break;
278		case 'e': /* fewer errors from dodgy lines */
279			iocccok = true;
280			break;
281		case 'h':
282			help();
283			break;
284		case 'K': /* keep ambiguous #ifs */
285			strictlogic = true;
286			break;
287		case 'k': /* process constant #ifs */
288			killconsts = true;
289			break;
290		case 'm': /* modify in place */
291			inplace = true;
292			break;
293		case 'M': /* modify in place and keep backup */
294			inplace = true;
295			backext = optarg;
296			break;
297		case 'n': /* add #line directive after deleted lines */
298			lnnum = true;
299			break;
300		case 'o': /* output to a file */
301			ofilename = optarg;
302			break;
303		case 's': /* only output list of symbols that control #ifs */
304			symlist = true;
305			break;
306		case 'S': /* list symbols with their nesting depth */
307			symlist = symdepth = true;
308			break;
309		case 't': /* don't parse C comments */
310			text = true;
311			break;
312		case 'V':
313			version();
314			break;
315		case 'x':
316			exitmode = atoi(optarg);
317			if(exitmode < 0 || exitmode > 2)
318				usage();
319			break;
320		default:
321			usage();
322		}
323	argc -= optind;
324	argv += optind;
325	if (compblank && lnblank)
326		errx(2, "-B and -b are mutually exclusive");
327	if (symlist && (ofilename != NULL || inplace || argc > 1))
328		errx(2, "-s only works with one input file");
329	if (argc > 1 && ofilename != NULL)
330		errx(2, "-o cannot be used with multiple input files");
331	if (argc > 1 && !inplace)
332		errx(2, "multiple input files require -m or -M");
333	if (argc == 0)
334		argc = 1;
335	if (argc == 1 && !inplace && ofilename == NULL)
336		ofilename = "-";
337
338	atexit(cleantemp);
339	if (ofilename != NULL)
340		processinout(*argv, ofilename);
341	else while (argc-- > 0) {
342		processinout(*argv, *argv);
343		argv++;
344	}
345	switch(exitmode) {
346	case(0): exit(exitstat);
347	case(1): exit(!exitstat);
348	case(2): exit(0);
349	default: abort(); /* bug */
350	}
351}
352
353/*
354 * File logistics.
355 */
356static void
357processinout(const char *ifn, const char *ofn)
358{
359	struct stat st;
360
361	if (ifn == NULL || strcmp(ifn, "-") == 0) {
362		filename = "[stdin]";
363		linefile = NULL;
364		input = fbinmode(stdin);
365	} else {
366		filename = ifn;
367		linefile = ifn;
368		input = fopen(ifn, "rb");
369		if (input == NULL)
370			err(2, "can't open %s", ifn);
371	}
372	if (strcmp(ofn, "-") == 0) {
373		output = fbinmode(stdout);
374		process();
375		return;
376	}
377	if (stat(ofn, &st) < 0) {
378		output = fopen(ofn, "wb");
379		if (output == NULL)
380			err(2, "can't create %s", ofn);
381		process();
382		return;
383	}
384
385	tempname = astrcat(ofn, ".XXXXXX");
386	output = mktempmode(tempname, st.st_mode);
387	if (output == NULL)
388		err(2, "can't create %s", tempname);
389
390	process();
391
392	if (backext != NULL) {
393		char *backname = astrcat(ofn, backext);
394		if (rename(ofn, backname) < 0)
395			err(2, "can't rename \"%s\" to \"%s%s\"", ofn, ofn, backext);
396		free(backname);
397	}
398	if (rename(tempname, ofn) < 0)
399		err(2, "can't rename \"%s\" to \"%s\"", tempname, ofn);
400	free(tempname);
401	tempname = NULL;
402}
403
404/*
405 * For cleaning up if there is an error.
406 */
407static void
408cleantemp(void)
409{
410	if (tempname != NULL)
411		remove(tempname);
412}
413
414/*
415 * Self-identification functions.
416 */
417
418static void
419version(void)
420{
421	const char *c = copyright;
422	for (;;) {
423		while (*++c != '$')
424			if (*c == '\0')
425				exit(0);
426		while (*++c != '$')
427			putc(*c, stderr);
428		putc('\n', stderr);
429	}
430}
431
432static void
433synopsis(FILE *fp)
434{
435	fprintf(fp,
436	    "usage:	unifdef [-bBcdehKkmnsStV] [-x{012}] [-Mext] [-opath] \\\n"
437	    "		[-[i]Dsym[=val]] [-[i]Usym] ... [file] ...\n");
438}
439
440static void
441usage(void)
442{
443	synopsis(stderr);
444	exit(2);
445}
446
447static void
448help(void)
449{
450	synopsis(stdout);
451	printf(
452	    "	-Dsym=val  define preprocessor symbol with given value\n"
453	    "	-Dsym      define preprocessor symbol with value 1\n"
454	    "	-Usym	   preprocessor symbol is undefined\n"
455	    "	-iDsym=val \\  ignore C strings and comments\n"
456	    "	-iDsym      ) in sections controlled by these\n"
457	    "	-iUsym	   /  preprocessor symbols\n"
458	    "	-b	blank lines instead of deleting them\n"
459	    "	-B	compress blank lines around deleted section\n"
460	    "	-c	complement (invert) keep vs. delete\n"
461	    "	-d	debugging mode\n"
462	    "	-e	ignore multiline preprocessor directives\n"
463	    "	-h	print help\n"
464	    "	-Ipath	extra include file path (ignored)\n"
465	    "	-K	disable && and || short-circuiting\n"
466	    "	-k	process constant #if expressions\n"
467	    "	-Mext	modify in place and keep backups\n"
468	    "	-m	modify input files in place\n"
469	    "	-n	add #line directives to output\n"
470	    "	-opath	output file name\n"
471	    "	-S	list #if control symbols with nesting\n"
472	    "	-s	list #if control symbols\n"
473	    "	-t	ignore C strings and comments\n"
474	    "	-V	print version\n"
475	    "	-x{012}	exit status mode\n"
476	);
477	exit(0);
478}
479
480/*
481 * A state transition function alters the global #if processing state
482 * in a particular way. The table below is indexed by the current
483 * processing state and the type of the current line.
484 *
485 * Nesting is handled by keeping a stack of states; some transition
486 * functions increase or decrease the depth. They also maintain the
487 * ignore state on a stack. In some complicated cases they have to
488 * alter the preprocessor directive, as follows.
489 *
490 * When we have processed a group that starts off with a known-false
491 * #if/#elif sequence (which has therefore been deleted) followed by a
492 * #elif that we don't understand and therefore must keep, we edit the
493 * latter into a #if to keep the nesting correct. We use memcpy() to
494 * overwrite the 4 byte token "elif" with "if  " without a '\0' byte.
495 *
496 * When we find a true #elif in a group, the following block will
497 * always be kept and the rest of the sequence after the next #elif or
498 * #else will be discarded. We edit the #elif into a #else and the
499 * following directive to #endif since this has the desired behaviour.
500 *
501 * "Dodgy" directives are split across multiple lines, the most common
502 * example being a multi-line comment hanging off the right of the
503 * directive. We can handle them correctly only if there is no change
504 * from printing to dropping (or vice versa) caused by that directive.
505 * If the directive is the first of a group we have a choice between
506 * failing with an error, or passing it through unchanged instead of
507 * evaluating it. The latter is not the default to avoid questions from
508 * users about unifdef unexpectedly leaving behind preprocessor directives.
509 */
510typedef void state_fn(void);
511
512/* report an error */
513static void Eelif (void) { error("Inappropriate #elif"); }
514static void Eelse (void) { error("Inappropriate #else"); }
515static void Eendif(void) { error("Inappropriate #endif"); }
516static void Eeof  (void) { error("Premature EOF"); }
517static void Eioccc(void) { error("Obfuscated preprocessor control line"); }
518/* plain line handling */
519static void print (void) { flushline(true); }
520static void drop  (void) { flushline(false); }
521/* output lacks group's start line */
522static void Strue (void) { drop();  ignoreoff(); state(IS_TRUE_PREFIX); }
523static void Sfalse(void) { drop();  ignoreoff(); state(IS_FALSE_PREFIX); }
524static void Selse (void) { drop();               state(IS_TRUE_ELSE); }
525/* print/pass this block */
526static void Pelif (void) { print(); ignoreoff(); state(IS_PASS_MIDDLE); }
527static void Pelse (void) { print();              state(IS_PASS_ELSE); }
528static void Pendif(void) { print(); unnest(); }
529/* discard this block */
530static void Dfalse(void) { drop();  ignoreoff(); state(IS_FALSE_TRAILER); }
531static void Delif (void) { drop();  ignoreoff(); state(IS_FALSE_MIDDLE); }
532static void Delse (void) { drop();               state(IS_FALSE_ELSE); }
533static void Dendif(void) { drop();  unnest(); }
534/* first line of group */
535static void Fdrop (void) { nest();  Dfalse(); }
536static void Fpass (void) { nest();  Pelif(); }
537static void Ftrue (void) { nest();  Strue(); }
538static void Ffalse(void) { nest();  Sfalse(); }
539/* variable pedantry for obfuscated lines */
540static void Oiffy (void) { if (!iocccok) Eioccc(); Fpass(); ignoreon(); }
541static void Oif   (void) { if (!iocccok) Eioccc(); Fpass(); }
542static void Oelif (void) { if (!iocccok) Eioccc(); Pelif(); }
543/* ignore comments in this block */
544static void Idrop (void) { Fdrop();  ignoreon(); }
545static void Itrue (void) { Ftrue();  ignoreon(); }
546static void Ifalse(void) { Ffalse(); ignoreon(); }
547/* modify this line */
548static void Mpass (void) { memcpy(keyword, "if  ", 4); Pelif(); }
549static void Mtrue (void) { keywordedit("else");  state(IS_TRUE_MIDDLE); }
550static void Melif (void) { keywordedit("endif"); state(IS_FALSE_TRAILER); }
551static void Melse (void) { keywordedit("endif"); state(IS_FALSE_ELSE); }
552
553static state_fn * const trans_table[IS_COUNT][LT_COUNT] = {
554/* IS_OUTSIDE */
555{ Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Eelif, Eelif, Eelse, Eendif,
556  Oiffy, Oiffy, Fpass, Oif,   Oif,   Eelif, Eelif, Eelif, Eelse, Eendif,
557  print, done,  abort },
558/* IS_FALSE_PREFIX */
559{ Idrop, Idrop, Fdrop, Fdrop, Fdrop, Mpass, Strue, Sfalse,Selse, Dendif,
560  Idrop, Idrop, Fdrop, Fdrop, Fdrop, Mpass, Eioccc,Eioccc,Eioccc,Eioccc,
561  drop,  Eeof,  abort },
562/* IS_TRUE_PREFIX */
563{ Itrue, Ifalse,Fpass, Ftrue, Ffalse,Dfalse,Dfalse,Dfalse,Delse, Dendif,
564  Oiffy, Oiffy, Fpass, Oif,   Oif,   Eioccc,Eioccc,Eioccc,Eioccc,Eioccc,
565  print, Eeof,  abort },
566/* IS_PASS_MIDDLE */
567{ Itrue, Ifalse,Fpass, Ftrue, Ffalse,Pelif, Mtrue, Delif, Pelse, Pendif,
568  Oiffy, Oiffy, Fpass, Oif,   Oif,   Pelif, Oelif, Oelif, Pelse, Pendif,
569  print, Eeof,  abort },
570/* IS_FALSE_MIDDLE */
571{ Idrop, Idrop, Fdrop, Fdrop, Fdrop, Pelif, Mtrue, Delif, Pelse, Pendif,
572  Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eioccc,Eioccc,Eioccc,Eioccc,Eioccc,
573  drop,  Eeof,  abort },
574/* IS_TRUE_MIDDLE */
575{ Itrue, Ifalse,Fpass, Ftrue, Ffalse,Melif, Melif, Melif, Melse, Pendif,
576  Oiffy, Oiffy, Fpass, Oif,   Oif,   Eioccc,Eioccc,Eioccc,Eioccc,Pendif,
577  print, Eeof,  abort },
578/* IS_PASS_ELSE */
579{ Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Eelif, Eelif, Eelse, Pendif,
580  Oiffy, Oiffy, Fpass, Oif,   Oif,   Eelif, Eelif, Eelif, Eelse, Pendif,
581  print, Eeof,  abort },
582/* IS_FALSE_ELSE */
583{ Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eelif, Eelif, Eelif, Eelse, Dendif,
584  Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eelif, Eelif, Eelif, Eelse, Eioccc,
585  drop,  Eeof,  abort },
586/* IS_TRUE_ELSE */
587{ Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Eelif, Eelif, Eelse, Dendif,
588  Oiffy, Oiffy, Fpass, Oif,   Oif,   Eelif, Eelif, Eelif, Eelse, Eioccc,
589  print, Eeof,  abort },
590/* IS_FALSE_TRAILER */
591{ Idrop, Idrop, Fdrop, Fdrop, Fdrop, Dfalse,Dfalse,Dfalse,Delse, Dendif,
592  Idrop, Idrop, Fdrop, Fdrop, Fdrop, Dfalse,Dfalse,Dfalse,Delse, Eioccc,
593  drop,  Eeof,  abort }
594/*TRUEI  FALSEI IF     TRUE   FALSE  ELIF   ELTRUE ELFALSE ELSE  ENDIF
595  TRUEI  FALSEI IF     TRUE   FALSE  ELIF   ELTRUE ELFALSE ELSE  ENDIF (DODGY)
596  PLAIN  EOF    ERROR */
597};
598
599/*
600 * State machine utility functions
601 */
602static void
603ignoreoff(void)
604{
605	if (depth == 0)
606		abort(); /* bug */
607	ignoring[depth] = ignoring[depth-1];
608}
609static void
610ignoreon(void)
611{
612	ignoring[depth] = true;
613}
614static void
615keywordedit(const char *replacement)
616{
617	snprintf(keyword, tline + sizeof(tline) - keyword,
618	    "%s%s", replacement, newline);
619	print();
620}
621static void
622nest(void)
623{
624	if (depth > MAXDEPTH-1)
625		abort(); /* bug */
626	if (depth == MAXDEPTH-1)
627		error("Too many levels of nesting");
628	depth += 1;
629	stifline[depth] = linenum;
630}
631static void
632unnest(void)
633{
634	if (depth == 0)
635		abort(); /* bug */
636	depth -= 1;
637}
638static void
639state(Ifstate is)
640{
641	ifstate[depth] = is;
642}
643
644/*
645 * The last state transition function. When this is called,
646 * lineval == LT_EOF, so the process() loop will terminate.
647 */
648static void
649done(void)
650{
651	if (incomment)
652		error("EOF in comment");
653	closeout();
654}
655
656/*
657 * Write a line to the output or not, according to command line options.
658 * If writing fails, closeout() will print the error and exit.
659 */
660static void
661flushline(bool keep)
662{
663	if (symlist)
664		return;
665	if (keep ^ complement) {
666		bool blankline = tline[strspn(tline, " \t\r\n")] == '\0';
667		if (blankline && compblank && blankcount != blankmax) {
668			delcount += 1;
669			blankcount += 1;
670		} else {
671			if (lnnum && delcount > 0)
672				hashline();
673			if (fputs(tline, output) == EOF)
674				closeout();
675			delcount = 0;
676			blankmax = blankcount = blankline ? blankcount + 1 : 0;
677		}
678	} else {
679		if (lnblank && fputs(newline, output) == EOF)
680			closeout();
681		exitstat = 1;
682		delcount += 1;
683		blankcount = 0;
684	}
685	if (debugging && fflush(output) == EOF)
686		closeout();
687}
688
689/*
690 * Format of #line directives depends on whether we know the input filename.
691 */
692static void
693hashline(void)
694{
695	int e;
696
697	if (linefile == NULL)
698		e = fprintf(output, "#line %d%s", linenum, newline);
699	else
700		e = fprintf(output, "#line %d \"%s\"%s",
701		    linenum, linefile, newline);
702	if (e < 0)
703		closeout();
704}
705
706/*
707 * Flush the output and handle errors.
708 */
709static void
710closeout(void)
711{
712	/* Tidy up after findsym(). */
713	if (symdepth && !zerosyms)
714		printf("\n");
715	if (ferror(output) || fclose(output) == EOF)
716		err(2, "%s: can't write to output", filename);
717}
718
719/*
720 * The driver for the state machine.
721 */
722static void
723process(void)
724{
725	Linetype lineval = LT_PLAIN;
726	/* When compressing blank lines, act as if the file
727	   is preceded by a large number of blank lines. */
728	blankmax = blankcount = 1000;
729	zerosyms = true;
730	while (lineval != LT_EOF) {
731		lineval = parseline();
732		trans_table[ifstate[depth]][lineval]();
733		debug("process line %d %s -> %s depth %d",
734		    linenum, linetype_name[lineval],
735		    ifstate_name[ifstate[depth]], depth);
736	}
737}
738
739/*
740 * Parse a line and determine its type. We keep the preprocessor line
741 * parser state between calls in the global variable linestate, with
742 * help from skipcomment().
743 */
744static Linetype
745parseline(void)
746{
747	const char *cp;
748	int cursym;
749	int kwlen;
750	Linetype retval;
751	Comment_state wascomment;
752
753	linenum++;
754	if (fgets(tline, MAXLINE, input) == NULL) {
755		if (ferror(input))
756			err(2, "can't read %s", filename);
757		else
758			return (LT_EOF);
759	}
760	if (newline == NULL) {
761		if (strrchr(tline, '\n') == strrchr(tline, '\r') + 1)
762			newline = newline_crlf;
763		else
764			newline = newline_unix;
765	}
766	retval = LT_PLAIN;
767	wascomment = incomment;
768	cp = skipcomment(tline);
769	if (linestate == LS_START) {
770		if (*cp == '#') {
771			linestate = LS_HASH;
772			firstsym = true;
773			cp = skipcomment(cp + 1);
774		} else if (*cp != '\0')
775			linestate = LS_DIRTY;
776	}
777	if (!incomment && linestate == LS_HASH) {
778		keyword = tline + (cp - tline);
779		cp = skipsym(cp);
780		kwlen = cp - keyword;
781		/* no way can we deal with a continuation inside a keyword */
782		if (strncmp(cp, "\\\r\n", 3) == 0 ||
783		    strncmp(cp, "\\\n", 2) == 0)
784			Eioccc();
785		if (strlcmp("ifdef", keyword, kwlen) == 0 ||
786		    strlcmp("ifndef", keyword, kwlen) == 0) {
787			cp = skipcomment(cp);
788			if ((cursym = findsym(cp)) < 0)
789				retval = LT_IF;
790			else {
791				retval = (keyword[2] == 'n')
792				    ? LT_FALSE : LT_TRUE;
793				if (value[cursym] == NULL)
794					retval = (retval == LT_TRUE)
795					    ? LT_FALSE : LT_TRUE;
796				if (ignore[cursym])
797					retval = (retval == LT_TRUE)
798					    ? LT_TRUEI : LT_FALSEI;
799			}
800			cp = skipsym(cp);
801		} else if (strlcmp("if", keyword, kwlen) == 0)
802			retval = ifeval(&cp);
803		else if (strlcmp("elif", keyword, kwlen) == 0)
804			retval = linetype_if2elif(ifeval(&cp));
805		else if (strlcmp("else", keyword, kwlen) == 0)
806			retval = LT_ELSE;
807		else if (strlcmp("endif", keyword, kwlen) == 0)
808			retval = LT_ENDIF;
809		else {
810			linestate = LS_DIRTY;
811			retval = LT_PLAIN;
812		}
813		cp = skipcomment(cp);
814		if (*cp != '\0') {
815			linestate = LS_DIRTY;
816			if (retval == LT_TRUE || retval == LT_FALSE ||
817			    retval == LT_TRUEI || retval == LT_FALSEI)
818				retval = LT_IF;
819			if (retval == LT_ELTRUE || retval == LT_ELFALSE)
820				retval = LT_ELIF;
821		}
822		if (retval != LT_PLAIN && (wascomment || incomment)) {
823			retval = linetype_2dodgy(retval);
824			if (incomment)
825				linestate = LS_DIRTY;
826		}
827		/* skipcomment normally changes the state, except
828		   if the last line of the file lacks a newline, or
829		   if there is too much whitespace in a directive */
830		if (linestate == LS_HASH) {
831			size_t len = cp - tline;
832			if (fgets(tline + len, MAXLINE - len, input) == NULL) {
833				if (ferror(input))
834					err(2, "can't read %s", filename);
835				/* append the missing newline at eof */
836				strcpy(tline + len, newline);
837				cp += strlen(newline);
838				linestate = LS_START;
839			} else {
840				linestate = LS_DIRTY;
841			}
842		}
843	}
844	if (linestate == LS_DIRTY) {
845		while (*cp != '\0')
846			cp = skipcomment(cp + 1);
847	}
848	debug("parser line %d state %s comment %s line", linenum,
849	    comment_name[incomment], linestate_name[linestate]);
850	return (retval);
851}
852
853/*
854 * These are the binary operators that are supported by the expression
855 * evaluator.
856 */
857static Linetype op_strict(int *p, int v, Linetype at, Linetype bt) {
858	if(at == LT_IF || bt == LT_IF) return (LT_IF);
859	return (*p = v, v ? LT_TRUE : LT_FALSE);
860}
861static Linetype op_lt(int *p, Linetype at, int a, Linetype bt, int b) {
862	return op_strict(p, a < b, at, bt);
863}
864static Linetype op_gt(int *p, Linetype at, int a, Linetype bt, int b) {
865	return op_strict(p, a > b, at, bt);
866}
867static Linetype op_le(int *p, Linetype at, int a, Linetype bt, int b) {
868	return op_strict(p, a <= b, at, bt);
869}
870static Linetype op_ge(int *p, Linetype at, int a, Linetype bt, int b) {
871	return op_strict(p, a >= b, at, bt);
872}
873static Linetype op_eq(int *p, Linetype at, int a, Linetype bt, int b) {
874	return op_strict(p, a == b, at, bt);
875}
876static Linetype op_ne(int *p, Linetype at, int a, Linetype bt, int b) {
877	return op_strict(p, a != b, at, bt);
878}
879static Linetype op_or(int *p, Linetype at, int a, Linetype bt, int b) {
880	if (!strictlogic && (at == LT_TRUE || bt == LT_TRUE))
881		return (*p = 1, LT_TRUE);
882	return op_strict(p, a || b, at, bt);
883}
884static Linetype op_and(int *p, Linetype at, int a, Linetype bt, int b) {
885	if (!strictlogic && (at == LT_FALSE || bt == LT_FALSE))
886		return (*p = 0, LT_FALSE);
887	return op_strict(p, a && b, at, bt);
888}
889
890/*
891 * An evaluation function takes three arguments, as follows: (1) a pointer to
892 * an element of the precedence table which lists the operators at the current
893 * level of precedence; (2) a pointer to an integer which will receive the
894 * value of the expression; and (3) a pointer to a char* that points to the
895 * expression to be evaluated and that is updated to the end of the expression
896 * when evaluation is complete. The function returns LT_FALSE if the value of
897 * the expression is zero, LT_TRUE if it is non-zero, LT_IF if the expression
898 * depends on an unknown symbol, or LT_ERROR if there is a parse failure.
899 */
900struct ops;
901
902typedef Linetype eval_fn(const struct ops *, int *, const char **);
903
904static eval_fn eval_table, eval_unary;
905
906/*
907 * The precedence table. Expressions involving binary operators are evaluated
908 * in a table-driven way by eval_table. When it evaluates a subexpression it
909 * calls the inner function with its first argument pointing to the next
910 * element of the table. Innermost expressions have special non-table-driven
911 * handling.
912 */
913struct op {
914	const char *str;
915	Linetype (*fn)(int *, Linetype, int, Linetype, int);
916};
917struct ops {
918	eval_fn *inner;
919	struct op op[5];
920};
921static const struct ops eval_ops[] = {
922	{ eval_table, { { "||", op_or } } },
923	{ eval_table, { { "&&", op_and } } },
924	{ eval_table, { { "==", op_eq },
925			{ "!=", op_ne } } },
926	{ eval_unary, { { "<=", op_le },
927			{ ">=", op_ge },
928			{ "<", op_lt },
929			{ ">", op_gt } } }
930};
931
932/* Current operator precedence level */
933static int prec(const struct ops *ops)
934{
935	return (ops - eval_ops);
936}
937
938/*
939 * Function for evaluating the innermost parts of expressions,
940 * viz. !expr (expr) number defined(symbol) symbol
941 * We reset the constexpr flag in the last two cases.
942 */
943static Linetype
944eval_unary(const struct ops *ops, int *valp, const char **cpp)
945{
946	const char *cp;
947	char *ep;
948	int sym;
949	bool defparen;
950	Linetype lt;
951
952	cp = skipcomment(*cpp);
953	if (*cp == '!') {
954		debug("eval%d !", prec(ops));
955		cp++;
956		lt = eval_unary(ops, valp, &cp);
957		if (lt == LT_ERROR)
958			return (LT_ERROR);
959		if (lt != LT_IF) {
960			*valp = !*valp;
961			lt = *valp ? LT_TRUE : LT_FALSE;
962		}
963	} else if (*cp == '(') {
964		cp++;
965		debug("eval%d (", prec(ops));
966		lt = eval_table(eval_ops, valp, &cp);
967		if (lt == LT_ERROR)
968			return (LT_ERROR);
969		cp = skipcomment(cp);
970		if (*cp++ != ')')
971			return (LT_ERROR);
972	} else if (isdigit((unsigned char)*cp)) {
973		debug("eval%d number", prec(ops));
974		*valp = strtol(cp, &ep, 0);
975		if (ep == cp)
976			return (LT_ERROR);
977		lt = *valp ? LT_TRUE : LT_FALSE;
978		cp = skipsym(cp);
979	} else if (strncmp(cp, "defined", 7) == 0 && endsym(cp[7])) {
980		cp = skipcomment(cp+7);
981		debug("eval%d defined", prec(ops));
982		if (*cp == '(') {
983			cp = skipcomment(cp+1);
984			defparen = true;
985		} else {
986			defparen = false;
987		}
988		sym = findsym(cp);
989		if (sym < 0) {
990			lt = LT_IF;
991		} else {
992			*valp = (value[sym] != NULL);
993			lt = *valp ? LT_TRUE : LT_FALSE;
994		}
995		cp = skipsym(cp);
996		cp = skipcomment(cp);
997		if (defparen && *cp++ != ')')
998			return (LT_ERROR);
999		constexpr = false;
1000	} else if (!endsym(*cp)) {
1001		debug("eval%d symbol", prec(ops));
1002		sym = findsym(cp);
1003		cp = skipsym(cp);
1004		if (sym < 0) {
1005			lt = LT_IF;
1006			cp = skipargs(cp);
1007		} else if (value[sym] == NULL) {
1008			*valp = 0;
1009			lt = LT_FALSE;
1010		} else {
1011			*valp = strtol(value[sym], &ep, 0);
1012			if (*ep != '\0' || ep == value[sym])
1013				return (LT_ERROR);
1014			lt = *valp ? LT_TRUE : LT_FALSE;
1015			cp = skipargs(cp);
1016		}
1017		constexpr = false;
1018	} else {
1019		debug("eval%d bad expr", prec(ops));
1020		return (LT_ERROR);
1021	}
1022
1023	*cpp = cp;
1024	debug("eval%d = %d", prec(ops), *valp);
1025	return (lt);
1026}
1027
1028/*
1029 * Table-driven evaluation of binary operators.
1030 */
1031static Linetype
1032eval_table(const struct ops *ops, int *valp, const char **cpp)
1033{
1034	const struct op *op;
1035	const char *cp;
1036	int val;
1037	Linetype lt, rt;
1038
1039	debug("eval%d", prec(ops));
1040	cp = *cpp;
1041	lt = ops->inner(ops+1, valp, &cp);
1042	if (lt == LT_ERROR)
1043		return (LT_ERROR);
1044	for (;;) {
1045		cp = skipcomment(cp);
1046		for (op = ops->op; op->str != NULL; op++)
1047			if (strncmp(cp, op->str, strlen(op->str)) == 0)
1048				break;
1049		if (op->str == NULL)
1050			break;
1051		cp += strlen(op->str);
1052		debug("eval%d %s", prec(ops), op->str);
1053		rt = ops->inner(ops+1, &val, &cp);
1054		if (rt == LT_ERROR)
1055			return (LT_ERROR);
1056		lt = op->fn(valp, lt, *valp, rt, val);
1057	}
1058
1059	*cpp = cp;
1060	debug("eval%d = %d", prec(ops), *valp);
1061	debug("eval%d lt = %s", prec(ops), linetype_name[lt]);
1062	return (lt);
1063}
1064
1065/*
1066 * Evaluate the expression on a #if or #elif line. If we can work out
1067 * the result we return LT_TRUE or LT_FALSE accordingly, otherwise we
1068 * return just a generic LT_IF.
1069 */
1070static Linetype
1071ifeval(const char **cpp)
1072{
1073	Linetype ret;
1074	int val = 0;
1075
1076	debug("eval %s", *cpp);
1077	constexpr = killconsts ? false : true;
1078	ret = eval_table(eval_ops, &val, cpp);
1079	debug("eval = %d", val);
1080	return (constexpr ? LT_IF : ret == LT_ERROR ? LT_IF : ret);
1081}
1082
1083/*
1084 * Skip over comments, strings, and character literals and stop at the
1085 * next character position that is not whitespace. Between calls we keep
1086 * the comment state in the global variable incomment, and we also adjust
1087 * the global variable linestate when we see a newline.
1088 * XXX: doesn't cope with the buffer splitting inside a state transition.
1089 */
1090static const char *
1091skipcomment(const char *cp)
1092{
1093	if (text || ignoring[depth]) {
1094		for (; isspace((unsigned char)*cp); cp++)
1095			if (*cp == '\n')
1096				linestate = LS_START;
1097		return (cp);
1098	}
1099	while (*cp != '\0')
1100		/* don't reset to LS_START after a line continuation */
1101		if (strncmp(cp, "\\\r\n", 3) == 0)
1102			cp += 3;
1103		else if (strncmp(cp, "\\\n", 2) == 0)
1104			cp += 2;
1105		else switch (incomment) {
1106		case NO_COMMENT:
1107			if (strncmp(cp, "/\\\r\n", 4) == 0) {
1108				incomment = STARTING_COMMENT;
1109				cp += 4;
1110			} else if (strncmp(cp, "/\\\n", 3) == 0) {
1111				incomment = STARTING_COMMENT;
1112				cp += 3;
1113			} else if (strncmp(cp, "/*", 2) == 0) {
1114				incomment = C_COMMENT;
1115				cp += 2;
1116			} else if (strncmp(cp, "//", 2) == 0) {
1117				incomment = CXX_COMMENT;
1118				cp += 2;
1119			} else if (strncmp(cp, "\'", 1) == 0) {
1120				incomment = CHAR_LITERAL;
1121				linestate = LS_DIRTY;
1122				cp += 1;
1123			} else if (strncmp(cp, "\"", 1) == 0) {
1124				incomment = STRING_LITERAL;
1125				linestate = LS_DIRTY;
1126				cp += 1;
1127			} else if (strncmp(cp, "\n", 1) == 0) {
1128				linestate = LS_START;
1129				cp += 1;
1130			} else if (strchr(" \r\t", *cp) != NULL) {
1131				cp += 1;
1132			} else
1133				return (cp);
1134			continue;
1135		case CXX_COMMENT:
1136			if (strncmp(cp, "\n", 1) == 0) {
1137				incomment = NO_COMMENT;
1138				linestate = LS_START;
1139			}
1140			cp += 1;
1141			continue;
1142		case CHAR_LITERAL:
1143		case STRING_LITERAL:
1144			if ((incomment == CHAR_LITERAL && cp[0] == '\'') ||
1145			    (incomment == STRING_LITERAL && cp[0] == '\"')) {
1146				incomment = NO_COMMENT;
1147				cp += 1;
1148			} else if (cp[0] == '\\') {
1149				if (cp[1] == '\0')
1150					cp += 1;
1151				else
1152					cp += 2;
1153			} else if (strncmp(cp, "\n", 1) == 0) {
1154				if (incomment == CHAR_LITERAL)
1155					error("unterminated char literal");
1156				else
1157					error("unterminated string literal");
1158			} else
1159				cp += 1;
1160			continue;
1161		case C_COMMENT:
1162			if (strncmp(cp, "*\\\r\n", 4) == 0) {
1163				incomment = FINISHING_COMMENT;
1164				cp += 4;
1165			} else if (strncmp(cp, "*\\\n", 3) == 0) {
1166				incomment = FINISHING_COMMENT;
1167				cp += 3;
1168			} else if (strncmp(cp, "*/", 2) == 0) {
1169				incomment = NO_COMMENT;
1170				cp += 2;
1171			} else
1172				cp += 1;
1173			continue;
1174		case STARTING_COMMENT:
1175			if (*cp == '*') {
1176				incomment = C_COMMENT;
1177				cp += 1;
1178			} else if (*cp == '/') {
1179				incomment = CXX_COMMENT;
1180				cp += 1;
1181			} else {
1182				incomment = NO_COMMENT;
1183				linestate = LS_DIRTY;
1184			}
1185			continue;
1186		case FINISHING_COMMENT:
1187			if (*cp == '/') {
1188				incomment = NO_COMMENT;
1189				cp += 1;
1190			} else
1191				incomment = C_COMMENT;
1192			continue;
1193		default:
1194			abort(); /* bug */
1195		}
1196	return (cp);
1197}
1198
1199/*
1200 * Skip macro arguments.
1201 */
1202static const char *
1203skipargs(const char *cp)
1204{
1205	const char *ocp = cp;
1206	int level = 0;
1207	cp = skipcomment(cp);
1208	if (*cp != '(')
1209		return (cp);
1210	do {
1211		if (*cp == '(')
1212			level++;
1213		if (*cp == ')')
1214			level--;
1215		cp = skipcomment(cp+1);
1216	} while (level != 0 && *cp != '\0');
1217	if (level == 0)
1218		return (cp);
1219	else
1220	/* Rewind and re-detect the syntax error later. */
1221		return (ocp);
1222}
1223
1224/*
1225 * Skip over an identifier.
1226 */
1227static const char *
1228skipsym(const char *cp)
1229{
1230	while (!endsym(*cp))
1231		++cp;
1232	return (cp);
1233}
1234
1235/*
1236 * Look for the symbol in the symbol table. If it is found, we return
1237 * the symbol table index, else we return -1.
1238 */
1239static int
1240findsym(const char *str)
1241{
1242	const char *cp;
1243	int symind;
1244
1245	cp = skipsym(str);
1246	if (cp == str)
1247		return (-1);
1248	if (symlist) {
1249		if (symdepth && firstsym)
1250			printf("%s%3d", zerosyms ? "" : "\n", depth);
1251		firstsym = zerosyms = false;
1252		printf("%s%.*s%s",
1253		    symdepth ? " " : "",
1254		    (int)(cp-str), str,
1255		    symdepth ? "" : "\n");
1256		/* we don't care about the value of the symbol */
1257		return (0);
1258	}
1259	for (symind = 0; symind < nsyms; ++symind) {
1260		if (strlcmp(symname[symind], str, cp-str) == 0) {
1261			debug("findsym %s %s", symname[symind],
1262			    value[symind] ? value[symind] : "");
1263			return (symind);
1264		}
1265	}
1266	return (-1);
1267}
1268
1269/*
1270 * Add a symbol to the symbol table.
1271 */
1272static void
1273addsym(bool ignorethis, bool definethis, char *sym)
1274{
1275	int symind;
1276	char *val;
1277
1278	symind = findsym(sym);
1279	if (symind < 0) {
1280		if (nsyms >= MAXSYMS)
1281			errx(2, "too many symbols");
1282		symind = nsyms++;
1283	}
1284	symname[symind] = sym;
1285	ignore[symind] = ignorethis;
1286	val = sym + (skipsym(sym) - sym);
1287	if (definethis) {
1288		if (*val == '=') {
1289			value[symind] = val+1;
1290			*val = '\0';
1291		} else if (*val == '\0')
1292			value[symind] = "1";
1293		else
1294			usage();
1295	} else {
1296		if (*val != '\0')
1297			usage();
1298		value[symind] = NULL;
1299	}
1300	debug("addsym %s=%s", symname[symind],
1301	    value[symind] ? value[symind] : "undef");
1302}
1303
1304/*
1305 * Compare s with n characters of t.
1306 * The same as strncmp() except that it checks that s[n] == '\0'.
1307 */
1308static int
1309strlcmp(const char *s, const char *t, size_t n)
1310{
1311	while (n-- && *t != '\0')
1312		if (*s != *t)
1313			return ((unsigned char)*s - (unsigned char)*t);
1314		else
1315			++s, ++t;
1316	return ((unsigned char)*s);
1317}
1318
1319/*
1320 * Concatenate two strings into new memory, checking for failure.
1321 */
1322static char *
1323astrcat(const char *s1, const char *s2)
1324{
1325	char *s;
1326	int len;
1327
1328	len = 1 + snprintf(NULL, 0, "%s%s", s1, s2);
1329	s = (char *)malloc(len);
1330	if (s == NULL)
1331		err(2, "malloc");
1332	snprintf(s, len, "%s%s", s1, s2);
1333	return (s);
1334}
1335
1336/*
1337 * Diagnostics.
1338 */
1339static void
1340debug(const char *msg, ...)
1341{
1342	va_list ap;
1343
1344	if (debugging) {
1345		va_start(ap, msg);
1346		vwarnx(msg, ap);
1347		va_end(ap);
1348	}
1349}
1350
1351static void
1352error(const char *msg)
1353{
1354	if (depth == 0)
1355		warnx("%s: %d: %s", filename, linenum, msg);
1356	else
1357		warnx("%s: %d: %s (#if line %d depth %d)",
1358		    filename, linenum, msg, stifline[depth], depth);
1359	closeout();
1360	errx(2, "output may be truncated");
1361}
1362