/* Utilities to execute a program in a subprocess (possibly linked by pipes
with other subprocesses), and wait for it. Generic Win32 specialization.
- Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006
- Free Software Foundation, Inc.
+ Copyright (C) 1996-2019 Free Software Foundation, Inc.
This file is part of the libiberty library.
Libiberty is free software; you can redistribute it and/or
}
static int pex_win32_open_read (struct pex_obj *, const char *, int);
-static int pex_win32_open_write (struct pex_obj *, const char *, int);
+static int pex_win32_open_write (struct pex_obj *, const char *, int, int);
static pid_t pex_win32_exec_child (struct pex_obj *, int, const char *,
char * const *, char * const *,
int, int, int, int,
const char **, int *);
static int pex_win32_close (struct pex_obj *, int);
-static int pex_win32_wait (struct pex_obj *, pid_t, int *,
+static pid_t pex_win32_wait (struct pex_obj *, pid_t, int *,
struct pex_time *, int, const char **, int *);
static int pex_win32_pipe (struct pex_obj *, int *, int);
static FILE *pex_win32_fdopenr (struct pex_obj *, int, int);
static int
pex_win32_open_write (struct pex_obj *obj ATTRIBUTE_UNUSED, const char *name,
- int binary)
+ int binary, int append)
{
/* Note that we can't use O_EXCL here because gcc may have already
created the temporary file via make_temp_file. */
+ if (append)
+ return -1;
return _open (name,
(_O_WRONLY | _O_CREAT | _O_TRUNC
| (binary ? _O_BINARY : _O_TEXT)),
if (!namebuf || !foundbuf)
{
RegCloseKey (hKey);
- if (namebuf)
- free (namebuf);
- if (foundbuf)
- free (foundbuf);
+ free (namebuf);
+ free (foundbuf);
return executable;
}
return tack_on_executable (buf, executable);
/* failed */
- if (buf)
- free (buf);
+ free (buf);
return executable;
}
#endif
+/* Return the number of arguments in an argv array, not including the null
+ terminating argument. */
+
+static int
+argv_to_argc (char *const *argv)
+{
+ char *const *i = argv;
+ while (*i)
+ i++;
+ return i - argv;
+}
+
/* Return a Windows command-line from ARGV. It is the caller's
responsibility to free the string returned. */
char *p;
size_t cmdline_len;
int i, j, k;
+ int needs_quotes;
cmdline_len = 0;
for (i = 0; argv[i]; i++)
{
- /* We quote every last argument. This simplifies the problem;
- we need only escape embedded double-quotes and immediately
+ /* We only quote arguments that contain spaces, \t or " characters to
+ prevent wasting 2 chars per argument of the CreateProcess 32k char
+ limit. We need only escape embedded double-quotes and immediately
preceeding backslash characters. A sequence of backslach characters
that is not follwed by a double quote character will not be
escaped. */
+ needs_quotes = 0;
for (j = 0; argv[i][j]; j++)
{
+ if (argv[i][j] == ' ' || argv[i][j] == '\t' || argv[i][j] == '"')
+ {
+ needs_quotes = 1;
+ }
+
if (argv[i][j] == '"')
{
/* Escape preceeding backslashes. */
cmdline_len++;
}
}
+ if (j == 0)
+ needs_quotes = 1;
/* Trailing backslashes also need to be escaped because they will be
followed by the terminating quote. */
- for (k = j - 1; k >= 0 && argv[i][k] == '\\'; k--)
- cmdline_len++;
+ if (needs_quotes)
+ {
+ for (k = j - 1; k >= 0 && argv[i][k] == '\\'; k--)
+ cmdline_len++;
+ }
cmdline_len += j;
- cmdline_len += 3; /* for leading and trailing quotes and space */
+ /* for leading and trailing quotes and space */
+ cmdline_len += needs_quotes * 2 + 1;
}
cmdline = XNEWVEC (char, cmdline_len);
p = cmdline;
for (i = 0; argv[i]; i++)
{
- *p++ = '"';
+ needs_quotes = 0;
+ for (j = 0; argv[i][j]; j++)
+ {
+ if (argv[i][j] == ' ' || argv[i][j] == '\t' || argv[i][j] == '"')
+ {
+ needs_quotes = 1;
+ break;
+ }
+ }
+ if (j == 0)
+ needs_quotes = 1;
+
+ if (needs_quotes)
+ {
+ *p++ = '"';
+ }
for (j = 0; argv[i][j]; j++)
{
if (argv[i][j] == '"')
}
*p++ = argv[i][j];
}
- for (k = j - 1; k >= 0 && argv[i][k] == '\\'; k--)
- *p++ = '\\';
- *p++ = '"';
+ if (needs_quotes)
+ {
+ for (k = j - 1; k >= 0 && argv[i][k] == '\\'; k--)
+ *p++ = '\\';
+ *p++ = '"';
+ }
*p++ = ' ';
}
p[-1] = '\0';
return c1 - c2;
}
+/* Execute a Windows executable as a child process. This will fail if the
+ * target is not actually an executable, such as if it is a shell script. */
+
static pid_t
win32_spawn (const char *executable,
BOOL search,
si,
pi))
{
- if (env_block)
- free (env_block);
+ free (env_block);
free (full_executable);
/* Clean up. */
CloseHandle (pi->hThread);
free (full_executable);
- if (env_block)
- free (env_block);
+ free (env_block);
return (pid_t) pi->hProcess;
error:
- if (env_block)
- free (env_block);
- if (cmdline)
- free (cmdline);
- if (full_executable)
- free (full_executable);
+ free (env_block);
+ free (cmdline);
+ free (full_executable);
return (pid_t) -1;
}
+/* Spawn a script. This simulates the Unix script execution mechanism.
+ This function is called as a fallback if win32_spawn fails. */
+
static pid_t
spawn_script (const char *executable, char *const *argv,
char* const *env,
int save_errno = errno;
int fd = _open (executable, _O_RDONLY);
+ /* Try to open script, check header format, extract interpreter path,
+ and spawn script using that interpretter. */
if (fd >= 0)
{
char buf[MAX_PATH + 5];
eol = strchr (buf, '\n');
if (eol && strncmp (buf, "#!", 2) == 0)
{
+
+ /* Header format is OK. */
char *executable1;
- const char ** avhere = (const char **) --argv;
+ int new_argc;
+ const char **avhere;
+
+ /* Extract interpreter path. */
do
*eol = '\0';
while (*--eol == '\r' || *eol == ' ' || *eol == '\t');
for (executable1 = buf + 2; *executable1 == ' ' || *executable1 == '\t'; executable1++)
continue;
-
backslashify (executable1);
+
+ /* Duplicate argv, prepending the interpreter path. */
+ new_argc = argv_to_argc (argv) + 1;
+ avhere = XNEWVEC (const char *, new_argc + 1);
*avhere = executable1;
+ memcpy (avhere + 1, argv, new_argc * sizeof(*argv));
+ argv = (char *const *)avhere;
+
+ /* Spawn the child. */
#ifndef USE_MINGW_MSYS
executable = strrchr (executable1, '\\') + 1;
if (!executable)
dwCreationFlags, si, pi);
if (executable1 != newex)
free ((char *) newex);
- if ((long) pid < 0)
+ if (pid == (pid_t) -1)
{
newex = msys_rootify (executable1);
if (newex != executable1)
}
}
#endif
+ free (avhere);
}
}
}
- if ((long) pid < 0)
+ if (pid == (pid_t) -1)
errno = save_errno;
return pid;
}
OSVERSIONINFO version_info;
STARTUPINFO si;
PROCESS_INFORMATION pi;
+ int orig_out, orig_in, orig_err;
+ BOOL separate_stderr = !(flags & PEX_STDERR_TO_STDOUT);
+
+ /* Ensure we have inheritable descriptors to pass to the child. */
+ orig_in = in;
+ in = _dup (orig_in);
+
+ orig_out = out;
+ out = _dup (orig_out);
+
+ if (separate_stderr)
+ {
+ orig_err = errdes;
+ errdes = _dup (orig_err);
+ }
stdin_handle = INVALID_HANDLE_VALUE;
stdout_handle = INVALID_HANDLE_VALUE;
stdin_handle = (HANDLE) _get_osfhandle (in);
stdout_handle = (HANDLE) _get_osfhandle (out);
- if (!(flags & PEX_STDERR_TO_STDOUT))
+ if (separate_stderr)
stderr_handle = (HANDLE) _get_osfhandle (errdes);
else
stderr_handle = stdout_handle;
*errmsg = "CreateProcess";
}
- /* Close the standard output and standard error handles in the
- parent. */
- if (out != STDOUT_FILENO)
- obj->funcs->close (obj, out);
- if (errdes != STDERR_FILENO)
- obj->funcs->close (obj, errdes);
+ /* If the child was created successfully, close the original file
+ descriptors. If the process creation fails, these are closed by
+ pex_run_in_environment instead. We must not close them twice as
+ that seems to cause a Windows exception. */
+
+ if (pid != (pid_t) -1)
+ {
+ if (orig_in != STDIN_FILENO)
+ _close (orig_in);
+ if (orig_out != STDOUT_FILENO)
+ _close (orig_out);
+ if (separate_stderr
+ && orig_err != STDERR_FILENO)
+ _close (orig_err);
+ }
+
+ /* Close the standard input, standard output and standard error handles
+ in the parent. */
+
+ _close (in);
+ _close (out);
+ if (separate_stderr)
+ _close (errdes);
return pid;
}
status == 3. We fix the status code to conform to the usual WIF*
macros. Note that WIFSIGNALED will never be true under CRTDLL. */
-static int
+static pid_t
pex_win32_wait (struct pex_obj *obj ATTRIBUTE_UNUSED, pid_t pid,
int *status, struct pex_time *time, int done ATTRIBUTE_UNUSED,
const char **errmsg, int *err)
pex_win32_pipe (struct pex_obj *obj ATTRIBUTE_UNUSED, int *p,
int binary)
{
- return _pipe (p, 256, binary ? _O_BINARY : _O_TEXT);
+ return _pipe (p, 256, (binary ? _O_BINARY : _O_TEXT) | _O_NOINHERIT);
}
/* Get a FILE pointer to read from a file descriptor. */
pex_win32_fdopenr (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd,
int binary)
{
+ HANDLE h = (HANDLE) _get_osfhandle (fd);
+ if (h == INVALID_HANDLE_VALUE)
+ return NULL;
+ if (! SetHandleInformation (h, HANDLE_FLAG_INHERIT, 0))
+ return NULL;
return fdopen (fd, binary ? "rb" : "r");
}