Wed Jul 29 19:48:33 2020 UTC ()
make(1): add enough tests to cover the ApplyModifier functions

Only a few return statements are still missing from the code coverage.

In ApplyModifier_Assign, the test for an empty variable name is skipped
for now since it segfaults.

In ApplyModifier_SysV after the second ParseModifierPart, the branch for
the missing delimiter is not reached since this case is already checked
for in the first part of the function. To trigger this branch, a
specially crafted, unrealistic string needs to be created, and that's too
complicated for the moment.


(rillig)
diff -r1.353 -r1.354 src/usr.bin/make/var.c
diff -r1.75 -r1.76 src/usr.bin/make/unit-tests/Makefile
diff -r1.7 -r1.8 src/usr.bin/make/unit-tests/moderrs.exp
diff -r1.6 -r1.7 src/usr.bin/make/unit-tests/moderrs.mk

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

--- src/usr.bin/make/var.c 2020/07/28 17:18:40 1.353
+++ src/usr.bin/make/var.c 2020/07/29 19:48:33 1.354
@@ -1,1078 +1,1078 @@ @@ -1,1078 +1,1078 @@
1/* $NetBSD: var.c,v 1.353 2020/07/28 17:18:40 rillig Exp $ */ 1/* $NetBSD: var.c,v 1.354 2020/07/29 19:48:33 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#ifndef MAKE_NATIVE 71#ifndef MAKE_NATIVE
72static char rcsid[] = "$NetBSD: var.c,v 1.353 2020/07/28 17:18:40 rillig Exp $"; 72static char rcsid[] = "$NetBSD: var.c,v 1.354 2020/07/29 19:48:33 rillig Exp $";
73#else 73#else
74#include <sys/cdefs.h> 74#include <sys/cdefs.h>
75#ifndef lint 75#ifndef lint
76#if 0 76#if 0
77static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 3/19/94"; 77static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 3/19/94";
78#else 78#else
79__RCSID("$NetBSD: var.c,v 1.353 2020/07/28 17:18:40 rillig Exp $"); 79__RCSID("$NetBSD: var.c,v 1.354 2020/07/29 19:48:33 rillig Exp $");
80#endif 80#endif
81#endif /* not lint */ 81#endif /* not lint */
82#endif 82#endif
83 83
84/*- 84/*-
85 * var.c -- 85 * var.c --
86 * Variable-handling functions 86 * Variable-handling functions
87 * 87 *
88 * Interface: 88 * Interface:
89 * Var_Set Set the value of a variable in the given 89 * Var_Set Set the value of a variable in the given
90 * context. The variable is created if it doesn't 90 * context. The variable is created if it doesn't
91 * yet exist. 91 * yet exist.
92 * 92 *
93 * Var_Append Append more characters to an existing variable 93 * Var_Append Append more characters to an existing variable
94 * in the given context. The variable needn't 94 * in the given context. The variable needn't
95 * exist already -- it will be created if it doesn't. 95 * exist already -- it will be created if it doesn't.
96 * A space is placed between the old value and the 96 * A space is placed between the old value and the
97 * new one. 97 * new one.
98 * 98 *
99 * Var_Exists See if a variable exists. 99 * Var_Exists See if a variable exists.
100 * 100 *
101 * Var_Value Return the unexpanded value of a variable in a 101 * Var_Value Return the unexpanded value of a variable in a
102 * context or NULL if the variable is undefined. 102 * context or NULL if the variable is undefined.
103 * 103 *
104 * Var_Subst Substitute either a single variable or all 104 * Var_Subst Substitute either a single variable or all
105 * variables in a string, using the given context. 105 * variables in a string, using the given context.
106 * 106 *
107 * Var_Parse Parse a variable expansion from a string and 107 * Var_Parse Parse a variable expansion from a string and
108 * return the result and the number of characters 108 * return the result and the number of characters
109 * consumed. 109 * consumed.
110 * 110 *
111 * Var_Delete Delete a variable in a context. 111 * Var_Delete Delete a variable in a context.
112 * 112 *
113 * Var_Init Initialize this module. 113 * Var_Init Initialize this module.
114 * 114 *
115 * Debugging: 115 * Debugging:
116 * Var_Dump Print out all variables defined in the given 116 * Var_Dump Print out all variables defined in the given
117 * context. 117 * context.
118 * 118 *
119 * XXX: There's a lot of duplication in these functions. 119 * XXX: There's a lot of duplication in these functions.
120 */ 120 */
121 121
122#include <sys/stat.h> 122#include <sys/stat.h>
123#ifndef NO_REGEX 123#ifndef NO_REGEX
124#include <sys/types.h> 124#include <sys/types.h>
125#include <regex.h> 125#include <regex.h>
126#endif 126#endif
127#include <ctype.h> 127#include <ctype.h>
128#include <inttypes.h> 128#include <inttypes.h>
129#include <limits.h> 129#include <limits.h>
130#include <stdlib.h> 130#include <stdlib.h>
131#include <time.h> 131#include <time.h>
132 132
133#include "make.h" 133#include "make.h"
134#include "buf.h" 134#include "buf.h"
135#include "dir.h" 135#include "dir.h"
136#include "job.h" 136#include "job.h"
137#include "metachar.h" 137#include "metachar.h"
138 138
139#define VAR_DEBUG(fmt, ...) \ 139#define VAR_DEBUG(fmt, ...) \
140 if (!DEBUG(VAR)) \ 140 if (!DEBUG(VAR)) \
141 (void) 0; \ 141 (void) 0; \
142 else \ 142 else \
143 fprintf(debug_file, fmt, __VA_ARGS__) 143 fprintf(debug_file, fmt, __VA_ARGS__)
144 144
145/* 145/*
146 * This lets us tell if we have replaced the original environ 146 * This lets us tell if we have replaced the original environ
147 * (which we cannot free). 147 * (which we cannot free).
148 */ 148 */
149char **savedEnv = NULL; 149char **savedEnv = NULL;
150 150
151/* 151/*
152 * This is a harmless return value for Var_Parse that can be used by Var_Subst 152 * This is a harmless return value for Var_Parse that can be used by Var_Subst
153 * to determine if there was an error in parsing -- easier than returning 153 * to determine if there was an error in parsing -- easier than returning
154 * a flag, as things outside this module don't give a hoot. 154 * a flag, as things outside this module don't give a hoot.
155 */ 155 */
156char var_Error[] = ""; 156char var_Error[] = "";
157 157
158/* 158/*
159 * Similar to var_Error, but returned when the 'VARE_UNDEFERR' flag for 159 * Similar to var_Error, but returned when the 'VARE_UNDEFERR' flag for
160 * Var_Parse is not set. Why not just use a constant? Well, GCC likes 160 * Var_Parse is not set. Why not just use a constant? Well, GCC likes
161 * to condense identical string instances... 161 * to condense identical string instances...
162 */ 162 */
163static char varNoError[] = ""; 163static char varNoError[] = "";
164 164
165/* 165/*
166 * Traditionally we consume $$ during := like any other expansion. 166 * Traditionally we consume $$ during := like any other expansion.
167 * Other make's do not. 167 * Other make's do not.
168 * This knob allows controlling the behavior. 168 * This knob allows controlling the behavior.
169 * FALSE to consume $$ during := assignment. 169 * FALSE to consume $$ during := assignment.
170 * TRUE to preserve $$ during := assignment. 170 * TRUE to preserve $$ during := assignment.
171 */ 171 */
172#define SAVE_DOLLARS ".MAKE.SAVE_DOLLARS" 172#define SAVE_DOLLARS ".MAKE.SAVE_DOLLARS"
173static Boolean save_dollars = TRUE; 173static Boolean save_dollars = TRUE;
174 174
175/* 175/*
176 * Internally, variables are contained in four different contexts. 176 * Internally, variables are contained in four different contexts.
177 * 1) the environment. They cannot be changed. If an environment 177 * 1) the environment. They cannot be changed. If an environment
178 * variable is appended to, the result is placed in the global 178 * variable is appended to, the result is placed in the global
179 * context. 179 * context.
180 * 2) the global context. Variables set in the Makefile are located in 180 * 2) the global context. Variables set in the Makefile are located in
181 * the global context. 181 * the global context.
182 * 3) the command-line context. All variables set on the command line 182 * 3) the command-line context. All variables set on the command line
183 * are placed in this context. They are UNALTERABLE once placed here. 183 * are placed in this context. They are UNALTERABLE once placed here.
184 * 4) the local context. Each target has associated with it a context 184 * 4) the local context. Each target has associated with it a context
185 * list. On this list are located the structures describing such 185 * list. On this list are located the structures describing such
186 * local variables as $(@) and $(*) 186 * local variables as $(@) and $(*)
187 * The four contexts are searched in the reverse order from which they are 187 * The four contexts are searched in the reverse order from which they are
188 * listed (but see checkEnvFirst). 188 * listed (but see checkEnvFirst).
189 */ 189 */
190GNode *VAR_INTERNAL; /* variables from make itself */ 190GNode *VAR_INTERNAL; /* variables from make itself */
191GNode *VAR_GLOBAL; /* variables from the makefile */ 191GNode *VAR_GLOBAL; /* variables from the makefile */
192GNode *VAR_CMD; /* variables defined on the command-line */ 192GNode *VAR_CMD; /* variables defined on the command-line */
193 193
194typedef enum { 194typedef enum {
195 FIND_CMD = 0x01, /* look in VAR_CMD when searching */ 195 FIND_CMD = 0x01, /* look in VAR_CMD when searching */
196 FIND_GLOBAL = 0x02, /* look in VAR_GLOBAL as well */ 196 FIND_GLOBAL = 0x02, /* look in VAR_GLOBAL as well */
197 FIND_ENV = 0x04 /* look in the environment also */ 197 FIND_ENV = 0x04 /* look in the environment also */
198} VarFindFlags; 198} VarFindFlags;
199 199
200typedef enum { 200typedef enum {
201 VAR_IN_USE = 0x01, /* Variable's value is currently being used 201 VAR_IN_USE = 0x01, /* Variable's value is currently being used
202 * by Var_Parse or Var_Subst. 202 * by Var_Parse or Var_Subst.
203 * Used to avoid endless recursion */ 203 * Used to avoid endless recursion */
204 VAR_FROM_ENV = 0x02, /* Variable comes from the environment */ 204 VAR_FROM_ENV = 0x02, /* Variable comes from the environment */
205 VAR_JUNK = 0x04, /* Variable is a junk variable that 205 VAR_JUNK = 0x04, /* Variable is a junk variable that
206 * should be destroyed when done with 206 * should be destroyed when done with
207 * it. Used by Var_Parse for undefined, 207 * it. Used by Var_Parse for undefined,
208 * modified variables */ 208 * modified variables */
209 VAR_KEEP = 0x08, /* Variable is VAR_JUNK, but we found 209 VAR_KEEP = 0x08, /* Variable is VAR_JUNK, but we found
210 * a use for it in some modifier and 210 * a use for it in some modifier and
211 * the value is therefore valid */ 211 * the value is therefore valid */
212 VAR_EXPORTED = 0x10, /* Variable is exported */ 212 VAR_EXPORTED = 0x10, /* Variable is exported */
213 VAR_REEXPORT = 0x20, /* Indicate if var needs re-export. 213 VAR_REEXPORT = 0x20, /* Indicate if var needs re-export.
214 * This would be true if it contains $'s */ 214 * This would be true if it contains $'s */
215 VAR_FROM_CMD = 0x40 /* Variable came from command line */ 215 VAR_FROM_CMD = 0x40 /* Variable came from command line */
216} Var_Flags; 216} Var_Flags;
217 217
218typedef struct Var { 218typedef struct Var {
219 char *name; /* the variable's name */ 219 char *name; /* the variable's name */
220 Buffer val; /* its value */ 220 Buffer val; /* its value */
221 Var_Flags flags; /* miscellaneous status flags */ 221 Var_Flags flags; /* miscellaneous status flags */
222} Var; 222} Var;
223 223
224/* 224/*
225 * Exporting vars is expensive so skip it if we can 225 * Exporting vars is expensive so skip it if we can
226 */ 226 */
227typedef enum { 227typedef enum {
228 VAR_EXPORTED_NONE, 228 VAR_EXPORTED_NONE,
229 VAR_EXPORTED_YES, 229 VAR_EXPORTED_YES,
230 VAR_EXPORTED_ALL 230 VAR_EXPORTED_ALL
231} VarExportedMode; 231} VarExportedMode;
232static VarExportedMode var_exportedVars = VAR_EXPORTED_NONE; 232static VarExportedMode var_exportedVars = VAR_EXPORTED_NONE;
233 233
234typedef enum { 234typedef enum {
235 /* 235 /*
236 * We pass this to Var_Export when doing the initial export 236 * We pass this to Var_Export when doing the initial export
237 * or after updating an exported var. 237 * or after updating an exported var.
238 */ 238 */
239 VAR_EXPORT_PARENT = 0x01, 239 VAR_EXPORT_PARENT = 0x01,
240 /* 240 /*
241 * We pass this to Var_Export1 to tell it to leave the value alone. 241 * We pass this to Var_Export1 to tell it to leave the value alone.
242 */ 242 */
243 VAR_EXPORT_LITERAL = 0x02 243 VAR_EXPORT_LITERAL = 0x02
244} VarExportFlags; 244} VarExportFlags;
245 245
246/* Flags for pattern matching in the :S and :C modifiers */ 246/* Flags for pattern matching in the :S and :C modifiers */
247typedef enum { 247typedef enum {
248 VARP_SUB_GLOBAL = 0x01, /* Apply substitution globally */ 248 VARP_SUB_GLOBAL = 0x01, /* Apply substitution globally */
249 VARP_SUB_ONE = 0x02, /* Apply substitution to one word */ 249 VARP_SUB_ONE = 0x02, /* Apply substitution to one word */
250 VARP_SUB_MATCHED = 0x04, /* There was a match */ 250 VARP_SUB_MATCHED = 0x04, /* There was a match */
251 VARP_ANCHOR_START = 0x08, /* Match at start of word */ 251 VARP_ANCHOR_START = 0x08, /* Match at start of word */
252 VARP_ANCHOR_END = 0x10 /* Match at end of word */ 252 VARP_ANCHOR_END = 0x10 /* Match at end of word */
253} VarPatternFlags; 253} VarPatternFlags;
254 254
255typedef enum { 255typedef enum {
256 VAR_NO_EXPORT = 0x01 /* do not export */ 256 VAR_NO_EXPORT = 0x01 /* do not export */
257} VarSet_Flags; 257} VarSet_Flags;
258 258
259#define BROPEN '{' 259#define BROPEN '{'
260#define BRCLOSE '}' 260#define BRCLOSE '}'
261#define PROPEN '(' 261#define PROPEN '('
262#define PRCLOSE ')' 262#define PRCLOSE ')'
263 263
264/*- 264/*-
265 *----------------------------------------------------------------------- 265 *-----------------------------------------------------------------------
266 * VarFind -- 266 * VarFind --
267 * Find the given variable in the given context and any other contexts 267 * Find the given variable in the given context and any other contexts
268 * indicated. 268 * indicated.
269 * 269 *
270 * Input: 270 * Input:
271 * name name to find 271 * name name to find
272 * ctxt context in which to find it 272 * ctxt context in which to find it
273 * flags FIND_GLOBAL look in VAR_GLOBAL as well 273 * flags FIND_GLOBAL look in VAR_GLOBAL as well
274 * FIND_CMD look in VAR_CMD as well 274 * FIND_CMD look in VAR_CMD as well
275 * FIND_ENV look in the environment as well 275 * FIND_ENV look in the environment as well
276 * 276 *
277 * Results: 277 * Results:
278 * A pointer to the structure describing the desired variable or 278 * A pointer to the structure describing the desired variable or
279 * NULL if the variable does not exist. 279 * NULL if the variable does not exist.
280 * 280 *
281 * Side Effects: 281 * Side Effects:
282 * None 282 * None
283 *----------------------------------------------------------------------- 283 *-----------------------------------------------------------------------
284 */ 284 */
285static Var * 285static Var *
286VarFind(const char *name, GNode *ctxt, VarFindFlags flags) 286VarFind(const char *name, GNode *ctxt, VarFindFlags flags)
287{ 287{
288 /* 288 /*
289 * If the variable name begins with a '.', it could very well be one of 289 * If the variable name begins with a '.', it could very well be one of
290 * the local ones. We check the name against all the local variables 290 * the local ones. We check the name against all the local variables
291 * and substitute the short version in for 'name' if it matches one of 291 * and substitute the short version in for 'name' if it matches one of
292 * them. 292 * them.
293 */ 293 */
294 if (*name == '.' && isupper((unsigned char) name[1])) { 294 if (*name == '.' && isupper((unsigned char) name[1])) {
295 switch (name[1]) { 295 switch (name[1]) {
296 case 'A': 296 case 'A':
297 if (strcmp(name, ".ALLSRC") == 0) 297 if (strcmp(name, ".ALLSRC") == 0)
298 name = ALLSRC; 298 name = ALLSRC;
299 if (strcmp(name, ".ARCHIVE") == 0) 299 if (strcmp(name, ".ARCHIVE") == 0)
300 name = ARCHIVE; 300 name = ARCHIVE;
301 break; 301 break;
302 case 'I': 302 case 'I':
303 if (strcmp(name, ".IMPSRC") == 0) 303 if (strcmp(name, ".IMPSRC") == 0)
304 name = IMPSRC; 304 name = IMPSRC;
305 break; 305 break;
306 case 'M': 306 case 'M':
307 if (strcmp(name, ".MEMBER") == 0) 307 if (strcmp(name, ".MEMBER") == 0)
308 name = MEMBER; 308 name = MEMBER;
309 break; 309 break;
310 case 'O': 310 case 'O':
311 if (strcmp(name, ".OODATE") == 0) 311 if (strcmp(name, ".OODATE") == 0)
312 name = OODATE; 312 name = OODATE;
313 break; 313 break;
314 case 'P': 314 case 'P':
315 if (strcmp(name, ".PREFIX") == 0) 315 if (strcmp(name, ".PREFIX") == 0)
316 name = PREFIX; 316 name = PREFIX;
317 break; 317 break;
318 case 'T': 318 case 'T':
319 if (strcmp(name, ".TARGET") == 0) 319 if (strcmp(name, ".TARGET") == 0)
320 name = TARGET; 320 name = TARGET;
321 break; 321 break;
322 } 322 }
323 } 323 }
324 324
325#ifdef notyet 325#ifdef notyet
326 /* for compatibility with gmake */ 326 /* for compatibility with gmake */
327 if (name[0] == '^' && name[1] == '\0') 327 if (name[0] == '^' && name[1] == '\0')
328 name = ALLSRC; 328 name = ALLSRC;
329#endif 329#endif
330 330
331 /* 331 /*
332 * First look for the variable in the given context. If it's not there, 332 * First look for the variable in the given context. If it's not there,
333 * look for it in VAR_CMD, VAR_GLOBAL and the environment, in that order, 333 * look for it in VAR_CMD, VAR_GLOBAL and the environment, in that order,
334 * depending on the FIND_* flags in 'flags' 334 * depending on the FIND_* flags in 'flags'
335 */ 335 */
336 Hash_Entry *var = Hash_FindEntry(&ctxt->context, name); 336 Hash_Entry *var = Hash_FindEntry(&ctxt->context, name);
337 337
338 if (var == NULL && (flags & FIND_CMD) && ctxt != VAR_CMD) { 338 if (var == NULL && (flags & FIND_CMD) && ctxt != VAR_CMD) {
339 var = Hash_FindEntry(&VAR_CMD->context, name); 339 var = Hash_FindEntry(&VAR_CMD->context, name);
340 } 340 }
341 if (!checkEnvFirst && var == NULL && (flags & FIND_GLOBAL) && 341 if (!checkEnvFirst && var == NULL && (flags & FIND_GLOBAL) &&
342 ctxt != VAR_GLOBAL) 342 ctxt != VAR_GLOBAL)
343 { 343 {
344 var = Hash_FindEntry(&VAR_GLOBAL->context, name); 344 var = Hash_FindEntry(&VAR_GLOBAL->context, name);
345 if (var == NULL && ctxt != VAR_INTERNAL) { 345 if (var == NULL && ctxt != VAR_INTERNAL) {
346 /* VAR_INTERNAL is subordinate to VAR_GLOBAL */ 346 /* VAR_INTERNAL is subordinate to VAR_GLOBAL */
347 var = Hash_FindEntry(&VAR_INTERNAL->context, name); 347 var = Hash_FindEntry(&VAR_INTERNAL->context, name);
348 } 348 }
349 } 349 }
350 if (var == NULL && (flags & FIND_ENV)) { 350 if (var == NULL && (flags & FIND_ENV)) {
351 char *env; 351 char *env;
352 352
353 if ((env = getenv(name)) != NULL) { 353 if ((env = getenv(name)) != NULL) {
354 Var *v = bmake_malloc(sizeof(Var)); 354 Var *v = bmake_malloc(sizeof(Var));
355 v->name = bmake_strdup(name); 355 v->name = bmake_strdup(name);
356 356
357 int len = (int)strlen(env); 357 int len = (int)strlen(env);
358 Buf_Init(&v->val, len + 1); 358 Buf_Init(&v->val, len + 1);
359 Buf_AddBytes(&v->val, len, env); 359 Buf_AddBytes(&v->val, len, env);
360 360
361 v->flags = VAR_FROM_ENV; 361 v->flags = VAR_FROM_ENV;
362 return v; 362 return v;
363 } else if (checkEnvFirst && (flags & FIND_GLOBAL) && 363 } else if (checkEnvFirst && (flags & FIND_GLOBAL) &&
364 ctxt != VAR_GLOBAL) 364 ctxt != VAR_GLOBAL)
365 { 365 {
366 var = Hash_FindEntry(&VAR_GLOBAL->context, name); 366 var = Hash_FindEntry(&VAR_GLOBAL->context, name);
367 if (var == NULL && ctxt != VAR_INTERNAL) { 367 if (var == NULL && ctxt != VAR_INTERNAL) {
368 var = Hash_FindEntry(&VAR_INTERNAL->context, name); 368 var = Hash_FindEntry(&VAR_INTERNAL->context, name);
369 } 369 }
370 if (var == NULL) { 370 if (var == NULL) {
371 return NULL; 371 return NULL;
372 } else { 372 } else {
373 return (Var *)Hash_GetValue(var); 373 return (Var *)Hash_GetValue(var);
374 } 374 }
375 } else { 375 } else {
376 return NULL; 376 return NULL;
377 } 377 }
378 } else if (var == NULL) { 378 } else if (var == NULL) {
379 return NULL; 379 return NULL;
380 } else { 380 } else {
381 return (Var *)Hash_GetValue(var); 381 return (Var *)Hash_GetValue(var);
382 } 382 }
383} 383}
384 384
385/*- 385/*-
386 *----------------------------------------------------------------------- 386 *-----------------------------------------------------------------------
387 * VarFreeEnv -- 387 * VarFreeEnv --
388 * If the variable is an environment variable, free it 388 * If the variable is an environment variable, free it
389 * 389 *
390 * Input: 390 * Input:
391 * v the variable 391 * v the variable
392 * destroy true if the value buffer should be destroyed. 392 * destroy true if the value buffer should be destroyed.
393 * 393 *
394 * Results: 394 * Results:
395 * 1 if it is an environment variable 0 ow. 395 * 1 if it is an environment variable 0 ow.
396 * 396 *
397 * Side Effects: 397 * Side Effects:
398 * The variable is free'ed if it is an environent variable. 398 * The variable is free'ed if it is an environent variable.
399 *----------------------------------------------------------------------- 399 *-----------------------------------------------------------------------
400 */ 400 */
401static Boolean 401static Boolean
402VarFreeEnv(Var *v, Boolean destroy) 402VarFreeEnv(Var *v, Boolean destroy)
403{ 403{
404 if (!(v->flags & VAR_FROM_ENV)) 404 if (!(v->flags & VAR_FROM_ENV))
405 return FALSE; 405 return FALSE;
406 free(v->name); 406 free(v->name);
407 Buf_Destroy(&v->val, destroy); 407 Buf_Destroy(&v->val, destroy);
408 free(v); 408 free(v);
409 return TRUE; 409 return TRUE;
410} 410}
411 411
412/*- 412/*-
413 *----------------------------------------------------------------------- 413 *-----------------------------------------------------------------------
414 * VarAdd -- 414 * VarAdd --
415 * Add a new variable of name name and value val to the given context 415 * Add a new variable of name name and value val to the given context
416 * 416 *
417 * Input: 417 * Input:
418 * name name of variable to add 418 * name name of variable to add
419 * val value to set it to 419 * val value to set it to
420 * ctxt context in which to set it 420 * ctxt context in which to set it
421 * 421 *
422 * Side Effects: 422 * Side Effects:
423 * The new variable is placed at the front of the given context 423 * The new variable is placed at the front of the given context
424 * The name and val arguments are duplicated so they may 424 * The name and val arguments are duplicated so they may
425 * safely be freed. 425 * safely be freed.
426 *----------------------------------------------------------------------- 426 *-----------------------------------------------------------------------
427 */ 427 */
428static void 428static void
429VarAdd(const char *name, const char *val, GNode *ctxt) 429VarAdd(const char *name, const char *val, GNode *ctxt)
430{ 430{
431 Var *v = bmake_malloc(sizeof(Var)); 431 Var *v = bmake_malloc(sizeof(Var));
432 432
433 int len = val != NULL ? (int)strlen(val) : 0; 433 int len = val != NULL ? (int)strlen(val) : 0;
434 Buf_Init(&v->val, len + 1); 434 Buf_Init(&v->val, len + 1);
435 Buf_AddBytes(&v->val, len, val); 435 Buf_AddBytes(&v->val, len, val);
436 436
437 v->flags = 0; 437 v->flags = 0;
438 438
439 Hash_Entry *h = Hash_CreateEntry(&ctxt->context, name, NULL); 439 Hash_Entry *h = Hash_CreateEntry(&ctxt->context, name, NULL);
440 Hash_SetValue(h, v); 440 Hash_SetValue(h, v);
441 v->name = h->name; 441 v->name = h->name;
442 if (DEBUG(VAR) && !(ctxt->flags & INTERNAL)) { 442 if (DEBUG(VAR) && !(ctxt->flags & INTERNAL)) {
443 fprintf(debug_file, "%s:%s = %s\n", ctxt->name, name, val); 443 fprintf(debug_file, "%s:%s = %s\n", ctxt->name, name, val);
444 } 444 }
445} 445}
446 446
447/*- 447/*-
448 *----------------------------------------------------------------------- 448 *-----------------------------------------------------------------------
449 * Var_Delete -- 449 * Var_Delete --
450 * Remove a variable from a context. 450 * Remove a variable from a context.
451 * 451 *
452 * Side Effects: 452 * Side Effects:
453 * The Var structure is removed and freed. 453 * The Var structure is removed and freed.
454 * 454 *
455 *----------------------------------------------------------------------- 455 *-----------------------------------------------------------------------
456 */ 456 */
457void 457void
458Var_Delete(const char *name, GNode *ctxt) 458Var_Delete(const char *name, GNode *ctxt)
459{ 459{
460 Hash_Entry *ln; 460 Hash_Entry *ln;
461 char *cp; 461 char *cp;
462 462
463 if (strchr(name, '$') != NULL) { 463 if (strchr(name, '$') != NULL) {
464 cp = Var_Subst(name, VAR_GLOBAL, VARE_WANTRES); 464 cp = Var_Subst(name, VAR_GLOBAL, VARE_WANTRES);
465 } else { 465 } else {
466 cp = UNCONST(name); 466 cp = UNCONST(name);
467 } 467 }
468 ln = Hash_FindEntry(&ctxt->context, cp); 468 ln = Hash_FindEntry(&ctxt->context, cp);
469 if (DEBUG(VAR)) { 469 if (DEBUG(VAR)) {
470 fprintf(debug_file, "%s:delete %s%s\n", 470 fprintf(debug_file, "%s:delete %s%s\n",
471 ctxt->name, cp, ln ? "" : " (not found)"); 471 ctxt->name, cp, ln ? "" : " (not found)");
472 } 472 }
473 if (cp != name) 473 if (cp != name)
474 free(cp); 474 free(cp);
475 if (ln != NULL) { 475 if (ln != NULL) {
476 Var *v = (Var *)Hash_GetValue(ln); 476 Var *v = (Var *)Hash_GetValue(ln);
477 if (v->flags & VAR_EXPORTED) 477 if (v->flags & VAR_EXPORTED)
478 unsetenv(v->name); 478 unsetenv(v->name);
479 if (strcmp(MAKE_EXPORTED, v->name) == 0) 479 if (strcmp(MAKE_EXPORTED, v->name) == 0)
480 var_exportedVars = VAR_EXPORTED_NONE; 480 var_exportedVars = VAR_EXPORTED_NONE;
481 if (v->name != ln->name) 481 if (v->name != ln->name)
482 free(v->name); 482 free(v->name);
483 Hash_DeleteEntry(&ctxt->context, ln); 483 Hash_DeleteEntry(&ctxt->context, ln);
484 Buf_Destroy(&v->val, TRUE); 484 Buf_Destroy(&v->val, TRUE);
485 free(v); 485 free(v);
486 } 486 }
487} 487}
488 488
489 489
490/* 490/*
491 * Export a var. 491 * Export a var.
492 * We ignore make internal variables (those which start with '.') 492 * We ignore make internal variables (those which start with '.')
493 * Also we jump through some hoops to avoid calling setenv 493 * Also we jump through some hoops to avoid calling setenv
494 * more than necessary since it can leak. 494 * more than necessary since it can leak.
495 * We only manipulate flags of vars if 'parent' is set. 495 * We only manipulate flags of vars if 'parent' is set.
496 */ 496 */
497static int 497static int
498Var_Export1(const char *name, VarExportFlags flags) 498Var_Export1(const char *name, VarExportFlags flags)
499{ 499{
500 char tmp[BUFSIZ]; 500 char tmp[BUFSIZ];
501 Var *v; 501 Var *v;
502 char *val = NULL; 502 char *val = NULL;
503 int n; 503 int n;
504 VarExportFlags parent = flags & VAR_EXPORT_PARENT; 504 VarExportFlags parent = flags & VAR_EXPORT_PARENT;
505 505
506 if (*name == '.') 506 if (*name == '.')
507 return 0; /* skip internals */ 507 return 0; /* skip internals */
508 if (!name[1]) { 508 if (!name[1]) {
509 /* 509 /*
510 * A single char. 510 * A single char.
511 * If it is one of the vars that should only appear in 511 * If it is one of the vars that should only appear in
512 * local context, skip it, else we can get Var_Subst 512 * local context, skip it, else we can get Var_Subst
513 * into a loop. 513 * into a loop.
514 */ 514 */
515 switch (name[0]) { 515 switch (name[0]) {
516 case '@': 516 case '@':
517 case '%': 517 case '%':
518 case '*': 518 case '*':
519 case '!': 519 case '!':
520 return 0; 520 return 0;
521 } 521 }
522 } 522 }
523 v = VarFind(name, VAR_GLOBAL, 0); 523 v = VarFind(name, VAR_GLOBAL, 0);
524 if (v == NULL) 524 if (v == NULL)
525 return 0; 525 return 0;
526 if (!parent && 526 if (!parent &&
527 (v->flags & (VAR_EXPORTED | VAR_REEXPORT)) == VAR_EXPORTED) { 527 (v->flags & (VAR_EXPORTED | VAR_REEXPORT)) == VAR_EXPORTED) {
528 return 0; /* nothing to do */ 528 return 0; /* nothing to do */
529 } 529 }
530 val = Buf_GetAll(&v->val, NULL); 530 val = Buf_GetAll(&v->val, NULL);
531 if ((flags & VAR_EXPORT_LITERAL) == 0 && strchr(val, '$')) { 531 if ((flags & VAR_EXPORT_LITERAL) == 0 && strchr(val, '$')) {
532 if (parent) { 532 if (parent) {
533 /* 533 /*
534 * Flag this as something we need to re-export. 534 * Flag this as something we need to re-export.
535 * No point actually exporting it now though, 535 * No point actually exporting it now though,
536 * the child can do it at the last minute. 536 * the child can do it at the last minute.
537 */ 537 */
538 v->flags |= (VAR_EXPORTED | VAR_REEXPORT); 538 v->flags |= (VAR_EXPORTED | VAR_REEXPORT);
539 return 1; 539 return 1;
540 } 540 }
541 if (v->flags & VAR_IN_USE) { 541 if (v->flags & VAR_IN_USE) {
542 /* 542 /*
543 * We recursed while exporting in a child. 543 * We recursed while exporting in a child.
544 * This isn't going to end well, just skip it. 544 * This isn't going to end well, just skip it.
545 */ 545 */
546 return 0; 546 return 0;
547 } 547 }
548 n = snprintf(tmp, sizeof(tmp), "${%s}", name); 548 n = snprintf(tmp, sizeof(tmp), "${%s}", name);
549 if (n < (int)sizeof(tmp)) { 549 if (n < (int)sizeof(tmp)) {
550 val = Var_Subst(tmp, VAR_GLOBAL, VARE_WANTRES); 550 val = Var_Subst(tmp, VAR_GLOBAL, VARE_WANTRES);
551 setenv(name, val, 1); 551 setenv(name, val, 1);
552 free(val); 552 free(val);
553 } 553 }
554 } else { 554 } else {
555 if (parent) 555 if (parent)
556 v->flags &= ~VAR_REEXPORT; /* once will do */ 556 v->flags &= ~VAR_REEXPORT; /* once will do */
557 if (parent || !(v->flags & VAR_EXPORTED)) 557 if (parent || !(v->flags & VAR_EXPORTED))
558 setenv(name, val, 1); 558 setenv(name, val, 1);
559 } 559 }
560 /* 560 /*
561 * This is so Var_Set knows to call Var_Export again... 561 * This is so Var_Set knows to call Var_Export again...
562 */ 562 */
563 if (parent) { 563 if (parent) {
564 v->flags |= VAR_EXPORTED; 564 v->flags |= VAR_EXPORTED;
565 } 565 }
566 return 1; 566 return 1;
567} 567}
568 568
569static void 569static void
570Var_ExportVars_callback(void *entry, void *unused MAKE_ATTR_UNUSED) 570Var_ExportVars_callback(void *entry, void *unused MAKE_ATTR_UNUSED)
571{ 571{
572 Var *var = entry; 572 Var *var = entry;
573 Var_Export1(var->name, 0); 573 Var_Export1(var->name, 0);
574} 574}
575 575
576/* 576/*
577 * This gets called from our children. 577 * This gets called from our children.
578 */ 578 */
579void 579void
580Var_ExportVars(void) 580Var_ExportVars(void)
581{ 581{
582 char tmp[BUFSIZ]; 582 char tmp[BUFSIZ];
583 char *val; 583 char *val;
584 int n; 584 int n;
585 585
586 /* 586 /*
587 * Several make's support this sort of mechanism for tracking 587 * Several make's support this sort of mechanism for tracking
588 * recursion - but each uses a different name. 588 * recursion - but each uses a different name.
589 * We allow the makefiles to update MAKELEVEL and ensure 589 * We allow the makefiles to update MAKELEVEL and ensure
590 * children see a correctly incremented value. 590 * children see a correctly incremented value.
591 */ 591 */
592 snprintf(tmp, sizeof(tmp), "%d", makelevel + 1); 592 snprintf(tmp, sizeof(tmp), "%d", makelevel + 1);
593 setenv(MAKE_LEVEL_ENV, tmp, 1); 593 setenv(MAKE_LEVEL_ENV, tmp, 1);
594 594
595 if (VAR_EXPORTED_NONE == var_exportedVars) 595 if (VAR_EXPORTED_NONE == var_exportedVars)
596 return; 596 return;
597 597
598 if (VAR_EXPORTED_ALL == var_exportedVars) { 598 if (VAR_EXPORTED_ALL == var_exportedVars) {
599 /* Ouch! This is crazy... */ 599 /* Ouch! This is crazy... */
600 Hash_ForEach(&VAR_GLOBAL->context, Var_ExportVars_callback, NULL); 600 Hash_ForEach(&VAR_GLOBAL->context, Var_ExportVars_callback, NULL);
601 return; 601 return;
602 } 602 }
603 /* 603 /*
604 * We have a number of exported vars, 604 * We have a number of exported vars,
605 */ 605 */
606 n = snprintf(tmp, sizeof(tmp), "${" MAKE_EXPORTED ":O:u}"); 606 n = snprintf(tmp, sizeof(tmp), "${" MAKE_EXPORTED ":O:u}");
607 if (n < (int)sizeof(tmp)) { 607 if (n < (int)sizeof(tmp)) {
608 char **av; 608 char **av;
609 char *as; 609 char *as;
610 int ac; 610 int ac;
611 int i; 611 int i;
612 612
613 val = Var_Subst(tmp, VAR_GLOBAL, VARE_WANTRES); 613 val = Var_Subst(tmp, VAR_GLOBAL, VARE_WANTRES);
614 if (*val) { 614 if (*val) {
615 av = brk_string(val, &ac, FALSE, &as); 615 av = brk_string(val, &ac, FALSE, &as);
616 for (i = 0; i < ac; i++) 616 for (i = 0; i < ac; i++)
617 Var_Export1(av[i], 0); 617 Var_Export1(av[i], 0);
618 free(as); 618 free(as);
619 free(av); 619 free(av);
620 } 620 }
621 free(val); 621 free(val);
622 } 622 }
623} 623}
624 624
625/* 625/*
626 * This is called when .export is seen or 626 * This is called when .export is seen or
627 * .MAKE.EXPORTED is modified. 627 * .MAKE.EXPORTED is modified.
628 * It is also called when any exported var is modified. 628 * It is also called when any exported var is modified.
629 */ 629 */
630void 630void
631Var_Export(char *str, int isExport) 631Var_Export(char *str, int isExport)
632{ 632{
633 char **av; 633 char **av;
634 char *as; 634 char *as;
635 VarExportFlags flags; 635 VarExportFlags flags;
636 int ac; 636 int ac;
637 int i; 637 int i;
638 638
639 if (isExport && (!str || !str[0])) { 639 if (isExport && (!str || !str[0])) {
640 var_exportedVars = VAR_EXPORTED_ALL; /* use with caution! */ 640 var_exportedVars = VAR_EXPORTED_ALL; /* use with caution! */
641 return; 641 return;
642 } 642 }
643 643
644 flags = 0; 644 flags = 0;
645 if (strncmp(str, "-env", 4) == 0) { 645 if (strncmp(str, "-env", 4) == 0) {
646 str += 4; 646 str += 4;
647 } else if (strncmp(str, "-literal", 8) == 0) { 647 } else if (strncmp(str, "-literal", 8) == 0) {
648 str += 8; 648 str += 8;
649 flags |= VAR_EXPORT_LITERAL; 649 flags |= VAR_EXPORT_LITERAL;
650 } else { 650 } else {
651 flags |= VAR_EXPORT_PARENT; 651 flags |= VAR_EXPORT_PARENT;
652 } 652 }
653 653
654 char *val = Var_Subst(str, VAR_GLOBAL, VARE_WANTRES); 654 char *val = Var_Subst(str, VAR_GLOBAL, VARE_WANTRES);
655 if (*val) { 655 if (*val) {
656 av = brk_string(val, &ac, FALSE, &as); 656 av = brk_string(val, &ac, FALSE, &as);
657 for (i = 0; i < ac; i++) { 657 for (i = 0; i < ac; i++) {
658 const char *name = av[i]; 658 const char *name = av[i];
659 if (!name[1]) { 659 if (!name[1]) {
660 /* 660 /*
661 * A single char. 661 * A single char.
662 * If it is one of the vars that should only appear in 662 * If it is one of the vars that should only appear in
663 * local context, skip it, else we can get Var_Subst 663 * local context, skip it, else we can get Var_Subst
664 * into a loop. 664 * into a loop.
665 */ 665 */
666 switch (name[0]) { 666 switch (name[0]) {
667 case '@': 667 case '@':
668 case '%': 668 case '%':
669 case '*': 669 case '*':
670 case '!': 670 case '!':
671 continue; 671 continue;
672 } 672 }
673 } 673 }
674 if (Var_Export1(name, flags)) { 674 if (Var_Export1(name, flags)) {
675 if (VAR_EXPORTED_ALL != var_exportedVars) 675 if (VAR_EXPORTED_ALL != var_exportedVars)
676 var_exportedVars = VAR_EXPORTED_YES; 676 var_exportedVars = VAR_EXPORTED_YES;
677 if (isExport && (flags & VAR_EXPORT_PARENT)) { 677 if (isExport && (flags & VAR_EXPORT_PARENT)) {
678 Var_Append(MAKE_EXPORTED, name, VAR_GLOBAL); 678 Var_Append(MAKE_EXPORTED, name, VAR_GLOBAL);
679 } 679 }
680 } 680 }
681 } 681 }
682 free(as); 682 free(as);
683 free(av); 683 free(av);
684 } 684 }
685 free(val); 685 free(val);
686} 686}
687 687
688 688
689extern char **environ; 689extern char **environ;
690 690
691/* 691/*
692 * This is called when .unexport[-env] is seen. 692 * This is called when .unexport[-env] is seen.
693 * 693 *
694 * str must have the form "unexport[-env] varname...". 694 * str must have the form "unexport[-env] varname...".
695 */ 695 */
696void 696void
697Var_UnExport(char *str) 697Var_UnExport(char *str)
698{ 698{
699 char tmp[BUFSIZ]; 699 char tmp[BUFSIZ];
700 char *vlist; 700 char *vlist;
701 char *cp; 701 char *cp;
702 Boolean unexport_env; 702 Boolean unexport_env;
703 int n; 703 int n;
704 704
705 vlist = NULL; 705 vlist = NULL;
706 706
707 str += strlen("unexport"); 707 str += strlen("unexport");
708 unexport_env = (strncmp(str, "-env", 4) == 0); 708 unexport_env = (strncmp(str, "-env", 4) == 0);
709 if (unexport_env) { 709 if (unexport_env) {
710 char **newenv; 710 char **newenv;
711 711
712 cp = getenv(MAKE_LEVEL_ENV); /* we should preserve this */ 712 cp = getenv(MAKE_LEVEL_ENV); /* we should preserve this */
713 if (environ == savedEnv) { 713 if (environ == savedEnv) {
714 /* we have been here before! */ 714 /* we have been here before! */
715 newenv = bmake_realloc(environ, 2 * sizeof(char *)); 715 newenv = bmake_realloc(environ, 2 * sizeof(char *));
716 } else { 716 } else {
717 if (savedEnv) { 717 if (savedEnv) {
718 free(savedEnv); 718 free(savedEnv);
719 savedEnv = NULL; 719 savedEnv = NULL;
720 } 720 }
721 newenv = bmake_malloc(2 * sizeof(char *)); 721 newenv = bmake_malloc(2 * sizeof(char *));
722 } 722 }
723 if (!newenv) 723 if (!newenv)
724 return; 724 return;
725 /* Note: we cannot safely free() the original environ. */ 725 /* Note: we cannot safely free() the original environ. */
726 environ = savedEnv = newenv; 726 environ = savedEnv = newenv;
727 newenv[0] = NULL; 727 newenv[0] = NULL;
728 newenv[1] = NULL; 728 newenv[1] = NULL;
729 if (cp && *cp) 729 if (cp && *cp)
730 setenv(MAKE_LEVEL_ENV, cp, 1); 730 setenv(MAKE_LEVEL_ENV, cp, 1);
731 } else { 731 } else {
732 for (; *str != '\n' && isspace((unsigned char) *str); str++) 732 for (; *str != '\n' && isspace((unsigned char) *str); str++)
733 continue; 733 continue;
734 if (str[0] && str[0] != '\n') { 734 if (str[0] && str[0] != '\n') {
735 vlist = str; 735 vlist = str;
736 } 736 }
737 } 737 }
738 738
739 if (!vlist) { 739 if (!vlist) {
740 /* Using .MAKE.EXPORTED */ 740 /* Using .MAKE.EXPORTED */
741 vlist = Var_Subst("${" MAKE_EXPORTED ":O:u}", VAR_GLOBAL, 741 vlist = Var_Subst("${" MAKE_EXPORTED ":O:u}", VAR_GLOBAL,
742 VARE_WANTRES); 742 VARE_WANTRES);
743 } 743 }
744 if (vlist) { 744 if (vlist) {
745 Var *v; 745 Var *v;
746 char **av; 746 char **av;
747 char *as; 747 char *as;
748 int ac; 748 int ac;
749 int i; 749 int i;
750 750
751 av = brk_string(vlist, &ac, FALSE, &as); 751 av = brk_string(vlist, &ac, FALSE, &as);
752 for (i = 0; i < ac; i++) { 752 for (i = 0; i < ac; i++) {
753 v = VarFind(av[i], VAR_GLOBAL, 0); 753 v = VarFind(av[i], VAR_GLOBAL, 0);
754 if (!v) 754 if (!v)
755 continue; 755 continue;
756 if (!unexport_env && 756 if (!unexport_env &&
757 (v->flags & (VAR_EXPORTED | VAR_REEXPORT)) == VAR_EXPORTED) 757 (v->flags & (VAR_EXPORTED | VAR_REEXPORT)) == VAR_EXPORTED)
758 unsetenv(v->name); 758 unsetenv(v->name);
759 v->flags &= ~(VAR_EXPORTED | VAR_REEXPORT); 759 v->flags &= ~(VAR_EXPORTED | VAR_REEXPORT);
760 /* 760 /*
761 * If we are unexporting a list, 761 * If we are unexporting a list,
762 * remove each one from .MAKE.EXPORTED. 762 * remove each one from .MAKE.EXPORTED.
763 * If we are removing them all, 763 * If we are removing them all,
764 * just delete .MAKE.EXPORTED below. 764 * just delete .MAKE.EXPORTED below.
765 */ 765 */
766 if (vlist == str) { 766 if (vlist == str) {
767 n = snprintf(tmp, sizeof(tmp), 767 n = snprintf(tmp, sizeof(tmp),
768 "${" MAKE_EXPORTED ":N%s}", v->name); 768 "${" MAKE_EXPORTED ":N%s}", v->name);
769 if (n < (int)sizeof(tmp)) { 769 if (n < (int)sizeof(tmp)) {
770 cp = Var_Subst(tmp, VAR_GLOBAL, VARE_WANTRES); 770 cp = Var_Subst(tmp, VAR_GLOBAL, VARE_WANTRES);
771 Var_Set(MAKE_EXPORTED, cp, VAR_GLOBAL); 771 Var_Set(MAKE_EXPORTED, cp, VAR_GLOBAL);
772 free(cp); 772 free(cp);
773 } 773 }
774 } 774 }
775 } 775 }
776 free(as); 776 free(as);
777 free(av); 777 free(av);
778 if (vlist != str) { 778 if (vlist != str) {
779 Var_Delete(MAKE_EXPORTED, VAR_GLOBAL); 779 Var_Delete(MAKE_EXPORTED, VAR_GLOBAL);
780 free(vlist); 780 free(vlist);
781 } 781 }
782 } 782 }
783} 783}
784 784
785static void 785static void
786Var_Set_with_flags(const char *name, const char *val, GNode *ctxt, 786Var_Set_with_flags(const char *name, const char *val, GNode *ctxt,
787 VarSet_Flags flags) 787 VarSet_Flags flags)
788{ 788{
789 Var *v; 789 Var *v;
790 char *expanded_name = NULL; 790 char *expanded_name = NULL;
791 791
792 /* 792 /*
793 * We only look for a variable in the given context since anything set 793 * We only look for a variable in the given context since anything set
794 * here will override anything in a lower context, so there's not much 794 * here will override anything in a lower context, so there's not much
795 * point in searching them all just to save a bit of memory... 795 * point in searching them all just to save a bit of memory...
796 */ 796 */
797 if (strchr(name, '$') != NULL) { 797 if (strchr(name, '$') != NULL) {
798 expanded_name = Var_Subst(name, ctxt, VARE_WANTRES); 798 expanded_name = Var_Subst(name, ctxt, VARE_WANTRES);
799 if (expanded_name[0] == '\0') { 799 if (expanded_name[0] == '\0') {
800 if (DEBUG(VAR)) { 800 if (DEBUG(VAR)) {
801 fprintf(debug_file, "Var_Set(\"%s\", \"%s\", ...) " 801 fprintf(debug_file, "Var_Set(\"%s\", \"%s\", ...) "
802 "name expands to empty string - ignored\n", 802 "name expands to empty string - ignored\n",
803 name, val); 803 name, val);
804 } 804 }
805 free(expanded_name); 805 free(expanded_name);
806 return; 806 return;
807 } 807 }
808 name = expanded_name; 808 name = expanded_name;
809 } 809 }
810 if (ctxt == VAR_GLOBAL) { 810 if (ctxt == VAR_GLOBAL) {
811 v = VarFind(name, VAR_CMD, 0); 811 v = VarFind(name, VAR_CMD, 0);
812 if (v != NULL) { 812 if (v != NULL) {
813 if ((v->flags & VAR_FROM_CMD)) { 813 if ((v->flags & VAR_FROM_CMD)) {
814 if (DEBUG(VAR)) { 814 if (DEBUG(VAR)) {
815 fprintf(debug_file, "%s:%s = %s ignored!\n", ctxt->name, name, val); 815 fprintf(debug_file, "%s:%s = %s ignored!\n", ctxt->name, name, val);
816 } 816 }
817 goto out; 817 goto out;
818 } 818 }
819 VarFreeEnv(v, TRUE); 819 VarFreeEnv(v, TRUE);
820 } 820 }
821 } 821 }
822 v = VarFind(name, ctxt, 0); 822 v = VarFind(name, ctxt, 0);
823 if (v == NULL) { 823 if (v == NULL) {
824 if (ctxt == VAR_CMD && (flags & VAR_NO_EXPORT) == 0) { 824 if (ctxt == VAR_CMD && (flags & VAR_NO_EXPORT) == 0) {
825 /* 825 /*
826 * This var would normally prevent the same name being added 826 * This var would normally prevent the same name being added
827 * to VAR_GLOBAL, so delete it from there if needed. 827 * to VAR_GLOBAL, so delete it from there if needed.
828 * Otherwise -V name may show the wrong value. 828 * Otherwise -V name may show the wrong value.
829 */ 829 */
830 Var_Delete(name, VAR_GLOBAL); 830 Var_Delete(name, VAR_GLOBAL);
831 } 831 }
832 VarAdd(name, val, ctxt); 832 VarAdd(name, val, ctxt);
833 } else { 833 } else {
834 Buf_Empty(&v->val); 834 Buf_Empty(&v->val);
835 if (val) 835 if (val)
836 Buf_AddStr(&v->val, val); 836 Buf_AddStr(&v->val, val);
837 837
838 if (DEBUG(VAR)) { 838 if (DEBUG(VAR)) {
839 fprintf(debug_file, "%s:%s = %s\n", ctxt->name, name, val); 839 fprintf(debug_file, "%s:%s = %s\n", ctxt->name, name, val);
840 } 840 }
841 if ((v->flags & VAR_EXPORTED)) { 841 if ((v->flags & VAR_EXPORTED)) {
842 Var_Export1(name, VAR_EXPORT_PARENT); 842 Var_Export1(name, VAR_EXPORT_PARENT);
843 } 843 }
844 } 844 }
845 /* 845 /*
846 * Any variables given on the command line are automatically exported 846 * Any variables given on the command line are automatically exported
847 * to the environment (as per POSIX standard) 847 * to the environment (as per POSIX standard)
848 */ 848 */
849 if (ctxt == VAR_CMD && (flags & VAR_NO_EXPORT) == 0) { 849 if (ctxt == VAR_CMD && (flags & VAR_NO_EXPORT) == 0) {
850 if (v == NULL) { 850 if (v == NULL) {
851 /* we just added it */ 851 /* we just added it */
852 v = VarFind(name, ctxt, 0); 852 v = VarFind(name, ctxt, 0);
853 } 853 }
854 if (v != NULL) 854 if (v != NULL)
855 v->flags |= VAR_FROM_CMD; 855 v->flags |= VAR_FROM_CMD;
856 /* 856 /*
857 * If requested, don't export these in the environment 857 * If requested, don't export these in the environment
858 * individually. We still put them in MAKEOVERRIDES so 858 * individually. We still put them in MAKEOVERRIDES so
859 * that the command-line settings continue to override 859 * that the command-line settings continue to override
860 * Makefile settings. 860 * Makefile settings.
861 */ 861 */
862 if (varNoExportEnv != TRUE) 862 if (varNoExportEnv != TRUE)
863 setenv(name, val ? val : "", 1); 863 setenv(name, val ? val : "", 1);
864 864
865 Var_Append(MAKEOVERRIDES, name, VAR_GLOBAL); 865 Var_Append(MAKEOVERRIDES, name, VAR_GLOBAL);
866 } 866 }
867 if (name[0] == '.' && strcmp(name, SAVE_DOLLARS) == 0) 867 if (name[0] == '.' && strcmp(name, SAVE_DOLLARS) == 0)
868 save_dollars = s2Boolean(val, save_dollars); 868 save_dollars = s2Boolean(val, save_dollars);
869 869
870out: 870out:
871 free(expanded_name); 871 free(expanded_name);
872 if (v != NULL) 872 if (v != NULL)
873 VarFreeEnv(v, TRUE); 873 VarFreeEnv(v, TRUE);
874} 874}
875 875
876/*- 876/*-
877 *----------------------------------------------------------------------- 877 *-----------------------------------------------------------------------
878 * Var_Set -- 878 * Var_Set --
879 * Set the variable name to the value val in the given context. 879 * Set the variable name to the value val in the given context.
880 * 880 *
881 * Input: 881 * Input:
882 * name name of variable to set 882 * name name of variable to set
883 * val value to give to the variable 883 * val value to give to the variable
884 * ctxt context in which to set it 884 * ctxt context in which to set it
885 * 885 *
886 * Side Effects: 886 * Side Effects:
887 * If the variable doesn't yet exist, a new record is created for it. 887 * If the variable doesn't yet exist, a new record is created for it.
888 * Else the old value is freed and the new one stuck in its place 888 * Else the old value is freed and the new one stuck in its place
889 * 889 *
890 * Notes: 890 * Notes:
891 * The variable is searched for only in its context before being 891 * The variable is searched for only in its context before being
892 * created in that context. I.e. if the context is VAR_GLOBAL, 892 * created in that context. I.e. if the context is VAR_GLOBAL,
893 * only VAR_GLOBAL->context is searched. Likewise if it is VAR_CMD, only 893 * only VAR_GLOBAL->context is searched. Likewise if it is VAR_CMD, only
894 * VAR_CMD->context is searched. This is done to avoid the literally 894 * VAR_CMD->context is searched. This is done to avoid the literally
895 * thousands of unnecessary strcmp's that used to be done to 895 * thousands of unnecessary strcmp's that used to be done to
896 * set, say, $(@) or $(<). 896 * set, say, $(@) or $(<).
897 * If the context is VAR_GLOBAL though, we check if the variable 897 * If the context is VAR_GLOBAL though, we check if the variable
898 * was set in VAR_CMD from the command line and skip it if so. 898 * was set in VAR_CMD from the command line and skip it if so.
899 *----------------------------------------------------------------------- 899 *-----------------------------------------------------------------------
900 */ 900 */
901void 901void
902Var_Set(const char *name, const char *val, GNode *ctxt) 902Var_Set(const char *name, const char *val, GNode *ctxt)
903{ 903{
904 Var_Set_with_flags(name, val, ctxt, 0); 904 Var_Set_with_flags(name, val, ctxt, 0);
905} 905}
906 906
907/*- 907/*-
908 *----------------------------------------------------------------------- 908 *-----------------------------------------------------------------------
909 * Var_Append -- 909 * Var_Append --
910 * The variable of the given name has the given value appended to it in 910 * The variable of the given name has the given value appended to it in
911 * the given context. 911 * the given context.
912 * 912 *
913 * Input: 913 * Input:
914 * name name of variable to modify 914 * name name of variable to modify
915 * val String to append to it 915 * val String to append to it
916 * ctxt Context in which this should occur 916 * ctxt Context in which this should occur
917 * 917 *
918 * Side Effects: 918 * Side Effects:
919 * If the variable doesn't exist, it is created. Else the strings 919 * If the variable doesn't exist, it is created. Else the strings
920 * are concatenated (with a space in between). 920 * are concatenated (with a space in between).
921 * 921 *
922 * Notes: 922 * Notes:
923 * Only if the variable is being sought in the global context is the 923 * Only if the variable is being sought in the global context is the
924 * environment searched. 924 * environment searched.
925 * XXX: Knows its calling circumstances in that if called with ctxt 925 * XXX: Knows its calling circumstances in that if called with ctxt
926 * an actual target, it will only search that context since only 926 * an actual target, it will only search that context since only
927 * a local variable could be being appended to. This is actually 927 * a local variable could be being appended to. This is actually
928 * a big win and must be tolerated. 928 * a big win and must be tolerated.
929 *----------------------------------------------------------------------- 929 *-----------------------------------------------------------------------
930 */ 930 */
931void 931void
932Var_Append(const char *name, const char *val, GNode *ctxt) 932Var_Append(const char *name, const char *val, GNode *ctxt)
933{ 933{
934 Var *v; 934 Var *v;
935 Hash_Entry *h; 935 Hash_Entry *h;
936 char *expanded_name = NULL; 936 char *expanded_name = NULL;
937 937
938 if (strchr(name, '$') != NULL) { 938 if (strchr(name, '$') != NULL) {
939 expanded_name = Var_Subst(name, ctxt, VARE_WANTRES); 939 expanded_name = Var_Subst(name, ctxt, VARE_WANTRES);
940 if (expanded_name[0] == '\0') { 940 if (expanded_name[0] == '\0') {
941 if (DEBUG(VAR)) { 941 if (DEBUG(VAR)) {
942 fprintf(debug_file, "Var_Append(\"%s\", \"%s\", ...) " 942 fprintf(debug_file, "Var_Append(\"%s\", \"%s\", ...) "
943 "name expands to empty string - ignored\n", 943 "name expands to empty string - ignored\n",
944 name, val); 944 name, val);
945 } 945 }
946 free(expanded_name); 946 free(expanded_name);
947 return; 947 return;
948 } 948 }
949 name = expanded_name; 949 name = expanded_name;
950 } 950 }
951 951
952 v = VarFind(name, ctxt, ctxt == VAR_GLOBAL ? (FIND_CMD | FIND_ENV) : 0); 952 v = VarFind(name, ctxt, ctxt == VAR_GLOBAL ? (FIND_CMD | FIND_ENV) : 0);
953 953
954 if (v == NULL) { 954 if (v == NULL) {
955 Var_Set(name, val, ctxt); 955 Var_Set(name, val, ctxt);
956 } else if (ctxt == VAR_CMD || !(v->flags & VAR_FROM_CMD)) { 956 } else if (ctxt == VAR_CMD || !(v->flags & VAR_FROM_CMD)) {
957 Buf_AddByte(&v->val, ' '); 957 Buf_AddByte(&v->val, ' ');
958 Buf_AddStr(&v->val, val); 958 Buf_AddStr(&v->val, val);
959 959
960 if (DEBUG(VAR)) { 960 if (DEBUG(VAR)) {
961 fprintf(debug_file, "%s:%s = %s\n", ctxt->name, name, 961 fprintf(debug_file, "%s:%s = %s\n", ctxt->name, name,
962 Buf_GetAll(&v->val, NULL)); 962 Buf_GetAll(&v->val, NULL));
963 } 963 }
964 964
965 if (v->flags & VAR_FROM_ENV) { 965 if (v->flags & VAR_FROM_ENV) {
966 /* 966 /*
967 * If the original variable came from the environment, we 967 * If the original variable came from the environment, we
968 * have to install it in the global context (we could place 968 * have to install it in the global context (we could place
969 * it in the environment, but then we should provide a way to 969 * it in the environment, but then we should provide a way to
970 * export other variables...) 970 * export other variables...)
971 */ 971 */
972 v->flags &= ~VAR_FROM_ENV; 972 v->flags &= ~VAR_FROM_ENV;
973 h = Hash_CreateEntry(&ctxt->context, name, NULL); 973 h = Hash_CreateEntry(&ctxt->context, name, NULL);
974 Hash_SetValue(h, v); 974 Hash_SetValue(h, v);
975 } 975 }
976 } 976 }
977 free(expanded_name); 977 free(expanded_name);
978} 978}
979 979
980/*- 980/*-
981 *----------------------------------------------------------------------- 981 *-----------------------------------------------------------------------
982 * Var_Exists -- 982 * Var_Exists --
983 * See if the given variable exists. 983 * See if the given variable exists.
984 * 984 *
985 * Input: 985 * Input:
986 * name Variable to find 986 * name Variable to find
987 * ctxt Context in which to start search 987 * ctxt Context in which to start search
988 * 988 *
989 * Results: 989 * Results:
990 * TRUE if it does, FALSE if it doesn't 990 * TRUE if it does, FALSE if it doesn't
991 * 991 *
992 * Side Effects: 992 * Side Effects:
993 * None. 993 * None.
994 * 994 *
995 *----------------------------------------------------------------------- 995 *-----------------------------------------------------------------------
996 */ 996 */
997Boolean 997Boolean
998Var_Exists(const char *name, GNode *ctxt) 998Var_Exists(const char *name, GNode *ctxt)
999{ 999{
1000 Var *v; 1000 Var *v;
1001 char *cp; 1001 char *cp;
1002 1002
1003 if ((cp = strchr(name, '$')) != NULL) 1003 if ((cp = strchr(name, '$')) != NULL)
1004 cp = Var_Subst(name, ctxt, VARE_WANTRES); 1004 cp = Var_Subst(name, ctxt, VARE_WANTRES);
1005 v = VarFind(cp ? cp : name, ctxt, FIND_CMD | FIND_GLOBAL | FIND_ENV); 1005 v = VarFind(cp ? cp : name, ctxt, FIND_CMD | FIND_GLOBAL | FIND_ENV);
1006 free(cp); 1006 free(cp);
1007 if (v == NULL) 1007 if (v == NULL)
1008 return FALSE; 1008 return FALSE;
1009 1009
1010 (void)VarFreeEnv(v, TRUE); 1010 (void)VarFreeEnv(v, TRUE);
1011 return TRUE; 1011 return TRUE;
1012} 1012}
1013 1013
1014/*- 1014/*-
1015 *----------------------------------------------------------------------- 1015 *-----------------------------------------------------------------------
1016 * Var_Value -- 1016 * Var_Value --
1017 * Return the unexpanded value of the given variable in the given 1017 * Return the unexpanded value of the given variable in the given
1018 * context. 1018 * context.
1019 * 1019 *
1020 * Input: 1020 * Input:
1021 * name name to find 1021 * name name to find
1022 * ctxt context in which to search for it 1022 * ctxt context in which to search for it
1023 * 1023 *
1024 * Results: 1024 * Results:
1025 * The value if the variable exists, NULL if it doesn't. 1025 * The value if the variable exists, NULL if it doesn't.
1026 * If the returned value is not NULL, the caller must free *freeIt 1026 * If the returned value is not NULL, the caller must free *freeIt
1027 * as soon as the returned value is no longer needed. 1027 * as soon as the returned value is no longer needed.
1028 *----------------------------------------------------------------------- 1028 *-----------------------------------------------------------------------
1029 */ 1029 */
1030char * 1030char *
1031Var_Value(const char *name, GNode *ctxt, char **freeIt) 1031Var_Value(const char *name, GNode *ctxt, char **freeIt)
1032{ 1032{
1033 Var *v = VarFind(name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); 1033 Var *v = VarFind(name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD);
1034 *freeIt = NULL; 1034 *freeIt = NULL;
1035 if (v == NULL) 1035 if (v == NULL)
1036 return NULL; 1036 return NULL;
1037 1037
1038 char *p = Buf_GetAll(&v->val, NULL); 1038 char *p = Buf_GetAll(&v->val, NULL);
1039 if (VarFreeEnv(v, FALSE)) 1039 if (VarFreeEnv(v, FALSE))
1040 *freeIt = p; 1040 *freeIt = p;
1041 return p; 1041 return p;
1042} 1042}
1043 1043
1044 1044
1045/* SepBuf is a string being built from "words", interleaved with separators. */ 1045/* SepBuf is a string being built from "words", interleaved with separators. */
1046typedef struct { 1046typedef struct {
1047 Buffer buf; 1047 Buffer buf;
1048 Boolean needSep; 1048 Boolean needSep;
1049 char sep; 1049 char sep;
1050} SepBuf; 1050} SepBuf;
1051 1051
1052static void 1052static void
1053SepBuf_Init(SepBuf *buf, char sep) 1053SepBuf_Init(SepBuf *buf, char sep)
1054{ 1054{
1055 Buf_Init(&buf->buf, 32 /* bytes */); 1055 Buf_Init(&buf->buf, 32 /* bytes */);
1056 buf->needSep = FALSE; 1056 buf->needSep = FALSE;
1057 buf->sep = sep; 1057 buf->sep = sep;
1058} 1058}
1059 1059
1060static void 1060static void
1061SepBuf_Sep(SepBuf *buf) 1061SepBuf_Sep(SepBuf *buf)
1062{ 1062{
1063 buf->needSep = TRUE; 1063 buf->needSep = TRUE;
1064} 1064}
1065 1065
1066static void 1066static void
1067SepBuf_AddBytes(SepBuf *buf, const char *mem, size_t mem_size) 1067SepBuf_AddBytes(SepBuf *buf, const char *mem, size_t mem_size)
1068{ 1068{
1069 if (mem_size == 0) 1069 if (mem_size == 0)
1070 return; 1070 return;
1071 if (buf->needSep && buf->sep != '\0') { 1071 if (buf->needSep && buf->sep != '\0') {
1072 Buf_AddByte(&buf->buf, buf->sep); 1072 Buf_AddByte(&buf->buf, buf->sep);
1073 buf->needSep = FALSE; 1073 buf->needSep = FALSE;
1074 } 1074 }
1075 Buf_AddBytes(&buf->buf, mem_size, mem); 1075 Buf_AddBytes(&buf->buf, mem_size, mem);
1076} 1076}
1077 1077
1078static void 1078static void
@@ -1527,1999 +1527,1999 @@ VarSelectWords(Byte sep, Boolean oneBigW @@ -1527,1999 +1527,1999 @@ VarSelectWords(Byte sep, Boolean oneBigW
1527 av = bmake_malloc((ac + 1) * sizeof(char *)); 1527 av = bmake_malloc((ac + 1) * sizeof(char *));
1528 as = bmake_strdup(str); 1528 as = bmake_strdup(str);
1529 av[0] = as; 1529 av[0] = as;
1530 av[1] = NULL; 1530 av[1] = NULL;
1531 } else { 1531 } else {
1532 av = brk_string(str, &ac, FALSE, &as); 1532 av = brk_string(str, &ac, FALSE, &as);
1533 } 1533 }
1534 1534
1535 /* 1535 /*
1536 * Now sanitize seldata. 1536 * Now sanitize seldata.
1537 * If seldata->start or seldata->end are negative, convert them to 1537 * If seldata->start or seldata->end are negative, convert them to
1538 * the positive equivalents (-1 gets converted to argc, -2 gets 1538 * the positive equivalents (-1 gets converted to argc, -2 gets
1539 * converted to (argc-1), etc.). 1539 * converted to (argc-1), etc.).
1540 */ 1540 */
1541 if (first < 0) 1541 if (first < 0)
1542 first += ac + 1; 1542 first += ac + 1;
1543 if (last < 0) 1543 if (last < 0)
1544 last += ac + 1; 1544 last += ac + 1;
1545 1545
1546 /* 1546 /*
1547 * We avoid scanning more of the list than we need to. 1547 * We avoid scanning more of the list than we need to.
1548 */ 1548 */
1549 if (first > last) { 1549 if (first > last) {
1550 start = MIN(ac, first) - 1; 1550 start = MIN(ac, first) - 1;
1551 end = MAX(0, last - 1); 1551 end = MAX(0, last - 1);
1552 step = -1; 1552 step = -1;
1553 } else { 1553 } else {
1554 start = MAX(0, first - 1); 1554 start = MAX(0, first - 1);
1555 end = MIN(ac, last); 1555 end = MIN(ac, last);
1556 step = 1; 1556 step = 1;
1557 } 1557 }
1558 1558
1559 for (i = start; (step < 0) == (i >= end); i += step) { 1559 for (i = start; (step < 0) == (i >= end); i += step) {
1560 SepBuf_AddStr(&buf, av[i]); 1560 SepBuf_AddStr(&buf, av[i]);
1561 SepBuf_Sep(&buf); 1561 SepBuf_Sep(&buf);
1562 } 1562 }
1563 1563
1564 free(as); 1564 free(as);
1565 free(av); 1565 free(av);
1566 1566
1567 return SepBuf_Destroy(&buf, FALSE); 1567 return SepBuf_Destroy(&buf, FALSE);
1568} 1568}
1569 1569
1570 1570
1571/* Callback for ModifyWords to implement the :tA modifier. 1571/* Callback for ModifyWords to implement the :tA modifier.
1572 * Replace each word with the result of realpath() if successful. */ 1572 * Replace each word with the result of realpath() if successful. */
1573static void 1573static void
1574ModifyWord_Realpath(const char *word, SepBuf *buf, void *data MAKE_ATTR_UNUSED) 1574ModifyWord_Realpath(const char *word, SepBuf *buf, void *data MAKE_ATTR_UNUSED)
1575{ 1575{
1576 struct stat st; 1576 struct stat st;
1577 char rbuf[MAXPATHLEN]; 1577 char rbuf[MAXPATHLEN];
1578 1578
1579 const char *rp = cached_realpath(word, rbuf); 1579 const char *rp = cached_realpath(word, rbuf);
1580 if (rp != NULL && *rp == '/' && stat(rp, &st) == 0) 1580 if (rp != NULL && *rp == '/' && stat(rp, &st) == 0)
1581 word = rp; 1581 word = rp;
1582 1582
1583 SepBuf_AddStr(buf, word); 1583 SepBuf_AddStr(buf, word);
1584} 1584}
1585 1585
1586/*- 1586/*-
1587 *----------------------------------------------------------------------- 1587 *-----------------------------------------------------------------------
1588 * Modify each of the words of the passed string using the given function. 1588 * Modify each of the words of the passed string using the given function.
1589 * 1589 *
1590 * Input: 1590 * Input:
1591 * str String whose words should be modified 1591 * str String whose words should be modified
1592 * modifyWord Function that modifies a single word 1592 * modifyWord Function that modifies a single word
1593 * data Custom data for modifyWord 1593 * data Custom data for modifyWord
1594 * 1594 *
1595 * Results: 1595 * Results:
1596 * A string of all the words modified appropriately. 1596 * A string of all the words modified appropriately.
1597 *----------------------------------------------------------------------- 1597 *-----------------------------------------------------------------------
1598 */ 1598 */
1599static char * 1599static char *
1600ModifyWords(GNode *ctx, Byte sep, Boolean oneBigWord, 1600ModifyWords(GNode *ctx, Byte sep, Boolean oneBigWord,
1601 const char *str, ModifyWordsCallback modifyWord, void *data) 1601 const char *str, ModifyWordsCallback modifyWord, void *data)
1602{ 1602{
1603 if (oneBigWord) { 1603 if (oneBigWord) {
1604 SepBuf result; 1604 SepBuf result;
1605 SepBuf_Init(&result, sep); 1605 SepBuf_Init(&result, sep);
1606 modifyWord(str, &result, data); 1606 modifyWord(str, &result, data);
1607 return SepBuf_Destroy(&result, FALSE); 1607 return SepBuf_Destroy(&result, FALSE);
1608 } 1608 }
1609 1609
1610 SepBuf result; 1610 SepBuf result;
1611 char **av; /* word list */ 1611 char **av; /* word list */
1612 char *as; /* word list memory */ 1612 char *as; /* word list memory */
1613 int ac, i; 1613 int ac, i;
1614 1614
1615 SepBuf_Init(&result, sep); 1615 SepBuf_Init(&result, sep);
1616 1616
1617 av = brk_string(str, &ac, FALSE, &as); 1617 av = brk_string(str, &ac, FALSE, &as);
1618 1618
1619 if (DEBUG(VAR)) { 1619 if (DEBUG(VAR)) {
1620 fprintf(debug_file, "ModifyWords: split \"%s\" into %d words\n", 1620 fprintf(debug_file, "ModifyWords: split \"%s\" into %d words\n",
1621 str, ac); 1621 str, ac);
1622 } 1622 }
1623 1623
1624 for (i = 0; i < ac; i++) { 1624 for (i = 0; i < ac; i++) {
1625 size_t orig_count = result.buf.count; 1625 size_t orig_count = result.buf.count;
1626 modifyWord(av[i], &result, data); 1626 modifyWord(av[i], &result, data);
1627 size_t count = result.buf.count; 1627 size_t count = result.buf.count;
1628 if (count != orig_count) 1628 if (count != orig_count)
1629 SepBuf_Sep(&result); 1629 SepBuf_Sep(&result);
1630 } 1630 }
1631 1631
1632 free(as); 1632 free(as);
1633 free(av); 1633 free(av);
1634 1634
1635 return SepBuf_Destroy(&result, FALSE); 1635 return SepBuf_Destroy(&result, FALSE);
1636} 1636}
1637 1637
1638 1638
1639static int 1639static int
1640VarWordCompare(const void *a, const void *b) 1640VarWordCompare(const void *a, const void *b)
1641{ 1641{
1642 int r = strcmp(*(const char * const *)a, *(const char * const *)b); 1642 int r = strcmp(*(const char * const *)a, *(const char * const *)b);
1643 return r; 1643 return r;
1644} 1644}
1645 1645
1646static int 1646static int
1647VarWordCompareReverse(const void *a, const void *b) 1647VarWordCompareReverse(const void *a, const void *b)
1648{ 1648{
1649 int r = strcmp(*(const char * const *)b, *(const char * const *)a); 1649 int r = strcmp(*(const char * const *)b, *(const char * const *)a);
1650 return r; 1650 return r;
1651} 1651}
1652 1652
1653/*- 1653/*-
1654 *----------------------------------------------------------------------- 1654 *-----------------------------------------------------------------------
1655 * VarOrder -- 1655 * VarOrder --
1656 * Order the words in the string. 1656 * Order the words in the string.
1657 * 1657 *
1658 * Input: 1658 * Input:
1659 * str String whose words should be sorted. 1659 * str String whose words should be sorted.
1660 * otype How to order: s - sort, x - random. 1660 * otype How to order: s - sort, x - random.
1661 * 1661 *
1662 * Results: 1662 * Results:
1663 * A string containing the words ordered. 1663 * A string containing the words ordered.
1664 * 1664 *
1665 * Side Effects: 1665 * Side Effects:
1666 * None. 1666 * None.
1667 * 1667 *
1668 *----------------------------------------------------------------------- 1668 *-----------------------------------------------------------------------
1669 */ 1669 */
1670static char * 1670static char *
1671VarOrder(const char *str, const char otype) 1671VarOrder(const char *str, const char otype)
1672{ 1672{
1673 Buffer buf; /* Buffer for the new string */ 1673 Buffer buf; /* Buffer for the new string */
1674 char **av; /* word list */ 1674 char **av; /* word list */
1675 char *as; /* word list memory */ 1675 char *as; /* word list memory */
1676 int ac, i; 1676 int ac, i;
1677 1677
1678 Buf_Init(&buf, 0); 1678 Buf_Init(&buf, 0);
1679 1679
1680 av = brk_string(str, &ac, FALSE, &as); 1680 av = brk_string(str, &ac, FALSE, &as);
1681 1681
1682 if (ac > 0) { 1682 if (ac > 0) {
1683 switch (otype) { 1683 switch (otype) {
1684 case 'r': /* reverse sort alphabetically */ 1684 case 'r': /* reverse sort alphabetically */
1685 qsort(av, ac, sizeof(char *), VarWordCompareReverse); 1685 qsort(av, ac, sizeof(char *), VarWordCompareReverse);
1686 break; 1686 break;
1687 case 's': /* sort alphabetically */ 1687 case 's': /* sort alphabetically */
1688 qsort(av, ac, sizeof(char *), VarWordCompare); 1688 qsort(av, ac, sizeof(char *), VarWordCompare);
1689 break; 1689 break;
1690 case 'x': /* randomize */ 1690 case 'x': /* randomize */
1691 /* 1691 /*
1692 * We will use [ac..2] range for mod factors. This will produce 1692 * We will use [ac..2] range for mod factors. This will produce
1693 * random numbers in [(ac-1)..0] interval, and minimal 1693 * random numbers in [(ac-1)..0] interval, and minimal
1694 * reasonable value for mod factor is 2 (the mod 1 will produce 1694 * reasonable value for mod factor is 2 (the mod 1 will produce
1695 * 0 with probability 1). 1695 * 0 with probability 1).
1696 */ 1696 */
1697 for (i = ac - 1; i > 0; i--) { 1697 for (i = ac - 1; i > 0; i--) {
1698 int rndidx = random() % (i + 1); 1698 int rndidx = random() % (i + 1);
1699 char *t = av[i]; 1699 char *t = av[i];
1700 av[i] = av[rndidx]; 1700 av[i] = av[rndidx];
1701 av[rndidx] = t; 1701 av[rndidx] = t;
1702 } 1702 }
1703 } 1703 }
1704 } 1704 }
1705 1705
1706 for (i = 0; i < ac; i++) { 1706 for (i = 0; i < ac; i++) {
1707 if (i != 0) 1707 if (i != 0)
1708 Buf_AddByte(&buf, ' '); 1708 Buf_AddByte(&buf, ' ');
1709 Buf_AddStr(&buf, av[i]); 1709 Buf_AddStr(&buf, av[i]);
1710 } 1710 }
1711 1711
1712 free(as); 1712 free(as);
1713 free(av); 1713 free(av);
1714 1714
1715 return Buf_Destroy(&buf, FALSE); 1715 return Buf_Destroy(&buf, FALSE);
1716} 1716}
1717 1717
1718 1718
1719/* Remove adjacent duplicate words. */ 1719/* Remove adjacent duplicate words. */
1720static char * 1720static char *
1721VarUniq(const char *str) 1721VarUniq(const char *str)
1722{ 1722{
1723 Buffer buf; /* Buffer for new string */ 1723 Buffer buf; /* Buffer for new string */
1724 char **av; /* List of words to affect */ 1724 char **av; /* List of words to affect */
1725 char *as; /* Word list memory */ 1725 char *as; /* Word list memory */
1726 int ac, i, j; 1726 int ac, i, j;
1727 1727
1728 Buf_Init(&buf, 0); 1728 Buf_Init(&buf, 0);
1729 av = brk_string(str, &ac, FALSE, &as); 1729 av = brk_string(str, &ac, FALSE, &as);
1730 1730
1731 if (ac > 1) { 1731 if (ac > 1) {
1732 for (j = 0, i = 1; i < ac; i++) 1732 for (j = 0, i = 1; i < ac; i++)
1733 if (strcmp(av[i], av[j]) != 0 && (++j != i)) 1733 if (strcmp(av[i], av[j]) != 0 && (++j != i))
1734 av[j] = av[i]; 1734 av[j] = av[i];
1735 ac = j + 1; 1735 ac = j + 1;
1736 } 1736 }
1737 1737
1738 for (i = 0; i < ac; i++) { 1738 for (i = 0; i < ac; i++) {
1739 if (i != 0) 1739 if (i != 0)
1740 Buf_AddByte(&buf, ' '); 1740 Buf_AddByte(&buf, ' ');
1741 Buf_AddStr(&buf, av[i]); 1741 Buf_AddStr(&buf, av[i]);
1742 } 1742 }
1743 1743
1744 free(as); 1744 free(as);
1745 free(av); 1745 free(av);
1746 1746
1747 return Buf_Destroy(&buf, FALSE); 1747 return Buf_Destroy(&buf, FALSE);
1748} 1748}
1749 1749
1750/*- 1750/*-
1751 *----------------------------------------------------------------------- 1751 *-----------------------------------------------------------------------
1752 * VarRange -- 1752 * VarRange --
1753 * Return an integer sequence 1753 * Return an integer sequence
1754 * 1754 *
1755 * Input: 1755 * Input:
1756 * str String whose words provide default range 1756 * str String whose words provide default range
1757 * ac range length, if 0 use str words 1757 * ac range length, if 0 use str words
1758 * 1758 *
1759 * Side Effects: 1759 * Side Effects:
1760 * None. 1760 * None.
1761 * 1761 *
1762 *----------------------------------------------------------------------- 1762 *-----------------------------------------------------------------------
1763 */ 1763 */
1764static char * 1764static char *
1765VarRange(const char *str, int ac) 1765VarRange(const char *str, int ac)
1766{ 1766{
1767 Buffer buf; /* Buffer for new string */ 1767 Buffer buf; /* Buffer for new string */
1768 char **av; /* List of words to affect */ 1768 char **av; /* List of words to affect */
1769 char *as; /* Word list memory */ 1769 char *as; /* Word list memory */
1770 int i; 1770 int i;
1771 1771
1772 Buf_Init(&buf, 0); 1772 Buf_Init(&buf, 0);
1773 if (ac > 0) { 1773 if (ac > 0) {
1774 as = NULL; 1774 as = NULL;
1775 av = NULL; 1775 av = NULL;
1776 } else { 1776 } else {
1777 av = brk_string(str, &ac, FALSE, &as); 1777 av = brk_string(str, &ac, FALSE, &as);
1778 } 1778 }
1779 for (i = 0; i < ac; i++) { 1779 for (i = 0; i < ac; i++) {
1780 if (i != 0) 1780 if (i != 0)
1781 Buf_AddByte(&buf, ' '); 1781 Buf_AddByte(&buf, ' ');
1782 Buf_AddInt(&buf, 1 + i); 1782 Buf_AddInt(&buf, 1 + i);
1783 } 1783 }
1784 1784
1785 free(as); 1785 free(as);
1786 free(av); 1786 free(av);
1787 1787
1788 return Buf_Destroy(&buf, FALSE); 1788 return Buf_Destroy(&buf, FALSE);
1789} 1789}
1790 1790
1791 1791
1792/*- 1792/*-
1793 * Parse a text part of a modifier such as the "from" and "to" in :S/from/to/ 1793 * Parse a text part of a modifier such as the "from" and "to" in :S/from/to/
1794 * or the :@ modifier, until the next unescaped delimiter. The delimiter, as 1794 * or the :@ modifier, until the next unescaped delimiter. The delimiter, as
1795 * well as the backslash or the dollar, can be escaped with a backslash. 1795 * well as the backslash or the dollar, can be escaped with a backslash.
1796 * 1796 *
1797 * Return the parsed (and possibly expanded) string, or NULL if no delimiter 1797 * Return the parsed (and possibly expanded) string, or NULL if no delimiter
1798 * was found. 1798 * was found.
1799 * 1799 *
1800 * Nested variables in the text are expanded unless VARE_NOSUBST is set. 1800 * Nested variables in the text are expanded unless VARE_NOSUBST is set.
1801 * 1801 *
1802 * If out_length is specified, store the length of the returned string, just 1802 * If out_length is specified, store the length of the returned string, just
1803 * to save another strlen call. 1803 * to save another strlen call.
1804 * 1804 *
1805 * If out_pflags is specified and the last character of the pattern is a $, 1805 * If out_pflags is specified and the last character of the pattern is a $,
1806 * set the VARP_ANCHOR_END bit of mpflags (for the first part of the :S 1806 * set the VARP_ANCHOR_END bit of mpflags (for the first part of the :S
1807 * modifier). 1807 * modifier).
1808 * 1808 *
1809 * If subst is specified, handle escaped ampersands and replace unescaped 1809 * If subst is specified, handle escaped ampersands and replace unescaped
1810 * ampersands with the lhs of the pattern (for the second part of the :S 1810 * ampersands with the lhs of the pattern (for the second part of the :S
1811 * modifier). 1811 * modifier).
1812 */ 1812 */
1813static char * 1813static char *
1814ParseModifierPart(const char **tstr, int delim, VarEvalFlags eflags, 1814ParseModifierPart(const char **tstr, int delim, VarEvalFlags eflags,
1815 GNode *ctxt, size_t *out_length, 1815 GNode *ctxt, size_t *out_length,
1816 VarPatternFlags *out_pflags, ModifyWord_SubstArgs *subst) 1816 VarPatternFlags *out_pflags, ModifyWord_SubstArgs *subst)
1817{ 1817{
1818 const char *cp; 1818 const char *cp;
1819 char *rstr; 1819 char *rstr;
1820 Buffer buf; 1820 Buffer buf;
1821 VarEvalFlags errnum = eflags & VARE_UNDEFERR; 1821 VarEvalFlags errnum = eflags & VARE_UNDEFERR;
1822 1822
1823 Buf_Init(&buf, 0); 1823 Buf_Init(&buf, 0);
1824 1824
1825 /* 1825 /*
1826 * Skim through until the matching delimiter is found; 1826 * Skim through until the matching delimiter is found;
1827 * pick up variable substitutions on the way. Also allow 1827 * pick up variable substitutions on the way. Also allow
1828 * backslashes to quote the delimiter, $, and \, but don't 1828 * backslashes to quote the delimiter, $, and \, but don't
1829 * touch other backslashes. 1829 * touch other backslashes.
1830 */ 1830 */
1831 for (cp = *tstr; *cp != '\0' && *cp != delim; cp++) { 1831 for (cp = *tstr; *cp != '\0' && *cp != delim; cp++) {
1832 Boolean is_escaped = cp[0] == '\\' && ( 1832 Boolean is_escaped = cp[0] == '\\' && (
1833 cp[1] == delim || cp[1] == '\\' || cp[1] == '$' || 1833 cp[1] == delim || cp[1] == '\\' || cp[1] == '$' ||
1834 (cp[1] == '&' && subst != NULL)); 1834 (cp[1] == '&' && subst != NULL));
1835 if (is_escaped) { 1835 if (is_escaped) {
1836 Buf_AddByte(&buf, cp[1]); 1836 Buf_AddByte(&buf, cp[1]);
1837 cp++; 1837 cp++;
1838 } else if (*cp == '$') { 1838 } else if (*cp == '$') {
1839 if (cp[1] == delim) { /* Unescaped $ at end of pattern */ 1839 if (cp[1] == delim) { /* Unescaped $ at end of pattern */
1840 if (out_pflags != NULL) 1840 if (out_pflags != NULL)
1841 *out_pflags |= VARP_ANCHOR_END; 1841 *out_pflags |= VARP_ANCHOR_END;
1842 else 1842 else
1843 Buf_AddByte(&buf, *cp); 1843 Buf_AddByte(&buf, *cp);
1844 } else { 1844 } else {
1845 if (eflags & VARE_WANTRES) { 1845 if (eflags & VARE_WANTRES) {
1846 const char *cp2; 1846 const char *cp2;
1847 int len; 1847 int len;
1848 void *freeIt; 1848 void *freeIt;
1849 1849
1850 /* 1850 /*
1851 * If unescaped dollar sign not before the 1851 * If unescaped dollar sign not before the
1852 * delimiter, assume it's a variable 1852 * delimiter, assume it's a variable
1853 * substitution and recurse. 1853 * substitution and recurse.
1854 */ 1854 */
1855 cp2 = Var_Parse(cp, ctxt, errnum | (eflags & VARE_WANTRES), 1855 cp2 = Var_Parse(cp, ctxt, errnum | (eflags & VARE_WANTRES),
1856 &len, &freeIt); 1856 &len, &freeIt);
1857 Buf_AddStr(&buf, cp2); 1857 Buf_AddStr(&buf, cp2);
1858 free(freeIt); 1858 free(freeIt);
1859 cp += len - 1; 1859 cp += len - 1;
1860 } else { 1860 } else {
1861 const char *cp2 = &cp[1]; 1861 const char *cp2 = &cp[1];
1862 1862
1863 if (*cp2 == PROPEN || *cp2 == BROPEN) { 1863 if (*cp2 == PROPEN || *cp2 == BROPEN) {
1864 /* 1864 /*
1865 * Find the end of this variable reference 1865 * Find the end of this variable reference
1866 * and suck it in without further ado. 1866 * and suck it in without further ado.
1867 * It will be interpreted later. 1867 * It will be interpreted later.
1868 */ 1868 */
1869 int have = *cp2; 1869 int have = *cp2;
1870 int want = (*cp2 == PROPEN) ? PRCLOSE : BRCLOSE; 1870 int want = (*cp2 == PROPEN) ? PRCLOSE : BRCLOSE;
1871 int depth = 1; 1871 int depth = 1;
1872 1872
1873 for (++cp2; *cp2 != '\0' && depth > 0; ++cp2) { 1873 for (++cp2; *cp2 != '\0' && depth > 0; ++cp2) {
1874 if (cp2[-1] != '\\') { 1874 if (cp2[-1] != '\\') {
1875 if (*cp2 == have) 1875 if (*cp2 == have)
1876 ++depth; 1876 ++depth;
1877 if (*cp2 == want) 1877 if (*cp2 == want)
1878 --depth; 1878 --depth;
1879 } 1879 }
1880 } 1880 }
1881 Buf_AddBytesBetween(&buf, cp, cp2); 1881 Buf_AddBytesBetween(&buf, cp, cp2);
1882 cp = --cp2; 1882 cp = --cp2;
1883 } else 1883 } else
1884 Buf_AddByte(&buf, *cp); 1884 Buf_AddByte(&buf, *cp);
1885 } 1885 }
1886 } 1886 }
1887 } else if (subst != NULL && *cp == '&') 1887 } else if (subst != NULL && *cp == '&')
1888 Buf_AddBytes(&buf, subst->lhsLen, subst->lhs); 1888 Buf_AddBytes(&buf, subst->lhsLen, subst->lhs);
1889 else 1889 else
1890 Buf_AddByte(&buf, *cp); 1890 Buf_AddByte(&buf, *cp);
1891 } 1891 }
1892 1892
1893 if (*cp != delim) { 1893 if (*cp != delim) {
1894 *tstr = cp; 1894 *tstr = cp;
1895 return NULL; 1895 return NULL;
1896 } 1896 }
1897 1897
1898 *tstr = ++cp; 1898 *tstr = ++cp;
1899 if (out_length != NULL) 1899 if (out_length != NULL)
1900 *out_length = Buf_Size(&buf); 1900 *out_length = Buf_Size(&buf);
1901 rstr = Buf_Destroy(&buf, FALSE); 1901 rstr = Buf_Destroy(&buf, FALSE);
1902 if (DEBUG(VAR)) 1902 if (DEBUG(VAR))
1903 fprintf(debug_file, "Modifier part: \"%s\"\n", rstr); 1903 fprintf(debug_file, "Modifier part: \"%s\"\n", rstr);
1904 return rstr; 1904 return rstr;
1905} 1905}
1906 1906
1907/*- 1907/*-
1908 *----------------------------------------------------------------------- 1908 *-----------------------------------------------------------------------
1909 * VarQuote -- 1909 * VarQuote --
1910 * Quote shell meta-characters and space characters in the string 1910 * Quote shell meta-characters and space characters in the string
1911 * if quoteDollar is set, also quote and double any '$' characters. 1911 * if quoteDollar is set, also quote and double any '$' characters.
1912 * 1912 *
1913 * Results: 1913 * Results:
1914 * The quoted string 1914 * The quoted string
1915 * 1915 *
1916 * Side Effects: 1916 * Side Effects:
1917 * None. 1917 * None.
1918 * 1918 *
1919 *----------------------------------------------------------------------- 1919 *-----------------------------------------------------------------------
1920 */ 1920 */
1921static char * 1921static char *
1922VarQuote(char *str, Boolean quoteDollar) 1922VarQuote(char *str, Boolean quoteDollar)
1923{ 1923{
1924 Buffer buf; 1924 Buffer buf;
1925 Buf_Init(&buf, 0); 1925 Buf_Init(&buf, 0);
1926 1926
1927 for (; *str != '\0'; str++) { 1927 for (; *str != '\0'; str++) {
1928 if (*str == '\n') { 1928 if (*str == '\n') {
1929 const char *newline = Shell_GetNewline(); 1929 const char *newline = Shell_GetNewline();
1930 if (newline == NULL) 1930 if (newline == NULL)
1931 newline = "\\\n"; 1931 newline = "\\\n";
1932 Buf_AddStr(&buf, newline); 1932 Buf_AddStr(&buf, newline);
1933 continue; 1933 continue;
1934 } 1934 }
1935 if (isspace((unsigned char)*str) || ismeta((unsigned char)*str)) 1935 if (isspace((unsigned char)*str) || ismeta((unsigned char)*str))
1936 Buf_AddByte(&buf, '\\'); 1936 Buf_AddByte(&buf, '\\');
1937 Buf_AddByte(&buf, *str); 1937 Buf_AddByte(&buf, *str);
1938 if (quoteDollar && *str == '$') 1938 if (quoteDollar && *str == '$')
1939 Buf_AddStr(&buf, "\\$"); 1939 Buf_AddStr(&buf, "\\$");
1940 } 1940 }
1941 1941
1942 str = Buf_Destroy(&buf, FALSE); 1942 str = Buf_Destroy(&buf, FALSE);
1943 if (DEBUG(VAR)) 1943 if (DEBUG(VAR))
1944 fprintf(debug_file, "QuoteMeta: [%s]\n", str); 1944 fprintf(debug_file, "QuoteMeta: [%s]\n", str);
1945 return str; 1945 return str;
1946} 1946}
1947 1947
1948/*- 1948/*-
1949 *----------------------------------------------------------------------- 1949 *-----------------------------------------------------------------------
1950 * VarHash -- 1950 * VarHash --
1951 * Hash the string using the MurmurHash3 algorithm. 1951 * Hash the string using the MurmurHash3 algorithm.
1952 * Output is computed using 32bit Little Endian arithmetic. 1952 * Output is computed using 32bit Little Endian arithmetic.
1953 * 1953 *
1954 * Input: 1954 * Input:
1955 * str String to modify 1955 * str String to modify
1956 * 1956 *
1957 * Results: 1957 * Results:
1958 * Hash value of str, encoded as 8 hex digits. 1958 * Hash value of str, encoded as 8 hex digits.
1959 * 1959 *
1960 * Side Effects: 1960 * Side Effects:
1961 * None. 1961 * None.
1962 * 1962 *
1963 *----------------------------------------------------------------------- 1963 *-----------------------------------------------------------------------
1964 */ 1964 */
1965static char * 1965static char *
1966VarHash(const char *str) 1966VarHash(const char *str)
1967{ 1967{
1968 static const char hexdigits[16] = "0123456789abcdef"; 1968 static const char hexdigits[16] = "0123456789abcdef";
1969 Buffer buf; 1969 Buffer buf;
1970 size_t len, len2; 1970 size_t len, len2;
1971 const unsigned char *ustr = (const unsigned char *)str; 1971 const unsigned char *ustr = (const unsigned char *)str;
1972 uint32_t h, k, c1, c2; 1972 uint32_t h, k, c1, c2;
1973 1973
1974 h = 0x971e137bU; 1974 h = 0x971e137bU;
1975 c1 = 0x95543787U; 1975 c1 = 0x95543787U;
1976 c2 = 0x2ad7eb25U; 1976 c2 = 0x2ad7eb25U;
1977 len2 = strlen(str); 1977 len2 = strlen(str);
1978 1978
1979 for (len = len2; len; ) { 1979 for (len = len2; len; ) {
1980 k = 0; 1980 k = 0;
1981 switch (len) { 1981 switch (len) {
1982 default: 1982 default:
1983 k = ((uint32_t)ustr[3] << 24) | 1983 k = ((uint32_t)ustr[3] << 24) |
1984 ((uint32_t)ustr[2] << 16) | 1984 ((uint32_t)ustr[2] << 16) |
1985 ((uint32_t)ustr[1] << 8) | 1985 ((uint32_t)ustr[1] << 8) |
1986 (uint32_t)ustr[0]; 1986 (uint32_t)ustr[0];
1987 len -= 4; 1987 len -= 4;
1988 ustr += 4; 1988 ustr += 4;
1989 break; 1989 break;
1990 case 3: 1990 case 3:
1991 k |= (uint32_t)ustr[2] << 16; 1991 k |= (uint32_t)ustr[2] << 16;
1992 /* FALLTHROUGH */ 1992 /* FALLTHROUGH */
1993 case 2: 1993 case 2:
1994 k |= (uint32_t)ustr[1] << 8; 1994 k |= (uint32_t)ustr[1] << 8;
1995 /* FALLTHROUGH */ 1995 /* FALLTHROUGH */
1996 case 1: 1996 case 1:
1997 k |= (uint32_t)ustr[0]; 1997 k |= (uint32_t)ustr[0];
1998 len = 0; 1998 len = 0;
1999 } 1999 }
2000 c1 = c1 * 5 + 0x7b7d159cU; 2000 c1 = c1 * 5 + 0x7b7d159cU;
2001 c2 = c2 * 5 + 0x6bce6396U; 2001 c2 = c2 * 5 + 0x6bce6396U;
2002 k *= c1; 2002 k *= c1;
2003 k = (k << 11) ^ (k >> 21); 2003 k = (k << 11) ^ (k >> 21);
2004 k *= c2; 2004 k *= c2;
2005 h = (h << 13) ^ (h >> 19); 2005 h = (h << 13) ^ (h >> 19);
2006 h = h * 5 + 0x52dce729U; 2006 h = h * 5 + 0x52dce729U;
2007 h ^= k; 2007 h ^= k;
2008 } 2008 }
2009 h ^= len2; 2009 h ^= len2;
2010 h *= 0x85ebca6b; 2010 h *= 0x85ebca6b;
2011 h ^= h >> 13; 2011 h ^= h >> 13;
2012 h *= 0xc2b2ae35; 2012 h *= 0xc2b2ae35;
2013 h ^= h >> 16; 2013 h ^= h >> 16;
2014 2014
2015 Buf_Init(&buf, 0); 2015 Buf_Init(&buf, 0);
2016 for (len = 0; len < 8; ++len) { 2016 for (len = 0; len < 8; ++len) {
2017 Buf_AddByte(&buf, hexdigits[h & 15]); 2017 Buf_AddByte(&buf, hexdigits[h & 15]);
2018 h >>= 4; 2018 h >>= 4;
2019 } 2019 }
2020 2020
2021 return Buf_Destroy(&buf, FALSE); 2021 return Buf_Destroy(&buf, FALSE);
2022} 2022}
2023 2023
2024static char * 2024static char *
2025VarStrftime(const char *fmt, int zulu, time_t utc) 2025VarStrftime(const char *fmt, int zulu, time_t utc)
2026{ 2026{
2027 char buf[BUFSIZ]; 2027 char buf[BUFSIZ];
2028 2028
2029 if (!utc) 2029 if (!utc)
2030 time(&utc); 2030 time(&utc);
2031 if (!*fmt) 2031 if (!*fmt)
2032 fmt = "%c"; 2032 fmt = "%c";
2033 strftime(buf, sizeof(buf), fmt, zulu ? gmtime(&utc) : localtime(&utc)); 2033 strftime(buf, sizeof(buf), fmt, zulu ? gmtime(&utc) : localtime(&utc));
2034 2034
2035 buf[sizeof(buf) - 1] = '\0'; 2035 buf[sizeof(buf) - 1] = '\0';
2036 return bmake_strdup(buf); 2036 return bmake_strdup(buf);
2037} 2037}
2038 2038
2039/* The ApplyModifier functions all work in the same way. 2039/* The ApplyModifier functions all work in the same way.
2040 * They parse the modifier (often until the next colon) and store the 2040 * They parse the modifier (often until the next colon) and store the
2041 * updated position for the parser into st->next. 2041 * updated position for the parser into st->next.
2042 * They take the st->val and generate st->newVal from it. 2042 * They take the st->val and generate st->newVal from it.
2043 * On failure, many of them update st->missing_delim. 2043 * On failure, many of them update st->missing_delim.
2044 */ 2044 */
2045typedef struct { 2045typedef struct {
2046 int startc; /* '\0' or '{' or '(' */ 2046 int startc; /* '\0' or '{' or '(' */
2047 int endc; 2047 int endc;
2048 Var *v; 2048 Var *v;
2049 GNode *ctxt; 2049 GNode *ctxt;
2050 VarEvalFlags eflags; 2050 VarEvalFlags eflags;
2051 2051
2052 char *val; /* The value of the expression before the 2052 char *val; /* The value of the expression before the
2053 * modifier is applied */ 2053 * modifier is applied */
2054 char *newVal; /* The new value after applying the modifier 2054 char *newVal; /* The new value after applying the modifier
2055 * to the expression */ 2055 * to the expression */
2056 const char *next; /* The position where parsing continues 2056 const char *next; /* The position where parsing continues
2057 * after the current modifier. */ 2057 * after the current modifier. */
2058 char missing_delim; /* For error reporting */ 2058 char missing_delim; /* For error reporting */
2059 2059
2060 Byte sep; /* Word separator in expansions */ 2060 Byte sep; /* Word separator in expansions */
2061 Boolean oneBigWord; /* TRUE if we will treat the variable as a 2061 Boolean oneBigWord; /* TRUE if we will treat the variable as a
2062 * single big word, even if it contains 2062 * single big word, even if it contains
2063 * embedded spaces (as opposed to the 2063 * embedded spaces (as opposed to the
2064 * usual behaviour of treating it as 2064 * usual behaviour of treating it as
2065 * several space-separated words). */ 2065 * several space-separated words). */
2066 2066
2067} ApplyModifiersState; 2067} ApplyModifiersState;
2068 2068
2069/* we now have some modifiers with long names */ 2069/* we now have some modifiers with long names */
2070static Boolean 2070static Boolean
2071ModMatch(const char *mod, const char *modname, char endc) 2071ModMatch(const char *mod, const char *modname, char endc)
2072{ 2072{
2073 size_t n = strlen(modname); 2073 size_t n = strlen(modname);
2074 return strncmp(mod, modname, n) == 0 && 2074 return strncmp(mod, modname, n) == 0 &&
2075 (mod[n] == endc || mod[n] == ':'); 2075 (mod[n] == endc || mod[n] == ':');
2076} 2076}
2077 2077
2078static inline Boolean 2078static inline Boolean
2079ModMatchEq(const char *mod, const char *modname, char endc) 2079ModMatchEq(const char *mod, const char *modname, char endc)
2080{ 2080{
2081 size_t n = strlen(modname); 2081 size_t n = strlen(modname);
2082 return strncmp(mod, modname, n) == 0 && 2082 return strncmp(mod, modname, n) == 0 &&
2083 (mod[n] == endc || mod[n] == ':' || mod[n] == '='); 2083 (mod[n] == endc || mod[n] == ':' || mod[n] == '=');
2084} 2084}
2085 2085
2086/* :@var@...${var}...@ */ 2086/* :@var@...${var}...@ */
2087static Boolean 2087static Boolean
2088ApplyModifier_Loop(const char *mod, ApplyModifiersState *st) { 2088ApplyModifier_Loop(const char *mod, ApplyModifiersState *st) {
2089 ModifyWord_LoopArgs args; 2089 ModifyWord_LoopArgs args;
2090 2090
2091 args.ctx = st->ctxt; 2091 args.ctx = st->ctxt;
2092 st->next = mod + 1; 2092 st->next = mod + 1;
2093 char delim = '@'; 2093 char delim = '@';
2094 args.tvar = ParseModifierPart(&st->next, delim, st->eflags & ~VARE_WANTRES, 2094 args.tvar = ParseModifierPart(&st->next, delim, st->eflags & ~VARE_WANTRES,
2095 st->ctxt, NULL, NULL, NULL); 2095 st->ctxt, NULL, NULL, NULL);
2096 if (args.tvar == NULL) { 2096 if (args.tvar == NULL) {
2097 st->missing_delim = delim; 2097 st->missing_delim = delim;
2098 return FALSE; 2098 return FALSE;
2099 } 2099 }
2100 2100
2101 args.str = ParseModifierPart(&st->next, delim, st->eflags & ~VARE_WANTRES, 2101 args.str = ParseModifierPart(&st->next, delim, st->eflags & ~VARE_WANTRES,
2102 st->ctxt, NULL, NULL, NULL); 2102 st->ctxt, NULL, NULL, NULL);
2103 if (args.str == NULL) { 2103 if (args.str == NULL) {
2104 st->missing_delim = delim; 2104 st->missing_delim = delim;
2105 return FALSE; 2105 return FALSE;
2106 } 2106 }
2107 2107
2108 args.eflags = st->eflags & (VARE_UNDEFERR | VARE_WANTRES); 2108 args.eflags = st->eflags & (VARE_UNDEFERR | VARE_WANTRES);
2109 int prev_sep = st->sep; 2109 int prev_sep = st->sep;
2110 st->sep = ' '; /* XXX: this is inconsistent */ 2110 st->sep = ' '; /* XXX: this is inconsistent */
2111 st->newVal = ModifyWords(st->ctxt, st->sep, st->oneBigWord, st->val, 2111 st->newVal = ModifyWords(st->ctxt, st->sep, st->oneBigWord, st->val,
2112 ModifyWord_Loop, &args); 2112 ModifyWord_Loop, &args);
2113 st->sep = prev_sep; 2113 st->sep = prev_sep;
2114 Var_Delete(args.tvar, st->ctxt); 2114 Var_Delete(args.tvar, st->ctxt);
2115 free(args.tvar); 2115 free(args.tvar);
2116 free(args.str); 2116 free(args.str);
2117 return TRUE; 2117 return TRUE;
2118} 2118}
2119 2119
2120/* :Ddefined or :Uundefined */ 2120/* :Ddefined or :Uundefined */
2121static void 2121static void
2122ApplyModifier_Defined(const char *mod, ApplyModifiersState *st) 2122ApplyModifier_Defined(const char *mod, ApplyModifiersState *st)
2123{ 2123{
2124 Buffer buf; /* Buffer for patterns */ 2124 Buffer buf; /* Buffer for patterns */
2125 VarEvalFlags neflags; 2125 VarEvalFlags neflags;
2126 2126
2127 if (st->eflags & VARE_WANTRES) { 2127 if (st->eflags & VARE_WANTRES) {
2128 Boolean wantres; 2128 Boolean wantres;
2129 if (*mod == 'U') 2129 if (*mod == 'U')
2130 wantres = ((st->v->flags & VAR_JUNK) != 0); 2130 wantres = ((st->v->flags & VAR_JUNK) != 0);
2131 else 2131 else
2132 wantres = ((st->v->flags & VAR_JUNK) == 0); 2132 wantres = ((st->v->flags & VAR_JUNK) == 0);
2133 neflags = st->eflags & ~VARE_WANTRES; 2133 neflags = st->eflags & ~VARE_WANTRES;
2134 if (wantres) 2134 if (wantres)
2135 neflags |= VARE_WANTRES; 2135 neflags |= VARE_WANTRES;
2136 } else 2136 } else
2137 neflags = st->eflags; 2137 neflags = st->eflags;
2138 2138
2139 /* 2139 /*
2140 * Pass through mod looking for 1) escaped delimiters, 2140 * Pass through mod looking for 1) escaped delimiters,
2141 * '$'s and backslashes (place the escaped character in 2141 * '$'s and backslashes (place the escaped character in
2142 * uninterpreted) and 2) unescaped $'s that aren't before 2142 * uninterpreted) and 2) unescaped $'s that aren't before
2143 * the delimiter (expand the variable substitution). 2143 * the delimiter (expand the variable substitution).
2144 * The result is left in the Buffer buf. 2144 * The result is left in the Buffer buf.
2145 */ 2145 */
2146 Buf_Init(&buf, 0); 2146 Buf_Init(&buf, 0);
2147 const char *p = mod + 1; 2147 const char *p = mod + 1;
2148 while (*p != st->endc && *p != ':' && *p != '\0') { 2148 while (*p != st->endc && *p != ':' && *p != '\0') {
2149 if (*p == '\\' && 2149 if (*p == '\\' &&
2150 (p[1] == ':' || p[1] == '$' || p[1] == st->endc || p[1] == '\\')) { 2150 (p[1] == ':' || p[1] == '$' || p[1] == st->endc || p[1] == '\\')) {
2151 Buf_AddByte(&buf, p[1]); 2151 Buf_AddByte(&buf, p[1]);
2152 p += 2; 2152 p += 2;
2153 } else if (*p == '$') { 2153 } else if (*p == '$') {
2154 /* 2154 /*
2155 * If unescaped dollar sign, assume it's a 2155 * If unescaped dollar sign, assume it's a
2156 * variable substitution and recurse. 2156 * variable substitution and recurse.
2157 */ 2157 */
2158 const char *cp2; 2158 const char *cp2;
2159 int len; 2159 int len;
2160 void *freeIt; 2160 void *freeIt;
2161 2161
2162 cp2 = Var_Parse(p, st->ctxt, neflags, &len, &freeIt); 2162 cp2 = Var_Parse(p, st->ctxt, neflags, &len, &freeIt);
2163 Buf_AddStr(&buf, cp2); 2163 Buf_AddStr(&buf, cp2);
2164 free(freeIt); 2164 free(freeIt);
2165 p += len; 2165 p += len;
2166 } else { 2166 } else {
2167 Buf_AddByte(&buf, *p); 2167 Buf_AddByte(&buf, *p);
2168 p++; 2168 p++;
2169 } 2169 }
2170 } 2170 }
2171 2171
2172 st->next = p; 2172 st->next = p;
2173 2173
2174 if (st->v->flags & VAR_JUNK) 2174 if (st->v->flags & VAR_JUNK)
2175 st->v->flags |= VAR_KEEP; 2175 st->v->flags |= VAR_KEEP;
2176 if (neflags & VARE_WANTRES) { 2176 if (neflags & VARE_WANTRES) {
2177 st->newVal = Buf_Destroy(&buf, FALSE); 2177 st->newVal = Buf_Destroy(&buf, FALSE);
2178 } else { 2178 } else {
2179 st->newVal = st->val; 2179 st->newVal = st->val;
2180 Buf_Destroy(&buf, TRUE); 2180 Buf_Destroy(&buf, TRUE);
2181 } 2181 }
2182} 2182}
2183 2183
2184/* :gmtime */ 2184/* :gmtime */
2185static Boolean 2185static Boolean
2186ApplyModifier_Gmtime(const char *mod, ApplyModifiersState *st) 2186ApplyModifier_Gmtime(const char *mod, ApplyModifiersState *st)
2187{ 2187{
2188 if (!ModMatchEq(mod, "gmtime", st->endc)) { 2188 if (!ModMatchEq(mod, "gmtime", st->endc)) {
2189 st->next = mod + 1; 2189 st->next = mod + 1;
2190 return FALSE; 2190 return FALSE;
2191 } 2191 }
2192 2192
2193 time_t utc; 2193 time_t utc;
2194 if (mod[6] == '=') { 2194 if (mod[6] == '=') {
2195 char *ep; 2195 char *ep;
2196 utc = strtoul(mod + 7, &ep, 10); 2196 utc = strtoul(mod + 7, &ep, 10);
2197 st->next = ep; 2197 st->next = ep;
2198 } else { 2198 } else {
2199 utc = 0; 2199 utc = 0;
2200 st->next = mod + 6; 2200 st->next = mod + 6;
2201 } 2201 }
2202 st->newVal = VarStrftime(st->val, 1, utc); 2202 st->newVal = VarStrftime(st->val, 1, utc);
2203 return TRUE; 2203 return TRUE;
2204} 2204}
2205 2205
2206/* :localtime */ 2206/* :localtime */
2207static Boolean 2207static Boolean
2208ApplyModifier_Localtime(const char *mod, ApplyModifiersState *st) 2208ApplyModifier_Localtime(const char *mod, ApplyModifiersState *st)
2209{ 2209{
2210 if (!ModMatchEq(mod, "localtime", st->endc)) { 2210 if (!ModMatchEq(mod, "localtime", st->endc)) {
2211 st->next = mod + 1; 2211 st->next = mod + 1;
2212 return FALSE; 2212 return FALSE;
2213 } 2213 }
2214 2214
2215 time_t utc; 2215 time_t utc;
2216 if (mod[9] == '=') { 2216 if (mod[9] == '=') {
2217 char *ep; 2217 char *ep;
2218 utc = strtoul(mod + 10, &ep, 10); 2218 utc = strtoul(mod + 10, &ep, 10);
2219 st->next = ep; 2219 st->next = ep;
2220 } else { 2220 } else {
2221 utc = 0; 2221 utc = 0;
2222 st->next = mod + 9; 2222 st->next = mod + 9;
2223 } 2223 }
2224 st->newVal = VarStrftime(st->val, 0, utc); 2224 st->newVal = VarStrftime(st->val, 0, utc);
2225 return TRUE; 2225 return TRUE;
2226} 2226}
2227 2227
2228/* :hash */ 2228/* :hash */
2229static Boolean 2229static Boolean
2230ApplyModifier_Hash(const char *mod, ApplyModifiersState *st) 2230ApplyModifier_Hash(const char *mod, ApplyModifiersState *st)
2231{ 2231{
2232 if (!ModMatch(mod, "hash", st->endc)) { 2232 if (!ModMatch(mod, "hash", st->endc)) {
2233 st->next = mod + 1; 2233 st->next = mod + 1;
2234 return FALSE; 2234 return FALSE;
2235 } 2235 }
2236 2236
2237 st->newVal = VarHash(st->val); 2237 st->newVal = VarHash(st->val);
2238 st->next = mod + 4; 2238 st->next = mod + 4;
2239 return TRUE; 2239 return TRUE;
2240} 2240}
2241 2241
2242/* :P */ 2242/* :P */
2243static void 2243static void
2244ApplyModifier_Path(const char *mod, ApplyModifiersState *st) 2244ApplyModifier_Path(const char *mod, ApplyModifiersState *st)
2245{ 2245{
2246 if (st->v->flags & VAR_JUNK) 2246 if (st->v->flags & VAR_JUNK)
2247 st->v->flags |= VAR_KEEP; 2247 st->v->flags |= VAR_KEEP;
2248 GNode *gn = Targ_FindNode(st->v->name, TARG_NOCREATE); 2248 GNode *gn = Targ_FindNode(st->v->name, TARG_NOCREATE);
2249 if (gn == NULL || gn->type & OP_NOPATH) { 2249 if (gn == NULL || gn->type & OP_NOPATH) {
2250 st->newVal = NULL; 2250 st->newVal = NULL;
2251 } else if (gn->path) { 2251 } else if (gn->path) {
2252 st->newVal = bmake_strdup(gn->path); 2252 st->newVal = bmake_strdup(gn->path);
2253 } else { 2253 } else {
2254 st->newVal = Dir_FindFile(st->v->name, Suff_FindPath(gn)); 2254 st->newVal = Dir_FindFile(st->v->name, Suff_FindPath(gn));
2255 } 2255 }
2256 if (!st->newVal) 2256 if (!st->newVal)
2257 st->newVal = bmake_strdup(st->v->name); 2257 st->newVal = bmake_strdup(st->v->name);
2258 st->next = mod + 1; 2258 st->next = mod + 1;
2259} 2259}
2260 2260
2261/* :!cmd! */ 2261/* :!cmd! */
2262static Boolean 2262static Boolean
2263ApplyModifier_Exclam(const char *mod, ApplyModifiersState *st) 2263ApplyModifier_Exclam(const char *mod, ApplyModifiersState *st)
2264{ 2264{
2265 st->next = mod + 1; 2265 st->next = mod + 1;
2266 char delim = '!'; 2266 char delim = '!';
2267 char *cmd = ParseModifierPart(&st->next, delim, st->eflags, st->ctxt, 2267 char *cmd = ParseModifierPart(&st->next, delim, st->eflags, st->ctxt,
2268 NULL, NULL, NULL); 2268 NULL, NULL, NULL);
2269 if (cmd == NULL) { 2269 if (cmd == NULL) {
2270 st->missing_delim = delim; 2270 st->missing_delim = delim;
2271 return FALSE; 2271 return FALSE;
2272 } 2272 }
2273 2273
2274 const char *emsg = NULL; 2274 const char *emsg = NULL;
2275 if (st->eflags & VARE_WANTRES) 2275 if (st->eflags & VARE_WANTRES)
2276 st->newVal = Cmd_Exec(cmd, &emsg); 2276 st->newVal = Cmd_Exec(cmd, &emsg);
2277 else 2277 else
2278 st->newVal = varNoError; 2278 st->newVal = varNoError;
2279 free(cmd); 2279 free(cmd);
2280 2280
2281 if (emsg) 2281 if (emsg)
2282 Error(emsg, st->val); 2282 Error(emsg, st->val);
2283 2283
2284 if (st->v->flags & VAR_JUNK) 2284 if (st->v->flags & VAR_JUNK)
2285 st->v->flags |= VAR_KEEP; 2285 st->v->flags |= VAR_KEEP;
2286 return TRUE; 2286 return TRUE;
2287} 2287}
2288 2288
2289/* :range */ 2289/* :range */
2290static Boolean 2290static Boolean
2291ApplyModifier_Range(const char *mod, ApplyModifiersState *st) 2291ApplyModifier_Range(const char *mod, ApplyModifiersState *st)
2292{ 2292{
2293 if (!ModMatchEq(mod, "range", st->endc)) { 2293 if (!ModMatchEq(mod, "range", st->endc)) {
2294 st->next = mod + 1; 2294 st->next = mod + 1;
2295 return FALSE; 2295 return FALSE;
2296 } 2296 }
2297 2297
2298 int n; 2298 int n;
2299 if (mod[5] == '=') { 2299 if (mod[5] == '=') {
2300 char *ep; 2300 char *ep;
2301 n = strtoul(mod + 6, &ep, 10); 2301 n = strtoul(mod + 6, &ep, 10);
2302 st->next = ep; 2302 st->next = ep;
2303 } else { 2303 } else {
2304 n = 0; 2304 n = 0;
2305 st->next = mod + 5; 2305 st->next = mod + 5;
2306 } 2306 }
2307 st->newVal = VarRange(st->val, n); 2307 st->newVal = VarRange(st->val, n);
2308 return TRUE; 2308 return TRUE;
2309} 2309}
2310 2310
2311/* :Mpattern or :Npattern */ 2311/* :Mpattern or :Npattern */
2312static void 2312static void
2313ApplyModifier_Match(const char *mod, ApplyModifiersState *st) 2313ApplyModifier_Match(const char *mod, ApplyModifiersState *st)
2314{ 2314{
2315 Boolean copy = FALSE; /* pattern should be, or has been, copied */ 2315 Boolean copy = FALSE; /* pattern should be, or has been, copied */
2316 Boolean needSubst = FALSE; 2316 Boolean needSubst = FALSE;
2317 /* 2317 /*
2318 * In the loop below, ignore ':' unless we are at (or back to) the 2318 * In the loop below, ignore ':' unless we are at (or back to) the
2319 * original brace level. 2319 * original brace level.
2320 * XXX This will likely not work right if $() and ${} are intermixed. 2320 * XXX This will likely not work right if $() and ${} are intermixed.
2321 */ 2321 */
2322 int nest = 1; 2322 int nest = 1;
2323 const char *p; 2323 const char *p;
2324 for (p = mod + 1; *p != '\0' && !(*p == ':' && nest == 1); p++) { 2324 for (p = mod + 1; *p != '\0' && !(*p == ':' && nest == 1); p++) {
2325 if (*p == '\\' && 2325 if (*p == '\\' &&
2326 (p[1] == ':' || p[1] == st->endc || p[1] == st->startc)) { 2326 (p[1] == ':' || p[1] == st->endc || p[1] == st->startc)) {
2327 if (!needSubst) 2327 if (!needSubst)
2328 copy = TRUE; 2328 copy = TRUE;
2329 p++; 2329 p++;
2330 continue; 2330 continue;
2331 } 2331 }
2332 if (*p == '$') 2332 if (*p == '$')
2333 needSubst = TRUE; 2333 needSubst = TRUE;
2334 if (*p == '(' || *p == '{') 2334 if (*p == '(' || *p == '{')
2335 ++nest; 2335 ++nest;
2336 if (*p == ')' || *p == '}') { 2336 if (*p == ')' || *p == '}') {
2337 --nest; 2337 --nest;
2338 if (nest == 0) 2338 if (nest == 0)
2339 break; 2339 break;
2340 } 2340 }
2341 } 2341 }
2342 st->next = p; 2342 st->next = p;
2343 const char *endpat = st->next; 2343 const char *endpat = st->next;
2344 2344
2345 char *pattern; 2345 char *pattern;
2346 if (copy) { 2346 if (copy) {
2347 /* Compress the \:'s out of the pattern. */ 2347 /* Compress the \:'s out of the pattern. */
2348 pattern = bmake_malloc(endpat - (mod + 1) + 1); 2348 pattern = bmake_malloc(endpat - (mod + 1) + 1);
2349 char *dst = pattern; 2349 char *dst = pattern;
2350 const char *src = mod + 1; 2350 const char *src = mod + 1;
2351 for (; src < endpat; src++, dst++) { 2351 for (; src < endpat; src++, dst++) {
2352 if (src[0] == '\\' && src + 1 < endpat && 2352 if (src[0] == '\\' && src + 1 < endpat &&
2353 /* XXX: st->startc is missing here; see above */ 2353 /* XXX: st->startc is missing here; see above */
2354 (src[1] == ':' || src[1] == st->endc)) 2354 (src[1] == ':' || src[1] == st->endc))
2355 src++; 2355 src++;
2356 *dst = *src; 2356 *dst = *src;
2357 } 2357 }
2358 *dst = '\0'; 2358 *dst = '\0';
2359 endpat = dst; 2359 endpat = dst;
2360 } else { 2360 } else {
2361 /* 2361 /*
2362 * Either Var_Subst or ModifyWords will need a 2362 * Either Var_Subst or ModifyWords will need a
2363 * nul-terminated string soon, so construct one now. 2363 * nul-terminated string soon, so construct one now.
2364 */ 2364 */
2365 pattern = bmake_strndup(mod + 1, endpat - (mod + 1)); 2365 pattern = bmake_strndup(mod + 1, endpat - (mod + 1));
2366 } 2366 }
2367 2367
2368 if (needSubst) { 2368 if (needSubst) {
2369 /* pattern contains embedded '$', so use Var_Subst to expand it. */ 2369 /* pattern contains embedded '$', so use Var_Subst to expand it. */
2370 char *old_pattern = pattern; 2370 char *old_pattern = pattern;
2371 pattern = Var_Subst(pattern, st->ctxt, st->eflags); 2371 pattern = Var_Subst(pattern, st->ctxt, st->eflags);
2372 free(old_pattern); 2372 free(old_pattern);
2373 } 2373 }
2374 2374
2375 if (DEBUG(VAR)) 2375 if (DEBUG(VAR))
2376 fprintf(debug_file, "Pattern[%s] for [%s] is [%s]\n", 2376 fprintf(debug_file, "Pattern[%s] for [%s] is [%s]\n",
2377 st->v->name, st->val, pattern); 2377 st->v->name, st->val, pattern);
2378 2378
2379 ModifyWordsCallback callback = mod[0] == 'M' 2379 ModifyWordsCallback callback = mod[0] == 'M'
2380 ? ModifyWord_Match : ModifyWord_NoMatch; 2380 ? ModifyWord_Match : ModifyWord_NoMatch;
2381 st->newVal = ModifyWords(st->ctxt, st->sep, st->oneBigWord, st->val, 2381 st->newVal = ModifyWords(st->ctxt, st->sep, st->oneBigWord, st->val,
2382 callback, pattern); 2382 callback, pattern);
2383 free(pattern); 2383 free(pattern);
2384} 2384}
2385 2385
2386/* :S,from,to, */ 2386/* :S,from,to, */
2387static Boolean 2387static Boolean
2388ApplyModifier_Subst(const char * const mod, ApplyModifiersState *st) 2388ApplyModifier_Subst(const char * const mod, ApplyModifiersState *st)
2389{ 2389{
2390 ModifyWord_SubstArgs args; 2390 ModifyWord_SubstArgs args;
2391 Boolean oneBigWord = st->oneBigWord; 2391 Boolean oneBigWord = st->oneBigWord;
2392 char delim = mod[1]; 2392 char delim = mod[1];
2393 2393
2394 st->next = mod + 2; 2394 st->next = mod + 2;
2395 2395
2396 /* 2396 /*
2397 * If pattern begins with '^', it is anchored to the 2397 * If pattern begins with '^', it is anchored to the
2398 * start of the word -- skip over it and flag pattern. 2398 * start of the word -- skip over it and flag pattern.
2399 */ 2399 */
2400 args.pflags = 0; 2400 args.pflags = 0;
2401 if (*st->next == '^') { 2401 if (*st->next == '^') {
2402 args.pflags |= VARP_ANCHOR_START; 2402 args.pflags |= VARP_ANCHOR_START;
2403 st->next++; 2403 st->next++;
2404 } 2404 }
2405 2405
2406 char *lhs = ParseModifierPart(&st->next, delim, st->eflags, st->ctxt, 2406 char *lhs = ParseModifierPart(&st->next, delim, st->eflags, st->ctxt,
2407 &args.lhsLen, &args.pflags, NULL); 2407 &args.lhsLen, &args.pflags, NULL);
2408 if (lhs == NULL) { 2408 if (lhs == NULL) {
2409 st->missing_delim = delim; 2409 st->missing_delim = delim;
2410 return FALSE; 2410 return FALSE;
2411 } 2411 }
2412 args.lhs = lhs; 2412 args.lhs = lhs;
2413 2413
2414 char *rhs = ParseModifierPart(&st->next, delim, st->eflags, st->ctxt, 2414 char *rhs = ParseModifierPart(&st->next, delim, st->eflags, st->ctxt,
2415 &args.rhsLen, NULL, &args); 2415 &args.rhsLen, NULL, &args);
2416 if (rhs == NULL) { 2416 if (rhs == NULL) {
2417 st->missing_delim = delim; 2417 st->missing_delim = delim;
2418 return FALSE; 2418 return FALSE;
2419 } 2419 }
2420 args.rhs = rhs; 2420 args.rhs = rhs;
2421 2421
2422 /* 2422 /*
2423 * Check for global substitution. If 'g' after the final 2423 * Check for global substitution. If 'g' after the final
2424 * delimiter, substitution is global and is marked that 2424 * delimiter, substitution is global and is marked that
2425 * way. 2425 * way.
2426 */ 2426 */
2427 for (;; st->next++) { 2427 for (;; st->next++) {
2428 switch (*st->next) { 2428 switch (*st->next) {
2429 case 'g': 2429 case 'g':
2430 args.pflags |= VARP_SUB_GLOBAL; 2430 args.pflags |= VARP_SUB_GLOBAL;
2431 continue; 2431 continue;
2432 case '1': 2432 case '1':
2433 args.pflags |= VARP_SUB_ONE; 2433 args.pflags |= VARP_SUB_ONE;
2434 continue; 2434 continue;
2435 case 'W': 2435 case 'W':
2436 oneBigWord = TRUE; 2436 oneBigWord = TRUE;
2437 continue; 2437 continue;
2438 } 2438 }
2439 break; 2439 break;
2440 } 2440 }
2441 2441
2442 st->newVal = ModifyWords(st->ctxt, st->sep, oneBigWord, st->val, 2442 st->newVal = ModifyWords(st->ctxt, st->sep, oneBigWord, st->val,
2443 ModifyWord_Subst, &args); 2443 ModifyWord_Subst, &args);
2444 2444
2445 free(lhs); 2445 free(lhs);
2446 free(rhs); 2446 free(rhs);
2447 return TRUE; 2447 return TRUE;
2448} 2448}
2449 2449
2450#ifndef NO_REGEX 2450#ifndef NO_REGEX
2451 2451
2452/* :C,from,to, */ 2452/* :C,from,to, */
2453static Boolean 2453static Boolean
2454ApplyModifier_Regex(const char *mod, ApplyModifiersState *st) 2454ApplyModifier_Regex(const char *mod, ApplyModifiersState *st)
2455{ 2455{
2456 ModifyWord_SubstRegexArgs args; 2456 ModifyWord_SubstRegexArgs args;
2457 2457
2458 args.pflags = 0; 2458 args.pflags = 0;
2459 Boolean oneBigWord = st->oneBigWord; 2459 Boolean oneBigWord = st->oneBigWord;
2460 char delim = mod[1]; 2460 char delim = mod[1];
2461 2461
2462 st->next = mod + 2; 2462 st->next = mod + 2;
2463 2463
2464 char *re = ParseModifierPart(&st->next, delim, st->eflags, st->ctxt, 2464 char *re = ParseModifierPart(&st->next, delim, st->eflags, st->ctxt,
2465 NULL, NULL, NULL); 2465 NULL, NULL, NULL);
2466 if (re == NULL) { 2466 if (re == NULL) {
2467 st->missing_delim = delim; 2467 st->missing_delim = delim;
2468 return FALSE; 2468 return FALSE;
2469 } 2469 }
2470 2470
2471 args.replace = ParseModifierPart(&st->next, delim, st->eflags, st->ctxt, 2471 args.replace = ParseModifierPart(&st->next, delim, st->eflags, st->ctxt,
2472 NULL, NULL, NULL); 2472 NULL, NULL, NULL);
2473 if (args.replace == NULL) { 2473 if (args.replace == NULL) {
2474 free(re); 2474 free(re);
2475 st->missing_delim = delim; 2475 st->missing_delim = delim;
2476 return FALSE; 2476 return FALSE;
2477 } 2477 }
2478 2478
2479 for (;; st->next++) { 2479 for (;; st->next++) {
2480 switch (*st->next) { 2480 switch (*st->next) {
2481 case 'g': 2481 case 'g':
2482 args.pflags |= VARP_SUB_GLOBAL; 2482 args.pflags |= VARP_SUB_GLOBAL;
2483 continue; 2483 continue;
2484 case '1': 2484 case '1':
2485 args.pflags |= VARP_SUB_ONE; 2485 args.pflags |= VARP_SUB_ONE;
2486 continue; 2486 continue;
2487 case 'W': 2487 case 'W':
2488 oneBigWord = TRUE; 2488 oneBigWord = TRUE;
2489 continue; 2489 continue;
2490 } 2490 }
2491 break; 2491 break;
2492 } 2492 }
2493 2493
2494 int error = regcomp(&args.re, re, REG_EXTENDED); 2494 int error = regcomp(&args.re, re, REG_EXTENDED);
2495 free(re); 2495 free(re);
2496 if (error) { 2496 if (error) {
2497 VarREError(error, &args.re, "RE substitution error"); 2497 VarREError(error, &args.re, "RE substitution error");
2498 free(args.replace); 2498 free(args.replace);
2499 return FALSE; 2499 return FALSE;
2500 } 2500 }
2501 2501
2502 args.nsub = args.re.re_nsub + 1; 2502 args.nsub = args.re.re_nsub + 1;
2503 if (args.nsub < 1) 2503 if (args.nsub < 1)
2504 args.nsub = 1; 2504 args.nsub = 1;
2505 if (args.nsub > 10) 2505 if (args.nsub > 10)
2506 args.nsub = 10; 2506 args.nsub = 10;
2507 st->newVal = ModifyWords(st->ctxt, st->sep, oneBigWord, st->val, 2507 st->newVal = ModifyWords(st->ctxt, st->sep, oneBigWord, st->val,
2508 ModifyWord_SubstRegex, &args); 2508 ModifyWord_SubstRegex, &args);
2509 regfree(&args.re); 2509 regfree(&args.re);
2510 free(args.replace); 2510 free(args.replace);
2511 return TRUE; 2511 return TRUE;
2512} 2512}
2513#endif 2513#endif
2514 2514
2515static void 2515static void
2516ModifyWord_Copy(const char *word, SepBuf *buf, void *data MAKE_ATTR_UNUSED) 2516ModifyWord_Copy(const char *word, SepBuf *buf, void *data MAKE_ATTR_UNUSED)
2517{ 2517{
2518 SepBuf_AddStr(buf, word); 2518 SepBuf_AddStr(buf, word);
2519} 2519}
2520 2520
2521/* :ts<separator> */ 2521/* :ts<separator> */
2522static Boolean 2522static Boolean
2523ApplyModifier_ToSep(const char *sep, ApplyModifiersState *st) 2523ApplyModifier_ToSep(const char *sep, ApplyModifiersState *st)
2524{ 2524{
2525 if (sep[0] != st->endc && (sep[1] == st->endc || sep[1] == ':')) { 2525 if (sep[0] != st->endc && (sep[1] == st->endc || sep[1] == ':')) {
2526 /* ":ts<unrecognised><endc>" or ":ts<unrecognised>:" */ 2526 /* ":ts<any><endc>" or ":ts<any>:" */
2527 st->sep = sep[0]; 2527 st->sep = sep[0];
2528 st->next = sep + 1; 2528 st->next = sep + 1;
2529 } else if (sep[0] == st->endc || sep[0] == ':') { 2529 } else if (sep[0] == st->endc || sep[0] == ':') {
2530 /* ":ts<endc>" or ":ts:" */ 2530 /* ":ts<endc>" or ":ts:" */
2531 st->sep = '\0'; /* no separator */ 2531 st->sep = '\0'; /* no separator */
2532 st->next = sep; 2532 st->next = sep;
2533 } else if (sep[0] == '\\') { 2533 } else if (sep[0] == '\\') {
2534 const char *xp = sep + 1; 2534 const char *xp = sep + 1;
2535 int base = 8; /* assume octal */ 2535 int base = 8; /* assume octal */
2536 2536
2537 switch (sep[1]) { 2537 switch (sep[1]) {
2538 case 'n': 2538 case 'n':
2539 st->sep = '\n'; 2539 st->sep = '\n';
2540 st->next = sep + 2; 2540 st->next = sep + 2;
2541 break; 2541 break;
2542 case 't': 2542 case 't':
2543 st->sep = '\t'; 2543 st->sep = '\t';
2544 st->next = sep + 2; 2544 st->next = sep + 2;
2545 break; 2545 break;
2546 case 'x': 2546 case 'x':
2547 base = 16; 2547 base = 16;
2548 xp++; 2548 xp++;
2549 goto get_numeric; 2549 goto get_numeric;
2550 case '0': 2550 case '0':
2551 base = 0; 2551 base = 0;
2552 goto get_numeric; 2552 goto get_numeric;
2553 default: 2553 default:
2554 if (!isdigit((unsigned char)sep[1])) 2554 if (!isdigit((unsigned char)sep[1]))
2555 return FALSE; /* ":ts<backslash><unrecognised>". */ 2555 return FALSE; /* ":ts<backslash><unrecognised>". */
2556 2556
2557 char *end; 2557 char *end;
2558 get_numeric: 2558 get_numeric:
2559 st->sep = strtoul(sep + 1 + (sep[1] == 'x'), &end, base); 2559 st->sep = strtoul(sep + 1 + (sep[1] == 'x'), &end, base);
2560 if (*end != ':' && *end != st->endc) 2560 if (*end != ':' && *end != st->endc)
2561 return FALSE; 2561 return FALSE;
2562 st->next = end; 2562 st->next = end;
2563 break; 2563 break;
2564 } 2564 }
2565 } else { 2565 } else {
2566 return FALSE; /* Found ":ts<unrecognised><unrecognised>". */ 2566 return FALSE; /* Found ":ts<unrecognised><unrecognised>". */
2567 } 2567 }
2568 2568
2569 st->newVal = ModifyWords(st->ctxt, st->sep, st->oneBigWord, st->val, 2569 st->newVal = ModifyWords(st->ctxt, st->sep, st->oneBigWord, st->val,
2570 ModifyWord_Copy, NULL); 2570 ModifyWord_Copy, NULL);
2571 return TRUE; 2571 return TRUE;
2572} 2572}
2573 2573
2574/* :tA, :tu, :tl, :ts<separator>, etc. */ 2574/* :tA, :tu, :tl, :ts<separator>, etc. */
2575static Boolean 2575static Boolean
2576ApplyModifier_To(const char *mod, ApplyModifiersState *st) 2576ApplyModifier_To(const char *mod, ApplyModifiersState *st)
2577{ 2577{
2578 st->next = mod + 1; /* make sure it is set */ 2578 st->next = mod + 1; /* make sure it is set */
2579 if (mod[1] == st->endc || mod[1] == ':') 2579 if (mod[1] == st->endc || mod[1] == ':')
2580 return FALSE; /* Found ":t<endc>" or ":t:". */ 2580 return FALSE; /* Found ":t<endc>" or ":t:". */
2581 2581
2582 if (mod[1] == 's') 2582 if (mod[1] == 's')
2583 return ApplyModifier_ToSep(mod + 2, st); 2583 return ApplyModifier_ToSep(mod + 2, st);
2584 2584
2585 if (mod[2] != st->endc && mod[2] != ':') 2585 if (mod[2] != st->endc && mod[2] != ':')
2586 return FALSE; /* Found ":t<unrecognised><unrecognised>". */ 2586 return FALSE; /* Found ":t<unrecognised><unrecognised>". */
2587 2587
2588 /* Check for two-character options: ":tu", ":tl" */ 2588 /* Check for two-character options: ":tu", ":tl" */
2589 if (mod[1] == 'A') { /* absolute path */ 2589 if (mod[1] == 'A') { /* absolute path */
2590 st->newVal = ModifyWords(st->ctxt, st->sep, st->oneBigWord, st->val, 2590 st->newVal = ModifyWords(st->ctxt, st->sep, st->oneBigWord, st->val,
2591 ModifyWord_Realpath, NULL); 2591 ModifyWord_Realpath, NULL);
2592 st->next = mod + 2; 2592 st->next = mod + 2;
2593 } else if (mod[1] == 'u') { 2593 } else if (mod[1] == 'u') {
2594 char *dp = bmake_strdup(st->val); 2594 char *dp = bmake_strdup(st->val);
2595 for (st->newVal = dp; *dp; dp++) 2595 for (st->newVal = dp; *dp; dp++)
2596 *dp = toupper((unsigned char)*dp); 2596 *dp = toupper((unsigned char)*dp);
2597 st->next = mod + 2; 2597 st->next = mod + 2;
2598 } else if (mod[1] == 'l') { 2598 } else if (mod[1] == 'l') {
2599 char *dp = bmake_strdup(st->val); 2599 char *dp = bmake_strdup(st->val);
2600 for (st->newVal = dp; *dp; dp++) 2600 for (st->newVal = dp; *dp; dp++)
2601 *dp = tolower((unsigned char)*dp); 2601 *dp = tolower((unsigned char)*dp);
2602 st->next = mod + 2; 2602 st->next = mod + 2;
2603 } else if (mod[1] == 'W' || mod[1] == 'w') { 2603 } else if (mod[1] == 'W' || mod[1] == 'w') {
2604 st->oneBigWord = mod[1] == 'W'; 2604 st->oneBigWord = mod[1] == 'W';
2605 st->newVal = st->val; 2605 st->newVal = st->val;
2606 st->next = mod + 2; 2606 st->next = mod + 2;
2607 } else { 2607 } else {
2608 /* Found ":t<unrecognised>:" or ":t<unrecognised><endc>". */ 2608 /* Found ":t<unrecognised>:" or ":t<unrecognised><endc>". */
2609 return FALSE; 2609 return FALSE;
2610 } 2610 }
2611 return TRUE; 2611 return TRUE;
2612} 2612}
2613 2613
2614/* :[#], :[1], etc. */ 2614/* :[#], :[1], etc. */
2615static int 2615static int
2616ApplyModifier_Words(const char *mod, ApplyModifiersState *st) 2616ApplyModifier_Words(const char *mod, ApplyModifiersState *st)
2617{ 2617{
2618 st->next = mod + 1; /* point to char after '[' */ 2618 st->next = mod + 1; /* point to char after '[' */
2619 char delim = ']'; /* look for closing ']' */ 2619 char delim = ']'; /* look for closing ']' */
2620 char *estr = ParseModifierPart(&st->next, delim, st->eflags, st->ctxt, 2620 char *estr = ParseModifierPart(&st->next, delim, st->eflags, st->ctxt,
2621 NULL, NULL, NULL); 2621 NULL, NULL, NULL);
2622 if (estr == NULL) { 2622 if (estr == NULL) {
2623 st->missing_delim = delim; 2623 st->missing_delim = delim;
2624 return 'c'; 2624 return 'c';
2625 } 2625 }
2626 2626
2627 /* now st->next points just after the closing ']' */ 2627 /* now st->next points just after the closing ']' */
2628 if (st->next[0] != ':' && st->next[0] != st->endc) 2628 if (st->next[0] != ':' && st->next[0] != st->endc)
2629 goto bad_modifier; /* Found junk after ']' */ 2629 goto bad_modifier; /* Found junk after ']' */
2630 2630
2631 if (estr[0] == '\0') 2631 if (estr[0] == '\0')
2632 goto bad_modifier; /* empty square brackets in ":[]". */ 2632 goto bad_modifier; /* empty square brackets in ":[]". */
2633 2633
2634 if (estr[0] == '#' && estr[1] == '\0') { /* Found ":[#]" */ 2634 if (estr[0] == '#' && estr[1] == '\0') { /* Found ":[#]" */
2635 if (st->oneBigWord) { 2635 if (st->oneBigWord) {
2636 st->newVal = bmake_strdup("1"); 2636 st->newVal = bmake_strdup("1");
2637 } else { 2637 } else {
2638 /* XXX: brk_string() is a rather expensive 2638 /* XXX: brk_string() is a rather expensive
2639 * way of counting words. */ 2639 * way of counting words. */
2640 char *as; 2640 char *as;
2641 int ac; 2641 int ac;
2642 char **av = brk_string(st->val, &ac, FALSE, &as); 2642 char **av = brk_string(st->val, &ac, FALSE, &as);
2643 free(as); 2643 free(as);
2644 free(av); 2644 free(av);
2645 2645
2646 Buffer buf; 2646 Buffer buf;
2647 Buf_Init(&buf, 4); /* 3 digits + '\0' */ 2647 Buf_Init(&buf, 4); /* 3 digits + '\0' */
2648 Buf_AddInt(&buf, ac); 2648 Buf_AddInt(&buf, ac);
2649 st->newVal = Buf_Destroy(&buf, FALSE); 2649 st->newVal = Buf_Destroy(&buf, FALSE);
2650 } 2650 }
2651 goto ok; 2651 goto ok;
2652 } 2652 }
2653 2653
2654 if (estr[0] == '*' && estr[1] == '\0') { 2654 if (estr[0] == '*' && estr[1] == '\0') {
2655 /* Found ":[*]" */ 2655 /* Found ":[*]" */
2656 st->oneBigWord = TRUE; 2656 st->oneBigWord = TRUE;
2657 st->newVal = st->val; 2657 st->newVal = st->val;
2658 goto ok; 2658 goto ok;
2659 } 2659 }
2660 2660
2661 if (estr[0] == '@' && estr[1] == '\0') { 2661 if (estr[0] == '@' && estr[1] == '\0') {
2662 /* Found ":[@]" */ 2662 /* Found ":[@]" */
2663 st->oneBigWord = FALSE; 2663 st->oneBigWord = FALSE;
2664 st->newVal = st->val; 2664 st->newVal = st->val;
2665 goto ok; 2665 goto ok;
2666 } 2666 }
2667 2667
2668 /* 2668 /*
2669 * We expect estr to contain a single integer for :[N], or two integers 2669 * We expect estr to contain a single integer for :[N], or two integers
2670 * separated by ".." for :[start..end]. 2670 * separated by ".." for :[start..end].
2671 */ 2671 */
2672 char *ep; 2672 char *ep;
2673 int first = strtol(estr, &ep, 0); 2673 int first = strtol(estr, &ep, 0);
2674 int last; 2674 int last;
2675 if (ep == estr) /* Found junk instead of a number */ 2675 if (ep == estr) /* Found junk instead of a number */
2676 goto bad_modifier; 2676 goto bad_modifier;
2677 2677
2678 if (ep[0] == '\0') { /* Found only one integer in :[N] */ 2678 if (ep[0] == '\0') { /* Found only one integer in :[N] */
2679 last = first; 2679 last = first;
2680 } else if (ep[0] == '.' && ep[1] == '.' && ep[2] != '\0') { 2680 } else if (ep[0] == '.' && ep[1] == '.' && ep[2] != '\0') {
2681 /* Expecting another integer after ".." */ 2681 /* Expecting another integer after ".." */
2682 ep += 2; 2682 ep += 2;
2683 last = strtol(ep, &ep, 0); 2683 last = strtol(ep, &ep, 0);
2684 if (ep[0] != '\0') /* Found junk after ".." */ 2684 if (ep[0] != '\0') /* Found junk after ".." */
2685 goto bad_modifier; 2685 goto bad_modifier;
2686 } else 2686 } else
2687 goto bad_modifier; /* Found junk instead of ".." */ 2687 goto bad_modifier; /* Found junk instead of ".." */
2688 2688
2689 /* 2689 /*
2690 * Now seldata is properly filled in, but we still have to check for 0 as 2690 * Now seldata is properly filled in, but we still have to check for 0 as
2691 * a special case. 2691 * a special case.
2692 */ 2692 */
2693 if (first == 0 && last == 0) { 2693 if (first == 0 && last == 0) {
2694 /* ":[0]" or perhaps ":[0..0]" */ 2694 /* ":[0]" or perhaps ":[0..0]" */
2695 st->oneBigWord = TRUE; 2695 st->oneBigWord = TRUE;
2696 st->newVal = st->val; 2696 st->newVal = st->val;
2697 goto ok; 2697 goto ok;
2698 } 2698 }
2699 2699
2700 /* ":[0..N]" or ":[N..0]" */ 2700 /* ":[0..N]" or ":[N..0]" */
2701 if (first == 0 || last == 0) 2701 if (first == 0 || last == 0)
2702 goto bad_modifier; 2702 goto bad_modifier;
2703 2703
2704 /* Normal case: select the words described by seldata. */ 2704 /* Normal case: select the words described by seldata. */
2705 st->newVal = VarSelectWords(st->sep, st->oneBigWord, st->val, first, last); 2705 st->newVal = VarSelectWords(st->sep, st->oneBigWord, st->val, first, last);
2706 2706
2707ok: 2707ok:
2708 free(estr); 2708 free(estr);
2709 return 0; 2709 return 0;
2710 2710
2711bad_modifier: 2711bad_modifier:
2712 free(estr); 2712 free(estr);
2713 return 'b'; 2713 return 'b';
2714} 2714}
2715 2715
2716/* :O or :Ox */ 2716/* :O or :Ox */
2717static Boolean 2717static Boolean
2718ApplyModifier_Order(const char *mod, ApplyModifiersState *st) 2718ApplyModifier_Order(const char *mod, ApplyModifiersState *st)
2719{ 2719{
2720 char otype; 2720 char otype;
2721 2721
2722 st->next = mod + 1; /* skip to the rest in any case */ 2722 st->next = mod + 1; /* skip to the rest in any case */
2723 if (mod[1] == st->endc || mod[1] == ':') { 2723 if (mod[1] == st->endc || mod[1] == ':') {
2724 otype = 's'; 2724 otype = 's';
2725 } else if ((mod[1] == 'r' || mod[1] == 'x') && 2725 } else if ((mod[1] == 'r' || mod[1] == 'x') &&
2726 (mod[2] == st->endc || mod[2] == ':')) { 2726 (mod[2] == st->endc || mod[2] == ':')) {
2727 otype = mod[1]; 2727 otype = mod[1];
2728 st->next = mod + 2; 2728 st->next = mod + 2;
2729 } else { 2729 } else {
2730 return FALSE; 2730 return FALSE;
2731 } 2731 }
2732 st->newVal = VarOrder(st->val, otype); 2732 st->newVal = VarOrder(st->val, otype);
2733 return TRUE; 2733 return TRUE;
2734} 2734}
2735 2735
2736/* :? then : else */ 2736/* :? then : else */
2737static Boolean 2737static Boolean
2738ApplyModifier_IfElse(const char *mod, ApplyModifiersState *st) 2738ApplyModifier_IfElse(const char *mod, ApplyModifiersState *st)
2739{ 2739{
2740 Boolean value = FALSE; 2740 Boolean value = FALSE;
2741 int cond_rc = 0; 2741 int cond_rc = 0;
2742 VarEvalFlags then_eflags = st->eflags & ~VARE_WANTRES; 2742 VarEvalFlags then_eflags = st->eflags & ~VARE_WANTRES;
2743 VarEvalFlags else_eflags = st->eflags & ~VARE_WANTRES; 2743 VarEvalFlags else_eflags = st->eflags & ~VARE_WANTRES;
2744 2744
2745 if (st->eflags & VARE_WANTRES) { 2745 if (st->eflags & VARE_WANTRES) {
2746 cond_rc = Cond_EvalExpression(NULL, st->v->name, &value, 0, FALSE); 2746 cond_rc = Cond_EvalExpression(NULL, st->v->name, &value, 0, FALSE);
2747 if (cond_rc != COND_INVALID && value) 2747 if (cond_rc != COND_INVALID && value)
2748 then_eflags |= VARE_WANTRES; 2748 then_eflags |= VARE_WANTRES;
2749 if (cond_rc != COND_INVALID && !value) 2749 if (cond_rc != COND_INVALID && !value)
2750 else_eflags |= VARE_WANTRES; 2750 else_eflags |= VARE_WANTRES;
2751 } 2751 }
2752 2752
2753 st->next = mod + 1; 2753 st->next = mod + 1;
2754 char delim = ':'; 2754 char delim = ':';
2755 char *then_expr = ParseModifierPart(&st->next, delim, then_eflags, st->ctxt, 2755 char *then_expr = ParseModifierPart(&st->next, delim, then_eflags, st->ctxt,
2756 NULL, NULL, NULL); 2756 NULL, NULL, NULL);
2757 if (then_expr == NULL) { 2757 if (then_expr == NULL) {
2758 st->missing_delim = delim; 2758 st->missing_delim = delim;
2759 return FALSE; 2759 return FALSE;
2760 } 2760 }
2761 2761
2762 delim = st->endc; /* BRCLOSE or PRCLOSE */ 2762 delim = st->endc; /* BRCLOSE or PRCLOSE */
2763 char *else_expr = ParseModifierPart(&st->next, delim, else_eflags, st->ctxt, 2763 char *else_expr = ParseModifierPart(&st->next, delim, else_eflags, st->ctxt,
2764 NULL, NULL, NULL); 2764 NULL, NULL, NULL);
2765 if (else_expr == NULL) { 2765 if (else_expr == NULL) {
2766 st->missing_delim = delim; 2766 st->missing_delim = delim;
2767 return FALSE; 2767 return FALSE;
2768 } 2768 }
2769 2769
2770 st->next--; 2770 st->next--;
2771 if (cond_rc == COND_INVALID) { 2771 if (cond_rc == COND_INVALID) {
2772 Error("Bad conditional expression `%s' in %s?%s:%s", 2772 Error("Bad conditional expression `%s' in %s?%s:%s",
2773 st->v->name, st->v->name, then_expr, else_expr); 2773 st->v->name, st->v->name, then_expr, else_expr);
2774 return FALSE; 2774 return FALSE;
2775 } 2775 }
2776 2776
2777 if (value) { 2777 if (value) {
2778 st->newVal = then_expr; 2778 st->newVal = then_expr;
2779 free(else_expr); 2779 free(else_expr);
2780 } else { 2780 } else {
2781 st->newVal = else_expr; 2781 st->newVal = else_expr;
2782 free(then_expr); 2782 free(then_expr);
2783 } 2783 }
2784 if (st->v->flags & VAR_JUNK) 2784 if (st->v->flags & VAR_JUNK)
2785 st->v->flags |= VAR_KEEP; 2785 st->v->flags |= VAR_KEEP;
2786 return TRUE; 2786 return TRUE;
2787} 2787}
2788 2788
2789/* 2789/*
2790 * The ::= modifiers actually assign a value to the variable. 2790 * The ::= modifiers actually assign a value to the variable.
2791 * Their main purpose is in supporting modifiers of .for loop 2791 * Their main purpose is in supporting modifiers of .for loop
2792 * iterators and other obscure uses. They always expand to 2792 * iterators and other obscure uses. They always expand to
2793 * nothing. In a target rule that would otherwise expand to an 2793 * nothing. In a target rule that would otherwise expand to an
2794 * empty line they can be preceded with @: to keep make happy. 2794 * empty line they can be preceded with @: to keep make happy.
2795 * Eg. 2795 * Eg.
2796 * 2796 *
2797 * foo: .USE 2797 * foo: .USE
2798 * .for i in ${.TARGET} ${.TARGET:R}.gz 2798 * .for i in ${.TARGET} ${.TARGET:R}.gz
2799 * @: ${t::=$i} 2799 * @: ${t::=$i}
2800 * @echo blah ${t:T} 2800 * @echo blah ${t:T}
2801 * .endfor 2801 * .endfor
2802 * 2802 *
2803 * ::=<str> Assigns <str> as the new value of variable. 2803 * ::=<str> Assigns <str> as the new value of variable.
2804 * ::?=<str> Assigns <str> as value of variable if 2804 * ::?=<str> Assigns <str> as value of variable if
2805 * it was not already set. 2805 * it was not already set.
2806 * ::+=<str> Appends <str> to variable. 2806 * ::+=<str> Appends <str> to variable.
2807 * ::!=<cmd> Assigns output of <cmd> as the new value of 2807 * ::!=<cmd> Assigns output of <cmd> as the new value of
2808 * variable. 2808 * variable.
2809 */ 2809 */
2810static int 2810static int
2811ApplyModifier_Assign(const char *mod, ApplyModifiersState *st) 2811ApplyModifier_Assign(const char *mod, ApplyModifiersState *st)
2812{ 2812{
2813 const char *op = mod + 1; 2813 const char *op = mod + 1;
2814 if (!(op[0] == '=' || 2814 if (!(op[0] == '=' ||
2815 (op[1] == '=' && 2815 (op[1] == '=' &&
2816 (op[0] == '!' || op[0] == '+' || op[0] == '?')))) 2816 (op[0] == '!' || op[0] == '+' || op[0] == '?'))))
2817 return 'd'; /* "::<unrecognised>" */ 2817 return 'd'; /* "::<unrecognised>" */
2818 2818
2819 GNode *v_ctxt; /* context where v belongs */ 2819 GNode *v_ctxt; /* context where v belongs */
2820 2820
2821 if (st->v->name[0] == 0) 2821 if (st->v->name[0] == 0)
2822 return 'b'; 2822 return 'b';
2823 2823
2824 v_ctxt = st->ctxt; 2824 v_ctxt = st->ctxt;
2825 char *sv_name = NULL; 2825 char *sv_name = NULL;
2826 if (st->v->flags & VAR_JUNK) { 2826 if (st->v->flags & VAR_JUNK) {
2827 /* 2827 /*
2828 * We need to bmake_strdup() it incase ParseModifierPart() recurses. 2828 * We need to bmake_strdup() it incase ParseModifierPart() recurses.
2829 */ 2829 */
2830 sv_name = st->v->name; 2830 sv_name = st->v->name;
2831 st->v->name = bmake_strdup(st->v->name); 2831 st->v->name = bmake_strdup(st->v->name);
2832 } else if (st->ctxt != VAR_GLOBAL) { 2832 } else if (st->ctxt != VAR_GLOBAL) {
2833 Var *gv = VarFind(st->v->name, st->ctxt, 0); 2833 Var *gv = VarFind(st->v->name, st->ctxt, 0);
2834 if (gv == NULL) 2834 if (gv == NULL)
2835 v_ctxt = VAR_GLOBAL; 2835 v_ctxt = VAR_GLOBAL;
2836 else 2836 else
2837 VarFreeEnv(gv, TRUE); 2837 VarFreeEnv(gv, TRUE);
2838 } 2838 }
2839 2839
2840 switch (op[0]) { 2840 switch (op[0]) {
2841 case '+': 2841 case '+':
2842 case '?': 2842 case '?':
2843 case '!': 2843 case '!':
2844 st->next = mod + 3; 2844 st->next = mod + 3;
2845 break; 2845 break;
2846 default: 2846 default:
2847 st->next = mod + 2; 2847 st->next = mod + 2;
2848 break; 2848 break;
2849 } 2849 }
2850 2850
2851 char delim = st->startc == PROPEN ? PRCLOSE : BRCLOSE; 2851 char delim = st->startc == PROPEN ? PRCLOSE : BRCLOSE;
2852 char *val = ParseModifierPart(&st->next, delim, st->eflags, st->ctxt, 2852 char *val = ParseModifierPart(&st->next, delim, st->eflags, st->ctxt,
2853 NULL, NULL, NULL); 2853 NULL, NULL, NULL);
2854 if (st->v->flags & VAR_JUNK) { 2854 if (st->v->flags & VAR_JUNK) {
2855 /* restore original name */ 2855 /* restore original name */
2856 free(st->v->name); 2856 free(st->v->name);
2857 st->v->name = sv_name; 2857 st->v->name = sv_name;
2858 } 2858 }
2859 if (val == NULL) { 2859 if (val == NULL) {
2860 st->missing_delim = delim; 2860 st->missing_delim = delim;
2861 return 'c'; 2861 return 'c';
2862 } 2862 }
2863 2863
2864 st->next--; 2864 st->next--;
2865 2865
2866 if (st->eflags & VARE_WANTRES) { 2866 if (st->eflags & VARE_WANTRES) {
2867 switch (op[0]) { 2867 switch (op[0]) {
2868 case '+': 2868 case '+':
2869 Var_Append(st->v->name, val, v_ctxt); 2869 Var_Append(st->v->name, val, v_ctxt);
2870 break; 2870 break;
2871 case '!': { 2871 case '!': {
2872 const char *emsg; 2872 const char *emsg;
2873 st->newVal = Cmd_Exec(val, &emsg); 2873 st->newVal = Cmd_Exec(val, &emsg);
2874 if (emsg) 2874 if (emsg)
2875 Error(emsg, st->val); 2875 Error(emsg, st->val);
2876 else 2876 else
2877 Var_Set(st->v->name, st->newVal, v_ctxt); 2877 Var_Set(st->v->name, st->newVal, v_ctxt);
2878 free(st->newVal); 2878 free(st->newVal);
2879 break; 2879 break;
2880 } 2880 }
2881 case '?': 2881 case '?':
2882 if (!(st->v->flags & VAR_JUNK)) 2882 if (!(st->v->flags & VAR_JUNK))
2883 break; 2883 break;
2884 /* FALLTHROUGH */ 2884 /* FALLTHROUGH */
2885 default: 2885 default:
2886 Var_Set(st->v->name, val, v_ctxt); 2886 Var_Set(st->v->name, val, v_ctxt);
2887 break; 2887 break;
2888 } 2888 }
2889 } 2889 }
2890 free(val); 2890 free(val);
2891 st->newVal = varNoError; 2891 st->newVal = varNoError;
2892 return 0; 2892 return 0;
2893} 2893}
2894 2894
2895/* remember current value */ 2895/* remember current value */
2896static Boolean 2896static Boolean
2897ApplyModifier_Remember(const char *mod, ApplyModifiersState *st) 2897ApplyModifier_Remember(const char *mod, ApplyModifiersState *st)
2898{ 2898{
2899 if (!ModMatchEq(mod, "_", st->endc)) { 2899 if (!ModMatchEq(mod, "_", st->endc)) {
2900 st->next = mod + 1; 2900 st->next = mod + 1;
2901 return FALSE; 2901 return FALSE;
2902 } 2902 }
2903 2903
2904 if (mod[1] == '=') { 2904 if (mod[1] == '=') {
2905 size_t n = strcspn(mod + 2, ":)}"); 2905 size_t n = strcspn(mod + 2, ":)}");
2906 char *name = bmake_strndup(mod + 2, n); 2906 char *name = bmake_strndup(mod + 2, n);
2907 Var_Set(name, st->val, st->ctxt); 2907 Var_Set(name, st->val, st->ctxt);
2908 free(name); 2908 free(name);
2909 st->next = mod + 2 + n; 2909 st->next = mod + 2 + n;
2910 } else { 2910 } else {
2911 Var_Set("_", st->val, st->ctxt); 2911 Var_Set("_", st->val, st->ctxt);
2912 st->next = mod + 1; 2912 st->next = mod + 1;
2913 } 2913 }
2914 st->newVal = st->val; 2914 st->newVal = st->val;
2915 return TRUE; 2915 return TRUE;
2916} 2916}
2917 2917
2918#ifdef SYSVVARSUB 2918#ifdef SYSVVARSUB
2919/* :from=to */ 2919/* :from=to */
2920static int 2920static int
2921ApplyModifier_SysV(const char *mod, ApplyModifiersState *st) 2921ApplyModifier_SysV(const char *mod, ApplyModifiersState *st)
2922{ 2922{
2923 Boolean eqFound = FALSE; 2923 Boolean eqFound = FALSE;
2924 2924
2925 /* 2925 /*
2926 * First we make a pass through the string trying 2926 * First we make a pass through the string trying
2927 * to verify it is a SYSV-make-style translation: 2927 * to verify it is a SYSV-make-style translation:
2928 * it must be: <string1>=<string2>) 2928 * it must be: <string1>=<string2>)
2929 */ 2929 */
2930 st->next = mod; 2930 st->next = mod;
2931 int nest = 1; 2931 int nest = 1;
2932 while (*st->next != '\0' && nest > 0) { 2932 while (*st->next != '\0' && nest > 0) {
2933 if (*st->next == '=') { 2933 if (*st->next == '=') {
2934 eqFound = TRUE; 2934 eqFound = TRUE;
2935 /* continue looking for st->endc */ 2935 /* continue looking for st->endc */
2936 } else if (*st->next == st->endc) 2936 } else if (*st->next == st->endc)
2937 nest--; 2937 nest--;
2938 else if (*st->next == st->startc) 2938 else if (*st->next == st->startc)
2939 nest++; 2939 nest++;
2940 if (nest > 0) 2940 if (nest > 0)
2941 st->next++; 2941 st->next++;
2942 } 2942 }
2943 if (*st->next != st->endc || !eqFound) 2943 if (*st->next != st->endc || !eqFound)
2944 return 0; 2944 return 0;
2945 2945
2946 char delim = '='; 2946 char delim = '=';
2947 st->next = mod; 2947 st->next = mod;
2948 char *lhs = ParseModifierPart(&st->next, delim, st->eflags, st->ctxt, 2948 char *lhs = ParseModifierPart(&st->next, delim, st->eflags, st->ctxt,
2949 NULL, NULL, NULL); 2949 NULL, NULL, NULL);
2950 if (lhs == NULL) { 2950 if (lhs == NULL) {
2951 st->missing_delim = delim; 2951 st->missing_delim = delim;
2952 return 'c'; 2952 return 'c';
2953 } 2953 }
2954 2954
2955 delim = st->endc; 2955 delim = st->endc;
2956 char *rhs = ParseModifierPart(&st->next, delim, st->eflags, st->ctxt, 2956 char *rhs = ParseModifierPart(&st->next, delim, st->eflags, st->ctxt,
2957 NULL, NULL, NULL); 2957 NULL, NULL, NULL);
2958 if (rhs == NULL) { 2958 if (rhs == NULL) {
2959 st->missing_delim = delim; 2959 st->missing_delim = delim;
2960 return 'c'; 2960 return 'c';
2961 } 2961 }
2962 2962
2963 /* 2963 /*
2964 * SYSV modifications happen through the whole 2964 * SYSV modifications happen through the whole
2965 * string. Note the pattern is anchored at the end. 2965 * string. Note the pattern is anchored at the end.
2966 */ 2966 */
2967 st->next--; 2967 st->next--;
2968 if (lhs[0] == '\0' && *st->val == '\0') { 2968 if (lhs[0] == '\0' && *st->val == '\0') {
2969 st->newVal = st->val; /* special case */ 2969 st->newVal = st->val; /* special case */
2970 } else { 2970 } else {
2971 ModifyWord_SYSVSubstArgs args = { st->ctxt, lhs, rhs }; 2971 ModifyWord_SYSVSubstArgs args = { st->ctxt, lhs, rhs };
2972 st->newVal = ModifyWords(st->ctxt, st->sep, st->oneBigWord, st->val, 2972 st->newVal = ModifyWords(st->ctxt, st->sep, st->oneBigWord, st->val,
2973 ModifyWord_SYSVSubst, &args); 2973 ModifyWord_SYSVSubst, &args);
2974 } 2974 }
2975 free(lhs); 2975 free(lhs);
2976 free(rhs); 2976 free(rhs);
2977 return '='; 2977 return '=';
2978} 2978}
2979#endif 2979#endif
2980 2980
2981/* 2981/*
2982 * Now we need to apply any modifiers the user wants applied. 2982 * Now we need to apply any modifiers the user wants applied.
2983 * These are: 2983 * These are:
2984 * :M<pattern> words which match the given <pattern>. 2984 * :M<pattern> words which match the given <pattern>.
2985 * <pattern> is of the standard file 2985 * <pattern> is of the standard file
2986 * wildcarding form. 2986 * wildcarding form.
2987 * :N<pattern> words which do not match the given <pattern>. 2987 * :N<pattern> words which do not match the given <pattern>.
2988 * :S<d><pat1><d><pat2><d>[1gW] 2988 * :S<d><pat1><d><pat2><d>[1gW]
2989 * Substitute <pat2> for <pat1> in the value 2989 * Substitute <pat2> for <pat1> in the value
2990 * :C<d><pat1><d><pat2><d>[1gW] 2990 * :C<d><pat1><d><pat2><d>[1gW]
2991 * Substitute <pat2> for regex <pat1> in the value 2991 * Substitute <pat2> for regex <pat1> in the value
2992 * :H Substitute the head of each word 2992 * :H Substitute the head of each word
2993 * :T Substitute the tail of each word 2993 * :T Substitute the tail of each word
2994 * :E Substitute the extension (minus '.') of 2994 * :E Substitute the extension (minus '.') of
2995 * each word 2995 * each word
2996 * :R Substitute the root of each word 2996 * :R Substitute the root of each word
2997 * (pathname minus the suffix). 2997 * (pathname minus the suffix).
2998 * :O ("Order") Alphabeticaly sort words in variable. 2998 * :O ("Order") Alphabeticaly sort words in variable.
2999 * :Ox ("intermiX") Randomize words in variable. 2999 * :Ox ("intermiX") Randomize words in variable.
3000 * :u ("uniq") Remove adjacent duplicate words. 3000 * :u ("uniq") Remove adjacent duplicate words.
3001 * :tu Converts the variable contents to uppercase. 3001 * :tu Converts the variable contents to uppercase.
3002 * :tl Converts the variable contents to lowercase. 3002 * :tl Converts the variable contents to lowercase.
3003 * :ts[c] Sets varSpace - the char used to 3003 * :ts[c] Sets varSpace - the char used to
3004 * separate words to 'c'. If 'c' is 3004 * separate words to 'c'. If 'c' is
3005 * omitted then no separation is used. 3005 * omitted then no separation is used.
3006 * :tW Treat the variable contents as a single 3006 * :tW Treat the variable contents as a single
3007 * word, even if it contains spaces. 3007 * word, even if it contains spaces.
3008 * (Mnemonic: one big 'W'ord.) 3008 * (Mnemonic: one big 'W'ord.)
3009 * :tw Treat the variable contents as multiple 3009 * :tw Treat the variable contents as multiple
3010 * space-separated words. 3010 * space-separated words.
3011 * (Mnemonic: many small 'w'ords.) 3011 * (Mnemonic: many small 'w'ords.)
3012 * :[index] Select a single word from the value. 3012 * :[index] Select a single word from the value.
3013 * :[start..end] Select multiple words from the value. 3013 * :[start..end] Select multiple words from the value.
3014 * :[*] or :[0] Select the entire value, as a single 3014 * :[*] or :[0] Select the entire value, as a single
3015 * word. Equivalent to :tW. 3015 * word. Equivalent to :tW.
3016 * :[@] Select the entire value, as multiple 3016 * :[@] Select the entire value, as multiple
3017 * words. Undoes the effect of :[*]. 3017 * words. Undoes the effect of :[*].
3018 * Equivalent to :tw. 3018 * Equivalent to :tw.
3019 * :[#] Returns the number of words in the value. 3019 * :[#] Returns the number of words in the value.
3020 * 3020 *
3021 * :?<true-value>:<false-value> 3021 * :?<true-value>:<false-value>
3022 * If the variable evaluates to true, return 3022 * If the variable evaluates to true, return
3023 * true value, else return the second value. 3023 * true value, else return the second value.
3024 * :lhs=rhs Like :S, but the rhs goes to the end of 3024 * :lhs=rhs Like :S, but the rhs goes to the end of
3025 * the invocation. 3025 * the invocation.
3026 * :sh Treat the current value as a command 3026 * :sh Treat the current value as a command
3027 * to be run, new value is its output. 3027 * to be run, new value is its output.
3028 * The following added so we can handle ODE makefiles. 3028 * The following added so we can handle ODE makefiles.
3029 * :@<tmpvar>@<newval>@ 3029 * :@<tmpvar>@<newval>@
3030 * Assign a temporary local variable <tmpvar> 3030 * Assign a temporary local variable <tmpvar>
3031 * to the current value of each word in turn 3031 * to the current value of each word in turn
3032 * and replace each word with the result of 3032 * and replace each word with the result of
3033 * evaluating <newval> 3033 * evaluating <newval>
3034 * :D<newval> Use <newval> as value if variable defined 3034 * :D<newval> Use <newval> as value if variable defined
3035 * :U<newval> Use <newval> as value if variable undefined 3035 * :U<newval> Use <newval> as value if variable undefined
3036 * :L Use the name of the variable as the value. 3036 * :L Use the name of the variable as the value.
3037 * :P Use the path of the node that has the same 3037 * :P Use the path of the node that has the same
3038 * name as the variable as the value. This 3038 * name as the variable as the value. This
3039 * basically includes an implied :L so that 3039 * basically includes an implied :L so that
3040 * the common method of refering to the path 3040 * the common method of refering to the path
3041 * of your dependent 'x' in a rule is to use 3041 * of your dependent 'x' in a rule is to use
3042 * the form '${x:P}'. 3042 * the form '${x:P}'.
3043 * :!<cmd>! Run cmd much the same as :sh run's the 3043 * :!<cmd>! Run cmd much the same as :sh run's the
3044 * current value of the variable. 3044 * current value of the variable.
3045 * Assignment operators (see ApplyModifier_Assign). 3045 * Assignment operators (see ApplyModifier_Assign).
3046 */ 3046 */
3047static char * 3047static char *
3048ApplyModifiers(char *val, const char * const tstr, 3048ApplyModifiers(char *val, const char * const tstr,
3049 int const startc, int const endc, 3049 int const startc, int const endc,
3050 Var * const v, GNode * const ctxt, VarEvalFlags const eflags, 3050 Var * const v, GNode * const ctxt, VarEvalFlags const eflags,
3051 int * const lengthPtr, void ** const freePtr) 3051 int * const lengthPtr, void ** const freePtr)
3052{ 3052{
3053 ApplyModifiersState st = { 3053 ApplyModifiersState st = {
3054 startc, endc, v, ctxt, eflags, 3054 startc, endc, v, ctxt, eflags,
3055 val, NULL, NULL, '\0', ' ', FALSE 3055 val, NULL, NULL, '\0', ' ', FALSE
3056 }; 3056 };
3057 3057
3058 const char *p = tstr; 3058 const char *p = tstr;
3059 while (*p != '\0' && *p != endc) { 3059 while (*p != '\0' && *p != endc) {
3060 3060
3061 if (*p == '$') { 3061 if (*p == '$') {
3062 /* 3062 /*
3063 * We may have some complex modifiers in a variable. 3063 * We may have some complex modifiers in a variable.
3064 */ 3064 */
3065 void *freeIt; 3065 void *freeIt;
3066 const char *rval; 3066 const char *rval;
3067 int rlen; 3067 int rlen;
3068 int c; 3068 int c;
3069 3069
3070 rval = Var_Parse(p, st.ctxt, st.eflags, &rlen, &freeIt); 3070 rval = Var_Parse(p, st.ctxt, st.eflags, &rlen, &freeIt);
3071 3071
3072 /* 3072 /*
3073 * If we have not parsed up to st.endc or ':', 3073 * If we have not parsed up to st.endc or ':',
3074 * we are not interested. 3074 * we are not interested.
3075 */ 3075 */
3076 if (rval != NULL && *rval && 3076 if (rval != NULL && *rval &&
3077 (c = p[rlen]) != '\0' && 3077 (c = p[rlen]) != '\0' &&
3078 c != ':' && 3078 c != ':' &&
3079 c != st.endc) { 3079 c != st.endc) {
3080 free(freeIt); 3080 free(freeIt);
3081 goto apply_mods; 3081 goto apply_mods;
3082 } 3082 }
3083 3083
3084 if (DEBUG(VAR)) { 3084 if (DEBUG(VAR)) {
3085 fprintf(debug_file, "Got '%s' from '%.*s'%.*s\n", 3085 fprintf(debug_file, "Got '%s' from '%.*s'%.*s\n",
3086 rval, rlen, p, rlen, p + rlen); 3086 rval, rlen, p, rlen, p + rlen);
3087 } 3087 }
3088 3088
3089 p += rlen; 3089 p += rlen;
3090 3090
3091 if (rval != NULL && *rval) { 3091 if (rval != NULL && *rval) {
3092 int used; 3092 int used;
3093 3093
3094 st.val = ApplyModifiers(st.val, rval, 0, 0, st.v, 3094 st.val = ApplyModifiers(st.val, rval, 0, 0, st.v,
3095 st.ctxt, st.eflags, &used, freePtr); 3095 st.ctxt, st.eflags, &used, freePtr);
3096 if (st.val == var_Error 3096 if (st.val == var_Error
3097 || (st.val == varNoError && (st.eflags & VARE_UNDEFERR) == 0) 3097 || (st.val == varNoError && (st.eflags & VARE_UNDEFERR) == 0)
3098 || strlen(rval) != (size_t) used) { 3098 || strlen(rval) != (size_t) used) {
3099 free(freeIt); 3099 free(freeIt);
3100 goto out; /* error already reported */ 3100 goto out; /* error already reported */
3101 } 3101 }
3102 } 3102 }
3103 free(freeIt); 3103 free(freeIt);
3104 if (*p == ':') 3104 if (*p == ':')
3105 p++; 3105 p++;
3106 else if (*p == '\0' && endc != '\0') { 3106 else if (*p == '\0' && endc != '\0') {
3107 Error("Unclosed variable specification after complex " 3107 Error("Unclosed variable specification after complex "
3108 "modifier (expecting '%c') for %s", st.endc, st.v->name); 3108 "modifier (expecting '%c') for %s", st.endc, st.v->name);
3109 goto out; 3109 goto out;
3110 } 3110 }
3111 continue; 3111 continue;
3112 } 3112 }
3113 apply_mods: 3113 apply_mods:
3114 if (DEBUG(VAR)) { 3114 if (DEBUG(VAR)) {
3115 fprintf(debug_file, "Applying[%s] :%c to \"%s\"\n", st.v->name, 3115 fprintf(debug_file, "Applying[%s] :%c to \"%s\"\n", st.v->name,
3116 *p, st.val); 3116 *p, st.val);
3117 } 3117 }
3118 st.newVal = var_Error; 3118 st.newVal = var_Error;
3119 char modifier = *p; 3119 char modifier = *p;
3120 switch (modifier) { 3120 switch (modifier) {
3121 case ':': 3121 case ':':
3122 { 3122 {
3123 int res = ApplyModifier_Assign(p, &st); 3123 int res = ApplyModifier_Assign(p, &st);
3124 if (res == 'b') 3124 if (res == 'b')
3125 goto bad_modifier; 3125 goto bad_modifier;
3126 if (res == 'c') 3126 if (res == 'c')
3127 goto cleanup; 3127 goto cleanup;
3128 if (res == 'd') 3128 if (res == 'd')
3129 goto default_case; 3129 goto default_case;
3130 break; 3130 break;
3131 } 3131 }
3132 case '@': 3132 case '@':
3133 if (!ApplyModifier_Loop(p, &st)) 3133 if (!ApplyModifier_Loop(p, &st))
3134 goto cleanup; 3134 goto cleanup;
3135 break; 3135 break;
3136 case '_': 3136 case '_':
3137 if (!ApplyModifier_Remember(p, &st)) 3137 if (!ApplyModifier_Remember(p, &st))
3138 goto default_case; 3138 goto default_case;
3139 break; 3139 break;
3140 case 'D': 3140 case 'D':
3141 case 'U': 3141 case 'U':
3142 ApplyModifier_Defined(p, &st); 3142 ApplyModifier_Defined(p, &st);
3143 break; 3143 break;
3144 case 'L': 3144 case 'L':
3145 { 3145 {
3146 if (st.v->flags & VAR_JUNK) 3146 if (st.v->flags & VAR_JUNK)
3147 st.v->flags |= VAR_KEEP; 3147 st.v->flags |= VAR_KEEP;
3148 st.newVal = bmake_strdup(st.v->name); 3148 st.newVal = bmake_strdup(st.v->name);
3149 st.next = p + 1; 3149 st.next = p + 1;
3150 break; 3150 break;
3151 } 3151 }
3152 case 'P': 3152 case 'P':
3153 ApplyModifier_Path(p, &st); 3153 ApplyModifier_Path(p, &st);
3154 break; 3154 break;
3155 case '!': 3155 case '!':
3156 if (!ApplyModifier_Exclam(p, &st)) 3156 if (!ApplyModifier_Exclam(p, &st))
3157 goto cleanup; 3157 goto cleanup;
3158 break; 3158 break;
3159 case '[': 3159 case '[':
3160 { 3160 {
3161 int res = ApplyModifier_Words(p, &st); 3161 int res = ApplyModifier_Words(p, &st);
3162 if (res == 'b') 3162 if (res == 'b')
3163 goto bad_modifier; 3163 goto bad_modifier;
3164 if (res == 'c') 3164 if (res == 'c')
3165 goto cleanup; 3165 goto cleanup;
3166 break; 3166 break;
3167 } 3167 }
3168 case 'g': 3168 case 'g':
3169 if (!ApplyModifier_Gmtime(p, &st)) 3169 if (!ApplyModifier_Gmtime(p, &st))
3170 goto default_case; 3170 goto default_case;
3171 break; 3171 break;
3172 case 'h': 3172 case 'h':
3173 if (!ApplyModifier_Hash(p, &st)) 3173 if (!ApplyModifier_Hash(p, &st))
3174 goto default_case; 3174 goto default_case;
3175 break; 3175 break;
3176 case 'l': 3176 case 'l':
3177 if (!ApplyModifier_Localtime(p, &st)) 3177 if (!ApplyModifier_Localtime(p, &st))
3178 goto default_case; 3178 goto default_case;
3179 break; 3179 break;
3180 case 't': 3180 case 't':
3181 if (!ApplyModifier_To(p, &st)) 3181 if (!ApplyModifier_To(p, &st))
3182 goto bad_modifier; 3182 goto bad_modifier;
3183 break; 3183 break;
3184 case 'N': 3184 case 'N':
3185 case 'M': 3185 case 'M':
3186 ApplyModifier_Match(p, &st); 3186 ApplyModifier_Match(p, &st);
3187 break; 3187 break;
3188 case 'S': 3188 case 'S':
3189 if (!ApplyModifier_Subst(p, &st)) 3189 if (!ApplyModifier_Subst(p, &st))
3190 goto cleanup; 3190 goto cleanup;
3191 break; 3191 break;
3192 case '?': 3192 case '?':
3193 if (!ApplyModifier_IfElse(p, &st)) 3193 if (!ApplyModifier_IfElse(p, &st))
3194 goto cleanup; 3194 goto cleanup;
3195 break; 3195 break;
3196#ifndef NO_REGEX 3196#ifndef NO_REGEX
3197 case 'C': 3197 case 'C':
3198 if (!ApplyModifier_Regex(p, &st)) 3198 if (!ApplyModifier_Regex(p, &st))
3199 goto cleanup; 3199 goto cleanup;
3200 break; 3200 break;
3201#endif 3201#endif
3202 case 'q': 3202 case 'q':
3203 case 'Q': 3203 case 'Q':
3204 if (p[1] == st.endc || p[1] == ':') { 3204 if (p[1] == st.endc || p[1] == ':') {
3205 st.newVal = VarQuote(st.val, modifier == 'q'); 3205 st.newVal = VarQuote(st.val, modifier == 'q');
3206 st.next = p + 1; 3206 st.next = p + 1;
3207 break; 3207 break;
3208 } 3208 }
3209 goto default_case; 3209 goto default_case;
3210 case 'T': 3210 case 'T':
3211 if (p[1] == st.endc || p[1] == ':') { 3211 if (p[1] == st.endc || p[1] == ':') {
3212 st.newVal = ModifyWords(st.ctxt, st.sep, st.oneBigWord, 3212 st.newVal = ModifyWords(st.ctxt, st.sep, st.oneBigWord,
3213 st.val, ModifyWord_Tail, NULL); 3213 st.val, ModifyWord_Tail, NULL);
3214 st.next = p + 1; 3214 st.next = p + 1;
3215 break; 3215 break;
3216 } 3216 }
3217 goto default_case; 3217 goto default_case;
3218 case 'H': 3218 case 'H':
3219 if (p[1] == st.endc || p[1] == ':') { 3219 if (p[1] == st.endc || p[1] == ':') {
3220 st.newVal = ModifyWords(st.ctxt, st.sep, st.oneBigWord, 3220 st.newVal = ModifyWords(st.ctxt, st.sep, st.oneBigWord,
3221 st.val, ModifyWord_Head, NULL); 3221 st.val, ModifyWord_Head, NULL);
3222 st.next = p + 1; 3222 st.next = p + 1;
3223 break; 3223 break;
3224 } 3224 }
3225 goto default_case; 3225 goto default_case;
3226 case 'E': 3226 case 'E':
3227 if (p[1] == st.endc || p[1] == ':') { 3227 if (p[1] == st.endc || p[1] == ':') {
3228 st.newVal = ModifyWords(st.ctxt, st.sep, st.oneBigWord, 3228 st.newVal = ModifyWords(st.ctxt, st.sep, st.oneBigWord,
3229 st.val, ModifyWord_Suffix, NULL); 3229 st.val, ModifyWord_Suffix, NULL);
3230 st.next = p + 1; 3230 st.next = p + 1;
3231 break; 3231 break;
3232 } 3232 }
3233 goto default_case; 3233 goto default_case;
3234 case 'R': 3234 case 'R':
3235 if (p[1] == st.endc || p[1] == ':') { 3235 if (p[1] == st.endc || p[1] == ':') {
3236 st.newVal = ModifyWords(st.ctxt, st.sep, st.oneBigWord, 3236 st.newVal = ModifyWords(st.ctxt, st.sep, st.oneBigWord,
3237 st.val, ModifyWord_Root, NULL); 3237 st.val, ModifyWord_Root, NULL);
3238 st.next = p + 1; 3238 st.next = p + 1;
3239 break; 3239 break;
3240 } 3240 }
3241 goto default_case; 3241 goto default_case;
3242 case 'r': 3242 case 'r':
3243 if (!ApplyModifier_Range(p, &st)) 3243 if (!ApplyModifier_Range(p, &st))
3244 goto default_case; 3244 goto default_case;
3245 break; 3245 break;
3246 case 'O': 3246 case 'O':
3247 if (!ApplyModifier_Order(p, &st)) 3247 if (!ApplyModifier_Order(p, &st))
3248 goto bad_modifier; 3248 goto bad_modifier;
3249 break; 3249 break;
3250 case 'u': 3250 case 'u':
3251 if (p[1] == st.endc || p[1] == ':') { 3251 if (p[1] == st.endc || p[1] == ':') {
3252 st.newVal = VarUniq(st.val); 3252 st.newVal = VarUniq(st.val);
3253 st.next = p + 1; 3253 st.next = p + 1;
3254 break; 3254 break;
3255 } 3255 }
3256 goto default_case; 3256 goto default_case;
3257#ifdef SUNSHCMD 3257#ifdef SUNSHCMD
3258 case 's': 3258 case 's':
3259 if (p[1] == 'h' && (p[2] == st.endc || p[2] == ':')) { 3259 if (p[1] == 'h' && (p[2] == st.endc || p[2] == ':')) {
3260 const char *emsg; 3260 const char *emsg;
3261 if (st.eflags & VARE_WANTRES) { 3261 if (st.eflags & VARE_WANTRES) {
3262 st.newVal = Cmd_Exec(st.val, &emsg); 3262 st.newVal = Cmd_Exec(st.val, &emsg);
3263 if (emsg) 3263 if (emsg)
3264 Error(emsg, st.val); 3264 Error(emsg, st.val);
3265 } else 3265 } else
3266 st.newVal = varNoError; 3266 st.newVal = varNoError;
3267 st.next = p + 2; 3267 st.next = p + 2;
3268 break; 3268 break;
3269 } 3269 }
3270 goto default_case; 3270 goto default_case;
3271#endif 3271#endif
3272 default: 3272 default:
3273 default_case: 3273 default_case:
3274 { 3274 {
3275#ifdef SYSVVARSUB 3275#ifdef SYSVVARSUB
3276 int res = ApplyModifier_SysV(p, &st); 3276 int res = ApplyModifier_SysV(p, &st);
3277 if (res == 'c') 3277 if (res == 'c')
3278 goto cleanup; 3278 goto cleanup;
3279 if (res != '=') 3279 if (res != '=')
3280#endif 3280#endif
3281 { 3281 {
3282 Error("Unknown modifier '%c'", *p); 3282 Error("Unknown modifier '%c'", *p);
3283 for (st.next = p + 1; 3283 for (st.next = p + 1;
3284 *st.next != ':' && *st.next != st.endc && 3284 *st.next != ':' && *st.next != st.endc &&
3285 *st.next != '\0'; 3285 *st.next != '\0';
3286 st.next++) 3286 st.next++)
3287 continue; 3287 continue;
3288 st.newVal = var_Error; 3288 st.newVal = var_Error;
3289 } 3289 }
3290 } 3290 }
3291 } 3291 }
3292 if (DEBUG(VAR)) { 3292 if (DEBUG(VAR)) {
3293 fprintf(debug_file, "Result[%s] of :%c is \"%s\"\n", 3293 fprintf(debug_file, "Result[%s] of :%c is \"%s\"\n",
3294 st.v->name, modifier, st.newVal); 3294 st.v->name, modifier, st.newVal);
3295 } 3295 }
3296 3296
3297 if (st.newVal != st.val) { 3297 if (st.newVal != st.val) {
3298 if (*freePtr) { 3298 if (*freePtr) {
3299 free(st.val); 3299 free(st.val);
3300 *freePtr = NULL; 3300 *freePtr = NULL;
3301 } 3301 }
3302 st.val = st.newVal; 3302 st.val = st.newVal;
3303 if (st.val != var_Error && st.val != varNoError) { 3303 if (st.val != var_Error && st.val != varNoError) {
3304 *freePtr = st.val; 3304 *freePtr = st.val;
3305 } 3305 }
3306 } 3306 }
3307 if (*st.next == '\0' && st.endc != '\0') { 3307 if (*st.next == '\0' && st.endc != '\0') {
3308 Error("Unclosed variable specification (expecting '%c') " 3308 Error("Unclosed variable specification (expecting '%c') "
3309 "for \"%s\" (value \"%s\") modifier %c", 3309 "for \"%s\" (value \"%s\") modifier %c",
3310 st.endc, st.v->name, st.val, modifier); 3310 st.endc, st.v->name, st.val, modifier);
3311 } else if (*st.next == ':') { 3311 } else if (*st.next == ':') {
3312 st.next++; 3312 st.next++;
3313 } 3313 }
3314 p = st.next; 3314 p = st.next;
3315 } 3315 }
3316out: 3316out:
3317 *lengthPtr = p - tstr; 3317 *lengthPtr = p - tstr;
3318 return st.val; 3318 return st.val;
3319 3319
3320bad_modifier: 3320bad_modifier:
3321 Error("Bad modifier `:%.*s' for %s", 3321 Error("Bad modifier `:%.*s' for %s",
3322 (int)strcspn(p, ":)}"), p, st.v->name); 3322 (int)strcspn(p, ":)}"), p, st.v->name);
3323 3323
3324cleanup: 3324cleanup:
3325 *lengthPtr = st.next - tstr; 3325 *lengthPtr = st.next - tstr;
3326 if (st.missing_delim != '\0') 3326 if (st.missing_delim != '\0')
3327 Error("Unclosed substitution for %s (%c missing)", 3327 Error("Unclosed substitution for %s (%c missing)",
3328 st.v->name, st.missing_delim); 3328 st.v->name, st.missing_delim);
3329 free(*freePtr); 3329 free(*freePtr);
3330 *freePtr = NULL; 3330 *freePtr = NULL;
3331 return var_Error; 3331 return var_Error;
3332} 3332}
3333 3333
3334static Boolean 3334static Boolean
3335VarIsDynamic(GNode *ctxt, const char *varname, size_t namelen) 3335VarIsDynamic(GNode *ctxt, const char *varname, size_t namelen)
3336{ 3336{
3337 if ((namelen == 1 || 3337 if ((namelen == 1 ||
3338 (namelen == 2 && (varname[1] == 'F' || varname[1] == 'D'))) && 3338 (namelen == 2 && (varname[1] == 'F' || varname[1] == 'D'))) &&
3339 (ctxt == VAR_CMD || ctxt == VAR_GLOBAL)) 3339 (ctxt == VAR_CMD || ctxt == VAR_GLOBAL))
3340 { 3340 {
3341 /* 3341 /*
3342 * If substituting a local variable in a non-local context, 3342 * If substituting a local variable in a non-local context,
3343 * assume it's for dynamic source stuff. We have to handle 3343 * assume it's for dynamic source stuff. We have to handle
3344 * this specially and return the longhand for the variable 3344 * this specially and return the longhand for the variable
3345 * with the dollar sign escaped so it makes it back to the 3345 * with the dollar sign escaped so it makes it back to the
3346 * caller. Only four of the local variables are treated 3346 * caller. Only four of the local variables are treated
3347 * specially as they are the only four that will be set 3347 * specially as they are the only four that will be set
3348 * when dynamic sources are expanded. 3348 * when dynamic sources are expanded.
3349 */ 3349 */
3350 switch (varname[0]) { 3350 switch (varname[0]) {
3351 case '@': 3351 case '@':
3352 case '%': 3352 case '%':
3353 case '*': 3353 case '*':
3354 case '!': 3354 case '!':
3355 return TRUE; 3355 return TRUE;
3356 } 3356 }
3357 return FALSE; 3357 return FALSE;
3358 } 3358 }
3359 3359
3360 if ((namelen == 7 || namelen == 8) && varname[0] == '.' && 3360 if ((namelen == 7 || namelen == 8) && varname[0] == '.' &&
3361 isupper((unsigned char) varname[1]) && 3361 isupper((unsigned char) varname[1]) &&
3362 (ctxt == VAR_CMD || ctxt == VAR_GLOBAL)) 3362 (ctxt == VAR_CMD || ctxt == VAR_GLOBAL))
3363 { 3363 {
3364 return strcmp(varname, ".TARGET") == 0 || 3364 return strcmp(varname, ".TARGET") == 0 ||
3365 strcmp(varname, ".ARCHIVE") == 0 || 3365 strcmp(varname, ".ARCHIVE") == 0 ||
3366 strcmp(varname, ".PREFIX") == 0 || 3366 strcmp(varname, ".PREFIX") == 0 ||
3367 strcmp(varname, ".MEMBER") == 0; 3367 strcmp(varname, ".MEMBER") == 0;
3368 } 3368 }
3369 3369
3370 return FALSE; 3370 return FALSE;
3371} 3371}
3372 3372
3373/*- 3373/*-
3374 *----------------------------------------------------------------------- 3374 *-----------------------------------------------------------------------
3375 * Var_Parse -- 3375 * Var_Parse --
3376 * Given the start of a variable invocation (such as $v, $(VAR), 3376 * Given the start of a variable invocation (such as $v, $(VAR),
3377 * ${VAR:Mpattern}), extract the variable name, possibly some 3377 * ${VAR:Mpattern}), extract the variable name, possibly some
3378 * modifiers and find its value by applying the modifiers to the 3378 * modifiers and find its value by applying the modifiers to the
3379 * original value. 3379 * original value.
3380 * 3380 *
3381 * Input: 3381 * Input:
3382 * str The string to parse 3382 * str The string to parse
3383 * ctxt The context for the variable 3383 * ctxt The context for the variable
3384 * flags VARE_UNDEFERR if undefineds are an error 3384 * flags VARE_UNDEFERR if undefineds are an error
3385 * VARE_WANTRES if we actually want the result 3385 * VARE_WANTRES if we actually want the result
3386 * VARE_ASSIGN if we are in a := assignment 3386 * VARE_ASSIGN if we are in a := assignment
3387 * lengthPtr OUT: The length of the specification 3387 * lengthPtr OUT: The length of the specification
3388 * freePtr OUT: Non-NULL if caller should free *freePtr 3388 * freePtr OUT: Non-NULL if caller should free *freePtr
3389 * 3389 *
3390 * Results: 3390 * Results:
3391 * The (possibly-modified) value of the variable or var_Error if the 3391 * The (possibly-modified) value of the variable or var_Error if the
3392 * specification is invalid. The length of the specification is 3392 * specification is invalid. The length of the specification is
3393 * placed in *lengthPtr (for invalid specifications, this is just 3393 * placed in *lengthPtr (for invalid specifications, this is just
3394 * 2...?). 3394 * 2...?).
3395 * If *freePtr is non-NULL then it's a pointer that the caller 3395 * If *freePtr is non-NULL then it's a pointer that the caller
3396 * should pass to free() to free memory used by the result. 3396 * should pass to free() to free memory used by the result.
3397 * 3397 *
3398 * Side Effects: 3398 * Side Effects:
3399 * None. 3399 * None.
3400 * 3400 *
3401 *----------------------------------------------------------------------- 3401 *-----------------------------------------------------------------------
3402 */ 3402 */
3403/* coverity[+alloc : arg-*4] */ 3403/* coverity[+alloc : arg-*4] */
3404const char * 3404const char *
3405Var_Parse(const char * const str, GNode *ctxt, VarEvalFlags eflags, 3405Var_Parse(const char * const str, GNode *ctxt, VarEvalFlags eflags,
3406 int *lengthPtr, void **freePtr) 3406 int *lengthPtr, void **freePtr)
3407{ 3407{
3408 const char *tstr; /* Pointer into str */ 3408 const char *tstr; /* Pointer into str */
3409 Var *v; /* Variable in invocation */ 3409 Var *v; /* Variable in invocation */
3410 Boolean haveModifier; /* TRUE if have modifiers for the variable */ 3410 Boolean haveModifier; /* TRUE if have modifiers for the variable */
3411 char endc; /* Ending character when variable in parens 3411 char endc; /* Ending character when variable in parens
3412 * or braces */ 3412 * or braces */
3413 char startc; /* Starting character when variable in parens 3413 char startc; /* Starting character when variable in parens
3414 * or braces */ 3414 * or braces */
3415 char *nstr; /* New string, used during expansion */ 3415 char *nstr; /* New string, used during expansion */
3416 Boolean dynamic; /* TRUE if the variable is local and we're 3416 Boolean dynamic; /* TRUE if the variable is local and we're
3417 * expanding it in a non-local context. This 3417 * expanding it in a non-local context. This
3418 * is done to support dynamic sources. The 3418 * is done to support dynamic sources. The
3419 * result is just the invocation, unaltered */ 3419 * result is just the invocation, unaltered */
3420 const char *extramodifiers; /* extra modifiers to apply first */ 3420 const char *extramodifiers; /* extra modifiers to apply first */
3421 3421
3422 *freePtr = NULL; 3422 *freePtr = NULL;
3423 extramodifiers = NULL; 3423 extramodifiers = NULL;
3424 dynamic = FALSE; 3424 dynamic = FALSE;
3425 3425
3426 startc = str[1]; 3426 startc = str[1];
3427 if (startc != PROPEN && startc != BROPEN) { 3427 if (startc != PROPEN && startc != BROPEN) {
3428 /* 3428 /*
3429 * If it's not bounded by braces of some sort, life is much simpler. 3429 * If it's not bounded by braces of some sort, life is much simpler.
3430 * We just need to check for the first character and return the 3430 * We just need to check for the first character and return the
3431 * value if it exists. 3431 * value if it exists.
3432 */ 3432 */
3433 3433
3434 /* Error out some really stupid names */ 3434 /* Error out some really stupid names */
3435 if (startc == '\0' || strchr(")}:$", startc)) { 3435 if (startc == '\0' || strchr(")}:$", startc)) {
3436 *lengthPtr = 1; 3436 *lengthPtr = 1;
3437 return var_Error; 3437 return var_Error;
3438 } 3438 }
3439 char name[] = { startc, '\0' }; 3439 char name[] = { startc, '\0' };
3440 3440
3441 v = VarFind(name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); 3441 v = VarFind(name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD);
3442 if (v == NULL) { 3442 if (v == NULL) {
3443 *lengthPtr = 2; 3443 *lengthPtr = 2;
3444 3444
3445 if ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)) { 3445 if ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)) {
3446 /* 3446 /*
3447 * If substituting a local variable in a non-local context, 3447 * If substituting a local variable in a non-local context,
3448 * assume it's for dynamic source stuff. We have to handle 3448 * assume it's for dynamic source stuff. We have to handle
3449 * this specially and return the longhand for the variable 3449 * this specially and return the longhand for the variable
3450 * with the dollar sign escaped so it makes it back to the 3450 * with the dollar sign escaped so it makes it back to the
3451 * caller. Only four of the local variables are treated 3451 * caller. Only four of the local variables are treated
3452 * specially as they are the only four that will be set 3452 * specially as they are the only four that will be set
3453 * when dynamic sources are expanded. 3453 * when dynamic sources are expanded.
3454 */ 3454 */
3455 switch (str[1]) { 3455 switch (str[1]) {
3456 case '@': 3456 case '@':
3457 return "$(.TARGET)"; 3457 return "$(.TARGET)";
3458 case '%': 3458 case '%':
3459 return "$(.MEMBER)"; 3459 return "$(.MEMBER)";
3460 case '*': 3460 case '*':
3461 return "$(.PREFIX)"; 3461 return "$(.PREFIX)";
3462 case '!': 3462 case '!':
3463 return "$(.ARCHIVE)"; 3463 return "$(.ARCHIVE)";
3464 } 3464 }
3465 } 3465 }
3466 return (eflags & VARE_UNDEFERR) ? var_Error : varNoError; 3466 return (eflags & VARE_UNDEFERR) ? var_Error : varNoError;
3467 } else { 3467 } else {
3468 haveModifier = FALSE; 3468 haveModifier = FALSE;
3469 tstr = str + 1; 3469 tstr = str + 1;
3470 endc = str[1]; 3470 endc = str[1];
3471 } 3471 }
3472 } else { 3472 } else {
3473 Buffer namebuf; /* Holds the variable name */ 3473 Buffer namebuf; /* Holds the variable name */
3474 int depth = 1; 3474 int depth = 1;
3475 3475
3476 endc = startc == PROPEN ? PRCLOSE : BRCLOSE; 3476 endc = startc == PROPEN ? PRCLOSE : BRCLOSE;
3477 Buf_Init(&namebuf, 0); 3477 Buf_Init(&namebuf, 0);
3478 3478
3479 /* 3479 /*
3480 * Skip to the end character or a colon, whichever comes first. 3480 * Skip to the end character or a colon, whichever comes first.
3481 */ 3481 */
3482 for (tstr = str + 2; *tstr != '\0'; tstr++) { 3482 for (tstr = str + 2; *tstr != '\0'; tstr++) {
3483 /* Track depth so we can spot parse errors. */ 3483 /* Track depth so we can spot parse errors. */
3484 if (*tstr == startc) 3484 if (*tstr == startc)
3485 depth++; 3485 depth++;
3486 if (*tstr == endc) { 3486 if (*tstr == endc) {
3487 if (--depth == 0) 3487 if (--depth == 0)
3488 break; 3488 break;
3489 } 3489 }
3490 if (depth == 1 && *tstr == ':') 3490 if (depth == 1 && *tstr == ':')
3491 break; 3491 break;
3492 /* A variable inside a variable, expand. */ 3492 /* A variable inside a variable, expand. */
3493 if (*tstr == '$') { 3493 if (*tstr == '$') {
3494 int rlen; 3494 int rlen;
3495 void *freeIt; 3495 void *freeIt;
3496 const char *rval = Var_Parse(tstr, ctxt, eflags, &rlen, &freeIt); 3496 const char *rval = Var_Parse(tstr, ctxt, eflags, &rlen, &freeIt);
3497 if (rval != NULL) 3497 if (rval != NULL)
3498 Buf_AddStr(&namebuf, rval); 3498 Buf_AddStr(&namebuf, rval);
3499 free(freeIt); 3499 free(freeIt);
3500 tstr += rlen - 1; 3500 tstr += rlen - 1;
3501 } else 3501 } else
3502 Buf_AddByte(&namebuf, *tstr); 3502 Buf_AddByte(&namebuf, *tstr);
3503 } 3503 }
3504 if (*tstr == ':') { 3504 if (*tstr == ':') {
3505 haveModifier = TRUE; 3505 haveModifier = TRUE;
3506 } else if (*tstr == endc) { 3506 } else if (*tstr == endc) {
3507 haveModifier = FALSE; 3507 haveModifier = FALSE;
3508 } else { 3508 } else {
3509 Parse_Error(PARSE_FATAL, "Unclosed variable \"%s\"", 3509 Parse_Error(PARSE_FATAL, "Unclosed variable \"%s\"",
3510 Buf_GetAll(&namebuf, NULL)); 3510 Buf_GetAll(&namebuf, NULL));
3511 /* 3511 /*
3512 * If we never did find the end character, return NULL 3512 * If we never did find the end character, return NULL
3513 * right now, setting the length to be the distance to 3513 * right now, setting the length to be the distance to
3514 * the end of the string, since that's what make does. 3514 * the end of the string, since that's what make does.
3515 */ 3515 */
3516 *lengthPtr = tstr - str; 3516 *lengthPtr = tstr - str;
3517 Buf_Destroy(&namebuf, TRUE); 3517 Buf_Destroy(&namebuf, TRUE);
3518 return var_Error; 3518 return var_Error;
3519 } 3519 }
3520 3520
3521 int namelen; 3521 int namelen;
3522 char *varname = Buf_GetAll(&namebuf, &namelen); 3522 char *varname = Buf_GetAll(&namebuf, &namelen);
3523 3523
3524 /* 3524 /*
3525 * At this point, varname points into newly allocated memory from 3525 * At this point, varname points into newly allocated memory from

cvs diff -r1.75 -r1.76 src/usr.bin/make/unit-tests/Makefile (switch to unified diff)

--- src/usr.bin/make/unit-tests/Makefile 2020/07/28 22:44:44 1.75
+++ src/usr.bin/make/unit-tests/Makefile 2020/07/29 19:48:33 1.76
@@ -1,191 +1,192 @@ @@ -1,191 +1,192 @@
1# $NetBSD: Makefile,v 1.75 2020/07/28 22:44:44 rillig Exp $ 1# $NetBSD: Makefile,v 1.76 2020/07/29 19:48:33 rillig Exp $
2# 2#
3# Unit tests for make(1) 3# Unit tests for make(1)
4# 4#
5# The main targets are: 5# The main targets are:
6# 6#
7# all: 7# all:
8# run all the tests 8# run all the tests
9# test: 9# test:
10# run 'all', and compare to expected results 10# run 'all', and compare to expected results
11# accept: 11# accept:
12# move generated output to expected results 12# move generated output to expected results
13# 13#
14# Settable variables 14# Settable variables
15# 15#
16# TEST_MAKE 16# TEST_MAKE
17# The make program to be tested. 17# The make program to be tested.
18# 18#
19# 19#
20# Adding a test case 20# Adding a test case
21# 21#
22# Each feature should get its own set of tests in its own suitably 22# Each feature should get its own set of tests in its own suitably
23# named makefile (*.mk), with its own set of expected results (*.exp), 23# named makefile (*.mk), with its own set of expected results (*.exp),
24# and it should be added to the TESTS list. 24# and it should be added to the TESTS list.
25# 25#
26# Any added files must also be added to src/distrib/sets/lists/tests/mi. 26# Any added files must also be added to src/distrib/sets/lists/tests/mi.
27# Makefiles that are not added to TESTS must be ignored in 27# Makefiles that are not added to TESTS must be ignored in
28# src/tests/usr.bin/make/t_make.sh (example: include-sub). 28# src/tests/usr.bin/make/t_make.sh (example: include-sub).
29# 29#
30 30
31# Each test is in a sub-makefile. 31# Each test is in a sub-makefile.
32# Keep the list sorted. 32# Keep the list sorted.
33TESTS+= # archive # broken on FreeBSD 33TESTS+= # archive # broken on FreeBSD
34TESTS+= cmdline 34TESTS+= cmdline
35TESTS+= comment 35TESTS+= comment
36TESTS+= cond-late 36TESTS+= cond-late
37TESTS+= cond-short 37TESTS+= cond-short
38TESTS+= cond1 38TESTS+= cond1
39TESTS+= cond2 39TESTS+= cond2
40TESTS+= directives 40TESTS+= directives
41TESTS+= dollar 41TESTS+= dollar
42TESTS+= doterror 42TESTS+= doterror
43TESTS+= dotwait 43TESTS+= dotwait
44TESTS+= envfirst 44TESTS+= envfirst
45TESTS+= error 45TESTS+= error
46TESTS+= # escape # broken by reverting POSIX changes 46TESTS+= # escape # broken by reverting POSIX changes
47TESTS+= export 47TESTS+= export
48TESTS+= export-all 48TESTS+= export-all
49TESTS+= export-env 49TESTS+= export-env
50TESTS+= forloop 50TESTS+= forloop
51TESTS+= forsubst 51TESTS+= forsubst
52TESTS+= hash 52TESTS+= hash
53TESTS+= # impsrc # broken by reverting POSIX changes 53TESTS+= # impsrc # broken by reverting POSIX changes
54TESTS+= include-main 54TESTS+= include-main
55TESTS+= misc 55TESTS+= misc
56TESTS+= moderrs 56TESTS+= moderrs
57TESTS+= modmatch 57TESTS+= modmatch
58TESTS+= modmisc 58TESTS+= modmisc
59TESTS+= modorder 59TESTS+= modorder
60TESTS+= modts 60TESTS+= modts
61TESTS+= modword 61TESTS+= modword
62TESTS+= order 62TESTS+= order
63TESTS+= # phony-end # broken by reverting POSIX changes 63TESTS+= # phony-end # broken by reverting POSIX changes
64TESTS+= posix 64TESTS+= posix
65TESTS+= # posix1 # broken by reverting POSIX changes 65TESTS+= # posix1 # broken by reverting POSIX changes
66TESTS+= qequals 66TESTS+= qequals
67TESTS+= # suffixes # broken by reverting POSIX changes 67TESTS+= # suffixes # broken by reverting POSIX changes
68TESTS+= sunshcmd 68TESTS+= sunshcmd
69TESTS+= sysv 69TESTS+= sysv
70TESTS+= ternary 70TESTS+= ternary
71TESTS+= unexport 71TESTS+= unexport
72TESTS+= unexport-env 72TESTS+= unexport-env
73TESTS+= varcmd 73TESTS+= varcmd
74TESTS+= vardebug 74TESTS+= vardebug
75TESTS+= varfind 75TESTS+= varfind
76TESTS+= varmisc 76TESTS+= varmisc
77TESTS+= varmod-edge 77TESTS+= varmod-edge
78TESTS+= varparse-dynamic 78TESTS+= varparse-dynamic
79TESTS+= varquote 79TESTS+= varquote
80TESTS+= varshell 80TESTS+= varshell
81 81
82# Override environment variables for some of the tests. 82# Override environment variables for some of the tests.
83ENV.envfirst= FROM_ENV=value-from-env 83ENV.envfirst= FROM_ENV=value-from-env
84ENV.export= -i PATH=${PATH:Q} 84ENV.export= -i PATH=${PATH:Q}
85ENV.varmisc= FROM_ENV=env 85ENV.varmisc= FROM_ENV=env
86ENV.varmisc+= FROM_ENV_BEFORE=env 86ENV.varmisc+= FROM_ENV_BEFORE=env
87ENV.varmisc+= FROM_ENV_AFTER=env 87ENV.varmisc+= FROM_ENV_AFTER=env
88 88
89# Override make flags for some of the tests; default is -k. 89# Override make flags for some of the tests; default is -k.
90FLAGS.doterror= # none 90FLAGS.doterror= # none
91FLAGS.envfirst= -e 91FLAGS.envfirst= -e
92FLAGS.export= -r 92FLAGS.export= -r
93FLAGS.order= -j1 93FLAGS.order= -j1
94FLAGS.vardebug= -k -dv FROM_CMDLINE= 94FLAGS.vardebug= -k -dv FROM_CMDLINE=
95 95
96# Some tests need extra post-processing. 96# Some tests need extra post-processing.
 97SED_CMDS.moderrs+= -e 's,\(substitution error:\).*,\1 (details omitted),'
97SED_CMDS.modmisc+= -e 's,\(substitution error:\).*,\1 (details omitted),' 98SED_CMDS.modmisc+= -e 's,\(substitution error:\).*,\1 (details omitted),'
98SED_CMDS.varshell+= -e 's,^[a-z]*sh: ,,' 99SED_CMDS.varshell+= -e 's,^[a-z]*sh: ,,'
99SED_CMDS.varshell+= -e '/command/s,No such.*,not found,' 100SED_CMDS.varshell+= -e '/command/s,No such.*,not found,'
100 101
101# Some tests need an additional round of postprocessing. 102# Some tests need an additional round of postprocessing.
102POSTPROC.vardebug= ${TOOL_SED} -n -e '/:RELEVANT = yes/,/:RELEVANT = no/p' 103POSTPROC.vardebug= ${TOOL_SED} -n -e '/:RELEVANT = yes/,/:RELEVANT = no/p'
103 104
104# End of the configuration section. 105# End of the configuration section.
105 106
106.MAIN: all 107.MAIN: all
107 108
108UNIT_TESTS:= ${.PARSEDIR} 109UNIT_TESTS:= ${.PARSEDIR}
109.PATH: ${UNIT_TESTS} 110.PATH: ${UNIT_TESTS}
110 111
111OUTFILES= ${TESTS:=.out} 112OUTFILES= ${TESTS:=.out}
112 113
113all: ${OUTFILES} 114all: ${OUTFILES}
114 115
115CLEANFILES+= *.rawout *.out *.status *.tmp *.core *.tmp 116CLEANFILES+= *.rawout *.out *.status *.tmp *.core *.tmp
116CLEANFILES+= obj*.[och] lib*.a # posix1.mk 117CLEANFILES+= obj*.[och] lib*.a # posix1.mk
117CLEANFILES+= issue* .[ab]* # suffixes.mk 118CLEANFILES+= issue* .[ab]* # suffixes.mk
118CLEANRECURSIVE+= dir dummy # posix1.mk 119CLEANRECURSIVE+= dir dummy # posix1.mk
119 120
120clean: 121clean:
121 rm -f ${CLEANFILES} 122 rm -f ${CLEANFILES}
122.if !empty(CLEANRECURSIVE) 123.if !empty(CLEANRECURSIVE)
123 rm -rf ${CLEANRECURSIVE} 124 rm -rf ${CLEANRECURSIVE}
124.endif 125.endif
125 126
126TEST_MAKE?= ${.MAKE} 127TEST_MAKE?= ${.MAKE}
127TOOL_SED?= sed 128TOOL_SED?= sed
128 129
129# ensure consistent results from sort(1) 130# ensure consistent results from sort(1)
130LC_ALL= C 131LC_ALL= C
131LANG= C 132LANG= C
132.export LANG LC_ALL 133.export LANG LC_ALL
133 134
134# the tests are actually done with sub-makes. 135# the tests are actually done with sub-makes.
135.SUFFIXES: .mk .rawout .out 136.SUFFIXES: .mk .rawout .out
136.mk.rawout: 137.mk.rawout:
137 @echo testing ${.IMPSRC} 138 @echo testing ${.IMPSRC}
138 @set -eu; \ 139 @set -eu; \
139 cd ${.OBJDIR}; \ 140 cd ${.OBJDIR}; \
140 env ${ENV.${.TARGET:R}} ${TEST_MAKE} -C ${.CURDIR} \ 141 env ${ENV.${.TARGET:R}} ${TEST_MAKE} -C ${.CURDIR} \
141 ${FLAGS.${.TARGET:R}:U-k} -f ${.IMPSRC} \ 142 ${FLAGS.${.TARGET:R}:U-k} -f ${.IMPSRC} \
142 > ${.TARGET}.tmp 2>&1 \ 143 > ${.TARGET}.tmp 2>&1 \
143 && status=$$? || status=$$?; \ 144 && status=$$? || status=$$?; \
144 echo $$status > ${.TARGET:R}.status 145 echo $$status > ${.TARGET:R}.status
145 @mv ${.TARGET}.tmp ${.TARGET} 146 @mv ${.TARGET}.tmp ${.TARGET}
146 147
147# Post-process the test output so that the results can be compared. 148# Post-process the test output so that the results can be compared.
148# 149#
149# always pretend .MAKE was called 'make' 150# always pretend .MAKE was called 'make'
150_SED_CMDS+= -e 's,^${TEST_MAKE:T:S,.,\\.,g}[][0-9]*:,make:,' 151_SED_CMDS+= -e 's,^${TEST_MAKE:T:S,.,\\.,g}[][0-9]*:,make:,'
151_SED_CMDS+= -e 's,${TEST_MAKE:S,.,\\.,g},make,' 152_SED_CMDS+= -e 's,${TEST_MAKE:S,.,\\.,g},make,'
152# replace anything after 'stopped in' with unit-tests 153# replace anything after 'stopped in' with unit-tests
153_SED_CMDS+= -e '/stopped/s, /.*, unit-tests,' 154_SED_CMDS+= -e '/stopped/s, /.*, unit-tests,'
154# strip ${.CURDIR}/ from the output 155# strip ${.CURDIR}/ from the output
155_SED_CMDS+= -e 's,${.CURDIR:S,.,\\.,g}/,,g' 156_SED_CMDS+= -e 's,${.CURDIR:S,.,\\.,g}/,,g'
156_SED_CMDS+= -e 's,${UNIT_TESTS:S,.,\\.,g}/,,g' 157_SED_CMDS+= -e 's,${UNIT_TESTS:S,.,\\.,g}/,,g'
157 158
158.rawout.out: 159.rawout.out:
159 @echo postprocess ${.TARGET} 160 @echo postprocess ${.TARGET}
160 @${TOOL_SED} ${_SED_CMDS} ${SED_CMDS.${.TARGET:R}} \ 161 @${TOOL_SED} ${_SED_CMDS} ${SED_CMDS.${.TARGET:R}} \
161 < ${.IMPSRC} > ${.TARGET}.tmp1 162 < ${.IMPSRC} > ${.TARGET}.tmp1
162 @${POSTPROC.${.TARGET:R}:Ucat} < ${.TARGET}.tmp1 > ${.TARGET}.tmp2 163 @${POSTPROC.${.TARGET:R}:Ucat} < ${.TARGET}.tmp1 > ${.TARGET}.tmp2
163 @rm ${.TARGET}.tmp1 164 @rm ${.TARGET}.tmp1
164 @echo "exit status `cat ${.TARGET:R}.status`" >> ${.TARGET}.tmp2 165 @echo "exit status `cat ${.TARGET:R}.status`" >> ${.TARGET}.tmp2
165 @mv ${.TARGET}.tmp2 ${.TARGET} 166 @mv ${.TARGET}.tmp2 ${.TARGET}
166 167
167# Compare all output files 168# Compare all output files
168test: ${OUTFILES} .PHONY 169test: ${OUTFILES} .PHONY
169 @failed= ; \ 170 @failed= ; \
170 for test in ${TESTS}; do \ 171 for test in ${TESTS}; do \
171 diff -u ${UNIT_TESTS}/$${test}.exp $${test}.out \ 172 diff -u ${UNIT_TESTS}/$${test}.exp $${test}.out \
172 || failed="$${failed}$${failed:+ }$${test}" ; \ 173 || failed="$${failed}$${failed:+ }$${test}" ; \
173 done ; \ 174 done ; \
174 if [ -n "$${failed}" ]; then \ 175 if [ -n "$${failed}" ]; then \
175 echo "Failed tests: $${failed}" ; false ; \ 176 echo "Failed tests: $${failed}" ; false ; \
176 else \ 177 else \
177 echo "All tests passed" ; \ 178 echo "All tests passed" ; \
178 fi 179 fi
179 180
180accept: 181accept:
181 @for test in ${TESTS}; do \ 182 @for test in ${TESTS}; do \
182 cmp -s ${UNIT_TESTS}/$${test}.exp $${test}.out \ 183 cmp -s ${UNIT_TESTS}/$${test}.exp $${test}.out \
183 || { echo "Replacing $${test}.exp" ; \ 184 || { echo "Replacing $${test}.exp" ; \
184 cp $${test}.out ${UNIT_TESTS}/$${test}.exp ; } \ 185 cp $${test}.out ${UNIT_TESTS}/$${test}.exp ; } \
185 done 186 done
186 187
187.if exists(${TEST_MAKE}) 188.if exists(${TEST_MAKE})
188${TESTS:=.rawout}: ${TEST_MAKE} 189${TESTS:=.rawout}: ${TEST_MAKE}
189.endif 190.endif
190 191
191.-include <bsd.obj.mk> 192.-include <bsd.obj.mk>

cvs diff -r1.7 -r1.8 src/usr.bin/make/unit-tests/moderrs.exp (switch to unified diff)

--- src/usr.bin/make/unit-tests/moderrs.exp 2020/07/29 18:48:47 1.7
+++ src/usr.bin/make/unit-tests/moderrs.exp 2020/07/29 19:48:33 1.8
@@ -1,65 +1,137 @@ @@ -1,65 +1,137 @@
1Expect: Unknown modifier 'Z' 1Expect: Unknown modifier 'Z'
2make: Unknown modifier 'Z' 2make: Unknown modifier 'Z'
3VAR:Z= 3VAR:Z=
4Expect: Unknown modifier 'Z' 4Expect: Unknown modifier 'Z'
5make: Unknown modifier 'Z' 5make: Unknown modifier 'Z'
6VAR:Z= 6VAR:Z=
7Expect: Unclosed variable specification for VAR 7Expect: Unclosed variable specification for VAR
8make: Unclosed variable specification (expecting '}') for "VAR" (value "Thevariable") modifier S 8make: Unclosed variable specification (expecting '}') for "VAR" (value "Thevariable") modifier S
9VAR:S,V,v,=Thevariable 9VAR:S,V,v,=Thevariable
10Expect: Unclosed variable specification for VAR 10Expect: Unclosed variable specification for VAR
11make: Unclosed variable specification after complex modifier (expecting '}') for VAR 11make: Unclosed variable specification after complex modifier (expecting '}') for VAR
12VAR:S,V,v,=Thevariable 12VAR:S,V,v,=Thevariable
13Expect: Unclosed substitution for VAR (, missing) 13Expect: Unclosed substitution for VAR (, missing)
14make: Unclosed substitution for VAR (, missing) 14make: Unclosed substitution for VAR (, missing)
15VAR:S,V,v= 15VAR:S,V,v=
16Expect: 2 errors about missing @ delimiter 16Expect: 2 errors about missing @ delimiter
17make: Unclosed substitution for UNDEF (@ missing) 17make: Unclosed substitution for UNDEF (@ missing)
18 18
19make: Unclosed substitution for UNDEF (@ missing) 19make: Unclosed substitution for UNDEF (@ missing)
20 20
211 2 3 211 2 3
22modloop-close: 22modloop-close:
23make: Unclosed variable specification (expecting '}') for "UNDEF" (value "1}... 2}... 3}...") modifier @ 23make: Unclosed variable specification (expecting '}') for "UNDEF" (value "1}... 2}... 3}...") modifier @
241}... 2}... 3}... 241}... 2}... 3}...
251}... 2}... 3}... 251}... 2}... 3}...
26Expect: 2 errors about missing ] delimiter 26Expect: 2 errors about missing ] delimiter
27make: Unclosed substitution for UNDEF (] missing) 27make: Unclosed substitution for UNDEF (] missing)
28 28
29make: Unclosed substitution for UNDEF (] missing) 29make: Unclosed substitution for UNDEF (] missing)
30 30
3113= 3113=
3212345=ok 3212345=ok
33Expect: 2 errors about missing ! delimiter 33Expect: 2 errors about missing ! delimiter
34make: Unclosed substitution for VARNAME (! missing) 34make: Unclosed substitution for VARNAME (! missing)
35 35
36make: Unclosed substitution for ! (! missing) 36make: Unclosed substitution for ! (! missing)
37 37
38mod-subst-delimiter: 38mod-subst-delimiter:
39make: Unclosed substitution for VAR (@ missing) 39make: Unclosed substitution for VAR (@ missing)
40 40
41make: Unclosed substitution for VAR (, missing) 41make: Unclosed substitution for VAR (, missing)
42 42
43make: Unclosed substitution for VAR (, missing) 43make: Unclosed substitution for VAR (, missing)
44 44
45make: Unclosed substitution for VAR (, missing) 45make: Unclosed substitution for VAR (, missing)
46 46
47make: Unclosed substitution for VAR (, missing) 47make: Unclosed substitution for VAR (, missing)
48 48
49make: Unclosed variable specification (expecting '}') for "VAR" (value "TheVariable") modifier S 49make: Unclosed variable specification (expecting '}') for "VAR" (value "TheVariable") modifier S
50TheVariable 50TheVariable
51TheVariable 51TheVariable
52make: Unclosed variable specification (expecting '}') for "VAR" (value "TheVariable") modifier S 52make: Unclosed variable specification (expecting '}') for "VAR" (value "TheVariable") modifier S
531: TheVariable 531: TheVariable
54make: Unclosed substitution for VAR (, missing) 54make: Unclosed substitution for VAR (, missing)
552: 552:
56make: Unclosed substitution for VAR (, missing) 56make: Unclosed substitution for VAR (, missing)
573: 573:
58make: Unclosed substitution for VAR (, missing) 58make: Unclosed substitution for VAR (, missing)
59 59
60make: Unclosed substitution for VAR (, missing) 60make: Unclosed substitution for VAR (, missing)
61 61
62make: Unclosed variable specification (expecting '}') for "VAR" (value "TheVariable") modifier S 62make: Unclosed variable specification (expecting '}') for "VAR" (value "TheVariable") modifier S
63TheVariable 63TheVariable
64TheVariable 64TheVariable
 65mod-regex-delimiter:
 66make: RE substitution error: (details omitted)
 67make: Unclosed substitution for VAR (, missing)
 68echo
 69make: Unclosed substitution for VAR (, missing)
 70
 71make: Unclosed substitution for VAR (, missing)
 72
 73make: Unclosed substitution for VAR (, missing)
 74
 75make: Unclosed substitution for VAR (, missing)
 76
 77make: Unclosed variable specification (expecting '}') for "VAR" (value "TheVariable") modifier C
 78TheVariable
 79TheVariable
 80make: RE substitution error: (details omitted)
 811:
 82make: Unclosed substitution for VAR (, missing)
 832:
 84make: Unclosed substitution for VAR (, missing)
 853:
 86make: Unclosed substitution for VAR (, missing)
 87
 88make: Unclosed substitution for VAR (, missing)
 89
 90make: Unclosed variable specification (expecting '}') for "VAR" (value "TheVariable") modifier C
 91TheVariable
 92TheVariable
 93mod-ts-parse:
 94112358132134
 9515152535558513521534
 96make: Bad modifier `:ts\65oct' for FIB
 9765oct}
 98make: Bad modifier `:tsxy' for FIB
 99xy}
 100mod-t-parse:
 101make: Bad modifier `:txy' for FIB
 102y}
 103make: Bad modifier `:t' for FIB
 104
 105make: Bad modifier `:t' for FIB
 106M*}
 107mod-ifelse-parse:
 108make: Unclosed substitution for FIB (: missing)
 109
 110make: Unclosed substitution for FIB (: missing)
 111
 112make: Unclosed substitution for FIB (} missing)
 113
 114make: Unclosed substitution for FIB (} missing)
 115
 116then
 117mod-assign-parse:
 118make: Unknown modifier ':'
 119
 120make: Unclosed substitution for ASSIGN (} missing)
 121
 122mod-remember-parse:
 1231 1 2 3 5 8 13 21 34
 124make: Unknown modifier '_'
 125
 126mod-sysv-parse:
 127make: Unknown modifier '3'
 128make: Unclosed variable specification (expecting '}') for "FIB" (value "") modifier 3
 129
 130make: Unknown modifier '3'
 131make: Unclosed variable specification (expecting '}') for "FIB" (value "") modifier 3
 132
 133make: Unknown modifier '3'
 134make: Unclosed variable specification (expecting '}') for "FIB" (value "") modifier 3
 135
 1361 1 2 x3 5 8 1x3 21 34
65exit status 0 137exit status 0

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

--- src/usr.bin/make/unit-tests/moderrs.mk 2020/07/29 18:48:47 1.6
+++ src/usr.bin/make/unit-tests/moderrs.mk 2020/07/29 19:48:33 1.7
@@ -1,101 +1,169 @@ @@ -1,101 +1,169 @@
1# $Id: moderrs.mk,v 1.6 2020/07/29 18:48:47 rillig Exp $ 1# $Id: moderrs.mk,v 1.7 2020/07/29 19:48:33 rillig Exp $
2# 2#
3# various modifier error tests 3# various modifier error tests
4 4
5VAR=TheVariable 5VAR=TheVariable
6# incase we have to change it ;-) 6# incase we have to change it ;-)
7MOD_UNKN=Z 7MOD_UNKN=Z
8MOD_TERM=S,V,v 8MOD_TERM=S,V,v
9MOD_S:= ${MOD_TERM}, 9MOD_S:= ${MOD_TERM},
10 10
 11FIB= 1 1 2 3 5 8 13 21 34
 12
11all: modunkn modunknV varterm vartermV modtermV modloop 13all: modunkn modunknV varterm vartermV modtermV modloop
12all: modloop-close 14all: modloop-close
13all: modwords 15all: modwords
14all: modexclam 16all: modexclam
15all: mod-subst-delimiter 17all: mod-subst-delimiter
 18all: mod-regex-delimiter
 19all: mod-ts-parse
 20all: mod-t-parse
 21all: mod-ifelse-parse
 22all: mod-assign-parse
 23all: mod-remember-parse
 24all: mod-sysv-parse
16 25
17modunkn: 26modunkn:
18 @echo "Expect: Unknown modifier 'Z'" 27 @echo "Expect: Unknown modifier 'Z'"
19 @echo "VAR:Z=${VAR:Z}" 28 @echo "VAR:Z=${VAR:Z}"
20 29
21modunknV: 30modunknV:
22 @echo "Expect: Unknown modifier 'Z'" 31 @echo "Expect: Unknown modifier 'Z'"
23 @echo "VAR:${MOD_UNKN}=${VAR:${MOD_UNKN}}" 32 @echo "VAR:${MOD_UNKN}=${VAR:${MOD_UNKN}}"
24 33
25varterm: 34varterm:
26 @echo "Expect: Unclosed variable specification for VAR" 35 @echo "Expect: Unclosed variable specification for VAR"
27 @echo VAR:S,V,v,=${VAR:S,V,v, 36 @echo VAR:S,V,v,=${VAR:S,V,v,
28 37
29vartermV: 38vartermV:
30 @echo "Expect: Unclosed variable specification for VAR" 39 @echo "Expect: Unclosed variable specification for VAR"
31 @echo VAR:${MOD_TERM},=${VAR:${MOD_S} 40 @echo VAR:${MOD_TERM},=${VAR:${MOD_S}
32 41
33modtermV: 42modtermV:
34 @echo "Expect: Unclosed substitution for VAR (, missing)" 43 @echo "Expect: Unclosed substitution for VAR (, missing)"
35 -@echo "VAR:${MOD_TERM}=${VAR:${MOD_TERM}}" 44 -@echo "VAR:${MOD_TERM}=${VAR:${MOD_TERM}}"
36 45
37modloop: 46modloop:
38 @echo "Expect: 2 errors about missing @ delimiter" 47 @echo "Expect: 2 errors about missing @ delimiter"
39 @echo ${UNDEF:U1 2 3:@var} 48 @echo ${UNDEF:U1 2 3:@var}
40 @echo ${UNDEF:U1 2 3:@var@...} 49 @echo ${UNDEF:U1 2 3:@var@...}
41 @echo ${UNDEF:U1 2 3:@var@${var}@} 50 @echo ${UNDEF:U1 2 3:@var@${var}@}
42 51
43# The closing brace after the ${var} is part of the replacement string. 52# The closing brace after the ${var} is part of the replacement string.
44# In ParseModifierPart, braces and parentheses don't have to be balanced. 53# In ParseModifierPart, braces and parentheses don't have to be balanced.
45# This is contrary to the :M, :N modifiers, where both parentheses and 54# This is contrary to the :M, :N modifiers, where both parentheses and
46# braces must be balanced. 55# braces must be balanced.
47# This is also contrary to the SysV modifier, where only the actually 56# This is also contrary to the SysV modifier, where only the actually
48# used delimiter (either braces or parentheses) must be balanced. 57# used delimiter (either braces or parentheses) must be balanced.
49modloop-close: 58modloop-close:
50 @echo $@: 59 @echo $@:
51 @echo ${UNDEF:U1 2 3:@var@${var}}...@ 60 @echo ${UNDEF:U1 2 3:@var@${var}}...@
52 @echo ${UNDEF:U1 2 3:@var@${var}}...@} 61 @echo ${UNDEF:U1 2 3:@var@${var}}...@}
53 62
54modwords: 63modwords:
55 @echo "Expect: 2 errors about missing ] delimiter" 64 @echo "Expect: 2 errors about missing ] delimiter"
56 @echo ${UNDEF:U1 2 3:[} 65 @echo ${UNDEF:U1 2 3:[}
57 @echo ${UNDEF:U1 2 3:[#} 66 @echo ${UNDEF:U1 2 3:[#}
58 67
59 # out of bounds => empty 68 # out of bounds => empty
60 @echo 13=${UNDEF:U1 2 3:[13]} 69 @echo 13=${UNDEF:U1 2 3:[13]}
61 70
62 # Word index out of bounds. 71 # Word index out of bounds.
63 # 72 #
64 # On LP64I32, strtol returns LONG_MAX, 73 # On LP64I32, strtol returns LONG_MAX,
65 # which is then truncated to int (undefined behavior), 74 # which is then truncated to int (undefined behavior),
66 # typically resulting in -1. 75 # typically resulting in -1.
67 # This -1 is interpreted as "the last word". 76 # This -1 is interpreted as "the last word".
68 # 77 #
69 # On ILP32, strtol returns LONG_MAX, 78 # On ILP32, strtol returns LONG_MAX,
70 # which is a large number. 79 # which is a large number.
71 # This results in a range from LONG_MAX - 1 to 3, 80 # This results in a range from LONG_MAX - 1 to 3,
72 # which is empty. 81 # which is empty.
73 @echo 12345=${UNDEF:U1 2 3:[123451234512345123451234512345]:S,^$,ok,:S,^3$,ok,} 82 @echo 12345=${UNDEF:U1 2 3:[123451234512345123451234512345]:S,^$,ok,:S,^3$,ok,}
74 83
75modexclam: 84modexclam:
76 @echo "Expect: 2 errors about missing ! delimiter" 85 @echo "Expect: 2 errors about missing ! delimiter"
77 @echo ${VARNAME:!echo} 86 @echo ${VARNAME:!echo}
78 # When the final exclamation mark is missing, there is no 87 # When the final exclamation mark is missing, there is no
79 # fallback to the SysV substitution modifier. 88 # fallback to the SysV substitution modifier.
80 # If there were a fallback, the output would be "exclam", 89 # If there were a fallback, the output would be "exclam",
81 # and the above would have produced an "Unknown modifier '!'". 90 # and the above would have produced an "Unknown modifier '!'".
82 @echo ${!:L:!=exclam} 91 @echo ${!:L:!=exclam}
83 92
84# XXX: For "${VAR:S", I wonder where the "(@ missing)" comes from. 93# XXX: For "${VAR:S", I wonder where the "(@ missing)" comes from.
85# This could be undefined behavior, but it's reproducible. 94# This could be undefined behavior, but it's reproducible.
86mod-subst-delimiter: 95mod-subst-delimiter:
87 @echo $@: 96 @echo $@:
88 @echo ${VAR:S 97 @echo ${VAR:S
89 @echo ${VAR:S, 98 @echo ${VAR:S,
90 @echo ${VAR:S,from 99 @echo ${VAR:S,from
91 @echo ${VAR:S,from, 100 @echo ${VAR:S,from,
92 @echo ${VAR:S,from,to 101 @echo ${VAR:S,from,to
93 @echo ${VAR:S,from,to, 102 @echo ${VAR:S,from,to,
94 @echo ${VAR:S,from,to,} 103 @echo ${VAR:S,from,to,}
95 @echo 1: ${VAR:S 104 @echo 1: ${VAR:S
96 @echo 2: ${VAR:S, 105 @echo 2: ${VAR:S,
97 @echo 3: ${VAR:S,from 106 @echo 3: ${VAR:S,from
98 @echo ${VAR:S,from, 107 @echo ${VAR:S,from,
99 @echo ${VAR:S,from,to 108 @echo ${VAR:S,from,to
100 @echo ${VAR:S,from,to, 109 @echo ${VAR:S,from,to,
101 @echo ${VAR:S,from,to,} 110 @echo ${VAR:S,from,to,}
 111
 112# XXX: Where does the "echo" in the output of "${VAR:C" come from?
 113mod-regex-delimiter:
 114 @echo $@:
 115 @echo ${VAR:C
 116 @echo ${VAR:C,
 117 @echo ${VAR:C,from
 118 @echo ${VAR:C,from,
 119 @echo ${VAR:C,from,to
 120 @echo ${VAR:C,from,to,
 121 @echo ${VAR:C,from,to,}
 122 @echo 1: ${VAR:C
 123 @echo 2: ${VAR:C,
 124 @echo 3: ${VAR:C,from
 125 @echo ${VAR:C,from,
 126 @echo ${VAR:C,from,to
 127 @echo ${VAR:C,from,to,
 128 @echo ${VAR:C,from,to,}
 129
 130mod-ts-parse:
 131 @echo $@:
 132 @echo ${FIB:ts}
 133 @echo ${FIB:ts\65} # octal 065 == U+0035 == '5'
 134 @echo ${FIB:ts\65oct} # bad modifier
 135 @echo ${FIB:tsxy} # modifier too long
 136
 137mod-t-parse:
 138 @echo $@:
 139 @echo ${FIB:txy}
 140 @echo ${FIB:t}
 141 @echo ${FIB:t:M*}
 142
 143mod-ifelse-parse:
 144 @echo $@:
 145 @echo ${FIB:?
 146 @echo ${FIB:?then
 147 @echo ${FIB:?then:
 148 @echo ${FIB:?then:else
 149 @echo ${FIB:?then:else}
 150
 151mod-assign-parse:
 152 @echo $@:
 153 @echo ${ASSIGN::x} # 'x' is an unknown assignment operator
 154# disabled for now; segfaults on NetBSD-8.0-x86_64 in Var_Parse line 3636:
 155# *lengthPtr = tstr - str + (*tstr ? 1 : 0);
 156# @echo ${::=value} # trying to set the empty variable
 157 @echo ${ASSIGN::=value # missing closing brace
 158
 159mod-remember-parse:
 160 @echo $@:
 161 @echo ${FIB:_} # ok
 162 @echo ${FIB:__} # modifier name too long
 163
 164mod-sysv-parse:
 165 @echo $@:
 166 @echo ${FIB:3
 167 @echo ${FIB:3=
 168 @echo ${FIB:3=x3
 169 @echo ${FIB:3=x3} # ok