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