Sun Mar 14 10:57:13 2021 UTC ()
make: fix wrong expression evaluation in -dL mode

The modifier ':C' now only compiles the regular expression if the result
of the expression is actually needed.

Several other modifiers have the same bug of evaluating the expression
in cases where this is not needed.  It just doesn't show up because they
don't have any noticeable side effects, other than wasting CPU time.
This affects irrelevant conditions as well.


(rillig)
diff -r1.855 -r1.856 src/usr.bin/make/var.c
diff -r1.15 -r1.16 src/usr.bin/make/unit-tests/opt-debug-lint.exp
diff -r1.13 -r1.14 src/usr.bin/make/unit-tests/opt-debug-lint.mk

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

--- src/usr.bin/make/var.c 2021/02/23 16:29:52 1.855
+++ src/usr.bin/make/var.c 2021/03/14 10:57:12 1.856
@@ -1,1142 +1,1142 @@ @@ -1,1142 +1,1142 @@
1/* $NetBSD: var.c,v 1.855 2021/02/23 16:29:52 rillig Exp $ */ 1/* $NetBSD: var.c,v 1.856 2021/03/14 10:57:12 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
85 * Set the value of the variable, creating it if 85 * Set the value of the variable, creating it if
86 * necessary. 86 * necessary.
87 * 87 *
88 * Var_Append 88 * Var_Append
89 * Var_AppendExpand 89 * Var_AppendExpand
90 * Append more characters to the variable, creating it if 90 * Append more characters to the variable, creating it if
91 * necessary. A space is placed between the old value and 91 * necessary. A space is placed between the old value and
92 * the new one. 92 * the new one.
93 * 93 *
94 * Var_Exists 94 * Var_Exists
95 * Var_ExistsExpand 95 * Var_ExistsExpand
96 * See if a variable exists. 96 * See if a variable exists.
97 * 97 *
98 * Var_Value Return the unexpanded value of a variable, or NULL if 98 * Var_Value Return the unexpanded value of a variable, or NULL if
99 * the variable is undefined. 99 * the variable is undefined.
100 * 100 *
101 * Var_Subst Substitute all variable expressions in a string. 101 * Var_Subst Substitute all variable expressions in a string.
102 * 102 *
103 * Var_Parse Parse a variable expression such as ${VAR:Mpattern}. 103 * Var_Parse Parse a variable expression such as ${VAR:Mpattern}.
104 * 104 *
105 * Var_Delete 105 * Var_Delete
106 * Var_DeleteExpand 106 * Var_DeleteExpand
107 * Delete a variable. 107 * Delete a variable.
108 * 108 *
109 * Var_ReexportVars 109 * Var_ReexportVars
110 * Export some or even all variables to the environment 110 * Export some or even all variables to the environment
111 * of this process and its child processes. 111 * of this process and its child processes.
112 * 112 *
113 * Var_Export Export the variable to the environment of this process 113 * Var_Export Export the variable to the environment of this process
114 * and its child processes. 114 * and its child processes.
115 * 115 *
116 * Var_UnExport Don't export the variable anymore. 116 * Var_UnExport Don't export the variable anymore.
117 * 117 *
118 * Debugging: 118 * Debugging:
119 * Var_Stats Print out hashing statistics if in -dh mode. 119 * Var_Stats Print out hashing statistics if in -dh mode.
120 * 120 *
121 * Var_Dump Print out all variables defined in the given scope. 121 * Var_Dump Print out all variables defined in the given scope.
122 * 122 *
123 * XXX: There's a lot of almost duplicate code in these functions that only 123 * XXX: There's a lot of almost duplicate code in these functions that only
124 * differs in subtle details that are not mentioned in the manual page. 124 * differs in subtle details that are not mentioned in the manual page.
125 */ 125 */
126 126
127#include <sys/stat.h> 127#include <sys/stat.h>
128#ifndef NO_REGEX 128#ifndef NO_REGEX
129#include <sys/types.h> 129#include <sys/types.h>
130#include <regex.h> 130#include <regex.h>
131#endif 131#endif
132#include <errno.h> 132#include <errno.h>
133#include <inttypes.h> 133#include <inttypes.h>
134#include <limits.h> 134#include <limits.h>
135#include <time.h> 135#include <time.h>
136 136
137#include "make.h" 137#include "make.h"
138#include "dir.h" 138#include "dir.h"
139#include "job.h" 139#include "job.h"
140#include "metachar.h" 140#include "metachar.h"
141 141
142/* "@(#)var.c 8.3 (Berkeley) 3/19/94" */ 142/* "@(#)var.c 8.3 (Berkeley) 3/19/94" */
143MAKE_RCSID("$NetBSD: var.c,v 1.855 2021/02/23 16:29:52 rillig Exp $"); 143MAKE_RCSID("$NetBSD: var.c,v 1.856 2021/03/14 10:57:12 rillig Exp $");
144 144
145typedef enum VarFlags { 145typedef enum VarFlags {
146 VFL_NONE = 0, 146 VFL_NONE = 0,
147 147
148 /* 148 /*
149 * The variable's value is currently being used by Var_Parse or 149 * The variable's value is currently being used by Var_Parse or
150 * Var_Subst. This marker is used to avoid endless recursion. 150 * Var_Subst. This marker is used to avoid endless recursion.
151 */ 151 */
152 VFL_IN_USE = 1 << 0, 152 VFL_IN_USE = 1 << 0,
153 153
154 /* 154 /*
155 * The variable comes from the environment. 155 * The variable comes from the environment.
156 * These variables are not registered in any GNode, therefore they 156 * These variables are not registered in any GNode, therefore they
157 * must be freed as soon as they are not used anymore. 157 * must be freed as soon as they are not used anymore.
158 */ 158 */
159 VFL_FROM_ENV = 1 << 1, 159 VFL_FROM_ENV = 1 << 1,
160 160
161 /* 161 /*
162 * The variable is exported to the environment, to be used by child 162 * The variable is exported to the environment, to be used by child
163 * processes. 163 * processes.
164 */ 164 */
165 VFL_EXPORTED = 1 << 2, 165 VFL_EXPORTED = 1 << 2,
166 166
167 /* 167 /*
168 * At the point where this variable was exported, it contained an 168 * At the point where this variable was exported, it contained an
169 * unresolved reference to another variable. Before any child 169 * unresolved reference to another variable. Before any child
170 * process is started, it needs to be exported again, in the hope 170 * process is started, it needs to be exported again, in the hope
171 * that the referenced variable can then be resolved. 171 * that the referenced variable can then be resolved.
172 */ 172 */
173 VFL_REEXPORT = 1 << 3, 173 VFL_REEXPORT = 1 << 3,
174 174
175 /* The variable came from the command line. */ 175 /* The variable came from the command line. */
176 VFL_FROM_CMD = 1 << 4, 176 VFL_FROM_CMD = 1 << 4,
177 177
178 /* 178 /*
179 * The variable value cannot be changed anymore, and the variable 179 * The variable value cannot be changed anymore, and the variable
180 * cannot be deleted. Any attempts to do so are silently ignored, 180 * cannot be deleted. Any attempts to do so are silently ignored,
181 * they are logged with -dv though. 181 * they are logged with -dv though.
182 * 182 *
183 * See VAR_SET_READONLY. 183 * See VAR_SET_READONLY.
184 */ 184 */
185 VFL_READONLY = 1 << 5 185 VFL_READONLY = 1 << 5
186} VarFlags; 186} VarFlags;
187 187
188/* 188/*
189 * Variables are defined using one of the VAR=value assignments. Their 189 * Variables are defined using one of the VAR=value assignments. Their
190 * value can be queried by expressions such as $V, ${VAR}, or with modifiers 190 * value can be queried by expressions such as $V, ${VAR}, or with modifiers
191 * such as ${VAR:S,from,to,g:Q}. 191 * such as ${VAR:S,from,to,g:Q}.
192 * 192 *
193 * There are 3 kinds of variables: scope variables, environment variables, 193 * There are 3 kinds of variables: scope variables, environment variables,
194 * undefined variables. 194 * undefined variables.
195 * 195 *
196 * Scope variables are stored in a GNode.scope. The only way to undefine 196 * Scope variables are stored in a GNode.scope. The only way to undefine
197 * a scope variable is using the .undef directive. In particular, it must 197 * a scope variable is using the .undef directive. In particular, it must
198 * not be possible to undefine a variable during the evaluation of an 198 * not be possible to undefine a variable during the evaluation of an
199 * expression, or Var.name might point nowhere. 199 * expression, or Var.name might point nowhere.
200 * 200 *
201 * Environment variables are temporary. They are returned by VarFind, and 201 * Environment variables are temporary. They are returned by VarFind, and
202 * after using them, they must be freed using VarFreeEnv. 202 * after using them, they must be freed using VarFreeEnv.
203 * 203 *
204 * Undefined variables occur during evaluation of variable expressions such 204 * Undefined variables occur during evaluation of variable expressions such
205 * as ${UNDEF:Ufallback} in Var_Parse and ApplyModifiers. 205 * as ${UNDEF:Ufallback} in Var_Parse and ApplyModifiers.
206 */ 206 */
207typedef struct Var { 207typedef struct Var {
208 /* 208 /*
209 * The name of the variable, once set, doesn't change anymore. 209 * The name of the variable, once set, doesn't change anymore.
210 * For scope variables, it aliases the corresponding HashEntry name. 210 * For scope variables, it aliases the corresponding HashEntry name.
211 * For environment and undefined variables, it is allocated. 211 * For environment and undefined variables, it is allocated.
212 */ 212 */
213 FStr name; 213 FStr name;
214 214
215 /* The unexpanded value of the variable. */ 215 /* The unexpanded value of the variable. */
216 Buffer val; 216 Buffer val;
217 /* Miscellaneous status flags. */ 217 /* Miscellaneous status flags. */
218 VarFlags flags; 218 VarFlags flags;
219} Var; 219} Var;
220 220
221/* 221/*
222 * Exporting variables is expensive and may leak memory, so skip it if we 222 * Exporting variables is expensive and may leak memory, so skip it if we
223 * can. 223 * can.
224 * 224 *
225 * To avoid this, it might be worth encapsulating the environment variables 225 * To avoid this, it might be worth encapsulating the environment variables
226 * in a separate data structure called EnvVars. 226 * in a separate data structure called EnvVars.
227 */ 227 */
228typedef enum VarExportedMode { 228typedef enum VarExportedMode {
229 VAR_EXPORTED_NONE, 229 VAR_EXPORTED_NONE,
230 VAR_EXPORTED_SOME, 230 VAR_EXPORTED_SOME,
231 VAR_EXPORTED_ALL 231 VAR_EXPORTED_ALL
232} VarExportedMode; 232} VarExportedMode;
233 233
234typedef enum UnexportWhat { 234typedef enum UnexportWhat {
235 /* Unexport the variables given by name. */ 235 /* Unexport the variables given by name. */
236 UNEXPORT_NAMED, 236 UNEXPORT_NAMED,
237 /* 237 /*
238 * Unexport all globals previously exported, but keep the environment 238 * Unexport all globals previously exported, but keep the environment
239 * inherited from the parent. 239 * inherited from the parent.
240 */ 240 */
241 UNEXPORT_ALL, 241 UNEXPORT_ALL,
242 /* 242 /*
243 * Unexport all globals previously exported and clear the environment 243 * Unexport all globals previously exported and clear the environment
244 * inherited from the parent. 244 * inherited from the parent.
245 */ 245 */
246 UNEXPORT_ENV 246 UNEXPORT_ENV
247} UnexportWhat; 247} UnexportWhat;
248 248
249/* Flags for pattern matching in the :S and :C modifiers */ 249/* Flags for pattern matching in the :S and :C modifiers */
250typedef struct VarPatternFlags { 250typedef struct VarPatternFlags {
251 251
252 /* Replace as often as possible ('g') */ 252 /* Replace as often as possible ('g') */
253 Boolean subGlobal: 1; 253 Boolean subGlobal: 1;
254 /* Replace only once ('1') */ 254 /* Replace only once ('1') */
255 Boolean subOnce: 1; 255 Boolean subOnce: 1;
256 /* Match at start of word ('^') */ 256 /* Match at start of word ('^') */
257 Boolean anchorStart: 1; 257 Boolean anchorStart: 1;
258 /* Match at end of word ('$') */ 258 /* Match at end of word ('$') */
259 Boolean anchorEnd: 1; 259 Boolean anchorEnd: 1;
260} VarPatternFlags; 260} VarPatternFlags;
261 261
262/* SepBuf builds a string from words interleaved with separators. */ 262/* SepBuf builds a string from words interleaved with separators. */
263typedef struct SepBuf { 263typedef struct SepBuf {
264 Buffer buf; 264 Buffer buf;
265 Boolean needSep; 265 Boolean needSep;
266 /* Usually ' ', but see the ':ts' modifier. */ 266 /* Usually ' ', but see the ':ts' modifier. */
267 char sep; 267 char sep;
268} SepBuf; 268} SepBuf;
269 269
270 270
271ENUM_FLAGS_RTTI_4(VarEvalFlags, 271ENUM_FLAGS_RTTI_4(VarEvalFlags,
272 VARE_UNDEFERR, VARE_WANTRES, VARE_KEEP_DOLLAR, 272 VARE_UNDEFERR, VARE_WANTRES, VARE_KEEP_DOLLAR,
273 VARE_KEEP_UNDEF); 273 VARE_KEEP_UNDEF);
274 274
275/* 275/*
276 * This lets us tell if we have replaced the original environ 276 * This lets us tell if we have replaced the original environ
277 * (which we cannot free). 277 * (which we cannot free).
278 */ 278 */
279char **savedEnv = NULL; 279char **savedEnv = NULL;
280 280
281/* 281/*
282 * Special return value for Var_Parse, indicating a parse error. It may be 282 * Special return value for Var_Parse, indicating a parse error. It may be
283 * caused by an undefined variable, a syntax error in a modifier or 283 * caused by an undefined variable, a syntax error in a modifier or
284 * something entirely different. 284 * something entirely different.
285 */ 285 */
286char var_Error[] = ""; 286char var_Error[] = "";
287 287
288/* 288/*
289 * Special return value for Var_Parse, indicating an undefined variable in 289 * Special return value for Var_Parse, indicating an undefined variable in
290 * a case where VARE_UNDEFERR is not set. This undefined variable is 290 * a case where VARE_UNDEFERR is not set. This undefined variable is
291 * typically a dynamic variable such as ${.TARGET}, whose expansion needs to 291 * typically a dynamic variable such as ${.TARGET}, whose expansion needs to
292 * be deferred until it is defined in an actual target. 292 * be deferred until it is defined in an actual target.
293 * 293 *
294 * See VARE_KEEP_UNDEF. 294 * See VARE_KEEP_UNDEF.
295 */ 295 */
296static char varUndefined[] = ""; 296static char varUndefined[] = "";
297 297
298/* 298/*
299 * Traditionally this make consumed $$ during := like any other expansion. 299 * Traditionally this make consumed $$ during := like any other expansion.
300 * Other make's do not, and this make follows straight since 2016-01-09. 300 * Other make's do not, and this make follows straight since 2016-01-09.
301 * 301 *
302 * This knob allows controlling the behavior. 302 * This knob allows controlling the behavior.
303 * FALSE to consume $$ during := assignment. 303 * FALSE to consume $$ during := assignment.
304 * TRUE to preserve $$ during := assignment. 304 * TRUE to preserve $$ during := assignment.
305 */ 305 */
306#define MAKE_SAVE_DOLLARS ".MAKE.SAVE_DOLLARS" 306#define MAKE_SAVE_DOLLARS ".MAKE.SAVE_DOLLARS"
307static Boolean save_dollars = TRUE; 307static Boolean save_dollars = TRUE;
308 308
309/* 309/*
310 * A scope collects variable names and their values. 310 * A scope collects variable names and their values.
311 * 311 *
312 * The main scope is SCOPE_GLOBAL, which contains the variables that are set 312 * The main scope is SCOPE_GLOBAL, which contains the variables that are set
313 * in the makefiles. SCOPE_INTERNAL acts as a fallback for SCOPE_GLOBAL and 313 * in the makefiles. SCOPE_INTERNAL acts as a fallback for SCOPE_GLOBAL and
314 * contains some internal make variables. These internal variables can thus 314 * contains some internal make variables. These internal variables can thus
315 * be overridden, they can also be restored by undefining the overriding 315 * be overridden, they can also be restored by undefining the overriding
316 * variable. 316 * variable.
317 * 317 *
318 * SCOPE_CMDLINE contains variables from the command line arguments. These 318 * SCOPE_CMDLINE contains variables from the command line arguments. These
319 * override variables from SCOPE_GLOBAL. 319 * override variables from SCOPE_GLOBAL.
320 * 320 *
321 * There is no scope for environment variables, these are generated on-the-fly 321 * There is no scope for environment variables, these are generated on-the-fly
322 * whenever they are referenced. If there were such a scope, each change to 322 * whenever they are referenced. If there were such a scope, each change to
323 * environment variables would have to be reflected in that scope, which may 323 * environment variables would have to be reflected in that scope, which may
324 * be simpler or more complex than the current implementation. 324 * be simpler or more complex than the current implementation.
325 * 325 *
326 * Each target has its own scope, containing the 7 target-local variables 326 * Each target has its own scope, containing the 7 target-local variables
327 * .TARGET, .ALLSRC, etc. No other variables are in these scopes. 327 * .TARGET, .ALLSRC, etc. No other variables are in these scopes.
328 */ 328 */
329 329
330GNode *SCOPE_CMDLINE; 330GNode *SCOPE_CMDLINE;
331GNode *SCOPE_GLOBAL; 331GNode *SCOPE_GLOBAL;
332GNode *SCOPE_INTERNAL; 332GNode *SCOPE_INTERNAL;
333 333
334ENUM_FLAGS_RTTI_6(VarFlags, 334ENUM_FLAGS_RTTI_6(VarFlags,
335 VFL_IN_USE, VFL_FROM_ENV, 335 VFL_IN_USE, VFL_FROM_ENV,
336 VFL_EXPORTED, VFL_REEXPORT, VFL_FROM_CMD, VFL_READONLY); 336 VFL_EXPORTED, VFL_REEXPORT, VFL_FROM_CMD, VFL_READONLY);
337 337
338static VarExportedMode var_exportedVars = VAR_EXPORTED_NONE; 338static VarExportedMode var_exportedVars = VAR_EXPORTED_NONE;
339 339
340 340
341static Var * 341static Var *
342VarNew(FStr name, const char *value, VarFlags flags) 342VarNew(FStr name, const char *value, VarFlags flags)
343{ 343{
344 size_t value_len = strlen(value); 344 size_t value_len = strlen(value);
345 Var *var = bmake_malloc(sizeof *var); 345 Var *var = bmake_malloc(sizeof *var);
346 var->name = name; 346 var->name = name;
347 Buf_InitSize(&var->val, value_len + 1); 347 Buf_InitSize(&var->val, value_len + 1);
348 Buf_AddBytes(&var->val, value, value_len); 348 Buf_AddBytes(&var->val, value, value_len);
349 var->flags = flags; 349 var->flags = flags;
350 return var; 350 return var;
351} 351}
352 352
353static const char * 353static const char *
354CanonicalVarname(const char *name) 354CanonicalVarname(const char *name)
355{ 355{
356 if (*name == '.' && ch_isupper(name[1])) { 356 if (*name == '.' && ch_isupper(name[1])) {
357 switch (name[1]) { 357 switch (name[1]) {
358 case 'A': 358 case 'A':
359 if (strcmp(name, ".ALLSRC") == 0) 359 if (strcmp(name, ".ALLSRC") == 0)
360 name = ALLSRC; 360 name = ALLSRC;
361 if (strcmp(name, ".ARCHIVE") == 0) 361 if (strcmp(name, ".ARCHIVE") == 0)
362 name = ARCHIVE; 362 name = ARCHIVE;
363 break; 363 break;
364 case 'I': 364 case 'I':
365 if (strcmp(name, ".IMPSRC") == 0) 365 if (strcmp(name, ".IMPSRC") == 0)
366 name = IMPSRC; 366 name = IMPSRC;
367 break; 367 break;
368 case 'M': 368 case 'M':
369 if (strcmp(name, ".MEMBER") == 0) 369 if (strcmp(name, ".MEMBER") == 0)
370 name = MEMBER; 370 name = MEMBER;
371 break; 371 break;
372 case 'O': 372 case 'O':
373 if (strcmp(name, ".OODATE") == 0) 373 if (strcmp(name, ".OODATE") == 0)
374 name = OODATE; 374 name = OODATE;
375 break; 375 break;
376 case 'P': 376 case 'P':
377 if (strcmp(name, ".PREFIX") == 0) 377 if (strcmp(name, ".PREFIX") == 0)
378 name = PREFIX; 378 name = PREFIX;
379 break; 379 break;
380 case 'S': 380 case 'S':
381 if (strcmp(name, ".SHELL") == 0) { 381 if (strcmp(name, ".SHELL") == 0) {
382 if (shellPath == NULL) 382 if (shellPath == NULL)
383 Shell_Init(); 383 Shell_Init();
384 } 384 }
385 break; 385 break;
386 case 'T': 386 case 'T':
387 if (strcmp(name, ".TARGET") == 0) 387 if (strcmp(name, ".TARGET") == 0)
388 name = TARGET; 388 name = TARGET;
389 break; 389 break;
390 } 390 }
391 } 391 }
392 392
393 /* GNU make has an additional alias $^ == ${.ALLSRC}. */ 393 /* GNU make has an additional alias $^ == ${.ALLSRC}. */
394 394
395 return name; 395 return name;
396} 396}
397 397
398static Var * 398static Var *
399GNode_FindVar(GNode *scope, const char *varname, unsigned int hash) 399GNode_FindVar(GNode *scope, const char *varname, unsigned int hash)
400{ 400{
401 return HashTable_FindValueHash(&scope->vars, varname, hash); 401 return HashTable_FindValueHash(&scope->vars, varname, hash);
402} 402}
403 403
404/* 404/*
405 * Find the variable in the scope, and maybe in other scopes as well. 405 * Find the variable in the scope, and maybe in other scopes as well.
406 * 406 *
407 * Input: 407 * Input:
408 * name name to find, is not expanded any further 408 * name name to find, is not expanded any further
409 * scope scope in which to look first 409 * scope scope in which to look first
410 * elsewhere TRUE to look in other scopes as well 410 * elsewhere TRUE to look in other scopes as well
411 * 411 *
412 * Results: 412 * Results:
413 * The found variable, or NULL if the variable does not exist. 413 * The found variable, or NULL if the variable does not exist.
414 * If the variable is an environment variable, it must be freed using 414 * If the variable is an environment variable, it must be freed using
415 * VarFreeEnv after use. 415 * VarFreeEnv after use.
416 */ 416 */
417static Var * 417static Var *
418VarFind(const char *name, GNode *scope, Boolean elsewhere) 418VarFind(const char *name, GNode *scope, Boolean elsewhere)
419{ 419{
420 Var *var; 420 Var *var;
421 unsigned int nameHash; 421 unsigned int nameHash;
422 422
423 /* Replace '.TARGET' with '@', likewise for other local variables. */ 423 /* Replace '.TARGET' with '@', likewise for other local variables. */
424 name = CanonicalVarname(name); 424 name = CanonicalVarname(name);
425 nameHash = Hash_Hash(name); 425 nameHash = Hash_Hash(name);
426 426
427 var = GNode_FindVar(scope, name, nameHash); 427 var = GNode_FindVar(scope, name, nameHash);
428 if (!elsewhere) 428 if (!elsewhere)
429 return var; 429 return var;
430 430
431 if (var == NULL && scope != SCOPE_CMDLINE) 431 if (var == NULL && scope != SCOPE_CMDLINE)
432 var = GNode_FindVar(SCOPE_CMDLINE, name, nameHash); 432 var = GNode_FindVar(SCOPE_CMDLINE, name, nameHash);
433 433
434 if (!opts.checkEnvFirst && var == NULL && scope != SCOPE_GLOBAL) { 434 if (!opts.checkEnvFirst && var == NULL && scope != SCOPE_GLOBAL) {
435 var = GNode_FindVar(SCOPE_GLOBAL, name, nameHash); 435 var = GNode_FindVar(SCOPE_GLOBAL, name, nameHash);
436 if (var == NULL && scope != SCOPE_INTERNAL) { 436 if (var == NULL && scope != SCOPE_INTERNAL) {
437 /* SCOPE_INTERNAL is subordinate to SCOPE_GLOBAL */ 437 /* SCOPE_INTERNAL is subordinate to SCOPE_GLOBAL */
438 var = GNode_FindVar(SCOPE_INTERNAL, name, nameHash); 438 var = GNode_FindVar(SCOPE_INTERNAL, name, nameHash);
439 } 439 }
440 } 440 }
441 441
442 if (var == NULL) { 442 if (var == NULL) {
443 char *env; 443 char *env;
444 444
445 if ((env = getenv(name)) != NULL) { 445 if ((env = getenv(name)) != NULL) {
446 char *varname = bmake_strdup(name); 446 char *varname = bmake_strdup(name);
447 return VarNew(FStr_InitOwn(varname), env, VFL_FROM_ENV); 447 return VarNew(FStr_InitOwn(varname), env, VFL_FROM_ENV);
448 } 448 }
449 449
450 if (opts.checkEnvFirst && scope != SCOPE_GLOBAL) { 450 if (opts.checkEnvFirst && scope != SCOPE_GLOBAL) {
451 var = GNode_FindVar(SCOPE_GLOBAL, name, nameHash); 451 var = GNode_FindVar(SCOPE_GLOBAL, name, nameHash);
452 if (var == NULL && scope != SCOPE_INTERNAL) 452 if (var == NULL && scope != SCOPE_INTERNAL)
453 var = GNode_FindVar(SCOPE_INTERNAL, name, 453 var = GNode_FindVar(SCOPE_INTERNAL, name,
454 nameHash); 454 nameHash);
455 return var; 455 return var;
456 } 456 }
457 457
458 return NULL; 458 return NULL;
459 } 459 }
460 460
461 return var; 461 return var;
462} 462}
463 463
464/* 464/*
465 * If the variable is an environment variable, free it, including its value. 465 * If the variable is an environment variable, free it, including its value.
466 * 466 *
467 * Results: 467 * Results:
468 * TRUE if it was an environment variable, 468 * TRUE if it was an environment variable,
469 * FALSE if it is still a regular variable. 469 * FALSE if it is still a regular variable.
470 */ 470 */
471static void 471static void
472VarFreeEnv(Var *v) 472VarFreeEnv(Var *v)
473{ 473{
474 if (!(v->flags & VFL_FROM_ENV)) 474 if (!(v->flags & VFL_FROM_ENV))
475 return; 475 return;
476 476
477 FStr_Done(&v->name); 477 FStr_Done(&v->name);
478 Buf_Done(&v->val); 478 Buf_Done(&v->val);
479 free(v); 479 free(v);
480} 480}
481 481
482/* Add a new variable of the given name and value to the given scope. */ 482/* Add a new variable of the given name and value to the given scope. */
483static Var * 483static Var *
484VarAdd(const char *name, const char *value, GNode *scope, VarSetFlags flags) 484VarAdd(const char *name, const char *value, GNode *scope, VarSetFlags flags)
485{ 485{
486 HashEntry *he = HashTable_CreateEntry(&scope->vars, name, NULL); 486 HashEntry *he = HashTable_CreateEntry(&scope->vars, name, NULL);
487 Var *v = VarNew(FStr_InitRefer(/* aliased to */ he->key), value, 487 Var *v = VarNew(FStr_InitRefer(/* aliased to */ he->key), value,
488 flags & VAR_SET_READONLY ? VFL_READONLY : VFL_NONE); 488 flags & VAR_SET_READONLY ? VFL_READONLY : VFL_NONE);
489 HashEntry_Set(he, v); 489 HashEntry_Set(he, v);
490 DEBUG3(VAR, "%s:%s = %s\n", scope->name, name, value); 490 DEBUG3(VAR, "%s:%s = %s\n", scope->name, name, value);
491 return v; 491 return v;
492} 492}
493 493
494/* 494/*
495 * Remove a variable from a scope, freeing all related memory as well. 495 * Remove a variable from a scope, freeing all related memory as well.
496 * The variable name is kept as-is, it is not expanded. 496 * The variable name is kept as-is, it is not expanded.
497 */ 497 */
498void 498void
499Var_Delete(GNode *scope, const char *varname) 499Var_Delete(GNode *scope, const char *varname)
500{ 500{
501 HashEntry *he = HashTable_FindEntry(&scope->vars, varname); 501 HashEntry *he = HashTable_FindEntry(&scope->vars, varname);
502 Var *v; 502 Var *v;
503 503
504 if (he == NULL) { 504 if (he == NULL) {
505 DEBUG2(VAR, "%s:delete %s (not found)\n", scope->name, varname); 505 DEBUG2(VAR, "%s:delete %s (not found)\n", scope->name, varname);
506 return; 506 return;
507 } 507 }
508 508
509 DEBUG2(VAR, "%s:delete %s\n", scope->name, varname); 509 DEBUG2(VAR, "%s:delete %s\n", scope->name, varname);
510 v = he->value; 510 v = he->value;
511 if (v->flags & VFL_EXPORTED) 511 if (v->flags & VFL_EXPORTED)
512 unsetenv(v->name.str); 512 unsetenv(v->name.str);
513 if (strcmp(v->name.str, MAKE_EXPORTED) == 0) 513 if (strcmp(v->name.str, MAKE_EXPORTED) == 0)
514 var_exportedVars = VAR_EXPORTED_NONE; 514 var_exportedVars = VAR_EXPORTED_NONE;
515 assert(v->name.freeIt == NULL); 515 assert(v->name.freeIt == NULL);
516 HashTable_DeleteEntry(&scope->vars, he); 516 HashTable_DeleteEntry(&scope->vars, he);
517 Buf_Done(&v->val); 517 Buf_Done(&v->val);
518 free(v); 518 free(v);
519} 519}
520 520
521/* 521/*
522 * Remove a variable from a scope, freeing all related memory as well. 522 * Remove a variable from a scope, freeing all related memory as well.
523 * The variable name is expanded once. 523 * The variable name is expanded once.
524 */ 524 */
525void 525void
526Var_DeleteExpand(GNode *scope, const char *name) 526Var_DeleteExpand(GNode *scope, const char *name)
527{ 527{
528 FStr varname = FStr_InitRefer(name); 528 FStr varname = FStr_InitRefer(name);
529 529
530 if (strchr(varname.str, '$') != NULL) { 530 if (strchr(varname.str, '$') != NULL) {
531 char *expanded; 531 char *expanded;
532 (void)Var_Subst(varname.str, SCOPE_GLOBAL, VARE_WANTRES, 532 (void)Var_Subst(varname.str, SCOPE_GLOBAL, VARE_WANTRES,
533 &expanded); 533 &expanded);
534 /* TODO: handle errors */ 534 /* TODO: handle errors */
535 varname = FStr_InitOwn(expanded); 535 varname = FStr_InitOwn(expanded);
536 } 536 }
537 537
538 Var_Delete(scope, varname.str); 538 Var_Delete(scope, varname.str);
539 FStr_Done(&varname); 539 FStr_Done(&varname);
540} 540}
541 541
542/* 542/*
543 * Undefine one or more variables from the global scope. 543 * Undefine one or more variables from the global scope.
544 * The argument is expanded exactly once and then split into words. 544 * The argument is expanded exactly once and then split into words.
545 */ 545 */
546void 546void
547Var_Undef(const char *arg) 547Var_Undef(const char *arg)
548{ 548{
549 VarParseResult vpr; 549 VarParseResult vpr;
550 char *expanded; 550 char *expanded;
551 Words varnames; 551 Words varnames;
552 size_t i; 552 size_t i;
553 553
554 if (arg[0] == '\0') { 554 if (arg[0] == '\0') {
555 Parse_Error(PARSE_FATAL, 555 Parse_Error(PARSE_FATAL,
556 "The .undef directive requires an argument"); 556 "The .undef directive requires an argument");
557 return; 557 return;
558 } 558 }
559 559
560 vpr = Var_Subst(arg, SCOPE_GLOBAL, VARE_WANTRES, &expanded); 560 vpr = Var_Subst(arg, SCOPE_GLOBAL, VARE_WANTRES, &expanded);
561 if (vpr != VPR_OK) { 561 if (vpr != VPR_OK) {
562 Parse_Error(PARSE_FATAL, 562 Parse_Error(PARSE_FATAL,
563 "Error in variable names to be undefined"); 563 "Error in variable names to be undefined");
564 return; 564 return;
565 } 565 }
566 566
567 varnames = Str_Words(expanded, FALSE); 567 varnames = Str_Words(expanded, FALSE);
568 if (varnames.len == 1 && varnames.words[0][0] == '\0') 568 if (varnames.len == 1 && varnames.words[0][0] == '\0')
569 varnames.len = 0; 569 varnames.len = 0;
570 570
571 for (i = 0; i < varnames.len; i++) { 571 for (i = 0; i < varnames.len; i++) {
572 const char *varname = varnames.words[i]; 572 const char *varname = varnames.words[i];
573 Global_Delete(varname); 573 Global_Delete(varname);
574 } 574 }
575 575
576 Words_Free(varnames); 576 Words_Free(varnames);
577 free(expanded); 577 free(expanded);
578} 578}
579 579
580static Boolean 580static Boolean
581MayExport(const char *name) 581MayExport(const char *name)
582{ 582{
583 if (name[0] == '.') 583 if (name[0] == '.')
584 return FALSE; /* skip internals */ 584 return FALSE; /* skip internals */
585 if (name[0] == '-') 585 if (name[0] == '-')
586 return FALSE; /* skip misnamed variables */ 586 return FALSE; /* skip misnamed variables */
587 if (name[1] == '\0') { 587 if (name[1] == '\0') {
588 /* 588 /*
589 * A single char. 589 * A single char.
590 * If it is one of the variables that should only appear in 590 * If it is one of the variables that should only appear in
591 * local scope, skip it, else we can get Var_Subst 591 * local scope, skip it, else we can get Var_Subst
592 * into a loop. 592 * into a loop.
593 */ 593 */
594 switch (name[0]) { 594 switch (name[0]) {
595 case '@': 595 case '@':
596 case '%': 596 case '%':
597 case '*': 597 case '*':
598 case '!': 598 case '!':
599 return FALSE; 599 return FALSE;
600 } 600 }
601 } 601 }
602 return TRUE; 602 return TRUE;
603} 603}
604 604
605static Boolean 605static Boolean
606ExportVarEnv(Var *v) 606ExportVarEnv(Var *v)
607{ 607{
608 const char *name = v->name.str; 608 const char *name = v->name.str;
609 char *val = v->val.data; 609 char *val = v->val.data;
610 char *expr; 610 char *expr;
611 611
612 if ((v->flags & VFL_EXPORTED) && !(v->flags & VFL_REEXPORT)) 612 if ((v->flags & VFL_EXPORTED) && !(v->flags & VFL_REEXPORT))
613 return FALSE; /* nothing to do */ 613 return FALSE; /* nothing to do */
614 614
615 if (strchr(val, '$') == NULL) { 615 if (strchr(val, '$') == NULL) {
616 if (!(v->flags & VFL_EXPORTED)) 616 if (!(v->flags & VFL_EXPORTED))
617 setenv(name, val, 1); 617 setenv(name, val, 1);
618 return TRUE; 618 return TRUE;
619 } 619 }
620 620
621 if (v->flags & VFL_IN_USE) { 621 if (v->flags & VFL_IN_USE) {
622 /* 622 /*
623 * We recursed while exporting in a child. 623 * We recursed while exporting in a child.
624 * This isn't going to end well, just skip it. 624 * This isn't going to end well, just skip it.
625 */ 625 */
626 return FALSE; 626 return FALSE;
627 } 627 }
628 628
629 /* XXX: name is injected without escaping it */ 629 /* XXX: name is injected without escaping it */
630 expr = str_concat3("${", name, "}"); 630 expr = str_concat3("${", name, "}");
631 (void)Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES, &val); 631 (void)Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES, &val);
632 /* TODO: handle errors */ 632 /* TODO: handle errors */
633 setenv(name, val, 1); 633 setenv(name, val, 1);
634 free(val); 634 free(val);
635 free(expr); 635 free(expr);
636 return TRUE; 636 return TRUE;
637} 637}
638 638
639static Boolean 639static Boolean
640ExportVarPlain(Var *v) 640ExportVarPlain(Var *v)
641{ 641{
642 if (strchr(v->val.data, '$') == NULL) { 642 if (strchr(v->val.data, '$') == NULL) {
643 setenv(v->name.str, v->val.data, 1); 643 setenv(v->name.str, v->val.data, 1);
644 v->flags |= VFL_EXPORTED; 644 v->flags |= VFL_EXPORTED;
645 v->flags &= ~(unsigned)VFL_REEXPORT; 645 v->flags &= ~(unsigned)VFL_REEXPORT;
646 return TRUE; 646 return TRUE;
647 } 647 }
648 648
649 /* 649 /*
650 * Flag the variable as something we need to re-export. 650 * Flag the variable as something we need to re-export.
651 * No point actually exporting it now though, 651 * No point actually exporting it now though,
652 * the child process can do it at the last minute. 652 * the child process can do it at the last minute.
653 * Avoid calling setenv more often than necessary since it can leak. 653 * Avoid calling setenv more often than necessary since it can leak.
654 */ 654 */
655 v->flags |= VFL_EXPORTED | VFL_REEXPORT; 655 v->flags |= VFL_EXPORTED | VFL_REEXPORT;
656 return TRUE; 656 return TRUE;
657} 657}
658 658
659static Boolean 659static Boolean
660ExportVarLiteral(Var *v) 660ExportVarLiteral(Var *v)
661{ 661{
662 if ((v->flags & VFL_EXPORTED) && !(v->flags & VFL_REEXPORT)) 662 if ((v->flags & VFL_EXPORTED) && !(v->flags & VFL_REEXPORT))
663 return FALSE; 663 return FALSE;
664 664
665 if (!(v->flags & VFL_EXPORTED)) 665 if (!(v->flags & VFL_EXPORTED))
666 setenv(v->name.str, v->val.data, 1); 666 setenv(v->name.str, v->val.data, 1);
667 667
668 return TRUE; 668 return TRUE;
669} 669}
670 670
671/* 671/*
672 * Mark a single variable to be exported later for subprocesses. 672 * Mark a single variable to be exported later for subprocesses.
673 * 673 *
674 * Internal variables (those starting with '.') are not exported. 674 * Internal variables (those starting with '.') are not exported.
675 */ 675 */
676static Boolean 676static Boolean
677ExportVar(const char *name, VarExportMode mode) 677ExportVar(const char *name, VarExportMode mode)
678{ 678{
679 Var *v; 679 Var *v;
680 680
681 if (!MayExport(name)) 681 if (!MayExport(name))
682 return FALSE; 682 return FALSE;
683 683
684 v = VarFind(name, SCOPE_GLOBAL, FALSE); 684 v = VarFind(name, SCOPE_GLOBAL, FALSE);
685 if (v == NULL) 685 if (v == NULL)
686 return FALSE; 686 return FALSE;
687 687
688 if (mode == VEM_ENV) 688 if (mode == VEM_ENV)
689 return ExportVarEnv(v); 689 return ExportVarEnv(v);
690 else if (mode == VEM_PLAIN) 690 else if (mode == VEM_PLAIN)
691 return ExportVarPlain(v); 691 return ExportVarPlain(v);
692 else 692 else
693 return ExportVarLiteral(v); 693 return ExportVarLiteral(v);
694} 694}
695 695
696/* 696/*
697 * Actually export the variables that have been marked as needing to be 697 * Actually export the variables that have been marked as needing to be
698 * re-exported. 698 * re-exported.
699 */ 699 */
700void 700void
701Var_ReexportVars(void) 701Var_ReexportVars(void)
702{ 702{
703 char *xvarnames; 703 char *xvarnames;
704 704
705 /* 705 /*
706 * Several make implementations support this sort of mechanism for 706 * Several make implementations support this sort of mechanism for
707 * tracking recursion - but each uses a different name. 707 * tracking recursion - but each uses a different name.
708 * We allow the makefiles to update MAKELEVEL and ensure 708 * We allow the makefiles to update MAKELEVEL and ensure
709 * children see a correctly incremented value. 709 * children see a correctly incremented value.
710 */ 710 */
711 char tmp[21]; 711 char tmp[21];
712 snprintf(tmp, sizeof tmp, "%d", makelevel + 1); 712 snprintf(tmp, sizeof tmp, "%d", makelevel + 1);
713 setenv(MAKE_LEVEL_ENV, tmp, 1); 713 setenv(MAKE_LEVEL_ENV, tmp, 1);
714 714
715 if (var_exportedVars == VAR_EXPORTED_NONE) 715 if (var_exportedVars == VAR_EXPORTED_NONE)
716 return; 716 return;
717 717
718 if (var_exportedVars == VAR_EXPORTED_ALL) { 718 if (var_exportedVars == VAR_EXPORTED_ALL) {
719 HashIter hi; 719 HashIter hi;
720 720
721 /* Ouch! Exporting all variables at once is crazy. */ 721 /* Ouch! Exporting all variables at once is crazy. */
722 HashIter_Init(&hi, &SCOPE_GLOBAL->vars); 722 HashIter_Init(&hi, &SCOPE_GLOBAL->vars);
723 while (HashIter_Next(&hi) != NULL) { 723 while (HashIter_Next(&hi) != NULL) {
724 Var *var = hi.entry->value; 724 Var *var = hi.entry->value;
725 ExportVar(var->name.str, VEM_ENV); 725 ExportVar(var->name.str, VEM_ENV);
726 } 726 }
727 return; 727 return;
728 } 728 }
729 729
730 (void)Var_Subst("${" MAKE_EXPORTED ":O:u}", SCOPE_GLOBAL, VARE_WANTRES, 730 (void)Var_Subst("${" MAKE_EXPORTED ":O:u}", SCOPE_GLOBAL, VARE_WANTRES,
731 &xvarnames); 731 &xvarnames);
732 /* TODO: handle errors */ 732 /* TODO: handle errors */
733 if (xvarnames[0] != '\0') { 733 if (xvarnames[0] != '\0') {
734 Words varnames = Str_Words(xvarnames, FALSE); 734 Words varnames = Str_Words(xvarnames, FALSE);
735 size_t i; 735 size_t i;
736 736
737 for (i = 0; i < varnames.len; i++) 737 for (i = 0; i < varnames.len; i++)
738 ExportVar(varnames.words[i], VEM_ENV); 738 ExportVar(varnames.words[i], VEM_ENV);
739 Words_Free(varnames); 739 Words_Free(varnames);
740 } 740 }
741 free(xvarnames); 741 free(xvarnames);
742} 742}
743 743
744static void 744static void
745ExportVars(const char *varnames, Boolean isExport, VarExportMode mode) 745ExportVars(const char *varnames, Boolean isExport, VarExportMode mode)
746/* TODO: try to combine the parameters 'isExport' and 'mode'. */ 746/* TODO: try to combine the parameters 'isExport' and 'mode'. */
747{ 747{
748 Words words = Str_Words(varnames, FALSE); 748 Words words = Str_Words(varnames, FALSE);
749 size_t i; 749 size_t i;
750 750
751 if (words.len == 1 && words.words[0][0] == '\0') 751 if (words.len == 1 && words.words[0][0] == '\0')
752 words.len = 0; 752 words.len = 0;
753 753
754 for (i = 0; i < words.len; i++) { 754 for (i = 0; i < words.len; i++) {
755 const char *varname = words.words[i]; 755 const char *varname = words.words[i];
756 if (!ExportVar(varname, mode)) 756 if (!ExportVar(varname, mode))
757 continue; 757 continue;
758 758
759 if (var_exportedVars == VAR_EXPORTED_NONE) 759 if (var_exportedVars == VAR_EXPORTED_NONE)
760 var_exportedVars = VAR_EXPORTED_SOME; 760 var_exportedVars = VAR_EXPORTED_SOME;
761 761
762 if (isExport && mode == VEM_PLAIN) 762 if (isExport && mode == VEM_PLAIN)
763 Global_Append(MAKE_EXPORTED, varname); 763 Global_Append(MAKE_EXPORTED, varname);
764 } 764 }
765 Words_Free(words); 765 Words_Free(words);
766} 766}
767 767
768static void 768static void
769ExportVarsExpand(const char *uvarnames, Boolean isExport, VarExportMode mode) 769ExportVarsExpand(const char *uvarnames, Boolean isExport, VarExportMode mode)
770{ 770{
771 char *xvarnames; 771 char *xvarnames;
772 772
773 (void)Var_Subst(uvarnames, SCOPE_GLOBAL, VARE_WANTRES, &xvarnames); 773 (void)Var_Subst(uvarnames, SCOPE_GLOBAL, VARE_WANTRES, &xvarnames);
774 /* TODO: handle errors */ 774 /* TODO: handle errors */
775 ExportVars(xvarnames, isExport, mode); 775 ExportVars(xvarnames, isExport, mode);
776 free(xvarnames); 776 free(xvarnames);
777} 777}
778 778
779/* Export the named variables, or all variables. */ 779/* Export the named variables, or all variables. */
780void 780void
781Var_Export(VarExportMode mode, const char *varnames) 781Var_Export(VarExportMode mode, const char *varnames)
782{ 782{
783 if (mode == VEM_PLAIN && varnames[0] == '\0') { 783 if (mode == VEM_PLAIN && varnames[0] == '\0') {
784 var_exportedVars = VAR_EXPORTED_ALL; /* use with caution! */ 784 var_exportedVars = VAR_EXPORTED_ALL; /* use with caution! */
785 return; 785 return;
786 } 786 }
787 787
788 ExportVarsExpand(varnames, TRUE, mode); 788 ExportVarsExpand(varnames, TRUE, mode);
789} 789}
790 790
791void 791void
792Var_ExportVars(const char *varnames) 792Var_ExportVars(const char *varnames)
793{ 793{
794 ExportVarsExpand(varnames, FALSE, VEM_PLAIN); 794 ExportVarsExpand(varnames, FALSE, VEM_PLAIN);
795} 795}
796 796
797 797
798extern char **environ; 798extern char **environ;
799 799
800static void 800static void
801ClearEnv(void) 801ClearEnv(void)
802{ 802{
803 const char *cp; 803 const char *cp;
804 char **newenv; 804 char **newenv;
805 805
806 cp = getenv(MAKE_LEVEL_ENV); /* we should preserve this */ 806 cp = getenv(MAKE_LEVEL_ENV); /* we should preserve this */
807 if (environ == savedEnv) { 807 if (environ == savedEnv) {
808 /* we have been here before! */ 808 /* we have been here before! */
809 newenv = bmake_realloc(environ, 2 * sizeof(char *)); 809 newenv = bmake_realloc(environ, 2 * sizeof(char *));
810 } else { 810 } else {
811 if (savedEnv != NULL) { 811 if (savedEnv != NULL) {
812 free(savedEnv); 812 free(savedEnv);
813 savedEnv = NULL; 813 savedEnv = NULL;
814 } 814 }
815 newenv = bmake_malloc(2 * sizeof(char *)); 815 newenv = bmake_malloc(2 * sizeof(char *));
816 } 816 }
817 817
818 /* Note: we cannot safely free() the original environ. */ 818 /* Note: we cannot safely free() the original environ. */
819 environ = savedEnv = newenv; 819 environ = savedEnv = newenv;
820 newenv[0] = NULL; 820 newenv[0] = NULL;
821 newenv[1] = NULL; 821 newenv[1] = NULL;
822 if (cp != NULL && *cp != '\0') 822 if (cp != NULL && *cp != '\0')
823 setenv(MAKE_LEVEL_ENV, cp, 1); 823 setenv(MAKE_LEVEL_ENV, cp, 1);
824} 824}
825 825
826static void 826static void
827GetVarnamesToUnexport(Boolean isEnv, const char *arg, 827GetVarnamesToUnexport(Boolean isEnv, const char *arg,
828 FStr *out_varnames, UnexportWhat *out_what) 828 FStr *out_varnames, UnexportWhat *out_what)
829{ 829{
830 UnexportWhat what; 830 UnexportWhat what;
831 FStr varnames = FStr_InitRefer(""); 831 FStr varnames = FStr_InitRefer("");
832 832
833 if (isEnv) { 833 if (isEnv) {
834 if (arg[0] != '\0') { 834 if (arg[0] != '\0') {
835 Parse_Error(PARSE_FATAL, 835 Parse_Error(PARSE_FATAL,
836 "The directive .unexport-env does not take " 836 "The directive .unexport-env does not take "
837 "arguments"); 837 "arguments");
838 /* continue anyway */ 838 /* continue anyway */
839 } 839 }
840 what = UNEXPORT_ENV; 840 what = UNEXPORT_ENV;
841 841
842 } else { 842 } else {
843 what = arg[0] != '\0' ? UNEXPORT_NAMED : UNEXPORT_ALL; 843 what = arg[0] != '\0' ? UNEXPORT_NAMED : UNEXPORT_ALL;
844 if (what == UNEXPORT_NAMED) 844 if (what == UNEXPORT_NAMED)
845 varnames = FStr_InitRefer(arg); 845 varnames = FStr_InitRefer(arg);
846 } 846 }
847 847
848 if (what != UNEXPORT_NAMED) { 848 if (what != UNEXPORT_NAMED) {
849 char *expanded; 849 char *expanded;
850 /* Using .MAKE.EXPORTED */ 850 /* Using .MAKE.EXPORTED */
851 (void)Var_Subst("${" MAKE_EXPORTED ":O:u}", SCOPE_GLOBAL, 851 (void)Var_Subst("${" MAKE_EXPORTED ":O:u}", SCOPE_GLOBAL,
852 VARE_WANTRES, &expanded); 852 VARE_WANTRES, &expanded);
853 /* TODO: handle errors */ 853 /* TODO: handle errors */
854 varnames = FStr_InitOwn(expanded); 854 varnames = FStr_InitOwn(expanded);
855 } 855 }
856 856
857 *out_varnames = varnames; 857 *out_varnames = varnames;
858 *out_what = what; 858 *out_what = what;
859} 859}
860 860
861static void 861static void
862UnexportVar(const char *varname, UnexportWhat what) 862UnexportVar(const char *varname, UnexportWhat what)
863{ 863{
864 Var *v = VarFind(varname, SCOPE_GLOBAL, FALSE); 864 Var *v = VarFind(varname, SCOPE_GLOBAL, FALSE);
865 if (v == NULL) { 865 if (v == NULL) {
866 DEBUG1(VAR, "Not unexporting \"%s\" (not found)\n", varname); 866 DEBUG1(VAR, "Not unexporting \"%s\" (not found)\n", varname);
867 return; 867 return;
868 } 868 }
869 869
870 DEBUG1(VAR, "Unexporting \"%s\"\n", varname); 870 DEBUG1(VAR, "Unexporting \"%s\"\n", varname);
871 if (what != UNEXPORT_ENV && 871 if (what != UNEXPORT_ENV &&
872 (v->flags & VFL_EXPORTED) && !(v->flags & VFL_REEXPORT)) 872 (v->flags & VFL_EXPORTED) && !(v->flags & VFL_REEXPORT))
873 unsetenv(v->name.str); 873 unsetenv(v->name.str);
874 v->flags &= ~(unsigned)(VFL_EXPORTED | VFL_REEXPORT); 874 v->flags &= ~(unsigned)(VFL_EXPORTED | VFL_REEXPORT);
875 875
876 if (what == UNEXPORT_NAMED) { 876 if (what == UNEXPORT_NAMED) {
877 /* Remove the variable names from .MAKE.EXPORTED. */ 877 /* Remove the variable names from .MAKE.EXPORTED. */
878 /* XXX: v->name is injected without escaping it */ 878 /* XXX: v->name is injected without escaping it */
879 char *expr = str_concat3("${" MAKE_EXPORTED ":N", 879 char *expr = str_concat3("${" MAKE_EXPORTED ":N",
880 v->name.str, "}"); 880 v->name.str, "}");
881 char *cp; 881 char *cp;
882 (void)Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES, &cp); 882 (void)Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES, &cp);
883 /* TODO: handle errors */ 883 /* TODO: handle errors */
884 Global_Set(MAKE_EXPORTED, cp); 884 Global_Set(MAKE_EXPORTED, cp);
885 free(cp); 885 free(cp);
886 free(expr); 886 free(expr);
887 } 887 }
888} 888}
889 889
890static void 890static void
891UnexportVars(FStr *varnames, UnexportWhat what) 891UnexportVars(FStr *varnames, UnexportWhat what)
892{ 892{
893 size_t i; 893 size_t i;
894 Words words; 894 Words words;
895 895
896 if (what == UNEXPORT_ENV) 896 if (what == UNEXPORT_ENV)
897 ClearEnv(); 897 ClearEnv();
898 898
899 words = Str_Words(varnames->str, FALSE); 899 words = Str_Words(varnames->str, FALSE);
900 for (i = 0; i < words.len; i++) { 900 for (i = 0; i < words.len; i++) {
901 const char *varname = words.words[i]; 901 const char *varname = words.words[i];
902 UnexportVar(varname, what); 902 UnexportVar(varname, what);
903 } 903 }
904 Words_Free(words); 904 Words_Free(words);
905 905
906 if (what != UNEXPORT_NAMED) 906 if (what != UNEXPORT_NAMED)
907 Global_Delete(MAKE_EXPORTED); 907 Global_Delete(MAKE_EXPORTED);
908} 908}
909 909
910/* 910/*
911 * This is called when .unexport[-env] is seen. 911 * This is called when .unexport[-env] is seen.
912 * 912 *
913 * str must have the form "unexport[-env] varname...". 913 * str must have the form "unexport[-env] varname...".
914 */ 914 */
915void 915void
916Var_UnExport(Boolean isEnv, const char *arg) 916Var_UnExport(Boolean isEnv, const char *arg)
917{ 917{
918 UnexportWhat what; 918 UnexportWhat what;
919 FStr varnames; 919 FStr varnames;
920 920
921 GetVarnamesToUnexport(isEnv, arg, &varnames, &what); 921 GetVarnamesToUnexport(isEnv, arg, &varnames, &what);
922 UnexportVars(&varnames, what); 922 UnexportVars(&varnames, what);
923 FStr_Done(&varnames); 923 FStr_Done(&varnames);
924} 924}
925 925
926/* 926/*
927 * When there is a variable of the same name in the command line scope, the 927 * When there is a variable of the same name in the command line scope, the
928 * global variable would not be visible anywhere. Therefore there is no 928 * global variable would not be visible anywhere. Therefore there is no
929 * point in setting it at all. 929 * point in setting it at all.
930 * 930 *
931 * See 'scope == SCOPE_CMDLINE' in Var_SetWithFlags. 931 * See 'scope == SCOPE_CMDLINE' in Var_SetWithFlags.
932 */ 932 */
933static Boolean 933static Boolean
934ExistsInCmdline(const char *name, const char *val) 934ExistsInCmdline(const char *name, const char *val)
935{ 935{
936 Var *v; 936 Var *v;
937 937
938 v = VarFind(name, SCOPE_CMDLINE, FALSE); 938 v = VarFind(name, SCOPE_CMDLINE, FALSE);
939 if (v == NULL) 939 if (v == NULL)
940 return FALSE; 940 return FALSE;
941 941
942 if (v->flags & VFL_FROM_CMD) { 942 if (v->flags & VFL_FROM_CMD) {
943 DEBUG3(VAR, "%s:%s = %s ignored!\n", 943 DEBUG3(VAR, "%s:%s = %s ignored!\n",
944 SCOPE_GLOBAL->name, name, val); 944 SCOPE_GLOBAL->name, name, val);
945 return TRUE; 945 return TRUE;
946 } 946 }
947 947
948 VarFreeEnv(v); 948 VarFreeEnv(v);
949 return FALSE; 949 return FALSE;
950} 950}
951 951
952/* Set the variable to the value; the name is not expanded. */ 952/* Set the variable to the value; the name is not expanded. */
953void 953void
954Var_SetWithFlags(GNode *scope, const char *name, const char *val, 954Var_SetWithFlags(GNode *scope, const char *name, const char *val,
955 VarSetFlags flags) 955 VarSetFlags flags)
956{ 956{
957 Var *v; 957 Var *v;
958 958
959 assert(val != NULL); 959 assert(val != NULL);
960 if (name[0] == '\0') { 960 if (name[0] == '\0') {
961 DEBUG0(VAR, "SetVar: variable name is empty - ignored\n"); 961 DEBUG0(VAR, "SetVar: variable name is empty - ignored\n");
962 return; 962 return;
963 } 963 }
964 964
965 if (scope == SCOPE_GLOBAL && ExistsInCmdline(name, val)) 965 if (scope == SCOPE_GLOBAL && ExistsInCmdline(name, val))
966 return; 966 return;
967 967
968 /* 968 /*
969 * Only look for a variable in the given scope since anything set 969 * Only look for a variable in the given scope since anything set
970 * here will override anything in a lower scope, so there's not much 970 * here will override anything in a lower scope, so there's not much
971 * point in searching them all. 971 * point in searching them all.
972 */ 972 */
973 v = VarFind(name, scope, FALSE); 973 v = VarFind(name, scope, FALSE);
974 if (v == NULL) { 974 if (v == NULL) {
975 if (scope == SCOPE_CMDLINE && !(flags & VAR_SET_NO_EXPORT)) { 975 if (scope == SCOPE_CMDLINE && !(flags & VAR_SET_NO_EXPORT)) {
976 /* 976 /*
977 * This var would normally prevent the same name being 977 * This var would normally prevent the same name being
978 * added to SCOPE_GLOBAL, so delete it from there if 978 * added to SCOPE_GLOBAL, so delete it from there if
979 * needed. Otherwise -V name may show the wrong value. 979 * needed. Otherwise -V name may show the wrong value.
980 * 980 *
981 * See ExistsInCmdline. 981 * See ExistsInCmdline.
982 */ 982 */
983 Var_Delete(SCOPE_GLOBAL, name); 983 Var_Delete(SCOPE_GLOBAL, name);
984 } 984 }
985 v = VarAdd(name, val, scope, flags); 985 v = VarAdd(name, val, scope, flags);
986 } else { 986 } else {
987 if ((v->flags & VFL_READONLY) && !(flags & VAR_SET_READONLY)) { 987 if ((v->flags & VFL_READONLY) && !(flags & VAR_SET_READONLY)) {
988 DEBUG3(VAR, "%s:%s = %s ignored (read-only)\n", 988 DEBUG3(VAR, "%s:%s = %s ignored (read-only)\n",
989 scope->name, name, val); 989 scope->name, name, val);
990 return; 990 return;
991 } 991 }
992 Buf_Empty(&v->val); 992 Buf_Empty(&v->val);
993 Buf_AddStr(&v->val, val); 993 Buf_AddStr(&v->val, val);
994 994
995 DEBUG3(VAR, "%s:%s = %s\n", scope->name, name, val); 995 DEBUG3(VAR, "%s:%s = %s\n", scope->name, name, val);
996 if (v->flags & VFL_EXPORTED) 996 if (v->flags & VFL_EXPORTED)
997 ExportVar(name, VEM_PLAIN); 997 ExportVar(name, VEM_PLAIN);
998 } 998 }
999 999
1000 /* 1000 /*
1001 * Any variables given on the command line are automatically exported 1001 * Any variables given on the command line are automatically exported
1002 * to the environment (as per POSIX standard), except for internals. 1002 * to the environment (as per POSIX standard), except for internals.
1003 */ 1003 */
1004 if (scope == SCOPE_CMDLINE && !(flags & VAR_SET_NO_EXPORT) && 1004 if (scope == SCOPE_CMDLINE && !(flags & VAR_SET_NO_EXPORT) &&
1005 name[0] != '.') { 1005 name[0] != '.') {
1006 v->flags |= VFL_FROM_CMD; 1006 v->flags |= VFL_FROM_CMD;
1007 1007
1008 /* 1008 /*
1009 * If requested, don't export these in the environment 1009 * If requested, don't export these in the environment
1010 * individually. We still put them in MAKEOVERRIDES so 1010 * individually. We still put them in MAKEOVERRIDES so
1011 * that the command-line settings continue to override 1011 * that the command-line settings continue to override
1012 * Makefile settings. 1012 * Makefile settings.
1013 */ 1013 */
1014 if (!opts.varNoExportEnv) 1014 if (!opts.varNoExportEnv)
1015 setenv(name, val, 1); 1015 setenv(name, val, 1);
1016 /* XXX: What about .MAKE.EXPORTED? */ 1016 /* XXX: What about .MAKE.EXPORTED? */
1017 /* XXX: Why not just mark the variable for needing export, 1017 /* XXX: Why not just mark the variable for needing export,
1018 * as in ExportVarPlain? */ 1018 * as in ExportVarPlain? */
1019 1019
1020 Global_Append(MAKEOVERRIDES, name); 1020 Global_Append(MAKEOVERRIDES, name);
1021 } 1021 }
1022 1022
1023 if (name[0] == '.' && strcmp(name, MAKE_SAVE_DOLLARS) == 0) 1023 if (name[0] == '.' && strcmp(name, MAKE_SAVE_DOLLARS) == 0)
1024 save_dollars = ParseBoolean(val, save_dollars); 1024 save_dollars = ParseBoolean(val, save_dollars);
1025 1025
1026 if (v != NULL) 1026 if (v != NULL)
1027 VarFreeEnv(v); 1027 VarFreeEnv(v);
1028} 1028}
1029 1029
1030/* See Var_Set for documentation. */ 1030/* See Var_Set for documentation. */
1031void 1031void
1032Var_SetExpandWithFlags(GNode *scope, const char *name, const char *val, 1032Var_SetExpandWithFlags(GNode *scope, const char *name, const char *val,
1033 VarSetFlags flags) 1033 VarSetFlags flags)
1034{ 1034{
1035 const char *unexpanded_name = name; 1035 const char *unexpanded_name = name;
1036 FStr varname = FStr_InitRefer(name); 1036 FStr varname = FStr_InitRefer(name);
1037 1037
1038 assert(val != NULL); 1038 assert(val != NULL);
1039 1039
1040 if (strchr(varname.str, '$') != NULL) { 1040 if (strchr(varname.str, '$') != NULL) {
1041 char *expanded; 1041 char *expanded;
1042 (void)Var_Subst(varname.str, scope, VARE_WANTRES, &expanded); 1042 (void)Var_Subst(varname.str, scope, VARE_WANTRES, &expanded);
1043 /* TODO: handle errors */ 1043 /* TODO: handle errors */
1044 varname = FStr_InitOwn(expanded); 1044 varname = FStr_InitOwn(expanded);
1045 } 1045 }
1046 1046
1047 if (varname.str[0] == '\0') { 1047 if (varname.str[0] == '\0') {
1048 DEBUG2(VAR, "Var_Set(\"%s\", \"%s\", ...) " 1048 DEBUG2(VAR, "Var_Set(\"%s\", \"%s\", ...) "
1049 "name expands to empty string - ignored\n", 1049 "name expands to empty string - ignored\n",
1050 unexpanded_name, val); 1050 unexpanded_name, val);
1051 } else 1051 } else
1052 Var_SetWithFlags(scope, varname.str, val, flags); 1052 Var_SetWithFlags(scope, varname.str, val, flags);
1053 1053
1054 FStr_Done(&varname); 1054 FStr_Done(&varname);
1055} 1055}
1056 1056
1057void 1057void
1058Var_Set(GNode *scope, const char *name, const char *val) 1058Var_Set(GNode *scope, const char *name, const char *val)
1059{ 1059{
1060 Var_SetWithFlags(scope, name, val, VAR_SET_NONE); 1060 Var_SetWithFlags(scope, name, val, VAR_SET_NONE);
1061} 1061}
1062 1062
1063/* 1063/*
1064 * Set the variable name to the value val in the given scope. 1064 * Set the variable name to the value val in the given scope.
1065 * 1065 *
1066 * If the variable doesn't yet exist, it is created. 1066 * If the variable doesn't yet exist, it is created.
1067 * Otherwise the new value overwrites and replaces the old value. 1067 * Otherwise the new value overwrites and replaces the old value.
1068 * 1068 *
1069 * Input: 1069 * Input:
1070 * name name of the variable to set, is expanded once 1070 * name name of the variable to set, is expanded once
1071 * val value to give to the variable 1071 * val value to give to the variable
1072 * scope scope in which to set it 1072 * scope scope in which to set it
1073 */ 1073 */
1074void 1074void
1075Var_SetExpand(GNode *scope, const char *name, const char *val) 1075Var_SetExpand(GNode *scope, const char *name, const char *val)
1076{ 1076{
1077 Var_SetExpandWithFlags(scope, name, val, VAR_SET_NONE); 1077 Var_SetExpandWithFlags(scope, name, val, VAR_SET_NONE);
1078} 1078}
1079 1079
1080void 1080void
1081Global_Set(const char *name, const char *value) 1081Global_Set(const char *name, const char *value)
1082{ 1082{
1083 Var_Set(SCOPE_GLOBAL, name, value); 1083 Var_Set(SCOPE_GLOBAL, name, value);
1084} 1084}
1085 1085
1086void 1086void
1087Global_SetExpand(const char *name, const char *value) 1087Global_SetExpand(const char *name, const char *value)
1088{ 1088{
1089 Var_SetExpand(SCOPE_GLOBAL, name, value); 1089 Var_SetExpand(SCOPE_GLOBAL, name, value);
1090} 1090}
1091 1091
1092void 1092void
1093Global_Delete(const char *name) 1093Global_Delete(const char *name)
1094{ 1094{
1095 Var_Delete(SCOPE_GLOBAL, name); 1095 Var_Delete(SCOPE_GLOBAL, name);
1096} 1096}
1097 1097
1098/* 1098/*
1099 * Append the value to the named variable. 1099 * Append the value to the named variable.
1100 * 1100 *
1101 * If the variable doesn't exist, it is created. Otherwise a single space 1101 * If the variable doesn't exist, it is created. Otherwise a single space
1102 * and the given value are appended. 1102 * and the given value are appended.
1103 */ 1103 */
1104void 1104void
1105Var_Append(GNode *scope, const char *name, const char *val) 1105Var_Append(GNode *scope, const char *name, const char *val)
1106{ 1106{
1107 Var *v; 1107 Var *v;
1108 1108
1109 v = VarFind(name, scope, scope == SCOPE_GLOBAL); 1109 v = VarFind(name, scope, scope == SCOPE_GLOBAL);
1110 1110
1111 if (v == NULL) { 1111 if (v == NULL) {
1112 Var_SetWithFlags(scope, name, val, VAR_SET_NONE); 1112 Var_SetWithFlags(scope, name, val, VAR_SET_NONE);
1113 } else if (v->flags & VFL_READONLY) { 1113 } else if (v->flags & VFL_READONLY) {
1114 DEBUG1(VAR, "Ignoring append to %s since it is read-only\n", 1114 DEBUG1(VAR, "Ignoring append to %s since it is read-only\n",
1115 name); 1115 name);
1116 } else if (scope == SCOPE_CMDLINE || !(v->flags & VFL_FROM_CMD)) { 1116 } else if (scope == SCOPE_CMDLINE || !(v->flags & VFL_FROM_CMD)) {
1117 Buf_AddByte(&v->val, ' '); 1117 Buf_AddByte(&v->val, ' ');
1118 Buf_AddStr(&v->val, val); 1118 Buf_AddStr(&v->val, val);
1119 1119
1120 DEBUG3(VAR, "%s:%s = %s\n", scope->name, name, v->val.data); 1120 DEBUG3(VAR, "%s:%s = %s\n", scope->name, name, v->val.data);
1121 1121
1122 if (v->flags & VFL_FROM_ENV) { 1122 if (v->flags & VFL_FROM_ENV) {
1123 /* 1123 /*
1124 * If the original variable came from the environment, 1124 * If the original variable came from the environment,
1125 * we have to install it in the global scope (we 1125 * we have to install it in the global scope (we
1126 * could place it in the environment, but then we 1126 * could place it in the environment, but then we
1127 * should provide a way to export other variables...) 1127 * should provide a way to export other variables...)
1128 */ 1128 */
1129 v->flags &= ~(unsigned)VFL_FROM_ENV; 1129 v->flags &= ~(unsigned)VFL_FROM_ENV;
1130 /* 1130 /*
1131 * This is the only place where a variable is 1131 * This is the only place where a variable is
1132 * created whose v->name is not the same as 1132 * created whose v->name is not the same as
1133 * scope->vars->key. 1133 * scope->vars->key.
1134 */ 1134 */
1135 HashTable_Set(&scope->vars, name, v); 1135 HashTable_Set(&scope->vars, name, v);
1136 } 1136 }
1137 } 1137 }
1138} 1138}
1139 1139
1140/* 1140/*
1141 * The variable of the given name has the given value appended to it in the 1141 * The variable of the given name has the given value appended to it in the
1142 * given scope. 1142 * given scope.
@@ -1896,1998 +1896,2004 @@ VarHash(const char *str) @@ -1896,1998 +1896,2004 @@ VarHash(const char *str)
1896 1896
1897 char *buf; 1897 char *buf;
1898 size_t i; 1898 size_t i;
1899 1899
1900 size_t len; 1900 size_t len;
1901 for (len = len2; len != 0;) { 1901 for (len = len2; len != 0;) {
1902 uint32_t k = 0; 1902 uint32_t k = 0;
1903 switch (len) { 1903 switch (len) {
1904 default: 1904 default:
1905 k = ((uint32_t)ustr[3] << 24) | 1905 k = ((uint32_t)ustr[3] << 24) |
1906 ((uint32_t)ustr[2] << 16) | 1906 ((uint32_t)ustr[2] << 16) |
1907 ((uint32_t)ustr[1] << 8) | 1907 ((uint32_t)ustr[1] << 8) |
1908 (uint32_t)ustr[0]; 1908 (uint32_t)ustr[0];
1909 len -= 4; 1909 len -= 4;
1910 ustr += 4; 1910 ustr += 4;
1911 break; 1911 break;
1912 case 3: 1912 case 3:
1913 k |= (uint32_t)ustr[2] << 16; 1913 k |= (uint32_t)ustr[2] << 16;
1914 /* FALLTHROUGH */ 1914 /* FALLTHROUGH */
1915 case 2: 1915 case 2:
1916 k |= (uint32_t)ustr[1] << 8; 1916 k |= (uint32_t)ustr[1] << 8;
1917 /* FALLTHROUGH */ 1917 /* FALLTHROUGH */
1918 case 1: 1918 case 1:
1919 k |= (uint32_t)ustr[0]; 1919 k |= (uint32_t)ustr[0];
1920 len = 0; 1920 len = 0;
1921 } 1921 }
1922 c1 = c1 * 5 + 0x7b7d159cU; 1922 c1 = c1 * 5 + 0x7b7d159cU;
1923 c2 = c2 * 5 + 0x6bce6396U; 1923 c2 = c2 * 5 + 0x6bce6396U;
1924 k *= c1; 1924 k *= c1;
1925 k = (k << 11) ^ (k >> 21); 1925 k = (k << 11) ^ (k >> 21);
1926 k *= c2; 1926 k *= c2;
1927 h = (h << 13) ^ (h >> 19); 1927 h = (h << 13) ^ (h >> 19);
1928 h = h * 5 + 0x52dce729U; 1928 h = h * 5 + 0x52dce729U;
1929 h ^= k; 1929 h ^= k;
1930 } 1930 }
1931 h ^= (uint32_t)len2; 1931 h ^= (uint32_t)len2;
1932 h *= 0x85ebca6b; 1932 h *= 0x85ebca6b;
1933 h ^= h >> 13; 1933 h ^= h >> 13;
1934 h *= 0xc2b2ae35; 1934 h *= 0xc2b2ae35;
1935 h ^= h >> 16; 1935 h ^= h >> 16;
1936 1936
1937 buf = bmake_malloc(9); 1937 buf = bmake_malloc(9);
1938 for (i = 0; i < 8; i++) { 1938 for (i = 0; i < 8; i++) {
1939 buf[i] = hexdigits[h & 0x0f]; 1939 buf[i] = hexdigits[h & 0x0f];
1940 h >>= 4; 1940 h >>= 4;
1941 } 1941 }
1942 buf[8] = '\0'; 1942 buf[8] = '\0';
1943 return buf; 1943 return buf;
1944} 1944}
1945 1945
1946static char * 1946static char *
1947VarStrftime(const char *fmt, Boolean zulu, time_t tim) 1947VarStrftime(const char *fmt, Boolean zulu, time_t tim)
1948{ 1948{
1949 char buf[BUFSIZ]; 1949 char buf[BUFSIZ];
1950 1950
1951 if (tim == 0) 1951 if (tim == 0)
1952 time(&tim); 1952 time(&tim);
1953 if (*fmt == '\0') 1953 if (*fmt == '\0')
1954 fmt = "%c"; 1954 fmt = "%c";
1955 strftime(buf, sizeof buf, fmt, zulu ? gmtime(&tim) : localtime(&tim)); 1955 strftime(buf, sizeof buf, fmt, zulu ? gmtime(&tim) : localtime(&tim));
1956 1956
1957 buf[sizeof buf - 1] = '\0'; 1957 buf[sizeof buf - 1] = '\0';
1958 return bmake_strdup(buf); 1958 return bmake_strdup(buf);
1959} 1959}
1960 1960
1961/* 1961/*
1962 * The ApplyModifier functions take an expression that is being evaluated. 1962 * The ApplyModifier functions take an expression that is being evaluated.
1963 * Their task is to apply a single modifier to the expression. This involves 1963 * Their task is to apply a single modifier to the expression. This involves
1964 * parsing the modifier, evaluating it and finally updating the value of the 1964 * parsing the modifier, evaluating it and finally updating the value of the
1965 * expression. 1965 * expression.
1966 * 1966 *
1967 * Parsing the modifier 1967 * Parsing the modifier
1968 * 1968 *
1969 * If parsing succeeds, the parsing position *pp is updated to point to the 1969 * If parsing succeeds, the parsing position *pp is updated to point to the
1970 * first character following the modifier, which typically is either ':' or 1970 * first character following the modifier, which typically is either ':' or
1971 * st->endc. The modifier doesn't have to check for this delimiter character, 1971 * st->endc. The modifier doesn't have to check for this delimiter character,
1972 * this is done by ApplyModifiers. 1972 * this is done by ApplyModifiers.
1973 * 1973 *
1974 * XXX: As of 2020-11-15, some modifiers such as :S, :C, :P, :L do not 1974 * XXX: As of 2020-11-15, some modifiers such as :S, :C, :P, :L do not
1975 * need to be followed by a ':' or endc; this was an unintended mistake. 1975 * need to be followed by a ':' or endc; this was an unintended mistake.
1976 * 1976 *
1977 * If parsing fails because of a missing delimiter (as in the :S, :C or :@ 1977 * If parsing fails because of a missing delimiter (as in the :S, :C or :@
1978 * modifiers), return AMR_CLEANUP. 1978 * modifiers), return AMR_CLEANUP.
1979 * 1979 *
1980 * If parsing fails because the modifier is unknown, return AMR_UNKNOWN to 1980 * If parsing fails because the modifier is unknown, return AMR_UNKNOWN to
1981 * try the SysV modifier ${VAR:from=to} as fallback. This should only be 1981 * try the SysV modifier ${VAR:from=to} as fallback. This should only be
1982 * done as long as there have been no side effects from evaluating nested 1982 * done as long as there have been no side effects from evaluating nested
1983 * variables, to avoid evaluating them more than once. In this case, the 1983 * variables, to avoid evaluating them more than once. In this case, the
1984 * parsing position may or may not be updated. (XXX: Why not? The original 1984 * parsing position may or may not be updated. (XXX: Why not? The original
1985 * parsing position is well-known in ApplyModifiers.) 1985 * parsing position is well-known in ApplyModifiers.)
1986 * 1986 *
1987 * If parsing fails and the SysV modifier ${VAR:from=to} should not be used 1987 * If parsing fails and the SysV modifier ${VAR:from=to} should not be used
1988 * as a fallback, either issue an error message using Error or Parse_Error 1988 * as a fallback, either issue an error message using Error or Parse_Error
1989 * and then return AMR_CLEANUP, or return AMR_BAD for the default error 1989 * and then return AMR_CLEANUP, or return AMR_BAD for the default error
1990 * message. Both of these return values will stop processing the variable 1990 * message. Both of these return values will stop processing the variable
1991 * expression. (XXX: As of 2020-08-23, evaluation of the whole string 1991 * expression. (XXX: As of 2020-08-23, evaluation of the whole string
1992 * continues nevertheless after skipping a few bytes, which essentially is 1992 * continues nevertheless after skipping a few bytes, which essentially is
1993 * undefined behavior. Not in the sense of C, but still the resulting string 1993 * undefined behavior. Not in the sense of C, but still the resulting string
1994 * is garbage.) 1994 * is garbage.)
1995 * 1995 *
1996 * Evaluating the modifier 1996 * Evaluating the modifier
1997 * 1997 *
1998 * After parsing, the modifier is evaluated. The side effects from evaluating 1998 * After parsing, the modifier is evaluated. The side effects from evaluating
1999 * nested variable expressions in the modifier text often already happen 1999 * nested variable expressions in the modifier text often already happen
2000 * during parsing though. For most modifiers this doesn't matter since their 2000 * during parsing though. For most modifiers this doesn't matter since their
2001 * only noticeable effect is that the update the value of the expression. 2001 * only noticeable effect is that the update the value of the expression.
2002 * Some modifiers such as ':sh' or '::=' have noticeable side effects though. 2002 * Some modifiers such as ':sh' or '::=' have noticeable side effects though.
2003 * 2003 *
2004 * Evaluating the modifier usually takes the current value of the variable 2004 * Evaluating the modifier usually takes the current value of the variable
2005 * expression from st->expr->value, or the variable name from st->var->name 2005 * expression from st->expr->value, or the variable name from st->var->name
2006 * and stores the result back in expr->value via Expr_SetValueOwn or 2006 * and stores the result back in expr->value via Expr_SetValueOwn or
2007 * Expr_SetValueRefer. 2007 * Expr_SetValueRefer.
2008 * 2008 *
2009 * If evaluating fails (as of 2020-08-23), an error message is printed using 2009 * If evaluating fails (as of 2020-08-23), an error message is printed using
2010 * Error. This function has no side-effects, it really just prints the error 2010 * Error. This function has no side-effects, it really just prints the error
2011 * message. Processing the expression continues as if everything were ok. 2011 * message. Processing the expression continues as if everything were ok.
2012 * XXX: This should be fixed by adding proper error handling to Var_Subst, 2012 * XXX: This should be fixed by adding proper error handling to Var_Subst,
2013 * Var_Parse, ApplyModifiers and ModifyWords. 2013 * Var_Parse, ApplyModifiers and ModifyWords.
2014 * 2014 *
2015 * Housekeeping 2015 * Housekeeping
2016 * 2016 *
2017 * Some modifiers such as :D and :U turn undefined expressions into defined 2017 * Some modifiers such as :D and :U turn undefined expressions into defined
2018 * expressions (see Expr_Define). 2018 * expressions (see Expr_Define).
2019 * 2019 *
2020 * Some modifiers need to free some memory. 2020 * Some modifiers need to free some memory.
2021 */ 2021 */
2022 2022
2023typedef enum ExprDefined { 2023typedef enum ExprDefined {
2024 /* The variable expression is based on a regular, defined variable. */ 2024 /* The variable expression is based on a regular, defined variable. */
2025 DEF_REGULAR, 2025 DEF_REGULAR,
2026 /* The variable expression is based on an undefined variable. */ 2026 /* The variable expression is based on an undefined variable. */
2027 DEF_UNDEF, 2027 DEF_UNDEF,
2028 /* 2028 /*
2029 * The variable expression started as an undefined expression, but one 2029 * The variable expression started as an undefined expression, but one
2030 * of the modifiers (such as ':D' or ':U') has turned the expression 2030 * of the modifiers (such as ':D' or ':U') has turned the expression
2031 * from undefined to defined. 2031 * from undefined to defined.
2032 */ 2032 */
2033 DEF_DEFINED 2033 DEF_DEFINED
2034} ExprDefined; 2034} ExprDefined;
2035 2035
2036static const char *const ExprDefined_Name[] = { 2036static const char *const ExprDefined_Name[] = {
2037 "regular", 2037 "regular",
2038 "undefined", 2038 "undefined",
2039 "defined" 2039 "defined"
2040}; 2040};
2041 2041
2042/* A variable expression such as $@ or ${VAR:Mpattern:Q}. */ 2042/* A variable expression such as $@ or ${VAR:Mpattern:Q}. */
2043typedef struct Expr { 2043typedef struct Expr {
2044 Var *var; 2044 Var *var;
2045 FStr value; 2045 FStr value;
2046 VarEvalFlags const eflags; 2046 VarEvalFlags const eflags;
2047 GNode *const scope; 2047 GNode *const scope;
2048 ExprDefined defined; 2048 ExprDefined defined;
2049} Expr; 2049} Expr;
2050 2050
2051/* 2051/*
2052 * Data that is used when applying a chain of modifiers to an expression. 2052 * Data that is used when applying a chain of modifiers to an expression.
2053 * For indirect modifiers, the effects of this data stops after the indirect 2053 * For indirect modifiers, the effects of this data stops after the indirect
2054 * modifiers have been applies. 2054 * modifiers have been applies.
2055 * 2055 *
2056 * It may or may not be intended that 'status' has scope Expr while 'sep' and 2056 * It may or may not be intended that 'status' has scope Expr while 'sep' and
2057 * 'oneBigWord' have smaller scope, terminating at the end of a chain of 2057 * 'oneBigWord' have smaller scope, terminating at the end of a chain of
2058 * indirect modifiers. 2058 * indirect modifiers.
2059 * 2059 *
2060 * See varmod-indirect.mk. 2060 * See varmod-indirect.mk.
2061 */ 2061 */
2062typedef struct ApplyModifiersState { 2062typedef struct ApplyModifiersState {
2063 Expr *expr; 2063 Expr *expr;
2064 /* '\0' or '{' or '(' */ 2064 /* '\0' or '{' or '(' */
2065 const char startc; 2065 const char startc;
2066 /* '\0' or '}' or ')' */ 2066 /* '\0' or '}' or ')' */
2067 const char endc; 2067 const char endc;
2068 /* Word separator in expansions (see the :ts modifier). */ 2068 /* Word separator in expansions (see the :ts modifier). */
2069 char sep; 2069 char sep;
2070 /* 2070 /*
2071 * TRUE if some modifiers that otherwise split the variable value 2071 * TRUE if some modifiers that otherwise split the variable value
2072 * into words, like :S and :C, treat the variable value as a single 2072 * into words, like :S and :C, treat the variable value as a single
2073 * big word, possibly containing spaces. 2073 * big word, possibly containing spaces.
2074 */ 2074 */
2075 Boolean oneBigWord; 2075 Boolean oneBigWord;
2076} ApplyModifiersState; 2076} ApplyModifiersState;
2077 2077
2078static void 2078static void
2079Expr_Define(Expr *expr) 2079Expr_Define(Expr *expr)
2080{ 2080{
2081 if (expr->defined == DEF_UNDEF) 2081 if (expr->defined == DEF_UNDEF)
2082 expr->defined = DEF_DEFINED; 2082 expr->defined = DEF_DEFINED;
2083} 2083}
2084 2084
2085static void 2085static void
2086Expr_SetValueOwn(Expr *expr, char *value) 2086Expr_SetValueOwn(Expr *expr, char *value)
2087{ 2087{
2088 FStr_Done(&expr->value); 2088 FStr_Done(&expr->value);
2089 expr->value = FStr_InitOwn(value); 2089 expr->value = FStr_InitOwn(value);
2090} 2090}
2091 2091
2092static void 2092static void
2093Expr_SetValueRefer(Expr *expr, const char *value) 2093Expr_SetValueRefer(Expr *expr, const char *value)
2094{ 2094{
2095 FStr_Done(&expr->value); 2095 FStr_Done(&expr->value);
2096 expr->value = FStr_InitRefer(value); 2096 expr->value = FStr_InitRefer(value);
2097} 2097}
2098 2098
2099typedef enum ApplyModifierResult { 2099typedef enum ApplyModifierResult {
2100 /* Continue parsing */ 2100 /* Continue parsing */
2101 AMR_OK, 2101 AMR_OK,
2102 /* Not a match, try other modifiers as well. */ 2102 /* Not a match, try other modifiers as well. */
2103 AMR_UNKNOWN, 2103 AMR_UNKNOWN,
2104 /* Error out with "Bad modifier" message. */ 2104 /* Error out with "Bad modifier" message. */
2105 AMR_BAD, 2105 AMR_BAD,
2106 /* Error out without the standard error message. */ 2106 /* Error out without the standard error message. */
2107 AMR_CLEANUP 2107 AMR_CLEANUP
2108} ApplyModifierResult; 2108} ApplyModifierResult;
2109 2109
2110/* 2110/*
2111 * Allow backslashes to escape the delimiter, $, and \, but don't touch other 2111 * Allow backslashes to escape the delimiter, $, and \, but don't touch other
2112 * backslashes. 2112 * backslashes.
2113 */ 2113 */
2114static Boolean 2114static Boolean
2115IsEscapedModifierPart(const char *p, char delim, 2115IsEscapedModifierPart(const char *p, char delim,
2116 struct ModifyWord_SubstArgs *subst) 2116 struct ModifyWord_SubstArgs *subst)
2117{ 2117{
2118 if (p[0] != '\\') 2118 if (p[0] != '\\')
2119 return FALSE; 2119 return FALSE;
2120 if (p[1] == delim || p[1] == '\\' || p[1] == '$') 2120 if (p[1] == delim || p[1] == '\\' || p[1] == '$')
2121 return TRUE; 2121 return TRUE;
2122 return p[1] == '&' && subst != NULL; 2122 return p[1] == '&' && subst != NULL;
2123} 2123}
2124 2124
2125/* See ParseModifierPart */ 2125/* See ParseModifierPart */
2126static VarParseResult 2126static VarParseResult
2127ParseModifierPartSubst( 2127ParseModifierPartSubst(
2128 const char **pp, 2128 const char **pp,
2129 char delim, 2129 char delim,
2130 VarEvalFlags eflags, 2130 VarEvalFlags eflags,
2131 ApplyModifiersState *st, 2131 ApplyModifiersState *st,
2132 char **out_part, 2132 char **out_part,
2133 /* Optionally stores the length of the returned string, just to save 2133 /* Optionally stores the length of the returned string, just to save
2134 * another strlen call. */ 2134 * another strlen call. */
2135 size_t *out_length, 2135 size_t *out_length,
2136 /* For the first part of the :S modifier, sets the VARP_ANCHOR_END flag 2136 /* For the first part of the :S modifier, sets the VARP_ANCHOR_END flag
2137 * if the last character of the pattern is a $. */ 2137 * if the last character of the pattern is a $. */
2138 VarPatternFlags *out_pflags, 2138 VarPatternFlags *out_pflags,
2139 /* For the second part of the :S modifier, allow ampersands to be 2139 /* For the second part of the :S modifier, allow ampersands to be
2140 * escaped and replace unescaped ampersands with subst->lhs. */ 2140 * escaped and replace unescaped ampersands with subst->lhs. */
2141 struct ModifyWord_SubstArgs *subst 2141 struct ModifyWord_SubstArgs *subst
2142) 2142)
2143{ 2143{
2144 Buffer buf; 2144 Buffer buf;
2145 const char *p; 2145 const char *p;
2146 2146
2147 Buf_Init(&buf); 2147 Buf_Init(&buf);
2148 2148
2149 /* 2149 /*
2150 * Skim through until the matching delimiter is found; pick up 2150 * Skim through until the matching delimiter is found; pick up
2151 * variable expressions on the way. 2151 * variable expressions on the way.
2152 */ 2152 */
2153 p = *pp; 2153 p = *pp;
2154 while (*p != '\0' && *p != delim) { 2154 while (*p != '\0' && *p != delim) {
2155 const char *varstart; 2155 const char *varstart;
2156 2156
2157 if (IsEscapedModifierPart(p, delim, subst)) { 2157 if (IsEscapedModifierPart(p, delim, subst)) {
2158 Buf_AddByte(&buf, p[1]); 2158 Buf_AddByte(&buf, p[1]);
2159 p += 2; 2159 p += 2;
2160 continue; 2160 continue;
2161 } 2161 }
2162 2162
2163 if (*p != '$') { /* Unescaped, simple text */ 2163 if (*p != '$') { /* Unescaped, simple text */
2164 if (subst != NULL && *p == '&') 2164 if (subst != NULL && *p == '&')
2165 Buf_AddBytes(&buf, subst->lhs, subst->lhsLen); 2165 Buf_AddBytes(&buf, subst->lhs, subst->lhsLen);
2166 else 2166 else
2167 Buf_AddByte(&buf, *p); 2167 Buf_AddByte(&buf, *p);
2168 p++; 2168 p++;
2169 continue; 2169 continue;
2170 } 2170 }
2171 2171
2172 if (p[1] == delim) { /* Unescaped $ at end of pattern */ 2172 if (p[1] == delim) { /* Unescaped $ at end of pattern */
2173 if (out_pflags != NULL) 2173 if (out_pflags != NULL)
2174 out_pflags->anchorEnd = TRUE; 2174 out_pflags->anchorEnd = TRUE;
2175 else 2175 else
2176 Buf_AddByte(&buf, *p); 2176 Buf_AddByte(&buf, *p);
2177 p++; 2177 p++;
2178 continue; 2178 continue;
2179 } 2179 }
2180 2180
2181 if (eflags & VARE_WANTRES) { /* Nested variable, evaluated */ 2181 if (eflags & VARE_WANTRES) { /* Nested variable, evaluated */
2182 const char *nested_p = p; 2182 const char *nested_p = p;
2183 FStr nested_val; 2183 FStr nested_val;
2184 VarEvalFlags nested_eflags = 2184 VarEvalFlags nested_eflags =
2185 eflags & ~(unsigned)VARE_KEEP_DOLLAR; 2185 eflags & ~(unsigned)VARE_KEEP_DOLLAR;
2186 2186
2187 (void)Var_Parse(&nested_p, st->expr->scope, 2187 (void)Var_Parse(&nested_p, st->expr->scope,
2188 nested_eflags, &nested_val); 2188 nested_eflags, &nested_val);
2189 /* TODO: handle errors */ 2189 /* TODO: handle errors */
2190 Buf_AddStr(&buf, nested_val.str); 2190 Buf_AddStr(&buf, nested_val.str);
2191 FStr_Done(&nested_val); 2191 FStr_Done(&nested_val);
2192 p += nested_p - p; 2192 p += nested_p - p;
2193 continue; 2193 continue;
2194 } 2194 }
2195 2195
2196 /* 2196 /*
2197 * XXX: This whole block is very similar to Var_Parse without 2197 * XXX: This whole block is very similar to Var_Parse without
2198 * VARE_WANTRES. There may be subtle edge cases though that 2198 * VARE_WANTRES. There may be subtle edge cases though that
2199 * are not yet covered in the unit tests and that are parsed 2199 * are not yet covered in the unit tests and that are parsed
2200 * differently, depending on whether they are evaluated or 2200 * differently, depending on whether they are evaluated or
2201 * not. 2201 * not.
2202 * 2202 *
2203 * This subtle difference is not documented in the manual 2203 * This subtle difference is not documented in the manual
2204 * page, neither is the difference between parsing :D and 2204 * page, neither is the difference between parsing :D and
2205 * :M documented. No code should ever depend on these 2205 * :M documented. No code should ever depend on these
2206 * details, but who knows. 2206 * details, but who knows.
2207 */ 2207 */
2208 2208
2209 varstart = p; /* Nested variable, only parsed */ 2209 varstart = p; /* Nested variable, only parsed */
2210 if (p[1] == '(' || p[1] == '{') { 2210 if (p[1] == '(' || p[1] == '{') {
2211 /* 2211 /*
2212 * Find the end of this variable reference 2212 * Find the end of this variable reference
2213 * and suck it in without further ado. 2213 * and suck it in without further ado.
2214 * It will be interpreted later. 2214 * It will be interpreted later.
2215 */ 2215 */
2216 char startc = p[1]; 2216 char startc = p[1];
2217 int endc = startc == '(' ? ')' : '}'; 2217 int endc = startc == '(' ? ')' : '}';
2218 int depth = 1; 2218 int depth = 1;
2219 2219
2220 for (p += 2; *p != '\0' && depth > 0; p++) { 2220 for (p += 2; *p != '\0' && depth > 0; p++) {
2221 if (p[-1] != '\\') { 2221 if (p[-1] != '\\') {
2222 if (*p == startc) 2222 if (*p == startc)
2223 depth++; 2223 depth++;
2224 if (*p == endc) 2224 if (*p == endc)
2225 depth--; 2225 depth--;
2226 } 2226 }
2227 } 2227 }
2228 Buf_AddBytesBetween(&buf, varstart, p); 2228 Buf_AddBytesBetween(&buf, varstart, p);
2229 } else { 2229 } else {
2230 Buf_AddByte(&buf, *varstart); 2230 Buf_AddByte(&buf, *varstart);
2231 p++; 2231 p++;
2232 } 2232 }
2233 } 2233 }
2234 2234
2235 if (*p != delim) { 2235 if (*p != delim) {
2236 *pp = p; 2236 *pp = p;
2237 Error("Unfinished modifier for \"%s\" ('%c' missing)", 2237 Error("Unfinished modifier for \"%s\" ('%c' missing)",
2238 st->expr->var->name.str, delim); 2238 st->expr->var->name.str, delim);
2239 *out_part = NULL; 2239 *out_part = NULL;
2240 return VPR_ERR; 2240 return VPR_ERR;
2241 } 2241 }
2242 2242
2243 *pp = p + 1; 2243 *pp = p + 1;
2244 if (out_length != NULL) 2244 if (out_length != NULL)
2245 *out_length = buf.len; 2245 *out_length = buf.len;
2246 2246
2247 *out_part = Buf_DoneData(&buf); 2247 *out_part = Buf_DoneData(&buf);
2248 DEBUG1(VAR, "Modifier part: \"%s\"\n", *out_part); 2248 DEBUG1(VAR, "Modifier part: \"%s\"\n", *out_part);
2249 return VPR_OK; 2249 return VPR_OK;
2250} 2250}
2251 2251
2252/* 2252/*
2253 * Parse a part of a modifier such as the "from" and "to" in :S/from/to/ or 2253 * Parse a part of a modifier such as the "from" and "to" in :S/from/to/ or
2254 * the "var" or "replacement ${var}" in :@var@replacement ${var}@, up to and 2254 * the "var" or "replacement ${var}" in :@var@replacement ${var}@, up to and
2255 * including the next unescaped delimiter. The delimiter, as well as the 2255 * including the next unescaped delimiter. The delimiter, as well as the
2256 * backslash or the dollar, can be escaped with a backslash. 2256 * backslash or the dollar, can be escaped with a backslash.
2257 * 2257 *
2258 * Return the parsed (and possibly expanded) string, or NULL if no delimiter 2258 * Return the parsed (and possibly expanded) string, or NULL if no delimiter
2259 * was found. On successful return, the parsing position pp points right 2259 * was found. On successful return, the parsing position pp points right
2260 * after the delimiter. The delimiter is not included in the returned 2260 * after the delimiter. The delimiter is not included in the returned
2261 * value though. 2261 * value though.
2262 */ 2262 */
2263static VarParseResult 2263static VarParseResult
2264ParseModifierPart( 2264ParseModifierPart(
2265 /* The parsing position, updated upon return */ 2265 /* The parsing position, updated upon return */
2266 const char **pp, 2266 const char **pp,
2267 /* Parsing stops at this delimiter */ 2267 /* Parsing stops at this delimiter */
2268 char delim, 2268 char delim,
2269 /* Flags for evaluating nested variables; if VARE_WANTRES is not set, 2269 /* Flags for evaluating nested variables; if VARE_WANTRES is not set,
2270 * the text is only parsed. */ 2270 * the text is only parsed. */
2271 VarEvalFlags eflags, 2271 VarEvalFlags eflags,
2272 ApplyModifiersState *st, 2272 ApplyModifiersState *st,
2273 char **out_part 2273 char **out_part
2274) 2274)
2275{ 2275{
2276 return ParseModifierPartSubst(pp, delim, eflags, st, out_part, 2276 return ParseModifierPartSubst(pp, delim, eflags, st, out_part,
2277 NULL, NULL, NULL); 2277 NULL, NULL, NULL);
2278} 2278}
2279 2279
2280/* Test whether mod starts with modname, followed by a delimiter. */ 2280/* Test whether mod starts with modname, followed by a delimiter. */
2281MAKE_INLINE Boolean 2281MAKE_INLINE Boolean
2282ModMatch(const char *mod, const char *modname, char endc) 2282ModMatch(const char *mod, const char *modname, char endc)
2283{ 2283{
2284 size_t n = strlen(modname); 2284 size_t n = strlen(modname);
2285 return strncmp(mod, modname, n) == 0 && 2285 return strncmp(mod, modname, n) == 0 &&
2286 (mod[n] == endc || mod[n] == ':'); 2286 (mod[n] == endc || mod[n] == ':');
2287} 2287}
2288 2288
2289/* Test whether mod starts with modname, followed by a delimiter or '='. */ 2289/* Test whether mod starts with modname, followed by a delimiter or '='. */
2290MAKE_INLINE Boolean 2290MAKE_INLINE Boolean
2291ModMatchEq(const char *mod, const char *modname, char endc) 2291ModMatchEq(const char *mod, const char *modname, char endc)
2292{ 2292{
2293 size_t n = strlen(modname); 2293 size_t n = strlen(modname);
2294 return strncmp(mod, modname, n) == 0 && 2294 return strncmp(mod, modname, n) == 0 &&
2295 (mod[n] == endc || mod[n] == ':' || mod[n] == '='); 2295 (mod[n] == endc || mod[n] == ':' || mod[n] == '=');
2296} 2296}
2297 2297
2298static Boolean 2298static Boolean
2299TryParseIntBase0(const char **pp, int *out_num) 2299TryParseIntBase0(const char **pp, int *out_num)
2300{ 2300{
2301 char *end; 2301 char *end;
2302 long n; 2302 long n;
2303 2303
2304 errno = 0; 2304 errno = 0;
2305 n = strtol(*pp, &end, 0); 2305 n = strtol(*pp, &end, 0);
2306 if ((n == LONG_MIN || n == LONG_MAX) && errno == ERANGE) 2306 if ((n == LONG_MIN || n == LONG_MAX) && errno == ERANGE)
2307 return FALSE; 2307 return FALSE;
2308 if (n < INT_MIN || n > INT_MAX) 2308 if (n < INT_MIN || n > INT_MAX)
2309 return FALSE; 2309 return FALSE;
2310 2310
2311 *pp = end; 2311 *pp = end;
2312 *out_num = (int)n; 2312 *out_num = (int)n;
2313 return TRUE; 2313 return TRUE;
2314} 2314}
2315 2315
2316static Boolean 2316static Boolean
2317TryParseSize(const char **pp, size_t *out_num) 2317TryParseSize(const char **pp, size_t *out_num)
2318{ 2318{
2319 char *end; 2319 char *end;
2320 unsigned long n; 2320 unsigned long n;
2321 2321
2322 if (!ch_isdigit(**pp)) 2322 if (!ch_isdigit(**pp))
2323 return FALSE; 2323 return FALSE;
2324 2324
2325 errno = 0; 2325 errno = 0;
2326 n = strtoul(*pp, &end, 10); 2326 n = strtoul(*pp, &end, 10);
2327 if (n == ULONG_MAX && errno == ERANGE) 2327 if (n == ULONG_MAX && errno == ERANGE)
2328 return FALSE; 2328 return FALSE;
2329 if (n > SIZE_MAX) 2329 if (n > SIZE_MAX)
2330 return FALSE; 2330 return FALSE;
2331 2331
2332 *pp = end; 2332 *pp = end;
2333 *out_num = (size_t)n; 2333 *out_num = (size_t)n;
2334 return TRUE; 2334 return TRUE;
2335} 2335}
2336 2336
2337static Boolean 2337static Boolean
2338TryParseChar(const char **pp, int base, char *out_ch) 2338TryParseChar(const char **pp, int base, char *out_ch)
2339{ 2339{
2340 char *end; 2340 char *end;
2341 unsigned long n; 2341 unsigned long n;
2342 2342
2343 if (!ch_isalnum(**pp)) 2343 if (!ch_isalnum(**pp))
2344 return FALSE; 2344 return FALSE;
2345 2345
2346 errno = 0; 2346 errno = 0;
2347 n = strtoul(*pp, &end, base); 2347 n = strtoul(*pp, &end, base);
2348 if (n == ULONG_MAX && errno == ERANGE) 2348 if (n == ULONG_MAX && errno == ERANGE)
2349 return FALSE; 2349 return FALSE;
2350 if (n > UCHAR_MAX) 2350 if (n > UCHAR_MAX)
2351 return FALSE; 2351 return FALSE;
2352 2352
2353 *pp = end; 2353 *pp = end;
2354 *out_ch = (char)n; 2354 *out_ch = (char)n;
2355 return TRUE; 2355 return TRUE;
2356} 2356}
2357 2357
2358/* 2358/*
2359 * Modify each word of the expression using the given function and place the 2359 * Modify each word of the expression using the given function and place the
2360 * result back in the expression. 2360 * result back in the expression.
2361 */ 2361 */
2362static void 2362static void
2363ModifyWords(ApplyModifiersState *st, 2363ModifyWords(ApplyModifiersState *st,
2364 ModifyWordProc modifyWord, void *modifyWord_args, 2364 ModifyWordProc modifyWord, void *modifyWord_args,
2365 Boolean oneBigWord) 2365 Boolean oneBigWord)
2366{ 2366{
2367 Expr *expr = st->expr; 2367 Expr *expr = st->expr;
2368 const char *val = expr->value.str; 2368 const char *val = expr->value.str;
2369 SepBuf result; 2369 SepBuf result;
2370 Words words; 2370 Words words;
2371 size_t i; 2371 size_t i;
2372 2372
2373 if (oneBigWord) { 2373 if (oneBigWord) {
2374 SepBuf_Init(&result, st->sep); 2374 SepBuf_Init(&result, st->sep);
2375 modifyWord(val, &result, modifyWord_args); 2375 modifyWord(val, &result, modifyWord_args);
2376 goto done; 2376 goto done;
2377 } 2377 }
2378 2378
2379 words = Str_Words(val, FALSE); 2379 words = Str_Words(val, FALSE);
2380 2380
2381 DEBUG2(VAR, "ModifyWords: split \"%s\" into %u words\n", 2381 DEBUG2(VAR, "ModifyWords: split \"%s\" into %u words\n",
2382 val, (unsigned)words.len); 2382 val, (unsigned)words.len);
2383 2383
2384 SepBuf_Init(&result, st->sep); 2384 SepBuf_Init(&result, st->sep);
2385 for (i = 0; i < words.len; i++) { 2385 for (i = 0; i < words.len; i++) {
2386 modifyWord(words.words[i], &result, modifyWord_args); 2386 modifyWord(words.words[i], &result, modifyWord_args);
2387 if (result.buf.len > 0) 2387 if (result.buf.len > 0)
2388 SepBuf_Sep(&result); 2388 SepBuf_Sep(&result);
2389 } 2389 }
2390 2390
2391 Words_Free(words); 2391 Words_Free(words);
2392 2392
2393done: 2393done:
2394 Expr_SetValueOwn(expr, SepBuf_DoneData(&result)); 2394 Expr_SetValueOwn(expr, SepBuf_DoneData(&result));
2395} 2395}
2396 2396
2397/* :@var@...${var}...@ */ 2397/* :@var@...${var}...@ */
2398static ApplyModifierResult 2398static ApplyModifierResult
2399ApplyModifier_Loop(const char **pp, ApplyModifiersState *st) 2399ApplyModifier_Loop(const char **pp, ApplyModifiersState *st)
2400{ 2400{
2401 Expr *expr = st->expr; 2401 Expr *expr = st->expr;
2402 struct ModifyWord_LoopArgs args; 2402 struct ModifyWord_LoopArgs args;
2403 char prev_sep; 2403 char prev_sep;
2404 VarParseResult res; 2404 VarParseResult res;
2405 2405
2406 args.scope = expr->scope; 2406 args.scope = expr->scope;
2407 2407
2408 (*pp)++; /* Skip the first '@' */ 2408 (*pp)++; /* Skip the first '@' */
2409 res = ParseModifierPart(pp, '@', VARE_NONE, st, &args.tvar); 2409 res = ParseModifierPart(pp, '@', VARE_NONE, st, &args.tvar);
2410 if (res != VPR_OK) 2410 if (res != VPR_OK)
2411 return AMR_CLEANUP; 2411 return AMR_CLEANUP;
2412 if (opts.strict && strchr(args.tvar, '$') != NULL) { 2412 if (opts.strict && strchr(args.tvar, '$') != NULL) {
2413 Parse_Error(PARSE_FATAL, 2413 Parse_Error(PARSE_FATAL,
2414 "In the :@ modifier of \"%s\", the variable name \"%s\" " 2414 "In the :@ modifier of \"%s\", the variable name \"%s\" "
2415 "must not contain a dollar.", 2415 "must not contain a dollar.",
2416 expr->var->name.str, args.tvar); 2416 expr->var->name.str, args.tvar);
2417 return AMR_CLEANUP; 2417 return AMR_CLEANUP;
2418 } 2418 }
2419 2419
2420 res = ParseModifierPart(pp, '@', VARE_NONE, st, &args.str); 2420 res = ParseModifierPart(pp, '@', VARE_NONE, st, &args.str);
2421 if (res != VPR_OK) 2421 if (res != VPR_OK)
2422 return AMR_CLEANUP; 2422 return AMR_CLEANUP;
2423 2423
2424 args.eflags = expr->eflags & ~(unsigned)VARE_KEEP_DOLLAR; 2424 args.eflags = expr->eflags & ~(unsigned)VARE_KEEP_DOLLAR;
2425 prev_sep = st->sep; 2425 prev_sep = st->sep;
2426 st->sep = ' '; /* XXX: should be st->sep for consistency */ 2426 st->sep = ' '; /* XXX: should be st->sep for consistency */
2427 ModifyWords(st, ModifyWord_Loop, &args, st->oneBigWord); 2427 ModifyWords(st, ModifyWord_Loop, &args, st->oneBigWord);
2428 st->sep = prev_sep; 2428 st->sep = prev_sep;
2429 /* XXX: Consider restoring the previous variable instead of deleting. */ 2429 /* XXX: Consider restoring the previous variable instead of deleting. */
2430 /* 2430 /*
2431 * XXX: The variable name should not be expanded here, see 2431 * XXX: The variable name should not be expanded here, see
2432 * ModifyWord_Loop. 2432 * ModifyWord_Loop.
2433 */ 2433 */
2434 Var_DeleteExpand(expr->scope, args.tvar); 2434 Var_DeleteExpand(expr->scope, args.tvar);
2435 free(args.tvar); 2435 free(args.tvar);
2436 free(args.str); 2436 free(args.str);
2437 return AMR_OK; 2437 return AMR_OK;
2438} 2438}
2439 2439
2440/* :Ddefined or :Uundefined */ 2440/* :Ddefined or :Uundefined */
2441static ApplyModifierResult 2441static ApplyModifierResult
2442ApplyModifier_Defined(const char **pp, ApplyModifiersState *st) 2442ApplyModifier_Defined(const char **pp, ApplyModifiersState *st)
2443{ 2443{
2444 Expr *expr = st->expr; 2444 Expr *expr = st->expr;
2445 Buffer buf; 2445 Buffer buf;
2446 const char *p; 2446 const char *p;
2447 2447
2448 VarEvalFlags eflags = VARE_NONE; 2448 VarEvalFlags eflags = VARE_NONE;
2449 if (expr->eflags & VARE_WANTRES) 2449 if (expr->eflags & VARE_WANTRES)
2450 if ((**pp == 'D') == (expr->defined == DEF_REGULAR)) 2450 if ((**pp == 'D') == (expr->defined == DEF_REGULAR))
2451 eflags = expr->eflags; 2451 eflags = expr->eflags;
2452 2452
2453 Buf_Init(&buf); 2453 Buf_Init(&buf);
2454 p = *pp + 1; 2454 p = *pp + 1;
2455 while (*p != st->endc && *p != ':' && *p != '\0') { 2455 while (*p != st->endc && *p != ':' && *p != '\0') {
2456 2456
2457 /* XXX: This code is similar to the one in Var_Parse. 2457 /* XXX: This code is similar to the one in Var_Parse.
2458 * See if the code can be merged. 2458 * See if the code can be merged.
2459 * See also ApplyModifier_Match and ParseModifierPart. */ 2459 * See also ApplyModifier_Match and ParseModifierPart. */
2460 2460
2461 /* Escaped delimiter or other special character */ 2461 /* Escaped delimiter or other special character */
2462 /* See Buf_AddEscaped in for.c. */ 2462 /* See Buf_AddEscaped in for.c. */
2463 if (*p == '\\') { 2463 if (*p == '\\') {
2464 char c = p[1]; 2464 char c = p[1];
2465 if (c == st->endc || c == ':' || c == '$' || 2465 if (c == st->endc || c == ':' || c == '$' ||
2466 c == '\\') { 2466 c == '\\') {
2467 Buf_AddByte(&buf, c); 2467 Buf_AddByte(&buf, c);
2468 p += 2; 2468 p += 2;
2469 continue; 2469 continue;
2470 } 2470 }
2471 } 2471 }
2472 2472
2473 /* Nested variable expression */ 2473 /* Nested variable expression */
2474 if (*p == '$') { 2474 if (*p == '$') {
2475 FStr nested_val; 2475 FStr nested_val;
2476 2476
2477 (void)Var_Parse(&p, expr->scope, eflags, &nested_val); 2477 (void)Var_Parse(&p, expr->scope, eflags, &nested_val);
2478 /* TODO: handle errors */ 2478 /* TODO: handle errors */
2479 Buf_AddStr(&buf, nested_val.str); 2479 Buf_AddStr(&buf, nested_val.str);
2480 FStr_Done(&nested_val); 2480 FStr_Done(&nested_val);
2481 continue; 2481 continue;
2482 } 2482 }
2483 2483
2484 /* Ordinary text */ 2484 /* Ordinary text */
2485 Buf_AddByte(&buf, *p); 2485 Buf_AddByte(&buf, *p);
2486 p++; 2486 p++;
2487 } 2487 }
2488 *pp = p; 2488 *pp = p;
2489 2489
2490 Expr_Define(expr); 2490 Expr_Define(expr);
2491 2491
2492 if (eflags & VARE_WANTRES) 2492 if (eflags & VARE_WANTRES)
2493 Expr_SetValueOwn(expr, Buf_DoneData(&buf)); 2493 Expr_SetValueOwn(expr, Buf_DoneData(&buf));
2494 else 2494 else
2495 Buf_Done(&buf); 2495 Buf_Done(&buf);
2496 2496
2497 return AMR_OK; 2497 return AMR_OK;
2498} 2498}
2499 2499
2500/* :L */ 2500/* :L */
2501static ApplyModifierResult 2501static ApplyModifierResult
2502ApplyModifier_Literal(const char **pp, ApplyModifiersState *st) 2502ApplyModifier_Literal(const char **pp, ApplyModifiersState *st)
2503{ 2503{
2504 Expr *expr = st->expr; 2504 Expr *expr = st->expr;
2505 Expr_Define(expr); 2505 Expr_Define(expr);
2506 Expr_SetValueOwn(expr, bmake_strdup(expr->var->name.str)); 2506 Expr_SetValueOwn(expr, bmake_strdup(expr->var->name.str));
2507 (*pp)++; 2507 (*pp)++;
2508 return AMR_OK; 2508 return AMR_OK;
2509} 2509}
2510 2510
2511static Boolean 2511static Boolean
2512TryParseTime(const char **pp, time_t *out_time) 2512TryParseTime(const char **pp, time_t *out_time)
2513{ 2513{
2514 char *end; 2514 char *end;
2515 unsigned long n; 2515 unsigned long n;
2516 2516
2517 if (!ch_isdigit(**pp)) 2517 if (!ch_isdigit(**pp))
2518 return FALSE; 2518 return FALSE;
2519 2519
2520 errno = 0; 2520 errno = 0;
2521 n = strtoul(*pp, &end, 10); 2521 n = strtoul(*pp, &end, 10);
2522 if (n == ULONG_MAX && errno == ERANGE) 2522 if (n == ULONG_MAX && errno == ERANGE)
2523 return FALSE; 2523 return FALSE;
2524 2524
2525 *pp = end; 2525 *pp = end;
2526 *out_time = (time_t)n; /* ignore possible truncation for now */ 2526 *out_time = (time_t)n; /* ignore possible truncation for now */
2527 return TRUE; 2527 return TRUE;
2528} 2528}
2529 2529
2530/* :gmtime */ 2530/* :gmtime */
2531static ApplyModifierResult 2531static ApplyModifierResult
2532ApplyModifier_Gmtime(const char **pp, ApplyModifiersState *st) 2532ApplyModifier_Gmtime(const char **pp, ApplyModifiersState *st)
2533{ 2533{
2534 time_t utc; 2534 time_t utc;
2535 2535
2536 const char *mod = *pp; 2536 const char *mod = *pp;
2537 if (!ModMatchEq(mod, "gmtime", st->endc)) 2537 if (!ModMatchEq(mod, "gmtime", st->endc))
2538 return AMR_UNKNOWN; 2538 return AMR_UNKNOWN;
2539 2539
2540 if (mod[6] == '=') { 2540 if (mod[6] == '=') {
2541 const char *p = mod + 7; 2541 const char *p = mod + 7;
2542 if (!TryParseTime(&p, &utc)) { 2542 if (!TryParseTime(&p, &utc)) {
2543 Parse_Error(PARSE_FATAL, 2543 Parse_Error(PARSE_FATAL,
2544 "Invalid time value: %s", mod + 7); 2544 "Invalid time value: %s", mod + 7);
2545 return AMR_CLEANUP; 2545 return AMR_CLEANUP;
2546 } 2546 }
2547 *pp = p; 2547 *pp = p;
2548 } else { 2548 } else {
2549 utc = 0; 2549 utc = 0;
2550 *pp = mod + 6; 2550 *pp = mod + 6;
2551 } 2551 }
2552 Expr_SetValueOwn(st->expr, 2552 Expr_SetValueOwn(st->expr,
2553 VarStrftime(st->expr->value.str, TRUE, utc)); 2553 VarStrftime(st->expr->value.str, TRUE, utc));
2554 return AMR_OK; 2554 return AMR_OK;
2555} 2555}
2556 2556
2557/* :localtime */ 2557/* :localtime */
2558static ApplyModifierResult 2558static ApplyModifierResult
2559ApplyModifier_Localtime(const char **pp, ApplyModifiersState *st) 2559ApplyModifier_Localtime(const char **pp, ApplyModifiersState *st)
2560{ 2560{
2561 time_t utc; 2561 time_t utc;
2562 2562
2563 const char *mod = *pp; 2563 const char *mod = *pp;
2564 if (!ModMatchEq(mod, "localtime", st->endc)) 2564 if (!ModMatchEq(mod, "localtime", st->endc))
2565 return AMR_UNKNOWN; 2565 return AMR_UNKNOWN;
2566 2566
2567 if (mod[9] == '=') { 2567 if (mod[9] == '=') {
2568 const char *p = mod + 10; 2568 const char *p = mod + 10;
2569 if (!TryParseTime(&p, &utc)) { 2569 if (!TryParseTime(&p, &utc)) {
2570 Parse_Error(PARSE_FATAL, 2570 Parse_Error(PARSE_FATAL,
2571 "Invalid time value: %s", mod + 10); 2571 "Invalid time value: %s", mod + 10);
2572 return AMR_CLEANUP; 2572 return AMR_CLEANUP;
2573 } 2573 }
2574 *pp = p; 2574 *pp = p;
2575 } else { 2575 } else {
2576 utc = 0; 2576 utc = 0;
2577 *pp = mod + 9; 2577 *pp = mod + 9;
2578 } 2578 }
2579 Expr_SetValueOwn(st->expr, 2579 Expr_SetValueOwn(st->expr,
2580 VarStrftime(st->expr->value.str, FALSE, utc)); 2580 VarStrftime(st->expr->value.str, FALSE, utc));
2581 return AMR_OK; 2581 return AMR_OK;
2582} 2582}
2583 2583
2584/* :hash */ 2584/* :hash */
2585static ApplyModifierResult 2585static ApplyModifierResult
2586ApplyModifier_Hash(const char **pp, ApplyModifiersState *st) 2586ApplyModifier_Hash(const char **pp, ApplyModifiersState *st)
2587{ 2587{
2588 if (!ModMatch(*pp, "hash", st->endc)) 2588 if (!ModMatch(*pp, "hash", st->endc))
2589 return AMR_UNKNOWN; 2589 return AMR_UNKNOWN;
2590 2590
2591 Expr_SetValueOwn(st->expr, VarHash(st->expr->value.str)); 2591 Expr_SetValueOwn(st->expr, VarHash(st->expr->value.str));
2592 *pp += 4; 2592 *pp += 4;
2593 return AMR_OK; 2593 return AMR_OK;
2594} 2594}
2595 2595
2596/* :P */ 2596/* :P */
2597static ApplyModifierResult 2597static ApplyModifierResult
2598ApplyModifier_Path(const char **pp, ApplyModifiersState *st) 2598ApplyModifier_Path(const char **pp, ApplyModifiersState *st)
2599{ 2599{
2600 Expr *expr = st->expr; 2600 Expr *expr = st->expr;
2601 GNode *gn; 2601 GNode *gn;
2602 char *path; 2602 char *path;
2603 2603
2604 Expr_Define(expr); 2604 Expr_Define(expr);
2605 2605
2606 gn = Targ_FindNode(expr->var->name.str); 2606 gn = Targ_FindNode(expr->var->name.str);
2607 if (gn == NULL || gn->type & OP_NOPATH) { 2607 if (gn == NULL || gn->type & OP_NOPATH) {
2608 path = NULL; 2608 path = NULL;
2609 } else if (gn->path != NULL) { 2609 } else if (gn->path != NULL) {
2610 path = bmake_strdup(gn->path); 2610 path = bmake_strdup(gn->path);
2611 } else { 2611 } else {
2612 SearchPath *searchPath = Suff_FindPath(gn); 2612 SearchPath *searchPath = Suff_FindPath(gn);
2613 path = Dir_FindFile(expr->var->name.str, searchPath); 2613 path = Dir_FindFile(expr->var->name.str, searchPath);
2614 } 2614 }
2615 if (path == NULL) 2615 if (path == NULL)
2616 path = bmake_strdup(expr->var->name.str); 2616 path = bmake_strdup(expr->var->name.str);
2617 Expr_SetValueOwn(expr, path); 2617 Expr_SetValueOwn(expr, path);
2618 2618
2619 (*pp)++; 2619 (*pp)++;
2620 return AMR_OK; 2620 return AMR_OK;
2621} 2621}
2622 2622
2623/* :!cmd! */ 2623/* :!cmd! */
2624static ApplyModifierResult 2624static ApplyModifierResult
2625ApplyModifier_ShellCommand(const char **pp, ApplyModifiersState *st) 2625ApplyModifier_ShellCommand(const char **pp, ApplyModifiersState *st)
2626{ 2626{
2627 Expr *expr = st->expr; 2627 Expr *expr = st->expr;
2628 char *cmd; 2628 char *cmd;
2629 const char *errfmt; 2629 const char *errfmt;
2630 VarParseResult res; 2630 VarParseResult res;
2631 2631
2632 (*pp)++; 2632 (*pp)++;
2633 res = ParseModifierPart(pp, '!', expr->eflags, st, &cmd); 2633 res = ParseModifierPart(pp, '!', expr->eflags, st, &cmd);
2634 if (res != VPR_OK) 2634 if (res != VPR_OK)
2635 return AMR_CLEANUP; 2635 return AMR_CLEANUP;
2636 2636
2637 errfmt = NULL; 2637 errfmt = NULL;
2638 if (expr->eflags & VARE_WANTRES) 2638 if (expr->eflags & VARE_WANTRES)
2639 Expr_SetValueOwn(expr, Cmd_Exec(cmd, &errfmt)); 2639 Expr_SetValueOwn(expr, Cmd_Exec(cmd, &errfmt));
2640 else 2640 else
2641 Expr_SetValueRefer(expr, ""); 2641 Expr_SetValueRefer(expr, "");
2642 if (errfmt != NULL) 2642 if (errfmt != NULL)
2643 Error(errfmt, cmd); /* XXX: why still return AMR_OK? */ 2643 Error(errfmt, cmd); /* XXX: why still return AMR_OK? */
2644 free(cmd); 2644 free(cmd);
2645 2645
2646 Expr_Define(expr); 2646 Expr_Define(expr);
2647 return AMR_OK; 2647 return AMR_OK;
2648} 2648}
2649 2649
2650/* 2650/*
2651 * The :range modifier generates an integer sequence as long as the words. 2651 * The :range modifier generates an integer sequence as long as the words.
2652 * The :range=7 modifier generates an integer sequence from 1 to 7. 2652 * The :range=7 modifier generates an integer sequence from 1 to 7.
2653 */ 2653 */
2654static ApplyModifierResult 2654static ApplyModifierResult
2655ApplyModifier_Range(const char **pp, ApplyModifiersState *st) 2655ApplyModifier_Range(const char **pp, ApplyModifiersState *st)
2656{ 2656{
2657 size_t n; 2657 size_t n;
2658 Buffer buf; 2658 Buffer buf;
2659 size_t i; 2659 size_t i;
2660 2660
2661 const char *mod = *pp; 2661 const char *mod = *pp;
2662 if (!ModMatchEq(mod, "range", st->endc)) 2662 if (!ModMatchEq(mod, "range", st->endc))
2663 return AMR_UNKNOWN; 2663 return AMR_UNKNOWN;
2664 2664
2665 if (mod[5] == '=') { 2665 if (mod[5] == '=') {
2666 const char *p = mod + 6; 2666 const char *p = mod + 6;
2667 if (!TryParseSize(&p, &n)) { 2667 if (!TryParseSize(&p, &n)) {
2668 Parse_Error(PARSE_FATAL, 2668 Parse_Error(PARSE_FATAL,
2669 "Invalid number \"%s\" for ':range' modifier", 2669 "Invalid number \"%s\" for ':range' modifier",
2670 mod + 6); 2670 mod + 6);
2671 return AMR_CLEANUP; 2671 return AMR_CLEANUP;
2672 } 2672 }
2673 *pp = p; 2673 *pp = p;
2674 } else { 2674 } else {
2675 n = 0; 2675 n = 0;
2676 *pp = mod + 5; 2676 *pp = mod + 5;
2677 } 2677 }
2678 2678
2679 if (n == 0) { 2679 if (n == 0) {
2680 Words words = Str_Words(st->expr->value.str, FALSE); 2680 Words words = Str_Words(st->expr->value.str, FALSE);
2681 n = words.len; 2681 n = words.len;
2682 Words_Free(words); 2682 Words_Free(words);
2683 } 2683 }
2684 2684
2685 Buf_Init(&buf); 2685 Buf_Init(&buf);
2686 2686
2687 for (i = 0; i < n; i++) { 2687 for (i = 0; i < n; i++) {
2688 if (i != 0) { 2688 if (i != 0) {
2689 /* XXX: Use st->sep instead of ' ', for consistency. */ 2689 /* XXX: Use st->sep instead of ' ', for consistency. */
2690 Buf_AddByte(&buf, ' '); 2690 Buf_AddByte(&buf, ' ');
2691 } 2691 }
2692 Buf_AddInt(&buf, 1 + (int)i); 2692 Buf_AddInt(&buf, 1 + (int)i);
2693 } 2693 }
2694 2694
2695 Expr_SetValueOwn(st->expr, Buf_DoneData(&buf)); 2695 Expr_SetValueOwn(st->expr, Buf_DoneData(&buf));
2696 return AMR_OK; 2696 return AMR_OK;
2697} 2697}
2698 2698
2699/* Parse a ':M' or ':N' modifier. */ 2699/* Parse a ':M' or ':N' modifier. */
2700static void 2700static void
2701ParseModifier_Match(const char **pp, const ApplyModifiersState *st, 2701ParseModifier_Match(const char **pp, const ApplyModifiersState *st,
2702 char **out_pattern) 2702 char **out_pattern)
2703{ 2703{
2704 const char *mod = *pp; 2704 const char *mod = *pp;
2705 Expr *expr = st->expr; 2705 Expr *expr = st->expr;
2706 Boolean copy = FALSE; /* pattern should be, or has been, copied */ 2706 Boolean copy = FALSE; /* pattern should be, or has been, copied */
2707 Boolean needSubst = FALSE; 2707 Boolean needSubst = FALSE;
2708 const char *endpat; 2708 const char *endpat;
2709 char *pattern; 2709 char *pattern;
2710 2710
2711 /* 2711 /*
2712 * In the loop below, ignore ':' unless we are at (or back to) the 2712 * In the loop below, ignore ':' unless we are at (or back to) the
2713 * original brace level. 2713 * original brace level.
2714 * XXX: This will likely not work right if $() and ${} are intermixed. 2714 * XXX: This will likely not work right if $() and ${} are intermixed.
2715 */ 2715 */
2716 /* 2716 /*
2717 * XXX: This code is similar to the one in Var_Parse. 2717 * XXX: This code is similar to the one in Var_Parse.
2718 * See if the code can be merged. 2718 * See if the code can be merged.
2719 * See also ApplyModifier_Defined. 2719 * See also ApplyModifier_Defined.
2720 */ 2720 */
2721 int nest = 0; 2721 int nest = 0;
2722 const char *p; 2722 const char *p;
2723 for (p = mod + 1; *p != '\0' && !(*p == ':' && nest == 0); p++) { 2723 for (p = mod + 1; *p != '\0' && !(*p == ':' && nest == 0); p++) {
2724 if (*p == '\\' && 2724 if (*p == '\\' &&
2725 (p[1] == ':' || p[1] == st->endc || p[1] == st->startc)) { 2725 (p[1] == ':' || p[1] == st->endc || p[1] == st->startc)) {
2726 if (!needSubst) 2726 if (!needSubst)
2727 copy = TRUE; 2727 copy = TRUE;
2728 p++; 2728 p++;
2729 continue; 2729 continue;
2730 } 2730 }
2731 if (*p == '$') 2731 if (*p == '$')
2732 needSubst = TRUE; 2732 needSubst = TRUE;
2733 if (*p == '(' || *p == '{') 2733 if (*p == '(' || *p == '{')
2734 nest++; 2734 nest++;
2735 if (*p == ')' || *p == '}') { 2735 if (*p == ')' || *p == '}') {
2736 nest--; 2736 nest--;
2737 if (nest < 0) 2737 if (nest < 0)
2738 break; 2738 break;
2739 } 2739 }
2740 } 2740 }
2741 *pp = p; 2741 *pp = p;
2742 endpat = p; 2742 endpat = p;
2743 2743
2744 if (copy) { 2744 if (copy) {
2745 char *dst; 2745 char *dst;
2746 const char *src; 2746 const char *src;
2747 2747
2748 /* Compress the \:'s out of the pattern. */ 2748 /* Compress the \:'s out of the pattern. */
2749 pattern = bmake_malloc((size_t)(endpat - (mod + 1)) + 1); 2749 pattern = bmake_malloc((size_t)(endpat - (mod + 1)) + 1);
2750 dst = pattern; 2750 dst = pattern;
2751 src = mod + 1; 2751 src = mod + 1;
2752 for (; src < endpat; src++, dst++) { 2752 for (; src < endpat; src++, dst++) {
2753 if (src[0] == '\\' && src + 1 < endpat && 2753 if (src[0] == '\\' && src + 1 < endpat &&
2754 /* XXX: st->startc is missing here; see above */ 2754 /* XXX: st->startc is missing here; see above */
2755 (src[1] == ':' || src[1] == st->endc)) 2755 (src[1] == ':' || src[1] == st->endc))
2756 src++; 2756 src++;
2757 *dst = *src; 2757 *dst = *src;
2758 } 2758 }
2759 *dst = '\0'; 2759 *dst = '\0';
2760 } else { 2760 } else {
2761 pattern = bmake_strsedup(mod + 1, endpat); 2761 pattern = bmake_strsedup(mod + 1, endpat);
2762 } 2762 }
2763 2763
2764 if (needSubst) { 2764 if (needSubst) {
2765 char *old_pattern = pattern; 2765 char *old_pattern = pattern;
2766 (void)Var_Subst(pattern, expr->scope, expr->eflags, &pattern); 2766 (void)Var_Subst(pattern, expr->scope, expr->eflags, &pattern);
2767 /* TODO: handle errors */ 2767 /* TODO: handle errors */
2768 free(old_pattern); 2768 free(old_pattern);
2769 } 2769 }
2770 2770
2771 DEBUG3(VAR, "Pattern[%s] for [%s] is [%s]\n", 2771 DEBUG3(VAR, "Pattern[%s] for [%s] is [%s]\n",
2772 expr->var->name.str, expr->value.str, pattern); 2772 expr->var->name.str, expr->value.str, pattern);
2773 2773
2774 *out_pattern = pattern; 2774 *out_pattern = pattern;
2775} 2775}
2776 2776
2777/* :Mpattern or :Npattern */ 2777/* :Mpattern or :Npattern */
2778static ApplyModifierResult 2778static ApplyModifierResult
2779ApplyModifier_Match(const char **pp, ApplyModifiersState *st) 2779ApplyModifier_Match(const char **pp, ApplyModifiersState *st)
2780{ 2780{
2781 const char *mod = *pp; 2781 const char *mod = *pp;
2782 char *pattern; 2782 char *pattern;
2783 ModifyWordProc modifyWord; 2783 ModifyWordProc modifyWord;
2784 2784
2785 ParseModifier_Match(pp, st, &pattern); 2785 ParseModifier_Match(pp, st, &pattern);
2786 2786
2787 modifyWord = mod[0] == 'M' ? ModifyWord_Match : ModifyWord_NoMatch; 2787 modifyWord = mod[0] == 'M' ? ModifyWord_Match : ModifyWord_NoMatch;
2788 ModifyWords(st, modifyWord, pattern, st->oneBigWord); 2788 ModifyWords(st, modifyWord, pattern, st->oneBigWord);
2789 free(pattern); 2789 free(pattern);
2790 return AMR_OK; 2790 return AMR_OK;
2791} 2791}
2792 2792
2793/* :S,from,to, */ 2793/* :S,from,to, */
2794static ApplyModifierResult 2794static ApplyModifierResult
2795ApplyModifier_Subst(const char **pp, ApplyModifiersState *st) 2795ApplyModifier_Subst(const char **pp, ApplyModifiersState *st)
2796{ 2796{
2797 struct ModifyWord_SubstArgs args; 2797 struct ModifyWord_SubstArgs args;
2798 char *lhs, *rhs; 2798 char *lhs, *rhs;
2799 Boolean oneBigWord; 2799 Boolean oneBigWord;
2800 VarParseResult res; 2800 VarParseResult res;
2801 2801
2802 char delim = (*pp)[1]; 2802 char delim = (*pp)[1];
2803 if (delim == '\0') { 2803 if (delim == '\0') {
2804 Error("Missing delimiter for modifier ':S'"); 2804 Error("Missing delimiter for modifier ':S'");
2805 (*pp)++; 2805 (*pp)++;
2806 return AMR_CLEANUP; 2806 return AMR_CLEANUP;
2807 } 2807 }
2808 2808
2809 *pp += 2; 2809 *pp += 2;
2810 2810
2811 args.pflags = (VarPatternFlags){ FALSE, FALSE, FALSE, FALSE }; 2811 args.pflags = (VarPatternFlags){ FALSE, FALSE, FALSE, FALSE };
2812 args.matched = FALSE; 2812 args.matched = FALSE;
2813 2813
2814 if (**pp == '^') { 2814 if (**pp == '^') {
2815 args.pflags.anchorStart = TRUE; 2815 args.pflags.anchorStart = TRUE;
2816 (*pp)++; 2816 (*pp)++;
2817 } 2817 }
2818 2818
2819 res = ParseModifierPartSubst(pp, delim, st->expr->eflags, st, &lhs, 2819 res = ParseModifierPartSubst(pp, delim, st->expr->eflags, st, &lhs,
2820 &args.lhsLen, &args.pflags, NULL); 2820 &args.lhsLen, &args.pflags, NULL);
2821 if (res != VPR_OK) 2821 if (res != VPR_OK)
2822 return AMR_CLEANUP; 2822 return AMR_CLEANUP;
2823 args.lhs = lhs; 2823 args.lhs = lhs;
2824 2824
2825 res = ParseModifierPartSubst(pp, delim, st->expr->eflags, st, &rhs, 2825 res = ParseModifierPartSubst(pp, delim, st->expr->eflags, st, &rhs,
2826 &args.rhsLen, NULL, &args); 2826 &args.rhsLen, NULL, &args);
2827 if (res != VPR_OK) 2827 if (res != VPR_OK)
2828 return AMR_CLEANUP; 2828 return AMR_CLEANUP;
2829 args.rhs = rhs; 2829 args.rhs = rhs;
2830 2830
2831 oneBigWord = st->oneBigWord; 2831 oneBigWord = st->oneBigWord;
2832 for (;; (*pp)++) { 2832 for (;; (*pp)++) {
2833 if (**pp == 'g') 2833 if (**pp == 'g')
2834 args.pflags.subGlobal = TRUE; 2834 args.pflags.subGlobal = TRUE;
2835 else if (**pp == '1') 2835 else if (**pp == '1')
2836 args.pflags.subOnce = TRUE; 2836 args.pflags.subOnce = TRUE;
2837 else if (**pp == 'W') 2837 else if (**pp == 'W')
2838 oneBigWord = TRUE; 2838 oneBigWord = TRUE;
2839 else 2839 else
2840 break; 2840 break;
2841 } 2841 }
2842 2842
2843 ModifyWords(st, ModifyWord_Subst, &args, oneBigWord); 2843 ModifyWords(st, ModifyWord_Subst, &args, oneBigWord);
2844 2844
2845 free(lhs); 2845 free(lhs);
2846 free(rhs); 2846 free(rhs);
2847 return AMR_OK; 2847 return AMR_OK;
2848} 2848}
2849 2849
2850#ifndef NO_REGEX 2850#ifndef NO_REGEX
2851 2851
2852/* :C,from,to, */ 2852/* :C,from,to, */
2853static ApplyModifierResult 2853static ApplyModifierResult
2854ApplyModifier_Regex(const char **pp, ApplyModifiersState *st) 2854ApplyModifier_Regex(const char **pp, ApplyModifiersState *st)
2855{ 2855{
2856 char *re; 2856 char *re;
2857 struct ModifyWord_SubstRegexArgs args; 2857 struct ModifyWord_SubstRegexArgs args;
2858 Boolean oneBigWord; 2858 Boolean oneBigWord;
2859 int error; 2859 int error;
2860 VarParseResult res; 2860 VarParseResult res;
2861 2861
2862 char delim = (*pp)[1]; 2862 char delim = (*pp)[1];
2863 if (delim == '\0') { 2863 if (delim == '\0') {
2864 Error("Missing delimiter for :C modifier"); 2864 Error("Missing delimiter for :C modifier");
2865 (*pp)++; 2865 (*pp)++;
2866 return AMR_CLEANUP; 2866 return AMR_CLEANUP;
2867 } 2867 }
2868 2868
2869 *pp += 2; 2869 *pp += 2;
2870 2870
2871 res = ParseModifierPart(pp, delim, st->expr->eflags, st, &re); 2871 res = ParseModifierPart(pp, delim, st->expr->eflags, st, &re);
2872 if (res != VPR_OK) 2872 if (res != VPR_OK)
2873 return AMR_CLEANUP; 2873 return AMR_CLEANUP;
2874 2874
2875 res = ParseModifierPart(pp, delim, st->expr->eflags, st, &args.replace); 2875 res = ParseModifierPart(pp, delim, st->expr->eflags, st, &args.replace);
2876 if (args.replace == NULL) { 2876 if (args.replace == NULL) {
2877 free(re); 2877 free(re);
2878 return AMR_CLEANUP; 2878 return AMR_CLEANUP;
2879 } 2879 }
2880 2880
2881 args.pflags = (VarPatternFlags){ FALSE, FALSE, FALSE, FALSE }; 2881 args.pflags = (VarPatternFlags){ FALSE, FALSE, FALSE, FALSE };
2882 args.matched = FALSE; 2882 args.matched = FALSE;
2883 oneBigWord = st->oneBigWord; 2883 oneBigWord = st->oneBigWord;
2884 for (;; (*pp)++) { 2884 for (;; (*pp)++) {
2885 if (**pp == 'g') 2885 if (**pp == 'g')
2886 args.pflags.subGlobal = TRUE; 2886 args.pflags.subGlobal = TRUE;
2887 else if (**pp == '1') 2887 else if (**pp == '1')
2888 args.pflags.subOnce = TRUE; 2888 args.pflags.subOnce = TRUE;
2889 else if (**pp == 'W') 2889 else if (**pp == 'W')
2890 oneBigWord = TRUE; 2890 oneBigWord = TRUE;
2891 else 2891 else
2892 break; 2892 break;
2893 } 2893 }
2894 2894
 2895 if (!(st->expr->eflags & VARE_WANTRES)) {
 2896 free(args.replace);
 2897 free(re);
 2898 return AMR_OK;
 2899 }
 2900
2895 error = regcomp(&args.re, re, REG_EXTENDED); 2901 error = regcomp(&args.re, re, REG_EXTENDED);
2896 free(re); 2902 free(re);
2897 if (error != 0) { 2903 if (error != 0) {
2898 VarREError(error, &args.re, "Regex compilation error"); 2904 VarREError(error, &args.re, "Regex compilation error");
2899 free(args.replace); 2905 free(args.replace);
2900 return AMR_CLEANUP; 2906 return AMR_CLEANUP;
2901 } 2907 }
2902 2908
2903 args.nsub = args.re.re_nsub + 1; 2909 args.nsub = args.re.re_nsub + 1;
2904 if (args.nsub > 10) 2910 if (args.nsub > 10)
2905 args.nsub = 10; 2911 args.nsub = 10;
2906 2912
2907 ModifyWords(st, ModifyWord_SubstRegex, &args, oneBigWord); 2913 ModifyWords(st, ModifyWord_SubstRegex, &args, oneBigWord);
2908 2914
2909 regfree(&args.re); 2915 regfree(&args.re);
2910 free(args.replace); 2916 free(args.replace);
2911 return AMR_OK; 2917 return AMR_OK;
2912} 2918}
2913 2919
2914#endif 2920#endif
2915 2921
2916/* :Q, :q */ 2922/* :Q, :q */
2917static ApplyModifierResult 2923static ApplyModifierResult
2918ApplyModifier_Quote(const char **pp, ApplyModifiersState *st) 2924ApplyModifier_Quote(const char **pp, ApplyModifiersState *st)
2919{ 2925{
2920 if ((*pp)[1] == st->endc || (*pp)[1] == ':') { 2926 if ((*pp)[1] == st->endc || (*pp)[1] == ':') {
2921 Expr_SetValueOwn(st->expr, 2927 Expr_SetValueOwn(st->expr,
2922 VarQuote(st->expr->value.str, **pp == 'q')); 2928 VarQuote(st->expr->value.str, **pp == 'q'));
2923 (*pp)++; 2929 (*pp)++;
2924 return AMR_OK; 2930 return AMR_OK;
2925 } else 2931 } else
2926 return AMR_UNKNOWN; 2932 return AMR_UNKNOWN;
2927} 2933}
2928 2934
2929/*ARGSUSED*/ 2935/*ARGSUSED*/
2930static void 2936static void
2931ModifyWord_Copy(const char *word, SepBuf *buf, void *data MAKE_ATTR_UNUSED) 2937ModifyWord_Copy(const char *word, SepBuf *buf, void *data MAKE_ATTR_UNUSED)
2932{ 2938{
2933 SepBuf_AddStr(buf, word); 2939 SepBuf_AddStr(buf, word);
2934} 2940}
2935 2941
2936/* :ts<separator> */ 2942/* :ts<separator> */
2937static ApplyModifierResult 2943static ApplyModifierResult
2938ApplyModifier_ToSep(const char **pp, ApplyModifiersState *st) 2944ApplyModifier_ToSep(const char **pp, ApplyModifiersState *st)
2939{ 2945{
2940 const char *sep = *pp + 2; 2946 const char *sep = *pp + 2;
2941 2947
2942 /* ":ts<any><endc>" or ":ts<any>:" */ 2948 /* ":ts<any><endc>" or ":ts<any>:" */
2943 if (sep[0] != st->endc && (sep[1] == st->endc || sep[1] == ':')) { 2949 if (sep[0] != st->endc && (sep[1] == st->endc || sep[1] == ':')) {
2944 st->sep = sep[0]; 2950 st->sep = sep[0];
2945 *pp = sep + 1; 2951 *pp = sep + 1;
2946 goto ok; 2952 goto ok;
2947 } 2953 }
2948 2954
2949 /* ":ts<endc>" or ":ts:" */ 2955 /* ":ts<endc>" or ":ts:" */
2950 if (sep[0] == st->endc || sep[0] == ':') { 2956 if (sep[0] == st->endc || sep[0] == ':') {
2951 st->sep = '\0'; /* no separator */ 2957 st->sep = '\0'; /* no separator */
2952 *pp = sep; 2958 *pp = sep;
2953 goto ok; 2959 goto ok;
2954 } 2960 }
2955 2961
2956 /* ":ts<unrecognised><unrecognised>". */ 2962 /* ":ts<unrecognised><unrecognised>". */
2957 if (sep[0] != '\\') { 2963 if (sep[0] != '\\') {
2958 (*pp)++; /* just for backwards compatibility */ 2964 (*pp)++; /* just for backwards compatibility */
2959 return AMR_BAD; 2965 return AMR_BAD;
2960 } 2966 }
2961 2967
2962 /* ":ts\n" */ 2968 /* ":ts\n" */
2963 if (sep[1] == 'n') { 2969 if (sep[1] == 'n') {
2964 st->sep = '\n'; 2970 st->sep = '\n';
2965 *pp = sep + 2; 2971 *pp = sep + 2;
2966 goto ok; 2972 goto ok;
2967 } 2973 }
2968 2974
2969 /* ":ts\t" */ 2975 /* ":ts\t" */
2970 if (sep[1] == 't') { 2976 if (sep[1] == 't') {
2971 st->sep = '\t'; 2977 st->sep = '\t';
2972 *pp = sep + 2; 2978 *pp = sep + 2;
2973 goto ok; 2979 goto ok;
2974 } 2980 }
2975 2981
2976 /* ":ts\x40" or ":ts\100" */ 2982 /* ":ts\x40" or ":ts\100" */
2977 { 2983 {
2978 const char *p = sep + 1; 2984 const char *p = sep + 1;
2979 int base = 8; /* assume octal */ 2985 int base = 8; /* assume octal */
2980 2986
2981 if (sep[1] == 'x') { 2987 if (sep[1] == 'x') {
2982 base = 16; 2988 base = 16;
2983 p++; 2989 p++;
2984 } else if (!ch_isdigit(sep[1])) { 2990 } else if (!ch_isdigit(sep[1])) {
2985 (*pp)++; /* just for backwards compatibility */ 2991 (*pp)++; /* just for backwards compatibility */
2986 return AMR_BAD; /* ":ts<backslash><unrecognised>". */ 2992 return AMR_BAD; /* ":ts<backslash><unrecognised>". */
2987 } 2993 }
2988 2994
2989 if (!TryParseChar(&p, base, &st->sep)) { 2995 if (!TryParseChar(&p, base, &st->sep)) {
2990 Parse_Error(PARSE_FATAL, 2996 Parse_Error(PARSE_FATAL,
2991 "Invalid character number: %s", p); 2997 "Invalid character number: %s", p);
2992 return AMR_CLEANUP; 2998 return AMR_CLEANUP;
2993 } 2999 }
2994 if (*p != ':' && *p != st->endc) { 3000 if (*p != ':' && *p != st->endc) {
2995 (*pp)++; /* just for backwards compatibility */ 3001 (*pp)++; /* just for backwards compatibility */
2996 return AMR_BAD; 3002 return AMR_BAD;
2997 } 3003 }
2998 3004
2999 *pp = p; 3005 *pp = p;
3000 } 3006 }
3001 3007
3002ok: 3008ok:
3003 ModifyWords(st, ModifyWord_Copy, NULL, st->oneBigWord); 3009 ModifyWords(st, ModifyWord_Copy, NULL, st->oneBigWord);
3004 return AMR_OK; 3010 return AMR_OK;
3005} 3011}
3006 3012
3007static char * 3013static char *
3008str_toupper(const char *str) 3014str_toupper(const char *str)
3009{ 3015{
3010 char *res; 3016 char *res;
3011 size_t i, len; 3017 size_t i, len;
3012 3018
3013 len = strlen(str); 3019 len = strlen(str);
3014 res = bmake_malloc(len + 1); 3020 res = bmake_malloc(len + 1);
3015 for (i = 0; i < len + 1; i++) 3021 for (i = 0; i < len + 1; i++)
3016 res[i] = ch_toupper(str[i]); 3022 res[i] = ch_toupper(str[i]);
3017 3023
3018 return res; 3024 return res;
3019} 3025}
3020 3026
3021static char * 3027static char *
3022str_tolower(const char *str) 3028str_tolower(const char *str)
3023{ 3029{
3024 char *res; 3030 char *res;
3025 size_t i, len; 3031 size_t i, len;
3026 3032
3027 len = strlen(str); 3033 len = strlen(str);
3028 res = bmake_malloc(len + 1); 3034 res = bmake_malloc(len + 1);
3029 for (i = 0; i < len + 1; i++) 3035 for (i = 0; i < len + 1; i++)
3030 res[i] = ch_tolower(str[i]); 3036 res[i] = ch_tolower(str[i]);
3031 3037
3032 return res; 3038 return res;
3033} 3039}
3034 3040
3035/* :tA, :tu, :tl, :ts<separator>, etc. */ 3041/* :tA, :tu, :tl, :ts<separator>, etc. */
3036static ApplyModifierResult 3042static ApplyModifierResult
3037ApplyModifier_To(const char **pp, ApplyModifiersState *st) 3043ApplyModifier_To(const char **pp, ApplyModifiersState *st)
3038{ 3044{
3039 Expr *expr = st->expr; 3045 Expr *expr = st->expr;
3040 const char *mod = *pp; 3046 const char *mod = *pp;
3041 assert(mod[0] == 't'); 3047 assert(mod[0] == 't');
3042 3048
3043 if (mod[1] == st->endc || mod[1] == ':' || mod[1] == '\0') { 3049 if (mod[1] == st->endc || mod[1] == ':' || mod[1] == '\0') {
3044 *pp = mod + 1; 3050 *pp = mod + 1;
3045 return AMR_BAD; /* Found ":t<endc>" or ":t:". */ 3051 return AMR_BAD; /* Found ":t<endc>" or ":t:". */
3046 } 3052 }
3047 3053
3048 if (mod[1] == 's') 3054 if (mod[1] == 's')
3049 return ApplyModifier_ToSep(pp, st); 3055 return ApplyModifier_ToSep(pp, st);
3050 3056
3051 if (mod[2] != st->endc && mod[2] != ':') { /* :t<unrecognized> */ 3057 if (mod[2] != st->endc && mod[2] != ':') { /* :t<unrecognized> */
3052 *pp = mod + 1; 3058 *pp = mod + 1;
3053 return AMR_BAD; 3059 return AMR_BAD;
3054 } 3060 }
3055 3061
3056 if (mod[1] == 'A') { /* :tA */ 3062 if (mod[1] == 'A') { /* :tA */
3057 ModifyWords(st, ModifyWord_Realpath, NULL, st->oneBigWord); 3063 ModifyWords(st, ModifyWord_Realpath, NULL, st->oneBigWord);
3058 *pp = mod + 2; 3064 *pp = mod + 2;
3059 return AMR_OK; 3065 return AMR_OK;
3060 } 3066 }
3061 3067
3062 if (mod[1] == 'u') { /* :tu */ 3068 if (mod[1] == 'u') { /* :tu */
3063 Expr_SetValueOwn(expr, str_toupper(expr->value.str)); 3069 Expr_SetValueOwn(expr, str_toupper(expr->value.str));
3064 *pp = mod + 2; 3070 *pp = mod + 2;
3065 return AMR_OK; 3071 return AMR_OK;
3066 } 3072 }
3067 3073
3068 if (mod[1] == 'l') { /* :tl */ 3074 if (mod[1] == 'l') { /* :tl */
3069 Expr_SetValueOwn(expr, str_tolower(expr->value.str)); 3075 Expr_SetValueOwn(expr, str_tolower(expr->value.str));
3070 *pp = mod + 2; 3076 *pp = mod + 2;
3071 return AMR_OK; 3077 return AMR_OK;
3072 } 3078 }
3073 3079
3074 if (mod[1] == 'W' || mod[1] == 'w') { /* :tW, :tw */ 3080 if (mod[1] == 'W' || mod[1] == 'w') { /* :tW, :tw */
3075 st->oneBigWord = mod[1] == 'W'; 3081 st->oneBigWord = mod[1] == 'W';
3076 *pp = mod + 2; 3082 *pp = mod + 2;
3077 return AMR_OK; 3083 return AMR_OK;
3078 } 3084 }
3079 3085
3080 /* Found ":t<unrecognised>:" or ":t<unrecognised><endc>". */ 3086 /* Found ":t<unrecognised>:" or ":t<unrecognised><endc>". */
3081 *pp = mod + 1; 3087 *pp = mod + 1;
3082 return AMR_BAD; 3088 return AMR_BAD;
3083} 3089}
3084 3090
3085/* :[#], :[1], :[-1..1], etc. */ 3091/* :[#], :[1], :[-1..1], etc. */
3086static ApplyModifierResult 3092static ApplyModifierResult
3087ApplyModifier_Words(const char **pp, ApplyModifiersState *st) 3093ApplyModifier_Words(const char **pp, ApplyModifiersState *st)
3088{ 3094{
3089 Expr *expr = st->expr; 3095 Expr *expr = st->expr;
3090 char *estr; 3096 char *estr;
3091 int first, last; 3097 int first, last;
3092 VarParseResult res; 3098 VarParseResult res;
3093 const char *p; 3099 const char *p;
3094 3100
3095 (*pp)++; /* skip the '[' */ 3101 (*pp)++; /* skip the '[' */
3096 res = ParseModifierPart(pp, ']', expr->eflags, st, &estr); 3102 res = ParseModifierPart(pp, ']', expr->eflags, st, &estr);
3097 if (res != VPR_OK) 3103 if (res != VPR_OK)
3098 return AMR_CLEANUP; 3104 return AMR_CLEANUP;
3099 3105
3100 if (**pp != ':' && **pp != st->endc) 3106 if (**pp != ':' && **pp != st->endc)
3101 goto bad_modifier; /* Found junk after ']' */ 3107 goto bad_modifier; /* Found junk after ']' */
3102 3108
3103 if (estr[0] == '\0') 3109 if (estr[0] == '\0')
3104 goto bad_modifier; /* Found ":[]". */ 3110 goto bad_modifier; /* Found ":[]". */
3105 3111
3106 if (estr[0] == '#' && estr[1] == '\0') { /* Found ":[#]" */ 3112 if (estr[0] == '#' && estr[1] == '\0') { /* Found ":[#]" */
3107 if (st->oneBigWord) { 3113 if (st->oneBigWord) {
3108 Expr_SetValueRefer(expr, "1"); 3114 Expr_SetValueRefer(expr, "1");
3109 } else { 3115 } else {
3110 Buffer buf; 3116 Buffer buf;
3111 3117
3112 Words words = Str_Words(expr->value.str, FALSE); 3118 Words words = Str_Words(expr->value.str, FALSE);
3113 size_t ac = words.len; 3119 size_t ac = words.len;
3114 Words_Free(words); 3120 Words_Free(words);
3115 3121
3116 /* 3 digits + '\0' is usually enough */ 3122 /* 3 digits + '\0' is usually enough */
3117 Buf_InitSize(&buf, 4); 3123 Buf_InitSize(&buf, 4);
3118 Buf_AddInt(&buf, (int)ac); 3124 Buf_AddInt(&buf, (int)ac);
3119 Expr_SetValueOwn(expr, Buf_DoneData(&buf)); 3125 Expr_SetValueOwn(expr, Buf_DoneData(&buf));
3120 } 3126 }
3121 goto ok; 3127 goto ok;
3122 } 3128 }
3123 3129
3124 if (estr[0] == '*' && estr[1] == '\0') { /* Found ":[*]" */ 3130 if (estr[0] == '*' && estr[1] == '\0') { /* Found ":[*]" */
3125 st->oneBigWord = TRUE; 3131 st->oneBigWord = TRUE;
3126 goto ok; 3132 goto ok;
3127 } 3133 }
3128 3134
3129 if (estr[0] == '@' && estr[1] == '\0') { /* Found ":[@]" */ 3135 if (estr[0] == '@' && estr[1] == '\0') { /* Found ":[@]" */
3130 st->oneBigWord = FALSE; 3136 st->oneBigWord = FALSE;
3131 goto ok; 3137 goto ok;
3132 } 3138 }
3133 3139
3134 /* 3140 /*
3135 * We expect estr to contain a single integer for :[N], or two 3141 * We expect estr to contain a single integer for :[N], or two
3136 * integers separated by ".." for :[start..end]. 3142 * integers separated by ".." for :[start..end].
3137 */ 3143 */
3138 p = estr; 3144 p = estr;
3139 if (!TryParseIntBase0(&p, &first)) 3145 if (!TryParseIntBase0(&p, &first))
3140 goto bad_modifier; /* Found junk instead of a number */ 3146 goto bad_modifier; /* Found junk instead of a number */
3141 3147
3142 if (p[0] == '\0') { /* Found only one integer in :[N] */ 3148 if (p[0] == '\0') { /* Found only one integer in :[N] */
3143 last = first; 3149 last = first;
3144 } else if (p[0] == '.' && p[1] == '.' && p[2] != '\0') { 3150 } else if (p[0] == '.' && p[1] == '.' && p[2] != '\0') {
3145 /* Expecting another integer after ".." */ 3151 /* Expecting another integer after ".." */
3146 p += 2; 3152 p += 2;
3147 if (!TryParseIntBase0(&p, &last) || *p != '\0') 3153 if (!TryParseIntBase0(&p, &last) || *p != '\0')
3148 goto bad_modifier; /* Found junk after ".." */ 3154 goto bad_modifier; /* Found junk after ".." */
3149 } else 3155 } else
3150 goto bad_modifier; /* Found junk instead of ".." */ 3156 goto bad_modifier; /* Found junk instead of ".." */
3151 3157
3152 /* 3158 /*
3153 * Now first and last are properly filled in, but we still have to 3159 * Now first and last are properly filled in, but we still have to
3154 * check for 0 as a special case. 3160 * check for 0 as a special case.
3155 */ 3161 */
3156 if (first == 0 && last == 0) { 3162 if (first == 0 && last == 0) {
3157 /* ":[0]" or perhaps ":[0..0]" */ 3163 /* ":[0]" or perhaps ":[0..0]" */
3158 st->oneBigWord = TRUE; 3164 st->oneBigWord = TRUE;
3159 goto ok; 3165 goto ok;
3160 } 3166 }
3161 3167
3162 /* ":[0..N]" or ":[N..0]" */ 3168 /* ":[0..N]" or ":[N..0]" */
3163 if (first == 0 || last == 0) 3169 if (first == 0 || last == 0)
3164 goto bad_modifier; 3170 goto bad_modifier;
3165 3171
3166 /* Normal case: select the words described by first and last. */ 3172 /* Normal case: select the words described by first and last. */
3167 Expr_SetValueOwn(expr, 3173 Expr_SetValueOwn(expr,
3168 VarSelectWords(expr->value.str, first, last, 3174 VarSelectWords(expr->value.str, first, last,
3169 st->sep, st->oneBigWord)); 3175 st->sep, st->oneBigWord));
3170 3176
3171ok: 3177ok:
3172 free(estr); 3178 free(estr);
3173 return AMR_OK; 3179 return AMR_OK;
3174 3180
3175bad_modifier: 3181bad_modifier:
3176 free(estr); 3182 free(estr);
3177 return AMR_BAD; 3183 return AMR_BAD;
3178} 3184}
3179 3185
3180static int 3186static int
3181str_cmp_asc(const void *a, const void *b) 3187str_cmp_asc(const void *a, const void *b)
3182{ 3188{
3183 return strcmp(*(const char *const *)a, *(const char *const *)b); 3189 return strcmp(*(const char *const *)a, *(const char *const *)b);
3184} 3190}
3185 3191
3186static int 3192static int
3187str_cmp_desc(const void *a, const void *b) 3193str_cmp_desc(const void *a, const void *b)
3188{ 3194{
3189 return strcmp(*(const char *const *)b, *(const char *const *)a); 3195 return strcmp(*(const char *const *)b, *(const char *const *)a);
3190} 3196}
3191 3197
3192static void 3198static void
3193ShuffleStrings(char **strs, size_t n) 3199ShuffleStrings(char **strs, size_t n)
3194{ 3200{
3195 size_t i; 3201 size_t i;
3196 3202
3197 for (i = n - 1; i > 0; i--) { 3203 for (i = n - 1; i > 0; i--) {
3198 size_t rndidx = (size_t)random() % (i + 1); 3204 size_t rndidx = (size_t)random() % (i + 1);
3199 char *t = strs[i]; 3205 char *t = strs[i];
3200 strs[i] = strs[rndidx]; 3206 strs[i] = strs[rndidx];
3201 strs[rndidx] = t; 3207 strs[rndidx] = t;
3202 } 3208 }
3203} 3209}
3204 3210
3205/* :O (order ascending) or :Or (order descending) or :Ox (shuffle) */ 3211/* :O (order ascending) or :Or (order descending) or :Ox (shuffle) */
3206static ApplyModifierResult 3212static ApplyModifierResult
3207ApplyModifier_Order(const char **pp, ApplyModifiersState *st) 3213ApplyModifier_Order(const char **pp, ApplyModifiersState *st)
3208{ 3214{
3209 const char *mod = (*pp)++; /* skip past the 'O' in any case */ 3215 const char *mod = (*pp)++; /* skip past the 'O' in any case */
3210 3216
3211 Words words = Str_Words(st->expr->value.str, FALSE); 3217 Words words = Str_Words(st->expr->value.str, FALSE);
3212 3218
3213 if (mod[1] == st->endc || mod[1] == ':') { 3219 if (mod[1] == st->endc || mod[1] == ':') {
3214 /* :O sorts ascending */ 3220 /* :O sorts ascending */
3215 qsort(words.words, words.len, sizeof words.words[0], 3221 qsort(words.words, words.len, sizeof words.words[0],
3216 str_cmp_asc); 3222 str_cmp_asc);
3217 3223
3218 } else if ((mod[1] == 'r' || mod[1] == 'x') && 3224 } else if ((mod[1] == 'r' || mod[1] == 'x') &&
3219 (mod[2] == st->endc || mod[2] == ':')) { 3225 (mod[2] == st->endc || mod[2] == ':')) {
3220 (*pp)++; 3226 (*pp)++;
3221 3227
3222 if (mod[1] == 'r') { /* :Or sorts descending */ 3228 if (mod[1] == 'r') { /* :Or sorts descending */
3223 qsort(words.words, words.len, sizeof words.words[0], 3229 qsort(words.words, words.len, sizeof words.words[0],
3224 str_cmp_desc); 3230 str_cmp_desc);
3225 } else 3231 } else
3226 ShuffleStrings(words.words, words.len); 3232 ShuffleStrings(words.words, words.len);
3227 } else { 3233 } else {
3228 Words_Free(words); 3234 Words_Free(words);
3229 return AMR_BAD; 3235 return AMR_BAD;
3230 } 3236 }
3231 3237
3232 Expr_SetValueOwn(st->expr, Words_JoinFree(words)); 3238 Expr_SetValueOwn(st->expr, Words_JoinFree(words));
3233 return AMR_OK; 3239 return AMR_OK;
3234} 3240}
3235 3241
3236/* :? then : else */ 3242/* :? then : else */
3237static ApplyModifierResult 3243static ApplyModifierResult
3238ApplyModifier_IfElse(const char **pp, ApplyModifiersState *st) 3244ApplyModifier_IfElse(const char **pp, ApplyModifiersState *st)
3239{ 3245{
3240 Expr *expr = st->expr; 3246 Expr *expr = st->expr;
3241 char *then_expr, *else_expr; 3247 char *then_expr, *else_expr;
3242 VarParseResult res; 3248 VarParseResult res;
3243 3249
3244 Boolean value = FALSE; 3250 Boolean value = FALSE;
3245 VarEvalFlags then_eflags = VARE_NONE; 3251 VarEvalFlags then_eflags = VARE_NONE;
3246 VarEvalFlags else_eflags = VARE_NONE; 3252 VarEvalFlags else_eflags = VARE_NONE;
3247 3253
3248 int cond_rc = COND_PARSE; /* anything other than COND_INVALID */ 3254 int cond_rc = COND_PARSE; /* anything other than COND_INVALID */
3249 if (expr->eflags & VARE_WANTRES) { 3255 if (expr->eflags & VARE_WANTRES) {
3250 cond_rc = Cond_EvalCondition(expr->var->name.str, &value); 3256 cond_rc = Cond_EvalCondition(expr->var->name.str, &value);
3251 if (cond_rc != COND_INVALID && value) 3257 if (cond_rc != COND_INVALID && value)
3252 then_eflags = expr->eflags; 3258 then_eflags = expr->eflags;
3253 if (cond_rc != COND_INVALID && !value) 3259 if (cond_rc != COND_INVALID && !value)
3254 else_eflags = expr->eflags; 3260 else_eflags = expr->eflags;
3255 } 3261 }
3256 3262
3257 (*pp)++; /* skip past the '?' */ 3263 (*pp)++; /* skip past the '?' */
3258 res = ParseModifierPart(pp, ':', then_eflags, st, &then_expr); 3264 res = ParseModifierPart(pp, ':', then_eflags, st, &then_expr);
3259 if (res != VPR_OK) 3265 if (res != VPR_OK)
3260 return AMR_CLEANUP; 3266 return AMR_CLEANUP;
3261 3267
3262 res = ParseModifierPart(pp, st->endc, else_eflags, st, &else_expr); 3268 res = ParseModifierPart(pp, st->endc, else_eflags, st, &else_expr);
3263 if (res != VPR_OK) 3269 if (res != VPR_OK)
3264 return AMR_CLEANUP; 3270 return AMR_CLEANUP;
3265 3271
3266 (*pp)--; /* Go back to the st->endc. */ 3272 (*pp)--; /* Go back to the st->endc. */
3267 3273
3268 if (cond_rc == COND_INVALID) { 3274 if (cond_rc == COND_INVALID) {
3269 Error("Bad conditional expression `%s' in %s?%s:%s", 3275 Error("Bad conditional expression `%s' in %s?%s:%s",
3270 expr->var->name.str, expr->var->name.str, 3276 expr->var->name.str, expr->var->name.str,
3271 then_expr, else_expr); 3277 then_expr, else_expr);
3272 return AMR_CLEANUP; 3278 return AMR_CLEANUP;
3273 } 3279 }
3274 3280
3275 if (value) { 3281 if (value) {
3276 Expr_SetValueOwn(expr, then_expr); 3282 Expr_SetValueOwn(expr, then_expr);
3277 free(else_expr); 3283 free(else_expr);
3278 } else { 3284 } else {
3279 Expr_SetValueOwn(expr, else_expr); 3285 Expr_SetValueOwn(expr, else_expr);
3280 free(then_expr); 3286 free(then_expr);
3281 } 3287 }
3282 Expr_Define(expr); 3288 Expr_Define(expr);
3283 return AMR_OK; 3289 return AMR_OK;
3284} 3290}
3285 3291
3286/* 3292/*
3287 * The ::= modifiers are special in that they do not read the variable value 3293 * The ::= modifiers are special in that they do not read the variable value
3288 * but instead assign to that variable. They always expand to an empty 3294 * but instead assign to that variable. They always expand to an empty
3289 * string. 3295 * string.
3290 * 3296 *
3291 * Their main purpose is in supporting .for loops that generate shell commands 3297 * Their main purpose is in supporting .for loops that generate shell commands
3292 * since an ordinary variable assignment at that point would terminate the 3298 * since an ordinary variable assignment at that point would terminate the
3293 * dependency group for these targets. For example: 3299 * dependency group for these targets. For example:
3294 * 3300 *
3295 * list-targets: .USE 3301 * list-targets: .USE
3296 * .for i in ${.TARGET} ${.TARGET:R}.gz 3302 * .for i in ${.TARGET} ${.TARGET:R}.gz
3297 * @${t::=$i} 3303 * @${t::=$i}
3298 * @echo 'The target is ${t:T}.' 3304 * @echo 'The target is ${t:T}.'
3299 * .endfor 3305 * .endfor
3300 * 3306 *
3301 * ::=<str> Assigns <str> as the new value of variable. 3307 * ::=<str> Assigns <str> as the new value of variable.
3302 * ::?=<str> Assigns <str> as value of variable if 3308 * ::?=<str> Assigns <str> as value of variable if
3303 * it was not already set. 3309 * it was not already set.
3304 * ::+=<str> Appends <str> to variable. 3310 * ::+=<str> Appends <str> to variable.
3305 * ::!=<cmd> Assigns output of <cmd> as the new value of 3311 * ::!=<cmd> Assigns output of <cmd> as the new value of
3306 * variable. 3312 * variable.
3307 */ 3313 */
3308static ApplyModifierResult 3314static ApplyModifierResult
3309ApplyModifier_Assign(const char **pp, ApplyModifiersState *st) 3315ApplyModifier_Assign(const char **pp, ApplyModifiersState *st)
3310{ 3316{
3311 Expr *expr = st->expr; 3317 Expr *expr = st->expr;
3312 GNode *scope; 3318 GNode *scope;
3313 char *val; 3319 char *val;
3314 VarParseResult res; 3320 VarParseResult res;
3315 3321
3316 const char *mod = *pp; 3322 const char *mod = *pp;
3317 const char *op = mod + 1; 3323 const char *op = mod + 1;
3318 3324
3319 if (op[0] == '=') 3325 if (op[0] == '=')
3320 goto ok; 3326 goto ok;
3321 if ((op[0] == '!' || op[0] == '+' || op[0] == '?') && op[1] == '=') 3327 if ((op[0] == '!' || op[0] == '+' || op[0] == '?') && op[1] == '=')
3322 goto ok; 3328 goto ok;
3323 return AMR_UNKNOWN; /* "::<unrecognised>" */ 3329 return AMR_UNKNOWN; /* "::<unrecognised>" */
3324ok: 3330ok:
3325 3331
3326 if (expr->var->name.str[0] == '\0') { 3332 if (expr->var->name.str[0] == '\0') {
3327 *pp = mod + 1; 3333 *pp = mod + 1;
3328 return AMR_BAD; 3334 return AMR_BAD;
3329 } 3335 }
3330 3336
3331 scope = expr->scope; /* scope where v belongs */ 3337 scope = expr->scope; /* scope where v belongs */
3332 if (expr->defined == DEF_REGULAR && expr->scope != SCOPE_GLOBAL) { 3338 if (expr->defined == DEF_REGULAR && expr->scope != SCOPE_GLOBAL) {
3333 Var *gv = VarFind(expr->var->name.str, expr->scope, FALSE); 3339 Var *gv = VarFind(expr->var->name.str, expr->scope, FALSE);
3334 if (gv == NULL) 3340 if (gv == NULL)
3335 scope = SCOPE_GLOBAL; 3341 scope = SCOPE_GLOBAL;
3336 else 3342 else
3337 VarFreeEnv(gv); 3343 VarFreeEnv(gv);
3338 } 3344 }
3339 3345
3340 switch (op[0]) { 3346 switch (op[0]) {
3341 case '+': 3347 case '+':
3342 case '?': 3348 case '?':
3343 case '!': 3349 case '!':
3344 *pp = mod + 3; 3350 *pp = mod + 3;
3345 break; 3351 break;
3346 default: 3352 default:
3347 *pp = mod + 2; 3353 *pp = mod + 2;
3348 break; 3354 break;
3349 } 3355 }
3350 3356
3351 res = ParseModifierPart(pp, st->endc, expr->eflags, st, &val); 3357 res = ParseModifierPart(pp, st->endc, expr->eflags, st, &val);
3352 if (res != VPR_OK) 3358 if (res != VPR_OK)
3353 return AMR_CLEANUP; 3359 return AMR_CLEANUP;
3354 3360
3355 (*pp)--; /* Go back to the st->endc. */ 3361 (*pp)--; /* Go back to the st->endc. */
3356 3362
3357 /* XXX: Expanding the variable name at this point sounds wrong. */ 3363 /* XXX: Expanding the variable name at this point sounds wrong. */
3358 if (expr->eflags & VARE_WANTRES) { 3364 if (expr->eflags & VARE_WANTRES) {
3359 switch (op[0]) { 3365 switch (op[0]) {
3360 case '+': 3366 case '+':
3361 Var_AppendExpand(scope, expr->var->name.str, val); 3367 Var_AppendExpand(scope, expr->var->name.str, val);
3362 break; 3368 break;
3363 case '!': { 3369 case '!': {
3364 const char *errfmt; 3370 const char *errfmt;
3365 char *cmd_output = Cmd_Exec(val, &errfmt); 3371 char *cmd_output = Cmd_Exec(val, &errfmt);
3366 if (errfmt != NULL) 3372 if (errfmt != NULL)
3367 Error(errfmt, val); 3373 Error(errfmt, val);
3368 else 3374 else
3369 Var_SetExpand(scope, 3375 Var_SetExpand(scope,
3370 expr->var->name.str, cmd_output); 3376 expr->var->name.str, cmd_output);
3371 free(cmd_output); 3377 free(cmd_output);
3372 break; 3378 break;
3373 } 3379 }
3374 case '?': 3380 case '?':
3375 if (expr->defined == DEF_REGULAR) 3381 if (expr->defined == DEF_REGULAR)
3376 break; 3382 break;
3377 /* FALLTHROUGH */ 3383 /* FALLTHROUGH */
3378 default: 3384 default:
3379 Var_SetExpand(scope, expr->var->name.str, val); 3385 Var_SetExpand(scope, expr->var->name.str, val);
3380 break; 3386 break;
3381 } 3387 }
3382 } 3388 }
3383 free(val); 3389 free(val);
3384 Expr_SetValueRefer(expr, ""); 3390 Expr_SetValueRefer(expr, "");
3385 return AMR_OK; 3391 return AMR_OK;
3386} 3392}
3387 3393
3388/* 3394/*
3389 * :_=... 3395 * :_=...
3390 * remember current value 3396 * remember current value
3391 */ 3397 */
3392static ApplyModifierResult 3398static ApplyModifierResult
3393ApplyModifier_Remember(const char **pp, ApplyModifiersState *st) 3399ApplyModifier_Remember(const char **pp, ApplyModifiersState *st)
3394{ 3400{
3395 Expr *expr = st->expr; 3401 Expr *expr = st->expr;
3396 const char *mod = *pp; 3402 const char *mod = *pp;
3397 if (!ModMatchEq(mod, "_", st->endc)) 3403 if (!ModMatchEq(mod, "_", st->endc))
3398 return AMR_UNKNOWN; 3404 return AMR_UNKNOWN;
3399 3405
3400 if (mod[1] == '=') { 3406 if (mod[1] == '=') {
3401 /* 3407 /*
3402 * XXX: This ad-hoc call to strcspn deviates from the usual 3408 * XXX: This ad-hoc call to strcspn deviates from the usual
3403 * behavior defined in ParseModifierPart. This creates an 3409 * behavior defined in ParseModifierPart. This creates an
3404 * unnecessary, undocumented inconsistency in make. 3410 * unnecessary, undocumented inconsistency in make.
3405 */ 3411 */
3406 size_t n = strcspn(mod + 2, ":)}"); 3412 size_t n = strcspn(mod + 2, ":)}");
3407 char *name = bmake_strldup(mod + 2, n); 3413 char *name = bmake_strldup(mod + 2, n);
3408 Var_SetExpand(expr->scope, name, expr->value.str); 3414 Var_SetExpand(expr->scope, name, expr->value.str);
3409 free(name); 3415 free(name);
3410 *pp = mod + 2 + n; 3416 *pp = mod + 2 + n;
3411 } else { 3417 } else {
3412 Var_Set(expr->scope, "_", expr->value.str); 3418 Var_Set(expr->scope, "_", expr->value.str);
3413 *pp = mod + 1; 3419 *pp = mod + 1;
3414 } 3420 }
3415 return AMR_OK; 3421 return AMR_OK;
3416} 3422}
3417 3423
3418/* 3424/*
3419 * Apply the given function to each word of the variable value, 3425 * Apply the given function to each word of the variable value,
3420 * for a single-letter modifier such as :H, :T. 3426 * for a single-letter modifier such as :H, :T.
3421 */ 3427 */
3422static ApplyModifierResult 3428static ApplyModifierResult
3423ApplyModifier_WordFunc(const char **pp, ApplyModifiersState *st, 3429ApplyModifier_WordFunc(const char **pp, ApplyModifiersState *st,
3424 ModifyWordProc modifyWord) 3430 ModifyWordProc modifyWord)
3425{ 3431{
3426 char delim = (*pp)[1]; 3432 char delim = (*pp)[1];
3427 if (delim != st->endc && delim != ':') 3433 if (delim != st->endc && delim != ':')
3428 return AMR_UNKNOWN; 3434 return AMR_UNKNOWN;
3429 3435
3430 ModifyWords(st, modifyWord, NULL, st->oneBigWord); 3436 ModifyWords(st, modifyWord, NULL, st->oneBigWord);
3431 (*pp)++; 3437 (*pp)++;
3432 return AMR_OK; 3438 return AMR_OK;
3433} 3439}
3434 3440
3435static ApplyModifierResult 3441static ApplyModifierResult
3436ApplyModifier_Unique(const char **pp, ApplyModifiersState *st) 3442ApplyModifier_Unique(const char **pp, ApplyModifiersState *st)
3437{ 3443{
3438 if ((*pp)[1] == st->endc || (*pp)[1] == ':') { 3444 if ((*pp)[1] == st->endc || (*pp)[1] == ':') {
3439 Expr_SetValueOwn(st->expr, VarUniq(st->expr->value.str)); 3445 Expr_SetValueOwn(st->expr, VarUniq(st->expr->value.str));
3440 (*pp)++; 3446 (*pp)++;
3441 return AMR_OK; 3447 return AMR_OK;
3442 } else 3448 } else
3443 return AMR_UNKNOWN; 3449 return AMR_UNKNOWN;
3444} 3450}
3445 3451
3446#ifdef SYSVVARSUB 3452#ifdef SYSVVARSUB
3447/* :from=to */ 3453/* :from=to */
3448static ApplyModifierResult 3454static ApplyModifierResult
3449ApplyModifier_SysV(const char **pp, ApplyModifiersState *st) 3455ApplyModifier_SysV(const char **pp, ApplyModifiersState *st)
3450{ 3456{
3451 Expr *expr = st->expr; 3457 Expr *expr = st->expr;
3452 char *lhs, *rhs; 3458 char *lhs, *rhs;
3453 VarParseResult res; 3459 VarParseResult res;
3454 3460
3455 const char *mod = *pp; 3461 const char *mod = *pp;
3456 Boolean eqFound = FALSE; 3462 Boolean eqFound = FALSE;
3457 3463
3458 /* 3464 /*
3459 * First we make a pass through the string trying to verify it is a 3465 * First we make a pass through the string trying to verify it is a
3460 * SysV-make-style translation. It must be: <lhs>=<rhs> 3466 * SysV-make-style translation. It must be: <lhs>=<rhs>
3461 */ 3467 */
3462 int depth = 1; 3468 int depth = 1;
3463 const char *p = mod; 3469 const char *p = mod;
3464 while (*p != '\0' && depth > 0) { 3470 while (*p != '\0' && depth > 0) {
3465 if (*p == '=') { /* XXX: should also test depth == 1 */ 3471 if (*p == '=') { /* XXX: should also test depth == 1 */
3466 eqFound = TRUE; 3472 eqFound = TRUE;
3467 /* continue looking for st->endc */ 3473 /* continue looking for st->endc */
3468 } else if (*p == st->endc) 3474 } else if (*p == st->endc)
3469 depth--; 3475 depth--;
3470 else if (*p == st->startc) 3476 else if (*p == st->startc)
3471 depth++; 3477 depth++;
3472 if (depth > 0) 3478 if (depth > 0)
3473 p++; 3479 p++;
3474 } 3480 }
3475 if (*p != st->endc || !eqFound) 3481 if (*p != st->endc || !eqFound)
3476 return AMR_UNKNOWN; 3482 return AMR_UNKNOWN;
3477 3483
3478 res = ParseModifierPart(pp, '=', expr->eflags, st, &lhs); 3484 res = ParseModifierPart(pp, '=', expr->eflags, st, &lhs);
3479 if (res != VPR_OK) 3485 if (res != VPR_OK)
3480 return AMR_CLEANUP; 3486 return AMR_CLEANUP;
3481 3487
3482 /* The SysV modifier lasts until the end of the variable expression. */ 3488 /* The SysV modifier lasts until the end of the variable expression. */
3483 res = ParseModifierPart(pp, st->endc, expr->eflags, st, &rhs); 3489 res = ParseModifierPart(pp, st->endc, expr->eflags, st, &rhs);
3484 if (res != VPR_OK) 3490 if (res != VPR_OK)
3485 return AMR_CLEANUP; 3491 return AMR_CLEANUP;
3486 3492
3487 (*pp)--; /* Go back to the st->endc. */ 3493 (*pp)--; /* Go back to the st->endc. */
3488 3494
3489 if (lhs[0] == '\0' && expr->value.str[0] == '\0') { 3495 if (lhs[0] == '\0' && expr->value.str[0] == '\0') {
3490 /* Do not turn an empty expression into non-empty. */ 3496 /* Do not turn an empty expression into non-empty. */
3491 } else { 3497 } else {
3492 struct ModifyWord_SYSVSubstArgs args = { 3498 struct ModifyWord_SYSVSubstArgs args = {
3493 expr->scope, lhs, rhs 3499 expr->scope, lhs, rhs
3494 }; 3500 };
3495 ModifyWords(st, ModifyWord_SYSVSubst, &args, st->oneBigWord); 3501 ModifyWords(st, ModifyWord_SYSVSubst, &args, st->oneBigWord);
3496 } 3502 }
3497 free(lhs); 3503 free(lhs);
3498 free(rhs); 3504 free(rhs);
3499 return AMR_OK; 3505 return AMR_OK;
3500} 3506}
3501#endif 3507#endif
3502 3508
3503#ifdef SUNSHCMD 3509#ifdef SUNSHCMD
3504/* :sh */ 3510/* :sh */
3505static ApplyModifierResult 3511static ApplyModifierResult
3506ApplyModifier_SunShell(const char **pp, ApplyModifiersState *st) 3512ApplyModifier_SunShell(const char **pp, ApplyModifiersState *st)
3507{ 3513{
3508 Expr *expr = st->expr; 3514 Expr *expr = st->expr;
3509 const char *p = *pp; 3515 const char *p = *pp;
3510 if (p[1] == 'h' && (p[2] == st->endc || p[2] == ':')) { 3516 if (p[1] == 'h' && (p[2] == st->endc || p[2] == ':')) {
3511 if (expr->eflags & VARE_WANTRES) { 3517 if (expr->eflags & VARE_WANTRES) {
3512 const char *errfmt; 3518 const char *errfmt;
3513 char *output = Cmd_Exec(expr->value.str, &errfmt); 3519 char *output = Cmd_Exec(expr->value.str, &errfmt);
3514 if (errfmt != NULL) 3520 if (errfmt != NULL)
3515 Error(errfmt, expr->value.str); 3521 Error(errfmt, expr->value.str);
3516 Expr_SetValueOwn(expr, output); 3522 Expr_SetValueOwn(expr, output);
3517 } else { 3523 } else {
3518 /* 3524 /*
3519 * TODO: Check whether returning ":sh" would be 3525 * TODO: Check whether returning ":sh" would be
3520 * more consistent with the other modifiers. 3526 * more consistent with the other modifiers.
3521 * 3527 *
3522 * TODO: Add a unit test demonstrating that the 3528 * TODO: Add a unit test demonstrating that the
3523 * actual value of this expression has any effect. 3529 * actual value of this expression has any effect.
3524 */ 3530 */
3525 Expr_SetValueRefer(expr, ""); 3531 Expr_SetValueRefer(expr, "");
3526 } 3532 }
3527 *pp = p + 2; 3533 *pp = p + 2;
3528 return AMR_OK; 3534 return AMR_OK;
3529 } else 3535 } else
3530 return AMR_UNKNOWN; 3536 return AMR_UNKNOWN;
3531} 3537}
3532#endif 3538#endif
3533 3539
3534static void 3540static void
3535LogBeforeApply(const ApplyModifiersState *st, const char *mod, char endc) 3541LogBeforeApply(const ApplyModifiersState *st, const char *mod, char endc)
3536{ 3542{
3537 Expr *expr = st->expr; 3543 Expr *expr = st->expr;
3538 char eflags_str[VarEvalFlags_ToStringSize]; 3544 char eflags_str[VarEvalFlags_ToStringSize];
3539 char vflags_str[VarFlags_ToStringSize]; 3545 char vflags_str[VarFlags_ToStringSize];
3540 Boolean is_single_char = mod[0] != '\0' && 3546 Boolean is_single_char = mod[0] != '\0' &&
3541 (mod[1] == endc || mod[1] == ':'); 3547 (mod[1] == endc || mod[1] == ':');
3542 3548
3543 /* At this point, only the first character of the modifier can 3549 /* At this point, only the first character of the modifier can
3544 * be used since the end of the modifier is not yet known. */ 3550 * be used since the end of the modifier is not yet known. */
3545 debug_printf("Applying ${%s:%c%s} to \"%s\" (%s, %s, %s)\n", 3551 debug_printf("Applying ${%s:%c%s} to \"%s\" (%s, %s, %s)\n",
3546 expr->var->name.str, mod[0], is_single_char ? "" : "...", 3552 expr->var->name.str, mod[0], is_single_char ? "" : "...",
3547 expr->value.str, 3553 expr->value.str,
3548 VarEvalFlags_ToString(eflags_str, expr->eflags), 3554 VarEvalFlags_ToString(eflags_str, expr->eflags),
3549 VarFlags_ToString(vflags_str, expr->var->flags), 3555 VarFlags_ToString(vflags_str, expr->var->flags),
3550 ExprDefined_Name[expr->defined]); 3556 ExprDefined_Name[expr->defined]);
3551} 3557}
3552 3558
3553static void 3559static void
3554LogAfterApply(ApplyModifiersState *st, const char *p, const char *mod) 3560LogAfterApply(ApplyModifiersState *st, const char *p, const char *mod)
3555{ 3561{
3556 Expr *expr = st->expr; 3562 Expr *expr = st->expr;
3557 const char *value = expr->value.str; 3563 const char *value = expr->value.str;
3558 char eflags_str[VarEvalFlags_ToStringSize]; 3564 char eflags_str[VarEvalFlags_ToStringSize];
3559 char vflags_str[VarFlags_ToStringSize]; 3565 char vflags_str[VarFlags_ToStringSize];
3560 const char *quot = value == var_Error ? "" : "\""; 3566 const char *quot = value == var_Error ? "" : "\"";
3561 3567
3562 debug_printf("Result of ${%s:%.*s} is %s%s%s (%s, %s, %s)\n", 3568 debug_printf("Result of ${%s:%.*s} is %s%s%s (%s, %s, %s)\n",
3563 expr->var->name.str, (int)(p - mod), mod, 3569 expr->var->name.str, (int)(p - mod), mod,
3564 quot, value == var_Error ? "error" : value, quot, 3570 quot, value == var_Error ? "error" : value, quot,
3565 VarEvalFlags_ToString(eflags_str, expr->eflags), 3571 VarEvalFlags_ToString(eflags_str, expr->eflags),
3566 VarFlags_ToString(vflags_str, expr->var->flags), 3572 VarFlags_ToString(vflags_str, expr->var->flags),
3567 ExprDefined_Name[expr->defined]); 3573 ExprDefined_Name[expr->defined]);
3568} 3574}
3569 3575
3570static ApplyModifierResult 3576static ApplyModifierResult
3571ApplyModifier(const char **pp, ApplyModifiersState *st) 3577ApplyModifier(const char **pp, ApplyModifiersState *st)
3572{ 3578{
3573 switch (**pp) { 3579 switch (**pp) {
3574 case ':': 3580 case ':':
3575 return ApplyModifier_Assign(pp, st); 3581 return ApplyModifier_Assign(pp, st);
3576 case '@': 3582 case '@':
3577 return ApplyModifier_Loop(pp, st); 3583 return ApplyModifier_Loop(pp, st);
3578 case '_': 3584 case '_':
3579 return ApplyModifier_Remember(pp, st); 3585 return ApplyModifier_Remember(pp, st);
3580 case 'D': 3586 case 'D':
3581 case 'U': 3587 case 'U':
3582 return ApplyModifier_Defined(pp, st); 3588 return ApplyModifier_Defined(pp, st);
3583 case 'L': 3589 case 'L':
3584 return ApplyModifier_Literal(pp, st); 3590 return ApplyModifier_Literal(pp, st);
3585 case 'P': 3591 case 'P':
3586 return ApplyModifier_Path(pp, st); 3592 return ApplyModifier_Path(pp, st);
3587 case '!': 3593 case '!':
3588 return ApplyModifier_ShellCommand(pp, st); 3594 return ApplyModifier_ShellCommand(pp, st);
3589 case '[': 3595 case '[':
3590 return ApplyModifier_Words(pp, st); 3596 return ApplyModifier_Words(pp, st);
3591 case 'g': 3597 case 'g':
3592 return ApplyModifier_Gmtime(pp, st); 3598 return ApplyModifier_Gmtime(pp, st);
3593 case 'h': 3599 case 'h':
3594 return ApplyModifier_Hash(pp, st); 3600 return ApplyModifier_Hash(pp, st);
3595 case 'l': 3601 case 'l':
3596 return ApplyModifier_Localtime(pp, st); 3602 return ApplyModifier_Localtime(pp, st);
3597 case 't': 3603 case 't':
3598 return ApplyModifier_To(pp, st); 3604 return ApplyModifier_To(pp, st);
3599 case 'N': 3605 case 'N':
3600 case 'M': 3606 case 'M':
3601 return ApplyModifier_Match(pp, st); 3607 return ApplyModifier_Match(pp, st);
3602 case 'S': 3608 case 'S':
3603 return ApplyModifier_Subst(pp, st); 3609 return ApplyModifier_Subst(pp, st);
3604 case '?': 3610 case '?':
3605 return ApplyModifier_IfElse(pp, st); 3611 return ApplyModifier_IfElse(pp, st);
3606#ifndef NO_REGEX 3612#ifndef NO_REGEX
3607 case 'C': 3613 case 'C':
3608 return ApplyModifier_Regex(pp, st); 3614 return ApplyModifier_Regex(pp, st);
3609#endif 3615#endif
3610 case 'q': 3616 case 'q':
3611 case 'Q': 3617 case 'Q':
3612 return ApplyModifier_Quote(pp, st); 3618 return ApplyModifier_Quote(pp, st);
3613 case 'T': 3619 case 'T':
3614 return ApplyModifier_WordFunc(pp, st, ModifyWord_Tail); 3620 return ApplyModifier_WordFunc(pp, st, ModifyWord_Tail);
3615 case 'H': 3621 case 'H':
3616 return ApplyModifier_WordFunc(pp, st, ModifyWord_Head); 3622 return ApplyModifier_WordFunc(pp, st, ModifyWord_Head);
3617 case 'E': 3623 case 'E':
3618 return ApplyModifier_WordFunc(pp, st, ModifyWord_Suffix); 3624 return ApplyModifier_WordFunc(pp, st, ModifyWord_Suffix);
3619 case 'R': 3625 case 'R':
3620 return ApplyModifier_WordFunc(pp, st, ModifyWord_Root); 3626 return ApplyModifier_WordFunc(pp, st, ModifyWord_Root);
3621 case 'r': 3627 case 'r':
3622 return ApplyModifier_Range(pp, st); 3628 return ApplyModifier_Range(pp, st);
3623 case 'O': 3629 case 'O':
3624 return ApplyModifier_Order(pp, st); 3630 return ApplyModifier_Order(pp, st);
3625 case 'u': 3631 case 'u':
3626 return ApplyModifier_Unique(pp, st); 3632 return ApplyModifier_Unique(pp, st);
3627#ifdef SUNSHCMD 3633#ifdef SUNSHCMD
3628 case 's': 3634 case 's':
3629 return ApplyModifier_SunShell(pp, st); 3635 return ApplyModifier_SunShell(pp, st);
3630#endif 3636#endif
3631 default: 3637 default:
3632 return AMR_UNKNOWN; 3638 return AMR_UNKNOWN;
3633 } 3639 }
3634} 3640}
3635 3641
3636static void ApplyModifiers(Expr *, const char **, char, char); 3642static void ApplyModifiers(Expr *, const char **, char, char);
3637 3643
3638typedef enum ApplyModifiersIndirectResult { 3644typedef enum ApplyModifiersIndirectResult {
3639 /* The indirect modifiers have been applied successfully. */ 3645 /* The indirect modifiers have been applied successfully. */
3640 AMIR_CONTINUE, 3646 AMIR_CONTINUE,
3641 /* Fall back to the SysV modifier. */ 3647 /* Fall back to the SysV modifier. */
3642 AMIR_SYSV, 3648 AMIR_SYSV,
3643 /* Error out. */ 3649 /* Error out. */
3644 AMIR_OUT 3650 AMIR_OUT
3645} ApplyModifiersIndirectResult; 3651} ApplyModifiersIndirectResult;
3646 3652
3647/* 3653/*
3648 * While expanding a variable expression, expand and apply indirect modifiers, 3654 * While expanding a variable expression, expand and apply indirect modifiers,
3649 * such as in ${VAR:${M_indirect}}. 3655 * such as in ${VAR:${M_indirect}}.
3650 * 3656 *
3651 * All indirect modifiers of a group must come from a single variable 3657 * All indirect modifiers of a group must come from a single variable
3652 * expression. ${VAR:${M1}} is valid but ${VAR:${M1}${M2}} is not. 3658 * expression. ${VAR:${M1}} is valid but ${VAR:${M1}${M2}} is not.
3653 * 3659 *
3654 * Multiple groups of indirect modifiers can be chained by separating them 3660 * Multiple groups of indirect modifiers can be chained by separating them
3655 * with colons. ${VAR:${M1}:${M2}} contains 2 indirect modifiers. 3661 * with colons. ${VAR:${M1}:${M2}} contains 2 indirect modifiers.
3656 * 3662 *
3657 * If the variable expression is not followed by st->endc or ':', fall 3663 * If the variable expression is not followed by st->endc or ':', fall
3658 * back to trying the SysV modifier, such as in ${VAR:${FROM}=${TO}}. 3664 * back to trying the SysV modifier, such as in ${VAR:${FROM}=${TO}}.
3659 */ 3665 */
3660static ApplyModifiersIndirectResult 3666static ApplyModifiersIndirectResult
3661ApplyModifiersIndirect(ApplyModifiersState *st, const char **pp) 3667ApplyModifiersIndirect(ApplyModifiersState *st, const char **pp)
3662{ 3668{
3663 Expr *expr = st->expr; 3669 Expr *expr = st->expr;
3664 const char *p = *pp; 3670 const char *p = *pp;
3665 FStr mods; 3671 FStr mods;
3666 3672
3667 (void)Var_Parse(&p, expr->scope, expr->eflags, &mods); 3673 (void)Var_Parse(&p, expr->scope, expr->eflags, &mods);
3668 /* TODO: handle errors */ 3674 /* TODO: handle errors */
3669 3675
3670 if (mods.str[0] != '\0' && *p != '\0' && *p != ':' && *p != st->endc) { 3676 if (mods.str[0] != '\0' && *p != '\0' && *p != ':' && *p != st->endc) {
3671 FStr_Done(&mods); 3677 FStr_Done(&mods);
3672 return AMIR_SYSV; 3678 return AMIR_SYSV;
3673 } 3679 }
3674 3680
3675 DEBUG3(VAR, "Indirect modifier \"%s\" from \"%.*s\"\n", 3681 DEBUG3(VAR, "Indirect modifier \"%s\" from \"%.*s\"\n",
3676 mods.str, (int)(p - *pp), *pp); 3682 mods.str, (int)(p - *pp), *pp);
3677 3683
3678 if (mods.str[0] != '\0') { 3684 if (mods.str[0] != '\0') {
3679 const char *modsp = mods.str; 3685 const char *modsp = mods.str;
3680 ApplyModifiers(expr, &modsp, '\0', '\0'); 3686 ApplyModifiers(expr, &modsp, '\0', '\0');
3681 if (expr->value.str == var_Error || *modsp != '\0') { 3687 if (expr->value.str == var_Error || *modsp != '\0') {
3682 FStr_Done(&mods); 3688 FStr_Done(&mods);
3683 *pp = p; 3689 *pp = p;
3684 return AMIR_OUT; /* error already reported */ 3690 return AMIR_OUT; /* error already reported */
3685 } 3691 }
3686 } 3692 }
3687 FStr_Done(&mods); 3693 FStr_Done(&mods);
3688 3694
3689 if (*p == ':') 3695 if (*p == ':')
3690 p++; 3696 p++;
3691 else if (*p == '\0' && st->endc != '\0') { 3697 else if (*p == '\0' && st->endc != '\0') {
3692 Error("Unclosed variable expression after indirect " 3698 Error("Unclosed variable expression after indirect "
3693 "modifier, expecting '%c' for variable \"%s\"", 3699 "modifier, expecting '%c' for variable \"%s\"",
3694 st->endc, expr->var->name.str); 3700 st->endc, expr->var->name.str);
3695 *pp = p; 3701 *pp = p;
3696 return AMIR_OUT; 3702 return AMIR_OUT;
3697 } 3703 }
3698 3704
3699 *pp = p; 3705 *pp = p;
3700 return AMIR_CONTINUE; 3706 return AMIR_CONTINUE;
3701} 3707}
3702 3708
3703static ApplyModifierResult 3709static ApplyModifierResult
3704ApplySingleModifier(const char **pp, char endc, ApplyModifiersState *st) 3710ApplySingleModifier(const char **pp, char endc, ApplyModifiersState *st)
3705{ 3711{
3706 ApplyModifierResult res; 3712 ApplyModifierResult res;
3707 const char *mod = *pp; 3713 const char *mod = *pp;
3708 const char *p = *pp; 3714 const char *p = *pp;
3709 3715
3710 if (DEBUG(VAR)) 3716 if (DEBUG(VAR))
3711 LogBeforeApply(st, mod, endc); 3717 LogBeforeApply(st, mod, endc);
3712 3718
3713 res = ApplyModifier(&p, st); 3719 res = ApplyModifier(&p, st);
3714 3720
3715#ifdef SYSVVARSUB 3721#ifdef SYSVVARSUB
3716 if (res == AMR_UNKNOWN) { 3722 if (res == AMR_UNKNOWN) {
3717 assert(p == mod); 3723 assert(p == mod);
3718 res = ApplyModifier_SysV(&p, st); 3724 res = ApplyModifier_SysV(&p, st);
3719 } 3725 }
3720#endif 3726#endif
3721 3727
3722 if (res == AMR_UNKNOWN) { 3728 if (res == AMR_UNKNOWN) {
3723 /* 3729 /*
3724 * Guess the end of the current modifier. 3730 * Guess the end of the current modifier.
3725 * XXX: Skipping the rest of the modifier hides 3731 * XXX: Skipping the rest of the modifier hides
3726 * errors and leads to wrong results. 3732 * errors and leads to wrong results.
3727 * Parsing should rather stop here. 3733 * Parsing should rather stop here.
3728 */ 3734 */
3729 for (p++; *p != ':' && *p != st->endc && *p != '\0'; p++) 3735 for (p++; *p != ':' && *p != st->endc && *p != '\0'; p++)
3730 continue; 3736 continue;
3731 Parse_Error(PARSE_FATAL, "Unknown modifier \"%.*s\"", 3737 Parse_Error(PARSE_FATAL, "Unknown modifier \"%.*s\"",
3732 (int)(p - mod), mod); 3738 (int)(p - mod), mod);
3733 Expr_SetValueRefer(st->expr, var_Error); 3739 Expr_SetValueRefer(st->expr, var_Error);
3734 } 3740 }
3735 if (res == AMR_CLEANUP || res == AMR_BAD) { 3741 if (res == AMR_CLEANUP || res == AMR_BAD) {
3736 *pp = p; 3742 *pp = p;
3737 return res; 3743 return res;
3738 } 3744 }
3739 3745
3740 if (DEBUG(VAR)) 3746 if (DEBUG(VAR))
3741 LogAfterApply(st, p, mod); 3747 LogAfterApply(st, p, mod);
3742 3748
3743 if (*p == '\0' && st->endc != '\0') { 3749 if (*p == '\0' && st->endc != '\0') {
3744 Error( 3750 Error(
3745 "Unclosed variable expression, expecting '%c' for " 3751 "Unclosed variable expression, expecting '%c' for "
3746 "modifier \"%.*s\" of variable \"%s\" with value \"%s\"", 3752 "modifier \"%.*s\" of variable \"%s\" with value \"%s\"",
3747 st->endc, 3753 st->endc,
3748 (int)(p - mod), mod, 3754 (int)(p - mod), mod,
3749 st->expr->var->name.str, st->expr->value.str); 3755 st->expr->var->name.str, st->expr->value.str);
3750 } else if (*p == ':') { 3756 } else if (*p == ':') {
3751 p++; 3757 p++;
3752 } else if (opts.strict && *p != '\0' && *p != endc) { 3758 } else if (opts.strict && *p != '\0' && *p != endc) {
3753 Parse_Error(PARSE_FATAL, 3759 Parse_Error(PARSE_FATAL,
3754 "Missing delimiter ':' after modifier \"%.*s\"", 3760 "Missing delimiter ':' after modifier \"%.*s\"",
3755 (int)(p - mod), mod); 3761 (int)(p - mod), mod);
3756 /* 3762 /*
3757 * TODO: propagate parse error to the enclosing 3763 * TODO: propagate parse error to the enclosing
3758 * expression 3764 * expression
3759 */ 3765 */
3760 } 3766 }
3761 *pp = p; 3767 *pp = p;
3762 return AMR_OK; 3768 return AMR_OK;
3763} 3769}
3764 3770
3765/* Apply any modifiers (such as :Mpattern or :@var@loop@ or :Q or ::=value). */ 3771/* Apply any modifiers (such as :Mpattern or :@var@loop@ or :Q or ::=value). */
3766static void 3772static void
3767ApplyModifiers( 3773ApplyModifiers(
3768 Expr *expr, 3774 Expr *expr,
3769 const char **pp, /* the parsing position, updated upon return */ 3775 const char **pp, /* the parsing position, updated upon return */
3770 char startc, /* '(' or '{'; or '\0' for indirect modifiers */ 3776 char startc, /* '(' or '{'; or '\0' for indirect modifiers */
3771 char endc /* ')' or '}'; or '\0' for indirect modifiers */ 3777 char endc /* ')' or '}'; or '\0' for indirect modifiers */
3772) 3778)
3773{ 3779{
3774 ApplyModifiersState st = { 3780 ApplyModifiersState st = {
3775 expr, 3781 expr,
3776 startc, 3782 startc,
3777 endc, 3783 endc,
3778 ' ', /* .sep */ 3784 ' ', /* .sep */
3779 FALSE /* .oneBigWord */ 3785 FALSE /* .oneBigWord */
3780 }; 3786 };
3781 const char *p; 3787 const char *p;
3782 const char *mod; 3788 const char *mod;
3783 3789
3784 assert(startc == '(' || startc == '{' || startc == '\0'); 3790 assert(startc == '(' || startc == '{' || startc == '\0');
3785 assert(endc == ')' || endc == '}' || endc == '\0'); 3791 assert(endc == ')' || endc == '}' || endc == '\0');
3786 assert(expr->value.str != NULL); 3792 assert(expr->value.str != NULL);
3787 3793
3788 p = *pp; 3794 p = *pp;
3789 3795
3790 if (*p == '\0' && endc != '\0') { 3796 if (*p == '\0' && endc != '\0') {
3791 Error( 3797 Error(
3792 "Unclosed variable expression (expecting '%c') for \"%s\"", 3798 "Unclosed variable expression (expecting '%c') for \"%s\"",
3793 st.endc, expr->var->name.str); 3799 st.endc, expr->var->name.str);
3794 goto cleanup; 3800 goto cleanup;
3795 } 3801 }
3796 3802
3797 while (*p != '\0' && *p != endc) { 3803 while (*p != '\0' && *p != endc) {
3798 ApplyModifierResult res; 3804 ApplyModifierResult res;
3799 3805
3800 if (*p == '$') { 3806 if (*p == '$') {
3801 ApplyModifiersIndirectResult amir = 3807 ApplyModifiersIndirectResult amir =
3802 ApplyModifiersIndirect(&st, &p); 3808 ApplyModifiersIndirect(&st, &p);
3803 if (amir == AMIR_CONTINUE) 3809 if (amir == AMIR_CONTINUE)
3804 continue; 3810 continue;
3805 if (amir == AMIR_OUT) 3811 if (amir == AMIR_OUT)
3806 break; 3812 break;
3807 /* 3813 /*
3808 * It's neither '${VAR}:' nor '${VAR}}'. Try to parse 3814 * It's neither '${VAR}:' nor '${VAR}}'. Try to parse
3809 * it as a SysV modifier, as that is the only modifier 3815 * it as a SysV modifier, as that is the only modifier
3810 * that can start with '$'. 3816 * that can start with '$'.
3811 */ 3817 */
3812 } 3818 }
3813 3819
3814 mod = p; 3820 mod = p;
3815 3821
3816 res = ApplySingleModifier(&p, endc, &st); 3822 res = ApplySingleModifier(&p, endc, &st);
3817 if (res == AMR_CLEANUP) 3823 if (res == AMR_CLEANUP)
3818 goto cleanup; 3824 goto cleanup;
3819 if (res == AMR_BAD) 3825 if (res == AMR_BAD)
3820 goto bad_modifier; 3826 goto bad_modifier;
3821 } 3827 }
3822 3828
3823 *pp = p; 3829 *pp = p;
3824 assert(expr->value.str != NULL); /* Use var_Error or varUndefined. */ 3830 assert(expr->value.str != NULL); /* Use var_Error or varUndefined. */
3825 return; 3831 return;
3826 3832
3827bad_modifier: 3833bad_modifier:
3828 /* XXX: The modifier end is only guessed. */ 3834 /* XXX: The modifier end is only guessed. */
3829 Error("Bad modifier \":%.*s\" for variable \"%s\"", 3835 Error("Bad modifier \":%.*s\" for variable \"%s\"",
3830 (int)strcspn(mod, ":)}"), mod, expr->var->name.str); 3836 (int)strcspn(mod, ":)}"), mod, expr->var->name.str);
3831 3837
3832cleanup: 3838cleanup:
3833 *pp = p; 3839 *pp = p;
3834 Expr_SetValueRefer(expr, var_Error); 3840 Expr_SetValueRefer(expr, var_Error);
3835} 3841}
3836 3842
3837/* 3843/*
3838 * Only four of the local variables are treated specially as they are the 3844 * Only four of the local variables are treated specially as they are the
3839 * only four that will be set when dynamic sources are expanded. 3845 * only four that will be set when dynamic sources are expanded.
3840 */ 3846 */
3841static Boolean 3847static Boolean
3842VarnameIsDynamic(const char *name, size_t len) 3848VarnameIsDynamic(const char *name, size_t len)
3843{ 3849{
3844 if (len == 1 || (len == 2 && (name[1] == 'F' || name[1] == 'D'))) { 3850 if (len == 1 || (len == 2 && (name[1] == 'F' || name[1] == 'D'))) {
3845 switch (name[0]) { 3851 switch (name[0]) {
3846 case '@': 3852 case '@':
3847 case '%': 3853 case '%':
3848 case '*': 3854 case '*':
3849 case '!': 3855 case '!':
3850 return TRUE; 3856 return TRUE;
3851 } 3857 }
3852 return FALSE; 3858 return FALSE;
3853 } 3859 }
3854 3860
3855 if ((len == 7 || len == 8) && name[0] == '.' && ch_isupper(name[1])) { 3861 if ((len == 7 || len == 8) && name[0] == '.' && ch_isupper(name[1])) {
3856 return strcmp(name, ".TARGET") == 0 || 3862 return strcmp(name, ".TARGET") == 0 ||
3857 strcmp(name, ".ARCHIVE") == 0 || 3863 strcmp(name, ".ARCHIVE") == 0 ||
3858 strcmp(name, ".PREFIX") == 0 || 3864 strcmp(name, ".PREFIX") == 0 ||
3859 strcmp(name, ".MEMBER") == 0; 3865 strcmp(name, ".MEMBER") == 0;
3860 } 3866 }
3861 3867
3862 return FALSE; 3868 return FALSE;
3863} 3869}
3864 3870
3865static const char * 3871static const char *
3866UndefinedShortVarValue(char varname, const GNode *scope) 3872UndefinedShortVarValue(char varname, const GNode *scope)
3867{ 3873{
3868 if (scope == SCOPE_CMDLINE || scope == SCOPE_GLOBAL) { 3874 if (scope == SCOPE_CMDLINE || scope == SCOPE_GLOBAL) {
3869 /* 3875 /*
3870 * If substituting a local variable in a non-local scope, 3876 * If substituting a local variable in a non-local scope,
3871 * assume it's for dynamic source stuff. We have to handle 3877 * assume it's for dynamic source stuff. We have to handle
3872 * this specially and return the longhand for the variable 3878 * this specially and return the longhand for the variable
3873 * with the dollar sign escaped so it makes it back to the 3879 * with the dollar sign escaped so it makes it back to the
3874 * caller. Only four of the local variables are treated 3880 * caller. Only four of the local variables are treated
3875 * specially as they are the only four that will be set 3881 * specially as they are the only four that will be set
3876 * when dynamic sources are expanded. 3882 * when dynamic sources are expanded.
3877 */ 3883 */
3878 switch (varname) { 3884 switch (varname) {
3879 case '@': 3885 case '@':
3880 return "$(.TARGET)"; 3886 return "$(.TARGET)";
3881 case '%': 3887 case '%':
3882 return "$(.MEMBER)"; 3888 return "$(.MEMBER)";
3883 case '*': 3889 case '*':
3884 return "$(.PREFIX)"; 3890 return "$(.PREFIX)";
3885 case '!': 3891 case '!':
3886 return "$(.ARCHIVE)"; 3892 return "$(.ARCHIVE)";
3887 } 3893 }
3888 } 3894 }
3889 return NULL; 3895 return NULL;
3890} 3896}
3891 3897
3892/* 3898/*
3893 * Parse a variable name, until the end character or a colon, whichever 3899 * Parse a variable name, until the end character or a colon, whichever

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

--- src/usr.bin/make/unit-tests/opt-debug-lint.exp 2021/03/14 10:45:51 1.15
+++ src/usr.bin/make/unit-tests/opt-debug-lint.exp 2021/03/14 10:57:12 1.16
@@ -1,9 +1,8 @@ @@ -1,9 +1,8 @@
1make: "opt-debug-lint.mk" line 19: Variable "X" is undefined 1make: "opt-debug-lint.mk" line 19: Variable "X" is undefined
2make: "opt-debug-lint.mk" line 41: Variable "UNDEF" is undefined 2make: "opt-debug-lint.mk" line 41: Variable "UNDEF" is undefined
3make: "opt-debug-lint.mk" line 61: Missing delimiter ':' after modifier "L" 3make: "opt-debug-lint.mk" line 61: Missing delimiter ':' after modifier "L"
4make: "opt-debug-lint.mk" line 61: Missing delimiter ':' after modifier "P" 4make: "opt-debug-lint.mk" line 61: Missing delimiter ':' after modifier "P"
5make: "opt-debug-lint.mk" line 69: Unknown modifier "${" 5make: "opt-debug-lint.mk" line 69: Unknown modifier "${"
6make: Regex compilation error: (details omitted) 
7make: Fatal errors encountered -- cannot continue 6make: Fatal errors encountered -- cannot continue
8make: stopped in unit-tests 7make: stopped in unit-tests
9exit status 1 8exit status 1

cvs diff -r1.13 -r1.14 src/usr.bin/make/unit-tests/opt-debug-lint.mk (switch to unified diff)

--- src/usr.bin/make/unit-tests/opt-debug-lint.mk 2021/03/14 10:45:51 1.13
+++ src/usr.bin/make/unit-tests/opt-debug-lint.mk 2021/03/14 10:57:12 1.14
@@ -1,92 +1,95 @@ @@ -1,92 +1,95 @@
1# $NetBSD: opt-debug-lint.mk,v 1.13 2021/03/14 10:45:51 rillig Exp $ 1# $NetBSD: opt-debug-lint.mk,v 1.14 2021/03/14 10:57:12 rillig Exp $
2# 2#
3# Tests for the -dL command line option, which runs additional checks 3# Tests for the -dL command line option, which runs additional checks
4# to catch common mistakes, such as unclosed variable expressions. 4# to catch common mistakes, such as unclosed variable expressions.
5 5
6.MAKEFLAGS: -dL 6.MAKEFLAGS: -dL
7 7
8# Since 2020-09-13, undefined variables that are used on the left-hand side 8# Since 2020-09-13, undefined variables that are used on the left-hand side
9# of a condition at parse time get a proper error message. Before, the 9# of a condition at parse time get a proper error message. Before, the
10# error message was "Malformed conditional" only, which was wrong and 10# error message was "Malformed conditional" only, which was wrong and
11# misleading. The form of the condition is totally fine, it's the evaluation 11# misleading. The form of the condition is totally fine, it's the evaluation
12# that fails. 12# that fails.
13# 13#
14# Since 2020-09-13, the "Malformed conditional" error message is not printed 14# Since 2020-09-13, the "Malformed conditional" error message is not printed
15# anymore. 15# anymore.
16# 16#
17# See also: 17# See also:
18# cond-undef-lint.mk 18# cond-undef-lint.mk
19.if $X 19.if $X
20. error 20. error
21.endif 21.endif
22 22
23# The dynamic variables like .TARGET are treated specially. It does not make 23# The dynamic variables like .TARGET are treated specially. It does not make
24# sense to expand them in the global scope since they will never be defined 24# sense to expand them in the global scope since they will never be defined
25# there under normal circumstances. Therefore they expand to a string that 25# there under normal circumstances. Therefore they expand to a string that
26# will later be expanded correctly, when the variable is evaluated again in 26# will later be expanded correctly, when the variable is evaluated again in
27# the scope of an actual target. 27# the scope of an actual target.
28# 28#
29# Even though the "@" variable is not defined at this point, this is not an 29# Even though the "@" variable is not defined at this point, this is not an
30# error. In all practical cases, this is no problem. This particular test 30# error. In all practical cases, this is no problem. This particular test
31# case is made up and unrealistic. 31# case is made up and unrealistic.
32.if $@ != "\$(.TARGET)" 32.if $@ != "\$(.TARGET)"
33. error 33. error
34.endif 34.endif
35 35
36# Since 2020-09-13, Var_Parse properly reports errors for undefined variables, 36# Since 2020-09-13, Var_Parse properly reports errors for undefined variables,
37# but only in lint mode. Before, it had only silently returned var_Error, 37# but only in lint mode. Before, it had only silently returned var_Error,
38# hoping for the caller to print an error message. This resulted in the 38# hoping for the caller to print an error message. This resulted in the
39# well-known "Malformed conditional" error message, even though the 39# well-known "Malformed conditional" error message, even though the
40# conditional was well-formed and the only error was an undefined variable. 40# conditional was well-formed and the only error was an undefined variable.
41.if ${UNDEF} 41.if ${UNDEF}
42. error 42. error
43.endif 43.endif
44 44
45# Since 2020-09-14, dependency lines may contain undefined variables. 45# Since 2020-09-14, dependency lines may contain undefined variables.
46# Before, undefined variables were forbidden, but this distinction was not 46# Before, undefined variables were forbidden, but this distinction was not
47# observable from the outside of the function Var_Parse. 47# observable from the outside of the function Var_Parse.
48${UNDEF}: ${UNDEF} 48${UNDEF}: ${UNDEF}
49 49
50# In a condition that has a defined(UNDEF) guard, all guarded conditions 50# In a condition that has a defined(UNDEF) guard, all guarded conditions
51# may assume that the variable is defined since they will only be evaluated 51# may assume that the variable is defined since they will only be evaluated
52# if the variable is indeed defined. Otherwise they are only parsed, and 52# if the variable is indeed defined. Otherwise they are only parsed, and
53# for parsing it doesn't make a difference whether the variable is defined 53# for parsing it doesn't make a difference whether the variable is defined
54# or not. 54# or not.
55.if defined(UNDEF) && exists(${UNDEF}) 55.if defined(UNDEF) && exists(${UNDEF})
56. error 56. error
57.endif 57.endif
58 58
59# Since 2020-10-03, in lint mode the variable modifier must be separated 59# Since 2020-10-03, in lint mode the variable modifier must be separated
60# by colons. See varparse-mod.mk. 60# by colons. See varparse-mod.mk.
61.if ${value:LPL} != "value" 61.if ${value:LPL} != "value"
62. error 62. error
63.endif 63.endif
64 64
65# Between 2020-10-03 and var.c 1.752 from 2020-12-20, in lint mode the 65# Between 2020-10-03 and var.c 1.752 from 2020-12-20, in lint mode the
66# variable modifier had to be separated by colons. This was wrong though 66# variable modifier had to be separated by colons. This was wrong though
67# since make always fell back trying to parse the indirect modifier as a 67# since make always fell back trying to parse the indirect modifier as a
68# SysV modifier. 68# SysV modifier.
69.if ${value:${:UL}PL} != "LPL}" # FIXME: "LPL}" is unexpected here. 69.if ${value:${:UL}PL} != "LPL}" # FIXME: "LPL}" is unexpected here.
70. error ${value:${:UL}PL} 70. error ${value:${:UL}PL}
71.endif 71.endif
72 72
73# Typically, an indirect modifier is followed by a colon or the closing 73# Typically, an indirect modifier is followed by a colon or the closing
74# brace. This one isn't, therefore make falls back to parsing it as the SysV 74# brace. This one isn't, therefore make falls back to parsing it as the SysV
75# modifier ":lue=lid". 75# modifier ":lue=lid".
76.if ${value:L:${:Ulue}=${:Ulid}} != "valid" 76.if ${value:L:${:Ulue}=${:Ulid}} != "valid"
77. error 77. error
78.endif 78.endif
79 79
80# Before var.c 1.XXX from 2021-03-14, the whole variable text was evaluated 80# In lint mode, the whole variable text is evaluated to check for unclosed
81# to check for unclosed expressions and unknown operators. During this check, 81# expressions and unknown operators. During this check, the subexpression
82# the subexpression '${:U2}' was not expanded, instead it was copied verbatim 82# '${:U2}' is not expanded, instead it is copied verbatim into the regular
83# into the regular expression, which was '.*=.{1,${:U2}}$'. This regular 83# expression, leading to '.*=.{1,${:U2}}$'.
84# expression was then compiled (despite VARE_WANTRES being unset), which 84#
85# resulted in a wrong error message. 85# Before var.c 1.856 from 2021-03-14, this regular expression was then
 86# compiled even though that was not necessary for checking the syntax at the
 87# level of variable expressions. The unexpanded '$' then resulted in a wrong
 88# error message.
86# 89#
87# This only happened in lint mode since in default mode the early check for 90# This only happened in lint mode since in default mode the early check for
88# unclosed expressions and unknown modifiers is skipped. 91# unclosed expressions and unknown modifiers is skipped.
89# 92#
90# See VarCheckSyntax, ApplyModifier_Regex. 93# See VarCheckSyntax, ApplyModifier_Regex.
91# 94#
92VARMOD_REGEX= ${:UA=111 B=222 C=33:C/.*=.{1,${:U2}}$//g} 95VARMOD_REGEX= ${:UA=111 B=222 C=33:C/.*=.{1,${:U2}}$//g}