cond.c revision 1.354 1 /* $NetBSD: cond.c,v 1.354 2023/08/11 04:56:31 rillig Exp $ */
2
3 /*
4 * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Adam de Boor.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35 /*
36 * Copyright (c) 1988, 1989 by Adam de Boor
37 * Copyright (c) 1989 by Berkeley Softworks
38 * All rights reserved.
39 *
40 * This code is derived from software contributed to Berkeley by
41 * Adam de Boor.
42 *
43 * Redistribution and use in source and binary forms, with or without
44 * modification, are permitted provided that the following conditions
45 * are met:
46 * 1. Redistributions of source code must retain the above copyright
47 * notice, this list of conditions and the following disclaimer.
48 * 2. Redistributions in binary form must reproduce the above copyright
49 * notice, this list of conditions and the following disclaimer in the
50 * documentation and/or other materials provided with the distribution.
51 * 3. All advertising materials mentioning features or use of this software
52 * must display the following acknowledgement:
53 * This product includes software developed by the University of
54 * California, Berkeley and its contributors.
55 * 4. Neither the name of the University nor the names of its contributors
56 * may be used to endorse or promote products derived from this software
57 * without specific prior written permission.
58 *
59 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
60 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
61 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
62 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
63 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
64 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
65 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
66 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
67 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
68 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
69 * SUCH DAMAGE.
70 */
71
72 /*
73 * Handling of conditionals in a makefile.
74 *
75 * Interface:
76 * Cond_EvalLine Evaluate the conditional directive, such as
77 * '.if <cond>', '.elifnmake <cond>', '.else', '.endif'.
78 *
79 * Cond_EvalCondition
80 * Evaluate the conditional, which is either the argument
81 * of one of the .if directives or the condition in a
82 * ':?then:else' variable modifier.
83 *
84 * Cond_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.354 2023/08/11 04:56:31 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 and is not numerically zero. The other
140 * '.if' variants delegate to evalBare instead, for example '.ifdef
141 * ${VAR}' is equivalent to '.if defined(${VAR})', checking whether
142 * the variable named by the 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 *, 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 VarEvalMode emode = doEval
228 ? VARE_UNDEFERR
229 : VARE_PARSE_ONLY;
230 /*
231 * TODO: make Var_Parse complain about undefined
232 * variables.
233 */
234 FStr nestedVal = Var_Parse(&p, SCOPE_CMDLINE, emode);
235 /* TODO: handle errors */
236 Buf_AddStr(&word, nestedVal.str);
237 FStr_Done(&nestedVal);
238 continue;
239 }
240 if (ch == '(')
241 paren_depth++;
242 else if (ch == ')' && --paren_depth < 0)
243 break;
244 Buf_AddByte(&word, ch);
245 p++;
246 }
247
248 cpp_skip_hspace(&p);
249 *pp = p;
250
251 return Buf_DoneData(&word);
252 }
253
254 /* Parse the function argument, including the surrounding parentheses. */
255 static char *
256 ParseFuncArg(CondParser *par, const char **pp, bool doEval, const char *func)
257 {
258 const char *p = *pp;
259 char *res;
260
261 p++; /* Skip opening '(' - verified by caller */
262 cpp_skip_hspace(&p);
263 res = ParseWord(&p, doEval);
264 cpp_skip_hspace(&p);
265
266 if (*p++ != ')') {
267 int len = 0;
268 while (ch_isalpha(func[len]))
269 len++;
270
271 Parse_Error(PARSE_FATAL,
272 "Missing closing parenthesis for %.*s()", len, func);
273 par->printedError = true;
274 free(res);
275 return NULL;
276 }
277
278 *pp = p;
279 return res;
280 }
281
282 /* See if the given variable is defined. */
283 static bool
284 FuncDefined(const char *var)
285 {
286 return Var_Exists(SCOPE_CMDLINE, var);
287 }
288
289 /* See if a target matching targetPattern is requested to be made. */
290 static bool
291 FuncMake(const char *targetPattern)
292 {
293 StringListNode *ln;
294 bool warned = false;
295
296 for (ln = opts.create.first; ln != NULL; ln = ln->next) {
297 StrMatchResult res = Str_Match(ln->datum, targetPattern);
298 if (res.error != NULL && !warned) {
299 warned = true;
300 Parse_Error(PARSE_WARNING,
301 "%s in pattern argument '%s' to function 'make'",
302 res.error, targetPattern);
303 }
304 if (res.matched)
305 return true;
306 }
307 return false;
308 }
309
310 /* See if the given file exists. */
311 static bool
312 FuncExists(const char *file)
313 {
314 bool result;
315 char *path;
316
317 path = Dir_FindFile(file, &dirSearchPath);
318 DEBUG2(COND, "exists(%s) result is \"%s\"\n",
319 file, path != NULL ? path : "");
320 result = path != NULL;
321 free(path);
322 return result;
323 }
324
325 /* See if the given node exists and is an actual target. */
326 static bool
327 FuncTarget(const char *node)
328 {
329 GNode *gn = Targ_FindNode(node);
330 return gn != NULL && GNode_IsTarget(gn);
331 }
332
333 /*
334 * See if the given node exists and is an actual target with commands
335 * associated with it.
336 */
337 static bool
338 FuncCommands(const char *node)
339 {
340 GNode *gn = Targ_FindNode(node);
341 return gn != NULL && GNode_IsTarget(gn) &&
342 !Lst_IsEmpty(&gn->commands);
343 }
344
345 /*
346 * Convert the string to a floating point number. Accepted formats are
347 * base-10 integer, base-16 integer and finite floating point numbers.
348 */
349 static bool
350 TryParseNumber(const char *str, double *out_value)
351 {
352 char *end;
353 unsigned long ul_val;
354 double dbl_val;
355
356 if (str[0] == '\0') { /* XXX: why is an empty string a number? */
357 *out_value = 0.0;
358 return true;
359 }
360
361 errno = 0;
362 ul_val = strtoul(str, &end, str[1] == 'x' ? 16 : 10);
363 if (*end == '\0' && errno != ERANGE) {
364 *out_value = str[0] == '-' ? -(double)-ul_val : (double)ul_val;
365 return true;
366 }
367
368 if (*end != '\0' && *end != '.' && *end != 'e' && *end != 'E')
369 return false; /* skip the expensive strtod call */
370 dbl_val = strtod(str, &end);
371 if (*end != '\0')
372 return false;
373
374 *out_value = dbl_val;
375 return true;
376 }
377
378 static bool
379 is_separator(char ch)
380 {
381 return ch == '\0' || ch_isspace(ch) || ch == '!' || ch == '=' ||
382 ch == '>' || ch == '<' || ch == ')' /* but not '(' */;
383 }
384
385 /*
386 * In a quoted or unquoted string literal or a number, parse a variable
387 * expression and add its value to the buffer.
388 *
389 * Return whether to continue parsing the leaf.
390 *
391 * Example: .if x${CENTER}y == "${PREFIX}${SUFFIX}" || 0x${HEX}
392 */
393 static bool
394 CondParser_StringExpr(CondParser *par, const char *start,
395 bool doEval, bool quoted,
396 Buffer *buf, FStr *inout_str)
397 {
398 VarEvalMode emode;
399 const char *p;
400 bool atStart;
401
402 emode = doEval && quoted ? VARE_WANTRES
403 : doEval ? VARE_UNDEFERR
404 : VARE_PARSE_ONLY;
405
406 p = par->p;
407 atStart = p == start;
408 *inout_str = Var_Parse(&p, SCOPE_CMDLINE, emode);
409 /* TODO: handle errors */
410 if (inout_str->str == var_Error) {
411 FStr_Done(inout_str);
412 *inout_str = FStr_InitRefer(NULL);
413 return false;
414 }
415 par->p = p;
416
417 /*
418 * If the '$' started the string literal (which means no quotes), and
419 * the expression is followed by a space, a comparison operator or
420 * the end of the expression, we are done.
421 */
422 if (atStart && is_separator(par->p[0]))
423 return false;
424
425 Buf_AddStr(buf, inout_str->str);
426 FStr_Done(inout_str);
427 *inout_str = FStr_InitRefer(NULL); /* not finished yet */
428 return true;
429 }
430
431 /*
432 * Parse a string from a variable expression or an optionally quoted string,
433 * on the left-hand and right-hand sides of comparisons.
434 *
435 * Results:
436 * Returns the string without any enclosing quotes, or NULL on error.
437 * Sets out_quoted if the leaf was a quoted string literal.
438 */
439 static void
440 CondParser_Leaf(CondParser *par, bool doEval, bool unquotedOK,
441 FStr *out_str, bool *out_quoted)
442 {
443 Buffer buf;
444 FStr str;
445 bool quoted;
446 const char *start;
447
448 Buf_Init(&buf);
449 str = FStr_InitRefer(NULL);
450 *out_quoted = quoted = par->p[0] == '"';
451 start = par->p;
452 if (quoted)
453 par->p++;
454
455 while (par->p[0] != '\0' && str.str == NULL) {
456 switch (par->p[0]) {
457 case '\\':
458 par->p++;
459 if (par->p[0] != '\0') {
460 Buf_AddByte(&buf, par->p[0]);
461 par->p++;
462 }
463 continue;
464 case '"':
465 par->p++;
466 if (quoted)
467 goto return_buf; /* skip the closing quote */
468 Buf_AddByte(&buf, '"');
469 continue;
470 case ')': /* see is_separator */
471 case '!':
472 case '=':
473 case '>':
474 case '<':
475 case ' ':
476 case '\t':
477 if (!quoted)
478 goto return_buf;
479 Buf_AddByte(&buf, par->p[0]);
480 par->p++;
481 continue;
482 case '$':
483 if (!CondParser_StringExpr(par,
484 start, doEval, quoted, &buf, &str))
485 goto return_str;
486 continue;
487 default:
488 if (!unquotedOK && !quoted && *start != '$' &&
489 !ch_isdigit(*start)) {
490 /*
491 * The left-hand side must be quoted,
492 * a variable expression or a number.
493 */
494 str = FStr_InitRefer(NULL);
495 goto return_str;
496 }
497 Buf_AddByte(&buf, par->p[0]);
498 par->p++;
499 continue;
500 }
501 }
502 return_buf:
503 str = FStr_InitOwn(buf.data);
504 buf.data = NULL;
505 return_str:
506 Buf_Done(&buf);
507 *out_str = str;
508 }
509
510 /*
511 * Evaluate a "comparison without operator", such as in ".if ${VAR}" or
512 * ".if 0".
513 */
514 static bool
515 EvalTruthy(CondParser *par, const char *value, bool quoted)
516 {
517 double num;
518
519 /* For .ifxxx "...", check for non-empty string. */
520 if (quoted)
521 return value[0] != '\0';
522
523 /* For .ifxxx <number>, compare against zero */
524 if (TryParseNumber(value, &num))
525 return num != 0.0;
526
527 /*
528 * For .if ${...}, check for non-empty string. This is different
529 * from the evaluation function from that .if variant, which would
530 * test whether a variable of the given name were defined.
531 */
532 /*
533 * XXX: Whitespace should count as empty, just as in
534 * CondParser_FuncCallEmpty.
535 */
536 if (par->plain)
537 return value[0] != '\0';
538
539 return par->evalBare(value) != par->negateEvalBare;
540 }
541
542 /* Evaluate a numerical comparison, such as in ".if ${VAR} >= 9". */
543 static bool
544 EvalCompareNum(double lhs, ComparisonOp op, double rhs)
545 {
546 DEBUG3(COND, "Comparing %f %s %f\n", lhs, opname[op], rhs);
547
548 switch (op) {
549 case LT:
550 return lhs < rhs;
551 case LE:
552 return lhs <= rhs;
553 case GT:
554 return lhs > rhs;
555 case GE:
556 return lhs >= rhs;
557 case EQ:
558 return lhs == rhs;
559 default:
560 return lhs != rhs;
561 }
562 }
563
564 static Token
565 EvalCompareStr(CondParser *par, const char *lhs,
566 ComparisonOp op, const char *rhs)
567 {
568 if (op != EQ && op != NE) {
569 Parse_Error(PARSE_FATAL,
570 "Comparison with '%s' requires both operands "
571 "'%s' and '%s' to be numeric",
572 opname[op], lhs, rhs);
573 par->printedError = true;
574 return TOK_ERROR;
575 }
576
577 DEBUG3(COND, "Comparing \"%s\" %s \"%s\"\n", lhs, opname[op], rhs);
578 return ToToken((op == EQ) == (strcmp(lhs, rhs) == 0));
579 }
580
581 /* Evaluate a comparison, such as "${VAR} == 12345". */
582 static Token
583 EvalCompare(CondParser *par, const char *lhs, bool lhsQuoted,
584 ComparisonOp op, const char *rhs, bool rhsQuoted)
585 {
586 double left, right;
587
588 if (!rhsQuoted && !lhsQuoted)
589 if (TryParseNumber(lhs, &left) && TryParseNumber(rhs, &right))
590 return ToToken(EvalCompareNum(left, op, right));
591
592 return EvalCompareStr(par, lhs, op, rhs);
593 }
594
595 static bool
596 CondParser_ComparisonOp(CondParser *par, ComparisonOp *out_op)
597 {
598 const char *p = par->p;
599
600 if (p[0] == '<' && p[1] == '=')
601 return par->p += 2, *out_op = LE, true;
602 if (p[0] == '<')
603 return par->p += 1, *out_op = LT, true;
604 if (p[0] == '>' && p[1] == '=')
605 return par->p += 2, *out_op = GE, true;
606 if (p[0] == '>')
607 return par->p += 1, *out_op = GT, true;
608 if (p[0] == '=' && p[1] == '=')
609 return par->p += 2, *out_op = EQ, true;
610 if (p[0] == '!' && p[1] == '=')
611 return par->p += 2, *out_op = NE, true;
612 return false;
613 }
614
615 /*
616 * Parse a comparison condition such as:
617 *
618 * 0
619 * ${VAR:Mpattern}
620 * ${VAR} == value
621 * ${VAR:U0} < 12345
622 */
623 static Token
624 CondParser_Comparison(CondParser *par, bool doEval)
625 {
626 Token t = TOK_ERROR;
627 FStr lhs, rhs;
628 ComparisonOp op;
629 bool lhsQuoted, rhsQuoted;
630
631 CondParser_Leaf(par, doEval, par->leftUnquotedOK, &lhs, &lhsQuoted);
632 if (lhs.str == NULL)
633 goto done_lhs;
634
635 CondParser_SkipWhitespace(par);
636
637 if (!CondParser_ComparisonOp(par, &op)) {
638 /* Unknown operator, compare against an empty string or 0. */
639 t = ToToken(doEval && EvalTruthy(par, lhs.str, lhsQuoted));
640 goto done_lhs;
641 }
642
643 CondParser_SkipWhitespace(par);
644
645 if (par->p[0] == '\0') {
646 Parse_Error(PARSE_FATAL,
647 "Missing right-hand side of operator '%s'", opname[op]);
648 par->printedError = true;
649 goto done_lhs;
650 }
651
652 CondParser_Leaf(par, doEval, true, &rhs, &rhsQuoted);
653 t = rhs.str == NULL ? TOK_ERROR
654 : !doEval ? TOK_FALSE
655 : EvalCompare(par, lhs.str, lhsQuoted, op, rhs.str, rhsQuoted);
656 FStr_Done(&rhs);
657
658 done_lhs:
659 FStr_Done(&lhs);
660 return t;
661 }
662
663 /*
664 * The argument to empty() is a variable name, optionally followed by
665 * variable modifiers.
666 */
667 static bool
668 CondParser_FuncCallEmpty(CondParser *par, bool doEval, Token *out_token)
669 {
670 const char *cp = par->p;
671 Token tok;
672 FStr val;
673
674 if (!skip_string(&cp, "empty"))
675 return false;
676
677 cpp_skip_whitespace(&cp);
678 if (*cp != '(')
679 return false;
680
681 cp--; /* Make cp[1] point to the '('. */
682 val = Var_Parse(&cp, SCOPE_CMDLINE,
683 doEval ? VARE_WANTRES : VARE_PARSE_ONLY);
684 /* TODO: handle errors */
685
686 if (val.str == var_Error)
687 tok = TOK_ERROR;
688 else {
689 cpp_skip_whitespace(&val.str);
690 tok = ToToken(doEval && val.str[0] == '\0');
691 }
692
693 FStr_Done(&val);
694 *out_token = tok;
695 par->p = cp;
696 return true;
697 }
698
699 /* Parse a function call expression, such as 'exists(${file})'. */
700 static bool
701 CondParser_FuncCall(CondParser *par, bool doEval, Token *out_token)
702 {
703 char *arg;
704 const char *p = par->p;
705 bool (*fn)(const char *);
706 const char *fn_name = p;
707
708 if (skip_string(&p, "defined"))
709 fn = FuncDefined;
710 else if (skip_string(&p, "make"))
711 fn = FuncMake;
712 else if (skip_string(&p, "exists"))
713 fn = FuncExists;
714 else if (skip_string(&p, "target"))
715 fn = FuncTarget;
716 else if (skip_string(&p, "commands"))
717 fn = FuncCommands;
718 else
719 return false;
720
721 cpp_skip_whitespace(&p);
722 if (*p != '(')
723 return false;
724
725 arg = ParseFuncArg(par, &p, doEval, fn_name);
726 *out_token = ToToken(doEval &&
727 arg != NULL && arg[0] != '\0' && fn(arg));
728 free(arg);
729
730 par->p = p;
731 return true;
732 }
733
734 /*
735 * Parse a comparison that neither starts with '"' nor '$', such as the
736 * unusual 'bare == right' or '3 == ${VAR}', or a simple leaf without
737 * operator, which is a number, a variable expression or a string literal.
738 *
739 * TODO: Can this be merged into CondParser_Comparison?
740 */
741 static Token
742 CondParser_ComparisonOrLeaf(CondParser *par, bool doEval)
743 {
744 Token t;
745 char *arg;
746 const char *cp;
747
748 /* Push anything numeric through the compare expression */
749 cp = par->p;
750 if (ch_isdigit(cp[0]) || cp[0] == '-' || cp[0] == '+')
751 return CondParser_Comparison(par, doEval);
752
753 /*
754 * Most likely we have a naked token to apply the default function to.
755 * However ".if a == b" gets here when the "a" is unquoted and doesn't
756 * start with a '$'. This surprises people.
757 * If what follows the function argument is a '=' or '!' then the
758 * syntax would be invalid if we did "defined(a)" - so instead treat
759 * as an expression.
760 */
761 /*
762 * XXX: In edge cases, a variable expression may be evaluated twice,
763 * see cond-token-plain.mk, keyword 'twice'.
764 */
765 arg = ParseWord(&cp, doEval);
766 assert(arg[0] != '\0');
767
768 if (*cp == '=' || *cp == '!' || *cp == '<' || *cp == '>')
769 return CondParser_Comparison(par, doEval);
770 par->p = cp;
771
772 /*
773 * Evaluate the argument using the default function.
774 * This path always treats .if as .ifdef. To get here, the character
775 * after .if must have been taken literally, so the argument cannot
776 * be empty - even if it contained a variable expansion.
777 */
778 t = ToToken(doEval && par->evalBare(arg) != par->negateEvalBare);
779 free(arg);
780 return t;
781 }
782
783 /* Return the next token or comparison result from the parser. */
784 static Token
785 CondParser_Token(CondParser *par, bool doEval)
786 {
787 Token t;
788
789 t = par->curr;
790 if (t != TOK_NONE) {
791 par->curr = TOK_NONE;
792 return t;
793 }
794
795 cpp_skip_hspace(&par->p);
796
797 switch (par->p[0]) {
798
799 case '(':
800 par->p++;
801 return TOK_LPAREN;
802
803 case ')':
804 par->p++;
805 return TOK_RPAREN;
806
807 case '|':
808 par->p++;
809 if (par->p[0] == '|')
810 par->p++;
811 else if (opts.strict) {
812 Parse_Error(PARSE_FATAL, "Unknown operator '|'");
813 par->printedError = true;
814 return TOK_ERROR;
815 }
816 return TOK_OR;
817
818 case '&':
819 par->p++;
820 if (par->p[0] == '&')
821 par->p++;
822 else if (opts.strict) {
823 Parse_Error(PARSE_FATAL, "Unknown operator '&'");
824 par->printedError = true;
825 return TOK_ERROR;
826 }
827 return TOK_AND;
828
829 case '!':
830 par->p++;
831 return TOK_NOT;
832
833 case '#': /* XXX: see unit-tests/cond-token-plain.mk */
834 case '\n': /* XXX: why should this end the condition? */
835 /* Probably obsolete now, from 1993-03-21. */
836 case '\0':
837 return TOK_EOF;
838
839 case '"':
840 case '$':
841 return CondParser_Comparison(par, doEval);
842
843 default:
844 if (CondParser_FuncCallEmpty(par, doEval, &t))
845 return t;
846 if (CondParser_FuncCall(par, doEval, &t))
847 return t;
848 return CondParser_ComparisonOrLeaf(par, doEval);
849 }
850 }
851
852 /* Skip the next token if it equals t. */
853 static bool
854 CondParser_Skip(CondParser *par, Token t)
855 {
856 Token actual;
857
858 actual = CondParser_Token(par, false);
859 if (actual == t)
860 return true;
861
862 assert(par->curr == TOK_NONE);
863 assert(actual != TOK_NONE);
864 par->curr = actual;
865 return false;
866 }
867
868 /*
869 * Term -> '(' Or ')'
870 * Term -> '!' Term
871 * Term -> Leaf Operator Leaf
872 * Term -> Leaf
873 */
874 static CondResult
875 CondParser_Term(CondParser *par, bool doEval)
876 {
877 CondResult res;
878 Token t;
879
880 t = CondParser_Token(par, doEval);
881 if (t == TOK_TRUE)
882 return CR_TRUE;
883 if (t == TOK_FALSE)
884 return CR_FALSE;
885
886 if (t == TOK_LPAREN) {
887 res = CondParser_Or(par, doEval);
888 if (res == CR_ERROR)
889 return CR_ERROR;
890 if (CondParser_Token(par, doEval) != TOK_RPAREN)
891 return CR_ERROR;
892 return res;
893 }
894
895 if (t == TOK_NOT) {
896 res = CondParser_Term(par, doEval);
897 if (res == CR_TRUE)
898 res = CR_FALSE;
899 else if (res == CR_FALSE)
900 res = CR_TRUE;
901 return res;
902 }
903
904 return CR_ERROR;
905 }
906
907 /*
908 * And -> Term ('&&' Term)*
909 */
910 static CondResult
911 CondParser_And(CondParser *par, bool doEval)
912 {
913 CondResult res, rhs;
914
915 res = CR_TRUE;
916 do {
917 if ((rhs = CondParser_Term(par, doEval)) == CR_ERROR)
918 return CR_ERROR;
919 if (rhs == CR_FALSE) {
920 res = CR_FALSE;
921 doEval = false;
922 }
923 } while (CondParser_Skip(par, TOK_AND));
924
925 return res;
926 }
927
928 /*
929 * Or -> And ('||' And)*
930 */
931 static CondResult
932 CondParser_Or(CondParser *par, bool doEval)
933 {
934 CondResult res, rhs;
935
936 res = CR_FALSE;
937 do {
938 if ((rhs = CondParser_And(par, doEval)) == CR_ERROR)
939 return CR_ERROR;
940 if (rhs == CR_TRUE) {
941 res = CR_TRUE;
942 doEval = false;
943 }
944 } while (CondParser_Skip(par, TOK_OR));
945
946 return res;
947 }
948
949 static CondResult
950 CondParser_Eval(CondParser *par)
951 {
952 CondResult res;
953
954 DEBUG1(COND, "CondParser_Eval: %s\n", par->p);
955
956 res = CondParser_Or(par, true);
957 if (res != CR_ERROR && CondParser_Token(par, false) != TOK_EOF)
958 return CR_ERROR;
959
960 return res;
961 }
962
963 /*
964 * Evaluate the condition, including any side effects from the variable
965 * expressions in the condition. The condition consists of &&, ||, !,
966 * function(arg), comparisons and parenthetical groupings thereof.
967 */
968 static CondResult
969 CondEvalExpression(const char *cond, bool plain,
970 bool (*evalBare)(const char *), bool negate,
971 bool eprint, bool leftUnquotedOK)
972 {
973 CondParser par;
974 CondResult rval;
975
976 cpp_skip_hspace(&cond);
977
978 par.plain = plain;
979 par.evalBare = evalBare;
980 par.negateEvalBare = negate;
981 par.leftUnquotedOK = leftUnquotedOK;
982 par.p = cond;
983 par.curr = TOK_NONE;
984 par.printedError = false;
985
986 rval = CondParser_Eval(&par);
987
988 if (rval == CR_ERROR && eprint && !par.printedError)
989 Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", cond);
990
991 return rval;
992 }
993
994 /*
995 * Evaluate a condition in a :? modifier, such as
996 * ${"${VAR}" == value:?yes:no}.
997 */
998 CondResult
999 Cond_EvalCondition(const char *cond)
1000 {
1001 return CondEvalExpression(cond, true,
1002 FuncDefined, false, false, true);
1003 }
1004
1005 static bool
1006 IsEndif(const char *p)
1007 {
1008 return p[0] == 'e' && p[1] == 'n' && p[2] == 'd' &&
1009 p[3] == 'i' && p[4] == 'f' && !ch_isalpha(p[5]);
1010 }
1011
1012 static bool
1013 DetermineKindOfConditional(const char **pp, bool *out_plain,
1014 bool (**out_evalBare)(const char *),
1015 bool *out_negate)
1016 {
1017 const char *p = *pp + 2;
1018
1019 *out_plain = false;
1020 *out_evalBare = FuncDefined;
1021 *out_negate = skip_string(&p, "n");
1022
1023 if (skip_string(&p, "def")) { /* .ifdef and .ifndef */
1024 } else if (skip_string(&p, "make")) /* .ifmake and .ifnmake */
1025 *out_evalBare = FuncMake;
1026 else if (!*out_negate) /* plain .if */
1027 *out_plain = true;
1028 else
1029 goto unknown_directive;
1030 if (ch_isalpha(*p))
1031 goto unknown_directive;
1032
1033 *pp = p;
1034 return true;
1035
1036 unknown_directive:
1037 /*
1038 * TODO: Add error message about unknown directive, since there is no
1039 * other known directive that starts with 'el' or 'if'.
1040 *
1041 * Example: .elifx 123
1042 */
1043 return false;
1044 }
1045
1046 /*
1047 * Evaluate the conditional directive in the line, which is one of:
1048 *
1049 * .if <cond>
1050 * .ifmake <cond>
1051 * .ifnmake <cond>
1052 * .ifdef <cond>
1053 * .ifndef <cond>
1054 * .elif <cond>
1055 * .elifmake <cond>
1056 * .elifnmake <cond>
1057 * .elifdef <cond>
1058 * .elifndef <cond>
1059 * .else
1060 * .endif
1061 *
1062 * In these directives, <cond> consists of &&, ||, !, function(arg),
1063 * comparisons, expressions, bare words, numbers and strings, and
1064 * parenthetical groupings thereof.
1065 *
1066 * Results:
1067 * CR_TRUE to continue parsing the lines that follow the
1068 * conditional (when <cond> evaluates to true)
1069 * CR_FALSE to skip the lines after the conditional
1070 * (when <cond> evaluates to false, or when a previous
1071 * branch has already been taken)
1072 * CR_ERROR if the conditional was not valid, either because of
1073 * a syntax error or because some variable was undefined
1074 * or because the condition could not be evaluated
1075 */
1076 CondResult
1077 Cond_EvalLine(const char *line)
1078 {
1079 typedef enum IfState {
1080
1081 /* None of the previous <cond> evaluated to true. */
1082 IFS_INITIAL = 0,
1083
1084 /*
1085 * The previous <cond> evaluated to true. The lines following
1086 * this condition are interpreted.
1087 */
1088 IFS_ACTIVE = 1 << 0,
1089
1090 /* The previous directive was an '.else'. */
1091 IFS_SEEN_ELSE = 1 << 1,
1092
1093 /* One of the previous <cond> evaluated to true. */
1094 IFS_WAS_ACTIVE = 1 << 2
1095
1096 } IfState;
1097
1098 static enum IfState *cond_states = NULL;
1099 static unsigned int cond_states_cap = 128;
1100
1101 bool plain;
1102 bool (*evalBare)(const char *);
1103 bool negate;
1104 bool isElif;
1105 CondResult res;
1106 IfState state;
1107 const char *p = line;
1108
1109 if (cond_states == NULL) {
1110 cond_states = bmake_malloc(
1111 cond_states_cap * sizeof *cond_states);
1112 cond_states[0] = IFS_ACTIVE;
1113 }
1114
1115 p++; /* skip the leading '.' */
1116 cpp_skip_hspace(&p);
1117
1118 if (IsEndif(p)) { /* It is an '.endif'. */
1119 if (p[5] != '\0') {
1120 Parse_Error(PARSE_FATAL,
1121 "The .endif directive does not take arguments");
1122 }
1123
1124 if (cond_depth == CurFile_CondMinDepth()) {
1125 Parse_Error(PARSE_FATAL, "if-less endif");
1126 return CR_TRUE;
1127 }
1128
1129 /* Return state for previous conditional */
1130 cond_depth--;
1131 Parse_GuardEndif();
1132 return cond_states[cond_depth] & IFS_ACTIVE
1133 ? CR_TRUE : CR_FALSE;
1134 }
1135
1136 /* Parse the name of the directive, such as 'if', 'elif', 'endif'. */
1137 if (p[0] == 'e') {
1138 if (p[1] != 'l') {
1139 /*
1140 * Unknown directive. It might still be a
1141 * transformation rule like '.err.txt',
1142 * therefore no error message here.
1143 */
1144 return CR_ERROR;
1145 }
1146
1147 /* Quite likely this is 'else' or 'elif' */
1148 p += 2;
1149 if (strncmp(p, "se", 2) == 0 && !ch_isalpha(p[2])) {
1150 if (p[2] != '\0')
1151 Parse_Error(PARSE_FATAL,
1152 "The .else directive "
1153 "does not take arguments");
1154
1155 if (cond_depth == CurFile_CondMinDepth()) {
1156 Parse_Error(PARSE_FATAL, "if-less else");
1157 return CR_TRUE;
1158 }
1159 Parse_GuardElse();
1160
1161 state = cond_states[cond_depth];
1162 if (state == IFS_INITIAL) {
1163 state = IFS_ACTIVE | IFS_SEEN_ELSE;
1164 } else {
1165 if (state & IFS_SEEN_ELSE)
1166 Parse_Error(PARSE_WARNING,
1167 "extra else");
1168 state = IFS_WAS_ACTIVE | IFS_SEEN_ELSE;
1169 }
1170 cond_states[cond_depth] = state;
1171
1172 return state & IFS_ACTIVE ? CR_TRUE : CR_FALSE;
1173 }
1174 /* Assume for now it is an elif */
1175 isElif = true;
1176 } else
1177 isElif = false;
1178
1179 if (p[0] != 'i' || p[1] != 'f') {
1180 /*
1181 * Unknown directive. It might still be a transformation rule
1182 * like '.elisp.scm', therefore no error message here.
1183 */
1184 return CR_ERROR; /* Not an ifxxx or elifxxx line */
1185 }
1186
1187 if (!DetermineKindOfConditional(&p, &plain, &evalBare, &negate))
1188 return CR_ERROR;
1189
1190 if (isElif) {
1191 if (cond_depth == CurFile_CondMinDepth()) {
1192 Parse_Error(PARSE_FATAL, "if-less elif");
1193 return CR_TRUE;
1194 }
1195 Parse_GuardElse();
1196 state = cond_states[cond_depth];
1197 if (state & IFS_SEEN_ELSE) {
1198 Parse_Error(PARSE_WARNING, "extra elif");
1199 cond_states[cond_depth] =
1200 IFS_WAS_ACTIVE | IFS_SEEN_ELSE;
1201 return CR_FALSE;
1202 }
1203 if (state != IFS_INITIAL) {
1204 cond_states[cond_depth] = IFS_WAS_ACTIVE;
1205 return CR_FALSE;
1206 }
1207 } else {
1208 /* Normal .if */
1209 if (cond_depth + 1 >= cond_states_cap) {
1210 /*
1211 * This is rare, but not impossible.
1212 * In meta mode, dirdeps.mk (only runs at level 0)
1213 * can need more than the default.
1214 */
1215 cond_states_cap += 32;
1216 cond_states = bmake_realloc(cond_states,
1217 cond_states_cap * sizeof *cond_states);
1218 }
1219 state = cond_states[cond_depth];
1220 cond_depth++;
1221 if (!(state & IFS_ACTIVE)) {
1222 /*
1223 * If we aren't parsing the data,
1224 * treat as always false.
1225 */
1226 cond_states[cond_depth] = IFS_WAS_ACTIVE;
1227 return CR_FALSE;
1228 }
1229 }
1230
1231 /* And evaluate the conditional expression */
1232 res = CondEvalExpression(p, plain, evalBare, negate, true, false);
1233 if (res == CR_ERROR) {
1234 /* Syntax error, error message already output. */
1235 /* Skip everything to the matching '.endif'. */
1236 /* An extra '.else' is not detected in this case. */
1237 cond_states[cond_depth] = IFS_WAS_ACTIVE;
1238 return CR_FALSE;
1239 }
1240
1241 cond_states[cond_depth] = res == CR_TRUE ? IFS_ACTIVE : IFS_INITIAL;
1242 return res;
1243 }
1244
1245 static bool
1246 ParseVarnameGuard(const char **pp, const char **varname)
1247 {
1248 const char *p = *pp;
1249
1250 if (ch_isalpha(*p) || *p == '_') {
1251 while (ch_isalnum(*p) || *p == '_')
1252 p++;
1253 *varname = *pp;
1254 *pp = p;
1255 return true;
1256 }
1257 return false;
1258 }
1259
1260 /* Extracts the multiple-inclusion guard from a conditional, if any. */
1261 Guard *
1262 Cond_ExtractGuard(const char *line)
1263 {
1264 const char *p, *varname;
1265 Substring dir;
1266 Guard *guard;
1267
1268 p = line + 1; /* skip the '.' */
1269 cpp_skip_hspace(&p);
1270
1271 dir.start = p;
1272 while (ch_isalpha(*p))
1273 p++;
1274 dir.end = p;
1275 cpp_skip_hspace(&p);
1276
1277 if (Substring_Equals(dir, "if")) {
1278 if (skip_string(&p, "!defined(")) {
1279 if (ParseVarnameGuard(&p, &varname)
1280 && strcmp(p, ")") == 0)
1281 goto found_variable;
1282 } else if (skip_string(&p, "!target(")) {
1283 const char *arg_p = p;
1284 free(ParseWord(&p, false));
1285 if (strcmp(p, ")") == 0) {
1286 guard = bmake_malloc(sizeof(*guard));
1287 guard->kind = GK_TARGET;
1288 guard->name = ParseWord(&arg_p, true);
1289 return guard;
1290 }
1291 }
1292 } else if (Substring_Equals(dir, "ifndef")) {
1293 if (ParseVarnameGuard(&p, &varname) && *p == '\0')
1294 goto found_variable;
1295 }
1296 return NULL;
1297
1298 found_variable:
1299 guard = bmake_malloc(sizeof(*guard));
1300 guard->kind = GK_VARIABLE;
1301 guard->name = bmake_strsedup(varname, p);
1302 return guard;
1303 }
1304
1305 void
1306 Cond_EndFile(void)
1307 {
1308 unsigned int open_conds = cond_depth - CurFile_CondMinDepth();
1309
1310 if (open_conds != 0) {
1311 Parse_Error(PARSE_FATAL, "%u open conditional%s",
1312 open_conds, open_conds == 1 ? "" : "s");
1313 cond_depth = CurFile_CondMinDepth();
1314 }
1315 }
1316