110d565efSmrg /* Utilities to execute a program in a subprocess (possibly linked by pipes
210d565efSmrg    with other subprocesses), and wait for it.  Generic Win32 specialization.
3*ec02198aSmrg    Copyright (C) 1996-2020 Free Software Foundation, Inc.
410d565efSmrg 
510d565efSmrg This file is part of the libiberty library.
610d565efSmrg Libiberty is free software; you can redistribute it and/or
710d565efSmrg modify it under the terms of the GNU Library General Public
810d565efSmrg License as published by the Free Software Foundation; either
910d565efSmrg version 2 of the License, or (at your option) any later version.
1010d565efSmrg 
1110d565efSmrg Libiberty is distributed in the hope that it will be useful,
1210d565efSmrg but WITHOUT ANY WARRANTY; without even the implied warranty of
1310d565efSmrg MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
1410d565efSmrg Library General Public License for more details.
1510d565efSmrg 
1610d565efSmrg You should have received a copy of the GNU Library General Public
1710d565efSmrg License along with libiberty; see the file COPYING.LIB.  If not,
1810d565efSmrg write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
1910d565efSmrg Boston, MA 02110-1301, USA.  */
2010d565efSmrg 
2110d565efSmrg #include "pex-common.h"
2210d565efSmrg 
2310d565efSmrg #include <windows.h>
2410d565efSmrg 
2510d565efSmrg #ifdef HAVE_STDLIB_H
2610d565efSmrg #include <stdlib.h>
2710d565efSmrg #endif
2810d565efSmrg #ifdef HAVE_STRING_H
2910d565efSmrg #include <string.h>
3010d565efSmrg #endif
3110d565efSmrg #ifdef HAVE_UNISTD_H
3210d565efSmrg #include <unistd.h>
3310d565efSmrg #endif
3410d565efSmrg #ifdef HAVE_SYS_WAIT_H
3510d565efSmrg #include <sys/wait.h>
3610d565efSmrg #endif
3710d565efSmrg 
3810d565efSmrg #include <assert.h>
3910d565efSmrg #include <process.h>
4010d565efSmrg #include <io.h>
4110d565efSmrg #include <fcntl.h>
4210d565efSmrg #include <signal.h>
4310d565efSmrg #include <sys/stat.h>
4410d565efSmrg #include <errno.h>
4510d565efSmrg #include <ctype.h>
4610d565efSmrg 
4710d565efSmrg /* mingw32 headers may not define the following.  */
4810d565efSmrg 
4910d565efSmrg #ifndef _P_WAIT
5010d565efSmrg #  define _P_WAIT	0
5110d565efSmrg #  define _P_NOWAIT	1
5210d565efSmrg #  define _P_OVERLAY	2
5310d565efSmrg #  define _P_NOWAITO	3
5410d565efSmrg #  define _P_DETACH	4
5510d565efSmrg 
5610d565efSmrg #  define WAIT_CHILD		0
5710d565efSmrg #  define WAIT_GRANDCHILD	1
5810d565efSmrg #endif
5910d565efSmrg 
6010d565efSmrg #define MINGW_NAME "Minimalist GNU for Windows"
6110d565efSmrg #define MINGW_NAME_LEN (sizeof(MINGW_NAME) - 1)
6210d565efSmrg 
6310d565efSmrg extern char *stpcpy (char *dst, const char *src);
6410d565efSmrg 
6510d565efSmrg /* Ensure that the executable pathname uses Win32 backslashes. This
6610d565efSmrg    is not necessary on NT, but on W9x, forward slashes causes
6710d565efSmrg    failure of spawn* and exec* functions (and probably any function
6810d565efSmrg    that calls CreateProcess) *iff* the executable pathname (argv[0])
6910d565efSmrg    is a quoted string.  And quoting is necessary in case a pathname
7010d565efSmrg    contains embedded white space.  You can't win.  */
7110d565efSmrg static void
backslashify(char * s)7210d565efSmrg backslashify (char *s)
7310d565efSmrg {
7410d565efSmrg   while ((s = strchr (s, '/')) != NULL)
7510d565efSmrg     *s = '\\';
7610d565efSmrg   return;
7710d565efSmrg }
7810d565efSmrg 
7910d565efSmrg static int pex_win32_open_read (struct pex_obj *, const char *, int);
8010d565efSmrg static int pex_win32_open_write (struct pex_obj *, const char *, int, int);
8110d565efSmrg static pid_t pex_win32_exec_child (struct pex_obj *, int, const char *,
8210d565efSmrg 				  char * const *, char * const *,
8310d565efSmrg                                   int, int, int, int,
8410d565efSmrg 				  const char **, int *);
8510d565efSmrg static int pex_win32_close (struct pex_obj *, int);
8610d565efSmrg static pid_t pex_win32_wait (struct pex_obj *, pid_t, int *,
8710d565efSmrg 			   struct pex_time *, int, const char **, int *);
8810d565efSmrg static int pex_win32_pipe (struct pex_obj *, int *, int);
8910d565efSmrg static FILE *pex_win32_fdopenr (struct pex_obj *, int, int);
9010d565efSmrg static FILE *pex_win32_fdopenw (struct pex_obj *, int, int);
9110d565efSmrg 
9210d565efSmrg /* The list of functions we pass to the common routines.  */
9310d565efSmrg 
9410d565efSmrg const struct pex_funcs funcs =
9510d565efSmrg {
9610d565efSmrg   pex_win32_open_read,
9710d565efSmrg   pex_win32_open_write,
9810d565efSmrg   pex_win32_exec_child,
9910d565efSmrg   pex_win32_close,
10010d565efSmrg   pex_win32_wait,
10110d565efSmrg   pex_win32_pipe,
10210d565efSmrg   pex_win32_fdopenr,
10310d565efSmrg   pex_win32_fdopenw,
10410d565efSmrg   NULL /* cleanup */
10510d565efSmrg };
10610d565efSmrg 
10710d565efSmrg /* Return a newly initialized pex_obj structure.  */
10810d565efSmrg 
10910d565efSmrg struct pex_obj *
pex_init(int flags,const char * pname,const char * tempbase)11010d565efSmrg pex_init (int flags, const char *pname, const char *tempbase)
11110d565efSmrg {
11210d565efSmrg   return pex_init_common (flags, pname, tempbase, &funcs);
11310d565efSmrg }
11410d565efSmrg 
11510d565efSmrg /* Open a file for reading.  */
11610d565efSmrg 
11710d565efSmrg static int
pex_win32_open_read(struct pex_obj * obj ATTRIBUTE_UNUSED,const char * name,int binary)11810d565efSmrg pex_win32_open_read (struct pex_obj *obj ATTRIBUTE_UNUSED, const char *name,
11910d565efSmrg 		     int binary)
12010d565efSmrg {
12110d565efSmrg   return _open (name, _O_RDONLY | (binary ? _O_BINARY : _O_TEXT));
12210d565efSmrg }
12310d565efSmrg 
12410d565efSmrg /* Open a file for writing.  */
12510d565efSmrg 
12610d565efSmrg static int
pex_win32_open_write(struct pex_obj * obj ATTRIBUTE_UNUSED,const char * name,int binary,int append)12710d565efSmrg pex_win32_open_write (struct pex_obj *obj ATTRIBUTE_UNUSED, const char *name,
12810d565efSmrg 		      int binary, int append)
12910d565efSmrg {
13010d565efSmrg   /* Note that we can't use O_EXCL here because gcc may have already
13110d565efSmrg      created the temporary file via make_temp_file.  */
13210d565efSmrg   if (append)
13310d565efSmrg     return -1;
13410d565efSmrg   return _open (name,
13510d565efSmrg 		(_O_WRONLY | _O_CREAT | _O_TRUNC
13610d565efSmrg 		 | (binary ? _O_BINARY : _O_TEXT)),
13710d565efSmrg 		_S_IREAD | _S_IWRITE);
13810d565efSmrg }
13910d565efSmrg 
14010d565efSmrg /* Close a file.  */
14110d565efSmrg 
14210d565efSmrg static int
pex_win32_close(struct pex_obj * obj ATTRIBUTE_UNUSED,int fd)14310d565efSmrg pex_win32_close (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd)
14410d565efSmrg {
14510d565efSmrg   return _close (fd);
14610d565efSmrg }
14710d565efSmrg 
14810d565efSmrg #ifdef USE_MINGW_MSYS
14910d565efSmrg static const char *mingw_keys[] = {"SOFTWARE", "Microsoft", "Windows", "CurrentVersion", "Uninstall", NULL};
15010d565efSmrg 
15110d565efSmrg /* Tack the executable on the end of a (possibly slash terminated) buffer
15210d565efSmrg    and convert everything to \. */
15310d565efSmrg static const char *
tack_on_executable(char * buf,const char * executable)15410d565efSmrg tack_on_executable (char *buf, const char *executable)
15510d565efSmrg {
15610d565efSmrg   char *p = strchr (buf, '\0');
15710d565efSmrg   if (p > buf && (p[-1] == '\\' || p[-1] == '/'))
15810d565efSmrg     p[-1] = '\0';
15910d565efSmrg   backslashify (strcat (buf, executable));
16010d565efSmrg   return buf;
16110d565efSmrg }
16210d565efSmrg 
16310d565efSmrg /* Walk down a registry hierarchy until the end.  Return the key. */
16410d565efSmrg static HKEY
openkey(HKEY hStart,const char * keys[])16510d565efSmrg openkey (HKEY hStart, const char *keys[])
16610d565efSmrg {
16710d565efSmrg   HKEY hKey, hTmp;
16810d565efSmrg   for (hKey = hStart; *keys; keys++)
16910d565efSmrg     {
17010d565efSmrg       LONG res;
17110d565efSmrg       hTmp = hKey;
17210d565efSmrg       res = RegOpenKey (hTmp, *keys, &hKey);
17310d565efSmrg 
17410d565efSmrg       if (hTmp != HKEY_LOCAL_MACHINE)
17510d565efSmrg 	RegCloseKey (hTmp);
17610d565efSmrg 
17710d565efSmrg       if (res != ERROR_SUCCESS)
17810d565efSmrg 	return NULL;
17910d565efSmrg     }
18010d565efSmrg   return hKey;
18110d565efSmrg }
18210d565efSmrg 
18310d565efSmrg /* Return the "mingw root" as derived from the mingw uninstall information. */
18410d565efSmrg static const char *
mingw_rootify(const char * executable)18510d565efSmrg mingw_rootify (const char *executable)
18610d565efSmrg {
18710d565efSmrg   HKEY hKey, hTmp;
18810d565efSmrg   DWORD maxlen;
18910d565efSmrg   char *namebuf, *foundbuf;
19010d565efSmrg   DWORD i;
19110d565efSmrg   LONG res;
19210d565efSmrg 
19310d565efSmrg   /* Open the uninstall "directory". */
19410d565efSmrg   hKey = openkey (HKEY_LOCAL_MACHINE, mingw_keys);
19510d565efSmrg 
19610d565efSmrg   /* Not found. */
19710d565efSmrg   if (!hKey)
19810d565efSmrg     return executable;
19910d565efSmrg 
20010d565efSmrg   /* Need to enumerate all of the keys here looking for one the most recent
20110d565efSmrg      one for MinGW. */
20210d565efSmrg   if (RegQueryInfoKey (hKey, NULL, NULL, NULL, NULL, &maxlen, NULL, NULL,
20310d565efSmrg 		       NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
20410d565efSmrg     {
20510d565efSmrg       RegCloseKey (hKey);
20610d565efSmrg       return executable;
20710d565efSmrg     }
20810d565efSmrg   namebuf = XNEWVEC (char, ++maxlen);
20910d565efSmrg   foundbuf = XNEWVEC (char, maxlen);
21010d565efSmrg   foundbuf[0] = '\0';
21110d565efSmrg   if (!namebuf || !foundbuf)
21210d565efSmrg     {
21310d565efSmrg       RegCloseKey (hKey);
21410d565efSmrg       free (namebuf);
21510d565efSmrg       free (foundbuf);
21610d565efSmrg       return executable;
21710d565efSmrg     }
21810d565efSmrg 
21910d565efSmrg   /* Look through all of the keys for one that begins with Minimal GNU...
22010d565efSmrg      Try to get the latest version by doing a string compare although that
22110d565efSmrg      string never really works with version number sorting. */
22210d565efSmrg   for (i = 0; RegEnumKey (hKey, i, namebuf, maxlen) == ERROR_SUCCESS; i++)
22310d565efSmrg     {
22410d565efSmrg       int match = strcasecmp (namebuf, MINGW_NAME);
22510d565efSmrg       if (match < 0)
22610d565efSmrg 	continue;
22710d565efSmrg       if (match > 0 && strncasecmp (namebuf, MINGW_NAME, MINGW_NAME_LEN) > 0)
22810d565efSmrg 	continue;
22910d565efSmrg       if (strcasecmp (namebuf, foundbuf) > 0)
23010d565efSmrg 	strcpy (foundbuf, namebuf);
23110d565efSmrg     }
23210d565efSmrg   free (namebuf);
23310d565efSmrg 
23410d565efSmrg   /* If foundbuf is empty, we didn't find anything.  Punt. */
23510d565efSmrg   if (!foundbuf[0])
23610d565efSmrg     {
23710d565efSmrg       free (foundbuf);
23810d565efSmrg       RegCloseKey (hKey);
23910d565efSmrg       return executable;
24010d565efSmrg     }
24110d565efSmrg 
24210d565efSmrg   /* Open the key that we wanted */
24310d565efSmrg   res = RegOpenKey (hKey, foundbuf, &hTmp);
24410d565efSmrg   RegCloseKey (hKey);
24510d565efSmrg   free (foundbuf);
24610d565efSmrg 
24710d565efSmrg   /* Don't know why this would fail, but you gotta check */
24810d565efSmrg   if (res != ERROR_SUCCESS)
24910d565efSmrg     return executable;
25010d565efSmrg 
25110d565efSmrg   maxlen = 0;
25210d565efSmrg   /* Get the length of the value pointed to by InstallLocation */
25310d565efSmrg   if (RegQueryValueEx (hTmp, "InstallLocation", 0, NULL, NULL,
25410d565efSmrg 		       &maxlen) != ERROR_SUCCESS || maxlen == 0)
25510d565efSmrg     {
25610d565efSmrg       RegCloseKey (hTmp);
25710d565efSmrg       return executable;
25810d565efSmrg     }
25910d565efSmrg 
26010d565efSmrg   /* Allocate space for the install location */
26110d565efSmrg   foundbuf = XNEWVEC (char, maxlen + strlen (executable));
26210d565efSmrg   if (!foundbuf)
26310d565efSmrg     {
26410d565efSmrg       free (foundbuf);
26510d565efSmrg       RegCloseKey (hTmp);
26610d565efSmrg     }
26710d565efSmrg 
26810d565efSmrg   /* Read the install location into the buffer */
26910d565efSmrg   res = RegQueryValueEx (hTmp, "InstallLocation", 0, NULL, (LPBYTE) foundbuf,
27010d565efSmrg 			 &maxlen);
27110d565efSmrg   RegCloseKey (hTmp);
27210d565efSmrg   if (res != ERROR_SUCCESS)
27310d565efSmrg     {
27410d565efSmrg       free (foundbuf);
27510d565efSmrg       return executable;
27610d565efSmrg     }
27710d565efSmrg 
27810d565efSmrg   /* Concatenate the install location and the executable, turn all slashes
27910d565efSmrg      to backslashes, and return that. */
28010d565efSmrg   return tack_on_executable (foundbuf, executable);
28110d565efSmrg }
28210d565efSmrg 
28310d565efSmrg /* Read the install location of msys from it's installation file and
28410d565efSmrg    rootify the executable based on that. */
28510d565efSmrg static const char *
msys_rootify(const char * executable)28610d565efSmrg msys_rootify (const char *executable)
28710d565efSmrg {
28810d565efSmrg   size_t bufsize = 64;
28910d565efSmrg   size_t execlen = strlen (executable) + 1;
29010d565efSmrg   char *buf;
29110d565efSmrg   DWORD res = 0;
29210d565efSmrg   for (;;)
29310d565efSmrg     {
29410d565efSmrg       buf = XNEWVEC (char, bufsize + execlen);
29510d565efSmrg       if (!buf)
29610d565efSmrg 	break;
29710d565efSmrg       res = GetPrivateProfileString ("InstallSettings", "InstallPath", NULL,
29810d565efSmrg 				     buf, bufsize, "msys.ini");
29910d565efSmrg       if (!res)
30010d565efSmrg 	break;
30110d565efSmrg       if (strlen (buf) < bufsize)
30210d565efSmrg 	break;
30310d565efSmrg       res = 0;
30410d565efSmrg       free (buf);
30510d565efSmrg       bufsize *= 2;
30610d565efSmrg       if (bufsize > 65536)
30710d565efSmrg 	{
30810d565efSmrg 	  buf = NULL;
30910d565efSmrg 	  break;
31010d565efSmrg 	}
31110d565efSmrg     }
31210d565efSmrg 
31310d565efSmrg   if (res)
31410d565efSmrg     return tack_on_executable (buf, executable);
31510d565efSmrg 
31610d565efSmrg   /* failed */
31710d565efSmrg   free (buf);
31810d565efSmrg   return executable;
31910d565efSmrg }
32010d565efSmrg #endif
32110d565efSmrg 
32210d565efSmrg /* Return the number of arguments in an argv array, not including the null
32310d565efSmrg    terminating argument. */
32410d565efSmrg 
32510d565efSmrg static int
argv_to_argc(char * const * argv)32610d565efSmrg argv_to_argc (char *const *argv)
32710d565efSmrg {
32810d565efSmrg   char *const *i = argv;
32910d565efSmrg   while (*i)
33010d565efSmrg     i++;
33110d565efSmrg   return i - argv;
33210d565efSmrg }
33310d565efSmrg 
33410d565efSmrg /* Return a Windows command-line from ARGV.  It is the caller's
33510d565efSmrg    responsibility to free the string returned.  */
33610d565efSmrg 
33710d565efSmrg static char *
argv_to_cmdline(char * const * argv)33810d565efSmrg argv_to_cmdline (char *const *argv)
33910d565efSmrg {
34010d565efSmrg   char *cmdline;
34110d565efSmrg   char *p;
34210d565efSmrg   size_t cmdline_len;
34310d565efSmrg   int i, j, k;
34410d565efSmrg   int needs_quotes;
34510d565efSmrg 
34610d565efSmrg   cmdline_len = 0;
34710d565efSmrg   for (i = 0; argv[i]; i++)
34810d565efSmrg     {
34910d565efSmrg       /* We only quote arguments that contain spaces, \t or " characters to
35010d565efSmrg 	 prevent wasting 2 chars per argument of the CreateProcess 32k char
35110d565efSmrg 	 limit.  We need only escape embedded double-quotes and immediately
35210d565efSmrg 	 preceeding backslash characters.  A sequence of backslach characters
35310d565efSmrg 	 that is not follwed by a double quote character will not be
35410d565efSmrg 	 escaped.  */
35510d565efSmrg       needs_quotes = 0;
35610d565efSmrg       for (j = 0; argv[i][j]; j++)
35710d565efSmrg 	{
35810d565efSmrg 	  if (argv[i][j] == ' ' || argv[i][j] == '\t' || argv[i][j] == '"')
35910d565efSmrg 	    {
36010d565efSmrg 	      needs_quotes = 1;
36110d565efSmrg 	    }
36210d565efSmrg 
36310d565efSmrg 	  if (argv[i][j] == '"')
36410d565efSmrg 	    {
36510d565efSmrg 	      /* Escape preceeding backslashes.  */
36610d565efSmrg 	      for (k = j - 1; k >= 0 && argv[i][k] == '\\'; k--)
36710d565efSmrg 		cmdline_len++;
36810d565efSmrg 	      /* Escape the qote character.  */
36910d565efSmrg 	      cmdline_len++;
37010d565efSmrg 	    }
37110d565efSmrg 	}
37210d565efSmrg       if (j == 0)
37310d565efSmrg 	needs_quotes = 1;
37410d565efSmrg       /* Trailing backslashes also need to be escaped because they will be
37510d565efSmrg          followed by the terminating quote.  */
37610d565efSmrg       if (needs_quotes)
37710d565efSmrg         {
37810d565efSmrg           for (k = j - 1; k >= 0 && argv[i][k] == '\\'; k--)
37910d565efSmrg             cmdline_len++;
38010d565efSmrg         }
38110d565efSmrg       cmdline_len += j;
38210d565efSmrg       /* for leading and trailing quotes and space */
38310d565efSmrg       cmdline_len += needs_quotes * 2 + 1;
38410d565efSmrg     }
38510d565efSmrg   cmdline = XNEWVEC (char, cmdline_len);
38610d565efSmrg   p = cmdline;
38710d565efSmrg   for (i = 0; argv[i]; i++)
38810d565efSmrg     {
38910d565efSmrg       needs_quotes = 0;
39010d565efSmrg       for (j = 0; argv[i][j]; j++)
39110d565efSmrg         {
39210d565efSmrg           if (argv[i][j] == ' ' || argv[i][j] == '\t' || argv[i][j] == '"')
39310d565efSmrg             {
39410d565efSmrg               needs_quotes = 1;
39510d565efSmrg               break;
39610d565efSmrg             }
39710d565efSmrg         }
39810d565efSmrg       if (j == 0)
39910d565efSmrg 	needs_quotes = 1;
40010d565efSmrg 
40110d565efSmrg       if (needs_quotes)
40210d565efSmrg         {
40310d565efSmrg           *p++ = '"';
40410d565efSmrg         }
40510d565efSmrg       for (j = 0; argv[i][j]; j++)
40610d565efSmrg 	{
40710d565efSmrg 	  if (argv[i][j] == '"')
40810d565efSmrg 	    {
40910d565efSmrg 	      for (k = j - 1; k >= 0 && argv[i][k] == '\\'; k--)
41010d565efSmrg 		*p++ = '\\';
41110d565efSmrg 	      *p++ = '\\';
41210d565efSmrg 	    }
41310d565efSmrg 	  *p++ = argv[i][j];
41410d565efSmrg 	}
41510d565efSmrg       if (needs_quotes)
41610d565efSmrg         {
41710d565efSmrg           for (k = j - 1; k >= 0 && argv[i][k] == '\\'; k--)
41810d565efSmrg             *p++ = '\\';
41910d565efSmrg           *p++ = '"';
42010d565efSmrg         }
42110d565efSmrg       *p++ = ' ';
42210d565efSmrg     }
42310d565efSmrg   p[-1] = '\0';
42410d565efSmrg   return cmdline;
42510d565efSmrg }
42610d565efSmrg 
42710d565efSmrg /* We'll try the passed filename with all the known standard
42810d565efSmrg    extensions, and then without extension.  We try no extension
42910d565efSmrg    last so that we don't try to run some random extension-less
43010d565efSmrg    file that might be hanging around.  We try both extension
43110d565efSmrg    and no extension so that we don't need any fancy logic
43210d565efSmrg    to determine if a file has extension.  */
43310d565efSmrg static const char *const
43410d565efSmrg std_suffixes[] = {
43510d565efSmrg   ".com",
43610d565efSmrg   ".exe",
43710d565efSmrg   ".bat",
43810d565efSmrg   ".cmd",
43910d565efSmrg   "",
44010d565efSmrg   0
44110d565efSmrg };
44210d565efSmrg 
44310d565efSmrg /* Returns the full path to PROGRAM.  If SEARCH is true, look for
44410d565efSmrg    PROGRAM in each directory in PATH.  */
44510d565efSmrg 
44610d565efSmrg static char *
find_executable(const char * program,BOOL search)44710d565efSmrg find_executable (const char *program, BOOL search)
44810d565efSmrg {
44910d565efSmrg   char *full_executable;
45010d565efSmrg   char *e;
45110d565efSmrg   size_t fe_len;
45210d565efSmrg   const char *path = 0;
45310d565efSmrg   const char *const *ext;
45410d565efSmrg   const char *p, *q;
45510d565efSmrg   size_t proglen = strlen (program);
45610d565efSmrg   int has_slash = (strchr (program, '/') || strchr (program, '\\'));
45710d565efSmrg   HANDLE h;
45810d565efSmrg 
45910d565efSmrg   if (has_slash)
46010d565efSmrg     search = FALSE;
46110d565efSmrg 
46210d565efSmrg   if (search)
46310d565efSmrg     path = getenv ("PATH");
46410d565efSmrg   if (!path)
46510d565efSmrg     path = "";
46610d565efSmrg 
46710d565efSmrg   fe_len = 0;
46810d565efSmrg   for (p = path; *p; p = q)
46910d565efSmrg     {
47010d565efSmrg       q = p;
47110d565efSmrg       while (*q != ';' && *q != '\0')
47210d565efSmrg 	q++;
47310d565efSmrg       if ((size_t)(q - p) > fe_len)
47410d565efSmrg 	fe_len = q - p;
47510d565efSmrg       if (*q == ';')
47610d565efSmrg 	q++;
47710d565efSmrg     }
47810d565efSmrg   fe_len = fe_len + 1 + proglen + 5 /* space for extension */;
47910d565efSmrg   full_executable = XNEWVEC (char, fe_len);
48010d565efSmrg 
48110d565efSmrg   p = path;
48210d565efSmrg   do
48310d565efSmrg     {
48410d565efSmrg       q = p;
48510d565efSmrg       while (*q != ';' && *q != '\0')
48610d565efSmrg 	q++;
48710d565efSmrg 
48810d565efSmrg       e = full_executable;
48910d565efSmrg       memcpy (e, p, q - p);
49010d565efSmrg       e += (q - p);
49110d565efSmrg       if (q - p)
49210d565efSmrg 	*e++ = '\\';
49310d565efSmrg       strcpy (e, program);
49410d565efSmrg 
49510d565efSmrg       if (*q == ';')
49610d565efSmrg 	q++;
49710d565efSmrg 
49810d565efSmrg       for (e = full_executable; *e; e++)
49910d565efSmrg 	if (*e == '/')
50010d565efSmrg 	  *e = '\\';
50110d565efSmrg 
50210d565efSmrg       /* At this point, e points to the terminating NUL character for
50310d565efSmrg          full_executable.  */
50410d565efSmrg       for (ext = std_suffixes; *ext; ext++)
50510d565efSmrg 	{
50610d565efSmrg 	  /* Remove any current extension.  */
50710d565efSmrg 	  *e = '\0';
50810d565efSmrg 	  /* Add the new one.  */
50910d565efSmrg 	  strcat (full_executable, *ext);
51010d565efSmrg 
51110d565efSmrg 	  /* Attempt to open this file.  */
51210d565efSmrg 	  h = CreateFile (full_executable, GENERIC_READ,
51310d565efSmrg 			  FILE_SHARE_READ | FILE_SHARE_WRITE,
51410d565efSmrg 			  0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
51510d565efSmrg 	  if (h != INVALID_HANDLE_VALUE)
51610d565efSmrg 	    goto found;
51710d565efSmrg 	}
51810d565efSmrg       p = q;
51910d565efSmrg     }
52010d565efSmrg   while (*p);
52110d565efSmrg   free (full_executable);
52210d565efSmrg   return 0;
52310d565efSmrg 
52410d565efSmrg  found:
52510d565efSmrg   CloseHandle (h);
52610d565efSmrg   return full_executable;
52710d565efSmrg }
52810d565efSmrg 
52910d565efSmrg /* Low-level process creation function and helper.  */
53010d565efSmrg 
53110d565efSmrg static int
env_compare(const void * a_ptr,const void * b_ptr)53210d565efSmrg env_compare (const void *a_ptr, const void *b_ptr)
53310d565efSmrg {
53410d565efSmrg   const char *a;
53510d565efSmrg   const char *b;
53610d565efSmrg   unsigned char c1;
53710d565efSmrg   unsigned char c2;
53810d565efSmrg 
53910d565efSmrg   a = *(const char **) a_ptr;
54010d565efSmrg   b = *(const char **) b_ptr;
54110d565efSmrg 
54210d565efSmrg   /* a and b will be of the form: VAR=VALUE
54310d565efSmrg      We compare only the variable name part here using a case-insensitive
54410d565efSmrg      comparison algorithm.  It might appear that in fact strcasecmp () can
54510d565efSmrg      take the place of this whole function, and indeed it could, save for
54610d565efSmrg      the fact that it would fail in cases such as comparing A1=foo and
54710d565efSmrg      A=bar (because 1 is less than = in the ASCII character set).
54810d565efSmrg      (Environment variables containing no numbers would work in such a
54910d565efSmrg      scenario.)  */
55010d565efSmrg 
55110d565efSmrg   do
55210d565efSmrg     {
55310d565efSmrg       c1 = (unsigned char) tolower (*a++);
55410d565efSmrg       c2 = (unsigned char) tolower (*b++);
55510d565efSmrg 
55610d565efSmrg       if (c1 == '=')
55710d565efSmrg         c1 = '\0';
55810d565efSmrg 
55910d565efSmrg       if (c2 == '=')
56010d565efSmrg         c2 = '\0';
56110d565efSmrg     }
56210d565efSmrg   while (c1 == c2 && c1 != '\0');
56310d565efSmrg 
56410d565efSmrg   return c1 - c2;
56510d565efSmrg }
56610d565efSmrg 
56710d565efSmrg /* Execute a Windows executable as a child process.  This will fail if the
56810d565efSmrg  * target is not actually an executable, such as if it is a shell script. */
56910d565efSmrg 
57010d565efSmrg static pid_t
win32_spawn(const char * executable,BOOL search,char * const * argv,char * const * env,DWORD dwCreationFlags,LPSTARTUPINFO si,LPPROCESS_INFORMATION pi)57110d565efSmrg win32_spawn (const char *executable,
57210d565efSmrg 	     BOOL search,
57310d565efSmrg 	     char *const *argv,
57410d565efSmrg              char *const *env, /* array of strings of the form: VAR=VALUE */
57510d565efSmrg 	     DWORD dwCreationFlags,
57610d565efSmrg 	     LPSTARTUPINFO si,
57710d565efSmrg 	     LPPROCESS_INFORMATION pi)
57810d565efSmrg {
57910d565efSmrg   char *full_executable;
58010d565efSmrg   char *cmdline;
58110d565efSmrg   char **env_copy;
58210d565efSmrg   char *env_block = NULL;
58310d565efSmrg 
58410d565efSmrg   full_executable = NULL;
58510d565efSmrg   cmdline = NULL;
58610d565efSmrg 
58710d565efSmrg   if (env)
58810d565efSmrg     {
58910d565efSmrg       int env_size;
59010d565efSmrg 
59110d565efSmrg       /* Count the number of environment bindings supplied.  */
59210d565efSmrg       for (env_size = 0; env[env_size]; env_size++)
59310d565efSmrg         continue;
59410d565efSmrg 
59510d565efSmrg       /* Assemble an environment block, if required.  This consists of
59610d565efSmrg          VAR=VALUE strings juxtaposed (with one null character between each
59710d565efSmrg          pair) and an additional null at the end.  */
59810d565efSmrg       if (env_size > 0)
59910d565efSmrg         {
60010d565efSmrg           int var;
60110d565efSmrg           int total_size = 1; /* 1 is for the final null.  */
60210d565efSmrg           char *bufptr;
60310d565efSmrg 
60410d565efSmrg           /* Windows needs the members of the block to be sorted by variable
60510d565efSmrg              name.  */
60610d565efSmrg           env_copy = (char **) alloca (sizeof (char *) * env_size);
60710d565efSmrg           memcpy (env_copy, env, sizeof (char *) * env_size);
60810d565efSmrg           qsort (env_copy, env_size, sizeof (char *), env_compare);
60910d565efSmrg 
61010d565efSmrg           for (var = 0; var < env_size; var++)
61110d565efSmrg             total_size += strlen (env[var]) + 1;
61210d565efSmrg 
61310d565efSmrg           env_block = XNEWVEC (char, total_size);
61410d565efSmrg           bufptr = env_block;
61510d565efSmrg           for (var = 0; var < env_size; var++)
61610d565efSmrg             bufptr = stpcpy (bufptr, env_copy[var]) + 1;
61710d565efSmrg 
61810d565efSmrg           *bufptr = '\0';
61910d565efSmrg         }
62010d565efSmrg     }
62110d565efSmrg 
62210d565efSmrg   full_executable = find_executable (executable, search);
62310d565efSmrg   if (!full_executable)
62410d565efSmrg     goto error;
62510d565efSmrg   cmdline = argv_to_cmdline (argv);
62610d565efSmrg   if (!cmdline)
62710d565efSmrg     goto error;
62810d565efSmrg 
62910d565efSmrg   /* Create the child process.  */
63010d565efSmrg   if (!CreateProcess (full_executable, cmdline,
63110d565efSmrg 		      /*lpProcessAttributes=*/NULL,
63210d565efSmrg 		      /*lpThreadAttributes=*/NULL,
63310d565efSmrg 		      /*bInheritHandles=*/TRUE,
63410d565efSmrg 		      dwCreationFlags,
63510d565efSmrg 		      (LPVOID) env_block,
63610d565efSmrg 		      /*lpCurrentDirectory=*/NULL,
63710d565efSmrg 		      si,
63810d565efSmrg 		      pi))
63910d565efSmrg     {
64010d565efSmrg       free (env_block);
64110d565efSmrg 
64210d565efSmrg       free (full_executable);
64310d565efSmrg 
64410d565efSmrg       return (pid_t) -1;
64510d565efSmrg     }
64610d565efSmrg 
64710d565efSmrg   /* Clean up.  */
64810d565efSmrg   CloseHandle (pi->hThread);
64910d565efSmrg   free (full_executable);
65010d565efSmrg   free (env_block);
65110d565efSmrg 
65210d565efSmrg   return (pid_t) pi->hProcess;
65310d565efSmrg 
65410d565efSmrg  error:
65510d565efSmrg   free (env_block);
65610d565efSmrg   free (cmdline);
65710d565efSmrg   free (full_executable);
65810d565efSmrg 
65910d565efSmrg   return (pid_t) -1;
66010d565efSmrg }
66110d565efSmrg 
66210d565efSmrg /* Spawn a script.  This simulates the Unix script execution mechanism.
66310d565efSmrg    This function is called as a fallback if win32_spawn fails. */
66410d565efSmrg 
66510d565efSmrg static pid_t
spawn_script(const char * executable,char * const * argv,char * const * env,DWORD dwCreationFlags,LPSTARTUPINFO si,LPPROCESS_INFORMATION pi)66610d565efSmrg spawn_script (const char *executable, char *const *argv,
66710d565efSmrg               char* const *env,
66810d565efSmrg 	      DWORD dwCreationFlags,
66910d565efSmrg 	      LPSTARTUPINFO si,
67010d565efSmrg 	      LPPROCESS_INFORMATION pi)
67110d565efSmrg {
67210d565efSmrg   pid_t pid = (pid_t) -1;
67310d565efSmrg   int save_errno = errno;
67410d565efSmrg   int fd = _open (executable, _O_RDONLY);
67510d565efSmrg 
67610d565efSmrg   /* Try to open script, check header format, extract interpreter path,
67710d565efSmrg      and spawn script using that interpretter. */
67810d565efSmrg   if (fd >= 0)
67910d565efSmrg     {
68010d565efSmrg       char buf[MAX_PATH + 5];
68110d565efSmrg       int len = _read (fd, buf, sizeof (buf) - 1);
68210d565efSmrg       _close (fd);
68310d565efSmrg       if (len > 3)
68410d565efSmrg 	{
68510d565efSmrg 	  char *eol;
68610d565efSmrg 	  buf[len] = '\0';
68710d565efSmrg 	  eol = strchr (buf, '\n');
68810d565efSmrg 	  if (eol && strncmp (buf, "#!", 2) == 0)
68910d565efSmrg 	    {
69010d565efSmrg 
69110d565efSmrg 	      /* Header format is OK. */
69210d565efSmrg 	      char *executable1;
69310d565efSmrg               int new_argc;
69410d565efSmrg               const char **avhere;
69510d565efSmrg 
69610d565efSmrg 	      /* Extract interpreter path. */
69710d565efSmrg 	      do
69810d565efSmrg 		*eol = '\0';
69910d565efSmrg 	      while (*--eol == '\r' || *eol == ' ' || *eol == '\t');
70010d565efSmrg 	      for (executable1 = buf + 2; *executable1 == ' ' || *executable1 == '\t'; executable1++)
70110d565efSmrg 		continue;
70210d565efSmrg 	      backslashify (executable1);
70310d565efSmrg 
70410d565efSmrg 	      /* Duplicate argv, prepending the interpreter path. */
70510d565efSmrg 	      new_argc = argv_to_argc (argv) + 1;
70610d565efSmrg 	      avhere = XNEWVEC (const char *, new_argc + 1);
70710d565efSmrg 	      *avhere = executable1;
70810d565efSmrg 	      memcpy (avhere + 1, argv, new_argc * sizeof(*argv));
70910d565efSmrg 	      argv = (char *const *)avhere;
71010d565efSmrg 
71110d565efSmrg 	      /* Spawn the child. */
71210d565efSmrg #ifndef USE_MINGW_MSYS
71310d565efSmrg 	      executable = strrchr (executable1, '\\') + 1;
71410d565efSmrg 	      if (!executable)
71510d565efSmrg 		executable = executable1;
71610d565efSmrg 	      pid = win32_spawn (executable, TRUE, argv, env,
71710d565efSmrg 				 dwCreationFlags, si, pi);
71810d565efSmrg #else
71910d565efSmrg 	      if (strchr (executable1, '\\') == NULL)
72010d565efSmrg 		pid = win32_spawn (executable1, TRUE, argv, env,
72110d565efSmrg 				   dwCreationFlags, si, pi);
72210d565efSmrg 	      else if (executable1[0] != '\\')
72310d565efSmrg 		pid = win32_spawn (executable1, FALSE, argv, env,
72410d565efSmrg 				   dwCreationFlags, si, pi);
72510d565efSmrg 	      else
72610d565efSmrg 		{
72710d565efSmrg 		  const char *newex = mingw_rootify (executable1);
72810d565efSmrg 		  *avhere = newex;
72910d565efSmrg 		  pid = win32_spawn (newex, FALSE, argv, env,
73010d565efSmrg 				     dwCreationFlags, si, pi);
73110d565efSmrg 		  if (executable1 != newex)
73210d565efSmrg 		    free ((char *) newex);
73310d565efSmrg 		  if (pid == (pid_t) -1)
73410d565efSmrg 		    {
73510d565efSmrg 		      newex = msys_rootify (executable1);
73610d565efSmrg 		      if (newex != executable1)
73710d565efSmrg 			{
73810d565efSmrg 			  *avhere = newex;
73910d565efSmrg 			  pid = win32_spawn (newex, FALSE, argv, env,
74010d565efSmrg 					     dwCreationFlags, si, pi);
74110d565efSmrg 			  free ((char *) newex);
74210d565efSmrg 			}
74310d565efSmrg 		    }
74410d565efSmrg 		}
74510d565efSmrg #endif
74610d565efSmrg 	      free (avhere);
74710d565efSmrg 	    }
74810d565efSmrg 	}
74910d565efSmrg     }
75010d565efSmrg   if (pid == (pid_t) -1)
75110d565efSmrg     errno = save_errno;
75210d565efSmrg   return pid;
75310d565efSmrg }
75410d565efSmrg 
75510d565efSmrg /* Execute a child.  */
75610d565efSmrg 
75710d565efSmrg static pid_t
pex_win32_exec_child(struct pex_obj * obj ATTRIBUTE_UNUSED,int flags,const char * executable,char * const * argv,char * const * env,int in,int out,int errdes,int toclose ATTRIBUTE_UNUSED,const char ** errmsg,int * err)75810d565efSmrg pex_win32_exec_child (struct pex_obj *obj ATTRIBUTE_UNUSED, int flags,
75910d565efSmrg 		      const char *executable, char * const * argv,
76010d565efSmrg                       char* const* env,
76110d565efSmrg 		      int in, int out, int errdes,
76210d565efSmrg 		      int toclose ATTRIBUTE_UNUSED,
76310d565efSmrg 		      const char **errmsg,
76410d565efSmrg 		      int *err)
76510d565efSmrg {
76610d565efSmrg   pid_t pid;
76710d565efSmrg   HANDLE stdin_handle;
76810d565efSmrg   HANDLE stdout_handle;
76910d565efSmrg   HANDLE stderr_handle;
77010d565efSmrg   DWORD dwCreationFlags;
77110d565efSmrg   OSVERSIONINFO version_info;
77210d565efSmrg   STARTUPINFO si;
77310d565efSmrg   PROCESS_INFORMATION pi;
77410d565efSmrg   int orig_out, orig_in, orig_err;
77510d565efSmrg   BOOL separate_stderr = !(flags & PEX_STDERR_TO_STDOUT);
77610d565efSmrg 
77710d565efSmrg   /* Ensure we have inheritable descriptors to pass to the child.  */
77810d565efSmrg   orig_in = in;
77910d565efSmrg   in = _dup (orig_in);
78010d565efSmrg 
78110d565efSmrg   orig_out = out;
78210d565efSmrg   out = _dup (orig_out);
78310d565efSmrg 
78410d565efSmrg   if (separate_stderr)
78510d565efSmrg     {
78610d565efSmrg       orig_err = errdes;
78710d565efSmrg       errdes = _dup (orig_err);
78810d565efSmrg     }
78910d565efSmrg 
79010d565efSmrg   stdin_handle = INVALID_HANDLE_VALUE;
79110d565efSmrg   stdout_handle = INVALID_HANDLE_VALUE;
79210d565efSmrg   stderr_handle = INVALID_HANDLE_VALUE;
79310d565efSmrg 
79410d565efSmrg   stdin_handle = (HANDLE) _get_osfhandle (in);
79510d565efSmrg   stdout_handle = (HANDLE) _get_osfhandle (out);
79610d565efSmrg   if (separate_stderr)
79710d565efSmrg     stderr_handle = (HANDLE) _get_osfhandle (errdes);
79810d565efSmrg   else
79910d565efSmrg     stderr_handle = stdout_handle;
80010d565efSmrg 
80110d565efSmrg   /* Determine the version of Windows we are running on.  */
80210d565efSmrg   version_info.dwOSVersionInfoSize = sizeof (version_info);
80310d565efSmrg   GetVersionEx (&version_info);
80410d565efSmrg   if (version_info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
80510d565efSmrg     /* On Windows 95/98/ME the CREATE_NO_WINDOW flag is not
80610d565efSmrg        supported, so we cannot avoid creating a console window.  */
80710d565efSmrg     dwCreationFlags = 0;
80810d565efSmrg   else
80910d565efSmrg     {
81010d565efSmrg       HANDLE conout_handle;
81110d565efSmrg 
81210d565efSmrg       /* Determine whether or not we have an associated console.  */
81310d565efSmrg       conout_handle = CreateFile("CONOUT$",
81410d565efSmrg 				 GENERIC_WRITE,
81510d565efSmrg 				 FILE_SHARE_WRITE,
81610d565efSmrg 				 /*lpSecurityAttributes=*/NULL,
81710d565efSmrg 				 OPEN_EXISTING,
81810d565efSmrg 				 FILE_ATTRIBUTE_NORMAL,
81910d565efSmrg 				 /*hTemplateFile=*/NULL);
82010d565efSmrg       if (conout_handle == INVALID_HANDLE_VALUE)
82110d565efSmrg 	/* There is no console associated with this process.  Since
82210d565efSmrg 	   the child is a console process, the OS would normally
82310d565efSmrg 	   create a new console Window for the child.  Since we'll be
82410d565efSmrg 	   redirecting the child's standard streams, we do not need
82510d565efSmrg 	   the console window.  */
82610d565efSmrg 	dwCreationFlags = CREATE_NO_WINDOW;
82710d565efSmrg       else
82810d565efSmrg 	{
82910d565efSmrg 	  /* There is a console associated with the process, so the OS
83010d565efSmrg 	     will not create a new console.  And, if we use
83110d565efSmrg 	     CREATE_NO_WINDOW in this situation, the child will have
83210d565efSmrg 	     no associated console.  Therefore, if the child's
83310d565efSmrg 	     standard streams are connected to the console, the output
83410d565efSmrg 	     will be discarded.  */
83510d565efSmrg 	  CloseHandle(conout_handle);
83610d565efSmrg 	  dwCreationFlags = 0;
83710d565efSmrg 	}
83810d565efSmrg     }
83910d565efSmrg 
84010d565efSmrg   /* Since the child will be a console process, it will, by default,
84110d565efSmrg      connect standard input/output to its console.  However, we want
84210d565efSmrg      the child to use the handles specifically designated above.  In
84310d565efSmrg      addition, if there is no console (such as when we are running in
84410d565efSmrg      a Cygwin X window), then we must redirect the child's
84510d565efSmrg      input/output, as there is no console for the child to use.  */
84610d565efSmrg   memset (&si, 0, sizeof (si));
84710d565efSmrg   si.cb = sizeof (si);
84810d565efSmrg   si.dwFlags = STARTF_USESTDHANDLES;
84910d565efSmrg   si.hStdInput = stdin_handle;
85010d565efSmrg   si.hStdOutput = stdout_handle;
85110d565efSmrg   si.hStdError = stderr_handle;
85210d565efSmrg 
85310d565efSmrg   /* Create the child process.  */
85410d565efSmrg   pid = win32_spawn (executable, (flags & PEX_SEARCH) != 0,
85510d565efSmrg 		     argv, env, dwCreationFlags, &si, &pi);
85610d565efSmrg   if (pid == (pid_t) -1)
85710d565efSmrg     pid = spawn_script (executable, argv, env, dwCreationFlags,
85810d565efSmrg                         &si, &pi);
85910d565efSmrg   if (pid == (pid_t) -1)
86010d565efSmrg     {
86110d565efSmrg       *err = ENOENT;
86210d565efSmrg       *errmsg = "CreateProcess";
86310d565efSmrg     }
86410d565efSmrg 
86510d565efSmrg   /* If the child was created successfully, close the original file
86610d565efSmrg      descriptors.  If the process creation fails, these are closed by
86710d565efSmrg      pex_run_in_environment instead.  We must not close them twice as
86810d565efSmrg      that seems to cause a Windows exception.  */
86910d565efSmrg 
87010d565efSmrg   if (pid != (pid_t) -1)
87110d565efSmrg     {
87210d565efSmrg       if (orig_in != STDIN_FILENO)
87310d565efSmrg 	_close (orig_in);
87410d565efSmrg       if (orig_out != STDOUT_FILENO)
87510d565efSmrg 	_close (orig_out);
87610d565efSmrg       if (separate_stderr
87710d565efSmrg 	  && orig_err != STDERR_FILENO)
87810d565efSmrg 	_close (orig_err);
87910d565efSmrg     }
88010d565efSmrg 
88110d565efSmrg   /* Close the standard input, standard output and standard error handles
88210d565efSmrg      in the parent.  */
88310d565efSmrg 
88410d565efSmrg   _close (in);
88510d565efSmrg   _close (out);
88610d565efSmrg   if (separate_stderr)
88710d565efSmrg     _close (errdes);
88810d565efSmrg 
88910d565efSmrg   return pid;
89010d565efSmrg }
89110d565efSmrg 
89210d565efSmrg /* Wait for a child process to complete.  MS CRTDLL doesn't return
89310d565efSmrg    enough information in status to decide if the child exited due to a
89410d565efSmrg    signal or not, rather it simply returns an integer with the exit
89510d565efSmrg    code of the child; eg., if the child exited with an abort() call
89610d565efSmrg    and didn't have a handler for SIGABRT, it simply returns with
89710d565efSmrg    status == 3.  We fix the status code to conform to the usual WIF*
89810d565efSmrg    macros.  Note that WIFSIGNALED will never be true under CRTDLL. */
89910d565efSmrg 
90010d565efSmrg 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)90110d565efSmrg pex_win32_wait (struct pex_obj *obj ATTRIBUTE_UNUSED, pid_t pid,
90210d565efSmrg 		int *status, struct pex_time *time, int done ATTRIBUTE_UNUSED,
90310d565efSmrg 		const char **errmsg, int *err)
90410d565efSmrg {
90510d565efSmrg   DWORD termstat;
90610d565efSmrg   HANDLE h;
90710d565efSmrg 
90810d565efSmrg   if (time != NULL)
90910d565efSmrg     memset (time, 0, sizeof *time);
91010d565efSmrg 
91110d565efSmrg   h = (HANDLE) pid;
91210d565efSmrg 
91310d565efSmrg   /* FIXME: If done is non-zero, we should probably try to kill the
91410d565efSmrg      process.  */
91510d565efSmrg   if (WaitForSingleObject (h, INFINITE) != WAIT_OBJECT_0)
91610d565efSmrg     {
91710d565efSmrg       CloseHandle (h);
91810d565efSmrg       *err = ECHILD;
91910d565efSmrg       *errmsg = "WaitForSingleObject";
92010d565efSmrg       return -1;
92110d565efSmrg     }
92210d565efSmrg 
92310d565efSmrg   GetExitCodeProcess (h, &termstat);
92410d565efSmrg   CloseHandle (h);
92510d565efSmrg 
92610d565efSmrg   /* A value of 3 indicates that the child caught a signal, but not
92710d565efSmrg      which one.  Since only SIGABRT, SIGFPE and SIGINT do anything, we
92810d565efSmrg      report SIGABRT.  */
92910d565efSmrg   if (termstat == 3)
93010d565efSmrg     *status = SIGABRT;
93110d565efSmrg   else
93210d565efSmrg     *status = (termstat & 0xff) << 8;
93310d565efSmrg 
93410d565efSmrg   return 0;
93510d565efSmrg }
93610d565efSmrg 
93710d565efSmrg /* Create a pipe.  */
93810d565efSmrg 
93910d565efSmrg static int
pex_win32_pipe(struct pex_obj * obj ATTRIBUTE_UNUSED,int * p,int binary)94010d565efSmrg pex_win32_pipe (struct pex_obj *obj ATTRIBUTE_UNUSED, int *p,
94110d565efSmrg 		int binary)
94210d565efSmrg {
94310d565efSmrg   return _pipe (p, 256, (binary ? _O_BINARY : _O_TEXT) | _O_NOINHERIT);
94410d565efSmrg }
94510d565efSmrg 
94610d565efSmrg /* Get a FILE pointer to read from a file descriptor.  */
94710d565efSmrg 
94810d565efSmrg static FILE *
pex_win32_fdopenr(struct pex_obj * obj ATTRIBUTE_UNUSED,int fd,int binary)94910d565efSmrg pex_win32_fdopenr (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd,
95010d565efSmrg 		   int binary)
95110d565efSmrg {
95210d565efSmrg   HANDLE h = (HANDLE) _get_osfhandle (fd);
95310d565efSmrg   if (h == INVALID_HANDLE_VALUE)
95410d565efSmrg     return NULL;
95510d565efSmrg   if (! SetHandleInformation (h, HANDLE_FLAG_INHERIT, 0))
95610d565efSmrg     return NULL;
95710d565efSmrg   return fdopen (fd, binary ? "rb" : "r");
95810d565efSmrg }
95910d565efSmrg 
96010d565efSmrg static FILE *
pex_win32_fdopenw(struct pex_obj * obj ATTRIBUTE_UNUSED,int fd,int binary)96110d565efSmrg pex_win32_fdopenw (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd,
96210d565efSmrg 		   int binary)
96310d565efSmrg {
96410d565efSmrg   HANDLE h = (HANDLE) _get_osfhandle (fd);
96510d565efSmrg   if (h == INVALID_HANDLE_VALUE)
96610d565efSmrg     return NULL;
96710d565efSmrg   if (! SetHandleInformation (h, HANDLE_FLAG_INHERIT, 0))
96810d565efSmrg     return NULL;
96910d565efSmrg   return fdopen (fd, binary ? "wb" : "w");
97010d565efSmrg }
97110d565efSmrg 
97210d565efSmrg #ifdef MAIN
97310d565efSmrg #include <stdio.h>
97410d565efSmrg 
97510d565efSmrg int
main(int argc ATTRIBUTE_UNUSED,char ** argv)97610d565efSmrg main (int argc ATTRIBUTE_UNUSED, char **argv)
97710d565efSmrg {
97810d565efSmrg   char const *errmsg;
97910d565efSmrg   int err;
98010d565efSmrg   argv++;
98110d565efSmrg   printf ("%ld\n", (long) pex_win32_exec_child (NULL, PEX_SEARCH, argv[0], argv, NULL, 0, 0, 1, 2, &errmsg, &err));
98210d565efSmrg   exit (0);
98310d565efSmrg }
98410d565efSmrg #endif
985