| @@ -1,1469 +1,1463 @@ | | | @@ -1,1469 +1,1463 @@ |
1 | /* $NetBSD: var.c,v 1.882 2021/03/14 20:18:33 rillig Exp $ */ | | 1 | /* $NetBSD: var.c,v 1.883 2021/03/14 20:23:29 rillig Exp $ */ |
2 | | | 2 | |
3 | /* | | 3 | /* |
4 | * Copyright (c) 1988, 1989, 1990, 1993 | | 4 | * Copyright (c) 1988, 1989, 1990, 1993 |
5 | * The Regents of the University of California. All rights reserved. | | 5 | * The Regents of the University of California. All rights reserved. |
6 | * | | 6 | * |
7 | * This code is derived from software contributed to Berkeley by | | 7 | * This code is derived from software contributed to Berkeley by |
8 | * Adam de Boor. | | 8 | * Adam de Boor. |
9 | * | | 9 | * |
10 | * Redistribution and use in source and binary forms, with or without | | 10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions | | 11 | * modification, are permitted provided that the following conditions |
12 | * are met: | | 12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright | | 13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. | | 14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright | | 15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the | | 16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. | | 17 | * documentation and/or other materials provided with the distribution. |
18 | * 3. Neither the name of the University nor the names of its contributors | | 18 | * 3. Neither the name of the University nor the names of its contributors |
19 | * may be used to endorse or promote products derived from this software | | 19 | * may be used to endorse or promote products derived from this software |
20 | * without specific prior written permission. | | 20 | * without specific prior written permission. |
21 | * | | 21 | * |
22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | | 22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | 23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | 24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | | 25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | 26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | | 27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | | 28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | 29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | 30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | | 31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
32 | * SUCH DAMAGE. | | 32 | * SUCH DAMAGE. |
33 | */ | | 33 | */ |
34 | | | 34 | |
35 | /* | | 35 | /* |
36 | * Copyright (c) 1989 by Berkeley Softworks | | 36 | * Copyright (c) 1989 by Berkeley Softworks |
37 | * All rights reserved. | | 37 | * All rights reserved. |
38 | * | | 38 | * |
39 | * This code is derived from software contributed to Berkeley by | | 39 | * This code is derived from software contributed to Berkeley by |
40 | * Adam de Boor. | | 40 | * Adam de Boor. |
41 | * | | 41 | * |
42 | * Redistribution and use in source and binary forms, with or without | | 42 | * Redistribution and use in source and binary forms, with or without |
43 | * modification, are permitted provided that the following conditions | | 43 | * modification, are permitted provided that the following conditions |
44 | * are met: | | 44 | * are met: |
45 | * 1. Redistributions of source code must retain the above copyright | | 45 | * 1. Redistributions of source code must retain the above copyright |
46 | * notice, this list of conditions and the following disclaimer. | | 46 | * notice, this list of conditions and the following disclaimer. |
47 | * 2. Redistributions in binary form must reproduce the above copyright | | 47 | * 2. Redistributions in binary form must reproduce the above copyright |
48 | * notice, this list of conditions and the following disclaimer in the | | 48 | * notice, this list of conditions and the following disclaimer in the |
49 | * documentation and/or other materials provided with the distribution. | | 49 | * documentation and/or other materials provided with the distribution. |
50 | * 3. All advertising materials mentioning features or use of this software | | 50 | * 3. All advertising materials mentioning features or use of this software |
51 | * must display the following acknowledgement: | | 51 | * must display the following acknowledgement: |
52 | * This product includes software developed by the University of | | 52 | * This product includes software developed by the University of |
53 | * California, Berkeley and its contributors. | | 53 | * California, Berkeley and its contributors. |
54 | * 4. Neither the name of the University nor the names of its contributors | | 54 | * 4. Neither the name of the University nor the names of its contributors |
55 | * may be used to endorse or promote products derived from this software | | 55 | * may be used to endorse or promote products derived from this software |
56 | * without specific prior written permission. | | 56 | * without specific prior written permission. |
57 | * | | 57 | * |
58 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | | 58 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
59 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | 59 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
60 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | 60 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
61 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | | 61 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
62 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | 62 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
63 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | | 63 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
64 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | | 64 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
65 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | 65 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
66 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | 66 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
67 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | | 67 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
68 | * SUCH DAMAGE. | | 68 | * SUCH DAMAGE. |
69 | */ | | 69 | */ |
70 | | | 70 | |
71 | /* | | 71 | /* |
72 | * Handling of variables and the expressions formed from them. | | 72 | * Handling of variables and the expressions formed from them. |
73 | * | | 73 | * |
74 | * Variables are set using lines of the form VAR=value. Both the variable | | 74 | * Variables are set using lines of the form VAR=value. Both the variable |
75 | * name and the value can contain references to other variables, by using | | 75 | * name and the value can contain references to other variables, by using |
76 | * expressions like ${VAR}, ${VAR:Modifiers}, ${${VARNAME}} or ${VAR:${MODS}}. | | 76 | * expressions like ${VAR}, ${VAR:Modifiers}, ${${VARNAME}} or ${VAR:${MODS}}. |
77 | * | | 77 | * |
78 | * Interface: | | 78 | * Interface: |
79 | * Var_Init Initialize this module. | | 79 | * Var_Init Initialize this module. |
80 | * | | 80 | * |
81 | * Var_End Clean up the module. | | 81 | * Var_End Clean up the module. |
82 | * | | 82 | * |
83 | * Var_Set | | 83 | * Var_Set |
84 | * Var_SetExpand | | 84 | * Var_SetExpand |
85 | * Set the value of the variable, creating it if | | 85 | * Set the value of the variable, creating it if |
86 | * necessary. | | 86 | * necessary. |
87 | * | | 87 | * |
88 | * Var_Append | | 88 | * Var_Append |
89 | * Var_AppendExpand | | 89 | * Var_AppendExpand |
90 | * Append more characters to the variable, creating it if | | 90 | * Append more characters to the variable, creating it if |
91 | * necessary. A space is placed between the old value and | | 91 | * necessary. A space is placed between the old value and |
92 | * the new one. | | 92 | * the new one. |
93 | * | | 93 | * |
94 | * Var_Exists | | 94 | * Var_Exists |
95 | * Var_ExistsExpand | | 95 | * Var_ExistsExpand |
96 | * See if a variable exists. | | 96 | * See if a variable exists. |
97 | * | | 97 | * |
98 | * Var_Value Return the unexpanded value of a variable, or NULL if | | 98 | * Var_Value Return the unexpanded value of a variable, or NULL if |
99 | * the variable is undefined. | | 99 | * the variable is undefined. |
100 | * | | 100 | * |
101 | * Var_Subst Substitute all variable expressions in a string. | | 101 | * Var_Subst Substitute all variable expressions in a string. |
102 | * | | 102 | * |
103 | * Var_Parse Parse a variable expression such as ${VAR:Mpattern}. | | 103 | * Var_Parse Parse a variable expression such as ${VAR:Mpattern}. |
104 | * | | 104 | * |
105 | * Var_Delete | | 105 | * Var_Delete |
106 | * Var_DeleteExpand | | 106 | * Var_DeleteExpand |
107 | * Delete a variable. | | 107 | * Delete a variable. |
108 | * | | 108 | * |
109 | * Var_ReexportVars | | 109 | * Var_ReexportVars |
110 | * Export some or even all variables to the environment | | 110 | * Export some or even all variables to the environment |
111 | * of this process and its child processes. | | 111 | * of this process and its child processes. |
112 | * | | 112 | * |
113 | * Var_Export Export the variable to the environment of this process | | 113 | * Var_Export Export the variable to the environment of this process |
114 | * and its child processes. | | 114 | * and its child processes. |
115 | * | | 115 | * |
116 | * Var_UnExport Don't export the variable anymore. | | 116 | * Var_UnExport Don't export the variable anymore. |
117 | * | | 117 | * |
118 | * Debugging: | | 118 | * Debugging: |
119 | * Var_Stats Print out hashing statistics if in -dh mode. | | 119 | * Var_Stats Print out hashing statistics if in -dh mode. |
120 | * | | 120 | * |
121 | * Var_Dump Print out all variables defined in the given scope. | | 121 | * Var_Dump Print out all variables defined in the given scope. |
122 | * | | 122 | * |
123 | * XXX: There's a lot of almost duplicate code in these functions that only | | 123 | * XXX: There's a lot of almost duplicate code in these functions that only |
124 | * differs in subtle details that are not mentioned in the manual page. | | 124 | * differs in subtle details that are not mentioned in the manual page. |
125 | */ | | 125 | */ |
126 | | | 126 | |
127 | #include <sys/stat.h> | | 127 | #include <sys/stat.h> |
128 | #ifndef NO_REGEX | | 128 | #ifndef NO_REGEX |
129 | #include <sys/types.h> | | 129 | #include <sys/types.h> |
130 | #include <regex.h> | | 130 | #include <regex.h> |
131 | #endif | | 131 | #endif |
132 | #include <errno.h> | | 132 | #include <errno.h> |
133 | #include <inttypes.h> | | 133 | #include <inttypes.h> |
134 | #include <limits.h> | | 134 | #include <limits.h> |
135 | #include <time.h> | | 135 | #include <time.h> |
136 | | | 136 | |
137 | #include "make.h" | | 137 | #include "make.h" |
138 | #include "dir.h" | | 138 | #include "dir.h" |
139 | #include "job.h" | | 139 | #include "job.h" |
140 | #include "metachar.h" | | 140 | #include "metachar.h" |
141 | | | 141 | |
142 | /* "@(#)var.c 8.3 (Berkeley) 3/19/94" */ | | 142 | /* "@(#)var.c 8.3 (Berkeley) 3/19/94" */ |
143 | MAKE_RCSID("$NetBSD: var.c,v 1.882 2021/03/14 20:18:33 rillig Exp $"); | | 143 | MAKE_RCSID("$NetBSD: var.c,v 1.883 2021/03/14 20:23:29 rillig Exp $"); |
144 | | | 144 | |
145 | typedef enum VarFlags { | | 145 | typedef enum VarFlags { |
146 | VFL_NONE = 0, | | 146 | VFL_NONE = 0, |
147 | | | 147 | |
148 | /* | | 148 | /* |
149 | * The variable's value is currently being used by Var_Parse or | | 149 | * The variable's value is currently being used by Var_Parse or |
150 | * Var_Subst. This marker is used to avoid endless recursion. | | 150 | * Var_Subst. This marker is used to avoid endless recursion. |
151 | */ | | 151 | */ |
152 | VFL_IN_USE = 1 << 0, | | 152 | VFL_IN_USE = 1 << 0, |
153 | | | 153 | |
154 | /* | | 154 | /* |
155 | * The variable comes from the environment. | | 155 | * The variable comes from the environment. |
156 | * These variables are not registered in any GNode, therefore they | | 156 | * These variables are not registered in any GNode, therefore they |
157 | * must be freed as soon as they are not used anymore. | | 157 | * must be freed as soon as they are not used anymore. |
158 | */ | | 158 | */ |
159 | VFL_FROM_ENV = 1 << 1, | | 159 | VFL_FROM_ENV = 1 << 1, |
160 | | | 160 | |
161 | /* | | 161 | /* |
162 | * The variable is exported to the environment, to be used by child | | 162 | * The variable is exported to the environment, to be used by child |
163 | * processes. | | 163 | * processes. |
164 | */ | | 164 | */ |
165 | VFL_EXPORTED = 1 << 2, | | 165 | VFL_EXPORTED = 1 << 2, |
166 | | | 166 | |
167 | /* | | 167 | /* |
168 | * At the point where this variable was exported, it contained an | | 168 | * At the point where this variable was exported, it contained an |
169 | * unresolved reference to another variable. Before any child | | 169 | * unresolved reference to another variable. Before any child |
170 | * process is started, it needs to be exported again, in the hope | | 170 | * process is started, it needs to be exported again, in the hope |
171 | * that the referenced variable can then be resolved. | | 171 | * that the referenced variable can then be resolved. |
172 | */ | | 172 | */ |
173 | VFL_REEXPORT = 1 << 3, | | 173 | VFL_REEXPORT = 1 << 3, |
174 | | | 174 | |
175 | /* The variable came from the command line. */ | | 175 | /* The variable came from the command line. */ |
176 | VFL_FROM_CMD = 1 << 4, | | 176 | VFL_FROM_CMD = 1 << 4, |
177 | | | 177 | |
178 | /* | | 178 | /* |
179 | * The variable value cannot be changed anymore, and the variable | | 179 | * The variable value cannot be changed anymore, and the variable |
180 | * cannot be deleted. Any attempts to do so are silently ignored, | | 180 | * cannot be deleted. Any attempts to do so are silently ignored, |
181 | * they are logged with -dv though. | | 181 | * they are logged with -dv though. |
182 | * | | 182 | * |
183 | * See VAR_SET_READONLY. | | 183 | * See VAR_SET_READONLY. |
184 | */ | | 184 | */ |
185 | VFL_READONLY = 1 << 5 | | 185 | VFL_READONLY = 1 << 5 |
186 | } VarFlags; | | 186 | } VarFlags; |
187 | | | 187 | |
188 | /* | | 188 | /* |
189 | * Variables are defined using one of the VAR=value assignments. Their | | 189 | * Variables are defined using one of the VAR=value assignments. Their |
190 | * value can be queried by expressions such as $V, ${VAR}, or with modifiers | | 190 | * value can be queried by expressions such as $V, ${VAR}, or with modifiers |
191 | * such as ${VAR:S,from,to,g:Q}. | | 191 | * such as ${VAR:S,from,to,g:Q}. |
192 | * | | 192 | * |
193 | * There are 3 kinds of variables: scope variables, environment variables, | | 193 | * There are 3 kinds of variables: scope variables, environment variables, |
194 | * undefined variables. | | 194 | * undefined variables. |
195 | * | | 195 | * |
196 | * Scope variables are stored in a GNode.scope. The only way to undefine | | 196 | * Scope variables are stored in a GNode.scope. The only way to undefine |
197 | * a scope variable is using the .undef directive. In particular, it must | | 197 | * a scope variable is using the .undef directive. In particular, it must |
198 | * not be possible to undefine a variable during the evaluation of an | | 198 | * not be possible to undefine a variable during the evaluation of an |
199 | * expression, or Var.name might point nowhere. | | 199 | * expression, or Var.name might point nowhere. |
200 | * | | 200 | * |
201 | * Environment variables are temporary. They are returned by VarFind, and | | 201 | * Environment variables are temporary. They are returned by VarFind, and |
202 | * after using them, they must be freed using VarFreeEnv. | | 202 | * after using them, they must be freed using VarFreeEnv. |
203 | * | | 203 | * |
204 | * Undefined variables occur during evaluation of variable expressions such | | 204 | * Undefined variables occur during evaluation of variable expressions such |
205 | * as ${UNDEF:Ufallback} in Var_Parse and ApplyModifiers. | | 205 | * as ${UNDEF:Ufallback} in Var_Parse and ApplyModifiers. |
206 | */ | | 206 | */ |
207 | typedef struct Var { | | 207 | typedef struct Var { |
208 | /* | | 208 | /* |
209 | * The name of the variable, once set, doesn't change anymore. | | 209 | * The name of the variable, once set, doesn't change anymore. |
210 | * For scope variables, it aliases the corresponding HashEntry name. | | 210 | * For scope variables, it aliases the corresponding HashEntry name. |
211 | * For environment and undefined variables, it is allocated. | | 211 | * For environment and undefined variables, it is allocated. |
212 | */ | | 212 | */ |
213 | FStr name; | | 213 | FStr name; |
214 | | | 214 | |
215 | /* The unexpanded value of the variable. */ | | 215 | /* The unexpanded value of the variable. */ |
216 | Buffer val; | | 216 | Buffer val; |
217 | /* Miscellaneous status flags. */ | | 217 | /* Miscellaneous status flags. */ |
218 | VarFlags flags; | | 218 | VarFlags flags; |
219 | } Var; | | 219 | } Var; |
220 | | | 220 | |
221 | /* | | 221 | /* |
222 | * Exporting variables is expensive and may leak memory, so skip it if we | | 222 | * Exporting variables is expensive and may leak memory, so skip it if we |
223 | * can. | | 223 | * can. |
224 | * | | 224 | * |
225 | * To avoid this, it might be worth encapsulating the environment variables | | 225 | * To avoid this, it might be worth encapsulating the environment variables |
226 | * in a separate data structure called EnvVars. | | 226 | * in a separate data structure called EnvVars. |
227 | */ | | 227 | */ |
228 | typedef enum VarExportedMode { | | 228 | typedef enum VarExportedMode { |
229 | VAR_EXPORTED_NONE, | | 229 | VAR_EXPORTED_NONE, |
230 | VAR_EXPORTED_SOME, | | 230 | VAR_EXPORTED_SOME, |
231 | VAR_EXPORTED_ALL | | 231 | VAR_EXPORTED_ALL |
232 | } VarExportedMode; | | 232 | } VarExportedMode; |
233 | | | 233 | |
234 | typedef enum UnexportWhat { | | 234 | typedef enum UnexportWhat { |
235 | /* Unexport the variables given by name. */ | | 235 | /* Unexport the variables given by name. */ |
236 | UNEXPORT_NAMED, | | 236 | UNEXPORT_NAMED, |
237 | /* | | 237 | /* |
238 | * Unexport all globals previously exported, but keep the environment | | 238 | * Unexport all globals previously exported, but keep the environment |
239 | * inherited from the parent. | | 239 | * inherited from the parent. |
240 | */ | | 240 | */ |
241 | UNEXPORT_ALL, | | 241 | UNEXPORT_ALL, |
242 | /* | | 242 | /* |
243 | * Unexport all globals previously exported and clear the environment | | 243 | * Unexport all globals previously exported and clear the environment |
244 | * inherited from the parent. | | 244 | * inherited from the parent. |
245 | */ | | 245 | */ |
246 | UNEXPORT_ENV | | 246 | UNEXPORT_ENV |
247 | } UnexportWhat; | | 247 | } UnexportWhat; |
248 | | | 248 | |
249 | /* Flags for pattern matching in the :S and :C modifiers */ | | 249 | /* Flags for pattern matching in the :S and :C modifiers */ |
250 | typedef struct VarPatternFlags { | | 250 | typedef struct VarPatternFlags { |
251 | | | 251 | |
252 | /* Replace as often as possible ('g') */ | | 252 | /* Replace as often as possible ('g') */ |
253 | Boolean subGlobal: 1; | | 253 | Boolean subGlobal: 1; |
254 | /* Replace only once ('1') */ | | 254 | /* Replace only once ('1') */ |
255 | Boolean subOnce: 1; | | 255 | Boolean subOnce: 1; |
256 | /* Match at start of word ('^') */ | | 256 | /* Match at start of word ('^') */ |
257 | Boolean anchorStart: 1; | | 257 | Boolean anchorStart: 1; |
258 | /* Match at end of word ('$') */ | | 258 | /* Match at end of word ('$') */ |
259 | Boolean anchorEnd: 1; | | 259 | Boolean anchorEnd: 1; |
260 | } VarPatternFlags; | | 260 | } VarPatternFlags; |
261 | | | 261 | |
262 | /* SepBuf builds a string from words interleaved with separators. */ | | 262 | /* SepBuf builds a string from words interleaved with separators. */ |
263 | typedef struct SepBuf { | | 263 | typedef struct SepBuf { |
264 | Buffer buf; | | 264 | Buffer buf; |
265 | Boolean needSep; | | 265 | Boolean needSep; |
266 | /* Usually ' ', but see the ':ts' modifier. */ | | 266 | /* Usually ' ', but see the ':ts' modifier. */ |
267 | char sep; | | 267 | char sep; |
268 | } SepBuf; | | 268 | } SepBuf; |
269 | | | 269 | |
270 | | | 270 | |
271 | ENUM_FLAGS_RTTI_4(VarEvalFlags, | | 271 | ENUM_FLAGS_RTTI_4(VarEvalFlags, |
272 | VARE_UNDEFERR, VARE_WANTRES, VARE_KEEP_DOLLAR, | | 272 | VARE_UNDEFERR, VARE_WANTRES, VARE_KEEP_DOLLAR, |
273 | VARE_KEEP_UNDEF); | | 273 | VARE_KEEP_UNDEF); |
274 | | | 274 | |
275 | /* | | 275 | /* |
276 | * This lets us tell if we have replaced the original environ | | 276 | * This lets us tell if we have replaced the original environ |
277 | * (which we cannot free). | | 277 | * (which we cannot free). |
278 | */ | | 278 | */ |
279 | char **savedEnv = NULL; | | 279 | char **savedEnv = NULL; |
280 | | | 280 | |
281 | /* | | 281 | /* |
282 | * Special return value for Var_Parse, indicating a parse error. It may be | | 282 | * Special return value for Var_Parse, indicating a parse error. It may be |
283 | * caused by an undefined variable, a syntax error in a modifier or | | 283 | * caused by an undefined variable, a syntax error in a modifier or |
284 | * something entirely different. | | 284 | * something entirely different. |
285 | */ | | 285 | */ |
286 | char var_Error[] = ""; | | 286 | char var_Error[] = ""; |
287 | | | 287 | |
288 | /* | | 288 | /* |
289 | * Special return value for Var_Parse, indicating an undefined variable in | | 289 | * Special return value for Var_Parse, indicating an undefined variable in |
290 | * a case where VARE_UNDEFERR is not set. This undefined variable is | | 290 | * a case where VARE_UNDEFERR is not set. This undefined variable is |
291 | * typically a dynamic variable such as ${.TARGET}, whose expansion needs to | | 291 | * typically a dynamic variable such as ${.TARGET}, whose expansion needs to |
292 | * be deferred until it is defined in an actual target. | | 292 | * be deferred until it is defined in an actual target. |
293 | * | | 293 | * |
294 | * See VARE_KEEP_UNDEF. | | 294 | * See VARE_KEEP_UNDEF. |
295 | */ | | 295 | */ |
296 | static char varUndefined[] = ""; | | 296 | static char varUndefined[] = ""; |
297 | | | 297 | |
298 | /* | | 298 | /* |
299 | * Traditionally this make consumed $$ during := like any other expansion. | | 299 | * Traditionally this make consumed $$ during := like any other expansion. |
300 | * Other make's do not, and this make follows straight since 2016-01-09. | | 300 | * Other make's do not, and this make follows straight since 2016-01-09. |
301 | * | | 301 | * |
302 | * This knob allows controlling the behavior. | | 302 | * This knob allows controlling the behavior. |
303 | * FALSE to consume $$ during := assignment. | | 303 | * FALSE to consume $$ during := assignment. |
304 | * TRUE to preserve $$ during := assignment. | | 304 | * TRUE to preserve $$ during := assignment. |
305 | */ | | 305 | */ |
306 | #define MAKE_SAVE_DOLLARS ".MAKE.SAVE_DOLLARS" | | 306 | #define MAKE_SAVE_DOLLARS ".MAKE.SAVE_DOLLARS" |
307 | static Boolean save_dollars = TRUE; | | 307 | static Boolean save_dollars = TRUE; |
308 | | | 308 | |
309 | /* | | 309 | /* |
310 | * A scope collects variable names and their values. | | 310 | * A scope collects variable names and their values. |
311 | * | | 311 | * |
312 | * The main scope is SCOPE_GLOBAL, which contains the variables that are set | | 312 | * The main scope is SCOPE_GLOBAL, which contains the variables that are set |
313 | * in the makefiles. SCOPE_INTERNAL acts as a fallback for SCOPE_GLOBAL and | | 313 | * in the makefiles. SCOPE_INTERNAL acts as a fallback for SCOPE_GLOBAL and |
314 | * contains some internal make variables. These internal variables can thus | | 314 | * contains some internal make variables. These internal variables can thus |
315 | * be overridden, they can also be restored by undefining the overriding | | 315 | * be overridden, they can also be restored by undefining the overriding |
316 | * variable. | | 316 | * variable. |
317 | * | | 317 | * |
318 | * SCOPE_CMDLINE contains variables from the command line arguments. These | | 318 | * SCOPE_CMDLINE contains variables from the command line arguments. These |
319 | * override variables from SCOPE_GLOBAL. | | 319 | * override variables from SCOPE_GLOBAL. |
320 | * | | 320 | * |
321 | * There is no scope for environment variables, these are generated on-the-fly | | 321 | * There is no scope for environment variables, these are generated on-the-fly |
322 | * whenever they are referenced. If there were such a scope, each change to | | 322 | * whenever they are referenced. If there were such a scope, each change to |
323 | * environment variables would have to be reflected in that scope, which may | | 323 | * environment variables would have to be reflected in that scope, which may |
324 | * be simpler or more complex than the current implementation. | | 324 | * be simpler or more complex than the current implementation. |
325 | * | | 325 | * |
326 | * Each target has its own scope, containing the 7 target-local variables | | 326 | * Each target has its own scope, containing the 7 target-local variables |
327 | * .TARGET, .ALLSRC, etc. No other variables are in these scopes. | | 327 | * .TARGET, .ALLSRC, etc. No other variables are in these scopes. |
328 | */ | | 328 | */ |
329 | | | 329 | |
330 | GNode *SCOPE_CMDLINE; | | 330 | GNode *SCOPE_CMDLINE; |
331 | GNode *SCOPE_GLOBAL; | | 331 | GNode *SCOPE_GLOBAL; |
332 | GNode *SCOPE_INTERNAL; | | 332 | GNode *SCOPE_INTERNAL; |
333 | | | 333 | |
334 | ENUM_FLAGS_RTTI_6(VarFlags, | | 334 | ENUM_FLAGS_RTTI_6(VarFlags, |
335 | VFL_IN_USE, VFL_FROM_ENV, | | 335 | VFL_IN_USE, VFL_FROM_ENV, |
336 | VFL_EXPORTED, VFL_REEXPORT, VFL_FROM_CMD, VFL_READONLY); | | 336 | VFL_EXPORTED, VFL_REEXPORT, VFL_FROM_CMD, VFL_READONLY); |
337 | | | 337 | |
338 | static VarExportedMode var_exportedVars = VAR_EXPORTED_NONE; | | 338 | static VarExportedMode var_exportedVars = VAR_EXPORTED_NONE; |
339 | | | 339 | |
340 | | | 340 | |
341 | static Var * | | 341 | static Var * |
342 | VarNew(FStr name, const char *value, VarFlags flags) | | 342 | VarNew(FStr name, const char *value, VarFlags flags) |
343 | { | | 343 | { |
344 | size_t value_len = strlen(value); | | 344 | size_t value_len = strlen(value); |
345 | Var *var = bmake_malloc(sizeof *var); | | 345 | Var *var = bmake_malloc(sizeof *var); |
346 | var->name = name; | | 346 | var->name = name; |
347 | Buf_InitSize(&var->val, value_len + 1); | | 347 | Buf_InitSize(&var->val, value_len + 1); |
348 | Buf_AddBytes(&var->val, value, value_len); | | 348 | Buf_AddBytes(&var->val, value, value_len); |
349 | var->flags = flags; | | 349 | var->flags = flags; |
350 | return var; | | 350 | return var; |
351 | } | | 351 | } |
352 | | | 352 | |
353 | static const char * | | 353 | static const char * |
354 | CanonicalVarname(const char *name) | | 354 | CanonicalVarname(const char *name) |
355 | { | | 355 | { |
356 | if (*name == '.' && ch_isupper(name[1])) { | | 356 | if (*name == '.' && ch_isupper(name[1])) { |
357 | switch (name[1]) { | | 357 | switch (name[1]) { |
358 | case 'A': | | 358 | case 'A': |
359 | if (strcmp(name, ".ALLSRC") == 0) | | 359 | if (strcmp(name, ".ALLSRC") == 0) |
360 | name = ALLSRC; | | 360 | name = ALLSRC; |
361 | if (strcmp(name, ".ARCHIVE") == 0) | | 361 | if (strcmp(name, ".ARCHIVE") == 0) |
362 | name = ARCHIVE; | | 362 | name = ARCHIVE; |
363 | break; | | 363 | break; |
364 | case 'I': | | 364 | case 'I': |
365 | if (strcmp(name, ".IMPSRC") == 0) | | 365 | if (strcmp(name, ".IMPSRC") == 0) |
366 | name = IMPSRC; | | 366 | name = IMPSRC; |
367 | break; | | 367 | break; |
368 | case 'M': | | 368 | case 'M': |
369 | if (strcmp(name, ".MEMBER") == 0) | | 369 | if (strcmp(name, ".MEMBER") == 0) |
370 | name = MEMBER; | | 370 | name = MEMBER; |
371 | break; | | 371 | break; |
372 | case 'O': | | 372 | case 'O': |
373 | if (strcmp(name, ".OODATE") == 0) | | 373 | if (strcmp(name, ".OODATE") == 0) |
374 | name = OODATE; | | 374 | name = OODATE; |
375 | break; | | 375 | break; |
376 | case 'P': | | 376 | case 'P': |
377 | if (strcmp(name, ".PREFIX") == 0) | | 377 | if (strcmp(name, ".PREFIX") == 0) |
378 | name = PREFIX; | | 378 | name = PREFIX; |
379 | break; | | 379 | break; |
380 | case 'S': | | 380 | case 'S': |
381 | if (strcmp(name, ".SHELL") == 0) { | | 381 | if (strcmp(name, ".SHELL") == 0) { |
382 | if (shellPath == NULL) | | 382 | if (shellPath == NULL) |
383 | Shell_Init(); | | 383 | Shell_Init(); |
384 | } | | 384 | } |
385 | break; | | 385 | break; |
386 | case 'T': | | 386 | case 'T': |
387 | if (strcmp(name, ".TARGET") == 0) | | 387 | if (strcmp(name, ".TARGET") == 0) |
388 | name = TARGET; | | 388 | name = TARGET; |
389 | break; | | 389 | break; |
390 | } | | 390 | } |
391 | } | | 391 | } |
392 | | | 392 | |
393 | /* GNU make has an additional alias $^ == ${.ALLSRC}. */ | | 393 | /* GNU make has an additional alias $^ == ${.ALLSRC}. */ |
394 | | | 394 | |
395 | return name; | | 395 | return name; |
396 | } | | 396 | } |
397 | | | 397 | |
398 | static Var * | | 398 | static Var * |
399 | GNode_FindVar(GNode *scope, const char *varname, unsigned int hash) | | 399 | GNode_FindVar(GNode *scope, const char *varname, unsigned int hash) |
400 | { | | 400 | { |
401 | return HashTable_FindValueHash(&scope->vars, varname, hash); | | 401 | return HashTable_FindValueHash(&scope->vars, varname, hash); |
402 | } | | 402 | } |
403 | | | 403 | |
404 | /* | | 404 | /* |
405 | * Find the variable in the scope, and maybe in other scopes as well. | | 405 | * Find the variable in the scope, and maybe in other scopes as well. |
406 | * | | 406 | * |
407 | * Input: | | 407 | * Input: |
408 | * name name to find, is not expanded any further | | 408 | * name name to find, is not expanded any further |
409 | * scope scope in which to look first | | 409 | * scope scope in which to look first |
410 | * elsewhere TRUE to look in other scopes as well | | 410 | * elsewhere TRUE to look in other scopes as well |
411 | * | | 411 | * |
412 | * Results: | | 412 | * Results: |
413 | * The found variable, or NULL if the variable does not exist. | | 413 | * The found variable, or NULL if the variable does not exist. |
414 | * If the variable is an environment variable, it must be freed using | | 414 | * If the variable is an environment variable, it must be freed using |
415 | * VarFreeEnv after use. | | 415 | * VarFreeEnv after use. |
416 | */ | | 416 | */ |
417 | static Var * | | 417 | static Var * |
418 | VarFind(const char *name, GNode *scope, Boolean elsewhere) | | 418 | VarFind(const char *name, GNode *scope, Boolean elsewhere) |
419 | { | | 419 | { |
420 | Var *var; | | 420 | Var *var; |
421 | unsigned int nameHash; | | 421 | unsigned int nameHash; |
422 | | | 422 | |
423 | /* Replace '.TARGET' with '@', likewise for other local variables. */ | | 423 | /* Replace '.TARGET' with '@', likewise for other local variables. */ |
424 | name = CanonicalVarname(name); | | 424 | name = CanonicalVarname(name); |
425 | nameHash = Hash_Hash(name); | | 425 | nameHash = Hash_Hash(name); |
426 | | | 426 | |
427 | var = GNode_FindVar(scope, name, nameHash); | | 427 | var = GNode_FindVar(scope, name, nameHash); |
428 | if (!elsewhere) | | 428 | if (!elsewhere) |
429 | return var; | | 429 | return var; |
430 | | | 430 | |
431 | if (var == NULL && scope != SCOPE_CMDLINE) | | 431 | if (var == NULL && scope != SCOPE_CMDLINE) |
432 | var = GNode_FindVar(SCOPE_CMDLINE, name, nameHash); | | 432 | var = GNode_FindVar(SCOPE_CMDLINE, name, nameHash); |
433 | | | 433 | |
434 | if (!opts.checkEnvFirst && var == NULL && scope != SCOPE_GLOBAL) { | | 434 | if (!opts.checkEnvFirst && var == NULL && scope != SCOPE_GLOBAL) { |
435 | var = GNode_FindVar(SCOPE_GLOBAL, name, nameHash); | | 435 | var = GNode_FindVar(SCOPE_GLOBAL, name, nameHash); |
436 | if (var == NULL && scope != SCOPE_INTERNAL) { | | 436 | if (var == NULL && scope != SCOPE_INTERNAL) { |
437 | /* SCOPE_INTERNAL is subordinate to SCOPE_GLOBAL */ | | 437 | /* SCOPE_INTERNAL is subordinate to SCOPE_GLOBAL */ |
438 | var = GNode_FindVar(SCOPE_INTERNAL, name, nameHash); | | 438 | var = GNode_FindVar(SCOPE_INTERNAL, name, nameHash); |
439 | } | | 439 | } |
440 | } | | 440 | } |
441 | | | 441 | |
442 | if (var == NULL) { | | 442 | if (var == NULL) { |
443 | char *env; | | 443 | char *env; |
444 | | | 444 | |
445 | if ((env = getenv(name)) != NULL) { | | 445 | if ((env = getenv(name)) != NULL) { |
446 | char *varname = bmake_strdup(name); | | 446 | char *varname = bmake_strdup(name); |
447 | return VarNew(FStr_InitOwn(varname), env, VFL_FROM_ENV); | | 447 | return VarNew(FStr_InitOwn(varname), env, VFL_FROM_ENV); |
448 | } | | 448 | } |
449 | | | 449 | |
450 | if (opts.checkEnvFirst && scope != SCOPE_GLOBAL) { | | 450 | if (opts.checkEnvFirst && scope != SCOPE_GLOBAL) { |
451 | var = GNode_FindVar(SCOPE_GLOBAL, name, nameHash); | | 451 | var = GNode_FindVar(SCOPE_GLOBAL, name, nameHash); |
452 | if (var == NULL && scope != SCOPE_INTERNAL) | | 452 | if (var == NULL && scope != SCOPE_INTERNAL) |
453 | var = GNode_FindVar(SCOPE_INTERNAL, name, | | 453 | var = GNode_FindVar(SCOPE_INTERNAL, name, |
454 | nameHash); | | 454 | nameHash); |
455 | return var; | | 455 | return var; |
456 | } | | 456 | } |
457 | | | 457 | |
458 | return NULL; | | 458 | return NULL; |
459 | } | | 459 | } |
460 | | | 460 | |
461 | return var; | | 461 | return var; |
462 | } | | 462 | } |
463 | | | 463 | |
464 | /* | | 464 | /* If the variable is an environment variable, free it, including its value. */ |
465 | * If the variable is an environment variable, free it, including its value. | | | |
466 | * | | | |
467 | * Results: | | | |
468 | * TRUE if it was an environment variable, | | | |
469 | * FALSE if it is still a regular variable. | | | |
470 | */ | | | |
471 | static void | | 465 | static void |
472 | VarFreeEnv(Var *v) | | 466 | VarFreeEnv(Var *v) |
473 | { | | 467 | { |
474 | if (!(v->flags & VFL_FROM_ENV)) | | 468 | if (!(v->flags & VFL_FROM_ENV)) |
475 | return; | | 469 | return; |
476 | | | 470 | |
477 | FStr_Done(&v->name); | | 471 | FStr_Done(&v->name); |
478 | Buf_Done(&v->val); | | 472 | Buf_Done(&v->val); |
479 | free(v); | | 473 | free(v); |
480 | } | | 474 | } |
481 | | | 475 | |
482 | /* Add a new variable of the given name and value to the given scope. */ | | 476 | /* Add a new variable of the given name and value to the given scope. */ |
483 | static Var * | | 477 | static Var * |
484 | VarAdd(const char *name, const char *value, GNode *scope, VarSetFlags flags) | | 478 | VarAdd(const char *name, const char *value, GNode *scope, VarSetFlags flags) |
485 | { | | 479 | { |
486 | HashEntry *he = HashTable_CreateEntry(&scope->vars, name, NULL); | | 480 | HashEntry *he = HashTable_CreateEntry(&scope->vars, name, NULL); |
487 | Var *v = VarNew(FStr_InitRefer(/* aliased to */ he->key), value, | | 481 | Var *v = VarNew(FStr_InitRefer(/* aliased to */ he->key), value, |
488 | flags & VAR_SET_READONLY ? VFL_READONLY : VFL_NONE); | | 482 | flags & VAR_SET_READONLY ? VFL_READONLY : VFL_NONE); |
489 | HashEntry_Set(he, v); | | 483 | HashEntry_Set(he, v); |
490 | DEBUG3(VAR, "%s:%s = %s\n", scope->name, name, value); | | 484 | DEBUG3(VAR, "%s:%s = %s\n", scope->name, name, value); |
491 | return v; | | 485 | return v; |
492 | } | | 486 | } |
493 | | | 487 | |
494 | /* | | 488 | /* |
495 | * Remove a variable from a scope, freeing all related memory as well. | | 489 | * Remove a variable from a scope, freeing all related memory as well. |
496 | * The variable name is kept as-is, it is not expanded. | | 490 | * The variable name is kept as-is, it is not expanded. |
497 | */ | | 491 | */ |
498 | void | | 492 | void |
499 | Var_Delete(GNode *scope, const char *varname) | | 493 | Var_Delete(GNode *scope, const char *varname) |
500 | { | | 494 | { |
501 | HashEntry *he = HashTable_FindEntry(&scope->vars, varname); | | 495 | HashEntry *he = HashTable_FindEntry(&scope->vars, varname); |
502 | Var *v; | | 496 | Var *v; |
503 | | | 497 | |
504 | if (he == NULL) { | | 498 | if (he == NULL) { |
505 | DEBUG2(VAR, "%s:delete %s (not found)\n", scope->name, varname); | | 499 | DEBUG2(VAR, "%s:delete %s (not found)\n", scope->name, varname); |
506 | return; | | 500 | return; |
507 | } | | 501 | } |
508 | | | 502 | |
509 | DEBUG2(VAR, "%s:delete %s\n", scope->name, varname); | | 503 | DEBUG2(VAR, "%s:delete %s\n", scope->name, varname); |
510 | v = he->value; | | 504 | v = he->value; |
511 | if (v->flags & VFL_EXPORTED) | | 505 | if (v->flags & VFL_EXPORTED) |
512 | unsetenv(v->name.str); | | 506 | unsetenv(v->name.str); |
513 | if (strcmp(v->name.str, MAKE_EXPORTED) == 0) | | 507 | if (strcmp(v->name.str, MAKE_EXPORTED) == 0) |
514 | var_exportedVars = VAR_EXPORTED_NONE; | | 508 | var_exportedVars = VAR_EXPORTED_NONE; |
515 | assert(v->name.freeIt == NULL); | | 509 | assert(v->name.freeIt == NULL); |
516 | HashTable_DeleteEntry(&scope->vars, he); | | 510 | HashTable_DeleteEntry(&scope->vars, he); |
517 | Buf_Done(&v->val); | | 511 | Buf_Done(&v->val); |
518 | free(v); | | 512 | free(v); |
519 | } | | 513 | } |
520 | | | 514 | |
521 | /* | | 515 | /* |
522 | * Remove a variable from a scope, freeing all related memory as well. | | 516 | * Remove a variable from a scope, freeing all related memory as well. |
523 | * The variable name is expanded once. | | 517 | * The variable name is expanded once. |
524 | */ | | 518 | */ |
525 | void | | 519 | void |
526 | Var_DeleteExpand(GNode *scope, const char *name) | | 520 | Var_DeleteExpand(GNode *scope, const char *name) |
527 | { | | 521 | { |
528 | FStr varname = FStr_InitRefer(name); | | 522 | FStr varname = FStr_InitRefer(name); |
529 | | | 523 | |
530 | if (strchr(varname.str, '$') != NULL) { | | 524 | if (strchr(varname.str, '$') != NULL) { |
531 | char *expanded; | | 525 | char *expanded; |
532 | (void)Var_Subst(varname.str, SCOPE_GLOBAL, VARE_WANTRES, | | 526 | (void)Var_Subst(varname.str, SCOPE_GLOBAL, VARE_WANTRES, |
533 | &expanded); | | 527 | &expanded); |
534 | /* TODO: handle errors */ | | 528 | /* TODO: handle errors */ |
535 | varname = FStr_InitOwn(expanded); | | 529 | varname = FStr_InitOwn(expanded); |
536 | } | | 530 | } |
537 | | | 531 | |
538 | Var_Delete(scope, varname.str); | | 532 | Var_Delete(scope, varname.str); |
539 | FStr_Done(&varname); | | 533 | FStr_Done(&varname); |
540 | } | | 534 | } |
541 | | | 535 | |
542 | /* | | 536 | /* |
543 | * Undefine one or more variables from the global scope. | | 537 | * Undefine one or more variables from the global scope. |
544 | * The argument is expanded exactly once and then split into words. | | 538 | * The argument is expanded exactly once and then split into words. |
545 | */ | | 539 | */ |
546 | void | | 540 | void |
547 | Var_Undef(const char *arg) | | 541 | Var_Undef(const char *arg) |
548 | { | | 542 | { |
549 | VarParseResult vpr; | | 543 | VarParseResult vpr; |
550 | char *expanded; | | 544 | char *expanded; |
551 | Words varnames; | | 545 | Words varnames; |
552 | size_t i; | | 546 | size_t i; |
553 | | | 547 | |
554 | if (arg[0] == '\0') { | | 548 | if (arg[0] == '\0') { |
555 | Parse_Error(PARSE_FATAL, | | 549 | Parse_Error(PARSE_FATAL, |
556 | "The .undef directive requires an argument"); | | 550 | "The .undef directive requires an argument"); |
557 | return; | | 551 | return; |
558 | } | | 552 | } |
559 | | | 553 | |
560 | vpr = Var_Subst(arg, SCOPE_GLOBAL, VARE_WANTRES, &expanded); | | 554 | vpr = Var_Subst(arg, SCOPE_GLOBAL, VARE_WANTRES, &expanded); |
561 | if (vpr != VPR_OK) { | | 555 | if (vpr != VPR_OK) { |
562 | Parse_Error(PARSE_FATAL, | | 556 | Parse_Error(PARSE_FATAL, |
563 | "Error in variable names to be undefined"); | | 557 | "Error in variable names to be undefined"); |
564 | return; | | 558 | return; |
565 | } | | 559 | } |
566 | | | 560 | |
567 | varnames = Str_Words(expanded, FALSE); | | 561 | varnames = Str_Words(expanded, FALSE); |
568 | if (varnames.len == 1 && varnames.words[0][0] == '\0') | | 562 | if (varnames.len == 1 && varnames.words[0][0] == '\0') |
569 | varnames.len = 0; | | 563 | varnames.len = 0; |
570 | | | 564 | |
571 | for (i = 0; i < varnames.len; i++) { | | 565 | for (i = 0; i < varnames.len; i++) { |
572 | const char *varname = varnames.words[i]; | | 566 | const char *varname = varnames.words[i]; |
573 | Global_Delete(varname); | | 567 | Global_Delete(varname); |
574 | } | | 568 | } |
575 | | | 569 | |
576 | Words_Free(varnames); | | 570 | Words_Free(varnames); |
577 | free(expanded); | | 571 | free(expanded); |
578 | } | | 572 | } |
579 | | | 573 | |
580 | static Boolean | | 574 | static Boolean |
581 | MayExport(const char *name) | | 575 | MayExport(const char *name) |
582 | { | | 576 | { |
583 | if (name[0] == '.') | | 577 | if (name[0] == '.') |
584 | return FALSE; /* skip internals */ | | 578 | return FALSE; /* skip internals */ |
585 | if (name[0] == '-') | | 579 | if (name[0] == '-') |
586 | return FALSE; /* skip misnamed variables */ | | 580 | return FALSE; /* skip misnamed variables */ |
587 | if (name[1] == '\0') { | | 581 | if (name[1] == '\0') { |
588 | /* | | 582 | /* |
589 | * A single char. | | 583 | * A single char. |
590 | * If it is one of the variables that should only appear in | | 584 | * If it is one of the variables that should only appear in |
591 | * local scope, skip it, else we can get Var_Subst | | 585 | * local scope, skip it, else we can get Var_Subst |
592 | * into a loop. | | 586 | * into a loop. |
593 | */ | | 587 | */ |
594 | switch (name[0]) { | | 588 | switch (name[0]) { |
595 | case '@': | | 589 | case '@': |
596 | case '%': | | 590 | case '%': |
597 | case '*': | | 591 | case '*': |
598 | case '!': | | 592 | case '!': |
599 | return FALSE; | | 593 | return FALSE; |
600 | } | | 594 | } |
601 | } | | 595 | } |
602 | return TRUE; | | 596 | return TRUE; |
603 | } | | 597 | } |
604 | | | 598 | |
605 | static Boolean | | 599 | static Boolean |
606 | ExportVarEnv(Var *v) | | 600 | ExportVarEnv(Var *v) |
607 | { | | 601 | { |
608 | const char *name = v->name.str; | | 602 | const char *name = v->name.str; |
609 | char *val = v->val.data; | | 603 | char *val = v->val.data; |
610 | char *expr; | | 604 | char *expr; |
611 | | | 605 | |
612 | if ((v->flags & VFL_EXPORTED) && !(v->flags & VFL_REEXPORT)) | | 606 | if ((v->flags & VFL_EXPORTED) && !(v->flags & VFL_REEXPORT)) |
613 | return FALSE; /* nothing to do */ | | 607 | return FALSE; /* nothing to do */ |
614 | | | 608 | |
615 | if (strchr(val, '$') == NULL) { | | 609 | if (strchr(val, '$') == NULL) { |
616 | if (!(v->flags & VFL_EXPORTED)) | | 610 | if (!(v->flags & VFL_EXPORTED)) |
617 | setenv(name, val, 1); | | 611 | setenv(name, val, 1); |
618 | return TRUE; | | 612 | return TRUE; |
619 | } | | 613 | } |
620 | | | 614 | |
621 | if (v->flags & VFL_IN_USE) { | | 615 | if (v->flags & VFL_IN_USE) { |
622 | /* | | 616 | /* |
623 | * We recursed while exporting in a child. | | 617 | * We recursed while exporting in a child. |
624 | * This isn't going to end well, just skip it. | | 618 | * This isn't going to end well, just skip it. |
625 | */ | | 619 | */ |
626 | return FALSE; | | 620 | return FALSE; |
627 | } | | 621 | } |
628 | | | 622 | |
629 | /* XXX: name is injected without escaping it */ | | 623 | /* XXX: name is injected without escaping it */ |
630 | expr = str_concat3("${", name, "}"); | | 624 | expr = str_concat3("${", name, "}"); |
631 | (void)Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES, &val); | | 625 | (void)Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES, &val); |
632 | /* TODO: handle errors */ | | 626 | /* TODO: handle errors */ |
633 | setenv(name, val, 1); | | 627 | setenv(name, val, 1); |
634 | free(val); | | 628 | free(val); |
635 | free(expr); | | 629 | free(expr); |
636 | return TRUE; | | 630 | return TRUE; |
637 | } | | 631 | } |
638 | | | 632 | |
639 | static Boolean | | 633 | static Boolean |
640 | ExportVarPlain(Var *v) | | 634 | ExportVarPlain(Var *v) |
641 | { | | 635 | { |
642 | if (strchr(v->val.data, '$') == NULL) { | | 636 | if (strchr(v->val.data, '$') == NULL) { |
643 | setenv(v->name.str, v->val.data, 1); | | 637 | setenv(v->name.str, v->val.data, 1); |
644 | v->flags |= VFL_EXPORTED; | | 638 | v->flags |= VFL_EXPORTED; |
645 | v->flags &= ~(unsigned)VFL_REEXPORT; | | 639 | v->flags &= ~(unsigned)VFL_REEXPORT; |
646 | return TRUE; | | 640 | return TRUE; |
647 | } | | 641 | } |
648 | | | 642 | |
649 | /* | | 643 | /* |
650 | * Flag the variable as something we need to re-export. | | 644 | * Flag the variable as something we need to re-export. |
651 | * No point actually exporting it now though, | | 645 | * No point actually exporting it now though, |
652 | * the child process can do it at the last minute. | | 646 | * the child process can do it at the last minute. |
653 | * Avoid calling setenv more often than necessary since it can leak. | | 647 | * Avoid calling setenv more often than necessary since it can leak. |
654 | */ | | 648 | */ |
655 | v->flags |= VFL_EXPORTED | VFL_REEXPORT; | | 649 | v->flags |= VFL_EXPORTED | VFL_REEXPORT; |
656 | return TRUE; | | 650 | return TRUE; |
657 | } | | 651 | } |
658 | | | 652 | |
659 | static Boolean | | 653 | static Boolean |
660 | ExportVarLiteral(Var *v) | | 654 | ExportVarLiteral(Var *v) |
661 | { | | 655 | { |
662 | if ((v->flags & VFL_EXPORTED) && !(v->flags & VFL_REEXPORT)) | | 656 | if ((v->flags & VFL_EXPORTED) && !(v->flags & VFL_REEXPORT)) |
663 | return FALSE; | | 657 | return FALSE; |
664 | | | 658 | |
665 | if (!(v->flags & VFL_EXPORTED)) | | 659 | if (!(v->flags & VFL_EXPORTED)) |
666 | setenv(v->name.str, v->val.data, 1); | | 660 | setenv(v->name.str, v->val.data, 1); |
667 | | | 661 | |
668 | return TRUE; | | 662 | return TRUE; |
669 | } | | 663 | } |
670 | | | 664 | |
671 | /* | | 665 | /* |
672 | * Mark a single variable to be exported later for subprocesses. | | 666 | * Mark a single variable to be exported later for subprocesses. |
673 | * | | 667 | * |
674 | * Internal variables (those starting with '.') are not exported. | | 668 | * Internal variables (those starting with '.') are not exported. |
675 | */ | | 669 | */ |
676 | static Boolean | | 670 | static Boolean |
677 | ExportVar(const char *name, VarExportMode mode) | | 671 | ExportVar(const char *name, VarExportMode mode) |
678 | { | | 672 | { |
679 | Var *v; | | 673 | Var *v; |
680 | | | 674 | |
681 | if (!MayExport(name)) | | 675 | if (!MayExport(name)) |
682 | return FALSE; | | 676 | return FALSE; |
683 | | | 677 | |
684 | v = VarFind(name, SCOPE_GLOBAL, FALSE); | | 678 | v = VarFind(name, SCOPE_GLOBAL, FALSE); |
685 | if (v == NULL) | | 679 | if (v == NULL) |
686 | return FALSE; | | 680 | return FALSE; |
687 | | | 681 | |
688 | if (mode == VEM_ENV) | | 682 | if (mode == VEM_ENV) |
689 | return ExportVarEnv(v); | | 683 | return ExportVarEnv(v); |
690 | else if (mode == VEM_PLAIN) | | 684 | else if (mode == VEM_PLAIN) |
691 | return ExportVarPlain(v); | | 685 | return ExportVarPlain(v); |
692 | else | | 686 | else |
693 | return ExportVarLiteral(v); | | 687 | return ExportVarLiteral(v); |
694 | } | | 688 | } |
695 | | | 689 | |
696 | /* | | 690 | /* |
697 | * Actually export the variables that have been marked as needing to be | | 691 | * Actually export the variables that have been marked as needing to be |
698 | * re-exported. | | 692 | * re-exported. |
699 | */ | | 693 | */ |
700 | void | | 694 | void |
701 | Var_ReexportVars(void) | | 695 | Var_ReexportVars(void) |
702 | { | | 696 | { |
703 | char *xvarnames; | | 697 | char *xvarnames; |
704 | | | 698 | |
705 | /* | | 699 | /* |
706 | * Several make implementations support this sort of mechanism for | | 700 | * Several make implementations support this sort of mechanism for |
707 | * tracking recursion - but each uses a different name. | | 701 | * tracking recursion - but each uses a different name. |
708 | * We allow the makefiles to update MAKELEVEL and ensure | | 702 | * We allow the makefiles to update MAKELEVEL and ensure |
709 | * children see a correctly incremented value. | | 703 | * children see a correctly incremented value. |
710 | */ | | 704 | */ |
711 | char tmp[21]; | | 705 | char tmp[21]; |
712 | snprintf(tmp, sizeof tmp, "%d", makelevel + 1); | | 706 | snprintf(tmp, sizeof tmp, "%d", makelevel + 1); |
713 | setenv(MAKE_LEVEL_ENV, tmp, 1); | | 707 | setenv(MAKE_LEVEL_ENV, tmp, 1); |
714 | | | 708 | |
715 | if (var_exportedVars == VAR_EXPORTED_NONE) | | 709 | if (var_exportedVars == VAR_EXPORTED_NONE) |
716 | return; | | 710 | return; |
717 | | | 711 | |
718 | if (var_exportedVars == VAR_EXPORTED_ALL) { | | 712 | if (var_exportedVars == VAR_EXPORTED_ALL) { |
719 | HashIter hi; | | 713 | HashIter hi; |
720 | | | 714 | |
721 | /* Ouch! Exporting all variables at once is crazy. */ | | 715 | /* Ouch! Exporting all variables at once is crazy. */ |
722 | HashIter_Init(&hi, &SCOPE_GLOBAL->vars); | | 716 | HashIter_Init(&hi, &SCOPE_GLOBAL->vars); |
723 | while (HashIter_Next(&hi) != NULL) { | | 717 | while (HashIter_Next(&hi) != NULL) { |
724 | Var *var = hi.entry->value; | | 718 | Var *var = hi.entry->value; |
725 | ExportVar(var->name.str, VEM_ENV); | | 719 | ExportVar(var->name.str, VEM_ENV); |
726 | } | | 720 | } |
727 | return; | | 721 | return; |
728 | } | | 722 | } |
729 | | | 723 | |
730 | (void)Var_Subst("${" MAKE_EXPORTED ":O:u}", SCOPE_GLOBAL, VARE_WANTRES, | | 724 | (void)Var_Subst("${" MAKE_EXPORTED ":O:u}", SCOPE_GLOBAL, VARE_WANTRES, |
731 | &xvarnames); | | 725 | &xvarnames); |
732 | /* TODO: handle errors */ | | 726 | /* TODO: handle errors */ |
733 | if (xvarnames[0] != '\0') { | | 727 | if (xvarnames[0] != '\0') { |
734 | Words varnames = Str_Words(xvarnames, FALSE); | | 728 | Words varnames = Str_Words(xvarnames, FALSE); |
735 | size_t i; | | 729 | size_t i; |
736 | | | 730 | |
737 | for (i = 0; i < varnames.len; i++) | | 731 | for (i = 0; i < varnames.len; i++) |
738 | ExportVar(varnames.words[i], VEM_ENV); | | 732 | ExportVar(varnames.words[i], VEM_ENV); |
739 | Words_Free(varnames); | | 733 | Words_Free(varnames); |
740 | } | | 734 | } |
741 | free(xvarnames); | | 735 | free(xvarnames); |
742 | } | | 736 | } |
743 | | | 737 | |
744 | static void | | 738 | static void |
745 | ExportVars(const char *varnames, Boolean isExport, VarExportMode mode) | | 739 | ExportVars(const char *varnames, Boolean isExport, VarExportMode mode) |
746 | /* TODO: try to combine the parameters 'isExport' and 'mode'. */ | | 740 | /* TODO: try to combine the parameters 'isExport' and 'mode'. */ |
747 | { | | 741 | { |
748 | Words words = Str_Words(varnames, FALSE); | | 742 | Words words = Str_Words(varnames, FALSE); |
749 | size_t i; | | 743 | size_t i; |
750 | | | 744 | |
751 | if (words.len == 1 && words.words[0][0] == '\0') | | 745 | if (words.len == 1 && words.words[0][0] == '\0') |
752 | words.len = 0; | | 746 | words.len = 0; |
753 | | | 747 | |
754 | for (i = 0; i < words.len; i++) { | | 748 | for (i = 0; i < words.len; i++) { |
755 | const char *varname = words.words[i]; | | 749 | const char *varname = words.words[i]; |
756 | if (!ExportVar(varname, mode)) | | 750 | if (!ExportVar(varname, mode)) |
757 | continue; | | 751 | continue; |
758 | | | 752 | |
759 | if (var_exportedVars == VAR_EXPORTED_NONE) | | 753 | if (var_exportedVars == VAR_EXPORTED_NONE) |
760 | var_exportedVars = VAR_EXPORTED_SOME; | | 754 | var_exportedVars = VAR_EXPORTED_SOME; |
761 | | | 755 | |
762 | if (isExport && mode == VEM_PLAIN) | | 756 | if (isExport && mode == VEM_PLAIN) |
763 | Global_Append(MAKE_EXPORTED, varname); | | 757 | Global_Append(MAKE_EXPORTED, varname); |
764 | } | | 758 | } |
765 | Words_Free(words); | | 759 | Words_Free(words); |
766 | } | | 760 | } |
767 | | | 761 | |
768 | static void | | 762 | static void |
769 | ExportVarsExpand(const char *uvarnames, Boolean isExport, VarExportMode mode) | | 763 | ExportVarsExpand(const char *uvarnames, Boolean isExport, VarExportMode mode) |
770 | { | | 764 | { |
771 | char *xvarnames; | | 765 | char *xvarnames; |
772 | | | 766 | |
773 | (void)Var_Subst(uvarnames, SCOPE_GLOBAL, VARE_WANTRES, &xvarnames); | | 767 | (void)Var_Subst(uvarnames, SCOPE_GLOBAL, VARE_WANTRES, &xvarnames); |
774 | /* TODO: handle errors */ | | 768 | /* TODO: handle errors */ |
775 | ExportVars(xvarnames, isExport, mode); | | 769 | ExportVars(xvarnames, isExport, mode); |
776 | free(xvarnames); | | 770 | free(xvarnames); |
777 | } | | 771 | } |
778 | | | 772 | |
779 | /* Export the named variables, or all variables. */ | | 773 | /* Export the named variables, or all variables. */ |
780 | void | | 774 | void |
781 | Var_Export(VarExportMode mode, const char *varnames) | | 775 | Var_Export(VarExportMode mode, const char *varnames) |
782 | { | | 776 | { |
783 | if (mode == VEM_PLAIN && varnames[0] == '\0') { | | 777 | if (mode == VEM_PLAIN && varnames[0] == '\0') { |
784 | var_exportedVars = VAR_EXPORTED_ALL; /* use with caution! */ | | 778 | var_exportedVars = VAR_EXPORTED_ALL; /* use with caution! */ |
785 | return; | | 779 | return; |
786 | } | | 780 | } |
787 | | | 781 | |
788 | ExportVarsExpand(varnames, TRUE, mode); | | 782 | ExportVarsExpand(varnames, TRUE, mode); |
789 | } | | 783 | } |
790 | | | 784 | |
791 | void | | 785 | void |
792 | Var_ExportVars(const char *varnames) | | 786 | Var_ExportVars(const char *varnames) |
793 | { | | 787 | { |
794 | ExportVarsExpand(varnames, FALSE, VEM_PLAIN); | | 788 | ExportVarsExpand(varnames, FALSE, VEM_PLAIN); |
795 | } | | 789 | } |
796 | | | 790 | |
797 | | | 791 | |
798 | extern char **environ; | | 792 | extern char **environ; |
799 | | | 793 | |
800 | static void | | 794 | static void |
801 | ClearEnv(void) | | 795 | ClearEnv(void) |
802 | { | | 796 | { |
803 | const char *cp; | | 797 | const char *cp; |
804 | char **newenv; | | 798 | char **newenv; |
805 | | | 799 | |
806 | cp = getenv(MAKE_LEVEL_ENV); /* we should preserve this */ | | 800 | cp = getenv(MAKE_LEVEL_ENV); /* we should preserve this */ |
807 | if (environ == savedEnv) { | | 801 | if (environ == savedEnv) { |
808 | /* we have been here before! */ | | 802 | /* we have been here before! */ |
809 | newenv = bmake_realloc(environ, 2 * sizeof(char *)); | | 803 | newenv = bmake_realloc(environ, 2 * sizeof(char *)); |
810 | } else { | | 804 | } else { |
811 | if (savedEnv != NULL) { | | 805 | if (savedEnv != NULL) { |
812 | free(savedEnv); | | 806 | free(savedEnv); |
813 | savedEnv = NULL; | | 807 | savedEnv = NULL; |
814 | } | | 808 | } |
815 | newenv = bmake_malloc(2 * sizeof(char *)); | | 809 | newenv = bmake_malloc(2 * sizeof(char *)); |
816 | } | | 810 | } |
817 | | | 811 | |
818 | /* Note: we cannot safely free() the original environ. */ | | 812 | /* Note: we cannot safely free() the original environ. */ |
819 | environ = savedEnv = newenv; | | 813 | environ = savedEnv = newenv; |
820 | newenv[0] = NULL; | | 814 | newenv[0] = NULL; |
821 | newenv[1] = NULL; | | 815 | newenv[1] = NULL; |
822 | if (cp != NULL && *cp != '\0') | | 816 | if (cp != NULL && *cp != '\0') |
823 | setenv(MAKE_LEVEL_ENV, cp, 1); | | 817 | setenv(MAKE_LEVEL_ENV, cp, 1); |
824 | } | | 818 | } |
825 | | | 819 | |
826 | static void | | 820 | static void |
827 | GetVarnamesToUnexport(Boolean isEnv, const char *arg, | | 821 | GetVarnamesToUnexport(Boolean isEnv, const char *arg, |
828 | FStr *out_varnames, UnexportWhat *out_what) | | 822 | FStr *out_varnames, UnexportWhat *out_what) |
829 | { | | 823 | { |
830 | UnexportWhat what; | | 824 | UnexportWhat what; |
831 | FStr varnames = FStr_InitRefer(""); | | 825 | FStr varnames = FStr_InitRefer(""); |
832 | | | 826 | |
833 | if (isEnv) { | | 827 | if (isEnv) { |
834 | if (arg[0] != '\0') { | | 828 | if (arg[0] != '\0') { |
835 | Parse_Error(PARSE_FATAL, | | 829 | Parse_Error(PARSE_FATAL, |
836 | "The directive .unexport-env does not take " | | 830 | "The directive .unexport-env does not take " |
837 | "arguments"); | | 831 | "arguments"); |
838 | /* continue anyway */ | | 832 | /* continue anyway */ |
839 | } | | 833 | } |
840 | what = UNEXPORT_ENV; | | 834 | what = UNEXPORT_ENV; |
841 | | | 835 | |
842 | } else { | | 836 | } else { |
843 | what = arg[0] != '\0' ? UNEXPORT_NAMED : UNEXPORT_ALL; | | 837 | what = arg[0] != '\0' ? UNEXPORT_NAMED : UNEXPORT_ALL; |
844 | if (what == UNEXPORT_NAMED) | | 838 | if (what == UNEXPORT_NAMED) |
845 | varnames = FStr_InitRefer(arg); | | 839 | varnames = FStr_InitRefer(arg); |
846 | } | | 840 | } |
847 | | | 841 | |
848 | if (what != UNEXPORT_NAMED) { | | 842 | if (what != UNEXPORT_NAMED) { |
849 | char *expanded; | | 843 | char *expanded; |
850 | /* Using .MAKE.EXPORTED */ | | 844 | /* Using .MAKE.EXPORTED */ |
851 | (void)Var_Subst("${" MAKE_EXPORTED ":O:u}", SCOPE_GLOBAL, | | 845 | (void)Var_Subst("${" MAKE_EXPORTED ":O:u}", SCOPE_GLOBAL, |
852 | VARE_WANTRES, &expanded); | | 846 | VARE_WANTRES, &expanded); |
853 | /* TODO: handle errors */ | | 847 | /* TODO: handle errors */ |
854 | varnames = FStr_InitOwn(expanded); | | 848 | varnames = FStr_InitOwn(expanded); |
855 | } | | 849 | } |
856 | | | 850 | |
857 | *out_varnames = varnames; | | 851 | *out_varnames = varnames; |
858 | *out_what = what; | | 852 | *out_what = what; |
859 | } | | 853 | } |
860 | | | 854 | |
861 | static void | | 855 | static void |
862 | UnexportVar(const char *varname, UnexportWhat what) | | 856 | UnexportVar(const char *varname, UnexportWhat what) |
863 | { | | 857 | { |
864 | Var *v = VarFind(varname, SCOPE_GLOBAL, FALSE); | | 858 | Var *v = VarFind(varname, SCOPE_GLOBAL, FALSE); |
865 | if (v == NULL) { | | 859 | if (v == NULL) { |
866 | DEBUG1(VAR, "Not unexporting \"%s\" (not found)\n", varname); | | 860 | DEBUG1(VAR, "Not unexporting \"%s\" (not found)\n", varname); |
867 | return; | | 861 | return; |
868 | } | | 862 | } |
869 | | | 863 | |
870 | DEBUG1(VAR, "Unexporting \"%s\"\n", varname); | | 864 | DEBUG1(VAR, "Unexporting \"%s\"\n", varname); |
871 | if (what != UNEXPORT_ENV && | | 865 | if (what != UNEXPORT_ENV && |
872 | (v->flags & VFL_EXPORTED) && !(v->flags & VFL_REEXPORT)) | | 866 | (v->flags & VFL_EXPORTED) && !(v->flags & VFL_REEXPORT)) |
873 | unsetenv(v->name.str); | | 867 | unsetenv(v->name.str); |
874 | v->flags &= ~(unsigned)(VFL_EXPORTED | VFL_REEXPORT); | | 868 | v->flags &= ~(unsigned)(VFL_EXPORTED | VFL_REEXPORT); |
875 | | | 869 | |
876 | if (what == UNEXPORT_NAMED) { | | 870 | if (what == UNEXPORT_NAMED) { |
877 | /* Remove the variable names from .MAKE.EXPORTED. */ | | 871 | /* Remove the variable names from .MAKE.EXPORTED. */ |
878 | /* XXX: v->name is injected without escaping it */ | | 872 | /* XXX: v->name is injected without escaping it */ |
879 | char *expr = str_concat3("${" MAKE_EXPORTED ":N", | | 873 | char *expr = str_concat3("${" MAKE_EXPORTED ":N", |
880 | v->name.str, "}"); | | 874 | v->name.str, "}"); |
881 | char *cp; | | 875 | char *cp; |
882 | (void)Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES, &cp); | | 876 | (void)Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES, &cp); |
883 | /* TODO: handle errors */ | | 877 | /* TODO: handle errors */ |
884 | Global_Set(MAKE_EXPORTED, cp); | | 878 | Global_Set(MAKE_EXPORTED, cp); |
885 | free(cp); | | 879 | free(cp); |
886 | free(expr); | | 880 | free(expr); |
887 | } | | 881 | } |
888 | } | | 882 | } |
889 | | | 883 | |
890 | static void | | 884 | static void |
891 | UnexportVars(FStr *varnames, UnexportWhat what) | | 885 | UnexportVars(FStr *varnames, UnexportWhat what) |
892 | { | | 886 | { |
893 | size_t i; | | 887 | size_t i; |
894 | Words words; | | 888 | Words words; |
895 | | | 889 | |
896 | if (what == UNEXPORT_ENV) | | 890 | if (what == UNEXPORT_ENV) |
897 | ClearEnv(); | | 891 | ClearEnv(); |
898 | | | 892 | |
899 | words = Str_Words(varnames->str, FALSE); | | 893 | words = Str_Words(varnames->str, FALSE); |
900 | for (i = 0; i < words.len; i++) { | | 894 | for (i = 0; i < words.len; i++) { |
901 | const char *varname = words.words[i]; | | 895 | const char *varname = words.words[i]; |
902 | UnexportVar(varname, what); | | 896 | UnexportVar(varname, what); |
903 | } | | 897 | } |
904 | Words_Free(words); | | 898 | Words_Free(words); |
905 | | | 899 | |
906 | if (what != UNEXPORT_NAMED) | | 900 | if (what != UNEXPORT_NAMED) |
907 | Global_Delete(MAKE_EXPORTED); | | 901 | Global_Delete(MAKE_EXPORTED); |
908 | } | | 902 | } |
909 | | | 903 | |
910 | /* | | 904 | /* |
911 | * This is called when .unexport[-env] is seen. | | 905 | * This is called when .unexport[-env] is seen. |
912 | * | | 906 | * |
913 | * str must have the form "unexport[-env] varname...". | | 907 | * str must have the form "unexport[-env] varname...". |
914 | */ | | 908 | */ |
915 | void | | 909 | void |
916 | Var_UnExport(Boolean isEnv, const char *arg) | | 910 | Var_UnExport(Boolean isEnv, const char *arg) |
917 | { | | 911 | { |
918 | UnexportWhat what; | | 912 | UnexportWhat what; |
919 | FStr varnames; | | 913 | FStr varnames; |
920 | | | 914 | |
921 | GetVarnamesToUnexport(isEnv, arg, &varnames, &what); | | 915 | GetVarnamesToUnexport(isEnv, arg, &varnames, &what); |
922 | UnexportVars(&varnames, what); | | 916 | UnexportVars(&varnames, what); |
923 | FStr_Done(&varnames); | | 917 | FStr_Done(&varnames); |
924 | } | | 918 | } |
925 | | | 919 | |
926 | /* | | 920 | /* |
927 | * When there is a variable of the same name in the command line scope, the | | 921 | * When there is a variable of the same name in the command line scope, the |
928 | * global variable would not be visible anywhere. Therefore there is no | | 922 | * global variable would not be visible anywhere. Therefore there is no |
929 | * point in setting it at all. | | 923 | * point in setting it at all. |
930 | * | | 924 | * |
931 | * See 'scope == SCOPE_CMDLINE' in Var_SetWithFlags. | | 925 | * See 'scope == SCOPE_CMDLINE' in Var_SetWithFlags. |
932 | */ | | 926 | */ |
933 | static Boolean | | 927 | static Boolean |
934 | ExistsInCmdline(const char *name, const char *val) | | 928 | ExistsInCmdline(const char *name, const char *val) |
935 | { | | 929 | { |
936 | Var *v; | | 930 | Var *v; |
937 | | | 931 | |
938 | v = VarFind(name, SCOPE_CMDLINE, FALSE); | | 932 | v = VarFind(name, SCOPE_CMDLINE, FALSE); |
939 | if (v == NULL) | | 933 | if (v == NULL) |
940 | return FALSE; | | 934 | return FALSE; |
941 | | | 935 | |
942 | if (v->flags & VFL_FROM_CMD) { | | 936 | if (v->flags & VFL_FROM_CMD) { |
943 | DEBUG3(VAR, "%s:%s = %s ignored!\n", | | 937 | DEBUG3(VAR, "%s:%s = %s ignored!\n", |
944 | SCOPE_GLOBAL->name, name, val); | | 938 | SCOPE_GLOBAL->name, name, val); |
945 | return TRUE; | | 939 | return TRUE; |
946 | } | | 940 | } |
947 | | | 941 | |
948 | VarFreeEnv(v); | | 942 | VarFreeEnv(v); |
949 | return FALSE; | | 943 | return FALSE; |
950 | } | | 944 | } |
951 | | | 945 | |
952 | /* Set the variable to the value; the name is not expanded. */ | | 946 | /* Set the variable to the value; the name is not expanded. */ |
953 | void | | 947 | void |
954 | Var_SetWithFlags(GNode *scope, const char *name, const char *val, | | 948 | Var_SetWithFlags(GNode *scope, const char *name, const char *val, |
955 | VarSetFlags flags) | | 949 | VarSetFlags flags) |
956 | { | | 950 | { |
957 | Var *v; | | 951 | Var *v; |
958 | | | 952 | |
959 | assert(val != NULL); | | 953 | assert(val != NULL); |
960 | if (name[0] == '\0') { | | 954 | if (name[0] == '\0') { |
961 | DEBUG0(VAR, "SetVar: variable name is empty - ignored\n"); | | 955 | DEBUG0(VAR, "SetVar: variable name is empty - ignored\n"); |
962 | return; | | 956 | return; |
963 | } | | 957 | } |
964 | | | 958 | |
965 | if (scope == SCOPE_GLOBAL && ExistsInCmdline(name, val)) | | 959 | if (scope == SCOPE_GLOBAL && ExistsInCmdline(name, val)) |
966 | return; | | 960 | return; |
967 | | | 961 | |
968 | /* | | 962 | /* |
969 | * Only look for a variable in the given scope since anything set | | 963 | * Only look for a variable in the given scope since anything set |
970 | * here will override anything in a lower scope, so there's not much | | 964 | * here will override anything in a lower scope, so there's not much |
971 | * point in searching them all. | | 965 | * point in searching them all. |
972 | */ | | 966 | */ |
973 | v = VarFind(name, scope, FALSE); | | 967 | v = VarFind(name, scope, FALSE); |
974 | if (v == NULL) { | | 968 | if (v == NULL) { |
975 | if (scope == SCOPE_CMDLINE && !(flags & VAR_SET_NO_EXPORT)) { | | 969 | if (scope == SCOPE_CMDLINE && !(flags & VAR_SET_NO_EXPORT)) { |
976 | /* | | 970 | /* |
977 | * This var would normally prevent the same name being | | 971 | * This var would normally prevent the same name being |
978 | * added to SCOPE_GLOBAL, so delete it from there if | | 972 | * added to SCOPE_GLOBAL, so delete it from there if |
979 | * needed. Otherwise -V name may show the wrong value. | | 973 | * needed. Otherwise -V name may show the wrong value. |
980 | * | | 974 | * |
981 | * See ExistsInCmdline. | | 975 | * See ExistsInCmdline. |
982 | */ | | 976 | */ |
983 | Var_Delete(SCOPE_GLOBAL, name); | | 977 | Var_Delete(SCOPE_GLOBAL, name); |
984 | } | | 978 | } |
985 | v = VarAdd(name, val, scope, flags); | | 979 | v = VarAdd(name, val, scope, flags); |
986 | } else { | | 980 | } else { |
987 | if ((v->flags & VFL_READONLY) && !(flags & VAR_SET_READONLY)) { | | 981 | if ((v->flags & VFL_READONLY) && !(flags & VAR_SET_READONLY)) { |
988 | DEBUG3(VAR, "%s:%s = %s ignored (read-only)\n", | | 982 | DEBUG3(VAR, "%s:%s = %s ignored (read-only)\n", |
989 | scope->name, name, val); | | 983 | scope->name, name, val); |
990 | return; | | 984 | return; |
991 | } | | 985 | } |
992 | Buf_Empty(&v->val); | | 986 | Buf_Empty(&v->val); |
993 | Buf_AddStr(&v->val, val); | | 987 | Buf_AddStr(&v->val, val); |
994 | | | 988 | |
995 | DEBUG3(VAR, "%s:%s = %s\n", scope->name, name, val); | | 989 | DEBUG3(VAR, "%s:%s = %s\n", scope->name, name, val); |
996 | if (v->flags & VFL_EXPORTED) | | 990 | if (v->flags & VFL_EXPORTED) |
997 | ExportVar(name, VEM_PLAIN); | | 991 | ExportVar(name, VEM_PLAIN); |
998 | } | | 992 | } |
999 | | | 993 | |
1000 | /* | | 994 | /* |
1001 | * Any variables given on the command line are automatically exported | | 995 | * Any variables given on the command line are automatically exported |
1002 | * to the environment (as per POSIX standard), except for internals. | | 996 | * to the environment (as per POSIX standard), except for internals. |
1003 | */ | | 997 | */ |
1004 | if (scope == SCOPE_CMDLINE && !(flags & VAR_SET_NO_EXPORT) && | | 998 | if (scope == SCOPE_CMDLINE && !(flags & VAR_SET_NO_EXPORT) && |
1005 | name[0] != '.') { | | 999 | name[0] != '.') { |
1006 | v->flags |= VFL_FROM_CMD; | | 1000 | v->flags |= VFL_FROM_CMD; |
1007 | | | 1001 | |
1008 | /* | | 1002 | /* |
1009 | * If requested, don't export these in the environment | | 1003 | * If requested, don't export these in the environment |
1010 | * individually. We still put them in MAKEOVERRIDES so | | 1004 | * individually. We still put them in MAKEOVERRIDES so |
1011 | * that the command-line settings continue to override | | 1005 | * that the command-line settings continue to override |
1012 | * Makefile settings. | | 1006 | * Makefile settings. |
1013 | */ | | 1007 | */ |
1014 | if (!opts.varNoExportEnv) | | 1008 | if (!opts.varNoExportEnv) |
1015 | setenv(name, val, 1); | | 1009 | setenv(name, val, 1); |
1016 | /* XXX: What about .MAKE.EXPORTED? */ | | 1010 | /* XXX: What about .MAKE.EXPORTED? */ |
1017 | /* XXX: Why not just mark the variable for needing export, | | 1011 | /* XXX: Why not just mark the variable for needing export, |
1018 | * as in ExportVarPlain? */ | | 1012 | * as in ExportVarPlain? */ |
1019 | | | 1013 | |
1020 | Global_Append(MAKEOVERRIDES, name); | | 1014 | Global_Append(MAKEOVERRIDES, name); |
1021 | } | | 1015 | } |
1022 | | | 1016 | |
1023 | if (name[0] == '.' && strcmp(name, MAKE_SAVE_DOLLARS) == 0) | | 1017 | if (name[0] == '.' && strcmp(name, MAKE_SAVE_DOLLARS) == 0) |
1024 | save_dollars = ParseBoolean(val, save_dollars); | | 1018 | save_dollars = ParseBoolean(val, save_dollars); |
1025 | | | 1019 | |
1026 | if (v != NULL) | | 1020 | if (v != NULL) |
1027 | VarFreeEnv(v); | | 1021 | VarFreeEnv(v); |
1028 | } | | 1022 | } |
1029 | | | 1023 | |
1030 | /* See Var_Set for documentation. */ | | 1024 | /* See Var_Set for documentation. */ |
1031 | void | | 1025 | void |
1032 | Var_SetExpandWithFlags(GNode *scope, const char *name, const char *val, | | 1026 | Var_SetExpandWithFlags(GNode *scope, const char *name, const char *val, |
1033 | VarSetFlags flags) | | 1027 | VarSetFlags flags) |
1034 | { | | 1028 | { |
1035 | const char *unexpanded_name = name; | | 1029 | const char *unexpanded_name = name; |
1036 | FStr varname = FStr_InitRefer(name); | | 1030 | FStr varname = FStr_InitRefer(name); |
1037 | | | 1031 | |
1038 | assert(val != NULL); | | 1032 | assert(val != NULL); |
1039 | | | 1033 | |
1040 | if (strchr(varname.str, '$') != NULL) { | | 1034 | if (strchr(varname.str, '$') != NULL) { |
1041 | char *expanded; | | 1035 | char *expanded; |
1042 | (void)Var_Subst(varname.str, scope, VARE_WANTRES, &expanded); | | 1036 | (void)Var_Subst(varname.str, scope, VARE_WANTRES, &expanded); |
1043 | /* TODO: handle errors */ | | 1037 | /* TODO: handle errors */ |
1044 | varname = FStr_InitOwn(expanded); | | 1038 | varname = FStr_InitOwn(expanded); |
1045 | } | | 1039 | } |
1046 | | | 1040 | |
1047 | if (varname.str[0] == '\0') { | | 1041 | if (varname.str[0] == '\0') { |
1048 | DEBUG2(VAR, "Var_Set(\"%s\", \"%s\", ...) " | | 1042 | DEBUG2(VAR, "Var_Set(\"%s\", \"%s\", ...) " |
1049 | "name expands to empty string - ignored\n", | | 1043 | "name expands to empty string - ignored\n", |
1050 | unexpanded_name, val); | | 1044 | unexpanded_name, val); |
1051 | } else | | 1045 | } else |
1052 | Var_SetWithFlags(scope, varname.str, val, flags); | | 1046 | Var_SetWithFlags(scope, varname.str, val, flags); |
1053 | | | 1047 | |
1054 | FStr_Done(&varname); | | 1048 | FStr_Done(&varname); |
1055 | } | | 1049 | } |
1056 | | | 1050 | |
1057 | void | | 1051 | void |
1058 | Var_Set(GNode *scope, const char *name, const char *val) | | 1052 | Var_Set(GNode *scope, const char *name, const char *val) |
1059 | { | | 1053 | { |
1060 | Var_SetWithFlags(scope, name, val, VAR_SET_NONE); | | 1054 | Var_SetWithFlags(scope, name, val, VAR_SET_NONE); |
1061 | } | | 1055 | } |
1062 | | | 1056 | |
1063 | /* | | 1057 | /* |
1064 | * Set the variable name to the value val in the given scope. | | 1058 | * Set the variable name to the value val in the given scope. |
1065 | * | | 1059 | * |
1066 | * If the variable doesn't yet exist, it is created. | | 1060 | * If the variable doesn't yet exist, it is created. |
1067 | * Otherwise the new value overwrites and replaces the old value. | | 1061 | * Otherwise the new value overwrites and replaces the old value. |
1068 | * | | 1062 | * |
1069 | * Input: | | 1063 | * Input: |
1070 | * name name of the variable to set, is expanded once | | 1064 | * name name of the variable to set, is expanded once |
1071 | * val value to give to the variable | | 1065 | * val value to give to the variable |
1072 | * scope scope in which to set it | | 1066 | * scope scope in which to set it |
1073 | */ | | 1067 | */ |
1074 | void | | 1068 | void |
1075 | Var_SetExpand(GNode *scope, const char *name, const char *val) | | 1069 | Var_SetExpand(GNode *scope, const char *name, const char *val) |
1076 | { | | 1070 | { |
1077 | Var_SetExpandWithFlags(scope, name, val, VAR_SET_NONE); | | 1071 | Var_SetExpandWithFlags(scope, name, val, VAR_SET_NONE); |
1078 | } | | 1072 | } |
1079 | | | 1073 | |
1080 | void | | 1074 | void |
1081 | Global_Set(const char *name, const char *value) | | 1075 | Global_Set(const char *name, const char *value) |
1082 | { | | 1076 | { |
1083 | Var_Set(SCOPE_GLOBAL, name, value); | | 1077 | Var_Set(SCOPE_GLOBAL, name, value); |
1084 | } | | 1078 | } |
1085 | | | 1079 | |
1086 | void | | 1080 | void |
1087 | Global_SetExpand(const char *name, const char *value) | | 1081 | Global_SetExpand(const char *name, const char *value) |
1088 | { | | 1082 | { |
1089 | Var_SetExpand(SCOPE_GLOBAL, name, value); | | 1083 | Var_SetExpand(SCOPE_GLOBAL, name, value); |
1090 | } | | 1084 | } |
1091 | | | 1085 | |
1092 | void | | 1086 | void |
1093 | Global_Delete(const char *name) | | 1087 | Global_Delete(const char *name) |
1094 | { | | 1088 | { |
1095 | Var_Delete(SCOPE_GLOBAL, name); | | 1089 | Var_Delete(SCOPE_GLOBAL, name); |
1096 | } | | 1090 | } |
1097 | | | 1091 | |
1098 | /* | | 1092 | /* |
1099 | * Append the value to the named variable. | | 1093 | * Append the value to the named variable. |
1100 | * | | 1094 | * |
1101 | * If the variable doesn't exist, it is created. Otherwise a single space | | 1095 | * If the variable doesn't exist, it is created. Otherwise a single space |
1102 | * and the given value are appended. | | 1096 | * and the given value are appended. |
1103 | */ | | 1097 | */ |
1104 | void | | 1098 | void |
1105 | Var_Append(GNode *scope, const char *name, const char *val) | | 1099 | Var_Append(GNode *scope, const char *name, const char *val) |
1106 | { | | 1100 | { |
1107 | Var *v; | | 1101 | Var *v; |
1108 | | | 1102 | |
1109 | v = VarFind(name, scope, scope == SCOPE_GLOBAL); | | 1103 | v = VarFind(name, scope, scope == SCOPE_GLOBAL); |
1110 | | | 1104 | |
1111 | if (v == NULL) { | | 1105 | if (v == NULL) { |
1112 | Var_SetWithFlags(scope, name, val, VAR_SET_NONE); | | 1106 | Var_SetWithFlags(scope, name, val, VAR_SET_NONE); |
1113 | } else if (v->flags & VFL_READONLY) { | | 1107 | } else if (v->flags & VFL_READONLY) { |
1114 | DEBUG1(VAR, "Ignoring append to %s since it is read-only\n", | | 1108 | DEBUG1(VAR, "Ignoring append to %s since it is read-only\n", |
1115 | name); | | 1109 | name); |
1116 | } else if (scope == SCOPE_CMDLINE || !(v->flags & VFL_FROM_CMD)) { | | 1110 | } else if (scope == SCOPE_CMDLINE || !(v->flags & VFL_FROM_CMD)) { |
1117 | Buf_AddByte(&v->val, ' '); | | 1111 | Buf_AddByte(&v->val, ' '); |
1118 | Buf_AddStr(&v->val, val); | | 1112 | Buf_AddStr(&v->val, val); |
1119 | | | 1113 | |
1120 | DEBUG3(VAR, "%s:%s = %s\n", scope->name, name, v->val.data); | | 1114 | DEBUG3(VAR, "%s:%s = %s\n", scope->name, name, v->val.data); |
1121 | | | 1115 | |
1122 | if (v->flags & VFL_FROM_ENV) { | | 1116 | if (v->flags & VFL_FROM_ENV) { |
1123 | /* | | 1117 | /* |
1124 | * If the original variable came from the environment, | | 1118 | * If the original variable came from the environment, |
1125 | * we have to install it in the global scope (we | | 1119 | * we have to install it in the global scope (we |
1126 | * could place it in the environment, but then we | | 1120 | * could place it in the environment, but then we |
1127 | * should provide a way to export other variables...) | | 1121 | * should provide a way to export other variables...) |
1128 | */ | | 1122 | */ |
1129 | v->flags &= ~(unsigned)VFL_FROM_ENV; | | 1123 | v->flags &= ~(unsigned)VFL_FROM_ENV; |
1130 | /* | | 1124 | /* |
1131 | * This is the only place where a variable is | | 1125 | * This is the only place where a variable is |
1132 | * created whose v->name is not the same as | | 1126 | * created whose v->name is not the same as |
1133 | * scope->vars->key. | | 1127 | * scope->vars->key. |
1134 | */ | | 1128 | */ |
1135 | HashTable_Set(&scope->vars, name, v); | | 1129 | HashTable_Set(&scope->vars, name, v); |
1136 | } | | 1130 | } |
1137 | } | | 1131 | } |
1138 | } | | 1132 | } |
1139 | | | 1133 | |
1140 | /* | | 1134 | /* |
1141 | * The variable of the given name has the given value appended to it in the | | 1135 | * The variable of the given name has the given value appended to it in the |
1142 | * given scope. | | 1136 | * given scope. |
1143 | * | | 1137 | * |
1144 | * If the variable doesn't exist, it is created. Otherwise the strings are | | 1138 | * If the variable doesn't exist, it is created. Otherwise the strings are |
1145 | * concatenated, with a space in between. | | 1139 | * concatenated, with a space in between. |
1146 | * | | 1140 | * |
1147 | * Input: | | 1141 | * Input: |
1148 | * name name of the variable to modify, is expanded once | | 1142 | * name name of the variable to modify, is expanded once |
1149 | * val string to append to it | | 1143 | * val string to append to it |
1150 | * scope scope in which this should occur | | 1144 | * scope scope in which this should occur |
1151 | * | | 1145 | * |
1152 | * Notes: | | 1146 | * Notes: |
1153 | * Only if the variable is being sought in the global scope is the | | 1147 | * Only if the variable is being sought in the global scope is the |
1154 | * environment searched. | | 1148 | * environment searched. |
1155 | * XXX: Knows its calling circumstances in that if called with scope | | 1149 | * XXX: Knows its calling circumstances in that if called with scope |
1156 | * an actual target, it will only search that scope since only | | 1150 | * an actual target, it will only search that scope since only |
1157 | * a local variable could be being appended to. This is actually | | 1151 | * a local variable could be being appended to. This is actually |
1158 | * a big win and must be tolerated. | | 1152 | * a big win and must be tolerated. |
1159 | */ | | 1153 | */ |
1160 | void | | 1154 | void |
1161 | Var_AppendExpand(GNode *scope, const char *name, const char *val) | | 1155 | Var_AppendExpand(GNode *scope, const char *name, const char *val) |
1162 | { | | 1156 | { |
1163 | FStr xname = FStr_InitRefer(name); | | 1157 | FStr xname = FStr_InitRefer(name); |
1164 | | | 1158 | |
1165 | assert(val != NULL); | | 1159 | assert(val != NULL); |
1166 | | | 1160 | |
1167 | if (strchr(name, '$') != NULL) { | | 1161 | if (strchr(name, '$') != NULL) { |
1168 | char *expanded; | | 1162 | char *expanded; |
1169 | (void)Var_Subst(name, scope, VARE_WANTRES, &expanded); | | 1163 | (void)Var_Subst(name, scope, VARE_WANTRES, &expanded); |
1170 | /* TODO: handle errors */ | | 1164 | /* TODO: handle errors */ |
1171 | xname = FStr_InitOwn(expanded); | | 1165 | xname = FStr_InitOwn(expanded); |
1172 | if (expanded[0] == '\0') { | | 1166 | if (expanded[0] == '\0') { |
1173 | /* TODO: update function name in the debug message */ | | 1167 | /* TODO: update function name in the debug message */ |
1174 | DEBUG2(VAR, "Var_Append(\"%s\", \"%s\", ...) " | | 1168 | DEBUG2(VAR, "Var_Append(\"%s\", \"%s\", ...) " |
1175 | "name expands to empty string - ignored\n", | | 1169 | "name expands to empty string - ignored\n", |
1176 | name, val); | | 1170 | name, val); |
1177 | FStr_Done(&xname); | | 1171 | FStr_Done(&xname); |
1178 | return; | | 1172 | return; |
1179 | } | | 1173 | } |
1180 | } | | 1174 | } |
1181 | | | 1175 | |
1182 | Var_Append(scope, xname.str, val); | | 1176 | Var_Append(scope, xname.str, val); |
1183 | | | 1177 | |
1184 | FStr_Done(&xname); | | 1178 | FStr_Done(&xname); |
1185 | } | | 1179 | } |
1186 | | | 1180 | |
1187 | void | | 1181 | void |
1188 | Global_Append(const char *name, const char *value) | | 1182 | Global_Append(const char *name, const char *value) |
1189 | { | | 1183 | { |
1190 | Var_Append(SCOPE_GLOBAL, name, value); | | 1184 | Var_Append(SCOPE_GLOBAL, name, value); |
1191 | } | | 1185 | } |
1192 | | | 1186 | |
1193 | Boolean | | 1187 | Boolean |
1194 | Var_Exists(GNode *scope, const char *name) | | 1188 | Var_Exists(GNode *scope, const char *name) |
1195 | { | | 1189 | { |
1196 | Var *v = VarFind(name, scope, TRUE); | | 1190 | Var *v = VarFind(name, scope, TRUE); |
1197 | if (v == NULL) | | 1191 | if (v == NULL) |
1198 | return FALSE; | | 1192 | return FALSE; |
1199 | | | 1193 | |
1200 | VarFreeEnv(v); | | 1194 | VarFreeEnv(v); |
1201 | return TRUE; | | 1195 | return TRUE; |
1202 | } | | 1196 | } |
1203 | | | 1197 | |
1204 | /* | | 1198 | /* |
1205 | * See if the given variable exists, in the given scope or in other | | 1199 | * See if the given variable exists, in the given scope or in other |
1206 | * fallback scopes. | | 1200 | * fallback scopes. |
1207 | * | | 1201 | * |
1208 | * Input: | | 1202 | * Input: |
1209 | * name Variable to find, is expanded once | | 1203 | * name Variable to find, is expanded once |
1210 | * scope Scope in which to start search | | 1204 | * scope Scope in which to start search |
1211 | */ | | 1205 | */ |
1212 | Boolean | | 1206 | Boolean |
1213 | Var_ExistsExpand(GNode *scope, const char *name) | | 1207 | Var_ExistsExpand(GNode *scope, const char *name) |
1214 | { | | 1208 | { |
1215 | FStr varname = FStr_InitRefer(name); | | 1209 | FStr varname = FStr_InitRefer(name); |
1216 | Boolean exists; | | 1210 | Boolean exists; |
1217 | | | 1211 | |
1218 | if (strchr(varname.str, '$') != NULL) { | | 1212 | if (strchr(varname.str, '$') != NULL) { |
1219 | char *expanded; | | 1213 | char *expanded; |
1220 | (void)Var_Subst(varname.str, scope, VARE_WANTRES, &expanded); | | 1214 | (void)Var_Subst(varname.str, scope, VARE_WANTRES, &expanded); |
1221 | /* TODO: handle errors */ | | 1215 | /* TODO: handle errors */ |
1222 | varname = FStr_InitOwn(expanded); | | 1216 | varname = FStr_InitOwn(expanded); |
1223 | } | | 1217 | } |
1224 | | | 1218 | |
1225 | exists = Var_Exists(scope, varname.str); | | 1219 | exists = Var_Exists(scope, varname.str); |
1226 | FStr_Done(&varname); | | 1220 | FStr_Done(&varname); |
1227 | return exists; | | 1221 | return exists; |
1228 | } | | 1222 | } |
1229 | | | 1223 | |
1230 | /* | | 1224 | /* |
1231 | * Return the unexpanded value of the given variable in the given scope, | | 1225 | * Return the unexpanded value of the given variable in the given scope, |
1232 | * or the usual scopes. | | 1226 | * or the usual scopes. |
1233 | * | | 1227 | * |
1234 | * Input: | | 1228 | * Input: |
1235 | * name name to find, is not expanded any further | | 1229 | * name name to find, is not expanded any further |
1236 | * scope scope in which to search for it | | 1230 | * scope scope in which to search for it |
1237 | * | | 1231 | * |
1238 | * Results: | | 1232 | * Results: |
1239 | * The value if the variable exists, NULL if it doesn't. | | 1233 | * The value if the variable exists, NULL if it doesn't. |
1240 | * The value is valid until the next modification to any variable. | | 1234 | * The value is valid until the next modification to any variable. |
1241 | */ | | 1235 | */ |
1242 | FStr | | 1236 | FStr |
1243 | Var_Value(GNode *scope, const char *name) | | 1237 | Var_Value(GNode *scope, const char *name) |
1244 | { | | 1238 | { |
1245 | Var *v = VarFind(name, scope, TRUE); | | 1239 | Var *v = VarFind(name, scope, TRUE); |
1246 | char *value; | | 1240 | char *value; |
1247 | | | 1241 | |
1248 | if (v == NULL) | | 1242 | if (v == NULL) |
1249 | return FStr_InitRefer(NULL); | | 1243 | return FStr_InitRefer(NULL); |
1250 | | | 1244 | |
1251 | if (!(v->flags & VFL_FROM_ENV)) | | 1245 | if (!(v->flags & VFL_FROM_ENV)) |
1252 | return FStr_InitRefer(v->val.data); | | 1246 | return FStr_InitRefer(v->val.data); |
1253 | | | 1247 | |
1254 | /* Since environment variables are short-lived, free it now. */ | | 1248 | /* Since environment variables are short-lived, free it now. */ |
1255 | FStr_Done(&v->name); | | 1249 | FStr_Done(&v->name); |
1256 | value = Buf_DoneData(&v->val); | | 1250 | value = Buf_DoneData(&v->val); |
1257 | free(v); | | 1251 | free(v); |
1258 | return FStr_InitOwn(value); | | 1252 | return FStr_InitOwn(value); |
1259 | } | | 1253 | } |
1260 | | | 1254 | |
1261 | /* | | 1255 | /* |
1262 | * Return the unexpanded variable value from this node, without trying to look | | 1256 | * Return the unexpanded variable value from this node, without trying to look |
1263 | * up the variable in any other scope. | | 1257 | * up the variable in any other scope. |
1264 | */ | | 1258 | */ |
1265 | const char * | | 1259 | const char * |
1266 | GNode_ValueDirect(GNode *gn, const char *name) | | 1260 | GNode_ValueDirect(GNode *gn, const char *name) |
1267 | { | | 1261 | { |
1268 | Var *v = VarFind(name, gn, FALSE); | | 1262 | Var *v = VarFind(name, gn, FALSE); |
1269 | return v != NULL ? v->val.data : NULL; | | 1263 | return v != NULL ? v->val.data : NULL; |
1270 | } | | 1264 | } |
1271 | | | 1265 | |
1272 | | | 1266 | |
1273 | static void | | 1267 | static void |
1274 | SepBuf_Init(SepBuf *buf, char sep) | | 1268 | SepBuf_Init(SepBuf *buf, char sep) |
1275 | { | | 1269 | { |
1276 | Buf_InitSize(&buf->buf, 32); | | 1270 | Buf_InitSize(&buf->buf, 32); |
1277 | buf->needSep = FALSE; | | 1271 | buf->needSep = FALSE; |
1278 | buf->sep = sep; | | 1272 | buf->sep = sep; |
1279 | } | | 1273 | } |
1280 | | | 1274 | |
1281 | static void | | 1275 | static void |
1282 | SepBuf_Sep(SepBuf *buf) | | 1276 | SepBuf_Sep(SepBuf *buf) |
1283 | { | | 1277 | { |
1284 | buf->needSep = TRUE; | | 1278 | buf->needSep = TRUE; |
1285 | } | | 1279 | } |
1286 | | | 1280 | |
1287 | static void | | 1281 | static void |
1288 | SepBuf_AddBytes(SepBuf *buf, const char *mem, size_t mem_size) | | 1282 | SepBuf_AddBytes(SepBuf *buf, const char *mem, size_t mem_size) |
1289 | { | | 1283 | { |
1290 | if (mem_size == 0) | | 1284 | if (mem_size == 0) |
1291 | return; | | 1285 | return; |
1292 | if (buf->needSep && buf->sep != '\0') { | | 1286 | if (buf->needSep && buf->sep != '\0') { |
1293 | Buf_AddByte(&buf->buf, buf->sep); | | 1287 | Buf_AddByte(&buf->buf, buf->sep); |
1294 | buf->needSep = FALSE; | | 1288 | buf->needSep = FALSE; |
1295 | } | | 1289 | } |
1296 | Buf_AddBytes(&buf->buf, mem, mem_size); | | 1290 | Buf_AddBytes(&buf->buf, mem, mem_size); |
1297 | } | | 1291 | } |
1298 | | | 1292 | |
1299 | static void | | 1293 | static void |
1300 | SepBuf_AddBytesBetween(SepBuf *buf, const char *start, const char *end) | | 1294 | SepBuf_AddBytesBetween(SepBuf *buf, const char *start, const char *end) |
1301 | { | | 1295 | { |
1302 | SepBuf_AddBytes(buf, start, (size_t)(end - start)); | | 1296 | SepBuf_AddBytes(buf, start, (size_t)(end - start)); |
1303 | } | | 1297 | } |
1304 | | | 1298 | |
1305 | static void | | 1299 | static void |
1306 | SepBuf_AddStr(SepBuf *buf, const char *str) | | 1300 | SepBuf_AddStr(SepBuf *buf, const char *str) |
1307 | { | | 1301 | { |
1308 | SepBuf_AddBytes(buf, str, strlen(str)); | | 1302 | SepBuf_AddBytes(buf, str, strlen(str)); |
1309 | } | | 1303 | } |
1310 | | | 1304 | |
1311 | static char * | | 1305 | static char * |
1312 | SepBuf_DoneData(SepBuf *buf) | | 1306 | SepBuf_DoneData(SepBuf *buf) |
1313 | { | | 1307 | { |
1314 | return Buf_DoneData(&buf->buf); | | 1308 | return Buf_DoneData(&buf->buf); |
1315 | } | | 1309 | } |
1316 | | | 1310 | |
1317 | | | 1311 | |
1318 | /* | | 1312 | /* |
1319 | * This callback for ModifyWords gets a single word from a variable expression | | 1313 | * This callback for ModifyWords gets a single word from a variable expression |
1320 | * and typically adds a modification of this word to the buffer. It may also | | 1314 | * and typically adds a modification of this word to the buffer. It may also |
1321 | * do nothing or add several words. | | 1315 | * do nothing or add several words. |
1322 | * | | 1316 | * |
1323 | * For example, when evaluating the modifier ':M*b' in ${:Ua b c:M*b}, the | | 1317 | * For example, when evaluating the modifier ':M*b' in ${:Ua b c:M*b}, the |
1324 | * callback is called 3 times, once for "a", "b" and "c". | | 1318 | * callback is called 3 times, once for "a", "b" and "c". |
1325 | */ | | 1319 | */ |
1326 | typedef void (*ModifyWordProc)(const char *word, SepBuf *buf, void *data); | | 1320 | typedef void (*ModifyWordProc)(const char *word, SepBuf *buf, void *data); |
1327 | | | 1321 | |
1328 | | | 1322 | |
1329 | /* | | 1323 | /* |
1330 | * Callback for ModifyWords to implement the :H modifier. | | 1324 | * Callback for ModifyWords to implement the :H modifier. |
1331 | * Add the dirname of the given word to the buffer. | | 1325 | * Add the dirname of the given word to the buffer. |
1332 | */ | | 1326 | */ |
1333 | /*ARGSUSED*/ | | 1327 | /*ARGSUSED*/ |
1334 | static void | | 1328 | static void |
1335 | ModifyWord_Head(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) | | 1329 | ModifyWord_Head(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) |
1336 | { | | 1330 | { |
1337 | const char *slash = strrchr(word, '/'); | | 1331 | const char *slash = strrchr(word, '/'); |
1338 | if (slash != NULL) | | 1332 | if (slash != NULL) |
1339 | SepBuf_AddBytesBetween(buf, word, slash); | | 1333 | SepBuf_AddBytesBetween(buf, word, slash); |
1340 | else | | 1334 | else |
1341 | SepBuf_AddStr(buf, "."); | | 1335 | SepBuf_AddStr(buf, "."); |
1342 | } | | 1336 | } |
1343 | | | 1337 | |
1344 | /* | | 1338 | /* |
1345 | * Callback for ModifyWords to implement the :T modifier. | | 1339 | * Callback for ModifyWords to implement the :T modifier. |
1346 | * Add the basename of the given word to the buffer. | | 1340 | * Add the basename of the given word to the buffer. |
1347 | */ | | 1341 | */ |
1348 | /*ARGSUSED*/ | | 1342 | /*ARGSUSED*/ |
1349 | static void | | 1343 | static void |
1350 | ModifyWord_Tail(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) | | 1344 | ModifyWord_Tail(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) |
1351 | { | | 1345 | { |
1352 | SepBuf_AddStr(buf, str_basename(word)); | | 1346 | SepBuf_AddStr(buf, str_basename(word)); |
1353 | } | | 1347 | } |
1354 | | | 1348 | |
1355 | /* | | 1349 | /* |
1356 | * Callback for ModifyWords to implement the :E modifier. | | 1350 | * Callback for ModifyWords to implement the :E modifier. |
1357 | * Add the filename suffix of the given word to the buffer, if it exists. | | 1351 | * Add the filename suffix of the given word to the buffer, if it exists. |
1358 | */ | | 1352 | */ |
1359 | /*ARGSUSED*/ | | 1353 | /*ARGSUSED*/ |
1360 | static void | | 1354 | static void |
1361 | ModifyWord_Suffix(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) | | 1355 | ModifyWord_Suffix(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) |
1362 | { | | 1356 | { |
1363 | const char *lastDot = strrchr(word, '.'); | | 1357 | const char *lastDot = strrchr(word, '.'); |
1364 | if (lastDot != NULL) | | 1358 | if (lastDot != NULL) |
1365 | SepBuf_AddStr(buf, lastDot + 1); | | 1359 | SepBuf_AddStr(buf, lastDot + 1); |
1366 | } | | 1360 | } |
1367 | | | 1361 | |
1368 | /* | | 1362 | /* |
1369 | * Callback for ModifyWords to implement the :R modifier. | | 1363 | * Callback for ModifyWords to implement the :R modifier. |
1370 | * Add the filename without extension of the given word to the buffer. | | 1364 | * Add the filename without extension of the given word to the buffer. |
1371 | */ | | 1365 | */ |
1372 | /*ARGSUSED*/ | | 1366 | /*ARGSUSED*/ |
1373 | static void | | 1367 | static void |
1374 | ModifyWord_Root(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) | | 1368 | ModifyWord_Root(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) |
1375 | { | | 1369 | { |
1376 | const char *lastDot = strrchr(word, '.'); | | 1370 | const char *lastDot = strrchr(word, '.'); |
1377 | size_t len = lastDot != NULL ? (size_t)(lastDot - word) : strlen(word); | | 1371 | size_t len = lastDot != NULL ? (size_t)(lastDot - word) : strlen(word); |
1378 | SepBuf_AddBytes(buf, word, len); | | 1372 | SepBuf_AddBytes(buf, word, len); |
1379 | } | | 1373 | } |
1380 | | | 1374 | |
1381 | /* | | 1375 | /* |
1382 | * Callback for ModifyWords to implement the :M modifier. | | 1376 | * Callback for ModifyWords to implement the :M modifier. |
1383 | * Place the word in the buffer if it matches the given pattern. | | 1377 | * Place the word in the buffer if it matches the given pattern. |
1384 | */ | | 1378 | */ |
1385 | static void | | 1379 | static void |
1386 | ModifyWord_Match(const char *word, SepBuf *buf, void *data) | | 1380 | ModifyWord_Match(const char *word, SepBuf *buf, void *data) |
1387 | { | | 1381 | { |
1388 | const char *pattern = data; | | 1382 | const char *pattern = data; |
1389 | DEBUG2(VAR, "VarMatch [%s] [%s]\n", word, pattern); | | 1383 | DEBUG2(VAR, "VarMatch [%s] [%s]\n", word, pattern); |
1390 | if (Str_Match(word, pattern)) | | 1384 | if (Str_Match(word, pattern)) |
1391 | SepBuf_AddStr(buf, word); | | 1385 | SepBuf_AddStr(buf, word); |
1392 | } | | 1386 | } |
1393 | | | 1387 | |
1394 | /* | | 1388 | /* |
1395 | * Callback for ModifyWords to implement the :N modifier. | | 1389 | * Callback for ModifyWords to implement the :N modifier. |
1396 | * Place the word in the buffer if it doesn't match the given pattern. | | 1390 | * Place the word in the buffer if it doesn't match the given pattern. |
1397 | */ | | 1391 | */ |
1398 | static void | | 1392 | static void |
1399 | ModifyWord_NoMatch(const char *word, SepBuf *buf, void *data) | | 1393 | ModifyWord_NoMatch(const char *word, SepBuf *buf, void *data) |
1400 | { | | 1394 | { |
1401 | const char *pattern = data; | | 1395 | const char *pattern = data; |
1402 | if (!Str_Match(word, pattern)) | | 1396 | if (!Str_Match(word, pattern)) |
1403 | SepBuf_AddStr(buf, word); | | 1397 | SepBuf_AddStr(buf, word); |
1404 | } | | 1398 | } |
1405 | | | 1399 | |
1406 | #ifdef SYSVVARSUB | | 1400 | #ifdef SYSVVARSUB |
1407 | | | 1401 | |
1408 | /* | | 1402 | /* |
1409 | * Check word against pattern for a match (% is a wildcard). | | 1403 | * Check word against pattern for a match (% is a wildcard). |
1410 | * | | 1404 | * |
1411 | * Input: | | 1405 | * Input: |
1412 | * word Word to examine | | 1406 | * word Word to examine |
1413 | * pattern Pattern to examine against | | 1407 | * pattern Pattern to examine against |
1414 | * | | 1408 | * |
1415 | * Results: | | 1409 | * Results: |
1416 | * Returns the start of the match, or NULL. | | 1410 | * Returns the start of the match, or NULL. |
1417 | * out_match_len returns the length of the match, if any. | | 1411 | * out_match_len returns the length of the match, if any. |
1418 | * out_hasPercent returns whether the pattern contains a percent. | | 1412 | * out_hasPercent returns whether the pattern contains a percent. |
1419 | */ | | 1413 | */ |
1420 | static const char * | | 1414 | static const char * |
1421 | SysVMatch(const char *word, const char *pattern, | | 1415 | SysVMatch(const char *word, const char *pattern, |
1422 | size_t *out_match_len, Boolean *out_hasPercent) | | 1416 | size_t *out_match_len, Boolean *out_hasPercent) |
1423 | { | | 1417 | { |
1424 | const char *p = pattern; | | 1418 | const char *p = pattern; |
1425 | const char *w = word; | | 1419 | const char *w = word; |
1426 | const char *percent; | | 1420 | const char *percent; |
1427 | size_t w_len; | | 1421 | size_t w_len; |
1428 | size_t p_len; | | 1422 | size_t p_len; |
1429 | const char *w_tail; | | 1423 | const char *w_tail; |
1430 | | | 1424 | |
1431 | *out_hasPercent = FALSE; | | 1425 | *out_hasPercent = FALSE; |
1432 | percent = strchr(p, '%'); | | 1426 | percent = strchr(p, '%'); |
1433 | if (percent != NULL) { /* ${VAR:...%...=...} */ | | 1427 | if (percent != NULL) { /* ${VAR:...%...=...} */ |
1434 | *out_hasPercent = TRUE; | | 1428 | *out_hasPercent = TRUE; |
1435 | if (w[0] == '\0') | | 1429 | if (w[0] == '\0') |
1436 | return NULL; /* empty word does not match pattern */ | | 1430 | return NULL; /* empty word does not match pattern */ |
1437 | | | 1431 | |
1438 | /* check that the prefix matches */ | | 1432 | /* check that the prefix matches */ |
1439 | for (; p != percent && *w != '\0' && *w == *p; w++, p++) | | 1433 | for (; p != percent && *w != '\0' && *w == *p; w++, p++) |
1440 | continue; | | 1434 | continue; |
1441 | if (p != percent) | | 1435 | if (p != percent) |
1442 | return NULL; /* No match */ | | 1436 | return NULL; /* No match */ |
1443 | | | 1437 | |
1444 | p++; /* Skip the percent */ | | 1438 | p++; /* Skip the percent */ |
1445 | if (*p == '\0') { | | 1439 | if (*p == '\0') { |
1446 | /* No more pattern, return the rest of the string */ | | 1440 | /* No more pattern, return the rest of the string */ |
1447 | *out_match_len = strlen(w); | | 1441 | *out_match_len = strlen(w); |
1448 | return w; | | 1442 | return w; |
1449 | } | | 1443 | } |
1450 | } | | 1444 | } |
1451 | | | 1445 | |
1452 | /* Test whether the tail matches */ | | 1446 | /* Test whether the tail matches */ |
1453 | w_len = strlen(w); | | 1447 | w_len = strlen(w); |
1454 | p_len = strlen(p); | | 1448 | p_len = strlen(p); |
1455 | if (w_len < p_len) | | 1449 | if (w_len < p_len) |
1456 | return NULL; | | 1450 | return NULL; |
1457 | | | 1451 | |
1458 | w_tail = w + w_len - p_len; | | 1452 | w_tail = w + w_len - p_len; |
1459 | if (memcmp(p, w_tail, p_len) != 0) | | 1453 | if (memcmp(p, w_tail, p_len) != 0) |
1460 | return NULL; | | 1454 | return NULL; |
1461 | | | 1455 | |
1462 | *out_match_len = (size_t)(w_tail - w); | | 1456 | *out_match_len = (size_t)(w_tail - w); |
1463 | return w; | | 1457 | return w; |
1464 | } | | 1458 | } |
1465 | | | 1459 | |
1466 | struct ModifyWord_SYSVSubstArgs { | | 1460 | struct ModifyWord_SYSVSubstArgs { |
1467 | GNode *scope; | | 1461 | GNode *scope; |
1468 | const char *lhs; | | 1462 | const char *lhs; |
1469 | const char *rhs; | | 1463 | const char *rhs; |