| @@ -1,14 +1,14 @@ | | | @@ -1,14 +1,14 @@ |
1 | /* $NetBSD: cond.c,v 1.275 2021/09/21 21:43:32 rillig Exp $ */ | | 1 | /* $NetBSD: cond.c,v 1.276 2021/09/21 22:38:25 rillig Exp $ */ |
2 | | | 2 | |
3 | /* | | 3 | /* |
4 | * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. | | 4 | * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. |
5 | * All rights reserved. | | 5 | * All rights reserved. |
6 | * | | 6 | * |
7 | * This code is derived from software contributed to Berkeley by | | 7 | * This code is derived from software contributed to Berkeley by |
8 | * Adam de Boor. | | 8 | * Adam de Boor. |
9 | * | | 9 | * |
10 | * Redistribution and use in source and binary forms, with or without | | 10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions | | 11 | * modification, are permitted provided that the following conditions |
12 | * are met: | | 12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright | | 13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. | | 14 | * notice, this list of conditions and the following disclaimer. |
| @@ -85,27 +85,27 @@ | | | @@ -85,27 +85,27 @@ |
85 | * Cond_restore_depth | | 85 | * Cond_restore_depth |
86 | * Save and restore the nesting of the conditions, at | | 86 | * Save and restore the nesting of the conditions, at |
87 | * the start and end of including another makefile, to | | 87 | * the start and end of including another makefile, to |
88 | * ensure that in each makefile the conditional | | 88 | * ensure that in each makefile the conditional |
89 | * directives are well-balanced. | | 89 | * directives are well-balanced. |
90 | */ | | 90 | */ |
91 | | | 91 | |
92 | #include <errno.h> | | 92 | #include <errno.h> |
93 | | | 93 | |
94 | #include "make.h" | | 94 | #include "make.h" |
95 | #include "dir.h" | | 95 | #include "dir.h" |
96 | | | 96 | |
97 | /* "@(#)cond.c 8.2 (Berkeley) 1/2/94" */ | | 97 | /* "@(#)cond.c 8.2 (Berkeley) 1/2/94" */ |
98 | MAKE_RCSID("$NetBSD: cond.c,v 1.275 2021/09/21 21:43:32 rillig Exp $"); | | 98 | MAKE_RCSID("$NetBSD: cond.c,v 1.276 2021/09/21 22:38:25 rillig Exp $"); |
99 | | | 99 | |
100 | /* | | 100 | /* |
101 | * The parsing of conditional expressions is based on this grammar: | | 101 | * The parsing of conditional expressions is based on this grammar: |
102 | * Or -> And | | 102 | * Or -> And |
103 | * Or -> Or '||' And | | 103 | * Or -> Or '||' And |
104 | * And -> Term | | 104 | * And -> Term |
105 | * And -> And '&&' Term | | 105 | * And -> And '&&' Term |
106 | * Term -> Function '(' Argument ')' | | 106 | * Term -> Function '(' Argument ')' |
107 | * Term -> Leaf Operator Leaf | | 107 | * Term -> Leaf Operator Leaf |
108 | * Term -> Leaf | | 108 | * Term -> Leaf |
109 | * Term -> '(' Or ')' | | 109 | * Term -> '(' Or ')' |
110 | * Term -> '!' Term | | 110 | * Term -> '!' Term |
111 | * Leaf -> "string" | | 111 | * Leaf -> "string" |
| @@ -144,58 +144,60 @@ typedef enum ComparisonOp { | | | @@ -144,58 +144,60 @@ typedef enum ComparisonOp { |
144 | typedef struct CondParser { | | 144 | typedef struct CondParser { |
145 | | | 145 | |
146 | /* | | 146 | /* |
147 | * The plain '.if ${VAR}' evaluates to true if the value of the | | 147 | * The plain '.if ${VAR}' evaluates to true if the value of the |
148 | * expression has length > 0. The other '.if' variants delegate | | 148 | * expression has length > 0. The other '.if' variants delegate |
149 | * to evalBare instead. | | 149 | * to evalBare instead. |
150 | */ | | 150 | */ |
151 | bool plain; | | 151 | bool plain; |
152 | | | 152 | |
153 | /* The function to apply on unquoted bare words. */ | | 153 | /* The function to apply on unquoted bare words. */ |
154 | bool (*evalBare)(size_t, const char *); | | 154 | bool (*evalBare)(size_t, const char *); |
155 | bool negateEvalBare; | | 155 | bool negateEvalBare; |
156 | | | 156 | |
| | | 157 | /* |
| | | 158 | * Whether the left-hand side of a comparison may NOT 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 lhsStrict; |
| | | 170 | |
157 | const char *p; /* The remaining condition to parse */ | | 171 | const char *p; /* The remaining condition to parse */ |
158 | Token curr; /* Single push-back token used in parsing */ | | 172 | Token curr; /* Single push-back token used in parsing */ |
159 | | | 173 | |
160 | /* | | 174 | /* |
161 | * Whether an error message has already been printed for this | | 175 | * Whether an error message has already been printed for this |
162 | * condition. The first available error message is usually the most | | 176 | * condition. The first available error message is usually the most |
163 | * specific one, therefore it makes sense to suppress the standard | | 177 | * specific one, therefore it makes sense to suppress the standard |
164 | * "Malformed conditional" message. | | 178 | * "Malformed conditional" message. |
165 | */ | | 179 | */ |
166 | bool printedError; | | 180 | bool printedError; |
167 | } CondParser; | | 181 | } CondParser; |
168 | | | 182 | |
169 | static CondResult CondParser_Or(CondParser *par, bool); | | 183 | static CondResult CondParser_Or(CondParser *par, bool); |
170 | | | 184 | |
171 | static unsigned int cond_depth = 0; /* current .if nesting level */ | | 185 | static unsigned int cond_depth = 0; /* current .if nesting level */ |
172 | static unsigned int cond_min_depth = 0; /* depth at makefile open */ | | 186 | static unsigned int cond_min_depth = 0; /* depth at makefile open */ |
173 | | | 187 | |
174 | /* Names for ComparisonOp. */ | | 188 | /* Names for ComparisonOp. */ |
175 | static const char *opname[] = { "<", "<=", ">", ">=", "==", "!=" }; | | 189 | static const char *opname[] = { "<", "<=", ">", ">=", "==", "!=" }; |
176 | | | 190 | |
177 | /* | | | |
178 | * Indicate when we should be strict about lhs of comparisons. | | | |
179 | * In strict mode, the lhs must be a variable expression or a string literal | | | |
180 | * in quotes. In non-strict mode it may also be an unquoted string literal. | | | |
181 | * | | | |
182 | * True when CondEvalExpression is called from Cond_EvalLine (.if etc). | | | |
183 | * False when CondEvalExpression is called from ApplyModifier_IfElse | | | |
184 | * since lhs is already expanded, and at that point we cannot tell if | | | |
185 | * it was a variable reference or not. | | | |
186 | */ | | | |
187 | static bool lhsStrict; | | | |
188 | | | | |
189 | static bool | | 191 | static bool |
190 | is_token(const char *str, const char *tok, size_t len) | | 192 | is_token(const char *str, const char *tok, size_t len) |
191 | { | | 193 | { |
192 | return strncmp(str, tok, len) == 0 && !ch_isalpha(str[len]); | | 194 | return strncmp(str, tok, len) == 0 && !ch_isalpha(str[len]); |
193 | } | | 195 | } |
194 | | | 196 | |
195 | static Token | | 197 | static Token |
196 | ToToken(bool cond) | | 198 | ToToken(bool cond) |
197 | { | | 199 | { |
198 | return cond ? TOK_TRUE : TOK_FALSE; | | 200 | return cond ? TOK_TRUE : TOK_FALSE; |
199 | } | | 201 | } |
200 | | | 202 | |
201 | /* Push back the most recent token read. We only need one level of this. */ | | 203 | /* Push back the most recent token read. We only need one level of this. */ |
| @@ -672,27 +674,27 @@ length_1: | | | @@ -672,27 +674,27 @@ length_1: |
672 | * 0 | | 674 | * 0 |
673 | * ${VAR:Mpattern} | | 675 | * ${VAR:Mpattern} |
674 | * ${VAR} == value | | 676 | * ${VAR} == value |
675 | * ${VAR:U0} < 12345 | | 677 | * ${VAR:U0} < 12345 |
676 | */ | | 678 | */ |
677 | static Token | | 679 | static Token |
678 | CondParser_Comparison(CondParser *par, bool doEval) | | 680 | CondParser_Comparison(CondParser *par, bool doEval) |
679 | { | | 681 | { |
680 | Token t = TOK_ERROR; | | 682 | Token t = TOK_ERROR; |
681 | FStr lhs, rhs; | | 683 | FStr lhs, rhs; |
682 | ComparisonOp op; | | 684 | ComparisonOp op; |
683 | bool lhsQuoted, rhsQuoted; | | 685 | bool lhsQuoted, rhsQuoted; |
684 | | | 686 | |
685 | CondParser_Leaf(par, doEval, lhsStrict, &lhs, &lhsQuoted); | | 687 | CondParser_Leaf(par, doEval, par->lhsStrict, &lhs, &lhsQuoted); |
686 | if (lhs.str == NULL) | | 688 | if (lhs.str == NULL) |
687 | goto done_lhs; | | 689 | goto done_lhs; |
688 | | | 690 | |
689 | CondParser_SkipWhitespace(par); | | 691 | CondParser_SkipWhitespace(par); |
690 | | | 692 | |
691 | if (!CondParser_ComparisonOp(par, &op)) { | | 693 | if (!CondParser_ComparisonOp(par, &op)) { |
692 | /* Unknown operator, compare against an empty string or 0. */ | | 694 | /* Unknown operator, compare against an empty string or 0. */ |
693 | t = ToToken(doEval && EvalNotEmpty(par, lhs.str, lhsQuoted)); | | 695 | t = ToToken(doEval && EvalNotEmpty(par, lhs.str, lhsQuoted)); |
694 | goto done_lhs; | | 696 | goto done_lhs; |
695 | } | | 697 | } |
696 | | | 698 | |
697 | CondParser_SkipWhitespace(par); | | 699 | CondParser_SkipWhitespace(par); |
698 | | | 700 | |
| @@ -1053,33 +1055,32 @@ CondParser_Eval(CondParser *par, bool *o | | | @@ -1053,33 +1055,32 @@ CondParser_Eval(CondParser *par, bool *o |
1053 | * COND_PARSE if the condition was valid grammatically | | 1055 | * COND_PARSE if the condition was valid grammatically |
1054 | * COND_INVALID if not a valid conditional. | | 1056 | * COND_INVALID if not a valid conditional. |
1055 | * | | 1057 | * |
1056 | * (*value) is set to the boolean value of the condition | | 1058 | * (*value) is set to the boolean value of the condition |
1057 | */ | | 1059 | */ |
1058 | static CondEvalResult | | 1060 | static CondEvalResult |
1059 | CondEvalExpression(const char *cond, bool *out_value, bool plain, | | 1061 | CondEvalExpression(const char *cond, bool *out_value, bool plain, |
1060 | bool (*evalBare)(size_t, const char *), bool negate, | | 1062 | bool (*evalBare)(size_t, const char *), bool negate, |
1061 | bool eprint, bool strictLHS) | | 1063 | bool eprint, bool strictLHS) |
1062 | { | | 1064 | { |
1063 | CondParser par; | | 1065 | CondParser par; |
1064 | CondEvalResult rval; | | 1066 | CondEvalResult rval; |
1065 | | | 1067 | |
1066 | lhsStrict = strictLHS; | | | |
1067 | | | | |
1068 | cpp_skip_hspace(&cond); | | 1068 | cpp_skip_hspace(&cond); |
1069 | | | 1069 | |
1070 | par.plain = plain; | | 1070 | par.plain = plain; |
1071 | par.evalBare = evalBare; | | 1071 | par.evalBare = evalBare; |
1072 | par.negateEvalBare = negate; | | 1072 | par.negateEvalBare = negate; |
| | | 1073 | par.lhsStrict = strictLHS; |
1073 | par.p = cond; | | 1074 | par.p = cond; |
1074 | par.curr = TOK_NONE; | | 1075 | par.curr = TOK_NONE; |
1075 | par.printedError = false; | | 1076 | par.printedError = false; |
1076 | | | 1077 | |
1077 | rval = CondParser_Eval(&par, out_value); | | 1078 | rval = CondParser_Eval(&par, out_value); |
1078 | | | 1079 | |
1079 | if (rval == COND_INVALID && eprint && !par.printedError) | | 1080 | if (rval == COND_INVALID && eprint && !par.printedError) |
1080 | Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", cond); | | 1081 | Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", cond); |
1081 | | | 1082 | |
1082 | return rval; | | 1083 | return rval; |
1083 | } | | 1084 | } |
1084 | | | 1085 | |
1085 | /* | | 1086 | /* |