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