cond.c revision 1.113 1 /* $NetBSD: cond.c,v 1.113 2020/09/08 14:51:43 rillig Exp $ */
2
3 /*
4 * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
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. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35 /*
36 * Copyright (c) 1988, 1989 by Adam de Boor
37 * Copyright (c) 1989 by Berkeley Softworks
38 * All rights reserved.
39 *
40 * This code is derived from software contributed to Berkeley by
41 * Adam de Boor.
42 *
43 * Redistribution and use in source and binary forms, with or without
44 * modification, are permitted provided that the following conditions
45 * are met:
46 * 1. Redistributions of source code must retain the above copyright
47 * notice, this list of conditions and the following disclaimer.
48 * 2. Redistributions in binary form must reproduce the above copyright
49 * notice, this list of conditions and the following disclaimer in the
50 * documentation and/or other materials provided with the distribution.
51 * 3. All advertising materials mentioning features or use of this software
52 * must display the following acknowledgement:
53 * This product includes software developed by the University of
54 * California, Berkeley and its contributors.
55 * 4. Neither the name of the University nor the names of its contributors
56 * may be used to endorse or promote products derived from this software
57 * without specific prior written permission.
58 *
59 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
60 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
61 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
62 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
63 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
64 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
65 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
66 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
67 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
68 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
69 * SUCH DAMAGE.
70 */
71
72 #ifndef MAKE_NATIVE
73 static char rcsid[] = "$NetBSD: cond.c,v 1.113 2020/09/08 14:51:43 rillig Exp $";
74 #else
75 #include <sys/cdefs.h>
76 #ifndef lint
77 #if 0
78 static char sccsid[] = "@(#)cond.c 8.2 (Berkeley) 1/2/94";
79 #else
80 __RCSID("$NetBSD: cond.c,v 1.113 2020/09/08 14:51:43 rillig Exp $");
81 #endif
82 #endif /* not lint */
83 #endif
84
85 /*-
86 * cond.c --
87 * Functions to handle conditionals in a makefile.
88 *
89 * Interface:
90 * Cond_Eval Evaluate the conditional in the passed line.
91 *
92 */
93
94 #include <errno.h>
95
96 #include "make.h"
97 #include "dir.h"
98
99 /*
100 * The parsing of conditional expressions is based on this grammar:
101 * E -> F || E
102 * E -> F
103 * F -> T && F
104 * F -> T
105 * T -> defined(variable)
106 * T -> make(target)
107 * T -> exists(file)
108 * T -> empty(varspec)
109 * T -> target(name)
110 * T -> commands(name)
111 * T -> symbol
112 * T -> $(varspec) op value
113 * T -> $(varspec) == "string"
114 * T -> $(varspec) != "string"
115 * T -> "string"
116 * T -> ( E )
117 * T -> ! T
118 * op -> == | != | > | < | >= | <=
119 *
120 * 'symbol' is some other symbol to which the default function is applied.
121 *
122 * Tokens are scanned from the 'condExpr' string. The scanner (CondToken)
123 * will return TOK_AND for '&' and '&&', TOK_OR for '|' and '||',
124 * TOK_NOT for '!', TOK_LPAREN for '(', TOK_RPAREN for ')' and will evaluate
125 * the other terminal symbols, using either the default function or the
126 * function given in the terminal, and return the result as either TOK_TRUE
127 * or TOK_FALSE.
128 *
129 * TOK_FALSE is 0 and TOK_TRUE 1 so we can directly assign C comparisons.
130 *
131 * All Non-Terminal functions (CondE, CondF and CondT) return TOK_ERROR on
132 * error.
133 */
134 typedef enum {
135 TOK_FALSE = 0, TOK_TRUE = 1, TOK_AND, TOK_OR, TOK_NOT,
136 TOK_LPAREN, TOK_RPAREN, TOK_EOF, TOK_NONE, TOK_ERROR
137 } Token;
138
139 static Token CondE(Boolean);
140 static CondEvalResult do_Cond_EvalExpression(Boolean *);
141
142 static const struct If *if_info; /* Info for current statement */
143 static const char *condExpr; /* The expression to parse */
144 static Token condPushBack = TOK_NONE; /* Single push-back token used in
145 * parsing */
146
147 static unsigned int cond_depth = 0; /* current .if nesting level */
148 static unsigned int cond_min_depth = 0; /* depth at makefile open */
149
150 /*
151 * Indicate when we should be strict about lhs of comparisons.
152 * TRUE when Cond_EvalExpression is called from Cond_Eval (.if etc)
153 * FALSE when Cond_EvalExpression is called from var.c:ApplyModifiers
154 * since lhs is already expanded and we cannot tell if
155 * it was a variable reference or not.
156 */
157 static Boolean lhsStrict;
158
159 static int
160 istoken(const char *str, const char *tok, size_t len)
161 {
162 return strncmp(str, tok, len) == 0 && !isalpha((unsigned char)str[len]);
163 }
164
165 /* Push back the most recent token read. We only need one level of
166 * this, so the thing is just stored in 'condPushback'. */
167 static void
168 CondPushBack(Token t)
169 {
170 condPushBack = t;
171 }
172
173 /* Parse the argument of a built-in function.
174 *
175 * Arguments:
176 * *linePtr initially points to the '(', upon successful return points
177 * beyond the ')'.
178 *
179 * *out_arg receives the argument as string.
180 *
181 * func says whether the argument belongs to an actual function, or
182 * whether the parsed argument is passed to the default function.
183 *
184 * XXX: This is ambiguous for the empty() function since its argument is
185 * parsed differently.
186 *
187 * Return the length of the argument. */
188 static int
189 ParseFuncArg(Boolean doEval, const char **linePtr, char **out_arg,
190 const char *func) {
191 const char *cp;
192 Buffer buf;
193 int paren_depth;
194 char ch;
195 size_t argLen;
196
197 cp = *linePtr;
198 if (func != NULL)
199 /* Skip opening '(' - verified by caller */
200 cp++;
201
202 if (*cp == '\0') {
203 /*
204 * No arguments whatsoever. Because 'make' and 'defined' aren't really
205 * "reserved words", we don't print a message. I think this is better
206 * than hitting the user with a warning message every time s/he uses
207 * the word 'make' or 'defined' at the beginning of a symbol...
208 */
209 *out_arg = NULL;
210 return 0;
211 }
212
213 while (*cp == ' ' || *cp == '\t') {
214 cp++;
215 }
216
217 /*
218 * Create a buffer for the argument and start it out at 16 characters
219 * long. Why 16? Why not?
220 */
221 Buf_Init(&buf, 16);
222
223 paren_depth = 0;
224 for (;;) {
225 ch = *cp;
226 if (ch == 0 || ch == ' ' || ch == '\t')
227 break;
228 if ((ch == '&' || ch == '|') && paren_depth == 0)
229 break;
230 if (*cp == '$') {
231 /*
232 * Parse the variable spec and install it as part of the argument
233 * if it's valid. We tell Var_Parse to complain on an undefined
234 * variable, so we don't do it too. Nor do we return an error,
235 * though perhaps we should...
236 */
237 void *freeIt;
238 VarEvalFlags eflags = VARE_UNDEFERR | (doEval ? VARE_WANTRES : 0);
239 const char *cp2 = Var_ParsePP(&cp, VAR_CMD, eflags, &freeIt);
240 Buf_AddStr(&buf, cp2);
241 free(freeIt);
242 continue;
243 }
244 if (ch == '(')
245 paren_depth++;
246 else if (ch == ')' && --paren_depth < 0)
247 break;
248 Buf_AddByte(&buf, *cp);
249 cp++;
250 }
251
252 *out_arg = Buf_GetAll(&buf, &argLen);
253 Buf_Destroy(&buf, FALSE);
254
255 while (*cp == ' ' || *cp == '\t') {
256 cp++;
257 }
258
259 if (func != NULL && *cp++ != ')') {
260 Parse_Error(PARSE_WARNING, "Missing closing parenthesis for %s()",
261 func);
262 return 0;
263 }
264
265 *linePtr = cp;
266 return argLen;
267 }
268
269 /* Test whether the given variable is defined. */
270 static Boolean
271 CondDoDefined(int argLen MAKE_ATTR_UNUSED, const char *arg)
272 {
273 char *freeIt;
274 Boolean result = Var_Value(arg, VAR_CMD, &freeIt) != NULL;
275 bmake_free(freeIt);
276 return result;
277 }
278
279 /* Wrapper around Str_Match, to be used by Lst_Find. */
280 static Boolean
281 CondFindStrMatch(const void *string, const void *pattern)
282 {
283 return Str_Match(string, pattern);
284 }
285
286 /* See if the given target is being made. */
287 static Boolean
288 CondDoMake(int argLen MAKE_ATTR_UNUSED, const char *arg)
289 {
290 return Lst_Find(create, CondFindStrMatch, arg) != NULL;
291 }
292
293 /* See if the given file exists. */
294 static Boolean
295 CondDoExists(int argLen MAKE_ATTR_UNUSED, const char *arg)
296 {
297 Boolean result;
298 char *path;
299
300 path = Dir_FindFile(arg, dirSearchPath);
301 if (DEBUG(COND)) {
302 fprintf(debug_file, "exists(%s) result is \"%s\"\n",
303 arg, path ? path : "");
304 }
305 if (path != NULL) {
306 result = TRUE;
307 free(path);
308 } else {
309 result = FALSE;
310 }
311 return result;
312 }
313
314 /* See if the given node exists and is an actual target. */
315 static Boolean
316 CondDoTarget(int argLen MAKE_ATTR_UNUSED, const char *arg)
317 {
318 GNode *gn;
319
320 gn = Targ_FindNode(arg, TARG_NOCREATE);
321 return gn != NULL && !OP_NOP(gn->type);
322 }
323
324 /* See if the given node exists and is an actual target with commands
325 * associated with it. */
326 static Boolean
327 CondDoCommands(int argLen MAKE_ATTR_UNUSED, const char *arg)
328 {
329 GNode *gn;
330
331 gn = Targ_FindNode(arg, TARG_NOCREATE);
332 return gn != NULL && !OP_NOP(gn->type) && !Lst_IsEmpty(gn->commands);
333 }
334
335 /*-
336 * Convert the given number into a double.
337 * We try a base 10 or 16 integer conversion first, if that fails
338 * then we try a floating point conversion instead.
339 *
340 * Results:
341 * Sets 'value' to double value of string.
342 * Returns TRUE if the conversion succeeded.
343 */
344 static Boolean
345 CondCvtArg(const char *str, double *value)
346 {
347 char *eptr, ech;
348 unsigned long l_val;
349 double d_val;
350
351 errno = 0;
352 if (!*str) {
353 *value = (double)0;
354 return TRUE;
355 }
356 l_val = strtoul(str, &eptr, str[1] == 'x' ? 16 : 10);
357 ech = *eptr;
358 if (ech == 0 && errno != ERANGE) {
359 d_val = str[0] == '-' ? -(double)-l_val : (double)l_val;
360 } else {
361 if (ech != 0 && ech != '.' && ech != 'e' && ech != 'E')
362 return FALSE;
363 d_val = strtod(str, &eptr);
364 if (*eptr)
365 return FALSE;
366 }
367
368 *value = d_val;
369 return TRUE;
370 }
371
372 /*-
373 * Get a string from a variable reference or an optionally quoted
374 * string. This is called for the lhs and rhs of string compares.
375 *
376 * Results:
377 * Returns the string, absent any quotes, or NULL on error.
378 * Sets quoted if the string was quoted.
379 * Sets freeIt if needed.
380 *
381 * Side Effects:
382 * Moves condExpr past the end of this token.
383 */
384 /* coverity:[+alloc : arg-*2] */
385 static const char *
386 CondGetString(Boolean doEval, Boolean *quoted, void **freeIt, Boolean strictLHS)
387 {
388 Buffer buf;
389 const char *str;
390 int len;
391 Boolean qt;
392 const char *start;
393 VarEvalFlags eflags;
394
395 Buf_Init(&buf, 0);
396 str = NULL;
397 *freeIt = NULL;
398 *quoted = qt = *condExpr == '"' ? 1 : 0;
399 if (qt)
400 condExpr++;
401 for (start = condExpr; *condExpr && str == NULL; condExpr++) {
402 switch (*condExpr) {
403 case '\\':
404 if (condExpr[1] != '\0') {
405 condExpr++;
406 Buf_AddByte(&buf, *condExpr);
407 }
408 break;
409 case '"':
410 if (qt) {
411 condExpr++; /* we don't want the quotes */
412 goto got_str;
413 } else
414 Buf_AddByte(&buf, *condExpr); /* likely? */
415 break;
416 case ')':
417 case '!':
418 case '=':
419 case '>':
420 case '<':
421 case ' ':
422 case '\t':
423 if (!qt)
424 goto got_str;
425 else
426 Buf_AddByte(&buf, *condExpr);
427 break;
428 case '$':
429 /* if we are in quotes, then an undefined variable is ok */
430 eflags = ((!qt && doEval) ? VARE_UNDEFERR : 0) |
431 (doEval ? VARE_WANTRES : 0);
432 str = Var_Parse(condExpr, VAR_CMD, eflags, &len, freeIt);
433 if (str == var_Error) {
434 if (*freeIt) {
435 free(*freeIt);
436 *freeIt = NULL;
437 }
438 /*
439 * Even if !doEval, we still report syntax errors, which
440 * is what getting var_Error back with !doEval means.
441 */
442 str = NULL;
443 goto cleanup;
444 }
445 condExpr += len;
446 /*
447 * If the '$' was first char (no quotes), and we are
448 * followed by space, the operator or end of expression,
449 * we are done.
450 */
451 if ((condExpr == start + len) &&
452 (*condExpr == '\0' ||
453 isspace((unsigned char)*condExpr) ||
454 strchr("!=><)", *condExpr))) {
455 goto cleanup;
456 }
457
458 Buf_AddStr(&buf, str);
459 if (*freeIt) {
460 free(*freeIt);
461 *freeIt = NULL;
462 }
463 str = NULL; /* not finished yet */
464 condExpr--; /* don't skip over next char */
465 break;
466 default:
467 if (strictLHS && !qt && *start != '$' &&
468 !isdigit((unsigned char)*start)) {
469 /* lhs must be quoted, a variable reference or number */
470 if (*freeIt) {
471 free(*freeIt);
472 *freeIt = NULL;
473 }
474 str = NULL;
475 goto cleanup;
476 }
477 Buf_AddByte(&buf, *condExpr);
478 break;
479 }
480 }
481 got_str:
482 *freeIt = Buf_GetAll(&buf, NULL);
483 str = *freeIt;
484 cleanup:
485 Buf_Destroy(&buf, FALSE);
486 return str;
487 }
488
489 /* The different forms of #if's. */
490 static const struct If {
491 const char *form; /* Form of if */
492 size_t formlen; /* Length of form */
493 Boolean doNot; /* TRUE if default function should be negated */
494 Boolean (*defProc)(int, const char *); /* Default function to apply */
495 } ifs[] = {
496 { "def", 3, FALSE, CondDoDefined },
497 { "ndef", 4, TRUE, CondDoDefined },
498 { "make", 4, FALSE, CondDoMake },
499 { "nmake", 5, TRUE, CondDoMake },
500 { "", 0, FALSE, CondDoDefined },
501 { NULL, 0, FALSE, NULL }
502 };
503
504 /*-
505 * Return the next token from the input.
506 *
507 * Side Effects:
508 * condPushback will be set back to TOK_NONE if it is used.
509 */
510 static Token
511 compare_expression(Boolean doEval)
512 {
513 Token t;
514 const char *lhs;
515 const char *rhs;
516 const char *op;
517 void *lhsFree;
518 void *rhsFree;
519 Boolean lhsQuoted;
520 Boolean rhsQuoted;
521 double left, right;
522
523 t = TOK_ERROR;
524 rhs = NULL;
525 lhsFree = rhsFree = NULL;
526 lhsQuoted = rhsQuoted = FALSE;
527
528 /*
529 * Parse the variable spec and skip over it, saving its
530 * value in lhs.
531 */
532 lhs = CondGetString(doEval, &lhsQuoted, &lhsFree, lhsStrict);
533 if (!lhs)
534 goto done;
535
536 /*
537 * Skip whitespace to get to the operator
538 */
539 while (isspace((unsigned char)*condExpr))
540 condExpr++;
541
542 /*
543 * Make sure the operator is a valid one. If it isn't a
544 * known relational operator, pretend we got a
545 * != 0 comparison.
546 */
547 op = condExpr;
548 switch (*condExpr) {
549 case '!':
550 case '=':
551 case '<':
552 case '>':
553 if (condExpr[1] == '=') {
554 condExpr += 2;
555 } else {
556 condExpr += 1;
557 }
558 break;
559 default:
560 if (!doEval) {
561 t = TOK_FALSE;
562 goto done;
563 }
564 /* For .ifxxx "..." check for non-empty string. */
565 if (lhsQuoted) {
566 t = lhs[0] != 0;
567 goto done;
568 }
569 /* For .ifxxx <number> compare against zero */
570 if (CondCvtArg(lhs, &left)) {
571 t = left != 0.0;
572 goto done;
573 }
574 /* For .if ${...} check for non-empty string (defProc is ifdef). */
575 if (if_info->form[0] == 0) {
576 t = lhs[0] != 0;
577 goto done;
578 }
579 /* Otherwise action default test ... */
580 t = if_info->defProc(strlen(lhs), lhs) != if_info->doNot;
581 goto done;
582 }
583
584 while (isspace((unsigned char)*condExpr))
585 condExpr++;
586
587 if (*condExpr == '\0') {
588 Parse_Error(PARSE_WARNING,
589 "Missing right-hand-side of operator");
590 goto done;
591 }
592
593 rhs = CondGetString(doEval, &rhsQuoted, &rhsFree, FALSE);
594 if (!rhs)
595 goto done;
596
597 if (!doEval) {
598 t = TOK_FALSE;
599 goto done;
600 }
601
602 if (rhsQuoted || lhsQuoted) {
603 do_string_compare:
604 if (((*op != '!') && (*op != '=')) || (op[1] != '=')) {
605 Parse_Error(PARSE_WARNING,
606 "String comparison operator should be either == or !=");
607 goto done;
608 }
609
610 if (DEBUG(COND)) {
611 fprintf(debug_file, "lhs = \"%s\", rhs = \"%s\", op = %.2s\n",
612 lhs, rhs, op);
613 }
614 /*
615 * Null-terminate rhs and perform the comparison.
616 * t is set to the result.
617 */
618 if (*op == '=') {
619 t = strcmp(lhs, rhs) == 0;
620 } else {
621 t = strcmp(lhs, rhs) != 0;
622 }
623 } else {
624 /*
625 * rhs is either a float or an integer. Convert both the
626 * lhs and the rhs to a double and compare the two.
627 */
628
629 if (!CondCvtArg(lhs, &left) || !CondCvtArg(rhs, &right))
630 goto do_string_compare;
631
632 if (DEBUG(COND)) {
633 fprintf(debug_file, "left = %f, right = %f, op = %.2s\n", left,
634 right, op);
635 }
636 switch (op[0]) {
637 case '!':
638 if (op[1] != '=') {
639 Parse_Error(PARSE_WARNING,
640 "Unknown operator");
641 goto done;
642 }
643 t = (left != right);
644 break;
645 case '=':
646 if (op[1] != '=') {
647 Parse_Error(PARSE_WARNING,
648 "Unknown operator");
649 goto done;
650 }
651 t = (left == right);
652 break;
653 case '<':
654 if (op[1] == '=') {
655 t = (left <= right);
656 } else {
657 t = (left < right);
658 }
659 break;
660 case '>':
661 if (op[1] == '=') {
662 t = (left >= right);
663 } else {
664 t = (left > right);
665 }
666 break;
667 }
668 }
669
670 done:
671 free(lhsFree);
672 free(rhsFree);
673 return t;
674 }
675
676 static int
677 ParseEmptyArg(Boolean doEval, const char **linePtr, char **argPtr,
678 const char *func MAKE_ATTR_UNUSED)
679 {
680 void *val_freeIt;
681 const char *val;
682 int magic_res;
683
684 /* We do all the work here and return the result as the length */
685 *argPtr = NULL;
686
687 (*linePtr)--; /* Make (*linePtr)[1] point to the '('. */
688 val = Var_ParsePP(linePtr, VAR_CMD, doEval ? VARE_WANTRES : 0, &val_freeIt);
689 /* If successful, *linePtr points beyond the closing ')' now. */
690
691 if (val == var_Error) {
692 free(val_freeIt);
693 return -1;
694 }
695
696 /* A variable is empty when it just contains spaces... 4/15/92, christos */
697 while (isspace((unsigned char)val[0]))
698 val++;
699
700 /*
701 * For consistency with the other functions we can't generate the
702 * true/false here.
703 */
704 magic_res = *val != '\0' ? 2 : 1;
705 free(val_freeIt);
706 return magic_res;
707 }
708
709 static Boolean
710 CondDoEmpty(int arglen, const char *arg MAKE_ATTR_UNUSED)
711 {
712 /* Magic values ahead, see ParseEmptyArg. */
713 return arglen == 1;
714 }
715
716 static Token
717 compare_function(Boolean doEval)
718 {
719 static const struct fn_def {
720 const char *fn_name;
721 size_t fn_name_len;
722 int (*fn_getarg)(Boolean, const char **, char **, const char *);
723 Boolean (*fn_proc)(int, const char *);
724 } fn_defs[] = {
725 { "defined", 7, ParseFuncArg, CondDoDefined },
726 { "make", 4, ParseFuncArg, CondDoMake },
727 { "exists", 6, ParseFuncArg, CondDoExists },
728 { "empty", 5, ParseEmptyArg, CondDoEmpty },
729 { "target", 6, ParseFuncArg, CondDoTarget },
730 { "commands", 8, ParseFuncArg, CondDoCommands },
731 { NULL, 0, NULL, NULL },
732 };
733 const struct fn_def *fn_def;
734 Token t;
735 char *arg = NULL;
736 int arglen;
737 const char *cp = condExpr;
738 const char *cp1;
739
740 for (fn_def = fn_defs; fn_def->fn_name != NULL; fn_def++) {
741 if (!istoken(cp, fn_def->fn_name, fn_def->fn_name_len))
742 continue;
743 cp += fn_def->fn_name_len;
744 /* There can only be whitespace before the '(' */
745 while (isspace((unsigned char)*cp))
746 cp++;
747 if (*cp != '(')
748 break;
749
750 arglen = fn_def->fn_getarg(doEval, &cp, &arg, fn_def->fn_name);
751 if (arglen <= 0) {
752 condExpr = cp;
753 return arglen < 0 ? TOK_ERROR : TOK_FALSE;
754 }
755 /* Evaluate the argument using the required function. */
756 t = !doEval || fn_def->fn_proc(arglen, arg);
757 free(arg);
758 condExpr = cp;
759 return t;
760 }
761
762 /* Push anything numeric through the compare expression */
763 cp = condExpr;
764 if (isdigit((unsigned char)cp[0]) || strchr("+-", cp[0]))
765 return compare_expression(doEval);
766
767 /*
768 * Most likely we have a naked token to apply the default function to.
769 * However ".if a == b" gets here when the "a" is unquoted and doesn't
770 * start with a '$'. This surprises people.
771 * If what follows the function argument is a '=' or '!' then the syntax
772 * would be invalid if we did "defined(a)" - so instead treat as an
773 * expression.
774 */
775 arglen = ParseFuncArg(doEval, &cp, &arg, NULL);
776 for (cp1 = cp; isspace((unsigned char)*cp1); cp1++)
777 continue;
778 if (*cp1 == '=' || *cp1 == '!')
779 return compare_expression(doEval);
780 condExpr = cp;
781
782 /*
783 * Evaluate the argument using the default function.
784 * This path always treats .if as .ifdef. To get here the character
785 * after .if must have been taken literally, so the argument cannot
786 * be empty - even if it contained a variable expansion.
787 */
788 t = !doEval || if_info->defProc(arglen, arg) != if_info->doNot;
789 free(arg);
790 return t;
791 }
792
793 static Token
794 CondToken(Boolean doEval)
795 {
796 Token t;
797
798 t = condPushBack;
799 if (t != TOK_NONE) {
800 condPushBack = TOK_NONE;
801 return t;
802 }
803
804 while (*condExpr == ' ' || *condExpr == '\t') {
805 condExpr++;
806 }
807
808 switch (*condExpr) {
809
810 case '(':
811 condExpr++;
812 return TOK_LPAREN;
813
814 case ')':
815 condExpr++;
816 return TOK_RPAREN;
817
818 case '|':
819 if (condExpr[1] == '|') {
820 condExpr++;
821 }
822 condExpr++;
823 return TOK_OR;
824
825 case '&':
826 if (condExpr[1] == '&') {
827 condExpr++;
828 }
829 condExpr++;
830 return TOK_AND;
831
832 case '!':
833 condExpr++;
834 return TOK_NOT;
835
836 case '#':
837 case '\n':
838 case '\0':
839 return TOK_EOF;
840
841 case '"':
842 case '$':
843 return compare_expression(doEval);
844
845 default:
846 return compare_function(doEval);
847 }
848 }
849
850 /*-
851 *-----------------------------------------------------------------------
852 * CondT --
853 * Parse a single term in the expression. This consists of a terminal
854 * symbol or TOK_NOT and a terminal symbol (not including the binary
855 * operators):
856 * T -> defined(variable) | make(target) | exists(file) | symbol
857 * T -> ! T | ( E )
858 *
859 * Results:
860 * TOK_TRUE, TOK_FALSE or TOK_ERROR.
861 *
862 * Side Effects:
863 * Tokens are consumed.
864 *
865 *-----------------------------------------------------------------------
866 */
867 static Token
868 CondT(Boolean doEval)
869 {
870 Token t;
871
872 t = CondToken(doEval);
873
874 if (t == TOK_EOF) {
875 /*
876 * If we reached the end of the expression, the expression
877 * is malformed...
878 */
879 t = TOK_ERROR;
880 } else if (t == TOK_LPAREN) {
881 /*
882 * T -> ( E )
883 */
884 t = CondE(doEval);
885 if (t != TOK_ERROR) {
886 if (CondToken(doEval) != TOK_RPAREN) {
887 t = TOK_ERROR;
888 }
889 }
890 } else if (t == TOK_NOT) {
891 t = CondT(doEval);
892 if (t == TOK_TRUE) {
893 t = TOK_FALSE;
894 } else if (t == TOK_FALSE) {
895 t = TOK_TRUE;
896 }
897 }
898 return t;
899 }
900
901 /*-
902 *-----------------------------------------------------------------------
903 * CondF --
904 * Parse a conjunctive factor (nice name, wot?)
905 * F -> T && F | T
906 *
907 * Results:
908 * TOK_TRUE, TOK_FALSE or TOK_ERROR
909 *
910 * Side Effects:
911 * Tokens are consumed.
912 *
913 *-----------------------------------------------------------------------
914 */
915 static Token
916 CondF(Boolean doEval)
917 {
918 Token l, o;
919
920 l = CondT(doEval);
921 if (l != TOK_ERROR) {
922 o = CondToken(doEval);
923
924 if (o == TOK_AND) {
925 /*
926 * F -> T && F
927 *
928 * If T is TOK_FALSE, the whole thing will be TOK_FALSE, but we have to
929 * parse the r.h.s. anyway (to throw it away).
930 * If T is TOK_TRUE, the result is the r.h.s., be it an TOK_ERROR or no.
931 */
932 if (l == TOK_TRUE) {
933 l = CondF(doEval);
934 } else {
935 (void)CondF(FALSE);
936 }
937 } else {
938 /*
939 * F -> T
940 */
941 CondPushBack(o);
942 }
943 }
944 return l;
945 }
946
947 /*-
948 *-----------------------------------------------------------------------
949 * CondE --
950 * Main expression production.
951 * E -> F || E | F
952 *
953 * Results:
954 * TOK_TRUE, TOK_FALSE or TOK_ERROR.
955 *
956 * Side Effects:
957 * Tokens are, of course, consumed.
958 *
959 *-----------------------------------------------------------------------
960 */
961 static Token
962 CondE(Boolean doEval)
963 {
964 Token l, o;
965
966 l = CondF(doEval);
967 if (l != TOK_ERROR) {
968 o = CondToken(doEval);
969
970 if (o == TOK_OR) {
971 /*
972 * E -> F || E
973 *
974 * A similar thing occurs for ||, except that here we make sure
975 * the l.h.s. is TOK_FALSE before we bother to evaluate the r.h.s.
976 * Once again, if l is TOK_FALSE, the result is the r.h.s. and once
977 * again if l is TOK_TRUE, we parse the r.h.s. to throw it away.
978 */
979 if (l == TOK_FALSE) {
980 l = CondE(doEval);
981 } else {
982 (void)CondE(FALSE);
983 }
984 } else {
985 /*
986 * E -> F
987 */
988 CondPushBack(o);
989 }
990 }
991 return l;
992 }
993
994 static CondEvalResult
995 do_Cond_EvalExpression(Boolean *value)
996 {
997
998 switch (CondE(TRUE)) {
999 case TOK_TRUE:
1000 if (CondToken(TRUE) == TOK_EOF) {
1001 *value = TRUE;
1002 return COND_PARSE;
1003 }
1004 break;
1005 case TOK_FALSE:
1006 if (CondToken(TRUE) == TOK_EOF) {
1007 *value = FALSE;
1008 return COND_PARSE;
1009 }
1010 break;
1011 default:
1012 case TOK_ERROR:
1013 break;
1014 }
1015
1016 return COND_INVALID;
1017 }
1018
1019 /* Evaluate the condition in the passed line, including any side effects from
1020 * the variable expressions in the condition. The condition consists of &&,
1021 * ||, !, function(arg), comparisons and parenthetical groupings thereof.
1022 *
1023 * Results:
1024 * COND_PARSE if the condition was valid grammatically
1025 * COND_INVALID if not a valid conditional.
1026 *
1027 * (*value) is set to the boolean value of the condition
1028 */
1029 CondEvalResult
1030 Cond_EvalExpression(const struct If *info, const char *line, Boolean *value,
1031 int eprint, Boolean strictLHS)
1032 {
1033 static const struct If *dflt_info;
1034 const struct If *sv_if_info = if_info;
1035 const char *sv_condExpr = condExpr;
1036 Token sv_condPushBack = condPushBack;
1037 int rval;
1038
1039 lhsStrict = strictLHS;
1040
1041 while (*line == ' ' || *line == '\t')
1042 line++;
1043
1044 if (info == NULL && (info = dflt_info) == NULL) {
1045 /* Scan for the entry for .if - it can't be first */
1046 for (info = ifs;; info++)
1047 if (info->form[0] == 0)
1048 break;
1049 dflt_info = info;
1050 }
1051 assert(info != NULL);
1052
1053 if_info = info;
1054 condExpr = line;
1055 condPushBack = TOK_NONE;
1056
1057 rval = do_Cond_EvalExpression(value);
1058
1059 if (rval == COND_INVALID && eprint)
1060 Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", line);
1061
1062 if_info = sv_if_info;
1063 condExpr = sv_condExpr;
1064 condPushBack = sv_condPushBack;
1065
1066 return rval;
1067 }
1068
1069
1070 /* Evaluate the conditional in the passed line. The line looks like this:
1071 * .<cond-type> <expr>
1072 * In this line, <cond-type> is any of if, ifmake, ifnmake, ifdef, ifndef,
1073 * elif, elifmake, elifnmake, elifdef, elifndef.
1074 * In this line, <expr> consists of &&, ||, !, function(arg), comparisons
1075 * and parenthetical groupings thereof.
1076 *
1077 * Note that the states IF_ACTIVE and ELSE_ACTIVE are only different in order
1078 * to detect spurious .else lines (as are SKIP_TO_ELSE and SKIP_TO_ENDIF),
1079 * otherwise .else could be treated as '.elif 1'.
1080 *
1081 * Results:
1082 * COND_PARSE to continue parsing the lines after the conditional
1083 * (when .if or .else returns TRUE)
1084 * COND_SKIP to skip the lines after the conditional
1085 * (when .if or .elif returns FALSE, or when a previous
1086 * branch has already been taken)
1087 * COND_INVALID if the conditional was not valid, either because of
1088 * a syntax error or because some variable was undefined
1089 * or because the condition could not be evaluated
1090 */
1091 CondEvalResult
1092 Cond_Eval(const char *line)
1093 {
1094 enum { MAXIF = 128 }; /* maximum depth of .if'ing */
1095 enum { MAXIF_BUMP = 32 }; /* how much to grow by */
1096 enum if_states {
1097 IF_ACTIVE, /* .if or .elif part active */
1098 ELSE_ACTIVE, /* .else part active */
1099 SEARCH_FOR_ELIF, /* searching for .elif/else to execute */
1100 SKIP_TO_ELSE, /* has been true, but not seen '.else' */
1101 SKIP_TO_ENDIF /* nothing else to execute */
1102 };
1103 static enum if_states *cond_state = NULL;
1104 static unsigned int max_if_depth = MAXIF;
1105
1106 const struct If *ifp;
1107 Boolean isElif;
1108 Boolean value;
1109 int level; /* Level at which to report errors. */
1110 enum if_states state;
1111
1112 level = PARSE_FATAL;
1113 if (!cond_state) {
1114 cond_state = bmake_malloc(max_if_depth * sizeof(*cond_state));
1115 cond_state[0] = IF_ACTIVE;
1116 }
1117 /* skip leading character (the '.') and any whitespace */
1118 for (line++; *line == ' ' || *line == '\t'; line++)
1119 continue;
1120
1121 /* Find what type of if we're dealing with. */
1122 if (line[0] == 'e') {
1123 if (line[1] != 'l') {
1124 if (!istoken(line + 1, "ndif", 4))
1125 return COND_INVALID;
1126 /* End of conditional section */
1127 if (cond_depth == cond_min_depth) {
1128 Parse_Error(level, "if-less endif");
1129 return COND_PARSE;
1130 }
1131 /* Return state for previous conditional */
1132 cond_depth--;
1133 return cond_state[cond_depth] <= ELSE_ACTIVE
1134 ? COND_PARSE : COND_SKIP;
1135 }
1136
1137 /* Quite likely this is 'else' or 'elif' */
1138 line += 2;
1139 if (istoken(line, "se", 2)) {
1140 /* It is else... */
1141 if (cond_depth == cond_min_depth) {
1142 Parse_Error(level, "if-less else");
1143 return COND_PARSE;
1144 }
1145
1146 state = cond_state[cond_depth];
1147 switch (state) {
1148 case SEARCH_FOR_ELIF:
1149 state = ELSE_ACTIVE;
1150 break;
1151 case ELSE_ACTIVE:
1152 case SKIP_TO_ENDIF:
1153 Parse_Error(PARSE_WARNING, "extra else");
1154 /* FALLTHROUGH */
1155 default:
1156 case IF_ACTIVE:
1157 case SKIP_TO_ELSE:
1158 state = SKIP_TO_ENDIF;
1159 break;
1160 }
1161 cond_state[cond_depth] = state;
1162 return state <= ELSE_ACTIVE ? COND_PARSE : COND_SKIP;
1163 }
1164 /* Assume for now it is an elif */
1165 isElif = TRUE;
1166 } else
1167 isElif = FALSE;
1168
1169 if (line[0] != 'i' || line[1] != 'f')
1170 /* Not an ifxxx or elifxxx line */
1171 return COND_INVALID;
1172
1173 /*
1174 * Figure out what sort of conditional it is -- what its default
1175 * function is, etc. -- by looking in the table of valid "ifs"
1176 */
1177 line += 2;
1178 for (ifp = ifs;; ifp++) {
1179 if (ifp->form == NULL)
1180 return COND_INVALID;
1181 if (istoken(ifp->form, line, ifp->formlen)) {
1182 line += ifp->formlen;
1183 break;
1184 }
1185 }
1186
1187 /* Now we know what sort of 'if' it is... */
1188
1189 if (isElif) {
1190 if (cond_depth == cond_min_depth) {
1191 Parse_Error(level, "if-less elif");
1192 return COND_PARSE;
1193 }
1194 state = cond_state[cond_depth];
1195 if (state == SKIP_TO_ENDIF || state == ELSE_ACTIVE) {
1196 Parse_Error(PARSE_WARNING, "extra elif");
1197 cond_state[cond_depth] = SKIP_TO_ENDIF;
1198 return COND_SKIP;
1199 }
1200 if (state != SEARCH_FOR_ELIF) {
1201 /* Either just finished the 'true' block, or already SKIP_TO_ELSE */
1202 cond_state[cond_depth] = SKIP_TO_ELSE;
1203 return COND_SKIP;
1204 }
1205 } else {
1206 /* Normal .if */
1207 if (cond_depth + 1 >= max_if_depth) {
1208 /*
1209 * This is rare, but not impossible.
1210 * In meta mode, dirdeps.mk (only runs at level 0)
1211 * can need more than the default.
1212 */
1213 max_if_depth += MAXIF_BUMP;
1214 cond_state = bmake_realloc(cond_state,
1215 max_if_depth * sizeof(*cond_state));
1216 }
1217 state = cond_state[cond_depth];
1218 cond_depth++;
1219 if (state > ELSE_ACTIVE) {
1220 /* If we aren't parsing the data, treat as always false */
1221 cond_state[cond_depth] = SKIP_TO_ELSE;
1222 return COND_SKIP;
1223 }
1224 }
1225
1226 /* And evaluate the conditional expression */
1227 if (Cond_EvalExpression(ifp, line, &value, 1, TRUE) == COND_INVALID) {
1228 /* Syntax error in conditional, error message already output. */
1229 /* Skip everything to matching .endif */
1230 cond_state[cond_depth] = SKIP_TO_ELSE;
1231 return COND_SKIP;
1232 }
1233
1234 if (!value) {
1235 cond_state[cond_depth] = SEARCH_FOR_ELIF;
1236 return COND_SKIP;
1237 }
1238 cond_state[cond_depth] = IF_ACTIVE;
1239 return COND_PARSE;
1240 }
1241
1242 void
1243 Cond_restore_depth(unsigned int saved_depth)
1244 {
1245 int open_conds = cond_depth - cond_min_depth;
1246
1247 if (open_conds != 0 || saved_depth > cond_depth) {
1248 Parse_Error(PARSE_FATAL, "%d open conditional%s", open_conds,
1249 open_conds == 1 ? "" : "s");
1250 cond_depth = cond_min_depth;
1251 }
1252
1253 cond_min_depth = saved_depth;
1254 }
1255
1256 unsigned int
1257 Cond_save_depth(void)
1258 {
1259 int depth = cond_min_depth;
1260
1261 cond_min_depth = cond_depth;
1262 return depth;
1263 }
1264