Sun Mar 14 16:03:04 2021 UTC ()
make: return failure in TryParseIntBase0 for empty string

No functional change since the only caller of TryParseIntBase0 already
handles all possible parse errors.  Without this check, the code just
looked wrong though.


(rillig)
diff -r1.864 -r1.865 src/usr.bin/make/var.c

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

--- src/usr.bin/make/var.c 2021/03/14 15:43:31 1.864
+++ src/usr.bin/make/var.c 2021/03/14 16:03:04 1.865
@@ -1,1142 +1,1142 @@ @@ -1,1142 +1,1142 @@
1/* $NetBSD: var.c,v 1.864 2021/03/14 15:43:31 rillig Exp $ */ 1/* $NetBSD: var.c,v 1.865 2021/03/14 16:03:04 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.864 2021/03/14 15:43:31 rillig Exp $"); 143MAKE_RCSID("$NetBSD: var.c,v 1.865 2021/03/14 16:03:04 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.
@@ -1312,1998 +1312,2001 @@ static char * @@ -1312,1998 +1312,2001 @@ static char *
1312SepBuf_DoneData(SepBuf *buf) 1312SepBuf_DoneData(SepBuf *buf)
1313{ 1313{
1314 return Buf_DoneData(&buf->buf); 1314 return Buf_DoneData(&buf->buf);
1315} 1315}
1316 1316
1317 1317
1318/* 1318/*
1319 * This callback for ModifyWords gets a single word from a variable expression 1319 * This callback for ModifyWords gets a single word from a variable expression
1320 * and typically adds a modification of this word to the buffer. It may also 1320 * and typically adds a modification of this word to the buffer. It may also
1321 * do nothing or add several words. 1321 * do nothing or add several words.
1322 * 1322 *
1323 * For example, when evaluating the modifier ':M*b' in ${:Ua b c:M*b}, the 1323 * For example, when evaluating the modifier ':M*b' in ${:Ua b c:M*b}, the
1324 * callback is called 3 times, once for "a", "b" and "c". 1324 * callback is called 3 times, once for "a", "b" and "c".
1325 */ 1325 */
1326typedef void (*ModifyWordProc)(const char *word, SepBuf *buf, void *data); 1326typedef void (*ModifyWordProc)(const char *word, SepBuf *buf, void *data);
1327 1327
1328 1328
1329/* 1329/*
1330 * Callback for ModifyWords to implement the :H modifier. 1330 * Callback for ModifyWords to implement the :H modifier.
1331 * Add the dirname of the given word to the buffer. 1331 * Add the dirname of the given word to the buffer.
1332 */ 1332 */
1333/*ARGSUSED*/ 1333/*ARGSUSED*/
1334static void 1334static void
1335ModifyWord_Head(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) 1335ModifyWord_Head(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
1336{ 1336{
1337 const char *slash = strrchr(word, '/'); 1337 const char *slash = strrchr(word, '/');
1338 if (slash != NULL) 1338 if (slash != NULL)
1339 SepBuf_AddBytesBetween(buf, word, slash); 1339 SepBuf_AddBytesBetween(buf, word, slash);
1340 else 1340 else
1341 SepBuf_AddStr(buf, "."); 1341 SepBuf_AddStr(buf, ".");
1342} 1342}
1343 1343
1344/* 1344/*
1345 * Callback for ModifyWords to implement the :T modifier. 1345 * Callback for ModifyWords to implement the :T modifier.
1346 * Add the basename of the given word to the buffer. 1346 * Add the basename of the given word to the buffer.
1347 */ 1347 */
1348/*ARGSUSED*/ 1348/*ARGSUSED*/
1349static void 1349static void
1350ModifyWord_Tail(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) 1350ModifyWord_Tail(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
1351{ 1351{
1352 SepBuf_AddStr(buf, str_basename(word)); 1352 SepBuf_AddStr(buf, str_basename(word));
1353} 1353}
1354 1354
1355/* 1355/*
1356 * Callback for ModifyWords to implement the :E modifier. 1356 * Callback for ModifyWords to implement the :E modifier.
1357 * Add the filename suffix of the given word to the buffer, if it exists. 1357 * Add the filename suffix of the given word to the buffer, if it exists.
1358 */ 1358 */
1359/*ARGSUSED*/ 1359/*ARGSUSED*/
1360static void 1360static void
1361ModifyWord_Suffix(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) 1361ModifyWord_Suffix(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
1362{ 1362{
1363 const char *lastDot = strrchr(word, '.'); 1363 const char *lastDot = strrchr(word, '.');
1364 if (lastDot != NULL) 1364 if (lastDot != NULL)
1365 SepBuf_AddStr(buf, lastDot + 1); 1365 SepBuf_AddStr(buf, lastDot + 1);
1366} 1366}
1367 1367
1368/* 1368/*
1369 * Callback for ModifyWords to implement the :R modifier. 1369 * Callback for ModifyWords to implement the :R modifier.
1370 * Add the filename without extension of the given word to the buffer. 1370 * Add the filename without extension of the given word to the buffer.
1371 */ 1371 */
1372/*ARGSUSED*/ 1372/*ARGSUSED*/
1373static void 1373static void
1374ModifyWord_Root(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) 1374ModifyWord_Root(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
1375{ 1375{
1376 const char *lastDot = strrchr(word, '.'); 1376 const char *lastDot = strrchr(word, '.');
1377 size_t len = lastDot != NULL ? (size_t)(lastDot - word) : strlen(word); 1377 size_t len = lastDot != NULL ? (size_t)(lastDot - word) : strlen(word);
1378 SepBuf_AddBytes(buf, word, len); 1378 SepBuf_AddBytes(buf, word, len);
1379} 1379}
1380 1380
1381/* 1381/*
1382 * Callback for ModifyWords to implement the :M modifier. 1382 * Callback for ModifyWords to implement the :M modifier.
1383 * Place the word in the buffer if it matches the given pattern. 1383 * Place the word in the buffer if it matches the given pattern.
1384 */ 1384 */
1385static void 1385static void
1386ModifyWord_Match(const char *word, SepBuf *buf, void *data) 1386ModifyWord_Match(const char *word, SepBuf *buf, void *data)
1387{ 1387{
1388 const char *pattern = data; 1388 const char *pattern = data;
1389 DEBUG2(VAR, "VarMatch [%s] [%s]\n", word, pattern); 1389 DEBUG2(VAR, "VarMatch [%s] [%s]\n", word, pattern);
1390 if (Str_Match(word, pattern)) 1390 if (Str_Match(word, pattern))
1391 SepBuf_AddStr(buf, word); 1391 SepBuf_AddStr(buf, word);
1392} 1392}
1393 1393
1394/* 1394/*
1395 * Callback for ModifyWords to implement the :N modifier. 1395 * Callback for ModifyWords to implement the :N modifier.
1396 * Place the word in the buffer if it doesn't match the given pattern. 1396 * Place the word in the buffer if it doesn't match the given pattern.
1397 */ 1397 */
1398static void 1398static void
1399ModifyWord_NoMatch(const char *word, SepBuf *buf, void *data) 1399ModifyWord_NoMatch(const char *word, SepBuf *buf, void *data)
1400{ 1400{
1401 const char *pattern = data; 1401 const char *pattern = data;
1402 if (!Str_Match(word, pattern)) 1402 if (!Str_Match(word, pattern))
1403 SepBuf_AddStr(buf, word); 1403 SepBuf_AddStr(buf, word);
1404} 1404}
1405 1405
1406#ifdef SYSVVARSUB 1406#ifdef SYSVVARSUB
1407 1407
1408/* 1408/*
1409 * Check word against pattern for a match (% is a wildcard). 1409 * Check word against pattern for a match (% is a wildcard).
1410 * 1410 *
1411 * Input: 1411 * Input:
1412 * word Word to examine 1412 * word Word to examine
1413 * pattern Pattern to examine against 1413 * pattern Pattern to examine against
1414 * 1414 *
1415 * Results: 1415 * Results:
1416 * Returns the start of the match, or NULL. 1416 * Returns the start of the match, or NULL.
1417 * out_match_len returns the length of the match, if any. 1417 * out_match_len returns the length of the match, if any.
1418 * out_hasPercent returns whether the pattern contains a percent. 1418 * out_hasPercent returns whether the pattern contains a percent.
1419 */ 1419 */
1420static const char * 1420static const char *
1421SysVMatch(const char *word, const char *pattern, 1421SysVMatch(const char *word, const char *pattern,
1422 size_t *out_match_len, Boolean *out_hasPercent) 1422 size_t *out_match_len, Boolean *out_hasPercent)
1423{ 1423{
1424 const char *p = pattern; 1424 const char *p = pattern;
1425 const char *w = word; 1425 const char *w = word;
1426 const char *percent; 1426 const char *percent;
1427 size_t w_len; 1427 size_t w_len;
1428 size_t p_len; 1428 size_t p_len;
1429 const char *w_tail; 1429 const char *w_tail;
1430 1430
1431 *out_hasPercent = FALSE; 1431 *out_hasPercent = FALSE;
1432 percent = strchr(p, '%'); 1432 percent = strchr(p, '%');
1433 if (percent != NULL) { /* ${VAR:...%...=...} */ 1433 if (percent != NULL) { /* ${VAR:...%...=...} */
1434 *out_hasPercent = TRUE; 1434 *out_hasPercent = TRUE;
1435 if (w[0] == '\0') 1435 if (w[0] == '\0')
1436 return NULL; /* empty word does not match pattern */ 1436 return NULL; /* empty word does not match pattern */
1437 1437
1438 /* check that the prefix matches */ 1438 /* check that the prefix matches */
1439 for (; p != percent && *w != '\0' && *w == *p; w++, p++) 1439 for (; p != percent && *w != '\0' && *w == *p; w++, p++)
1440 continue; 1440 continue;
1441 if (p != percent) 1441 if (p != percent)
1442 return NULL; /* No match */ 1442 return NULL; /* No match */
1443 1443
1444 p++; /* Skip the percent */ 1444 p++; /* Skip the percent */
1445 if (*p == '\0') { 1445 if (*p == '\0') {
1446 /* No more pattern, return the rest of the string */ 1446 /* No more pattern, return the rest of the string */
1447 *out_match_len = strlen(w); 1447 *out_match_len = strlen(w);
1448 return w; 1448 return w;
1449 } 1449 }
1450 } 1450 }
1451 1451
1452 /* Test whether the tail matches */ 1452 /* Test whether the tail matches */
1453 w_len = strlen(w); 1453 w_len = strlen(w);
1454 p_len = strlen(p); 1454 p_len = strlen(p);
1455 if (w_len < p_len) 1455 if (w_len < p_len)
1456 return NULL; 1456 return NULL;
1457 1457
1458 w_tail = w + w_len - p_len; 1458 w_tail = w + w_len - p_len;
1459 if (memcmp(p, w_tail, p_len) != 0) 1459 if (memcmp(p, w_tail, p_len) != 0)
1460 return NULL; 1460 return NULL;
1461 1461
1462 *out_match_len = (size_t)(w_tail - w); 1462 *out_match_len = (size_t)(w_tail - w);
1463 return w; 1463 return w;
1464} 1464}
1465 1465
1466struct ModifyWord_SYSVSubstArgs { 1466struct ModifyWord_SYSVSubstArgs {
1467 GNode *scope; 1467 GNode *scope;
1468 const char *lhs; 1468 const char *lhs;
1469 const char *rhs; 1469 const char *rhs;
1470}; 1470};
1471 1471
1472/* Callback for ModifyWords to implement the :%.from=%.to modifier. */ 1472/* Callback for ModifyWords to implement the :%.from=%.to modifier. */
1473static void 1473static void
1474ModifyWord_SYSVSubst(const char *word, SepBuf *buf, void *data) 1474ModifyWord_SYSVSubst(const char *word, SepBuf *buf, void *data)
1475{ 1475{
1476 const struct ModifyWord_SYSVSubstArgs *args = data; 1476 const struct ModifyWord_SYSVSubstArgs *args = data;
1477 char *rhs_expanded; 1477 char *rhs_expanded;
1478 const char *rhs; 1478 const char *rhs;
1479 const char *percent; 1479 const char *percent;
1480 1480
1481 size_t match_len; 1481 size_t match_len;
1482 Boolean lhsPercent; 1482 Boolean lhsPercent;
1483 const char *match = SysVMatch(word, args->lhs, &match_len, &lhsPercent); 1483 const char *match = SysVMatch(word, args->lhs, &match_len, &lhsPercent);
1484 if (match == NULL) { 1484 if (match == NULL) {
1485 SepBuf_AddStr(buf, word); 1485 SepBuf_AddStr(buf, word);
1486 return; 1486 return;
1487 } 1487 }
1488 1488
1489 /* 1489 /*
1490 * Append rhs to the buffer, substituting the first '%' with the 1490 * Append rhs to the buffer, substituting the first '%' with the
1491 * match, but only if the lhs had a '%' as well. 1491 * match, but only if the lhs had a '%' as well.
1492 */ 1492 */
1493 1493
1494 (void)Var_Subst(args->rhs, args->scope, VARE_WANTRES, &rhs_expanded); 1494 (void)Var_Subst(args->rhs, args->scope, VARE_WANTRES, &rhs_expanded);
1495 /* TODO: handle errors */ 1495 /* TODO: handle errors */
1496 1496
1497 rhs = rhs_expanded; 1497 rhs = rhs_expanded;
1498 percent = strchr(rhs, '%'); 1498 percent = strchr(rhs, '%');
1499 1499
1500 if (percent != NULL && lhsPercent) { 1500 if (percent != NULL && lhsPercent) {
1501 /* Copy the prefix of the replacement pattern */ 1501 /* Copy the prefix of the replacement pattern */
1502 SepBuf_AddBytesBetween(buf, rhs, percent); 1502 SepBuf_AddBytesBetween(buf, rhs, percent);
1503 rhs = percent + 1; 1503 rhs = percent + 1;
1504 } 1504 }
1505 if (percent != NULL || !lhsPercent) 1505 if (percent != NULL || !lhsPercent)
1506 SepBuf_AddBytes(buf, match, match_len); 1506 SepBuf_AddBytes(buf, match, match_len);
1507 1507
1508 /* Append the suffix of the replacement pattern */ 1508 /* Append the suffix of the replacement pattern */
1509 SepBuf_AddStr(buf, rhs); 1509 SepBuf_AddStr(buf, rhs);
1510 1510
1511 free(rhs_expanded); 1511 free(rhs_expanded);
1512} 1512}
1513#endif 1513#endif
1514 1514
1515 1515
1516struct ModifyWord_SubstArgs { 1516struct ModifyWord_SubstArgs {
1517 const char *lhs; 1517 const char *lhs;
1518 size_t lhsLen; 1518 size_t lhsLen;
1519 const char *rhs; 1519 const char *rhs;
1520 size_t rhsLen; 1520 size_t rhsLen;
1521 VarPatternFlags pflags; 1521 VarPatternFlags pflags;
1522 Boolean matched; 1522 Boolean matched;
1523}; 1523};
1524 1524
1525/* 1525/*
1526 * Callback for ModifyWords to implement the :S,from,to, modifier. 1526 * Callback for ModifyWords to implement the :S,from,to, modifier.
1527 * Perform a string substitution on the given word. 1527 * Perform a string substitution on the given word.
1528 */ 1528 */
1529static void 1529static void
1530ModifyWord_Subst(const char *word, SepBuf *buf, void *data) 1530ModifyWord_Subst(const char *word, SepBuf *buf, void *data)
1531{ 1531{
1532 size_t wordLen = strlen(word); 1532 size_t wordLen = strlen(word);
1533 struct ModifyWord_SubstArgs *args = data; 1533 struct ModifyWord_SubstArgs *args = data;
1534 const char *match; 1534 const char *match;
1535 1535
1536 if (args->pflags.subOnce && args->matched) 1536 if (args->pflags.subOnce && args->matched)
1537 goto nosub; 1537 goto nosub;
1538 1538
1539 if (args->pflags.anchorStart) { 1539 if (args->pflags.anchorStart) {
1540 if (wordLen < args->lhsLen || 1540 if (wordLen < args->lhsLen ||
1541 memcmp(word, args->lhs, args->lhsLen) != 0) 1541 memcmp(word, args->lhs, args->lhsLen) != 0)
1542 goto nosub; 1542 goto nosub;
1543 1543
1544 if (args->pflags.anchorEnd && wordLen != args->lhsLen) 1544 if (args->pflags.anchorEnd && wordLen != args->lhsLen)
1545 goto nosub; 1545 goto nosub;
1546 1546
1547 /* :S,^prefix,replacement, or :S,^whole$,replacement, */ 1547 /* :S,^prefix,replacement, or :S,^whole$,replacement, */
1548 SepBuf_AddBytes(buf, args->rhs, args->rhsLen); 1548 SepBuf_AddBytes(buf, args->rhs, args->rhsLen);
1549 SepBuf_AddBytesBetween(buf, 1549 SepBuf_AddBytesBetween(buf,
1550 word + args->lhsLen, word + wordLen); 1550 word + args->lhsLen, word + wordLen);
1551 args->matched = TRUE; 1551 args->matched = TRUE;
1552 return; 1552 return;
1553 } 1553 }
1554 1554
1555 if (args->pflags.anchorEnd) { 1555 if (args->pflags.anchorEnd) {
1556 const char *start; 1556 const char *start;
1557 1557
1558 if (wordLen < args->lhsLen) 1558 if (wordLen < args->lhsLen)
1559 goto nosub; 1559 goto nosub;
1560 1560
1561 start = word + (wordLen - args->lhsLen); 1561 start = word + (wordLen - args->lhsLen);
1562 if (memcmp(start, args->lhs, args->lhsLen) != 0) 1562 if (memcmp(start, args->lhs, args->lhsLen) != 0)
1563 goto nosub; 1563 goto nosub;
1564 1564
1565 /* :S,suffix$,replacement, */ 1565 /* :S,suffix$,replacement, */
1566 SepBuf_AddBytesBetween(buf, word, start); 1566 SepBuf_AddBytesBetween(buf, word, start);
1567 SepBuf_AddBytes(buf, args->rhs, args->rhsLen); 1567 SepBuf_AddBytes(buf, args->rhs, args->rhsLen);
1568 args->matched = TRUE; 1568 args->matched = TRUE;
1569 return; 1569 return;
1570 } 1570 }
1571 1571
1572 if (args->lhs[0] == '\0') 1572 if (args->lhs[0] == '\0')
1573 goto nosub; 1573 goto nosub;
1574 1574
1575 /* unanchored case, may match more than once */ 1575 /* unanchored case, may match more than once */
1576 while ((match = strstr(word, args->lhs)) != NULL) { 1576 while ((match = strstr(word, args->lhs)) != NULL) {
1577 SepBuf_AddBytesBetween(buf, word, match); 1577 SepBuf_AddBytesBetween(buf, word, match);
1578 SepBuf_AddBytes(buf, args->rhs, args->rhsLen); 1578 SepBuf_AddBytes(buf, args->rhs, args->rhsLen);
1579 args->matched = TRUE; 1579 args->matched = TRUE;
1580 wordLen -= (size_t)(match - word) + args->lhsLen; 1580 wordLen -= (size_t)(match - word) + args->lhsLen;
1581 word += (size_t)(match - word) + args->lhsLen; 1581 word += (size_t)(match - word) + args->lhsLen;
1582 if (wordLen == 0 || !args->pflags.subGlobal) 1582 if (wordLen == 0 || !args->pflags.subGlobal)
1583 break; 1583 break;
1584 } 1584 }
1585nosub: 1585nosub:
1586 SepBuf_AddBytes(buf, word, wordLen); 1586 SepBuf_AddBytes(buf, word, wordLen);
1587} 1587}
1588 1588
1589#ifndef NO_REGEX 1589#ifndef NO_REGEX
1590/* Print the error caused by a regcomp or regexec call. */ 1590/* Print the error caused by a regcomp or regexec call. */
1591static void 1591static void
1592VarREError(int reerr, const regex_t *pat, const char *str) 1592VarREError(int reerr, const regex_t *pat, const char *str)
1593{ 1593{
1594 size_t errlen = regerror(reerr, pat, NULL, 0); 1594 size_t errlen = regerror(reerr, pat, NULL, 0);
1595 char *errbuf = bmake_malloc(errlen); 1595 char *errbuf = bmake_malloc(errlen);
1596 regerror(reerr, pat, errbuf, errlen); 1596 regerror(reerr, pat, errbuf, errlen);
1597 Error("%s: %s", str, errbuf); 1597 Error("%s: %s", str, errbuf);
1598 free(errbuf); 1598 free(errbuf);
1599} 1599}
1600 1600
1601struct ModifyWord_SubstRegexArgs { 1601struct ModifyWord_SubstRegexArgs {
1602 regex_t re; 1602 regex_t re;
1603 size_t nsub; 1603 size_t nsub;
1604 char *replace; 1604 char *replace;
1605 VarPatternFlags pflags; 1605 VarPatternFlags pflags;
1606 Boolean matched; 1606 Boolean matched;
1607}; 1607};
1608 1608
1609/* 1609/*
1610 * Callback for ModifyWords to implement the :C/from/to/ modifier. 1610 * Callback for ModifyWords to implement the :C/from/to/ modifier.
1611 * Perform a regex substitution on the given word. 1611 * Perform a regex substitution on the given word.
1612 */ 1612 */
1613static void 1613static void
1614ModifyWord_SubstRegex(const char *word, SepBuf *buf, void *data) 1614ModifyWord_SubstRegex(const char *word, SepBuf *buf, void *data)
1615{ 1615{
1616 struct ModifyWord_SubstRegexArgs *args = data; 1616 struct ModifyWord_SubstRegexArgs *args = data;
1617 int xrv; 1617 int xrv;
1618 const char *wp = word; 1618 const char *wp = word;
1619 char *rp; 1619 char *rp;
1620 int flags = 0; 1620 int flags = 0;
1621 regmatch_t m[10]; 1621 regmatch_t m[10];
1622 1622
1623 if (args->pflags.subOnce && args->matched) 1623 if (args->pflags.subOnce && args->matched)
1624 goto nosub; 1624 goto nosub;
1625 1625
1626tryagain: 1626tryagain:
1627 xrv = regexec(&args->re, wp, args->nsub, m, flags); 1627 xrv = regexec(&args->re, wp, args->nsub, m, flags);
1628 1628
1629 switch (xrv) { 1629 switch (xrv) {
1630 case 0: 1630 case 0:
1631 args->matched = TRUE; 1631 args->matched = TRUE;
1632 SepBuf_AddBytes(buf, wp, (size_t)m[0].rm_so); 1632 SepBuf_AddBytes(buf, wp, (size_t)m[0].rm_so);
1633 1633
1634 /* 1634 /*
1635 * Replacement of regular expressions is not specified by 1635 * Replacement of regular expressions is not specified by
1636 * POSIX, therefore implement it here. 1636 * POSIX, therefore implement it here.
1637 */ 1637 */
1638 1638
1639 for (rp = args->replace; *rp != '\0'; rp++) { 1639 for (rp = args->replace; *rp != '\0'; rp++) {
1640 if (*rp == '\\' && (rp[1] == '&' || rp[1] == '\\')) { 1640 if (*rp == '\\' && (rp[1] == '&' || rp[1] == '\\')) {
1641 SepBuf_AddBytes(buf, rp + 1, 1); 1641 SepBuf_AddBytes(buf, rp + 1, 1);
1642 rp++; 1642 rp++;
1643 continue; 1643 continue;
1644 } 1644 }
1645 1645
1646 if (*rp == '&') { 1646 if (*rp == '&') {
1647 SepBuf_AddBytesBetween(buf, 1647 SepBuf_AddBytesBetween(buf,
1648 wp + m[0].rm_so, wp + m[0].rm_eo); 1648 wp + m[0].rm_so, wp + m[0].rm_eo);
1649 continue; 1649 continue;
1650 } 1650 }
1651 1651
1652 if (*rp != '\\' || !ch_isdigit(rp[1])) { 1652 if (*rp != '\\' || !ch_isdigit(rp[1])) {
1653 SepBuf_AddBytes(buf, rp, 1); 1653 SepBuf_AddBytes(buf, rp, 1);
1654 continue; 1654 continue;
1655 } 1655 }
1656 1656
1657 { /* \0 to \9 backreference */ 1657 { /* \0 to \9 backreference */
1658 size_t n = (size_t)(rp[1] - '0'); 1658 size_t n = (size_t)(rp[1] - '0');
1659 rp++; 1659 rp++;
1660 1660
1661 if (n >= args->nsub) { 1661 if (n >= args->nsub) {
1662 Error("No subexpression \\%u", 1662 Error("No subexpression \\%u",
1663 (unsigned)n); 1663 (unsigned)n);
1664 } else if (m[n].rm_so == -1) { 1664 } else if (m[n].rm_so == -1) {
1665 Error( 1665 Error(
1666 "No match for subexpression \\%u", 1666 "No match for subexpression \\%u",
1667 (unsigned)n); 1667 (unsigned)n);
1668 } else { 1668 } else {
1669 SepBuf_AddBytesBetween(buf, 1669 SepBuf_AddBytesBetween(buf,
1670 wp + m[n].rm_so, wp + m[n].rm_eo); 1670 wp + m[n].rm_so, wp + m[n].rm_eo);
1671 } 1671 }
1672 } 1672 }
1673 } 1673 }
1674 1674
1675 wp += m[0].rm_eo; 1675 wp += m[0].rm_eo;
1676 if (args->pflags.subGlobal) { 1676 if (args->pflags.subGlobal) {
1677 flags |= REG_NOTBOL; 1677 flags |= REG_NOTBOL;
1678 if (m[0].rm_so == 0 && m[0].rm_eo == 0) { 1678 if (m[0].rm_so == 0 && m[0].rm_eo == 0) {
1679 SepBuf_AddBytes(buf, wp, 1); 1679 SepBuf_AddBytes(buf, wp, 1);
1680 wp++; 1680 wp++;
1681 } 1681 }
1682 if (*wp != '\0') 1682 if (*wp != '\0')
1683 goto tryagain; 1683 goto tryagain;
1684 } 1684 }
1685 if (*wp != '\0') 1685 if (*wp != '\0')
1686 SepBuf_AddStr(buf, wp); 1686 SepBuf_AddStr(buf, wp);
1687 break; 1687 break;
1688 default: 1688 default:
1689 VarREError(xrv, &args->re, "Unexpected regex error"); 1689 VarREError(xrv, &args->re, "Unexpected regex error");
1690 /* FALLTHROUGH */ 1690 /* FALLTHROUGH */
1691 case REG_NOMATCH: 1691 case REG_NOMATCH:
1692 nosub: 1692 nosub:
1693 SepBuf_AddStr(buf, wp); 1693 SepBuf_AddStr(buf, wp);
1694 break; 1694 break;
1695 } 1695 }
1696} 1696}
1697#endif 1697#endif
1698 1698
1699 1699
1700struct ModifyWord_LoopArgs { 1700struct ModifyWord_LoopArgs {
1701 GNode *scope; 1701 GNode *scope;
1702 char *tvar; /* name of temporary variable */ 1702 char *tvar; /* name of temporary variable */
1703 char *str; /* string to expand */ 1703 char *str; /* string to expand */
1704 VarEvalFlags eflags; 1704 VarEvalFlags eflags;
1705}; 1705};
1706 1706
1707/* Callback for ModifyWords to implement the :@var@...@ modifier of ODE make. */ 1707/* Callback for ModifyWords to implement the :@var@...@ modifier of ODE make. */
1708static void 1708static void
1709ModifyWord_Loop(const char *word, SepBuf *buf, void *data) 1709ModifyWord_Loop(const char *word, SepBuf *buf, void *data)
1710{ 1710{
1711 const struct ModifyWord_LoopArgs *args; 1711 const struct ModifyWord_LoopArgs *args;
1712 char *s; 1712 char *s;
1713 1713
1714 if (word[0] == '\0') 1714 if (word[0] == '\0')
1715 return; 1715 return;
1716 1716
1717 args = data; 1717 args = data;
1718 /* XXX: The variable name should not be expanded here. */ 1718 /* XXX: The variable name should not be expanded here. */
1719 Var_SetExpandWithFlags(args->scope, args->tvar, word, 1719 Var_SetExpandWithFlags(args->scope, args->tvar, word,
1720 VAR_SET_NO_EXPORT); 1720 VAR_SET_NO_EXPORT);
1721 (void)Var_Subst(args->str, args->scope, args->eflags, &s); 1721 (void)Var_Subst(args->str, args->scope, args->eflags, &s);
1722 /* TODO: handle errors */ 1722 /* TODO: handle errors */
1723 1723
1724 DEBUG4(VAR, "ModifyWord_Loop: " 1724 DEBUG4(VAR, "ModifyWord_Loop: "
1725 "in \"%s\", replace \"%s\" with \"%s\" to \"%s\"\n", 1725 "in \"%s\", replace \"%s\" with \"%s\" to \"%s\"\n",
1726 word, args->tvar, args->str, s); 1726 word, args->tvar, args->str, s);
1727 1727
1728 if (s[0] == '\n' || Buf_EndsWith(&buf->buf, '\n')) 1728 if (s[0] == '\n' || Buf_EndsWith(&buf->buf, '\n'))
1729 buf->needSep = FALSE; 1729 buf->needSep = FALSE;
1730 SepBuf_AddStr(buf, s); 1730 SepBuf_AddStr(buf, s);
1731 free(s); 1731 free(s);
1732} 1732}
1733 1733
1734 1734
1735/* 1735/*
1736 * The :[first..last] modifier selects words from the expression. 1736 * The :[first..last] modifier selects words from the expression.
1737 * It can also reverse the words. 1737 * It can also reverse the words.
1738 */ 1738 */
1739static char * 1739static char *
1740VarSelectWords(const char *str, int first, int last, 1740VarSelectWords(const char *str, int first, int last,
1741 char sep, Boolean oneBigWord) 1741 char sep, Boolean oneBigWord)
1742{ 1742{
1743 Words words; 1743 Words words;
1744 int len, start, end, step; 1744 int len, start, end, step;
1745 int i; 1745 int i;
1746 1746
1747 SepBuf buf; 1747 SepBuf buf;
1748 SepBuf_Init(&buf, sep); 1748 SepBuf_Init(&buf, sep);
1749 1749
1750 if (oneBigWord) { 1750 if (oneBigWord) {
1751 /* fake what Str_Words() would do if there were only one word */ 1751 /* fake what Str_Words() would do if there were only one word */
1752 words.len = 1; 1752 words.len = 1;
1753 words.words = bmake_malloc( 1753 words.words = bmake_malloc(
1754 (words.len + 1) * sizeof(words.words[0])); 1754 (words.len + 1) * sizeof(words.words[0]));
1755 words.freeIt = bmake_strdup(str); 1755 words.freeIt = bmake_strdup(str);
1756 words.words[0] = words.freeIt; 1756 words.words[0] = words.freeIt;
1757 words.words[1] = NULL; 1757 words.words[1] = NULL;
1758 } else { 1758 } else {
1759 words = Str_Words(str, FALSE); 1759 words = Str_Words(str, FALSE);
1760 } 1760 }
1761 1761
1762 /* 1762 /*
1763 * Now sanitize the given range. If first or last are negative, 1763 * Now sanitize the given range. If first or last are negative,
1764 * convert them to the positive equivalents (-1 gets converted to len, 1764 * convert them to the positive equivalents (-1 gets converted to len,
1765 * -2 gets converted to (len - 1), etc.). 1765 * -2 gets converted to (len - 1), etc.).
1766 */ 1766 */
1767 len = (int)words.len; 1767 len = (int)words.len;
1768 if (first < 0) 1768 if (first < 0)
1769 first += len + 1; 1769 first += len + 1;
1770 if (last < 0) 1770 if (last < 0)
1771 last += len + 1; 1771 last += len + 1;
1772 1772
1773 /* We avoid scanning more of the list than we need to. */ 1773 /* We avoid scanning more of the list than we need to. */
1774 if (first > last) { 1774 if (first > last) {
1775 start = (first > len ? len : first) - 1; 1775 start = (first > len ? len : first) - 1;
1776 end = last < 1 ? 0 : last - 1; 1776 end = last < 1 ? 0 : last - 1;
1777 step = -1; 1777 step = -1;
1778 } else { 1778 } else {
1779 start = first < 1 ? 0 : first - 1; 1779 start = first < 1 ? 0 : first - 1;
1780 end = last > len ? len : last; 1780 end = last > len ? len : last;
1781 step = 1; 1781 step = 1;
1782 } 1782 }
1783 1783
1784 for (i = start; (step < 0) == (i >= end); i += step) { 1784 for (i = start; (step < 0) == (i >= end); i += step) {
1785 SepBuf_AddStr(&buf, words.words[i]); 1785 SepBuf_AddStr(&buf, words.words[i]);
1786 SepBuf_Sep(&buf); 1786 SepBuf_Sep(&buf);
1787 } 1787 }
1788 1788
1789 Words_Free(words); 1789 Words_Free(words);
1790 1790
1791 return SepBuf_DoneData(&buf); 1791 return SepBuf_DoneData(&buf);
1792} 1792}
1793 1793
1794 1794
1795/* 1795/*
1796 * Callback for ModifyWords to implement the :tA modifier. 1796 * Callback for ModifyWords to implement the :tA modifier.
1797 * Replace each word with the result of realpath() if successful. 1797 * Replace each word with the result of realpath() if successful.
1798 */ 1798 */
1799/*ARGSUSED*/ 1799/*ARGSUSED*/
1800static void 1800static void
1801ModifyWord_Realpath(const char *word, SepBuf *buf, void *data MAKE_ATTR_UNUSED) 1801ModifyWord_Realpath(const char *word, SepBuf *buf, void *data MAKE_ATTR_UNUSED)
1802{ 1802{
1803 struct stat st; 1803 struct stat st;
1804 char rbuf[MAXPATHLEN]; 1804 char rbuf[MAXPATHLEN];
1805 1805
1806 const char *rp = cached_realpath(word, rbuf); 1806 const char *rp = cached_realpath(word, rbuf);
1807 if (rp != NULL && *rp == '/' && stat(rp, &st) == 0) 1807 if (rp != NULL && *rp == '/' && stat(rp, &st) == 0)
1808 word = rp; 1808 word = rp;
1809 1809
1810 SepBuf_AddStr(buf, word); 1810 SepBuf_AddStr(buf, word);
1811} 1811}
1812 1812
1813 1813
1814static char * 1814static char *
1815Words_JoinFree(Words words) 1815Words_JoinFree(Words words)
1816{ 1816{
1817 Buffer buf; 1817 Buffer buf;
1818 size_t i; 1818 size_t i;
1819 1819
1820 Buf_Init(&buf); 1820 Buf_Init(&buf);
1821 1821
1822 for (i = 0; i < words.len; i++) { 1822 for (i = 0; i < words.len; i++) {
1823 if (i != 0) { 1823 if (i != 0) {
1824 /* XXX: Use st->sep instead of ' ', for consistency. */ 1824 /* XXX: Use st->sep instead of ' ', for consistency. */
1825 Buf_AddByte(&buf, ' '); 1825 Buf_AddByte(&buf, ' ');
1826 } 1826 }
1827 Buf_AddStr(&buf, words.words[i]); 1827 Buf_AddStr(&buf, words.words[i]);
1828 } 1828 }
1829 1829
1830 Words_Free(words); 1830 Words_Free(words);
1831 1831
1832 return Buf_DoneData(&buf); 1832 return Buf_DoneData(&buf);
1833} 1833}
1834 1834
1835/* Remove adjacent duplicate words. */ 1835/* Remove adjacent duplicate words. */
1836static char * 1836static char *
1837VarUniq(const char *str) 1837VarUniq(const char *str)
1838{ 1838{
1839 Words words = Str_Words(str, FALSE); 1839 Words words = Str_Words(str, FALSE);
1840 1840
1841 if (words.len > 1) { 1841 if (words.len > 1) {
1842 size_t i, j; 1842 size_t i, j;
1843 for (j = 0, i = 1; i < words.len; i++) 1843 for (j = 0, i = 1; i < words.len; i++)
1844 if (strcmp(words.words[i], words.words[j]) != 0 && 1844 if (strcmp(words.words[i], words.words[j]) != 0 &&
1845 (++j != i)) 1845 (++j != i))
1846 words.words[j] = words.words[i]; 1846 words.words[j] = words.words[i];
1847 words.len = j + 1; 1847 words.len = j + 1;
1848 } 1848 }
1849 1849
1850 return Words_JoinFree(words); 1850 return Words_JoinFree(words);
1851} 1851}
1852 1852
1853 1853
1854/* 1854/*
1855 * Quote shell meta-characters and space characters in the string. 1855 * Quote shell meta-characters and space characters in the string.
1856 * If quoteDollar is set, also quote and double any '$' characters. 1856 * If quoteDollar is set, also quote and double any '$' characters.
1857 */ 1857 */
1858static char * 1858static char *
1859VarQuote(const char *str, Boolean quoteDollar) 1859VarQuote(const char *str, Boolean quoteDollar)
1860{ 1860{
1861 Buffer buf; 1861 Buffer buf;
1862 Buf_Init(&buf); 1862 Buf_Init(&buf);
1863 1863
1864 for (; *str != '\0'; str++) { 1864 for (; *str != '\0'; str++) {
1865 if (*str == '\n') { 1865 if (*str == '\n') {
1866 const char *newline = Shell_GetNewline(); 1866 const char *newline = Shell_GetNewline();
1867 if (newline == NULL) 1867 if (newline == NULL)
1868 newline = "\\\n"; 1868 newline = "\\\n";
1869 Buf_AddStr(&buf, newline); 1869 Buf_AddStr(&buf, newline);
1870 continue; 1870 continue;
1871 } 1871 }
1872 if (ch_isspace(*str) || is_shell_metachar((unsigned char)*str)) 1872 if (ch_isspace(*str) || is_shell_metachar((unsigned char)*str))
1873 Buf_AddByte(&buf, '\\'); 1873 Buf_AddByte(&buf, '\\');
1874 Buf_AddByte(&buf, *str); 1874 Buf_AddByte(&buf, *str);
1875 if (quoteDollar && *str == '$') 1875 if (quoteDollar && *str == '$')
1876 Buf_AddStr(&buf, "\\$"); 1876 Buf_AddStr(&buf, "\\$");
1877 } 1877 }
1878 1878
1879 return Buf_DoneData(&buf); 1879 return Buf_DoneData(&buf);
1880} 1880}
1881 1881
1882/* 1882/*
1883 * Compute the 32-bit hash of the given string, using the MurmurHash3 1883 * Compute the 32-bit hash of the given string, using the MurmurHash3
1884 * algorithm. Output is encoded as 8 hex digits, in Little Endian order. 1884 * algorithm. Output is encoded as 8 hex digits, in Little Endian order.
1885 */ 1885 */
1886static char * 1886static char *
1887VarHash(const char *str) 1887VarHash(const char *str)
1888{ 1888{
1889 static const char hexdigits[16] = "0123456789abcdef"; 1889 static const char hexdigits[16] = "0123456789abcdef";
1890 const unsigned char *ustr = (const unsigned char *)str; 1890 const unsigned char *ustr = (const unsigned char *)str;
1891 1891
1892 uint32_t h = 0x971e137bU; 1892 uint32_t h = 0x971e137bU;
1893 uint32_t c1 = 0x95543787U; 1893 uint32_t c1 = 0x95543787U;
1894 uint32_t c2 = 0x2ad7eb25U; 1894 uint32_t c2 = 0x2ad7eb25U;
1895 size_t len2 = strlen(str); 1895 size_t len2 = strlen(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
2280MAKE_INLINE Boolean 2280MAKE_INLINE Boolean
2281IsDelimiter(char ch, const ApplyModifiersState *st) 2281IsDelimiter(char ch, const ApplyModifiersState *st)
2282{ 2282{
2283 return ch == ':' || ch == st->endc; 2283 return ch == ':' || ch == st->endc;
2284} 2284}
2285 2285
2286/* Test whether mod starts with modname, followed by a delimiter. */ 2286/* Test whether mod starts with modname, followed by a delimiter. */
2287MAKE_INLINE Boolean 2287MAKE_INLINE Boolean
2288ModMatch(const char *mod, const char *modname, const ApplyModifiersState *st) 2288ModMatch(const char *mod, const char *modname, const ApplyModifiersState *st)
2289{ 2289{
2290 size_t n = strlen(modname); 2290 size_t n = strlen(modname);
2291 return strncmp(mod, modname, n) == 0 && IsDelimiter(mod[n], st); 2291 return strncmp(mod, modname, n) == 0 && IsDelimiter(mod[n], st);
2292} 2292}
2293 2293
2294/* Test whether mod starts with modname, followed by a delimiter or '='. */ 2294/* Test whether mod starts with modname, followed by a delimiter or '='. */
2295MAKE_INLINE Boolean 2295MAKE_INLINE Boolean
2296ModMatchEq(const char *mod, const char *modname, const ApplyModifiersState *st) 2296ModMatchEq(const char *mod, const char *modname, const ApplyModifiersState *st)
2297{ 2297{
2298 size_t n = strlen(modname); 2298 size_t n = strlen(modname);
2299 return strncmp(mod, modname, n) == 0 && 2299 return strncmp(mod, modname, n) == 0 &&
2300 (IsDelimiter(mod[n], st) || mod[n] == '='); 2300 (IsDelimiter(mod[n], st) || mod[n] == '=');
2301} 2301}
2302 2302
2303static Boolean 2303static Boolean
2304TryParseIntBase0(const char **pp, int *out_num) 2304TryParseIntBase0(const char **pp, int *out_num)
2305{ 2305{
2306 char *end; 2306 char *end;
2307 long n; 2307 long n;
2308 2308
2309 errno = 0; 2309 errno = 0;
2310 n = strtol(*pp, &end, 0); 2310 n = strtol(*pp, &end, 0);
 2311
 2312 if (end == *pp)
 2313 return FALSE;
2311 if ((n == LONG_MIN || n == LONG_MAX) && errno == ERANGE) 2314 if ((n == LONG_MIN || n == LONG_MAX) && errno == ERANGE)
2312 return FALSE; 2315 return FALSE;
2313 if (n < INT_MIN || n > INT_MAX) 2316 if (n < INT_MIN || n > INT_MAX)
2314 return FALSE; 2317 return FALSE;
2315 2318
2316 *pp = end; 2319 *pp = end;
2317 *out_num = (int)n; 2320 *out_num = (int)n;
2318 return TRUE; 2321 return TRUE;
2319} 2322}
2320 2323
2321static Boolean 2324static Boolean
2322TryParseSize(const char **pp, size_t *out_num) 2325TryParseSize(const char **pp, size_t *out_num)
2323{ 2326{
2324 char *end; 2327 char *end;
2325 unsigned long n; 2328 unsigned long n;
2326 2329
2327 if (!ch_isdigit(**pp)) 2330 if (!ch_isdigit(**pp))
2328 return FALSE; 2331 return FALSE;
2329 2332
2330 errno = 0; 2333 errno = 0;
2331 n = strtoul(*pp, &end, 10); 2334 n = strtoul(*pp, &end, 10);
2332 if (n == ULONG_MAX && errno == ERANGE) 2335 if (n == ULONG_MAX && errno == ERANGE)
2333 return FALSE; 2336 return FALSE;
2334 if (n > SIZE_MAX) 2337 if (n > SIZE_MAX)
2335 return FALSE; 2338 return FALSE;
2336 2339
2337 *pp = end; 2340 *pp = end;
2338 *out_num = (size_t)n; 2341 *out_num = (size_t)n;
2339 return TRUE; 2342 return TRUE;
2340} 2343}
2341 2344
2342static Boolean 2345static Boolean
2343TryParseChar(const char **pp, int base, char *out_ch) 2346TryParseChar(const char **pp, int base, char *out_ch)
2344{ 2347{
2345 char *end; 2348 char *end;
2346 unsigned long n; 2349 unsigned long n;
2347 2350
2348 if (!ch_isalnum(**pp)) 2351 if (!ch_isalnum(**pp))
2349 return FALSE; 2352 return FALSE;
2350 2353
2351 errno = 0; 2354 errno = 0;
2352 n = strtoul(*pp, &end, base); 2355 n = strtoul(*pp, &end, base);
2353 if (n == ULONG_MAX && errno == ERANGE) 2356 if (n == ULONG_MAX && errno == ERANGE)
2354 return FALSE; 2357 return FALSE;
2355 if (n > UCHAR_MAX) 2358 if (n > UCHAR_MAX)
2356 return FALSE; 2359 return FALSE;
2357 2360
2358 *pp = end; 2361 *pp = end;
2359 *out_ch = (char)n; 2362 *out_ch = (char)n;
2360 return TRUE; 2363 return TRUE;
2361} 2364}
2362 2365
2363/* 2366/*
2364 * Modify each word of the expression using the given function and place the 2367 * Modify each word of the expression using the given function and place the
2365 * result back in the expression. 2368 * result back in the expression.
2366 */ 2369 */
2367static void 2370static void
2368ModifyWords(ApplyModifiersState *st, 2371ModifyWords(ApplyModifiersState *st,
2369 ModifyWordProc modifyWord, void *modifyWord_args, 2372 ModifyWordProc modifyWord, void *modifyWord_args,
2370 Boolean oneBigWord) 2373 Boolean oneBigWord)
2371{ 2374{
2372 Expr *expr = st->expr; 2375 Expr *expr = st->expr;
2373 const char *val = expr->value.str; 2376 const char *val = expr->value.str;
2374 SepBuf result; 2377 SepBuf result;
2375 Words words; 2378 Words words;
2376 size_t i; 2379 size_t i;
2377 2380
2378 if (oneBigWord) { 2381 if (oneBigWord) {
2379 SepBuf_Init(&result, st->sep); 2382 SepBuf_Init(&result, st->sep);
2380 modifyWord(val, &result, modifyWord_args); 2383 modifyWord(val, &result, modifyWord_args);
2381 goto done; 2384 goto done;
2382 } 2385 }
2383 2386
2384 words = Str_Words(val, FALSE); 2387 words = Str_Words(val, FALSE);
2385 2388
2386 DEBUG2(VAR, "ModifyWords: split \"%s\" into %u words\n", 2389 DEBUG2(VAR, "ModifyWords: split \"%s\" into %u words\n",
2387 val, (unsigned)words.len); 2390 val, (unsigned)words.len);
2388 2391
2389 SepBuf_Init(&result, st->sep); 2392 SepBuf_Init(&result, st->sep);
2390 for (i = 0; i < words.len; i++) { 2393 for (i = 0; i < words.len; i++) {
2391 modifyWord(words.words[i], &result, modifyWord_args); 2394 modifyWord(words.words[i], &result, modifyWord_args);
2392 if (result.buf.len > 0) 2395 if (result.buf.len > 0)
2393 SepBuf_Sep(&result); 2396 SepBuf_Sep(&result);
2394 } 2397 }
2395 2398
2396 Words_Free(words); 2399 Words_Free(words);
2397 2400
2398done: 2401done:
2399 Expr_SetValueOwn(expr, SepBuf_DoneData(&result)); 2402 Expr_SetValueOwn(expr, SepBuf_DoneData(&result));
2400} 2403}
2401 2404
2402/* :@var@...${var}...@ */ 2405/* :@var@...${var}...@ */
2403static ApplyModifierResult 2406static ApplyModifierResult
2404ApplyModifier_Loop(const char **pp, ApplyModifiersState *st) 2407ApplyModifier_Loop(const char **pp, ApplyModifiersState *st)
2405{ 2408{
2406 Expr *expr = st->expr; 2409 Expr *expr = st->expr;
2407 struct ModifyWord_LoopArgs args; 2410 struct ModifyWord_LoopArgs args;
2408 char prev_sep; 2411 char prev_sep;
2409 VarParseResult res; 2412 VarParseResult res;
2410 2413
2411 args.scope = expr->scope; 2414 args.scope = expr->scope;
2412 2415
2413 (*pp)++; /* Skip the first '@' */ 2416 (*pp)++; /* Skip the first '@' */
2414 res = ParseModifierPart(pp, '@', VARE_NONE, st, &args.tvar); 2417 res = ParseModifierPart(pp, '@', VARE_NONE, st, &args.tvar);
2415 if (res != VPR_OK) 2418 if (res != VPR_OK)
2416 return AMR_CLEANUP; 2419 return AMR_CLEANUP;
2417 if (opts.strict && strchr(args.tvar, '$') != NULL) { 2420 if (opts.strict && strchr(args.tvar, '$') != NULL) {
2418 Parse_Error(PARSE_FATAL, 2421 Parse_Error(PARSE_FATAL,
2419 "In the :@ modifier of \"%s\", the variable name \"%s\" " 2422 "In the :@ modifier of \"%s\", the variable name \"%s\" "
2420 "must not contain a dollar.", 2423 "must not contain a dollar.",
2421 expr->var->name.str, args.tvar); 2424 expr->var->name.str, args.tvar);
2422 return AMR_CLEANUP; 2425 return AMR_CLEANUP;
2423 } 2426 }
2424 2427
2425 res = ParseModifierPart(pp, '@', VARE_NONE, st, &args.str); 2428 res = ParseModifierPart(pp, '@', VARE_NONE, st, &args.str);
2426 if (res != VPR_OK) 2429 if (res != VPR_OK)
2427 return AMR_CLEANUP; 2430 return AMR_CLEANUP;
2428 2431
2429 args.eflags = expr->eflags & ~(unsigned)VARE_KEEP_DOLLAR; 2432 args.eflags = expr->eflags & ~(unsigned)VARE_KEEP_DOLLAR;
2430 prev_sep = st->sep; 2433 prev_sep = st->sep;
2431 st->sep = ' '; /* XXX: should be st->sep for consistency */ 2434 st->sep = ' '; /* XXX: should be st->sep for consistency */
2432 ModifyWords(st, ModifyWord_Loop, &args, st->oneBigWord); 2435 ModifyWords(st, ModifyWord_Loop, &args, st->oneBigWord);
2433 st->sep = prev_sep; 2436 st->sep = prev_sep;
2434 /* XXX: Consider restoring the previous variable instead of deleting. */ 2437 /* XXX: Consider restoring the previous variable instead of deleting. */
2435 /* 2438 /*
2436 * XXX: The variable name should not be expanded here, see 2439 * XXX: The variable name should not be expanded here, see
2437 * ModifyWord_Loop. 2440 * ModifyWord_Loop.
2438 */ 2441 */
2439 Var_DeleteExpand(expr->scope, args.tvar); 2442 Var_DeleteExpand(expr->scope, args.tvar);
2440 free(args.tvar); 2443 free(args.tvar);
2441 free(args.str); 2444 free(args.str);
2442 return AMR_OK; 2445 return AMR_OK;
2443} 2446}
2444 2447
2445/* :Ddefined or :Uundefined */ 2448/* :Ddefined or :Uundefined */
2446static ApplyModifierResult 2449static ApplyModifierResult
2447ApplyModifier_Defined(const char **pp, ApplyModifiersState *st) 2450ApplyModifier_Defined(const char **pp, ApplyModifiersState *st)
2448{ 2451{
2449 Expr *expr = st->expr; 2452 Expr *expr = st->expr;
2450 Buffer buf; 2453 Buffer buf;
2451 const char *p; 2454 const char *p;
2452 2455
2453 VarEvalFlags eflags = VARE_NONE; 2456 VarEvalFlags eflags = VARE_NONE;
2454 if (expr->eflags & VARE_WANTRES) 2457 if (expr->eflags & VARE_WANTRES)
2455 if ((**pp == 'D') == (expr->defined == DEF_REGULAR)) 2458 if ((**pp == 'D') == (expr->defined == DEF_REGULAR))
2456 eflags = expr->eflags; 2459 eflags = expr->eflags;
2457 2460
2458 Buf_Init(&buf); 2461 Buf_Init(&buf);
2459 p = *pp + 1; 2462 p = *pp + 1;
2460 while (!IsDelimiter(*p, st) && *p != '\0') { 2463 while (!IsDelimiter(*p, st) && *p != '\0') {
2461 2464
2462 /* XXX: This code is similar to the one in Var_Parse. 2465 /* XXX: This code is similar to the one in Var_Parse.
2463 * See if the code can be merged. 2466 * See if the code can be merged.
2464 * See also ApplyModifier_Match and ParseModifierPart. */ 2467 * See also ApplyModifier_Match and ParseModifierPart. */
2465 2468
2466 /* Escaped delimiter or other special character */ 2469 /* Escaped delimiter or other special character */
2467 /* See Buf_AddEscaped in for.c. */ 2470 /* See Buf_AddEscaped in for.c. */
2468 if (*p == '\\') { 2471 if (*p == '\\') {
2469 char c = p[1]; 2472 char c = p[1];
2470 if (IsDelimiter(c, st) || c == '$' || c == '\\') { 2473 if (IsDelimiter(c, st) || c == '$' || c == '\\') {
2471 Buf_AddByte(&buf, c); 2474 Buf_AddByte(&buf, c);
2472 p += 2; 2475 p += 2;
2473 continue; 2476 continue;
2474 } 2477 }
2475 } 2478 }
2476 2479
2477 /* Nested variable expression */ 2480 /* Nested variable expression */
2478 if (*p == '$') { 2481 if (*p == '$') {
2479 FStr nested_val; 2482 FStr nested_val;
2480 2483
2481 (void)Var_Parse(&p, expr->scope, eflags, &nested_val); 2484 (void)Var_Parse(&p, expr->scope, eflags, &nested_val);
2482 /* TODO: handle errors */ 2485 /* TODO: handle errors */
2483 Buf_AddStr(&buf, nested_val.str); 2486 Buf_AddStr(&buf, nested_val.str);
2484 FStr_Done(&nested_val); 2487 FStr_Done(&nested_val);
2485 continue; 2488 continue;
2486 } 2489 }
2487 2490
2488 /* Ordinary text */ 2491 /* Ordinary text */
2489 Buf_AddByte(&buf, *p); 2492 Buf_AddByte(&buf, *p);
2490 p++; 2493 p++;
2491 } 2494 }
2492 *pp = p; 2495 *pp = p;
2493 2496
2494 Expr_Define(expr); 2497 Expr_Define(expr);
2495 2498
2496 if (eflags & VARE_WANTRES) 2499 if (eflags & VARE_WANTRES)
2497 Expr_SetValueOwn(expr, Buf_DoneData(&buf)); 2500 Expr_SetValueOwn(expr, Buf_DoneData(&buf));
2498 else 2501 else
2499 Buf_Done(&buf); 2502 Buf_Done(&buf);
2500 2503
2501 return AMR_OK; 2504 return AMR_OK;
2502} 2505}
2503 2506
2504/* :L */ 2507/* :L */
2505static ApplyModifierResult 2508static ApplyModifierResult
2506ApplyModifier_Literal(const char **pp, ApplyModifiersState *st) 2509ApplyModifier_Literal(const char **pp, ApplyModifiersState *st)
2507{ 2510{
2508 Expr *expr = st->expr; 2511 Expr *expr = st->expr;
2509 2512
2510 (*pp)++; 2513 (*pp)++;
2511 2514
2512 Expr_Define(expr); 2515 Expr_Define(expr);
2513 Expr_SetValueOwn(expr, bmake_strdup(expr->var->name.str)); 2516 Expr_SetValueOwn(expr, bmake_strdup(expr->var->name.str));
2514 2517
2515 return AMR_OK; 2518 return AMR_OK;
2516} 2519}
2517 2520
2518static Boolean 2521static Boolean
2519TryParseTime(const char **pp, time_t *out_time) 2522TryParseTime(const char **pp, time_t *out_time)
2520{ 2523{
2521 char *end; 2524 char *end;
2522 unsigned long n; 2525 unsigned long n;
2523 2526
2524 if (!ch_isdigit(**pp)) 2527 if (!ch_isdigit(**pp))
2525 return FALSE; 2528 return FALSE;
2526 2529
2527 errno = 0; 2530 errno = 0;
2528 n = strtoul(*pp, &end, 10); 2531 n = strtoul(*pp, &end, 10);
2529 if (n == ULONG_MAX && errno == ERANGE) 2532 if (n == ULONG_MAX && errno == ERANGE)
2530 return FALSE; 2533 return FALSE;
2531 2534
2532 *pp = end; 2535 *pp = end;
2533 *out_time = (time_t)n; /* ignore possible truncation for now */ 2536 *out_time = (time_t)n; /* ignore possible truncation for now */
2534 return TRUE; 2537 return TRUE;
2535} 2538}
2536 2539
2537/* :gmtime */ 2540/* :gmtime */
2538static ApplyModifierResult 2541static ApplyModifierResult
2539ApplyModifier_Gmtime(const char **pp, ApplyModifiersState *st) 2542ApplyModifier_Gmtime(const char **pp, ApplyModifiersState *st)
2540{ 2543{
2541 time_t utc; 2544 time_t utc;
2542 2545
2543 const char *mod = *pp; 2546 const char *mod = *pp;
2544 if (!ModMatchEq(mod, "gmtime", st)) 2547 if (!ModMatchEq(mod, "gmtime", st))
2545 return AMR_UNKNOWN; 2548 return AMR_UNKNOWN;
2546 2549
2547 if (mod[6] == '=') { 2550 if (mod[6] == '=') {
2548 const char *p = mod + 7; 2551 const char *p = mod + 7;
2549 if (!TryParseTime(&p, &utc)) { 2552 if (!TryParseTime(&p, &utc)) {
2550 Parse_Error(PARSE_FATAL, 2553 Parse_Error(PARSE_FATAL,
2551 "Invalid time value: %s", mod + 7); 2554 "Invalid time value: %s", mod + 7);
2552 return AMR_CLEANUP; 2555 return AMR_CLEANUP;
2553 } 2556 }
2554 *pp = p; 2557 *pp = p;
2555 } else { 2558 } else {
2556 utc = 0; 2559 utc = 0;
2557 *pp = mod + 6; 2560 *pp = mod + 6;
2558 } 2561 }
2559 2562
2560 Expr_SetValueOwn(st->expr, 2563 Expr_SetValueOwn(st->expr,
2561 VarStrftime(st->expr->value.str, TRUE, utc)); 2564 VarStrftime(st->expr->value.str, TRUE, utc));
2562 2565
2563 return AMR_OK; 2566 return AMR_OK;
2564} 2567}
2565 2568
2566/* :localtime */ 2569/* :localtime */
2567static ApplyModifierResult 2570static ApplyModifierResult
2568ApplyModifier_Localtime(const char **pp, ApplyModifiersState *st) 2571ApplyModifier_Localtime(const char **pp, ApplyModifiersState *st)
2569{ 2572{
2570 time_t utc; 2573 time_t utc;
2571 2574
2572 const char *mod = *pp; 2575 const char *mod = *pp;
2573 if (!ModMatchEq(mod, "localtime", st)) 2576 if (!ModMatchEq(mod, "localtime", st))
2574 return AMR_UNKNOWN; 2577 return AMR_UNKNOWN;
2575 2578
2576 if (mod[9] == '=') { 2579 if (mod[9] == '=') {
2577 const char *p = mod + 10; 2580 const char *p = mod + 10;
2578 if (!TryParseTime(&p, &utc)) { 2581 if (!TryParseTime(&p, &utc)) {
2579 Parse_Error(PARSE_FATAL, 2582 Parse_Error(PARSE_FATAL,
2580 "Invalid time value: %s", mod + 10); 2583 "Invalid time value: %s", mod + 10);
2581 return AMR_CLEANUP; 2584 return AMR_CLEANUP;
2582 } 2585 }
2583 *pp = p; 2586 *pp = p;
2584 } else { 2587 } else {
2585 utc = 0; 2588 utc = 0;
2586 *pp = mod + 9; 2589 *pp = mod + 9;
2587 } 2590 }
2588 2591
2589 Expr_SetValueOwn(st->expr, 2592 Expr_SetValueOwn(st->expr,
2590 VarStrftime(st->expr->value.str, FALSE, utc)); 2593 VarStrftime(st->expr->value.str, FALSE, utc));
2591 2594
2592 return AMR_OK; 2595 return AMR_OK;
2593} 2596}
2594 2597
2595/* :hash */ 2598/* :hash */
2596static ApplyModifierResult 2599static ApplyModifierResult
2597ApplyModifier_Hash(const char **pp, ApplyModifiersState *st) 2600ApplyModifier_Hash(const char **pp, ApplyModifiersState *st)
2598{ 2601{
2599 if (!ModMatch(*pp, "hash", st)) 2602 if (!ModMatch(*pp, "hash", st))
2600 return AMR_UNKNOWN; 2603 return AMR_UNKNOWN;
2601 *pp += 4; 2604 *pp += 4;
2602 2605
2603 Expr_SetValueOwn(st->expr, VarHash(st->expr->value.str)); 2606 Expr_SetValueOwn(st->expr, VarHash(st->expr->value.str));
2604 2607
2605 return AMR_OK; 2608 return AMR_OK;
2606} 2609}
2607 2610
2608/* :P */ 2611/* :P */
2609static ApplyModifierResult 2612static ApplyModifierResult
2610ApplyModifier_Path(const char **pp, ApplyModifiersState *st) 2613ApplyModifier_Path(const char **pp, ApplyModifiersState *st)
2611{ 2614{
2612 Expr *expr = st->expr; 2615 Expr *expr = st->expr;
2613 GNode *gn; 2616 GNode *gn;
2614 char *path; 2617 char *path;
2615 2618
2616 (*pp)++; 2619 (*pp)++;
2617 2620
2618 Expr_Define(expr); 2621 Expr_Define(expr);
2619 2622
2620 gn = Targ_FindNode(expr->var->name.str); 2623 gn = Targ_FindNode(expr->var->name.str);
2621 if (gn == NULL || gn->type & OP_NOPATH) { 2624 if (gn == NULL || gn->type & OP_NOPATH) {
2622 path = NULL; 2625 path = NULL;
2623 } else if (gn->path != NULL) { 2626 } else if (gn->path != NULL) {
2624 path = bmake_strdup(gn->path); 2627 path = bmake_strdup(gn->path);
2625 } else { 2628 } else {
2626 SearchPath *searchPath = Suff_FindPath(gn); 2629 SearchPath *searchPath = Suff_FindPath(gn);
2627 path = Dir_FindFile(expr->var->name.str, searchPath); 2630 path = Dir_FindFile(expr->var->name.str, searchPath);
2628 } 2631 }
2629 if (path == NULL) 2632 if (path == NULL)
2630 path = bmake_strdup(expr->var->name.str); 2633 path = bmake_strdup(expr->var->name.str);
2631 Expr_SetValueOwn(expr, path); 2634 Expr_SetValueOwn(expr, path);
2632 2635
2633 return AMR_OK; 2636 return AMR_OK;
2634} 2637}
2635 2638
2636/* :!cmd! */ 2639/* :!cmd! */
2637static ApplyModifierResult 2640static ApplyModifierResult
2638ApplyModifier_ShellCommand(const char **pp, ApplyModifiersState *st) 2641ApplyModifier_ShellCommand(const char **pp, ApplyModifiersState *st)
2639{ 2642{
2640 Expr *expr = st->expr; 2643 Expr *expr = st->expr;
2641 char *cmd; 2644 char *cmd;
2642 const char *errfmt; 2645 const char *errfmt;
2643 VarParseResult res; 2646 VarParseResult res;
2644 2647
2645 (*pp)++; 2648 (*pp)++;
2646 res = ParseModifierPart(pp, '!', expr->eflags, st, &cmd); 2649 res = ParseModifierPart(pp, '!', expr->eflags, st, &cmd);
2647 if (res != VPR_OK) 2650 if (res != VPR_OK)
2648 return AMR_CLEANUP; 2651 return AMR_CLEANUP;
2649 2652
2650 errfmt = NULL; 2653 errfmt = NULL;
2651 if (expr->eflags & VARE_WANTRES) 2654 if (expr->eflags & VARE_WANTRES)
2652 Expr_SetValueOwn(expr, Cmd_Exec(cmd, &errfmt)); 2655 Expr_SetValueOwn(expr, Cmd_Exec(cmd, &errfmt));
2653 else 2656 else
2654 Expr_SetValueRefer(expr, ""); 2657 Expr_SetValueRefer(expr, "");
2655 if (errfmt != NULL) 2658 if (errfmt != NULL)
2656 Error(errfmt, cmd); /* XXX: why still return AMR_OK? */ 2659 Error(errfmt, cmd); /* XXX: why still return AMR_OK? */
2657 free(cmd); 2660 free(cmd);
2658 Expr_Define(expr); 2661 Expr_Define(expr);
2659 2662
2660 return AMR_OK; 2663 return AMR_OK;
2661} 2664}
2662 2665
2663/* 2666/*
2664 * The :range modifier generates an integer sequence as long as the words. 2667 * The :range modifier generates an integer sequence as long as the words.
2665 * The :range=7 modifier generates an integer sequence from 1 to 7. 2668 * The :range=7 modifier generates an integer sequence from 1 to 7.
2666 */ 2669 */
2667static ApplyModifierResult 2670static ApplyModifierResult
2668ApplyModifier_Range(const char **pp, ApplyModifiersState *st) 2671ApplyModifier_Range(const char **pp, ApplyModifiersState *st)
2669{ 2672{
2670 size_t n; 2673 size_t n;
2671 Buffer buf; 2674 Buffer buf;
2672 size_t i; 2675 size_t i;
2673 2676
2674 const char *mod = *pp; 2677 const char *mod = *pp;
2675 if (!ModMatchEq(mod, "range", st)) 2678 if (!ModMatchEq(mod, "range", st))
2676 return AMR_UNKNOWN; 2679 return AMR_UNKNOWN;
2677 2680
2678 if (mod[5] == '=') { 2681 if (mod[5] == '=') {
2679 const char *p = mod + 6; 2682 const char *p = mod + 6;
2680 if (!TryParseSize(&p, &n)) { 2683 if (!TryParseSize(&p, &n)) {
2681 Parse_Error(PARSE_FATAL, 2684 Parse_Error(PARSE_FATAL,
2682 "Invalid number \"%s\" for ':range' modifier", 2685 "Invalid number \"%s\" for ':range' modifier",
2683 mod + 6); 2686 mod + 6);
2684 return AMR_CLEANUP; 2687 return AMR_CLEANUP;
2685 } 2688 }
2686 *pp = p; 2689 *pp = p;
2687 } else { 2690 } else {
2688 n = 0; 2691 n = 0;
2689 *pp = mod + 5; 2692 *pp = mod + 5;
2690 } 2693 }
2691 2694
2692 if (n == 0) { 2695 if (n == 0) {
2693 Words words = Str_Words(st->expr->value.str, FALSE); 2696 Words words = Str_Words(st->expr->value.str, FALSE);
2694 n = words.len; 2697 n = words.len;
2695 Words_Free(words); 2698 Words_Free(words);
2696 } 2699 }
2697 2700
2698 Buf_Init(&buf); 2701 Buf_Init(&buf);
2699 2702
2700 for (i = 0; i < n; i++) { 2703 for (i = 0; i < n; i++) {
2701 if (i != 0) { 2704 if (i != 0) {
2702 /* XXX: Use st->sep instead of ' ', for consistency. */ 2705 /* XXX: Use st->sep instead of ' ', for consistency. */
2703 Buf_AddByte(&buf, ' '); 2706 Buf_AddByte(&buf, ' ');
2704 } 2707 }
2705 Buf_AddInt(&buf, 1 + (int)i); 2708 Buf_AddInt(&buf, 1 + (int)i);
2706 } 2709 }
2707 2710
2708 Expr_SetValueOwn(st->expr, Buf_DoneData(&buf)); 2711 Expr_SetValueOwn(st->expr, Buf_DoneData(&buf));
2709 return AMR_OK; 2712 return AMR_OK;
2710} 2713}
2711 2714
2712/* Parse a ':M' or ':N' modifier. */ 2715/* Parse a ':M' or ':N' modifier. */
2713static void 2716static void
2714ParseModifier_Match(const char **pp, const ApplyModifiersState *st, 2717ParseModifier_Match(const char **pp, const ApplyModifiersState *st,
2715 char **out_pattern) 2718 char **out_pattern)
2716{ 2719{
2717 const char *mod = *pp; 2720 const char *mod = *pp;
2718 Expr *expr = st->expr; 2721 Expr *expr = st->expr;
2719 Boolean copy = FALSE; /* pattern should be, or has been, copied */ 2722 Boolean copy = FALSE; /* pattern should be, or has been, copied */
2720 Boolean needSubst = FALSE; 2723 Boolean needSubst = FALSE;
2721 const char *endpat; 2724 const char *endpat;
2722 char *pattern; 2725 char *pattern;
2723 2726
2724 /* 2727 /*
2725 * In the loop below, ignore ':' unless we are at (or back to) the 2728 * In the loop below, ignore ':' unless we are at (or back to) the
2726 * original brace level. 2729 * original brace level.
2727 * XXX: This will likely not work right if $() and ${} are intermixed. 2730 * XXX: This will likely not work right if $() and ${} are intermixed.
2728 */ 2731 */
2729 /* 2732 /*
2730 * XXX: This code is similar to the one in Var_Parse. 2733 * XXX: This code is similar to the one in Var_Parse.
2731 * See if the code can be merged. 2734 * See if the code can be merged.
2732 * See also ApplyModifier_Defined. 2735 * See also ApplyModifier_Defined.
2733 */ 2736 */
2734 int nest = 0; 2737 int nest = 0;
2735 const char *p; 2738 const char *p;
2736 for (p = mod + 1; *p != '\0' && !(*p == ':' && nest == 0); p++) { 2739 for (p = mod + 1; *p != '\0' && !(*p == ':' && nest == 0); p++) {
2737 if (*p == '\\' && 2740 if (*p == '\\' &&
2738 (IsDelimiter(p[1], st) || p[1] == st->startc)) { 2741 (IsDelimiter(p[1], st) || p[1] == st->startc)) {
2739 if (!needSubst) 2742 if (!needSubst)
2740 copy = TRUE; 2743 copy = TRUE;
2741 p++; 2744 p++;
2742 continue; 2745 continue;
2743 } 2746 }
2744 if (*p == '$') 2747 if (*p == '$')
2745 needSubst = TRUE; 2748 needSubst = TRUE;
2746 if (*p == '(' || *p == '{') 2749 if (*p == '(' || *p == '{')
2747 nest++; 2750 nest++;
2748 if (*p == ')' || *p == '}') { 2751 if (*p == ')' || *p == '}') {
2749 nest--; 2752 nest--;
2750 if (nest < 0) 2753 if (nest < 0)
2751 break; 2754 break;
2752 } 2755 }
2753 } 2756 }
2754 *pp = p; 2757 *pp = p;
2755 endpat = p; 2758 endpat = p;
2756 2759
2757 if (copy) { 2760 if (copy) {
2758 char *dst; 2761 char *dst;
2759 const char *src; 2762 const char *src;
2760 2763
2761 /* Compress the \:'s out of the pattern. */ 2764 /* Compress the \:'s out of the pattern. */
2762 pattern = bmake_malloc((size_t)(endpat - (mod + 1)) + 1); 2765 pattern = bmake_malloc((size_t)(endpat - (mod + 1)) + 1);
2763 dst = pattern; 2766 dst = pattern;
2764 src = mod + 1; 2767 src = mod + 1;
2765 for (; src < endpat; src++, dst++) { 2768 for (; src < endpat; src++, dst++) {
2766 if (src[0] == '\\' && src + 1 < endpat && 2769 if (src[0] == '\\' && src + 1 < endpat &&
2767 /* XXX: st->startc is missing here; see above */ 2770 /* XXX: st->startc is missing here; see above */
2768 IsDelimiter(src[1], st)) 2771 IsDelimiter(src[1], st))
2769 src++; 2772 src++;
2770 *dst = *src; 2773 *dst = *src;
2771 } 2774 }
2772 *dst = '\0'; 2775 *dst = '\0';
2773 } else { 2776 } else {
2774 pattern = bmake_strsedup(mod + 1, endpat); 2777 pattern = bmake_strsedup(mod + 1, endpat);
2775 } 2778 }
2776 2779
2777 if (needSubst) { 2780 if (needSubst) {
2778 char *old_pattern = pattern; 2781 char *old_pattern = pattern;
2779 (void)Var_Subst(pattern, expr->scope, expr->eflags, &pattern); 2782 (void)Var_Subst(pattern, expr->scope, expr->eflags, &pattern);
2780 /* TODO: handle errors */ 2783 /* TODO: handle errors */
2781 free(old_pattern); 2784 free(old_pattern);
2782 } 2785 }
2783 2786
2784 DEBUG3(VAR, "Pattern[%s] for [%s] is [%s]\n", 2787 DEBUG3(VAR, "Pattern[%s] for [%s] is [%s]\n",
2785 expr->var->name.str, expr->value.str, pattern); 2788 expr->var->name.str, expr->value.str, pattern);
2786 2789
2787 *out_pattern = pattern; 2790 *out_pattern = pattern;
2788} 2791}
2789 2792
2790/* :Mpattern or :Npattern */ 2793/* :Mpattern or :Npattern */
2791static ApplyModifierResult 2794static ApplyModifierResult
2792ApplyModifier_Match(const char **pp, ApplyModifiersState *st) 2795ApplyModifier_Match(const char **pp, ApplyModifiersState *st)
2793{ 2796{
2794 const char *mod = *pp; 2797 const char *mod = *pp;
2795 char *pattern; 2798 char *pattern;
2796 ModifyWordProc modifyWord; 2799 ModifyWordProc modifyWord;
2797 2800
2798 ParseModifier_Match(pp, st, &pattern); 2801 ParseModifier_Match(pp, st, &pattern);
2799 2802
2800 modifyWord = mod[0] == 'M' ? ModifyWord_Match : ModifyWord_NoMatch; 2803 modifyWord = mod[0] == 'M' ? ModifyWord_Match : ModifyWord_NoMatch;
2801 ModifyWords(st, modifyWord, pattern, st->oneBigWord); 2804 ModifyWords(st, modifyWord, pattern, st->oneBigWord);
2802 free(pattern); 2805 free(pattern);
2803 return AMR_OK; 2806 return AMR_OK;
2804} 2807}
2805 2808
2806static void 2809static void
2807ParsePatternFlags(const char **pp, VarPatternFlags *pflags, Boolean *oneBigWord) 2810ParsePatternFlags(const char **pp, VarPatternFlags *pflags, Boolean *oneBigWord)
2808{ 2811{
2809 for (;; (*pp)++) { 2812 for (;; (*pp)++) {
2810 if (**pp == 'g') 2813 if (**pp == 'g')
2811 pflags->subGlobal = TRUE; 2814 pflags->subGlobal = TRUE;
2812 else if (**pp == '1') 2815 else if (**pp == '1')
2813 pflags->subOnce = TRUE; 2816 pflags->subOnce = TRUE;
2814 else if (**pp == 'W') 2817 else if (**pp == 'W')
2815 *oneBigWord = TRUE; 2818 *oneBigWord = TRUE;
2816 else 2819 else
2817 break; 2820 break;
2818 } 2821 }
2819} 2822}
2820 2823
2821/* :S,from,to, */ 2824/* :S,from,to, */
2822static ApplyModifierResult 2825static ApplyModifierResult
2823ApplyModifier_Subst(const char **pp, ApplyModifiersState *st) 2826ApplyModifier_Subst(const char **pp, ApplyModifiersState *st)
2824{ 2827{
2825 struct ModifyWord_SubstArgs args; 2828 struct ModifyWord_SubstArgs args;
2826 char *lhs, *rhs; 2829 char *lhs, *rhs;
2827 Boolean oneBigWord; 2830 Boolean oneBigWord;
2828 VarParseResult res; 2831 VarParseResult res;
2829 2832
2830 char delim = (*pp)[1]; 2833 char delim = (*pp)[1];
2831 if (delim == '\0') { 2834 if (delim == '\0') {
2832 Error("Missing delimiter for modifier ':S'"); 2835 Error("Missing delimiter for modifier ':S'");
2833 (*pp)++; 2836 (*pp)++;
2834 return AMR_CLEANUP; 2837 return AMR_CLEANUP;
2835 } 2838 }
2836 2839
2837 *pp += 2; 2840 *pp += 2;
2838 2841
2839 args.pflags = (VarPatternFlags){ FALSE, FALSE, FALSE, FALSE }; 2842 args.pflags = (VarPatternFlags){ FALSE, FALSE, FALSE, FALSE };
2840 args.matched = FALSE; 2843 args.matched = FALSE;
2841 2844
2842 if (**pp == '^') { 2845 if (**pp == '^') {
2843 args.pflags.anchorStart = TRUE; 2846 args.pflags.anchorStart = TRUE;
2844 (*pp)++; 2847 (*pp)++;
2845 } 2848 }
2846 2849
2847 res = ParseModifierPartSubst(pp, delim, st->expr->eflags, st, &lhs, 2850 res = ParseModifierPartSubst(pp, delim, st->expr->eflags, st, &lhs,
2848 &args.lhsLen, &args.pflags, NULL); 2851 &args.lhsLen, &args.pflags, NULL);
2849 if (res != VPR_OK) 2852 if (res != VPR_OK)
2850 return AMR_CLEANUP; 2853 return AMR_CLEANUP;
2851 args.lhs = lhs; 2854 args.lhs = lhs;
2852 2855
2853 res = ParseModifierPartSubst(pp, delim, st->expr->eflags, st, &rhs, 2856 res = ParseModifierPartSubst(pp, delim, st->expr->eflags, st, &rhs,
2854 &args.rhsLen, NULL, &args); 2857 &args.rhsLen, NULL, &args);
2855 if (res != VPR_OK) 2858 if (res != VPR_OK)
2856 return AMR_CLEANUP; 2859 return AMR_CLEANUP;
2857 args.rhs = rhs; 2860 args.rhs = rhs;
2858 2861
2859 oneBigWord = st->oneBigWord; 2862 oneBigWord = st->oneBigWord;
2860 ParsePatternFlags(pp, &args.pflags, &oneBigWord); 2863 ParsePatternFlags(pp, &args.pflags, &oneBigWord);
2861 2864
2862 ModifyWords(st, ModifyWord_Subst, &args, oneBigWord); 2865 ModifyWords(st, ModifyWord_Subst, &args, oneBigWord);
2863 2866
2864 free(lhs); 2867 free(lhs);
2865 free(rhs); 2868 free(rhs);
2866 return AMR_OK; 2869 return AMR_OK;
2867} 2870}
2868 2871
2869#ifndef NO_REGEX 2872#ifndef NO_REGEX
2870 2873
2871/* :C,from,to, */ 2874/* :C,from,to, */
2872static ApplyModifierResult 2875static ApplyModifierResult
2873ApplyModifier_Regex(const char **pp, ApplyModifiersState *st) 2876ApplyModifier_Regex(const char **pp, ApplyModifiersState *st)
2874{ 2877{
2875 char *re; 2878 char *re;
2876 struct ModifyWord_SubstRegexArgs args; 2879 struct ModifyWord_SubstRegexArgs args;
2877 Boolean oneBigWord; 2880 Boolean oneBigWord;
2878 int error; 2881 int error;
2879 VarParseResult res; 2882 VarParseResult res;
2880 2883
2881 char delim = (*pp)[1]; 2884 char delim = (*pp)[1];
2882 if (delim == '\0') { 2885 if (delim == '\0') {
2883 Error("Missing delimiter for :C modifier"); 2886 Error("Missing delimiter for :C modifier");
2884 (*pp)++; 2887 (*pp)++;
2885 return AMR_CLEANUP; 2888 return AMR_CLEANUP;
2886 } 2889 }
2887 2890
2888 *pp += 2; 2891 *pp += 2;
2889 2892
2890 res = ParseModifierPart(pp, delim, st->expr->eflags, st, &re); 2893 res = ParseModifierPart(pp, delim, st->expr->eflags, st, &re);
2891 if (res != VPR_OK) 2894 if (res != VPR_OK)
2892 return AMR_CLEANUP; 2895 return AMR_CLEANUP;
2893 2896
2894 res = ParseModifierPart(pp, delim, st->expr->eflags, st, &args.replace); 2897 res = ParseModifierPart(pp, delim, st->expr->eflags, st, &args.replace);
2895 if (args.replace == NULL) { 2898 if (args.replace == NULL) {
2896 free(re); 2899 free(re);
2897 return AMR_CLEANUP; 2900 return AMR_CLEANUP;
2898 } 2901 }
2899 2902
2900 args.pflags = (VarPatternFlags){ FALSE, FALSE, FALSE, FALSE }; 2903 args.pflags = (VarPatternFlags){ FALSE, FALSE, FALSE, FALSE };
2901 args.matched = FALSE; 2904 args.matched = FALSE;
2902 oneBigWord = st->oneBigWord; 2905 oneBigWord = st->oneBigWord;
2903 ParsePatternFlags(pp, &args.pflags, &oneBigWord); 2906 ParsePatternFlags(pp, &args.pflags, &oneBigWord);
2904 2907
2905 if (!(st->expr->eflags & VARE_WANTRES)) { 2908 if (!(st->expr->eflags & VARE_WANTRES)) {
2906 free(args.replace); 2909 free(args.replace);
2907 free(re); 2910 free(re);
2908 return AMR_OK; 2911 return AMR_OK;
2909 } 2912 }
2910 2913
2911 error = regcomp(&args.re, re, REG_EXTENDED); 2914 error = regcomp(&args.re, re, REG_EXTENDED);
2912 free(re); 2915 free(re);
2913 if (error != 0) { 2916 if (error != 0) {
2914 VarREError(error, &args.re, "Regex compilation error"); 2917 VarREError(error, &args.re, "Regex compilation error");
2915 free(args.replace); 2918 free(args.replace);
2916 return AMR_CLEANUP; 2919 return AMR_CLEANUP;
2917 } 2920 }
2918 2921
2919 args.nsub = args.re.re_nsub + 1; 2922 args.nsub = args.re.re_nsub + 1;
2920 if (args.nsub > 10) 2923 if (args.nsub > 10)
2921 args.nsub = 10; 2924 args.nsub = 10;
2922 2925
2923 ModifyWords(st, ModifyWord_SubstRegex, &args, oneBigWord); 2926 ModifyWords(st, ModifyWord_SubstRegex, &args, oneBigWord);
2924 2927
2925 regfree(&args.re); 2928 regfree(&args.re);
2926 free(args.replace); 2929 free(args.replace);
2927 return AMR_OK; 2930 return AMR_OK;
2928} 2931}
2929 2932
2930#endif 2933#endif
2931 2934
2932/* :Q, :q */ 2935/* :Q, :q */
2933static ApplyModifierResult 2936static ApplyModifierResult
2934ApplyModifier_Quote(const char **pp, ApplyModifiersState *st) 2937ApplyModifier_Quote(const char **pp, ApplyModifiersState *st)
2935{ 2938{
2936 Boolean quoteDollar = **pp == 'q'; 2939 Boolean quoteDollar = **pp == 'q';
2937 if (!IsDelimiter((*pp)[1], st)) 2940 if (!IsDelimiter((*pp)[1], st))
2938 return AMR_UNKNOWN; 2941 return AMR_UNKNOWN;
2939 (*pp)++; 2942 (*pp)++;
2940 2943
2941 Expr_SetValueOwn(st->expr, VarQuote(st->expr->value.str, quoteDollar)); 2944 Expr_SetValueOwn(st->expr, VarQuote(st->expr->value.str, quoteDollar));
2942 2945
2943 return AMR_OK; 2946 return AMR_OK;
2944} 2947}
2945 2948
2946/*ARGSUSED*/ 2949/*ARGSUSED*/
2947static void 2950static void
2948ModifyWord_Copy(const char *word, SepBuf *buf, void *data MAKE_ATTR_UNUSED) 2951ModifyWord_Copy(const char *word, SepBuf *buf, void *data MAKE_ATTR_UNUSED)
2949{ 2952{
2950 SepBuf_AddStr(buf, word); 2953 SepBuf_AddStr(buf, word);
2951} 2954}
2952 2955
2953/* :ts<separator> */ 2956/* :ts<separator> */
2954static ApplyModifierResult 2957static ApplyModifierResult
2955ApplyModifier_ToSep(const char **pp, ApplyModifiersState *st) 2958ApplyModifier_ToSep(const char **pp, ApplyModifiersState *st)
2956{ 2959{
2957 const char *sep = *pp + 2; 2960 const char *sep = *pp + 2;
2958 2961
2959 /* ":ts<any><endc>" or ":ts<any>:" */ 2962 /* ":ts<any><endc>" or ":ts<any>:" */
2960 if (sep[0] != st->endc && IsDelimiter(sep[1], st)) { 2963 if (sep[0] != st->endc && IsDelimiter(sep[1], st)) {
2961 *pp = sep + 1; 2964 *pp = sep + 1;
2962 st->sep = sep[0]; 2965 st->sep = sep[0];
2963 goto ok; 2966 goto ok;
2964 } 2967 }
2965 2968
2966 /* ":ts<endc>" or ":ts:" */ 2969 /* ":ts<endc>" or ":ts:" */
2967 if (IsDelimiter(sep[0], st)) { 2970 if (IsDelimiter(sep[0], st)) {
2968 *pp = sep; 2971 *pp = sep;
2969 st->sep = '\0'; /* no separator */ 2972 st->sep = '\0'; /* no separator */
2970 goto ok; 2973 goto ok;
2971 } 2974 }
2972 2975
2973 /* ":ts<unrecognised><unrecognised>". */ 2976 /* ":ts<unrecognised><unrecognised>". */
2974 if (sep[0] != '\\') { 2977 if (sep[0] != '\\') {
2975 (*pp)++; /* just for backwards compatibility */ 2978 (*pp)++; /* just for backwards compatibility */
2976 return AMR_BAD; 2979 return AMR_BAD;
2977 } 2980 }
2978 2981
2979 /* ":ts\n" */ 2982 /* ":ts\n" */
2980 if (sep[1] == 'n') { 2983 if (sep[1] == 'n') {
2981 *pp = sep + 2; 2984 *pp = sep + 2;
2982 st->sep = '\n'; 2985 st->sep = '\n';
2983 goto ok; 2986 goto ok;
2984 } 2987 }
2985 2988
2986 /* ":ts\t" */ 2989 /* ":ts\t" */
2987 if (sep[1] == 't') { 2990 if (sep[1] == 't') {
2988 *pp = sep + 2; 2991 *pp = sep + 2;
2989 st->sep = '\t'; 2992 st->sep = '\t';
2990 goto ok; 2993 goto ok;
2991 } 2994 }
2992 2995
2993 /* ":ts\x40" or ":ts\100" */ 2996 /* ":ts\x40" or ":ts\100" */
2994 { 2997 {
2995 const char *p = sep + 1; 2998 const char *p = sep + 1;
2996 int base = 8; /* assume octal */ 2999 int base = 8; /* assume octal */
2997 3000
2998 if (sep[1] == 'x') { 3001 if (sep[1] == 'x') {
2999 base = 16; 3002 base = 16;
3000 p++; 3003 p++;
3001 } else if (!ch_isdigit(sep[1])) { 3004 } else if (!ch_isdigit(sep[1])) {
3002 (*pp)++; /* just for backwards compatibility */ 3005 (*pp)++; /* just for backwards compatibility */
3003 return AMR_BAD; /* ":ts<backslash><unrecognised>". */ 3006 return AMR_BAD; /* ":ts<backslash><unrecognised>". */
3004 } 3007 }
3005 3008
3006 if (!TryParseChar(&p, base, &st->sep)) { 3009 if (!TryParseChar(&p, base, &st->sep)) {
3007 Parse_Error(PARSE_FATAL, 3010 Parse_Error(PARSE_FATAL,
3008 "Invalid character number: %s", p); 3011 "Invalid character number: %s", p);
3009 return AMR_CLEANUP; 3012 return AMR_CLEANUP;
3010 } 3013 }
3011 if (!IsDelimiter(*p, st)) { 3014 if (!IsDelimiter(*p, st)) {
3012 (*pp)++; /* just for backwards compatibility */ 3015 (*pp)++; /* just for backwards compatibility */
3013 return AMR_BAD; 3016 return AMR_BAD;
3014 } 3017 }
3015 3018
3016 *pp = p; 3019 *pp = p;
3017 } 3020 }
3018 3021
3019ok: 3022ok:
3020 ModifyWords(st, ModifyWord_Copy, NULL, st->oneBigWord); 3023 ModifyWords(st, ModifyWord_Copy, NULL, st->oneBigWord);
3021 return AMR_OK; 3024 return AMR_OK;
3022} 3025}
3023 3026
3024static char * 3027static char *
3025str_toupper(const char *str) 3028str_toupper(const char *str)
3026{ 3029{
3027 char *res; 3030 char *res;
3028 size_t i, len; 3031 size_t i, len;
3029 3032
3030 len = strlen(str); 3033 len = strlen(str);
3031 res = bmake_malloc(len + 1); 3034 res = bmake_malloc(len + 1);
3032 for (i = 0; i < len + 1; i++) 3035 for (i = 0; i < len + 1; i++)
3033 res[i] = ch_toupper(str[i]); 3036 res[i] = ch_toupper(str[i]);
3034 3037
3035 return res; 3038 return res;
3036} 3039}
3037 3040
3038static char * 3041static char *
3039str_tolower(const char *str) 3042str_tolower(const char *str)
3040{ 3043{
3041 char *res; 3044 char *res;
3042 size_t i, len; 3045 size_t i, len;
3043 3046
3044 len = strlen(str); 3047 len = strlen(str);
3045 res = bmake_malloc(len + 1); 3048 res = bmake_malloc(len + 1);
3046 for (i = 0; i < len + 1; i++) 3049 for (i = 0; i < len + 1; i++)
3047 res[i] = ch_tolower(str[i]); 3050 res[i] = ch_tolower(str[i]);
3048 3051
3049 return res; 3052 return res;
3050} 3053}
3051 3054
3052/* :tA, :tu, :tl, :ts<separator>, etc. */ 3055/* :tA, :tu, :tl, :ts<separator>, etc. */
3053static ApplyModifierResult 3056static ApplyModifierResult
3054ApplyModifier_To(const char **pp, ApplyModifiersState *st) 3057ApplyModifier_To(const char **pp, ApplyModifiersState *st)
3055{ 3058{
3056 Expr *expr = st->expr; 3059 Expr *expr = st->expr;
3057 const char *mod = *pp; 3060 const char *mod = *pp;
3058 assert(mod[0] == 't'); 3061 assert(mod[0] == 't');
3059 3062
3060 if (IsDelimiter(mod[1], st) || mod[1] == '\0') { 3063 if (IsDelimiter(mod[1], st) || mod[1] == '\0') {
3061 *pp = mod + 1; 3064 *pp = mod + 1;
3062 return AMR_BAD; /* Found ":t<endc>" or ":t:". */ 3065 return AMR_BAD; /* Found ":t<endc>" or ":t:". */
3063 } 3066 }
3064 3067
3065 if (mod[1] == 's') 3068 if (mod[1] == 's')
3066 return ApplyModifier_ToSep(pp, st); 3069 return ApplyModifier_ToSep(pp, st);
3067 3070
3068 if (!IsDelimiter(mod[2], st)) { /* :t<unrecognized> */ 3071 if (!IsDelimiter(mod[2], st)) { /* :t<unrecognized> */
3069 *pp = mod + 1; 3072 *pp = mod + 1;
3070 return AMR_BAD; 3073 return AMR_BAD;
3071 } 3074 }
3072 3075
3073 if (mod[1] == 'A') { /* :tA */ 3076 if (mod[1] == 'A') { /* :tA */
3074 *pp = mod + 2; 3077 *pp = mod + 2;
3075 ModifyWords(st, ModifyWord_Realpath, NULL, st->oneBigWord); 3078 ModifyWords(st, ModifyWord_Realpath, NULL, st->oneBigWord);
3076 return AMR_OK; 3079 return AMR_OK;
3077 } 3080 }
3078 3081
3079 if (mod[1] == 'u') { /* :tu */ 3082 if (mod[1] == 'u') { /* :tu */
3080 *pp = mod + 2; 3083 *pp = mod + 2;
3081 Expr_SetValueOwn(expr, str_toupper(expr->value.str)); 3084 Expr_SetValueOwn(expr, str_toupper(expr->value.str));
3082 return AMR_OK; 3085 return AMR_OK;
3083 } 3086 }
3084 3087
3085 if (mod[1] == 'l') { /* :tl */ 3088 if (mod[1] == 'l') { /* :tl */
3086 *pp = mod + 2; 3089 *pp = mod + 2;
3087 Expr_SetValueOwn(expr, str_tolower(expr->value.str)); 3090 Expr_SetValueOwn(expr, str_tolower(expr->value.str));
3088 return AMR_OK; 3091 return AMR_OK;
3089 } 3092 }
3090 3093
3091 if (mod[1] == 'W' || mod[1] == 'w') { /* :tW, :tw */ 3094 if (mod[1] == 'W' || mod[1] == 'w') { /* :tW, :tw */
3092 *pp = mod + 2; 3095 *pp = mod + 2;
3093 st->oneBigWord = mod[1] == 'W'; 3096 st->oneBigWord = mod[1] == 'W';
3094 return AMR_OK; 3097 return AMR_OK;
3095 } 3098 }
3096 3099
3097 /* Found ":t<unrecognised>:" or ":t<unrecognised><endc>". */ 3100 /* Found ":t<unrecognised>:" or ":t<unrecognised><endc>". */
3098 *pp = mod + 1; /* XXX: unnecessary but observable */ 3101 *pp = mod + 1; /* XXX: unnecessary but observable */
3099 return AMR_BAD; 3102 return AMR_BAD;
3100} 3103}
3101 3104
3102/* :[#], :[1], :[-1..1], etc. */ 3105/* :[#], :[1], :[-1..1], etc. */
3103static ApplyModifierResult 3106static ApplyModifierResult
3104ApplyModifier_Words(const char **pp, ApplyModifiersState *st) 3107ApplyModifier_Words(const char **pp, ApplyModifiersState *st)
3105{ 3108{
3106 Expr *expr = st->expr; 3109 Expr *expr = st->expr;
3107 char *estr; 3110 char *estr;
3108 int first, last; 3111 int first, last;
3109 VarParseResult res; 3112 VarParseResult res;
3110 const char *p; 3113 const char *p;
3111 3114
3112 (*pp)++; /* skip the '[' */ 3115 (*pp)++; /* skip the '[' */
3113 res = ParseModifierPart(pp, ']', expr->eflags, st, &estr); 3116 res = ParseModifierPart(pp, ']', expr->eflags, st, &estr);
3114 if (res != VPR_OK) 3117 if (res != VPR_OK)
3115 return AMR_CLEANUP; 3118 return AMR_CLEANUP;
3116 3119
3117 if (!IsDelimiter(**pp, st)) 3120 if (!IsDelimiter(**pp, st))
3118 goto bad_modifier; /* Found junk after ']' */ 3121 goto bad_modifier; /* Found junk after ']' */
3119 3122
3120 if (estr[0] == '\0') 3123 if (estr[0] == '\0')
3121 goto bad_modifier; /* Found ":[]". */ 3124 goto bad_modifier; /* Found ":[]". */
3122 3125
3123 if (estr[0] == '#' && estr[1] == '\0') { /* Found ":[#]" */ 3126 if (estr[0] == '#' && estr[1] == '\0') { /* Found ":[#]" */
3124 if (st->oneBigWord) { 3127 if (st->oneBigWord) {
3125 Expr_SetValueRefer(expr, "1"); 3128 Expr_SetValueRefer(expr, "1");
3126 } else { 3129 } else {
3127 Buffer buf; 3130 Buffer buf;
3128 3131
3129 Words words = Str_Words(expr->value.str, FALSE); 3132 Words words = Str_Words(expr->value.str, FALSE);
3130 size_t ac = words.len; 3133 size_t ac = words.len;
3131 Words_Free(words); 3134 Words_Free(words);
3132 3135
3133 /* 3 digits + '\0' is usually enough */ 3136 /* 3 digits + '\0' is usually enough */
3134 Buf_InitSize(&buf, 4); 3137 Buf_InitSize(&buf, 4);
3135 Buf_AddInt(&buf, (int)ac); 3138 Buf_AddInt(&buf, (int)ac);
3136 Expr_SetValueOwn(expr, Buf_DoneData(&buf)); 3139 Expr_SetValueOwn(expr, Buf_DoneData(&buf));
3137 } 3140 }
3138 goto ok; 3141 goto ok;
3139 } 3142 }
3140 3143
3141 if (estr[0] == '*' && estr[1] == '\0') { /* Found ":[*]" */ 3144 if (estr[0] == '*' && estr[1] == '\0') { /* Found ":[*]" */
3142 st->oneBigWord = TRUE; 3145 st->oneBigWord = TRUE;
3143 goto ok; 3146 goto ok;
3144 } 3147 }
3145 3148
3146 if (estr[0] == '@' && estr[1] == '\0') { /* Found ":[@]" */ 3149 if (estr[0] == '@' && estr[1] == '\0') { /* Found ":[@]" */
3147 st->oneBigWord = FALSE; 3150 st->oneBigWord = FALSE;
3148 goto ok; 3151 goto ok;
3149 } 3152 }
3150 3153
3151 /* 3154 /*
3152 * We expect estr to contain a single integer for :[N], or two 3155 * We expect estr to contain a single integer for :[N], or two
3153 * integers separated by ".." for :[start..end]. 3156 * integers separated by ".." for :[start..end].
3154 */ 3157 */
3155 p = estr; 3158 p = estr;
3156 if (!TryParseIntBase0(&p, &first)) 3159 if (!TryParseIntBase0(&p, &first))
3157 goto bad_modifier; /* Found junk instead of a number */ 3160 goto bad_modifier; /* Found junk instead of a number */
3158 3161
3159 if (p[0] == '\0') { /* Found only one integer in :[N] */ 3162 if (p[0] == '\0') { /* Found only one integer in :[N] */
3160 last = first; 3163 last = first;
3161 } else if (p[0] == '.' && p[1] == '.' && p[2] != '\0') { 3164 } else if (p[0] == '.' && p[1] == '.' && p[2] != '\0') {
3162 /* Expecting another integer after ".." */ 3165 /* Expecting another integer after ".." */
3163 p += 2; 3166 p += 2;
3164 if (!TryParseIntBase0(&p, &last) || *p != '\0') 3167 if (!TryParseIntBase0(&p, &last) || *p != '\0')
3165 goto bad_modifier; /* Found junk after ".." */ 3168 goto bad_modifier; /* Found junk after ".." */
3166 } else 3169 } else
3167 goto bad_modifier; /* Found junk instead of ".." */ 3170 goto bad_modifier; /* Found junk instead of ".." */
3168 3171
3169 /* 3172 /*
3170 * Now first and last are properly filled in, but we still have to 3173 * Now first and last are properly filled in, but we still have to
3171 * check for 0 as a special case. 3174 * check for 0 as a special case.
3172 */ 3175 */
3173 if (first == 0 && last == 0) { 3176 if (first == 0 && last == 0) {
3174 /* ":[0]" or perhaps ":[0..0]" */ 3177 /* ":[0]" or perhaps ":[0..0]" */
3175 st->oneBigWord = TRUE; 3178 st->oneBigWord = TRUE;
3176 goto ok; 3179 goto ok;
3177 } 3180 }
3178 3181
3179 /* ":[0..N]" or ":[N..0]" */ 3182 /* ":[0..N]" or ":[N..0]" */
3180 if (first == 0 || last == 0) 3183 if (first == 0 || last == 0)
3181 goto bad_modifier; 3184 goto bad_modifier;
3182 3185
3183 /* Normal case: select the words described by first and last. */ 3186 /* Normal case: select the words described by first and last. */
3184 Expr_SetValueOwn(expr, 3187 Expr_SetValueOwn(expr,
3185 VarSelectWords(expr->value.str, first, last, 3188 VarSelectWords(expr->value.str, first, last,
3186 st->sep, st->oneBigWord)); 3189 st->sep, st->oneBigWord));
3187 3190
3188ok: 3191ok:
3189 free(estr); 3192 free(estr);
3190 return AMR_OK; 3193 return AMR_OK;
3191 3194
3192bad_modifier: 3195bad_modifier:
3193 free(estr); 3196 free(estr);
3194 return AMR_BAD; 3197 return AMR_BAD;
3195} 3198}
3196 3199
3197static int 3200static int
3198str_cmp_asc(const void *a, const void *b) 3201str_cmp_asc(const void *a, const void *b)
3199{ 3202{
3200 return strcmp(*(const char *const *)a, *(const char *const *)b); 3203 return strcmp(*(const char *const *)a, *(const char *const *)b);
3201} 3204}
3202 3205
3203static int 3206static int
3204str_cmp_desc(const void *a, const void *b) 3207str_cmp_desc(const void *a, const void *b)
3205{ 3208{
3206 return strcmp(*(const char *const *)b, *(const char *const *)a); 3209 return strcmp(*(const char *const *)b, *(const char *const *)a);
3207} 3210}
3208 3211
3209static void 3212static void
3210ShuffleStrings(char **strs, size_t n) 3213ShuffleStrings(char **strs, size_t n)
3211{ 3214{
3212 size_t i; 3215 size_t i;
3213 3216
3214 for (i = n - 1; i > 0; i--) { 3217 for (i = n - 1; i > 0; i--) {
3215 size_t rndidx = (size_t)random() % (i + 1); 3218 size_t rndidx = (size_t)random() % (i + 1);
3216 char *t = strs[i]; 3219 char *t = strs[i];
3217 strs[i] = strs[rndidx]; 3220 strs[i] = strs[rndidx];
3218 strs[rndidx] = t; 3221 strs[rndidx] = t;
3219 } 3222 }
3220} 3223}
3221 3224
3222/* :O (order ascending) or :Or (order descending) or :Ox (shuffle) */ 3225/* :O (order ascending) or :Or (order descending) or :Ox (shuffle) */
3223static ApplyModifierResult 3226static ApplyModifierResult
3224ApplyModifier_Order(const char **pp, ApplyModifiersState *st) 3227ApplyModifier_Order(const char **pp, ApplyModifiersState *st)
3225{ 3228{
3226 const char *mod = (*pp)++; /* skip past the 'O' in any case */ 3229 const char *mod = (*pp)++; /* skip past the 'O' in any case */
3227 3230
3228 /* TODO: separate parsing from evaluating */ 3231 /* TODO: separate parsing from evaluating */
3229 3232
3230 Words words = Str_Words(st->expr->value.str, FALSE); 3233 Words words = Str_Words(st->expr->value.str, FALSE);
3231 3234
3232 if (IsDelimiter(mod[1], st)) { 3235 if (IsDelimiter(mod[1], st)) {
3233 /* :O sorts ascending */ 3236 /* :O sorts ascending */
3234 qsort(words.words, words.len, sizeof words.words[0], 3237 qsort(words.words, words.len, sizeof words.words[0],
3235 str_cmp_asc); 3238 str_cmp_asc);
3236 3239
3237 } else if ((mod[1] == 'r' || mod[1] == 'x') && 3240 } else if ((mod[1] == 'r' || mod[1] == 'x') &&
3238 IsDelimiter(mod[2], st)) { 3241 IsDelimiter(mod[2], st)) {
3239 (*pp)++; 3242 (*pp)++;
3240 3243
3241 if (mod[1] == 'r') { /* :Or sorts descending */ 3244 if (mod[1] == 'r') { /* :Or sorts descending */
3242 qsort(words.words, words.len, sizeof words.words[0], 3245 qsort(words.words, words.len, sizeof words.words[0],
3243 str_cmp_desc); 3246 str_cmp_desc);
3244 } else 3247 } else
3245 ShuffleStrings(words.words, words.len); 3248 ShuffleStrings(words.words, words.len);
3246 } else { 3249 } else {
3247 Words_Free(words); 3250 Words_Free(words);
3248 return AMR_BAD; 3251 return AMR_BAD;
3249 } 3252 }
3250 3253
3251 Expr_SetValueOwn(st->expr, Words_JoinFree(words)); 3254 Expr_SetValueOwn(st->expr, Words_JoinFree(words));
3252 return AMR_OK; 3255 return AMR_OK;
3253} 3256}
3254 3257
3255/* :? then : else */ 3258/* :? then : else */
3256static ApplyModifierResult 3259static ApplyModifierResult
3257ApplyModifier_IfElse(const char **pp, ApplyModifiersState *st) 3260ApplyModifier_IfElse(const char **pp, ApplyModifiersState *st)
3258{ 3261{
3259 Expr *expr = st->expr; 3262 Expr *expr = st->expr;
3260 char *then_expr, *else_expr; 3263 char *then_expr, *else_expr;
3261 VarParseResult res; 3264 VarParseResult res;
3262 3265
3263 Boolean value = FALSE; 3266 Boolean value = FALSE;
3264 VarEvalFlags then_eflags = VARE_NONE; 3267 VarEvalFlags then_eflags = VARE_NONE;
3265 VarEvalFlags else_eflags = VARE_NONE; 3268 VarEvalFlags else_eflags = VARE_NONE;
3266 3269
3267 int cond_rc = COND_PARSE; /* anything other than COND_INVALID */ 3270 int cond_rc = COND_PARSE; /* anything other than COND_INVALID */
3268 if (expr->eflags & VARE_WANTRES) { 3271 if (expr->eflags & VARE_WANTRES) {
3269 cond_rc = Cond_EvalCondition(expr->var->name.str, &value); 3272 cond_rc = Cond_EvalCondition(expr->var->name.str, &value);
3270 if (cond_rc != COND_INVALID && value) 3273 if (cond_rc != COND_INVALID && value)
3271 then_eflags = expr->eflags; 3274 then_eflags = expr->eflags;
3272 if (cond_rc != COND_INVALID && !value) 3275 if (cond_rc != COND_INVALID && !value)
3273 else_eflags = expr->eflags; 3276 else_eflags = expr->eflags;
3274 } 3277 }
3275 3278
3276 (*pp)++; /* skip past the '?' */ 3279 (*pp)++; /* skip past the '?' */
3277 res = ParseModifierPart(pp, ':', then_eflags, st, &then_expr); 3280 res = ParseModifierPart(pp, ':', then_eflags, st, &then_expr);
3278 if (res != VPR_OK) 3281 if (res != VPR_OK)
3279 return AMR_CLEANUP; 3282 return AMR_CLEANUP;
3280 3283
3281 res = ParseModifierPart(pp, st->endc, else_eflags, st, &else_expr); 3284 res = ParseModifierPart(pp, st->endc, else_eflags, st, &else_expr);
3282 if (res != VPR_OK) 3285 if (res != VPR_OK)
3283 return AMR_CLEANUP; 3286 return AMR_CLEANUP;
3284 3287
3285 (*pp)--; /* Go back to the st->endc. */ 3288 (*pp)--; /* Go back to the st->endc. */
3286 3289
3287 if (cond_rc == COND_INVALID) { 3290 if (cond_rc == COND_INVALID) {
3288 Error("Bad conditional expression `%s' in %s?%s:%s", 3291 Error("Bad conditional expression `%s' in %s?%s:%s",
3289 expr->var->name.str, expr->var->name.str, 3292 expr->var->name.str, expr->var->name.str,
3290 then_expr, else_expr); 3293 then_expr, else_expr);
3291 return AMR_CLEANUP; 3294 return AMR_CLEANUP;
3292 } 3295 }
3293 3296
3294 if (value) { 3297 if (value) {
3295 Expr_SetValueOwn(expr, then_expr); 3298 Expr_SetValueOwn(expr, then_expr);
3296 free(else_expr); 3299 free(else_expr);
3297 } else { 3300 } else {
3298 Expr_SetValueOwn(expr, else_expr); 3301 Expr_SetValueOwn(expr, else_expr);
3299 free(then_expr); 3302 free(then_expr);
3300 } 3303 }
3301 Expr_Define(expr); 3304 Expr_Define(expr);
3302 return AMR_OK; 3305 return AMR_OK;
3303} 3306}
3304 3307
3305/* 3308/*
3306 * The ::= modifiers are special in that they do not read the variable value 3309 * The ::= modifiers are special in that they do not read the variable value
3307 * but instead assign to that variable. They always expand to an empty 3310 * but instead assign to that variable. They always expand to an empty
3308 * string. 3311 * string.
3309 * 3312 *