make(1): remove redundant null check from s2Booleandiff -r1.415 -r1.416 src/usr.bin/make/main.c
(rillig)
--- src/usr.bin/make/main.c 2020/10/31 09:23:38 1.415
+++ src/usr.bin/make/main.c 2020/10/31 09:27:19 1.416
@@ -1,1120 +1,1120 @@ | @@ -1,1120 +1,1120 @@ | |||
1 | /* $NetBSD: main.c,v 1.415 2020/10/31 09:23:38 rillig Exp $ */ | 1 | /* $NetBSD: main.c,v 1.416 2020/10/31 09:27:19 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 | * main.c -- | 72 | * main.c -- | |
73 | * The main file for this entire program. Exit routines etc | 73 | * The main file for this entire program. Exit routines etc | |
74 | * reside here. | 74 | * reside here. | |
75 | * | 75 | * | |
76 | * Utility functions defined in this file: | 76 | * Utility functions defined in this file: | |
77 | * Main_ParseArgLine Takes a line of arguments, breaks them and | 77 | * Main_ParseArgLine Takes a line of arguments, breaks them and | |
78 | * treats them as if they were given when first | 78 | * treats them as if they were given when first | |
79 | * invoked. Used by the parse module to implement | 79 | * invoked. Used by the parse module to implement | |
80 | * the .MFLAGS target. | 80 | * the .MFLAGS target. | |
81 | * | 81 | * | |
82 | * Error Print a tagged error message. The global | 82 | * Error Print a tagged error message. The global | |
83 | * MAKE variable must have been defined. This | 83 | * MAKE variable must have been defined. This | |
84 | * takes a format string and optional arguments | 84 | * takes a format string and optional arguments | |
85 | * for it. | 85 | * for it. | |
86 | * | 86 | * | |
87 | * Fatal Print an error message and exit. Also takes | 87 | * Fatal Print an error message and exit. Also takes | |
88 | * a format string and arguments for it. | 88 | * a format string and arguments for it. | |
89 | * | 89 | * | |
90 | * Punt Aborts all jobs and exits with a message. Also | 90 | * Punt Aborts all jobs and exits with a message. Also | |
91 | * takes a format string and arguments for it. | 91 | * takes a format string and arguments for it. | |
92 | * | 92 | * | |
93 | * Finish Finish things up by printing the number of | 93 | * Finish Finish things up by printing the number of | |
94 | * errors which occurred, as passed to it, and | 94 | * errors which occurred, as passed to it, and | |
95 | * exiting. | 95 | * exiting. | |
96 | */ | 96 | */ | |
97 | 97 | |||
98 | #include <sys/types.h> | 98 | #include <sys/types.h> | |
99 | #include <sys/time.h> | 99 | #include <sys/time.h> | |
100 | #include <sys/param.h> | 100 | #include <sys/param.h> | |
101 | #include <sys/resource.h> | 101 | #include <sys/resource.h> | |
102 | #include <sys/stat.h> | 102 | #include <sys/stat.h> | |
103 | #ifdef MAKE_NATIVE | 103 | #ifdef MAKE_NATIVE | |
104 | #include <sys/sysctl.h> | 104 | #include <sys/sysctl.h> | |
105 | #endif | 105 | #endif | |
106 | #include <sys/utsname.h> | 106 | #include <sys/utsname.h> | |
107 | #include <sys/wait.h> | 107 | #include <sys/wait.h> | |
108 | 108 | |||
109 | #include <errno.h> | 109 | #include <errno.h> | |
110 | #include <signal.h> | 110 | #include <signal.h> | |
111 | #include <stdarg.h> | 111 | #include <stdarg.h> | |
112 | #include <time.h> | 112 | #include <time.h> | |
113 | 113 | |||
114 | #include "make.h" | 114 | #include "make.h" | |
115 | #include "dir.h" | 115 | #include "dir.h" | |
116 | #include "job.h" | 116 | #include "job.h" | |
117 | #include "pathnames.h" | 117 | #include "pathnames.h" | |
118 | #include "trace.h" | 118 | #include "trace.h" | |
119 | 119 | |||
120 | /* "@(#)main.c 8.3 (Berkeley) 3/19/94" */ | 120 | /* "@(#)main.c 8.3 (Berkeley) 3/19/94" */ | |
121 | MAKE_RCSID("$NetBSD: main.c,v 1.415 2020/10/31 09:23:38 rillig Exp $"); | 121 | MAKE_RCSID("$NetBSD: main.c,v 1.416 2020/10/31 09:27:19 rillig Exp $"); | |
122 | #if defined(MAKE_NATIVE) && !defined(lint) | 122 | #if defined(MAKE_NATIVE) && !defined(lint) | |
123 | __COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993 " | 123 | __COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993 " | |
124 | "The Regents of the University of California. " | 124 | "The Regents of the University of California. " | |
125 | "All rights reserved."); | 125 | "All rights reserved."); | |
126 | #endif | 126 | #endif | |
127 | 127 | |||
128 | #ifndef DEFMAXLOCAL | 128 | #ifndef DEFMAXLOCAL | |
129 | #define DEFMAXLOCAL DEFMAXJOBS | 129 | #define DEFMAXLOCAL DEFMAXJOBS | |
130 | #endif | 130 | #endif | |
131 | 131 | |||
132 | CmdOpts opts; | 132 | CmdOpts opts; | |
133 | time_t now; /* Time at start of make */ | 133 | time_t now; /* Time at start of make */ | |
134 | GNode *DEFAULT; /* .DEFAULT node */ | 134 | GNode *DEFAULT; /* .DEFAULT node */ | |
135 | Boolean allPrecious; /* .PRECIOUS given on line by itself */ | 135 | Boolean allPrecious; /* .PRECIOUS given on line by itself */ | |
136 | Boolean deleteOnError; /* .DELETE_ON_ERROR: set */ | 136 | Boolean deleteOnError; /* .DELETE_ON_ERROR: set */ | |
137 | 137 | |||
138 | static int maxJobTokens; /* -j argument */ | 138 | static int maxJobTokens; /* -j argument */ | |
139 | Boolean enterFlagObj; /* -w and objdir != srcdir */ | 139 | Boolean enterFlagObj; /* -w and objdir != srcdir */ | |
140 | 140 | |||
141 | Boolean oldVars; /* variable substitution style */ | 141 | Boolean oldVars; /* variable substitution style */ | |
142 | static int jp_0 = -1, jp_1 = -1; /* ends of parent job pipe */ | 142 | static int jp_0 = -1, jp_1 = -1; /* ends of parent job pipe */ | |
143 | Boolean doing_depend; /* Set while reading .depend */ | 143 | Boolean doing_depend; /* Set while reading .depend */ | |
144 | static Boolean jobsRunning; /* TRUE if the jobs might be running */ | 144 | static Boolean jobsRunning; /* TRUE if the jobs might be running */ | |
145 | static const char * tracefile; | 145 | static const char * tracefile; | |
146 | static int ReadMakefile(const char *); | 146 | static int ReadMakefile(const char *); | |
147 | static void usage(void) MAKE_ATTR_DEAD; | 147 | static void usage(void) MAKE_ATTR_DEAD; | |
148 | static void purge_cached_realpaths(void); | 148 | static void purge_cached_realpaths(void); | |
149 | 149 | |||
150 | static Boolean ignorePWD; /* if we use -C, PWD is meaningless */ | 150 | static Boolean ignorePWD; /* if we use -C, PWD is meaningless */ | |
151 | static char objdir[MAXPATHLEN + 1]; /* where we chdir'ed to */ | 151 | static char objdir[MAXPATHLEN + 1]; /* where we chdir'ed to */ | |
152 | char curdir[MAXPATHLEN + 1]; /* Startup directory */ | 152 | char curdir[MAXPATHLEN + 1]; /* Startup directory */ | |
153 | char *progname; /* the program name */ | 153 | char *progname; /* the program name */ | |
154 | char *makeDependfile; | 154 | char *makeDependfile; | |
155 | pid_t myPid; | 155 | pid_t myPid; | |
156 | int makelevel; | 156 | int makelevel; | |
157 | 157 | |||
158 | Boolean forceJobs = FALSE; | 158 | Boolean forceJobs = FALSE; | |
159 | 159 | |||
160 | extern SearchPath *parseIncPath; | 160 | extern SearchPath *parseIncPath; | |
161 | 161 | |||
162 | /* | 162 | /* | |
163 | * For compatibility with the POSIX version of MAKEFLAGS that includes | 163 | * For compatibility with the POSIX version of MAKEFLAGS that includes | |
164 | * all the options with out -, convert flags to -f -l -a -g -s. | 164 | * all the options with out -, convert flags to -f -l -a -g -s. | |
165 | */ | 165 | */ | |
166 | static char * | 166 | static char * | |
167 | explode(const char *flags) | 167 | explode(const char *flags) | |
168 | { | 168 | { | |
169 | size_t len; | 169 | size_t len; | |
170 | char *nf, *st; | 170 | char *nf, *st; | |
171 | const char *f; | 171 | const char *f; | |
172 | 172 | |||
173 | if (flags == NULL) | 173 | if (flags == NULL) | |
174 | return NULL; | 174 | return NULL; | |
175 | 175 | |||
176 | for (f = flags; *f; f++) | 176 | for (f = flags; *f; f++) | |
177 | if (!ch_isalpha(*f)) | 177 | if (!ch_isalpha(*f)) | |
178 | break; | 178 | break; | |
179 | 179 | |||
180 | if (*f) | 180 | if (*f) | |
181 | return bmake_strdup(flags); | 181 | return bmake_strdup(flags); | |
182 | 182 | |||
183 | len = strlen(flags); | 183 | len = strlen(flags); | |
184 | st = nf = bmake_malloc(len * 3 + 1); | 184 | st = nf = bmake_malloc(len * 3 + 1); | |
185 | while (*flags) { | 185 | while (*flags) { | |
186 | *nf++ = '-'; | 186 | *nf++ = '-'; | |
187 | *nf++ = *flags++; | 187 | *nf++ = *flags++; | |
188 | *nf++ = ' '; | 188 | *nf++ = ' '; | |
189 | } | 189 | } | |
190 | *nf = '\0'; | 190 | *nf = '\0'; | |
191 | return st; | 191 | return st; | |
192 | } | 192 | } | |
193 | 193 | |||
194 | static void | 194 | static void | |
195 | parse_debug_option_F(const char *modules) | 195 | parse_debug_option_F(const char *modules) | |
196 | { | 196 | { | |
197 | const char *mode; | 197 | const char *mode; | |
198 | size_t len; | 198 | size_t len; | |
199 | char *fname; | 199 | char *fname; | |
200 | 200 | |||
201 | if (opts.debug_file != stdout && opts.debug_file != stderr) | 201 | if (opts.debug_file != stdout && opts.debug_file != stderr) | |
202 | fclose(opts.debug_file); | 202 | fclose(opts.debug_file); | |
203 | 203 | |||
204 | if (*modules == '+') { | 204 | if (*modules == '+') { | |
205 | modules++; | 205 | modules++; | |
206 | mode = "a"; | 206 | mode = "a"; | |
207 | } else | 207 | } else | |
208 | mode = "w"; | 208 | mode = "w"; | |
209 | 209 | |||
210 | if (strcmp(modules, "stdout") == 0) { | 210 | if (strcmp(modules, "stdout") == 0) { | |
211 | opts.debug_file = stdout; | 211 | opts.debug_file = stdout; | |
212 | return; | 212 | return; | |
213 | } | 213 | } | |
214 | if (strcmp(modules, "stderr") == 0) { | 214 | if (strcmp(modules, "stderr") == 0) { | |
215 | opts.debug_file = stderr; | 215 | opts.debug_file = stderr; | |
216 | return; | 216 | return; | |
217 | } | 217 | } | |
218 | 218 | |||
219 | len = strlen(modules); | 219 | len = strlen(modules); | |
220 | fname = bmake_malloc(len + 20); | 220 | fname = bmake_malloc(len + 20); | |
221 | memcpy(fname, modules, len + 1); | 221 | memcpy(fname, modules, len + 1); | |
222 | 222 | |||
223 | /* Let the filename be modified by the pid */ | 223 | /* Let the filename be modified by the pid */ | |
224 | if (strcmp(fname + len - 3, ".%d") == 0) | 224 | if (strcmp(fname + len - 3, ".%d") == 0) | |
225 | snprintf(fname + len - 2, 20, "%d", getpid()); | 225 | snprintf(fname + len - 2, 20, "%d", getpid()); | |
226 | 226 | |||
227 | opts.debug_file = fopen(fname, mode); | 227 | opts.debug_file = fopen(fname, mode); | |
228 | if (!opts.debug_file) { | 228 | if (!opts.debug_file) { | |
229 | fprintf(stderr, "Cannot open debug file %s\n", | 229 | fprintf(stderr, "Cannot open debug file %s\n", | |
230 | fname); | 230 | fname); | |
231 | usage(); | 231 | usage(); | |
232 | } | 232 | } | |
233 | free(fname); | 233 | free(fname); | |
234 | } | 234 | } | |
235 | 235 | |||
236 | static void | 236 | static void | |
237 | parse_debug_options(const char *argvalue) | 237 | parse_debug_options(const char *argvalue) | |
238 | { | 238 | { | |
239 | const char *modules; | 239 | const char *modules; | |
240 | 240 | |||
241 | for (modules = argvalue; *modules; ++modules) { | 241 | for (modules = argvalue; *modules; ++modules) { | |
242 | switch (*modules) { | 242 | switch (*modules) { | |
243 | case '0': /* undocumented, only intended for tests */ | 243 | case '0': /* undocumented, only intended for tests */ | |
244 | opts.debug &= DEBUG_LINT; | 244 | opts.debug &= DEBUG_LINT; | |
245 | break; | 245 | break; | |
246 | case 'A': | 246 | case 'A': | |
247 | opts.debug = ~(0|DEBUG_LINT); | 247 | opts.debug = ~(0|DEBUG_LINT); | |
248 | break; | 248 | break; | |
249 | case 'a': | 249 | case 'a': | |
250 | opts.debug |= DEBUG_ARCH; | 250 | opts.debug |= DEBUG_ARCH; | |
251 | break; | 251 | break; | |
252 | case 'C': | 252 | case 'C': | |
253 | opts.debug |= DEBUG_CWD; | 253 | opts.debug |= DEBUG_CWD; | |
254 | break; | 254 | break; | |
255 | case 'c': | 255 | case 'c': | |
256 | opts.debug |= DEBUG_COND; | 256 | opts.debug |= DEBUG_COND; | |
257 | break; | 257 | break; | |
258 | case 'd': | 258 | case 'd': | |
259 | opts.debug |= DEBUG_DIR; | 259 | opts.debug |= DEBUG_DIR; | |
260 | break; | 260 | break; | |
261 | case 'e': | 261 | case 'e': | |
262 | opts.debug |= DEBUG_ERROR; | 262 | opts.debug |= DEBUG_ERROR; | |
263 | break; | 263 | break; | |
264 | case 'f': | 264 | case 'f': | |
265 | opts.debug |= DEBUG_FOR; | 265 | opts.debug |= DEBUG_FOR; | |
266 | break; | 266 | break; | |
267 | case 'g': | 267 | case 'g': | |
268 | if (modules[1] == '1') { | 268 | if (modules[1] == '1') { | |
269 | opts.debug |= DEBUG_GRAPH1; | 269 | opts.debug |= DEBUG_GRAPH1; | |
270 | ++modules; | 270 | ++modules; | |
271 | } | 271 | } | |
272 | else if (modules[1] == '2') { | 272 | else if (modules[1] == '2') { | |
273 | opts.debug |= DEBUG_GRAPH2; | 273 | opts.debug |= DEBUG_GRAPH2; | |
274 | ++modules; | 274 | ++modules; | |
275 | } | 275 | } | |
276 | else if (modules[1] == '3') { | 276 | else if (modules[1] == '3') { | |
277 | opts.debug |= DEBUG_GRAPH3; | 277 | opts.debug |= DEBUG_GRAPH3; | |
278 | ++modules; | 278 | ++modules; | |
279 | } | 279 | } | |
280 | break; | 280 | break; | |
281 | case 'h': | 281 | case 'h': | |
282 | opts.debug |= DEBUG_HASH; | 282 | opts.debug |= DEBUG_HASH; | |
283 | break; | 283 | break; | |
284 | case 'j': | 284 | case 'j': | |
285 | opts.debug |= DEBUG_JOB; | 285 | opts.debug |= DEBUG_JOB; | |
286 | break; | 286 | break; | |
287 | case 'L': | 287 | case 'L': | |
288 | opts.debug |= DEBUG_LINT; | 288 | opts.debug |= DEBUG_LINT; | |
289 | break; | 289 | break; | |
290 | case 'l': | 290 | case 'l': | |
291 | opts.debug |= DEBUG_LOUD; | 291 | opts.debug |= DEBUG_LOUD; | |
292 | break; | 292 | break; | |
293 | case 'M': | 293 | case 'M': | |
294 | opts.debug |= DEBUG_META; | 294 | opts.debug |= DEBUG_META; | |
295 | break; | 295 | break; | |
296 | case 'm': | 296 | case 'm': | |
297 | opts.debug |= DEBUG_MAKE; | 297 | opts.debug |= DEBUG_MAKE; | |
298 | break; | 298 | break; | |
299 | case 'n': | 299 | case 'n': | |
300 | opts.debug |= DEBUG_SCRIPT; | 300 | opts.debug |= DEBUG_SCRIPT; | |
301 | break; | 301 | break; | |
302 | case 'p': | 302 | case 'p': | |
303 | opts.debug |= DEBUG_PARSE; | 303 | opts.debug |= DEBUG_PARSE; | |
304 | break; | 304 | break; | |
305 | case 's': | 305 | case 's': | |
306 | opts.debug |= DEBUG_SUFF; | 306 | opts.debug |= DEBUG_SUFF; | |
307 | break; | 307 | break; | |
308 | case 't': | 308 | case 't': | |
309 | opts.debug |= DEBUG_TARG; | 309 | opts.debug |= DEBUG_TARG; | |
310 | break; | 310 | break; | |
311 | case 'V': | 311 | case 'V': | |
312 | opts.debugVflag = TRUE; | 312 | opts.debugVflag = TRUE; | |
313 | break; | 313 | break; | |
314 | case 'v': | 314 | case 'v': | |
315 | opts.debug |= DEBUG_VAR; | 315 | opts.debug |= DEBUG_VAR; | |
316 | break; | 316 | break; | |
317 | case 'x': | 317 | case 'x': | |
318 | opts.debug |= DEBUG_SHELL; | 318 | opts.debug |= DEBUG_SHELL; | |
319 | break; | 319 | break; | |
320 | case 'F': | 320 | case 'F': | |
321 | parse_debug_option_F(modules + 1); | 321 | parse_debug_option_F(modules + 1); | |
322 | goto debug_setbuf; | 322 | goto debug_setbuf; | |
323 | default: | 323 | default: | |
324 | (void)fprintf(stderr, | 324 | (void)fprintf(stderr, | |
325 | "%s: illegal argument to d option -- %c\n", | 325 | "%s: illegal argument to d option -- %c\n", | |
326 | progname, *modules); | 326 | progname, *modules); | |
327 | usage(); | 327 | usage(); | |
328 | } | 328 | } | |
329 | } | 329 | } | |
330 | debug_setbuf: | 330 | debug_setbuf: | |
331 | /* | 331 | /* | |
332 | * Make the debug_file unbuffered, and make | 332 | * Make the debug_file unbuffered, and make | |
333 | * stdout line buffered (unless debugfile == stdout). | 333 | * stdout line buffered (unless debugfile == stdout). | |
334 | */ | 334 | */ | |
335 | setvbuf(opts.debug_file, NULL, _IONBF, 0); | 335 | setvbuf(opts.debug_file, NULL, _IONBF, 0); | |
336 | if (opts.debug_file != stdout) { | 336 | if (opts.debug_file != stdout) { | |
337 | setvbuf(stdout, NULL, _IOLBF, 0); | 337 | setvbuf(stdout, NULL, _IOLBF, 0); | |
338 | } | 338 | } | |
339 | } | 339 | } | |
340 | 340 | |||
341 | /* | 341 | /* | |
342 | * does path contain any relative components | 342 | * does path contain any relative components | |
343 | */ | 343 | */ | |
344 | static Boolean | 344 | static Boolean | |
345 | is_relpath(const char *path) | 345 | is_relpath(const char *path) | |
346 | { | 346 | { | |
347 | const char *cp; | 347 | const char *cp; | |
348 | 348 | |||
349 | if (path[0] != '/') | 349 | if (path[0] != '/') | |
350 | return TRUE; | 350 | return TRUE; | |
351 | cp = path; | 351 | cp = path; | |
352 | while ((cp = strstr(cp, "/.")) != NULL) { | 352 | while ((cp = strstr(cp, "/.")) != NULL) { | |
353 | cp += 2; | 353 | cp += 2; | |
354 | if (cp[0] == '/' || cp[0] == '\0') | 354 | if (cp[0] == '/' || cp[0] == '\0') | |
355 | return TRUE; | 355 | return TRUE; | |
356 | else if (cp[0] == '.') { | 356 | else if (cp[0] == '.') { | |
357 | if (cp[1] == '/' || cp[1] == '\0') | 357 | if (cp[1] == '/' || cp[1] == '\0') | |
358 | return TRUE; | 358 | return TRUE; | |
359 | } | 359 | } | |
360 | } | 360 | } | |
361 | return FALSE; | 361 | return FALSE; | |
362 | } | 362 | } | |
363 | 363 | |||
364 | static void | 364 | static void | |
365 | MainParseArgChdir(const char *argvalue) | 365 | MainParseArgChdir(const char *argvalue) | |
366 | { | 366 | { | |
367 | struct stat sa, sb; | 367 | struct stat sa, sb; | |
368 | 368 | |||
369 | if (chdir(argvalue) == -1) { | 369 | if (chdir(argvalue) == -1) { | |
370 | (void)fprintf(stderr, "%s: chdir %s: %s\n", | 370 | (void)fprintf(stderr, "%s: chdir %s: %s\n", | |
371 | progname, argvalue, strerror(errno)); | 371 | progname, argvalue, strerror(errno)); | |
372 | exit(1); | 372 | exit(1); | |
373 | } | 373 | } | |
374 | if (getcwd(curdir, MAXPATHLEN) == NULL) { | 374 | if (getcwd(curdir, MAXPATHLEN) == NULL) { | |
375 | (void)fprintf(stderr, "%s: %s.\n", progname, strerror(errno)); | 375 | (void)fprintf(stderr, "%s: %s.\n", progname, strerror(errno)); | |
376 | exit(2); | 376 | exit(2); | |
377 | } | 377 | } | |
378 | if (!is_relpath(argvalue) && | 378 | if (!is_relpath(argvalue) && | |
379 | stat(argvalue, &sa) != -1 && | 379 | stat(argvalue, &sa) != -1 && | |
380 | stat(curdir, &sb) != -1 && | 380 | stat(curdir, &sb) != -1 && | |
381 | sa.st_ino == sb.st_ino && | 381 | sa.st_ino == sb.st_ino && | |
382 | sa.st_dev == sb.st_dev) | 382 | sa.st_dev == sb.st_dev) | |
383 | strncpy(curdir, argvalue, MAXPATHLEN); | 383 | strncpy(curdir, argvalue, MAXPATHLEN); | |
384 | ignorePWD = TRUE; | 384 | ignorePWD = TRUE; | |
385 | } | 385 | } | |
386 | 386 | |||
387 | static void | 387 | static void | |
388 | MainParseArgJobsInternal(const char *argvalue) | 388 | MainParseArgJobsInternal(const char *argvalue) | |
389 | { | 389 | { | |
390 | if (sscanf(argvalue, "%d,%d", &jp_0, &jp_1) != 2) { | 390 | if (sscanf(argvalue, "%d,%d", &jp_0, &jp_1) != 2) { | |
391 | (void)fprintf(stderr, | 391 | (void)fprintf(stderr, | |
392 | "%s: internal error -- J option malformed (%s)\n", | 392 | "%s: internal error -- J option malformed (%s)\n", | |
393 | progname, argvalue); | 393 | progname, argvalue); | |
394 | usage(); | 394 | usage(); | |
395 | } | 395 | } | |
396 | if ((fcntl(jp_0, F_GETFD, 0) < 0) || | 396 | if ((fcntl(jp_0, F_GETFD, 0) < 0) || | |
397 | (fcntl(jp_1, F_GETFD, 0) < 0)) { | 397 | (fcntl(jp_1, F_GETFD, 0) < 0)) { | |
398 | #if 0 | 398 | #if 0 | |
399 | (void)fprintf(stderr, | 399 | (void)fprintf(stderr, | |
400 | "%s: ###### warning -- J descriptors were closed!\n", | 400 | "%s: ###### warning -- J descriptors were closed!\n", | |
401 | progname); | 401 | progname); | |
402 | exit(2); | 402 | exit(2); | |
403 | #endif | 403 | #endif | |
404 | jp_0 = -1; | 404 | jp_0 = -1; | |
405 | jp_1 = -1; | 405 | jp_1 = -1; | |
406 | opts.compatMake = TRUE; | 406 | opts.compatMake = TRUE; | |
407 | } else { | 407 | } else { | |
408 | Var_Append(MAKEFLAGS, "-J", VAR_GLOBAL); | 408 | Var_Append(MAKEFLAGS, "-J", VAR_GLOBAL); | |
409 | Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); | 409 | Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); | |
410 | } | 410 | } | |
411 | } | 411 | } | |
412 | 412 | |||
413 | static void | 413 | static void | |
414 | MainParseArgJobs(const char *argvalue) | 414 | MainParseArgJobs(const char *argvalue) | |
415 | { | 415 | { | |
416 | char *p; | 416 | char *p; | |
417 | 417 | |||
418 | forceJobs = TRUE; | 418 | forceJobs = TRUE; | |
419 | opts.maxJobs = (int)strtol(argvalue, &p, 0); | 419 | opts.maxJobs = (int)strtol(argvalue, &p, 0); | |
420 | if (*p != '\0' || opts.maxJobs < 1) { | 420 | if (*p != '\0' || opts.maxJobs < 1) { | |
421 | (void)fprintf(stderr, | 421 | (void)fprintf(stderr, | |
422 | "%s: illegal argument to -j -- must be positive integer!\n", | 422 | "%s: illegal argument to -j -- must be positive integer!\n", | |
423 | progname); | 423 | progname); | |
424 | exit(1); /* XXX: why not 2? */ | 424 | exit(1); /* XXX: why not 2? */ | |
425 | } | 425 | } | |
426 | Var_Append(MAKEFLAGS, "-j", VAR_GLOBAL); | 426 | Var_Append(MAKEFLAGS, "-j", VAR_GLOBAL); | |
427 | Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); | 427 | Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); | |
428 | Var_Set(".MAKE.JOBS", argvalue, VAR_GLOBAL); | 428 | Var_Set(".MAKE.JOBS", argvalue, VAR_GLOBAL); | |
429 | maxJobTokens = opts.maxJobs; | 429 | maxJobTokens = opts.maxJobs; | |
430 | } | 430 | } | |
431 | 431 | |||
432 | static void | 432 | static void | |
433 | MainParseArgSysInc(const char *argvalue) | 433 | MainParseArgSysInc(const char *argvalue) | |
434 | { | 434 | { | |
435 | /* look for magic parent directory search string */ | 435 | /* look for magic parent directory search string */ | |
436 | if (strncmp(".../", argvalue, 4) == 0) { | 436 | if (strncmp(".../", argvalue, 4) == 0) { | |
437 | char *found_path = Dir_FindHereOrAbove(curdir, argvalue + 4); | 437 | char *found_path = Dir_FindHereOrAbove(curdir, argvalue + 4); | |
438 | if (found_path == NULL) | 438 | if (found_path == NULL) | |
439 | return; | 439 | return; | |
440 | (void)Dir_AddDir(sysIncPath, found_path); | 440 | (void)Dir_AddDir(sysIncPath, found_path); | |
441 | free(found_path); | 441 | free(found_path); | |
442 | } else { | 442 | } else { | |
443 | (void)Dir_AddDir(sysIncPath, argvalue); | 443 | (void)Dir_AddDir(sysIncPath, argvalue); | |
444 | } | 444 | } | |
445 | Var_Append(MAKEFLAGS, "-m", VAR_GLOBAL); | 445 | Var_Append(MAKEFLAGS, "-m", VAR_GLOBAL); | |
446 | Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); | 446 | Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); | |
447 | } | 447 | } | |
448 | 448 | |||
449 | static Boolean | 449 | static Boolean | |
450 | MainParseArg(char c, const char *argvalue) | 450 | MainParseArg(char c, const char *argvalue) | |
451 | { | 451 | { | |
452 | switch (c) { | 452 | switch (c) { | |
453 | case '\0': | 453 | case '\0': | |
454 | break; | 454 | break; | |
455 | case 'B': | 455 | case 'B': | |
456 | opts.compatMake = TRUE; | 456 | opts.compatMake = TRUE; | |
457 | Var_Append(MAKEFLAGS, "-B", VAR_GLOBAL); | 457 | Var_Append(MAKEFLAGS, "-B", VAR_GLOBAL); | |
458 | Var_Set(MAKE_MODE, "compat", VAR_GLOBAL); | 458 | Var_Set(MAKE_MODE, "compat", VAR_GLOBAL); | |
459 | break; | 459 | break; | |
460 | case 'C': | 460 | case 'C': | |
461 | MainParseArgChdir(argvalue); | 461 | MainParseArgChdir(argvalue); | |
462 | break; | 462 | break; | |
463 | case 'D': | 463 | case 'D': | |
464 | if (argvalue[0] == '\0') return FALSE; | 464 | if (argvalue[0] == '\0') return FALSE; | |
465 | Var_Set(argvalue, "1", VAR_GLOBAL); | 465 | Var_Set(argvalue, "1", VAR_GLOBAL); | |
466 | Var_Append(MAKEFLAGS, "-D", VAR_GLOBAL); | 466 | Var_Append(MAKEFLAGS, "-D", VAR_GLOBAL); | |
467 | Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); | 467 | Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); | |
468 | break; | 468 | break; | |
469 | case 'I': | 469 | case 'I': | |
470 | Parse_AddIncludeDir(argvalue); | 470 | Parse_AddIncludeDir(argvalue); | |
471 | Var_Append(MAKEFLAGS, "-I", VAR_GLOBAL); | 471 | Var_Append(MAKEFLAGS, "-I", VAR_GLOBAL); | |
472 | Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); | 472 | Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); | |
473 | break; | 473 | break; | |
474 | case 'J': | 474 | case 'J': | |
475 | MainParseArgJobsInternal(argvalue); | 475 | MainParseArgJobsInternal(argvalue); | |
476 | break; | 476 | break; | |
477 | case 'N': | 477 | case 'N': | |
478 | opts.noExecute = TRUE; | 478 | opts.noExecute = TRUE; | |
479 | opts.noRecursiveExecute = TRUE; | 479 | opts.noRecursiveExecute = TRUE; | |
480 | Var_Append(MAKEFLAGS, "-N", VAR_GLOBAL); | 480 | Var_Append(MAKEFLAGS, "-N", VAR_GLOBAL); | |
481 | break; | 481 | break; | |
482 | case 'S': | 482 | case 'S': | |
483 | opts.keepgoing = FALSE; | 483 | opts.keepgoing = FALSE; | |
484 | Var_Append(MAKEFLAGS, "-S", VAR_GLOBAL); | 484 | Var_Append(MAKEFLAGS, "-S", VAR_GLOBAL); | |
485 | break; | 485 | break; | |
486 | case 'T': | 486 | case 'T': | |
487 | tracefile = bmake_strdup(argvalue); | 487 | tracefile = bmake_strdup(argvalue); | |
488 | Var_Append(MAKEFLAGS, "-T", VAR_GLOBAL); | 488 | Var_Append(MAKEFLAGS, "-T", VAR_GLOBAL); | |
489 | Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); | 489 | Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); | |
490 | break; | 490 | break; | |
491 | case 'V': | 491 | case 'V': | |
492 | case 'v': | 492 | case 'v': | |
493 | opts.printVars = c == 'v' ? EXPAND_VARS : COMPAT_VARS; | 493 | opts.printVars = c == 'v' ? EXPAND_VARS : COMPAT_VARS; | |
494 | Lst_Append(opts.variables, bmake_strdup(argvalue)); | 494 | Lst_Append(opts.variables, bmake_strdup(argvalue)); | |
495 | /* XXX: Why always -V? */ | 495 | /* XXX: Why always -V? */ | |
496 | Var_Append(MAKEFLAGS, "-V", VAR_GLOBAL); | 496 | Var_Append(MAKEFLAGS, "-V", VAR_GLOBAL); | |
497 | Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); | 497 | Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); | |
498 | break; | 498 | break; | |
499 | case 'W': | 499 | case 'W': | |
500 | opts.parseWarnFatal = TRUE; | 500 | opts.parseWarnFatal = TRUE; | |
501 | break; | 501 | break; | |
502 | case 'X': | 502 | case 'X': | |
503 | opts.varNoExportEnv = TRUE; | 503 | opts.varNoExportEnv = TRUE; | |
504 | Var_Append(MAKEFLAGS, "-X", VAR_GLOBAL); | 504 | Var_Append(MAKEFLAGS, "-X", VAR_GLOBAL); | |
505 | break; | 505 | break; | |
506 | case 'd': | 506 | case 'd': | |
507 | /* If '-d-opts' don't pass to children */ | 507 | /* If '-d-opts' don't pass to children */ | |
508 | if (argvalue[0] == '-') | 508 | if (argvalue[0] == '-') | |
509 | argvalue++; | 509 | argvalue++; | |
510 | else { | 510 | else { | |
511 | Var_Append(MAKEFLAGS, "-d", VAR_GLOBAL); | 511 | Var_Append(MAKEFLAGS, "-d", VAR_GLOBAL); | |
512 | Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); | 512 | Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); | |
513 | } | 513 | } | |
514 | parse_debug_options(argvalue); | 514 | parse_debug_options(argvalue); | |
515 | break; | 515 | break; | |
516 | case 'e': | 516 | case 'e': | |
517 | opts.checkEnvFirst = TRUE; | 517 | opts.checkEnvFirst = TRUE; | |
518 | Var_Append(MAKEFLAGS, "-e", VAR_GLOBAL); | 518 | Var_Append(MAKEFLAGS, "-e", VAR_GLOBAL); | |
519 | break; | 519 | break; | |
520 | case 'f': | 520 | case 'f': | |
521 | Lst_Append(opts.makefiles, bmake_strdup(argvalue)); | 521 | Lst_Append(opts.makefiles, bmake_strdup(argvalue)); | |
522 | break; | 522 | break; | |
523 | case 'i': | 523 | case 'i': | |
524 | opts.ignoreErrors = TRUE; | 524 | opts.ignoreErrors = TRUE; | |
525 | Var_Append(MAKEFLAGS, "-i", VAR_GLOBAL); | 525 | Var_Append(MAKEFLAGS, "-i", VAR_GLOBAL); | |
526 | break; | 526 | break; | |
527 | case 'j': | 527 | case 'j': | |
528 | MainParseArgJobs(argvalue); | 528 | MainParseArgJobs(argvalue); | |
529 | break; | 529 | break; | |
530 | case 'k': | 530 | case 'k': | |
531 | opts.keepgoing = TRUE; | 531 | opts.keepgoing = TRUE; | |
532 | Var_Append(MAKEFLAGS, "-k", VAR_GLOBAL); | 532 | Var_Append(MAKEFLAGS, "-k", VAR_GLOBAL); | |
533 | break; | 533 | break; | |
534 | case 'm': | 534 | case 'm': | |
535 | MainParseArgSysInc(argvalue); | 535 | MainParseArgSysInc(argvalue); | |
536 | break; | 536 | break; | |
537 | case 'n': | 537 | case 'n': | |
538 | opts.noExecute = TRUE; | 538 | opts.noExecute = TRUE; | |
539 | Var_Append(MAKEFLAGS, "-n", VAR_GLOBAL); | 539 | Var_Append(MAKEFLAGS, "-n", VAR_GLOBAL); | |
540 | break; | 540 | break; | |
541 | case 'q': | 541 | case 'q': | |
542 | opts.queryFlag = TRUE; | 542 | opts.queryFlag = TRUE; | |
543 | /* Kind of nonsensical, wot? */ | 543 | /* Kind of nonsensical, wot? */ | |
544 | Var_Append(MAKEFLAGS, "-q", VAR_GLOBAL); | 544 | Var_Append(MAKEFLAGS, "-q", VAR_GLOBAL); | |
545 | break; | 545 | break; | |
546 | case 'r': | 546 | case 'r': | |
547 | opts.noBuiltins = TRUE; | 547 | opts.noBuiltins = TRUE; | |
548 | Var_Append(MAKEFLAGS, "-r", VAR_GLOBAL); | 548 | Var_Append(MAKEFLAGS, "-r", VAR_GLOBAL); | |
549 | break; | 549 | break; | |
550 | case 's': | 550 | case 's': | |
551 | opts.beSilent = TRUE; | 551 | opts.beSilent = TRUE; | |
552 | Var_Append(MAKEFLAGS, "-s", VAR_GLOBAL); | 552 | Var_Append(MAKEFLAGS, "-s", VAR_GLOBAL); | |
553 | break; | 553 | break; | |
554 | case 't': | 554 | case 't': | |
555 | opts.touchFlag = TRUE; | 555 | opts.touchFlag = TRUE; | |
556 | Var_Append(MAKEFLAGS, "-t", VAR_GLOBAL); | 556 | Var_Append(MAKEFLAGS, "-t", VAR_GLOBAL); | |
557 | break; | 557 | break; | |
558 | case 'w': | 558 | case 'w': | |
559 | opts.enterFlag = TRUE; | 559 | opts.enterFlag = TRUE; | |
560 | Var_Append(MAKEFLAGS, "-w", VAR_GLOBAL); | 560 | Var_Append(MAKEFLAGS, "-w", VAR_GLOBAL); | |
561 | break; | 561 | break; | |
562 | default: | 562 | default: | |
563 | case '?': | 563 | case '?': | |
564 | usage(); | 564 | usage(); | |
565 | } | 565 | } | |
566 | return TRUE; | 566 | return TRUE; | |
567 | } | 567 | } | |
568 | 568 | |||
569 | /* Parse the given arguments. Called from main() and from | 569 | /* Parse the given arguments. Called from main() and from | |
570 | * Main_ParseArgLine() when the .MAKEFLAGS target is used. | 570 | * Main_ParseArgLine() when the .MAKEFLAGS target is used. | |
571 | * | 571 | * | |
572 | * The arguments must be treated as read-only and will be freed after the | 572 | * The arguments must be treated as read-only and will be freed after the | |
573 | * call. | 573 | * call. | |
574 | * | 574 | * | |
575 | * XXX: Deal with command line overriding .MAKEFLAGS in makefile */ | 575 | * XXX: Deal with command line overriding .MAKEFLAGS in makefile */ | |
576 | static void | 576 | static void | |
577 | MainParseArgs(int argc, char **argv) | 577 | MainParseArgs(int argc, char **argv) | |
578 | { | 578 | { | |
579 | char c; | 579 | char c; | |
580 | int arginc; | 580 | int arginc; | |
581 | char *argvalue; | 581 | char *argvalue; | |
582 | char *optscan; | 582 | char *optscan; | |
583 | Boolean inOption, dashDash = FALSE; | 583 | Boolean inOption, dashDash = FALSE; | |
584 | 584 | |||
585 | const char *optspecs = "BC:D:I:J:NST:V:WXd:ef:ij:km:nqrstv:w"; | 585 | const char *optspecs = "BC:D:I:J:NST:V:WXd:ef:ij:km:nqrstv:w"; | |
586 | /* Can't actually use getopt(3) because rescanning is not portable */ | 586 | /* Can't actually use getopt(3) because rescanning is not portable */ | |
587 | 587 | |||
588 | rearg: | 588 | rearg: | |
589 | inOption = FALSE; | 589 | inOption = FALSE; | |
590 | optscan = NULL; | 590 | optscan = NULL; | |
591 | while (argc > 1) { | 591 | while (argc > 1) { | |
592 | const char *optspec; | 592 | const char *optspec; | |
593 | if (!inOption) | 593 | if (!inOption) | |
594 | optscan = argv[1]; | 594 | optscan = argv[1]; | |
595 | c = *optscan++; | 595 | c = *optscan++; | |
596 | arginc = 0; | 596 | arginc = 0; | |
597 | if (inOption) { | 597 | if (inOption) { | |
598 | if (c == '\0') { | 598 | if (c == '\0') { | |
599 | ++argv; | 599 | ++argv; | |
600 | --argc; | 600 | --argc; | |
601 | inOption = FALSE; | 601 | inOption = FALSE; | |
602 | continue; | 602 | continue; | |
603 | } | 603 | } | |
604 | } else { | 604 | } else { | |
605 | if (c != '-' || dashDash) | 605 | if (c != '-' || dashDash) | |
606 | break; | 606 | break; | |
607 | inOption = TRUE; | 607 | inOption = TRUE; | |
608 | c = *optscan++; | 608 | c = *optscan++; | |
609 | } | 609 | } | |
610 | /* '-' found at some earlier point */ | 610 | /* '-' found at some earlier point */ | |
611 | optspec = strchr(optspecs, c); | 611 | optspec = strchr(optspecs, c); | |
612 | if (c != '\0' && optspec != NULL && optspec[1] == ':') { | 612 | if (c != '\0' && optspec != NULL && optspec[1] == ':') { | |
613 | /* -<something> found, and <something> should have an arg */ | 613 | /* -<something> found, and <something> should have an arg */ | |
614 | inOption = FALSE; | 614 | inOption = FALSE; | |
615 | arginc = 1; | 615 | arginc = 1; | |
616 | argvalue = optscan; | 616 | argvalue = optscan; | |
617 | if (*argvalue == '\0') { | 617 | if (*argvalue == '\0') { | |
618 | if (argc < 3) | 618 | if (argc < 3) | |
619 | goto noarg; | 619 | goto noarg; | |
620 | argvalue = argv[2]; | 620 | argvalue = argv[2]; | |
621 | arginc = 2; | 621 | arginc = 2; | |
622 | } | 622 | } | |
623 | } else { | 623 | } else { | |
624 | argvalue = NULL; | 624 | argvalue = NULL; | |
625 | } | 625 | } | |
626 | switch (c) { | 626 | switch (c) { | |
627 | case '\0': | 627 | case '\0': | |
628 | arginc = 1; | 628 | arginc = 1; | |
629 | inOption = FALSE; | 629 | inOption = FALSE; | |
630 | break; | 630 | break; | |
631 | case '-': | 631 | case '-': | |
632 | dashDash = TRUE; | 632 | dashDash = TRUE; | |
633 | break; | 633 | break; | |
634 | default: | 634 | default: | |
635 | if (!MainParseArg(c, argvalue)) | 635 | if (!MainParseArg(c, argvalue)) | |
636 | goto noarg; | 636 | goto noarg; | |
637 | } | 637 | } | |
638 | argv += arginc; | 638 | argv += arginc; | |
639 | argc -= arginc; | 639 | argc -= arginc; | |
640 | } | 640 | } | |
641 | 641 | |||
642 | oldVars = TRUE; | 642 | oldVars = TRUE; | |
643 | 643 | |||
644 | /* | 644 | /* | |
645 | * See if the rest of the arguments are variable assignments and | 645 | * See if the rest of the arguments are variable assignments and | |
646 | * perform them if so. Else take them to be targets and stuff them | 646 | * perform them if so. Else take them to be targets and stuff them | |
647 | * on the end of the "create" list. | 647 | * on the end of the "create" list. | |
648 | */ | 648 | */ | |
649 | for (; argc > 1; ++argv, --argc) { | 649 | for (; argc > 1; ++argv, --argc) { | |
650 | VarAssign var; | 650 | VarAssign var; | |
651 | if (Parse_IsVar(argv[1], &var)) { | 651 | if (Parse_IsVar(argv[1], &var)) { | |
652 | Parse_DoVar(&var, VAR_CMDLINE); | 652 | Parse_DoVar(&var, VAR_CMDLINE); | |
653 | } else { | 653 | } else { | |
654 | if (!*argv[1]) | 654 | if (!*argv[1]) | |
655 | Punt("illegal (null) argument."); | 655 | Punt("illegal (null) argument."); | |
656 | if (*argv[1] == '-' && !dashDash) | 656 | if (*argv[1] == '-' && !dashDash) | |
657 | goto rearg; | 657 | goto rearg; | |
658 | Lst_Append(opts.create, bmake_strdup(argv[1])); | 658 | Lst_Append(opts.create, bmake_strdup(argv[1])); | |
659 | } | 659 | } | |
660 | } | 660 | } | |
661 | 661 | |||
662 | return; | 662 | return; | |
663 | noarg: | 663 | noarg: | |
664 | (void)fprintf(stderr, "%s: option requires an argument -- %c\n", | 664 | (void)fprintf(stderr, "%s: option requires an argument -- %c\n", | |
665 | progname, c); | 665 | progname, c); | |
666 | usage(); | 666 | usage(); | |
667 | } | 667 | } | |
668 | 668 | |||
669 | /* Break a line of arguments into words and parse them. | 669 | /* Break a line of arguments into words and parse them. | |
670 | * | 670 | * | |
671 | * Used when a .MFLAGS or .MAKEFLAGS target is encountered during parsing and | 671 | * Used when a .MFLAGS or .MAKEFLAGS target is encountered during parsing and | |
672 | * by main() when reading the MAKEFLAGS environment variable. */ | 672 | * by main() when reading the MAKEFLAGS environment variable. */ | |
673 | void | 673 | void | |
674 | Main_ParseArgLine(const char *line) | 674 | Main_ParseArgLine(const char *line) | |
675 | { | 675 | { | |
676 | Words words; | 676 | Words words; | |
677 | void *p1; | 677 | void *p1; | |
678 | const char *argv0 = Var_Value(".MAKE", VAR_GLOBAL, &p1); | 678 | const char *argv0 = Var_Value(".MAKE", VAR_GLOBAL, &p1); | |
679 | char *buf; | 679 | char *buf; | |
680 | 680 | |||
681 | if (line == NULL) | 681 | if (line == NULL) | |
682 | return; | 682 | return; | |
683 | for (; *line == ' '; ++line) | 683 | for (; *line == ' '; ++line) | |
684 | continue; | 684 | continue; | |
685 | if (!*line) | 685 | if (!*line) | |
686 | return; | 686 | return; | |
687 | 687 | |||
688 | buf = str_concat3(argv0, " ", line); | 688 | buf = str_concat3(argv0, " ", line); | |
689 | free(p1); | 689 | free(p1); | |
690 | 690 | |||
691 | words = Str_Words(buf, TRUE); | 691 | words = Str_Words(buf, TRUE); | |
692 | if (words.words == NULL) { | 692 | if (words.words == NULL) { | |
693 | Error("Unterminated quoted string [%s]", buf); | 693 | Error("Unterminated quoted string [%s]", buf); | |
694 | free(buf); | 694 | free(buf); | |
695 | return; | 695 | return; | |
696 | } | 696 | } | |
697 | free(buf); | 697 | free(buf); | |
698 | MainParseArgs((int)words.len, words.words); | 698 | MainParseArgs((int)words.len, words.words); | |
699 | 699 | |||
700 | Words_Free(words); | 700 | Words_Free(words); | |
701 | } | 701 | } | |
702 | 702 | |||
703 | Boolean | 703 | Boolean | |
704 | Main_SetObjdir(const char *fmt, ...) | 704 | Main_SetObjdir(const char *fmt, ...) | |
705 | { | 705 | { | |
706 | struct stat sb; | 706 | struct stat sb; | |
707 | char *path; | 707 | char *path; | |
708 | char buf[MAXPATHLEN + 1]; | 708 | char buf[MAXPATHLEN + 1]; | |
709 | char buf2[MAXPATHLEN + 1]; | 709 | char buf2[MAXPATHLEN + 1]; | |
710 | Boolean rc = FALSE; | 710 | Boolean rc = FALSE; | |
711 | va_list ap; | 711 | va_list ap; | |
712 | 712 | |||
713 | va_start(ap, fmt); | 713 | va_start(ap, fmt); | |
714 | vsnprintf(path = buf, MAXPATHLEN, fmt, ap); | 714 | vsnprintf(path = buf, MAXPATHLEN, fmt, ap); | |
715 | va_end(ap); | 715 | va_end(ap); | |
716 | 716 | |||
717 | if (path[0] != '/') { | 717 | if (path[0] != '/') { | |
718 | snprintf(buf2, MAXPATHLEN, "%s/%s", curdir, path); | 718 | snprintf(buf2, MAXPATHLEN, "%s/%s", curdir, path); | |
719 | path = buf2; | 719 | path = buf2; | |
720 | } | 720 | } | |
721 | 721 | |||
722 | /* look for the directory and try to chdir there */ | 722 | /* look for the directory and try to chdir there */ | |
723 | if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) { | 723 | if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) { | |
724 | if (access(path, W_OK) || chdir(path)) { | 724 | if (access(path, W_OK) || chdir(path)) { | |
725 | (void)fprintf(stderr, "make warning: %s: %s.\n", | 725 | (void)fprintf(stderr, "make warning: %s: %s.\n", | |
726 | path, strerror(errno)); | 726 | path, strerror(errno)); | |
727 | } else { | 727 | } else { | |
728 | snprintf(objdir, sizeof objdir, "%s", path); | 728 | snprintf(objdir, sizeof objdir, "%s", path); | |
729 | Var_Set(".OBJDIR", objdir, VAR_GLOBAL); | 729 | Var_Set(".OBJDIR", objdir, VAR_GLOBAL); | |
730 | setenv("PWD", objdir, 1); | 730 | setenv("PWD", objdir, 1); | |
731 | Dir_InitDot(); | 731 | Dir_InitDot(); | |
732 | purge_cached_realpaths(); | 732 | purge_cached_realpaths(); | |
733 | rc = TRUE; | 733 | rc = TRUE; | |
734 | if (opts.enterFlag && strcmp(objdir, curdir) != 0) | 734 | if (opts.enterFlag && strcmp(objdir, curdir) != 0) | |
735 | enterFlagObj = TRUE; | 735 | enterFlagObj = TRUE; | |
736 | } | 736 | } | |
737 | } | 737 | } | |
738 | 738 | |||
739 | return rc; | 739 | return rc; | |
740 | } | 740 | } | |
741 | 741 | |||
742 | static Boolean | 742 | static Boolean | |
743 | Main_SetVarObjdir(const char *var, const char *suffix) | 743 | Main_SetVarObjdir(const char *var, const char *suffix) | |
744 | { | 744 | { | |
745 | void *path_freeIt; | 745 | void *path_freeIt; | |
746 | const char *path = Var_Value(var, VAR_CMDLINE, &path_freeIt); | 746 | const char *path = Var_Value(var, VAR_CMDLINE, &path_freeIt); | |
747 | const char *xpath; | 747 | const char *xpath; | |
748 | char *xpath_freeIt; | 748 | char *xpath_freeIt; | |
749 | 749 | |||
750 | if (path == NULL || path[0] == '\0') { | 750 | if (path == NULL || path[0] == '\0') { | |
751 | bmake_free(path_freeIt); | 751 | bmake_free(path_freeIt); | |
752 | return FALSE; | 752 | return FALSE; | |
753 | } | 753 | } | |
754 | 754 | |||
755 | /* expand variable substitutions */ | 755 | /* expand variable substitutions */ | |
756 | xpath = path; | 756 | xpath = path; | |
757 | xpath_freeIt = NULL; | 757 | xpath_freeIt = NULL; | |
758 | if (strchr(path, '$') != 0) { | 758 | if (strchr(path, '$') != 0) { | |
759 | (void)Var_Subst(path, VAR_GLOBAL, VARE_WANTRES, &xpath_freeIt); | 759 | (void)Var_Subst(path, VAR_GLOBAL, VARE_WANTRES, &xpath_freeIt); | |
760 | /* TODO: handle errors */ | 760 | /* TODO: handle errors */ | |
761 | xpath = xpath_freeIt; | 761 | xpath = xpath_freeIt; | |
762 | } | 762 | } | |
763 | 763 | |||
764 | (void)Main_SetObjdir("%s%s", xpath, suffix); | 764 | (void)Main_SetObjdir("%s%s", xpath, suffix); | |
765 | 765 | |||
766 | bmake_free(xpath_freeIt); | 766 | bmake_free(xpath_freeIt); | |
767 | bmake_free(path_freeIt); | 767 | bmake_free(path_freeIt); | |
768 | return TRUE; | 768 | return TRUE; | |
769 | } | 769 | } | |
770 | 770 | |||
771 | /* Read and parse the makefile. | 771 | /* Read and parse the makefile. | |
772 | * Return TRUE if reading the makefile succeeded. */ | 772 | * Return TRUE if reading the makefile succeeded. */ | |
773 | static int | 773 | static int | |
774 | ReadMakefileSucceeded(void *fname, void *unused) | 774 | ReadMakefileSucceeded(void *fname, void *unused) | |
775 | { | 775 | { | |
776 | return ReadMakefile(fname) == 0; | 776 | return ReadMakefile(fname) == 0; | |
777 | } | 777 | } | |
778 | 778 | |||
779 | int | 779 | int | |
780 | str2Lst_Append(StringList *lp, char *str, const char *sep) | 780 | str2Lst_Append(StringList *lp, char *str, const char *sep) | |
781 | { | 781 | { | |
782 | char *cp; | 782 | char *cp; | |
783 | int n; | 783 | int n; | |
784 | 784 | |||
785 | if (!sep) | 785 | if (!sep) | |
786 | sep = " \t"; | 786 | sep = " \t"; | |
787 | 787 | |||
788 | for (n = 0, cp = strtok(str, sep); cp; cp = strtok(NULL, sep)) { | 788 | for (n = 0, cp = strtok(str, sep); cp; cp = strtok(NULL, sep)) { | |
789 | Lst_Append(lp, cp); | 789 | Lst_Append(lp, cp); | |
790 | n++; | 790 | n++; | |
791 | } | 791 | } | |
792 | return n; | 792 | return n; | |
793 | } | 793 | } | |
794 | 794 | |||
795 | #ifdef SIGINFO | 795 | #ifdef SIGINFO | |
796 | /*ARGSUSED*/ | 796 | /*ARGSUSED*/ | |
797 | static void | 797 | static void | |
798 | siginfo(int signo MAKE_ATTR_UNUSED) | 798 | siginfo(int signo MAKE_ATTR_UNUSED) | |
799 | { | 799 | { | |
800 | char dir[MAXPATHLEN]; | 800 | char dir[MAXPATHLEN]; | |
801 | char str[2 * MAXPATHLEN]; | 801 | char str[2 * MAXPATHLEN]; | |
802 | int len; | 802 | int len; | |
803 | if (getcwd(dir, sizeof(dir)) == NULL) | 803 | if (getcwd(dir, sizeof(dir)) == NULL) | |
804 | return; | 804 | return; | |
805 | len = snprintf(str, sizeof(str), "%s: Working in: %s\n", progname, dir); | 805 | len = snprintf(str, sizeof(str), "%s: Working in: %s\n", progname, dir); | |
806 | if (len > 0) | 806 | if (len > 0) | |
807 | (void)write(STDERR_FILENO, str, (size_t)len); | 807 | (void)write(STDERR_FILENO, str, (size_t)len); | |
808 | } | 808 | } | |
809 | #endif | 809 | #endif | |
810 | 810 | |||
811 | /* | 811 | /* | |
812 | * Allow makefiles some control over the mode we run in. | 812 | * Allow makefiles some control over the mode we run in. | |
813 | */ | 813 | */ | |
814 | void | 814 | void | |
815 | MakeMode(const char *mode) | 815 | MakeMode(const char *mode) | |
816 | { | 816 | { | |
817 | char *mode_freeIt = NULL; | 817 | char *mode_freeIt = NULL; | |
818 | 818 | |||
819 | if (mode == NULL) { | 819 | if (mode == NULL) { | |
820 | (void)Var_Subst("${" MAKE_MODE ":tl}", | 820 | (void)Var_Subst("${" MAKE_MODE ":tl}", | |
821 | VAR_GLOBAL, VARE_WANTRES, &mode_freeIt); | 821 | VAR_GLOBAL, VARE_WANTRES, &mode_freeIt); | |
822 | /* TODO: handle errors */ | 822 | /* TODO: handle errors */ | |
823 | mode = mode_freeIt; | 823 | mode = mode_freeIt; | |
824 | } | 824 | } | |
825 | 825 | |||
826 | if (mode[0] != '\0') { | 826 | if (mode[0] != '\0') { | |
827 | if (strstr(mode, "compat")) { | 827 | if (strstr(mode, "compat")) { | |
828 | opts.compatMake = TRUE; | 828 | opts.compatMake = TRUE; | |
829 | forceJobs = FALSE; | 829 | forceJobs = FALSE; | |
830 | } | 830 | } | |
831 | #if USE_META | 831 | #if USE_META | |
832 | if (strstr(mode, "meta")) | 832 | if (strstr(mode, "meta")) | |
833 | meta_mode_init(mode); | 833 | meta_mode_init(mode); | |
834 | #endif | 834 | #endif | |
835 | } | 835 | } | |
836 | 836 | |||
837 | free(mode_freeIt); | 837 | free(mode_freeIt); | |
838 | } | 838 | } | |
839 | 839 | |||
840 | static void | 840 | static void | |
841 | PrintVar(const char *varname, Boolean expandVars) | 841 | PrintVar(const char *varname, Boolean expandVars) | |
842 | { | 842 | { | |
843 | if (strchr(varname, '$')) { | 843 | if (strchr(varname, '$')) { | |
844 | char *evalue; | 844 | char *evalue; | |
845 | (void)Var_Subst(varname, VAR_GLOBAL, VARE_WANTRES, &evalue); | 845 | (void)Var_Subst(varname, VAR_GLOBAL, VARE_WANTRES, &evalue); | |
846 | /* TODO: handle errors */ | 846 | /* TODO: handle errors */ | |
847 | printf("%s\n", evalue); | 847 | printf("%s\n", evalue); | |
848 | bmake_free(evalue); | 848 | bmake_free(evalue); | |
849 | 849 | |||
850 | } else if (expandVars) { | 850 | } else if (expandVars) { | |
851 | char *expr = str_concat3("${", varname, "}"); | 851 | char *expr = str_concat3("${", varname, "}"); | |
852 | char *evalue; | 852 | char *evalue; | |
853 | (void)Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES, &evalue); | 853 | (void)Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES, &evalue); | |
854 | /* TODO: handle errors */ | 854 | /* TODO: handle errors */ | |
855 | free(expr); | 855 | free(expr); | |
856 | printf("%s\n", evalue); | 856 | printf("%s\n", evalue); | |
857 | bmake_free(evalue); | 857 | bmake_free(evalue); | |
858 | 858 | |||
859 | } else { | 859 | } else { | |
860 | void *freeIt; | 860 | void *freeIt; | |
861 | const char *value = Var_Value(varname, VAR_GLOBAL, &freeIt); | 861 | const char *value = Var_Value(varname, VAR_GLOBAL, &freeIt); | |
862 | printf("%s\n", value ? value : ""); | 862 | printf("%s\n", value ? value : ""); | |
863 | bmake_free(freeIt); | 863 | bmake_free(freeIt); | |
864 | } | 864 | } | |
865 | } | 865 | } | |
866 | 866 | |||
867 | static void | 867 | static void | |
868 | doPrintVars(void) | 868 | doPrintVars(void) | |
869 | { | 869 | { | |
870 | StringListNode *ln; | 870 | StringListNode *ln; | |
871 | Boolean expandVars; | 871 | Boolean expandVars; | |
872 | 872 | |||
873 | if (opts.printVars == EXPAND_VARS) | 873 | if (opts.printVars == EXPAND_VARS) | |
874 | expandVars = TRUE; | 874 | expandVars = TRUE; | |
875 | else if (opts.debugVflag) | 875 | else if (opts.debugVflag) | |
876 | expandVars = FALSE; | 876 | expandVars = FALSE; | |
877 | else | 877 | else | |
878 | expandVars = getBoolean(".MAKE.EXPAND_VARIABLES", FALSE); | 878 | expandVars = getBoolean(".MAKE.EXPAND_VARIABLES", FALSE); | |
879 | 879 | |||
880 | for (ln = opts.variables->first; ln != NULL; ln = ln->next) { | 880 | for (ln = opts.variables->first; ln != NULL; ln = ln->next) { | |
881 | const char *varname = ln->datum; | 881 | const char *varname = ln->datum; | |
882 | PrintVar(varname, expandVars); | 882 | PrintVar(varname, expandVars); | |
883 | } | 883 | } | |
884 | } | 884 | } | |
885 | 885 | |||
886 | static Boolean | 886 | static Boolean | |
887 | runTargets(void) | 887 | runTargets(void) | |
888 | { | 888 | { | |
889 | GNodeList *targs; /* target nodes to create -- passed to Make_Init */ | 889 | GNodeList *targs; /* target nodes to create -- passed to Make_Init */ | |
890 | Boolean outOfDate; /* FALSE if all targets up to date */ | 890 | Boolean outOfDate; /* FALSE if all targets up to date */ | |
891 | 891 | |||
892 | /* | 892 | /* | |
893 | * Have now read the entire graph and need to make a list of | 893 | * Have now read the entire graph and need to make a list of | |
894 | * targets to create. If none was given on the command line, | 894 | * targets to create. If none was given on the command line, | |
895 | * we consult the parsing module to find the main target(s) | 895 | * we consult the parsing module to find the main target(s) | |
896 | * to create. | 896 | * to create. | |
897 | */ | 897 | */ | |
898 | if (Lst_IsEmpty(opts.create)) | 898 | if (Lst_IsEmpty(opts.create)) | |
899 | targs = Parse_MainName(); | 899 | targs = Parse_MainName(); | |
900 | else | 900 | else | |
901 | targs = Targ_FindList(opts.create); | 901 | targs = Targ_FindList(opts.create); | |
902 | 902 | |||
903 | if (!opts.compatMake) { | 903 | if (!opts.compatMake) { | |
904 | /* | 904 | /* | |
905 | * Initialize job module before traversing the graph | 905 | * Initialize job module before traversing the graph | |
906 | * now that any .BEGIN and .END targets have been read. | 906 | * now that any .BEGIN and .END targets have been read. | |
907 | * This is done only if the -q flag wasn't given | 907 | * This is done only if the -q flag wasn't given | |
908 | * (to prevent the .BEGIN from being executed should | 908 | * (to prevent the .BEGIN from being executed should | |
909 | * it exist). | 909 | * it exist). | |
910 | */ | 910 | */ | |
911 | if (!opts.queryFlag) { | 911 | if (!opts.queryFlag) { | |
912 | Job_Init(); | 912 | Job_Init(); | |
913 | jobsRunning = TRUE; | 913 | jobsRunning = TRUE; | |
914 | } | 914 | } | |
915 | 915 | |||
916 | /* Traverse the graph, checking on all the targets */ | 916 | /* Traverse the graph, checking on all the targets */ | |
917 | outOfDate = Make_Run(targs); | 917 | outOfDate = Make_Run(targs); | |
918 | } else { | 918 | } else { | |
919 | /* | 919 | /* | |
920 | * Compat_Init will take care of creating all the | 920 | * Compat_Init will take care of creating all the | |
921 | * targets as well as initializing the module. | 921 | * targets as well as initializing the module. | |
922 | */ | 922 | */ | |
923 | Compat_Run(targs); | 923 | Compat_Run(targs); | |
924 | outOfDate = FALSE; | 924 | outOfDate = FALSE; | |
925 | } | 925 | } | |
926 | Lst_Free(targs); | 926 | Lst_Free(targs); | |
927 | return outOfDate; | 927 | return outOfDate; | |
928 | } | 928 | } | |
929 | 929 | |||
930 | /* | 930 | /* | |
931 | * Set up the .TARGETS variable to contain the list of targets to be | 931 | * Set up the .TARGETS variable to contain the list of targets to be | |
932 | * created. If none specified, make the variable empty -- the parser | 932 | * created. If none specified, make the variable empty -- the parser | |
933 | * will fill the thing in with the default or .MAIN target. | 933 | * will fill the thing in with the default or .MAIN target. | |
934 | */ | 934 | */ | |
935 | static void | 935 | static void | |
936 | InitVarTargets(void) | 936 | InitVarTargets(void) | |
937 | { | 937 | { | |
938 | StringListNode *ln; | 938 | StringListNode *ln; | |
939 | 939 | |||
940 | if (Lst_IsEmpty(opts.create)) { | 940 | if (Lst_IsEmpty(opts.create)) { | |
941 | Var_Set(".TARGETS", "", VAR_GLOBAL); | 941 | Var_Set(".TARGETS", "", VAR_GLOBAL); | |
942 | return; | 942 | return; | |
943 | } | 943 | } | |
944 | 944 | |||
945 | for (ln = opts.create->first; ln != NULL; ln = ln->next) { | 945 | for (ln = opts.create->first; ln != NULL; ln = ln->next) { | |
946 | char *name = ln->datum; | 946 | char *name = ln->datum; | |
947 | Var_Append(".TARGETS", name, VAR_GLOBAL); | 947 | Var_Append(".TARGETS", name, VAR_GLOBAL); | |
948 | } | 948 | } | |
949 | } | 949 | } | |
950 | 950 | |||
951 | static void | 951 | static void | |
952 | InitRandom(void) | 952 | InitRandom(void) | |
953 | { | 953 | { | |
954 | struct timeval tv; | 954 | struct timeval tv; | |
955 | 955 | |||
956 | gettimeofday(&tv, NULL); | 956 | gettimeofday(&tv, NULL); | |
957 | srandom((unsigned int)(tv.tv_sec + tv.tv_usec)); | 957 | srandom((unsigned int)(tv.tv_sec + tv.tv_usec)); | |
958 | } | 958 | } | |
959 | 959 | |||
960 | static const char * | 960 | static const char * | |
961 | init_machine(const struct utsname *utsname) | 961 | init_machine(const struct utsname *utsname) | |
962 | { | 962 | { | |
963 | const char *machine = getenv("MACHINE"); | 963 | const char *machine = getenv("MACHINE"); | |
964 | if (machine != NULL) | 964 | if (machine != NULL) | |
965 | return machine; | 965 | return machine; | |
966 | 966 | |||
967 | #ifdef MAKE_NATIVE | 967 | #ifdef MAKE_NATIVE | |
968 | return utsname->machine; | 968 | return utsname->machine; | |
969 | #else | 969 | #else | |
970 | #ifdef MAKE_MACHINE | 970 | #ifdef MAKE_MACHINE | |
971 | return MAKE_MACHINE; | 971 | return MAKE_MACHINE; | |
972 | #else | 972 | #else | |
973 | return "unknown"; | 973 | return "unknown"; | |
974 | #endif | 974 | #endif | |
975 | #endif | 975 | #endif | |
976 | } | 976 | } | |
977 | 977 | |||
978 | static const char * | 978 | static const char * | |
979 | init_machine_arch(void) | 979 | init_machine_arch(void) | |
980 | { | 980 | { | |
981 | const char *env = getenv("MACHINE_ARCH"); | 981 | const char *env = getenv("MACHINE_ARCH"); | |
982 | if (env != NULL) | 982 | if (env != NULL) | |
983 | return env; | 983 | return env; | |
984 | 984 | |||
985 | #ifdef MAKE_NATIVE | 985 | #ifdef MAKE_NATIVE | |
986 | { | 986 | { | |
987 | struct utsname utsname; | 987 | struct utsname utsname; | |
988 | static char machine_arch_buf[sizeof(utsname.machine)]; | 988 | static char machine_arch_buf[sizeof(utsname.machine)]; | |
989 | const int mib[2] = { CTL_HW, HW_MACHINE_ARCH }; | 989 | const int mib[2] = { CTL_HW, HW_MACHINE_ARCH }; | |
990 | size_t len = sizeof(machine_arch_buf); | 990 | size_t len = sizeof(machine_arch_buf); | |
991 | 991 | |||
992 | if (sysctl(mib, __arraycount(mib), machine_arch_buf, | 992 | if (sysctl(mib, __arraycount(mib), machine_arch_buf, | |
993 | &len, NULL, 0) < 0) { | 993 | &len, NULL, 0) < 0) { | |
994 | (void)fprintf(stderr, "%s: sysctl failed (%s).\n", progname, | 994 | (void)fprintf(stderr, "%s: sysctl failed (%s).\n", progname, | |
995 | strerror(errno)); | 995 | strerror(errno)); | |
996 | exit(2); | 996 | exit(2); | |
997 | } | 997 | } | |
998 | 998 | |||
999 | return machine_arch_buf; | 999 | return machine_arch_buf; | |
1000 | } | 1000 | } | |
1001 | #else | 1001 | #else | |
1002 | #ifndef MACHINE_ARCH | 1002 | #ifndef MACHINE_ARCH | |
1003 | #ifdef MAKE_MACHINE_ARCH | 1003 | #ifdef MAKE_MACHINE_ARCH | |
1004 | return MAKE_MACHINE_ARCH; | 1004 | return MAKE_MACHINE_ARCH; | |
1005 | #else | 1005 | #else | |
1006 | return "unknown"; | 1006 | return "unknown"; | |
1007 | #endif | 1007 | #endif | |
1008 | #else | 1008 | #else | |
1009 | return MACHINE_ARCH; | 1009 | return MACHINE_ARCH; | |
1010 | #endif | 1010 | #endif | |
1011 | #endif | 1011 | #endif | |
1012 | } | 1012 | } | |
1013 | 1013 | |||
1014 | #ifndef NO_PWD_OVERRIDE | 1014 | #ifndef NO_PWD_OVERRIDE | |
1015 | /* | 1015 | /* | |
1016 | * All this code is so that we know where we are when we start up | 1016 | * All this code is so that we know where we are when we start up | |
1017 | * on a different machine with pmake. | 1017 | * on a different machine with pmake. | |
1018 | * | 1018 | * | |
1019 | * Overriding getcwd() with $PWD totally breaks MAKEOBJDIRPREFIX | 1019 | * Overriding getcwd() with $PWD totally breaks MAKEOBJDIRPREFIX | |
1020 | * since the value of curdir can vary depending on how we got | 1020 | * since the value of curdir can vary depending on how we got | |
1021 | * here. Ie sitting at a shell prompt (shell that provides $PWD) | 1021 | * here. Ie sitting at a shell prompt (shell that provides $PWD) | |
1022 | * or via subdir.mk in which case its likely a shell which does | 1022 | * or via subdir.mk in which case its likely a shell which does | |
1023 | * not provide it. | 1023 | * not provide it. | |
1024 | * | 1024 | * | |
1025 | * So, to stop it breaking this case only, we ignore PWD if | 1025 | * So, to stop it breaking this case only, we ignore PWD if | |
1026 | * MAKEOBJDIRPREFIX is set or MAKEOBJDIR contains a variable expression. | 1026 | * MAKEOBJDIRPREFIX is set or MAKEOBJDIR contains a variable expression. | |
1027 | */ | 1027 | */ | |
1028 | static void | 1028 | static void | |
1029 | HandlePWD(const struct stat *curdir_st) | 1029 | HandlePWD(const struct stat *curdir_st) | |
1030 | { | 1030 | { | |
1031 | char *pwd; | 1031 | char *pwd; | |
1032 | void *prefix_freeIt, *makeobjdir_freeIt; | 1032 | void *prefix_freeIt, *makeobjdir_freeIt; | |
1033 | const char *makeobjdir; | 1033 | const char *makeobjdir; | |
1034 | struct stat pwd_st; | 1034 | struct stat pwd_st; | |
1035 | 1035 | |||
1036 | if (ignorePWD || (pwd = getenv("PWD")) == NULL) | 1036 | if (ignorePWD || (pwd = getenv("PWD")) == NULL) | |
1037 | return; | 1037 | return; | |
1038 | 1038 | |||
1039 | if (Var_Value("MAKEOBJDIRPREFIX", VAR_CMDLINE, &prefix_freeIt) != NULL) { | 1039 | if (Var_Value("MAKEOBJDIRPREFIX", VAR_CMDLINE, &prefix_freeIt) != NULL) { | |
1040 | bmake_free(prefix_freeIt); | 1040 | bmake_free(prefix_freeIt); | |
1041 | return; | 1041 | return; | |
1042 | } | 1042 | } | |
1043 | 1043 | |||
1044 | makeobjdir = Var_Value("MAKEOBJDIR", VAR_CMDLINE, &makeobjdir_freeIt); | 1044 | makeobjdir = Var_Value("MAKEOBJDIR", VAR_CMDLINE, &makeobjdir_freeIt); | |
1045 | if (makeobjdir != NULL && strchr(makeobjdir, '$') != NULL) | 1045 | if (makeobjdir != NULL && strchr(makeobjdir, '$') != NULL) | |
1046 | goto ignore_pwd; | 1046 | goto ignore_pwd; | |
1047 | 1047 | |||
1048 | if (stat(pwd, &pwd_st) == 0 && | 1048 | if (stat(pwd, &pwd_st) == 0 && | |
1049 | curdir_st->st_ino == pwd_st.st_ino && | 1049 | curdir_st->st_ino == pwd_st.st_ino && | |
1050 | curdir_st->st_dev == pwd_st.st_dev) | 1050 | curdir_st->st_dev == pwd_st.st_dev) | |
1051 | (void)strncpy(curdir, pwd, MAXPATHLEN); | 1051 | (void)strncpy(curdir, pwd, MAXPATHLEN); | |
1052 | 1052 | |||
1053 | ignore_pwd: | 1053 | ignore_pwd: | |
1054 | bmake_free(makeobjdir_freeIt); | 1054 | bmake_free(makeobjdir_freeIt); | |
1055 | } | 1055 | } | |
1056 | #endif | 1056 | #endif | |
1057 | 1057 | |||
1058 | /* | 1058 | /* | |
1059 | * Find the .OBJDIR. If MAKEOBJDIRPREFIX, or failing that, | 1059 | * Find the .OBJDIR. If MAKEOBJDIRPREFIX, or failing that, | |
1060 | * MAKEOBJDIR is set in the environment, try only that value | 1060 | * MAKEOBJDIR is set in the environment, try only that value | |
1061 | * and fall back to .CURDIR if it does not exist. | 1061 | * and fall back to .CURDIR if it does not exist. | |
1062 | * | 1062 | * | |
1063 | * Otherwise, try _PATH_OBJDIR.MACHINE-MACHINE_ARCH, _PATH_OBJDIR.MACHINE, | 1063 | * Otherwise, try _PATH_OBJDIR.MACHINE-MACHINE_ARCH, _PATH_OBJDIR.MACHINE, | |
1064 | * and * finally _PATH_OBJDIRPREFIX`pwd`, in that order. If none | 1064 | * and * finally _PATH_OBJDIRPREFIX`pwd`, in that order. If none | |
1065 | * of these paths exist, just use .CURDIR. | 1065 | * of these paths exist, just use .CURDIR. | |
1066 | */ | 1066 | */ | |
1067 | static void | 1067 | static void | |
1068 | InitObjdir(const char *machine, const char *machine_arch) | 1068 | InitObjdir(const char *machine, const char *machine_arch) | |
1069 | { | 1069 | { | |
1070 | Dir_InitDir(curdir); | 1070 | Dir_InitDir(curdir); | |
1071 | (void)Main_SetObjdir("%s", curdir); | 1071 | (void)Main_SetObjdir("%s", curdir); | |
1072 | 1072 | |||
1073 | if (!Main_SetVarObjdir("MAKEOBJDIRPREFIX", curdir) && | 1073 | if (!Main_SetVarObjdir("MAKEOBJDIRPREFIX", curdir) && | |
1074 | !Main_SetVarObjdir("MAKEOBJDIR", "") && | 1074 | !Main_SetVarObjdir("MAKEOBJDIR", "") && | |
1075 | !Main_SetObjdir("%s.%s-%s", _PATH_OBJDIR, machine, machine_arch) && | 1075 | !Main_SetObjdir("%s.%s-%s", _PATH_OBJDIR, machine, machine_arch) && | |
1076 | !Main_SetObjdir("%s.%s", _PATH_OBJDIR, machine) && | 1076 | !Main_SetObjdir("%s.%s", _PATH_OBJDIR, machine) && | |
1077 | !Main_SetObjdir("%s", _PATH_OBJDIR)) | 1077 | !Main_SetObjdir("%s", _PATH_OBJDIR)) | |
1078 | (void)Main_SetObjdir("%s%s", _PATH_OBJDIRPREFIX, curdir); | 1078 | (void)Main_SetObjdir("%s%s", _PATH_OBJDIRPREFIX, curdir); | |
1079 | } | 1079 | } | |
1080 | 1080 | |||
1081 | /* get rid of resource limit on file descriptors */ | 1081 | /* get rid of resource limit on file descriptors */ | |
1082 | static void | 1082 | static void | |
1083 | UnlimitFiles(void) | 1083 | UnlimitFiles(void) | |
1084 | { | 1084 | { | |
1085 | #if defined(MAKE_NATIVE) || (defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)) | 1085 | #if defined(MAKE_NATIVE) || (defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)) | |
1086 | struct rlimit rl; | 1086 | struct rlimit rl; | |
1087 | if (getrlimit(RLIMIT_NOFILE, &rl) != -1 && | 1087 | if (getrlimit(RLIMIT_NOFILE, &rl) != -1 && | |
1088 | rl.rlim_cur != rl.rlim_max) { | 1088 | rl.rlim_cur != rl.rlim_max) { | |
1089 | rl.rlim_cur = rl.rlim_max; | 1089 | rl.rlim_cur = rl.rlim_max; | |
1090 | (void)setrlimit(RLIMIT_NOFILE, &rl); | 1090 | (void)setrlimit(RLIMIT_NOFILE, &rl); | |
1091 | } | 1091 | } | |
1092 | #endif | 1092 | #endif | |
1093 | } | 1093 | } | |
1094 | 1094 | |||
1095 | static void | 1095 | static void | |
1096 | CmdOpts_Init(void) | 1096 | CmdOpts_Init(void) | |
1097 | { | 1097 | { | |
1098 | opts.compatMake = FALSE; /* No compat mode */ | 1098 | opts.compatMake = FALSE; /* No compat mode */ | |
1099 | opts.debug = 0; /* No debug verbosity, please. */ | 1099 | opts.debug = 0; /* No debug verbosity, please. */ | |
1100 | /* opts.debug_file has been initialized earlier */ | 1100 | /* opts.debug_file has been initialized earlier */ | |
1101 | opts.debugVflag = FALSE; | 1101 | opts.debugVflag = FALSE; | |
1102 | opts.checkEnvFirst = FALSE; | 1102 | opts.checkEnvFirst = FALSE; | |
1103 | opts.makefiles = Lst_New(); | 1103 | opts.makefiles = Lst_New(); | |
1104 | opts.ignoreErrors = FALSE; /* Pay attention to non-zero returns */ | 1104 | opts.ignoreErrors = FALSE; /* Pay attention to non-zero returns */ | |
1105 | opts.maxJobs = DEFMAXLOCAL; /* Set default local max concurrency */ | 1105 | opts.maxJobs = DEFMAXLOCAL; /* Set default local max concurrency */ | |
1106 | opts.keepgoing = FALSE; /* Stop on error */ | 1106 | opts.keepgoing = FALSE; /* Stop on error */ | |
1107 | opts.noRecursiveExecute = FALSE; /* Execute all .MAKE targets */ | 1107 | opts.noRecursiveExecute = FALSE; /* Execute all .MAKE targets */ | |
1108 | opts.noExecute = FALSE; /* Execute all commands */ | 1108 | opts.noExecute = FALSE; /* Execute all commands */ | |
1109 | opts.queryFlag = FALSE; /* This is not just a check-run */ | 1109 | opts.queryFlag = FALSE; /* This is not just a check-run */ | |
1110 | opts.noBuiltins = FALSE; /* Read the built-in rules */ | 1110 | opts.noBuiltins = FALSE; /* Read the built-in rules */ | |
1111 | opts.beSilent = FALSE; /* Print commands as executed */ | 1111 | opts.beSilent = FALSE; /* Print commands as executed */ | |
1112 | opts.touchFlag = FALSE; /* Actually update targets */ | 1112 | opts.touchFlag = FALSE; /* Actually update targets */ | |
1113 | opts.printVars = 0; | 1113 | opts.printVars = 0; | |
1114 | opts.variables = Lst_New(); | 1114 | opts.variables = Lst_New(); | |
1115 | opts.parseWarnFatal = FALSE; | 1115 | opts.parseWarnFatal = FALSE; | |
1116 | opts.enterFlag = FALSE; | 1116 | opts.enterFlag = FALSE; | |
1117 | opts.varNoExportEnv = FALSE; | 1117 | opts.varNoExportEnv = FALSE; | |
1118 | opts.create = Lst_New(); | 1118 | opts.create = Lst_New(); | |
1119 | } | 1119 | } | |
1120 | 1120 | |||
@@ -1210,1040 +1210,1038 @@ InitMaxJobs(void) | @@ -1210,1040 +1210,1038 @@ InitMaxJobs(void) | |||
1210 | if (n < 1) { | 1210 | if (n < 1) { | |
1211 | (void)fprintf(stderr, | 1211 | (void)fprintf(stderr, | |
1212 | "%s: illegal value for .MAKE.JOBS " | 1212 | "%s: illegal value for .MAKE.JOBS " | |
1213 | "-- must be positive integer!\n", | 1213 | "-- must be positive integer!\n", | |
1214 | progname); | 1214 | progname); | |
1215 | exit(1); | 1215 | exit(1); | |
1216 | } | 1216 | } | |
1217 | 1217 | |||
1218 | if (n != opts.maxJobs) { | 1218 | if (n != opts.maxJobs) { | |
1219 | Var_Append(MAKEFLAGS, "-j", VAR_GLOBAL); | 1219 | Var_Append(MAKEFLAGS, "-j", VAR_GLOBAL); | |
1220 | Var_Append(MAKEFLAGS, value, VAR_GLOBAL); | 1220 | Var_Append(MAKEFLAGS, value, VAR_GLOBAL); | |
1221 | } | 1221 | } | |
1222 | 1222 | |||
1223 | opts.maxJobs = n; | 1223 | opts.maxJobs = n; | |
1224 | maxJobTokens = opts.maxJobs; | 1224 | maxJobTokens = opts.maxJobs; | |
1225 | forceJobs = TRUE; | 1225 | forceJobs = TRUE; | |
1226 | free(value); | 1226 | free(value); | |
1227 | } | 1227 | } | |
1228 | 1228 | |||
1229 | /* | 1229 | /* | |
1230 | * For compatibility, look at the directories in the VPATH variable | 1230 | * For compatibility, look at the directories in the VPATH variable | |
1231 | * and add them to the search path, if the variable is defined. The | 1231 | * and add them to the search path, if the variable is defined. The | |
1232 | * variable's value is in the same format as the PATH environment | 1232 | * variable's value is in the same format as the PATH environment | |
1233 | * variable, i.e. <directory>:<directory>:<directory>... | 1233 | * variable, i.e. <directory>:<directory>:<directory>... | |
1234 | */ | 1234 | */ | |
1235 | static void | 1235 | static void | |
1236 | InitVpath(void) | 1236 | InitVpath(void) | |
1237 | { | 1237 | { | |
1238 | char *vpath, savec, *path; | 1238 | char *vpath, savec, *path; | |
1239 | if (!Var_Exists("VPATH", VAR_CMDLINE)) | 1239 | if (!Var_Exists("VPATH", VAR_CMDLINE)) | |
1240 | return; | 1240 | return; | |
1241 | 1241 | |||
1242 | (void)Var_Subst("${VPATH}", VAR_CMDLINE, VARE_WANTRES, &vpath); | 1242 | (void)Var_Subst("${VPATH}", VAR_CMDLINE, VARE_WANTRES, &vpath); | |
1243 | /* TODO: handle errors */ | 1243 | /* TODO: handle errors */ | |
1244 | path = vpath; | 1244 | path = vpath; | |
1245 | do { | 1245 | do { | |
1246 | char *cp; | 1246 | char *cp; | |
1247 | /* skip to end of directory */ | 1247 | /* skip to end of directory */ | |
1248 | for (cp = path; *cp != ':' && *cp != '\0'; cp++) | 1248 | for (cp = path; *cp != ':' && *cp != '\0'; cp++) | |
1249 | continue; | 1249 | continue; | |
1250 | /* Save terminator character so know when to stop */ | 1250 | /* Save terminator character so know when to stop */ | |
1251 | savec = *cp; | 1251 | savec = *cp; | |
1252 | *cp = '\0'; | 1252 | *cp = '\0'; | |
1253 | /* Add directory to search path */ | 1253 | /* Add directory to search path */ | |
1254 | (void)Dir_AddDir(dirSearchPath, path); | 1254 | (void)Dir_AddDir(dirSearchPath, path); | |
1255 | *cp = savec; | 1255 | *cp = savec; | |
1256 | path = cp + 1; | 1256 | path = cp + 1; | |
1257 | } while (savec == ':'); | 1257 | } while (savec == ':'); | |
1258 | free(vpath); | 1258 | free(vpath); | |
1259 | } | 1259 | } | |
1260 | 1260 | |||
1261 | static void | 1261 | static void | |
1262 | ReadMakefiles(void) | 1262 | ReadMakefiles(void) | |
1263 | { | 1263 | { | |
1264 | if (opts.makefiles->first != NULL) { | 1264 | if (opts.makefiles->first != NULL) { | |
1265 | StringListNode *ln; | 1265 | StringListNode *ln; | |
1266 | 1266 | |||
1267 | for (ln = opts.makefiles->first; ln != NULL; ln = ln->next) { | 1267 | for (ln = opts.makefiles->first; ln != NULL; ln = ln->next) { | |
1268 | if (ReadMakefile(ln->datum) != 0) | 1268 | if (ReadMakefile(ln->datum) != 0) | |
1269 | Fatal("%s: cannot open %s.", | 1269 | Fatal("%s: cannot open %s.", | |
1270 | progname, (char *)ln->datum); | 1270 | progname, (char *)ln->datum); | |
1271 | } | 1271 | } | |
1272 | } else { | 1272 | } else { | |
1273 | char *p1; | 1273 | char *p1; | |
1274 | (void)Var_Subst("${" MAKEFILE_PREFERENCE "}", | 1274 | (void)Var_Subst("${" MAKEFILE_PREFERENCE "}", | |
1275 | VAR_CMDLINE, VARE_WANTRES, &p1); | 1275 | VAR_CMDLINE, VARE_WANTRES, &p1); | |
1276 | /* TODO: handle errors */ | 1276 | /* TODO: handle errors */ | |
1277 | (void)str2Lst_Append(opts.makefiles, p1, NULL); | 1277 | (void)str2Lst_Append(opts.makefiles, p1, NULL); | |
1278 | (void)Lst_ForEachUntil(opts.makefiles, | 1278 | (void)Lst_ForEachUntil(opts.makefiles, | |
1279 | ReadMakefileSucceeded, NULL); | 1279 | ReadMakefileSucceeded, NULL); | |
1280 | free(p1); | 1280 | free(p1); | |
1281 | } | 1281 | } | |
1282 | } | 1282 | } | |
1283 | 1283 | |||
1284 | static void | 1284 | static void | |
1285 | CleanUp(void) | 1285 | CleanUp(void) | |
1286 | { | 1286 | { | |
1287 | #ifdef CLEANUP | 1287 | #ifdef CLEANUP | |
1288 | Lst_Destroy(opts.variables, free); | 1288 | Lst_Destroy(opts.variables, free); | |
1289 | Lst_Free(opts.makefiles); /* don't free, may be used in GNodes */ | 1289 | Lst_Free(opts.makefiles); /* don't free, may be used in GNodes */ | |
1290 | Lst_Destroy(opts.create, free); | 1290 | Lst_Destroy(opts.create, free); | |
1291 | #endif | 1291 | #endif | |
1292 | 1292 | |||
1293 | /* print the graph now it's been processed if the user requested it */ | 1293 | /* print the graph now it's been processed if the user requested it */ | |
1294 | if (DEBUG(GRAPH2)) | 1294 | if (DEBUG(GRAPH2)) | |
1295 | Targ_PrintGraph(2); | 1295 | Targ_PrintGraph(2); | |
1296 | 1296 | |||
1297 | Trace_Log(MAKEEND, 0); | 1297 | Trace_Log(MAKEEND, 0); | |
1298 | 1298 | |||
1299 | if (enterFlagObj) | 1299 | if (enterFlagObj) | |
1300 | printf("%s: Leaving directory `%s'\n", progname, objdir); | 1300 | printf("%s: Leaving directory `%s'\n", progname, objdir); | |
1301 | if (opts.enterFlag) | 1301 | if (opts.enterFlag) | |
1302 | printf("%s: Leaving directory `%s'\n", progname, curdir); | 1302 | printf("%s: Leaving directory `%s'\n", progname, curdir); | |
1303 | 1303 | |||
1304 | #ifdef USE_META | 1304 | #ifdef USE_META | |
1305 | meta_finish(); | 1305 | meta_finish(); | |
1306 | #endif | 1306 | #endif | |
1307 | Suff_End(); | 1307 | Suff_End(); | |
1308 | Targ_End(); | 1308 | Targ_End(); | |
1309 | Arch_End(); | 1309 | Arch_End(); | |
1310 | Var_End(); | 1310 | Var_End(); | |
1311 | Parse_End(); | 1311 | Parse_End(); | |
1312 | Dir_End(); | 1312 | Dir_End(); | |
1313 | Job_End(); | 1313 | Job_End(); | |
1314 | Trace_End(); | 1314 | Trace_End(); | |
1315 | } | 1315 | } | |
1316 | 1316 | |||
1317 | /*- | 1317 | /*- | |
1318 | * main -- | 1318 | * main -- | |
1319 | * The main function, for obvious reasons. Initializes variables | 1319 | * The main function, for obvious reasons. Initializes variables | |
1320 | * and a few modules, then parses the arguments give it in the | 1320 | * and a few modules, then parses the arguments give it in the | |
1321 | * environment and on the command line. Reads the system makefile | 1321 | * environment and on the command line. Reads the system makefile | |
1322 | * followed by either Makefile, makefile or the file given by the | 1322 | * followed by either Makefile, makefile or the file given by the | |
1323 | * -f argument. Sets the .MAKEFLAGS PMake variable based on all the | 1323 | * -f argument. Sets the .MAKEFLAGS PMake variable based on all the | |
1324 | * flags it has received by then uses either the Make or the Compat | 1324 | * flags it has received by then uses either the Make or the Compat | |
1325 | * module to create the initial list of targets. | 1325 | * module to create the initial list of targets. | |
1326 | * | 1326 | * | |
1327 | * Results: | 1327 | * Results: | |
1328 | * If -q was given, exits -1 if anything was out-of-date. Else it exits | 1328 | * If -q was given, exits -1 if anything was out-of-date. Else it exits | |
1329 | * 0. | 1329 | * 0. | |
1330 | * | 1330 | * | |
1331 | * Side Effects: | 1331 | * Side Effects: | |
1332 | * The program exits when done. Targets are created. etc. etc. etc. | 1332 | * The program exits when done. Targets are created. etc. etc. etc. | |
1333 | */ | 1333 | */ | |
1334 | int | 1334 | int | |
1335 | main(int argc, char **argv) | 1335 | main(int argc, char **argv) | |
1336 | { | 1336 | { | |
1337 | Boolean outOfDate; /* FALSE if all targets up to date */ | 1337 | Boolean outOfDate; /* FALSE if all targets up to date */ | |
1338 | struct stat sa; | 1338 | struct stat sa; | |
1339 | const char *machine; | 1339 | const char *machine; | |
1340 | const char *machine_arch; | 1340 | const char *machine_arch; | |
1341 | char *syspath = getenv("MAKESYSPATH"); | 1341 | char *syspath = getenv("MAKESYSPATH"); | |
1342 | struct utsname utsname; | 1342 | struct utsname utsname; | |
1343 | 1343 | |||
1344 | /* default to writing debug to stderr */ | 1344 | /* default to writing debug to stderr */ | |
1345 | opts.debug_file = stderr; | 1345 | opts.debug_file = stderr; | |
1346 | 1346 | |||
1347 | #ifdef SIGINFO | 1347 | #ifdef SIGINFO | |
1348 | (void)bmake_signal(SIGINFO, siginfo); | 1348 | (void)bmake_signal(SIGINFO, siginfo); | |
1349 | #endif | 1349 | #endif | |
1350 | 1350 | |||
1351 | InitRandom(); | 1351 | InitRandom(); | |
1352 | 1352 | |||
1353 | if ((progname = strrchr(argv[0], '/')) != NULL) | 1353 | if ((progname = strrchr(argv[0], '/')) != NULL) | |
1354 | progname++; | 1354 | progname++; | |
1355 | else | 1355 | else | |
1356 | progname = argv[0]; | 1356 | progname = argv[0]; | |
1357 | 1357 | |||
1358 | UnlimitFiles(); | 1358 | UnlimitFiles(); | |
1359 | 1359 | |||
1360 | if (uname(&utsname) == -1) { | 1360 | if (uname(&utsname) == -1) { | |
1361 | (void)fprintf(stderr, "%s: uname failed (%s).\n", progname, | 1361 | (void)fprintf(stderr, "%s: uname failed (%s).\n", progname, | |
1362 | strerror(errno)); | 1362 | strerror(errno)); | |
1363 | exit(2); | 1363 | exit(2); | |
1364 | } | 1364 | } | |
1365 | 1365 | |||
1366 | /* | 1366 | /* | |
1367 | * Get the name of this type of MACHINE from utsname | 1367 | * Get the name of this type of MACHINE from utsname | |
1368 | * so we can share an executable for similar machines. | 1368 | * so we can share an executable for similar machines. | |
1369 | * (i.e. m68k: amiga hp300, mac68k, sun3, ...) | 1369 | * (i.e. m68k: amiga hp300, mac68k, sun3, ...) | |
1370 | * | 1370 | * | |
1371 | * Note that both MACHINE and MACHINE_ARCH are decided at | 1371 | * Note that both MACHINE and MACHINE_ARCH are decided at | |
1372 | * run-time. | 1372 | * run-time. | |
1373 | */ | 1373 | */ | |
1374 | machine = init_machine(&utsname); | 1374 | machine = init_machine(&utsname); | |
1375 | machine_arch = init_machine_arch(); | 1375 | machine_arch = init_machine_arch(); | |
1376 | 1376 | |||
1377 | myPid = getpid(); /* remember this for vFork() */ | 1377 | myPid = getpid(); /* remember this for vFork() */ | |
1378 | 1378 | |||
1379 | /* | 1379 | /* | |
1380 | * Just in case MAKEOBJDIR wants us to do something tricky. | 1380 | * Just in case MAKEOBJDIR wants us to do something tricky. | |
1381 | */ | 1381 | */ | |
1382 | Var_Init(); /* Initialize the lists of variables for | 1382 | Var_Init(); /* Initialize the lists of variables for | |
1383 | * parsing arguments */ | 1383 | * parsing arguments */ | |
1384 | Var_Set(".MAKE.OS", utsname.sysname, VAR_GLOBAL); | 1384 | Var_Set(".MAKE.OS", utsname.sysname, VAR_GLOBAL); | |
1385 | Var_Set("MACHINE", machine, VAR_GLOBAL); | 1385 | Var_Set("MACHINE", machine, VAR_GLOBAL); | |
1386 | Var_Set("MACHINE_ARCH", machine_arch, VAR_GLOBAL); | 1386 | Var_Set("MACHINE_ARCH", machine_arch, VAR_GLOBAL); | |
1387 | #ifdef MAKE_VERSION | 1387 | #ifdef MAKE_VERSION | |
1388 | Var_Set("MAKE_VERSION", MAKE_VERSION, VAR_GLOBAL); | 1388 | Var_Set("MAKE_VERSION", MAKE_VERSION, VAR_GLOBAL); | |
1389 | #endif | 1389 | #endif | |
1390 | Var_Set(".newline", "\n", VAR_GLOBAL); /* handy for :@ loops */ | 1390 | Var_Set(".newline", "\n", VAR_GLOBAL); /* handy for :@ loops */ | |
1391 | /* | 1391 | /* | |
1392 | * This is the traditional preference for makefiles. | 1392 | * This is the traditional preference for makefiles. | |
1393 | */ | 1393 | */ | |
1394 | #ifndef MAKEFILE_PREFERENCE_LIST | 1394 | #ifndef MAKEFILE_PREFERENCE_LIST | |
1395 | # define MAKEFILE_PREFERENCE_LIST "makefile Makefile" | 1395 | # define MAKEFILE_PREFERENCE_LIST "makefile Makefile" | |
1396 | #endif | 1396 | #endif | |
1397 | Var_Set(MAKEFILE_PREFERENCE, MAKEFILE_PREFERENCE_LIST, | 1397 | Var_Set(MAKEFILE_PREFERENCE, MAKEFILE_PREFERENCE_LIST, | |
1398 | VAR_GLOBAL); | 1398 | VAR_GLOBAL); | |
1399 | Var_Set(MAKE_DEPENDFILE, ".depend", VAR_GLOBAL); | 1399 | Var_Set(MAKE_DEPENDFILE, ".depend", VAR_GLOBAL); | |
1400 | 1400 | |||
1401 | CmdOpts_Init(); | 1401 | CmdOpts_Init(); | |
1402 | allPrecious = FALSE; /* Remove targets when interrupted */ | 1402 | allPrecious = FALSE; /* Remove targets when interrupted */ | |
1403 | deleteOnError = FALSE; /* Historical default behavior */ | 1403 | deleteOnError = FALSE; /* Historical default behavior */ | |
1404 | jobsRunning = FALSE; | 1404 | jobsRunning = FALSE; | |
1405 | 1405 | |||
1406 | maxJobTokens = opts.maxJobs; | 1406 | maxJobTokens = opts.maxJobs; | |
1407 | ignorePWD = FALSE; | 1407 | ignorePWD = FALSE; | |
1408 | 1408 | |||
1409 | /* | 1409 | /* | |
1410 | * Initialize the parsing, directory and variable modules to prepare | 1410 | * Initialize the parsing, directory and variable modules to prepare | |
1411 | * for the reading of inclusion paths and variable settings on the | 1411 | * for the reading of inclusion paths and variable settings on the | |
1412 | * command line | 1412 | * command line | |
1413 | */ | 1413 | */ | |
1414 | 1414 | |||
1415 | /* | 1415 | /* | |
1416 | * Initialize various variables. | 1416 | * Initialize various variables. | |
1417 | * MAKE also gets this name, for compatibility | 1417 | * MAKE also gets this name, for compatibility | |
1418 | * .MAKEFLAGS gets set to the empty string just in case. | 1418 | * .MAKEFLAGS gets set to the empty string just in case. | |
1419 | * MFLAGS also gets initialized empty, for compatibility. | 1419 | * MFLAGS also gets initialized empty, for compatibility. | |
1420 | */ | 1420 | */ | |
1421 | Parse_Init(); | 1421 | Parse_Init(); | |
1422 | InitVarMake(argv[0]); | 1422 | InitVarMake(argv[0]); | |
1423 | Var_Set(MAKEFLAGS, "", VAR_GLOBAL); | 1423 | Var_Set(MAKEFLAGS, "", VAR_GLOBAL); | |
1424 | Var_Set(MAKEOVERRIDES, "", VAR_GLOBAL); | 1424 | Var_Set(MAKEOVERRIDES, "", VAR_GLOBAL); | |
1425 | Var_Set("MFLAGS", "", VAR_GLOBAL); | 1425 | Var_Set("MFLAGS", "", VAR_GLOBAL); | |
1426 | Var_Set(".ALLTARGETS", "", VAR_GLOBAL); | 1426 | Var_Set(".ALLTARGETS", "", VAR_GLOBAL); | |
1427 | /* some makefiles need to know this */ | 1427 | /* some makefiles need to know this */ | |
1428 | Var_Set(MAKE_LEVEL ".ENV", MAKE_LEVEL_ENV, VAR_CMDLINE); | 1428 | Var_Set(MAKE_LEVEL ".ENV", MAKE_LEVEL_ENV, VAR_CMDLINE); | |
1429 | 1429 | |||
1430 | /* | 1430 | /* | |
1431 | * Set some other useful macros | 1431 | * Set some other useful macros | |
1432 | */ | 1432 | */ | |
1433 | { | 1433 | { | |
1434 | char tmp[64], *ep; | 1434 | char tmp[64], *ep; | |
1435 | 1435 | |||
1436 | makelevel = ((ep = getenv(MAKE_LEVEL_ENV)) && *ep) ? atoi(ep) : 0; | 1436 | makelevel = ((ep = getenv(MAKE_LEVEL_ENV)) && *ep) ? atoi(ep) : 0; | |
1437 | if (makelevel < 0) | 1437 | if (makelevel < 0) | |
1438 | makelevel = 0; | 1438 | makelevel = 0; | |
1439 | snprintf(tmp, sizeof(tmp), "%d", makelevel); | 1439 | snprintf(tmp, sizeof(tmp), "%d", makelevel); | |
1440 | Var_Set(MAKE_LEVEL, tmp, VAR_GLOBAL); | 1440 | Var_Set(MAKE_LEVEL, tmp, VAR_GLOBAL); | |
1441 | snprintf(tmp, sizeof(tmp), "%u", myPid); | 1441 | snprintf(tmp, sizeof(tmp), "%u", myPid); | |
1442 | Var_Set(".MAKE.PID", tmp, VAR_GLOBAL); | 1442 | Var_Set(".MAKE.PID", tmp, VAR_GLOBAL); | |
1443 | snprintf(tmp, sizeof(tmp), "%u", getppid()); | 1443 | snprintf(tmp, sizeof(tmp), "%u", getppid()); | |
1444 | Var_Set(".MAKE.PPID", tmp, VAR_GLOBAL); | 1444 | Var_Set(".MAKE.PPID", tmp, VAR_GLOBAL); | |
1445 | } | 1445 | } | |
1446 | if (makelevel > 0) { | 1446 | if (makelevel > 0) { | |
1447 | char pn[1024]; | 1447 | char pn[1024]; | |
1448 | snprintf(pn, sizeof(pn), "%s[%d]", progname, makelevel); | 1448 | snprintf(pn, sizeof(pn), "%s[%d]", progname, makelevel); | |
1449 | progname = bmake_strdup(pn); | 1449 | progname = bmake_strdup(pn); | |
1450 | } | 1450 | } | |
1451 | 1451 | |||
1452 | #ifdef USE_META | 1452 | #ifdef USE_META | |
1453 | meta_init(); | 1453 | meta_init(); | |
1454 | #endif | 1454 | #endif | |
1455 | Dir_Init(); | 1455 | Dir_Init(); | |
1456 | 1456 | |||
1457 | /* | 1457 | /* | |
1458 | * First snag any flags out of the MAKE environment variable. | 1458 | * First snag any flags out of the MAKE environment variable. | |
1459 | * (Note this is *not* MAKEFLAGS since /bin/make uses that and it's | 1459 | * (Note this is *not* MAKEFLAGS since /bin/make uses that and it's | |
1460 | * in a different format). | 1460 | * in a different format). | |
1461 | */ | 1461 | */ | |
1462 | #ifdef POSIX | 1462 | #ifdef POSIX | |
1463 | { | 1463 | { | |
1464 | char *p1 = explode(getenv("MAKEFLAGS")); | 1464 | char *p1 = explode(getenv("MAKEFLAGS")); | |
1465 | Main_ParseArgLine(p1); | 1465 | Main_ParseArgLine(p1); | |
1466 | free(p1); | 1466 | free(p1); | |
1467 | } | 1467 | } | |
1468 | #else | 1468 | #else | |
1469 | Main_ParseArgLine(getenv("MAKE")); | 1469 | Main_ParseArgLine(getenv("MAKE")); | |
1470 | #endif | 1470 | #endif | |
1471 | 1471 | |||
1472 | /* | 1472 | /* | |
1473 | * Find where we are (now). | 1473 | * Find where we are (now). | |
1474 | * We take care of PWD for the automounter below... | 1474 | * We take care of PWD for the automounter below... | |
1475 | */ | 1475 | */ | |
1476 | if (getcwd(curdir, MAXPATHLEN) == NULL) { | 1476 | if (getcwd(curdir, MAXPATHLEN) == NULL) { | |
1477 | (void)fprintf(stderr, "%s: getcwd: %s.\n", | 1477 | (void)fprintf(stderr, "%s: getcwd: %s.\n", | |
1478 | progname, strerror(errno)); | 1478 | progname, strerror(errno)); | |
1479 | exit(2); | 1479 | exit(2); | |
1480 | } | 1480 | } | |
1481 | 1481 | |||
1482 | MainParseArgs(argc, argv); | 1482 | MainParseArgs(argc, argv); | |
1483 | 1483 | |||
1484 | if (opts.enterFlag) | 1484 | if (opts.enterFlag) | |
1485 | printf("%s: Entering directory `%s'\n", progname, curdir); | 1485 | printf("%s: Entering directory `%s'\n", progname, curdir); | |
1486 | 1486 | |||
1487 | /* | 1487 | /* | |
1488 | * Verify that cwd is sane. | 1488 | * Verify that cwd is sane. | |
1489 | */ | 1489 | */ | |
1490 | if (stat(curdir, &sa) == -1) { | 1490 | if (stat(curdir, &sa) == -1) { | |
1491 | (void)fprintf(stderr, "%s: %s: %s.\n", | 1491 | (void)fprintf(stderr, "%s: %s: %s.\n", | |
1492 | progname, curdir, strerror(errno)); | 1492 | progname, curdir, strerror(errno)); | |
1493 | exit(2); | 1493 | exit(2); | |
1494 | } | 1494 | } | |
1495 | 1495 | |||
1496 | #ifndef NO_PWD_OVERRIDE | 1496 | #ifndef NO_PWD_OVERRIDE | |
1497 | HandlePWD(&sa); | 1497 | HandlePWD(&sa); | |
1498 | #endif | 1498 | #endif | |
1499 | Var_Set(".CURDIR", curdir, VAR_GLOBAL); | 1499 | Var_Set(".CURDIR", curdir, VAR_GLOBAL); | |
1500 | 1500 | |||
1501 | InitObjdir(machine, machine_arch); | 1501 | InitObjdir(machine, machine_arch); | |
1502 | 1502 | |||
1503 | /* | 1503 | /* | |
1504 | * Initialize archive, target and suffix modules in preparation for | 1504 | * Initialize archive, target and suffix modules in preparation for | |
1505 | * parsing the makefile(s) | 1505 | * parsing the makefile(s) | |
1506 | */ | 1506 | */ | |
1507 | Arch_Init(); | 1507 | Arch_Init(); | |
1508 | Targ_Init(); | 1508 | Targ_Init(); | |
1509 | Suff_Init(); | 1509 | Suff_Init(); | |
1510 | Trace_Init(tracefile); | 1510 | Trace_Init(tracefile); | |
1511 | 1511 | |||
1512 | DEFAULT = NULL; | 1512 | DEFAULT = NULL; | |
1513 | (void)time(&now); | 1513 | (void)time(&now); | |
1514 | 1514 | |||
1515 | Trace_Log(MAKESTART, NULL); | 1515 | Trace_Log(MAKESTART, NULL); | |
1516 | 1516 | |||
1517 | InitVarTargets(); | 1517 | InitVarTargets(); | |
1518 | 1518 | |||
1519 | InitDefSysIncPath(syspath); | 1519 | InitDefSysIncPath(syspath); | |
1520 | 1520 | |||
1521 | /* | 1521 | /* | |
1522 | * Read in the built-in rules first, followed by the specified | 1522 | * Read in the built-in rules first, followed by the specified | |
1523 | * makefiles, or the default makefile and Makefile, in that order, | 1523 | * makefiles, or the default makefile and Makefile, in that order, | |
1524 | * if no makefiles were given on the command line. | 1524 | * if no makefiles were given on the command line. | |
1525 | */ | 1525 | */ | |
1526 | if (!opts.noBuiltins) | 1526 | if (!opts.noBuiltins) | |
1527 | ReadBuiltinRules(); | 1527 | ReadBuiltinRules(); | |
1528 | ReadMakefiles(); | 1528 | ReadMakefiles(); | |
1529 | 1529 | |||
1530 | /* In particular suppress .depend for '-r -V .OBJDIR -f /dev/null' */ | 1530 | /* In particular suppress .depend for '-r -V .OBJDIR -f /dev/null' */ | |
1531 | if (!opts.noBuiltins || !opts.printVars) { | 1531 | if (!opts.noBuiltins || !opts.printVars) { | |
1532 | /* ignore /dev/null and anything starting with "no" */ | 1532 | /* ignore /dev/null and anything starting with "no" */ | |
1533 | (void)Var_Subst("${.MAKE.DEPENDFILE:N/dev/null:Nno*:T}", | 1533 | (void)Var_Subst("${.MAKE.DEPENDFILE:N/dev/null:Nno*:T}", | |
1534 | VAR_CMDLINE, VARE_WANTRES, &makeDependfile); | 1534 | VAR_CMDLINE, VARE_WANTRES, &makeDependfile); | |
1535 | if (makeDependfile[0] != '\0') { | 1535 | if (makeDependfile[0] != '\0') { | |
1536 | /* TODO: handle errors */ | 1536 | /* TODO: handle errors */ | |
1537 | doing_depend = TRUE; | 1537 | doing_depend = TRUE; | |
1538 | (void)ReadMakefile(makeDependfile); | 1538 | (void)ReadMakefile(makeDependfile); | |
1539 | doing_depend = FALSE; | 1539 | doing_depend = FALSE; | |
1540 | } | 1540 | } | |
1541 | } | 1541 | } | |
1542 | 1542 | |||
1543 | if (enterFlagObj) | 1543 | if (enterFlagObj) | |
1544 | printf("%s: Entering directory `%s'\n", progname, objdir); | 1544 | printf("%s: Entering directory `%s'\n", progname, objdir); | |
1545 | 1545 | |||
1546 | MakeMode(NULL); | 1546 | MakeMode(NULL); | |
1547 | 1547 | |||
1548 | { | 1548 | { | |
1549 | void *freeIt; | 1549 | void *freeIt; | |
1550 | Var_Append("MFLAGS", Var_Value(MAKEFLAGS, VAR_GLOBAL, &freeIt), | 1550 | Var_Append("MFLAGS", Var_Value(MAKEFLAGS, VAR_GLOBAL, &freeIt), | |
1551 | VAR_GLOBAL); | 1551 | VAR_GLOBAL); | |
1552 | bmake_free(freeIt); | 1552 | bmake_free(freeIt); | |
1553 | 1553 | |||
1554 | } | 1554 | } | |
1555 | 1555 | |||
1556 | InitMaxJobs(); | 1556 | InitMaxJobs(); | |
1557 | 1557 | |||
1558 | /* | 1558 | /* | |
1559 | * Be compatible if user did not specify -j and did not explicitly | 1559 | * Be compatible if user did not specify -j and did not explicitly | |
1560 | * turned compatibility on | 1560 | * turned compatibility on | |
1561 | */ | 1561 | */ | |
1562 | if (!opts.compatMake && !forceJobs) { | 1562 | if (!opts.compatMake && !forceJobs) { | |
1563 | opts.compatMake = TRUE; | 1563 | opts.compatMake = TRUE; | |
1564 | } | 1564 | } | |
1565 | 1565 | |||
1566 | if (!opts.compatMake) | 1566 | if (!opts.compatMake) | |
1567 | Job_ServerStart(maxJobTokens, jp_0, jp_1); | 1567 | Job_ServerStart(maxJobTokens, jp_0, jp_1); | |
1568 | DEBUG5(JOB, "job_pipe %d %d, maxjobs %d, tokens %d, compat %d\n", | 1568 | DEBUG5(JOB, "job_pipe %d %d, maxjobs %d, tokens %d, compat %d\n", | |
1569 | jp_0, jp_1, opts.maxJobs, maxJobTokens, opts.compatMake ? 1 : 0); | 1569 | jp_0, jp_1, opts.maxJobs, maxJobTokens, opts.compatMake ? 1 : 0); | |
1570 | 1570 | |||
1571 | if (!opts.printVars) | 1571 | if (!opts.printVars) | |
1572 | Main_ExportMAKEFLAGS(TRUE); /* initial export */ | 1572 | Main_ExportMAKEFLAGS(TRUE); /* initial export */ | |
1573 | 1573 | |||
1574 | InitVpath(); | 1574 | InitVpath(); | |
1575 | 1575 | |||
1576 | /* | 1576 | /* | |
1577 | * Now that all search paths have been read for suffixes et al, it's | 1577 | * Now that all search paths have been read for suffixes et al, it's | |
1578 | * time to add the default search path to their lists... | 1578 | * time to add the default search path to their lists... | |
1579 | */ | 1579 | */ | |
1580 | Suff_DoPaths(); | 1580 | Suff_DoPaths(); | |
1581 | 1581 | |||
1582 | /* | 1582 | /* | |
1583 | * Propagate attributes through :: dependency lists. | 1583 | * Propagate attributes through :: dependency lists. | |
1584 | */ | 1584 | */ | |
1585 | Targ_Propagate(); | 1585 | Targ_Propagate(); | |
1586 | 1586 | |||
1587 | /* print the initial graph, if the user requested it */ | 1587 | /* print the initial graph, if the user requested it */ | |
1588 | if (DEBUG(GRAPH1)) | 1588 | if (DEBUG(GRAPH1)) | |
1589 | Targ_PrintGraph(1); | 1589 | Targ_PrintGraph(1); | |
1590 | 1590 | |||
1591 | /* print the values of any variables requested by the user */ | 1591 | /* print the values of any variables requested by the user */ | |
1592 | if (opts.printVars) { | 1592 | if (opts.printVars) { | |
1593 | doPrintVars(); | 1593 | doPrintVars(); | |
1594 | outOfDate = FALSE; | 1594 | outOfDate = FALSE; | |
1595 | } else { | 1595 | } else { | |
1596 | outOfDate = runTargets(); | 1596 | outOfDate = runTargets(); | |
1597 | } | 1597 | } | |
1598 | 1598 | |||
1599 | CleanUp(); | 1599 | CleanUp(); | |
1600 | 1600 | |||
1601 | return outOfDate ? 1 : 0; | 1601 | return outOfDate ? 1 : 0; | |
1602 | } | 1602 | } | |
1603 | 1603 | |||
1604 | /* Open and parse the given makefile, with all its side effects. | 1604 | /* Open and parse the given makefile, with all its side effects. | |
1605 | * | 1605 | * | |
1606 | * Results: | 1606 | * Results: | |
1607 | * 0 if ok. -1 if couldn't open file. | 1607 | * 0 if ok. -1 if couldn't open file. | |
1608 | */ | 1608 | */ | |
1609 | static int | 1609 | static int | |
1610 | ReadMakefile(const char *fname) | 1610 | ReadMakefile(const char *fname) | |
1611 | { | 1611 | { | |
1612 | int fd; | 1612 | int fd; | |
1613 | char *name, *path = NULL; | 1613 | char *name, *path = NULL; | |
1614 | 1614 | |||
1615 | if (!strcmp(fname, "-")) { | 1615 | if (!strcmp(fname, "-")) { | |
1616 | Parse_File(NULL /*stdin*/, -1); | 1616 | Parse_File(NULL /*stdin*/, -1); | |
1617 | Var_Set("MAKEFILE", "", VAR_INTERNAL); | 1617 | Var_Set("MAKEFILE", "", VAR_INTERNAL); | |
1618 | } else { | 1618 | } else { | |
1619 | /* if we've chdir'd, rebuild the path name */ | 1619 | /* if we've chdir'd, rebuild the path name */ | |
1620 | if (strcmp(curdir, objdir) && *fname != '/') { | 1620 | if (strcmp(curdir, objdir) && *fname != '/') { | |
1621 | path = str_concat3(curdir, "/", fname); | 1621 | path = str_concat3(curdir, "/", fname); | |
1622 | fd = open(path, O_RDONLY); | 1622 | fd = open(path, O_RDONLY); | |
1623 | if (fd != -1) { | 1623 | if (fd != -1) { | |
1624 | fname = path; | 1624 | fname = path; | |
1625 | goto found; | 1625 | goto found; | |
1626 | } | 1626 | } | |
1627 | free(path); | 1627 | free(path); | |
1628 | 1628 | |||
1629 | /* If curdir failed, try objdir (ala .depend) */ | 1629 | /* If curdir failed, try objdir (ala .depend) */ | |
1630 | path = str_concat3(objdir, "/", fname); | 1630 | path = str_concat3(objdir, "/", fname); | |
1631 | fd = open(path, O_RDONLY); | 1631 | fd = open(path, O_RDONLY); | |
1632 | if (fd != -1) { | 1632 | if (fd != -1) { | |
1633 | fname = path; | 1633 | fname = path; | |
1634 | goto found; | 1634 | goto found; | |
1635 | } | 1635 | } | |
1636 | } else { | 1636 | } else { | |
1637 | fd = open(fname, O_RDONLY); | 1637 | fd = open(fname, O_RDONLY); | |
1638 | if (fd != -1) | 1638 | if (fd != -1) | |
1639 | goto found; | 1639 | goto found; | |
1640 | } | 1640 | } | |
1641 | /* look in -I and system include directories. */ | 1641 | /* look in -I and system include directories. */ | |
1642 | name = Dir_FindFile(fname, parseIncPath); | 1642 | name = Dir_FindFile(fname, parseIncPath); | |
1643 | if (!name) { | 1643 | if (!name) { | |
1644 | SearchPath *sysInc = Lst_IsEmpty(sysIncPath) | 1644 | SearchPath *sysInc = Lst_IsEmpty(sysIncPath) | |
1645 | ? defSysIncPath : sysIncPath; | 1645 | ? defSysIncPath : sysIncPath; | |
1646 | name = Dir_FindFile(fname, sysInc); | 1646 | name = Dir_FindFile(fname, sysInc); | |
1647 | } | 1647 | } | |
1648 | if (!name || (fd = open(name, O_RDONLY)) == -1) { | 1648 | if (!name || (fd = open(name, O_RDONLY)) == -1) { | |
1649 | free(name); | 1649 | free(name); | |
1650 | free(path); | 1650 | free(path); | |
1651 | return -1; | 1651 | return -1; | |
1652 | } | 1652 | } | |
1653 | fname = name; | 1653 | fname = name; | |
1654 | /* | 1654 | /* | |
1655 | * set the MAKEFILE variable desired by System V fans -- the | 1655 | * set the MAKEFILE variable desired by System V fans -- the | |
1656 | * placement of the setting here means it gets set to the last | 1656 | * placement of the setting here means it gets set to the last | |
1657 | * makefile specified, as it is set by SysV make. | 1657 | * makefile specified, as it is set by SysV make. | |
1658 | */ | 1658 | */ | |
1659 | found: | 1659 | found: | |
1660 | if (!doing_depend) | 1660 | if (!doing_depend) | |
1661 | Var_Set("MAKEFILE", fname, VAR_INTERNAL); | 1661 | Var_Set("MAKEFILE", fname, VAR_INTERNAL); | |
1662 | Parse_File(fname, fd); | 1662 | Parse_File(fname, fd); | |
1663 | } | 1663 | } | |
1664 | free(path); | 1664 | free(path); | |
1665 | return 0; | 1665 | return 0; | |
1666 | } | 1666 | } | |
1667 | 1667 | |||
1668 | 1668 | |||
1669 | 1669 | |||
1670 | /*- | 1670 | /*- | |
1671 | * Cmd_Exec -- | 1671 | * Cmd_Exec -- | |
1672 | * Execute the command in cmd, and return the output of that command | 1672 | * Execute the command in cmd, and return the output of that command | |
1673 | * in a string. In the output, newlines are replaced with spaces. | 1673 | * in a string. In the output, newlines are replaced with spaces. | |
1674 | * | 1674 | * | |
1675 | * Results: | 1675 | * Results: | |
1676 | * A string containing the output of the command, or the empty string. | 1676 | * A string containing the output of the command, or the empty string. | |
1677 | * *errfmt returns a format string describing the command failure, | 1677 | * *errfmt returns a format string describing the command failure, | |
1678 | * if any, using a single %s conversion specification. | 1678 | * if any, using a single %s conversion specification. | |
1679 | * | 1679 | * | |
1680 | * Side Effects: | 1680 | * Side Effects: | |
1681 | * The string must be freed by the caller. | 1681 | * The string must be freed by the caller. | |
1682 | */ | 1682 | */ | |
1683 | char * | 1683 | char * | |
1684 | Cmd_Exec(const char *cmd, const char **errfmt) | 1684 | Cmd_Exec(const char *cmd, const char **errfmt) | |
1685 | { | 1685 | { | |
1686 | const char *args[4]; /* Args for invoking the shell */ | 1686 | const char *args[4]; /* Args for invoking the shell */ | |
1687 | int fds[2]; /* Pipe streams */ | 1687 | int fds[2]; /* Pipe streams */ | |
1688 | int cpid; /* Child PID */ | 1688 | int cpid; /* Child PID */ | |
1689 | int pid; /* PID from wait() */ | 1689 | int pid; /* PID from wait() */ | |
1690 | int status; /* command exit status */ | 1690 | int status; /* command exit status */ | |
1691 | Buffer buf; /* buffer to store the result */ | 1691 | Buffer buf; /* buffer to store the result */ | |
1692 | ssize_t bytes_read; | 1692 | ssize_t bytes_read; | |
1693 | char *res; /* result */ | 1693 | char *res; /* result */ | |
1694 | size_t res_len; | 1694 | size_t res_len; | |
1695 | char *cp; | 1695 | char *cp; | |
1696 | int savederr; /* saved errno */ | 1696 | int savederr; /* saved errno */ | |
1697 | 1697 | |||
1698 | *errfmt = NULL; | 1698 | *errfmt = NULL; | |
1699 | 1699 | |||
1700 | if (!shellName) | 1700 | if (!shellName) | |
1701 | Shell_Init(); | 1701 | Shell_Init(); | |
1702 | /* | 1702 | /* | |
1703 | * Set up arguments for shell | 1703 | * Set up arguments for shell | |
1704 | */ | 1704 | */ | |
1705 | args[0] = shellName; | 1705 | args[0] = shellName; | |
1706 | args[1] = "-c"; | 1706 | args[1] = "-c"; | |
1707 | args[2] = cmd; | 1707 | args[2] = cmd; | |
1708 | args[3] = NULL; | 1708 | args[3] = NULL; | |
1709 | 1709 | |||
1710 | /* | 1710 | /* | |
1711 | * Open a pipe for fetching its output | 1711 | * Open a pipe for fetching its output | |
1712 | */ | 1712 | */ | |
1713 | if (pipe(fds) == -1) { | 1713 | if (pipe(fds) == -1) { | |
1714 | *errfmt = "Couldn't create pipe for \"%s\""; | 1714 | *errfmt = "Couldn't create pipe for \"%s\""; | |
1715 | goto bad; | 1715 | goto bad; | |
1716 | } | 1716 | } | |
1717 | 1717 | |||
1718 | /* | 1718 | /* | |
1719 | * Fork | 1719 | * Fork | |
1720 | */ | 1720 | */ | |
1721 | switch (cpid = vFork()) { | 1721 | switch (cpid = vFork()) { | |
1722 | case 0: | 1722 | case 0: | |
1723 | /* | 1723 | /* | |
1724 | * Close input side of pipe | 1724 | * Close input side of pipe | |
1725 | */ | 1725 | */ | |
1726 | (void)close(fds[0]); | 1726 | (void)close(fds[0]); | |
1727 | 1727 | |||
1728 | /* | 1728 | /* | |
1729 | * Duplicate the output stream to the shell's output, then | 1729 | * Duplicate the output stream to the shell's output, then | |
1730 | * shut the extra thing down. Note we don't fetch the error | 1730 | * shut the extra thing down. Note we don't fetch the error | |
1731 | * stream...why not? Why? | 1731 | * stream...why not? Why? | |
1732 | */ | 1732 | */ | |
1733 | (void)dup2(fds[1], 1); | 1733 | (void)dup2(fds[1], 1); | |
1734 | (void)close(fds[1]); | 1734 | (void)close(fds[1]); | |
1735 | 1735 | |||
1736 | Var_ExportVars(); | 1736 | Var_ExportVars(); | |
1737 | 1737 | |||
1738 | (void)execv(shellPath, UNCONST(args)); | 1738 | (void)execv(shellPath, UNCONST(args)); | |
1739 | _exit(1); | 1739 | _exit(1); | |
1740 | /*NOTREACHED*/ | 1740 | /*NOTREACHED*/ | |
1741 | 1741 | |||
1742 | case -1: | 1742 | case -1: | |
1743 | *errfmt = "Couldn't exec \"%s\""; | 1743 | *errfmt = "Couldn't exec \"%s\""; | |
1744 | goto bad; | 1744 | goto bad; | |
1745 | 1745 | |||
1746 | default: | 1746 | default: | |
1747 | /* | 1747 | /* | |
1748 | * No need for the writing half | 1748 | * No need for the writing half | |
1749 | */ | 1749 | */ | |
1750 | (void)close(fds[1]); | 1750 | (void)close(fds[1]); | |
1751 | 1751 | |||
1752 | savederr = 0; | 1752 | savederr = 0; | |
1753 | Buf_Init(&buf, 0); | 1753 | Buf_Init(&buf, 0); | |
1754 | 1754 | |||
1755 | do { | 1755 | do { | |
1756 | char result[BUFSIZ]; | 1756 | char result[BUFSIZ]; | |
1757 | bytes_read = read(fds[0], result, sizeof(result)); | 1757 | bytes_read = read(fds[0], result, sizeof(result)); | |
1758 | if (bytes_read > 0) | 1758 | if (bytes_read > 0) | |
1759 | Buf_AddBytes(&buf, result, (size_t)bytes_read); | 1759 | Buf_AddBytes(&buf, result, (size_t)bytes_read); | |
1760 | } | 1760 | } | |
1761 | while (bytes_read > 0 || (bytes_read == -1 && errno == EINTR)); | 1761 | while (bytes_read > 0 || (bytes_read == -1 && errno == EINTR)); | |
1762 | if (bytes_read == -1) | 1762 | if (bytes_read == -1) | |
1763 | savederr = errno; | 1763 | savederr = errno; | |
1764 | 1764 | |||
1765 | /* | 1765 | /* | |
1766 | * Close the input side of the pipe. | 1766 | * Close the input side of the pipe. | |
1767 | */ | 1767 | */ | |
1768 | (void)close(fds[0]); | 1768 | (void)close(fds[0]); | |
1769 | 1769 | |||
1770 | /* | 1770 | /* | |
1771 | * Wait for the process to exit. | 1771 | * Wait for the process to exit. | |
1772 | */ | 1772 | */ | |
1773 | while(((pid = waitpid(cpid, &status, 0)) != cpid) && (pid >= 0)) { | 1773 | while(((pid = waitpid(cpid, &status, 0)) != cpid) && (pid >= 0)) { | |
1774 | JobReapChild(pid, status, FALSE); | 1774 | JobReapChild(pid, status, FALSE); | |
1775 | continue; | 1775 | continue; | |
1776 | } | 1776 | } | |
1777 | res_len = Buf_Len(&buf); | 1777 | res_len = Buf_Len(&buf); | |
1778 | res = Buf_Destroy(&buf, FALSE); | 1778 | res = Buf_Destroy(&buf, FALSE); | |
1779 | 1779 | |||
1780 | if (savederr != 0) | 1780 | if (savederr != 0) | |
1781 | *errfmt = "Couldn't read shell's output for \"%s\""; | 1781 | *errfmt = "Couldn't read shell's output for \"%s\""; | |
1782 | 1782 | |||
1783 | if (WIFSIGNALED(status)) | 1783 | if (WIFSIGNALED(status)) | |
1784 | *errfmt = "\"%s\" exited on a signal"; | 1784 | *errfmt = "\"%s\" exited on a signal"; | |
1785 | else if (WEXITSTATUS(status) != 0) | 1785 | else if (WEXITSTATUS(status) != 0) | |
1786 | *errfmt = "\"%s\" returned non-zero status"; | 1786 | *errfmt = "\"%s\" returned non-zero status"; | |
1787 | 1787 | |||
1788 | /* Convert newlines to spaces. A final newline is just stripped */ | 1788 | /* Convert newlines to spaces. A final newline is just stripped */ | |
1789 | if (res_len > 0 && res[res_len - 1] == '\n') | 1789 | if (res_len > 0 && res[res_len - 1] == '\n') | |
1790 | res[res_len - 1] = '\0'; | 1790 | res[res_len - 1] = '\0'; | |
1791 | for (cp = res; *cp != '\0'; cp++) | 1791 | for (cp = res; *cp != '\0'; cp++) | |
1792 | if (*cp == '\n') | 1792 | if (*cp == '\n') | |
1793 | *cp = ' '; | 1793 | *cp = ' '; | |
1794 | break; | 1794 | break; | |
1795 | } | 1795 | } | |
1796 | return res; | 1796 | return res; | |
1797 | bad: | 1797 | bad: | |
1798 | return bmake_strdup(""); | 1798 | return bmake_strdup(""); | |
1799 | } | 1799 | } | |
1800 | 1800 | |||
1801 | /* Print a printf-style error message. | 1801 | /* Print a printf-style error message. | |
1802 | * | 1802 | * | |
1803 | * This error message has no consequences, in particular it does not affect | 1803 | * This error message has no consequences, in particular it does not affect | |
1804 | * the exit status. */ | 1804 | * the exit status. */ | |
1805 | void | 1805 | void | |
1806 | Error(const char *fmt, ...) | 1806 | Error(const char *fmt, ...) | |
1807 | { | 1807 | { | |
1808 | va_list ap; | 1808 | va_list ap; | |
1809 | FILE *err_file; | 1809 | FILE *err_file; | |
1810 | 1810 | |||
1811 | err_file = opts.debug_file; | 1811 | err_file = opts.debug_file; | |
1812 | if (err_file == stdout) | 1812 | if (err_file == stdout) | |
1813 | err_file = stderr; | 1813 | err_file = stderr; | |
1814 | (void)fflush(stdout); | 1814 | (void)fflush(stdout); | |
1815 | for (;;) { | 1815 | for (;;) { | |
1816 | va_start(ap, fmt); | 1816 | va_start(ap, fmt); | |
1817 | fprintf(err_file, "%s: ", progname); | 1817 | fprintf(err_file, "%s: ", progname); | |
1818 | (void)vfprintf(err_file, fmt, ap); | 1818 | (void)vfprintf(err_file, fmt, ap); | |
1819 | va_end(ap); | 1819 | va_end(ap); | |
1820 | (void)fprintf(err_file, "\n"); | 1820 | (void)fprintf(err_file, "\n"); | |
1821 | (void)fflush(err_file); | 1821 | (void)fflush(err_file); | |
1822 | if (err_file == stderr) | 1822 | if (err_file == stderr) | |
1823 | break; | 1823 | break; | |
1824 | err_file = stderr; | 1824 | err_file = stderr; | |
1825 | } | 1825 | } | |
1826 | } | 1826 | } | |
1827 | 1827 | |||
1828 | /* Produce a Fatal error message, then exit immediately. | 1828 | /* Produce a Fatal error message, then exit immediately. | |
1829 | * | 1829 | * | |
1830 | * If jobs are running, waits for them to finish. */ | 1830 | * If jobs are running, waits for them to finish. */ | |
1831 | void | 1831 | void | |
1832 | Fatal(const char *fmt, ...) | 1832 | Fatal(const char *fmt, ...) | |
1833 | { | 1833 | { | |
1834 | va_list ap; | 1834 | va_list ap; | |
1835 | 1835 | |||
1836 | va_start(ap, fmt); | 1836 | va_start(ap, fmt); | |
1837 | if (jobsRunning) | 1837 | if (jobsRunning) | |
1838 | Job_Wait(); | 1838 | Job_Wait(); | |
1839 | 1839 | |||
1840 | (void)fflush(stdout); | 1840 | (void)fflush(stdout); | |
1841 | (void)vfprintf(stderr, fmt, ap); | 1841 | (void)vfprintf(stderr, fmt, ap); | |
1842 | va_end(ap); | 1842 | va_end(ap); | |
1843 | (void)fprintf(stderr, "\n"); | 1843 | (void)fprintf(stderr, "\n"); | |
1844 | (void)fflush(stderr); | 1844 | (void)fflush(stderr); | |
1845 | 1845 | |||
1846 | PrintOnError(NULL, NULL); | 1846 | PrintOnError(NULL, NULL); | |
1847 | 1847 | |||
1848 | if (DEBUG(GRAPH2) || DEBUG(GRAPH3)) | 1848 | if (DEBUG(GRAPH2) || DEBUG(GRAPH3)) | |
1849 | Targ_PrintGraph(2); | 1849 | Targ_PrintGraph(2); | |
1850 | Trace_Log(MAKEERROR, 0); | 1850 | Trace_Log(MAKEERROR, 0); | |
1851 | exit(2); /* Not 1 so -q can distinguish error */ | 1851 | exit(2); /* Not 1 so -q can distinguish error */ | |
1852 | } | 1852 | } | |
1853 | 1853 | |||
1854 | /* Major exception once jobs are being created. | 1854 | /* Major exception once jobs are being created. | |
1855 | * Kills all jobs, prints a message and exits. */ | 1855 | * Kills all jobs, prints a message and exits. */ | |
1856 | void | 1856 | void | |
1857 | Punt(const char *fmt, ...) | 1857 | Punt(const char *fmt, ...) | |
1858 | { | 1858 | { | |
1859 | va_list ap; | 1859 | va_list ap; | |
1860 | 1860 | |||
1861 | va_start(ap, fmt); | 1861 | va_start(ap, fmt); | |
1862 | (void)fflush(stdout); | 1862 | (void)fflush(stdout); | |
1863 | (void)fprintf(stderr, "%s: ", progname); | 1863 | (void)fprintf(stderr, "%s: ", progname); | |
1864 | (void)vfprintf(stderr, fmt, ap); | 1864 | (void)vfprintf(stderr, fmt, ap); | |
1865 | va_end(ap); | 1865 | va_end(ap); | |
1866 | (void)fprintf(stderr, "\n"); | 1866 | (void)fprintf(stderr, "\n"); | |
1867 | (void)fflush(stderr); | 1867 | (void)fflush(stderr); | |
1868 | 1868 | |||
1869 | PrintOnError(NULL, NULL); | 1869 | PrintOnError(NULL, NULL); | |
1870 | 1870 | |||
1871 | DieHorribly(); | 1871 | DieHorribly(); | |
1872 | } | 1872 | } | |
1873 | 1873 | |||
1874 | /* Exit without giving a message. */ | 1874 | /* Exit without giving a message. */ | |
1875 | void | 1875 | void | |
1876 | DieHorribly(void) | 1876 | DieHorribly(void) | |
1877 | { | 1877 | { | |
1878 | if (jobsRunning) | 1878 | if (jobsRunning) | |
1879 | Job_AbortAll(); | 1879 | Job_AbortAll(); | |
1880 | if (DEBUG(GRAPH2)) | 1880 | if (DEBUG(GRAPH2)) | |
1881 | Targ_PrintGraph(2); | 1881 | Targ_PrintGraph(2); | |
1882 | Trace_Log(MAKEERROR, 0); | 1882 | Trace_Log(MAKEERROR, 0); | |
1883 | exit(2); /* Not 1, so -q can distinguish error */ | 1883 | exit(2); /* Not 1, so -q can distinguish error */ | |
1884 | } | 1884 | } | |
1885 | 1885 | |||
1886 | /* Called when aborting due to errors in child shell to signal abnormal exit. | 1886 | /* Called when aborting due to errors in child shell to signal abnormal exit. | |
1887 | * The program exits. | 1887 | * The program exits. | |
1888 | * Errors is the number of errors encountered in Make_Make. */ | 1888 | * Errors is the number of errors encountered in Make_Make. */ | |
1889 | void | 1889 | void | |
1890 | Finish(int errors) | 1890 | Finish(int errors) | |
1891 | { | 1891 | { | |
1892 | if (dieQuietly(NULL, -1)) | 1892 | if (dieQuietly(NULL, -1)) | |
1893 | exit(2); | 1893 | exit(2); | |
1894 | Fatal("%d error%s", errors, errors == 1 ? "" : "s"); | 1894 | Fatal("%d error%s", errors, errors == 1 ? "" : "s"); | |
1895 | } | 1895 | } | |
1896 | 1896 | |||
1897 | /* | 1897 | /* | |
1898 | * eunlink -- | 1898 | * eunlink -- | |
1899 | * Remove a file carefully, avoiding directories. | 1899 | * Remove a file carefully, avoiding directories. | |
1900 | */ | 1900 | */ | |
1901 | int | 1901 | int | |
1902 | eunlink(const char *file) | 1902 | eunlink(const char *file) | |
1903 | { | 1903 | { | |
1904 | struct stat st; | 1904 | struct stat st; | |
1905 | 1905 | |||
1906 | if (lstat(file, &st) == -1) | 1906 | if (lstat(file, &st) == -1) | |
1907 | return -1; | 1907 | return -1; | |
1908 | 1908 | |||
1909 | if (S_ISDIR(st.st_mode)) { | 1909 | if (S_ISDIR(st.st_mode)) { | |
1910 | errno = EISDIR; | 1910 | errno = EISDIR; | |
1911 | return -1; | 1911 | return -1; | |
1912 | } | 1912 | } | |
1913 | return unlink(file); | 1913 | return unlink(file); | |
1914 | } | 1914 | } | |
1915 | 1915 | |||
1916 | static void | 1916 | static void | |
1917 | write_all(int fd, const void *data, size_t n) | 1917 | write_all(int fd, const void *data, size_t n) | |
1918 | { | 1918 | { | |
1919 | const char *mem = data; | 1919 | const char *mem = data; | |
1920 | 1920 | |||
1921 | while (n > 0) { | 1921 | while (n > 0) { | |
1922 | ssize_t written = write(fd, mem, n); | 1922 | ssize_t written = write(fd, mem, n); | |
1923 | if (written == -1 && errno == EAGAIN) | 1923 | if (written == -1 && errno == EAGAIN) | |
1924 | continue; | 1924 | continue; | |
1925 | if (written == -1) | 1925 | if (written == -1) | |
1926 | break; | 1926 | break; | |
1927 | mem += written; | 1927 | mem += written; | |
1928 | n -= (size_t)written; | 1928 | n -= (size_t)written; | |
1929 | } | 1929 | } | |
1930 | } | 1930 | } | |
1931 | 1931 | |||
1932 | /* | 1932 | /* | |
1933 | * execDie -- | 1933 | * execDie -- | |
1934 | * Print why exec failed, avoiding stdio. | 1934 | * Print why exec failed, avoiding stdio. | |
1935 | */ | 1935 | */ | |
1936 | void MAKE_ATTR_DEAD | 1936 | void MAKE_ATTR_DEAD | |
1937 | execDie(const char *af, const char *av) | 1937 | execDie(const char *af, const char *av) | |
1938 | { | 1938 | { | |
1939 | Buffer buf; | 1939 | Buffer buf; | |
1940 | 1940 | |||
1941 | Buf_Init(&buf, 0); | 1941 | Buf_Init(&buf, 0); | |
1942 | Buf_AddStr(&buf, progname); | 1942 | Buf_AddStr(&buf, progname); | |
1943 | Buf_AddStr(&buf, ": "); | 1943 | Buf_AddStr(&buf, ": "); | |
1944 | Buf_AddStr(&buf, af); | 1944 | Buf_AddStr(&buf, af); | |
1945 | Buf_AddStr(&buf, "("); | 1945 | Buf_AddStr(&buf, "("); | |
1946 | Buf_AddStr(&buf, av); | 1946 | Buf_AddStr(&buf, av); | |
1947 | Buf_AddStr(&buf, ") failed ("); | 1947 | Buf_AddStr(&buf, ") failed ("); | |
1948 | Buf_AddStr(&buf, strerror(errno)); | 1948 | Buf_AddStr(&buf, strerror(errno)); | |
1949 | Buf_AddStr(&buf, ")\n"); | 1949 | Buf_AddStr(&buf, ")\n"); | |
1950 | 1950 | |||
1951 | write_all(STDERR_FILENO, Buf_GetAll(&buf, NULL), Buf_Len(&buf)); | 1951 | write_all(STDERR_FILENO, Buf_GetAll(&buf, NULL), Buf_Len(&buf)); | |
1952 | 1952 | |||
1953 | Buf_Destroy(&buf, TRUE); | 1953 | Buf_Destroy(&buf, TRUE); | |
1954 | _exit(1); | 1954 | _exit(1); | |
1955 | } | 1955 | } | |
1956 | 1956 | |||
1957 | /* | 1957 | /* | |
1958 | * usage -- | 1958 | * usage -- | |
1959 | * exit with usage message | 1959 | * exit with usage message | |
1960 | */ | 1960 | */ | |
1961 | static void | 1961 | static void | |
1962 | usage(void) | 1962 | usage(void) | |
1963 | { | 1963 | { | |
1964 | char *p; | 1964 | char *p; | |
1965 | if ((p = strchr(progname, '[')) != NULL) | 1965 | if ((p = strchr(progname, '[')) != NULL) | |
1966 | *p = '\0'; | 1966 | *p = '\0'; | |
1967 | 1967 | |||
1968 | (void)fprintf(stderr, | 1968 | (void)fprintf(stderr, | |
1969 | "usage: %s [-BeikNnqrstWwX] \n" | 1969 | "usage: %s [-BeikNnqrstWwX] \n" | |
1970 | " [-C directory] [-D variable] [-d flags] [-f makefile]\n" | 1970 | " [-C directory] [-D variable] [-d flags] [-f makefile]\n" | |
1971 | " [-I directory] [-J private] [-j max_jobs] [-m directory] [-T file]\n" | 1971 | " [-I directory] [-J private] [-j max_jobs] [-m directory] [-T file]\n" | |
1972 | " [-V variable] [-v variable] [variable=value] [target ...]\n", | 1972 | " [-V variable] [-v variable] [variable=value] [target ...]\n", | |
1973 | progname); | 1973 | progname); | |
1974 | exit(2); | 1974 | exit(2); | |
1975 | } | 1975 | } | |
1976 | 1976 | |||
1977 | /* | 1977 | /* | |
1978 | * realpath(3) can get expensive, cache results... | 1978 | * realpath(3) can get expensive, cache results... | |
1979 | */ | 1979 | */ | |
1980 | static GNode *cached_realpaths = NULL; | 1980 | static GNode *cached_realpaths = NULL; | |
1981 | 1981 | |||
1982 | static GNode * | 1982 | static GNode * | |
1983 | get_cached_realpaths(void) | 1983 | get_cached_realpaths(void) | |
1984 | { | 1984 | { | |
1985 | 1985 | |||
1986 | if (!cached_realpaths) { | 1986 | if (!cached_realpaths) { | |
1987 | cached_realpaths = Targ_NewGN("Realpath"); | 1987 | cached_realpaths = Targ_NewGN("Realpath"); | |
1988 | #ifndef DEBUG_REALPATH_CACHE | 1988 | #ifndef DEBUG_REALPATH_CACHE | |
1989 | cached_realpaths->flags = INTERNAL; | 1989 | cached_realpaths->flags = INTERNAL; | |
1990 | #endif | 1990 | #endif | |
1991 | } | 1991 | } | |
1992 | 1992 | |||
1993 | return cached_realpaths; | 1993 | return cached_realpaths; | |
1994 | } | 1994 | } | |
1995 | 1995 | |||
1996 | /* purge any relative paths */ | 1996 | /* purge any relative paths */ | |
1997 | static void | 1997 | static void | |
1998 | purge_cached_realpaths(void) | 1998 | purge_cached_realpaths(void) | |
1999 | { | 1999 | { | |
2000 | GNode *cache = get_cached_realpaths(); | 2000 | GNode *cache = get_cached_realpaths(); | |
2001 | HashEntry *he, *nhe; | 2001 | HashEntry *he, *nhe; | |
2002 | HashIter hi; | 2002 | HashIter hi; | |
2003 | 2003 | |||
2004 | HashIter_Init(&hi, &cache->context); | 2004 | HashIter_Init(&hi, &cache->context); | |
2005 | he = HashIter_Next(&hi); | 2005 | he = HashIter_Next(&hi); | |
2006 | while (he != NULL) { | 2006 | while (he != NULL) { | |
2007 | nhe = HashIter_Next(&hi); | 2007 | nhe = HashIter_Next(&hi); | |
2008 | if (he->key[0] != '/') { | 2008 | if (he->key[0] != '/') { | |
2009 | if (DEBUG(DIR)) | 2009 | if (DEBUG(DIR)) | |
2010 | fprintf(stderr, "cached_realpath: purging %s\n", he->key); | 2010 | fprintf(stderr, "cached_realpath: purging %s\n", he->key); | |
2011 | HashTable_DeleteEntry(&cache->context, he); | 2011 | HashTable_DeleteEntry(&cache->context, he); | |
2012 | } | 2012 | } | |
2013 | he = nhe; | 2013 | he = nhe; | |
2014 | } | 2014 | } | |
2015 | } | 2015 | } | |
2016 | 2016 | |||
2017 | char * | 2017 | char * | |
2018 | cached_realpath(const char *pathname, char *resolved) | 2018 | cached_realpath(const char *pathname, char *resolved) | |
2019 | { | 2019 | { | |
2020 | GNode *cache; | 2020 | GNode *cache; | |
2021 | const char *rp; | 2021 | const char *rp; | |
2022 | void *freeIt; | 2022 | void *freeIt; | |
2023 | 2023 | |||
2024 | if (!pathname || !pathname[0]) | 2024 | if (!pathname || !pathname[0]) | |
2025 | return NULL; | 2025 | return NULL; | |
2026 | 2026 | |||
2027 | cache = get_cached_realpaths(); | 2027 | cache = get_cached_realpaths(); | |
2028 | 2028 | |||
2029 | if ((rp = Var_Value(pathname, cache, &freeIt)) != NULL) { | 2029 | if ((rp = Var_Value(pathname, cache, &freeIt)) != NULL) { | |
2030 | /* a hit */ | 2030 | /* a hit */ | |
2031 | strncpy(resolved, rp, MAXPATHLEN); | 2031 | strncpy(resolved, rp, MAXPATHLEN); | |
2032 | resolved[MAXPATHLEN - 1] = '\0'; | 2032 | resolved[MAXPATHLEN - 1] = '\0'; | |
2033 | } else if ((rp = realpath(pathname, resolved)) != NULL) { | 2033 | } else if ((rp = realpath(pathname, resolved)) != NULL) { | |
2034 | Var_Set(pathname, rp, cache); | 2034 | Var_Set(pathname, rp, cache); | |
2035 | } /* else should we negative-cache? */ | 2035 | } /* else should we negative-cache? */ | |
2036 | 2036 | |||
2037 | bmake_free(freeIt); | 2037 | bmake_free(freeIt); | |
2038 | return rp ? resolved : NULL; | 2038 | return rp ? resolved : NULL; | |
2039 | } | 2039 | } | |
2040 | 2040 | |||
2041 | /* | 2041 | /* | |
2042 | * Return true if we should die without noise. | 2042 | * Return true if we should die without noise. | |
2043 | * For example our failing child was a sub-make | 2043 | * For example our failing child was a sub-make | |
2044 | * or failure happend elsewhere. | 2044 | * or failure happend elsewhere. | |
2045 | */ | 2045 | */ | |
2046 | int | 2046 | int | |
2047 | dieQuietly(GNode *gn, int bf) | 2047 | dieQuietly(GNode *gn, int bf) | |
2048 | { | 2048 | { | |
2049 | static int quietly = -1; | 2049 | static int quietly = -1; | |
2050 | 2050 | |||
2051 | if (quietly < 0) { | 2051 | if (quietly < 0) { | |
2052 | if (DEBUG(JOB) || getBoolean(".MAKE.DIE_QUIETLY", 1) == 0) | 2052 | if (DEBUG(JOB) || getBoolean(".MAKE.DIE_QUIETLY", 1) == 0) | |
2053 | quietly = 0; | 2053 | quietly = 0; | |
2054 | else if (bf >= 0) | 2054 | else if (bf >= 0) | |
2055 | quietly = bf; | 2055 | quietly = bf; | |
2056 | else | 2056 | else | |
2057 | quietly = gn != NULL ? ((gn->type & (OP_MAKE)) != 0) : 0; | 2057 | quietly = gn != NULL ? ((gn->type & (OP_MAKE)) != 0) : 0; | |
2058 | } | 2058 | } | |
2059 | return quietly; | 2059 | return quietly; | |
2060 | } | 2060 | } | |
2061 | 2061 | |||
2062 | static void | 2062 | static void | |
2063 | SetErrorVars(GNode *gn) | 2063 | SetErrorVars(GNode *gn) | |
2064 | { | 2064 | { | |
2065 | StringListNode *ln; | 2065 | StringListNode *ln; | |
2066 | 2066 | |||
2067 | /* | 2067 | /* | |
2068 | * We can print this even if there is no .ERROR target. | 2068 | * We can print this even if there is no .ERROR target. | |
2069 | */ | 2069 | */ | |
2070 | Var_Set(".ERROR_TARGET", gn->name, VAR_GLOBAL); | 2070 | Var_Set(".ERROR_TARGET", gn->name, VAR_GLOBAL); | |
2071 | Var_Delete(".ERROR_CMD", VAR_GLOBAL); | 2071 | Var_Delete(".ERROR_CMD", VAR_GLOBAL); | |
2072 | 2072 | |||
2073 | for (ln = gn->commands->first; ln != NULL; ln = ln->next) { | 2073 | for (ln = gn->commands->first; ln != NULL; ln = ln->next) { | |
2074 | const char *cmd = ln->datum; | 2074 | const char *cmd = ln->datum; | |
2075 | 2075 | |||
2076 | if (cmd == NULL) | 2076 | if (cmd == NULL) | |
2077 | break; | 2077 | break; | |
2078 | Var_Append(".ERROR_CMD", cmd, VAR_GLOBAL); | 2078 | Var_Append(".ERROR_CMD", cmd, VAR_GLOBAL); | |
2079 | } | 2079 | } | |
2080 | } | 2080 | } | |
2081 | 2081 | |||
2082 | void | 2082 | void | |
2083 | PrintOnError(GNode *gn, const char *s) | 2083 | PrintOnError(GNode *gn, const char *s) | |
2084 | { | 2084 | { | |
2085 | static GNode *en = NULL; | 2085 | static GNode *en = NULL; | |
2086 | const char *expr; | 2086 | const char *expr; | |
2087 | char *cp; | 2087 | char *cp; | |
2088 | 2088 | |||
2089 | if (DEBUG(HASH)) { | 2089 | if (DEBUG(HASH)) { | |
2090 | Targ_Stats(); | 2090 | Targ_Stats(); | |
2091 | Var_Stats(); | 2091 | Var_Stats(); | |
2092 | } | 2092 | } | |
2093 | 2093 | |||
2094 | /* we generally want to keep quiet if a sub-make died */ | 2094 | /* we generally want to keep quiet if a sub-make died */ | |
2095 | if (dieQuietly(gn, -1)) | 2095 | if (dieQuietly(gn, -1)) | |
2096 | return; | 2096 | return; | |
2097 | 2097 | |||
2098 | if (s) | 2098 | if (s) | |
2099 | printf("%s", s); | 2099 | printf("%s", s); | |
2100 | 2100 | |||
2101 | printf("\n%s: stopped in %s\n", progname, curdir); | 2101 | printf("\n%s: stopped in %s\n", progname, curdir); | |
2102 | 2102 | |||
2103 | if (en) | 2103 | if (en) | |
2104 | return; /* we've been here! */ | 2104 | return; /* we've been here! */ | |
2105 | if (gn) | 2105 | if (gn) | |
2106 | SetErrorVars(gn); | 2106 | SetErrorVars(gn); | |
2107 | expr = "${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'\n@}"; | 2107 | expr = "${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'\n@}"; | |
2108 | (void)Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES, &cp); | 2108 | (void)Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES, &cp); | |
2109 | /* TODO: handle errors */ | 2109 | /* TODO: handle errors */ | |
2110 | printf("%s", cp); | 2110 | printf("%s", cp); | |
2111 | free(cp); | 2111 | free(cp); | |
2112 | fflush(stdout); | 2112 | fflush(stdout); | |
2113 | 2113 | |||
2114 | /* | 2114 | /* | |
2115 | * Finally, see if there is a .ERROR target, and run it if so. | 2115 | * Finally, see if there is a .ERROR target, and run it if so. | |
2116 | */ | 2116 | */ | |
2117 | en = Targ_FindNode(".ERROR"); | 2117 | en = Targ_FindNode(".ERROR"); | |
2118 | if (en) { | 2118 | if (en) { | |
2119 | en->type |= OP_SPECIAL; | 2119 | en->type |= OP_SPECIAL; | |
2120 | Compat_Make(en, en); | 2120 | Compat_Make(en, en); | |
2121 | } | 2121 | } | |
2122 | } | 2122 | } | |
2123 | 2123 | |||
2124 | void | 2124 | void | |
2125 | Main_ExportMAKEFLAGS(Boolean first) | 2125 | Main_ExportMAKEFLAGS(Boolean first) | |
2126 | { | 2126 | { | |
2127 | static Boolean once = TRUE; | 2127 | static Boolean once = TRUE; | |
2128 | const char *expr; | 2128 | const char *expr; | |
2129 | char *s; | 2129 | char *s; | |
2130 | 2130 | |||
2131 | if (once != first) | 2131 | if (once != first) | |
2132 | return; | 2132 | return; | |
2133 | once = FALSE; | 2133 | once = FALSE; | |
2134 | 2134 | |||
2135 | expr = "${.MAKEFLAGS} ${.MAKEOVERRIDES:O:u:@v@$v=${$v:Q}@}"; | 2135 | expr = "${.MAKEFLAGS} ${.MAKEOVERRIDES:O:u:@v@$v=${$v:Q}@}"; | |
2136 | (void)Var_Subst(expr, VAR_CMDLINE, VARE_WANTRES, &s); | 2136 | (void)Var_Subst(expr, VAR_CMDLINE, VARE_WANTRES, &s); | |
2137 | /* TODO: handle errors */ | 2137 | /* TODO: handle errors */ | |
2138 | if (s[0] != '\0') { | 2138 | if (s[0] != '\0') { | |
2139 | #ifdef POSIX | 2139 | #ifdef POSIX | |
2140 | setenv("MAKEFLAGS", s, 1); | 2140 | setenv("MAKEFLAGS", s, 1); | |
2141 | #else | 2141 | #else | |
2142 | setenv("MAKE", s, 1); | 2142 | setenv("MAKE", s, 1); | |
2143 | #endif | 2143 | #endif | |
2144 | } | 2144 | } | |
2145 | } | 2145 | } | |
2146 | 2146 | |||
2147 | char * | 2147 | char * | |
2148 | getTmpdir(void) | 2148 | getTmpdir(void) | |
2149 | { | 2149 | { | |
2150 | static char *tmpdir = NULL; | 2150 | static char *tmpdir = NULL; | |
2151 | 2151 | |||
2152 | if (!tmpdir) { | 2152 | if (!tmpdir) { | |
2153 | struct stat st; | 2153 | struct stat st; | |
2154 | 2154 | |||
2155 | /* | 2155 | /* | |
2156 | * Honor $TMPDIR but only if it is valid. | 2156 | * Honor $TMPDIR but only if it is valid. | |
2157 | * Ensure it ends with /. | 2157 | * Ensure it ends with /. | |
2158 | */ | 2158 | */ | |
2159 | (void)Var_Subst("${TMPDIR:tA:U" _PATH_TMP "}/", VAR_GLOBAL, | 2159 | (void)Var_Subst("${TMPDIR:tA:U" _PATH_TMP "}/", VAR_GLOBAL, | |
2160 | VARE_WANTRES, &tmpdir); | 2160 | VARE_WANTRES, &tmpdir); | |
2161 | /* TODO: handle errors */ | 2161 | /* TODO: handle errors */ | |
2162 | if (stat(tmpdir, &st) < 0 || !S_ISDIR(st.st_mode)) { | 2162 | if (stat(tmpdir, &st) < 0 || !S_ISDIR(st.st_mode)) { | |
2163 | free(tmpdir); | 2163 | free(tmpdir); | |
2164 | tmpdir = bmake_strdup(_PATH_TMP); | 2164 | tmpdir = bmake_strdup(_PATH_TMP); | |
2165 | } | 2165 | } | |
2166 | } | 2166 | } | |
2167 | return tmpdir; | 2167 | return tmpdir; | |
2168 | } | 2168 | } | |
2169 | 2169 | |||
2170 | /* | 2170 | /* | |
2171 | * Create and open a temp file using "pattern". | 2171 | * Create and open a temp file using "pattern". | |
2172 | * If out_fname is provided, set it to a copy of the filename created. | 2172 | * If out_fname is provided, set it to a copy of the filename created. | |
2173 | * Otherwise unlink the file once open. | 2173 | * Otherwise unlink the file once open. | |
2174 | */ | 2174 | */ | |
2175 | int | 2175 | int | |
2176 | mkTempFile(const char *pattern, char **out_fname) | 2176 | mkTempFile(const char *pattern, char **out_fname) | |
2177 | { | 2177 | { | |
2178 | static char *tmpdir = NULL; | 2178 | static char *tmpdir = NULL; | |
2179 | char tfile[MAXPATHLEN]; | 2179 | char tfile[MAXPATHLEN]; | |
2180 | int fd; | 2180 | int fd; | |
2181 | 2181 | |||
2182 | if (pattern != NULL) | 2182 | if (pattern != NULL) | |
2183 | pattern = TMPPAT; | 2183 | pattern = TMPPAT; | |
2184 | if (tmpdir == NULL) | 2184 | if (tmpdir == NULL) | |
2185 | tmpdir = getTmpdir(); | 2185 | tmpdir = getTmpdir(); | |
2186 | if (pattern[0] == '/') { | 2186 | if (pattern[0] == '/') { | |
2187 | snprintf(tfile, sizeof(tfile), "%s", pattern); | 2187 | snprintf(tfile, sizeof(tfile), "%s", pattern); | |
2188 | } else { | 2188 | } else { | |
2189 | snprintf(tfile, sizeof(tfile), "%s%s", tmpdir, pattern); | 2189 | snprintf(tfile, sizeof(tfile), "%s%s", tmpdir, pattern); | |
2190 | } | 2190 | } | |
2191 | if ((fd = mkstemp(tfile)) < 0) | 2191 | if ((fd = mkstemp(tfile)) < 0) | |
2192 | Punt("Could not create temporary file %s: %s", tfile, strerror(errno)); | 2192 | Punt("Could not create temporary file %s: %s", tfile, strerror(errno)); | |
2193 | if (out_fname) { | 2193 | if (out_fname) { | |
2194 | *out_fname = bmake_strdup(tfile); | 2194 | *out_fname = bmake_strdup(tfile); | |
2195 | } else { | 2195 | } else { | |
2196 | unlink(tfile); /* we just want the descriptor */ | 2196 | unlink(tfile); /* we just want the descriptor */ | |
2197 | } | 2197 | } | |
2198 | return fd; | 2198 | return fd; | |
2199 | } | 2199 | } | |
2200 | 2200 | |||
2201 | /* | 2201 | /* | |
2202 | * Convert a string representation of a boolean. | 2202 | * Convert a string representation of a boolean. | |
2203 | * Anything that looks like "No", "False", "Off", "0" etc, | 2203 | * Anything that looks like "No", "False", "Off", "0" etc, | |
2204 | * is FALSE, otherwise TRUE. | 2204 | * is FALSE, otherwise TRUE. | |
2205 | */ | 2205 | */ | |
2206 | Boolean | 2206 | Boolean | |
2207 | s2Boolean(const char *s, Boolean bf) | 2207 | s2Boolean(const char *s, Boolean bf) | |
2208 | { | 2208 | { | |
2209 | if (s) { | 2209 | switch(s[0]) { | |
2210 | switch(*s) { | 2210 | case '\0': /* not set - the default wins */ | |
2211 | case '\0': /* not set - the default wins */ | 2211 | break; | |
2212 | break; | 2212 | case '0': | |
2213 | case '0': | 2213 | case 'F': | |
2214 | case 'F': | 2214 | case 'f': | |
2215 | case 'f': | 2215 | case 'N': | |
2216 | case 'N': | 2216 | case 'n': | |
2217 | case 'n': | 2217 | return FALSE; | |
2218 | return FALSE; | 2218 | case 'O': | |
2219 | case 'O': | 2219 | case 'o': | |
2220 | case 'o': | 2220 | return s[1] != 'F' && s[1] != 'f'; | |
2221 | return s[1] != 'F' && s[1] != 'f'; | 2221 | default: | |
2222 | default: | 2222 | return TRUE; | |
2223 | return TRUE; | |||
2224 | } | |||
2225 | } | 2223 | } | |
2226 | return bf; | 2224 | return bf; | |
2227 | } | 2225 | } | |
2228 | 2226 | |||
2229 | /* | 2227 | /* | |
2230 | * Return a Boolean based on setting of a knob. | 2228 | * Return a Boolean based on setting of a knob. | |
2231 | * | 2229 | * | |
2232 | * If the knob is not set, the supplied default is the return value. | 2230 | * If the knob is not set, the supplied default is the return value. | |
2233 | * If set, anything that looks or smells like "No", "False", "Off", "0" etc, | 2231 | * If set, anything that looks or smells like "No", "False", "Off", "0" etc, | |
2234 | * is FALSE, otherwise TRUE. | 2232 | * is FALSE, otherwise TRUE. | |
2235 | */ | 2233 | */ | |
2236 | Boolean | 2234 | Boolean | |
2237 | getBoolean(const char *name, Boolean fallback) | 2235 | getBoolean(const char *name, Boolean fallback) | |
2238 | { | 2236 | { | |
2239 | char *expr = str_concat3("${", name, ":U:tl}"); | 2237 | char *expr = str_concat3("${", name, ":U:tl}"); | |
2240 | char *value; | 2238 | char *value; | |
2241 | Boolean res; | 2239 | Boolean res; | |
2242 | 2240 | |||
2243 | (void)Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES, &value); | 2241 | (void)Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES, &value); | |
2244 | /* TODO: handle errors */ | 2242 | /* TODO: handle errors */ | |
2245 | res = s2Boolean(value, fallback); | 2243 | res = s2Boolean(value, fallback); | |
2246 | free(value); | 2244 | free(value); | |
2247 | free(expr); | 2245 | free(expr); | |
2248 | return res; | 2246 | return res; | |
2249 | } | 2247 | } |
--- src/usr.bin/make/var.c 2020/10/31 09:03:36 1.612
+++ src/usr.bin/make/var.c 2020/10/31 09:27:19 1.613
@@ -1,1855 +1,1855 @@ | @@ -1,1855 +1,1855 @@ | |||
1 | /* $NetBSD: var.c,v 1.612 2020/10/31 09:03:36 rillig Exp $ */ | 1 | /* $NetBSD: var.c,v 1.613 2020/10/31 09:27:19 rillig Exp $ */ | |
2 | 2 | |||
3 | /* | 3 | /* | |
4 | * Copyright (c) 1988, 1989, 1990, 1993 | 4 | * Copyright (c) 1988, 1989, 1990, 1993 | |
5 | * The Regents of the University of California. All rights reserved. | 5 | * The Regents of the University of California. All rights reserved. | |
6 | * | 6 | * | |
7 | * This code is derived from software contributed to Berkeley by | 7 | * This code is derived from software contributed to Berkeley by | |
8 | * Adam de Boor. | 8 | * Adam de Boor. | |
9 | * | 9 | * | |
10 | * Redistribution and use in source and binary forms, with or without | 10 | * Redistribution and use in source and binary forms, with or without | |
11 | * modification, are permitted provided that the following conditions | 11 | * modification, are permitted provided that the following conditions | |
12 | * are met: | 12 | * are met: | |
13 | * 1. Redistributions of source code must retain the above copyright | 13 | * 1. Redistributions of source code must retain the above copyright | |
14 | * notice, this list of conditions and the following disclaimer. | 14 | * notice, this list of conditions and the following disclaimer. | |
15 | * 2. Redistributions in binary form must reproduce the above copyright | 15 | * 2. Redistributions in binary form must reproduce the above copyright | |
16 | * notice, this list of conditions and the following disclaimer in the | 16 | * notice, this list of conditions and the following disclaimer in the | |
17 | * documentation and/or other materials provided with the distribution. | 17 | * documentation and/or other materials provided with the distribution. | |
18 | * 3. Neither the name of the University nor the names of its contributors | 18 | * 3. Neither the name of the University nor the names of its contributors | |
19 | * may be used to endorse or promote products derived from this software | 19 | * may be used to endorse or promote products derived from this software | |
20 | * without specific prior written permission. | 20 | * without specific prior written permission. | |
21 | * | 21 | * | |
22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | 22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | 25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | 27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | 28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | 29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | 30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | 31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
32 | * SUCH DAMAGE. | 32 | * SUCH DAMAGE. | |
33 | */ | 33 | */ | |
34 | 34 | |||
35 | /* | 35 | /* | |
36 | * Copyright (c) 1989 by Berkeley Softworks | 36 | * Copyright (c) 1989 by Berkeley Softworks | |
37 | * All rights reserved. | 37 | * All rights reserved. | |
38 | * | 38 | * | |
39 | * This code is derived from software contributed to Berkeley by | 39 | * This code is derived from software contributed to Berkeley by | |
40 | * Adam de Boor. | 40 | * Adam de Boor. | |
41 | * | 41 | * | |
42 | * Redistribution and use in source and binary forms, with or without | 42 | * Redistribution and use in source and binary forms, with or without | |
43 | * modification, are permitted provided that the following conditions | 43 | * modification, are permitted provided that the following conditions | |
44 | * are met: | 44 | * are met: | |
45 | * 1. Redistributions of source code must retain the above copyright | 45 | * 1. Redistributions of source code must retain the above copyright | |
46 | * notice, this list of conditions and the following disclaimer. | 46 | * notice, this list of conditions and the following disclaimer. | |
47 | * 2. Redistributions in binary form must reproduce the above copyright | 47 | * 2. Redistributions in binary form must reproduce the above copyright | |
48 | * notice, this list of conditions and the following disclaimer in the | 48 | * notice, this list of conditions and the following disclaimer in the | |
49 | * documentation and/or other materials provided with the distribution. | 49 | * documentation and/or other materials provided with the distribution. | |
50 | * 3. All advertising materials mentioning features or use of this software | 50 | * 3. All advertising materials mentioning features or use of this software | |
51 | * must display the following acknowledgement: | 51 | * must display the following acknowledgement: | |
52 | * This product includes software developed by the University of | 52 | * This product includes software developed by the University of | |
53 | * California, Berkeley and its contributors. | 53 | * California, Berkeley and its contributors. | |
54 | * 4. Neither the name of the University nor the names of its contributors | 54 | * 4. Neither the name of the University nor the names of its contributors | |
55 | * may be used to endorse or promote products derived from this software | 55 | * may be used to endorse or promote products derived from this software | |
56 | * without specific prior written permission. | 56 | * without specific prior written permission. | |
57 | * | 57 | * | |
58 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | 58 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
59 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 59 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
60 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 60 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
61 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | 61 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
62 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 62 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
63 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | 63 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
64 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | 64 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
65 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | 65 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
66 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | 66 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
67 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | 67 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
68 | * SUCH DAMAGE. | 68 | * SUCH DAMAGE. | |
69 | */ | 69 | */ | |
70 | 70 | |||
71 | /* | 71 | /* | |
72 | * Handling of variables and the expressions formed from them. | 72 | * Handling of variables and the expressions formed from them. | |
73 | * | 73 | * | |
74 | * Variables are set using lines of the form VAR=value. Both the variable | 74 | * Variables are set using lines of the form VAR=value. Both the variable | |
75 | * name and the value can contain references to other variables, by using | 75 | * name and the value can contain references to other variables, by using | |
76 | * expressions like ${VAR}, ${VAR:Modifiers}, ${${VARNAME}} or ${VAR:${MODS}}. | 76 | * expressions like ${VAR}, ${VAR:Modifiers}, ${${VARNAME}} or ${VAR:${MODS}}. | |
77 | * | 77 | * | |
78 | * Interface: | 78 | * Interface: | |
79 | * Var_Init Initialize this module. | 79 | * Var_Init Initialize this module. | |
80 | * | 80 | * | |
81 | * Var_End Clean up the module. | 81 | * Var_End Clean up the module. | |
82 | * | 82 | * | |
83 | * Var_Set Set the value of the variable, creating it if | 83 | * Var_Set Set the value of the variable, creating it if | |
84 | * necessary. | 84 | * necessary. | |
85 | * | 85 | * | |
86 | * Var_Append Append more characters to the variable, creating it if | 86 | * Var_Append Append more characters to the variable, creating it if | |
87 | * necessary. A space is placed between the old value and | 87 | * necessary. A space is placed between the old value and | |
88 | * the new one. | 88 | * the new one. | |
89 | * | 89 | * | |
90 | * Var_Exists See if a variable exists. | 90 | * Var_Exists See if a variable exists. | |
91 | * | 91 | * | |
92 | * Var_Value Return the unexpanded value of a variable, or NULL if | 92 | * Var_Value Return the unexpanded value of a variable, or NULL if | |
93 | * the variable is undefined. | 93 | * the variable is undefined. | |
94 | * | 94 | * | |
95 | * Var_Subst Substitute all variable expressions in a string. | 95 | * Var_Subst Substitute all variable expressions in a string. | |
96 | * | 96 | * | |
97 | * Var_Parse Parse a variable expression such as ${VAR:Mpattern}. | 97 | * Var_Parse Parse a variable expression such as ${VAR:Mpattern}. | |
98 | * | 98 | * | |
99 | * Var_Delete Delete a variable. | 99 | * Var_Delete Delete a variable. | |
100 | * | 100 | * | |
101 | * Var_ExportVars Export some or even all variables to the environment | 101 | * Var_ExportVars Export some or even all variables to the environment | |
102 | * of this process and its child processes. | 102 | * of this process and its child processes. | |
103 | * | 103 | * | |
104 | * Var_Export Export the variable to the environment of this process | 104 | * Var_Export Export the variable to the environment of this process | |
105 | * and its child processes. | 105 | * and its child processes. | |
106 | * | 106 | * | |
107 | * Var_UnExport Don't export the variable anymore. | 107 | * Var_UnExport Don't export the variable anymore. | |
108 | * | 108 | * | |
109 | * Debugging: | 109 | * Debugging: | |
110 | * Var_Stats Print out hashing statistics if in -dh mode. | 110 | * Var_Stats Print out hashing statistics if in -dh mode. | |
111 | * | 111 | * | |
112 | * Var_Dump Print out all variables defined in the given context. | 112 | * Var_Dump Print out all variables defined in the given context. | |
113 | * | 113 | * | |
114 | * XXX: There's a lot of duplication in these functions. | 114 | * XXX: There's a lot of duplication in these functions. | |
115 | */ | 115 | */ | |
116 | 116 | |||
117 | #include <sys/stat.h> | 117 | #include <sys/stat.h> | |
118 | #ifndef NO_REGEX | 118 | #ifndef NO_REGEX | |
119 | #include <sys/types.h> | 119 | #include <sys/types.h> | |
120 | #include <regex.h> | 120 | #include <regex.h> | |
121 | #endif | 121 | #endif | |
122 | #include <inttypes.h> | 122 | #include <inttypes.h> | |
123 | #include <limits.h> | 123 | #include <limits.h> | |
124 | #include <time.h> | 124 | #include <time.h> | |
125 | 125 | |||
126 | #include "make.h" | 126 | #include "make.h" | |
127 | #include "dir.h" | 127 | #include "dir.h" | |
128 | #include "job.h" | 128 | #include "job.h" | |
129 | #include "metachar.h" | 129 | #include "metachar.h" | |
130 | 130 | |||
131 | /* "@(#)var.c 8.3 (Berkeley) 3/19/94" */ | 131 | /* "@(#)var.c 8.3 (Berkeley) 3/19/94" */ | |
132 | MAKE_RCSID("$NetBSD: var.c,v 1.612 2020/10/31 09:03:36 rillig Exp $"); | 132 | MAKE_RCSID("$NetBSD: var.c,v 1.613 2020/10/31 09:27:19 rillig Exp $"); | |
133 | 133 | |||
134 | #define VAR_DEBUG1(fmt, arg1) DEBUG1(VAR, fmt, arg1) | 134 | #define VAR_DEBUG1(fmt, arg1) DEBUG1(VAR, fmt, arg1) | |
135 | #define VAR_DEBUG2(fmt, arg1, arg2) DEBUG2(VAR, fmt, arg1, arg2) | 135 | #define VAR_DEBUG2(fmt, arg1, arg2) DEBUG2(VAR, fmt, arg1, arg2) | |
136 | #define VAR_DEBUG3(fmt, arg1, arg2, arg3) DEBUG3(VAR, fmt, arg1, arg2, arg3) | 136 | #define VAR_DEBUG3(fmt, arg1, arg2, arg3) DEBUG3(VAR, fmt, arg1, arg2, arg3) | |
137 | #define VAR_DEBUG4(fmt, arg1, arg2, arg3, arg4) DEBUG4(VAR, fmt, arg1, arg2, arg3, arg4) | 137 | #define VAR_DEBUG4(fmt, arg1, arg2, arg3, arg4) DEBUG4(VAR, fmt, arg1, arg2, arg3, arg4) | |
138 | 138 | |||
139 | ENUM_FLAGS_RTTI_3(VarEvalFlags, | 139 | ENUM_FLAGS_RTTI_3(VarEvalFlags, | |
140 | VARE_UNDEFERR, VARE_WANTRES, VARE_ASSIGN); | 140 | VARE_UNDEFERR, VARE_WANTRES, VARE_ASSIGN); | |
141 | 141 | |||
142 | /* | 142 | /* | |
143 | * This lets us tell if we have replaced the original environ | 143 | * This lets us tell if we have replaced the original environ | |
144 | * (which we cannot free). | 144 | * (which we cannot free). | |
145 | */ | 145 | */ | |
146 | char **savedEnv = NULL; | 146 | char **savedEnv = NULL; | |
147 | 147 | |||
148 | /* Special return value for Var_Parse, indicating a parse error. It may be | 148 | /* Special return value for Var_Parse, indicating a parse error. It may be | |
149 | * caused by an undefined variable, a syntax error in a modifier or | 149 | * caused by an undefined variable, a syntax error in a modifier or | |
150 | * something entirely different. */ | 150 | * something entirely different. */ | |
151 | char var_Error[] = ""; | 151 | char var_Error[] = ""; | |
152 | 152 | |||
153 | /* Special return value for Var_Parse, indicating an undefined variable in | 153 | /* Special return value for Var_Parse, indicating an undefined variable in | |
154 | * a case where VARE_UNDEFERR is not set. This undefined variable is | 154 | * a case where VARE_UNDEFERR is not set. This undefined variable is | |
155 | * typically a dynamic variable such as ${.TARGET}, whose expansion needs to | 155 | * typically a dynamic variable such as ${.TARGET}, whose expansion needs to | |
156 | * be deferred until it is defined in an actual target. */ | 156 | * be deferred until it is defined in an actual target. */ | |
157 | static char varUndefined[] = ""; | 157 | static char varUndefined[] = ""; | |
158 | 158 | |||
159 | /* Special return value for Var_Parse, just to avoid allocating empty strings. | 159 | /* Special return value for Var_Parse, just to avoid allocating empty strings. | |
160 | * In contrast to var_Error and varUndefined, this is not an error marker but | 160 | * In contrast to var_Error and varUndefined, this is not an error marker but | |
161 | * just an ordinary successful return value. */ | 161 | * just an ordinary successful return value. */ | |
162 | static char emptyString[] = ""; | 162 | static char emptyString[] = ""; | |
163 | 163 | |||
164 | /* | 164 | /* | |
165 | * Traditionally this make consumed $$ during := like any other expansion. | 165 | * Traditionally this make consumed $$ during := like any other expansion. | |
166 | * Other make's do not, and this make follows straight since 2016-01-09. | 166 | * Other make's do not, and this make follows straight since 2016-01-09. | |
167 | * | 167 | * | |
168 | * This knob allows controlling the behavior. | 168 | * This knob allows controlling the behavior. | |
169 | * FALSE to consume $$ during := assignment. | 169 | * FALSE to consume $$ during := assignment. | |
170 | * TRUE to preserve $$ during := assignment. | 170 | * TRUE to preserve $$ during := assignment. | |
171 | */ | 171 | */ | |
172 | #define MAKE_SAVE_DOLLARS ".MAKE.SAVE_DOLLARS" | 172 | #define MAKE_SAVE_DOLLARS ".MAKE.SAVE_DOLLARS" | |
173 | static Boolean save_dollars = TRUE; | 173 | static Boolean save_dollars = TRUE; | |
174 | 174 | |||
175 | /* | 175 | /* | |
176 | * Internally, variables are contained in four different contexts. | 176 | * Internally, variables are contained in four different contexts. | |
177 | * 1) the environment. They cannot be changed. If an environment | 177 | * 1) the environment. They cannot be changed. If an environment | |
178 | * variable is appended to, the result is placed in the global | 178 | * variable is appended to, the result is placed in the global | |
179 | * context. | 179 | * context. | |
180 | * 2) the global context. Variables set in the makefiles are located | 180 | * 2) the global context. Variables set in the makefiles are located | |
181 | * here. | 181 | * here. | |
182 | * 3) the command-line context. All variables set on the command line | 182 | * 3) the command-line context. All variables set on the command line | |
183 | * are placed in this context. | 183 | * are placed in this context. | |
184 | * 4) the local context. Each target has associated with it a context | 184 | * 4) the local context. Each target has associated with it a context | |
185 | * list. On this list are located the structures describing such | 185 | * list. On this list are located the structures describing such | |
186 | * local variables as $(@) and $(*) | 186 | * local variables as $(@) and $(*) | |
187 | * The four contexts are searched in the reverse order from which they are | 187 | * The four contexts are searched in the reverse order from which they are | |
188 | * listed (but see opts.checkEnvFirst). | 188 | * listed (but see opts.checkEnvFirst). | |
189 | */ | 189 | */ | |
190 | GNode *VAR_INTERNAL; /* variables from make itself */ | 190 | GNode *VAR_INTERNAL; /* variables from make itself */ | |
191 | GNode *VAR_GLOBAL; /* variables from the makefile */ | 191 | GNode *VAR_GLOBAL; /* variables from the makefile */ | |
192 | GNode *VAR_CMDLINE; /* variables defined on the command-line */ | 192 | GNode *VAR_CMDLINE; /* variables defined on the command-line */ | |
193 | 193 | |||
194 | typedef enum VarFlags { | 194 | typedef enum VarFlags { | |
195 | 195 | |||
196 | /* The variable's value is currently being used by Var_Parse or Var_Subst. | 196 | /* The variable's value is currently being used by Var_Parse or Var_Subst. | |
197 | * This marker is used to avoid endless recursion. */ | 197 | * This marker is used to avoid endless recursion. */ | |
198 | VAR_IN_USE = 0x01, | 198 | VAR_IN_USE = 0x01, | |
199 | 199 | |||
200 | /* The variable comes from the environment. | 200 | /* The variable comes from the environment. | |
201 | * These variables are not registered in any GNode, therefore they must | 201 | * These variables are not registered in any GNode, therefore they must | |
202 | * be freed as soon as they are not used anymore. */ | 202 | * be freed as soon as they are not used anymore. */ | |
203 | VAR_FROM_ENV = 0x02, | 203 | VAR_FROM_ENV = 0x02, | |
204 | 204 | |||
205 | /* The variable is exported to the environment, to be used by child | 205 | /* The variable is exported to the environment, to be used by child | |
206 | * processes. */ | 206 | * processes. */ | |
207 | VAR_EXPORTED = 0x10, | 207 | VAR_EXPORTED = 0x10, | |
208 | 208 | |||
209 | /* At the point where this variable was exported, it contained an | 209 | /* At the point where this variable was exported, it contained an | |
210 | * unresolved reference to another variable. Before any child process is | 210 | * unresolved reference to another variable. Before any child process is | |
211 | * started, it needs to be exported again, in the hope that the referenced | 211 | * started, it needs to be exported again, in the hope that the referenced | |
212 | * variable can then be resolved. */ | 212 | * variable can then be resolved. */ | |
213 | VAR_REEXPORT = 0x20, | 213 | VAR_REEXPORT = 0x20, | |
214 | 214 | |||
215 | /* The variable came from the command line. */ | 215 | /* The variable came from the command line. */ | |
216 | VAR_FROM_CMD = 0x40, | 216 | VAR_FROM_CMD = 0x40, | |
217 | 217 | |||
218 | /* The variable value cannot be changed anymore, and the variable cannot | 218 | /* The variable value cannot be changed anymore, and the variable cannot | |
219 | * be deleted. Any attempts to do so are ignored. */ | 219 | * be deleted. Any attempts to do so are ignored. */ | |
220 | VAR_READONLY = 0x80 | 220 | VAR_READONLY = 0x80 | |
221 | } VarFlags; | 221 | } VarFlags; | |
222 | 222 | |||
223 | ENUM_FLAGS_RTTI_6(VarFlags, | 223 | ENUM_FLAGS_RTTI_6(VarFlags, | |
224 | VAR_IN_USE, VAR_FROM_ENV, | 224 | VAR_IN_USE, VAR_FROM_ENV, | |
225 | VAR_EXPORTED, VAR_REEXPORT, VAR_FROM_CMD, VAR_READONLY); | 225 | VAR_EXPORTED, VAR_REEXPORT, VAR_FROM_CMD, VAR_READONLY); | |
226 | 226 | |||
227 | /* Variables are defined using one of the VAR=value assignments. Their | 227 | /* Variables are defined using one of the VAR=value assignments. Their | |
228 | * value can be queried by expressions such as $V, ${VAR}, or with modifiers | 228 | * value can be queried by expressions such as $V, ${VAR}, or with modifiers | |
229 | * such as ${VAR:S,from,to,g:Q}. | 229 | * such as ${VAR:S,from,to,g:Q}. | |
230 | * | 230 | * | |
231 | * There are 3 kinds of variables: context variables, environment variables, | 231 | * There are 3 kinds of variables: context variables, environment variables, | |
232 | * undefined variables. | 232 | * undefined variables. | |
233 | * | 233 | * | |
234 | * Context variables are stored in a GNode.context. The only way to undefine | 234 | * Context variables are stored in a GNode.context. The only way to undefine | |
235 | * a context variable is using the .undef directive. In particular, it must | 235 | * a context variable is using the .undef directive. In particular, it must | |
236 | * not be possible to undefine a variable during the evaluation of an | 236 | * not be possible to undefine a variable during the evaluation of an | |
237 | * expression, or Var.name might point nowhere. | 237 | * expression, or Var.name might point nowhere. | |
238 | * | 238 | * | |
239 | * Environment variables are temporary. They are returned by VarFind, and | 239 | * Environment variables are temporary. They are returned by VarFind, and | |
240 | * after using them, they must be freed using VarFreeEnv. | 240 | * after using them, they must be freed using VarFreeEnv. | |
241 | * | 241 | * | |
242 | * Undefined variables occur during evaluation of variable expressions such | 242 | * Undefined variables occur during evaluation of variable expressions such | |
243 | * as ${UNDEF:Ufallback} in Var_Parse and ApplyModifiers. | 243 | * as ${UNDEF:Ufallback} in Var_Parse and ApplyModifiers. | |
244 | */ | 244 | */ | |
245 | typedef struct Var { | 245 | typedef struct Var { | |
246 | /* The name of the variable, once set, doesn't change anymore. | 246 | /* The name of the variable, once set, doesn't change anymore. | |
247 | * For context variables, it aliases the corresponding HashEntry name. | 247 | * For context variables, it aliases the corresponding HashEntry name. | |
248 | * For environment and undefined variables, it is allocated. */ | 248 | * For environment and undefined variables, it is allocated. */ | |
249 | const char *name; | 249 | const char *name; | |
250 | void *name_freeIt; | 250 | void *name_freeIt; | |
251 | 251 | |||
252 | Buffer val; /* its value */ | 252 | Buffer val; /* its value */ | |
253 | VarFlags flags; /* miscellaneous status flags */ | 253 | VarFlags flags; /* miscellaneous status flags */ | |
254 | } Var; | 254 | } Var; | |
255 | 255 | |||
256 | /* | 256 | /* | |
257 | * Exporting vars is expensive so skip it if we can | 257 | * Exporting vars is expensive so skip it if we can | |
258 | */ | 258 | */ | |
259 | typedef enum VarExportedMode { | 259 | typedef enum VarExportedMode { | |
260 | VAR_EXPORTED_NONE, | 260 | VAR_EXPORTED_NONE, | |
261 | VAR_EXPORTED_SOME, | 261 | VAR_EXPORTED_SOME, | |
262 | VAR_EXPORTED_ALL | 262 | VAR_EXPORTED_ALL | |
263 | } VarExportedMode; | 263 | } VarExportedMode; | |
264 | 264 | |||
265 | static VarExportedMode var_exportedVars = VAR_EXPORTED_NONE; | 265 | static VarExportedMode var_exportedVars = VAR_EXPORTED_NONE; | |
266 | 266 | |||
267 | typedef enum VarExportFlags { | 267 | typedef enum VarExportFlags { | |
268 | /* | 268 | /* | |
269 | * We pass this to Var_Export when doing the initial export | 269 | * We pass this to Var_Export when doing the initial export | |
270 | * or after updating an exported var. | 270 | * or after updating an exported var. | |
271 | */ | 271 | */ | |
272 | VAR_EXPORT_PARENT = 0x01, | 272 | VAR_EXPORT_PARENT = 0x01, | |
273 | /* | 273 | /* | |
274 | * We pass this to Var_Export1 to tell it to leave the value alone. | 274 | * We pass this to Var_Export1 to tell it to leave the value alone. | |
275 | */ | 275 | */ | |
276 | VAR_EXPORT_LITERAL = 0x02 | 276 | VAR_EXPORT_LITERAL = 0x02 | |
277 | } VarExportFlags; | 277 | } VarExportFlags; | |
278 | 278 | |||
279 | /* Flags for pattern matching in the :S and :C modifiers */ | 279 | /* Flags for pattern matching in the :S and :C modifiers */ | |
280 | typedef enum VarPatternFlags { | 280 | typedef enum VarPatternFlags { | |
281 | VARP_SUB_GLOBAL = 0x01, /* Replace as often as possible ('g') */ | 281 | VARP_SUB_GLOBAL = 0x01, /* Replace as often as possible ('g') */ | |
282 | VARP_SUB_ONE = 0x02, /* Replace only once ('1') */ | 282 | VARP_SUB_ONE = 0x02, /* Replace only once ('1') */ | |
283 | VARP_ANCHOR_START = 0x04, /* Match at start of word ('^') */ | 283 | VARP_ANCHOR_START = 0x04, /* Match at start of word ('^') */ | |
284 | VARP_ANCHOR_END = 0x08 /* Match at end of word ('$') */ | 284 | VARP_ANCHOR_END = 0x08 /* Match at end of word ('$') */ | |
285 | } VarPatternFlags; | 285 | } VarPatternFlags; | |
286 | 286 | |||
287 | static Var * | 287 | static Var * | |
288 | VarNew(const char *name, void *name_freeIt, const char *value, VarFlags flags) | 288 | VarNew(const char *name, void *name_freeIt, const char *value, VarFlags flags) | |
289 | { | 289 | { | |
290 | size_t value_len = strlen(value); | 290 | size_t value_len = strlen(value); | |
291 | Var *var = bmake_malloc(sizeof *var); | 291 | Var *var = bmake_malloc(sizeof *var); | |
292 | var->name = name; | 292 | var->name = name; | |
293 | var->name_freeIt = name_freeIt; | 293 | var->name_freeIt = name_freeIt; | |
294 | Buf_Init(&var->val, value_len + 1); | 294 | Buf_Init(&var->val, value_len + 1); | |
295 | Buf_AddBytes(&var->val, value, value_len); | 295 | Buf_AddBytes(&var->val, value, value_len); | |
296 | var->flags = flags; | 296 | var->flags = flags; | |
297 | return var; | 297 | return var; | |
298 | } | 298 | } | |
299 | 299 | |||
300 | static const char * | 300 | static const char * | |
301 | CanonicalVarname(const char *name) | 301 | CanonicalVarname(const char *name) | |
302 | { | 302 | { | |
303 | if (*name == '.' && ch_isupper(name[1])) { | 303 | if (*name == '.' && ch_isupper(name[1])) { | |
304 | switch (name[1]) { | 304 | switch (name[1]) { | |
305 | case 'A': | 305 | case 'A': | |
306 | if (strcmp(name, ".ALLSRC") == 0) | 306 | if (strcmp(name, ".ALLSRC") == 0) | |
307 | name = ALLSRC; | 307 | name = ALLSRC; | |
308 | if (strcmp(name, ".ARCHIVE") == 0) | 308 | if (strcmp(name, ".ARCHIVE") == 0) | |
309 | name = ARCHIVE; | 309 | name = ARCHIVE; | |
310 | break; | 310 | break; | |
311 | case 'I': | 311 | case 'I': | |
312 | if (strcmp(name, ".IMPSRC") == 0) | 312 | if (strcmp(name, ".IMPSRC") == 0) | |
313 | name = IMPSRC; | 313 | name = IMPSRC; | |
314 | break; | 314 | break; | |
315 | case 'M': | 315 | case 'M': | |
316 | if (strcmp(name, ".MEMBER") == 0) | 316 | if (strcmp(name, ".MEMBER") == 0) | |
317 | name = MEMBER; | 317 | name = MEMBER; | |
318 | break; | 318 | break; | |
319 | case 'O': | 319 | case 'O': | |
320 | if (strcmp(name, ".OODATE") == 0) | 320 | if (strcmp(name, ".OODATE") == 0) | |
321 | name = OODATE; | 321 | name = OODATE; | |
322 | break; | 322 | break; | |
323 | case 'P': | 323 | case 'P': | |
324 | if (strcmp(name, ".PREFIX") == 0) | 324 | if (strcmp(name, ".PREFIX") == 0) | |
325 | name = PREFIX; | 325 | name = PREFIX; | |
326 | break; | 326 | break; | |
327 | case 'S': | 327 | case 'S': | |
328 | if (strcmp(name, ".SHELL") == 0) { | 328 | if (strcmp(name, ".SHELL") == 0) { | |
329 | if (!shellPath) | 329 | if (!shellPath) | |
330 | Shell_Init(); | 330 | Shell_Init(); | |
331 | } | 331 | } | |
332 | break; | 332 | break; | |
333 | case 'T': | 333 | case 'T': | |
334 | if (strcmp(name, ".TARGET") == 0) | 334 | if (strcmp(name, ".TARGET") == 0) | |
335 | name = TARGET; | 335 | name = TARGET; | |
336 | break; | 336 | break; | |
337 | } | 337 | } | |
338 | } | 338 | } | |
339 | 339 | |||
340 | /* GNU make has an additional alias $^ == ${.ALLSRC}. */ | 340 | /* GNU make has an additional alias $^ == ${.ALLSRC}. */ | |
341 | 341 | |||
342 | return name; | 342 | return name; | |
343 | } | 343 | } | |
344 | 344 | |||
345 | static Var * | 345 | static Var * | |
346 | GNode_FindVar(GNode *ctxt, const char *varname, unsigned int hash) | 346 | GNode_FindVar(GNode *ctxt, const char *varname, unsigned int hash) | |
347 | { | 347 | { | |
348 | return HashTable_FindValueHash(&ctxt->context, varname, hash); | 348 | return HashTable_FindValueHash(&ctxt->context, varname, hash); | |
349 | } | 349 | } | |
350 | 350 | |||
351 | /* Find the variable in the context, and maybe in other contexts as well. | 351 | /* Find the variable in the context, and maybe in other contexts as well. | |
352 | * | 352 | * | |
353 | * Input: | 353 | * Input: | |
354 | * name name to find, is not expanded any further | 354 | * name name to find, is not expanded any further | |
355 | * ctxt context in which to look first | 355 | * ctxt context in which to look first | |
356 | * elsewhere TRUE to look in other contexts as well | 356 | * elsewhere TRUE to look in other contexts as well | |
357 | * | 357 | * | |
358 | * Results: | 358 | * Results: | |
359 | * The found variable, or NULL if the variable does not exist. | 359 | * The found variable, or NULL if the variable does not exist. | |
360 | * If the variable is an environment variable, it must be freed using | 360 | * If the variable is an environment variable, it must be freed using | |
361 | * VarFreeEnv after use. | 361 | * VarFreeEnv after use. | |
362 | */ | 362 | */ | |
363 | static Var * | 363 | static Var * | |
364 | VarFind(const char *name, GNode *ctxt, Boolean elsewhere) | 364 | VarFind(const char *name, GNode *ctxt, Boolean elsewhere) | |
365 | { | 365 | { | |
366 | Var *var; | 366 | Var *var; | |
367 | unsigned int nameHash; | 367 | unsigned int nameHash; | |
368 | 368 | |||
369 | /* | 369 | /* | |
370 | * If the variable name begins with a '.', it could very well be one of | 370 | * If the variable name begins with a '.', it could very well be one of | |
371 | * the local ones. We check the name against all the local variables | 371 | * the local ones. We check the name against all the local variables | |
372 | * and substitute the short version in for 'name' if it matches one of | 372 | * and substitute the short version in for 'name' if it matches one of | |
373 | * them. | 373 | * them. | |
374 | */ | 374 | */ | |
375 | name = CanonicalVarname(name); | 375 | name = CanonicalVarname(name); | |
376 | nameHash = Hash_Hash(name); | 376 | nameHash = Hash_Hash(name); | |
377 | 377 | |||
378 | /* First look for the variable in the given context. */ | 378 | /* First look for the variable in the given context. */ | |
379 | var = GNode_FindVar(ctxt, name, nameHash); | 379 | var = GNode_FindVar(ctxt, name, nameHash); | |
380 | if (!elsewhere) | 380 | if (!elsewhere) | |
381 | return var; | 381 | return var; | |
382 | 382 | |||
383 | /* The variable was not found in the given context. Now look for it in | 383 | /* The variable was not found in the given context. Now look for it in | |
384 | * the other contexts as well. */ | 384 | * the other contexts as well. */ | |
385 | if (var == NULL && ctxt != VAR_CMDLINE) | 385 | if (var == NULL && ctxt != VAR_CMDLINE) | |
386 | var = GNode_FindVar(VAR_CMDLINE, name, nameHash); | 386 | var = GNode_FindVar(VAR_CMDLINE, name, nameHash); | |
387 | 387 | |||
388 | if (!opts.checkEnvFirst && var == NULL && ctxt != VAR_GLOBAL) { | 388 | if (!opts.checkEnvFirst && var == NULL && ctxt != VAR_GLOBAL) { | |
389 | var = GNode_FindVar(VAR_GLOBAL, name, nameHash); | 389 | var = GNode_FindVar(VAR_GLOBAL, name, nameHash); | |
390 | if (var == NULL && ctxt != VAR_INTERNAL) { | 390 | if (var == NULL && ctxt != VAR_INTERNAL) { | |
391 | /* VAR_INTERNAL is subordinate to VAR_GLOBAL */ | 391 | /* VAR_INTERNAL is subordinate to VAR_GLOBAL */ | |
392 | var = GNode_FindVar(VAR_INTERNAL, name, nameHash); | 392 | var = GNode_FindVar(VAR_INTERNAL, name, nameHash); | |
393 | } | 393 | } | |
394 | } | 394 | } | |
395 | 395 | |||
396 | if (var == NULL) { | 396 | if (var == NULL) { | |
397 | char *env; | 397 | char *env; | |
398 | 398 | |||
399 | if ((env = getenv(name)) != NULL) { | 399 | if ((env = getenv(name)) != NULL) { | |
400 | char *varname = bmake_strdup(name); | 400 | char *varname = bmake_strdup(name); | |
401 | return VarNew(varname, varname, env, VAR_FROM_ENV); | 401 | return VarNew(varname, varname, env, VAR_FROM_ENV); | |
402 | } | 402 | } | |
403 | 403 | |||
404 | if (opts.checkEnvFirst && ctxt != VAR_GLOBAL) { | 404 | if (opts.checkEnvFirst && ctxt != VAR_GLOBAL) { | |
405 | var = GNode_FindVar(VAR_GLOBAL, name, nameHash); | 405 | var = GNode_FindVar(VAR_GLOBAL, name, nameHash); | |
406 | if (var == NULL && ctxt != VAR_INTERNAL) | 406 | if (var == NULL && ctxt != VAR_INTERNAL) | |
407 | var = GNode_FindVar(VAR_INTERNAL, name, nameHash); | 407 | var = GNode_FindVar(VAR_INTERNAL, name, nameHash); | |
408 | return var; | 408 | return var; | |
409 | } | 409 | } | |
410 | 410 | |||
411 | return NULL; | 411 | return NULL; | |
412 | } | 412 | } | |
413 | 413 | |||
414 | return var; | 414 | return var; | |
415 | } | 415 | } | |
416 | 416 | |||
417 | /* If the variable is an environment variable, free it. | 417 | /* If the variable is an environment variable, free it. | |
418 | * | 418 | * | |
419 | * Input: | 419 | * Input: | |
420 | * v the variable | 420 | * v the variable | |
421 | * freeValue true if the variable value should be freed as well | 421 | * freeValue true if the variable value should be freed as well | |
422 | * | 422 | * | |
423 | * Results: | 423 | * Results: | |
424 | * TRUE if it is an environment variable, FALSE otherwise. | 424 | * TRUE if it is an environment variable, FALSE otherwise. | |
425 | */ | 425 | */ | |
426 | static Boolean | 426 | static Boolean | |
427 | VarFreeEnv(Var *v, Boolean freeValue) | 427 | VarFreeEnv(Var *v, Boolean freeValue) | |
428 | { | 428 | { | |
429 | if (!(v->flags & VAR_FROM_ENV)) | 429 | if (!(v->flags & VAR_FROM_ENV)) | |
430 | return FALSE; | 430 | return FALSE; | |
431 | 431 | |||
432 | free(v->name_freeIt); | 432 | free(v->name_freeIt); | |
433 | Buf_Destroy(&v->val, freeValue); | 433 | Buf_Destroy(&v->val, freeValue); | |
434 | free(v); | 434 | free(v); | |
435 | return TRUE; | 435 | return TRUE; | |
436 | } | 436 | } | |
437 | 437 | |||
438 | /* Add a new variable of the given name and value to the given context. | 438 | /* Add a new variable of the given name and value to the given context. | |
439 | * The name and val arguments are duplicated so they may safely be freed. */ | 439 | * The name and val arguments are duplicated so they may safely be freed. */ | |
440 | static void | 440 | static void | |
441 | VarAdd(const char *name, const char *val, GNode *ctxt, VarSet_Flags flags) | 441 | VarAdd(const char *name, const char *val, GNode *ctxt, VarSet_Flags flags) | |
442 | { | 442 | { | |
443 | HashEntry *he = HashTable_CreateEntry(&ctxt->context, name, NULL); | 443 | HashEntry *he = HashTable_CreateEntry(&ctxt->context, name, NULL); | |
444 | Var *v = VarNew(he->key /* aliased */, NULL, val, | 444 | Var *v = VarNew(he->key /* aliased */, NULL, val, | |
445 | flags & VAR_SET_READONLY ? VAR_READONLY : 0); | 445 | flags & VAR_SET_READONLY ? VAR_READONLY : 0); | |
446 | HashEntry_Set(he, v); | 446 | HashEntry_Set(he, v); | |
447 | if (!(ctxt->flags & INTERNAL)) { | 447 | if (!(ctxt->flags & INTERNAL)) { | |
448 | VAR_DEBUG3("%s:%s = %s\n", ctxt->name, name, val); | 448 | VAR_DEBUG3("%s:%s = %s\n", ctxt->name, name, val); | |
449 | } | 449 | } | |
450 | } | 450 | } | |
451 | 451 | |||
452 | /* Remove a variable from a context, freeing all related memory as well. | 452 | /* Remove a variable from a context, freeing all related memory as well. | |
453 | * The variable name is expanded once. */ | 453 | * The variable name is expanded once. */ | |
454 | void | 454 | void | |
455 | Var_Delete(const char *name, GNode *ctxt) | 455 | Var_Delete(const char *name, GNode *ctxt) | |
456 | { | 456 | { | |
457 | char *name_freeIt = NULL; | 457 | char *name_freeIt = NULL; | |
458 | HashEntry *he; | 458 | HashEntry *he; | |
459 | 459 | |||
460 | if (strchr(name, '$') != NULL) { | 460 | if (strchr(name, '$') != NULL) { | |
461 | (void)Var_Subst(name, VAR_GLOBAL, VARE_WANTRES, &name_freeIt); | 461 | (void)Var_Subst(name, VAR_GLOBAL, VARE_WANTRES, &name_freeIt); | |
462 | /* TODO: handle errors */ | 462 | /* TODO: handle errors */ | |
463 | name = name_freeIt; | 463 | name = name_freeIt; | |
464 | } | 464 | } | |
465 | he = HashTable_FindEntry(&ctxt->context, name); | 465 | he = HashTable_FindEntry(&ctxt->context, name); | |
466 | VAR_DEBUG3("%s:delete %s%s\n", | 466 | VAR_DEBUG3("%s:delete %s%s\n", | |
467 | ctxt->name, name, he != NULL ? "" : " (not found)"); | 467 | ctxt->name, name, he != NULL ? "" : " (not found)"); | |
468 | free(name_freeIt); | 468 | free(name_freeIt); | |
469 | 469 | |||
470 | if (he != NULL) { | 470 | if (he != NULL) { | |
471 | Var *v = HashEntry_Get(he); | 471 | Var *v = HashEntry_Get(he); | |
472 | if (v->flags & VAR_EXPORTED) | 472 | if (v->flags & VAR_EXPORTED) | |
473 | unsetenv(v->name); | 473 | unsetenv(v->name); | |
474 | if (strcmp(v->name, MAKE_EXPORTED) == 0) | 474 | if (strcmp(v->name, MAKE_EXPORTED) == 0) | |
475 | var_exportedVars = VAR_EXPORTED_NONE; | 475 | var_exportedVars = VAR_EXPORTED_NONE; | |
476 | assert(v->name_freeIt == NULL); | 476 | assert(v->name_freeIt == NULL); | |
477 | HashTable_DeleteEntry(&ctxt->context, he); | 477 | HashTable_DeleteEntry(&ctxt->context, he); | |
478 | Buf_Destroy(&v->val, TRUE); | 478 | Buf_Destroy(&v->val, TRUE); | |
479 | free(v); | 479 | free(v); | |
480 | } | 480 | } | |
481 | } | 481 | } | |
482 | 482 | |||
483 | static Boolean | 483 | static Boolean | |
484 | MayExport(const char *name) | 484 | MayExport(const char *name) | |
485 | { | 485 | { | |
486 | if (name[0] == '.') | 486 | if (name[0] == '.') | |
487 | return FALSE; /* skip internals */ | 487 | return FALSE; /* skip internals */ | |
488 | if (name[0] == '-') | 488 | if (name[0] == '-') | |
489 | return FALSE; /* skip misnamed variables */ | 489 | return FALSE; /* skip misnamed variables */ | |
490 | if (name[1] == '\0') { | 490 | if (name[1] == '\0') { | |
491 | /* | 491 | /* | |
492 | * A single char. | 492 | * A single char. | |
493 | * If it is one of the vars that should only appear in | 493 | * If it is one of the vars that should only appear in | |
494 | * local context, skip it, else we can get Var_Subst | 494 | * local context, skip it, else we can get Var_Subst | |
495 | * into a loop. | 495 | * into a loop. | |
496 | */ | 496 | */ | |
497 | switch (name[0]) { | 497 | switch (name[0]) { | |
498 | case '@': | 498 | case '@': | |
499 | case '%': | 499 | case '%': | |
500 | case '*': | 500 | case '*': | |
501 | case '!': | 501 | case '!': | |
502 | return FALSE; | 502 | return FALSE; | |
503 | } | 503 | } | |
504 | } | 504 | } | |
505 | return TRUE; | 505 | return TRUE; | |
506 | } | 506 | } | |
507 | 507 | |||
508 | /* | 508 | /* | |
509 | * Export a single variable. | 509 | * Export a single variable. | |
510 | * We ignore make internal variables (those which start with '.'). | 510 | * We ignore make internal variables (those which start with '.'). | |
511 | * Also we jump through some hoops to avoid calling setenv | 511 | * Also we jump through some hoops to avoid calling setenv | |
512 | * more than necessary since it can leak. | 512 | * more than necessary since it can leak. | |
513 | * We only manipulate flags of vars if 'parent' is set. | 513 | * We only manipulate flags of vars if 'parent' is set. | |
514 | */ | 514 | */ | |
515 | static Boolean | 515 | static Boolean | |
516 | Var_Export1(const char *name, VarExportFlags flags) | 516 | Var_Export1(const char *name, VarExportFlags flags) | |
517 | { | 517 | { | |
518 | VarExportFlags parent = flags & VAR_EXPORT_PARENT; | 518 | VarExportFlags parent = flags & VAR_EXPORT_PARENT; | |
519 | Var *v; | 519 | Var *v; | |
520 | char *val; | 520 | char *val; | |
521 | 521 | |||
522 | if (!MayExport(name)) | 522 | if (!MayExport(name)) | |
523 | return FALSE; | 523 | return FALSE; | |
524 | 524 | |||
525 | v = VarFind(name, VAR_GLOBAL, 0); | 525 | v = VarFind(name, VAR_GLOBAL, 0); | |
526 | if (v == NULL) | 526 | if (v == NULL) | |
527 | return FALSE; | 527 | return FALSE; | |
528 | 528 | |||
529 | if (!parent && (v->flags & VAR_EXPORTED) && !(v->flags & VAR_REEXPORT)) | 529 | if (!parent && (v->flags & VAR_EXPORTED) && !(v->flags & VAR_REEXPORT)) | |
530 | return FALSE; /* nothing to do */ | 530 | return FALSE; /* nothing to do */ | |
531 | 531 | |||
532 | val = Buf_GetAll(&v->val, NULL); | 532 | val = Buf_GetAll(&v->val, NULL); | |
533 | if (!(flags & VAR_EXPORT_LITERAL) && strchr(val, '$') != NULL) { | 533 | if (!(flags & VAR_EXPORT_LITERAL) && strchr(val, '$') != NULL) { | |
534 | char *expr; | 534 | char *expr; | |
535 | 535 | |||
536 | if (parent) { | 536 | if (parent) { | |
537 | /* | 537 | /* | |
538 | * Flag the variable as something we need to re-export. | 538 | * Flag the variable as something we need to re-export. | |
539 | * No point actually exporting it now though, | 539 | * No point actually exporting it now though, | |
540 | * the child process can do it at the last minute. | 540 | * the child process can do it at the last minute. | |
541 | */ | 541 | */ | |
542 | v->flags |= VAR_EXPORTED | VAR_REEXPORT; | 542 | v->flags |= VAR_EXPORTED | VAR_REEXPORT; | |
543 | return TRUE; | 543 | return TRUE; | |
544 | } | 544 | } | |
545 | if (v->flags & VAR_IN_USE) { | 545 | if (v->flags & VAR_IN_USE) { | |
546 | /* | 546 | /* | |
547 | * We recursed while exporting in a child. | 547 | * We recursed while exporting in a child. | |
548 | * This isn't going to end well, just skip it. | 548 | * This isn't going to end well, just skip it. | |
549 | */ | 549 | */ | |
550 | return FALSE; | 550 | return FALSE; | |
551 | } | 551 | } | |
552 | 552 | |||
553 | /* XXX: name is injected without escaping it */ | 553 | /* XXX: name is injected without escaping it */ | |
554 | expr = str_concat3("${", name, "}"); | 554 | expr = str_concat3("${", name, "}"); | |
555 | (void)Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES, &val); | 555 | (void)Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES, &val); | |
556 | /* TODO: handle errors */ | 556 | /* TODO: handle errors */ | |
557 | setenv(name, val, 1); | 557 | setenv(name, val, 1); | |
558 | free(val); | 558 | free(val); | |
559 | free(expr); | 559 | free(expr); | |
560 | } else { | 560 | } else { | |
561 | if (parent) | 561 | if (parent) | |
562 | v->flags &= ~(unsigned)VAR_REEXPORT; /* once will do */ | 562 | v->flags &= ~(unsigned)VAR_REEXPORT; /* once will do */ | |
563 | if (parent || !(v->flags & VAR_EXPORTED)) | 563 | if (parent || !(v->flags & VAR_EXPORTED)) | |
564 | setenv(name, val, 1); | 564 | setenv(name, val, 1); | |
565 | } | 565 | } | |
566 | 566 | |||
567 | /* | 567 | /* | |
568 | * This is so Var_Set knows to call Var_Export again... | 568 | * This is so Var_Set knows to call Var_Export again... | |
569 | */ | 569 | */ | |
570 | if (parent) { | 570 | if (parent) { | |
571 | v->flags |= VAR_EXPORTED; | 571 | v->flags |= VAR_EXPORTED; | |
572 | } | 572 | } | |
573 | return TRUE; | 573 | return TRUE; | |
574 | } | 574 | } | |
575 | 575 | |||
576 | /* | 576 | /* | |
577 | * This gets called from our child processes. | 577 | * This gets called from our child processes. | |
578 | */ | 578 | */ | |
579 | void | 579 | void | |
580 | Var_ExportVars(void) | 580 | Var_ExportVars(void) | |
581 | { | 581 | { | |
582 | char *val; | 582 | char *val; | |
583 | 583 | |||
584 | /* | 584 | /* | |
585 | * Several make's support this sort of mechanism for tracking | 585 | * Several make's support this sort of mechanism for tracking | |
586 | * recursion - but each uses a different name. | 586 | * recursion - but each uses a different name. | |
587 | * We allow the makefiles to update MAKELEVEL and ensure | 587 | * We allow the makefiles to update MAKELEVEL and ensure | |
588 | * children see a correctly incremented value. | 588 | * children see a correctly incremented value. | |
589 | */ | 589 | */ | |
590 | char tmp[BUFSIZ]; | 590 | char tmp[BUFSIZ]; | |
591 | snprintf(tmp, sizeof(tmp), "%d", makelevel + 1); | 591 | snprintf(tmp, sizeof(tmp), "%d", makelevel + 1); | |
592 | setenv(MAKE_LEVEL_ENV, tmp, 1); | 592 | setenv(MAKE_LEVEL_ENV, tmp, 1); | |
593 | 593 | |||
594 | if (var_exportedVars == VAR_EXPORTED_NONE) | 594 | if (var_exportedVars == VAR_EXPORTED_NONE) | |
595 | return; | 595 | return; | |
596 | 596 | |||
597 | if (var_exportedVars == VAR_EXPORTED_ALL) { | 597 | if (var_exportedVars == VAR_EXPORTED_ALL) { | |
598 | HashIter hi; | 598 | HashIter hi; | |
599 | 599 | |||
600 | /* Ouch! Exporting all variables at once is crazy... */ | 600 | /* Ouch! Exporting all variables at once is crazy... */ | |
601 | HashIter_Init(&hi, &VAR_GLOBAL->context); | 601 | HashIter_Init(&hi, &VAR_GLOBAL->context); | |
602 | while (HashIter_Next(&hi) != NULL) { | 602 | while (HashIter_Next(&hi) != NULL) { | |
603 | Var *var = hi.entry->value; | 603 | Var *var = hi.entry->value; | |
604 | Var_Export1(var->name, 0); | 604 | Var_Export1(var->name, 0); | |
605 | } | 605 | } | |
606 | return; | 606 | return; | |
607 | } | 607 | } | |
608 | 608 | |||
609 | (void)Var_Subst("${" MAKE_EXPORTED ":O:u}", VAR_GLOBAL, VARE_WANTRES, &val); | 609 | (void)Var_Subst("${" MAKE_EXPORTED ":O:u}", VAR_GLOBAL, VARE_WANTRES, &val); | |
610 | /* TODO: handle errors */ | 610 | /* TODO: handle errors */ | |
611 | if (*val) { | 611 | if (*val) { | |
612 | Words words = Str_Words(val, FALSE); | 612 | Words words = Str_Words(val, FALSE); | |
613 | size_t i; | 613 | size_t i; | |
614 | 614 | |||
615 | for (i = 0; i < words.len; i++) | 615 | for (i = 0; i < words.len; i++) | |
616 | Var_Export1(words.words[i], 0); | 616 | Var_Export1(words.words[i], 0); | |
617 | Words_Free(words); | 617 | Words_Free(words); | |
618 | } | 618 | } | |
619 | free(val); | 619 | free(val); | |
620 | } | 620 | } | |
621 | 621 | |||
622 | /* | 622 | /* | |
623 | * This is called when .export is seen or .MAKE.EXPORTED is modified. | 623 | * This is called when .export is seen or .MAKE.EXPORTED is modified. | |
624 | * | 624 | * | |
625 | * It is also called when any exported variable is modified. | 625 | * It is also called when any exported variable is modified. | |
626 | * XXX: Is it really? | 626 | * XXX: Is it really? | |
627 | * | 627 | * | |
628 | * str has the format "[-env|-literal] varname...". | 628 | * str has the format "[-env|-literal] varname...". | |
629 | */ | 629 | */ | |
630 | void | 630 | void | |
631 | Var_Export(const char *str, Boolean isExport) | 631 | Var_Export(const char *str, Boolean isExport) | |
632 | { | 632 | { | |
633 | VarExportFlags flags; | 633 | VarExportFlags flags; | |
634 | char *val; | 634 | char *val; | |
635 | 635 | |||
636 | if (isExport && str[0] == '\0') { | 636 | if (isExport && str[0] == '\0') { | |
637 | var_exportedVars = VAR_EXPORTED_ALL; /* use with caution! */ | 637 | var_exportedVars = VAR_EXPORTED_ALL; /* use with caution! */ | |
638 | return; | 638 | return; | |
639 | } | 639 | } | |
640 | 640 | |||
641 | if (isExport && strncmp(str, "-env", 4) == 0) { | 641 | if (isExport && strncmp(str, "-env", 4) == 0) { | |
642 | str += 4; | 642 | str += 4; | |
643 | flags = 0; | 643 | flags = 0; | |
644 | } else if (isExport && strncmp(str, "-literal", 8) == 0) { | 644 | } else if (isExport && strncmp(str, "-literal", 8) == 0) { | |
645 | str += 8; | 645 | str += 8; | |
646 | flags = VAR_EXPORT_LITERAL; | 646 | flags = VAR_EXPORT_LITERAL; | |
647 | } else { | 647 | } else { | |
648 | flags = VAR_EXPORT_PARENT; | 648 | flags = VAR_EXPORT_PARENT; | |
649 | } | 649 | } | |
650 | 650 | |||
651 | (void)Var_Subst(str, VAR_GLOBAL, VARE_WANTRES, &val); | 651 | (void)Var_Subst(str, VAR_GLOBAL, VARE_WANTRES, &val); | |
652 | /* TODO: handle errors */ | 652 | /* TODO: handle errors */ | |
653 | if (val[0] != '\0') { | 653 | if (val[0] != '\0') { | |
654 | Words words = Str_Words(val, FALSE); | 654 | Words words = Str_Words(val, FALSE); | |
655 | 655 | |||
656 | size_t i; | 656 | size_t i; | |
657 | for (i = 0; i < words.len; i++) { | 657 | for (i = 0; i < words.len; i++) { | |
658 | const char *name = words.words[i]; | 658 | const char *name = words.words[i]; | |
659 | if (Var_Export1(name, flags)) { | 659 | if (Var_Export1(name, flags)) { | |
660 | if (var_exportedVars == VAR_EXPORTED_NONE) | 660 | if (var_exportedVars == VAR_EXPORTED_NONE) | |
661 | var_exportedVars = VAR_EXPORTED_SOME; | 661 | var_exportedVars = VAR_EXPORTED_SOME; | |
662 | if (isExport && (flags & VAR_EXPORT_PARENT)) { | 662 | if (isExport && (flags & VAR_EXPORT_PARENT)) { | |
663 | Var_Append(MAKE_EXPORTED, name, VAR_GLOBAL); | 663 | Var_Append(MAKE_EXPORTED, name, VAR_GLOBAL); | |
664 | } | 664 | } | |
665 | } | 665 | } | |
666 | } | 666 | } | |
667 | Words_Free(words); | 667 | Words_Free(words); | |
668 | } | 668 | } | |
669 | free(val); | 669 | free(val); | |
670 | } | 670 | } | |
671 | 671 | |||
672 | 672 | |||
673 | extern char **environ; | 673 | extern char **environ; | |
674 | 674 | |||
675 | /* | 675 | /* | |
676 | * This is called when .unexport[-env] is seen. | 676 | * This is called when .unexport[-env] is seen. | |
677 | * | 677 | * | |
678 | * str must have the form "unexport[-env] varname...". | 678 | * str must have the form "unexport[-env] varname...". | |
679 | */ | 679 | */ | |
680 | void | 680 | void | |
681 | Var_UnExport(const char *str) | 681 | Var_UnExport(const char *str) | |
682 | { | 682 | { | |
683 | const char *varnames; | 683 | const char *varnames; | |
684 | char *varnames_freeIt; | 684 | char *varnames_freeIt; | |
685 | Boolean unexport_env; | 685 | Boolean unexport_env; | |
686 | 686 | |||
687 | varnames = NULL; | 687 | varnames = NULL; | |
688 | varnames_freeIt = NULL; | 688 | varnames_freeIt = NULL; | |
689 | 689 | |||
690 | str += strlen("unexport"); | 690 | str += strlen("unexport"); | |
691 | unexport_env = strncmp(str, "-env", 4) == 0; | 691 | unexport_env = strncmp(str, "-env", 4) == 0; | |
692 | if (unexport_env) { | 692 | if (unexport_env) { | |
693 | const char *cp; | 693 | const char *cp; | |
694 | char **newenv; | 694 | char **newenv; | |
695 | 695 | |||
696 | cp = getenv(MAKE_LEVEL_ENV); /* we should preserve this */ | 696 | cp = getenv(MAKE_LEVEL_ENV); /* we should preserve this */ | |
697 | if (environ == savedEnv) { | 697 | if (environ == savedEnv) { | |
698 | /* we have been here before! */ | 698 | /* we have been here before! */ | |
699 | newenv = bmake_realloc(environ, 2 * sizeof(char *)); | 699 | newenv = bmake_realloc(environ, 2 * sizeof(char *)); | |
700 | } else { | 700 | } else { | |
701 | if (savedEnv) { | 701 | if (savedEnv) { | |
702 | free(savedEnv); | 702 | free(savedEnv); | |
703 | savedEnv = NULL; | 703 | savedEnv = NULL; | |
704 | } | 704 | } | |
705 | newenv = bmake_malloc(2 * sizeof(char *)); | 705 | newenv = bmake_malloc(2 * sizeof(char *)); | |
706 | } | 706 | } | |
707 | 707 | |||
708 | /* Note: we cannot safely free() the original environ. */ | 708 | /* Note: we cannot safely free() the original environ. */ | |
709 | environ = savedEnv = newenv; | 709 | environ = savedEnv = newenv; | |
710 | newenv[0] = NULL; | 710 | newenv[0] = NULL; | |
711 | newenv[1] = NULL; | 711 | newenv[1] = NULL; | |
712 | if (cp && *cp) | 712 | if (cp && *cp) | |
713 | setenv(MAKE_LEVEL_ENV, cp, 1); | 713 | setenv(MAKE_LEVEL_ENV, cp, 1); | |
714 | } else { | 714 | } else { | |
715 | cpp_skip_whitespace(&str); | 715 | cpp_skip_whitespace(&str); | |
716 | if (str[0] != '\0') | 716 | if (str[0] != '\0') | |
717 | varnames = str; | 717 | varnames = str; | |
718 | } | 718 | } | |
719 | 719 | |||
720 | if (varnames == NULL) { | 720 | if (varnames == NULL) { | |
721 | /* Using .MAKE.EXPORTED */ | 721 | /* Using .MAKE.EXPORTED */ | |
722 | (void)Var_Subst("${" MAKE_EXPORTED ":O:u}", VAR_GLOBAL, VARE_WANTRES, | 722 | (void)Var_Subst("${" MAKE_EXPORTED ":O:u}", VAR_GLOBAL, VARE_WANTRES, | |
723 | &varnames_freeIt); | 723 | &varnames_freeIt); | |
724 | /* TODO: handle errors */ | 724 | /* TODO: handle errors */ | |
725 | varnames = varnames_freeIt; | 725 | varnames = varnames_freeIt; | |
726 | } | 726 | } | |
727 | 727 | |||
728 | { | 728 | { | |
729 | Var *v; | 729 | Var *v; | |
730 | size_t i; | 730 | size_t i; | |
731 | 731 | |||
732 | Words words = Str_Words(varnames, FALSE); | 732 | Words words = Str_Words(varnames, FALSE); | |
733 | for (i = 0; i < words.len; i++) { | 733 | for (i = 0; i < words.len; i++) { | |
734 | const char *varname = words.words[i]; | 734 | const char *varname = words.words[i]; | |
735 | v = VarFind(varname, VAR_GLOBAL, 0); | 735 | v = VarFind(varname, VAR_GLOBAL, 0); | |
736 | if (v == NULL) { | 736 | if (v == NULL) { | |
737 | VAR_DEBUG1("Not unexporting \"%s\" (not found)\n", varname); | 737 | VAR_DEBUG1("Not unexporting \"%s\" (not found)\n", varname); | |
738 | continue; | 738 | continue; | |
739 | } | 739 | } | |
740 | 740 | |||
741 | VAR_DEBUG1("Unexporting \"%s\"\n", varname); | 741 | VAR_DEBUG1("Unexporting \"%s\"\n", varname); | |
742 | if (!unexport_env && (v->flags & VAR_EXPORTED) && | 742 | if (!unexport_env && (v->flags & VAR_EXPORTED) && | |
743 | !(v->flags & VAR_REEXPORT)) | 743 | !(v->flags & VAR_REEXPORT)) | |
744 | unsetenv(v->name); | 744 | unsetenv(v->name); | |
745 | v->flags &= ~(unsigned)(VAR_EXPORTED | VAR_REEXPORT); | 745 | v->flags &= ~(unsigned)(VAR_EXPORTED | VAR_REEXPORT); | |
746 | 746 | |||
747 | /* | 747 | /* | |
748 | * If we are unexporting a list, | 748 | * If we are unexporting a list, | |
749 | * remove each one from .MAKE.EXPORTED. | 749 | * remove each one from .MAKE.EXPORTED. | |
750 | * If we are removing them all, | 750 | * If we are removing them all, | |
751 | * just delete .MAKE.EXPORTED below. | 751 | * just delete .MAKE.EXPORTED below. | |
752 | */ | 752 | */ | |
753 | if (varnames == str) { | 753 | if (varnames == str) { | |
754 | /* XXX: v->name is injected without escaping it */ | 754 | /* XXX: v->name is injected without escaping it */ | |
755 | char *expr = str_concat3("${" MAKE_EXPORTED ":N", v->name, "}"); | 755 | char *expr = str_concat3("${" MAKE_EXPORTED ":N", v->name, "}"); | |
756 | char *cp; | 756 | char *cp; | |
757 | (void)Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES, &cp); | 757 | (void)Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES, &cp); | |
758 | /* TODO: handle errors */ | 758 | /* TODO: handle errors */ | |
759 | Var_Set(MAKE_EXPORTED, cp, VAR_GLOBAL); | 759 | Var_Set(MAKE_EXPORTED, cp, VAR_GLOBAL); | |
760 | free(cp); | 760 | free(cp); | |
761 | free(expr); | 761 | free(expr); | |
762 | } | 762 | } | |
763 | } | 763 | } | |
764 | Words_Free(words); | 764 | Words_Free(words); | |
765 | if (varnames != str) { | 765 | if (varnames != str) { | |
766 | Var_Delete(MAKE_EXPORTED, VAR_GLOBAL); | 766 | Var_Delete(MAKE_EXPORTED, VAR_GLOBAL); | |
767 | free(varnames_freeIt); | 767 | free(varnames_freeIt); | |
768 | } | 768 | } | |
769 | } | 769 | } | |
770 | } | 770 | } | |
771 | 771 | |||
772 | /* See Var_Set for documentation. */ | 772 | /* See Var_Set for documentation. */ | |
773 | void | 773 | void | |
774 | Var_Set_with_flags(const char *name, const char *val, GNode *ctxt, | 774 | Var_Set_with_flags(const char *name, const char *val, GNode *ctxt, | |
775 | VarSet_Flags flags) | 775 | VarSet_Flags flags) | |
776 | { | 776 | { | |
777 | const char *unexpanded_name = name; | 777 | const char *unexpanded_name = name; | |
778 | char *name_freeIt = NULL; | 778 | char *name_freeIt = NULL; | |
779 | Var *v; | 779 | Var *v; | |
780 | 780 | |||
781 | assert(val != NULL); | 781 | assert(val != NULL); | |
782 | 782 | |||
783 | if (strchr(name, '$') != NULL) { | 783 | if (strchr(name, '$') != NULL) { | |
784 | (void)Var_Subst(name, ctxt, VARE_WANTRES, &name_freeIt); | 784 | (void)Var_Subst(name, ctxt, VARE_WANTRES, &name_freeIt); | |
785 | /* TODO: handle errors */ | 785 | /* TODO: handle errors */ | |
786 | name = name_freeIt; | 786 | name = name_freeIt; | |
787 | } | 787 | } | |
788 | 788 | |||
789 | if (name[0] == '\0') { | 789 | if (name[0] == '\0') { | |
790 | VAR_DEBUG2("Var_Set(\"%s\", \"%s\", ...) " | 790 | VAR_DEBUG2("Var_Set(\"%s\", \"%s\", ...) " | |
791 | "name expands to empty string - ignored\n", | 791 | "name expands to empty string - ignored\n", | |
792 | unexpanded_name, val); | 792 | unexpanded_name, val); | |
793 | free(name_freeIt); | 793 | free(name_freeIt); | |
794 | return; | 794 | return; | |
795 | } | 795 | } | |
796 | 796 | |||
797 | if (ctxt == VAR_GLOBAL) { | 797 | if (ctxt == VAR_GLOBAL) { | |
798 | v = VarFind(name, VAR_CMDLINE, 0); | 798 | v = VarFind(name, VAR_CMDLINE, 0); | |
799 | if (v != NULL) { | 799 | if (v != NULL) { | |
800 | if (v->flags & VAR_FROM_CMD) { | 800 | if (v->flags & VAR_FROM_CMD) { | |
801 | VAR_DEBUG3("%s:%s = %s ignored!\n", ctxt->name, name, val); | 801 | VAR_DEBUG3("%s:%s = %s ignored!\n", ctxt->name, name, val); | |
802 | goto out; | 802 | goto out; | |
803 | } | 803 | } | |
804 | VarFreeEnv(v, TRUE); | 804 | VarFreeEnv(v, TRUE); | |
805 | } | 805 | } | |
806 | } | 806 | } | |
807 | 807 | |||
808 | /* | 808 | /* | |
809 | * We only look for a variable in the given context since anything set | 809 | * We only look for a variable in the given context since anything set | |
810 | * here will override anything in a lower context, so there's not much | 810 | * here will override anything in a lower context, so there's not much | |
811 | * point in searching them all just to save a bit of memory... | 811 | * point in searching them all just to save a bit of memory... | |
812 | */ | 812 | */ | |
813 | v = VarFind(name, ctxt, 0); | 813 | v = VarFind(name, ctxt, 0); | |
814 | if (v == NULL) { | 814 | if (v == NULL) { | |
815 | if (ctxt == VAR_CMDLINE && !(flags & VAR_NO_EXPORT)) { | 815 | if (ctxt == VAR_CMDLINE && !(flags & VAR_NO_EXPORT)) { | |
816 | /* | 816 | /* | |
817 | * This var would normally prevent the same name being added | 817 | * This var would normally prevent the same name being added | |
818 | * to VAR_GLOBAL, so delete it from there if needed. | 818 | * to VAR_GLOBAL, so delete it from there if needed. | |
819 | * Otherwise -V name may show the wrong value. | 819 | * Otherwise -V name may show the wrong value. | |
820 | */ | 820 | */ | |
821 | /* XXX: name is expanded for the second time */ | 821 | /* XXX: name is expanded for the second time */ | |
822 | Var_Delete(name, VAR_GLOBAL); | 822 | Var_Delete(name, VAR_GLOBAL); | |
823 | } | 823 | } | |
824 | VarAdd(name, val, ctxt, flags); | 824 | VarAdd(name, val, ctxt, flags); | |
825 | } else { | 825 | } else { | |
826 | if ((v->flags & VAR_READONLY) && !(flags & VAR_SET_READONLY)) { | 826 | if ((v->flags & VAR_READONLY) && !(flags & VAR_SET_READONLY)) { | |
827 | VAR_DEBUG3("%s:%s = %s ignored (read-only)\n", | 827 | VAR_DEBUG3("%s:%s = %s ignored (read-only)\n", | |
828 | ctxt->name, name, val); | 828 | ctxt->name, name, val); | |
829 | goto out; | 829 | goto out; | |
830 | } | 830 | } | |
831 | Buf_Empty(&v->val); | 831 | Buf_Empty(&v->val); | |
832 | Buf_AddStr(&v->val, val); | 832 | Buf_AddStr(&v->val, val); | |
833 | 833 | |||
834 | VAR_DEBUG3("%s:%s = %s\n", ctxt->name, name, val); | 834 | VAR_DEBUG3("%s:%s = %s\n", ctxt->name, name, val); | |
835 | if (v->flags & VAR_EXPORTED) { | 835 | if (v->flags & VAR_EXPORTED) { | |
836 | Var_Export1(name, VAR_EXPORT_PARENT); | 836 | Var_Export1(name, VAR_EXPORT_PARENT); | |
837 | } | 837 | } | |
838 | } | 838 | } | |
839 | /* | 839 | /* | |
840 | * Any variables given on the command line are automatically exported | 840 | * Any variables given on the command line are automatically exported | |
841 | * to the environment (as per POSIX standard) | 841 | * to the environment (as per POSIX standard) | |
842 | * Other than internals. | 842 | * Other than internals. | |
843 | */ | 843 | */ | |
844 | if (ctxt == VAR_CMDLINE && !(flags & VAR_NO_EXPORT) && name[0] != '.') { | 844 | if (ctxt == VAR_CMDLINE && !(flags & VAR_NO_EXPORT) && name[0] != '.') { | |
845 | if (v == NULL) | 845 | if (v == NULL) | |
846 | v = VarFind(name, ctxt, 0); /* we just added it */ | 846 | v = VarFind(name, ctxt, 0); /* we just added it */ | |
847 | v->flags |= VAR_FROM_CMD; | 847 | v->flags |= VAR_FROM_CMD; | |
848 | 848 | |||
849 | /* | 849 | /* | |
850 | * If requested, don't export these in the environment | 850 | * If requested, don't export these in the environment | |
851 | * individually. We still put them in MAKEOVERRIDES so | 851 | * individually. We still put them in MAKEOVERRIDES so | |
852 | * that the command-line settings continue to override | 852 | * that the command-line settings continue to override | |
853 | * Makefile settings. | 853 | * Makefile settings. | |
854 | */ | 854 | */ | |
855 | if (!opts.varNoExportEnv) | 855 | if (!opts.varNoExportEnv) | |
856 | setenv(name, val ? val : "", 1); | 856 | setenv(name, val, 1); | |
857 | 857 | |||
858 | Var_Append(MAKEOVERRIDES, name, VAR_GLOBAL); | 858 | Var_Append(MAKEOVERRIDES, name, VAR_GLOBAL); | |
859 | } | 859 | } | |
860 | if (name[0] == '.' && strcmp(name, MAKE_SAVE_DOLLARS) == 0) | 860 | if (name[0] == '.' && strcmp(name, MAKE_SAVE_DOLLARS) == 0) | |
861 | save_dollars = s2Boolean(val, save_dollars); | 861 | save_dollars = s2Boolean(val, save_dollars); | |
862 | 862 | |||
863 | out: | 863 | out: | |
864 | free(name_freeIt); | 864 | free(name_freeIt); | |
865 | if (v != NULL) | 865 | if (v != NULL) | |
866 | VarFreeEnv(v, TRUE); | 866 | VarFreeEnv(v, TRUE); | |
867 | } | 867 | } | |
868 | 868 | |||
869 | /*- | 869 | /*- | |
870 | *----------------------------------------------------------------------- | 870 | *----------------------------------------------------------------------- | |
871 | * Var_Set -- | 871 | * Var_Set -- | |
872 | * Set the variable name to the value val in the given context. | 872 | * Set the variable name to the value val in the given context. | |
873 | * | 873 | * | |
874 | * If the variable doesn't yet exist, it is created. | 874 | * If the variable doesn't yet exist, it is created. | |
875 | * Otherwise the new value overwrites and replaces the old value. | 875 | * Otherwise the new value overwrites and replaces the old value. | |
876 | * | 876 | * | |
877 | * Input: | 877 | * Input: | |
878 | * name name of the variable to set, is expanded once | 878 | * name name of the variable to set, is expanded once | |
879 | * val value to give to the variable | 879 | * val value to give to the variable | |
880 | * ctxt context in which to set it | 880 | * ctxt context in which to set it | |
881 | * | 881 | * | |
882 | * Notes: | 882 | * Notes: | |
883 | * The variable is searched for only in its context before being | 883 | * The variable is searched for only in its context before being | |
884 | * created in that context. I.e. if the context is VAR_GLOBAL, | 884 | * created in that context. I.e. if the context is VAR_GLOBAL, | |
885 | * only VAR_GLOBAL->context is searched. Likewise if it is VAR_CMDLINE, | 885 | * only VAR_GLOBAL->context is searched. Likewise if it is VAR_CMDLINE, | |
886 | * only VAR_CMDLINE->context is searched. This is done to avoid the | 886 | * only VAR_CMDLINE->context is searched. This is done to avoid the | |
887 | * literally thousands of unnecessary strcmp's that used to be done to | 887 | * literally thousands of unnecessary strcmp's that used to be done to | |
888 | * set, say, $(@) or $(<). | 888 | * set, say, $(@) or $(<). | |
889 | * If the context is VAR_GLOBAL though, we check if the variable | 889 | * If the context is VAR_GLOBAL though, we check if the variable | |
890 | * was set in VAR_CMDLINE from the command line and skip it if so. | 890 | * was set in VAR_CMDLINE from the command line and skip it if so. | |
891 | *----------------------------------------------------------------------- | 891 | *----------------------------------------------------------------------- | |
892 | */ | 892 | */ | |
893 | void | 893 | void | |
894 | Var_Set(const char *name, const char *val, GNode *ctxt) | 894 | Var_Set(const char *name, const char *val, GNode *ctxt) | |
895 | { | 895 | { | |
896 | Var_Set_with_flags(name, val, ctxt, 0); | 896 | Var_Set_with_flags(name, val, ctxt, 0); | |
897 | } | 897 | } | |
898 | 898 | |||
899 | /*- | 899 | /*- | |
900 | *----------------------------------------------------------------------- | 900 | *----------------------------------------------------------------------- | |
901 | * Var_Append -- | 901 | * Var_Append -- | |
902 | * The variable of the given name has the given value appended to it in | 902 | * The variable of the given name has the given value appended to it in | |
903 | * the given context. | 903 | * the given context. | |
904 | * | 904 | * | |
905 | * If the variable doesn't exist, it is created. Otherwise the strings | 905 | * If the variable doesn't exist, it is created. Otherwise the strings | |
906 | * are concatenated, with a space in between. | 906 | * are concatenated, with a space in between. | |
907 | * | 907 | * | |
908 | * Input: | 908 | * Input: | |
909 | * name name of the variable to modify, is expanded once | 909 | * name name of the variable to modify, is expanded once | |
910 | * val string to append to it | 910 | * val string to append to it | |
911 | * ctxt context in which this should occur | 911 | * ctxt context in which this should occur | |
912 | * | 912 | * | |
913 | * Notes: | 913 | * Notes: | |
914 | * Only if the variable is being sought in the global context is the | 914 | * Only if the variable is being sought in the global context is the | |
915 | * environment searched. | 915 | * environment searched. | |
916 | * XXX: Knows its calling circumstances in that if called with ctxt | 916 | * XXX: Knows its calling circumstances in that if called with ctxt | |
917 | * an actual target, it will only search that context since only | 917 | * an actual target, it will only search that context since only | |
918 | * a local variable could be being appended to. This is actually | 918 | * a local variable could be being appended to. This is actually | |
919 | * a big win and must be tolerated. | 919 | * a big win and must be tolerated. | |
920 | *----------------------------------------------------------------------- | 920 | *----------------------------------------------------------------------- | |
921 | */ | 921 | */ | |
922 | void | 922 | void | |
923 | Var_Append(const char *name, const char *val, GNode *ctxt) | 923 | Var_Append(const char *name, const char *val, GNode *ctxt) | |
924 | { | 924 | { | |
925 | char *name_freeIt = NULL; | 925 | char *name_freeIt = NULL; | |
926 | Var *v; | 926 | Var *v; | |
927 | 927 | |||
928 | assert(val != NULL); | 928 | assert(val != NULL); | |
929 | 929 | |||
930 | if (strchr(name, '$') != NULL) { | 930 | if (strchr(name, '$') != NULL) { | |
931 | const char *unexpanded_name = name; | 931 | const char *unexpanded_name = name; | |
932 | (void)Var_Subst(name, ctxt, VARE_WANTRES, &name_freeIt); | 932 | (void)Var_Subst(name, ctxt, VARE_WANTRES, &name_freeIt); | |
933 | /* TODO: handle errors */ | 933 | /* TODO: handle errors */ | |
934 | name = name_freeIt; | 934 | name = name_freeIt; | |
935 | if (name[0] == '\0') { | 935 | if (name[0] == '\0') { | |
936 | VAR_DEBUG2("Var_Append(\"%s\", \"%s\", ...) " | 936 | VAR_DEBUG2("Var_Append(\"%s\", \"%s\", ...) " | |
937 | "name expands to empty string - ignored\n", | 937 | "name expands to empty string - ignored\n", | |
938 | unexpanded_name, val); | 938 | unexpanded_name, val); | |
939 | free(name_freeIt); | 939 | free(name_freeIt); | |
940 | return; | 940 | return; | |
941 | } | 941 | } | |
942 | } | 942 | } | |
943 | 943 | |||
944 | v = VarFind(name, ctxt, ctxt == VAR_GLOBAL); | 944 | v = VarFind(name, ctxt, ctxt == VAR_GLOBAL); | |
945 | 945 | |||
946 | if (v == NULL) { | 946 | if (v == NULL) { | |
947 | /* XXX: name is expanded for the second time */ | 947 | /* XXX: name is expanded for the second time */ | |
948 | Var_Set(name, val, ctxt); | 948 | Var_Set(name, val, ctxt); | |
949 | } else if (v->flags & VAR_READONLY) { | 949 | } else if (v->flags & VAR_READONLY) { | |
950 | VAR_DEBUG1("Ignoring append to %s since it is read-only\n", name); | 950 | VAR_DEBUG1("Ignoring append to %s since it is read-only\n", name); | |
951 | } else if (ctxt == VAR_CMDLINE || !(v->flags & VAR_FROM_CMD)) { | 951 | } else if (ctxt == VAR_CMDLINE || !(v->flags & VAR_FROM_CMD)) { | |
952 | Buf_AddByte(&v->val, ' '); | 952 | Buf_AddByte(&v->val, ' '); | |
953 | Buf_AddStr(&v->val, val); | 953 | Buf_AddStr(&v->val, val); | |
954 | 954 | |||
955 | VAR_DEBUG3("%s:%s = %s\n", | 955 | VAR_DEBUG3("%s:%s = %s\n", | |
956 | ctxt->name, name, Buf_GetAll(&v->val, NULL)); | 956 | ctxt->name, name, Buf_GetAll(&v->val, NULL)); | |
957 | 957 | |||
958 | if (v->flags & VAR_FROM_ENV) { | 958 | if (v->flags & VAR_FROM_ENV) { | |
959 | HashEntry *h; | 959 | HashEntry *h; | |
960 | 960 | |||
961 | /* | 961 | /* | |
962 | * If the original variable came from the environment, we | 962 | * If the original variable came from the environment, we | |
963 | * have to install it in the global context (we could place | 963 | * have to install it in the global context (we could place | |
964 | * it in the environment, but then we should provide a way to | 964 | * it in the environment, but then we should provide a way to | |
965 | * export other variables...) | 965 | * export other variables...) | |
966 | */ | 966 | */ | |
967 | v->flags &= ~(unsigned)VAR_FROM_ENV; | 967 | v->flags &= ~(unsigned)VAR_FROM_ENV; | |
968 | h = HashTable_CreateEntry(&ctxt->context, name, NULL); | 968 | h = HashTable_CreateEntry(&ctxt->context, name, NULL); | |
969 | HashEntry_Set(h, v); | 969 | HashEntry_Set(h, v); | |
970 | } | 970 | } | |
971 | } | 971 | } | |
972 | free(name_freeIt); | 972 | free(name_freeIt); | |
973 | } | 973 | } | |
974 | 974 | |||
975 | /* See if the given variable exists, in the given context or in other | 975 | /* See if the given variable exists, in the given context or in other | |
976 | * fallback contexts. | 976 | * fallback contexts. | |
977 | * | 977 | * | |
978 | * Input: | 978 | * Input: | |
979 | * name Variable to find, is expanded once | 979 | * name Variable to find, is expanded once | |
980 | * ctxt Context in which to start search | 980 | * ctxt Context in which to start search | |
981 | */ | 981 | */ | |
982 | Boolean | 982 | Boolean | |
983 | Var_Exists(const char *name, GNode *ctxt) | 983 | Var_Exists(const char *name, GNode *ctxt) | |
984 | { | 984 | { | |
985 | char *name_freeIt = NULL; | 985 | char *name_freeIt = NULL; | |
986 | Var *v; | 986 | Var *v; | |
987 | 987 | |||
988 | if (strchr(name, '$') != NULL) { | 988 | if (strchr(name, '$') != NULL) { | |
989 | (void)Var_Subst(name, ctxt, VARE_WANTRES, &name_freeIt); | 989 | (void)Var_Subst(name, ctxt, VARE_WANTRES, &name_freeIt); | |
990 | /* TODO: handle errors */ | 990 | /* TODO: handle errors */ | |
991 | name = name_freeIt; | 991 | name = name_freeIt; | |
992 | } | 992 | } | |
993 | 993 | |||
994 | v = VarFind(name, ctxt, TRUE); | 994 | v = VarFind(name, ctxt, TRUE); | |
995 | free(name_freeIt); | 995 | free(name_freeIt); | |
996 | if (v == NULL) | 996 | if (v == NULL) | |
997 | return FALSE; | 997 | return FALSE; | |
998 | 998 | |||
999 | (void)VarFreeEnv(v, TRUE); | 999 | (void)VarFreeEnv(v, TRUE); | |
1000 | return TRUE; | 1000 | return TRUE; | |
1001 | } | 1001 | } | |
1002 | 1002 | |||
1003 | /*- | 1003 | /*- | |
1004 | *----------------------------------------------------------------------- | 1004 | *----------------------------------------------------------------------- | |
1005 | * Var_Value -- | 1005 | * Var_Value -- | |
1006 | * Return the unexpanded value of the given variable in the given | 1006 | * Return the unexpanded value of the given variable in the given | |
1007 | * context, or the usual contexts. | 1007 | * context, or the usual contexts. | |
1008 | * | 1008 | * | |
1009 | * Input: | 1009 | * Input: | |
1010 | * name name to find, is not expanded any further | 1010 | * name name to find, is not expanded any further | |
1011 | * ctxt context in which to search for it | 1011 | * ctxt context in which to search for it | |
1012 | * | 1012 | * | |
1013 | * Results: | 1013 | * Results: | |
1014 | * The value if the variable exists, NULL if it doesn't. | 1014 | * The value if the variable exists, NULL if it doesn't. | |
1015 | * If the returned value is not NULL, the caller must free *freeIt | 1015 | * If the returned value is not NULL, the caller must free *freeIt | |
1016 | * as soon as the returned value is no longer needed. | 1016 | * as soon as the returned value is no longer needed. | |
1017 | *----------------------------------------------------------------------- | 1017 | *----------------------------------------------------------------------- | |
1018 | */ | 1018 | */ | |
1019 | const char * | 1019 | const char * | |
1020 | Var_Value(const char *name, GNode *ctxt, void **freeIt) | 1020 | Var_Value(const char *name, GNode *ctxt, void **freeIt) | |
1021 | { | 1021 | { | |
1022 | Var *v = VarFind(name, ctxt, TRUE); | 1022 | Var *v = VarFind(name, ctxt, TRUE); | |
1023 | char *value; | 1023 | char *value; | |
1024 | 1024 | |||
1025 | *freeIt = NULL; | 1025 | *freeIt = NULL; | |
1026 | if (v == NULL) | 1026 | if (v == NULL) | |
1027 | return NULL; | 1027 | return NULL; | |
1028 | 1028 | |||
1029 | value = Buf_GetAll(&v->val, NULL); | 1029 | value = Buf_GetAll(&v->val, NULL); | |
1030 | if (VarFreeEnv(v, FALSE)) | 1030 | if (VarFreeEnv(v, FALSE)) | |
1031 | *freeIt = value; | 1031 | *freeIt = value; | |
1032 | return value; | 1032 | return value; | |
1033 | } | 1033 | } | |
1034 | 1034 | |||
1035 | 1035 | |||
1036 | /* SepBuf is a string being built from words, interleaved with separators. */ | 1036 | /* SepBuf is a string being built from words, interleaved with separators. */ | |
1037 | typedef struct SepBuf { | 1037 | typedef struct SepBuf { | |
1038 | Buffer buf; | 1038 | Buffer buf; | |
1039 | Boolean needSep; | 1039 | Boolean needSep; | |
1040 | char sep; /* usually ' ', but see the :ts modifier */ | 1040 | char sep; /* usually ' ', but see the :ts modifier */ | |
1041 | } SepBuf; | 1041 | } SepBuf; | |
1042 | 1042 | |||
1043 | static void | 1043 | static void | |
1044 | SepBuf_Init(SepBuf *buf, char sep) | 1044 | SepBuf_Init(SepBuf *buf, char sep) | |
1045 | { | 1045 | { | |
1046 | Buf_Init(&buf->buf, 32 /* bytes */); | 1046 | Buf_Init(&buf->buf, 32 /* bytes */); | |
1047 | buf->needSep = FALSE; | 1047 | buf->needSep = FALSE; | |
1048 | buf->sep = sep; | 1048 | buf->sep = sep; | |
1049 | } | 1049 | } | |
1050 | 1050 | |||
1051 | static void | 1051 | static void | |
1052 | SepBuf_Sep(SepBuf *buf) | 1052 | SepBuf_Sep(SepBuf *buf) | |
1053 | { | 1053 | { | |
1054 | buf->needSep = TRUE; | 1054 | buf->needSep = TRUE; | |
1055 | } | 1055 | } | |
1056 | 1056 | |||
1057 | static void | 1057 | static void | |
1058 | SepBuf_AddBytes(SepBuf *buf, const char *mem, size_t mem_size) | 1058 | SepBuf_AddBytes(SepBuf *buf, const char *mem, size_t mem_size) | |
1059 | { | 1059 | { | |
1060 | if (mem_size == 0) | 1060 | if (mem_size == 0) | |
1061 | return; | 1061 | return; | |
1062 | if (buf->needSep && buf->sep != '\0') { | 1062 | if (buf->needSep && buf->sep != '\0') { | |
1063 | Buf_AddByte(&buf->buf, buf->sep); | 1063 | Buf_AddByte(&buf->buf, buf->sep); | |
1064 | buf->needSep = FALSE; | 1064 | buf->needSep = FALSE; | |
1065 | } | 1065 | } | |
1066 | Buf_AddBytes(&buf->buf, mem, mem_size); | 1066 | Buf_AddBytes(&buf->buf, mem, mem_size); | |
1067 | } | 1067 | } | |
1068 | 1068 | |||
1069 | static void | 1069 | static void | |
1070 | SepBuf_AddBytesBetween(SepBuf *buf, const char *start, const char *end) | 1070 | SepBuf_AddBytesBetween(SepBuf *buf, const char *start, const char *end) | |
1071 | { | 1071 | { | |
1072 | SepBuf_AddBytes(buf, start, (size_t)(end - start)); | 1072 | SepBuf_AddBytes(buf, start, (size_t)(end - start)); | |
1073 | } | 1073 | } | |
1074 | 1074 | |||
1075 | static void | 1075 | static void | |
1076 | SepBuf_AddStr(SepBuf *buf, const char *str) | 1076 | SepBuf_AddStr(SepBuf *buf, const char *str) | |
1077 | { | 1077 | { | |
1078 | SepBuf_AddBytes(buf, str, strlen(str)); | 1078 | SepBuf_AddBytes(buf, str, strlen(str)); | |
1079 | } | 1079 | } | |
1080 | 1080 | |||
1081 | static char * | 1081 | static char * | |
1082 | SepBuf_Destroy(SepBuf *buf, Boolean free_buf) | 1082 | SepBuf_Destroy(SepBuf *buf, Boolean free_buf) | |
1083 | { | 1083 | { | |
1084 | return Buf_Destroy(&buf->buf, free_buf); | 1084 | return Buf_Destroy(&buf->buf, free_buf); | |
1085 | } | 1085 | } | |
1086 | 1086 | |||
1087 | 1087 | |||
1088 | /* This callback for ModifyWords gets a single word from a variable expression | 1088 | /* This callback for ModifyWords gets a single word from a variable expression | |
1089 | * and typically adds a modification of this word to the buffer. It may also | 1089 | * and typically adds a modification of this word to the buffer. It may also | |
1090 | * do nothing or add several words. | 1090 | * do nothing or add several words. | |
1091 | * | 1091 | * | |
1092 | * For example, in ${:Ua b c:M*2}, the callback is called 3 times, once for | 1092 | * For example, in ${:Ua b c:M*2}, the callback is called 3 times, once for | |
1093 | * each word of "a b c". */ | 1093 | * each word of "a b c". */ | |
1094 | typedef void (*ModifyWordsCallback)(const char *word, SepBuf *buf, void *data); | 1094 | typedef void (*ModifyWordsCallback)(const char *word, SepBuf *buf, void *data); | |
1095 | 1095 | |||
1096 | 1096 | |||
1097 | /* Callback for ModifyWords to implement the :H modifier. | 1097 | /* Callback for ModifyWords to implement the :H modifier. | |
1098 | * Add the dirname of the given word to the buffer. */ | 1098 | * Add the dirname of the given word to the buffer. */ | |
1099 | static void | 1099 | static void | |
1100 | ModifyWord_Head(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) | 1100 | ModifyWord_Head(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) | |
1101 | { | 1101 | { | |
1102 | const char *slash = strrchr(word, '/'); | 1102 | const char *slash = strrchr(word, '/'); | |
1103 | if (slash != NULL) | 1103 | if (slash != NULL) | |
1104 | SepBuf_AddBytesBetween(buf, word, slash); | 1104 | SepBuf_AddBytesBetween(buf, word, slash); | |
1105 | else | 1105 | else | |
1106 | SepBuf_AddStr(buf, "."); | 1106 | SepBuf_AddStr(buf, "."); | |
1107 | } | 1107 | } | |
1108 | 1108 | |||
1109 | /* Callback for ModifyWords to implement the :T modifier. | 1109 | /* Callback for ModifyWords to implement the :T modifier. | |
1110 | * Add the basename of the given word to the buffer. */ | 1110 | * Add the basename of the given word to the buffer. */ | |
1111 | static void | 1111 | static void | |
1112 | ModifyWord_Tail(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) | 1112 | ModifyWord_Tail(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) | |
1113 | { | 1113 | { | |
1114 | const char *slash = strrchr(word, '/'); | 1114 | const char *slash = strrchr(word, '/'); | |
1115 | const char *base = slash != NULL ? slash + 1 : word; | 1115 | const char *base = slash != NULL ? slash + 1 : word; | |
1116 | SepBuf_AddStr(buf, base); | 1116 | SepBuf_AddStr(buf, base); | |
1117 | } | 1117 | } | |
1118 | 1118 | |||
1119 | /* Callback for ModifyWords to implement the :E modifier. | 1119 | /* Callback for ModifyWords to implement the :E modifier. | |
1120 | * Add the filename suffix of the given word to the buffer, if it exists. */ | 1120 | * Add the filename suffix of the given word to the buffer, if it exists. */ | |
1121 | static void | 1121 | static void | |
1122 | ModifyWord_Suffix(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) | 1122 | ModifyWord_Suffix(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) | |
1123 | { | 1123 | { | |
1124 | const char *dot = strrchr(word, '.'); | 1124 | const char *dot = strrchr(word, '.'); | |
1125 | if (dot != NULL) | 1125 | if (dot != NULL) | |
1126 | SepBuf_AddStr(buf, dot + 1); | 1126 | SepBuf_AddStr(buf, dot + 1); | |
1127 | } | 1127 | } | |
1128 | 1128 | |||
1129 | /* Callback for ModifyWords to implement the :R modifier. | 1129 | /* Callback for ModifyWords to implement the :R modifier. | |
1130 | * Add the basename of the given word to the buffer. */ | 1130 | * Add the basename of the given word to the buffer. */ | |
1131 | static void | 1131 | static void | |
1132 | ModifyWord_Root(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) | 1132 | ModifyWord_Root(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) | |
1133 | { | 1133 | { | |
1134 | const char *dot = strrchr(word, '.'); | 1134 | const char *dot = strrchr(word, '.'); | |
1135 | size_t len = dot != NULL ? (size_t)(dot - word) : strlen(word); | 1135 | size_t len = dot != NULL ? (size_t)(dot - word) : strlen(word); | |
1136 | SepBuf_AddBytes(buf, word, len); | 1136 | SepBuf_AddBytes(buf, word, len); | |
1137 | } | 1137 | } | |
1138 | 1138 | |||
1139 | /* Callback for ModifyWords to implement the :M modifier. | 1139 | /* Callback for ModifyWords to implement the :M modifier. | |
1140 | * Place the word in the buffer if it matches the given pattern. */ | 1140 | * Place the word in the buffer if it matches the given pattern. */ | |
1141 | static void | 1141 | static void | |
1142 | ModifyWord_Match(const char *word, SepBuf *buf, void *data) | 1142 | ModifyWord_Match(const char *word, SepBuf *buf, void *data) | |
1143 | { | 1143 | { | |
1144 | const char *pattern = data; | 1144 | const char *pattern = data; | |
1145 | VAR_DEBUG2("VarMatch [%s] [%s]\n", word, pattern); | 1145 | VAR_DEBUG2("VarMatch [%s] [%s]\n", word, pattern); | |
1146 | if (Str_Match(word, pattern)) | 1146 | if (Str_Match(word, pattern)) | |
1147 | SepBuf_AddStr(buf, word); | 1147 | SepBuf_AddStr(buf, word); | |
1148 | } | 1148 | } | |
1149 | 1149 | |||
1150 | /* Callback for ModifyWords to implement the :N modifier. | 1150 | /* Callback for ModifyWords to implement the :N modifier. | |
1151 | * Place the word in the buffer if it doesn't match the given pattern. */ | 1151 | * Place the word in the buffer if it doesn't match the given pattern. */ | |
1152 | static void | 1152 | static void | |
1153 | ModifyWord_NoMatch(const char *word, SepBuf *buf, void *data) | 1153 | ModifyWord_NoMatch(const char *word, SepBuf *buf, void *data) | |
1154 | { | 1154 | { | |
1155 | const char *pattern = data; | 1155 | const char *pattern = data; | |
1156 | if (!Str_Match(word, pattern)) | 1156 | if (!Str_Match(word, pattern)) | |
1157 | SepBuf_AddStr(buf, word); | 1157 | SepBuf_AddStr(buf, word); | |
1158 | } | 1158 | } | |
1159 | 1159 | |||
1160 | #ifdef SYSVVARSUB | 1160 | #ifdef SYSVVARSUB | |
1161 | /* Check word against pattern for a match (% is a wildcard). | 1161 | /* Check word against pattern for a match (% is a wildcard). | |
1162 | * | 1162 | * | |
1163 | * Input: | 1163 | * Input: | |
1164 | * word Word to examine | 1164 | * word Word to examine | |
1165 | * pattern Pattern to examine against | 1165 | * pattern Pattern to examine against | |
1166 | * | 1166 | * | |
1167 | * Results: | 1167 | * Results: | |
1168 | * Returns the start of the match, or NULL. | 1168 | * Returns the start of the match, or NULL. | |
1169 | * out_match_len returns the length of the match, if any. | 1169 | * out_match_len returns the length of the match, if any. | |
1170 | * out_hasPercent returns whether the pattern contains a percent. | 1170 | * out_hasPercent returns whether the pattern contains a percent. | |
1171 | */ | 1171 | */ | |
1172 | static const char * | 1172 | static const char * | |
1173 | SysVMatch(const char *word, const char *pattern, | 1173 | SysVMatch(const char *word, const char *pattern, | |
1174 | size_t *out_match_len, Boolean *out_hasPercent) | 1174 | size_t *out_match_len, Boolean *out_hasPercent) | |
1175 | { | 1175 | { | |
1176 | const char *p = pattern; | 1176 | const char *p = pattern; | |
1177 | const char *w = word; | 1177 | const char *w = word; | |
1178 | const char *percent; | 1178 | const char *percent; | |
1179 | size_t w_len; | 1179 | size_t w_len; | |
1180 | size_t p_len; | 1180 | size_t p_len; | |
1181 | const char *w_tail; | 1181 | const char *w_tail; | |
1182 | 1182 | |||
1183 | *out_hasPercent = FALSE; | 1183 | *out_hasPercent = FALSE; | |
1184 | percent = strchr(p, '%'); | 1184 | percent = strchr(p, '%'); | |
1185 | if (percent != NULL) { /* ${VAR:...%...=...} */ | 1185 | if (percent != NULL) { /* ${VAR:...%...=...} */ | |
1186 | *out_hasPercent = TRUE; | 1186 | *out_hasPercent = TRUE; | |
1187 | if (*w == '\0') | 1187 | if (*w == '\0') | |
1188 | return NULL; /* empty word does not match pattern */ | 1188 | return NULL; /* empty word does not match pattern */ | |
1189 | 1189 | |||
1190 | /* check that the prefix matches */ | 1190 | /* check that the prefix matches */ | |
1191 | for (; p != percent && *w != '\0' && *w == *p; w++, p++) | 1191 | for (; p != percent && *w != '\0' && *w == *p; w++, p++) | |
1192 | continue; | 1192 | continue; | |
1193 | if (p != percent) | 1193 | if (p != percent) | |
1194 | return NULL; /* No match */ | 1194 | return NULL; /* No match */ | |
1195 | 1195 | |||
1196 | p++; /* Skip the percent */ | 1196 | p++; /* Skip the percent */ | |
1197 | if (*p == '\0') { | 1197 | if (*p == '\0') { | |
1198 | /* No more pattern, return the rest of the string */ | 1198 | /* No more pattern, return the rest of the string */ | |
1199 | *out_match_len = strlen(w); | 1199 | *out_match_len = strlen(w); | |
1200 | return w; | 1200 | return w; | |
1201 | } | 1201 | } | |
1202 | } | 1202 | } | |
1203 | 1203 | |||
1204 | /* Test whether the tail matches */ | 1204 | /* Test whether the tail matches */ | |
1205 | w_len = strlen(w); | 1205 | w_len = strlen(w); | |
1206 | p_len = strlen(p); | 1206 | p_len = strlen(p); | |
1207 | if (w_len < p_len) | 1207 | if (w_len < p_len) | |
1208 | return NULL; | 1208 | return NULL; | |
1209 | 1209 | |||
1210 | w_tail = w + w_len - p_len; | 1210 | w_tail = w + w_len - p_len; | |
1211 | if (memcmp(p, w_tail, p_len) != 0) | 1211 | if (memcmp(p, w_tail, p_len) != 0) | |
1212 | return NULL; | 1212 | return NULL; | |
1213 | 1213 | |||
1214 | *out_match_len = (size_t)(w_tail - w); | 1214 | *out_match_len = (size_t)(w_tail - w); | |
1215 | return w; | 1215 | return w; | |
1216 | } | 1216 | } | |
1217 | 1217 | |||
1218 | struct ModifyWord_SYSVSubstArgs { | 1218 | struct ModifyWord_SYSVSubstArgs { | |
1219 | GNode *ctx; | 1219 | GNode *ctx; | |
1220 | const char *lhs; | 1220 | const char *lhs; | |
1221 | const char *rhs; | 1221 | const char *rhs; | |
1222 | }; | 1222 | }; | |
1223 | 1223 | |||
1224 | /* Callback for ModifyWords to implement the :%.from=%.to modifier. */ | 1224 | /* Callback for ModifyWords to implement the :%.from=%.to modifier. */ | |
1225 | static void | 1225 | static void | |
1226 | ModifyWord_SYSVSubst(const char *word, SepBuf *buf, void *data) | 1226 | ModifyWord_SYSVSubst(const char *word, SepBuf *buf, void *data) | |
1227 | { | 1227 | { | |
1228 | const struct ModifyWord_SYSVSubstArgs *args = data; | 1228 | const struct ModifyWord_SYSVSubstArgs *args = data; | |
1229 | char *rhs_expanded; | 1229 | char *rhs_expanded; | |
1230 | const char *rhs; | 1230 | const char *rhs; | |
1231 | const char *percent; | 1231 | const char *percent; | |
1232 | 1232 | |||
1233 | size_t match_len; | 1233 | size_t match_len; | |
1234 | Boolean lhsPercent; | 1234 | Boolean lhsPercent; | |
1235 | const char *match = SysVMatch(word, args->lhs, &match_len, &lhsPercent); | 1235 | const char *match = SysVMatch(word, args->lhs, &match_len, &lhsPercent); | |
1236 | if (match == NULL) { | 1236 | if (match == NULL) { | |
1237 | SepBuf_AddStr(buf, word); | 1237 | SepBuf_AddStr(buf, word); | |
1238 | return; | 1238 | return; | |
1239 | } | 1239 | } | |
1240 | 1240 | |||
1241 | /* Append rhs to the buffer, substituting the first '%' with the | 1241 | /* Append rhs to the buffer, substituting the first '%' with the | |
1242 | * match, but only if the lhs had a '%' as well. */ | 1242 | * match, but only if the lhs had a '%' as well. */ | |
1243 | 1243 | |||
1244 | (void)Var_Subst(args->rhs, args->ctx, VARE_WANTRES, &rhs_expanded); | 1244 | (void)Var_Subst(args->rhs, args->ctx, VARE_WANTRES, &rhs_expanded); | |
1245 | /* TODO: handle errors */ | 1245 | /* TODO: handle errors */ | |
1246 | 1246 | |||
1247 | rhs = rhs_expanded; | 1247 | rhs = rhs_expanded; | |
1248 | percent = strchr(rhs, '%'); | 1248 | percent = strchr(rhs, '%'); | |
1249 | 1249 | |||
1250 | if (percent != NULL && lhsPercent) { | 1250 | if (percent != NULL && lhsPercent) { | |
1251 | /* Copy the prefix of the replacement pattern */ | 1251 | /* Copy the prefix of the replacement pattern */ | |
1252 | SepBuf_AddBytesBetween(buf, rhs, percent); | 1252 | SepBuf_AddBytesBetween(buf, rhs, percent); | |
1253 | rhs = percent + 1; | 1253 | rhs = percent + 1; | |
1254 | } | 1254 | } | |
1255 | if (percent != NULL || !lhsPercent) | 1255 | if (percent != NULL || !lhsPercent) | |
1256 | SepBuf_AddBytes(buf, match, match_len); | 1256 | SepBuf_AddBytes(buf, match, match_len); | |
1257 | 1257 | |||
1258 | /* Append the suffix of the replacement pattern */ | 1258 | /* Append the suffix of the replacement pattern */ | |
1259 | SepBuf_AddStr(buf, rhs); | 1259 | SepBuf_AddStr(buf, rhs); | |
1260 | 1260 | |||
1261 | free(rhs_expanded); | 1261 | free(rhs_expanded); | |
1262 | } | 1262 | } | |
1263 | #endif | 1263 | #endif | |
1264 | 1264 | |||
1265 | 1265 | |||
1266 | struct ModifyWord_SubstArgs { | 1266 | struct ModifyWord_SubstArgs { | |
1267 | const char *lhs; | 1267 | const char *lhs; | |
1268 | size_t lhsLen; | 1268 | size_t lhsLen; | |
1269 | const char *rhs; | 1269 | const char *rhs; | |
1270 | size_t rhsLen; | 1270 | size_t rhsLen; | |
1271 | VarPatternFlags pflags; | 1271 | VarPatternFlags pflags; | |
1272 | Boolean matched; | 1272 | Boolean matched; | |
1273 | }; | 1273 | }; | |
1274 | 1274 | |||
1275 | /* Callback for ModifyWords to implement the :S,from,to, modifier. | 1275 | /* Callback for ModifyWords to implement the :S,from,to, modifier. | |
1276 | * Perform a string substitution on the given word. */ | 1276 | * Perform a string substitution on the given word. */ | |
1277 | static void | 1277 | static void | |
1278 | ModifyWord_Subst(const char *word, SepBuf *buf, void *data) | 1278 | ModifyWord_Subst(const char *word, SepBuf *buf, void *data) | |
1279 | { | 1279 | { | |
1280 | size_t wordLen = strlen(word); | 1280 | size_t wordLen = strlen(word); | |
1281 | struct ModifyWord_SubstArgs *args = data; | 1281 | struct ModifyWord_SubstArgs *args = data; | |
1282 | const char *match; | 1282 | const char *match; | |
1283 | 1283 | |||
1284 | if ((args->pflags & VARP_SUB_ONE) && args->matched) | 1284 | if ((args->pflags & VARP_SUB_ONE) && args->matched) | |
1285 | goto nosub; | 1285 | goto nosub; | |
1286 | 1286 | |||
1287 | if (args->pflags & VARP_ANCHOR_START) { | 1287 | if (args->pflags & VARP_ANCHOR_START) { | |
1288 | if (wordLen < args->lhsLen || | 1288 | if (wordLen < args->lhsLen || | |
1289 | memcmp(word, args->lhs, args->lhsLen) != 0) | 1289 | memcmp(word, args->lhs, args->lhsLen) != 0) | |
1290 | goto nosub; | 1290 | goto nosub; | |
1291 | 1291 | |||
1292 | if ((args->pflags & VARP_ANCHOR_END) && wordLen != args->lhsLen) | 1292 | if ((args->pflags & VARP_ANCHOR_END) && wordLen != args->lhsLen) | |
1293 | goto nosub; | 1293 | goto nosub; | |
1294 | 1294 | |||
1295 | /* :S,^prefix,replacement, or :S,^whole$,replacement, */ | 1295 | /* :S,^prefix,replacement, or :S,^whole$,replacement, */ | |
1296 | SepBuf_AddBytes(buf, args->rhs, args->rhsLen); | 1296 | SepBuf_AddBytes(buf, args->rhs, args->rhsLen); | |
1297 | SepBuf_AddBytes(buf, word + args->lhsLen, wordLen - args->lhsLen); | 1297 | SepBuf_AddBytes(buf, word + args->lhsLen, wordLen - args->lhsLen); | |
1298 | args->matched = TRUE; | 1298 | args->matched = TRUE; | |
1299 | return; | 1299 | return; | |
1300 | } | 1300 | } | |
1301 | 1301 | |||
1302 | if (args->pflags & VARP_ANCHOR_END) { | 1302 | if (args->pflags & VARP_ANCHOR_END) { | |
1303 | const char *start; | 1303 | const char *start; | |
1304 | 1304 | |||
1305 | if (wordLen < args->lhsLen) | 1305 | if (wordLen < args->lhsLen) | |
1306 | goto nosub; | 1306 | goto nosub; | |
1307 | 1307 | |||
1308 | start = word + (wordLen - args->lhsLen); | 1308 | start = word + (wordLen - args->lhsLen); | |
1309 | if (memcmp(start, args->lhs, args->lhsLen) != 0) | 1309 | if (memcmp(start, args->lhs, args->lhsLen) != 0) | |
1310 | goto nosub; | 1310 | goto nosub; | |
1311 | 1311 | |||
1312 | /* :S,suffix$,replacement, */ | 1312 | /* :S,suffix$,replacement, */ | |
1313 | SepBuf_AddBytesBetween(buf, word, start); | 1313 | SepBuf_AddBytesBetween(buf, word, start); | |
1314 | SepBuf_AddBytes(buf, args->rhs, args->rhsLen); | 1314 | SepBuf_AddBytes(buf, args->rhs, args->rhsLen); | |
1315 | args->matched = TRUE; | 1315 | args->matched = TRUE; | |
1316 | return; | 1316 | return; | |
1317 | } | 1317 | } | |
1318 | 1318 | |||
1319 | if (args->lhs[0] == '\0') | 1319 | if (args->lhs[0] == '\0') | |
1320 | goto nosub; | 1320 | goto nosub; | |
1321 | 1321 | |||
1322 | /* unanchored case, may match more than once */ | 1322 | /* unanchored case, may match more than once */ | |
1323 | while ((match = strstr(word, args->lhs)) != NULL) { | 1323 | while ((match = strstr(word, args->lhs)) != NULL) { | |
1324 | SepBuf_AddBytesBetween(buf, word, match); | 1324 | SepBuf_AddBytesBetween(buf, word, match); | |
1325 | SepBuf_AddBytes(buf, args->rhs, args->rhsLen); | 1325 | SepBuf_AddBytes(buf, args->rhs, args->rhsLen); | |
1326 | args->matched = TRUE; | 1326 | args->matched = TRUE; | |
1327 | wordLen -= (size_t)(match - word) + args->lhsLen; | 1327 | wordLen -= (size_t)(match - word) + args->lhsLen; | |
1328 | word += (size_t)(match - word) + args->lhsLen; | 1328 | word += (size_t)(match - word) + args->lhsLen; | |
1329 | if (wordLen == 0 || !(args->pflags & VARP_SUB_GLOBAL)) | 1329 | if (wordLen == 0 || !(args->pflags & VARP_SUB_GLOBAL)) | |
1330 | break; | 1330 | break; | |
1331 | } | 1331 | } | |
1332 | nosub: | 1332 | nosub: | |
1333 | SepBuf_AddBytes(buf, word, wordLen); | 1333 | SepBuf_AddBytes(buf, word, wordLen); | |
1334 | } | 1334 | } | |
1335 | 1335 | |||
1336 | #ifndef NO_REGEX | 1336 | #ifndef NO_REGEX | |
1337 | /* Print the error caused by a regcomp or regexec call. */ | 1337 | /* Print the error caused by a regcomp or regexec call. */ | |
1338 | static void | 1338 | static void | |
1339 | VarREError(int reerr, regex_t *pat, const char *str) | 1339 | VarREError(int reerr, regex_t *pat, const char *str) | |
1340 | { | 1340 | { | |
1341 | size_t errlen = regerror(reerr, pat, 0, 0); | 1341 | size_t errlen = regerror(reerr, pat, 0, 0); | |
1342 | char *errbuf = bmake_malloc(errlen); | 1342 | char *errbuf = bmake_malloc(errlen); | |
1343 | regerror(reerr, pat, errbuf, errlen); | 1343 | regerror(reerr, pat, errbuf, errlen); | |
1344 | Error("%s: %s", str, errbuf); | 1344 | Error("%s: %s", str, errbuf); | |
1345 | free(errbuf); | 1345 | free(errbuf); | |
1346 | } | 1346 | } | |
1347 | 1347 | |||
1348 | struct ModifyWord_SubstRegexArgs { | 1348 | struct ModifyWord_SubstRegexArgs { | |
1349 | regex_t re; | 1349 | regex_t re; | |
1350 | size_t nsub; | 1350 | size_t nsub; | |
1351 | char *replace; | 1351 | char *replace; | |
1352 | VarPatternFlags pflags; | 1352 | VarPatternFlags pflags; | |
1353 | Boolean matched; | 1353 | Boolean matched; | |
1354 | }; | 1354 | }; | |
1355 | 1355 | |||
1356 | /* Callback for ModifyWords to implement the :C/from/to/ modifier. | 1356 | /* Callback for ModifyWords to implement the :C/from/to/ modifier. | |
1357 | * Perform a regex substitution on the given word. */ | 1357 | * Perform a regex substitution on the given word. */ | |
1358 | static void | 1358 | static void | |
1359 | ModifyWord_SubstRegex(const char *word, SepBuf *buf, void *data) | 1359 | ModifyWord_SubstRegex(const char *word, SepBuf *buf, void *data) | |
1360 | { | 1360 | { | |
1361 | struct ModifyWord_SubstRegexArgs *args = data; | 1361 | struct ModifyWord_SubstRegexArgs *args = data; | |
1362 | int xrv; | 1362 | int xrv; | |
1363 | const char *wp = word; | 1363 | const char *wp = word; | |
1364 | char *rp; | 1364 | char *rp; | |
1365 | int flags = 0; | 1365 | int flags = 0; | |
1366 | regmatch_t m[10]; | 1366 | regmatch_t m[10]; | |
1367 | 1367 | |||
1368 | if ((args->pflags & VARP_SUB_ONE) && args->matched) | 1368 | if ((args->pflags & VARP_SUB_ONE) && args->matched) | |
1369 | goto nosub; | 1369 | goto nosub; | |
1370 | 1370 | |||
1371 | tryagain: | 1371 | tryagain: | |
1372 | xrv = regexec(&args->re, wp, args->nsub, m, flags); | 1372 | xrv = regexec(&args->re, wp, args->nsub, m, flags); | |
1373 | 1373 | |||
1374 | switch (xrv) { | 1374 | switch (xrv) { | |
1375 | case 0: | 1375 | case 0: | |
1376 | args->matched = TRUE; | 1376 | args->matched = TRUE; | |
1377 | SepBuf_AddBytes(buf, wp, (size_t)m[0].rm_so); | 1377 | SepBuf_AddBytes(buf, wp, (size_t)m[0].rm_so); | |
1378 | 1378 | |||
1379 | for (rp = args->replace; *rp; rp++) { | 1379 | for (rp = args->replace; *rp; rp++) { | |
1380 | if (*rp == '\\' && (rp[1] == '&' || rp[1] == '\\')) { | 1380 | if (*rp == '\\' && (rp[1] == '&' || rp[1] == '\\')) { | |
1381 | SepBuf_AddBytes(buf, rp + 1, 1); | 1381 | SepBuf_AddBytes(buf, rp + 1, 1); | |
1382 | rp++; | 1382 | rp++; | |
1383 | continue; | 1383 | continue; | |
1384 | } | 1384 | } | |
1385 | 1385 | |||
1386 | if (*rp == '&') { | 1386 | if (*rp == '&') { | |
1387 | SepBuf_AddBytesBetween(buf, wp + m[0].rm_so, wp + m[0].rm_eo); | 1387 | SepBuf_AddBytesBetween(buf, wp + m[0].rm_so, wp + m[0].rm_eo); | |
1388 | continue; | 1388 | continue; | |
1389 | } | 1389 | } | |
1390 | 1390 | |||
1391 | if (*rp != '\\' || !ch_isdigit(rp[1])) { | 1391 | if (*rp != '\\' || !ch_isdigit(rp[1])) { | |
1392 | SepBuf_AddBytes(buf, rp, 1); | 1392 | SepBuf_AddBytes(buf, rp, 1); | |
1393 | continue; | 1393 | continue; | |
1394 | } | 1394 | } | |
1395 | 1395 | |||
1396 | { /* \0 to \9 backreference */ | 1396 | { /* \0 to \9 backreference */ | |
1397 | size_t n = (size_t)(rp[1] - '0'); | 1397 | size_t n = (size_t)(rp[1] - '0'); | |
1398 | rp++; | 1398 | rp++; | |
1399 | 1399 | |||
1400 | if (n >= args->nsub) { | 1400 | if (n >= args->nsub) { | |
1401 | Error("No subexpression \\%zu", n); | 1401 | Error("No subexpression \\%zu", n); | |
1402 | } else if (m[n].rm_so == -1 && m[n].rm_eo == -1) { | 1402 | } else if (m[n].rm_so == -1 && m[n].rm_eo == -1) { | |
1403 | Error("No match for subexpression \\%zu", n); | 1403 | Error("No match for subexpression \\%zu", n); | |
1404 | } else { | 1404 | } else { | |
1405 | SepBuf_AddBytesBetween(buf, wp + m[n].rm_so, | 1405 | SepBuf_AddBytesBetween(buf, wp + m[n].rm_so, | |
1406 | wp + m[n].rm_eo); | 1406 | wp + m[n].rm_eo); | |
1407 | } | 1407 | } | |
1408 | } | 1408 | } | |
1409 | } | 1409 | } | |
1410 | 1410 | |||
1411 | wp += m[0].rm_eo; | 1411 | wp += m[0].rm_eo; | |
1412 | if (args->pflags & VARP_SUB_GLOBAL) { | 1412 | if (args->pflags & VARP_SUB_GLOBAL) { | |
1413 | flags |= REG_NOTBOL; | 1413 | flags |= REG_NOTBOL; | |
1414 | if (m[0].rm_so == 0 && m[0].rm_eo == 0) { | 1414 | if (m[0].rm_so == 0 && m[0].rm_eo == 0) { | |
1415 | SepBuf_AddBytes(buf, wp, 1); | 1415 | SepBuf_AddBytes(buf, wp, 1); | |
1416 | wp++; | 1416 | wp++; | |
1417 | } | 1417 | } | |
1418 | if (*wp) | 1418 | if (*wp) | |
1419 | goto tryagain; | 1419 | goto tryagain; | |
1420 | } | 1420 | } | |
1421 | if (*wp) { | 1421 | if (*wp) { | |
1422 | SepBuf_AddStr(buf, wp); | 1422 | SepBuf_AddStr(buf, wp); | |
1423 | } | 1423 | } | |
1424 | break; | 1424 | break; | |
1425 | default: | 1425 | default: | |
1426 | VarREError(xrv, &args->re, "Unexpected regex error"); | 1426 | VarREError(xrv, &args->re, "Unexpected regex error"); | |
1427 | /* FALLTHROUGH */ | 1427 | /* FALLTHROUGH */ | |
1428 | case REG_NOMATCH: | 1428 | case REG_NOMATCH: | |
1429 | nosub: | 1429 | nosub: | |
1430 | SepBuf_AddStr(buf, wp); | 1430 | SepBuf_AddStr(buf, wp); | |
1431 | break; | 1431 | break; | |
1432 | } | 1432 | } | |
1433 | } | 1433 | } | |
1434 | #endif | 1434 | #endif | |
1435 | 1435 | |||
1436 | 1436 | |||
1437 | struct ModifyWord_LoopArgs { | 1437 | struct ModifyWord_LoopArgs { | |
1438 | GNode *ctx; | 1438 | GNode *ctx; | |
1439 | char *tvar; /* name of temporary variable */ | 1439 | char *tvar; /* name of temporary variable */ | |
1440 | char *str; /* string to expand */ | 1440 | char *str; /* string to expand */ | |
1441 | VarEvalFlags eflags; | 1441 | VarEvalFlags eflags; | |
1442 | }; | 1442 | }; | |
1443 | 1443 | |||
1444 | /* Callback for ModifyWords to implement the :@var@...@ modifier of ODE make. */ | 1444 | /* Callback for ModifyWords to implement the :@var@...@ modifier of ODE make. */ | |
1445 | static void | 1445 | static void | |
1446 | ModifyWord_Loop(const char *word, SepBuf *buf, void *data) | 1446 | ModifyWord_Loop(const char *word, SepBuf *buf, void *data) | |
1447 | { | 1447 | { | |
1448 | const struct ModifyWord_LoopArgs *args; | 1448 | const struct ModifyWord_LoopArgs *args; | |
1449 | char *s; | 1449 | char *s; | |
1450 | 1450 | |||
1451 | if (word[0] == '\0') | 1451 | if (word[0] == '\0') | |
1452 | return; | 1452 | return; | |
1453 | 1453 | |||
1454 | args = data; | 1454 | args = data; | |
1455 | Var_Set_with_flags(args->tvar, word, args->ctx, VAR_NO_EXPORT); | 1455 | Var_Set_with_flags(args->tvar, word, args->ctx, VAR_NO_EXPORT); | |
1456 | (void)Var_Subst(args->str, args->ctx, args->eflags, &s); | 1456 | (void)Var_Subst(args->str, args->ctx, args->eflags, &s); | |
1457 | /* TODO: handle errors */ | 1457 | /* TODO: handle errors */ | |
1458 | 1458 | |||
1459 | VAR_DEBUG4("ModifyWord_Loop: in \"%s\", replace \"%s\" with \"%s\" " | 1459 | VAR_DEBUG4("ModifyWord_Loop: in \"%s\", replace \"%s\" with \"%s\" " | |
1460 | "to \"%s\"\n", | 1460 | "to \"%s\"\n", | |
1461 | word, args->tvar, args->str, s); | 1461 | word, args->tvar, args->str, s); | |
1462 | 1462 | |||
1463 | if (s[0] == '\n' || Buf_EndsWith(&buf->buf, '\n')) | 1463 | if (s[0] == '\n' || Buf_EndsWith(&buf->buf, '\n')) | |
1464 | buf->needSep = FALSE; | 1464 | buf->needSep = FALSE; | |
1465 | SepBuf_AddStr(buf, s); | 1465 | SepBuf_AddStr(buf, s); | |
1466 | free(s); | 1466 | free(s); | |
1467 | } | 1467 | } | |
1468 | 1468 | |||
1469 | 1469 | |||
1470 | /*- | 1470 | /*- | |
1471 | * Implements the :[first..last] modifier. | 1471 | * Implements the :[first..last] modifier. | |
1472 | * This is a special case of ModifyWords since we want to be able | 1472 | * This is a special case of ModifyWords since we want to be able | |
1473 | * to scan the list backwards if first > last. | 1473 | * to scan the list backwards if first > last. | |
1474 | */ | 1474 | */ | |
1475 | static char * | 1475 | static char * | |
1476 | VarSelectWords(char sep, Boolean oneBigWord, const char *str, int first, | 1476 | VarSelectWords(char sep, Boolean oneBigWord, const char *str, int first, | |
1477 | int last) | 1477 | int last) | |
1478 | { | 1478 | { | |
1479 | Words words; | 1479 | Words words; | |
1480 | int len, start, end, step; | 1480 | int len, start, end, step; | |
1481 | int i; | 1481 | int i; | |
1482 | 1482 | |||
1483 | SepBuf buf; | 1483 | SepBuf buf; | |
1484 | SepBuf_Init(&buf, sep); | 1484 | SepBuf_Init(&buf, sep); | |
1485 | 1485 | |||
1486 | if (oneBigWord) { | 1486 | if (oneBigWord) { | |
1487 | /* fake what Str_Words() would do if there were only one word */ | 1487 | /* fake what Str_Words() would do if there were only one word */ | |
1488 | words.len = 1; | 1488 | words.len = 1; | |
1489 | words.words = bmake_malloc((words.len + 1) * sizeof(char *)); | 1489 | words.words = bmake_malloc((words.len + 1) * sizeof(char *)); | |
1490 | words.freeIt = bmake_strdup(str); | 1490 | words.freeIt = bmake_strdup(str); | |
1491 | words.words[0] = words.freeIt; | 1491 | words.words[0] = words.freeIt; | |
1492 | words.words[1] = NULL; | 1492 | words.words[1] = NULL; | |
1493 | } else { | 1493 | } else { | |
1494 | words = Str_Words(str, FALSE); | 1494 | words = Str_Words(str, FALSE); | |
1495 | } | 1495 | } | |
1496 | 1496 | |||
1497 | /* | 1497 | /* | |
1498 | * Now sanitize the given range. | 1498 | * Now sanitize the given range. | |
1499 | * If first or last are negative, convert them to the positive equivalents | 1499 | * If first or last are negative, convert them to the positive equivalents | |
1500 | * (-1 gets converted to ac, -2 gets converted to (ac - 1), etc.). | 1500 | * (-1 gets converted to ac, -2 gets converted to (ac - 1), etc.). | |
1501 | */ | 1501 | */ | |
1502 | len = (int)words.len; | 1502 | len = (int)words.len; | |
1503 | if (first < 0) | 1503 | if (first < 0) | |
1504 | first += len + 1; | 1504 | first += len + 1; | |
1505 | if (last < 0) | 1505 | if (last < 0) | |
1506 | last += len + 1; | 1506 | last += len + 1; | |
1507 | 1507 | |||
1508 | /* | 1508 | /* | |
1509 | * We avoid scanning more of the list than we need to. | 1509 | * We avoid scanning more of the list than we need to. | |
1510 | */ | 1510 | */ | |
1511 | if (first > last) { | 1511 | if (first > last) { | |
1512 | start = (first > len ? len : first) - 1; | 1512 | start = (first > len ? len : first) - 1; | |
1513 | end = last < 1 ? 0 : last - 1; | 1513 | end = last < 1 ? 0 : last - 1; | |
1514 | step = -1; | 1514 | step = -1; | |
1515 | } else { | 1515 | } else { | |
1516 | start = first < 1 ? 0 : first - 1; | 1516 | start = first < 1 ? 0 : first - 1; | |
1517 | end = last > len ? len : last; | 1517 | end = last > len ? len : last; | |
1518 | step = 1; | 1518 | step = 1; | |
1519 | } | 1519 | } | |
1520 | 1520 | |||
1521 | for (i = start; (step < 0) == (i >= end); i += step) { | 1521 | for (i = start; (step < 0) == (i >= end); i += step) { | |
1522 | SepBuf_AddStr(&buf, words.words[i]); | 1522 | SepBuf_AddStr(&buf, words.words[i]); | |
1523 | SepBuf_Sep(&buf); | 1523 | SepBuf_Sep(&buf); | |
1524 | } | 1524 | } | |
1525 | 1525 | |||
1526 | Words_Free(words); | 1526 | Words_Free(words); | |
1527 | 1527 | |||
1528 | return SepBuf_Destroy(&buf, FALSE); | 1528 | return SepBuf_Destroy(&buf, FALSE); | |
1529 | } | 1529 | } | |
1530 | 1530 | |||
1531 | 1531 | |||
1532 | /* Callback for ModifyWords to implement the :tA modifier. | 1532 | /* Callback for ModifyWords to implement the :tA modifier. | |
1533 | * Replace each word with the result of realpath() if successful. */ | 1533 | * Replace each word with the result of realpath() if successful. */ | |
1534 | static void | 1534 | static void | |
1535 | ModifyWord_Realpath(const char *word, SepBuf *buf, void *data MAKE_ATTR_UNUSED) | 1535 | ModifyWord_Realpath(const char *word, SepBuf *buf, void *data MAKE_ATTR_UNUSED) | |
1536 | { | 1536 | { | |
1537 | struct stat st; | 1537 | struct stat st; | |
1538 | char rbuf[MAXPATHLEN]; | 1538 | char rbuf[MAXPATHLEN]; | |
1539 | 1539 | |||
1540 | const char *rp = cached_realpath(word, rbuf); | 1540 | const char *rp = cached_realpath(word, rbuf); | |
1541 | if (rp != NULL && *rp == '/' && stat(rp, &st) == 0) | 1541 | if (rp != NULL && *rp == '/' && stat(rp, &st) == 0) | |
1542 | word = rp; | 1542 | word = rp; | |
1543 | 1543 | |||
1544 | SepBuf_AddStr(buf, word); | 1544 | SepBuf_AddStr(buf, word); | |
1545 | } | 1545 | } | |
1546 | 1546 | |||
1547 | /*- | 1547 | /*- | |
1548 | *----------------------------------------------------------------------- | 1548 | *----------------------------------------------------------------------- | |
1549 | * Modify each of the words of the passed string using the given function. | 1549 | * Modify each of the words of the passed string using the given function. | |
1550 | * | 1550 | * | |
1551 | * Input: | 1551 | * Input: | |
1552 | * str String whose words should be modified | 1552 | * str String whose words should be modified | |
1553 | * modifyWord Function that modifies a single word | 1553 | * modifyWord Function that modifies a single word | |
1554 | * modifyWord_args Custom arguments for modifyWord | 1554 | * modifyWord_args Custom arguments for modifyWord | |
1555 | * | 1555 | * | |
1556 | * Results: | 1556 | * Results: | |
1557 | * A string of all the words modified appropriately. | 1557 | * A string of all the words modified appropriately. | |
1558 | *----------------------------------------------------------------------- | 1558 | *----------------------------------------------------------------------- | |
1559 | */ | 1559 | */ | |
1560 | static char * | 1560 | static char * | |
1561 | ModifyWords(GNode *ctx, char sep, Boolean oneBigWord, const char *str, | 1561 | ModifyWords(GNode *ctx, char sep, Boolean oneBigWord, const char *str, | |
1562 | ModifyWordsCallback modifyWord, void *modifyWord_args) | 1562 | ModifyWordsCallback modifyWord, void *modifyWord_args) | |
1563 | { | 1563 | { | |
1564 | SepBuf result; | 1564 | SepBuf result; | |
1565 | Words words; | 1565 | Words words; | |
1566 | size_t i; | 1566 | size_t i; | |
1567 | 1567 | |||
1568 | if (oneBigWord) { | 1568 | if (oneBigWord) { | |
1569 | SepBuf_Init(&result, sep); | 1569 | SepBuf_Init(&result, sep); | |
1570 | modifyWord(str, &result, modifyWord_args); | 1570 | modifyWord(str, &result, modifyWord_args); | |
1571 | return SepBuf_Destroy(&result, FALSE); | 1571 | return SepBuf_Destroy(&result, FALSE); | |
1572 | } | 1572 | } | |
1573 | 1573 | |||
1574 | SepBuf_Init(&result, sep); | 1574 | SepBuf_Init(&result, sep); | |
1575 | 1575 | |||
1576 | words = Str_Words(str, FALSE); | 1576 | words = Str_Words(str, FALSE); | |
1577 | 1577 | |||
1578 | VAR_DEBUG2("ModifyWords: split \"%s\" into %zu words\n", str, words.len); | 1578 | VAR_DEBUG2("ModifyWords: split \"%s\" into %zu words\n", str, words.len); | |
1579 | 1579 | |||
1580 | for (i = 0; i < words.len; i++) { | 1580 | for (i = 0; i < words.len; i++) { | |
1581 | modifyWord(words.words[i], &result, modifyWord_args); | 1581 | modifyWord(words.words[i], &result, modifyWord_args); | |
1582 | if (Buf_Len(&result.buf) > 0) | 1582 | if (Buf_Len(&result.buf) > 0) | |
1583 | SepBuf_Sep(&result); | 1583 | SepBuf_Sep(&result); | |
1584 | } | 1584 | } | |
1585 | 1585 | |||
1586 | Words_Free(words); | 1586 | Words_Free(words); | |
1587 | 1587 | |||
1588 | return SepBuf_Destroy(&result, FALSE); | 1588 | return SepBuf_Destroy(&result, FALSE); | |
1589 | } | 1589 | } | |
1590 | 1590 | |||
1591 | 1591 | |||
1592 | static char * | 1592 | static char * | |
1593 | Words_JoinFree(Words words) | 1593 | Words_JoinFree(Words words) | |
1594 | { | 1594 | { | |
1595 | Buffer buf; | 1595 | Buffer buf; | |
1596 | size_t i; | 1596 | size_t i; | |
1597 | 1597 | |||
1598 | Buf_Init(&buf, 0); | 1598 | Buf_Init(&buf, 0); | |
1599 | 1599 | |||
1600 | for (i = 0; i < words.len; i++) { | 1600 | for (i = 0; i < words.len; i++) { | |
1601 | if (i != 0) | 1601 | if (i != 0) | |
1602 | Buf_AddByte(&buf, ' '); /* XXX: st->sep, for consistency */ | 1602 | Buf_AddByte(&buf, ' '); /* XXX: st->sep, for consistency */ | |
1603 | Buf_AddStr(&buf, words.words[i]); | 1603 | Buf_AddStr(&buf, words.words[i]); | |
1604 | } | 1604 | } | |
1605 | 1605 | |||
1606 | Words_Free(words); | 1606 | Words_Free(words); | |
1607 | 1607 | |||
1608 | return Buf_Destroy(&buf, FALSE); | 1608 | return Buf_Destroy(&buf, FALSE); | |
1609 | } | 1609 | } | |
1610 | 1610 | |||
1611 | /* Remove adjacent duplicate words. */ | 1611 | /* Remove adjacent duplicate words. */ | |
1612 | static char * | 1612 | static char * | |
1613 | VarUniq(const char *str) | 1613 | VarUniq(const char *str) | |
1614 | { | 1614 | { | |
1615 | Words words = Str_Words(str, FALSE); | 1615 | Words words = Str_Words(str, FALSE); | |
1616 | 1616 | |||
1617 | if (words.len > 1) { | 1617 | if (words.len > 1) { | |
1618 | size_t i, j; | 1618 | size_t i, j; | |
1619 | for (j = 0, i = 1; i < words.len; i++) | 1619 | for (j = 0, i = 1; i < words.len; i++) | |
1620 | if (strcmp(words.words[i], words.words[j]) != 0 && (++j != i)) | 1620 | if (strcmp(words.words[i], words.words[j]) != 0 && (++j != i)) | |
1621 | words.words[j] = words.words[i]; | 1621 | words.words[j] = words.words[i]; | |
1622 | words.len = j + 1; | 1622 | words.len = j + 1; | |
1623 | } | 1623 | } | |
1624 | 1624 | |||
1625 | return Words_JoinFree(words); | 1625 | return Words_JoinFree(words); | |
1626 | } | 1626 | } | |
1627 | 1627 | |||
1628 | 1628 | |||
1629 | /* Quote shell meta-characters and space characters in the string. | 1629 | /* Quote shell meta-characters and space characters in the string. | |
1630 | * If quoteDollar is set, also quote and double any '$' characters. */ | 1630 | * If quoteDollar is set, also quote and double any '$' characters. */ | |
1631 | static char * | 1631 | static char * | |
1632 | VarQuote(const char *str, Boolean quoteDollar) | 1632 | VarQuote(const char *str, Boolean quoteDollar) | |
1633 | { | 1633 | { | |
1634 | char *res; | 1634 | char *res; | |
1635 | Buffer buf; | 1635 | Buffer buf; | |
1636 | Buf_Init(&buf, 0); | 1636 | Buf_Init(&buf, 0); | |
1637 | 1637 | |||
1638 | for (; *str != '\0'; str++) { | 1638 | for (; *str != '\0'; str++) { | |
1639 | if (*str == '\n') { | 1639 | if (*str == '\n') { | |
1640 | const char *newline = Shell_GetNewline(); | 1640 | const char *newline = Shell_GetNewline(); | |
1641 | if (newline == NULL) | 1641 | if (newline == NULL) | |
1642 | newline = "\\\n"; | 1642 | newline = "\\\n"; | |
1643 | Buf_AddStr(&buf, newline); | 1643 | Buf_AddStr(&buf, newline); | |
1644 | continue; | 1644 | continue; | |
1645 | } | 1645 | } | |
1646 | if (ch_isspace(*str) || ismeta((unsigned char)*str)) | 1646 | if (ch_isspace(*str) || ismeta((unsigned char)*str)) | |
1647 | Buf_AddByte(&buf, '\\'); | 1647 | Buf_AddByte(&buf, '\\'); | |
1648 | Buf_AddByte(&buf, *str); | 1648 | Buf_AddByte(&buf, *str); | |
1649 | if (quoteDollar && *str == '$') | 1649 | if (quoteDollar && *str == '$') | |
1650 | Buf_AddStr(&buf, "\\$"); | 1650 | Buf_AddStr(&buf, "\\$"); | |
1651 | } | 1651 | } | |
1652 | 1652 | |||
1653 | res = Buf_Destroy(&buf, FALSE); | 1653 | res = Buf_Destroy(&buf, FALSE); | |
1654 | VAR_DEBUG1("QuoteMeta: [%s]\n", res); | 1654 | VAR_DEBUG1("QuoteMeta: [%s]\n", res); | |
1655 | return res; | 1655 | return res; | |
1656 | } | 1656 | } | |
1657 | 1657 | |||
1658 | /* Compute the 32-bit hash of the given string, using the MurmurHash3 | 1658 | /* Compute the 32-bit hash of the given string, using the MurmurHash3 | |
1659 | * algorithm. Output is encoded as 8 hex digits, in Little Endian order. */ | 1659 | * algorithm. Output is encoded as 8 hex digits, in Little Endian order. */ | |
1660 | static char * | 1660 | static char * | |
1661 | VarHash(const char *str) | 1661 | VarHash(const char *str) | |
1662 | { | 1662 | { | |
1663 | static const char hexdigits[16] = "0123456789abcdef"; | 1663 | static const char hexdigits[16] = "0123456789abcdef"; | |
1664 | const unsigned char *ustr = (const unsigned char *)str; | 1664 | const unsigned char *ustr = (const unsigned char *)str; | |
1665 | 1665 | |||
1666 | uint32_t h = 0x971e137bU; | 1666 | uint32_t h = 0x971e137bU; | |
1667 | uint32_t c1 = 0x95543787U; | 1667 | uint32_t c1 = 0x95543787U; | |
1668 | uint32_t c2 = 0x2ad7eb25U; | 1668 | uint32_t c2 = 0x2ad7eb25U; | |
1669 | size_t len2 = strlen(str); | 1669 | size_t len2 = strlen(str); | |
1670 | 1670 | |||
1671 | char *buf; | 1671 | char *buf; | |
1672 | size_t i; | 1672 | size_t i; | |
1673 | 1673 | |||
1674 | size_t len; | 1674 | size_t len; | |
1675 | for (len = len2; len; ) { | 1675 | for (len = len2; len; ) { | |
1676 | uint32_t k = 0; | 1676 | uint32_t k = 0; | |
1677 | switch (len) { | 1677 | switch (len) { | |
1678 | default: | 1678 | default: | |
1679 | k = ((uint32_t)ustr[3] << 24) | | 1679 | k = ((uint32_t)ustr[3] << 24) | | |
1680 | ((uint32_t)ustr[2] << 16) | | 1680 | ((uint32_t)ustr[2] << 16) | | |
1681 | ((uint32_t)ustr[1] << 8) | | 1681 | ((uint32_t)ustr[1] << 8) | | |
1682 | (uint32_t)ustr[0]; | 1682 | (uint32_t)ustr[0]; | |
1683 | len -= 4; | 1683 | len -= 4; | |
1684 | ustr += 4; | 1684 | ustr += 4; | |
1685 | break; | 1685 | break; | |
1686 | case 3: | 1686 | case 3: | |
1687 | k |= (uint32_t)ustr[2] << 16; | 1687 | k |= (uint32_t)ustr[2] << 16; | |
1688 | /* FALLTHROUGH */ | 1688 | /* FALLTHROUGH */ | |
1689 | case 2: | 1689 | case 2: | |
1690 | k |= (uint32_t)ustr[1] << 8; | 1690 | k |= (uint32_t)ustr[1] << 8; | |
1691 | /* FALLTHROUGH */ | 1691 | /* FALLTHROUGH */ | |
1692 | case 1: | 1692 | case 1: | |
1693 | k |= (uint32_t)ustr[0]; | 1693 | k |= (uint32_t)ustr[0]; | |
1694 | len = 0; | 1694 | len = 0; | |
1695 | } | 1695 | } | |
1696 | c1 = c1 * 5 + 0x7b7d159cU; | 1696 | c1 = c1 * 5 + 0x7b7d159cU; | |
1697 | c2 = c2 * 5 + 0x6bce6396U; | 1697 | c2 = c2 * 5 + 0x6bce6396U; | |
1698 | k *= c1; | 1698 | k *= c1; | |
1699 | k = (k << 11) ^ (k >> 21); | 1699 | k = (k << 11) ^ (k >> 21); | |
1700 | k *= c2; | 1700 | k *= c2; | |
1701 | h = (h << 13) ^ (h >> 19); | 1701 | h = (h << 13) ^ (h >> 19); | |
1702 | h = h * 5 + 0x52dce729U; | 1702 | h = h * 5 + 0x52dce729U; | |
1703 | h ^= k; | 1703 | h ^= k; | |
1704 | } | 1704 | } | |
1705 | h ^= (uint32_t)len2; | 1705 | h ^= (uint32_t)len2; | |
1706 | h *= 0x85ebca6b; | 1706 | h *= 0x85ebca6b; | |
1707 | h ^= h >> 13; | 1707 | h ^= h >> 13; | |
1708 | h *= 0xc2b2ae35; | 1708 | h *= 0xc2b2ae35; | |
1709 | h ^= h >> 16; | 1709 | h ^= h >> 16; | |
1710 | 1710 | |||
1711 | buf = bmake_malloc(9); | 1711 | buf = bmake_malloc(9); | |
1712 | for (i = 0; i < 8; i++) { | 1712 | for (i = 0; i < 8; i++) { | |
1713 | buf[i] = hexdigits[h & 0x0f]; | 1713 | buf[i] = hexdigits[h & 0x0f]; | |
1714 | h >>= 4; | 1714 | h >>= 4; | |
1715 | } | 1715 | } | |
1716 | buf[8] = '\0'; | 1716 | buf[8] = '\0'; | |
1717 | return buf; | 1717 | return buf; | |
1718 | } | 1718 | } | |
1719 | 1719 | |||
1720 | static char * | 1720 | static char * | |
1721 | VarStrftime(const char *fmt, Boolean zulu, time_t tim) | 1721 | VarStrftime(const char *fmt, Boolean zulu, time_t tim) | |
1722 | { | 1722 | { | |
1723 | char buf[BUFSIZ]; | 1723 | char buf[BUFSIZ]; | |
1724 | 1724 | |||
1725 | if (!tim) | 1725 | if (!tim) | |
1726 | time(&tim); | 1726 | time(&tim); | |
1727 | if (!*fmt) | 1727 | if (!*fmt) | |
1728 | fmt = "%c"; | 1728 | fmt = "%c"; | |
1729 | strftime(buf, sizeof(buf), fmt, zulu ? gmtime(&tim) : localtime(&tim)); | 1729 | strftime(buf, sizeof(buf), fmt, zulu ? gmtime(&tim) : localtime(&tim)); | |
1730 | 1730 | |||
1731 | buf[sizeof(buf) - 1] = '\0'; | 1731 | buf[sizeof(buf) - 1] = '\0'; | |
1732 | return bmake_strdup(buf); | 1732 | return bmake_strdup(buf); | |
1733 | } | 1733 | } | |
1734 | 1734 | |||
1735 | /* The ApplyModifier functions all work in the same way. They get the | 1735 | /* The ApplyModifier functions all work in the same way. They get the | |
1736 | * current parsing position (pp) and parse the modifier from there. The | 1736 | * current parsing position (pp) and parse the modifier from there. The | |
1737 | * modifier typically lasts until the next ':', or a closing '}' or ')' | 1737 | * modifier typically lasts until the next ':', or a closing '}' or ')' | |
1738 | * (taken from st->endc), or the end of the string (parse error). | 1738 | * (taken from st->endc), or the end of the string (parse error). | |
1739 | * | 1739 | * | |
1740 | * The high-level behavior of these functions is: | 1740 | * The high-level behavior of these functions is: | |
1741 | * | 1741 | * | |
1742 | * 1. parse the modifier | 1742 | * 1. parse the modifier | |
1743 | * 2. evaluate the modifier | 1743 | * 2. evaluate the modifier | |
1744 | * 3. housekeeping | 1744 | * 3. housekeeping | |
1745 | * | 1745 | * | |
1746 | * Parsing the modifier | 1746 | * Parsing the modifier | |
1747 | * | 1747 | * | |
1748 | * If parsing succeeds, the parsing position *pp is updated to point to the | 1748 | * If parsing succeeds, the parsing position *pp is updated to point to the | |
1749 | * first character following the modifier, which typically is either ':' or | 1749 | * first character following the modifier, which typically is either ':' or | |
1750 | * st->endc. | 1750 | * st->endc. | |
1751 | * | 1751 | * | |
1752 | * If parsing fails because of a missing delimiter (as in the :S, :C or :@ | 1752 | * If parsing fails because of a missing delimiter (as in the :S, :C or :@ | |
1753 | * modifiers), return AMR_CLEANUP. | 1753 | * modifiers), return AMR_CLEANUP. | |
1754 | * | 1754 | * | |
1755 | * If parsing fails because the modifier is unknown, return AMR_UNKNOWN to | 1755 | * If parsing fails because the modifier is unknown, return AMR_UNKNOWN to | |
1756 | * try the SysV modifier ${VAR:from=to} as fallback. This should only be | 1756 | * try the SysV modifier ${VAR:from=to} as fallback. This should only be | |
1757 | * done as long as there have been no side effects from evaluating nested | 1757 | * done as long as there have been no side effects from evaluating nested | |
1758 | * variables, to avoid evaluating them more than once. In this case, the | 1758 | * variables, to avoid evaluating them more than once. In this case, the | |
1759 | * parsing position must not be updated. (XXX: Why not? The original parsing | 1759 | * parsing position must not be updated. (XXX: Why not? The original parsing | |
1760 | * position is well-known in ApplyModifiers.) | 1760 | * position is well-known in ApplyModifiers.) | |
1761 | * | 1761 | * | |
1762 | * If parsing fails and the SysV modifier ${VAR:from=to} should not be used | 1762 | * If parsing fails and the SysV modifier ${VAR:from=to} should not be used | |
1763 | * as a fallback, either issue an error message using Error or Parse_Error | 1763 | * as a fallback, either issue an error message using Error or Parse_Error | |
1764 | * and then return AMR_CLEANUP, or return AMR_BAD for the default error | 1764 | * and then return AMR_CLEANUP, or return AMR_BAD for the default error | |
1765 | * message. Both of these return values will stop processing the variable | 1765 | * message. Both of these return values will stop processing the variable | |
1766 | * expression. (XXX: As of 2020-08-23, evaluation of the whole string | 1766 | * expression. (XXX: As of 2020-08-23, evaluation of the whole string | |
1767 | * continues nevertheless after skipping a few bytes, which essentially is | 1767 | * continues nevertheless after skipping a few bytes, which essentially is | |
1768 | * undefined behavior. Not in the sense of C, but still it's impossible to | 1768 | * undefined behavior. Not in the sense of C, but still it's impossible to | |
1769 | * predict what happens in the parser.) | 1769 | * predict what happens in the parser.) | |
1770 | * | 1770 | * | |
1771 | * Evaluating the modifier | 1771 | * Evaluating the modifier | |
1772 | * | 1772 | * | |
1773 | * After parsing, the modifier is evaluated. The side effects from evaluating | 1773 | * After parsing, the modifier is evaluated. The side effects from evaluating | |
1774 | * nested variable expressions in the modifier text often already happen | 1774 | * nested variable expressions in the modifier text often already happen | |
1775 | * during parsing though. | 1775 | * during parsing though. | |
1776 | * | 1776 | * | |
1777 | * Evaluating the modifier usually takes the current value of the variable | 1777 | * Evaluating the modifier usually takes the current value of the variable | |
1778 | * expression from st->val, or the variable name from st->v->name and stores | 1778 | * expression from st->val, or the variable name from st->v->name and stores | |
1779 | * the result in st->newVal. | 1779 | * the result in st->newVal. | |
1780 | * | 1780 | * | |
1781 | * If evaluating fails (as of 2020-08-23), an error message is printed using | 1781 | * If evaluating fails (as of 2020-08-23), an error message is printed using | |
1782 | * Error. This function has no side-effects, it really just prints the error | 1782 | * Error. This function has no side-effects, it really just prints the error | |
1783 | * message. Processing the expression continues as if everything were ok. | 1783 | * message. Processing the expression continues as if everything were ok. | |
1784 | * XXX: This should be fixed by adding proper error handling to Var_Subst, | 1784 | * XXX: This should be fixed by adding proper error handling to Var_Subst, | |
1785 | * Var_Parse, ApplyModifiers and ModifyWords. | 1785 | * Var_Parse, ApplyModifiers and ModifyWords. | |
1786 | * | 1786 | * | |
1787 | * Housekeeping | 1787 | * Housekeeping | |
1788 | * | 1788 | * | |
1789 | * Some modifiers such as :D and :U turn undefined expressions into defined | 1789 | * Some modifiers such as :D and :U turn undefined expressions into defined | |
1790 | * expressions (see VEF_UNDEF, VEF_DEF). | 1790 | * expressions (see VEF_UNDEF, VEF_DEF). | |
1791 | * | 1791 | * | |
1792 | * Some modifiers need to free some memory. | 1792 | * Some modifiers need to free some memory. | |
1793 | */ | 1793 | */ | |
1794 | 1794 | |||
1795 | typedef enum VarExprFlags { | 1795 | typedef enum VarExprFlags { | |
1796 | /* The variable expression is based on an undefined variable. */ | 1796 | /* The variable expression is based on an undefined variable. */ | |
1797 | VEF_UNDEF = 0x01, | 1797 | VEF_UNDEF = 0x01, | |
1798 | /* The variable expression started as an undefined expression, but one | 1798 | /* The variable expression started as an undefined expression, but one | |
1799 | * of the modifiers (such as :D or :U) has turned the expression from | 1799 | * of the modifiers (such as :D or :U) has turned the expression from | |
1800 | * undefined to defined. */ | 1800 | * undefined to defined. */ | |
1801 | VEF_DEF = 0x02 | 1801 | VEF_DEF = 0x02 | |
1802 | } VarExprFlags; | 1802 | } VarExprFlags; | |
1803 | 1803 | |||
1804 | ENUM_FLAGS_RTTI_2(VarExprFlags, | 1804 | ENUM_FLAGS_RTTI_2(VarExprFlags, | |
1805 | VEF_UNDEF, VEF_DEF); | 1805 | VEF_UNDEF, VEF_DEF); | |
1806 | 1806 | |||
1807 | 1807 | |||
1808 | typedef struct ApplyModifiersState { | 1808 | typedef struct ApplyModifiersState { | |
1809 | const char startc; /* '\0' or '{' or '(' */ | 1809 | const char startc; /* '\0' or '{' or '(' */ | |
1810 | const char endc; /* '\0' or '}' or ')' */ | 1810 | const char endc; /* '\0' or '}' or ')' */ | |
1811 | Var * const v; | 1811 | Var * const v; | |
1812 | GNode * const ctxt; | 1812 | GNode * const ctxt; | |
1813 | const VarEvalFlags eflags; | 1813 | const VarEvalFlags eflags; | |
1814 | 1814 | |||
1815 | char *val; /* The old value of the expression, | 1815 | char *val; /* The old value of the expression, | |
1816 | * before applying the modifier, never NULL */ | 1816 | * before applying the modifier, never NULL */ | |
1817 | char *newVal; /* The new value of the expression, | 1817 | char *newVal; /* The new value of the expression, | |
1818 | * after applying the modifier, never NULL */ | 1818 | * after applying the modifier, never NULL */ | |
1819 | char sep; /* Word separator in expansions | 1819 | char sep; /* Word separator in expansions | |
1820 | * (see the :ts modifier) */ | 1820 | * (see the :ts modifier) */ | |
1821 | Boolean oneBigWord; /* TRUE if some modifiers that otherwise split | 1821 | Boolean oneBigWord; /* TRUE if some modifiers that otherwise split | |
1822 | * the variable value into words, like :S and | 1822 | * the variable value into words, like :S and | |
1823 | * :C, treat the variable value as a single big | 1823 | * :C, treat the variable value as a single big | |
1824 | * word, possibly containing spaces. */ | 1824 | * word, possibly containing spaces. */ | |
1825 | VarExprFlags exprFlags; | 1825 | VarExprFlags exprFlags; | |
1826 | } ApplyModifiersState; | 1826 | } ApplyModifiersState; | |
1827 | 1827 | |||
1828 | static void | 1828 | static void | |
1829 | ApplyModifiersState_Define(ApplyModifiersState *st) | 1829 | ApplyModifiersState_Define(ApplyModifiersState *st) | |
1830 | { | 1830 | { | |
1831 | if (st->exprFlags & VEF_UNDEF) | 1831 | if (st->exprFlags & VEF_UNDEF) | |
1832 | st->exprFlags |= VEF_DEF; | 1832 | st->exprFlags |= VEF_DEF; | |
1833 | } | 1833 | } | |
1834 | 1834 | |||
1835 | typedef enum ApplyModifierResult { | 1835 | typedef enum ApplyModifierResult { | |
1836 | AMR_OK, /* Continue parsing */ | 1836 | AMR_OK, /* Continue parsing */ | |
1837 | AMR_UNKNOWN, /* Not a match, try other modifiers as well */ | 1837 | AMR_UNKNOWN, /* Not a match, try other modifiers as well */ | |
1838 | AMR_BAD, /* Error out with "Bad modifier" message */ | 1838 | AMR_BAD, /* Error out with "Bad modifier" message */ | |
1839 | AMR_CLEANUP /* Error out without error message */ | 1839 | AMR_CLEANUP /* Error out without error message */ | |
1840 | } ApplyModifierResult; | 1840 | } ApplyModifierResult; | |
1841 | 1841 | |||
1842 | /*- | 1842 | /*- | |
1843 | * Parse a part of a modifier such as the "from" and "to" in :S/from/to/ | 1843 | * Parse a part of a modifier such as the "from" and "to" in :S/from/to/ | |
1844 | * or the "var" or "replacement" in :@var@replacement+${var}@, up to and | 1844 | * or the "var" or "replacement" in :@var@replacement+${var}@, up to and | |
1845 | * including the next unescaped delimiter. The delimiter, as well as the | 1845 | * including the next unescaped delimiter. The delimiter, as well as the | |
1846 | * backslash or the dollar, can be escaped with a backslash. | 1846 | * backslash or the dollar, can be escaped with a backslash. | |
1847 | * | 1847 | * | |
1848 | * Return the parsed (and possibly expanded) string, or NULL if no delimiter | 1848 | * Return the parsed (and possibly expanded) string, or NULL if no delimiter | |
1849 | * was found. On successful return, the parsing position pp points right | 1849 | * was found. On successful return, the parsing position pp points right | |
1850 | * after the delimiter. The delimiter is not included in the returned | 1850 | * after the delimiter. The delimiter is not included in the returned | |
1851 | * value though. | 1851 | * value though. | |
1852 | */ | 1852 | */ | |
1853 | static VarParseResult | 1853 | static VarParseResult | |
1854 | ParseModifierPart( | 1854 | ParseModifierPart( | |
1855 | const char **pp, /* The parsing position, updated upon return */ | 1855 | const char **pp, /* The parsing position, updated upon return */ |