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