Commit | Line | Data |
---|---|---|
15637ed4 RG |
1 | /* |
2 | * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. | |
3 | * Copyright (c) 1988, 1989 by Adam de Boor | |
4 | * Copyright (c) 1989 by Berkeley Softworks | |
5 | * All rights reserved. | |
6 | * | |
7 | * This code is derived from software contributed to Berkeley by | |
8 | * Adam de Boor. | |
9 | * | |
10 | * Redistribution and use in source and binary forms, with or without | |
11 | * modification, are permitted provided that the following conditions | |
12 | * are met: | |
13 | * 1. Redistributions of source code must retain the above copyright | |
14 | * notice, this list of conditions and the following disclaimer. | |
15 | * 2. Redistributions in binary form must reproduce the above copyright | |
16 | * notice, this list of conditions and the following disclaimer in the | |
17 | * documentation and/or other materials provided with the distribution. | |
18 | * 3. All advertising materials mentioning features or use of this software | |
19 | * must display the following acknowledgement: | |
20 | * This product includes software developed by the University of | |
21 | * California, Berkeley and its contributors. | |
22 | * 4. Neither the name of the University nor the names of its contributors | |
23 | * may be used to endorse or promote products derived from this software | |
24 | * without specific prior written permission. | |
25 | * | |
26 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
27 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
28 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
29 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
30 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
31 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
32 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
33 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
34 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
35 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
36 | * SUCH DAMAGE. | |
37 | */ | |
38 | ||
39 | #ifndef lint | |
40 | static char sccsid[] = "@(#)cond.c 5.6 (Berkeley) 6/1/90"; | |
41 | #endif /* not lint */ | |
42 | ||
43 | /*- | |
44 | * cond.c -- | |
45 | * Functions to handle conditionals in a makefile. | |
46 | * | |
47 | * Interface: | |
48 | * Cond_Eval Evaluate the conditional in the passed line. | |
49 | * | |
50 | */ | |
51 | ||
52 | #include "make.h" | |
53 | #include <buf.h> | |
54 | #include <ctype.h> | |
55 | ||
56 | /* | |
57 | * The parsing of conditional expressions is based on this grammar: | |
58 | * E -> F || E | |
59 | * E -> F | |
60 | * F -> T && F | |
61 | * F -> T | |
62 | * T -> defined(variable) | |
63 | * T -> make(target) | |
64 | * T -> exists(file) | |
65 | * T -> empty(varspec) | |
66 | * T -> target(name) | |
67 | * T -> symbol | |
68 | * T -> $(varspec) op value | |
69 | * T -> $(varspec) == "string" | |
70 | * T -> $(varspec) != "string" | |
71 | * T -> ( E ) | |
72 | * T -> ! T | |
73 | * op -> == | != | > | < | >= | <= | |
74 | * | |
75 | * 'symbol' is some other symbol to which the default function (condDefProc) | |
76 | * is applied. | |
77 | * | |
78 | * Tokens are scanned from the 'condExpr' string. The scanner (CondToken) | |
79 | * will return And for '&' and '&&', Or for '|' and '||', Not for '!', | |
80 | * LParen for '(', RParen for ')' and will evaluate the other terminal | |
81 | * symbols, using either the default function or the function given in the | |
82 | * terminal, and return the result as either True or False. | |
83 | * | |
84 | * All Non-Terminal functions (CondE, CondF and CondT) return Err on error. | |
85 | */ | |
86 | typedef enum { | |
87 | And, Or, Not, True, False, LParen, RParen, EndOfFile, None, Err | |
88 | } Token; | |
89 | ||
90 | /*- | |
91 | * Structures to handle elegantly the different forms of #if's. The | |
92 | * last two fields are stored in condInvert and condDefProc, respectively. | |
93 | */ | |
94 | static Boolean CondDoDefined(), | |
95 | CondDoMake(); | |
96 | ||
97 | static struct If { | |
98 | char *form; /* Form of if */ | |
99 | int formlen; /* Length of form */ | |
100 | Boolean doNot; /* TRUE if default function should be negated */ | |
101 | Boolean (*defProc)(); /* Default function to apply */ | |
102 | } ifs[] = { | |
103 | "ifdef", 5, FALSE, CondDoDefined, | |
104 | "ifndef", 6, TRUE, CondDoDefined, | |
105 | "ifmake", 6, FALSE, CondDoMake, | |
106 | "ifnmake", 7, TRUE, CondDoMake, | |
107 | "if", 2, FALSE, CondDoDefined, | |
108 | (char *)0, 0, FALSE, (Boolean (*)())0, | |
109 | }; | |
110 | ||
111 | static Boolean condInvert; /* Invert the default function */ | |
112 | static Boolean (*condDefProc)(); /* Default function to apply */ | |
113 | static char *condExpr; /* The expression to parse */ | |
114 | static Token condPushBack=None; /* Single push-back token used in | |
115 | * parsing */ | |
116 | ||
117 | #define MAXIF 30 /* greatest depth of #if'ing */ | |
118 | ||
119 | static Boolean condStack[MAXIF]; /* Stack of conditionals's values */ | |
120 | static int condTop = MAXIF; /* Top-most conditional */ | |
121 | static int skipIfLevel=0; /* Depth of skipped conditionals */ | |
122 | static Boolean skipLine = FALSE; /* Whether the parse module is skipping | |
123 | * lines */ | |
124 | ||
125 | static Token CondT(), CondF(), CondE(); | |
126 | ||
127 | /*- | |
128 | *----------------------------------------------------------------------- | |
129 | * CondPushBack -- | |
130 | * Push back the most recent token read. We only need one level of | |
131 | * this, so the thing is just stored in 'condPushback'. | |
132 | * | |
133 | * Results: | |
134 | * None. | |
135 | * | |
136 | * Side Effects: | |
137 | * condPushback is overwritten. | |
138 | * | |
139 | *----------------------------------------------------------------------- | |
140 | */ | |
141 | static void | |
142 | CondPushBack (t) | |
143 | Token t; /* Token to push back into the "stream" */ | |
144 | { | |
145 | condPushBack = t; | |
146 | } | |
147 | \f | |
148 | /*- | |
149 | *----------------------------------------------------------------------- | |
150 | * CondGetArg -- | |
151 | * Find the argument of a built-in function. | |
152 | * | |
153 | * Results: | |
154 | * The length of the argument and the address of the argument. | |
155 | * | |
156 | * Side Effects: | |
157 | * The pointer is set to point to the closing parenthesis of the | |
158 | * function call. | |
159 | * | |
160 | *----------------------------------------------------------------------- | |
161 | */ | |
162 | static int | |
163 | CondGetArg (linePtr, argPtr, func, parens) | |
164 | char **linePtr; | |
165 | char **argPtr; | |
166 | char *func; | |
167 | Boolean parens; /* TRUE if arg should be bounded by parens */ | |
168 | { | |
169 | register char *cp; | |
170 | int argLen; | |
171 | register Buffer buf; | |
172 | ||
173 | cp = *linePtr; | |
174 | if (parens) { | |
175 | while (*cp != '(' && *cp != '\0') { | |
176 | cp++; | |
177 | } | |
178 | if (*cp == '(') { | |
179 | cp++; | |
180 | } | |
181 | } | |
182 | ||
183 | if (*cp == '\0') { | |
184 | /* | |
185 | * No arguments whatsoever. Because 'make' and 'defined' aren't really | |
186 | * "reserved words", we don't print a message. I think this is better | |
187 | * than hitting the user with a warning message every time s/he uses | |
188 | * the word 'make' or 'defined' at the beginning of a symbol... | |
189 | */ | |
190 | *argPtr = cp; | |
191 | return (0); | |
192 | } | |
193 | ||
194 | while (*cp == ' ' || *cp == '\t') { | |
195 | cp++; | |
196 | } | |
197 | ||
198 | /* | |
199 | * Create a buffer for the argument and start it out at 16 characters | |
200 | * long. Why 16? Why not? | |
201 | */ | |
202 | buf = Buf_Init(16); | |
203 | ||
204 | while ((index(" \t)&|", *cp) == (char *)NULL) && (*cp != '\0')) { | |
205 | if (*cp == '$') { | |
206 | /* | |
207 | * Parse the variable spec and install it as part of the argument | |
208 | * if it's valid. We tell Var_Parse to complain on an undefined | |
209 | * variable, so we don't do it too. Nor do we return an error, | |
210 | * though perhaps we should... | |
211 | */ | |
212 | char *cp2; | |
213 | int len; | |
214 | Boolean doFree; | |
215 | ||
216 | cp2 = Var_Parse(cp, VAR_CMD, TRUE, &len, &doFree); | |
217 | ||
218 | Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2); | |
219 | if (doFree) { | |
220 | free(cp2); | |
221 | } | |
222 | cp += len; | |
223 | } else { | |
224 | Buf_AddByte(buf, (Byte)*cp); | |
225 | cp++; | |
226 | } | |
227 | } | |
228 | ||
229 | Buf_AddByte(buf, (Byte)'\0'); | |
230 | *argPtr = (char *)Buf_GetAll(buf, &argLen); | |
231 | Buf_Destroy(buf, FALSE); | |
232 | ||
233 | while (*cp == ' ' || *cp == '\t') { | |
234 | cp++; | |
235 | } | |
236 | if (parens && *cp != ')') { | |
237 | Parse_Error (PARSE_WARNING, "Missing closing parenthesis for %s()", | |
238 | func); | |
239 | return (0); | |
240 | } else if (parens) { | |
241 | /* | |
242 | * Advance pointer past close parenthesis. | |
243 | */ | |
244 | cp++; | |
245 | } | |
246 | ||
247 | *linePtr = cp; | |
248 | return (argLen); | |
249 | } | |
250 | \f | |
251 | /*- | |
252 | *----------------------------------------------------------------------- | |
253 | * CondDoDefined -- | |
254 | * Handle the 'defined' function for conditionals. | |
255 | * | |
256 | * Results: | |
257 | * TRUE if the given variable is defined. | |
258 | * | |
259 | * Side Effects: | |
260 | * None. | |
261 | * | |
262 | *----------------------------------------------------------------------- | |
263 | */ | |
264 | static Boolean | |
265 | CondDoDefined (argLen, arg) | |
266 | int argLen; | |
267 | char *arg; | |
268 | { | |
269 | char savec = arg[argLen]; | |
270 | Boolean result; | |
271 | ||
272 | arg[argLen] = '\0'; | |
273 | if (Var_Value (arg, VAR_CMD) != (char *)NULL) { | |
274 | result = TRUE; | |
275 | } else { | |
276 | result = FALSE; | |
277 | } | |
278 | arg[argLen] = savec; | |
279 | return (result); | |
280 | } | |
281 | \f | |
282 | /*- | |
283 | *----------------------------------------------------------------------- | |
284 | * CondStrMatch -- | |
285 | * Front-end for Str_Match so it returns 0 on match and non-zero | |
286 | * on mismatch. Callback function for CondDoMake via Lst_Find | |
287 | * | |
288 | * Results: | |
289 | * 0 if string matches pattern | |
290 | * | |
291 | * Side Effects: | |
292 | * None | |
293 | * | |
294 | *----------------------------------------------------------------------- | |
295 | */ | |
296 | static int | |
297 | CondStrMatch(string, pattern) | |
298 | char *string; | |
299 | char *pattern; | |
300 | { | |
301 | return(!Str_Match(string,pattern)); | |
302 | } | |
303 | \f | |
304 | /*- | |
305 | *----------------------------------------------------------------------- | |
306 | * CondDoMake -- | |
307 | * Handle the 'make' function for conditionals. | |
308 | * | |
309 | * Results: | |
310 | * TRUE if the given target is being made. | |
311 | * | |
312 | * Side Effects: | |
313 | * None. | |
314 | * | |
315 | *----------------------------------------------------------------------- | |
316 | */ | |
317 | static Boolean | |
318 | CondDoMake (argLen, arg) | |
319 | int argLen; | |
320 | char *arg; | |
321 | { | |
322 | char savec = arg[argLen]; | |
323 | Boolean result; | |
324 | ||
325 | arg[argLen] = '\0'; | |
326 | if (Lst_Find (create, (ClientData)arg, CondStrMatch) == NILLNODE) { | |
327 | result = FALSE; | |
328 | } else { | |
329 | result = TRUE; | |
330 | } | |
331 | arg[argLen] = savec; | |
332 | return (result); | |
333 | } | |
334 | \f | |
335 | /*- | |
336 | *----------------------------------------------------------------------- | |
337 | * CondDoExists -- | |
338 | * See if the given file exists. | |
339 | * | |
340 | * Results: | |
341 | * TRUE if the file exists and FALSE if it does not. | |
342 | * | |
343 | * Side Effects: | |
344 | * None. | |
345 | * | |
346 | *----------------------------------------------------------------------- | |
347 | */ | |
348 | static Boolean | |
349 | CondDoExists (argLen, arg) | |
350 | int argLen; | |
351 | char *arg; | |
352 | { | |
353 | char savec = arg[argLen]; | |
354 | Boolean result; | |
355 | char *path; | |
356 | ||
357 | arg[argLen] = '\0'; | |
358 | path = Dir_FindFile(arg, dirSearchPath); | |
359 | if (path != (char *)NULL) { | |
360 | result = TRUE; | |
361 | free(path); | |
362 | } else { | |
363 | result = FALSE; | |
364 | } | |
365 | arg[argLen] = savec; | |
366 | return (result); | |
367 | } | |
368 | \f | |
369 | /*- | |
370 | *----------------------------------------------------------------------- | |
371 | * CondDoTarget -- | |
372 | * See if the given node exists and is an actual target. | |
373 | * | |
374 | * Results: | |
375 | * TRUE if the node exists as a target and FALSE if it does not. | |
376 | * | |
377 | * Side Effects: | |
378 | * None. | |
379 | * | |
380 | *----------------------------------------------------------------------- | |
381 | */ | |
382 | static Boolean | |
383 | CondDoTarget (argLen, arg) | |
384 | int argLen; | |
385 | char *arg; | |
386 | { | |
387 | char savec = arg[argLen]; | |
388 | Boolean result; | |
389 | GNode *gn; | |
390 | ||
391 | arg[argLen] = '\0'; | |
392 | gn = Targ_FindNode(arg, TARG_NOCREATE); | |
393 | if ((gn != NILGNODE) && !OP_NOP(gn->type)) { | |
394 | result = TRUE; | |
395 | } else { | |
396 | result = FALSE; | |
397 | } | |
398 | arg[argLen] = savec; | |
399 | return (result); | |
400 | } | |
401 | ||
402 | \f | |
403 | /*- | |
404 | *----------------------------------------------------------------------- | |
405 | * CondCvtArg -- | |
406 | * Convert the given number into a double. If the number begins | |
407 | * with 0x, or just x, it is interpreted as a hexadecimal integer | |
408 | * and converted to a double from there. All other strings just have | |
409 | * atof called on them. | |
410 | * | |
411 | * Results: | |
412 | * The double value of string. | |
413 | * | |
414 | * Side Effects: | |
415 | * | |
416 | * | |
417 | *----------------------------------------------------------------------- | |
418 | */ | |
419 | static double | |
420 | CondCvtArg(str) | |
421 | register char *str; | |
422 | { | |
423 | int sign = 1; | |
424 | double atof(); | |
425 | ||
426 | if (*str == '-') { | |
427 | sign = -1; | |
428 | str++; | |
429 | } else if (*str == '+') { | |
430 | str++; | |
431 | } | |
432 | if (((*str == '0') && (str[1] == 'x')) || | |
433 | (*str == 'x')) | |
434 | { | |
435 | register int i; | |
436 | ||
437 | str += (*str == 'x') ? 1 : 2; | |
438 | ||
439 | i = 0; | |
440 | ||
441 | while (isxdigit(*str)) { | |
442 | i *= 16; | |
443 | if (*str <= '9') { | |
444 | i += *str - '0'; | |
445 | } else if (*str <= 'F') { | |
446 | i += *str - 'A' + 10; | |
447 | } else { | |
448 | i += *str - 'a' + 10; | |
449 | } | |
450 | str++; | |
451 | } | |
452 | if (sign < 0) { | |
453 | return((double)(-i)); | |
454 | } else { | |
455 | return((double)i); | |
456 | } | |
457 | } else if (sign < 0) { | |
458 | return(- atof(str)); | |
459 | } else { | |
460 | return(atof(str)); | |
461 | } | |
462 | } | |
463 | \f | |
464 | /*- | |
465 | *----------------------------------------------------------------------- | |
466 | * CondToken -- | |
467 | * Return the next token from the input. | |
468 | * | |
469 | * Results: | |
470 | * A Token for the next lexical token in the stream. | |
471 | * | |
472 | * Side Effects: | |
473 | * condPushback will be set back to None if it is used. | |
474 | * | |
475 | *----------------------------------------------------------------------- | |
476 | */ | |
477 | static Token | |
478 | CondToken(doEval) | |
479 | Boolean doEval; | |
480 | { | |
481 | Token t; | |
482 | ||
483 | if (condPushBack == None) { | |
484 | while (*condExpr == ' ' || *condExpr == '\t') { | |
485 | condExpr++; | |
486 | } | |
487 | switch (*condExpr) { | |
488 | case '(': | |
489 | t = LParen; | |
490 | condExpr++; | |
491 | break; | |
492 | case ')': | |
493 | t = RParen; | |
494 | condExpr++; | |
495 | break; | |
496 | case '|': | |
497 | if (condExpr[1] == '|') { | |
498 | condExpr++; | |
499 | } | |
500 | condExpr++; | |
501 | t = Or; | |
502 | break; | |
503 | case '&': | |
504 | if (condExpr[1] == '&') { | |
505 | condExpr++; | |
506 | } | |
507 | condExpr++; | |
508 | t = And; | |
509 | break; | |
510 | case '!': | |
511 | t = Not; | |
512 | condExpr++; | |
513 | break; | |
514 | case '\n': | |
515 | case '\0': | |
516 | t = EndOfFile; | |
517 | break; | |
518 | case '$': { | |
519 | char *lhs; | |
520 | char *rhs; | |
521 | char *op; | |
522 | int varSpecLen; | |
523 | Boolean doFree; | |
524 | ||
525 | /* | |
526 | * Parse the variable spec and skip over it, saving its | |
527 | * value in lhs. | |
528 | */ | |
529 | t = Err; | |
530 | lhs = Var_Parse(condExpr, VAR_CMD, doEval,&varSpecLen,&doFree); | |
531 | if (lhs == var_Error) { | |
532 | /* | |
533 | * Even if !doEval, we still report syntax errors, which | |
534 | * is what getting var_Error back with !doEval means. | |
535 | */ | |
536 | return(Err); | |
537 | } | |
538 | condExpr += varSpecLen; | |
539 | ||
540 | /* | |
541 | * Skip whitespace to get to the operator | |
542 | */ | |
543 | while (isspace(*condExpr)) { | |
544 | condExpr++; | |
545 | } | |
546 | /* | |
547 | * Make sure the operator is a valid one. If it isn't a | |
548 | * known relational operator, pretend we got a | |
549 | * != 0 comparison. | |
550 | */ | |
551 | op = condExpr; | |
552 | switch (*condExpr) { | |
553 | case '!': | |
554 | case '=': | |
555 | case '<': | |
556 | case '>': | |
557 | if (condExpr[1] == '=') { | |
558 | condExpr += 2; | |
559 | } else { | |
560 | condExpr += 1; | |
561 | } | |
562 | break; | |
563 | default: | |
564 | op = "!="; | |
565 | rhs = "0"; | |
566 | ||
567 | goto do_compare; | |
568 | } | |
569 | while (isspace(*condExpr)) { | |
570 | condExpr++; | |
571 | } | |
572 | if (*condExpr == '\0') { | |
573 | Parse_Error(PARSE_WARNING, | |
574 | "Missing right-hand-side of operator"); | |
575 | goto error; | |
576 | } | |
577 | rhs = condExpr; | |
578 | do_compare: | |
579 | if (*rhs == '"') { | |
580 | /* | |
581 | * Doing a string comparison. Only allow == and != for | |
582 | * operators. | |
583 | */ | |
584 | char *string; | |
585 | char *cp, *cp2; | |
586 | Buffer buf; | |
587 | ||
588 | if (((*op != '!') && (*op != '=')) || (op[1] != '=')) { | |
589 | Parse_Error(PARSE_WARNING, | |
590 | "String comparison operator should be either == or !="); | |
591 | goto error; | |
592 | } | |
593 | ||
594 | buf = Buf_Init(0); | |
595 | ||
596 | for (cp = rhs+1; (*cp != '"') && (*cp != '\0'); cp++) { | |
597 | if ((*cp == '\\') && (cp[1] != '\0')) { | |
598 | /* | |
599 | * Backslash escapes things -- skip over next | |
600 | * character, if it exists. | |
601 | */ | |
602 | cp++; | |
603 | Buf_AddByte(buf, (Byte)*cp); | |
604 | } else if (*cp == '$') { | |
605 | int len; | |
606 | Boolean freeIt; | |
607 | ||
608 | cp2 = Var_Parse(cp, VAR_CMD, doEval,&len, &freeIt); | |
609 | if (cp2 != var_Error) { | |
610 | Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2); | |
611 | if (freeIt) { | |
612 | free(cp2); | |
613 | } | |
614 | cp += len - 1; | |
615 | } else { | |
616 | Buf_AddByte(buf, (Byte)*cp); | |
617 | } | |
618 | } else { | |
619 | Buf_AddByte(buf, (Byte)*cp); | |
620 | } | |
621 | } | |
622 | ||
623 | Buf_AddByte(buf, (Byte)0); | |
624 | ||
625 | string = (char *)Buf_GetAll(buf, (int *)0); | |
626 | Buf_Destroy(buf, FALSE); | |
627 | ||
628 | if (DEBUG(COND)) { | |
629 | printf("lhs = \"%s\", rhs = \"%s\", op = %.2s\n", | |
630 | lhs, string, op); | |
631 | } | |
632 | /* | |
633 | * Null-terminate rhs and perform the comparison. | |
634 | * t is set to the result. | |
635 | */ | |
636 | if (*op == '=') { | |
637 | t = strcmp(lhs, string) ? False : True; | |
638 | } else { | |
639 | t = strcmp(lhs, string) ? True : False; | |
640 | } | |
641 | free(string); | |
642 | if (rhs == condExpr) { | |
643 | condExpr = cp + 1; | |
644 | } | |
645 | } else { | |
646 | /* | |
647 | * rhs is either a float or an integer. Convert both the | |
648 | * lhs and the rhs to a double and compare the two. | |
649 | */ | |
650 | double left, right; | |
651 | char *string; | |
652 | ||
653 | left = CondCvtArg(lhs); | |
654 | if (*rhs == '$') { | |
655 | int len; | |
656 | Boolean freeIt; | |
657 | ||
658 | string = Var_Parse(rhs, VAR_CMD, doEval,&len,&freeIt); | |
659 | if (string == var_Error) { | |
660 | right = 0.0; | |
661 | } else { | |
662 | right = CondCvtArg(string); | |
663 | if (freeIt) { | |
664 | free(string); | |
665 | } | |
666 | if (rhs == condExpr) { | |
667 | condExpr += len; | |
668 | } | |
669 | } | |
670 | } else { | |
671 | right = CondCvtArg(rhs); | |
672 | if (rhs == condExpr) { | |
673 | /* | |
674 | * Skip over the right-hand side | |
675 | */ | |
676 | while(!isspace(*condExpr) && (*condExpr != '\0')) { | |
677 | condExpr++; | |
678 | } | |
679 | } | |
680 | } | |
681 | ||
682 | if (DEBUG(COND)) { | |
683 | printf("left = %f, right = %f, op = %.2s\n", left, | |
684 | right, op); | |
685 | } | |
686 | switch(op[0]) { | |
687 | case '!': | |
688 | if (op[1] != '=') { | |
689 | Parse_Error(PARSE_WARNING, | |
690 | "Unknown operator"); | |
691 | goto error; | |
692 | } | |
693 | t = (left != right ? True : False); | |
694 | break; | |
695 | case '=': | |
696 | if (op[1] != '=') { | |
697 | Parse_Error(PARSE_WARNING, | |
698 | "Unknown operator"); | |
699 | goto error; | |
700 | } | |
701 | t = (left == right ? True : False); | |
702 | break; | |
703 | case '<': | |
704 | if (op[1] == '=') { | |
705 | t = (left <= right ? True : False); | |
706 | } else { | |
707 | t = (left < right ? True : False); | |
708 | } | |
709 | break; | |
710 | case '>': | |
711 | if (op[1] == '=') { | |
712 | t = (left >= right ? True : False); | |
713 | } else { | |
714 | t = (left > right ? True : False); | |
715 | } | |
716 | break; | |
717 | } | |
718 | } | |
719 | error: | |
720 | if (doFree) { | |
721 | free(lhs); | |
722 | } | |
723 | break; | |
724 | } | |
725 | default: { | |
726 | Boolean (*evalProc)(); | |
727 | Boolean invert = FALSE; | |
728 | char *arg; | |
729 | int arglen; | |
730 | ||
731 | if (strncmp (condExpr, "defined", 7) == 0) { | |
732 | /* | |
733 | * Use CondDoDefined to evaluate the argument and | |
734 | * CondGetArg to extract the argument from the 'function | |
735 | * call'. | |
736 | */ | |
737 | evalProc = CondDoDefined; | |
738 | condExpr += 7; | |
739 | arglen = CondGetArg (&condExpr, &arg, "defined", TRUE); | |
740 | if (arglen == 0) { | |
741 | condExpr -= 7; | |
742 | goto use_default; | |
743 | } | |
744 | } else if (strncmp (condExpr, "make", 4) == 0) { | |
745 | /* | |
746 | * Use CondDoMake to evaluate the argument and | |
747 | * CondGetArg to extract the argument from the 'function | |
748 | * call'. | |
749 | */ | |
750 | evalProc = CondDoMake; | |
751 | condExpr += 4; | |
752 | arglen = CondGetArg (&condExpr, &arg, "make", TRUE); | |
753 | if (arglen == 0) { | |
754 | condExpr -= 4; | |
755 | goto use_default; | |
756 | } | |
757 | } else if (strncmp (condExpr, "exists", 6) == 0) { | |
758 | /* | |
759 | * Use CondDoExists to evaluate the argument and | |
760 | * CondGetArg to extract the argument from the | |
761 | * 'function call'. | |
762 | */ | |
763 | evalProc = CondDoExists; | |
764 | condExpr += 6; | |
765 | arglen = CondGetArg(&condExpr, &arg, "exists", TRUE); | |
766 | if (arglen == 0) { | |
767 | condExpr -= 6; | |
768 | goto use_default; | |
769 | } | |
770 | } else if (strncmp(condExpr, "empty", 5) == 0) { | |
771 | /* | |
772 | * Use Var_Parse to parse the spec in parens and return | |
773 | * True if the resulting string is empty. | |
774 | */ | |
775 | int length; | |
776 | Boolean doFree; | |
777 | char *val; | |
778 | ||
779 | condExpr += 5; | |
780 | ||
781 | for (arglen = 0; | |
782 | condExpr[arglen] != '(' && condExpr[arglen] != '\0'; | |
783 | arglen += 1) | |
784 | { | |
785 | /* void */ ; | |
786 | } | |
787 | if (condExpr[arglen] != '\0') { | |
788 | val = Var_Parse(&condExpr[arglen - 1], VAR_CMD, | |
789 | doEval, &length, &doFree); | |
790 | if (val == var_Error) { | |
791 | t = Err; | |
792 | } else { | |
793 | t = (*val == '\0') ? True : False; | |
794 | } | |
795 | if (doFree) { | |
796 | free(val); | |
797 | } | |
798 | /* | |
799 | * Advance condExpr to beyond the closing ). Note that | |
800 | * we subtract one from arglen + length b/c length | |
801 | * is calculated from condExpr[arglen - 1]. | |
802 | */ | |
803 | condExpr += arglen + length - 1; | |
804 | } else { | |
805 | condExpr -= 5; | |
806 | goto use_default; | |
807 | } | |
808 | break; | |
809 | } else if (strncmp (condExpr, "target", 6) == 0) { | |
810 | /* | |
811 | * Use CondDoTarget to evaluate the argument and | |
812 | * CondGetArg to extract the argument from the | |
813 | * 'function call'. | |
814 | */ | |
815 | evalProc = CondDoTarget; | |
816 | condExpr += 6; | |
817 | arglen = CondGetArg(&condExpr, &arg, "target", TRUE); | |
818 | if (arglen == 0) { | |
819 | condExpr -= 6; | |
820 | goto use_default; | |
821 | } | |
822 | } else { | |
823 | /* | |
824 | * The symbol is itself the argument to the default | |
825 | * function. We advance condExpr to the end of the symbol | |
826 | * by hand (the next whitespace, closing paren or | |
827 | * binary operator) and set to invert the evaluation | |
828 | * function if condInvert is TRUE. | |
829 | */ | |
830 | use_default: | |
831 | invert = condInvert; | |
832 | evalProc = condDefProc; | |
833 | arglen = CondGetArg(&condExpr, &arg, "", FALSE); | |
834 | } | |
835 | ||
836 | /* | |
837 | * Evaluate the argument using the set function. If invert | |
838 | * is TRUE, we invert the sense of the function. | |
839 | */ | |
840 | t = (!doEval || (* evalProc) (arglen, arg) ? | |
841 | (invert ? False : True) : | |
842 | (invert ? True : False)); | |
843 | free(arg); | |
844 | break; | |
845 | } | |
846 | } | |
847 | } else { | |
848 | t = condPushBack; | |
849 | condPushBack = None; | |
850 | } | |
851 | return (t); | |
852 | } | |
853 | \f | |
854 | /*- | |
855 | *----------------------------------------------------------------------- | |
856 | * CondT -- | |
857 | * Parse a single term in the expression. This consists of a terminal | |
858 | * symbol or Not and a terminal symbol (not including the binary | |
859 | * operators): | |
860 | * T -> defined(variable) | make(target) | exists(file) | symbol | |
861 | * T -> ! T | ( E ) | |
862 | * | |
863 | * Results: | |
864 | * True, False or Err. | |
865 | * | |
866 | * Side Effects: | |
867 | * Tokens are consumed. | |
868 | * | |
869 | *----------------------------------------------------------------------- | |
870 | */ | |
871 | static Token | |
872 | CondT(doEval) | |
873 | Boolean doEval; | |
874 | { | |
875 | Token t; | |
876 | ||
877 | t = CondToken(doEval); | |
878 | ||
879 | if (t == EndOfFile) { | |
880 | /* | |
881 | * If we reached the end of the expression, the expression | |
882 | * is malformed... | |
883 | */ | |
884 | t = Err; | |
885 | } else if (t == LParen) { | |
886 | /* | |
887 | * T -> ( E ) | |
888 | */ | |
889 | t = CondE(doEval); | |
890 | if (t != Err) { | |
891 | if (CondToken(doEval) != RParen) { | |
892 | t = Err; | |
893 | } | |
894 | } | |
895 | } else if (t == Not) { | |
896 | t = CondT(doEval); | |
897 | if (t == True) { | |
898 | t = False; | |
899 | } else if (t == False) { | |
900 | t = True; | |
901 | } | |
902 | } | |
903 | return (t); | |
904 | } | |
905 | \f | |
906 | /*- | |
907 | *----------------------------------------------------------------------- | |
908 | * CondF -- | |
909 | * Parse a conjunctive factor (nice name, wot?) | |
910 | * F -> T && F | T | |
911 | * | |
912 | * Results: | |
913 | * True, False or Err | |
914 | * | |
915 | * Side Effects: | |
916 | * Tokens are consumed. | |
917 | * | |
918 | *----------------------------------------------------------------------- | |
919 | */ | |
920 | static Token | |
921 | CondF(doEval) | |
922 | Boolean doEval; | |
923 | { | |
924 | Token l, o; | |
925 | ||
926 | l = CondT(doEval); | |
927 | if (l != Err) { | |
928 | o = CondToken(doEval); | |
929 | ||
930 | if (o == And) { | |
931 | /* | |
932 | * F -> T && F | |
933 | * | |
934 | * If T is False, the whole thing will be False, but we have to | |
935 | * parse the r.h.s. anyway (to throw it away). | |
936 | * If T is True, the result is the r.h.s., be it an Err or no. | |
937 | */ | |
938 | if (l == True) { | |
939 | l = CondF(doEval); | |
940 | } else { | |
941 | (void) CondF(FALSE); | |
942 | } | |
943 | } else { | |
944 | /* | |
945 | * F -> T | |
946 | */ | |
947 | CondPushBack (o); | |
948 | } | |
949 | } | |
950 | return (l); | |
951 | } | |
952 | \f | |
953 | /*- | |
954 | *----------------------------------------------------------------------- | |
955 | * CondE -- | |
956 | * Main expression production. | |
957 | * E -> F || E | F | |
958 | * | |
959 | * Results: | |
960 | * True, False or Err. | |
961 | * | |
962 | * Side Effects: | |
963 | * Tokens are, of course, consumed. | |
964 | * | |
965 | *----------------------------------------------------------------------- | |
966 | */ | |
967 | static Token | |
968 | CondE(doEval) | |
969 | Boolean doEval; | |
970 | { | |
971 | Token l, o; | |
972 | ||
973 | l = CondF(doEval); | |
974 | if (l != Err) { | |
975 | o = CondToken(doEval); | |
976 | ||
977 | if (o == Or) { | |
978 | /* | |
979 | * E -> F || E | |
980 | * | |
981 | * A similar thing occurs for ||, except that here we make sure | |
982 | * the l.h.s. is False before we bother to evaluate the r.h.s. | |
983 | * Once again, if l is False, the result is the r.h.s. and once | |
984 | * again if l is True, we parse the r.h.s. to throw it away. | |
985 | */ | |
986 | if (l == False) { | |
987 | l = CondE(doEval); | |
988 | } else { | |
989 | (void) CondE(FALSE); | |
990 | } | |
991 | } else { | |
992 | /* | |
993 | * E -> F | |
994 | */ | |
995 | CondPushBack (o); | |
996 | } | |
997 | } | |
998 | return (l); | |
999 | } | |
1000 | \f | |
1001 | /*- | |
1002 | *----------------------------------------------------------------------- | |
1003 | * Cond_Eval -- | |
1004 | * Evaluate the conditional in the passed line. The line | |
1005 | * looks like this: | |
1006 | * #<cond-type> <expr> | |
1007 | * where <cond-type> is any of if, ifmake, ifnmake, ifdef, | |
1008 | * ifndef, elif, elifmake, elifnmake, elifdef, elifndef | |
1009 | * and <expr> consists of &&, ||, !, make(target), defined(variable) | |
1010 | * and parenthetical groupings thereof. | |
1011 | * | |
1012 | * Results: | |
1013 | * COND_PARSE if should parse lines after the conditional | |
1014 | * COND_SKIP if should skip lines after the conditional | |
1015 | * COND_INVALID if not a valid conditional. | |
1016 | * | |
1017 | * Side Effects: | |
1018 | * None. | |
1019 | * | |
1020 | *----------------------------------------------------------------------- | |
1021 | */ | |
1022 | Cond_Eval (line) | |
1023 | char *line; /* Line to parse */ | |
1024 | { | |
1025 | struct If *ifp; | |
1026 | Boolean isElse; | |
1027 | Boolean value; | |
1028 | int level; /* Level at which to report errors. */ | |
1029 | ||
1030 | level = PARSE_FATAL; | |
1031 | ||
1032 | for (line++; *line == ' ' || *line == '\t'; line++) { | |
1033 | continue; | |
1034 | } | |
1035 | ||
1036 | /* | |
1037 | * Find what type of if we're dealing with. The result is left | |
1038 | * in ifp and isElse is set TRUE if it's an elif line. | |
1039 | */ | |
1040 | if (line[0] == 'e' && line[1] == 'l') { | |
1041 | line += 2; | |
1042 | isElse = TRUE; | |
1043 | } else if (strncmp (line, "endif", 5) == 0) { | |
1044 | /* | |
1045 | * End of a conditional section. If skipIfLevel is non-zero, that | |
1046 | * conditional was skipped, so lines following it should also be | |
1047 | * skipped. Hence, we return COND_SKIP. Otherwise, the conditional | |
1048 | * was read so succeeding lines should be parsed (think about it...) | |
1049 | * so we return COND_PARSE, unless this endif isn't paired with | |
1050 | * a decent if. | |
1051 | */ | |
1052 | if (skipIfLevel != 0) { | |
1053 | skipIfLevel -= 1; | |
1054 | return (COND_SKIP); | |
1055 | } else { | |
1056 | if (condTop == MAXIF) { | |
1057 | Parse_Error (level, "if-less endif"); | |
1058 | return (COND_INVALID); | |
1059 | } else { | |
1060 | skipLine = FALSE; | |
1061 | condTop += 1; | |
1062 | return (COND_PARSE); | |
1063 | } | |
1064 | } | |
1065 | } else { | |
1066 | isElse = FALSE; | |
1067 | } | |
1068 | ||
1069 | /* | |
1070 | * Figure out what sort of conditional it is -- what its default | |
1071 | * function is, etc. -- by looking in the table of valid "ifs" | |
1072 | */ | |
1073 | for (ifp = ifs; ifp->form != (char *)0; ifp++) { | |
1074 | if (strncmp (ifp->form, line, ifp->formlen) == 0) { | |
1075 | break; | |
1076 | } | |
1077 | } | |
1078 | ||
1079 | if (ifp->form == (char *) 0) { | |
1080 | /* | |
1081 | * Nothing fit. If the first word on the line is actually | |
1082 | * "else", it's a valid conditional whose value is the inverse | |
1083 | * of the previous if we parsed. | |
1084 | */ | |
1085 | if (isElse && (line[0] == 's') && (line[1] == 'e')) { | |
1086 | if (condTop == MAXIF) { | |
1087 | Parse_Error (level, "if-less else"); | |
1088 | return (COND_INVALID); | |
1089 | } else if (skipIfLevel == 0) { | |
1090 | value = !condStack[condTop]; | |
1091 | } else { | |
1092 | return (COND_SKIP); | |
1093 | } | |
1094 | } else { | |
1095 | /* | |
1096 | * Not a valid conditional type. No error... | |
1097 | */ | |
1098 | return (COND_INVALID); | |
1099 | } | |
1100 | } else { | |
1101 | if (isElse) { | |
1102 | if (condTop == MAXIF) { | |
1103 | Parse_Error (level, "if-less elif"); | |
1104 | return (COND_INVALID); | |
1105 | } else if (skipIfLevel != 0) { | |
1106 | /* | |
1107 | * If skipping this conditional, just ignore the whole thing. | |
1108 | * If we don't, the user might be employing a variable that's | |
1109 | * undefined, for which there's an enclosing ifdef that | |
1110 | * we're skipping... | |
1111 | */ | |
1112 | return(COND_SKIP); | |
1113 | } | |
1114 | } else if (skipLine) { | |
1115 | /* | |
1116 | * Don't even try to evaluate a conditional that's not an else if | |
1117 | * we're skipping things... | |
1118 | */ | |
1119 | skipIfLevel += 1; | |
1120 | return(COND_SKIP); | |
1121 | } | |
1122 | ||
1123 | /* | |
1124 | * Initialize file-global variables for parsing | |
1125 | */ | |
1126 | condDefProc = ifp->defProc; | |
1127 | condInvert = ifp->doNot; | |
1128 | ||
1129 | line += ifp->formlen; | |
1130 | ||
1131 | while (*line == ' ' || *line == '\t') { | |
1132 | line++; | |
1133 | } | |
1134 | ||
1135 | condExpr = line; | |
1136 | condPushBack = None; | |
1137 | ||
1138 | switch (CondE(TRUE)) { | |
1139 | case True: | |
1140 | if (CondToken(TRUE) == EndOfFile) { | |
1141 | value = TRUE; | |
1142 | break; | |
1143 | } | |
1144 | goto err; | |
1145 | /*FALLTHRU*/ | |
1146 | case False: | |
1147 | if (CondToken(TRUE) == EndOfFile) { | |
1148 | value = FALSE; | |
1149 | break; | |
1150 | } | |
1151 | /*FALLTHRU*/ | |
1152 | case Err: | |
1153 | err: | |
1154 | Parse_Error (level, "Malformed conditional (%s)", | |
1155 | line); | |
1156 | return (COND_INVALID); | |
1157 | } | |
1158 | } | |
1159 | if (!isElse) { | |
1160 | condTop -= 1; | |
1161 | } else if ((skipIfLevel != 0) || condStack[condTop]) { | |
1162 | /* | |
1163 | * If this is an else-type conditional, it should only take effect | |
1164 | * if its corresponding if was evaluated and FALSE. If its if was | |
1165 | * TRUE or skipped, we return COND_SKIP (and start skipping in case | |
1166 | * we weren't already), leaving the stack unmolested so later elif's | |
1167 | * don't screw up... | |
1168 | */ | |
1169 | skipLine = TRUE; | |
1170 | return (COND_SKIP); | |
1171 | } | |
1172 | ||
1173 | if (condTop < 0) { | |
1174 | /* | |
1175 | * This is the one case where we can definitely proclaim a fatal | |
1176 | * error. If we don't, we're hosed. | |
1177 | */ | |
1178 | Parse_Error (PARSE_FATAL, "Too many nested if's. %d max.", MAXIF); | |
1179 | return (COND_INVALID); | |
1180 | } else { | |
1181 | condStack[condTop] = value; | |
1182 | skipLine = !value; | |
1183 | return (value ? COND_PARSE : COND_SKIP); | |
1184 | } | |
1185 | } | |
1186 | \f | |
1187 | /*- | |
1188 | *----------------------------------------------------------------------- | |
1189 | * Cond_End -- | |
1190 | * Make sure everything's clean at the end of a makefile. | |
1191 | * | |
1192 | * Results: | |
1193 | * None. | |
1194 | * | |
1195 | * Side Effects: | |
1196 | * Parse_Error will be called if open conditionals are around. | |
1197 | * | |
1198 | *----------------------------------------------------------------------- | |
1199 | */ | |
1200 | void | |
1201 | Cond_End() | |
1202 | { | |
1203 | if (condTop != MAXIF) { | |
1204 | Parse_Error(PARSE_FATAL, "%d open conditional%s", MAXIF-condTop, | |
1205 | MAXIF-condTop == 1 ? "" : "s"); | |
1206 | } | |
1207 | condTop = MAXIF; | |
1208 | } |