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