Tue Apr 23 22:51:28 2024 UTC (39d)
make: clean up comments, code and tests


(rillig)
diff -r1.362 -r1.363 src/usr.bin/make/cond.c
diff -r1.330 -r1.331 src/usr.bin/make/make.h
diff -r1.720 -r1.721 src/usr.bin/make/parse.c
diff -r1.1104 -r1.1105 src/usr.bin/make/var.c
diff -r1.5 -r1.6 src/usr.bin/make/unit-tests/cmd-errors-jobs.exp
diff -r1.5 -r1.6 src/usr.bin/make/unit-tests/cmd-errors-lint.exp
diff -r1.5 -r1.6 src/usr.bin/make/unit-tests/cmd-errors.mk
diff -r1.3 -r1.4 src/usr.bin/make/unit-tests/cmd-errors-jobs.mk
diff -r1.1 -r1.2 src/usr.bin/make/unit-tests/cmd-errors-lint.mk
diff -r1.8 -r1.9 src/usr.bin/make/unit-tests/cmd-errors.exp
diff -r1.8 -r1.9 src/usr.bin/make/unit-tests/cond-func-defined.exp
diff -r1.4 -r1.5 src/usr.bin/make/unit-tests/cmdline-undefined.mk
diff -r1.4 -r1.5 src/usr.bin/make/unit-tests/cmdline.mk
diff -r1.6 -r1.7 src/usr.bin/make/unit-tests/comment.mk
diff -r1.18 -r1.19 src/usr.bin/make/unit-tests/cond-cmp-string.mk
diff -r1.11 -r1.12 src/usr.bin/make/unit-tests/cond-func-defined.mk
diff -r1.27 -r1.28 src/usr.bin/make/unit-tests/varmod-ifelse.mk
diff -r1.15 -r1.16 src/usr.bin/make/unit-tests/varmod-match.exp
diff -r1.21 -r1.22 src/usr.bin/make/unit-tests/varmod-match.mk

cvs diff -r1.362 -r1.363 src/usr.bin/make/cond.c (switch to unified diff)

