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