Mon Nov 2 20:50:24 2020 UTC ()
make(1): clean up CompatDeleteTarget and CompatInterrupt


(rillig)
diff -r1.173 -r1.174 src/usr.bin/make/compat.c

cvs diff -r1.173 -r1.174 src/usr.bin/make/compat.c (switch to unified diff)

--- src/usr.bin/make/compat.c 2020/11/01 17:47:26 1.173
+++ src/usr.bin/make/compat.c 2020/11/02 20:50:24 1.174
@@ -1,706 +1,704 @@ @@ -1,706 +1,704 @@
1/* $NetBSD: compat.c,v 1.173 2020/11/01 17:47:26 rillig Exp $ */ 1/* $NetBSD: compat.c,v 1.174 2020/11/02 20:50:24 rillig Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. 4 * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
5 * All rights reserved. 5 * 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) 1988, 1989 by Adam de Boor 36 * Copyright (c) 1988, 1989 by Adam de Boor
37 * Copyright (c) 1989 by Berkeley Softworks 37 * Copyright (c) 1989 by Berkeley Softworks
38 * All rights reserved. 38 * All rights reserved.
39 * 39 *
40 * This code is derived from software contributed to Berkeley by 40 * This code is derived from software contributed to Berkeley by
41 * Adam de Boor. 41 * Adam de Boor.
42 * 42 *
43 * Redistribution and use in source and binary forms, with or without 43 * Redistribution and use in source and binary forms, with or without
44 * modification, are permitted provided that the following conditions 44 * modification, are permitted provided that the following conditions
45 * are met: 45 * are met:
46 * 1. Redistributions of source code must retain the above copyright 46 * 1. Redistributions of source code must retain the above copyright
47 * notice, this list of conditions and the following disclaimer. 47 * notice, this list of conditions and the following disclaimer.
48 * 2. Redistributions in binary form must reproduce the above copyright 48 * 2. Redistributions in binary form must reproduce the above copyright
49 * notice, this list of conditions and the following disclaimer in the 49 * notice, this list of conditions and the following disclaimer in the
50 * documentation and/or other materials provided with the distribution. 50 * documentation and/or other materials provided with the distribution.
51 * 3. All advertising materials mentioning features or use of this software 51 * 3. All advertising materials mentioning features or use of this software
52 * must display the following acknowledgement: 52 * must display the following acknowledgement:
53 * This product includes software developed by the University of 53 * This product includes software developed by the University of
54 * California, Berkeley and its contributors. 54 * California, Berkeley and its contributors.
55 * 4. Neither the name of the University nor the names of its contributors 55 * 4. Neither the name of the University nor the names of its contributors
56 * may be used to endorse or promote products derived from this software 56 * may be used to endorse or promote products derived from this software
57 * without specific prior written permission. 57 * without specific prior written permission.
58 * 58 *
59 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 59 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
60 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 60 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
61 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 61 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
62 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 62 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
63 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 63 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
64 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 64 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
65 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 65 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
66 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 66 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
67 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 67 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
68 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 68 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
69 * SUCH DAMAGE. 69 * SUCH DAMAGE.
70 */ 70 */
71 71
72/*- 72/*-
73 * compat.c -- 73 * compat.c --
74 * The routines in this file implement the full-compatibility 74 * The routines in this file implement the full-compatibility
75 * mode of PMake. Most of the special functionality of PMake 75 * mode of PMake. Most of the special functionality of PMake
76 * is available in this mode. Things not supported: 76 * is available in this mode. Things not supported:
77 * - different shells. 77 * - different shells.
78 * - friendly variable substitution. 78 * - friendly variable substitution.
79 * 79 *
80 * Interface: 80 * Interface:
81 * Compat_Run Initialize things for this module and recreate 81 * Compat_Run Initialize things for this module and recreate
82 * thems as need creatin' 82 * thems as need creatin'
83 */ 83 */
84 84
85#include <sys/types.h> 85#include <sys/types.h>
86#include <sys/stat.h> 86#include <sys/stat.h>
87#include <sys/wait.h> 87#include <sys/wait.h>
88 88
89#include <errno.h> 89#include <errno.h>
90#include <signal.h> 90#include <signal.h>
91 91
92#include "make.h" 92#include "make.h"
93#include "dir.h" 93#include "dir.h"
94#include "job.h" 94#include "job.h"
95#include "metachar.h" 95#include "metachar.h"
96#include "pathnames.h" 96#include "pathnames.h"
97 97
98/* "@(#)compat.c 8.2 (Berkeley) 3/19/94" */ 98/* "@(#)compat.c 8.2 (Berkeley) 3/19/94" */
99MAKE_RCSID("$NetBSD: compat.c,v 1.173 2020/11/01 17:47:26 rillig Exp $"); 99MAKE_RCSID("$NetBSD: compat.c,v 1.174 2020/11/02 20:50:24 rillig Exp $");
100 100
101static GNode *curTarg = NULL; 101static GNode *curTarg = NULL;
102static pid_t compatChild; 102static pid_t compatChild;
103static int compatSigno; 103static int compatSigno;
104 104
105/* 105/*
106 * CompatDeleteTarget -- delete a failed, interrupted, or otherwise 106 * CompatDeleteTarget -- delete the file of a failed, interrupted, or
107 * duffed target if not inhibited by .PRECIOUS. 107 * otherwise duffed target if not inhibited by .PRECIOUS.
108 */ 108 */
109static void 109static void
110CompatDeleteTarget(GNode *gn) 110CompatDeleteTarget(GNode *gn)
111{ 111{
112 if (gn != NULL && !Targ_Precious(gn)) { 112 if (gn != NULL && !Targ_Precious(gn)) {
113 const char *file = GNode_VarTarget(gn); 113 const char *file = GNode_VarTarget(gn);
114 114
115 if (!opts.noExecute && eunlink(file) != -1) { 115 if (!opts.noExecute && eunlink(file) != -1) {
116 Error("*** %s removed", file); 116 Error("*** %s removed", file);
117 } 117 }
118 } 118 }
119} 119}
120 120
121/* Interrupt the creation of the current target and remove it if it ain't 121/* Interrupt the creation of the current target and remove it if it ain't
122 * precious. Then exit. 122 * precious. Then exit.
123 * 123 *
124 * If .INTERRUPT exists, its commands are run first WITH INTERRUPTS IGNORED. 124 * If .INTERRUPT exists, its commands are run first WITH INTERRUPTS IGNORED.
125 * 125 *
126 * XXX: is .PRECIOUS supposed to inhibit .INTERRUPT? I doubt it, but I've 126 * XXX: is .PRECIOUS supposed to inhibit .INTERRUPT? I doubt it, but I've
127 * left the logic alone for now. - dholland 20160826 127 * left the logic alone for now. - dholland 20160826
128 */ 128 */
129static void 129static void
130CompatInterrupt(int signo) 130CompatInterrupt(int signo)
131{ 131{
132 GNode *gn; 
133 
134 CompatDeleteTarget(curTarg); 132 CompatDeleteTarget(curTarg);
135 133
136 if (curTarg != NULL && !Targ_Precious(curTarg)) { 134 if (curTarg != NULL && !Targ_Precious(curTarg)) {
137 /* 135 /*
138 * Run .INTERRUPT only if hit with interrupt signal 136 * Run .INTERRUPT only if hit with interrupt signal
139 */ 137 */
140 if (signo == SIGINT) { 138 if (signo == SIGINT) {
141 gn = Targ_FindNode(".INTERRUPT"); 139 GNode *gn = Targ_FindNode(".INTERRUPT");
142 if (gn != NULL) { 140 if (gn != NULL) {
143 Compat_Make(gn, gn); 141 Compat_Make(gn, gn);
144 } 142 }
145 } 143 }
146 } 144 }
147 145
148 if (signo == SIGQUIT) 146 if (signo == SIGQUIT)
149 _exit(signo); 147 _exit(signo);
150 148
151 /* 149 /*
152 * If there is a child running, pass the signal on. 150 * If there is a child running, pass the signal on.
153 * We will exist after it has exited. 151 * We will exist after it has exited.
154 */ 152 */
155 compatSigno = signo; 153 compatSigno = signo;
156 if (compatChild > 0) { 154 if (compatChild > 0) {
157 KILLPG(compatChild, signo); 155 KILLPG(compatChild, signo);
158 } else { 156 } else {
159 bmake_signal(signo, SIG_DFL); 157 bmake_signal(signo, SIG_DFL);
160 kill(myPid, signo); 158 kill(myPid, signo);
161 } 159 }
162} 160}
163 161
164/* Execute the next command for a target. If the command returns an error, 162/* Execute the next command for a target. If the command returns an error,
165 * the node's made field is set to ERROR and creation stops. 163 * the node's made field is set to ERROR and creation stops.
166 * 164 *
167 * Input: 165 * Input:
168 * cmdp Command to execute 166 * cmdp Command to execute
169 * gnp Node from which the command came 167 * gnp Node from which the command came
170 * 168 *
171 * Results: 169 * Results:
172 * 0 if the command succeeded, 1 if an error occurred. 170 * 0 if the command succeeded, 1 if an error occurred.
173 */ 171 */
174int 172int
175Compat_RunCommand(const char *cmdp, GNode *gn) 173Compat_RunCommand(const char *cmdp, GNode *gn)
176{ 174{
177 char *cmdStart; /* Start of expanded command */ 175 char *cmdStart; /* Start of expanded command */
178 char *bp; 176 char *bp;
179 Boolean silent; /* Don't print command */ 177 Boolean silent; /* Don't print command */
180 Boolean doIt; /* Execute even if -n */ 178 Boolean doIt; /* Execute even if -n */
181 volatile Boolean errCheck; /* Check errors */ 179 volatile Boolean errCheck; /* Check errors */
182 int reason; /* Reason for child's death */ 180 int reason; /* Reason for child's death */
183 int status; /* Description of child's death */ 181 int status; /* Description of child's death */
184 pid_t cpid; /* Child actually found */ 182 pid_t cpid; /* Child actually found */
185 pid_t retstat; /* Result of wait */ 183 pid_t retstat; /* Result of wait */
186 StringListNode *cmdNode; /* Node where current command is located */ 184 StringListNode *cmdNode; /* Node where current command is located */
187 const char **volatile av; /* Argument vector for thing to exec */ 185 const char **volatile av; /* Argument vector for thing to exec */
188 char **volatile mav; /* Copy of the argument vector for freeing */ 186 char **volatile mav; /* Copy of the argument vector for freeing */
189 Boolean useShell; /* TRUE if command should be executed 187 Boolean useShell; /* TRUE if command should be executed
190 * using a shell */ 188 * using a shell */
191 const char *volatile cmd = cmdp; 189 const char *volatile cmd = cmdp;
192 190
193 silent = (gn->type & OP_SILENT) != 0; 191 silent = (gn->type & OP_SILENT) != 0;
194 errCheck = !(gn->type & OP_IGNORE); 192 errCheck = !(gn->type & OP_IGNORE);
195 doIt = FALSE; 193 doIt = FALSE;
196 194
197 /* Luckily the commands don't end up in a string pool, otherwise 195 /* Luckily the commands don't end up in a string pool, otherwise
198 * this comparison could match too early, in a dependency using "..." 196 * this comparison could match too early, in a dependency using "..."
199 * for delayed commands, run in parallel mode, using the same shell 197 * for delayed commands, run in parallel mode, using the same shell
200 * command line more than once; see JobPrintCommand. 198 * command line more than once; see JobPrintCommand.
201 * TODO: write a unit-test to protect against this potential bug. */ 199 * TODO: write a unit-test to protect against this potential bug. */
202 cmdNode = Lst_FindDatum(gn->commands, cmd); 200 cmdNode = Lst_FindDatum(gn->commands, cmd);
203 (void)Var_Subst(cmd, gn, VARE_WANTRES, &cmdStart); 201 (void)Var_Subst(cmd, gn, VARE_WANTRES, &cmdStart);
204 /* TODO: handle errors */ 202 /* TODO: handle errors */
205 203
206 if (*cmdStart == '\0') { 204 if (*cmdStart == '\0') {
207 free(cmdStart); 205 free(cmdStart);
208 return 0; 206 return 0;
209 } 207 }
210 cmd = cmdStart; 208 cmd = cmdStart;
211 LstNode_Set(cmdNode, cmdStart); 209 LstNode_Set(cmdNode, cmdStart);
212 210
213 if (gn->type & OP_SAVE_CMDS) { 211 if (gn->type & OP_SAVE_CMDS) {
214 GNode *endNode = Targ_GetEndNode(); 212 GNode *endNode = Targ_GetEndNode();
215 if (gn != endNode) { 213 if (gn != endNode) {
216 Lst_Append(endNode->commands, cmdStart); 214 Lst_Append(endNode->commands, cmdStart);
217 return 0; 215 return 0;
218 } 216 }
219 } 217 }
220 if (strcmp(cmdStart, "...") == 0) { 218 if (strcmp(cmdStart, "...") == 0) {
221 gn->type |= OP_SAVE_CMDS; 219 gn->type |= OP_SAVE_CMDS;
222 return 0; 220 return 0;
223 } 221 }
224 222
225 while (*cmd == '@' || *cmd == '-' || *cmd == '+') { 223 while (*cmd == '@' || *cmd == '-' || *cmd == '+') {
226 switch (*cmd) { 224 switch (*cmd) {
227 case '@': 225 case '@':
228 silent = !DEBUG(LOUD); 226 silent = !DEBUG(LOUD);
229 break; 227 break;
230 case '-': 228 case '-':
231 errCheck = FALSE; 229 errCheck = FALSE;
232 break; 230 break;
233 case '+': 231 case '+':
234 doIt = TRUE; 232 doIt = TRUE;
235 if (!shellName) /* we came here from jobs */ 233 if (!shellName) /* we came here from jobs */
236 Shell_Init(); 234 Shell_Init();
237 break; 235 break;
238 } 236 }
239 cmd++; 237 cmd++;
240 } 238 }
241 239
242 while (ch_isspace(*cmd)) 240 while (ch_isspace(*cmd))
243 cmd++; 241 cmd++;
244 242
245 /* 243 /*
246 * If we did not end up with a command, just skip it. 244 * If we did not end up with a command, just skip it.
247 */ 245 */
248 if (!*cmd) 246 if (!*cmd)
249 return 0; 247 return 0;
250 248
251#if !defined(MAKE_NATIVE) 249#if !defined(MAKE_NATIVE)
252 /* 250 /*
253 * In a non-native build, the host environment might be weird enough 251 * In a non-native build, the host environment might be weird enough
254 * that it's necessary to go through a shell to get the correct 252 * that it's necessary to go through a shell to get the correct
255 * behaviour. Or perhaps the shell has been replaced with something 253 * behaviour. Or perhaps the shell has been replaced with something
256 * that does extra logging, and that should not be bypassed. 254 * that does extra logging, and that should not be bypassed.
257 */ 255 */
258 useShell = TRUE; 256 useShell = TRUE;
259#else 257#else
260 /* 258 /*
261 * Search for meta characters in the command. If there are no meta 259 * Search for meta characters in the command. If there are no meta
262 * characters, there's no need to execute a shell to execute the 260 * characters, there's no need to execute a shell to execute the
263 * command. 261 * command.
264 * 262 *
265 * Additionally variable assignments and empty commands 263 * Additionally variable assignments and empty commands
266 * go to the shell. Therefore treat '=' and ':' like shell 264 * go to the shell. Therefore treat '=' and ':' like shell
267 * meta characters as documented in make(1). 265 * meta characters as documented in make(1).
268 */ 266 */
269 267
270 useShell = needshell(cmd); 268 useShell = needshell(cmd);
271#endif 269#endif
272 270
273 /* 271 /*
274 * Print the command before echoing if we're not supposed to be quiet for 272 * Print the command before echoing if we're not supposed to be quiet for
275 * this one. We also print the command if -n given. 273 * this one. We also print the command if -n given.
276 */ 274 */
277 if (!silent || !GNode_ShouldExecute(gn)) { 275 if (!silent || !GNode_ShouldExecute(gn)) {
278 printf("%s\n", cmd); 276 printf("%s\n", cmd);
279 fflush(stdout); 277 fflush(stdout);
280 } 278 }
281 279
282 /* 280 /*
283 * If we're not supposed to execute any commands, this is as far as 281 * If we're not supposed to execute any commands, this is as far as
284 * we go... 282 * we go...
285 */ 283 */
286 if (!doIt && !GNode_ShouldExecute(gn)) { 284 if (!doIt && !GNode_ShouldExecute(gn)) {
287 return 0; 285 return 0;
288 } 286 }
289 DEBUG1(JOB, "Execute: '%s'\n", cmd); 287 DEBUG1(JOB, "Execute: '%s'\n", cmd);
290 288
291 if (useShell) { 289 if (useShell) {
292 /* 290 /*
293 * We need to pass the command off to the shell, typically 291 * We need to pass the command off to the shell, typically
294 * because the command contains a "meta" character. 292 * because the command contains a "meta" character.
295 */ 293 */
296 static const char *shargv[5]; 294 static const char *shargv[5];
297 int shargc; 295 int shargc;
298 296
299 shargc = 0; 297 shargc = 0;
300 shargv[shargc++] = shellPath; 298 shargv[shargc++] = shellPath;
301 /* 299 /*
302 * The following work for any of the builtin shell specs. 300 * The following work for any of the builtin shell specs.
303 */ 301 */
304 if (errCheck && shellErrFlag) { 302 if (errCheck && shellErrFlag) {
305 shargv[shargc++] = shellErrFlag; 303 shargv[shargc++] = shellErrFlag;
306 } 304 }
307 if (DEBUG(SHELL)) 305 if (DEBUG(SHELL))
308 shargv[shargc++] = "-xc"; 306 shargv[shargc++] = "-xc";
309 else 307 else
310 shargv[shargc++] = "-c"; 308 shargv[shargc++] = "-c";
311 shargv[shargc++] = cmd; 309 shargv[shargc++] = cmd;
312 shargv[shargc] = NULL; 310 shargv[shargc] = NULL;
313 av = shargv; 311 av = shargv;
314 bp = NULL; 312 bp = NULL;
315 mav = NULL; 313 mav = NULL;
316 } else { 314 } else {
317 /* 315 /*
318 * No meta-characters, so no need to exec a shell. Break the command 316 * No meta-characters, so no need to exec a shell. Break the command
319 * into words to form an argument vector we can execute. 317 * into words to form an argument vector we can execute.
320 */ 318 */
321 Words words = Str_Words(cmd, FALSE); 319 Words words = Str_Words(cmd, FALSE);
322 mav = words.words; 320 mav = words.words;
323 bp = words.freeIt; 321 bp = words.freeIt;
324 av = (void *)mav; 322 av = (void *)mav;
325 } 323 }
326 324
327#ifdef USE_META 325#ifdef USE_META
328 if (useMeta) { 326 if (useMeta) {
329 meta_compat_start(); 327 meta_compat_start();
330 } 328 }
331#endif 329#endif
332 330
333 /* 331 /*
334 * Fork and execute the single command. If the fork fails, we abort. 332 * Fork and execute the single command. If the fork fails, we abort.
335 */ 333 */
336 compatChild = cpid = vFork(); 334 compatChild = cpid = vFork();
337 if (cpid < 0) { 335 if (cpid < 0) {
338 Fatal("Could not fork"); 336 Fatal("Could not fork");
339 } 337 }
340 if (cpid == 0) { 338 if (cpid == 0) {
341 Var_ExportVars(); 339 Var_ExportVars();
342#ifdef USE_META 340#ifdef USE_META
343 if (useMeta) { 341 if (useMeta) {
344 meta_compat_child(); 342 meta_compat_child();
345 } 343 }
346#endif 344#endif
347 (void)execvp(av[0], (char *const *)UNCONST(av)); 345 (void)execvp(av[0], (char *const *)UNCONST(av));
348 execDie("exec", av[0]); 346 execDie("exec", av[0]);
349 } 347 }
350 348
351 free(mav); 349 free(mav);
352 free(bp); 350 free(bp);
353 351
354 /* XXX: Memory management looks suspicious here. */ 352 /* XXX: Memory management looks suspicious here. */
355 /* XXX: Setting a list item to NULL is unexpected. */ 353 /* XXX: Setting a list item to NULL is unexpected. */
356 LstNode_SetNull(cmdNode); 354 LstNode_SetNull(cmdNode);
357 355
358#ifdef USE_META 356#ifdef USE_META
359 if (useMeta) { 357 if (useMeta) {
360 meta_compat_parent(cpid); 358 meta_compat_parent(cpid);
361 } 359 }
362#endif 360#endif
363 361
364 /* 362 /*
365 * The child is off and running. Now all we can do is wait... 363 * The child is off and running. Now all we can do is wait...
366 */ 364 */
367 while ((retstat = wait(&reason)) != cpid) { 365 while ((retstat = wait(&reason)) != cpid) {
368 if (retstat > 0) 366 if (retstat > 0)
369 JobReapChild(retstat, reason, FALSE); /* not ours? */ 367 JobReapChild(retstat, reason, FALSE); /* not ours? */
370 if (retstat == -1 && errno != EINTR) { 368 if (retstat == -1 && errno != EINTR) {
371 break; 369 break;
372 } 370 }
373 } 371 }
374 372
375 if (retstat < 0) 373 if (retstat < 0)
376 Fatal("error in wait: %d: %s", retstat, strerror(errno)); 374 Fatal("error in wait: %d: %s", retstat, strerror(errno));
377 375
378 if (WIFSTOPPED(reason)) { 376 if (WIFSTOPPED(reason)) {
379 status = WSTOPSIG(reason); /* stopped */ 377 status = WSTOPSIG(reason); /* stopped */
380 } else if (WIFEXITED(reason)) { 378 } else if (WIFEXITED(reason)) {
381 status = WEXITSTATUS(reason); /* exited */ 379 status = WEXITSTATUS(reason); /* exited */
382#if defined(USE_META) && defined(USE_FILEMON_ONCE) 380#if defined(USE_META) && defined(USE_FILEMON_ONCE)
383 if (useMeta) { 381 if (useMeta) {
384 meta_cmd_finish(NULL); 382 meta_cmd_finish(NULL);
385 } 383 }
386#endif 384#endif
387 if (status != 0) { 385 if (status != 0) {
388 if (DEBUG(ERROR)) { 386 if (DEBUG(ERROR)) {
389 const char *cp; 387 const char *cp;
390 debug_printf("\n*** Failed target: %s\n*** Failed command: ", 388 debug_printf("\n*** Failed target: %s\n*** Failed command: ",
391 gn->name); 389 gn->name);
392 for (cp = cmd; *cp; ) { 390 for (cp = cmd; *cp; ) {
393 if (ch_isspace(*cp)) { 391 if (ch_isspace(*cp)) {
394 debug_printf(" "); 392 debug_printf(" ");
395 while (ch_isspace(*cp)) 393 while (ch_isspace(*cp))
396 cp++; 394 cp++;
397 } else { 395 } else {
398 debug_printf("%c", *cp); 396 debug_printf("%c", *cp);
399 cp++; 397 cp++;
400 } 398 }
401 } 399 }
402 debug_printf("\n"); 400 debug_printf("\n");
403 } 401 }
404 printf("*** Error code %d", status); 402 printf("*** Error code %d", status);
405 } 403 }
406 } else { 404 } else {
407 status = WTERMSIG(reason); /* signaled */ 405 status = WTERMSIG(reason); /* signaled */
408 printf("*** Signal %d", status); 406 printf("*** Signal %d", status);
409 } 407 }
410 408
411 409
412 if (!WIFEXITED(reason) || status != 0) { 410 if (!WIFEXITED(reason) || status != 0) {
413 if (errCheck) { 411 if (errCheck) {
414#ifdef USE_META 412#ifdef USE_META
415 if (useMeta) { 413 if (useMeta) {
416 meta_job_error(NULL, gn, 0, status); 414 meta_job_error(NULL, gn, 0, status);
417 } 415 }
418#endif 416#endif
419 gn->made = ERROR; 417 gn->made = ERROR;
420 if (opts.keepgoing) { 418 if (opts.keepgoing) {
421 /* Abort the current target, but let others continue. */ 419 /* Abort the current target, but let others continue. */
422 printf(" (continuing)\n"); 420 printf(" (continuing)\n");
423 } else { 421 } else {
424 printf("\n"); 422 printf("\n");
425 } 423 }
426 if (deleteOnError) 424 if (deleteOnError)
427 CompatDeleteTarget(gn); 425 CompatDeleteTarget(gn);
428 } else { 426 } else {
429 /* 427 /*
430 * Continue executing commands for this target. 428 * Continue executing commands for this target.
431 * If we return 0, this will happen... 429 * If we return 0, this will happen...
432 */ 430 */
433 printf(" (ignored)\n"); 431 printf(" (ignored)\n");
434 status = 0; 432 status = 0;
435 } 433 }
436 } 434 }
437 435
438 free(cmdStart); 436 free(cmdStart);
439 compatChild = 0; 437 compatChild = 0;
440 if (compatSigno) { 438 if (compatSigno) {
441 bmake_signal(compatSigno, SIG_DFL); 439 bmake_signal(compatSigno, SIG_DFL);
442 kill(myPid, compatSigno); 440 kill(myPid, compatSigno);
443 } 441 }
444 442
445 return status; 443 return status;
446} 444}
447 445
448static void 446static void
449RunCommands(GNode *gn) 447RunCommands(GNode *gn)
450{ 448{
451 StringListNode *ln; 449 StringListNode *ln;
452 for (ln = gn->commands->first; ln != NULL; ln = ln->next) { 450 for (ln = gn->commands->first; ln != NULL; ln = ln->next) {
453 const char *cmd = ln->datum; 451 const char *cmd = ln->datum;
454 if (Compat_RunCommand(cmd, gn) != 0) 452 if (Compat_RunCommand(cmd, gn) != 0)
455 break; 453 break;
456 } 454 }
457} 455}
458 456
459static void 457static void
460MakeNodes(GNodeList *gnodes, GNode *pgn) 458MakeNodes(GNodeList *gnodes, GNode *pgn)
461{ 459{
462 GNodeListNode *ln; 460 GNodeListNode *ln;
463 for (ln = gnodes->first; ln != NULL; ln = ln->next) { 461 for (ln = gnodes->first; ln != NULL; ln = ln->next) {
464 GNode *cohort = ln->datum; 462 GNode *cohort = ln->datum;
465 Compat_Make(cohort, pgn); 463 Compat_Make(cohort, pgn);
466 } 464 }
467} 465}
468 466
469/* Make a target. 467/* Make a target.
470 * 468 *
471 * If an error is detected and not being ignored, the process exits. 469 * If an error is detected and not being ignored, the process exits.
472 * 470 *
473 * Input: 471 * Input:
474 * gn The node to make 472 * gn The node to make
475 * pgn Parent to abort if necessary 473 * pgn Parent to abort if necessary
476 */ 474 */
477void 475void
478Compat_Make(GNode *gn, GNode *pgn) 476Compat_Make(GNode *gn, GNode *pgn)
479{ 477{
480 if (!shellName) /* we came here from jobs */ 478 if (!shellName) /* we came here from jobs */
481 Shell_Init(); 479 Shell_Init();
482 if (gn->made == UNMADE && (gn == pgn || !(pgn->type & OP_MADE))) { 480 if (gn->made == UNMADE && (gn == pgn || !(pgn->type & OP_MADE))) {
483 /* 481 /*
484 * First mark ourselves to be made, then apply whatever transformations 482 * First mark ourselves to be made, then apply whatever transformations
485 * the suffix module thinks are necessary. Once that's done, we can 483 * the suffix module thinks are necessary. Once that's done, we can
486 * descend and make all our children. If any of them has an error 484 * descend and make all our children. If any of them has an error
487 * but the -k flag was given, our 'make' field will be set FALSE again. 485 * but the -k flag was given, our 'make' field will be set FALSE again.
488 * This is our signal to not attempt to do anything but abort our 486 * This is our signal to not attempt to do anything but abort our
489 * parent as well. 487 * parent as well.
490 */ 488 */
491 gn->flags |= REMAKE; 489 gn->flags |= REMAKE;
492 gn->made = BEINGMADE; 490 gn->made = BEINGMADE;
493 if (!(gn->type & OP_MADE)) 491 if (!(gn->type & OP_MADE))
494 Suff_FindDeps(gn); 492 Suff_FindDeps(gn);
495 MakeNodes(gn->children, gn); 493 MakeNodes(gn->children, gn);
496 if (!(gn->flags & REMAKE)) { 494 if (!(gn->flags & REMAKE)) {
497 gn->made = ABORTED; 495 gn->made = ABORTED;
498 pgn->flags &= ~(unsigned)REMAKE; 496 pgn->flags &= ~(unsigned)REMAKE;
499 goto cohorts; 497 goto cohorts;
500 } 498 }
501 499
502 if (Lst_FindDatum(gn->implicitParents, pgn) != NULL) 500 if (Lst_FindDatum(gn->implicitParents, pgn) != NULL)
503 Var_Set(IMPSRC, GNode_VarTarget(gn), pgn); 501 Var_Set(IMPSRC, GNode_VarTarget(gn), pgn);
504 502
505 /* 503 /*
506 * All the children were made ok. Now youngestChild->mtime contains the 504 * All the children were made ok. Now youngestChild->mtime contains the
507 * modification time of the newest child, we need to find out if we 505 * modification time of the newest child, we need to find out if we
508 * exist and when we were modified last. The criteria for datedness 506 * exist and when we were modified last. The criteria for datedness
509 * are defined by the Make_OODate function. 507 * are defined by the Make_OODate function.
510 */ 508 */
511 DEBUG1(MAKE, "Examining %s...", gn->name); 509 DEBUG1(MAKE, "Examining %s...", gn->name);
512 if (!Make_OODate(gn)) { 510 if (!Make_OODate(gn)) {
513 gn->made = UPTODATE; 511 gn->made = UPTODATE;
514 DEBUG0(MAKE, "up-to-date.\n"); 512 DEBUG0(MAKE, "up-to-date.\n");
515 goto cohorts; 513 goto cohorts;
516 } else 514 } else
517 DEBUG0(MAKE, "out-of-date.\n"); 515 DEBUG0(MAKE, "out-of-date.\n");
518 516
519 /* 517 /*
520 * If the user is just seeing if something is out-of-date, exit now 518 * If the user is just seeing if something is out-of-date, exit now
521 * to tell him/her "yes". 519 * to tell him/her "yes".
522 */ 520 */
523 if (opts.queryFlag) { 521 if (opts.queryFlag) {
524 exit(1); 522 exit(1);
525 } 523 }
526 524
527 /* 525 /*
528 * We need to be re-made. We also have to make sure we've got a $? 526 * We need to be re-made. We also have to make sure we've got a $?
529 * variable. To be nice, we also define the $> variable using 527 * variable. To be nice, we also define the $> variable using
530 * Make_DoAllVar(). 528 * Make_DoAllVar().
531 */ 529 */
532 Make_DoAllVar(gn); 530 Make_DoAllVar(gn);
533 531
534 /* 532 /*
535 * Alter our type to tell if errors should be ignored or things 533 * Alter our type to tell if errors should be ignored or things
536 * should not be printed so CompatRunCommand knows what to do. 534 * should not be printed so CompatRunCommand knows what to do.
537 */ 535 */
538 if (Targ_Ignore(gn)) 536 if (Targ_Ignore(gn))
539 gn->type |= OP_IGNORE; 537 gn->type |= OP_IGNORE;
540 if (Targ_Silent(gn)) 538 if (Targ_Silent(gn))
541 gn->type |= OP_SILENT; 539 gn->type |= OP_SILENT;
542 540
543 if (Job_CheckCommands(gn, Fatal)) { 541 if (Job_CheckCommands(gn, Fatal)) {
544 /* 542 /*
545 * Our commands are ok, but we still have to worry about the -t 543 * Our commands are ok, but we still have to worry about the -t
546 * flag... 544 * flag...
547 */ 545 */
548 if (!opts.touchFlag || (gn->type & OP_MAKE)) { 546 if (!opts.touchFlag || (gn->type & OP_MAKE)) {
549 curTarg = gn; 547 curTarg = gn;
550#ifdef USE_META 548#ifdef USE_META
551 if (useMeta && GNode_ShouldExecute(gn)) { 549 if (useMeta && GNode_ShouldExecute(gn)) {
552 meta_job_start(NULL, gn); 550 meta_job_start(NULL, gn);
553 } 551 }
554#endif 552#endif
555 RunCommands(gn); 553 RunCommands(gn);
556 curTarg = NULL; 554 curTarg = NULL;
557 } else { 555 } else {
558 Job_Touch(gn, (gn->type & OP_SILENT) != 0); 556 Job_Touch(gn, (gn->type & OP_SILENT) != 0);
559 } 557 }
560 } else { 558 } else {
561 gn->made = ERROR; 559 gn->made = ERROR;
562 } 560 }
563#ifdef USE_META 561#ifdef USE_META
564 if (useMeta && GNode_ShouldExecute(gn)) { 562 if (useMeta && GNode_ShouldExecute(gn)) {
565 if (meta_job_finish(NULL) != 0) 563 if (meta_job_finish(NULL) != 0)
566 gn->made = ERROR; 564 gn->made = ERROR;
567 } 565 }
568#endif 566#endif
569 567
570 if (gn->made != ERROR) { 568 if (gn->made != ERROR) {
571 /* 569 /*
572 * If the node was made successfully, mark it so, update 570 * If the node was made successfully, mark it so, update
573 * its modification time and timestamp all its parents. Note 571 * its modification time and timestamp all its parents. Note
574 * that for .ZEROTIME targets, the timestamping isn't done. 572 * that for .ZEROTIME targets, the timestamping isn't done.
575 * This is to keep its state from affecting that of its parent. 573 * This is to keep its state from affecting that of its parent.
576 */ 574 */
577 gn->made = MADE; 575 gn->made = MADE;
578 pgn->flags |= Make_Recheck(gn) == 0 ? FORCE : 0; 576 pgn->flags |= Make_Recheck(gn) == 0 ? FORCE : 0;
579 if (!(gn->type & OP_EXEC)) { 577 if (!(gn->type & OP_EXEC)) {
580 pgn->flags |= CHILDMADE; 578 pgn->flags |= CHILDMADE;
581 Make_TimeStamp(pgn, gn); 579 Make_TimeStamp(pgn, gn);
582 } 580 }
583 } else if (opts.keepgoing) { 581 } else if (opts.keepgoing) {
584 pgn->flags &= ~(unsigned)REMAKE; 582 pgn->flags &= ~(unsigned)REMAKE;
585 } else { 583 } else {
586 PrintOnError(gn, "\nStop."); 584 PrintOnError(gn, "\nStop.");
587 exit(1); 585 exit(1);
588 } 586 }
589 } else if (gn->made == ERROR) { 587 } else if (gn->made == ERROR) {
590 /* Already had an error when making this. Tell the parent to abort. */ 588 /* Already had an error when making this. Tell the parent to abort. */
591 pgn->flags &= ~(unsigned)REMAKE; 589 pgn->flags &= ~(unsigned)REMAKE;
592 } else { 590 } else {
593 if (Lst_FindDatum(gn->implicitParents, pgn) != NULL) { 591 if (Lst_FindDatum(gn->implicitParents, pgn) != NULL) {
594 const char *target = GNode_VarTarget(gn); 592 const char *target = GNode_VarTarget(gn);
595 Var_Set(IMPSRC, target != NULL ? target : "", pgn); 593 Var_Set(IMPSRC, target != NULL ? target : "", pgn);
596 } 594 }
597 switch(gn->made) { 595 switch(gn->made) {
598 case BEINGMADE: 596 case BEINGMADE:
599 Error("Graph cycles through %s", gn->name); 597 Error("Graph cycles through %s", gn->name);
600 gn->made = ERROR; 598 gn->made = ERROR;
601 pgn->flags &= ~(unsigned)REMAKE; 599 pgn->flags &= ~(unsigned)REMAKE;
602 break; 600 break;
603 case MADE: 601 case MADE:
604 if ((gn->type & OP_EXEC) == 0) { 602 if ((gn->type & OP_EXEC) == 0) {
605 pgn->flags |= CHILDMADE; 603 pgn->flags |= CHILDMADE;
606 Make_TimeStamp(pgn, gn); 604 Make_TimeStamp(pgn, gn);
607 } 605 }
608 break; 606 break;
609 case UPTODATE: 607 case UPTODATE:
610 if ((gn->type & OP_EXEC) == 0) { 608 if ((gn->type & OP_EXEC) == 0) {
611 Make_TimeStamp(pgn, gn); 609 Make_TimeStamp(pgn, gn);
612 } 610 }
613 break; 611 break;
614 default: 612 default:
615 break; 613 break;
616 } 614 }
617 } 615 }
618 616
619cohorts: 617cohorts:
620 MakeNodes(gn->cohorts, pgn); 618 MakeNodes(gn->cohorts, pgn);
621} 619}
622 620
623/* Initialize this module and start making. 621/* Initialize this module and start making.
624 * 622 *
625 * Input: 623 * Input:
626 * targs The target nodes to re-create 624 * targs The target nodes to re-create
627 */ 625 */
628void 626void
629Compat_Run(GNodeList *targs) 627Compat_Run(GNodeList *targs)
630{ 628{
631 GNode *gn = NULL; /* Current root target */ 629 GNode *gn = NULL; /* Current root target */
632 int errors; /* Number of targets not remade due to errors */ 630 int errors; /* Number of targets not remade due to errors */
633 631
634 if (!shellName) 632 if (!shellName)
635 Shell_Init(); 633 Shell_Init();
636 634
637 if (bmake_signal(SIGINT, SIG_IGN) != SIG_IGN) 635 if (bmake_signal(SIGINT, SIG_IGN) != SIG_IGN)
638 bmake_signal(SIGINT, CompatInterrupt); 636 bmake_signal(SIGINT, CompatInterrupt);
639 if (bmake_signal(SIGTERM, SIG_IGN) != SIG_IGN) 637 if (bmake_signal(SIGTERM, SIG_IGN) != SIG_IGN)
640 bmake_signal(SIGTERM, CompatInterrupt); 638 bmake_signal(SIGTERM, CompatInterrupt);
641 if (bmake_signal(SIGHUP, SIG_IGN) != SIG_IGN) 639 if (bmake_signal(SIGHUP, SIG_IGN) != SIG_IGN)
642 bmake_signal(SIGHUP, CompatInterrupt); 640 bmake_signal(SIGHUP, CompatInterrupt);
643 if (bmake_signal(SIGQUIT, SIG_IGN) != SIG_IGN) 641 if (bmake_signal(SIGQUIT, SIG_IGN) != SIG_IGN)
644 bmake_signal(SIGQUIT, CompatInterrupt); 642 bmake_signal(SIGQUIT, CompatInterrupt);
645 643
646 /* Create the .END node now, to keep the (debug) output of the 644 /* Create the .END node now, to keep the (debug) output of the
647 * counter.mk test the same as before 2020-09-23. This implementation 645 * counter.mk test the same as before 2020-09-23. This implementation
648 * detail probably doesn't matter though. */ 646 * detail probably doesn't matter though. */
649 (void)Targ_GetEndNode(); 647 (void)Targ_GetEndNode();
650 /* 648 /*
651 * If the user has defined a .BEGIN target, execute the commands attached 649 * If the user has defined a .BEGIN target, execute the commands attached
652 * to it. 650 * to it.
653 */ 651 */
654 if (!opts.queryFlag) { 652 if (!opts.queryFlag) {
655 gn = Targ_FindNode(".BEGIN"); 653 gn = Targ_FindNode(".BEGIN");
656 if (gn != NULL) { 654 if (gn != NULL) {
657 Compat_Make(gn, gn); 655 Compat_Make(gn, gn);
658 if (gn->made == ERROR) { 656 if (gn->made == ERROR) {
659 PrintOnError(gn, "\nStop."); 657 PrintOnError(gn, "\nStop.");
660 exit(1); 658 exit(1);
661 } 659 }
662 } 660 }
663 } 661 }
664 662
665 /* 663 /*
666 * Expand .USE nodes right now, because they can modify the structure 664 * Expand .USE nodes right now, because they can modify the structure
667 * of the tree. 665 * of the tree.
668 */ 666 */
669 Make_ExpandUse(targs); 667 Make_ExpandUse(targs);
670 668
671 /* 669 /*
672 * For each entry in the list of targets to create, call Compat_Make on 670 * For each entry in the list of targets to create, call Compat_Make on
673 * it to create the thing. Compat_Make will leave the 'made' field of gn 671 * it to create the thing. Compat_Make will leave the 'made' field of gn
674 * in one of several states: 672 * in one of several states:
675 * UPTODATE gn was already up-to-date 673 * UPTODATE gn was already up-to-date
676 * MADE gn was recreated successfully 674 * MADE gn was recreated successfully
677 * ERROR An error occurred while gn was being created 675 * ERROR An error occurred while gn was being created
678 * ABORTED gn was not remade because one of its inferiors 676 * ABORTED gn was not remade because one of its inferiors
679 * could not be made due to errors. 677 * could not be made due to errors.
680 */ 678 */
681 errors = 0; 679 errors = 0;
682 while (!Lst_IsEmpty(targs)) { 680 while (!Lst_IsEmpty(targs)) {
683 gn = Lst_Dequeue(targs); 681 gn = Lst_Dequeue(targs);
684 Compat_Make(gn, gn); 682 Compat_Make(gn, gn);
685 683
686 if (gn->made == UPTODATE) { 684 if (gn->made == UPTODATE) {
687 printf("`%s' is up to date.\n", gn->name); 685 printf("`%s' is up to date.\n", gn->name);
688 } else if (gn->made == ABORTED) { 686 } else if (gn->made == ABORTED) {
689 printf("`%s' not remade because of errors.\n", gn->name); 687 printf("`%s' not remade because of errors.\n", gn->name);
690 errors++; 688 errors++;
691 } 689 }
692 } 690 }
693 691
694 /* 692 /*
695 * If the user has defined a .END target, run its commands. 693 * If the user has defined a .END target, run its commands.
696 */ 694 */
697 if (errors == 0) { 695 if (errors == 0) {
698 GNode *endNode = Targ_GetEndNode(); 696 GNode *endNode = Targ_GetEndNode();
699 Compat_Make(endNode, endNode); 697 Compat_Make(endNode, endNode);
700 /* XXX: Did you mean endNode->made instead of gn->made? */ 698 /* XXX: Did you mean endNode->made instead of gn->made? */
701 if (gn->made == ERROR) { 699 if (gn->made == ERROR) {
702 PrintOnError(gn, "\nStop."); 700 PrintOnError(gn, "\nStop.");
703 exit(1); 701 exit(1);
704 } 702 }
705 } 703 }
706} 704}