Fri Oct 30 16:48:58 2020 UTC ()
make(1): make iterating over HashTable simpler


(rillig)
diff -r1.147 -r1.148 src/usr.bin/make/arch.c
diff -r1.602 -r1.603 src/usr.bin/make/var.c

cvs diff -r1.147 -r1.148 src/usr.bin/make/arch.c (switch to unified diff)

--- src/usr.bin/make/arch.c 2020/10/25 19:19:07 1.147
+++ src/usr.bin/make/arch.c 2020/10/30 16:48:58 1.148
@@ -1,1122 +1,1121 @@ @@ -1,1122 +1,1121 @@
1/* $NetBSD: arch.c,v 1.147 2020/10/25 19:19:07 rillig Exp $ */ 1/* $NetBSD: arch.c,v 1.148 2020/10/30 16:48:58 rillig Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 1988, 1989, 1990, 1993 4 * Copyright (c) 1988, 1989, 1990, 1993
5 * The Regents of the University of California. All rights reserved. 5 * The Regents of the University of California. All rights reserved.
6 * 6 *
7 * This code is derived from software contributed to Berkeley by 7 * This code is derived from software contributed to Berkeley by
8 * Adam de Boor. 8 * Adam de Boor.
9 * 9 *
10 * Redistribution and use in source and binary forms, with or without 10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions 11 * modification, are permitted provided that the following conditions
12 * are met: 12 * are met:
13 * 1. Redistributions of source code must retain the above copyright 13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer. 14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright 15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the 16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution. 17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors 18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software 19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission. 20 * without specific prior written permission.
21 * 21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE. 32 * SUCH DAMAGE.
33 */ 33 */
34 34
35/* 35/*
36 * Copyright (c) 1989 by Berkeley Softworks 36 * Copyright (c) 1989 by Berkeley Softworks
37 * All rights reserved. 37 * All rights reserved.
38 * 38 *
39 * This code is derived from software contributed to Berkeley by 39 * This code is derived from software contributed to Berkeley by
40 * Adam de Boor. 40 * Adam de Boor.
41 * 41 *
42 * Redistribution and use in source and binary forms, with or without 42 * Redistribution and use in source and binary forms, with or without
43 * modification, are permitted provided that the following conditions 43 * modification, are permitted provided that the following conditions
44 * are met: 44 * are met:
45 * 1. Redistributions of source code must retain the above copyright 45 * 1. Redistributions of source code must retain the above copyright
46 * notice, this list of conditions and the following disclaimer. 46 * notice, this list of conditions and the following disclaimer.
47 * 2. Redistributions in binary form must reproduce the above copyright 47 * 2. Redistributions in binary form must reproduce the above copyright
48 * notice, this list of conditions and the following disclaimer in the 48 * notice, this list of conditions and the following disclaimer in the
49 * documentation and/or other materials provided with the distribution. 49 * documentation and/or other materials provided with the distribution.
50 * 3. All advertising materials mentioning features or use of this software 50 * 3. All advertising materials mentioning features or use of this software
51 * must display the following acknowledgement: 51 * must display the following acknowledgement:
52 * This product includes software developed by the University of 52 * This product includes software developed by the University of
53 * California, Berkeley and its contributors. 53 * California, Berkeley and its contributors.
54 * 4. Neither the name of the University nor the names of its contributors 54 * 4. Neither the name of the University nor the names of its contributors
55 * may be used to endorse or promote products derived from this software 55 * may be used to endorse or promote products derived from this software
56 * without specific prior written permission. 56 * without specific prior written permission.
57 * 57 *
58 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 58 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
59 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 59 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
60 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 60 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
61 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 61 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
62 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 62 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
63 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 63 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
64 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 64 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
65 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 65 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
66 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 66 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
67 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 67 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
68 * SUCH DAMAGE. 68 * SUCH DAMAGE.
69 */ 69 */
70 70
71/*- 71/*-
72 * arch.c -- 72 * arch.c --
73 * Functions to manipulate libraries, archives and their members. 73 * Functions to manipulate libraries, archives and their members.
74 * 74 *
75 * Once again, cacheing/hashing comes into play in the manipulation 75 * Once again, cacheing/hashing comes into play in the manipulation
76 * of archives. The first time an archive is referenced, all of its members' 76 * of archives. The first time an archive is referenced, all of its members'
77 * headers are read and hashed and the archive closed again. All hashed 77 * headers are read and hashed and the archive closed again. All hashed
78 * archives are kept on a list which is searched each time an archive member 78 * archives are kept on a list which is searched each time an archive member
79 * is referenced. 79 * is referenced.
80 * 80 *
81 * The interface to this module is: 81 * The interface to this module is:
82 * Arch_ParseArchive 82 * Arch_ParseArchive
83 * Given an archive specification, return a list 83 * Given an archive specification, return a list
84 * of GNode's, one for each member in the spec. 84 * of GNode's, one for each member in the spec.
85 * FALSE is returned if the specification is 85 * FALSE is returned if the specification is
86 * invalid for some reason. 86 * invalid for some reason.
87 * 87 *
88 * Arch_Touch Alter the modification time of the archive 88 * Arch_Touch Alter the modification time of the archive
89 * member described by the given node to be 89 * member described by the given node to be
90 * the current time. 90 * the current time.
91 * 91 *
92 * Arch_TouchLib Update the modification time of the library 92 * Arch_TouchLib Update the modification time of the library
93 * described by the given node. This is special 93 * described by the given node. This is special
94 * because it also updates the modification time 94 * because it also updates the modification time
95 * of the library's table of contents. 95 * of the library's table of contents.
96 * 96 *
97 * Arch_MTime Find the modification time of a member of 97 * Arch_MTime Find the modification time of a member of
98 * an archive *in the archive*. The time is also 98 * an archive *in the archive*. The time is also
99 * placed in the member's GNode. Returns the 99 * placed in the member's GNode. Returns the
100 * modification time. 100 * modification time.
101 * 101 *
102 * Arch_MemTime Find the modification time of a member of 102 * Arch_MemTime Find the modification time of a member of
103 * an archive. Called when the member doesn't 103 * an archive. Called when the member doesn't
104 * already exist. Looks in the archive for the 104 * already exist. Looks in the archive for the
105 * modification time. Returns the modification 105 * modification time. Returns the modification
106 * time. 106 * time.
107 * 107 *
108 * Arch_FindLib Search for a library along a path. The 108 * Arch_FindLib Search for a library along a path. The
109 * library name in the GNode should be in 109 * library name in the GNode should be in
110 * -l<name> format. 110 * -l<name> format.
111 * 111 *
112 * Arch_LibOODate Special function to decide if a library node 112 * Arch_LibOODate Special function to decide if a library node
113 * is out-of-date. 113 * is out-of-date.
114 * 114 *
115 * Arch_Init Initialize this module. 115 * Arch_Init Initialize this module.
116 * 116 *
117 * Arch_End Clean up this module. 117 * Arch_End Clean up this module.
118 */ 118 */
119 119
120#include <sys/types.h> 120#include <sys/types.h>
121#include <sys/stat.h> 121#include <sys/stat.h>
122#include <sys/time.h> 122#include <sys/time.h>
123#include <sys/param.h> 123#include <sys/param.h>
124 124
125#include <ar.h> 125#include <ar.h>
126#include <utime.h> 126#include <utime.h>
127 127
128#include "make.h" 128#include "make.h"
129#include "dir.h" 129#include "dir.h"
130#include "config.h" 130#include "config.h"
131 131
132/* "@(#)arch.c 8.2 (Berkeley) 1/2/94" */ 132/* "@(#)arch.c 8.2 (Berkeley) 1/2/94" */
133MAKE_RCSID("$NetBSD: arch.c,v 1.147 2020/10/25 19:19:07 rillig Exp $"); 133MAKE_RCSID("$NetBSD: arch.c,v 1.148 2020/10/30 16:48:58 rillig Exp $");
134 134
135#ifdef TARGET_MACHINE 135#ifdef TARGET_MACHINE
136#undef MAKE_MACHINE 136#undef MAKE_MACHINE
137#define MAKE_MACHINE TARGET_MACHINE 137#define MAKE_MACHINE TARGET_MACHINE
138#endif 138#endif
139#ifdef TARGET_MACHINE_ARCH 139#ifdef TARGET_MACHINE_ARCH
140#undef MAKE_MACHINE_ARCH 140#undef MAKE_MACHINE_ARCH
141#define MAKE_MACHINE_ARCH TARGET_MACHINE_ARCH 141#define MAKE_MACHINE_ARCH TARGET_MACHINE_ARCH
142#endif 142#endif
143 143
144typedef struct List ArchList; 144typedef struct List ArchList;
145typedef struct ListNode ArchListNode; 145typedef struct ListNode ArchListNode;
146 146
147static ArchList *archives; /* The archives we've already examined */ 147static ArchList *archives; /* The archives we've already examined */
148 148
149typedef struct Arch { 149typedef struct Arch {
150 char *name; /* Name of archive */ 150 char *name; /* Name of archive */
151 HashTable members; /* All the members of the archive described 151 HashTable members; /* All the members of the archive described
152 * by <name, struct ar_hdr *> key/value pairs */ 152 * by <name, struct ar_hdr *> key/value pairs */
153 char *fnametab; /* Extended name table strings */ 153 char *fnametab; /* Extended name table strings */
154 size_t fnamesize; /* Size of the string table */ 154 size_t fnamesize; /* Size of the string table */
155} Arch; 155} Arch;
156 156
157static FILE *ArchFindMember(const char *, const char *, 157static FILE *ArchFindMember(const char *, const char *,
158 struct ar_hdr *, const char *); 158 struct ar_hdr *, const char *);
159#if defined(__svr4__) || defined(__SVR4) || defined(__ELF__) 159#if defined(__svr4__) || defined(__SVR4) || defined(__ELF__)
160#define SVR4ARCHIVES 160#define SVR4ARCHIVES
161static int ArchSVR4Entry(Arch *, char *, size_t, FILE *); 161static int ArchSVR4Entry(Arch *, char *, size_t, FILE *);
162#endif 162#endif
163 163
164#ifdef CLEANUP 164#ifdef CLEANUP
165static void 165static void
166ArchFree(void *ap) 166ArchFree(void *ap)
167{ 167{
168 Arch *a = ap; 168 Arch *a = ap;
169 HashIter hi; 169 HashIter hi;
170 HashEntry *he; 
171 170
172 /* Free memory from hash entries */ 171 /* Free memory from hash entries */
173 HashIter_Init(&hi, &a->members); 172 HashIter_Init(&hi, &a->members);
174 while ((he = HashIter_Next(&hi)) != NULL) 173 while (HashIter_Next(&hi) != NULL)
175 free(HashEntry_Get(he)); 174 free(hi.entry->value);
176 175
177 free(a->name); 176 free(a->name);
178 free(a->fnametab); 177 free(a->fnametab);
179 HashTable_Done(&a->members); 178 HashTable_Done(&a->members);
180 free(a); 179 free(a);
181} 180}
182#endif 181#endif
183 182
184 183
185/*- 184/*-
186 *----------------------------------------------------------------------- 185 *-----------------------------------------------------------------------
187 * Arch_ParseArchive -- 186 * Arch_ParseArchive --
188 * Parse the archive specification in the given line and find/create 187 * Parse the archive specification in the given line and find/create
189 * the nodes for the specified archive members, placing their nodes 188 * the nodes for the specified archive members, placing their nodes
190 * on the given list. 189 * on the given list.
191 * 190 *
192 * Input: 191 * Input:
193 * linePtr Pointer to start of specification 192 * linePtr Pointer to start of specification
194 * nodeLst Lst on which to place the nodes 193 * nodeLst Lst on which to place the nodes
195 * ctxt Context in which to expand variables 194 * ctxt Context in which to expand variables
196 * 195 *
197 * Results: 196 * Results:
198 * TRUE if it was a valid specification. The linePtr is updated 197 * TRUE if it was a valid specification. The linePtr is updated
199 * to point to the first non-space after the archive spec. The 198 * to point to the first non-space after the archive spec. The
200 * nodes for the members are placed on the given list. 199 * nodes for the members are placed on the given list.
201 *----------------------------------------------------------------------- 200 *-----------------------------------------------------------------------
202 */ 201 */
203Boolean 202Boolean
204Arch_ParseArchive(char **linePtr, GNodeList *nodeLst, GNode *ctxt) 203Arch_ParseArchive(char **linePtr, GNodeList *nodeLst, GNode *ctxt)
205{ 204{
206 char *cp; /* Pointer into line */ 205 char *cp; /* Pointer into line */
207 GNode *gn; /* New node */ 206 GNode *gn; /* New node */
208 char *libName; /* Library-part of specification */ 207 char *libName; /* Library-part of specification */
209 char *memName; /* Member-part of specification */ 208 char *memName; /* Member-part of specification */
210 char saveChar; /* Ending delimiter of member-name */ 209 char saveChar; /* Ending delimiter of member-name */
211 Boolean subLibName; /* TRUE if libName should have/had 210 Boolean subLibName; /* TRUE if libName should have/had
212 * variable substitution performed on it */ 211 * variable substitution performed on it */
213 212
214 libName = *linePtr; 213 libName = *linePtr;
215 214
216 subLibName = FALSE; 215 subLibName = FALSE;
217 216
218 for (cp = libName; *cp != '(' && *cp != '\0';) { 217 for (cp = libName; *cp != '(' && *cp != '\0';) {
219 if (*cp == '$') { 218 if (*cp == '$') {
220 /* 219 /*
221 * Variable spec, so call the Var module to parse the puppy 220 * Variable spec, so call the Var module to parse the puppy
222 * so we can safely advance beyond it... 221 * so we can safely advance beyond it...
223 */ 222 */
224 const char *nested_p = cp; 223 const char *nested_p = cp;
225 void *result_freeIt; 224 void *result_freeIt;
226 const char *result; 225 const char *result;
227 Boolean isError; 226 Boolean isError;
228 227
229 (void)Var_Parse(&nested_p, ctxt, VARE_UNDEFERR|VARE_WANTRES, 228 (void)Var_Parse(&nested_p, ctxt, VARE_UNDEFERR|VARE_WANTRES,
230 &result, &result_freeIt); 229 &result, &result_freeIt);
231 /* TODO: handle errors */ 230 /* TODO: handle errors */
232 isError = result == var_Error; 231 isError = result == var_Error;
233 free(result_freeIt); 232 free(result_freeIt);
234 if (isError) 233 if (isError)
235 return FALSE; 234 return FALSE;
236 235
237 subLibName = TRUE; 236 subLibName = TRUE;
238 cp += nested_p - cp; 237 cp += nested_p - cp;
239 } else 238 } else
240 cp++; 239 cp++;
241 } 240 }
242 241
243 *cp++ = '\0'; 242 *cp++ = '\0';
244 if (subLibName) { 243 if (subLibName) {
245 (void)Var_Subst(libName, ctxt, VARE_UNDEFERR|VARE_WANTRES, &libName); 244 (void)Var_Subst(libName, ctxt, VARE_UNDEFERR|VARE_WANTRES, &libName);
246 /* TODO: handle errors */ 245 /* TODO: handle errors */
247 } 246 }
248 247
249 248
250 for (;;) { 249 for (;;) {
251 /* 250 /*
252 * First skip to the start of the member's name, mark that 251 * First skip to the start of the member's name, mark that
253 * place and skip to the end of it (either white-space or 252 * place and skip to the end of it (either white-space or
254 * a close paren). 253 * a close paren).
255 */ 254 */
256 Boolean doSubst = FALSE; /* TRUE if need to substitute in memName */ 255 Boolean doSubst = FALSE; /* TRUE if need to substitute in memName */
257 256
258 pp_skip_whitespace(&cp); 257 pp_skip_whitespace(&cp);
259 258
260 memName = cp; 259 memName = cp;
261 while (*cp != '\0' && *cp != ')' && !ch_isspace(*cp)) { 260 while (*cp != '\0' && *cp != ')' && !ch_isspace(*cp)) {
262 if (*cp == '$') { 261 if (*cp == '$') {
263 /* 262 /*
264 * Variable spec, so call the Var module to parse the puppy 263 * Variable spec, so call the Var module to parse the puppy
265 * so we can safely advance beyond it... 264 * so we can safely advance beyond it...
266 */ 265 */
267 void *freeIt; 266 void *freeIt;
268 const char *result; 267 const char *result;
269 Boolean isError; 268 Boolean isError;
270 const char *nested_p = cp; 269 const char *nested_p = cp;
271 270
272 (void)Var_Parse(&nested_p, ctxt, VARE_UNDEFERR|VARE_WANTRES, 271 (void)Var_Parse(&nested_p, ctxt, VARE_UNDEFERR|VARE_WANTRES,
273 &result, &freeIt); 272 &result, &freeIt);
274 /* TODO: handle errors */ 273 /* TODO: handle errors */
275 isError = result == var_Error; 274 isError = result == var_Error;
276 free(freeIt); 275 free(freeIt);
277 276
278 if (isError) 277 if (isError)
279 return FALSE; 278 return FALSE;
280 279
281 doSubst = TRUE; 280 doSubst = TRUE;
282 cp += nested_p - cp; 281 cp += nested_p - cp;
283 } else { 282 } else {
284 cp++; 283 cp++;
285 } 284 }
286 } 285 }
287 286
288 /* 287 /*
289 * If the specification ends without a closing parenthesis, 288 * If the specification ends without a closing parenthesis,
290 * chances are there's something wrong (like a missing backslash), 289 * chances are there's something wrong (like a missing backslash),
291 * so it's better to return failure than allow such things to happen 290 * so it's better to return failure than allow such things to happen
292 */ 291 */
293 if (*cp == '\0') { 292 if (*cp == '\0') {
294 printf("No closing parenthesis in archive specification\n"); 293 printf("No closing parenthesis in archive specification\n");
295 return FALSE; 294 return FALSE;
296 } 295 }
297 296
298 /* 297 /*
299 * If we didn't move anywhere, we must be done 298 * If we didn't move anywhere, we must be done
300 */ 299 */
301 if (cp == memName) { 300 if (cp == memName) {
302 break; 301 break;
303 } 302 }
304 303
305 saveChar = *cp; 304 saveChar = *cp;
306 *cp = '\0'; 305 *cp = '\0';
307 306
308 /* 307 /*
309 * XXX: This should be taken care of intelligently by 308 * XXX: This should be taken care of intelligently by
310 * SuffExpandChildren, both for the archive and the member portions. 309 * SuffExpandChildren, both for the archive and the member portions.
311 */ 310 */
312 /* 311 /*
313 * If member contains variables, try and substitute for them. 312 * If member contains variables, try and substitute for them.
314 * This will slow down archive specs with dynamic sources, of course, 313 * This will slow down archive specs with dynamic sources, of course,
315 * since we'll be (non-)substituting them three times, but them's 314 * since we'll be (non-)substituting them three times, but them's
316 * the breaks -- we need to do this since SuffExpandChildren calls 315 * the breaks -- we need to do this since SuffExpandChildren calls
317 * us, otherwise we could assume the thing would be taken care of 316 * us, otherwise we could assume the thing would be taken care of
318 * later. 317 * later.
319 */ 318 */
320 if (doSubst) { 319 if (doSubst) {
321 char *buf; 320 char *buf;
322 char *sacrifice; 321 char *sacrifice;
323 char *oldMemName = memName; 322 char *oldMemName = memName;
324 323
325 (void)Var_Subst(memName, ctxt, VARE_UNDEFERR|VARE_WANTRES, 324 (void)Var_Subst(memName, ctxt, VARE_UNDEFERR|VARE_WANTRES,
326 &memName); 325 &memName);
327 /* TODO: handle errors */ 326 /* TODO: handle errors */
328 327
329 /* 328 /*
330 * Now form an archive spec and recurse to deal with nested 329 * Now form an archive spec and recurse to deal with nested
331 * variables and multi-word variable values.... The results 330 * variables and multi-word variable values.... The results
332 * are just placed at the end of the nodeLst we're returning. 331 * are just placed at the end of the nodeLst we're returning.
333 */ 332 */
334 buf = sacrifice = str_concat4(libName, "(", memName, ")"); 333 buf = sacrifice = str_concat4(libName, "(", memName, ")");
335 334
336 if (strchr(memName, '$') && strcmp(memName, oldMemName) == 0) { 335 if (strchr(memName, '$') && strcmp(memName, oldMemName) == 0) {
337 /* 336 /*
338 * Must contain dynamic sources, so we can't deal with it now. 337 * Must contain dynamic sources, so we can't deal with it now.
339 * Just create an ARCHV node for the thing and let 338 * Just create an ARCHV node for the thing and let
340 * SuffExpandChildren handle it... 339 * SuffExpandChildren handle it...
341 */ 340 */
342 gn = Targ_GetNode(buf); 341 gn = Targ_GetNode(buf);
343 gn->type |= OP_ARCHV; 342 gn->type |= OP_ARCHV;
344 Lst_Append(nodeLst, gn); 343 Lst_Append(nodeLst, gn);
345 344
346 } else if (!Arch_ParseArchive(&sacrifice, nodeLst, ctxt)) { 345 } else if (!Arch_ParseArchive(&sacrifice, nodeLst, ctxt)) {
347 /* Error in nested call. */ 346 /* Error in nested call. */
348 free(buf); 347 free(buf);
349 return FALSE; 348 return FALSE;
350 } 349 }
351 free(buf); 350 free(buf);
352 351
353 } else if (Dir_HasWildcards(memName)) { 352 } else if (Dir_HasWildcards(memName)) {
354 StringList *members = Lst_New(); 353 StringList *members = Lst_New();
355 Dir_Expand(memName, dirSearchPath, members); 354 Dir_Expand(memName, dirSearchPath, members);
356 355
357 while (!Lst_IsEmpty(members)) { 356 while (!Lst_IsEmpty(members)) {
358 char *member = Lst_Dequeue(members); 357 char *member = Lst_Dequeue(members);
359 char *fullname = str_concat4(libName, "(", member, ")"); 358 char *fullname = str_concat4(libName, "(", member, ")");
360 free(member); 359 free(member);
361 360
362 gn = Targ_GetNode(fullname); 361 gn = Targ_GetNode(fullname);
363 free(fullname); 362 free(fullname);
364 363
365 gn->type |= OP_ARCHV; 364 gn->type |= OP_ARCHV;
366 Lst_Append(nodeLst, gn); 365 Lst_Append(nodeLst, gn);
367 } 366 }
368 Lst_Free(members); 367 Lst_Free(members);
369 368
370 } else { 369 } else {
371 char *fullname = str_concat4(libName, "(", memName, ")"); 370 char *fullname = str_concat4(libName, "(", memName, ")");
372 gn = Targ_GetNode(fullname); 371 gn = Targ_GetNode(fullname);
373 free(fullname); 372 free(fullname);
374 373
375 /* 374 /*
376 * We've found the node, but have to make sure the rest of the 375 * We've found the node, but have to make sure the rest of the
377 * world knows it's an archive member, without having to 376 * world knows it's an archive member, without having to
378 * constantly check for parentheses, so we type the thing with 377 * constantly check for parentheses, so we type the thing with
379 * the OP_ARCHV bit before we place it on the end of the 378 * the OP_ARCHV bit before we place it on the end of the
380 * provided list. 379 * provided list.
381 */ 380 */
382 gn->type |= OP_ARCHV; 381 gn->type |= OP_ARCHV;
383 Lst_Append(nodeLst, gn); 382 Lst_Append(nodeLst, gn);
384 } 383 }
385 if (doSubst) { 384 if (doSubst) {
386 free(memName); 385 free(memName);
387 } 386 }
388 387
389 *cp = saveChar; 388 *cp = saveChar;
390 } 389 }
391 390
392 /* 391 /*
393 * If substituted libName, free it now, since we need it no longer. 392 * If substituted libName, free it now, since we need it no longer.
394 */ 393 */
395 if (subLibName) { 394 if (subLibName) {
396 free(libName); 395 free(libName);
397 } 396 }
398 397
399 cp++; /* skip the ')' */ 398 cp++; /* skip the ')' */
400 /* We promised that linePtr would be set up at the next non-space. */ 399 /* We promised that linePtr would be set up at the next non-space. */
401 pp_skip_whitespace(&cp); 400 pp_skip_whitespace(&cp);
402 *linePtr = cp; 401 *linePtr = cp;
403 return TRUE; 402 return TRUE;
404} 403}
405 404
406/* Locate a member of an archive, given the path of the archive and the path 405/* Locate a member of an archive, given the path of the archive and the path
407 * of the desired member. 406 * of the desired member.
408 * 407 *
409 * Input: 408 * Input:
410 * archive Path to the archive 409 * archive Path to the archive
411 * member Name of member; only its basename is used. 410 * member Name of member; only its basename is used.
412 * hash TRUE if archive should be hashed if not already so. 411 * hash TRUE if archive should be hashed if not already so.
413 * 412 *
414 * Results: 413 * Results:
415 * The ar_hdr for the member. 414 * The ar_hdr for the member.
416 */ 415 */
417static struct ar_hdr * 416static struct ar_hdr *
418ArchStatMember(const char *archive, const char *member, Boolean hash) 417ArchStatMember(const char *archive, const char *member, Boolean hash)
419{ 418{
420#define AR_MAX_NAME_LEN (sizeof(arh.ar_name) - 1) 419#define AR_MAX_NAME_LEN (sizeof(arh.ar_name) - 1)
421 FILE *arch; /* Stream to archive */ 420 FILE *arch; /* Stream to archive */
422 size_t size; /* Size of archive member */ 421 size_t size; /* Size of archive member */
423 char magic[SARMAG]; 422 char magic[SARMAG];
424 ArchListNode *ln; 423 ArchListNode *ln;
425 Arch *ar; /* Archive descriptor */ 424 Arch *ar; /* Archive descriptor */
426 struct ar_hdr arh; /* archive-member header for reading archive */ 425 struct ar_hdr arh; /* archive-member header for reading archive */
427 char memName[MAXPATHLEN + 1]; 426 char memName[MAXPATHLEN + 1];
428 /* Current member name while hashing. */ 427 /* Current member name while hashing. */
429 428
430 /* 429 /*
431 * Because of space constraints and similar things, files are archived 430 * Because of space constraints and similar things, files are archived
432 * using their basename, not the entire path. 431 * using their basename, not the entire path.
433 */ 432 */
434 const char *lastSlash = strrchr(member, '/'); 433 const char *lastSlash = strrchr(member, '/');
435 if (lastSlash != NULL) 434 if (lastSlash != NULL)
436 member = lastSlash + 1; 435 member = lastSlash + 1;
437 436
438 for (ln = archives->first; ln != NULL; ln = ln->next) { 437 for (ln = archives->first; ln != NULL; ln = ln->next) {
439 const Arch *archPtr = ln->datum; 438 const Arch *archPtr = ln->datum;
440 if (strcmp(archPtr->name, archive) == 0) 439 if (strcmp(archPtr->name, archive) == 0)
441 break; 440 break;
442 } 441 }
443 442
444 if (ln != NULL) { 443 if (ln != NULL) {
445 struct ar_hdr *hdr; 444 struct ar_hdr *hdr;
446 445
447 ar = ln->datum; 446 ar = ln->datum;
448 hdr = HashTable_FindValue(&ar->members, member); 447 hdr = HashTable_FindValue(&ar->members, member);
449 if (hdr != NULL) 448 if (hdr != NULL)
450 return hdr; 449 return hdr;
451 450
452 { 451 {
453 /* Try truncated name */ 452 /* Try truncated name */
454 char copy[AR_MAX_NAME_LEN + 1]; 453 char copy[AR_MAX_NAME_LEN + 1];
455 size_t len = strlen(member); 454 size_t len = strlen(member);
456 455
457 if (len > AR_MAX_NAME_LEN) { 456 if (len > AR_MAX_NAME_LEN) {
458 len = AR_MAX_NAME_LEN; 457 len = AR_MAX_NAME_LEN;
459 snprintf(copy, sizeof copy, "%s", member); 458 snprintf(copy, sizeof copy, "%s", member);
460 } 459 }
461 hdr = HashTable_FindValue(&ar->members, copy); 460 hdr = HashTable_FindValue(&ar->members, copy);
462 return hdr; 461 return hdr;
463 } 462 }
464 } 463 }
465 464
466 if (!hash) { 465 if (!hash) {
467 /* 466 /*
468 * Caller doesn't want the thing hashed, just use ArchFindMember 467 * Caller doesn't want the thing hashed, just use ArchFindMember
469 * to read the header for the member out and close down the stream 468 * to read the header for the member out and close down the stream
470 * again. Since the archive is not to be hashed, we assume there's 469 * again. Since the archive is not to be hashed, we assume there's
471 * no need to allocate extra room for the header we're returning, 470 * no need to allocate extra room for the header we're returning,
472 * so just declare it static. 471 * so just declare it static.
473 */ 472 */
474 static struct ar_hdr sarh; 473 static struct ar_hdr sarh;
475 474
476 arch = ArchFindMember(archive, member, &sarh, "r"); 475 arch = ArchFindMember(archive, member, &sarh, "r");
477 if (arch == NULL) 476 if (arch == NULL)
478 return NULL; 477 return NULL;
479 478
480 fclose(arch); 479 fclose(arch);
481 return &sarh; 480 return &sarh;
482 } 481 }
483 482
484 /* 483 /*
485 * We don't have this archive on the list yet, so we want to find out 484 * We don't have this archive on the list yet, so we want to find out
486 * everything that's in it and cache it so we can get at it quickly. 485 * everything that's in it and cache it so we can get at it quickly.
487 */ 486 */
488 arch = fopen(archive, "r"); 487 arch = fopen(archive, "r");
489 if (arch == NULL) 488 if (arch == NULL)
490 return NULL; 489 return NULL;
491 490
492 /* 491 /*
493 * We use the ARMAG string to make sure this is an archive we 492 * We use the ARMAG string to make sure this is an archive we
494 * can handle... 493 * can handle...
495 */ 494 */
496 if ((fread(magic, SARMAG, 1, arch) != 1) || 495 if ((fread(magic, SARMAG, 1, arch) != 1) ||
497 (strncmp(magic, ARMAG, SARMAG) != 0)) { 496 (strncmp(magic, ARMAG, SARMAG) != 0)) {
498 fclose(arch); 497 fclose(arch);
499 return NULL; 498 return NULL;
500 } 499 }
501 500
502 ar = bmake_malloc(sizeof(Arch)); 501 ar = bmake_malloc(sizeof(Arch));
503 ar->name = bmake_strdup(archive); 502 ar->name = bmake_strdup(archive);
504 ar->fnametab = NULL; 503 ar->fnametab = NULL;
505 ar->fnamesize = 0; 504 ar->fnamesize = 0;
506 HashTable_Init(&ar->members); 505 HashTable_Init(&ar->members);
507 memName[AR_MAX_NAME_LEN] = '\0'; 506 memName[AR_MAX_NAME_LEN] = '\0';
508 507
509 while (fread((char *)&arh, sizeof(struct ar_hdr), 1, arch) == 1) { 508 while (fread((char *)&arh, sizeof(struct ar_hdr), 1, arch) == 1) {
510 if (strncmp(arh.ar_fmag, ARFMAG, sizeof(arh.ar_fmag)) != 0) { 509 if (strncmp(arh.ar_fmag, ARFMAG, sizeof(arh.ar_fmag)) != 0) {
511 /* 510 /*
512 * The header is bogus, so the archive is bad 511 * The header is bogus, so the archive is bad
513 * and there's no way we can recover... 512 * and there's no way we can recover...
514 */ 513 */
515 goto badarch; 514 goto badarch;
516 } else { 515 } else {
517 char *nameend; 516 char *nameend;
518 517
519 /* 518 /*
520 * We need to advance the stream's pointer to the start of the 519 * We need to advance the stream's pointer to the start of the
521 * next header. Files are padded with newlines to an even-byte 520 * next header. Files are padded with newlines to an even-byte
522 * boundary, so we need to extract the size of the file from the 521 * boundary, so we need to extract the size of the file from the
523 * 'size' field of the header and round it up during the seek. 522 * 'size' field of the header and round it up during the seek.
524 */ 523 */
525 arh.ar_size[sizeof(arh.ar_size) - 1] = '\0'; 524 arh.ar_size[sizeof(arh.ar_size) - 1] = '\0';
526 size = (size_t)strtol(arh.ar_size, NULL, 10); 525 size = (size_t)strtol(arh.ar_size, NULL, 10);
527 526
528 memcpy(memName, arh.ar_name, sizeof(arh.ar_name)); 527 memcpy(memName, arh.ar_name, sizeof(arh.ar_name));
529 nameend = memName + AR_MAX_NAME_LEN; 528 nameend = memName + AR_MAX_NAME_LEN;
530 while (*nameend == ' ') { 529 while (*nameend == ' ') {
531 nameend--; 530 nameend--;
532 } 531 }
533 nameend[1] = '\0'; 532 nameend[1] = '\0';
534 533
535#ifdef SVR4ARCHIVES 534#ifdef SVR4ARCHIVES
536 /* 535 /*
537 * svr4 names are slash terminated. Also svr4 extended AR format. 536 * svr4 names are slash terminated. Also svr4 extended AR format.
538 */ 537 */
539 if (memName[0] == '/') { 538 if (memName[0] == '/') {
540 /* 539 /*
541 * svr4 magic mode; handle it 540 * svr4 magic mode; handle it
542 */ 541 */
543 switch (ArchSVR4Entry(ar, memName, size, arch)) { 542 switch (ArchSVR4Entry(ar, memName, size, arch)) {
544 case -1: /* Invalid data */ 543 case -1: /* Invalid data */
545 goto badarch; 544 goto badarch;
546 case 0: /* List of files entry */ 545 case 0: /* List of files entry */
547 continue; 546 continue;
548 default: /* Got the entry */ 547 default: /* Got the entry */
549 break; 548 break;
550 } 549 }
551 } else { 550 } else {
552 if (nameend[0] == '/') 551 if (nameend[0] == '/')
553 nameend[0] = '\0'; 552 nameend[0] = '\0';
554 } 553 }
555#endif 554#endif
556 555
557#ifdef AR_EFMT1 556#ifdef AR_EFMT1
558 /* 557 /*
559 * BSD 4.4 extended AR format: #1/<namelen>, with name as the 558 * BSD 4.4 extended AR format: #1/<namelen>, with name as the
560 * first <namelen> bytes of the file 559 * first <namelen> bytes of the file
561 */ 560 */
562 if (strncmp(memName, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0 && 561 if (strncmp(memName, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0 &&
563 ch_isdigit(memName[sizeof(AR_EFMT1) - 1])) { 562 ch_isdigit(memName[sizeof(AR_EFMT1) - 1])) {
564 563
565 int elen = atoi(&memName[sizeof(AR_EFMT1) - 1]); 564 int elen = atoi(&memName[sizeof(AR_EFMT1) - 1]);
566 565
567 if ((unsigned int)elen > MAXPATHLEN) 566 if ((unsigned int)elen > MAXPATHLEN)
568 goto badarch; 567 goto badarch;
569 if (fread(memName, (size_t)elen, 1, arch) != 1) 568 if (fread(memName, (size_t)elen, 1, arch) != 1)
570 goto badarch; 569 goto badarch;
571 memName[elen] = '\0'; 570 memName[elen] = '\0';
572 if (fseek(arch, -elen, SEEK_CUR) != 0) 571 if (fseek(arch, -elen, SEEK_CUR) != 0)
573 goto badarch; 572 goto badarch;
574 if (DEBUG(ARCH) || DEBUG(MAKE)) { 573 if (DEBUG(ARCH) || DEBUG(MAKE)) {
575 debug_printf("ArchStat: Extended format entry for %s\n", 574 debug_printf("ArchStat: Extended format entry for %s\n",
576 memName); 575 memName);
577 } 576 }
578 } 577 }
579#endif 578#endif
580 579
581 { 580 {
582 HashEntry *he; 581 HashEntry *he;
583 he = HashTable_CreateEntry(&ar->members, memName, NULL); 582 he = HashTable_CreateEntry(&ar->members, memName, NULL);
584 HashEntry_Set(he, bmake_malloc(sizeof(struct ar_hdr))); 583 HashEntry_Set(he, bmake_malloc(sizeof(struct ar_hdr)));
585 memcpy(HashEntry_Get(he), &arh, sizeof(struct ar_hdr)); 584 memcpy(HashEntry_Get(he), &arh, sizeof(struct ar_hdr));
586 } 585 }
587 } 586 }
588 if (fseek(arch, ((long)size + 1) & ~1, SEEK_CUR) != 0) 587 if (fseek(arch, ((long)size + 1) & ~1, SEEK_CUR) != 0)
589 goto badarch; 588 goto badarch;
590 } 589 }
591 590
592 fclose(arch); 591 fclose(arch);
593 592
594 Lst_Append(archives, ar); 593 Lst_Append(archives, ar);
595 594
596 /* 595 /*
597 * Now that the archive has been read and cached, we can look into 596 * Now that the archive has been read and cached, we can look into
598 * the hash table to find the desired member's header. 597 * the hash table to find the desired member's header.
599 */ 598 */
600 return HashTable_FindValue(&ar->members, member); 599 return HashTable_FindValue(&ar->members, member);
601 600
602badarch: 601badarch:
603 fclose(arch); 602 fclose(arch);
604 HashTable_Done(&ar->members); 603 HashTable_Done(&ar->members);
605 free(ar->fnametab); 604 free(ar->fnametab);
606 free(ar); 605 free(ar);
607 return NULL; 606 return NULL;
608} 607}
609 608
610#ifdef SVR4ARCHIVES 609#ifdef SVR4ARCHIVES
611/*- 610/*-
612 *----------------------------------------------------------------------- 611 *-----------------------------------------------------------------------
613 * ArchSVR4Entry -- 612 * ArchSVR4Entry --
614 * Parse an SVR4 style entry that begins with a slash. 613 * Parse an SVR4 style entry that begins with a slash.
615 * If it is "//", then load the table of filenames 614 * If it is "//", then load the table of filenames
616 * If it is "/<offset>", then try to substitute the long file name 615 * If it is "/<offset>", then try to substitute the long file name
617 * from offset of a table previously read. 616 * from offset of a table previously read.
618 * If a table is read, the file pointer is moved to the next archive 617 * If a table is read, the file pointer is moved to the next archive
619 * member. 618 * member.
620 * 619 *
621 * Results: 620 * Results:
622 * -1: Bad data in archive 621 * -1: Bad data in archive
623 * 0: A table was loaded from the file 622 * 0: A table was loaded from the file
624 * 1: Name was successfully substituted from table 623 * 1: Name was successfully substituted from table
625 * 2: Name was not successfully substituted from table 624 * 2: Name was not successfully substituted from table
626 *----------------------------------------------------------------------- 625 *-----------------------------------------------------------------------
627 */ 626 */
628static int 627static int
629ArchSVR4Entry(Arch *ar, char *name, size_t size, FILE *arch) 628ArchSVR4Entry(Arch *ar, char *name, size_t size, FILE *arch)
630{ 629{
631#define ARLONGNAMES1 "//" 630#define ARLONGNAMES1 "//"
632#define ARLONGNAMES2 "/ARFILENAMES" 631#define ARLONGNAMES2 "/ARFILENAMES"
633 size_t entry; 632 size_t entry;
634 char *ptr, *eptr; 633 char *ptr, *eptr;
635 634
636 if (strncmp(name, ARLONGNAMES1, sizeof(ARLONGNAMES1) - 1) == 0 || 635 if (strncmp(name, ARLONGNAMES1, sizeof(ARLONGNAMES1) - 1) == 0 ||
637 strncmp(name, ARLONGNAMES2, sizeof(ARLONGNAMES2) - 1) == 0) { 636 strncmp(name, ARLONGNAMES2, sizeof(ARLONGNAMES2) - 1) == 0) {
638 637
639 if (ar->fnametab != NULL) { 638 if (ar->fnametab != NULL) {
640 DEBUG0(ARCH, "Attempted to redefine an SVR4 name table\n"); 639 DEBUG0(ARCH, "Attempted to redefine an SVR4 name table\n");
641 return -1; 640 return -1;
642 } 641 }
643 642
644 /* 643 /*
645 * This is a table of archive names, so we build one for 644 * This is a table of archive names, so we build one for
646 * ourselves 645 * ourselves
647 */ 646 */
648 ar->fnametab = bmake_malloc(size); 647 ar->fnametab = bmake_malloc(size);
649 ar->fnamesize = size; 648 ar->fnamesize = size;
650 649
651 if (fread(ar->fnametab, size, 1, arch) != 1) { 650 if (fread(ar->fnametab, size, 1, arch) != 1) {
652 DEBUG0(ARCH, "Reading an SVR4 name table failed\n"); 651 DEBUG0(ARCH, "Reading an SVR4 name table failed\n");
653 return -1; 652 return -1;
654 } 653 }
655 eptr = ar->fnametab + size; 654 eptr = ar->fnametab + size;
656 for (entry = 0, ptr = ar->fnametab; ptr < eptr; ptr++) 655 for (entry = 0, ptr = ar->fnametab; ptr < eptr; ptr++)
657 if (*ptr == '/') { 656 if (*ptr == '/') {
658 entry++; 657 entry++;
659 *ptr = '\0'; 658 *ptr = '\0';
660 } 659 }
661 DEBUG1(ARCH, "Found svr4 archive name table with %lu entries\n", 660 DEBUG1(ARCH, "Found svr4 archive name table with %lu entries\n",
662 (unsigned long)entry); 661 (unsigned long)entry);
663 return 0; 662 return 0;
664 } 663 }
665 664
666 if (name[1] == ' ' || name[1] == '\0') 665 if (name[1] == ' ' || name[1] == '\0')
667 return 2; 666 return 2;
668 667
669 entry = (size_t)strtol(&name[1], &eptr, 0); 668 entry = (size_t)strtol(&name[1], &eptr, 0);
670 if ((*eptr != ' ' && *eptr != '\0') || eptr == &name[1]) { 669 if ((*eptr != ' ' && *eptr != '\0') || eptr == &name[1]) {
671 DEBUG1(ARCH, "Could not parse SVR4 name %s\n", name); 670 DEBUG1(ARCH, "Could not parse SVR4 name %s\n", name);
672 return 2; 671 return 2;
673 } 672 }
674 if (entry >= ar->fnamesize) { 673 if (entry >= ar->fnamesize) {
675 DEBUG2(ARCH, "SVR4 entry offset %s is greater than %lu\n", 674 DEBUG2(ARCH, "SVR4 entry offset %s is greater than %lu\n",
676 name, (unsigned long)ar->fnamesize); 675 name, (unsigned long)ar->fnamesize);
677 return 2; 676 return 2;
678 } 677 }
679 678
680 DEBUG2(ARCH, "Replaced %s with %s\n", name, &ar->fnametab[entry]); 679 DEBUG2(ARCH, "Replaced %s with %s\n", name, &ar->fnametab[entry]);
681 680
682 snprintf(name, MAXPATHLEN + 1, "%s", &ar->fnametab[entry]); 681 snprintf(name, MAXPATHLEN + 1, "%s", &ar->fnametab[entry]);
683 return 1; 682 return 1;
684} 683}
685#endif 684#endif
686 685
687 686
688/*- 687/*-
689 *----------------------------------------------------------------------- 688 *-----------------------------------------------------------------------
690 * ArchFindMember -- 689 * ArchFindMember --
691 * Locate a member of an archive, given the path of the archive and 690 * Locate a member of an archive, given the path of the archive and
692 * the path of the desired member. If the archive is to be modified, 691 * the path of the desired member. If the archive is to be modified,
693 * the mode should be "r+", if not, it should be "r". 692 * the mode should be "r+", if not, it should be "r".
694 * The passed struct ar_hdr structure is filled in. 693 * The passed struct ar_hdr structure is filled in.
695 * 694 *
696 * Input: 695 * Input:
697 * archive Path to the archive 696 * archive Path to the archive
698 * member Name of member. If it is a path, only the last 697 * member Name of member. If it is a path, only the last
699 * component is used. 698 * component is used.
700 * arhPtr Pointer to header structure to be filled in 699 * arhPtr Pointer to header structure to be filled in
701 * mode The mode for opening the stream 700 * mode The mode for opening the stream
702 * 701 *
703 * Results: 702 * Results:
704 * An FILE *, opened for reading and writing, positioned at the 703 * An FILE *, opened for reading and writing, positioned at the
705 * start of the member's struct ar_hdr, or NULL if the member was 704 * start of the member's struct ar_hdr, or NULL if the member was
706 * nonexistent. The current struct ar_hdr for member. 705 * nonexistent. The current struct ar_hdr for member.
707 *----------------------------------------------------------------------- 706 *-----------------------------------------------------------------------
708 */ 707 */
709static FILE * 708static FILE *
710ArchFindMember(const char *archive, const char *member, struct ar_hdr *arhPtr, 709ArchFindMember(const char *archive, const char *member, struct ar_hdr *arhPtr,
711 const char *mode) 710 const char *mode)
712{ 711{
713 FILE *arch; /* Stream to archive */ 712 FILE *arch; /* Stream to archive */
714 int size; /* Size of archive member */ 713 int size; /* Size of archive member */
715 char magic[SARMAG]; 714 char magic[SARMAG];
716 size_t len, tlen; 715 size_t len, tlen;
717 const char *lastSlash; 716 const char *lastSlash;
718 717
719 arch = fopen(archive, mode); 718 arch = fopen(archive, mode);
720 if (arch == NULL) 719 if (arch == NULL)
721 return NULL; 720 return NULL;
722 721
723 /* 722 /*
724 * We use the ARMAG string to make sure this is an archive we 723 * We use the ARMAG string to make sure this is an archive we
725 * can handle... 724 * can handle...
726 */ 725 */
727 if ((fread(magic, SARMAG, 1, arch) != 1) || 726 if ((fread(magic, SARMAG, 1, arch) != 1) ||
728 (strncmp(magic, ARMAG, SARMAG) != 0)) { 727 (strncmp(magic, ARMAG, SARMAG) != 0)) {
729 fclose(arch); 728 fclose(arch);
730 return NULL; 729 return NULL;
731 } 730 }
732 731
733 /* 732 /*
734 * Because of space constraints and similar things, files are archived 733 * Because of space constraints and similar things, files are archived
735 * using their basename, not the entire path. 734 * using their basename, not the entire path.
736 */ 735 */
737 lastSlash = strrchr(member, '/'); 736 lastSlash = strrchr(member, '/');
738 if (lastSlash != NULL) 737 if (lastSlash != NULL)
739 member = lastSlash + 1; 738 member = lastSlash + 1;
740 739
741 len = tlen = strlen(member); 740 len = tlen = strlen(member);
742 if (len > sizeof(arhPtr->ar_name)) { 741 if (len > sizeof(arhPtr->ar_name)) {
743 tlen = sizeof(arhPtr->ar_name); 742 tlen = sizeof(arhPtr->ar_name);
744 } 743 }
745 744
746 while (fread((char *)arhPtr, sizeof(struct ar_hdr), 1, arch) == 1) { 745 while (fread((char *)arhPtr, sizeof(struct ar_hdr), 1, arch) == 1) {
747 746
748 if (strncmp(arhPtr->ar_fmag, ARFMAG, sizeof(arhPtr->ar_fmag)) != 0) { 747 if (strncmp(arhPtr->ar_fmag, ARFMAG, sizeof(arhPtr->ar_fmag)) != 0) {
749 /* 748 /*
750 * The header is bogus, so the archive is bad 749 * The header is bogus, so the archive is bad
751 * and there's no way we can recover... 750 * and there's no way we can recover...
752 */ 751 */
753 fclose(arch); 752 fclose(arch);
754 return NULL; 753 return NULL;
755 } 754 }
756 755
757 if (strncmp(member, arhPtr->ar_name, tlen) == 0) { 756 if (strncmp(member, arhPtr->ar_name, tlen) == 0) {
758 /* 757 /*
759 * If the member's name doesn't take up the entire 'name' field, 758 * If the member's name doesn't take up the entire 'name' field,
760 * we have to be careful of matching prefixes. Names are space- 759 * we have to be careful of matching prefixes. Names are space-
761 * padded to the right, so if the character in 'name' at the end 760 * padded to the right, so if the character in 'name' at the end
762 * of the matched string is anything but a space, this isn't the 761 * of the matched string is anything but a space, this isn't the
763 * member we sought. 762 * member we sought.
764 */ 763 */
765 if (tlen != sizeof arhPtr->ar_name && arhPtr->ar_name[tlen] != ' ') 764 if (tlen != sizeof arhPtr->ar_name && arhPtr->ar_name[tlen] != ' ')
766 goto skip; 765 goto skip;
767 766
768 /* 767 /*
769 * To make life easier, we reposition the file at the start 768 * To make life easier, we reposition the file at the start
770 * of the header we just read before we return the stream. 769 * of the header we just read before we return the stream.
771 * In a more general situation, it might be better to leave 770 * In a more general situation, it might be better to leave
772 * the file at the actual member, rather than its header, but 771 * the file at the actual member, rather than its header, but
773 * not here... 772 * not here...
774 */ 773 */
775 if (fseek(arch, -(long)sizeof(struct ar_hdr), SEEK_CUR) != 0) { 774 if (fseek(arch, -(long)sizeof(struct ar_hdr), SEEK_CUR) != 0) {
776 fclose(arch); 775 fclose(arch);
777 return NULL; 776 return NULL;
778 } 777 }
779 return arch; 778 return arch;
780 } 779 }
781 780
782#ifdef AR_EFMT1 781#ifdef AR_EFMT1
783 /* 782 /*
784 * BSD 4.4 extended AR format: #1/<namelen>, with name as the 783 * BSD 4.4 extended AR format: #1/<namelen>, with name as the
785 * first <namelen> bytes of the file 784 * first <namelen> bytes of the file
786 */ 785 */
787 if (strncmp(arhPtr->ar_name, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0 && 786 if (strncmp(arhPtr->ar_name, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0 &&
788 ch_isdigit(arhPtr->ar_name[sizeof(AR_EFMT1) - 1])) 787 ch_isdigit(arhPtr->ar_name[sizeof(AR_EFMT1) - 1]))
789 { 788 {
790 int elen = atoi(&arhPtr->ar_name[sizeof(AR_EFMT1) - 1]); 789 int elen = atoi(&arhPtr->ar_name[sizeof(AR_EFMT1) - 1]);
791 char ename[MAXPATHLEN + 1]; 790 char ename[MAXPATHLEN + 1];
792 791
793 if ((unsigned int)elen > MAXPATHLEN) { 792 if ((unsigned int)elen > MAXPATHLEN) {
794 fclose(arch); 793 fclose(arch);
795 return NULL; 794 return NULL;
796 } 795 }
797 if (fread(ename, (size_t)elen, 1, arch) != 1) { 796 if (fread(ename, (size_t)elen, 1, arch) != 1) {
798 fclose(arch); 797 fclose(arch);
799 return NULL; 798 return NULL;
800 } 799 }
801 ename[elen] = '\0'; 800 ename[elen] = '\0';
802 if (DEBUG(ARCH) || DEBUG(MAKE)) { 801 if (DEBUG(ARCH) || DEBUG(MAKE)) {
803 debug_printf("ArchFind: Extended format entry for %s\n", ename); 802 debug_printf("ArchFind: Extended format entry for %s\n", ename);
804 } 803 }
805 if (strncmp(ename, member, len) == 0) { 804 if (strncmp(ename, member, len) == 0) {
806 /* Found as extended name */ 805 /* Found as extended name */
807 if (fseek(arch, -(long)sizeof(struct ar_hdr) - elen, 806 if (fseek(arch, -(long)sizeof(struct ar_hdr) - elen,
808 SEEK_CUR) != 0) { 807 SEEK_CUR) != 0) {
809 fclose(arch); 808 fclose(arch);
810 return NULL; 809 return NULL;
811 } 810 }
812 return arch; 811 return arch;
813 } 812 }
814 if (fseek(arch, -elen, SEEK_CUR) != 0) { 813 if (fseek(arch, -elen, SEEK_CUR) != 0) {
815 fclose(arch); 814 fclose(arch);
816 return NULL; 815 return NULL;
817 } 816 }
818 } 817 }
819#endif 818#endif
820 819
821skip: 820skip:
822 /* 821 /*
823 * This isn't the member we're after, so we need to advance the 822 * This isn't the member we're after, so we need to advance the
824 * stream's pointer to the start of the next header. Files are 823 * stream's pointer to the start of the next header. Files are
825 * padded with newlines to an even-byte boundary, so we need to 824 * padded with newlines to an even-byte boundary, so we need to
826 * extract the size of the file from the 'size' field of the 825 * extract the size of the file from the 'size' field of the
827 * header and round it up during the seek. 826 * header and round it up during the seek.
828 */ 827 */
829 arhPtr->ar_size[sizeof(arhPtr->ar_size) - 1] = '\0'; 828 arhPtr->ar_size[sizeof(arhPtr->ar_size) - 1] = '\0';
830 size = (int)strtol(arhPtr->ar_size, NULL, 10); 829 size = (int)strtol(arhPtr->ar_size, NULL, 10);
831 if (fseek(arch, (size + 1) & ~1, SEEK_CUR) != 0) { 830 if (fseek(arch, (size + 1) & ~1, SEEK_CUR) != 0) {
832 fclose(arch); 831 fclose(arch);
833 return NULL; 832 return NULL;
834 } 833 }
835 } 834 }
836 835
837 /* 836 /*
838 * We've looked everywhere, but the member is not to be found. Close the 837 * We've looked everywhere, but the member is not to be found. Close the
839 * archive and return NULL -- an error. 838 * archive and return NULL -- an error.
840 */ 839 */
841 fclose(arch); 840 fclose(arch);
842 return NULL; 841 return NULL;
843} 842}
844 843
845/*- 844/*-
846 *----------------------------------------------------------------------- 845 *-----------------------------------------------------------------------
847 * Arch_Touch -- 846 * Arch_Touch --
848 * Touch a member of an archive. 847 * Touch a member of an archive.
849 * The modification time of the entire archive is also changed. 848 * The modification time of the entire archive is also changed.
850 * For a library, this could necessitate the re-ranlib'ing of the 849 * For a library, this could necessitate the re-ranlib'ing of the
851 * whole thing. 850 * whole thing.
852 * 851 *
853 * Input: 852 * Input:
854 * gn Node of member to touch 853 * gn Node of member to touch
855 * 854 *
856 * Results: 855 * Results:
857 * The 'time' field of the member's header is updated. 856 * The 'time' field of the member's header is updated.
858 *----------------------------------------------------------------------- 857 *-----------------------------------------------------------------------
859 */ 858 */
860void 859void
861Arch_Touch(GNode *gn) 860Arch_Touch(GNode *gn)
862{ 861{
863 FILE *arch; /* Stream open to archive, positioned properly */ 862 FILE *arch; /* Stream open to archive, positioned properly */
864 struct ar_hdr arh; /* Current header describing member */ 863 struct ar_hdr arh; /* Current header describing member */
865 char *p1, *p2; 864 char *p1, *p2;
866 865
867 arch = ArchFindMember(Var_Value(ARCHIVE, gn, &p1), 866 arch = ArchFindMember(Var_Value(ARCHIVE, gn, &p1),
868 Var_Value(MEMBER, gn, &p2), 867 Var_Value(MEMBER, gn, &p2),
869 &arh, "r+"); 868 &arh, "r+");
870 869
871 bmake_free(p1); 870 bmake_free(p1);
872 bmake_free(p2); 871 bmake_free(p2);
873 872
874 snprintf(arh.ar_date, sizeof(arh.ar_date), "%-12ld", (long)now); 873 snprintf(arh.ar_date, sizeof(arh.ar_date), "%-12ld", (long)now);
875 874
876 if (arch != NULL) { 875 if (arch != NULL) {
877 (void)fwrite((char *)&arh, sizeof(struct ar_hdr), 1, arch); 876 (void)fwrite((char *)&arh, sizeof(struct ar_hdr), 1, arch);
878 fclose(arch); 877 fclose(arch);
879 } 878 }
880} 879}
881 880
882/* Given a node which represents a library, touch the thing, making sure that 881/* Given a node which represents a library, touch the thing, making sure that
883 * the table of contents also is touched. 882 * the table of contents also is touched.
884 * 883 *
885 * Both the modification time of the library and of the RANLIBMAG member are 884 * Both the modification time of the library and of the RANLIBMAG member are
886 * set to 'now'. 885 * set to 'now'.
887 * 886 *
888 * Input: 887 * Input:
889 * gn The node of the library to touch 888 * gn The node of the library to touch
890 */ 889 */
891void 890void
892Arch_TouchLib(GNode *gn) 891Arch_TouchLib(GNode *gn)
893{ 892{
894#ifdef RANLIBMAG 893#ifdef RANLIBMAG
895 FILE * arch; /* Stream open to archive */ 894 FILE * arch; /* Stream open to archive */
896 struct ar_hdr arh; /* Header describing table of contents */ 895 struct ar_hdr arh; /* Header describing table of contents */
897 struct utimbuf times; /* Times for utime() call */ 896 struct utimbuf times; /* Times for utime() call */
898 897
899 arch = ArchFindMember(gn->path, RANLIBMAG, &arh, "r+"); 898 arch = ArchFindMember(gn->path, RANLIBMAG, &arh, "r+");
900 snprintf(arh.ar_date, sizeof(arh.ar_date), "%-12ld", (long) now); 899 snprintf(arh.ar_date, sizeof(arh.ar_date), "%-12ld", (long) now);
901 900
902 if (arch != NULL) { 901 if (arch != NULL) {
903 (void)fwrite((char *)&arh, sizeof(struct ar_hdr), 1, arch); 902 (void)fwrite((char *)&arh, sizeof(struct ar_hdr), 1, arch);
904 fclose(arch); 903 fclose(arch);
905 904
906 times.actime = times.modtime = now; 905 times.actime = times.modtime = now;
907 utime(gn->path, &times); 906 utime(gn->path, &times);
908 } 907 }
909#else 908#else
910 (void)gn; 909 (void)gn;
911#endif 910#endif
912} 911}
913 912
914/* Return the modification time of a member of an archive. The mtime field 913/* Return the modification time of a member of an archive. The mtime field
915 * of the given node is filled in with the value returned by the function. 914 * of the given node is filled in with the value returned by the function.
916 * 915 *
917 * Input: 916 * Input:
918 * gn Node describing archive member 917 * gn Node describing archive member
919 */ 918 */
920time_t 919time_t
921Arch_MTime(GNode *gn) 920Arch_MTime(GNode *gn)
922{ 921{
923 struct ar_hdr *arhPtr; /* Header of desired member */ 922 struct ar_hdr *arhPtr; /* Header of desired member */
924 time_t modTime; /* Modification time as an integer */ 923 time_t modTime; /* Modification time as an integer */
925 char *p1, *p2; 924 char *p1, *p2;
926 925
927 arhPtr = ArchStatMember(Var_Value(ARCHIVE, gn, &p1), 926 arhPtr = ArchStatMember(Var_Value(ARCHIVE, gn, &p1),
928 Var_Value(MEMBER, gn, &p2), 927 Var_Value(MEMBER, gn, &p2),
929 TRUE); 928 TRUE);
930 929
931 bmake_free(p1); 930 bmake_free(p1);
932 bmake_free(p2); 931 bmake_free(p2);
933 932
934 if (arhPtr != NULL) { 933 if (arhPtr != NULL) {
935 modTime = (time_t)strtol(arhPtr->ar_date, NULL, 10); 934 modTime = (time_t)strtol(arhPtr->ar_date, NULL, 10);
936 } else { 935 } else {
937 modTime = 0; 936 modTime = 0;
938 } 937 }
939 938
940 gn->mtime = modTime; 939 gn->mtime = modTime;
941 return modTime; 940 return modTime;
942} 941}
943 942
944/* Given a non-existent archive member's node, get its modification time from 943/* Given a non-existent archive member's node, get its modification time from
945 * its archived form, if it exists. gn->mtime is filled in as well. */ 944 * its archived form, if it exists. gn->mtime is filled in as well. */
946time_t 945time_t
947Arch_MemMTime(GNode *gn) 946Arch_MemMTime(GNode *gn)
948{ 947{
949 GNodeListNode *ln; 948 GNodeListNode *ln;
950 949
951 for (ln = gn->parents->first; ln != NULL; ln = ln->next) { 950 for (ln = gn->parents->first; ln != NULL; ln = ln->next) {
952 GNode *pgn = ln->datum; 951 GNode *pgn = ln->datum;
953 952
954 if (pgn->type & OP_ARCHV) { 953 if (pgn->type & OP_ARCHV) {
955 /* 954 /*
956 * If the parent is an archive specification and is being made 955 * If the parent is an archive specification and is being made
957 * and its member's name matches the name of the node we were 956 * and its member's name matches the name of the node we were
958 * given, record the modification time of the parent in the 957 * given, record the modification time of the parent in the
959 * child. We keep searching its parents in case some other 958 * child. We keep searching its parents in case some other
960 * parent requires this child to exist... 959 * parent requires this child to exist...
961 */ 960 */
962 const char *nameStart = strchr(pgn->name, '(') + 1; 961 const char *nameStart = strchr(pgn->name, '(') + 1;
963 const char *nameEnd = strchr(nameStart, ')'); 962 const char *nameEnd = strchr(nameStart, ')');
964 size_t nameLen = (size_t)(nameEnd - nameStart); 963 size_t nameLen = (size_t)(nameEnd - nameStart);
965 964
966 if ((pgn->flags & REMAKE) && 965 if ((pgn->flags & REMAKE) &&
967 strncmp(nameStart, gn->name, nameLen) == 0) { 966 strncmp(nameStart, gn->name, nameLen) == 0) {
968 gn->mtime = Arch_MTime(pgn); 967 gn->mtime = Arch_MTime(pgn);
969 } 968 }
970 } else if (pgn->flags & REMAKE) { 969 } else if (pgn->flags & REMAKE) {
971 /* 970 /*
972 * Something which isn't a library depends on the existence of 971 * Something which isn't a library depends on the existence of
973 * this target, so it needs to exist. 972 * this target, so it needs to exist.
974 */ 973 */
975 gn->mtime = 0; 974 gn->mtime = 0;
976 break; 975 break;
977 } 976 }
978 } 977 }
979 978
980 return gn->mtime; 979 return gn->mtime;
981} 980}
982 981
983/* Search for a library along the given search path. 982/* Search for a library along the given search path.
984 * 983 *
985 * The node's 'path' field is set to the found path (including the 984 * The node's 'path' field is set to the found path (including the
986 * actual file name, not -l...). If the system can handle the -L 985 * actual file name, not -l...). If the system can handle the -L
987 * flag when linking (or we cannot find the library), we assume that 986 * flag when linking (or we cannot find the library), we assume that
988 * the user has placed the .LIBS variable in the final linking 987 * the user has placed the .LIBS variable in the final linking
989 * command (or the linker will know where to find it) and set the 988 * command (or the linker will know where to find it) and set the
990 * TARGET variable for this node to be the node's name. Otherwise, 989 * TARGET variable for this node to be the node's name. Otherwise,
991 * we set the TARGET variable to be the full path of the library, 990 * we set the TARGET variable to be the full path of the library,
992 * as returned by Dir_FindFile. 991 * as returned by Dir_FindFile.
993 * 992 *
994 * Input: 993 * Input:
995 * gn Node of library to find 994 * gn Node of library to find
996 */ 995 */
997void 996void
998Arch_FindLib(GNode *gn, SearchPath *path) 997Arch_FindLib(GNode *gn, SearchPath *path)
999{ 998{
1000 char *libName = str_concat3("lib", gn->name + 2, ".a"); 999 char *libName = str_concat3("lib", gn->name + 2, ".a");
1001 gn->path = Dir_FindFile(libName, path); 1000 gn->path = Dir_FindFile(libName, path);
1002 free(libName); 1001 free(libName);
1003 1002
1004#ifdef LIBRARIES 1003#ifdef LIBRARIES
1005 Var_Set(TARGET, gn->name, gn); 1004 Var_Set(TARGET, gn->name, gn);
1006#else 1005#else
1007 Var_Set(TARGET, gn->path == NULL ? gn->name : gn->path, gn); 1006 Var_Set(TARGET, gn->path == NULL ? gn->name : gn->path, gn);
1008#endif 1007#endif
1009} 1008}
1010 1009
1011/* Decide if a node with the OP_LIB attribute is out-of-date. Called from 1010/* Decide if a node with the OP_LIB attribute is out-of-date. Called from
1012 * Make_OODate to make its life easier. 1011 * Make_OODate to make its life easier.
1013 * The library will be hashed if it hasn't been already. 1012 * The library will be hashed if it hasn't been already.
1014 * 1013 *
1015 * There are several ways for a library to be out-of-date that are 1014 * There are several ways for a library to be out-of-date that are
1016 * not available to ordinary files. In addition, there are ways 1015 * not available to ordinary files. In addition, there are ways
1017 * that are open to regular files that are not available to 1016 * that are open to regular files that are not available to
1018 * libraries. A library that is only used as a source is never 1017 * libraries. A library that is only used as a source is never
1019 * considered out-of-date by itself. This does not preclude the 1018 * considered out-of-date by itself. This does not preclude the
1020 * library's modification time from making its parent be out-of-date. 1019 * library's modification time from making its parent be out-of-date.
1021 * A library will be considered out-of-date for any of these reasons, 1020 * A library will be considered out-of-date for any of these reasons,
1022 * given that it is a target on a dependency line somewhere: 1021 * given that it is a target on a dependency line somewhere:
1023 * 1022 *
1024 * Its modification time is less than that of one of its sources 1023 * Its modification time is less than that of one of its sources
1025 * (gn->mtime < gn->youngestChild->mtime). 1024 * (gn->mtime < gn->youngestChild->mtime).
1026 * 1025 *
1027 * Its modification time is greater than the time at which the make 1026 * Its modification time is greater than the time at which the make
1028 * began (i.e. it's been modified in the course of the make, probably 1027 * began (i.e. it's been modified in the course of the make, probably
1029 * by archiving). 1028 * by archiving).
1030 * 1029 *
1031 * The modification time of one of its sources is greater than the one 1030 * The modification time of one of its sources is greater than the one
1032 * of its RANLIBMAG member (i.e. its table of contents is out-of-date). 1031 * of its RANLIBMAG member (i.e. its table of contents is out-of-date).
1033 * We don't compare of the archive time vs. TOC time because they can be 1032 * We don't compare of the archive time vs. TOC time because they can be
1034 * too close. In my opinion we should not bother with the TOC at all 1033 * too close. In my opinion we should not bother with the TOC at all
1035 * since this is used by 'ar' rules that affect the data contents of the 1034 * since this is used by 'ar' rules that affect the data contents of the
1036 * archive, not by ranlib rules, which affect the TOC. 1035 * archive, not by ranlib rules, which affect the TOC.
1037 * 1036 *
1038 * Input: 1037 * Input:
1039 * gn The library's graph node 1038 * gn The library's graph node
1040 * 1039 *
1041 * Results: 1040 * Results:
1042 * TRUE if the library is out-of-date. FALSE otherwise. 1041 * TRUE if the library is out-of-date. FALSE otherwise.
1043 */ 1042 */
1044Boolean 1043Boolean
1045Arch_LibOODate(GNode *gn) 1044Arch_LibOODate(GNode *gn)
1046{ 1045{
1047 Boolean oodate; 1046 Boolean oodate;
1048 1047
1049 if (gn->type & OP_PHONY) { 1048 if (gn->type & OP_PHONY) {
1050 oodate = TRUE; 1049 oodate = TRUE;
1051 } else if (!GNode_IsTarget(gn) && Lst_IsEmpty(gn->children)) { 1050 } else if (!GNode_IsTarget(gn) && Lst_IsEmpty(gn->children)) {
1052 oodate = FALSE; 1051 oodate = FALSE;
1053 } else if ((!Lst_IsEmpty(gn->children) && gn->youngestChild == NULL) || 1052 } else if ((!Lst_IsEmpty(gn->children) && gn->youngestChild == NULL) ||
1054 (gn->mtime > now) || 1053 (gn->mtime > now) ||
1055 (gn->youngestChild != NULL && 1054 (gn->youngestChild != NULL &&
1056 gn->mtime < gn->youngestChild->mtime)) { 1055 gn->mtime < gn->youngestChild->mtime)) {
1057 oodate = TRUE; 1056 oodate = TRUE;
1058 } else { 1057 } else {
1059#ifdef RANLIBMAG 1058#ifdef RANLIBMAG
1060 struct ar_hdr *arhPtr; /* Header for __.SYMDEF */ 1059 struct ar_hdr *arhPtr; /* Header for __.SYMDEF */
1061 int modTimeTOC; /* The table-of-contents's mod time */ 1060 int modTimeTOC; /* The table-of-contents's mod time */
1062 1061
1063 arhPtr = ArchStatMember(gn->path, RANLIBMAG, FALSE); 1062 arhPtr = ArchStatMember(gn->path, RANLIBMAG, FALSE);
1064 1063
1065 if (arhPtr != NULL) { 1064 if (arhPtr != NULL) {
1066 modTimeTOC = (int)strtol(arhPtr->ar_date, NULL, 10); 1065 modTimeTOC = (int)strtol(arhPtr->ar_date, NULL, 10);
1067 1066
1068 if (DEBUG(ARCH) || DEBUG(MAKE)) { 1067 if (DEBUG(ARCH) || DEBUG(MAKE)) {
1069 debug_printf("%s modified %s...", RANLIBMAG, Targ_FmtTime(modTimeTOC)); 1068 debug_printf("%s modified %s...", RANLIBMAG, Targ_FmtTime(modTimeTOC));
1070 } 1069 }
1071 oodate = (gn->youngestChild == NULL || gn->youngestChild->mtime > modTimeTOC); 1070 oodate = (gn->youngestChild == NULL || gn->youngestChild->mtime > modTimeTOC);
1072 } else { 1071 } else {
1073 /* 1072 /*
1074 * A library w/o a table of contents is out-of-date 1073 * A library w/o a table of contents is out-of-date
1075 */ 1074 */
1076 if (DEBUG(ARCH) || DEBUG(MAKE)) { 1075 if (DEBUG(ARCH) || DEBUG(MAKE)) {
1077 debug_printf("No t.o.c...."); 1076 debug_printf("No t.o.c....");
1078 } 1077 }
1079 oodate = TRUE; 1078 oodate = TRUE;
1080 } 1079 }
1081#else 1080#else
1082 oodate = FALSE; 1081 oodate = FALSE;
1083#endif 1082#endif
1084 } 1083 }
1085 return oodate; 1084 return oodate;
1086} 1085}
1087 1086
1088/* Initialize the archives module. */ 1087/* Initialize the archives module. */
1089void 1088void
1090Arch_Init(void) 1089Arch_Init(void)
1091{ 1090{
1092 archives = Lst_New(); 1091 archives = Lst_New();
1093} 1092}
1094 1093
1095/* Clean up the archives module. */ 1094/* Clean up the archives module. */
1096void 1095void
1097Arch_End(void) 1096Arch_End(void)
1098{ 1097{
1099#ifdef CLEANUP 1098#ifdef CLEANUP
1100 Lst_Destroy(archives, ArchFree); 1099 Lst_Destroy(archives, ArchFree);
1101#endif 1100#endif
1102} 1101}
1103 1102
1104Boolean 1103Boolean
1105Arch_IsLib(GNode *gn) 1104Arch_IsLib(GNode *gn)
1106{ 1105{
1107 static const char armag[] = "!<arch>\n"; 1106 static const char armag[] = "!<arch>\n";
1108 char buf[sizeof armag - 1]; 1107 char buf[sizeof armag - 1];
1109 int fd; 1108 int fd;
1110 1109
1111 if ((fd = open(gn->path, O_RDONLY)) == -1) 1110 if ((fd = open(gn->path, O_RDONLY)) == -1)
1112 return FALSE; 1111 return FALSE;
1113 1112
1114 if (read(fd, buf, sizeof buf) != sizeof buf) { 1113 if (read(fd, buf, sizeof buf) != sizeof buf) {
1115 (void)close(fd); 1114 (void)close(fd);
1116 return FALSE; 1115 return FALSE;
1117 } 1116 }
1118 1117
1119 (void)close(fd); 1118 (void)close(fd);
1120 1119
1121 return memcmp(buf, armag, sizeof buf) == 0; 1120 return memcmp(buf, armag, sizeof buf) == 0;
1122} 1121}

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

--- src/usr.bin/make/var.c 2020/10/30 16:45:37 1.602
+++ src/usr.bin/make/var.c 2020/10/30 16:48:58 1.603
@@ -1,1609 +1,1608 @@ @@ -1,1609 +1,1608 @@
1/* $NetBSD: var.c,v 1.602 2020/10/30 16:45:37 rillig Exp $ */ 1/* $NetBSD: var.c,v 1.603 2020/10/30 16:48:58 rillig Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 1988, 1989, 1990, 1993 4 * Copyright (c) 1988, 1989, 1990, 1993
5 * The Regents of the University of California. All rights reserved. 5 * The Regents of the University of California. All rights reserved.
6 * 6 *
7 * This code is derived from software contributed to Berkeley by 7 * This code is derived from software contributed to Berkeley by
8 * Adam de Boor. 8 * Adam de Boor.
9 * 9 *
10 * Redistribution and use in source and binary forms, with or without 10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions 11 * modification, are permitted provided that the following conditions
12 * are met: 12 * are met:
13 * 1. Redistributions of source code must retain the above copyright 13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer. 14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright 15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the 16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution. 17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors 18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software 19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission. 20 * without specific prior written permission.
21 * 21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE. 32 * SUCH DAMAGE.
33 */ 33 */
34 34
35/* 35/*
36 * Copyright (c) 1989 by Berkeley Softworks 36 * Copyright (c) 1989 by Berkeley Softworks
37 * All rights reserved. 37 * All rights reserved.
38 * 38 *
39 * This code is derived from software contributed to Berkeley by 39 * This code is derived from software contributed to Berkeley by
40 * Adam de Boor. 40 * Adam de Boor.
41 * 41 *
42 * Redistribution and use in source and binary forms, with or without 42 * Redistribution and use in source and binary forms, with or without
43 * modification, are permitted provided that the following conditions 43 * modification, are permitted provided that the following conditions
44 * are met: 44 * are met:
45 * 1. Redistributions of source code must retain the above copyright 45 * 1. Redistributions of source code must retain the above copyright
46 * notice, this list of conditions and the following disclaimer. 46 * notice, this list of conditions and the following disclaimer.
47 * 2. Redistributions in binary form must reproduce the above copyright 47 * 2. Redistributions in binary form must reproduce the above copyright
48 * notice, this list of conditions and the following disclaimer in the 48 * notice, this list of conditions and the following disclaimer in the
49 * documentation and/or other materials provided with the distribution. 49 * documentation and/or other materials provided with the distribution.
50 * 3. All advertising materials mentioning features or use of this software 50 * 3. All advertising materials mentioning features or use of this software
51 * must display the following acknowledgement: 51 * must display the following acknowledgement:
52 * This product includes software developed by the University of 52 * This product includes software developed by the University of
53 * California, Berkeley and its contributors. 53 * California, Berkeley and its contributors.
54 * 4. Neither the name of the University nor the names of its contributors 54 * 4. Neither the name of the University nor the names of its contributors
55 * may be used to endorse or promote products derived from this software 55 * may be used to endorse or promote products derived from this software
56 * without specific prior written permission. 56 * without specific prior written permission.
57 * 57 *
58 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 58 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
59 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 59 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
60 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 60 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
61 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 61 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
62 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 62 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
63 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 63 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
64 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 64 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
65 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 65 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
66 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 66 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
67 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 67 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
68 * SUCH DAMAGE. 68 * SUCH DAMAGE.
69 */ 69 */
70 70
71/* 71/*
72 * Handling of variables and the expressions formed from them. 72 * Handling of variables and the expressions formed from them.
73 * 73 *
74 * Variables are set using lines of the form VAR=value. Both the variable 74 * Variables are set using lines of the form VAR=value. Both the variable
75 * name and the value can contain references to other variables, by using 75 * name and the value can contain references to other variables, by using
76 * expressions like ${VAR}, ${VAR:Modifiers}, ${${VARNAME}} or ${VAR:${MODS}}. 76 * expressions like ${VAR}, ${VAR:Modifiers}, ${${VARNAME}} or ${VAR:${MODS}}.
77 * 77 *
78 * Interface: 78 * Interface:
79 * Var_Init Initialize this module. 79 * Var_Init Initialize this module.
80 * 80 *
81 * Var_End Clean up the module. 81 * Var_End Clean up the module.
82 * 82 *
83 * Var_Set Set the value of the variable, creating it if 83 * Var_Set Set the value of the variable, creating it if
84 * necessary. 84 * necessary.
85 * 85 *
86 * Var_Append Append more characters to the variable, creating it if 86 * Var_Append Append more characters to the variable, creating it if
87 * necessary. A space is placed between the old value and 87 * necessary. A space is placed between the old value and
88 * the new one. 88 * the new one.
89 * 89 *
90 * Var_Exists See if a variable exists. 90 * Var_Exists See if a variable exists.
91 * 91 *
92 * Var_Value Return the unexpanded value of a variable, or NULL if 92 * Var_Value Return the unexpanded value of a variable, or NULL if
93 * the variable is undefined. 93 * the variable is undefined.
94 * 94 *
95 * Var_Subst Substitute all variable expressions in a string. 95 * Var_Subst Substitute all variable expressions in a string.
96 * 96 *
97 * Var_Parse Parse a variable expression such as ${VAR:Mpattern}. 97 * Var_Parse Parse a variable expression such as ${VAR:Mpattern}.
98 * 98 *
99 * Var_Delete Delete a variable. 99 * Var_Delete Delete a variable.
100 * 100 *
101 * Var_ExportVars Export some or even all variables to the environment 101 * Var_ExportVars Export some or even all variables to the environment
102 * of this process and its child processes. 102 * of this process and its child processes.
103 * 103 *
104 * Var_Export Export the variable to the environment of this process 104 * Var_Export Export the variable to the environment of this process
105 * and its child processes. 105 * and its child processes.
106 * 106 *
107 * Var_UnExport Don't export the variable anymore. 107 * Var_UnExport Don't export the variable anymore.
108 * 108 *
109 * Debugging: 109 * Debugging:
110 * Var_Stats Print out hashing statistics if in -dh mode. 110 * Var_Stats Print out hashing statistics if in -dh mode.
111 * 111 *
112 * Var_Dump Print out all variables defined in the given context. 112 * Var_Dump Print out all variables defined in the given context.
113 * 113 *
114 * XXX: There's a lot of duplication in these functions. 114 * XXX: There's a lot of duplication in these functions.
115 */ 115 */
116 116
117#include <sys/stat.h> 117#include <sys/stat.h>
118#ifndef NO_REGEX 118#ifndef NO_REGEX
119#include <sys/types.h> 119#include <sys/types.h>
120#include <regex.h> 120#include <regex.h>
121#endif 121#endif
122#include <inttypes.h> 122#include <inttypes.h>
123#include <limits.h> 123#include <limits.h>
124#include <time.h> 124#include <time.h>
125 125
126#include "make.h" 126#include "make.h"
127#include "dir.h" 127#include "dir.h"
128#include "job.h" 128#include "job.h"
129#include "metachar.h" 129#include "metachar.h"
130 130
131/* "@(#)var.c 8.3 (Berkeley) 3/19/94" */ 131/* "@(#)var.c 8.3 (Berkeley) 3/19/94" */
132MAKE_RCSID("$NetBSD: var.c,v 1.602 2020/10/30 16:45:37 rillig Exp $"); 132MAKE_RCSID("$NetBSD: var.c,v 1.603 2020/10/30 16:48:58 rillig Exp $");
133 133
134#define VAR_DEBUG1(fmt, arg1) DEBUG1(VAR, fmt, arg1) 134#define VAR_DEBUG1(fmt, arg1) DEBUG1(VAR, fmt, arg1)
135#define VAR_DEBUG2(fmt, arg1, arg2) DEBUG2(VAR, fmt, arg1, arg2) 135#define VAR_DEBUG2(fmt, arg1, arg2) DEBUG2(VAR, fmt, arg1, arg2)
136#define VAR_DEBUG3(fmt, arg1, arg2, arg3) DEBUG3(VAR, fmt, arg1, arg2, arg3) 136#define VAR_DEBUG3(fmt, arg1, arg2, arg3) DEBUG3(VAR, fmt, arg1, arg2, arg3)
137#define VAR_DEBUG4(fmt, arg1, arg2, arg3, arg4) DEBUG4(VAR, fmt, arg1, arg2, arg3, arg4) 137#define VAR_DEBUG4(fmt, arg1, arg2, arg3, arg4) DEBUG4(VAR, fmt, arg1, arg2, arg3, arg4)
138 138
139ENUM_FLAGS_RTTI_3(VarEvalFlags, 139ENUM_FLAGS_RTTI_3(VarEvalFlags,
140 VARE_UNDEFERR, VARE_WANTRES, VARE_ASSIGN); 140 VARE_UNDEFERR, VARE_WANTRES, VARE_ASSIGN);
141 141
142/* 142/*
143 * This lets us tell if we have replaced the original environ 143 * This lets us tell if we have replaced the original environ
144 * (which we cannot free). 144 * (which we cannot free).
145 */ 145 */
146char **savedEnv = NULL; 146char **savedEnv = NULL;
147 147
148/* Special return value for Var_Parse, indicating a parse error. It may be 148/* Special return value for Var_Parse, indicating a parse error. It may be
149 * caused by an undefined variable, a syntax error in a modifier or 149 * caused by an undefined variable, a syntax error in a modifier or
150 * something entirely different. */ 150 * something entirely different. */
151char var_Error[] = ""; 151char var_Error[] = "";
152 152
153/* Special return value for Var_Parse, indicating an undefined variable in 153/* Special return value for Var_Parse, indicating an undefined variable in
154 * a case where VARE_UNDEFERR is not set. This undefined variable is 154 * a case where VARE_UNDEFERR is not set. This undefined variable is
155 * typically a dynamic variable such as ${.TARGET}, whose expansion needs to 155 * typically a dynamic variable such as ${.TARGET}, whose expansion needs to
156 * be deferred until it is defined in an actual target. */ 156 * be deferred until it is defined in an actual target. */
157static char varUndefined[] = ""; 157static char varUndefined[] = "";
158 158
159/* Special return value for Var_Parse, just to avoid allocating empty strings. 159/* Special return value for Var_Parse, just to avoid allocating empty strings.
160 * In contrast to var_Error and varUndefined, this is not an error marker but 160 * In contrast to var_Error and varUndefined, this is not an error marker but
161 * just an ordinary successful return value. */ 161 * just an ordinary successful return value. */
162static char emptyString[] = ""; 162static char emptyString[] = "";
163 163
164/* 164/*
165 * Traditionally this make consumed $$ during := like any other expansion. 165 * Traditionally this make consumed $$ during := like any other expansion.
166 * Other make's do not, and this make follows straight since 2016-01-09. 166 * Other make's do not, and this make follows straight since 2016-01-09.
167 * 167 *
168 * This knob allows controlling the behavior. 168 * This knob allows controlling the behavior.
169 * FALSE to consume $$ during := assignment. 169 * FALSE to consume $$ during := assignment.
170 * TRUE to preserve $$ during := assignment. 170 * TRUE to preserve $$ during := assignment.
171 */ 171 */
172#define MAKE_SAVE_DOLLARS ".MAKE.SAVE_DOLLARS" 172#define MAKE_SAVE_DOLLARS ".MAKE.SAVE_DOLLARS"
173static Boolean save_dollars = TRUE; 173static Boolean save_dollars = TRUE;
174 174
175/* 175/*
176 * Internally, variables are contained in four different contexts. 176 * Internally, variables are contained in four different contexts.
177 * 1) the environment. They cannot be changed. If an environment 177 * 1) the environment. They cannot be changed. If an environment
178 * variable is appended to, the result is placed in the global 178 * variable is appended to, the result is placed in the global
179 * context. 179 * context.
180 * 2) the global context. Variables set in the makefiles are located 180 * 2) the global context. Variables set in the makefiles are located
181 * here. 181 * here.
182 * 3) the command-line context. All variables set on the command line 182 * 3) the command-line context. All variables set on the command line
183 * are placed in this context. 183 * are placed in this context.
184 * 4) the local context. Each target has associated with it a context 184 * 4) the local context. Each target has associated with it a context
185 * list. On this list are located the structures describing such 185 * list. On this list are located the structures describing such
186 * local variables as $(@) and $(*) 186 * local variables as $(@) and $(*)
187 * The four contexts are searched in the reverse order from which they are 187 * The four contexts are searched in the reverse order from which they are
188 * listed (but see opts.checkEnvFirst). 188 * listed (but see opts.checkEnvFirst).
189 */ 189 */
190GNode *VAR_INTERNAL; /* variables from make itself */ 190GNode *VAR_INTERNAL; /* variables from make itself */
191GNode *VAR_GLOBAL; /* variables from the makefile */ 191GNode *VAR_GLOBAL; /* variables from the makefile */
192GNode *VAR_CMDLINE; /* variables defined on the command-line */ 192GNode *VAR_CMDLINE; /* variables defined on the command-line */
193 193
194typedef enum VarFlags { 194typedef enum VarFlags {
195 195
196 /* The variable's value is currently being used by Var_Parse or Var_Subst. 196 /* The variable's value is currently being used by Var_Parse or Var_Subst.
197 * This marker is used to avoid endless recursion. */ 197 * This marker is used to avoid endless recursion. */
198 VAR_IN_USE = 0x01, 198 VAR_IN_USE = 0x01,
199 199
200 /* The variable comes from the environment. 200 /* The variable comes from the environment.
201 * These variables are not registered in any GNode, therefore they must 201 * These variables are not registered in any GNode, therefore they must
202 * be freed as soon as they are not used anymore. */ 202 * be freed as soon as they are not used anymore. */
203 VAR_FROM_ENV = 0x02, 203 VAR_FROM_ENV = 0x02,
204 204
205 /* The variable is exported to the environment, to be used by child 205 /* The variable is exported to the environment, to be used by child
206 * processes. */ 206 * processes. */
207 VAR_EXPORTED = 0x10, 207 VAR_EXPORTED = 0x10,
208 208
209 /* At the point where this variable was exported, it contained an 209 /* At the point where this variable was exported, it contained an
210 * unresolved reference to another variable. Before any child process is 210 * unresolved reference to another variable. Before any child process is
211 * started, it needs to be exported again, in the hope that the referenced 211 * started, it needs to be exported again, in the hope that the referenced
212 * variable can then be resolved. */ 212 * variable can then be resolved. */
213 VAR_REEXPORT = 0x20, 213 VAR_REEXPORT = 0x20,
214 214
215 /* The variable came from the command line. */ 215 /* The variable came from the command line. */
216 VAR_FROM_CMD = 0x40, 216 VAR_FROM_CMD = 0x40,
217 217
218 /* The variable value cannot be changed anymore, and the variable cannot 218 /* The variable value cannot be changed anymore, and the variable cannot
219 * be deleted. Any attempts to do so are ignored. */ 219 * be deleted. Any attempts to do so are ignored. */
220 VAR_READONLY = 0x80 220 VAR_READONLY = 0x80
221} VarFlags; 221} VarFlags;
222 222
223ENUM_FLAGS_RTTI_6(VarFlags, 223ENUM_FLAGS_RTTI_6(VarFlags,
224 VAR_IN_USE, VAR_FROM_ENV, 224 VAR_IN_USE, VAR_FROM_ENV,
225 VAR_EXPORTED, VAR_REEXPORT, VAR_FROM_CMD, VAR_READONLY); 225 VAR_EXPORTED, VAR_REEXPORT, VAR_FROM_CMD, VAR_READONLY);
226 226
227/* Variables are defined using one of the VAR=value assignments. Their 227/* Variables are defined using one of the VAR=value assignments. Their
228 * value can be queried by expressions such as $V, ${VAR}, or with modifiers 228 * value can be queried by expressions such as $V, ${VAR}, or with modifiers
229 * such as ${VAR:S,from,to,g:Q}. 229 * such as ${VAR:S,from,to,g:Q}.
230 * 230 *
231 * There are 3 kinds of variables: context variables, environment variables, 231 * There are 3 kinds of variables: context variables, environment variables,
232 * undefined variables. 232 * undefined variables.
233 * 233 *
234 * Context variables are stored in a GNode.context. The only way to undefine 234 * Context variables are stored in a GNode.context. The only way to undefine
235 * a context variable is using the .undef directive. In particular, it must 235 * a context variable is using the .undef directive. In particular, it must
236 * not be possible to undefine a variable during the evaluation of an 236 * not be possible to undefine a variable during the evaluation of an
237 * expression, or Var.name might point nowhere. 237 * expression, or Var.name might point nowhere.
238 * 238 *
239 * Environment variables are temporary. They are returned by VarFind, and 239 * Environment variables are temporary. They are returned by VarFind, and
240 * after using them, they must be freed using VarFreeEnv. 240 * after using them, they must be freed using VarFreeEnv.
241 * 241 *
242 * Undefined variables occur during evaluation of variable expressions such 242 * Undefined variables occur during evaluation of variable expressions such
243 * as ${UNDEF:Ufallback} in Var_Parse and ApplyModifiers. 243 * as ${UNDEF:Ufallback} in Var_Parse and ApplyModifiers.
244 */ 244 */
245typedef struct Var { 245typedef struct Var {
246 /* The name of the variable, once set, doesn't change anymore. 246 /* The name of the variable, once set, doesn't change anymore.
247 * For context variables, it aliases the corresponding HashEntry name. 247 * For context variables, it aliases the corresponding HashEntry name.
248 * For environment and undefined variables, it is allocated. */ 248 * For environment and undefined variables, it is allocated. */
249 const char *name; 249 const char *name;
250 void *name_freeIt; 250 void *name_freeIt;
251 251
252 Buffer val; /* its value */ 252 Buffer val; /* its value */
253 VarFlags flags; /* miscellaneous status flags */ 253 VarFlags flags; /* miscellaneous status flags */
254} Var; 254} Var;
255 255
256/* 256/*
257 * Exporting vars is expensive so skip it if we can 257 * Exporting vars is expensive so skip it if we can
258 */ 258 */
259typedef enum VarExportedMode { 259typedef enum VarExportedMode {
260 VAR_EXPORTED_NONE, 260 VAR_EXPORTED_NONE,
261 VAR_EXPORTED_SOME, 261 VAR_EXPORTED_SOME,
262 VAR_EXPORTED_ALL 262 VAR_EXPORTED_ALL
263} VarExportedMode; 263} VarExportedMode;
264 264
265static VarExportedMode var_exportedVars = VAR_EXPORTED_NONE; 265static VarExportedMode var_exportedVars = VAR_EXPORTED_NONE;
266 266
267typedef enum VarExportFlags { 267typedef enum VarExportFlags {
268 /* 268 /*
269 * We pass this to Var_Export when doing the initial export 269 * We pass this to Var_Export when doing the initial export
270 * or after updating an exported var. 270 * or after updating an exported var.
271 */ 271 */
272 VAR_EXPORT_PARENT = 0x01, 272 VAR_EXPORT_PARENT = 0x01,
273 /* 273 /*
274 * We pass this to Var_Export1 to tell it to leave the value alone. 274 * We pass this to Var_Export1 to tell it to leave the value alone.
275 */ 275 */
276 VAR_EXPORT_LITERAL = 0x02 276 VAR_EXPORT_LITERAL = 0x02
277} VarExportFlags; 277} VarExportFlags;
278 278
279/* Flags for pattern matching in the :S and :C modifiers */ 279/* Flags for pattern matching in the :S and :C modifiers */
280typedef enum VarPatternFlags { 280typedef enum VarPatternFlags {
281 VARP_SUB_GLOBAL = 0x01, /* Apply substitution globally */ 281 VARP_SUB_GLOBAL = 0x01, /* Apply substitution globally */
282 VARP_SUB_ONE = 0x02, /* Apply substitution to one word */ 282 VARP_SUB_ONE = 0x02, /* Apply substitution to one word */
283 VARP_ANCHOR_START = 0x04, /* Match at start of word */ 283 VARP_ANCHOR_START = 0x04, /* Match at start of word */
284 VARP_ANCHOR_END = 0x08 /* Match at end of word */ 284 VARP_ANCHOR_END = 0x08 /* Match at end of word */
285} VarPatternFlags; 285} VarPatternFlags;
286 286
287static Var * 287static Var *
288VarNew(const char *name, void *name_freeIt, const char *value, VarFlags flags) 288VarNew(const char *name, void *name_freeIt, const char *value, VarFlags flags)
289{ 289{
290 size_t value_len = strlen(value); 290 size_t value_len = strlen(value);
291 Var *var = bmake_malloc(sizeof *var); 291 Var *var = bmake_malloc(sizeof *var);
292 var->name = name; 292 var->name = name;
293 var->name_freeIt = name_freeIt; 293 var->name_freeIt = name_freeIt;
294 Buf_Init(&var->val, value_len + 1); 294 Buf_Init(&var->val, value_len + 1);
295 Buf_AddBytes(&var->val, value, value_len); 295 Buf_AddBytes(&var->val, value, value_len);
296 var->flags = flags; 296 var->flags = flags;
297 return var; 297 return var;
298} 298}
299 299
300static const char * 300static const char *
301CanonicalVarname(const char *name) 301CanonicalVarname(const char *name)
302{ 302{
303 if (*name == '.' && ch_isupper(name[1])) { 303 if (*name == '.' && ch_isupper(name[1])) {
304 switch (name[1]) { 304 switch (name[1]) {
305 case 'A': 305 case 'A':
306 if (strcmp(name, ".ALLSRC") == 0) 306 if (strcmp(name, ".ALLSRC") == 0)
307 name = ALLSRC; 307 name = ALLSRC;
308 if (strcmp(name, ".ARCHIVE") == 0) 308 if (strcmp(name, ".ARCHIVE") == 0)
309 name = ARCHIVE; 309 name = ARCHIVE;
310 break; 310 break;
311 case 'I': 311 case 'I':
312 if (strcmp(name, ".IMPSRC") == 0) 312 if (strcmp(name, ".IMPSRC") == 0)
313 name = IMPSRC; 313 name = IMPSRC;
314 break; 314 break;
315 case 'M': 315 case 'M':
316 if (strcmp(name, ".MEMBER") == 0) 316 if (strcmp(name, ".MEMBER") == 0)
317 name = MEMBER; 317 name = MEMBER;
318 break; 318 break;
319 case 'O': 319 case 'O':
320 if (strcmp(name, ".OODATE") == 0) 320 if (strcmp(name, ".OODATE") == 0)
321 name = OODATE; 321 name = OODATE;
322 break; 322 break;
323 case 'P': 323 case 'P':
324 if (strcmp(name, ".PREFIX") == 0) 324 if (strcmp(name, ".PREFIX") == 0)
325 name = PREFIX; 325 name = PREFIX;
326 break; 326 break;
327 case 'S': 327 case 'S':
328 if (strcmp(name, ".SHELL") == 0) { 328 if (strcmp(name, ".SHELL") == 0) {
329 if (!shellPath) 329 if (!shellPath)
330 Shell_Init(); 330 Shell_Init();
331 } 331 }
332 break; 332 break;
333 case 'T': 333 case 'T':
334 if (strcmp(name, ".TARGET") == 0) 334 if (strcmp(name, ".TARGET") == 0)
335 name = TARGET; 335 name = TARGET;
336 break; 336 break;
337 } 337 }
338 } 338 }
339 339
340 /* GNU make has an additional alias $^ == ${.ALLSRC}. */ 340 /* GNU make has an additional alias $^ == ${.ALLSRC}. */
341 341
342 return name; 342 return name;
343} 343}
344 344
345static Var * 345static Var *
346GNode_FindVar(GNode *ctxt, const char *varname, unsigned int hash) 346GNode_FindVar(GNode *ctxt, const char *varname, unsigned int hash)
347{ 347{
348 return HashTable_FindValueHash(&ctxt->context, varname, hash); 348 return HashTable_FindValueHash(&ctxt->context, varname, hash);
349} 349}
350 350
351/*- 351/*-
352 *----------------------------------------------------------------------- 352 *-----------------------------------------------------------------------
353 * VarFind -- 353 * VarFind --
354 * Find the given variable in the given context and any other contexts 354 * Find the given variable in the given context and any other contexts
355 * indicated. 355 * indicated.
356 * 356 *
357 * Input: 357 * Input:
358 * name name to find 358 * name name to find
359 * ctxt context in which to find it 359 * ctxt context in which to find it
360 * elsewhere to look in other contexts as well 360 * elsewhere to look in other contexts as well
361 * 361 *
362 * Results: 362 * Results:
363 * A pointer to the structure describing the desired variable or 363 * A pointer to the structure describing the desired variable or
364 * NULL if the variable does not exist. 364 * NULL if the variable does not exist.
365 *----------------------------------------------------------------------- 365 *-----------------------------------------------------------------------
366 */ 366 */
367static Var * 367static Var *
368VarFind(const char *name, GNode *ctxt, Boolean elsewhere) 368VarFind(const char *name, GNode *ctxt, Boolean elsewhere)
369{ 369{
370 Var *var; 370 Var *var;
371 unsigned int nameHash; 371 unsigned int nameHash;
372 372
373 /* 373 /*
374 * If the variable name begins with a '.', it could very well be one of 374 * If the variable name begins with a '.', it could very well be one of
375 * the local ones. We check the name against all the local variables 375 * the local ones. We check the name against all the local variables
376 * and substitute the short version in for 'name' if it matches one of 376 * and substitute the short version in for 'name' if it matches one of
377 * them. 377 * them.
378 */ 378 */
379 name = CanonicalVarname(name); 379 name = CanonicalVarname(name);
380 nameHash = Hash_Hash(name); 380 nameHash = Hash_Hash(name);
381 381
382 /* 382 /*
383 * First look for the variable in the given context. If it's not there, 383 * First look for the variable in the given context. If it's not there,
384 * look for it in VAR_CMDLINE, VAR_GLOBAL and the environment, in that order, 384 * look for it in VAR_CMDLINE, VAR_GLOBAL and the environment, in that order,
385 * depending on the FIND_* flags in 'flags' 385 * depending on the FIND_* flags in 'flags'
386 */ 386 */
387 var = GNode_FindVar(ctxt, name, nameHash); 387 var = GNode_FindVar(ctxt, name, nameHash);
388 if (!elsewhere) 388 if (!elsewhere)
389 return var; 389 return var;
390 390
391 if (var == NULL && ctxt != VAR_CMDLINE) 391 if (var == NULL && ctxt != VAR_CMDLINE)
392 var = GNode_FindVar(VAR_CMDLINE, name, nameHash); 392 var = GNode_FindVar(VAR_CMDLINE, name, nameHash);
393 393
394 if (!opts.checkEnvFirst && var == NULL && ctxt != VAR_GLOBAL) { 394 if (!opts.checkEnvFirst && var == NULL && ctxt != VAR_GLOBAL) {
395 var = GNode_FindVar(VAR_GLOBAL, name, nameHash); 395 var = GNode_FindVar(VAR_GLOBAL, name, nameHash);
396 if (var == NULL && ctxt != VAR_INTERNAL) { 396 if (var == NULL && ctxt != VAR_INTERNAL) {
397 /* VAR_INTERNAL is subordinate to VAR_GLOBAL */ 397 /* VAR_INTERNAL is subordinate to VAR_GLOBAL */
398 var = GNode_FindVar(VAR_INTERNAL, name, nameHash); 398 var = GNode_FindVar(VAR_INTERNAL, name, nameHash);
399 } 399 }
400 } 400 }
401 401
402 if (var == NULL) { 402 if (var == NULL) {
403 char *env; 403 char *env;
404 404
405 if ((env = getenv(name)) != NULL) { 405 if ((env = getenv(name)) != NULL) {
406 char *varname = bmake_strdup(name); 406 char *varname = bmake_strdup(name);
407 return VarNew(varname, varname, env, VAR_FROM_ENV); 407 return VarNew(varname, varname, env, VAR_FROM_ENV);
408 } 408 }
409 409
410 if (opts.checkEnvFirst && ctxt != VAR_GLOBAL) { 410 if (opts.checkEnvFirst && ctxt != VAR_GLOBAL) {
411 var = GNode_FindVar(VAR_GLOBAL, name, nameHash); 411 var = GNode_FindVar(VAR_GLOBAL, name, nameHash);
412 if (var == NULL && ctxt != VAR_INTERNAL) 412 if (var == NULL && ctxt != VAR_INTERNAL)
413 var = GNode_FindVar(VAR_INTERNAL, name, nameHash); 413 var = GNode_FindVar(VAR_INTERNAL, name, nameHash);
414 return var; 414 return var;
415 } 415 }
416 416
417 return NULL; 417 return NULL;
418 } 418 }
419 419
420 return var; 420 return var;
421} 421}
422 422
423/*- 423/*-
424 *----------------------------------------------------------------------- 424 *-----------------------------------------------------------------------
425 * VarFreeEnv -- 425 * VarFreeEnv --
426 * If the variable is an environment variable, free it 426 * If the variable is an environment variable, free it
427 * 427 *
428 * Input: 428 * Input:
429 * v the variable 429 * v the variable
430 * destroy true if the value buffer should be destroyed. 430 * destroy true if the value buffer should be destroyed.
431 * 431 *
432 * Results: 432 * Results:
433 * TRUE if it is an environment variable, FALSE otherwise. 433 * TRUE if it is an environment variable, FALSE otherwise.
434 *----------------------------------------------------------------------- 434 *-----------------------------------------------------------------------
435 */ 435 */
436static Boolean 436static Boolean
437VarFreeEnv(Var *v, Boolean destroy) 437VarFreeEnv(Var *v, Boolean destroy)
438{ 438{
439 if (!(v->flags & VAR_FROM_ENV)) 439 if (!(v->flags & VAR_FROM_ENV))
440 return FALSE; 440 return FALSE;
441 free(v->name_freeIt); 441 free(v->name_freeIt);
442 Buf_Destroy(&v->val, destroy); 442 Buf_Destroy(&v->val, destroy);
443 free(v); 443 free(v);
444 return TRUE; 444 return TRUE;
445} 445}
446 446
447/* Add a new variable of the given name and value to the given context. 447/* Add a new variable of the given name and value to the given context.
448 * The name and val arguments are duplicated so they may safely be freed. */ 448 * The name and val arguments are duplicated so they may safely be freed. */
449static void 449static void
450VarAdd(const char *name, const char *val, GNode *ctxt, VarSet_Flags flags) 450VarAdd(const char *name, const char *val, GNode *ctxt, VarSet_Flags flags)
451{ 451{
452 HashEntry *he = HashTable_CreateEntry(&ctxt->context, name, NULL); 452 HashEntry *he = HashTable_CreateEntry(&ctxt->context, name, NULL);
453 Var *v = VarNew(he->key, NULL, val, 453 Var *v = VarNew(he->key, NULL, val,
454 flags & VAR_SET_READONLY ? VAR_READONLY : 0); 454 flags & VAR_SET_READONLY ? VAR_READONLY : 0);
455 HashEntry_Set(he, v); 455 HashEntry_Set(he, v);
456 if (!(ctxt->flags & INTERNAL)) { 456 if (!(ctxt->flags & INTERNAL)) {
457 VAR_DEBUG3("%s:%s = %s\n", ctxt->name, name, val); 457 VAR_DEBUG3("%s:%s = %s\n", ctxt->name, name, val);
458 } 458 }
459} 459}
460 460
461/* Remove a variable from a context, freeing the Var structure as well. */ 461/* Remove a variable from a context, freeing the Var structure as well. */
462void 462void
463Var_Delete(const char *name, GNode *ctxt) 463Var_Delete(const char *name, GNode *ctxt)
464{ 464{
465 char *name_freeIt = NULL; 465 char *name_freeIt = NULL;
466 HashEntry *he; 466 HashEntry *he;
467 467
468 if (strchr(name, '$') != NULL) { 468 if (strchr(name, '$') != NULL) {
469 (void)Var_Subst(name, VAR_GLOBAL, VARE_WANTRES, &name_freeIt); 469 (void)Var_Subst(name, VAR_GLOBAL, VARE_WANTRES, &name_freeIt);
470 /* TODO: handle errors */ 470 /* TODO: handle errors */
471 name = name_freeIt; 471 name = name_freeIt;
472 } 472 }
473 he = HashTable_FindEntry(&ctxt->context, name); 473 he = HashTable_FindEntry(&ctxt->context, name);
474 VAR_DEBUG3("%s:delete %s%s\n", 474 VAR_DEBUG3("%s:delete %s%s\n",
475 ctxt->name, name, he != NULL ? "" : " (not found)"); 475 ctxt->name, name, he != NULL ? "" : " (not found)");
476 free(name_freeIt); 476 free(name_freeIt);
477 477
478 if (he != NULL) { 478 if (he != NULL) {
479 Var *v = HashEntry_Get(he); 479 Var *v = HashEntry_Get(he);
480 if (v->flags & VAR_EXPORTED) 480 if (v->flags & VAR_EXPORTED)
481 unsetenv(v->name); 481 unsetenv(v->name);
482 if (strcmp(v->name, MAKE_EXPORTED) == 0) 482 if (strcmp(v->name, MAKE_EXPORTED) == 0)
483 var_exportedVars = VAR_EXPORTED_NONE; 483 var_exportedVars = VAR_EXPORTED_NONE;
484 assert(v->name_freeIt == NULL); 484 assert(v->name_freeIt == NULL);
485 HashTable_DeleteEntry(&ctxt->context, he); 485 HashTable_DeleteEntry(&ctxt->context, he);
486 Buf_Destroy(&v->val, TRUE); 486 Buf_Destroy(&v->val, TRUE);
487 free(v); 487 free(v);
488 } 488 }
489} 489}
490 490
491static Boolean 491static Boolean
492MayExport(const char *name) 492MayExport(const char *name)
493{ 493{
494 if (name[0] == '.') 494 if (name[0] == '.')
495 return FALSE; /* skip internals */ 495 return FALSE; /* skip internals */
496 if (name[0] == '-') 496 if (name[0] == '-')
497 return FALSE; /* skip misnamed variables */ 497 return FALSE; /* skip misnamed variables */
498 if (name[1] == '\0') { 498 if (name[1] == '\0') {
499 /* 499 /*
500 * A single char. 500 * A single char.
501 * If it is one of the vars that should only appear in 501 * If it is one of the vars that should only appear in
502 * local context, skip it, else we can get Var_Subst 502 * local context, skip it, else we can get Var_Subst
503 * into a loop. 503 * into a loop.
504 */ 504 */
505 switch (name[0]) { 505 switch (name[0]) {
506 case '@': 506 case '@':
507 case '%': 507 case '%':
508 case '*': 508 case '*':
509 case '!': 509 case '!':
510 return FALSE; 510 return FALSE;
511 } 511 }
512 } 512 }
513 return TRUE; 513 return TRUE;
514} 514}
515 515
516/* 516/*
517 * Export a single variable. 517 * Export a single variable.
518 * We ignore make internal variables (those which start with '.'). 518 * We ignore make internal variables (those which start with '.').
519 * Also we jump through some hoops to avoid calling setenv 519 * Also we jump through some hoops to avoid calling setenv
520 * more than necessary since it can leak. 520 * more than necessary since it can leak.
521 * We only manipulate flags of vars if 'parent' is set. 521 * We only manipulate flags of vars if 'parent' is set.
522 */ 522 */
523static Boolean 523static Boolean
524Var_Export1(const char *name, VarExportFlags flags) 524Var_Export1(const char *name, VarExportFlags flags)
525{ 525{
526 VarExportFlags parent = flags & VAR_EXPORT_PARENT; 526 VarExportFlags parent = flags & VAR_EXPORT_PARENT;
527 Var *v; 527 Var *v;
528 char *val; 528 char *val;
529 529
530 if (!MayExport(name)) 530 if (!MayExport(name))
531 return FALSE; 531 return FALSE;
532 532
533 v = VarFind(name, VAR_GLOBAL, 0); 533 v = VarFind(name, VAR_GLOBAL, 0);
534 if (v == NULL) 534 if (v == NULL)
535 return FALSE; 535 return FALSE;
536 536
537 if (!parent && (v->flags & VAR_EXPORTED) && !(v->flags & VAR_REEXPORT)) 537 if (!parent && (v->flags & VAR_EXPORTED) && !(v->flags & VAR_REEXPORT))
538 return FALSE; /* nothing to do */ 538 return FALSE; /* nothing to do */
539 539
540 val = Buf_GetAll(&v->val, NULL); 540 val = Buf_GetAll(&v->val, NULL);
541 if (!(flags & VAR_EXPORT_LITERAL) && strchr(val, '$') != NULL) { 541 if (!(flags & VAR_EXPORT_LITERAL) && strchr(val, '$') != NULL) {
542 char *expr; 542 char *expr;
543 543
544 if (parent) { 544 if (parent) {
545 /* 545 /*
546 * Flag this as something we need to re-export. 546 * Flag this as something we need to re-export.
547 * No point actually exporting it now though, 547 * No point actually exporting it now though,
548 * the child can do it at the last minute. 548 * the child can do it at the last minute.
549 */ 549 */
550 v->flags |= VAR_EXPORTED | VAR_REEXPORT; 550 v->flags |= VAR_EXPORTED | VAR_REEXPORT;
551 return TRUE; 551 return TRUE;
552 } 552 }
553 if (v->flags & VAR_IN_USE) { 553 if (v->flags & VAR_IN_USE) {
554 /* 554 /*
555 * We recursed while exporting in a child. 555 * We recursed while exporting in a child.
556 * This isn't going to end well, just skip it. 556 * This isn't going to end well, just skip it.
557 */ 557 */
558 return FALSE; 558 return FALSE;
559 } 559 }
560 560
561 expr = str_concat3("${", name, "}"); 561 expr = str_concat3("${", name, "}");
562 (void)Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES, &val); 562 (void)Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES, &val);
563 /* TODO: handle errors */ 563 /* TODO: handle errors */
564 setenv(name, val, 1); 564 setenv(name, val, 1);
565 free(val); 565 free(val);
566 free(expr); 566 free(expr);
567 } else { 567 } else {
568 if (parent) 568 if (parent)
569 v->flags &= ~(unsigned)VAR_REEXPORT; /* once will do */ 569 v->flags &= ~(unsigned)VAR_REEXPORT; /* once will do */
570 if (parent || !(v->flags & VAR_EXPORTED)) 570 if (parent || !(v->flags & VAR_EXPORTED))
571 setenv(name, val, 1); 571 setenv(name, val, 1);
572 } 572 }
573 /* 573 /*
574 * This is so Var_Set knows to call Var_Export again... 574 * This is so Var_Set knows to call Var_Export again...
575 */ 575 */
576 if (parent) { 576 if (parent) {
577 v->flags |= VAR_EXPORTED; 577 v->flags |= VAR_EXPORTED;
578 } 578 }
579 return TRUE; 579 return TRUE;
580} 580}
581 581
582/* 582/*
583 * This gets called from our children. 583 * This gets called from our children.
584 */ 584 */
585void 585void
586Var_ExportVars(void) 586Var_ExportVars(void)
587{ 587{
588 char *val; 588 char *val;
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 char tmp[BUFSIZ]; 596 char tmp[BUFSIZ];
597 snprintf(tmp, sizeof(tmp), "%d", makelevel + 1); 597 snprintf(tmp, sizeof(tmp), "%d", makelevel + 1);
598 setenv(MAKE_LEVEL_ENV, tmp, 1); 598 setenv(MAKE_LEVEL_ENV, tmp, 1);
599 599
600 if (var_exportedVars == VAR_EXPORTED_NONE) 600 if (var_exportedVars == VAR_EXPORTED_NONE)
601 return; 601 return;
602 602
603 if (var_exportedVars == VAR_EXPORTED_ALL) { 603 if (var_exportedVars == VAR_EXPORTED_ALL) {
604 HashIter hi; 604 HashIter hi;
605 HashEntry *he; 
606 605
607 /* Ouch! Exporting all variables at once is crazy... */ 606 /* Ouch! Exporting all variables at once is crazy... */
608 HashIter_Init(&hi, &VAR_GLOBAL->context); 607 HashIter_Init(&hi, &VAR_GLOBAL->context);
609 while ((he = HashIter_Next(&hi)) != NULL) { 608 while (HashIter_Next(&hi) != NULL) {
610 Var *var = HashEntry_Get(he); 609 Var *var = hi.entry->value;
611 Var_Export1(var->name, 0); 610 Var_Export1(var->name, 0);
612 } 611 }
613 return; 612 return;
614 } 613 }
615 614
616 (void)Var_Subst("${" MAKE_EXPORTED ":O:u}", VAR_GLOBAL, VARE_WANTRES, &val); 615 (void)Var_Subst("${" MAKE_EXPORTED ":O:u}", VAR_GLOBAL, VARE_WANTRES, &val);
617 /* TODO: handle errors */ 616 /* TODO: handle errors */
618 if (*val) { 617 if (*val) {
619 Words words = Str_Words(val, FALSE); 618 Words words = Str_Words(val, FALSE);
620 size_t i; 619 size_t i;
621 620
622 for (i = 0; i < words.len; i++) 621 for (i = 0; i < words.len; i++)
623 Var_Export1(words.words[i], 0); 622 Var_Export1(words.words[i], 0);
624 Words_Free(words); 623 Words_Free(words);
625 } 624 }
626 free(val); 625 free(val);
627} 626}
628 627
629/* 628/*
630 * This is called when .export is seen or .MAKE.EXPORTED is modified. 629 * This is called when .export is seen or .MAKE.EXPORTED is modified.
631 * 630 *
632 * It is also called when any exported variable is modified. 631 * It is also called when any exported variable is modified.
633 * XXX: Is it really? 632 * XXX: Is it really?
634 * 633 *
635 * str has the format "[-env|-literal] varname...". 634 * str has the format "[-env|-literal] varname...".
636 */ 635 */
637void 636void
638Var_Export(const char *str, Boolean isExport) 637Var_Export(const char *str, Boolean isExport)
639{ 638{
640 VarExportFlags flags; 639 VarExportFlags flags;
641 char *val; 640 char *val;
642 641
643 if (isExport && str[0] == '\0') { 642 if (isExport && str[0] == '\0') {
644 var_exportedVars = VAR_EXPORTED_ALL; /* use with caution! */ 643 var_exportedVars = VAR_EXPORTED_ALL; /* use with caution! */
645 return; 644 return;
646 } 645 }
647 646
648 if (isExport && strncmp(str, "-env", 4) == 0) { 647 if (isExport && strncmp(str, "-env", 4) == 0) {
649 str += 4; 648 str += 4;
650 flags = 0; 649 flags = 0;
651 } else if (isExport && strncmp(str, "-literal", 8) == 0) { 650 } else if (isExport && strncmp(str, "-literal", 8) == 0) {
652 str += 8; 651 str += 8;
653 flags = VAR_EXPORT_LITERAL; 652 flags = VAR_EXPORT_LITERAL;
654 } else { 653 } else {
655 flags = VAR_EXPORT_PARENT; 654 flags = VAR_EXPORT_PARENT;
656 } 655 }
657 656
658 (void)Var_Subst(str, VAR_GLOBAL, VARE_WANTRES, &val); 657 (void)Var_Subst(str, VAR_GLOBAL, VARE_WANTRES, &val);
659 /* TODO: handle errors */ 658 /* TODO: handle errors */
660 if (val[0] != '\0') { 659 if (val[0] != '\0') {
661 Words words = Str_Words(val, FALSE); 660 Words words = Str_Words(val, FALSE);
662 661
663 size_t i; 662 size_t i;
664 for (i = 0; i < words.len; i++) { 663 for (i = 0; i < words.len; i++) {
665 const char *name = words.words[i]; 664 const char *name = words.words[i];
666 if (Var_Export1(name, flags)) { 665 if (Var_Export1(name, flags)) {
667 if (var_exportedVars == VAR_EXPORTED_NONE) 666 if (var_exportedVars == VAR_EXPORTED_NONE)
668 var_exportedVars = VAR_EXPORTED_SOME; 667 var_exportedVars = VAR_EXPORTED_SOME;
669 if (isExport && (flags & VAR_EXPORT_PARENT)) { 668 if (isExport && (flags & VAR_EXPORT_PARENT)) {
670 Var_Append(MAKE_EXPORTED, name, VAR_GLOBAL); 669 Var_Append(MAKE_EXPORTED, name, VAR_GLOBAL);
671 } 670 }
672 } 671 }
673 } 672 }
674 Words_Free(words); 673 Words_Free(words);
675 } 674 }
676 free(val); 675 free(val);
677} 676}
678 677
679 678
680extern char **environ; 679extern char **environ;
681 680
682/* 681/*
683 * This is called when .unexport[-env] is seen. 682 * This is called when .unexport[-env] is seen.
684 * 683 *
685 * str must have the form "unexport[-env] varname...". 684 * str must have the form "unexport[-env] varname...".
686 */ 685 */
687void 686void
688Var_UnExport(const char *str) 687Var_UnExport(const char *str)
689{ 688{
690 const char *varnames; 689 const char *varnames;
691 char *varnames_freeIt; 690 char *varnames_freeIt;
692 Boolean unexport_env; 691 Boolean unexport_env;
693 692
694 varnames = NULL; 693 varnames = NULL;
695 varnames_freeIt = NULL; 694 varnames_freeIt = NULL;
696 695
697 str += strlen("unexport"); 696 str += strlen("unexport");
698 unexport_env = strncmp(str, "-env", 4) == 0; 697 unexport_env = strncmp(str, "-env", 4) == 0;
699 if (unexport_env) { 698 if (unexport_env) {
700 const char *cp; 699 const char *cp;
701 char **newenv; 700 char **newenv;
702 701
703 cp = getenv(MAKE_LEVEL_ENV); /* we should preserve this */ 702 cp = getenv(MAKE_LEVEL_ENV); /* we should preserve this */
704 if (environ == savedEnv) { 703 if (environ == savedEnv) {
705 /* we have been here before! */ 704 /* we have been here before! */
706 newenv = bmake_realloc(environ, 2 * sizeof(char *)); 705 newenv = bmake_realloc(environ, 2 * sizeof(char *));
707 } else { 706 } else {
708 if (savedEnv) { 707 if (savedEnv) {
709 free(savedEnv); 708 free(savedEnv);
710 savedEnv = NULL; 709 savedEnv = NULL;
711 } 710 }
712 newenv = bmake_malloc(2 * sizeof(char *)); 711 newenv = bmake_malloc(2 * sizeof(char *));
713 } 712 }
714 713
715 /* Note: we cannot safely free() the original environ. */ 714 /* Note: we cannot safely free() the original environ. */
716 environ = savedEnv = newenv; 715 environ = savedEnv = newenv;
717 newenv[0] = NULL; 716 newenv[0] = NULL;
718 newenv[1] = NULL; 717 newenv[1] = NULL;
719 if (cp && *cp) 718 if (cp && *cp)
720 setenv(MAKE_LEVEL_ENV, cp, 1); 719 setenv(MAKE_LEVEL_ENV, cp, 1);
721 } else { 720 } else {
722 cpp_skip_whitespace(&str); 721 cpp_skip_whitespace(&str);
723 if (str[0] != '\0') 722 if (str[0] != '\0')
724 varnames = str; 723 varnames = str;
725 } 724 }
726 725
727 if (varnames == NULL) { 726 if (varnames == NULL) {
728 /* Using .MAKE.EXPORTED */ 727 /* Using .MAKE.EXPORTED */
729 (void)Var_Subst("${" MAKE_EXPORTED ":O:u}", VAR_GLOBAL, VARE_WANTRES, 728 (void)Var_Subst("${" MAKE_EXPORTED ":O:u}", VAR_GLOBAL, VARE_WANTRES,
730 &varnames_freeIt); 729 &varnames_freeIt);
731 /* TODO: handle errors */ 730 /* TODO: handle errors */
732 varnames = varnames_freeIt; 731 varnames = varnames_freeIt;
733 } 732 }
734 733
735 { 734 {
736 Var *v; 735 Var *v;
737 size_t i; 736 size_t i;
738 737
739 Words words = Str_Words(varnames, FALSE); 738 Words words = Str_Words(varnames, FALSE);
740 for (i = 0; i < words.len; i++) { 739 for (i = 0; i < words.len; i++) {
741 const char *varname = words.words[i]; 740 const char *varname = words.words[i];
742 v = VarFind(varname, VAR_GLOBAL, 0); 741 v = VarFind(varname, VAR_GLOBAL, 0);
743 if (v == NULL) { 742 if (v == NULL) {
744 VAR_DEBUG1("Not unexporting \"%s\" (not found)\n", varname); 743 VAR_DEBUG1("Not unexporting \"%s\" (not found)\n", varname);
745 continue; 744 continue;
746 } 745 }
747 746
748 VAR_DEBUG1("Unexporting \"%s\"\n", varname); 747 VAR_DEBUG1("Unexporting \"%s\"\n", varname);
749 if (!unexport_env && (v->flags & VAR_EXPORTED) && 748 if (!unexport_env && (v->flags & VAR_EXPORTED) &&
750 !(v->flags & VAR_REEXPORT)) 749 !(v->flags & VAR_REEXPORT))
751 unsetenv(v->name); 750 unsetenv(v->name);
752 v->flags &= ~(unsigned)(VAR_EXPORTED | VAR_REEXPORT); 751 v->flags &= ~(unsigned)(VAR_EXPORTED | VAR_REEXPORT);
753 752
754 /* 753 /*
755 * If we are unexporting a list, 754 * If we are unexporting a list,
756 * remove each one from .MAKE.EXPORTED. 755 * remove each one from .MAKE.EXPORTED.
757 * If we are removing them all, 756 * If we are removing them all,
758 * just delete .MAKE.EXPORTED below. 757 * just delete .MAKE.EXPORTED below.
759 */ 758 */
760 if (varnames == str) { 759 if (varnames == str) {
761 char *expr = str_concat3("${" MAKE_EXPORTED ":N", v->name, "}"); 760 char *expr = str_concat3("${" MAKE_EXPORTED ":N", v->name, "}");
762 char *cp; 761 char *cp;
763 (void)Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES, &cp); 762 (void)Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES, &cp);
764 /* TODO: handle errors */ 763 /* TODO: handle errors */
765 Var_Set(MAKE_EXPORTED, cp, VAR_GLOBAL); 764 Var_Set(MAKE_EXPORTED, cp, VAR_GLOBAL);
766 free(cp); 765 free(cp);
767 free(expr); 766 free(expr);
768 } 767 }
769 } 768 }
770 Words_Free(words); 769 Words_Free(words);
771 if (varnames != str) { 770 if (varnames != str) {
772 Var_Delete(MAKE_EXPORTED, VAR_GLOBAL); 771 Var_Delete(MAKE_EXPORTED, VAR_GLOBAL);
773 free(varnames_freeIt); 772 free(varnames_freeIt);
774 } 773 }
775 } 774 }
776} 775}
777 776
778/* See Var_Set for documentation. */ 777/* See Var_Set for documentation. */
779void 778void
780Var_Set_with_flags(const char *name, const char *val, GNode *ctxt, 779Var_Set_with_flags(const char *name, const char *val, GNode *ctxt,
781 VarSet_Flags flags) 780 VarSet_Flags flags)
782{ 781{
783 const char *unexpanded_name = name; 782 const char *unexpanded_name = name;
784 char *name_freeIt = NULL; 783 char *name_freeIt = NULL;
785 Var *v; 784 Var *v;
786 785
787 assert(val != NULL); 786 assert(val != NULL);
788 787
789 if (strchr(name, '$') != NULL) { 788 if (strchr(name, '$') != NULL) {
790 (void)Var_Subst(name, ctxt, VARE_WANTRES, &name_freeIt); 789 (void)Var_Subst(name, ctxt, VARE_WANTRES, &name_freeIt);
791 /* TODO: handle errors */ 790 /* TODO: handle errors */
792 name = name_freeIt; 791 name = name_freeIt;
793 } 792 }
794 793
795 if (name[0] == '\0') { 794 if (name[0] == '\0') {
796 VAR_DEBUG2("Var_Set(\"%s\", \"%s\", ...) " 795 VAR_DEBUG2("Var_Set(\"%s\", \"%s\", ...) "
797 "name expands to empty string - ignored\n", 796 "name expands to empty string - ignored\n",
798 unexpanded_name, val); 797 unexpanded_name, val);
799 free(name_freeIt); 798 free(name_freeIt);
800 return; 799 return;
801 } 800 }
802 801
803 if (ctxt == VAR_GLOBAL) { 802 if (ctxt == VAR_GLOBAL) {
804 v = VarFind(name, VAR_CMDLINE, 0); 803 v = VarFind(name, VAR_CMDLINE, 0);
805 if (v != NULL) { 804 if (v != NULL) {
806 if (v->flags & VAR_FROM_CMD) { 805 if (v->flags & VAR_FROM_CMD) {
807 VAR_DEBUG3("%s:%s = %s ignored!\n", ctxt->name, name, val); 806 VAR_DEBUG3("%s:%s = %s ignored!\n", ctxt->name, name, val);
808 goto out; 807 goto out;
809 } 808 }
810 VarFreeEnv(v, TRUE); 809 VarFreeEnv(v, TRUE);
811 } 810 }
812 } 811 }
813 812
814 /* 813 /*
815 * We only look for a variable in the given context since anything set 814 * We only look for a variable in the given context since anything set
816 * here will override anything in a lower context, so there's not much 815 * here will override anything in a lower context, so there's not much
817 * point in searching them all just to save a bit of memory... 816 * point in searching them all just to save a bit of memory...
818 */ 817 */
819 v = VarFind(name, ctxt, 0); 818 v = VarFind(name, ctxt, 0);
820 if (v == NULL) { 819 if (v == NULL) {
821 if (ctxt == VAR_CMDLINE && !(flags & VAR_NO_EXPORT)) { 820 if (ctxt == VAR_CMDLINE && !(flags & VAR_NO_EXPORT)) {
822 /* 821 /*
823 * This var would normally prevent the same name being added 822 * This var would normally prevent the same name being added
824 * to VAR_GLOBAL, so delete it from there if needed. 823 * to VAR_GLOBAL, so delete it from there if needed.
825 * Otherwise -V name may show the wrong value. 824 * Otherwise -V name may show the wrong value.
826 */ 825 */
827 Var_Delete(name, VAR_GLOBAL); 826 Var_Delete(name, VAR_GLOBAL);
828 } 827 }
829 VarAdd(name, val, ctxt, flags); 828 VarAdd(name, val, ctxt, flags);
830 } else { 829 } else {
831 if ((v->flags & VAR_READONLY) && !(flags & VAR_SET_READONLY)) { 830 if ((v->flags & VAR_READONLY) && !(flags & VAR_SET_READONLY)) {
832 VAR_DEBUG3("%s:%s = %s ignored (read-only)\n", 831 VAR_DEBUG3("%s:%s = %s ignored (read-only)\n",
833 ctxt->name, name, val); 832 ctxt->name, name, val);
834 goto out; 833 goto out;
835 } 834 }
836 Buf_Empty(&v->val); 835 Buf_Empty(&v->val);
837 if (val) 836 if (val)
838 Buf_AddStr(&v->val, val); 837 Buf_AddStr(&v->val, val);
839 838
840 VAR_DEBUG3("%s:%s = %s\n", ctxt->name, name, val); 839 VAR_DEBUG3("%s:%s = %s\n", ctxt->name, name, val);
841 if (v->flags & VAR_EXPORTED) { 840 if (v->flags & VAR_EXPORTED) {
842 Var_Export1(name, VAR_EXPORT_PARENT); 841 Var_Export1(name, VAR_EXPORT_PARENT);
843 } 842 }
844 } 843 }
845 /* 844 /*
846 * Any variables given on the command line are automatically exported 845 * Any variables given on the command line are automatically exported
847 * to the environment (as per POSIX standard) 846 * to the environment (as per POSIX standard)
848 * Other than internals. 847 * Other than internals.
849 */ 848 */
850 if (ctxt == VAR_CMDLINE && !(flags & VAR_NO_EXPORT) && name[0] != '.') { 849 if (ctxt == VAR_CMDLINE && !(flags & VAR_NO_EXPORT) && name[0] != '.') {
851 if (v == NULL) { 850 if (v == NULL) {
852 /* we just added it */ 851 /* we just added it */
853 v = VarFind(name, ctxt, 0); 852 v = VarFind(name, ctxt, 0);
854 } 853 }
855 if (v != NULL) 854 if (v != NULL)
856 v->flags |= VAR_FROM_CMD; 855 v->flags |= VAR_FROM_CMD;
857 /* 856 /*
858 * If requested, don't export these in the environment 857 * If requested, don't export these in the environment
859 * individually. We still put them in MAKEOVERRIDES so 858 * individually. We still put them in MAKEOVERRIDES so
860 * that the command-line settings continue to override 859 * that the command-line settings continue to override
861 * Makefile settings. 860 * Makefile settings.
862 */ 861 */
863 if (!opts.varNoExportEnv) 862 if (!opts.varNoExportEnv)
864 setenv(name, val ? val : "", 1); 863 setenv(name, val ? val : "", 1);
865 864
866 Var_Append(MAKEOVERRIDES, name, VAR_GLOBAL); 865 Var_Append(MAKEOVERRIDES, name, VAR_GLOBAL);
867 } 866 }
868 if (name[0] == '.' && strcmp(name, MAKE_SAVE_DOLLARS) == 0) 867 if (name[0] == '.' && strcmp(name, MAKE_SAVE_DOLLARS) == 0)
869 save_dollars = s2Boolean(val, save_dollars); 868 save_dollars = s2Boolean(val, save_dollars);
870 869
871out: 870out:
872 free(name_freeIt); 871 free(name_freeIt);
873 if (v != NULL) 872 if (v != NULL)
874 VarFreeEnv(v, TRUE); 873 VarFreeEnv(v, TRUE);
875} 874}
876 875
877/*- 876/*-
878 *----------------------------------------------------------------------- 877 *-----------------------------------------------------------------------
879 * Var_Set -- 878 * Var_Set --
880 * Set the variable name to the value val in the given context. 879 * Set the variable name to the value val in the given context.
881 * 880 *
882 * If the variable doesn't yet exist, it is created. 881 * If the variable doesn't yet exist, it is created.
883 * Otherwise the new value overwrites and replaces the old value. 882 * Otherwise the new value overwrites and replaces the old value.
884 * 883 *
885 * Input: 884 * Input:
886 * name name of variable to set 885 * name name of variable to set
887 * val value to give to the variable 886 * val value to give to the variable
888 * ctxt context in which to set it 887 * ctxt context in which to set it
889 * 888 *
890 * Notes: 889 * Notes:
891 * The variable is searched for only in its context before being 890 * The variable is searched for only in its context before being
892 * created in that context. I.e. if the context is VAR_GLOBAL, 891 * created in that context. I.e. if the context is VAR_GLOBAL,
893 * only VAR_GLOBAL->context is searched. Likewise if it is VAR_CMDLINE, 892 * only VAR_GLOBAL->context is searched. Likewise if it is VAR_CMDLINE,
894 * only VAR_CMDLINE->context is searched. This is done to avoid the 893 * only VAR_CMDLINE->context is searched. This is done to avoid the
895 * literally thousands of unnecessary strcmp's that used to be done to 894 * literally thousands of unnecessary strcmp's that used to be done to
896 * set, say, $(@) or $(<). 895 * set, say, $(@) or $(<).
897 * If the context is VAR_GLOBAL though, we check if the variable 896 * If the context is VAR_GLOBAL though, we check if the variable
898 * was set in VAR_CMDLINE from the command line and skip it if so. 897 * was set in VAR_CMDLINE from the command line and skip it if so.
899 *----------------------------------------------------------------------- 898 *-----------------------------------------------------------------------
900 */ 899 */
901void 900void
902Var_Set(const char *name, const char *val, GNode *ctxt) 901Var_Set(const char *name, const char *val, GNode *ctxt)
903{ 902{
904 Var_Set_with_flags(name, val, ctxt, 0); 903 Var_Set_with_flags(name, val, ctxt, 0);
905} 904}
906 905
907/*- 906/*-
908 *----------------------------------------------------------------------- 907 *-----------------------------------------------------------------------
909 * Var_Append -- 908 * Var_Append --
910 * The variable of the given name has the given value appended to it in 909 * The variable of the given name has the given value appended to it in
911 * the given context. 910 * the given context.
912 * 911 *
913 * If the variable doesn't exist, it is created. Otherwise the strings 912 * If the variable doesn't exist, it is created. Otherwise the strings
914 * are concatenated, with a space in between. 913 * are concatenated, with a space in between.
915 * 914 *
916 * Input: 915 * Input:
917 * name name of variable to modify 916 * name name of variable to modify
918 * val string to append to it 917 * val string to append to it
919 * ctxt context in which this should occur 918 * ctxt context in which this should occur
920 * 919 *
921 * Notes: 920 * Notes:
922 * Only if the variable is being sought in the global context is the 921 * Only if the variable is being sought in the global context is the
923 * environment searched. 922 * environment searched.
924 * XXX: Knows its calling circumstances in that if called with ctxt 923 * XXX: Knows its calling circumstances in that if called with ctxt
925 * an actual target, it will only search that context since only 924 * an actual target, it will only search that context since only
926 * a local variable could be being appended to. This is actually 925 * a local variable could be being appended to. This is actually
927 * a big win and must be tolerated. 926 * a big win and must be tolerated.
928 *----------------------------------------------------------------------- 927 *-----------------------------------------------------------------------
929 */ 928 */
930void 929void
931Var_Append(const char *name, const char *val, GNode *ctxt) 930Var_Append(const char *name, const char *val, GNode *ctxt)
932{ 931{
933 char *name_freeIt = NULL; 932 char *name_freeIt = NULL;
934 Var *v; 933 Var *v;
935 934
936 assert(val != NULL); 935 assert(val != NULL);
937 936
938 if (strchr(name, '$') != NULL) { 937 if (strchr(name, '$') != NULL) {
939 const char *unexpanded_name = name; 938 const char *unexpanded_name = name;
940 (void)Var_Subst(name, ctxt, VARE_WANTRES, &name_freeIt); 939 (void)Var_Subst(name, ctxt, VARE_WANTRES, &name_freeIt);
941 /* TODO: handle errors */ 940 /* TODO: handle errors */
942 name = name_freeIt; 941 name = name_freeIt;
943 if (name[0] == '\0') { 942 if (name[0] == '\0') {
944 VAR_DEBUG2("Var_Append(\"%s\", \"%s\", ...) " 943 VAR_DEBUG2("Var_Append(\"%s\", \"%s\", ...) "
945 "name expands to empty string - ignored\n", 944 "name expands to empty string - ignored\n",
946 unexpanded_name, val); 945 unexpanded_name, val);
947 free(name_freeIt); 946 free(name_freeIt);
948 return; 947 return;
949 } 948 }
950 } 949 }
951 950
952 v = VarFind(name, ctxt, ctxt == VAR_GLOBAL); 951 v = VarFind(name, ctxt, ctxt == VAR_GLOBAL);
953 952
954 if (v == NULL) { 953 if (v == NULL) {
955 Var_Set(name, val, ctxt); 954 Var_Set(name, val, ctxt);
956 } else if (v->flags & VAR_READONLY) { 955 } else if (v->flags & VAR_READONLY) {
957 VAR_DEBUG1("Ignoring append to %s since it is read-only\n", name); 956 VAR_DEBUG1("Ignoring append to %s since it is read-only\n", name);
958 } else if (ctxt == VAR_CMDLINE || !(v->flags & VAR_FROM_CMD)) { 957 } else if (ctxt == VAR_CMDLINE || !(v->flags & VAR_FROM_CMD)) {
959 Buf_AddByte(&v->val, ' '); 958 Buf_AddByte(&v->val, ' ');
960 Buf_AddStr(&v->val, val); 959 Buf_AddStr(&v->val, val);
961 960
962 VAR_DEBUG3("%s:%s = %s\n", 961 VAR_DEBUG3("%s:%s = %s\n",
963 ctxt->name, name, Buf_GetAll(&v->val, NULL)); 962 ctxt->name, name, Buf_GetAll(&v->val, NULL));
964 963
965 if (v->flags & VAR_FROM_ENV) { 964 if (v->flags & VAR_FROM_ENV) {
966 HashEntry *h; 965 HashEntry *h;
967 966
968 /* 967 /*
969 * If the original variable came from the environment, we 968 * If the original variable came from the environment, we
970 * have to install it in the global context (we could place 969 * have to install it in the global context (we could place
971 * it in the environment, but then we should provide a way to 970 * it in the environment, but then we should provide a way to
972 * export other variables...) 971 * export other variables...)
973 */ 972 */
974 v->flags &= ~(unsigned)VAR_FROM_ENV; 973 v->flags &= ~(unsigned)VAR_FROM_ENV;
975 h = HashTable_CreateEntry(&ctxt->context, name, NULL); 974 h = HashTable_CreateEntry(&ctxt->context, name, NULL);
976 HashEntry_Set(h, v); 975 HashEntry_Set(h, v);
977 } 976 }
978 } 977 }
979 free(name_freeIt); 978 free(name_freeIt);
980} 979}
981 980
982/* See if the given variable exists, in the given context or in other 981/* See if the given variable exists, in the given context or in other
983 * fallback contexts. 982 * fallback contexts.
984 * 983 *
985 * Input: 984 * Input:
986 * name Variable to find 985 * name Variable to find
987 * ctxt Context in which to start search 986 * ctxt Context in which to start search
988 */ 987 */
989Boolean 988Boolean
990Var_Exists(const char *name, GNode *ctxt) 989Var_Exists(const char *name, GNode *ctxt)
991{ 990{
992 char *name_freeIt = NULL; 991 char *name_freeIt = NULL;
993 Var *v; 992 Var *v;
994 993
995 if (strchr(name, '$') != NULL) { 994 if (strchr(name, '$') != NULL) {
996 (void)Var_Subst(name, ctxt, VARE_WANTRES, &name_freeIt); 995 (void)Var_Subst(name, ctxt, VARE_WANTRES, &name_freeIt);
997 /* TODO: handle errors */ 996 /* TODO: handle errors */
998 name = name_freeIt; 997 name = name_freeIt;
999 } 998 }
1000 999
1001 v = VarFind(name, ctxt, TRUE); 1000 v = VarFind(name, ctxt, TRUE);
1002 free(name_freeIt); 1001 free(name_freeIt);
1003 if (v == NULL) 1002 if (v == NULL)
1004 return FALSE; 1003 return FALSE;
1005 1004
1006 (void)VarFreeEnv(v, TRUE); 1005 (void)VarFreeEnv(v, TRUE);
1007 return TRUE; 1006 return TRUE;
1008} 1007}
1009 1008
1010/*- 1009/*-
1011 *----------------------------------------------------------------------- 1010 *-----------------------------------------------------------------------
1012 * Var_Value -- 1011 * Var_Value --
1013 * Return the unexpanded value of the given variable in the given 1012 * Return the unexpanded value of the given variable in the given
1014 * context, or the usual contexts. 1013 * context, or the usual contexts.
1015 * 1014 *
1016 * Input: 1015 * Input:
1017 * name name to find 1016 * name name to find
1018 * ctxt context in which to search for it 1017 * ctxt context in which to search for it
1019 * 1018 *
1020 * Results: 1019 * Results:
1021 * The value if the variable exists, NULL if it doesn't. 1020 * The value if the variable exists, NULL if it doesn't.
1022 * If the returned value is not NULL, the caller must free *freeIt 1021 * If the returned value is not NULL, the caller must free *freeIt
1023 * as soon as the returned value is no longer needed. 1022 * as soon as the returned value is no longer needed.
1024 *----------------------------------------------------------------------- 1023 *-----------------------------------------------------------------------
1025 */ 1024 */
1026const char * 1025const char *
1027Var_Value(const char *name, GNode *ctxt, char **freeIt) 1026Var_Value(const char *name, GNode *ctxt, char **freeIt)
1028{ 1027{
1029 Var *v = VarFind(name, ctxt, TRUE); 1028 Var *v = VarFind(name, ctxt, TRUE);
1030 char *p; 1029 char *p;
1031 1030
1032 *freeIt = NULL; 1031 *freeIt = NULL;
1033 if (v == NULL) 1032 if (v == NULL)
1034 return NULL; 1033 return NULL;
1035 1034
1036 p = Buf_GetAll(&v->val, NULL); 1035 p = Buf_GetAll(&v->val, NULL);
1037 if (VarFreeEnv(v, FALSE)) 1036 if (VarFreeEnv(v, FALSE))
1038 *freeIt = p; 1037 *freeIt = p;
1039 return p; 1038 return p;
1040} 1039}
1041 1040
1042 1041
1043/* SepBuf is a string being built from "words", interleaved with separators. */ 1042/* SepBuf is a string being built from "words", interleaved with separators. */
1044typedef struct SepBuf { 1043typedef struct SepBuf {
1045 Buffer buf; 1044 Buffer buf;
1046 Boolean needSep; 1045 Boolean needSep;
1047 char sep; /* usually ' ', but see the :ts modifier */ 1046 char sep; /* usually ' ', but see the :ts modifier */
1048} SepBuf; 1047} SepBuf;
1049 1048
1050static void 1049static void
1051SepBuf_Init(SepBuf *buf, char sep) 1050SepBuf_Init(SepBuf *buf, char sep)
1052{ 1051{
1053 Buf_Init(&buf->buf, 32 /* bytes */); 1052 Buf_Init(&buf->buf, 32 /* bytes */);
1054 buf->needSep = FALSE; 1053 buf->needSep = FALSE;
1055 buf->sep = sep; 1054 buf->sep = sep;
1056} 1055}
1057 1056
1058static void 1057static void
1059SepBuf_Sep(SepBuf *buf) 1058SepBuf_Sep(SepBuf *buf)
1060{ 1059{
1061 buf->needSep = TRUE; 1060 buf->needSep = TRUE;
1062} 1061}
1063 1062
1064static void 1063static void
1065SepBuf_AddBytes(SepBuf *buf, const char *mem, size_t mem_size) 1064SepBuf_AddBytes(SepBuf *buf, const char *mem, size_t mem_size)
1066{ 1065{
1067 if (mem_size == 0) 1066 if (mem_size == 0)
1068 return; 1067 return;
1069 if (buf->needSep && buf->sep != '\0') { 1068 if (buf->needSep && buf->sep != '\0') {
1070 Buf_AddByte(&buf->buf, buf->sep); 1069 Buf_AddByte(&buf->buf, buf->sep);
1071 buf->needSep = FALSE; 1070 buf->needSep = FALSE;
1072 } 1071 }
1073 Buf_AddBytes(&buf->buf, mem, mem_size); 1072 Buf_AddBytes(&buf->buf, mem, mem_size);
1074} 1073}
1075 1074
1076static void 1075static void
1077SepBuf_AddBytesBetween(SepBuf *buf, const char *start, const char *end) 1076SepBuf_AddBytesBetween(SepBuf *buf, const char *start, const char *end)
1078{ 1077{
1079 SepBuf_AddBytes(buf, start, (size_t)(end - start)); 1078 SepBuf_AddBytes(buf, start, (size_t)(end - start));
1080} 1079}
1081 1080
1082static void 1081static void
1083SepBuf_AddStr(SepBuf *buf, const char *str) 1082SepBuf_AddStr(SepBuf *buf, const char *str)
1084{ 1083{
1085 SepBuf_AddBytes(buf, str, strlen(str)); 1084 SepBuf_AddBytes(buf, str, strlen(str));
1086} 1085}
1087 1086
1088static char * 1087static char *
1089SepBuf_Destroy(SepBuf *buf, Boolean free_buf) 1088SepBuf_Destroy(SepBuf *buf, Boolean free_buf)
1090{ 1089{
1091 return Buf_Destroy(&buf->buf, free_buf); 1090 return Buf_Destroy(&buf->buf, free_buf);
1092} 1091}
1093 1092
1094 1093
1095/* This callback for ModifyWords gets a single word from an expression and 1094/* This callback for ModifyWords gets a single word from an expression and
1096 * typically adds a modification of this word to the buffer. It may also do 1095 * typically adds a modification of this word to the buffer. It may also do
1097 * nothing or add several words. */ 1096 * nothing or add several words. */
1098typedef void (*ModifyWordsCallback)(const char *word, SepBuf *buf, void *data); 1097typedef void (*ModifyWordsCallback)(const char *word, SepBuf *buf, void *data);
1099 1098
1100 1099
1101/* Callback for ModifyWords to implement the :H modifier. 1100/* Callback for ModifyWords to implement the :H modifier.
1102 * Add the dirname of the given word to the buffer. */ 1101 * Add the dirname of the given word to the buffer. */
1103static void 1102static void
1104ModifyWord_Head(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) 1103ModifyWord_Head(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
1105{ 1104{
1106 const char *slash = strrchr(word, '/'); 1105 const char *slash = strrchr(word, '/');
1107 if (slash != NULL) 1106 if (slash != NULL)
1108 SepBuf_AddBytesBetween(buf, word, slash); 1107 SepBuf_AddBytesBetween(buf, word, slash);
1109 else 1108 else
1110 SepBuf_AddStr(buf, "."); 1109 SepBuf_AddStr(buf, ".");
1111} 1110}
1112 1111
1113/* Callback for ModifyWords to implement the :T modifier. 1112/* Callback for ModifyWords to implement the :T modifier.
1114 * Add the basename of the given word to the buffer. */ 1113 * Add the basename of the given word to the buffer. */
1115static void 1114static void
1116ModifyWord_Tail(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) 1115ModifyWord_Tail(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
1117{ 1116{
1118 const char *slash = strrchr(word, '/'); 1117 const char *slash = strrchr(word, '/');
1119 const char *base = slash != NULL ? slash + 1 : word; 1118 const char *base = slash != NULL ? slash + 1 : word;
1120 SepBuf_AddStr(buf, base); 1119 SepBuf_AddStr(buf, base);
1121} 1120}
1122 1121
1123/* Callback for ModifyWords to implement the :E modifier. 1122/* Callback for ModifyWords to implement the :E modifier.
1124 * Add the filename suffix of the given word to the buffer, if it exists. */ 1123 * Add the filename suffix of the given word to the buffer, if it exists. */
1125static void 1124static void
1126ModifyWord_Suffix(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) 1125ModifyWord_Suffix(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
1127{ 1126{
1128 const char *dot = strrchr(word, '.'); 1127 const char *dot = strrchr(word, '.');
1129 if (dot != NULL) 1128 if (dot != NULL)
1130 SepBuf_AddStr(buf, dot + 1); 1129 SepBuf_AddStr(buf, dot + 1);
1131} 1130}
1132 1131
1133/* Callback for ModifyWords to implement the :R modifier. 1132/* Callback for ModifyWords to implement the :R modifier.
1134 * Add the basename of the given word to the buffer. */ 1133 * Add the basename of the given word to the buffer. */
1135static void 1134static void
1136ModifyWord_Root(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) 1135ModifyWord_Root(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
1137{ 1136{
1138 const char *dot = strrchr(word, '.'); 1137 const char *dot = strrchr(word, '.');
1139 size_t len = dot != NULL ? (size_t)(dot - word) : strlen(word); 1138 size_t len = dot != NULL ? (size_t)(dot - word) : strlen(word);
1140 SepBuf_AddBytes(buf, word, len); 1139 SepBuf_AddBytes(buf, word, len);
1141} 1140}
1142 1141
1143/* Callback for ModifyWords to implement the :M modifier. 1142/* Callback for ModifyWords to implement the :M modifier.
1144 * Place the word in the buffer if it matches the given pattern. */ 1143 * Place the word in the buffer if it matches the given pattern. */
1145static void 1144static void
1146ModifyWord_Match(const char *word, SepBuf *buf, void *data) 1145ModifyWord_Match(const char *word, SepBuf *buf, void *data)
1147{ 1146{
1148 const char *pattern = data; 1147 const char *pattern = data;
1149 VAR_DEBUG2("VarMatch [%s] [%s]\n", word, pattern); 1148 VAR_DEBUG2("VarMatch [%s] [%s]\n", word, pattern);
1150 if (Str_Match(word, pattern)) 1149 if (Str_Match(word, pattern))
1151 SepBuf_AddStr(buf, word); 1150 SepBuf_AddStr(buf, word);
1152} 1151}
1153 1152
1154/* Callback for ModifyWords to implement the :N modifier. 1153/* Callback for ModifyWords to implement the :N modifier.
1155 * Place the word in the buffer if it doesn't match the given pattern. */ 1154 * Place the word in the buffer if it doesn't match the given pattern. */
1156static void 1155static void
1157ModifyWord_NoMatch(const char *word, SepBuf *buf, void *data) 1156ModifyWord_NoMatch(const char *word, SepBuf *buf, void *data)
1158{ 1157{
1159 const char *pattern = data; 1158 const char *pattern = data;
1160 if (!Str_Match(word, pattern)) 1159 if (!Str_Match(word, pattern))
1161 SepBuf_AddStr(buf, word); 1160 SepBuf_AddStr(buf, word);
1162} 1161}
1163 1162
1164#ifdef SYSVVARSUB 1163#ifdef SYSVVARSUB
1165/*- 1164/*-
1166 *----------------------------------------------------------------------- 1165 *-----------------------------------------------------------------------
1167 * Str_SYSVMatch -- 1166 * Str_SYSVMatch --
1168 * Check word against pattern for a match (% is wild), 1167 * Check word against pattern for a match (% is wild),
1169 * 1168 *
1170 * Input: 1169 * Input:
1171 * word Word to examine 1170 * word Word to examine
1172 * pattern Pattern to examine against 1171 * pattern Pattern to examine against
1173 * 1172 *
1174 * Results: 1173 * Results:
1175 * Returns the start of the match, or NULL. 1174 * Returns the start of the match, or NULL.
1176 * *match_len returns the length of the match, if any. 1175 * *match_len returns the length of the match, if any.
1177 * *hasPercent returns whether the pattern contains a percent. 1176 * *hasPercent returns whether the pattern contains a percent.
1178 *----------------------------------------------------------------------- 1177 *-----------------------------------------------------------------------
1179 */ 1178 */
1180static const char * 1179static const char *
1181Str_SYSVMatch(const char *word, const char *pattern, size_t *match_len, 1180Str_SYSVMatch(const char *word, const char *pattern, size_t *match_len,
1182 Boolean *hasPercent) 1181 Boolean *hasPercent)
1183{ 1182{
1184 const char *p = pattern; 1183 const char *p = pattern;
1185 const char *w = word; 1184 const char *w = word;
1186 const char *percent; 1185 const char *percent;
1187 size_t w_len; 1186 size_t w_len;
1188 size_t p_len; 1187 size_t p_len;
1189 const char *w_tail; 1188 const char *w_tail;
1190 1189
1191 *hasPercent = FALSE; 1190 *hasPercent = FALSE;
1192 if (*p == '\0') { /* ${VAR:=suffix} */ 1191 if (*p == '\0') { /* ${VAR:=suffix} */
1193 *match_len = strlen(w); /* Null pattern is the whole string */ 1192 *match_len = strlen(w); /* Null pattern is the whole string */
1194 return w; 1193 return w;
1195 } 1194 }
1196 1195
1197 percent = strchr(p, '%'); 1196 percent = strchr(p, '%');
1198 if (percent != NULL) { /* ${VAR:...%...=...} */ 1197 if (percent != NULL) { /* ${VAR:...%...=...} */
1199 *hasPercent = TRUE; 1198 *hasPercent = TRUE;
1200 if (*w == '\0') 1199 if (*w == '\0')
1201 return NULL; /* empty word does not match pattern */ 1200 return NULL; /* empty word does not match pattern */
1202 1201
1203 /* check that the prefix matches */ 1202 /* check that the prefix matches */
1204 for (; p != percent && *w != '\0' && *w == *p; w++, p++) 1203 for (; p != percent && *w != '\0' && *w == *p; w++, p++)
1205 continue; 1204 continue;
1206 if (p != percent) 1205 if (p != percent)
1207 return NULL; /* No match */ 1206 return NULL; /* No match */
1208 1207
1209 p++; /* Skip the percent */ 1208 p++; /* Skip the percent */
1210 if (*p == '\0') { 1209 if (*p == '\0') {
1211 /* No more pattern, return the rest of the string */ 1210 /* No more pattern, return the rest of the string */
1212 *match_len = strlen(w); 1211 *match_len = strlen(w);
1213 return w; 1212 return w;
1214 } 1213 }
1215 } 1214 }
1216 1215
1217 /* Test whether the tail matches */ 1216 /* Test whether the tail matches */
1218 w_len = strlen(w); 1217 w_len = strlen(w);
1219 p_len = strlen(p); 1218 p_len = strlen(p);
1220 if (w_len < p_len) 1219 if (w_len < p_len)
1221 return NULL; 1220 return NULL;
1222 1221
1223 w_tail = w + w_len - p_len; 1222 w_tail = w + w_len - p_len;
1224 if (memcmp(p, w_tail, p_len) != 0) 1223 if (memcmp(p, w_tail, p_len) != 0)
1225 return NULL; 1224 return NULL;
1226 1225
1227 *match_len = (size_t)(w_tail - w); 1226 *match_len = (size_t)(w_tail - w);
1228 return w; 1227 return w;
1229} 1228}
1230 1229
1231struct ModifyWord_SYSVSubstArgs { 1230struct ModifyWord_SYSVSubstArgs {
1232 GNode *ctx; 1231 GNode *ctx;
1233 const char *lhs; 1232 const char *lhs;
1234 const char *rhs; 1233 const char *rhs;
1235}; 1234};
1236 1235
1237/* Callback for ModifyWords to implement the :%.from=%.to modifier. */ 1236/* Callback for ModifyWords to implement the :%.from=%.to modifier. */
1238static void 1237static void
1239ModifyWord_SYSVSubst(const char *word, SepBuf *buf, void *data) 1238ModifyWord_SYSVSubst(const char *word, SepBuf *buf, void *data)
1240{ 1239{
1241 const struct ModifyWord_SYSVSubstArgs *args = data; 1240 const struct ModifyWord_SYSVSubstArgs *args = data;
1242 char *rhs_expanded; 1241 char *rhs_expanded;
1243 const char *rhs; 1242 const char *rhs;
1244 const char *percent; 1243 const char *percent;
1245 1244
1246 size_t match_len; 1245 size_t match_len;
1247 Boolean lhsPercent; 1246 Boolean lhsPercent;
1248 const char *match = Str_SYSVMatch(word, args->lhs, &match_len, &lhsPercent); 1247 const char *match = Str_SYSVMatch(word, args->lhs, &match_len, &lhsPercent);
1249 if (match == NULL) { 1248 if (match == NULL) {
1250 SepBuf_AddStr(buf, word); 1249 SepBuf_AddStr(buf, word);
1251 return; 1250 return;
1252 } 1251 }
1253 1252
1254 /* Append rhs to the buffer, substituting the first '%' with the 1253 /* Append rhs to the buffer, substituting the first '%' with the
1255 * match, but only if the lhs had a '%' as well. */ 1254 * match, but only if the lhs had a '%' as well. */
1256 1255
1257 (void)Var_Subst(args->rhs, args->ctx, VARE_WANTRES, &rhs_expanded); 1256 (void)Var_Subst(args->rhs, args->ctx, VARE_WANTRES, &rhs_expanded);
1258 /* TODO: handle errors */ 1257 /* TODO: handle errors */
1259 1258
1260 rhs = rhs_expanded; 1259 rhs = rhs_expanded;
1261 percent = strchr(rhs, '%'); 1260 percent = strchr(rhs, '%');
1262 1261
1263 if (percent != NULL && lhsPercent) { 1262 if (percent != NULL && lhsPercent) {
1264 /* Copy the prefix of the replacement pattern */ 1263 /* Copy the prefix of the replacement pattern */
1265 SepBuf_AddBytesBetween(buf, rhs, percent); 1264 SepBuf_AddBytesBetween(buf, rhs, percent);
1266 rhs = percent + 1; 1265 rhs = percent + 1;
1267 } 1266 }
1268 if (percent != NULL || !lhsPercent) 1267 if (percent != NULL || !lhsPercent)
1269 SepBuf_AddBytes(buf, match, match_len); 1268 SepBuf_AddBytes(buf, match, match_len);
1270 1269
1271 /* Append the suffix of the replacement pattern */ 1270 /* Append the suffix of the replacement pattern */
1272 SepBuf_AddStr(buf, rhs); 1271 SepBuf_AddStr(buf, rhs);
1273 1272
1274 free(rhs_expanded); 1273 free(rhs_expanded);
1275} 1274}
1276#endif 1275#endif
1277 1276
1278 1277
1279struct ModifyWord_SubstArgs { 1278struct ModifyWord_SubstArgs {
1280 const char *lhs; 1279 const char *lhs;
1281 size_t lhsLen; 1280 size_t lhsLen;
1282 const char *rhs; 1281 const char *rhs;
1283 size_t rhsLen; 1282 size_t rhsLen;
1284 VarPatternFlags pflags; 1283 VarPatternFlags pflags;
1285 Boolean matched; 1284 Boolean matched;
1286}; 1285};
1287 1286
1288/* Callback for ModifyWords to implement the :S,from,to, modifier. 1287/* Callback for ModifyWords to implement the :S,from,to, modifier.
1289 * Perform a string substitution on the given word. */ 1288 * Perform a string substitution on the given word. */
1290static void 1289static void
1291ModifyWord_Subst(const char *word, SepBuf *buf, void *data) 1290ModifyWord_Subst(const char *word, SepBuf *buf, void *data)
1292{ 1291{
1293 size_t wordLen = strlen(word); 1292 size_t wordLen = strlen(word);
1294 struct ModifyWord_SubstArgs *args = data; 1293 struct ModifyWord_SubstArgs *args = data;
1295 const char *match; 1294 const char *match;
1296 1295
1297 if ((args->pflags & VARP_SUB_ONE) && args->matched) 1296 if ((args->pflags & VARP_SUB_ONE) && args->matched)
1298 goto nosub; 1297 goto nosub;
1299 1298
1300 if (args->pflags & VARP_ANCHOR_START) { 1299 if (args->pflags & VARP_ANCHOR_START) {
1301 if (wordLen < args->lhsLen || 1300 if (wordLen < args->lhsLen ||
1302 memcmp(word, args->lhs, args->lhsLen) != 0) 1301 memcmp(word, args->lhs, args->lhsLen) != 0)
1303 goto nosub; 1302 goto nosub;
1304 1303
1305 if (args->pflags & VARP_ANCHOR_END) { 1304 if (args->pflags & VARP_ANCHOR_END) {
1306 if (wordLen != args->lhsLen) 1305 if (wordLen != args->lhsLen)
1307 goto nosub; 1306 goto nosub;
1308 1307
1309 /* :S,^whole$,replacement, */ 1308 /* :S,^whole$,replacement, */
1310 SepBuf_AddBytes(buf, args->rhs, args->rhsLen); 1309 SepBuf_AddBytes(buf, args->rhs, args->rhsLen);
1311 args->matched = TRUE; 1310 args->matched = TRUE;
1312 } else { 1311 } else {
1313 /* :S,^prefix,replacement, */ 1312 /* :S,^prefix,replacement, */
1314 SepBuf_AddBytes(buf, args->rhs, args->rhsLen); 1313 SepBuf_AddBytes(buf, args->rhs, args->rhsLen);
1315 SepBuf_AddBytes(buf, word + args->lhsLen, wordLen - args->lhsLen); 1314 SepBuf_AddBytes(buf, word + args->lhsLen, wordLen - args->lhsLen);
1316 args->matched = TRUE; 1315 args->matched = TRUE;
1317 } 1316 }
1318 return; 1317 return;
1319 } 1318 }
1320 1319
1321 if (args->pflags & VARP_ANCHOR_END) { 1320 if (args->pflags & VARP_ANCHOR_END) {
1322 const char *start; 1321 const char *start;
1323 1322
1324 if (wordLen < args->lhsLen) 1323 if (wordLen < args->lhsLen)
1325 goto nosub; 1324 goto nosub;
1326 1325
1327 start = word + (wordLen - args->lhsLen); 1326 start = word + (wordLen - args->lhsLen);
1328 if (memcmp(start, args->lhs, args->lhsLen) != 0) 1327 if (memcmp(start, args->lhs, args->lhsLen) != 0)
1329 goto nosub; 1328 goto nosub;
1330 1329
1331 /* :S,suffix$,replacement, */ 1330 /* :S,suffix$,replacement, */
1332 SepBuf_AddBytesBetween(buf, word, start); 1331 SepBuf_AddBytesBetween(buf, word, start);
1333 SepBuf_AddBytes(buf, args->rhs, args->rhsLen); 1332 SepBuf_AddBytes(buf, args->rhs, args->rhsLen);
1334 args->matched = TRUE; 1333 args->matched = TRUE;
1335 return; 1334 return;
1336 } 1335 }
1337 1336
1338 if (args->lhs[0] == '\0') 1337 if (args->lhs[0] == '\0')
1339 goto nosub; 1338 goto nosub;
1340 1339
1341 /* unanchored case, may match more than once */ 1340 /* unanchored case, may match more than once */
1342 while ((match = strstr(word, args->lhs)) != NULL) { 1341 while ((match = strstr(word, args->lhs)) != NULL) {
1343 SepBuf_AddBytesBetween(buf, word, match); 1342 SepBuf_AddBytesBetween(buf, word, match);
1344 SepBuf_AddBytes(buf, args->rhs, args->rhsLen); 1343 SepBuf_AddBytes(buf, args->rhs, args->rhsLen);
1345 args->matched = TRUE; 1344 args->matched = TRUE;
1346 wordLen -= (size_t)(match - word) + args->lhsLen; 1345 wordLen -= (size_t)(match - word) + args->lhsLen;
1347 word += (size_t)(match - word) + args->lhsLen; 1346 word += (size_t)(match - word) + args->lhsLen;
1348 if (wordLen == 0 || !(args->pflags & VARP_SUB_GLOBAL)) 1347 if (wordLen == 0 || !(args->pflags & VARP_SUB_GLOBAL))
1349 break; 1348 break;
1350 } 1349 }
1351nosub: 1350nosub:
1352 SepBuf_AddBytes(buf, word, wordLen); 1351 SepBuf_AddBytes(buf, word, wordLen);
1353} 1352}
1354 1353
1355#ifndef NO_REGEX 1354#ifndef NO_REGEX
1356/* Print the error caused by a regcomp or regexec call. */ 1355/* Print the error caused by a regcomp or regexec call. */
1357static void 1356static void
1358VarREError(int reerr, regex_t *pat, const char *str) 1357VarREError(int reerr, regex_t *pat, const char *str)
1359{ 1358{
1360 size_t errlen = regerror(reerr, pat, 0, 0); 1359 size_t errlen = regerror(reerr, pat, 0, 0);
1361 char *errbuf = bmake_malloc(errlen); 1360 char *errbuf = bmake_malloc(errlen);
1362 regerror(reerr, pat, errbuf, errlen); 1361 regerror(reerr, pat, errbuf, errlen);
1363 Error("%s: %s", str, errbuf); 1362 Error("%s: %s", str, errbuf);
1364 free(errbuf); 1363 free(errbuf);
1365} 1364}
1366 1365
1367struct ModifyWord_SubstRegexArgs { 1366struct ModifyWord_SubstRegexArgs {
1368 regex_t re; 1367 regex_t re;
1369 size_t nsub; 1368 size_t nsub;
1370 char *replace; 1369 char *replace;
1371 VarPatternFlags pflags; 1370 VarPatternFlags pflags;
1372 Boolean matched; 1371 Boolean matched;
1373}; 1372};
1374 1373
1375/* Callback for ModifyWords to implement the :C/from/to/ modifier. 1374/* Callback for ModifyWords to implement the :C/from/to/ modifier.
1376 * Perform a regex substitution on the given word. */ 1375 * Perform a regex substitution on the given word. */
1377static void 1376static void
1378ModifyWord_SubstRegex(const char *word, SepBuf *buf, void *data) 1377ModifyWord_SubstRegex(const char *word, SepBuf *buf, void *data)
1379{ 1378{
1380 struct ModifyWord_SubstRegexArgs *args = data; 1379 struct ModifyWord_SubstRegexArgs *args = data;
1381 int xrv; 1380 int xrv;
1382 const char *wp = word; 1381 const char *wp = word;
1383 char *rp; 1382 char *rp;
1384 int flags = 0; 1383 int flags = 0;
1385 regmatch_t m[10]; 1384 regmatch_t m[10];
1386 1385
1387 if ((args->pflags & VARP_SUB_ONE) && args->matched) 1386 if ((args->pflags & VARP_SUB_ONE) && args->matched)
1388 goto nosub; 1387 goto nosub;
1389 1388
1390tryagain: 1389tryagain:
1391 xrv = regexec(&args->re, wp, args->nsub, m, flags); 1390 xrv = regexec(&args->re, wp, args->nsub, m, flags);
1392 1391
1393 switch (xrv) { 1392 switch (xrv) {
1394 case 0: 1393 case 0:
1395 args->matched = TRUE; 1394 args->matched = TRUE;
1396 SepBuf_AddBytes(buf, wp, (size_t)m[0].rm_so); 1395 SepBuf_AddBytes(buf, wp, (size_t)m[0].rm_so);
1397 1396
1398 for (rp = args->replace; *rp; rp++) { 1397 for (rp = args->replace; *rp; rp++) {
1399 if (*rp == '\\' && (rp[1] == '&' || rp[1] == '\\')) { 1398 if (*rp == '\\' && (rp[1] == '&' || rp[1] == '\\')) {
1400 SepBuf_AddBytes(buf, rp + 1, 1); 1399 SepBuf_AddBytes(buf, rp + 1, 1);
1401 rp++; 1400 rp++;
1402 continue; 1401 continue;
1403 } 1402 }
1404 1403
1405 if (*rp == '&') { 1404 if (*rp == '&') {
1406 SepBuf_AddBytesBetween(buf, wp + m[0].rm_so, wp + m[0].rm_eo); 1405 SepBuf_AddBytesBetween(buf, wp + m[0].rm_so, wp + m[0].rm_eo);
1407 continue; 1406 continue;
1408 } 1407 }
1409 1408
1410 if (*rp != '\\' || !ch_isdigit(rp[1])) { 1409 if (*rp != '\\' || !ch_isdigit(rp[1])) {
1411 SepBuf_AddBytes(buf, rp, 1); 1410 SepBuf_AddBytes(buf, rp, 1);
1412 continue; 1411 continue;
1413 } 1412 }
1414 1413
1415 { /* \0 to \9 backreference */ 1414 { /* \0 to \9 backreference */
1416 size_t n = (size_t)(rp[1] - '0'); 1415 size_t n = (size_t)(rp[1] - '0');
1417 rp++; 1416 rp++;
1418 1417
1419 if (n >= args->nsub) { 1418 if (n >= args->nsub) {
1420 Error("No subexpression \\%zu", n); 1419 Error("No subexpression \\%zu", n);
1421 } else if (m[n].rm_so == -1 && m[n].rm_eo == -1) { 1420 } else if (m[n].rm_so == -1 && m[n].rm_eo == -1) {
1422 Error("No match for subexpression \\%zu", n); 1421 Error("No match for subexpression \\%zu", n);
1423 } else { 1422 } else {
1424 SepBuf_AddBytesBetween(buf, wp + m[n].rm_so, 1423 SepBuf_AddBytesBetween(buf, wp + m[n].rm_so,
1425 wp + m[n].rm_eo); 1424 wp + m[n].rm_eo);
1426 } 1425 }
1427 } 1426 }
1428 } 1427 }
1429 1428
1430 wp += m[0].rm_eo; 1429 wp += m[0].rm_eo;
1431 if (args->pflags & VARP_SUB_GLOBAL) { 1430 if (args->pflags & VARP_SUB_GLOBAL) {
1432 flags |= REG_NOTBOL; 1431 flags |= REG_NOTBOL;
1433 if (m[0].rm_so == 0 && m[0].rm_eo == 0) { 1432 if (m[0].rm_so == 0 && m[0].rm_eo == 0) {
1434 SepBuf_AddBytes(buf, wp, 1); 1433 SepBuf_AddBytes(buf, wp, 1);
1435 wp++; 1434 wp++;
1436 } 1435 }
1437 if (*wp) 1436 if (*wp)
1438 goto tryagain; 1437 goto tryagain;
1439 } 1438 }
1440 if (*wp) { 1439 if (*wp) {
1441 SepBuf_AddStr(buf, wp); 1440 SepBuf_AddStr(buf, wp);
1442 } 1441 }
1443 break; 1442 break;
1444 default: 1443 default:
1445 VarREError(xrv, &args->re, "Unexpected regex error"); 1444 VarREError(xrv, &args->re, "Unexpected regex error");
1446 /* FALLTHROUGH */ 1445 /* FALLTHROUGH */
1447 case REG_NOMATCH: 1446 case REG_NOMATCH:
1448 nosub: 1447 nosub:
1449 SepBuf_AddStr(buf, wp); 1448 SepBuf_AddStr(buf, wp);
1450 break; 1449 break;
1451 } 1450 }
1452} 1451}
1453#endif 1452#endif
1454 1453
1455 1454
1456struct ModifyWord_LoopArgs { 1455struct ModifyWord_LoopArgs {
1457 GNode *ctx; 1456 GNode *ctx;
1458 char *tvar; /* name of temporary variable */ 1457 char *tvar; /* name of temporary variable */
1459 char *str; /* string to expand */ 1458 char *str; /* string to expand */
1460 VarEvalFlags eflags; 1459 VarEvalFlags eflags;
1461}; 1460};
1462 1461
1463/* Callback for ModifyWords to implement the :@var@...@ modifier of ODE make. */ 1462/* Callback for ModifyWords to implement the :@var@...@ modifier of ODE make. */
1464static void 1463static void
1465ModifyWord_Loop(const char *word, SepBuf *buf, void *data) 1464ModifyWord_Loop(const char *word, SepBuf *buf, void *data)
1466{ 1465{
1467 const struct ModifyWord_LoopArgs *args; 1466 const struct ModifyWord_LoopArgs *args;
1468 char *s; 1467 char *s;
1469 1468
1470 if (word[0] == '\0') 1469 if (word[0] == '\0')
1471 return; 1470 return;
1472 1471
1473 args = data; 1472 args = data;
1474 Var_Set_with_flags(args->tvar, word, args->ctx, VAR_NO_EXPORT); 1473 Var_Set_with_flags(args->tvar, word, args->ctx, VAR_NO_EXPORT);
1475 (void)Var_Subst(args->str, args->ctx, args->eflags, &s); 1474 (void)Var_Subst(args->str, args->ctx, args->eflags, &s);
1476 /* TODO: handle errors */ 1475 /* TODO: handle errors */
1477 1476
1478 VAR_DEBUG4("ModifyWord_Loop: in \"%s\", replace \"%s\" with \"%s\" " 1477 VAR_DEBUG4("ModifyWord_Loop: in \"%s\", replace \"%s\" with \"%s\" "
1479 "to \"%s\"\n", 1478 "to \"%s\"\n",
1480 word, args->tvar, args->str, s); 1479 word, args->tvar, args->str, s);
1481 1480
1482 if (s[0] == '\n' || Buf_EndsWith(&buf->buf, '\n')) 1481 if (s[0] == '\n' || Buf_EndsWith(&buf->buf, '\n'))
1483 buf->needSep = FALSE; 1482 buf->needSep = FALSE;
1484 SepBuf_AddStr(buf, s); 1483 SepBuf_AddStr(buf, s);
1485 free(s); 1484 free(s);
1486} 1485}
1487 1486
1488 1487
1489/*- 1488/*-
1490 * Implements the :[first..last] modifier. 1489 * Implements the :[first..last] modifier.
1491 * This is a special case of ModifyWords since we want to be able 1490 * This is a special case of ModifyWords since we want to be able
1492 * to scan the list backwards if first > last. 1491 * to scan the list backwards if first > last.
1493 */ 1492 */
1494static char * 1493static char *
1495VarSelectWords(char sep, Boolean oneBigWord, const char *str, int first, 1494VarSelectWords(char sep, Boolean oneBigWord, const char *str, int first,
1496 int last) 1495 int last)
1497{ 1496{
1498 Words words; 1497 Words words;
1499 int len, start, end, step; 1498 int len, start, end, step;
1500 int i; 1499 int i;
1501 1500
1502 SepBuf buf; 1501 SepBuf buf;
1503 SepBuf_Init(&buf, sep); 1502 SepBuf_Init(&buf, sep);
1504 1503
1505 if (oneBigWord) { 1504 if (oneBigWord) {
1506 /* fake what Str_Words() would do if there were only one word */ 1505 /* fake what Str_Words() would do if there were only one word */
1507 words.len = 1; 1506 words.len = 1;
1508 words.words = bmake_malloc((words.len + 1) * sizeof(char *)); 1507 words.words = bmake_malloc((words.len + 1) * sizeof(char *));
1509 words.freeIt = bmake_strdup(str); 1508 words.freeIt = bmake_strdup(str);
1510 words.words[0] = words.freeIt; 1509 words.words[0] = words.freeIt;
1511 words.words[1] = NULL; 1510 words.words[1] = NULL;
1512 } else { 1511 } else {
1513 words = Str_Words(str, FALSE); 1512 words = Str_Words(str, FALSE);
1514 } 1513 }
1515 1514
1516 /* 1515 /*
1517 * Now sanitize the given range. 1516 * Now sanitize the given range.
1518 * If first or last are negative, convert them to the positive equivalents 1517 * If first or last are negative, convert them to the positive equivalents
1519 * (-1 gets converted to ac, -2 gets converted to (ac - 1), etc.). 1518 * (-1 gets converted to ac, -2 gets converted to (ac - 1), etc.).
1520 */ 1519 */
1521 len = (int)words.len; 1520 len = (int)words.len;
1522 if (first < 0) 1521 if (first < 0)
1523 first += len + 1; 1522 first += len + 1;
1524 if (last < 0) 1523 if (last < 0)
1525 last += len + 1; 1524 last += len + 1;
1526 1525
1527 /* 1526 /*
1528 * We avoid scanning more of the list than we need to. 1527 * We avoid scanning more of the list than we need to.
1529 */ 1528 */
1530 if (first > last) { 1529 if (first > last) {
1531 start = (first > len ? len : first) - 1; 1530 start = (first > len ? len : first) - 1;
1532 end = last < 1 ? 0 : last - 1; 1531 end = last < 1 ? 0 : last - 1;
1533 step = -1; 1532 step = -1;
1534 } else { 1533 } else {
1535 start = first < 1 ? 0 : first - 1; 1534 start = first < 1 ? 0 : first - 1;
1536 end = last > len ? len : last; 1535 end = last > len ? len : last;
1537 step = 1; 1536 step = 1;
1538 } 1537 }
1539 1538
1540 for (i = start; (step < 0) == (i >= end); i += step) { 1539 for (i = start; (step < 0) == (i >= end); i += step) {
1541 SepBuf_AddStr(&buf, words.words[i]); 1540 SepBuf_AddStr(&buf, words.words[i]);
1542 SepBuf_Sep(&buf); 1541 SepBuf_Sep(&buf);
1543 } 1542 }
1544 1543
1545 Words_Free(words); 1544 Words_Free(words);
1546 1545
1547 return SepBuf_Destroy(&buf, FALSE); 1546 return SepBuf_Destroy(&buf, FALSE);
1548} 1547}
1549 1548
1550 1549
1551/* Callback for ModifyWords to implement the :tA modifier. 1550/* Callback for ModifyWords to implement the :tA modifier.
1552 * Replace each word with the result of realpath() if successful. */ 1551 * Replace each word with the result of realpath() if successful. */
1553static void 1552static void
1554ModifyWord_Realpath(const char *word, SepBuf *buf, void *data MAKE_ATTR_UNUSED) 1553ModifyWord_Realpath(const char *word, SepBuf *buf, void *data MAKE_ATTR_UNUSED)
1555{ 1554{
1556 struct stat st; 1555 struct stat st;
1557 char rbuf[MAXPATHLEN]; 1556 char rbuf[MAXPATHLEN];
1558 1557
1559 const char *rp = cached_realpath(word, rbuf); 1558 const char *rp = cached_realpath(word, rbuf);
1560 if (rp != NULL && *rp == '/' && stat(rp, &st) == 0) 1559 if (rp != NULL && *rp == '/' && stat(rp, &st) == 0)
1561 word = rp; 1560 word = rp;
1562 1561
1563 SepBuf_AddStr(buf, word); 1562 SepBuf_AddStr(buf, word);
1564} 1563}
1565 1564
1566/*- 1565/*-
1567 *----------------------------------------------------------------------- 1566 *-----------------------------------------------------------------------
1568 * Modify each of the words of the passed string using the given function. 1567 * Modify each of the words of the passed string using the given function.
1569 * 1568 *
1570 * Input: 1569 * Input:
1571 * str String whose words should be modified 1570 * str String whose words should be modified
1572 * modifyWord Function that modifies a single word 1571 * modifyWord Function that modifies a single word
1573 * modifyWord_args Custom arguments for modifyWord 1572 * modifyWord_args Custom arguments for modifyWord
1574 * 1573 *
1575 * Results: 1574 * Results:
1576 * A string of all the words modified appropriately. 1575 * A string of all the words modified appropriately.
1577 *----------------------------------------------------------------------- 1576 *-----------------------------------------------------------------------
1578 */ 1577 */
1579static char * 1578static char *
1580ModifyWords(GNode *ctx, char sep, Boolean oneBigWord, const char *str, 1579ModifyWords(GNode *ctx, char sep, Boolean oneBigWord, const char *str,
1581 ModifyWordsCallback modifyWord, void *modifyWord_args) 1580 ModifyWordsCallback modifyWord, void *modifyWord_args)
1582{ 1581{
1583 SepBuf result; 1582 SepBuf result;
1584 Words words; 1583 Words words;
1585 size_t i; 1584 size_t i;
1586 1585
1587 if (oneBigWord) { 1586 if (oneBigWord) {
1588 SepBuf_Init(&result, sep); 1587 SepBuf_Init(&result, sep);
1589 modifyWord(str, &result, modifyWord_args); 1588 modifyWord(str, &result, modifyWord_args);
1590 return SepBuf_Destroy(&result, FALSE); 1589 return SepBuf_Destroy(&result, FALSE);
1591 } 1590 }
1592 1591
1593 SepBuf_Init(&result, sep); 1592 SepBuf_Init(&result, sep);
1594 1593
1595 words = Str_Words(str, FALSE); 1594 words = Str_Words(str, FALSE);
1596 1595
1597 VAR_DEBUG2("ModifyWords: split \"%s\" into %zu words\n", str, words.len); 1596 VAR_DEBUG2("ModifyWords: split \"%s\" into %zu words\n", str, words.len);
1598 1597
1599 for (i = 0; i < words.len; i++) { 1598 for (i = 0; i < words.len; i++) {
1600 modifyWord(words.words[i], &result, modifyWord_args); 1599 modifyWord(words.words[i], &result, modifyWord_args);
1601 if (Buf_Len(&result.buf) > 0) 1600 if (Buf_Len(&result.buf) > 0)
1602 SepBuf_Sep(&result); 1601 SepBuf_Sep(&result);
1603 } 1602 }
1604 1603
1605 Words_Free(words); 1604 Words_Free(words);
1606 1605
1607 return SepBuf_Destroy(&result, FALSE); 1606 return SepBuf_Destroy(&result, FALSE);
1608} 1607}
1609 1608
@@ -2897,1020 +2896,1019 @@ ApplyModifier_Assign(const char **pp, Ap @@ -2897,1020 +2896,1019 @@ ApplyModifier_Assign(const char **pp, Ap
2897 *pp = mod + 2; 2896 *pp = mod + 2;
2898 break; 2897 break;
2899 } 2898 }
2900 2899
2901 delim = st->startc == '(' ? ')' : '}'; 2900 delim = st->startc == '(' ? ')' : '}';
2902 res = ParseModifierPart(pp, delim, st->eflags, st, &val, NULL, NULL, NULL); 2901 res = ParseModifierPart(pp, delim, st->eflags, st, &val, NULL, NULL, NULL);
2903 if (res != VPR_OK) 2902 if (res != VPR_OK)
2904 return AMR_CLEANUP; 2903 return AMR_CLEANUP;
2905 2904
2906 (*pp)--; 2905 (*pp)--;
2907 2906
2908 if (st->eflags & VARE_WANTRES) { 2907 if (st->eflags & VARE_WANTRES) {
2909 switch (op[0]) { 2908 switch (op[0]) {
2910 case '+': 2909 case '+':
2911 Var_Append(st->v->name, val, v_ctxt); 2910 Var_Append(st->v->name, val, v_ctxt);
2912 break; 2911 break;
2913 case '!': { 2912 case '!': {
2914 const char *errfmt; 2913 const char *errfmt;
2915 char *cmd_output = Cmd_Exec(val, &errfmt); 2914 char *cmd_output = Cmd_Exec(val, &errfmt);
2916 if (errfmt) 2915 if (errfmt)
2917 Error(errfmt, val); 2916 Error(errfmt, val);
2918 else 2917 else
2919 Var_Set(st->v->name, cmd_output, v_ctxt); 2918 Var_Set(st->v->name, cmd_output, v_ctxt);
2920 free(cmd_output); 2919 free(cmd_output);
2921 break; 2920 break;
2922 } 2921 }
2923 case '?': 2922 case '?':
2924 if (!(st->exprFlags & VEF_UNDEF)) 2923 if (!(st->exprFlags & VEF_UNDEF))
2925 break; 2924 break;
2926 /* FALLTHROUGH */ 2925 /* FALLTHROUGH */
2927 default: 2926 default:
2928 Var_Set(st->v->name, val, v_ctxt); 2927 Var_Set(st->v->name, val, v_ctxt);
2929 break; 2928 break;
2930 } 2929 }
2931 } 2930 }
2932 free(val); 2931 free(val);
2933 st->newVal = emptyString; 2932 st->newVal = emptyString;
2934 return AMR_OK; 2933 return AMR_OK;
2935} 2934}
2936 2935
2937/* remember current value */ 2936/* remember current value */
2938static ApplyModifierResult 2937static ApplyModifierResult
2939ApplyModifier_Remember(const char **pp, ApplyModifiersState *st) 2938ApplyModifier_Remember(const char **pp, ApplyModifiersState *st)
2940{ 2939{
2941 const char *mod = *pp; 2940 const char *mod = *pp;
2942 if (!ModMatchEq(mod, "_", st->endc)) 2941 if (!ModMatchEq(mod, "_", st->endc))
2943 return AMR_UNKNOWN; 2942 return AMR_UNKNOWN;
2944 2943
2945 if (mod[1] == '=') { 2944 if (mod[1] == '=') {
2946 size_t n = strcspn(mod + 2, ":)}"); 2945 size_t n = strcspn(mod + 2, ":)}");
2947 char *name = bmake_strldup(mod + 2, n); 2946 char *name = bmake_strldup(mod + 2, n);
2948 Var_Set(name, st->val, st->ctxt); 2947 Var_Set(name, st->val, st->ctxt);
2949 free(name); 2948 free(name);
2950 *pp = mod + 2 + n; 2949 *pp = mod + 2 + n;
2951 } else { 2950 } else {
2952 Var_Set("_", st->val, st->ctxt); 2951 Var_Set("_", st->val, st->ctxt);
2953 *pp = mod + 1; 2952 *pp = mod + 1;
2954 } 2953 }
2955 st->newVal = st->val; 2954 st->newVal = st->val;
2956 return AMR_OK; 2955 return AMR_OK;
2957} 2956}
2958 2957
2959/* Apply the given function to each word of the variable value. */ 2958/* Apply the given function to each word of the variable value. */
2960static ApplyModifierResult 2959static ApplyModifierResult
2961ApplyModifier_WordFunc(const char **pp, ApplyModifiersState *st, 2960ApplyModifier_WordFunc(const char **pp, ApplyModifiersState *st,
2962 ModifyWordsCallback modifyWord) 2961 ModifyWordsCallback modifyWord)
2963{ 2962{
2964 char delim = (*pp)[1]; 2963 char delim = (*pp)[1];
2965 if (delim != st->endc && delim != ':') 2964 if (delim != st->endc && delim != ':')
2966 return AMR_UNKNOWN; 2965 return AMR_UNKNOWN;
2967 2966
2968 st->newVal = ModifyWords(st->ctxt, st->sep, st->oneBigWord, 2967 st->newVal = ModifyWords(st->ctxt, st->sep, st->oneBigWord,
2969 st->val, modifyWord, NULL); 2968 st->val, modifyWord, NULL);
2970 (*pp)++; 2969 (*pp)++;
2971 return AMR_OK; 2970 return AMR_OK;
2972} 2971}
2973 2972
2974static ApplyModifierResult 2973static ApplyModifierResult
2975ApplyModifier_Unique(const char **pp, ApplyModifiersState *st) 2974ApplyModifier_Unique(const char **pp, ApplyModifiersState *st)
2976{ 2975{
2977 if ((*pp)[1] == st->endc || (*pp)[1] == ':') { 2976 if ((*pp)[1] == st->endc || (*pp)[1] == ':') {
2978 st->newVal = VarUniq(st->val); 2977 st->newVal = VarUniq(st->val);
2979 (*pp)++; 2978 (*pp)++;
2980 return AMR_OK; 2979 return AMR_OK;
2981 } else 2980 } else
2982 return AMR_UNKNOWN; 2981 return AMR_UNKNOWN;
2983} 2982}
2984 2983
2985#ifdef SYSVVARSUB 2984#ifdef SYSVVARSUB
2986/* :from=to */ 2985/* :from=to */
2987static ApplyModifierResult 2986static ApplyModifierResult
2988ApplyModifier_SysV(const char **pp, ApplyModifiersState *st) 2987ApplyModifier_SysV(const char **pp, ApplyModifiersState *st)
2989{ 2988{
2990 char *lhs, *rhs; 2989 char *lhs, *rhs;
2991 VarParseResult res; 2990 VarParseResult res;
2992 2991
2993 const char *mod = *pp; 2992 const char *mod = *pp;
2994 Boolean eqFound = FALSE; 2993 Boolean eqFound = FALSE;
2995 2994
2996 /* 2995 /*
2997 * First we make a pass through the string trying 2996 * First we make a pass through the string trying
2998 * to verify it is a SYSV-make-style translation: 2997 * to verify it is a SYSV-make-style translation:
2999 * it must be: <string1>=<string2>) 2998 * it must be: <string1>=<string2>)
3000 */ 2999 */
3001 int nest = 1; 3000 int nest = 1;
3002 const char *next = mod; 3001 const char *next = mod;
3003 while (*next != '\0' && nest > 0) { 3002 while (*next != '\0' && nest > 0) {
3004 if (*next == '=') { 3003 if (*next == '=') {
3005 eqFound = TRUE; 3004 eqFound = TRUE;
3006 /* continue looking for st->endc */ 3005 /* continue looking for st->endc */
3007 } else if (*next == st->endc) 3006 } else if (*next == st->endc)
3008 nest--; 3007 nest--;
3009 else if (*next == st->startc) 3008 else if (*next == st->startc)
3010 nest++; 3009 nest++;
3011 if (nest > 0) 3010 if (nest > 0)
3012 next++; 3011 next++;
3013 } 3012 }
3014 if (*next != st->endc || !eqFound) 3013 if (*next != st->endc || !eqFound)
3015 return AMR_UNKNOWN; 3014 return AMR_UNKNOWN;
3016 3015
3017 *pp = mod; 3016 *pp = mod;
3018 res = ParseModifierPart(pp, '=', st->eflags, st, 3017 res = ParseModifierPart(pp, '=', st->eflags, st,
3019 &lhs, NULL, NULL, NULL); 3018 &lhs, NULL, NULL, NULL);
3020 if (res != VPR_OK) 3019 if (res != VPR_OK)
3021 return AMR_CLEANUP; 3020 return AMR_CLEANUP;
3022 3021
3023 res = ParseModifierPart(pp, st->endc, st->eflags, st, 3022 res = ParseModifierPart(pp, st->endc, st->eflags, st,
3024 &rhs, NULL, NULL, NULL); 3023 &rhs, NULL, NULL, NULL);
3025 if (res != VPR_OK) 3024 if (res != VPR_OK)
3026 return AMR_CLEANUP; 3025 return AMR_CLEANUP;
3027 3026
3028 /* 3027 /*
3029 * SYSV modifications happen through the whole 3028 * SYSV modifications happen through the whole
3030 * string. Note the pattern is anchored at the end. 3029 * string. Note the pattern is anchored at the end.
3031 */ 3030 */
3032 (*pp)--; 3031 (*pp)--;
3033 if (lhs[0] == '\0' && st->val[0] == '\0') { 3032 if (lhs[0] == '\0' && st->val[0] == '\0') {
3034 st->newVal = st->val; /* special case */ 3033 st->newVal = st->val; /* special case */
3035 } else { 3034 } else {
3036 struct ModifyWord_SYSVSubstArgs args = {st->ctxt, lhs, rhs}; 3035 struct ModifyWord_SYSVSubstArgs args = {st->ctxt, lhs, rhs};
3037 st->newVal = ModifyWords(st->ctxt, st->sep, st->oneBigWord, st->val, 3036 st->newVal = ModifyWords(st->ctxt, st->sep, st->oneBigWord, st->val,
3038 ModifyWord_SYSVSubst, &args); 3037 ModifyWord_SYSVSubst, &args);
3039 } 3038 }
3040 free(lhs); 3039 free(lhs);
3041 free(rhs); 3040 free(rhs);
3042 return AMR_OK; 3041 return AMR_OK;
3043} 3042}
3044#endif 3043#endif
3045 3044
3046#ifdef SUNSHCMD 3045#ifdef SUNSHCMD
3047/* :sh */ 3046/* :sh */
3048static ApplyModifierResult 3047static ApplyModifierResult
3049ApplyModifier_SunShell(const char **pp, ApplyModifiersState *st) 3048ApplyModifier_SunShell(const char **pp, ApplyModifiersState *st)
3050{ 3049{
3051 const char *p = *pp; 3050 const char *p = *pp;
3052 if (p[1] == 'h' && (p[2] == st->endc || p[2] == ':')) { 3051 if (p[1] == 'h' && (p[2] == st->endc || p[2] == ':')) {
3053 if (st->eflags & VARE_WANTRES) { 3052 if (st->eflags & VARE_WANTRES) {
3054 const char *errfmt; 3053 const char *errfmt;
3055 st->newVal = Cmd_Exec(st->val, &errfmt); 3054 st->newVal = Cmd_Exec(st->val, &errfmt);
3056 if (errfmt) 3055 if (errfmt)
3057 Error(errfmt, st->val); 3056 Error(errfmt, st->val);
3058 } else 3057 } else
3059 st->newVal = emptyString; 3058 st->newVal = emptyString;
3060 *pp = p + 2; 3059 *pp = p + 2;
3061 return AMR_OK; 3060 return AMR_OK;
3062 } else 3061 } else
3063 return AMR_UNKNOWN; 3062 return AMR_UNKNOWN;
3064} 3063}
3065#endif 3064#endif
3066 3065
3067static void 3066static void
3068LogBeforeApply(const ApplyModifiersState *st, const char *mod, const char endc) 3067LogBeforeApply(const ApplyModifiersState *st, const char *mod, const char endc)
3069{ 3068{
3070 char eflags_str[VarEvalFlags_ToStringSize]; 3069 char eflags_str[VarEvalFlags_ToStringSize];
3071 char vflags_str[VarFlags_ToStringSize]; 3070 char vflags_str[VarFlags_ToStringSize];
3072 char exprflags_str[VarExprFlags_ToStringSize]; 3071 char exprflags_str[VarExprFlags_ToStringSize];
3073 Boolean is_single_char = mod[0] != '\0' && 3072 Boolean is_single_char = mod[0] != '\0' &&
3074 (mod[1] == endc || mod[1] == ':'); 3073 (mod[1] == endc || mod[1] == ':');
3075 3074
3076 /* At this point, only the first character of the modifier can 3075 /* At this point, only the first character of the modifier can
3077 * be used since the end of the modifier is not yet known. */ 3076 * be used since the end of the modifier is not yet known. */
3078 debug_printf("Applying ${%s:%c%s} to \"%s\" (%s, %s, %s)\n", 3077 debug_printf("Applying ${%s:%c%s} to \"%s\" (%s, %s, %s)\n",
3079 st->v->name, mod[0], is_single_char ? "" : "...", st->val, 3078 st->v->name, mod[0], is_single_char ? "" : "...", st->val,
3080 Enum_FlagsToString(eflags_str, sizeof eflags_str, 3079 Enum_FlagsToString(eflags_str, sizeof eflags_str,
3081 st->eflags, VarEvalFlags_ToStringSpecs), 3080 st->eflags, VarEvalFlags_ToStringSpecs),
3082 Enum_FlagsToString(vflags_str, sizeof vflags_str, 3081 Enum_FlagsToString(vflags_str, sizeof vflags_str,
3083 st->v->flags, VarFlags_ToStringSpecs), 3082 st->v->flags, VarFlags_ToStringSpecs),
3084 Enum_FlagsToString(exprflags_str, sizeof exprflags_str, 3083 Enum_FlagsToString(exprflags_str, sizeof exprflags_str,
3085 st->exprFlags, 3084 st->exprFlags,
3086 VarExprFlags_ToStringSpecs)); 3085 VarExprFlags_ToStringSpecs));
3087} 3086}
3088 3087
3089static void 3088static void
3090LogAfterApply(ApplyModifiersState *st, const char *p, const char *mod) 3089LogAfterApply(ApplyModifiersState *st, const char *p, const char *mod)
3091{ 3090{
3092 char eflags_str[VarEvalFlags_ToStringSize]; 3091 char eflags_str[VarEvalFlags_ToStringSize];
3093 char vflags_str[VarFlags_ToStringSize]; 3092 char vflags_str[VarFlags_ToStringSize];
3094 char exprflags_str[VarExprFlags_ToStringSize]; 3093 char exprflags_str[VarExprFlags_ToStringSize];
3095 const char *quot = st->newVal == var_Error ? "" : "\""; 3094 const char *quot = st->newVal == var_Error ? "" : "\"";
3096 const char *newVal = st->newVal == var_Error ? "error" : st->newVal; 3095 const char *newVal = st->newVal == var_Error ? "error" : st->newVal;
3097 3096
3098 debug_printf("Result of ${%s:%.*s} is %s%s%s (%s, %s, %s)\n", 3097 debug_printf("Result of ${%s:%.*s} is %s%s%s (%s, %s, %s)\n",
3099 st->v->name, (int)(p - mod), mod, quot, newVal, quot, 3098 st->v->name, (int)(p - mod), mod, quot, newVal, quot,
3100 Enum_FlagsToString(eflags_str, sizeof eflags_str, 3099 Enum_FlagsToString(eflags_str, sizeof eflags_str,
3101 st->eflags, VarEvalFlags_ToStringSpecs), 3100 st->eflags, VarEvalFlags_ToStringSpecs),
3102 Enum_FlagsToString(vflags_str, sizeof vflags_str, 3101 Enum_FlagsToString(vflags_str, sizeof vflags_str,
3103 st->v->flags, VarFlags_ToStringSpecs), 3102 st->v->flags, VarFlags_ToStringSpecs),
3104 Enum_FlagsToString(exprflags_str, sizeof exprflags_str, 3103 Enum_FlagsToString(exprflags_str, sizeof exprflags_str,
3105 st->exprFlags, 3104 st->exprFlags,
3106 VarExprFlags_ToStringSpecs)); 3105 VarExprFlags_ToStringSpecs));
3107} 3106}
3108 3107
3109static ApplyModifierResult 3108static ApplyModifierResult
3110ApplyModifier(const char **pp, ApplyModifiersState *st) 3109ApplyModifier(const char **pp, ApplyModifiersState *st)
3111{ 3110{
3112 switch (**pp) { 3111 switch (**pp) {
3113 case ':': 3112 case ':':
3114 return ApplyModifier_Assign(pp, st); 3113 return ApplyModifier_Assign(pp, st);
3115 case '@': 3114 case '@':
3116 return ApplyModifier_Loop(pp, st); 3115 return ApplyModifier_Loop(pp, st);
3117 case '_': 3116 case '_':
3118 return ApplyModifier_Remember(pp, st); 3117 return ApplyModifier_Remember(pp, st);
3119 case 'D': 3118 case 'D':
3120 case 'U': 3119 case 'U':
3121 return ApplyModifier_Defined(pp, st); 3120 return ApplyModifier_Defined(pp, st);
3122 case 'L': 3121 case 'L':
3123 return ApplyModifier_Literal(pp, st); 3122 return ApplyModifier_Literal(pp, st);
3124 case 'P': 3123 case 'P':
3125 return ApplyModifier_Path(pp, st); 3124 return ApplyModifier_Path(pp, st);
3126 case '!': 3125 case '!':
3127 return ApplyModifier_ShellCommand(pp, st); 3126 return ApplyModifier_ShellCommand(pp, st);
3128 case '[': 3127 case '[':
3129 return ApplyModifier_Words(pp, st); 3128 return ApplyModifier_Words(pp, st);
3130 case 'g': 3129 case 'g':
3131 return ApplyModifier_Gmtime(pp, st); 3130 return ApplyModifier_Gmtime(pp, st);
3132 case 'h': 3131 case 'h':
3133 return ApplyModifier_Hash(pp, st); 3132 return ApplyModifier_Hash(pp, st);
3134 case 'l': 3133 case 'l':
3135 return ApplyModifier_Localtime(pp, st); 3134 return ApplyModifier_Localtime(pp, st);
3136 case 't': 3135 case 't':
3137 return ApplyModifier_To(pp, st); 3136 return ApplyModifier_To(pp, st);
3138 case 'N': 3137 case 'N':
3139 case 'M': 3138 case 'M':
3140 return ApplyModifier_Match(pp, st); 3139 return ApplyModifier_Match(pp, st);
3141 case 'S': 3140 case 'S':
3142 return ApplyModifier_Subst(pp, st); 3141 return ApplyModifier_Subst(pp, st);
3143 case '?': 3142 case '?':
3144 return ApplyModifier_IfElse(pp, st); 3143 return ApplyModifier_IfElse(pp, st);
3145#ifndef NO_REGEX 3144#ifndef NO_REGEX
3146 case 'C': 3145 case 'C':
3147 return ApplyModifier_Regex(pp, st); 3146 return ApplyModifier_Regex(pp, st);
3148#endif 3147#endif
3149 case 'q': 3148 case 'q':
3150 case 'Q': 3149 case 'Q':
3151 return ApplyModifier_Quote(pp, st); 3150 return ApplyModifier_Quote(pp, st);
3152 case 'T': 3151 case 'T':
3153 return ApplyModifier_WordFunc(pp, st, ModifyWord_Tail); 3152 return ApplyModifier_WordFunc(pp, st, ModifyWord_Tail);
3154 case 'H': 3153 case 'H':
3155 return ApplyModifier_WordFunc(pp, st, ModifyWord_Head); 3154 return ApplyModifier_WordFunc(pp, st, ModifyWord_Head);
3156 case 'E': 3155 case 'E':
3157 return ApplyModifier_WordFunc(pp, st, ModifyWord_Suffix); 3156 return ApplyModifier_WordFunc(pp, st, ModifyWord_Suffix);
3158 case 'R': 3157 case 'R':
3159 return ApplyModifier_WordFunc(pp, st, ModifyWord_Root); 3158 return ApplyModifier_WordFunc(pp, st, ModifyWord_Root);
3160 case 'r': 3159 case 'r':
3161 return ApplyModifier_Range(pp, st); 3160 return ApplyModifier_Range(pp, st);
3162 case 'O': 3161 case 'O':
3163 return ApplyModifier_Order(pp, st); 3162 return ApplyModifier_Order(pp, st);
3164 case 'u': 3163 case 'u':
3165 return ApplyModifier_Unique(pp, st); 3164 return ApplyModifier_Unique(pp, st);
3166#ifdef SUNSHCMD 3165#ifdef SUNSHCMD
3167 case 's': 3166 case 's':
3168 return ApplyModifier_SunShell(pp, st); 3167 return ApplyModifier_SunShell(pp, st);
3169#endif 3168#endif
3170 default: 3169 default:
3171 return AMR_UNKNOWN; 3170 return AMR_UNKNOWN;
3172 } 3171 }
3173} 3172}
3174 3173
3175/* Apply any modifiers (such as :Mpattern or :@var@loop@ or :Q or ::=value). */ 3174/* Apply any modifiers (such as :Mpattern or :@var@loop@ or :Q or ::=value). */
3176static char * 3175static char *
3177ApplyModifiers( 3176ApplyModifiers(
3178 const char **pp, /* the parsing position, updated upon return */ 3177 const char **pp, /* the parsing position, updated upon return */
3179 char *val, /* the current value of the variable */ 3178 char *val, /* the current value of the variable */
3180 char const startc, /* '(' or '{', or '\0' for indirect modifiers */ 3179 char const startc, /* '(' or '{', or '\0' for indirect modifiers */
3181 char const endc, /* ')' or '}', or '\0' for indirect modifiers */ 3180 char const endc, /* ')' or '}', or '\0' for indirect modifiers */
3182 Var * const v, 3181 Var * const v,
3183 VarExprFlags *exprFlags, 3182 VarExprFlags *exprFlags,
3184 GNode * const ctxt, /* for looking up and modifying variables */ 3183 GNode * const ctxt, /* for looking up and modifying variables */
3185 VarEvalFlags const eflags, 3184 VarEvalFlags const eflags,
3186 void ** const freePtr /* free this after using the return value */ 3185 void ** const freePtr /* free this after using the return value */
3187) { 3186) {
3188 ApplyModifiersState st = { 3187 ApplyModifiersState st = {
3189 startc, endc, v, ctxt, eflags, val, 3188 startc, endc, v, ctxt, eflags, val,
3190 var_Error, /* .newVal */ 3189 var_Error, /* .newVal */
3191 ' ', /* .sep */ 3190 ' ', /* .sep */
3192 FALSE, /* .oneBigWord */ 3191 FALSE, /* .oneBigWord */
3193 *exprFlags /* .exprFlags */ 3192 *exprFlags /* .exprFlags */
3194 }; 3193 };
3195 const char *p; 3194 const char *p;
3196 const char *mod; 3195 const char *mod;
3197 ApplyModifierResult res; 3196 ApplyModifierResult res;
3198 3197
3199 assert(startc == '(' || startc == '{' || startc == '\0'); 3198 assert(startc == '(' || startc == '{' || startc == '\0');
3200 assert(endc == ')' || endc == '}' || endc == '\0'); 3199 assert(endc == ')' || endc == '}' || endc == '\0');
3201 assert(val != NULL); 3200 assert(val != NULL);
3202 3201
3203 p = *pp; 3202 p = *pp;
3204 while (*p != '\0' && *p != endc) { 3203 while (*p != '\0' && *p != endc) {
3205 3204
3206 if (*p == '$') { 3205 if (*p == '$') {
3207 /* 3206 /*
3208 * We may have some complex modifiers in a variable. 3207 * We may have some complex modifiers in a variable.
3209 */ 3208 */
3210 const char *nested_p = p; 3209 const char *nested_p = p;
3211 void *freeIt; 3210 void *freeIt;
3212 const char *rval; 3211 const char *rval;
3213 char c; 3212 char c;
3214 3213
3215 (void)Var_Parse(&nested_p, st.ctxt, st.eflags, &rval, &freeIt); 3214 (void)Var_Parse(&nested_p, st.ctxt, st.eflags, &rval, &freeIt);
3216 /* TODO: handle errors */ 3215 /* TODO: handle errors */
3217 3216
3218 /* 3217 /*
3219 * If we have not parsed up to st.endc or ':', we are not 3218 * If we have not parsed up to st.endc or ':', we are not
3220 * interested. This means the expression ${VAR:${M_1}${M_2}} 3219 * interested. This means the expression ${VAR:${M_1}${M_2}}
3221 * is not accepted, but ${VAR:${M_1}:${M_2}} is. 3220 * is not accepted, but ${VAR:${M_1}:${M_2}} is.
3222 */ 3221 */
3223 if (rval[0] != '\0' && 3222 if (rval[0] != '\0' &&
3224 (c = *nested_p) != '\0' && c != ':' && c != st.endc) { 3223 (c = *nested_p) != '\0' && c != ':' && c != st.endc) {
3225 if (DEBUG(LINT)) 3224 if (DEBUG(LINT))
3226 Parse_Error(PARSE_FATAL, 3225 Parse_Error(PARSE_FATAL,
3227 "Missing delimiter ':' after indirect modifier \"%.*s\"", 3226 "Missing delimiter ':' after indirect modifier \"%.*s\"",
3228 (int)(nested_p - p), p); 3227 (int)(nested_p - p), p);
3229 3228
3230 free(freeIt); 3229 free(freeIt);
3231 /* XXX: apply_mods doesn't sound like "not interested". */ 3230 /* XXX: apply_mods doesn't sound like "not interested". */
3232 /* XXX: Why is the indirect modifier parsed again by 3231 /* XXX: Why is the indirect modifier parsed again by
3233 * apply_mods? If any, p should be advanced to nested_p. */ 3232 * apply_mods? If any, p should be advanced to nested_p. */
3234 goto apply_mods; 3233 goto apply_mods;
3235 } 3234 }
3236 3235
3237 VAR_DEBUG3("Indirect modifier \"%s\" from \"%.*s\"\n", 3236 VAR_DEBUG3("Indirect modifier \"%s\" from \"%.*s\"\n",
3238 rval, (int)(size_t)(nested_p - p), p); 3237 rval, (int)(size_t)(nested_p - p), p);
3239 3238
3240 p = nested_p; 3239 p = nested_p;
3241 3240
3242 if (rval[0] != '\0') { 3241 if (rval[0] != '\0') {
3243 const char *rval_pp = rval; 3242 const char *rval_pp = rval;
3244 st.val = ApplyModifiers(&rval_pp, st.val, '\0', '\0', v, 3243 st.val = ApplyModifiers(&rval_pp, st.val, '\0', '\0', v,
3245 &st.exprFlags, ctxt, eflags, freePtr); 3244 &st.exprFlags, ctxt, eflags, freePtr);
3246 if (st.val == var_Error 3245 if (st.val == var_Error
3247 || (st.val == varUndefined && !(st.eflags & VARE_UNDEFERR)) 3246 || (st.val == varUndefined && !(st.eflags & VARE_UNDEFERR))
3248 || *rval_pp != '\0') { 3247 || *rval_pp != '\0') {
3249 free(freeIt); 3248 free(freeIt);
3250 goto out; /* error already reported */ 3249 goto out; /* error already reported */
3251 } 3250 }
3252 } 3251 }
3253 free(freeIt); 3252 free(freeIt);
3254 3253
3255 if (*p == ':') 3254 if (*p == ':')
3256 p++; 3255 p++;
3257 else if (*p == '\0' && endc != '\0') { 3256 else if (*p == '\0' && endc != '\0') {
3258 Error("Unclosed variable specification after complex " 3257 Error("Unclosed variable specification after complex "
3259 "modifier (expecting '%c') for %s", st.endc, st.v->name); 3258 "modifier (expecting '%c') for %s", st.endc, st.v->name);
3260 goto out; 3259 goto out;
3261 } 3260 }
3262 continue; 3261 continue;
3263 } 3262 }
3264 apply_mods: 3263 apply_mods:
3265 st.newVal = var_Error; /* default value, in case of errors */ 3264 st.newVal = var_Error; /* default value, in case of errors */
3266 mod = p; 3265 mod = p;
3267 3266
3268 if (DEBUG(VAR)) 3267 if (DEBUG(VAR))
3269 LogBeforeApply(&st, mod, endc); 3268 LogBeforeApply(&st, mod, endc);
3270 3269
3271 res = ApplyModifier(&p, &st); 3270 res = ApplyModifier(&p, &st);
3272 3271
3273#ifdef SYSVVARSUB 3272#ifdef SYSVVARSUB
3274 if (res == AMR_UNKNOWN) { 3273 if (res == AMR_UNKNOWN) {
3275 assert(p == mod); 3274 assert(p == mod);
3276 res = ApplyModifier_SysV(&p, &st); 3275 res = ApplyModifier_SysV(&p, &st);
3277 } 3276 }
3278#endif 3277#endif
3279 3278
3280 if (res == AMR_UNKNOWN) { 3279 if (res == AMR_UNKNOWN) {
3281 Error("Unknown modifier '%c'", *mod); 3280 Error("Unknown modifier '%c'", *mod);
3282 for (p++; *p != ':' && *p != st.endc && *p != '\0'; p++) 3281 for (p++; *p != ':' && *p != st.endc && *p != '\0'; p++)
3283 continue; 3282 continue;
3284 st.newVal = var_Error; 3283 st.newVal = var_Error;
3285 } 3284 }
3286 if (res == AMR_CLEANUP) 3285 if (res == AMR_CLEANUP)
3287 goto cleanup; 3286 goto cleanup;
3288 if (res == AMR_BAD) 3287 if (res == AMR_BAD)
3289 goto bad_modifier; 3288 goto bad_modifier;
3290 3289
3291 if (DEBUG(VAR)) 3290 if (DEBUG(VAR))
3292 LogAfterApply(&st, p, mod); 3291 LogAfterApply(&st, p, mod);
3293 3292
3294 if (st.newVal != st.val) { 3293 if (st.newVal != st.val) {
3295 if (*freePtr) { 3294 if (*freePtr) {
3296 free(st.val); 3295 free(st.val);
3297 *freePtr = NULL; 3296 *freePtr = NULL;
3298 } 3297 }
3299 st.val = st.newVal; 3298 st.val = st.newVal;
3300 if (st.val != var_Error && st.val != varUndefined && 3299 if (st.val != var_Error && st.val != varUndefined &&
3301 st.val != emptyString) { 3300 st.val != emptyString) {
3302 *freePtr = st.val; 3301 *freePtr = st.val;
3303 } 3302 }
3304 } 3303 }
3305 if (*p == '\0' && st.endc != '\0') { 3304 if (*p == '\0' && st.endc != '\0') {
3306 Error("Unclosed variable specification (expecting '%c') " 3305 Error("Unclosed variable specification (expecting '%c') "
3307 "for \"%s\" (value \"%s\") modifier %c", 3306 "for \"%s\" (value \"%s\") modifier %c",
3308 st.endc, st.v->name, st.val, *mod); 3307 st.endc, st.v->name, st.val, *mod);
3309 } else if (*p == ':') { 3308 } else if (*p == ':') {
3310 p++; 3309 p++;
3311 } else if (DEBUG(LINT) && *p != '\0' && *p != endc) { 3310 } else if (DEBUG(LINT) && *p != '\0' && *p != endc) {
3312 Parse_Error(PARSE_FATAL, 3311 Parse_Error(PARSE_FATAL,
3313 "Missing delimiter ':' after modifier \"%.*s\"", 3312 "Missing delimiter ':' after modifier \"%.*s\"",
3314 (int)(p - mod), mod); 3313 (int)(p - mod), mod);
3315 } 3314 }
3316 } 3315 }
3317out: 3316out:
3318 *pp = p; 3317 *pp = p;
3319 assert(st.val != NULL); /* Use var_Error or varUndefined instead. */ 3318 assert(st.val != NULL); /* Use var_Error or varUndefined instead. */
3320 *exprFlags = st.exprFlags; 3319 *exprFlags = st.exprFlags;
3321 return st.val; 3320 return st.val;
3322 3321
3323bad_modifier: 3322bad_modifier:
3324 Error("Bad modifier `:%.*s' for %s", 3323 Error("Bad modifier `:%.*s' for %s",
3325 (int)strcspn(mod, ":)}"), mod, st.v->name); 3324 (int)strcspn(mod, ":)}"), mod, st.v->name);
3326 3325
3327cleanup: 3326cleanup:
3328 *pp = p; 3327 *pp = p;
3329 free(*freePtr); 3328 free(*freePtr);
3330 *freePtr = NULL; 3329 *freePtr = NULL;
3331 *exprFlags = st.exprFlags; 3330 *exprFlags = st.exprFlags;
3332 return var_Error; 3331 return var_Error;
3333} 3332}
3334 3333
3335static Boolean 3334static Boolean
3336VarIsDynamic(GNode *ctxt, const char *varname, size_t namelen) 3335VarIsDynamic(GNode *ctxt, const char *varname, size_t namelen)
3337{ 3336{
3338 if ((namelen == 1 || 3337 if ((namelen == 1 ||
3339 (namelen == 2 && (varname[1] == 'F' || varname[1] == 'D'))) && 3338 (namelen == 2 && (varname[1] == 'F' || varname[1] == 'D'))) &&
3340 (ctxt == VAR_CMDLINE || ctxt == VAR_GLOBAL)) 3339 (ctxt == VAR_CMDLINE || ctxt == VAR_GLOBAL))
3341 { 3340 {
3342 /* 3341 /*
3343 * If substituting a local variable in a non-local context, 3342 * If substituting a local variable in a non-local context,
3344 * assume it's for dynamic source stuff. We have to handle 3343 * assume it's for dynamic source stuff. We have to handle
3345 * this specially and return the longhand for the variable 3344 * this specially and return the longhand for the variable
3346 * with the dollar sign escaped so it makes it back to the 3345 * with the dollar sign escaped so it makes it back to the
3347 * caller. Only four of the local variables are treated 3346 * caller. Only four of the local variables are treated
3348 * specially as they are the only four that will be set 3347 * specially as they are the only four that will be set
3349 * when dynamic sources are expanded. 3348 * when dynamic sources are expanded.
3350 */ 3349 */
3351 switch (varname[0]) { 3350 switch (varname[0]) {
3352 case '@': 3351 case '@':
3353 case '%': 3352 case '%':
3354 case '*': 3353 case '*':
3355 case '!': 3354 case '!':
3356 return TRUE; 3355 return TRUE;
3357 } 3356 }
3358 return FALSE; 3357 return FALSE;
3359 } 3358 }
3360 3359
3361 if ((namelen == 7 || namelen == 8) && varname[0] == '.' && 3360 if ((namelen == 7 || namelen == 8) && varname[0] == '.' &&
3362 ch_isupper(varname[1]) && (ctxt == VAR_CMDLINE || ctxt == VAR_GLOBAL)) 3361 ch_isupper(varname[1]) && (ctxt == VAR_CMDLINE || ctxt == VAR_GLOBAL))
3363 { 3362 {
3364 return strcmp(varname, ".TARGET") == 0 || 3363 return strcmp(varname, ".TARGET") == 0 ||
3365 strcmp(varname, ".ARCHIVE") == 0 || 3364 strcmp(varname, ".ARCHIVE") == 0 ||
3366 strcmp(varname, ".PREFIX") == 0 || 3365 strcmp(varname, ".PREFIX") == 0 ||
3367 strcmp(varname, ".MEMBER") == 0; 3366 strcmp(varname, ".MEMBER") == 0;
3368 } 3367 }
3369 3368
3370 return FALSE; 3369 return FALSE;
3371} 3370}
3372 3371
3373static const char * 3372static const char *
3374UndefinedShortVarValue(char varname, const GNode *ctxt, VarEvalFlags eflags) 3373UndefinedShortVarValue(char varname, const GNode *ctxt, VarEvalFlags eflags)
3375{ 3374{
3376 if (ctxt == VAR_CMDLINE || ctxt == VAR_GLOBAL) { 3375 if (ctxt == VAR_CMDLINE || ctxt == VAR_GLOBAL) {
3377 /* 3376 /*
3378 * If substituting a local variable in a non-local context, 3377 * If substituting a local variable in a non-local context,
3379 * assume it's for dynamic source stuff. We have to handle 3378 * assume it's for dynamic source stuff. We have to handle
3380 * this specially and return the longhand for the variable 3379 * this specially and return the longhand for the variable
3381 * with the dollar sign escaped so it makes it back to the 3380 * with the dollar sign escaped so it makes it back to the
3382 * caller. Only four of the local variables are treated 3381 * caller. Only four of the local variables are treated
3383 * specially as they are the only four that will be set 3382 * specially as they are the only four that will be set
3384 * when dynamic sources are expanded. 3383 * when dynamic sources are expanded.
3385 */ 3384 */
3386 switch (varname) { 3385 switch (varname) {
3387 case '@': 3386 case '@':
3388 return "$(.TARGET)"; 3387 return "$(.TARGET)";
3389 case '%': 3388 case '%':
3390 return "$(.MEMBER)"; 3389 return "$(.MEMBER)";
3391 case '*': 3390 case '*':
3392 return "$(.PREFIX)"; 3391 return "$(.PREFIX)";
3393 case '!': 3392 case '!':
3394 return "$(.ARCHIVE)"; 3393 return "$(.ARCHIVE)";
3395 } 3394 }
3396 } 3395 }
3397 return eflags & VARE_UNDEFERR ? var_Error : varUndefined; 3396 return eflags & VARE_UNDEFERR ? var_Error : varUndefined;
3398} 3397}
3399 3398
3400/* Parse a variable name, until the end character or a colon, whichever 3399/* Parse a variable name, until the end character or a colon, whichever
3401 * comes first. */ 3400 * comes first. */
3402static char * 3401static char *
3403ParseVarname(const char **pp, char startc, char endc, 3402ParseVarname(const char **pp, char startc, char endc,
3404 GNode *ctxt, VarEvalFlags eflags, 3403 GNode *ctxt, VarEvalFlags eflags,
3405 size_t *out_varname_len) 3404 size_t *out_varname_len)
3406{ 3405{
3407 Buffer buf; 3406 Buffer buf;
3408 const char *p = *pp; 3407 const char *p = *pp;
3409 int depth = 1; 3408 int depth = 1;
3410 3409
3411 Buf_Init(&buf, 0); 3410 Buf_Init(&buf, 0);
3412 3411
3413 while (*p != '\0') { 3412 while (*p != '\0') {
3414 /* Track depth so we can spot parse errors. */ 3413 /* Track depth so we can spot parse errors. */
3415 if (*p == startc) 3414 if (*p == startc)
3416 depth++; 3415 depth++;
3417 if (*p == endc) { 3416 if (*p == endc) {
3418 if (--depth == 0) 3417 if (--depth == 0)
3419 break; 3418 break;
3420 } 3419 }
3421 if (*p == ':' && depth == 1) 3420 if (*p == ':' && depth == 1)
3422 break; 3421 break;
3423 3422
3424 /* A variable inside a variable, expand. */ 3423 /* A variable inside a variable, expand. */
3425 if (*p == '$') { 3424 if (*p == '$') {
3426 void *freeIt; 3425 void *freeIt;
3427 const char *rval; 3426 const char *rval;
3428 (void)Var_Parse(&p, ctxt, eflags, &rval, &freeIt); 3427 (void)Var_Parse(&p, ctxt, eflags, &rval, &freeIt);
3429 /* TODO: handle errors */ 3428 /* TODO: handle errors */
3430 Buf_AddStr(&buf, rval); 3429 Buf_AddStr(&buf, rval);
3431 free(freeIt); 3430 free(freeIt);
3432 } else { 3431 } else {
3433 Buf_AddByte(&buf, *p); 3432 Buf_AddByte(&buf, *p);
3434 p++; 3433 p++;
3435 } 3434 }
3436 } 3435 }
3437 *pp = p; 3436 *pp = p;
3438 *out_varname_len = Buf_Len(&buf); 3437 *out_varname_len = Buf_Len(&buf);
3439 return Buf_Destroy(&buf, FALSE); 3438 return Buf_Destroy(&buf, FALSE);
3440} 3439}
3441 3440
3442static Boolean 3441static Boolean
3443ValidShortVarname(char varname, const char *start) 3442ValidShortVarname(char varname, const char *start)
3444{ 3443{
3445 switch (varname) { 3444 switch (varname) {
3446 case '\0': 3445 case '\0':
3447 case ')': 3446 case ')':
3448 case '}': 3447 case '}':
3449 case ':': 3448 case ':':
3450 case '$': 3449 case '$':
3451 break; /* and continue below */ 3450 break; /* and continue below */
3452 default: 3451 default:
3453 return TRUE; 3452 return TRUE;
3454 } 3453 }
3455 3454
3456 if (!DEBUG(LINT)) 3455 if (!DEBUG(LINT))
3457 return FALSE; 3456 return FALSE;
3458 3457
3459 if (varname == '$') 3458 if (varname == '$')
3460 Parse_Error(PARSE_FATAL, 3459 Parse_Error(PARSE_FATAL,
3461 "To escape a dollar, use \\$, not $$, at \"%s\"", start); 3460 "To escape a dollar, use \\$, not $$, at \"%s\"", start);
3462 else if (varname == '\0') 3461 else if (varname == '\0')
3463 Parse_Error(PARSE_FATAL, "Dollar followed by nothing"); 3462 Parse_Error(PARSE_FATAL, "Dollar followed by nothing");
3464 else 3463 else
3465 Parse_Error(PARSE_FATAL, 3464 Parse_Error(PARSE_FATAL,
3466 "Invalid variable name '%c', at \"%s\"", varname, start); 3465 "Invalid variable name '%c', at \"%s\"", varname, start);
3467 3466
3468 return FALSE; 3467 return FALSE;
3469} 3468}
3470 3469
3471/*- 3470/*-
3472 *----------------------------------------------------------------------- 3471 *-----------------------------------------------------------------------
3473 * Var_Parse -- 3472 * Var_Parse --
3474 * Given the start of a variable expression (such as $v, $(VAR), 3473 * Given the start of a variable expression (such as $v, $(VAR),
3475 * ${VAR:Mpattern}), extract the variable name, possibly some 3474 * ${VAR:Mpattern}), extract the variable name, possibly some
3476 * modifiers and find its value by applying the modifiers to the 3475 * modifiers and find its value by applying the modifiers to the
3477 * original value. 3476 * original value.
3478 * 3477 *
3479 * When parsing a condition in ParseEmptyArg, pp may also point to 3478 * When parsing a condition in ParseEmptyArg, pp may also point to
3480 * the "y" of "empty(VARNAME:Modifiers)", which is syntactically 3479 * the "y" of "empty(VARNAME:Modifiers)", which is syntactically
3481 * identical. 3480 * identical.
3482 * 3481 *
3483 * Input: 3482 * Input:
3484 * str The string to parse 3483 * str The string to parse
3485 * ctxt The context for the variable 3484 * ctxt The context for the variable
3486 * flags VARE_UNDEFERR if undefineds are an error 3485 * flags VARE_UNDEFERR if undefineds are an error
3487 * VARE_WANTRES if we actually want the result 3486 * VARE_WANTRES if we actually want the result
3488 * VARE_ASSIGN if we are in a := assignment 3487 * VARE_ASSIGN if we are in a := assignment
3489 * lengthPtr OUT: The length of the specification 3488 * lengthPtr OUT: The length of the specification
3490 * freePtr OUT: Non-NULL if caller should free *freePtr 3489 * freePtr OUT: Non-NULL if caller should free *freePtr
3491 * 3490 *
3492 * Results: 3491 * Results:
3493 * Returns the value of the variable expression, never NULL. 3492 * Returns the value of the variable expression, never NULL.
3494 * Returns var_Error if there was a parse error and VARE_UNDEFERR was 3493 * Returns var_Error if there was a parse error and VARE_UNDEFERR was
3495 * set. 3494 * set.
3496 * Returns varUndefined if there was an undefined variable and 3495 * Returns varUndefined if there was an undefined variable and
3497 * VARE_UNDEFERR was not set. 3496 * VARE_UNDEFERR was not set.
3498 * 3497 *
3499 * Parsing should continue at *pp. 3498 * Parsing should continue at *pp.
3500 * TODO: Document the value of *pp on parse errors. It might be advanced 3499 * TODO: Document the value of *pp on parse errors. It might be advanced
3501 * by 0, or +1, or the index of the parse error, or the guessed end of the 3500 * by 0, or +1, or the index of the parse error, or the guessed end of the
3502 * variable expression. 3501 * variable expression.
3503 * 3502 *
3504 * If var_Error is returned, a diagnostic may or may not have been 3503 * If var_Error is returned, a diagnostic may or may not have been
3505 * printed. XXX: This is inconsistent. 3504 * printed. XXX: This is inconsistent.
3506 * 3505 *
3507 * If varUndefined is returned, a diagnostic may or may not have been 3506 * If varUndefined is returned, a diagnostic may or may not have been
3508 * printed. XXX: This is inconsistent. 3507 * printed. XXX: This is inconsistent.
3509 * 3508 *
3510 * After using the returned value, *freePtr must be freed, preferably 3509 * After using the returned value, *freePtr must be freed, preferably
3511 * using bmake_free since it is NULL in most cases. 3510 * using bmake_free since it is NULL in most cases.
3512 * 3511 *
3513 * Side Effects: 3512 * Side Effects:
3514 * Any effects from the modifiers, such as :!cmd! or ::=value. 3513 * Any effects from the modifiers, such as :!cmd! or ::=value.
3515 *----------------------------------------------------------------------- 3514 *-----------------------------------------------------------------------
3516 */ 3515 */
3517/* coverity[+alloc : arg-*4] */ 3516/* coverity[+alloc : arg-*4] */
3518VarParseResult 3517VarParseResult
3519Var_Parse(const char **pp, GNode *ctxt, VarEvalFlags eflags, 3518Var_Parse(const char **pp, GNode *ctxt, VarEvalFlags eflags,
3520 const char **out_val, void **freePtr) 3519 const char **out_val, void **freePtr)
3521{ 3520{
3522 const char *const start = *pp; 3521 const char *const start = *pp;
3523 const char *p; 3522 const char *p;
3524 Boolean haveModifier; /* TRUE if have modifiers for the variable */ 3523 Boolean haveModifier; /* TRUE if have modifiers for the variable */
3525 char startc; /* Starting character if variable in parens 3524 char startc; /* Starting character if variable in parens
3526 * or braces */ 3525 * or braces */
3527 char endc; /* Ending character if variable in parens 3526 char endc; /* Ending character if variable in parens
3528 * or braces */ 3527 * or braces */
3529 Boolean dynamic; /* TRUE if the variable is local and we're 3528 Boolean dynamic; /* TRUE if the variable is local and we're
3530 * expanding it in a non-local context. This 3529 * expanding it in a non-local context. This
3531 * is done to support dynamic sources. The 3530 * is done to support dynamic sources. The
3532 * result is just the expression, unaltered */ 3531 * result is just the expression, unaltered */
3533 const char *extramodifiers; 3532 const char *extramodifiers;
3534 Var *v; 3533 Var *v;
3535 char *nstr; 3534 char *nstr;
3536 char eflags_str[VarEvalFlags_ToStringSize]; 3535 char eflags_str[VarEvalFlags_ToStringSize];
3537 VarExprFlags exprFlags = 0; 3536 VarExprFlags exprFlags = 0;
3538 3537
3539 VAR_DEBUG3("%s: %s with %s\n", __func__, start, 3538 VAR_DEBUG3("%s: %s with %s\n", __func__, start,
3540 Enum_FlagsToString(eflags_str, sizeof eflags_str, eflags, 3539 Enum_FlagsToString(eflags_str, sizeof eflags_str, eflags,
3541 VarEvalFlags_ToStringSpecs)); 3540 VarEvalFlags_ToStringSpecs));
3542 3541
3543 *freePtr = NULL; 3542 *freePtr = NULL;
3544 extramodifiers = NULL; /* extra modifiers to apply first */ 3543 extramodifiers = NULL; /* extra modifiers to apply first */
3545 dynamic = FALSE; 3544 dynamic = FALSE;
3546 3545
3547 /* Appease GCC, which thinks that the variable might not be 3546 /* Appease GCC, which thinks that the variable might not be
3548 * initialized. */ 3547 * initialized. */
3549 endc = '\0'; 3548 endc = '\0';
3550 3549
3551 startc = start[1]; 3550 startc = start[1];
3552 if (startc != '(' && startc != '{') { 3551 if (startc != '(' && startc != '{') {
3553 char name[2]; 3552 char name[2];
3554 3553
3555 /* 3554 /*
3556 * If it's not bounded by braces of some sort, life is much simpler. 3555 * If it's not bounded by braces of some sort, life is much simpler.
3557 * We just need to check for the first character and return the 3556 * We just need to check for the first character and return the
3558 * value if it exists. 3557 * value if it exists.
3559 */ 3558 */
3560 3559
3561 if (!ValidShortVarname(startc, start)) { 3560 if (!ValidShortVarname(startc, start)) {
3562 (*pp)++; 3561 (*pp)++;
3563 *out_val = var_Error; 3562 *out_val = var_Error;
3564 return VPR_PARSE_MSG; 3563 return VPR_PARSE_MSG;
3565 } 3564 }
3566 3565
3567 name[0] = startc; 3566 name[0] = startc;
3568 name[1] = '\0'; 3567 name[1] = '\0';
3569 v = VarFind(name, ctxt, TRUE); 3568 v = VarFind(name, ctxt, TRUE);
3570 if (v == NULL) { 3569 if (v == NULL) {
3571 *pp += 2; 3570 *pp += 2;
3572 3571
3573 *out_val = UndefinedShortVarValue(startc, ctxt, eflags); 3572 *out_val = UndefinedShortVarValue(startc, ctxt, eflags);
3574 if (DEBUG(LINT) && *out_val == var_Error) { 3573 if (DEBUG(LINT) && *out_val == var_Error) {
3575 Parse_Error(PARSE_FATAL, "Variable \"%s\" is undefined", name); 3574 Parse_Error(PARSE_FATAL, "Variable \"%s\" is undefined", name);
3576 return VPR_UNDEF_MSG; 3575 return VPR_UNDEF_MSG;
3577 } 3576 }
3578 return eflags & VARE_UNDEFERR ? VPR_UNDEF_SILENT : VPR_OK; 3577 return eflags & VARE_UNDEFERR ? VPR_UNDEF_SILENT : VPR_OK;
3579 } else { 3578 } else {
3580 haveModifier = FALSE; 3579 haveModifier = FALSE;
3581 p = start + 1; 3580 p = start + 1;
3582 } 3581 }
3583 } else { 3582 } else {
3584 size_t namelen; 3583 size_t namelen;
3585 char *varname; 3584 char *varname;
3586 3585
3587 endc = startc == '(' ? ')' : '}'; 3586 endc = startc == '(' ? ')' : '}';
3588 3587
3589 p = start + 2; 3588 p = start + 2;
3590 varname = ParseVarname(&p, startc, endc, ctxt, eflags, &namelen); 3589 varname = ParseVarname(&p, startc, endc, ctxt, eflags, &namelen);
3591 3590
3592 if (*p == ':') { 3591 if (*p == ':') {
3593 haveModifier = TRUE; 3592 haveModifier = TRUE;
3594 } else if (*p == endc) { 3593 } else if (*p == endc) {
3595 haveModifier = FALSE; 3594 haveModifier = FALSE;
3596 } else { 3595 } else {
3597 Parse_Error(PARSE_FATAL, "Unclosed variable \"%s\"", varname); 3596 Parse_Error(PARSE_FATAL, "Unclosed variable \"%s\"", varname);
3598 *pp = p; 3597 *pp = p;
3599 free(varname); 3598 free(varname);
3600 *out_val = var_Error; 3599 *out_val = var_Error;
3601 return VPR_PARSE_MSG; 3600 return VPR_PARSE_MSG;
3602 } 3601 }
3603 3602
3604 v = VarFind(varname, ctxt, TRUE); 3603 v = VarFind(varname, ctxt, TRUE);
3605 3604
3606 /* At this point, p points just after the variable name, 3605 /* At this point, p points just after the variable name,
3607 * either at ':' or at endc. */ 3606 * either at ':' or at endc. */
3608 3607
3609 /* 3608 /*
3610 * Check also for bogus D and F forms of local variables since we're 3609 * Check also for bogus D and F forms of local variables since we're
3611 * in a local context and the name is the right length. 3610 * in a local context and the name is the right length.
3612 */ 3611 */
3613 if (v == NULL && ctxt != VAR_CMDLINE && ctxt != VAR_GLOBAL && 3612 if (v == NULL && ctxt != VAR_CMDLINE && ctxt != VAR_GLOBAL &&
3614 namelen == 2 && (varname[1] == 'F' || varname[1] == 'D') && 3613 namelen == 2 && (varname[1] == 'F' || varname[1] == 'D') &&
3615 strchr("@%?*!<>", varname[0]) != NULL) 3614 strchr("@%?*!<>", varname[0]) != NULL)
3616 { 3615 {
3617 /* 3616 /*
3618 * Well, it's local -- go look for it. 3617 * Well, it's local -- go look for it.
3619 */ 3618 */
3620 char name[] = { varname[0], '\0' }; 3619 char name[] = { varname[0], '\0' };
3621 v = VarFind(name, ctxt, 0); 3620 v = VarFind(name, ctxt, 0);
3622 3621
3623 if (v != NULL) { 3622 if (v != NULL) {
3624 if (varname[1] == 'D') { 3623 if (varname[1] == 'D') {
3625 extramodifiers = "H:"; 3624 extramodifiers = "H:";
3626 } else { /* F */ 3625 } else { /* F */
3627 extramodifiers = "T:"; 3626 extramodifiers = "T:";
3628 } 3627 }
3629 } 3628 }
3630 } 3629 }
3631 3630
3632 if (v == NULL) { 3631 if (v == NULL) {
3633 dynamic = VarIsDynamic(ctxt, varname, namelen); 3632 dynamic = VarIsDynamic(ctxt, varname, namelen);
3634 3633
3635 if (!haveModifier) { 3634 if (!haveModifier) {
3636 p++; /* skip endc */ 3635 p++; /* skip endc */
3637 *pp = p; 3636 *pp = p;
3638 if (dynamic) { 3637 if (dynamic) {
3639 char *pstr = bmake_strsedup(start, p); 3638 char *pstr = bmake_strsedup(start, p);
3640 *freePtr = pstr; 3639 *freePtr = pstr;
3641 free(varname); 3640 free(varname);
3642 *out_val = pstr; 3641 *out_val = pstr;
3643 return VPR_OK; 3642 return VPR_OK;
3644 } 3643 }
3645 3644
3646 if ((eflags & VARE_UNDEFERR) && (eflags & VARE_WANTRES) && 3645 if ((eflags & VARE_UNDEFERR) && (eflags & VARE_WANTRES) &&
3647 DEBUG(LINT)) 3646 DEBUG(LINT))
3648 { 3647 {
3649 Parse_Error(PARSE_FATAL, "Variable \"%s\" is undefined", 3648 Parse_Error(PARSE_FATAL, "Variable \"%s\" is undefined",
3650 varname); 3649 varname);
3651 free(varname); 3650 free(varname);
3652 *out_val = var_Error; 3651 *out_val = var_Error;
3653 return VPR_UNDEF_MSG; 3652 return VPR_UNDEF_MSG;
3654 } 3653 }
3655 3654
3656 if (eflags & VARE_UNDEFERR) { 3655 if (eflags & VARE_UNDEFERR) {
3657 free(varname); 3656 free(varname);
3658 *out_val = var_Error; 3657 *out_val = var_Error;
3659 return VPR_UNDEF_SILENT; 3658 return VPR_UNDEF_SILENT;
3660 } 3659 }
3661 3660
3662 free(varname); 3661 free(varname);
3663 *out_val = varUndefined; 3662 *out_val = varUndefined;
3664 return VPR_OK; 3663 return VPR_OK;
3665 } 3664 }
3666 3665
3667 /* The variable expression is based on an undefined variable. 3666 /* The variable expression is based on an undefined variable.
3668 * Nevertheless it needs a Var, for modifiers that access the 3667 * Nevertheless it needs a Var, for modifiers that access the
3669 * variable name, such as :L or :?. 3668 * variable name, such as :L or :?.
3670 * 3669 *
3671 * Most modifiers leave this expression in the "undefined" state 3670 * Most modifiers leave this expression in the "undefined" state
3672 * (VEF_UNDEF), only a few modifiers like :D, :U, :L, :P turn this 3671 * (VEF_UNDEF), only a few modifiers like :D, :U, :L, :P turn this
3673 * undefined expression into a defined expression (VEF_DEF). 3672 * undefined expression into a defined expression (VEF_DEF).
3674 * 3673 *
3675 * At the end, after applying all modifiers, if the expression 3674 * At the end, after applying all modifiers, if the expression
3676 * is still undefined, Var_Parse will return an empty string 3675 * is still undefined, Var_Parse will return an empty string
3677 * instead of the actually computed value. */ 3676 * instead of the actually computed value. */
3678 v = VarNew(varname, varname, "", 0); 3677 v = VarNew(varname, varname, "", 0);
3679 exprFlags = VEF_UNDEF; 3678 exprFlags = VEF_UNDEF;
3680 } else 3679 } else
3681 free(varname); 3680 free(varname);
3682 } 3681 }
3683 3682
3684 if (v->flags & VAR_IN_USE) { 3683 if (v->flags & VAR_IN_USE) {
3685 Fatal("Variable %s is recursive.", v->name); 3684 Fatal("Variable %s is recursive.", v->name);
3686 /*NOTREACHED*/ 3685 /*NOTREACHED*/
3687 } else { 3686 } else {
3688 v->flags |= VAR_IN_USE; 3687 v->flags |= VAR_IN_USE;
3689 } 3688 }
3690 3689
3691 /* 3690 /*
3692 * Before doing any modification, we have to make sure the value 3691 * Before doing any modification, we have to make sure the value
3693 * has been fully expanded. If it looks like recursion might be 3692 * has been fully expanded. If it looks like recursion might be
3694 * necessary (there's a dollar sign somewhere in the variable's value) 3693 * necessary (there's a dollar sign somewhere in the variable's value)
3695 * we just call Var_Subst to do any other substitutions that are 3694 * we just call Var_Subst to do any other substitutions that are
3696 * necessary. Note that the value returned by Var_Subst will have 3695 * necessary. Note that the value returned by Var_Subst will have
3697 * been dynamically-allocated, so it will need freeing when we 3696 * been dynamically-allocated, so it will need freeing when we
3698 * return. 3697 * return.
3699 */ 3698 */
3700 nstr = Buf_GetAll(&v->val, NULL); 3699 nstr = Buf_GetAll(&v->val, NULL);
3701 if (strchr(nstr, '$') != NULL && (eflags & VARE_WANTRES)) { 3700 if (strchr(nstr, '$') != NULL && (eflags & VARE_WANTRES)) {
3702 VarEvalFlags nested_eflags = eflags; 3701 VarEvalFlags nested_eflags = eflags;
3703 if (DEBUG(LINT)) 3702 if (DEBUG(LINT))
3704 nested_eflags &= ~(unsigned)VARE_UNDEFERR; 3703 nested_eflags &= ~(unsigned)VARE_UNDEFERR;
3705 (void)Var_Subst(nstr, ctxt, nested_eflags, &nstr); 3704 (void)Var_Subst(nstr, ctxt, nested_eflags, &nstr);
3706 /* TODO: handle errors */ 3705 /* TODO: handle errors */
3707 *freePtr = nstr; 3706 *freePtr = nstr;
3708 } 3707 }
3709 3708
3710 v->flags &= ~(unsigned)VAR_IN_USE; 3709 v->flags &= ~(unsigned)VAR_IN_USE;
3711 3710
3712 if (haveModifier || extramodifiers != NULL) { 3711 if (haveModifier || extramodifiers != NULL) {
3713 void *extraFree; 3712 void *extraFree;
3714 3713
3715 extraFree = NULL; 3714 extraFree = NULL;
3716 if (extramodifiers != NULL) { 3715 if (extramodifiers != NULL) {
3717 const char *em = extramodifiers; 3716 const char *em = extramodifiers;
3718 nstr = ApplyModifiers(&em, nstr, '(', ')', 3717 nstr = ApplyModifiers(&em, nstr, '(', ')',
3719 v, &exprFlags, ctxt, eflags, &extraFree); 3718 v, &exprFlags, ctxt, eflags, &extraFree);
3720 } 3719 }
3721 3720
3722 if (haveModifier) { 3721 if (haveModifier) {
3723 /* Skip initial colon. */ 3722 /* Skip initial colon. */
3724 p++; 3723 p++;
3725 3724
3726 nstr = ApplyModifiers(&p, nstr, startc, endc, 3725 nstr = ApplyModifiers(&p, nstr, startc, endc,
3727 v, &exprFlags, ctxt, eflags, freePtr); 3726 v, &exprFlags, ctxt, eflags, freePtr);
3728 free(extraFree); 3727 free(extraFree);
3729 } else { 3728 } else {
3730 *freePtr = extraFree; 3729 *freePtr = extraFree;
3731 } 3730 }
3732 } 3731 }
3733 3732
3734 if (*p != '\0') /* Skip past endc if possible. */ 3733 if (*p != '\0') /* Skip past endc if possible. */
3735 p++; 3734 p++;
3736 3735
3737 *pp = p; 3736 *pp = p;
3738 3737
3739 if (v->flags & VAR_FROM_ENV) { 3738 if (v->flags & VAR_FROM_ENV) {
3740 /* Free the environment variable now since we own it, 3739 /* Free the environment variable now since we own it,
3741 * but don't free the variable value if it will be returned. */ 3740 * but don't free the variable value if it will be returned. */
3742 Boolean keepValue = nstr == Buf_GetAll(&v->val, NULL); 3741 Boolean keepValue = nstr == Buf_GetAll(&v->val, NULL);
3743 if (keepValue) 3742 if (keepValue)
3744 *freePtr = nstr; 3743 *freePtr = nstr;
3745 (void)VarFreeEnv(v, !keepValue); 3744 (void)VarFreeEnv(v, !keepValue);
3746 3745
3747 } else if (exprFlags & VEF_UNDEF) { 3746 } else if (exprFlags & VEF_UNDEF) {
3748 if (!(exprFlags & VEF_DEF)) { 3747 if (!(exprFlags & VEF_DEF)) {
3749 if (*freePtr != NULL) { 3748 if (*freePtr != NULL) {
3750 free(*freePtr); 3749 free(*freePtr);
3751 *freePtr = NULL; 3750 *freePtr = NULL;
3752 } 3751 }
3753 if (dynamic) { 3752 if (dynamic) {
3754 nstr = bmake_strsedup(start, p); 3753 nstr = bmake_strsedup(start, p);
3755 *freePtr = nstr; 3754 *freePtr = nstr;
3756 } else { 3755 } else {
3757 /* The expression is still undefined, therefore discard the 3756 /* The expression is still undefined, therefore discard the
3758 * actual value and return an error marker instead. */ 3757 * actual value and return an error marker instead. */
3759 nstr = (eflags & VARE_UNDEFERR) ? var_Error : varUndefined; 3758 nstr = (eflags & VARE_UNDEFERR) ? var_Error : varUndefined;
3760 } 3759 }
3761 } 3760 }
3762 if (nstr != Buf_GetAll(&v->val, NULL)) 3761 if (nstr != Buf_GetAll(&v->val, NULL))
3763 Buf_Destroy(&v->val, TRUE); 3762 Buf_Destroy(&v->val, TRUE);
3764 free(v->name_freeIt); 3763 free(v->name_freeIt);
3765 free(v); 3764 free(v);
3766 } 3765 }
3767 *out_val = nstr; 3766 *out_val = nstr;
3768 return VPR_UNKNOWN; 3767 return VPR_UNKNOWN;
3769} 3768}
3770 3769
3771/* Substitute for all variables in the given string in the given context. 3770/* Substitute for all variables in the given string in the given context.
3772 * 3771 *
3773 * If eflags & VARE_UNDEFERR, Parse_Error will be called when an undefined 3772 * If eflags & VARE_UNDEFERR, Parse_Error will be called when an undefined
3774 * variable is encountered. 3773 * variable is encountered.
3775 * 3774 *
3776 * If eflags & VARE_WANTRES, any effects from the modifiers, such as ::=, 3775 * If eflags & VARE_WANTRES, any effects from the modifiers, such as ::=,
3777 * :sh or !cmd! take place. 3776 * :sh or !cmd! take place.
3778 * 3777 *
3779 * Input: 3778 * Input:
3780 * str the string which to substitute 3779 * str the string which to substitute
3781 * ctxt the context wherein to find variables 3780 * ctxt the context wherein to find variables
3782 * eflags VARE_UNDEFERR if undefineds are an error 3781 * eflags VARE_UNDEFERR if undefineds are an error
3783 * VARE_WANTRES if we actually want the result 3782 * VARE_WANTRES if we actually want the result
3784 * VARE_ASSIGN if we are in a := assignment 3783 * VARE_ASSIGN if we are in a := assignment
3785 * 3784 *
3786 * Results: 3785 * Results:
3787 * The resulting string. 3786 * The resulting string.
3788 */ 3787 */
3789VarParseResult 3788VarParseResult
3790Var_Subst(const char *str, GNode *ctxt, VarEvalFlags eflags, char **out_res) 3789Var_Subst(const char *str, GNode *ctxt, VarEvalFlags eflags, char **out_res)
3791{ 3790{
3792 const char *p = str; 3791 const char *p = str;
3793 Buffer buf; /* Buffer for forming things */ 3792 Buffer buf; /* Buffer for forming things */
3794 3793
3795 /* Set true if an error has already been reported, 3794 /* Set true if an error has already been reported,
3796 * to prevent a plethora of messages when recursing */ 3795 * to prevent a plethora of messages when recursing */
3797 static Boolean errorReported; 3796 static Boolean errorReported;
3798 3797
3799 Buf_Init(&buf, 0); 3798 Buf_Init(&buf, 0);
3800 errorReported = FALSE; 3799 errorReported = FALSE;
3801 3800
3802 while (*p != '\0') { 3801 while (*p != '\0') {
3803 if (p[0] == '$' && p[1] == '$') { 3802 if (p[0] == '$' && p[1] == '$') {
3804 /* A dollar sign may be escaped with another dollar sign. */ 3803 /* A dollar sign may be escaped with another dollar sign. */
3805 if (save_dollars && (eflags & VARE_ASSIGN)) 3804 if (save_dollars && (eflags & VARE_ASSIGN))
3806 Buf_AddByte(&buf, '$'); 3805 Buf_AddByte(&buf, '$');
3807 Buf_AddByte(&buf, '$'); 3806 Buf_AddByte(&buf, '$');
3808 p += 2; 3807 p += 2;
3809 } else if (*p != '$') { 3808 } else if (*p != '$') {
3810 /* 3809 /*
3811 * Skip as many characters as possible -- either to the end of 3810 * Skip as many characters as possible -- either to the end of
3812 * the string or to the next dollar sign (variable expression). 3811 * the string or to the next dollar sign (variable expression).
3813 */ 3812 */
3814 const char *plainStart = p; 3813 const char *plainStart = p;
3815 3814
3816 for (p++; *p != '$' && *p != '\0'; p++) 3815 for (p++; *p != '$' && *p != '\0'; p++)
3817 continue; 3816 continue;
3818 Buf_AddBytesBetween(&buf, plainStart, p); 3817 Buf_AddBytesBetween(&buf, plainStart, p);
3819 } else { 3818 } else {
3820 const char *nested_p = p; 3819 const char *nested_p = p;
3821 void *freeIt; 3820 void *freeIt;
3822 const char *val; 3821 const char *val;
3823 (void)Var_Parse(&nested_p, ctxt, eflags, &val, &freeIt); 3822 (void)Var_Parse(&nested_p, ctxt, eflags, &val, &freeIt);
3824 /* TODO: handle errors */ 3823 /* TODO: handle errors */
3825 3824
3826 if (val == var_Error || val == varUndefined) { 3825 if (val == var_Error || val == varUndefined) {
3827 /* 3826 /*
3828 * If performing old-time variable substitution, skip over 3827 * If performing old-time variable substitution, skip over
3829 * the variable and continue with the substitution. Otherwise, 3828 * the variable and continue with the substitution. Otherwise,
3830 * store the dollar sign and advance str so we continue with 3829 * store the dollar sign and advance str so we continue with
3831 * the string... 3830 * the string...
3832 */ 3831 */
3833 if (oldVars) { 3832 if (oldVars) {
3834 p = nested_p; 3833 p = nested_p;
3835 } else if ((eflags & VARE_UNDEFERR) || val == var_Error) { 3834 } else if ((eflags & VARE_UNDEFERR) || val == var_Error) {
3836 /* 3835 /*
3837 * If variable is undefined, complain and skip the 3836 * If variable is undefined, complain and skip the
3838 * variable. The complaint will stop us from doing anything 3837 * variable. The complaint will stop us from doing anything
3839 * when the file is parsed. 3838 * when the file is parsed.
3840 */ 3839 */
3841 if (!errorReported) { 3840 if (!errorReported) {
3842 Parse_Error(PARSE_FATAL, "Undefined variable \"%.*s\"", 3841 Parse_Error(PARSE_FATAL, "Undefined variable \"%.*s\"",
3843 (int)(size_t)(nested_p - p), p); 3842 (int)(size_t)(nested_p - p), p);
3844 } 3843 }
3845 p = nested_p; 3844 p = nested_p;
3846 errorReported = TRUE; 3845 errorReported = TRUE;
3847 } else { 3846 } else {
3848 /* Copy the initial '$' of the undefined expression, 3847 /* Copy the initial '$' of the undefined expression,
3849 * thereby deferring expansion of the expression, but 3848 * thereby deferring expansion of the expression, but
3850 * expand nested expressions if already possible. 3849 * expand nested expressions if already possible.
3851 * See unit-tests/varparse-undef-partial.mk. */ 3850 * See unit-tests/varparse-undef-partial.mk. */
3852 Buf_AddByte(&buf, *p); 3851 Buf_AddByte(&buf, *p);
3853 p++; 3852 p++;
3854 } 3853 }
3855 } else { 3854 } else {
3856 p = nested_p; 3855 p = nested_p;
3857 Buf_AddStr(&buf, val); 3856 Buf_AddStr(&buf, val);
3858 } 3857 }
3859 free(freeIt); 3858 free(freeIt);
3860 freeIt = NULL; 3859 freeIt = NULL;
3861 } 3860 }
3862 } 3861 }
3863 3862
3864 *out_res = Buf_DestroyCompact(&buf); 3863 *out_res = Buf_DestroyCompact(&buf);
3865 return VPR_OK; 3864 return VPR_OK;
3866} 3865}
3867 3866
3868/* Initialize the variables module. */ 3867/* Initialize the variables module. */
3869void 3868void
3870Var_Init(void) 3869Var_Init(void)
3871{ 3870{
3872 VAR_INTERNAL = Targ_NewGN("Internal"); 3871 VAR_INTERNAL = Targ_NewGN("Internal");
3873 VAR_GLOBAL = Targ_NewGN("Global"); 3872 VAR_GLOBAL = Targ_NewGN("Global");
3874 VAR_CMDLINE = Targ_NewGN("Command"); 3873 VAR_CMDLINE = Targ_NewGN("Command");
3875} 3874}
3876 3875
3877/* Clean up the variables module. */ 3876/* Clean up the variables module. */
3878void 3877void
3879Var_End(void) 3878Var_End(void)
3880{ 3879{
3881 Var_Stats(); 3880 Var_Stats();
3882} 3881}
3883 3882
3884void 3883void
3885Var_Stats(void) 3884Var_Stats(void)
3886{ 3885{
3887 HashTable_DebugStats(&VAR_GLOBAL->context, "VAR_GLOBAL"); 3886 HashTable_DebugStats(&VAR_GLOBAL->context, "VAR_GLOBAL");
3888} 3887}
3889 3888
3890/* Print all variables in a context, sorted by name. */ 3889/* Print all variables in a context, sorted by name. */
3891void 3890void
3892Var_Dump(GNode *ctxt) 3891Var_Dump(GNode *ctxt)
3893{ 3892{
3894 Vector /* of const char * */ vec; 3893 Vector /* of const char * */ vec;
3895 HashIter hi; 3894 HashIter hi;
3896 HashEntry *he; 
3897 size_t i; 3895 size_t i;
3898 const char **varnames; 3896 const char **varnames;
3899 3897
3900 Vector_Init(&vec, sizeof(const char *)); 3898 Vector_Init(&vec, sizeof(const char *));
3901 3899
3902 HashIter_Init(&hi, &ctxt->context); 3900 HashIter_Init(&hi, &ctxt->context);
3903 while ((he = HashIter_Next(&hi)) != NULL) 3901 while (HashIter_Next(&hi) != NULL)
3904 *(const char **)Vector_Push(&vec) = he->key; 3902 *(const char **)Vector_Push(&vec) = hi.entry->key;
3905 varnames = vec.items; 3903 varnames = vec.items;
3906 3904
3907 qsort(varnames, vec.len, sizeof varnames[0], str_cmp_asc); 3905 qsort(varnames, vec.len, sizeof varnames[0], str_cmp_asc);
3908 3906
3909 for (i = 0; i < vec.len; i++) { 3907 for (i = 0; i < vec.len; i++) {
3910 const char *varname = varnames[i]; 3908 const char *varname = varnames[i];
3911 Var *var = HashTable_FindValue(&ctxt->context, varname); 3909 Var *var = HashTable_FindValue(&ctxt->context, varname);
3912 debug_printf("%-16s = %s\n", varname, Buf_GetAll(&var->val, NULL)); 3910 debug_printf("%-16s = %s\n", varname, Buf_GetAll(&var->val, NULL));
3913 } 3911 }
3914 3912
3915 Vector_Done(&vec); 3913 Vector_Done(&vec);
3916} 3914}