cond.c revision 1.318 1 /* $NetBSD: cond.c,v 1.318 2021/12/30 01:06: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 /*
73 * Handling of conditionals in a makefile.
74 *
75 * Interface:
76 * Cond_EvalLine Evaluate the conditional directive, such as
77 * '.if <cond>', '.elifnmake <cond>', '.else', '.endif'.
78 *
79 * Cond_EvalCondition
80 * Evaluate the conditional, which is either the argument
81 * of one of the .if directives or the condition in a
82 * ':?then:else' variable modifier.
83 *
84 * Cond_save_depth
85 * Cond_restore_depth
86 * Save and restore the nesting of the conditions, at
87 * the start and end of including another makefile, to
88 * ensure that in each makefile the conditional
89 * directives are well-balanced.
90 */
91
92 #include <errno.h>
93
94 #include "make.h"
95 #include "dir.h"
96
97 /* "@(#)cond.c 8.2 (Berkeley) 1/2/94" */
98 MAKE_RCSID("$NetBSD: cond.c,v 1.318 2021/12/30 01:06:43 rillig Exp $");
99
100 /*
101 * The parsing of conditional expressions is based on this grammar:
102 * Or -> And ('||' And)*
103 * And -> Term ('&&' Term)*
104 * Term -> Function '(' Argument ')'
105 * Term -> Leaf Operator Leaf
106 * Term -> Leaf
107 * Term -> '(' Or ')'
108 * Term -> '!' Term
109 * Leaf -> "string"
110 * Leaf -> Number
111 * Leaf -> VariableExpression
112 * Leaf -> Symbol
113 * Operator -> '==' | '!=' | '>' | '<' | '>=' | '<='
114 *
115 * 'Symbol' is an unquoted string literal to which the default function is
116 * applied.
117 *
118 * The tokens are scanned by CondToken, which returns:
119 * TOK_AND for '&&'
120 * TOK_OR for '||'
121 * TOK_NOT for '!'
122 * TOK_LPAREN for '('
123 * TOK_RPAREN for ')'
124 *
125 * Other terminal symbols are evaluated using either the default function or
126 * the function given in the terminal, they return either TOK_TRUE, TOK_FALSE
127 * or TOK_ERROR.
128 */
129 typedef enum Token {
130 TOK_FALSE, TOK_TRUE, TOK_AND, TOK_OR, TOK_NOT,
131 TOK_LPAREN, TOK_RPAREN, TOK_EOF, TOK_NONE, TOK_ERROR
132 } Token;
133
134 typedef enum ComparisonOp {
135 LT, LE, GT, GE, EQ, NE
136 } ComparisonOp;
137
138 typedef struct CondParser {
139
140 /*
141 * The plain '.if ${VAR}' evaluates to true if the value of the
142 * expression has length > 0. The other '.if' variants delegate
143 * to evalBare instead.
144 */
145 bool plain;
146
147 /* The function to apply on unquoted bare words. */
148 bool (*evalBare)(const char *);
149 bool negateEvalBare;
150
151 /*
152 * Whether the left-hand side of a comparison may be an unquoted
153 * string. This is allowed for expressions of the form
154 * ${condition:?:}, see ApplyModifier_IfElse. Such a condition is
155 * expanded before it is evaluated, due to ease of implementation.
156 * This means that at the point where the condition is evaluated,
157 * make cannot know anymore whether the left-hand side had originally
158 * been a variable expression or a plain word.
159 *
160 * In all other contexts, the left-hand side must either be a
161 * variable expression, a quoted string or a number.
162 */
163 bool leftUnquotedOK;
164
165 const char *p; /* The remaining condition to parse */
166 Token curr; /* Single push-back token used in parsing */
167
168 /*
169 * Whether an error message has already been printed for this
170 * condition. The first available error message is usually the most
171 * specific one, therefore it makes sense to suppress the standard
172 * "Malformed conditional" message.
173 */
174 bool printedError;
175 } CondParser;
176
177 static CondResult CondParser_Or(CondParser *par, bool);
178
179 static unsigned int cond_depth = 0; /* current .if nesting level */
180 static unsigned int cond_min_depth = 0; /* depth at makefile open */
181
182 /* Names for ComparisonOp. */
183 static const char opname[][3] = { "<", "<=", ">", ">=", "==", "!=" };
184
185 MAKE_INLINE bool
186 skip_string(const char **pp, const char *str)
187 {
188 size_t len = strlen(str);
189 bool ok = strncmp(*pp, str, len) == 0;
190 if (ok)
191 *pp += len;
192 return ok;
193 }
194
195 static Token
196 ToToken(bool cond)
197 {
198 return cond ? TOK_TRUE : TOK_FALSE;
199 }
200
201 static void
202 CondParser_SkipWhitespace(CondParser *par)
203 {
204 cpp_skip_whitespace(&par->p);
205 }
206
207 /*
208 * Parse a single word, taking into account balanced parentheses as well as
209 * embedded expressions. Used for the argument of a built-in function as
210 * well as for bare words, which are then passed to the default function.
211 *
212 * Arguments:
213 * *pp initially points at the '(',
214 * upon successful return it points right after the ')'.
215 *
216 * *out_arg receives the argument as string.
217 *
218 * func says whether the argument belongs to an actual function, or
219 * NULL when parsing a bare word.
220 *
221 * Return NULL if there was a parse error or the argument was empty.
222 */
223 static char *
224 ParseWord(CondParser *par, const char **pp, bool doEval, const char *func)
225 {
226 const char *p = *pp;
227 Buffer argBuf;
228 int paren_depth;
229 char *res;
230
231 if (func != NULL)
232 p++; /* Skip opening '(' - verified by caller */
233
234 cpp_skip_hspace(&p);
235
236 Buf_InitSize(&argBuf, 16);
237
238 paren_depth = 0;
239 for (;;) {
240 char ch = *p;
241 if (ch == '\0' || ch == ' ' || ch == '\t')
242 break;
243 if ((ch == '&' || ch == '|') && paren_depth == 0)
244 break;
245 if (*p == '$') {
246 /*
247 * Parse the variable expression and install it as
248 * part of the argument if it's valid. We tell
249 * Var_Parse to complain on an undefined variable,
250 * (XXX: but Var_Parse ignores that request)
251 * so we don't need to do it. Nor do we return an
252 * error, though perhaps we should.
253 */
254 VarEvalMode emode = doEval
255 ? VARE_UNDEFERR
256 : VARE_PARSE_ONLY;
257 FStr nestedVal;
258 (void)Var_Parse(&p, SCOPE_CMDLINE, emode, &nestedVal);
259 /* TODO: handle errors */
260 Buf_AddStr(&argBuf, nestedVal.str);
261 FStr_Done(&nestedVal);
262 continue;
263 }
264 if (ch == '(')
265 paren_depth++;
266 else if (ch == ')' && --paren_depth < 0)
267 break;
268 Buf_AddByte(&argBuf, *p);
269 p++;
270 }
271
272 res = Buf_DoneData(&argBuf);
273
274 cpp_skip_hspace(&p);
275
276 if (func != NULL && *p++ != ')') {
277 int len = 0;
278 while (ch_isalpha(func[len]))
279 len++;
280
281 Parse_Error(PARSE_FATAL,
282 "Missing closing parenthesis for %.*s()", len, func);
283 par->printedError = true;
284 free(res);
285 return NULL;
286 }
287
288 *pp = p;
289
290 if (res[0] == '\0') {
291 free(res);
292 res = NULL;
293 }
294 return res;
295 }
296
297 /* Test whether the given variable is defined. */
298 static bool
299 FuncDefined(const char *arg)
300 {
301 FStr value = Var_Value(SCOPE_CMDLINE, arg);
302 bool result = value.str != NULL;
303 FStr_Done(&value);
304 return result;
305 }
306
307 /* See if the given target is requested to be made. */
308 static bool
309 FuncMake(const char *arg)
310 {
311 StringListNode *ln;
312
313 for (ln = opts.create.first; ln != NULL; ln = ln->next)
314 if (Str_Match(ln->datum, arg))
315 return true;
316 return false;
317 }
318
319 /* See if the given file exists. */
320 static bool
321 FuncExists(const char *arg)
322 {
323 bool result;
324 char *path;
325
326 path = Dir_FindFile(arg, &dirSearchPath);
327 DEBUG2(COND, "exists(%s) result is \"%s\"\n",
328 arg, path != NULL ? path : "");
329 result = path != NULL;
330 free(path);
331 return result;
332 }
333
334 /* See if the given node exists and is an actual target. */
335 static bool
336 FuncTarget(const char *arg)
337 {
338 GNode *gn = Targ_FindNode(arg);
339 return gn != NULL && GNode_IsTarget(gn);
340 }
341
342 /*
343 * See if the given node exists and is an actual target with commands
344 * associated with it.
345 */
346 static bool
347 FuncCommands(const char *arg)
348 {
349 GNode *gn = Targ_FindNode(arg);
350 return gn != NULL && GNode_IsTarget(gn) && !Lst_IsEmpty(&gn->commands);
351 }
352
353 /*
354 * Convert the given number into a double.
355 * We try a base 10 or 16 integer conversion first, if that fails
356 * then we try a floating point conversion instead.
357 *
358 * Results:
359 * Returns true if the conversion succeeded.
360 * Sets 'out_value' to the converted number.
361 */
362 static bool
363 TryParseNumber(const char *str, double *out_value)
364 {
365 char *end;
366 unsigned long ul_val;
367 double dbl_val;
368
369 if (str[0] == '\0') { /* XXX: why is an empty string a number? */
370 *out_value = 0.0;
371 return true;
372 }
373
374 errno = 0;
375 ul_val = strtoul(str, &end, str[1] == 'x' ? 16 : 10);
376 if (*end == '\0' && errno != ERANGE) {
377 *out_value = str[0] == '-' ? -(double)-ul_val : (double)ul_val;
378 return true;
379 }
380
381 if (*end != '\0' && *end != '.' && *end != 'e' && *end != 'E')
382 return false; /* skip the expensive strtod call */
383 dbl_val = strtod(str, &end);
384 if (*end != '\0')
385 return false;
386
387 *out_value = dbl_val;
388 return true;
389 }
390
391 static bool
392 is_separator(char ch)
393 {
394 return ch == '\0' || ch_isspace(ch) || ch == '!' || ch == '=' ||
395 ch == '>' || ch == '<' || ch == ')' /* but not '(' */;
396 }
397
398 /*
399 * In a quoted or unquoted string literal or a number, parse a variable
400 * expression.
401 *
402 * Example: .if x${CENTER}y == "${PREFIX}${SUFFIX}" || 0x${HEX}
403 */
404 static bool
405 CondParser_StringExpr(CondParser *par, const char *start,
406 bool doEval, bool quoted,
407 Buffer *buf, FStr *inout_str)
408 {
409 VarEvalMode emode;
410 const char *p;
411 bool atStart;
412 VarParseResult parseResult;
413
414 emode = doEval && quoted ? VARE_WANTRES
415 : doEval ? VARE_UNDEFERR
416 : VARE_PARSE_ONLY;
417
418 p = par->p;
419 atStart = p == start;
420 parseResult = Var_Parse(&p, SCOPE_CMDLINE, emode, inout_str);
421 /* TODO: handle errors */
422 if (inout_str->str == var_Error) {
423 if (parseResult == VPR_ERR) {
424 /*
425 * FIXME: Even if an error occurs, there is no
426 * guarantee that it is reported.
427 *
428 * See cond-token-plain.mk $$$$$$$$.
429 */
430 par->printedError = true;
431 }
432 /*
433 * XXX: Can there be any situation in which a returned
434 * var_Error needs to be freed?
435 */
436 FStr_Done(inout_str);
437 /*
438 * Even if !doEval, we still report syntax errors, which is
439 * what getting var_Error back with !doEval means.
440 */
441 *inout_str = FStr_InitRefer(NULL);
442 return false;
443 }
444 par->p = p;
445
446 /*
447 * If the '$' started the string literal (which means no quotes), and
448 * the variable expression is followed by a space, looks like a
449 * comparison operator or is the end of the expression, we are done.
450 */
451 if (atStart && is_separator(par->p[0]))
452 return false;
453
454 Buf_AddStr(buf, inout_str->str);
455 FStr_Done(inout_str);
456 *inout_str = FStr_InitRefer(NULL); /* not finished yet */
457 return true;
458 }
459
460 /*
461 * Parse a string from a variable expression or an optionally quoted string,
462 * on the left-hand and right-hand sides of comparisons.
463 *
464 * Results:
465 * Returns the string without any enclosing quotes, or NULL on error.
466 * Sets out_quoted if the leaf was a quoted string literal.
467 */
468 static void
469 CondParser_Leaf(CondParser *par, bool doEval, bool unquotedOK,
470 FStr *out_str, bool *out_quoted)
471 {
472 Buffer buf;
473 FStr str;
474 bool quoted;
475 const char *start;
476
477 Buf_Init(&buf);
478 str = FStr_InitRefer(NULL);
479 *out_quoted = quoted = par->p[0] == '"';
480 start = par->p;
481 if (quoted)
482 par->p++;
483
484 while (par->p[0] != '\0' && str.str == NULL) {
485 switch (par->p[0]) {
486 case '\\':
487 par->p++;
488 if (par->p[0] != '\0') {
489 Buf_AddByte(&buf, par->p[0]);
490 par->p++;
491 }
492 continue;
493 case '"':
494 par->p++;
495 if (quoted)
496 goto got_str; /* skip the closing quote */
497 Buf_AddByte(&buf, '"');
498 continue;
499 case ')': /* see is_separator */
500 case '!':
501 case '=':
502 case '>':
503 case '<':
504 case ' ':
505 case '\t':
506 if (!quoted)
507 goto got_str;
508 Buf_AddByte(&buf, par->p[0]);
509 par->p++;
510 continue;
511 case '$':
512 if (!CondParser_StringExpr(par,
513 start, doEval, quoted, &buf, &str))
514 goto cleanup;
515 continue;
516 default:
517 if (!unquotedOK && !quoted && *start != '$' &&
518 !ch_isdigit(*start)) {
519 /*
520 * The left-hand side must be quoted,
521 * a variable expression or a number.
522 */
523 str = FStr_InitRefer(NULL);
524 goto cleanup;
525 }
526 Buf_AddByte(&buf, par->p[0]);
527 par->p++;
528 continue;
529 }
530 }
531 got_str:
532 str = FStr_InitOwn(buf.data);
533 buf.data = NULL;
534 cleanup:
535 Buf_Done(&buf);
536 *out_str = str;
537 }
538
539 static bool
540 EvalBare(const CondParser *par, const char *arg)
541 {
542 bool res = par->evalBare(arg);
543 return par->negateEvalBare ? !res : res;
544 }
545
546 /*
547 * Evaluate a "comparison without operator", such as in ".if ${VAR}" or
548 * ".if 0".
549 */
550 static bool
551 EvalNotEmpty(CondParser *par, const char *value, bool quoted)
552 {
553 double num;
554
555 /* For .ifxxx "...", check for non-empty string. */
556 if (quoted)
557 return value[0] != '\0';
558
559 /* For .ifxxx <number>, compare against zero */
560 if (TryParseNumber(value, &num))
561 return num != 0.0;
562
563 /*
564 * For .if ${...}, check for non-empty string. This is different
565 * from the evaluation function from that .if variant, which would
566 * test whether a variable of the given name were defined.
567 */
568 /*
569 * XXX: Whitespace should count as empty, just as in
570 * CondParser_FuncCallEmpty.
571 */
572 if (par->plain)
573 return value[0] != '\0';
574
575 return EvalBare(par, value);
576 }
577
578 /* Evaluate a numerical comparison, such as in ".if ${VAR} >= 9". */
579 static bool
580 EvalCompareNum(double lhs, ComparisonOp op, double rhs)
581 {
582 DEBUG3(COND, "lhs = %f, rhs = %f, op = %.2s\n", lhs, rhs, opname[op]);
583
584 switch (op) {
585 case LT:
586 return lhs < rhs;
587 case LE:
588 return lhs <= rhs;
589 case GT:
590 return lhs > rhs;
591 case GE:
592 return lhs >= rhs;
593 case NE:
594 return lhs != rhs;
595 default:
596 return lhs == rhs;
597 }
598 }
599
600 static Token
601 EvalCompareStr(CondParser *par, const char *lhs,
602 ComparisonOp op, const char *rhs)
603 {
604 if (op != EQ && op != NE) {
605 Parse_Error(PARSE_FATAL,
606 "String comparison operator must be either == or !=");
607 par->printedError = true;
608 return TOK_ERROR;
609 }
610
611 DEBUG3(COND, "lhs = \"%s\", rhs = \"%s\", op = %.2s\n",
612 lhs, rhs, opname[op]);
613 return ToToken((op == EQ) == (strcmp(lhs, rhs) == 0));
614 }
615
616 /* Evaluate a comparison, such as "${VAR} == 12345". */
617 static Token
618 EvalCompare(CondParser *par, const char *lhs, bool lhsQuoted,
619 ComparisonOp op, const char *rhs, bool rhsQuoted)
620 {
621 double left, right;
622
623 if (!rhsQuoted && !lhsQuoted)
624 if (TryParseNumber(lhs, &left) && TryParseNumber(rhs, &right))
625 return ToToken(EvalCompareNum(left, op, right));
626
627 return EvalCompareStr(par, lhs, op, rhs);
628 }
629
630 static bool
631 CondParser_ComparisonOp(CondParser *par, ComparisonOp *out_op)
632 {
633 const char *p = par->p;
634
635 if (p[0] == '<' && p[1] == '=')
636 return par->p += 2, *out_op = LE, true;
637 if (p[0] == '<')
638 return par->p += 1, *out_op = LT, true;
639 if (p[0] == '>' && p[1] == '=')
640 return par->p += 2, *out_op = GE, true;
641 if (p[0] == '>')
642 return par->p += 1, *out_op = GT, true;
643 if (p[0] == '=' && p[1] == '=')
644 return par->p += 2, *out_op = EQ, true;
645 if (p[0] == '!' && p[1] == '=')
646 return par->p += 2, *out_op = NE, true;
647 return false;
648 }
649
650 /*
651 * Parse a comparison condition such as:
652 *
653 * 0
654 * ${VAR:Mpattern}
655 * ${VAR} == value
656 * ${VAR:U0} < 12345
657 */
658 static Token
659 CondParser_Comparison(CondParser *par, bool doEval)
660 {
661 Token t = TOK_ERROR;
662 FStr lhs, rhs;
663 ComparisonOp op;
664 bool lhsQuoted, rhsQuoted;
665
666 CondParser_Leaf(par, doEval, par->leftUnquotedOK, &lhs, &lhsQuoted);
667 if (lhs.str == NULL)
668 goto done_lhs;
669
670 CondParser_SkipWhitespace(par);
671
672 if (!CondParser_ComparisonOp(par, &op)) {
673 /* Unknown operator, compare against an empty string or 0. */
674 t = ToToken(doEval && EvalNotEmpty(par, lhs.str, lhsQuoted));
675 goto done_lhs;
676 }
677
678 CondParser_SkipWhitespace(par);
679
680 if (par->p[0] == '\0') {
681 Parse_Error(PARSE_FATAL,
682 "Missing right-hand side of operator '%s'", opname[op]);
683 par->printedError = true;
684 goto done_lhs;
685 }
686
687 CondParser_Leaf(par, doEval, true, &rhs, &rhsQuoted);
688 if (rhs.str == NULL)
689 goto done_rhs;
690
691 if (!doEval) {
692 t = TOK_FALSE;
693 goto done_rhs;
694 }
695
696 t = EvalCompare(par, lhs.str, lhsQuoted, op, rhs.str, rhsQuoted);
697
698 done_rhs:
699 FStr_Done(&rhs);
700 done_lhs:
701 FStr_Done(&lhs);
702 return t;
703 }
704
705 /*
706 * The argument to empty() is a variable name, optionally followed by
707 * variable modifiers.
708 */
709 static bool
710 CondParser_FuncCallEmpty(CondParser *par, bool doEval, Token *out_token)
711 {
712 const char *cp = par->p;
713 Token tok;
714 FStr val;
715
716 if (!skip_string(&cp, "empty"))
717 return false;
718
719 cpp_skip_whitespace(&cp);
720 if (*cp != '(')
721 return false;
722
723 cp--; /* Make cp[1] point to the '('. */
724 (void)Var_Parse(&cp, SCOPE_CMDLINE,
725 doEval ? VARE_WANTRES : VARE_PARSE_ONLY, &val);
726 /* TODO: handle errors */
727
728 if (val.str == var_Error)
729 tok = TOK_ERROR;
730 else {
731 cpp_skip_whitespace(&val.str);
732 tok = ToToken(doEval && val.str[0] == '\0');
733 }
734
735 FStr_Done(&val);
736 *out_token = tok;
737 par->p = cp;
738 return true;
739 }
740
741 /* Parse a function call expression, such as 'defined(${file})'. */
742 static bool
743 CondParser_FuncCall(CondParser *par, bool doEval, Token *out_token)
744 {
745 char *arg;
746 const char *p = par->p;
747 bool (*fn)(const char *);
748 const char *fn_name = p;
749
750 if (skip_string(&p, "defined"))
751 fn = FuncDefined;
752 else if (skip_string(&p, "make"))
753 fn = FuncMake;
754 else if (skip_string(&p, "exists"))
755 fn = FuncExists;
756 else if (skip_string(&p, "target"))
757 fn = FuncTarget;
758 else if (skip_string(&p, "commands"))
759 fn = FuncCommands;
760 else
761 return false;
762
763 cpp_skip_whitespace(&p);
764 if (*p != '(')
765 return false;
766
767 arg = ParseWord(par, &p, doEval, fn_name);
768 *out_token = ToToken(doEval && arg != NULL && fn(arg));
769 free(arg);
770
771 par->p = p;
772 return true;
773 }
774
775 /*
776 * Parse a comparison that neither starts with '"' nor '$', such as the
777 * unusual 'bare == right' or '3 == ${VAR}', or a simple leaf without
778 * operator, which is a number, a variable expression or a string literal.
779 *
780 * TODO: Can this be merged into CondParser_Comparison?
781 */
782 static Token
783 CondParser_ComparisonOrLeaf(CondParser *par, bool doEval)
784 {
785 Token t;
786 char *arg = NULL;
787 const char *cp;
788 const char *cp1;
789
790 /* Push anything numeric through the compare expression */
791 cp = par->p;
792 if (ch_isdigit(cp[0]) || cp[0] == '-' || cp[0] == '+')
793 return CondParser_Comparison(par, doEval);
794
795 /*
796 * Most likely we have a naked token to apply the default function to.
797 * However ".if a == b" gets here when the "a" is unquoted and doesn't
798 * start with a '$'. This surprises people.
799 * If what follows the function argument is a '=' or '!' then the
800 * syntax would be invalid if we did "defined(a)" - so instead treat
801 * as an expression.
802 */
803 /*
804 * XXX: Is it possible to have a variable expression evaluated twice
805 * at this point?
806 */
807 arg = ParseWord(par, &cp, doEval, NULL);
808 assert(arg != NULL);
809
810 cp1 = cp;
811 cpp_skip_whitespace(&cp1);
812 if (*cp1 == '=' || *cp1 == '!' || *cp1 == '<' || *cp1 == '>')
813 return CondParser_Comparison(par, doEval);
814 par->p = cp;
815
816 /*
817 * Evaluate the argument using the default function.
818 * This path always treats .if as .ifdef. To get here, the character
819 * after .if must have been taken literally, so the argument cannot
820 * be empty - even if it contained a variable expansion.
821 */
822 t = ToToken(doEval && EvalBare(par, arg));
823 free(arg);
824 return t;
825 }
826
827 /* Return the next token or comparison result from the parser. */
828 static Token
829 CondParser_Token(CondParser *par, bool doEval)
830 {
831 Token t;
832
833 t = par->curr;
834 if (t != TOK_NONE) {
835 par->curr = TOK_NONE;
836 return t;
837 }
838
839 cpp_skip_hspace(&par->p);
840
841 switch (par->p[0]) {
842
843 case '(':
844 par->p++;
845 return TOK_LPAREN;
846
847 case ')':
848 par->p++;
849 return TOK_RPAREN;
850
851 case '|':
852 par->p++;
853 if (par->p[0] == '|')
854 par->p++;
855 else if (opts.strict) {
856 Parse_Error(PARSE_FATAL, "Unknown operator '|'");
857 par->printedError = true;
858 return TOK_ERROR;
859 }
860 return TOK_OR;
861
862 case '&':
863 par->p++;
864 if (par->p[0] == '&')
865 par->p++;
866 else if (opts.strict) {
867 Parse_Error(PARSE_FATAL, "Unknown operator '&'");
868 par->printedError = true;
869 return TOK_ERROR;
870 }
871 return TOK_AND;
872
873 case '!':
874 par->p++;
875 return TOK_NOT;
876
877 case '#': /* XXX: see unit-tests/cond-token-plain.mk */
878 case '\n': /* XXX: why should this end the condition? */
879 /* Probably obsolete now, from 1993-03-21. */
880 case '\0':
881 return TOK_EOF;
882
883 case '"':
884 case '$':
885 return CondParser_Comparison(par, doEval);
886
887 default:
888 if (CondParser_FuncCallEmpty(par, doEval, &t))
889 return t;
890 if (CondParser_FuncCall(par, doEval, &t))
891 return t;
892 return CondParser_ComparisonOrLeaf(par, doEval);
893 }
894 }
895
896 /* Skip the next token if it equals t. */
897 static bool
898 CondParser_Skip(CondParser *par, Token t)
899 {
900 Token actual;
901
902 actual = CondParser_Token(par, false);
903 if (actual == t)
904 return true;
905
906 assert(par->curr == TOK_NONE);
907 assert(actual != TOK_NONE);
908 par->curr = actual;
909 return false;
910 }
911
912 /*
913 * Term -> '(' Or ')'
914 * Term -> '!' Term
915 * Term -> Leaf Operator Leaf
916 * Term -> Leaf
917 */
918 static CondResult
919 CondParser_Term(CondParser *par, bool doEval)
920 {
921 CondResult res;
922 Token t;
923
924 t = CondParser_Token(par, doEval);
925 if (t == TOK_TRUE)
926 return CR_TRUE;
927 if (t == TOK_FALSE)
928 return CR_FALSE;
929
930 if (t == TOK_LPAREN) {
931 res = CondParser_Or(par, doEval);
932 if (res == CR_ERROR)
933 return CR_ERROR;
934 if (CondParser_Token(par, doEval) != TOK_RPAREN)
935 return CR_ERROR;
936 return res;
937 }
938
939 if (t == TOK_NOT) {
940 res = CondParser_Term(par, doEval);
941 if (res == CR_TRUE)
942 res = CR_FALSE;
943 else if (res == CR_FALSE)
944 res = CR_TRUE;
945 return res;
946 }
947
948 return CR_ERROR;
949 }
950
951 /*
952 * And -> Term ('&&' Term)*
953 */
954 static CondResult
955 CondParser_And(CondParser *par, bool doEval)
956 {
957 CondResult res, rhs;
958
959 res = CR_TRUE;
960 do {
961 if ((rhs = CondParser_Term(par, doEval)) == CR_ERROR)
962 return CR_ERROR;
963 if (rhs == CR_FALSE) {
964 res = CR_FALSE;
965 doEval = false;
966 }
967 } while (CondParser_Skip(par, TOK_AND));
968
969 return res;
970 }
971
972 /*
973 * Or -> And ('||' And)*
974 */
975 static CondResult
976 CondParser_Or(CondParser *par, bool doEval)
977 {
978 CondResult res, rhs;
979
980 res = CR_FALSE;
981 do {
982 if ((rhs = CondParser_And(par, doEval)) == CR_ERROR)
983 return CR_ERROR;
984 if (rhs == CR_TRUE) {
985 res = CR_TRUE;
986 doEval = false;
987 }
988 } while (CondParser_Skip(par, TOK_OR));
989
990 return res;
991 }
992
993 static CondResult
994 CondParser_Eval(CondParser *par)
995 {
996 CondResult res;
997
998 DEBUG1(COND, "CondParser_Eval: %s\n", par->p);
999
1000 res = CondParser_Or(par, true);
1001 if (res != CR_ERROR && CondParser_Token(par, false) != TOK_EOF)
1002 return CR_ERROR;
1003
1004 return res;
1005 }
1006
1007 /*
1008 * Evaluate the condition, including any side effects from the variable
1009 * expressions in the condition. The condition consists of &&, ||, !,
1010 * function(arg), comparisons and parenthetical groupings thereof.
1011 */
1012 static CondResult
1013 CondEvalExpression(const char *cond, bool plain,
1014 bool (*evalBare)(const char *), bool negate,
1015 bool eprint, bool leftUnquotedOK)
1016 {
1017 CondParser par;
1018 CondResult rval;
1019
1020 cpp_skip_hspace(&cond);
1021
1022 par.plain = plain;
1023 par.evalBare = evalBare;
1024 par.negateEvalBare = negate;
1025 par.leftUnquotedOK = leftUnquotedOK;
1026 par.p = cond;
1027 par.curr = TOK_NONE;
1028 par.printedError = false;
1029
1030 rval = CondParser_Eval(&par);
1031
1032 if (rval == CR_ERROR && eprint && !par.printedError)
1033 Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", cond);
1034
1035 return rval;
1036 }
1037
1038 /*
1039 * Evaluate a condition in a :? modifier, such as
1040 * ${"${VAR}" == value:?yes:no}.
1041 */
1042 CondResult
1043 Cond_EvalCondition(const char *cond)
1044 {
1045 return CondEvalExpression(cond, true,
1046 FuncDefined, false, false, true);
1047 }
1048
1049 static bool
1050 IsEndif(const char *p)
1051 {
1052 return p[0] == 'e' && p[1] == 'n' && p[2] == 'd' &&
1053 p[3] == 'i' && p[4] == 'f' && !ch_isalpha(p[5]);
1054 }
1055
1056 static bool
1057 DetermineKindOfConditional(const char **pp, bool *out_plain,
1058 bool (**out_evalBare)(const char *),
1059 bool *out_negate)
1060 {
1061 const char *p = *pp + 2;
1062
1063 *out_plain = false;
1064 *out_evalBare = FuncDefined;
1065 *out_negate = skip_string(&p, "n");
1066
1067 if (skip_string(&p, "def")) { /* .ifdef and .ifndef */
1068 } else if (skip_string(&p, "make")) /* .ifmake and .ifnmake */
1069 *out_evalBare = FuncMake;
1070 else if (!*out_negate) /* plain .if */
1071 *out_plain = true;
1072 else
1073 goto unknown_directive;
1074 if (ch_isalpha(*p))
1075 goto unknown_directive;
1076
1077 *pp = p;
1078 return true;
1079
1080 unknown_directive:
1081 /*
1082 * TODO: Add error message about unknown directive, since there is no
1083 * other known directive that starts with 'el' or 'if'.
1084 *
1085 * Example: .elifx 123
1086 */
1087 return false;
1088 }
1089
1090 /*
1091 * Evaluate the conditional directive in the line, which is one of:
1092 *
1093 * .if <cond>
1094 * .ifmake <cond>
1095 * .ifnmake <cond>
1096 * .ifdef <cond>
1097 * .ifndef <cond>
1098 * .elif <cond>
1099 * .elifmake <cond>
1100 * .elifnmake <cond>
1101 * .elifdef <cond>
1102 * .elifndef <cond>
1103 * .else
1104 * .endif
1105 *
1106 * In these directives, <cond> consists of &&, ||, !, function(arg),
1107 * comparisons, expressions, bare words, numbers and strings, and
1108 * parenthetical groupings thereof.
1109 *
1110 * Results:
1111 * CR_TRUE to continue parsing the lines that follow the
1112 * conditional (when <cond> evaluates to true)
1113 * CR_FALSE to skip the lines after the conditional
1114 * (when <cond> evaluates to false, or when a previous
1115 * branch has already been taken)
1116 * CR_ERROR if the conditional was not valid, either because of
1117 * a syntax error or because some variable was undefined
1118 * or because the condition could not be evaluated
1119 */
1120 CondResult
1121 Cond_EvalLine(const char *line)
1122 {
1123 typedef enum IfState {
1124
1125 /* None of the previous <cond> evaluated to true. */
1126 IFS_INITIAL = 0,
1127
1128 /*
1129 * The previous <cond> evaluated to true. The lines following
1130 * this condition are interpreted.
1131 */
1132 IFS_ACTIVE = 1 << 0,
1133
1134 /* The previous directive was an '.else'. */
1135 IFS_SEEN_ELSE = 1 << 1,
1136
1137 /* One of the previous <cond> evaluated to true. */
1138 IFS_WAS_ACTIVE = 1 << 2
1139
1140 } IfState;
1141
1142 static enum IfState *cond_states = NULL;
1143 static unsigned int cond_states_cap = 128;
1144
1145 bool plain;
1146 bool (*evalBare)(const char *);
1147 bool negate;
1148 bool isElif;
1149 CondResult res;
1150 IfState state;
1151 const char *p = line;
1152
1153 if (cond_states == NULL) {
1154 cond_states = bmake_malloc(
1155 cond_states_cap * sizeof *cond_states);
1156 cond_states[0] = IFS_ACTIVE;
1157 }
1158
1159 p++; /* skip the leading '.' */
1160 cpp_skip_hspace(&p);
1161
1162 if (IsEndif(p)) { /* It is an '.endif'. */
1163 if (p[5] != '\0') {
1164 Parse_Error(PARSE_FATAL,
1165 "The .endif directive does not take arguments");
1166 }
1167
1168 if (cond_depth == cond_min_depth) {
1169 Parse_Error(PARSE_FATAL, "if-less endif");
1170 return CR_TRUE;
1171 }
1172
1173 /* Return state for previous conditional */
1174 cond_depth--;
1175 return cond_states[cond_depth] & IFS_ACTIVE
1176 ? CR_TRUE : CR_FALSE;
1177 }
1178
1179 /* Parse the name of the directive, such as 'if', 'elif', 'endif'. */
1180 if (p[0] == 'e') {
1181 if (p[1] != 'l') {
1182 /*
1183 * Unknown directive. It might still be a
1184 * transformation rule like '.err.txt',
1185 * therefore no error message here.
1186 */
1187 return CR_ERROR;
1188 }
1189
1190 /* Quite likely this is 'else' or 'elif' */
1191 p += 2;
1192 if (strncmp(p, "se", 2) == 0 && !ch_isalpha(p[2])) {
1193 if (p[2] != '\0')
1194 Parse_Error(PARSE_FATAL,
1195 "The .else directive "
1196 "does not take arguments");
1197
1198 if (cond_depth == cond_min_depth) {
1199 Parse_Error(PARSE_FATAL, "if-less else");
1200 return CR_TRUE;
1201 }
1202
1203 state = cond_states[cond_depth];
1204 if (state == IFS_INITIAL) {
1205 state = IFS_ACTIVE | IFS_SEEN_ELSE;
1206 } else {
1207 if (state & IFS_SEEN_ELSE)
1208 Parse_Error(PARSE_WARNING,
1209 "extra else");
1210 state = IFS_WAS_ACTIVE | IFS_SEEN_ELSE;
1211 }
1212 cond_states[cond_depth] = state;
1213
1214 return state & IFS_ACTIVE ? CR_TRUE : CR_FALSE;
1215 }
1216 /* Assume for now it is an elif */
1217 isElif = true;
1218 } else
1219 isElif = false;
1220
1221 if (p[0] != 'i' || p[1] != 'f') {
1222 /*
1223 * Unknown directive. It might still be a transformation rule
1224 * like '.elisp.scm', therefore no error message here.
1225 */
1226 return CR_ERROR; /* Not an ifxxx or elifxxx line */
1227 }
1228
1229 if (!DetermineKindOfConditional(&p, &plain, &evalBare, &negate))
1230 return CR_ERROR;
1231
1232 if (isElif) {
1233 if (cond_depth == cond_min_depth) {
1234 Parse_Error(PARSE_FATAL, "if-less elif");
1235 return CR_TRUE;
1236 }
1237 state = cond_states[cond_depth];
1238 if (state & IFS_SEEN_ELSE) {
1239 Parse_Error(PARSE_WARNING, "extra elif");
1240 cond_states[cond_depth] =
1241 IFS_WAS_ACTIVE | IFS_SEEN_ELSE;
1242 return CR_FALSE;
1243 }
1244 if (state != IFS_INITIAL) {
1245 cond_states[cond_depth] = IFS_WAS_ACTIVE;
1246 return CR_FALSE;
1247 }
1248 } else {
1249 /* Normal .if */
1250 if (cond_depth + 1 >= cond_states_cap) {
1251 /*
1252 * This is rare, but not impossible.
1253 * In meta mode, dirdeps.mk (only runs at level 0)
1254 * can need more than the default.
1255 */
1256 cond_states_cap += 32;
1257 cond_states = bmake_realloc(cond_states,
1258 cond_states_cap * sizeof *cond_states);
1259 }
1260 state = cond_states[cond_depth];
1261 cond_depth++;
1262 if (!(state & IFS_ACTIVE)) {
1263 /*
1264 * If we aren't parsing the data,
1265 * treat as always false.
1266 */
1267 cond_states[cond_depth] = IFS_WAS_ACTIVE;
1268 return CR_FALSE;
1269 }
1270 }
1271
1272 /* And evaluate the conditional expression */
1273 res = CondEvalExpression(p, plain, evalBare, negate, true, false);
1274 if (res == CR_ERROR) {
1275 /*
1276 * Syntax error in conditional, error message already output.
1277 */
1278 /* Skip everything to matching .endif */
1279 /* XXX: An extra '.else' is not detected in this case. */
1280 cond_states[cond_depth] = IFS_WAS_ACTIVE;
1281 return CR_FALSE;
1282 }
1283
1284 cond_states[cond_depth] = res == CR_TRUE ? IFS_ACTIVE : IFS_INITIAL;
1285 return res;
1286 }
1287
1288 void
1289 Cond_restore_depth(unsigned int saved_depth)
1290 {
1291 unsigned int open_conds = cond_depth - cond_min_depth;
1292
1293 if (open_conds != 0 || saved_depth > cond_depth) {
1294 Parse_Error(PARSE_FATAL, "%u open conditional%s",
1295 open_conds, open_conds == 1 ? "" : "s");
1296 cond_depth = cond_min_depth;
1297 }
1298
1299 cond_min_depth = saved_depth;
1300 }
1301
1302 unsigned int
1303 Cond_save_depth(void)
1304 {
1305 unsigned int depth = cond_min_depth;
1306
1307 cond_min_depth = cond_depth;
1308 return depth;
1309 }
1310