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