make: clean up comments, code and testsdiff -r1.362 -r1.363 src/usr.bin/make/cond.c
(rillig)
--- src/usr.bin/make/cond.c 2024/02/07 07:21:22 1.362
+++ src/usr.bin/make/cond.c 2024/04/23 22:51:28 1.363
@@ -1,1246 +1,1245 @@ | @@ -1,1246 +1,1245 @@ | |||
1 | /* $NetBSD: cond.c,v 1.362 2024/02/07 07:21:22 rillig Exp $ */ | 1 | /* $NetBSD: cond.c,v 1.363 2024/04/23 22:51:28 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. | |
15 | * 2. Redistributions in binary form must reproduce the above copyright | 15 | * 2. Redistributions in binary form must reproduce the above copyright | |
16 | * notice, this list of conditions and the following disclaimer in the | 16 | * notice, this list of conditions and the following disclaimer in the | |
17 | * documentation and/or other materials provided with the distribution. | 17 | * documentation and/or other materials provided with the distribution. | |
18 | * 3. Neither the name of the University nor the names of its contributors | 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 | 19 | * may be used to endorse or promote products derived from this software | |
20 | * without specific prior written permission. | 20 | * without specific prior written permission. | |
21 | * | 21 | * | |
22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | 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 | 23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | 25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | 27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | 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 | 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 | 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 | 31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
32 | * SUCH DAMAGE. | 32 | * SUCH DAMAGE. | |
33 | */ | 33 | */ | |
34 | 34 | |||
35 | /* | 35 | /* | |
36 | * Copyright (c) 1988, 1989 by Adam de Boor | 36 | * Copyright (c) 1988, 1989 by Adam de Boor | |
37 | * Copyright (c) 1989 by Berkeley Softworks | 37 | * Copyright (c) 1989 by Berkeley Softworks | |
38 | * All rights reserved. | 38 | * All rights reserved. | |
39 | * | 39 | * | |
40 | * This code is derived from software contributed to Berkeley by | 40 | * This code is derived from software contributed to Berkeley by | |
41 | * Adam de Boor. | 41 | * Adam de Boor. | |
42 | * | 42 | * | |
43 | * Redistribution and use in source and binary forms, with or without | 43 | * Redistribution and use in source and binary forms, with or without | |
44 | * modification, are permitted provided that the following conditions | 44 | * modification, are permitted provided that the following conditions | |
45 | * are met: | 45 | * are met: | |
46 | * 1. Redistributions of source code must retain the above copyright | 46 | * 1. Redistributions of source code must retain the above copyright | |
47 | * notice, this list of conditions and the following disclaimer. | 47 | * notice, this list of conditions and the following disclaimer. | |
48 | * 2. Redistributions in binary form must reproduce the above copyright | 48 | * 2. Redistributions in binary form must reproduce the above copyright | |
49 | * notice, this list of conditions and the following disclaimer in the | 49 | * notice, this list of conditions and the following disclaimer in the | |
50 | * documentation and/or other materials provided with the distribution. | 50 | * documentation and/or other materials provided with the distribution. | |
51 | * 3. All advertising materials mentioning features or use of this software | 51 | * 3. All advertising materials mentioning features or use of this software | |
52 | * must display the following acknowledgement: | 52 | * must display the following acknowledgement: | |
53 | * This product includes software developed by the University of | 53 | * This product includes software developed by the University of | |
54 | * California, Berkeley and its contributors. | 54 | * California, Berkeley and its contributors. | |
55 | * 4. Neither the name of the University nor the names of 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 | 56 | * may be used to endorse or promote products derived from this software | |
57 | * without specific prior written permission. | 57 | * without specific prior written permission. | |
58 | * | 58 | * | |
59 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | 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 | 60 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
61 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 61 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
62 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | 62 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
63 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 63 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
64 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | 64 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
65 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | 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 | 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 | 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 | 68 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
69 | * SUCH DAMAGE. | 69 | * SUCH DAMAGE. | |
70 | */ | 70 | */ | |
71 | 71 | |||
72 | /* | 72 | /* | |
73 | * Handling of conditionals in a makefile. | 73 | * Handling of conditionals in a makefile. | |
74 | * | 74 | * | |
75 | * Interface: | 75 | * Interface: | |
76 | * Cond_EvalLine Evaluate the conditional directive, such as | 76 | * Cond_EvalLine Evaluate the conditional directive, such as | |
77 | * '.if <cond>', '.elifnmake <cond>', '.else', '.endif'. | 77 | * '.if <cond>', '.elifnmake <cond>', '.else', '.endif'. | |
78 | * | 78 | * | |
79 | * Cond_EvalCondition | 79 | * Cond_EvalCondition | |
80 | * Evaluate the conditional, which is either the argument | 80 | * Evaluate the conditional, which is either the argument | |
81 | * of one of the .if directives or the condition in a | 81 | * of one of the .if directives or the condition in a | |
82 | * ':?then:else' variable modifier. | 82 | * ':?then:else' variable modifier. | |
83 | * | 83 | * | |
84 | * Cond_EndFile At the end of reading a makefile, ensure that the | 84 | * Cond_EndFile At the end of reading a makefile, ensure that the | |
85 | * conditional directives are well-balanced. | 85 | * conditional directives are well-balanced. | |
86 | */ | 86 | */ | |
87 | 87 | |||
88 | #include <errno.h> | 88 | #include <errno.h> | |
89 | 89 | |||
90 | #include "make.h" | 90 | #include "make.h" | |
91 | #include "dir.h" | 91 | #include "dir.h" | |
92 | 92 | |||
93 | /* "@(#)cond.c 8.2 (Berkeley) 1/2/94" */ | 93 | /* "@(#)cond.c 8.2 (Berkeley) 1/2/94" */ | |
94 | MAKE_RCSID("$NetBSD: cond.c,v 1.362 2024/02/07 07:21:22 rillig Exp $"); | 94 | MAKE_RCSID("$NetBSD: cond.c,v 1.363 2024/04/23 22:51:28 rillig Exp $"); | |
95 | 95 | |||
96 | /* | 96 | /* | |
97 | * Conditional expressions conform to this grammar: | 97 | * Conditional expressions conform to this grammar: | |
98 | * Or -> And ('||' And)* | 98 | * Or -> And ('||' And)* | |
99 | * And -> Term ('&&' Term)* | 99 | * And -> Term ('&&' Term)* | |
100 | * Term -> Function '(' Argument ')' | 100 | * Term -> Function '(' Argument ')' | |
101 | * Term -> Leaf Operator Leaf | 101 | * Term -> Leaf Operator Leaf | |
102 | * Term -> Leaf | 102 | * Term -> Leaf | |
103 | * Term -> '(' Or ')' | 103 | * Term -> '(' Or ')' | |
104 | * Term -> '!' Term | 104 | * Term -> '!' Term | |
105 | * Leaf -> "string" | 105 | * Leaf -> "string" | |
106 | * Leaf -> Number | 106 | * Leaf -> Number | |
107 | * Leaf -> VariableExpression | 107 | * Leaf -> VariableExpression | |
108 | * Leaf -> BareWord | 108 | * Leaf -> BareWord | |
109 | * Operator -> '==' | '!=' | '>' | '<' | '>=' | '<=' | 109 | * Operator -> '==' | '!=' | '>' | '<' | '>=' | '<=' | |
110 | * | 110 | * | |
111 | * BareWord is an unquoted string literal, its evaluation depends on the kind | 111 | * BareWord is an unquoted string literal, its evaluation depends on the kind | |
112 | * of '.if' directive. | 112 | * of '.if' directive. | |
113 | * | 113 | * | |
114 | * The tokens are scanned by CondParser_Token, which returns: | 114 | * The tokens are scanned by CondParser_Token, which returns: | |
115 | * TOK_AND for '&&' | 115 | * TOK_AND for '&&' | |
116 | * TOK_OR for '||' | 116 | * TOK_OR for '||' | |
117 | * TOK_NOT for '!' | 117 | * TOK_NOT for '!' | |
118 | * TOK_LPAREN for '(' | 118 | * TOK_LPAREN for '(' | |
119 | * TOK_RPAREN for ')' | 119 | * TOK_RPAREN for ')' | |
120 | * | 120 | * | |
121 | * Other terminal symbols are evaluated using either the default function or | 121 | * Other terminal symbols are evaluated using either the default function or | |
122 | * the function given in the terminal, they return either TOK_TRUE, TOK_FALSE | 122 | * the function given in the terminal, they return either TOK_TRUE, TOK_FALSE | |
123 | * or TOK_ERROR. | 123 | * or TOK_ERROR. | |
124 | */ | 124 | */ | |
125 | typedef enum Token { | 125 | typedef enum Token { | |
126 | TOK_FALSE, TOK_TRUE, TOK_AND, TOK_OR, TOK_NOT, | 126 | TOK_FALSE, TOK_TRUE, TOK_AND, TOK_OR, TOK_NOT, | |
127 | TOK_LPAREN, TOK_RPAREN, TOK_EOF, TOK_NONE, TOK_ERROR | 127 | TOK_LPAREN, TOK_RPAREN, TOK_EOF, TOK_NONE, TOK_ERROR | |
128 | } Token; | 128 | } Token; | |
129 | 129 | |||
130 | typedef enum ComparisonOp { | 130 | typedef enum ComparisonOp { | |
131 | LT, LE, GT, GE, EQ, NE | 131 | LT, LE, GT, GE, EQ, NE | |
132 | } ComparisonOp; | 132 | } ComparisonOp; | |
133 | 133 | |||
134 | typedef struct CondParser { | 134 | typedef struct CondParser { | |
135 | 135 | |||
136 | /* | 136 | /* | |
137 | * The plain '.if ${VAR}' evaluates to true if the value of the | 137 | * The plain '.if ${VAR}' evaluates to true if the value of the | |
138 | * expression has length > 0 and is not numerically zero. The other | 138 | * expression has length > 0 and is not numerically zero. The other | |
139 | * '.if' variants delegate to evalBare instead, for example '.ifdef | 139 | * '.if' variants delegate to evalBare instead, for example '.ifdef | |
140 | * ${VAR}' is equivalent to '.if defined(${VAR})', checking whether | 140 | * ${VAR}' is equivalent to '.if defined(${VAR})', checking whether | |
141 | * the variable named by the expression '${VAR}' is defined. | 141 | * the variable named by the expression '${VAR}' is defined. | |
142 | */ | 142 | */ | |
143 | bool plain; | 143 | bool plain; | |
144 | 144 | |||
145 | /* The function to apply on unquoted bare words. */ | 145 | /* The function to apply on unquoted bare words. */ | |
146 | bool (*evalBare)(const char *); | 146 | bool (*evalBare)(const char *); | |
147 | bool negateEvalBare; | 147 | bool negateEvalBare; | |
148 | 148 | |||
149 | /* | 149 | /* | |
150 | * Whether the left-hand side of a comparison may be an unquoted | 150 | * Whether the left-hand side of a comparison may be an unquoted | |
151 | * string. This is allowed for expressions of the form | 151 | * string. This is allowed for expressions of the form | |
152 | * ${condition:?:}, see ApplyModifier_IfElse. Such a condition is | 152 | * ${condition:?:}, see ApplyModifier_IfElse. Such a condition is | |
153 | * expanded before it is evaluated, due to ease of implementation. | 153 | * expanded before it is evaluated, due to ease of implementation. | |
154 | * This means that at the point where the condition is evaluated, | 154 | * This means that at the point where the condition is evaluated, | |
155 | * make cannot know anymore whether the left-hand side had originally | 155 | * make cannot know anymore whether the left-hand side had originally | |
156 | * been an expression or a plain word. | 156 | * been an expression or a plain word. | |
157 | * | 157 | * | |
158 | * In conditional directives like '.if', the left-hand side must | 158 | * In conditional directives like '.if', the left-hand side must | |
159 | * either be an expression, a quoted string or a number. | 159 | * either be an expression, a quoted string or a number. | |
160 | */ | 160 | */ | |
161 | bool leftUnquotedOK; | 161 | bool leftUnquotedOK; | |
162 | 162 | |||
163 | const char *p; /* The remaining condition to parse */ | 163 | const char *p; /* The remaining condition to parse */ | |
164 | Token curr; /* Single push-back token used in parsing */ | 164 | Token curr; /* Single push-back token used in parsing */ | |
165 | 165 | |||
166 | /* | 166 | /* | |
167 | * Whether an error message has already been printed for this | 167 | * Whether an error message has already been printed for this | |
168 | * condition. | 168 | * condition. | |
169 | */ | 169 | */ | |
170 | bool printedError; | 170 | bool printedError; | |
171 | } CondParser; | 171 | } CondParser; | |
172 | 172 | |||
173 | static CondResult CondParser_Or(CondParser *, bool); | 173 | static CondResult CondParser_Or(CondParser *, bool); | |
174 | 174 | |||
175 | unsigned int cond_depth = 0; /* current .if nesting level */ | 175 | unsigned int cond_depth = 0; /* current .if nesting level */ | |
176 | 176 | |||
177 | /* Names for ComparisonOp. */ | 177 | /* Names for ComparisonOp. */ | |
178 | static const char opname[][3] = { "<", "<=", ">", ">=", "==", "!=" }; | 178 | static const char opname[][3] = { "<", "<=", ">", ">=", "==", "!=" }; | |
179 | 179 | |||
180 | MAKE_INLINE bool | 180 | MAKE_INLINE bool | |
181 | skip_string(const char **pp, const char *str) | 181 | skip_string(const char **pp, const char *str) | |
182 | { | 182 | { | |
183 | size_t len = strlen(str); | 183 | size_t len = strlen(str); | |
184 | bool ok = strncmp(*pp, str, len) == 0; | 184 | bool ok = strncmp(*pp, str, len) == 0; | |
185 | if (ok) | 185 | if (ok) | |
186 | *pp += len; | 186 | *pp += len; | |
187 | return ok; | 187 | return ok; | |
188 | } | 188 | } | |
189 | 189 | |||
190 | static Token | 190 | static Token | |
191 | ToToken(bool cond) | 191 | ToToken(bool cond) | |
192 | { | 192 | { | |
193 | return cond ? TOK_TRUE : TOK_FALSE; | 193 | return cond ? TOK_TRUE : TOK_FALSE; | |
194 | } | 194 | } | |
195 | 195 | |||
196 | static void | 196 | static void | |
197 | CondParser_SkipWhitespace(CondParser *par) | 197 | CondParser_SkipWhitespace(CondParser *par) | |
198 | { | 198 | { | |
199 | cpp_skip_whitespace(&par->p); | 199 | cpp_skip_whitespace(&par->p); | |
200 | } | 200 | } | |
201 | 201 | |||
202 | /* | 202 | /* | |
203 | * Parse a single word, taking into account balanced parentheses as well as | 203 | * Parse a single word, taking into account balanced parentheses as well as | |
204 | * embedded expressions. Used for the argument of a built-in function as | 204 | * embedded expressions. Used for the argument of a built-in function as | |
205 | * well as for bare words, which are then passed to the default function. | 205 | * well as for bare words, which are then passed to the default function. | |
206 | */ | 206 | */ | |
207 | static char * | 207 | static char * | |
208 | ParseWord(const char **pp, bool doEval) | 208 | ParseWord(const char **pp, bool doEval) | |
209 | { | 209 | { | |
210 | const char *p = *pp; | 210 | const char *p = *pp; | |
211 | Buffer word; | 211 | Buffer word; | |
212 | int depth; | 212 | int depth; | |
213 | 213 | |||
214 | Buf_Init(&word); | 214 | Buf_Init(&word); | |
215 | 215 | |||
216 | depth = 0; | 216 | depth = 0; | |
217 | for (;;) { | 217 | for (;;) { | |
218 | char ch = *p; | 218 | char ch = *p; | |
219 | if (ch == '\0' || ch == ' ' || ch == '\t') | 219 | if (ch == '\0' || ch == ' ' || ch == '\t') | |
220 | break; | 220 | break; | |
221 | if ((ch == '&' || ch == '|') && depth == 0) | 221 | if ((ch == '&' || ch == '|') && depth == 0) | |
222 | break; | 222 | break; | |
223 | if (ch == '$') { | 223 | if (ch == '$') { | |
224 | VarEvalMode emode = doEval | 224 | VarEvalMode emode = doEval | |
225 | ? VARE_UNDEFERR | 225 | ? VARE_UNDEFERR | |
226 | : VARE_PARSE_ONLY; | 226 | : VARE_PARSE_ONLY; | |
227 | /* | 227 | /* | |
228 | * TODO: make Var_Parse complain about undefined | 228 | * TODO: make Var_Parse complain about undefined | |
229 | * variables. | 229 | * variables. | |
230 | */ | 230 | */ | |
231 | FStr nestedVal = Var_Parse(&p, SCOPE_CMDLINE, emode); | 231 | FStr nestedVal = Var_Parse(&p, SCOPE_CMDLINE, emode); | |
232 | /* TODO: handle errors */ | 232 | /* TODO: handle errors */ | |
233 | Buf_AddStr(&word, nestedVal.str); | 233 | Buf_AddStr(&word, nestedVal.str); | |
234 | FStr_Done(&nestedVal); | 234 | FStr_Done(&nestedVal); | |
235 | continue; | 235 | continue; | |
236 | } | 236 | } | |
237 | if (ch == '(') | 237 | if (ch == '(') | |
238 | depth++; | 238 | depth++; | |
239 | else if (ch == ')' && --depth < 0) | 239 | else if (ch == ')' && --depth < 0) | |
240 | break; | 240 | break; | |
241 | Buf_AddByte(&word, ch); | 241 | Buf_AddByte(&word, ch); | |
242 | p++; | 242 | p++; | |
243 | } | 243 | } | |
244 | 244 | |||
245 | cpp_skip_hspace(&p); | 245 | cpp_skip_hspace(&p); | |
246 | *pp = p; | 246 | *pp = p; | |
247 | 247 | |||
248 | return Buf_DoneData(&word); | 248 | return Buf_DoneData(&word); | |
249 | } | 249 | } | |
250 | 250 | |||
251 | /* Parse the function argument, including the surrounding parentheses. */ | 251 | /* Parse the function argument, including the surrounding parentheses. */ | |
252 | static char * | 252 | static char * | |
253 | ParseFuncArg(CondParser *par, const char **pp, bool doEval, const char *func) | 253 | ParseFuncArg(CondParser *par, const char **pp, bool doEval, const char *func) | |
254 | { | 254 | { | |
255 | const char *p = *pp; | 255 | const char *p = *pp; | |
256 | char *res; | 256 | char *res; | |
257 | 257 | |||
258 | p++; /* skip the '(' */ | 258 | p++; /* skip the '(' */ | |
259 | cpp_skip_hspace(&p); | 259 | cpp_skip_hspace(&p); | |
260 | res = ParseWord(&p, doEval); | 260 | res = ParseWord(&p, doEval); | |
261 | cpp_skip_hspace(&p); | 261 | cpp_skip_hspace(&p); | |
262 | 262 | |||
263 | if (*p++ != ')') { | 263 | if (*p++ != ')') { | |
264 | int len = 0; | 264 | int len = 0; | |
265 | while (ch_isalpha(func[len])) | 265 | while (ch_isalpha(func[len])) | |
266 | len++; | 266 | len++; | |
267 | 267 | |||
268 | Parse_Error(PARSE_FATAL, | 268 | Parse_Error(PARSE_FATAL, | |
269 | "Missing closing parenthesis for %.*s()", len, func); | 269 | "Missing closing parenthesis for %.*s()", len, func); | |
270 | par->printedError = true; | 270 | par->printedError = true; | |
271 | free(res); | 271 | free(res); | |
272 | return NULL; | 272 | return NULL; | |
273 | } | 273 | } | |
274 | 274 | |||
275 | *pp = p; | 275 | *pp = p; | |
276 | return res; | 276 | return res; | |
277 | } | 277 | } | |
278 | 278 | |||
279 | /* See if the given variable is defined. */ | 279 | /* See if the given variable is defined. */ | |
280 | static bool | 280 | static bool | |
281 | FuncDefined(const char *var) | 281 | FuncDefined(const char *var) | |
282 | { | 282 | { | |
283 | return Var_Exists(SCOPE_CMDLINE, var); | 283 | return Var_Exists(SCOPE_CMDLINE, var); | |
284 | } | 284 | } | |
285 | 285 | |||
286 | /* See if a target matching targetPattern is requested to be made. */ | 286 | /* See if a target matching targetPattern is requested to be made. */ | |
287 | static bool | 287 | static bool | |
288 | FuncMake(const char *targetPattern) | 288 | FuncMake(const char *targetPattern) | |
289 | { | 289 | { | |
290 | StringListNode *ln; | 290 | StringListNode *ln; | |
291 | bool warned = false; | 291 | bool warned = false; | |
292 | 292 | |||
293 | for (ln = opts.create.first; ln != NULL; ln = ln->next) { | 293 | for (ln = opts.create.first; ln != NULL; ln = ln->next) { | |
294 | StrMatchResult res = Str_Match(ln->datum, targetPattern); | 294 | StrMatchResult res = Str_Match(ln->datum, targetPattern); | |
295 | if (res.error != NULL && !warned) { | 295 | if (res.error != NULL && !warned) { | |
296 | warned = true; | 296 | warned = true; | |
297 | Parse_Error(PARSE_WARNING, | 297 | Parse_Error(PARSE_WARNING, | |
298 | "%s in pattern argument '%s' to function 'make'", | 298 | "%s in pattern argument '%s' to function 'make'", | |
299 | res.error, targetPattern); | 299 | res.error, targetPattern); | |
300 | } | 300 | } | |
301 | if (res.matched) | 301 | if (res.matched) | |
302 | return true; | 302 | return true; | |
303 | } | 303 | } | |
304 | return false; | 304 | return false; | |
305 | } | 305 | } | |
306 | 306 | |||
307 | /* See if the given file exists. */ | 307 | /* See if the given file exists. */ | |
308 | static bool | 308 | static bool | |
309 | FuncExists(const char *file) | 309 | FuncExists(const char *file) | |
310 | { | 310 | { | |
311 | bool result; | 311 | bool result; | |
312 | char *path; | 312 | char *path; | |
313 | 313 | |||
314 | path = Dir_FindFile(file, &dirSearchPath); | 314 | path = Dir_FindFile(file, &dirSearchPath); | |
315 | DEBUG2(COND, "exists(%s) result is \"%s\"\n", | 315 | DEBUG2(COND, "exists(%s) result is \"%s\"\n", | |
316 | file, path != NULL ? path : ""); | 316 | file, path != NULL ? path : ""); | |
317 | result = path != NULL; | 317 | result = path != NULL; | |
318 | free(path); | 318 | free(path); | |
319 | return result; | 319 | return result; | |
320 | } | 320 | } | |
321 | 321 | |||
322 | /* See if the given node exists and is an actual target. */ | 322 | /* See if the given node exists and is an actual target. */ | |
323 | static bool | 323 | static bool | |
324 | FuncTarget(const char *node) | 324 | FuncTarget(const char *node) | |
325 | { | 325 | { | |
326 | GNode *gn = Targ_FindNode(node); | 326 | GNode *gn = Targ_FindNode(node); | |
327 | return gn != NULL && GNode_IsTarget(gn); | 327 | return gn != NULL && GNode_IsTarget(gn); | |
328 | } | 328 | } | |
329 | 329 | |||
330 | /* | 330 | /* | |
331 | * See if the given node exists and is an actual target with commands | 331 | * See if the given node exists and is an actual target with commands | |
332 | * associated with it. | 332 | * associated with it. | |
333 | */ | 333 | */ | |
334 | static bool | 334 | static bool | |
335 | FuncCommands(const char *node) | 335 | FuncCommands(const char *node) | |
336 | { | 336 | { | |
337 | GNode *gn = Targ_FindNode(node); | 337 | GNode *gn = Targ_FindNode(node); | |
338 | return gn != NULL && GNode_IsTarget(gn) && | 338 | return gn != NULL && GNode_IsTarget(gn) && | |
339 | !Lst_IsEmpty(&gn->commands); | 339 | !Lst_IsEmpty(&gn->commands); | |
340 | } | 340 | } | |
341 | 341 | |||
342 | /* | 342 | /* | |
343 | * Convert the string to a floating point number. Accepted formats are | 343 | * Convert the string to a floating point number. Accepted formats are | |
344 | * base-10 integer, base-16 integer and finite floating point numbers. | 344 | * base-10 integer, base-16 integer and finite floating point numbers. | |
345 | */ | 345 | */ | |
346 | static bool | 346 | static bool | |
347 | TryParseNumber(const char *str, double *out_value) | 347 | TryParseNumber(const char *str, double *out_value) | |
348 | { | 348 | { | |
349 | char *end; | 349 | char *end; | |
350 | unsigned long ul_val; | 350 | unsigned long ul_val; | |
351 | double dbl_val; | 351 | double dbl_val; | |
352 | 352 | |||
353 | if (str[0] == '\0') { /* XXX: why is an empty string a number? */ | 353 | if (str[0] == '\0') { /* XXX: why is an empty string a number? */ | |
354 | *out_value = 0.0; | 354 | *out_value = 0.0; | |
355 | return true; | 355 | return true; | |
356 | } | 356 | } | |
357 | 357 | |||
358 | errno = 0; | 358 | errno = 0; | |
359 | ul_val = strtoul(str, &end, str[1] == 'x' ? 16 : 10); | 359 | ul_val = strtoul(str, &end, str[1] == 'x' ? 16 : 10); | |
360 | if (*end == '\0' && errno != ERANGE) { | 360 | if (*end == '\0' && errno != ERANGE) { | |
361 | *out_value = str[0] == '-' ? -(double)-ul_val : (double)ul_val; | 361 | *out_value = str[0] == '-' ? -(double)-ul_val : (double)ul_val; | |
362 | return true; | 362 | return true; | |
363 | } | 363 | } | |
364 | 364 | |||
365 | if (*end != '\0' && *end != '.' && *end != 'e' && *end != 'E') | 365 | if (*end != '\0' && *end != '.' && *end != 'e' && *end != 'E') | |
366 | return false; /* skip the expensive strtod call */ | 366 | return false; /* skip the expensive strtod call */ | |
367 | dbl_val = strtod(str, &end); | 367 | dbl_val = strtod(str, &end); | |
368 | if (*end != '\0') | 368 | if (*end != '\0') | |
369 | return false; | 369 | return false; | |
370 | 370 | |||
371 | *out_value = dbl_val; | 371 | *out_value = dbl_val; | |
372 | return true; | 372 | return true; | |
373 | } | 373 | } | |
374 | 374 | |||
375 | static bool | 375 | static bool | |
376 | is_separator(char ch) | 376 | is_separator(char ch) | |
377 | { | 377 | { | |
378 | return ch == '\0' || ch_isspace(ch) || ch == '!' || ch == '=' || | 378 | return ch == '\0' || ch_isspace(ch) || ch == '!' || ch == '=' || | |
379 | ch == '>' || ch == '<' || ch == ')' /* but not '(' */; | 379 | ch == '>' || ch == '<' || ch == ')' /* but not '(' */; | |
380 | } | 380 | } | |
381 | 381 | |||
382 | /* | 382 | /* | |
383 | * In a quoted or unquoted string literal or a number, parse an | 383 | * In a quoted or unquoted string literal or a number, parse an | |
384 | * expression and add its value to the buffer. | 384 | * expression and add its value to the buffer. | |
385 | * | 385 | * | |
386 | * Return whether to continue parsing the leaf. | 386 | * Return whether to continue parsing the leaf. | |
387 | * | 387 | * | |
388 | * Example: .if x${CENTER}y == "${PREFIX}${SUFFIX}" || 0x${HEX} | 388 | * Example: .if x${CENTER}y == "${PREFIX}${SUFFIX}" || 0x${HEX} | |
389 | */ | 389 | */ | |
390 | static bool | 390 | static bool | |
391 | CondParser_StringExpr(CondParser *par, const char *start, | 391 | CondParser_StringExpr(CondParser *par, const char *start, | |
392 | bool doEval, bool quoted, | 392 | bool doEval, bool quoted, | |
393 | Buffer *buf, FStr *inout_str) | 393 | Buffer *buf, FStr *inout_str) | |
394 | { | 394 | { | |
395 | VarEvalMode emode; | 395 | VarEvalMode emode; | |
396 | const char *p; | 396 | const char *p; | |
397 | bool atStart; /* true means an expression outside quotes */ | 397 | bool atStart; /* true means an expression outside quotes */ | |
398 | 398 | |||
399 | emode = doEval && quoted ? VARE_WANTRES | 399 | emode = doEval && quoted ? VARE_WANTRES | |
400 | : doEval ? VARE_UNDEFERR | 400 | : doEval ? VARE_UNDEFERR | |
401 | : VARE_PARSE_ONLY; | 401 | : VARE_PARSE_ONLY; | |
402 | 402 | |||
403 | p = par->p; | 403 | p = par->p; | |
404 | atStart = p == start; | 404 | atStart = p == start; | |
405 | *inout_str = Var_Parse(&p, SCOPE_CMDLINE, emode); | 405 | *inout_str = Var_Parse(&p, SCOPE_CMDLINE, emode); | |
406 | /* TODO: handle errors */ | 406 | /* TODO: handle errors */ | |
407 | if (inout_str->str == var_Error) { | 407 | if (inout_str->str == var_Error) { | |
408 | FStr_Done(inout_str); | 408 | FStr_Done(inout_str); | |
409 | *inout_str = FStr_InitRefer(NULL); | 409 | *inout_str = FStr_InitRefer(NULL); | |
410 | return false; | 410 | return false; | |
411 | } | 411 | } | |
412 | par->p = p; | 412 | par->p = p; | |
413 | 413 | |||
414 | if (atStart && is_separator(par->p[0])) | 414 | if (atStart && is_separator(par->p[0])) | |
415 | return false; | 415 | return false; | |
416 | 416 | |||
417 | Buf_AddStr(buf, inout_str->str); | 417 | Buf_AddStr(buf, inout_str->str); | |
418 | FStr_Done(inout_str); | 418 | FStr_Done(inout_str); | |
419 | *inout_str = FStr_InitRefer(NULL); /* not finished yet */ | 419 | *inout_str = FStr_InitRefer(NULL); /* not finished yet */ | |
420 | return true; | 420 | return true; | |
421 | } | 421 | } | |
422 | 422 | |||
423 | /* | 423 | /* | |
424 | * Parse a string from an expression or an optionally quoted string, | 424 | * Parse a string from an expression or an optionally quoted string, | |
425 | * on the left-hand and right-hand sides of comparisons. | 425 | * on the left-hand and right-hand sides of comparisons. | |
426 | * | 426 | * | |
427 | * Results: | 427 | * Return the string without any enclosing quotes, or NULL on error. | |
428 | * Returns the string without any enclosing quotes, or NULL on error. | 428 | * Sets out_quoted if the leaf was a quoted string literal. | |
429 | * Sets out_quoted if the leaf was a quoted string literal. | |||
430 | */ | 429 | */ | |
431 | static void | 430 | static FStr | |
432 | CondParser_Leaf(CondParser *par, bool doEval, bool unquotedOK, | 431 | CondParser_Leaf(CondParser *par, bool doEval, bool unquotedOK, | |
433 | FStr *out_str, bool *out_quoted) | 432 | bool *out_quoted) | |
434 | { | 433 | { | |
435 | Buffer buf; | 434 | Buffer buf; | |
436 | FStr str; | 435 | FStr str; | |
437 | bool quoted; | 436 | bool quoted; | |
438 | const char *start; | 437 | const char *start; | |
439 | 438 | |||
440 | Buf_Init(&buf); | 439 | Buf_Init(&buf); | |
441 | str = FStr_InitRefer(NULL); | 440 | str = FStr_InitRefer(NULL); | |
442 | *out_quoted = quoted = par->p[0] == '"'; | 441 | *out_quoted = quoted = par->p[0] == '"'; | |
443 | start = par->p; | 442 | start = par->p; | |
444 | if (quoted) | 443 | if (quoted) | |
445 | par->p++; | 444 | par->p++; | |
446 | 445 | |||
447 | while (par->p[0] != '\0' && str.str == NULL) { | 446 | while (par->p[0] != '\0' && str.str == NULL) { | |
448 | switch (par->p[0]) { | 447 | switch (par->p[0]) { | |
449 | case '\\': | 448 | case '\\': | |
450 | par->p++; | 449 | par->p++; | |
451 | if (par->p[0] != '\0') { | 450 | if (par->p[0] != '\0') { | |
452 | Buf_AddByte(&buf, par->p[0]); | 451 | Buf_AddByte(&buf, par->p[0]); | |
453 | par->p++; | 452 | par->p++; | |
454 | } | 453 | } | |
455 | continue; | 454 | continue; | |
456 | case '"': | 455 | case '"': | |
457 | par->p++; | 456 | par->p++; | |
458 | if (quoted) | 457 | if (quoted) | |
459 | goto return_buf; /* skip the closing quote */ | 458 | goto return_buf; /* skip the closing quote */ | |
460 | Buf_AddByte(&buf, '"'); | 459 | Buf_AddByte(&buf, '"'); | |
461 | continue; | 460 | continue; | |
462 | case ')': /* see is_separator */ | 461 | case ')': /* see is_separator */ | |
463 | case '!': | 462 | case '!': | |
464 | case '=': | 463 | case '=': | |
465 | case '>': | 464 | case '>': | |
466 | case '<': | 465 | case '<': | |
467 | case ' ': | 466 | case ' ': | |
468 | case '\t': | 467 | case '\t': | |
469 | if (!quoted) | 468 | if (!quoted) | |
470 | goto return_buf; | 469 | goto return_buf; | |
471 | Buf_AddByte(&buf, par->p[0]); | 470 | Buf_AddByte(&buf, par->p[0]); | |
472 | par->p++; | 471 | par->p++; | |
473 | continue; | 472 | continue; | |
474 | case '$': | 473 | case '$': | |
475 | if (!CondParser_StringExpr(par, | 474 | if (!CondParser_StringExpr(par, | |
476 | start, doEval, quoted, &buf, &str)) | 475 | start, doEval, quoted, &buf, &str)) | |
477 | goto return_str; | 476 | goto return_str; | |
478 | continue; | 477 | continue; | |
479 | default: | 478 | default: | |
480 | if (!unquotedOK && !quoted && *start != '$' && | 479 | if (!unquotedOK && !quoted && *start != '$' && | |
481 | !ch_isdigit(*start)) { | 480 | !ch_isdigit(*start)) { | |
482 | str = FStr_InitRefer(NULL); | 481 | str = FStr_InitRefer(NULL); | |
483 | goto return_str; | 482 | goto return_str; | |
484 | } | 483 | } | |
485 | Buf_AddByte(&buf, par->p[0]); | 484 | Buf_AddByte(&buf, par->p[0]); | |
486 | par->p++; | 485 | par->p++; | |
487 | continue; | 486 | continue; | |
488 | } | 487 | } | |
489 | } | 488 | } | |
490 | return_buf: | 489 | return_buf: | |
491 | str = FStr_InitOwn(buf.data); | 490 | str = FStr_InitOwn(buf.data); | |
492 | buf.data = NULL; | 491 | buf.data = NULL; | |
493 | return_str: | 492 | return_str: | |
494 | Buf_Done(&buf); | 493 | Buf_Done(&buf); | |
495 | *out_str = str; | 494 | return str; | |
496 | } | 495 | } | |
497 | 496 | |||
498 | /* | 497 | /* | |
499 | * Evaluate a "comparison without operator", such as in ".if ${VAR}" or | 498 | * Evaluate a "comparison without operator", such as in ".if ${VAR}" or | |
500 | * ".if 0". | 499 | * ".if 0". | |
501 | */ | 500 | */ | |
502 | static bool | 501 | static bool | |
503 | EvalTruthy(CondParser *par, const char *value, bool quoted) | 502 | EvalTruthy(CondParser *par, const char *value, bool quoted) | |
504 | { | 503 | { | |
505 | double num; | 504 | double num; | |
506 | 505 | |||
507 | if (quoted) | 506 | if (quoted) | |
508 | return value[0] != '\0'; | 507 | return value[0] != '\0'; | |
509 | if (TryParseNumber(value, &num)) | 508 | if (TryParseNumber(value, &num)) | |
510 | return num != 0.0; | 509 | return num != 0.0; | |
511 | if (par->plain) | 510 | if (par->plain) | |
512 | return value[0] != '\0'; | 511 | return value[0] != '\0'; | |
513 | return par->evalBare(value) != par->negateEvalBare; | 512 | return par->evalBare(value) != par->negateEvalBare; | |
514 | } | 513 | } | |
515 | 514 | |||
516 | /* Evaluate a numerical comparison, such as in ".if ${VAR} >= 9". */ | 515 | /* Evaluate a numerical comparison, such as in ".if ${VAR} >= 9". */ | |
517 | static bool | 516 | static bool | |
518 | EvalCompareNum(double lhs, ComparisonOp op, double rhs) | 517 | EvalCompareNum(double lhs, ComparisonOp op, double rhs) | |
519 | { | 518 | { | |
520 | DEBUG3(COND, "Comparing %f %s %f\n", lhs, opname[op], rhs); | 519 | DEBUG3(COND, "Comparing %f %s %f\n", lhs, opname[op], rhs); | |
521 | 520 | |||
522 | switch (op) { | 521 | switch (op) { | |
523 | case LT: | 522 | case LT: | |
524 | return lhs < rhs; | 523 | return lhs < rhs; | |
525 | case LE: | 524 | case LE: | |
526 | return lhs <= rhs; | 525 | return lhs <= rhs; | |
527 | case GT: | 526 | case GT: | |
528 | return lhs > rhs; | 527 | return lhs > rhs; | |
529 | case GE: | 528 | case GE: | |
530 | return lhs >= rhs; | 529 | return lhs >= rhs; | |
531 | case EQ: | 530 | case EQ: | |
532 | return lhs == rhs; | 531 | return lhs == rhs; | |
533 | default: | 532 | default: | |
534 | return lhs != rhs; | 533 | return lhs != rhs; | |
535 | } | 534 | } | |
536 | } | 535 | } | |
537 | 536 | |||
538 | static Token | 537 | static Token | |
539 | EvalCompareStr(CondParser *par, const char *lhs, | 538 | EvalCompareStr(CondParser *par, const char *lhs, | |
540 | ComparisonOp op, const char *rhs) | 539 | ComparisonOp op, const char *rhs) | |
541 | { | 540 | { | |
542 | if (op != EQ && op != NE) { | 541 | if (op != EQ && op != NE) { | |
543 | Parse_Error(PARSE_FATAL, | 542 | Parse_Error(PARSE_FATAL, | |
544 | "Comparison with '%s' requires both operands " | 543 | "Comparison with '%s' requires both operands " | |
545 | "'%s' and '%s' to be numeric", | 544 | "'%s' and '%s' to be numeric", | |
546 | opname[op], lhs, rhs); | 545 | opname[op], lhs, rhs); | |
547 | par->printedError = true; | 546 | par->printedError = true; | |
548 | return TOK_ERROR; | 547 | return TOK_ERROR; | |
549 | } | 548 | } | |
550 | 549 | |||
551 | DEBUG3(COND, "Comparing \"%s\" %s \"%s\"\n", lhs, opname[op], rhs); | 550 | DEBUG3(COND, "Comparing \"%s\" %s \"%s\"\n", lhs, opname[op], rhs); | |
552 | return ToToken((op == EQ) == (strcmp(lhs, rhs) == 0)); | 551 | return ToToken((op == EQ) == (strcmp(lhs, rhs) == 0)); | |
553 | } | 552 | } | |
554 | 553 | |||
555 | /* Evaluate a comparison, such as "${VAR} == 12345". */ | 554 | /* Evaluate a comparison, such as "${VAR} == 12345". */ | |
556 | static Token | 555 | static Token | |
557 | EvalCompare(CondParser *par, const char *lhs, bool lhsQuoted, | 556 | EvalCompare(CondParser *par, const char *lhs, bool lhsQuoted, | |
558 | ComparisonOp op, const char *rhs, bool rhsQuoted) | 557 | ComparisonOp op, const char *rhs, bool rhsQuoted) | |
559 | { | 558 | { | |
560 | double left, right; | 559 | double left, right; | |
561 | 560 | |||
562 | if (!rhsQuoted && !lhsQuoted) | 561 | if (!rhsQuoted && !lhsQuoted) | |
563 | if (TryParseNumber(lhs, &left) && TryParseNumber(rhs, &right)) | 562 | if (TryParseNumber(lhs, &left) && TryParseNumber(rhs, &right)) | |
564 | return ToToken(EvalCompareNum(left, op, right)); | 563 | return ToToken(EvalCompareNum(left, op, right)); | |
565 | 564 | |||
566 | return EvalCompareStr(par, lhs, op, rhs); | 565 | return EvalCompareStr(par, lhs, op, rhs); | |
567 | } | 566 | } | |
568 | 567 | |||
569 | static bool | 568 | static bool | |
570 | CondParser_ComparisonOp(CondParser *par, ComparisonOp *out_op) | 569 | CondParser_ComparisonOp(CondParser *par, ComparisonOp *out_op) | |
571 | { | 570 | { | |
572 | const char *p = par->p; | 571 | const char *p = par->p; | |
573 | 572 | |||
574 | if (p[0] == '<' && p[1] == '=') | 573 | if (p[0] == '<' && p[1] == '=') | |
575 | return par->p += 2, *out_op = LE, true; | 574 | return par->p += 2, *out_op = LE, true; | |
576 | if (p[0] == '<') | 575 | if (p[0] == '<') | |
577 | return par->p += 1, *out_op = LT, true; | 576 | return par->p += 1, *out_op = LT, true; | |
578 | if (p[0] == '>' && p[1] == '=') | 577 | if (p[0] == '>' && p[1] == '=') | |
579 | return par->p += 2, *out_op = GE, true; | 578 | return par->p += 2, *out_op = GE, true; | |
580 | if (p[0] == '>') | 579 | if (p[0] == '>') | |
581 | return par->p += 1, *out_op = GT, true; | 580 | return par->p += 1, *out_op = GT, true; | |
582 | if (p[0] == '=' && p[1] == '=') | 581 | if (p[0] == '=' && p[1] == '=') | |
583 | return par->p += 2, *out_op = EQ, true; | 582 | return par->p += 2, *out_op = EQ, true; | |
584 | if (p[0] == '!' && p[1] == '=') | 583 | if (p[0] == '!' && p[1] == '=') | |
585 | return par->p += 2, *out_op = NE, true; | 584 | return par->p += 2, *out_op = NE, true; | |
586 | return false; | 585 | return false; | |
587 | } | 586 | } | |
588 | 587 | |||
589 | /* | 588 | /* | |
590 | * Parse a comparison condition such as: | 589 | * Parse a comparison condition such as: | |
591 | * | 590 | * | |
592 | * 0 | 591 | * 0 | |
593 | * ${VAR:Mpattern} | 592 | * ${VAR:Mpattern} | |
594 | * ${VAR} == value | 593 | * ${VAR} == value | |
595 | * ${VAR:U0} < 12345 | 594 | * ${VAR:U0} < 12345 | |
596 | */ | 595 | */ | |
597 | static Token | 596 | static Token | |
598 | CondParser_Comparison(CondParser *par, bool doEval) | 597 | CondParser_Comparison(CondParser *par, bool doEval) | |
599 | { | 598 | { | |
600 | Token t = TOK_ERROR; | 599 | Token t = TOK_ERROR; | |
601 | FStr lhs, rhs; | 600 | FStr lhs, rhs; | |
602 | ComparisonOp op; | 601 | ComparisonOp op; | |
603 | bool lhsQuoted, rhsQuoted; | 602 | bool lhsQuoted, rhsQuoted; | |
604 | 603 | |||
605 | CondParser_Leaf(par, doEval, par->leftUnquotedOK, &lhs, &lhsQuoted); | 604 | lhs = CondParser_Leaf(par, doEval, par->leftUnquotedOK, &lhsQuoted); | |
606 | if (lhs.str == NULL) | 605 | if (lhs.str == NULL) | |
607 | goto done_lhs; | 606 | goto done_lhs; | |
608 | 607 | |||
609 | CondParser_SkipWhitespace(par); | 608 | CondParser_SkipWhitespace(par); | |
610 | 609 | |||
611 | if (!CondParser_ComparisonOp(par, &op)) { | 610 | if (!CondParser_ComparisonOp(par, &op)) { | |
612 | t = ToToken(doEval && EvalTruthy(par, lhs.str, lhsQuoted)); | 611 | t = ToToken(doEval && EvalTruthy(par, lhs.str, lhsQuoted)); | |
613 | goto done_lhs; | 612 | goto done_lhs; | |
614 | } | 613 | } | |
615 | 614 | |||
616 | CondParser_SkipWhitespace(par); | 615 | CondParser_SkipWhitespace(par); | |
617 | 616 | |||
618 | if (par->p[0] == '\0') { | 617 | if (par->p[0] == '\0') { | |
619 | Parse_Error(PARSE_FATAL, | 618 | Parse_Error(PARSE_FATAL, | |
620 | "Missing right-hand side of operator '%s'", opname[op]); | 619 | "Missing right-hand side of operator '%s'", opname[op]); | |
621 | par->printedError = true; | 620 | par->printedError = true; | |
622 | goto done_lhs; | 621 | goto done_lhs; | |
623 | } | 622 | } | |
624 | 623 | |||
625 | CondParser_Leaf(par, doEval, true, &rhs, &rhsQuoted); | 624 | rhs = CondParser_Leaf(par, doEval, true, &rhsQuoted); | |
626 | t = rhs.str == NULL ? TOK_ERROR | 625 | t = rhs.str == NULL ? TOK_ERROR | |
627 | : !doEval ? TOK_FALSE | 626 | : !doEval ? TOK_FALSE | |
628 | : EvalCompare(par, lhs.str, lhsQuoted, op, rhs.str, rhsQuoted); | 627 | : EvalCompare(par, lhs.str, lhsQuoted, op, rhs.str, rhsQuoted); | |
629 | FStr_Done(&rhs); | 628 | FStr_Done(&rhs); | |
630 | 629 | |||
631 | done_lhs: | 630 | done_lhs: | |
632 | FStr_Done(&lhs); | 631 | FStr_Done(&lhs); | |
633 | return t; | 632 | return t; | |
634 | } | 633 | } | |
635 | 634 | |||
636 | /* | 635 | /* | |
637 | * The argument to empty() is a variable name, optionally followed by | 636 | * The argument to empty() is a variable name, optionally followed by | |
638 | * variable modifiers. | 637 | * variable modifiers. | |
639 | */ | 638 | */ | |
640 | static bool | 639 | static bool | |
641 | CondParser_FuncCallEmpty(CondParser *par, bool doEval, Token *out_token) | 640 | CondParser_FuncCallEmpty(CondParser *par, bool doEval, Token *out_token) | |
642 | { | 641 | { | |
643 | const char *p = par->p; | 642 | const char *p = par->p; | |
644 | Token tok; | 643 | Token tok; | |
645 | FStr val; | 644 | FStr val; | |
646 | 645 | |||
647 | if (!skip_string(&p, "empty")) | 646 | if (!skip_string(&p, "empty")) | |
648 | return false; | 647 | return false; | |
649 | 648 | |||
650 | cpp_skip_whitespace(&p); | 649 | cpp_skip_whitespace(&p); | |
651 | if (*p != '(') | 650 | if (*p != '(') | |
652 | return false; | 651 | return false; | |
653 | 652 | |||
654 | p--; /* Make p[1] point to the '('. */ | 653 | p--; /* Make p[1] point to the '('. */ | |
655 | val = Var_Parse(&p, SCOPE_CMDLINE, | 654 | val = Var_Parse(&p, SCOPE_CMDLINE, | |
656 | doEval ? VARE_WANTRES : VARE_PARSE_ONLY); | 655 | doEval ? VARE_WANTRES : VARE_PARSE_ONLY); | |
657 | /* TODO: handle errors */ | 656 | /* TODO: handle errors */ | |
658 | 657 | |||
659 | if (val.str == var_Error) | 658 | if (val.str == var_Error) | |
660 | tok = TOK_ERROR; | 659 | tok = TOK_ERROR; | |
661 | else { | 660 | else { | |
662 | cpp_skip_whitespace(&val.str); | 661 | cpp_skip_whitespace(&val.str); | |
663 | tok = ToToken(doEval && val.str[0] == '\0'); | 662 | tok = ToToken(doEval && val.str[0] == '\0'); | |
664 | } | 663 | } | |
665 | 664 | |||
666 | FStr_Done(&val); | 665 | FStr_Done(&val); | |
667 | *out_token = tok; | 666 | *out_token = tok; | |
668 | par->p = p; | 667 | par->p = p; | |
669 | return true; | 668 | return true; | |
670 | } | 669 | } | |
671 | 670 | |||
672 | /* Parse a function call expression, such as 'exists(${file})'. */ | 671 | /* Parse a function call expression, such as 'exists(${file})'. */ | |
673 | static bool | 672 | static bool | |
674 | CondParser_FuncCall(CondParser *par, bool doEval, Token *out_token) | 673 | CondParser_FuncCall(CondParser *par, bool doEval, Token *out_token) | |
675 | { | 674 | { | |
676 | char *arg; | 675 | char *arg; | |
677 | const char *p = par->p; | 676 | const char *p = par->p; | |
678 | bool (*fn)(const char *); | 677 | bool (*fn)(const char *); | |
679 | const char *fn_name = p; | 678 | const char *fn_name = p; | |
680 | 679 | |||
681 | if (skip_string(&p, "defined")) | 680 | if (skip_string(&p, "defined")) | |
682 | fn = FuncDefined; | 681 | fn = FuncDefined; | |
683 | else if (skip_string(&p, "make")) | 682 | else if (skip_string(&p, "make")) | |
684 | fn = FuncMake; | 683 | fn = FuncMake; | |
685 | else if (skip_string(&p, "exists")) | 684 | else if (skip_string(&p, "exists")) | |
686 | fn = FuncExists; | 685 | fn = FuncExists; | |
687 | else if (skip_string(&p, "target")) | 686 | else if (skip_string(&p, "target")) | |
688 | fn = FuncTarget; | 687 | fn = FuncTarget; | |
689 | else if (skip_string(&p, "commands")) | 688 | else if (skip_string(&p, "commands")) | |
690 | fn = FuncCommands; | 689 | fn = FuncCommands; | |
691 | else | 690 | else | |
692 | return false; | 691 | return false; | |
693 | 692 | |||
694 | cpp_skip_whitespace(&p); | 693 | cpp_skip_whitespace(&p); | |
695 | if (*p != '(') | 694 | if (*p != '(') | |
696 | return false; | 695 | return false; | |
697 | 696 | |||
698 | arg = ParseFuncArg(par, &p, doEval, fn_name); | 697 | arg = ParseFuncArg(par, &p, doEval, fn_name); | |
699 | *out_token = ToToken(doEval && | 698 | *out_token = ToToken(doEval && | |
700 | arg != NULL && arg[0] != '\0' && fn(arg)); | 699 | arg != NULL && arg[0] != '\0' && fn(arg)); | |
701 | free(arg); | 700 | free(arg); | |
702 | 701 | |||
703 | par->p = p; | 702 | par->p = p; | |
704 | return true; | 703 | return true; | |
705 | } | 704 | } | |
706 | 705 | |||
707 | /* | 706 | /* | |
708 | * Parse a comparison that neither starts with '"' nor '$', such as the | 707 | * Parse a comparison that neither starts with '"' nor '$', such as the | |
709 | * unusual 'bare == right' or '3 == ${VAR}', or a simple leaf without | 708 | * unusual 'bare == right' or '3 == ${VAR}', or a simple leaf without | |
710 | * operator, which is a number, an expression or a string literal. | 709 | * operator, which is a number, an expression or a string literal. | |
711 | * | 710 | * | |
712 | * TODO: Can this be merged into CondParser_Comparison? | 711 | * TODO: Can this be merged into CondParser_Comparison? | |
713 | */ | 712 | */ | |
714 | static Token | 713 | static Token | |
715 | CondParser_ComparisonOrLeaf(CondParser *par, bool doEval) | 714 | CondParser_ComparisonOrLeaf(CondParser *par, bool doEval) | |
716 | { | 715 | { | |
717 | Token t; | 716 | Token t; | |
718 | char *arg; | 717 | char *arg; | |
719 | const char *p; | 718 | const char *p; | |
720 | 719 | |||
721 | p = par->p; | 720 | p = par->p; | |
722 | if (ch_isdigit(p[0]) || p[0] == '-' || p[0] == '+') | 721 | if (ch_isdigit(p[0]) || p[0] == '-' || p[0] == '+') | |
723 | return CondParser_Comparison(par, doEval); | 722 | return CondParser_Comparison(par, doEval); | |
724 | 723 | |||
725 | /* | 724 | /* | |
726 | * Most likely we have a bare word to apply the default function to. | 725 | * Most likely we have a bare word to apply the default function to. | |
727 | * However, ".if a == b" gets here when the "a" is unquoted and | 726 | * However, ".if a == b" gets here when the "a" is unquoted and | |
728 | * doesn't start with a '$'. This surprises people. | 727 | * doesn't start with a '$'. This surprises people. | |
729 | * If what follows the function argument is a '=' or '!' then the | 728 | * If what follows the function argument is a '=' or '!' then the | |
730 | * syntax would be invalid if we did "defined(a)" - so instead treat | 729 | * syntax would be invalid if we did "defined(a)" - so instead treat | |
731 | * as an expression. | 730 | * as an expression. | |
732 | */ | 731 | */ | |
733 | /* | 732 | /* | |
734 | * XXX: In edge cases, an expression may be evaluated twice, | 733 | * XXX: In edge cases, an expression may be evaluated twice, | |
735 | * see cond-token-plain.mk, keyword 'twice'. | 734 | * see cond-token-plain.mk, keyword 'twice'. | |
736 | */ | 735 | */ | |
737 | arg = ParseWord(&p, doEval); | 736 | arg = ParseWord(&p, doEval); | |
738 | assert(arg[0] != '\0'); | 737 | assert(arg[0] != '\0'); | |
739 | 738 | |||
740 | if (*p == '=' || *p == '!' || *p == '<' || *p == '>') | 739 | if (*p == '=' || *p == '!' || *p == '<' || *p == '>') | |
741 | return CondParser_Comparison(par, doEval); | 740 | return CondParser_Comparison(par, doEval); | |
742 | par->p = p; | 741 | par->p = p; | |
743 | 742 | |||
744 | /* | 743 | /* | |
745 | * Evaluate the argument using the default function. | 744 | * Evaluate the argument using the default function. | |
746 | * This path always treats .if as .ifdef. To get here, the character | 745 | * This path always treats .if as .ifdef. To get here, the character | |
747 | * after .if must have been taken literally, so the argument cannot | 746 | * after .if must have been taken literally, so the argument cannot | |
748 | * be empty - even if it contained an expression. | 747 | * be empty - even if it contained an expression. | |
749 | */ | 748 | */ | |
750 | t = ToToken(doEval && par->evalBare(arg) != par->negateEvalBare); | 749 | t = ToToken(doEval && par->evalBare(arg) != par->negateEvalBare); | |
751 | free(arg); | 750 | free(arg); | |
752 | return t; | 751 | return t; | |
753 | } | 752 | } | |
754 | 753 | |||
755 | /* Return the next token or comparison result from the parser. */ | 754 | /* Return the next token or comparison result from the parser. */ | |
756 | static Token | 755 | static Token | |
757 | CondParser_Token(CondParser *par, bool doEval) | 756 | CondParser_Token(CondParser *par, bool doEval) | |
758 | { | 757 | { | |
759 | Token t; | 758 | Token t; | |
760 | 759 | |||
761 | t = par->curr; | 760 | t = par->curr; | |
762 | if (t != TOK_NONE) { | 761 | if (t != TOK_NONE) { | |
763 | par->curr = TOK_NONE; | 762 | par->curr = TOK_NONE; | |
764 | return t; | 763 | return t; | |
765 | } | 764 | } | |
766 | 765 | |||
767 | cpp_skip_hspace(&par->p); | 766 | cpp_skip_hspace(&par->p); | |
768 | 767 | |||
769 | switch (par->p[0]) { | 768 | switch (par->p[0]) { | |
770 | 769 | |||
771 | case '(': | 770 | case '(': | |
772 | par->p++; | 771 | par->p++; | |
773 | return TOK_LPAREN; | 772 | return TOK_LPAREN; | |
774 | 773 | |||
775 | case ')': | 774 | case ')': | |
776 | par->p++; | 775 | par->p++; | |
777 | return TOK_RPAREN; | 776 | return TOK_RPAREN; | |
778 | 777 | |||
779 | case '|': | 778 | case '|': | |
780 | par->p++; | 779 | par->p++; | |
781 | if (par->p[0] == '|') | 780 | if (par->p[0] == '|') | |
782 | par->p++; | 781 | par->p++; | |
783 | else if (opts.strict) { | 782 | else if (opts.strict) { | |
784 | Parse_Error(PARSE_FATAL, "Unknown operator '|'"); | 783 | Parse_Error(PARSE_FATAL, "Unknown operator '|'"); | |
785 | par->printedError = true; | 784 | par->printedError = true; | |
786 | return TOK_ERROR; | 785 | return TOK_ERROR; | |
787 | } | 786 | } | |
788 | return TOK_OR; | 787 | return TOK_OR; | |
789 | 788 | |||
790 | case '&': | 789 | case '&': | |
791 | par->p++; | 790 | par->p++; | |
792 | if (par->p[0] == '&') | 791 | if (par->p[0] == '&') | |
793 | par->p++; | 792 | par->p++; | |
794 | else if (opts.strict) { | 793 | else if (opts.strict) { | |
795 | Parse_Error(PARSE_FATAL, "Unknown operator '&'"); | 794 | Parse_Error(PARSE_FATAL, "Unknown operator '&'"); | |
796 | par->printedError = true; | 795 | par->printedError = true; | |
797 | return TOK_ERROR; | 796 | return TOK_ERROR; | |
798 | } | 797 | } | |
799 | return TOK_AND; | 798 | return TOK_AND; | |
800 | 799 | |||
801 | case '!': | 800 | case '!': | |
802 | par->p++; | 801 | par->p++; | |
803 | return TOK_NOT; | 802 | return TOK_NOT; | |
804 | 803 | |||
805 | case '#': /* XXX: see unit-tests/cond-token-plain.mk */ | 804 | case '#': /* XXX: see unit-tests/cond-token-plain.mk */ | |
806 | case '\n': /* XXX: why should this end the condition? */ | 805 | case '\n': /* XXX: why should this end the condition? */ | |
807 | /* Probably obsolete now, from 1993-03-21. */ | 806 | /* Probably obsolete now, from 1993-03-21. */ | |
808 | case '\0': | 807 | case '\0': | |
809 | return TOK_EOF; | 808 | return TOK_EOF; | |
810 | 809 | |||
811 | case '"': | 810 | case '"': | |
812 | case '$': | 811 | case '$': | |
813 | return CondParser_Comparison(par, doEval); | 812 | return CondParser_Comparison(par, doEval); | |
814 | 813 | |||
815 | default: | 814 | default: | |
816 | if (CondParser_FuncCallEmpty(par, doEval, &t)) | 815 | if (CondParser_FuncCallEmpty(par, doEval, &t)) | |
817 | return t; | 816 | return t; | |
818 | if (CondParser_FuncCall(par, doEval, &t)) | 817 | if (CondParser_FuncCall(par, doEval, &t)) | |
819 | return t; | 818 | return t; | |
820 | return CondParser_ComparisonOrLeaf(par, doEval); | 819 | return CondParser_ComparisonOrLeaf(par, doEval); | |
821 | } | 820 | } | |
822 | } | 821 | } | |
823 | 822 | |||
824 | /* Skip the next token if it equals t. */ | 823 | /* Skip the next token if it equals t. */ | |
825 | static bool | 824 | static bool | |
826 | CondParser_Skip(CondParser *par, Token t) | 825 | CondParser_Skip(CondParser *par, Token t) | |
827 | { | 826 | { | |
828 | Token actual; | 827 | Token actual; | |
829 | 828 | |||
830 | actual = CondParser_Token(par, false); | 829 | actual = CondParser_Token(par, false); | |
831 | if (actual == t) | 830 | if (actual == t) | |
832 | return true; | 831 | return true; | |
833 | 832 | |||
834 | assert(par->curr == TOK_NONE); | 833 | assert(par->curr == TOK_NONE); | |
835 | assert(actual != TOK_NONE); | 834 | assert(actual != TOK_NONE); | |
836 | par->curr = actual; | 835 | par->curr = actual; | |
837 | return false; | 836 | return false; | |
838 | } | 837 | } | |
839 | 838 | |||
840 | /* | 839 | /* | |
841 | * Term -> '(' Or ')' | 840 | * Term -> '(' Or ')' | |
842 | * Term -> '!' Term | 841 | * Term -> '!' Term | |
843 | * Term -> Leaf Operator Leaf | 842 | * Term -> Leaf Operator Leaf | |
844 | * Term -> Leaf | 843 | * Term -> Leaf | |
845 | */ | 844 | */ | |
846 | static CondResult | 845 | static CondResult | |
847 | CondParser_Term(CondParser *par, bool doEval) | 846 | CondParser_Term(CondParser *par, bool doEval) | |
848 | { | 847 | { | |
849 | CondResult res; | 848 | CondResult res; | |
850 | Token t; | 849 | Token t; | |
851 | bool neg = false; | 850 | bool neg = false; | |
852 | 851 | |||
853 | while ((t = CondParser_Token(par, doEval)) == TOK_NOT) | 852 | while ((t = CondParser_Token(par, doEval)) == TOK_NOT) | |
854 | neg = !neg; | 853 | neg = !neg; | |
855 | 854 | |||
856 | if (t == TOK_TRUE || t == TOK_FALSE) | 855 | if (t == TOK_TRUE || t == TOK_FALSE) | |
857 | return neg == (t == TOK_FALSE) ? CR_TRUE : CR_FALSE; | 856 | return neg == (t == TOK_FALSE) ? CR_TRUE : CR_FALSE; | |
858 | 857 | |||
859 | if (t == TOK_LPAREN) { | 858 | if (t == TOK_LPAREN) { | |
860 | res = CondParser_Or(par, doEval); | 859 | res = CondParser_Or(par, doEval); | |
861 | if (res == CR_ERROR) | 860 | if (res == CR_ERROR) | |
862 | return CR_ERROR; | 861 | return CR_ERROR; | |
863 | if (CondParser_Token(par, doEval) != TOK_RPAREN) | 862 | if (CondParser_Token(par, doEval) != TOK_RPAREN) | |
864 | return CR_ERROR; | 863 | return CR_ERROR; | |
865 | return neg == (res == CR_FALSE) ? CR_TRUE : CR_FALSE; | 864 | return neg == (res == CR_FALSE) ? CR_TRUE : CR_FALSE; | |
866 | } | 865 | } | |
867 | 866 | |||
868 | return CR_ERROR; | 867 | return CR_ERROR; | |
869 | } | 868 | } | |
870 | 869 | |||
871 | /* | 870 | /* | |
872 | * And -> Term ('&&' Term)* | 871 | * And -> Term ('&&' Term)* | |
873 | */ | 872 | */ | |
874 | static CondResult | 873 | static CondResult | |
875 | CondParser_And(CondParser *par, bool doEval) | 874 | CondParser_And(CondParser *par, bool doEval) | |
876 | { | 875 | { | |
877 | CondResult res, rhs; | 876 | CondResult res, rhs; | |
878 | 877 | |||
879 | res = CR_TRUE; | 878 | res = CR_TRUE; | |
880 | do { | 879 | do { | |
881 | if ((rhs = CondParser_Term(par, doEval)) == CR_ERROR) | 880 | if ((rhs = CondParser_Term(par, doEval)) == CR_ERROR) | |
882 | return CR_ERROR; | 881 | return CR_ERROR; | |
883 | if (rhs == CR_FALSE) { | 882 | if (rhs == CR_FALSE) { | |
884 | res = CR_FALSE; | 883 | res = CR_FALSE; | |
885 | doEval = false; | 884 | doEval = false; | |
886 | } | 885 | } | |
887 | } while (CondParser_Skip(par, TOK_AND)); | 886 | } while (CondParser_Skip(par, TOK_AND)); | |
888 | 887 | |||
889 | return res; | 888 | return res; | |
890 | } | 889 | } | |
891 | 890 | |||
892 | /* | 891 | /* | |
893 | * Or -> And ('||' And)* | 892 | * Or -> And ('||' And)* | |
894 | */ | 893 | */ | |
895 | static CondResult | 894 | static CondResult | |
896 | CondParser_Or(CondParser *par, bool doEval) | 895 | CondParser_Or(CondParser *par, bool doEval) | |
897 | { | 896 | { | |
898 | CondResult res, rhs; | 897 | CondResult res, rhs; | |
899 | 898 | |||
900 | res = CR_FALSE; | 899 | res = CR_FALSE; | |
901 | do { | 900 | do { | |
902 | if ((rhs = CondParser_And(par, doEval)) == CR_ERROR) | 901 | if ((rhs = CondParser_And(par, doEval)) == CR_ERROR) | |
903 | return CR_ERROR; | 902 | return CR_ERROR; | |
904 | if (rhs == CR_TRUE) { | 903 | if (rhs == CR_TRUE) { | |
905 | res = CR_TRUE; | 904 | res = CR_TRUE; | |
906 | doEval = false; | 905 | doEval = false; | |
907 | } | 906 | } | |
908 | } while (CondParser_Skip(par, TOK_OR)); | 907 | } while (CondParser_Skip(par, TOK_OR)); | |
909 | 908 | |||
910 | return res; | 909 | return res; | |
911 | } | 910 | } | |
912 | 911 | |||
913 | /* | 912 | /* | |
914 | * Evaluate the condition, including any side effects from the | 913 | * Evaluate the condition, including any side effects from the | |
915 | * expressions in the condition. The condition consists of &&, ||, !, | 914 | * expressions in the condition. The condition consists of &&, ||, !, | |
916 | * function(arg), comparisons and parenthetical groupings thereof. | 915 | * function(arg), comparisons and parenthetical groupings thereof. | |
917 | */ | 916 | */ | |
918 | static CondResult | 917 | static CondResult | |
919 | CondEvalExpression(const char *cond, bool plain, | 918 | CondEvalExpression(const char *cond, bool plain, | |
920 | bool (*evalBare)(const char *), bool negate, | 919 | bool (*evalBare)(const char *), bool negate, | |
921 | bool eprint, bool leftUnquotedOK) | 920 | bool eprint, bool leftUnquotedOK) | |
922 | { | 921 | { | |
923 | CondParser par; | 922 | CondParser par; | |
924 | CondResult rval; | 923 | CondResult rval; | |
925 | 924 | |||
926 | cpp_skip_hspace(&cond); | 925 | cpp_skip_hspace(&cond); | |
927 | 926 | |||
928 | par.plain = plain; | 927 | par.plain = plain; | |
929 | par.evalBare = evalBare; | 928 | par.evalBare = evalBare; | |
930 | par.negateEvalBare = negate; | 929 | par.negateEvalBare = negate; | |
931 | par.leftUnquotedOK = leftUnquotedOK; | 930 | par.leftUnquotedOK = leftUnquotedOK; | |
932 | par.p = cond; | 931 | par.p = cond; | |
933 | par.curr = TOK_NONE; | 932 | par.curr = TOK_NONE; | |
934 | par.printedError = false; | 933 | par.printedError = false; | |
935 | 934 | |||
936 | DEBUG1(COND, "CondParser_Eval: %s\n", par.p); | 935 | DEBUG1(COND, "CondParser_Eval: %s\n", par.p); | |
937 | rval = CondParser_Or(&par, true); | 936 | rval = CondParser_Or(&par, true); | |
938 | if (par.curr != TOK_EOF) | 937 | if (par.curr != TOK_EOF) | |
939 | rval = CR_ERROR; | 938 | rval = CR_ERROR; | |
940 | 939 | |||
941 | if (rval == CR_ERROR && eprint && !par.printedError) | 940 | if (rval == CR_ERROR && eprint && !par.printedError) | |
942 | Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", cond); | 941 | Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", cond); | |
943 | 942 | |||
944 | return rval; | 943 | return rval; | |
945 | } | 944 | } | |
946 | 945 | |||
947 | /* | 946 | /* | |
948 | * Evaluate a condition in a :? modifier, such as | 947 | * Evaluate a condition in a :? modifier, such as | |
949 | * ${"${VAR}" == value:?yes:no}. | 948 | * ${"${VAR}" == value:?yes:no}. | |
950 | */ | 949 | */ | |
951 | CondResult | 950 | CondResult | |
952 | Cond_EvalCondition(const char *cond) | 951 | Cond_EvalCondition(const char *cond) | |
953 | { | 952 | { | |
954 | return CondEvalExpression(cond, true, | 953 | return CondEvalExpression(cond, true, | |
955 | FuncDefined, false, false, true); | 954 | FuncDefined, false, false, true); | |
956 | } | 955 | } | |
957 | 956 | |||
958 | static bool | 957 | static bool | |
959 | IsEndif(const char *p) | 958 | IsEndif(const char *p) | |
960 | { | 959 | { | |
961 | return p[0] == 'e' && p[1] == 'n' && p[2] == 'd' && | 960 | return p[0] == 'e' && p[1] == 'n' && p[2] == 'd' && | |
962 | p[3] == 'i' && p[4] == 'f' && !ch_isalpha(p[5]); | 961 | p[3] == 'i' && p[4] == 'f' && !ch_isalpha(p[5]); | |
963 | } | 962 | } | |
964 | 963 | |||
965 | static bool | 964 | static bool | |
966 | DetermineKindOfConditional(const char **pp, bool *out_plain, | 965 | DetermineKindOfConditional(const char **pp, bool *out_plain, | |
967 | bool (**out_evalBare)(const char *), | 966 | bool (**out_evalBare)(const char *), | |
968 | bool *out_negate) | 967 | bool *out_negate) | |
969 | { | 968 | { | |
970 | const char *p = *pp + 2; | 969 | const char *p = *pp + 2; | |
971 | 970 | |||
972 | *out_plain = false; | 971 | *out_plain = false; | |
973 | *out_evalBare = FuncDefined; | 972 | *out_evalBare = FuncDefined; | |
974 | *out_negate = skip_string(&p, "n"); | 973 | *out_negate = skip_string(&p, "n"); | |
975 | 974 | |||
976 | if (skip_string(&p, "def")) { /* .ifdef and .ifndef */ | 975 | if (skip_string(&p, "def")) { /* .ifdef and .ifndef */ | |
977 | } else if (skip_string(&p, "make")) /* .ifmake and .ifnmake */ | 976 | } else if (skip_string(&p, "make")) /* .ifmake and .ifnmake */ | |
978 | *out_evalBare = FuncMake; | 977 | *out_evalBare = FuncMake; | |
979 | else if (!*out_negate) /* plain .if */ | 978 | else if (!*out_negate) /* plain .if */ | |
980 | *out_plain = true; | 979 | *out_plain = true; | |
981 | else | 980 | else | |
982 | goto unknown_directive; | 981 | goto unknown_directive; | |
983 | if (ch_isalpha(*p)) | 982 | if (ch_isalpha(*p)) | |
984 | goto unknown_directive; | 983 | goto unknown_directive; | |
985 | 984 | |||
986 | *pp = p; | 985 | *pp = p; | |
987 | return true; | 986 | return true; | |
988 | 987 | |||
989 | unknown_directive: | 988 | unknown_directive: | |
990 | return false; | 989 | return false; | |
991 | } | 990 | } | |
992 | 991 | |||
993 | /* | 992 | /* | |
994 | * Evaluate the conditional directive in the line, which is one of: | 993 | * Evaluate the conditional directive in the line, which is one of: | |
995 | * | 994 | * | |
996 | * .if <cond> | 995 | * .if <cond> | |
997 | * .ifmake <cond> | 996 | * .ifmake <cond> | |
998 | * .ifnmake <cond> | 997 | * .ifnmake <cond> | |
999 | * .ifdef <cond> | 998 | * .ifdef <cond> | |
1000 | * .ifndef <cond> | 999 | * .ifndef <cond> | |
1001 | * .elif <cond> | 1000 | * .elif <cond> | |
1002 | * .elifmake <cond> | 1001 | * .elifmake <cond> | |
1003 | * .elifnmake <cond> | 1002 | * .elifnmake <cond> | |
1004 | * .elifdef <cond> | 1003 | * .elifdef <cond> | |
1005 | * .elifndef <cond> | 1004 | * .elifndef <cond> | |
1006 | * .else | 1005 | * .else | |
1007 | * .endif | 1006 | * .endif | |
1008 | * | 1007 | * | |
1009 | * In these directives, <cond> consists of &&, ||, !, function(arg), | 1008 | * In these directives, <cond> consists of &&, ||, !, function(arg), | |
1010 | * comparisons, expressions, bare words, numbers and strings, and | 1009 | * comparisons, expressions, bare words, numbers and strings, and | |
1011 | * parenthetical groupings thereof. | 1010 | * parenthetical groupings thereof. | |
1012 | * | 1011 | * | |
1013 | * Results: | 1012 | * Results: | |
1014 | * CR_TRUE to continue parsing the lines that follow the | 1013 | * CR_TRUE to continue parsing the lines that follow the | |
1015 | * conditional (when <cond> evaluates to true) | 1014 | * conditional (when <cond> evaluates to true) | |
1016 | * CR_FALSE to skip the lines after the conditional | 1015 | * CR_FALSE to skip the lines after the conditional | |
1017 | * (when <cond> evaluates to false, or when a previous | 1016 | * (when <cond> evaluates to false, or when a previous | |
1018 | * branch was already taken) | 1017 | * branch was already taken) | |
1019 | * CR_ERROR if the conditional was not valid, either because of | 1018 | * CR_ERROR if the conditional was not valid, either because of | |
1020 | * a syntax error or because some variable was undefined | 1019 | * a syntax error or because some variable was undefined | |
1021 | * or because the condition could not be evaluated | 1020 | * or because the condition could not be evaluated | |
1022 | */ | 1021 | */ | |
1023 | CondResult | 1022 | CondResult | |
1024 | Cond_EvalLine(const char *line) | 1023 | Cond_EvalLine(const char *line) | |
1025 | { | 1024 | { | |
1026 | typedef enum IfState { | 1025 | typedef enum IfState { | |
1027 | 1026 | |||
1028 | /* None of the previous <cond> evaluated to true. */ | 1027 | /* None of the previous <cond> evaluated to true. */ | |
1029 | IFS_INITIAL = 0, | 1028 | IFS_INITIAL = 0, | |
1030 | 1029 | |||
1031 | /* | 1030 | /* | |
1032 | * The previous <cond> evaluated to true. The lines following | 1031 | * The previous <cond> evaluated to true. The lines following | |
1033 | * this condition are interpreted. | 1032 | * this condition are interpreted. | |
1034 | */ | 1033 | */ | |
1035 | IFS_ACTIVE = 1 << 0, | 1034 | IFS_ACTIVE = 1 << 0, | |
1036 | 1035 | |||
1037 | /* The previous directive was an '.else'. */ | 1036 | /* The previous directive was an '.else'. */ | |
1038 | IFS_SEEN_ELSE = 1 << 1, | 1037 | IFS_SEEN_ELSE = 1 << 1, | |
1039 | 1038 | |||
1040 | /* One of the previous <cond> evaluated to true. */ | 1039 | /* One of the previous <cond> evaluated to true. */ | |
1041 | IFS_WAS_ACTIVE = 1 << 2 | 1040 | IFS_WAS_ACTIVE = 1 << 2 | |
1042 | 1041 | |||
1043 | } IfState; | 1042 | } IfState; | |
1044 | 1043 | |||
1045 | static enum IfState *cond_states = NULL; | 1044 | static enum IfState *cond_states = NULL; | |
1046 | static unsigned int cond_states_cap = 128; | 1045 | static unsigned int cond_states_cap = 128; | |
1047 | 1046 | |||
1048 | bool plain; | 1047 | bool plain; | |
1049 | bool (*evalBare)(const char *); | 1048 | bool (*evalBare)(const char *); | |
1050 | bool negate; | 1049 | bool negate; | |
1051 | bool isElif; | 1050 | bool isElif; | |
1052 | CondResult res; | 1051 | CondResult res; | |
1053 | IfState state; | 1052 | IfState state; | |
1054 | const char *p = line; | 1053 | const char *p = line; | |
1055 | 1054 | |||
1056 | if (cond_states == NULL) { | 1055 | if (cond_states == NULL) { | |
1057 | cond_states = bmake_malloc( | 1056 | cond_states = bmake_malloc( | |
1058 | cond_states_cap * sizeof *cond_states); | 1057 | cond_states_cap * sizeof *cond_states); | |
1059 | cond_states[0] = IFS_ACTIVE; | 1058 | cond_states[0] = IFS_ACTIVE; | |
1060 | } | 1059 | } | |
1061 | 1060 | |||
1062 | p++; /* skip the leading '.' */ | 1061 | p++; /* skip the leading '.' */ | |
1063 | cpp_skip_hspace(&p); | 1062 | cpp_skip_hspace(&p); | |
1064 | 1063 | |||
1065 | if (IsEndif(p)) { | 1064 | if (IsEndif(p)) { | |
1066 | if (p[5] != '\0') { | 1065 | if (p[5] != '\0') { | |
1067 | Parse_Error(PARSE_FATAL, | 1066 | Parse_Error(PARSE_FATAL, | |
1068 | "The .endif directive does not take arguments"); | 1067 | "The .endif directive does not take arguments"); | |
1069 | } | 1068 | } | |
1070 | 1069 | |||
1071 | if (cond_depth == CurFile_CondMinDepth()) { | 1070 | if (cond_depth == CurFile_CondMinDepth()) { | |
1072 | Parse_Error(PARSE_FATAL, "if-less endif"); | 1071 | Parse_Error(PARSE_FATAL, "if-less endif"); | |
1073 | return CR_TRUE; | 1072 | return CR_TRUE; | |
1074 | } | 1073 | } | |
1075 | 1074 | |||
1076 | /* Return state for previous conditional */ | 1075 | /* Return state for previous conditional */ | |
1077 | cond_depth--; | 1076 | cond_depth--; | |
1078 | Parse_GuardEndif(); | 1077 | Parse_GuardEndif(); | |
1079 | return cond_states[cond_depth] & IFS_ACTIVE | 1078 | return cond_states[cond_depth] & IFS_ACTIVE | |
1080 | ? CR_TRUE : CR_FALSE; | 1079 | ? CR_TRUE : CR_FALSE; | |
1081 | } | 1080 | } | |
1082 | 1081 | |||
1083 | /* Parse the name of the directive, such as 'if', 'elif', 'endif'. */ | 1082 | /* Parse the name of the directive, such as 'if', 'elif', 'endif'. */ | |
1084 | if (p[0] == 'e') { | 1083 | if (p[0] == 'e') { | |
1085 | if (p[1] != 'l') | 1084 | if (p[1] != 'l') | |
1086 | return CR_ERROR; | 1085 | return CR_ERROR; | |
1087 | 1086 | |||
1088 | /* Quite likely this is 'else' or 'elif' */ | 1087 | /* Quite likely this is 'else' or 'elif' */ | |
1089 | p += 2; | 1088 | p += 2; | |
1090 | if (strncmp(p, "se", 2) == 0 && !ch_isalpha(p[2])) { | 1089 | if (strncmp(p, "se", 2) == 0 && !ch_isalpha(p[2])) { | |
1091 | if (p[2] != '\0') | 1090 | if (p[2] != '\0') | |
1092 | Parse_Error(PARSE_FATAL, | 1091 | Parse_Error(PARSE_FATAL, | |
1093 | "The .else directive " | 1092 | "The .else directive " | |
1094 | "does not take arguments"); | 1093 | "does not take arguments"); | |
1095 | 1094 | |||
1096 | if (cond_depth == CurFile_CondMinDepth()) { | 1095 | if (cond_depth == CurFile_CondMinDepth()) { | |
1097 | Parse_Error(PARSE_FATAL, "if-less else"); | 1096 | Parse_Error(PARSE_FATAL, "if-less else"); | |
1098 | return CR_TRUE; | 1097 | return CR_TRUE; | |
1099 | } | 1098 | } | |
1100 | Parse_GuardElse(); | 1099 | Parse_GuardElse(); | |
1101 | 1100 | |||
1102 | state = cond_states[cond_depth]; | 1101 | state = cond_states[cond_depth]; | |
1103 | if (state == IFS_INITIAL) { | 1102 | if (state == IFS_INITIAL) { | |
1104 | state = IFS_ACTIVE | IFS_SEEN_ELSE; | 1103 | state = IFS_ACTIVE | IFS_SEEN_ELSE; | |
1105 | } else { | 1104 | } else { | |
1106 | if (state & IFS_SEEN_ELSE) | 1105 | if (state & IFS_SEEN_ELSE) | |
1107 | Parse_Error(PARSE_WARNING, | 1106 | Parse_Error(PARSE_WARNING, | |
1108 | "extra else"); | 1107 | "extra else"); | |
1109 | state = IFS_WAS_ACTIVE | IFS_SEEN_ELSE; | 1108 | state = IFS_WAS_ACTIVE | IFS_SEEN_ELSE; | |
1110 | } | 1109 | } | |
1111 | cond_states[cond_depth] = state; | 1110 | cond_states[cond_depth] = state; | |
1112 | 1111 | |||
1113 | return state & IFS_ACTIVE ? CR_TRUE : CR_FALSE; | 1112 | return state & IFS_ACTIVE ? CR_TRUE : CR_FALSE; | |
1114 | } | 1113 | } | |
1115 | /* Assume for now it is an elif */ | 1114 | /* Assume for now it is an elif */ | |
1116 | isElif = true; | 1115 | isElif = true; | |
1117 | } else | 1116 | } else | |
1118 | isElif = false; | 1117 | isElif = false; | |
1119 | 1118 | |||
1120 | if (p[0] != 'i' || p[1] != 'f') | 1119 | if (p[0] != 'i' || p[1] != 'f') | |
1121 | return CR_ERROR; | 1120 | return CR_ERROR; | |
1122 | 1121 | |||
1123 | if (!DetermineKindOfConditional(&p, &plain, &evalBare, &negate)) | 1122 | if (!DetermineKindOfConditional(&p, &plain, &evalBare, &negate)) | |
1124 | return CR_ERROR; | 1123 | return CR_ERROR; | |
1125 | 1124 | |||
1126 | if (isElif) { | 1125 | if (isElif) { | |
1127 | if (cond_depth == CurFile_CondMinDepth()) { | 1126 | if (cond_depth == CurFile_CondMinDepth()) { | |
1128 | Parse_Error(PARSE_FATAL, "if-less elif"); | 1127 | Parse_Error(PARSE_FATAL, "if-less elif"); | |
1129 | return CR_TRUE; | 1128 | return CR_TRUE; | |
1130 | } | 1129 | } | |
1131 | Parse_GuardElse(); | 1130 | Parse_GuardElse(); | |
1132 | state = cond_states[cond_depth]; | 1131 | state = cond_states[cond_depth]; | |
1133 | if (state & IFS_SEEN_ELSE) { | 1132 | if (state & IFS_SEEN_ELSE) { | |
1134 | Parse_Error(PARSE_WARNING, "extra elif"); | 1133 | Parse_Error(PARSE_WARNING, "extra elif"); | |
1135 | cond_states[cond_depth] = | 1134 | cond_states[cond_depth] = | |
1136 | IFS_WAS_ACTIVE | IFS_SEEN_ELSE; | 1135 | IFS_WAS_ACTIVE | IFS_SEEN_ELSE; | |
1137 | return CR_FALSE; | 1136 | return CR_FALSE; | |
1138 | } | 1137 | } | |
1139 | if (state != IFS_INITIAL) { | 1138 | if (state != IFS_INITIAL) { | |
1140 | cond_states[cond_depth] = IFS_WAS_ACTIVE; | 1139 | cond_states[cond_depth] = IFS_WAS_ACTIVE; | |
1141 | return CR_FALSE; | 1140 | return CR_FALSE; | |
1142 | } | 1141 | } | |
1143 | } else { | 1142 | } else { | |
1144 | /* Normal .if */ | 1143 | /* Normal .if */ | |
1145 | if (cond_depth + 1 >= cond_states_cap) { | 1144 | if (cond_depth + 1 >= cond_states_cap) { | |
1146 | /* | 1145 | /* | |
1147 | * This is rare, but not impossible. | 1146 | * This is rare, but not impossible. | |
1148 | * In meta mode, dirdeps.mk (only runs at level 0) | 1147 | * In meta mode, dirdeps.mk (only runs at level 0) | |
1149 | * can need more than the default. | 1148 | * can need more than the default. | |
1150 | */ | 1149 | */ | |
1151 | cond_states_cap += 32; | 1150 | cond_states_cap += 32; | |
1152 | cond_states = bmake_realloc(cond_states, | 1151 | cond_states = bmake_realloc(cond_states, | |
1153 | cond_states_cap * sizeof *cond_states); | 1152 | cond_states_cap * sizeof *cond_states); | |
1154 | } | 1153 | } | |
1155 | state = cond_states[cond_depth]; | 1154 | state = cond_states[cond_depth]; | |
1156 | cond_depth++; | 1155 | cond_depth++; | |
1157 | if (!(state & IFS_ACTIVE)) { | 1156 | if (!(state & IFS_ACTIVE)) { | |
1158 | cond_states[cond_depth] = IFS_WAS_ACTIVE; | 1157 | cond_states[cond_depth] = IFS_WAS_ACTIVE; | |
1159 | return CR_FALSE; | 1158 | return CR_FALSE; | |
1160 | } | 1159 | } | |
1161 | } | 1160 | } | |
1162 | 1161 | |||
1163 | res = CondEvalExpression(p, plain, evalBare, negate, true, false); | 1162 | res = CondEvalExpression(p, plain, evalBare, negate, true, false); | |
1164 | if (res == CR_ERROR) { | 1163 | if (res == CR_ERROR) { | |
1165 | /* Syntax error, error message already output. */ | 1164 | /* Syntax error, error message already output. */ | |
1166 | /* Skip everything to the matching '.endif'. */ | 1165 | /* Skip everything to the matching '.endif'. */ | |
1167 | /* An extra '.else' is not detected in this case. */ | 1166 | /* An extra '.else' is not detected in this case. */ | |
1168 | cond_states[cond_depth] = IFS_WAS_ACTIVE; | 1167 | cond_states[cond_depth] = IFS_WAS_ACTIVE; | |
1169 | return CR_FALSE; | 1168 | return CR_FALSE; | |
1170 | } | 1169 | } | |
1171 | 1170 | |||
1172 | cond_states[cond_depth] = res == CR_TRUE ? IFS_ACTIVE : IFS_INITIAL; | 1171 | cond_states[cond_depth] = res == CR_TRUE ? IFS_ACTIVE : IFS_INITIAL; | |
1173 | return res; | 1172 | return res; | |
1174 | } | 1173 | } | |
1175 | 1174 | |||
1176 | static bool | 1175 | static bool | |
1177 | ParseVarnameGuard(const char **pp, const char **varname) | 1176 | ParseVarnameGuard(const char **pp, const char **varname) | |
1178 | { | 1177 | { | |
1179 | const char *p = *pp; | 1178 | const char *p = *pp; | |
1180 | 1179 | |||
1181 | if (ch_isalpha(*p) || *p == '_') { | 1180 | if (ch_isalpha(*p) || *p == '_') { | |
1182 | while (ch_isalnum(*p) || *p == '_') | 1181 | while (ch_isalnum(*p) || *p == '_') | |
1183 | p++; | 1182 | p++; | |
1184 | *varname = *pp; | 1183 | *varname = *pp; | |
1185 | *pp = p; | 1184 | *pp = p; | |
1186 | return true; | 1185 | return true; | |
1187 | } | 1186 | } | |
1188 | return false; | 1187 | return false; | |
1189 | } | 1188 | } | |
1190 | 1189 | |||
1191 | /* Extracts the multiple-inclusion guard from a conditional, if any. */ | 1190 | /* Extracts the multiple-inclusion guard from a conditional, if any. */ | |
1192 | Guard * | 1191 | Guard * | |
1193 | Cond_ExtractGuard(const char *line) | 1192 | Cond_ExtractGuard(const char *line) | |
1194 | { | 1193 | { | |
1195 | const char *p, *varname; | 1194 | const char *p, *varname; | |
1196 | Substring dir; | 1195 | Substring dir; | |
1197 | Guard *guard; | 1196 | Guard *guard; | |
1198 | 1197 | |||
1199 | p = line + 1; /* skip the '.' */ | 1198 | p = line + 1; /* skip the '.' */ | |
1200 | cpp_skip_hspace(&p); | 1199 | cpp_skip_hspace(&p); | |
1201 | 1200 | |||
1202 | dir.start = p; | 1201 | dir.start = p; | |
1203 | while (ch_isalpha(*p)) | 1202 | while (ch_isalpha(*p)) | |
1204 | p++; | 1203 | p++; | |
1205 | dir.end = p; | 1204 | dir.end = p; | |
1206 | cpp_skip_hspace(&p); | 1205 | cpp_skip_hspace(&p); | |
1207 | 1206 | |||
1208 | if (Substring_Equals(dir, "if")) { | 1207 | if (Substring_Equals(dir, "if")) { | |
1209 | if (skip_string(&p, "!defined(")) { | 1208 | if (skip_string(&p, "!defined(")) { | |
1210 | if (ParseVarnameGuard(&p, &varname) | 1209 | if (ParseVarnameGuard(&p, &varname) | |
1211 | && strcmp(p, ")") == 0) | 1210 | && strcmp(p, ")") == 0) | |
1212 | goto found_variable; | 1211 | goto found_variable; | |
1213 | } else if (skip_string(&p, "!target(")) { | 1212 | } else if (skip_string(&p, "!target(")) { | |
1214 | const char *arg_p = p; | 1213 | const char *arg_p = p; | |
1215 | free(ParseWord(&p, false)); | 1214 | free(ParseWord(&p, false)); | |
1216 | if (strcmp(p, ")") == 0) { | 1215 | if (strcmp(p, ")") == 0) { | |
1217 | guard = bmake_malloc(sizeof(*guard)); | 1216 | guard = bmake_malloc(sizeof(*guard)); | |
1218 | guard->kind = GK_TARGET; | 1217 | guard->kind = GK_TARGET; | |
1219 | guard->name = ParseWord(&arg_p, true); | 1218 | guard->name = ParseWord(&arg_p, true); | |
1220 | return guard; | 1219 | return guard; | |
1221 | } | 1220 | } | |
1222 | } | 1221 | } | |
1223 | } else if (Substring_Equals(dir, "ifndef")) { | 1222 | } else if (Substring_Equals(dir, "ifndef")) { | |
1224 | if (ParseVarnameGuard(&p, &varname) && *p == '\0') | 1223 | if (ParseVarnameGuard(&p, &varname) && *p == '\0') | |
1225 | goto found_variable; | 1224 | goto found_variable; | |
1226 | } | 1225 | } | |
1227 | return NULL; | 1226 | return NULL; | |
1228 | 1227 | |||
1229 | found_variable: | 1228 | found_variable: | |
1230 | guard = bmake_malloc(sizeof(*guard)); | 1229 | guard = bmake_malloc(sizeof(*guard)); | |
1231 | guard->kind = GK_VARIABLE; | 1230 | guard->kind = GK_VARIABLE; | |
1232 | guard->name = bmake_strsedup(varname, p); | 1231 | guard->name = bmake_strsedup(varname, p); | |
1233 | return guard; | 1232 | return guard; | |
1234 | } | 1233 | } | |
1235 | 1234 | |||
1236 | void | 1235 | void | |
1237 | Cond_EndFile(void) | 1236 | Cond_EndFile(void) | |
1238 | { | 1237 | { | |
1239 | unsigned int open_conds = cond_depth - CurFile_CondMinDepth(); | 1238 | unsigned int open_conds = cond_depth - CurFile_CondMinDepth(); | |
1240 | 1239 | |||
1241 | if (open_conds != 0) { | 1240 | if (open_conds != 0) { | |
1242 | Parse_Error(PARSE_FATAL, "%u open conditional%s", | 1241 | Parse_Error(PARSE_FATAL, "%u open conditional%s", | |
1243 | open_conds, open_conds == 1 ? "" : "s"); | 1242 | open_conds, open_conds == 1 ? "" : "s"); | |
1244 | cond_depth = CurFile_CondMinDepth(); | 1243 | cond_depth = CurFile_CondMinDepth(); | |
1245 | } | 1244 | } | |
1246 | } | 1245 | } |
--- src/usr.bin/make/make.h 2024/04/20 10:18:55 1.330
+++ src/usr.bin/make/make.h 2024/04/23 22:51:28 1.331
@@ -1,1214 +1,1214 @@ | @@ -1,1214 +1,1214 @@ | |||
1 | /* $NetBSD: make.h,v 1.330 2024/04/20 10:18:55 rillig Exp $ */ | 1 | /* $NetBSD: make.h,v 1.331 2024/04/23 22:51:28 rillig Exp $ */ | |
2 | 2 | |||
3 | /* | 3 | /* | |
4 | * Copyright (c) 1988, 1989, 1990, 1993 | 4 | * Copyright (c) 1988, 1989, 1990, 1993 | |
5 | * The Regents of the University of California. All rights reserved. | 5 | * The Regents of the University of California. 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. | |
15 | * 2. Redistributions in binary form must reproduce the above copyright | 15 | * 2. Redistributions in binary form must reproduce the above copyright | |
16 | * notice, this list of conditions and the following disclaimer in the | 16 | * notice, this list of conditions and the following disclaimer in the | |
17 | * documentation and/or other materials provided with the distribution. | 17 | * documentation and/or other materials provided with the distribution. | |
18 | * 3. Neither the name of the University nor the names of its contributors | 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 | 19 | * may be used to endorse or promote products derived from this software | |
20 | * without specific prior written permission. | 20 | * without specific prior written permission. | |
21 | * | 21 | * | |
22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | 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 | 23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | 25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | 27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | 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 | 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 | 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 | 31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
32 | * SUCH DAMAGE. | 32 | * SUCH DAMAGE. | |
33 | * | 33 | * | |
34 | * from: @(#)make.h 8.3 (Berkeley) 6/13/95 | 34 | * from: @(#)make.h 8.3 (Berkeley) 6/13/95 | |
35 | */ | 35 | */ | |
36 | 36 | |||
37 | /* | 37 | /* | |
38 | * Copyright (c) 1989 by Berkeley Softworks | 38 | * Copyright (c) 1989 by Berkeley Softworks | |
39 | * All rights reserved. | 39 | * All rights reserved. | |
40 | * | 40 | * | |
41 | * This code is derived from software contributed to Berkeley by | 41 | * This code is derived from software contributed to Berkeley by | |
42 | * Adam de Boor. | 42 | * Adam de Boor. | |
43 | * | 43 | * | |
44 | * Redistribution and use in source and binary forms, with or without | 44 | * Redistribution and use in source and binary forms, with or without | |
45 | * modification, are permitted provided that the following conditions | 45 | * modification, are permitted provided that the following conditions | |
46 | * are met: | 46 | * are met: | |
47 | * 1. Redistributions of source code must retain the above copyright | 47 | * 1. Redistributions of source code must retain the above copyright | |
48 | * notice, this list of conditions and the following disclaimer. | 48 | * notice, this list of conditions and the following disclaimer. | |
49 | * 2. Redistributions in binary form must reproduce the above copyright | 49 | * 2. Redistributions in binary form must reproduce the above copyright | |
50 | * notice, this list of conditions and the following disclaimer in the | 50 | * notice, this list of conditions and the following disclaimer in the | |
51 | * documentation and/or other materials provided with the distribution. | 51 | * documentation and/or other materials provided with the distribution. | |
52 | * 3. All advertising materials mentioning features or use of this software | 52 | * 3. All advertising materials mentioning features or use of this software | |
53 | * must display the following acknowledgement: | 53 | * must display the following acknowledgement: | |
54 | * This product includes software developed by the University of | 54 | * This product includes software developed by the University of | |
55 | * California, Berkeley and its contributors. | 55 | * California, Berkeley and its contributors. | |
56 | * 4. Neither the name of the University nor the names of its contributors | 56 | * 4. Neither the name of the University nor the names of its contributors | |
57 | * may be used to endorse or promote products derived from this software | 57 | * may be used to endorse or promote products derived from this software | |
58 | * without specific prior written permission. | 58 | * without specific prior written permission. | |
59 | * | 59 | * | |
60 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | 60 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
61 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 61 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
62 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 62 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
63 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | 63 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
64 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 64 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
65 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | 65 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
66 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | 66 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
67 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | 67 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
68 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | 68 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
69 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | 69 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
70 | * SUCH DAMAGE. | 70 | * SUCH DAMAGE. | |
71 | * | 71 | * | |
72 | * from: @(#)make.h 8.3 (Berkeley) 6/13/95 | 72 | * from: @(#)make.h 8.3 (Berkeley) 6/13/95 | |
73 | */ | 73 | */ | |
74 | 74 | |||
75 | /* | 75 | /* | |
76 | * make.h -- | 76 | * make.h -- | |
77 | * The global definitions for make | 77 | * The global definitions for make | |
78 | */ | 78 | */ | |
79 | 79 | |||
80 | #ifndef MAKE_MAKE_H | 80 | #ifndef MAKE_MAKE_H | |
81 | #define MAKE_MAKE_H | 81 | #define MAKE_MAKE_H | |
82 | 82 | |||
83 | #include <sys/types.h> | 83 | #include <sys/types.h> | |
84 | #include <sys/param.h> | 84 | #include <sys/param.h> | |
85 | #include <sys/stat.h> | 85 | #include <sys/stat.h> | |
86 | 86 | |||
87 | #include <assert.h> | 87 | #include <assert.h> | |
88 | #include <ctype.h> | 88 | #include <ctype.h> | |
89 | #include <fcntl.h> | 89 | #include <fcntl.h> | |
90 | #include <stdarg.h> | 90 | #include <stdarg.h> | |
91 | #include <stdio.h> | 91 | #include <stdio.h> | |
92 | #include <stdlib.h> | 92 | #include <stdlib.h> | |
93 | #include <string.h> | 93 | #include <string.h> | |
94 | #include <unistd.h> | 94 | #include <unistd.h> | |
95 | 95 | |||
96 | #ifdef BSD4_4 | 96 | #ifdef BSD4_4 | |
97 | # include <sys/cdefs.h> | 97 | # include <sys/cdefs.h> | |
98 | #endif | 98 | #endif | |
99 | 99 | |||
100 | #ifndef FD_CLOEXEC | 100 | #ifndef FD_CLOEXEC | |
101 | #define FD_CLOEXEC 1 | 101 | #define FD_CLOEXEC 1 | |
102 | #endif | 102 | #endif | |
103 | 103 | |||
104 | #if defined(__GNUC__) | 104 | #if defined(__GNUC__) | |
105 | #define MAKE_GNUC_PREREQ(x, y) \ | 105 | #define MAKE_GNUC_PREREQ(x, y) \ | |
106 | ((__GNUC__ == (x) && __GNUC_MINOR__ >= (y)) || \ | 106 | ((__GNUC__ == (x) && __GNUC_MINOR__ >= (y)) || \ | |
107 | (__GNUC__ > (x))) | 107 | (__GNUC__ > (x))) | |
108 | #else | 108 | #else | |
109 | #define MAKE_GNUC_PREREQ(x, y) 0 | 109 | #define MAKE_GNUC_PREREQ(x, y) 0 | |
110 | #endif | 110 | #endif | |
111 | 111 | |||
112 | #if MAKE_GNUC_PREREQ(2, 7) | 112 | #if MAKE_GNUC_PREREQ(2, 7) | |
113 | #define MAKE_ATTR_UNUSED __attribute__((__unused__)) | 113 | #define MAKE_ATTR_UNUSED __attribute__((__unused__)) | |
114 | #else | 114 | #else | |
115 | #define MAKE_ATTR_UNUSED /* delete */ | 115 | #define MAKE_ATTR_UNUSED /* delete */ | |
116 | #endif | 116 | #endif | |
117 | 117 | |||
118 | #if MAKE_GNUC_PREREQ(2, 5) | 118 | #if MAKE_GNUC_PREREQ(2, 5) | |
119 | #define MAKE_ATTR_DEAD __attribute__((__noreturn__)) | 119 | #define MAKE_ATTR_DEAD __attribute__((__noreturn__)) | |
120 | #elif defined(__GNUC__) | 120 | #elif defined(__GNUC__) | |
121 | #define MAKE_ATTR_DEAD __volatile | 121 | #define MAKE_ATTR_DEAD __volatile | |
122 | #else | 122 | #else | |
123 | #define MAKE_ATTR_DEAD /* delete */ | 123 | #define MAKE_ATTR_DEAD /* delete */ | |
124 | #endif | 124 | #endif | |
125 | 125 | |||
126 | #if MAKE_GNUC_PREREQ(2, 7) | 126 | #if MAKE_GNUC_PREREQ(2, 7) | |
127 | #define MAKE_ATTR_PRINTFLIKE(fmtarg, firstvararg) \ | 127 | #define MAKE_ATTR_PRINTFLIKE(fmtarg, firstvararg) \ | |
128 | __attribute__((__format__ (__printf__, fmtarg, firstvararg))) | 128 | __attribute__((__format__ (__printf__, fmtarg, firstvararg))) | |
129 | #else | 129 | #else | |
130 | #define MAKE_ATTR_PRINTFLIKE(fmtarg, firstvararg) /* delete */ | 130 | #define MAKE_ATTR_PRINTFLIKE(fmtarg, firstvararg) /* delete */ | |
131 | #endif | 131 | #endif | |
132 | 132 | |||
133 | #if MAKE_GNUC_PREREQ(4, 0) | 133 | #if MAKE_GNUC_PREREQ(4, 0) | |
134 | #define MAKE_ATTR_USE __attribute__((__warn_unused_result__)) | 134 | #define MAKE_ATTR_USE __attribute__((__warn_unused_result__)) | |
135 | #else | 135 | #else | |
136 | #define MAKE_ATTR_USE /* delete */ | 136 | #define MAKE_ATTR_USE /* delete */ | |
137 | #endif | 137 | #endif | |
138 | 138 | |||
139 | #if __STDC_VERSION__ >= 199901L || defined(lint) | 139 | #if __STDC_VERSION__ >= 199901L || defined(lint) | |
140 | #define MAKE_INLINE static inline MAKE_ATTR_UNUSED | 140 | #define MAKE_INLINE static inline MAKE_ATTR_UNUSED | |
141 | #else | 141 | #else | |
142 | #define MAKE_INLINE static MAKE_ATTR_UNUSED | 142 | #define MAKE_INLINE static MAKE_ATTR_UNUSED | |
143 | #endif | 143 | #endif | |
144 | 144 | |||
145 | /* MAKE_STATIC marks a function that may or may not be inlined. */ | 145 | /* MAKE_STATIC marks a function that may or may not be inlined. */ | |
146 | #if defined(lint) | 146 | #if defined(lint) | |
147 | /* As of 2021-07-31, NetBSD lint ignores __attribute__((unused)). */ | 147 | /* As of 2021-07-31, NetBSD lint ignores __attribute__((unused)). */ | |
148 | #define MAKE_STATIC MAKE_INLINE | 148 | #define MAKE_STATIC MAKE_INLINE | |
149 | #else | 149 | #else | |
150 | #define MAKE_STATIC static MAKE_ATTR_UNUSED | 150 | #define MAKE_STATIC static MAKE_ATTR_UNUSED | |
151 | #endif | 151 | #endif | |
152 | 152 | |||
153 | #if __STDC_VERSION__ >= 199901L || defined(lint) || defined(USE_C99_BOOLEAN) | 153 | #if __STDC_VERSION__ >= 199901L || defined(lint) || defined(USE_C99_BOOLEAN) | |
154 | #include <stdbool.h> | 154 | #include <stdbool.h> | |
155 | #elif defined(__bool_true_false_are_defined) | 155 | #elif defined(__bool_true_false_are_defined) | |
156 | /* | 156 | /* | |
157 | * All files of make must be compiled with the same definition of bool. | 157 | * All files of make must be compiled with the same definition of bool. | |
158 | * Since one of the files includes <stdbool.h>, that means the header is | 158 | * Since one of the files includes <stdbool.h>, that means the header is | |
159 | * available on this platform. Recompile everything with -DUSE_C99_BOOLEAN. | 159 | * available on this platform. Recompile everything with -DUSE_C99_BOOLEAN. | |
160 | */ | 160 | */ | |
161 | #error "<stdbool.h> is included in pre-C99 mode" | 161 | #error "<stdbool.h> is included in pre-C99 mode" | |
162 | #elif defined(bool) || defined(true) || defined(false) | 162 | #elif defined(bool) || defined(true) || defined(false) | |
163 | /* | 163 | /* | |
164 | * In pre-C99 mode, make does not expect that bool is already defined. | 164 | * In pre-C99 mode, make does not expect that bool is already defined. | |
165 | * You need to ensure that all translation units use the same definition for | 165 | * You need to ensure that all translation units use the same definition for | |
166 | * bool. | 166 | * bool. | |
167 | */ | 167 | */ | |
168 | #error "bool/true/false is defined in pre-C99 mode" | 168 | #error "bool/true/false is defined in pre-C99 mode" | |
169 | #else | 169 | #else | |
170 | typedef unsigned char bool; | 170 | typedef unsigned char bool; | |
171 | #define true 1 | 171 | #define true 1 | |
172 | #define false 0 | 172 | #define false 0 | |
173 | #endif | 173 | #endif | |
174 | 174 | |||
175 | #include "lst.h" | 175 | #include "lst.h" | |
176 | #include "make_malloc.h" | 176 | #include "make_malloc.h" | |
177 | #include "str.h" | 177 | #include "str.h" | |
178 | #include "hash.h" | 178 | #include "hash.h" | |
179 | #include "config.h" | 179 | #include "config.h" | |
180 | #include "buf.h" | 180 | #include "buf.h" | |
181 | 181 | |||
182 | /* | 182 | /* | |
183 | * The typical flow of states is: | 183 | * The typical flow of states is: | |
184 | * | 184 | * | |
185 | * The direct successful path: | 185 | * The direct successful path: | |
186 | * UNMADE -> BEINGMADE -> MADE. | 186 | * UNMADE -> BEINGMADE -> MADE. | |
187 | * | 187 | * | |
188 | * The direct error path: | 188 | * The direct error path: | |
189 | * UNMADE -> BEINGMADE -> ERROR. | 189 | * UNMADE -> BEINGMADE -> ERROR. | |
190 | * | 190 | * | |
191 | * The successful path when dependencies need to be made first: | 191 | * The successful path when dependencies need to be made first: | |
192 | * UNMADE -> DEFERRED -> REQUESTED -> BEINGMADE -> MADE. | 192 | * UNMADE -> DEFERRED -> REQUESTED -> BEINGMADE -> MADE. | |
193 | * | 193 | * | |
194 | * A node that has dependencies, and one of the dependencies cannot be made: | 194 | * A node that has dependencies, and one of the dependencies cannot be made: | |
195 | * UNMADE -> DEFERRED -> ABORTED. | 195 | * UNMADE -> DEFERRED -> ABORTED. | |
196 | * | 196 | * | |
197 | * A node that turns out to be up-to-date: | 197 | * A node that turns out to be up-to-date: | |
198 | * UNMADE -> BEINGMADE -> UPTODATE. | 198 | * UNMADE -> BEINGMADE -> UPTODATE. | |
199 | */ | 199 | */ | |
200 | typedef enum GNodeMade { | 200 | typedef enum GNodeMade { | |
201 | /* Not examined yet. */ | 201 | /* Not examined yet. */ | |
202 | UNMADE, | 202 | UNMADE, | |
203 | /* | 203 | /* | |
204 | * The node has been examined but is not yet ready since its | 204 | * The node has been examined but is not yet ready since its | |
205 | * dependencies have to be made first. | 205 | * dependencies have to be made first. | |
206 | */ | 206 | */ | |
207 | DEFERRED, | 207 | DEFERRED, | |
208 | 208 | |||
209 | /* The node is on the toBeMade list. */ | 209 | /* The node is on the toBeMade list. */ | |
210 | REQUESTED, | 210 | REQUESTED, | |
211 | 211 | |||
212 | /* | 212 | /* | |
213 | * The node is already being made. Trying to build a node in this | 213 | * The node is already being made. Trying to build a node in this | |
214 | * state indicates a cycle in the graph. | 214 | * state indicates a cycle in the graph. | |
215 | */ | 215 | */ | |
216 | BEINGMADE, | 216 | BEINGMADE, | |
217 | 217 | |||
218 | /* Was out-of-date and has been made. */ | 218 | /* Was out-of-date and has been made. */ | |
219 | MADE, | 219 | MADE, | |
220 | /* Was already up-to-date, does not need to be made. */ | 220 | /* Was already up-to-date, does not need to be made. */ | |
221 | UPTODATE, | 221 | UPTODATE, | |
222 | /* | 222 | /* | |
223 | * An error occurred while it was being made. Used only in compat | 223 | * An error occurred while it was being made. Used only in compat | |
224 | * mode. | 224 | * mode. | |
225 | */ | 225 | */ | |
226 | ERROR, | 226 | ERROR, | |
227 | /* | 227 | /* | |
228 | * The target was aborted due to an error making a dependency. Used | 228 | * The target was aborted due to an error making a dependency. Used | |
229 | * only in compat mode. | 229 | * only in compat mode. | |
230 | */ | 230 | */ | |
231 | ABORTED | 231 | ABORTED | |
232 | } GNodeMade; | 232 | } GNodeMade; | |
233 | 233 | |||
234 | /* | 234 | /* | |
235 | * The OP_ constants are used when parsing a dependency line as a way of | 235 | * The OP_ constants are used when parsing a dependency line as a way of | |
236 | * communicating to other parts of the program the way in which a target | 236 | * communicating to other parts of the program the way in which a target | |
237 | * should be made. | 237 | * should be made. | |
238 | * | 238 | * | |
239 | * Some of the OP_ constants can be combined, others cannot. | 239 | * Some of the OP_ constants can be combined, others cannot. | |
240 | * | 240 | * | |
241 | * See the tests depsrc-*.mk and deptgt-*.mk. | 241 | * See the tests depsrc-*.mk and deptgt-*.mk. | |
242 | */ | 242 | */ | |
243 | typedef enum GNodeType { | 243 | typedef enum GNodeType { | |
244 | OP_NONE = 0, | 244 | OP_NONE = 0, | |
245 | 245 | |||
246 | /* | 246 | /* | |
247 | * The dependency operator ':' is the most common one. The commands | 247 | * The dependency operator ':' is the most common one. The commands | |
248 | * of this node are executed if any child is out-of-date. | 248 | * of this node are executed if any child is out-of-date. | |
249 | */ | 249 | */ | |
250 | OP_DEPENDS = 1 << 0, | 250 | OP_DEPENDS = 1 << 0, | |
251 | /* | 251 | /* | |
252 | * The dependency operator '!' always executes its commands, even if | 252 | * The dependency operator '!' always executes its commands, even if | |
253 | * its children are up-to-date. | 253 | * its children are up-to-date. | |
254 | */ | 254 | */ | |
255 | OP_FORCE = 1 << 1, | 255 | OP_FORCE = 1 << 1, | |
256 | /* | 256 | /* | |
257 | * The dependency operator '::' behaves like ':', except that it | 257 | * The dependency operator '::' behaves like ':', except that it | |
258 | * allows multiple dependency groups to be defined. Each of these | 258 | * allows multiple dependency groups to be defined. Each of these | |
259 | * groups is executed on its own, independently from the others. Each | 259 | * groups is executed on its own, independently from the others. Each | |
260 | * individual dependency group is called a cohort. | 260 | * individual dependency group is called a cohort. | |
261 | */ | 261 | */ | |
262 | OP_DOUBLEDEP = 1 << 2, | 262 | OP_DOUBLEDEP = 1 << 2, | |
263 | 263 | |||
264 | /* Matches the dependency operators ':', '!' and '::'. */ | 264 | /* Matches the dependency operators ':', '!' and '::'. */ | |
265 | OP_OPMASK = OP_DEPENDS | OP_FORCE | OP_DOUBLEDEP, | 265 | OP_OPMASK = OP_DEPENDS | OP_FORCE | OP_DOUBLEDEP, | |
266 | 266 | |||
267 | /* Don't care if the target doesn't exist and can't be created. */ | 267 | /* Don't care if the target doesn't exist and can't be created. */ | |
268 | OP_OPTIONAL = 1 << 3, | 268 | OP_OPTIONAL = 1 << 3, | |
269 | /* Use associated commands for parents. */ | 269 | /* Use associated commands for parents. */ | |
270 | OP_USE = 1 << 4, | 270 | OP_USE = 1 << 4, | |
271 | /* | 271 | /* | |
272 | * Target is never out of date, but always execute commands anyway. | 272 | * Target is never out of date, but always execute commands anyway. | |
273 | * Its time doesn't matter, so it has none...sort of. | 273 | * Its time doesn't matter, so it has none...sort of. | |
274 | */ | 274 | */ | |
275 | OP_EXEC = 1 << 5, | 275 | OP_EXEC = 1 << 5, | |
276 | /* | 276 | /* | |
277 | * Ignore non-zero exit status from shell commands when creating the | 277 | * Ignore non-zero exit status from shell commands when creating the | |
278 | * node. | 278 | * node. | |
279 | */ | 279 | */ | |
280 | OP_IGNORE = 1 << 6, | 280 | OP_IGNORE = 1 << 6, | |
281 | /* Don't remove the target when interrupted. */ | 281 | /* Don't remove the target when interrupted. */ | |
282 | OP_PRECIOUS = 1 << 7, | 282 | OP_PRECIOUS = 1 << 7, | |
283 | /* Don't echo commands when executed. */ | 283 | /* Don't echo commands when executed. */ | |
284 | OP_SILENT = 1 << 8, | 284 | OP_SILENT = 1 << 8, | |
285 | /* | 285 | /* | |
286 | * Target is a recursive make so its commands should always be | 286 | * Target is a recursive make so its commands should always be | |
287 | * executed when it is out of date, regardless of the state of the -n | 287 | * executed when it is out of date, regardless of the state of the -n | |
288 | * or -t flags. | 288 | * or -t flags. | |
289 | */ | 289 | */ | |
290 | OP_MAKE = 1 << 9, | 290 | OP_MAKE = 1 << 9, | |
291 | /* | 291 | /* | |
292 | * Target is out-of-date only if any of its children was out-of-date. | 292 | * Target is out-of-date only if any of its children was out-of-date. | |
293 | */ | 293 | */ | |
294 | OP_JOIN = 1 << 10, | 294 | OP_JOIN = 1 << 10, | |
295 | /* Assume the children of the node have been already made. */ | 295 | /* Assume the children of the node have been already made. */ | |
296 | OP_MADE = 1 << 11, | 296 | OP_MADE = 1 << 11, | |
297 | /* Special .BEGIN, .END or .INTERRUPT. */ | 297 | /* Special .BEGIN, .END or .INTERRUPT. */ | |
298 | OP_SPECIAL = 1 << 12, | 298 | OP_SPECIAL = 1 << 12, | |
299 | /* Like .USE, only prepend commands. */ | 299 | /* Like .USE, only prepend commands. */ | |
300 | OP_USEBEFORE = 1 << 13, | 300 | OP_USEBEFORE = 1 << 13, | |
301 | /* | 301 | /* | |
302 | * The node is invisible to its parents. I.e. it doesn't show up in | 302 | * The node is invisible to its parents. I.e. it doesn't show up in | |
303 | * the parents' local variables (.IMPSRC, .ALLSRC). | 303 | * the parents' local variables (.IMPSRC, .ALLSRC). | |
304 | */ | 304 | */ | |
305 | OP_INVISIBLE = 1 << 14, | 305 | OP_INVISIBLE = 1 << 14, | |
306 | /* | 306 | /* | |
307 | * The node does not become the main target, even if it is the first | 307 | * The node does not become the main target, even if it is the first | |
308 | * target in the first makefile. | 308 | * target in the first makefile. | |
309 | */ | 309 | */ | |
310 | OP_NOTMAIN = 1 << 15, | 310 | OP_NOTMAIN = 1 << 15, | |
311 | /* Not a file target; run always. */ | 311 | /* Not a file target; run always. */ | |
312 | OP_PHONY = 1 << 16, | 312 | OP_PHONY = 1 << 16, | |
313 | /* Don't search for the file in the path. */ | 313 | /* Don't search for the file in the path. */ | |
314 | OP_NOPATH = 1 << 17, | 314 | OP_NOPATH = 1 << 17, | |
315 | /* | 315 | /* | |
316 | * In a dependency line "target: source1 .WAIT source2", source1 is | 316 | * In a dependency line "target: source1 .WAIT source2", source1 is | |
317 | * made first, including its children. Once that is finished, | 317 | * made first, including its children. Once that is finished, | |
318 | * source2 is made, including its children. The .WAIT keyword may | 318 | * source2 is made, including its children. The .WAIT keyword may | |
319 | * appear more than once in a single dependency declaration. | 319 | * appear more than once in a single dependency declaration. | |
320 | */ | 320 | */ | |
321 | OP_WAIT = 1 << 18, | 321 | OP_WAIT = 1 << 18, | |
322 | /* .NOMETA do not create a .meta file */ | 322 | /* .NOMETA do not create a .meta file */ | |
323 | OP_NOMETA = 1 << 19, | 323 | OP_NOMETA = 1 << 19, | |
324 | /* .META we _do_ want a .meta file */ | 324 | /* .META we _do_ want a .meta file */ | |
325 | OP_META = 1 << 20, | 325 | OP_META = 1 << 20, | |
326 | /* Do not compare commands in .meta file */ | 326 | /* Do not compare commands in .meta file */ | |
327 | OP_NOMETA_CMP = 1 << 21, | 327 | OP_NOMETA_CMP = 1 << 21, | |
328 | /* Possibly a submake node */ | 328 | /* Possibly a submake node */ | |
329 | OP_SUBMAKE = 1 << 22, | 329 | OP_SUBMAKE = 1 << 22, | |
330 | 330 | |||
331 | /* Attributes applied by PMake */ | 331 | /* Attributes applied by PMake */ | |
332 | 332 | |||
333 | /* The node is a transformation rule, such as ".c.o". */ | 333 | /* The node is a transformation rule, such as ".c.o". */ | |
334 | OP_TRANSFORM = 1 << 30, | 334 | OP_TRANSFORM = 1 << 30, | |
335 | /* Target is a member of an archive */ | 335 | /* Target is a member of an archive */ | |
336 | /* XXX: How does this differ from OP_ARCHV? */ | 336 | /* XXX: How does this differ from OP_ARCHV? */ | |
337 | OP_MEMBER = 1 << 29, | 337 | OP_MEMBER = 1 << 29, | |
338 | /* | 338 | /* | |
339 | * The node is a library, its name has the form "-l<libname>". | 339 | * The node is a library, its name has the form "-l<libname>". | |
340 | */ | 340 | */ | |
341 | OP_LIB = 1 << 28, | 341 | OP_LIB = 1 << 28, | |
342 | /* | 342 | /* | |
343 | * The node is an archive member, its name has the form | 343 | * The node is an archive member, its name has the form | |
344 | * "archive(member)". | 344 | * "archive(member)". | |
345 | */ | 345 | */ | |
346 | /* XXX: How does this differ from OP_MEMBER? */ | 346 | /* XXX: How does this differ from OP_MEMBER? */ | |
347 | OP_ARCHV = 1 << 27, | 347 | OP_ARCHV = 1 << 27, | |
348 | /* | 348 | /* | |
349 | * Target has all the commands it should. Used when parsing to catch | 349 | * Target has all the commands it should. Used when parsing to catch | |
350 | * multiple command groups for a target. Only applies to the | 350 | * multiple command groups for a target. Only applies to the | |
351 | * dependency operators ':' and '!', but not to '::'. | 351 | * dependency operators ':' and '!', but not to '::'. | |
352 | */ | 352 | */ | |
353 | OP_HAS_COMMANDS = 1 << 26, | 353 | OP_HAS_COMMANDS = 1 << 26, | |
354 | /* | 354 | /* | |
355 | * The special command "..." has been seen. All further commands from | 355 | * The special command "..." has been seen. All further commands from | |
356 | * this node will be saved on the .END node instead, to be executed | 356 | * this node will be saved on the .END node instead, to be executed | |
357 | * at the very end. | 357 | * at the very end. | |
358 | */ | 358 | */ | |
359 | OP_SAVE_CMDS = 1 << 25, | 359 | OP_SAVE_CMDS = 1 << 25, | |
360 | /* | 360 | /* | |
361 | * Already processed by Suff_FindDeps, to find dependencies from | 361 | * Already processed by Suff_FindDeps, to find dependencies from | |
362 | * suffix transformation rules. | 362 | * suffix transformation rules. | |
363 | */ | 363 | */ | |
364 | OP_DEPS_FOUND = 1 << 24, | 364 | OP_DEPS_FOUND = 1 << 24, | |
365 | /* Node found while expanding .ALLSRC */ | 365 | /* Node found while expanding .ALLSRC */ | |
366 | OP_MARK = 1 << 23 | 366 | OP_MARK = 1 << 23 | |
367 | } GNodeType; | 367 | } GNodeType; | |
368 | 368 | |||
369 | typedef struct GNodeFlags { | 369 | typedef struct GNodeFlags { | |
370 | /* this target needs to be (re)made */ | 370 | /* this target needs to be (re)made */ | |
371 | bool remake:1; | 371 | bool remake:1; | |
372 | /* children of this target were made */ | 372 | /* children of this target were made */ | |
373 | bool childMade:1; | 373 | bool childMade:1; | |
374 | /* children don't exist, and we pretend made */ | 374 | /* children don't exist, and we pretend made */ | |
375 | bool force:1; | 375 | bool force:1; | |
376 | /* Set by Make_ProcessWait() */ | 376 | /* Set by Make_ProcessWait() */ | |
377 | bool doneWait:1; | 377 | bool doneWait:1; | |
378 | /* Build requested by .ORDER processing */ | 378 | /* Build requested by .ORDER processing */ | |
379 | bool doneOrder:1; | 379 | bool doneOrder:1; | |
380 | /* Node created from .depend */ | 380 | /* Node created from .depend */ | |
381 | bool fromDepend:1; | 381 | bool fromDepend:1; | |
382 | /* We do it once only */ | 382 | /* We do it once only */ | |
383 | bool doneAllsrc:1; | 383 | bool doneAllsrc:1; | |
384 | /* Used by MakePrintStatus */ | 384 | /* Used by MakePrintStatus */ | |
385 | bool cycle:1; | 385 | bool cycle:1; | |
386 | /* Used by MakePrintStatus */ | 386 | /* Used by MakePrintStatus */ | |
387 | bool doneCycle:1; | 387 | bool doneCycle:1; | |
388 | } GNodeFlags; | 388 | } GNodeFlags; | |
389 | 389 | |||
390 | typedef struct List StringList; | 390 | typedef struct List StringList; | |
391 | typedef struct ListNode StringListNode; | 391 | typedef struct ListNode StringListNode; | |
392 | 392 | |||
393 | typedef struct List GNodeList; | 393 | typedef struct List GNodeList; | |
394 | typedef struct ListNode GNodeListNode; | 394 | typedef struct ListNode GNodeListNode; | |
395 | 395 | |||
396 | typedef struct SearchPath { | 396 | typedef struct SearchPath { | |
397 | List /* of CachedDir */ dirs; | 397 | List /* of CachedDir */ dirs; | |
398 | } SearchPath; | 398 | } SearchPath; | |
399 | 399 | |||
400 | /* | 400 | /* | |
401 | * A graph node represents a target that can possibly be made, including its | 401 | * A graph node represents a target that can possibly be made, including its | |
402 | * relation to other targets and a lot of other details. | 402 | * relation to other targets. | |
403 | */ | 403 | */ | |
404 | typedef struct GNode { | 404 | typedef struct GNode { | |
405 | /* The target's name, such as "clean" or "make.c" */ | 405 | /* The target's name, such as "clean" or "make.c" */ | |
406 | char *name; | 406 | char *name; | |
407 | /* The unexpanded name of a .USE node */ | 407 | /* The unexpanded name of a .USE node */ | |
408 | char *uname; | 408 | char *uname; | |
409 | /* | 409 | /* | |
410 | * The full pathname of the file belonging to the target. | 410 | * The full pathname of the file belonging to the target. | |
411 | * | 411 | * | |
412 | * XXX: What about .PHONY targets? These don't have an associated | 412 | * XXX: What about .PHONY targets? These don't have an associated | |
413 | * path. | 413 | * path. | |
414 | */ | 414 | */ | |
415 | char *path; | 415 | char *path; | |
416 | 416 | |||
417 | /* | 417 | /* | |
418 | * The type of operator used to define the sources (see the OP flags | 418 | * The type of operator used to define the sources (see the OP flags | |
419 | * below). | 419 | * below). | |
420 | * | 420 | * | |
421 | * XXX: This looks like a wild mixture of type and flags. | 421 | * XXX: This looks like a wild mixture of type and flags. | |
422 | */ | 422 | */ | |
423 | GNodeType type; | 423 | GNodeType type; | |
424 | GNodeFlags flags; | 424 | GNodeFlags flags; | |
425 | 425 | |||
426 | /* The state of processing on this node */ | 426 | /* The state of processing on this node */ | |
427 | GNodeMade made; | 427 | GNodeMade made; | |
428 | /* The number of unmade children */ | 428 | /* The number of unmade children */ | |
429 | int unmade; | 429 | int unmade; | |
430 | 430 | |||
431 | /* | 431 | /* | |
432 | * The modification time; 0 means the node does not have a | 432 | * The modification time; 0 means the node does not have a | |
433 | * corresponding file; see GNode_IsOODate. | 433 | * corresponding file; see GNode_IsOODate. | |
434 | */ | 434 | */ | |
435 | time_t mtime; | 435 | time_t mtime; | |
436 | struct GNode *youngestChild; | 436 | struct GNode *youngestChild; | |
437 | 437 | |||
438 | /* | 438 | /* | |
439 | * The GNodes for which this node is an implied source. May be empty. | 439 | * The GNodes for which this node is an implied source. May be empty. | |
440 | * For example, when there is an inference rule for .c.o, the node | 440 | * For example, when there is an inference rule for .c.o, the node | |
441 | * for file.c has the node for file.o in this list. | 441 | * for file.c has the node for file.o in this list. | |
442 | */ | 442 | */ | |
443 | GNodeList implicitParents; | 443 | GNodeList implicitParents; | |
444 | 444 | |||
445 | /* | 445 | /* | |
446 | * The nodes that depend on this one, or in other words, the nodes | 446 | * The nodes that depend on this one, or in other words, the nodes | |
447 | * for which this is a source. | 447 | * for which this is a source. | |
448 | */ | 448 | */ | |
449 | GNodeList parents; | 449 | GNodeList parents; | |
450 | /* The nodes on which this one depends. */ | 450 | /* The nodes on which this one depends. */ | |
451 | GNodeList children; | 451 | GNodeList children; | |
452 | 452 | |||
453 | /* | 453 | /* | |
454 | * .ORDER nodes we need made. The nodes that must be made (if they're | 454 | * .ORDER nodes we need made. The nodes that must be made (if they're | |
455 | * made) before this node can be made, but that do not enter into the | 455 | * made) before this node can be made, but that do not enter into the | |
456 | * datedness of this node. | 456 | * datedness of this node. | |
457 | */ | 457 | */ | |
458 | GNodeList order_pred; | 458 | GNodeList order_pred; | |
459 | /* | 459 | /* | |
460 | * .ORDER nodes who need us. The nodes that must be made (if they're | 460 | * .ORDER nodes who need us. The nodes that must be made (if they're | |
461 | * made at all) after this node is made, but that do not depend on | 461 | * made at all) after this node is made, but that do not depend on | |
462 | * this node, in the normal sense. | 462 | * this node, in the normal sense. | |
463 | */ | 463 | */ | |
464 | GNodeList order_succ; | 464 | GNodeList order_succ; | |
465 | 465 | |||
466 | /* | 466 | /* | |
467 | * Other nodes of the same name, for targets that were defined using | 467 | * Other nodes of the same name, for targets that were defined using | |
468 | * the '::' dependency operator (OP_DOUBLEDEP). | 468 | * the '::' dependency operator (OP_DOUBLEDEP). | |
469 | */ | 469 | */ | |
470 | GNodeList cohorts; | 470 | GNodeList cohorts; | |
471 | /* The "#n" suffix for this cohort, or "" for other nodes */ | 471 | /* The "#n" suffix for this cohort, or "" for other nodes */ | |
472 | char cohort_num[8]; | 472 | char cohort_num[8]; | |
473 | /* The number of unmade instances on the cohorts list */ | 473 | /* The number of unmade instances on the cohorts list */ | |
474 | int unmade_cohorts; | 474 | int unmade_cohorts; | |
475 | /* | 475 | /* | |
476 | * Pointer to the first instance of a '::' node; only set when on a | 476 | * Pointer to the first instance of a '::' node; only set when on a | |
477 | * cohorts list | 477 | * cohorts list | |
478 | */ | 478 | */ | |
479 | struct GNode *centurion; | 479 | struct GNode *centurion; | |
480 | 480 | |||
481 | /* Last time (sequence number) we tried to make this node */ | 481 | /* Last time (sequence number) we tried to make this node */ | |
482 | unsigned int checked_seqno; | 482 | unsigned int checked_seqno; | |
483 | 483 | |||
484 | /* | 484 | /* | |
485 | * The "local" variables that are specific to this target and this | 485 | * The "local" variables that are specific to this target and this | |
486 | * target only, such as $@, $<, $?. | 486 | * target only, such as $@, $<, $?. | |
487 | * | 487 | * | |
488 | * Also used for the global variable scopes SCOPE_GLOBAL, | 488 | * Also used for the global variable scopes SCOPE_GLOBAL, | |
489 | * SCOPE_CMDLINE, SCOPE_INTERNAL, which contain variables with | 489 | * SCOPE_CMDLINE, SCOPE_INTERNAL, which contain variables with | |
490 | * arbitrary names. | 490 | * arbitrary names. | |
491 | */ | 491 | */ | |
492 | HashTable /* of Var pointer */ vars; | 492 | HashTable /* of Var pointer */ vars; | |
493 | 493 | |||
494 | /* The commands to be given to a shell to create this target. */ | 494 | /* The commands to be given to a shell to create this target. */ | |
495 | StringList commands; | 495 | StringList commands; | |
496 | 496 | |||
497 | /* | 497 | /* | |
498 | * Suffix for the node (determined by Suff_FindDeps and opaque to | 498 | * Suffix for the node (determined by Suff_FindDeps and opaque to | |
499 | * everyone but the Suff module) | 499 | * everyone but the Suff module) | |
500 | */ | 500 | */ | |
501 | struct Suffix *suffix; | 501 | struct Suffix *suffix; | |
502 | 502 | |||
503 | /* Filename where the GNode got defined, unlimited lifetime */ | 503 | /* Filename where the GNode got defined, unlimited lifetime */ | |
504 | const char *fname; | 504 | const char *fname; | |
505 | /* Line number where the GNode got defined, 1-based */ | 505 | /* Line number where the GNode got defined, 1-based */ | |
506 | unsigned lineno; | 506 | unsigned lineno; | |
507 | int exit_status; | 507 | int exit_status; | |
508 | } GNode; | 508 | } GNode; | |
509 | 509 | |||
510 | /* | 510 | /* | |
511 | * Keep track of whether to include <posix.mk> when parsing the line | 511 | * Keep track of whether to include <posix.mk> when parsing the line | |
512 | * '.POSIX:'. | 512 | * '.POSIX:'. | |
513 | */ | 513 | */ | |
514 | extern enum PosixState { | 514 | extern enum PosixState { | |
515 | PS_NOT_YET, | 515 | PS_NOT_YET, | |
516 | PS_MAYBE_NEXT_LINE, | 516 | PS_MAYBE_NEXT_LINE, | |
517 | PS_NOW_OR_NEVER, | 517 | PS_NOW_OR_NEVER, | |
518 | PS_TOO_LATE | 518 | PS_TOO_LATE | |
519 | } posix_state; | 519 | } posix_state; | |
520 | 520 | |||
521 | /* Error levels for diagnostics during parsing. */ | 521 | /* Error levels for diagnostics during parsing. */ | |
522 | typedef enum ParseErrorLevel { | 522 | typedef enum ParseErrorLevel { | |
523 | /* | 523 | /* | |
524 | * Exit when the current top-level makefile has been parsed | 524 | * Exit when the current top-level makefile has been parsed | |
525 | * completely. | 525 | * completely. | |
526 | */ | 526 | */ | |
527 | PARSE_FATAL = 1, | 527 | PARSE_FATAL = 1, | |
528 | /* Print "warning"; may be upgraded to fatal by the -w option. */ | 528 | /* Print "warning"; may be upgraded to fatal by the -w option. */ | |
529 | PARSE_WARNING, | 529 | PARSE_WARNING, | |
530 | /* Informational, mainly used during development of makefiles. */ | 530 | /* Informational, mainly used during development of makefiles. */ | |
531 | PARSE_INFO | 531 | PARSE_INFO | |
532 | } ParseErrorLevel; | 532 | } ParseErrorLevel; | |
533 | 533 | |||
534 | /* | 534 | /* | |
535 | * Values returned by Cond_EvalLine and Cond_EvalCondition. | 535 | * Values returned by Cond_EvalLine and Cond_EvalCondition. | |
536 | */ | 536 | */ | |
537 | typedef enum CondResult { | 537 | typedef enum CondResult { | |
538 | CR_TRUE, /* Parse the next lines */ | 538 | CR_TRUE, /* Parse the next lines */ | |
539 | CR_FALSE, /* Skip the next lines */ | 539 | CR_FALSE, /* Skip the next lines */ | |
540 | CR_ERROR /* Unknown directive or parse error */ | 540 | CR_ERROR /* Unknown directive or parse error */ | |
541 | } CondResult; | 541 | } CondResult; | |
542 | 542 | |||
543 | typedef struct { | 543 | typedef struct { | |
544 | enum GuardKind { | 544 | enum GuardKind { | |
545 | GK_VARIABLE, | 545 | GK_VARIABLE, | |
546 | GK_TARGET | 546 | GK_TARGET | |
547 | } kind; | 547 | } kind; | |
548 | char *name; | 548 | char *name; | |
549 | } Guard; | 549 | } Guard; | |
550 | 550 | |||
551 | /* Names of the variables that are "local" to a specific target. */ | 551 | /* Names of the variables that are "local" to a specific target. */ | |
552 | #define TARGET "@" /* Target of dependency */ | 552 | #define TARGET "@" /* Target of dependency */ | |
553 | #define OODATE "?" /* All out-of-date sources */ | 553 | #define OODATE "?" /* All out-of-date sources */ | |
554 | #define ALLSRC ">" /* All sources */ | 554 | #define ALLSRC ">" /* All sources */ | |
555 | #define IMPSRC "<" /* Source implied by transformation */ | 555 | #define IMPSRC "<" /* Source implied by transformation */ | |
556 | #define PREFIX "*" /* Common prefix */ | 556 | #define PREFIX "*" /* Common prefix */ | |
557 | #define ARCHIVE "!" /* Archive in "archive(member)" syntax */ | 557 | #define ARCHIVE "!" /* Archive in "archive(member)" syntax */ | |
558 | #define MEMBER "%" /* Member in "archive(member)" syntax */ | 558 | #define MEMBER "%" /* Member in "archive(member)" syntax */ | |
559 | 559 | |||
560 | /* | 560 | /* | |
561 | * Global Variables | 561 | * Global Variables | |
562 | */ | 562 | */ | |
563 | 563 | |||
564 | /* True if every target is precious */ | 564 | /* True if every target is precious */ | |
565 | extern bool allPrecious; | 565 | extern bool allPrecious; | |
566 | /* True if failed targets should be deleted */ | 566 | /* True if failed targets should be deleted */ | |
567 | extern bool deleteOnError; | 567 | extern bool deleteOnError; | |
568 | /* true while processing .depend */ | 568 | /* true while processing .depend */ | |
569 | extern bool doing_depend; | 569 | extern bool doing_depend; | |
570 | /* .DEFAULT rule */ | 570 | /* .DEFAULT rule */ | |
571 | extern GNode *defaultNode; | 571 | extern GNode *defaultNode; | |
572 | 572 | |||
573 | /* | 573 | /* | |
574 | * Variables defined internally by make which should not override those set | 574 | * Variables defined internally by make which should not override those set | |
575 | * by makefiles. | 575 | * by makefiles. | |
576 | */ | 576 | */ | |
577 | extern GNode *SCOPE_INTERNAL; | 577 | extern GNode *SCOPE_INTERNAL; | |
578 | /* Variables defined in a global scope, e.g in the makefile itself. */ | 578 | /* Variables defined in a global scope, e.g in the makefile itself. */ | |
579 | extern GNode *SCOPE_GLOBAL; | 579 | extern GNode *SCOPE_GLOBAL; | |
580 | /* Variables defined on the command line. */ | 580 | /* Variables defined on the command line. */ | |
581 | extern GNode *SCOPE_CMDLINE; | 581 | extern GNode *SCOPE_CMDLINE; | |
582 | 582 | |||
583 | /* | 583 | /* | |
584 | * Value returned by Var_Parse when an error is encountered. It actually | 584 | * Value returned by Var_Parse when an error is encountered. It points to an | |
585 | * points to an empty string, so naive callers needn't worry about it. | 585 | * empty string, so naive callers needn't worry about it. | |
586 | */ | 586 | */ | |
587 | extern char var_Error[]; | 587 | extern char var_Error[]; | |
588 | 588 | |||
589 | /* The time at the start of this whole process */ | 589 | /* The time at the start of this whole process */ | |
590 | extern time_t now; | 590 | extern time_t now; | |
591 | 591 | |||
592 | /* | 592 | /* | |
593 | * The list of directories to search when looking for targets (set by the | 593 | * The list of directories to search when looking for targets (set by the | |
594 | * special target .PATH). | 594 | * special target .PATH). | |
595 | */ | 595 | */ | |
596 | extern SearchPath dirSearchPath; | 596 | extern SearchPath dirSearchPath; | |
597 | /* Used for .include "...". */ | 597 | /* Used for .include "...". */ | |
598 | extern SearchPath *parseIncPath; | 598 | extern SearchPath *parseIncPath; | |
599 | /* | 599 | /* | |
600 | * Used for .include <...>, for the built-in sys.mk and for makefiles from | 600 | * Used for .include <...>, for the built-in sys.mk and for makefiles from | |
601 | * the command line arguments. | 601 | * the command line arguments. | |
602 | */ | 602 | */ | |
603 | extern SearchPath *sysIncPath; | 603 | extern SearchPath *sysIncPath; | |
604 | /* The default for sysIncPath. */ | 604 | /* The default for sysIncPath. */ | |
605 | extern SearchPath *defSysIncPath; | 605 | extern SearchPath *defSysIncPath; | |
606 | 606 | |||
607 | /* Startup directory */ | 607 | /* Startup directory */ | |
608 | extern char curdir[]; | 608 | extern char curdir[]; | |
609 | /* The basename of the program name, suffixed with [n] for sub-makes. */ | 609 | /* The basename of the program name, suffixed with [n] for sub-makes. */ | |
610 | extern const char *progname; | 610 | extern const char *progname; | |
611 | extern int makelevel; | 611 | extern int makelevel; | |
612 | /* Name of the .depend makefile */ | 612 | /* Name of the .depend makefile */ | |
613 | extern char *makeDependfile; | 613 | extern char *makeDependfile; | |
614 | /* If we replaced environ, this will be non-NULL. */ | 614 | /* If we replaced environ, this will be non-NULL. */ | |
615 | extern char **savedEnv; | 615 | extern char **savedEnv; | |
616 | extern GNode *mainNode; | 616 | extern GNode *mainNode; | |
617 | 617 | |||
618 | extern pid_t myPid; | 618 | extern pid_t myPid; | |
619 | 619 | |||
620 | #define MAKEFLAGS ".MAKEFLAGS" | 620 | #define MAKEFLAGS ".MAKEFLAGS" | |
621 | #ifndef MAKE_LEVEL_ENV | 621 | #ifndef MAKE_LEVEL_ENV | |
622 | # define MAKE_LEVEL_ENV "MAKELEVEL" | 622 | # define MAKE_LEVEL_ENV "MAKELEVEL" | |
623 | #endif | 623 | #endif | |
624 | 624 | |||
625 | typedef struct DebugFlags { | 625 | typedef struct DebugFlags { | |
626 | bool DEBUG_ARCH:1; | 626 | bool DEBUG_ARCH:1; | |
627 | bool DEBUG_COND:1; | 627 | bool DEBUG_COND:1; | |
628 | bool DEBUG_CWD:1; | 628 | bool DEBUG_CWD:1; | |
629 | bool DEBUG_DIR:1; | 629 | bool DEBUG_DIR:1; | |
630 | bool DEBUG_ERROR:1; | 630 | bool DEBUG_ERROR:1; | |
631 | bool DEBUG_FOR:1; | 631 | bool DEBUG_FOR:1; | |
632 | bool DEBUG_GRAPH1:1; | 632 | bool DEBUG_GRAPH1:1; | |
633 | bool DEBUG_GRAPH2:1; | 633 | bool DEBUG_GRAPH2:1; | |
634 | bool DEBUG_GRAPH3:1; | 634 | bool DEBUG_GRAPH3:1; | |
635 | bool DEBUG_HASH:1; | 635 | bool DEBUG_HASH:1; | |
636 | bool DEBUG_JOB:1; | 636 | bool DEBUG_JOB:1; | |
637 | bool DEBUG_LOUD:1; | 637 | bool DEBUG_LOUD:1; | |
638 | bool DEBUG_MAKE:1; | 638 | bool DEBUG_MAKE:1; | |
639 | bool DEBUG_META:1; | 639 | bool DEBUG_META:1; | |
640 | bool DEBUG_PARSE:1; | 640 | bool DEBUG_PARSE:1; | |
641 | bool DEBUG_SCRIPT:1; | 641 | bool DEBUG_SCRIPT:1; | |
642 | bool DEBUG_SHELL:1; | 642 | bool DEBUG_SHELL:1; | |
643 | bool DEBUG_SUFF:1; | 643 | bool DEBUG_SUFF:1; | |
644 | bool DEBUG_TARG:1; | 644 | bool DEBUG_TARG:1; | |
645 | bool DEBUG_VAR:1; | 645 | bool DEBUG_VAR:1; | |
646 | } DebugFlags; | 646 | } DebugFlags; | |
647 | 647 | |||
648 | #define CONCAT(a, b) a##b | 648 | #define CONCAT(a, b) a##b | |
649 | 649 | |||
650 | #define DEBUG(module) (opts.debug.CONCAT(DEBUG_, module)) | 650 | #define DEBUG(module) (opts.debug.CONCAT(DEBUG_, module)) | |
651 | 651 | |||
652 | void debug_printf(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2); | 652 | void debug_printf(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2); | |
653 | 653 | |||
654 | #define DEBUG_IMPL(module, args) \ | 654 | #define DEBUG_IMPL(module, args) \ | |
655 | do { \ | 655 | do { \ | |
656 | if (DEBUG(module)) \ | 656 | if (DEBUG(module)) \ | |
657 | debug_printf args; \ | 657 | debug_printf args; \ | |
658 | } while (false) | 658 | } while (false) | |
659 | 659 | |||
660 | #define DEBUG0(module, fmt) \ | 660 | #define DEBUG0(module, fmt) \ | |
661 | DEBUG_IMPL(module, (fmt)) | 661 | DEBUG_IMPL(module, (fmt)) | |
662 | #define DEBUG1(module, fmt, arg1) \ | 662 | #define DEBUG1(module, fmt, arg1) \ | |
663 | DEBUG_IMPL(module, (fmt, arg1)) | 663 | DEBUG_IMPL(module, (fmt, arg1)) | |
664 | #define DEBUG2(module, fmt, arg1, arg2) \ | 664 | #define DEBUG2(module, fmt, arg1, arg2) \ | |
665 | DEBUG_IMPL(module, (fmt, arg1, arg2)) | 665 | DEBUG_IMPL(module, (fmt, arg1, arg2)) | |
666 | #define DEBUG3(module, fmt, arg1, arg2, arg3) \ | 666 | #define DEBUG3(module, fmt, arg1, arg2, arg3) \ | |
667 | DEBUG_IMPL(module, (fmt, arg1, arg2, arg3)) | 667 | DEBUG_IMPL(module, (fmt, arg1, arg2, arg3)) | |
668 | #define DEBUG4(module, fmt, arg1, arg2, arg3, arg4) \ | 668 | #define DEBUG4(module, fmt, arg1, arg2, arg3, arg4) \ | |
669 | DEBUG_IMPL(module, (fmt, arg1, arg2, arg3, arg4)) | 669 | DEBUG_IMPL(module, (fmt, arg1, arg2, arg3, arg4)) | |
670 | #define DEBUG5(module, fmt, arg1, arg2, arg3, arg4, arg5) \ | 670 | #define DEBUG5(module, fmt, arg1, arg2, arg3, arg4, arg5) \ | |
671 | DEBUG_IMPL(module, (fmt, arg1, arg2, arg3, arg4, arg5)) | 671 | DEBUG_IMPL(module, (fmt, arg1, arg2, arg3, arg4, arg5)) | |
672 | 672 | |||
673 | typedef enum PrintVarsMode { | 673 | typedef enum PrintVarsMode { | |
674 | PVM_NONE, | 674 | PVM_NONE, | |
675 | PVM_UNEXPANDED, | 675 | PVM_UNEXPANDED, | |
676 | PVM_EXPANDED | 676 | PVM_EXPANDED | |
677 | } PrintVarsMode; | 677 | } PrintVarsMode; | |
678 | 678 | |||
679 | /* Command line options */ | 679 | /* Command line options */ | |
680 | typedef struct CmdOpts { | 680 | typedef struct CmdOpts { | |
681 | /* -B: whether we are make compatible */ | 681 | /* -B: whether to be compatible to traditional make */ | |
682 | bool compatMake; | 682 | bool compatMake; | |
683 | 683 | |||
684 | /* | 684 | /* | |
685 | * -d: debug control: There is one bit per module. It is up to the | 685 | * -d: debug control: There is one flag per module. It is up to the | |
686 | * module what debug information to print. | 686 | * module what debug information to print. | |
687 | */ | 687 | */ | |
688 | DebugFlags debug; | 688 | DebugFlags debug; | |
689 | 689 | |||
690 | /* -df: debug output is written here - default stderr */ | 690 | /* -df: debug output is written here - default stderr */ | |
691 | FILE *debug_file; | 691 | FILE *debug_file; | |
692 | 692 | |||
693 | /* | 693 | /* | |
694 | * -dL: lint mode | 694 | * -dL: lint mode | |
695 | * | 695 | * | |
696 | * Runs make in strict mode, with additional checks and better error | 696 | * Runs make in strict mode, with additional checks and better error | |
697 | * handling. | 697 | * handling. | |
698 | */ | 698 | */ | |
699 | bool strict; | 699 | bool strict; | |
700 | 700 | |||
701 | /* -dV: for the -V option, print unexpanded variable values */ | 701 | /* -dV: for the -V option, print unexpanded variable values */ | |
702 | bool debugVflag; | 702 | bool debugVflag; | |
703 | 703 | |||
704 | /* -e: check environment variables before global variables */ | 704 | /* -e: check environment variables before global variables */ | |
705 | bool checkEnvFirst; | 705 | bool checkEnvFirst; | |
706 | 706 | |||
707 | /* -f: the makefiles to read */ | 707 | /* -f: the makefiles to read */ | |
708 | StringList makefiles; | 708 | StringList makefiles; | |
709 | 709 | |||
710 | /* -i: if true, ignore all errors from shell commands */ | 710 | /* -i: if true, ignore all errors from shell commands */ | |
711 | bool ignoreErrors; | 711 | bool ignoreErrors; | |
712 | 712 | |||
713 | /* | 713 | /* | |
714 | * -j: the maximum number of jobs that can run in parallel; this is | 714 | * -j: the maximum number of jobs that can run in parallel; this is | |
715 | * coordinated with the submakes | 715 | * coordinated with the submakes | |
716 | */ | 716 | */ | |
717 | int maxJobs; | 717 | int maxJobs; | |
718 | 718 | |||
719 | /* | 719 | /* | |
720 | * -k: if true and an error occurs while making a node, continue | 720 | * -k: if true and an error occurs while making a node, continue | |
721 | * making nodes that do not depend on the erroneous node | 721 | * making nodes that do not depend on the erroneous node | |
722 | */ | 722 | */ | |
723 | bool keepgoing; | 723 | bool keepgoing; | |
724 | 724 | |||
725 | /* -N: execute no commands from the targets */ | 725 | /* -N: execute no commands from the targets */ | |
726 | bool noRecursiveExecute; | 726 | bool noRecursiveExecute; | |
727 | 727 | |||
728 | /* -n: execute almost no commands from the targets */ | 728 | /* -n: execute almost no commands from the targets */ | |
729 | bool noExecute; | 729 | bool noExecute; | |
730 | 730 | |||
731 | /* | 731 | /* | |
732 | * -q: if true, do not really make anything, just see if the targets | 732 | * -q: if true, do not really make anything, just see if the targets | |
733 | * are out-of-date | 733 | * are out-of-date | |
734 | */ | 734 | */ | |
735 | bool query; | 735 | bool query; | |
736 | 736 | |||
737 | /* -r: raw mode, do not load the builtin rules. */ | 737 | /* -r: raw mode, do not load the builtin rules. */ | |
738 | bool noBuiltins; | 738 | bool noBuiltins; | |
739 | 739 | |||
740 | /* -s: don't echo the shell commands before executing them */ | 740 | /* -s: don't echo the shell commands before executing them */ | |
741 | bool silent; | 741 | bool silent; | |
742 | 742 | |||
743 | /* | 743 | /* | |
744 | * -t: touch the targets if they are out-of-date, but don't actually | 744 | * -t: touch the targets if they are out-of-date, but don't actually | |
745 | * make them | 745 | * make them | |
746 | */ | 746 | */ | |
747 | bool touch; | 747 | bool touch; | |
748 | 748 | |||
749 | /* -[Vv]: print expanded or unexpanded selected variables */ | 749 | /* -[Vv]: print expanded or unexpanded selected variables */ | |
750 | PrintVarsMode printVars; | 750 | PrintVarsMode printVars; | |
751 | /* -[Vv]: the variables to print */ | 751 | /* -[Vv]: the variables to print */ | |
752 | StringList variables; | 752 | StringList variables; | |
753 | 753 | |||
754 | /* -W: if true, makefile parsing warnings are treated as errors */ | 754 | /* -W: if true, makefile parsing warnings are treated as errors */ | |
755 | bool parseWarnFatal; | 755 | bool parseWarnFatal; | |
756 | 756 | |||
757 | /* -w: print 'Entering' and 'Leaving' for submakes */ | 757 | /* -w: print 'Entering' and 'Leaving' for submakes */ | |
758 | bool enterFlag; | 758 | bool enterFlag; | |
759 | 759 | |||
760 | /* | 760 | /* | |
761 | * -X: if true, do not export variables set on the command line to | 761 | * -X: if true, do not export variables set on the command line to | |
762 | * the environment. | 762 | * the environment. | |
763 | */ | 763 | */ | |
764 | bool varNoExportEnv; | 764 | bool varNoExportEnv; | |
765 | 765 | |||
766 | /* | 766 | /* | |
767 | * The target names specified on the command line. Used to resolve | 767 | * The target names specified on the command line. Used to resolve | |
768 | * .if make(...) statements. | 768 | * .if make(...) statements. | |
769 | */ | 769 | */ | |
770 | StringList create; | 770 | StringList create; | |
771 | 771 | |||
772 | /* | 772 | /* | |
773 | * Randomize the order in which the targets from toBeMade are made, | 773 | * Randomize the order in which the targets from toBeMade are made, | |
774 | * to catch undeclared dependencies. | 774 | * to catch undeclared dependencies. | |
775 | */ | 775 | */ | |
776 | bool randomizeTargets; | 776 | bool randomizeTargets; | |
777 | } CmdOpts; | 777 | } CmdOpts; | |
778 | 778 | |||
779 | extern CmdOpts opts; | 779 | extern CmdOpts opts; | |
780 | extern bool forceJobs; | 780 | extern bool forceJobs; | |
781 | extern char **environ; | 781 | extern char **environ; | |
782 | 782 | |||
783 | /* arch.c */ | 783 | /* arch.c */ | |
784 | void Arch_Init(void); | 784 | void Arch_Init(void); | |
785 | void Arch_End(void); | 785 | void Arch_End(void); | |
786 | 786 | |||
787 | bool Arch_ParseArchive(char **, GNodeList *, GNode *); | 787 | bool Arch_ParseArchive(char **, GNodeList *, GNode *); | |
788 | void Arch_Touch(GNode *); | 788 | void Arch_Touch(GNode *); | |
789 | void Arch_TouchLib(GNode *); | 789 | void Arch_TouchLib(GNode *); | |
790 | void Arch_UpdateMTime(GNode *); | 790 | void Arch_UpdateMTime(GNode *); | |
791 | void Arch_UpdateMemberMTime(GNode *); | 791 | void Arch_UpdateMemberMTime(GNode *); | |
792 | void Arch_FindLib(GNode *, SearchPath *); | 792 | void Arch_FindLib(GNode *, SearchPath *); | |
793 | bool Arch_LibOODate(GNode *) MAKE_ATTR_USE; | 793 | bool Arch_LibOODate(GNode *) MAKE_ATTR_USE; | |
794 | bool Arch_IsLib(GNode *) MAKE_ATTR_USE; | 794 | bool Arch_IsLib(GNode *) MAKE_ATTR_USE; | |
795 | 795 | |||
796 | /* compat.c */ | 796 | /* compat.c */ | |
797 | bool Compat_RunCommand(const char *, GNode *, StringListNode *); | 797 | bool Compat_RunCommand(const char *, GNode *, StringListNode *); | |
798 | void Compat_MakeAll(GNodeList *); | 798 | void Compat_MakeAll(GNodeList *); | |
799 | void Compat_Make(GNode *, GNode *); | 799 | void Compat_Make(GNode *, GNode *); | |
800 | 800 | |||
801 | /* cond.c */ | 801 | /* cond.c */ | |
802 | extern unsigned int cond_depth; | 802 | extern unsigned int cond_depth; | |
803 | CondResult Cond_EvalCondition(const char *) MAKE_ATTR_USE; | 803 | CondResult Cond_EvalCondition(const char *) MAKE_ATTR_USE; | |
804 | CondResult Cond_EvalLine(const char *) MAKE_ATTR_USE; | 804 | CondResult Cond_EvalLine(const char *) MAKE_ATTR_USE; | |
805 | Guard *Cond_ExtractGuard(const char *) MAKE_ATTR_USE; | 805 | Guard *Cond_ExtractGuard(const char *) MAKE_ATTR_USE; | |
806 | void Cond_EndFile(void); | 806 | void Cond_EndFile(void); | |
807 | 807 | |||
808 | /* dir.c; see also dir.h */ | 808 | /* dir.c; see also dir.h */ | |
809 | 809 | |||
810 | MAKE_INLINE const char * MAKE_ATTR_USE | 810 | MAKE_INLINE const char * MAKE_ATTR_USE | |
811 | str_basename(const char *pathname) | 811 | str_basename(const char *pathname) | |
812 | { | 812 | { | |
813 | const char *lastSlash = strrchr(pathname, '/'); | 813 | const char *lastSlash = strrchr(pathname, '/'); | |
814 | return lastSlash != NULL ? lastSlash + 1 : pathname; | 814 | return lastSlash != NULL ? lastSlash + 1 : pathname; | |
815 | } | 815 | } | |
816 | 816 | |||
817 | MAKE_INLINE SearchPath * MAKE_ATTR_USE | 817 | MAKE_INLINE SearchPath * MAKE_ATTR_USE | |
818 | SearchPath_New(void) | 818 | SearchPath_New(void) | |
819 | { | 819 | { | |
820 | SearchPath *path = bmake_malloc(sizeof *path); | 820 | SearchPath *path = bmake_malloc(sizeof *path); | |
821 | Lst_Init(&path->dirs); | 821 | Lst_Init(&path->dirs); | |
822 | return path; | 822 | return path; | |
823 | } | 823 | } | |
824 | 824 | |||
825 | void SearchPath_Free(SearchPath *); | 825 | void SearchPath_Free(SearchPath *); | |
826 | 826 | |||
827 | /* for.c */ | 827 | /* for.c */ | |
828 | struct ForLoop; | 828 | struct ForLoop; | |
829 | int For_Eval(const char *) MAKE_ATTR_USE; | 829 | int For_Eval(const char *) MAKE_ATTR_USE; | |
830 | bool For_Accum(const char *, int *) MAKE_ATTR_USE; | 830 | bool For_Accum(const char *, int *) MAKE_ATTR_USE; | |
831 | void For_Run(unsigned, unsigned); | 831 | void For_Run(unsigned, unsigned); | |
832 | bool For_NextIteration(struct ForLoop *, Buffer *); | 832 | bool For_NextIteration(struct ForLoop *, Buffer *); | |
833 | char *ForLoop_Details(const struct ForLoop *); | 833 | char *ForLoop_Details(const struct ForLoop *); | |
834 | void ForLoop_Free(struct ForLoop *); | 834 | void ForLoop_Free(struct ForLoop *); | |
835 | void For_Break(struct ForLoop *); | 835 | void For_Break(struct ForLoop *); | |
836 | 836 | |||
837 | /* job.c */ | 837 | /* job.c */ | |
838 | void JobReapChild(pid_t, int, bool); | 838 | void JobReapChild(pid_t, int, bool); | |
839 | 839 | |||
840 | /* main.c */ | 840 | /* main.c */ | |
841 | void Main_ParseArgLine(const char *); | 841 | void Main_ParseArgLine(const char *); | |
842 | char *Cmd_Exec(const char *, char **) MAKE_ATTR_USE; | 842 | char *Cmd_Exec(const char *, char **) MAKE_ATTR_USE; | |
843 | void Error(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2); | 843 | void Error(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2); | |
844 | void Fatal(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2) MAKE_ATTR_DEAD; | 844 | void Fatal(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2) MAKE_ATTR_DEAD; | |
845 | void Punt(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2) MAKE_ATTR_DEAD; | 845 | void Punt(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2) MAKE_ATTR_DEAD; | |
846 | void DieHorribly(void) MAKE_ATTR_DEAD; | 846 | void DieHorribly(void) MAKE_ATTR_DEAD; | |
847 | void Finish(int) MAKE_ATTR_DEAD; | 847 | void Finish(int) MAKE_ATTR_DEAD; | |
848 | int unlink_file(const char *) MAKE_ATTR_USE; | 848 | int unlink_file(const char *) MAKE_ATTR_USE; | |
849 | void execDie(const char *, const char *); | 849 | void execDie(const char *, const char *); | |
850 | char *getTmpdir(void) MAKE_ATTR_USE; | 850 | char *getTmpdir(void) MAKE_ATTR_USE; | |
851 | bool ParseBoolean(const char *, bool) MAKE_ATTR_USE; | 851 | bool ParseBoolean(const char *, bool) MAKE_ATTR_USE; | |
852 | const char *cached_realpath(const char *, char *); | 852 | const char *cached_realpath(const char *, char *); | |
853 | bool GetBooleanExpr(const char *, bool); | 853 | bool GetBooleanExpr(const char *, bool); | |
854 | 854 | |||
855 | /* parse.c */ | 855 | /* parse.c */ | |
856 | void Parse_Init(void); | 856 | void Parse_Init(void); | |
857 | void Parse_End(void); | 857 | void Parse_End(void); | |
858 | 858 | |||
859 | void PrintLocation(FILE *, bool, const GNode *); | 859 | void PrintLocation(FILE *, bool, const GNode *); | |
860 | void PrintStackTrace(bool); | 860 | void PrintStackTrace(bool); | |
861 | void Parse_Error(ParseErrorLevel, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3); | 861 | void Parse_Error(ParseErrorLevel, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3); | |
862 | bool Parse_VarAssign(const char *, bool, GNode *) MAKE_ATTR_USE; | 862 | bool Parse_VarAssign(const char *, bool, GNode *) MAKE_ATTR_USE; | |
863 | void Parse_File(const char *, int); | 863 | void Parse_File(const char *, int); | |
864 | void Parse_PushInput(const char *, unsigned, unsigned, Buffer, | 864 | void Parse_PushInput(const char *, unsigned, unsigned, Buffer, | |
865 | struct ForLoop *); | 865 | struct ForLoop *); | |
866 | void Parse_MainName(GNodeList *); | 866 | void Parse_MainName(GNodeList *); | |
867 | int Parse_NumErrors(void) MAKE_ATTR_USE; | 867 | int Parse_NumErrors(void) MAKE_ATTR_USE; | |
868 | unsigned int CurFile_CondMinDepth(void) MAKE_ATTR_USE; | 868 | unsigned int CurFile_CondMinDepth(void) MAKE_ATTR_USE; | |
869 | void Parse_GuardElse(void); | 869 | void Parse_GuardElse(void); | |
870 | void Parse_GuardEndif(void); | 870 | void Parse_GuardEndif(void); | |
871 | 871 | |||
872 | 872 | |||
873 | /* suff.c */ | 873 | /* suff.c */ | |
874 | void Suff_Init(void); | 874 | void Suff_Init(void); | |
875 | void Suff_End(void); | 875 | void Suff_End(void); | |
876 | 876 | |||
877 | void Suff_ClearSuffixes(void); | 877 | void Suff_ClearSuffixes(void); | |
878 | bool Suff_IsTransform(const char *) MAKE_ATTR_USE; | 878 | bool Suff_IsTransform(const char *) MAKE_ATTR_USE; | |
879 | GNode *Suff_AddTransform(const char *); | 879 | GNode *Suff_AddTransform(const char *); | |
880 | void Suff_EndTransform(GNode *); | 880 | void Suff_EndTransform(GNode *); | |
881 | void Suff_AddSuffix(const char *); | 881 | void Suff_AddSuffix(const char *); | |
882 | SearchPath *Suff_GetPath(const char *) MAKE_ATTR_USE; | 882 | SearchPath *Suff_GetPath(const char *) MAKE_ATTR_USE; | |
883 | void Suff_ExtendPaths(void); | 883 | void Suff_ExtendPaths(void); | |
884 | void Suff_AddInclude(const char *); | 884 | void Suff_AddInclude(const char *); | |
885 | void Suff_AddLib(const char *); | 885 | void Suff_AddLib(const char *); | |
886 | void Suff_FindDeps(GNode *); | 886 | void Suff_FindDeps(GNode *); | |
887 | SearchPath *Suff_FindPath(GNode *) MAKE_ATTR_USE; | 887 | SearchPath *Suff_FindPath(GNode *) MAKE_ATTR_USE; | |
888 | void Suff_SetNull(const char *); | 888 | void Suff_SetNull(const char *); | |
889 | void Suff_PrintAll(void); | 889 | void Suff_PrintAll(void); | |
890 | char *Suff_NamesStr(void) MAKE_ATTR_USE; | 890 | char *Suff_NamesStr(void) MAKE_ATTR_USE; | |
891 | 891 | |||
892 | /* targ.c */ | 892 | /* targ.c */ | |
893 | void Targ_Init(void); | 893 | void Targ_Init(void); | |
894 | void Targ_End(void); | 894 | void Targ_End(void); | |
895 | 895 | |||
896 | void Targ_Stats(void); | 896 | void Targ_Stats(void); | |
897 | GNodeList *Targ_List(void) MAKE_ATTR_USE; | 897 | GNodeList *Targ_List(void) MAKE_ATTR_USE; | |
898 | GNode *GNode_New(const char *) MAKE_ATTR_USE; | 898 | GNode *GNode_New(const char *) MAKE_ATTR_USE; | |
899 | GNode *Targ_FindNode(const char *) MAKE_ATTR_USE; | 899 | GNode *Targ_FindNode(const char *) MAKE_ATTR_USE; | |
900 | GNode *Targ_GetNode(const char *) MAKE_ATTR_USE; | 900 | GNode *Targ_GetNode(const char *) MAKE_ATTR_USE; | |
901 | GNode *Targ_NewInternalNode(const char *) MAKE_ATTR_USE; | 901 | GNode *Targ_NewInternalNode(const char *) MAKE_ATTR_USE; | |
902 | GNode *Targ_GetEndNode(void); | 902 | GNode *Targ_GetEndNode(void); | |
903 | void Targ_FindList(GNodeList *, StringList *); | 903 | void Targ_FindList(GNodeList *, StringList *); | |
904 | void Targ_PrintCmds(GNode *); | 904 | void Targ_PrintCmds(GNode *); | |
905 | void Targ_PrintNode(GNode *, int); | 905 | void Targ_PrintNode(GNode *, int); | |
906 | void Targ_PrintNodes(GNodeList *, int); | 906 | void Targ_PrintNodes(GNodeList *, int); | |
907 | const char *Targ_FmtTime(time_t) MAKE_ATTR_USE; | 907 | const char *Targ_FmtTime(time_t) MAKE_ATTR_USE; | |
908 | void Targ_PrintType(GNodeType); | 908 | void Targ_PrintType(GNodeType); | |
909 | void Targ_PrintGraph(int); | 909 | void Targ_PrintGraph(int); | |
910 | void Targ_Propagate(void); | 910 | void Targ_Propagate(void); | |
911 | const char *GNodeMade_Name(GNodeMade) MAKE_ATTR_USE; | 911 | const char *GNodeMade_Name(GNodeMade) MAKE_ATTR_USE; | |
912 | 912 | |||
913 | /* var.c */ | 913 | /* var.c */ | |
914 | void Var_Init(void); | 914 | void Var_Init(void); | |
915 | void Var_End(void); | 915 | void Var_End(void); | |
916 | 916 | |||
917 | typedef enum VarEvalMode { | 917 | typedef enum VarEvalMode { | |
918 | 918 | |||
919 | /* | 919 | /* | |
920 | * Only parse the expression but don't evaluate any part of it. | 920 | * Only parse the expression but don't evaluate any part of it. | |
921 | * | 921 | * | |
922 | * TODO: Document what Var_Parse and Var_Subst return in this mode. | 922 | * TODO: Document what Var_Parse and Var_Subst return in this mode. | |
923 | * As of 2021-03-15, they return unspecified, inconsistent results. | 923 | * As of 2021-03-15, they return unspecified, inconsistent results. | |
924 | */ | 924 | */ | |
925 | VARE_PARSE_ONLY, | 925 | VARE_PARSE_ONLY, | |
926 | 926 | |||
927 | /* | 927 | /* | |
928 | * Parse text in which '${...}' and '$(...)' are not parsed as | 928 | * Parse text in which '${...}' and '$(...)' are not parsed as | |
929 | * subexpressions (with all their individual escaping rules) but | 929 | * subexpressions (with all their individual escaping rules) but | |
930 | * instead simply as text with balanced '${}' or '$()'. Other '$' | 930 | * instead simply as text with balanced '${}' or '$()'. Other '$' | |
931 | * are copied verbatim. | 931 | * are copied verbatim. | |
932 | */ | 932 | */ | |
933 | VARE_PARSE_BALANCED, | 933 | VARE_PARSE_BALANCED, | |
934 | 934 | |||
935 | /* Parse and evaluate the expression. */ | 935 | /* Parse and evaluate the expression. */ | |
936 | VARE_WANTRES, | 936 | VARE_WANTRES, | |
937 | 937 | |||
938 | /* | 938 | /* | |
939 | * Parse and evaluate the expression. It is an error if a | 939 | * Parse and evaluate the expression. It is an error if a | |
940 | * subexpression evaluates to undefined. | 940 | * subexpression evaluates to undefined. | |
941 | */ | 941 | */ | |
942 | VARE_UNDEFERR, | 942 | VARE_UNDEFERR, | |
943 | 943 | |||
944 | /* | 944 | /* | |
945 | * Parse and evaluate the expression. Keep '$$' as '$$' instead of | 945 | * Parse and evaluate the expression. Keep '$$' as '$$' instead of | |
946 | * reducing it to a single '$'. Subexpressions that evaluate to | 946 | * reducing it to a single '$'. Subexpressions that evaluate to | |
947 | * undefined expand to an empty string. | 947 | * undefined expand to an empty string. | |
948 | * | 948 | * | |
949 | * Used in variable assignments using the ':=' operator. It allows | 949 | * Used in variable assignments using the ':=' operator. It allows | |
950 | * multiple such assignments to be chained without accidentally | 950 | * multiple such assignments to be chained without accidentally | |
951 | * expanding '$$file' to '$file' in the first assignment and | 951 | * expanding '$$file' to '$file' in the first assignment and | |
952 | * interpreting it as '${f}' followed by 'ile' in the next assignment. | 952 | * interpreting it as '${f}' followed by 'ile' in the next assignment. | |
953 | */ | 953 | */ | |
954 | VARE_EVAL_KEEP_DOLLAR, | 954 | VARE_EVAL_KEEP_DOLLAR, | |
955 | 955 | |||
956 | /* | 956 | /* | |
957 | * Parse and evaluate the expression. Keep undefined variables as-is | 957 | * Parse and evaluate the expression. Keep undefined variables as-is | |
958 | * instead of expanding them to an empty string. | 958 | * instead of expanding them to an empty string. | |
959 | * | 959 | * | |
960 | * Example for a ':=' assignment: | 960 | * Example for a ':=' assignment: | |
961 | * CFLAGS = $(.INCLUDES) | 961 | * CFLAGS = $(.INCLUDES) | |
962 | * CFLAGS := -I.. $(CFLAGS) | 962 | * CFLAGS := -I.. $(CFLAGS) | |
963 | * # If .INCLUDES (an undocumented special variable, by the | 963 | * # If .INCLUDES (an undocumented special variable, by the | |
964 | * # way) is still undefined, the updated CFLAGS becomes | 964 | * # way) is still undefined, the updated CFLAGS becomes | |
965 | * # "-I.. $(.INCLUDES)". | 965 | * # "-I.. $(.INCLUDES)". | |
966 | */ | 966 | */ | |
967 | VARE_EVAL_KEEP_UNDEF, | 967 | VARE_EVAL_KEEP_UNDEF, | |
968 | 968 | |||
969 | /* | 969 | /* | |
970 | * Parse and evaluate the expression. Keep '$$' as '$$' and preserve | 970 | * Parse and evaluate the expression. Keep '$$' as '$$' and preserve | |
971 | * undefined subexpressions. | 971 | * undefined subexpressions. | |
972 | */ | 972 | */ | |
973 | VARE_KEEP_DOLLAR_UNDEF | 973 | VARE_KEEP_DOLLAR_UNDEF | |
974 | } VarEvalMode; | 974 | } VarEvalMode; | |
975 | 975 | |||
976 | typedef enum VarSetFlags { | 976 | typedef enum VarSetFlags { | |
977 | VAR_SET_NONE = 0, | 977 | VAR_SET_NONE = 0, | |
978 | 978 | |||
979 | /* do not export */ | 979 | /* do not export */ | |
980 | VAR_SET_NO_EXPORT = 1 << 0, | 980 | VAR_SET_NO_EXPORT = 1 << 0, | |
981 | 981 | |||
982 | /* | 982 | /* | |
983 | * Make the variable read-only. No further modification is possible, | 983 | * Make the variable read-only. No further modification is possible, | |
984 | * except for another call to Var_Set with the same flag. See the | 984 | * except for another call to Var_Set with the same flag. See the | |
985 | * special targets '.NOREADONLY' and '.READONLY'. | 985 | * special targets '.NOREADONLY' and '.READONLY'. | |
986 | */ | 986 | */ | |
987 | VAR_SET_READONLY = 1 << 1 | 987 | VAR_SET_READONLY = 1 << 1 | |
988 | } VarSetFlags; | 988 | } VarSetFlags; | |
989 | 989 | |||
990 | typedef enum VarExportMode { | 990 | typedef enum VarExportMode { | |
991 | /* .export-env */ | 991 | /* .export-env */ | |
992 | VEM_ENV, | 992 | VEM_ENV, | |
993 | /* .export: Initial export or update an already exported variable. */ | 993 | /* .export: Initial export or update an already exported variable. */ | |
994 | VEM_PLAIN, | 994 | VEM_PLAIN, | |
995 | /* .export-literal: Do not expand the variable value. */ | 995 | /* .export-literal: Do not expand the variable value. */ | |
996 | VEM_LITERAL | 996 | VEM_LITERAL | |
997 | } VarExportMode; | 997 | } VarExportMode; | |
998 | 998 | |||
999 | void Var_Delete(GNode *, const char *); | 999 | void Var_Delete(GNode *, const char *); | |
1000 | void Var_Undef(const char *); | 1000 | void Var_Undef(const char *); | |
1001 | void Var_Set(GNode *, const char *, const char *); | 1001 | void Var_Set(GNode *, const char *, const char *); | |
1002 | void Var_SetExpand(GNode *, const char *, const char *); | 1002 | void Var_SetExpand(GNode *, const char *, const char *); | |
1003 | void Var_SetWithFlags(GNode *, const char *, const char *, VarSetFlags); | 1003 | void Var_SetWithFlags(GNode *, const char *, const char *, VarSetFlags); | |
1004 | void Var_Append(GNode *, const char *, const char *); | 1004 | void Var_Append(GNode *, const char *, const char *); | |
1005 | void Var_AppendExpand(GNode *, const char *, const char *); | 1005 | void Var_AppendExpand(GNode *, const char *, const char *); | |
1006 | bool Var_Exists(GNode *, const char *) MAKE_ATTR_USE; | 1006 | bool Var_Exists(GNode *, const char *) MAKE_ATTR_USE; | |
1007 | bool Var_ExistsExpand(GNode *, const char *) MAKE_ATTR_USE; | 1007 | bool Var_ExistsExpand(GNode *, const char *) MAKE_ATTR_USE; | |
1008 | FStr Var_Value(GNode *, const char *) MAKE_ATTR_USE; | 1008 | FStr Var_Value(GNode *, const char *) MAKE_ATTR_USE; | |
1009 | const char *GNode_ValueDirect(GNode *, const char *) MAKE_ATTR_USE; | 1009 | const char *GNode_ValueDirect(GNode *, const char *) MAKE_ATTR_USE; | |
1010 | FStr Var_Parse(const char **, GNode *, VarEvalMode); | 1010 | FStr Var_Parse(const char **, GNode *, VarEvalMode); | |
1011 | char *Var_Subst(const char *, GNode *, VarEvalMode); | 1011 | char *Var_Subst(const char *, GNode *, VarEvalMode); | |
1012 | void Var_Expand(FStr *, GNode *, VarEvalMode); | 1012 | void Var_Expand(FStr *, GNode *, VarEvalMode); | |
1013 | void Var_Stats(void); | 1013 | void Var_Stats(void); | |
1014 | void Var_Dump(GNode *); | 1014 | void Var_Dump(GNode *); | |
1015 | void Var_ReexportVars(GNode *); | 1015 | void Var_ReexportVars(GNode *); | |
1016 | void Var_Export(VarExportMode, const char *); | 1016 | void Var_Export(VarExportMode, const char *); | |
1017 | void Var_ExportVars(const char *); | 1017 | void Var_ExportVars(const char *); | |
1018 | void Var_UnExport(bool, const char *); | 1018 | void Var_UnExport(bool, const char *); | |
1019 | void Var_ReadOnly(const char *, bool); | 1019 | void Var_ReadOnly(const char *, bool); | |
1020 | 1020 | |||
1021 | void Global_Set(const char *, const char *); | 1021 | void Global_Set(const char *, const char *); | |
1022 | void Global_Append(const char *, const char *); | 1022 | void Global_Append(const char *, const char *); | |
1023 | void Global_Delete(const char *); | 1023 | void Global_Delete(const char *); | |
1024 | void Global_Set_ReadOnly(const char *, const char *); | 1024 | void Global_Set_ReadOnly(const char *, const char *); | |
1025 | 1025 | |||
1026 | void EvalStack_Push(const char *, const char *, const char *); | 1026 | void EvalStack_Push(const char *, const char *, const char *); | |
1027 | void EvalStack_Pop(void); | 1027 | void EvalStack_Pop(void); | |
1028 | const char *EvalStack_Details(void); | 1028 | const char *EvalStack_Details(void); | |
1029 | 1029 | |||
1030 | /* util.c */ | 1030 | /* util.c */ | |
1031 | typedef void (*SignalProc)(int); | 1031 | typedef void (*SignalProc)(int); | |
1032 | SignalProc bmake_signal(int, SignalProc); | 1032 | SignalProc bmake_signal(int, SignalProc); | |
1033 | 1033 | |||
1034 | /* make.c */ | 1034 | /* make.c */ | |
1035 | void GNode_UpdateYoungestChild(GNode *, GNode *); | 1035 | void GNode_UpdateYoungestChild(GNode *, GNode *); | |
1036 | bool GNode_IsOODate(GNode *) MAKE_ATTR_USE; | 1036 | bool GNode_IsOODate(GNode *) MAKE_ATTR_USE; | |
1037 | void Make_ExpandUse(GNodeList *); | 1037 | void Make_ExpandUse(GNodeList *); | |
1038 | time_t Make_Recheck(GNode *) MAKE_ATTR_USE; | 1038 | time_t Make_Recheck(GNode *) MAKE_ATTR_USE; | |
1039 | void Make_HandleUse(GNode *, GNode *); | 1039 | void Make_HandleUse(GNode *, GNode *); | |
1040 | void Make_Update(GNode *); | 1040 | void Make_Update(GNode *); | |
1041 | void GNode_SetLocalVars(GNode *); | 1041 | void GNode_SetLocalVars(GNode *); | |
1042 | bool Make_Run(GNodeList *); | 1042 | bool Make_Run(GNodeList *); | |
1043 | bool shouldDieQuietly(GNode *, int) MAKE_ATTR_USE; | 1043 | bool shouldDieQuietly(GNode *, int) MAKE_ATTR_USE; | |
1044 | void PrintOnError(GNode *, const char *); | 1044 | void PrintOnError(GNode *, const char *); | |
1045 | void Main_ExportMAKEFLAGS(bool); | 1045 | void Main_ExportMAKEFLAGS(bool); | |
1046 | bool Main_SetObjdir(bool, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3); | 1046 | bool Main_SetObjdir(bool, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3); | |
1047 | int mkTempFile(const char *, char *, size_t) MAKE_ATTR_USE; | 1047 | int mkTempFile(const char *, char *, size_t) MAKE_ATTR_USE; | |
1048 | void AppendWords(StringList *, char *); | 1048 | void AppendWords(StringList *, char *); | |
1049 | void GNode_FprintDetails(FILE *, const char *, const GNode *, const char *); | 1049 | void GNode_FprintDetails(FILE *, const char *, const GNode *, const char *); | |
1050 | bool GNode_ShouldExecute(GNode *gn) MAKE_ATTR_USE; | 1050 | bool GNode_ShouldExecute(GNode *gn) MAKE_ATTR_USE; | |
1051 | 1051 | |||
1052 | /* See if the node was seen on the left-hand side of a dependency operator. */ | 1052 | /* See if the node was seen on the left-hand side of a dependency operator. */ | |
1053 | MAKE_INLINE bool MAKE_ATTR_USE | 1053 | MAKE_INLINE bool MAKE_ATTR_USE | |
1054 | GNode_IsTarget(const GNode *gn) | 1054 | GNode_IsTarget(const GNode *gn) | |
1055 | { | 1055 | { | |
1056 | return (gn->type & OP_OPMASK) != OP_NONE; | 1056 | return (gn->type & OP_OPMASK) != OP_NONE; | |
1057 | } | 1057 | } | |
1058 | 1058 | |||
1059 | MAKE_INLINE const char * MAKE_ATTR_USE | 1059 | MAKE_INLINE const char * MAKE_ATTR_USE | |
1060 | GNode_Path(const GNode *gn) | 1060 | GNode_Path(const GNode *gn) | |
1061 | { | 1061 | { | |
1062 | return gn->path != NULL ? gn->path : gn->name; | 1062 | return gn->path != NULL ? gn->path : gn->name; | |
1063 | } | 1063 | } | |
1064 | 1064 | |||
1065 | MAKE_INLINE bool MAKE_ATTR_USE | 1065 | MAKE_INLINE bool MAKE_ATTR_USE | |
1066 | GNode_IsWaitingFor(const GNode *gn) | 1066 | GNode_IsWaitingFor(const GNode *gn) | |
1067 | { | 1067 | { | |
1068 | return gn->flags.remake && gn->made <= REQUESTED; | 1068 | return gn->flags.remake && gn->made <= REQUESTED; | |
1069 | } | 1069 | } | |
1070 | 1070 | |||
1071 | MAKE_INLINE bool MAKE_ATTR_USE | 1071 | MAKE_INLINE bool MAKE_ATTR_USE | |
1072 | GNode_IsReady(const GNode *gn) | 1072 | GNode_IsReady(const GNode *gn) | |
1073 | { | 1073 | { | |
1074 | return gn->made > DEFERRED; | 1074 | return gn->made > DEFERRED; | |
1075 | } | 1075 | } | |
1076 | 1076 | |||
1077 | MAKE_INLINE bool MAKE_ATTR_USE | 1077 | MAKE_INLINE bool MAKE_ATTR_USE | |
1078 | GNode_IsDone(const GNode *gn) | 1078 | GNode_IsDone(const GNode *gn) | |
1079 | { | 1079 | { | |
1080 | return gn->made >= MADE; | 1080 | return gn->made >= MADE; | |
1081 | } | 1081 | } | |
1082 | 1082 | |||
1083 | MAKE_INLINE bool MAKE_ATTR_USE | 1083 | MAKE_INLINE bool MAKE_ATTR_USE | |
1084 | GNode_IsError(const GNode *gn) | 1084 | GNode_IsError(const GNode *gn) | |
1085 | { | 1085 | { | |
1086 | return gn->made == ERROR || gn->made == ABORTED; | 1086 | return gn->made == ERROR || gn->made == ABORTED; | |
1087 | } | 1087 | } | |
1088 | 1088 | |||
1089 | MAKE_INLINE bool MAKE_ATTR_USE | 1089 | MAKE_INLINE bool MAKE_ATTR_USE | |
1090 | GNode_IsMainCandidate(const GNode *gn) | 1090 | GNode_IsMainCandidate(const GNode *gn) | |
1091 | { | 1091 | { | |
1092 | return (gn->type & (OP_NOTMAIN | OP_USE | OP_USEBEFORE | | 1092 | return (gn->type & (OP_NOTMAIN | OP_USE | OP_USEBEFORE | | |
1093 | OP_EXEC | OP_TRANSFORM)) == 0; | 1093 | OP_EXEC | OP_TRANSFORM)) == 0; | |
1094 | } | 1094 | } | |
1095 | 1095 | |||
1096 | /* Return whether the target file should be preserved on interrupt. */ | 1096 | /* Return whether the target file should be preserved on interrupt. */ | |
1097 | MAKE_INLINE bool MAKE_ATTR_USE | 1097 | MAKE_INLINE bool MAKE_ATTR_USE | |
1098 | GNode_IsPrecious(const GNode *gn) | 1098 | GNode_IsPrecious(const GNode *gn) | |
1099 | { | 1099 | { | |
1100 | /* XXX: Why are '::' targets precious? */ | 1100 | /* XXX: Why are '::' targets precious? */ | |
1101 | return allPrecious || gn->type & (OP_PRECIOUS | OP_DOUBLEDEP); | 1101 | return allPrecious || gn->type & (OP_PRECIOUS | OP_DOUBLEDEP); | |
1102 | } | 1102 | } | |
1103 | 1103 | |||
1104 | MAKE_INLINE const char * MAKE_ATTR_USE | 1104 | MAKE_INLINE const char * MAKE_ATTR_USE | |
1105 | GNode_VarTarget(GNode *gn) { return GNode_ValueDirect(gn, TARGET); } | 1105 | GNode_VarTarget(GNode *gn) { return GNode_ValueDirect(gn, TARGET); } | |
1106 | MAKE_INLINE const char * MAKE_ATTR_USE | 1106 | MAKE_INLINE const char * MAKE_ATTR_USE | |
1107 | GNode_VarOodate(GNode *gn) { return GNode_ValueDirect(gn, OODATE); } | 1107 | GNode_VarOodate(GNode *gn) { return GNode_ValueDirect(gn, OODATE); } | |
1108 | MAKE_INLINE const char * MAKE_ATTR_USE | 1108 | MAKE_INLINE const char * MAKE_ATTR_USE | |
1109 | GNode_VarAllsrc(GNode *gn) { return GNode_ValueDirect(gn, ALLSRC); } | 1109 | GNode_VarAllsrc(GNode *gn) { return GNode_ValueDirect(gn, ALLSRC); } | |
1110 | MAKE_INLINE const char * MAKE_ATTR_USE | 1110 | MAKE_INLINE const char * MAKE_ATTR_USE | |
1111 | GNode_VarImpsrc(GNode *gn) { return GNode_ValueDirect(gn, IMPSRC); } | 1111 | GNode_VarImpsrc(GNode *gn) { return GNode_ValueDirect(gn, IMPSRC); } | |
1112 | MAKE_INLINE const char * MAKE_ATTR_USE | 1112 | MAKE_INLINE const char * MAKE_ATTR_USE | |
1113 | GNode_VarPrefix(GNode *gn) { return GNode_ValueDirect(gn, PREFIX); } | 1113 | GNode_VarPrefix(GNode *gn) { return GNode_ValueDirect(gn, PREFIX); } | |
1114 | MAKE_INLINE const char * MAKE_ATTR_USE | 1114 | MAKE_INLINE const char * MAKE_ATTR_USE | |
1115 | GNode_VarArchive(GNode *gn) { return GNode_ValueDirect(gn, ARCHIVE); } | 1115 | GNode_VarArchive(GNode *gn) { return GNode_ValueDirect(gn, ARCHIVE); } | |
1116 | MAKE_INLINE const char * MAKE_ATTR_USE | 1116 | MAKE_INLINE const char * MAKE_ATTR_USE | |
1117 | GNode_VarMember(GNode *gn) { return GNode_ValueDirect(gn, MEMBER); } | 1117 | GNode_VarMember(GNode *gn) { return GNode_ValueDirect(gn, MEMBER); } | |
1118 | 1118 | |||
1119 | MAKE_INLINE void * MAKE_ATTR_USE | 1119 | MAKE_INLINE void * MAKE_ATTR_USE | |
1120 | UNCONST(const void *ptr) | 1120 | UNCONST(const void *ptr) | |
1121 | { | 1121 | { | |
1122 | void *ret; | 1122 | void *ret; | |
1123 | memcpy(&ret, &ptr, sizeof(ret)); | 1123 | memcpy(&ret, &ptr, sizeof(ret)); | |
1124 | return ret; | 1124 | return ret; | |
1125 | } | 1125 | } | |
1126 | 1126 | |||
1127 | /* At least GNU/Hurd systems lack hardcoded MAXPATHLEN/PATH_MAX */ | 1127 | /* At least GNU/Hurd systems lack hardcoded MAXPATHLEN/PATH_MAX */ | |
1128 | #include <limits.h> | 1128 | #include <limits.h> | |
1129 | #ifndef MAXPATHLEN | 1129 | #ifndef MAXPATHLEN | |
1130 | #define MAXPATHLEN 4096 | 1130 | #define MAXPATHLEN 4096 | |
1131 | #endif | 1131 | #endif | |
1132 | #ifndef PATH_MAX | 1132 | #ifndef PATH_MAX | |
1133 | #define PATH_MAX MAXPATHLEN | 1133 | #define PATH_MAX MAXPATHLEN | |
1134 | #endif | 1134 | #endif | |
1135 | 1135 | |||
1136 | #if defined(SYSV) | 1136 | #if defined(SYSV) | |
1137 | #define KILLPG(pid, sig) kill(-(pid), (sig)) | 1137 | #define KILLPG(pid, sig) kill(-(pid), (sig)) | |
1138 | #else | 1138 | #else | |
1139 | #define KILLPG(pid, sig) killpg((pid), (sig)) | 1139 | #define KILLPG(pid, sig) killpg((pid), (sig)) | |
1140 | #endif | 1140 | #endif | |
1141 | 1141 | |||
1142 | MAKE_INLINE bool MAKE_ATTR_USE | 1142 | MAKE_INLINE bool MAKE_ATTR_USE | |
1143 | ch_isalnum(char ch) { return isalnum((unsigned char)ch) != 0; } | 1143 | ch_isalnum(char ch) { return isalnum((unsigned char)ch) != 0; } | |
1144 | MAKE_INLINE bool MAKE_ATTR_USE | 1144 | MAKE_INLINE bool MAKE_ATTR_USE | |
1145 | ch_isalpha(char ch) { return isalpha((unsigned char)ch) != 0; } | 1145 | ch_isalpha(char ch) { return isalpha((unsigned char)ch) != 0; } | |
1146 | MAKE_INLINE bool MAKE_ATTR_USE | 1146 | MAKE_INLINE bool MAKE_ATTR_USE | |
1147 | ch_isdigit(char ch) { return isdigit((unsigned char)ch) != 0; } | 1147 | ch_isdigit(char ch) { return isdigit((unsigned char)ch) != 0; } | |
1148 | MAKE_INLINE bool MAKE_ATTR_USE | 1148 | MAKE_INLINE bool MAKE_ATTR_USE | |
1149 | ch_islower(char ch) { return islower((unsigned char)ch) != 0; } | 1149 | ch_islower(char ch) { return islower((unsigned char)ch) != 0; } | |
1150 | MAKE_INLINE bool MAKE_ATTR_USE | 1150 | MAKE_INLINE bool MAKE_ATTR_USE | |
1151 | ch_isspace(char ch) { return isspace((unsigned char)ch) != 0; } | 1151 | ch_isspace(char ch) { return isspace((unsigned char)ch) != 0; } | |
1152 | MAKE_INLINE bool MAKE_ATTR_USE | 1152 | MAKE_INLINE bool MAKE_ATTR_USE | |
1153 | ch_isupper(char ch) { return isupper((unsigned char)ch) != 0; } | 1153 | ch_isupper(char ch) { return isupper((unsigned char)ch) != 0; } | |
1154 | MAKE_INLINE char MAKE_ATTR_USE | 1154 | MAKE_INLINE char MAKE_ATTR_USE | |
1155 | ch_tolower(char ch) { return (char)tolower((unsigned char)ch); } | 1155 | ch_tolower(char ch) { return (char)tolower((unsigned char)ch); } | |
1156 | MAKE_INLINE char MAKE_ATTR_USE | 1156 | MAKE_INLINE char MAKE_ATTR_USE | |
1157 | ch_toupper(char ch) { return (char)toupper((unsigned char)ch); } | 1157 | ch_toupper(char ch) { return (char)toupper((unsigned char)ch); } | |
1158 | 1158 | |||
1159 | MAKE_INLINE void | 1159 | MAKE_INLINE void | |
1160 | cpp_skip_whitespace(const char **pp) | 1160 | cpp_skip_whitespace(const char **pp) | |
1161 | { | 1161 | { | |
1162 | while (ch_isspace(**pp)) | 1162 | while (ch_isspace(**pp)) | |
1163 | (*pp)++; | 1163 | (*pp)++; | |
1164 | } | 1164 | } | |
1165 | 1165 | |||
1166 | MAKE_INLINE void | 1166 | MAKE_INLINE void | |
1167 | cpp_skip_hspace(const char **pp) | 1167 | cpp_skip_hspace(const char **pp) | |
1168 | { | 1168 | { | |
1169 | while (**pp == ' ' || **pp == '\t') | 1169 | while (**pp == ' ' || **pp == '\t') | |
1170 | (*pp)++; | 1170 | (*pp)++; | |
1171 | } | 1171 | } | |
1172 | 1172 | |||
1173 | MAKE_INLINE bool | 1173 | MAKE_INLINE bool | |
1174 | cpp_skip_string(const char **pp, const char *s) | 1174 | cpp_skip_string(const char **pp, const char *s) | |
1175 | { | 1175 | { | |
1176 | const char *p = *pp; | 1176 | const char *p = *pp; | |
1177 | while (*p == *s && *s != '\0') | 1177 | while (*p == *s && *s != '\0') | |
1178 | p++, s++; | 1178 | p++, s++; | |
1179 | if (*s == '\0') | 1179 | if (*s == '\0') | |
1180 | *pp = p; | 1180 | *pp = p; | |
1181 | return *s == '\0'; | 1181 | return *s == '\0'; | |
1182 | } | 1182 | } | |
1183 | 1183 | |||
1184 | MAKE_INLINE void | 1184 | MAKE_INLINE void | |
1185 | pp_skip_whitespace(char **pp) | 1185 | pp_skip_whitespace(char **pp) | |
1186 | { | 1186 | { | |
1187 | while (ch_isspace(**pp)) | 1187 | while (ch_isspace(**pp)) | |
1188 | (*pp)++; | 1188 | (*pp)++; | |
1189 | } | 1189 | } | |
1190 | 1190 | |||
1191 | MAKE_INLINE void | 1191 | MAKE_INLINE void | |
1192 | pp_skip_hspace(char **pp) | 1192 | pp_skip_hspace(char **pp) | |
1193 | { | 1193 | { | |
1194 | while (**pp == ' ' || **pp == '\t') | 1194 | while (**pp == ' ' || **pp == '\t') | |
1195 | (*pp)++; | 1195 | (*pp)++; | |
1196 | } | 1196 | } | |
1197 | 1197 | |||
1198 | #if defined(lint) | 1198 | #if defined(lint) | |
1199 | void do_not_define_rcsid(void); /* for lint */ | 1199 | void do_not_define_rcsid(void); /* for lint */ | |
1200 | # define MAKE_RCSID(id) void do_not_define_rcsid(void) | 1200 | # define MAKE_RCSID(id) void do_not_define_rcsid(void) | |
1201 | #elif defined(MAKE_NATIVE) | 1201 | #elif defined(MAKE_NATIVE) | |
1202 | # include <sys/cdefs.h> | 1202 | # include <sys/cdefs.h> | |
1203 | # define MAKE_RCSID(id) __RCSID(id) | 1203 | # define MAKE_RCSID(id) __RCSID(id) | |
1204 | #elif defined(MAKE_ALL_IN_ONE) && defined(__COUNTER__) | 1204 | #elif defined(MAKE_ALL_IN_ONE) && defined(__COUNTER__) | |
1205 | # define MAKE_RCSID_CONCAT(x, y) CONCAT(x, y) | 1205 | # define MAKE_RCSID_CONCAT(x, y) CONCAT(x, y) | |
1206 | # define MAKE_RCSID(id) static volatile char \ | 1206 | # define MAKE_RCSID(id) static volatile char \ | |
1207 | MAKE_RCSID_CONCAT(rcsid_, __COUNTER__)[] = id | 1207 | MAKE_RCSID_CONCAT(rcsid_, __COUNTER__)[] = id | |
1208 | #elif defined(MAKE_ALL_IN_ONE) | 1208 | #elif defined(MAKE_ALL_IN_ONE) | |
1209 | # define MAKE_RCSID(id) void do_not_define_rcsid(void) | 1209 | # define MAKE_RCSID(id) void do_not_define_rcsid(void) | |
1210 | #else | 1210 | #else | |
1211 | # define MAKE_RCSID(id) static volatile char rcsid[] = id | 1211 | # define MAKE_RCSID(id) static volatile char rcsid[] = id | |
1212 | #endif | 1212 | #endif | |
1213 | 1213 | |||
1214 | #endif | 1214 | #endif |
--- src/usr.bin/make/parse.c 2024/04/20 10:18:55 1.720
+++ src/usr.bin/make/parse.c 2024/04/23 22:51:28 1.721
@@ -1,2653 +1,2653 @@ | @@ -1,2653 +1,2653 @@ | |||
1 | /* $NetBSD: parse.c,v 1.720 2024/04/20 10:18:55 rillig Exp $ */ | 1 | /* $NetBSD: parse.c,v 1.721 2024/04/23 22:51:28 rillig Exp $ */ | |
2 | 2 | |||
3 | /* | 3 | /* | |
4 | * Copyright (c) 1988, 1989, 1990, 1993 | 4 | * Copyright (c) 1988, 1989, 1990, 1993 | |
5 | * The Regents of the University of California. All rights reserved. | 5 | * The Regents of the University of California. 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. | |
15 | * 2. Redistributions in binary form must reproduce the above copyright | 15 | * 2. Redistributions in binary form must reproduce the above copyright | |
16 | * notice, this list of conditions and the following disclaimer in the | 16 | * notice, this list of conditions and the following disclaimer in the | |
17 | * documentation and/or other materials provided with the distribution. | 17 | * documentation and/or other materials provided with the distribution. | |
18 | * 3. Neither the name of the University nor the names of its contributors | 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 | 19 | * may be used to endorse or promote products derived from this software | |
20 | * without specific prior written permission. | 20 | * without specific prior written permission. | |
21 | * | 21 | * | |
22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | 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 | 23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | 25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | 27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | 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 | 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 | 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 | 31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
32 | * SUCH DAMAGE. | 32 | * SUCH DAMAGE. | |
33 | */ | 33 | */ | |
34 | 34 | |||
35 | /* | 35 | /* | |
36 | * Copyright (c) 1989 by Berkeley Softworks | 36 | * Copyright (c) 1989 by Berkeley Softworks | |
37 | * All rights reserved. | 37 | * All rights reserved. | |
38 | * | 38 | * | |
39 | * This code is derived from software contributed to Berkeley by | 39 | * This code is derived from software contributed to Berkeley by | |
40 | * Adam de Boor. | 40 | * Adam de Boor. | |
41 | * | 41 | * | |
42 | * Redistribution and use in source and binary forms, with or without | 42 | * Redistribution and use in source and binary forms, with or without | |
43 | * modification, are permitted provided that the following conditions | 43 | * modification, are permitted provided that the following conditions | |
44 | * are met: | 44 | * are met: | |
45 | * 1. Redistributions of source code must retain the above copyright | 45 | * 1. Redistributions of source code must retain the above copyright | |
46 | * notice, this list of conditions and the following disclaimer. | 46 | * notice, this list of conditions and the following disclaimer. | |
47 | * 2. Redistributions in binary form must reproduce the above copyright | 47 | * 2. Redistributions in binary form must reproduce the above copyright | |
48 | * notice, this list of conditions and the following disclaimer in the | 48 | * notice, this list of conditions and the following disclaimer in the | |
49 | * documentation and/or other materials provided with the distribution. | 49 | * documentation and/or other materials provided with the distribution. | |
50 | * 3. All advertising materials mentioning features or use of this software | 50 | * 3. All advertising materials mentioning features or use of this software | |
51 | * must display the following acknowledgement: | 51 | * must display the following acknowledgement: | |
52 | * This product includes software developed by the University of | 52 | * This product includes software developed by the University of | |
53 | * California, Berkeley and its contributors. | 53 | * California, Berkeley and its contributors. | |
54 | * 4. Neither the name of the University nor the names of its contributors | 54 | * 4. Neither the name of the University nor the names of its contributors | |
55 | * may be used to endorse or promote products derived from this software | 55 | * may be used to endorse or promote products derived from this software | |
56 | * without specific prior written permission. | 56 | * without specific prior written permission. | |
57 | * | 57 | * | |
58 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | 58 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
59 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 59 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
60 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 60 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
61 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | 61 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
62 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 62 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
63 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | 63 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
64 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | 64 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
65 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | 65 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
66 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | 66 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
67 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | 67 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
68 | * SUCH DAMAGE. | 68 | * SUCH DAMAGE. | |
69 | */ | 69 | */ | |
70 | 70 | |||
71 | /* | 71 | /* | |
72 | * Parsing of makefiles. | 72 | * Parsing of makefiles. | |
73 | * | 73 | * | |
74 | * Parse_File is the main entry point and controls most of the other | 74 | * Parse_File is the main entry point and controls most of the other | |
75 | * functions in this module. | 75 | * functions in this module. | |
76 | * | 76 | * | |
77 | * Interface: | 77 | * Interface: | |
78 | * Parse_Init Initialize the module | 78 | * Parse_Init Initialize the module | |
79 | * | 79 | * | |
80 | * Parse_End Clean up the module | 80 | * Parse_End Clean up the module | |
81 | * | 81 | * | |
82 | * Parse_File Parse a top-level makefile. Included files are | 82 | * Parse_File Parse a top-level makefile. Included files are | |
83 | * handled by IncludeFile instead. | 83 | * handled by IncludeFile instead. | |
84 | * | 84 | * | |
85 | * Parse_VarAssign | 85 | * Parse_VarAssign | |
86 | * Try to parse the given line as a variable assignment. | 86 | * Try to parse the given line as a variable assignment. | |
87 | * Used by MainParseArgs to determine if an argument is | 87 | * Used by MainParseArgs to determine if an argument is | |
88 | * a target or a variable assignment. Used internally | 88 | * a target or a variable assignment. Used internally | |
89 | * for pretty much the same thing. | 89 | * for pretty much the same thing. | |
90 | * | 90 | * | |
91 | * Parse_Error Report a parse error, a warning or an informational | 91 | * Parse_Error Report a parse error, a warning or an informational | |
92 | * message. | 92 | * message. | |
93 | * | 93 | * | |
94 | * Parse_MainName Populate the list of targets to create. | 94 | * Parse_MainName Populate the list of targets to create. | |
95 | */ | 95 | */ | |
96 | 96 | |||
97 | #include <sys/types.h> | 97 | #include <sys/types.h> | |
98 | #include <sys/stat.h> | 98 | #include <sys/stat.h> | |
99 | #include <errno.h> | 99 | #include <errno.h> | |
100 | #include <stdarg.h> | 100 | #include <stdarg.h> | |
101 | 101 | |||
102 | #include "make.h" | 102 | #include "make.h" | |
103 | #include "dir.h" | 103 | #include "dir.h" | |
104 | #include "job.h" | 104 | #include "job.h" | |
105 | #include "pathnames.h" | 105 | #include "pathnames.h" | |
106 | 106 | |||
107 | /* "@(#)parse.c 8.3 (Berkeley) 3/19/94" */ | 107 | /* "@(#)parse.c 8.3 (Berkeley) 3/19/94" */ | |
108 | MAKE_RCSID("$NetBSD: parse.c,v 1.720 2024/04/20 10:18:55 rillig Exp $"); | 108 | MAKE_RCSID("$NetBSD: parse.c,v 1.721 2024/04/23 22:51:28 rillig Exp $"); | |
109 | 109 | |||
110 | /* Detects a multiple-inclusion guard in a makefile. */ | 110 | /* Detects a multiple-inclusion guard in a makefile. */ | |
111 | typedef enum { | 111 | typedef enum { | |
112 | GS_START, /* at the beginning of the file */ | 112 | GS_START, /* at the beginning of the file */ | |
113 | GS_COND, /* after the guard condition */ | 113 | GS_COND, /* after the guard condition */ | |
114 | GS_DONE, /* after the closing .endif */ | 114 | GS_DONE, /* after the closing .endif */ | |
115 | GS_NO /* the file is not guarded */ | 115 | GS_NO /* the file is not guarded */ | |
116 | } GuardState; | 116 | } GuardState; | |
117 | 117 | |||
118 | /* A file being parsed. */ | 118 | /* A file being parsed. */ | |
119 | typedef struct IncludedFile { | 119 | typedef struct IncludedFile { | |
120 | FStr name; /* absolute or relative to the cwd */ | 120 | FStr name; /* absolute or relative to the cwd */ | |
121 | unsigned lineno; /* 1-based */ | 121 | unsigned lineno; /* 1-based */ | |
122 | unsigned readLines; /* the number of physical lines that have | 122 | unsigned readLines; /* the number of physical lines that have | |
123 | * been read from the file */ | 123 | * been read from the file */ | |
124 | unsigned forHeadLineno; /* 1-based */ | 124 | unsigned forHeadLineno; /* 1-based */ | |
125 | unsigned forBodyReadLines; /* the number of physical lines that have | 125 | unsigned forBodyReadLines; /* the number of physical lines that have | |
126 | * been read from the file above the body of | 126 | * been read from the file above the body of | |
127 | * the .for loop */ | 127 | * the .for loop */ | |
128 | unsigned int condMinDepth; /* depth of nested 'if' directives, at the | 128 | unsigned int condMinDepth; /* depth of nested 'if' directives, at the | |
129 | * beginning of the file */ | 129 | * beginning of the file */ | |
130 | bool depending; /* state of doing_depend on EOF */ | 130 | bool depending; /* state of doing_depend on EOF */ | |
131 | 131 | |||
132 | Buffer buf; /* the file's content or the body of the .for | 132 | Buffer buf; /* the file's content or the body of the .for | |
133 | * loop; either empty or ends with '\n' */ | 133 | * loop; either empty or ends with '\n' */ | |
134 | char *buf_ptr; /* next char to be read from buf */ | 134 | char *buf_ptr; /* next char to be read from buf */ | |
135 | char *buf_end; /* buf_end[-1] == '\n' */ | 135 | char *buf_end; /* buf_end[-1] == '\n' */ | |
136 | 136 | |||
137 | GuardState guardState; | 137 | GuardState guardState; | |
138 | Guard *guard; | 138 | Guard *guard; | |
139 | 139 | |||
140 | struct ForLoop *forLoop; | 140 | struct ForLoop *forLoop; | |
141 | } IncludedFile; | 141 | } IncludedFile; | |
142 | 142 | |||
143 | /* Special attributes for target nodes. */ | 143 | /* Special attributes for target nodes. */ | |
144 | typedef enum ParseSpecial { | 144 | typedef enum ParseSpecial { | |
145 | SP_ATTRIBUTE, /* Generic attribute */ | 145 | SP_ATTRIBUTE, /* Generic attribute */ | |
146 | SP_BEGIN, /* .BEGIN */ | 146 | SP_BEGIN, /* .BEGIN */ | |
147 | SP_DEFAULT, /* .DEFAULT */ | 147 | SP_DEFAULT, /* .DEFAULT */ | |
148 | SP_DELETE_ON_ERROR, /* .DELETE_ON_ERROR */ | 148 | SP_DELETE_ON_ERROR, /* .DELETE_ON_ERROR */ | |
149 | SP_END, /* .END */ | 149 | SP_END, /* .END */ | |
150 | SP_ERROR, /* .ERROR */ | 150 | SP_ERROR, /* .ERROR */ | |
151 | SP_IGNORE, /* .IGNORE */ | 151 | SP_IGNORE, /* .IGNORE */ | |
152 | SP_INCLUDES, /* .INCLUDES; not mentioned in the manual page */ | 152 | SP_INCLUDES, /* .INCLUDES; not mentioned in the manual page */ | |
153 | SP_INTERRUPT, /* .INTERRUPT */ | 153 | SP_INTERRUPT, /* .INTERRUPT */ | |
154 | SP_LIBS, /* .LIBS; not mentioned in the manual page */ | 154 | SP_LIBS, /* .LIBS; not mentioned in the manual page */ | |
155 | SP_MAIN, /* .MAIN and no user-specified targets to make */ | 155 | SP_MAIN, /* .MAIN and no user-specified targets to make */ | |
156 | SP_META, /* .META */ | 156 | SP_META, /* .META */ | |
157 | SP_MFLAGS, /* .MFLAGS or .MAKEFLAGS */ | 157 | SP_MFLAGS, /* .MFLAGS or .MAKEFLAGS */ | |
158 | SP_NOMETA, /* .NOMETA */ | 158 | SP_NOMETA, /* .NOMETA */ | |
159 | SP_NOMETA_CMP, /* .NOMETA_CMP */ | 159 | SP_NOMETA_CMP, /* .NOMETA_CMP */ | |
160 | SP_NOPATH, /* .NOPATH */ | 160 | SP_NOPATH, /* .NOPATH */ | |
161 | SP_NOREADONLY, /* .NOREADONLY */ | 161 | SP_NOREADONLY, /* .NOREADONLY */ | |
162 | SP_NOT, /* Not special */ | 162 | SP_NOT, /* Not special */ | |
163 | SP_NOTPARALLEL, /* .NOTPARALLEL or .NO_PARALLEL */ | 163 | SP_NOTPARALLEL, /* .NOTPARALLEL or .NO_PARALLEL */ | |
164 | SP_NULL, /* .NULL; not mentioned in the manual page */ | 164 | SP_NULL, /* .NULL; not mentioned in the manual page */ | |
165 | SP_OBJDIR, /* .OBJDIR */ | 165 | SP_OBJDIR, /* .OBJDIR */ | |
166 | SP_ORDER, /* .ORDER */ | 166 | SP_ORDER, /* .ORDER */ | |
167 | SP_PARALLEL, /* .PARALLEL; not mentioned in the manual page */ | 167 | SP_PARALLEL, /* .PARALLEL; not mentioned in the manual page */ | |
168 | SP_PATH, /* .PATH or .PATH.suffix */ | 168 | SP_PATH, /* .PATH or .PATH.suffix */ | |
169 | SP_PHONY, /* .PHONY */ | 169 | SP_PHONY, /* .PHONY */ | |
170 | SP_POSIX, /* .POSIX; not mentioned in the manual page */ | 170 | SP_POSIX, /* .POSIX; not mentioned in the manual page */ | |
171 | SP_PRECIOUS, /* .PRECIOUS */ | 171 | SP_PRECIOUS, /* .PRECIOUS */ | |
172 | SP_READONLY, /* .READONLY */ | 172 | SP_READONLY, /* .READONLY */ | |
173 | SP_SHELL, /* .SHELL */ | 173 | SP_SHELL, /* .SHELL */ | |
174 | SP_SILENT, /* .SILENT */ | 174 | SP_SILENT, /* .SILENT */ | |
175 | SP_SINGLESHELL, /* .SINGLESHELL; not mentioned in the manual page */ | 175 | SP_SINGLESHELL, /* .SINGLESHELL; not mentioned in the manual page */ | |
176 | SP_STALE, /* .STALE */ | 176 | SP_STALE, /* .STALE */ | |
177 | SP_SUFFIXES, /* .SUFFIXES */ | 177 | SP_SUFFIXES, /* .SUFFIXES */ | |
178 | SP_SYSPATH, /* .SYSPATH */ | 178 | SP_SYSPATH, /* .SYSPATH */ | |
179 | SP_WAIT /* .WAIT */ | 179 | SP_WAIT /* .WAIT */ | |
180 | } ParseSpecial; | 180 | } ParseSpecial; | |
181 | 181 | |||
182 | typedef List SearchPathList; | 182 | typedef List SearchPathList; | |
183 | typedef ListNode SearchPathListNode; | 183 | typedef ListNode SearchPathListNode; | |
184 | 184 | |||
185 | 185 | |||
186 | typedef enum VarAssignOp { | 186 | typedef enum VarAssignOp { | |
187 | VAR_NORMAL, /* = */ | 187 | VAR_NORMAL, /* = */ | |
188 | VAR_APPEND, /* += */ | 188 | VAR_APPEND, /* += */ | |
189 | VAR_DEFAULT, /* ?= */ | 189 | VAR_DEFAULT, /* ?= */ | |
190 | VAR_SUBST, /* := */ | 190 | VAR_SUBST, /* := */ | |
191 | VAR_SHELL /* != or :sh= */ | 191 | VAR_SHELL /* != or :sh= */ | |
192 | } VarAssignOp; | 192 | } VarAssignOp; | |
193 | 193 | |||
194 | typedef struct VarAssign { | 194 | typedef struct VarAssign { | |
195 | char *varname; /* unexpanded */ | 195 | char *varname; /* unexpanded */ | |
196 | VarAssignOp op; | 196 | VarAssignOp op; | |
197 | const char *value; /* unexpanded */ | 197 | const char *value; /* unexpanded */ | |
198 | } VarAssign; | 198 | } VarAssign; | |
199 | 199 | |||
200 | static bool Parse_IsVar(const char *, VarAssign *); | 200 | static bool Parse_IsVar(const char *, VarAssign *); | |
201 | static void Parse_Var(VarAssign *, GNode *); | 201 | static void Parse_Var(VarAssign *, GNode *); | |
202 | 202 | |||
203 | /* | 203 | /* | |
204 | * The target to be made if no targets are specified in the command line. | 204 | * The target to be made if no targets are specified in the command line. | |
205 | * This is the first target defined in any of the makefiles. | 205 | * This is the first target defined in any of the makefiles. | |
206 | */ | 206 | */ | |
207 | GNode *mainNode; | 207 | GNode *mainNode; | |
208 | 208 | |||
209 | /* | 209 | /* | |
210 | * During parsing, the targets from the left-hand side of the currently | 210 | * During parsing, the targets from the left-hand side of the currently | |
211 | * active dependency line, or NULL if the current line does not belong to a | 211 | * active dependency line, or NULL if the current line does not belong to a | |
212 | * dependency line, for example because it is a variable assignment. | 212 | * dependency line, for example because it is a variable assignment. | |
213 | * | 213 | * | |
214 | * See unit-tests/deptgt.mk, keyword "parse.c:targets". | 214 | * See unit-tests/deptgt.mk, keyword "parse.c:targets". | |
215 | */ | 215 | */ | |
216 | static GNodeList *targets; | 216 | static GNodeList *targets; | |
217 | 217 | |||
218 | #ifdef CLEANUP | 218 | #ifdef CLEANUP | |
219 | /* | 219 | /* | |
220 | * All shell commands for all targets, in no particular order and possibly | 220 | * All shell commands for all targets, in no particular order and possibly | |
221 | * with duplicates. Kept in a separate list since the commands from .USE or | 221 | * with duplicates. Kept in a separate list since the commands from .USE or | |
222 | * .USEBEFORE nodes are shared with other GNodes, thereby giving up the | 222 | * .USEBEFORE nodes are shared with other GNodes, thereby giving up the | |
223 | * easily understandable ownership over the allocated strings. | 223 | * easily understandable ownership over the allocated strings. | |
224 | */ | 224 | */ | |
225 | static StringList targCmds = LST_INIT; | 225 | static StringList targCmds = LST_INIT; | |
226 | #endif | 226 | #endif | |
227 | 227 | |||
228 | /* | 228 | /* | |
229 | * Predecessor node for handling .ORDER. Initialized to NULL when .ORDER | 229 | * Predecessor node for handling .ORDER. Initialized to NULL when .ORDER | |
230 | * is seen, then set to each successive source on the line. | 230 | * is seen, then set to each successive source on the line. | |
231 | */ | 231 | */ | |
232 | static GNode *order_pred; | 232 | static GNode *order_pred; | |
233 | 233 | |||
234 | static int parseErrors; | 234 | static int parseErrors; | |
235 | 235 | |||
236 | /* | 236 | /* | |
237 | * The include chain of makefiles. At index 0 is the top-level makefile from | 237 | * The include chain of makefiles. At index 0 is the top-level makefile from | |
238 | * the command line, followed by the included files or .for loops, up to and | 238 | * the command line, followed by the included files or .for loops, up to and | |
239 | * including the current file. | 239 | * including the current file. | |
240 | * | 240 | * | |
241 | * See PrintStackTrace for how to interpret the data. | 241 | * See PrintStackTrace for how to interpret the data. | |
242 | */ | 242 | */ | |
243 | static Vector /* of IncludedFile */ includes; | 243 | static Vector /* of IncludedFile */ includes; | |
244 | 244 | |||
245 | SearchPath *parseIncPath; /* directories for "..." includes */ | 245 | SearchPath *parseIncPath; /* directories for "..." includes */ | |
246 | SearchPath *sysIncPath; /* directories for <...> includes */ | 246 | SearchPath *sysIncPath; /* directories for <...> includes */ | |
247 | SearchPath *defSysIncPath; /* default for sysIncPath */ | 247 | SearchPath *defSysIncPath; /* default for sysIncPath */ | |
248 | 248 | |||
249 | /* | 249 | /* | |
250 | * The parseKeywords table is searched using binary search when deciding | 250 | * The parseKeywords table is searched using binary search when deciding | |
251 | * if a target or source is special. | 251 | * if a target or source is special. | |
252 | */ | 252 | */ | |
253 | static const struct { | 253 | static const struct { | |
254 | const char name[17]; | 254 | const char name[17]; | |
255 | ParseSpecial special; /* when used as a target */ | 255 | ParseSpecial special; /* when used as a target */ | |
256 | GNodeType targetAttr; /* when used as a source */ | 256 | GNodeType targetAttr; /* when used as a source */ | |
257 | } parseKeywords[] = { | 257 | } parseKeywords[] = { | |
258 | { ".BEGIN", SP_BEGIN, OP_NONE }, | 258 | { ".BEGIN", SP_BEGIN, OP_NONE }, | |
259 | { ".DEFAULT", SP_DEFAULT, OP_NONE }, | 259 | { ".DEFAULT", SP_DEFAULT, OP_NONE }, | |
260 | { ".DELETE_ON_ERROR", SP_DELETE_ON_ERROR, OP_NONE }, | 260 | { ".DELETE_ON_ERROR", SP_DELETE_ON_ERROR, OP_NONE }, | |
261 | { ".END", SP_END, OP_NONE }, | 261 | { ".END", SP_END, OP_NONE }, | |
262 | { ".ERROR", SP_ERROR, OP_NONE }, | 262 | { ".ERROR", SP_ERROR, OP_NONE }, | |
263 | { ".EXEC", SP_ATTRIBUTE, OP_EXEC }, | 263 | { ".EXEC", SP_ATTRIBUTE, OP_EXEC }, | |
264 | { ".IGNORE", SP_IGNORE, OP_IGNORE }, | 264 | { ".IGNORE", SP_IGNORE, OP_IGNORE }, | |
265 | { ".INCLUDES", SP_INCLUDES, OP_NONE }, | 265 | { ".INCLUDES", SP_INCLUDES, OP_NONE }, | |
266 | { ".INTERRUPT", SP_INTERRUPT, OP_NONE }, | 266 | { ".INTERRUPT", SP_INTERRUPT, OP_NONE }, | |
267 | { ".INVISIBLE", SP_ATTRIBUTE, OP_INVISIBLE }, | 267 | { ".INVISIBLE", SP_ATTRIBUTE, OP_INVISIBLE }, | |
268 | { ".JOIN", SP_ATTRIBUTE, OP_JOIN }, | 268 | { ".JOIN", SP_ATTRIBUTE, OP_JOIN }, | |
269 | { ".LIBS", SP_LIBS, OP_NONE }, | 269 | { ".LIBS", SP_LIBS, OP_NONE }, | |
270 | { ".MADE", SP_ATTRIBUTE, OP_MADE }, | 270 | { ".MADE", SP_ATTRIBUTE, OP_MADE }, | |
271 | { ".MAIN", SP_MAIN, OP_NONE }, | 271 | { ".MAIN", SP_MAIN, OP_NONE }, | |
272 | { ".MAKE", SP_ATTRIBUTE, OP_MAKE }, | 272 | { ".MAKE", SP_ATTRIBUTE, OP_MAKE }, | |
273 | { ".MAKEFLAGS", SP_MFLAGS, OP_NONE }, | 273 | { ".MAKEFLAGS", SP_MFLAGS, OP_NONE }, | |
274 | { ".META", SP_META, OP_META }, | 274 | { ".META", SP_META, OP_META }, | |
275 | { ".MFLAGS", SP_MFLAGS, OP_NONE }, | 275 | { ".MFLAGS", SP_MFLAGS, OP_NONE }, | |
276 | { ".NOMETA", SP_NOMETA, OP_NOMETA }, | 276 | { ".NOMETA", SP_NOMETA, OP_NOMETA }, | |
277 | { ".NOMETA_CMP", SP_NOMETA_CMP, OP_NOMETA_CMP }, | 277 | { ".NOMETA_CMP", SP_NOMETA_CMP, OP_NOMETA_CMP }, | |
278 | { ".NOPATH", SP_NOPATH, OP_NOPATH }, | 278 | { ".NOPATH", SP_NOPATH, OP_NOPATH }, | |
279 | { ".NOREADONLY", SP_NOREADONLY, OP_NONE }, | 279 | { ".NOREADONLY", SP_NOREADONLY, OP_NONE }, | |
280 | { ".NOTMAIN", SP_ATTRIBUTE, OP_NOTMAIN }, | 280 | { ".NOTMAIN", SP_ATTRIBUTE, OP_NOTMAIN }, | |
281 | { ".NOTPARALLEL", SP_NOTPARALLEL, OP_NONE }, | 281 | { ".NOTPARALLEL", SP_NOTPARALLEL, OP_NONE }, | |
282 | { ".NO_PARALLEL", SP_NOTPARALLEL, OP_NONE }, | 282 | { ".NO_PARALLEL", SP_NOTPARALLEL, OP_NONE }, | |
283 | { ".NULL", SP_NULL, OP_NONE }, | 283 | { ".NULL", SP_NULL, OP_NONE }, | |
284 | { ".OBJDIR", SP_OBJDIR, OP_NONE }, | 284 | { ".OBJDIR", SP_OBJDIR, OP_NONE }, | |
285 | { ".OPTIONAL", SP_ATTRIBUTE, OP_OPTIONAL }, | 285 | { ".OPTIONAL", SP_ATTRIBUTE, OP_OPTIONAL }, | |
286 | { ".ORDER", SP_ORDER, OP_NONE }, | 286 | { ".ORDER", SP_ORDER, OP_NONE }, | |
287 | { ".PARALLEL", SP_PARALLEL, OP_NONE }, | 287 | { ".PARALLEL", SP_PARALLEL, OP_NONE }, | |
288 | { ".PATH", SP_PATH, OP_NONE }, | 288 | { ".PATH", SP_PATH, OP_NONE }, | |
289 | { ".PHONY", SP_PHONY, OP_PHONY }, | 289 | { ".PHONY", SP_PHONY, OP_PHONY }, | |
290 | { ".POSIX", SP_POSIX, OP_NONE }, | 290 | { ".POSIX", SP_POSIX, OP_NONE }, | |
291 | { ".PRECIOUS", SP_PRECIOUS, OP_PRECIOUS }, | 291 | { ".PRECIOUS", SP_PRECIOUS, OP_PRECIOUS }, | |
292 | { ".READONLY", SP_READONLY, OP_NONE }, | 292 | { ".READONLY", SP_READONLY, OP_NONE }, | |
293 | { ".RECURSIVE", SP_ATTRIBUTE, OP_MAKE }, | 293 | { ".RECURSIVE", SP_ATTRIBUTE, OP_MAKE }, | |
294 | { ".SHELL", SP_SHELL, OP_NONE }, | 294 | { ".SHELL", SP_SHELL, OP_NONE }, | |
295 | { ".SILENT", SP_SILENT, OP_SILENT }, | 295 | { ".SILENT", SP_SILENT, OP_SILENT }, | |
296 | { ".SINGLESHELL", SP_SINGLESHELL, OP_NONE }, | 296 | { ".SINGLESHELL", SP_SINGLESHELL, OP_NONE }, | |
297 | { ".STALE", SP_STALE, OP_NONE }, | 297 | { ".STALE", SP_STALE, OP_NONE }, | |
298 | { ".SUFFIXES", SP_SUFFIXES, OP_NONE }, | 298 | { ".SUFFIXES", SP_SUFFIXES, OP_NONE }, | |
299 | { ".SYSPATH", SP_SYSPATH, OP_NONE }, | 299 | { ".SYSPATH", SP_SYSPATH, OP_NONE }, | |
300 | { ".USE", SP_ATTRIBUTE, OP_USE }, | 300 | { ".USE", SP_ATTRIBUTE, OP_USE }, | |
301 | { ".USEBEFORE", SP_ATTRIBUTE, OP_USEBEFORE }, | 301 | { ".USEBEFORE", SP_ATTRIBUTE, OP_USEBEFORE }, | |
302 | { ".WAIT", SP_WAIT, OP_NONE }, | 302 | { ".WAIT", SP_WAIT, OP_NONE }, | |
303 | }; | 303 | }; | |
304 | 304 | |||
305 | enum PosixState posix_state = PS_NOT_YET; | 305 | enum PosixState posix_state = PS_NOT_YET; | |
306 | 306 | |||
307 | static HashTable /* full file name -> Guard */ guards; | 307 | static HashTable /* full file name -> Guard */ guards; | |
308 | 308 | |||
309 | 309 | |||
310 | static List * | 310 | static List * | |
311 | Lst_New(void) | 311 | Lst_New(void) | |
312 | { | 312 | { | |
313 | List *list = bmake_malloc(sizeof *list); | 313 | List *list = bmake_malloc(sizeof *list); | |
314 | Lst_Init(list); | 314 | Lst_Init(list); | |
315 | return list; | 315 | return list; | |
316 | } | 316 | } | |
317 | 317 | |||
318 | static void | 318 | static void | |
319 | Lst_Free(List *list) | 319 | Lst_Free(List *list) | |
320 | { | 320 | { | |
321 | 321 | |||
322 | Lst_Done(list); | 322 | Lst_Done(list); | |
323 | free(list); | 323 | free(list); | |
324 | } | 324 | } | |
325 | 325 | |||
326 | static IncludedFile * | 326 | static IncludedFile * | |
327 | GetInclude(size_t i) | 327 | GetInclude(size_t i) | |
328 | { | 328 | { | |
329 | assert(i < includes.len); | 329 | assert(i < includes.len); | |
330 | return Vector_Get(&includes, i); | 330 | return Vector_Get(&includes, i); | |
331 | } | 331 | } | |
332 | 332 | |||
333 | /* The makefile or the body of a .for loop that is currently being read. */ | 333 | /* The makefile or the body of a .for loop that is currently being read. */ | |
334 | static IncludedFile * | 334 | static IncludedFile * | |
335 | CurFile(void) | 335 | CurFile(void) | |
336 | { | 336 | { | |
337 | return GetInclude(includes.len - 1); | 337 | return GetInclude(includes.len - 1); | |
338 | } | 338 | } | |
339 | 339 | |||
340 | unsigned int | 340 | unsigned int | |
341 | CurFile_CondMinDepth(void) | 341 | CurFile_CondMinDepth(void) | |
342 | { | 342 | { | |
343 | return CurFile()->condMinDepth; | 343 | return CurFile()->condMinDepth; | |
344 | } | 344 | } | |
345 | 345 | |||
346 | static Buffer | 346 | static Buffer | |
347 | LoadFile(const char *path, int fd) | 347 | LoadFile(const char *path, int fd) | |
348 | { | 348 | { | |
349 | ssize_t n; | 349 | ssize_t n; | |
350 | Buffer buf; | 350 | Buffer buf; | |
351 | size_t bufSize; | 351 | size_t bufSize; | |
352 | struct stat st; | 352 | struct stat st; | |
353 | 353 | |||
354 | bufSize = fstat(fd, &st) == 0 && S_ISREG(st.st_mode) && | 354 | bufSize = fstat(fd, &st) == 0 && S_ISREG(st.st_mode) && | |
355 | st.st_size > 0 && st.st_size < 1024 * 1024 * 1024 | 355 | st.st_size > 0 && st.st_size < 1024 * 1024 * 1024 | |
356 | ? (size_t)st.st_size : 1024; | 356 | ? (size_t)st.st_size : 1024; | |
357 | Buf_InitSize(&buf, bufSize); | 357 | Buf_InitSize(&buf, bufSize); | |
358 | 358 | |||
359 | for (;;) { | 359 | for (;;) { | |
360 | if (buf.len == buf.cap) { | 360 | if (buf.len == buf.cap) { | |
361 | if (buf.cap >= 512 * 1024 * 1024) { | 361 | if (buf.cap >= 512 * 1024 * 1024) { | |
362 | Error("%s: file too large", path); | 362 | Error("%s: file too large", path); | |
363 | exit(2); /* Not 1 so -q can distinguish error */ | 363 | exit(2); /* Not 1 so -q can distinguish error */ | |
364 | } | 364 | } | |
365 | Buf_Expand(&buf); | 365 | Buf_Expand(&buf); | |
366 | } | 366 | } | |
367 | assert(buf.len < buf.cap); | 367 | assert(buf.len < buf.cap); | |
368 | n = read(fd, buf.data + buf.len, buf.cap - buf.len); | 368 | n = read(fd, buf.data + buf.len, buf.cap - buf.len); | |
369 | if (n < 0) { | 369 | if (n < 0) { | |
370 | Error("%s: read error: %s", path, strerror(errno)); | 370 | Error("%s: read error: %s", path, strerror(errno)); | |
371 | exit(2); /* Not 1 so -q can distinguish error */ | 371 | exit(2); /* Not 1 so -q can distinguish error */ | |
372 | } | 372 | } | |
373 | if (n == 0) | 373 | if (n == 0) | |
374 | break; | 374 | break; | |
375 | 375 | |||
376 | buf.len += (size_t)n; | 376 | buf.len += (size_t)n; | |
377 | } | 377 | } | |
378 | assert(buf.len <= buf.cap); | 378 | assert(buf.len <= buf.cap); | |
379 | 379 | |||
380 | if (buf.len > 0 && !Buf_EndsWith(&buf, '\n')) | 380 | if (buf.len > 0 && !Buf_EndsWith(&buf, '\n')) | |
381 | Buf_AddByte(&buf, '\n'); | 381 | Buf_AddByte(&buf, '\n'); | |
382 | 382 | |||
383 | return buf; /* may not be null-terminated */ | 383 | return buf; /* may not be null-terminated */ | |
384 | } | 384 | } | |
385 | 385 | |||
386 | /* | 386 | /* | |
387 | * Print the current chain of .include and .for directives. In Parse_Fatal | 387 | * Print the current chain of .include and .for directives. In Parse_Fatal | |
388 | * or other functions that already print the location, includingInnermost | 388 | * or other functions that already print the location, includingInnermost | |
389 | * would be redundant, but in other cases like Error or Fatal it needs to be | 389 | * would be redundant, but in other cases like Error or Fatal it needs to be | |
390 | * included. | 390 | * included. | |
391 | */ | 391 | */ | |
392 | void | 392 | void | |
393 | PrintStackTrace(bool includingInnermost) | 393 | PrintStackTrace(bool includingInnermost) | |
394 | { | 394 | { | |
395 | const IncludedFile *entries; | 395 | const IncludedFile *entries; | |
396 | size_t i, n; | 396 | size_t i, n; | |
397 | 397 | |||
398 | n = includes.len; | 398 | n = includes.len; | |
399 | if (n == 0) | 399 | if (n == 0) | |
400 | return; | 400 | return; | |
401 | 401 | |||
402 | entries = GetInclude(0); | 402 | entries = GetInclude(0); | |
403 | if (!includingInnermost && entries[n - 1].forLoop == NULL) | 403 | if (!includingInnermost && entries[n - 1].forLoop == NULL) | |
404 | n--; /* already in the diagnostic */ | 404 | n--; /* already in the diagnostic */ | |
405 | 405 | |||
406 | for (i = n; i-- > 0;) { | 406 | for (i = n; i-- > 0;) { | |
407 | const IncludedFile *entry = entries + i; | 407 | const IncludedFile *entry = entries + i; | |
408 | const char *fname = entry->name.str; | 408 | const char *fname = entry->name.str; | |
409 | char dirbuf[MAXPATHLEN + 1]; | 409 | char dirbuf[MAXPATHLEN + 1]; | |
410 | 410 | |||
411 | if (fname[0] != '/' && strcmp(fname, "(stdin)") != 0) { | 411 | if (fname[0] != '/' && strcmp(fname, "(stdin)") != 0) { | |
412 | const char *realPath = realpath(fname, dirbuf); | 412 | const char *realPath = realpath(fname, dirbuf); | |
413 | if (realPath != NULL) | 413 | if (realPath != NULL) | |
414 | fname = realPath; | 414 | fname = realPath; | |
415 | } | 415 | } | |
416 | 416 | |||
417 | if (entry->forLoop != NULL) { | 417 | if (entry->forLoop != NULL) { | |
418 | char *details = ForLoop_Details(entry->forLoop); | 418 | char *details = ForLoop_Details(entry->forLoop); | |
419 | debug_printf("\tin .for loop from %s:%u with %s\n", | 419 | debug_printf("\tin .for loop from %s:%u with %s\n", | |
420 | fname, entry->forHeadLineno, details); | 420 | fname, entry->forHeadLineno, details); | |
421 | free(details); | 421 | free(details); | |
422 | } else if (i + 1 < n && entries[i + 1].forLoop != NULL) { | 422 | } else if (i + 1 < n && entries[i + 1].forLoop != NULL) { | |
423 | /* entry->lineno is not a useful line number */ | 423 | /* entry->lineno is not a useful line number */ | |
424 | } else | 424 | } else | |
425 | debug_printf("\tin %s:%u\n", fname, entry->lineno); | 425 | debug_printf("\tin %s:%u\n", fname, entry->lineno); | |
426 | } | 426 | } | |
427 | } | 427 | } | |
428 | 428 | |||
429 | /* Check if the current character is escaped on the current line. */ | 429 | /* Check if the current character is escaped on the current line. */ | |
430 | static bool | 430 | static bool | |
431 | IsEscaped(const char *line, const char *p) | 431 | IsEscaped(const char *line, const char *p) | |
432 | { | 432 | { | |
433 | bool escaped = false; | 433 | bool escaped = false; | |
434 | while (p > line && *--p == '\\') | 434 | while (p > line && *--p == '\\') | |
435 | escaped = !escaped; | 435 | escaped = !escaped; | |
436 | return escaped; | 436 | return escaped; | |
437 | } | 437 | } | |
438 | 438 | |||
439 | /* | 439 | /* | |
440 | * Remember the location (filename and lineno) where the last command was | 440 | * Remember the location (filename and lineno) where the last command was | |
441 | * added or where the node was mentioned in a .depend file. | 441 | * added or where the node was mentioned in a .depend file. | |
442 | */ | 442 | */ | |
443 | static void | 443 | static void | |
444 | RememberLocation(GNode *gn) | 444 | RememberLocation(GNode *gn) | |
445 | { | 445 | { | |
446 | IncludedFile *curFile = CurFile(); | 446 | IncludedFile *curFile = CurFile(); | |
447 | gn->fname = Str_Intern(curFile->name.str); | 447 | gn->fname = Str_Intern(curFile->name.str); | |
448 | gn->lineno = curFile->lineno; | 448 | gn->lineno = curFile->lineno; | |
449 | } | 449 | } | |
450 | 450 | |||
451 | /* | 451 | /* | |
452 | * Look in the table of keywords for one matching the given string. | 452 | * Look in the table of keywords for one matching the given string. | |
453 | * Return the index of the keyword, or -1 if it isn't there. | 453 | * Return the index of the keyword, or -1 if it isn't there. | |
454 | */ | 454 | */ | |
455 | static int | 455 | static int | |
456 | FindKeyword(const char *str) | 456 | FindKeyword(const char *str) | |
457 | { | 457 | { | |
458 | int start = 0; | 458 | int start = 0; | |
459 | int end = sizeof parseKeywords / sizeof parseKeywords[0] - 1; | 459 | int end = sizeof parseKeywords / sizeof parseKeywords[0] - 1; | |
460 | 460 | |||
461 | while (start <= end) { | 461 | while (start <= end) { | |
462 | int curr = start + (end - start) / 2; | 462 | int curr = start + (end - start) / 2; | |
463 | int diff = strcmp(str, parseKeywords[curr].name); | 463 | int diff = strcmp(str, parseKeywords[curr].name); | |
464 | 464 | |||
465 | if (diff == 0) | 465 | if (diff == 0) | |
466 | return curr; | 466 | return curr; | |
467 | if (diff < 0) | 467 | if (diff < 0) | |
468 | end = curr - 1; | 468 | end = curr - 1; | |
469 | else | 469 | else | |
470 | start = curr + 1; | 470 | start = curr + 1; | |
471 | } | 471 | } | |
472 | 472 | |||
473 | return -1; | 473 | return -1; | |
474 | } | 474 | } | |
475 | 475 | |||
476 | void | 476 | void | |
477 | PrintLocation(FILE *f, bool useVars, const GNode *gn) | 477 | PrintLocation(FILE *f, bool useVars, const GNode *gn) | |
478 | { | 478 | { | |
479 | char dirbuf[MAXPATHLEN + 1]; | 479 | char dirbuf[MAXPATHLEN + 1]; | |
480 | FStr dir, base; | 480 | FStr dir, base; | |
481 | const char *fname; | 481 | const char *fname; | |
482 | unsigned lineno; | 482 | unsigned lineno; | |
483 | 483 | |||
484 | if (gn != NULL) { | 484 | if (gn != NULL) { | |
485 | fname = gn->fname; | 485 | fname = gn->fname; | |
486 | lineno = gn->lineno; | 486 | lineno = gn->lineno; | |
487 | } else if (includes.len > 0) { | 487 | } else if (includes.len > 0) { | |
488 | IncludedFile *curFile = CurFile(); | 488 | IncludedFile *curFile = CurFile(); | |
489 | fname = curFile->name.str; | 489 | fname = curFile->name.str; | |
490 | lineno = curFile->lineno; | 490 | lineno = curFile->lineno; | |
491 | } else | 491 | } else | |
492 | return; | 492 | return; | |
493 | 493 | |||
494 | if (!useVars || fname[0] == '/' || strcmp(fname, "(stdin)") == 0) { | 494 | if (!useVars || fname[0] == '/' || strcmp(fname, "(stdin)") == 0) { | |
495 | (void)fprintf(f, "\"%s\" line %u: ", fname, lineno); | 495 | (void)fprintf(f, "\"%s\" line %u: ", fname, lineno); | |
496 | return; | 496 | return; | |
497 | } | 497 | } | |
498 | 498 | |||
499 | dir = Var_Value(SCOPE_GLOBAL, ".PARSEDIR"); | 499 | dir = Var_Value(SCOPE_GLOBAL, ".PARSEDIR"); | |
500 | if (dir.str == NULL) | 500 | if (dir.str == NULL) | |
501 | dir.str = "."; | 501 | dir.str = "."; | |
502 | if (dir.str[0] != '/') | 502 | if (dir.str[0] != '/') | |
503 | dir.str = realpath(dir.str, dirbuf); | 503 | dir.str = realpath(dir.str, dirbuf); | |
504 | 504 | |||
505 | base = Var_Value(SCOPE_GLOBAL, ".PARSEFILE"); | 505 | base = Var_Value(SCOPE_GLOBAL, ".PARSEFILE"); | |
506 | if (base.str == NULL) | 506 | if (base.str == NULL) | |
507 | base.str = str_basename(fname); | 507 | base.str = str_basename(fname); | |
508 | 508 | |||
509 | (void)fprintf(f, "\"%s/%s\" line %u: ", dir.str, base.str, lineno); | 509 | (void)fprintf(f, "\"%s/%s\" line %u: ", dir.str, base.str, lineno); | |
510 | 510 | |||
511 | FStr_Done(&base); | 511 | FStr_Done(&base); | |
512 | FStr_Done(&dir); | 512 | FStr_Done(&dir); | |
513 | } | 513 | } | |
514 | 514 | |||
515 | static void MAKE_ATTR_PRINTFLIKE(5, 0) | 515 | static void MAKE_ATTR_PRINTFLIKE(5, 0) | |
516 | ParseVErrorInternal(FILE *f, bool useVars, const GNode *gn, | 516 | ParseVErrorInternal(FILE *f, bool useVars, const GNode *gn, | |
517 | ParseErrorLevel level, const char *fmt, va_list ap) | 517 | ParseErrorLevel level, const char *fmt, va_list ap) | |
518 | { | 518 | { | |
519 | static bool fatal_warning_error_printed = false; | 519 | static bool fatal_warning_error_printed = false; | |
520 | 520 | |||
521 | (void)fprintf(f, "%s: ", progname); | 521 | (void)fprintf(f, "%s: ", progname); | |
522 | 522 | |||
523 | PrintLocation(f, useVars, gn); | 523 | PrintLocation(f, useVars, gn); | |
524 | fprintf(f, "%s", EvalStack_Details()); | 524 | fprintf(f, "%s", EvalStack_Details()); | |
525 | if (level == PARSE_WARNING) | 525 | if (level == PARSE_WARNING) | |
526 | (void)fprintf(f, "warning: "); | 526 | (void)fprintf(f, "warning: "); | |
527 | (void)vfprintf(f, fmt, ap); | 527 | (void)vfprintf(f, fmt, ap); | |
528 | (void)fprintf(f, "\n"); | 528 | (void)fprintf(f, "\n"); | |
529 | (void)fflush(f); | 529 | (void)fflush(f); | |
530 | 530 | |||
531 | if (level == PARSE_FATAL) | 531 | if (level == PARSE_FATAL) | |
532 | parseErrors++; | 532 | parseErrors++; | |
533 | if (level == PARSE_WARNING && opts.parseWarnFatal) { | 533 | if (level == PARSE_WARNING && opts.parseWarnFatal) { | |
534 | if (!fatal_warning_error_printed) { | 534 | if (!fatal_warning_error_printed) { | |
535 | Error("parsing warnings being treated as errors"); | 535 | Error("parsing warnings being treated as errors"); | |
536 | fatal_warning_error_printed = true; | 536 | fatal_warning_error_printed = true; | |
537 | } | 537 | } | |
538 | parseErrors++; | 538 | parseErrors++; | |
539 | } | 539 | } | |
540 | 540 | |||
541 | if (DEBUG(PARSE)) | 541 | if (DEBUG(PARSE)) | |
542 | PrintStackTrace(false); | 542 | PrintStackTrace(false); | |
543 | } | 543 | } | |
544 | 544 | |||
545 | static void MAKE_ATTR_PRINTFLIKE(3, 4) | 545 | static void MAKE_ATTR_PRINTFLIKE(3, 4) | |
546 | ParseErrorInternal(const GNode *gn, | 546 | ParseErrorInternal(const GNode *gn, | |
547 | ParseErrorLevel level, const char *fmt, ...) | 547 | ParseErrorLevel level, const char *fmt, ...) | |
548 | { | 548 | { | |
549 | va_list ap; | 549 | va_list ap; | |
550 | 550 | |||
551 | (void)fflush(stdout); | 551 | (void)fflush(stdout); | |
552 | va_start(ap, fmt); | 552 | va_start(ap, fmt); | |
553 | ParseVErrorInternal(stderr, false, gn, level, fmt, ap); | 553 | ParseVErrorInternal(stderr, false, gn, level, fmt, ap); | |
554 | va_end(ap); | 554 | va_end(ap); | |
555 | 555 | |||
556 | if (opts.debug_file != stdout && opts.debug_file != stderr) { | 556 | if (opts.debug_file != stdout && opts.debug_file != stderr) { | |
557 | va_start(ap, fmt); | 557 | va_start(ap, fmt); | |
558 | ParseVErrorInternal(opts.debug_file, false, gn, | 558 | ParseVErrorInternal(opts.debug_file, false, gn, | |
559 | level, fmt, ap); | 559 | level, fmt, ap); | |
560 | va_end(ap); | 560 | va_end(ap); | |
561 | } | 561 | } | |
562 | } | 562 | } | |
563 | 563 | |||
564 | /* | 564 | /* | |
565 | * Print a message, including location information. | 565 | * Print a message, including location information. | |
566 | * | 566 | * | |
567 | * If the level is PARSE_FATAL, continue parsing until the end of the | 567 | * If the level is PARSE_FATAL, continue parsing until the end of the | |
568 | * current top-level makefile, then exit (see Parse_File). | 568 | * current top-level makefile, then exit (see Parse_File). | |
569 | * | 569 | * | |
570 | * Fmt is given without a trailing newline. | 570 | * Fmt is given without a trailing newline. | |
571 | */ | 571 | */ | |
572 | void | 572 | void | |
573 | Parse_Error(ParseErrorLevel level, const char *fmt, ...) | 573 | Parse_Error(ParseErrorLevel level, const char *fmt, ...) | |
574 | { | 574 | { | |
575 | va_list ap; | 575 | va_list ap; | |
576 | 576 | |||
577 | (void)fflush(stdout); | 577 | (void)fflush(stdout); | |
578 | va_start(ap, fmt); | 578 | va_start(ap, fmt); | |
579 | ParseVErrorInternal(stderr, true, NULL, level, fmt, ap); | 579 | ParseVErrorInternal(stderr, true, NULL, level, fmt, ap); | |
580 | va_end(ap); | 580 | va_end(ap); | |
581 | 581 | |||
582 | if (opts.debug_file != stdout && opts.debug_file != stderr) { | 582 | if (opts.debug_file != stdout && opts.debug_file != stderr) { | |
583 | va_start(ap, fmt); | 583 | va_start(ap, fmt); | |
584 | ParseVErrorInternal(opts.debug_file, true, NULL, | 584 | ParseVErrorInternal(opts.debug_file, true, NULL, | |
585 | level, fmt, ap); | 585 | level, fmt, ap); | |
586 | va_end(ap); | 586 | va_end(ap); | |
587 | } | 587 | } | |
588 | } | 588 | } | |
589 | 589 | |||
590 | 590 | |||
591 | /* | 591 | /* | |
592 | * Handle an .info, .warning or .error directive. For an .error directive, | 592 | * Handle an .info, .warning or .error directive. For an .error directive, | |
593 | * exit immediately. | 593 | * exit immediately. | |
594 | */ | 594 | */ | |
595 | static void | 595 | static void | |
596 | HandleMessage(ParseErrorLevel level, const char *levelName, const char *umsg) | 596 | HandleMessage(ParseErrorLevel level, const char *levelName, const char *umsg) | |
597 | { | 597 | { | |
598 | char *xmsg; | 598 | char *xmsg; | |
599 | 599 | |||
600 | if (umsg[0] == '\0') { | 600 | if (umsg[0] == '\0') { | |
601 | Parse_Error(PARSE_FATAL, "Missing argument for \".%s\"", | 601 | Parse_Error(PARSE_FATAL, "Missing argument for \".%s\"", | |
602 | levelName); | 602 | levelName); | |
603 | return; | 603 | return; | |
604 | } | 604 | } | |
605 | 605 | |||
606 | xmsg = Var_Subst(umsg, SCOPE_CMDLINE, VARE_WANTRES); | 606 | xmsg = Var_Subst(umsg, SCOPE_CMDLINE, VARE_WANTRES); | |
607 | /* TODO: handle errors */ | 607 | /* TODO: handle errors */ | |
608 | 608 | |||
609 | Parse_Error(level, "%s", xmsg); | 609 | Parse_Error(level, "%s", xmsg); | |
610 | free(xmsg); | 610 | free(xmsg); | |
611 | 611 | |||
612 | if (level == PARSE_FATAL) { | 612 | if (level == PARSE_FATAL) { | |
613 | PrintOnError(NULL, "\n"); | 613 | PrintOnError(NULL, "\n"); | |
614 | exit(1); | 614 | exit(1); | |
615 | } | 615 | } | |
616 | } | 616 | } | |
617 | 617 | |||
618 | /* | 618 | /* | |
619 | * Add the child to the parent's children, and for non-special targets, vice | 619 | * Add the child to the parent's children, and for non-special targets, vice | |
620 | * versa. | 620 | * versa. | |
621 | */ | 621 | */ | |
622 | static void | 622 | static void | |
623 | LinkSource(GNode *pgn, GNode *cgn, bool isSpecial) | 623 | LinkSource(GNode *pgn, GNode *cgn, bool isSpecial) | |
624 | { | 624 | { | |
625 | if ((pgn->type & OP_DOUBLEDEP) && !Lst_IsEmpty(&pgn->cohorts)) | 625 | if ((pgn->type & OP_DOUBLEDEP) && !Lst_IsEmpty(&pgn->cohorts)) | |
626 | pgn = pgn->cohorts.last->datum; | 626 | pgn = pgn->cohorts.last->datum; | |
627 | 627 | |||
628 | Lst_Append(&pgn->children, cgn); | 628 | Lst_Append(&pgn->children, cgn); | |
629 | pgn->unmade++; | 629 | pgn->unmade++; | |
630 | 630 | |||
631 | /* | 631 | /* | |
632 | * Special targets like .END do not need to be informed once the child | 632 | * Special targets like .END do not need to be informed once the child | |
633 | * target has been made. | 633 | * target has been made. | |
634 | */ | 634 | */ | |
635 | if (!isSpecial) | 635 | if (!isSpecial) | |
636 | Lst_Append(&cgn->parents, pgn); | 636 | Lst_Append(&cgn->parents, pgn); | |
637 | 637 | |||
638 | if (DEBUG(PARSE)) { | 638 | if (DEBUG(PARSE)) { | |
639 | debug_printf("# LinkSource: added child %s - %s\n", | 639 | debug_printf("# LinkSource: added child %s - %s\n", | |
640 | pgn->name, cgn->name); | 640 | pgn->name, cgn->name); | |
641 | Targ_PrintNode(pgn, 0); | 641 | Targ_PrintNode(pgn, 0); | |
642 | Targ_PrintNode(cgn, 0); | 642 | Targ_PrintNode(cgn, 0); | |
643 | } | 643 | } | |
644 | } | 644 | } | |
645 | 645 | |||
646 | /* Add the node to each target from the current dependency group. */ | 646 | /* Add the node to each target from the current dependency group. */ | |
647 | static void | 647 | static void | |
648 | LinkToTargets(GNode *gn, bool isSpecial) | 648 | LinkToTargets(GNode *gn, bool isSpecial) | |
649 | { | 649 | { | |
650 | GNodeListNode *ln; | 650 | GNodeListNode *ln; | |
651 | 651 | |||
652 | for (ln = targets->first; ln != NULL; ln = ln->next) | 652 | for (ln = targets->first; ln != NULL; ln = ln->next) | |
653 | LinkSource(ln->datum, gn, isSpecial); | 653 | LinkSource(ln->datum, gn, isSpecial); | |
654 | } | 654 | } | |
655 | 655 | |||
656 | static bool | 656 | static bool | |
657 | TryApplyDependencyOperator(GNode *gn, GNodeType op) | 657 | TryApplyDependencyOperator(GNode *gn, GNodeType op) | |
658 | { | 658 | { | |
659 | /* | 659 | /* | |
660 | * If the node occurred on the left-hand side of a dependency and the | 660 | * If the node occurred on the left-hand side of a dependency and the | |
661 | * operator also defines a dependency, they must match. | 661 | * operator also defines a dependency, they must match. | |
662 | */ | 662 | */ | |
663 | if ((op & OP_OPMASK) && (gn->type & OP_OPMASK) && | 663 | if ((op & OP_OPMASK) && (gn->type & OP_OPMASK) && | |
664 | ((op & OP_OPMASK) != (gn->type & OP_OPMASK))) { | 664 | ((op & OP_OPMASK) != (gn->type & OP_OPMASK))) { | |
665 | Parse_Error(PARSE_FATAL, "Inconsistent operator for %s", | 665 | Parse_Error(PARSE_FATAL, "Inconsistent operator for %s", | |
666 | gn->name); | 666 | gn->name); | |
667 | return false; | 667 | return false; | |
668 | } | 668 | } | |
669 | 669 | |||
670 | if (op == OP_DOUBLEDEP && (gn->type & OP_OPMASK) == OP_DOUBLEDEP) { | 670 | if (op == OP_DOUBLEDEP && (gn->type & OP_OPMASK) == OP_DOUBLEDEP) { | |
671 | /* | 671 | /* | |
672 | * If the node was on the left-hand side of a '::' operator, | 672 | * If the node was on the left-hand side of a '::' operator, | |
673 | * create a new node for the children and commands on this | 673 | * create a new node for the children and commands on this | |
674 | * dependency line, since each of these dependency groups has | 674 | * dependency line, since each of these dependency groups has | |
675 | * its own attributes and commands, separate from the others. | 675 | * its own attributes and commands, separate from the others. | |
676 | * | 676 | * | |
677 | * The new instance is placed on the 'cohorts' list of the | 677 | * The new instance is placed on the 'cohorts' list of the | |
678 | * initial one (note the initial one is not on its own | 678 | * initial one (note the initial one is not on its own | |
679 | * cohorts list) and the new instance is linked to all | 679 | * cohorts list) and the new instance is linked to all | |
680 | * parents of the initial instance. | 680 | * parents of the initial instance. | |
681 | */ | 681 | */ | |
682 | GNode *cohort; | 682 | GNode *cohort; | |
683 | 683 | |||
684 | /* | 684 | /* | |
685 | * Propagate copied bits to the initial node. They'll be | 685 | * Propagate copied bits to the initial node. They'll be | |
686 | * propagated back to the rest of the cohorts later. | 686 | * propagated back to the rest of the cohorts later. | |
687 | */ | 687 | */ | |
688 | gn->type |= op & (unsigned)~OP_OPMASK; | 688 | gn->type |= op & (unsigned)~OP_OPMASK; | |
689 | 689 | |||
690 | cohort = Targ_NewInternalNode(gn->name); | 690 | cohort = Targ_NewInternalNode(gn->name); | |
691 | if (doing_depend) | 691 | if (doing_depend) | |
692 | RememberLocation(cohort); | 692 | RememberLocation(cohort); | |
693 | /* | 693 | /* | |
694 | * Make the cohort invisible to avoid duplicating it | 694 | * Make the cohort invisible to avoid duplicating it | |
695 | * into other variables. True, parents of this target won't | 695 | * into other variables. True, parents of this target won't | |
696 | * tend to do anything with their local variables, but better | 696 | * tend to do anything with their local variables, but better | |
697 | * safe than sorry. | 697 | * safe than sorry. | |
698 | * | 698 | * | |
699 | * (I think this is pointless now, since the relevant list | 699 | * (I think this is pointless now, since the relevant list | |
700 | * traversals will no longer see this node anyway. -mycroft) | 700 | * traversals will no longer see this node anyway. -mycroft) | |
701 | */ | 701 | */ | |
702 | cohort->type = op | OP_INVISIBLE; | 702 | cohort->type = op | OP_INVISIBLE; | |
703 | Lst_Append(&gn->cohorts, cohort); | 703 | Lst_Append(&gn->cohorts, cohort); | |
704 | cohort->centurion = gn; | 704 | cohort->centurion = gn; | |
705 | gn->unmade_cohorts++; | 705 | gn->unmade_cohorts++; | |
706 | snprintf(cohort->cohort_num, sizeof cohort->cohort_num, "#%d", | 706 | snprintf(cohort->cohort_num, sizeof cohort->cohort_num, "#%d", | |
707 | (unsigned int)gn->unmade_cohorts % 1000000); | 707 | (unsigned int)gn->unmade_cohorts % 1000000); | |
708 | } else { | 708 | } else { | |
709 | gn->type |= op; /* preserve any previous flags */ | 709 | gn->type |= op; /* preserve any previous flags */ | |
710 | } | 710 | } | |
711 | 711 | |||
712 | return true; | 712 | return true; | |
713 | } | 713 | } | |
714 | 714 | |||
715 | static void | 715 | static void | |
716 | ApplyDependencyOperator(GNodeType op) | 716 | ApplyDependencyOperator(GNodeType op) | |
717 | { | 717 | { | |
718 | GNodeListNode *ln; | 718 | GNodeListNode *ln; | |
719 | 719 | |||
720 | for (ln = targets->first; ln != NULL; ln = ln->next) | 720 | for (ln = targets->first; ln != NULL; ln = ln->next) | |
721 | if (!TryApplyDependencyOperator(ln->datum, op)) | 721 | if (!TryApplyDependencyOperator(ln->datum, op)) | |
722 | break; | 722 | break; | |
723 | } | 723 | } | |
724 | 724 | |||
725 | /* | 725 | /* | |
726 | * Add a .WAIT node in the dependency list. After any dynamic dependencies | 726 | * Add a .WAIT node in the dependency list. After any dynamic dependencies | |
727 | * (and filename globbing) have happened, it is given a dependency on each | 727 | * (and filename globbing) have happened, it is given a dependency on each | |
728 | * previous child, back until the previous .WAIT node. The next child won't | 728 | * previous child, back until the previous .WAIT node. The next child won't | |
729 | * be scheduled until the .WAIT node is built. | 729 | * be scheduled until the .WAIT node is built. | |
730 | * | 730 | * | |
731 | * Give each .WAIT node a unique name (mainly for diagnostics). | 731 | * Give each .WAIT node a unique name (mainly for diagnostics). | |
732 | */ | 732 | */ | |
733 | static void | 733 | static void | |
734 | ApplyDependencySourceWait(bool isSpecial) | 734 | ApplyDependencySourceWait(bool isSpecial) | |
735 | { | 735 | { | |
736 | static unsigned wait_number = 0; | 736 | static unsigned wait_number = 0; | |
737 | char name[6 + 10 + 1]; | 737 | char name[6 + 10 + 1]; | |
738 | GNode *gn; | 738 | GNode *gn; | |
739 | 739 | |||
740 | snprintf(name, sizeof name, ".WAIT_%u", ++wait_number); | 740 | snprintf(name, sizeof name, ".WAIT_%u", ++wait_number); | |
741 | gn = Targ_NewInternalNode(name); | 741 | gn = Targ_NewInternalNode(name); | |
742 | if (doing_depend) | 742 | if (doing_depend) | |
743 | RememberLocation(gn); | 743 | RememberLocation(gn); | |
744 | gn->type = OP_WAIT | OP_PHONY | OP_DEPENDS | OP_NOTMAIN; | 744 | gn->type = OP_WAIT | OP_PHONY | OP_DEPENDS | OP_NOTMAIN; | |
745 | LinkToTargets(gn, isSpecial); | 745 | LinkToTargets(gn, isSpecial); | |
746 | } | 746 | } | |
747 | 747 | |||
748 | static bool | 748 | static bool | |
749 | ApplyDependencySourceKeyword(const char *src, ParseSpecial special) | 749 | ApplyDependencySourceKeyword(const char *src, ParseSpecial special) | |
750 | { | 750 | { | |
751 | int keywd; | 751 | int keywd; | |
752 | GNodeType targetAttr; | 752 | GNodeType targetAttr; | |
753 | 753 | |||
754 | if (*src != '.' || !ch_isupper(src[1])) | 754 | if (*src != '.' || !ch_isupper(src[1])) | |
755 | return false; | 755 | return false; | |
756 | 756 | |||
757 | keywd = FindKeyword(src); | 757 | keywd = FindKeyword(src); | |
758 | if (keywd == -1) | 758 | if (keywd == -1) | |
759 | return false; | 759 | return false; | |
760 | 760 | |||
761 | targetAttr = parseKeywords[keywd].targetAttr; | 761 | targetAttr = parseKeywords[keywd].targetAttr; | |
762 | if (targetAttr != OP_NONE) { | 762 | if (targetAttr != OP_NONE) { | |
763 | ApplyDependencyOperator(targetAttr); | 763 | ApplyDependencyOperator(targetAttr); | |
764 | return true; | 764 | return true; | |
765 | } | 765 | } | |
766 | if (parseKeywords[keywd].special == SP_WAIT) { | 766 | if (parseKeywords[keywd].special == SP_WAIT) { | |
767 | ApplyDependencySourceWait(special != SP_NOT); | 767 | ApplyDependencySourceWait(special != SP_NOT); | |
768 | return true; | 768 | return true; | |
769 | } | 769 | } | |
770 | return false; | 770 | return false; | |
771 | } | 771 | } | |
772 | 772 | |||
773 | /* | 773 | /* | |
774 | * In a line like ".MAIN: source1 source2", add all sources to the list of | 774 | * In a line like ".MAIN: source1 source2", add all sources to the list of | |
775 | * things to create, but only if the user didn't specify a target on the | 775 | * things to create, but only if the user didn't specify a target on the | |
776 | * command line and .MAIN occurs for the first time. | 776 | * command line and .MAIN occurs for the first time. | |
777 | * | 777 | * | |
778 | * See HandleDependencyTargetSpecial, branch SP_MAIN. | 778 | * See HandleDependencyTargetSpecial, branch SP_MAIN. | |
779 | * See unit-tests/cond-func-make-main.mk. | 779 | * See unit-tests/cond-func-make-main.mk. | |
780 | */ | 780 | */ | |
781 | static void | 781 | static void | |
782 | ApplyDependencySourceMain(const char *src) | 782 | ApplyDependencySourceMain(const char *src) | |
783 | { | 783 | { | |
784 | Lst_Append(&opts.create, bmake_strdup(src)); | 784 | Lst_Append(&opts.create, bmake_strdup(src)); | |
785 | /* | 785 | /* | |
786 | * Add the name to the .TARGETS variable as well, so the user can | 786 | * Add the name to the .TARGETS variable as well, so the user can | |
787 | * employ that, if desired. | 787 | * employ that, if desired. | |
788 | */ | 788 | */ | |
789 | Global_Append(".TARGETS", src); | 789 | Global_Append(".TARGETS", src); | |
790 | } | 790 | } | |
791 | 791 | |||
792 | /* | 792 | /* | |
793 | * For the sources of a .ORDER target, create predecessor/successor links | 793 | * For the sources of a .ORDER target, create predecessor/successor links | |
794 | * between the previous source and the current one. | 794 | * between the previous source and the current one. | |
795 | */ | 795 | */ | |
796 | static void | 796 | static void | |
797 | ApplyDependencySourceOrder(const char *src) | 797 | ApplyDependencySourceOrder(const char *src) | |
798 | { | 798 | { | |
799 | GNode *gn; | 799 | GNode *gn; | |
800 | 800 | |||
801 | gn = Targ_GetNode(src); | 801 | gn = Targ_GetNode(src); | |
802 | if (doing_depend) | 802 | if (doing_depend) | |
803 | RememberLocation(gn); | 803 | RememberLocation(gn); | |
804 | if (order_pred != NULL) { | 804 | if (order_pred != NULL) { | |
805 | Lst_Append(&order_pred->order_succ, gn); | 805 | Lst_Append(&order_pred->order_succ, gn); | |
806 | Lst_Append(&gn->order_pred, order_pred); | 806 | Lst_Append(&gn->order_pred, order_pred); | |
807 | if (DEBUG(PARSE)) { | 807 | if (DEBUG(PARSE)) { | |
808 | debug_printf( | 808 | debug_printf( | |
809 | "# .ORDER forces '%s' to be made before '%s'\n", | 809 | "# .ORDER forces '%s' to be made before '%s'\n", | |
810 | order_pred->name, gn->name); | 810 | order_pred->name, gn->name); | |
811 | Targ_PrintNode(order_pred, 0); | 811 | Targ_PrintNode(order_pred, 0); | |
812 | Targ_PrintNode(gn, 0); | 812 | Targ_PrintNode(gn, 0); | |
813 | } | 813 | } | |
814 | } | 814 | } | |
815 | /* The current source now becomes the predecessor for the next one. */ | 815 | /* The current source now becomes the predecessor for the next one. */ | |
816 | order_pred = gn; | 816 | order_pred = gn; | |
817 | } | 817 | } | |
818 | 818 | |||
819 | /* The source is not an attribute, so find/create a node for it. */ | 819 | /* The source is not an attribute, so find/create a node for it. */ | |
820 | static void | 820 | static void | |
821 | ApplyDependencySourceOther(const char *src, GNodeType targetAttr, | 821 | ApplyDependencySourceOther(const char *src, GNodeType targetAttr, | |
822 | ParseSpecial special) | 822 | ParseSpecial special) | |
823 | { | 823 | { | |
824 | GNode *gn; | 824 | GNode *gn; | |
825 | 825 | |||
826 | gn = Targ_GetNode(src); | 826 | gn = Targ_GetNode(src); | |
827 | if (doing_depend) | 827 | if (doing_depend) | |
828 | RememberLocation(gn); | 828 | RememberLocation(gn); | |
829 | if (targetAttr != OP_NONE) | 829 | if (targetAttr != OP_NONE) | |
830 | gn->type |= targetAttr; | 830 | gn->type |= targetAttr; | |
831 | else | 831 | else | |
832 | LinkToTargets(gn, special != SP_NOT); | 832 | LinkToTargets(gn, special != SP_NOT); | |
833 | } | 833 | } | |
834 | 834 | |||
835 | /* | 835 | /* | |
836 | * Given the name of a source in a dependency line, figure out if it is an | 836 | * Given the name of a source in a dependency line, figure out if it is an | |
837 | * attribute (such as .SILENT) and if so, apply it to all targets. Otherwise | 837 | * attribute (such as .SILENT) and if so, apply it to all targets. Otherwise | |
838 | * decide if there is some attribute which should be applied *to* the source | 838 | * decide if there is some attribute which should be applied *to* the source | |
839 | * because of some special target (such as .PHONY) and apply it if so. | 839 | * because of some special target (such as .PHONY) and apply it if so. | |
840 | * Otherwise, make the source a child of the targets. | 840 | * Otherwise, make the source a child of the targets. | |
841 | */ | 841 | */ | |
842 | static void | 842 | static void | |
843 | ApplyDependencySource(GNodeType targetAttr, const char *src, | 843 | ApplyDependencySource(GNodeType targetAttr, const char *src, | |
844 | ParseSpecial special) | 844 | ParseSpecial special) | |
845 | { | 845 | { | |
846 | if (ApplyDependencySourceKeyword(src, special)) | 846 | if (ApplyDependencySourceKeyword(src, special)) | |
847 | return; | 847 | return; | |
848 | 848 | |||
849 | if (special == SP_MAIN) | 849 | if (special == SP_MAIN) | |
850 | ApplyDependencySourceMain(src); | 850 | ApplyDependencySourceMain(src); | |
851 | else if (special == SP_ORDER) | 851 | else if (special == SP_ORDER) | |
852 | ApplyDependencySourceOrder(src); | 852 | ApplyDependencySourceOrder(src); | |
853 | else | 853 | else | |
854 | ApplyDependencySourceOther(src, targetAttr, special); | 854 | ApplyDependencySourceOther(src, targetAttr, special); | |
855 | } | 855 | } | |
856 | 856 | |||
857 | /* | 857 | /* | |
858 | * If we have yet to decide on a main target to make, in the absence of any | 858 | * If we have yet to decide on a main target to make, in the absence of any | |
859 | * user input, we want the first target on the first dependency line that is | 859 | * user input, we want the first target on the first dependency line that is | |
860 | * actually a real target (i.e. isn't a .USE or .EXEC rule) to be made. | 860 | * actually a real target (i.e. isn't a .USE or .EXEC rule) to be made. | |
861 | */ | 861 | */ | |
862 | static void | 862 | static void | |
863 | MaybeUpdateMainTarget(void) | 863 | MaybeUpdateMainTarget(void) | |
864 | { | 864 | { | |
865 | GNodeListNode *ln; | 865 | GNodeListNode *ln; | |
866 | 866 | |||
867 | if (mainNode != NULL) | 867 | if (mainNode != NULL) | |
868 | return; | 868 | return; | |
869 | 869 | |||
870 | for (ln = targets->first; ln != NULL; ln = ln->next) { | 870 | for (ln = targets->first; ln != NULL; ln = ln->next) { | |
871 | GNode *gn = ln->datum; | 871 | GNode *gn = ln->datum; | |
872 | if (GNode_IsMainCandidate(gn)) { | 872 | if (GNode_IsMainCandidate(gn)) { | |
873 | DEBUG1(MAKE, "Setting main node to \"%s\"\n", | 873 | DEBUG1(MAKE, "Setting main node to \"%s\"\n", | |
874 | gn->name); | 874 | gn->name); | |
875 | mainNode = gn; | 875 | mainNode = gn; | |
876 | return; | 876 | return; | |
877 | } | 877 | } | |
878 | } | 878 | } | |
879 | } | 879 | } | |
880 | 880 | |||
881 | static void | 881 | static void | |
882 | InvalidLineType(const char *line, const char *unexpanded_line) | 882 | InvalidLineType(const char *line, const char *unexpanded_line) | |
883 | { | 883 | { | |
884 | if (unexpanded_line[0] == '.') { | 884 | if (unexpanded_line[0] == '.') { | |
885 | const char *dirstart = unexpanded_line + 1; | 885 | const char *dirstart = unexpanded_line + 1; | |
886 | const char *dirend; | 886 | const char *dirend; | |
887 | cpp_skip_whitespace(&dirstart); | 887 | cpp_skip_whitespace(&dirstart); | |
888 | dirend = dirstart; | 888 | dirend = dirstart; | |
889 | while (ch_isalnum(*dirend) || *dirend == '-') | 889 | while (ch_isalnum(*dirend) || *dirend == '-') | |
890 | dirend++; | 890 | dirend++; | |
891 | Parse_Error(PARSE_FATAL, "Unknown directive \"%.*s\"", | 891 | Parse_Error(PARSE_FATAL, "Unknown directive \"%.*s\"", | |
892 | (int)(dirend - dirstart), dirstart); | 892 | (int)(dirend - dirstart), dirstart); | |
893 | } else if (strcmp(line, unexpanded_line) == 0) | 893 | } else if (strcmp(line, unexpanded_line) == 0) | |
894 | Parse_Error(PARSE_FATAL, "Invalid line '%s'", line); | 894 | Parse_Error(PARSE_FATAL, "Invalid line '%s'", line); | |
895 | else | 895 | else | |
896 | Parse_Error(PARSE_FATAL, | 896 | Parse_Error(PARSE_FATAL, | |
897 | "Invalid line '%s', expanded to '%s'", | 897 | "Invalid line '%s', expanded to '%s'", | |
898 | unexpanded_line, line); | 898 | unexpanded_line, line); | |
899 | } | 899 | } | |
900 | 900 | |||
901 | static void | 901 | static void | |
902 | ParseDependencyTargetWord(char **pp, const char *lstart) | 902 | ParseDependencyTargetWord(char **pp, const char *lstart) | |
903 | { | 903 | { | |
904 | const char *p = *pp; | 904 | const char *p = *pp; | |
905 | 905 | |||
906 | while (*p != '\0') { | 906 | while (*p != '\0') { | |
907 | if ((ch_isspace(*p) || *p == '!' || *p == ':' || *p == '(') | 907 | if ((ch_isspace(*p) || *p == '!' || *p == ':' || *p == '(') | |
908 | && !IsEscaped(lstart, p)) | 908 | && !IsEscaped(lstart, p)) | |
909 | break; | 909 | break; | |
910 | 910 | |||
911 | if (*p == '$') { | 911 | if (*p == '$') { | |
912 | FStr val = Var_Parse(&p, SCOPE_CMDLINE, | 912 | FStr val = Var_Parse(&p, SCOPE_CMDLINE, | |
913 | VARE_PARSE_ONLY); | 913 | VARE_PARSE_ONLY); | |
914 | /* TODO: handle errors */ | 914 | /* TODO: handle errors */ | |
915 | FStr_Done(&val); | 915 | FStr_Done(&val); | |
916 | } else | 916 | } else | |
917 | p++; | 917 | p++; | |
918 | } | 918 | } | |
919 | 919 | |||
920 | *pp += p - *pp; | 920 | *pp += p - *pp; | |
921 | } | 921 | } | |
922 | 922 | |||
923 | /* | 923 | /* | |
924 | * Handle special targets like .PATH, .DEFAULT, .BEGIN, .ORDER. | 924 | * Handle special targets like .PATH, .DEFAULT, .BEGIN, .ORDER. | |
925 | * | 925 | * | |
926 | * See the tests deptgt-*.mk. | 926 | * See the tests deptgt-*.mk. | |
927 | */ | 927 | */ | |
928 | static void | 928 | static void | |
929 | HandleDependencyTargetSpecial(const char *targetName, | 929 | HandleDependencyTargetSpecial(const char *targetName, | |
930 | ParseSpecial *inout_special, | 930 | ParseSpecial *inout_special, | |
931 | SearchPathList **inout_paths) | 931 | SearchPathList **inout_paths) | |
932 | { | 932 | { | |
933 | switch (*inout_special) { | 933 | switch (*inout_special) { | |
934 | case SP_PATH: | 934 | case SP_PATH: | |
935 | if (*inout_paths == NULL) | 935 | if (*inout_paths == NULL) | |
936 | *inout_paths = Lst_New(); | 936 | *inout_paths = Lst_New(); | |
937 | Lst_Append(*inout_paths, &dirSearchPath); | 937 | Lst_Append(*inout_paths, &dirSearchPath); | |
938 | break; | 938 | break; | |
939 | case SP_SYSPATH: | 939 | case SP_SYSPATH: | |
940 | if (*inout_paths == NULL) | 940 | if (*inout_paths == NULL) | |
941 | *inout_paths = Lst_New(); | 941 | *inout_paths = Lst_New(); | |
942 | Lst_Append(*inout_paths, sysIncPath); | 942 | Lst_Append(*inout_paths, sysIncPath); | |
943 | break; | 943 | break; | |
944 | case SP_MAIN: | 944 | case SP_MAIN: | |
945 | /* | 945 | /* | |
946 | * Allow targets from the command line to override the | 946 | * Allow targets from the command line to override the | |
947 | * .MAIN node. | 947 | * .MAIN node. | |
948 | */ | 948 | */ | |
949 | if (!Lst_IsEmpty(&opts.create)) | 949 | if (!Lst_IsEmpty(&opts.create)) | |
950 | *inout_special = SP_NOT; | 950 | *inout_special = SP_NOT; | |
951 | break; | 951 | break; | |
952 | case SP_BEGIN: | 952 | case SP_BEGIN: | |
953 | case SP_END: | 953 | case SP_END: | |
954 | case SP_STALE: | 954 | case SP_STALE: | |
955 | case SP_ERROR: | 955 | case SP_ERROR: | |
956 | case SP_INTERRUPT: { | 956 | case SP_INTERRUPT: { | |
957 | GNode *gn = Targ_GetNode(targetName); | 957 | GNode *gn = Targ_GetNode(targetName); | |
958 | if (doing_depend) | 958 | if (doing_depend) | |
959 | RememberLocation(gn); | 959 | RememberLocation(gn); | |
960 | gn->type |= OP_NOTMAIN | OP_SPECIAL; | 960 | gn->type |= OP_NOTMAIN | OP_SPECIAL; | |
961 | Lst_Append(targets, gn); | 961 | Lst_Append(targets, gn); | |
962 | break; | 962 | break; | |
963 | } | 963 | } | |
964 | case SP_DEFAULT: { | 964 | case SP_DEFAULT: { | |
965 | /* | 965 | /* | |
966 | * Need to create a node to hang commands on, but we don't | 966 | * Need to create a node to hang commands on, but we don't | |
967 | * want it in the graph, nor do we want it to be the Main | 967 | * want it in the graph, nor do we want it to be the Main | |
968 | * Target. We claim the node is a transformation rule to make | 968 | * Target. We claim the node is a transformation rule to make | |
969 | * life easier later, when we'll use Make_HandleUse to | 969 | * life easier later, when we'll use Make_HandleUse to | |
970 | * actually apply the .DEFAULT commands. | 970 | * actually apply the .DEFAULT commands. | |
971 | */ | 971 | */ | |
972 | GNode *gn = GNode_New(".DEFAULT"); | 972 | GNode *gn = GNode_New(".DEFAULT"); | |
973 | gn->type |= OP_NOTMAIN | OP_TRANSFORM; | 973 | gn->type |= OP_NOTMAIN | OP_TRANSFORM; | |
974 | Lst_Append(targets, gn); | 974 | Lst_Append(targets, gn); | |
975 | defaultNode = gn; | 975 | defaultNode = gn; | |
976 | break; | 976 | break; | |
977 | } | 977 | } | |
978 | case SP_DELETE_ON_ERROR: | 978 | case SP_DELETE_ON_ERROR: | |
979 | deleteOnError = true; | 979 | deleteOnError = true; | |
980 | break; | 980 | break; | |
981 | case SP_NOTPARALLEL: | 981 | case SP_NOTPARALLEL: | |
982 | opts.maxJobs = 1; | 982 | opts.maxJobs = 1; | |
983 | break; | 983 | break; | |
984 | case SP_SINGLESHELL: | 984 | case SP_SINGLESHELL: | |
985 | opts.compatMake = true; | 985 | opts.compatMake = true; | |
986 | break; | 986 | break; | |
987 | case SP_ORDER: | 987 | case SP_ORDER: | |
988 | order_pred = NULL; | 988 | order_pred = NULL; | |
989 | break; | 989 | break; | |
990 | default: | 990 | default: | |
991 | break; | 991 | break; | |
992 | } | 992 | } | |
993 | } | 993 | } | |
994 | 994 | |||
995 | static bool | 995 | static bool | |
996 | HandleDependencyTargetPath(const char *suffixName, | 996 | HandleDependencyTargetPath(const char *suffixName, | |
997 | SearchPathList **inout_paths) | 997 | SearchPathList **inout_paths) | |
998 | { | 998 | { | |
999 | SearchPath *path; | 999 | SearchPath *path; | |
1000 | 1000 | |||
1001 | path = Suff_GetPath(suffixName); | 1001 | path = Suff_GetPath(suffixName); | |
1002 | if (path == NULL) { | 1002 | if (path == NULL) { | |
1003 | Parse_Error(PARSE_FATAL, | 1003 | Parse_Error(PARSE_FATAL, | |
1004 | "Suffix '%s' not defined (yet)", suffixName); | 1004 | "Suffix '%s' not defined (yet)", suffixName); | |
1005 | return false; | 1005 | return false; | |
1006 | } | 1006 | } | |
1007 | 1007 | |||
1008 | if (*inout_paths == NULL) | 1008 | if (*inout_paths == NULL) | |
1009 | *inout_paths = Lst_New(); | 1009 | *inout_paths = Lst_New(); | |
1010 | Lst_Append(*inout_paths, path); | 1010 | Lst_Append(*inout_paths, path); | |
1011 | 1011 | |||
1012 | return true; | 1012 | return true; | |
1013 | } | 1013 | } | |
1014 | 1014 | |||
1015 | /* See if it's a special target and if so set inout_special to match it. */ | 1015 | /* See if it's a special target and if so set inout_special to match it. */ | |
1016 | static bool | 1016 | static bool | |
1017 | HandleDependencyTarget(const char *targetName, | 1017 | HandleDependencyTarget(const char *targetName, | |
1018 | ParseSpecial *inout_special, | 1018 | ParseSpecial *inout_special, | |
1019 | GNodeType *inout_targetAttr, | 1019 | GNodeType *inout_targetAttr, | |
1020 | SearchPathList **inout_paths) | 1020 | SearchPathList **inout_paths) | |
1021 | { | 1021 | { | |
1022 | int keywd; | 1022 | int keywd; | |
1023 | 1023 | |||
1024 | if (!(targetName[0] == '.' && ch_isupper(targetName[1]))) | 1024 | if (!(targetName[0] == '.' && ch_isupper(targetName[1]))) | |
1025 | return true; | 1025 | return true; | |
1026 | 1026 | |||
1027 | /* | 1027 | /* | |
1028 | * See if the target is a special target that must have it | 1028 | * See if the target is a special target that must have it | |
1029 | * or its sources handled specially. | 1029 | * or its sources handled specially. | |
1030 | */ | 1030 | */ | |
1031 | keywd = FindKeyword(targetName); | 1031 | keywd = FindKeyword(targetName); | |
1032 | if (keywd != -1) { | 1032 | if (keywd != -1) { | |
1033 | if (*inout_special == SP_PATH && | 1033 | if (*inout_special == SP_PATH && | |
1034 | parseKeywords[keywd].special != SP_PATH) { | 1034 | parseKeywords[keywd].special != SP_PATH) { | |
1035 | Parse_Error(PARSE_FATAL, "Mismatched special targets"); | 1035 | Parse_Error(PARSE_FATAL, "Mismatched special targets"); | |
1036 | return false; | 1036 | return false; | |
1037 | } | 1037 | } | |
1038 | 1038 | |||
1039 | *inout_special = parseKeywords[keywd].special; | 1039 | *inout_special = parseKeywords[keywd].special; | |
1040 | *inout_targetAttr = parseKeywords[keywd].targetAttr; | 1040 | *inout_targetAttr = parseKeywords[keywd].targetAttr; | |
1041 | 1041 | |||
1042 | HandleDependencyTargetSpecial(targetName, inout_special, | 1042 | HandleDependencyTargetSpecial(targetName, inout_special, | |
1043 | inout_paths); | 1043 | inout_paths); | |
1044 | 1044 | |||
1045 | } else if (strncmp(targetName, ".PATH", 5) == 0) { | 1045 | } else if (strncmp(targetName, ".PATH", 5) == 0) { | |
1046 | *inout_special = SP_PATH; | 1046 | *inout_special = SP_PATH; | |
1047 | if (!HandleDependencyTargetPath(targetName + 5, inout_paths)) | 1047 | if (!HandleDependencyTargetPath(targetName + 5, inout_paths)) | |
1048 | return false; | 1048 | return false; | |
1049 | } | 1049 | } | |
1050 | return true; | 1050 | return true; | |
1051 | } | 1051 | } | |
1052 | 1052 | |||
1053 | static void | 1053 | static void | |
1054 | HandleSingleDependencyTargetMundane(const char *name) | 1054 | HandleSingleDependencyTargetMundane(const char *name) | |
1055 | { | 1055 | { | |
1056 | GNode *gn = Suff_IsTransform(name) | 1056 | GNode *gn = Suff_IsTransform(name) | |
1057 | ? Suff_AddTransform(name) | 1057 | ? Suff_AddTransform(name) | |
1058 | : Targ_GetNode(name); | 1058 | : Targ_GetNode(name); | |
1059 | if (doing_depend) | 1059 | if (doing_depend) | |
1060 | RememberLocation(gn); | 1060 | RememberLocation(gn); | |
1061 | 1061 | |||
1062 | Lst_Append(targets, gn); | 1062 | Lst_Append(targets, gn); | |
1063 | } | 1063 | } | |
1064 | 1064 | |||
1065 | static void | 1065 | static void | |
1066 | HandleDependencyTargetMundane(const char *targetName) | 1066 | HandleDependencyTargetMundane(const char *targetName) | |
1067 | { | 1067 | { | |
1068 | if (Dir_HasWildcards(targetName)) { | 1068 | if (Dir_HasWildcards(targetName)) { | |
1069 | StringList targetNames = LST_INIT; | 1069 | StringList targetNames = LST_INIT; | |
1070 | 1070 | |||
1071 | SearchPath *emptyPath = SearchPath_New(); | 1071 | SearchPath *emptyPath = SearchPath_New(); | |
1072 | SearchPath_Expand(emptyPath, targetName, &targetNames); | 1072 | SearchPath_Expand(emptyPath, targetName, &targetNames); | |
1073 | SearchPath_Free(emptyPath); | 1073 | SearchPath_Free(emptyPath); | |
1074 | 1074 | |||
1075 | while (!Lst_IsEmpty(&targetNames)) { | 1075 | while (!Lst_IsEmpty(&targetNames)) { | |
1076 | char *targName = Lst_Dequeue(&targetNames); | 1076 | char *targName = Lst_Dequeue(&targetNames); | |
1077 | HandleSingleDependencyTargetMundane(targName); | 1077 | HandleSingleDependencyTargetMundane(targName); | |
1078 | free(targName); | 1078 | free(targName); | |
1079 | } | 1079 | } | |
1080 | } else | 1080 | } else | |
1081 | HandleSingleDependencyTargetMundane(targetName); | 1081 | HandleSingleDependencyTargetMundane(targetName); | |
1082 | } | 1082 | } | |
1083 | 1083 | |||
1084 | static void | 1084 | static void | |
1085 | SkipExtraTargets(char **pp, const char *lstart) | 1085 | SkipExtraTargets(char **pp, const char *lstart) | |
1086 | { | 1086 | { | |
1087 | bool warning = false; | 1087 | bool warning = false; | |
1088 | const char *p = *pp; | 1088 | const char *p = *pp; | |
1089 | 1089 | |||
1090 | while (*p != '\0') { | 1090 | while (*p != '\0') { | |
1091 | if (!IsEscaped(lstart, p) && (*p == '!' || *p == ':')) | 1091 | if (!IsEscaped(lstart, p) && (*p == '!' || *p == ':')) | |
1092 | break; | 1092 | break; | |
1093 | if (IsEscaped(lstart, p) || (*p != ' ' && *p != '\t')) | 1093 | if (IsEscaped(lstart, p) || (*p != ' ' && *p != '\t')) | |
1094 | warning = true; | 1094 | warning = true; | |
1095 | p++; | 1095 | p++; | |
1096 | } | 1096 | } | |
1097 | if (warning) { | 1097 | if (warning) { | |
1098 | const char *start = *pp; | 1098 | const char *start = *pp; | |
1099 | cpp_skip_whitespace(&start); | 1099 | cpp_skip_whitespace(&start); | |
1100 | Parse_Error(PARSE_WARNING, "Extra target '%.*s' ignored", | 1100 | Parse_Error(PARSE_WARNING, "Extra target '%.*s' ignored", | |
1101 | (int)(p - start), start); | 1101 | (int)(p - start), start); | |
1102 | } | 1102 | } | |
1103 | 1103 | |||
1104 | *pp += p - *pp; | 1104 | *pp += p - *pp; | |
1105 | } | 1105 | } | |
1106 | 1106 | |||
1107 | static void | 1107 | static void | |
1108 | CheckSpecialMundaneMixture(ParseSpecial special) | 1108 | CheckSpecialMundaneMixture(ParseSpecial special) | |
1109 | { | 1109 | { | |
1110 | switch (special) { | 1110 | switch (special) { | |
1111 | case SP_DEFAULT: | 1111 | case SP_DEFAULT: | |
1112 | case SP_STALE: | 1112 | case SP_STALE: | |
1113 | case SP_BEGIN: | 1113 | case SP_BEGIN: | |
1114 | case SP_END: | 1114 | case SP_END: | |
1115 | case SP_ERROR: | 1115 | case SP_ERROR: | |
1116 | case SP_INTERRUPT: | 1116 | case SP_INTERRUPT: | |
1117 | /* | 1117 | /* | |
1118 | * These create nodes on which to hang commands, so targets | 1118 | * These create nodes on which to hang commands, so targets | |
1119 | * shouldn't be empty. | 1119 | * shouldn't be empty. | |
1120 | */ | 1120 | */ | |
1121 | case SP_NOT: | 1121 | case SP_NOT: | |
1122 | /* Nothing special here -- targets may be empty. */ | 1122 | /* Nothing special here -- targets may be empty. */ | |
1123 | break; | 1123 | break; | |
1124 | default: | 1124 | default: | |
1125 | Parse_Error(PARSE_WARNING, | 1125 | Parse_Error(PARSE_WARNING, | |
1126 | "Special and mundane targets don't mix. " | 1126 | "Special and mundane targets don't mix. " | |
1127 | "Mundane ones ignored"); | 1127 | "Mundane ones ignored"); | |
1128 | break; | 1128 | break; | |
1129 | } | 1129 | } | |
1130 | } | 1130 | } | |
1131 | 1131 | |||
1132 | /* | 1132 | /* | |
1133 | * In a dependency line like 'targets: sources' or 'targets! sources', parse | 1133 | * In a dependency line like 'targets: sources' or 'targets! sources', parse | |
1134 | * the operator ':', '::' or '!' from between the targets and the sources. | 1134 | * the operator ':', '::' or '!' from between the targets and the sources. | |
1135 | */ | 1135 | */ | |
1136 | static GNodeType | 1136 | static GNodeType | |
1137 | ParseDependencyOp(char **pp) | 1137 | ParseDependencyOp(char **pp) | |
1138 | { | 1138 | { | |
1139 | if (**pp == '!') | 1139 | if (**pp == '!') | |
1140 | return (*pp)++, OP_FORCE; | 1140 | return (*pp)++, OP_FORCE; | |
1141 | if (**pp == ':' && (*pp)[1] == ':') | 1141 | if (**pp == ':' && (*pp)[1] == ':') | |
1142 | return *pp += 2, OP_DOUBLEDEP; | 1142 | return *pp += 2, OP_DOUBLEDEP; | |
1143 | else if (**pp == ':') | 1143 | else if (**pp == ':') | |
1144 | return (*pp)++, OP_DEPENDS; | 1144 | return (*pp)++, OP_DEPENDS; | |
1145 | else | 1145 | else | |
1146 | return OP_NONE; | 1146 | return OP_NONE; | |
1147 | } | 1147 | } | |
1148 | 1148 | |||
1149 | static void | 1149 | static void | |
1150 | ClearPaths(ParseSpecial special, SearchPathList *paths) | 1150 | ClearPaths(ParseSpecial special, SearchPathList *paths) | |
1151 | { | 1151 | { | |
1152 | if (paths != NULL) { | 1152 | if (paths != NULL) { | |
1153 | SearchPathListNode *ln; | 1153 | SearchPathListNode *ln; | |
1154 | for (ln = paths->first; ln != NULL; ln = ln->next) | 1154 | for (ln = paths->first; ln != NULL; ln = ln->next) | |
1155 | SearchPath_Clear(ln->datum); | 1155 | SearchPath_Clear(ln->datum); | |
1156 | } | 1156 | } | |
1157 | if (special == SP_SYSPATH) | 1157 | if (special == SP_SYSPATH) | |
1158 | Dir_SetSYSPATH(); | 1158 | Dir_SetSYSPATH(); | |
1159 | else | 1159 | else | |
1160 | Dir_SetPATH(); | 1160 | Dir_SetPATH(); | |
1161 | } | 1161 | } | |
1162 | 1162 | |||
1163 | static char * | 1163 | static char * | |
1164 | FindInDirOfIncludingFile(const char *file) | 1164 | FindInDirOfIncludingFile(const char *file) | |
1165 | { | 1165 | { | |
1166 | char *fullname, *incdir, *slash, *newName; | 1166 | char *fullname, *incdir, *slash, *newName; | |
1167 | int i; | 1167 | int i; | |
1168 | 1168 | |||
1169 | fullname = NULL; | 1169 | fullname = NULL; | |
1170 | incdir = bmake_strdup(CurFile()->name.str); | 1170 | incdir = bmake_strdup(CurFile()->name.str); | |
1171 | slash = strrchr(incdir, '/'); | 1171 | slash = strrchr(incdir, '/'); | |
1172 | if (slash != NULL) { | 1172 | if (slash != NULL) { | |
1173 | *slash = '\0'; | 1173 | *slash = '\0'; | |
1174 | /* | 1174 | /* | |
1175 | * Now do lexical processing of leading "../" on the | 1175 | * Now do lexical processing of leading "../" on the | |
1176 | * filename. | 1176 | * filename. | |
1177 | */ | 1177 | */ | |
1178 | for (i = 0; strncmp(file + i, "../", 3) == 0; i += 3) { | 1178 | for (i = 0; strncmp(file + i, "../", 3) == 0; i += 3) { | |
1179 | slash = strrchr(incdir + 1, '/'); | 1179 | slash = strrchr(incdir + 1, '/'); | |
1180 | if (slash == NULL || strcmp(slash, "/..") == 0) | 1180 | if (slash == NULL || strcmp(slash, "/..") == 0) | |
1181 | break; | 1181 | break; | |
1182 | *slash = '\0'; | 1182 | *slash = '\0'; | |
1183 | } | 1183 | } | |
1184 | newName = str_concat3(incdir, "/", file + i); | 1184 | newName = str_concat3(incdir, "/", file + i); | |
1185 | fullname = Dir_FindFile(newName, parseIncPath); | 1185 | fullname = Dir_FindFile(newName, parseIncPath); | |
1186 | if (fullname == NULL) | 1186 | if (fullname == NULL) | |
1187 | fullname = Dir_FindFile(newName, &dirSearchPath); | 1187 | fullname = Dir_FindFile(newName, &dirSearchPath); | |
1188 | free(newName); | 1188 | free(newName); | |
1189 | } | 1189 | } | |
1190 | free(incdir); | 1190 | free(incdir); | |
1191 | return fullname; | 1191 | return fullname; | |
1192 | } | 1192 | } | |
1193 | 1193 | |||
1194 | static char * | 1194 | static char * | |
1195 | FindInQuotPath(const char *file) | 1195 | FindInQuotPath(const char *file) | |
1196 | { | 1196 | { | |
1197 | const char *suff; | 1197 | const char *suff; | |
1198 | SearchPath *suffPath; | 1198 | SearchPath *suffPath; | |
1199 | char *fullname; | 1199 | char *fullname; | |
1200 | 1200 | |||
1201 | fullname = FindInDirOfIncludingFile(file); | 1201 | fullname = FindInDirOfIncludingFile(file); | |
1202 | if (fullname == NULL && | 1202 | if (fullname == NULL && | |
1203 | (suff = strrchr(file, '.')) != NULL && | 1203 | (suff = strrchr(file, '.')) != NULL && | |
1204 | (suffPath = Suff_GetPath(suff)) != NULL) | 1204 | (suffPath = Suff_GetPath(suff)) != NULL) | |
1205 | fullname = Dir_FindFile(file, suffPath); | 1205 | fullname = Dir_FindFile(file, suffPath); | |
1206 | if (fullname == NULL) | 1206 | if (fullname == NULL) | |
1207 | fullname = Dir_FindFile(file, parseIncPath); | 1207 | fullname = Dir_FindFile(file, parseIncPath); | |
1208 | if (fullname == NULL) | 1208 | if (fullname == NULL) | |
1209 | fullname = Dir_FindFile(file, &dirSearchPath); | 1209 | fullname = Dir_FindFile(file, &dirSearchPath); | |
1210 | return fullname; | 1210 | return fullname; | |
1211 | } | 1211 | } | |
1212 | 1212 | |||
1213 | static bool | 1213 | static bool | |
1214 | SkipGuarded(const char *fullname) | 1214 | SkipGuarded(const char *fullname) | |
1215 | { | 1215 | { | |
1216 | Guard *guard = HashTable_FindValue(&guards, fullname); | 1216 | Guard *guard = HashTable_FindValue(&guards, fullname); | |
1217 | if (guard != NULL && guard->kind == GK_VARIABLE | 1217 | if (guard != NULL && guard->kind == GK_VARIABLE | |
1218 | && GNode_ValueDirect(SCOPE_GLOBAL, guard->name) != NULL) | 1218 | && GNode_ValueDirect(SCOPE_GLOBAL, guard->name) != NULL) | |
1219 | goto skip; | 1219 | goto skip; | |
1220 | if (guard != NULL && guard->kind == GK_TARGET | 1220 | if (guard != NULL && guard->kind == GK_TARGET | |
1221 | && Targ_FindNode(guard->name) != NULL) | 1221 | && Targ_FindNode(guard->name) != NULL) | |
1222 | goto skip; | 1222 | goto skip; | |
1223 | return false; | 1223 | return false; | |
1224 | 1224 | |||
1225 | skip: | 1225 | skip: | |
1226 | DEBUG2(PARSE, "Skipping '%s' because '%s' is defined\n", | 1226 | DEBUG2(PARSE, "Skipping '%s' because '%s' is defined\n", | |
1227 | fullname, guard->name); | 1227 | fullname, guard->name); | |
1228 | return true; | 1228 | return true; | |
1229 | } | 1229 | } | |
1230 | 1230 | |||
1231 | /* | 1231 | /* | |
1232 | * Handle one of the .[-ds]include directives by remembering the current file | 1232 | * Handle one of the .[-ds]include directives by remembering the current file | |
1233 | * and pushing the included file on the stack. After the included file has | 1233 | * and pushing the included file on the stack. After the included file has | |
1234 | * finished, parsing continues with the including file; see Parse_PushInput | 1234 | * finished, parsing continues with the including file; see Parse_PushInput | |
1235 | * and ParseEOF. | 1235 | * and ParseEOF. | |
1236 | * | 1236 | * | |
1237 | * System includes are looked up in sysIncPath, any other includes are looked | 1237 | * System includes are looked up in sysIncPath, any other includes are looked | |
1238 | * up in the parsedir and then in the directories specified by the -I command | 1238 | * up in the parsedir and then in the directories specified by the -I command | |
1239 | * line options. | 1239 | * line options. | |
1240 | */ | 1240 | */ | |
1241 | static void | 1241 | static void | |
1242 | IncludeFile(const char *file, bool isSystem, bool depinc, bool silent) | 1242 | IncludeFile(const char *file, bool isSystem, bool depinc, bool silent) | |
1243 | { | 1243 | { | |
1244 | Buffer buf; | 1244 | Buffer buf; | |
1245 | char *fullname; /* full pathname of file */ | 1245 | char *fullname; /* full pathname of file */ | |
1246 | int fd; | 1246 | int fd; | |
1247 | 1247 | |||
1248 | fullname = file[0] == '/' ? bmake_strdup(file) : NULL; | 1248 | fullname = file[0] == '/' ? bmake_strdup(file) : NULL; | |
1249 | 1249 | |||
1250 | if (fullname == NULL && !isSystem) | 1250 | if (fullname == NULL && !isSystem) | |
1251 | fullname = FindInQuotPath(file); | 1251 | fullname = FindInQuotPath(file); | |
1252 | 1252 | |||
1253 | if (fullname == NULL) { | 1253 | if (fullname == NULL) { | |
1254 | SearchPath *path = Lst_IsEmpty(&sysIncPath->dirs) | 1254 | SearchPath *path = Lst_IsEmpty(&sysIncPath->dirs) | |
1255 | ? defSysIncPath : sysIncPath; | 1255 | ? defSysIncPath : sysIncPath; | |
1256 | fullname = Dir_FindFile(file, path); | 1256 | fullname = Dir_FindFile(file, path); | |
1257 | } | 1257 | } | |
1258 | 1258 | |||
1259 | if (fullname == NULL) { | 1259 | if (fullname == NULL) { | |
1260 | if (!silent) | 1260 | if (!silent) | |
1261 | Parse_Error(PARSE_FATAL, "Could not find %s", file); | 1261 | Parse_Error(PARSE_FATAL, "Could not find %s", file); | |
1262 | return; | 1262 | return; | |
1263 | } | 1263 | } | |
1264 | 1264 | |||
1265 | if (SkipGuarded(fullname)) | 1265 | if (SkipGuarded(fullname)) | |
1266 | return; | 1266 | return; | |
1267 | 1267 | |||
1268 | if ((fd = open(fullname, O_RDONLY)) == -1) { | 1268 | if ((fd = open(fullname, O_RDONLY)) == -1) { | |
1269 | if (!silent) | 1269 | if (!silent) | |
1270 | Parse_Error(PARSE_FATAL, "Cannot open %s", fullname); | 1270 | Parse_Error(PARSE_FATAL, "Cannot open %s", fullname); | |
1271 | free(fullname); | 1271 | free(fullname); | |
1272 | return; | 1272 | return; | |
1273 | } | 1273 | } | |
1274 | 1274 | |||
1275 | buf = LoadFile(fullname, fd); | 1275 | buf = LoadFile(fullname, fd); | |
1276 | (void)close(fd); | 1276 | (void)close(fd); | |
1277 | 1277 | |||
1278 | Parse_PushInput(fullname, 1, 0, buf, NULL); | 1278 | Parse_PushInput(fullname, 1, 0, buf, NULL); | |
1279 | if (depinc) | 1279 | if (depinc) | |
1280 | doing_depend = depinc; /* only turn it on */ | 1280 | doing_depend = depinc; /* only turn it on */ | |
1281 | free(fullname); | 1281 | free(fullname); | |
1282 | } | 1282 | } | |
1283 | 1283 | |||
1284 | /* Handle a "dependency" line like '.SPECIAL:' without any sources. */ | 1284 | /* Handle a "dependency" line like '.SPECIAL:' without any sources. */ | |
1285 | static void | 1285 | static void | |
1286 | HandleDependencySourcesEmpty(ParseSpecial special, SearchPathList *paths) | 1286 | HandleDependencySourcesEmpty(ParseSpecial special, SearchPathList *paths) | |
1287 | { | 1287 | { | |
1288 | switch (special) { | 1288 | switch (special) { | |
1289 | case SP_SUFFIXES: | 1289 | case SP_SUFFIXES: | |
1290 | Suff_ClearSuffixes(); | 1290 | Suff_ClearSuffixes(); | |
1291 | break; | 1291 | break; | |
1292 | case SP_PRECIOUS: | 1292 | case SP_PRECIOUS: | |
1293 | allPrecious = true; | 1293 | allPrecious = true; | |
1294 | break; | 1294 | break; | |
1295 | case SP_IGNORE: | 1295 | case SP_IGNORE: | |
1296 | opts.ignoreErrors = true; | 1296 | opts.ignoreErrors = true; | |
1297 | break; | 1297 | break; | |
1298 | case SP_SILENT: | 1298 | case SP_SILENT: | |
1299 | opts.silent = true; | 1299 | opts.silent = true; | |
1300 | break; | 1300 | break; | |
1301 | case SP_PATH: | 1301 | case SP_PATH: | |
1302 | case SP_SYSPATH: | 1302 | case SP_SYSPATH: | |
1303 | ClearPaths(special, paths); | 1303 | ClearPaths(special, paths); | |
1304 | break; | 1304 | break; | |
1305 | case SP_POSIX: | 1305 | case SP_POSIX: | |
1306 | if (posix_state == PS_NOW_OR_NEVER) { | 1306 | if (posix_state == PS_NOW_OR_NEVER) { | |
1307 | /* | 1307 | /* | |
1308 | * With '-r', 'posix.mk' (if it exists) | 1308 | * With '-r', 'posix.mk' (if it exists) | |
1309 | * can effectively substitute for 'sys.mk', | 1309 | * can effectively substitute for 'sys.mk', | |
1310 | * otherwise it is an extension. | 1310 | * otherwise it is an extension. | |
1311 | */ | 1311 | */ | |
1312 | Global_Set("%POSIX", "1003.2"); | 1312 | Global_Set("%POSIX", "1003.2"); | |
1313 | IncludeFile("posix.mk", true, false, true); | 1313 | IncludeFile("posix.mk", true, false, true); | |
1314 | } | 1314 | } | |
1315 | break; | 1315 | break; | |
1316 | default: | 1316 | default: | |
1317 | break; | 1317 | break; | |
1318 | } | 1318 | } | |
1319 | } | 1319 | } | |
1320 | 1320 | |||
1321 | static void | 1321 | static void | |
1322 | AddToPaths(const char *dir, SearchPathList *paths) | 1322 | AddToPaths(const char *dir, SearchPathList *paths) | |
1323 | { | 1323 | { | |
1324 | if (paths != NULL) { | 1324 | if (paths != NULL) { | |
1325 | SearchPathListNode *ln; | 1325 | SearchPathListNode *ln; | |
1326 | for (ln = paths->first; ln != NULL; ln = ln->next) | 1326 | for (ln = paths->first; ln != NULL; ln = ln->next) | |
1327 | (void)SearchPath_Add(ln->datum, dir); | 1327 | (void)SearchPath_Add(ln->datum, dir); | |
1328 | } | 1328 | } | |
1329 | } | 1329 | } | |
1330 | 1330 | |||
1331 | /* | 1331 | /* | |
1332 | * If the target was one that doesn't take files as its sources but takes | 1332 | * If the target was one that doesn't take files as its sources but takes | |
1333 | * something like suffixes, we take each space-separated word on the line as | 1333 | * something like suffixes, we take each space-separated word on the line as | |
1334 | * a something and deal with it accordingly. | 1334 | * a something and deal with it accordingly. | |
1335 | */ | 1335 | */ | |
1336 | static void | 1336 | static void | |
1337 | ParseDependencySourceSpecial(ParseSpecial special, const char *word, | 1337 | ParseDependencySourceSpecial(ParseSpecial special, const char *word, | |
1338 | SearchPathList *paths) | 1338 | SearchPathList *paths) | |
1339 | { | 1339 | { | |
1340 | switch (special) { | 1340 | switch (special) { | |
1341 | case SP_SUFFIXES: | 1341 | case SP_SUFFIXES: | |
1342 | Suff_AddSuffix(word); | 1342 | Suff_AddSuffix(word); | |
1343 | break; | 1343 | break; | |
1344 | case SP_PATH: | 1344 | case SP_PATH: | |
1345 | case SP_SYSPATH: | 1345 | case SP_SYSPATH: | |
1346 | AddToPaths(word, paths); | 1346 | AddToPaths(word, paths); | |
1347 | break; | 1347 | break; | |
1348 | case SP_INCLUDES: | 1348 | case SP_INCLUDES: | |
1349 | Suff_AddInclude(word); | 1349 | Suff_AddInclude(word); | |
1350 | break; | 1350 | break; | |
1351 | case SP_LIBS: | 1351 | case SP_LIBS: | |
1352 | Suff_AddLib(word); | 1352 | Suff_AddLib(word); | |
1353 | break; | 1353 | break; | |
1354 | case SP_NOREADONLY: | 1354 | case SP_NOREADONLY: | |
1355 | Var_ReadOnly(word, false); | 1355 | Var_ReadOnly(word, false); | |
1356 | break; | 1356 | break; | |
1357 | case SP_NULL: | 1357 | case SP_NULL: | |
1358 | Suff_SetNull(word); | 1358 | Suff_SetNull(word); | |
1359 | break; | 1359 | break; | |
1360 | case SP_OBJDIR: | 1360 | case SP_OBJDIR: | |
1361 | Main_SetObjdir(false, "%s", word); | 1361 | Main_SetObjdir(false, "%s", word); | |
1362 | break; | 1362 | break; | |
1363 | case SP_READONLY: | 1363 | case SP_READONLY: | |
1364 | Var_ReadOnly(word, true); | 1364 | Var_ReadOnly(word, true); | |
1365 | break; | 1365 | break; | |
1366 | default: | 1366 | default: | |
1367 | break; | 1367 | break; | |
1368 | } | 1368 | } | |
1369 | } | 1369 | } | |
1370 | 1370 | |||
1371 | static bool | 1371 | static bool | |
1372 | ApplyDependencyTarget(char *name, char *nameEnd, ParseSpecial *inout_special, | 1372 | ApplyDependencyTarget(char *name, char *nameEnd, ParseSpecial *inout_special, | |
1373 | GNodeType *inout_targetAttr, | 1373 | GNodeType *inout_targetAttr, | |
1374 | SearchPathList **inout_paths) | 1374 | SearchPathList **inout_paths) | |
1375 | { | 1375 | { | |
1376 | char savedNameEnd = *nameEnd; | 1376 | char savedNameEnd = *nameEnd; | |
1377 | *nameEnd = '\0'; | 1377 | *nameEnd = '\0'; | |
1378 | 1378 | |||
1379 | if (!HandleDependencyTarget(name, inout_special, | 1379 | if (!HandleDependencyTarget(name, inout_special, | |
1380 | inout_targetAttr, inout_paths)) | 1380 | inout_targetAttr, inout_paths)) | |
1381 | return false; | 1381 | return false; | |
1382 | 1382 | |||
1383 | if (*inout_special == SP_NOT && *name != '\0') | 1383 | if (*inout_special == SP_NOT && *name != '\0') | |
1384 | HandleDependencyTargetMundane(name); | 1384 | HandleDependencyTargetMundane(name); | |
1385 | else if (*inout_special == SP_PATH && *name != '.' && *name != '\0') | 1385 | else if (*inout_special == SP_PATH && *name != '.' && *name != '\0') | |
1386 | Parse_Error(PARSE_WARNING, "Extra target (%s) ignored", name); | 1386 | Parse_Error(PARSE_WARNING, "Extra target (%s) ignored", name); | |
1387 | 1387 | |||
1388 | *nameEnd = savedNameEnd; | 1388 | *nameEnd = savedNameEnd; | |
1389 | return true; | 1389 | return true; | |
1390 | } | 1390 | } | |
1391 | 1391 | |||
1392 | static bool | 1392 | static bool | |
1393 | ParseDependencyTargets(char **pp, | 1393 | ParseDependencyTargets(char **pp, | |
1394 | const char *lstart, | 1394 | const char *lstart, | |
1395 | ParseSpecial *inout_special, | 1395 | ParseSpecial *inout_special, | |
1396 | GNodeType *inout_targetAttr, | 1396 | GNodeType *inout_targetAttr, | |
1397 | SearchPathList **inout_paths, | 1397 | SearchPathList **inout_paths, | |
1398 | const char *unexpanded_line) | 1398 | const char *unexpanded_line) | |
1399 | { | 1399 | { | |
1400 | char *p = *pp; | 1400 | char *p = *pp; | |
1401 | 1401 | |||
1402 | for (;;) { | 1402 | for (;;) { | |
1403 | char *tgt = p; | 1403 | char *tgt = p; | |
1404 | 1404 | |||
1405 | ParseDependencyTargetWord(&p, lstart); | 1405 | ParseDependencyTargetWord(&p, lstart); | |
1406 | 1406 | |||
1407 | /* | 1407 | /* | |
1408 | * If the word is followed by a left parenthesis, it's the | 1408 | * If the word is followed by a left parenthesis, it's the | |
1409 | * name of one or more files inside an archive. | 1409 | * name of one or more files inside an archive. | |
1410 | */ | 1410 | */ | |
1411 | if (!IsEscaped(lstart, p) && *p == '(') { | 1411 | if (!IsEscaped(lstart, p) && *p == '(') { | |
1412 | p = tgt; | 1412 | p = tgt; | |
1413 | if (!Arch_ParseArchive(&p, targets, SCOPE_CMDLINE)) { | 1413 | if (!Arch_ParseArchive(&p, targets, SCOPE_CMDLINE)) { | |
1414 | Parse_Error(PARSE_FATAL, | 1414 | Parse_Error(PARSE_FATAL, | |
1415 | "Error in archive specification: \"%s\"", | 1415 | "Error in archive specification: \"%s\"", | |
1416 | tgt); | 1416 | tgt); | |
1417 | return false; | 1417 | return false; | |
1418 | } | 1418 | } | |
1419 | continue; | 1419 | continue; | |
1420 | } | 1420 | } | |
1421 | 1421 | |||
1422 | if (*p == '\0') { | 1422 | if (*p == '\0') { | |
1423 | InvalidLineType(lstart, unexpanded_line); | 1423 | InvalidLineType(lstart, unexpanded_line); | |
1424 | return false; | 1424 | return false; | |
1425 | } | 1425 | } | |
1426 | 1426 | |||
1427 | if (!ApplyDependencyTarget(tgt, p, inout_special, | 1427 | if (!ApplyDependencyTarget(tgt, p, inout_special, | |
1428 | inout_targetAttr, inout_paths)) | 1428 | inout_targetAttr, inout_paths)) | |
1429 | return false; | 1429 | return false; | |
1430 | 1430 | |||
1431 | if (*inout_special != SP_NOT && *inout_special != SP_PATH) | 1431 | if (*inout_special != SP_NOT && *inout_special != SP_PATH) | |
1432 | SkipExtraTargets(&p, lstart); | 1432 | SkipExtraTargets(&p, lstart); | |
1433 | else | 1433 | else | |
1434 | pp_skip_whitespace(&p); | 1434 | pp_skip_whitespace(&p); | |
1435 | 1435 | |||
1436 | if (*p == '\0') | 1436 | if (*p == '\0') | |
1437 | break; | 1437 | break; | |
1438 | if ((*p == '!' || *p == ':') && !IsEscaped(lstart, p)) | 1438 | if ((*p == '!' || *p == ':') && !IsEscaped(lstart, p)) | |
1439 | break; | 1439 | break; | |
1440 | } | 1440 | } | |
1441 | 1441 | |||
1442 | *pp = p; | 1442 | *pp = p; | |
1443 | return true; | 1443 | return true; | |
1444 | } | 1444 | } | |
1445 | 1445 | |||
1446 | static void | 1446 | static void | |
1447 | ParseDependencySourcesSpecial(char *start, | 1447 | ParseDependencySourcesSpecial(char *start, | |
1448 | ParseSpecial special, SearchPathList *paths) | 1448 | ParseSpecial special, SearchPathList *paths) | |
1449 | { | 1449 | { | |
1450 | 1450 | |||
1451 | while (*start != '\0') { | 1451 | while (*start != '\0') { | |
1452 | char savedEnd; | 1452 | char savedEnd; | |
1453 | char *end = start; | 1453 | char *end = start; | |
1454 | while (*end != '\0' && !ch_isspace(*end)) | 1454 | while (*end != '\0' && !ch_isspace(*end)) | |
1455 | end++; | 1455 | end++; | |
1456 | savedEnd = *end; | 1456 | savedEnd = *end; | |
1457 | *end = '\0'; | 1457 | *end = '\0'; | |
1458 | ParseDependencySourceSpecial(special, start, paths); | 1458 | ParseDependencySourceSpecial(special, start, paths); | |
1459 | *end = savedEnd; | 1459 | *end = savedEnd; | |
1460 | if (savedEnd != '\0') | 1460 | if (savedEnd != '\0') | |
1461 | end++; | 1461 | end++; | |
1462 | pp_skip_whitespace(&end); | 1462 | pp_skip_whitespace(&end); | |
1463 | start = end; | 1463 | start = end; | |
1464 | } | 1464 | } | |
1465 | } | 1465 | } | |
1466 | 1466 | |||
1467 | static void | 1467 | static void | |
1468 | LinkVarToTargets(VarAssign *var) | 1468 | LinkVarToTargets(VarAssign *var) | |
1469 | { | 1469 | { | |
1470 | GNodeListNode *ln; | 1470 | GNodeListNode *ln; | |
1471 | 1471 | |||
1472 | for (ln = targets->first; ln != NULL; ln = ln->next) | 1472 | for (ln = targets->first; ln != NULL; ln = ln->next) | |
1473 | Parse_Var(var, ln->datum); | 1473 | Parse_Var(var, ln->datum); | |
1474 | } | 1474 | } | |
1475 | 1475 | |||
1476 | static bool | 1476 | static bool | |
1477 | ParseDependencySourcesMundane(char *start, | 1477 | ParseDependencySourcesMundane(char *start, | |
1478 | ParseSpecial special, GNodeType targetAttr) | 1478 | ParseSpecial special, GNodeType targetAttr) | |
1479 | { | 1479 | { | |
1480 | while (*start != '\0') { | 1480 | while (*start != '\0') { | |
1481 | char *end = start; | 1481 | char *end = start; | |
1482 | VarAssign var; | 1482 | VarAssign var; | |
1483 | 1483 | |||
1484 | /* | 1484 | /* | |
1485 | * Check for local variable assignment, | 1485 | * Check for local variable assignment, | |
1486 | * rest of the line is the value. | 1486 | * rest of the line is the value. | |
1487 | */ | 1487 | */ | |
1488 | if (Parse_IsVar(start, &var)) { | 1488 | if (Parse_IsVar(start, &var)) { | |
1489 | bool targetVarsEnabled = GetBooleanExpr( | 1489 | bool targetVarsEnabled = GetBooleanExpr( | |
1490 | "${.MAKE.TARGET_LOCAL_VARIABLES}", true); | 1490 | "${.MAKE.TARGET_LOCAL_VARIABLES}", true); | |
1491 | 1491 | |||
1492 | if (targetVarsEnabled) | 1492 | if (targetVarsEnabled) | |
1493 | LinkVarToTargets(&var); | 1493 | LinkVarToTargets(&var); | |
1494 | free(var.varname); | 1494 | free(var.varname); | |
1495 | if (targetVarsEnabled) | 1495 | if (targetVarsEnabled) | |
1496 | return true; | 1496 | return true; | |
1497 | } | 1497 | } | |
1498 | 1498 | |||
1499 | /* | 1499 | /* | |
1500 | * The targets take real sources, so we must beware of archive | 1500 | * The targets take real sources, so we must beware of archive | |
1501 | * specifications (i.e. things with left parentheses in them) | 1501 | * specifications (i.e. things with left parentheses in them) | |
1502 | * and handle them accordingly. | 1502 | * and handle them accordingly. | |
1503 | */ | 1503 | */ | |
1504 | for (; *end != '\0' && !ch_isspace(*end); end++) { | 1504 | for (; *end != '\0' && !ch_isspace(*end); end++) { | |
1505 | if (*end == '(' && end > start && end[-1] != '$') { | 1505 | if (*end == '(' && end > start && end[-1] != '$') { | |
1506 | /* | 1506 | /* | |
1507 | * Only stop for a left parenthesis if it | 1507 | * Only stop for a left parenthesis if it | |
1508 | * isn't at the start of a word (that'll be | 1508 | * isn't at the start of a word (that'll be | |
1509 | * for variable changes later) and isn't | 1509 | * for variable changes later) and isn't | |
1510 | * preceded by a dollar sign (a dynamic | 1510 | * preceded by a dollar sign (a dynamic | |
1511 | * source). | 1511 | * source). | |
1512 | */ | 1512 | */ | |
1513 | break; | 1513 | break; | |
1514 | } | 1514 | } | |
1515 | } | 1515 | } | |
1516 | 1516 | |||
1517 | if (*end == '(') { | 1517 | if (*end == '(') { | |
1518 | GNodeList sources = LST_INIT; | 1518 | GNodeList sources = LST_INIT; | |
1519 | if (!Arch_ParseArchive(&start, &sources, | 1519 | if (!Arch_ParseArchive(&start, &sources, | |
1520 | SCOPE_CMDLINE)) { | 1520 | SCOPE_CMDLINE)) { | |
1521 | Parse_Error(PARSE_FATAL, | 1521 | Parse_Error(PARSE_FATAL, | |
1522 | "Error in source archive spec \"%s\"", | 1522 | "Error in source archive spec \"%s\"", | |
1523 | start); | 1523 | start); | |
1524 | return false; | 1524 | return false; | |
1525 | } | 1525 | } | |
1526 | 1526 | |||
1527 | while (!Lst_IsEmpty(&sources)) { | 1527 | while (!Lst_IsEmpty(&sources)) { | |
1528 | GNode *gn = Lst_Dequeue(&sources); | 1528 | GNode *gn = Lst_Dequeue(&sources); | |
1529 | ApplyDependencySource(targetAttr, gn->name, | 1529 | ApplyDependencySource(targetAttr, gn->name, | |
1530 | special); | 1530 | special); | |
1531 | } | 1531 | } | |
1532 | Lst_Done(&sources); | 1532 | Lst_Done(&sources); | |
1533 | end = start; | 1533 | end = start; | |
1534 | } else { | 1534 | } else { | |
1535 | if (*end != '\0') { | 1535 | if (*end != '\0') { | |
1536 | *end = '\0'; | 1536 | *end = '\0'; | |
1537 | end++; | 1537 | end++; | |
1538 | } | 1538 | } | |
1539 | 1539 | |||
1540 | ApplyDependencySource(targetAttr, start, special); | 1540 | ApplyDependencySource(targetAttr, start, special); | |
1541 | } | 1541 | } | |
1542 | pp_skip_whitespace(&end); | 1542 | pp_skip_whitespace(&end); | |
1543 | start = end; | 1543 | start = end; | |
1544 | } | 1544 | } | |
1545 | return true; | 1545 | return true; | |
1546 | } | 1546 | } | |
1547 | 1547 | |||
1548 | /* | 1548 | /* | |
1549 | * From a dependency line like 'targets: sources', parse the sources. | 1549 | * From a dependency line like 'targets: sources', parse the sources. | |
1550 | * | 1550 | * | |
1551 | * See the tests depsrc-*.mk. | 1551 | * See the tests depsrc-*.mk. | |
1552 | */ | 1552 | */ | |
1553 | static void | 1553 | static void | |
1554 | ParseDependencySources(char *p, GNodeType targetAttr, | 1554 | ParseDependencySources(char *p, GNodeType targetAttr, | |
1555 | ParseSpecial special, SearchPathList **inout_paths) | 1555 | ParseSpecial special, SearchPathList **inout_paths) | |
1556 | { | 1556 | { | |
1557 | if (*p == '\0') { | 1557 | if (*p == '\0') { | |
1558 | HandleDependencySourcesEmpty(special, *inout_paths); | 1558 | HandleDependencySourcesEmpty(special, *inout_paths); | |
1559 | } else if (special == SP_MFLAGS) { | 1559 | } else if (special == SP_MFLAGS) { | |
1560 | Main_ParseArgLine(p); | 1560 | Main_ParseArgLine(p); | |
1561 | return; | 1561 | return; | |
1562 | } else if (special == SP_SHELL) { | 1562 | } else if (special == SP_SHELL) { | |
1563 | if (!Job_ParseShell(p)) { | 1563 | if (!Job_ParseShell(p)) { | |
1564 | Parse_Error(PARSE_FATAL, | 1564 | Parse_Error(PARSE_FATAL, | |
1565 | "improper shell specification"); | 1565 | "improper shell specification"); | |
1566 | return; | 1566 | return; | |
1567 | } | 1567 | } | |
1568 | return; | 1568 | return; | |
1569 | } else if (special == SP_NOTPARALLEL || special == SP_SINGLESHELL || | 1569 | } else if (special == SP_NOTPARALLEL || special == SP_SINGLESHELL || | |
1570 | special == SP_DELETE_ON_ERROR) { | 1570 | special == SP_DELETE_ON_ERROR) { | |
1571 | return; | 1571 | return; | |
1572 | } | 1572 | } | |
1573 | 1573 | |||
1574 | switch (special) { | 1574 | switch (special) { | |
1575 | case SP_INCLUDES: | 1575 | case SP_INCLUDES: | |
1576 | case SP_LIBS: | 1576 | case SP_LIBS: | |
1577 | case SP_NOREADONLY: | 1577 | case SP_NOREADONLY: | |
1578 | case SP_NULL: | 1578 | case SP_NULL: | |
1579 | case SP_OBJDIR: | 1579 | case SP_OBJDIR: | |
1580 | case SP_PATH: | 1580 | case SP_PATH: | |
1581 | case SP_READONLY: | 1581 | case SP_READONLY: | |
1582 | case SP_SUFFIXES: | 1582 | case SP_SUFFIXES: | |
1583 | case SP_SYSPATH: | 1583 | case SP_SYSPATH: | |
1584 | ParseDependencySourcesSpecial(p, special, *inout_paths); | 1584 | ParseDependencySourcesSpecial(p, special, *inout_paths); | |
1585 | if (*inout_paths != NULL) { | 1585 | if (*inout_paths != NULL) { | |
1586 | Lst_Free(*inout_paths); | 1586 | Lst_Free(*inout_paths); | |
1587 | *inout_paths = NULL; | 1587 | *inout_paths = NULL; | |
1588 | } | 1588 | } | |
1589 | if (special == SP_PATH) | 1589 | if (special == SP_PATH) | |
1590 | Dir_SetPATH(); | 1590 | Dir_SetPATH(); | |
1591 | if (special == SP_SYSPATH) | 1591 | if (special == SP_SYSPATH) | |
1592 | Dir_SetSYSPATH(); | 1592 | Dir_SetSYSPATH(); | |
1593 | break; | 1593 | break; | |
1594 | default: | 1594 | default: | |
1595 | assert(*inout_paths == NULL); | 1595 | assert(*inout_paths == NULL); | |
1596 | if (!ParseDependencySourcesMundane(p, special, targetAttr)) | 1596 | if (!ParseDependencySourcesMundane(p, special, targetAttr)) | |
1597 | return; | 1597 | return; | |
1598 | break; | 1598 | break; | |
1599 | } | 1599 | } | |
1600 | 1600 | |||
1601 | MaybeUpdateMainTarget(); | 1601 | MaybeUpdateMainTarget(); | |
1602 | } | 1602 | } | |
1603 | 1603 | |||
1604 | /* | 1604 | /* | |
1605 | * Parse a dependency line consisting of targets, followed by a dependency | 1605 | * Parse a dependency line consisting of targets, followed by a dependency | |
1606 | * operator, optionally followed by sources. | 1606 | * operator, optionally followed by sources. | |
1607 | * | 1607 | * | |
1608 | * The nodes of the sources are linked as children to the nodes of the | 1608 | * The nodes of the sources are linked as children to the nodes of the | |
1609 | * targets. Nodes are created as necessary. | 1609 | * targets. Nodes are created as necessary. | |
1610 | * | 1610 | * | |
1611 | * The operator is applied to each node in the global 'targets' list, | 1611 | * The operator is applied to each node in the global 'targets' list, | |
1612 | * which is where the nodes found for the targets are kept. | 1612 | * which is where the nodes found for the targets are kept. | |
1613 | * | 1613 | * | |
1614 | * The sources are parsed in much the same way as the targets, except | 1614 | * The sources are parsed in much the same way as the targets, except | |
1615 | * that they are expanded using the wildcarding scheme of the C-Shell, | 1615 | * that they are expanded using the wildcarding scheme of the C-Shell, | |
1616 | * and a target is created for each expanded word. Each of the resulting | 1616 | * and a target is created for each expanded word. Each of the resulting | |
1617 | * nodes is then linked to each of the targets as one of its children. | 1617 | * nodes is then linked to each of the targets as one of its children. | |
1618 | * | 1618 | * | |
1619 | * Certain targets and sources such as .PHONY or .PRECIOUS are handled | 1619 | * Certain targets and sources such as .PHONY or .PRECIOUS are handled | |
1620 | * specially, see ParseSpecial. | 1620 | * specially, see ParseSpecial. | |
1621 | * | 1621 | * | |
1622 | * Transformation rules such as '.c.o' are also handled here, see | 1622 | * Transformation rules such as '.c.o' are also handled here, see | |
1623 | * Suff_AddTransform. | 1623 | * Suff_AddTransform. | |
1624 | * | 1624 | * | |
1625 | * Upon return, the value of the line is unspecified. | 1625 | * Upon return, the value of expandedLine is unspecified. | |
1626 | */ | 1626 | */ | |
1627 | static void | 1627 | static void | |
1628 | ParseDependency(char *line, const char *unexpanded_line) | 1628 | ParseDependency(char *expandedLine, const char *unexpandedLine) | |
1629 | { | 1629 | { | |
1630 | char *p; | 1630 | char *p; | |
1631 | SearchPathList *paths; /* search paths to alter when parsing a list | 1631 | SearchPathList *paths; /* search paths to alter when parsing a list | |
1632 | * of .PATH targets */ | 1632 | * of .PATH targets */ | |
1633 | GNodeType targetAttr; /* from special sources */ | 1633 | GNodeType targetAttr; /* from special sources */ | |
1634 | ParseSpecial special; /* in special targets, the children are | 1634 | ParseSpecial special; /* in special targets, the children are | |
1635 | * linked as children of the parent but not | 1635 | * linked as children of the parent but not | |
1636 | * vice versa */ | 1636 | * vice versa */ | |
1637 | GNodeType op; | 1637 | GNodeType op; | |
1638 | 1638 | |||
1639 | DEBUG1(PARSE, "ParseDependency(%s)\n", line); | 1639 | DEBUG1(PARSE, "ParseDependency(%s)\n", expandedLine); | |
1640 | p = line; | 1640 | p = expandedLine; | |
1641 | paths = NULL; | 1641 | paths = NULL; | |
1642 | targetAttr = OP_NONE; | 1642 | targetAttr = OP_NONE; | |
1643 | special = SP_NOT; | 1643 | special = SP_NOT; | |
1644 | 1644 | |||
1645 | if (!ParseDependencyTargets(&p, line, &special, &targetAttr, &paths, | 1645 | if (!ParseDependencyTargets(&p, expandedLine, &special, &targetAttr, | |
1646 | unexpanded_line)) | 1646 | &paths, unexpandedLine)) | |
1647 | goto out; | 1647 | goto out; | |
1648 | 1648 | |||
1649 | if (!Lst_IsEmpty(targets)) | 1649 | if (!Lst_IsEmpty(targets)) | |
1650 | CheckSpecialMundaneMixture(special); | 1650 | CheckSpecialMundaneMixture(special); | |
1651 | 1651 | |||
1652 | op = ParseDependencyOp(&p); | 1652 | op = ParseDependencyOp(&p); | |
1653 | if (op == OP_NONE) { | 1653 | if (op == OP_NONE) { | |
1654 | InvalidLineType(line, unexpanded_line); | 1654 | InvalidLineType(expandedLine, unexpandedLine); | |
1655 | goto out; | 1655 | goto out; | |
1656 | } | 1656 | } | |
1657 | ApplyDependencyOperator(op); | 1657 | ApplyDependencyOperator(op); | |
1658 | 1658 | |||
1659 | pp_skip_whitespace(&p); | 1659 | pp_skip_whitespace(&p); | |
1660 | 1660 | |||
1661 | ParseDependencySources(p, targetAttr, special, &paths); | 1661 | ParseDependencySources(p, targetAttr, special, &paths); | |
1662 | 1662 | |||
1663 | out: | 1663 | out: | |
1664 | if (paths != NULL) | 1664 | if (paths != NULL) | |
1665 | Lst_Free(paths); | 1665 | Lst_Free(paths); | |
1666 | } | 1666 | } | |
1667 | 1667 | |||
1668 | /* | 1668 | /* | |
1669 | * Determine the assignment operator and adjust the end of the variable | 1669 | * Determine the assignment operator and adjust the end of the variable | |
1670 | * name accordingly. | 1670 | * name accordingly. | |
1671 | */ | 1671 | */ | |
1672 | static VarAssign | 1672 | static VarAssign | |
1673 | AdjustVarassignOp(const char *name, const char *nameEnd, const char *op, | 1673 | AdjustVarassignOp(const char *name, const char *nameEnd, const char *op, | |
1674 | const char *value) | 1674 | const char *value) | |
1675 | { | 1675 | { | |
1676 | VarAssignOp type; | 1676 | VarAssignOp type; | |
1677 | VarAssign va; | 1677 | VarAssign va; | |
1678 | 1678 | |||
1679 | if (op > name && op[-1] == '+') { | 1679 | if (op > name && op[-1] == '+') { | |
1680 | op--; | 1680 | op--; | |
1681 | type = VAR_APPEND; | 1681 | type = VAR_APPEND; | |
1682 | 1682 | |||
1683 | } else if (op > name && op[-1] == '?') { | 1683 | } else if (op > name && op[-1] == '?') { | |
1684 | op--; | 1684 | op--; | |
1685 | type = VAR_DEFAULT; | 1685 | type = VAR_DEFAULT; | |
1686 | 1686 | |||
1687 | } else if (op > name && op[-1] == ':') { | 1687 | } else if (op > name && op[-1] == ':') { | |
1688 | op--; | 1688 | op--; | |
1689 | type = VAR_SUBST; | 1689 | type = VAR_SUBST; | |
1690 | 1690 | |||
1691 | } else if (op > name && op[-1] == '!') { | 1691 | } else if (op > name && op[-1] == '!') { | |
1692 | op--; | 1692 | op--; | |
1693 | type = VAR_SHELL; | 1693 | type = VAR_SHELL; | |
1694 | 1694 | |||
1695 | } else { | 1695 | } else { | |
1696 | type = VAR_NORMAL; | 1696 | type = VAR_NORMAL; | |
1697 | while (op > name && ch_isspace(op[-1])) | 1697 | while (op > name && ch_isspace(op[-1])) | |
1698 | op--; | 1698 | op--; | |
1699 | 1699 | |||
1700 | if (op - name >= 3 && memcmp(op - 3, ":sh", 3) == 0) { | 1700 | if (op - name >= 3 && memcmp(op - 3, ":sh", 3) == 0) { | |
1701 | op -= 3; | 1701 | op -= 3; | |
1702 | type = VAR_SHELL; | 1702 | type = VAR_SHELL; | |
1703 | } | 1703 | } | |
1704 | } | 1704 | } | |
1705 | 1705 | |||
1706 | va.varname = bmake_strsedup(name, nameEnd < op ? nameEnd : op); | 1706 | va.varname = bmake_strsedup(name, nameEnd < op ? nameEnd : op); | |
1707 | va.op = type; | 1707 | va.op = type; | |
1708 | va.value = value; | 1708 | va.value = value; | |
1709 | return va; | 1709 | return va; | |
1710 | } | 1710 | } | |
1711 | 1711 | |||
1712 | /* | 1712 | /* | |
1713 | * Parse a variable assignment, consisting of a single-word variable name, | 1713 | * Parse a variable assignment, consisting of a single-word variable name, | |
1714 | * optional whitespace, an assignment operator, optional whitespace and the | 1714 | * optional whitespace, an assignment operator, optional whitespace and the | |
1715 | * variable value. | 1715 | * variable value. | |
1716 | * | 1716 | * | |
1717 | * Note: There is a lexical ambiguity with assignment modifier characters | 1717 | * Note: There is a lexical ambiguity with assignment modifier characters | |
1718 | * in variable names. This routine interprets the character before the = | 1718 | * in variable names. This routine interprets the character before the = | |
1719 | * as a modifier. Therefore, an assignment like | 1719 | * as a modifier. Therefore, an assignment like | |
1720 | * C++=/usr/bin/CC | 1720 | * C++=/usr/bin/CC | |
1721 | * is interpreted as "C+ +=" instead of "C++ =". | 1721 | * is interpreted as "C+ +=" instead of "C++ =". | |
1722 | * | 1722 | * | |
1723 | * Used for both lines in a file and command line arguments. | 1723 | * Used for both lines in a file and command line arguments. | |
1724 | */ | 1724 | */ | |
1725 | static bool | 1725 | static bool | |
1726 | Parse_IsVar(const char *p, VarAssign *out_var) | 1726 | Parse_IsVar(const char *p, VarAssign *out_var) | |
1727 | { | 1727 | { | |
1728 | const char *nameStart, *nameEnd, *firstSpace, *eq; | 1728 | const char *nameStart, *nameEnd, *firstSpace, *eq; | |
1729 | int level = 0; | 1729 | int level = 0; | |
1730 | 1730 | |||
1731 | cpp_skip_hspace(&p); /* Skip to variable name */ | 1731 | cpp_skip_hspace(&p); /* Skip to variable name */ | |
1732 | 1732 | |||
1733 | /* | 1733 | /* | |
1734 | * During parsing, the '+' of the operator '+=' is initially parsed | 1734 | * During parsing, the '+' of the operator '+=' is initially parsed | |
1735 | * as part of the variable name. It is later corrected, as is the | 1735 | * as part of the variable name. It is later corrected, as is the | |
1736 | * ':sh' modifier. Of these two (nameEnd and eq), the earlier one | 1736 | * ':sh' modifier. Of these two (nameEnd and eq), the earlier one | |
1737 | * determines the actual end of the variable name. | 1737 | * determines the actual end of the variable name. | |
1738 | */ | 1738 | */ | |
1739 | 1739 | |||
1740 | nameStart = p; | 1740 | nameStart = p; | |
1741 | firstSpace = NULL; | 1741 | firstSpace = NULL; | |
1742 | 1742 | |||
1743 | /* Scan for one of the assignment operators outside an expression. */ | 1743 | /* Scan for one of the assignment operators outside an expression. */ | |
1744 | while (*p != '\0') { | 1744 | while (*p != '\0') { | |
1745 | char ch = *p++; | 1745 | char ch = *p++; | |
1746 | if (ch == '(' || ch == '{') { | 1746 | if (ch == '(' || ch == '{') { | |
1747 | level++; | 1747 | level++; | |
1748 | continue; | 1748 | continue; | |
1749 | } | 1749 | } | |
1750 | if (ch == ')' || ch == '}') { | 1750 | if (ch == ')' || ch == '}') { | |
1751 | level--; | 1751 | level--; | |
1752 | continue; | 1752 | continue; | |
1753 | } | 1753 | } | |
1754 | 1754 | |||
1755 | if (level != 0) | 1755 | if (level != 0) | |
1756 | continue; | 1756 | continue; | |
1757 | 1757 | |||
1758 | if ((ch == ' ' || ch == '\t') && firstSpace == NULL) | 1758 | if ((ch == ' ' || ch == '\t') && firstSpace == NULL) | |
1759 | firstSpace = p - 1; | 1759 | firstSpace = p - 1; | |
1760 | while (ch == ' ' || ch == '\t') | 1760 | while (ch == ' ' || ch == '\t') | |
1761 | ch = *p++; | 1761 | ch = *p++; | |
1762 | 1762 | |||
1763 | if (ch == '\0') | 1763 | if (ch == '\0') | |
1764 | return false; | 1764 | return false; | |
1765 | if (ch == ':' && p[0] == 's' && p[1] == 'h') { | 1765 | if (ch == ':' && p[0] == 's' && p[1] == 'h') { | |
1766 | p += 2; | 1766 | p += 2; | |
1767 | continue; | 1767 | continue; | |
1768 | } | 1768 | } | |
1769 | if (ch == '=') | 1769 | if (ch == '=') | |
1770 | eq = p - 1; | 1770 | eq = p - 1; | |
1771 | else if (*p == '=' && | 1771 | else if (*p == '=' && | |
1772 | (ch == '+' || ch == ':' || ch == '?' || ch == '!')) | 1772 | (ch == '+' || ch == ':' || ch == '?' || ch == '!')) | |
1773 | eq = p; | 1773 | eq = p; | |
1774 | else if (firstSpace != NULL) | 1774 | else if (firstSpace != NULL) | |
1775 | return false; | 1775 | return false; | |
1776 | else | 1776 | else | |
1777 | continue; | 1777 | continue; | |
1778 | 1778 | |||
1779 | nameEnd = firstSpace != NULL ? firstSpace : eq; | 1779 | nameEnd = firstSpace != NULL ? firstSpace : eq; | |
1780 | p = eq + 1; | 1780 | p = eq + 1; | |
1781 | cpp_skip_whitespace(&p); | 1781 | cpp_skip_whitespace(&p); | |
1782 | *out_var = AdjustVarassignOp(nameStart, nameEnd, eq, p); | 1782 | *out_var = AdjustVarassignOp(nameStart, nameEnd, eq, p); | |
1783 | return true; | 1783 | return true; | |
1784 | } | 1784 | } | |
1785 | 1785 | |||
1786 | return false; | 1786 | return false; | |
1787 | } | 1787 | } | |
1788 | 1788 | |||
1789 | /* | 1789 | /* | |
1790 | * Check for syntax errors such as unclosed expressions or unknown modifiers. | 1790 | * Check for syntax errors such as unclosed expressions or unknown modifiers. | |
1791 | */ | 1791 | */ | |
1792 | static void | 1792 | static void | |
1793 | VarCheckSyntax(VarAssignOp op, const char *uvalue, GNode *scope) | 1793 | VarCheckSyntax(VarAssignOp op, const char *uvalue, GNode *scope) | |
1794 | { | 1794 | { | |
1795 | if (opts.strict) { | 1795 | if (opts.strict) { | |
1796 | if (op != VAR_SUBST && strchr(uvalue, '$') != NULL) { | 1796 | if (op != VAR_SUBST && strchr(uvalue, '$') != NULL) { | |
1797 | char *parsedValue = Var_Subst(uvalue, | 1797 | char *parsedValue = Var_Subst(uvalue, | |
1798 | scope, VARE_PARSE_ONLY); | 1798 | scope, VARE_PARSE_ONLY); | |
1799 | /* TODO: handle errors */ | 1799 | /* TODO: handle errors */ | |
1800 | free(parsedValue); | 1800 | free(parsedValue); | |
1801 | } | 1801 | } | |
1802 | } | 1802 | } | |
1803 | } | 1803 | } | |
1804 | 1804 | |||
1805 | /* Perform a variable assignment that uses the operator ':='. */ | 1805 | /* Perform a variable assignment that uses the operator ':='. */ | |
1806 | static void | 1806 | static void | |
1807 | VarAssign_EvalSubst(GNode *scope, const char *name, const char *uvalue, | 1807 | VarAssign_EvalSubst(GNode *scope, const char *name, const char *uvalue, | |
1808 | FStr *out_avalue) | 1808 | FStr *out_avalue) | |
1809 | { | 1809 | { | |
1810 | char *evalue; | 1810 | char *evalue; | |
1811 | 1811 | |||
1812 | /* | 1812 | /* | |
1813 | * Make sure that we set the variable the first time to nothing | 1813 | * Make sure that we set the variable the first time to nothing | |
1814 | * so that it gets substituted. | 1814 | * so that it gets substituted. | |
1815 | * | 1815 | * | |
1816 | * TODO: Add a test that demonstrates why this code is needed, | 1816 | * TODO: Add a test that demonstrates why this code is needed, | |
1817 | * apart from making the debug log longer. | 1817 | * apart from making the debug log longer. | |
1818 | * | 1818 | * | |
1819 | * XXX: The variable name is expanded up to 3 times. | 1819 | * XXX: The variable name is expanded up to 3 times. | |
1820 | */ | 1820 | */ | |
1821 | if (!Var_ExistsExpand(scope, name)) | 1821 | if (!Var_ExistsExpand(scope, name)) | |
1822 | Var_SetExpand(scope, name, ""); | 1822 | Var_SetExpand(scope, name, ""); | |
1823 | 1823 | |||
1824 | evalue = Var_Subst(uvalue, scope, VARE_KEEP_DOLLAR_UNDEF); | 1824 | evalue = Var_Subst(uvalue, scope, VARE_KEEP_DOLLAR_UNDEF); | |
1825 | /* TODO: handle errors */ | 1825 | /* TODO: handle errors */ | |
1826 | 1826 | |||
1827 | Var_SetExpand(scope, name, evalue); | 1827 | Var_SetExpand(scope, name, evalue); | |
1828 | 1828 | |||
1829 | *out_avalue = FStr_InitOwn(evalue); | 1829 | *out_avalue = FStr_InitOwn(evalue); | |
1830 | } | 1830 | } | |
1831 | 1831 | |||
1832 | /* Perform a variable assignment that uses the operator '!='. */ | 1832 | /* Perform a variable assignment that uses the operator '!='. */ | |
1833 | static void | 1833 | static void | |
1834 | VarAssign_EvalShell(const char *name, const char *uvalue, GNode *scope, | 1834 | VarAssign_EvalShell(const char *name, const char *uvalue, GNode *scope, | |
1835 | FStr *out_avalue) | 1835 | FStr *out_avalue) | |
1836 | { | 1836 | { | |
1837 | FStr cmd; | 1837 | FStr cmd; | |
1838 | char *output, *error; | 1838 | char *output, *error; | |
1839 | 1839 | |||
1840 | cmd = FStr_InitRefer(uvalue); | 1840 | cmd = FStr_InitRefer(uvalue); | |
1841 | Var_Expand(&cmd, SCOPE_CMDLINE, VARE_UNDEFERR); | 1841 | Var_Expand(&cmd, SCOPE_CMDLINE, VARE_UNDEFERR); | |
1842 | 1842 | |||
1843 | output = Cmd_Exec(cmd.str, &error); | 1843 | output = Cmd_Exec(cmd.str, &error); | |
1844 | Var_SetExpand(scope, name, output); | 1844 | Var_SetExpand(scope, name, output); | |
1845 | *out_avalue = FStr_InitOwn(output); | 1845 | *out_avalue = FStr_InitOwn(output); | |
1846 | if (error != NULL) { | 1846 | if (error != NULL) { | |
1847 | Parse_Error(PARSE_WARNING, "%s", error); | 1847 | Parse_Error(PARSE_WARNING, "%s", error); | |
1848 | free(error); | 1848 | free(error); | |
1849 | } | 1849 | } | |
1850 | 1850 | |||
1851 | FStr_Done(&cmd); | 1851 | FStr_Done(&cmd); | |
1852 | } | 1852 | } | |
1853 | 1853 | |||
1854 | /* | 1854 | /* | |
1855 | * Perform a variable assignment. | 1855 | * Perform a variable assignment. | |
1856 | * | 1856 | * | |
1857 | * The actual value of the variable is returned in *out_true_avalue. | 1857 | * The actual value of the variable is returned in *out_true_avalue. | |
1858 | * Especially for VAR_SUBST and VAR_SHELL this can differ from the literal | 1858 | * Especially for VAR_SUBST and VAR_SHELL this can differ from the literal | |
1859 | * value. | 1859 | * value. | |
1860 | * | 1860 | * | |
1861 | * Return whether the assignment was actually performed, which is usually | 1861 | * Return whether the assignment was actually performed, which is usually | |
1862 | * the case. It is only skipped if the operator is '?=' and the variable | 1862 | * the case. It is only skipped if the operator is '?=' and the variable | |
1863 | * already exists. | 1863 | * already exists. | |
1864 | */ | 1864 | */ | |
1865 | static bool | 1865 | static bool | |
1866 | VarAssign_Eval(const char *name, VarAssignOp op, const char *uvalue, | 1866 | VarAssign_Eval(const char *name, VarAssignOp op, const char *uvalue, | |
1867 | GNode *scope, FStr *out_true_avalue) | 1867 | GNode *scope, FStr *out_true_avalue) | |
1868 | { | 1868 | { | |
1869 | FStr avalue = FStr_InitRefer(uvalue); | 1869 | FStr avalue = FStr_InitRefer(uvalue); | |
1870 | 1870 | |||
1871 | if (op == VAR_APPEND) | 1871 | if (op == VAR_APPEND) | |
1872 | Var_AppendExpand(scope, name, uvalue); | 1872 | Var_AppendExpand(scope, name, uvalue); | |
1873 | else if (op == VAR_SUBST) | 1873 | else if (op == VAR_SUBST) | |
1874 | VarAssign_EvalSubst(scope, name, uvalue, &avalue); | 1874 | VarAssign_EvalSubst(scope, name, uvalue, &avalue); | |
1875 | else if (op == VAR_SHELL) | 1875 | else if (op == VAR_SHELL) | |
1876 | VarAssign_EvalShell(name, uvalue, scope, &avalue); | 1876 | VarAssign_EvalShell(name, uvalue, scope, &avalue); | |
1877 | else { | 1877 | else { | |
1878 | /* XXX: The variable name is expanded up to 2 times. */ | 1878 | /* XXX: The variable name is expanded up to 2 times. */ | |
1879 | if (op == VAR_DEFAULT && Var_ExistsExpand(scope, name)) | 1879 | if (op == VAR_DEFAULT && Var_ExistsExpand(scope, name)) | |
1880 | return false; | 1880 | return false; | |
1881 | 1881 | |||
1882 | /* Normal assignment -- just do it. */ | 1882 | /* Normal assignment -- just do it. */ | |
1883 | Var_SetExpand(scope, name, uvalue); | 1883 | Var_SetExpand(scope, name, uvalue); | |
1884 | } | 1884 | } | |
1885 | 1885 | |||
1886 | *out_true_avalue = avalue; | 1886 | *out_true_avalue = avalue; | |
1887 | return true; | 1887 | return true; | |
1888 | } | 1888 | } | |
1889 | 1889 | |||
1890 | static void | 1890 | static void | |
1891 | VarAssignSpecial(const char *name, const char *avalue) | 1891 | VarAssignSpecial(const char *name, const char *avalue) | |
1892 | { | 1892 | { | |
1893 | if (strcmp(name, ".MAKEOVERRIDES") == 0) | 1893 | if (strcmp(name, ".MAKEOVERRIDES") == 0) | |
1894 | Main_ExportMAKEFLAGS(false); /* re-export MAKEFLAGS */ | 1894 | Main_ExportMAKEFLAGS(false); /* re-export MAKEFLAGS */ | |
1895 | else if (strcmp(name, ".CURDIR") == 0) { | 1895 | else if (strcmp(name, ".CURDIR") == 0) { | |
1896 | /* | 1896 | /* | |
1897 | * Someone is being (too?) clever... | 1897 | * Someone is being (too?) clever... | |
1898 | * Let's pretend they know what they are doing and | 1898 | * Let's pretend they know what they are doing and | |
1899 | * re-initialize the 'cur' CachedDir. | 1899 | * re-initialize the 'cur' CachedDir. | |
1900 | */ | 1900 | */ | |
1901 | Dir_InitCur(avalue); | 1901 | Dir_InitCur(avalue); | |
1902 | Dir_SetPATH(); | 1902 | Dir_SetPATH(); | |
1903 | } else if (strcmp(name, ".MAKE.JOB.PREFIX") == 0) | 1903 | } else if (strcmp(name, ".MAKE.JOB.PREFIX") == 0) | |
1904 | Job_SetPrefix(); | 1904 | Job_SetPrefix(); | |
1905 | else if (strcmp(name, ".MAKE.EXPORTED") == 0) | 1905 | else if (strcmp(name, ".MAKE.EXPORTED") == 0) | |
1906 | Var_ExportVars(avalue); | 1906 | Var_ExportVars(avalue); | |
1907 | } | 1907 | } | |
1908 | 1908 | |||
1909 | /* Perform the variable assignment in the given scope. */ | 1909 | /* Perform the variable assignment in the given scope. */ | |
1910 | static void | 1910 | static void | |
1911 | Parse_Var(VarAssign *var, GNode *scope) | 1911 | Parse_Var(VarAssign *var, GNode *scope) | |
1912 | { | 1912 | { | |
1913 | FStr avalue; /* actual value (maybe expanded) */ | 1913 | FStr avalue; /* actual value (maybe expanded) */ | |
1914 | 1914 | |||
1915 | VarCheckSyntax(var->op, var->value, scope); | 1915 | VarCheckSyntax(var->op, var->value, scope); | |
1916 | if (VarAssign_Eval(var->varname, var->op, var->value, scope, &avalue)) { | 1916 | if (VarAssign_Eval(var->varname, var->op, var->value, scope, &avalue)) { | |
1917 | VarAssignSpecial(var->varname, avalue.str); | 1917 | VarAssignSpecial(var->varname, avalue.str); | |
1918 | FStr_Done(&avalue); | 1918 | FStr_Done(&avalue); | |
1919 | } | 1919 | } | |
1920 | } | 1920 | } | |
1921 | 1921 | |||
1922 | 1922 | |||
1923 | /* | 1923 | /* | |
1924 | * See if the command possibly calls a sub-make by using the | 1924 | * See if the command possibly calls a sub-make by using the | |
1925 | * expressions ${.MAKE}, ${MAKE} or the plain word "make". | 1925 | * expressions ${.MAKE}, ${MAKE} or the plain word "make". | |
1926 | */ | 1926 | */ | |
1927 | static bool | 1927 | static bool | |
1928 | MaybeSubMake(const char *cmd) | 1928 | MaybeSubMake(const char *cmd) | |
1929 | { | 1929 | { | |
1930 | const char *start; | 1930 | const char *start; | |
1931 | 1931 | |||
1932 | for (start = cmd; *start != '\0'; start++) { | 1932 | for (start = cmd; *start != '\0'; start++) { | |
1933 | const char *p = start; | 1933 | const char *p = start; | |
1934 | char endc; | 1934 | char endc; | |
1935 | 1935 | |||
1936 | /* XXX: What if progname != "make"? */ | 1936 | /* XXX: What if progname != "make"? */ | |
1937 | if (strncmp(p, "make", 4) == 0) | 1937 | if (strncmp(p, "make", 4) == 0) | |
1938 | if (start == cmd || !ch_isalnum(p[-1])) | 1938 | if (start == cmd || !ch_isalnum(p[-1])) | |
1939 | if (!ch_isalnum(p[4])) | 1939 | if (!ch_isalnum(p[4])) | |
1940 | return true; | 1940 | return true; | |
1941 | 1941 | |||
1942 | if (*p != '$') | 1942 | if (*p != '$') | |
1943 | continue; | 1943 | continue; | |
1944 | p++; | 1944 | p++; | |
1945 | 1945 | |||
1946 | if (*p == '{') | 1946 | if (*p == '{') | |
1947 | endc = '}'; | 1947 | endc = '}'; | |
1948 | else if (*p == '(') | 1948 | else if (*p == '(') | |
1949 | endc = ')'; | 1949 | endc = ')'; | |
1950 | else | 1950 | else | |
1951 | continue; | 1951 | continue; | |
1952 | p++; | 1952 | p++; | |
1953 | 1953 | |||
1954 | if (*p == '.') /* Accept either ${.MAKE} or ${MAKE}. */ | 1954 | if (*p == '.') /* Accept either ${.MAKE} or ${MAKE}. */ | |
1955 | p++; | 1955 | p++; | |
1956 | 1956 | |||
1957 | if (strncmp(p, "MAKE", 4) == 0 && p[4] == endc) | 1957 | if (strncmp(p, "MAKE", 4) == 0 && p[4] == endc) | |
1958 | return true; | 1958 | return true; | |
1959 | } | 1959 | } | |
1960 | return false; | 1960 | return false; | |
1961 | } | 1961 | } | |
1962 | 1962 | |||
1963 | /* Append the command to the target node. */ | 1963 | /* Append the command to the target node. */ | |
1964 | static void | 1964 | static void | |
1965 | GNode_AddCommand(GNode *gn, char *cmd) | 1965 | GNode_AddCommand(GNode *gn, char *cmd) | |
1966 | { | 1966 | { | |
1967 | if ((gn->type & OP_DOUBLEDEP) && gn->cohorts.last != NULL) | 1967 | if ((gn->type & OP_DOUBLEDEP) && gn->cohorts.last != NULL) | |
1968 | gn = gn->cohorts.last->datum; | 1968 | gn = gn->cohorts.last->datum; | |
1969 | 1969 | |||
1970 | /* if target already supplied, ignore commands */ | 1970 | /* if target already supplied, ignore commands */ | |
1971 | if (!(gn->type & OP_HAS_COMMANDS)) { | 1971 | if (!(gn->type & OP_HAS_COMMANDS)) { | |
1972 | Lst_Append(&gn->commands, cmd); | 1972 | Lst_Append(&gn->commands, cmd); | |
1973 | if (MaybeSubMake(cmd)) | 1973 | if (MaybeSubMake(cmd)) | |
1974 | gn->type |= OP_SUBMAKE; | 1974 | gn->type |= OP_SUBMAKE; | |
1975 | RememberLocation(gn); | 1975 | RememberLocation(gn); | |
1976 | } else { | 1976 | } else { | |
1977 | Parse_Error(PARSE_WARNING, | 1977 | Parse_Error(PARSE_WARNING, | |
1978 | "duplicate script for target \"%s\" ignored", | 1978 | "duplicate script for target \"%s\" ignored", | |
1979 | gn->name); | 1979 | gn->name); | |
1980 | ParseErrorInternal(gn, PARSE_WARNING, | 1980 | ParseErrorInternal(gn, PARSE_WARNING, | |
1981 | "using previous script for \"%s\" defined here", | 1981 | "using previous script for \"%s\" defined here", | |
1982 | gn->name); | 1982 | gn->name); | |
1983 | } | 1983 | } | |
1984 | } | 1984 | } | |
1985 | 1985 | |||
1986 | /* | 1986 | /* | |
1987 | * Parse a directive like '.include' or '.-include'. | 1987 | * Parse a directive like '.include' or '.-include'. | |
1988 | * | 1988 | * | |
1989 | * .include "user-makefile.mk" | 1989 | * .include "user-makefile.mk" | |
1990 | * .include <system-makefile.mk> | 1990 | * .include <system-makefile.mk> | |
1991 | */ | 1991 | */ | |
1992 | static void | 1992 | static void | |
1993 | ParseInclude(char *directive) | 1993 | ParseInclude(char *directive) | |
1994 | { | 1994 | { | |
1995 | char endc; /* '>' or '"' */ | 1995 | char endc; /* '>' or '"' */ | |
1996 | char *p; | 1996 | char *p; | |
1997 | bool silent = directive[0] != 'i'; | 1997 | bool silent = directive[0] != 'i'; | |
1998 | FStr file; | 1998 | FStr file; | |
1999 | 1999 | |||
2000 | p = directive + (silent ? 8 : 7); | 2000 | p = directive + (silent ? 8 : 7); | |
2001 | pp_skip_hspace(&p); | 2001 | pp_skip_hspace(&p); | |
2002 | 2002 | |||
2003 | if (*p != '"' && *p != '<') { | 2003 | if (*p != '"' && *p != '<') { | |
2004 | Parse_Error(PARSE_FATAL, | 2004 | Parse_Error(PARSE_FATAL, | |
2005 | ".include filename must be delimited by '\"' or '<'"); | 2005 | ".include filename must be delimited by '\"' or '<'"); | |
2006 | return; | 2006 | return; | |
2007 | } | 2007 | } | |
2008 | 2008 | |||
2009 | endc = *p++ == '<' ? '>' : '"'; | 2009 | endc = *p++ == '<' ? '>' : '"'; | |
2010 | file = FStr_InitRefer(p); | 2010 | file = FStr_InitRefer(p); | |
2011 | 2011 | |||
2012 | while (*p != '\0' && *p != endc) | 2012 | while (*p != '\0' && *p != endc) | |
2013 | p++; | 2013 | p++; | |
2014 | 2014 | |||
2015 | if (*p != endc) { | 2015 | if (*p != endc) { | |
2016 | Parse_Error(PARSE_FATAL, | 2016 | Parse_Error(PARSE_FATAL, | |
2017 | "Unclosed .include filename. '%c' expected", endc); | 2017 | "Unclosed .include filename. '%c' expected", endc); | |
2018 | return; | 2018 | return; | |
2019 | } | 2019 | } | |
2020 | 2020 | |||
2021 | *p = '\0'; | 2021 | *p = '\0'; | |
2022 | 2022 | |||
2023 | Var_Expand(&file, SCOPE_CMDLINE, VARE_WANTRES); | 2023 | Var_Expand(&file, SCOPE_CMDLINE, VARE_WANTRES); | |
2024 | IncludeFile(file.str, endc == '>', directive[0] == 'd', silent); | 2024 | IncludeFile(file.str, endc == '>', directive[0] == 'd', silent); | |
2025 | FStr_Done(&file); | 2025 | FStr_Done(&file); | |
2026 | } | 2026 | } | |
2027 | 2027 | |||
2028 | /* | 2028 | /* | |
2029 | * Split filename into dirname + basename, then assign these to the | 2029 | * Split filename into dirname + basename, then assign these to the | |
2030 | * given variables. | 2030 | * given variables. | |
2031 | */ | 2031 | */ | |
2032 | static void | 2032 | static void | |
2033 | SetFilenameVars(const char *filename, const char *dirvar, const char *filevar) | 2033 | SetFilenameVars(const char *filename, const char *dirvar, const char *filevar) | |
2034 | { | 2034 | { | |
2035 | const char *slash, *basename; | 2035 | const char *slash, *basename; | |
2036 | FStr dirname; | 2036 | FStr dirname; | |
2037 | 2037 | |||
2038 | slash = strrchr(filename, '/'); | 2038 | slash = strrchr(filename, '/'); | |
2039 | if (slash == NULL) { | 2039 | if (slash == NULL) { | |
2040 | dirname = FStr_InitRefer(curdir); | 2040 | dirname = FStr_InitRefer(curdir); | |
2041 | basename = filename; | 2041 | basename = filename; | |
2042 | } else { | 2042 | } else { | |
2043 | dirname = FStr_InitOwn(bmake_strsedup(filename, slash)); | 2043 | dirname = FStr_InitOwn(bmake_strsedup(filename, slash)); | |
2044 | basename = slash + 1; | 2044 | basename = slash + 1; | |
2045 | } | 2045 | } | |
2046 | 2046 | |||
2047 | Global_Set(dirvar, dirname.str); | 2047 | Global_Set(dirvar, dirname.str); | |
2048 | Global_Set(filevar, basename); | 2048 | Global_Set(filevar, basename); | |
2049 | 2049 | |||
2050 | DEBUG4(PARSE, "SetFilenameVars: ${%s} = `%s' ${%s} = `%s'\n", | 2050 | DEBUG4(PARSE, "SetFilenameVars: ${%s} = `%s' ${%s} = `%s'\n", | |
2051 | dirvar, dirname.str, filevar, basename); | 2051 | dirvar, dirname.str, filevar, basename); | |
2052 | FStr_Done(&dirname); | 2052 | FStr_Done(&dirname); | |
2053 | } | 2053 | } | |
2054 | 2054 | |||
2055 | /* | 2055 | /* | |
2056 | * Return the immediately including file. | 2056 | * Return the immediately including file. | |
2057 | * | 2057 | * | |
2058 | * This is made complicated since the .for loop is implemented as a special | 2058 | * This is made complicated since the .for loop is implemented as a special | |
2059 | * kind of .include; see For_Run. | 2059 | * kind of .include; see For_Run. | |
2060 | */ | 2060 | */ | |
2061 | static const char * | 2061 | static const char * | |
2062 | GetActuallyIncludingFile(void) | 2062 | GetActuallyIncludingFile(void) | |
2063 | { | 2063 | { | |
2064 | size_t i; | 2064 | size_t i; | |
2065 | const IncludedFile *incs = GetInclude(0); | 2065 | const IncludedFile *incs = GetInclude(0); | |
2066 | 2066 | |||
2067 | for (i = includes.len; i >= 2; i--) | 2067 | for (i = includes.len; i >= 2; i--) | |
2068 | if (incs[i - 1].forLoop == NULL) | 2068 | if (incs[i - 1].forLoop == NULL) | |
2069 | return incs[i - 2].name.str; | 2069 | return incs[i - 2].name.str; | |
2070 | return NULL; | 2070 | return NULL; | |
2071 | } | 2071 | } | |
2072 | 2072 | |||
2073 | /* Set .PARSEDIR, .PARSEFILE, .INCLUDEDFROMDIR and .INCLUDEDFROMFILE. */ | 2073 | /* Set .PARSEDIR, .PARSEFILE, .INCLUDEDFROMDIR and .INCLUDEDFROMFILE. */ | |
2074 | static void | 2074 | static void | |
2075 | SetParseFile(const char *filename) | 2075 | SetParseFile(const char *filename) | |
2076 | { | 2076 | { | |
2077 | const char *including; | 2077 | const char *including; | |
2078 | 2078 | |||
2079 | SetFilenameVars(filename, ".PARSEDIR", ".PARSEFILE"); | 2079 | SetFilenameVars(filename, ".PARSEDIR", ".PARSEFILE"); | |
2080 | 2080 | |||
2081 | including = GetActuallyIncludingFile(); | 2081 | including = GetActuallyIncludingFile(); | |
2082 | if (including != NULL) { | 2082 | if (including != NULL) { | |
2083 | SetFilenameVars(including, | 2083 | SetFilenameVars(including, | |
2084 | ".INCLUDEDFROMDIR", ".INCLUDEDFROMFILE"); | 2084 | ".INCLUDEDFROMDIR", ".INCLUDEDFROMFILE"); | |
2085 | } else { | 2085 | } else { | |
2086 | Global_Delete(".INCLUDEDFROMDIR"); | 2086 | Global_Delete(".INCLUDEDFROMDIR"); | |
2087 | Global_Delete(".INCLUDEDFROMFILE"); | 2087 | Global_Delete(".INCLUDEDFROMFILE"); | |
2088 | } | 2088 | } | |
2089 | } | 2089 | } | |
2090 | 2090 | |||
2091 | static bool | 2091 | static bool | |
2092 | StrContainsWord(const char *str, const char *word) | 2092 | StrContainsWord(const char *str, const char *word) | |
2093 | { | 2093 | { | |
2094 | size_t strLen = strlen(str); | 2094 | size_t strLen = strlen(str); | |
2095 | size_t wordLen = strlen(word); | 2095 | size_t wordLen = strlen(word); | |
2096 | const char *p; | 2096 | const char *p; | |
2097 | 2097 | |||
2098 | if (strLen < wordLen) | 2098 | if (strLen < wordLen) | |
2099 | return false; | 2099 | return false; | |
2100 | 2100 | |||
2101 | for (p = str; p != NULL; p = strchr(p, ' ')) { | 2101 | for (p = str; p != NULL; p = strchr(p, ' ')) { | |
2102 | if (*p == ' ') | 2102 | if (*p == ' ') | |
2103 | p++; | 2103 | p++; | |
2104 | if (p > str + strLen - wordLen) | 2104 | if (p > str + strLen - wordLen) | |
2105 | return false; | 2105 | return false; | |
2106 | 2106 | |||
2107 | if (memcmp(p, word, wordLen) == 0 && | 2107 | if (memcmp(p, word, wordLen) == 0 && | |
2108 | (p[wordLen] == '\0' || p[wordLen] == ' ')) | 2108 | (p[wordLen] == '\0' || p[wordLen] == ' ')) | |
2109 | return true; | 2109 | return true; | |
2110 | } | 2110 | } | |
2111 | return false; | 2111 | return false; | |
2112 | } | 2112 | } | |
2113 | 2113 | |||
2114 | /* | 2114 | /* | |
2115 | * XXX: Searching through a set of words with this linear search is | 2115 | * XXX: Searching through a set of words with this linear search is | |
2116 | * inefficient for variables that contain thousands of words. | 2116 | * inefficient for variables that contain thousands of words. | |
2117 | * | 2117 | * | |
2118 | * XXX: The paths in this list don't seem to be normalized in any way. | 2118 | * XXX: The paths in this list don't seem to be normalized in any way. | |
2119 | */ | 2119 | */ | |
2120 | static bool | 2120 | static bool | |
2121 | VarContainsWord(const char *varname, const char *word) | 2121 | VarContainsWord(const char *varname, const char *word) | |
2122 | { | 2122 | { | |
2123 | FStr val = Var_Value(SCOPE_GLOBAL, varname); | 2123 | FStr val = Var_Value(SCOPE_GLOBAL, varname); | |
2124 | bool found = val.str != NULL && StrContainsWord(val.str, word); | 2124 | bool found = val.str != NULL && StrContainsWord(val.str, word); | |
2125 | FStr_Done(&val); | 2125 | FStr_Done(&val); | |
2126 | return found; | 2126 | return found; | |
2127 | } | 2127 | } | |
2128 | 2128 | |||
2129 | /* | 2129 | /* | |
2130 | * Track the makefiles we read - so makefiles can set dependencies on them. | 2130 | * Track the makefiles we read - so makefiles can set dependencies on them. | |
2131 | * Avoid adding anything more than once. | 2131 | * Avoid adding anything more than once. | |
2132 | * | 2132 | * | |
2133 | * Time complexity: O(n) per call, in total O(n^2), where n is the number | 2133 | * Time complexity: O(n) per call, in total O(n^2), where n is the number | |
2134 | * of makefiles that have been loaded. | 2134 | * of makefiles that have been loaded. | |
2135 | */ | 2135 | */ | |
2136 | static void | 2136 | static void | |
2137 | TrackInput(const char *name) | 2137 | TrackInput(const char *name) | |
2138 | { | 2138 | { | |
2139 | if (!VarContainsWord(".MAKE.MAKEFILES", name)) | 2139 | if (!VarContainsWord(".MAKE.MAKEFILES", name)) | |
2140 | Global_Append(".MAKE.MAKEFILES", name); | 2140 | Global_Append(".MAKE.MAKEFILES", name); | |
2141 | } | 2141 | } | |
2142 | 2142 | |||
2143 | 2143 | |||
2144 | /* Parse from the given buffer, later return to the current file. */ | 2144 | /* Parse from the given buffer, later return to the current file. */ | |
2145 | void | 2145 | void | |
2146 | Parse_PushInput(const char *name, unsigned lineno, unsigned readLines, | 2146 | Parse_PushInput(const char *name, unsigned lineno, unsigned readLines, | |
2147 | Buffer buf, struct ForLoop *forLoop) | 2147 | Buffer buf, struct ForLoop *forLoop) | |
2148 | { | 2148 | { | |
2149 | IncludedFile *curFile; | 2149 | IncludedFile *curFile; | |
2150 | 2150 | |||
2151 | if (forLoop != NULL) | 2151 | if (forLoop != NULL) | |
2152 | name = CurFile()->name.str; | 2152 | name = CurFile()->name.str; | |
2153 | else | 2153 | else | |
2154 | TrackInput(name); | 2154 | TrackInput(name); | |
2155 | 2155 | |||
2156 | DEBUG3(PARSE, "Parse_PushInput: %s %s, line %u\n", | 2156 | DEBUG3(PARSE, "Parse_PushInput: %s %s, line %u\n", | |
2157 | forLoop != NULL ? ".for loop in": "file", name, lineno); | 2157 | forLoop != NULL ? ".for loop in": "file", name, lineno); | |
2158 | 2158 | |||
2159 | curFile = Vector_Push(&includes); | 2159 | curFile = Vector_Push(&includes); | |
2160 | curFile->name = FStr_InitOwn(bmake_strdup(name)); | 2160 | curFile->name = FStr_InitOwn(bmake_strdup(name)); | |
2161 | curFile->lineno = lineno; | 2161 | curFile->lineno = lineno; | |
2162 | curFile->readLines = readLines; | 2162 | curFile->readLines = readLines; | |
2163 | curFile->forHeadLineno = lineno; | 2163 | curFile->forHeadLineno = lineno; | |
2164 | curFile->forBodyReadLines = readLines; | 2164 | curFile->forBodyReadLines = readLines; | |
2165 | curFile->buf = buf; | 2165 | curFile->buf = buf; | |
2166 | curFile->depending = doing_depend; /* restore this on EOF */ | 2166 | curFile->depending = doing_depend; /* restore this on EOF */ | |
2167 | curFile->guardState = forLoop == NULL ? GS_START : GS_NO; | 2167 | curFile->guardState = forLoop == NULL ? GS_START : GS_NO; | |
2168 | curFile->guard = NULL; | 2168 | curFile->guard = NULL; | |
2169 | curFile->forLoop = forLoop; | 2169 | curFile->forLoop = forLoop; | |
2170 | 2170 | |||
2171 | if (forLoop != NULL && !For_NextIteration(forLoop, &curFile->buf)) | 2171 | if (forLoop != NULL && !For_NextIteration(forLoop, &curFile->buf)) | |
2172 | abort(); /* see For_Run */ | 2172 | abort(); /* see For_Run */ | |
2173 | 2173 | |||
2174 | curFile->buf_ptr = curFile->buf.data; | 2174 | curFile->buf_ptr = curFile->buf.data; | |
2175 | curFile->buf_end = curFile->buf.data + curFile->buf.len; | 2175 | curFile->buf_end = curFile->buf.data + curFile->buf.len; | |
2176 | curFile->condMinDepth = cond_depth; | 2176 | curFile->condMinDepth = cond_depth; | |
2177 | SetParseFile(name); | 2177 | SetParseFile(name); | |
2178 | } | 2178 | } | |
2179 | 2179 | |||
2180 | /* Check if the directive is an include directive. */ | 2180 | /* Check if the directive is an include directive. */ | |
2181 | static bool | 2181 | static bool | |
2182 | IsInclude(const char *dir, bool sysv) | 2182 | IsInclude(const char *dir, bool sysv) | |
2183 | { | 2183 | { | |
2184 | if (dir[0] == 's' || dir[0] == '-' || (dir[0] == 'd' && !sysv)) | 2184 | if (dir[0] == 's' || dir[0] == '-' || (dir[0] == 'd' && !sysv)) | |
2185 | dir++; | 2185 | dir++; | |
2186 | 2186 | |||
2187 | if (strncmp(dir, "include", 7) != 0) | 2187 | if (strncmp(dir, "include", 7) != 0) | |
2188 | return false; | 2188 | return false; | |
2189 | 2189 | |||
2190 | /* Space is not mandatory for BSD .include */ | 2190 | /* Space is not mandatory for BSD .include */ | |
2191 | return !sysv || ch_isspace(dir[7]); | 2191 | return !sysv || ch_isspace(dir[7]); | |
2192 | } | 2192 | } | |
2193 | 2193 | |||
2194 | 2194 | |||
2195 | /* Check if the line is a SYSV include directive. */ | 2195 | /* Check if the line is a SYSV include directive. */ | |
2196 | static bool | 2196 | static bool | |
2197 | IsSysVInclude(const char *line) | 2197 | IsSysVInclude(const char *line) | |
2198 | { | 2198 | { | |
2199 | const char *p; | 2199 | const char *p; | |
2200 | 2200 | |||
2201 | if (!IsInclude(line, true)) | 2201 | if (!IsInclude(line, true)) | |
2202 | return false; | 2202 | return false; | |
2203 | 2203 | |||
2204 | /* Avoid interpreting a dependency line as an include */ | 2204 | /* Avoid interpreting a dependency line as an include */ | |
2205 | for (p = line; (p = strchr(p, ':')) != NULL;) { | 2205 | for (p = line; (p = strchr(p, ':')) != NULL;) { | |
2206 | 2206 | |||
2207 | /* end of line -> it's a dependency */ | 2207 | /* end of line -> it's a dependency */ | |
2208 | if (*++p == '\0') | 2208 | if (*++p == '\0') | |
2209 | return false; | 2209 | return false; | |
2210 | 2210 | |||
2211 | /* '::' operator or ': ' -> it's a dependency */ | 2211 | /* '::' operator or ': ' -> it's a dependency */ | |
2212 | if (*p == ':' || ch_isspace(*p)) | 2212 | if (*p == ':' || ch_isspace(*p)) | |
2213 | return false; | 2213 | return false; | |
2214 | } | 2214 | } | |
2215 | return true; | 2215 | return true; | |
2216 | } | 2216 | } | |
2217 | 2217 | |||
2218 | /* Push to another file. The line points to the word "include". */ | 2218 | /* Push to another file. The line points to the word "include". */ | |
2219 | static void | 2219 | static void | |
2220 | ParseTraditionalInclude(char *line) | 2220 | ParseTraditionalInclude(char *line) | |
2221 | { | 2221 | { | |
2222 | char *p; /* current position in file spec */ | 2222 | char *p; /* current position in file spec */ | |
2223 | bool done = false; | 2223 | bool done = false; | |
2224 | bool silent = line[0] != 'i'; | 2224 | bool silent = line[0] != 'i'; | |
2225 | char *file = line + (silent ? 8 : 7); | 2225 | char *file = line + (silent ? 8 : 7); | |
2226 | char *all_files; | 2226 | char *all_files; | |
2227 | 2227 | |||
2228 | DEBUG1(PARSE, "ParseTraditionalInclude: %s\n", file); | 2228 | DEBUG1(PARSE, "ParseTraditionalInclude: %s\n", file); | |
2229 | 2229 | |||
2230 | pp_skip_whitespace(&file); | 2230 | pp_skip_whitespace(&file); | |
2231 | 2231 | |||
2232 | all_files = Var_Subst(file, SCOPE_CMDLINE, VARE_WANTRES); | 2232 | all_files = Var_Subst(file, SCOPE_CMDLINE, VARE_WANTRES); | |
2233 | /* TODO: handle errors */ | 2233 | /* TODO: handle errors */ | |
2234 | 2234 | |||
2235 | for (file = all_files; !done; file = p + 1) { | 2235 | for (file = all_files; !done; file = p + 1) { | |
2236 | /* Skip to end of line or next whitespace */ | 2236 | /* Skip to end of line or next whitespace */ | |
2237 | for (p = file; *p != '\0' && !ch_isspace(*p); p++) | 2237 | for (p = file; *p != '\0' && !ch_isspace(*p); p++) | |
2238 | continue; | 2238 | continue; | |
2239 | 2239 | |||
2240 | if (*p != '\0') | 2240 | if (*p != '\0') | |
2241 | *p = '\0'; | 2241 | *p = '\0'; | |
2242 | else | 2242 | else | |
2243 | done = true; | 2243 | done = true; | |
2244 | 2244 | |||
2245 | IncludeFile(file, false, false, silent); | 2245 | IncludeFile(file, false, false, silent); | |
2246 | } | 2246 | } | |
2247 | 2247 | |||
2248 | free(all_files); | 2248 | free(all_files); | |
2249 | } | 2249 | } | |
2250 | 2250 | |||
2251 | /* Parse "export <variable>=<value>", and actually export it. */ | 2251 | /* Parse "export <variable>=<value>", and actually export it. */ | |
2252 | static void | 2252 | static void | |
2253 | ParseGmakeExport(char *line) | 2253 | ParseGmakeExport(char *line) | |
2254 | { | 2254 | { | |
2255 | char *variable = line + 6; | 2255 | char *variable = line + 6; | |
2256 | char *value; | 2256 | char *value; | |
2257 | 2257 | |||
2258 | DEBUG1(PARSE, "ParseGmakeExport: %s\n", variable); | 2258 | DEBUG1(PARSE, "ParseGmakeExport: %s\n", variable); | |
2259 | 2259 | |||
2260 | pp_skip_whitespace(&variable); | 2260 | pp_skip_whitespace(&variable); | |
2261 | 2261 | |||
2262 | for (value = variable; *value != '\0' && *value != '='; value++) | 2262 | for (value = variable; *value != '\0' && *value != '='; value++) | |
2263 | continue; | 2263 | continue; | |
2264 | 2264 | |||
2265 | if (*value != '=') { | 2265 | if (*value != '=') { | |
2266 | Parse_Error(PARSE_FATAL, | 2266 | Parse_Error(PARSE_FATAL, | |
2267 | "Variable/Value missing from \"export\""); | 2267 | "Variable/Value missing from \"export\""); | |
2268 | return; | 2268 | return; | |
2269 | } | 2269 | } | |
2270 | *value++ = '\0'; /* terminate variable */ | 2270 | *value++ = '\0'; /* terminate variable */ | |
2271 | 2271 | |||
2272 | /* | 2272 | /* | |
2273 | * Expand the value before putting it in the environment. | 2273 | * Expand the value before putting it in the environment. | |
2274 | */ | 2274 | */ | |
2275 | value = Var_Subst(value, SCOPE_CMDLINE, VARE_WANTRES); | 2275 | value = Var_Subst(value, SCOPE_CMDLINE, VARE_WANTRES); | |
2276 | /* TODO: handle errors */ | 2276 | /* TODO: handle errors */ | |
2277 | 2277 | |||
2278 | setenv(variable, value, 1); | 2278 | setenv(variable, value, 1); | |
2279 | free(value); | 2279 | free(value); | |
2280 | } | 2280 | } | |
2281 | 2281 | |||
2282 | /* | 2282 | /* | |
2283 | * When the end of the current file or .for loop is reached, continue reading | 2283 | * When the end of the current file or .for loop is reached, continue reading | |
2284 | * the previous file at the previous location. | 2284 | * the previous file at the previous location. | |
2285 | * | 2285 | * | |
2286 | * Results: | 2286 | * Results: | |
2287 | * true to continue parsing, i.e. it had only reached the end of an | 2287 | * true to continue parsing, i.e. it had only reached the end of an | |
2288 | * included file, false if the main file has been parsed completely. | 2288 | * included file, false if the main file has been parsed completely. | |
2289 | */ | 2289 | */ | |
2290 | static bool | 2290 | static bool | |
2291 | ParseEOF(void) | 2291 | ParseEOF(void) | |
2292 | { | 2292 | { | |
2293 | IncludedFile *curFile = CurFile(); | 2293 | IncludedFile *curFile = CurFile(); | |
2294 | 2294 | |||
2295 | doing_depend = curFile->depending; | 2295 | doing_depend = curFile->depending; | |
2296 | if (curFile->forLoop != NULL && | 2296 | if (curFile->forLoop != NULL && | |
2297 | For_NextIteration(curFile->forLoop, &curFile->buf)) { | 2297 | For_NextIteration(curFile->forLoop, &curFile->buf)) { | |
2298 | curFile->buf_ptr = curFile->buf.data; | 2298 | curFile->buf_ptr = curFile->buf.data; | |
2299 | curFile->buf_end = curFile->buf.data + curFile->buf.len; | 2299 | curFile->buf_end = curFile->buf.data + curFile->buf.len; | |
2300 | curFile->readLines = curFile->forBodyReadLines; | 2300 | curFile->readLines = curFile->forBodyReadLines; | |
2301 | return true; | 2301 | return true; | |
2302 | } | 2302 | } | |
2303 | 2303 | |||
2304 | Cond_EndFile(); | 2304 | Cond_EndFile(); | |
2305 | 2305 | |||
2306 | if (curFile->guardState == GS_DONE) | 2306 | if (curFile->guardState == GS_DONE) | |
2307 | HashTable_Set(&guards, curFile->name.str, curFile->guard); | 2307 | HashTable_Set(&guards, curFile->name.str, curFile->guard); | |
2308 | else if (curFile->guard != NULL) { | 2308 | else if (curFile->guard != NULL) { | |
2309 | free(curFile->guard->name); | 2309 | free(curFile->guard->name); | |
2310 | free(curFile->guard); | 2310 | free(curFile->guard); | |
2311 | } | 2311 | } | |
2312 | 2312 | |||
2313 | FStr_Done(&curFile->name); | 2313 | FStr_Done(&curFile->name); | |
2314 | Buf_Done(&curFile->buf); | 2314 | Buf_Done(&curFile->buf); | |
2315 | if (curFile->forLoop != NULL) | 2315 | if (curFile->forLoop != NULL) | |
2316 | ForLoop_Free(curFile->forLoop); | 2316 | ForLoop_Free(curFile->forLoop); | |
2317 | Vector_Pop(&includes); | 2317 | Vector_Pop(&includes); | |
2318 | 2318 | |||
2319 | if (includes.len == 0) { | 2319 | if (includes.len == 0) { | |
2320 | /* We've run out of input */ | 2320 | /* We've run out of input */ | |
2321 | Global_Delete(".PARSEDIR"); | 2321 | Global_Delete(".PARSEDIR"); | |
2322 | Global_Delete(".PARSEFILE"); | 2322 | Global_Delete(".PARSEFILE"); | |
2323 | Global_Delete(".INCLUDEDFROMDIR"); | 2323 | Global_Delete(".INCLUDEDFROMDIR"); | |
2324 | Global_Delete(".INCLUDEDFROMFILE"); | 2324 | Global_Delete(".INCLUDEDFROMFILE"); | |
2325 | return false; | 2325 | return false; | |
2326 | } | 2326 | } | |
2327 | 2327 | |||
2328 | curFile = CurFile(); | 2328 | curFile = CurFile(); | |
2329 | DEBUG2(PARSE, "ParseEOF: returning to file %s, line %u\n", | 2329 | DEBUG2(PARSE, "ParseEOF: returning to file %s, line %u\n", | |
2330 | curFile->name.str, curFile->readLines + 1); | 2330 | curFile->name.str, curFile->readLines + 1); | |
2331 | 2331 | |||
2332 | SetParseFile(curFile->name.str); | 2332 | SetParseFile(curFile->name.str); | |
2333 | return true; | 2333 | return true; | |
2334 | } | 2334 | } | |
2335 | 2335 | |||
2336 | typedef enum ParseRawLineResult { | 2336 | typedef enum ParseRawLineResult { | |
2337 | PRLR_LINE, | 2337 | PRLR_LINE, | |
2338 | PRLR_EOF, | 2338 | PRLR_EOF, | |
2339 | PRLR_ERROR | 2339 | PRLR_ERROR | |
2340 | } ParseRawLineResult; | 2340 | } ParseRawLineResult; | |
2341 | 2341 | |||
2342 | /* | 2342 | /* | |
2343 | * Parse until the end of a line, taking into account lines that end with | 2343 | * Parse until the end of a line, taking into account lines that end with | |
2344 | * backslash-newline. The resulting line goes from out_line to out_line_end; | 2344 | * backslash-newline. The resulting line goes from out_line to out_line_end; | |
2345 | * the line is not null-terminated. | 2345 | * the line is not null-terminated. | |
2346 | */ | 2346 | */ | |
2347 | static ParseRawLineResult | 2347 | static ParseRawLineResult | |
2348 | ParseRawLine(IncludedFile *curFile, char **out_line, char **out_line_end, | 2348 | ParseRawLine(IncludedFile *curFile, char **out_line, char **out_line_end, | |
2349 | char **out_firstBackslash, char **out_commentLineEnd) | 2349 | char **out_firstBackslash, char **out_commentLineEnd) | |
2350 | { | 2350 | { | |
2351 | char *line = curFile->buf_ptr; | 2351 | char *line = curFile->buf_ptr; | |
2352 | char *buf_end = curFile->buf_end; | 2352 | char *buf_end = curFile->buf_end; | |
2353 | char *p = line; | 2353 | char *p = line; | |
2354 | char *line_end = line; | 2354 | char *line_end = line; | |
2355 | char *firstBackslash = NULL; | 2355 | char *firstBackslash = NULL; | |
2356 | char *commentLineEnd = NULL; | 2356 | char *commentLineEnd = NULL; | |
2357 | ParseRawLineResult res = PRLR_LINE; | 2357 | ParseRawLineResult res = PRLR_LINE; | |
2358 | 2358 | |||
2359 | curFile->readLines++; | 2359 | curFile->readLines++; | |
2360 | 2360 | |||
2361 | for (;;) { | 2361 | for (;;) { | |
2362 | char ch; | 2362 | char ch; | |
2363 | 2363 | |||
2364 | if (p == buf_end) { | 2364 | if (p == buf_end) { | |
2365 | res = PRLR_EOF; | 2365 | res = PRLR_EOF; | |
2366 | break; | 2366 | break; | |
2367 | } | 2367 | } | |
2368 | 2368 | |||
2369 | ch = *p; | 2369 | ch = *p; | |
2370 | if (ch == '\0' || (ch == '\\' && p[1] == '\0')) { | 2370 | if (ch == '\0' || (ch == '\\' && p[1] == '\0')) { | |
2371 | Parse_Error(PARSE_FATAL, "Zero byte read from file"); | 2371 | Parse_Error(PARSE_FATAL, "Zero byte read from file"); | |
2372 | exit(2); | 2372 | exit(2); | |
2373 | } | 2373 | } | |
2374 | 2374 | |||
2375 | /* Treat next character after '\' as literal. */ | 2375 | /* Treat next character after '\' as literal. */ | |
2376 | if (ch == '\\') { | 2376 | if (ch == '\\') { | |
2377 | if (firstBackslash == NULL) | 2377 | if (firstBackslash == NULL) | |
2378 | firstBackslash = p; | 2378 | firstBackslash = p; | |
2379 | if (p[1] == '\n') { | 2379 | if (p[1] == '\n') { | |
2380 | curFile->readLines++; | 2380 | curFile->readLines++; | |
2381 | if (p + 2 == buf_end) { | 2381 | if (p + 2 == buf_end) { | |
2382 | line_end = p; | 2382 | line_end = p; | |
2383 | *line_end = '\n'; | 2383 | *line_end = '\n'; | |
2384 | p += 2; | 2384 | p += 2; | |
2385 | continue; | 2385 | continue; | |
2386 | } | 2386 | } | |
2387 | } | 2387 | } | |
2388 | p += 2; | 2388 | p += 2; | |
2389 | line_end = p; | 2389 | line_end = p; | |
2390 | assert(p <= buf_end); | 2390 | assert(p <= buf_end); | |
2391 | continue; | 2391 | continue; | |
2392 | } | 2392 | } | |
2393 | 2393 | |||
2394 | /* | 2394 | /* | |
2395 | * Remember the first '#' for comment stripping, unless | 2395 | * Remember the first '#' for comment stripping, unless | |
2396 | * the previous char was '[', as in the modifier ':[#]'. | 2396 | * the previous char was '[', as in the modifier ':[#]'. | |
2397 | */ | 2397 | */ | |
2398 | if (ch == '#' && commentLineEnd == NULL && | 2398 | if (ch == '#' && commentLineEnd == NULL && | |
2399 | !(p > line && p[-1] == '[')) | 2399 | !(p > line && p[-1] == '[')) | |
2400 | commentLineEnd = line_end; | 2400 | commentLineEnd = line_end; | |
2401 | 2401 | |||
2402 | p++; | 2402 | p++; | |
2403 | if (ch == '\n') | 2403 | if (ch == '\n') | |
2404 | break; | 2404 | break; | |
2405 | 2405 | |||
2406 | /* We are not interested in trailing whitespace. */ | 2406 | /* We are not interested in trailing whitespace. */ | |
2407 | if (!ch_isspace(ch)) | 2407 | if (!ch_isspace(ch)) | |
2408 | line_end = p; | 2408 | line_end = p; | |
2409 | } | 2409 | } | |
2410 | 2410 | |||
2411 | curFile->buf_ptr = p; | 2411 | curFile->buf_ptr = p; | |
2412 | *out_line = line; | 2412 | *out_line = line; | |
2413 | *out_line_end = line_end; | 2413 | *out_line_end = line_end; | |
2414 | *out_firstBackslash = firstBackslash; | 2414 | *out_firstBackslash = firstBackslash; | |
2415 | *out_commentLineEnd = commentLineEnd; | 2415 | *out_commentLineEnd = commentLineEnd; | |
2416 | return res; | 2416 | return res; | |
2417 | } | 2417 | } | |
2418 | 2418 | |||
2419 | /* | 2419 | /* | |
2420 | * Beginning at start, unescape '\#' to '#' and replace backslash-newline | 2420 | * Beginning at start, unescape '\#' to '#' and replace backslash-newline | |
2421 | * with a single space. | 2421 | * with a single space. | |
2422 | */ | 2422 | */ | |
2423 | static void | 2423 | static void | |
2424 | UnescapeBackslash(char *line, char *start) | 2424 | UnescapeBackslash(char *line, char *start) | |
2425 | { | 2425 | { | |
2426 | const char *src = start; | 2426 | const char *src = start; | |
2427 | char *dst = start; | 2427 | char *dst = start; | |
2428 | char *spaceStart = line; | 2428 | char *spaceStart = line; | |
2429 | 2429 | |||
2430 | for (;;) { | 2430 | for (;;) { | |
2431 | char ch = *src++; | 2431 | char ch = *src++; | |
2432 | if (ch != '\\') { | 2432 | if (ch != '\\') { | |
2433 | if (ch == '\0') | 2433 | if (ch == '\0') | |
2434 | break; | 2434 | break; | |
2435 | *dst++ = ch; | 2435 | *dst++ = ch; | |
2436 | continue; | 2436 | continue; | |
2437 | } | 2437 | } | |
2438 | 2438 | |||
2439 | ch = *src++; | 2439 | ch = *src++; | |
2440 | if (ch == '\0') { | 2440 | if (ch == '\0') { | |
2441 | /* Delete '\\' at the end of the buffer. */ | 2441 | /* Delete '\\' at the end of the buffer. */ | |
2442 | dst--; | 2442 | dst--; | |
2443 | break; | 2443 | break; | |
2444 | } | 2444 | } | |
2445 | 2445 | |||
2446 | /* Delete '\\' from before '#' on non-command lines. */ | 2446 | /* Delete '\\' from before '#' on non-command lines. */ | |
2447 | if (ch == '#' && line[0] != '\t') | 2447 | if (ch == '#' && line[0] != '\t') | |
2448 | *dst++ = ch; | 2448 | *dst++ = ch; | |
2449 | else if (ch == '\n') { | 2449 | else if (ch == '\n') { | |
2450 | cpp_skip_hspace(&src); | 2450 | cpp_skip_hspace(&src); | |
2451 | *dst++ = ' '; | 2451 | *dst++ = ' '; | |
2452 | } else { | 2452 | } else { | |
2453 | /* Leave '\\' in the buffer for later. */ | 2453 | /* Leave '\\' in the buffer for later. */ | |
2454 | *dst++ = '\\'; | 2454 | *dst++ = '\\'; | |
2455 | *dst++ = ch; | 2455 | *dst++ = ch; | |
2456 | /* Keep an escaped ' ' at the line end. */ | 2456 | /* Keep an escaped ' ' at the line end. */ | |
2457 | spaceStart = dst; | 2457 | spaceStart = dst; | |
2458 | } | 2458 | } | |
2459 | } | 2459 | } | |
2460 | 2460 | |||
2461 | /* Delete any trailing spaces - eg from empty continuations */ | 2461 | /* Delete any trailing spaces - eg from empty continuations */ | |
2462 | while (dst > spaceStart && ch_isspace(dst[-1])) | 2462 | while (dst > spaceStart && ch_isspace(dst[-1])) | |
2463 | dst--; | 2463 | dst--; | |
2464 | *dst = '\0'; | 2464 | *dst = '\0'; | |
2465 | } | 2465 | } | |
2466 | 2466 | |||
2467 | typedef enum LineKind { | 2467 | typedef enum LineKind { | |
2468 | /* | 2468 | /* | |
2469 | * Return the next line that is neither empty nor a comment. | 2469 | * Return the next line that is neither empty nor a comment. | |
2470 | * Backslash line continuations are folded into a single space. | 2470 | * Backslash line continuations are folded into a single space. | |
2471 | * A trailing comment, if any, is discarded. | 2471 | * A trailing comment, if any, is discarded. | |
2472 | */ | 2472 | */ | |
2473 | LK_NONEMPTY, | 2473 | LK_NONEMPTY, | |
2474 | 2474 | |||
2475 | /* | 2475 | /* | |
2476 | * Return the next line, even if it is empty or a comment. | 2476 | * Return the next line, even if it is empty or a comment. | |
2477 | * Preserve backslash-newline to keep the line numbers correct. | 2477 | * Preserve backslash-newline to keep the line numbers correct. | |
2478 | * | 2478 | * | |
2479 | * Used in .for loops to collect the body of the loop while waiting | 2479 | * Used in .for loops to collect the body of the loop while waiting | |
2480 | * for the corresponding .endfor. | 2480 | * for the corresponding .endfor. | |
2481 | */ | 2481 | */ | |
2482 | LK_FOR_BODY, | 2482 | LK_FOR_BODY, | |
2483 | 2483 | |||
2484 | /* | 2484 | /* | |
2485 | * Return the next line that starts with a dot. | 2485 | * Return the next line that starts with a dot. | |
2486 | * Backslash line continuations are folded into a single space. | 2486 | * Backslash line continuations are folded into a single space. | |
2487 | * A trailing comment, if any, is discarded. | 2487 | * A trailing comment, if any, is discarded. | |
2488 | * | 2488 | * | |
2489 | * Used in .if directives to skip over irrelevant branches while | 2489 | * Used in .if directives to skip over irrelevant branches while | |
2490 | * waiting for the corresponding .endif. | 2490 | * waiting for the corresponding .endif. | |
2491 | */ | 2491 | */ | |
2492 | LK_DOT | 2492 | LK_DOT | |
2493 | } LineKind; | 2493 | } LineKind; | |
2494 | 2494 | |||
2495 | /* | 2495 | /* | |
2496 | * Return the next "interesting" logical line from the current file. The | 2496 | * Return the next "interesting" logical line from the current file. The | |
2497 | * returned string will be freed at the end of including the file. | 2497 | * returned string will be freed at the end of including the file. | |
2498 | */ | 2498 | */ | |
2499 | static char * | 2499 | static char * | |
2500 | ReadLowLevelLine(LineKind kind) | 2500 | ReadLowLevelLine(LineKind kind) | |
2501 | { | 2501 | { | |
2502 | IncludedFile *curFile = CurFile(); | 2502 | IncludedFile *curFile = CurFile(); | |
2503 | ParseRawLineResult res; | 2503 | ParseRawLineResult res; | |
2504 | char *line; | 2504 | char *line; | |
2505 | char *line_end; | 2505 | char *line_end; | |
2506 | char *firstBackslash; | 2506 | char *firstBackslash; | |
2507 | char *commentLineEnd; | 2507 | char *commentLineEnd; | |
2508 | 2508 | |||
2509 | for (;;) { | 2509 | for (;;) { | |
2510 | curFile->lineno = curFile->readLines + 1; | 2510 | curFile->lineno = curFile->readLines + 1; | |
2511 | res = ParseRawLine(curFile, | 2511 | res = ParseRawLine(curFile, | |
2512 | &line, &line_end, &firstBackslash, &commentLineEnd); | 2512 | &line, &line_end, &firstBackslash, &commentLineEnd); | |
2513 | if (res == PRLR_ERROR) | 2513 | if (res == PRLR_ERROR) | |
2514 | return NULL; | 2514 | return NULL; | |
2515 | 2515 | |||
2516 | if (line == line_end || line == commentLineEnd) { | 2516 | if (line == line_end || line == commentLineEnd) { | |
2517 | if (res == PRLR_EOF) | 2517 | if (res == PRLR_EOF) | |
2518 | return NULL; | 2518 | return NULL; | |
2519 | if (kind != LK_FOR_BODY) | 2519 | if (kind != LK_FOR_BODY) | |
2520 | continue; | 2520 | continue; | |
2521 | } | 2521 | } | |
2522 | 2522 | |||
2523 | /* We now have a line of data */ | 2523 | /* We now have a line of data */ | |
2524 | assert(ch_isspace(*line_end)); | 2524 | assert(ch_isspace(*line_end)); | |
2525 | *line_end = '\0'; | 2525 | *line_end = '\0'; | |
2526 | 2526 | |||
2527 | if (kind == LK_FOR_BODY) | 2527 | if (kind == LK_FOR_BODY) | |
2528 | return line; /* Don't join the physical lines. */ | 2528 | return line; /* Don't join the physical lines. */ | |
2529 | 2529 | |||
2530 | if (kind == LK_DOT && line[0] != '.') | 2530 | if (kind == LK_DOT && line[0] != '.') | |
2531 | continue; | 2531 | continue; | |
2532 | break; | 2532 | break; | |
2533 | } | 2533 | } | |
2534 | 2534 | |||
2535 | if (commentLineEnd != NULL && line[0] != '\t') | 2535 | if (commentLineEnd != NULL && line[0] != '\t') | |
2536 | *commentLineEnd = '\0'; | 2536 | *commentLineEnd = '\0'; | |
2537 | if (firstBackslash != NULL) | 2537 | if (firstBackslash != NULL) | |
2538 | UnescapeBackslash(line, firstBackslash); | 2538 | UnescapeBackslash(line, firstBackslash); | |
2539 | return line; | 2539 | return line; | |
2540 | } | 2540 | } | |
2541 | 2541 | |||
2542 | static bool | 2542 | static bool | |
2543 | SkipIrrelevantBranches(void) | 2543 | SkipIrrelevantBranches(void) | |
2544 | { | 2544 | { | |
2545 | const char *line; | 2545 | const char *line; | |
2546 | 2546 | |||
2547 | while ((line = ReadLowLevelLine(LK_DOT)) != NULL) | 2547 | while ((line = ReadLowLevelLine(LK_DOT)) != NULL) | |
2548 | if (Cond_EvalLine(line) == CR_TRUE) | 2548 | if (Cond_EvalLine(line) == CR_TRUE) | |
2549 | return true; | 2549 | return true; | |
2550 | return false; | 2550 | return false; | |
2551 | } | 2551 | } | |
2552 | 2552 | |||
2553 | static bool | 2553 | static bool | |
2554 | ParseForLoop(const char *line) | 2554 | ParseForLoop(const char *line) | |
2555 | { | 2555 | { | |
2556 | int rval; | 2556 | int rval; | |
2557 | unsigned forHeadLineno; | 2557 | unsigned forHeadLineno; | |
2558 | unsigned bodyReadLines; | 2558 | unsigned bodyReadLines; | |
2559 | int forLevel; | 2559 | int forLevel; | |
2560 | 2560 | |||
2561 | rval = For_Eval(line); | 2561 | rval = For_Eval(line); | |
2562 | if (rval == 0) | 2562 | if (rval == 0) | |
2563 | return false; /* Not a .for line */ | 2563 | return false; /* Not a .for line */ | |
2564 | if (rval < 0) | 2564 | if (rval < 0) | |
2565 | return true; /* Syntax error - error printed, ignore line */ | 2565 | return true; /* Syntax error - error printed, ignore line */ | |
2566 | 2566 | |||
2567 | forHeadLineno = CurFile()->lineno; | 2567 | forHeadLineno = CurFile()->lineno; | |
2568 | bodyReadLines = CurFile()->readLines; | 2568 | bodyReadLines = CurFile()->readLines; | |
2569 | 2569 | |||
2570 | /* Accumulate the loop body until the matching '.endfor'. */ | 2570 | /* Accumulate the loop body until the matching '.endfor'. */ | |
2571 | forLevel = 1; | 2571 | forLevel = 1; | |
2572 | do { | 2572 | do { | |
2573 | line = ReadLowLevelLine(LK_FOR_BODY); | 2573 | line = ReadLowLevelLine(LK_FOR_BODY); | |
2574 | if (line == NULL) { | 2574 | if (line == NULL) { | |
2575 | Parse_Error(PARSE_FATAL, | 2575 | Parse_Error(PARSE_FATAL, | |
2576 | "Unexpected end of file in .for loop"); | 2576 | "Unexpected end of file in .for loop"); | |
2577 | break; | 2577 | break; | |
2578 | } | 2578 | } | |
2579 | } while (For_Accum(line, &forLevel)); | 2579 | } while (For_Accum(line, &forLevel)); | |
2580 | 2580 | |||
2581 | For_Run(forHeadLineno, bodyReadLines); | 2581 | For_Run(forHeadLineno, bodyReadLines); | |
2582 | return true; | 2582 | return true; | |
2583 | } | 2583 | } | |
2584 | 2584 | |||
2585 | /* | 2585 | /* | |
2586 | * Read an entire line from the input file. | 2586 | * Read an entire line from the input file. | |
2587 | * | 2587 | * | |
2588 | * Empty lines, .if and .for are handled by this function, while variable | 2588 | * Empty lines, .if and .for are handled by this function, while variable | |
2589 | * assignments, other directives, dependency lines and shell commands are | 2589 | * assignments, other directives, dependency lines and shell commands are | |
2590 | * handled by the caller. | 2590 | * handled by the caller. | |
2591 | * | 2591 | * | |
2592 | * Return a line without trailing whitespace, or NULL for EOF. The returned | 2592 | * Return a line without trailing whitespace, or NULL for EOF. The returned | |
2593 | * string will be freed at the end of including the file. | 2593 | * string will be freed at the end of including the file. | |
2594 | */ | 2594 | */ | |
2595 | static char * | 2595 | static char * | |
2596 | ReadHighLevelLine(void) | 2596 | ReadHighLevelLine(void) | |
2597 | { | 2597 | { | |
2598 | char *line; | 2598 | char *line; | |
2599 | CondResult condResult; | 2599 | CondResult condResult; | |
2600 | 2600 | |||
2601 | for (;;) { | 2601 | for (;;) { | |
2602 | IncludedFile *curFile = CurFile(); | 2602 | IncludedFile *curFile = CurFile(); | |
2603 | line = ReadLowLevelLine(LK_NONEMPTY); | 2603 | line = ReadLowLevelLine(LK_NONEMPTY); | |
2604 | if (posix_state == PS_MAYBE_NEXT_LINE) | 2604 | if (posix_state == PS_MAYBE_NEXT_LINE) | |
2605 | posix_state = PS_NOW_OR_NEVER; | 2605 | posix_state = PS_NOW_OR_NEVER; | |
2606 | else | 2606 | else | |
2607 | posix_state = PS_TOO_LATE; | 2607 | posix_state = PS_TOO_LATE; | |
2608 | if (line == NULL) | 2608 | if (line == NULL) | |
2609 | return NULL; | 2609 | return NULL; | |
2610 | 2610 | |||
2611 | DEBUG2(PARSE, "Parsing line %u: %s\n", curFile->lineno, line); | 2611 | DEBUG2(PARSE, "Parsing line %u: %s\n", curFile->lineno, line); | |
2612 | if (curFile->guardState != GS_NO | 2612 | if (curFile->guardState != GS_NO | |
2613 | && ((curFile->guardState == GS_START && line[0] != '.') | 2613 | && ((curFile->guardState == GS_START && line[0] != '.') | |
2614 | || curFile->guardState == GS_DONE)) | 2614 | || curFile->guardState == GS_DONE)) | |
2615 | curFile->guardState = GS_NO; | 2615 | curFile->guardState = GS_NO; | |
2616 | if (line[0] != '.') | 2616 | if (line[0] != '.') | |
2617 | return line; | 2617 | return line; | |
2618 | 2618 | |||
2619 | condResult = Cond_EvalLine(line); | 2619 | condResult = Cond_EvalLine(line); | |
2620 | if (curFile->guardState == GS_START) { | 2620 | if (curFile->guardState == GS_START) { | |
2621 | Guard *guard; | 2621 | Guard *guard; | |
2622 | if (condResult != CR_ERROR | 2622 | if (condResult != CR_ERROR | |
2623 | && (guard = Cond_ExtractGuard(line)) != NULL) { | 2623 | && (guard = Cond_ExtractGuard(line)) != NULL) { | |
2624 | curFile->guardState = GS_COND; | 2624 | curFile->guardState = GS_COND; | |
2625 | curFile->guard = guard; | 2625 | curFile->guard = guard; | |
2626 | } else | 2626 | } else | |
2627 | curFile->guardState = GS_NO; | 2627 | curFile->guardState = GS_NO; | |
2628 | } | 2628 | } | |
2629 | switch (condResult) { | 2629 | switch (condResult) { | |
2630 | case CR_FALSE: /* May also mean a syntax error. */ | 2630 | case CR_FALSE: /* May also mean a syntax error. */ | |
2631 | if (!SkipIrrelevantBranches()) | 2631 | if (!SkipIrrelevantBranches()) | |
2632 | return NULL; | 2632 | return NULL; | |
2633 | continue; | 2633 | continue; | |
2634 | case CR_TRUE: | 2634 | case CR_TRUE: | |
2635 | continue; | 2635 | continue; | |
2636 | case CR_ERROR: /* Not a conditional line */ | 2636 | case CR_ERROR: /* Not a conditional line */ | |
2637 | if (ParseForLoop(line)) | 2637 | if (ParseForLoop(line)) | |
2638 | continue; | 2638 | continue; | |
2639 | break; | 2639 | break; | |
2640 | } | 2640 | } | |
2641 | return line; | 2641 | return line; | |
2642 | } | 2642 | } | |
2643 | } | 2643 | } | |
2644 | 2644 | |||
2645 | static void | 2645 | static void | |
2646 | FinishDependencyGroup(void) | 2646 | FinishDependencyGroup(void) | |
2647 | { | 2647 | { | |
2648 | GNodeListNode *ln; | 2648 | GNodeListNode *ln; | |
2649 | 2649 | |||
2650 | if (targets == NULL) | 2650 | if (targets == NULL) | |
2651 | return; | 2651 | return; | |
2652 | 2652 | |||
2653 | for (ln = targets->first; ln != NULL; ln = ln->next) { | 2653 | for (ln = targets->first; ln != NULL; ln = ln->next) { |
--- src/usr.bin/make/var.c 2024/04/21 21:59:48 1.1104
+++ src/usr.bin/make/var.c 2024/04/23 22:51:28 1.1105
@@ -1,2008 +1,2001 @@ | @@ -1,2008 +1,2001 @@ | |||
1 | /* $NetBSD: var.c,v 1.1104 2024/04/21 21:59:48 rillig Exp $ */ | 1 | /* $NetBSD: var.c,v 1.1105 2024/04/23 22:51:28 rillig Exp $ */ | |
2 | 2 | |||
3 | /* | 3 | /* | |
4 | * Copyright (c) 1988, 1989, 1990, 1993 | 4 | * Copyright (c) 1988, 1989, 1990, 1993 | |
5 | * The Regents of the University of California. All rights reserved. | 5 | * The Regents of the University of California. 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. | |
15 | * 2. Redistributions in binary form must reproduce the above copyright | 15 | * 2. Redistributions in binary form must reproduce the above copyright | |
16 | * notice, this list of conditions and the following disclaimer in the | 16 | * notice, this list of conditions and the following disclaimer in the | |
17 | * documentation and/or other materials provided with the distribution. | 17 | * documentation and/or other materials provided with the distribution. | |
18 | * 3. Neither the name of the University nor the names of its contributors | 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 | 19 | * may be used to endorse or promote products derived from this software | |
20 | * without specific prior written permission. | 20 | * without specific prior written permission. | |
21 | * | 21 | * | |
22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | 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 | 23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | 25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | 27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | 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 | 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 | 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 | 31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
32 | * SUCH DAMAGE. | 32 | * SUCH DAMAGE. | |
33 | */ | 33 | */ | |
34 | 34 | |||
35 | /* | 35 | /* | |
36 | * Copyright (c) 1989 by Berkeley Softworks | 36 | * Copyright (c) 1989 by Berkeley Softworks | |
37 | * All rights reserved. | 37 | * All rights reserved. | |
38 | * | 38 | * | |
39 | * This code is derived from software contributed to Berkeley by | 39 | * This code is derived from software contributed to Berkeley by | |
40 | * Adam de Boor. | 40 | * Adam de Boor. | |
41 | * | 41 | * | |
42 | * Redistribution and use in source and binary forms, with or without | 42 | * Redistribution and use in source and binary forms, with or without | |
43 | * modification, are permitted provided that the following conditions | 43 | * modification, are permitted provided that the following conditions | |
44 | * are met: | 44 | * are met: | |
45 | * 1. Redistributions of source code must retain the above copyright | 45 | * 1. Redistributions of source code must retain the above copyright | |
46 | * notice, this list of conditions and the following disclaimer. | 46 | * notice, this list of conditions and the following disclaimer. | |
47 | * 2. Redistributions in binary form must reproduce the above copyright | 47 | * 2. Redistributions in binary form must reproduce the above copyright | |
48 | * notice, this list of conditions and the following disclaimer in the | 48 | * notice, this list of conditions and the following disclaimer in the | |
49 | * documentation and/or other materials provided with the distribution. | 49 | * documentation and/or other materials provided with the distribution. | |
50 | * 3. All advertising materials mentioning features or use of this software | 50 | * 3. All advertising materials mentioning features or use of this software | |
51 | * must display the following acknowledgement: | 51 | * must display the following acknowledgement: | |
52 | * This product includes software developed by the University of | 52 | * This product includes software developed by the University of | |
53 | * California, Berkeley and its contributors. | 53 | * California, Berkeley and its contributors. | |
54 | * 4. Neither the name of the University nor the names of its contributors | 54 | * 4. Neither the name of the University nor the names of its contributors | |
55 | * may be used to endorse or promote products derived from this software | 55 | * may be used to endorse or promote products derived from this software | |
56 | * without specific prior written permission. | 56 | * without specific prior written permission. | |
57 | * | 57 | * | |
58 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | 58 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
59 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 59 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
60 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 60 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
61 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | 61 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
62 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 62 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
63 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | 63 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
64 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | 64 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
65 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | 65 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
66 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | 66 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
67 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | 67 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
68 | * SUCH DAMAGE. | 68 | * SUCH DAMAGE. | |
69 | */ | 69 | */ | |
70 | 70 | |||
71 | /* | 71 | /* | |
72 | * Handling of variables and the expressions formed from them. | 72 | * Handling of variables and the expressions formed from them. | |
73 | * | 73 | * | |
74 | * Variables are set using lines of the form VAR=value. Both the variable | 74 | * Variables are set using lines of the form VAR=value. Both the variable | |
75 | * name and the value can contain references to other variables, by using | 75 | * name and the value can contain references to other variables, by using | |
76 | * expressions like ${VAR}, ${VAR:Modifiers}, ${${VARNAME}} or ${VAR:${MODS}}. | 76 | * expressions like ${VAR}, ${VAR:Modifiers}, ${${VARNAME}} or ${VAR:${MODS}}. | |
77 | * | 77 | * | |
78 | * Interface: | 78 | * Interface: | |
79 | * Var_Init Initialize this module. | 79 | * Var_Init Initialize this module. | |
80 | * | 80 | * | |
81 | * Var_End Clean up the module. | 81 | * Var_End Clean up the module. | |
82 | * | 82 | * | |
83 | * Var_Set | 83 | * Var_Set | |
84 | * Var_SetExpand | 84 | * Var_SetExpand Set the value of the variable, creating it if | |
85 | * Set the value of the variable, creating it if | |||
86 | * necessary. | 85 | * necessary. | |
87 | * | 86 | * | |
88 | * Var_Append | 87 | * Var_Append | |
89 | * Var_AppendExpand | 88 | * Var_AppendExpand | |
90 | * Append more characters to the variable, creating it if | 89 | * Append more characters to the variable, creating it if | |
91 | * necessary. A space is placed between the old value and | 90 | * necessary. A space is placed between the old value and | |
92 | * the new one. | 91 | * the new one. | |
93 | * | 92 | * | |
94 | * Var_Exists | 93 | * Var_Exists | |
95 | * Var_ExistsExpand | 94 | * Var_ExistsExpand | |
96 | * See if a variable exists. | 95 | * See if a variable exists. | |
97 | * | 96 | * | |
98 | * Var_Value Return the unexpanded value of a variable, or NULL if | 97 | * Var_Value Return the unexpanded value of a variable, or NULL if | |
99 | * the variable is undefined. | 98 | * the variable is undefined. | |
100 | * | 99 | * | |
101 | * Var_Subst Substitute all expressions in a string. | 100 | * Var_Subst Substitute all expressions in a string. | |
102 | * | 101 | * | |
103 | * Var_Parse Parse an expression such as ${VAR:Mpattern}. | 102 | * Var_Parse Parse an expression such as ${VAR:Mpattern}. | |
104 | * | 103 | * | |
105 | * Var_Delete | 104 | * Var_Delete Delete a variable. | |
106 | * Delete a variable. | |||
107 | * | 105 | * | |
108 | * Var_ReexportVars | 106 | * Var_ReexportVars | |
109 | * Export some or even all variables to the environment | 107 | * Export some or even all variables to the environment | |
110 | * of this process and its child processes. | 108 | * of this process and its child processes. | |
111 | * | 109 | * | |
112 | * Var_Export Export the variable to the environment of this process | 110 | * Var_Export Export the variable to the environment of this process | |
113 | * and its child processes. | 111 | * and its child processes. | |
114 | * | 112 | * | |
115 | * Var_UnExport Don't export the variable anymore. | 113 | * Var_UnExport Don't export the variable anymore. | |
116 | * | 114 | * | |
117 | * Debugging: | 115 | * Debugging: | |
118 | * Var_Stats Print out hashing statistics if in -dh mode. | 116 | * Var_Stats Print out hashing statistics if in -dh mode. | |
119 | * | 117 | * | |
120 | * Var_Dump Print out all variables defined in the given scope. | 118 | * Var_Dump Print out all variables defined in the given scope. | |
121 | * | |||
122 | * XXX: There's a lot of almost duplicate code in these functions that only | |||
123 | * differs in subtle details that are not mentioned in the manual page. | |||
124 | */ | 119 | */ | |
125 | 120 | |||
126 | #include <sys/stat.h> | 121 | #include <sys/stat.h> | |
127 | #include <sys/types.h> | 122 | #include <sys/types.h> | |
128 | #include <regex.h> | 123 | #include <regex.h> | |
129 | #include <errno.h> | 124 | #include <errno.h> | |
130 | #include <inttypes.h> | 125 | #include <inttypes.h> | |
131 | #include <limits.h> | 126 | #include <limits.h> | |
132 | #include <time.h> | 127 | #include <time.h> | |
133 | 128 | |||
134 | #include "make.h" | 129 | #include "make.h" | |
135 | #include "dir.h" | 130 | #include "dir.h" | |
136 | #include "job.h" | 131 | #include "job.h" | |
137 | #include "metachar.h" | 132 | #include "metachar.h" | |
138 | 133 | |||
139 | /* "@(#)var.c 8.3 (Berkeley) 3/19/94" */ | 134 | /* "@(#)var.c 8.3 (Berkeley) 3/19/94" */ | |
140 | MAKE_RCSID("$NetBSD: var.c,v 1.1104 2024/04/21 21:59:48 rillig Exp $"); | 135 | MAKE_RCSID("$NetBSD: var.c,v 1.1105 2024/04/23 22:51:28 rillig Exp $"); | |
141 | 136 | |||
142 | /* | 137 | /* | |
143 | * Variables are defined using one of the VAR=value assignments. Their | 138 | * Variables are defined using one of the VAR=value assignments. Their | |
144 | * value can be queried by expressions such as $V, ${VAR}, or with modifiers | 139 | * value can be queried by expressions such as $V, ${VAR}, or with modifiers | |
145 | * such as ${VAR:S,from,to,g:Q}. | 140 | * such as ${VAR:S,from,to,g:Q}. | |
146 | * | 141 | * | |
147 | * There are 3 kinds of variables: scope variables, environment variables, | 142 | * There are 3 kinds of variables: scope variables, environment variables, | |
148 | * undefined variables. | 143 | * undefined variables. | |
149 | * | 144 | * | |
150 | * Scope variables are stored in a GNode.scope. The only way to undefine | 145 | * Scope variables are stored in GNode.vars. The only way to undefine | |
151 | * a scope variable is using the .undef directive. In particular, it must | 146 | * a scope variable is using the .undef directive. In particular, it must | |
152 | * not be possible to undefine a variable during the evaluation of an | 147 | * not be possible to undefine a variable during the evaluation of an | |
153 | * expression, or Var.name might point nowhere. (There is another, | 148 | * expression, or Var.name might point nowhere. (There is another, | |
154 | * unintended way to undefine a scope variable, see varmod-loop-delete.mk.) | 149 | * unintended way to undefine a scope variable, see varmod-loop-delete.mk.) | |
155 | * | 150 | * | |
156 | * Environment variables are short-lived. They are returned by VarFind, and | 151 | * Environment variables are short-lived. They are returned by VarFind, and | |
157 | * after using them, they must be freed using VarFreeShortLived. | 152 | * after using them, they must be freed using VarFreeShortLived. | |
158 | * | 153 | * | |
159 | * Undefined variables occur during evaluation of expressions such | 154 | * Undefined variables occur during evaluation of expressions such | |
160 | * as ${UNDEF:Ufallback} in Var_Parse and ApplyModifiers. | 155 | * as ${UNDEF:Ufallback} in Var_Parse and ApplyModifiers. | |
161 | */ | 156 | */ | |
162 | typedef struct Var { | 157 | typedef struct Var { | |
163 | /* | 158 | /* | |
164 | * The name of the variable, once set, doesn't change anymore. | 159 | * The name of the variable, once set, doesn't change anymore. | |
165 | * For scope variables, it aliases the corresponding HashEntry name. | 160 | * For scope variables, it aliases the corresponding HashEntry name. | |
166 | * For environment and undefined variables, it is allocated. | 161 | * For environment and undefined variables, it is allocated. | |
167 | */ | 162 | */ | |
168 | FStr name; | 163 | FStr name; | |
169 | 164 | |||
170 | /* The unexpanded value of the variable. */ | 165 | /* The unexpanded value of the variable. */ | |
171 | Buffer val; | 166 | Buffer val; | |
172 | 167 | |||
173 | /* The variable came from the command line. */ | 168 | /* The variable came from the command line. */ | |
174 | bool fromCmd:1; | 169 | bool fromCmd:1; | |
175 | 170 | |||
176 | /* | 171 | /* | |
177 | * The variable is short-lived. | 172 | * The variable is short-lived. | |
178 | * These variables are not registered in any GNode, therefore they | 173 | * These variables are not registered in any GNode, therefore they | |
179 | * must be freed after use. | 174 | * must be freed after use. | |
180 | */ | 175 | */ | |
181 | bool shortLived:1; | 176 | bool shortLived:1; | |
182 | 177 | |||
183 | /* | 178 | /* | |
184 | * The variable comes from the environment. | 179 | * The variable comes from the environment. | |
185 | * Appending to its value moves the variable to the global scope. | 180 | * Appending to its value depends on the scope, see var-op-append.mk. | |
186 | */ | 181 | */ | |
187 | bool fromEnvironment:1; | 182 | bool fromEnvironment:1; | |
188 | 183 | |||
189 | /* | 184 | /* | |
190 | * The variable value cannot be changed anymore, and the variable | 185 | * The variable value cannot be changed anymore, and the variable | |
191 | * cannot be deleted. Any attempts to do so are silently ignored, | 186 | * cannot be deleted. Any attempts to do so are silently ignored, | |
192 | * they are logged with -dv though. | 187 | * they are logged with -dv though. | |
193 | * Use .[NO]READONLY: to adjust. | 188 | * Use .[NO]READONLY: to adjust. | |
194 | * | 189 | * | |
195 | * See VAR_SET_READONLY. | 190 | * See VAR_SET_READONLY. | |
196 | */ | 191 | */ | |
197 | bool readOnly:1; | 192 | bool readOnly:1; | |
198 | 193 | |||
199 | /* | 194 | /* | |
200 | * The variable is currently being accessed by Var_Parse or Var_Subst. | 195 | * The variable is currently being accessed by Var_Parse or Var_Subst. | |
201 | * This temporary marker is used to avoid endless recursion. | 196 | * This temporary marker is used to avoid endless recursion. | |
202 | */ | 197 | */ | |
203 | bool inUse:1; | 198 | bool inUse:1; | |
204 | 199 | |||
205 | /* | 200 | /* | |
206 | * The variable is exported to the environment, to be used by child | 201 | * The variable is exported to the environment, to be used by child | |
207 | * processes. | 202 | * processes. | |
208 | */ | 203 | */ | |
209 | bool exported:1; | 204 | bool exported:1; | |
210 | 205 | |||
211 | /* | 206 | /* | |
212 | * At the point where this variable was exported, it contained an | 207 | * At the point where this variable was exported, it contained an | |
213 | * unresolved reference to another variable. Before any child | 208 | * unresolved reference to another variable. Before any child | |
214 | * process is started, it needs to be actually exported, resolving | 209 | * process is started, it needs to be actually exported, resolving | |
215 | * the referenced variable just in time. | 210 | * the referenced variable just in time. | |
216 | */ | 211 | */ | |
217 | bool reexport:1; | 212 | bool reexport:1; | |
218 | } Var; | 213 | } Var; | |
219 | 214 | |||
220 | /* | 215 | /* | |
221 | * Exporting variables is expensive and may leak memory, so skip it if we | 216 | * Exporting variables is expensive and may leak memory, so skip it if we | |
222 | * can. | 217 | * can. | |
223 | */ | 218 | */ | |
224 | typedef enum VarExportedMode { | 219 | typedef enum VarExportedMode { | |
225 | VAR_EXPORTED_NONE, | 220 | VAR_EXPORTED_NONE, | |
226 | VAR_EXPORTED_SOME, | 221 | VAR_EXPORTED_SOME, | |
227 | VAR_EXPORTED_ALL | 222 | VAR_EXPORTED_ALL | |
228 | } VarExportedMode; | 223 | } VarExportedMode; | |
229 | 224 | |||
230 | typedef enum UnexportWhat { | 225 | typedef enum UnexportWhat { | |
231 | /* Unexport the variables given by name. */ | 226 | /* Unexport the variables given by name. */ | |
232 | UNEXPORT_NAMED, | 227 | UNEXPORT_NAMED, | |
233 | /* | 228 | /* | |
234 | * Unexport all globals previously exported, but keep the environment | 229 | * Unexport all globals previously exported, but keep the environment | |
235 | * inherited from the parent. | 230 | * inherited from the parent. | |
236 | */ | 231 | */ | |
237 | UNEXPORT_ALL, | 232 | UNEXPORT_ALL, | |
238 | /* | 233 | /* | |
239 | * Unexport all globals previously exported and clear the environment | 234 | * Unexport all globals previously exported and clear the environment | |
240 | * inherited from the parent. | 235 | * inherited from the parent. | |
241 | */ | 236 | */ | |
242 | UNEXPORT_ENV | 237 | UNEXPORT_ENV | |
243 | } UnexportWhat; | 238 | } UnexportWhat; | |
244 | 239 | |||
245 | /* Flags for pattern matching in the :S and :C modifiers */ | 240 | /* Flags for pattern matching in the :S and :C modifiers */ | |
246 | typedef struct PatternFlags { | 241 | typedef struct PatternFlags { | |
247 | bool subGlobal:1; /* 'g': replace as often as possible */ | 242 | bool subGlobal:1; /* 'g': replace as often as possible */ | |
248 | bool subOnce:1; /* '1': replace only once */ | 243 | bool subOnce:1; /* '1': replace only once */ | |
249 | bool anchorStart:1; /* '^': match only at start of word */ | 244 | bool anchorStart:1; /* '^': match only at start of word */ | |
250 | bool anchorEnd:1; /* '$': match only at end of word */ | 245 | bool anchorEnd:1; /* '$': match only at end of word */ | |
251 | } PatternFlags; | 246 | } PatternFlags; | |
252 | 247 | |||
253 | /* SepBuf builds a string from words interleaved with separators. */ | 248 | /* SepBuf builds a string from words interleaved with separators. */ | |
254 | typedef struct SepBuf { | 249 | typedef struct SepBuf { | |
255 | Buffer buf; | 250 | Buffer buf; | |
256 | bool needSep; | 251 | bool needSep; | |
257 | /* Usually ' ', but see the ':ts' modifier. */ | 252 | /* Usually ' ', but see the ':ts' modifier. */ | |
258 | char sep; | 253 | char sep; | |
259 | } SepBuf; | 254 | } SepBuf; | |
260 | 255 | |||
261 | typedef struct { | 256 | typedef struct { | |
262 | const char *target; | 257 | const char *target; | |
263 | const char *varname; | 258 | const char *varname; | |
264 | const char *expr; | 259 | const char *expr; | |
265 | } EvalStackElement; | 260 | } EvalStackElement; | |
266 | 261 | |||
267 | typedef struct { | 262 | typedef struct { | |
268 | EvalStackElement *elems; | 263 | EvalStackElement *elems; | |
269 | size_t len; | 264 | size_t len; | |
270 | size_t cap; | 265 | size_t cap; | |
271 | Buffer details; | 266 | Buffer details; | |
272 | } EvalStack; | 267 | } EvalStack; | |
273 | 268 | |||
274 | /* Whether we have replaced the original environ (which we cannot free). */ | 269 | /* Whether we have replaced the original environ (which we cannot free). */ | |
275 | char **savedEnv = NULL; | 270 | char **savedEnv = NULL; | |
276 | 271 | |||
277 | /* | 272 | /* | |
278 | * Special return value for Var_Parse, indicating a parse error. It may be | 273 | * Special return value for Var_Parse, indicating a parse error. It may be | |
279 | * caused by an undefined variable, a syntax error in a modifier or | 274 | * caused by an undefined variable, a syntax error in a modifier or | |
280 | * something entirely different. | 275 | * something entirely different. | |
281 | */ | 276 | */ | |
282 | char var_Error[] = ""; | 277 | char var_Error[] = ""; | |
283 | 278 | |||
284 | /* | 279 | /* | |
285 | * Special return value for Var_Parse, indicating an undefined variable in | 280 | * Special return value for Var_Parse, indicating an undefined variable in | |
286 | * a case where VARE_UNDEFERR is not set. This undefined variable is | 281 | * a case where VARE_UNDEFERR is not set. This undefined variable is | |
287 | * typically a dynamic variable such as ${.TARGET}, whose expansion needs to | 282 | * typically a dynamic variable such as ${.TARGET}, whose expansion needs to | |
288 | * be deferred until it is defined in an actual target. | 283 | * be deferred until it is defined in an actual target. | |
289 | * | 284 | * | |
290 | * See VARE_EVAL_KEEP_UNDEF. | 285 | * See VARE_EVAL_KEEP_UNDEF. | |
291 | */ | 286 | */ | |
292 | static char varUndefined[] = ""; | 287 | static char varUndefined[] = ""; | |
293 | 288 | |||
294 | /* | 289 | /* | |
295 | * Traditionally this make consumed $$ during := like any other expansion. | 290 | * Traditionally this make consumed $$ during := like any other expansion. | |
296 | * Other make's do not, and this make follows straight since 2016-01-09. | 291 | * Other make's do not, and this make follows straight since 2016-01-09. | |
297 | * | 292 | * | |
298 | * This knob allows controlling the behavior: | 293 | * This knob allows controlling the behavior: | |
299 | * false to consume $$ during := assignment. | 294 | * false to consume $$ during := assignment. | |
300 | * true to preserve $$ during := assignment. | 295 | * true to preserve $$ during := assignment. | |
301 | */ | 296 | */ | |
302 | #define MAKE_SAVE_DOLLARS ".MAKE.SAVE_DOLLARS" | 297 | #define MAKE_SAVE_DOLLARS ".MAKE.SAVE_DOLLARS" | |
303 | static bool save_dollars = true; | 298 | static bool save_dollars = true; | |
304 | 299 | |||
305 | /* | 300 | /* | |
306 | * A scope collects variable names and their values. | 301 | * A scope collects variable names and their values. | |
307 | * | 302 | * | |
308 | * The main scope is SCOPE_GLOBAL, which contains the variables that are set | 303 | * The main scope is SCOPE_GLOBAL, which contains the variables that are set | |
309 | * in the makefiles. SCOPE_INTERNAL acts as a fallback for SCOPE_GLOBAL and | 304 | * in the makefiles. SCOPE_INTERNAL acts as a fallback for SCOPE_GLOBAL and | |
310 | * contains some internal make variables. These internal variables can thus | 305 | * contains some internal make variables. These internal variables can thus | |
311 | * be overridden, they can also be restored by undefining the overriding | 306 | * be overridden, they can also be restored by undefining the overriding | |
312 | * variable. | 307 | * variable. | |
313 | * | 308 | * | |
314 | * SCOPE_CMDLINE contains variables from the command line arguments. These | 309 | * SCOPE_CMDLINE contains variables from the command line arguments. These | |
315 | * override variables from SCOPE_GLOBAL. | 310 | * override variables from SCOPE_GLOBAL. | |
316 | * | 311 | * | |
317 | * There is no scope for environment variables, these are generated on-the-fly | 312 | * There is no scope for environment variables, these are generated on-the-fly | |
318 | * whenever they are referenced. | 313 | * whenever they are referenced. | |
319 | * | 314 | * | |
320 | * Each target has its own scope, containing the 7 target-local variables | 315 | * Each target has its own scope, containing the 7 target-local variables | |
321 | * .TARGET, .ALLSRC, etc. Variables set on dependency lines also go in | 316 | * .TARGET, .ALLSRC, etc. Variables set on dependency lines also go in | |
322 | * this scope. | 317 | * this scope. | |
323 | */ | 318 | */ | |
324 | 319 | |||
325 | GNode *SCOPE_CMDLINE; | 320 | GNode *SCOPE_CMDLINE; | |
326 | GNode *SCOPE_GLOBAL; | 321 | GNode *SCOPE_GLOBAL; | |
327 | GNode *SCOPE_INTERNAL; | 322 | GNode *SCOPE_INTERNAL; | |
328 | 323 | |||
329 | static VarExportedMode var_exportedVars = VAR_EXPORTED_NONE; | 324 | static VarExportedMode var_exportedVars = VAR_EXPORTED_NONE; | |
330 | 325 | |||
331 | static const char VarEvalMode_Name[][32] = { | 326 | static const char VarEvalMode_Name[][32] = { | |
332 | "parse-only", | 327 | "parse-only", | |
333 | "parse-balanced", | 328 | "parse-balanced", | |
334 | "eval", | 329 | "eval", | |
335 | "eval-defined", | 330 | "eval-defined", | |
336 | "eval-keep-dollar", | 331 | "eval-keep-dollar", | |
337 | "eval-keep-undefined", | 332 | "eval-keep-undefined", | |
338 | "eval-keep-dollar-and-undefined", | 333 | "eval-keep-dollar-and-undefined", | |
339 | }; | 334 | }; | |
340 | 335 | |||
341 | static EvalStack evalStack; | 336 | static EvalStack evalStack; | |
342 | 337 | |||
343 | 338 | |||
344 | void | 339 | void | |
345 | EvalStack_Push(const char *target, const char *expr, const char *varname) | 340 | EvalStack_Push(const char *target, const char *expr, const char *varname) | |
346 | { | 341 | { | |
347 | if (evalStack.len >= evalStack.cap) { | 342 | if (evalStack.len >= evalStack.cap) { | |
348 | evalStack.cap = 16 + 2 * evalStack.cap; | 343 | evalStack.cap = 16 + 2 * evalStack.cap; | |
349 | evalStack.elems = bmake_realloc(evalStack.elems, | 344 | evalStack.elems = bmake_realloc(evalStack.elems, | |
350 | evalStack.cap * sizeof(*evalStack.elems)); | 345 | evalStack.cap * sizeof(*evalStack.elems)); | |
351 | } | 346 | } | |
352 | evalStack.elems[evalStack.len].target = target; | 347 | evalStack.elems[evalStack.len].target = target; | |
353 | evalStack.elems[evalStack.len].expr = expr; | 348 | evalStack.elems[evalStack.len].expr = expr; | |
354 | evalStack.elems[evalStack.len].varname = varname; | 349 | evalStack.elems[evalStack.len].varname = varname; | |
355 | evalStack.len++; | 350 | evalStack.len++; | |
356 | } | 351 | } | |
357 | 352 | |||
358 | void | 353 | void | |
359 | EvalStack_Pop(void) | 354 | EvalStack_Pop(void) | |
360 | { | 355 | { | |
361 | assert(evalStack.len > 0); | 356 | assert(evalStack.len > 0); | |
362 | evalStack.len--; | 357 | evalStack.len--; | |
363 | } | 358 | } | |
364 | 359 | |||
365 | const char * | 360 | const char * | |
366 | EvalStack_Details(void) | 361 | EvalStack_Details(void) | |
367 | { | 362 | { | |
368 | size_t i; | 363 | size_t i; | |
369 | Buffer *buf = &evalStack.details; | 364 | Buffer *buf = &evalStack.details; | |
370 | 365 | |||
371 | 366 | |||
372 | buf->len = 0; | 367 | buf->len = 0; | |
373 | for (i = 0; i < evalStack.len; i++) { | 368 | for (i = 0; i < evalStack.len; i++) { | |
374 | EvalStackElement *elem = evalStack.elems + i; | 369 | EvalStackElement *elem = evalStack.elems + i; | |
375 | if (elem->target != NULL) { | 370 | if (elem->target != NULL) { | |
376 | Buf_AddStr(buf, "in target \""); | 371 | Buf_AddStr(buf, "in target \""); | |
377 | Buf_AddStr(buf, elem->target); | 372 | Buf_AddStr(buf, elem->target); | |
378 | Buf_AddStr(buf, "\": "); | 373 | Buf_AddStr(buf, "\": "); | |
379 | } | 374 | } | |
380 | if (elem->expr != NULL) { | 375 | if (elem->expr != NULL) { | |
381 | Buf_AddStr(buf, "while evaluating \""); | 376 | Buf_AddStr(buf, "while evaluating \""); | |
382 | Buf_AddStr(buf, elem->expr); | 377 | Buf_AddStr(buf, elem->expr); | |
383 | Buf_AddStr(buf, "\": "); | 378 | Buf_AddStr(buf, "\": "); | |
384 | } | 379 | } | |
385 | if (elem->varname != NULL) { | 380 | if (elem->varname != NULL) { | |
386 | Buf_AddStr(buf, "while evaluating variable \""); | 381 | Buf_AddStr(buf, "while evaluating variable \""); | |
387 | Buf_AddStr(buf, elem->varname); | 382 | Buf_AddStr(buf, elem->varname); | |
388 | Buf_AddStr(buf, "\": "); | 383 | Buf_AddStr(buf, "\": "); | |
389 | } | 384 | } | |
390 | } | 385 | } | |
391 | return buf->len > 0 ? buf->data : ""; | 386 | return buf->len > 0 ? buf->data : ""; | |
392 | } | 387 | } | |
393 | 388 | |||
394 | static Var * | 389 | static Var * | |
395 | VarNew(FStr name, const char *value, | 390 | VarNew(FStr name, const char *value, | |
396 | bool shortLived, bool fromEnvironment, bool readOnly) | 391 | bool shortLived, bool fromEnvironment, bool readOnly) | |
397 | { | 392 | { | |
398 | size_t value_len = strlen(value); | 393 | size_t value_len = strlen(value); | |
399 | Var *var = bmake_malloc(sizeof *var); | 394 | Var *var = bmake_malloc(sizeof *var); | |
400 | var->name = name; | 395 | var->name = name; | |
401 | Buf_InitSize(&var->val, value_len + 1); | 396 | Buf_InitSize(&var->val, value_len + 1); | |
402 | Buf_AddBytes(&var->val, value, value_len); | 397 | Buf_AddBytes(&var->val, value, value_len); | |
403 | var->fromCmd = false; | 398 | var->fromCmd = false; | |
404 | var->shortLived = shortLived; | 399 | var->shortLived = shortLived; | |
405 | var->fromEnvironment = fromEnvironment; | 400 | var->fromEnvironment = fromEnvironment; | |
406 | var->readOnly = readOnly; | 401 | var->readOnly = readOnly; | |
407 | var->inUse = false; | 402 | var->inUse = false; | |
408 | var->exported = false; | 403 | var->exported = false; | |
409 | var->reexport = false; | 404 | var->reexport = false; | |
410 | return var; | 405 | return var; | |
411 | } | 406 | } | |
412 | 407 | |||
413 | static Substring | 408 | static Substring | |
414 | CanonicalVarname(Substring name) | 409 | CanonicalVarname(Substring name) | |
415 | { | 410 | { | |
416 | 411 | |||
417 | if (!(Substring_Length(name) > 0 && name.start[0] == '.')) | 412 | if (!(Substring_Length(name) > 0 && name.start[0] == '.')) | |
418 | return name; | 413 | return name; | |
419 | 414 | |||
420 | if (Substring_Equals(name, ".ALLSRC")) | 415 | if (Substring_Equals(name, ".ALLSRC")) | |
421 | return Substring_InitStr(ALLSRC); | 416 | return Substring_InitStr(ALLSRC); | |
422 | if (Substring_Equals(name, ".ARCHIVE")) | 417 | if (Substring_Equals(name, ".ARCHIVE")) | |
423 | return Substring_InitStr(ARCHIVE); | 418 | return Substring_InitStr(ARCHIVE); | |
424 | if (Substring_Equals(name, ".IMPSRC")) | 419 | if (Substring_Equals(name, ".IMPSRC")) | |
425 | return Substring_InitStr(IMPSRC); | 420 | return Substring_InitStr(IMPSRC); | |
426 | if (Substring_Equals(name, ".MEMBER")) | 421 | if (Substring_Equals(name, ".MEMBER")) | |
427 | return Substring_InitStr(MEMBER); | 422 | return Substring_InitStr(MEMBER); | |
428 | if (Substring_Equals(name, ".OODATE")) | 423 | if (Substring_Equals(name, ".OODATE")) | |
429 | return Substring_InitStr(OODATE); | 424 | return Substring_InitStr(OODATE); | |
430 | if (Substring_Equals(name, ".PREFIX")) | 425 | if (Substring_Equals(name, ".PREFIX")) | |
431 | return Substring_InitStr(PREFIX); | 426 | return Substring_InitStr(PREFIX); | |
432 | if (Substring_Equals(name, ".TARGET")) | 427 | if (Substring_Equals(name, ".TARGET")) | |
433 | return Substring_InitStr(TARGET); | 428 | return Substring_InitStr(TARGET); | |
434 | 429 | |||
435 | /* GNU make has an additional alias $^ == ${.ALLSRC}. */ | 430 | /* GNU make has an additional alias $^ == ${.ALLSRC}. */ | |
436 | 431 | |||
437 | if (Substring_Equals(name, ".SHELL") && shellPath == NULL) | 432 | if (Substring_Equals(name, ".SHELL") && shellPath == NULL) | |
438 | Shell_Init(); | 433 | Shell_Init(); | |
439 | 434 | |||
440 | return name; | 435 | return name; | |
441 | } | 436 | } | |
442 | 437 | |||
443 | static Var * | 438 | static Var * | |
444 | GNode_FindVar(GNode *scope, Substring varname, unsigned int hash) | 439 | GNode_FindVar(GNode *scope, Substring varname, unsigned int hash) | |
445 | { | 440 | { | |
446 | return HashTable_FindValueBySubstringHash(&scope->vars, varname, hash); | 441 | return HashTable_FindValueBySubstringHash(&scope->vars, varname, hash); | |
447 | } | 442 | } | |
448 | 443 | |||
449 | /* | 444 | /* | |
450 | * Find the variable in the scope, and maybe in other scopes as well. | 445 | * Find the variable in the scope, and maybe in other scopes as well. | |
451 | * | 446 | * | |
452 | * Input: | 447 | * Input: | |
453 | * name name to find, is not expanded any further | 448 | * name name to find, is not expanded any further | |
454 | * scope scope in which to look first | 449 | * scope scope in which to look first | |
455 | * elsewhere true to look in other scopes as well | 450 | * elsewhere true to look in other scopes as well | |
456 | * | 451 | * | |
457 | * Results: | 452 | * Results: | |
458 | * The found variable, or NULL if the variable does not exist. | 453 | * The found variable, or NULL if the variable does not exist. | |
459 | * If the variable is short-lived (such as environment variables), it | 454 | * If the variable is short-lived (such as environment variables), it | |
460 | * must be freed using VarFreeShortLived after use. | 455 | * must be freed using VarFreeShortLived after use. | |
461 | */ | 456 | */ | |
462 | static Var * | 457 | static Var * | |
463 | VarFindSubstring(Substring name, GNode *scope, bool elsewhere) | 458 | VarFindSubstring(Substring name, GNode *scope, bool elsewhere) | |
464 | { | 459 | { | |
465 | Var *var; | 460 | Var *var; | |
466 | unsigned int nameHash; | 461 | unsigned int nameHash; | |
467 | 462 | |||
468 | /* Replace '.TARGET' with '@', likewise for other local variables. */ | 463 | /* Replace '.TARGET' with '@', likewise for other local variables. */ | |
469 | name = CanonicalVarname(name); | 464 | name = CanonicalVarname(name); | |
470 | nameHash = Hash_Substring(name); | 465 | nameHash = Hash_Substring(name); | |
471 | 466 | |||
472 | var = GNode_FindVar(scope, name, nameHash); | 467 | var = GNode_FindVar(scope, name, nameHash); | |
473 | if (!elsewhere) | 468 | if (!elsewhere) | |
474 | return var; | 469 | return var; | |
475 | 470 | |||
476 | if (var == NULL && scope != SCOPE_CMDLINE) | 471 | if (var == NULL && scope != SCOPE_CMDLINE) | |
477 | var = GNode_FindVar(SCOPE_CMDLINE, name, nameHash); | 472 | var = GNode_FindVar(SCOPE_CMDLINE, name, nameHash); | |
478 | 473 | |||
479 | if (!opts.checkEnvFirst && var == NULL && scope != SCOPE_GLOBAL) { | 474 | if (!opts.checkEnvFirst && var == NULL && scope != SCOPE_GLOBAL) { | |
480 | var = GNode_FindVar(SCOPE_GLOBAL, name, nameHash); | 475 | var = GNode_FindVar(SCOPE_GLOBAL, name, nameHash); | |
481 | if (var == NULL && scope != SCOPE_INTERNAL) { | 476 | if (var == NULL && scope != SCOPE_INTERNAL) { | |
482 | /* SCOPE_INTERNAL is subordinate to SCOPE_GLOBAL */ | 477 | /* SCOPE_INTERNAL is subordinate to SCOPE_GLOBAL */ | |
483 | var = GNode_FindVar(SCOPE_INTERNAL, name, nameHash); | 478 | var = GNode_FindVar(SCOPE_INTERNAL, name, nameHash); | |
484 | } | 479 | } | |
485 | } | 480 | } | |
486 | 481 | |||
487 | if (var == NULL) { | 482 | if (var == NULL) { | |
488 | FStr envName; | 483 | FStr envName = Substring_Str(name); | |
489 | const char *envValue; | 484 | const char *envValue = getenv(envName.str); | |
490 | ||||
491 | envName = Substring_Str(name); | |||
492 | envValue = getenv(envName.str); | |||
493 | if (envValue != NULL) | 485 | if (envValue != NULL) | |
494 | return VarNew(envName, envValue, true, true, false); | 486 | return VarNew(envName, envValue, true, true, false); | |
495 | FStr_Done(&envName); | 487 | FStr_Done(&envName); | |
496 | 488 | |||
497 | if (opts.checkEnvFirst && scope != SCOPE_GLOBAL) { | 489 | if (opts.checkEnvFirst && scope != SCOPE_GLOBAL) { | |
498 | var = GNode_FindVar(SCOPE_GLOBAL, name, nameHash); | 490 | var = GNode_FindVar(SCOPE_GLOBAL, name, nameHash); | |
499 | if (var == NULL && scope != SCOPE_INTERNAL) | 491 | if (var == NULL && scope != SCOPE_INTERNAL) | |
500 | var = GNode_FindVar(SCOPE_INTERNAL, name, | 492 | var = GNode_FindVar(SCOPE_INTERNAL, name, | |
501 | nameHash); | 493 | nameHash); | |
502 | return var; | 494 | return var; | |
503 | } | 495 | } | |
504 | 496 | |||
505 | return NULL; | 497 | return NULL; | |
506 | } | 498 | } | |
507 | 499 | |||
508 | return var; | 500 | return var; | |
509 | } | 501 | } | |
510 | 502 | |||
511 | static Var * | 503 | static Var * | |
512 | VarFind(const char *name, GNode *scope, bool elsewhere) | 504 | VarFind(const char *name, GNode *scope, bool elsewhere) | |
513 | { | 505 | { | |
514 | return VarFindSubstring(Substring_InitStr(name), scope, elsewhere); | 506 | return VarFindSubstring(Substring_InitStr(name), scope, elsewhere); | |
515 | } | 507 | } | |
516 | 508 | |||
517 | /* If the variable is short-lived, free it, including its value. */ | 509 | /* If the variable is short-lived, free it, including its value. */ | |
518 | static void | 510 | static void | |
519 | VarFreeShortLived(Var *v) | 511 | VarFreeShortLived(Var *v) | |
520 | { | 512 | { | |
521 | if (!v->shortLived) | 513 | if (!v->shortLived) | |
522 | return; | 514 | return; | |
523 | 515 | |||
524 | FStr_Done(&v->name); | 516 | FStr_Done(&v->name); | |
525 | Buf_Done(&v->val); | 517 | Buf_Done(&v->val); | |
526 | free(v); | 518 | free(v); | |
527 | } | 519 | } | |
528 | 520 | |||
529 | static const char * | 521 | static const char * | |
530 | ValueDescription(const char *value) | 522 | ValueDescription(const char *value) | |
531 | { | 523 | { | |
532 | if (value[0] == '\0') | 524 | if (value[0] == '\0') | |
533 | return "# (empty)"; | 525 | return "# (empty)"; | |
534 | if (ch_isspace(value[strlen(value) - 1])) | 526 | if (ch_isspace(value[strlen(value) - 1])) | |
535 | return "# (ends with space)"; | 527 | return "# (ends with space)"; | |
536 | return ""; | 528 | return ""; | |
537 | } | 529 | } | |
538 | 530 | |||
539 | /* Add a new variable of the given name and value to the given scope. */ | 531 | /* Add a new variable of the given name and value to the given scope. */ | |
540 | static Var * | 532 | static Var * | |
541 | VarAdd(const char *name, const char *value, GNode *scope, VarSetFlags flags) | 533 | VarAdd(const char *name, const char *value, GNode *scope, VarSetFlags flags) | |
542 | { | 534 | { | |
543 | HashEntry *he = HashTable_CreateEntry(&scope->vars, name, NULL); | 535 | HashEntry *he = HashTable_CreateEntry(&scope->vars, name, NULL); | |
544 | Var *v = VarNew(FStr_InitRefer(/* aliased to */ he->key), value, | 536 | Var *v = VarNew(FStr_InitRefer(/* aliased to */ he->key), value, | |
545 | false, false, (flags & VAR_SET_READONLY) != 0); | 537 | false, false, (flags & VAR_SET_READONLY) != 0); | |
546 | HashEntry_Set(he, v); | 538 | HashEntry_Set(he, v); | |
547 | DEBUG4(VAR, "%s: %s = %s%s\n", | 539 | DEBUG4(VAR, "%s: %s = %s%s\n", | |
548 | scope->name, name, value, ValueDescription(value)); | 540 | scope->name, name, value, ValueDescription(value)); | |
549 | return v; | 541 | return v; | |
550 | } | 542 | } | |
551 | 543 | |||
552 | /* | 544 | /* | |
553 | * Remove a variable from a scope, freeing all related memory as well. | 545 | * Remove a variable from a scope, freeing all related memory as well. | |
554 | * The variable name is kept as-is, it is not expanded. | 546 | * The variable name is kept as-is, it is not expanded. | |
555 | */ | 547 | */ | |
556 | void | 548 | void | |
557 | Var_Delete(GNode *scope, const char *varname) | 549 | Var_Delete(GNode *scope, const char *varname) | |
558 | { | 550 | { | |
559 | HashEntry *he = HashTable_FindEntry(&scope->vars, varname); | 551 | HashEntry *he = HashTable_FindEntry(&scope->vars, varname); | |
560 | Var *v; | 552 | Var *v; | |
561 | 553 | |||
562 | if (he == NULL) { | 554 | if (he == NULL) { | |
563 | DEBUG2(VAR, "%s: ignoring delete '%s' as it is not found\n", | 555 | DEBUG2(VAR, "%s: ignoring delete '%s' as it is not found\n", | |
564 | scope->name, varname); | 556 | scope->name, varname); | |
565 | return; | 557 | return; | |
566 | } | 558 | } | |
567 | 559 | |||
568 | v = he->value; | 560 | v = he->value; | |
569 | if (v->readOnly) { | 561 | if (v->readOnly) { | |
570 | DEBUG2(VAR, "%s: ignoring delete '%s' as it is read-only\n", | 562 | DEBUG2(VAR, "%s: ignoring delete '%s' as it is read-only\n", | |
571 | scope->name, varname); | 563 | scope->name, varname); | |
572 | return; | 564 | return; | |
573 | } | 565 | } | |
574 | if (v->inUse) { | 566 | if (v->inUse) { | |
575 | Parse_Error(PARSE_FATAL, | 567 | Parse_Error(PARSE_FATAL, | |
576 | "Cannot delete variable \"%s\" while it is used", | 568 | "Cannot delete variable \"%s\" while it is used", | |
577 | v->name.str); | 569 | v->name.str); | |
578 | return; | 570 | return; | |
579 | } | 571 | } | |
580 | 572 | |||
581 | DEBUG2(VAR, "%s: delete %s\n", scope->name, varname); | 573 | DEBUG2(VAR, "%s: delete %s\n", scope->name, varname); | |
582 | if (v->exported) | 574 | if (v->exported) | |
583 | unsetenv(v->name.str); | 575 | unsetenv(v->name.str); | |
584 | if (strcmp(v->name.str, ".MAKE.EXPORTED") == 0) | 576 | if (strcmp(v->name.str, ".MAKE.EXPORTED") == 0) | |
585 | var_exportedVars = VAR_EXPORTED_NONE; | 577 | var_exportedVars = VAR_EXPORTED_NONE; | |
586 | 578 | |||
587 | assert(v->name.freeIt == NULL); | 579 | assert(v->name.freeIt == NULL); | |
588 | HashTable_DeleteEntry(&scope->vars, he); | 580 | HashTable_DeleteEntry(&scope->vars, he); | |
589 | Buf_Done(&v->val); | 581 | Buf_Done(&v->val); | |
590 | free(v); | 582 | free(v); | |
591 | } | 583 | } | |
592 | 584 | |||
593 | /* | 585 | /* | |
594 | * Undefine one or more variables from the global scope. | 586 | * Undefine one or more variables from the global scope. | |
595 | * The argument is expanded exactly once and then split into words. | 587 | * The argument is expanded exactly once and then split into words. | |
596 | */ | 588 | */ | |
597 | void | 589 | void | |
598 | Var_Undef(const char *arg) | 590 | Var_Undef(const char *arg) | |
599 | { | 591 | { | |
600 | char *expanded; | 592 | char *expanded; | |
601 | Words varnames; | 593 | Words varnames; | |
602 | size_t i; | 594 | size_t i; | |
603 | 595 | |||
604 | if (arg[0] == '\0') { | 596 | if (arg[0] == '\0') { | |
605 | Parse_Error(PARSE_FATAL, | 597 | Parse_Error(PARSE_FATAL, | |
606 | "The .undef directive requires an argument"); | 598 | "The .undef directive requires an argument"); | |
607 | return; | 599 | return; | |
608 | } | 600 | } | |
609 | 601 | |||
610 | expanded = Var_Subst(arg, SCOPE_GLOBAL, VARE_WANTRES); | 602 | expanded = Var_Subst(arg, SCOPE_GLOBAL, VARE_WANTRES); | |
611 | if (expanded == var_Error) { | 603 | if (expanded == var_Error) { | |
612 | /* TODO: Make this part of the code reachable. */ | 604 | /* TODO: Make this part of the code reachable. */ | |
613 | Parse_Error(PARSE_FATAL, | 605 | Parse_Error(PARSE_FATAL, | |
614 | "Error in variable names to be undefined"); | 606 | "Error in variable names to be undefined"); | |
615 | return; | 607 | return; | |
616 | } | 608 | } | |
617 | 609 | |||
618 | varnames = Str_Words(expanded, false); | 610 | varnames = Str_Words(expanded, false); | |
619 | if (varnames.len == 1 && varnames.words[0][0] == '\0') | 611 | if (varnames.len == 1 && varnames.words[0][0] == '\0') | |
620 | varnames.len = 0; | 612 | varnames.len = 0; | |
621 | 613 | |||
622 | for (i = 0; i < varnames.len; i++) { | 614 | for (i = 0; i < varnames.len; i++) { | |
623 | const char *varname = varnames.words[i]; | 615 | const char *varname = varnames.words[i]; | |
624 | Global_Delete(varname); | 616 | Global_Delete(varname); | |
625 | } | 617 | } | |
626 | 618 | |||
627 | Words_Free(varnames); | 619 | Words_Free(varnames); | |
628 | free(expanded); | 620 | free(expanded); | |
629 | } | 621 | } | |
630 | 622 | |||
631 | static bool | 623 | static bool | |
632 | MayExport(const char *name) | 624 | MayExport(const char *name) | |
633 | { | 625 | { | |
634 | if (name[0] == '.') | 626 | if (name[0] == '.') | |
635 | return false; /* skip internals */ | 627 | return false; /* skip internals */ | |
636 | if (name[0] == '-') | 628 | if (name[0] == '-') | |
637 | return false; /* skip misnamed variables */ | 629 | return false; /* skip misnamed variables */ | |
638 | if (name[1] == '\0') { | 630 | if (name[1] == '\0') { | |
639 | /* | 631 | /* | |
640 | * A single char. | 632 | * A single char. | |
641 | * If it is one of the variables that should only appear in | 633 | * If it is one of the variables that should only appear in | |
642 | * local scope, skip it, else we can get Var_Subst | 634 | * local scope, skip it, else we can get Var_Subst | |
643 | * into a loop. | 635 | * into a loop. | |
644 | */ | 636 | */ | |
645 | switch (name[0]) { | 637 | switch (name[0]) { | |
646 | case '@': | 638 | case '@': | |
647 | case '%': | 639 | case '%': | |
648 | case '*': | 640 | case '*': | |
649 | case '!': | 641 | case '!': | |
650 | return false; | 642 | return false; | |
651 | } | 643 | } | |
652 | } | 644 | } | |
653 | return true; | 645 | return true; | |
654 | } | 646 | } | |
655 | 647 | |||
656 | static bool | 648 | static bool | |
657 | ExportVarEnv(Var *v, GNode *scope) | 649 | ExportVarEnv(Var *v, GNode *scope) | |
658 | { | 650 | { | |
659 | const char *name = v->name.str; | 651 | const char *name = v->name.str; | |
660 | char *val = v->val.data; | 652 | char *val = v->val.data; | |
661 | char *expr; | 653 | char *expr; | |
662 | 654 | |||
663 | if (v->exported && !v->reexport) | 655 | if (v->exported && !v->reexport) | |
664 | return false; /* nothing to do */ | 656 | return false; /* nothing to do */ | |
665 | 657 | |||
666 | if (strchr(val, '$') == NULL) { | 658 | if (strchr(val, '$') == NULL) { | |
667 | if (!v->exported) | 659 | if (!v->exported) | |
668 | setenv(name, val, 1); | 660 | setenv(name, val, 1); | |
669 | return true; | 661 | return true; | |
670 | } | 662 | } | |
671 | 663 | |||
672 | if (v->inUse) | 664 | if (v->inUse) | |
673 | return false; /* see EMPTY_SHELL in directive-export.mk */ | 665 | return false; /* see EMPTY_SHELL in directive-export.mk */ | |
674 | 666 | |||
675 | /* XXX: name is injected without escaping it */ | 667 | /* XXX: name is injected without escaping it */ | |
676 | expr = str_concat3("${", name, "}"); | 668 | expr = str_concat3("${", name, "}"); | |
677 | val = Var_Subst(expr, scope, VARE_WANTRES); | 669 | val = Var_Subst(expr, scope, VARE_WANTRES); | |
678 | if (scope != SCOPE_GLOBAL) { | 670 | if (scope != SCOPE_GLOBAL) { | |
679 | /* we will need to re-export the global version */ | 671 | /* we will need to re-export the global version */ | |
680 | v = VarFind(name, SCOPE_GLOBAL, false); | 672 | v = VarFind(name, SCOPE_GLOBAL, false); | |
681 | if (v != NULL) | 673 | if (v != NULL) | |
682 | v->exported = false; | 674 | v->exported = false; | |
683 | } | 675 | } | |
684 | /* TODO: handle errors */ | 676 | /* TODO: handle errors */ | |
685 | setenv(name, val, 1); | 677 | setenv(name, val, 1); | |
686 | free(val); | 678 | free(val); | |
687 | free(expr); | 679 | free(expr); | |
688 | return true; | 680 | return true; | |
689 | } | 681 | } | |
690 | 682 | |||
691 | static bool | 683 | static bool | |
692 | ExportVarPlain(Var *v) | 684 | ExportVarPlain(Var *v) | |
693 | { | 685 | { | |
694 | if (strchr(v->val.data, '$') == NULL) { | 686 | if (strchr(v->val.data, '$') == NULL) { | |
695 | setenv(v->name.str, v->val.data, 1); | 687 | setenv(v->name.str, v->val.data, 1); | |
696 | v->exported = true; | 688 | v->exported = true; | |
697 | v->reexport = false; | 689 | v->reexport = false; | |
698 | return true; | 690 | return true; | |
699 | } | 691 | } | |
700 | 692 | |||
701 | /* | 693 | /* | |
702 | * Flag the variable as something we need to re-export. | 694 | * Flag the variable as something we need to re-export. | |
703 | * No point actually exporting it now though, | 695 | * No point actually exporting it now though, | |
704 | * the child process can do it at the last minute. | 696 | * the child process can do it at the last minute. | |
705 | * Avoid calling setenv more often than necessary since it can leak. | 697 | * Avoid calling setenv more often than necessary since it can leak. | |
706 | */ | 698 | */ | |
707 | v->exported = true; | 699 | v->exported = true; | |
708 | v->reexport = true; | 700 | v->reexport = true; | |
709 | return true; | 701 | return true; | |
710 | } | 702 | } | |
711 | 703 | |||
712 | static bool | 704 | static bool | |
713 | ExportVarLiteral(Var *v) | 705 | ExportVarLiteral(Var *v) | |
714 | { | 706 | { | |
715 | if (v->exported && !v->reexport) | 707 | if (v->exported && !v->reexport) | |
716 | return false; | 708 | return false; | |
717 | 709 | |||
718 | if (!v->exported) | 710 | if (!v->exported) | |
719 | setenv(v->name.str, v->val.data, 1); | 711 | setenv(v->name.str, v->val.data, 1); | |
720 | 712 | |||
721 | return true; | 713 | return true; | |
722 | } | 714 | } | |
723 | 715 | |||
724 | /* | 716 | /* | |
725 | * Mark a single variable to be exported later for subprocesses. | 717 | * Mark a single variable to be exported later for subprocesses. | |
726 | * | 718 | * | |
727 | * Internal variables are not exported. | 719 | * Internal variables are not exported. | |
728 | */ | 720 | */ | |
729 | static bool | 721 | static bool | |
730 | ExportVar(const char *name, GNode *scope, VarExportMode mode) | 722 | ExportVar(const char *name, GNode *scope, VarExportMode mode) | |
731 | { | 723 | { | |
732 | Var *v; | 724 | Var *v; | |
733 | 725 | |||
734 | if (!MayExport(name)) | 726 | if (!MayExport(name)) | |
735 | return false; | 727 | return false; | |
736 | 728 | |||
737 | v = VarFind(name, scope, false); | 729 | v = VarFind(name, scope, false); | |
738 | if (v == NULL && scope != SCOPE_GLOBAL) | 730 | if (v == NULL && scope != SCOPE_GLOBAL) | |
739 | v = VarFind(name, SCOPE_GLOBAL, false); | 731 | v = VarFind(name, SCOPE_GLOBAL, false); | |
740 | if (v == NULL) | 732 | if (v == NULL) | |
741 | return false; | 733 | return false; | |
742 | 734 | |||
743 | if (mode == VEM_ENV) | 735 | if (mode == VEM_ENV) | |
744 | return ExportVarEnv(v, scope); | 736 | return ExportVarEnv(v, scope); | |
745 | else if (mode == VEM_PLAIN) | 737 | else if (mode == VEM_PLAIN) | |
746 | return ExportVarPlain(v); | 738 | return ExportVarPlain(v); | |
747 | else | 739 | else | |
748 | return ExportVarLiteral(v); | 740 | return ExportVarLiteral(v); | |
749 | } | 741 | } | |
750 | 742 | |||
751 | /* | 743 | /* | |
752 | * Actually export the variables that have been marked as needing to be | 744 | * Actually export the variables that have been marked as needing to be | |
753 | * re-exported. | 745 | * re-exported. | |
754 | */ | 746 | */ | |
755 | void | 747 | void | |
756 | Var_ReexportVars(GNode *scope) | 748 | Var_ReexportVars(GNode *scope) | |
757 | { | 749 | { | |
758 | char *xvarnames; | 750 | char *xvarnames; | |
759 | 751 | |||
760 | /* | 752 | /* | |
761 | * Several make implementations support this sort of mechanism for | 753 | * Several make implementations support this sort of mechanism for | |
762 | * tracking recursion - but each uses a different name. | 754 | * tracking recursion - but each uses a different name. | |
763 | * We allow the makefiles to update MAKELEVEL and ensure | 755 | * We allow the makefiles to update MAKELEVEL and ensure | |
764 | * children see a correctly incremented value. | 756 | * children see a correctly incremented value. | |
765 | */ | 757 | */ | |
766 | char level_buf[21]; | 758 | char level_buf[21]; | |
767 | snprintf(level_buf, sizeof level_buf, "%d", makelevel + 1); | 759 | snprintf(level_buf, sizeof level_buf, "%d", makelevel + 1); | |
768 | setenv(MAKE_LEVEL_ENV, level_buf, 1); | 760 | setenv(MAKE_LEVEL_ENV, level_buf, 1); | |
769 | 761 | |||
770 | if (var_exportedVars == VAR_EXPORTED_NONE) | 762 | if (var_exportedVars == VAR_EXPORTED_NONE) | |
771 | return; | 763 | return; | |
772 | 764 | |||
773 | if (var_exportedVars == VAR_EXPORTED_ALL) { | 765 | if (var_exportedVars == VAR_EXPORTED_ALL) { | |
774 | HashIter hi; | 766 | HashIter hi; | |
775 | 767 | |||
776 | /* Ouch! Exporting all variables at once is crazy. */ | 768 | /* Ouch! Exporting all variables at once is crazy. */ | |
777 | HashIter_Init(&hi, &SCOPE_GLOBAL->vars); | 769 | HashIter_Init(&hi, &SCOPE_GLOBAL->vars); | |
778 | while (HashIter_Next(&hi) != NULL) { | 770 | while (HashIter_Next(&hi) != NULL) { | |
779 | Var *var = hi.entry->value; | 771 | Var *var = hi.entry->value; | |
780 | ExportVar(var->name.str, scope, VEM_ENV); | 772 | ExportVar(var->name.str, scope, VEM_ENV); | |
781 | } | 773 | } | |
782 | return; | 774 | return; | |
783 | } | 775 | } | |
784 | 776 | |||
785 | xvarnames = Var_Subst("${.MAKE.EXPORTED:O:u}", SCOPE_GLOBAL, | 777 | xvarnames = Var_Subst("${.MAKE.EXPORTED:O:u}", SCOPE_GLOBAL, | |
786 | VARE_WANTRES); | 778 | VARE_WANTRES); | |
787 | /* TODO: handle errors */ | 779 | /* TODO: handle errors */ | |
788 | if (xvarnames[0] != '\0') { | 780 | if (xvarnames[0] != '\0') { | |
789 | Words varnames = Str_Words(xvarnames, false); | 781 | Words varnames = Str_Words(xvarnames, false); | |
790 | size_t i; | 782 | size_t i; | |
791 | 783 | |||
792 | for (i = 0; i < varnames.len; i++) | 784 | for (i = 0; i < varnames.len; i++) | |
793 | ExportVar(varnames.words[i], scope, VEM_ENV); | 785 | ExportVar(varnames.words[i], scope, VEM_ENV); | |
794 | Words_Free(varnames); | 786 | Words_Free(varnames); | |
795 | } | 787 | } | |
796 | free(xvarnames); | 788 | free(xvarnames); | |
797 | } | 789 | } | |
798 | 790 | |||
799 | static void | 791 | static void | |
800 | ExportVars(const char *varnames, bool isExport, VarExportMode mode) | 792 | ExportVars(const char *varnames, bool isExport, VarExportMode mode) | |
801 | /* TODO: try to combine the parameters 'isExport' and 'mode'. */ | 793 | /* TODO: try to combine the parameters 'isExport' and 'mode'. */ | |
802 | { | 794 | { | |
803 | Words words = Str_Words(varnames, false); | 795 | Words words = Str_Words(varnames, false); | |
804 | size_t i; | 796 | size_t i; | |
805 | 797 | |||
806 | if (words.len == 1 && words.words[0][0] == '\0') | 798 | if (words.len == 1 && words.words[0][0] == '\0') | |
807 | words.len = 0; | 799 | words.len = 0; | |
808 | 800 | |||
809 | for (i = 0; i < words.len; i++) { | 801 | for (i = 0; i < words.len; i++) { | |
810 | const char *varname = words.words[i]; | 802 | const char *varname = words.words[i]; | |
811 | if (!ExportVar(varname, SCOPE_GLOBAL, mode)) | 803 | if (!ExportVar(varname, SCOPE_GLOBAL, mode)) | |
812 | continue; | 804 | continue; | |
813 | 805 | |||
814 | if (var_exportedVars == VAR_EXPORTED_NONE) | 806 | if (var_exportedVars == VAR_EXPORTED_NONE) | |
815 | var_exportedVars = VAR_EXPORTED_SOME; | 807 | var_exportedVars = VAR_EXPORTED_SOME; | |
816 | 808 | |||
817 | if (isExport && mode == VEM_PLAIN) | 809 | if (isExport && mode == VEM_PLAIN) | |
818 | Global_Append(".MAKE.EXPORTED", varname); | 810 | Global_Append(".MAKE.EXPORTED", varname); | |
819 | } | 811 | } | |
820 | Words_Free(words); | 812 | Words_Free(words); | |
821 | } | 813 | } | |
822 | 814 | |||
823 | static void | 815 | static void | |
824 | ExportVarsExpand(const char *uvarnames, bool isExport, VarExportMode mode) | 816 | ExportVarsExpand(const char *uvarnames, bool isExport, VarExportMode mode) | |
825 | { | 817 | { | |
826 | char *xvarnames = Var_Subst(uvarnames, SCOPE_GLOBAL, VARE_WANTRES); | 818 | char *xvarnames = Var_Subst(uvarnames, SCOPE_GLOBAL, VARE_WANTRES); | |
827 | /* TODO: handle errors */ | 819 | /* TODO: handle errors */ | |
828 | ExportVars(xvarnames, isExport, mode); | 820 | ExportVars(xvarnames, isExport, mode); | |
829 | free(xvarnames); | 821 | free(xvarnames); | |
830 | } | 822 | } | |
831 | 823 | |||
832 | /* Export the named variables, or all variables. */ | 824 | /* Export the named variables, or all variables. */ | |
833 | void | 825 | void | |
834 | Var_Export(VarExportMode mode, const char *varnames) | 826 | Var_Export(VarExportMode mode, const char *varnames) | |
835 | { | 827 | { | |
836 | if (mode == VEM_PLAIN && varnames[0] == '\0') { | 828 | if (mode == VEM_PLAIN && varnames[0] == '\0') { | |
837 | var_exportedVars = VAR_EXPORTED_ALL; /* use with caution! */ | 829 | var_exportedVars = VAR_EXPORTED_ALL; /* use with caution! */ | |
838 | return; | 830 | return; | |
839 | } | 831 | } | |
840 | 832 | |||
841 | ExportVarsExpand(varnames, true, mode); | 833 | ExportVarsExpand(varnames, true, mode); | |
842 | } | 834 | } | |
843 | 835 | |||
844 | void | 836 | void | |
845 | Var_ExportVars(const char *varnames) | 837 | Var_ExportVars(const char *varnames) | |
846 | { | 838 | { | |
847 | ExportVarsExpand(varnames, false, VEM_PLAIN); | 839 | ExportVarsExpand(varnames, false, VEM_PLAIN); | |
848 | } | 840 | } | |
849 | 841 | |||
850 | 842 | |||
851 | static void | 843 | static void | |
852 | ClearEnv(void) | 844 | ClearEnv(void) | |
853 | { | 845 | { | |
854 | const char *level; | 846 | const char *level; | |
855 | char **newenv; | 847 | char **newenv; | |
856 | 848 | |||
857 | level = getenv(MAKE_LEVEL_ENV); /* we should preserve this */ | 849 | level = getenv(MAKE_LEVEL_ENV); /* we should preserve this */ | |
858 | if (environ == savedEnv) { | 850 | if (environ == savedEnv) { | |
859 | /* we have been here before! */ | 851 | /* we have been here before! */ | |
860 | newenv = bmake_realloc(environ, 2 * sizeof(char *)); | 852 | newenv = bmake_realloc(environ, 2 * sizeof(char *)); | |
861 | } else { | 853 | } else { | |
862 | if (savedEnv != NULL) { | 854 | if (savedEnv != NULL) { | |
863 | free(savedEnv); | 855 | free(savedEnv); | |
864 | savedEnv = NULL; | 856 | savedEnv = NULL; | |
865 | } | 857 | } | |
866 | newenv = bmake_malloc(2 * sizeof(char *)); | 858 | newenv = bmake_malloc(2 * sizeof(char *)); | |
867 | } | 859 | } | |
868 | 860 | |||
869 | /* Note: we cannot safely free() the original environ. */ | 861 | /* Note: we cannot safely free() the original environ. */ | |
870 | environ = savedEnv = newenv; | 862 | environ = savedEnv = newenv; | |
871 | newenv[0] = NULL; | 863 | newenv[0] = NULL; | |
872 | newenv[1] = NULL; | 864 | newenv[1] = NULL; | |
873 | if (level != NULL && *level != '\0') | 865 | if (level != NULL && *level != '\0') | |
874 | setenv(MAKE_LEVEL_ENV, level, 1); | 866 | setenv(MAKE_LEVEL_ENV, level, 1); | |
875 | } | 867 | } | |
876 | 868 | |||
877 | static void | 869 | static void | |
878 | GetVarnamesToUnexport(bool isEnv, const char *arg, | 870 | GetVarnamesToUnexport(bool isEnv, const char *arg, | |
879 | FStr *out_varnames, UnexportWhat *out_what) | 871 | FStr *out_varnames, UnexportWhat *out_what) | |
880 | { | 872 | { | |
881 | UnexportWhat what; | 873 | UnexportWhat what; | |
882 | FStr varnames = FStr_InitRefer(""); | 874 | FStr varnames = FStr_InitRefer(""); | |
883 | 875 | |||
884 | if (isEnv) { | 876 | if (isEnv) { | |
885 | if (arg[0] != '\0') { | 877 | if (arg[0] != '\0') { | |
886 | Parse_Error(PARSE_FATAL, | 878 | Parse_Error(PARSE_FATAL, | |
887 | "The directive .unexport-env does not take " | 879 | "The directive .unexport-env does not take " | |
888 | "arguments"); | 880 | "arguments"); | |
889 | /* continue anyway */ | 881 | /* continue anyway */ | |
890 | } | 882 | } | |
891 | what = UNEXPORT_ENV; | 883 | what = UNEXPORT_ENV; | |
892 | 884 | |||
893 | } else { | 885 | } else { | |
894 | what = arg[0] != '\0' ? UNEXPORT_NAMED : UNEXPORT_ALL; | 886 | what = arg[0] != '\0' ? UNEXPORT_NAMED : UNEXPORT_ALL; | |
895 | if (what == UNEXPORT_NAMED) | 887 | if (what == UNEXPORT_NAMED) | |
896 | varnames = FStr_InitRefer(arg); | 888 | varnames = FStr_InitRefer(arg); | |
897 | } | 889 | } | |
898 | 890 | |||
899 | if (what != UNEXPORT_NAMED) { | 891 | if (what != UNEXPORT_NAMED) { | |
900 | char *expanded = Var_Subst("${.MAKE.EXPORTED:O:u}", | 892 | char *expanded = Var_Subst("${.MAKE.EXPORTED:O:u}", | |
901 | SCOPE_GLOBAL, VARE_WANTRES); | 893 | SCOPE_GLOBAL, VARE_WANTRES); | |
902 | /* TODO: handle errors */ | 894 | /* TODO: handle errors */ | |
903 | varnames = FStr_InitOwn(expanded); | 895 | varnames = FStr_InitOwn(expanded); | |
904 | } | 896 | } | |
905 | 897 | |||
906 | *out_varnames = varnames; | 898 | *out_varnames = varnames; | |
907 | *out_what = what; | 899 | *out_what = what; | |
908 | } | 900 | } | |
909 | 901 | |||
910 | static void | 902 | static void | |
911 | UnexportVar(Substring varname, UnexportWhat what) | 903 | UnexportVar(Substring varname, UnexportWhat what) | |
912 | { | 904 | { | |
913 | Var *v = VarFindSubstring(varname, SCOPE_GLOBAL, false); | 905 | Var *v = VarFindSubstring(varname, SCOPE_GLOBAL, false); | |
914 | if (v == NULL) { | 906 | if (v == NULL) { | |
915 | DEBUG2(VAR, "Not unexporting \"%.*s\" (not found)\n", | 907 | DEBUG2(VAR, "Not unexporting \"%.*s\" (not found)\n", | |
916 | (int)Substring_Length(varname), varname.start); | 908 | (int)Substring_Length(varname), varname.start); | |
917 | return; | 909 | return; | |
918 | } | 910 | } | |
919 | 911 | |||
920 | DEBUG2(VAR, "Unexporting \"%.*s\"\n", | 912 | DEBUG2(VAR, "Unexporting \"%.*s\"\n", | |
921 | (int)Substring_Length(varname), varname.start); | 913 | (int)Substring_Length(varname), varname.start); | |
922 | if (what != UNEXPORT_ENV && v->exported && !v->reexport) | 914 | if (what != UNEXPORT_ENV && v->exported && !v->reexport) | |
923 | unsetenv(v->name.str); | 915 | unsetenv(v->name.str); | |
924 | v->exported = false; | 916 | v->exported = false; | |
925 | v->reexport = false; | 917 | v->reexport = false; | |
926 | 918 | |||
927 | if (what == UNEXPORT_NAMED) { | 919 | if (what == UNEXPORT_NAMED) { | |
928 | /* Remove the variable names from .MAKE.EXPORTED. */ | 920 | /* Remove the variable names from .MAKE.EXPORTED. */ | |
929 | /* XXX: v->name is injected without escaping it */ | 921 | /* XXX: v->name is injected without escaping it */ | |
930 | char *expr = str_concat3( | 922 | char *expr = str_concat3( | |
931 | "${.MAKE.EXPORTED:N", v->name.str, "}"); | 923 | "${.MAKE.EXPORTED:N", v->name.str, "}"); | |
932 | char *filtered = Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES); | 924 | char *filtered = Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES); | |
933 | /* TODO: handle errors */ | 925 | /* TODO: handle errors */ | |
934 | Global_Set(".MAKE.EXPORTED", filtered); | 926 | Global_Set(".MAKE.EXPORTED", filtered); | |
935 | free(filtered); | 927 | free(filtered); | |
936 | free(expr); | 928 | free(expr); | |
937 | } | 929 | } | |
938 | } | 930 | } | |
939 | 931 | |||
940 | static void | 932 | static void | |
941 | UnexportVars(FStr *varnames, UnexportWhat what) | 933 | UnexportVars(FStr *varnames, UnexportWhat what) | |
942 | { | 934 | { | |
943 | size_t i; | 935 | size_t i; | |
944 | SubstringWords words; | 936 | SubstringWords words; | |
945 | 937 | |||
946 | if (what == UNEXPORT_ENV) | 938 | if (what == UNEXPORT_ENV) | |
947 | ClearEnv(); | 939 | ClearEnv(); | |
948 | 940 | |||
949 | words = Substring_Words(varnames->str, false); | 941 | words = Substring_Words(varnames->str, false); | |
950 | for (i = 0; i < words.len; i++) | 942 | for (i = 0; i < words.len; i++) | |
951 | UnexportVar(words.words[i], what); | 943 | UnexportVar(words.words[i], what); | |
952 | SubstringWords_Free(words); | 944 | SubstringWords_Free(words); | |
953 | 945 | |||
954 | if (what != UNEXPORT_NAMED) | 946 | if (what != UNEXPORT_NAMED) | |
955 | Global_Delete(".MAKE.EXPORTED"); | 947 | Global_Delete(".MAKE.EXPORTED"); | |
956 | } | 948 | } | |
957 | 949 | |||
958 | /* Handle the .unexport and .unexport-env directives. */ | 950 | /* Handle the .unexport and .unexport-env directives. */ | |
959 | void | 951 | void | |
960 | Var_UnExport(bool isEnv, const char *arg) | 952 | Var_UnExport(bool isEnv, const char *arg) | |
961 | { | 953 | { | |
962 | UnexportWhat what; | 954 | UnexportWhat what; | |
963 | FStr varnames; | 955 | FStr varnames; | |
964 | 956 | |||
965 | GetVarnamesToUnexport(isEnv, arg, &varnames, &what); | 957 | GetVarnamesToUnexport(isEnv, arg, &varnames, &what); | |
966 | UnexportVars(&varnames, what); | 958 | UnexportVars(&varnames, what); | |
967 | FStr_Done(&varnames); | 959 | FStr_Done(&varnames); | |
968 | } | 960 | } | |
969 | 961 | |||
970 | /* Set the variable to the value; the name is not expanded. */ | 962 | /* Set the variable to the value; the name is not expanded. */ | |
971 | void | 963 | void | |
972 | Var_SetWithFlags(GNode *scope, const char *name, const char *val, | 964 | Var_SetWithFlags(GNode *scope, const char *name, const char *val, | |
973 | VarSetFlags flags) | 965 | VarSetFlags flags) | |
974 | { | 966 | { | |
975 | Var *v; | 967 | Var *v; | |
976 | 968 | |||
977 | assert(val != NULL); | 969 | assert(val != NULL); | |
978 | if (name[0] == '\0') { | 970 | if (name[0] == '\0') { | |
979 | DEBUG3(VAR, | 971 | DEBUG3(VAR, | |
980 | "%s: ignoring '%s = %s' as the variable name is empty\n", | 972 | "%s: ignoring '%s = %s' as the variable name is empty\n", | |
981 | scope->name, name, val); | 973 | scope->name, name, val); | |
982 | return; | 974 | return; | |
983 | } | 975 | } | |
984 | 976 | |||
985 | if (scope == SCOPE_GLOBAL | 977 | if (scope == SCOPE_GLOBAL | |
986 | && VarFind(name, SCOPE_CMDLINE, false) != NULL) { | 978 | && VarFind(name, SCOPE_CMDLINE, false) != NULL) { | |
987 | /* | 979 | /* | |
988 | * The global variable would not be visible anywhere. | 980 | * The global variable would not be visible anywhere. | |
989 | * Therefore, there is no point in setting it at all. | 981 | * Therefore, there is no point in setting it at all. | |
990 | */ | 982 | */ | |
991 | DEBUG3(VAR, | 983 | DEBUG3(VAR, | |
992 | "%s: ignoring '%s = %s' " | 984 | "%s: ignoring '%s = %s' " | |
993 | "due to a command line variable of the same name\n", | 985 | "due to a command line variable of the same name\n", | |
994 | scope->name, name, val); | 986 | scope->name, name, val); | |
995 | return; | 987 | return; | |
996 | } | 988 | } | |
997 | 989 | |||
998 | /* | 990 | /* | |
999 | * Only look for a variable in the given scope since anything set | 991 | * Only look for a variable in the given scope since anything set | |
1000 | * here will override anything in a lower scope, so there's not much | 992 | * here will override anything in a lower scope, so there's not much | |
1001 | * point in searching them all. | 993 | * point in searching them all. | |
1002 | */ | 994 | */ | |
1003 | v = VarFind(name, scope, false); | 995 | v = VarFind(name, scope, false); | |
1004 | if (v == NULL) { | 996 | if (v == NULL) { | |
1005 | if (scope == SCOPE_CMDLINE && !(flags & VAR_SET_NO_EXPORT)) { | 997 | if (scope == SCOPE_CMDLINE && !(flags & VAR_SET_NO_EXPORT)) { | |
1006 | /* | 998 | /* | |
1007 | * This var would normally prevent the same name being | 999 | * This variable would normally prevent the same name | |
1008 | * added to SCOPE_GLOBAL, so delete it from there if | 1000 | * being added to SCOPE_GLOBAL, so delete it from | |
1009 | * needed. Otherwise -V name may show the wrong value. | 1001 | * there if needed. Otherwise -V name may show the | |
1002 | * wrong value. | |||
1010 | * | 1003 | * | |
1011 | * See ExistsInCmdline. | 1004 | * See ExistsInCmdline. | |
1012 | */ | 1005 | */ | |
1013 | Var_Delete(SCOPE_GLOBAL, name); | 1006 | Var_Delete(SCOPE_GLOBAL, name); | |
1014 | } | 1007 | } | |
1015 | if (strcmp(name, ".SUFFIXES") == 0) { | 1008 | if (strcmp(name, ".SUFFIXES") == 0) { | |
1016 | /* special: treat as read-only */ | 1009 | /* special: treat as read-only */ | |
1017 | DEBUG3(VAR, | 1010 | DEBUG3(VAR, | |
1018 | "%s: ignoring '%s = %s' as it is read-only\n", | 1011 | "%s: ignoring '%s = %s' as it is read-only\n", | |
1019 | scope->name, name, val); | 1012 | scope->name, name, val); | |
1020 | return; | 1013 | return; | |
1021 | } | 1014 | } | |
1022 | v = VarAdd(name, val, scope, flags); | 1015 | v = VarAdd(name, val, scope, flags); | |
1023 | } else { | 1016 | } else { | |
1024 | if (v->readOnly && !(flags & VAR_SET_READONLY)) { | 1017 | if (v->readOnly && !(flags & VAR_SET_READONLY)) { | |
1025 | DEBUG3(VAR, | 1018 | DEBUG3(VAR, | |
1026 | "%s: ignoring '%s = %s' as it is read-only\n", | 1019 | "%s: ignoring '%s = %s' as it is read-only\n", | |
1027 | scope->name, name, val); | 1020 | scope->name, name, val); | |
1028 | return; | 1021 | return; | |
1029 | } | 1022 | } | |
1030 | Buf_Clear(&v->val); | 1023 | Buf_Clear(&v->val); | |
1031 | Buf_AddStr(&v->val, val); | 1024 | Buf_AddStr(&v->val, val); | |
1032 | 1025 | |||
1033 | DEBUG4(VAR, "%s: %s = %s%s\n", | 1026 | DEBUG4(VAR, "%s: %s = %s%s\n", | |
1034 | scope->name, name, val, ValueDescription(val)); | 1027 | scope->name, name, val, ValueDescription(val)); | |
1035 | if (v->exported) | 1028 | if (v->exported) | |
1036 | ExportVar(name, scope, VEM_PLAIN); | 1029 | ExportVar(name, scope, VEM_PLAIN); | |
1037 | } | 1030 | } | |
1038 | 1031 | |||
1039 | if (scope == SCOPE_CMDLINE) { | 1032 | if (scope == SCOPE_CMDLINE) { | |
1040 | v->fromCmd = true; | 1033 | v->fromCmd = true; | |
1041 | 1034 | |||
1042 | /* | 1035 | /* | |
1043 | * Any variables given on the command line are automatically | 1036 | * Any variables given on the command line are automatically | |
1044 | * exported to the environment (as per POSIX standard), except | 1037 | * exported to the environment (as per POSIX standard), except | |
1045 | * for internals. | 1038 | * for internals. | |
1046 | */ | 1039 | */ | |
1047 | if (!(flags & VAR_SET_NO_EXPORT) && name[0] != '.') { | 1040 | if (!(flags & VAR_SET_NO_EXPORT) && name[0] != '.') { | |
1048 | 1041 | |||
1049 | /* | 1042 | /* | |
1050 | * If requested, don't export these in the | 1043 | * If requested, don't export these in the | |
1051 | * environment individually. We still put | 1044 | * environment individually. We still put | |
1052 | * them in .MAKEOVERRIDES so that the | 1045 | * them in .MAKEOVERRIDES so that the | |
1053 | * command-line settings continue to override | 1046 | * command-line settings continue to override | |
1054 | * Makefile settings. | 1047 | * Makefile settings. | |
1055 | */ | 1048 | */ | |
1056 | if (!opts.varNoExportEnv) | 1049 | if (!opts.varNoExportEnv) | |
1057 | setenv(name, val, 1); | 1050 | setenv(name, val, 1); | |
1058 | /* XXX: What about .MAKE.EXPORTED? */ | 1051 | /* XXX: What about .MAKE.EXPORTED? */ | |
1059 | /* | 1052 | /* | |
1060 | * XXX: Why not just mark the variable for | 1053 | * XXX: Why not just mark the variable for | |
1061 | * needing export, as in ExportVarPlain? | 1054 | * needing export, as in ExportVarPlain? | |
1062 | */ | 1055 | */ | |
1063 | Global_Append(".MAKEOVERRIDES", name); | 1056 | Global_Append(".MAKEOVERRIDES", name); | |
1064 | } | 1057 | } | |
1065 | } | 1058 | } | |
1066 | 1059 | |||
1067 | if (name[0] == '.' && strcmp(name, MAKE_SAVE_DOLLARS) == 0) | 1060 | if (name[0] == '.' && strcmp(name, MAKE_SAVE_DOLLARS) == 0) | |
1068 | save_dollars = ParseBoolean(val, save_dollars); | 1061 | save_dollars = ParseBoolean(val, save_dollars); | |
1069 | 1062 | |||
1070 | if (v != NULL) | 1063 | if (v != NULL) | |
1071 | VarFreeShortLived(v); | 1064 | VarFreeShortLived(v); | |
1072 | } | 1065 | } | |
1073 | 1066 | |||
1074 | void | 1067 | void | |
1075 | Var_Set(GNode *scope, const char *name, const char *val) | 1068 | Var_Set(GNode *scope, const char *name, const char *val) | |
1076 | { | 1069 | { | |
1077 | Var_SetWithFlags(scope, name, val, VAR_SET_NONE); | 1070 | Var_SetWithFlags(scope, name, val, VAR_SET_NONE); | |
1078 | } | 1071 | } | |
1079 | 1072 | |||
1080 | /* | 1073 | /* | |
1081 | * In the scope, expand the variable name once, then create the variable or | 1074 | * In the scope, expand the variable name once, then create the variable or | |
1082 | * replace its value. | 1075 | * replace its value. | |
1083 | */ | 1076 | */ | |
1084 | void | 1077 | void | |
1085 | Var_SetExpand(GNode *scope, const char *name, const char *val) | 1078 | Var_SetExpand(GNode *scope, const char *name, const char *val) | |
1086 | { | 1079 | { | |
1087 | FStr varname = FStr_InitRefer(name); | 1080 | FStr varname = FStr_InitRefer(name); | |
1088 | 1081 | |||
1089 | assert(val != NULL); | 1082 | assert(val != NULL); | |
1090 | 1083 | |||
1091 | Var_Expand(&varname, scope, VARE_WANTRES); | 1084 | Var_Expand(&varname, scope, VARE_WANTRES); | |
1092 | 1085 | |||
1093 | if (varname.str[0] == '\0') { | 1086 | if (varname.str[0] == '\0') { | |
1094 | DEBUG4(VAR, | 1087 | DEBUG4(VAR, | |
1095 | "%s: ignoring '%s = %s' " | 1088 | "%s: ignoring '%s = %s' " | |
1096 | "as the variable name '%s' expands to empty\n", | 1089 | "as the variable name '%s' expands to empty\n", | |
1097 | scope->name, varname.str, val, name); | 1090 | scope->name, varname.str, val, name); | |
1098 | } else | 1091 | } else | |
1099 | Var_SetWithFlags(scope, varname.str, val, VAR_SET_NONE); | 1092 | Var_SetWithFlags(scope, varname.str, val, VAR_SET_NONE); | |
1100 | 1093 | |||
1101 | FStr_Done(&varname); | 1094 | FStr_Done(&varname); | |
1102 | } | 1095 | } | |
1103 | 1096 | |||
1104 | void | 1097 | void | |
1105 | Global_Set(const char *name, const char *value) | 1098 | Global_Set(const char *name, const char *value) | |
1106 | { | 1099 | { | |
1107 | Var_Set(SCOPE_GLOBAL, name, value); | 1100 | Var_Set(SCOPE_GLOBAL, name, value); | |
1108 | } | 1101 | } | |
1109 | 1102 | |||
1110 | void | 1103 | void | |
1111 | Global_Delete(const char *name) | 1104 | Global_Delete(const char *name) | |
1112 | { | 1105 | { | |
1113 | Var_Delete(SCOPE_GLOBAL, name); | 1106 | Var_Delete(SCOPE_GLOBAL, name); | |
1114 | } | 1107 | } | |
1115 | 1108 | |||
1116 | void | 1109 | void | |
1117 | Global_Set_ReadOnly(const char *name, const char *value) | 1110 | Global_Set_ReadOnly(const char *name, const char *value) | |
1118 | { | 1111 | { | |
1119 | Var_SetWithFlags(SCOPE_GLOBAL, name, value, VAR_SET_READONLY); | 1112 | Var_SetWithFlags(SCOPE_GLOBAL, name, value, VAR_SET_READONLY); | |
1120 | } | 1113 | } | |
1121 | 1114 | |||
1122 | /* | 1115 | /* | |
1123 | * Append the value to the named variable. | 1116 | * Append the value to the named variable. | |
1124 | * | 1117 | * | |
1125 | * If the variable doesn't exist, it is created. Otherwise a single space | 1118 | * If the variable doesn't exist, it is created. Otherwise a single space | |
1126 | * and the given value are appended. | 1119 | * and the given value are appended. | |
1127 | */ | 1120 | */ | |
1128 | void | 1121 | void | |
1129 | Var_Append(GNode *scope, const char *name, const char *val) | 1122 | Var_Append(GNode *scope, const char *name, const char *val) | |
1130 | { | 1123 | { | |
1131 | Var *v; | 1124 | Var *v; | |
1132 | 1125 | |||
1133 | v = VarFind(name, scope, scope == SCOPE_GLOBAL); | 1126 | v = VarFind(name, scope, scope == SCOPE_GLOBAL); | |
1134 | 1127 | |||
1135 | if (v == NULL) { | 1128 | if (v == NULL) { | |
1136 | Var_SetWithFlags(scope, name, val, VAR_SET_NONE); | 1129 | Var_SetWithFlags(scope, name, val, VAR_SET_NONE); | |
1137 | } else if (v->readOnly) { | 1130 | } else if (v->readOnly) { | |
1138 | DEBUG3(VAR, "%s: ignoring '%s += %s' as it is read-only\n", | 1131 | DEBUG3(VAR, "%s: ignoring '%s += %s' as it is read-only\n", | |
1139 | scope->name, name, val); | 1132 | scope->name, name, val); | |
1140 | } else if (scope == SCOPE_CMDLINE || !v->fromCmd) { | 1133 | } else if (scope == SCOPE_CMDLINE || !v->fromCmd) { | |
1141 | Buf_AddByte(&v->val, ' '); | 1134 | Buf_AddByte(&v->val, ' '); | |
1142 | Buf_AddStr(&v->val, val); | 1135 | Buf_AddStr(&v->val, val); | |
1143 | 1136 | |||
1144 | DEBUG3(VAR, "%s: %s = %s\n", scope->name, name, v->val.data); | 1137 | DEBUG3(VAR, "%s: %s = %s\n", scope->name, name, v->val.data); | |
1145 | 1138 | |||
1146 | if (v->fromEnvironment) { | 1139 | if (v->fromEnvironment) { | |
1147 | /* See VarAdd. */ | 1140 | /* See VarAdd. */ | |
1148 | HashEntry *he = | 1141 | HashEntry *he = | |
1149 | HashTable_CreateEntry(&scope->vars, name, NULL); | 1142 | HashTable_CreateEntry(&scope->vars, name, NULL); | |
1150 | HashEntry_Set(he, v); | 1143 | HashEntry_Set(he, v); | |
1151 | FStr_Done(&v->name); | 1144 | FStr_Done(&v->name); | |
1152 | v->name = FStr_InitRefer(/* aliased to */ he->key); | 1145 | v->name = FStr_InitRefer(/* aliased to */ he->key); | |
1153 | v->shortLived = false; | 1146 | v->shortLived = false; | |
1154 | v->fromEnvironment = false; | 1147 | v->fromEnvironment = false; | |
1155 | } | 1148 | } | |
1156 | } | 1149 | } | |
1157 | } | 1150 | } | |
1158 | 1151 | |||
1159 | /* | 1152 | /* | |
1160 | * In the scope, expand the variable name once. If the variable exists in the | 1153 | * In the scope, expand the variable name once. If the variable exists in the | |
1161 | * scope, add a space and the value, otherwise set the variable to the value. | 1154 | * scope, add a space and the value, otherwise set the variable to the value. | |
1162 | * | 1155 | * | |
1163 | * Appending to an environment variable only works in the global scope, that | 1156 | * Appending to an environment variable only works in the global scope, that | |
1164 | * is, for variable assignments in makefiles, but not inside conditions or the | 1157 | * is, for variable assignments in makefiles, but not inside conditions or the | |
1165 | * commands of a target. | 1158 | * commands of a target. | |
1166 | */ | 1159 | */ | |
1167 | void | 1160 | void | |
1168 | Var_AppendExpand(GNode *scope, const char *name, const char *val) | 1161 | Var_AppendExpand(GNode *scope, const char *name, const char *val) | |
1169 | { | 1162 | { | |
1170 | FStr xname = FStr_InitRefer(name); | 1163 | FStr xname = FStr_InitRefer(name); | |
1171 | 1164 | |||
1172 | assert(val != NULL); | 1165 | assert(val != NULL); | |
1173 | 1166 | |||
1174 | Var_Expand(&xname, scope, VARE_WANTRES); | 1167 | Var_Expand(&xname, scope, VARE_WANTRES); | |
1175 | if (xname.str != name && xname.str[0] == '\0') | 1168 | if (xname.str != name && xname.str[0] == '\0') | |
1176 | DEBUG4(VAR, | 1169 | DEBUG4(VAR, | |
1177 | "%s: ignoring '%s += %s' " | 1170 | "%s: ignoring '%s += %s' " | |
1178 | "as the variable name '%s' expands to empty\n", | 1171 | "as the variable name '%s' expands to empty\n", | |
1179 | scope->name, xname.str, val, name); | 1172 | scope->name, xname.str, val, name); | |
1180 | else | 1173 | else | |
1181 | Var_Append(scope, xname.str, val); | 1174 | Var_Append(scope, xname.str, val); | |
1182 | 1175 | |||
1183 | FStr_Done(&xname); | 1176 | FStr_Done(&xname); | |
1184 | } | 1177 | } | |
1185 | 1178 | |||
1186 | void | 1179 | void | |
1187 | Global_Append(const char *name, const char *value) | 1180 | Global_Append(const char *name, const char *value) | |
1188 | { | 1181 | { | |
1189 | Var_Append(SCOPE_GLOBAL, name, value); | 1182 | Var_Append(SCOPE_GLOBAL, name, value); | |
1190 | } | 1183 | } | |
1191 | 1184 | |||
1192 | bool | 1185 | bool | |
1193 | Var_Exists(GNode *scope, const char *name) | 1186 | Var_Exists(GNode *scope, const char *name) | |
1194 | { | 1187 | { | |
1195 | Var *v = VarFind(name, scope, true); | 1188 | Var *v = VarFind(name, scope, true); | |
1196 | if (v == NULL) | 1189 | if (v == NULL) | |
1197 | return false; | 1190 | return false; | |
1198 | 1191 | |||
1199 | VarFreeShortLived(v); | 1192 | VarFreeShortLived(v); | |
1200 | return true; | 1193 | return true; | |
1201 | } | 1194 | } | |
1202 | 1195 | |||
1203 | /* | 1196 | /* | |
1204 | * See if the given variable exists, in the given scope or in other | 1197 | * See if the given variable exists, in the given scope or in other | |
1205 | * fallback scopes. | 1198 | * fallback scopes. | |
1206 | * | 1199 | * | |
1207 | * Input: | 1200 | * Input: | |
1208 | * scope scope in which to start search | 1201 | * scope scope in which to start search | |
1209 | * name name of the variable to find, is expanded once | 1202 | * name name of the variable to find, is expanded once | |
1210 | */ | 1203 | */ | |
1211 | bool | 1204 | bool | |
1212 | Var_ExistsExpand(GNode *scope, const char *name) | 1205 | Var_ExistsExpand(GNode *scope, const char *name) | |
1213 | { | 1206 | { | |
1214 | FStr varname = FStr_InitRefer(name); | 1207 | FStr varname = FStr_InitRefer(name); | |
1215 | bool exists; | 1208 | bool exists; | |
1216 | 1209 | |||
1217 | Var_Expand(&varname, scope, VARE_WANTRES); | 1210 | Var_Expand(&varname, scope, VARE_WANTRES); | |
1218 | exists = Var_Exists(scope, varname.str); | 1211 | exists = Var_Exists(scope, varname.str); | |
1219 | FStr_Done(&varname); | 1212 | FStr_Done(&varname); | |
1220 | return exists; | 1213 | return exists; | |
1221 | } | 1214 | } | |
1222 | 1215 | |||
1223 | /* | 1216 | /* | |
1224 | * Return the unexpanded value of the given variable in the given scope, | 1217 | * Return the unexpanded value of the given variable in the given scope, | |
1225 | * falling back to the command, global and environment scopes, in this order, | 1218 | * falling back to the command, global and environment scopes, in this order, | |
1226 | * but see the -e option. | 1219 | * but see the -e option. | |
1227 | * | 1220 | * | |
1228 | * Input: | 1221 | * Input: | |
1229 | * name the name to find, is not expanded any further | 1222 | * name the name to find, is not expanded any further | |
1230 | * | 1223 | * | |
1231 | * Results: | 1224 | * Results: | |
1232 | * The value if the variable exists, NULL if it doesn't. | 1225 | * The value if the variable exists, NULL if it doesn't. | |
1233 | * The value is valid until the next modification to any variable. | 1226 | * The value is valid until the next modification to any variable. | |
1234 | */ | 1227 | */ | |
1235 | FStr | 1228 | FStr | |
1236 | Var_Value(GNode *scope, const char *name) | 1229 | Var_Value(GNode *scope, const char *name) | |
1237 | { | 1230 | { | |
1238 | Var *v = VarFind(name, scope, true); | 1231 | Var *v = VarFind(name, scope, true); | |
1239 | char *value; | 1232 | char *value; | |
1240 | 1233 | |||
1241 | if (v == NULL) | 1234 | if (v == NULL) | |
1242 | return FStr_InitRefer(NULL); | 1235 | return FStr_InitRefer(NULL); | |
1243 | 1236 | |||
1244 | if (!v->shortLived) | 1237 | if (!v->shortLived) | |
1245 | return FStr_InitRefer(v->val.data); | 1238 | return FStr_InitRefer(v->val.data); | |
1246 | 1239 | |||
1247 | value = v->val.data; | 1240 | value = v->val.data; | |
1248 | v->val.data = NULL; | 1241 | v->val.data = NULL; | |
1249 | VarFreeShortLived(v); | 1242 | VarFreeShortLived(v); | |
1250 | 1243 | |||
1251 | return FStr_InitOwn(value); | 1244 | return FStr_InitOwn(value); | |
1252 | } | 1245 | } | |
1253 | 1246 | |||
1254 | /* Set or clear the read-only attribute of the variable if it exists. */ | 1247 | /* Set or clear the read-only attribute of the variable if it exists. */ | |
1255 | void | 1248 | void | |
1256 | Var_ReadOnly(const char *name, bool bf) | 1249 | Var_ReadOnly(const char *name, bool bf) | |
1257 | { | 1250 | { | |
1258 | Var *v; | 1251 | Var *v; | |
1259 | 1252 | |||
1260 | v = VarFind(name, SCOPE_GLOBAL, false); | 1253 | v = VarFind(name, SCOPE_GLOBAL, false); | |
1261 | if (v == NULL) { | 1254 | if (v == NULL) { | |
1262 | DEBUG1(VAR, "Var_ReadOnly: %s not found\n", name); | 1255 | DEBUG1(VAR, "Var_ReadOnly: %s not found\n", name); | |
1263 | return; | 1256 | return; | |
1264 | } | 1257 | } | |
1265 | v->readOnly = bf; | 1258 | v->readOnly = bf; | |
1266 | DEBUG2(VAR, "Var_ReadOnly: %s %s\n", name, bf ? "true" : "false"); | 1259 | DEBUG2(VAR, "Var_ReadOnly: %s %s\n", name, bf ? "true" : "false"); | |
1267 | } | 1260 | } | |
1268 | 1261 | |||
1269 | /* | 1262 | /* | |
1270 | * Return the unexpanded variable value from this node, without trying to look | 1263 | * Return the unexpanded variable value from this node, without trying to look | |
1271 | * up the variable in any other scope. | 1264 | * up the variable in any other scope. | |
1272 | */ | 1265 | */ | |
1273 | const char * | 1266 | const char * | |
1274 | GNode_ValueDirect(GNode *gn, const char *name) | 1267 | GNode_ValueDirect(GNode *gn, const char *name) | |
1275 | { | 1268 | { | |
1276 | Var *v = VarFind(name, gn, false); | 1269 | Var *v = VarFind(name, gn, false); | |
1277 | return v != NULL ? v->val.data : NULL; | 1270 | return v != NULL ? v->val.data : NULL; | |
1278 | } | 1271 | } | |
1279 | 1272 | |||
1280 | static VarEvalMode | 1273 | static VarEvalMode | |
1281 | VarEvalMode_WithoutKeepDollar(VarEvalMode emode) | 1274 | VarEvalMode_WithoutKeepDollar(VarEvalMode emode) | |
1282 | { | 1275 | { | |
1283 | if (emode == VARE_KEEP_DOLLAR_UNDEF) | 1276 | if (emode == VARE_KEEP_DOLLAR_UNDEF) | |
1284 | return VARE_EVAL_KEEP_UNDEF; | 1277 | return VARE_EVAL_KEEP_UNDEF; | |
1285 | if (emode == VARE_EVAL_KEEP_DOLLAR) | 1278 | if (emode == VARE_EVAL_KEEP_DOLLAR) | |
1286 | return VARE_WANTRES; | 1279 | return VARE_WANTRES; | |
1287 | return emode; | 1280 | return emode; | |
1288 | } | 1281 | } | |
1289 | 1282 | |||
1290 | static VarEvalMode | 1283 | static VarEvalMode | |
1291 | VarEvalMode_UndefOk(VarEvalMode emode) | 1284 | VarEvalMode_UndefOk(VarEvalMode emode) | |
1292 | { | 1285 | { | |
1293 | return emode == VARE_UNDEFERR ? VARE_WANTRES : emode; | 1286 | return emode == VARE_UNDEFERR ? VARE_WANTRES : emode; | |
1294 | } | 1287 | } | |
1295 | 1288 | |||
1296 | static bool | 1289 | static bool | |
1297 | VarEvalMode_ShouldEval(VarEvalMode emode) | 1290 | VarEvalMode_ShouldEval(VarEvalMode emode) | |
1298 | { | 1291 | { | |
1299 | return emode != VARE_PARSE_ONLY; | 1292 | return emode != VARE_PARSE_ONLY; | |
1300 | } | 1293 | } | |
1301 | 1294 | |||
1302 | static bool | 1295 | static bool | |
1303 | VarEvalMode_ShouldKeepUndef(VarEvalMode emode) | 1296 | VarEvalMode_ShouldKeepUndef(VarEvalMode emode) | |
1304 | { | 1297 | { | |
1305 | return emode == VARE_EVAL_KEEP_UNDEF || | 1298 | return emode == VARE_EVAL_KEEP_UNDEF || | |
1306 | emode == VARE_KEEP_DOLLAR_UNDEF; | 1299 | emode == VARE_KEEP_DOLLAR_UNDEF; | |
1307 | } | 1300 | } | |
1308 | 1301 | |||
1309 | static bool | 1302 | static bool | |
1310 | VarEvalMode_ShouldKeepDollar(VarEvalMode emode) | 1303 | VarEvalMode_ShouldKeepDollar(VarEvalMode emode) | |
1311 | { | 1304 | { | |
1312 | return emode == VARE_EVAL_KEEP_DOLLAR || | 1305 | return emode == VARE_EVAL_KEEP_DOLLAR || | |
1313 | emode == VARE_KEEP_DOLLAR_UNDEF; | 1306 | emode == VARE_KEEP_DOLLAR_UNDEF; | |
1314 | } | 1307 | } | |
1315 | 1308 | |||
1316 | 1309 | |||
1317 | static void | 1310 | static void | |
1318 | SepBuf_Init(SepBuf *buf, char sep) | 1311 | SepBuf_Init(SepBuf *buf, char sep) | |
1319 | { | 1312 | { | |
1320 | Buf_InitSize(&buf->buf, 32); | 1313 | Buf_InitSize(&buf->buf, 32); | |
1321 | buf->needSep = false; | 1314 | buf->needSep = false; | |
1322 | buf->sep = sep; | 1315 | buf->sep = sep; | |
1323 | } | 1316 | } | |
1324 | 1317 | |||
1325 | static void | 1318 | static void | |
1326 | SepBuf_Sep(SepBuf *buf) | 1319 | SepBuf_Sep(SepBuf *buf) | |
1327 | { | 1320 | { | |
1328 | buf->needSep = true; | 1321 | buf->needSep = true; | |
1329 | } | 1322 | } | |
1330 | 1323 | |||
1331 | static void | 1324 | static void | |
1332 | SepBuf_AddBytes(SepBuf *buf, const char *mem, size_t mem_size) | 1325 | SepBuf_AddBytes(SepBuf *buf, const char *mem, size_t mem_size) | |
1333 | { | 1326 | { | |
1334 | if (mem_size == 0) | 1327 | if (mem_size == 0) | |
1335 | return; | 1328 | return; | |
1336 | if (buf->needSep && buf->sep != '\0') { | 1329 | if (buf->needSep && buf->sep != '\0') { | |
1337 | Buf_AddByte(&buf->buf, buf->sep); | 1330 | Buf_AddByte(&buf->buf, buf->sep); | |
1338 | buf->needSep = false; | 1331 | buf->needSep = false; | |
1339 | } | 1332 | } | |
1340 | Buf_AddBytes(&buf->buf, mem, mem_size); | 1333 | Buf_AddBytes(&buf->buf, mem, mem_size); | |
1341 | } | 1334 | } | |
1342 | 1335 | |||
1343 | static void | 1336 | static void | |
1344 | SepBuf_AddRange(SepBuf *buf, const char *start, const char *end) | 1337 | SepBuf_AddRange(SepBuf *buf, const char *start, const char *end) | |
1345 | { | 1338 | { | |
1346 | SepBuf_AddBytes(buf, start, (size_t)(end - start)); | 1339 | SepBuf_AddBytes(buf, start, (size_t)(end - start)); | |
1347 | } | 1340 | } | |
1348 | 1341 | |||
1349 | static void | 1342 | static void | |
1350 | SepBuf_AddStr(SepBuf *buf, const char *str) | 1343 | SepBuf_AddStr(SepBuf *buf, const char *str) | |
1351 | { | 1344 | { | |
1352 | SepBuf_AddBytes(buf, str, strlen(str)); | 1345 | SepBuf_AddBytes(buf, str, strlen(str)); | |
1353 | } | 1346 | } | |
1354 | 1347 | |||
1355 | static void | 1348 | static void | |
1356 | SepBuf_AddSubstring(SepBuf *buf, Substring sub) | 1349 | SepBuf_AddSubstring(SepBuf *buf, Substring sub) | |
1357 | { | 1350 | { | |
1358 | SepBuf_AddRange(buf, sub.start, sub.end); | 1351 | SepBuf_AddRange(buf, sub.start, sub.end); | |
1359 | } | 1352 | } | |
1360 | 1353 | |||
1361 | static char * | 1354 | static char * | |
1362 | SepBuf_DoneData(SepBuf *buf) | 1355 | SepBuf_DoneData(SepBuf *buf) | |
1363 | { | 1356 | { | |
1364 | return Buf_DoneData(&buf->buf); | 1357 | return Buf_DoneData(&buf->buf); | |
1365 | } | 1358 | } | |
1366 | 1359 | |||
1367 | 1360 | |||
1368 | /* | 1361 | /* | |
1369 | * This callback for ModifyWords gets a single word from an expression | 1362 | * This callback for ModifyWords gets a single word from an expression | |
1370 | * and typically adds a modification of this word to the buffer. It may also | 1363 | * and typically adds a modification of this word to the buffer. It may also | |
1371 | * do nothing or add several words. | 1364 | * do nothing or add several words. | |
1372 | * | 1365 | * | |
1373 | * For example, when evaluating the modifier ':M*b' in ${:Ua b c:M*b}, the | 1366 | * For example, when evaluating the modifier ':M*b' in ${:Ua b c:M*b}, the | |
1374 | * callback is called 3 times, once for "a", "b" and "c". | 1367 | * callback is called 3 times, once for "a", "b" and "c". | |
1375 | * | 1368 | * | |
1376 | * Some ModifyWord functions assume that they are always passed a | 1369 | * Some ModifyWord functions assume that they are always passed a | |
1377 | * null-terminated substring, which is currently guaranteed but may change in | 1370 | * null-terminated substring, which is currently guaranteed but may change in | |
1378 | * the future. | 1371 | * the future. | |
1379 | */ | 1372 | */ | |
1380 | typedef void (*ModifyWordProc)(Substring word, SepBuf *buf, void *data); | 1373 | typedef void (*ModifyWordProc)(Substring word, SepBuf *buf, void *data); | |
1381 | 1374 | |||
1382 | 1375 | |||
1383 | /*ARGSUSED*/ | 1376 | /*ARGSUSED*/ | |
1384 | static void | 1377 | static void | |
1385 | ModifyWord_Head(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) | 1378 | ModifyWord_Head(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) | |
1386 | { | 1379 | { | |
1387 | SepBuf_AddSubstring(buf, Substring_Dirname(word)); | 1380 | SepBuf_AddSubstring(buf, Substring_Dirname(word)); | |
1388 | } | 1381 | } | |
1389 | 1382 | |||
1390 | /*ARGSUSED*/ | 1383 | /*ARGSUSED*/ | |
1391 | static void | 1384 | static void | |
1392 | ModifyWord_Tail(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) | 1385 | ModifyWord_Tail(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) | |
1393 | { | 1386 | { | |
1394 | SepBuf_AddSubstring(buf, Substring_Basename(word)); | 1387 | SepBuf_AddSubstring(buf, Substring_Basename(word)); | |
1395 | } | 1388 | } | |
1396 | 1389 | |||
1397 | /*ARGSUSED*/ | 1390 | /*ARGSUSED*/ | |
1398 | static void | 1391 | static void | |
1399 | ModifyWord_Suffix(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) | 1392 | ModifyWord_Suffix(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) | |
1400 | { | 1393 | { | |
1401 | const char *lastDot = Substring_FindLast(word, '.'); | 1394 | const char *lastDot = Substring_FindLast(word, '.'); | |
1402 | if (lastDot != NULL) | 1395 | if (lastDot != NULL) | |
1403 | SepBuf_AddRange(buf, lastDot + 1, word.end); | 1396 | SepBuf_AddRange(buf, lastDot + 1, word.end); | |
1404 | } | 1397 | } | |
1405 | 1398 | |||
1406 | /*ARGSUSED*/ | 1399 | /*ARGSUSED*/ | |
1407 | static void | 1400 | static void | |
1408 | ModifyWord_Root(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) | 1401 | ModifyWord_Root(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) | |
1409 | { | 1402 | { | |
1410 | const char *lastDot, *end; | 1403 | const char *lastDot, *end; | |
1411 | 1404 | |||
1412 | lastDot = Substring_FindLast(word, '.'); | 1405 | lastDot = Substring_FindLast(word, '.'); | |
1413 | end = lastDot != NULL ? lastDot : word.end; | 1406 | end = lastDot != NULL ? lastDot : word.end; | |
1414 | SepBuf_AddRange(buf, word.start, end); | 1407 | SepBuf_AddRange(buf, word.start, end); | |
1415 | } | 1408 | } | |
1416 | 1409 | |||
1417 | struct ModifyWord_SysVSubstArgs { | 1410 | struct ModifyWord_SysVSubstArgs { | |
1418 | GNode *scope; | 1411 | GNode *scope; | |
1419 | Substring lhsPrefix; | 1412 | Substring lhsPrefix; | |
1420 | bool lhsPercent; | 1413 | bool lhsPercent; | |
1421 | Substring lhsSuffix; | 1414 | Substring lhsSuffix; | |
1422 | const char *rhs; | 1415 | const char *rhs; | |
1423 | }; | 1416 | }; | |
1424 | 1417 | |||
1425 | static void | 1418 | static void | |
1426 | ModifyWord_SysVSubst(Substring word, SepBuf *buf, void *data) | 1419 | ModifyWord_SysVSubst(Substring word, SepBuf *buf, void *data) | |
1427 | { | 1420 | { | |
1428 | const struct ModifyWord_SysVSubstArgs *args = data; | 1421 | const struct ModifyWord_SysVSubstArgs *args = data; | |
1429 | FStr rhs; | 1422 | FStr rhs; | |
1430 | const char *percent; | 1423 | const char *percent; | |
1431 | 1424 | |||
1432 | if (Substring_IsEmpty(word)) | 1425 | if (Substring_IsEmpty(word)) | |
1433 | return; | 1426 | return; | |
1434 | 1427 | |||
1435 | if (!Substring_HasPrefix(word, args->lhsPrefix) || | 1428 | if (!Substring_HasPrefix(word, args->lhsPrefix) || | |
1436 | !Substring_HasSuffix(word, args->lhsSuffix)) { | 1429 | !Substring_HasSuffix(word, args->lhsSuffix)) { | |
1437 | SepBuf_AddSubstring(buf, word); | 1430 | SepBuf_AddSubstring(buf, word); | |
1438 | return; | 1431 | return; | |
1439 | } | 1432 | } | |
1440 | 1433 | |||
1441 | rhs = FStr_InitRefer(args->rhs); | 1434 | rhs = FStr_InitRefer(args->rhs); | |
1442 | Var_Expand(&rhs, args->scope, VARE_WANTRES); | 1435 | Var_Expand(&rhs, args->scope, VARE_WANTRES); | |
1443 | 1436 | |||
1444 | percent = args->lhsPercent ? strchr(rhs.str, '%') : NULL; | 1437 | percent = args->lhsPercent ? strchr(rhs.str, '%') : NULL; | |
1445 | 1438 | |||
1446 | if (percent != NULL) | 1439 | if (percent != NULL) | |
1447 | SepBuf_AddRange(buf, rhs.str, percent); | 1440 | SepBuf_AddRange(buf, rhs.str, percent); | |
1448 | if (percent != NULL || !args->lhsPercent) | 1441 | if (percent != NULL || !args->lhsPercent) | |
1449 | SepBuf_AddRange(buf, | 1442 | SepBuf_AddRange(buf, | |
1450 | word.start + Substring_Length(args->lhsPrefix), | 1443 | word.start + Substring_Length(args->lhsPrefix), | |
1451 | word.end - Substring_Length(args->lhsSuffix)); | 1444 | word.end - Substring_Length(args->lhsSuffix)); | |
1452 | SepBuf_AddStr(buf, percent != NULL ? percent + 1 : rhs.str); | 1445 | SepBuf_AddStr(buf, percent != NULL ? percent + 1 : rhs.str); | |
1453 | 1446 | |||
1454 | FStr_Done(&rhs); | 1447 | FStr_Done(&rhs); | |
1455 | } | 1448 | } | |
1456 | 1449 | |||
1457 | static const char * | 1450 | static const char * | |
1458 | Substring_Find(Substring haystack, Substring needle) | 1451 | Substring_Find(Substring haystack, Substring needle) | |
1459 | { | 1452 | { | |
1460 | size_t len, needleLen, i; | 1453 | size_t len, needleLen, i; | |
1461 | 1454 | |||
1462 | len = Substring_Length(haystack); | 1455 | len = Substring_Length(haystack); | |
1463 | needleLen = Substring_Length(needle); | 1456 | needleLen = Substring_Length(needle); | |
1464 | for (i = 0; i + needleLen <= len; i++) | 1457 | for (i = 0; i + needleLen <= len; i++) | |
1465 | if (memcmp(haystack.start + i, needle.start, needleLen) == 0) | 1458 | if (memcmp(haystack.start + i, needle.start, needleLen) == 0) | |
1466 | return haystack.start + i; | 1459 | return haystack.start + i; | |
1467 | return NULL; | 1460 | return NULL; | |
1468 | } | 1461 | } | |
1469 | 1462 | |||
1470 | struct ModifyWord_SubstArgs { | 1463 | struct ModifyWord_SubstArgs { | |
1471 | Substring lhs; | 1464 | Substring lhs; | |
1472 | Substring rhs; | 1465 | Substring rhs; | |
1473 | PatternFlags pflags; | 1466 | PatternFlags pflags; | |
1474 | bool matched; | 1467 | bool matched; | |
1475 | }; | 1468 | }; | |
1476 | 1469 | |||
1477 | static void | 1470 | static void | |
1478 | ModifyWord_Subst(Substring word, SepBuf *buf, void *data) | 1471 | ModifyWord_Subst(Substring word, SepBuf *buf, void *data) | |
1479 | { | 1472 | { | |
1480 | struct ModifyWord_SubstArgs *args = data; | 1473 | struct ModifyWord_SubstArgs *args = data; | |
1481 | size_t wordLen, lhsLen; | 1474 | size_t wordLen, lhsLen; | |
1482 | const char *match; | 1475 | const char *match; | |
1483 | 1476 | |||
1484 | wordLen = Substring_Length(word); | 1477 | wordLen = Substring_Length(word); | |
1485 | if (args->pflags.subOnce && args->matched) | 1478 | if (args->pflags.subOnce && args->matched) | |
1486 | goto nosub; | 1479 | goto nosub; | |
1487 | 1480 | |||
1488 | lhsLen = Substring_Length(args->lhs); | 1481 | lhsLen = Substring_Length(args->lhs); | |
1489 | if (args->pflags.anchorStart) { | 1482 | if (args->pflags.anchorStart) { | |
1490 | if (wordLen < lhsLen || | 1483 | if (wordLen < lhsLen || | |
1491 | memcmp(word.start, args->lhs.start, lhsLen) != 0) | 1484 | memcmp(word.start, args->lhs.start, lhsLen) != 0) | |
1492 | goto nosub; | 1485 | goto nosub; | |
1493 | 1486 | |||
1494 | if (args->pflags.anchorEnd && wordLen != lhsLen) | 1487 | if (args->pflags.anchorEnd && wordLen != lhsLen) | |
1495 | goto nosub; | 1488 | goto nosub; | |
1496 | 1489 | |||
1497 | /* :S,^prefix,replacement, or :S,^whole$,replacement, */ | 1490 | /* :S,^prefix,replacement, or :S,^whole$,replacement, */ | |
1498 | SepBuf_AddSubstring(buf, args->rhs); | 1491 | SepBuf_AddSubstring(buf, args->rhs); | |
1499 | SepBuf_AddRange(buf, word.start + lhsLen, word.end); | 1492 | SepBuf_AddRange(buf, word.start + lhsLen, word.end); | |
1500 | args->matched = true; | 1493 | args->matched = true; | |
1501 | return; | 1494 | return; | |
1502 | } | 1495 | } | |
1503 | 1496 | |||
1504 | if (args->pflags.anchorEnd) { | 1497 | if (args->pflags.anchorEnd) { | |
1505 | if (wordLen < lhsLen) | 1498 | if (wordLen < lhsLen) | |
1506 | goto nosub; | 1499 | goto nosub; | |
1507 | if (memcmp(word.end - lhsLen, args->lhs.start, lhsLen) != 0) | 1500 | if (memcmp(word.end - lhsLen, args->lhs.start, lhsLen) != 0) | |
1508 | goto nosub; | 1501 | goto nosub; | |
1509 | 1502 | |||
1510 | /* :S,suffix$,replacement, */ | 1503 | /* :S,suffix$,replacement, */ | |
1511 | SepBuf_AddRange(buf, word.start, word.end - lhsLen); | 1504 | SepBuf_AddRange(buf, word.start, word.end - lhsLen); | |
1512 | SepBuf_AddSubstring(buf, args->rhs); | 1505 | SepBuf_AddSubstring(buf, args->rhs); | |
1513 | args->matched = true; | 1506 | args->matched = true; | |
1514 | return; | 1507 | return; | |
1515 | } | 1508 | } | |
1516 | 1509 | |||
1517 | if (Substring_IsEmpty(args->lhs)) | 1510 | if (Substring_IsEmpty(args->lhs)) | |
1518 | goto nosub; | 1511 | goto nosub; | |
1519 | 1512 | |||
1520 | /* unanchored case, may match more than once */ | 1513 | /* unanchored case, may match more than once */ | |
1521 | while ((match = Substring_Find(word, args->lhs)) != NULL) { | 1514 | while ((match = Substring_Find(word, args->lhs)) != NULL) { | |
1522 | SepBuf_AddRange(buf, word.start, match); | 1515 | SepBuf_AddRange(buf, word.start, match); | |
1523 | SepBuf_AddSubstring(buf, args->rhs); | 1516 | SepBuf_AddSubstring(buf, args->rhs); | |
1524 | args->matched = true; | 1517 | args->matched = true; | |
1525 | word.start = match + lhsLen; | 1518 | word.start = match + lhsLen; | |
1526 | if (Substring_IsEmpty(word) || !args->pflags.subGlobal) | 1519 | if (Substring_IsEmpty(word) || !args->pflags.subGlobal) | |
1527 | break; | 1520 | break; | |
1528 | } | 1521 | } | |
1529 | nosub: | 1522 | nosub: | |
1530 | SepBuf_AddSubstring(buf, word); | 1523 | SepBuf_AddSubstring(buf, word); | |
1531 | } | 1524 | } | |
1532 | 1525 | |||
1533 | /* Print the error caused by a regcomp or regexec call. */ | 1526 | /* Print the error caused by a regcomp or regexec call. */ | |
1534 | static void | 1527 | static void | |
1535 | RegexError(int reerr, const regex_t *pat, const char *str) | 1528 | RegexError(int reerr, const regex_t *pat, const char *str) | |
1536 | { | 1529 | { | |
1537 | size_t errlen = regerror(reerr, pat, NULL, 0); | 1530 | size_t errlen = regerror(reerr, pat, NULL, 0); | |
1538 | char *errbuf = bmake_malloc(errlen); | 1531 | char *errbuf = bmake_malloc(errlen); | |
1539 | regerror(reerr, pat, errbuf, errlen); | 1532 | regerror(reerr, pat, errbuf, errlen); | |
1540 | Error("%s: %s", str, errbuf); | 1533 | Error("%s: %s", str, errbuf); | |
1541 | free(errbuf); | 1534 | free(errbuf); | |
1542 | } | 1535 | } | |
1543 | 1536 | |||
1544 | /* In the modifier ':C', replace a backreference from \0 to \9. */ | 1537 | /* In the modifier ':C', replace a backreference from \0 to \9. */ | |
1545 | static void | 1538 | static void | |
1546 | RegexReplaceBackref(char ref, SepBuf *buf, const char *wp, | 1539 | RegexReplaceBackref(char ref, SepBuf *buf, const char *wp, | |
1547 | const regmatch_t *m, size_t nsub) | 1540 | const regmatch_t *m, size_t nsub) | |
1548 | { | 1541 | { | |
1549 | unsigned int n = (unsigned)ref - '0'; | 1542 | unsigned int n = (unsigned)ref - '0'; | |
1550 | 1543 | |||
1551 | if (n >= nsub) | 1544 | if (n >= nsub) | |
1552 | Error("No subexpression \\%u", n); | 1545 | Error("No subexpression \\%u", n); | |
1553 | else if (m[n].rm_so == -1) { | 1546 | else if (m[n].rm_so == -1) { | |
1554 | if (opts.strict) | 1547 | if (opts.strict) | |
1555 | Error("No match for subexpression \\%u", n); | 1548 | Error("No match for subexpression \\%u", n); | |
1556 | } else { | 1549 | } else { | |
1557 | SepBuf_AddRange(buf, | 1550 | SepBuf_AddRange(buf, | |
1558 | wp + (size_t)m[n].rm_so, | 1551 | wp + (size_t)m[n].rm_so, | |
1559 | wp + (size_t)m[n].rm_eo); | 1552 | wp + (size_t)m[n].rm_eo); | |
1560 | } | 1553 | } | |
1561 | } | 1554 | } | |
1562 | 1555 | |||
1563 | /* | 1556 | /* | |
1564 | * The regular expression matches the word; now add the replacement to the | 1557 | * The regular expression matches the word; now add the replacement to the | |
1565 | * buffer, taking back-references from 'wp'. | 1558 | * buffer, taking back-references from 'wp'. | |
1566 | */ | 1559 | */ | |
1567 | static void | 1560 | static void | |
1568 | RegexReplace(Substring replace, SepBuf *buf, const char *wp, | 1561 | RegexReplace(Substring replace, SepBuf *buf, const char *wp, | |
1569 | const regmatch_t *m, size_t nsub) | 1562 | const regmatch_t *m, size_t nsub) | |
1570 | { | 1563 | { | |
1571 | const char *rp; | 1564 | const char *rp; | |
1572 | 1565 | |||
1573 | for (rp = replace.start; rp != replace.end; rp++) { | 1566 | for (rp = replace.start; rp != replace.end; rp++) { | |
1574 | if (*rp == '\\' && rp + 1 != replace.end && | 1567 | if (*rp == '\\' && rp + 1 != replace.end && | |
1575 | (rp[1] == '&' || rp[1] == '\\')) | 1568 | (rp[1] == '&' || rp[1] == '\\')) | |
1576 | SepBuf_AddBytes(buf, ++rp, 1); | 1569 | SepBuf_AddBytes(buf, ++rp, 1); | |
1577 | else if (*rp == '\\' && rp + 1 != replace.end && | 1570 | else if (*rp == '\\' && rp + 1 != replace.end && | |
1578 | ch_isdigit(rp[1])) | 1571 | ch_isdigit(rp[1])) | |
1579 | RegexReplaceBackref(*++rp, buf, wp, m, nsub); | 1572 | RegexReplaceBackref(*++rp, buf, wp, m, nsub); | |
1580 | else if (*rp == '&') { | 1573 | else if (*rp == '&') { | |
1581 | SepBuf_AddRange(buf, | 1574 | SepBuf_AddRange(buf, | |
1582 | wp + (size_t)m[0].rm_so, | 1575 | wp + (size_t)m[0].rm_so, | |
1583 | wp + (size_t)m[0].rm_eo); | 1576 | wp + (size_t)m[0].rm_eo); | |
1584 | } else | 1577 | } else | |
1585 | SepBuf_AddBytes(buf, rp, 1); | 1578 | SepBuf_AddBytes(buf, rp, 1); | |
1586 | } | 1579 | } | |
1587 | } | 1580 | } | |
1588 | 1581 | |||
1589 | struct ModifyWord_SubstRegexArgs { | 1582 | struct ModifyWord_SubstRegexArgs { | |
1590 | regex_t re; | 1583 | regex_t re; | |
1591 | size_t nsub; | 1584 | size_t nsub; | |
1592 | Substring replace; | 1585 | Substring replace; | |
1593 | PatternFlags pflags; | 1586 | PatternFlags pflags; | |
1594 | bool matched; | 1587 | bool matched; | |
1595 | }; | 1588 | }; | |
1596 | 1589 | |||
1597 | static void | 1590 | static void | |
1598 | ModifyWord_SubstRegex(Substring word, SepBuf *buf, void *data) | 1591 | ModifyWord_SubstRegex(Substring word, SepBuf *buf, void *data) | |
1599 | { | 1592 | { | |
1600 | struct ModifyWord_SubstRegexArgs *args = data; | 1593 | struct ModifyWord_SubstRegexArgs *args = data; | |
1601 | int xrv; | 1594 | int xrv; | |
1602 | const char *wp; | 1595 | const char *wp; | |
1603 | int flags = 0; | 1596 | int flags = 0; | |
1604 | regmatch_t m[10]; | 1597 | regmatch_t m[10]; | |
1605 | 1598 | |||
1606 | assert(word.end[0] == '\0'); /* assume null-terminated word */ | 1599 | assert(word.end[0] == '\0'); /* assume null-terminated word */ | |
1607 | wp = word.start; | 1600 | wp = word.start; | |
1608 | if (args->pflags.subOnce && args->matched) | 1601 | if (args->pflags.subOnce && args->matched) | |
1609 | goto no_match; | 1602 | goto no_match; | |
1610 | 1603 | |||
1611 | again: | 1604 | again: | |
1612 | xrv = regexec(&args->re, wp, args->nsub, m, flags); | 1605 | xrv = regexec(&args->re, wp, args->nsub, m, flags); | |
1613 | if (xrv == 0) | 1606 | if (xrv == 0) | |
1614 | goto ok; | 1607 | goto ok; | |
1615 | if (xrv != REG_NOMATCH) | 1608 | if (xrv != REG_NOMATCH) | |
1616 | RegexError(xrv, &args->re, "Unexpected regex error"); | 1609 | RegexError(xrv, &args->re, "Unexpected regex error"); | |
1617 | no_match: | 1610 | no_match: | |
1618 | SepBuf_AddRange(buf, wp, word.end); | 1611 | SepBuf_AddRange(buf, wp, word.end); | |
1619 | return; | 1612 | return; | |
1620 | 1613 | |||
1621 | ok: | 1614 | ok: | |
1622 | args->matched = true; | 1615 | args->matched = true; | |
1623 | SepBuf_AddBytes(buf, wp, (size_t)m[0].rm_so); | 1616 | SepBuf_AddBytes(buf, wp, (size_t)m[0].rm_so); | |
1624 | 1617 | |||
1625 | RegexReplace(args->replace, buf, wp, m, args->nsub); | 1618 | RegexReplace(args->replace, buf, wp, m, args->nsub); | |
1626 | 1619 | |||
1627 | wp += (size_t)m[0].rm_eo; | 1620 | wp += (size_t)m[0].rm_eo; | |
1628 | if (args->pflags.subGlobal) { | 1621 | if (args->pflags.subGlobal) { | |
1629 | flags |= REG_NOTBOL; | 1622 | flags |= REG_NOTBOL; | |
1630 | if (m[0].rm_so == 0 && m[0].rm_eo == 0 && *wp != '\0') { | 1623 | if (m[0].rm_so == 0 && m[0].rm_eo == 0 && *wp != '\0') { | |
1631 | SepBuf_AddBytes(buf, wp, 1); | 1624 | SepBuf_AddBytes(buf, wp, 1); | |
1632 | wp++; | 1625 | wp++; | |
1633 | } | 1626 | } | |
1634 | if (*wp != '\0') | 1627 | if (*wp != '\0') | |
1635 | goto again; | 1628 | goto again; | |
1636 | } | 1629 | } | |
1637 | if (*wp != '\0') | 1630 | if (*wp != '\0') | |
1638 | SepBuf_AddStr(buf, wp); | 1631 | SepBuf_AddStr(buf, wp); | |
1639 | } | 1632 | } | |
1640 | 1633 | |||
1641 | 1634 | |||
1642 | struct ModifyWord_LoopArgs { | 1635 | struct ModifyWord_LoopArgs { | |
1643 | GNode *scope; | 1636 | GNode *scope; | |
1644 | const char *var; /* name of the temporary variable */ | 1637 | const char *var; /* name of the temporary variable */ | |
1645 | const char *body; /* string to expand */ | 1638 | const char *body; /* string to expand */ | |
1646 | VarEvalMode emode; | 1639 | VarEvalMode emode; | |
1647 | }; | 1640 | }; | |
1648 | 1641 | |||
1649 | static void | 1642 | static void | |
1650 | ModifyWord_Loop(Substring word, SepBuf *buf, void *data) | 1643 | ModifyWord_Loop(Substring word, SepBuf *buf, void *data) | |
1651 | { | 1644 | { | |
1652 | const struct ModifyWord_LoopArgs *args; | 1645 | const struct ModifyWord_LoopArgs *args; | |
1653 | char *s; | 1646 | char *s; | |
1654 | 1647 | |||
1655 | if (Substring_IsEmpty(word)) | 1648 | if (Substring_IsEmpty(word)) | |
1656 | return; | 1649 | return; | |
1657 | 1650 | |||
1658 | args = data; | 1651 | args = data; | |
1659 | assert(word.end[0] == '\0'); /* assume null-terminated word */ | 1652 | assert(word.end[0] == '\0'); /* assume null-terminated word */ | |
1660 | Var_SetWithFlags(args->scope, args->var, word.start, | 1653 | Var_SetWithFlags(args->scope, args->var, word.start, | |
1661 | VAR_SET_NO_EXPORT); | 1654 | VAR_SET_NO_EXPORT); | |
1662 | s = Var_Subst(args->body, args->scope, args->emode); | 1655 | s = Var_Subst(args->body, args->scope, args->emode); | |
1663 | /* TODO: handle errors */ | 1656 | /* TODO: handle errors */ | |
1664 | 1657 | |||
1665 | DEBUG2(VAR, "ModifyWord_Loop: expand \"%s\" to \"%s\"\n", | 1658 | DEBUG2(VAR, "ModifyWord_Loop: expand \"%s\" to \"%s\"\n", | |
1666 | args->body, s); | 1659 | args->body, s); | |
1667 | 1660 | |||
1668 | if (s[0] == '\n' || Buf_EndsWith(&buf->buf, '\n')) | 1661 | if (s[0] == '\n' || Buf_EndsWith(&buf->buf, '\n')) | |
1669 | buf->needSep = false; | 1662 | buf->needSep = false; | |
1670 | SepBuf_AddStr(buf, s); | 1663 | SepBuf_AddStr(buf, s); | |
1671 | free(s); | 1664 | free(s); | |
1672 | } | 1665 | } | |
1673 | 1666 | |||
1674 | 1667 | |||
1675 | /* | 1668 | /* | |
1676 | * The :[first..last] modifier selects words from the expression. | 1669 | * The :[first..last] modifier selects words from the expression. | |
1677 | * It can also reverse the words. | 1670 | * It can also reverse the words. | |
1678 | */ | 1671 | */ | |
1679 | static char * | 1672 | static char * | |
1680 | VarSelectWords(const char *str, int first, int last, | 1673 | VarSelectWords(const char *str, int first, int last, | |
1681 | char sep, bool oneBigWord) | 1674 | char sep, bool oneBigWord) | |
1682 | { | 1675 | { | |
1683 | SubstringWords words; | 1676 | SubstringWords words; | |
1684 | int len, start, end, step; | 1677 | int len, start, end, step; | |
1685 | int i; | 1678 | int i; | |
1686 | 1679 | |||
1687 | SepBuf buf; | 1680 | SepBuf buf; | |
1688 | SepBuf_Init(&buf, sep); | 1681 | SepBuf_Init(&buf, sep); | |
1689 | 1682 | |||
1690 | if (oneBigWord) { | 1683 | if (oneBigWord) { | |
1691 | /* fake what Substring_Words() would do */ | 1684 | /* fake what Substring_Words() would do */ | |
1692 | words.len = 1; | 1685 | words.len = 1; | |
1693 | words.words = bmake_malloc(sizeof(words.words[0])); | 1686 | words.words = bmake_malloc(sizeof(words.words[0])); | |
1694 | words.freeIt = NULL; | 1687 | words.freeIt = NULL; | |
1695 | words.words[0] = Substring_InitStr(str); /* no need to copy */ | 1688 | words.words[0] = Substring_InitStr(str); /* no need to copy */ | |
1696 | } else { | 1689 | } else { | |
1697 | words = Substring_Words(str, false); | 1690 | words = Substring_Words(str, false); | |
1698 | } | 1691 | } | |
1699 | 1692 | |||
1700 | /* Convert -1 to len, -2 to (len - 1), etc. */ | 1693 | /* Convert -1 to len, -2 to (len - 1), etc. */ | |
1701 | len = (int)words.len; | 1694 | len = (int)words.len; | |
1702 | if (first < 0) | 1695 | if (first < 0) | |
1703 | first += len + 1; | 1696 | first += len + 1; | |
1704 | if (last < 0) | 1697 | if (last < 0) | |
1705 | last += len + 1; | 1698 | last += len + 1; | |
1706 | 1699 | |||
1707 | if (first > last) { | 1700 | if (first > last) { | |
1708 | start = (first > len ? len : first) - 1; | 1701 | start = (first > len ? len : first) - 1; | |
1709 | end = last < 1 ? 0 : last - 1; | 1702 | end = last < 1 ? 0 : last - 1; | |
1710 | step = -1; | 1703 | step = -1; | |
1711 | } else { | 1704 | } else { | |
1712 | start = first < 1 ? 0 : first - 1; | 1705 | start = first < 1 ? 0 : first - 1; | |
1713 | end = last > len ? len : last; | 1706 | end = last > len ? len : last; | |
1714 | step = 1; | 1707 | step = 1; | |
1715 | } | 1708 | } | |
1716 | 1709 | |||
1717 | for (i = start; (step < 0) == (i >= end); i += step) { | 1710 | for (i = start; (step < 0) == (i >= end); i += step) { | |
1718 | SepBuf_AddSubstring(&buf, words.words[i]); | 1711 | SepBuf_AddSubstring(&buf, words.words[i]); | |
1719 | SepBuf_Sep(&buf); | 1712 | SepBuf_Sep(&buf); | |
1720 | } | 1713 | } | |
1721 | 1714 | |||
1722 | SubstringWords_Free(words); | 1715 | SubstringWords_Free(words); | |
1723 | 1716 | |||
1724 | return SepBuf_DoneData(&buf); | 1717 | return SepBuf_DoneData(&buf); | |
1725 | } | 1718 | } | |
1726 | 1719 | |||
1727 | 1720 | |||
1728 | /*ARGSUSED*/ | 1721 | /*ARGSUSED*/ | |
1729 | static void | 1722 | static void | |
1730 | ModifyWord_Realpath(Substring word, SepBuf *buf, void *data MAKE_ATTR_UNUSED) | 1723 | ModifyWord_Realpath(Substring word, SepBuf *buf, void *data MAKE_ATTR_UNUSED) | |
1731 | { | 1724 | { | |
1732 | struct stat st; | 1725 | struct stat st; | |
1733 | char rbuf[MAXPATHLEN]; | 1726 | char rbuf[MAXPATHLEN]; | |
1734 | const char *rp; | 1727 | const char *rp; | |
1735 | 1728 | |||
1736 | assert(word.end[0] == '\0'); /* assume null-terminated word */ | 1729 | assert(word.end[0] == '\0'); /* assume null-terminated word */ | |
1737 | rp = cached_realpath(word.start, rbuf); | 1730 | rp = cached_realpath(word.start, rbuf); | |
1738 | if (rp != NULL && *rp == '/' && stat(rp, &st) == 0) | 1731 | if (rp != NULL && *rp == '/' && stat(rp, &st) == 0) | |
1739 | SepBuf_AddStr(buf, rp); | 1732 | SepBuf_AddStr(buf, rp); | |
1740 | else | 1733 | else | |
1741 | SepBuf_AddSubstring(buf, word); | 1734 | SepBuf_AddSubstring(buf, word); | |
1742 | } | 1735 | } | |
1743 | 1736 | |||
1744 | 1737 | |||
1745 | static char * | 1738 | static char * | |
1746 | SubstringWords_JoinFree(SubstringWords words) | 1739 | SubstringWords_JoinFree(SubstringWords words) | |
1747 | { | 1740 | { | |
1748 | Buffer buf; | 1741 | Buffer buf; | |
1749 | size_t i; | 1742 | size_t i; | |
1750 | 1743 | |||
1751 | Buf_Init(&buf); | 1744 | Buf_Init(&buf); | |
1752 | 1745 | |||
1753 | for (i = 0; i < words.len; i++) { | 1746 | for (i = 0; i < words.len; i++) { | |
1754 | if (i != 0) { | 1747 | if (i != 0) { | |
1755 | /* | 1748 | /* | |
1756 | * XXX: Use ch->sep instead of ' ', for consistency. | 1749 | * XXX: Use ch->sep instead of ' ', for consistency. | |
1757 | */ | 1750 | */ | |
1758 | Buf_AddByte(&buf, ' '); | 1751 | Buf_AddByte(&buf, ' '); | |
1759 | } | 1752 | } | |
1760 | Buf_AddRange(&buf, words.words[i].start, words.words[i].end); | 1753 | Buf_AddRange(&buf, words.words[i].start, words.words[i].end); | |
1761 | } | 1754 | } | |
1762 | 1755 | |||
1763 | SubstringWords_Free(words); | 1756 | SubstringWords_Free(words); | |
1764 | 1757 | |||
1765 | return Buf_DoneData(&buf); | 1758 | return Buf_DoneData(&buf); | |
1766 | } | 1759 | } | |
1767 | 1760 | |||
1768 | 1761 | |||
1769 | /* | 1762 | /* | |
1770 | * Quote shell meta-characters and space characters in the string. | 1763 | * Quote shell meta-characters and space characters in the string. | |
1771 | * If quoteDollar is set, also quote and double any '$' characters. | 1764 | * If quoteDollar is set, also quote and double any '$' characters. | |
1772 | */ | 1765 | */ | |
1773 | static void | 1766 | static void | |
1774 | QuoteShell(const char *str, bool quoteDollar, LazyBuf *buf) | 1767 | QuoteShell(const char *str, bool quoteDollar, LazyBuf *buf) | |
1775 | { | 1768 | { | |
1776 | const char *p; | 1769 | const char *p; | |
1777 | 1770 | |||
1778 | LazyBuf_Init(buf, str); | 1771 | LazyBuf_Init(buf, str); | |
1779 | for (p = str; *p != '\0'; p++) { | 1772 | for (p = str; *p != '\0'; p++) { | |
1780 | if (*p == '\n') { | 1773 | if (*p == '\n') { | |
1781 | const char *newline = Shell_GetNewline(); | 1774 | const char *newline = Shell_GetNewline(); | |
1782 | if (newline == NULL) | 1775 | if (newline == NULL) | |
1783 | newline = "\\\n"; | 1776 | newline = "\\\n"; | |
1784 | LazyBuf_AddStr(buf, newline); | 1777 | LazyBuf_AddStr(buf, newline); | |
1785 | continue; | 1778 | continue; | |
1786 | } | 1779 | } | |
1787 | if (ch_isspace(*p) || ch_is_shell_meta(*p)) | 1780 | if (ch_isspace(*p) || ch_is_shell_meta(*p)) | |
1788 | LazyBuf_Add(buf, '\\'); | 1781 | LazyBuf_Add(buf, '\\'); | |
1789 | LazyBuf_Add(buf, *p); | 1782 | LazyBuf_Add(buf, *p); | |
1790 | if (quoteDollar && *p == '$') | 1783 | if (quoteDollar && *p == '$') | |
1791 | LazyBuf_AddStr(buf, "\\$"); | 1784 | LazyBuf_AddStr(buf, "\\$"); | |
1792 | } | 1785 | } | |
1793 | } | 1786 | } | |
1794 | 1787 | |||
1795 | /* | 1788 | /* | |
1796 | * Compute the 32-bit hash of the given string, using the MurmurHash3 | 1789 | * Compute the 32-bit hash of the given string, using the MurmurHash3 | |
1797 | * algorithm. Output is encoded as 8 hex digits, in Little Endian order. | 1790 | * algorithm. Output is encoded as 8 hex digits, in Little Endian order. | |
1798 | */ | 1791 | */ | |
1799 | static char * | 1792 | static char * | |
1800 | Hash(const char *str) | 1793 | Hash(const char *str) | |
1801 | { | 1794 | { | |
1802 | static const char hexdigits[16] = "0123456789abcdef"; | 1795 | static const char hexdigits[16] = "0123456789abcdef"; | |
1803 | const unsigned char *ustr = (const unsigned char *)str; | 1796 | const unsigned char *ustr = (const unsigned char *)str; | |
1804 | 1797 | |||
1805 | uint32_t h = 0x971e137bU; | 1798 | uint32_t h = 0x971e137bU; | |
1806 | uint32_t c1 = 0x95543787U; | 1799 | uint32_t c1 = 0x95543787U; | |
1807 | uint32_t c2 = 0x2ad7eb25U; | 1800 | uint32_t c2 = 0x2ad7eb25U; | |
1808 | size_t len2 = strlen(str); | 1801 | size_t len2 = strlen(str); | |
1809 | 1802 | |||
1810 | char *buf; | 1803 | char *buf; | |
1811 | size_t i; | 1804 | size_t i; | |
1812 | 1805 | |||
1813 | size_t len; | 1806 | size_t len; | |
1814 | for (len = len2; len != 0;) { | 1807 | for (len = len2; len != 0;) { | |
1815 | uint32_t k = 0; | 1808 | uint32_t k = 0; | |
1816 | switch (len) { | 1809 | switch (len) { | |
1817 | default: | 1810 | default: | |
1818 | k = ((uint32_t)ustr[3] << 24) | | 1811 | k = ((uint32_t)ustr[3] << 24) | | |
1819 | ((uint32_t)ustr[2] << 16) | | 1812 | ((uint32_t)ustr[2] << 16) | | |
1820 | ((uint32_t)ustr[1] << 8) | | 1813 | ((uint32_t)ustr[1] << 8) | | |
1821 | (uint32_t)ustr[0]; | 1814 | (uint32_t)ustr[0]; | |
1822 | len -= 4; | 1815 | len -= 4; | |
1823 | ustr += 4; | 1816 | ustr += 4; | |
1824 | break; | 1817 | break; | |
1825 | case 3: | 1818 | case 3: | |
1826 | k |= (uint32_t)ustr[2] << 16; | 1819 | k |= (uint32_t)ustr[2] << 16; | |
1827 | /* FALLTHROUGH */ | 1820 | /* FALLTHROUGH */ | |
1828 | case 2: | 1821 | case 2: | |
1829 | k |= (uint32_t)ustr[1] << 8; | 1822 | k |= (uint32_t)ustr[1] << 8; | |
1830 | /* FALLTHROUGH */ | 1823 | /* FALLTHROUGH */ | |
1831 | case 1: | 1824 | case 1: | |
1832 | k |= (uint32_t)ustr[0]; | 1825 | k |= (uint32_t)ustr[0]; | |
1833 | len = 0; | 1826 | len = 0; | |
1834 | } | 1827 | } | |
1835 | c1 = c1 * 5 + 0x7b7d159cU; | 1828 | c1 = c1 * 5 + 0x7b7d159cU; | |
1836 | c2 = c2 * 5 + 0x6bce6396U; | 1829 | c2 = c2 * 5 + 0x6bce6396U; | |
1837 | k *= c1; | 1830 | k *= c1; | |
1838 | k = (k << 11) ^ (k >> 21); | 1831 | k = (k << 11) ^ (k >> 21); | |
1839 | k *= c2; | 1832 | k *= c2; | |
1840 | h = (h << 13) ^ (h >> 19); | 1833 | h = (h << 13) ^ (h >> 19); | |
1841 | h = h * 5 + 0x52dce729U; | 1834 | h = h * 5 + 0x52dce729U; | |
1842 | h ^= k; | 1835 | h ^= k; | |
1843 | } | 1836 | } | |
1844 | h ^= (uint32_t)len2; | 1837 | h ^= (uint32_t)len2; | |
1845 | h *= 0x85ebca6b; | 1838 | h *= 0x85ebca6b; | |
1846 | h ^= h >> 13; | 1839 | h ^= h >> 13; | |
1847 | h *= 0xc2b2ae35; | 1840 | h *= 0xc2b2ae35; | |
1848 | h ^= h >> 16; | 1841 | h ^= h >> 16; | |
1849 | 1842 | |||
1850 | buf = bmake_malloc(9); | 1843 | buf = bmake_malloc(9); | |
1851 | for (i = 0; i < 8; i++) { | 1844 | for (i = 0; i < 8; i++) { | |
1852 | buf[i] = hexdigits[h & 0x0f]; | 1845 | buf[i] = hexdigits[h & 0x0f]; | |
1853 | h >>= 4; | 1846 | h >>= 4; | |
1854 | } | 1847 | } | |
1855 | buf[8] = '\0'; | 1848 | buf[8] = '\0'; | |
1856 | return buf; | 1849 | return buf; | |
1857 | } | 1850 | } | |
1858 | 1851 | |||
1859 | static char * | 1852 | static char * | |
1860 | FormatTime(const char *fmt, time_t t, bool gmt) | 1853 | FormatTime(const char *fmt, time_t t, bool gmt) | |
1861 | { | 1854 | { | |
1862 | char buf[BUFSIZ]; | 1855 | char buf[BUFSIZ]; | |
1863 | 1856 | |||
1864 | if (t == 0) | 1857 | if (t == 0) | |
1865 | time(&t); | 1858 | time(&t); | |
1866 | if (*fmt == '\0') | 1859 | if (*fmt == '\0') | |
1867 | fmt = "%c"; | 1860 | fmt = "%c"; | |
1868 | if (gmt && strchr(fmt, 's') != NULL) { | 1861 | if (gmt && strchr(fmt, 's') != NULL) { | |
1869 | /* strftime "%s" only works with localtime, not with gmtime. */ | 1862 | /* strftime "%s" only works with localtime, not with gmtime. */ | |
1870 | const char *prev_tz_env = getenv("TZ"); | 1863 | const char *prev_tz_env = getenv("TZ"); | |
1871 | char *prev_tz = prev_tz_env != NULL | 1864 | char *prev_tz = prev_tz_env != NULL | |
1872 | ? bmake_strdup(prev_tz_env) : NULL; | 1865 | ? bmake_strdup(prev_tz_env) : NULL; | |
1873 | setenv("TZ", "UTC", 1); | 1866 | setenv("TZ", "UTC", 1); | |
1874 | strftime(buf, sizeof buf, fmt, localtime(&t)); | 1867 | strftime(buf, sizeof buf, fmt, localtime(&t)); | |
1875 | if (prev_tz != NULL) { | 1868 | if (prev_tz != NULL) { | |
1876 | setenv("TZ", prev_tz, 1); | 1869 | setenv("TZ", prev_tz, 1); | |
1877 | free(prev_tz); | 1870 | free(prev_tz); | |
1878 | } else | 1871 | } else | |
1879 | unsetenv("TZ"); | 1872 | unsetenv("TZ"); | |
1880 | } else | 1873 | } else | |
1881 | strftime(buf, sizeof buf, fmt, (gmt ? gmtime : localtime)(&t)); | 1874 | strftime(buf, sizeof buf, fmt, (gmt ? gmtime : localtime)(&t)); | |
1882 | 1875 | |||
1883 | buf[sizeof buf - 1] = '\0'; | 1876 | buf[sizeof buf - 1] = '\0'; | |
1884 | return bmake_strdup(buf); | 1877 | return bmake_strdup(buf); | |
1885 | } | 1878 | } | |
1886 | 1879 | |||
1887 | /* | 1880 | /* | |
1888 | * The ApplyModifier functions take an expression that is being evaluated. | 1881 | * The ApplyModifier functions take an expression that is being evaluated. | |
1889 | * Their task is to apply a single modifier to the expression. This involves | 1882 | * Their task is to apply a single modifier to the expression. This involves | |
1890 | * parsing the modifier, evaluating it and finally updating the value of the | 1883 | * parsing the modifier, evaluating it and finally updating the value of the | |
1891 | * expression. | 1884 | * expression. | |
1892 | * | 1885 | * | |
1893 | * Parsing the modifier | 1886 | * Parsing the modifier | |
1894 | * | 1887 | * | |
1895 | * If parsing succeeds, the parsing position *pp is updated to point to the | 1888 | * If parsing succeeds, the parsing position *pp is updated to point to the | |
1896 | * first character following the modifier, which typically is either ':' or | 1889 | * first character following the modifier, which typically is either ':' or | |
1897 | * ch->endc. The modifier doesn't have to check for this delimiter character, | 1890 | * ch->endc. The modifier doesn't have to check for this delimiter character, | |
1898 | * this is done by ApplyModifiers. | 1891 | * this is done by ApplyModifiers. | |
1899 | * | 1892 | * | |
1900 | * XXX: As of 2020-11-15, some modifiers such as :S, :C, :P, :L do not | 1893 | * XXX: As of 2020-11-15, some modifiers such as :S, :C, :P, :L do not | |
1901 | * need to be followed by a ':' or endc; this was an unintended mistake. | 1894 | * need to be followed by a ':' or endc; this was an unintended mistake. | |
1902 | * | 1895 | * | |
1903 | * If parsing fails because of a missing delimiter after a modifier part (as | 1896 | * If parsing fails because of a missing delimiter after a modifier part (as | |
1904 | * in the :S, :C or :@ modifiers), return AMR_CLEANUP. | 1897 | * in the :S, :C or :@ modifiers), return AMR_CLEANUP. | |
1905 | * | 1898 | * | |
1906 | * If parsing fails because the modifier is unknown, return AMR_UNKNOWN to | 1899 | * If parsing fails because the modifier is unknown, return AMR_UNKNOWN to | |
1907 | * try the SysV modifier ':from=to' as fallback. This should only be | 1900 | * try the SysV modifier ':from=to' as fallback. This should only be | |
1908 | * done as long as there have been no side effects from evaluating nested | 1901 | * done as long as there have been no side effects from evaluating nested | |
1909 | * variables, to avoid evaluating them more than once. In this case, the | 1902 | * variables, to avoid evaluating them more than once. In this case, the | |
1910 | * parsing position may or may not be updated. (XXX: Why not? The original | 1903 | * parsing position may or may not be updated. (XXX: Why not? The original | |
1911 | * parsing position is well-known in ApplyModifiers.) | 1904 | * parsing position is well-known in ApplyModifiers.) | |
1912 | * | 1905 | * | |
1913 | * If parsing fails and the SysV modifier ${VAR:from=to} should not be used | 1906 | * If parsing fails and the SysV modifier ${VAR:from=to} should not be used | |
1914 | * as a fallback, issue an error message using Parse_Error (preferred over | 1907 | * as a fallback, issue an error message using Parse_Error (preferred over | |
1915 | * Error) and then return AMR_CLEANUP, which stops processing the expression. | 1908 | * Error) and then return AMR_CLEANUP, which stops processing the expression. | |
1916 | * (XXX: As of 2020-08-23, evaluation of the string continues nevertheless | 1909 | * (XXX: As of 2020-08-23, evaluation of the string continues nevertheless | |
1917 | * after skipping a few bytes, which results in garbage.) | 1910 | * after skipping a few bytes, which results in garbage.) | |
1918 | * | 1911 | * | |
1919 | * Evaluating the modifier | 1912 | * Evaluating the modifier | |
1920 | * | 1913 | * | |
1921 | * After parsing, the modifier is evaluated. The side effects from evaluating | 1914 | * After parsing, the modifier is evaluated. The side effects from evaluating | |
1922 | * nested expressions in the modifier text often already happen | 1915 | * nested expressions in the modifier text often already happen | |
1923 | * during parsing though. For most modifiers this doesn't matter since their | 1916 | * during parsing though. For most modifiers this doesn't matter since their | |
1924 | * only noticeable effect is that they update the value of the expression. | 1917 | * only noticeable effect is that they update the value of the expression. | |
1925 | * Some modifiers such as ':sh' or '::=' have noticeable side effects though. | 1918 | * Some modifiers such as ':sh' or '::=' have noticeable side effects though. | |
1926 | * | 1919 | * | |
1927 | * Evaluating the modifier usually takes the current value of the | 1920 | * Evaluating the modifier usually takes the current value of the | |
1928 | * expression from ch->expr->value, or the variable name from ch->var->name, | 1921 | * expression from ch->expr->value, or the variable name from ch->var->name, | |
1929 | * and stores the result back in ch->expr->value via Expr_SetValueOwn or | 1922 | * and stores the result back in ch->expr->value via Expr_SetValueOwn or | |
1930 | * Expr_SetValueRefer. | 1923 | * Expr_SetValueRefer. | |
1931 | * | 1924 | * | |
1932 | * If evaluating fails, the fallback error message "Bad modifier" is printed | 1925 | * If evaluating fails, the fallback error message "Bad modifier" is printed | |
1933 | * using Error. This function has no side effects, it really just prints the | 1926 | * using Error. This function has no side effects, it really just prints the | |
1934 | * error message, continuing as if nothing had happened. TODO: This should be | 1927 | * error message, continuing as if nothing had happened. TODO: This should be | |
1935 | * fixed by adding proper error handling to Var_Subst, Var_Parse, | 1928 | * fixed by adding proper error handling to Var_Subst, Var_Parse, | |
1936 | * ApplyModifiers and ModifyWords. | 1929 | * ApplyModifiers and ModifyWords. | |
1937 | * | 1930 | * | |
1938 | * Some modifiers such as :D and :U turn undefined expressions into defined | 1931 | * Some modifiers such as :D and :U turn undefined expressions into defined | |
1939 | * expressions using Expr_Define. | 1932 | * expressions using Expr_Define. | |
1940 | */ | 1933 | */ | |
1941 | 1934 | |||
1942 | typedef enum ExprDefined { | 1935 | typedef enum ExprDefined { | |
1943 | /* The expression is based on a regular, defined variable. */ | 1936 | /* The expression is based on a regular, defined variable. */ | |
1944 | DEF_REGULAR, | 1937 | DEF_REGULAR, | |
1945 | /* The expression is based on an undefined variable. */ | 1938 | /* The expression is based on an undefined variable. */ | |
1946 | DEF_UNDEF, | 1939 | DEF_UNDEF, | |
1947 | /* | 1940 | /* | |
1948 | * The expression started as an undefined expression, but one | 1941 | * The expression started as an undefined expression, but one | |
1949 | * of the modifiers (such as ':D' or ':U') has turned the expression | 1942 | * of the modifiers (such as ':D' or ':U') has turned the expression | |
1950 | * from undefined to defined. | 1943 | * from undefined to defined. | |
1951 | */ | 1944 | */ | |
1952 | DEF_DEFINED | 1945 | DEF_DEFINED | |
1953 | } ExprDefined; | 1946 | } ExprDefined; | |
1954 | 1947 | |||
1955 | static const char ExprDefined_Name[][10] = { | 1948 | static const char ExprDefined_Name[][10] = { | |
1956 | "regular", | 1949 | "regular", | |
1957 | "undefined", | 1950 | "undefined", | |
1958 | "defined" | 1951 | "defined" | |
1959 | }; | 1952 | }; | |
1960 | 1953 | |||
1961 | #if __STDC_VERSION__ >= 199901L | 1954 | #if __STDC_VERSION__ >= 199901L | |
1962 | #define const_member const | 1955 | #define const_member const | |
1963 | #else | 1956 | #else | |
1964 | #define const_member /* no const possible */ | 1957 | #define const_member /* no const possible */ | |
1965 | #endif | 1958 | #endif | |
1966 | 1959 | |||
1967 | /* An expression based on a variable, such as $@ or ${VAR:Mpattern:Q}. */ | 1960 | /* An expression based on a variable, such as $@ or ${VAR:Mpattern:Q}. */ | |
1968 | typedef struct Expr { | 1961 | typedef struct Expr { | |
1969 | const char *name; | 1962 | const char *name; | |
1970 | FStr value; | 1963 | FStr value; | |
1971 | VarEvalMode const_member emode; | 1964 | VarEvalMode const_member emode; | |
1972 | GNode *const_member scope; | 1965 | GNode *const_member scope; | |
1973 | ExprDefined defined; | 1966 | ExprDefined defined; | |
1974 | } Expr; | 1967 | } Expr; | |
1975 | 1968 | |||
1976 | /* | 1969 | /* | |
1977 | * The status of applying a chain of modifiers to an expression. | 1970 | * The status of applying a chain of modifiers to an expression. | |
1978 | * | 1971 | * | |
1979 | * The modifiers of an expression are broken into chains of modifiers, | 1972 | * The modifiers of an expression are broken into chains of modifiers, | |
1980 | * starting a new nested chain whenever an indirect modifier starts. There | 1973 | * starting a new nested chain whenever an indirect modifier starts. There | |
1981 | * are at most 2 nesting levels: the outer one for the direct modifiers, and | 1974 | * are at most 2 nesting levels: the outer one for the direct modifiers, and | |
1982 | * the inner one for the indirect modifiers. | 1975 | * the inner one for the indirect modifiers. | |
1983 | * | 1976 | * | |
1984 | * For example, the expression ${VAR:M*:${IND1}:${IND2}:O:u} has 3 chains of | 1977 | * For example, the expression ${VAR:M*:${IND1}:${IND2}:O:u} has 3 chains of | |
1985 | * modifiers: | 1978 | * modifiers: | |
1986 | * | 1979 | * | |
1987 | * Chain 1 starts with the single modifier ':M*'. | 1980 | * Chain 1 starts with the single modifier ':M*'. | |
1988 | * Chain 2 starts with all modifiers from ${IND1}. | 1981 | * Chain 2 starts with all modifiers from ${IND1}. | |
1989 | * Chain 2 ends at the ':' between ${IND1} and ${IND2}. | 1982 | * Chain 2 ends at the ':' between ${IND1} and ${IND2}. | |
1990 | * Chain 3 starts with all modifiers from ${IND2}. | 1983 | * Chain 3 starts with all modifiers from ${IND2}. | |
1991 | * Chain 3 ends at the ':' after ${IND2}. | 1984 | * Chain 3 ends at the ':' after ${IND2}. | |
1992 | * Chain 1 continues with the 2 modifiers ':O' and ':u'. | 1985 | * Chain 1 continues with the 2 modifiers ':O' and ':u'. | |
1993 | * Chain 1 ends at the final '}' of the expression. | 1986 | * Chain 1 ends at the final '}' of the expression. | |
1994 | * | 1987 | * | |
1995 | * After such a chain ends, its properties no longer have any effect. | 1988 | * After such a chain ends, its properties no longer have any effect. | |
1996 | * | 1989 | * | |
1997 | * See varmod-indirect.mk. | 1990 | * See varmod-indirect.mk. | |
1998 | */ | 1991 | */ | |
1999 | typedef struct ModChain { | 1992 | typedef struct ModChain { | |
2000 | Expr *expr; | 1993 | Expr *expr; | |
2001 | /* '\0' or '{' or '(' */ | 1994 | /* '\0' or '{' or '(' */ | |
2002 | char const_member startc; | 1995 | char const_member startc; | |
2003 | /* '\0' or '}' or ')' */ | 1996 | /* '\0' or '}' or ')' */ | |
2004 | char const_member endc; | 1997 | char const_member endc; | |
2005 | /* Separator when joining words (see the :ts modifier). */ | 1998 | /* Separator when joining words (see the :ts modifier). */ | |
2006 | char sep; | 1999 | char sep; | |
2007 | /* | 2000 | /* | |
2008 | * Whether some modifiers that otherwise split the variable value | 2001 | * Whether some modifiers that otherwise split the variable value | |
@@ -3720,1102 +3713,1101 @@ ApplyModifier_SysV(const char **pp, ModC | @@ -3720,1102 +3713,1101 @@ ApplyModifier_SysV(const char **pp, ModC | |||
3720 | args.rhs = rhs.str; | 3713 | args.rhs = rhs.str; | |
3721 | 3714 | |||
3722 | ModifyWords(ch, ModifyWord_SysVSubst, &args, ch->oneBigWord); | 3715 | ModifyWords(ch, ModifyWord_SysVSubst, &args, ch->oneBigWord); | |
3723 | 3716 | |||
3724 | done: | 3717 | done: | |
3725 | LazyBuf_Done(&lhsBuf); | 3718 | LazyBuf_Done(&lhsBuf); | |
3726 | FStr_Done(&rhs); | 3719 | FStr_Done(&rhs); | |
3727 | return AMR_OK; | 3720 | return AMR_OK; | |
3728 | } | 3721 | } | |
3729 | 3722 | |||
3730 | /* :sh */ | 3723 | /* :sh */ | |
3731 | static ApplyModifierResult | 3724 | static ApplyModifierResult | |
3732 | ApplyModifier_SunShell(const char **pp, ModChain *ch) | 3725 | ApplyModifier_SunShell(const char **pp, ModChain *ch) | |
3733 | { | 3726 | { | |
3734 | Expr *expr = ch->expr; | 3727 | Expr *expr = ch->expr; | |
3735 | const char *p = *pp; | 3728 | const char *p = *pp; | |
3736 | if (!(p[1] == 'h' && IsDelimiter(p[2], ch))) | 3729 | if (!(p[1] == 'h' && IsDelimiter(p[2], ch))) | |
3737 | return AMR_UNKNOWN; | 3730 | return AMR_UNKNOWN; | |
3738 | *pp = p + 2; | 3731 | *pp = p + 2; | |
3739 | 3732 | |||
3740 | if (Expr_ShouldEval(expr)) { | 3733 | if (Expr_ShouldEval(expr)) { | |
3741 | char *output, *error; | 3734 | char *output, *error; | |
3742 | output = Cmd_Exec(Expr_Str(expr), &error); | 3735 | output = Cmd_Exec(Expr_Str(expr), &error); | |
3743 | if (error != NULL) { | 3736 | if (error != NULL) { | |
3744 | Error("%s", error); | 3737 | Error("%s", error); | |
3745 | free(error); | 3738 | free(error); | |
3746 | } | 3739 | } | |
3747 | Expr_SetValueOwn(expr, output); | 3740 | Expr_SetValueOwn(expr, output); | |
3748 | } | 3741 | } | |
3749 | 3742 | |||
3750 | return AMR_OK; | 3743 | return AMR_OK; | |
3751 | } | 3744 | } | |
3752 | 3745 | |||
3753 | /* | 3746 | /* | |
3754 | * In cases where the evaluation mode and the definedness are the "standard" | 3747 | * In cases where the evaluation mode and the definedness are the "standard" | |
3755 | * ones, don't log them, to keep the logs readable. | 3748 | * ones, don't log them, to keep the logs readable. | |
3756 | */ | 3749 | */ | |
3757 | static bool | 3750 | static bool | |
3758 | ShouldLogInSimpleFormat(const Expr *expr) | 3751 | ShouldLogInSimpleFormat(const Expr *expr) | |
3759 | { | 3752 | { | |
3760 | return (expr->emode == VARE_WANTRES || | 3753 | return (expr->emode == VARE_WANTRES || | |
3761 | expr->emode == VARE_UNDEFERR) && | 3754 | expr->emode == VARE_UNDEFERR) && | |
3762 | expr->defined == DEF_REGULAR; | 3755 | expr->defined == DEF_REGULAR; | |
3763 | } | 3756 | } | |
3764 | 3757 | |||
3765 | static void | 3758 | static void | |
3766 | LogBeforeApply(const ModChain *ch, const char *mod) | 3759 | LogBeforeApply(const ModChain *ch, const char *mod) | |
3767 | { | 3760 | { | |
3768 | const Expr *expr = ch->expr; | 3761 | const Expr *expr = ch->expr; | |
3769 | bool is_single_char = mod[0] != '\0' && IsDelimiter(mod[1], ch); | 3762 | bool is_single_char = mod[0] != '\0' && IsDelimiter(mod[1], ch); | |
3770 | 3763 | |||
3771 | /* | 3764 | /* | |
3772 | * At this point, only the first character of the modifier can | 3765 | * At this point, only the first character of the modifier can | |
3773 | * be used since the end of the modifier is not yet known. | 3766 | * be used since the end of the modifier is not yet known. | |
3774 | */ | 3767 | */ | |
3775 | 3768 | |||
3776 | if (!Expr_ShouldEval(expr)) { | 3769 | if (!Expr_ShouldEval(expr)) { | |
3777 | debug_printf("Parsing modifier ${%s:%c%s}\n", | 3770 | debug_printf("Parsing modifier ${%s:%c%s}\n", | |
3778 | expr->name, mod[0], is_single_char ? "" : "..."); | 3771 | expr->name, mod[0], is_single_char ? "" : "..."); | |
3779 | return; | 3772 | return; | |
3780 | } | 3773 | } | |
3781 | 3774 | |||
3782 | if (ShouldLogInSimpleFormat(expr)) { | 3775 | if (ShouldLogInSimpleFormat(expr)) { | |
3783 | debug_printf( | 3776 | debug_printf( | |
3784 | "Evaluating modifier ${%s:%c%s} on value \"%s\"\n", | 3777 | "Evaluating modifier ${%s:%c%s} on value \"%s\"\n", | |
3785 | expr->name, mod[0], is_single_char ? "" : "...", | 3778 | expr->name, mod[0], is_single_char ? "" : "...", | |
3786 | Expr_Str(expr)); | 3779 | Expr_Str(expr)); | |
3787 | return; | 3780 | return; | |
3788 | } | 3781 | } | |
3789 | 3782 | |||
3790 | debug_printf( | 3783 | debug_printf( | |
3791 | "Evaluating modifier ${%s:%c%s} on value \"%s\" (%s, %s)\n", | 3784 | "Evaluating modifier ${%s:%c%s} on value \"%s\" (%s, %s)\n", | |
3792 | expr->name, mod[0], is_single_char ? "" : "...", Expr_Str(expr), | 3785 | expr->name, mod[0], is_single_char ? "" : "...", Expr_Str(expr), | |
3793 | VarEvalMode_Name[expr->emode], ExprDefined_Name[expr->defined]); | 3786 | VarEvalMode_Name[expr->emode], ExprDefined_Name[expr->defined]); | |
3794 | } | 3787 | } | |
3795 | 3788 | |||
3796 | static void | 3789 | static void | |
3797 | LogAfterApply(const ModChain *ch, const char *p, const char *mod) | 3790 | LogAfterApply(const ModChain *ch, const char *p, const char *mod) | |
3798 | { | 3791 | { | |
3799 | const Expr *expr = ch->expr; | 3792 | const Expr *expr = ch->expr; | |
3800 | const char *value = Expr_Str(expr); | 3793 | const char *value = Expr_Str(expr); | |
3801 | const char *quot = value == var_Error ? "" : "\""; | 3794 | const char *quot = value == var_Error ? "" : "\""; | |
3802 | 3795 | |||
3803 | if (ShouldLogInSimpleFormat(expr)) { | 3796 | if (ShouldLogInSimpleFormat(expr)) { | |
3804 | debug_printf("Result of ${%s:%.*s} is %s%s%s\n", | 3797 | debug_printf("Result of ${%s:%.*s} is %s%s%s\n", | |
3805 | expr->name, (int)(p - mod), mod, | 3798 | expr->name, (int)(p - mod), mod, | |
3806 | quot, value == var_Error ? "error" : value, quot); | 3799 | quot, value == var_Error ? "error" : value, quot); | |
3807 | return; | 3800 | return; | |
3808 | } | 3801 | } | |
3809 | 3802 | |||
3810 | debug_printf("Result of ${%s:%.*s} is %s%s%s (%s, %s)\n", | 3803 | debug_printf("Result of ${%s:%.*s} is %s%s%s (%s, %s)\n", | |
3811 | expr->name, (int)(p - mod), mod, | 3804 | expr->name, (int)(p - mod), mod, | |
3812 | quot, value == var_Error ? "error" : value, quot, | 3805 | quot, value == var_Error ? "error" : value, quot, | |
3813 | VarEvalMode_Name[expr->emode], | 3806 | VarEvalMode_Name[expr->emode], | |
3814 | ExprDefined_Name[expr->defined]); | 3807 | ExprDefined_Name[expr->defined]); | |
3815 | } | 3808 | } | |
3816 | 3809 | |||
3817 | static ApplyModifierResult | 3810 | static ApplyModifierResult | |
3818 | ApplyModifier(const char **pp, ModChain *ch) | 3811 | ApplyModifier(const char **pp, ModChain *ch) | |
3819 | { | 3812 | { | |
3820 | switch (**pp) { | 3813 | switch (**pp) { | |
3821 | case '!': | 3814 | case '!': | |
3822 | return ApplyModifier_ShellCommand(pp, ch); | 3815 | return ApplyModifier_ShellCommand(pp, ch); | |
3823 | case ':': | 3816 | case ':': | |
3824 | return ApplyModifier_Assign(pp, ch); | 3817 | return ApplyModifier_Assign(pp, ch); | |
3825 | case '?': | 3818 | case '?': | |
3826 | return ApplyModifier_IfElse(pp, ch); | 3819 | return ApplyModifier_IfElse(pp, ch); | |
3827 | case '@': | 3820 | case '@': | |
3828 | return ApplyModifier_Loop(pp, ch); | 3821 | return ApplyModifier_Loop(pp, ch); | |
3829 | case '[': | 3822 | case '[': | |
3830 | return ApplyModifier_Words(pp, ch); | 3823 | return ApplyModifier_Words(pp, ch); | |
3831 | case '_': | 3824 | case '_': | |
3832 | return ApplyModifier_Remember(pp, ch); | 3825 | return ApplyModifier_Remember(pp, ch); | |
3833 | case 'C': | 3826 | case 'C': | |
3834 | return ApplyModifier_Regex(pp, ch); | 3827 | return ApplyModifier_Regex(pp, ch); | |
3835 | case 'D': | 3828 | case 'D': | |
3836 | case 'U': | 3829 | case 'U': | |
3837 | return ApplyModifier_Defined(pp, ch); | 3830 | return ApplyModifier_Defined(pp, ch); | |
3838 | case 'E': | 3831 | case 'E': | |
3839 | return ApplyModifier_WordFunc(pp, ch, ModifyWord_Suffix); | 3832 | return ApplyModifier_WordFunc(pp, ch, ModifyWord_Suffix); | |
3840 | case 'g': | 3833 | case 'g': | |
3841 | case 'l': | 3834 | case 'l': | |
3842 | return ApplyModifier_Time(pp, ch); | 3835 | return ApplyModifier_Time(pp, ch); | |
3843 | case 'H': | 3836 | case 'H': | |
3844 | return ApplyModifier_WordFunc(pp, ch, ModifyWord_Head); | 3837 | return ApplyModifier_WordFunc(pp, ch, ModifyWord_Head); | |
3845 | case 'h': | 3838 | case 'h': | |
3846 | return ApplyModifier_Hash(pp, ch); | 3839 | return ApplyModifier_Hash(pp, ch); | |
3847 | case 'L': | 3840 | case 'L': | |
3848 | return ApplyModifier_Literal(pp, ch); | 3841 | return ApplyModifier_Literal(pp, ch); | |
3849 | case 'M': | 3842 | case 'M': | |
3850 | case 'N': | 3843 | case 'N': | |
3851 | return ApplyModifier_Match(pp, ch); | 3844 | return ApplyModifier_Match(pp, ch); | |
3852 | case 'm': | 3845 | case 'm': | |
3853 | return ApplyModifier_Mtime(pp, ch); | 3846 | return ApplyModifier_Mtime(pp, ch); | |
3854 | case 'O': | 3847 | case 'O': | |
3855 | return ApplyModifier_Order(pp, ch); | 3848 | return ApplyModifier_Order(pp, ch); | |
3856 | case 'P': | 3849 | case 'P': | |
3857 | return ApplyModifier_Path(pp, ch); | 3850 | return ApplyModifier_Path(pp, ch); | |
3858 | case 'Q': | 3851 | case 'Q': | |
3859 | case 'q': | 3852 | case 'q': | |
3860 | return ApplyModifier_Quote(pp, ch); | 3853 | return ApplyModifier_Quote(pp, ch); | |
3861 | case 'R': | 3854 | case 'R': | |
3862 | return ApplyModifier_WordFunc(pp, ch, ModifyWord_Root); | 3855 | return ApplyModifier_WordFunc(pp, ch, ModifyWord_Root); | |
3863 | case 'r': | 3856 | case 'r': | |
3864 | return ApplyModifier_Range(pp, ch); | 3857 | return ApplyModifier_Range(pp, ch); | |
3865 | case 'S': | 3858 | case 'S': | |
3866 | return ApplyModifier_Subst(pp, ch); | 3859 | return ApplyModifier_Subst(pp, ch); | |
3867 | case 's': | 3860 | case 's': | |
3868 | return ApplyModifier_SunShell(pp, ch); | 3861 | return ApplyModifier_SunShell(pp, ch); | |
3869 | case 'T': | 3862 | case 'T': | |
3870 | return ApplyModifier_WordFunc(pp, ch, ModifyWord_Tail); | 3863 | return ApplyModifier_WordFunc(pp, ch, ModifyWord_Tail); | |
3871 | case 't': | 3864 | case 't': | |
3872 | return ApplyModifier_To(pp, ch); | 3865 | return ApplyModifier_To(pp, ch); | |
3873 | case 'u': | 3866 | case 'u': | |
3874 | return ApplyModifier_Unique(pp, ch); | 3867 | return ApplyModifier_Unique(pp, ch); | |
3875 | default: | 3868 | default: | |
3876 | return AMR_UNKNOWN; | 3869 | return AMR_UNKNOWN; | |
3877 | } | 3870 | } | |
3878 | } | 3871 | } | |
3879 | 3872 | |||
3880 | static void ApplyModifiers(Expr *, const char **, char, char); | 3873 | static void ApplyModifiers(Expr *, const char **, char, char); | |
3881 | 3874 | |||
3882 | typedef enum ApplyModifiersIndirectResult { | 3875 | typedef enum ApplyModifiersIndirectResult { | |
3883 | /* The indirect modifiers have been applied successfully. */ | 3876 | /* The indirect modifiers have been applied successfully. */ | |
3884 | AMIR_CONTINUE, | 3877 | AMIR_CONTINUE, | |
3885 | /* Fall back to the SysV modifier. */ | 3878 | /* Fall back to the SysV modifier. */ | |
3886 | AMIR_SYSV, | 3879 | AMIR_SYSV, | |
3887 | /* Error out. */ | 3880 | /* Error out. */ | |
3888 | AMIR_OUT | 3881 | AMIR_OUT | |
3889 | } ApplyModifiersIndirectResult; | 3882 | } ApplyModifiersIndirectResult; | |
3890 | 3883 | |||
3891 | /* | 3884 | /* | |
3892 | * While expanding an expression, expand and apply indirect modifiers, | 3885 | * While expanding an expression, expand and apply indirect modifiers, | |
3893 | * such as in ${VAR:${M_indirect}}. | 3886 | * such as in ${VAR:${M_indirect}}. | |
3894 | * | 3887 | * | |
3895 | * All indirect modifiers of a group must come from a single | 3888 | * All indirect modifiers of a group must come from a single | |
3896 | * expression. ${VAR:${M1}} is valid but ${VAR:${M1}${M2}} is not. | 3889 | * expression. ${VAR:${M1}} is valid but ${VAR:${M1}${M2}} is not. | |
3897 | * | 3890 | * | |
3898 | * Multiple groups of indirect modifiers can be chained by separating them | 3891 | * Multiple groups of indirect modifiers can be chained by separating them | |
3899 | * with colons. ${VAR:${M1}:${M2}} contains 2 indirect modifiers. | 3892 | * with colons. ${VAR:${M1}:${M2}} contains 2 indirect modifiers. | |
3900 | * | 3893 | * | |
3901 | * If the expression is not followed by ch->endc or ':', fall | 3894 | * If the expression is not followed by ch->endc or ':', fall | |
3902 | * back to trying the SysV modifier, such as in ${VAR:${FROM}=${TO}}. | 3895 | * back to trying the SysV modifier, such as in ${VAR:${FROM}=${TO}}. | |
3903 | */ | 3896 | */ | |
3904 | static ApplyModifiersIndirectResult | 3897 | static ApplyModifiersIndirectResult | |
3905 | ApplyModifiersIndirect(ModChain *ch, const char **pp) | 3898 | ApplyModifiersIndirect(ModChain *ch, const char **pp) | |
3906 | { | 3899 | { | |
3907 | Expr *expr = ch->expr; | 3900 | Expr *expr = ch->expr; | |
3908 | const char *p = *pp; | 3901 | const char *p = *pp; | |
3909 | FStr mods = Var_Parse(&p, expr->scope, expr->emode); | 3902 | FStr mods = Var_Parse(&p, expr->scope, expr->emode); | |
3910 | /* TODO: handle errors */ | 3903 | /* TODO: handle errors */ | |
3911 | 3904 | |||
3912 | if (mods.str[0] != '\0' && !IsDelimiter(*p, ch)) { | 3905 | if (mods.str[0] != '\0' && !IsDelimiter(*p, ch)) { | |
3913 | FStr_Done(&mods); | 3906 | FStr_Done(&mods); | |
3914 | return AMIR_SYSV; | 3907 | return AMIR_SYSV; | |
3915 | } | 3908 | } | |
3916 | 3909 | |||
3917 | DEBUG3(VAR, "Indirect modifier \"%s\" from \"%.*s\"\n", | 3910 | DEBUG3(VAR, "Indirect modifier \"%s\" from \"%.*s\"\n", | |
3918 | mods.str, (int)(p - *pp), *pp); | 3911 | mods.str, (int)(p - *pp), *pp); | |
3919 | 3912 | |||
3920 | if (ModChain_ShouldEval(ch) && mods.str[0] != '\0') { | 3913 | if (ModChain_ShouldEval(ch) && mods.str[0] != '\0') { | |
3921 | const char *modsp = mods.str; | 3914 | const char *modsp = mods.str; | |
3922 | ApplyModifiers(expr, &modsp, '\0', '\0'); | 3915 | ApplyModifiers(expr, &modsp, '\0', '\0'); | |
3923 | if (Expr_Str(expr) == var_Error || *modsp != '\0') { | 3916 | if (Expr_Str(expr) == var_Error || *modsp != '\0') { | |
3924 | FStr_Done(&mods); | 3917 | FStr_Done(&mods); | |
3925 | *pp = p; | 3918 | *pp = p; | |
3926 | return AMIR_OUT; /* error already reported */ | 3919 | return AMIR_OUT; /* error already reported */ | |
3927 | } | 3920 | } | |
3928 | } | 3921 | } | |
3929 | FStr_Done(&mods); | 3922 | FStr_Done(&mods); | |
3930 | 3923 | |||
3931 | if (*p == ':') | 3924 | if (*p == ':') | |
3932 | p++; | 3925 | p++; | |
3933 | else if (*p == '\0' && ch->endc != '\0') { | 3926 | else if (*p == '\0' && ch->endc != '\0') { | |
3934 | Error("Unclosed expression after indirect modifier, " | 3927 | Error("Unclosed expression after indirect modifier, " | |
3935 | "expecting '%c' for variable \"%s\"", | 3928 | "expecting '%c' for variable \"%s\"", | |
3936 | ch->endc, expr->name); | 3929 | ch->endc, expr->name); | |
3937 | *pp = p; | 3930 | *pp = p; | |
3938 | return AMIR_OUT; | 3931 | return AMIR_OUT; | |
3939 | } | 3932 | } | |
3940 | 3933 | |||
3941 | *pp = p; | 3934 | *pp = p; | |
3942 | return AMIR_CONTINUE; | 3935 | return AMIR_CONTINUE; | |
3943 | } | 3936 | } | |
3944 | 3937 | |||
3945 | static ApplyModifierResult | 3938 | static ApplyModifierResult | |
3946 | ApplySingleModifier(const char **pp, ModChain *ch) | 3939 | ApplySingleModifier(const char **pp, ModChain *ch) | |
3947 | { | 3940 | { | |
3948 | ApplyModifierResult res; | 3941 | ApplyModifierResult res; | |
3949 | const char *mod = *pp; | 3942 | const char *mod = *pp; | |
3950 | const char *p = *pp; | 3943 | const char *p = *pp; | |
3951 | 3944 | |||
3952 | if (DEBUG(VAR)) | 3945 | if (DEBUG(VAR)) | |
3953 | LogBeforeApply(ch, mod); | 3946 | LogBeforeApply(ch, mod); | |
3954 | 3947 | |||
3955 | res = ApplyModifier(&p, ch); | 3948 | res = ApplyModifier(&p, ch); | |
3956 | 3949 | |||
3957 | if (res == AMR_UNKNOWN) { | 3950 | if (res == AMR_UNKNOWN) { | |
3958 | assert(p == mod); | 3951 | assert(p == mod); | |
3959 | res = ApplyModifier_SysV(&p, ch); | 3952 | res = ApplyModifier_SysV(&p, ch); | |
3960 | } | 3953 | } | |
3961 | 3954 | |||
3962 | if (res == AMR_UNKNOWN) { | 3955 | if (res == AMR_UNKNOWN) { | |
3963 | /* | 3956 | /* | |
3964 | * Guess the end of the current modifier. | 3957 | * Guess the end of the current modifier. | |
3965 | * XXX: Skipping the rest of the modifier hides | 3958 | * XXX: Skipping the rest of the modifier hides | |
3966 | * errors and leads to wrong results. | 3959 | * errors and leads to wrong results. | |
3967 | * Parsing should rather stop here. | 3960 | * Parsing should rather stop here. | |
3968 | */ | 3961 | */ | |
3969 | for (p++; !IsDelimiter(*p, ch); p++) | 3962 | for (p++; !IsDelimiter(*p, ch); p++) | |
3970 | continue; | 3963 | continue; | |
3971 | Parse_Error(PARSE_FATAL, "Unknown modifier \"%.*s\"", | 3964 | Parse_Error(PARSE_FATAL, "Unknown modifier \"%.*s\"", | |
3972 | (int)(p - mod), mod); | 3965 | (int)(p - mod), mod); | |
3973 | Expr_SetValueRefer(ch->expr, var_Error); | 3966 | Expr_SetValueRefer(ch->expr, var_Error); | |
3974 | } | 3967 | } | |
3975 | if (res == AMR_CLEANUP || res == AMR_BAD) { | 3968 | if (res == AMR_CLEANUP || res == AMR_BAD) { | |
3976 | *pp = p; | 3969 | *pp = p; | |
3977 | return res; | 3970 | return res; | |
3978 | } | 3971 | } | |
3979 | 3972 | |||
3980 | if (DEBUG(VAR)) | 3973 | if (DEBUG(VAR)) | |
3981 | LogAfterApply(ch, p, mod); | 3974 | LogAfterApply(ch, p, mod); | |
3982 | 3975 | |||
3983 | if (*p == '\0' && ch->endc != '\0') { | 3976 | if (*p == '\0' && ch->endc != '\0') { | |
3984 | Error( | 3977 | Error( | |
3985 | "Unclosed expression, expecting '%c' for " | 3978 | "Unclosed expression, expecting '%c' for " | |
3986 | "modifier \"%.*s\" of variable \"%s\" with value \"%s\"", | 3979 | "modifier \"%.*s\" of variable \"%s\" with value \"%s\"", | |
3987 | ch->endc, | 3980 | ch->endc, | |
3988 | (int)(p - mod), mod, | 3981 | (int)(p - mod), mod, | |
3989 | ch->expr->name, Expr_Str(ch->expr)); | 3982 | ch->expr->name, Expr_Str(ch->expr)); | |
3990 | } else if (*p == ':') { | 3983 | } else if (*p == ':') { | |
3991 | p++; | 3984 | p++; | |
3992 | } else if (opts.strict && *p != '\0' && *p != ch->endc) { | 3985 | } else if (opts.strict && *p != '\0' && *p != ch->endc) { | |
3993 | Parse_Error(PARSE_FATAL, | 3986 | Parse_Error(PARSE_FATAL, | |
3994 | "Missing delimiter ':' after modifier \"%.*s\"", | 3987 | "Missing delimiter ':' after modifier \"%.*s\"", | |
3995 | (int)(p - mod), mod); | 3988 | (int)(p - mod), mod); | |
3996 | /* | 3989 | /* | |
3997 | * TODO: propagate parse error to the enclosing | 3990 | * TODO: propagate parse error to the enclosing | |
3998 | * expression | 3991 | * expression | |
3999 | */ | 3992 | */ | |
4000 | } | 3993 | } | |
4001 | *pp = p; | 3994 | *pp = p; | |
4002 | return AMR_OK; | 3995 | return AMR_OK; | |
4003 | } | 3996 | } | |
4004 | 3997 | |||
4005 | #if __STDC_VERSION__ >= 199901L | 3998 | #if __STDC_VERSION__ >= 199901L | |
4006 | #define ModChain_Init(expr, startc, endc, sep, oneBigWord) \ | 3999 | #define ModChain_Init(expr, startc, endc, sep, oneBigWord) \ | |
4007 | (ModChain) { expr, startc, endc, sep, oneBigWord } | 4000 | (ModChain) { expr, startc, endc, sep, oneBigWord } | |
4008 | #else | 4001 | #else | |
4009 | MAKE_INLINE ModChain | 4002 | MAKE_INLINE ModChain | |
4010 | ModChain_Init(Expr *expr, char startc, char endc, char sep, bool oneBigWord) | 4003 | ModChain_Init(Expr *expr, char startc, char endc, char sep, bool oneBigWord) | |
4011 | { | 4004 | { | |
4012 | ModChain ch; | 4005 | ModChain ch; | |
4013 | ch.expr = expr; | 4006 | ch.expr = expr; | |
4014 | ch.startc = startc; | 4007 | ch.startc = startc; | |
4015 | ch.endc = endc; | 4008 | ch.endc = endc; | |
4016 | ch.sep = sep; | 4009 | ch.sep = sep; | |
4017 | ch.oneBigWord = oneBigWord; | 4010 | ch.oneBigWord = oneBigWord; | |
4018 | return ch; | 4011 | return ch; | |
4019 | } | 4012 | } | |
4020 | #endif | 4013 | #endif | |
4021 | 4014 | |||
4022 | /* Apply any modifiers (such as :Mpattern or :@var@loop@ or :Q or ::=value). */ | 4015 | /* Apply any modifiers (such as :Mpattern or :@var@loop@ or :Q or ::=value). */ | |
4023 | static void | 4016 | static void | |
4024 | ApplyModifiers( | 4017 | ApplyModifiers( | |
4025 | Expr *expr, | 4018 | Expr *expr, | |
4026 | const char **pp, /* the parsing position, updated upon return */ | 4019 | const char **pp, /* the parsing position, updated upon return */ | |
4027 | char startc, /* '(' or '{'; or '\0' for indirect modifiers */ | 4020 | char startc, /* '(' or '{'; or '\0' for indirect modifiers */ | |
4028 | char endc /* ')' or '}'; or '\0' for indirect modifiers */ | 4021 | char endc /* ')' or '}'; or '\0' for indirect modifiers */ | |
4029 | ) | 4022 | ) | |
4030 | { | 4023 | { | |
4031 | ModChain ch = ModChain_Init(expr, startc, endc, ' ', false); | 4024 | ModChain ch = ModChain_Init(expr, startc, endc, ' ', false); | |
4032 | const char *p; | 4025 | const char *p; | |
4033 | const char *mod; | 4026 | const char *mod; | |
4034 | 4027 | |||
4035 | assert(startc == '(' || startc == '{' || startc == '\0'); | 4028 | assert(startc == '(' || startc == '{' || startc == '\0'); | |
4036 | assert(endc == ')' || endc == '}' || endc == '\0'); | 4029 | assert(endc == ')' || endc == '}' || endc == '\0'); | |
4037 | assert(Expr_Str(expr) != NULL); | 4030 | assert(Expr_Str(expr) != NULL); | |
4038 | 4031 | |||
4039 | p = *pp; | 4032 | p = *pp; | |
4040 | 4033 | |||
4041 | if (*p == '\0' && endc != '\0') { | 4034 | if (*p == '\0' && endc != '\0') { | |
4042 | Error( | 4035 | Error( | |
4043 | "Unclosed expression, expecting '%c' for \"%s\"", | 4036 | "Unclosed expression, expecting '%c' for \"%s\"", | |
4044 | ch.endc, expr->name); | 4037 | ch.endc, expr->name); | |
4045 | goto cleanup; | 4038 | goto cleanup; | |
4046 | } | 4039 | } | |
4047 | 4040 | |||
4048 | while (*p != '\0' && *p != endc) { | 4041 | while (*p != '\0' && *p != endc) { | |
4049 | ApplyModifierResult res; | 4042 | ApplyModifierResult res; | |
4050 | 4043 | |||
4051 | if (*p == '$') { | 4044 | if (*p == '$') { | |
4052 | /* | 4045 | /* | |
4053 | * TODO: Only evaluate the expression once, no matter | 4046 | * TODO: Only evaluate the expression once, no matter | |
4054 | * whether it's an indirect modifier or the initial | 4047 | * whether it's an indirect modifier or the initial | |
4055 | * part of a SysV modifier. | 4048 | * part of a SysV modifier. | |
4056 | */ | 4049 | */ | |
4057 | ApplyModifiersIndirectResult amir = | 4050 | ApplyModifiersIndirectResult amir = | |
4058 | ApplyModifiersIndirect(&ch, &p); | 4051 | ApplyModifiersIndirect(&ch, &p); | |
4059 | if (amir == AMIR_CONTINUE) | 4052 | if (amir == AMIR_CONTINUE) | |
4060 | continue; | 4053 | continue; | |
4061 | if (amir == AMIR_OUT) | 4054 | if (amir == AMIR_OUT) | |
4062 | break; | 4055 | break; | |
4063 | } | 4056 | } | |
4064 | 4057 | |||
4065 | mod = p; | 4058 | mod = p; | |
4066 | 4059 | |||
4067 | res = ApplySingleModifier(&p, &ch); | 4060 | res = ApplySingleModifier(&p, &ch); | |
4068 | if (res == AMR_CLEANUP) | 4061 | if (res == AMR_CLEANUP) | |
4069 | goto cleanup; | 4062 | goto cleanup; | |
4070 | if (res == AMR_BAD) | 4063 | if (res == AMR_BAD) | |
4071 | goto bad_modifier; | 4064 | goto bad_modifier; | |
4072 | } | 4065 | } | |
4073 | 4066 | |||
4074 | *pp = p; | 4067 | *pp = p; | |
4075 | assert(Expr_Str(expr) != NULL); /* Use var_Error or varUndefined. */ | 4068 | assert(Expr_Str(expr) != NULL); /* Use var_Error or varUndefined. */ | |
4076 | return; | 4069 | return; | |
4077 | 4070 | |||
4078 | bad_modifier: | 4071 | bad_modifier: | |
4079 | /* Take a guess at where the modifier ends. */ | 4072 | /* Take a guess at where the modifier ends. */ | |
4080 | Error("Bad modifier \":%.*s\" for variable \"%s\"", | 4073 | Error("Bad modifier \":%.*s\" for variable \"%s\"", | |
4081 | (int)strcspn(mod, ":)}"), mod, expr->name); | 4074 | (int)strcspn(mod, ":)}"), mod, expr->name); | |
4082 | 4075 | |||
4083 | cleanup: | 4076 | cleanup: | |
4084 | /* | 4077 | /* | |
4085 | * TODO: Use p + strlen(p) instead, to stop parsing immediately. | 4078 | * TODO: Use p + strlen(p) instead, to stop parsing immediately. | |
4086 | * | 4079 | * | |
4087 | * In the unit tests, this generates a few shell commands with | 4080 | * In the unit tests, this generates a few shell commands with | |
4088 | * unbalanced quotes. Instead of producing these incomplete strings, | 4081 | * unbalanced quotes. Instead of producing these incomplete strings, | |
4089 | * commands with evaluation errors should not be run at all. | 4082 | * commands with evaluation errors should not be run at all. | |
4090 | * | 4083 | * | |
4091 | * To make that happen, Var_Subst must report the actual errors | 4084 | * To make that happen, Var_Subst must report the actual errors | |
4092 | * instead of returning the resulting string unconditionally. | 4085 | * instead of returning the resulting string unconditionally. | |
4093 | */ | 4086 | */ | |
4094 | *pp = p; | 4087 | *pp = p; | |
4095 | Expr_SetValueRefer(expr, var_Error); | 4088 | Expr_SetValueRefer(expr, var_Error); | |
4096 | } | 4089 | } | |
4097 | 4090 | |||
4098 | /* | 4091 | /* | |
4099 | * Only 4 of the 7 built-in local variables are treated specially as they are | 4092 | * Only 4 of the 7 built-in local variables are treated specially as they are | |
4100 | * the only ones that will be set when dynamic sources are expanded. | 4093 | * the only ones that will be set when dynamic sources are expanded. | |
4101 | */ | 4094 | */ | |
4102 | static bool | 4095 | static bool | |
4103 | VarnameIsDynamic(Substring varname) | 4096 | VarnameIsDynamic(Substring varname) | |
4104 | { | 4097 | { | |
4105 | const char *name; | 4098 | const char *name; | |
4106 | size_t len; | 4099 | size_t len; | |
4107 | 4100 | |||
4108 | name = varname.start; | 4101 | name = varname.start; | |
4109 | len = Substring_Length(varname); | 4102 | len = Substring_Length(varname); | |
4110 | if (len == 1 || (len == 2 && (name[1] == 'F' || name[1] == 'D'))) { | 4103 | if (len == 1 || (len == 2 && (name[1] == 'F' || name[1] == 'D'))) { | |
4111 | switch (name[0]) { | 4104 | switch (name[0]) { | |
4112 | case '@': | 4105 | case '@': | |
4113 | case '%': | 4106 | case '%': | |
4114 | case '*': | 4107 | case '*': | |
4115 | case '!': | 4108 | case '!': | |
4116 | return true; | 4109 | return true; | |
4117 | } | 4110 | } | |
4118 | return false; | 4111 | return false; | |
4119 | } | 4112 | } | |
4120 | 4113 | |||
4121 | if ((len == 7 || len == 8) && name[0] == '.' && ch_isupper(name[1])) { | 4114 | if ((len == 7 || len == 8) && name[0] == '.' && ch_isupper(name[1])) { | |
4122 | return Substring_Equals(varname, ".TARGET") || | 4115 | return Substring_Equals(varname, ".TARGET") || | |
4123 | Substring_Equals(varname, ".ARCHIVE") || | 4116 | Substring_Equals(varname, ".ARCHIVE") || | |
4124 | Substring_Equals(varname, ".PREFIX") || | 4117 | Substring_Equals(varname, ".PREFIX") || | |
4125 | Substring_Equals(varname, ".MEMBER"); | 4118 | Substring_Equals(varname, ".MEMBER"); | |
4126 | } | 4119 | } | |
4127 | 4120 | |||
4128 | return false; | 4121 | return false; | |
4129 | } | 4122 | } | |
4130 | 4123 | |||
4131 | static const char * | 4124 | static const char * | |
4132 | UndefinedShortVarValue(char varname, const GNode *scope) | 4125 | UndefinedShortVarValue(char varname, const GNode *scope) | |
4133 | { | 4126 | { | |
4134 | if (scope == SCOPE_CMDLINE || scope == SCOPE_GLOBAL) { | 4127 | if (scope == SCOPE_CMDLINE || scope == SCOPE_GLOBAL) { | |
4135 | /* | 4128 | /* | |
4136 | * If substituting a local variable in a non-local scope, | 4129 | * If substituting a local variable in a non-local scope, | |
4137 | * assume it's for dynamic source stuff. We have to handle | 4130 | * assume it's for dynamic source stuff. We have to handle | |
4138 | * this specially and return the longhand for the variable | 4131 | * this specially and return the longhand for the variable | |
4139 | * with the dollar sign escaped so it makes it back to the | 4132 | * with the dollar sign escaped so it makes it back to the | |
4140 | * caller. Only four of the local variables are treated | 4133 | * caller. Only four of the local variables are treated | |
4141 | * specially as they are the only four that will be set | 4134 | * specially as they are the only four that will be set | |
4142 | * when dynamic sources are expanded. | 4135 | * when dynamic sources are expanded. | |
4143 | */ | 4136 | */ | |
4144 | switch (varname) { | 4137 | switch (varname) { | |
4145 | case '@': | 4138 | case '@': | |
4146 | return "$(.TARGET)"; | 4139 | return "$(.TARGET)"; | |
4147 | case '%': | 4140 | case '%': | |
4148 | return "$(.MEMBER)"; | 4141 | return "$(.MEMBER)"; | |
4149 | case '*': | 4142 | case '*': | |
4150 | return "$(.PREFIX)"; | 4143 | return "$(.PREFIX)"; | |
4151 | case '!': | 4144 | case '!': | |
4152 | return "$(.ARCHIVE)"; | 4145 | return "$(.ARCHIVE)"; | |
4153 | } | 4146 | } | |
4154 | } | 4147 | } | |
4155 | return NULL; | 4148 | return NULL; | |
4156 | } | 4149 | } | |
4157 | 4150 | |||
4158 | /* | 4151 | /* | |
4159 | * Parse a variable name, until the end character or a colon, whichever | 4152 | * Parse a variable name, until the end character or a colon, whichever | |
4160 | * comes first. | 4153 | * comes first. | |
4161 | */ | 4154 | */ | |
4162 | static void | 4155 | static void | |
4163 | ParseVarname(const char **pp, char startc, char endc, | 4156 | ParseVarname(const char **pp, char startc, char endc, | |
4164 | GNode *scope, VarEvalMode emode, | 4157 | GNode *scope, VarEvalMode emode, | |
4165 | LazyBuf *buf) | 4158 | LazyBuf *buf) | |
4166 | { | 4159 | { | |
4167 | const char *p = *pp; | 4160 | const char *p = *pp; | |
4168 | int depth = 0; | 4161 | int depth = 0; | |
4169 | 4162 | |||
4170 | LazyBuf_Init(buf, p); | 4163 | LazyBuf_Init(buf, p); | |
4171 | 4164 | |||
4172 | while (*p != '\0') { | 4165 | while (*p != '\0') { | |
4173 | if ((*p == endc || *p == ':') && depth == 0) | 4166 | if ((*p == endc || *p == ':') && depth == 0) | |
4174 | break; | 4167 | break; | |
4175 | if (*p == startc) | 4168 | if (*p == startc) | |
4176 | depth++; | 4169 | depth++; | |
4177 | if (*p == endc) | 4170 | if (*p == endc) | |
4178 | depth--; | 4171 | depth--; | |
4179 | 4172 | |||
4180 | if (*p == '$') { | 4173 | if (*p == '$') { | |
4181 | FStr nested_val = Var_Parse(&p, scope, emode); | 4174 | FStr nested_val = Var_Parse(&p, scope, emode); | |
4182 | /* TODO: handle errors */ | 4175 | /* TODO: handle errors */ | |
4183 | LazyBuf_AddStr(buf, nested_val.str); | 4176 | LazyBuf_AddStr(buf, nested_val.str); | |
4184 | FStr_Done(&nested_val); | 4177 | FStr_Done(&nested_val); | |
4185 | } else { | 4178 | } else { | |
4186 | LazyBuf_Add(buf, *p); | 4179 | LazyBuf_Add(buf, *p); | |
4187 | p++; | 4180 | p++; | |
4188 | } | 4181 | } | |
4189 | } | 4182 | } | |
4190 | *pp = p; | 4183 | *pp = p; | |
4191 | } | 4184 | } | |
4192 | 4185 | |||
4193 | static bool | 4186 | static bool | |
4194 | IsShortVarnameValid(char varname, const char *start) | 4187 | IsShortVarnameValid(char varname, const char *start) | |
4195 | { | 4188 | { | |
4196 | if (varname != '$' && varname != ':' && varname != '}' && | 4189 | if (varname != '$' && varname != ':' && varname != '}' && | |
4197 | varname != ')' && varname != '\0') | 4190 | varname != ')' && varname != '\0') | |
4198 | return true; | 4191 | return true; | |
4199 | 4192 | |||
4200 | if (!opts.strict) | 4193 | if (!opts.strict) | |
4201 | return false; /* XXX: Missing error message */ | 4194 | return false; /* XXX: Missing error message */ | |
4202 | 4195 | |||
4203 | if (varname == '$' && save_dollars) | 4196 | if (varname == '$' && save_dollars) | |
4204 | Parse_Error(PARSE_FATAL, | 4197 | Parse_Error(PARSE_FATAL, | |
4205 | "To escape a dollar, use \\$, not $$, at \"%s\"", start); | 4198 | "To escape a dollar, use \\$, not $$, at \"%s\"", start); | |
4206 | else if (varname == '\0') | 4199 | else if (varname == '\0') | |
4207 | Parse_Error(PARSE_FATAL, "Dollar followed by nothing"); | 4200 | Parse_Error(PARSE_FATAL, "Dollar followed by nothing"); | |
4208 | else if (save_dollars) | 4201 | else if (save_dollars) | |
4209 | Parse_Error(PARSE_FATAL, | 4202 | Parse_Error(PARSE_FATAL, | |
4210 | "Invalid variable name '%c', at \"%s\"", varname, start); | 4203 | "Invalid variable name '%c', at \"%s\"", varname, start); | |
4211 | 4204 | |||
4212 | return false; | 4205 | return false; | |
4213 | } | 4206 | } | |
4214 | 4207 | |||
4215 | /* | 4208 | /* | |
4216 | * Parse a single-character variable name such as in $V or $@. | 4209 | * Parse a single-character variable name such as in $V or $@. | |
4217 | * Return whether to continue parsing. | 4210 | * Return whether to continue parsing. | |
4218 | */ | 4211 | */ | |
4219 | static bool | 4212 | static bool | |
4220 | ParseVarnameShort(char varname, const char **pp, GNode *scope, | 4213 | ParseVarnameShort(char varname, const char **pp, GNode *scope, | |
4221 | VarEvalMode emode, | 4214 | VarEvalMode emode, | |
4222 | const char **out_false_val, | 4215 | const char **out_false_val, | |
4223 | Var **out_true_var) | 4216 | Var **out_true_var) | |
4224 | { | 4217 | { | |
4225 | char name[2]; | 4218 | char name[2]; | |
4226 | Var *v; | 4219 | Var *v; | |
4227 | const char *val; | 4220 | const char *val; | |
4228 | 4221 | |||
4229 | if (!IsShortVarnameValid(varname, *pp)) { | 4222 | if (!IsShortVarnameValid(varname, *pp)) { | |
4230 | (*pp)++; /* only skip the '$' */ | 4223 | (*pp)++; /* only skip the '$' */ | |
4231 | *out_false_val = var_Error; | 4224 | *out_false_val = var_Error; | |
4232 | return false; | 4225 | return false; | |
4233 | } | 4226 | } | |
4234 | 4227 | |||
4235 | name[0] = varname; | 4228 | name[0] = varname; | |
4236 | name[1] = '\0'; | 4229 | name[1] = '\0'; | |
4237 | v = VarFind(name, scope, true); | 4230 | v = VarFind(name, scope, true); | |
4238 | if (v != NULL) { | 4231 | if (v != NULL) { | |
4239 | /* No need to advance *pp, the calling code handles this. */ | 4232 | /* No need to advance *pp, the calling code handles this. */ | |
4240 | *out_true_var = v; | 4233 | *out_true_var = v; | |
4241 | return true; | 4234 | return true; | |
4242 | } | 4235 | } | |
4243 | 4236 | |||
4244 | *pp += 2; | 4237 | *pp += 2; | |
4245 | 4238 | |||
4246 | val = UndefinedShortVarValue(varname, scope); | 4239 | val = UndefinedShortVarValue(varname, scope); | |
4247 | if (val == NULL) | 4240 | if (val == NULL) | |
4248 | val = emode == VARE_UNDEFERR ? var_Error : varUndefined; | 4241 | val = emode == VARE_UNDEFERR ? var_Error : varUndefined; | |
4249 | 4242 | |||
4250 | if (opts.strict && val == var_Error) { | 4243 | if (opts.strict && val == var_Error) { | |
4251 | Parse_Error(PARSE_FATAL, | 4244 | Parse_Error(PARSE_FATAL, | |
4252 | "Variable \"%s\" is undefined", name); | 4245 | "Variable \"%s\" is undefined", name); | |
4253 | } | 4246 | } | |
4254 | 4247 | |||
4255 | *out_false_val = val; | 4248 | *out_false_val = val; | |
4256 | return false; | 4249 | return false; | |
4257 | } | 4250 | } | |
4258 | 4251 | |||
4259 | /* Find variables like @F or <D. */ | 4252 | /* Find variables like @F or <D. */ | |
4260 | static Var * | 4253 | static Var * | |
4261 | FindLocalLegacyVar(Substring varname, GNode *scope, | 4254 | FindLocalLegacyVar(Substring varname, GNode *scope, | |
4262 | const char **out_extraModifiers) | 4255 | const char **out_extraModifiers) | |
4263 | { | 4256 | { | |
4264 | Var *v; | 4257 | Var *v; | |
4265 | 4258 | |||
4266 | /* Only resolve these variables if scope is a "real" target. */ | 4259 | /* Only resolve these variables if scope is a "real" target. */ | |
4267 | if (scope == SCOPE_CMDLINE || scope == SCOPE_GLOBAL) | 4260 | if (scope == SCOPE_CMDLINE || scope == SCOPE_GLOBAL) | |
4268 | return NULL; | 4261 | return NULL; | |
4269 | 4262 | |||
4270 | if (Substring_Length(varname) != 2) | 4263 | if (Substring_Length(varname) != 2) | |
4271 | return NULL; | 4264 | return NULL; | |
4272 | if (varname.start[1] != 'F' && varname.start[1] != 'D') | 4265 | if (varname.start[1] != 'F' && varname.start[1] != 'D') | |
4273 | return NULL; | 4266 | return NULL; | |
4274 | if (strchr("@%?*!<>", varname.start[0]) == NULL) | 4267 | if (strchr("@%?*!<>", varname.start[0]) == NULL) | |
4275 | return NULL; | 4268 | return NULL; | |
4276 | 4269 | |||
4277 | v = VarFindSubstring(Substring_Init(varname.start, varname.start + 1), | 4270 | v = VarFindSubstring(Substring_Init(varname.start, varname.start + 1), | |
4278 | scope, false); | 4271 | scope, false); | |
4279 | if (v == NULL) | 4272 | if (v == NULL) | |
4280 | return NULL; | 4273 | return NULL; | |
4281 | 4274 | |||
4282 | *out_extraModifiers = varname.start[1] == 'D' ? "H:" : "T:"; | 4275 | *out_extraModifiers = varname.start[1] == 'D' ? "H:" : "T:"; | |
4283 | return v; | 4276 | return v; | |
4284 | } | 4277 | } | |
4285 | 4278 | |||
4286 | static FStr | 4279 | static FStr | |
4287 | EvalUndefined(bool dynamic, const char *start, const char *p, | 4280 | EvalUndefined(bool dynamic, const char *start, const char *p, | |
4288 | Substring varname, VarEvalMode emode) | 4281 | Substring varname, VarEvalMode emode) | |
4289 | { | 4282 | { | |
4290 | if (dynamic) | 4283 | if (dynamic) | |
4291 | return FStr_InitOwn(bmake_strsedup(start, p)); | 4284 | return FStr_InitOwn(bmake_strsedup(start, p)); | |
4292 | 4285 | |||
4293 | if (emode == VARE_UNDEFERR && opts.strict) { | 4286 | if (emode == VARE_UNDEFERR && opts.strict) { | |
4294 | Parse_Error(PARSE_FATAL, | 4287 | Parse_Error(PARSE_FATAL, | |
4295 | "Variable \"%.*s\" is undefined", | 4288 | "Variable \"%.*s\" is undefined", | |
4296 | (int)Substring_Length(varname), varname.start); | 4289 | (int)Substring_Length(varname), varname.start); | |
4297 | return FStr_InitRefer(var_Error); | 4290 | return FStr_InitRefer(var_Error); | |
4298 | } | 4291 | } | |
4299 | 4292 | |||
4300 | return FStr_InitRefer( | 4293 | return FStr_InitRefer( | |
4301 | emode == VARE_UNDEFERR ? var_Error : varUndefined); | 4294 | emode == VARE_UNDEFERR ? var_Error : varUndefined); | |
4302 | } | 4295 | } | |
4303 | 4296 | |||
4304 | /* | 4297 | /* | |
4305 | * Parse a long variable name enclosed in braces or parentheses such as $(VAR) | 4298 | * Parse a long variable name enclosed in braces or parentheses such as $(VAR) | |
4306 | * or ${VAR}, up to the closing brace or parenthesis, or in the case of | 4299 | * or ${VAR}, up to the closing brace or parenthesis, or in the case of | |
4307 | * ${VAR:Modifiers}, up to the ':' that starts the modifiers. | 4300 | * ${VAR:Modifiers}, up to the ':' that starts the modifiers. | |
4308 | * Return whether to continue parsing. | 4301 | * Return whether to continue parsing. | |
4309 | */ | 4302 | */ | |
4310 | static bool | 4303 | static bool | |
4311 | ParseVarnameLong( | 4304 | ParseVarnameLong( | |
4312 | const char **pp, | 4305 | const char **pp, | |
4313 | char startc, | 4306 | char startc, | |
4314 | GNode *scope, | 4307 | GNode *scope, | |
4315 | VarEvalMode emode, | 4308 | VarEvalMode emode, | |
4316 | 4309 | |||
4317 | const char **out_false_pp, | 4310 | const char **out_false_pp, | |
4318 | FStr *out_false_val, | 4311 | FStr *out_false_val, | |
4319 | 4312 | |||
4320 | char *out_true_endc, | 4313 | char *out_true_endc, | |
4321 | Var **out_true_v, | 4314 | Var **out_true_v, | |
4322 | bool *out_true_haveModifier, | 4315 | bool *out_true_haveModifier, | |
4323 | const char **out_true_extraModifiers, | 4316 | const char **out_true_extraModifiers, | |
4324 | bool *out_true_dynamic, | 4317 | bool *out_true_dynamic, | |
4325 | ExprDefined *out_true_exprDefined | 4318 | ExprDefined *out_true_exprDefined | |
4326 | ) | 4319 | ) | |
4327 | { | 4320 | { | |
4328 | LazyBuf varname; | 4321 | LazyBuf varname; | |
4329 | Substring name; | 4322 | Substring name; | |
4330 | Var *v; | 4323 | Var *v; | |
4331 | bool haveModifier; | 4324 | bool haveModifier; | |
4332 | bool dynamic = false; | 4325 | bool dynamic = false; | |
4333 | 4326 | |||
4334 | const char *p = *pp; | 4327 | const char *p = *pp; | |
4335 | const char *start = p; | 4328 | const char *start = p; | |
4336 | char endc = startc == '(' ? ')' : '}'; | 4329 | char endc = startc == '(' ? ')' : '}'; | |
4337 | 4330 | |||
4338 | p += 2; /* skip "${" or "$(" or "y(" */ | 4331 | p += 2; /* skip "${" or "$(" or "y(" */ | |
4339 | ParseVarname(&p, startc, endc, scope, emode, &varname); | 4332 | ParseVarname(&p, startc, endc, scope, emode, &varname); | |
4340 | name = LazyBuf_Get(&varname); | 4333 | name = LazyBuf_Get(&varname); | |
4341 | 4334 | |||
4342 | if (*p == ':') | 4335 | if (*p == ':') | |
4343 | haveModifier = true; | 4336 | haveModifier = true; | |
4344 | else if (*p == endc) | 4337 | else if (*p == endc) | |
4345 | haveModifier = false; | 4338 | haveModifier = false; | |
4346 | else { | 4339 | else { | |
4347 | Parse_Error(PARSE_FATAL, "Unclosed variable \"%.*s\"", | 4340 | Parse_Error(PARSE_FATAL, "Unclosed variable \"%.*s\"", | |
4348 | (int)Substring_Length(name), name.start); | 4341 | (int)Substring_Length(name), name.start); | |
4349 | LazyBuf_Done(&varname); | 4342 | LazyBuf_Done(&varname); | |
4350 | *out_false_pp = p; | 4343 | *out_false_pp = p; | |
4351 | *out_false_val = FStr_InitRefer(var_Error); | 4344 | *out_false_val = FStr_InitRefer(var_Error); | |
4352 | return false; | 4345 | return false; | |
4353 | } | 4346 | } | |
4354 | 4347 | |||
4355 | v = VarFindSubstring(name, scope, true); | 4348 | v = VarFindSubstring(name, scope, true); | |
4356 | 4349 | |||
4357 | /* | 4350 | /* | |
4358 | * At this point, p points just after the variable name, either at | 4351 | * At this point, p points just after the variable name, either at | |
4359 | * ':' or at endc. | 4352 | * ':' or at endc. | |
4360 | */ | 4353 | */ | |
4361 | 4354 | |||
4362 | if (v == NULL && Substring_Equals(name, ".SUFFIXES")) { | 4355 | if (v == NULL && Substring_Equals(name, ".SUFFIXES")) { | |
4363 | char *suffixes = Suff_NamesStr(); | 4356 | char *suffixes = Suff_NamesStr(); | |
4364 | v = VarNew(FStr_InitRefer(".SUFFIXES"), suffixes, | 4357 | v = VarNew(FStr_InitRefer(".SUFFIXES"), suffixes, | |
4365 | true, false, true); | 4358 | true, false, true); | |
4366 | free(suffixes); | 4359 | free(suffixes); | |
4367 | } else if (v == NULL) | 4360 | } else if (v == NULL) | |
4368 | v = FindLocalLegacyVar(name, scope, out_true_extraModifiers); | 4361 | v = FindLocalLegacyVar(name, scope, out_true_extraModifiers); | |
4369 | 4362 | |||
4370 | if (v == NULL) { | 4363 | if (v == NULL) { | |
4371 | /* | 4364 | /* | |
4372 | * Defer expansion of dynamic variables if they appear in | 4365 | * Defer expansion of dynamic variables if they appear in | |
4373 | * non-local scope since they are not defined there. | 4366 | * non-local scope since they are not defined there. | |
4374 | */ | 4367 | */ | |
4375 | dynamic = VarnameIsDynamic(name) && | 4368 | dynamic = VarnameIsDynamic(name) && | |
4376 | (scope == SCOPE_CMDLINE || scope == SCOPE_GLOBAL); | 4369 | (scope == SCOPE_CMDLINE || scope == SCOPE_GLOBAL); | |
4377 | 4370 | |||
4378 | if (!haveModifier) { | 4371 | if (!haveModifier) { | |
4379 | p++; /* skip endc */ | 4372 | p++; /* skip endc */ | |
4380 | *out_false_pp = p; | 4373 | *out_false_pp = p; | |
4381 | *out_false_val = EvalUndefined(dynamic, start, p, | 4374 | *out_false_val = EvalUndefined(dynamic, start, p, | |
4382 | name, emode); | 4375 | name, emode); | |
4383 | LazyBuf_Done(&varname); | 4376 | LazyBuf_Done(&varname); | |
4384 | return false; | 4377 | return false; | |
4385 | } | 4378 | } | |
4386 | 4379 | |||
4387 | /* | 4380 | /* | |
4388 | * The expression is based on an undefined variable. | 4381 | * The expression is based on an undefined variable. | |
4389 | * Nevertheless it needs a Var, for modifiers that access the | 4382 | * Nevertheless it needs a Var, for modifiers that access the | |
4390 | * variable name, such as :L or :?. | 4383 | * variable name, such as :L or :?. | |
4391 | * | 4384 | * | |
4392 | * Most modifiers leave this expression in the "undefined" | 4385 | * Most modifiers leave this expression in the "undefined" | |
4393 | * state (DEF_UNDEF), only a few modifiers like :D, :U, :L, | 4386 | * state (DEF_UNDEF), only a few modifiers like :D, :U, :L, | |
4394 | * :P turn this undefined expression into a defined | 4387 | * :P turn this undefined expression into a defined | |
4395 | * expression (DEF_DEFINED). | 4388 | * expression (DEF_DEFINED). | |
4396 | * | 4389 | * | |
4397 | * In the end, after applying all modifiers, if the expression | 4390 | * In the end, after applying all modifiers, if the expression | |
4398 | * is still undefined, Var_Parse will return an empty string | 4391 | * is still undefined, Var_Parse will return an empty string | |
4399 | * instead of the actually computed value. | 4392 | * instead of the actually computed value. | |
4400 | */ | 4393 | */ | |
4401 | v = VarNew(LazyBuf_DoneGet(&varname), "", | 4394 | v = VarNew(LazyBuf_DoneGet(&varname), "", | |
4402 | true, false, false); | 4395 | true, false, false); | |
4403 | *out_true_exprDefined = DEF_UNDEF; | 4396 | *out_true_exprDefined = DEF_UNDEF; | |
4404 | } else | 4397 | } else | |
4405 | LazyBuf_Done(&varname); | 4398 | LazyBuf_Done(&varname); | |
4406 | 4399 | |||
4407 | *pp = p; | 4400 | *pp = p; | |
4408 | *out_true_endc = endc; | 4401 | *out_true_endc = endc; | |
4409 | *out_true_v = v; | 4402 | *out_true_v = v; | |
4410 | *out_true_haveModifier = haveModifier; | 4403 | *out_true_haveModifier = haveModifier; | |
4411 | *out_true_dynamic = dynamic; | 4404 | *out_true_dynamic = dynamic; | |
4412 | return true; | 4405 | return true; | |
4413 | } | 4406 | } | |
4414 | 4407 | |||
4415 | #if __STDC_VERSION__ >= 199901L | 4408 | #if __STDC_VERSION__ >= 199901L | |
4416 | #define Expr_Init(name, value, emode, scope, defined) \ | 4409 | #define Expr_Init(name, value, emode, scope, defined) \ | |
4417 | (Expr) { name, value, emode, scope, defined } | 4410 | (Expr) { name, value, emode, scope, defined } | |
4418 | #else | 4411 | #else | |
4419 | MAKE_INLINE Expr | 4412 | MAKE_INLINE Expr | |
4420 | Expr_Init(const char *name, FStr value, | 4413 | Expr_Init(const char *name, FStr value, | |
4421 | VarEvalMode emode, GNode *scope, ExprDefined defined) | 4414 | VarEvalMode emode, GNode *scope, ExprDefined defined) | |
4422 | { | 4415 | { | |
4423 | Expr expr; | 4416 | Expr expr; | |
4424 | 4417 | |||
4425 | expr.name = name; | 4418 | expr.name = name; | |
4426 | expr.value = value; | 4419 | expr.value = value; | |
4427 | expr.emode = emode; | 4420 | expr.emode = emode; | |
4428 | expr.scope = scope; | 4421 | expr.scope = scope; | |
4429 | expr.defined = defined; | 4422 | expr.defined = defined; | |
4430 | return expr; | 4423 | return expr; | |
4431 | } | 4424 | } | |
4432 | #endif | 4425 | #endif | |
4433 | 4426 | |||
4434 | /* | 4427 | /* | |
4435 | * Expressions of the form ${:U...} with a trivial value are often generated | 4428 | * Expressions of the form ${:U...} with a trivial value are often generated | |
4436 | * by .for loops and are boring, so evaluate them without debug logging. | 4429 | * by .for loops and are boring, so evaluate them without debug logging. | |
4437 | */ | 4430 | */ | |
4438 | static bool | 4431 | static bool | |
4439 | Var_Parse_FastLane(const char **pp, VarEvalMode emode, FStr *out_value) | 4432 | Var_Parse_FastLane(const char **pp, VarEvalMode emode, FStr *out_value) | |
4440 | { | 4433 | { | |
4441 | const char *p; | 4434 | const char *p; | |
4442 | 4435 | |||
4443 | p = *pp; | 4436 | p = *pp; | |
4444 | if (!(p[0] == '$' && p[1] == '{' && p[2] == ':' && p[3] == 'U')) | 4437 | if (!(p[0] == '$' && p[1] == '{' && p[2] == ':' && p[3] == 'U')) | |
4445 | return false; | 4438 | return false; | |
4446 | 4439 | |||
4447 | p += 4; | 4440 | p += 4; | |
4448 | while (*p != '$' && *p != '{' && *p != ':' && *p != '\\' && | 4441 | while (*p != '$' && *p != '{' && *p != ':' && *p != '\\' && | |
4449 | *p != '}' && *p != '\0') | 4442 | *p != '}' && *p != '\0') | |
4450 | p++; | 4443 | p++; | |
4451 | if (*p != '}') | 4444 | if (*p != '}') | |
4452 | return false; | 4445 | return false; | |
4453 | 4446 | |||
4454 | *out_value = emode == VARE_PARSE_ONLY | 4447 | *out_value = emode == VARE_PARSE_ONLY | |
4455 | ? FStr_InitRefer("") | 4448 | ? FStr_InitRefer("") | |
4456 | : FStr_InitOwn(bmake_strsedup(*pp + 4, p)); | 4449 | : FStr_InitOwn(bmake_strsedup(*pp + 4, p)); | |
4457 | *pp = p + 1; | 4450 | *pp = p + 1; | |
4458 | return true; | 4451 | return true; | |
4459 | } | 4452 | } | |
4460 | 4453 | |||
4461 | /* | 4454 | /* | |
4462 | * Given the start of an expression (such as $v, $(VAR), ${VAR:Mpattern}), | 4455 | * Given the start of an expression (such as $v, $(VAR), ${VAR:Mpattern}), | |
4463 | * extract the variable name and the modifiers, if any. While parsing, apply | 4456 | * extract the variable name and the modifiers, if any. While parsing, apply | |
4464 | * the modifiers to the value of the expression. | 4457 | * the modifiers to the value of the expression. | |
4465 | * | 4458 | * | |
4466 | * Input: | 4459 | * Input: | |
4467 | * *pp The string to parse. | 4460 | * *pp The string to parse. | |
4468 | * When called from CondParser_FuncCallEmpty, it can | 4461 | * When called from CondParser_FuncCallEmpty, it can | |
4469 | * also point to the "y" of "empty(VARNAME:Modifiers)". | 4462 | * also point to the "y" of "empty(VARNAME:Modifiers)". | |
4470 | * scope The scope for finding variables. | 4463 | * scope The scope for finding variables. | |
4471 | * emode Controls the exact details of parsing and evaluation. | 4464 | * emode Controls the exact details of parsing and evaluation. | |
4472 | * | 4465 | * | |
4473 | * Output: | 4466 | * Output: | |
4474 | * *pp The position where to continue parsing. | 4467 | * *pp The position where to continue parsing. | |
4475 | * TODO: After a parse error, the value of *pp is | 4468 | * TODO: After a parse error, the value of *pp is | |
4476 | * unspecified. It may not have been updated at all, | 4469 | * unspecified. It may not have been updated at all, | |
4477 | * point to some random character in the string, to the | 4470 | * point to some random character in the string, to the | |
4478 | * location of the parse error, or at the end of the | 4471 | * location of the parse error, or at the end of the | |
4479 | * string. | 4472 | * string. | |
4480 | * return The value of the expression, never NULL. | 4473 | * return The value of the expression, never NULL. | |
4481 | * return var_Error if there was a parse error. | 4474 | * return var_Error if there was a parse error. | |
4482 | * return var_Error if the base variable of the expression was | 4475 | * return var_Error if the base variable of the expression was | |
4483 | * undefined, emode is VARE_UNDEFERR, and none of | 4476 | * undefined, emode is VARE_UNDEFERR, and none of | |
4484 | * the modifiers turned the undefined expression into a | 4477 | * the modifiers turned the undefined expression into a | |
4485 | * defined expression. | 4478 | * defined expression. | |
4486 | * XXX: It is not guaranteed that an error message has | 4479 | * XXX: It is not guaranteed that an error message has | |
4487 | * been printed. | 4480 | * been printed. | |
4488 | * return varUndefined if the base variable of the expression | 4481 | * return varUndefined if the base variable of the expression | |
4489 | * was undefined, emode was not VARE_UNDEFERR, | 4482 | * was undefined, emode was not VARE_UNDEFERR, | |
4490 | * and none of the modifiers turned the undefined | 4483 | * and none of the modifiers turned the undefined | |
4491 | * expression into a defined expression. | 4484 | * expression into a defined expression. | |
4492 | * XXX: It is not guaranteed that an error message has | 4485 | * XXX: It is not guaranteed that an error message has | |
4493 | * been printed. | 4486 | * been printed. | |
4494 | */ | 4487 | */ | |
4495 | FStr | 4488 | FStr | |
4496 | Var_Parse(const char **pp, GNode *scope, VarEvalMode emode) | 4489 | Var_Parse(const char **pp, GNode *scope, VarEvalMode emode) | |
4497 | { | 4490 | { | |
4498 | const char *p = *pp; | 4491 | const char *p = *pp; | |
4499 | const char *const start = p; | 4492 | const char *const start = p; | |
4500 | bool haveModifier; /* true for ${VAR:...}, false for ${VAR} */ | 4493 | bool haveModifier; /* true for ${VAR:...}, false for ${VAR} */ | |
4501 | char startc; /* the actual '{' or '(' or '\0' */ | 4494 | char startc; /* the actual '{' or '(' or '\0' */ | |
4502 | char endc; /* the expected '}' or ')' or '\0' */ | 4495 | char endc; /* the expected '}' or ')' or '\0' */ | |
4503 | /* | 4496 | /* | |
4504 | * true if the expression is based on one of the 7 predefined | 4497 | * true if the expression is based on one of the 7 predefined | |
4505 | * variables that are local to a target, and the expression is | 4498 | * variables that are local to a target, and the expression is | |
4506 | * expanded in a non-local scope. The result is the text of the | 4499 | * expanded in a non-local scope. The result is the text of the | |
4507 | * expression, unaltered. This is needed to support dynamic sources. | 4500 | * expression, unaltered. This is needed to support dynamic sources. | |
4508 | */ | 4501 | */ | |
4509 | bool dynamic; | 4502 | bool dynamic; | |
4510 | const char *extramodifiers; | 4503 | const char *extramodifiers; | |
4511 | Var *v; | 4504 | Var *v; | |
4512 | Expr expr = Expr_Init(NULL, FStr_InitRefer(NULL), emode, | 4505 | Expr expr = Expr_Init(NULL, FStr_InitRefer(NULL), emode, | |
4513 | scope, DEF_REGULAR); | 4506 | scope, DEF_REGULAR); | |
4514 | FStr val; | 4507 | FStr val; | |
4515 | 4508 | |||
4516 | if (Var_Parse_FastLane(pp, emode, &val)) | 4509 | if (Var_Parse_FastLane(pp, emode, &val)) | |
4517 | return val; | 4510 | return val; | |
4518 | 4511 | |||
4519 | DEBUG2(VAR, "Var_Parse: %s (%s)\n", start, VarEvalMode_Name[emode]); | 4512 | DEBUG2(VAR, "Var_Parse: %s (%s)\n", start, VarEvalMode_Name[emode]); | |
4520 | 4513 | |||
4521 | val = FStr_InitRefer(NULL); | 4514 | val = FStr_InitRefer(NULL); | |
4522 | extramodifiers = NULL; /* extra modifiers to apply first */ | 4515 | extramodifiers = NULL; /* extra modifiers to apply first */ | |
4523 | dynamic = false; | 4516 | dynamic = false; | |
4524 | 4517 | |||
4525 | endc = '\0'; /* Appease GCC. */ | 4518 | endc = '\0'; /* Appease GCC. */ | |
4526 | 4519 | |||
4527 | startc = p[1]; | 4520 | startc = p[1]; | |
4528 | if (startc != '(' && startc != '{') { | 4521 | if (startc != '(' && startc != '{') { | |
4529 | if (!ParseVarnameShort(startc, pp, scope, emode, &val.str, &v)) | 4522 | if (!ParseVarnameShort(startc, pp, scope, emode, &val.str, &v)) | |
4530 | return val; | 4523 | return val; | |
4531 | haveModifier = false; | 4524 | haveModifier = false; | |
4532 | p++; | 4525 | p++; | |
4533 | } else { | 4526 | } else { | |
4534 | if (!ParseVarnameLong(&p, startc, scope, emode, | 4527 | if (!ParseVarnameLong(&p, startc, scope, emode, | |
4535 | pp, &val, | 4528 | pp, &val, | |
4536 | &endc, &v, &haveModifier, &extramodifiers, | 4529 | &endc, &v, &haveModifier, &extramodifiers, | |
4537 | &dynamic, &expr.defined)) | 4530 | &dynamic, &expr.defined)) | |
4538 | return val; | 4531 | return val; | |
4539 | } | 4532 | } | |
4540 | 4533 | |||
4541 | expr.name = v->name.str; | 4534 | expr.name = v->name.str; | |
4542 | if (v->inUse && VarEvalMode_ShouldEval(emode)) { | 4535 | if (v->inUse && VarEvalMode_ShouldEval(emode)) { | |
4543 | if (scope->fname != NULL) { | 4536 | if (scope->fname != NULL) { | |
4544 | fprintf(stderr, "In a command near "); | 4537 | fprintf(stderr, "In a command near "); | |
4545 | PrintLocation(stderr, false, scope); | 4538 | PrintLocation(stderr, false, scope); | |
4546 | } | 4539 | } | |
4547 | Fatal("Variable %s is recursive.", v->name.str); | 4540 | Fatal("Variable %s is recursive.", v->name.str); | |
4548 | } | 4541 | } | |
4549 | 4542 | |||
4550 | /* | 4543 | /* | |
4551 | * FIXME: This assignment creates an alias to the current value of the | 4544 | * FIXME: This assignment creates an alias to the current value of the | |
4552 | * variable. This means that as long as the value of the expression | 4545 | * variable. This means that as long as the value of the expression | |
4553 | * stays the same, the value of the variable must not change, and the | 4546 | * stays the same, the value of the variable must not change, and the | |
4554 | * variable must not be deleted. Using the ':@' modifier, it is | 4547 | * variable must not be deleted. Using the ':@' modifier, it is | |
4555 | * possible (since var.c 1.212 from 2017-02-01) to delete the variable | 4548 | * possible (since var.c 1.212 from 2017-02-01) to delete the variable | |
4556 | * while its value is still being used: | 4549 | * while its value is still being used: | |
4557 | * | 4550 | * | |
4558 | * VAR= value | 4551 | * VAR= value | |
4559 | * _:= ${VAR:${:U@VAR@loop@}:S,^,prefix,} | 4552 | * _:= ${VAR:${:U@VAR@loop@}:S,^,prefix,} | |
4560 | * | 4553 | * | |
4561 | * The same effect might be achievable using the '::=' or the ':_' | 4554 | * The same effect might be achievable using the '::=' or the ':_' | |
4562 | * modifiers. | 4555 | * modifiers. | |
4563 | * | 4556 | * | |
4564 | * At the bottom of this function, the resulting value is compared to | 4557 | * At the bottom of this function, the resulting value is compared to | |
4565 | * the then-current value of the variable. This might also invoke | 4558 | * the then-current value of the variable. This might also invoke | |
4566 | * undefined behavior. | 4559 | * undefined behavior. | |
4567 | */ | 4560 | */ | |
4568 | expr.value = FStr_InitRefer(v->val.data); | 4561 | expr.value = FStr_InitRefer(v->val.data); | |
4569 | 4562 | |||
4570 | if (expr.name[0] != '\0') | 4563 | if (expr.name[0] != '\0') | |
4571 | EvalStack_Push(NULL, NULL, expr.name); | 4564 | EvalStack_Push(NULL, NULL, expr.name); | |
4572 | else | 4565 | else | |
4573 | EvalStack_Push(NULL, start, NULL); | 4566 | EvalStack_Push(NULL, start, NULL); | |
4574 | 4567 | |||
4575 | /* | 4568 | /* | |
4576 | * Before applying any modifiers, expand any nested expressions from | 4569 | * Before applying any modifiers, expand any nested expressions from | |
4577 | * the variable value. | 4570 | * the variable value. | |
4578 | */ | 4571 | */ | |
4579 | if (VarEvalMode_ShouldEval(emode) && | 4572 | if (VarEvalMode_ShouldEval(emode) && | |
4580 | strchr(Expr_Str(&expr), '$') != NULL) { | 4573 | strchr(Expr_Str(&expr), '$') != NULL) { | |
4581 | char *expanded; | 4574 | char *expanded; | |
4582 | VarEvalMode nested_emode = emode; | 4575 | VarEvalMode nested_emode = emode; | |
4583 | if (opts.strict) | 4576 | if (opts.strict) | |
4584 | nested_emode = VarEvalMode_UndefOk(nested_emode); | 4577 | nested_emode = VarEvalMode_UndefOk(nested_emode); | |
4585 | v->inUse = true; | 4578 | v->inUse = true; | |
4586 | expanded = Var_Subst(Expr_Str(&expr), scope, nested_emode); | 4579 | expanded = Var_Subst(Expr_Str(&expr), scope, nested_emode); | |
4587 | v->inUse = false; | 4580 | v->inUse = false; | |
4588 | /* TODO: handle errors */ | 4581 | /* TODO: handle errors */ | |
4589 | Expr_SetValueOwn(&expr, expanded); | 4582 | Expr_SetValueOwn(&expr, expanded); | |
4590 | } | 4583 | } | |
4591 | 4584 | |||
4592 | if (extramodifiers != NULL) { | 4585 | if (extramodifiers != NULL) { | |
4593 | const char *em = extramodifiers; | 4586 | const char *em = extramodifiers; | |
4594 | ApplyModifiers(&expr, &em, '\0', '\0'); | 4587 | ApplyModifiers(&expr, &em, '\0', '\0'); | |
4595 | } | 4588 | } | |
4596 | 4589 | |||
4597 | if (haveModifier) { | 4590 | if (haveModifier) { | |
4598 | p++; /* Skip initial colon. */ | 4591 | p++; /* Skip initial colon. */ | |
4599 | ApplyModifiers(&expr, &p, startc, endc); | 4592 | ApplyModifiers(&expr, &p, startc, endc); | |
4600 | } | 4593 | } | |
4601 | 4594 | |||
4602 | if (*p != '\0') /* Skip past endc if possible. */ | 4595 | if (*p != '\0') /* Skip past endc if possible. */ | |
4603 | p++; | 4596 | p++; | |
4604 | 4597 | |||
4605 | *pp = p; | 4598 | *pp = p; | |
4606 | 4599 | |||
4607 | if (expr.defined == DEF_UNDEF) { | 4600 | if (expr.defined == DEF_UNDEF) { | |
4608 | if (dynamic) | 4601 | if (dynamic) | |
4609 | Expr_SetValueOwn(&expr, bmake_strsedup(start, p)); | 4602 | Expr_SetValueOwn(&expr, bmake_strsedup(start, p)); | |
4610 | else { | 4603 | else { | |
4611 | /* | 4604 | /* | |
4612 | * The expression is still undefined, therefore | 4605 | * The expression is still undefined, therefore | |
4613 | * discard the actual value and return an error marker | 4606 | * discard the actual value and return an error marker | |
4614 | * instead. | 4607 | * instead. | |
4615 | */ | 4608 | */ | |
4616 | Expr_SetValueRefer(&expr, | 4609 | Expr_SetValueRefer(&expr, | |
4617 | emode == VARE_UNDEFERR | 4610 | emode == VARE_UNDEFERR | |
4618 | ? var_Error : varUndefined); | 4611 | ? var_Error : varUndefined); | |
4619 | } | 4612 | } | |
4620 | } | 4613 | } | |
4621 | 4614 | |||
4622 | if (v->shortLived) { | 4615 | if (v->shortLived) { | |
4623 | if (expr.value.str == v->val.data) { | 4616 | if (expr.value.str == v->val.data) { | |
4624 | /* move ownership */ | 4617 | /* move ownership */ | |
4625 | expr.value.freeIt = v->val.data; | 4618 | expr.value.freeIt = v->val.data; | |
4626 | v->val.data = NULL; | 4619 | v->val.data = NULL; | |
4627 | } | 4620 | } | |
4628 | VarFreeShortLived(v); | 4621 | VarFreeShortLived(v); | |
4629 | } | 4622 | } | |
4630 | 4623 | |||
4631 | EvalStack_Pop(); | 4624 | EvalStack_Pop(); | |
4632 | return expr.value; | 4625 | return expr.value; | |
4633 | } | 4626 | } | |
4634 | 4627 | |||
4635 | static void | 4628 | static void | |
4636 | VarSubstDollarDollar(const char **pp, Buffer *res, VarEvalMode emode) | 4629 | VarSubstDollarDollar(const char **pp, Buffer *res, VarEvalMode emode) | |
4637 | { | 4630 | { | |
4638 | /* A dollar sign may be escaped with another dollar sign. */ | 4631 | /* A dollar sign may be escaped with another dollar sign. */ | |
4639 | if (save_dollars && VarEvalMode_ShouldKeepDollar(emode)) | 4632 | if (save_dollars && VarEvalMode_ShouldKeepDollar(emode)) | |
4640 | Buf_AddByte(res, '$'); | 4633 | Buf_AddByte(res, '$'); | |
4641 | Buf_AddByte(res, '$'); | 4634 | Buf_AddByte(res, '$'); | |
4642 | *pp += 2; | 4635 | *pp += 2; | |
4643 | } | 4636 | } | |
4644 | 4637 | |||
4645 | static void | 4638 | static void | |
4646 | VarSubstExpr(const char **pp, Buffer *buf, GNode *scope, | 4639 | VarSubstExpr(const char **pp, Buffer *buf, GNode *scope, | |
4647 | VarEvalMode emode, bool *inout_errorReported) | 4640 | VarEvalMode emode, bool *inout_errorReported) | |
4648 | { | 4641 | { | |
4649 | const char *p = *pp; | 4642 | const char *p = *pp; | |
4650 | const char *nested_p = p; | 4643 | const char *nested_p = p; | |
4651 | FStr val = Var_Parse(&nested_p, scope, emode); | 4644 | FStr val = Var_Parse(&nested_p, scope, emode); | |
4652 | /* TODO: handle errors */ | 4645 | /* TODO: handle errors */ | |
4653 | 4646 | |||
4654 | if (val.str == var_Error || val.str == varUndefined) { | 4647 | if (val.str == var_Error || val.str == varUndefined) { | |
4655 | if (!VarEvalMode_ShouldKeepUndef(emode)) { | 4648 | if (!VarEvalMode_ShouldKeepUndef(emode)) { | |
4656 | p = nested_p; | 4649 | p = nested_p; | |
4657 | } else if (val.str == var_Error) { | 4650 | } else if (val.str == var_Error) { | |
4658 | 4651 | |||
4659 | /* | 4652 | /* | |
4660 | * FIXME: The condition 'val.str == var_Error' doesn't | 4653 | * FIXME: The condition 'val.str == var_Error' doesn't | |
4661 | * mean there was an undefined variable. It could | 4654 | * mean there was an undefined variable. It could | |
4662 | * equally well be a parse error; see | 4655 | * equally well be a parse error; see | |
4663 | * unit-tests/varmod-order.mk. | 4656 | * unit-tests/varmod-order.mk. | |
4664 | */ | 4657 | */ | |
4665 | 4658 | |||
4666 | /* | 4659 | /* | |
4667 | * If variable is undefined, complain and skip the | 4660 | * If variable is undefined, complain and skip the | |
4668 | * variable. The complaint will stop us from doing | 4661 | * variable. The complaint will stop us from doing | |
4669 | * anything when the file is parsed. | 4662 | * anything when the file is parsed. | |
4670 | */ | 4663 | */ | |
4671 | if (!*inout_errorReported) { | 4664 | if (!*inout_errorReported) { | |
4672 | Parse_Error(PARSE_FATAL, | 4665 | Parse_Error(PARSE_FATAL, | |
4673 | "Undefined variable \"%.*s\"", | 4666 | "Undefined variable \"%.*s\"", | |
4674 | (int)(nested_p - p), p); | 4667 | (int)(nested_p - p), p); | |
4675 | *inout_errorReported = true; | 4668 | *inout_errorReported = true; | |
4676 | } | 4669 | } | |
4677 | p = nested_p; | 4670 | p = nested_p; | |
4678 | } else { | 4671 | } else { | |
4679 | /* | 4672 | /* | |
4680 | * Copy the initial '$' of the undefined expression, | 4673 | * Copy the initial '$' of the undefined expression, | |
4681 | * thereby deferring expansion of the expression, but | 4674 | * thereby deferring expansion of the expression, but | |
4682 | * expand nested expressions if already possible. See | 4675 | * expand nested expressions if already possible. See | |
4683 | * unit-tests/varparse-undef-partial.mk. | 4676 | * unit-tests/varparse-undef-partial.mk. | |
4684 | */ | 4677 | */ | |
4685 | Buf_AddByte(buf, *p); | 4678 | Buf_AddByte(buf, *p); | |
4686 | p++; | 4679 | p++; | |
4687 | } | 4680 | } | |
4688 | } else { | 4681 | } else { | |
4689 | p = nested_p; | 4682 | p = nested_p; | |
4690 | Buf_AddStr(buf, val.str); | 4683 | Buf_AddStr(buf, val.str); | |
4691 | } | 4684 | } | |
4692 | 4685 | |||
4693 | FStr_Done(&val); | 4686 | FStr_Done(&val); | |
4694 | 4687 | |||
4695 | *pp = p; | 4688 | *pp = p; | |
4696 | } | 4689 | } | |
4697 | 4690 | |||
4698 | /* | 4691 | /* | |
4699 | * Skip as many characters as possible -- either to the end of the string, | 4692 | * Skip as many characters as possible -- either to the end of the string, | |
4700 | * or to the next dollar sign, which may start an expression. | 4693 | * or to the next dollar sign, which may start an expression. | |
4701 | */ | 4694 | */ | |
4702 | static void | 4695 | static void | |
4703 | VarSubstPlain(const char **pp, Buffer *res) | 4696 | VarSubstPlain(const char **pp, Buffer *res) | |
4704 | { | 4697 | { | |
4705 | const char *p = *pp; | 4698 | const char *p = *pp; | |
4706 | const char *start = p; | 4699 | const char *start = p; | |
4707 | 4700 | |||
4708 | for (p++; *p != '$' && *p != '\0'; p++) | 4701 | for (p++; *p != '$' && *p != '\0'; p++) | |
4709 | continue; | 4702 | continue; | |
4710 | Buf_AddRange(res, start, p); | 4703 | Buf_AddRange(res, start, p); | |
4711 | *pp = p; | 4704 | *pp = p; | |
4712 | } | 4705 | } | |
4713 | 4706 | |||
4714 | /* | 4707 | /* | |
4715 | * Expand all expressions like $V, ${VAR}, $(VAR:Modifiers) in the | 4708 | * Expand all expressions like $V, ${VAR}, $(VAR:Modifiers) in the | |
4716 | * given string. | 4709 | * given string. | |
4717 | * | 4710 | * | |
4718 | * Input: | 4711 | * Input: | |
4719 | * str The string in which the expressions are | 4712 | * str The string in which the expressions are expanded. | |
4720 | * expanded. | 4713 | * scope The scope in which to start searching for variables. | |
4721 | * scope The scope in which to start searching for | 4714 | * The other scopes are searched as well. | |
4722 | * variables. The other scopes are searched as well. | |||
4723 | * emode The mode for parsing or evaluating subexpressions. | 4715 | * emode The mode for parsing or evaluating subexpressions. | |
4724 | */ | 4716 | */ | |
4725 | char * | 4717 | char * | |
4726 | Var_Subst(const char *str, GNode *scope, VarEvalMode emode) | 4718 | Var_Subst(const char *str, GNode *scope, VarEvalMode emode) | |
4727 | { | 4719 | { | |
4728 | const char *p = str; | 4720 | const char *p = str; | |
4729 | Buffer res; | 4721 | Buffer res; | |
4730 | 4722 | |||
4731 | /* | 4723 | /* | |
4732 | * Set true if an error has already been reported, to prevent a | 4724 | * Set true if an error has already been reported, to prevent a | |
4733 | * plethora of messages when recursing | 4725 | * plethora of messages when recursing | |
4734 | */ | 4726 | */ | |
4735 | static bool errorReported; | 4727 | static bool errorReported; | |
4736 | 4728 | |||
4737 | Buf_Init(&res); | 4729 | Buf_Init(&res); | |
4738 | errorReported = false; | 4730 | errorReported = false; | |
4739 | 4731 | |||
4740 | while (*p != '\0') { | 4732 | while (*p != '\0') { | |
4741 | if (p[0] == '$' && p[1] == '$') | 4733 | if (p[0] == '$' && p[1] == '$') | |
4742 | VarSubstDollarDollar(&p, &res, emode); | 4734 | VarSubstDollarDollar(&p, &res, emode); | |
4743 | else if (p[0] == '$') | 4735 | else if (p[0] == '$') | |
4744 | VarSubstExpr(&p, &res, scope, emode, &errorReported); | 4736 | VarSubstExpr(&p, &res, scope, emode, &errorReported); | |
4745 | else | 4737 | else | |
4746 | VarSubstPlain(&p, &res); | 4738 | VarSubstPlain(&p, &res); | |
4747 | } | 4739 | } | |
4748 | 4740 | |||
4749 | return Buf_DoneDataCompact(&res); | 4741 | return Buf_DoneDataCompact(&res); | |
4750 | } | 4742 | } | |
4751 | 4743 | |||
4752 | void | 4744 | void | |
4753 | Var_Expand(FStr *str, GNode *scope, VarEvalMode emode) | 4745 | Var_Expand(FStr *str, GNode *scope, VarEvalMode emode) | |
4754 | { | 4746 | { | |
4755 | char *expanded; | 4747 | char *expanded; | |
4756 | 4748 | |||
4757 | if (strchr(str->str, '$') == NULL) | 4749 | if (strchr(str->str, '$') == NULL) | |
4758 | return; | 4750 | return; | |
4759 | expanded = Var_Subst(str->str, scope, emode); | 4751 | expanded = Var_Subst(str->str, scope, emode); | |
4760 | /* TODO: handle errors */ | 4752 | /* TODO: handle errors */ | |
4761 | FStr_Done(str); | 4753 | FStr_Done(str); | |
4762 | *str = FStr_InitOwn(expanded); | 4754 | *str = FStr_InitOwn(expanded); | |
4763 | } | 4755 | } | |
4764 | 4756 | |||
4765 | /* Initialize the variables module. */ | 4757 | /* Initialize the variables module. */ | |
4766 | void | 4758 | void | |
4767 | Var_Init(void) | 4759 | Var_Init(void) | |
4768 | { | 4760 | { | |
4769 | SCOPE_INTERNAL = GNode_New("Internal"); | 4761 | SCOPE_INTERNAL = GNode_New("Internal"); | |
4770 | SCOPE_GLOBAL = GNode_New("Global"); | 4762 | SCOPE_GLOBAL = GNode_New("Global"); | |
4771 | SCOPE_CMDLINE = GNode_New("Command"); | 4763 | SCOPE_CMDLINE = GNode_New("Command"); | |
4772 | } | 4764 | } | |
4773 | 4765 | |||
4774 | /* Clean up the variables module. */ | 4766 | /* Clean up the variables module. */ | |
4775 | void | 4767 | void | |
4776 | Var_End(void) | 4768 | Var_End(void) | |
4777 | { | 4769 | { | |
4778 | Var_Stats(); | 4770 | Var_Stats(); | |
4779 | } | 4771 | } | |
4780 | 4772 | |||
4781 | void | 4773 | void | |
4782 | Var_Stats(void) | 4774 | Var_Stats(void) | |
4783 | { | 4775 | { | |
4784 | HashTable_DebugStats(&SCOPE_GLOBAL->vars, "Global variables"); | 4776 | HashTable_DebugStats(&SCOPE_GLOBAL->vars, "Global variables"); | |
4785 | } | 4777 | } | |
4786 | 4778 | |||
4787 | static int | 4779 | static int | |
4788 | StrAsc(const void *sa, const void *sb) | 4780 | StrAsc(const void *sa, const void *sb) | |
4789 | { | 4781 | { | |
4790 | return strcmp( | 4782 | return strcmp( | |
4791 | *((const char *const *)sa), *((const char *const *)sb)); | 4783 | *((const char *const *)sa), *((const char *const *)sb)); | |
4792 | } | 4784 | } | |
4793 | 4785 | |||
4794 | 4786 | |||
4795 | /* Print all variables in a scope, sorted by name. */ | 4787 | /* Print all variables in a scope, sorted by name. */ | |
4796 | void | 4788 | void | |
4797 | Var_Dump(GNode *scope) | 4789 | Var_Dump(GNode *scope) | |
4798 | { | 4790 | { | |
4799 | Vector /* of const char * */ vec; | 4791 | Vector /* of const char * */ vec; | |
4800 | HashIter hi; | 4792 | HashIter hi; | |
4801 | size_t i; | 4793 | size_t i; | |
4802 | const char **varnames; | 4794 | const char **varnames; | |
4803 | 4795 | |||
4804 | Vector_Init(&vec, sizeof(const char *)); | 4796 | Vector_Init(&vec, sizeof(const char *)); | |
4805 | 4797 | |||
4806 | HashIter_Init(&hi, &scope->vars); | 4798 | HashIter_Init(&hi, &scope->vars); | |
4807 | while (HashIter_Next(&hi) != NULL) | 4799 | while (HashIter_Next(&hi) != NULL) | |
4808 | *(const char **)Vector_Push(&vec) = hi.entry->key; | 4800 | *(const char **)Vector_Push(&vec) = hi.entry->key; | |
4809 | varnames = vec.items; | 4801 | varnames = vec.items; | |
4810 | 4802 | |||
4811 | qsort(varnames, vec.len, sizeof varnames[0], StrAsc); | 4803 | qsort(varnames, vec.len, sizeof varnames[0], StrAsc); | |
4812 | 4804 | |||
4813 | for (i = 0; i < vec.len; i++) { | 4805 | for (i = 0; i < vec.len; i++) { | |
4814 | const char *varname = varnames[i]; | 4806 | const char *varname = varnames[i]; | |
4815 | const Var *var = HashTable_FindValue(&scope->vars, varname); | 4807 | const Var *var = HashTable_FindValue(&scope->vars, varname); | |
4816 | debug_printf("%-16s = %s%s\n", varname, | 4808 | debug_printf("%-16s = %s%s\n", varname, | |
4817 | var->val.data, ValueDescription(var->val.data)); | 4809 | var->val.data, ValueDescription(var->val.data)); | |
4818 | } | 4810 | } | |
4819 | 4811 | |||
4820 | Vector_Done(&vec); | 4812 | Vector_Done(&vec); | |
4821 | } | 4813 | } |
--- src/usr.bin/make/unit-tests/cmd-errors-jobs.exp 2024/04/20 10:18:55 1.5
+++ src/usr.bin/make/unit-tests/cmd-errors-jobs.exp 2024/04/23 22:51:28 1.6
@@ -1,9 +1,9 @@ | @@ -1,9 +1,9 @@ | |||
1 | : undefined--eol | 1 | : undefined--eol | |
2 | make: in target "unclosed-variable": Unclosed variable "UNCLOSED" | 2 | make: in target "unclosed-expression": Unclosed variable "UNCLOSED" | |
3 | : unclosed-variable- | 3 | : unclosed-expression- | |
4 | make: Unclosed expression, expecting '}' for "UNCLOSED" | 4 | make: Unclosed expression, expecting '}' for "UNCLOSED" | |
5 | : unclosed-modifier- | 5 | : unclosed-modifier- | |
6 | make: in target "unknown-modifier": while evaluating variable "UNKNOWN": Unknown modifier "Z" | 6 | make: in target "unknown-modifier": while evaluating variable "UNKNOWN": Unknown modifier "Z" | |
7 | : unknown-modifier--eol | 7 | : unknown-modifier--eol | |
8 | : end-eol | 8 | : end-eol | |
9 | exit status 0 | 9 | exit status 0 |
--- src/usr.bin/make/unit-tests/cmd-errors-lint.exp 2024/04/20 10:18:55 1.5
+++ src/usr.bin/make/unit-tests/cmd-errors-lint.exp 2024/04/23 22:51:28 1.6
@@ -1,9 +1,9 @@ | @@ -1,9 +1,9 @@ | |||
1 | : undefined | 1 | : undefined | |
2 | make: in target "unclosed-variable": Unclosed variable "UNCLOSED" | 2 | make: in target "unclosed-expression": Unclosed variable "UNCLOSED" | |
3 | : unclosed-variable | 3 | : unclosed-expression | |
4 | make: Unclosed expression, expecting '}' for "UNCLOSED" | 4 | make: Unclosed expression, expecting '}' for "UNCLOSED" | |
5 | : unclosed-modifier | 5 | : unclosed-modifier | |
6 | make: in target "unknown-modifier": while evaluating variable "UNKNOWN": Unknown modifier "Z" | 6 | make: in target "unknown-modifier": while evaluating variable "UNKNOWN": Unknown modifier "Z" | |
7 | : unknown-modifier | 7 | : unknown-modifier | |
8 | : end | 8 | : end | |
9 | exit status 2 | 9 | exit status 2 |
--- src/usr.bin/make/unit-tests/cmd-errors.mk 2022/09/25 12:51:37 1.5
+++ src/usr.bin/make/unit-tests/cmd-errors.mk 2024/04/23 22:51:28 1.6
@@ -1,30 +1,31 @@ | @@ -1,30 +1,31 @@ | |||
1 | # $NetBSD: cmd-errors.mk,v 1.5 2022/09/25 12:51:37 rillig Exp $ | 1 | # $NetBSD: cmd-errors.mk,v 1.6 2024/04/23 22:51:28 rillig Exp $ | |
2 | # | 2 | # | |
3 | # Demonstrate how errors in variable expansions affect whether the commands | 3 | # Demonstrate how errors in expressions affect whether the commands | |
4 | # are actually executed in compat mode. | 4 | # are actually executed in compat mode. | |
5 | 5 | |||
6 | all: undefined unclosed-variable unclosed-modifier unknown-modifier end | 6 | all: undefined unclosed-expression unclosed-modifier unknown-modifier end | |
7 | 7 | |||
8 | # Undefined variables are not an error. They expand to empty strings. | 8 | # Undefined variables in expressions are not an error. They expand to empty | |
9 | # strings. | |||
9 | undefined: | 10 | undefined: | |
10 | : $@-${UNDEFINED}-eol | 11 | : $@-${UNDEFINED}-eol | |
11 | 12 | |||
12 | # XXX: As of 2020-11-01, this command is executed even though it contains | 13 | # XXX: As of 2020-11-01, this command is executed even though it contains | |
13 | # parse errors. | 14 | # parse errors. | |
14 | unclosed-variable: | 15 | unclosed-expression: | |
15 | : $@-${UNCLOSED | 16 | : $@-${UNCLOSED | |
16 | 17 | |||
17 | # XXX: As of 2020-11-01, this command is executed even though it contains | 18 | # XXX: As of 2020-11-01, this command is executed even though it contains | |
18 | # parse errors. | 19 | # parse errors. | |
19 | unclosed-modifier: | 20 | unclosed-modifier: | |
20 | : $@-${UNCLOSED: | 21 | : $@-${UNCLOSED: | |
21 | 22 | |||
22 | # XXX: As of 2020-11-01, this command is executed even though it contains | 23 | # XXX: As of 2020-11-01, this command is executed even though it contains | |
23 | # parse errors. | 24 | # parse errors. | |
24 | unknown-modifier: | 25 | unknown-modifier: | |
25 | : $@-${UNKNOWN:Z}-eol | 26 | : $@-${UNKNOWN:Z}-eol | |
26 | 27 | |||
27 | end: | 28 | end: | |
28 | : $@-eol | 29 | : $@-eol | |
29 | 30 | |||
30 | # XXX: As of 2020-11-02, despite the parse errors, the exit status is 0. | 31 | # XXX: As of 2020-11-02, despite the parse errors, the exit status is 0. |
--- src/usr.bin/make/unit-tests/cmd-errors-jobs.mk 2024/04/20 10:18:55 1.3
+++ src/usr.bin/make/unit-tests/cmd-errors-jobs.mk 2024/04/23 22:51:28 1.4
@@ -1,38 +1,39 @@ | @@ -1,38 +1,39 @@ | |||
1 | # $NetBSD: cmd-errors-jobs.mk,v 1.3 2024/04/20 10:18:55 rillig Exp $ | 1 | # $NetBSD: cmd-errors-jobs.mk,v 1.4 2024/04/23 22:51:28 rillig Exp $ | |
2 | # | 2 | # | |
3 | # Demonstrate how errors in variable expansions affect whether the commands | 3 | # Demonstrate how errors in expressions affect whether the commands | |
4 | # are actually executed in jobs mode. | 4 | # are actually executed in jobs mode. | |
5 | 5 | |||
6 | .MAKEFLAGS: -j1 | 6 | .MAKEFLAGS: -j1 | |
7 | 7 | |||
8 | all: undefined unclosed-variable unclosed-modifier unknown-modifier end | 8 | all: undefined unclosed-expression unclosed-modifier unknown-modifier end | |
9 | 9 | |||
10 | # Undefined variables are not an error. They expand to empty strings. | 10 | # Undefined variables in expressions are not an error. They expand to empty | |
11 | # strings. | |||
11 | # expect: : undefined--eol | 12 | # expect: : undefined--eol | |
12 | undefined: | 13 | undefined: | |
13 | : $@-${UNDEFINED}-eol | 14 | : $@-${UNDEFINED}-eol | |
14 | 15 | |||
15 | # XXX: This command is executed even though it contains parse errors. | 16 | # XXX: This command is executed even though it contains parse errors. | |
16 | # expect: make: in target "unclosed-variable": Unclosed variable "UNCLOSED" | 17 | # expect: make: in target "unclosed-expression": Unclosed variable "UNCLOSED" | |
17 | # expect: : unclosed-variable- | 18 | # expect: : unclosed-expression- | |
18 | unclosed-variable: | 19 | unclosed-expression: | |
19 | : $@-${UNCLOSED | 20 | : $@-${UNCLOSED | |
20 | 21 | |||
21 | # XXX: This command is executed even though it contains parse errors. | 22 | # XXX: This command is executed even though it contains parse errors. | |
22 | # expect: make: Unclosed expression, expecting '}' for "UNCLOSED" | 23 | # expect: make: Unclosed expression, expecting '}' for "UNCLOSED" | |
23 | # expect: : unclosed-modifier- | 24 | # expect: : unclosed-modifier- | |
24 | unclosed-modifier: | 25 | unclosed-modifier: | |
25 | : $@-${UNCLOSED: | 26 | : $@-${UNCLOSED: | |
26 | 27 | |||
27 | # XXX: This command is executed even though it contains parse errors. | 28 | # XXX: This command is executed even though it contains parse errors. | |
28 | # expect: make: in target "unknown-modifier": while evaluating variable "UNKNOWN": Unknown modifier "Z" | 29 | # expect: make: in target "unknown-modifier": while evaluating variable "UNKNOWN": Unknown modifier "Z" | |
29 | # expect: : unknown-modifier--eol | 30 | # expect: : unknown-modifier--eol | |
30 | unknown-modifier: | 31 | unknown-modifier: | |
31 | : $@-${UNKNOWN:Z}-eol | 32 | : $@-${UNKNOWN:Z}-eol | |
32 | 33 | |||
33 | # expect: : end-eol | 34 | # expect: : end-eol | |
34 | end: | 35 | end: | |
35 | : $@-eol | 36 | : $@-eol | |
36 | 37 | |||
37 | # XXX: Despite the parse errors, the exit status is 0. | 38 | # XXX: Despite the parse errors, the exit status is 0. | |
38 | # expect: exit status 0 | 39 | # expect: exit status 0 |
--- src/usr.bin/make/unit-tests/cmd-errors-lint.mk 2020/11/02 20:43:27 1.1
+++ src/usr.bin/make/unit-tests/cmd-errors-lint.mk 2024/04/23 22:51:28 1.2
@@ -1,32 +1,33 @@ | @@ -1,32 +1,33 @@ | |||
1 | # $NetBSD: cmd-errors-lint.mk,v 1.1 2020/11/02 20:43:27 rillig Exp $ | 1 | # $NetBSD: cmd-errors-lint.mk,v 1.2 2024/04/23 22:51:28 rillig Exp $ | |
2 | # | 2 | # | |
3 | # Demonstrate how errors in variable expansions affect whether the commands | 3 | # Demonstrate how errors in expressions affect whether the commands | |
4 | # are actually executed. | 4 | # are actually executed. | |
5 | 5 | |||
6 | .MAKEFLAGS: -dL | 6 | .MAKEFLAGS: -dL | |
7 | 7 | |||
8 | all: undefined unclosed-variable unclosed-modifier unknown-modifier end | 8 | all: undefined unclosed-expression unclosed-modifier unknown-modifier end | |
9 | 9 | |||
10 | # Undefined variables are not an error. They expand to empty strings. | 10 | # Undefined variables in expressions are not an error. They expand to empty | |
11 | # strings. | |||
11 | undefined: | 12 | undefined: | |
12 | : $@ ${UNDEFINED} | 13 | : $@ ${UNDEFINED} | |
13 | 14 | |||
14 | # XXX: As of 2020-11-01, this obvious syntax error is not detected. | 15 | # XXX: As of 2020-11-01, this obvious syntax error is not detected. | |
15 | # XXX: As of 2020-11-01, this command is executed even though it contains | 16 | # XXX: As of 2020-11-01, this command is executed even though it contains | |
16 | # parse errors. | 17 | # parse errors. | |
17 | unclosed-variable: | 18 | unclosed-expression: | |
18 | : $@ ${UNCLOSED | 19 | : $@ ${UNCLOSED | |
19 | 20 | |||
20 | # XXX: As of 2020-11-01, this obvious syntax error is not detected. | 21 | # XXX: As of 2020-11-01, this obvious syntax error is not detected. | |
21 | # XXX: As of 2020-11-01, this command is executed even though it contains | 22 | # XXX: As of 2020-11-01, this command is executed even though it contains | |
22 | # parse errors. | 23 | # parse errors. | |
23 | unclosed-modifier: | 24 | unclosed-modifier: | |
24 | : $@ ${UNCLOSED: | 25 | : $@ ${UNCLOSED: | |
25 | 26 | |||
26 | # XXX: As of 2020-11-01, this command is executed even though it contains | 27 | # XXX: As of 2020-11-01, this command is executed even though it contains | |
27 | # parse errors. | 28 | # parse errors. | |
28 | unknown-modifier: | 29 | unknown-modifier: | |
29 | : $@ ${UNKNOWN:Z} | 30 | : $@ ${UNKNOWN:Z} | |
30 | 31 | |||
31 | end: | 32 | end: | |
32 | : $@ | 33 | : $@ |
--- src/usr.bin/make/unit-tests/cmd-errors.exp 2024/04/20 10:18:55 1.8
+++ src/usr.bin/make/unit-tests/cmd-errors.exp 2024/04/23 22:51:28 1.9
@@ -1,9 +1,9 @@ | @@ -1,9 +1,9 @@ | |||
1 | : undefined--eol | 1 | : undefined--eol | |
2 | make: in target "unclosed-variable": Unclosed variable "UNCLOSED" | 2 | make: in target "unclosed-expression": Unclosed variable "UNCLOSED" | |
3 | : unclosed-variable- | 3 | : unclosed-expression- | |
4 | make: Unclosed expression, expecting '}' for "UNCLOSED" | 4 | make: Unclosed expression, expecting '}' for "UNCLOSED" | |
5 | : unclosed-modifier- | 5 | : unclosed-modifier- | |
6 | make: in target "unknown-modifier": while evaluating variable "UNKNOWN": Unknown modifier "Z" | 6 | make: in target "unknown-modifier": while evaluating variable "UNKNOWN": Unknown modifier "Z" | |
7 | : unknown-modifier--eol | 7 | : unknown-modifier--eol | |
8 | : end-eol | 8 | : end-eol | |
9 | exit status 0 | 9 | exit status 0 |
--- src/usr.bin/make/unit-tests/cond-func-defined.exp 2023/11/19 21:47:52 1.8
+++ src/usr.bin/make/unit-tests/cond-func-defined.exp 2024/04/23 22:51:28 1.9
@@ -1,8 +1,5 @@ | @@ -1,8 +1,5 @@ | |||
1 | make: "cond-func-defined.mk" line 24: Missing closing parenthesis for defined() | 1 | make: "cond-func-defined.mk" line 24: Missing closing parenthesis for defined() | |
2 | make: "cond-func-defined.mk" line 34: Missing closing parenthesis for defined() | 2 | make: "cond-func-defined.mk" line 34: Missing closing parenthesis for defined() | |
3 | make: "cond-func-defined.mk" line 47: In .for loops, expressions for the loop variables are | |||
4 | make: "cond-func-defined.mk" line 49: substituted at evaluation time. There is no actual variable | |||
5 | make: "cond-func-defined.mk" line 51: involved, even if it feels like it. | |||
6 | make: Fatal errors encountered -- cannot continue | 3 | make: Fatal errors encountered -- cannot continue | |
7 | make: stopped in unit-tests | 4 | make: stopped in unit-tests | |
8 | exit status 1 | 5 | exit status 1 |
--- src/usr.bin/make/unit-tests/cmdline-undefined.mk 2023/11/19 21:47:52 1.4
+++ src/usr.bin/make/unit-tests/cmdline-undefined.mk 2024/04/23 22:51:28 1.5
@@ -1,52 +1,52 @@ | @@ -1,52 +1,52 @@ | |||
1 | # $NetBSD: cmdline-undefined.mk,v 1.4 2023/11/19 21:47:52 rillig Exp $ | 1 | # $NetBSD: cmdline-undefined.mk,v 1.5 2024/04/23 22:51:28 rillig Exp $ | |
2 | # | 2 | # | |
3 | # Tests for undefined expressions in the command line. | 3 | # Tests for undefined variables in expressions in the command line. | |
4 | 4 | |||
5 | all: | 5 | all: | |
6 | # When the command line is parsed, variable assignments using the | 6 | # When the command line is parsed, variable assignments using the | |
7 | # '=' assignment operator do get their variable name expanded | 7 | # '=' assignment operator do get their variable name expanded | |
8 | # (which probably occurs rarely in practice, if at all), but their | 8 | # (which probably occurs rarely in practice, if at all), but their | |
9 | # variable value is not expanded, as usual. | 9 | # variable value is not expanded, as usual. | |
10 | # | 10 | # | |
11 | @echo 'The = assignment operator' | 11 | @echo 'The = assignment operator' | |
12 | @${.MAKE} -f ${MAKEFILE} print-undefined \ | 12 | @${.MAKE} -f ${MAKEFILE} print-undefined \ | |
13 | CMDLINE='Undefined is $${UNDEFINED}.' | 13 | CMDLINE='Undefined is $${UNDEFINED}.' | |
14 | @echo | 14 | @echo | |
15 | 15 | |||
16 | # The interesting case is using the ':=' assignment operator, which | 16 | # The interesting case is using the ':=' assignment operator, which | |
17 | # expands its right-hand side. But only those variables that are | 17 | # expands its right-hand side. But only those variables that are | |
18 | # defined. | 18 | # defined. | |
19 | @echo 'The := assignment operator' | 19 | @echo 'The := assignment operator' | |
20 | @${.MAKE} -f ${MAKEFILE} print-undefined \ | 20 | @${.MAKE} -f ${MAKEFILE} print-undefined \ | |
21 | CMDLINE:='Undefined is $${UNDEFINED}.' | 21 | CMDLINE:='Undefined is $${UNDEFINED}.' | |
22 | @echo | 22 | @echo | |
23 | 23 | |||
24 | .if make(print-undefined) | 24 | .if make(print-undefined) | |
25 | 25 | |||
26 | .MAKEFLAGS: MAKEFLAGS_ASSIGN='Undefined is $${UNDEFINED}.' | 26 | .MAKEFLAGS: MAKEFLAGS_ASSIGN='Undefined is $${UNDEFINED}.' | |
27 | .MAKEFLAGS: MAKEFLAGS_SUBST:='Undefined is $${UNDEFINED}.' | 27 | .MAKEFLAGS: MAKEFLAGS_SUBST:='Undefined is $${UNDEFINED}.' | |
28 | 28 | |||
29 | # expect+2: From the command line: Undefined is . | 29 | # expect+2: From the command line: Undefined is . | |
30 | # expect+1: From the command line: Undefined is . | 30 | # expect+1: From the command line: Undefined is . | |
31 | .info From the command line: ${CMDLINE} | 31 | .info From the command line: ${CMDLINE} | |
32 | # expect+2: From .MAKEFLAGS '=': Undefined is . | 32 | # expect+2: From .MAKEFLAGS '=': Undefined is . | |
33 | # expect+1: From .MAKEFLAGS '=': Undefined is . | 33 | # expect+1: From .MAKEFLAGS '=': Undefined is . | |
34 | .info From .MAKEFLAGS '=': ${MAKEFLAGS_ASSIGN} | 34 | .info From .MAKEFLAGS '=': ${MAKEFLAGS_ASSIGN} | |
35 | # expect+2: From .MAKEFLAGS ':=': Undefined is . | 35 | # expect+2: From .MAKEFLAGS ':=': Undefined is . | |
36 | # expect+1: From .MAKEFLAGS ':=': Undefined is . | 36 | # expect+1: From .MAKEFLAGS ':=': Undefined is . | |
37 | .info From .MAKEFLAGS ':=': ${MAKEFLAGS_SUBST} | 37 | .info From .MAKEFLAGS ':=': ${MAKEFLAGS_SUBST} | |
38 | 38 | |||
39 | UNDEFINED?= now defined | 39 | UNDEFINED?= now defined | |
40 | 40 | |||
41 | # expect+2: From the command line: Undefined is now defined. | 41 | # expect+2: From the command line: Undefined is now defined. | |
42 | # expect+1: From the command line: Undefined is now defined. | 42 | # expect+1: From the command line: Undefined is now defined. | |
43 | .info From the command line: ${CMDLINE} | 43 | .info From the command line: ${CMDLINE} | |
44 | # expect+2: From .MAKEFLAGS '=': Undefined is now defined. | 44 | # expect+2: From .MAKEFLAGS '=': Undefined is now defined. | |
45 | # expect+1: From .MAKEFLAGS '=': Undefined is now defined. | 45 | # expect+1: From .MAKEFLAGS '=': Undefined is now defined. | |
46 | .info From .MAKEFLAGS '=': ${MAKEFLAGS_ASSIGN} | 46 | .info From .MAKEFLAGS '=': ${MAKEFLAGS_ASSIGN} | |
47 | # expect+2: From .MAKEFLAGS ':=': Undefined is now defined. | 47 | # expect+2: From .MAKEFLAGS ':=': Undefined is now defined. | |
48 | # expect+1: From .MAKEFLAGS ':=': Undefined is now defined. | 48 | # expect+1: From .MAKEFLAGS ':=': Undefined is now defined. | |
49 | .info From .MAKEFLAGS ':=': ${MAKEFLAGS_SUBST} | 49 | .info From .MAKEFLAGS ':=': ${MAKEFLAGS_SUBST} | |
50 | 50 | |||
51 | print-undefined: | 51 | print-undefined: | |
52 | .endif | 52 | .endif |
--- src/usr.bin/make/unit-tests/cmdline.mk 2022/06/10 18:58:07 1.4
+++ src/usr.bin/make/unit-tests/cmdline.mk 2024/04/23 22:51:28 1.5
@@ -1,58 +1,58 @@ | @@ -1,58 +1,58 @@ | |||
1 | # $NetBSD: cmdline.mk,v 1.4 2022/06/10 18:58:07 rillig Exp $ | 1 | # $NetBSD: cmdline.mk,v 1.5 2024/04/23 22:51:28 rillig Exp $ | |
2 | # | 2 | # | |
3 | # Tests for command line parsing and related special variables. | 3 | # Tests for command line parsing and related special variables. | |
4 | 4 | |||
5 | TMPBASE?= ${TMPDIR:U/tmp/uid${.MAKE.UID}} | 5 | TMPBASE?= ${TMPDIR:U/tmp/uid${.MAKE.UID}} | |
6 | SUB1= a7b41170-53f8-4cc2-bc5c-e4c3dd93ec45 # just a random UUID | 6 | SUB1= a7b41170-53f8-4cc2-bc5c-e4c3dd93ec45 # just a random UUID | |
7 | SUB2= 6a8899d2-d227-4b55-9b6b-f3c8eeb83fd5 # just a random UUID | 7 | SUB2= 6a8899d2-d227-4b55-9b6b-f3c8eeb83fd5 # just a random UUID | |
8 | MAKE_CMD= env TMPBASE=${TMPBASE}/${SUB1} ${.MAKE} -f ${MAKEFILE} -r | 8 | MAKE_CMD= env TMPBASE=${TMPBASE}/${SUB1} ${.MAKE} -f ${MAKEFILE} -r | |
9 | DIR2= ${TMPBASE}/${SUB2} | 9 | DIR2= ${TMPBASE}/${SUB2} | |
10 | DIR12= ${TMPBASE}/${SUB1}/${SUB2} | 10 | DIR12= ${TMPBASE}/${SUB1}/${SUB2} | |
11 | 11 | |||
12 | all: prepare-dirs | 12 | all: prepare-dirs | |
13 | all: makeobjdir-direct makeobjdir-indirect | 13 | all: makeobjdir-direct makeobjdir-indirect | |
14 | all: space-and-comment | 14 | all: space-and-comment | |
15 | 15 | |||
16 | prepare-dirs: | 16 | prepare-dirs: | |
17 | @rm -rf ${DIR2} ${DIR12} | 17 | @rm -rf ${DIR2} ${DIR12} | |
18 | @mkdir -p ${DIR2} ${DIR12} | 18 | @mkdir -p ${DIR2} ${DIR12} | |
19 | 19 | |||
20 | # The .OBJDIR can be set via the MAKEOBJDIR command line variable. | 20 | # The .OBJDIR can be set via the MAKEOBJDIR command line variable. | |
21 | # It must be a command line variable; an environment variable would not work. | 21 | # It must be a command line variable; an environment variable would not work. | |
22 | makeobjdir-direct: | 22 | makeobjdir-direct: | |
23 | @echo $@: | 23 | @echo $@: | |
24 | @${MAKE_CMD} MAKEOBJDIR=${DIR2} show-objdir | 24 | @${MAKE_CMD} MAKEOBJDIR=${DIR2} show-objdir | |
25 | 25 | |||
26 | # The .OBJDIR can be set via the MAKEOBJDIR command line variable, | 26 | # The .OBJDIR can be set via the MAKEOBJDIR command line variable, | |
27 | # and that variable could even contain the usual modifiers. | 27 | # and expressions based on that variable can contain the usual modifiers. | |
28 | # Since the .OBJDIR=MAKEOBJDIR assignment happens very early, | 28 | # Since the .OBJDIR=MAKEOBJDIR assignment happens very early, | |
29 | # the SUB2 variable in the modifier is not defined yet and is therefore empty. | 29 | # the SUB2 variable in the modifier is not defined yet and is therefore empty. | |
30 | # The SUB1 in the resulting path comes from the environment variable TMPBASE, | 30 | # The SUB1 in the resulting path comes from the environment variable TMPBASE, | |
31 | # see MAKE_CMD. | 31 | # see MAKE_CMD. | |
32 | makeobjdir-indirect: | 32 | makeobjdir-indirect: | |
33 | @echo $@: | 33 | @echo $@: | |
34 | @${MAKE_CMD} MAKEOBJDIR='$${TMPBASE}/$${SUB2}' show-objdir | 34 | @${MAKE_CMD} MAKEOBJDIR='$${TMPBASE}/$${SUB2}' show-objdir | |
35 | 35 | |||
36 | show-objdir: | 36 | show-objdir: | |
37 | @echo $@: ${.OBJDIR:Q} | 37 | @echo $@: ${.OBJDIR:Q} | |
38 | 38 | |||
39 | 39 | |||
40 | # Variable assignments in the command line are handled differently from | 40 | # Variable assignments in the command line are handled differently from | |
41 | # variable assignments in makefiles. In the command line, trailing whitespace | 41 | # variable assignments in makefiles. In the command line, trailing whitespace | |
42 | # is preserved, and the '#' does not start a comment. This is because the | 42 | # is preserved, and the '#' does not start a comment. This is because the | |
43 | # low-level parsing from ParseRawLine does not take place. | 43 | # low-level parsing from ParseRawLine does not take place. | |
44 | # | 44 | # | |
45 | # Preserving '#' and trailing whitespace has the benefit that when passing | 45 | # Preserving '#' and trailing whitespace has the benefit that when passing | |
46 | # such values to sub-makes via MAKEFLAGS, no special encoding is needed. | 46 | # such values to sub-makes via MAKEFLAGS, no special encoding is needed. | |
47 | # Leading whitespace in the variable value is discarded though, which makes | 47 | # Leading whitespace in the variable value is discarded though, which makes | |
48 | # the behavior inconsistent. | 48 | # the behavior inconsistent. | |
49 | space-and-comment: .PHONY | 49 | space-and-comment: .PHONY | |
50 | @echo $@: | 50 | @echo $@: | |
51 | 51 | |||
52 | @env -i \ | 52 | @env -i \ | |
53 | ${MAKE} -r -f /dev/null ' VAR= value # no comment ' -v VAR \ | 53 | ${MAKE} -r -f /dev/null ' VAR= value # no comment ' -v VAR \ | |
54 | | sed 's,$$,$$,' | 54 | | sed 's,$$,$$,' | |
55 | 55 | |||
56 | @env -i MAKEFLAGS="' VAR= value # no comment '" \ | 56 | @env -i MAKEFLAGS="' VAR= value # no comment '" \ | |
57 | ${MAKE} -r -f /dev/null -v VAR \ | 57 | ${MAKE} -r -f /dev/null -v VAR \ | |
58 | | sed 's,$$,$$,' | 58 | | sed 's,$$,$$,' |
--- src/usr.bin/make/unit-tests/comment.mk 2023/11/19 21:47:52 1.6
+++ src/usr.bin/make/unit-tests/comment.mk 2024/04/23 22:51:28 1.7
@@ -1,80 +1,80 @@ | @@ -1,80 +1,80 @@ | |||
1 | # $NetBSD: comment.mk,v 1.6 2023/11/19 21:47:52 rillig Exp $ | 1 | # $NetBSD: comment.mk,v 1.7 2024/04/23 22:51:28 rillig Exp $ | |
2 | # | 2 | # | |
3 | # Demonstrate how comments are written in makefiles. | 3 | # Demonstrate how comments are written in makefiles. | |
4 | 4 | |||
5 | # This is a comment. | 5 | # This is a comment. | |
6 | 6 | |||
7 | #\ | 7 | #\ | |
8 | This is a multiline comment. | 8 | This is a multiline comment. | |
9 | 9 | |||
10 | # Another multiline comment \ | 10 | # Another multiline comment \ | |
11 | that \ | 11 | that \ | |
12 | goes \ | 12 | goes \ | |
13 | on and on. | 13 | on and on. | |
14 | 14 | |||
15 | # Comments can be indented with spaces, but that is rather unusual. | 15 | # Comments can be indented with spaces, but that is rather unusual. | |
16 | 16 | |||
17 | # Comments can be indented with a tab. | 17 | # Comments can be indented with a tab. | |
18 | # Since parse.c 1.127 from 2007-01-01, these are not shell commands, | 18 | # Since parse.c 1.127 from 2007-01-01, these are not shell commands, | |
19 | # they are just makefile comments. Before that commit, these comments | 19 | # they are just makefile comments. Before that commit, these comments | |
20 | # triggered the error message "Unassociated shell command". | 20 | # triggered the error message "Unassociated shell command". | |
21 | 21 | |||
22 | .if 1 # There can be comments after conditions. | 22 | .if 1 # There can be comments after conditions. | |
23 | .endif # And after the closing directive. | 23 | .endif # And after the closing directive. | |
24 | 24 | |||
25 | VAR= # This comment makes the variable value empty. | 25 | VAR= # This comment makes the variable value empty. | |
26 | # ParseRawLine removes any whitespace before the | 26 | # ParseRawLine removes any whitespace before the | |
27 | # comment. | 27 | # comment. | |
28 | .if ${VAR} != "" | 28 | .if ${VAR} != "" | |
29 | . error | 29 | . error | |
30 | .endif | 30 | .endif | |
31 | 31 | |||
32 | # The comment does not need to start at the beginning of a word (as in the | 32 | # The comment does not need to start at the beginning of a word (as in the | |
33 | # shell), it can start anywhere. | 33 | # shell), it can start anywhere. | |
34 | VAR=# defined but empty | 34 | VAR=# defined but empty | |
35 | 35 | |||
36 | # The space before the comment is always trimmed. | 36 | # The space before the comment is always trimmed. | |
37 | VAR= value | 37 | VAR= value | |
38 | .if ${VAR} != "value" | 38 | .if ${VAR} != "value" | |
39 | . error | 39 | . error | |
40 | .endif | 40 | .endif | |
41 | 41 | |||
42 | # This comment ends with 2 backslashes. An even number of backslashes does | 42 | # This comment ends with 2 backslashes. An even number of backslashes does | |
43 | # not count as a line continuation, therefore the variable assignment that | 43 | # not count as a line continuation, therefore the variable assignment that | |
44 | # follows is actively interpreted. \\ | 44 | # follows is actively interpreted. \\ | |
45 | VAR= not part of the comment | 45 | VAR= not part of the comment | |
46 | .if ${VAR} != "not part of the comment" | 46 | .if ${VAR} != "not part of the comment" | |
47 | . error | 47 | . error | |
48 | .endif | 48 | .endif | |
49 | 49 | |||
50 | # To escape a comment sign, precede it with a backslash. | 50 | # To escape a comment sign, precede it with a backslash. | |
51 | VAR= \# # Both in the assignment. | 51 | VAR= \# # Both in the assignment. | |
52 | .if ${VAR} != "\#" # And in the comparison. | 52 | .if ${VAR} != "\#" # And in the comparison. | |
53 | . error | 53 | . error | |
54 | .endif | 54 | .endif | |
55 | 55 | |||
56 | # Since 2012-03-24 the variable modifier :[#] does not need to be escaped. | 56 | # Since 2012-03-24 the modifier :[#] does not need to be escaped. | |
57 | # To keep the parsing code simple, any "[#" does not start a comment, even | 57 | # To keep the parsing code simple, the "#" in "[#" does not start a comment, | |
58 | # outside of an expression. | 58 | # regardless of the syntactical context it appears in. | |
59 | WORDS= ${VAR:[#]} [# | 59 | WORDS= ${VAR:[#]} [# | |
60 | .if ${WORDS} != "1 [#" | 60 | .if ${WORDS} != "1 [#" | |
61 | . error | 61 | . error | |
62 | .endif | 62 | .endif | |
63 | 63 | |||
64 | # An odd number of backslashes makes a line continuation, \\\ | 64 | # An odd number of backslashes makes a line continuation, \\\ | |
65 | no matter if it is 3 or 5 \\\\\ | 65 | no matter if it is 3 or 5 \\\\\ | |
66 | or 9 backslashes. \\\\\\\\\ | 66 | or 9 backslashes. \\\\\\\\\ | |
67 | This is the last line of the comment. | 67 | This is the last line of the comment. | |
68 | VAR= no comment anymore | 68 | VAR= no comment anymore | |
69 | .if ${VAR} != "no comment anymore" | 69 | .if ${VAR} != "no comment anymore" | |
70 | . error | 70 | . error | |
71 | .endif | 71 | .endif | |
72 | 72 | |||
73 | all: | 73 | all: | |
74 | # In the commands associated with a target, the '#' does not start a makefile | 74 | # In the commands associated with a target, the '#' does not start a makefile | |
75 | # comment. The '#' is just passed to the shell, like any ordinary character. | 75 | # comment. The '#' is just passed to the shell, like any ordinary character. | |
76 | echo This is a shell comment: # comment | 76 | echo This is a shell comment: # comment | |
77 | # If the '#' were to start a makefile comment, the following shell command | 77 | # If the '#' were to start a makefile comment, the following shell command | |
78 | # would have unbalanced quotes. | 78 | # would have unbalanced quotes. | |
79 | echo This is not a shell comment: '# comment' | 79 | echo This is not a shell comment: '# comment' | |
80 | @echo A shell comment can#not start in the middle of a word. | 80 | @echo A shell comment can#not start in the middle of a word. |
--- src/usr.bin/make/unit-tests/cond-cmp-string.mk 2023/11/19 21:47:52 1.18
+++ src/usr.bin/make/unit-tests/cond-cmp-string.mk 2024/04/23 22:51:28 1.19
@@ -1,154 +1,154 @@ | @@ -1,154 +1,154 @@ | |||
1 | # $NetBSD: cond-cmp-string.mk,v 1.18 2023/11/19 21:47:52 rillig Exp $ | 1 | # $NetBSD: cond-cmp-string.mk,v 1.19 2024/04/23 22:51:28 rillig Exp $ | |
2 | # | 2 | # | |
3 | # Tests for string comparisons in .if conditions. | 3 | # Tests for string comparisons in .if conditions. | |
4 | 4 | |||
5 | # This is a simple comparison of string literals. | 5 | # This is a simple comparison of string literals. | |
6 | # Nothing surprising here. | 6 | # Nothing surprising here. | |
7 | .if "str" != "str" | 7 | .if "str" != "str" | |
8 | . error | 8 | . error | |
9 | .endif | 9 | .endif | |
10 | 10 | |||
11 | # The right-hand side of the comparison may be written without quotes. | 11 | # The right-hand side of the comparison may be written without quotes. | |
12 | .if "str" != str | 12 | .if "str" != str | |
13 | . error | 13 | . error | |
14 | .endif | 14 | .endif | |
15 | 15 | |||
16 | # The left-hand side of the comparison must be enclosed in quotes. | 16 | # The left-hand side of the comparison must be enclosed in quotes. | |
17 | # This one is not enclosed in quotes and thus generates an error message. | 17 | # This one is not enclosed in quotes and thus generates an error message. | |
18 | # expect+1: Malformed conditional (str != str) | 18 | # expect+1: Malformed conditional (str != str) | |
19 | .if str != str | 19 | .if str != str | |
20 | . error | 20 | . error | |
21 | .endif | 21 | .endif | |
22 | 22 | |||
23 | # The left-hand side of the comparison requires that any expression | 23 | # An expression that occurs on the left-hand side of the comparison must be | |
24 | # is defined. | 24 | # defined. | |
25 | # | 25 | # | |
26 | # The variable named "" is never defined, nevertheless it can be used as a | 26 | # The variable named "" is never defined, nevertheless it can be used as a | |
27 | # starting point for expressions. Applying the :U modifier to such | 27 | # starting point for an expression. Applying the :U modifier to such an | |
28 | # an undefined expression turns it into a defined expression. | 28 | # undefined expression turns it into a defined expression. | |
29 | # | 29 | # | |
30 | # See ApplyModifier_Defined and DEF_DEFINED. | 30 | # See ApplyModifier_Defined and DEF_DEFINED. | |
31 | .if ${:Ustr} != "str" | 31 | .if ${:Ustr} != "str" | |
32 | . error | 32 | . error | |
33 | .endif | 33 | .endif | |
34 | 34 | |||
35 | # Any character in a string literal may be escaped using a backslash. | 35 | # Any character in a string literal may be escaped using a backslash. | |
36 | # This means that "\n" does not mean a newline but a simple "n". | 36 | # This means that "\n" does not mean a newline but a simple "n". | |
37 | .if "string" != "\s\t\r\i\n\g" | 37 | .if "string" != "\s\t\r\i\n\g" | |
38 | . error | 38 | . error | |
39 | .endif | 39 | .endif | |
40 | 40 | |||
41 | # It is not possible to concatenate two string literals to form a single | 41 | # It is not possible to concatenate two string literals to form a single | |
42 | # string. In C, Python and the shell this is possible, but not in make. | 42 | # string. In C, Python and the shell this is possible, but not in make. | |
43 | # expect+1: Malformed conditional ("string" != "str""ing") | 43 | # expect+1: Malformed conditional ("string" != "str""ing") | |
44 | .if "string" != "str""ing" | 44 | .if "string" != "str""ing" | |
45 | . error | 45 | . error | |
46 | .else | 46 | .else | |
47 | . error | 47 | . error | |
48 | .endif | 48 | .endif | |
49 | 49 | |||
50 | # There is no = operator for strings. | 50 | # There is no = operator for strings. | |
51 | # expect+1: Malformed conditional (!("value" = "value")) | 51 | # expect+1: Malformed conditional (!("value" = "value")) | |
52 | .if !("value" = "value") | 52 | .if !("value" = "value") | |
53 | . error | 53 | . error | |
54 | .else | 54 | .else | |
55 | . error | 55 | . error | |
56 | .endif | 56 | .endif | |
57 | 57 | |||
58 | # There is no === operator for strings either. | 58 | # There is no === operator for strings either. | |
59 | # expect+1: Malformed conditional (!("value" === "value")) | 59 | # expect+1: Malformed conditional (!("value" === "value")) | |
60 | .if !("value" === "value") | 60 | .if !("value" === "value") | |
61 | . error | 61 | . error | |
62 | .else | 62 | .else | |
63 | . error | 63 | . error | |
64 | .endif | 64 | .endif | |
65 | 65 | |||
66 | # An expression can be enclosed in double quotes. | 66 | # An expression can be enclosed in double quotes. | |
67 | .if ${:Uword} != "${:Uword}" | 67 | .if ${:Uword} != "${:Uword}" | |
68 | . error | 68 | . error | |
69 | .endif | 69 | .endif | |
70 | 70 | |||
71 | # Between 2003-01-01 (maybe even earlier) and 2020-10-30, adding one of the | 71 | # Between 2003-01-01 (maybe even earlier) and 2020-10-30, adding one of the | |
72 | # characters " \t!=><" directly after an expression resulted in a | 72 | # characters " \t!=><" directly after an expression in a string literal | |
73 | # "Malformed conditional", even though the string was well-formed. | 73 | # resulted in a "Malformed conditional", even though the string was | |
74 | # well-formed. | |||
74 | .if ${:Uword } != "${:Uword} " | 75 | .if ${:Uword } != "${:Uword} " | |
75 | . error | 76 | . error | |
76 | .endif | 77 | .endif | |
77 | # Some other characters worked though, and some didn't. | 78 | # Some other characters worked though, and some didn't. | |
78 | # Those that are mentioned in is_separator didn't work. | 79 | # Those that are mentioned in is_separator didn't work. | |
79 | .if ${:Uword0} != "${:Uword}0" | 80 | .if ${:Uword0} != "${:Uword}0" | |
80 | . error | 81 | . error | |
81 | .endif | 82 | .endif | |
82 | .if ${:Uword&} != "${:Uword}&" | 83 | .if ${:Uword&} != "${:Uword}&" | |
83 | . error | 84 | . error | |
84 | .endif | 85 | .endif | |
85 | .if ${:Uword!} != "${:Uword}!" | 86 | .if ${:Uword!} != "${:Uword}!" | |
86 | . error | 87 | . error | |
87 | .endif | 88 | .endif | |
88 | .if ${:Uword<} != "${:Uword}<" | 89 | .if ${:Uword<} != "${:Uword}<" | |
89 | . error | 90 | . error | |
90 | .endif | 91 | .endif | |
91 | 92 | |||
92 | # Adding another expression to the string literal works though. | 93 | # Adding another expression to the string literal works though. | |
93 | .if ${:Uword} != "${:Uwo}${:Urd}" | 94 | .if ${:Uword} != "${:Uwo}${:Urd}" | |
94 | . error | 95 | . error | |
95 | .endif | 96 | .endif | |
96 | 97 | |||
97 | # Adding a space at the beginning of the quoted expression works | 98 | # Adding a space at the beginning of the quoted expression works though. | |
98 | # though. | |||
99 | .if ${:U word } != " ${:Uword} " | 99 | .if ${:U word } != " ${:Uword} " | |
100 | . error | 100 | . error | |
101 | .endif | 101 | .endif | |
102 | 102 | |||
103 | # If at least one side of the comparison is a string literal, the string | 103 | # If at least one side of the comparison is a string literal, the string | |
104 | # comparison is performed. | 104 | # comparison is performed. | |
105 | .if 12345 != "12345" | 105 | .if 12345 != "12345" | |
106 | . error | 106 | . error | |
107 | .endif | 107 | .endif | |
108 | 108 | |||
109 | # If at least one side of the comparison is a string literal, the string | 109 | # If at least one side of the comparison is a string literal, the string | |
110 | # comparison is performed. The ".0" in the left-hand side makes the two | 110 | # comparison is performed. The ".0" in the left-hand side makes the two | |
111 | # sides of the equation unequal. | 111 | # sides of the equation unequal. | |
112 | .if 12345.0 == "12345" | 112 | .if 12345.0 == "12345" | |
113 | . error | 113 | . error | |
114 | .endif | 114 | .endif | |
115 | 115 | |||
116 | # Strings cannot be compared relationally, only for equality. | 116 | # Strings cannot be compared relationally, only for equality. | |
117 | # expect+1: Comparison with '<' requires both operands 'string' and 'string' to be numeric | 117 | # expect+1: Comparison with '<' requires both operands 'string' and 'string' to be numeric | |
118 | .if "string" < "string" | 118 | .if "string" < "string" | |
119 | . error | 119 | . error | |
120 | .else | 120 | .else | |
121 | . error | 121 | . error | |
122 | .endif | 122 | .endif | |
123 | 123 | |||
124 | # Strings cannot be compared relationally, only for equality. | 124 | # Strings cannot be compared relationally, only for equality. | |
125 | # expect+1: Comparison with '<=' requires both operands 'string' and 'string' to be numeric | 125 | # expect+1: Comparison with '<=' requires both operands 'string' and 'string' to be numeric | |
126 | .if "string" <= "string" | 126 | .if "string" <= "string" | |
127 | . error | 127 | . error | |
128 | .else | 128 | .else | |
129 | . error | 129 | . error | |
130 | .endif | 130 | .endif | |
131 | 131 | |||
132 | # Strings cannot be compared relationally, only for equality. | 132 | # Strings cannot be compared relationally, only for equality. | |
133 | # expect+1: Comparison with '>' requires both operands 'string' and 'string' to be numeric | 133 | # expect+1: Comparison with '>' requires both operands 'string' and 'string' to be numeric | |
134 | .if "string" > "string" | 134 | .if "string" > "string" | |
135 | . error | 135 | . error | |
136 | .else | 136 | .else | |
137 | . error | 137 | . error | |
138 | .endif | 138 | .endif | |
139 | 139 | |||
140 | # Strings cannot be compared relationally, only for equality. | 140 | # Strings cannot be compared relationally, only for equality. | |
141 | # expect+1: Comparison with '>=' requires both operands 'string' and 'string' to be numeric | 141 | # expect+1: Comparison with '>=' requires both operands 'string' and 'string' to be numeric | |
142 | .if "string" >= "string" | 142 | .if "string" >= "string" | |
143 | . error | 143 | . error | |
144 | .else | 144 | .else | |
145 | . error | 145 | . error | |
146 | .endif | 146 | .endif | |
147 | 147 | |||
148 | # Two variables with different values compare unequal. | 148 | # Two expressions with different values compare unequal. | |
149 | VAR1= value1 | 149 | VAR1= value1 | |
150 | VAR2= value2 | 150 | VAR2= value2 | |
151 | .if ${VAR1} != ${VAR2} | 151 | .if ${VAR1} != ${VAR2} | |
152 | .else | 152 | .else | |
153 | . error | 153 | . error | |
154 | .endif | 154 | .endif |
--- src/usr.bin/make/unit-tests/cond-func-defined.mk 2023/11/19 21:47:52 1.11
+++ src/usr.bin/make/unit-tests/cond-func-defined.mk 2024/04/23 22:51:28 1.12
@@ -1,61 +1,58 @@ | @@ -1,61 +1,58 @@ | |||
1 | # $NetBSD: cond-func-defined.mk,v 1.11 2023/11/19 21:47:52 rillig Exp $ | 1 | # $NetBSD: cond-func-defined.mk,v 1.12 2024/04/23 22:51:28 rillig Exp $ | |
2 | # | 2 | # | |
3 | # Tests for the defined() function in .if conditions. | 3 | # Tests for the defined() function in .if conditions. | |
4 | 4 | |||
5 | DEF= defined | 5 | DEF= defined | |
6 | ${:UA B}= variable name with spaces | 6 | ${:UA B}= variable name with spaces | |
7 | 7 | |||
8 | .if !defined(DEF) | 8 | .if !defined(DEF) | |
9 | . error | 9 | . error | |
10 | .endif | 10 | .endif | |
11 | 11 | |||
12 | # Horizontal whitespace (space tab) after the opening parenthesis is ignored. | 12 | # Horizontal whitespace (space tab) after the opening parenthesis is ignored. | |
13 | .if !defined( DEF) | 13 | .if !defined( DEF) | |
14 | . error | 14 | . error | |
15 | .endif | 15 | .endif | |
16 | 16 | |||
17 | # Horizontal whitespace (space tab) before the closing parenthesis is ignored. | 17 | # Horizontal whitespace (space tab) before the closing parenthesis is ignored. | |
18 | .if !defined(DEF ) | 18 | .if !defined(DEF ) | |
19 | . error | 19 | . error | |
20 | .endif | 20 | .endif | |
21 | 21 | |||
22 | # The argument of a function must not directly contain whitespace. | 22 | # The argument of a function must not directly contain whitespace. | |
23 | # expect+1: Missing closing parenthesis for defined() | 23 | # expect+1: Missing closing parenthesis for defined() | |
24 | .if !defined(A B) | 24 | .if !defined(A B) | |
25 | . error | 25 | . error | |
26 | .endif | 26 | .endif | |
27 | 27 | |||
28 | # If necessary, the whitespace can be generated by an expression. | 28 | # If necessary, the whitespace can be generated by an expression. | |
29 | .if !defined(${:UA B}) | 29 | .if !defined(${:UA B}) | |
30 | . error | 30 | . error | |
31 | .endif | 31 | .endif | |
32 | 32 | |||
33 | # expect+1: Missing closing parenthesis for defined() | 33 | # expect+1: Missing closing parenthesis for defined() | |
34 | .if defined(DEF | 34 | .if defined(DEF | |
35 | . error | 35 | . error | |
36 | .else | 36 | .else | |
37 | . error | 37 | . error | |
38 | .endif | 38 | .endif | |
39 | 39 | |||
40 | # Variables from .for loops are not defined. | 40 | # Variables from .for loops are not defined. | |
41 | # See directive-for.mk for more details. | 41 | # See directive-for.mk for more details. | |
42 | .for var in value | 42 | .for var in value | |
43 | . if defined(var) | 43 | . if defined(var) | |
44 | . error | 44 | . error | |
45 | . else | 45 | . else | |
46 | # expect+1: In .for loops, expressions for the loop variables are | 46 | # In .for loops, expressions based on the loop variables are substituted at | |
47 | . info In .for loops, expressions for the loop variables are | 47 | # evaluation time. There is no actual variable involved, even if the code in | |
48 | # expect+1: substituted at evaluation time. There is no actual variable | 48 | # the makefiles looks like it. | |
49 | . info substituted at evaluation time. There is no actual variable | |||
50 | # expect+1: involved, even if it feels like it. | |||
51 | . info involved, even if it feels like it. | |||
52 | . endif | 49 | . endif | |
53 | .endfor | 50 | .endfor | |
54 | 51 | |||
55 | # Neither of the conditions is true. Before July 2020, the right-hand | 52 | # Neither of the conditions is true. Before July 2020, the right-hand | |
56 | # condition was evaluated even though it was irrelevant. | 53 | # condition was evaluated even though it was irrelevant. | |
57 | .if defined(UNDEF) && ${UNDEF:Mx} != "" | 54 | .if defined(UNDEF) && ${UNDEF:Mx} != "" | |
58 | . error | 55 | . error | |
59 | .endif | 56 | .endif | |
60 | 57 | |||
61 | all: .PHONY | 58 | all: .PHONY |
--- src/usr.bin/make/unit-tests/varmod-ifelse.mk 2024/04/20 10:18:55 1.27
+++ src/usr.bin/make/unit-tests/varmod-ifelse.mk 2024/04/23 22:51:28 1.28
@@ -1,307 +1,307 @@ | @@ -1,307 +1,307 @@ | |||
1 | # $NetBSD: varmod-ifelse.mk,v 1.27 2024/04/20 10:18:55 rillig Exp $ | 1 | # $NetBSD: varmod-ifelse.mk,v 1.28 2024/04/23 22:51:28 rillig Exp $ | |
2 | # | 2 | # | |
3 | # Tests for the ${cond:?then:else} variable modifier, which evaluates either | 3 | # Tests for the ${cond:?then:else} variable modifier, which evaluates either | |
4 | # the then-expression or the else-expression, depending on the condition. | 4 | # the then-expression or the else-expression, depending on the condition. | |
5 | # | 5 | # | |
6 | # The modifier was added on 1998-04-01. | 6 | # The modifier was added on 1998-04-01. | |
7 | # | 7 | # | |
8 | # Until 2015-10-11, the modifier always evaluated both the "then" and the | 8 | # Until 2015-10-11, the modifier always evaluated both the "then" and the | |
9 | # "else" expressions. | 9 | # "else" expressions. | |
10 | 10 | |||
11 | # TODO: Implementation | 11 | # TODO: Implementation | |
12 | 12 | |||
13 | # The variable name of the expression is expanded and then taken as the | 13 | # The variable name of the expression is expanded and then taken as the | |
14 | # condition. In the below example it becomes: | 14 | # condition. In the below example it becomes: | |
15 | # | 15 | # | |
16 | # bare words == "literal" | 16 | # bare words == "literal" | |
17 | # | 17 | # | |
18 | # This confuses the parser, which expects an operator instead of the bare | 18 | # This confuses the parser, which expects an operator instead of the bare | |
19 | # word "expression". If the name were expanded lazily, everything would be | 19 | # word "expression". If the name were expanded lazily, everything would be | |
20 | # fine since the condition would be: | 20 | # fine since the condition would be: | |
21 | # | 21 | # | |
22 | # ${:Ubare words} == "literal" | 22 | # ${:Ubare words} == "literal" | |
23 | # | 23 | # | |
24 | # Evaluating the variable name lazily would require additional code in | 24 | # Evaluating the variable name lazily would require additional code in | |
25 | # Var_Parse and ParseVarname, it would be more useful and predictable | 25 | # Var_Parse and ParseVarname, it would be more useful and predictable | |
26 | # though. | 26 | # though. | |
27 | # expect+1: Malformed conditional (${${:Ubare words} == "literal":?bad:bad}) | 27 | # expect+1: Malformed conditional (${${:Ubare words} == "literal":?bad:bad}) | |
28 | .if ${${:Ubare words} == "literal":?bad:bad} | 28 | .if ${${:Ubare words} == "literal":?bad:bad} | |
29 | . error | 29 | . error | |
30 | .else | 30 | .else | |
31 | . error | 31 | . error | |
32 | .endif | 32 | .endif | |
33 | 33 | |||
34 | # In a variable assignment, undefined variables are not an error. | 34 | # In a variable assignment, undefined variables are not an error. | |
35 | # Because of the early expansion, the whole condition evaluates to | 35 | # Because of the early expansion, the whole condition evaluates to | |
36 | # ' == ""' though, which cannot be parsed because the left-hand side looks | 36 | # ' == ""' though, which cannot be parsed because the left-hand side looks | |
37 | # empty. | 37 | # empty. | |
38 | COND:= ${${UNDEF} == "":?bad-assign:bad-assign} | 38 | COND:= ${${UNDEF} == "":?bad-assign:bad-assign} | |
39 | 39 | |||
40 | # In a condition, undefined variables generate a "Malformed conditional" | 40 | # In a condition, undefined variables generate a "Malformed conditional" | |
41 | # error. That error message is wrong though. In lint mode, the correct | 41 | # error. That error message is wrong though. In lint mode, the correct | |
42 | # "Undefined variable" error message is generated. | 42 | # "Undefined variable" error message is generated. | |
43 | # The difference to the ':=' variable assignment is the additional | 43 | # The difference to the ':=' variable assignment is the additional | |
44 | # "Malformed conditional" error message. | 44 | # "Malformed conditional" error message. | |
45 | # expect+1: Malformed conditional (${${UNDEF} == "":?bad-cond:bad-cond}) | 45 | # expect+1: Malformed conditional (${${UNDEF} == "":?bad-cond:bad-cond}) | |
46 | .if ${${UNDEF} == "":?bad-cond:bad-cond} | 46 | .if ${${UNDEF} == "":?bad-cond:bad-cond} | |
47 | . error | 47 | . error | |
48 | .else | 48 | .else | |
49 | . error | 49 | . error | |
50 | .endif | 50 | .endif | |
51 | 51 | |||
52 | # When the :? is parsed, it is greedy. The else branch spans all the | 52 | # When the :? is parsed, it is greedy. The else branch spans all the | |
53 | # text, up until the closing character '}', even if the text looks like | 53 | # text, up until the closing character '}', even if the text looks like | |
54 | # another modifier. | 54 | # another modifier. | |
55 | .if ${1:?then:else:Q} != "then" | 55 | .if ${1:?then:else:Q} != "then" | |
56 | . error | 56 | . error | |
57 | .endif | 57 | .endif | |
58 | .if ${0:?then:else:Q} != "else:Q" | 58 | .if ${0:?then:else:Q} != "else:Q" | |
59 | . error | 59 | . error | |
60 | .endif | 60 | .endif | |
61 | 61 | |||
62 | # This line generates 2 error messages. The first comes from evaluating the | 62 | # This line generates 2 error messages. The first comes from evaluating the | |
63 | # malformed conditional "1 == == 2", which is reported as "Bad conditional | 63 | # malformed conditional "1 == == 2", which is reported as "Bad conditional | |
64 | # expression" by ApplyModifier_IfElse. The expression containing that | 64 | # expression" by ApplyModifier_IfElse. The expression containing that | |
65 | # conditional therefore returns a parse error from Var_Parse, and this parse | 65 | # conditional therefore returns a parse error from Var_Parse, and this parse | |
66 | # error propagates to CondEvalExpression, where the "Malformed conditional" | 66 | # error propagates to CondEvalExpression, where the "Malformed conditional" | |
67 | # comes from. | 67 | # comes from. | |
68 | # expect+1: Malformed conditional (${1 == == 2:?yes:no} != "") | 68 | # expect+1: Malformed conditional (${1 == == 2:?yes:no} != "") | |
69 | .if ${1 == == 2:?yes:no} != "" | 69 | .if ${1 == == 2:?yes:no} != "" | |
70 | . error | 70 | . error | |
71 | .else | 71 | .else | |
72 | . error | 72 | . error | |
73 | .endif | 73 | .endif | |
74 | 74 | |||
75 | # If the "Bad conditional expression" appears in a quoted string literal, the | 75 | # If the "Bad conditional expression" appears in a quoted string literal, the | |
76 | # error message "Malformed conditional" is not printed, leaving only the "Bad | 76 | # error message "Malformed conditional" is not printed, leaving only the "Bad | |
77 | # conditional expression". | 77 | # conditional expression". | |
78 | # | 78 | # | |
79 | # XXX: The left-hand side is enclosed in quotes. This results in Var_Parse | 79 | # XXX: The left-hand side is enclosed in quotes. This results in Var_Parse | |
80 | # being called without VARE_UNDEFERR. When ApplyModifier_IfElse | 80 | # being called without VARE_UNDEFERR. When ApplyModifier_IfElse | |
81 | # returns AMR_CLEANUP as result, Var_Parse returns varUndefined since the | 81 | # returns AMR_CLEANUP as result, Var_Parse returns varUndefined since the | |
82 | # value of the expression is still undefined. CondParser_String is | 82 | # value of the expression is still undefined. CondParser_String is | |
83 | # then supposed to do proper error handling, but since varUndefined is local | 83 | # then supposed to do proper error handling, but since varUndefined is local | |
84 | # to var.c, it cannot distinguish this return value from an ordinary empty | 84 | # to var.c, it cannot distinguish this return value from an ordinary empty | |
85 | # string. The left-hand side of the comparison is therefore just an empty | 85 | # string. The left-hand side of the comparison is therefore just an empty | |
86 | # string, which is obviously equal to the empty string on the right-hand side. | 86 | # string, which is obviously equal to the empty string on the right-hand side. | |
87 | # | 87 | # | |
88 | # XXX: The debug log for -dc shows a comparison between 1.0 and 0.0. The | 88 | # XXX: The debug log for -dc shows a comparison between 1.0 and 0.0. The | |
89 | # condition should be detected as being malformed before any comparison is | 89 | # condition should be detected as being malformed before any comparison is | |
90 | # done since there is no well-formed comparison in the condition at all. | 90 | # done since there is no well-formed comparison in the condition at all. | |
91 | .MAKEFLAGS: -dc | 91 | .MAKEFLAGS: -dc | |
92 | .if "${1 == == 2:?yes:no}" != "" | 92 | .if "${1 == == 2:?yes:no}" != "" | |
93 | . error | 93 | . error | |
94 | .else | 94 | .else | |
95 | # expect+1: warning: Oops, the parse error should have been propagated. | 95 | # expect+1: warning: Oops, the parse error should have been propagated. | |
96 | . warning Oops, the parse error should have been propagated. | 96 | . warning Oops, the parse error should have been propagated. | |
97 | .endif | 97 | .endif | |
98 | .MAKEFLAGS: -d0 | 98 | .MAKEFLAGS: -d0 | |
99 | 99 | |||
100 | # As of 2020-12-10, the variable "VAR" is first expanded, and the result of | 100 | # As of 2020-12-10, the variable "VAR" is first expanded, and the result of | |
101 | # this expansion is then taken as the condition. To force the | 101 | # this expansion is then taken as the condition. To force the | |
102 | # expression in the condition to be evaluated at exactly the right point, | 102 | # expression in the condition to be evaluated at exactly the right point, | |
103 | # the '$' of the intended '${VAR}' escapes from the parser in form of the | 103 | # the '$' of the intended '${VAR}' escapes from the parser in form of the | |
104 | # expression ${:U\$}. Because of this escaping, the variable "VAR" and thus | 104 | # expression ${:U\$}. Because of this escaping, the variable "VAR" and thus | |
105 | # the condition ends up as "${VAR} == value", just as intended. | 105 | # the condition ends up as "${VAR} == value", just as intended. | |
106 | # | 106 | # | |
107 | # This hack does not work for variables from .for loops since these are | 107 | # This hack does not work for variables from .for loops since these are | |
108 | # expanded at parse time to their corresponding ${:Uvalue} expressions. | 108 | # expanded at parse time to their corresponding ${:Uvalue} expressions. | |
109 | # Making the '$' of the '${VAR}' expression indirect hides this expression | 109 | # Making the '$' of the '${VAR}' expression indirect hides this expression | |
110 | # from the parser of the .for loop body. See ForLoop_SubstVarLong. | 110 | # from the parser of the .for loop body. See ForLoop_SubstVarLong. | |
111 | .MAKEFLAGS: -dc | 111 | .MAKEFLAGS: -dc | |
112 | VAR= value | 112 | VAR= value | |
113 | .if ${ ${:U\$}{VAR} == value:?ok:bad} != "ok" | 113 | .if ${ ${:U\$}{VAR} == value:?ok:bad} != "ok" | |
114 | . error | 114 | . error | |
115 | .endif | 115 | .endif | |
116 | .MAKEFLAGS: -d0 | 116 | .MAKEFLAGS: -d0 | |
117 | 117 | |||
118 | # On 2021-04-19, when building external/bsd/tmux with HAVE_LLVM=yes and | 118 | # On 2021-04-19, when building external/bsd/tmux with HAVE_LLVM=yes and | |
119 | # HAVE_GCC=no, the following conditional generated this error message: | 119 | # HAVE_GCC=no, the following conditional generated this error message: | |
120 | # | 120 | # | |
121 | # make: Bad conditional expression 'string == "literal" && no >= 10' | 121 | # make: Bad conditional expression 'string == "literal" && no >= 10' | |
122 | # in 'string == "literal" && no >= 10?yes:no' | 122 | # in 'string == "literal" && no >= 10?yes:no' | |
123 | # | 123 | # | |
124 | # Despite the error message (which was not clearly marked with "error:"), | 124 | # Despite the error message (which was not clearly marked with "error:"), | |
125 | # the build continued, for historical reasons, see main_Exit. | 125 | # the build continued, for historical reasons, see main_Exit. | |
126 | # | 126 | # | |
127 | # The tricky detail here is that the condition that looks so obvious in the | 127 | # The tricky detail here is that the condition that looks so obvious in the | |
128 | # form written in the makefile becomes tricky when it is actually evaluated. | 128 | # form written in the makefile becomes tricky when it is actually evaluated. | |
129 | # This is because the condition is written in the place of the variable name | 129 | # This is because the condition is written in the place of the variable name | |
130 | # of the expression, and in an expression, the variable name is always | 130 | # of the expression, and in an expression, the variable name is always | |
131 | # expanded first, before even looking at the modifiers. This happens for the | 131 | # expanded first, before even looking at the modifiers. This happens for the | |
132 | # modifier ':?' as well, so when CondEvalExpression gets to see the | 132 | # modifier ':?' as well, so when CondEvalExpression gets to see the | |
133 | # expression, it already looks like this: | 133 | # expression, it already looks like this: | |
134 | # | 134 | # | |
135 | # string == "literal" && no >= 10 | 135 | # string == "literal" && no >= 10 | |
136 | # | 136 | # | |
137 | # When parsing such an expression, the parser used to be strict. It first | 137 | # When parsing such an expression, the parser used to be strict. It first | |
138 | # evaluated the left-hand side of the operator '&&' and then started parsing | 138 | # evaluated the left-hand side of the operator '&&' and then started parsing | |
139 | # the right-hand side 'no >= 10'. The word 'no' is obviously a string | 139 | # the right-hand side 'no >= 10'. The word 'no' is obviously a string | |
140 | # literal, not enclosed in quotes, which is OK, even on the left-hand side of | 140 | # literal, not enclosed in quotes, which is OK, even on the left-hand side of | |
141 | # the comparison operator, but only because this is a condition in the | 141 | # the comparison operator, but only because this is a condition in the | |
142 | # modifier ':?'. In an ordinary directive '.if', this would be a parse error. | 142 | # modifier ':?'. In an ordinary directive '.if', this would be a parse error. | |
143 | # For strings, only the comparison operators '==' and '!=' are defined, | 143 | # For strings, only the comparison operators '==' and '!=' are defined, | |
144 | # therefore parsing stopped at the '>', producing the 'Bad conditional | 144 | # therefore parsing stopped at the '>', producing the 'Bad conditional | |
145 | # expression'. | 145 | # expression'. | |
146 | # | 146 | # | |
147 | # Ideally, the conditional expression would not be expanded before parsing | 147 | # Ideally, the conditional expression would not be expanded before parsing | |
148 | # it. This would allow to write the conditions exactly as seen below. That | 148 | # it. This would allow to write the conditions exactly as seen below. That | |
149 | # change has a high chance of breaking _some_ existing code and would need | 149 | # change has a high chance of breaking _some_ existing code and would need | |
150 | # to be thoroughly tested. | 150 | # to be thoroughly tested. | |
151 | # | 151 | # | |
152 | # Since cond.c 1.262 from 2021-04-20, make reports a more specific error | 152 | # Since cond.c 1.262 from 2021-04-20, make reports a more specific error | |
153 | # message in situations like these, pointing directly to the specific problem | 153 | # message in situations like these, pointing directly to the specific problem | |
154 | # instead of just saying that the whole condition is bad. | 154 | # instead of just saying that the whole condition is bad. | |
155 | STRING= string | 155 | STRING= string | |
156 | NUMBER= no # not really a number | 156 | NUMBER= no # not really a number | |
157 | # expect+1: no. | 157 | # expect+1: no. | |
158 | .info ${${STRING} == "literal" && ${NUMBER} >= 10:?yes:no}. | 158 | .info ${${STRING} == "literal" && ${NUMBER} >= 10:?yes:no}. | |
159 | # expect+3: while evaluating variable "string == "literal" || no >= 10": Comparison with '>=' requires both operands 'no' and '10' to be numeric | 159 | # expect+3: while evaluating variable "string == "literal" || no >= 10": Comparison with '>=' requires both operands 'no' and '10' to be numeric | |
160 | # expect: make: Bad conditional expression 'string == "literal" || no >= 10' before '?yes:no' | 160 | # expect: make: Bad conditional expression 'string == "literal" || no >= 10' before '?yes:no' | |
161 | # expect+1: . | 161 | # expect+1: . | |
162 | .info ${${STRING} == "literal" || ${NUMBER} >= 10:?yes:no}. | 162 | .info ${${STRING} == "literal" || ${NUMBER} >= 10:?yes:no}. | |
163 | 163 | |||
164 | # The following situation occasionally occurs with MKINET6 or similar | 164 | # The following situation occasionally occurs with MKINET6 or similar | |
165 | # variables. | 165 | # variables. | |
166 | NUMBER= # empty, not really a number either | 166 | NUMBER= # empty, not really a number either | |
167 | # expect: make: Bad conditional expression 'string == "literal" && >= 10' before '?yes:no' | 167 | # expect: make: Bad conditional expression 'string == "literal" && >= 10' before '?yes:no' | |
168 | # expect+1: . | 168 | # expect+1: . | |
169 | .info ${${STRING} == "literal" && ${NUMBER} >= 10:?yes:no}. | 169 | .info ${${STRING} == "literal" && ${NUMBER} >= 10:?yes:no}. | |
170 | # expect: make: Bad conditional expression 'string == "literal" || >= 10' before '?yes:no' | 170 | # expect: make: Bad conditional expression 'string == "literal" || >= 10' before '?yes:no' | |
171 | # expect+1: . | 171 | # expect+1: . | |
172 | .info ${${STRING} == "literal" || ${NUMBER} >= 10:?yes:no}. | 172 | .info ${${STRING} == "literal" || ${NUMBER} >= 10:?yes:no}. | |
173 | 173 | |||
174 | # CondParser_LeafToken handles [0-9-+] specially, treating them as a number. | 174 | # CondParser_LeafToken handles [0-9-+] specially, treating them as a number. | |
175 | PLUS= + | 175 | PLUS= + | |
176 | ASTERISK= * | 176 | ASTERISK= * | |
177 | EMPTY= # empty | 177 | EMPTY= # empty | |
178 | # "true" since "+" is not the empty string. | 178 | # "true" since "+" is not the empty string. | |
179 | # expect+1: <true> | 179 | # expect+1: <true> | |
180 | .info <${${PLUS} :?true:false}> | 180 | .info <${${PLUS} :?true:false}> | |
181 | # "false" since the variable named "*" is not defined. | 181 | # "false" since the variable named "*" is not defined. | |
182 | # expect+1: <false> | 182 | # expect+1: <false> | |
183 | .info <${${ASTERISK} :?true:false}> | 183 | .info <${${ASTERISK} :?true:false}> | |
184 | # syntax error since the condition is completely blank. | 184 | # syntax error since the condition is completely blank. | |
185 | # expect+1: <> | 185 | # expect+1: <> | |
186 | .info <${${EMPTY} :?true:false}> | 186 | .info <${${EMPTY} :?true:false}> | |
187 | 187 | |||
188 | 188 | |||
189 | # Since the condition of the '?:' modifier is expanded before being parsed and | 189 | # Since the condition of the '?:' modifier is expanded before being parsed and | |
190 | # evaluated, it is common practice to enclose expressions in quotes, to avoid | 190 | # evaluated, it is common practice to enclose expressions in quotes, to avoid | |
191 | # producing syntactically invalid conditions such as ' == value'. This only | 191 | # producing syntactically invalid conditions such as ' == value'. This only | |
192 | # works if the expanded values neither contain quotes nor backslashes. For | 192 | # works if the expanded values neither contain quotes nor backslashes. For | |
193 | # strings containing quotes or backslashes, the '?:' modifier should not be | 193 | # strings containing quotes or backslashes, the '?:' modifier should not be | |
194 | # used. | 194 | # used. | |
195 | PRIMES= 2 3 5 7 11 | 195 | PRIMES= 2 3 5 7 11 | |
196 | .if ${1 2 3 4 5:L:@n@$n:${ ("${PRIMES:M$n}" != "") :?prime:not_prime}@} != \ | 196 | .if ${1 2 3 4 5:L:@n@$n:${ ("${PRIMES:M$n}" != "") :?prime:not_prime}@} != \ | |
197 | "1:not_prime 2:prime 3:prime 4:not_prime 5:prime" | 197 | "1:not_prime 2:prime 3:prime 4:not_prime 5:prime" | |
198 | . error | 198 | . error | |
199 | .endif | 199 | .endif | |
200 | 200 | |||
201 | # When parsing the modifier ':?', there are 3 possible cases: | 201 | # When parsing the modifier ':?', there are 3 possible cases: | |
202 | # | 202 | # | |
203 | # 1. The whole expression is only parsed. | 203 | # 1. The whole expression is only parsed. | |
204 | # 2. The expression is parsed and the 'then' branch is evaluated. | 204 | # 2. The expression is parsed and the 'then' branch is evaluated. | |
205 | # 3. The expression is parsed and the 'else' branch is evaluated. | 205 | # 3. The expression is parsed and the 'else' branch is evaluated. | |
206 | # | 206 | # | |
207 | # In all of these cases, the expression must be parsed in the same way, | 207 | # In all of these cases, the expression must be parsed in the same way, | |
208 | # especially when one of the branches contains unbalanced '{}' braces. | 208 | # especially when one of the branches contains unbalanced '{}' braces. | |
209 | # | 209 | # | |
210 | # At 2020-01-01, the expressions from the 'then' and 'else' branches were | 210 | # At 2020-01-01, the expressions from the 'then' and 'else' branches were | |
211 | # parsed differently, depending on whether the branch was taken or not. When | 211 | # parsed differently, depending on whether the branch was taken or not. When | |
212 | # the branch was taken, the parser recognized that in the modifier ':S,}},,', | 212 | # the branch was taken, the parser recognized that in the modifier ':S,}},,', | |
213 | # the '}}' were ordinary characters. When the branch was not taken, the | 213 | # the '}}' were ordinary characters. When the branch was not taken, the | |
214 | # parser only counted balanced '{' and '}', ignoring any escaping or other | 214 | # parser only counted balanced '{' and '}', ignoring any escaping or other | |
215 | # changes in the interpretation. | 215 | # changes in the interpretation. | |
216 | # | 216 | # | |
217 | # In var.c 1.285 from 2020-07-20, the parsing of the expressions changed so | 217 | # In var.c 1.285 from 2020-07-20, the parsing of the expressions changed so | |
218 | # that in both cases the expression is parsed in the same way, taking the | 218 | # that in both cases the expression is parsed in the same way, taking the | |
219 | # unbalanced braces in the ':S' modifiers into account. This change was not | 219 | # unbalanced braces in the ':S' modifiers into account. This change was not | |
220 | # on purpose, the commit message mentioned 'has the same effect', which was a | 220 | # on purpose, the commit message mentioned 'has the same effect', which was a | |
221 | # wrong assumption. | 221 | # wrong assumption. | |
222 | # | 222 | # | |
223 | # In var.c 1.323 from 2020-07-26, the unintended fix from var.c 1.285 was | 223 | # In var.c 1.323 from 2020-07-26, the unintended fix from var.c 1.285 was | |
224 | # reverted, still not knowing about the difference between regular parsing and | 224 | # reverted, still not knowing about the difference between regular parsing and | |
225 | # balanced-mode parsing. | 225 | # balanced-mode parsing. | |
226 | # | 226 | # | |
227 | # In var.c 1.1028 from 2022-08-08, there was another attempt at fixing this | 227 | # In var.c 1.1028 from 2022-08-08, there was another attempt at fixing this | |
228 | # inconsistency in parsing, but since that broke parsing of the modifier ':@', | 228 | # inconsistency in parsing, but since that broke parsing of the modifier ':@', | |
229 | # it was reverted in var.c 1.1029 from 2022-08-23. | 229 | # it was reverted in var.c 1.1029 from 2022-08-23. | |
230 | # | 230 | # | |
231 | # In var.c 1.1047 from 2023-02-18, the inconsistency in parsing was finally | 231 | # In var.c 1.1047 from 2023-02-18, the inconsistency in parsing was finally | |
232 | # fixed. The modifier ':@' now parses the body in balanced mode, while | 232 | # fixed. The modifier ':@' now parses the body in balanced mode, while | |
233 | # everywhere else the modifier parts have their subexpressions parsed in the | 233 | # everywhere else the modifier parts have their subexpressions parsed in the | |
234 | # same way, no matter whether they are evaluated or not. | 234 | # same way, no matter whether they are evaluated or not. | |
235 | # | 235 | # | |
236 | # The modifiers ':@' and ':?' are similar in that they conceptually contain | 236 | # The modifiers ':@' and ':?' are similar in that they conceptually contain | |
237 | # text to be evaluated later or conditionally, still they parse that text | 237 | # text to be evaluated later or conditionally, still they parse that text | |
238 | # differently. The crucial difference is that the body of the modifier ':@' | 238 | # differently. The crucial difference is that the body of the modifier ':@' | |
239 | # is always parsed using balanced mode. The modifier ':?', on the other hand, | 239 | # is always parsed using balanced mode. The modifier ':?', on the other hand, | |
240 | # must parse both of its branches in the same way, no matter whether they are | 240 | # must parse both of its branches in the same way, no matter whether they are | |
241 | # evaluated or not. Since balanced mode and standard mode are incompatible, | 241 | # evaluated or not. Since balanced mode and standard mode are incompatible, | |
242 | # it's impossible to use balanced mode in the modifier ':?'. | 242 | # it's impossible to use balanced mode in the modifier ':?'. | |
243 | .MAKEFLAGS: -dc | 243 | .MAKEFLAGS: -dc | |
244 | .if 0 && ${1:?${:Uthen0:S,}},,}:${:Uelse0:S,}},,}} != "not evaluated" | 244 | .if 0 && ${1:?${:Uthen0:S,}},,}:${:Uelse0:S,}},,}} != "not evaluated" | |
245 | # At 2020-01-07, the expression evaluated to 'then0,,}}', even though it was | 245 | # At 2020-01-07, the expression evaluated to 'then0,,}}', even though it was | |
246 | # irrelevant as the '0' had already been evaluated to 'false'. | 246 | # irrelevant as the '0' had already been evaluated to 'false'. | |
247 | . error | 247 | . error | |
248 | .endif | 248 | .endif | |
249 | .if 1 && ${0:?${:Uthen1:S,}},,}:${:Uelse1:S,}},,}} != "else1" | 249 | .if 1 && ${0:?${:Uthen1:S,}},,}:${:Uelse1:S,}},,}} != "else1" | |
250 | . error | 250 | . error | |
251 | .endif | 251 | .endif | |
252 | .if 2 && ${1:?${:Uthen2:S,}},,}:${:Uelse2:S,}},,}} != "then2" | 252 | .if 2 && ${1:?${:Uthen2:S,}},,}:${:Uelse2:S,}},,}} != "then2" | |
253 | # At 2020-01-07, the whole expression evaluated to 'then2,,}}' instead of the | 253 | # At 2020-01-07, the whole expression evaluated to 'then2,,}}' instead of the | |
254 | # expected 'then2'. The 'then' branch of the ':?' modifier was parsed | 254 | # expected 'then2'. The 'then' branch of the ':?' modifier was parsed | |
255 | # normally, parsing and evaluating the ':S' modifier, thereby treating the | 255 | # normally, parsing and evaluating the ':S' modifier, thereby treating the | |
256 | # '}}' as ordinary characters and resulting in 'then2'. The 'else' branch was | 256 | # '}}' as ordinary characters and resulting in 'then2'. The 'else' branch was | |
257 | # parsed in balanced mode, ignoring that the inner '}}' were ordinary | 257 | # parsed in balanced mode, ignoring that the inner '}}' were ordinary | |
258 | # characters. The '}}' were thus interpreted as the end of the 'else' branch | 258 | # characters. The '}}' were thus interpreted as the end of the 'else' branch | |
259 | # and the whole expression. This left the trailing ',,}}', which together | 259 | # and the whole expression. This left the trailing ',,}}', which together | |
260 | # with the 'then2' formed the result 'then2,,}}'. | 260 | # with the 'then2' formed the result 'then2,,}}'. | |
261 | . error | 261 | . error | |
262 | .endif | 262 | .endif | |
263 | 263 | |||
264 | 264 | |||
265 | # Since the condition is taken from the variable name of the expression, not | 265 | # Since the condition is taken from the variable name of the expression, not | |
266 | # from its value, it is evaluated early. It is possible though to construct | 266 | # from its value, it is evaluated early. It is possible though to construct | |
267 | # conditions that are evaluated lazily, at exactly the right point. There is | 267 | # conditions that are evaluated lazily, at exactly the right point. There is | |
268 | # no way to escape a '$' directly in the variable name, but there are | 268 | # no way to escape a '$' directly in the variable name, but there are | |
269 | # alternative ways to bring a '$' into the condition. | 269 | # alternative ways to bring a '$' into the condition. | |
270 | # | 270 | # | |
271 | # In an indirect condition using the ':U' modifier, each '$', ':' and | 271 | # In an indirect condition using the ':U' modifier, each '$', ':' and | |
272 | # '}' must be escaped as '\$', '\:' and '\}', respectively, but '{' must | 272 | # '}' must be escaped as '\$', '\:' and '\}', respectively, but '{' must | |
273 | # not be escaped. | 273 | # not be escaped. | |
274 | # | 274 | # | |
275 | # In an indirect condition using a separate variable, each '$' must be | 275 | # In an indirect condition using a separate variable, each '$' must be | |
276 | # escaped as '$$'. | 276 | # escaped as '$$'. | |
277 | # | 277 | # | |
278 | # These two forms allow the variables to contain arbitrary characters, as the | 278 | # These two forms allow the variables to contain arbitrary characters, as the | |
279 | # condition parser does not see them. | 279 | # condition parser does not see them. | |
280 | DELAYED= two | 280 | DELAYED= two | |
281 | # expect+1: no | 281 | # expect+1: no | |
282 | .info ${ ${:U \${DELAYED\} == "one"}:?yes:no} | 282 | .info ${ ${:U \${DELAYED\} == "one"}:?yes:no} | |
283 | # expect+1: yes | 283 | # expect+1: yes | |
284 | .info ${ ${:U \${DELAYED\} == "two"}:?yes:no} | 284 | .info ${ ${:U \${DELAYED\} == "two"}:?yes:no} | |
285 | INDIRECT_COND1= $${DELAYED} == "one" | 285 | INDIRECT_COND1= $${DELAYED} == "one" | |
286 | # expect+1: no | 286 | # expect+1: no | |
287 | .info ${ ${INDIRECT_COND1}:?yes:no} | 287 | .info ${ ${INDIRECT_COND1}:?yes:no} | |
288 | INDIRECT_COND2= $${DELAYED} == "two" | 288 | INDIRECT_COND2= $${DELAYED} == "two" | |
289 | # expect+1: yes | 289 | # expect+1: yes | |
290 | .info ${ ${INDIRECT_COND2}:?yes:no} | 290 | .info ${ ${INDIRECT_COND2}:?yes:no} | |
291 | 291 | |||
292 | 292 | |||
293 | .MAKEFLAGS: -d0 | 293 | .MAKEFLAGS: -d0 | |
294 | 294 | |||
295 | 295 | |||
296 | # In the modifier parts for the 'then' and 'else' branches, subexpressions are | 296 | # In the modifier parts for the 'then' and 'else' branches, subexpressions are | |
297 | # parsed in by inspecting the actual modifiers. In 2008, 2015, 2020, 2022 and | 297 | # parsed by inspecting the actual modifiers. In 2008, 2015, 2020, 2022 and | |
298 | # 2023, the exact parsing algorithm switched a few times, counting balanced | 298 | # 2023, the exact parsing algorithm switched a few times, counting balanced | |
299 | # braces instead of proper subexpressions, which meant that unbalanced braces | 299 | # braces instead of proper subexpressions, which meant that unbalanced braces | |
300 | # were parsed differently, depending on whether the branch was active or not. | 300 | # were parsed differently, depending on whether the branch was active or not. | |
301 | BRACES= }}} | 301 | BRACES= }}} | |
302 | NO= ${0:?${BRACES:S,}}},yes,}:${BRACES:S,}}},no,}} | 302 | NO= ${0:?${BRACES:S,}}},yes,}:${BRACES:S,}}},no,}} | |
303 | YES= ${1:?${BRACES:S,}}},yes,}:${BRACES:S,}}},no,}} | 303 | YES= ${1:?${BRACES:S,}}},yes,}:${BRACES:S,}}},no,}} | |
304 | BOTH= <${YES}> <${NO}> | 304 | BOTH= <${YES}> <${NO}> | |
305 | .if ${BOTH} != "<yes> <no>" | 305 | .if ${BOTH} != "<yes> <no>" | |
306 | . error | 306 | . error | |
307 | .endif | 307 | .endif |
--- src/usr.bin/make/unit-tests/varmod-match.exp 2024/04/20 10:18:55 1.15
+++ src/usr.bin/make/unit-tests/varmod-match.exp 2024/04/23 22:51:28 1.16
@@ -1,14 +1,14 @@ | @@ -1,14 +1,14 @@ | |||
1 | make: "varmod-match.mk" line 289: while evaluating variable "WORDS": warning: Unfinished character list in pattern 'a[' of modifier ':M' | 1 | make: "varmod-match.mk" line 290: while evaluating variable "WORDS": warning: Unfinished character list in pattern 'a[' of modifier ':M' | |
2 | make: "varmod-match.mk" line 297: while evaluating variable "WORDS": warning: Unfinished character list in pattern 'a[^' of modifier ':M' | 2 | make: "varmod-match.mk" line 298: while evaluating variable "WORDS": warning: Unfinished character list in pattern 'a[^' of modifier ':M' | |
3 | make: "varmod-match.mk" line 305: while evaluating variable "WORDS": warning: Unfinished character list in pattern '[-x1-3' of modifier ':M' | 3 | make: "varmod-match.mk" line 306: while evaluating variable "WORDS": warning: Unfinished character list in pattern '[-x1-3' of modifier ':M' | |
4 | make: "varmod-match.mk" line 313: while evaluating variable "WORDS": warning: Unfinished character list in pattern '*[-x1-3' of modifier ':M' | 4 | make: "varmod-match.mk" line 314: while evaluating variable "WORDS": warning: Unfinished character list in pattern '*[-x1-3' of modifier ':M' | |
5 | make: "varmod-match.mk" line 322: while evaluating variable "WORDS": warning: Unfinished character list in pattern '[^-x1-3' of modifier ':M' | 5 | make: "varmod-match.mk" line 323: while evaluating variable "WORDS": warning: Unfinished character list in pattern '[^-x1-3' of modifier ':M' | |
6 | make: "varmod-match.mk" line 336: while evaluating variable "WORDS": warning: Unfinished character list in pattern '?[\' of modifier ':M' | 6 | make: "varmod-match.mk" line 337: while evaluating variable "WORDS": warning: Unfinished character list in pattern '?[\' of modifier ':M' | |
7 | make: "varmod-match.mk" line 344: while evaluating variable "WORDS": warning: Unfinished character range in pattern '[x-' of modifier ':M' | 7 | make: "varmod-match.mk" line 345: while evaluating variable "WORDS": warning: Unfinished character range in pattern '[x-' of modifier ':M' | |
8 | make: "varmod-match.mk" line 356: while evaluating variable "WORDS": warning: Unfinished character range in pattern '[^x-' of modifier ':M' | 8 | make: "varmod-match.mk" line 357: while evaluating variable "WORDS": warning: Unfinished character range in pattern '[^x-' of modifier ':M' | |
9 | make: "varmod-match.mk" line 364: while evaluating variable " : :: ": warning: Unfinished character list in pattern '[' of modifier ':M' | 9 | make: "varmod-match.mk" line 365: while evaluating variable " : :: ": warning: Unfinished character list in pattern '[' of modifier ':M' | |
10 | make: "varmod-match.mk" line 364: while evaluating variable " : :: ": Unknown modifier "]" | 10 | make: "varmod-match.mk" line 365: while evaluating variable " : :: ": Unknown modifier "]" | |
11 | make: "varmod-match.mk" line 364: Malformed conditional (${ ${:U\:} ${:U\:\:} :L:M[:]} != ":") | 11 | make: "varmod-match.mk" line 365: Malformed conditional (${ ${:U\:} ${:U\:\:} :L:M[:]} != ":") | |
12 | make: Fatal errors encountered -- cannot continue | 12 | make: Fatal errors encountered -- cannot continue | |
13 | make: stopped in unit-tests | 13 | make: stopped in unit-tests | |
14 | exit status 1 | 14 | exit status 1 |
--- src/usr.bin/make/unit-tests/varmod-match.mk 2024/04/20 10:18:55 1.21
+++ src/usr.bin/make/unit-tests/varmod-match.mk 2024/04/23 22:51:28 1.22
@@ -1,380 +1,388 @@ | @@ -1,380 +1,388 @@ | |||
1 | # $NetBSD: varmod-match.mk,v 1.21 2024/04/20 10:18:55 rillig Exp $ | 1 | # $NetBSD: varmod-match.mk,v 1.22 2024/04/23 22:51:28 rillig Exp $ | |
2 | # | 2 | # | |
3 | # Tests for the ':M' modifier, which keeps only those words that match the | 3 | # Tests for the ':M' modifier, which keeps only those words that match the | |
4 | # given pattern. | 4 | # given pattern. | |
5 | # | 5 | # | |
6 | # Table of contents | 6 | # Table of contents | |
7 | # | 7 | # | |
8 | # 1. Pattern characters '*', '?' and '\' | 8 | # 1. Pattern characters '*', '?' and '\' | |
9 | # 2. Character lists and character ranges | 9 | # 2. Character lists and character ranges | |
10 | # 3. Parsing and escaping | 10 | # 3. Parsing and escaping | |
11 | # 4. Interaction with other modifiers | 11 | # 4. Interaction with other modifiers | |
12 | # 5. Performance | 12 | # 5. Performance | |
13 | # 6. Error handling | 13 | # 6. Error handling | |
14 | # 7. Historical bugs | 14 | # 7. Historical bugs | |
15 | # | 15 | # | |
16 | # See ApplyModifier_Match, ParseModifier_Match, ModifyWord_Match and | 16 | # See ApplyModifier_Match, ParseModifier_Match, ModifyWord_Match and | |
17 | # Str_Match. | 17 | # Str_Match. | |
18 | 18 | |||
19 | 19 | |||
20 | # 1. Pattern characters '*', '?' and '\' | 20 | # 1. Pattern characters '*', '?' and '\' | |
21 | # | 21 | # | |
22 | # * matches 0 or more characters | 22 | # * matches 0 or more characters | |
23 | # ? matches 1 character | 23 | # ? matches 1 character | |
24 | # \x matches the character 'x' | 24 | # \x matches the character 'x' | |
25 | 25 | |||
26 | # The pattern is anchored both at the beginning and at the end of the word. | 26 | # The pattern is anchored both at the beginning and at the end of the word. | |
27 | # Since the pattern 'e' does not contain any pattern matching characters, it | 27 | # Since the pattern 'e' does not contain any pattern matching characters, it | |
28 | # matches exactly the word 'e', twice. | 28 | # matches exactly the word 'e', twice. | |
29 | .if ${a c e aa cc ee e f g:L:Me} != "e e" | 29 | .if ${a c e aa cc ee e f g:L:Me} != "e e" | |
30 | . error | 30 | . error | |
31 | .endif | 31 | .endif | |
32 | 32 | |||
33 | # The pattern character '?' matches exactly 1 character, the pattern character | 33 | # The pattern character '?' matches exactly 1 character, the pattern character | |
34 | # '*' matches 0 or more characters. The whole pattern matches all words that | 34 | # '*' matches 0 or more characters. The whole pattern matches all words that | |
35 | # start with 's' and have 3 or more characters. | 35 | # start with 's' and have 3 or more characters. | |
36 | .if ${One Two Three Four five six seven:L:Ms??*} != "six seven" | 36 | .if ${One Two Three Four five six seven so s:L:Ms??*} != "six seven" | |
37 | . error | 37 | . error | |
38 | .endif | 38 | .endif | |
39 | 39 | |||
40 | # Ensure that a pattern without placeholders only matches itself. | 40 | # A pattern without placeholders only matches itself. | |
41 | .if ${a aa aaa b ba baa bab:L:Ma} != "a" | 41 | .if ${a aa aaa b ba baa bab:L:Ma} != "a" | |
42 | . error | 42 | . error | |
43 | .endif | 43 | .endif | |
44 | 44 | |||
45 | # Ensure that a pattern that ends with '*' is properly anchored at the | 45 | # A pattern that ends with '*' is anchored at the | |
46 | # beginning. | 46 | # beginning. | |
47 | .if ${a aa aaa b ba baa bab:L:Ma*} != "a aa aaa" | 47 | .if ${a aa aaa b ba baa bab:L:Ma*} != "a aa aaa" | |
48 | . error | 48 | . error | |
49 | .endif | 49 | .endif | |
50 | 50 | |||
51 | # Ensure that a pattern that starts with '*' is properly anchored at the end. | 51 | # A pattern that starts with '*' is anchored at the end. | |
52 | .if ${a aa aaa b ba baa bab:L:M*a} != "a aa aaa ba baa" | 52 | .if ${a aa aaa b ba baa bab:L:M*a} != "a aa aaa ba baa" | |
53 | . error | 53 | . error | |
54 | .endif | 54 | .endif | |
55 | 55 | |||
56 | # Test the fast code path for '*' followed by a regular character. | 56 | # Test the fast code path for '*' followed by a regular character. | |
57 | .if ${:U file.c file.*c file.h file\.c :M*.c} != "file.c file\\.c" | 57 | .if ${:U file.c file.*c file.h file\.c :M*.c} != "file.c file\\.c" | |
58 | . error | 58 | . error | |
59 | .endif | 59 | .endif | |
60 | # Ensure that the fast code path correctly handles the backslash. | 60 | # Ensure that the fast code path correctly handles the backslash. | |
61 | .if ${:U file.c file.*c file.h file\.c :M*\.c} != "file.c file\\.c" | 61 | .if ${:U file.c file.*c file.h file\.c :M*\.c} != "file.c file\\.c" | |
62 | . error | 62 | . error | |
63 | .endif | 63 | .endif | |
64 | # Ensure that the fast code path correctly handles '\*'. | 64 | # Ensure that the fast code path correctly handles '\*'. | |
65 | .if ${:U file.c file.*c file.h file\.c :M*\*c} != "file.*c" | 65 | .if ${:U file.c file.*c file.h file\.c :M*\*c} != "file.*c" | |
66 | . error | 66 | . error | |
67 | .endif | 67 | .endif | |
68 | # Ensure that the partial match '.c' doesn't confuse the fast code path. | 68 | # Ensure that the partial match '.c' doesn't confuse the fast code path. | |
69 | .if ${:U file.c.cc file.cc.cc file.cc.c :M*.cc} != "file.c.cc file.cc.cc" | 69 | .if ${:U file.c.cc file.cc.cc file.cc.c :M*.cc} != "file.c.cc file.cc.cc" | |
70 | . error | 70 | . error | |
71 | .endif | 71 | .endif | |
72 | # Ensure that the substring '.cc' doesn't confuse the fast code path for '.c'. | 72 | # Ensure that the substring '.cc' doesn't confuse the fast code path for '.c'. | |
73 | .if ${:U file.c.cc file.cc.cc file.cc.c :M*.c} != "file.cc.c" | 73 | .if ${:U file.c.cc file.cc.cc file.cc.c :M*.c} != "file.cc.c" | |
74 | . error | 74 | . error | |
75 | .endif | 75 | .endif | |
76 | 76 | |||
77 | 77 | |||
78 | # 2. Character lists and character ranges | 78 | # 2. Character lists and character ranges | |
79 | # | 79 | # | |
80 | # [...] matches 1 character from the listed characters | 80 | # [...] matches 1 character from the listed characters | |
81 | # [^...] matches 1 character from the unlisted characters | 81 | # [^...] matches 1 character from the unlisted characters | |
82 | # [a-z] matches 1 character from the range 'a' to 'z' | 82 | # [a-z] matches 1 character from the range 'a' to 'z' | |
83 | # [z-a] matches 1 character from the range 'a' to 'z' | 83 | # [z-a] matches 1 character from the range 'a' to 'z' | |
84 | 84 | |||
85 | # Only keep words that start with an uppercase letter. | 85 | # Only keep words that start with an uppercase letter. | |
86 | .if ${One Two Three Four five six seven:L:M[A-Z]*} != "One Two Three Four" | 86 | .if ${One Two Three Four five six seven:L:M[A-Z]*} != "One Two Three Four" | |
87 | . error | 87 | . error | |
88 | .endif | 88 | .endif | |
89 | 89 | |||
90 | # Only keep words that start with a character other than an uppercase letter. | 90 | # Only keep words that start with a character other than an uppercase letter. | |
91 | .if ${One Two Three Four five six seven:L:M[^A-Z]*} != "five six seven" | 91 | .if ${One Two Three Four five six seven:L:M[^A-Z]*} != "five six seven" | |
92 | . error | 92 | . error | |
93 | .endif | 93 | .endif | |
94 | 94 | |||
95 | # [] matches never | 95 | # [] matches never | |
96 | .if ${ ab a[]b a[b a b :L:M[]} != "" | 96 | .if ${ ab a[]b a[b a b :L:M[]} != "" | |
97 | . error | 97 | . error | |
98 | .endif | 98 | .endif | |
99 | 99 | |||
100 | # a[]b matches never | 100 | # a[]b matches never | |
101 | .if ${ ab a[]b a[b a b [ ] :L:Ma[]b} != "" | 101 | .if ${ ab a[]b a[b a b [ ] :L:Ma[]b} != "" | |
102 | . error | 102 | . error | |
103 | .endif | 103 | .endif | |
104 | 104 | |||
105 | # [^] matches exactly 1 arbitrary character | 105 | # [^] matches exactly 1 arbitrary character | |
106 | .if ${ ab a[]b a[b a b [ ] :L:M[^]} != "a b [ ]" | 106 | .if ${ ab a[]b a[b a b [ ] :L:M[^]} != "a b [ ]" | |
107 | . error | 107 | . error | |
108 | .endif | 108 | .endif | |
109 | 109 | |||
110 | # a[^]b matches 'a', then exactly 1 arbitrary character, then 'b' | 110 | # a[^]b matches 'a', then exactly 1 arbitrary character, then 'b' | |
111 | .if ${ ab a[]b a[b a b :L:Ma[^]b} != "a[b" | 111 | .if ${ ab a[]b a[b a b :L:Ma[^]b} != "a[b" | |
112 | . error | 112 | . error | |
113 | .endif | 113 | .endif | |
114 | 114 | |||
115 | # [Nn0] matches exactly 1 character from the set 'N', 'n', '0' | 115 | # [Nn0] matches exactly 1 character from the set 'N', 'n', '0' | |
116 | .if ${ a b N n 0 Nn0 [ ] :L:M[Nn0]} != "N n 0" | 116 | .if ${ a b N n 0 Nn0 [ ] :L:M[Nn0]} != "N n 0" | |
117 | . error | 117 | . error | |
118 | .endif | 118 | .endif | |
119 | 119 | |||
120 | # [a-c] matches exactly 1 character from the range 'a' to 'c' | 120 | # [a-c] matches exactly 1 character from the range 'a' to 'c' | |
121 | .if ${ A B C a b c d [a-c] [a] :L:M[a-c]} != "a b c" | 121 | .if ${ A B C a b c d [a-c] [a] :L:M[a-c]} != "a b c" | |
122 | . error | 122 | . error | |
123 | .endif | 123 | .endif | |
124 | 124 | |||
125 | # [c-a] matches the same as [a-c] | 125 | # [c-a] matches the same as [a-c] | |
126 | .if ${ A B C a b c d [a-c] [a] :L:M[c-a]} != "a b c" | 126 | .if ${ A B C a b c d [a-c] [a] :L:M[c-a]} != "a b c" | |
127 | . error | 127 | . error | |
128 | .endif | 128 | .endif | |
129 | 129 | |||
130 | # [^a-c67] | 130 | # [^a-c67] | |
131 | # matches a single character, except for 'a', 'b', 'c', '6' or | 131 | # matches a single character, except for 'a', 'b', 'c', '6' or | |
132 | # '7' | 132 | # '7' | |
133 | .if ${ A B C a b c d 5 6 7 8 [a-c] [a] :L:M[^a-c67]} != "A B C d 5 8" | 133 | .if ${ A B C a b c d 5 6 7 8 [a-c] [a] :L:M[^a-c67]} != "A B C d 5 8" | |
134 | . error | 134 | . error | |
135 | .endif | 135 | .endif | |
136 | 136 | |||
137 | # [\] matches a single backslash; no escaping takes place in | 137 | # [\] matches a single backslash; no escaping takes place in | |
138 | # character ranges | 138 | # character ranges | |
139 | # Without the 'b' in the below words, the backslash would end a word and thus | 139 | # Without the 'b' in the below words, the backslash would end a word and thus | |
140 | # influence how the string is split into words. | 140 | # influence how the string is split into words. | |
141 | WORDS= a\b a[\]b ab a\\b | 141 | WORDS= a\b a[\]b ab a\\b | |
142 | .if ${WORDS:Ma[\]b} != "a\\b" | 142 | .if ${WORDS:Ma[\]b} != "a\\b" | |
143 | . error | 143 | . error | |
144 | .endif | 144 | .endif | |
145 | 145 | |||
146 | # [[-]] May look like it would match a single '[', '\' or ']', but | 146 | # [[-]] May look like it would match a single '[', '\' or ']', but | |
147 | # the inner ']' has two roles: it is the upper bound of the | 147 | # the inner ']' has two roles: it is the upper bound of the | |
148 | # character range as well as the closing character of the | 148 | # character range as well as the closing character of the | |
149 | # character list. The outer ']' is just a regular character. | 149 | # character list. The outer ']' is just a regular character. | |
150 | WORDS= [ ] [] \] ]] | 150 | WORDS= [ ] [] \] ]] | |
151 | .if ${WORDS:M[[-]]} != "[] \\] ]]" | 151 | .if ${WORDS:M[[-]]} != "[] \\] ]]" | |
152 | . error | 152 | . error | |
153 | .endif | 153 | .endif | |
154 | 154 | |||
155 | # [b[-]a] | 155 | # [b[-]a] | |
156 | # Same as for '[[-]]': the character list stops at the first | 156 | # Same as for '[[-]]': the character list stops at the first | |
157 | # ']', and the 'a]' is treated as a literal string. | 157 | # ']', and the 'a]' is treated as a literal string. | |
158 | WORDS= [a \a ]a []a \]a ]]a [a] \a] ]a] ba] | 158 | WORDS= [a \a ]a []a \]a ]]a [a] \a] ]a] ba] | |
159 | .if ${WORDS:M[b[-]a]} != "[a] \\a] ]a] ba]" | 159 | .if ${WORDS:M[b[-]a]} != "[a] \\a] ]a] ba]" | |
160 | . error | 160 | . error | |
161 | .endif | 161 | .endif | |
162 | 162 | |||
163 | # [-] Matches a single '-' since the '-' only becomes part of a | 163 | # [-] Matches a single '-' since the '-' only becomes part of a | |
164 | # character range if it is preceded and followed by another | 164 | # character range if it is preceded and followed by another | |
165 | # character. | 165 | # character. | |
166 | WORDS= - -] | 166 | WORDS= - -] | |
167 | .if ${WORDS:M[-]} != "-" | 167 | .if ${WORDS:M[-]} != "-" | |
168 | . error | 168 | . error | |
169 | .endif | 169 | .endif | |
170 | 170 | |||
171 | # Only keep words that don't start with s and at the same time end with | 171 | # Only keep words that don't start with s and at the same time end with | |
172 | # either of [ex]. | 172 | # either of [ex]. | |
173 | # | 173 | # | |
174 | # This test case ensures that the negation from the first character list | 174 | # This test case ensures that the negation from the first character list | |
175 | # '[^s]' does not propagate to the second character list '[ex]'. | 175 | # '[^s]' does not propagate to the second character list '[ex]'. | |
176 | .if ${One Two Three Four five six seven:L:M[^s]*[ex]} != "One Three five" | 176 | .if ${One Two Three Four five six seven:L:M[^s]*[ex]} != "One Three five" | |
177 | . error | 177 | . error | |
178 | .endif | 178 | .endif | |
179 | 179 | |||
180 | 180 | |||
181 | # 3. Parsing and escaping | 181 | # 3. Parsing and escaping | |
182 | # | 182 | # | |
183 | # * matches 0 or more characters | 183 | # * matches 0 or more characters | |
184 | # ? matches 1 character | 184 | # ? matches 1 character | |
185 | # \ outside a character list, escapes the following character | 185 | # \ outside a character list, escapes the following character | |
186 | # [ starts a character list for matching 1 character | 186 | # [ starts a character list for matching 1 character | |
187 | # ] ends a character list for matching 1 character | 187 | # ] ends a character list for matching 1 character | |
188 | # - in a character list, forms a character range | 188 | # - in a character list, forms a character range | |
189 | # ^ at the beginning of a character list, negates the list | 189 | # ^ at the beginning of a character list, negates the list | |
190 | # ( while parsing the pattern, starts a nesting level | 190 | # ( while parsing the pattern, starts a nesting level | |
191 | # ) while parsing the pattern, ends a nesting level | 191 | # ) while parsing the pattern, ends a nesting level | |
192 | # { while parsing the pattern, starts a nesting level | 192 | # { while parsing the pattern, starts a nesting level | |
193 | # } while parsing the pattern, ends a nesting level | 193 | # } while parsing the pattern, ends a nesting level | |
194 | # : while parsing the pattern, terminates the pattern | 194 | # : while parsing the pattern, terminates the pattern | |
195 | # $ while parsing the pattern, starts a nested expression | 195 | # $ while parsing the pattern, starts a nested expression | |
196 | # # in a line except a shell command, starts a comment | 196 | # # in a line except a shell command, starts a comment | |
197 | 197 | |||
198 | # The pattern can come from an expression. For single-letter | 198 | # The pattern can come from an expression. For single-letter | |
199 | # variables, either the short form or the long form can be used, just as | 199 | # variables, either the short form or the long form can be used, just as | |
200 | # everywhere else. | 200 | # everywhere else. | |
201 | PRIMES= 2 3 5 7 11 | 201 | PRIMES= 2 3 5 7 11 | |
202 | n= 2 | 202 | n= 2 | |
203 | .if ${PRIMES:M$n} != "2" | 203 | .if ${PRIMES:M$n} != "2" | |
204 | . error | 204 | . error | |
205 | .endif | 205 | .endif | |
206 | .if ${PRIMES:M${n}} != "2" | 206 | .if ${PRIMES:M${n}} != "2" | |
207 | . error | 207 | . error | |
208 | .endif | 208 | .endif | |
209 | .if ${PRIMES:M${:U2}} != "2" | 209 | .if ${PRIMES:M${:U2}} != "2" | |
210 | . error | 210 | . error | |
211 | .endif | 211 | .endif | |
212 | 212 | |||
213 | # : terminates the pattern | 213 | # : terminates the pattern | |
214 | .if ${ A * :L:M:} != "" | 214 | .if ${ A * :L:M:} != "" | |
215 | . error | 215 | . error | |
216 | .endif | 216 | .endif | |
217 | 217 | |||
218 | # \: matches a colon | 218 | # \: matches a colon | |
219 | .if ${ ${:U\: \:\:} :L:M\:} != ":" | 219 | .if ${ ${:U\: \:\:} :L:M\:} != ":" | |
220 | . error | 220 | . error | |
221 | .endif | 221 | .endif | |
222 | 222 | |||
223 | # ${:U\:} matches a colon | 223 | # ${:U\:} matches a colon | |
224 | .if ${ ${:U\:} ${:U\:\:} :L:M${:U\:}} != ":" | 224 | .if ${ ${:U\:} ${:U\:\:} :L:M${:U\:}} != ":" | |
225 | . error | 225 | . error | |
226 | .endif | 226 | .endif | |
227 | 227 | |||
228 | # To match a dollar sign in a word, double it. | 228 | # To match a dollar sign in a word, double it. | |
229 | # | 229 | # | |
230 | # This is different from the :S and :C modifiers, where a '$' has to be | 230 | # This is different from the :S and :C modifiers, where a '$' has to be | |
231 | # escaped as '\$'. | 231 | # escaped as '\$'. | |
232 | .if ${:Ua \$ sign:M*$$*} != "\$" | 232 | .if ${:Ua \$ sign:M*$$*} != "\$" | |
233 | . error | 233 | . error | |
234 | .endif | 234 | .endif | |
235 | 235 | |||
236 | # In the :M modifier, '\$' does not escape a dollar. Instead it is | 236 | # In the :M modifier, '\$' does not escape a dollar. Instead it is | |
237 | # interpreted as a backslash followed by whatever expression the | 237 | # interpreted as a backslash followed by whatever expression the | |
238 | # '$' starts. | 238 | # '$' starts. | |
239 | # | 239 | # | |
240 | # This differs from the :S, :C and several other modifiers. | 240 | # This differs from the :S, :C and several other modifiers. | |
241 | ${:U*}= asterisk | 241 | ${:U*}= asterisk | |
242 | .if ${:Ua \$ sign any-asterisk:M*\$*} != "any-asterisk" | 242 | .if ${:Ua \$ sign any-asterisk:M*\$*} != "any-asterisk" | |
243 | . error | 243 | . error | |
244 | .endif | 244 | .endif | |
245 | 245 | |||
246 | # TODO: ${VAR:M(((}}}} | 246 | # TODO: ${VAR:M(((}}}} | |
247 | # TODO: ${VAR:M{{{)))} | 247 | # TODO: ${VAR:M{{{)))} | |
248 | # TODO: ${VAR:M${UNBALANCED}} | 248 | # TODO: ${VAR:M${UNBALANCED}} | |
249 | # TODO: ${VAR:M${:U(((\}\}\}}} | 249 | # TODO: ${VAR:M${:U(((\}\}\}}} | |
250 | 250 | |||
251 | 251 | |||
252 | # 4. Interaction with other modifiers | 252 | # 4. Interaction with other modifiers | |
253 | 253 | |||
254 | # The modifier ':tW' prevents splitting at whitespace. Even leading and | 254 | # The modifier ':tW' prevents splitting at whitespace. Even leading and | |
255 | # trailing whitespace is preserved. | 255 | # trailing whitespace is preserved. | |
256 | .if ${ plain string :L:tW:M*} != " plain string " | 256 | .if ${ plain string :L:tW:M*} != " plain string " | |
257 | . error | 257 | . error | |
258 | .endif | 258 | .endif | |
259 | 259 | |||
260 | # Without the modifier ':tW', the string is split into words. All whitespace | 260 | # Without the modifier ':tW', the string is split into words. Whitespace | |
261 | # around and between the words is normalized to a single space. | 261 | # around the words is discarded, and whitespace between the words is | |
262 | # normalized to a single space. | |||
262 | .if ${ plain string :L:M*} != "plain string" | 263 | .if ${ plain string :L:M*} != "plain string" | |
263 | . error | 264 | . error | |
264 | .endif | 265 | .endif | |
265 | 266 | |||
266 | 267 | |||
267 | # 5. Performance | 268 | # 5. Performance | |
268 | 269 | |||
269 | # Before 2020-06-13, this expression called Str_Match 601,080,390 times. | 270 | # Before 2020-06-13, this expression called Str_Match 601,080,390 times. | |
270 | # Since 2020-06-13, this expression calls Str_Match 1 time. | 271 | # Since 2020-06-13, this expression calls Str_Match 1 time. | |
271 | .if ${:U****************:M****************b} | 272 | .if ${:U****************:M****************b} | |
272 | .endif | 273 | .endif | |
273 | 274 | |||
274 | # Before 2023-06-22, this expression called Str_Match 2,621,112 times. | 275 | # Before 2023-06-22, this expression called Str_Match 2,621,112 times. | |
275 | # Adding another '*?' to the pattern called Str_Match 20,630,572 times. | 276 | # Adding another '*?' to the pattern called Str_Match 20,630,572 times. | |
276 | # Adding another '*?' to the pattern called Str_Match 136,405,672 times. | 277 | # Adding another '*?' to the pattern called Str_Match 136,405,672 times. | |
277 | # Adding another '*?' to the pattern called Str_Match 773,168,722 times. | 278 | # Adding another '*?' to the pattern called Str_Match 773,168,722 times. | |
278 | # Adding another '*?' to the pattern called Str_Match 3,815,481,072 times. | 279 | # Adding another '*?' to the pattern called Str_Match 3,815,481,072 times. | |
279 | # Since 2023-06-22, Str_Match no longer backtracks. | 280 | # Since 2023-06-22, Str_Match no longer backtracks. | |
280 | .if ${:U..................................................b:M*?*?*?*?*?a} | 281 | .if ${:U..................................................b:M*?*?*?*?*?a} | |
281 | .endif | 282 | .endif | |
282 | 283 | |||
283 | 284 | |||
284 | # 6. Error handling | 285 | # 6. Error handling | |
285 | 286 | |||
286 | # [ Incomplete empty character list, never matches. | 287 | # [ Incomplete empty character list, never matches. | |
287 | WORDS= a a[ | 288 | WORDS= a a[ | |
288 | # expect+1: while evaluating variable "WORDS": warning: Unfinished character list in pattern 'a[' of modifier ':M' | 289 | # expect+1: while evaluating variable "WORDS": warning: Unfinished character list in pattern 'a[' of modifier ':M' | |
289 | .if ${WORDS:Ma[} != "" | 290 | .if ${WORDS:Ma[} != "" | |
290 | . error | 291 | . error | |
291 | .endif | 292 | .endif | |
292 | 293 | |||
293 | # [^ Incomplete negated empty character list, matches any single | 294 | # [^ Incomplete negated empty character list, matches any single | |
294 | # character. | 295 | # character. | |
295 | WORDS= a a[ aX | 296 | WORDS= a a[ aX | |
296 | # expect+1: while evaluating variable "WORDS": warning: Unfinished character list in pattern 'a[^' of modifier ':M' | 297 | # expect+1: while evaluating variable "WORDS": warning: Unfinished character list in pattern 'a[^' of modifier ':M' | |
297 | .if ${WORDS:Ma[^} != "a[ aX" | 298 | .if ${WORDS:Ma[^} != "a[ aX" | |
298 | . error | 299 | . error | |
299 | .endif | 300 | .endif | |
300 | 301 | |||
301 | # [-x1-3 Incomplete character list, matches those elements that can be | 302 | # [-x1-3 Incomplete character list, matches those elements that can be | |
302 | # parsed without lookahead. | 303 | # parsed without lookahead. | |
303 | WORDS= - + x xx 0 1 2 3 4 [x1-3 | 304 | WORDS= - + x xx 0 1 2 3 4 [x1-3 | |
304 | # expect+1: while evaluating variable "WORDS": warning: Unfinished character list in pattern '[-x1-3' of modifier ':M' | 305 | # expect+1: while evaluating variable "WORDS": warning: Unfinished character list in pattern '[-x1-3' of modifier ':M' | |
305 | .if ${WORDS:M[-x1-3} != "- x 1 2 3" | 306 | .if ${WORDS:M[-x1-3} != "- x 1 2 3" | |
306 | . error | 307 | . error | |
307 | .endif | 308 | .endif | |
308 | 309 | |||
309 | # *[-x1-3 Incomplete character list after a wildcard, matches those | 310 | # *[-x1-3 Incomplete character list after a wildcard, matches those | |
310 | # words that end with one of the characters from the list. | 311 | # words that end with one of the characters from the list. | |
311 | WORDS= - + x xx 0 1 2 3 4 00 01 10 11 000 001 010 011 100 101 110 111 [x1-3 | 312 | WORDS= - + x xx 0 1 2 3 4 00 01 10 11 000 001 010 011 100 101 110 111 [x1-3 | |
312 | # expect+1: while evaluating variable "WORDS": warning: Unfinished character list in pattern '*[-x1-3' of modifier ':M' | 313 | # expect+1: while evaluating variable "WORDS": warning: Unfinished character list in pattern '*[-x1-3' of modifier ':M' | |
313 | .if ${WORDS:M*[-x1-3} != "- x xx 1 2 3 01 11 001 011 101 111 [x1-3" | 314 | .if ${WORDS:M*[-x1-3} != "- x xx 1 2 3 01 11 001 011 101 111 [x1-3" | |
314 | . warning ${WORDS:M*[-x1-3} | 315 | . warning ${WORDS:M*[-x1-3} | |
315 | .endif | 316 | .endif | |
316 | 317 | |||
317 | # [^-x1-3 | 318 | # [^-x1-3 | |
318 | # Incomplete negated character list, matches any character | 319 | # Incomplete negated character list, matches any character | |
319 | # except those elements that can be parsed without lookahead. | 320 | # except those elements that can be parsed without lookahead. | |
320 | WORDS= - + x xx 0 1 2 3 4 [x1-3 | 321 | WORDS= - + x xx 0 1 2 3 4 [x1-3 | |
321 | # expect+1: while evaluating variable "WORDS": warning: Unfinished character list in pattern '[^-x1-3' of modifier ':M' | 322 | # expect+1: while evaluating variable "WORDS": warning: Unfinished character list in pattern '[^-x1-3' of modifier ':M' | |
322 | .if ${WORDS:M[^-x1-3} != "+ 0 4" | 323 | .if ${WORDS:M[^-x1-3} != "+ 0 4" | |
323 | . error | 324 | . error | |
324 | .endif | 325 | .endif | |
325 | 326 | |||
326 | # [\ Incomplete character list containing a single '\'. | 327 | # [\ Incomplete character list containing a single '\'. | |
327 | # | 328 | # | |
328 | # A word can only end with a backslash if the preceding | 329 | # A word can only end with a backslash if the preceding | |
329 | # character is a backslash as well; in all other cases the final | 330 | # character is a backslash as well; in all other cases the final | |
330 | # backslash would escape the following space, making the space | 331 | # backslash would escape the following space, making the space | |
331 | # part of the word. Only the very last word of a string can be | 332 | # part of the word. Only the very last word of a string can be | |
332 | # '\', as there is no following space that could be escaped. | 333 | # '\', as there is no following space that could be escaped. | |
333 | WORDS= \\ \a ${:Ux\\} | 334 | WORDS= \\ \a ${:Ux\\} | |
334 | PATTERN= ${:U?[\\} | 335 | PATTERN= ${:U?[\\} | |
335 | # expect+1: while evaluating variable "WORDS": warning: Unfinished character list in pattern '?[\' of modifier ':M' | 336 | # expect+1: while evaluating variable "WORDS": warning: Unfinished character list in pattern '?[\' of modifier ':M' | |
336 | .if ${WORDS:M${PATTERN}} != "\\\\ x\\" | 337 | .if ${WORDS:M${PATTERN}} != "\\\\ x\\" | |
337 | . error | 338 | . error | |
338 | .endif | 339 | .endif | |
339 | 340 | |||
340 | # [x- Incomplete character list containing an incomplete character | 341 | # [x- Incomplete character list containing an incomplete character | |
341 | # range, matches only the 'x'. | 342 | # range, matches only the 'x'. | |
342 | WORDS= [x- x x- y | 343 | WORDS= [x- x x- y | |
343 | # expect+1: while evaluating variable "WORDS": warning: Unfinished character range in pattern '[x-' of modifier ':M' | 344 | # expect+1: while evaluating variable "WORDS": warning: Unfinished character range in pattern '[x-' of modifier ':M' | |
344 | .if ${WORDS:M[x-} != "x" | 345 | .if ${WORDS:M[x-} != "x" | |
345 | . error | 346 | . error | |
346 | .endif | 347 | .endif | |
347 | 348 | |||
348 | # [^x- Incomplete negated character list containing an incomplete | 349 | # [^x- Incomplete negated character list containing an incomplete | |
349 | # character range; matches each word that does not have an 'x' | 350 | # character range; matches each word that does not have an 'x' | |
350 | # at the position of the character list. | 351 | # at the position of the character list. | |
351 | # | 352 | # | |
352 | # XXX: Even matches strings that are longer than a single | 353 | # XXX: Even matches strings that are longer than a single | |
353 | # character. | 354 | # character. | |
354 | WORDS= [x- x x- y yyyyy | 355 | WORDS= [x- x x- y yyyyy | |
355 | # expect+1: while evaluating variable "WORDS": warning: Unfinished character range in pattern '[^x-' of modifier ':M' | 356 | # expect+1: while evaluating variable "WORDS": warning: Unfinished character range in pattern '[^x-' of modifier ':M' | |
356 | .if ${WORDS:M[^x-} != "[x- y yyyyy" | 357 | .if ${WORDS:M[^x-} != "[x- y yyyyy" | |
357 | . error | 358 | . error | |
358 | .endif | 359 | .endif | |
359 | 360 | |||
360 | # [:] matches never since the ':' starts the next modifier | 361 | # [:] matches never since the ':' starts the next modifier | |
361 | # expect+3: while evaluating variable " : :: ": warning: Unfinished character list in pattern '[' of modifier ':M' | 362 | # expect+3: while evaluating variable " : :: ": warning: Unfinished character list in pattern '[' of modifier ':M' | |
362 | # expect+2: while evaluating variable " : :: ": Unknown modifier "]" | 363 | # expect+2: while evaluating variable " : :: ": Unknown modifier "]" | |
363 | # expect+1: Malformed conditional (${ ${:U\:} ${:U\:\:} :L:M[:]} != ":") | 364 | # expect+1: Malformed conditional (${ ${:U\:} ${:U\:\:} :L:M[:]} != ":") | |
364 | .if ${ ${:U\:} ${:U\:\:} :L:M[:]} != ":" | 365 | .if ${ ${:U\:} ${:U\:\:} :L:M[:]} != ":" | |
365 | . error | 366 | . error | |
366 | .else | 367 | .else | |
367 | . error | 368 | . error | |
368 | .endif | 369 | .endif | |
369 | 370 | |||
370 | 371 | |||
371 | # 7. Historical bugs | 372 | # 7. Historical bugs | |
372 | 373 | |||
373 | # Before var.c 1.1031 from 2022-08-24, the following expressions caused an | 374 | # Before var.c 1.1031 from 2022-08-24, the following expressions caused an | |
374 | # out-of-bounds read beyond the indirect ':M' modifiers. | 375 | # out-of-bounds read beyond the indirect ':M' modifiers. | |
375 | .if ${:U:${:UM\\}} # The ':M' pattern need not be unescaped, the | 376 | # | |
376 | . error # resulting pattern is '\', it never matches | 377 | # The argument to the inner ':U' is unescaped to 'M\'. | |
377 | .endif # anything. | 378 | # This 'M\' becomes an # indirect modifier ':M' with the pattern '\'. | |
378 | .if ${:U:${:UM\\\:\\}} # The ':M' pattern must be unescaped, the | 379 | # The pattern '\' never matches. | |
379 | . error # resulting pattern is ':\', it never matches | 380 | .if ${:U:${:UM\\}} | |
380 | .endif # anything. | 381 | . error | |
382 | .endif | |||
383 | # The argument to the inner ':U' is unescaped to 'M\:\'. | |||
384 | # This 'M\:\' becomes an indirect modifier ':M' with the pattern ':\'. | |||
385 | # The pattern ':\' never matches. | |||
386 | .if ${:U:${:UM\\\:\\}} | |||
387 | . error | |||
388 | .endif |