unifdef.c revision 1.21 1 1.21 joerg /* $NetBSD: unifdef.c,v 1.21 2012/02/29 23:35:10 joerg Exp $ */
2 1.3 jtc
3 1.1 cgd /*
4 1.3 jtc * Copyright (c) 1985, 1993
5 1.3 jtc * The Regents of the University of California. All rights reserved.
6 1.1 cgd *
7 1.1 cgd * This code is derived from software contributed to Berkeley by
8 1.11 itojun * Dave Yost. It was rewritten to support ANSI C by Tony Finch.
9 1.1 cgd *
10 1.1 cgd * Redistribution and use in source and binary forms, with or without
11 1.1 cgd * modification, are permitted provided that the following conditions
12 1.1 cgd * are met:
13 1.1 cgd * 1. Redistributions of source code must retain the above copyright
14 1.1 cgd * notice, this list of conditions and the following disclaimer.
15 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright
16 1.1 cgd * notice, this list of conditions and the following disclaimer in the
17 1.1 cgd * documentation and/or other materials provided with the distribution.
18 1.12 agc * 3. Neither the name of the University nor the names of its contributors
19 1.12 agc * may be used to endorse or promote products derived from this software
20 1.12 agc * without specific prior written permission.
21 1.12 agc *
22 1.12 agc * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 1.12 agc * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 1.12 agc * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 1.12 agc * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 1.12 agc * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 1.12 agc * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 1.12 agc * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 1.12 agc * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 1.12 agc * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 1.12 agc * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 1.12 agc * SUCH DAMAGE.
33 1.12 agc */
34 1.12 agc
35 1.12 agc /*
36 1.12 agc * Copyright (c) 2002, 2003 Tony Finch <dot (at) dotat.at>
37 1.12 agc *
38 1.12 agc * This code is derived from software contributed to Berkeley by
39 1.12 agc * Dave Yost. It was rewritten to support ANSI C by Tony Finch.
40 1.12 agc *
41 1.12 agc * Redistribution and use in source and binary forms, with or without
42 1.12 agc * modification, are permitted provided that the following conditions
43 1.12 agc * are met:
44 1.12 agc * 1. Redistributions of source code must retain the above copyright
45 1.12 agc * notice, this list of conditions and the following disclaimer.
46 1.12 agc * 2. Redistributions in binary form must reproduce the above copyright
47 1.12 agc * notice, this list of conditions and the following disclaimer in the
48 1.12 agc * documentation and/or other materials provided with the distribution.
49 1.1 cgd * 3. All advertising materials mentioning features or use of this software
50 1.1 cgd * must display the following acknowledgement:
51 1.1 cgd * This product includes software developed by the University of
52 1.1 cgd * California, Berkeley and its contributors.
53 1.1 cgd * 4. Neither the name of the University nor the names of its contributors
54 1.1 cgd * may be used to endorse or promote products derived from this software
55 1.1 cgd * without specific prior written permission.
56 1.1 cgd *
57 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
58 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
59 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
60 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
61 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
62 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
63 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
64 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
65 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
66 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
67 1.1 cgd * SUCH DAMAGE.
68 1.1 cgd */
69 1.1 cgd
70 1.5 lukem #include <sys/cdefs.h>
71 1.1 cgd
72 1.1 cgd #ifndef lint
73 1.3 jtc #if 0
74 1.11 itojun static const char copyright[] =
75 1.11 itojun "@(#) Copyright (c) 1985, 1993\n\
76 1.11 itojun The Regents of the University of California. All rights reserved.\n";
77 1.11 itojun #endif
78 1.11 itojun #ifdef __IDSTRING
79 1.11 itojun __IDSTRING(Berkeley, "@(#)unifdef.c 8.1 (Berkeley) 6/6/93");
80 1.21 joerg __IDSTRING(NetBSD, "$NetBSD: unifdef.c,v 1.21 2012/02/29 23:35:10 joerg Exp $");
81 1.11 itojun __IDSTRING(dotat, "$dotat: things/unifdef.c,v 1.161 2003/07/01 15:32:48 fanf2 Exp $");
82 1.11 itojun #endif
83 1.11 itojun #endif /* not lint */
84 1.11 itojun #ifdef __FBSDID
85 1.11 itojun __FBSDID("$FreeBSD: src/usr.bin/unifdef/unifdef.c,v 1.18 2003/07/01 15:30:43 fanf Exp $");
86 1.3 jtc #endif
87 1.1 cgd
88 1.1 cgd /*
89 1.1 cgd * unifdef - remove ifdef'ed lines
90 1.1 cgd *
91 1.1 cgd * Wishlist:
92 1.1 cgd * provide an option which will append the name of the
93 1.1 cgd * appropriate symbol after #else's and #endif's
94 1.1 cgd * provide an option which will check symbols after
95 1.1 cgd * #else's and #endif's to see that they match their
96 1.1 cgd * corresponding #ifdef or #ifndef
97 1.11 itojun * generate #line directives in place of deleted code
98 1.11 itojun *
99 1.11 itojun * The first two items above require better buffer handling, which would
100 1.11 itojun * also make it possible to handle all "dodgy" directives correctly.
101 1.1 cgd */
102 1.1 cgd
103 1.11 itojun #include <ctype.h>
104 1.11 itojun #include <err.h>
105 1.14 ginsbach #include <libgen.h>
106 1.11 itojun #include <stdarg.h>
107 1.1 cgd #include <stdio.h>
108 1.8 matt #include <stdlib.h>
109 1.8 matt #include <string.h>
110 1.11 itojun #include <unistd.h>
111 1.11 itojun
112 1.14 ginsbach #include <sys/param.h>
113 1.14 ginsbach #include <sys/stat.h>
114 1.14 ginsbach
115 1.11 itojun #include "stdbool.h"
116 1.11 itojun
117 1.11 itojun /* types of input lines: */
118 1.11 itojun typedef enum {
119 1.11 itojun LT_TRUEI, /* a true #if with ignore flag */
120 1.11 itojun LT_FALSEI, /* a false #if with ignore flag */
121 1.11 itojun LT_IF, /* an unknown #if */
122 1.11 itojun LT_TRUE, /* a true #if */
123 1.11 itojun LT_FALSE, /* a false #if */
124 1.11 itojun LT_ELIF, /* an unknown #elif */
125 1.11 itojun LT_ELTRUE, /* a true #elif */
126 1.11 itojun LT_ELFALSE, /* a false #elif */
127 1.11 itojun LT_ELSE, /* #else */
128 1.11 itojun LT_ENDIF, /* #endif */
129 1.11 itojun LT_DODGY, /* flag: directive is not on one line */
130 1.11 itojun LT_DODGY_LAST = LT_DODGY + LT_ENDIF,
131 1.11 itojun LT_PLAIN, /* ordinary line */
132 1.11 itojun LT_EOF, /* end of file */
133 1.11 itojun LT_COUNT
134 1.11 itojun } Linetype;
135 1.11 itojun
136 1.11 itojun static char const * const linetype_name[] = {
137 1.11 itojun "TRUEI", "FALSEI", "IF", "TRUE", "FALSE",
138 1.11 itojun "ELIF", "ELTRUE", "ELFALSE", "ELSE", "ENDIF",
139 1.11 itojun "DODGY TRUEI", "DODGY FALSEI",
140 1.11 itojun "DODGY IF", "DODGY TRUE", "DODGY FALSE",
141 1.11 itojun "DODGY ELIF", "DODGY ELTRUE", "DODGY ELFALSE",
142 1.11 itojun "DODGY ELSE", "DODGY ENDIF",
143 1.11 itojun "PLAIN", "EOF"
144 1.11 itojun };
145 1.11 itojun
146 1.11 itojun /* state of #if processing */
147 1.11 itojun typedef enum {
148 1.11 itojun IS_OUTSIDE,
149 1.11 itojun IS_FALSE_PREFIX, /* false #if followed by false #elifs */
150 1.11 itojun IS_TRUE_PREFIX, /* first non-false #(el)if is true */
151 1.11 itojun IS_PASS_MIDDLE, /* first non-false #(el)if is unknown */
152 1.11 itojun IS_FALSE_MIDDLE, /* a false #elif after a pass state */
153 1.11 itojun IS_TRUE_MIDDLE, /* a true #elif after a pass state */
154 1.11 itojun IS_PASS_ELSE, /* an else after a pass state */
155 1.11 itojun IS_FALSE_ELSE, /* an else after a true state */
156 1.11 itojun IS_TRUE_ELSE, /* an else after only false states */
157 1.11 itojun IS_FALSE_TRAILER, /* #elifs after a true are false */
158 1.11 itojun IS_COUNT
159 1.11 itojun } Ifstate;
160 1.11 itojun
161 1.11 itojun static char const * const ifstate_name[] = {
162 1.11 itojun "OUTSIDE", "FALSE_PREFIX", "TRUE_PREFIX",
163 1.11 itojun "PASS_MIDDLE", "FALSE_MIDDLE", "TRUE_MIDDLE",
164 1.11 itojun "PASS_ELSE", "FALSE_ELSE", "TRUE_ELSE",
165 1.11 itojun "FALSE_TRAILER"
166 1.11 itojun };
167 1.11 itojun
168 1.11 itojun /* state of comment parser */
169 1.11 itojun typedef enum {
170 1.11 itojun NO_COMMENT = false, /* outside a comment */
171 1.11 itojun C_COMMENT, /* in a comment like this one */
172 1.11 itojun CXX_COMMENT, /* between // and end of line */
173 1.11 itojun STARTING_COMMENT, /* just after slash-backslash-newline */
174 1.11 itojun FINISHING_COMMENT /* star-backslash-newline in a C comment */
175 1.11 itojun } Comment_state;
176 1.11 itojun
177 1.11 itojun static char const * const comment_name[] = {
178 1.11 itojun "NO", "C", "CXX", "STARTING", "FINISHING"
179 1.11 itojun };
180 1.11 itojun
181 1.11 itojun /* state of preprocessor line parser */
182 1.11 itojun typedef enum {
183 1.11 itojun LS_START, /* only space and comments on this line */
184 1.11 itojun LS_HASH, /* only space, comments, and a hash */
185 1.11 itojun LS_DIRTY /* this line can't be a preprocessor line */
186 1.11 itojun } Line_state;
187 1.11 itojun
188 1.11 itojun static char const * const linestate_name[] = {
189 1.11 itojun "START", "HASH", "DIRTY"
190 1.11 itojun };
191 1.11 itojun
192 1.11 itojun /*
193 1.11 itojun * Minimum translation limits from ISO/IEC 9899:1999 5.2.4.1
194 1.11 itojun */
195 1.11 itojun #define MAXDEPTH 64 /* maximum #if nesting */
196 1.11 itojun #define MAXLINE 4096 /* maximum length of line */
197 1.11 itojun #define MAXSYMS 4096 /* maximum number of symbols */
198 1.11 itojun
199 1.11 itojun /*
200 1.11 itojun * Sometimes when editing a keyword the replacement text is longer, so
201 1.11 itojun * we leave some space at the end of the tline buffer to accommodate this.
202 1.11 itojun */
203 1.11 itojun #define EDITSLOP 10
204 1.11 itojun
205 1.11 itojun /*
206 1.11 itojun * Globals.
207 1.11 itojun */
208 1.11 itojun
209 1.11 itojun static bool complement; /* -c: do the complement */
210 1.11 itojun static bool debugging; /* -d: debugging reports */
211 1.11 itojun static bool iocccok; /* -e: fewer IOCCC errors */
212 1.11 itojun static bool killconsts; /* -k: eval constant #ifs */
213 1.11 itojun static bool lnblank; /* -l: blank deleted lines */
214 1.11 itojun static bool symlist; /* -s: output symbol list */
215 1.11 itojun static bool text; /* -t: this is a text file */
216 1.11 itojun
217 1.11 itojun static const char *symname[MAXSYMS]; /* symbol name */
218 1.11 itojun static const char *value[MAXSYMS]; /* -Dsym=value */
219 1.11 itojun static bool ignore[MAXSYMS]; /* -iDsym or -iUsym */
220 1.11 itojun static int nsyms; /* number of symbols */
221 1.11 itojun
222 1.11 itojun static FILE *input; /* input file pointer */
223 1.14 ginsbach static FILE *output; /* output file pointer */
224 1.11 itojun static const char *filename; /* input file name */
225 1.14 ginsbach static char *ofilename; /* output file name */
226 1.14 ginsbach static char tmpname[MAXPATHLEN]; /* used when overwriting */
227 1.11 itojun static int linenum; /* current line number */
228 1.14 ginsbach static int overwriting; /* output overwrites input */
229 1.11 itojun
230 1.11 itojun static char tline[MAXLINE+EDITSLOP];/* input buffer plus space */
231 1.11 itojun static char *keyword; /* used for editing #elif's */
232 1.11 itojun
233 1.11 itojun static Comment_state incomment; /* comment parser state */
234 1.11 itojun static Line_state linestate; /* #if line parser state */
235 1.11 itojun static Ifstate ifstate[MAXDEPTH]; /* #if processor state */
236 1.11 itojun static bool ignoring[MAXDEPTH]; /* ignore comments state */
237 1.11 itojun static int stifline[MAXDEPTH]; /* start of current #if */
238 1.11 itojun static int depth; /* current #if nesting */
239 1.11 itojun static bool keepthis; /* don't delete constant #if */
240 1.11 itojun
241 1.11 itojun static int exitstat; /* program exit status */
242 1.11 itojun
243 1.11 itojun static void addsym(bool, bool, char *);
244 1.21 joerg static void debug(const char *, ...) __printflike(1, 2);
245 1.20 joerg __dead static void done(void);
246 1.20 joerg __dead static void error(const char *);
247 1.11 itojun static int findsym(const char *);
248 1.11 itojun static void flushline(bool);
249 1.15 roy static Linetype get_line(void);
250 1.11 itojun static Linetype ifeval(const char **);
251 1.11 itojun static void ignoreoff(void);
252 1.11 itojun static void ignoreon(void);
253 1.11 itojun static void keywordedit(const char *);
254 1.11 itojun static void nest(void);
255 1.20 joerg __dead static void process(void);
256 1.11 itojun static const char *skipcomment(const char *);
257 1.11 itojun static const char *skipsym(const char *);
258 1.11 itojun static void state(Ifstate);
259 1.11 itojun static int strlcmp(const char *, const char *, size_t);
260 1.20 joerg __dead static void usage(void);
261 1.1 cgd
262 1.11 itojun #define endsym(c) (!isalpha((unsigned char)c) && !isdigit((unsigned char)c) && c != '_')
263 1.1 cgd
264 1.11 itojun /*
265 1.11 itojun * The main program.
266 1.11 itojun */
267 1.4 jtc int
268 1.11 itojun main(int argc, char *argv[])
269 1.11 itojun {
270 1.11 itojun int opt;
271 1.14 ginsbach struct stat isb, osb;
272 1.11 itojun
273 1.14 ginsbach while ((opt = getopt(argc, argv, "i:D:U:I:o:cdeklst")) != -1)
274 1.11 itojun switch (opt) {
275 1.11 itojun case 'i': /* treat stuff controlled by these symbols as text */
276 1.11 itojun /*
277 1.11 itojun * For strict backwards-compatibility the U or D
278 1.11 itojun * should be immediately after the -i but it doesn't
279 1.11 itojun * matter much if we relax that requirement.
280 1.11 itojun */
281 1.11 itojun opt = *optarg++;
282 1.11 itojun if (opt == 'D')
283 1.11 itojun addsym(true, true, optarg);
284 1.11 itojun else if (opt == 'U')
285 1.11 itojun addsym(true, false, optarg);
286 1.11 itojun else
287 1.11 itojun usage();
288 1.11 itojun break;
289 1.11 itojun case 'D': /* define a symbol */
290 1.11 itojun addsym(false, true, optarg);
291 1.11 itojun break;
292 1.11 itojun case 'U': /* undef a symbol */
293 1.11 itojun addsym(false, false, optarg);
294 1.11 itojun break;
295 1.11 itojun case 'I':
296 1.11 itojun /* no-op for compatibility with cpp */
297 1.11 itojun break;
298 1.11 itojun case 'c': /* treat -D as -U and vice versa */
299 1.11 itojun complement = true;
300 1.11 itojun break;
301 1.11 itojun case 'd':
302 1.11 itojun debugging = true;
303 1.11 itojun break;
304 1.11 itojun case 'e': /* fewer errors from dodgy lines */
305 1.11 itojun iocccok = true;
306 1.11 itojun break;
307 1.11 itojun case 'k': /* process constant #ifs */
308 1.11 itojun killconsts = true;
309 1.11 itojun break;
310 1.11 itojun case 'l': /* blank deleted lines instead of omitting them */
311 1.11 itojun lnblank = true;
312 1.11 itojun break;
313 1.14 ginsbach case 'o': /* output to a file */
314 1.14 ginsbach ofilename = optarg;
315 1.14 ginsbach break;
316 1.11 itojun case 's': /* only output list of symbols that control #ifs */
317 1.11 itojun symlist = true;
318 1.11 itojun break;
319 1.11 itojun case 't': /* don't parse C comments */
320 1.11 itojun text = true;
321 1.11 itojun break;
322 1.11 itojun default:
323 1.11 itojun usage();
324 1.10 itojun }
325 1.11 itojun argc -= optind;
326 1.11 itojun argv += optind;
327 1.11 itojun if (nsyms == 0 && !symlist) {
328 1.11 itojun warnx("must -D or -U at least one symbol");
329 1.11 itojun usage();
330 1.1 cgd }
331 1.11 itojun if (argc > 1) {
332 1.11 itojun errx(2, "can only do one file");
333 1.11 itojun } else if (argc == 1 && strcmp(*argv, "-") != 0) {
334 1.11 itojun filename = *argv;
335 1.11 itojun input = fopen(filename, "r");
336 1.11 itojun if (input == NULL)
337 1.11 itojun err(2, "can't open %s", filename);
338 1.11 itojun } else {
339 1.11 itojun filename = "[stdin]";
340 1.11 itojun input = stdin;
341 1.1 cgd }
342 1.14 ginsbach if (ofilename == NULL) {
343 1.14 ginsbach output = stdout;
344 1.14 ginsbach } else {
345 1.17 ginsbach if (stat(ofilename, &osb) == 0) {
346 1.17 ginsbach if (fstat(fileno(input), &isb) != 0)
347 1.17 ginsbach err(2, "can't fstat %s", filename);
348 1.14 ginsbach
349 1.17 ginsbach overwriting = (osb.st_dev == isb.st_dev &&
350 1.19 ginsbach osb.st_ino == isb.st_ino);
351 1.17 ginsbach }
352 1.14 ginsbach if (overwriting) {
353 1.14 ginsbach int ofd;
354 1.14 ginsbach
355 1.14 ginsbach snprintf(tmpname, sizeof(tmpname), "%s/unifdef.XXXXXX",
356 1.14 ginsbach dirname(ofilename));
357 1.14 ginsbach if ((ofd = mkstemp(tmpname)) != -1)
358 1.14 ginsbach output = fdopen(ofd, "w+");
359 1.14 ginsbach if (output == NULL)
360 1.14 ginsbach err(2, "can't create temporary file");
361 1.14 ginsbach fchmod(ofd, isb.st_mode & ACCESSPERMS);
362 1.14 ginsbach } else {
363 1.14 ginsbach output = fopen(ofilename, "w");
364 1.14 ginsbach if (output == NULL)
365 1.14 ginsbach err(2, "can't open %s", ofilename);
366 1.14 ginsbach }
367 1.14 ginsbach }
368 1.11 itojun process();
369 1.11 itojun abort(); /* bug */
370 1.11 itojun }
371 1.11 itojun
372 1.11 itojun static void
373 1.11 itojun usage(void)
374 1.11 itojun {
375 1.14 ginsbach fprintf(stderr, "usage: unifdef [-cdeklst] [-o output]"
376 1.11 itojun " [-Dsym[=val]] [-Usym] [-iDsym[=val]] [-iUsym] ... [file]\n");
377 1.11 itojun exit(2);
378 1.11 itojun }
379 1.11 itojun
380 1.11 itojun /*
381 1.11 itojun * A state transition function alters the global #if processing state
382 1.11 itojun * in a particular way. The table below is indexed by the current
383 1.11 itojun * processing state and the type of the current line.
384 1.11 itojun *
385 1.11 itojun * Nesting is handled by keeping a stack of states; some transition
386 1.11 itojun * functions increase or decrease the depth. They also maintain the
387 1.11 itojun * ignore state on a stack. In some complicated cases they have to
388 1.11 itojun * alter the preprocessor directive, as follows.
389 1.11 itojun *
390 1.11 itojun * When we have processed a group that starts off with a known-false
391 1.11 itojun * #if/#elif sequence (which has therefore been deleted) followed by a
392 1.11 itojun * #elif that we don't understand and therefore must keep, we edit the
393 1.11 itojun * latter into a #if to keep the nesting correct.
394 1.11 itojun *
395 1.11 itojun * When we find a true #elif in a group, the following block will
396 1.11 itojun * always be kept and the rest of the sequence after the next #elif or
397 1.11 itojun * #else will be discarded. We edit the #elif into a #else and the
398 1.11 itojun * following directive to #endif since this has the desired behaviour.
399 1.11 itojun *
400 1.11 itojun * "Dodgy" directives are split across multiple lines, the most common
401 1.11 itojun * example being a multi-line comment hanging off the right of the
402 1.11 itojun * directive. We can handle them correctly only if there is no change
403 1.11 itojun * from printing to dropping (or vice versa) caused by that directive.
404 1.11 itojun * If the directive is the first of a group we have a choice between
405 1.11 itojun * failing with an error, or passing it through unchanged instead of
406 1.11 itojun * evaluating it. The latter is not the default to avoid questions from
407 1.11 itojun * users about unifdef unexpectedly leaving behind preprocessor directives.
408 1.11 itojun */
409 1.11 itojun typedef void state_fn(void);
410 1.11 itojun
411 1.11 itojun /* report an error */
412 1.20 joerg __dead static void Eelif (void) { error("Inappropriate #elif"); }
413 1.20 joerg __dead static void Eelse (void) { error("Inappropriate #else"); }
414 1.20 joerg __dead static void Eendif(void) { error("Inappropriate #endif"); }
415 1.20 joerg __dead static void Eeof (void) { error("Premature EOF"); }
416 1.20 joerg __dead static void Eioccc(void) { error("Obfuscated preprocessor control line"); }
417 1.11 itojun /* plain line handling */
418 1.11 itojun static void print (void) { flushline(true); }
419 1.11 itojun static void drop (void) { flushline(false); }
420 1.11 itojun /* output lacks group's start line */
421 1.11 itojun static void Strue (void) { drop(); ignoreoff(); state(IS_TRUE_PREFIX); }
422 1.11 itojun static void Sfalse(void) { drop(); ignoreoff(); state(IS_FALSE_PREFIX); }
423 1.11 itojun static void Selse (void) { drop(); state(IS_TRUE_ELSE); }
424 1.11 itojun /* print/pass this block */
425 1.11 itojun static void Pelif (void) { print(); ignoreoff(); state(IS_PASS_MIDDLE); }
426 1.11 itojun static void Pelse (void) { print(); state(IS_PASS_ELSE); }
427 1.11 itojun static void Pendif(void) { print(); --depth; }
428 1.11 itojun /* discard this block */
429 1.11 itojun static void Dfalse(void) { drop(); ignoreoff(); state(IS_FALSE_TRAILER); }
430 1.11 itojun static void Delif (void) { drop(); ignoreoff(); state(IS_FALSE_MIDDLE); }
431 1.11 itojun static void Delse (void) { drop(); state(IS_FALSE_ELSE); }
432 1.11 itojun static void Dendif(void) { drop(); --depth; }
433 1.11 itojun /* first line of group */
434 1.11 itojun static void Fdrop (void) { nest(); Dfalse(); }
435 1.11 itojun static void Fpass (void) { nest(); Pelif(); }
436 1.11 itojun static void Ftrue (void) { nest(); Strue(); }
437 1.11 itojun static void Ffalse(void) { nest(); Sfalse(); }
438 1.11 itojun /* variable pedantry for obfuscated lines */
439 1.11 itojun static void Oiffy (void) { if (iocccok) Fpass(); else Eioccc(); ignoreon(); }
440 1.11 itojun static void Oif (void) { if (iocccok) Fpass(); else Eioccc(); }
441 1.11 itojun static void Oelif (void) { if (iocccok) Pelif(); else Eioccc(); }
442 1.11 itojun /* ignore comments in this block */
443 1.11 itojun static void Idrop (void) { Fdrop(); ignoreon(); }
444 1.11 itojun static void Itrue (void) { Ftrue(); ignoreon(); }
445 1.11 itojun static void Ifalse(void) { Ffalse(); ignoreon(); }
446 1.11 itojun /* edit this line */
447 1.11 itojun static void Mpass (void) { strncpy(keyword, "if ", 4); Pelif(); }
448 1.11 itojun static void Mtrue (void) { keywordedit("else\n"); state(IS_TRUE_MIDDLE); }
449 1.11 itojun static void Melif (void) { keywordedit("endif\n"); state(IS_FALSE_TRAILER); }
450 1.11 itojun static void Melse (void) { keywordedit("endif\n"); state(IS_FALSE_ELSE); }
451 1.11 itojun
452 1.11 itojun static state_fn * const trans_table[IS_COUNT][LT_COUNT] = {
453 1.11 itojun /* IS_OUTSIDE */
454 1.11 itojun { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Eelif, Eelif, Eelse, Eendif,
455 1.11 itojun Oiffy, Oiffy, Fpass, Oif, Oif, Eelif, Eelif, Eelif, Eelse, Eendif,
456 1.11 itojun print, done },
457 1.11 itojun /* IS_FALSE_PREFIX */
458 1.11 itojun { Idrop, Idrop, Fdrop, Fdrop, Fdrop, Mpass, Strue, Sfalse,Selse, Dendif,
459 1.11 itojun Idrop, Idrop, Fdrop, Fdrop, Fdrop, Mpass, Eioccc,Eioccc,Eioccc,Eioccc,
460 1.11 itojun drop, Eeof },
461 1.11 itojun /* IS_TRUE_PREFIX */
462 1.11 itojun { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Dfalse,Dfalse,Dfalse,Delse, Dendif,
463 1.11 itojun Oiffy, Oiffy, Fpass, Oif, Oif, Eioccc,Eioccc,Eioccc,Eioccc,Eioccc,
464 1.11 itojun print, Eeof },
465 1.11 itojun /* IS_PASS_MIDDLE */
466 1.11 itojun { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Pelif, Mtrue, Delif, Pelse, Pendif,
467 1.11 itojun Oiffy, Oiffy, Fpass, Oif, Oif, Pelif, Oelif, Oelif, Pelse, Pendif,
468 1.11 itojun print, Eeof },
469 1.11 itojun /* IS_FALSE_MIDDLE */
470 1.11 itojun { Idrop, Idrop, Fdrop, Fdrop, Fdrop, Pelif, Mtrue, Delif, Pelse, Pendif,
471 1.11 itojun Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eioccc,Eioccc,Eioccc,Eioccc,Eioccc,
472 1.11 itojun drop, Eeof },
473 1.11 itojun /* IS_TRUE_MIDDLE */
474 1.11 itojun { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Melif, Melif, Melif, Melse, Pendif,
475 1.11 itojun Oiffy, Oiffy, Fpass, Oif, Oif, Eioccc,Eioccc,Eioccc,Eioccc,Pendif,
476 1.11 itojun print, Eeof },
477 1.11 itojun /* IS_PASS_ELSE */
478 1.11 itojun { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Eelif, Eelif, Eelse, Pendif,
479 1.11 itojun Oiffy, Oiffy, Fpass, Oif, Oif, Eelif, Eelif, Eelif, Eelse, Pendif,
480 1.11 itojun print, Eeof },
481 1.11 itojun /* IS_FALSE_ELSE */
482 1.11 itojun { Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eelif, Eelif, Eelif, Eelse, Dendif,
483 1.11 itojun Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eelif, Eelif, Eelif, Eelse, Eioccc,
484 1.11 itojun drop, Eeof },
485 1.11 itojun /* IS_TRUE_ELSE */
486 1.11 itojun { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Eelif, Eelif, Eelse, Dendif,
487 1.11 itojun Oiffy, Oiffy, Fpass, Oif, Oif, Eelif, Eelif, Eelif, Eelse, Eioccc,
488 1.11 itojun print, Eeof },
489 1.11 itojun /* IS_FALSE_TRAILER */
490 1.11 itojun { Idrop, Idrop, Fdrop, Fdrop, Fdrop, Dfalse,Dfalse,Dfalse,Delse, Dendif,
491 1.11 itojun Idrop, Idrop, Fdrop, Fdrop, Fdrop, Dfalse,Dfalse,Dfalse,Delse, Eioccc,
492 1.11 itojun drop, Eeof }
493 1.11 itojun /*TRUEI FALSEI IF TRUE FALSE ELIF ELTRUE ELFALSE ELSE ENDIF
494 1.11 itojun TRUEI FALSEI IF TRUE FALSE ELIF ELTRUE ELFALSE ELSE ENDIF (DODGY)
495 1.11 itojun PLAIN EOF */
496 1.11 itojun };
497 1.1 cgd
498 1.11 itojun /*
499 1.11 itojun * State machine utility functions
500 1.11 itojun */
501 1.11 itojun static void
502 1.11 itojun done(void)
503 1.11 itojun {
504 1.11 itojun if (incomment)
505 1.11 itojun error("EOF in comment");
506 1.14 ginsbach if (fclose(output)) {
507 1.14 ginsbach if (overwriting) {
508 1.14 ginsbach unlink(tmpname);
509 1.19 ginsbach errx(2, "%s unchanged", ofilename);
510 1.14 ginsbach }
511 1.14 ginsbach }
512 1.19 ginsbach if (overwriting && rename(tmpname, ofilename)) {
513 1.14 ginsbach unlink(tmpname);
514 1.19 ginsbach errx(2, "%s unchanged", ofilename);
515 1.14 ginsbach }
516 1.5 lukem exit(exitstat);
517 1.1 cgd }
518 1.11 itojun static void
519 1.11 itojun ignoreoff(void)
520 1.11 itojun {
521 1.11 itojun ignoring[depth] = ignoring[depth-1];
522 1.11 itojun }
523 1.11 itojun static void
524 1.11 itojun ignoreon(void)
525 1.11 itojun {
526 1.11 itojun ignoring[depth] = true;
527 1.11 itojun }
528 1.11 itojun static void
529 1.11 itojun keywordedit(const char *replacement)
530 1.11 itojun {
531 1.11 itojun strlcpy(keyword, replacement, tline + sizeof(tline) - keyword);
532 1.11 itojun print();
533 1.11 itojun }
534 1.11 itojun static void
535 1.11 itojun nest(void)
536 1.11 itojun {
537 1.11 itojun depth += 1;
538 1.11 itojun if (depth >= MAXDEPTH)
539 1.11 itojun error("Too many levels of nesting");
540 1.11 itojun stifline[depth] = linenum;
541 1.11 itojun }
542 1.11 itojun static void
543 1.11 itojun state(Ifstate is)
544 1.11 itojun {
545 1.11 itojun ifstate[depth] = is;
546 1.11 itojun }
547 1.11 itojun
548 1.11 itojun /*
549 1.11 itojun * Write a line to the output or not, according to command line options.
550 1.11 itojun */
551 1.11 itojun static void
552 1.11 itojun flushline(bool keep)
553 1.11 itojun {
554 1.11 itojun if (symlist)
555 1.11 itojun return;
556 1.11 itojun if (keep ^ complement)
557 1.14 ginsbach fputs(tline, output);
558 1.11 itojun else {
559 1.11 itojun if (lnblank)
560 1.14 ginsbach putc('\n', output);
561 1.11 itojun exitstat = 1;
562 1.11 itojun }
563 1.1 cgd }
564 1.1 cgd
565 1.11 itojun /*
566 1.11 itojun * The driver for the state machine.
567 1.11 itojun */
568 1.11 itojun static void
569 1.11 itojun process(void)
570 1.5 lukem {
571 1.5 lukem Linetype lineval;
572 1.5 lukem
573 1.5 lukem for (;;) {
574 1.11 itojun linenum++;
575 1.15 roy lineval = get_line();
576 1.11 itojun trans_table[ifstate[depth]][lineval]();
577 1.11 itojun debug("process %s -> %s depth %d",
578 1.11 itojun linetype_name[lineval],
579 1.11 itojun ifstate_name[ifstate[depth]], depth);
580 1.11 itojun }
581 1.11 itojun }
582 1.11 itojun
583 1.11 itojun /*
584 1.11 itojun * Parse a line and determine its type. We keep the preprocessor line
585 1.11 itojun * parser state between calls in the global variable linestate, with
586 1.11 itojun * help from skipcomment().
587 1.11 itojun */
588 1.11 itojun static Linetype
589 1.15 roy get_line(void)
590 1.11 itojun {
591 1.11 itojun const char *cp;
592 1.11 itojun int cursym;
593 1.11 itojun int kwlen;
594 1.11 itojun Linetype retval;
595 1.11 itojun Comment_state wascomment;
596 1.5 lukem
597 1.11 itojun if (fgets(tline, MAXLINE, input) == NULL)
598 1.11 itojun return (LT_EOF);
599 1.11 itojun retval = LT_PLAIN;
600 1.11 itojun wascomment = incomment;
601 1.11 itojun cp = skipcomment(tline);
602 1.11 itojun if (linestate == LS_START) {
603 1.11 itojun if (*cp == '#') {
604 1.11 itojun linestate = LS_HASH;
605 1.11 itojun cp = skipcomment(cp + 1);
606 1.11 itojun } else if (*cp != '\0')
607 1.11 itojun linestate = LS_DIRTY;
608 1.11 itojun }
609 1.11 itojun if (!incomment && linestate == LS_HASH) {
610 1.11 itojun keyword = tline + (cp - tline);
611 1.11 itojun cp = skipsym(cp);
612 1.11 itojun kwlen = cp - keyword;
613 1.11 itojun /* no way can we deal with a continuation inside a keyword */
614 1.11 itojun if (strncmp(cp, "\\\n", 2) == 0)
615 1.11 itojun Eioccc();
616 1.11 itojun if (strlcmp("ifdef", keyword, kwlen) == 0 ||
617 1.11 itojun strlcmp("ifndef", keyword, kwlen) == 0) {
618 1.11 itojun cp = skipcomment(cp);
619 1.11 itojun if ((cursym = findsym(cp)) < 0)
620 1.11 itojun retval = LT_IF;
621 1.5 lukem else {
622 1.11 itojun retval = (keyword[2] == 'n')
623 1.11 itojun ? LT_FALSE : LT_TRUE;
624 1.11 itojun if (value[cursym] == NULL)
625 1.11 itojun retval = (retval == LT_TRUE)
626 1.11 itojun ? LT_FALSE : LT_TRUE;
627 1.11 itojun if (ignore[cursym])
628 1.11 itojun retval = (retval == LT_TRUE)
629 1.11 itojun ? LT_TRUEI : LT_FALSEI;
630 1.11 itojun }
631 1.11 itojun cp = skipsym(cp);
632 1.11 itojun } else if (strlcmp("if", keyword, kwlen) == 0)
633 1.11 itojun retval = ifeval(&cp);
634 1.11 itojun else if (strlcmp("elif", keyword, kwlen) == 0)
635 1.11 itojun retval = ifeval(&cp) - LT_IF + LT_ELIF;
636 1.11 itojun else if (strlcmp("else", keyword, kwlen) == 0)
637 1.11 itojun retval = LT_ELSE;
638 1.11 itojun else if (strlcmp("endif", keyword, kwlen) == 0)
639 1.11 itojun retval = LT_ENDIF;
640 1.11 itojun else {
641 1.11 itojun linestate = LS_DIRTY;
642 1.11 itojun retval = LT_PLAIN;
643 1.11 itojun }
644 1.11 itojun cp = skipcomment(cp);
645 1.11 itojun if (*cp != '\0') {
646 1.11 itojun linestate = LS_DIRTY;
647 1.11 itojun if (retval == LT_TRUE || retval == LT_FALSE ||
648 1.11 itojun retval == LT_TRUEI || retval == LT_FALSEI)
649 1.11 itojun retval = LT_IF;
650 1.11 itojun if (retval == LT_ELTRUE || retval == LT_ELFALSE)
651 1.11 itojun retval = LT_ELIF;
652 1.11 itojun }
653 1.11 itojun if (retval != LT_PLAIN && (wascomment || incomment)) {
654 1.11 itojun retval += LT_DODGY;
655 1.11 itojun if (incomment)
656 1.11 itojun linestate = LS_DIRTY;
657 1.11 itojun }
658 1.11 itojun /* skipcomment should have changed the state */
659 1.11 itojun if (linestate == LS_HASH)
660 1.11 itojun abort(); /* bug */
661 1.11 itojun }
662 1.11 itojun if (linestate == LS_DIRTY) {
663 1.11 itojun while (*cp != '\0')
664 1.11 itojun cp = skipcomment(cp + 1);
665 1.11 itojun }
666 1.11 itojun debug("parser %s comment %s line",
667 1.11 itojun comment_name[incomment], linestate_name[linestate]);
668 1.11 itojun return (retval);
669 1.11 itojun }
670 1.11 itojun
671 1.11 itojun /*
672 1.11 itojun * These are the binary operators that are supported by the expression
673 1.11 itojun * evaluator. Note that if support for division is added then we also
674 1.11 itojun * need short-circuiting booleans because of divide-by-zero.
675 1.11 itojun */
676 1.11 itojun static int op_lt(int a, int b) { return (a < b); }
677 1.11 itojun static int op_gt(int a, int b) { return (a > b); }
678 1.11 itojun static int op_le(int a, int b) { return (a <= b); }
679 1.11 itojun static int op_ge(int a, int b) { return (a >= b); }
680 1.11 itojun static int op_eq(int a, int b) { return (a == b); }
681 1.11 itojun static int op_ne(int a, int b) { return (a != b); }
682 1.11 itojun static int op_or(int a, int b) { return (a || b); }
683 1.11 itojun static int op_and(int a, int b) { return (a && b); }
684 1.11 itojun
685 1.11 itojun /*
686 1.11 itojun * An evaluation function takes three arguments, as follows: (1) a pointer to
687 1.11 itojun * an element of the precedence table which lists the operators at the current
688 1.11 itojun * level of precedence; (2) a pointer to an integer which will receive the
689 1.11 itojun * value of the expression; and (3) a pointer to a char* that points to the
690 1.11 itojun * expression to be evaluated and that is updated to the end of the expression
691 1.11 itojun * when evaluation is complete. The function returns LT_FALSE if the value of
692 1.11 itojun * the expression is zero, LT_TRUE if it is non-zero, or LT_IF if the
693 1.11 itojun * expression could not be evaluated.
694 1.11 itojun */
695 1.11 itojun struct ops;
696 1.11 itojun
697 1.11 itojun typedef Linetype eval_fn(const struct ops *, int *, const char **);
698 1.5 lukem
699 1.11 itojun static eval_fn eval_table, eval_unary;
700 1.5 lukem
701 1.11 itojun /*
702 1.11 itojun * The precedence table. Expressions involving binary operators are evaluated
703 1.11 itojun * in a table-driven way by eval_table. When it evaluates a subexpression it
704 1.11 itojun * calls the inner function with its first argument pointing to the next
705 1.11 itojun * element of the table. Innermost expressions have special non-table-driven
706 1.11 itojun * handling.
707 1.11 itojun */
708 1.11 itojun static const struct ops {
709 1.11 itojun eval_fn *inner;
710 1.11 itojun struct op {
711 1.11 itojun const char *str;
712 1.11 itojun int (*fn)(int, int);
713 1.11 itojun } op[5];
714 1.11 itojun } eval_ops[] = {
715 1.11 itojun { eval_table, { { "||", op_or } } },
716 1.11 itojun { eval_table, { { "&&", op_and } } },
717 1.11 itojun { eval_table, { { "==", op_eq },
718 1.11 itojun { "!=", op_ne } } },
719 1.11 itojun { eval_unary, { { "<=", op_le },
720 1.11 itojun { ">=", op_ge },
721 1.11 itojun { "<", op_lt },
722 1.11 itojun { ">", op_gt } } }
723 1.11 itojun };
724 1.5 lukem
725 1.11 itojun /*
726 1.11 itojun * Function for evaluating the innermost parts of expressions,
727 1.11 itojun * viz. !expr (expr) defined(symbol) symbol number
728 1.11 itojun * We reset the keepthis flag when we find a non-constant subexpression.
729 1.11 itojun */
730 1.11 itojun static Linetype
731 1.11 itojun eval_unary(const struct ops *ops, int *valp, const char **cpp)
732 1.11 itojun {
733 1.11 itojun const char *cp;
734 1.11 itojun char *ep;
735 1.11 itojun int sym;
736 1.11 itojun
737 1.11 itojun cp = skipcomment(*cpp);
738 1.11 itojun if (*cp == '!') {
739 1.21 joerg debug("eval%td !", ops - eval_ops);
740 1.11 itojun cp++;
741 1.11 itojun if (eval_unary(ops, valp, &cp) == LT_IF)
742 1.11 itojun return (LT_IF);
743 1.11 itojun *valp = !*valp;
744 1.11 itojun } else if (*cp == '(') {
745 1.11 itojun cp++;
746 1.21 joerg debug("eval%td (", ops - eval_ops);
747 1.11 itojun if (eval_table(eval_ops, valp, &cp) == LT_IF)
748 1.11 itojun return (LT_IF);
749 1.11 itojun cp = skipcomment(cp);
750 1.11 itojun if (*cp++ != ')')
751 1.11 itojun return (LT_IF);
752 1.11 itojun } else if (isdigit((unsigned char)*cp)) {
753 1.21 joerg debug("eval%td number", ops - eval_ops);
754 1.11 itojun *valp = strtol(cp, &ep, 0);
755 1.11 itojun cp = skipsym(cp);
756 1.11 itojun } else if (strncmp(cp, "defined", 7) == 0 && endsym(cp[7])) {
757 1.11 itojun cp = skipcomment(cp+7);
758 1.21 joerg debug("eval%td defined", ops - eval_ops);
759 1.11 itojun if (*cp++ != '(')
760 1.11 itojun return (LT_IF);
761 1.11 itojun cp = skipcomment(cp);
762 1.11 itojun sym = findsym(cp);
763 1.16 christos if (sym < 0 || symlist)
764 1.11 itojun return (LT_IF);
765 1.11 itojun *valp = (value[sym] != NULL);
766 1.11 itojun cp = skipsym(cp);
767 1.11 itojun cp = skipcomment(cp);
768 1.11 itojun if (*cp++ != ')')
769 1.11 itojun return (LT_IF);
770 1.11 itojun keepthis = false;
771 1.11 itojun } else if (!endsym(*cp)) {
772 1.21 joerg debug("eval%td symbol", ops - eval_ops);
773 1.11 itojun sym = findsym(cp);
774 1.16 christos if (sym < 0 || symlist)
775 1.11 itojun return (LT_IF);
776 1.11 itojun if (value[sym] == NULL)
777 1.11 itojun *valp = 0;
778 1.11 itojun else {
779 1.11 itojun *valp = strtol(value[sym], &ep, 0);
780 1.11 itojun if (*ep != '\0' || ep == value[sym])
781 1.11 itojun return (LT_IF);
782 1.1 cgd }
783 1.11 itojun cp = skipsym(cp);
784 1.11 itojun keepthis = false;
785 1.11 itojun } else {
786 1.21 joerg debug("eval%td bad expr", ops - eval_ops);
787 1.11 itojun return (LT_IF);
788 1.1 cgd }
789 1.11 itojun
790 1.11 itojun *cpp = cp;
791 1.21 joerg debug("eval%td = %d", ops - eval_ops, *valp);
792 1.11 itojun return (*valp ? LT_TRUE : LT_FALSE);
793 1.1 cgd }
794 1.1 cgd
795 1.11 itojun /*
796 1.11 itojun * Table-driven evaluation of binary operators.
797 1.11 itojun */
798 1.11 itojun static Linetype
799 1.11 itojun eval_table(const struct ops *ops, int *valp, const char **cpp)
800 1.11 itojun {
801 1.11 itojun const struct op *op;
802 1.11 itojun const char *cp;
803 1.11 itojun int val;
804 1.11 itojun
805 1.21 joerg debug("eval%td", ops - eval_ops);
806 1.11 itojun cp = *cpp;
807 1.11 itojun if (ops->inner(ops+1, valp, &cp) == LT_IF)
808 1.11 itojun return (LT_IF);
809 1.11 itojun for (;;) {
810 1.11 itojun cp = skipcomment(cp);
811 1.11 itojun for (op = ops->op; op->str != NULL; op++)
812 1.11 itojun if (strncmp(cp, op->str, strlen(op->str)) == 0)
813 1.11 itojun break;
814 1.11 itojun if (op->str == NULL)
815 1.11 itojun break;
816 1.11 itojun cp += strlen(op->str);
817 1.21 joerg debug("eval%td %s", ops - eval_ops, op->str);
818 1.11 itojun if (ops->inner(ops+1, &val, &cp) == LT_IF)
819 1.11 itojun return (LT_IF);
820 1.11 itojun *valp = op->fn(*valp, val);
821 1.11 itojun }
822 1.1 cgd
823 1.11 itojun *cpp = cp;
824 1.21 joerg debug("eval%td = %d", ops - eval_ops, *valp);
825 1.11 itojun return (*valp ? LT_TRUE : LT_FALSE);
826 1.11 itojun }
827 1.5 lukem
828 1.11 itojun /*
829 1.11 itojun * Evaluate the expression on a #if or #elif line. If we can work out
830 1.11 itojun * the result we return LT_TRUE or LT_FALSE accordingly, otherwise we
831 1.11 itojun * return just a generic LT_IF.
832 1.11 itojun */
833 1.11 itojun static Linetype
834 1.11 itojun ifeval(const char **cpp)
835 1.11 itojun {
836 1.11 itojun int ret;
837 1.11 itojun int val;
838 1.5 lukem
839 1.11 itojun debug("eval %s", *cpp);
840 1.11 itojun keepthis = killconsts ? false : true;
841 1.11 itojun ret = eval_table(eval_ops, &val, cpp);
842 1.11 itojun debug("eval = %d", val);
843 1.11 itojun return (keepthis ? LT_IF : ret);
844 1.11 itojun }
845 1.9 itojun
846 1.1 cgd /*
847 1.11 itojun * Skip over comments and stop at the next character position that is
848 1.11 itojun * not whitespace. Between calls we keep the comment state in the
849 1.11 itojun * global variable incomment, and we also adjust the global variable
850 1.11 itojun * linestate when we see a newline.
851 1.11 itojun * XXX: doesn't cope with the buffer splitting inside a state transition.
852 1.1 cgd */
853 1.11 itojun static const char *
854 1.11 itojun skipcomment(const char *cp)
855 1.5 lukem {
856 1.11 itojun if (text || ignoring[depth]) {
857 1.11 itojun for (; isspace((unsigned char)*cp); cp++)
858 1.11 itojun if (*cp == '\n')
859 1.11 itojun linestate = LS_START;
860 1.11 itojun return (cp);
861 1.11 itojun }
862 1.11 itojun while (*cp != '\0')
863 1.11 itojun /* don't reset to LS_START after a line continuation */
864 1.11 itojun if (strncmp(cp, "\\\n", 2) == 0)
865 1.11 itojun cp += 2;
866 1.11 itojun else switch (incomment) {
867 1.11 itojun case NO_COMMENT:
868 1.11 itojun if (strncmp(cp, "/\\\n", 3) == 0) {
869 1.11 itojun incomment = STARTING_COMMENT;
870 1.11 itojun cp += 3;
871 1.11 itojun } else if (strncmp(cp, "/*", 2) == 0) {
872 1.11 itojun incomment = C_COMMENT;
873 1.11 itojun cp += 2;
874 1.11 itojun } else if (strncmp(cp, "//", 2) == 0) {
875 1.11 itojun incomment = CXX_COMMENT;
876 1.11 itojun cp += 2;
877 1.11 itojun } else if (strncmp(cp, "\n", 1) == 0) {
878 1.11 itojun linestate = LS_START;
879 1.11 itojun cp += 1;
880 1.11 itojun } else if (strchr(" \t", *cp) != NULL) {
881 1.11 itojun cp += 1;
882 1.11 itojun } else
883 1.11 itojun return (cp);
884 1.11 itojun continue;
885 1.11 itojun case CXX_COMMENT:
886 1.11 itojun if (strncmp(cp, "\n", 1) == 0) {
887 1.11 itojun incomment = NO_COMMENT;
888 1.11 itojun linestate = LS_START;
889 1.11 itojun }
890 1.11 itojun cp += 1;
891 1.11 itojun continue;
892 1.11 itojun case C_COMMENT:
893 1.11 itojun if (strncmp(cp, "*\\\n", 3) == 0) {
894 1.11 itojun incomment = FINISHING_COMMENT;
895 1.11 itojun cp += 3;
896 1.11 itojun } else if (strncmp(cp, "*/", 2) == 0) {
897 1.11 itojun incomment = NO_COMMENT;
898 1.11 itojun cp += 2;
899 1.11 itojun } else
900 1.11 itojun cp += 1;
901 1.11 itojun continue;
902 1.11 itojun case STARTING_COMMENT:
903 1.11 itojun if (*cp == '*') {
904 1.6 wsanchez incomment = C_COMMENT;
905 1.11 itojun cp += 1;
906 1.11 itojun } else if (*cp == '/') {
907 1.6 wsanchez incomment = CXX_COMMENT;
908 1.11 itojun cp += 1;
909 1.11 itojun } else {
910 1.11 itojun incomment = NO_COMMENT;
911 1.11 itojun linestate = LS_DIRTY;
912 1.6 wsanchez }
913 1.11 itojun continue;
914 1.11 itojun case FINISHING_COMMENT:
915 1.11 itojun if (*cp == '/') {
916 1.11 itojun incomment = NO_COMMENT;
917 1.11 itojun cp += 1;
918 1.11 itojun } else
919 1.11 itojun incomment = C_COMMENT;
920 1.11 itojun continue;
921 1.11 itojun default:
922 1.11 itojun abort(); /* bug */
923 1.5 lukem }
924 1.11 itojun return (cp);
925 1.1 cgd }
926 1.11 itojun
927 1.1 cgd /*
928 1.11 itojun * Skip over an identifier.
929 1.1 cgd */
930 1.11 itojun static const char *
931 1.11 itojun skipsym(const char *cp)
932 1.11 itojun {
933 1.11 itojun while (!endsym(*cp))
934 1.11 itojun ++cp;
935 1.11 itojun return (cp);
936 1.1 cgd }
937 1.11 itojun
938 1.1 cgd /*
939 1.18 mbalmer * Look for the symbol in the symbol table. If it is found, we return
940 1.11 itojun * the symbol table index, else we return -1.
941 1.1 cgd */
942 1.11 itojun static int
943 1.11 itojun findsym(const char *str)
944 1.1 cgd {
945 1.11 itojun const char *cp;
946 1.11 itojun int symind;
947 1.5 lukem
948 1.11 itojun cp = skipsym(str);
949 1.11 itojun if (cp == str)
950 1.11 itojun return (-1);
951 1.11 itojun if (symlist)
952 1.11 itojun printf("%.*s\n", (int)(cp-str), str);
953 1.5 lukem for (symind = 0; symind < nsyms; ++symind) {
954 1.11 itojun if (strlcmp(symname[symind], str, cp-str) == 0) {
955 1.11 itojun debug("findsym %s %s", symname[symind],
956 1.11 itojun value[symind] ? value[symind] : "");
957 1.11 itojun return (symind);
958 1.5 lukem }
959 1.1 cgd }
960 1.11 itojun return (-1);
961 1.1 cgd }
962 1.11 itojun
963 1.1 cgd /*
964 1.11 itojun * Add a symbol to the symbol table.
965 1.1 cgd */
966 1.11 itojun static void
967 1.11 itojun addsym(bool ignorethis, bool definethis, char *sym)
968 1.11 itojun {
969 1.11 itojun int symind;
970 1.11 itojun char *val;
971 1.5 lukem
972 1.11 itojun symind = findsym(sym);
973 1.11 itojun if (symind < 0) {
974 1.11 itojun if (nsyms >= MAXSYMS)
975 1.11 itojun errx(2, "too many symbols");
976 1.11 itojun symind = nsyms++;
977 1.11 itojun }
978 1.11 itojun symname[symind] = sym;
979 1.11 itojun ignore[symind] = ignorethis;
980 1.11 itojun val = sym + (skipsym(sym) - sym);
981 1.11 itojun if (definethis) {
982 1.11 itojun if (*val == '=') {
983 1.11 itojun value[symind] = val+1;
984 1.11 itojun *val = '\0';
985 1.11 itojun } else if (*val == '\0')
986 1.11 itojun value[symind] = "";
987 1.11 itojun else
988 1.11 itojun usage();
989 1.11 itojun } else {
990 1.11 itojun if (*val != '\0')
991 1.11 itojun usage();
992 1.11 itojun value[symind] = NULL;
993 1.5 lukem }
994 1.1 cgd }
995 1.1 cgd
996 1.11 itojun /*
997 1.11 itojun * Compare s with n characters of t.
998 1.11 itojun * The same as strncmp() except that it checks that s[n] == '\0'.
999 1.11 itojun */
1000 1.11 itojun static int
1001 1.11 itojun strlcmp(const char *s, const char *t, size_t n)
1002 1.1 cgd {
1003 1.11 itojun while (n-- && *t != '\0')
1004 1.11 itojun if (*s != *t)
1005 1.11 itojun return ((unsigned char)*s - (unsigned char)*t);
1006 1.11 itojun else
1007 1.11 itojun ++s, ++t;
1008 1.11 itojun return ((unsigned char)*s);
1009 1.1 cgd }
1010 1.1 cgd
1011 1.11 itojun /*
1012 1.11 itojun * Diagnostics.
1013 1.11 itojun */
1014 1.11 itojun static void
1015 1.11 itojun debug(const char *msg, ...)
1016 1.11 itojun {
1017 1.11 itojun va_list ap;
1018 1.11 itojun
1019 1.11 itojun if (debugging) {
1020 1.11 itojun va_start(ap, msg);
1021 1.11 itojun vwarnx(msg, ap);
1022 1.11 itojun va_end(ap);
1023 1.11 itojun }
1024 1.11 itojun }
1025 1.1 cgd
1026 1.11 itojun static void
1027 1.11 itojun error(const char *msg)
1028 1.11 itojun {
1029 1.11 itojun if (depth == 0)
1030 1.11 itojun warnx("%s: %d: %s", filename, linenum, msg);
1031 1.11 itojun else
1032 1.11 itojun warnx("%s: %d: %s (#if line %d depth %d)",
1033 1.11 itojun filename, linenum, msg, stifline[depth], depth);
1034 1.14 ginsbach fclose(output);
1035 1.14 ginsbach if (overwriting) {
1036 1.14 ginsbach unlink(tmpname);
1037 1.19 ginsbach errx(2, "%s unchanged", ofilename);
1038 1.14 ginsbach }
1039 1.11 itojun errx(2, "output may be truncated");
1040 1.1 cgd }
1041