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