#include "make.h"
#include <assert.h>
#include "job.h"
#include "debug.h"
#include "filedef.h"
#include "commands.h"
#include "variable.h"
#include "debug.h"
#include <string.h>
#if USE_POSIX_SPAWN
#include <spawn.h>
#endif
#ifdef WINDOWS32
#include <windows.h>
char *default_shell = "sh.exe";
int no_default_sh_exe = 1;
int batch_mode_shell = 1;
HANDLE main_thread;
#elif defined (_AMIGA)
char default_shell[] = "";
extern int MyExecute (char **);
int batch_mode_shell = 0;
#elif defined (__MSDOS__)
char *default_shell = "command.com";
int batch_mode_shell = 0;
#elif defined (__EMX__)
char *default_shell = "/bin/sh";
int batch_mode_shell = 0;
#elif defined (VMS)
# include <descrip.h>
char default_shell[] = "";
int batch_mode_shell = 0;
#elif defined (__riscos__)
char default_shell[] = "";
int batch_mode_shell = 0;
#else
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
#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
#ifdef VMS
# ifndef __GNUC__
# include <processes.h>
# endif
# include <starlet.h>
# include <lib$routines.h>
static void vmsWaitForChildren PARAMS ((int *));
#endif
#ifdef WINDOWS32
# include <windows.h>
# include <io.h>
# include <process.h>
# include "sub_proc.h"
# include "w32err.h"
# include "pathstuff.h"
#endif
#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
# ifdef HAVE_WAIT3
# ifndef wait3
extern int wait3 ();
# endif
# define WAIT_NOHANG(status) wait3 ((status), WNOHANG, (struct rusage *) 0)
# endif
#endif
#if !defined (wait) && !defined (POSIX)
extern 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
# 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
#ifndef HAVE_UNISTD_H
extern int dup2 ();
extern int execve ();
extern void _exit ();
# ifndef VMS
extern int geteuid ();
extern int getegid ();
extern int setgid ();
extern int getgid ();
# endif
#endif
extern char *allocated_variable_expand_for_file PARAMS ((char *line, struct file *file));
extern int getloadavg PARAMS ((double loadavg[], int nelem));
extern int start_remote_job PARAMS ((char **argv, char **envp, int stdin_fd,
int *is_remote, int *id_ptr, int *used_stdin));
extern int start_remote_job_p PARAMS ((int));
extern int remote_status PARAMS ((int *exit_code_ptr, int *signal_ptr,
int *coredump_ptr, int block));
RETSIGTYPE child_handler PARAMS ((int));
static void free_child PARAMS ((struct child *));
static void start_job_command PARAMS ((struct child *child));
static int load_too_high PARAMS ((void));
static int job_next_command PARAMS ((struct child *));
static int start_waiting_job PARAMS ((struct child *));
struct child *children = 0;
unsigned int job_slots_used = 0;
static int good_stdin_used = 0;
static struct child *waiting_jobs = 0;
int unixy_shell = 1;
unsigned long job_counter = 0;
unsigned int jobserver_tokens = 0;
#ifdef WINDOWS32
int
w32_kill(int pid, int sig)
{
return ((process_kill((HANDLE)pid, sig) == TRUE) ? 0 : -1);
}
static char *
create_batch_file (char const *base, int unixy, int *fd)
{
const char *const ext = unixy ? "sh" : "bat";
const char *error = NULL;
char temp_path[MAXPATHLEN];
unsigned path_size = GetTempPath(sizeof temp_path, temp_path);
int path_is_dot = 0;
unsigned uniq = 1;
const unsigned sizemax = strlen (base) + strlen (ext) + 10;
if (path_size == 0)
{
path_size = GetCurrentDirectory (sizeof temp_path, temp_path);
path_is_dot = 1;
}
while (path_size > 0 &&
path_size + sizemax < sizeof temp_path &&
uniq < 0x10000)
{
unsigned size = sprintf (temp_path + path_size,
"%s%s-%x.%s",
temp_path[path_size - 1] == '\\' ? "" : "\\",
base, uniq, ext);
HANDLE h = CreateFile (temp_path,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
CREATE_NEW,
FILE_ATTRIBUTE_NORMAL |
FILE_ATTRIBUTE_TEMPORARY,
NULL);
if (h == INVALID_HANDLE_VALUE)
{
const DWORD er = GetLastError();
if (er == ERROR_FILE_EXISTS || er == ERROR_ALREADY_EXISTS)
++uniq;
else if (path_is_dot == 0)
{
path_size = GetCurrentDirectory (sizeof temp_path, temp_path);
path_is_dot = 1;
}
else
{
error = map_windows32_error_to_string (er);
break;
}
}
else
{
const unsigned final_size = path_size + size + 1;
char *const path = (char *) xmalloc (final_size);
memcpy (path, temp_path, final_size);
*fd = _open_osfhandle ((long)h, 0);
if (unixy)
{
char *p;
int ch;
for (p = path; (ch = *p) != 0; ++p)
if (ch == '\\')
*p = '/';
}
return path;
}
}
*fd = -1;
if (error == NULL)
error = _("Cannot create a temporary file\n");
fatal (NILF, error);
return NULL;
}
#endif
#ifdef __EMX__
int
_is_unixy_shell (const char *path)
{
const char *known_os2shells[] = {
"cmd.exe",
"cmd",
"4os2.exe",
"4os2",
"4dos.exe",
"4dos",
"command.com",
"command",
NULL
};
const char *name = strrchr (path, '/');
const char *p = strrchr (path, '\\');
unsigned i;
if (name && p)
name = (name > p) ? name : p;
else if (p)
name = p;
else if (!name)
name = path;
if (*name == '/' || *name == '\\') name++;
i = 0;
while (known_os2shells[i] != NULL) {
if (stricmp (name, known_os2shells[i]) == 0)
return 0;
i++;
}
return 1;
}
#endif
static void
child_error (char *target_name, int exit_code, int exit_sig, int coredump,
int ignored)
{
if (ignored && silent_flag)
return;
#ifdef VMS
if (!(exit_code & 1))
error (NILF,
(ignored ? _("*** [%s] Error 0x%x (ignored)")
: _("*** [%s] Error 0x%x")),
target_name, exit_code);
#else
if (exit_sig == 0)
error (NILF, ignored ? _("[%s] Error %d (ignored)") :
_("*** [%s] Error %d"),
target_name, exit_code);
else
error (NILF, "*** [%s] %s%s",
target_name, strsignal (exit_sig),
coredump ? _(" (core dumped)") : "");
#endif
}
static unsigned int dead_children = 0;
RETSIGTYPE
child_handler (int sig UNUSED)
{
++dead_children;
if (job_rfd >= 0)
{
close (job_rfd);
job_rfd = -1;
}
#ifdef __EMX__
signal (SIGCHLD, SIG_DFL);
#endif
}
extern int shell_function_pid, shell_function_completed;
void
reap_children (int block, int err)
{
#ifndef WINDOWS32
WAIT_T status;
int reap_more = 1;
#endif
#ifdef WAIT_NOHANG
# define REAP_MORE reap_more
#else
# define REAP_MORE dead_children
#endif
while ((children != 0 || shell_function_pid != 0)
&& (block || REAP_MORE))
{
int remote = 0;
pid_t pid;
int exit_code, exit_sig, coredump;
register struct child *lastc, *c;
int child_failed;
int any_remote, any_local;
int dontcare;
if (err && block)
{
static int printed = 0;
fflush (stdout);
if (!printed)
error (NILF, _("*** Waiting for unfinished jobs...."));
printed = 1;
}
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;
DB (DB_JOBS, (_("Live child 0x%08lx (%s) PID %ld %s\n"),
(unsigned long int) c, c->file->name,
(long) c->pid, c->remote ? _(" (remote)") : ""));
#ifdef VMS
break;
#endif
}
if (any_remote)
pid = remote_status (&exit_code, &exit_sig, &coredump, 0);
else
pid = 0;
if (pid > 0)
remote = 1;
else if (pid < 0)
{
remote_status_lose:
pfatal_with_name ("remote_status");
}
else
{
#if !defined(__MSDOS__) && !defined(_AMIGA) && !defined(WINDOWS32)
if (any_local)
{
#ifdef VMS
vmsWaitForChildren (&status);
pid = c->pid;
#else
#ifdef WAIT_NOHANG
if (!block)
pid = WAIT_NOHANG (&status);
else
#endif
pid = wait (&status);
#endif
}
else
pid = 0;
if (pid < 0)
{
pfatal_with_name ("wait");
}
else if (pid > 0)
{
exit_code = WEXITSTATUS (status);
exit_sig = WIFSIGNALED (status) ? WTERMSIG (status) : 0;
coredump = WCOREDUMP (status);
if (job_counter)
--job_counter;
}
else
{
reap_more = 0;
if (!block || !any_remote)
break;
pid = remote_status (&exit_code, &exit_sig, &coredump, 1);
if (pid < 0)
goto remote_status_lose;
else if (pid == 0)
break;
remote = 1;
}
#endif
#ifdef __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
#ifdef _AMIGA
pid = amiga_pid - 1;
status = amiga_status;
exit_code = amiga_status;
exit_sig = 0;
coredump = 0;
#endif
#ifdef WINDOWS32
{
HANDLE hPID;
int werr;
HANDLE hcTID, hcPID;
exit_code = 0;
exit_sig = 0;
coredump = 0;
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 = 0x%08lx\n",
(unsigned long)main_thread));
}
hPID = process_wait_for_any();
if (hPID)
{
werr = process_last_err(hPID);
exit_code = process_exit_code(hPID);
if (werr)
fprintf(stderr, "make (e=%d): %s",
exit_code, map_windows32_error_to_string(exit_code));
exit_sig = process_signal(hPID);
process_cleanup(hPID);
coredump = 0;
}
pid = (pid_t) hPID;
}
#endif
}
if (!remote && pid == shell_function_pid)
{
if (exit_sig == 0 && exit_code == 127)
shell_function_completed = -1;
else
shell_function_completed = 1;
break;
}
child_failed = exit_sig != 0 || exit_code != 0;
lastc = 0;
for (c = children; c != 0; lastc = c, c = c->next)
if (c->remote == remote && c->pid == pid)
break;
if (c == 0)
continue;
DB (DB_JOBS, (child_failed
? _("Reaping losing child 0x%08lx PID %ld %s\n")
: _("Reaping winning child 0x%08lx PID %ld %s\n"),
(unsigned long int) c, (long) c->pid,
c->remote ? _(" (remote)") : ""));
if (c->sh_batch_file) {
DB (DB_JOBS, (_("Cleaning up temp batch file %s\n"),
c->sh_batch_file));
remove (c->sh_batch_file);
free (c->sh_batch_file);
c->sh_batch_file = NULL;
}
if (c->good_stdin)
good_stdin_used = 0;
dontcare = c->dontcare;
if (child_failed && !c->noerror && !ignore_errors_flag)
{
static int delete_on_error = -1;
if (!dontcare)
child_error (c->file->name, exit_code, exit_sig, coredump, 0);
c->file->update_status = 2;
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)
{
child_error (c->file->name,
exit_code, exit_sig, coredump, 1);
child_failed = 0;
}
if (job_next_command (c))
{
if (handling_fatal_signal)
{
c->file->update_status = 2;
}
else
{
c->remote = start_remote_job_p (0);
start_job_command (c);
unblock_sigs ();
if (c->file->command_state == cs_running)
continue;
}
if (c->file->update_status != 0)
delete_child_targets (c);
}
else
c->file->update_status = 0;
}
if (! handling_fatal_signal)
notice_finished_file (c->file);
DB (DB_JOBS, (_("Removing child 0x%08lx PID %ld%s from chain.\n"),
(unsigned long int) c, (long) c->pid,
c->remote ? _(" (remote)") : ""));
block_sigs ();
if (job_slots_used > 0)
--job_slots_used;
if (lastc == 0)
children = c->next;
else
lastc->next = c->next;
free_child (c);
unblock_sigs ();
if (!err && child_failed && !dontcare && !keep_going_flag &&
!handling_fatal_signal)
die (2);
block = 0;
}
return;
}
static void
free_child (struct child *child)
{
if (!jobserver_tokens)
fatal (NILF, "INTERNAL: Freeing child 0x%08lx (%s) but no tokens left!\n",
(unsigned long int) child, child->file->name);
if (job_fds[1] >= 0 && jobserver_tokens > 1)
{
char token = '+';
int r;
EINTRLOOP (r, write (job_fds[1], &token, 1));
if (r != 1)
pfatal_with_name (_("write jobserver"));
DB (DB_JOBS, (_("Released token for child 0x%08lx (%s).\n"),
(unsigned long int) child, child->file->name));
}
--jobserver_tokens;
if (handling_fatal_signal)
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 ((char *) child->command_lines);
}
if (child->environment != 0)
{
register char **ep = child->environment;
while (*ep != 0)
free (*ep++);
free ((char *) child->environment);
}
free ((char *) 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
#ifdef MAKE_JOBSERVER
RETSIGTYPE
job_noop (int sig UNUSED)
{
}
static void
set_child_handler_action_flags (int set_handler, int set_alarm)
{
struct sigaction sa;
#ifdef __EMX__
signal (SIGCHLD, SIG_DFL);
#endif
bzero ((char *) &sa, sizeof sa);
sa.sa_handler = child_handler;
sa.sa_flags = set_handler ? 0 : SA_RESTART;
#if defined SIGCHLD
sigaction (SIGCHLD, &sa, NULL);
#endif
#if defined SIGCLD && SIGCLD != SIGCHLD
sigaction (SIGCLD, &sa, NULL);
#endif
#if defined SIGALRM
if (set_alarm)
{
alarm (set_handler ? 1 : 0);
sa.sa_handler = set_handler ? job_noop : SIG_DFL;
sa.sa_flags = 0;
sigaction (SIGALRM, &sa, NULL);
}
#endif
}
#endif
static void
start_job_command (struct child *child)
{
#if !defined(_AMIGA) && !defined(WINDOWS32)
static int bad_stdin = -1;
#endif
register char *p;
int flags;
int already_set_status = 0;
char *saved_cmd_ptr = NULL;
#ifdef VMS
char *argv;
#else
char **argv;
#endif
if (!child->command_ptr)
goto next_command;
flags = (child->file->command_flags
| child->file->cmds->lines_flags[child->command_line - 1]);
saved_cmd_ptr = p = child->command_ptr;
child->noerror = (flags & COMMANDS_NOERROR) ? 1 : 0;
while (*p != '\0')
{
if (*p == '@')
flags |= COMMANDS_SILENT;
else if (*p == '+')
flags |= COMMANDS_RECURSE;
else if (*p == '-')
child->noerror = 1;
else if (!isblank ((unsigned char)*p))
break;
++p;
}
child->file->cmds->lines_flags[child->command_line - 1]
|= flags & COMMANDS_RECURSE;
{
char *end = 0;
#ifdef VMS
argv = p;
#else
argv = construct_command_argv (p, &end, child->file, &child->sh_batch_file);
#endif
if (end == NULL)
child->command_ptr = NULL;
else
{
*end++ = '\0';
child->command_ptr = end;
}
}
if (argv != 0 && question_flag && !(flags & COMMANDS_RECURSE))
{
#ifndef VMS
free (argv[0]);
free ((char *) argv);
#endif
child->file->update_status = 1;
already_set_status = 1;
if (!posix_pedantic) {
notice_finished_file (child->file);
return;
} else {
argv = 0;
}
}
if (argv && touch_flag) {
if (!(flags & COMMANDS_RECURSE)) {
#ifndef VMS
if (argv) {
free (argv[0]);
free ((char *) argv);
}
#endif
argv = 0;
} else {
struct child *nk = malloc(sizeof(struct child));
*nk = *child;
char *cp = saved_cmd_ptr;
while(*cp == '-' || *cp == '+' || *cp == '@') {
fprintf(stderr, "cp %p %c\n", cp, *cp);
cp++;
}
nk->command_ptr = cp;
child->next = nk;
nk->file->cmds->lines_flags[nk->command_line - 1] &= ~COMMANDS_RECURSE;
}
}
if (argv == 0)
{
next_command:
#ifdef __MSDOS__
execute_by_shell = 0;
#endif
if (job_next_command (child))
start_job_command (child);
else
{
set_command_state (child->file, cs_running);
if (!already_set_status) {
child->file->update_status = 0;
}
notice_finished_file (child->file);
}
return;
}
message (0, (just_print_flag || (!(flags & COMMANDS_SILENT) && !silent_flag))
? "%s" : (char *) 0, p);
++commands_started;
#if !defined(VMS) && !defined(_AMIGA)
if (
#if defined __MSDOS__ || defined (__EMX__)
unixy_shell
#else
(argv[0] && !strcmp (argv[0], "/bin/sh"))
#endif
#if defined(__APPLE__) || defined(NeXT) || defined(NeXT_PDO)
&& ((argv[1]
&& argv[1][0] == '-' && argv[1][1] == 'c' && argv[1][2] == '\0') ||
(argv[1]
&& argv[1][0] == '-' && argv[1][1] == 'e' && argv[1][2] == 'c' && argv[1][3] == '\0'))
#else
&& (argv[1]
&& argv[1][0] == '-' && argv[1][1] == 'c' && argv[1][2] == '\0')
#endif __APPLE__ || NeXT || NeXT_PDO
&& (argv[2] && argv[2][0] == ':' && argv[2][1] == '\0')
&& argv[3] == NULL)
{
free (argv[0]);
free ((char *) argv);
goto next_command;
}
#endif
if (just_print_flag && !(flags & COMMANDS_RECURSE))
{
#ifndef VMS
free (argv[0]);
free ((char *) argv);
#endif
goto next_command;
}
fflush (stdout);
fflush (stderr);
#ifndef VMS
#if !defined(WINDOWS32) && !defined(_AMIGA) && !defined(__MSDOS__)
if (bad_stdin == -1)
{
int pd[2];
if (pipe (pd) == 0)
{
(void) close (pd[1]);
bad_stdin = pd[0];
CLOSE_ON_EXEC (bad_stdin);
}
}
#endif
child->good_stdin = !good_stdin_used;
if (child->good_stdin)
good_stdin_used = 1;
#endif
child->deleted = 0;
#ifndef _AMIGA
if (child->environment == 0)
child->environment = target_environment (child->file);
#endif
#if !defined(__MSDOS__) && !defined(_AMIGA) && !defined(WINDOWS32)
#ifndef VMS
if (child->remote)
{
int is_remote, id, used_stdin;
if (start_remote_job (argv, child->environment,
child->good_stdin ? 0 : bad_stdin,
&is_remote, &id, &used_stdin))
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
{
char **parent_environ;
run_local:
block_sigs ();
child->remote = 0;
#ifdef VMS
if (!child_execute_job (argv, child)) {
perror_with_name ("vfork", "");
goto error;
}
#else
parent_environ = environ;
# ifdef __EMX__
if (!(flags & COMMANDS_RECURSE) && job_fds[0] >= 0)
{
CLOSE_ON_EXEC (job_fds[0]);
CLOSE_ON_EXEC (job_fds[1]);
}
if (job_rfd >= 0)
CLOSE_ON_EXEC (job_rfd);
child->pid = child_execute_job (child->good_stdin ? 0 : bad_stdin, 1,
argv, child->environment);
if (child->pid < 0)
{
unblock_sigs ();
perror_with_name ("spawn", "");
goto error;
}
if (!(flags & COMMANDS_RECURSE) && job_fds[0] >= 0)
{
fcntl (job_fds[0], F_SETFD, 0);
fcntl (job_fds[1], F_SETFD, 0);
}
if (job_rfd >= 0)
fcntl (job_rfd, F_SETFD, 0);
#else
#if USE_POSIX_SPAWN
posix_spawn_file_actions_t file_actions;
posix_spawnattr_t attr;
sigset_t nosigs, allsigs;
int rc;
struct stat stat;
short spawn_flags = 0;
posix_spawn_file_actions_init(&file_actions);
if (!(flags & COMMANDS_RECURSE)) {
if (job_fds[0] >= 0 && fstat(job_fds[0], &stat))
posix_spawn_file_actions_addclose(&file_actions, job_fds[0]);
if (job_fds[1] >= 0 && fstat(job_fds[1], &stat))
posix_spawn_file_actions_addclose(&file_actions, job_fds[1]);
}
if (job_rfd >= 0 && fstat(job_rfd, &stat))
posix_spawn_file_actions_addclose(&file_actions, job_rfd);
if (!child->good_stdin && fstat(bad_stdin, &stat))
posix_spawn_file_actions_adddup2(&file_actions, bad_stdin, 0);
posix_spawnattr_init(&attr);
sigemptyset(&nosigs);
sigfillset(&allsigs);
posix_spawnattr_setsigmask(&attr, &nosigs);
posix_spawnattr_setsigdefault(&attr, &allsigs);
posix_spawnattr_getflags(&attr, &spawn_flags);
spawn_flags |= POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK;
posix_spawnattr_setflags(&attr, spawn_flags);
rc = posix_spawnp(&child->pid, argv[0], &file_actions, &attr, argv, child->environment);
environ = parent_environ;
posix_spawnattr_destroy(&attr);
posix_spawn_file_actions_destroy(&file_actions);
if (rc) {
error (NILF, _("%s: %s"), argv[0], strerror(rc));
rc = posix_spawnp(&child->pid, "/usr/bin/false", NULL, NULL, NULL, NULL);
}
#else
child->pid = vfork ();
environ = parent_environ;
if (child->pid == 0)
{
unblock_sigs ();
if (!(flags & COMMANDS_RECURSE) && job_fds[0] >= 0)
{
close (job_fds[0]);
close (job_fds[1]);
}
if (job_rfd >= 0)
close (job_rfd);
child_execute_job (child->good_stdin ? 0 : bad_stdin, 1,
argv, child->environment);
}
else if (child->pid < 0)
{
unblock_sigs ();
perror_with_name ("vfork", "");
goto error;
}
# endif
# endif
#endif
}
#else
#ifdef __MSDOS__
{
int proc_return;
block_sigs ();
dos_status = 0;
if (execute_by_shell)
{
char *cmdline = argv[0];
char **parent_environ = environ;
environ = child->environment;
if (unixy_shell)
{
cmdline = argv[2];
}
dos_command_running = 1;
proc_return = system (cmdline);
environ = parent_environ;
execute_by_shell = 0;
}
else
{
dos_command_running = 1;
proc_return = spawnvpe (P_WAIT, argv[0], argv, child->environment);
}
unblock_sigs ();
dos_command_running = 0;
if (proc_return == -1)
dos_status |= 0xff;
else
dos_status |= (proc_return & 0xff);
++dead_children;
child->pid = dos_pid++;
}
#endif
#ifdef _AMIGA
amiga_status = MyExecute (argv);
++dead_children;
child->pid = amiga_pid++;
if (amiga_batch_file)
{
amiga_batch_file = 0;
DeleteFile (amiga_bname);
}
#endif
#ifdef WINDOWS32
{
HANDLE hPID;
char* arg0;
arg0 = argv[0];
if (arg0 && arg0[0] == '/' && arg0[1] == '/')
for ( ; arg0 && *arg0; arg0++)
if (*arg0 == '/')
*arg0 = '\\';
sync_Path_environment();
hPID = process_easy(argv, child->environment);
if (hPID != INVALID_HANDLE_VALUE)
child->pid = (int) 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;
}
}
#endif
#endif
++job_counter;
set_command_state (child->file, cs_running);
#ifndef VMS
free (argv[0]);
free ((char *) argv);
#endif
return;
error:
child->file->update_status = 2;
notice_finished_file (child->file);
return;
}
static int
start_waiting_job (struct child *c)
{
struct file *f = c->file;
c->remote = start_remote_job_p (1);
if (!c->remote
&& ((job_slots_used > 0 && load_too_high ())
#ifdef WINDOWS32
|| (process_used_slots () >= MAXIMUM_WAIT_OBJECTS)
#endif
))
{
set_command_state (f, cs_running);
c->next = waiting_jobs;
waiting_jobs = c;
return 0;
}
start_job_command (c);
switch (f->command_state)
{
case cs_running:
c->next = children;
DB (DB_JOBS, (_("Putting child 0x%08lx (%s) PID %ld%s on the chain.\n"),
(unsigned long int) c, c->file->name,
(long) c->pid, c->remote ? _(" (remote)") : ""));
children = c;
++job_slots_used;
unblock_sigs ();
break;
case cs_not_started:
f->update_status = 0;
case cs_finished:
notice_finished_file (f);
free_child (c);
break;
default:
assert (f->command_state == cs_finished);
break;
}
return 1;
}
void
new_job (struct file *file)
{
register struct commands *cmds = file->cmds;
register struct child *c;
char **lines;
register unsigned int i;
start_waiting_jobs ();
reap_children (0, 0);
chop_commands (cmds);
lines = (char **) xmalloc (cmds->ncommand_lines * sizeof (char *));
for (i = 0; i < cmds->ncommand_lines; ++i)
{
char *in, *out, *ref;
in = out = cmds->command_lines[i];
while ((ref = strchr (in, '$')) != 0)
{
++ref;
if (out != in)
bcopy (in, out, ref - in);
out += ref - in;
in = ref;
if (*ref == '(' || *ref == '{')
{
char openparen = *ref;
char closeparen = openparen == '(' ? ')' : '}';
int count;
char *p;
*out++ = *in++;
count = 0;
while (*in != '\0')
{
if (*in == closeparen && --count < 0)
break;
else if (*in == '\\' && in[1] == '\n')
{
int quoted = 0;
for (p = in - 1; p > ref && *p == '\\'; --p)
quoted = !quoted;
if (quoted)
*out++ = *in++;
else
{
in = next_token (in + 2);
while (out > ref
&& isblank ((unsigned char)out[-1]))
--out;
*out++ = ' ';
}
}
else
{
if (*in == openparen)
++count;
*out++ = *in++;
}
}
}
}
if (out != in)
strcpy (out, in);
lines[i] = allocated_variable_expand_for_file (cmds->command_lines[i],
file);
}
#if defined(__APPLE__) || defined(NeXT) || defined(NeXT_PDO)
if (next_flag & NEXT_VPATH_FLAG) {
for (i = 0; i < cmds->ncommand_lines; ++i) {
char *line;
if (lines[i] != 0) {
line = allocated_vpath_expand_for_file (lines[i], file);
free (lines[i]);
lines[i] = line;
}
}
}
#endif
c = (struct child *) xmalloc (sizeof (struct child));
bzero ((char *)c, sizeof (struct child));
c->file = file;
c->command_lines = lines;
c->sh_batch_file = NULL;
c->dontcare = file->dontcare;
job_next_command (c);
if (job_slots != 0)
while (job_slots_used == job_slots)
reap_children (1, 0);
#ifdef MAKE_JOBSERVER
else if (job_fds[0] >= 0)
while (1)
{
char token;
int got_token;
int saved_errno;
DB (DB_JOBS, ("Need a job token; we %shave children\n",
children ? "" : "don't "));
if (!jobserver_tokens)
break;
if (job_rfd < 0)
{
DB (DB_JOBS, ("Duplicate the job FD\n"));
job_rfd = dup (job_fds[0]);
}
reap_children (0, 0);
start_waiting_jobs ();
if (!jobserver_tokens)
break;
if (!children)
fatal (NILF, "INTERNAL: no children as we go to sleep on read\n");
set_child_handler_action_flags (1, waiting_jobs != NULL);
got_token = read (job_rfd, &token, 1);
saved_errno = errno;
set_child_handler_action_flags (0, waiting_jobs != NULL);
if (got_token == 1)
{
DB (DB_JOBS, (_("Obtained token for child 0x%08lx (%s).\n"),
(unsigned long int) c, c->file->name));
break;
}
errno = saved_errno;
if (errno != EINTR && errno != EBADF)
pfatal_with_name (_("read jobs pipe"));
if (errno == EBADF)
DB (DB_JOBS, ("Read returned EBADF.\n"));
}
#endif
++jobserver_tokens;
(void) start_waiting_job (c);
if (job_slots == 1 || not_parallel)
while (file->command_state == cs_running)
reap_children (1, 0);
return;
}
static int
job_next_command (struct child *child)
{
while (child->command_ptr == 0 || *child->command_ptr == '\0')
{
if (child->command_line == child->file->cmds->ncommand_lines)
{
child->command_ptr = 0;
return 0;
}
else
child->command_ptr = child->command_lines[child->command_line++];
}
return 1;
}
#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__)
return 1;
#else
static double last_sec;
static time_t last_now;
double load, guess;
time_t now;
#ifdef WINDOWS32
if (process_used_slots () >= MAXIMUM_WAIT_OBJECTS)
return 1;
#endif
if (max_load_average < 0)
return 0;
make_access ();
if (getloadavg (&load, 1) != 1)
{
static int lossage = -1;
if (lossage == -1 || errno != lossage)
{
if (errno == 0)
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 ();
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;
}
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
}
void
start_waiting_jobs (void)
{
struct child *job;
if (waiting_jobs == 0)
return;
do
{
reap_children (0, 0);
job = waiting_jobs;
waiting_jobs = job->next;
}
while (start_waiting_job (job) && waiting_jobs != 0);
return;
}
#ifndef WINDOWS32
# if defined __MSDOS__ || defined __EMX__
int
child_execute_job (int stdin_fd, int stdout_fd, char **argv, char **envp)
{
int pid;
int save_stdin = (stdin_fd != 0) ? dup (0) : 0;
int save_stdout = (stdout_fd != 1) ? dup (1): 1;
if (save_stdin < 0)
fatal (NILF, _("no more file handles: could not duplicate stdin\n"));
if (save_stdout < 0)
fatal (NILF, _("no more file handles: could not duplicate stdout\n"));
if (save_stdin != 0)
CLOSE_ON_EXEC (save_stdin);
if (save_stdout != 1)
CLOSE_ON_EXEC (save_stdout);
if (stdin_fd != 0)
(void) dup2 (stdin_fd, 0);
if (stdout_fd != 1)
(void) dup2 (stdout_fd, 1);
if (stdin_fd != 0)
CLOSE_ON_EXEC (stdin_fd);
if (stdout_fd != 1)
CLOSE_ON_EXEC (stdout_fd);
pid = exec_command (argv, envp);
if (stdin_fd != 0)
{
if (dup2 (save_stdin, 0) != 0)
fatal (NILF, _("Could not restore stdin\n"));
else
close (save_stdin);
}
if (stdout_fd != 1)
{
if (dup2 (save_stdout, 1) != 1)
fatal (NILF, _("Could not restore stdout\n"));
else
close (save_stdout);
}
return pid;
}
#elif !defined (_AMIGA) && !defined (__MSDOS__) && !defined (VMS)
void
child_execute_job (int stdin_fd, int stdout_fd, char **argv, char **envp)
{
if (stdin_fd != 0)
(void) dup2 (stdin_fd, 0);
if (stdout_fd != 1)
(void) dup2 (stdout_fd, 1);
if (stdin_fd != 0)
(void) close (stdin_fd);
if (stdout_fd != 1)
(void) close (stdout_fd);
exec_command (argv, envp);
}
#endif
#endif
#ifndef _AMIGA
# ifdef __EMX__
int
# else
void
# endif
exec_command (char **argv, char **envp)
{
#ifdef VMS
#ifdef SIGCHLD
signal (SIGCHLD,SIG_IGN);
#endif
execve (argv[0], argv, envp);
perror_with_name ("execve: ", argv[0]);
_exit (EXIT_FAILURE);
#else
#ifdef WINDOWS32
HANDLE hPID;
HANDLE hWaitPID;
int err = 0;
int exit_code = EXIT_FAILURE;
sync_Path_environment();
hPID = process_easy(argv, envp);
if (hPID == INVALID_HANDLE_VALUE)
{
int i;
fprintf(stderr,
_("process_easy() failed 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);
}
hWaitPID = process_wait_for_any();
while (hWaitPID)
{
err = process_last_err(hWaitPID);
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));
process_cleanup(hWaitPID);
if (hWaitPID == hPID)
break;
else
fprintf(stderr,
_("make reaped child pid %ld, still waiting for pid %ld\n"),
(DWORD)hWaitPID, (DWORD)hPID);
}
exit(exit_code);
#else
# ifdef __EMX__
int pid;
# endif
child_access ();
# ifdef __EMX__
pid = spawnvpe (P_NOWAIT, argv[0], argv, envp);
if (pid >= 0)
return pid;
if (errno == ENOENT)
errno = ENOEXEC;
# else
environ = envp;
execvp (argv[0], argv);
# endif
switch (errno)
{
case ENOENT:
error (NILF, _("%s: Command not found"), argv[0]);
break;
case ENOEXEC:
{
extern char *getenv ();
char *shell;
char **new_argv;
int argc;
int i=1;
# ifdef __EMX__
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 = (char **) alloca ((1 + argc + 1) * sizeof (char *));
new_argv[0] = 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)
error (NILF, _("%s: Shell program not found"), shell);
else
perror_with_name ("execvp: ", shell);
break;
}
# ifdef __EMX__
case EINVAL:
error (NILF, _("spawnvpe: environment space might be exhausted"));
# endif
default:
perror_with_name ("execvp: ", argv[0]);
break;
}
# ifdef __EMX__
return pid;
# else
_exit (127);
# endif
#endif
#endif
}
#else
void exec_command (char **argv)
{
MyExecute (argv);
}
void clean_tmp (void)
{
DeleteFile (amiga_bname);
}
#endif
#ifndef VMS
static char **
construct_command_argv_internal (char *line, char **restp, char *shell,
char *ifs, char **batch_filename_ptr)
{
#ifdef __MSDOS__
static char sh_chars_dos[] = "*?[];|<>%^&()";
static 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 char sh_chars_sh[] = "#;\"*?[]&|<>(){}$`^";
static 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",
0 };
char *sh_chars;
char **sh_cmds;
#elif defined (__EMX__)
static char sh_chars_dos[] = "*?[];|<>%^&()";
static 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 char sh_chars_os2[] = "*?[];|<>%^()\"'&";
static 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 char sh_chars_sh[] = "#;\"*?[]&|<>(){}$`^~'";
static 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 };
char *sh_chars;
char **sh_cmds;
#elif defined (_AMIGA)
static char sh_chars[] = "#;\"|<>()?*$`";
static 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)
static char sh_chars_dos[] = "\"|&<>";
static char *sh_cmds_dos[] = { "break", "call", "cd", "chcp", "chdir", "cls",
"copy", "ctty", "date", "del", "dir", "echo",
"erase", "exit", "for", "goto", "if", "if", "md",
"mkdir", "path", "pause", "prompt", "rd", "rem",
"ren", "rename", "rmdir", "set", "shift", "time",
"type", "ver", "verify", "vol", ":", 0 };
static char sh_chars_sh[] = "#;\"*?[]&|<>(){}$`^";
static 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 };
char* sh_chars;
char** sh_cmds;
#elif defined(__riscos__)
static char sh_chars[] = "";
static char *sh_cmds[] = { 0 };
#else
static char sh_chars[] = "#;\"*?[]&|<>(){}$`^~!";
static char *sh_cmds[] = { ".", ":", "break", "case", "cd", "continue",
"eval", "exec", "exit", "export", "for", "if",
"login", "logout", "read", "readonly", "set",
"shift", "switch", "test", "times", "trap",
"umask", "wait", "while", 0 };
#endif
register int i;
register char *p;
register char *ap;
char *end;
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
if (restp != NULL)
*restp = NULL;
while (isblank ((unsigned char)*line))
++line;
if (*line == '\0')
return 0;
if (shell == 0)
shell = default_shell;
#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 : ""));
if (s1)
free (s1);
if (s2)
free (s2);
}
if (slow_flag)
goto slow;
#else
#if defined (__MSDOS__) || defined (__EMX__)
else if (stricmp (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);
default_shell = xstrdup (shell);
}
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
else if (strcmp (shell, default_shell))
goto slow;
#endif
#endif
if (ifs != 0)
for (ap = ifs; *ap != '\0'; ++ap)
if (*ap != ' ' && *ap != '\t' && *ap != '\n')
goto slow;
i = strlen (line) + 1;
new_argv = (char **) xmalloc (i * sizeof (char *));
ap = new_argv[0] = argstr = (char *) xmalloc (i);
end = ap + i;
i = 0;
instring = word_has_equals = seen_nonequals = last_argument_was_empty = 0;
for (p = line; *p != '\0'; ++p)
{
assert (ap <= end);
if (instring)
{
if (*p == instring)
{
instring = 0;
if (ap == new_argv[0] || *(ap-1) == '\0')
last_argument_was_empty = 1;
}
else if (*p == '\\' && p[1] == '\n')
{
++p;
if (p[1] == '\t')
++p;
}
else if (*p == '\n' && restp != NULL)
{
*restp = p;
goto end_of_line;
}
else if (instring == '"' && strchr ("\\$`", *p) != 0 && unixy_shell)
goto slow;
else
*ap++ = *p;
}
else if (strchr (sh_chars, *p) != 0)
goto slow;
#ifdef __MSDOS__
else if (*p == '.' && p[1] == '.' && p[2] == '.' && p[3] != '.')
goto slow;
#endif
else
switch (*p)
{
case '=':
if (! seen_nonequals && unixy_shell)
goto slow;
word_has_equals = 1;
*ap++ = '=';
break;
case '\\':
if (p[1] == '\n')
{
++p;
if (p[1] == '\t')
++p;
if (ap == new_argv[i])
p = next_token (p + 1) - 1;
}
else if (p[1] != '\0')
{
#ifdef HAVE_DOS_PATHS
#ifdef __MSDOS__
if (strneq (p + 1, ".\\.\\.", 5))
{
*ap++ = '.';
*ap++ = '.';
p += 4;
}
else
#endif
if (p[1] != '\\' && p[1] != '\''
&& !isspace ((unsigned char)p[1])
&& strchr (sh_chars_sh, p[1]) == 0)
--p;
#endif
*ap++ = *++p;
}
break;
case '\'':
case '"':
instring = *p;
break;
case '\n':
if (restp != NULL)
{
*restp = p;
goto end_of_line;
}
else
*ap++ = '\n';
break;
case ' ':
case '\t':
*ap++ = '\0';
new_argv[++i] = ap;
last_argument_was_empty = 0;
seen_nonequals |= ! word_has_equals;
if (word_has_equals && ! seen_nonequals)
goto slow;
word_has_equals = 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;
# ifdef __EMX__
if (!unixy_shell
&& strcasecmp (sh_cmds[j], new_argv[0]) == 0)
goto slow;
# endif
}
}
p = next_token (p) - 1;
break;
default:
*ap++ = *p;
break;
}
}
end_of_line:
if (instring)
goto slow;
*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)
{
free (argstr);
free ((char *)new_argv);
return 0;
}
return new_argv;
slow:;
if (new_argv != 0)
{
free (argstr);
free ((char *)new_argv);
}
#ifdef __MSDOS__
execute_by_shell = 1;
#endif
#ifdef _AMIGA
{
char *ptr;
char *buffer;
char *dptr;
buffer = (char *)xmalloc (strlen (line)+1);
ptr = line;
for (dptr=buffer; *ptr; )
{
if (*ptr == '\\' && ptr[1] == '\n')
ptr += 2;
else if (*ptr == '@')
{
ptr += 2;
*dptr++ = '\n';
}
else
*dptr++ = *ptr++;
}
*dptr = 0;
new_argv = (char **) xmalloc (2 * sizeof (char *));
new_argv[0] = buffer;
new_argv[1] = 0;
}
#else
#ifdef WINDOWS32
while (isspace ((unsigned char)*line))
++line;
if (*line == '\0')
return 0;
#endif
{
unsigned int shell_len = strlen (shell);
#if defined(__APPLE__) || defined(NeXT) || defined(NeXT_PDO)
char *minus_c;
int minus_c_len;
if (next_flag & NEXT_ERREXIT_FLAG) {
minus_c = " -ec ";
minus_c_len = 5;
} else {
minus_c = " -c ";
minus_c_len = 4;
}
#else
#ifndef VMS
static char minus_c[] = " -c ";
#else
static char minus_c[] = "";
#endif
#endif
unsigned int line_len = strlen (line);
#if defined(__APPLE__) || defined(NeXT) || defined(NeXT_PDO)
char *new_line = (char *) alloca (shell_len + minus_c_len
+ (line_len * 2) + 1);
#else
char *new_line = (char *) alloca (shell_len + (sizeof (minus_c) - 1)
+ (line_len * 2) + 1);
#endif
char *command_ptr = NULL;
# ifdef __EMX__
if (!unixy_shell)
minus_c[1] = '/';
# endif
ap = new_line;
bcopy (shell, ap, shell_len);
ap += shell_len;
#ifdef __APPLE__
bcopy (minus_c, ap, minus_c_len);
ap += minus_c_len;
#else
bcopy (minus_c, ap, sizeof (minus_c) - 1);
ap += sizeof (minus_c) - 1;
#endif
command_ptr = ap;
for (p = line; *p != '\0'; ++p)
{
if (restp != NULL && *p == '\n')
{
*restp = p;
break;
}
else if (*p == '\\' && p[1] == '\n')
{
++p;
if (p[1] == '\t')
++p;
continue;
}
if (unixy_shell && !batch_mode_shell &&
(*p == '\\' || *p == '\'' || *p == '"'
|| isspace ((unsigned char)*p)
|| strchr (sh_chars, *p) != 0))
*ap++ = '\\';
#ifdef __MSDOS__
else if (unixy_shell && strneq (p, "...", 3))
{
strcpy (ap, "\\.\\.\\");
ap += 5;
p += 2;
}
#endif
*ap++ = *p;
}
if (ap == new_line + shell_len + minus_c_len)
return 0;
*ap = '\0';
#ifdef WINDOWS32
if (just_print_flag) {
new_argv = (char **) xmalloc(2 * sizeof (char *));
new_argv[0] = xstrdup ("");
new_argv[1] = NULL;
} else if ((no_default_sh_exe || batch_mode_shell) && batch_filename_ptr) {
int temp_fd;
FILE* batch = NULL;
int id = GetCurrentProcessId();
PATH_VAR(fbuf);
sprintf(fbuf, "make%d", id);
*batch_filename_ptr = create_batch_file (fbuf, unixy_shell, &temp_fd);
DB (DB_JOBS, (_("Creating temporary batch file %s\n"),
*batch_filename_ptr));
_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);
new_argv = (char **) xmalloc(3 * sizeof (char *));
if (unixy_shell) {
new_argv[0] = xstrdup (shell);
new_argv[1] = *batch_filename_ptr;
} else {
new_argv[0] = xstrdup (*batch_filename_ptr);
new_argv[1] = NULL;
}
new_argv[2] = NULL;
} else
#endif
if (unixy_shell)
new_argv = construct_command_argv_internal (new_line, (char **) NULL,
(char *) 0, (char *) 0,
(char **) 0);
#ifdef __EMX__
else if (!unixy_shell)
{
size_t line_len = strlen (line);
char *p = new_line;
char *q = new_line;
memcpy (new_line, line, line_len + 1);
while (*q != '\0')
{
if (q[0] == '\\' && q[1] == '\n')
{
q += 2;
if (q[0] == '\t')
q++;
}
else
*p++ = *q++;
}
*p = '\0';
# ifndef NO_CMD_DEFAULT
if (strnicmp (new_line, "echo", 4) == 0
&& (new_line[4] == ' ' || new_line[4] == '\t'))
{
size_t echo_len = line_len - 5;
char *echo_line = new_line + 5;
if (echo_line[0] == '\''
&& echo_line[echo_len - 1] == '\''
&& strncmp (echo_line + 1, "ac_maketemp=",
strlen ("ac_maketemp=")) == 0)
{
memmove (echo_line, echo_line + 1, echo_len - 2);
echo_line[echo_len - 2] = '\0';
}
}
# endif
{
size_t sh_len = strlen (shell);
new_argv = (char **) xmalloc (4 * sizeof (char *));
new_argv[0] = (char *) malloc (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
{
new_argv = (char **) xmalloc (sizeof (char *));
line_len = strlen (new_line) - shell_len - sizeof (minus_c) + 1;
new_argv[0] = xmalloc (line_len + 1);
strncpy (new_argv[0],
new_line + shell_len + sizeof (minus_c) - 1, line_len);
new_argv[0][line_len] = '\0';
}
#else
else
fatal (NILF, _("%s (line %d) Bad shell context (!unixy && !batch_mode_shell)\n"),
__FILE__, __LINE__);
#endif
}
#endif
return new_argv;
}
#endif
char **
construct_command_argv (char *line, char **restp, struct file *file,
char **batch_filename_ptr)
{
char *shell, *ifs;
char **argv;
#ifdef VMS
char *cptr;
int argc;
argc = 0;
cptr = line;
for (;;)
{
while ((*cptr != 0)
&& (isspace ((unsigned char)*cptr)))
cptr++;
if (*cptr == 0)
break;
while ((*cptr != 0)
&& (!isspace((unsigned char)*cptr)))
cptr++;
argc++;
}
argv = (char **)malloc (argc * sizeof (char *));
if (argv == 0)
abort ();
cptr = line;
argc = 0;
for (;;)
{
while ((*cptr != 0)
&& (isspace ((unsigned char)*cptr)))
cptr++;
if (*cptr == 0)
break;
DB (DB_JOBS, ("argv[%d] = [%s]\n", argc, cptr));
argv[argc++] = cptr;
while ((*cptr != 0)
&& (!isspace((unsigned char)*cptr)))
cptr++;
if (*cptr != 0)
*cptr++ = 0;
}
#else
{
int save = warn_undefined_variables_flag;
warn_undefined_variables_flag = 0;
shell = allocated_variable_expand_for_file ("$(SHELL)", file);
#ifdef WINDOWS32
if (shell) {
char *p = w32ify (shell, 0);
strcpy (shell, p);
}
#endif
#ifdef __EMX__
{
static const char *unixroot = NULL;
static const char *last_shell = "";
static int init = 0;
if (init == 0)
{
unixroot = getenv ("UNIXROOT");
if (unixroot && unixroot[0] == '\0') unixroot = NULL;
init = 1;
}
if (unixroot && shell && strcmp (shell, last_shell) != 0
&& (shell[0] == '/' || shell[0] == '\\'))
{
size_t size = strlen (shell);
char *buf = xmalloc (size + 7);
memcpy (buf, shell, size);
memcpy (buf + size, ".exe", 5);
if (access (shell, F_OK) != 0 && access (buf, F_OK) != 0)
{
memmove (buf + 2, buf, size + 5);
buf[0] = unixroot[0];
buf[1] = unixroot[1];
if (access (buf, F_OK) == 0)
shell = buf;
else
free (buf);
}
else
free (buf);
}
}
#endif
ifs = allocated_variable_expand_for_file ("$(IFS)", file);
warn_undefined_variables_flag = save;
}
argv = construct_command_argv_internal (line, restp, shell, ifs, batch_filename_ptr);
free (shell);
free (ifs);
#endif
return argv;
}
#if !defined(HAVE_DUP2) && !defined(_AMIGA)
int
dup2 (int old, int new)
{
int fd;
(void) close (new);
fd = dup (old);
if (fd != new)
{
(void) close (fd);
errno = EMFILE;
return -1;
}
return fd;
}
#endif
#ifdef VMS
#include "vmsjobs.c"
#endif