--- 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" */
94MAKE_RCSID("$NetBSD: cond.c,v 1.362 2024/02/07 07:21:22 rillig Exp $"); 94MAKE_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 */
125typedef enum Token { 125typedef 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
130typedef enum ComparisonOp { 130typedef enum ComparisonOp {
131 LT, LE, GT, GE, EQ, NE 131 LT, LE, GT, GE, EQ, NE
132} ComparisonOp; 132} ComparisonOp;
133 133
134typedef struct CondParser { 134typedef 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
173static CondResult CondParser_Or(CondParser *, bool); 173static CondResult CondParser_Or(CondParser *, bool);
174 174
175unsigned int cond_depth = 0; /* current .if nesting level */ 175unsigned int cond_depth = 0; /* current .if nesting level */
176 176
177/* Names for ComparisonOp. */ 177/* Names for ComparisonOp. */
178static const char opname[][3] = { "<", "<=", ">", ">=", "==", "!=" }; 178static const char opname[][3] = { "<", "<=", ">", ">=", "==", "!=" };
179 179
180MAKE_INLINE bool 180MAKE_INLINE bool
181skip_string(const char **pp, const char *str) 181skip_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
190static Token 190static Token
191ToToken(bool cond) 191ToToken(bool cond)
192{ 192{
193 return cond ? TOK_TRUE : TOK_FALSE; 193 return cond ? TOK_TRUE : TOK_FALSE;
194} 194}
195 195
196static void 196static void
197CondParser_SkipWhitespace(CondParser *par) 197CondParser_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 */
207static char * 207static char *
208ParseWord(const char **pp, bool doEval) 208ParseWord(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. */
252static char * 252static char *
253ParseFuncArg(CondParser *par, const char **pp, bool doEval, const char *func) 253ParseFuncArg(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. */
280static bool 280static bool
281FuncDefined(const char *var) 281FuncDefined(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. */
287static bool 287static bool
288FuncMake(const char *targetPattern) 288FuncMake(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. */
308static bool 308static bool
309FuncExists(const char *file) 309FuncExists(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. */
323static bool 323static bool
324FuncTarget(const char *node) 324FuncTarget(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 */
334static bool 334static bool
335FuncCommands(const char *node) 335FuncCommands(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 */
346static bool 346static bool
347TryParseNumber(const char *str, double *out_value) 347TryParseNumber(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
375static bool 375static bool
376is_separator(char ch) 376is_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 */
390static bool 390static bool
391CondParser_StringExpr(CondParser *par, const char *start, 391CondParser_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 */
431static void 430static FStr
432CondParser_Leaf(CondParser *par, bool doEval, bool unquotedOK, 431CondParser_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 }
490return_buf: 489return_buf:
491 str = FStr_InitOwn(buf.data); 490 str = FStr_InitOwn(buf.data);
492 buf.data = NULL; 491 buf.data = NULL;
493return_str: 492return_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 */
502static bool 501static bool
503EvalTruthy(CondParser *par, const char *value, bool quoted) 502EvalTruthy(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". */
517static bool 516static bool
518EvalCompareNum(double lhs, ComparisonOp op, double rhs) 517EvalCompareNum(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
538static Token 537static Token
539EvalCompareStr(CondParser *par, const char *lhs, 538EvalCompareStr(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". */
556static Token 555static Token
557EvalCompare(CondParser *par, const char *lhs, bool lhsQuoted, 556EvalCompare(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
569static bool 568static bool
570CondParser_ComparisonOp(CondParser *par, ComparisonOp *out_op) 569CondParser_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 */
597static Token 596static Token
598CondParser_Comparison(CondParser *par, bool doEval) 597CondParser_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
631done_lhs: 630done_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 */
640static bool 639static bool
641CondParser_FuncCallEmpty(CondParser *par, bool doEval, Token *out_token) 640CondParser_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})'. */
673static bool 672static bool
674CondParser_FuncCall(CondParser *par, bool doEval, Token *out_token) 673CondParser_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 */
714static Token 713static Token
715CondParser_ComparisonOrLeaf(CondParser *par, bool doEval) 714CondParser_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. */
756static Token 755static Token
757CondParser_Token(CondParser *par, bool doEval) 756CondParser_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. */
825static bool 824static bool
826CondParser_Skip(CondParser *par, Token t) 825CondParser_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 */
846static CondResult 845static CondResult
847CondParser_Term(CondParser *par, bool doEval) 846CondParser_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 */
874static CondResult 873static CondResult
875CondParser_And(CondParser *par, bool doEval) 874CondParser_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 */
895static CondResult 894static CondResult
896CondParser_Or(CondParser *par, bool doEval) 895CondParser_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 */
918static CondResult 917static CondResult
919CondEvalExpression(const char *cond, bool plain, 918CondEvalExpression(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 */
951CondResult 950CondResult
952Cond_EvalCondition(const char *cond) 951Cond_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
958static bool 957static bool
959IsEndif(const char *p) 958IsEndif(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
965static bool 964static bool
966DetermineKindOfConditional(const char **pp, bool *out_plain, 965DetermineKindOfConditional(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
989unknown_directive: 988unknown_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 */
1023CondResult 1022CondResult
1024Cond_EvalLine(const char *line) 1023Cond_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
1176static bool 1175static bool
1177ParseVarnameGuard(const char **pp, const char **varname) 1176ParseVarnameGuard(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. */
1192Guard * 1191Guard *
1193Cond_ExtractGuard(const char *line) 1192Cond_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
1229found_variable: 1228found_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
1236void 1235void
1237Cond_EndFile(void) 1236Cond_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}

cvs diff -r1.330 -r1.331 src/usr.bin/make/make.h (switch to unified diff)

--- 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
170typedef unsigned char bool; 170typedef 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 */
200typedef enum GNodeMade { 200typedef 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 */
243typedef enum GNodeType { 243typedef 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
369typedef struct GNodeFlags { 369typedef 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
390typedef struct List StringList; 390typedef struct List StringList;
391typedef struct ListNode StringListNode; 391typedef struct ListNode StringListNode;
392 392
393typedef struct List GNodeList; 393typedef struct List GNodeList;
394typedef struct ListNode GNodeListNode; 394typedef struct ListNode GNodeListNode;
395 395
396typedef struct SearchPath { 396typedef 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 */
404typedef struct GNode { 404typedef 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 */
514extern enum PosixState { 514extern 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. */
522typedef enum ParseErrorLevel { 522typedef 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 */
537typedef enum CondResult { 537typedef 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
543typedef struct { 543typedef 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 */
565extern bool allPrecious; 565extern bool allPrecious;
566/* True if failed targets should be deleted */ 566/* True if failed targets should be deleted */
567extern bool deleteOnError; 567extern bool deleteOnError;
568/* true while processing .depend */ 568/* true while processing .depend */
569extern bool doing_depend; 569extern bool doing_depend;
570/* .DEFAULT rule */ 570/* .DEFAULT rule */
571extern GNode *defaultNode; 571extern 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 */
577extern GNode *SCOPE_INTERNAL; 577extern 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. */
579extern GNode *SCOPE_GLOBAL; 579extern GNode *SCOPE_GLOBAL;
580/* Variables defined on the command line. */ 580/* Variables defined on the command line. */
581extern GNode *SCOPE_CMDLINE; 581extern 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 */
587extern char var_Error[]; 587extern 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 */
590extern time_t now; 590extern 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 */
596extern SearchPath dirSearchPath; 596extern SearchPath dirSearchPath;
597/* Used for .include "...". */ 597/* Used for .include "...". */
598extern SearchPath *parseIncPath; 598extern 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 */
603extern SearchPath *sysIncPath; 603extern SearchPath *sysIncPath;
604/* The default for sysIncPath. */ 604/* The default for sysIncPath. */
605extern SearchPath *defSysIncPath; 605extern SearchPath *defSysIncPath;
606 606
607/* Startup directory */ 607/* Startup directory */
608extern char curdir[]; 608extern 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. */
610extern const char *progname; 610extern const char *progname;
611extern int makelevel; 611extern int makelevel;
612/* Name of the .depend makefile */ 612/* Name of the .depend makefile */
613extern char *makeDependfile; 613extern char *makeDependfile;
614/* If we replaced environ, this will be non-NULL. */ 614/* If we replaced environ, this will be non-NULL. */
615extern char **savedEnv; 615extern char **savedEnv;
616extern GNode *mainNode; 616extern GNode *mainNode;
617 617
618extern pid_t myPid; 618extern 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
625typedef struct DebugFlags { 625typedef 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
652void debug_printf(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2); 652void 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
673typedef enum PrintVarsMode { 673typedef 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 */
680typedef struct CmdOpts { 680typedef 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
779extern CmdOpts opts; 779extern CmdOpts opts;
780extern bool forceJobs; 780extern bool forceJobs;
781extern char **environ; 781extern char **environ;
782 782
783/* arch.c */ 783/* arch.c */
784void Arch_Init(void); 784void Arch_Init(void);
785void Arch_End(void); 785void Arch_End(void);
786 786
787bool Arch_ParseArchive(char **, GNodeList *, GNode *); 787bool Arch_ParseArchive(char **, GNodeList *, GNode *);
788void Arch_Touch(GNode *); 788void Arch_Touch(GNode *);
789void Arch_TouchLib(GNode *); 789void Arch_TouchLib(GNode *);
790void Arch_UpdateMTime(GNode *); 790void Arch_UpdateMTime(GNode *);
791void Arch_UpdateMemberMTime(GNode *); 791void Arch_UpdateMemberMTime(GNode *);
792void Arch_FindLib(GNode *, SearchPath *); 792void Arch_FindLib(GNode *, SearchPath *);
793bool Arch_LibOODate(GNode *) MAKE_ATTR_USE; 793bool Arch_LibOODate(GNode *) MAKE_ATTR_USE;
794bool Arch_IsLib(GNode *) MAKE_ATTR_USE; 794bool Arch_IsLib(GNode *) MAKE_ATTR_USE;
795 795
796/* compat.c */ 796/* compat.c */
797bool Compat_RunCommand(const char *, GNode *, StringListNode *); 797bool Compat_RunCommand(const char *, GNode *, StringListNode *);
798void Compat_MakeAll(GNodeList *); 798void Compat_MakeAll(GNodeList *);
799void Compat_Make(GNode *, GNode *); 799void Compat_Make(GNode *, GNode *);
800 800
801/* cond.c */ 801/* cond.c */
802extern unsigned int cond_depth; 802extern unsigned int cond_depth;
803CondResult Cond_EvalCondition(const char *) MAKE_ATTR_USE; 803CondResult Cond_EvalCondition(const char *) MAKE_ATTR_USE;
804CondResult Cond_EvalLine(const char *) MAKE_ATTR_USE; 804CondResult Cond_EvalLine(const char *) MAKE_ATTR_USE;
805Guard *Cond_ExtractGuard(const char *) MAKE_ATTR_USE; 805Guard *Cond_ExtractGuard(const char *) MAKE_ATTR_USE;
806void Cond_EndFile(void); 806void Cond_EndFile(void);
807 807
808/* dir.c; see also dir.h */ 808/* dir.c; see also dir.h */
809 809
810MAKE_INLINE const char * MAKE_ATTR_USE 810MAKE_INLINE const char * MAKE_ATTR_USE
811str_basename(const char *pathname) 811str_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
817MAKE_INLINE SearchPath * MAKE_ATTR_USE 817MAKE_INLINE SearchPath * MAKE_ATTR_USE
818SearchPath_New(void) 818SearchPath_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
825void SearchPath_Free(SearchPath *); 825void SearchPath_Free(SearchPath *);
826 826
827/* for.c */ 827/* for.c */
828struct ForLoop; 828struct ForLoop;
829int For_Eval(const char *) MAKE_ATTR_USE; 829int For_Eval(const char *) MAKE_ATTR_USE;
830bool For_Accum(const char *, int *) MAKE_ATTR_USE; 830bool For_Accum(const char *, int *) MAKE_ATTR_USE;
831void For_Run(unsigned, unsigned); 831void For_Run(unsigned, unsigned);
832bool For_NextIteration(struct ForLoop *, Buffer *); 832bool For_NextIteration(struct ForLoop *, Buffer *);
833char *ForLoop_Details(const struct ForLoop *); 833char *ForLoop_Details(const struct ForLoop *);
834void ForLoop_Free(struct ForLoop *); 834void ForLoop_Free(struct ForLoop *);
835void For_Break(struct ForLoop *); 835void For_Break(struct ForLoop *);
836 836
837/* job.c */ 837/* job.c */
838void JobReapChild(pid_t, int, bool); 838void JobReapChild(pid_t, int, bool);
839 839
840/* main.c */ 840/* main.c */
841void Main_ParseArgLine(const char *); 841void Main_ParseArgLine(const char *);
842char *Cmd_Exec(const char *, char **) MAKE_ATTR_USE; 842char *Cmd_Exec(const char *, char **) MAKE_ATTR_USE;
843void Error(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2); 843void Error(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2);
844void Fatal(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2) MAKE_ATTR_DEAD; 844void Fatal(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2) MAKE_ATTR_DEAD;
845void Punt(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2) MAKE_ATTR_DEAD; 845void Punt(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2) MAKE_ATTR_DEAD;
846void DieHorribly(void) MAKE_ATTR_DEAD; 846void DieHorribly(void) MAKE_ATTR_DEAD;
847void Finish(int) MAKE_ATTR_DEAD; 847void Finish(int) MAKE_ATTR_DEAD;
848int unlink_file(const char *) MAKE_ATTR_USE; 848int unlink_file(const char *) MAKE_ATTR_USE;
849void execDie(const char *, const char *); 849void execDie(const char *, const char *);
850char *getTmpdir(void) MAKE_ATTR_USE; 850char *getTmpdir(void) MAKE_ATTR_USE;
851bool ParseBoolean(const char *, bool) MAKE_ATTR_USE; 851bool ParseBoolean(const char *, bool) MAKE_ATTR_USE;
852const char *cached_realpath(const char *, char *); 852const char *cached_realpath(const char *, char *);
853bool GetBooleanExpr(const char *, bool); 853bool GetBooleanExpr(const char *, bool);
854 854
855/* parse.c */ 855/* parse.c */
856void Parse_Init(void); 856void Parse_Init(void);
857void Parse_End(void); 857void Parse_End(void);
858 858
859void PrintLocation(FILE *, bool, const GNode *); 859void PrintLocation(FILE *, bool, const GNode *);
860void PrintStackTrace(bool); 860void PrintStackTrace(bool);
861void Parse_Error(ParseErrorLevel, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3); 861void Parse_Error(ParseErrorLevel, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3);
862bool Parse_VarAssign(const char *, bool, GNode *) MAKE_ATTR_USE; 862bool Parse_VarAssign(const char *, bool, GNode *) MAKE_ATTR_USE;
863void Parse_File(const char *, int); 863void Parse_File(const char *, int);
864void Parse_PushInput(const char *, unsigned, unsigned, Buffer, 864void Parse_PushInput(const char *, unsigned, unsigned, Buffer,
865 struct ForLoop *); 865 struct ForLoop *);
866void Parse_MainName(GNodeList *); 866void Parse_MainName(GNodeList *);
867int Parse_NumErrors(void) MAKE_ATTR_USE; 867int Parse_NumErrors(void) MAKE_ATTR_USE;
868unsigned int CurFile_CondMinDepth(void) MAKE_ATTR_USE; 868unsigned int CurFile_CondMinDepth(void) MAKE_ATTR_USE;
869void Parse_GuardElse(void); 869void Parse_GuardElse(void);
870void Parse_GuardEndif(void); 870void Parse_GuardEndif(void);
871 871
872 872
873/* suff.c */ 873/* suff.c */
874void Suff_Init(void); 874void Suff_Init(void);
875void Suff_End(void); 875void Suff_End(void);
876 876
877void Suff_ClearSuffixes(void); 877void Suff_ClearSuffixes(void);
878bool Suff_IsTransform(const char *) MAKE_ATTR_USE; 878bool Suff_IsTransform(const char *) MAKE_ATTR_USE;
879GNode *Suff_AddTransform(const char *); 879GNode *Suff_AddTransform(const char *);
880void Suff_EndTransform(GNode *); 880void Suff_EndTransform(GNode *);
881void Suff_AddSuffix(const char *); 881void Suff_AddSuffix(const char *);
882SearchPath *Suff_GetPath(const char *) MAKE_ATTR_USE; 882SearchPath *Suff_GetPath(const char *) MAKE_ATTR_USE;
883void Suff_ExtendPaths(void); 883void Suff_ExtendPaths(void);
884void Suff_AddInclude(const char *); 884void Suff_AddInclude(const char *);
885void Suff_AddLib(const char *); 885void Suff_AddLib(const char *);
886void Suff_FindDeps(GNode *); 886void Suff_FindDeps(GNode *);
887SearchPath *Suff_FindPath(GNode *) MAKE_ATTR_USE; 887SearchPath *Suff_FindPath(GNode *) MAKE_ATTR_USE;
888void Suff_SetNull(const char *); 888void Suff_SetNull(const char *);
889void Suff_PrintAll(void); 889void Suff_PrintAll(void);
890char *Suff_NamesStr(void) MAKE_ATTR_USE; 890char *Suff_NamesStr(void) MAKE_ATTR_USE;
891 891
892/* targ.c */ 892/* targ.c */
893void Targ_Init(void); 893void Targ_Init(void);
894void Targ_End(void); 894void Targ_End(void);
895 895
896void Targ_Stats(void); 896void Targ_Stats(void);
897GNodeList *Targ_List(void) MAKE_ATTR_USE; 897GNodeList *Targ_List(void) MAKE_ATTR_USE;
898GNode *GNode_New(const char *) MAKE_ATTR_USE; 898GNode *GNode_New(const char *) MAKE_ATTR_USE;
899GNode *Targ_FindNode(const char *) MAKE_ATTR_USE; 899GNode *Targ_FindNode(const char *) MAKE_ATTR_USE;
900GNode *Targ_GetNode(const char *) MAKE_ATTR_USE; 900GNode *Targ_GetNode(const char *) MAKE_ATTR_USE;
901GNode *Targ_NewInternalNode(const char *) MAKE_ATTR_USE; 901GNode *Targ_NewInternalNode(const char *) MAKE_ATTR_USE;
902GNode *Targ_GetEndNode(void); 902GNode *Targ_GetEndNode(void);
903void Targ_FindList(GNodeList *, StringList *); 903void Targ_FindList(GNodeList *, StringList *);
904void Targ_PrintCmds(GNode *); 904void Targ_PrintCmds(GNode *);
905void Targ_PrintNode(GNode *, int); 905void Targ_PrintNode(GNode *, int);
906void Targ_PrintNodes(GNodeList *, int); 906void Targ_PrintNodes(GNodeList *, int);
907const char *Targ_FmtTime(time_t) MAKE_ATTR_USE; 907const char *Targ_FmtTime(time_t) MAKE_ATTR_USE;
908void Targ_PrintType(GNodeType); 908void Targ_PrintType(GNodeType);
909void Targ_PrintGraph(int); 909void Targ_PrintGraph(int);
910void Targ_Propagate(void); 910void Targ_Propagate(void);
911const char *GNodeMade_Name(GNodeMade) MAKE_ATTR_USE; 911const char *GNodeMade_Name(GNodeMade) MAKE_ATTR_USE;
912 912
913/* var.c */ 913/* var.c */
914void Var_Init(void); 914void Var_Init(void);
915void Var_End(void); 915void Var_End(void);
916 916
917typedef enum VarEvalMode { 917typedef 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
976typedef enum VarSetFlags { 976typedef 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
990typedef enum VarExportMode { 990typedef 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
999void Var_Delete(GNode *, const char *); 999void Var_Delete(GNode *, const char *);
1000void Var_Undef(const char *); 1000void Var_Undef(const char *);
1001void Var_Set(GNode *, const char *, const char *); 1001void Var_Set(GNode *, const char *, const char *);
1002void Var_SetExpand(GNode *, const char *, const char *); 1002void Var_SetExpand(GNode *, const char *, const char *);
1003void Var_SetWithFlags(GNode *, const char *, const char *, VarSetFlags); 1003void Var_SetWithFlags(GNode *, const char *, const char *, VarSetFlags);
1004void Var_Append(GNode *, const char *, const char *); 1004void Var_Append(GNode *, const char *, const char *);
1005void Var_AppendExpand(GNode *, const char *, const char *); 1005void Var_AppendExpand(GNode *, const char *, const char *);
1006bool Var_Exists(GNode *, const char *) MAKE_ATTR_USE; 1006bool Var_Exists(GNode *, const char *) MAKE_ATTR_USE;
1007bool Var_ExistsExpand(GNode *, const char *) MAKE_ATTR_USE; 1007bool Var_ExistsExpand(GNode *, const char *) MAKE_ATTR_USE;
1008FStr Var_Value(GNode *, const char *) MAKE_ATTR_USE; 1008FStr Var_Value(GNode *, const char *) MAKE_ATTR_USE;
1009const char *GNode_ValueDirect(GNode *, const char *) MAKE_ATTR_USE; 1009const char *GNode_ValueDirect(GNode *, const char *) MAKE_ATTR_USE;
1010FStr Var_Parse(const char **, GNode *, VarEvalMode); 1010FStr Var_Parse(const char **, GNode *, VarEvalMode);
1011char *Var_Subst(const char *, GNode *, VarEvalMode); 1011char *Var_Subst(const char *, GNode *, VarEvalMode);
1012void Var_Expand(FStr *, GNode *, VarEvalMode); 1012void Var_Expand(FStr *, GNode *, VarEvalMode);
1013void Var_Stats(void); 1013void Var_Stats(void);
1014void Var_Dump(GNode *); 1014void Var_Dump(GNode *);
1015void Var_ReexportVars(GNode *); 1015void Var_ReexportVars(GNode *);
1016void Var_Export(VarExportMode, const char *); 1016void Var_Export(VarExportMode, const char *);
1017void Var_ExportVars(const char *); 1017void Var_ExportVars(const char *);
1018void Var_UnExport(bool, const char *); 1018void Var_UnExport(bool, const char *);
1019void Var_ReadOnly(const char *, bool); 1019void Var_ReadOnly(const char *, bool);
1020 1020
1021void Global_Set(const char *, const char *); 1021void Global_Set(const char *, const char *);
1022void Global_Append(const char *, const char *); 1022void Global_Append(const char *, const char *);
1023void Global_Delete(const char *); 1023void Global_Delete(const char *);
1024void Global_Set_ReadOnly(const char *, const char *); 1024void Global_Set_ReadOnly(const char *, const char *);
1025 1025
1026void EvalStack_Push(const char *, const char *, const char *); 1026void EvalStack_Push(const char *, const char *, const char *);
1027void EvalStack_Pop(void); 1027void EvalStack_Pop(void);
1028const char *EvalStack_Details(void); 1028const char *EvalStack_Details(void);
1029 1029
1030/* util.c */ 1030/* util.c */
1031typedef void (*SignalProc)(int); 1031typedef void (*SignalProc)(int);
1032SignalProc bmake_signal(int, SignalProc); 1032SignalProc bmake_signal(int, SignalProc);
1033 1033
1034/* make.c */ 1034/* make.c */
1035void GNode_UpdateYoungestChild(GNode *, GNode *); 1035void GNode_UpdateYoungestChild(GNode *, GNode *);
1036bool GNode_IsOODate(GNode *) MAKE_ATTR_USE; 1036bool GNode_IsOODate(GNode *) MAKE_ATTR_USE;
1037void Make_ExpandUse(GNodeList *); 1037void Make_ExpandUse(GNodeList *);
1038time_t Make_Recheck(GNode *) MAKE_ATTR_USE; 1038time_t Make_Recheck(GNode *) MAKE_ATTR_USE;
1039void Make_HandleUse(GNode *, GNode *); 1039void Make_HandleUse(GNode *, GNode *);
1040void Make_Update(GNode *); 1040void Make_Update(GNode *);
1041void GNode_SetLocalVars(GNode *); 1041void GNode_SetLocalVars(GNode *);
1042bool Make_Run(GNodeList *); 1042bool Make_Run(GNodeList *);
1043bool shouldDieQuietly(GNode *, int) MAKE_ATTR_USE; 1043bool shouldDieQuietly(GNode *, int) MAKE_ATTR_USE;
1044void PrintOnError(GNode *, const char *); 1044void PrintOnError(GNode *, const char *);
1045void Main_ExportMAKEFLAGS(bool); 1045void Main_ExportMAKEFLAGS(bool);
1046bool Main_SetObjdir(bool, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3); 1046bool Main_SetObjdir(bool, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3);
1047int mkTempFile(const char *, char *, size_t) MAKE_ATTR_USE; 1047int mkTempFile(const char *, char *, size_t) MAKE_ATTR_USE;
1048void AppendWords(StringList *, char *); 1048void AppendWords(StringList *, char *);
1049void GNode_FprintDetails(FILE *, const char *, const GNode *, const char *); 1049void GNode_FprintDetails(FILE *, const char *, const GNode *, const char *);
1050bool GNode_ShouldExecute(GNode *gn) MAKE_ATTR_USE; 1050bool 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. */
1053MAKE_INLINE bool MAKE_ATTR_USE 1053MAKE_INLINE bool MAKE_ATTR_USE
1054GNode_IsTarget(const GNode *gn) 1054GNode_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
1059MAKE_INLINE const char * MAKE_ATTR_USE 1059MAKE_INLINE const char * MAKE_ATTR_USE
1060GNode_Path(const GNode *gn) 1060GNode_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
1065MAKE_INLINE bool MAKE_ATTR_USE 1065MAKE_INLINE bool MAKE_ATTR_USE
1066GNode_IsWaitingFor(const GNode *gn) 1066GNode_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
1071MAKE_INLINE bool MAKE_ATTR_USE 1071MAKE_INLINE bool MAKE_ATTR_USE
1072GNode_IsReady(const GNode *gn) 1072GNode_IsReady(const GNode *gn)
1073{ 1073{
1074 return gn->made > DEFERRED; 1074 return gn->made > DEFERRED;
1075} 1075}
1076 1076
1077MAKE_INLINE bool MAKE_ATTR_USE 1077MAKE_INLINE bool MAKE_ATTR_USE
1078GNode_IsDone(const GNode *gn) 1078GNode_IsDone(const GNode *gn)
1079{ 1079{
1080 return gn->made >= MADE; 1080 return gn->made >= MADE;
1081} 1081}
1082 1082
1083MAKE_INLINE bool MAKE_ATTR_USE 1083MAKE_INLINE bool MAKE_ATTR_USE
1084GNode_IsError(const GNode *gn) 1084GNode_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
1089MAKE_INLINE bool MAKE_ATTR_USE 1089MAKE_INLINE bool MAKE_ATTR_USE
1090GNode_IsMainCandidate(const GNode *gn) 1090GNode_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. */
1097MAKE_INLINE bool MAKE_ATTR_USE 1097MAKE_INLINE bool MAKE_ATTR_USE
1098GNode_IsPrecious(const GNode *gn) 1098GNode_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
1104MAKE_INLINE const char * MAKE_ATTR_USE 1104MAKE_INLINE const char * MAKE_ATTR_USE
1105GNode_VarTarget(GNode *gn) { return GNode_ValueDirect(gn, TARGET); } 1105GNode_VarTarget(GNode *gn) { return GNode_ValueDirect(gn, TARGET); }
1106MAKE_INLINE const char * MAKE_ATTR_USE 1106MAKE_INLINE const char * MAKE_ATTR_USE
1107GNode_VarOodate(GNode *gn) { return GNode_ValueDirect(gn, OODATE); } 1107GNode_VarOodate(GNode *gn) { return GNode_ValueDirect(gn, OODATE); }
1108MAKE_INLINE const char * MAKE_ATTR_USE 1108MAKE_INLINE const char * MAKE_ATTR_USE
1109GNode_VarAllsrc(GNode *gn) { return GNode_ValueDirect(gn, ALLSRC); } 1109GNode_VarAllsrc(GNode *gn) { return GNode_ValueDirect(gn, ALLSRC); }
1110MAKE_INLINE const char * MAKE_ATTR_USE 1110MAKE_INLINE const char * MAKE_ATTR_USE
1111GNode_VarImpsrc(GNode *gn) { return GNode_ValueDirect(gn, IMPSRC); } 1111GNode_VarImpsrc(GNode *gn) { return GNode_ValueDirect(gn, IMPSRC); }
1112MAKE_INLINE const char * MAKE_ATTR_USE 1112MAKE_INLINE const char * MAKE_ATTR_USE
1113GNode_VarPrefix(GNode *gn) { return GNode_ValueDirect(gn, PREFIX); } 1113GNode_VarPrefix(GNode *gn) { return GNode_ValueDirect(gn, PREFIX); }
1114MAKE_INLINE const char * MAKE_ATTR_USE 1114MAKE_INLINE const char * MAKE_ATTR_USE
1115GNode_VarArchive(GNode *gn) { return GNode_ValueDirect(gn, ARCHIVE); } 1115GNode_VarArchive(GNode *gn) { return GNode_ValueDirect(gn, ARCHIVE); }
1116MAKE_INLINE const char * MAKE_ATTR_USE 1116MAKE_INLINE const char * MAKE_ATTR_USE
1117GNode_VarMember(GNode *gn) { return GNode_ValueDirect(gn, MEMBER); } 1117GNode_VarMember(GNode *gn) { return GNode_ValueDirect(gn, MEMBER); }
1118 1118
1119MAKE_INLINE void * MAKE_ATTR_USE 1119MAKE_INLINE void * MAKE_ATTR_USE
1120UNCONST(const void *ptr) 1120UNCONST(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
1142MAKE_INLINE bool MAKE_ATTR_USE 1142MAKE_INLINE bool MAKE_ATTR_USE
1143ch_isalnum(char ch) { return isalnum((unsigned char)ch) != 0; } 1143ch_isalnum(char ch) { return isalnum((unsigned char)ch) != 0; }
1144MAKE_INLINE bool MAKE_ATTR_USE 1144MAKE_INLINE bool MAKE_ATTR_USE
1145ch_isalpha(char ch) { return isalpha((unsigned char)ch) != 0; } 1145ch_isalpha(char ch) { return isalpha((unsigned char)ch) != 0; }
1146MAKE_INLINE bool MAKE_ATTR_USE 1146MAKE_INLINE bool MAKE_ATTR_USE
1147ch_isdigit(char ch) { return isdigit((unsigned char)ch) != 0; } 1147ch_isdigit(char ch) { return isdigit((unsigned char)ch) != 0; }
1148MAKE_INLINE bool MAKE_ATTR_USE 1148MAKE_INLINE bool MAKE_ATTR_USE
1149ch_islower(char ch) { return islower((unsigned char)ch) != 0; } 1149ch_islower(char ch) { return islower((unsigned char)ch) != 0; }
1150MAKE_INLINE bool MAKE_ATTR_USE 1150MAKE_INLINE bool MAKE_ATTR_USE
1151ch_isspace(char ch) { return isspace((unsigned char)ch) != 0; } 1151ch_isspace(char ch) { return isspace((unsigned char)ch) != 0; }
1152MAKE_INLINE bool MAKE_ATTR_USE 1152MAKE_INLINE bool MAKE_ATTR_USE
1153ch_isupper(char ch) { return isupper((unsigned char)ch) != 0; } 1153ch_isupper(char ch) { return isupper((unsigned char)ch) != 0; }
1154MAKE_INLINE char MAKE_ATTR_USE 1154MAKE_INLINE char MAKE_ATTR_USE
1155ch_tolower(char ch) { return (char)tolower((unsigned char)ch); } 1155ch_tolower(char ch) { return (char)tolower((unsigned char)ch); }
1156MAKE_INLINE char MAKE_ATTR_USE 1156MAKE_INLINE char MAKE_ATTR_USE
1157ch_toupper(char ch) { return (char)toupper((unsigned char)ch); } 1157ch_toupper(char ch) { return (char)toupper((unsigned char)ch); }
1158 1158
1159MAKE_INLINE void 1159MAKE_INLINE void
1160cpp_skip_whitespace(const char **pp) 1160cpp_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
1166MAKE_INLINE void 1166MAKE_INLINE void
1167cpp_skip_hspace(const char **pp) 1167cpp_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
1173MAKE_INLINE bool 1173MAKE_INLINE bool
1174cpp_skip_string(const char **pp, const char *s) 1174cpp_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
1184MAKE_INLINE void 1184MAKE_INLINE void
1185pp_skip_whitespace(char **pp) 1185pp_skip_whitespace(char **pp)
1186{ 1186{
1187 while (ch_isspace(**pp)) 1187 while (ch_isspace(**pp))
1188 (*pp)++; 1188 (*pp)++;
1189} 1189}
1190 1190
1191MAKE_INLINE void 1191MAKE_INLINE void
1192pp_skip_hspace(char **pp) 1192pp_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)
1199void do_not_define_rcsid(void); /* for lint */ 1199void 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

cvs diff -r1.720 -r1.721 src/usr.bin/make/parse.c (switch to unified diff)

--- 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" */
108MAKE_RCSID("$NetBSD: parse.c,v 1.720 2024/04/20 10:18:55 rillig Exp $"); 108MAKE_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. */
111typedef enum { 111typedef 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. */
119typedef struct IncludedFile { 119typedef 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. */
144typedef enum ParseSpecial { 144typedef 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
182typedef List SearchPathList; 182typedef List SearchPathList;
183typedef ListNode SearchPathListNode; 183typedef ListNode SearchPathListNode;
184 184
185 185
186typedef enum VarAssignOp { 186typedef 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
194typedef struct VarAssign { 194typedef 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
200static bool Parse_IsVar(const char *, VarAssign *); 200static bool Parse_IsVar(const char *, VarAssign *);
201static void Parse_Var(VarAssign *, GNode *); 201static 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 */
207GNode *mainNode; 207GNode *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 */
216static GNodeList *targets; 216static 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 */
225static StringList targCmds = LST_INIT; 225static 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 */
232static GNode *order_pred; 232static GNode *order_pred;
233 233
234static int parseErrors; 234static 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 */
243static Vector /* of IncludedFile */ includes; 243static Vector /* of IncludedFile */ includes;
244 244
245SearchPath *parseIncPath; /* directories for "..." includes */ 245SearchPath *parseIncPath; /* directories for "..." includes */
246SearchPath *sysIncPath; /* directories for <...> includes */ 246SearchPath *sysIncPath; /* directories for <...> includes */
247SearchPath *defSysIncPath; /* default for sysIncPath */ 247SearchPath *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 */
253static const struct { 253static 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
305enum PosixState posix_state = PS_NOT_YET; 305enum PosixState posix_state = PS_NOT_YET;
306 306
307static HashTable /* full file name -> Guard */ guards; 307static HashTable /* full file name -> Guard */ guards;
308 308
309 309
310static List * 310static List *
311Lst_New(void) 311Lst_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
318static void 318static void
319Lst_Free(List *list) 319Lst_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
326static IncludedFile * 326static IncludedFile *
327GetInclude(size_t i) 327GetInclude(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. */
334static IncludedFile * 334static IncludedFile *
335CurFile(void) 335CurFile(void)
336{ 336{
337 return GetInclude(includes.len - 1); 337 return GetInclude(includes.len - 1);
338} 338}
339 339
340unsigned int 340unsigned int
341CurFile_CondMinDepth(void) 341CurFile_CondMinDepth(void)
342{ 342{
343 return CurFile()->condMinDepth; 343 return CurFile()->condMinDepth;
344} 344}
345 345
346static Buffer 346static Buffer
347LoadFile(const char *path, int fd) 347LoadFile(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 */
392void 392void
393PrintStackTrace(bool includingInnermost) 393PrintStackTrace(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. */
430static bool 430static bool
431IsEscaped(const char *line, const char *p) 431IsEscaped(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 */
443static void 443static void
444RememberLocation(GNode *gn) 444RememberLocation(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 */
455static int 455static int
456FindKeyword(const char *str) 456FindKeyword(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
476void 476void
477PrintLocation(FILE *f, bool useVars, const GNode *gn) 477PrintLocation(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
515static void MAKE_ATTR_PRINTFLIKE(5, 0) 515static void MAKE_ATTR_PRINTFLIKE(5, 0)
516ParseVErrorInternal(FILE *f, bool useVars, const GNode *gn, 516ParseVErrorInternal(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
545static void MAKE_ATTR_PRINTFLIKE(3, 4) 545static void MAKE_ATTR_PRINTFLIKE(3, 4)
546ParseErrorInternal(const GNode *gn, 546ParseErrorInternal(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 */
572void 572void
573Parse_Error(ParseErrorLevel level, const char *fmt, ...) 573Parse_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 */
595static void 595static void
596HandleMessage(ParseErrorLevel level, const char *levelName, const char *umsg) 596HandleMessage(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 */
622static void 622static void
623LinkSource(GNode *pgn, GNode *cgn, bool isSpecial) 623LinkSource(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. */
647static void 647static void
648LinkToTargets(GNode *gn, bool isSpecial) 648LinkToTargets(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
656static bool 656static bool
657TryApplyDependencyOperator(GNode *gn, GNodeType op) 657TryApplyDependencyOperator(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
715static void 715static void
716ApplyDependencyOperator(GNodeType op) 716ApplyDependencyOperator(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 */
733static void 733static void
734ApplyDependencySourceWait(bool isSpecial) 734ApplyDependencySourceWait(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
748static bool 748static bool
749ApplyDependencySourceKeyword(const char *src, ParseSpecial special) 749ApplyDependencySourceKeyword(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 */
781static void 781static void
782ApplyDependencySourceMain(const char *src) 782ApplyDependencySourceMain(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 */
796static void 796static void
797ApplyDependencySourceOrder(const char *src) 797ApplyDependencySourceOrder(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. */
820static void 820static void
821ApplyDependencySourceOther(const char *src, GNodeType targetAttr, 821ApplyDependencySourceOther(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 */
842static void 842static void
843ApplyDependencySource(GNodeType targetAttr, const char *src, 843ApplyDependencySource(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 */
862static void 862static void
863MaybeUpdateMainTarget(void) 863MaybeUpdateMainTarget(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
881static void 881static void
882InvalidLineType(const char *line, const char *unexpanded_line) 882InvalidLineType(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
901static void 901static void
902ParseDependencyTargetWord(char **pp, const char *lstart) 902ParseDependencyTargetWord(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 */
928static void 928static void
929HandleDependencyTargetSpecial(const char *targetName, 929HandleDependencyTargetSpecial(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
995static bool 995static bool
996HandleDependencyTargetPath(const char *suffixName, 996HandleDependencyTargetPath(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. */
1016static bool 1016static bool
1017HandleDependencyTarget(const char *targetName, 1017HandleDependencyTarget(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
1053static void 1053static void
1054HandleSingleDependencyTargetMundane(const char *name) 1054HandleSingleDependencyTargetMundane(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
1065static void 1065static void
1066HandleDependencyTargetMundane(const char *targetName) 1066HandleDependencyTargetMundane(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
1084static void 1084static void
1085SkipExtraTargets(char **pp, const char *lstart) 1085SkipExtraTargets(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
1107static void 1107static void
1108CheckSpecialMundaneMixture(ParseSpecial special) 1108CheckSpecialMundaneMixture(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 */
1136static GNodeType 1136static GNodeType
1137ParseDependencyOp(char **pp) 1137ParseDependencyOp(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
1149static void 1149static void
1150ClearPaths(ParseSpecial special, SearchPathList *paths) 1150ClearPaths(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
1163static char * 1163static char *
1164FindInDirOfIncludingFile(const char *file) 1164FindInDirOfIncludingFile(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
1194static char * 1194static char *
1195FindInQuotPath(const char *file) 1195FindInQuotPath(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
1213static bool 1213static bool
1214SkipGuarded(const char *fullname) 1214SkipGuarded(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
1225skip: 1225skip:
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 */
1241static void 1241static void
1242IncludeFile(const char *file, bool isSystem, bool depinc, bool silent) 1242IncludeFile(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. */
1285static void 1285static void
1286HandleDependencySourcesEmpty(ParseSpecial special, SearchPathList *paths) 1286HandleDependencySourcesEmpty(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
1321static void 1321static void
1322AddToPaths(const char *dir, SearchPathList *paths) 1322AddToPaths(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 */
1336static void 1336static void
1337ParseDependencySourceSpecial(ParseSpecial special, const char *word, 1337ParseDependencySourceSpecial(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
1371static bool 1371static bool
1372ApplyDependencyTarget(char *name, char *nameEnd, ParseSpecial *inout_special, 1372ApplyDependencyTarget(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
1392static bool 1392static bool
1393ParseDependencyTargets(char **pp, 1393ParseDependencyTargets(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
1446static void 1446static void
1447ParseDependencySourcesSpecial(char *start, 1447ParseDependencySourcesSpecial(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
1467static void 1467static void
1468LinkVarToTargets(VarAssign *var) 1468LinkVarToTargets(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
1476static bool 1476static bool
1477ParseDependencySourcesMundane(char *start, 1477ParseDependencySourcesMundane(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 */
1553static void 1553static void
1554ParseDependencySources(char *p, GNodeType targetAttr, 1554ParseDependencySources(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 */
1627static void 1627static void
1628ParseDependency(char *line, const char *unexpanded_line) 1628ParseDependency(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
1663out: 1663out:
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 */
1672static VarAssign 1672static VarAssign
1673AdjustVarassignOp(const char *name, const char *nameEnd, const char *op, 1673AdjustVarassignOp(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 */
1725static bool 1725static bool
1726Parse_IsVar(const char *p, VarAssign *out_var) 1726Parse_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 */
1792static void 1792static void
1793VarCheckSyntax(VarAssignOp op, const char *uvalue, GNode *scope) 1793VarCheckSyntax(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 ':='. */
1806static void 1806static void
1807VarAssign_EvalSubst(GNode *scope, const char *name, const char *uvalue, 1807VarAssign_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 '!='. */
1833static void 1833static void
1834VarAssign_EvalShell(const char *name, const char *uvalue, GNode *scope, 1834VarAssign_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 */
1865static bool 1865static bool
1866VarAssign_Eval(const char *name, VarAssignOp op, const char *uvalue, 1866VarAssign_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
1890static void 1890static void
1891VarAssignSpecial(const char *name, const char *avalue) 1891VarAssignSpecial(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. */
1910static void 1910static void
1911Parse_Var(VarAssign *var, GNode *scope) 1911Parse_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 */
1927static bool 1927static bool
1928MaybeSubMake(const char *cmd) 1928MaybeSubMake(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. */
1964static void 1964static void
1965GNode_AddCommand(GNode *gn, char *cmd) 1965GNode_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 */
1992static void 1992static void
1993ParseInclude(char *directive) 1993ParseInclude(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 */
2032static void 2032static void
2033SetFilenameVars(const char *filename, const char *dirvar, const char *filevar) 2033SetFilenameVars(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 */
2061static const char * 2061static const char *
2062GetActuallyIncludingFile(void) 2062GetActuallyIncludingFile(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. */
2074static void 2074static void
2075SetParseFile(const char *filename) 2075SetParseFile(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
2091static bool 2091static bool
2092StrContainsWord(const char *str, const char *word) 2092StrContainsWord(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 */
2120static bool 2120static bool
2121VarContainsWord(const char *varname, const char *word) 2121VarContainsWord(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 */
2136static void 2136static void
2137TrackInput(const char *name) 2137TrackInput(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. */
2145void 2145void
2146Parse_PushInput(const char *name, unsigned lineno, unsigned readLines, 2146Parse_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. */
2181static bool 2181static bool
2182IsInclude(const char *dir, bool sysv) 2182IsInclude(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. */
2196static bool 2196static bool
2197IsSysVInclude(const char *line) 2197IsSysVInclude(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". */
2219static void 2219static void
2220ParseTraditionalInclude(char *line) 2220ParseTraditionalInclude(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. */
2252static void 2252static void
2253ParseGmakeExport(char *line) 2253ParseGmakeExport(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 */
2290static bool 2290static bool
2291ParseEOF(void) 2291ParseEOF(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
2336typedef enum ParseRawLineResult { 2336typedef 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 */
2347static ParseRawLineResult 2347static ParseRawLineResult
2348ParseRawLine(IncludedFile *curFile, char **out_line, char **out_line_end, 2348ParseRawLine(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 */
2423static void 2423static void
2424UnescapeBackslash(char *line, char *start) 2424UnescapeBackslash(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
2467typedef enum LineKind { 2467typedef 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 */
2499static char * 2499static char *
2500ReadLowLevelLine(LineKind kind) 2500ReadLowLevelLine(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
2542static bool 2542static bool
2543SkipIrrelevantBranches(void) 2543SkipIrrelevantBranches(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
2553static bool 2553static bool
2554ParseForLoop(const char *line) 2554ParseForLoop(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 */
2595static char * 2595static char *
2596ReadHighLevelLine(void) 2596ReadHighLevelLine(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
2645static void 2645static void
2646FinishDependencyGroup(void) 2646FinishDependencyGroup(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) {

cvs diff -r1.1104 -r1.1105 src/usr.bin/make/var.c (switch to unified diff)

--- 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" */
140MAKE_RCSID("$NetBSD: var.c,v 1.1104 2024/04/21 21:59:48 rillig Exp $"); 135MAKE_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 */
162typedef struct Var { 157typedef 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 */
224typedef enum VarExportedMode { 219typedef 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
230typedef enum UnexportWhat { 225typedef 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 */
246typedef struct PatternFlags { 241typedef 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. */
254typedef struct SepBuf { 249typedef 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
261typedef struct { 256typedef 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
267typedef struct { 262typedef 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). */
275char **savedEnv = NULL; 270char **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 */
282char var_Error[] = ""; 277char 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 */
292static char varUndefined[] = ""; 287static 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"
303static bool save_dollars = true; 298static 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
325GNode *SCOPE_CMDLINE; 320GNode *SCOPE_CMDLINE;
326GNode *SCOPE_GLOBAL; 321GNode *SCOPE_GLOBAL;
327GNode *SCOPE_INTERNAL; 322GNode *SCOPE_INTERNAL;
328 323
329static VarExportedMode var_exportedVars = VAR_EXPORTED_NONE; 324static VarExportedMode var_exportedVars = VAR_EXPORTED_NONE;
330 325
331static const char VarEvalMode_Name[][32] = { 326static 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
341static EvalStack evalStack; 336static EvalStack evalStack;
342 337
343 338
344void 339void
345EvalStack_Push(const char *target, const char *expr, const char *varname) 340EvalStack_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
358void 353void
359EvalStack_Pop(void) 354EvalStack_Pop(void)
360{ 355{
361 assert(evalStack.len > 0); 356 assert(evalStack.len > 0);
362 evalStack.len--; 357 evalStack.len--;
363} 358}
364 359
365const char * 360const char *
366EvalStack_Details(void) 361EvalStack_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
394static Var * 389static Var *
395VarNew(FStr name, const char *value, 390VarNew(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
413static Substring 408static Substring
414CanonicalVarname(Substring name) 409CanonicalVarname(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
443static Var * 438static Var *
444GNode_FindVar(GNode *scope, Substring varname, unsigned int hash) 439GNode_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 */
462static Var * 457static Var *
463VarFindSubstring(Substring name, GNode *scope, bool elsewhere) 458VarFindSubstring(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
511static Var * 503static Var *
512VarFind(const char *name, GNode *scope, bool elsewhere) 504VarFind(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. */
518static void 510static void
519VarFreeShortLived(Var *v) 511VarFreeShortLived(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
529static const char * 521static const char *
530ValueDescription(const char *value) 522ValueDescription(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. */
540static Var * 532static Var *
541VarAdd(const char *name, const char *value, GNode *scope, VarSetFlags flags) 533VarAdd(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 */
556void 548void
557Var_Delete(GNode *scope, const char *varname) 549Var_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 */
597void 589void
598Var_Undef(const char *arg) 590Var_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
631static bool 623static bool
632MayExport(const char *name) 624MayExport(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
656static bool 648static bool
657ExportVarEnv(Var *v, GNode *scope) 649ExportVarEnv(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
691static bool 683static bool
692ExportVarPlain(Var *v) 684ExportVarPlain(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
712static bool 704static bool
713ExportVarLiteral(Var *v) 705ExportVarLiteral(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 */
729static bool 721static bool
730ExportVar(const char *name, GNode *scope, VarExportMode mode) 722ExportVar(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 */
755void 747void
756Var_ReexportVars(GNode *scope) 748Var_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
799static void 791static void
800ExportVars(const char *varnames, bool isExport, VarExportMode mode) 792ExportVars(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
823static void 815static void
824ExportVarsExpand(const char *uvarnames, bool isExport, VarExportMode mode) 816ExportVarsExpand(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. */
833void 825void
834Var_Export(VarExportMode mode, const char *varnames) 826Var_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
844void 836void
845Var_ExportVars(const char *varnames) 837Var_ExportVars(const char *varnames)
846{ 838{
847 ExportVarsExpand(varnames, false, VEM_PLAIN); 839 ExportVarsExpand(varnames, false, VEM_PLAIN);
848} 840}
849 841
850 842
851static void 843static void
852ClearEnv(void) 844ClearEnv(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
877static void 869static void
878GetVarnamesToUnexport(bool isEnv, const char *arg, 870GetVarnamesToUnexport(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
910static void 902static void
911UnexportVar(Substring varname, UnexportWhat what) 903UnexportVar(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
940static void 932static void
941UnexportVars(FStr *varnames, UnexportWhat what) 933UnexportVars(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. */
959void 951void
960Var_UnExport(bool isEnv, const char *arg) 952Var_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. */
971void 963void
972Var_SetWithFlags(GNode *scope, const char *name, const char *val, 964Var_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
1074void 1067void
1075Var_Set(GNode *scope, const char *name, const char *val) 1068Var_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 */
1084void 1077void
1085Var_SetExpand(GNode *scope, const char *name, const char *val) 1078Var_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
1104void 1097void
1105Global_Set(const char *name, const char *value) 1098Global_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
1110void 1103void
1111Global_Delete(const char *name) 1104Global_Delete(const char *name)
1112{ 1105{
1113 Var_Delete(SCOPE_GLOBAL, name); 1106 Var_Delete(SCOPE_GLOBAL, name);
1114} 1107}
1115 1108
1116void 1109void
1117Global_Set_ReadOnly(const char *name, const char *value) 1110Global_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 */
1128void 1121void
1129Var_Append(GNode *scope, const char *name, const char *val) 1122Var_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 */
1167void 1160void
1168Var_AppendExpand(GNode *scope, const char *name, const char *val) 1161Var_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
1186void 1179void
1187Global_Append(const char *name, const char *value) 1180Global_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
1192bool 1185bool
1193Var_Exists(GNode *scope, const char *name) 1186Var_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 */
1211bool 1204bool
1212Var_ExistsExpand(GNode *scope, const char *name) 1205Var_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 */
1235FStr 1228FStr
1236Var_Value(GNode *scope, const char *name) 1229Var_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. */
1255void 1248void
1256Var_ReadOnly(const char *name, bool bf) 1249Var_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 */
1273const char * 1266const char *
1274GNode_ValueDirect(GNode *gn, const char *name) 1267GNode_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
1280static VarEvalMode 1273static VarEvalMode
1281VarEvalMode_WithoutKeepDollar(VarEvalMode emode) 1274VarEvalMode_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
1290static VarEvalMode 1283static VarEvalMode
1291VarEvalMode_UndefOk(VarEvalMode emode) 1284VarEvalMode_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
1296static bool 1289static bool
1297VarEvalMode_ShouldEval(VarEvalMode emode) 1290VarEvalMode_ShouldEval(VarEvalMode emode)
1298{ 1291{
1299 return emode != VARE_PARSE_ONLY; 1292 return emode != VARE_PARSE_ONLY;
1300} 1293}
1301 1294
1302static bool 1295static bool
1303VarEvalMode_ShouldKeepUndef(VarEvalMode emode) 1296VarEvalMode_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
1309static bool 1302static bool
1310VarEvalMode_ShouldKeepDollar(VarEvalMode emode) 1303VarEvalMode_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
1317static void 1310static void
1318SepBuf_Init(SepBuf *buf, char sep) 1311SepBuf_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
1325static void 1318static void
1326SepBuf_Sep(SepBuf *buf) 1319SepBuf_Sep(SepBuf *buf)
1327{ 1320{
1328 buf->needSep = true; 1321 buf->needSep = true;
1329} 1322}
1330 1323
1331static void 1324static void
1332SepBuf_AddBytes(SepBuf *buf, const char *mem, size_t mem_size) 1325SepBuf_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
1343static void 1336static void
1344SepBuf_AddRange(SepBuf *buf, const char *start, const char *end) 1337SepBuf_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
1349static void 1342static void
1350SepBuf_AddStr(SepBuf *buf, const char *str) 1343SepBuf_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
1355static void 1348static void
1356SepBuf_AddSubstring(SepBuf *buf, Substring sub) 1349SepBuf_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
1361static char * 1354static char *
1362SepBuf_DoneData(SepBuf *buf) 1355SepBuf_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 */
1380typedef void (*ModifyWordProc)(Substring word, SepBuf *buf, void *data); 1373typedef void (*ModifyWordProc)(Substring word, SepBuf *buf, void *data);
1381 1374
1382 1375
1383/*ARGSUSED*/ 1376/*ARGSUSED*/
1384static void 1377static void
1385ModifyWord_Head(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) 1378ModifyWord_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*/
1391static void 1384static void
1392ModifyWord_Tail(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) 1385ModifyWord_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*/
1398static void 1391static void
1399ModifyWord_Suffix(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) 1392ModifyWord_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*/
1407static void 1400static void
1408ModifyWord_Root(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) 1401ModifyWord_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
1417struct ModifyWord_SysVSubstArgs { 1410struct 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
1425static void 1418static void
1426ModifyWord_SysVSubst(Substring word, SepBuf *buf, void *data) 1419ModifyWord_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
1457static const char * 1450static const char *
1458Substring_Find(Substring haystack, Substring needle) 1451Substring_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
1470struct ModifyWord_SubstArgs { 1463struct 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
1477static void 1470static void
1478ModifyWord_Subst(Substring word, SepBuf *buf, void *data) 1471ModifyWord_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 }
1529nosub: 1522nosub:
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. */
1534static void 1527static void
1535RegexError(int reerr, const regex_t *pat, const char *str) 1528RegexError(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. */
1545static void 1538static void
1546RegexReplaceBackref(char ref, SepBuf *buf, const char *wp, 1539RegexReplaceBackref(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 */
1567static void 1560static void
1568RegexReplace(Substring replace, SepBuf *buf, const char *wp, 1561RegexReplace(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
1589struct ModifyWord_SubstRegexArgs { 1582struct 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
1597static void 1590static void
1598ModifyWord_SubstRegex(Substring word, SepBuf *buf, void *data) 1591ModifyWord_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
1611again: 1604again:
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");
1617no_match: 1610no_match:
1618 SepBuf_AddRange(buf, wp, word.end); 1611 SepBuf_AddRange(buf, wp, word.end);
1619 return; 1612 return;
1620 1613
1621ok: 1614ok:
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
1642struct ModifyWord_LoopArgs { 1635struct 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
1649static void 1642static void
1650ModifyWord_Loop(Substring word, SepBuf *buf, void *data) 1643ModifyWord_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 */
1679static char * 1672static char *
1680VarSelectWords(const char *str, int first, int last, 1673VarSelectWords(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*/
1729static void 1722static void
1730ModifyWord_Realpath(Substring word, SepBuf *buf, void *data MAKE_ATTR_UNUSED) 1723ModifyWord_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
1745static char * 1738static char *
1746SubstringWords_JoinFree(SubstringWords words) 1739SubstringWords_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 */
1773static void 1766static void
1774QuoteShell(const char *str, bool quoteDollar, LazyBuf *buf) 1767QuoteShell(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 */
1799static char * 1792static char *
1800Hash(const char *str) 1793Hash(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
1859static char * 1852static char *
1860FormatTime(const char *fmt, time_t t, bool gmt) 1853FormatTime(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
1942typedef enum ExprDefined { 1935typedef 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
1955static const char ExprDefined_Name[][10] = { 1948static 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}. */
1968typedef struct Expr { 1961typedef 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 */
1999typedef struct ModChain { 1992typedef 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
3724done: 3717done:
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 */
3731static ApplyModifierResult 3724static ApplyModifierResult
3732ApplyModifier_SunShell(const char **pp, ModChain *ch) 3725ApplyModifier_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 */
3757static bool 3750static bool
3758ShouldLogInSimpleFormat(const Expr *expr) 3751ShouldLogInSimpleFormat(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
3765static void 3758static void
3766LogBeforeApply(const ModChain *ch, const char *mod) 3759LogBeforeApply(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
3796static void 3789static void
3797LogAfterApply(const ModChain *ch, const char *p, const char *mod) 3790LogAfterApply(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
3817static ApplyModifierResult 3810static ApplyModifierResult
3818ApplyModifier(const char **pp, ModChain *ch) 3811ApplyModifier(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
3880static void ApplyModifiers(Expr *, const char **, char, char); 3873static void ApplyModifiers(Expr *, const char **, char, char);
3881 3874
3882typedef enum ApplyModifiersIndirectResult { 3875typedef 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 */
3904static ApplyModifiersIndirectResult 3897static ApplyModifiersIndirectResult
3905ApplyModifiersIndirect(ModChain *ch, const char **pp) 3898ApplyModifiersIndirect(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
3945static ApplyModifierResult 3938static ApplyModifierResult
3946ApplySingleModifier(const char **pp, ModChain *ch) 3939ApplySingleModifier(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
4009MAKE_INLINE ModChain 4002MAKE_INLINE ModChain
4010ModChain_Init(Expr *expr, char startc, char endc, char sep, bool oneBigWord) 4003ModChain_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). */
4023static void 4016static void
4024ApplyModifiers( 4017ApplyModifiers(
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
4078bad_modifier: 4071bad_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
4083cleanup: 4076cleanup:
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 */
4102static bool 4095static bool
4103VarnameIsDynamic(Substring varname) 4096VarnameIsDynamic(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
4131static const char * 4124static const char *
4132UndefinedShortVarValue(char varname, const GNode *scope) 4125UndefinedShortVarValue(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 */
4162static void 4155static void
4163ParseVarname(const char **pp, char startc, char endc, 4156ParseVarname(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
4193static bool 4186static bool
4194IsShortVarnameValid(char varname, const char *start) 4187IsShortVarnameValid(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 */
4219static bool 4212static bool
4220ParseVarnameShort(char varname, const char **pp, GNode *scope, 4213ParseVarnameShort(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. */
4260static Var * 4253static Var *
4261FindLocalLegacyVar(Substring varname, GNode *scope, 4254FindLocalLegacyVar(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
4286static FStr 4279static FStr
4287EvalUndefined(bool dynamic, const char *start, const char *p, 4280EvalUndefined(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 */
4310static bool 4303static bool
4311ParseVarnameLong( 4304ParseVarnameLong(
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
4419MAKE_INLINE Expr 4412MAKE_INLINE Expr
4420Expr_Init(const char *name, FStr value, 4413Expr_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 */
4438static bool 4431static bool
4439Var_Parse_FastLane(const char **pp, VarEvalMode emode, FStr *out_value) 4432Var_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 */
4495FStr 4488FStr
4496Var_Parse(const char **pp, GNode *scope, VarEvalMode emode) 4489Var_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
4635static void 4628static void
4636VarSubstDollarDollar(const char **pp, Buffer *res, VarEvalMode emode) 4629VarSubstDollarDollar(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
4645static void 4638static void
4646VarSubstExpr(const char **pp, Buffer *buf, GNode *scope, 4639VarSubstExpr(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 */
4702static void 4695static void
4703VarSubstPlain(const char **pp, Buffer *res) 4696VarSubstPlain(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 */
4725char * 4717char *
4726Var_Subst(const char *str, GNode *scope, VarEvalMode emode) 4718Var_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
4752void 4744void
4753Var_Expand(FStr *str, GNode *scope, VarEvalMode emode) 4745Var_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. */
4766void 4758void
4767Var_Init(void) 4759Var_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. */
4775void 4767void
4776Var_End(void) 4768Var_End(void)
4777{ 4769{
4778 Var_Stats(); 4770 Var_Stats();
4779} 4771}
4780 4772
4781void 4773void
4782Var_Stats(void) 4774Var_Stats(void)
4783{ 4775{
4784 HashTable_DebugStats(&SCOPE_GLOBAL->vars, "Global variables"); 4776 HashTable_DebugStats(&SCOPE_GLOBAL->vars, "Global variables");
4785} 4777}
4786 4778
4787static int 4779static int
4788StrAsc(const void *sa, const void *sb) 4780StrAsc(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. */
4796void 4788void
4797Var_Dump(GNode *scope) 4789Var_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}

cvs diff -r1.5 -r1.6 src/usr.bin/make/unit-tests/cmd-errors-jobs.exp (switch to unified diff)

--- 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
2make: in target "unclosed-variable": Unclosed variable "UNCLOSED" 2make: in target "unclosed-expression": Unclosed variable "UNCLOSED"
3: unclosed-variable- 3: unclosed-expression-
4make: Unclosed expression, expecting '}' for "UNCLOSED" 4make: Unclosed expression, expecting '}' for "UNCLOSED"
5: unclosed-modifier- 5: unclosed-modifier-
6make: in target "unknown-modifier": while evaluating variable "UNKNOWN": Unknown modifier "Z" 6make: 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
9exit status 0 9exit status 0

cvs diff -r1.5 -r1.6 src/usr.bin/make/unit-tests/cmd-errors-lint.exp (switch to unified diff)

--- 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
2make: in target "unclosed-variable": Unclosed variable "UNCLOSED" 2make: in target "unclosed-expression": Unclosed variable "UNCLOSED"
3: unclosed-variable  3: unclosed-expression
4make: Unclosed expression, expecting '}' for "UNCLOSED" 4make: Unclosed expression, expecting '}' for "UNCLOSED"
5: unclosed-modifier  5: unclosed-modifier
6make: in target "unknown-modifier": while evaluating variable "UNKNOWN": Unknown modifier "Z" 6make: in target "unknown-modifier": while evaluating variable "UNKNOWN": Unknown modifier "Z"
7: unknown-modifier  7: unknown-modifier
8: end 8: end
9exit status 2 9exit status 2

cvs diff -r1.5 -r1.6 src/usr.bin/make/unit-tests/cmd-errors.mk (switch to unified diff)

--- 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
6all: undefined unclosed-variable unclosed-modifier unknown-modifier end 6all: 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.
9undefined: 10undefined:
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.
14unclosed-variable: 15unclosed-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.
19unclosed-modifier: 20unclosed-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.
24unknown-modifier: 25unknown-modifier:
25 : $@-${UNKNOWN:Z}-eol 26 : $@-${UNKNOWN:Z}-eol
26 27
27end: 28end:
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.

cvs diff -r1.3 -r1.4 src/usr.bin/make/unit-tests/cmd-errors-jobs.mk (switch to unified diff)

--- 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
8all: undefined unclosed-variable unclosed-modifier unknown-modifier end 8all: 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
12undefined: 13undefined:
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-
18unclosed-variable: 19unclosed-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-
24unclosed-modifier: 25unclosed-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
30unknown-modifier: 31unknown-modifier:
31 : $@-${UNKNOWN:Z}-eol 32 : $@-${UNKNOWN:Z}-eol
32 33
33# expect: : end-eol 34# expect: : end-eol
34end: 35end:
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

cvs diff -r1.1 -r1.2 src/usr.bin/make/unit-tests/cmd-errors-lint.mk (switch to unified diff)

--- 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
8all: undefined unclosed-variable unclosed-modifier unknown-modifier end 8all: 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.
11undefined: 12undefined:
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.
17unclosed-variable: 18unclosed-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.
23unclosed-modifier: 24unclosed-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.
28unknown-modifier: 29unknown-modifier:
29 : $@ ${UNKNOWN:Z} 30 : $@ ${UNKNOWN:Z}
30 31
31end: 32end:
32 : $@ 33 : $@

cvs diff -r1.8 -r1.9 src/usr.bin/make/unit-tests/cmd-errors.exp (switch to unified diff)

--- 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
2make: in target "unclosed-variable": Unclosed variable "UNCLOSED" 2make: in target "unclosed-expression": Unclosed variable "UNCLOSED"
3: unclosed-variable- 3: unclosed-expression-
4make: Unclosed expression, expecting '}' for "UNCLOSED" 4make: Unclosed expression, expecting '}' for "UNCLOSED"
5: unclosed-modifier- 5: unclosed-modifier-
6make: in target "unknown-modifier": while evaluating variable "UNKNOWN": Unknown modifier "Z" 6make: 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
9exit status 0 9exit status 0

cvs diff -r1.8 -r1.9 src/usr.bin/make/unit-tests/cond-func-defined.exp (switch to unified diff)

--- 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 @@
1make: "cond-func-defined.mk" line 24: Missing closing parenthesis for defined() 1make: "cond-func-defined.mk" line 24: Missing closing parenthesis for defined()
2make: "cond-func-defined.mk" line 34: Missing closing parenthesis for defined() 2make: "cond-func-defined.mk" line 34: Missing closing parenthesis for defined()
3make: "cond-func-defined.mk" line 47: In .for loops, expressions for the loop variables are 
4make: "cond-func-defined.mk" line 49: substituted at evaluation time. There is no actual variable 
5make: "cond-func-defined.mk" line 51: involved, even if it feels like it. 
6make: Fatal errors encountered -- cannot continue 3make: Fatal errors encountered -- cannot continue
7make: stopped in unit-tests 4make: stopped in unit-tests
8exit status 1 5exit status 1

cvs diff -r1.4 -r1.5 src/usr.bin/make/unit-tests/cmdline-undefined.mk (switch to unified diff)

--- 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
5all: 5all:
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
39UNDEFINED?= now defined 39UNDEFINED?= 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
51print-undefined: 51print-undefined:
52.endif 52.endif

cvs diff -r1.4 -r1.5 src/usr.bin/make/unit-tests/cmdline.mk (switch to unified diff)

--- 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
5TMPBASE?= ${TMPDIR:U/tmp/uid${.MAKE.UID}} 5TMPBASE?= ${TMPDIR:U/tmp/uid${.MAKE.UID}}
6SUB1= a7b41170-53f8-4cc2-bc5c-e4c3dd93ec45 # just a random UUID 6SUB1= a7b41170-53f8-4cc2-bc5c-e4c3dd93ec45 # just a random UUID
7SUB2= 6a8899d2-d227-4b55-9b6b-f3c8eeb83fd5 # just a random UUID 7SUB2= 6a8899d2-d227-4b55-9b6b-f3c8eeb83fd5 # just a random UUID
8MAKE_CMD= env TMPBASE=${TMPBASE}/${SUB1} ${.MAKE} -f ${MAKEFILE} -r 8MAKE_CMD= env TMPBASE=${TMPBASE}/${SUB1} ${.MAKE} -f ${MAKEFILE} -r
9DIR2= ${TMPBASE}/${SUB2} 9DIR2= ${TMPBASE}/${SUB2}
10DIR12= ${TMPBASE}/${SUB1}/${SUB2} 10DIR12= ${TMPBASE}/${SUB1}/${SUB2}
11 11
12all: prepare-dirs 12all: prepare-dirs
13all: makeobjdir-direct makeobjdir-indirect 13all: makeobjdir-direct makeobjdir-indirect
14all: space-and-comment 14all: space-and-comment
15 15
16prepare-dirs: 16prepare-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.
22makeobjdir-direct: 22makeobjdir-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.
32makeobjdir-indirect: 32makeobjdir-indirect:
33 @echo $@: 33 @echo $@:
34 @${MAKE_CMD} MAKEOBJDIR='$${TMPBASE}/$${SUB2}' show-objdir 34 @${MAKE_CMD} MAKEOBJDIR='$${TMPBASE}/$${SUB2}' show-objdir
35 35
36show-objdir: 36show-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.
49space-and-comment: .PHONY 49space-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,$$,$$,'

cvs diff -r1.6 -r1.7 src/usr.bin/make/unit-tests/comment.mk (switch to unified diff)

--- 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#\
8This is a multiline comment. 8This is a multiline comment.
9 9
10# Another multiline comment \ 10# Another multiline comment \
11that \ 11that \
12goes \ 12goes \
13on and on. 13on 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
25VAR= # This comment makes the variable value empty. 25VAR= # 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.
34VAR=# defined but empty 34VAR=# defined but empty
35 35
36# The space before the comment is always trimmed. 36# The space before the comment is always trimmed.
37VAR= value 37VAR= 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. \\
45VAR= not part of the comment 45VAR= 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.
51VAR= \# # Both in the assignment. 51VAR= \# # 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.
59WORDS= ${VAR:[#]} [# 59WORDS= ${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, \\\
65no matter if it is 3 or 5 \\\\\ 65no matter if it is 3 or 5 \\\\\
66or 9 backslashes. \\\\\\\\\ 66or 9 backslashes. \\\\\\\\\
67This is the last line of the comment. 67This is the last line of the comment.
68VAR= no comment anymore 68VAR= 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
73all: 73all:
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.

cvs diff -r1.18 -r1.19 src/usr.bin/make/unit-tests/cond-cmp-string.mk (switch to unified diff)

--- 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.
149VAR1= value1 149VAR1= value1
150VAR2= value2 150VAR2= value2
151.if ${VAR1} != ${VAR2} 151.if ${VAR1} != ${VAR2}
152.else 152.else
153. error 153. error
154.endif 154.endif

cvs diff -r1.11 -r1.12 src/usr.bin/make/unit-tests/cond-func-defined.mk (switch to unified diff)

--- 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
5DEF= defined 5DEF= 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
61all: .PHONY 58all: .PHONY

cvs diff -r1.27 -r1.28 src/usr.bin/make/unit-tests/varmod-ifelse.mk (switch to unified diff)

--- 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.
38COND:= ${${UNDEF} == "":?bad-assign:bad-assign} 38COND:= ${${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
112VAR= value 112VAR= 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.
155STRING= string 155STRING= string
156NUMBER= no # not really a number 156NUMBER= 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.
166NUMBER= # empty, not really a number either 166NUMBER= # 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.
175PLUS= + 175PLUS= +
176ASTERISK= * 176ASTERISK= *
177EMPTY= # empty 177EMPTY= # 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.
195PRIMES= 2 3 5 7 11 195PRIMES= 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.
280DELAYED= two 280DELAYED= 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}
285INDIRECT_COND1= $${DELAYED} == "one" 285INDIRECT_COND1= $${DELAYED} == "one"
286# expect+1: no 286# expect+1: no
287.info ${ ${INDIRECT_COND1}:?yes:no} 287.info ${ ${INDIRECT_COND1}:?yes:no}
288INDIRECT_COND2= $${DELAYED} == "two" 288INDIRECT_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.
301BRACES= }}} 301BRACES= }}}
302NO= ${0:?${BRACES:S,}}},yes,}:${BRACES:S,}}},no,}} 302NO= ${0:?${BRACES:S,}}},yes,}:${BRACES:S,}}},no,}}
303YES= ${1:?${BRACES:S,}}},yes,}:${BRACES:S,}}},no,}} 303YES= ${1:?${BRACES:S,}}},yes,}:${BRACES:S,}}},no,}}
304BOTH= <${YES}> <${NO}> 304BOTH= <${YES}> <${NO}>
305.if ${BOTH} != "<yes> <no>" 305.if ${BOTH} != "<yes> <no>"
306. error 306. error
307.endif 307.endif

cvs diff -r1.15 -r1.16 src/usr.bin/make/unit-tests/varmod-match.exp (switch to unified diff)

--- 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 @@
1make: "varmod-match.mk" line 289: while evaluating variable "WORDS": warning: Unfinished character list in pattern 'a[' of modifier ':M' 1make: "varmod-match.mk" line 290: while evaluating variable "WORDS": warning: Unfinished character list in pattern 'a[' of modifier ':M'
2make: "varmod-match.mk" line 297: while evaluating variable "WORDS": warning: Unfinished character list in pattern 'a[^' of modifier ':M' 2make: "varmod-match.mk" line 298: while evaluating variable "WORDS": warning: Unfinished character list in pattern 'a[^' of modifier ':M'
3make: "varmod-match.mk" line 305: while evaluating variable "WORDS": warning: Unfinished character list in pattern '[-x1-3' of modifier ':M' 3make: "varmod-match.mk" line 306: while evaluating variable "WORDS": warning: Unfinished character list in pattern '[-x1-3' of modifier ':M'
4make: "varmod-match.mk" line 313: while evaluating variable "WORDS": warning: Unfinished character list in pattern '*[-x1-3' of modifier ':M' 4make: "varmod-match.mk" line 314: while evaluating variable "WORDS": warning: Unfinished character list in pattern '*[-x1-3' of modifier ':M'
5make: "varmod-match.mk" line 322: while evaluating variable "WORDS": warning: Unfinished character list in pattern '[^-x1-3' of modifier ':M' 5make: "varmod-match.mk" line 323: while evaluating variable "WORDS": warning: Unfinished character list in pattern '[^-x1-3' of modifier ':M'
6make: "varmod-match.mk" line 336: while evaluating variable "WORDS": warning: Unfinished character list in pattern '?[\' of modifier ':M' 6make: "varmod-match.mk" line 337: while evaluating variable "WORDS": warning: Unfinished character list in pattern '?[\' of modifier ':M'
7make: "varmod-match.mk" line 344: while evaluating variable "WORDS": warning: Unfinished character range in pattern '[x-' of modifier ':M' 7make: "varmod-match.mk" line 345: while evaluating variable "WORDS": warning: Unfinished character range in pattern '[x-' of modifier ':M'
8make: "varmod-match.mk" line 356: while evaluating variable "WORDS": warning: Unfinished character range in pattern '[^x-' of modifier ':M' 8make: "varmod-match.mk" line 357: while evaluating variable "WORDS": warning: Unfinished character range in pattern '[^x-' of modifier ':M'
9make: "varmod-match.mk" line 364: while evaluating variable " : :: ": warning: Unfinished character list in pattern '[' of modifier ':M' 9make: "varmod-match.mk" line 365: while evaluating variable " : :: ": warning: Unfinished character list in pattern '[' of modifier ':M'
10make: "varmod-match.mk" line 364: while evaluating variable " : :: ": Unknown modifier "]" 10make: "varmod-match.mk" line 365: while evaluating variable " : :: ": Unknown modifier "]"
11make: "varmod-match.mk" line 364: Malformed conditional (${ ${:U\:} ${:U\:\:} :L:M[:]} != ":") 11make: "varmod-match.mk" line 365: Malformed conditional (${ ${:U\:} ${:U\:\:} :L:M[:]} != ":")
12make: Fatal errors encountered -- cannot continue 12make: Fatal errors encountered -- cannot continue
13make: stopped in unit-tests 13make: stopped in unit-tests
14exit status 1 14exit status 1

cvs diff -r1.21 -r1.22 src/usr.bin/make/unit-tests/varmod-match.mk (switch to unified diff)

--- 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.
141WORDS= a\b a[\]b ab a\\b 141WORDS= 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.
150WORDS= [ ] [] \] ]] 150WORDS= [ ] [] \] ]]
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.
158WORDS= [a \a ]a []a \]a ]]a [a] \a] ]a] ba] 158WORDS= [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.
166WORDS= - -] 166WORDS= - -]
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.
201PRIMES= 2 3 5 7 11 201PRIMES= 2 3 5 7 11
202n= 2 202n= 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.
287WORDS= a a[ 288WORDS= 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.
295WORDS= a a[ aX 296WORDS= 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.
303WORDS= - + x xx 0 1 2 3 4 [x1-3 304WORDS= - + 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.
311WORDS= - + x xx 0 1 2 3 4 00 01 10 11 000 001 010 011 100 101 110 111 [x1-3 312WORDS= - + 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.
320WORDS= - + x xx 0 1 2 3 4 [x1-3 321WORDS= - + 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.
333WORDS= \\ \a ${:Ux\\} 334WORDS= \\ \a ${:Ux\\}
334PATTERN= ${:U?[\\} 335PATTERN= ${: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'.
342WORDS= [x- x x- y 343WORDS= [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.
354WORDS= [x- x x- y yyyyy 355WORDS= [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