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