summaryrefslogtreecommitdiffstats
path: root/src/kmk/job.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/kmk/job.c')
-rw-r--r--src/kmk/job.c3991
1 files changed, 3991 insertions, 0 deletions
diff --git a/src/kmk/job.c b/src/kmk/job.c
new file mode 100644
index 0000000..12d2ad3
--- /dev/null
+++ b/src/kmk/job.c
@@ -0,0 +1,3991 @@
+/* Job execution and handling for GNU Make.
+Copyright (C) 1988-2016 Free Software Foundation, Inc.
+This file is part of GNU Make.
+
+GNU Make is free software; you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation; either version 3 of the License, or (at your option) any later
+version.
+
+GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "makeint.h"
+
+#include <assert.h>
+
+#include "job.h"
+#include "debug.h"
+#include "filedef.h"
+#include "commands.h"
+#include "variable.h"
+#include "os.h"
+#ifdef CONFIG_WITH_KMK_BUILTIN
+# include "kmkbuiltin.h"
+#endif
+#ifdef KMK
+# include "kbuild.h"
+#endif
+
+
+#include <string.h>
+
+/* Default shell to use. */
+#ifdef WINDOWS32
+#include <windows.h>
+
+const char *default_shell = "sh.exe";
+int no_default_sh_exe = 1;
+int batch_mode_shell = 1;
+# ifndef CONFIG_NEW_WIN32_CTRL_EVENT
+HANDLE main_thread;
+# endif
+
+#elif defined (_AMIGA)
+
+const char *default_shell = "";
+extern int MyExecute (char **);
+int batch_mode_shell = 0;
+
+#elif defined (__MSDOS__)
+
+/* The default shell is a pointer so we can change it if Makefile
+ says so. It is without an explicit path so we get a chance
+ to search the $PATH for it (since MSDOS doesn't have standard
+ directories we could trust). */
+const char *default_shell = "command.com";
+int batch_mode_shell = 0;
+
+#elif defined (__EMX__)
+
+const char *default_shell = "sh.exe"; /* bird changed this from "/bin/sh" as that doesn't make sense on OS/2. */
+int batch_mode_shell = 0;
+
+#elif defined (VMS)
+
+# include <descrip.h>
+# include <stsdef.h>
+const char *default_shell = "";
+int batch_mode_shell = 0;
+
+#define strsignal vms_strsignal
+char * vms_strsignal (int status);
+
+#ifndef C_FACILITY_NO
+# define C_FACILITY_NO 0x350000
+#endif
+#ifndef VMS_POSIX_EXIT_MASK
+# define VMS_POSIX_EXIT_MASK (C_FACILITY_NO | 0xA000)
+#endif
+
+#else
+
+const char *default_shell = "/bin/sh";
+int batch_mode_shell = 0;
+
+#endif
+
+#ifdef __MSDOS__
+# include <process.h>
+static int execute_by_shell;
+static int dos_pid = 123;
+int dos_status;
+int dos_command_running;
+#endif /* __MSDOS__ */
+
+#ifdef _AMIGA
+# include <proto/dos.h>
+static int amiga_pid = 123;
+static int amiga_status;
+static char amiga_bname[32];
+static int amiga_batch_file;
+#endif /* Amiga. */
+
+#ifdef VMS
+# ifndef __GNUC__
+# include <processes.h>
+# endif
+# include <starlet.h>
+# include <lib$routines.h>
+static void vmsWaitForChildren (int *);
+#endif
+
+#ifdef WINDOWS32
+# include <windows.h>
+# include <io.h>
+# include <process.h>
+# ifdef CONFIG_NEW_WIN_CHILDREN
+# include "w32/winchildren.h"
+# else
+# include "sub_proc.h"
+# endif
+# include "w32err.h"
+# include "pathstuff.h"
+# define WAIT_NOHANG 1
+#endif /* WINDOWS32 */
+
+#ifdef __EMX__
+# include <process.h>
+#endif
+
+#if defined (HAVE_SYS_WAIT_H) || defined (HAVE_UNION_WAIT)
+# include <sys/wait.h>
+#endif
+
+#ifdef HAVE_WAITPID
+# define WAIT_NOHANG(status) waitpid (-1, (status), WNOHANG)
+#else /* Don't have waitpid. */
+# ifdef HAVE_WAIT3
+# ifndef wait3
+extern int wait3 ();
+# endif
+# define WAIT_NOHANG(status) wait3 ((status), WNOHANG, (struct rusage *) 0)
+# endif /* Have wait3. */
+#endif /* Have waitpid. */
+
+#if !defined (wait) && !defined (POSIX)
+int wait ();
+#endif
+
+#ifndef HAVE_UNION_WAIT
+
+# define WAIT_T int
+
+# ifndef WTERMSIG
+# define WTERMSIG(x) ((x) & 0x7f)
+# endif
+# ifndef WCOREDUMP
+# define WCOREDUMP(x) ((x) & 0x80)
+# endif
+# ifndef WEXITSTATUS
+# define WEXITSTATUS(x) (((x) >> 8) & 0xff)
+# endif
+# ifndef WIFSIGNALED
+# define WIFSIGNALED(x) (WTERMSIG (x) != 0)
+# endif
+# ifndef WIFEXITED
+# define WIFEXITED(x) (WTERMSIG (x) == 0)
+# endif
+
+#else /* Have 'union wait'. */
+
+# define WAIT_T union wait
+# ifndef WTERMSIG
+# define WTERMSIG(x) ((x).w_termsig)
+# endif
+# ifndef WCOREDUMP
+# define WCOREDUMP(x) ((x).w_coredump)
+# endif
+# ifndef WEXITSTATUS
+# define WEXITSTATUS(x) ((x).w_retcode)
+# endif
+# ifndef WIFSIGNALED
+# define WIFSIGNALED(x) (WTERMSIG(x) != 0)
+# endif
+# ifndef WIFEXITED
+# define WIFEXITED(x) (WTERMSIG(x) == 0)
+# endif
+
+#endif /* Don't have 'union wait'. */
+
+#if !defined(HAVE_UNISTD_H) && !defined(WINDOWS32)
+# ifndef _MSC_VER /* bird */
+int dup2 ();
+int execve ();
+void _exit ();
+# endif /* bird */
+# ifndef VMS
+int geteuid ();
+int getegid ();
+int setgid ();
+int getgid ();
+# endif
+#endif
+
+/* Different systems have different requirements for pid_t.
+ Plus we have to support gettext string translation... Argh. */
+static const char *
+pid2str (pid_t pid)
+{
+ static char pidstring[100];
+#if defined(WINDOWS32) && (__GNUC__ > 3 || _MSC_VER > 1300)
+ /* %Id is only needed for 64-builds, which were not supported by
+ older versions of Windows compilers. */
+ sprintf (pidstring, "%Id", pid);
+#else
+ sprintf (pidstring, "%lu", (unsigned long) pid);
+#endif
+ return pidstring;
+}
+
+#ifndef HAVE_GETLOADAVG
+int getloadavg (double loadavg[], int nelem);
+#endif
+
+static void free_child (struct child *);
+static void start_job_command (struct child *child);
+static int load_too_high (void);
+static int job_next_command (struct child *);
+static int start_waiting_job (struct child *);
+#ifdef CONFIG_WITH_PRINT_TIME_SWITCH
+static void print_job_time (struct child *);
+#endif
+
+/* Chain of all live (or recently deceased) children. */
+
+struct child *children = 0;
+
+/* Number of children currently running. */
+
+unsigned int job_slots_used = 0;
+
+/* Nonzero if the 'good' standard input is in use. */
+
+static int good_stdin_used = 0;
+
+/* Chain of children waiting to run until the load average goes down. */
+
+static struct child *waiting_jobs = 0;
+
+/* Non-zero if we use a *real* shell (always so on Unix). */
+
+int unixy_shell = 1;
+
+/* Number of jobs started in the current second. */
+
+unsigned long job_counter = 0;
+
+/* Number of jobserver tokens this instance is currently using. */
+
+unsigned int jobserver_tokens = 0;
+
+
+#ifdef WINDOWS32
+# ifndef CONFIG_NEW_WIN_CHILDREN /* (only used by commands.c) */
+/*
+ * The macro which references this function is defined in makeint.h.
+ */
+int
+w32_kill (pid_t pid, int sig)
+{
+ return ((process_kill ((HANDLE)pid, sig) == TRUE) ? 0 : -1);
+}
+# endif /* !CONFIG_NEW_WIN_CHILDREN */
+
+/* This function creates a temporary file name with an extension specified
+ * by the unixy arg.
+ * Return an xmalloc'ed string of a newly created temp file and its
+ * file descriptor, or die. */
+static char *
+create_batch_file (char const *base, int unixy, int *fd)
+{
+ const char *const ext = unixy ? "sh" : "bat";
+ const char *error_string = NULL;
+ char temp_path[MAXPATHLEN]; /* need to know its length */
+ unsigned path_size = GetTempPath (sizeof temp_path, temp_path);
+ int path_is_dot = 0;
+ /* The following variable is static so we won't try to reuse a name
+ that was generated a little while ago, because that file might
+ not be on disk yet, since we use FILE_ATTRIBUTE_TEMPORARY below,
+ which tells the OS it doesn't need to flush the cache to disk.
+ If the file is not yet on disk, we might think the name is
+ available, while it really isn't. This happens in parallel
+ builds, where Make doesn't wait for one job to finish before it
+ launches the next one. */
+ static unsigned uniq = 0;
+ static int second_loop = 0;
+ const unsigned sizemax = strlen (base) + strlen (ext) + 10;
+
+ if (path_size == 0)
+ {
+ path_size = GetCurrentDirectory (sizeof temp_path, temp_path);
+ path_is_dot = 1;
+ }
+
+ ++uniq;
+ if (uniq >= 0x10000 && !second_loop)
+ {
+ /* If we already had 64K batch files in this
+ process, make a second loop through the numbers,
+ looking for free slots, i.e. files that were
+ deleted in the meantime. */
+ second_loop = 1;
+ uniq = 1;
+ }
+ while (path_size > 0 &&
+ path_size + sizemax < sizeof temp_path &&
+ !(uniq >= 0x10000 && second_loop))
+ {
+ unsigned size = sprintf (temp_path + path_size,
+ "%s%s-%x.%s",
+ temp_path[path_size - 1] == '\\' ? "" : "\\",
+ base, uniq, ext);
+ HANDLE h = CreateFile (temp_path, /* file name */
+ GENERIC_READ | GENERIC_WRITE, /* desired access */
+ 0, /* no share mode */
+ NULL, /* default security attributes */
+ CREATE_NEW, /* creation disposition */
+ FILE_ATTRIBUTE_NORMAL | /* flags and attributes */
+ FILE_ATTRIBUTE_TEMPORARY, /* we'll delete it */
+ NULL); /* no template file */
+
+ if (h == INVALID_HANDLE_VALUE)
+ {
+ const DWORD er = GetLastError ();
+
+ if (er == ERROR_FILE_EXISTS || er == ERROR_ALREADY_EXISTS)
+ {
+ ++uniq;
+ if (uniq == 0x10000 && !second_loop)
+ {
+ second_loop = 1;
+ uniq = 1;
+ }
+ }
+
+ /* the temporary path is not guaranteed to exist */
+ else if (path_is_dot == 0)
+ {
+ path_size = GetCurrentDirectory (sizeof temp_path, temp_path);
+ path_is_dot = 1;
+ }
+
+ else
+ {
+ error_string = map_windows32_error_to_string (er);
+ break;
+ }
+ }
+ else
+ {
+ const unsigned final_size = path_size + size + 1;
+ char *const path = xmalloc (final_size);
+ memcpy (path, temp_path, final_size);
+ *fd = _open_osfhandle ((intptr_t)h, 0);
+ if (unixy)
+ {
+ char *p;
+ int ch;
+ for (p = path; (ch = *p) != 0; ++p)
+ if (ch == '\\')
+ *p = '/';
+ }
+ return path; /* good return */
+ }
+ }
+
+ *fd = -1;
+ if (error_string == NULL)
+ error_string = _("Cannot create a temporary file\n");
+ O (fatal, NILF, error_string);
+
+ /* not reached */
+ return NULL;
+}
+#endif /* WINDOWS32 */
+
+#ifdef __EMX__
+/* returns whether path is assumed to be a unix like shell. */
+int
+_is_unixy_shell (const char *path)
+{
+ /* list of non unix shells */
+ const char *known_os2shells[] = {
+ "cmd.exe",
+ "cmd",
+ "4os2.exe",
+ "4os2",
+ "4dos.exe",
+ "4dos",
+ "command.com",
+ "command",
+ NULL
+ };
+
+ /* find the rightmost '/' or '\\' */
+ const char *name = strrchr (path, '/');
+ const char *p = strrchr (path, '\\');
+ unsigned i;
+
+ if (name && p) /* take the max */
+ name = (name > p) ? name : p;
+ else if (p) /* name must be 0 */
+ name = p;
+ else if (!name) /* name and p must be 0 */
+ name = path;
+
+ if (*name == '/' || *name == '\\') name++;
+
+ i = 0;
+ while (known_os2shells[i] != NULL)
+ {
+ if (strcasecmp (name, known_os2shells[i]) == 0)
+ return 0; /* not a unix shell */
+ i++;
+ }
+
+ /* in doubt assume a unix like shell */
+ return 1;
+}
+#endif /* __EMX__ */
+
+/* determines whether path looks to be a Bourne-like shell. */
+int
+is_bourne_compatible_shell (const char *path)
+{
+ /* List of known POSIX (or POSIX-ish) shells. */
+ static const char *unix_shells[] = {
+ "sh",
+ "bash",
+ "ksh",
+ "rksh",
+ "zsh",
+ "ash",
+ "dash",
+ NULL
+ };
+ const char **s;
+
+ /* find the rightmost '/' or '\\' */
+ const char *name = strrchr (path, '/');
+ char *p = strrchr (path, '\\');
+
+ if (name && p) /* take the max */
+ name = (name > p) ? name : p;
+ else if (p) /* name must be 0 */
+ name = p;
+ else if (!name) /* name and p must be 0 */
+ name = path;
+
+ if (*name == '/' || *name == '\\')
+ ++name;
+
+ /* this should be able to deal with extensions on Windows-like systems */
+ for (s = unix_shells; *s != NULL; ++s)
+ {
+#if defined(WINDOWS32) || defined(__MSDOS__)
+ unsigned int len = strlen (*s);
+ if ((strlen (name) >= len && STOP_SET (name[len], MAP_DOT|MAP_NUL))
+ && strncasecmp (name, *s, len) == 0)
+#else
+ if (strcmp (name, *s) == 0)
+#endif
+ return 1; /* a known unix-style shell */
+ }
+
+ /* if not on the list, assume it's not a Bourne-like shell */
+ return 0;
+}
+
+
+/* Write an error message describing the exit status given in
+ EXIT_CODE, EXIT_SIG, and COREDUMP, for the target TARGET_NAME.
+ Append "(ignored)" if IGNORED is nonzero. */
+
+static void
+child_error (struct child *child,
+ int exit_code, int exit_sig, int coredump, int ignored)
+{
+ const char *pre = "*** ";
+ const char *post = "";
+ const char *dump = "";
+ const struct file *f = child->file;
+ const floc *flocp = &f->cmds->fileinfo;
+ const char *nm;
+ size_t l;
+
+ if (ignored && silent_flag)
+ return;
+
+ if (exit_sig && coredump)
+ dump = _(" (core dumped)");
+
+ if (ignored)
+ {
+ pre = "";
+ post = _(" (ignored)");
+ }
+
+ if (! flocp->filenm)
+ nm = _("<builtin>");
+ else
+ {
+ char *a = alloca (strlen (flocp->filenm) + 1 + 11 + 1);
+ sprintf (a, "%s:%lu", flocp->filenm, flocp->lineno + flocp->offset);
+ nm = a;
+ }
+
+ l = strlen (pre) + strlen (nm) + strlen (f->name) + strlen (post);
+
+ OUTPUT_SET (&child->output);
+
+ show_goal_error ();
+
+ if (exit_sig == 0)
+# if defined(KMK) && defined(KBUILD_OS_WINDOWS)
+ {
+ const char *exit_name = NULL;
+ switch ((unsigned)exit_code)
+ {
+ case 0xc0000005U: exit_name = "STATUS_ACCESS_VIOLATION"; break;
+ case 0xc000013aU: exit_name = "STATUS_CONTROL_C_EXIT"; break;
+ case 0xc0000374U: exit_name = "STATUS_HEAP_CORRUPTION"; break;
+ case 0xc0000409U: exit_name = "STATUS_STACK_BUFFER_OVERRUN"; break;
+ case 0xc0000417U: exit_name = "STATUS_INVALID_CRUNTIME_PARAMETER"; break;
+ case 0x80000003U: exit_name = "STATUS_BREAKPOINT"; break;
+ case 0x40000015U: exit_name = "STATUS_FATAL_APP_EXIT"; break;
+ case 0x40010004U: exit_name = "DBG_TERMINATE_PROCESS"; break;
+ case 0x40010005U: exit_name = "DBG_CONTROL_C"; break;
+ case 0x40010008U: exit_name = "DBG_CONTROL_BREAK"; break;
+ }
+ if (exit_name)
+ error (NILF, l + strlen (exit_name) + INTSTR_LENGTH,
+ _("%s[%s: %s] Error %d (%s)%s"),
+ pre, nm, f->name, exit_code, exit_name, post);
+ else
+ error (NILF, l + INTSTR_LENGTH + INTSTR_LENGTH,
+ _("%s[%s: %s] Error %d (%#x)%s"),
+ pre, nm, f->name, exit_code, exit_code, post);
+ }
+# else
+ error (NILF, l + INTSTR_LENGTH,
+ _("%s[%s: %s] Error %d%s"), pre, nm, f->name, exit_code, post);
+# endif
+ else
+ {
+ const char *s = strsignal (exit_sig);
+ error (NILF, l + strlen (s) + strlen (dump),
+ "%s[%s: %s] %s%s%s", pre, nm, f->name, s, dump, post);
+ }
+
+ OUTPUT_UNSET ();
+}
+
+
+/* Handle a dead child. This handler may or may not ever be installed.
+
+ If we're using the jobserver feature without pselect(), we need it.
+ First, installing it ensures the read will interrupt on SIGCHLD. Second,
+ we close the dup'd read FD to ensure we don't enter another blocking read
+ without reaping all the dead children. In this case we don't need the
+ dead_children count.
+
+ If we don't have either waitpid or wait3, then make is unreliable, but we
+ use the dead_children count to reap children as best we can. */
+
+static unsigned int dead_children = 0;
+
+RETSIGTYPE
+child_handler (int sig UNUSED)
+{
+ ++dead_children;
+
+ jobserver_signal ();
+
+#if defined __EMX__ && !defined(__INNOTEK_LIBC__) /* bird */
+ /* The signal handler must called only once! */
+ signal (SIGCHLD, SIG_DFL);
+#endif
+}
+
+extern pid_t shell_function_pid;
+
+/* Reap all dead children, storing the returned status and the new command
+ state ('cs_finished') in the 'file' member of the 'struct child' for the
+ dead child, and removing the child from the chain. In addition, if BLOCK
+ nonzero, we block in this function until we've reaped at least one
+ complete child, waiting for it to die if necessary. If ERR is nonzero,
+ print an error message first. */
+
+void
+reap_children (int block, int err)
+{
+#ifndef WINDOWS32
+ WAIT_T status;
+#endif
+ /* Initially, assume we have some. */
+ int reap_more = 1;
+
+#ifdef WAIT_NOHANG
+# define REAP_MORE reap_more
+#else
+# define REAP_MORE dead_children
+#endif
+
+ /* As long as:
+
+ We have at least one child outstanding OR a shell function in progress,
+ AND
+ We're blocking for a complete child OR there are more children to reap
+
+ we'll keep reaping children. */
+
+ while ((children != 0 || shell_function_pid != 0)
+ && (block || REAP_MORE))
+ {
+ unsigned int remote = 0;
+ pid_t pid;
+ int exit_code, exit_sig, coredump;
+ struct child *lastc, *c;
+ int child_failed;
+ int any_remote, any_local;
+ int dontcare;
+#ifdef CONFIG_WITH_KMK_BUILTIN
+ struct child *completed_child = NULL;
+#endif
+
+ if (err && block)
+ {
+ static int printed = 0;
+
+ /* We might block for a while, so let the user know why.
+ Only print this message once no matter how many jobs are left. */
+ fflush (stdout);
+ if (!printed)
+ O (error, NILF, _("*** Waiting for unfinished jobs...."));
+ printed = 1;
+ }
+
+ /* We have one less dead child to reap. As noted in
+ child_handler() above, this count is completely unimportant for
+ all modern, POSIX-y systems that support wait3() or waitpid().
+ The rest of this comment below applies only to early, broken
+ pre-POSIX systems. We keep the count only because... it's there...
+
+ The test and decrement are not atomic; if it is compiled into:
+ register = dead_children - 1;
+ dead_children = register;
+ a SIGCHLD could come between the two instructions.
+ child_handler increments dead_children.
+ The second instruction here would lose that increment. But the
+ only effect of dead_children being wrong is that we might wait
+ longer than necessary to reap a child, and lose some parallelism;
+ and we might print the "Waiting for unfinished jobs" message above
+ when not necessary. */
+
+ if (dead_children > 0)
+ --dead_children;
+
+ any_remote = 0;
+ any_local = shell_function_pid != 0;
+ for (c = children; c != 0; c = c->next)
+ {
+ any_remote |= c->remote;
+ any_local |= ! c->remote;
+#ifdef CONFIG_WITH_KMK_BUILTIN
+ if (c->has_status)
+ {
+ completed_child = c;
+ DB (DB_JOBS, (_("builtin child %p (%s) PID %s %s Status %ld\n"),
+ (void *)c, c->file->name,
+ pid2str (c->pid), c->remote ? _(" (remote)") : "",
+ (long) c->status));
+ }
+ else
+#endif
+ DB (DB_JOBS, (_("Live child %p (%s) PID %s %s\n"),
+ (void *)c, c->file->name, pid2str (c->pid),
+ c->remote ? _(" (remote)") : ""));
+#ifdef VMS
+ break;
+#endif
+ }
+
+ /* First, check for remote children. */
+ if (any_remote)
+ pid = remote_status (&exit_code, &exit_sig, &coredump, 0);
+ else
+ pid = 0;
+
+ if (pid > 0)
+ /* We got a remote child. */
+ remote = 1;
+ else if (pid < 0)
+ {
+ /* A remote status command failed miserably. Punt. */
+ remote_status_lose:
+ pfatal_with_name ("remote_status");
+ }
+ else
+ {
+ /* No remote children. Check for local children. */
+#ifdef CONFIG_WITH_KMK_BUILTIN
+ if (completed_child)
+ {
+ pid = completed_child->pid;
+# if defined(WINDOWS32)
+ exit_code = completed_child->status;
+ exit_sig = 0;
+ coredump = 0;
+# else
+ status = (WAIT_T)completed_child->status;
+# endif
+ }
+ else
+#endif /* CONFIG_WITH_KMK_BUILTIN */
+#if !defined(__MSDOS__) && !defined(_AMIGA) && !defined(WINDOWS32)
+ if (any_local)
+ {
+#ifdef VMS
+ /* Todo: This needs more untangling multi-process support */
+ /* Just do single child process support now */
+ vmsWaitForChildren (&status);
+ pid = c->pid;
+
+ /* VMS failure status can not be fully translated */
+ status = $VMS_STATUS_SUCCESS (c->cstatus) ? 0 : (1 << 8);
+
+ /* A Posix failure can be exactly translated */
+ if ((c->cstatus & VMS_POSIX_EXIT_MASK) == VMS_POSIX_EXIT_MASK)
+ status = (c->cstatus >> 3 & 255) << 8;
+#else
+#ifdef WAIT_NOHANG
+ if (!block)
+ pid = WAIT_NOHANG (&status);
+ else
+#endif
+ EINTRLOOP (pid, wait (&status));
+#endif /* !VMS */
+ }
+ else
+ pid = 0;
+
+ if (pid < 0)
+ {
+ /* The wait*() failed miserably. Punt. */
+ pfatal_with_name ("wait");
+ }
+ else if (pid > 0)
+ {
+ /* We got a child exit; chop the status word up. */
+ exit_code = WEXITSTATUS (status);
+ exit_sig = WIFSIGNALED (status) ? WTERMSIG (status) : 0;
+ coredump = WCOREDUMP (status);
+
+ /* If we have started jobs in this second, remove one. */
+ if (job_counter)
+ --job_counter;
+ }
+ else
+ {
+ /* No local children are dead. */
+ reap_more = 0;
+
+ if (!block || !any_remote)
+ break;
+
+ /* Now try a blocking wait for a remote child. */
+ pid = remote_status (&exit_code, &exit_sig, &coredump, 1);
+ if (pid < 0)
+ goto remote_status_lose;
+ else if (pid == 0)
+ /* No remote children either. Finally give up. */
+ break;
+
+ /* We got a remote child. */
+ remote = 1;
+ }
+#endif /* !__MSDOS__, !Amiga, !WINDOWS32. */
+
+#ifdef __MSDOS__
+ /* Life is very different on MSDOS. */
+ pid = dos_pid - 1;
+ status = dos_status;
+ exit_code = WEXITSTATUS (status);
+ if (exit_code == 0xff)
+ exit_code = -1;
+ exit_sig = WIFSIGNALED (status) ? WTERMSIG (status) : 0;
+ coredump = 0;
+#endif /* __MSDOS__ */
+#ifdef _AMIGA
+ /* Same on Amiga */
+ pid = amiga_pid - 1;
+ status = amiga_status;
+ exit_code = amiga_status;
+ exit_sig = 0;
+ coredump = 0;
+#endif /* _AMIGA */
+#ifdef WINDOWS32
+ {
+# ifndef CONFIG_NEW_WIN_CHILDREN
+ HANDLE hPID;
+ HANDLE hcTID, hcPID;
+ DWORD dwWaitStatus = 0;
+ exit_code = 0;
+ exit_sig = 0;
+ coredump = 0;
+
+# ifndef CONFIG_NEW_WIN32_CTRL_EVENT
+ /* Record the thread ID of the main process, so that we
+ could suspend it in the signal handler. */
+ if (!main_thread)
+ {
+ hcTID = GetCurrentThread ();
+ hcPID = GetCurrentProcess ();
+ if (!DuplicateHandle (hcPID, hcTID, hcPID, &main_thread, 0,
+ FALSE, DUPLICATE_SAME_ACCESS))
+ {
+ DWORD e = GetLastError ();
+ fprintf (stderr,
+ "Determine main thread ID (Error %ld: %s)\n",
+ e, map_windows32_error_to_string (e));
+ }
+ else
+ DB (DB_VERBOSE, ("Main thread handle = %p\n", main_thread));
+ }
+# endif
+
+ /* wait for anything to finish */
+ hPID = process_wait_for_any (block, &dwWaitStatus);
+ if (hPID)
+ {
+ /* was an error found on this process? */
+ int werr = process_last_err (hPID);
+
+ /* get exit data */
+ exit_code = process_exit_code (hPID);
+
+ if (werr)
+ fprintf (stderr, "make (e=%d): %s", exit_code,
+ map_windows32_error_to_string (exit_code));
+
+ /* signal */
+ exit_sig = process_signal (hPID);
+
+ /* cleanup process */
+ process_cleanup (hPID);
+
+ coredump = 0;
+ }
+ else if (dwWaitStatus == WAIT_FAILED)
+ {
+ /* The WaitForMultipleObjects() failed miserably. Punt. */
+ pfatal_with_name ("WaitForMultipleObjects");
+ }
+ else if (dwWaitStatus == WAIT_TIMEOUT)
+ {
+ /* No child processes are finished. Give up waiting. */
+ reap_more = 0;
+ break;
+ }
+
+ pid = (pid_t) hPID;
+# else /* CONFIG_NEW_WIN_CHILDREN */
+# ifndef CONFIG_NEW_WIN32_CTRL_EVENT
+ /* Ctrl-C handler needs to suspend the main thread handle to
+ prevent mayhem when concurrently calling reap_children. */
+ if ( !main_thread
+ && !DuplicateHandle (GetCurrentProcess (), GetCurrentThread (),
+ GetCurrentProcess (), &main_thread, 0,
+ FALSE, DUPLICATE_SAME_ACCESS))
+ fprintf (stderr, "Failed to duplicate main thread handle: %u\n",
+ GetLastError ());
+# endif
+
+ assert (!any_remote);
+ pid = 0;
+ coredump = exit_sig = exit_code = 0;
+ {
+ int rc = MkWinChildWait(block, &pid, &exit_code, &exit_sig, &coredump, &c);
+ if (rc != 0)
+ ON (fatal, NILF, _("MkWinChildWait: %u"), rc);
+ }
+ if (pid == 0)
+ {
+ /* No more children, stop. */
+ reap_more = 0;
+ break;
+ }
+
+ /* If we have started jobs in this second, remove one. */
+ if (job_counter)
+ --job_counter;
+# endif /* CONFIG_NEW_WIN_CHILDREN */
+ }
+#endif /* WINDOWS32 */
+ }
+
+ /* Check if this is the child of the 'shell' function. */
+ if (!remote && pid == shell_function_pid)
+ {
+ shell_completed (exit_code, exit_sig);
+ break;
+ }
+
+ /* Search for a child matching the deceased one. */
+ lastc = 0;
+ for (c = children; c != 0; lastc = c, c = c->next)
+ if (c->pid == pid && c->remote == remote)
+ break;
+
+ if (c == 0)
+ /* An unknown child died.
+ Ignore it; it was inherited from our invoker. */
+ continue;
+
+ /* Determine the failure status: 0 for success, 1 for updating target in
+ question mode, 2 for anything else. */
+ if (exit_sig == 0 && exit_code == 0)
+ child_failed = MAKE_SUCCESS;
+ else if (exit_sig == 0 && exit_code == 1 && question_flag && c->recursive)
+ child_failed = MAKE_TROUBLE;
+ else
+ child_failed = MAKE_FAILURE;
+
+ DB (DB_JOBS, (child_failed
+ ? _("Reaping losing child %p PID %s %s\n")
+ : _("Reaping winning child %p PID %s %s\n"),
+ (void *)c, pid2str (c->pid), c->remote ? _(" (remote)") : ""));
+
+ if (c->sh_batch_file)
+ {
+ int rm_status;
+
+ DB (DB_JOBS, (_("Cleaning up temp batch file %s\n"),
+ c->sh_batch_file));
+
+ errno = 0;
+ rm_status = remove (c->sh_batch_file);
+ if (rm_status)
+ DB (DB_JOBS, (_("Cleaning up temp batch file %s failed (%d)\n"),
+ c->sh_batch_file, errno));
+
+ /* all done with memory */
+ free (c->sh_batch_file);
+ c->sh_batch_file = NULL;
+ }
+
+ /* If this child had the good stdin, say it is now free. */
+ if (c->good_stdin)
+ good_stdin_used = 0;
+
+ dontcare = c->dontcare;
+
+ if (child_failed && !c->noerror && !ignore_errors_flag)
+ {
+ /* The commands failed. Write an error message,
+ delete non-precious targets, and abort. */
+ static int delete_on_error = -1;
+
+ if (!dontcare && child_failed == MAKE_FAILURE)
+#ifdef KMK
+ {
+ child_error (c, exit_code, exit_sig, coredump, 0);
+ if ( ( c->file->cmds->lines_flags[c->command_line - 1]
+ & (COMMANDS_SILENT | COMMANDS_RECURSE))
+ == COMMANDS_SILENT
+# ifdef KBUILD_OS_WINDOWS /* show commands for NT statuses */
+ || (exit_code & 0xc0000000)
+# endif
+ || exit_sig != 0)
+ OS (message, 0, "The failing command:\n%s", c->file->cmds->command_lines[c->command_line - 1]);
+ }
+#else /* !KMK */
+ child_error (c, exit_code, exit_sig, coredump, 0);
+#endif /* !KMK */
+
+ c->file->update_status = child_failed == MAKE_FAILURE ? us_failed : us_question;
+ if (delete_on_error == -1)
+ {
+ struct file *f = lookup_file (".DELETE_ON_ERROR");
+ delete_on_error = f != 0 && f->is_target;
+ }
+ if (exit_sig != 0 || delete_on_error)
+ delete_child_targets (c);
+ }
+ else
+ {
+ if (child_failed)
+ {
+ /* The commands failed, but we don't care. */
+ child_error (c, exit_code, exit_sig, coredump, 1);
+ child_failed = 0;
+ }
+
+ /* If there are more commands to run, try to start them. */
+ if (job_next_command (c))
+ {
+ if (handling_fatal_signal)
+ {
+ /* Never start new commands while we are dying.
+ Since there are more commands that wanted to be run,
+ the target was not completely remade. So we treat
+ this as if a command had failed. */
+ c->file->update_status = us_failed;
+ }
+ else
+ {
+#ifndef NO_OUTPUT_SYNC
+ /* If we're sync'ing per line, write the previous line's
+ output before starting the next one. */
+ if (output_sync == OUTPUT_SYNC_LINE)
+ output_dump (&c->output);
+#endif
+ /* Check again whether to start remotely.
+ Whether or not we want to changes over time.
+ Also, start_remote_job may need state set up
+ by start_remote_job_p. */
+ c->remote = start_remote_job_p (0);
+ start_job_command (c);
+ /* Fatal signals are left blocked in case we were
+ about to put that child on the chain. But it is
+ already there, so it is safe for a fatal signal to
+ arrive now; it will clean up this child's targets. */
+ unblock_sigs ();
+ if (c->file->command_state == cs_running)
+ /* We successfully started the new command.
+ Loop to reap more children. */
+ continue;
+ }
+
+ if (c->file->update_status != us_success)
+ /* We failed to start the commands. */
+ delete_child_targets (c);
+ }
+ else
+ /* There are no more commands. We got through them all
+ without an unignored error. Now the target has been
+ successfully updated. */
+ c->file->update_status = us_success;
+ }
+
+ /* When we get here, all the commands for c->file are finished. */
+
+#ifndef NO_OUTPUT_SYNC
+ /* Synchronize any remaining parallel output. */
+# ifdef KMK
+ c->output.dont_truncate = !err && child_failed && !dontcare && !keep_going_flag && !handling_fatal_signal;
+# endif
+ output_dump (&c->output);
+#endif
+
+ /* At this point c->file->update_status is success or failed. But
+ c->file->command_state is still cs_running if all the commands
+ ran; notice_finish_file looks for cs_running to tell it that
+ it's interesting to check the file's modtime again now. */
+
+ if (! handling_fatal_signal)
+ /* Notice if the target of the commands has been changed.
+ This also propagates its values for command_state and
+ update_status to its also_make files. */
+ notice_finished_file (c->file);
+
+ DB (DB_JOBS, (_("Removing child %p PID %s%s from chain.\n"),
+ (void *)c, pid2str (c->pid), c->remote ? _(" (remote)") : ""));
+
+ /* Block fatal signals while frobnicating the list, so that
+ children and job_slots_used are always consistent. Otherwise
+ a fatal signal arriving after the child is off the chain and
+ before job_slots_used is decremented would believe a child was
+ live and call reap_children again. */
+ block_sigs ();
+
+ /* There is now another slot open. */
+ if (job_slots_used > 0)
+ --job_slots_used;
+
+ /* Remove the child from the chain and free it. */
+ if (lastc == 0)
+ children = c->next;
+ else
+ lastc->next = c->next;
+
+#ifdef KMK /* Repeat the error */
+ /* If the job failed, and the -k flag was not given, die,
+ unless we are already in the process of dying. */
+ if (!err && child_failed && !dontcare && !keep_going_flag &&
+ /* fatal_error_signal will die with the right signal. */
+ !handling_fatal_signal)
+ {
+ unblock_sigs ();
+ die_with_job_output (child_failed, &c->output);
+ }
+#endif
+
+ free_child (c);
+
+ unblock_sigs ();
+
+#ifndef KMK /* See above. */
+ /* If the job failed, and the -k flag was not given, die,
+ unless we are already in the process of dying. */
+ if (!err && child_failed && !dontcare && !keep_going_flag &&
+ /* fatal_error_signal will die with the right signal. */
+ !handling_fatal_signal)
+ die (child_failed);
+#endif
+
+ /* Only block for one child. */
+ block = 0;
+ }
+
+ return;
+}
+
+/* Free the storage allocated for CHILD. */
+
+static void
+free_child (struct child *child)
+{
+#ifdef CONFIG_WITH_PRINT_TIME_SWITCH
+ print_job_time (child);
+#endif
+ output_close (&child->output);
+
+ /* bird: Make sure the output_context doesn't point to a freed structure when
+ we return from this function. This is probably an issue elsewhere
+ in the code, however it doesn't cost us much fixing it here. (The
+ access after free was caught in a die() scenario, both in error
+ situations and successful ones.) */
+ if (output_context == &child->output)
+ OUTPUT_UNSET();
+
+ if (!jobserver_tokens)
+ ONS (fatal, NILF, "INTERNAL: Freeing child %p (%s) but no tokens left!\n",
+ (void *)child, child->file->name);
+
+ /* If we're using the jobserver and this child is not the only outstanding
+ job, put a token back into the pipe for it. */
+
+ if (jobserver_enabled () && jobserver_tokens > 1)
+ {
+ jobserver_release (1);
+ DB (DB_JOBS, (_("Released token for child %p (%s).\n"),
+ (void *)child, child->file->name));
+ }
+
+ --jobserver_tokens;
+
+ if (handling_fatal_signal) /* Don't bother free'ing if about to die. */
+ return;
+
+ if (child->command_lines != 0)
+ {
+ register unsigned int i;
+ for (i = 0; i < child->file->cmds->ncommand_lines; ++i)
+ free (child->command_lines[i]);
+ free (child->command_lines);
+ }
+
+ if (child->environment != 0)
+ {
+ register char **ep = child->environment;
+ while (*ep != 0)
+ free (*ep++);
+ free (child->environment);
+ }
+
+#ifdef CONFIG_WITH_MEMORY_OPTIMIZATIONS
+ /* Free the chopped command lines for simple targets when
+ there are no more active references to them. */
+
+ child->file->cmds->refs--;
+ if ( !child->file->intermediate
+ && !child->file->pat_variables)
+ free_chopped_commands(child->file->cmds);
+#endif /* CONFIG_WITH_MEMORY_OPTIMIZATIONS */
+
+ free (child);
+}
+
+#ifdef POSIX
+extern sigset_t fatal_signal_set;
+#endif
+
+void
+block_sigs (void)
+{
+#ifdef POSIX
+ (void) sigprocmask (SIG_BLOCK, &fatal_signal_set, (sigset_t *) 0);
+#else
+# ifdef HAVE_SIGSETMASK
+ (void) sigblock (fatal_signal_mask);
+# endif
+#endif
+}
+
+#ifdef POSIX
+void
+unblock_sigs (void)
+{
+ sigset_t empty;
+ sigemptyset (&empty);
+ sigprocmask (SIG_SETMASK, &empty, (sigset_t *) 0);
+}
+#endif
+
+/* Start a job to run the commands specified in CHILD.
+ CHILD is updated to reflect the commands and ID of the child process.
+
+ NOTE: On return fatal signals are blocked! The caller is responsible
+ for calling 'unblock_sigs', once the new child is safely on the chain so
+ it can be cleaned up in the event of a fatal signal. */
+
+static void
+start_job_command (struct child *child)
+{
+ int flags;
+ char *p;
+#ifdef VMS
+ char *argv;
+#else
+ char **argv;
+#endif
+
+ /* If we have a completely empty commandset, stop now. */
+ if (!child->command_ptr)
+ goto next_command;
+
+#ifdef CONFIG_WITH_PRINT_TIME_SWITCH
+ if (child->start_ts == -1)
+ child->start_ts = nano_timestamp ();
+#endif
+
+ /* Combine the flags parsed for the line itself with
+ the flags specified globally for this target. */
+ flags = (child->file->command_flags
+ | child->file->cmds->lines_flags[child->command_line - 1]);
+
+ p = child->command_ptr;
+ child->noerror = ((flags & COMMANDS_NOERROR) != 0);
+
+ while (*p != '\0')
+ {
+ if (*p == '@')
+ flags |= COMMANDS_SILENT;
+ else if (*p == '+')
+ flags |= COMMANDS_RECURSE;
+ else if (*p == '-')
+ child->noerror = 1;
+#ifdef CONFIG_WITH_COMMANDS_FUNC
+ else if (*p == '%')
+ flags |= COMMAND_GETTER_SKIP_IT;
+#endif
+ /* Don't skip newlines. */
+ else if (!ISBLANK (*p))
+#ifndef CONFIG_WITH_KMK_BUILTIN
+ break;
+#else /* CONFIG_WITH_KMK_BUILTIN */
+
+ {
+ if ( !(flags & COMMANDS_KMK_BUILTIN)
+ && !strncmp(p, "kmk_builtin_", sizeof("kmk_builtin_") - 1))
+ flags |= COMMANDS_KMK_BUILTIN;
+ break;
+ }
+#endif /* CONFIG_WITH_KMK_BUILTIN */
+ ++p;
+ }
+
+ child->recursive = ((flags & COMMANDS_RECURSE) != 0);
+
+ /* Update the file's command flags with any new ones we found. We only
+ keep the COMMANDS_RECURSE setting. Even this isn't 100% correct; we are
+ now marking more commands recursive than should be in the case of
+ multiline define/endef scripts where only one line is marked "+". In
+ order to really fix this, we'll have to keep a lines_flags for every
+ actual line, after expansion. */
+ child->file->cmds->lines_flags[child->command_line - 1] |= flags & COMMANDS_RECURSE;
+
+ /* POSIX requires that a recipe prefix after a backslash-newline should
+ be ignored. Remove it now so the output is correct. */
+ {
+ char prefix = child->file->cmds->recipe_prefix;
+ char *p1, *p2;
+ p1 = p2 = p;
+ while (*p1 != '\0')
+ {
+ *(p2++) = *p1;
+ if (p1[0] == '\n' && p1[1] == prefix)
+ ++p1;
+ ++p1;
+ }
+ *p2 = *p1;
+ }
+
+ /* Figure out an argument list from this command line. */
+ {
+ char *end = 0;
+#ifdef VMS
+ /* Skip any leading whitespace */
+ while (*p)
+ {
+ if (!ISSPACE (*p))
+ {
+ if (*p != '\\')
+ break;
+ if ((p[1] != '\n') && (p[1] != 'n') && (p[1] != 't'))
+ break;
+ }
+ p++;
+ }
+
+ argv = p;
+ /* Although construct_command_argv contains some code for VMS, it was/is
+ not called/used. Please note, for VMS argv is a string (not an array
+ of strings) which contains the complete command line, which for
+ multi-line variables still includes the newlines. So detect newlines
+ and set 'end' (which is used for child->command_ptr) instead of
+ (re-)writing construct_command_argv */
+ if (!one_shell)
+ {
+ char *s = p;
+ int instring = 0;
+ while (*s)
+ {
+ if (*s == '"')
+ instring = !instring;
+ else if (*s == '\\' && !instring && *(s+1) != 0)
+ s++;
+ else if (*s == '\n' && !instring)
+ {
+ end = s;
+ break;
+ }
+ ++s;
+ }
+ }
+#else
+ argv = construct_command_argv (p, &end, child->file,
+ child->file->cmds->lines_flags[child->command_line - 1],
+ &child->sh_batch_file);
+#endif
+ if (end == NULL)
+ child->command_ptr = NULL;
+ else
+ {
+ *end++ = '\0';
+ child->command_ptr = end;
+ }
+ }
+
+ /* If -q was given, say that updating 'failed' if there was any text on the
+ command line, or 'succeeded' otherwise. The exit status of 1 tells the
+ user that -q is saying 'something to do'; the exit status for a random
+ error is 2. */
+ if (argv != 0 && question_flag && !(flags & COMMANDS_RECURSE))
+ {
+#ifndef VMS
+ free (argv[0]);
+ free (argv);
+#endif
+#ifdef VMS
+ /* On VMS, argv[0] can be a null string here */
+ if (argv[0] != 0)
+ {
+#endif
+ child->file->update_status = us_question;
+ notice_finished_file (child->file);
+ return;
+#ifdef VMS
+ }
+#endif
+ }
+
+ if (touch_flag && !(flags & COMMANDS_RECURSE))
+ {
+ /* Go on to the next command. It might be the recursive one.
+ We construct ARGV only to find the end of the command line. */
+#ifndef VMS
+ if (argv)
+ {
+ free (argv[0]);
+ free (argv);
+ }
+#endif
+ argv = 0;
+ }
+
+ if (argv == 0)
+ {
+ next_command:
+#ifdef __MSDOS__
+ execute_by_shell = 0; /* in case construct_command_argv sets it */
+#endif
+ /* This line has no commands. Go to the next. */
+ if (job_next_command (child))
+ start_job_command (child);
+ else
+ {
+ /* No more commands. Make sure we're "running"; we might not be if
+ (e.g.) all commands were skipped due to -n. */
+ set_command_state (child->file, cs_running);
+ child->file->update_status = us_success;
+ notice_finished_file (child->file);
+ }
+
+ OUTPUT_UNSET();
+ return;
+ }
+
+ /* Are we going to synchronize this command's output? Do so if either we're
+ in SYNC_RECURSE mode or this command is not recursive. We'll also check
+ output_sync separately below in case it changes due to error. */
+ child->output.syncout = output_sync && (output_sync == OUTPUT_SYNC_RECURSE
+ || !(flags & COMMANDS_RECURSE));
+ OUTPUT_SET (&child->output);
+
+#ifndef NO_OUTPUT_SYNC
+ if (! child->output.syncout)
+ /* We don't want to sync this command: to avoid misordered
+ output ensure any already-synced content is written. */
+ output_dump (&child->output);
+#endif
+
+ /* Print the command if appropriate. */
+#ifdef CONFIG_PRETTY_COMMAND_PRINTING
+ if ( pretty_command_printing
+ && (just_print_flag || (!(flags & COMMANDS_SILENT) && !silent_flag))
+ && argv[0][0] != '\0')
+ {
+ unsigned i;
+ for (i = 0; argv[i]; i++)
+ OSSS ( message, 0, "%s'%s'%s", i ? "\t" : "> ", argv[i], argv[i + 1] ? " \\" : "");
+ }
+ else
+#endif /* CONFIG_PRETTY_COMMAND_PRINTING */
+ if (just_print_flag || trace_flag
+ || (!(flags & COMMANDS_SILENT) && !silent_flag))
+ OS (message, 0, "%s", p);
+
+ /* Tell update_goal_chain that a command has been started on behalf of
+ this target. It is important that this happens here and not in
+ reap_children (where we used to do it), because reap_children might be
+ reaping children from a different target. We want this increment to
+ guaranteedly indicate that a command was started for the dependency
+ chain (i.e., update_file recursion chain) we are processing. */
+
+ ++commands_started;
+
+ /* Optimize an empty command. People use this for timestamp rules,
+ so avoid forking a useless shell. Do this after we increment
+ commands_started so make still treats this special case as if it
+ performed some action (makes a difference as to what messages are
+ printed, etc. */
+
+#if !defined(VMS) && !defined(_AMIGA)
+ if (
+#if defined __MSDOS__ || defined (__EMX__)
+ unixy_shell /* the test is complicated and we already did it */
+#else
+ (argv[0] && is_bourne_compatible_shell (argv[0]))
+#endif
+ && (argv[1] && argv[1][0] == '-'
+ &&
+ ((argv[1][1] == 'c' && argv[1][2] == '\0')
+ ||
+ (argv[1][1] == 'e' && argv[1][2] == 'c' && argv[1][3] == '\0')))
+ && (argv[2] && argv[2][0] == ':' && argv[2][1] == '\0')
+ && argv[3] == NULL)
+ {
+ free (argv[0]);
+ free (argv);
+ goto next_command;
+ }
+#endif /* !VMS && !_AMIGA */
+
+ /* If -n was given, recurse to get the next line in the sequence. */
+
+ if (just_print_flag && !(flags & COMMANDS_RECURSE))
+ {
+#ifndef VMS
+ free (argv[0]);
+ free (argv);
+#endif
+ goto next_command;
+ }
+
+ /* We're sure we're going to invoke a command: set up the output. */
+ output_start ();
+
+ /* Flush the output streams so they won't have things written twice. */
+
+ fflush (stdout);
+ fflush (stderr);
+
+#ifdef CONFIG_WITH_KMK_BUILTIN
+ /* If builtin command then pass it on to the builtin shell interpreter. */
+
+ if ((flags & COMMANDS_KMK_BUILTIN) && !just_print_flag)
+ {
+ int rc;
+ char **argv_spawn = NULL;
+ char **p2 = argv;
+ while (*p2 && strncmp (*p2, "kmk_builtin_", sizeof("kmk_builtin_") - 1))
+ p2++;
+ assert (*p2);
+ set_command_state (child->file, cs_running);
+ child->deleted = 0;
+ child->pid = 0;
+ if (p2 != argv)
+ rc = kmk_builtin_command (*p2, child, &argv_spawn, &child->pid);
+ else
+ {
+ int argc = 1;
+ while (argv[argc])
+ argc++;
+ rc = kmk_builtin_command_parsed (argc, argv, child, &argv_spawn, &child->pid);
+ }
+
+# ifndef VMS
+ free (argv[0]);
+ free ((char *) argv);
+# endif
+
+ if (!rc)
+ {
+ /* spawned a child? */
+ if (child->pid)
+ {
+ ++job_counter;
+ return;
+ }
+
+ /* synchronous command execution? */
+ if (!argv_spawn)
+ goto next_command;
+ }
+
+ /* failure? */
+ if (rc)
+ {
+ child->pid = (pid_t)42424242;
+ child->status = rc << 8;
+ child->has_status = 1;
+ unblock_sigs();
+ return;
+ }
+
+ /* conditional check == true; kicking off a child (not kmk_builtin_*). */
+ argv = argv_spawn;
+ }
+#endif /* CONFIG_WITH_KMK_BUILTIN */
+
+ /* Decide whether to give this child the 'good' standard input
+ (one that points to the terminal or whatever), or the 'bad' one
+ that points to the read side of a broken pipe. */
+
+ child->good_stdin = !good_stdin_used;
+ if (child->good_stdin)
+ good_stdin_used = 1;
+
+ child->deleted = 0;
+
+#ifndef _AMIGA
+ /* Set up the environment for the child. */
+ if (child->environment == 0)
+ child->environment = target_environment (child->file);
+#endif
+
+#if !defined(__MSDOS__) && !defined(_AMIGA) && !defined(WINDOWS32)
+
+#ifndef VMS
+ /* start_waiting_job has set CHILD->remote if we can start a remote job. */
+ if (child->remote)
+ {
+ int is_remote, id, used_stdin;
+ if (start_remote_job (argv, child->environment,
+ child->good_stdin ? 0 : get_bad_stdin (),
+ &is_remote, &id, &used_stdin))
+ /* Don't give up; remote execution may fail for various reasons. If
+ so, simply run the job locally. */
+ goto run_local;
+ else
+ {
+ if (child->good_stdin && !used_stdin)
+ {
+ child->good_stdin = 0;
+ good_stdin_used = 0;
+ }
+ child->remote = is_remote;
+ child->pid = id;
+ }
+ }
+ else
+#endif /* !VMS */
+ {
+ /* Fork the child process. */
+
+ char **parent_environ;
+
+ run_local:
+ block_sigs ();
+
+ child->remote = 0;
+
+#ifdef VMS
+ if (!child_execute_job (child, argv))
+ {
+ /* Fork failed! */
+ perror_with_name ("fork", "");
+ goto error;
+ }
+
+#else
+
+ parent_environ = environ;
+
+ jobserver_pre_child (flags & COMMANDS_RECURSE);
+
+ child->pid = child_execute_job (&child->output, child->good_stdin, argv, child->environment);
+
+ environ = parent_environ; /* Restore value child may have clobbered. */
+ jobserver_post_child (flags & COMMANDS_RECURSE);
+
+ if (child->pid < 0)
+ {
+ /* Fork failed! */
+ unblock_sigs ();
+ perror_with_name ("fork", "");
+ goto error;
+ }
+#endif /* !VMS */
+ }
+
+#else /* __MSDOS__ or Amiga or WINDOWS32 */
+#ifdef __MSDOS__
+ {
+ int proc_return;
+
+ block_sigs ();
+ dos_status = 0;
+
+ /* We call 'system' to do the job of the SHELL, since stock DOS
+ shell is too dumb. Our 'system' knows how to handle long
+ command lines even if pipes/redirection is needed; it will only
+ call COMMAND.COM when its internal commands are used. */
+ if (execute_by_shell)
+ {
+ char *cmdline = argv[0];
+ /* We don't have a way to pass environment to 'system',
+ so we need to save and restore ours, sigh... */
+ char **parent_environ = environ;
+
+ environ = child->environment;
+
+ /* If we have a *real* shell, tell 'system' to call
+ it to do everything for us. */
+ if (unixy_shell)
+ {
+ /* A *real* shell on MSDOS may not support long
+ command lines the DJGPP way, so we must use 'system'. */
+ cmdline = argv[2]; /* get past "shell -c" */
+ }
+
+ dos_command_running = 1;
+ proc_return = system (cmdline);
+ environ = parent_environ;
+ execute_by_shell = 0; /* for the next time */
+ }
+ else
+ {
+ dos_command_running = 1;
+ proc_return = spawnvpe (P_WAIT, argv[0], argv, child->environment);
+ }
+
+ /* Need to unblock signals before turning off
+ dos_command_running, so that child's signals
+ will be treated as such (see fatal_error_signal). */
+ unblock_sigs ();
+ dos_command_running = 0;
+
+ /* If the child got a signal, dos_status has its
+ high 8 bits set, so be careful not to alter them. */
+ if (proc_return == -1)
+ dos_status |= 0xff;
+ else
+ dos_status |= (proc_return & 0xff);
+ ++dead_children;
+ child->pid = dos_pid++;
+ }
+#endif /* __MSDOS__ */
+#ifdef _AMIGA
+ amiga_status = MyExecute (argv);
+
+ ++dead_children;
+ child->pid = amiga_pid++;
+ if (amiga_batch_file)
+ {
+ amiga_batch_file = 0;
+ DeleteFile (amiga_bname); /* Ignore errors. */
+ }
+#endif /* Amiga */
+#ifdef WINDOWS32
+ {
+# ifndef CONFIG_NEW_WIN_CHILDREN
+ HANDLE hPID;
+ char* arg0;
+ int outfd = FD_STDOUT;
+ int errfd = FD_STDERR;
+
+ /* make UNC paths safe for CreateProcess -- backslash format */
+ arg0 = argv[0];
+ if (arg0 && arg0[0] == '/' && arg0[1] == '/')
+ for ( ; arg0 && *arg0; arg0++)
+ if (*arg0 == '/')
+ *arg0 = '\\';
+
+ /* make sure CreateProcess() has Path it needs */
+ sync_Path_environment ();
+
+#ifndef NO_OUTPUT_SYNC
+ /* Divert child output if output_sync in use. */
+ if (child->output.syncout)
+ {
+ if (child->output.out >= 0)
+ outfd = child->output.out;
+ if (child->output.err >= 0)
+ errfd = child->output.err;
+ }
+#else
+ outfd = errfd = -1;
+#endif
+ hPID = process_easy (argv, child->environment, outfd, errfd);
+
+ if (hPID != INVALID_HANDLE_VALUE)
+ child->pid = (pid_t) hPID;
+ else
+ {
+ int i;
+ unblock_sigs ();
+ fprintf (stderr,
+ _("process_easy() failed to launch process (e=%ld)\n"),
+ process_last_err (hPID));
+ for (i = 0; argv[i]; i++)
+ fprintf (stderr, "%s ", argv[i]);
+ fprintf (stderr, _("\nCounted %d args in failed launch\n"), i);
+ goto error;
+ }
+# else /* CONFIG_NEW_WIN_CHILDREN */
+ struct variable *shell_var = lookup_variable("SHELL", 5);
+ const char *shell_value = !shell_var ? NULL
+ : !shell_var->recursive || strchr(shell_var->value, '$') == NULL
+ ? shell_var->value : variable_expand (shell_var->value);
+ int rc = MkWinChildCreate(argv, child->environment, shell_value, child, &child->pid);
+ if (rc != 0)
+ {
+ int i;
+ unblock_sigs ();
+ fprintf (stderr, _("failed to launch process (rc=%d)\n"), rc);
+ for (i = 0; argv[i]; i++)
+ fprintf (stderr, "%s ", argv[i]);
+ fprintf (stderr, "\n", argv[i]);
+ goto error;
+ }
+# endif /* CONFIG_NEW_WIN_CHILDREN */
+ }
+#endif /* WINDOWS32 */
+#endif /* __MSDOS__ or Amiga or WINDOWS32 */
+
+ /* Bump the number of jobs started in this second. */
+ ++job_counter;
+
+ /* We are the parent side. Set the state to
+ say the commands are running and return. */
+
+ set_command_state (child->file, cs_running);
+
+ /* Free the storage used by the child's argument list. */
+#ifdef KMK /* leak */
+ cleanup_argv:
+#endif
+#ifndef VMS
+ free (argv[0]);
+ free (argv);
+#endif
+
+ OUTPUT_UNSET();
+ return;
+
+ error:
+ child->file->update_status = us_failed;
+ notice_finished_file (child->file);
+#ifdef KMK /* fix leak */
+ goto cleanup_argv;
+#else
+ OUTPUT_UNSET();
+#endif
+}
+
+/* Try to start a child running.
+ Returns nonzero if the child was started (and maybe finished), or zero if
+ the load was too high and the child was put on the 'waiting_jobs' chain. */
+
+static int
+start_waiting_job (struct child *c)
+{
+ struct file *f = c->file;
+#ifdef DB_KMK
+ DB (DB_KMK, (_("start_waiting_job %p (`%s') command_flags=%#x slots=%d/%d\n"),
+ (void *)c, c->file->name, c->file->command_flags, job_slots_used, job_slots));
+#endif
+
+ /* If we can start a job remotely, we always want to, and don't care about
+ the local load average. We record that the job should be started
+ remotely in C->remote for start_job_command to test. */
+
+ c->remote = start_remote_job_p (1);
+
+#ifdef CONFIG_WITH_EXTENDED_NOTPARALLEL
+ if (c->file->command_flags & COMMANDS_NOTPARALLEL)
+ {
+ DB (DB_KMK, (_("not_parallel %d -> %d (file=%p `%s') [start_waiting_job]\n"),
+ not_parallel, not_parallel + 1, (void *)c->file, c->file->name));
+ assert(not_parallel >= 0);
+ ++not_parallel;
+ }
+#endif /* CONFIG_WITH_EXTENDED_NOTPARALLEL */
+
+ /* If we are running at least one job already and the load average
+ is too high, make this one wait. */
+ if (!c->remote
+#ifdef CONFIG_WITH_EXTENDED_NOTPARALLEL
+ && ((job_slots_used > 0 && (not_parallel > 0 || load_too_high ()))
+#else
+ && ((job_slots_used > 0 && load_too_high ())
+#endif
+#ifdef WINDOWS32
+# ifndef CONFIG_NEW_WIN_CHILDREN
+ || (process_used_slots () >= MAXIMUM_WAIT_OBJECTS)
+# endif
+#endif
+ ))
+ {
+#ifndef CONFIG_WITH_EXTENDED_NOTPARALLEL
+ /* Put this child on the chain of children waiting for the load average
+ to go down. */
+ set_command_state (f, cs_running);
+ c->next = waiting_jobs;
+ waiting_jobs = c;
+
+#else /* CONFIG_WITH_EXTENDED_NOTPARALLEL */
+
+ /* Put this child on the chain of children waiting for the load average
+ to go down. If not parallel, put it last. */
+ set_command_state (f, cs_running);
+ c->next = waiting_jobs;
+ if (c->next && (c->file->command_flags & COMMANDS_NOTPARALLEL))
+ {
+ struct child *prev = waiting_jobs;
+ while (prev->next)
+ prev = prev->next;
+ c->next = 0;
+ prev->next = c;
+ }
+ else /* FIXME: insert after the last node with COMMANDS_NOTPARALLEL set */
+ waiting_jobs = c;
+ DB (DB_KMK, (_("queued child %p (`%s')\n"), (void *)c, c->file->name));
+#endif /* CONFIG_WITH_EXTENDED_NOTPARALLEL */
+ return 0;
+ }
+
+ /* Start the first command; reap_children will run later command lines. */
+ start_job_command (c);
+
+ switch (f->command_state)
+ {
+ case cs_running:
+ c->next = children;
+ DB (DB_JOBS, (_("Putting child %p (%s) PID %s%s on the chain.\n"),
+ (void *)c, c->file->name, pid2str (c->pid),
+ c->remote ? _(" (remote)") : ""));
+ children = c;
+ /* One more job slot is in use. */
+ ++job_slots_used;
+ unblock_sigs ();
+ break;
+
+ case cs_not_started:
+ /* All the command lines turned out to be empty. */
+ f->update_status = us_success;
+ /* FALLTHROUGH */
+
+ case cs_finished:
+ notice_finished_file (f);
+ free_child (c);
+ break;
+
+ default:
+ assert (f->command_state == cs_finished);
+ break;
+ }
+
+ return 1;
+}
+
+/* Create a 'struct child' for FILE and start its commands running. */
+
+void
+new_job (struct file *file)
+{
+ struct commands *cmds = file->cmds;
+ struct child *c;
+ char **lines;
+ unsigned int i;
+
+ /* Let any previously decided-upon jobs that are waiting
+ for the load to go down start before this new one. */
+ start_waiting_jobs ();
+
+ /* Reap any children that might have finished recently. */
+ reap_children (0, 0);
+
+ /* Chop the commands up into lines if they aren't already. */
+ chop_commands (cmds);
+#ifdef CONFIG_WITH_MEMORY_OPTIMIZATIONS
+ cmds->refs++; /* retain the chopped lines. */
+#endif
+
+ /* Start the command sequence, record it in a new
+ 'struct child', and add that to the chain. */
+
+ c = xcalloc (sizeof (struct child));
+ output_init (&c->output);
+
+ c->file = file;
+ c->sh_batch_file = NULL;
+
+ /* Cache dontcare flag because file->dontcare can be changed once we
+ return. Check dontcare inheritance mechanism for details. */
+ c->dontcare = file->dontcare;
+
+ /* Start saving output in case the expansion uses $(info ...) etc. */
+ OUTPUT_SET (&c->output);
+
+ /* Expand the command lines and store the results in LINES. */
+ lines = xmalloc (cmds->ncommand_lines * sizeof (char *));
+ for (i = 0; i < cmds->ncommand_lines; ++i)
+ {
+ /* Collapse backslash-newline combinations that are inside variable
+ or function references. These are left alone by the parser so
+ that they will appear in the echoing of commands (where they look
+ nice); and collapsed by construct_command_argv when it tokenizes.
+ But letting them survive inside function invocations loses because
+ we don't want the functions to see them as part of the text. */
+
+ char *in, *out, *ref;
+
+ /* IN points to where in the line we are scanning.
+ OUT points to where in the line we are writing.
+ When we collapse a backslash-newline combination,
+ IN gets ahead of OUT. */
+
+ in = out = cmds->command_lines[i];
+ while ((ref = strchr (in, '$')) != 0)
+ {
+ ++ref; /* Move past the $. */
+
+ if (out != in)
+ /* Copy the text between the end of the last chunk
+ we processed (where IN points) and the new chunk
+ we are about to process (where REF points). */
+ memmove (out, in, ref - in);
+
+ /* Move both pointers past the boring stuff. */
+ out += ref - in;
+ in = ref;
+
+ if (*ref == '(' || *ref == '{')
+ {
+ char openparen = *ref;
+ char closeparen = openparen == '(' ? ')' : '}';
+ char *outref;
+ int count;
+ char *p;
+
+ *out++ = *in++; /* Copy OPENPAREN. */
+ outref = out;
+ /* IN now points past the opening paren or brace.
+ Count parens or braces until it is matched. */
+ count = 0;
+ while (*in != '\0')
+ {
+ if (*in == closeparen && --count < 0)
+ break;
+ else if (*in == '\\' && in[1] == '\n')
+ {
+ /* We have found a backslash-newline inside a
+ variable or function reference. Eat it and
+ any following whitespace. */
+
+ int quoted = 0;
+ for (p = in - 1; p > ref && *p == '\\'; --p)
+ quoted = !quoted;
+
+ if (quoted)
+ /* There were two or more backslashes, so this is
+ not really a continuation line. We don't collapse
+ the quoting backslashes here as is done in
+ collapse_continuations, because the line will
+ be collapsed again after expansion. */
+ *out++ = *in++;
+ else
+ {
+ /* Skip the backslash, newline, and whitespace. */
+ in += 2;
+ NEXT_TOKEN (in);
+
+ /* Discard any preceding whitespace that has
+ already been written to the output. */
+ while (out > outref && ISBLANK (out[-1]))
+ --out;
+
+ /* Replace it all with a single space. */
+ *out++ = ' ';
+ }
+ }
+ else
+ {
+ if (*in == openparen)
+ ++count;
+
+ *out++ = *in++;
+ }
+ }
+ }
+ }
+
+ /* There are no more references in this line to worry about.
+ Copy the remaining uninteresting text to the output. */
+ if (out != in)
+ memmove (out, in, strlen (in) + 1);
+
+ /* Finally, expand the line. */
+ cmds->fileinfo.offset = i;
+ lines[i] = allocated_variable_expand_for_file (cmds->command_lines[i],
+ file);
+ }
+
+ cmds->fileinfo.offset = 0;
+ c->command_lines = lines;
+#ifdef CONFIG_WITH_PRINT_TIME_SWITCH
+ c->start_ts = -1;
+#endif
+
+ /* Fetch the first command line to be run. */
+ job_next_command (c);
+
+ /* Wait for a job slot to be freed up. If we allow an infinite number
+ don't bother; also job_slots will == 0 if we're using the jobserver. */
+
+ if (job_slots != 0)
+ while (job_slots_used == job_slots)
+ reap_children (1, 0);
+
+#ifdef MAKE_JOBSERVER
+ /* If we are controlling multiple jobs make sure we have a token before
+ starting the child. */
+
+ /* This can be inefficient. There's a decent chance that this job won't
+ actually have to run any subprocesses: the command script may be empty
+ or otherwise optimized away. It would be nice if we could defer
+ obtaining a token until just before we need it, in start_job_command.
+ To do that we'd need to keep track of whether we'd already obtained a
+ token (since start_job_command is called for each line of the job, not
+ just once). Also more thought needs to go into the entire algorithm;
+ this is where the old parallel job code waits, so... */
+
+ else if (jobserver_enabled ())
+ while (1)
+ {
+ int got_token;
+
+ DB (DB_JOBS, ("Need a job token; we %shave children\n",
+ children ? "" : "don't "));
+
+ /* If we don't already have a job started, use our "free" token. */
+ if (!jobserver_tokens)
+ break;
+
+ /* Prepare for jobserver token acquisition. */
+ jobserver_pre_acquire ();
+
+ /* Reap anything that's currently waiting. */
+ reap_children (0, 0);
+
+ /* Kick off any jobs we have waiting for an opportunity that
+ can run now (i.e., waiting for load). */
+ start_waiting_jobs ();
+
+ /* If our "free" slot is available, use it; we don't need a token. */
+ if (!jobserver_tokens)
+ break;
+
+ /* There must be at least one child already, or we have no business
+ waiting for a token. */
+ if (!children)
+ O (fatal, NILF, "INTERNAL: no children as we go to sleep on read\n");
+
+ /* Get a token. */
+ got_token = jobserver_acquire (waiting_jobs != NULL);
+
+ /* If we got one, we're done here. */
+ if (got_token == 1)
+ {
+ DB (DB_JOBS, (_("Obtained token for child %p (%s).\n"),
+ (void *)c, c->file->name));
+ break;
+ }
+ }
+#endif
+
+ ++jobserver_tokens;
+
+ /* Trace the build.
+ Use message here so that changes to working directories are logged. */
+ if (trace_flag)
+ {
+ char *newer = allocated_variable_expand_for_file ("$?", c->file);
+ const char *nm;
+
+ if (! cmds->fileinfo.filenm)
+ nm = _("<builtin>");
+ else
+ {
+ char *n = alloca (strlen (cmds->fileinfo.filenm) + 1 + 11 + 1);
+ sprintf (n, "%s:%lu", cmds->fileinfo.filenm, cmds->fileinfo.lineno);
+ nm = n;
+ }
+
+ if (newer[0] == '\0')
+ OSS (message, 0,
+ _("%s: target '%s' does not exist"), nm, c->file->name);
+ else
+ OSSS (message, 0,
+ _("%s: update target '%s' due to: %s"), nm, c->file->name, newer);
+
+ free (newer);
+ }
+
+ /* The job is now primed. Start it running.
+ (This will notice if there is in fact no recipe.) */
+ start_waiting_job (c);
+
+#ifndef CONFIG_WITH_EXTENDED_NOTPARALLEL
+ if (job_slots == 1 || not_parallel)
+ /* Since there is only one job slot, make things run linearly.
+ Wait for the child to die, setting the state to 'cs_finished'. */
+ while (file->command_state == cs_running)
+ reap_children (1, 0);
+
+#else /* CONFIG_WITH_EXTENDED_NOTPARALLEL */
+
+ if (job_slots == 1 || not_parallel < 0)
+ {
+ /* Since there is only one job slot, make things run linearly.
+ Wait for the child to die, setting the state to `cs_finished'. */
+ while (file->command_state == cs_running)
+ reap_children (1, 0);
+ }
+ else if (not_parallel > 0)
+ {
+ /* wait for all live children to finish and then continue
+ with the not-parallel child(s). FIXME: this loop could be better? */
+ while (file->command_state == cs_running
+ && (children != 0 || shell_function_pid != 0) /* reap_child condition */
+ && not_parallel > 0)
+ reap_children (1, 0);
+ }
+#endif /* CONFIG_WITH_EXTENDED_NOTPARALLEL */
+
+ OUTPUT_UNSET ();
+ return;
+}
+
+/* Move CHILD's pointers to the next command for it to execute.
+ Returns nonzero if there is another command. */
+
+static int
+job_next_command (struct child *child)
+{
+ while (child->command_ptr == 0 || *child->command_ptr == '\0')
+ {
+ /* There are no more lines in the expansion of this line. */
+ if (child->command_line == child->file->cmds->ncommand_lines)
+ {
+ /* There are no more lines to be expanded. */
+ child->command_ptr = 0;
+ child->file->cmds->fileinfo.offset = 0;
+ return 0;
+ }
+ else
+ /* Get the next line to run. */
+ child->command_ptr = child->command_lines[child->command_line++];
+ }
+
+ child->file->cmds->fileinfo.offset = child->command_line - 1;
+ return 1;
+}
+
+/* Determine if the load average on the system is too high to start a new job.
+ The real system load average is only recomputed once a second. However, a
+ very parallel make can easily start tens or even hundreds of jobs in a
+ second, which brings the system to its knees for a while until that first
+ batch of jobs clears out.
+
+ To avoid this we use a weighted algorithm to try to account for jobs which
+ have been started since the last second, and guess what the load average
+ would be now if it were computed.
+
+ This algorithm was provided by Thomas Riedl <thomas.riedl@siemens.com>,
+ who writes:
+
+! calculate something load-oid and add to the observed sys.load,
+! so that latter can catch up:
+! - every job started increases jobctr;
+! - every dying job decreases a positive jobctr;
+! - the jobctr value gets zeroed every change of seconds,
+! after its value*weight_b is stored into the 'backlog' value last_sec
+! - weight_a times the sum of jobctr and last_sec gets
+! added to the observed sys.load.
+!
+! The two weights have been tried out on 24 and 48 proc. Sun Solaris-9
+! machines, using a several-thousand-jobs-mix of cpp, cc, cxx and smallish
+! sub-shelled commands (rm, echo, sed...) for tests.
+! lowering the 'direct influence' factor weight_a (e.g. to 0.1)
+! resulted in significant excession of the load limit, raising it
+! (e.g. to 0.5) took bad to small, fast-executing jobs and didn't
+! reach the limit in most test cases.
+!
+! lowering the 'history influence' weight_b (e.g. to 0.1) resulted in
+! exceeding the limit for longer-running stuff (compile jobs in
+! the .5 to 1.5 sec. range),raising it (e.g. to 0.5) overrepresented
+! small jobs' effects.
+
+ */
+
+#define LOAD_WEIGHT_A 0.25
+#define LOAD_WEIGHT_B 0.25
+
+static int
+load_too_high (void)
+{
+#if defined(__MSDOS__) || defined(VMS) || defined(_AMIGA) || defined(__riscos__) || defined(__HAIKU__)
+ return 1;
+#else
+ static double last_sec;
+ static time_t last_now;
+ double load, guess;
+ time_t now;
+
+#if defined(WINDOWS32) && !defined(CONFIG_NEW_WIN_CHILDREN)
+ /* sub_proc.c cannot wait for more than MAXIMUM_WAIT_OBJECTS children */
+ if (process_used_slots () >= MAXIMUM_WAIT_OBJECTS)
+ return 1;
+#endif
+
+ if (max_load_average < 0)
+ return 0;
+
+ /* Find the real system load average. */
+ make_access ();
+ if (getloadavg (&load, 1) != 1)
+ {
+ static int lossage = -1;
+ /* Complain only once for the same error. */
+ if (lossage == -1 || errno != lossage)
+ {
+ if (errno == 0)
+ /* An errno value of zero means getloadavg is just unsupported. */
+ O (error, NILF,
+ _("cannot enforce load limits on this operating system"));
+ else
+ perror_with_name (_("cannot enforce load limit: "), "getloadavg");
+ }
+ lossage = errno;
+ load = 0;
+ }
+ user_access ();
+
+ /* If we're in a new second zero the counter and correct the backlog
+ value. Only keep the backlog for one extra second; after that it's 0. */
+ now = time (NULL);
+ if (last_now < now)
+ {
+ if (last_now == now - 1)
+ last_sec = LOAD_WEIGHT_B * job_counter;
+ else
+ last_sec = 0.0;
+
+ job_counter = 0;
+ last_now = now;
+ }
+
+ /* Try to guess what the load would be right now. */
+ guess = load + (LOAD_WEIGHT_A * (job_counter + last_sec));
+
+ DB (DB_JOBS, ("Estimated system load = %f (actual = %f) (max requested = %f)\n",
+ guess, load, max_load_average));
+
+ return guess >= max_load_average;
+#endif
+}
+
+/* Start jobs that are waiting for the load to be lower. */
+
+void
+start_waiting_jobs (void)
+{
+ struct child *job;
+
+ if (waiting_jobs == 0)
+ return;
+
+ do
+ {
+ /* Check for recently deceased descendants. */
+ reap_children (0, 0);
+
+ /* Take a job off the waiting list. */
+ job = waiting_jobs;
+ waiting_jobs = job->next;
+
+#ifdef CONFIG_WITH_EXTENDED_NOTPARALLEL
+ /* If it's a not-parallel job, we've already counted it once
+ when it was queued in start_waiting_job, so decrement
+ before sending it to start_waiting_job again. */
+ if (job->file->command_flags & COMMANDS_NOTPARALLEL)
+ {
+ DB (DB_KMK, (_("not_parallel %d -> %d (file=%p `%s') [start_waiting_jobs]\n"),
+ not_parallel, not_parallel - 1, (void *) job->file, job->file->name));
+ assert(not_parallel > 0);
+ --not_parallel;
+ }
+#endif /* CONFIG_WITH_EXTENDED_NOTPARALLEL */
+
+ /* Try to start that job. We break out of the loop as soon
+ as start_waiting_job puts one back on the waiting list. */
+ }
+ while (start_waiting_job (job) && waiting_jobs != 0);
+
+ return;
+}
+
+#ifndef WINDOWS32
+
+/* EMX: Start a child process. This function returns the new pid. */
+# if defined __EMX__
+int
+child_execute_job (struct output *out, int good_stdin, char **argv, char **envp)
+{
+ int pid;
+ int fdin = good_stdin ? FD_STDIN : get_bad_stdin ();
+ int fdout = FD_STDOUT;
+ int fderr = FD_STDERR;
+ int save_fdin = -1;
+ int save_fdout = -1;
+ int save_fderr = -1;
+
+ /* Divert child output if we want to capture output. */
+ if (out && out->syncout)
+ {
+ if (out->out >= 0)
+ fdout = out->out;
+ if (out->err >= 0)
+ fderr = out->err;
+ }
+
+ /* For each FD which needs to be redirected first make a dup of the standard
+ FD to save and mark it close on exec so our child won't see it. Then
+ dup2() the standard FD to the redirect FD, and also mark the redirect FD
+ as close on exec. */
+ if (fdin != FD_STDIN)
+ {
+ save_fdin = dup (FD_STDIN);
+ if (save_fdin < 0)
+ O (fatal, NILF, _("no more file handles: could not duplicate stdin\n"));
+ CLOSE_ON_EXEC (save_fdin);
+
+ dup2 (fdin, FD_STDIN);
+ CLOSE_ON_EXEC (fdin);
+ }
+
+ if (fdout != FD_STDOUT)
+ {
+ save_fdout = dup (FD_STDOUT);
+ if (save_fdout < 0)
+ O (fatal, NILF,
+ _("no more file handles: could not duplicate stdout\n"));
+ CLOSE_ON_EXEC (save_fdout);
+
+ dup2 (fdout, FD_STDOUT);
+ CLOSE_ON_EXEC (fdout);
+ }
+
+ if (fderr != FD_STDERR)
+ {
+ if (fderr != fdout)
+ {
+ save_fderr = dup (FD_STDERR);
+ if (save_fderr < 0)
+ O (fatal, NILF,
+ _("no more file handles: could not duplicate stderr\n"));
+ CLOSE_ON_EXEC (save_fderr);
+ }
+
+ dup2 (fderr, FD_STDERR);
+ CLOSE_ON_EXEC (fderr);
+ }
+
+ /* Run the command. */
+ pid = exec_command (argv, envp);
+
+ /* Restore stdout/stdin/stderr of the parent and close temporary FDs. */
+ if (save_fdin >= 0)
+ {
+ if (dup2 (save_fdin, FD_STDIN) != FD_STDIN)
+ O (fatal, NILF, _("Could not restore stdin\n"));
+ else
+ close (save_fdin);
+ }
+
+ if (save_fdout >= 0)
+ {
+ if (dup2 (save_fdout, FD_STDOUT) != FD_STDOUT)
+ O (fatal, NILF, _("Could not restore stdout\n"));
+ else
+ close (save_fdout);
+ }
+
+ if (save_fderr >= 0)
+ {
+ if (dup2 (save_fderr, FD_STDERR) != FD_STDERR)
+ O (fatal, NILF, _("Could not restore stderr\n"));
+ else
+ close (save_fderr);
+ }
+
+ return pid;
+}
+
+#elif !defined (_AMIGA) && !defined (__MSDOS__) && !defined (VMS)
+
+/* POSIX:
+ Create a child process executing the command in ARGV.
+ ENVP is the environment of the new program. Returns the PID or -1. */
+int
+child_execute_job (struct output *out, int good_stdin, char **argv, char **envp)
+{
+ int r;
+ int pid;
+ int fdin = good_stdin ? FD_STDIN : get_bad_stdin ();
+ int fdout = FD_STDOUT;
+ int fderr = FD_STDERR;
+
+ /* Divert child output if we want to capture it. */
+ if (out && out->syncout)
+ {
+ if (out->out >= 0)
+ fdout = out->out;
+ if (out->err >= 0)
+ fderr = out->err;
+ }
+
+ pid = vfork();
+ if (pid != 0)
+ return pid;
+
+ /* We are the child. */
+ unblock_sigs ();
+
+#ifdef SET_STACK_SIZE
+ /* Reset limits, if necessary. */
+ if (stack_limit.rlim_cur)
+ setrlimit (RLIMIT_STACK, &stack_limit);
+#endif
+
+ /* For any redirected FD, dup2() it to the standard FD.
+ They are all marked close-on-exec already. */
+ if (fdin != FD_STDIN)
+ EINTRLOOP (r, dup2 (fdin, FD_STDIN));
+ if (fdout != FD_STDOUT)
+ EINTRLOOP (r, dup2 (fdout, FD_STDOUT));
+ if (fderr != FD_STDERR)
+ EINTRLOOP (r, dup2 (fderr, FD_STDERR));
+
+ /* Run the command. */
+ exec_command (argv, envp);
+}
+#endif /* !AMIGA && !__MSDOS__ && !VMS */
+#endif /* !WINDOWS32 */
+
+#if !defined(WINDOWS32) || !defined(CONFIG_NEW_WIN_CHILDREN)
+#ifndef _AMIGA
+/* Replace the current process with one running the command in ARGV,
+ with environment ENVP. This function does not return. */
+
+/* EMX: This function returns the pid of the child process. */
+# ifdef __EMX__
+int
+# else
+void
+# endif
+exec_command (char **argv, char **envp)
+{
+#ifdef VMS
+ /* to work around a problem with signals and execve: ignore them */
+#ifdef SIGCHLD
+ signal (SIGCHLD,SIG_IGN);
+#endif
+ /* Run the program. */
+ execve (argv[0], argv, envp);
+ perror_with_name ("execve: ", argv[0]);
+ _exit (EXIT_FAILURE);
+#else
+#ifdef WINDOWS32
+# ifndef CONFIG_NEW_WIN_CHILDREN
+ HANDLE hPID;
+ HANDLE hWaitPID;
+ int exit_code = EXIT_FAILURE;
+
+ /* make sure CreateProcess() has Path it needs */
+ sync_Path_environment ();
+
+ /* launch command */
+ hPID = process_easy (argv, envp, -1, -1);
+
+ /* make sure launch ok */
+ if (hPID == INVALID_HANDLE_VALUE)
+ {
+ int i;
+ fprintf (stderr, _("process_easy() failed to launch process (e=%ld)\n"),
+ process_last_err (hPID));
+ for (i = 0; argv[i]; i++)
+ fprintf (stderr, "%s ", argv[i]);
+ fprintf (stderr, _("\nCounted %d args in failed launch\n"), i);
+ exit (EXIT_FAILURE);
+ }
+
+ /* wait and reap last child */
+ hWaitPID = process_wait_for_any (1, 0);
+ while (hWaitPID)
+ {
+ /* was an error found on this process? */
+ int err = process_last_err (hWaitPID);
+
+ /* get exit data */
+ exit_code = process_exit_code (hWaitPID);
+
+ if (err)
+ fprintf (stderr, "make (e=%d, rc=%d): %s",
+ err, exit_code, map_windows32_error_to_string (err));
+
+ /* cleanup process */
+ process_cleanup (hWaitPID);
+
+ /* expect to find only last pid, warn about other pids reaped */
+ if (hWaitPID == hPID)
+ break;
+ else
+ {
+ char *pidstr = xstrdup (pid2str ((pid_t)hWaitPID));
+
+ fprintf (stderr,
+ _("make reaped child pid %s, still waiting for pid %s\n"),
+ pidstr, pid2str ((pid_t)hPID));
+ free (pidstr);
+ }
+ }
+
+ /* return child's exit code as our exit code */
+ exit (exit_code);
+# else /* CONFIG_NEW_WIN_CHILDREN */
+
+# endif /* CONFIG_NEW_WIN_CHILDREN */
+#else /* !WINDOWS32 */
+
+# ifdef __EMX__
+ int pid;
+# endif
+
+ /* Be the user, permanently. */
+ child_access ();
+
+# ifdef __EMX__
+ /* Run the program. */
+ pid = spawnvpe (P_NOWAIT, argv[0], argv, envp);
+ if (pid >= 0)
+ return pid;
+
+ /* the file might have a strange shell extension */
+ if (errno == ENOENT)
+ errno = ENOEXEC;
+
+# else
+ /* Run the program. */
+ environ = envp;
+ execvp (argv[0], argv);
+
+# endif /* !__EMX__ */
+
+ switch (errno)
+ {
+ case ENOENT:
+ /* We are in the child: don't use the output buffer.
+ It's not right to run fprintf() here! */
+ if (makelevel == 0)
+ fprintf (stderr, _("%s: %s: Command not found\n"), program, argv[0]);
+ else
+ fprintf (stderr, _("%s[%u]: %s: Command not found\n"),
+ program, makelevel, argv[0]);
+ break;
+ case ENOEXEC:
+ {
+ /* The file is not executable. Try it as a shell script. */
+ const char *shell;
+ char **new_argv;
+ int argc;
+ int i=1;
+
+# ifdef __EMX__
+ /* Do not use $SHELL from the environment */
+ struct variable *p = lookup_variable ("SHELL", 5);
+ if (p)
+ shell = p->value;
+ else
+ shell = 0;
+# else
+ shell = getenv ("SHELL");
+# endif
+ if (shell == 0)
+ shell = default_shell;
+
+ argc = 1;
+ while (argv[argc] != 0)
+ ++argc;
+
+# ifdef __EMX__
+ if (!unixy_shell)
+ ++argc;
+# endif
+
+ new_argv = alloca ((1 + argc + 1) * sizeof (char *));
+ new_argv[0] = (char *)shell;
+
+# ifdef __EMX__
+ if (!unixy_shell)
+ {
+ new_argv[1] = "/c";
+ ++i;
+ --argc;
+ }
+# endif
+
+ new_argv[i] = argv[0];
+ while (argc > 0)
+ {
+ new_argv[i + argc] = argv[argc];
+ --argc;
+ }
+
+# ifdef __EMX__
+ pid = spawnvpe (P_NOWAIT, shell, new_argv, envp);
+ if (pid >= 0)
+ break;
+# else
+ execvp (shell, new_argv);
+# endif
+ if (errno == ENOENT)
+ OS (error, NILF, _("%s: Shell program not found"), shell);
+ else
+ perror_with_name ("execvp: ", shell);
+ break;
+ }
+
+# ifdef __EMX__
+ case EINVAL:
+ /* this nasty error was driving me nuts :-( */
+ O (error, NILF, _("spawnvpe: environment space might be exhausted"));
+ /* FALLTHROUGH */
+# endif
+
+ default:
+ perror_with_name ("execvp: ", argv[0]);
+ break;
+ }
+
+# ifdef __EMX__
+ return pid;
+# else
+ _exit (127);
+# endif
+#endif /* !WINDOWS32 */
+#endif /* !VMS */
+}
+#else /* On Amiga */
+void
+exec_command (char **argv)
+{
+ MyExecute (argv);
+}
+
+void clean_tmp (void)
+{
+ DeleteFile (amiga_bname);
+}
+
+#endif /* On Amiga */
+#endif /* !defined(WINDOWS32) || !defined(CONFIG_NEW_WIN_CHILDREN) */
+
+#ifndef VMS
+/* Figure out the argument list necessary to run LINE as a command. Try to
+ avoid using a shell. This routine handles only ' quoting, and " quoting
+ when no backslash, $ or ' characters are seen in the quotes. Starting
+ quotes may be escaped with a backslash. If any of the characters in
+ sh_chars is seen, or any of the builtin commands listed in sh_cmds
+ is the first word of a line, the shell is used.
+
+ If RESTP is not NULL, *RESTP is set to point to the first newline in LINE.
+ If *RESTP is NULL, newlines will be ignored.
+
+ SHELL is the shell to use, or nil to use the default shell.
+ IFS is the value of $IFS, or nil (meaning the default).
+
+ FLAGS is the value of lines_flags for this command line. It is
+ used in the WINDOWS32 port to check whether + or $(MAKE) were found
+ in this command line, in which case the effect of just_print_flag
+ is overridden. */
+
+static char **
+construct_command_argv_internal (char *line, char **restp, const char *shell,
+ const char *shellflags, const char *ifs,
+ int flags, char **batch_filename UNUSED)
+{
+#ifdef __MSDOS__
+ /* MSDOS supports both the stock DOS shell and ports of Unixy shells.
+ We call 'system' for anything that requires ''slow'' processing,
+ because DOS shells are too dumb. When $SHELL points to a real
+ (unix-style) shell, 'system' just calls it to do everything. When
+ $SHELL points to a DOS shell, 'system' does most of the work
+ internally, calling the shell only for its internal commands.
+ However, it looks on the $PATH first, so you can e.g. have an
+ external command named 'mkdir'.
+
+ Since we call 'system', certain characters and commands below are
+ actually not specific to COMMAND.COM, but to the DJGPP implementation
+ of 'system'. In particular:
+
+ The shell wildcard characters are in DOS_CHARS because they will
+ not be expanded if we call the child via 'spawnXX'.
+
+ The ';' is in DOS_CHARS, because our 'system' knows how to run
+ multiple commands on a single line.
+
+ DOS_CHARS also include characters special to 4DOS/NDOS, so we
+ won't have to tell one from another and have one more set of
+ commands and special characters. */
+ static const char *sh_chars_dos = "*?[];|<>%^&()";
+ static const char *sh_cmds_dos[] =
+ { "break", "call", "cd", "chcp", "chdir", "cls", "copy", "ctty", "date",
+ "del", "dir", "echo", "erase", "exit", "for", "goto", "if", "md",
+ "mkdir", "path", "pause", "prompt", "rd", "rmdir", "rem", "ren",
+ "rename", "set", "shift", "time", "type", "ver", "verify", "vol", ":",
+ 0 };
+
+ static const char *sh_chars_sh = "#;\"*?[]&|<>(){}$`^";
+ static const char *sh_cmds_sh[] =
+ { "cd", "echo", "eval", "exec", "exit", "login", "logout", "set", "umask",
+ "wait", "while", "for", "case", "if", ":", ".", "break", "continue",
+ "export", "read", "readonly", "shift", "times", "trap", "switch",
+ "unset", "ulimit", 0 };
+
+ const char *sh_chars;
+ const char **sh_cmds;
+
+#elif defined (__EMX__)
+ static const char *sh_chars_dos = "*?[];|<>%^&()";
+ static const char *sh_cmds_dos[] =
+ { "break", "call", "cd", "chcp", "chdir", "cls", "copy", "ctty", "date",
+ "del", "dir", "echo", "erase", "exit", "for", "goto", "if", "md",
+ "mkdir", "path", "pause", "prompt", "rd", "rmdir", "rem", "ren",
+ "rename", "set", "shift", "time", "type", "ver", "verify", "vol", ":",
+ 0 };
+
+ static const char *sh_chars_os2 = "*?[];|<>%^()\"'&";
+ static const char *sh_cmds_os2[] =
+ { "call", "cd", "chcp", "chdir", "cls", "copy", "date", "del", "detach",
+ "dir", "echo", "endlocal", "erase", "exit", "for", "goto", "if", "keys",
+ "md", "mkdir", "move", "path", "pause", "prompt", "rd", "rem", "ren",
+ "rename", "rmdir", "set", "setlocal", "shift", "start", "time", "type",
+ "ver", "verify", "vol", ":", 0 };
+
+ static const char *sh_chars_sh = "#;\"*?[]&|<>(){}$`^~'";
+ static const char *sh_cmds_sh[] =
+ { "echo", "cd", "eval", "exec", "exit", "login", "logout", "set", "umask",
+ "wait", "while", "for", "case", "if", ":", ".", "break", "continue",
+ "export", "read", "readonly", "shift", "times", "trap", "switch",
+ "unset", 0 };
+
+ const char *sh_chars;
+ const char **sh_cmds;
+
+#elif defined (_AMIGA)
+ static const char *sh_chars = "#;\"|<>()?*$`";
+ static const char *sh_cmds[] =
+ { "cd", "eval", "if", "delete", "echo", "copy", "rename", "set", "setenv",
+ "date", "makedir", "skip", "else", "endif", "path", "prompt", "unset",
+ "unsetenv", "version", 0 };
+
+#elif defined (WINDOWS32)
+ /* We used to have a double quote (") in sh_chars_dos[] below, but
+ that caused any command line with quoted file names be run
+ through a temporary batch file, which introduces command-line
+ limit of 4K charcaters imposed by cmd.exe. Since CreateProcess
+ can handle quoted file names just fine, removing the quote lifts
+ the limit from a very frequent use case, because using quoted
+ file names is commonplace on MS-Windows. */
+ static const char *sh_chars_dos = "|&<>";
+ static const char *sh_cmds_dos[] =
+ { "assoc", "break", "call", "cd", "chcp", "chdir", "cls", "color", "copy",
+ "ctty", "date", "del", "dir", "echo", "echo.", "endlocal", "erase",
+ "exit", "for", "ftype", "goto", "if", "if", "md", "mkdir", "move",
+ "path", "pause", "prompt", "rd", "rem", "ren", "rename", "rmdir",
+ "set", "setlocal", "shift", "time", "title", "type", "ver", "verify",
+ "vol", ":", 0 };
+
+ static const char *sh_chars_sh = "#;\"*?[]&|<>(){}$`^";
+ static const char *sh_cmds_sh[] =
+ { "cd", "eval", "exec", "exit", "login", "logout", "set", "umask", "wait",
+ "while", "for", "case", "if", ":", ".", "break", "continue", "export",
+ "read", "readonly", "shift", "times", "trap", "switch", "test",
+#ifdef BATCH_MODE_ONLY_SHELL
+ "echo",
+#endif
+ 0 };
+
+ const char *sh_chars;
+ char const * const * sh_cmds; /* kmk: +_sh +const*2 */
+#elif defined(__riscos__)
+ static const char *sh_chars = "";
+ static const char *sh_cmds[] = { 0 };
+#else /* must be UNIX-ish */
+ static const char *sh_chars_sh = "#;\"*?[]&|<>(){}$`^~!"; /* kmk: +_sh */
+ static const char *sh_cmds_sh[] = /* kmk: +_sh */
+ { ".", ":", "break", "case", "cd", "continue", "eval", "exec", "exit",
+ "export", "for", "if", "login", "logout", "read", "readonly", "set",
+ "shift", "switch", "test", "times", "trap", "ulimit", "umask", "unset",
+ "wait", "while", 0 };
+
+# if 0 /*def HAVE_DOS_PATHS - kmk */
+ /* This is required if the MSYS/Cygwin ports (which do not define
+ WINDOWS32) are compiled with HAVE_DOS_PATHS defined, which uses
+ sh_chars_sh directly (see below). The value must be identical
+ to that of sh_chars immediately above. */
+ static const char *sh_chars_sh = "#;\"*?[]&|<>(){}$`^~!";
+# endif /* HAVE_DOS_PATHS */
+ char const * sh_chars = sh_chars_sh; /* kmk: +_sh +const */
+ char const * const * sh_cmds = sh_cmds_sh; /* kmk: +_sh +const*2 */
+#endif
+#ifdef KMK
+ static const char sh_chars_kash[] = "#;*?[]&|<>(){}$`^~!"; /* note: no \" - good idea? */
+ static const char * const sh_cmds_kash[] = {
+ ".", ":", "break", "case", "cd", "continue", "echo", "eval", "exec", "exit",
+ "export", "for", "if", "login", "logout", "read", "readonly", "set",
+ "shift", "switch", "test", "times", "trap", "umask", "wait", "while", 0 /* +echo, -ulimit, -unset */
+ };
+ int is_kmk_shell = 0;
+#endif
+ int i;
+ char *p;
+#ifndef NDEBUG
+ char *end;
+#endif
+ char *ap;
+ const char *cap;
+ const char *cp;
+ int instring, word_has_equals, seen_nonequals, last_argument_was_empty;
+ char **new_argv = 0;
+ char *argstr = 0;
+#ifdef WINDOWS32
+ int slow_flag = 0;
+
+ if (!unixy_shell)
+ {
+ sh_cmds = sh_cmds_dos;
+ sh_chars = sh_chars_dos;
+ }
+ else
+ {
+ sh_cmds = sh_cmds_sh;
+ sh_chars = sh_chars_sh;
+ }
+#endif /* WINDOWS32 */
+
+ if (restp != NULL)
+ *restp = NULL;
+
+ /* Make sure not to bother processing an empty line but stop at newline. */
+ while (ISBLANK (*line))
+ ++line;
+ if (*line == '\0')
+ return 0;
+
+ if (shellflags == 0)
+ shellflags = posix_pedantic ? "-ec" : "-c";
+
+ /* See if it is safe to parse commands internally. */
+#ifdef KMK /* kmk_ash and kmk_kash are both fine, kmk_ash is the default btw. */
+ if (shell == 0)
+ {
+ is_kmk_shell = 1;
+ shell = (char *)get_default_kbuild_shell ();
+ }
+ else if (!strcmp (shell, get_default_kbuild_shell()))
+ is_kmk_shell = 1;
+ else
+ {
+ const char *psz = strstr (shell, "/kmk_ash");
+ if (psz)
+ psz += sizeof ("/kmk_ash") - 1;
+ else
+ {
+ psz = strstr (shell, "/kmk_kash");
+ if (psz)
+ psz += sizeof ("/kmk_kash") - 1;
+ }
+# if defined (__OS2__) || defined (_WIN32) || defined (WINDOWS32)
+ is_kmk_shell = psz && (*psz == '\0' || !stricmp (psz, ".exe"));
+# else
+ is_kmk_shell = psz && *psz == '\0';
+# endif
+ }
+ if (is_kmk_shell)
+ {
+ sh_chars = sh_chars_kash;
+ sh_cmds = sh_cmds_kash;
+ }
+#else /* !KMK */
+ if (shell == 0)
+ shell = default_shell;
+#endif /* !KMK */
+#ifdef WINDOWS32
+ else if (strcmp (shell, default_shell))
+ {
+ char *s1 = _fullpath (NULL, shell, 0);
+ char *s2 = _fullpath (NULL, default_shell, 0);
+
+ slow_flag = strcmp ((s1 ? s1 : ""), (s2 ? s2 : ""));
+
+ free (s1);
+ free (s2);
+ }
+ if (slow_flag)
+ goto slow;
+#else /* not WINDOWS32 */
+#if defined (__MSDOS__) || defined (__EMX__)
+ else if (strcasecmp (shell, default_shell))
+ {
+ extern int _is_unixy_shell (const char *_path);
+
+ DB (DB_BASIC, (_("$SHELL changed (was '%s', now '%s')\n"),
+ default_shell, shell));
+ unixy_shell = _is_unixy_shell (shell);
+ /* we must allocate a copy of shell: construct_command_argv() will free
+ * shell after this function returns. */
+ default_shell = xstrdup (shell);
+ }
+# ifdef KMK
+ if (is_kmk_shell)
+ { /* done above already */ }
+ else
+# endif
+ if (unixy_shell)
+ {
+ sh_chars = sh_chars_sh;
+ sh_cmds = sh_cmds_sh;
+ }
+ else
+ {
+ sh_chars = sh_chars_dos;
+ sh_cmds = sh_cmds_dos;
+# ifdef __EMX__
+ if (_osmode == OS2_MODE)
+ {
+ sh_chars = sh_chars_os2;
+ sh_cmds = sh_cmds_os2;
+ }
+# endif
+ }
+#else /* !__MSDOS__ */
+ else if (strcmp (shell, default_shell))
+ goto slow;
+#endif /* !__MSDOS__ && !__EMX__ */
+#endif /* not WINDOWS32 */
+
+ if (ifs)
+ for (cap = ifs; *cap != '\0'; ++cap)
+ if (*cap != ' ' && *cap != '\t' && *cap != '\n')
+ goto slow;
+
+ if (shellflags)
+ if (shellflags[0] != '-'
+ || ((shellflags[1] != 'c' || shellflags[2] != '\0')
+ && (shellflags[1] != 'e' || shellflags[2] != 'c' || shellflags[3] != '\0')))
+ goto slow;
+
+ i = strlen (line) + 1;
+
+ /* More than 1 arg per character is impossible. */
+ new_argv = xmalloc (i * sizeof (char *));
+
+ /* All the args can fit in a buffer as big as LINE is. */
+ ap = new_argv[0] = argstr = xmalloc (i);
+#ifndef NDEBUG
+ end = ap + i;
+#endif
+
+ /* I is how many complete arguments have been found. */
+ i = 0;
+ instring = word_has_equals = seen_nonequals = last_argument_was_empty = 0;
+ for (p = line; *p != '\0'; ++p)
+ {
+ assert (ap <= end);
+
+ if (instring)
+ {
+ /* Inside a string, just copy any char except a closing quote
+ or a backslash-newline combination. */
+ if (*p == instring)
+ {
+ instring = 0;
+ if (ap == new_argv[0] || *(ap-1) == '\0')
+ last_argument_was_empty = 1;
+ }
+ else if (*p == '\\' && p[1] == '\n')
+ {
+ /* Backslash-newline is handled differently depending on what
+ kind of string we're in: inside single-quoted strings you
+ keep them; in double-quoted strings they disappear. For
+ DOS/Windows/OS2, if we don't have a POSIX shell, we keep the
+ pre-POSIX behavior of removing the backslash-newline. */
+ if (instring == '"'
+#if defined (__MSDOS__) || defined (__EMX__) || defined (WINDOWS32)
+ || !unixy_shell
+#endif
+ )
+ ++p;
+ else
+ {
+ *(ap++) = *(p++);
+ *(ap++) = *p;
+ }
+ }
+ else if (*p == '\n' && restp != NULL)
+ {
+ /* End of the command line. */
+ *restp = p;
+ goto end_of_line;
+ }
+ /* Backslash, $, and ` are special inside double quotes.
+ If we see any of those, punt.
+ But on MSDOS, if we use COMMAND.COM, double and single
+ quotes have the same effect. */
+ else if (instring == '"' && strchr ("\\$`", *p) != 0 && unixy_shell)
+ goto slow;
+#ifdef WINDOWS32
+ /* Quoted wildcard characters must be passed quoted to the
+ command, so give up the fast route. */
+ else if (instring == '"' && strchr ("*?", *p) != 0 && !unixy_shell)
+ goto slow;
+ else if (instring == '"' && strncmp (p, "\\\"", 2) == 0)
+ *ap++ = *++p;
+#endif
+ else
+ *ap++ = *p;
+ }
+ else if (strchr (sh_chars, *p) != 0)
+#ifdef KMK
+ {
+ /* Tilde is only special if at the start of a path spec,
+ i.e. don't get excited when we by 8.3 files on windows. */
+ if ( *p == '~'
+ && p > line
+ && !ISSPACE (p[-1])
+ && p[-1] != '='
+ && p[-1] != ':'
+ && p[-1] != '"'
+ && p[-1] != '\'')
+ *ap++ = *p;
+ else
+ /* Not inside a string, but it's a special char. */
+ goto slow;
+ }
+#else /* !KMK */
+ /* Not inside a string, but it's a special char. */
+ goto slow;
+#endif /* !KMK */
+ else if (one_shell && *p == '\n')
+ /* In .ONESHELL mode \n is a separator like ; or && */
+ goto slow;
+#ifdef __MSDOS__
+ else if (*p == '.' && p[1] == '.' && p[2] == '.' && p[3] != '.')
+ /* '...' is a wildcard in DJGPP. */
+ goto slow;
+#endif
+ else
+ /* Not a special char. */
+ switch (*p)
+ {
+ case '=':
+ /* Equals is a special character in leading words before the
+ first word with no equals sign in it. This is not the case
+ with sh -k, but we never get here when using nonstandard
+ shell flags. */
+ if (! seen_nonequals && unixy_shell)
+ goto slow;
+ word_has_equals = 1;
+ *ap++ = '=';
+ break;
+
+ case '\\':
+ /* Backslash-newline has special case handling, ref POSIX.
+ We're in the fastpath, so emulate what the shell would do. */
+ if (p[1] == '\n')
+ {
+ /* Throw out the backslash and newline. */
+ ++p;
+
+ /* At the beginning of the argument, skip any whitespace other
+ than newline before the start of the next word. */
+ if (ap == new_argv[i])
+ while (ISBLANK (p[1]))
+ ++p;
+ }
+#ifdef WINDOWS32
+ /* Backslash before whitespace is not special if our shell
+ is not Unixy. */
+ else if (ISSPACE (p[1]) && !unixy_shell)
+ {
+ *ap++ = *p;
+ break;
+ }
+#endif
+ else if (p[1] != '\0')
+ {
+#ifdef HAVE_DOS_PATHS
+ /* Only remove backslashes before characters special to Unixy
+ shells. All other backslashes are copied verbatim, since
+ they are probably DOS-style directory separators. This
+ still leaves a small window for problems, but at least it
+ should work for the vast majority of naive users. */
+
+#ifdef __MSDOS__
+ /* A dot is only special as part of the "..."
+ wildcard. */
+ if (strneq (p + 1, ".\\.\\.", 5))
+ {
+ *ap++ = '.';
+ *ap++ = '.';
+ p += 4;
+ }
+ else
+#endif
+ if (p[1] != '\\' && p[1] != '\''
+ && !ISSPACE (p[1])
+# ifdef KMK
+ && strchr (sh_chars, p[1]) == 0
+ && (p[1] != '"' || !unixy_shell))
+# else
+ && strchr (sh_chars_sh, p[1]) == 0)
+# endif
+ /* back up one notch, to copy the backslash */
+ --p;
+#endif /* HAVE_DOS_PATHS */
+
+ /* Copy and skip the following char. */
+ *ap++ = *++p;
+ }
+ break;
+
+ case '\'':
+ case '"':
+ instring = *p;
+ break;
+
+ case '\n':
+ if (restp != NULL)
+ {
+ /* End of the command line. */
+ *restp = p;
+ goto end_of_line;
+ }
+ else
+ /* Newlines are not special. */
+ *ap++ = '\n';
+ break;
+
+ case ' ':
+ case '\t':
+ /* We have the end of an argument.
+ Terminate the text of the argument. */
+ *ap++ = '\0';
+ new_argv[++i] = ap;
+ last_argument_was_empty = 0;
+
+ /* Update SEEN_NONEQUALS, which tells us if every word
+ heretofore has contained an '='. */
+ seen_nonequals |= ! word_has_equals;
+ if (word_has_equals && ! seen_nonequals)
+ /* An '=' in a word before the first
+ word without one is magical. */
+ goto slow;
+ word_has_equals = 0; /* Prepare for the next word. */
+
+ /* If this argument is the command name,
+ see if it is a built-in shell command.
+ If so, have the shell handle it. */
+ if (i == 1)
+ {
+ register int j;
+ for (j = 0; sh_cmds[j] != 0; ++j)
+ {
+ if (streq (sh_cmds[j], new_argv[0]))
+ goto slow;
+#if defined(__EMX__) || defined(WINDOWS32)
+ /* Non-Unix shells are case insensitive. */
+ if (!unixy_shell
+ && strcasecmp (sh_cmds[j], new_argv[0]) == 0)
+ goto slow;
+#endif
+ }
+ }
+
+ /* Skip whitespace chars, but not newlines. */
+ while (ISBLANK (p[1]))
+ ++p;
+ break;
+
+ default:
+ *ap++ = *p;
+ break;
+ }
+ }
+ end_of_line:
+
+ if (instring)
+ /* Let the shell deal with an unterminated quote. */
+ goto slow;
+
+ /* Terminate the last argument and the argument list. */
+
+ *ap = '\0';
+ if (new_argv[i][0] != '\0' || last_argument_was_empty)
+ ++i;
+ new_argv[i] = 0;
+
+ if (i == 1)
+ {
+ register int j;
+ for (j = 0; sh_cmds[j] != 0; ++j)
+ if (streq (sh_cmds[j], new_argv[0]))
+ goto slow;
+ }
+
+ if (new_argv[0] == 0)
+ {
+ /* Line was empty. */
+ free (argstr);
+ free (new_argv);
+ return 0;
+ }
+
+ return new_argv;
+
+ slow:;
+ /* We must use the shell. */
+
+ if (new_argv != 0)
+ {
+ /* Free the old argument list we were working on. */
+ free (argstr);
+ free (new_argv);
+ }
+
+#ifdef __MSDOS__
+ execute_by_shell = 1; /* actually, call 'system' if shell isn't unixy */
+#endif
+
+#ifdef _AMIGA
+ {
+ char *ptr;
+ char *buffer;
+ char *dptr;
+
+ buffer = xmalloc (strlen (line)+1);
+
+ ptr = line;
+ for (dptr=buffer; *ptr; )
+ {
+ if (*ptr == '\\' && ptr[1] == '\n')
+ ptr += 2;
+ else if (*ptr == '@') /* Kludge: multiline commands */
+ {
+ ptr += 2;
+ *dptr++ = '\n';
+ }
+ else
+ *dptr++ = *ptr++;
+ }
+ *dptr = 0;
+
+ new_argv = xmalloc (2 * sizeof (char *));
+ new_argv[0] = buffer;
+ new_argv[1] = 0;
+ }
+#else /* Not Amiga */
+#ifdef WINDOWS32
+ /*
+ * Not eating this whitespace caused things like
+ *
+ * sh -c "\n"
+ *
+ * which gave the shell fits. I think we have to eat
+ * whitespace here, but this code should be considered
+ * suspicious if things start failing....
+ */
+
+ /* Make sure not to bother processing an empty line. */
+ NEXT_TOKEN (line);
+ if (*line == '\0')
+ return 0;
+#endif /* WINDOWS32 */
+
+ {
+ /* SHELL may be a multi-word command. Construct a command line
+ "$(SHELL) $(.SHELLFLAGS) LINE", with all special chars in LINE escaped.
+ Then recurse, expanding this command line to get the final
+ argument list. */
+
+ char *new_line;
+ unsigned int shell_len = strlen (shell);
+ unsigned int line_len = strlen (line);
+ unsigned int sflags_len = shellflags ? strlen (shellflags) : 0;
+#ifdef WINDOWS32
+ char *command_ptr = NULL; /* used for batch_mode_shell mode */
+#endif
+
+# ifdef __EMX__ /* is this necessary? */
+ if (!unixy_shell && shellflags)
+ ((char *)shellflags)[0] = '/'; /* "/c" */
+# endif
+
+ /* In .ONESHELL mode we are allowed to throw the entire current
+ recipe string at a single shell and trust that the user
+ has configured the shell and shell flags, and formatted
+ the string, appropriately. */
+ if (one_shell)
+ {
+ /* If the shell is Bourne compatible, we must remove and ignore
+ interior special chars [@+-] because they're meaningless to
+ the shell itself. If, however, we're in .ONESHELL mode and
+ have changed SHELL to something non-standard, we should
+ leave those alone because they could be part of the
+ script. In this case we must also leave in place
+ any leading [@+-] for the same reason. */
+
+ /* Remove and ignore interior prefix chars [@+-] because they're
+ meaningless given a single shell. */
+#if defined __MSDOS__ || defined (__EMX__)
+ if (unixy_shell) /* the test is complicated and we already did it */
+#else
+ if (is_bourne_compatible_shell (shell)
+#ifdef WINDOWS32
+ /* If we didn't find any sh.exe, don't behave is if we did! */
+ && !no_default_sh_exe
+#endif
+ )
+#endif
+ {
+ const char *f = line;
+ char *t = line;
+
+ /* Copy the recipe, removing and ignoring interior prefix chars
+ [@+-]: they're meaningless in .ONESHELL mode. */
+ while (f[0] != '\0')
+ {
+ int esc = 0;
+
+ /* This is the start of a new recipe line. Skip whitespace
+ and prefix characters but not newlines. */
+#ifndef CONFIG_WITH_COMMANDS_FUNC
+ while (ISBLANK (*f) || *f == '-' || *f == '@' || *f == '+')
+#else
+ char ch;
+ while (ISBLANK ((ch = *f)) || ch == '-' || ch == '@' || ch == '+' || ch == '%')
+#endif
+ ++f;
+
+ /* Copy until we get to the next logical recipe line. */
+ while (*f != '\0')
+ {
+ *(t++) = *(f++);
+ if (f[-1] == '\\')
+ esc = !esc;
+ else
+ {
+ /* On unescaped newline, we're done with this line. */
+ if (f[-1] == '\n' && ! esc)
+ break;
+
+ /* Something else: reset the escape sequence. */
+ esc = 0;
+ }
+ }
+ }
+ *t = '\0';
+ }
+#ifdef WINDOWS32
+ else /* non-Posix shell (cmd.exe etc.) */
+ {
+ const char *f = line;
+ char *t = line;
+ char *tstart = t;
+ int temp_fd;
+ FILE* batch = NULL;
+ int id = GetCurrentProcessId ();
+ PATH_VAR(fbuf);
+
+ /* Generate a file name for the temporary batch file. */
+ sprintf (fbuf, "make%d", id);
+ *batch_filename = create_batch_file (fbuf, 0, &temp_fd);
+ DB (DB_JOBS, (_("Creating temporary batch file %s\n"),
+ *batch_filename));
+
+ /* Create a FILE object for the batch file, and write to it the
+ commands to be executed. Put the batch file in TEXT mode. */
+ _setmode (temp_fd, _O_TEXT);
+ batch = _fdopen (temp_fd, "wt");
+ fputs ("@echo off\n", batch);
+ DB (DB_JOBS, (_("Batch file contents:\n\t@echo off\n")));
+
+ /* Copy the recipe, removing and ignoring interior prefix chars
+ [@+-]: they're meaningless in .ONESHELL mode. */
+ while (*f != '\0')
+ {
+ /* This is the start of a new recipe line. Skip whitespace
+ and prefix characters but not newlines. */
+#ifndef CONFIG_WITH_COMMANDS_FUNC
+ while (ISBLANK (*f) || *f == '-' || *f == '@' || *f == '+')
+#else
+ char ch;
+ while (ISBLANK ((ch = *f)) || ch == '-' || ch == '@' || ch == '+' || ch == '%')
+#endif
+ ++f;
+
+ /* Copy until we get to the next logical recipe line. */
+ while (*f != '\0')
+ {
+ /* Remove the escaped newlines in the command, and the
+ blanks that follow them. Windows shells cannot handle
+ escaped newlines. */
+ if (*f == '\\' && f[1] == '\n')
+ {
+ f += 2;
+ while (ISBLANK (*f))
+ ++f;
+ }
+ *(t++) = *(f++);
+ /* On an unescaped newline, we're done with this
+ line. */
+ if (f[-1] == '\n')
+ break;
+ }
+ /* Write another line into the batch file. */
+ if (t > tstart)
+ {
+ char c = *t;
+ *t = '\0';
+ fputs (tstart, batch);
+ DB (DB_JOBS, ("\t%s", tstart));
+ tstart = t;
+ *t = c;
+ }
+ }
+ DB (DB_JOBS, ("\n"));
+ fclose (batch);
+
+ /* Create an argv list for the shell command line that
+ will run the batch file. */
+ new_argv = xmalloc (2 * sizeof (char *));
+ new_argv[0] = xstrdup (*batch_filename);
+ new_argv[1] = NULL;
+ return new_argv;
+ }
+#endif /* WINDOWS32 */
+ /* Create an argv list for the shell command line. */
+ {
+ int n = 0;
+
+ new_argv = xmalloc ((4 + sflags_len/2) * sizeof (char *));
+ new_argv[n++] = xstrdup (shell);
+
+ /* Chop up the shellflags (if any) and assign them. */
+ if (! shellflags)
+ new_argv[n++] = xstrdup ("");
+ else
+ {
+ const char *s = shellflags;
+ char *t;
+ unsigned int len;
+ while ((t = find_next_token (&s, &len)) != 0)
+ new_argv[n++] = xstrndup (t, len);
+ }
+
+ /* Set the command to invoke. */
+ new_argv[n++] = line;
+ new_argv[n++] = NULL;
+ }
+ return new_argv;
+ }
+
+ new_line = xmalloc ((shell_len*2) + 1 + sflags_len + 1
+ + (line_len*2) + 1);
+ ap = new_line;
+ /* Copy SHELL, escaping any characters special to the shell. If
+ we don't escape them, construct_command_argv_internal will
+ recursively call itself ad nauseam, or until stack overflow,
+ whichever happens first. */
+ for (cp = shell; *cp != '\0'; ++cp)
+ {
+ if (strchr (sh_chars, *cp) != 0)
+ *(ap++) = '\\';
+ *(ap++) = *cp;
+ }
+ *(ap++) = ' ';
+ if (shellflags)
+ memcpy (ap, shellflags, sflags_len);
+ ap += sflags_len;
+ *(ap++) = ' ';
+#ifdef WINDOWS32
+ command_ptr = ap;
+#endif
+ for (p = line; *p != '\0'; ++p)
+ {
+ if (restp != NULL && *p == '\n')
+ {
+ *restp = p;
+ break;
+ }
+ else if (*p == '\\' && p[1] == '\n')
+ {
+ /* POSIX says we keep the backslash-newline. If we don't have a
+ POSIX shell on DOS/Windows/OS2, mimic the pre-POSIX behavior
+ and remove the backslash/newline. */
+#if defined (__MSDOS__) || defined (__EMX__) || defined (WINDOWS32)
+# define PRESERVE_BSNL unixy_shell
+#else
+# define PRESERVE_BSNL 1
+#endif
+ if (PRESERVE_BSNL)
+ {
+ *(ap++) = '\\';
+ /* Only non-batch execution needs another backslash,
+ because it will be passed through a recursive
+ invocation of this function. */
+ if (!batch_mode_shell)
+ *(ap++) = '\\';
+ *(ap++) = '\n';
+ }
+ ++p;
+ continue;
+ }
+
+ /* DOS shells don't know about backslash-escaping. */
+ if (unixy_shell && !batch_mode_shell &&
+ (*p == '\\' || *p == '\'' || *p == '"'
+ || ISSPACE (*p)
+ || strchr (sh_chars, *p) != 0))
+ *ap++ = '\\';
+#ifdef __MSDOS__
+ else if (unixy_shell && strneq (p, "...", 3))
+ {
+ /* The case of '...' wildcard again. */
+ strcpy (ap, "\\.\\.\\");
+ ap += 5;
+ p += 2;
+ }
+#endif
+ *ap++ = *p;
+ }
+ if (ap == new_line + shell_len + sflags_len + 2)
+ {
+ /* Line was empty. */
+ free (new_line);
+ return 0;
+ }
+ *ap = '\0';
+
+#ifdef WINDOWS32
+ /* Some shells do not work well when invoked as 'sh -c xxx' to run a
+ command line (e.g. Cygnus GNUWIN32 sh.exe on WIN32 systems). In these
+ cases, run commands via a script file. */
+ if (just_print_flag && !(flags & COMMANDS_RECURSE))
+ {
+ /* Need to allocate new_argv, although it's unused, because
+ start_job_command will want to free it and its 0'th element. */
+ new_argv = xmalloc (2 * sizeof (char *));
+ new_argv[0] = xstrdup ("");
+ new_argv[1] = NULL;
+ }
+ else if ((no_default_sh_exe || batch_mode_shell) && batch_filename)
+ {
+ int temp_fd;
+ FILE* batch = NULL;
+ int id = GetCurrentProcessId ();
+ PATH_VAR (fbuf);
+
+ /* create a file name */
+ sprintf (fbuf, "make%d", id);
+ *batch_filename = create_batch_file (fbuf, unixy_shell, &temp_fd);
+
+ DB (DB_JOBS, (_("Creating temporary batch file %s\n"),
+ *batch_filename));
+
+ /* Create a FILE object for the batch file, and write to it the
+ commands to be executed. Put the batch file in TEXT mode. */
+ _setmode (temp_fd, _O_TEXT);
+ batch = _fdopen (temp_fd, "wt");
+ if (!unixy_shell)
+ fputs ("@echo off\n", batch);
+ fputs (command_ptr, batch);
+ fputc ('\n', batch);
+ fclose (batch);
+ DB (DB_JOBS, (_("Batch file contents:%s\n\t%s\n"),
+ !unixy_shell ? "\n\t@echo off" : "", command_ptr));
+
+ /* create argv */
+ new_argv = xmalloc (3 * sizeof (char *));
+ if (unixy_shell)
+ {
+ new_argv[0] = xstrdup (shell);
+ new_argv[1] = *batch_filename; /* only argv[0] gets freed later */
+ }
+ else
+ {
+ new_argv[0] = xstrdup (*batch_filename);
+ new_argv[1] = NULL;
+ }
+ new_argv[2] = NULL;
+ }
+ else
+#endif /* WINDOWS32 */
+
+ if (unixy_shell)
+ new_argv = construct_command_argv_internal (new_line, 0, 0, 0, 0,
+ flags, 0);
+
+#ifdef __EMX__
+ else if (!unixy_shell)
+ {
+ /* new_line is local, must not be freed therefore
+ We use line here instead of new_line because we run the shell
+ manually. */
+ size_t line_len = strlen (line);
+ char *p = new_line;
+ char *q = new_line;
+ memcpy (new_line, line, line_len + 1);
+ /* Replace all backslash-newline combination and also following tabs.
+ Important: stop at the first '\n' because that's what the loop above
+ did. The next line starting at restp[0] will be executed during the
+ next call of this function. */
+ while (*q != '\0' && *q != '\n')
+ {
+ if (q[0] == '\\' && q[1] == '\n')
+ q += 2; /* remove '\\' and '\n' */
+ else
+ *p++ = *q++;
+ }
+ *p = '\0';
+
+# ifndef NO_CMD_DEFAULT
+ if (strnicmp (new_line, "echo", 4) == 0
+ && (new_line[4] == ' ' || new_line[4] == '\t'))
+ {
+ /* the builtin echo command: handle it separately */
+ size_t echo_len = line_len - 5;
+ char *echo_line = new_line + 5;
+
+ /* special case: echo 'x="y"'
+ cmd works this way: a string is printed as is, i.e., no quotes
+ are removed. But autoconf uses a command like echo 'x="y"' to
+ determine whether make works. autoconf expects the output x="y"
+ so we will do exactly that.
+ Note: if we do not allow cmd to be the default shell
+ we do not need this kind of voodoo */
+ if (echo_line[0] == '\''
+ && echo_line[echo_len - 1] == '\''
+ && strncmp (echo_line + 1, "ac_maketemp=",
+ strlen ("ac_maketemp=")) == 0)
+ {
+ /* remove the enclosing quotes */
+ memmove (echo_line, echo_line + 1, echo_len - 2);
+ echo_line[echo_len - 2] = '\0';
+ }
+ }
+# endif
+
+ {
+ /* Let the shell decide what to do. Put the command line into the
+ 2nd command line argument and hope for the best ;-) */
+ size_t sh_len = strlen (shell);
+
+ /* exactly 3 arguments + NULL */
+ new_argv = xmalloc (4 * sizeof (char *));
+ /* Exactly strlen(shell) + strlen("/c") + strlen(line) + 3 times
+ the trailing '\0' */
+ new_argv[0] = xmalloc (sh_len + line_len + 5);
+ memcpy (new_argv[0], shell, sh_len + 1);
+ new_argv[1] = new_argv[0] + sh_len + 1;
+ memcpy (new_argv[1], "/c", 3);
+ new_argv[2] = new_argv[1] + 3;
+ memcpy (new_argv[2], new_line, line_len + 1);
+ new_argv[3] = NULL;
+ }
+ }
+#elif defined(__MSDOS__)
+ else
+ {
+ /* With MSDOS shells, we must construct the command line here
+ instead of recursively calling ourselves, because we
+ cannot backslash-escape the special characters (see above). */
+ new_argv = xmalloc (sizeof (char *));
+ line_len = strlen (new_line) - shell_len - sflags_len - 2;
+ new_argv[0] = xmalloc (line_len + 1);
+ strncpy (new_argv[0],
+ new_line + shell_len + sflags_len + 2, line_len);
+ new_argv[0][line_len] = '\0';
+ }
+#else
+ else
+ fatal (NILF, CSTRLEN (__FILE__) + INTSTR_LENGTH,
+ _("%s (line %d) Bad shell context (!unixy && !batch_mode_shell)\n"),
+ __FILE__, __LINE__);
+#endif
+
+ free (new_line);
+ }
+#endif /* ! AMIGA */
+
+ return new_argv;
+}
+#endif /* !VMS */
+
+/* Figure out the argument list necessary to run LINE as a command. Try to
+ avoid using a shell. This routine handles only ' quoting, and " quoting
+ when no backslash, $ or ' characters are seen in the quotes. Starting
+ quotes may be escaped with a backslash. If any of the characters in
+ sh_chars is seen, or any of the builtin commands listed in sh_cmds
+ is the first word of a line, the shell is used.
+
+ If RESTP is not NULL, *RESTP is set to point to the first newline in LINE.
+ If *RESTP is NULL, newlines will be ignored.
+
+ FILE is the target whose commands these are. It is used for
+ variable expansion for $(SHELL) and $(IFS). */
+
+char **
+construct_command_argv (char *line, char **restp, struct file *file,
+ int cmd_flags, char **batch_filename)
+{
+ char *shell, *ifs, *shellflags;
+ char **argv;
+
+#ifdef VMS
+ char *cptr;
+ int argc;
+
+ argc = 0;
+ cptr = line;
+ for (;;)
+ {
+ while ((*cptr != 0) && (ISSPACE (*cptr)))
+ cptr++;
+ if (*cptr == 0)
+ break;
+ while ((*cptr != 0) && (!ISSPACE (*cptr)))
+ cptr++;
+ argc++;
+ }
+
+ argv = xmalloc (argc * sizeof (char *));
+ if (argv == 0)
+ abort ();
+
+ cptr = line;
+ argc = 0;
+ for (;;)
+ {
+ while ((*cptr != 0) && (ISSPACE (*cptr)))
+ cptr++;
+ if (*cptr == 0)
+ break;
+ DB (DB_JOBS, ("argv[%d] = [%s]\n", argc, cptr));
+ argv[argc++] = cptr;
+ while ((*cptr != 0) && (!ISSPACE (*cptr)))
+ cptr++;
+ if (*cptr != 0)
+ *cptr++ = 0;
+ }
+#else
+ {
+ /* Turn off --warn-undefined-variables while we expand SHELL and IFS. */
+ int save = warn_undefined_variables_flag;
+ warn_undefined_variables_flag = 0;
+
+ shell = allocated_variable_expand_for_file ("$(SHELL)", file);
+#ifdef WINDOWS32
+ /*
+ * Convert to forward slashes so that construct_command_argv_internal()
+ * is not confused.
+ */
+ if (shell)
+ {
+# if 1 /* bird */
+ unix_slashes (shell);
+# else
+ char *p = w32ify (shell, 0);
+ strcpy (shell, p);
+# endif
+ }
+#endif
+#ifdef __EMX__
+ {
+ static const char *unixroot = NULL;
+ static const char *last_shell = "";
+ static int init = 0;
+ if (init == 0)
+ {
+ unixroot = getenv ("UNIXROOT");
+ /* unixroot must be NULL or not empty */
+ if (unixroot && unixroot[0] == '\0') unixroot = NULL;
+ init = 1;
+ }
+
+ /* if we have an unixroot drive and if shell is not default_shell
+ (which means it's either cmd.exe or the test has already been
+ performed) and if shell is an absolute path without drive letter,
+ try whether it exists e.g.: if "/bin/sh" does not exist use
+ "$UNIXROOT/bin/sh" instead. */
+ if (unixroot && shell && strcmp (shell, last_shell) != 0
+ && (shell[0] == '/' || shell[0] == '\\'))
+ {
+ /* trying a new shell, check whether it exists */
+ size_t size = strlen (shell);
+ char *buf = xmalloc (size + 7);
+ memcpy (buf, shell, size);
+ memcpy (buf + size, ".exe", 5); /* including the trailing '\0' */
+ if (access (shell, F_OK) != 0 && access (buf, F_OK) != 0)
+ {
+ /* try the same for the unixroot drive */
+ memmove (buf + 2, buf, size + 5);
+ buf[0] = unixroot[0];
+ buf[1] = unixroot[1];
+ if (access (buf, F_OK) == 0)
+ /* we have found a shell! */
+ /* free(shell); */
+ shell = buf;
+ else
+ free (buf);
+ }
+ else
+ free (buf);
+ }
+ }
+#endif /* __EMX__ */
+
+ shellflags = allocated_variable_expand_for_file ("$(.SHELLFLAGS)", file);
+ ifs = allocated_variable_expand_for_file ("$(IFS)", file);
+
+ warn_undefined_variables_flag = save;
+ }
+
+# ifdef CONFIG_WITH_KMK_BUILTIN
+ /* If it's a kmk_builtin command, make sure we're treated like a
+ unix shell and and don't get batch files. */
+ if ( ( !unixy_shell
+ || batch_mode_shell
+# ifdef WINDOWS32
+ || no_default_sh_exe
+# endif
+ )
+ && line
+ && !strncmp (line, "kmk_builtin_", sizeof("kmk_builtin_") - 1))
+ {
+ int saved_batch_mode_shell = batch_mode_shell;
+ int saved_unixy_shell = unixy_shell;
+# ifdef WINDOWS32
+ int saved_no_default_sh_exe = no_default_sh_exe;
+ no_default_sh_exe = 0;
+# endif
+ unixy_shell = 1;
+ batch_mode_shell = 0;
+ argv = construct_command_argv_internal (line, restp, shell, shellflags, ifs,
+ cmd_flags, batch_filename);
+ batch_mode_shell = saved_batch_mode_shell;
+ unixy_shell = saved_unixy_shell;
+# ifdef WINDOWS32
+ no_default_sh_exe = saved_no_default_sh_exe;
+# endif
+ }
+ else
+# endif /* CONFIG_WITH_KMK_BUILTIN */
+ argv = construct_command_argv_internal (line, restp, shell, shellflags, ifs,
+ cmd_flags, batch_filename);
+
+ free (shell);
+ free (shellflags);
+ free (ifs);
+#endif /* !VMS */
+ return argv;
+}
+
+#if !defined(HAVE_DUP2) && !defined(_AMIGA)
+int
+dup2 (int old, int new)
+{
+ int fd;
+
+ (void) close (new);
+ EINTRLOOP (fd, dup (old));
+ if (fd != new)
+ {
+ (void) close (fd);
+ errno = EMFILE;
+ return -1;
+ }
+
+ return fd;
+}
+#endif /* !HAVE_DUP2 && !_AMIGA */
+
+#ifdef CONFIG_WITH_PRINT_TIME_SWITCH
+/* Prints the time elapsed while executing the commands for the given job. */
+void print_job_time (struct child *c)
+{
+ if ( !handling_fatal_signal
+ && print_time_min != -1
+ && c->start_ts != -1)
+ {
+ big_int elapsed = nano_timestamp () - c->start_ts;
+ if (elapsed >= print_time_min * BIG_INT_C(1000000000))
+ {
+ char buf[64];
+ int len = format_elapsed_nano (buf, sizeof (buf), elapsed);
+ if (len > print_time_width)
+ print_time_width = len;
+ message (1, print_time_width + strlen (c->file->name),
+ _("%*s - %s"), print_time_width, buf, c->file->name);
+ }
+ }
+}
+#endif
+
+/* On VMS systems, include special VMS functions. */
+
+#ifdef VMS
+#include "vmsjobs.c"
+#endif