1 |
/* $XConsortium: parse.c,v 1.30 94/04/17 20:10:38 gildea Exp $ */ |
2 |
/* |
3 |
* Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved. |
4 |
* |
5 |
* Redistribution and use in source and binary forms, with or without |
6 |
* modification, are permitted provided that the following conditions |
7 |
* are met: |
8 |
* |
9 |
* 1. Redistributions of source code must retain the above copyright |
10 |
* notice, this list of conditions and the following disclaimer. |
11 |
* |
12 |
* 2. Redistributions in binary form must reproduce the above copyright |
13 |
* notice, this list of conditions and the following disclaimer in |
14 |
* the documentation and/or other materials provided with the |
15 |
* distribution. |
16 |
* |
17 |
* 3. The name "Carnegie Mellon University" must not be used to |
18 |
* endorse or promote products derived from this software without |
19 |
* prior written permission. For permission or any other legal |
20 |
* details, please contact |
21 |
* Office of Technology Transfer |
22 |
* Carnegie Mellon University |
23 |
* 5000 Forbes Avenue |
24 |
* Pittsburgh, PA 15213-3890 |
25 |
* (412) 268-4387, fax: (412) 268-7395 |
26 |
* tech-transfer@andrew.cmu.edu |
27 |
* |
28 |
* 4. Redistributions of any form whatsoever must retain the following |
29 |
* acknowledgment: |
30 |
* "This product includes software developed by Computing Services |
31 |
* at Carnegie Mellon University (http://www.cmu.edu/computing/)." |
32 |
* |
33 |
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO |
34 |
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY |
35 |
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE |
36 |
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
37 |
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN |
38 |
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING |
39 |
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
40 |
* |
41 |
*/ |
42 |
|
43 |
/* |
44 |
|
45 |
Copyright (c) 1993, 1994 X Consortium |
46 |
|
47 |
Permission is hereby granted, free of charge, to any person obtaining a copy |
48 |
of this software and associated documentation files (the "Software"), to deal |
49 |
in the Software without restriction, including without limitation the rights |
50 |
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
51 |
copies of the Software, and to permit persons to whom the Software is |
52 |
furnished to do so, subject to the following conditions: |
53 |
|
54 |
The above copyright notice and this permission notice shall be included in |
55 |
all copies or substantial portions of the Software. |
56 |
|
57 |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
58 |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
59 |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
60 |
X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN |
61 |
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
62 |
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
63 |
|
64 |
Except as contained in this notice, the name of the X Consortium shall not be |
65 |
used in advertising or otherwise to promote the sale, use or other dealings |
66 |
in this Software without prior written authorization from the X Consortium. |
67 |
|
68 |
*/ |
69 |
|
70 |
#include "def.h" |
71 |
|
72 |
extern char *directives[]; |
73 |
extern struct inclist maininclist; |
74 |
|
75 |
find_includes(filep, file, file_red, recursion, failOK) |
76 |
struct filepointer *filep; |
77 |
struct inclist *file, *file_red; |
78 |
int recursion; |
79 |
boolean failOK; |
80 |
{ |
81 |
register char *line; |
82 |
register int type; |
83 |
boolean recfailOK; |
84 |
|
85 |
while (line = getline(filep)) { |
86 |
switch(type = deftype(line, filep, file_red, file, TRUE)) { |
87 |
case IF: |
88 |
doif: |
89 |
type = find_includes(filep, file, |
90 |
file_red, recursion+1, failOK); |
91 |
while ((type == ELIF) || (type == ELIFFALSE) || |
92 |
(type == ELIFGUESSFALSE)) |
93 |
type = gobble(filep, file, file_red); |
94 |
if (type == ELSE) |
95 |
gobble(filep, file, file_red); |
96 |
break; |
97 |
case IFFALSE: |
98 |
case IFGUESSFALSE: |
99 |
doiffalse: |
100 |
if (type == IFGUESSFALSE || type == ELIFGUESSFALSE) |
101 |
recfailOK = TRUE; |
102 |
else |
103 |
recfailOK = failOK; |
104 |
type = gobble(filep, file, file_red); |
105 |
if (type == ELSE) |
106 |
find_includes(filep, file, |
107 |
file_red, recursion+1, recfailOK); |
108 |
else |
109 |
if (type == ELIF) |
110 |
goto doif; |
111 |
else |
112 |
if ((type == ELIFFALSE) || (type == ELIFGUESSFALSE)) |
113 |
goto doiffalse; |
114 |
break; |
115 |
case IFDEF: |
116 |
case IFNDEF: |
117 |
if ((type == IFDEF && isdefined(line, file_red, NULL)) |
118 |
|| (type == IFNDEF && !isdefined(line, file_red, NULL))) { |
119 |
debug(1,(type == IFNDEF ? |
120 |
"line %d: %s !def'd in %s via %s%s\n" : "", |
121 |
filep->f_line, line, |
122 |
file->i_file, file_red->i_file, ": doit")); |
123 |
type = find_includes(filep, file, |
124 |
file_red, recursion+1, failOK); |
125 |
while (type == ELIF || type == ELIFFALSE || type == ELIFGUESSFALSE) |
126 |
type = gobble(filep, file, file_red); |
127 |
if (type == ELSE) |
128 |
gobble(filep, file, file_red); |
129 |
} |
130 |
else { |
131 |
debug(1,(type == IFDEF ? |
132 |
"line %d: %s !def'd in %s via %s%s\n" : "", |
133 |
filep->f_line, line, |
134 |
file->i_file, file_red->i_file, ": gobble")); |
135 |
type = gobble(filep, file, file_red); |
136 |
if (type == ELSE) |
137 |
find_includes(filep, file, |
138 |
file_red, recursion+1, failOK); |
139 |
else if (type == ELIF) |
140 |
goto doif; |
141 |
else if (type == ELIFFALSE || type == ELIFGUESSFALSE) |
142 |
goto doiffalse; |
143 |
} |
144 |
break; |
145 |
case ELSE: |
146 |
case ELIFFALSE: |
147 |
case ELIFGUESSFALSE: |
148 |
case ELIF: |
149 |
if (!recursion) |
150 |
gobble(filep, file, file_red); |
151 |
case ENDIF: |
152 |
if (recursion) |
153 |
return(type); |
154 |
case DEFINE: |
155 |
define(line, file); |
156 |
break; |
157 |
case UNDEF: |
158 |
if (!*line) { |
159 |
warning("%s, line %d: incomplete undef == \"%s\"\n", |
160 |
file_red->i_file, filep->f_line, line); |
161 |
break; |
162 |
} |
163 |
undefine(line, file_red); |
164 |
break; |
165 |
case INCLUDE: |
166 |
add_include(filep, file, file_red, line, FALSE, failOK); |
167 |
break; |
168 |
case INCLUDEDOT: |
169 |
add_include(filep, file, file_red, line, TRUE, failOK); |
170 |
break; |
171 |
case ERROR: |
172 |
warning("%s: %d: %s\n", file_red->i_file, |
173 |
filep->f_line, line); |
174 |
break; |
175 |
|
176 |
case PRAGMA: |
177 |
case IDENT: |
178 |
case SCCS: |
179 |
case EJECT: |
180 |
break; |
181 |
case -1: |
182 |
warning("%s", file_red->i_file); |
183 |
if (file_red != file) |
184 |
warning1(" (reading %s)", file->i_file); |
185 |
warning1(", line %d: unknown directive == \"%s\"\n", |
186 |
filep->f_line, line); |
187 |
break; |
188 |
case -2: |
189 |
warning("%s", file_red->i_file); |
190 |
if (file_red != file) |
191 |
warning1(" (reading %s)", file->i_file); |
192 |
warning1(", line %d: incomplete include == \"%s\"\n", |
193 |
filep->f_line, line); |
194 |
break; |
195 |
} |
196 |
} |
197 |
return(-1); |
198 |
} |
199 |
|
200 |
gobble(filep, file, file_red) |
201 |
register struct filepointer *filep; |
202 |
struct inclist *file, *file_red; |
203 |
{ |
204 |
register char *line; |
205 |
register int type; |
206 |
|
207 |
while (line = getline(filep)) { |
208 |
switch(type = deftype(line, filep, file_red, file, FALSE)) { |
209 |
case IF: |
210 |
case IFFALSE: |
211 |
case IFGUESSFALSE: |
212 |
case IFDEF: |
213 |
case IFNDEF: |
214 |
type = gobble(filep, file, file_red); |
215 |
while ((type == ELIF) || (type == ELIFFALSE) || |
216 |
(type == ELIFGUESSFALSE)) |
217 |
type = gobble(filep, file, file_red); |
218 |
if (type == ELSE) |
219 |
(void)gobble(filep, file, file_red); |
220 |
break; |
221 |
case ELSE: |
222 |
case ENDIF: |
223 |
debug(0,("%s, line %d: #%s\n", |
224 |
file->i_file, filep->f_line, |
225 |
directives[type])); |
226 |
return(type); |
227 |
case DEFINE: |
228 |
case UNDEF: |
229 |
case INCLUDE: |
230 |
case INCLUDEDOT: |
231 |
case PRAGMA: |
232 |
case ERROR: |
233 |
case IDENT: |
234 |
case SCCS: |
235 |
case EJECT: |
236 |
break; |
237 |
case ELIF: |
238 |
case ELIFFALSE: |
239 |
case ELIFGUESSFALSE: |
240 |
return(type); |
241 |
case -1: |
242 |
warning("%s, line %d: unknown directive == \"%s\"\n", |
243 |
file_red->i_file, filep->f_line, line); |
244 |
break; |
245 |
} |
246 |
} |
247 |
return(-1); |
248 |
} |
249 |
|
250 |
/* |
251 |
* Decide what type of # directive this line is. |
252 |
*/ |
253 |
int deftype (line, filep, file_red, file, parse_it) |
254 |
register char *line; |
255 |
register struct filepointer *filep; |
256 |
register struct inclist *file_red, *file; |
257 |
int parse_it; |
258 |
{ |
259 |
register char *p; |
260 |
char *directive, savechar; |
261 |
register int ret; |
262 |
|
263 |
/* |
264 |
* Parse the directive... |
265 |
*/ |
266 |
directive=line+1; |
267 |
while (*directive == ' ' || *directive == '\t') |
268 |
directive++; |
269 |
|
270 |
p = directive; |
271 |
while (*p >= 'a' && *p <= 'z') |
272 |
p++; |
273 |
savechar = *p; |
274 |
*p = '\0'; |
275 |
ret = match(directive, directives); |
276 |
*p = savechar; |
277 |
|
278 |
/* If we don't recognize this compiler directive or we happen to just |
279 |
* be gobbling up text while waiting for an #endif or #elif or #else |
280 |
* in the case of an #elif we must check the zero_value and return an |
281 |
* ELIF or an ELIFFALSE. |
282 |
*/ |
283 |
|
284 |
if (ret == ELIF && !parse_it) |
285 |
{ |
286 |
while (*p == ' ' || *p == '\t') |
287 |
p++; |
288 |
/* |
289 |
* parse an expression. |
290 |
*/ |
291 |
debug(0,("%s, line %d: #elif %s ", |
292 |
file->i_file, filep->f_line, p)); |
293 |
ret = zero_value(p, filep, file_red); |
294 |
if (ret != IF) |
295 |
{ |
296 |
debug(0,("false...\n")); |
297 |
if (ret == IFFALSE) |
298 |
return(ELIFFALSE); |
299 |
else |
300 |
return(ELIFGUESSFALSE); |
301 |
} |
302 |
else |
303 |
{ |
304 |
debug(0,("true...\n")); |
305 |
return(ELIF); |
306 |
} |
307 |
} |
308 |
|
309 |
if (ret < 0 || ! parse_it) |
310 |
return(ret); |
311 |
|
312 |
/* |
313 |
* now decide how to parse the directive, and do it. |
314 |
*/ |
315 |
while (*p == ' ' || *p == '\t') |
316 |
p++; |
317 |
switch (ret) { |
318 |
case IF: |
319 |
/* |
320 |
* parse an expression. |
321 |
*/ |
322 |
ret = zero_value(p, filep, file_red); |
323 |
debug(0,("%s, line %d: %s #if %s\n", |
324 |
file->i_file, filep->f_line, ret?"false":"true", p)); |
325 |
break; |
326 |
case IFDEF: |
327 |
case IFNDEF: |
328 |
debug(0,("%s, line %d: #%s %s\n", |
329 |
file->i_file, filep->f_line, directives[ret], p)); |
330 |
case UNDEF: |
331 |
/* |
332 |
* separate the name of a single symbol. |
333 |
*/ |
334 |
while (isalnum(*p) || *p == '_') |
335 |
*line++ = *p++; |
336 |
*line = '\0'; |
337 |
break; |
338 |
case INCLUDE: |
339 |
debug(2,("%s, line %d: #include %s\n", |
340 |
file->i_file, filep->f_line, p)); |
341 |
|
342 |
/* Support ANSI macro substitution */ |
343 |
{ |
344 |
struct symtab *sym = isdefined(p, file_red, NULL); |
345 |
while (sym) { |
346 |
p = sym->s_value; |
347 |
debug(3,("%s : #includes SYMBOL %s = %s\n", |
348 |
file->i_incstring, |
349 |
sym -> s_name, |
350 |
sym -> s_value)); |
351 |
/* mark file as having included a 'soft include' */ |
352 |
file->i_included_sym = TRUE; |
353 |
sym = isdefined(p, file_red, NULL); |
354 |
} |
355 |
} |
356 |
|
357 |
/* |
358 |
* Separate the name of the include file. |
359 |
*/ |
360 |
while (*p && *p != '"' && *p != '<') |
361 |
p++; |
362 |
if (! *p) |
363 |
return(-2); |
364 |
if (*p++ == '"') { |
365 |
ret = INCLUDEDOT; |
366 |
while (*p && *p != '"') |
367 |
*line++ = *p++; |
368 |
} else |
369 |
while (*p && *p != '>') |
370 |
*line++ = *p++; |
371 |
*line = '\0'; |
372 |
break; |
373 |
case DEFINE: |
374 |
/* |
375 |
* copy the definition back to the beginning of the line. |
376 |
*/ |
377 |
strcpy (line, p); |
378 |
break; |
379 |
case ELSE: |
380 |
case ENDIF: |
381 |
case ELIF: |
382 |
case PRAGMA: |
383 |
case ERROR: |
384 |
case IDENT: |
385 |
case SCCS: |
386 |
case EJECT: |
387 |
debug(0,("%s, line %d: #%s\n", |
388 |
file->i_file, filep->f_line, directives[ret])); |
389 |
/* |
390 |
* nothing to do. |
391 |
*/ |
392 |
break; |
393 |
} |
394 |
return(ret); |
395 |
} |
396 |
|
397 |
struct symtab *isdefined(symbol, file, srcfile) |
398 |
register char *symbol; |
399 |
struct inclist *file; |
400 |
struct inclist **srcfile; |
401 |
{ |
402 |
register struct symtab *val; |
403 |
|
404 |
if (val = slookup(symbol, &maininclist)) { |
405 |
debug(1,("%s defined on command line\n", symbol)); |
406 |
if (srcfile != NULL) *srcfile = &maininclist; |
407 |
return(val); |
408 |
} |
409 |
if (val = fdefined(symbol, file, srcfile)) |
410 |
return(val); |
411 |
debug(1,("%s not defined in %s\n", symbol, file->i_file)); |
412 |
return(NULL); |
413 |
} |
414 |
|
415 |
struct symtab *fdefined(symbol, file, srcfile) |
416 |
register char *symbol; |
417 |
struct inclist *file; |
418 |
struct inclist **srcfile; |
419 |
{ |
420 |
register struct inclist **ip; |
421 |
register struct symtab *val; |
422 |
register int i; |
423 |
static int recurse_lvl = 0; |
424 |
|
425 |
if (file->i_defchecked) |
426 |
return(NULL); |
427 |
file->i_defchecked = TRUE; |
428 |
if (val = slookup(symbol, file)) |
429 |
debug(1,("%s defined in %s as %s\n", symbol, file->i_file, val->s_value)); |
430 |
if (val == NULL && file->i_list) |
431 |
{ |
432 |
for (ip = file->i_list, i=0; i < file->i_listlen; i++, ip++) |
433 |
if (val = fdefined(symbol, *ip, srcfile)) { |
434 |
break; |
435 |
} |
436 |
} |
437 |
else if (val != NULL && srcfile != NULL) *srcfile = file; |
438 |
recurse_lvl--; |
439 |
file->i_defchecked = FALSE; |
440 |
|
441 |
return(val); |
442 |
} |
443 |
|
444 |
/* |
445 |
* Return type based on if the #if expression evaluates to 0 |
446 |
*/ |
447 |
zero_value(exp, filep, file_red) |
448 |
register char *exp; |
449 |
register struct filepointer *filep; |
450 |
register struct inclist *file_red; |
451 |
{ |
452 |
if (cppsetup(exp, filep, file_red)) |
453 |
return(IFFALSE); |
454 |
else |
455 |
return(IF); |
456 |
} |
457 |
|
458 |
define(def, file) |
459 |
char *def; |
460 |
struct inclist *file; |
461 |
{ |
462 |
char *val; |
463 |
|
464 |
/* Separate symbol name and its value */ |
465 |
val = def; |
466 |
while (isalnum(*val) || *val == '_') |
467 |
val++; |
468 |
if (*val) |
469 |
*val++ = '\0'; |
470 |
while (*val == ' ' || *val == '\t') |
471 |
val++; |
472 |
|
473 |
if (!*val) |
474 |
val = "1"; |
475 |
define2(def, val, file); |
476 |
} |
477 |
|
478 |
define2(name, val, file) |
479 |
char *name, *val; |
480 |
struct inclist *file; |
481 |
{ |
482 |
int first, last, below; |
483 |
register struct symtab *sp = NULL, *dest; |
484 |
|
485 |
/* Make space if it's needed */ |
486 |
if (file->i_defs == NULL) |
487 |
{ |
488 |
file->i_defs = (struct symtab *) |
489 |
malloc(sizeof (struct symtab) * SYMTABINC); |
490 |
file->i_deflen = SYMTABINC; |
491 |
file->i_ndefs = 0; |
492 |
} |
493 |
else if (file->i_ndefs == file->i_deflen) |
494 |
file->i_defs = (struct symtab *) |
495 |
realloc(file->i_defs, |
496 |
sizeof(struct symtab)*(file->i_deflen+=SYMTABINC)); |
497 |
|
498 |
if (file->i_defs == NULL) |
499 |
fatalerr("malloc()/realloc() failure in insert_defn()\n"); |
500 |
|
501 |
below = first = 0; |
502 |
last = file->i_ndefs - 1; |
503 |
while (last >= first) |
504 |
{ |
505 |
/* Fast inline binary search */ |
506 |
register char *s1; |
507 |
register char *s2; |
508 |
register int middle = (first + last) / 2; |
509 |
|
510 |
/* Fast inline strchr() */ |
511 |
s1 = name; |
512 |
s2 = file->i_defs[middle].s_name; |
513 |
while (*s1++ == *s2++) |
514 |
if (s2[-1] == '\0') break; |
515 |
|
516 |
/* If exact match, set sp and break */ |
517 |
if (*--s1 == *--s2) |
518 |
{ |
519 |
sp = file->i_defs + middle; |
520 |
break; |
521 |
} |
522 |
|
523 |
/* If name > i_defs[middle] ... */ |
524 |
if (*s1 > *s2) |
525 |
{ |
526 |
below = first; |
527 |
first = middle + 1; |
528 |
} |
529 |
/* else ... */ |
530 |
else |
531 |
{ |
532 |
below = last = middle - 1; |
533 |
} |
534 |
} |
535 |
|
536 |
/* Search is done. If we found an exact match to the symbol name, |
537 |
just replace its s_value */ |
538 |
if (sp != NULL) |
539 |
{ |
540 |
free(sp->s_value); |
541 |
sp->s_value = copy(val); |
542 |
return; |
543 |
} |
544 |
|
545 |
sp = file->i_defs + file->i_ndefs++; |
546 |
dest = file->i_defs + below + 1; |
547 |
while (sp > dest) |
548 |
{ |
549 |
*sp = sp[-1]; |
550 |
sp--; |
551 |
} |
552 |
sp->s_name = copy(name); |
553 |
sp->s_value = copy(val); |
554 |
} |
555 |
|
556 |
struct symtab *slookup(symbol, file) |
557 |
register char *symbol; |
558 |
register struct inclist *file; |
559 |
{ |
560 |
register int first = 0; |
561 |
register int last = file->i_ndefs - 1; |
562 |
|
563 |
if (file) while (last >= first) |
564 |
{ |
565 |
/* Fast inline binary search */ |
566 |
register char *s1; |
567 |
register char *s2; |
568 |
register int middle = (first + last) / 2; |
569 |
|
570 |
/* Fast inline strchr() */ |
571 |
s1 = symbol; |
572 |
s2 = file->i_defs[middle].s_name; |
573 |
while (*s1++ == *s2++) |
574 |
if (s2[-1] == '\0') break; |
575 |
|
576 |
/* If exact match, we're done */ |
577 |
if (*--s1 == *--s2) |
578 |
{ |
579 |
return file->i_defs + middle; |
580 |
} |
581 |
|
582 |
/* If symbol > i_defs[middle] ... */ |
583 |
if (*s1 > *s2) |
584 |
{ |
585 |
first = middle + 1; |
586 |
} |
587 |
/* else ... */ |
588 |
else |
589 |
{ |
590 |
last = middle - 1; |
591 |
} |
592 |
} |
593 |
return(NULL); |
594 |
} |
595 |
|
596 |
undefine(symbol, file) |
597 |
char *symbol; |
598 |
register struct inclist *file; |
599 |
{ |
600 |
register struct symtab *ptr; |
601 |
struct inclist *srcfile; |
602 |
while ((ptr = isdefined(symbol, file, &srcfile)) != NULL) |
603 |
{ |
604 |
srcfile->i_ndefs--; |
605 |
for (; ptr < srcfile->i_defs + srcfile->i_ndefs; ptr++) |
606 |
*ptr = ptr[1]; |
607 |
} |
608 |
} |