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