Sun Jul 26 12:19:37 2020 UTC ()
make(1): fix bug in :S modifier from 2020-07-19


(rillig)
diff -r1.309 -r1.310 src/usr.bin/make/var.c
diff -r1.24 -r1.25 src/usr.bin/make/unit-tests/modmisc.mk

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

--- src/usr.bin/make/var.c 2020/07/26 10:11:04 1.309
+++ src/usr.bin/make/var.c 2020/07/26 12:19:37 1.310
@@ -1,2345 +1,2345 @@ @@ -1,2345 +1,2345 @@
1/* $NetBSD: var.c,v 1.309 2020/07/26 10:11:04 rillig Exp $ */ 1/* $NetBSD: var.c,v 1.310 2020/07/26 12:19:37 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.309 2020/07/26 10:11:04 rillig Exp $"; 72static char rcsid[] = "$NetBSD: var.c,v 1.310 2020/07/26 12:19:37 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.309 2020/07/26 10:11:04 rillig Exp $"); 79__RCSID("$NetBSD: var.c,v 1.310 2020/07/26 12:19:37 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 value of a variable in a context or 101 * Var_Value Return the value of a variable in a context or
102 * NULL if the variable is undefined. 102 * 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/* 139/*
140 * This lets us tell if we have replaced the original environ 140 * This lets us tell if we have replaced the original environ
141 * (which we cannot free). 141 * (which we cannot free).
142 */ 142 */
143char **savedEnv = NULL; 143char **savedEnv = NULL;
144 144
145/* 145/*
146 * This is a harmless return value for Var_Parse that can be used by Var_Subst 146 * This is a harmless return value for Var_Parse that can be used by Var_Subst
147 * to determine if there was an error in parsing -- easier than returning 147 * to determine if there was an error in parsing -- easier than returning
148 * a flag, as things outside this module don't give a hoot. 148 * a flag, as things outside this module don't give a hoot.
149 */ 149 */
150char var_Error[] = ""; 150char var_Error[] = "";
151 151
152/* 152/*
153 * Similar to var_Error, but returned when the 'VARE_UNDEFERR' flag for 153 * Similar to var_Error, but returned when the 'VARE_UNDEFERR' flag for
154 * Var_Parse is not set. Why not just use a constant? Well, GCC likes 154 * Var_Parse is not set. Why not just use a constant? Well, GCC likes
155 * to condense identical string instances... 155 * to condense identical string instances...
156 */ 156 */
157static char varNoError[] = ""; 157static char varNoError[] = "";
158 158
159/* 159/*
160 * Traditionally we consume $$ during := like any other expansion. 160 * Traditionally we consume $$ during := like any other expansion.
161 * Other make's do not. 161 * Other make's do not.
162 * This knob allows controlling the behavior. 162 * This knob allows controlling the behavior.
163 * FALSE to consume $$ during := assignment. 163 * FALSE to consume $$ during := assignment.
164 * TRUE to preserve $$ during := assignment. 164 * TRUE to preserve $$ during := assignment.
165 */ 165 */
166#define SAVE_DOLLARS ".MAKE.SAVE_DOLLARS" 166#define SAVE_DOLLARS ".MAKE.SAVE_DOLLARS"
167static Boolean save_dollars = TRUE; 167static Boolean save_dollars = TRUE;
168 168
169/* 169/*
170 * Internally, variables are contained in four different contexts. 170 * Internally, variables are contained in four different contexts.
171 * 1) the environment. They cannot be changed. If an environment 171 * 1) the environment. They cannot be changed. If an environment
172 * variable is appended to, the result is placed in the global 172 * variable is appended to, the result is placed in the global
173 * context. 173 * context.
174 * 2) the global context. Variables set in the Makefile are located in 174 * 2) the global context. Variables set in the Makefile are located in
175 * the global context. 175 * the global context.
176 * 3) the command-line context. All variables set on the command line 176 * 3) the command-line context. All variables set on the command line
177 * are placed in this context. They are UNALTERABLE once placed here. 177 * are placed in this context. They are UNALTERABLE once placed here.
178 * 4) the local context. Each target has associated with it a context 178 * 4) the local context. Each target has associated with it a context
179 * list. On this list are located the structures describing such 179 * list. On this list are located the structures describing such
180 * local variables as $(@) and $(*) 180 * local variables as $(@) and $(*)
181 * The four contexts are searched in the reverse order from which they are 181 * The four contexts are searched in the reverse order from which they are
182 * listed (but see checkEnvFirst). 182 * listed (but see checkEnvFirst).
183 */ 183 */
184GNode *VAR_INTERNAL; /* variables from make itself */ 184GNode *VAR_INTERNAL; /* variables from make itself */
185GNode *VAR_GLOBAL; /* variables from the makefile */ 185GNode *VAR_GLOBAL; /* variables from the makefile */
186GNode *VAR_CMD; /* variables defined on the command-line */ 186GNode *VAR_CMD; /* variables defined on the command-line */
187 187
188typedef enum { 188typedef enum {
189 FIND_CMD = 0x01, /* look in VAR_CMD when searching */ 189 FIND_CMD = 0x01, /* look in VAR_CMD when searching */
190 FIND_GLOBAL = 0x02, /* look in VAR_GLOBAL as well */ 190 FIND_GLOBAL = 0x02, /* look in VAR_GLOBAL as well */
191 FIND_ENV = 0x04 /* look in the environment also */ 191 FIND_ENV = 0x04 /* look in the environment also */
192} VarFindFlags; 192} VarFindFlags;
193 193
194typedef enum { 194typedef enum {
195 VAR_IN_USE = 0x01, /* Variable's value is currently being used 195 VAR_IN_USE = 0x01, /* Variable's value is currently being used
196 * by Var_Parse or Var_Subst. 196 * by Var_Parse or Var_Subst.
197 * Used to avoid endless recursion */ 197 * Used to avoid endless recursion */
198 VAR_FROM_ENV = 0x02, /* Variable comes from the environment */ 198 VAR_FROM_ENV = 0x02, /* Variable comes from the environment */
199 VAR_JUNK = 0x04, /* Variable is a junk variable that 199 VAR_JUNK = 0x04, /* Variable is a junk variable that
200 * should be destroyed when done with 200 * should be destroyed when done with
201 * it. Used by Var_Parse for undefined, 201 * it. Used by Var_Parse for undefined,
202 * modified variables */ 202 * modified variables */
203 VAR_KEEP = 0x08, /* Variable is VAR_JUNK, but we found 203 VAR_KEEP = 0x08, /* Variable is VAR_JUNK, but we found
204 * a use for it in some modifier and 204 * a use for it in some modifier and
205 * the value is therefore valid */ 205 * the value is therefore valid */
206 VAR_EXPORTED = 0x10, /* Variable is exported */ 206 VAR_EXPORTED = 0x10, /* Variable is exported */
207 VAR_REEXPORT = 0x20, /* Indicate if var needs re-export. 207 VAR_REEXPORT = 0x20, /* Indicate if var needs re-export.
208 * This would be true if it contains $'s */ 208 * This would be true if it contains $'s */
209 VAR_FROM_CMD = 0x40 /* Variable came from command line */ 209 VAR_FROM_CMD = 0x40 /* Variable came from command line */
210} Var_Flags; 210} Var_Flags;
211 211
212typedef struct Var { 212typedef struct Var {
213 char *name; /* the variable's name */ 213 char *name; /* the variable's name */
214 Buffer val; /* its value */ 214 Buffer val; /* its value */
215 Var_Flags flags; /* miscellaneous status flags */ 215 Var_Flags flags; /* miscellaneous status flags */
216} Var; 216} Var;
217 217
218/* 218/*
219 * Exporting vars is expensive so skip it if we can 219 * Exporting vars is expensive so skip it if we can
220 */ 220 */
221typedef enum { 221typedef enum {
222 VAR_EXPORTED_NONE, 222 VAR_EXPORTED_NONE,
223 VAR_EXPORTED_YES, 223 VAR_EXPORTED_YES,
224 VAR_EXPORTED_ALL 224 VAR_EXPORTED_ALL
225} VarExportedMode; 225} VarExportedMode;
226static VarExportedMode var_exportedVars = VAR_EXPORTED_NONE; 226static VarExportedMode var_exportedVars = VAR_EXPORTED_NONE;
227 227
228typedef enum { 228typedef enum {
229 /* 229 /*
230 * We pass this to Var_Export when doing the initial export 230 * We pass this to Var_Export when doing the initial export
231 * or after updating an exported var. 231 * or after updating an exported var.
232 */ 232 */
233 VAR_EXPORT_PARENT = 0x01, 233 VAR_EXPORT_PARENT = 0x01,
234 /* 234 /*
235 * We pass this to Var_Export1 to tell it to leave the value alone. 235 * We pass this to Var_Export1 to tell it to leave the value alone.
236 */ 236 */
237 VAR_EXPORT_LITERAL = 0x02 237 VAR_EXPORT_LITERAL = 0x02
238} VarExportFlags; 238} VarExportFlags;
239 239
240/* Flags for pattern matching in the :S and :C modifiers */ 240/* Flags for pattern matching in the :S and :C modifiers */
241typedef enum { 241typedef enum {
242 VARP_SUB_GLOBAL = 0x01, /* Apply substitution globally */ 242 VARP_SUB_GLOBAL = 0x01, /* Apply substitution globally */
243 VARP_SUB_ONE = 0x02, /* Apply substitution to one word */ 243 VARP_SUB_ONE = 0x02, /* Apply substitution to one word */
244 VARP_SUB_MATCHED = 0x04, /* There was a match */ 244 VARP_SUB_MATCHED = 0x04, /* There was a match */
245 VARP_ANCHOR_START = 0x08, /* Match at start of word */ 245 VARP_ANCHOR_START = 0x08, /* Match at start of word */
246 VARP_ANCHOR_END = 0x10 /* Match at end of word */ 246 VARP_ANCHOR_END = 0x10 /* Match at end of word */
247} VarPatternFlags; 247} VarPatternFlags;
248 248
249typedef enum { 249typedef enum {
250 VAR_NO_EXPORT = 0x01 /* do not export */ 250 VAR_NO_EXPORT = 0x01 /* do not export */
251} VarSet_Flags; 251} VarSet_Flags;
252 252
253#define BROPEN '{' 253#define BROPEN '{'
254#define BRCLOSE '}' 254#define BRCLOSE '}'
255#define PROPEN '(' 255#define PROPEN '('
256#define PRCLOSE ')' 256#define PRCLOSE ')'
257 257
258/*- 258/*-
259 *----------------------------------------------------------------------- 259 *-----------------------------------------------------------------------
260 * VarFind -- 260 * VarFind --
261 * Find the given variable in the given context and any other contexts 261 * Find the given variable in the given context and any other contexts
262 * indicated. 262 * indicated.
263 * 263 *
264 * Input: 264 * Input:
265 * name name to find 265 * name name to find
266 * ctxt context in which to find it 266 * ctxt context in which to find it
267 * flags FIND_GLOBAL look in VAR_GLOBAL as well 267 * flags FIND_GLOBAL look in VAR_GLOBAL as well
268 * FIND_CMD look in VAR_CMD as well 268 * FIND_CMD look in VAR_CMD as well
269 * FIND_ENV look in the environment as well 269 * FIND_ENV look in the environment as well
270 * 270 *
271 * Results: 271 * Results:
272 * A pointer to the structure describing the desired variable or 272 * A pointer to the structure describing the desired variable or
273 * NULL if the variable does not exist. 273 * NULL if the variable does not exist.
274 * 274 *
275 * Side Effects: 275 * Side Effects:
276 * None 276 * None
277 *----------------------------------------------------------------------- 277 *-----------------------------------------------------------------------
278 */ 278 */
279static Var * 279static Var *
280VarFind(const char *name, GNode *ctxt, VarFindFlags flags) 280VarFind(const char *name, GNode *ctxt, VarFindFlags flags)
281{ 281{
282 Hash_Entry *var; 282 Hash_Entry *var;
283 Var *v; 283 Var *v;
284 284
285 /* 285 /*
286 * If the variable name begins with a '.', it could very well be one of 286 * If the variable name begins with a '.', it could very well be one of
287 * the local ones. We check the name against all the local variables 287 * the local ones. We check the name against all the local variables
288 * and substitute the short version in for 'name' if it matches one of 288 * and substitute the short version in for 'name' if it matches one of
289 * them. 289 * them.
290 */ 290 */
291 if (*name == '.' && isupper((unsigned char) name[1])) { 291 if (*name == '.' && isupper((unsigned char) name[1])) {
292 switch (name[1]) { 292 switch (name[1]) {
293 case 'A': 293 case 'A':
294 if (strcmp(name, ".ALLSRC") == 0) 294 if (strcmp(name, ".ALLSRC") == 0)
295 name = ALLSRC; 295 name = ALLSRC;
296 if (strcmp(name, ".ARCHIVE") == 0) 296 if (strcmp(name, ".ARCHIVE") == 0)
297 name = ARCHIVE; 297 name = ARCHIVE;
298 break; 298 break;
299 case 'I': 299 case 'I':
300 if (strcmp(name, ".IMPSRC") == 0) 300 if (strcmp(name, ".IMPSRC") == 0)
301 name = IMPSRC; 301 name = IMPSRC;
302 break; 302 break;
303 case 'M': 303 case 'M':
304 if (strcmp(name, ".MEMBER") == 0) 304 if (strcmp(name, ".MEMBER") == 0)
305 name = MEMBER; 305 name = MEMBER;
306 break; 306 break;
307 case 'O': 307 case 'O':
308 if (strcmp(name, ".OODATE") == 0) 308 if (strcmp(name, ".OODATE") == 0)
309 name = OODATE; 309 name = OODATE;
310 break; 310 break;
311 case 'P': 311 case 'P':
312 if (strcmp(name, ".PREFIX") == 0) 312 if (strcmp(name, ".PREFIX") == 0)
313 name = PREFIX; 313 name = PREFIX;
314 break; 314 break;
315 case 'T': 315 case 'T':
316 if (strcmp(name, ".TARGET") == 0) 316 if (strcmp(name, ".TARGET") == 0)
317 name = TARGET; 317 name = TARGET;
318 break; 318 break;
319 } 319 }
320 } 320 }
321 321
322#ifdef notyet 322#ifdef notyet
323 /* for compatibility with gmake */ 323 /* for compatibility with gmake */
324 if (name[0] == '^' && name[1] == '\0') 324 if (name[0] == '^' && name[1] == '\0')
325 name = ALLSRC; 325 name = ALLSRC;
326#endif 326#endif
327 327
328 /* 328 /*
329 * First look for the variable in the given context. If it's not there, 329 * First look for the variable in the given context. If it's not there,
330 * look for it in VAR_CMD, VAR_GLOBAL and the environment, in that order, 330 * look for it in VAR_CMD, VAR_GLOBAL and the environment, in that order,
331 * depending on the FIND_* flags in 'flags' 331 * depending on the FIND_* flags in 'flags'
332 */ 332 */
333 var = Hash_FindEntry(&ctxt->context, name); 333 var = Hash_FindEntry(&ctxt->context, name);
334 334
335 if (var == NULL && (flags & FIND_CMD) && ctxt != VAR_CMD) { 335 if (var == NULL && (flags & FIND_CMD) && ctxt != VAR_CMD) {
336 var = Hash_FindEntry(&VAR_CMD->context, name); 336 var = Hash_FindEntry(&VAR_CMD->context, name);
337 } 337 }
338 if (!checkEnvFirst && var == NULL && (flags & FIND_GLOBAL) && 338 if (!checkEnvFirst && var == NULL && (flags & FIND_GLOBAL) &&
339 ctxt != VAR_GLOBAL) 339 ctxt != VAR_GLOBAL)
340 { 340 {
341 var = Hash_FindEntry(&VAR_GLOBAL->context, name); 341 var = Hash_FindEntry(&VAR_GLOBAL->context, name);
342 if (var == NULL && ctxt != VAR_INTERNAL) { 342 if (var == NULL && ctxt != VAR_INTERNAL) {
343 /* VAR_INTERNAL is subordinate to VAR_GLOBAL */ 343 /* VAR_INTERNAL is subordinate to VAR_GLOBAL */
344 var = Hash_FindEntry(&VAR_INTERNAL->context, name); 344 var = Hash_FindEntry(&VAR_INTERNAL->context, name);
345 } 345 }
346 } 346 }
347 if (var == NULL && (flags & FIND_ENV)) { 347 if (var == NULL && (flags & FIND_ENV)) {
348 char *env; 348 char *env;
349 349
350 if ((env = getenv(name)) != NULL) { 350 if ((env = getenv(name)) != NULL) {
351 int len; 351 int len;
352 352
353 v = bmake_malloc(sizeof(Var)); 353 v = bmake_malloc(sizeof(Var));
354 v->name = bmake_strdup(name); 354 v->name = bmake_strdup(name);
355 355
356 len = strlen(env); 356 len = strlen(env);
357 357
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; 431 Var *v;
432 int len; 432 int len;
433 Hash_Entry *h; 433 Hash_Entry *h;
434 434
435 v = bmake_malloc(sizeof(Var)); 435 v = bmake_malloc(sizeof(Var));
436 436
437 len = val != NULL ? strlen(val) : 0; 437 len = val != NULL ? strlen(val) : 0;
438 Buf_Init(&v->val, len + 1); 438 Buf_Init(&v->val, len + 1);
439 Buf_AddBytes(&v->val, len, val); 439 Buf_AddBytes(&v->val, len, val);
440 440
441 v->flags = 0; 441 v->flags = 0;
442 442
443 h = Hash_CreateEntry(&ctxt->context, name, NULL); 443 h = Hash_CreateEntry(&ctxt->context, name, NULL);
444 Hash_SetValue(h, v); 444 Hash_SetValue(h, v);
445 v->name = h->name; 445 v->name = h->name;
446 if (DEBUG(VAR) && !(ctxt->flags & INTERNAL)) { 446 if (DEBUG(VAR) && !(ctxt->flags & INTERNAL)) {
447 fprintf(debug_file, "%s:%s = %s\n", ctxt->name, name, val); 447 fprintf(debug_file, "%s:%s = %s\n", ctxt->name, name, val);
448 } 448 }
449} 449}
450 450
451/*- 451/*-
452 *----------------------------------------------------------------------- 452 *-----------------------------------------------------------------------
453 * Var_Delete -- 453 * Var_Delete --
454 * Remove a variable from a context. 454 * Remove a variable from a context.
455 * 455 *
456 * Side Effects: 456 * Side Effects:
457 * The Var structure is removed and freed. 457 * The Var structure is removed and freed.
458 * 458 *
459 *----------------------------------------------------------------------- 459 *-----------------------------------------------------------------------
460 */ 460 */
461void 461void
462Var_Delete(const char *name, GNode *ctxt) 462Var_Delete(const char *name, GNode *ctxt)
463{ 463{
464 Hash_Entry *ln; 464 Hash_Entry *ln;
465 char *cp; 465 char *cp;
466 466
467 if (strchr(name, '$') != NULL) { 467 if (strchr(name, '$') != NULL) {
468 cp = Var_Subst(NULL, name, VAR_GLOBAL, VARE_WANTRES); 468 cp = Var_Subst(NULL, name, VAR_GLOBAL, VARE_WANTRES);
469 } else { 469 } else {
470 cp = UNCONST(name); 470 cp = UNCONST(name);
471 } 471 }
472 ln = Hash_FindEntry(&ctxt->context, cp); 472 ln = Hash_FindEntry(&ctxt->context, cp);
473 if (DEBUG(VAR)) { 473 if (DEBUG(VAR)) {
474 fprintf(debug_file, "%s:delete %s%s\n", 474 fprintf(debug_file, "%s:delete %s%s\n",
475 ctxt->name, cp, ln ? "" : " (not found)"); 475 ctxt->name, cp, ln ? "" : " (not found)");
476 } 476 }
477 if (cp != name) 477 if (cp != name)
478 free(cp); 478 free(cp);
479 if (ln != NULL) { 479 if (ln != NULL) {
480 Var *v = (Var *)Hash_GetValue(ln); 480 Var *v = (Var *)Hash_GetValue(ln);
481 if (v->flags & VAR_EXPORTED) 481 if (v->flags & VAR_EXPORTED)
482 unsetenv(v->name); 482 unsetenv(v->name);
483 if (strcmp(MAKE_EXPORTED, v->name) == 0) 483 if (strcmp(MAKE_EXPORTED, v->name) == 0)
484 var_exportedVars = VAR_EXPORTED_NONE; 484 var_exportedVars = VAR_EXPORTED_NONE;
485 if (v->name != ln->name) 485 if (v->name != ln->name)
486 free(v->name); 486 free(v->name);
487 Hash_DeleteEntry(&ctxt->context, ln); 487 Hash_DeleteEntry(&ctxt->context, ln);
488 Buf_Destroy(&v->val, TRUE); 488 Buf_Destroy(&v->val, TRUE);
489 free(v); 489 free(v);
490 } 490 }
491} 491}
492 492
493 493
494/* 494/*
495 * Export a var. 495 * Export a var.
496 * We ignore make internal variables (those which start with '.') 496 * We ignore make internal variables (those which start with '.')
497 * Also we jump through some hoops to avoid calling setenv 497 * Also we jump through some hoops to avoid calling setenv
498 * more than necessary since it can leak. 498 * more than necessary since it can leak.
499 * We only manipulate flags of vars if 'parent' is set. 499 * We only manipulate flags of vars if 'parent' is set.
500 */ 500 */
501static int 501static int
502Var_Export1(const char *name, VarExportFlags flags) 502Var_Export1(const char *name, VarExportFlags flags)
503{ 503{
504 char tmp[BUFSIZ]; 504 char tmp[BUFSIZ];
505 Var *v; 505 Var *v;
506 char *val = NULL; 506 char *val = NULL;
507 int n; 507 int n;
508 VarExportFlags parent = flags & VAR_EXPORT_PARENT; 508 VarExportFlags parent = flags & VAR_EXPORT_PARENT;
509 509
510 if (*name == '.') 510 if (*name == '.')
511 return 0; /* skip internals */ 511 return 0; /* skip internals */
512 if (!name[1]) { 512 if (!name[1]) {
513 /* 513 /*
514 * A single char. 514 * A single char.
515 * If it is one of the vars that should only appear in 515 * If it is one of the vars that should only appear in
516 * local context, skip it, else we can get Var_Subst 516 * local context, skip it, else we can get Var_Subst
517 * into a loop. 517 * into a loop.
518 */ 518 */
519 switch (name[0]) { 519 switch (name[0]) {
520 case '@': 520 case '@':
521 case '%': 521 case '%':
522 case '*': 522 case '*':
523 case '!': 523 case '!':
524 return 0; 524 return 0;
525 } 525 }
526 } 526 }
527 v = VarFind(name, VAR_GLOBAL, 0); 527 v = VarFind(name, VAR_GLOBAL, 0);
528 if (v == NULL) 528 if (v == NULL)
529 return 0; 529 return 0;
530 if (!parent && 530 if (!parent &&
531 (v->flags & (VAR_EXPORTED | VAR_REEXPORT)) == VAR_EXPORTED) { 531 (v->flags & (VAR_EXPORTED | VAR_REEXPORT)) == VAR_EXPORTED) {
532 return 0; /* nothing to do */ 532 return 0; /* nothing to do */
533 } 533 }
534 val = Buf_GetAll(&v->val, NULL); 534 val = Buf_GetAll(&v->val, NULL);
535 if ((flags & VAR_EXPORT_LITERAL) == 0 && strchr(val, '$')) { 535 if ((flags & VAR_EXPORT_LITERAL) == 0 && strchr(val, '$')) {
536 if (parent) { 536 if (parent) {
537 /* 537 /*
538 * Flag this as something we need to re-export. 538 * Flag this as something we need to re-export.
539 * No point actually exporting it now though, 539 * No point actually exporting it now though,
540 * the child can do it at the last minute. 540 * the child can do it at the last minute.
541 */ 541 */
542 v->flags |= (VAR_EXPORTED | VAR_REEXPORT); 542 v->flags |= (VAR_EXPORTED | VAR_REEXPORT);
543 return 1; 543 return 1;
544 } 544 }
545 if (v->flags & VAR_IN_USE) { 545 if (v->flags & VAR_IN_USE) {
546 /* 546 /*
547 * We recursed while exporting in a child. 547 * We recursed while exporting in a child.
548 * This isn't going to end well, just skip it. 548 * This isn't going to end well, just skip it.
549 */ 549 */
550 return 0; 550 return 0;
551 } 551 }
552 n = snprintf(tmp, sizeof(tmp), "${%s}", name); 552 n = snprintf(tmp, sizeof(tmp), "${%s}", name);
553 if (n < (int)sizeof(tmp)) { 553 if (n < (int)sizeof(tmp)) {
554 val = Var_Subst(NULL, tmp, VAR_GLOBAL, VARE_WANTRES); 554 val = Var_Subst(NULL, tmp, VAR_GLOBAL, VARE_WANTRES);
555 setenv(name, val, 1); 555 setenv(name, val, 1);
556 free(val); 556 free(val);
557 } 557 }
558 } else { 558 } else {
559 if (parent) 559 if (parent)
560 v->flags &= ~VAR_REEXPORT; /* once will do */ 560 v->flags &= ~VAR_REEXPORT; /* once will do */
561 if (parent || !(v->flags & VAR_EXPORTED)) 561 if (parent || !(v->flags & VAR_EXPORTED))
562 setenv(name, val, 1); 562 setenv(name, val, 1);
563 } 563 }
564 /* 564 /*
565 * This is so Var_Set knows to call Var_Export again... 565 * This is so Var_Set knows to call Var_Export again...
566 */ 566 */
567 if (parent) { 567 if (parent) {
568 v->flags |= VAR_EXPORTED; 568 v->flags |= VAR_EXPORTED;
569 } 569 }
570 return 1; 570 return 1;
571} 571}
572 572
573static void 573static void
574Var_ExportVars_callback(void *entry, void *unused MAKE_ATTR_UNUSED) 574Var_ExportVars_callback(void *entry, void *unused MAKE_ATTR_UNUSED)
575{ 575{
576 Var *var = entry; 576 Var *var = entry;
577 Var_Export1(var->name, 0); 577 Var_Export1(var->name, 0);
578} 578}
579 579
580/* 580/*
581 * This gets called from our children. 581 * This gets called from our children.
582 */ 582 */
583void 583void
584Var_ExportVars(void) 584Var_ExportVars(void)
585{ 585{
586 char tmp[BUFSIZ]; 586 char tmp[BUFSIZ];
587 char *val; 587 char *val;
588 int n; 588 int n;
589 589
590 /* 590 /*
591 * Several make's support this sort of mechanism for tracking 591 * Several make's support this sort of mechanism for tracking
592 * recursion - but each uses a different name. 592 * recursion - but each uses a different name.
593 * We allow the makefiles to update MAKELEVEL and ensure 593 * We allow the makefiles to update MAKELEVEL and ensure
594 * children see a correctly incremented value. 594 * children see a correctly incremented value.
595 */ 595 */
596 snprintf(tmp, sizeof(tmp), "%d", makelevel + 1); 596 snprintf(tmp, sizeof(tmp), "%d", makelevel + 1);
597 setenv(MAKE_LEVEL_ENV, tmp, 1); 597 setenv(MAKE_LEVEL_ENV, tmp, 1);
598 598
599 if (VAR_EXPORTED_NONE == var_exportedVars) 599 if (VAR_EXPORTED_NONE == var_exportedVars)
600 return; 600 return;
601 601
602 if (VAR_EXPORTED_ALL == var_exportedVars) { 602 if (VAR_EXPORTED_ALL == var_exportedVars) {
603 /* Ouch! This is crazy... */ 603 /* Ouch! This is crazy... */
604 Hash_ForEach(&VAR_GLOBAL->context, Var_ExportVars_callback, NULL); 604 Hash_ForEach(&VAR_GLOBAL->context, Var_ExportVars_callback, NULL);
605 return; 605 return;
606 } 606 }
607 /* 607 /*
608 * We have a number of exported vars, 608 * We have a number of exported vars,
609 */ 609 */
610 n = snprintf(tmp, sizeof(tmp), "${" MAKE_EXPORTED ":O:u}"); 610 n = snprintf(tmp, sizeof(tmp), "${" MAKE_EXPORTED ":O:u}");
611 if (n < (int)sizeof(tmp)) { 611 if (n < (int)sizeof(tmp)) {
612 char **av; 612 char **av;
613 char *as; 613 char *as;
614 int ac; 614 int ac;
615 int i; 615 int i;
616 616
617 val = Var_Subst(NULL, tmp, VAR_GLOBAL, VARE_WANTRES); 617 val = Var_Subst(NULL, tmp, VAR_GLOBAL, VARE_WANTRES);
618 if (*val) { 618 if (*val) {
619 av = brk_string(val, &ac, FALSE, &as); 619 av = brk_string(val, &ac, FALSE, &as);
620 for (i = 0; i < ac; i++) 620 for (i = 0; i < ac; i++)
621 Var_Export1(av[i], 0); 621 Var_Export1(av[i], 0);
622 free(as); 622 free(as);
623 free(av); 623 free(av);
624 } 624 }
625 free(val); 625 free(val);
626 } 626 }
627} 627}
628 628
629/* 629/*
630 * This is called when .export is seen or 630 * This is called when .export is seen or
631 * .MAKE.EXPORTED is modified. 631 * .MAKE.EXPORTED is modified.
632 * It is also called when any exported var is modified. 632 * It is also called when any exported var is modified.
633 */ 633 */
634void 634void
635Var_Export(char *str, int isExport) 635Var_Export(char *str, int isExport)
636{ 636{
637 char **av; 637 char **av;
638 char *as; 638 char *as;
639 VarExportFlags flags; 639 VarExportFlags flags;
640 int ac; 640 int ac;
641 int i; 641 int i;
642 642
643 if (isExport && (!str || !str[0])) { 643 if (isExport && (!str || !str[0])) {
644 var_exportedVars = VAR_EXPORTED_ALL; /* use with caution! */ 644 var_exportedVars = VAR_EXPORTED_ALL; /* use with caution! */
645 return; 645 return;
646 } 646 }
647 647
648 flags = 0; 648 flags = 0;
649 if (strncmp(str, "-env", 4) == 0) { 649 if (strncmp(str, "-env", 4) == 0) {
650 str += 4; 650 str += 4;
651 } else if (strncmp(str, "-literal", 8) == 0) { 651 } else if (strncmp(str, "-literal", 8) == 0) {
652 str += 8; 652 str += 8;
653 flags |= VAR_EXPORT_LITERAL; 653 flags |= VAR_EXPORT_LITERAL;
654 } else { 654 } else {
655 flags |= VAR_EXPORT_PARENT; 655 flags |= VAR_EXPORT_PARENT;
656 } 656 }
657 657
658 char *val = Var_Subst(NULL, str, VAR_GLOBAL, VARE_WANTRES); 658 char *val = Var_Subst(NULL, str, VAR_GLOBAL, VARE_WANTRES);
659 if (*val) { 659 if (*val) {
660 av = brk_string(val, &ac, FALSE, &as); 660 av = brk_string(val, &ac, FALSE, &as);
661 for (i = 0; i < ac; i++) { 661 for (i = 0; i < ac; i++) {
662 const char *name = av[i]; 662 const char *name = av[i];
663 if (!name[1]) { 663 if (!name[1]) {
664 /* 664 /*
665 * A single char. 665 * A single char.
666 * If it is one of the vars that should only appear in 666 * If it is one of the vars that should only appear in
667 * local context, skip it, else we can get Var_Subst 667 * local context, skip it, else we can get Var_Subst
668 * into a loop. 668 * into a loop.
669 */ 669 */
670 switch (name[0]) { 670 switch (name[0]) {
671 case '@': 671 case '@':
672 case '%': 672 case '%':
673 case '*': 673 case '*':
674 case '!': 674 case '!':
675 continue; 675 continue;
676 } 676 }
677 } 677 }
678 if (Var_Export1(name, flags)) { 678 if (Var_Export1(name, flags)) {
679 if (VAR_EXPORTED_ALL != var_exportedVars) 679 if (VAR_EXPORTED_ALL != var_exportedVars)
680 var_exportedVars = VAR_EXPORTED_YES; 680 var_exportedVars = VAR_EXPORTED_YES;
681 if (isExport && (flags & VAR_EXPORT_PARENT)) { 681 if (isExport && (flags & VAR_EXPORT_PARENT)) {
682 Var_Append(MAKE_EXPORTED, name, VAR_GLOBAL); 682 Var_Append(MAKE_EXPORTED, name, VAR_GLOBAL);
683 } 683 }
684 } 684 }
685 } 685 }
686 free(as); 686 free(as);
687 free(av); 687 free(av);
688 } 688 }
689 free(val); 689 free(val);
690} 690}
691 691
692 692
693extern char **environ; 693extern char **environ;
694 694
695/* 695/*
696 * This is called when .unexport[-env] is seen. 696 * This is called when .unexport[-env] is seen.
697 */ 697 */
698void 698void
699Var_UnExport(char *str) 699Var_UnExport(char *str)
700{ 700{
701 char tmp[BUFSIZ]; 701 char tmp[BUFSIZ];
702 char *vlist; 702 char *vlist;
703 char *cp; 703 char *cp;
704 Boolean unexport_env; 704 Boolean unexport_env;
705 int n; 705 int n;
706 706
707 if (str == NULL || str[0] == '\0') 707 if (str == NULL || str[0] == '\0')
708 return; /* assert? */ 708 return; /* assert? */
709 709
710 vlist = NULL; 710 vlist = NULL;
711 711
712 str += 8; 712 str += 8;
713 unexport_env = (strncmp(str, "-env", 4) == 0); 713 unexport_env = (strncmp(str, "-env", 4) == 0);
714 if (unexport_env) { 714 if (unexport_env) {
715 char **newenv; 715 char **newenv;
716 716
717 cp = getenv(MAKE_LEVEL_ENV); /* we should preserve this */ 717 cp = getenv(MAKE_LEVEL_ENV); /* we should preserve this */
718 if (environ == savedEnv) { 718 if (environ == savedEnv) {
719 /* we have been here before! */ 719 /* we have been here before! */
720 newenv = bmake_realloc(environ, 2 * sizeof(char *)); 720 newenv = bmake_realloc(environ, 2 * sizeof(char *));
721 } else { 721 } else {
722 if (savedEnv) { 722 if (savedEnv) {
723 free(savedEnv); 723 free(savedEnv);
724 savedEnv = NULL; 724 savedEnv = NULL;
725 } 725 }
726 newenv = bmake_malloc(2 * sizeof(char *)); 726 newenv = bmake_malloc(2 * sizeof(char *));
727 } 727 }
728 if (!newenv) 728 if (!newenv)
729 return; 729 return;
730 /* Note: we cannot safely free() the original environ. */ 730 /* Note: we cannot safely free() the original environ. */
731 environ = savedEnv = newenv; 731 environ = savedEnv = newenv;
732 newenv[0] = NULL; 732 newenv[0] = NULL;
733 newenv[1] = NULL; 733 newenv[1] = NULL;
734 if (cp && *cp) 734 if (cp && *cp)
735 setenv(MAKE_LEVEL_ENV, cp, 1); 735 setenv(MAKE_LEVEL_ENV, cp, 1);
736 } else { 736 } else {
737 for (; *str != '\n' && isspace((unsigned char) *str); str++) 737 for (; *str != '\n' && isspace((unsigned char) *str); str++)
738 continue; 738 continue;
739 if (str[0] && str[0] != '\n') { 739 if (str[0] && str[0] != '\n') {
740 vlist = str; 740 vlist = str;
741 } 741 }
742 } 742 }
743 743
744 if (!vlist) { 744 if (!vlist) {
745 /* Using .MAKE.EXPORTED */ 745 /* Using .MAKE.EXPORTED */
746 vlist = Var_Subst(NULL, "${" MAKE_EXPORTED ":O:u}", VAR_GLOBAL, 746 vlist = Var_Subst(NULL, "${" MAKE_EXPORTED ":O:u}", VAR_GLOBAL,
747 VARE_WANTRES); 747 VARE_WANTRES);
748 } 748 }
749 if (vlist) { 749 if (vlist) {
750 Var *v; 750 Var *v;
751 char **av; 751 char **av;
752 char *as; 752 char *as;
753 int ac; 753 int ac;
754 int i; 754 int i;
755 755
756 av = brk_string(vlist, &ac, FALSE, &as); 756 av = brk_string(vlist, &ac, FALSE, &as);
757 for (i = 0; i < ac; i++) { 757 for (i = 0; i < ac; i++) {
758 v = VarFind(av[i], VAR_GLOBAL, 0); 758 v = VarFind(av[i], VAR_GLOBAL, 0);
759 if (!v) 759 if (!v)
760 continue; 760 continue;
761 if (!unexport_env && 761 if (!unexport_env &&
762 (v->flags & (VAR_EXPORTED | VAR_REEXPORT)) == VAR_EXPORTED) 762 (v->flags & (VAR_EXPORTED | VAR_REEXPORT)) == VAR_EXPORTED)
763 unsetenv(v->name); 763 unsetenv(v->name);
764 v->flags &= ~(VAR_EXPORTED | VAR_REEXPORT); 764 v->flags &= ~(VAR_EXPORTED | VAR_REEXPORT);
765 /* 765 /*
766 * If we are unexporting a list, 766 * If we are unexporting a list,
767 * remove each one from .MAKE.EXPORTED. 767 * remove each one from .MAKE.EXPORTED.
768 * If we are removing them all, 768 * If we are removing them all,
769 * just delete .MAKE.EXPORTED below. 769 * just delete .MAKE.EXPORTED below.
770 */ 770 */
771 if (vlist == str) { 771 if (vlist == str) {
772 n = snprintf(tmp, sizeof(tmp), 772 n = snprintf(tmp, sizeof(tmp),
773 "${" MAKE_EXPORTED ":N%s}", v->name); 773 "${" MAKE_EXPORTED ":N%s}", v->name);
774 if (n < (int)sizeof(tmp)) { 774 if (n < (int)sizeof(tmp)) {
775 cp = Var_Subst(NULL, tmp, VAR_GLOBAL, VARE_WANTRES); 775 cp = Var_Subst(NULL, tmp, VAR_GLOBAL, VARE_WANTRES);
776 Var_Set(MAKE_EXPORTED, cp, VAR_GLOBAL); 776 Var_Set(MAKE_EXPORTED, cp, VAR_GLOBAL);
777 free(cp); 777 free(cp);
778 } 778 }
779 } 779 }
780 } 780 }
781 free(as); 781 free(as);
782 free(av); 782 free(av);
783 if (vlist != str) { 783 if (vlist != str) {
784 Var_Delete(MAKE_EXPORTED, VAR_GLOBAL); 784 Var_Delete(MAKE_EXPORTED, VAR_GLOBAL);
785 free(vlist); 785 free(vlist);
786 } 786 }
787 } 787 }
788} 788}
789 789
790static void 790static void
791Var_Set_with_flags(const char *name, const char *val, GNode *ctxt, 791Var_Set_with_flags(const char *name, const char *val, GNode *ctxt,
792 VarSet_Flags flags) 792 VarSet_Flags flags)
793{ 793{
794 Var *v; 794 Var *v;
795 char *expanded_name = NULL; 795 char *expanded_name = NULL;
796 796
797 /* 797 /*
798 * We only look for a variable in the given context since anything set 798 * We only look for a variable in the given context since anything set
799 * here will override anything in a lower context, so there's not much 799 * here will override anything in a lower context, so there's not much
800 * point in searching them all just to save a bit of memory... 800 * point in searching them all just to save a bit of memory...
801 */ 801 */
802 if (strchr(name, '$') != NULL) { 802 if (strchr(name, '$') != NULL) {
803 expanded_name = Var_Subst(NULL, name, ctxt, VARE_WANTRES); 803 expanded_name = Var_Subst(NULL, name, ctxt, VARE_WANTRES);
804 if (expanded_name[0] == '\0') { 804 if (expanded_name[0] == '\0') {
805 if (DEBUG(VAR)) { 805 if (DEBUG(VAR)) {
806 fprintf(debug_file, "Var_Set(\"%s\", \"%s\", ...) " 806 fprintf(debug_file, "Var_Set(\"%s\", \"%s\", ...) "
807 "name expands to empty string - ignored\n", 807 "name expands to empty string - ignored\n",
808 name, val); 808 name, val);
809 } 809 }
810 free(expanded_name); 810 free(expanded_name);
811 return; 811 return;
812 } 812 }
813 name = expanded_name; 813 name = expanded_name;
814 } 814 }
815 if (ctxt == VAR_GLOBAL) { 815 if (ctxt == VAR_GLOBAL) {
816 v = VarFind(name, VAR_CMD, 0); 816 v = VarFind(name, VAR_CMD, 0);
817 if (v != NULL) { 817 if (v != NULL) {
818 if ((v->flags & VAR_FROM_CMD)) { 818 if ((v->flags & VAR_FROM_CMD)) {
819 if (DEBUG(VAR)) { 819 if (DEBUG(VAR)) {
820 fprintf(debug_file, "%s:%s = %s ignored!\n", ctxt->name, name, val); 820 fprintf(debug_file, "%s:%s = %s ignored!\n", ctxt->name, name, val);
821 } 821 }
822 goto out; 822 goto out;
823 } 823 }
824 VarFreeEnv(v, TRUE); 824 VarFreeEnv(v, TRUE);
825 } 825 }
826 } 826 }
827 v = VarFind(name, ctxt, 0); 827 v = VarFind(name, ctxt, 0);
828 if (v == NULL) { 828 if (v == NULL) {
829 if (ctxt == VAR_CMD && (flags & VAR_NO_EXPORT) == 0) { 829 if (ctxt == VAR_CMD && (flags & VAR_NO_EXPORT) == 0) {
830 /* 830 /*
831 * This var would normally prevent the same name being added 831 * This var would normally prevent the same name being added
832 * to VAR_GLOBAL, so delete it from there if needed. 832 * to VAR_GLOBAL, so delete it from there if needed.
833 * Otherwise -V name may show the wrong value. 833 * Otherwise -V name may show the wrong value.
834 */ 834 */
835 Var_Delete(name, VAR_GLOBAL); 835 Var_Delete(name, VAR_GLOBAL);
836 } 836 }
837 VarAdd(name, val, ctxt); 837 VarAdd(name, val, ctxt);
838 } else { 838 } else {
839 Buf_Empty(&v->val); 839 Buf_Empty(&v->val);
840 if (val) 840 if (val)
841 Buf_AddBytes(&v->val, strlen(val), val); 841 Buf_AddBytes(&v->val, strlen(val), val);
842 842
843 if (DEBUG(VAR)) { 843 if (DEBUG(VAR)) {
844 fprintf(debug_file, "%s:%s = %s\n", ctxt->name, name, val); 844 fprintf(debug_file, "%s:%s = %s\n", ctxt->name, name, val);
845 } 845 }
846 if ((v->flags & VAR_EXPORTED)) { 846 if ((v->flags & VAR_EXPORTED)) {
847 Var_Export1(name, VAR_EXPORT_PARENT); 847 Var_Export1(name, VAR_EXPORT_PARENT);
848 } 848 }
849 } 849 }
850 /* 850 /*
851 * Any variables given on the command line are automatically exported 851 * Any variables given on the command line are automatically exported
852 * to the environment (as per POSIX standard) 852 * to the environment (as per POSIX standard)
853 */ 853 */
854 if (ctxt == VAR_CMD && (flags & VAR_NO_EXPORT) == 0) { 854 if (ctxt == VAR_CMD && (flags & VAR_NO_EXPORT) == 0) {
855 if (v == NULL) { 855 if (v == NULL) {
856 /* we just added it */ 856 /* we just added it */
857 v = VarFind(name, ctxt, 0); 857 v = VarFind(name, ctxt, 0);
858 } 858 }
859 if (v != NULL) 859 if (v != NULL)
860 v->flags |= VAR_FROM_CMD; 860 v->flags |= VAR_FROM_CMD;
861 /* 861 /*
862 * If requested, don't export these in the environment 862 * If requested, don't export these in the environment
863 * individually. We still put them in MAKEOVERRIDES so 863 * individually. We still put them in MAKEOVERRIDES so
864 * that the command-line settings continue to override 864 * that the command-line settings continue to override
865 * Makefile settings. 865 * Makefile settings.
866 */ 866 */
867 if (varNoExportEnv != TRUE) 867 if (varNoExportEnv != TRUE)
868 setenv(name, val ? val : "", 1); 868 setenv(name, val ? val : "", 1);
869 869
870 Var_Append(MAKEOVERRIDES, name, VAR_GLOBAL); 870 Var_Append(MAKEOVERRIDES, name, VAR_GLOBAL);
871 } 871 }
872 if (name[0] == '.' && strcmp(name, SAVE_DOLLARS) == 0) 872 if (name[0] == '.' && strcmp(name, SAVE_DOLLARS) == 0)
873 save_dollars = s2Boolean(val, save_dollars); 873 save_dollars = s2Boolean(val, save_dollars);
874 874
875out: 875out:
876 free(expanded_name); 876 free(expanded_name);
877 if (v != NULL) 877 if (v != NULL)
878 VarFreeEnv(v, TRUE); 878 VarFreeEnv(v, TRUE);
879} 879}
880 880
881/*- 881/*-
882 *----------------------------------------------------------------------- 882 *-----------------------------------------------------------------------
883 * Var_Set -- 883 * Var_Set --
884 * Set the variable name to the value val in the given context. 884 * Set the variable name to the value val in the given context.
885 * 885 *
886 * Input: 886 * Input:
887 * name name of variable to set 887 * name name of variable to set
888 * val value to give to the variable 888 * val value to give to the variable
889 * ctxt context in which to set it 889 * ctxt context in which to set it
890 * 890 *
891 * Side Effects: 891 * Side Effects:
892 * If the variable doesn't yet exist, a new record is created for it. 892 * If the variable doesn't yet exist, a new record is created for it.
893 * Else the old value is freed and the new one stuck in its place 893 * Else the old value is freed and the new one stuck in its place
894 * 894 *
895 * Notes: 895 * Notes:
896 * The variable is searched for only in its context before being 896 * The variable is searched for only in its context before being
897 * created in that context. I.e. if the context is VAR_GLOBAL, 897 * created in that context. I.e. if the context is VAR_GLOBAL,
898 * only VAR_GLOBAL->context is searched. Likewise if it is VAR_CMD, only 898 * only VAR_GLOBAL->context is searched. Likewise if it is VAR_CMD, only
899 * VAR_CMD->context is searched. This is done to avoid the literally 899 * VAR_CMD->context is searched. This is done to avoid the literally
900 * thousands of unnecessary strcmp's that used to be done to 900 * thousands of unnecessary strcmp's that used to be done to
901 * set, say, $(@) or $(<). 901 * set, say, $(@) or $(<).
902 * If the context is VAR_GLOBAL though, we check if the variable 902 * If the context is VAR_GLOBAL though, we check if the variable
903 * was set in VAR_CMD from the command line and skip it if so. 903 * was set in VAR_CMD from the command line and skip it if so.
904 *----------------------------------------------------------------------- 904 *-----------------------------------------------------------------------
905 */ 905 */
906void 906void
907Var_Set(const char *name, const char *val, GNode *ctxt) 907Var_Set(const char *name, const char *val, GNode *ctxt)
908{ 908{
909 Var_Set_with_flags(name, val, ctxt, 0); 909 Var_Set_with_flags(name, val, ctxt, 0);
910} 910}
911 911
912/*- 912/*-
913 *----------------------------------------------------------------------- 913 *-----------------------------------------------------------------------
914 * Var_Append -- 914 * Var_Append --
915 * The variable of the given name has the given value appended to it in 915 * The variable of the given name has the given value appended to it in
916 * the given context. 916 * the given context.
917 * 917 *
918 * Input: 918 * Input:
919 * name name of variable to modify 919 * name name of variable to modify
920 * val String to append to it 920 * val String to append to it
921 * ctxt Context in which this should occur 921 * ctxt Context in which this should occur
922 * 922 *
923 * Side Effects: 923 * Side Effects:
924 * If the variable doesn't exist, it is created. Else the strings 924 * If the variable doesn't exist, it is created. Else the strings
925 * are concatenated (with a space in between). 925 * are concatenated (with a space in between).
926 * 926 *
927 * Notes: 927 * Notes:
928 * Only if the variable is being sought in the global context is the 928 * Only if the variable is being sought in the global context is the
929 * environment searched. 929 * environment searched.
930 * XXX: Knows its calling circumstances in that if called with ctxt 930 * XXX: Knows its calling circumstances in that if called with ctxt
931 * an actual target, it will only search that context since only 931 * an actual target, it will only search that context since only
932 * a local variable could be being appended to. This is actually 932 * a local variable could be being appended to. This is actually
933 * a big win and must be tolerated. 933 * a big win and must be tolerated.
934 *----------------------------------------------------------------------- 934 *-----------------------------------------------------------------------
935 */ 935 */
936void 936void
937Var_Append(const char *name, const char *val, GNode *ctxt) 937Var_Append(const char *name, const char *val, GNode *ctxt)
938{ 938{
939 Var *v; 939 Var *v;
940 Hash_Entry *h; 940 Hash_Entry *h;
941 char *expanded_name = NULL; 941 char *expanded_name = NULL;
942 942
943 if (strchr(name, '$') != NULL) { 943 if (strchr(name, '$') != NULL) {
944 expanded_name = Var_Subst(NULL, name, ctxt, VARE_WANTRES); 944 expanded_name = Var_Subst(NULL, name, ctxt, VARE_WANTRES);
945 if (expanded_name[0] == '\0') { 945 if (expanded_name[0] == '\0') {
946 if (DEBUG(VAR)) { 946 if (DEBUG(VAR)) {
947 fprintf(debug_file, "Var_Append(\"%s\", \"%s\", ...) " 947 fprintf(debug_file, "Var_Append(\"%s\", \"%s\", ...) "
948 "name expands to empty string - ignored\n", 948 "name expands to empty string - ignored\n",
949 name, val); 949 name, val);
950 } 950 }
951 free(expanded_name); 951 free(expanded_name);
952 return; 952 return;
953 } 953 }
954 name = expanded_name; 954 name = expanded_name;
955 } 955 }
956 956
957 v = VarFind(name, ctxt, ctxt == VAR_GLOBAL ? (FIND_CMD | FIND_ENV) : 0); 957 v = VarFind(name, ctxt, ctxt == VAR_GLOBAL ? (FIND_CMD | FIND_ENV) : 0);
958 958
959 if (v == NULL) { 959 if (v == NULL) {
960 Var_Set(name, val, ctxt); 960 Var_Set(name, val, ctxt);
961 } else if (ctxt == VAR_CMD || !(v->flags & VAR_FROM_CMD)) { 961 } else if (ctxt == VAR_CMD || !(v->flags & VAR_FROM_CMD)) {
962 Buf_AddByte(&v->val, ' '); 962 Buf_AddByte(&v->val, ' ');
963 Buf_AddBytes(&v->val, strlen(val), val); 963 Buf_AddBytes(&v->val, strlen(val), val);
964 964
965 if (DEBUG(VAR)) { 965 if (DEBUG(VAR)) {
966 fprintf(debug_file, "%s:%s = %s\n", ctxt->name, name, 966 fprintf(debug_file, "%s:%s = %s\n", ctxt->name, name,
967 Buf_GetAll(&v->val, NULL)); 967 Buf_GetAll(&v->val, NULL));
968 } 968 }
969 969
970 if (v->flags & VAR_FROM_ENV) { 970 if (v->flags & VAR_FROM_ENV) {
971 /* 971 /*
972 * If the original variable came from the environment, we 972 * If the original variable came from the environment, we
973 * have to install it in the global context (we could place 973 * have to install it in the global context (we could place
974 * it in the environment, but then we should provide a way to 974 * it in the environment, but then we should provide a way to
975 * export other variables...) 975 * export other variables...)
976 */ 976 */
977 v->flags &= ~VAR_FROM_ENV; 977 v->flags &= ~VAR_FROM_ENV;
978 h = Hash_CreateEntry(&ctxt->context, name, NULL); 978 h = Hash_CreateEntry(&ctxt->context, name, NULL);
979 Hash_SetValue(h, v); 979 Hash_SetValue(h, v);
980 } 980 }
981 } 981 }
982 free(expanded_name); 982 free(expanded_name);
983} 983}
984 984
985/*- 985/*-
986 *----------------------------------------------------------------------- 986 *-----------------------------------------------------------------------
987 * Var_Exists -- 987 * Var_Exists --
988 * See if the given variable exists. 988 * See if the given variable exists.
989 * 989 *
990 * Input: 990 * Input:
991 * name Variable to find 991 * name Variable to find
992 * ctxt Context in which to start search 992 * ctxt Context in which to start search
993 * 993 *
994 * Results: 994 * Results:
995 * TRUE if it does, FALSE if it doesn't 995 * TRUE if it does, FALSE if it doesn't
996 * 996 *
997 * Side Effects: 997 * Side Effects:
998 * None. 998 * None.
999 * 999 *
1000 *----------------------------------------------------------------------- 1000 *-----------------------------------------------------------------------
1001 */ 1001 */
1002Boolean 1002Boolean
1003Var_Exists(const char *name, GNode *ctxt) 1003Var_Exists(const char *name, GNode *ctxt)
1004{ 1004{
1005 Var *v; 1005 Var *v;
1006 char *cp; 1006 char *cp;
1007 1007
1008 if ((cp = strchr(name, '$')) != NULL) 1008 if ((cp = strchr(name, '$')) != NULL)
1009 cp = Var_Subst(NULL, name, ctxt, VARE_WANTRES); 1009 cp = Var_Subst(NULL, name, ctxt, VARE_WANTRES);
1010 v = VarFind(cp ? cp : name, ctxt, FIND_CMD | FIND_GLOBAL | FIND_ENV); 1010 v = VarFind(cp ? cp : name, ctxt, FIND_CMD | FIND_GLOBAL | FIND_ENV);
1011 free(cp); 1011 free(cp);
1012 if (v == NULL) 1012 if (v == NULL)
1013 return FALSE; 1013 return FALSE;
1014 1014
1015 (void)VarFreeEnv(v, TRUE); 1015 (void)VarFreeEnv(v, TRUE);
1016 return TRUE; 1016 return TRUE;
1017} 1017}
1018 1018
1019/*- 1019/*-
1020 *----------------------------------------------------------------------- 1020 *-----------------------------------------------------------------------
1021 * Var_Value -- 1021 * Var_Value --
1022 * Return the value of the named variable in the given context 1022 * Return the value of the named variable in the given context
1023 * 1023 *
1024 * Input: 1024 * Input:
1025 * name name to find 1025 * name name to find
1026 * ctxt context in which to search for it 1026 * ctxt context in which to search for it
1027 * 1027 *
1028 * Results: 1028 * Results:
1029 * The value if the variable exists, NULL if it doesn't 1029 * The value if the variable exists, NULL if it doesn't
1030 * 1030 *
1031 * Side Effects: 1031 * Side Effects:
1032 * None 1032 * None
1033 *----------------------------------------------------------------------- 1033 *-----------------------------------------------------------------------
1034 */ 1034 */
1035char * 1035char *
1036Var_Value(const char *name, GNode *ctxt, char **frp) 1036Var_Value(const char *name, GNode *ctxt, char **frp)
1037{ 1037{
1038 Var *v = VarFind(name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); 1038 Var *v = VarFind(name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD);
1039 *frp = NULL; 1039 *frp = NULL;
1040 if (v == NULL) 1040 if (v == NULL)
1041 return NULL; 1041 return NULL;
1042 1042
1043 char *p = Buf_GetAll(&v->val, NULL); 1043 char *p = Buf_GetAll(&v->val, NULL);
1044 if (VarFreeEnv(v, FALSE)) 1044 if (VarFreeEnv(v, FALSE))
1045 *frp = p; 1045 *frp = p;
1046 return p; 1046 return p;
1047} 1047}
1048 1048
1049 1049
1050/* SepBuf is a string being built from "words", interleaved with separators. */ 1050/* SepBuf is a string being built from "words", interleaved with separators. */
1051typedef struct { 1051typedef struct {
1052 Buffer buf; 1052 Buffer buf;
1053 Boolean needSep; 1053 Boolean needSep;
1054 char sep; 1054 char sep;
1055} SepBuf; 1055} SepBuf;
1056 1056
1057static void 1057static void
1058SepBuf_Init(SepBuf *buf, char sep) 1058SepBuf_Init(SepBuf *buf, char sep)
1059{ 1059{
1060 Buf_Init(&buf->buf, 32 /* bytes */); 1060 Buf_Init(&buf->buf, 32 /* bytes */);
1061 buf->needSep = FALSE; 1061 buf->needSep = FALSE;
1062 buf->sep = sep; 1062 buf->sep = sep;
1063} 1063}
1064 1064
1065static void 1065static void
1066SepBuf_Sep(SepBuf *buf) 1066SepBuf_Sep(SepBuf *buf)
1067{ 1067{
1068 buf->needSep = TRUE; 1068 buf->needSep = TRUE;
1069} 1069}
1070 1070
1071static void 1071static void
1072SepBuf_AddBytes(SepBuf *buf, const void *mem, size_t mem_size) 1072SepBuf_AddBytes(SepBuf *buf, const void *mem, size_t mem_size)
1073{ 1073{
1074 if (mem_size == 0) 1074 if (mem_size == 0)
1075 return; 1075 return;
1076 if (buf->needSep && buf->sep != '\0') { 1076 if (buf->needSep && buf->sep != '\0') {
1077 Buf_AddByte(&buf->buf, buf->sep); 1077 Buf_AddByte(&buf->buf, buf->sep);
1078 buf->needSep = FALSE; 1078 buf->needSep = FALSE;
1079 } 1079 }
1080 Buf_AddBytes(&buf->buf, mem_size, mem); 1080 Buf_AddBytes(&buf->buf, mem_size, mem);
1081} 1081}
1082 1082
1083static char * 1083static char *
1084SepBuf_Destroy(SepBuf *buf, Boolean free_buf) 1084SepBuf_Destroy(SepBuf *buf, Boolean free_buf)
1085{ 1085{
1086 return Buf_Destroy(&buf->buf, free_buf); 1086 return Buf_Destroy(&buf->buf, free_buf);
1087} 1087}
1088 1088
1089 1089
1090/* This callback for ModifyWords gets a single word from an expression and 1090/* This callback for ModifyWords gets a single word from an expression and
1091 * typically adds a modification of this word to the buffer. It may also do 1091 * typically adds a modification of this word to the buffer. It may also do
1092 * nothing or add several words. */ 1092 * nothing or add several words. */
1093typedef void (*ModifyWordsCallback)(const char *word, SepBuf *buf, void *data); 1093typedef void (*ModifyWordsCallback)(const char *word, SepBuf *buf, void *data);
1094 1094
1095 1095
1096/* Callback for ModifyWords to implement the :H modifier. 1096/* Callback for ModifyWords to implement the :H modifier.
1097 * Add the dirname of the given word to the buffer. */ 1097 * Add the dirname of the given word to the buffer. */
1098static void 1098static void
1099ModifyWord_Head(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) 1099ModifyWord_Head(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
1100{ 1100{
1101 const char *slash = strrchr(word, '/'); 1101 const char *slash = strrchr(word, '/');
1102 if (slash != NULL) 1102 if (slash != NULL)
1103 SepBuf_AddBytes(buf, word, slash - word); 1103 SepBuf_AddBytes(buf, word, slash - word);
1104 else 1104 else
1105 SepBuf_AddBytes(buf, ".", 1); 1105 SepBuf_AddBytes(buf, ".", 1);
1106} 1106}
1107 1107
1108/* Callback for ModifyWords to implement the :T modifier. 1108/* Callback for ModifyWords to implement the :T modifier.
1109 * Add the basename of the given word to the buffer. */ 1109 * Add the basename of the given word to the buffer. */
1110static void 1110static void
1111ModifyWord_Tail(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) 1111ModifyWord_Tail(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
1112{ 1112{
1113 const char *slash = strrchr(word, '/'); 1113 const char *slash = strrchr(word, '/');
1114 const char *base = slash != NULL ? slash + 1 : word; 1114 const char *base = slash != NULL ? slash + 1 : word;
1115 SepBuf_AddBytes(buf, base, strlen(base)); 1115 SepBuf_AddBytes(buf, base, strlen(base));
1116} 1116}
1117 1117
1118/* Callback for ModifyWords to implement the :E modifier. 1118/* Callback for ModifyWords to implement the :E modifier.
1119 * Add the filename suffix of the given word to the buffer, if it exists. */ 1119 * Add the filename suffix of the given word to the buffer, if it exists. */
1120static void 1120static void
1121ModifyWord_Suffix(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) 1121ModifyWord_Suffix(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
1122{ 1122{
1123 const char *dot = strrchr(word, '.'); 1123 const char *dot = strrchr(word, '.');
1124 if (dot != NULL) 1124 if (dot != NULL)
1125 SepBuf_AddBytes(buf, dot + 1, strlen(dot + 1)); 1125 SepBuf_AddBytes(buf, dot + 1, strlen(dot + 1));
1126} 1126}
1127 1127
1128/* Callback for ModifyWords to implement the :R modifier. 1128/* Callback for ModifyWords to implement the :R modifier.
1129 * Add the basename of the given word to the buffer. */ 1129 * Add the basename of the given word to the buffer. */
1130static void 1130static void
1131ModifyWord_Root(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) 1131ModifyWord_Root(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
1132{ 1132{
1133 char *dot = strrchr(word, '.'); 1133 char *dot = strrchr(word, '.');
1134 size_t len = dot != NULL ? (size_t)(dot - word) : strlen(word); 1134 size_t len = dot != NULL ? (size_t)(dot - word) : strlen(word);
1135 SepBuf_AddBytes(buf, word, len); 1135 SepBuf_AddBytes(buf, word, len);
1136} 1136}
1137 1137
1138/* Callback for ModifyWords to implement the :M modifier. 1138/* Callback for ModifyWords to implement the :M modifier.
1139 * Place the word in the buffer if it matches the given pattern. */ 1139 * Place the word in the buffer if it matches the given pattern. */
1140static void 1140static void
1141ModifyWord_Match(const char *word, SepBuf *buf, void *data) 1141ModifyWord_Match(const char *word, SepBuf *buf, void *data)
1142{ 1142{
1143 const char *pattern = data; 1143 const char *pattern = data;
1144 if (DEBUG(VAR)) 1144 if (DEBUG(VAR))
1145 fprintf(debug_file, "VarMatch [%s] [%s]\n", word, pattern); 1145 fprintf(debug_file, "VarMatch [%s] [%s]\n", word, pattern);
1146 if (Str_Match(word, pattern)) 1146 if (Str_Match(word, pattern))
1147 SepBuf_AddBytes(buf, word, strlen(word)); 1147 SepBuf_AddBytes(buf, word, strlen(word));
1148} 1148}
1149 1149
1150/* Callback for ModifyWords to implement the :N modifier. 1150/* Callback for ModifyWords to implement the :N modifier.
1151 * Place the word in the buffer if it doesn't match the given pattern. */ 1151 * Place the word in the buffer if it doesn't match the given pattern. */
1152static void 1152static void
1153ModifyWord_NoMatch(const char *word, SepBuf *buf, void *data) 1153ModifyWord_NoMatch(const char *word, SepBuf *buf, void *data)
1154{ 1154{
1155 const char *pattern = data; 1155 const char *pattern = data;
1156 if (!Str_Match(word, pattern)) 1156 if (!Str_Match(word, pattern))
1157 SepBuf_AddBytes(buf, word, strlen(word)); 1157 SepBuf_AddBytes(buf, word, strlen(word));
1158} 1158}
1159 1159
1160#ifdef SYSVVARSUB 1160#ifdef SYSVVARSUB
1161/*- 1161/*-
1162 *----------------------------------------------------------------------- 1162 *-----------------------------------------------------------------------
1163 * Str_SYSVMatch -- 1163 * Str_SYSVMatch --
1164 * Check word against pattern for a match (% is wild), 1164 * Check word against pattern for a match (% is wild),
1165 * 1165 *
1166 * Input: 1166 * Input:
1167 * word Word to examine 1167 * word Word to examine
1168 * pattern Pattern to examine against 1168 * pattern Pattern to examine against
1169 * len Number of characters to substitute 1169 * len Number of characters to substitute
1170 * 1170 *
1171 * Results: 1171 * Results:
1172 * Returns the beginning position of a match or null. The number 1172 * Returns the beginning position of a match or null. The number
1173 * of characters matched is returned in len. 1173 * of characters matched is returned in len.
1174 *----------------------------------------------------------------------- 1174 *-----------------------------------------------------------------------
1175 */ 1175 */
1176static const char * 1176static const char *
1177Str_SYSVMatch(const char *word, const char *pattern, size_t *len, 1177Str_SYSVMatch(const char *word, const char *pattern, size_t *len,
1178 Boolean *hasPercent) 1178 Boolean *hasPercent)
1179{ 1179{
1180 const char *p = pattern; 1180 const char *p = pattern;
1181 const char *w = word; 1181 const char *w = word;
1182 const char *m; 1182 const char *m;
1183 1183
1184 *hasPercent = FALSE; 1184 *hasPercent = FALSE;
1185 if (*p == '\0') { 1185 if (*p == '\0') {
1186 /* Null pattern is the whole string */ 1186 /* Null pattern is the whole string */
1187 *len = strlen(w); 1187 *len = strlen(w);
1188 return w; 1188 return w;
1189 } 1189 }
1190 1190
1191 if ((m = strchr(p, '%')) != NULL) { 1191 if ((m = strchr(p, '%')) != NULL) {
1192 *hasPercent = TRUE; 1192 *hasPercent = TRUE;
1193 if (*w == '\0') { 1193 if (*w == '\0') {
1194 /* empty word does not match pattern */ 1194 /* empty word does not match pattern */
1195 return NULL; 1195 return NULL;
1196 } 1196 }
1197 /* check that the prefix matches */ 1197 /* check that the prefix matches */
1198 for (; p != m && *w && *w == *p; w++, p++) 1198 for (; p != m && *w && *w == *p; w++, p++)
1199 continue; 1199 continue;
1200 1200
1201 if (p != m) 1201 if (p != m)
1202 return NULL; /* No match */ 1202 return NULL; /* No match */
1203 1203
1204 if (*++p == '\0') { 1204 if (*++p == '\0') {
1205 /* No more pattern, return the rest of the string */ 1205 /* No more pattern, return the rest of the string */
1206 *len = strlen(w); 1206 *len = strlen(w);
1207 return w; 1207 return w;
1208 } 1208 }
1209 } 1209 }
1210 1210
1211 m = w; 1211 m = w;
1212 1212
1213 /* Find a matching tail */ 1213 /* Find a matching tail */
1214 do { 1214 do {
1215 if (strcmp(p, w) == 0) { 1215 if (strcmp(p, w) == 0) {
1216 *len = w - m; 1216 *len = w - m;
1217 return m; 1217 return m;
1218 } 1218 }
1219 } while (*w++ != '\0'); 1219 } while (*w++ != '\0');
1220 1220
1221 return NULL; 1221 return NULL;
1222} 1222}
1223 1223
1224 1224
1225/*- 1225/*-
1226 *----------------------------------------------------------------------- 1226 *-----------------------------------------------------------------------
1227 * Str_SYSVSubst -- 1227 * Str_SYSVSubst --
1228 * Substitute '%' on the pattern with len characters from src. 1228 * Substitute '%' on the pattern with len characters from src.
1229 * If the pattern does not contain a '%' prepend len characters 1229 * If the pattern does not contain a '%' prepend len characters
1230 * from src. 1230 * from src.
1231 * 1231 *
1232 * Side Effects: 1232 * Side Effects:
1233 * Places result on buf 1233 * Places result on buf
1234 *----------------------------------------------------------------------- 1234 *-----------------------------------------------------------------------
1235 */ 1235 */
1236static void 1236static void
1237Str_SYSVSubst(SepBuf *buf, const char *pat, const char *src, size_t len, 1237Str_SYSVSubst(SepBuf *buf, const char *pat, const char *src, size_t len,
1238 Boolean lhsHasPercent) 1238 Boolean lhsHasPercent)
1239{ 1239{
1240 const char *m; 1240 const char *m;
1241 1241
1242 if ((m = strchr(pat, '%')) != NULL && lhsHasPercent) { 1242 if ((m = strchr(pat, '%')) != NULL && lhsHasPercent) {
1243 /* Copy the prefix */ 1243 /* Copy the prefix */
1244 SepBuf_AddBytes(buf, pat, m - pat); 1244 SepBuf_AddBytes(buf, pat, m - pat);
1245 /* skip the % */ 1245 /* skip the % */
1246 pat = m + 1; 1246 pat = m + 1;
1247 } 1247 }
1248 if (m != NULL || !lhsHasPercent) { 1248 if (m != NULL || !lhsHasPercent) {
1249 /* Copy the pattern */ 1249 /* Copy the pattern */
1250 SepBuf_AddBytes(buf, src, len); 1250 SepBuf_AddBytes(buf, src, len);
1251 } 1251 }
1252 1252
1253 /* append the rest */ 1253 /* append the rest */
1254 SepBuf_AddBytes(buf, pat, strlen(pat)); 1254 SepBuf_AddBytes(buf, pat, strlen(pat));
1255} 1255}
1256 1256
1257 1257
1258typedef struct { 1258typedef struct {
1259 GNode *ctx; 1259 GNode *ctx;
1260 const char *lhs; 1260 const char *lhs;
1261 const char *rhs; 1261 const char *rhs;
1262} ModifyWord_SYSVSubstArgs; 1262} ModifyWord_SYSVSubstArgs;
1263 1263
1264/* Callback for ModifyWords to implement the :%.from=%.to modifier. */ 1264/* Callback for ModifyWords to implement the :%.from=%.to modifier. */
1265static void 1265static void
1266ModifyWord_SYSVSubst(const char *word, SepBuf *buf, void *data) 1266ModifyWord_SYSVSubst(const char *word, SepBuf *buf, void *data)
1267{ 1267{
1268 const ModifyWord_SYSVSubstArgs *args = data; 1268 const ModifyWord_SYSVSubstArgs *args = data;
1269 1269
1270 size_t len; 1270 size_t len;
1271 Boolean hasPercent; 1271 Boolean hasPercent;
1272 const char *ptr = Str_SYSVMatch(word, args->lhs, &len, &hasPercent); 1272 const char *ptr = Str_SYSVMatch(word, args->lhs, &len, &hasPercent);
1273 if (ptr != NULL) { 1273 if (ptr != NULL) {
1274 char *varexp = Var_Subst(NULL, args->rhs, args->ctx, VARE_WANTRES); 1274 char *varexp = Var_Subst(NULL, args->rhs, args->ctx, VARE_WANTRES);
1275 Str_SYSVSubst(buf, varexp, ptr, len, hasPercent); 1275 Str_SYSVSubst(buf, varexp, ptr, len, hasPercent);
1276 free(varexp); 1276 free(varexp);
1277 } else { 1277 } else {
1278 SepBuf_AddBytes(buf, word, strlen(word)); 1278 SepBuf_AddBytes(buf, word, strlen(word));
1279 } 1279 }
1280} 1280}
1281#endif 1281#endif
1282 1282
1283 1283
1284typedef struct { 1284typedef struct {
1285 const char *lhs; 1285 const char *lhs;
1286 size_t lhsLen; 1286 size_t lhsLen;
1287 const char *rhs; 1287 const char *rhs;
1288 size_t rhsLen; 1288 size_t rhsLen;
1289 VarPatternFlags pflags; 1289 VarPatternFlags pflags;
1290} ModifyWord_SubstArgs; 1290} ModifyWord_SubstArgs;
1291 1291
1292/* Callback for ModifyWords to implement the :S,from,to, modifier. 1292/* Callback for ModifyWords to implement the :S,from,to, modifier.
1293 * Perform a string substitution on the given word. */ 1293 * Perform a string substitution on the given word. */
1294static void 1294static void
1295ModifyWord_Subst(const char *word, SepBuf *buf, void *data) 1295ModifyWord_Subst(const char *word, SepBuf *buf, void *data)
1296{ 1296{
1297 size_t wordLen = strlen(word); 1297 size_t wordLen = strlen(word);
1298 ModifyWord_SubstArgs *args = data; 1298 ModifyWord_SubstArgs *args = data;
1299 const VarPatternFlags pflags = args->pflags; 1299 const VarPatternFlags pflags = args->pflags;
1300 1300
1301 if ((pflags & (VARP_SUB_ONE | VARP_SUB_MATCHED)) == 1301 if ((pflags & (VARP_SUB_ONE | VARP_SUB_MATCHED)) ==
1302 (VARP_SUB_ONE | VARP_SUB_MATCHED)) 1302 (VARP_SUB_ONE | VARP_SUB_MATCHED))
1303 goto nosub; 1303 goto nosub;
1304 1304
1305 if (args->pflags & VARP_ANCHOR_START) { 1305 if (args->pflags & VARP_ANCHOR_START) {
1306 if (wordLen < args->lhsLen || 1306 if (wordLen < args->lhsLen ||
1307 memcmp(word, args->lhs, args->lhsLen) != 0) 1307 memcmp(word, args->lhs, args->lhsLen) != 0)
1308 goto nosub; 1308 goto nosub;
1309 1309
1310 if (args->pflags & VARP_ANCHOR_END) { 1310 if (args->pflags & VARP_ANCHOR_END) {
1311 if (wordLen != args->lhsLen) 1311 if (wordLen != args->lhsLen)
1312 goto nosub; 1312 goto nosub;
1313 1313
1314 SepBuf_AddBytes(buf, args->rhs, args->rhsLen); 1314 SepBuf_AddBytes(buf, args->rhs, args->rhsLen);
1315 args->pflags |= VARP_SUB_MATCHED; 1315 args->pflags |= VARP_SUB_MATCHED;
1316 } else { 1316 } else {
1317 SepBuf_AddBytes(buf, args->rhs, args->rhsLen); 1317 SepBuf_AddBytes(buf, args->rhs, args->rhsLen);
1318 SepBuf_AddBytes(buf, word + args->lhsLen, wordLen - args->lhsLen); 1318 SepBuf_AddBytes(buf, word + args->lhsLen, wordLen - args->lhsLen);
1319 args->pflags |= VARP_SUB_MATCHED; 1319 args->pflags |= VARP_SUB_MATCHED;
1320 } 1320 }
1321 return; 1321 return;
1322 } 1322 }
1323 1323
1324 if (args->pflags & VARP_ANCHOR_END) { 1324 if (args->pflags & VARP_ANCHOR_END) {
1325 if (wordLen < args->lhsLen) 1325 if (wordLen < args->lhsLen)
1326 goto nosub; 1326 goto nosub;
1327 const char *start = word + (wordLen - args->lhsLen); 1327 const char *start = word + (wordLen - args->lhsLen);
1328 if (memcmp(start, args->lhs, args->lhsLen) != 0) 1328 if (memcmp(start, args->lhs, args->lhsLen) != 0)
1329 goto nosub; 1329 goto nosub;
1330 1330
1331 SepBuf_AddBytes(buf, word, start - word); 1331 SepBuf_AddBytes(buf, word, start - word);
1332 SepBuf_AddBytes(buf, args->rhs, args->rhsLen); 1332 SepBuf_AddBytes(buf, args->rhs, args->rhsLen);
1333 args->pflags |= VARP_SUB_MATCHED; 1333 args->pflags |= VARP_SUB_MATCHED;
1334 return; 1334 return;
1335 } 1335 }
1336 1336
1337 /* unanchored */ 1337 /* unanchored */
1338 const char *cp; 1338 const char *cp;
1339 while ((cp = Str_FindSubstring(word, args->lhs)) != NULL) { 1339 while ((cp = Str_FindSubstring(word, args->lhs)) != NULL) {
1340 SepBuf_AddBytes(buf, word, cp - word); 1340 SepBuf_AddBytes(buf, word, cp - word);
1341 SepBuf_AddBytes(buf, args->rhs, args->rhsLen); 1341 SepBuf_AddBytes(buf, args->rhs, args->rhsLen);
 1342 args->pflags |= VARP_SUB_MATCHED;
1342 wordLen -= (cp - word) + args->lhsLen; 1343 wordLen -= (cp - word) + args->lhsLen;
1343 word = cp + args->lhsLen; 1344 word = cp + args->lhsLen;
1344 if (wordLen == 0 || !(args->pflags & VARP_SUB_GLOBAL)) 1345 if (wordLen == 0 || !(args->pflags & VARP_SUB_GLOBAL))
1345 break; 1346 break;
1346 args->pflags |= VARP_SUB_MATCHED; 
1347 } 1347 }
1348nosub: 1348nosub:
1349 SepBuf_AddBytes(buf, word, wordLen); 1349 SepBuf_AddBytes(buf, word, wordLen);
1350} 1350}
1351 1351
1352#ifndef NO_REGEX 1352#ifndef NO_REGEX
1353/*- 1353/*-
1354 *----------------------------------------------------------------------- 1354 *-----------------------------------------------------------------------
1355 * VarREError -- 1355 * VarREError --
1356 * Print the error caused by a regcomp or regexec call. 1356 * Print the error caused by a regcomp or regexec call.
1357 * 1357 *
1358 * Side Effects: 1358 * Side Effects:
1359 * An error gets printed. 1359 * An error gets printed.
1360 * 1360 *
1361 *----------------------------------------------------------------------- 1361 *-----------------------------------------------------------------------
1362 */ 1362 */
1363static void 1363static void
1364VarREError(int reerr, regex_t *pat, const char *str) 1364VarREError(int reerr, regex_t *pat, const char *str)
1365{ 1365{
1366 char *errbuf; 1366 char *errbuf;
1367 int errlen; 1367 int errlen;
1368 1368
1369 errlen = regerror(reerr, pat, 0, 0); 1369 errlen = regerror(reerr, pat, 0, 0);
1370 errbuf = bmake_malloc(errlen); 1370 errbuf = bmake_malloc(errlen);
1371 regerror(reerr, pat, errbuf, errlen); 1371 regerror(reerr, pat, errbuf, errlen);
1372 Error("%s: %s", str, errbuf); 1372 Error("%s: %s", str, errbuf);
1373 free(errbuf); 1373 free(errbuf);
1374} 1374}
1375 1375
1376typedef struct { 1376typedef struct {
1377 regex_t re; 1377 regex_t re;
1378 int nsub; 1378 int nsub;
1379 char *replace; 1379 char *replace;
1380 VarPatternFlags pflags; 1380 VarPatternFlags pflags;
1381} ModifyWord_SubstRegexArgs; 1381} ModifyWord_SubstRegexArgs;
1382 1382
1383/* Callback for ModifyWords to implement the :C/from/to/ modifier. 1383/* Callback for ModifyWords to implement the :C/from/to/ modifier.
1384 * Perform a regex substitution on the given word. */ 1384 * Perform a regex substitution on the given word. */
1385static void 1385static void
1386ModifyWord_SubstRegex(const char *word, SepBuf *buf, void *data) 1386ModifyWord_SubstRegex(const char *word, SepBuf *buf, void *data)
1387{ 1387{
1388 ModifyWord_SubstRegexArgs *args = data; 1388 ModifyWord_SubstRegexArgs *args = data;
1389 int xrv; 1389 int xrv;
1390 const char *wp = word; 1390 const char *wp = word;
1391 char *rp; 1391 char *rp;
1392 int flags = 0; 1392 int flags = 0;
1393 regmatch_t m[10]; 1393 regmatch_t m[10];
1394 1394
1395 if ((args->pflags & VARP_SUB_ONE) && (args->pflags & VARP_SUB_MATCHED)) 1395 if ((args->pflags & VARP_SUB_ONE) && (args->pflags & VARP_SUB_MATCHED))
1396 goto nosub; 1396 goto nosub;
1397 1397
1398tryagain: 1398tryagain:
1399 xrv = regexec(&args->re, wp, args->nsub, m, flags); 1399 xrv = regexec(&args->re, wp, args->nsub, m, flags);
1400 1400
1401 switch (xrv) { 1401 switch (xrv) {
1402 case 0: 1402 case 0:
1403 args->pflags |= VARP_SUB_MATCHED; 1403 args->pflags |= VARP_SUB_MATCHED;
1404 SepBuf_AddBytes(buf, wp, m[0].rm_so); 1404 SepBuf_AddBytes(buf, wp, m[0].rm_so);
1405 1405
1406 for (rp = args->replace; *rp; rp++) { 1406 for (rp = args->replace; *rp; rp++) {
1407 if (*rp == '\\' && (rp[1] == '&' || rp[1] == '\\')) { 1407 if (*rp == '\\' && (rp[1] == '&' || rp[1] == '\\')) {
1408 SepBuf_AddBytes(buf, rp + 1, 1); 1408 SepBuf_AddBytes(buf, rp + 1, 1);
1409 rp++; 1409 rp++;
1410 } else if (*rp == '&' || 1410 } else if (*rp == '&' ||
1411 (*rp == '\\' && isdigit((unsigned char)rp[1]))) { 1411 (*rp == '\\' && isdigit((unsigned char)rp[1]))) {
1412 int n; 1412 int n;
1413 char errstr[3]; 1413 char errstr[3];
1414 1414
1415 if (*rp == '&') { 1415 if (*rp == '&') {
1416 n = 0; 1416 n = 0;
1417 errstr[0] = '&'; 1417 errstr[0] = '&';
1418 errstr[1] = '\0'; 1418 errstr[1] = '\0';
1419 } else { 1419 } else {
1420 n = rp[1] - '0'; 1420 n = rp[1] - '0';
1421 errstr[0] = '\\'; 1421 errstr[0] = '\\';
1422 errstr[1] = rp[1]; 1422 errstr[1] = rp[1];
1423 errstr[2] = '\0'; 1423 errstr[2] = '\0';
1424 rp++; 1424 rp++;
1425 } 1425 }
1426 1426
1427 if (n >= args->nsub) { 1427 if (n >= args->nsub) {
1428 Error("No subexpression %s", errstr); 1428 Error("No subexpression %s", errstr);
1429 } else if (m[n].rm_so == -1 && m[n].rm_eo == -1) { 1429 } else if (m[n].rm_so == -1 && m[n].rm_eo == -1) {
1430 Error("No match for subexpression %s", errstr); 1430 Error("No match for subexpression %s", errstr);
1431 } else { 1431 } else {
1432 SepBuf_AddBytes(buf, wp + m[n].rm_so, 1432 SepBuf_AddBytes(buf, wp + m[n].rm_so,
1433 m[n].rm_eo - m[n].rm_so); 1433 m[n].rm_eo - m[n].rm_so);
1434 } 1434 }
1435 1435
1436 } else { 1436 } else {
1437 SepBuf_AddBytes(buf, rp, 1); 1437 SepBuf_AddBytes(buf, rp, 1);
1438 } 1438 }
1439 } 1439 }
1440 wp += m[0].rm_eo; 1440 wp += m[0].rm_eo;
1441 if (args->pflags & VARP_SUB_GLOBAL) { 1441 if (args->pflags & VARP_SUB_GLOBAL) {
1442 flags |= REG_NOTBOL; 1442 flags |= REG_NOTBOL;
1443 if (m[0].rm_so == 0 && m[0].rm_eo == 0) { 1443 if (m[0].rm_so == 0 && m[0].rm_eo == 0) {
1444 SepBuf_AddBytes(buf, wp, 1); 1444 SepBuf_AddBytes(buf, wp, 1);
1445 wp++; 1445 wp++;
1446 } 1446 }
1447 if (*wp) 1447 if (*wp)
1448 goto tryagain; 1448 goto tryagain;
1449 } 1449 }
1450 if (*wp) { 1450 if (*wp) {
1451 SepBuf_AddBytes(buf, wp, strlen(wp)); 1451 SepBuf_AddBytes(buf, wp, strlen(wp));
1452 } 1452 }
1453 break; 1453 break;
1454 default: 1454 default:
1455 VarREError(xrv, &args->re, "Unexpected regex error"); 1455 VarREError(xrv, &args->re, "Unexpected regex error");
1456 /* fall through */ 1456 /* fall through */
1457 case REG_NOMATCH: 1457 case REG_NOMATCH:
1458 nosub: 1458 nosub:
1459 SepBuf_AddBytes(buf, wp, strlen(wp)); 1459 SepBuf_AddBytes(buf, wp, strlen(wp));
1460 break; 1460 break;
1461 } 1461 }
1462} 1462}
1463#endif 1463#endif
1464 1464
1465 1465
1466typedef struct { 1466typedef struct {
1467 GNode *ctx; 1467 GNode *ctx;
1468 char *tvar; /* name of temporary variable */ 1468 char *tvar; /* name of temporary variable */
1469 char *str; /* string to expand */ 1469 char *str; /* string to expand */
1470 VarEvalFlags eflags; 1470 VarEvalFlags eflags;
1471} ModifyWord_LoopArgs; 1471} ModifyWord_LoopArgs;
1472 1472
1473/* Callback for ModifyWords to implement the :@var@...@ modifier of ODE make. */ 1473/* Callback for ModifyWords to implement the :@var@...@ modifier of ODE make. */
1474static void 1474static void
1475ModifyWord_Loop(const char *word, SepBuf *buf, void *data) 1475ModifyWord_Loop(const char *word, SepBuf *buf, void *data)
1476{ 1476{
1477 if (word[0] == '\0') 1477 if (word[0] == '\0')
1478 return; 1478 return;
1479 1479
1480 const ModifyWord_LoopArgs *args = data; 1480 const ModifyWord_LoopArgs *args = data;
1481 Var_Set_with_flags(args->tvar, word, args->ctx, VAR_NO_EXPORT); 1481 Var_Set_with_flags(args->tvar, word, args->ctx, VAR_NO_EXPORT);
1482 char *s = Var_Subst(NULL, args->str, args->ctx, args->eflags); 1482 char *s = Var_Subst(NULL, args->str, args->ctx, args->eflags);
1483 if (DEBUG(VAR)) { 1483 if (DEBUG(VAR)) {
1484 fprintf(debug_file, 1484 fprintf(debug_file,
1485 "ModifyWord_Loop: in \"%s\", replace \"%s\" with \"%s\" " 1485 "ModifyWord_Loop: in \"%s\", replace \"%s\" with \"%s\" "
1486 "to \"%s\"\n", 1486 "to \"%s\"\n",
1487 word, args->tvar, args->str, s ? s : "(null)"); 1487 word, args->tvar, args->str, s ? s : "(null)");
1488 } 1488 }
1489 1489
1490 if (s != NULL && s[0] != '\0') { 1490 if (s != NULL && s[0] != '\0') {
1491 if (s[0] == '\n' || (buf->buf.count > 0 && 1491 if (s[0] == '\n' || (buf->buf.count > 0 &&
1492 buf->buf.buffer[buf->buf.count - 1] == '\n')) 1492 buf->buf.buffer[buf->buf.count - 1] == '\n'))
1493 buf->needSep = FALSE; 1493 buf->needSep = FALSE;
1494 SepBuf_AddBytes(buf, s, strlen(s)); 1494 SepBuf_AddBytes(buf, s, strlen(s));
1495 } 1495 }
1496 free(s); 1496 free(s);
1497} 1497}
1498 1498
1499 1499
1500/*- 1500/*-
1501 * Implements the :[first..last] modifier. 1501 * Implements the :[first..last] modifier.
1502 * This is a special case of ModifyWords since we want to be able 1502 * This is a special case of ModifyWords since we want to be able
1503 * to scan the list backwards if first > last. 1503 * to scan the list backwards if first > last.
1504 */ 1504 */
1505static char * 1505static char *
1506VarSelectWords(Byte sep, Boolean oneBigWord, const char *str, int first, 1506VarSelectWords(Byte sep, Boolean oneBigWord, const char *str, int first,
1507 int last) 1507 int last)
1508{ 1508{
1509 SepBuf buf; 1509 SepBuf buf;
1510 char **av; /* word list */ 1510 char **av; /* word list */
1511 char *as; /* word list memory */ 1511 char *as; /* word list memory */
1512 int ac, i; 1512 int ac, i;
1513 int start, end, step; 1513 int start, end, step;
1514 1514
1515 SepBuf_Init(&buf, sep); 1515 SepBuf_Init(&buf, sep);
1516 1516
1517 if (oneBigWord) { 1517 if (oneBigWord) {
1518 /* fake what brk_string() would do if there were only one word */ 1518 /* fake what brk_string() would do if there were only one word */
1519 ac = 1; 1519 ac = 1;
1520 av = bmake_malloc((ac + 1) * sizeof(char *)); 1520 av = bmake_malloc((ac + 1) * sizeof(char *));
1521 as = bmake_strdup(str); 1521 as = bmake_strdup(str);
1522 av[0] = as; 1522 av[0] = as;
1523 av[1] = NULL; 1523 av[1] = NULL;
1524 } else { 1524 } else {
1525 av = brk_string(str, &ac, FALSE, &as); 1525 av = brk_string(str, &ac, FALSE, &as);
1526 } 1526 }
1527 1527
1528 /* 1528 /*
1529 * Now sanitize seldata. 1529 * Now sanitize seldata.
1530 * If seldata->start or seldata->end are negative, convert them to 1530 * If seldata->start or seldata->end are negative, convert them to
1531 * the positive equivalents (-1 gets converted to argc, -2 gets 1531 * the positive equivalents (-1 gets converted to argc, -2 gets
1532 * converted to (argc-1), etc.). 1532 * converted to (argc-1), etc.).
1533 */ 1533 */
1534 if (first < 0) 1534 if (first < 0)
1535 first += ac + 1; 1535 first += ac + 1;
1536 if (last < 0) 1536 if (last < 0)
1537 last += ac + 1; 1537 last += ac + 1;
1538 1538
1539 /* 1539 /*
1540 * We avoid scanning more of the list than we need to. 1540 * We avoid scanning more of the list than we need to.
1541 */ 1541 */
1542 if (first > last) { 1542 if (first > last) {
1543 start = MIN(ac, first) - 1; 1543 start = MIN(ac, first) - 1;
1544 end = MAX(0, last - 1); 1544 end = MAX(0, last - 1);
1545 step = -1; 1545 step = -1;
1546 } else { 1546 } else {
1547 start = MAX(0, first - 1); 1547 start = MAX(0, first - 1);
1548 end = MIN(ac, last); 1548 end = MIN(ac, last);
1549 step = 1; 1549 step = 1;
1550 } 1550 }
1551 1551
1552 for (i = start; (step < 0) == (i >= end); i += step) { 1552 for (i = start; (step < 0) == (i >= end); i += step) {
1553 SepBuf_AddBytes(&buf, av[i], strlen(av[i])); 1553 SepBuf_AddBytes(&buf, av[i], strlen(av[i]));
1554 SepBuf_Sep(&buf); 1554 SepBuf_Sep(&buf);
1555 } 1555 }
1556 1556
1557 free(as); 1557 free(as);
1558 free(av); 1558 free(av);
1559 1559
1560 return SepBuf_Destroy(&buf, FALSE); 1560 return SepBuf_Destroy(&buf, FALSE);
1561} 1561}
1562 1562
1563 1563
1564/* Callback for ModifyWords to implement the :tA modifier. 1564/* Callback for ModifyWords to implement the :tA modifier.
1565 * Replace each word with the result of realpath() if successful. */ 1565 * Replace each word with the result of realpath() if successful. */
1566static void 1566static void
1567ModifyWord_Realpath(const char *word, SepBuf *buf, void *data MAKE_ATTR_UNUSED) 1567ModifyWord_Realpath(const char *word, SepBuf *buf, void *data MAKE_ATTR_UNUSED)
1568{ 1568{
1569 struct stat st; 1569 struct stat st;
1570 char rbuf[MAXPATHLEN]; 1570 char rbuf[MAXPATHLEN];
1571 1571
1572 char *rp = cached_realpath(word, rbuf); 1572 char *rp = cached_realpath(word, rbuf);
1573 if (rp != NULL && *rp == '/' && stat(rp, &st) == 0) 1573 if (rp != NULL && *rp == '/' && stat(rp, &st) == 0)
1574 word = rp; 1574 word = rp;
1575 1575
1576 SepBuf_AddBytes(buf, word, strlen(word)); 1576 SepBuf_AddBytes(buf, word, strlen(word));
1577} 1577}
1578 1578
1579/*- 1579/*-
1580 *----------------------------------------------------------------------- 1580 *-----------------------------------------------------------------------
1581 * Modify each of the words of the passed string using the given function. 1581 * Modify each of the words of the passed string using the given function.
1582 * 1582 *
1583 * Input: 1583 * Input:
1584 * str String whose words should be modified 1584 * str String whose words should be modified
1585 * modifyWord Function that modifies a single word 1585 * modifyWord Function that modifies a single word
1586 * data Custom data for modifyWord 1586 * data Custom data for modifyWord
1587 * 1587 *
1588 * Results: 1588 * Results:
1589 * A string of all the words modified appropriately. 1589 * A string of all the words modified appropriately.
1590 *----------------------------------------------------------------------- 1590 *-----------------------------------------------------------------------
1591 */ 1591 */
1592static char * 1592static char *
1593ModifyWords(GNode *ctx, Byte sep, Boolean oneBigWord, 1593ModifyWords(GNode *ctx, Byte sep, Boolean oneBigWord,
1594 const char *str, ModifyWordsCallback modifyWord, void *data) 1594 const char *str, ModifyWordsCallback modifyWord, void *data)
1595{ 1595{
1596 SepBuf result; 1596 SepBuf result;
1597 char **av; /* word list */ 1597 char **av; /* word list */
1598 char *as; /* word list memory */ 1598 char *as; /* word list memory */
1599 int ac, i; 1599 int ac, i;
1600 1600
1601 SepBuf_Init(&result, sep); 1601 SepBuf_Init(&result, sep);
1602 1602
1603 if (oneBigWord) { 1603 if (oneBigWord) {
1604 /* fake what brk_string() would do if there were only one word */ 1604 /* fake what brk_string() would do if there were only one word */
1605 ac = 1; 1605 ac = 1;
1606 av = bmake_malloc((ac + 1) * sizeof(char *)); 1606 av = bmake_malloc((ac + 1) * sizeof(char *));
1607 as = bmake_strdup(str); 1607 as = bmake_strdup(str);
1608 av[0] = as; 1608 av[0] = as;
1609 av[1] = NULL; 1609 av[1] = NULL;
1610 } else { 1610 } else {
1611 av = brk_string(str, &ac, FALSE, &as); 1611 av = brk_string(str, &ac, FALSE, &as);
1612 } 1612 }
1613 1613
1614 if (DEBUG(VAR)) { 1614 if (DEBUG(VAR)) {
1615 fprintf(debug_file, "ModifyWords: split \"%s\" into %d words\n", 1615 fprintf(debug_file, "ModifyWords: split \"%s\" into %d words\n",
1616 str, ac); 1616 str, ac);
1617 } 1617 }
1618 1618
1619 for (i = 0; i < ac; i++) { 1619 for (i = 0; i < ac; i++) {
1620 size_t orig_count = result.buf.count; 1620 size_t orig_count = result.buf.count;
1621 modifyWord(av[i], &result, data); 1621 modifyWord(av[i], &result, data);
1622 size_t count = result.buf.count; 1622 size_t count = result.buf.count;
1623 if (count != orig_count) 1623 if (count != orig_count)
1624 SepBuf_Sep(&result); 1624 SepBuf_Sep(&result);
1625 } 1625 }
1626 1626
1627 free(as); 1627 free(as);
1628 free(av); 1628 free(av);
1629 1629
1630 return SepBuf_Destroy(&result, FALSE); 1630 return SepBuf_Destroy(&result, FALSE);
1631} 1631}
1632 1632
1633 1633
1634static int 1634static int
1635VarWordCompare(const void *a, const void *b) 1635VarWordCompare(const void *a, const void *b)
1636{ 1636{
1637 int r = strcmp(*(const char * const *)a, *(const char * const *)b); 1637 int r = strcmp(*(const char * const *)a, *(const char * const *)b);
1638 return r; 1638 return r;
1639} 1639}
1640 1640
1641static int 1641static int
1642VarWordCompareReverse(const void *a, const void *b) 1642VarWordCompareReverse(const void *a, const void *b)
1643{ 1643{
1644 int r = strcmp(*(const char * const *)b, *(const char * const *)a); 1644 int r = strcmp(*(const char * const *)b, *(const char * const *)a);
1645 return r; 1645 return r;
1646} 1646}
1647 1647
1648/*- 1648/*-
1649 *----------------------------------------------------------------------- 1649 *-----------------------------------------------------------------------
1650 * VarOrder -- 1650 * VarOrder --
1651 * Order the words in the string. 1651 * Order the words in the string.
1652 * 1652 *
1653 * Input: 1653 * Input:
1654 * str String whose words should be sorted. 1654 * str String whose words should be sorted.
1655 * otype How to order: s - sort, x - random. 1655 * otype How to order: s - sort, x - random.
1656 * 1656 *
1657 * Results: 1657 * Results:
1658 * A string containing the words ordered. 1658 * A string containing the words ordered.
1659 * 1659 *
1660 * Side Effects: 1660 * Side Effects:
1661 * None. 1661 * None.
1662 * 1662 *
1663 *----------------------------------------------------------------------- 1663 *-----------------------------------------------------------------------
1664 */ 1664 */
1665static char * 1665static char *
1666VarOrder(const char *str, const char otype) 1666VarOrder(const char *str, const char otype)
1667{ 1667{
1668 Buffer buf; /* Buffer for the new string */ 1668 Buffer buf; /* Buffer for the new string */
1669 char **av; /* word list [first word does not count] */ 1669 char **av; /* word list [first word does not count] */
1670 char *as; /* word list memory */ 1670 char *as; /* word list memory */
1671 int ac, i; 1671 int ac, i;
1672 1672
1673 Buf_Init(&buf, 0); 1673 Buf_Init(&buf, 0);
1674 1674
1675 av = brk_string(str, &ac, FALSE, &as); 1675 av = brk_string(str, &ac, FALSE, &as);
1676 1676
1677 if (ac > 0) { 1677 if (ac > 0) {
1678 switch (otype) { 1678 switch (otype) {
1679 case 'r': /* reverse sort alphabetically */ 1679 case 'r': /* reverse sort alphabetically */
1680 qsort(av, ac, sizeof(char *), VarWordCompareReverse); 1680 qsort(av, ac, sizeof(char *), VarWordCompareReverse);
1681 break; 1681 break;
1682 case 's': /* sort alphabetically */ 1682 case 's': /* sort alphabetically */
1683 qsort(av, ac, sizeof(char *), VarWordCompare); 1683 qsort(av, ac, sizeof(char *), VarWordCompare);
1684 break; 1684 break;
1685 case 'x': /* randomize */ 1685 case 'x': /* randomize */
1686 { 1686 {
1687 /* 1687 /*
1688 * We will use [ac..2] range for mod factors. This will produce 1688 * We will use [ac..2] range for mod factors. This will produce
1689 * random numbers in [(ac-1)..0] interval, and minimal 1689 * random numbers in [(ac-1)..0] interval, and minimal
1690 * reasonable value for mod factor is 2 (the mod 1 will produce 1690 * reasonable value for mod factor is 2 (the mod 1 will produce
1691 * 0 with probability 1). 1691 * 0 with probability 1).
1692 */ 1692 */
1693 for (i = ac - 1; i > 0; i--) { 1693 for (i = ac - 1; i > 0; i--) {
1694 int rndidx = random() % (i + 1); 1694 int rndidx = random() % (i + 1);
1695 char *t = av[i]; 1695 char *t = av[i];
1696 av[i] = av[rndidx]; 1696 av[i] = av[rndidx];
1697 av[rndidx] = t; 1697 av[rndidx] = t;
1698 } 1698 }
1699 } 1699 }
1700 } 1700 }
1701 } 1701 }
1702 1702
1703 for (i = 0; i < ac; i++) { 1703 for (i = 0; i < ac; i++) {
1704 Buf_AddBytes(&buf, strlen(av[i]), av[i]); 1704 Buf_AddBytes(&buf, strlen(av[i]), av[i]);
1705 if (i != ac - 1) 1705 if (i != ac - 1)
1706 Buf_AddByte(&buf, ' '); 1706 Buf_AddByte(&buf, ' ');
1707 } 1707 }
1708 1708
1709 free(as); 1709 free(as);
1710 free(av); 1710 free(av);
1711 1711
1712 return Buf_Destroy(&buf, FALSE); 1712 return Buf_Destroy(&buf, FALSE);
1713} 1713}
1714 1714
1715 1715
1716/*- 1716/*-
1717 *----------------------------------------------------------------------- 1717 *-----------------------------------------------------------------------
1718 * VarUniq -- 1718 * VarUniq --
1719 * Remove adjacent duplicate words. 1719 * Remove adjacent duplicate words.
1720 * 1720 *
1721 * Input: 1721 * Input:
1722 * str String whose words should be sorted 1722 * str String whose words should be sorted
1723 * 1723 *
1724 * Results: 1724 * Results:
1725 * A string containing the resulting words. 1725 * A string containing the resulting words.
1726 * 1726 *
1727 * Side Effects: 1727 * Side Effects:
1728 * None. 1728 * None.
1729 * 1729 *
1730 *----------------------------------------------------------------------- 1730 *-----------------------------------------------------------------------
1731 */ 1731 */
1732static char * 1732static char *
1733VarUniq(const char *str) 1733VarUniq(const char *str)
1734{ 1734{
1735 Buffer buf; /* Buffer for new string */ 1735 Buffer buf; /* Buffer for new string */
1736 char **av; /* List of words to affect */ 1736 char **av; /* List of words to affect */
1737 char *as; /* Word list memory */ 1737 char *as; /* Word list memory */
1738 int ac, i, j; 1738 int ac, i, j;
1739 1739
1740 Buf_Init(&buf, 0); 1740 Buf_Init(&buf, 0);
1741 av = brk_string(str, &ac, FALSE, &as); 1741 av = brk_string(str, &ac, FALSE, &as);
1742 1742
1743 if (ac > 1) { 1743 if (ac > 1) {
1744 for (j = 0, i = 1; i < ac; i++) 1744 for (j = 0, i = 1; i < ac; i++)
1745 if (strcmp(av[i], av[j]) != 0 && (++j != i)) 1745 if (strcmp(av[i], av[j]) != 0 && (++j != i))
1746 av[j] = av[i]; 1746 av[j] = av[i];
1747 ac = j + 1; 1747 ac = j + 1;
1748 } 1748 }
1749 1749
1750 for (i = 0; i < ac; i++) { 1750 for (i = 0; i < ac; i++) {
1751 Buf_AddBytes(&buf, strlen(av[i]), av[i]); 1751 Buf_AddBytes(&buf, strlen(av[i]), av[i]);
1752 if (i != ac - 1) 1752 if (i != ac - 1)
1753 Buf_AddByte(&buf, ' '); 1753 Buf_AddByte(&buf, ' ');
1754 } 1754 }
1755 1755
1756 free(as); 1756 free(as);
1757 free(av); 1757 free(av);
1758 1758
1759 return Buf_Destroy(&buf, FALSE); 1759 return Buf_Destroy(&buf, FALSE);
1760} 1760}
1761 1761
1762/*- 1762/*-
1763 *----------------------------------------------------------------------- 1763 *-----------------------------------------------------------------------
1764 * VarRange -- 1764 * VarRange --
1765 * Return an integer sequence 1765 * Return an integer sequence
1766 * 1766 *
1767 * Input: 1767 * Input:
1768 * str String whose words provide default range 1768 * str String whose words provide default range
1769 * ac range length, if 0 use str words 1769 * ac range length, if 0 use str words
1770 * 1770 *
1771 * Side Effects: 1771 * Side Effects:
1772 * None. 1772 * None.
1773 * 1773 *
1774 *----------------------------------------------------------------------- 1774 *-----------------------------------------------------------------------
1775 */ 1775 */
1776static char * 1776static char *
1777VarRange(const char *str, int ac) 1777VarRange(const char *str, int ac)
1778{ 1778{
1779 Buffer buf; /* Buffer for new string */ 1779 Buffer buf; /* Buffer for new string */
1780 char tmp[32]; /* each element */ 1780 char tmp[32]; /* each element */
1781 char **av; /* List of words to affect */ 1781 char **av; /* List of words to affect */
1782 char *as; /* Word list memory */ 1782 char *as; /* Word list memory */
1783 int i, n; 1783 int i, n;
1784 1784
1785 Buf_Init(&buf, 0); 1785 Buf_Init(&buf, 0);
1786 if (ac > 0) { 1786 if (ac > 0) {
1787 as = NULL; 1787 as = NULL;
1788 av = NULL; 1788 av = NULL;
1789 } else { 1789 } else {
1790 av = brk_string(str, &ac, FALSE, &as); 1790 av = brk_string(str, &ac, FALSE, &as);
1791 } 1791 }
1792 for (i = 0; i < ac; i++) { 1792 for (i = 0; i < ac; i++) {
1793 n = snprintf(tmp, sizeof(tmp), "%d", 1 + i); 1793 n = snprintf(tmp, sizeof(tmp), "%d", 1 + i);
1794 if (n >= (int)sizeof(tmp)) 1794 if (n >= (int)sizeof(tmp))
1795 break; 1795 break;
1796 Buf_AddBytes(&buf, n, tmp); 1796 Buf_AddBytes(&buf, n, tmp);
1797 if (i != ac - 1) 1797 if (i != ac - 1)
1798 Buf_AddByte(&buf, ' '); 1798 Buf_AddByte(&buf, ' ');
1799 } 1799 }
1800 1800
1801 free(as); 1801 free(as);
1802 free(av); 1802 free(av);
1803 1803
1804 return Buf_Destroy(&buf, FALSE); 1804 return Buf_Destroy(&buf, FALSE);
1805} 1805}
1806 1806
1807 1807
1808/*- 1808/*-
1809 * Parse a text part of a modifier such as the "from" and "to" in :S/from/to/ 1809 * Parse a text part of a modifier such as the "from" and "to" in :S/from/to/
1810 * or the :@ modifier. Nested variables in the text are expanded unless 1810 * or the :@ modifier. Nested variables in the text are expanded unless
1811 * VARE_NOSUBST is set. 1811 * VARE_NOSUBST is set.
1812 * 1812 *
1813 * The text part is parsed until the next delimiter. To escape the delimiter, 1813 * The text part is parsed until the next delimiter. To escape the delimiter,
1814 * a backslash or a dollar, put a backslash before it. 1814 * a backslash or a dollar, put a backslash before it.
1815 * 1815 *
1816 * Return the expanded string or NULL if the delimiter was missing. 1816 * Return the expanded string or NULL if the delimiter was missing.
1817 * If pattern is specified, handle escaped ampersands and replace unescaped 1817 * If pattern is specified, handle escaped ampersands and replace unescaped
1818 * ampersands with the lhs of the pattern (for the :S and :C modifiers). 1818 * ampersands with the lhs of the pattern (for the :S and :C modifiers).
1819 * 1819 *
1820 * If length is specified, return the string length of the buffer. 1820 * If length is specified, return the string length of the buffer.
1821 * If mpflags is specified and the last character of the pattern is a $, 1821 * If mpflags is specified and the last character of the pattern is a $,
1822 * set the VARP_ANCHOR_END bit of mpflags. 1822 * set the VARP_ANCHOR_END bit of mpflags.
1823 */ 1823 */
1824static char * 1824static char *
1825ParseModifierPart(GNode *ctxt, const char **tstr, int delim, 1825ParseModifierPart(GNode *ctxt, const char **tstr, int delim,
1826 VarEvalFlags eflags, VarPatternFlags *mpflags, 1826 VarEvalFlags eflags, VarPatternFlags *mpflags,
1827 size_t *length, ModifyWord_SubstArgs *subst) 1827 size_t *length, ModifyWord_SubstArgs *subst)
1828{ 1828{
1829 const char *cp; 1829 const char *cp;
1830 char *rstr; 1830 char *rstr;
1831 Buffer buf; 1831 Buffer buf;
1832 size_t junk; 1832 size_t junk;
1833 VarEvalFlags errnum = eflags & VARE_UNDEFERR; 1833 VarEvalFlags errnum = eflags & VARE_UNDEFERR;
1834 1834
1835 Buf_Init(&buf, 0); 1835 Buf_Init(&buf, 0);
1836 if (length == NULL) 1836 if (length == NULL)
1837 length = &junk; 1837 length = &junk;
1838 1838
1839 /* 1839 /*
1840 * Skim through until the matching delimiter is found; 1840 * Skim through until the matching delimiter is found;
1841 * pick up variable substitutions on the way. Also allow 1841 * pick up variable substitutions on the way. Also allow
1842 * backslashes to quote the delimiter, $, and \, but don't 1842 * backslashes to quote the delimiter, $, and \, but don't
1843 * touch other backslashes. 1843 * touch other backslashes.
1844 */ 1844 */
1845 for (cp = *tstr; *cp != '\0' && *cp != delim; cp++) { 1845 for (cp = *tstr; *cp != '\0' && *cp != delim; cp++) {
1846 Boolean is_escaped = cp[0] == '\\' && (cp[1] == delim || 1846 Boolean is_escaped = cp[0] == '\\' && (cp[1] == delim ||
1847 cp[1] == '\\' || cp[1] == '$' || (subst != NULL && cp[1] == '&')); 1847 cp[1] == '\\' || cp[1] == '$' || (subst != NULL && cp[1] == '&'));
1848 if (is_escaped) { 1848 if (is_escaped) {
1849 Buf_AddByte(&buf, cp[1]); 1849 Buf_AddByte(&buf, cp[1]);
1850 cp++; 1850 cp++;
1851 } else if (*cp == '$') { 1851 } else if (*cp == '$') {
1852 if (cp[1] == delim) { /* Unescaped $ at end of pattern */ 1852 if (cp[1] == delim) { /* Unescaped $ at end of pattern */
1853 if (mpflags == NULL) 1853 if (mpflags == NULL)
1854 Buf_AddByte(&buf, *cp); 1854 Buf_AddByte(&buf, *cp);
1855 else 1855 else
1856 *mpflags |= VARP_ANCHOR_END; 1856 *mpflags |= VARP_ANCHOR_END;
1857 } else { 1857 } else {
1858 if (!(eflags & VARE_NOSUBST)) { 1858 if (!(eflags & VARE_NOSUBST)) {
1859 char *cp2; 1859 char *cp2;
1860 int len; 1860 int len;
1861 void *freeIt; 1861 void *freeIt;
1862 1862
1863 /* 1863 /*
1864 * If unescaped dollar sign not before the 1864 * If unescaped dollar sign not before the
1865 * delimiter, assume it's a variable 1865 * delimiter, assume it's a variable
1866 * substitution and recurse. 1866 * substitution and recurse.
1867 */ 1867 */
1868 cp2 = Var_Parse(cp, ctxt, errnum | (eflags & VARE_WANTRES), 1868 cp2 = Var_Parse(cp, ctxt, errnum | (eflags & VARE_WANTRES),
1869 &len, &freeIt); 1869 &len, &freeIt);
1870 Buf_AddBytes(&buf, strlen(cp2), cp2); 1870 Buf_AddBytes(&buf, strlen(cp2), cp2);
1871 free(freeIt); 1871 free(freeIt);
1872 cp += len - 1; 1872 cp += len - 1;
1873 } else { 1873 } else {
1874 const char *cp2 = &cp[1]; 1874 const char *cp2 = &cp[1];
1875 1875
1876 if (*cp2 == PROPEN || *cp2 == BROPEN) { 1876 if (*cp2 == PROPEN || *cp2 == BROPEN) {
1877 /* 1877 /*
1878 * Find the end of this variable reference 1878 * Find the end of this variable reference
1879 * and suck it in without further ado. 1879 * and suck it in without further ado.
1880 * It will be interpreted later. 1880 * It will be interpreted later.
1881 */ 1881 */
1882 int have = *cp2; 1882 int have = *cp2;
1883 int want = (*cp2 == PROPEN) ? PRCLOSE : BRCLOSE; 1883 int want = (*cp2 == PROPEN) ? PRCLOSE : BRCLOSE;
1884 int depth = 1; 1884 int depth = 1;
1885 1885
1886 for (++cp2; *cp2 != '\0' && depth > 0; ++cp2) { 1886 for (++cp2; *cp2 != '\0' && depth > 0; ++cp2) {
1887 if (cp2[-1] != '\\') { 1887 if (cp2[-1] != '\\') {
1888 if (*cp2 == have) 1888 if (*cp2 == have)
1889 ++depth; 1889 ++depth;
1890 if (*cp2 == want) 1890 if (*cp2 == want)
1891 --depth; 1891 --depth;
1892 } 1892 }
1893 } 1893 }
1894 Buf_AddBytes(&buf, cp2 - cp, cp); 1894 Buf_AddBytes(&buf, cp2 - cp, cp);
1895 cp = --cp2; 1895 cp = --cp2;
1896 } else 1896 } else
1897 Buf_AddByte(&buf, *cp); 1897 Buf_AddByte(&buf, *cp);
1898 } 1898 }
1899 } 1899 }
1900 } else if (subst != NULL && *cp == '&') 1900 } else if (subst != NULL && *cp == '&')
1901 Buf_AddBytes(&buf, subst->lhsLen, subst->lhs); 1901 Buf_AddBytes(&buf, subst->lhsLen, subst->lhs);
1902 else 1902 else
1903 Buf_AddByte(&buf, *cp); 1903 Buf_AddByte(&buf, *cp);
1904 } 1904 }
1905 1905
1906 if (*cp != delim) { 1906 if (*cp != delim) {
1907 *tstr = cp; 1907 *tstr = cp;
1908 *length = 0; 1908 *length = 0;
1909 return NULL; 1909 return NULL;
1910 } 1910 }
1911 1911
1912 *tstr = ++cp; 1912 *tstr = ++cp;
1913 *length = Buf_Size(&buf); 1913 *length = Buf_Size(&buf);
1914 rstr = Buf_Destroy(&buf, FALSE); 1914 rstr = Buf_Destroy(&buf, FALSE);
1915 if (DEBUG(VAR)) 1915 if (DEBUG(VAR))
1916 fprintf(debug_file, "Modifier part: \"%s\"\n", rstr); 1916 fprintf(debug_file, "Modifier part: \"%s\"\n", rstr);
1917 return rstr; 1917 return rstr;
1918} 1918}
1919 1919
1920/*- 1920/*-
1921 *----------------------------------------------------------------------- 1921 *-----------------------------------------------------------------------
1922 * VarQuote -- 1922 * VarQuote --
1923 * Quote shell meta-characters and space characters in the string 1923 * Quote shell meta-characters and space characters in the string
1924 * if quoteDollar is set, also quote and double any '$' characters. 1924 * if quoteDollar is set, also quote and double any '$' characters.
1925 * 1925 *
1926 * Results: 1926 * Results:
1927 * The quoted string 1927 * The quoted string
1928 * 1928 *
1929 * Side Effects: 1929 * Side Effects:
1930 * None. 1930 * None.
1931 * 1931 *
1932 *----------------------------------------------------------------------- 1932 *-----------------------------------------------------------------------
1933 */ 1933 */
1934static char * 1934static char *
1935VarQuote(char *str, Boolean quoteDollar) 1935VarQuote(char *str, Boolean quoteDollar)
1936{ 1936{
1937 Buffer buf; 1937 Buffer buf;
1938 Buf_Init(&buf, 0); 1938 Buf_Init(&buf, 0);
1939 1939
1940 for (; *str != '\0'; str++) { 1940 for (; *str != '\0'; str++) {
1941 if (*str == '\n') { 1941 if (*str == '\n') {
1942 const char *newline = Shell_GetNewline(); 1942 const char *newline = Shell_GetNewline();
1943 if (newline == NULL) 1943 if (newline == NULL)
1944 newline = "\\\n"; 1944 newline = "\\\n";
1945 Buf_AddBytes(&buf, strlen(newline), newline); 1945 Buf_AddBytes(&buf, strlen(newline), newline);
1946 continue; 1946 continue;
1947 } 1947 }
1948 if (isspace((unsigned char)*str) || ismeta((unsigned char)*str)) 1948 if (isspace((unsigned char)*str) || ismeta((unsigned char)*str))
1949 Buf_AddByte(&buf, '\\'); 1949 Buf_AddByte(&buf, '\\');
1950 Buf_AddByte(&buf, *str); 1950 Buf_AddByte(&buf, *str);
1951 if (quoteDollar && *str == '$') 1951 if (quoteDollar && *str == '$')
1952 Buf_AddBytes(&buf, 2, "\\$"); 1952 Buf_AddBytes(&buf, 2, "\\$");
1953 } 1953 }
1954 1954
1955 str = Buf_Destroy(&buf, FALSE); 1955 str = Buf_Destroy(&buf, FALSE);
1956 if (DEBUG(VAR)) 1956 if (DEBUG(VAR))
1957 fprintf(debug_file, "QuoteMeta: [%s]\n", str); 1957 fprintf(debug_file, "QuoteMeta: [%s]\n", str);
1958 return str; 1958 return str;
1959} 1959}
1960 1960
1961/*- 1961/*-
1962 *----------------------------------------------------------------------- 1962 *-----------------------------------------------------------------------
1963 * VarHash -- 1963 * VarHash --
1964 * Hash the string using the MurmurHash3 algorithm. 1964 * Hash the string using the MurmurHash3 algorithm.
1965 * Output is computed using 32bit Little Endian arithmetic. 1965 * Output is computed using 32bit Little Endian arithmetic.
1966 * 1966 *
1967 * Input: 1967 * Input:
1968 * str String to modify 1968 * str String to modify
1969 * 1969 *
1970 * Results: 1970 * Results:
1971 * Hash value of str, encoded as 8 hex digits. 1971 * Hash value of str, encoded as 8 hex digits.
1972 * 1972 *
1973 * Side Effects: 1973 * Side Effects:
1974 * None. 1974 * None.
1975 * 1975 *
1976 *----------------------------------------------------------------------- 1976 *-----------------------------------------------------------------------
1977 */ 1977 */
1978static char * 1978static char *
1979VarHash(const char *str) 1979VarHash(const char *str)
1980{ 1980{
1981 static const char hexdigits[16] = "0123456789abcdef"; 1981 static const char hexdigits[16] = "0123456789abcdef";
1982 Buffer buf; 1982 Buffer buf;
1983 size_t len, len2; 1983 size_t len, len2;
1984 const unsigned char *ustr = (const unsigned char *)str; 1984 const unsigned char *ustr = (const unsigned char *)str;
1985 uint32_t h, k, c1, c2; 1985 uint32_t h, k, c1, c2;
1986 1986
1987 h = 0x971e137bU; 1987 h = 0x971e137bU;
1988 c1 = 0x95543787U; 1988 c1 = 0x95543787U;
1989 c2 = 0x2ad7eb25U; 1989 c2 = 0x2ad7eb25U;
1990 len2 = strlen(str); 1990 len2 = strlen(str);
1991 1991
1992 for (len = len2; len; ) { 1992 for (len = len2; len; ) {
1993 k = 0; 1993 k = 0;
1994 switch (len) { 1994 switch (len) {
1995 default: 1995 default:
1996 k = ((uint32_t)ustr[3] << 24) | 1996 k = ((uint32_t)ustr[3] << 24) |
1997 ((uint32_t)ustr[2] << 16) | 1997 ((uint32_t)ustr[2] << 16) |
1998 ((uint32_t)ustr[1] << 8) | 1998 ((uint32_t)ustr[1] << 8) |
1999 (uint32_t)ustr[0]; 1999 (uint32_t)ustr[0];
2000 len -= 4; 2000 len -= 4;
2001 ustr += 4; 2001 ustr += 4;
2002 break; 2002 break;
2003 case 3: 2003 case 3:
2004 k |= (uint32_t)ustr[2] << 16; 2004 k |= (uint32_t)ustr[2] << 16;
2005 /* FALLTHROUGH */ 2005 /* FALLTHROUGH */
2006 case 2: 2006 case 2:
2007 k |= (uint32_t)ustr[1] << 8; 2007 k |= (uint32_t)ustr[1] << 8;
2008 /* FALLTHROUGH */ 2008 /* FALLTHROUGH */
2009 case 1: 2009 case 1:
2010 k |= (uint32_t)ustr[0]; 2010 k |= (uint32_t)ustr[0];
2011 len = 0; 2011 len = 0;
2012 } 2012 }
2013 c1 = c1 * 5 + 0x7b7d159cU; 2013 c1 = c1 * 5 + 0x7b7d159cU;
2014 c2 = c2 * 5 + 0x6bce6396U; 2014 c2 = c2 * 5 + 0x6bce6396U;
2015 k *= c1; 2015 k *= c1;
2016 k = (k << 11) ^ (k >> 21); 2016 k = (k << 11) ^ (k >> 21);
2017 k *= c2; 2017 k *= c2;
2018 h = (h << 13) ^ (h >> 19); 2018 h = (h << 13) ^ (h >> 19);
2019 h = h * 5 + 0x52dce729U; 2019 h = h * 5 + 0x52dce729U;
2020 h ^= k; 2020 h ^= k;
2021 } 2021 }
2022 h ^= len2; 2022 h ^= len2;
2023 h *= 0x85ebca6b; 2023 h *= 0x85ebca6b;
2024 h ^= h >> 13; 2024 h ^= h >> 13;
2025 h *= 0xc2b2ae35; 2025 h *= 0xc2b2ae35;
2026 h ^= h >> 16; 2026 h ^= h >> 16;
2027 2027
2028 Buf_Init(&buf, 0); 2028 Buf_Init(&buf, 0);
2029 for (len = 0; len < 8; ++len) { 2029 for (len = 0; len < 8; ++len) {
2030 Buf_AddByte(&buf, hexdigits[h & 15]); 2030 Buf_AddByte(&buf, hexdigits[h & 15]);
2031 h >>= 4; 2031 h >>= 4;
2032 } 2032 }
2033 2033
2034 return Buf_Destroy(&buf, FALSE); 2034 return Buf_Destroy(&buf, FALSE);
2035} 2035}
2036 2036
2037static char * 2037static char *
2038VarStrftime(const char *fmt, int zulu, time_t utc) 2038VarStrftime(const char *fmt, int zulu, time_t utc)
2039{ 2039{
2040 char buf[BUFSIZ]; 2040 char buf[BUFSIZ];
2041 2041
2042 if (!utc) 2042 if (!utc)
2043 time(&utc); 2043 time(&utc);
2044 if (!*fmt) 2044 if (!*fmt)
2045 fmt = "%c"; 2045 fmt = "%c";
2046 strftime(buf, sizeof(buf), fmt, zulu ? gmtime(&utc) : localtime(&utc)); 2046 strftime(buf, sizeof(buf), fmt, zulu ? gmtime(&utc) : localtime(&utc));
2047 2047
2048 buf[sizeof(buf) - 1] = '\0'; 2048 buf[sizeof(buf) - 1] = '\0';
2049 return bmake_strdup(buf); 2049 return bmake_strdup(buf);
2050} 2050}
2051 2051
2052typedef struct { 2052typedef struct {
2053 /* const parameters */ 2053 /* const parameters */
2054 int startc; /* '\0' or '{' or '(' */ 2054 int startc; /* '\0' or '{' or '(' */
2055 int endc; 2055 int endc;
2056 Var *v; 2056 Var *v;
2057 GNode *ctxt; 2057 GNode *ctxt;
2058 VarEvalFlags eflags; 2058 VarEvalFlags eflags;
2059 int *lengthPtr; 2059 int *lengthPtr;
2060 void **freePtr; 2060 void **freePtr;
2061 2061
2062 /* read-write */ 2062 /* read-write */
2063 char *nstr; 2063 char *nstr;
2064 const char *start; 2064 const char *start;
2065 const char *cp; /* The position where parsing continues 2065 const char *cp; /* The position where parsing continues
2066 * after the current modifier. */ 2066 * after the current modifier. */
2067 char termc; /* Character which terminated scan */ 2067 char termc; /* Character which terminated scan */
2068 char missing_delim; /* For error reporting */ 2068 char missing_delim; /* For error reporting */
2069 int modifier; /* that we are processing */ 2069 int modifier; /* that we are processing */
2070 2070
2071 Byte sep; /* Word separator in expansions */ 2071 Byte sep; /* Word separator in expansions */
2072 Boolean oneBigWord; /* TRUE if we will treat the variable as a 2072 Boolean oneBigWord; /* TRUE if we will treat the variable as a
2073 * single big word, even if it contains 2073 * single big word, even if it contains
2074 * embedded spaces (as opposed to the 2074 * embedded spaces (as opposed to the
2075 * usual behaviour of treating it as 2075 * usual behaviour of treating it as
2076 * several space-separated words). */ 2076 * several space-separated words). */
2077 2077
2078 /* result */ 2078 /* result */
2079 char *newStr; /* New value to return */ 2079 char *newStr; /* New value to return */
2080} ApplyModifiersState; 2080} ApplyModifiersState;
2081 2081
2082/* we now have some modifiers with long names */ 2082/* we now have some modifiers with long names */
2083#define STRMOD_MATCH(s, want, n) \ 2083#define STRMOD_MATCH(s, want, n) \
2084 (strncmp(s, want, n) == 0 && (s[n] == st->endc || s[n] == ':')) 2084 (strncmp(s, want, n) == 0 && (s[n] == st->endc || s[n] == ':'))
2085#define STRMOD_MATCHX(s, want, n) \ 2085#define STRMOD_MATCHX(s, want, n) \
2086 (strncmp(s, want, n) == 0 && \ 2086 (strncmp(s, want, n) == 0 && \
2087 (s[n] == st->endc || s[n] == ':' || s[n] == '=')) 2087 (s[n] == st->endc || s[n] == ':' || s[n] == '='))
2088#define CHARMOD_MATCH(c) (c == st->endc || c == ':') 2088#define CHARMOD_MATCH(c) (c == st->endc || c == ':')
2089 2089
2090/* :@var@...${var}...@ */ 2090/* :@var@...${var}...@ */
2091static Boolean 2091static Boolean
2092ApplyModifier_Loop(const char *mod, ApplyModifiersState *st) { 2092ApplyModifier_Loop(const char *mod, ApplyModifiersState *st) {
2093 ModifyWord_LoopArgs args; 2093 ModifyWord_LoopArgs args;
2094 2094
2095 args.ctx = st->ctxt; 2095 args.ctx = st->ctxt;
2096 st->cp = mod + 1; 2096 st->cp = mod + 1;
2097 char delim = '@'; 2097 char delim = '@';
2098 args.tvar = ParseModifierPart(st->ctxt, &st->cp, delim, 2098 args.tvar = ParseModifierPart(st->ctxt, &st->cp, delim,
2099 st->eflags | VARE_NOSUBST, 2099 st->eflags | VARE_NOSUBST,
2100 NULL, NULL, NULL); 2100 NULL, NULL, NULL);
2101 if (args.tvar == NULL) { 2101 if (args.tvar == NULL) {
2102 st->missing_delim = delim; 2102 st->missing_delim = delim;
2103 return FALSE; 2103 return FALSE;
2104 } 2104 }
2105 2105
2106 args.str = ParseModifierPart(st->ctxt, &st->cp, delim, 2106 args.str = ParseModifierPart(st->ctxt, &st->cp, delim,
2107 st->eflags | VARE_NOSUBST, 2107 st->eflags | VARE_NOSUBST,
2108 NULL, NULL, NULL); 2108 NULL, NULL, NULL);
2109 if (args.str == NULL) { 2109 if (args.str == NULL) {
2110 st->missing_delim = delim; 2110 st->missing_delim = delim;
2111 return FALSE; 2111 return FALSE;
2112 } 2112 }
2113 2113
2114 st->termc = *st->cp; 2114 st->termc = *st->cp;
2115 2115
2116 args.eflags = st->eflags & (VARE_UNDEFERR | VARE_WANTRES); 2116 args.eflags = st->eflags & (VARE_UNDEFERR | VARE_WANTRES);
2117 int prev_sep = st->sep; 2117 int prev_sep = st->sep;
2118 st->sep = ' '; /* XXX: this is inconsistent */ 2118 st->sep = ' '; /* XXX: this is inconsistent */
2119 st->newStr = ModifyWords(st->ctxt, st->sep, st->oneBigWord, st->nstr, 2119 st->newStr = ModifyWords(st->ctxt, st->sep, st->oneBigWord, st->nstr,
2120 ModifyWord_Loop, &args); 2120 ModifyWord_Loop, &args);
2121 st->sep = prev_sep; 2121 st->sep = prev_sep;
2122 Var_Delete(args.tvar, st->ctxt); 2122 Var_Delete(args.tvar, st->ctxt);
2123 free(args.tvar); 2123 free(args.tvar);
2124 free(args.str); 2124 free(args.str);
2125 return TRUE; 2125 return TRUE;
2126} 2126}
2127 2127
2128/* :Ddefined or :Uundefined */ 2128/* :Ddefined or :Uundefined */
2129static void 2129static void
2130ApplyModifier_Defined(const char *mod, ApplyModifiersState *st) 2130ApplyModifier_Defined(const char *mod, ApplyModifiersState *st)
2131{ 2131{
2132 Buffer buf; /* Buffer for patterns */ 2132 Buffer buf; /* Buffer for patterns */
2133 VarEvalFlags neflags; 2133 VarEvalFlags neflags;
2134 2134
2135 if (st->eflags & VARE_WANTRES) { 2135 if (st->eflags & VARE_WANTRES) {
2136 Boolean wantres; 2136 Boolean wantres;
2137 if (*mod == 'U') 2137 if (*mod == 'U')
2138 wantres = ((st->v->flags & VAR_JUNK) != 0); 2138 wantres = ((st->v->flags & VAR_JUNK) != 0);
2139 else 2139 else
2140 wantres = ((st->v->flags & VAR_JUNK) == 0); 2140 wantres = ((st->v->flags & VAR_JUNK) == 0);
2141 neflags = st->eflags & ~VARE_WANTRES; 2141 neflags = st->eflags & ~VARE_WANTRES;
2142 if (wantres) 2142 if (wantres)
2143 neflags |= VARE_WANTRES; 2143 neflags |= VARE_WANTRES;
2144 } else 2144 } else
2145 neflags = st->eflags; 2145 neflags = st->eflags;
2146 2146
2147 /* 2147 /*
2148 * Pass through mod looking for 1) escaped delimiters, 2148 * Pass through mod looking for 1) escaped delimiters,
2149 * '$'s and backslashes (place the escaped character in 2149 * '$'s and backslashes (place the escaped character in
2150 * uninterpreted) and 2) unescaped $'s that aren't before 2150 * uninterpreted) and 2) unescaped $'s that aren't before
2151 * the delimiter (expand the variable substitution). 2151 * the delimiter (expand the variable substitution).
2152 * The result is left in the Buffer buf. 2152 * The result is left in the Buffer buf.
2153 */ 2153 */
2154 Buf_Init(&buf, 0); 2154 Buf_Init(&buf, 0);
2155 for (st->cp = mod + 1; 2155 for (st->cp = mod + 1;
2156 *st->cp != st->endc && *st->cp != ':' && *st->cp != '\0'; 2156 *st->cp != st->endc && *st->cp != ':' && *st->cp != '\0';
2157 st->cp++) { 2157 st->cp++) {
2158 if (*st->cp == '\\' && 2158 if (*st->cp == '\\' &&
2159 (st->cp[1] == ':' || st->cp[1] == '$' || st->cp[1] == st->endc || 2159 (st->cp[1] == ':' || st->cp[1] == '$' || st->cp[1] == st->endc ||
2160 st->cp[1] == '\\')) { 2160 st->cp[1] == '\\')) {
2161 Buf_AddByte(&buf, st->cp[1]); 2161 Buf_AddByte(&buf, st->cp[1]);
2162 st->cp++; 2162 st->cp++;
2163 } else if (*st->cp == '$') { 2163 } else if (*st->cp == '$') {
2164 /* 2164 /*
2165 * If unescaped dollar sign, assume it's a 2165 * If unescaped dollar sign, assume it's a
2166 * variable substitution and recurse. 2166 * variable substitution and recurse.
2167 */ 2167 */
2168 char *cp2; 2168 char *cp2;
2169 int len; 2169 int len;
2170 void *freeIt; 2170 void *freeIt;
2171 2171
2172 cp2 = Var_Parse(st->cp, st->ctxt, neflags, &len, &freeIt); 2172 cp2 = Var_Parse(st->cp, st->ctxt, neflags, &len, &freeIt);
2173 Buf_AddBytes(&buf, strlen(cp2), cp2); 2173 Buf_AddBytes(&buf, strlen(cp2), cp2);
2174 free(freeIt); 2174 free(freeIt);
2175 st->cp += len - 1; 2175 st->cp += len - 1;
2176 } else { 2176 } else {
2177 Buf_AddByte(&buf, *st->cp); 2177 Buf_AddByte(&buf, *st->cp);
2178 } 2178 }
2179 } 2179 }
2180 2180
2181 st->termc = *st->cp; 2181 st->termc = *st->cp;
2182 2182
2183 if (st->v->flags & VAR_JUNK) 2183 if (st->v->flags & VAR_JUNK)
2184 st->v->flags |= VAR_KEEP; 2184 st->v->flags |= VAR_KEEP;
2185 if (neflags & VARE_WANTRES) { 2185 if (neflags & VARE_WANTRES) {
2186 st->newStr = Buf_Destroy(&buf, FALSE); 2186 st->newStr = Buf_Destroy(&buf, FALSE);
2187 } else { 2187 } else {
2188 st->newStr = st->nstr; 2188 st->newStr = st->nstr;
2189 Buf_Destroy(&buf, TRUE); 2189 Buf_Destroy(&buf, TRUE);
2190 } 2190 }
2191} 2191}
2192 2192
2193/* :gmtime */ 2193/* :gmtime */
2194static Boolean 2194static Boolean
2195ApplyModifier_Gmtime(const char *mod, ApplyModifiersState *st) 2195ApplyModifier_Gmtime(const char *mod, ApplyModifiersState *st)
2196{ 2196{
2197 time_t utc; 2197 time_t utc;
2198 char *ep; 2198 char *ep;
2199 2199
2200 st->cp = mod + 1; /* make sure it is set */ 2200 st->cp = mod + 1; /* make sure it is set */
2201 if (!STRMOD_MATCHX(mod, "gmtime", 6)) 2201 if (!STRMOD_MATCHX(mod, "gmtime", 6))
2202 return FALSE; 2202 return FALSE;
2203 if (mod[6] == '=') { 2203 if (mod[6] == '=') {
2204 utc = strtoul(mod + 7, &ep, 10); 2204 utc = strtoul(mod + 7, &ep, 10);
2205 st->cp = ep; 2205 st->cp = ep;
2206 } else { 2206 } else {
2207 utc = 0; 2207 utc = 0;
2208 st->cp = mod + 6; 2208 st->cp = mod + 6;
2209 } 2209 }
2210 st->newStr = VarStrftime(st->nstr, 1, utc); 2210 st->newStr = VarStrftime(st->nstr, 1, utc);
2211 st->termc = *st->cp; 2211 st->termc = *st->cp;
2212 return TRUE; 2212 return TRUE;
2213} 2213}
2214 2214
2215/* :localtime */ 2215/* :localtime */
2216static Boolean 2216static Boolean
2217ApplyModifier_Localtime(const char *mod, ApplyModifiersState *st) 2217ApplyModifier_Localtime(const char *mod, ApplyModifiersState *st)
2218{ 2218{
2219 time_t utc; 2219 time_t utc;
2220 char *ep; 2220 char *ep;
2221 2221
2222 st->cp = mod + 1; /* make sure it is set */ 2222 st->cp = mod + 1; /* make sure it is set */
2223 if (!STRMOD_MATCHX(mod, "localtime", 9)) 2223 if (!STRMOD_MATCHX(mod, "localtime", 9))
2224 return FALSE; 2224 return FALSE;
2225 2225
2226 if (mod[9] == '=') { 2226 if (mod[9] == '=') {
2227 utc = strtoul(mod + 10, &ep, 10); 2227 utc = strtoul(mod + 10, &ep, 10);
2228 st->cp = ep; 2228 st->cp = ep;
2229 } else { 2229 } else {
2230 utc = 0; 2230 utc = 0;
2231 st->cp = mod + 9; 2231 st->cp = mod + 9;
2232 } 2232 }
2233 st->newStr = VarStrftime(st->nstr, 0, utc); 2233 st->newStr = VarStrftime(st->nstr, 0, utc);
2234 st->termc = *st->cp; 2234 st->termc = *st->cp;
2235 return TRUE; 2235 return TRUE;
2236} 2236}
2237 2237
2238/* :hash */ 2238/* :hash */
2239static Boolean 2239static Boolean
2240ApplyModifier_Hash(const char *mod, ApplyModifiersState *st) 2240ApplyModifier_Hash(const char *mod, ApplyModifiersState *st)
2241{ 2241{
2242 st->cp = mod + 1; /* make sure it is set */ 2242 st->cp = mod + 1; /* make sure it is set */
2243 if (!STRMOD_MATCH(mod, "hash", 4)) 2243 if (!STRMOD_MATCH(mod, "hash", 4))
2244 return FALSE; 2244 return FALSE;
2245 st->newStr = VarHash(st->nstr); 2245 st->newStr = VarHash(st->nstr);
2246 st->cp = mod + 4; 2246 st->cp = mod + 4;
2247 st->termc = *st->cp; 2247 st->termc = *st->cp;
2248 return TRUE; 2248 return TRUE;
2249} 2249}
2250 2250
2251/* :P */ 2251/* :P */
2252static void 2252static void
2253ApplyModifier_Path(const char *mod, ApplyModifiersState *st) 2253ApplyModifier_Path(const char *mod, ApplyModifiersState *st)
2254{ 2254{
2255 if ((st->v->flags & VAR_JUNK) != 0) 2255 if ((st->v->flags & VAR_JUNK) != 0)
2256 st->v->flags |= VAR_KEEP; 2256 st->v->flags |= VAR_KEEP;
2257 GNode *gn = Targ_FindNode(st->v->name, TARG_NOCREATE); 2257 GNode *gn = Targ_FindNode(st->v->name, TARG_NOCREATE);
2258 if (gn == NULL || gn->type & OP_NOPATH) { 2258 if (gn == NULL || gn->type & OP_NOPATH) {
2259 st->newStr = NULL; 2259 st->newStr = NULL;
2260 } else if (gn->path) { 2260 } else if (gn->path) {
2261 st->newStr = bmake_strdup(gn->path); 2261 st->newStr = bmake_strdup(gn->path);
2262 } else { 2262 } else {
2263 st->newStr = Dir_FindFile(st->v->name, Suff_FindPath(gn)); 2263 st->newStr = Dir_FindFile(st->v->name, Suff_FindPath(gn));
2264 } 2264 }
2265 if (!st->newStr) 2265 if (!st->newStr)
2266 st->newStr = bmake_strdup(st->v->name); 2266 st->newStr = bmake_strdup(st->v->name);
2267 st->cp = mod + 1; 2267 st->cp = mod + 1;
2268 st->termc = *st->cp; 2268 st->termc = *st->cp;
2269} 2269}
2270 2270
2271/* :!cmd! */ 2271/* :!cmd! */
2272static Boolean 2272static Boolean
2273ApplyModifier_Exclam(const char *mod, ApplyModifiersState *st) 2273ApplyModifier_Exclam(const char *mod, ApplyModifiersState *st)
2274{ 2274{
2275 st->cp = mod + 1; 2275 st->cp = mod + 1;
2276 char delim = '!'; 2276 char delim = '!';
2277 char *cmd = ParseModifierPart(st->ctxt, &st->cp, delim, st->eflags, 2277 char *cmd = ParseModifierPart(st->ctxt, &st->cp, delim, st->eflags,
2278 NULL, NULL, NULL); 2278 NULL, NULL, NULL);
2279 if (cmd == NULL) { 2279 if (cmd == NULL) {
2280 st->missing_delim = delim; 2280 st->missing_delim = delim;
2281 return FALSE; 2281 return FALSE;
2282 } 2282 }
2283 2283
2284 const char *emsg = NULL; 2284 const char *emsg = NULL;
2285 if (st->eflags & VARE_WANTRES) 2285 if (st->eflags & VARE_WANTRES)
2286 st->newStr = Cmd_Exec(cmd, &emsg); 2286 st->newStr = Cmd_Exec(cmd, &emsg);
2287 else 2287 else
2288 st->newStr = varNoError; 2288 st->newStr = varNoError;
2289 free(cmd); 2289 free(cmd);
2290 2290
2291 if (emsg) 2291 if (emsg)
2292 Error(emsg, st->nstr); 2292 Error(emsg, st->nstr);
2293 2293
2294 st->termc = *st->cp; 2294 st->termc = *st->cp;
2295 if (st->v->flags & VAR_JUNK) 2295 if (st->v->flags & VAR_JUNK)
2296 st->v->flags |= VAR_KEEP; 2296 st->v->flags |= VAR_KEEP;
2297 return TRUE; 2297 return TRUE;
2298} 2298}
2299 2299
2300/* :range */ 2300/* :range */
2301static Boolean 2301static Boolean
2302ApplyModifier_Range(const char *mod, ApplyModifiersState *st) 2302ApplyModifier_Range(const char *mod, ApplyModifiersState *st)
2303{ 2303{
2304 int n; 2304 int n;
2305 char *ep; 2305 char *ep;
2306 2306
2307 st->cp = mod + 1; /* make sure it is set */ 2307 st->cp = mod + 1; /* make sure it is set */
2308 if (!STRMOD_MATCHX(mod, "range", 5)) 2308 if (!STRMOD_MATCHX(mod, "range", 5))
2309 return FALSE; 2309 return FALSE;
2310 2310
2311 if (mod[5] == '=') { 2311 if (mod[5] == '=') {
2312 n = strtoul(mod + 6, &ep, 10); 2312 n = strtoul(mod + 6, &ep, 10);
2313 st->cp = ep; 2313 st->cp = ep;
2314 } else { 2314 } else {
2315 n = 0; 2315 n = 0;
2316 st->cp = mod + 5; 2316 st->cp = mod + 5;
2317 } 2317 }
2318 st->newStr = VarRange(st->nstr, n); 2318 st->newStr = VarRange(st->nstr, n);
2319 st->termc = *st->cp; 2319 st->termc = *st->cp;
2320 return TRUE; 2320 return TRUE;
2321} 2321}
2322 2322
2323/* :Mpattern or :Npattern */ 2323/* :Mpattern or :Npattern */
2324static void 2324static void
2325ApplyModifier_Match(const char *mod, ApplyModifiersState *st) 2325ApplyModifier_Match(const char *mod, ApplyModifiersState *st)
2326{ 2326{
2327 Boolean copy = FALSE; /* pattern should be, or has been, copied */ 2327 Boolean copy = FALSE; /* pattern should be, or has been, copied */
2328 Boolean needSubst = FALSE; 2328 Boolean needSubst = FALSE;
2329 /* 2329 /*
2330 * In the loop below, ignore ':' unless we are at (or back to) the 2330 * In the loop below, ignore ':' unless we are at (or back to) the
2331 * original brace level. 2331 * original brace level.
2332 * XXX This will likely not work right if $() and ${} are intermixed. 2332 * XXX This will likely not work right if $() and ${} are intermixed.
2333 */ 2333 */
2334 int nest = 1; 2334 int nest = 1;
2335 for (st->cp = mod + 1; 2335 for (st->cp = mod + 1;
2336 *st->cp != '\0' && !(*st->cp == ':' && nest == 1); 2336 *st->cp != '\0' && !(*st->cp == ':' && nest == 1);
2337 st->cp++) { 2337 st->cp++) {
2338 if (*st->cp == '\\' && 2338 if (*st->cp == '\\' &&
2339 (st->cp[1] == ':' || st->cp[1] == st->endc || 2339 (st->cp[1] == ':' || st->cp[1] == st->endc ||
2340 st->cp[1] == st->startc)) { 2340 st->cp[1] == st->startc)) {
2341 if (!needSubst) 2341 if (!needSubst)
2342 copy = TRUE; 2342 copy = TRUE;
2343 st->cp++; 2343 st->cp++;
2344 continue; 2344 continue;
2345 } 2345 }

cvs diff -r1.24 -r1.25 src/usr.bin/make/unit-tests/modmisc.mk (switch to unified diff)

--- src/usr.bin/make/unit-tests/modmisc.mk 2020/07/26 12:18:11 1.24
+++ src/usr.bin/make/unit-tests/modmisc.mk 2020/07/26 12:19:37 1.25
@@ -1,215 +1,214 @@ @@ -1,215 +1,214 @@
1# $Id: modmisc.mk,v 1.24 2020/07/26 12:18:11 rillig Exp $ 1# $Id: modmisc.mk,v 1.25 2020/07/26 12:19:37 rillig Exp $
2# 2#
3# miscellaneous modifier tests 3# miscellaneous modifier tests
4 4
5# do not put any dirs in this list which exist on some 5# do not put any dirs in this list which exist on some
6# but not all target systems - an exists() check is below. 6# but not all target systems - an exists() check is below.
7path=:/bin:/tmp::/:.:/no/such/dir:. 7path=:/bin:/tmp::/:.:/no/such/dir:.
8# strip cwd from path. 8# strip cwd from path.
9MOD_NODOT=S/:/ /g:N.:ts: 9MOD_NODOT=S/:/ /g:N.:ts:
10# and decorate, note that $'s need to be doubled. Also note that  10# and decorate, note that $'s need to be doubled. Also note that
11# the modifier_variable can be used with other modifiers. 11# the modifier_variable can be used with other modifiers.
12MOD_NODOTX=S/:/ /g:N.:@d@'$$d'@ 12MOD_NODOTX=S/:/ /g:N.:@d@'$$d'@
13# another mod - pretend it is more interesting 13# another mod - pretend it is more interesting
14MOD_HOMES=S,/home/,/homes/, 14MOD_HOMES=S,/home/,/homes/,
15MOD_OPT=@d@$${exists($$d):?$$d:$${d:S,/usr,/opt,}}@ 15MOD_OPT=@d@$${exists($$d):?$$d:$${d:S,/usr,/opt,}}@
16MOD_SEP=S,:, ,g 16MOD_SEP=S,:, ,g
17 17
18all: modvar modvarloop modsysv mod-HTE emptyvar undefvar 18all: modvar modvarloop modsysv mod-HTE emptyvar undefvar
19all: mod-subst 19all: mod-subst
20all: mod-regex 20all: mod-regex
21all: mod-loop-varname mod-loop-resolve mod-loop-varname-dollar 21all: mod-loop-varname mod-loop-resolve mod-loop-varname-dollar
22all: mod-subst-dollar mod-loop-dollar 22all: mod-subst-dollar mod-loop-dollar
23all: mod-regex-limits 23all: mod-regex-limits
24all: mod-regex-errors 24all: mod-regex-errors
25all: mod-assign 25all: mod-assign
26all: mod-assign-nested 26all: mod-assign-nested
27all: mod-tu-space 27all: mod-tu-space
28all: mod-quote 28all: mod-quote
29all: mod-break-many-words 29all: mod-break-many-words
30 30
31# See also sysv.mk. 31# See also sysv.mk.
32modsysv: 32modsysv:
33 @echo "The answer is ${libfoo.a:L:libfoo.a=42}" 33 @echo "The answer is ${libfoo.a:L:libfoo.a=42}"
34 34
35# Demonstrates modifiers that are given indirectly from a variable. 35# Demonstrates modifiers that are given indirectly from a variable.
36modvar: 36modvar:
37 @echo "path='${path}'" 37 @echo "path='${path}'"
38 @echo "path='${path:${MOD_NODOT}}'" 38 @echo "path='${path:${MOD_NODOT}}'"
39 @echo "path='${path:S,home,homes,:${MOD_NODOT}}'" 39 @echo "path='${path:S,home,homes,:${MOD_NODOT}}'"
40 @echo "path=${path:${MOD_NODOTX}:ts:}" 40 @echo "path=${path:${MOD_NODOTX}:ts:}"
41 @echo "path=${path:${MOD_HOMES}:${MOD_NODOTX}:ts:}" 41 @echo "path=${path:${MOD_HOMES}:${MOD_NODOTX}:ts:}"
42 42
43.for d in ${path:${MOD_SEP}:N.} /usr/xbin 43.for d in ${path:${MOD_SEP}:N.} /usr/xbin
44path_$d?= ${d:${MOD_OPT}:${MOD_HOMES}}/ 44path_$d?= ${d:${MOD_OPT}:${MOD_HOMES}}/
45paths+= ${d:${MOD_OPT}:${MOD_HOMES}} 45paths+= ${d:${MOD_OPT}:${MOD_HOMES}}
46.endfor 46.endfor
47 47
48modvarloop: 48modvarloop:
49 @echo "path_/usr/xbin=${path_/usr/xbin}" 49 @echo "path_/usr/xbin=${path_/usr/xbin}"
50 @echo "paths=${paths}" 50 @echo "paths=${paths}"
51 @echo "PATHS=${paths:tu}" 51 @echo "PATHS=${paths:tu}"
52 52
53PATHNAMES= a/b/c def a.b.c a.b/c a a.a .gitignore a a.a 53PATHNAMES= a/b/c def a.b.c a.b/c a a.a .gitignore a a.a
54mod-HTE: 54mod-HTE:
55 @echo "dirname of '"${PATHNAMES:Q}"' is '"${PATHNAMES:H:Q}"'" 55 @echo "dirname of '"${PATHNAMES:Q}"' is '"${PATHNAMES:H:Q}"'"
56 @echo "basename of '"${PATHNAMES:Q}"' is '"${PATHNAMES:T:Q}"'" 56 @echo "basename of '"${PATHNAMES:Q}"' is '"${PATHNAMES:T:Q}"'"
57 @echo "suffix of '"${PATHNAMES:Q}"' is '"${PATHNAMES:E:Q}"'" 57 @echo "suffix of '"${PATHNAMES:Q}"' is '"${PATHNAMES:E:Q}"'"
58 @echo "root of '"${PATHNAMES:Q}"' is '"${PATHNAMES:R:Q}"'" 58 @echo "root of '"${PATHNAMES:Q}"' is '"${PATHNAMES:R:Q}"'"
59 59
60# When a modifier is applied to the "" variable, the result is discarded. 60# When a modifier is applied to the "" variable, the result is discarded.
61emptyvar: 61emptyvar:
62 @echo S:${:S,^$,empty,} 62 @echo S:${:S,^$,empty,}
63 @echo C:${:C,^$,empty,} 63 @echo C:${:C,^$,empty,}
64 @echo @:${:@var@${var}@} 64 @echo @:${:@var@${var}@}
65 65
66# The :U modifier turns even the "" variable into something that has a value. 66# The :U modifier turns even the "" variable into something that has a value.
67# The resulting variable is empty, but is still considered to contain a 67# The resulting variable is empty, but is still considered to contain a
68# single empty word. This word can be accessed by the :S and :C modifiers, 68# single empty word. This word can be accessed by the :S and :C modifiers,
69# but not by the :@ modifier since it explicitly skips empty words. 69# but not by the :@ modifier since it explicitly skips empty words.
70undefvar: 70undefvar:
71 @echo S:${:U:S,^$,empty,} 71 @echo S:${:U:S,^$,empty,}
72 @echo C:${:U:C,^$,empty,} 72 @echo C:${:U:C,^$,empty,}
73 @echo @:${:U:@var@empty@} 73 @echo @:${:U:@var@empty@}
74 74
75WORDS= sequences of letters 75WORDS= sequences of letters
76# FIXME: The "*" in "letters" must not be substituted because of the 1. 76.if ${WORDS:S,e,*,1} != "s*quences of letters"
77.if ${WORDS:S,e,*,1} != "s*quences of l*tters" 
78.warning ${WORDS:S,e,*,1} 77.warning ${WORDS:S,e,*,1}
79.endif 78.endif
80.if ${WORDS:S,e,*,} != "s*quences of l*tters" 79.if ${WORDS:S,e,*,} != "s*quences of l*tters"
81.error oops 80.error oops
82.endif 81.endif
83.if ${WORDS:S,e,*,g} != "s*qu*nc*s of l*tt*rs" 82.if ${WORDS:S,e,*,g} != "s*qu*nc*s of l*tt*rs"
84.error oops 83.error oops
85.endif 84.endif
86.if ${WORDS:S,^sequ,occurr,} != "occurrences of letters" 85.if ${WORDS:S,^sequ,occurr,} != "occurrences of letters"
87.error oops 86.error oops
88.endif 87.endif
89.if ${WORDS:S,^of,with,} != "sequences with letters" 88.if ${WORDS:S,^of,with,} != "sequences with letters"
90.error oops 89.error oops
91.endif 90.endif
92.if ${WORDS:S,^office,does not match,} != ${WORDS} 91.if ${WORDS:S,^office,does not match,} != ${WORDS}
93.error oops 92.error oops
94.endif 93.endif
95 94
96mod-subst: 95mod-subst:
97 @echo $@: 96 @echo $@:
98 @echo :${:Ua b b c:S,a b,,:Q}: 97 @echo :${:Ua b b c:S,a b,,:Q}:
99 @echo :${:Ua b b c:S,a b,,1:Q}: 98 @echo :${:Ua b b c:S,a b,,1:Q}:
100 @echo :${:Ua b b c:S,a b,,W:Q}: 99 @echo :${:Ua b b c:S,a b,,W:Q}:
101 @echo :${:Ua b b c:S,b,,g:Q}: 100 @echo :${:Ua b b c:S,b,,g:Q}:
102 @echo :${:U1 2 3 1 2 3:S,1 2,___,Wg:S,_,x,:Q}: 101 @echo :${:U1 2 3 1 2 3:S,1 2,___,Wg:S,_,x,:Q}:
103 @echo ${:U12345:S,,sep,g:Q} 102 @echo ${:U12345:S,,sep,g:Q}
104 103
105mod-regex: 104mod-regex:
106 @echo $@: 105 @echo $@:
107 @echo :${:Ua b b c:C,a b,,:Q}: 106 @echo :${:Ua b b c:C,a b,,:Q}:
108 @echo :${:Ua b b c:C,a b,,1:Q}: 107 @echo :${:Ua b b c:C,a b,,1:Q}:
109 @echo :${:Ua b b c:C,a b,,W:Q}: 108 @echo :${:Ua b b c:C,a b,,W:Q}:
110 @echo :${:Uword1 word2:C,****,____,g:C,word,____,:Q}: 109 @echo :${:Uword1 word2:C,****,____,g:C,word,____,:Q}:
111 @echo :${:Ua b b c:C,b,,g:Q}: 110 @echo :${:Ua b b c:C,b,,g:Q}:
112 @echo :${:U1 2 3 1 2 3:C,1 2,___,Wg:C,_,x,:Q}: 111 @echo :${:U1 2 3 1 2 3:C,1 2,___,Wg:C,_,x,:Q}:
113 112
114# In the :@ modifier, the name of the loop variable can even be generated 113# In the :@ modifier, the name of the loop variable can even be generated
115# dynamically. There's no practical use-case for this, and hopefully nobody 114# dynamically. There's no practical use-case for this, and hopefully nobody
116# will ever depend on this, but technically it's possible. 115# will ever depend on this, but technically it's possible.
117mod-loop-varname: 116mod-loop-varname:
118 @echo :${:Uone two three:@${:Ubar:S,b,v,}@+${var}+@:Q}: 117 @echo :${:Uone two three:@${:Ubar:S,b,v,}@+${var}+@:Q}:
119 118
120# The :@ modifier resolves the variables a little more often than expected. 119# The :@ modifier resolves the variables a little more often than expected.
121# In particular, it resolves _all_ variables from the context, and not only 120# In particular, it resolves _all_ variables from the context, and not only
122# the loop variable (in this case v). 121# the loop variable (in this case v).
123# 122#
124# The d means direct reference, the i means indirect reference. 123# The d means direct reference, the i means indirect reference.
125RESOLVE= ${RES1} $${RES1} 124RESOLVE= ${RES1} $${RES1}
126RES1= 1d${RES2} 1i$${RES2} 125RES1= 1d${RES2} 1i$${RES2}
127RES2= 2d${RES3} 2i$${RES3} 126RES2= 2d${RES3} 2i$${RES3}
128RES3= 3 127RES3= 3
129 128
130mod-loop-resolve: 129mod-loop-resolve:
131 @echo $@:${RESOLVE:@v@w${v}w@:Q}: 130 @echo $@:${RESOLVE:@v@w${v}w@:Q}:
132 131
133# Until 2020-07-20, the variable name of the :@ modifier could end with one 132# Until 2020-07-20, the variable name of the :@ modifier could end with one
134# or two dollar signs, which were silently ignored. 133# or two dollar signs, which were silently ignored.
135# There's no point in allowing a dollar sign in that position. 134# There's no point in allowing a dollar sign in that position.
136mod-loop-varname-dollar: 135mod-loop-varname-dollar:
137 @echo $@:${1 2 3:L:@v$@($v)@:Q}. 136 @echo $@:${1 2 3:L:@v$@($v)@:Q}.
138 @echo $@:${1 2 3:L:@v$$@($v)@:Q}. 137 @echo $@:${1 2 3:L:@v$$@($v)@:Q}.
139 @echo $@:${1 2 3:L:@v$$$@($v)@:Q}. 138 @echo $@:${1 2 3:L:@v$$$@($v)@:Q}.
140 139
141# No matter how many dollar characters there are, they all get merged 140# No matter how many dollar characters there are, they all get merged
142# into a single dollar by the :S modifier. 141# into a single dollar by the :S modifier.
143mod-subst-dollar: 142mod-subst-dollar:
144 @echo $@:${:U1:S,^,$,:Q}: 143 @echo $@:${:U1:S,^,$,:Q}:
145 @echo $@:${:U2:S,^,$$,:Q}: 144 @echo $@:${:U2:S,^,$$,:Q}:
146 @echo $@:${:U3:S,^,$$$,:Q}: 145 @echo $@:${:U3:S,^,$$$,:Q}:
147 @echo $@:${:U4:S,^,$$$$,:Q}: 146 @echo $@:${:U4:S,^,$$$$,:Q}:
148 @echo $@:${:U5:S,^,$$$$$,:Q}: 147 @echo $@:${:U5:S,^,$$$$$,:Q}:
149 @echo $@:${:U6:S,^,$$$$$$,:Q}: 148 @echo $@:${:U6:S,^,$$$$$$,:Q}:
150 @echo $@:${:U7:S,^,$$$$$$$,:Q}: 149 @echo $@:${:U7:S,^,$$$$$$$,:Q}:
151 @echo $@:${:U8:S,^,$$$$$$$$,:Q}: 150 @echo $@:${:U8:S,^,$$$$$$$$,:Q}:
152# This generates no dollar at all: 151# This generates no dollar at all:
153 @echo $@:${:UU8:S,^,${:U$$$$$$$$},:Q}: 152 @echo $@:${:UU8:S,^,${:U$$$$$$$$},:Q}:
154# Here is an alternative way to generate dollar characters. 153# Here is an alternative way to generate dollar characters.
155# It's unexpectedly complicated though. 154# It's unexpectedly complicated though.
156 @echo $@:${:U:range=5:ts\x24:C,[0-9],,g:Q}: 155 @echo $@:${:U:range=5:ts\x24:C,[0-9],,g:Q}:
157 156
158# Demonstrate that it is possible to generate dollar characters using the 157# Demonstrate that it is possible to generate dollar characters using the
159# :@ modifier. 158# :@ modifier.
160# 159#
161# These are edge cases that could have resulted in a parse error as well 160# These are edge cases that could have resulted in a parse error as well
162# since the $@ at the end could have been interpreted as a variable, which 161# since the $@ at the end could have been interpreted as a variable, which
163# would mean a missing closing @ delimiter. 162# would mean a missing closing @ delimiter.
164mod-loop-dollar: 163mod-loop-dollar:
165 @echo $@:${:U1:@word@${word}$@:Q}: 164 @echo $@:${:U1:@word@${word}$@:Q}:
166 @echo $@:${:U2:@word@$${word}$$@:Q}: 165 @echo $@:${:U2:@word@$${word}$$@:Q}:
167 @echo $@:${:U3:@word@$$${word}$$$@:Q}: 166 @echo $@:${:U3:@word@$$${word}$$$@:Q}:
168 @echo $@:${:U4:@word@$$$${word}$$$$@:Q}: 167 @echo $@:${:U4:@word@$$$${word}$$$$@:Q}:
169 @echo $@:${:U5:@word@$$$$${word}$$$$$@:Q}: 168 @echo $@:${:U5:@word@$$$$${word}$$$$$@:Q}:
170 @echo $@:${:U6:@word@$$$$$${word}$$$$$$@:Q}: 169 @echo $@:${:U6:@word@$$$$$${word}$$$$$$@:Q}:
171 170
172mod-regex-limits: 171mod-regex-limits:
173 @echo $@:00-ok:${:U1 23 456:C,..,\0\0,:Q} 172 @echo $@:00-ok:${:U1 23 456:C,..,\0\0,:Q}
174 @echo $@:11-missing:${:U1 23 456:C,..,\1\1,:Q} 173 @echo $@:11-missing:${:U1 23 456:C,..,\1\1,:Q}
175 @echo $@:11-ok:${:U1 23 456:C,(.).,\1\1,:Q} 174 @echo $@:11-ok:${:U1 23 456:C,(.).,\1\1,:Q}
176 @echo $@:22-missing:${:U1 23 456:C,..,\2\2,:Q} 175 @echo $@:22-missing:${:U1 23 456:C,..,\2\2,:Q}
177 @echo $@:22-missing:${:U1 23 456:C,(.).,\2\2,:Q} 176 @echo $@:22-missing:${:U1 23 456:C,(.).,\2\2,:Q}
178 @echo $@:22-ok:${:U1 23 456:C,(.)(.),\2\2,:Q} 177 @echo $@:22-ok:${:U1 23 456:C,(.)(.),\2\2,:Q}
179 # The :C modifier only handles single-digit capturing groups, 178 # The :C modifier only handles single-digit capturing groups,
180 # which is more than enough for daily use. 179 # which is more than enough for daily use.
181 @echo $@:capture:${:UabcdefghijABCDEFGHIJrest:C,(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.),\9\8\7\6\5\4\3\2\1\0\10\11\12,} 180 @echo $@:capture:${:UabcdefghijABCDEFGHIJrest:C,(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.),\9\8\7\6\5\4\3\2\1\0\10\11\12,}
182 181
183mod-regex-errors: 182mod-regex-errors:
184 @echo $@: ${UNDEF:Uvalue:C,[,,} 183 @echo $@: ${UNDEF:Uvalue:C,[,,}
185 184
186# Just a bit of basic code coverage for the obscure ::= assignment modifiers. 185# Just a bit of basic code coverage for the obscure ::= assignment modifiers.
187mod-assign: 186mod-assign:
188 @echo $@: ${1 2 3:L:@i@${FIRST::?=$i}@} first=${FIRST}. 187 @echo $@: ${1 2 3:L:@i@${FIRST::?=$i}@} first=${FIRST}.
189 @echo $@: ${1 2 3:L:@i@${LAST::=$i}@} last=${LAST}. 188 @echo $@: ${1 2 3:L:@i@${LAST::=$i}@} last=${LAST}.
190 @echo $@: ${1 2 3:L:@i@${APPENDED::+=$i}@} appended=${APPENDED}. 189 @echo $@: ${1 2 3:L:@i@${APPENDED::+=$i}@} appended=${APPENDED}.
191 @echo $@: ${echo.1 echo.2 echo.3:L:@i@${RAN::!=${i:C,.*,&; & 1>\&2,:S,., ,g}}@} ran:${RAN}. 190 @echo $@: ${echo.1 echo.2 echo.3:L:@i@${RAN::!=${i:C,.*,&; & 1>\&2,:S,., ,g}}@} ran:${RAN}.
192 # The assignments happen in the global scope and thus are 191 # The assignments happen in the global scope and thus are
193 # preserved even after the shell command has been run. 192 # preserved even after the shell command has been run.
194 @echo $@: global: ${FIRST:Q}, ${LAST:Q}, ${APPENDED:Q}, ${RAN:Q}. 193 @echo $@: global: ${FIRST:Q}, ${LAST:Q}, ${APPENDED:Q}, ${RAN:Q}.
195 194
196mod-assign-nested: 195mod-assign-nested:
197 @echo $@: ${1:?${THEN1::=then1${IT1::=t1}}:${ELSE1::=else1${IE1::=e1}}}${THEN1}${ELSE1}${IT1}${IE1} 196 @echo $@: ${1:?${THEN1::=then1${IT1::=t1}}:${ELSE1::=else1${IE1::=e1}}}${THEN1}${ELSE1}${IT1}${IE1}
198 @echo $@: ${0:?${THEN2::=then2${IT2::=t2}}:${ELSE2::=else2${IE2::=e2}}}${THEN2}${ELSE2}${IT2}${IE2} 197 @echo $@: ${0:?${THEN2::=then2${IT2::=t2}}:${ELSE2::=else2${IE2::=e2}}}${THEN2}${ELSE2}${IT2}${IE2}
199 @echo $@: ${SINK3:Q} 198 @echo $@: ${SINK3:Q}
200 @echo $@: ${SINK4:Q} 199 @echo $@: ${SINK4:Q}
201SINK3:= ${1:?${THEN3::=then3${IT3::=t3}}:${ELSE3::=else3${IE3::=e3}}}${THEN3}${ELSE3}${IT3}${IE3} 200SINK3:= ${1:?${THEN3::=then3${IT3::=t3}}:${ELSE3::=else3${IE3::=e3}}}${THEN3}${ELSE3}${IT3}${IE3}
202SINK4:= ${0:?${THEN4::=then4${IT4::=t4}}:${ELSE4::=else4${IE4::=e4}}}${THEN4}${ELSE4}${IT4}${IE4} 201SINK4:= ${0:?${THEN4::=then4${IT4::=t4}}:${ELSE4::=else4${IE4::=e4}}}${THEN4}${ELSE4}${IT4}${IE4}
203 202
204mod-tu-space: 203mod-tu-space:
205 # The :tu and :tl modifiers operate on the variable value 204 # The :tu and :tl modifiers operate on the variable value
206 # as a single string, not as a list of words. Therefore, 205 # as a single string, not as a list of words. Therefore,
207 # the adjacent spaces are preserved. 206 # the adjacent spaces are preserved.
208 @echo $@: ${a b:L:tu:Q} 207 @echo $@: ${a b:L:tu:Q}
209 208
210mod-quote: 209mod-quote:
211 @echo $@: new${.newline:Q}${.newline:Q}line 210 @echo $@: new${.newline:Q}${.newline:Q}line
212 211
213# Cover the bmake_realloc in brk_string. 212# Cover the bmake_realloc in brk_string.
214mod-break-many-words: 213mod-break-many-words:
215 @echo $@: ${UNDEF:U:range=500:[#]} 214 @echo $@: ${UNDEF:U:range=500:[#]}