cond.c revision 1.324 1 /* $NetBSD: cond.c,v 1.324 2022/01/07 09:19: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.324 2022/01/07 09:19:43 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 FStr value = Var_Value(SCOPE_CMDLINE, var);
294 bool result = value.str != NULL;
295 FStr_Done(&value);
296 return result;
297 }
298
299 /* See if the given target is requested to be made. */
300 static bool
301 FuncMake(const char *target)
302 {
303 StringListNode *ln;
304
305 for (ln = opts.create.first; ln != NULL; ln = ln->next)
306 if (Str_Match(ln->datum, target))
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 got_str; /* 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 got_str;
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 cleanup;
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 cleanup;
513 }
514 Buf_AddByte(&buf, par->p[0]);
515 par->p++;
516 continue;
517 }
518 }
519 got_str:
520 str = FStr_InitOwn(buf.data);
521 buf.data = NULL;
522 cleanup:
523 Buf_Done(&buf);
524 *out_str = str;
525 }
526
527 static bool
528 EvalBare(const CondParser *par, const char *arg)
529 {
530 bool res = par->evalBare(arg);
531 return par->negateEvalBare ? !res : res;
532 }
533
534 /*
535 * Evaluate a "comparison without operator", such as in ".if ${VAR}" or
536 * ".if 0".
537 */
538 static bool
539 EvalNotEmpty(CondParser *par, const char *value, bool quoted)
540 {
541 double num;
542
543 /* For .ifxxx "...", check for non-empty string. */
544 if (quoted)
545 return value[0] != '\0';
546
547 /* For .ifxxx <number>, compare against zero */
548 if (TryParseNumber(value, &num))
549 return num != 0.0;
550
551 /*
552 * For .if ${...}, check for non-empty string. This is different
553 * from the evaluation function from that .if variant, which would
554 * test whether a variable of the given name were defined.
555 */
556 /*
557 * XXX: Whitespace should count as empty, just as in
558 * CondParser_FuncCallEmpty.
559 */
560 if (par->plain)
561 return value[0] != '\0';
562
563 return EvalBare(par, value);
564 }
565
566 /* Evaluate a numerical comparison, such as in ".if ${VAR} >= 9". */
567 static bool
568 EvalCompareNum(double lhs, ComparisonOp op, double rhs)
569 {
570 DEBUG3(COND, "lhs = %f, rhs = %f, op = %.2s\n", lhs, rhs, opname[op]);
571
572 switch (op) {
573 case LT:
574 return lhs < rhs;
575 case LE:
576 return lhs <= rhs;
577 case GT:
578 return lhs > rhs;
579 case GE:
580 return lhs >= rhs;
581 case NE:
582 return lhs != rhs;
583 default:
584 return lhs == rhs;
585 }
586 }
587
588 static Token
589 EvalCompareStr(CondParser *par, const char *lhs,
590 ComparisonOp op, const char *rhs)
591 {
592 if (op != EQ && op != NE) {
593 Parse_Error(PARSE_FATAL,
594 "String comparison operator must be either == or !=");
595 par->printedError = true;
596 return TOK_ERROR;
597 }
598
599 DEBUG3(COND, "lhs = \"%s\", rhs = \"%s\", op = %.2s\n",
600 lhs, rhs, opname[op]);
601 return ToToken((op == EQ) == (strcmp(lhs, rhs) == 0));
602 }
603
604 /* Evaluate a comparison, such as "${VAR} == 12345". */
605 static Token
606 EvalCompare(CondParser *par, const char *lhs, bool lhsQuoted,
607 ComparisonOp op, const char *rhs, bool rhsQuoted)
608 {
609 double left, right;
610
611 if (!rhsQuoted && !lhsQuoted)
612 if (TryParseNumber(lhs, &left) && TryParseNumber(rhs, &right))
613 return ToToken(EvalCompareNum(left, op, right));
614
615 return EvalCompareStr(par, lhs, op, rhs);
616 }
617
618 static bool
619 CondParser_ComparisonOp(CondParser *par, ComparisonOp *out_op)
620 {
621 const char *p = par->p;
622
623 if (p[0] == '<' && p[1] == '=')
624 return par->p += 2, *out_op = LE, true;
625 if (p[0] == '<')
626 return par->p += 1, *out_op = LT, true;
627 if (p[0] == '>' && p[1] == '=')
628 return par->p += 2, *out_op = GE, true;
629 if (p[0] == '>')
630 return par->p += 1, *out_op = GT, true;
631 if (p[0] == '=' && p[1] == '=')
632 return par->p += 2, *out_op = EQ, true;
633 if (p[0] == '!' && p[1] == '=')
634 return par->p += 2, *out_op = NE, true;
635 return false;
636 }
637
638 /*
639 * Parse a comparison condition such as:
640 *
641 * 0
642 * ${VAR:Mpattern}
643 * ${VAR} == value
644 * ${VAR:U0} < 12345
645 */
646 static Token
647 CondParser_Comparison(CondParser *par, bool doEval)
648 {
649 Token t = TOK_ERROR;
650 FStr lhs, rhs;
651 ComparisonOp op;
652 bool lhsQuoted, rhsQuoted;
653
654 CondParser_Leaf(par, doEval, par->leftUnquotedOK, &lhs, &lhsQuoted);
655 if (lhs.str == NULL)
656 goto done_lhs;
657
658 CondParser_SkipWhitespace(par);
659
660 if (!CondParser_ComparisonOp(par, &op)) {
661 /* Unknown operator, compare against an empty string or 0. */
662 t = ToToken(doEval && EvalNotEmpty(par, lhs.str, lhsQuoted));
663 goto done_lhs;
664 }
665
666 CondParser_SkipWhitespace(par);
667
668 if (par->p[0] == '\0') {
669 Parse_Error(PARSE_FATAL,
670 "Missing right-hand side of operator '%s'", opname[op]);
671 par->printedError = true;
672 goto done_lhs;
673 }
674
675 CondParser_Leaf(par, doEval, true, &rhs, &rhsQuoted);
676 if (rhs.str == NULL)
677 goto done_rhs;
678
679 if (!doEval) {
680 t = TOK_FALSE;
681 goto done_rhs;
682 }
683
684 t = EvalCompare(par, lhs.str, lhsQuoted, op, rhs.str, rhsQuoted);
685
686 done_rhs:
687 FStr_Done(&rhs);
688 done_lhs:
689 FStr_Done(&lhs);
690 return t;
691 }
692
693 /*
694 * The argument to empty() is a variable name, optionally followed by
695 * variable modifiers.
696 */
697 static bool
698 CondParser_FuncCallEmpty(CondParser *par, bool doEval, Token *out_token)
699 {
700 const char *cp = par->p;
701 Token tok;
702 FStr val;
703
704 if (!skip_string(&cp, "empty"))
705 return false;
706
707 cpp_skip_whitespace(&cp);
708 if (*cp != '(')
709 return false;
710
711 cp--; /* Make cp[1] point to the '('. */
712 (void)Var_Parse(&cp, SCOPE_CMDLINE,
713 doEval ? VARE_WANTRES : VARE_PARSE_ONLY, &val);
714 /* TODO: handle errors */
715
716 if (val.str == var_Error)
717 tok = TOK_ERROR;
718 else {
719 cpp_skip_whitespace(&val.str);
720 tok = ToToken(doEval && val.str[0] == '\0');
721 }
722
723 FStr_Done(&val);
724 *out_token = tok;
725 par->p = cp;
726 return true;
727 }
728
729 /* Parse a function call expression, such as 'defined(${file})'. */
730 static bool
731 CondParser_FuncCall(CondParser *par, bool doEval, Token *out_token)
732 {
733 char *arg;
734 const char *p = par->p;
735 bool (*fn)(const char *);
736 const char *fn_name = p;
737
738 if (skip_string(&p, "defined"))
739 fn = FuncDefined;
740 else if (skip_string(&p, "make"))
741 fn = FuncMake;
742 else if (skip_string(&p, "exists"))
743 fn = FuncExists;
744 else if (skip_string(&p, "target"))
745 fn = FuncTarget;
746 else if (skip_string(&p, "commands"))
747 fn = FuncCommands;
748 else
749 return false;
750
751 cpp_skip_whitespace(&p);
752 if (*p != '(')
753 return false;
754
755 arg = ParseFuncArg(par, &p, doEval, fn_name);
756 *out_token = ToToken(doEval &&
757 arg != NULL && arg[0] != '\0' && fn(arg));
758 free(arg);
759
760 par->p = p;
761 return true;
762 }
763
764 /*
765 * Parse a comparison that neither starts with '"' nor '$', such as the
766 * unusual 'bare == right' or '3 == ${VAR}', or a simple leaf without
767 * operator, which is a number, a variable expression or a string literal.
768 *
769 * TODO: Can this be merged into CondParser_Comparison?
770 */
771 static Token
772 CondParser_ComparisonOrLeaf(CondParser *par, bool doEval)
773 {
774 Token t;
775 char *arg;
776 const char *cp;
777
778 /* Push anything numeric through the compare expression */
779 cp = par->p;
780 if (ch_isdigit(cp[0]) || cp[0] == '-' || cp[0] == '+')
781 return CondParser_Comparison(par, doEval);
782
783 /*
784 * Most likely we have a naked token to apply the default function to.
785 * However ".if a == b" gets here when the "a" is unquoted and doesn't
786 * start with a '$'. This surprises people.
787 * If what follows the function argument is a '=' or '!' then the
788 * syntax would be invalid if we did "defined(a)" - so instead treat
789 * as an expression.
790 */
791 /*
792 * XXX: In edge cases, a variable expression may be evaluated twice,
793 * see cond-token-plain.mk, keyword 'twice'.
794 */
795 arg = ParseWord(&cp, doEval);
796 assert(arg[0] != '\0');
797
798 if (*cp == '=' || *cp == '!' || *cp == '<' || *cp == '>')
799 return CondParser_Comparison(par, doEval);
800 par->p = cp;
801
802 /*
803 * Evaluate the argument using the default function.
804 * This path always treats .if as .ifdef. To get here, the character
805 * after .if must have been taken literally, so the argument cannot
806 * be empty - even if it contained a variable expansion.
807 */
808 t = ToToken(doEval && EvalBare(par, arg));
809 free(arg);
810 return t;
811 }
812
813 /* Return the next token or comparison result from the parser. */
814 static Token
815 CondParser_Token(CondParser *par, bool doEval)
816 {
817 Token t;
818
819 t = par->curr;
820 if (t != TOK_NONE) {
821 par->curr = TOK_NONE;
822 return t;
823 }
824
825 cpp_skip_hspace(&par->p);
826
827 switch (par->p[0]) {
828
829 case '(':
830 par->p++;
831 return TOK_LPAREN;
832
833 case ')':
834 par->p++;
835 return TOK_RPAREN;
836
837 case '|':
838 par->p++;
839 if (par->p[0] == '|')
840 par->p++;
841 else if (opts.strict) {
842 Parse_Error(PARSE_FATAL, "Unknown operator '|'");
843 par->printedError = true;
844 return TOK_ERROR;
845 }
846 return TOK_OR;
847
848 case '&':
849 par->p++;
850 if (par->p[0] == '&')
851 par->p++;
852 else if (opts.strict) {
853 Parse_Error(PARSE_FATAL, "Unknown operator '&'");
854 par->printedError = true;
855 return TOK_ERROR;
856 }
857 return TOK_AND;
858
859 case '!':
860 par->p++;
861 return TOK_NOT;
862
863 case '#': /* XXX: see unit-tests/cond-token-plain.mk */
864 case '\n': /* XXX: why should this end the condition? */
865 /* Probably obsolete now, from 1993-03-21. */
866 case '\0':
867 return TOK_EOF;
868
869 case '"':
870 case '$':
871 return CondParser_Comparison(par, doEval);
872
873 default:
874 if (CondParser_FuncCallEmpty(par, doEval, &t))
875 return t;
876 if (CondParser_FuncCall(par, doEval, &t))
877 return t;
878 return CondParser_ComparisonOrLeaf(par, doEval);
879 }
880 }
881
882 /* Skip the next token if it equals t. */
883 static bool
884 CondParser_Skip(CondParser *par, Token t)
885 {
886 Token actual;
887
888 actual = CondParser_Token(par, false);
889 if (actual == t)
890 return true;
891
892 assert(par->curr == TOK_NONE);
893 assert(actual != TOK_NONE);
894 par->curr = actual;
895 return false;
896 }
897
898 /*
899 * Term -> '(' Or ')'
900 * Term -> '!' Term
901 * Term -> Leaf Operator Leaf
902 * Term -> Leaf
903 */
904 static CondResult
905 CondParser_Term(CondParser *par, bool doEval)
906 {
907 CondResult res;
908 Token t;
909
910 t = CondParser_Token(par, doEval);
911 if (t == TOK_TRUE)
912 return CR_TRUE;
913 if (t == TOK_FALSE)
914 return CR_FALSE;
915
916 if (t == TOK_LPAREN) {
917 res = CondParser_Or(par, doEval);
918 if (res == CR_ERROR)
919 return CR_ERROR;
920 if (CondParser_Token(par, doEval) != TOK_RPAREN)
921 return CR_ERROR;
922 return res;
923 }
924
925 if (t == TOK_NOT) {
926 res = CondParser_Term(par, doEval);
927 if (res == CR_TRUE)
928 res = CR_FALSE;
929 else if (res == CR_FALSE)
930 res = CR_TRUE;
931 return res;
932 }
933
934 return CR_ERROR;
935 }
936
937 /*
938 * And -> Term ('&&' Term)*
939 */
940 static CondResult
941 CondParser_And(CondParser *par, bool doEval)
942 {
943 CondResult res, rhs;
944
945 res = CR_TRUE;
946 do {
947 if ((rhs = CondParser_Term(par, doEval)) == CR_ERROR)
948 return CR_ERROR;
949 if (rhs == CR_FALSE) {
950 res = CR_FALSE;
951 doEval = false;
952 }
953 } while (CondParser_Skip(par, TOK_AND));
954
955 return res;
956 }
957
958 /*
959 * Or -> And ('||' And)*
960 */
961 static CondResult
962 CondParser_Or(CondParser *par, bool doEval)
963 {
964 CondResult res, rhs;
965
966 res = CR_FALSE;
967 do {
968 if ((rhs = CondParser_And(par, doEval)) == CR_ERROR)
969 return CR_ERROR;
970 if (rhs == CR_TRUE) {
971 res = CR_TRUE;
972 doEval = false;
973 }
974 } while (CondParser_Skip(par, TOK_OR));
975
976 return res;
977 }
978
979 static CondResult
980 CondParser_Eval(CondParser *par)
981 {
982 CondResult res;
983
984 DEBUG1(COND, "CondParser_Eval: %s\n", par->p);
985
986 res = CondParser_Or(par, true);
987 if (res != CR_ERROR && CondParser_Token(par, false) != TOK_EOF)
988 return CR_ERROR;
989
990 return res;
991 }
992
993 /*
994 * Evaluate the condition, including any side effects from the variable
995 * expressions in the condition. The condition consists of &&, ||, !,
996 * function(arg), comparisons and parenthetical groupings thereof.
997 */
998 static CondResult
999 CondEvalExpression(const char *cond, bool plain,
1000 bool (*evalBare)(const char *), bool negate,
1001 bool eprint, bool leftUnquotedOK)
1002 {
1003 CondParser par;
1004 CondResult rval;
1005
1006 cpp_skip_hspace(&cond);
1007
1008 par.plain = plain;
1009 par.evalBare = evalBare;
1010 par.negateEvalBare = negate;
1011 par.leftUnquotedOK = leftUnquotedOK;
1012 par.p = cond;
1013 par.curr = TOK_NONE;
1014 par.printedError = false;
1015
1016 rval = CondParser_Eval(&par);
1017
1018 if (rval == CR_ERROR && eprint && !par.printedError)
1019 Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", cond);
1020
1021 return rval;
1022 }
1023
1024 /*
1025 * Evaluate a condition in a :? modifier, such as
1026 * ${"${VAR}" == value:?yes:no}.
1027 */
1028 CondResult
1029 Cond_EvalCondition(const char *cond)
1030 {
1031 return CondEvalExpression(cond, true,
1032 FuncDefined, false, false, true);
1033 }
1034
1035 static bool
1036 IsEndif(const char *p)
1037 {
1038 return p[0] == 'e' && p[1] == 'n' && p[2] == 'd' &&
1039 p[3] == 'i' && p[4] == 'f' && !ch_isalpha(p[5]);
1040 }
1041
1042 static bool
1043 DetermineKindOfConditional(const char **pp, bool *out_plain,
1044 bool (**out_evalBare)(const char *),
1045 bool *out_negate)
1046 {
1047 const char *p = *pp + 2;
1048
1049 *out_plain = false;
1050 *out_evalBare = FuncDefined;
1051 *out_negate = skip_string(&p, "n");
1052
1053 if (skip_string(&p, "def")) { /* .ifdef and .ifndef */
1054 } else if (skip_string(&p, "make")) /* .ifmake and .ifnmake */
1055 *out_evalBare = FuncMake;
1056 else if (!*out_negate) /* plain .if */
1057 *out_plain = true;
1058 else
1059 goto unknown_directive;
1060 if (ch_isalpha(*p))
1061 goto unknown_directive;
1062
1063 *pp = p;
1064 return true;
1065
1066 unknown_directive:
1067 /*
1068 * TODO: Add error message about unknown directive, since there is no
1069 * other known directive that starts with 'el' or 'if'.
1070 *
1071 * Example: .elifx 123
1072 */
1073 return false;
1074 }
1075
1076 /*
1077 * Evaluate the conditional directive in the line, which is one of:
1078 *
1079 * .if <cond>
1080 * .ifmake <cond>
1081 * .ifnmake <cond>
1082 * .ifdef <cond>
1083 * .ifndef <cond>
1084 * .elif <cond>
1085 * .elifmake <cond>
1086 * .elifnmake <cond>
1087 * .elifdef <cond>
1088 * .elifndef <cond>
1089 * .else
1090 * .endif
1091 *
1092 * In these directives, <cond> consists of &&, ||, !, function(arg),
1093 * comparisons, expressions, bare words, numbers and strings, and
1094 * parenthetical groupings thereof.
1095 *
1096 * Results:
1097 * CR_TRUE to continue parsing the lines that follow the
1098 * conditional (when <cond> evaluates to true)
1099 * CR_FALSE to skip the lines after the conditional
1100 * (when <cond> evaluates to false, or when a previous
1101 * branch has already been taken)
1102 * CR_ERROR if the conditional was not valid, either because of
1103 * a syntax error or because some variable was undefined
1104 * or because the condition could not be evaluated
1105 */
1106 CondResult
1107 Cond_EvalLine(const char *line)
1108 {
1109 typedef enum IfState {
1110
1111 /* None of the previous <cond> evaluated to true. */
1112 IFS_INITIAL = 0,
1113
1114 /*
1115 * The previous <cond> evaluated to true. The lines following
1116 * this condition are interpreted.
1117 */
1118 IFS_ACTIVE = 1 << 0,
1119
1120 /* The previous directive was an '.else'. */
1121 IFS_SEEN_ELSE = 1 << 1,
1122
1123 /* One of the previous <cond> evaluated to true. */
1124 IFS_WAS_ACTIVE = 1 << 2
1125
1126 } IfState;
1127
1128 static enum IfState *cond_states = NULL;
1129 static unsigned int cond_states_cap = 128;
1130
1131 bool plain;
1132 bool (*evalBare)(const char *);
1133 bool negate;
1134 bool isElif;
1135 CondResult res;
1136 IfState state;
1137 const char *p = line;
1138
1139 if (cond_states == NULL) {
1140 cond_states = bmake_malloc(
1141 cond_states_cap * sizeof *cond_states);
1142 cond_states[0] = IFS_ACTIVE;
1143 }
1144
1145 p++; /* skip the leading '.' */
1146 cpp_skip_hspace(&p);
1147
1148 if (IsEndif(p)) { /* It is an '.endif'. */
1149 if (p[5] != '\0') {
1150 Parse_Error(PARSE_FATAL,
1151 "The .endif directive does not take arguments");
1152 }
1153
1154 if (cond_depth == cond_min_depth) {
1155 Parse_Error(PARSE_FATAL, "if-less endif");
1156 return CR_TRUE;
1157 }
1158
1159 /* Return state for previous conditional */
1160 cond_depth--;
1161 return cond_states[cond_depth] & IFS_ACTIVE
1162 ? CR_TRUE : CR_FALSE;
1163 }
1164
1165 /* Parse the name of the directive, such as 'if', 'elif', 'endif'. */
1166 if (p[0] == 'e') {
1167 if (p[1] != 'l') {
1168 /*
1169 * Unknown directive. It might still be a
1170 * transformation rule like '.err.txt',
1171 * therefore no error message here.
1172 */
1173 return CR_ERROR;
1174 }
1175
1176 /* Quite likely this is 'else' or 'elif' */
1177 p += 2;
1178 if (strncmp(p, "se", 2) == 0 && !ch_isalpha(p[2])) {
1179 if (p[2] != '\0')
1180 Parse_Error(PARSE_FATAL,
1181 "The .else directive "
1182 "does not take arguments");
1183
1184 if (cond_depth == cond_min_depth) {
1185 Parse_Error(PARSE_FATAL, "if-less else");
1186 return CR_TRUE;
1187 }
1188
1189 state = cond_states[cond_depth];
1190 if (state == IFS_INITIAL) {
1191 state = IFS_ACTIVE | IFS_SEEN_ELSE;
1192 } else {
1193 if (state & IFS_SEEN_ELSE)
1194 Parse_Error(PARSE_WARNING,
1195 "extra else");
1196 state = IFS_WAS_ACTIVE | IFS_SEEN_ELSE;
1197 }
1198 cond_states[cond_depth] = state;
1199
1200 return state & IFS_ACTIVE ? CR_TRUE : CR_FALSE;
1201 }
1202 /* Assume for now it is an elif */
1203 isElif = true;
1204 } else
1205 isElif = false;
1206
1207 if (p[0] != 'i' || p[1] != 'f') {
1208 /*
1209 * Unknown directive. It might still be a transformation rule
1210 * like '.elisp.scm', therefore no error message here.
1211 */
1212 return CR_ERROR; /* Not an ifxxx or elifxxx line */
1213 }
1214
1215 if (!DetermineKindOfConditional(&p, &plain, &evalBare, &negate))
1216 return CR_ERROR;
1217
1218 if (isElif) {
1219 if (cond_depth == cond_min_depth) {
1220 Parse_Error(PARSE_FATAL, "if-less elif");
1221 return CR_TRUE;
1222 }
1223 state = cond_states[cond_depth];
1224 if (state & IFS_SEEN_ELSE) {
1225 Parse_Error(PARSE_WARNING, "extra elif");
1226 cond_states[cond_depth] =
1227 IFS_WAS_ACTIVE | IFS_SEEN_ELSE;
1228 return CR_FALSE;
1229 }
1230 if (state != IFS_INITIAL) {
1231 cond_states[cond_depth] = IFS_WAS_ACTIVE;
1232 return CR_FALSE;
1233 }
1234 } else {
1235 /* Normal .if */
1236 if (cond_depth + 1 >= cond_states_cap) {
1237 /*
1238 * This is rare, but not impossible.
1239 * In meta mode, dirdeps.mk (only runs at level 0)
1240 * can need more than the default.
1241 */
1242 cond_states_cap += 32;
1243 cond_states = bmake_realloc(cond_states,
1244 cond_states_cap * sizeof *cond_states);
1245 }
1246 state = cond_states[cond_depth];
1247 cond_depth++;
1248 if (!(state & IFS_ACTIVE)) {
1249 /*
1250 * If we aren't parsing the data,
1251 * treat as always false.
1252 */
1253 cond_states[cond_depth] = IFS_WAS_ACTIVE;
1254 return CR_FALSE;
1255 }
1256 }
1257
1258 /* And evaluate the conditional expression */
1259 res = CondEvalExpression(p, plain, evalBare, negate, true, false);
1260 if (res == CR_ERROR) {
1261 /* Syntax error, error message already output. */
1262 /* Skip everything to the matching '.endif'. */
1263 /* An extra '.else' is not detected in this case. */
1264 cond_states[cond_depth] = IFS_WAS_ACTIVE;
1265 return CR_FALSE;
1266 }
1267
1268 cond_states[cond_depth] = res == CR_TRUE ? IFS_ACTIVE : IFS_INITIAL;
1269 return res;
1270 }
1271
1272 void
1273 Cond_restore_depth(unsigned int saved_depth)
1274 {
1275 unsigned int open_conds = cond_depth - cond_min_depth;
1276
1277 if (open_conds != 0 || saved_depth > cond_depth) {
1278 Parse_Error(PARSE_FATAL, "%u open conditional%s",
1279 open_conds, open_conds == 1 ? "" : "s");
1280 cond_depth = cond_min_depth;
1281 }
1282
1283 cond_min_depth = saved_depth;
1284 }
1285
1286 unsigned int
1287 Cond_save_depth(void)
1288 {
1289 unsigned int depth = cond_min_depth;
1290
1291 cond_min_depth = cond_depth;
1292 return depth;
1293 }
1294