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