xref: /netbsd-src/external/gpl3/gdb/dist/libiberty/pex-win32.c (revision 7e120ff03ede3fe64e2c8620c01465d528502ddb)
198b9484cSchristos /* Utilities to execute a program in a subprocess (possibly linked by pipes
298b9484cSchristos    with other subprocesses), and wait for it.  Generic Win32 specialization.
3*7e120ff0Schristos    Copyright (C) 1996-2024 Free Software Foundation, Inc.
498b9484cSchristos 
598b9484cSchristos This file is part of the libiberty library.
698b9484cSchristos Libiberty is free software; you can redistribute it and/or
798b9484cSchristos modify it under the terms of the GNU Library General Public
898b9484cSchristos License as published by the Free Software Foundation; either
998b9484cSchristos version 2 of the License, or (at your option) any later version.
1098b9484cSchristos 
1198b9484cSchristos Libiberty is distributed in the hope that it will be useful,
1298b9484cSchristos but WITHOUT ANY WARRANTY; without even the implied warranty of
1398b9484cSchristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
1498b9484cSchristos Library General Public License for more details.
1598b9484cSchristos 
1698b9484cSchristos You should have received a copy of the GNU Library General Public
1798b9484cSchristos License along with libiberty; see the file COPYING.LIB.  If not,
1898b9484cSchristos write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
1998b9484cSchristos Boston, MA 02110-1301, USA.  */
2098b9484cSchristos 
2198b9484cSchristos #include "pex-common.h"
2298b9484cSchristos 
23*7e120ff0Schristos #define WIN32_LEAN_AND_MEAN
2498b9484cSchristos #include <windows.h>
2598b9484cSchristos 
2698b9484cSchristos #ifdef HAVE_STDLIB_H
2798b9484cSchristos #include <stdlib.h>
2898b9484cSchristos #endif
2998b9484cSchristos #ifdef HAVE_STRING_H
3098b9484cSchristos #include <string.h>
3198b9484cSchristos #endif
3298b9484cSchristos #ifdef HAVE_UNISTD_H
3398b9484cSchristos #include <unistd.h>
3498b9484cSchristos #endif
3598b9484cSchristos #ifdef HAVE_SYS_WAIT_H
3698b9484cSchristos #include <sys/wait.h>
3798b9484cSchristos #endif
3898b9484cSchristos 
3998b9484cSchristos #include <assert.h>
4098b9484cSchristos #include <process.h>
4198b9484cSchristos #include <io.h>
4298b9484cSchristos #include <fcntl.h>
4398b9484cSchristos #include <signal.h>
4498b9484cSchristos #include <sys/stat.h>
4598b9484cSchristos #include <errno.h>
4698b9484cSchristos #include <ctype.h>
4798b9484cSchristos 
4898b9484cSchristos /* mingw32 headers may not define the following.  */
4998b9484cSchristos 
5098b9484cSchristos #ifndef _P_WAIT
5198b9484cSchristos #  define _P_WAIT	0
5298b9484cSchristos #  define _P_NOWAIT	1
5398b9484cSchristos #  define _P_OVERLAY	2
5498b9484cSchristos #  define _P_NOWAITO	3
5598b9484cSchristos #  define _P_DETACH	4
5698b9484cSchristos 
5798b9484cSchristos #  define WAIT_CHILD		0
5898b9484cSchristos #  define WAIT_GRANDCHILD	1
5998b9484cSchristos #endif
6098b9484cSchristos 
6198b9484cSchristos #define MINGW_NAME "Minimalist GNU for Windows"
6298b9484cSchristos #define MINGW_NAME_LEN (sizeof(MINGW_NAME) - 1)
6398b9484cSchristos 
6498b9484cSchristos extern char *stpcpy (char *dst, const char *src);
6598b9484cSchristos 
6698b9484cSchristos /* Ensure that the executable pathname uses Win32 backslashes. This
6798b9484cSchristos    is not necessary on NT, but on W9x, forward slashes causes
6898b9484cSchristos    failure of spawn* and exec* functions (and probably any function
6998b9484cSchristos    that calls CreateProcess) *iff* the executable pathname (argv[0])
7098b9484cSchristos    is a quoted string.  And quoting is necessary in case a pathname
7198b9484cSchristos    contains embedded white space.  You can't win.  */
7298b9484cSchristos static void
7398b9484cSchristos backslashify (char *s)
7498b9484cSchristos {
7598b9484cSchristos   while ((s = strchr (s, '/')) != NULL)
7698b9484cSchristos     *s = '\\';
7798b9484cSchristos   return;
7898b9484cSchristos }
7998b9484cSchristos 
8098b9484cSchristos static int pex_win32_open_read (struct pex_obj *, const char *, int);
81837edd6bSchristos static int pex_win32_open_write (struct pex_obj *, const char *, int, int);
8298b9484cSchristos static pid_t pex_win32_exec_child (struct pex_obj *, int, const char *,
8398b9484cSchristos 				  char * const *, char * const *,
8498b9484cSchristos                                   int, int, int, int,
8598b9484cSchristos 				  const char **, int *);
8698b9484cSchristos static int pex_win32_close (struct pex_obj *, int);
8798b9484cSchristos static pid_t pex_win32_wait (struct pex_obj *, pid_t, int *,
8898b9484cSchristos 			   struct pex_time *, int, const char **, int *);
8998b9484cSchristos static int pex_win32_pipe (struct pex_obj *, int *, int);
9098b9484cSchristos static FILE *pex_win32_fdopenr (struct pex_obj *, int, int);
9198b9484cSchristos static FILE *pex_win32_fdopenw (struct pex_obj *, int, int);
9298b9484cSchristos 
9398b9484cSchristos /* The list of functions we pass to the common routines.  */
9498b9484cSchristos 
9598b9484cSchristos const struct pex_funcs funcs =
9698b9484cSchristos {
9798b9484cSchristos   pex_win32_open_read,
9898b9484cSchristos   pex_win32_open_write,
9998b9484cSchristos   pex_win32_exec_child,
10098b9484cSchristos   pex_win32_close,
10198b9484cSchristos   pex_win32_wait,
10298b9484cSchristos   pex_win32_pipe,
10398b9484cSchristos   pex_win32_fdopenr,
10498b9484cSchristos   pex_win32_fdopenw,
10598b9484cSchristos   NULL /* cleanup */
10698b9484cSchristos };
10798b9484cSchristos 
10898b9484cSchristos /* Return a newly initialized pex_obj structure.  */
10998b9484cSchristos 
11098b9484cSchristos struct pex_obj *
11198b9484cSchristos pex_init (int flags, const char *pname, const char *tempbase)
11298b9484cSchristos {
11398b9484cSchristos   return pex_init_common (flags, pname, tempbase, &funcs);
11498b9484cSchristos }
11598b9484cSchristos 
11698b9484cSchristos /* Open a file for reading.  */
11798b9484cSchristos 
11898b9484cSchristos static int
11998b9484cSchristos pex_win32_open_read (struct pex_obj *obj ATTRIBUTE_UNUSED, const char *name,
12098b9484cSchristos 		     int binary)
12198b9484cSchristos {
12298b9484cSchristos   return _open (name, _O_RDONLY | (binary ? _O_BINARY : _O_TEXT));
12398b9484cSchristos }
12498b9484cSchristos 
12598b9484cSchristos /* Open a file for writing.  */
12698b9484cSchristos 
12798b9484cSchristos static int
12898b9484cSchristos pex_win32_open_write (struct pex_obj *obj ATTRIBUTE_UNUSED, const char *name,
129837edd6bSchristos 		      int binary, int append)
13098b9484cSchristos {
13198b9484cSchristos   /* Note that we can't use O_EXCL here because gcc may have already
13298b9484cSchristos      created the temporary file via make_temp_file.  */
133837edd6bSchristos   if (append)
134837edd6bSchristos     return -1;
13598b9484cSchristos   return _open (name,
13698b9484cSchristos 		(_O_WRONLY | _O_CREAT | _O_TRUNC
13798b9484cSchristos 		 | (binary ? _O_BINARY : _O_TEXT)),
13898b9484cSchristos 		_S_IREAD | _S_IWRITE);
13998b9484cSchristos }
14098b9484cSchristos 
14198b9484cSchristos /* Close a file.  */
14298b9484cSchristos 
14398b9484cSchristos static int
14498b9484cSchristos pex_win32_close (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd)
14598b9484cSchristos {
14698b9484cSchristos   return _close (fd);
14798b9484cSchristos }
14898b9484cSchristos 
14998b9484cSchristos #ifdef USE_MINGW_MSYS
15098b9484cSchristos static const char *mingw_keys[] = {"SOFTWARE", "Microsoft", "Windows", "CurrentVersion", "Uninstall", NULL};
15198b9484cSchristos 
15298b9484cSchristos /* Tack the executable on the end of a (possibly slash terminated) buffer
15398b9484cSchristos    and convert everything to \. */
15498b9484cSchristos static const char *
15598b9484cSchristos tack_on_executable (char *buf, const char *executable)
15698b9484cSchristos {
15798b9484cSchristos   char *p = strchr (buf, '\0');
15898b9484cSchristos   if (p > buf && (p[-1] == '\\' || p[-1] == '/'))
15998b9484cSchristos     p[-1] = '\0';
16098b9484cSchristos   backslashify (strcat (buf, executable));
16198b9484cSchristos   return buf;
16298b9484cSchristos }
16398b9484cSchristos 
16498b9484cSchristos /* Walk down a registry hierarchy until the end.  Return the key. */
16598b9484cSchristos static HKEY
16698b9484cSchristos openkey (HKEY hStart, const char *keys[])
16798b9484cSchristos {
16898b9484cSchristos   HKEY hKey, hTmp;
16998b9484cSchristos   for (hKey = hStart; *keys; keys++)
17098b9484cSchristos     {
17198b9484cSchristos       LONG res;
17298b9484cSchristos       hTmp = hKey;
17398b9484cSchristos       res = RegOpenKey (hTmp, *keys, &hKey);
17498b9484cSchristos 
17598b9484cSchristos       if (hTmp != HKEY_LOCAL_MACHINE)
17698b9484cSchristos 	RegCloseKey (hTmp);
17798b9484cSchristos 
17898b9484cSchristos       if (res != ERROR_SUCCESS)
17998b9484cSchristos 	return NULL;
18098b9484cSchristos     }
18198b9484cSchristos   return hKey;
18298b9484cSchristos }
18398b9484cSchristos 
18498b9484cSchristos /* Return the "mingw root" as derived from the mingw uninstall information. */
18598b9484cSchristos static const char *
18698b9484cSchristos mingw_rootify (const char *executable)
18798b9484cSchristos {
18898b9484cSchristos   HKEY hKey, hTmp;
18998b9484cSchristos   DWORD maxlen;
19098b9484cSchristos   char *namebuf, *foundbuf;
19198b9484cSchristos   DWORD i;
19298b9484cSchristos   LONG res;
19398b9484cSchristos 
19498b9484cSchristos   /* Open the uninstall "directory". */
19598b9484cSchristos   hKey = openkey (HKEY_LOCAL_MACHINE, mingw_keys);
19698b9484cSchristos 
19798b9484cSchristos   /* Not found. */
19898b9484cSchristos   if (!hKey)
19998b9484cSchristos     return executable;
20098b9484cSchristos 
20198b9484cSchristos   /* Need to enumerate all of the keys here looking for one the most recent
20298b9484cSchristos      one for MinGW. */
20398b9484cSchristos   if (RegQueryInfoKey (hKey, NULL, NULL, NULL, NULL, &maxlen, NULL, NULL,
20498b9484cSchristos 		       NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
20598b9484cSchristos     {
20698b9484cSchristos       RegCloseKey (hKey);
20798b9484cSchristos       return executable;
20898b9484cSchristos     }
20998b9484cSchristos   namebuf = XNEWVEC (char, ++maxlen);
21098b9484cSchristos   foundbuf = XNEWVEC (char, maxlen);
21198b9484cSchristos   foundbuf[0] = '\0';
21298b9484cSchristos   if (!namebuf || !foundbuf)
21398b9484cSchristos     {
21498b9484cSchristos       RegCloseKey (hKey);
21598b9484cSchristos       free (namebuf);
21698b9484cSchristos       free (foundbuf);
21798b9484cSchristos       return executable;
21898b9484cSchristos     }
21998b9484cSchristos 
22098b9484cSchristos   /* Look through all of the keys for one that begins with Minimal GNU...
22198b9484cSchristos      Try to get the latest version by doing a string compare although that
22298b9484cSchristos      string never really works with version number sorting. */
22398b9484cSchristos   for (i = 0; RegEnumKey (hKey, i, namebuf, maxlen) == ERROR_SUCCESS; i++)
22498b9484cSchristos     {
22598b9484cSchristos       int match = strcasecmp (namebuf, MINGW_NAME);
22698b9484cSchristos       if (match < 0)
22798b9484cSchristos 	continue;
22898b9484cSchristos       if (match > 0 && strncasecmp (namebuf, MINGW_NAME, MINGW_NAME_LEN) > 0)
22998b9484cSchristos 	continue;
23098b9484cSchristos       if (strcasecmp (namebuf, foundbuf) > 0)
23198b9484cSchristos 	strcpy (foundbuf, namebuf);
23298b9484cSchristos     }
23398b9484cSchristos   free (namebuf);
23498b9484cSchristos 
23598b9484cSchristos   /* If foundbuf is empty, we didn't find anything.  Punt. */
23698b9484cSchristos   if (!foundbuf[0])
23798b9484cSchristos     {
23898b9484cSchristos       free (foundbuf);
23998b9484cSchristos       RegCloseKey (hKey);
24098b9484cSchristos       return executable;
24198b9484cSchristos     }
24298b9484cSchristos 
24398b9484cSchristos   /* Open the key that we wanted */
24498b9484cSchristos   res = RegOpenKey (hKey, foundbuf, &hTmp);
24598b9484cSchristos   RegCloseKey (hKey);
24698b9484cSchristos   free (foundbuf);
24798b9484cSchristos 
24898b9484cSchristos   /* Don't know why this would fail, but you gotta check */
24998b9484cSchristos   if (res != ERROR_SUCCESS)
25098b9484cSchristos     return executable;
25198b9484cSchristos 
25298b9484cSchristos   maxlen = 0;
25398b9484cSchristos   /* Get the length of the value pointed to by InstallLocation */
25498b9484cSchristos   if (RegQueryValueEx (hTmp, "InstallLocation", 0, NULL, NULL,
25598b9484cSchristos 		       &maxlen) != ERROR_SUCCESS || maxlen == 0)
25698b9484cSchristos     {
25798b9484cSchristos       RegCloseKey (hTmp);
25898b9484cSchristos       return executable;
25998b9484cSchristos     }
26098b9484cSchristos 
26198b9484cSchristos   /* Allocate space for the install location */
26298b9484cSchristos   foundbuf = XNEWVEC (char, maxlen + strlen (executable));
26398b9484cSchristos   if (!foundbuf)
26498b9484cSchristos     {
26598b9484cSchristos       free (foundbuf);
26698b9484cSchristos       RegCloseKey (hTmp);
26798b9484cSchristos     }
26898b9484cSchristos 
26998b9484cSchristos   /* Read the install location into the buffer */
27098b9484cSchristos   res = RegQueryValueEx (hTmp, "InstallLocation", 0, NULL, (LPBYTE) foundbuf,
27198b9484cSchristos 			 &maxlen);
27298b9484cSchristos   RegCloseKey (hTmp);
27398b9484cSchristos   if (res != ERROR_SUCCESS)
27498b9484cSchristos     {
27598b9484cSchristos       free (foundbuf);
27698b9484cSchristos       return executable;
27798b9484cSchristos     }
27898b9484cSchristos 
27998b9484cSchristos   /* Concatenate the install location and the executable, turn all slashes
28098b9484cSchristos      to backslashes, and return that. */
28198b9484cSchristos   return tack_on_executable (foundbuf, executable);
28298b9484cSchristos }
28398b9484cSchristos 
28498b9484cSchristos /* Read the install location of msys from it's installation file and
28598b9484cSchristos    rootify the executable based on that. */
28698b9484cSchristos static const char *
28798b9484cSchristos msys_rootify (const char *executable)
28898b9484cSchristos {
28998b9484cSchristos   size_t bufsize = 64;
29098b9484cSchristos   size_t execlen = strlen (executable) + 1;
29198b9484cSchristos   char *buf;
29298b9484cSchristos   DWORD res = 0;
29398b9484cSchristos   for (;;)
29498b9484cSchristos     {
29598b9484cSchristos       buf = XNEWVEC (char, bufsize + execlen);
29698b9484cSchristos       if (!buf)
29798b9484cSchristos 	break;
29898b9484cSchristos       res = GetPrivateProfileString ("InstallSettings", "InstallPath", NULL,
29998b9484cSchristos 				     buf, bufsize, "msys.ini");
30098b9484cSchristos       if (!res)
30198b9484cSchristos 	break;
30298b9484cSchristos       if (strlen (buf) < bufsize)
30398b9484cSchristos 	break;
30498b9484cSchristos       res = 0;
30598b9484cSchristos       free (buf);
30698b9484cSchristos       bufsize *= 2;
30798b9484cSchristos       if (bufsize > 65536)
30898b9484cSchristos 	{
30998b9484cSchristos 	  buf = NULL;
31098b9484cSchristos 	  break;
31198b9484cSchristos 	}
31298b9484cSchristos     }
31398b9484cSchristos 
31498b9484cSchristos   if (res)
31598b9484cSchristos     return tack_on_executable (buf, executable);
31698b9484cSchristos 
31798b9484cSchristos   /* failed */
31898b9484cSchristos   free (buf);
31998b9484cSchristos   return executable;
32098b9484cSchristos }
32198b9484cSchristos #endif
32298b9484cSchristos 
32398b9484cSchristos /* Return the number of arguments in an argv array, not including the null
32498b9484cSchristos    terminating argument. */
32598b9484cSchristos 
32698b9484cSchristos static int
32798b9484cSchristos argv_to_argc (char *const *argv)
32898b9484cSchristos {
32998b9484cSchristos   char *const *i = argv;
33098b9484cSchristos   while (*i)
33198b9484cSchristos     i++;
33298b9484cSchristos   return i - argv;
33398b9484cSchristos }
33498b9484cSchristos 
33598b9484cSchristos /* Return a Windows command-line from ARGV.  It is the caller's
33698b9484cSchristos    responsibility to free the string returned.  */
33798b9484cSchristos 
33898b9484cSchristos static char *
33998b9484cSchristos argv_to_cmdline (char *const *argv)
34098b9484cSchristos {
34198b9484cSchristos   char *cmdline;
34298b9484cSchristos   char *p;
34398b9484cSchristos   size_t cmdline_len;
34498b9484cSchristos   int i, j, k;
345837edd6bSchristos   int needs_quotes;
34698b9484cSchristos 
34798b9484cSchristos   cmdline_len = 0;
34898b9484cSchristos   for (i = 0; argv[i]; i++)
34998b9484cSchristos     {
350837edd6bSchristos       /* We only quote arguments that contain spaces, \t or " characters to
351837edd6bSchristos 	 prevent wasting 2 chars per argument of the CreateProcess 32k char
352837edd6bSchristos 	 limit.  We need only escape embedded double-quotes and immediately
35398b9484cSchristos 	 preceeding backslash characters.  A sequence of backslach characters
354*7e120ff0Schristos 	 that is not followed by a double quote character will not be
35598b9484cSchristos 	 escaped.  */
356837edd6bSchristos       needs_quotes = 0;
35798b9484cSchristos       for (j = 0; argv[i][j]; j++)
35898b9484cSchristos 	{
359837edd6bSchristos 	  if (argv[i][j] == ' ' || argv[i][j] == '\t' || argv[i][j] == '"')
360837edd6bSchristos 	    {
361837edd6bSchristos 	      needs_quotes = 1;
362837edd6bSchristos 	    }
363837edd6bSchristos 
36498b9484cSchristos 	  if (argv[i][j] == '"')
36598b9484cSchristos 	    {
36698b9484cSchristos 	      /* Escape preceeding backslashes.  */
36798b9484cSchristos 	      for (k = j - 1; k >= 0 && argv[i][k] == '\\'; k--)
36898b9484cSchristos 		cmdline_len++;
369*7e120ff0Schristos 	      /* Escape the quote character.  */
37098b9484cSchristos 	      cmdline_len++;
37198b9484cSchristos 	    }
37298b9484cSchristos 	}
373796c32c9Schristos       if (j == 0)
374796c32c9Schristos 	needs_quotes = 1;
37598b9484cSchristos       /* Trailing backslashes also need to be escaped because they will be
37698b9484cSchristos          followed by the terminating quote.  */
377837edd6bSchristos       if (needs_quotes)
378837edd6bSchristos         {
37998b9484cSchristos           for (k = j - 1; k >= 0 && argv[i][k] == '\\'; k--)
38098b9484cSchristos             cmdline_len++;
381837edd6bSchristos         }
38298b9484cSchristos       cmdline_len += j;
383837edd6bSchristos       /* for leading and trailing quotes and space */
384837edd6bSchristos       cmdline_len += needs_quotes * 2 + 1;
38598b9484cSchristos     }
38698b9484cSchristos   cmdline = XNEWVEC (char, cmdline_len);
38798b9484cSchristos   p = cmdline;
38898b9484cSchristos   for (i = 0; argv[i]; i++)
38998b9484cSchristos     {
390837edd6bSchristos       needs_quotes = 0;
391837edd6bSchristos       for (j = 0; argv[i][j]; j++)
392837edd6bSchristos         {
393837edd6bSchristos           if (argv[i][j] == ' ' || argv[i][j] == '\t' || argv[i][j] == '"')
394837edd6bSchristos             {
395837edd6bSchristos               needs_quotes = 1;
396837edd6bSchristos               break;
397837edd6bSchristos             }
398837edd6bSchristos         }
399796c32c9Schristos       if (j == 0)
400796c32c9Schristos 	needs_quotes = 1;
401837edd6bSchristos 
402837edd6bSchristos       if (needs_quotes)
403837edd6bSchristos         {
40498b9484cSchristos           *p++ = '"';
405837edd6bSchristos         }
40698b9484cSchristos       for (j = 0; argv[i][j]; j++)
40798b9484cSchristos 	{
40898b9484cSchristos 	  if (argv[i][j] == '"')
40998b9484cSchristos 	    {
41098b9484cSchristos 	      for (k = j - 1; k >= 0 && argv[i][k] == '\\'; k--)
41198b9484cSchristos 		*p++ = '\\';
41298b9484cSchristos 	      *p++ = '\\';
41398b9484cSchristos 	    }
41498b9484cSchristos 	  *p++ = argv[i][j];
41598b9484cSchristos 	}
416837edd6bSchristos       if (needs_quotes)
417837edd6bSchristos         {
41898b9484cSchristos           for (k = j - 1; k >= 0 && argv[i][k] == '\\'; k--)
41998b9484cSchristos             *p++ = '\\';
42098b9484cSchristos           *p++ = '"';
421837edd6bSchristos         }
42298b9484cSchristos       *p++ = ' ';
42398b9484cSchristos     }
42498b9484cSchristos   p[-1] = '\0';
42598b9484cSchristos   return cmdline;
42698b9484cSchristos }
42798b9484cSchristos 
42898b9484cSchristos /* We'll try the passed filename with all the known standard
42998b9484cSchristos    extensions, and then without extension.  We try no extension
43098b9484cSchristos    last so that we don't try to run some random extension-less
43198b9484cSchristos    file that might be hanging around.  We try both extension
43298b9484cSchristos    and no extension so that we don't need any fancy logic
43398b9484cSchristos    to determine if a file has extension.  */
43498b9484cSchristos static const char *const
43598b9484cSchristos std_suffixes[] = {
43698b9484cSchristos   ".com",
43798b9484cSchristos   ".exe",
43898b9484cSchristos   ".bat",
43998b9484cSchristos   ".cmd",
44098b9484cSchristos   "",
44198b9484cSchristos   0
44298b9484cSchristos };
44398b9484cSchristos 
44498b9484cSchristos /* Returns the full path to PROGRAM.  If SEARCH is true, look for
44598b9484cSchristos    PROGRAM in each directory in PATH.  */
44698b9484cSchristos 
44798b9484cSchristos static char *
44898b9484cSchristos find_executable (const char *program, BOOL search)
44998b9484cSchristos {
45098b9484cSchristos   char *full_executable;
45198b9484cSchristos   char *e;
45298b9484cSchristos   size_t fe_len;
45398b9484cSchristos   const char *path = 0;
45498b9484cSchristos   const char *const *ext;
45598b9484cSchristos   const char *p, *q;
45698b9484cSchristos   size_t proglen = strlen (program);
45798b9484cSchristos   int has_slash = (strchr (program, '/') || strchr (program, '\\'));
45898b9484cSchristos   HANDLE h;
45998b9484cSchristos 
46098b9484cSchristos   if (has_slash)
46198b9484cSchristos     search = FALSE;
46298b9484cSchristos 
46398b9484cSchristos   if (search)
46498b9484cSchristos     path = getenv ("PATH");
46598b9484cSchristos   if (!path)
46698b9484cSchristos     path = "";
46798b9484cSchristos 
46898b9484cSchristos   fe_len = 0;
46998b9484cSchristos   for (p = path; *p; p = q)
47098b9484cSchristos     {
47198b9484cSchristos       q = p;
47298b9484cSchristos       while (*q != ';' && *q != '\0')
47398b9484cSchristos 	q++;
47498b9484cSchristos       if ((size_t)(q - p) > fe_len)
47598b9484cSchristos 	fe_len = q - p;
47698b9484cSchristos       if (*q == ';')
47798b9484cSchristos 	q++;
47898b9484cSchristos     }
47998b9484cSchristos   fe_len = fe_len + 1 + proglen + 5 /* space for extension */;
48098b9484cSchristos   full_executable = XNEWVEC (char, fe_len);
48198b9484cSchristos 
48298b9484cSchristos   p = path;
48398b9484cSchristos   do
48498b9484cSchristos     {
48598b9484cSchristos       q = p;
48698b9484cSchristos       while (*q != ';' && *q != '\0')
48798b9484cSchristos 	q++;
48898b9484cSchristos 
48998b9484cSchristos       e = full_executable;
49098b9484cSchristos       memcpy (e, p, q - p);
49198b9484cSchristos       e += (q - p);
49298b9484cSchristos       if (q - p)
49398b9484cSchristos 	*e++ = '\\';
49498b9484cSchristos       strcpy (e, program);
49598b9484cSchristos 
49698b9484cSchristos       if (*q == ';')
49798b9484cSchristos 	q++;
49898b9484cSchristos 
49998b9484cSchristos       for (e = full_executable; *e; e++)
50098b9484cSchristos 	if (*e == '/')
50198b9484cSchristos 	  *e = '\\';
50298b9484cSchristos 
50398b9484cSchristos       /* At this point, e points to the terminating NUL character for
50498b9484cSchristos          full_executable.  */
50598b9484cSchristos       for (ext = std_suffixes; *ext; ext++)
50698b9484cSchristos 	{
50798b9484cSchristos 	  /* Remove any current extension.  */
50898b9484cSchristos 	  *e = '\0';
50998b9484cSchristos 	  /* Add the new one.  */
51098b9484cSchristos 	  strcat (full_executable, *ext);
51198b9484cSchristos 
51298b9484cSchristos 	  /* Attempt to open this file.  */
51398b9484cSchristos 	  h = CreateFile (full_executable, GENERIC_READ,
51498b9484cSchristos 			  FILE_SHARE_READ | FILE_SHARE_WRITE,
51598b9484cSchristos 			  0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
51698b9484cSchristos 	  if (h != INVALID_HANDLE_VALUE)
51798b9484cSchristos 	    goto found;
51898b9484cSchristos 	}
51998b9484cSchristos       p = q;
52098b9484cSchristos     }
52198b9484cSchristos   while (*p);
52298b9484cSchristos   free (full_executable);
52398b9484cSchristos   return 0;
52498b9484cSchristos 
52598b9484cSchristos  found:
52698b9484cSchristos   CloseHandle (h);
52798b9484cSchristos   return full_executable;
52898b9484cSchristos }
52998b9484cSchristos 
53098b9484cSchristos /* Low-level process creation function and helper.  */
53198b9484cSchristos 
53298b9484cSchristos static int
53398b9484cSchristos env_compare (const void *a_ptr, const void *b_ptr)
53498b9484cSchristos {
53598b9484cSchristos   const char *a;
53698b9484cSchristos   const char *b;
53798b9484cSchristos   unsigned char c1;
53898b9484cSchristos   unsigned char c2;
53998b9484cSchristos 
54098b9484cSchristos   a = *(const char **) a_ptr;
54198b9484cSchristos   b = *(const char **) b_ptr;
54298b9484cSchristos 
54398b9484cSchristos   /* a and b will be of the form: VAR=VALUE
54498b9484cSchristos      We compare only the variable name part here using a case-insensitive
54598b9484cSchristos      comparison algorithm.  It might appear that in fact strcasecmp () can
54698b9484cSchristos      take the place of this whole function, and indeed it could, save for
54798b9484cSchristos      the fact that it would fail in cases such as comparing A1=foo and
54898b9484cSchristos      A=bar (because 1 is less than = in the ASCII character set).
54998b9484cSchristos      (Environment variables containing no numbers would work in such a
55098b9484cSchristos      scenario.)  */
55198b9484cSchristos 
55298b9484cSchristos   do
55398b9484cSchristos     {
55498b9484cSchristos       c1 = (unsigned char) tolower (*a++);
55598b9484cSchristos       c2 = (unsigned char) tolower (*b++);
55698b9484cSchristos 
55798b9484cSchristos       if (c1 == '=')
55898b9484cSchristos         c1 = '\0';
55998b9484cSchristos 
56098b9484cSchristos       if (c2 == '=')
56198b9484cSchristos         c2 = '\0';
56298b9484cSchristos     }
56398b9484cSchristos   while (c1 == c2 && c1 != '\0');
56498b9484cSchristos 
56598b9484cSchristos   return c1 - c2;
56698b9484cSchristos }
56798b9484cSchristos 
56898b9484cSchristos /* Execute a Windows executable as a child process.  This will fail if the
56998b9484cSchristos  * target is not actually an executable, such as if it is a shell script. */
57098b9484cSchristos 
57198b9484cSchristos static pid_t
572*7e120ff0Schristos win32_spawn (struct pex_obj *obj,
573*7e120ff0Schristos          const char *executable,
57498b9484cSchristos 	     BOOL search,
57598b9484cSchristos 	     char *const *argv,
57698b9484cSchristos              char *const *env, /* array of strings of the form: VAR=VALUE */
57798b9484cSchristos 	     DWORD dwCreationFlags,
57898b9484cSchristos 	     LPSTARTUPINFO si,
57998b9484cSchristos 	     LPPROCESS_INFORMATION pi)
58098b9484cSchristos {
581*7e120ff0Schristos   char *full_executable = NULL;
582*7e120ff0Schristos   char *cmdline = NULL;
583*7e120ff0Schristos   pid_t pid = (pid_t) -1;
58498b9484cSchristos   char **env_copy;
58598b9484cSchristos   char *env_block = NULL;
58698b9484cSchristos 
58798b9484cSchristos   if (env)
58898b9484cSchristos     {
58998b9484cSchristos       int env_size;
59098b9484cSchristos 
59198b9484cSchristos       /* Count the number of environment bindings supplied.  */
59298b9484cSchristos       for (env_size = 0; env[env_size]; env_size++)
59398b9484cSchristos         continue;
59498b9484cSchristos 
59598b9484cSchristos       /* Assemble an environment block, if required.  This consists of
59698b9484cSchristos          VAR=VALUE strings juxtaposed (with one null character between each
59798b9484cSchristos          pair) and an additional null at the end.  */
59898b9484cSchristos       if (env_size > 0)
59998b9484cSchristos         {
60098b9484cSchristos           int var;
60198b9484cSchristos           int total_size = 1; /* 1 is for the final null.  */
60298b9484cSchristos           char *bufptr;
60398b9484cSchristos 
60498b9484cSchristos           /* Windows needs the members of the block to be sorted by variable
60598b9484cSchristos              name.  */
60698b9484cSchristos           env_copy = (char **) alloca (sizeof (char *) * env_size);
60798b9484cSchristos           memcpy (env_copy, env, sizeof (char *) * env_size);
60898b9484cSchristos           qsort (env_copy, env_size, sizeof (char *), env_compare);
60998b9484cSchristos 
61098b9484cSchristos           for (var = 0; var < env_size; var++)
61198b9484cSchristos             total_size += strlen (env[var]) + 1;
61298b9484cSchristos 
61398b9484cSchristos           env_block = XNEWVEC (char, total_size);
61498b9484cSchristos           bufptr = env_block;
61598b9484cSchristos           for (var = 0; var < env_size; var++)
61698b9484cSchristos             bufptr = stpcpy (bufptr, env_copy[var]) + 1;
61798b9484cSchristos 
61898b9484cSchristos           *bufptr = '\0';
61998b9484cSchristos         }
62098b9484cSchristos     }
62198b9484cSchristos 
62298b9484cSchristos   full_executable = find_executable (executable, search);
62398b9484cSchristos   if (!full_executable)
624*7e120ff0Schristos     goto exit;
62598b9484cSchristos   cmdline = argv_to_cmdline (argv);
62698b9484cSchristos   if (!cmdline)
627*7e120ff0Schristos     goto exit;
628*7e120ff0Schristos   /* If cmdline is too large, CreateProcess will fail with a bad
629*7e120ff0Schristos      'No such file or directory' error. Try passing it through a
630*7e120ff0Schristos      temporary response file instead.  */
631*7e120ff0Schristos   if (strlen (cmdline) > 32767)
632*7e120ff0Schristos     {
633*7e120ff0Schristos       char *response_file = make_temp_file ("");
634*7e120ff0Schristos       /* Register the file for deletion by pex_free.  */
635*7e120ff0Schristos       ++obj->remove_count;
636*7e120ff0Schristos       obj->remove = XRESIZEVEC (char *, obj->remove, obj->remove_count);
637*7e120ff0Schristos       obj->remove[obj->remove_count - 1] = response_file;
638*7e120ff0Schristos       int fd = pex_win32_open_write (obj, response_file, 0, 0);
639*7e120ff0Schristos       if (fd == -1)
640*7e120ff0Schristos         goto exit;
641*7e120ff0Schristos       FILE *f = pex_win32_fdopenw (obj, fd, 0);
642*7e120ff0Schristos       /* Don't write argv[0] (program name) to the response file.  */
643*7e120ff0Schristos       if (writeargv (&argv[1], f))
644*7e120ff0Schristos         {
645*7e120ff0Schristos           fclose (f);
646*7e120ff0Schristos           goto exit;
647*7e120ff0Schristos         }
648*7e120ff0Schristos       fclose (f); /* Also closes fd and the underlying OS handle.  */
649*7e120ff0Schristos       char *response_arg = concat ("@", response_file, NULL);
650*7e120ff0Schristos       char *response_argv[3] = {argv[0], response_arg, NULL};
651*7e120ff0Schristos       free (cmdline);
652*7e120ff0Schristos       cmdline = argv_to_cmdline (response_argv);
653*7e120ff0Schristos       free (response_arg);
654*7e120ff0Schristos       if (!cmdline)
655*7e120ff0Schristos         goto exit;
656*7e120ff0Schristos     }
65798b9484cSchristos 
65898b9484cSchristos   /* Create the child process.  */
659*7e120ff0Schristos   if (CreateProcess (full_executable, cmdline,
66098b9484cSchristos 		      /*lpProcessAttributes=*/NULL,
66198b9484cSchristos 		      /*lpThreadAttributes=*/NULL,
66298b9484cSchristos 		      /*bInheritHandles=*/TRUE,
66398b9484cSchristos 		      dwCreationFlags,
66498b9484cSchristos 		      (LPVOID) env_block,
66598b9484cSchristos 		      /*lpCurrentDirectory=*/NULL,
66698b9484cSchristos 		      si,
66798b9484cSchristos 		      pi))
66898b9484cSchristos     {
669*7e120ff0Schristos       CloseHandle (pi->hThread);
670*7e120ff0Schristos       pid = (pid_t) pi->hProcess;
67198b9484cSchristos     }
67298b9484cSchristos 
673*7e120ff0Schristos  exit:
67498b9484cSchristos   /* Clean up.  */
67598b9484cSchristos   free (env_block);
67698b9484cSchristos   free (cmdline);
67798b9484cSchristos   free (full_executable);
67898b9484cSchristos 
679*7e120ff0Schristos   return pid;
68098b9484cSchristos }
68198b9484cSchristos 
68298b9484cSchristos /* Spawn a script.  This simulates the Unix script execution mechanism.
68398b9484cSchristos    This function is called as a fallback if win32_spawn fails. */
68498b9484cSchristos 
68598b9484cSchristos static pid_t
686*7e120ff0Schristos spawn_script (struct pex_obj *obj,
687*7e120ff0Schristos               const char *executable, char *const *argv,
68898b9484cSchristos               char* const *env,
68998b9484cSchristos 	      DWORD dwCreationFlags,
69098b9484cSchristos 	      LPSTARTUPINFO si,
69198b9484cSchristos 	      LPPROCESS_INFORMATION pi)
69298b9484cSchristos {
69398b9484cSchristos   pid_t pid = (pid_t) -1;
69498b9484cSchristos   int save_errno = errno;
69598b9484cSchristos   int fd = _open (executable, _O_RDONLY);
69698b9484cSchristos 
69798b9484cSchristos   /* Try to open script, check header format, extract interpreter path,
69898b9484cSchristos      and spawn script using that interpretter. */
69998b9484cSchristos   if (fd >= 0)
70098b9484cSchristos     {
70198b9484cSchristos       char buf[MAX_PATH + 5];
70298b9484cSchristos       int len = _read (fd, buf, sizeof (buf) - 1);
70398b9484cSchristos       _close (fd);
70498b9484cSchristos       if (len > 3)
70598b9484cSchristos 	{
70698b9484cSchristos 	  char *eol;
70798b9484cSchristos 	  buf[len] = '\0';
70898b9484cSchristos 	  eol = strchr (buf, '\n');
70998b9484cSchristos 	  if (eol && strncmp (buf, "#!", 2) == 0)
71098b9484cSchristos 	    {
71198b9484cSchristos 
71298b9484cSchristos 	      /* Header format is OK. */
71398b9484cSchristos 	      char *executable1;
71498b9484cSchristos               int new_argc;
71598b9484cSchristos               const char **avhere;
71698b9484cSchristos 
71798b9484cSchristos 	      /* Extract interpreter path. */
71898b9484cSchristos 	      do
71998b9484cSchristos 		*eol = '\0';
72098b9484cSchristos 	      while (*--eol == '\r' || *eol == ' ' || *eol == '\t');
72198b9484cSchristos 	      for (executable1 = buf + 2; *executable1 == ' ' || *executable1 == '\t'; executable1++)
72298b9484cSchristos 		continue;
72398b9484cSchristos 	      backslashify (executable1);
72498b9484cSchristos 
72598b9484cSchristos 	      /* Duplicate argv, prepending the interpreter path. */
72698b9484cSchristos 	      new_argc = argv_to_argc (argv) + 1;
72798b9484cSchristos 	      avhere = XNEWVEC (const char *, new_argc + 1);
72898b9484cSchristos 	      *avhere = executable1;
72998b9484cSchristos 	      memcpy (avhere + 1, argv, new_argc * sizeof(*argv));
73098b9484cSchristos 	      argv = (char *const *)avhere;
73198b9484cSchristos 
73298b9484cSchristos 	      /* Spawn the child. */
73398b9484cSchristos #ifndef USE_MINGW_MSYS
73498b9484cSchristos 	      executable = strrchr (executable1, '\\') + 1;
73598b9484cSchristos 	      if (!executable)
73698b9484cSchristos 		executable = executable1;
737*7e120ff0Schristos 	      pid = win32_spawn (obj, executable, TRUE, argv, env,
73898b9484cSchristos 				 dwCreationFlags, si, pi);
73998b9484cSchristos #else
74098b9484cSchristos 	      if (strchr (executable1, '\\') == NULL)
741*7e120ff0Schristos 		pid = win32_spawn (obj, executable1, TRUE, argv, env,
74298b9484cSchristos 				   dwCreationFlags, si, pi);
74398b9484cSchristos 	      else if (executable1[0] != '\\')
744*7e120ff0Schristos 		pid = win32_spawn (obj, executable1, FALSE, argv, env,
74598b9484cSchristos 				   dwCreationFlags, si, pi);
74698b9484cSchristos 	      else
74798b9484cSchristos 		{
74898b9484cSchristos 		  const char *newex = mingw_rootify (executable1);
74998b9484cSchristos 		  *avhere = newex;
750*7e120ff0Schristos 		  pid = win32_spawn (obj, newex, FALSE, argv, env,
75198b9484cSchristos 				     dwCreationFlags, si, pi);
75298b9484cSchristos 		  if (executable1 != newex)
75398b9484cSchristos 		    free ((char *) newex);
75498b9484cSchristos 		  if (pid == (pid_t) -1)
75598b9484cSchristos 		    {
75698b9484cSchristos 		      newex = msys_rootify (executable1);
75798b9484cSchristos 		      if (newex != executable1)
75898b9484cSchristos 			{
75998b9484cSchristos 			  *avhere = newex;
760*7e120ff0Schristos 			  pid = win32_spawn (obj, newex, FALSE, argv, env,
76198b9484cSchristos 					     dwCreationFlags, si, pi);
76298b9484cSchristos 			  free ((char *) newex);
76398b9484cSchristos 			}
76498b9484cSchristos 		    }
76598b9484cSchristos 		}
76698b9484cSchristos #endif
76798b9484cSchristos 	      free (avhere);
76898b9484cSchristos 	    }
76998b9484cSchristos 	}
77098b9484cSchristos     }
77198b9484cSchristos   if (pid == (pid_t) -1)
77298b9484cSchristos     errno = save_errno;
77398b9484cSchristos   return pid;
77498b9484cSchristos }
77598b9484cSchristos 
77698b9484cSchristos /* Execute a child.  */
77798b9484cSchristos 
77898b9484cSchristos static pid_t
779*7e120ff0Schristos pex_win32_exec_child (struct pex_obj *obj, int flags,
78098b9484cSchristos 		      const char *executable, char * const * argv,
78198b9484cSchristos                       char* const* env,
78298b9484cSchristos 		      int in, int out, int errdes,
78398b9484cSchristos 		      int toclose ATTRIBUTE_UNUSED,
78498b9484cSchristos 		      const char **errmsg,
78598b9484cSchristos 		      int *err)
78698b9484cSchristos {
78798b9484cSchristos   pid_t pid;
78898b9484cSchristos   HANDLE stdin_handle;
78998b9484cSchristos   HANDLE stdout_handle;
79098b9484cSchristos   HANDLE stderr_handle;
79198b9484cSchristos   DWORD dwCreationFlags;
79298b9484cSchristos   OSVERSIONINFO version_info;
79398b9484cSchristos   STARTUPINFO si;
79498b9484cSchristos   PROCESS_INFORMATION pi;
7954b169a6bSchristos   int orig_out, orig_in, orig_err = 0;
79698b9484cSchristos   BOOL separate_stderr = !(flags & PEX_STDERR_TO_STDOUT);
79798b9484cSchristos 
798a2e2270fSchristos   /* Ensure we have inheritable descriptors to pass to the child.  */
79998b9484cSchristos   orig_in = in;
80098b9484cSchristos   in = _dup (orig_in);
80198b9484cSchristos 
80298b9484cSchristos   orig_out = out;
80398b9484cSchristos   out = _dup (orig_out);
80498b9484cSchristos 
80598b9484cSchristos   if (separate_stderr)
80698b9484cSchristos     {
80798b9484cSchristos       orig_err = errdes;
80898b9484cSchristos       errdes = _dup (orig_err);
80998b9484cSchristos     }
81098b9484cSchristos 
81198b9484cSchristos   stdin_handle = INVALID_HANDLE_VALUE;
81298b9484cSchristos   stdout_handle = INVALID_HANDLE_VALUE;
81398b9484cSchristos   stderr_handle = INVALID_HANDLE_VALUE;
81498b9484cSchristos 
81598b9484cSchristos   stdin_handle = (HANDLE) _get_osfhandle (in);
81698b9484cSchristos   stdout_handle = (HANDLE) _get_osfhandle (out);
81798b9484cSchristos   if (separate_stderr)
81898b9484cSchristos     stderr_handle = (HANDLE) _get_osfhandle (errdes);
81998b9484cSchristos   else
82098b9484cSchristos     stderr_handle = stdout_handle;
82198b9484cSchristos 
82298b9484cSchristos   /* Determine the version of Windows we are running on.  */
82398b9484cSchristos   version_info.dwOSVersionInfoSize = sizeof (version_info);
82498b9484cSchristos   GetVersionEx (&version_info);
82598b9484cSchristos   if (version_info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
82698b9484cSchristos     /* On Windows 95/98/ME the CREATE_NO_WINDOW flag is not
82798b9484cSchristos        supported, so we cannot avoid creating a console window.  */
82898b9484cSchristos     dwCreationFlags = 0;
82998b9484cSchristos   else
83098b9484cSchristos     {
83198b9484cSchristos       HANDLE conout_handle;
83298b9484cSchristos 
83398b9484cSchristos       /* Determine whether or not we have an associated console.  */
83498b9484cSchristos       conout_handle = CreateFile("CONOUT$",
83598b9484cSchristos 				 GENERIC_WRITE,
83698b9484cSchristos 				 FILE_SHARE_WRITE,
83798b9484cSchristos 				 /*lpSecurityAttributes=*/NULL,
83898b9484cSchristos 				 OPEN_EXISTING,
83998b9484cSchristos 				 FILE_ATTRIBUTE_NORMAL,
84098b9484cSchristos 				 /*hTemplateFile=*/NULL);
84198b9484cSchristos       if (conout_handle == INVALID_HANDLE_VALUE)
84298b9484cSchristos 	/* There is no console associated with this process.  Since
84398b9484cSchristos 	   the child is a console process, the OS would normally
84498b9484cSchristos 	   create a new console Window for the child.  Since we'll be
84598b9484cSchristos 	   redirecting the child's standard streams, we do not need
84698b9484cSchristos 	   the console window.  */
84798b9484cSchristos 	dwCreationFlags = CREATE_NO_WINDOW;
84898b9484cSchristos       else
84998b9484cSchristos 	{
85098b9484cSchristos 	  /* There is a console associated with the process, so the OS
85198b9484cSchristos 	     will not create a new console.  And, if we use
85298b9484cSchristos 	     CREATE_NO_WINDOW in this situation, the child will have
85398b9484cSchristos 	     no associated console.  Therefore, if the child's
85498b9484cSchristos 	     standard streams are connected to the console, the output
85598b9484cSchristos 	     will be discarded.  */
85698b9484cSchristos 	  CloseHandle(conout_handle);
85798b9484cSchristos 	  dwCreationFlags = 0;
85898b9484cSchristos 	}
85998b9484cSchristos     }
86098b9484cSchristos 
86198b9484cSchristos   /* Since the child will be a console process, it will, by default,
86298b9484cSchristos      connect standard input/output to its console.  However, we want
86398b9484cSchristos      the child to use the handles specifically designated above.  In
86498b9484cSchristos      addition, if there is no console (such as when we are running in
86598b9484cSchristos      a Cygwin X window), then we must redirect the child's
86698b9484cSchristos      input/output, as there is no console for the child to use.  */
86798b9484cSchristos   memset (&si, 0, sizeof (si));
86898b9484cSchristos   si.cb = sizeof (si);
86998b9484cSchristos   si.dwFlags = STARTF_USESTDHANDLES;
87098b9484cSchristos   si.hStdInput = stdin_handle;
87198b9484cSchristos   si.hStdOutput = stdout_handle;
87298b9484cSchristos   si.hStdError = stderr_handle;
87398b9484cSchristos 
87498b9484cSchristos   /* Create the child process.  */
875*7e120ff0Schristos   pid = win32_spawn (obj, executable, (flags & PEX_SEARCH) != 0,
87698b9484cSchristos 		     argv, env, dwCreationFlags, &si, &pi);
87798b9484cSchristos   if (pid == (pid_t) -1)
878*7e120ff0Schristos     pid = spawn_script (obj, executable, argv, env, dwCreationFlags,
87998b9484cSchristos                         &si, &pi);
88098b9484cSchristos   if (pid == (pid_t) -1)
88198b9484cSchristos     {
88298b9484cSchristos       *err = ENOENT;
88398b9484cSchristos       *errmsg = "CreateProcess";
88498b9484cSchristos     }
88598b9484cSchristos 
886a2e2270fSchristos   /* If the child was created successfully, close the original file
887a2e2270fSchristos      descriptors.  If the process creation fails, these are closed by
888a2e2270fSchristos      pex_run_in_environment instead.  We must not close them twice as
889a2e2270fSchristos      that seems to cause a Windows exception.  */
890a2e2270fSchristos 
891a2e2270fSchristos   if (pid != (pid_t) -1)
892a2e2270fSchristos     {
893a2e2270fSchristos       if (orig_in != STDIN_FILENO)
894a2e2270fSchristos 	_close (orig_in);
895a2e2270fSchristos       if (orig_out != STDOUT_FILENO)
896a2e2270fSchristos 	_close (orig_out);
897a2e2270fSchristos       if (separate_stderr
898a2e2270fSchristos 	  && orig_err != STDERR_FILENO)
899a2e2270fSchristos 	_close (orig_err);
900a2e2270fSchristos     }
901a2e2270fSchristos 
90298b9484cSchristos   /* Close the standard input, standard output and standard error handles
90398b9484cSchristos      in the parent.  */
90498b9484cSchristos 
90598b9484cSchristos   _close (in);
90698b9484cSchristos   _close (out);
90798b9484cSchristos   if (separate_stderr)
90898b9484cSchristos     _close (errdes);
90998b9484cSchristos 
91098b9484cSchristos   return pid;
91198b9484cSchristos }
91298b9484cSchristos 
91398b9484cSchristos /* Wait for a child process to complete.  MS CRTDLL doesn't return
91498b9484cSchristos    enough information in status to decide if the child exited due to a
91598b9484cSchristos    signal or not, rather it simply returns an integer with the exit
91698b9484cSchristos    code of the child; eg., if the child exited with an abort() call
91798b9484cSchristos    and didn't have a handler for SIGABRT, it simply returns with
91898b9484cSchristos    status == 3.  We fix the status code to conform to the usual WIF*
91998b9484cSchristos    macros.  Note that WIFSIGNALED will never be true under CRTDLL. */
92098b9484cSchristos 
92198b9484cSchristos static pid_t
92298b9484cSchristos pex_win32_wait (struct pex_obj *obj ATTRIBUTE_UNUSED, pid_t pid,
92398b9484cSchristos 		int *status, struct pex_time *time, int done ATTRIBUTE_UNUSED,
92498b9484cSchristos 		const char **errmsg, int *err)
92598b9484cSchristos {
92698b9484cSchristos   DWORD termstat;
92798b9484cSchristos   HANDLE h;
92898b9484cSchristos 
92998b9484cSchristos   if (time != NULL)
93098b9484cSchristos     memset (time, 0, sizeof *time);
93198b9484cSchristos 
93298b9484cSchristos   h = (HANDLE) pid;
93398b9484cSchristos 
93498b9484cSchristos   /* FIXME: If done is non-zero, we should probably try to kill the
93598b9484cSchristos      process.  */
93698b9484cSchristos   if (WaitForSingleObject (h, INFINITE) != WAIT_OBJECT_0)
93798b9484cSchristos     {
93898b9484cSchristos       CloseHandle (h);
93998b9484cSchristos       *err = ECHILD;
94098b9484cSchristos       *errmsg = "WaitForSingleObject";
94198b9484cSchristos       return -1;
94298b9484cSchristos     }
94398b9484cSchristos 
94498b9484cSchristos   GetExitCodeProcess (h, &termstat);
94598b9484cSchristos   CloseHandle (h);
94698b9484cSchristos 
94798b9484cSchristos   /* A value of 3 indicates that the child caught a signal, but not
94898b9484cSchristos      which one.  Since only SIGABRT, SIGFPE and SIGINT do anything, we
94998b9484cSchristos      report SIGABRT.  */
95098b9484cSchristos   if (termstat == 3)
95198b9484cSchristos     *status = SIGABRT;
95298b9484cSchristos   else
95398b9484cSchristos     *status = (termstat & 0xff) << 8;
95498b9484cSchristos 
95598b9484cSchristos   return 0;
95698b9484cSchristos }
95798b9484cSchristos 
95898b9484cSchristos /* Create a pipe.  */
95998b9484cSchristos 
96098b9484cSchristos static int
96198b9484cSchristos pex_win32_pipe (struct pex_obj *obj ATTRIBUTE_UNUSED, int *p,
96298b9484cSchristos 		int binary)
96398b9484cSchristos {
96498b9484cSchristos   return _pipe (p, 256, (binary ? _O_BINARY : _O_TEXT) | _O_NOINHERIT);
96598b9484cSchristos }
96698b9484cSchristos 
96798b9484cSchristos /* Get a FILE pointer to read from a file descriptor.  */
96898b9484cSchristos 
96998b9484cSchristos static FILE *
97098b9484cSchristos pex_win32_fdopenr (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd,
97198b9484cSchristos 		   int binary)
97298b9484cSchristos {
97398b9484cSchristos   HANDLE h = (HANDLE) _get_osfhandle (fd);
97498b9484cSchristos   if (h == INVALID_HANDLE_VALUE)
97598b9484cSchristos     return NULL;
97698b9484cSchristos   if (! SetHandleInformation (h, HANDLE_FLAG_INHERIT, 0))
97798b9484cSchristos     return NULL;
97898b9484cSchristos   return fdopen (fd, binary ? "rb" : "r");
97998b9484cSchristos }
98098b9484cSchristos 
98198b9484cSchristos static FILE *
98298b9484cSchristos pex_win32_fdopenw (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd,
98398b9484cSchristos 		   int binary)
98498b9484cSchristos {
98598b9484cSchristos   HANDLE h = (HANDLE) _get_osfhandle (fd);
98698b9484cSchristos   if (h == INVALID_HANDLE_VALUE)
98798b9484cSchristos     return NULL;
98898b9484cSchristos   if (! SetHandleInformation (h, HANDLE_FLAG_INHERIT, 0))
98998b9484cSchristos     return NULL;
99098b9484cSchristos   return fdopen (fd, binary ? "wb" : "w");
99198b9484cSchristos }
99298b9484cSchristos 
99398b9484cSchristos #ifdef MAIN
99498b9484cSchristos #include <stdio.h>
99598b9484cSchristos 
99698b9484cSchristos int
99798b9484cSchristos main (int argc ATTRIBUTE_UNUSED, char **argv)
99898b9484cSchristos {
99998b9484cSchristos   char const *errmsg;
100098b9484cSchristos   int err;
100198b9484cSchristos   argv++;
100298b9484cSchristos   printf ("%ld\n", (long) pex_win32_exec_child (NULL, PEX_SEARCH, argv[0], argv, NULL, 0, 0, 1, 2, &errmsg, &err));
100398b9484cSchristos   exit (0);
100498b9484cSchristos }
100598b9484cSchristos #endif
1006