pex-win32.c revision 302408
1/* Utilities to execute a program in a subprocess (possibly linked by pipes
2   with other subprocesses), and wait for it.  Generic Win32 specialization.
3   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005
4   Free Software Foundation, Inc.
5
6This file is part of the libiberty library.
7Libiberty is free software; you can redistribute it and/or
8modify it under the terms of the GNU Library General Public
9License as published by the Free Software Foundation; either
10version 2 of the License, or (at your option) any later version.
11
12Libiberty is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15Library General Public License for more details.
16
17You should have received a copy of the GNU Library General Public
18License along with libiberty; see the file COPYING.LIB.  If not,
19write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
20Boston, MA 02110-1301, USA.  */
21
22#include "pex-common.h"
23
24#include <windows.h>
25
26#ifdef HAVE_STDLIB_H
27#include <stdlib.h>
28#endif
29#ifdef HAVE_STRING_H
30#include <string.h>
31#endif
32#ifdef HAVE_UNISTD_H
33#include <unistd.h>
34#endif
35#ifdef HAVE_SYS_WAIT_H
36#include <sys/wait.h>
37#endif
38
39#include <assert.h>
40#include <process.h>
41#include <io.h>
42#include <fcntl.h>
43#include <signal.h>
44#include <sys/stat.h>
45#include <errno.h>
46#include <ctype.h>
47
48/* mingw32 headers may not define the following.  */
49
50#ifndef _P_WAIT
51#  define _P_WAIT	0
52#  define _P_NOWAIT	1
53#  define _P_OVERLAY	2
54#  define _P_NOWAITO	3
55#  define _P_DETACH	4
56
57#  define WAIT_CHILD		0
58#  define WAIT_GRANDCHILD	1
59#endif
60
61#define MINGW_NAME "Minimalist GNU for Windows"
62#define MINGW_NAME_LEN (sizeof(MINGW_NAME) - 1)
63
64extern char *stpcpy (char *dst, const char *src);
65
66/* Ensure that the executable pathname uses Win32 backslashes. This
67   is not necessary on NT, but on W9x, forward slashes causes
68   failure of spawn* and exec* functions (and probably any function
69   that calls CreateProcess) *iff* the executable pathname (argv[0])
70   is a quoted string.  And quoting is necessary in case a pathname
71   contains embedded white space.  You can't win.  */
72static void
73backslashify (char *s)
74{
75  while ((s = strchr (s, '/')) != NULL)
76    *s = '\\';
77  return;
78}
79
80static int pex_win32_open_read (struct pex_obj *, const char *, int);
81static int pex_win32_open_write (struct pex_obj *, const char *, int);
82static long pex_win32_exec_child (struct pex_obj *, int, const char *,
83				  char * const *, char * const *,
84                                  int, int, int, int,
85				  const char **, int *);
86static int pex_win32_close (struct pex_obj *, int);
87static int pex_win32_wait (struct pex_obj *, long, int *,
88			   struct pex_time *, int, const char **, int *);
89static int pex_win32_pipe (struct pex_obj *, int *, int);
90static FILE *pex_win32_fdopenr (struct pex_obj *, int, int);
91static FILE *pex_win32_fdopenw (struct pex_obj *, int, int);
92
93/* The list of functions we pass to the common routines.  */
94
95const struct pex_funcs funcs =
96{
97  pex_win32_open_read,
98  pex_win32_open_write,
99  pex_win32_exec_child,
100  pex_win32_close,
101  pex_win32_wait,
102  pex_win32_pipe,
103  pex_win32_fdopenr,
104  pex_win32_fdopenw,
105  NULL /* cleanup */
106};
107
108/* Return a newly initialized pex_obj structure.  */
109
110struct pex_obj *
111pex_init (int flags, const char *pname, const char *tempbase)
112{
113  return pex_init_common (flags, pname, tempbase, &funcs);
114}
115
116/* Open a file for reading.  */
117
118static int
119pex_win32_open_read (struct pex_obj *obj ATTRIBUTE_UNUSED, const char *name,
120		     int binary)
121{
122  return _open (name, _O_RDONLY | (binary ? _O_BINARY : _O_TEXT));
123}
124
125/* Open a file for writing.  */
126
127static int
128pex_win32_open_write (struct pex_obj *obj ATTRIBUTE_UNUSED, const char *name,
129		      int binary)
130{
131  /* Note that we can't use O_EXCL here because gcc may have already
132     created the temporary file via make_temp_file.  */
133  return _open (name,
134		(_O_WRONLY | _O_CREAT | _O_TRUNC
135		 | (binary ? _O_BINARY : _O_TEXT)),
136		_S_IREAD | _S_IWRITE);
137}
138
139/* Close a file.  */
140
141static int
142pex_win32_close (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd)
143{
144  return _close (fd);
145}
146
147#ifdef USE_MINGW_MSYS
148static const char *mingw_keys[] = {"SOFTWARE", "Microsoft", "Windows", "CurrentVersion", "Uninstall", NULL};
149
150/* Tack the executable on the end of a (possibly slash terminated) buffer
151   and convert everything to \. */
152static const char *
153tack_on_executable (char *buf, const char *executable)
154{
155  char *p = strchr (buf, '\0');
156  if (p > buf && (p[-1] == '\\' || p[-1] == '/'))
157    p[-1] = '\0';
158  backslashify (strcat (buf, executable));
159  return buf;
160}
161
162/* Walk down a registry hierarchy until the end.  Return the key. */
163static HKEY
164openkey (HKEY hStart, const char *keys[])
165{
166  HKEY hKey, hTmp;
167  for (hKey = hStart; *keys; keys++)
168    {
169      LONG res;
170      hTmp = hKey;
171      res = RegOpenKey (hTmp, *keys, &hKey);
172
173      if (hTmp != HKEY_LOCAL_MACHINE)
174	RegCloseKey (hTmp);
175
176      if (res != ERROR_SUCCESS)
177	return NULL;
178    }
179  return hKey;
180}
181
182/* Return the "mingw root" as derived from the mingw uninstall information. */
183static const char *
184mingw_rootify (const char *executable)
185{
186  HKEY hKey, hTmp;
187  DWORD maxlen;
188  char *namebuf, *foundbuf;
189  DWORD i;
190  LONG res;
191
192  /* Open the uninstall "directory". */
193  hKey = openkey (HKEY_LOCAL_MACHINE, mingw_keys);
194
195  /* Not found. */
196  if (!hKey)
197    return executable;
198
199  /* Need to enumerate all of the keys here looking for one the most recent
200     one for MinGW. */
201  if (RegQueryInfoKey (hKey, NULL, NULL, NULL, NULL, &maxlen, NULL, NULL,
202		       NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
203    {
204      RegCloseKey (hKey);
205      return executable;
206    }
207  namebuf = XNEWVEC (char, ++maxlen);
208  foundbuf = XNEWVEC (char, maxlen);
209  foundbuf[0] = '\0';
210  if (!namebuf || !foundbuf)
211    {
212      RegCloseKey (hKey);
213      if (namebuf)
214	free (namebuf);
215      if (foundbuf)
216	free (foundbuf);
217      return executable;
218    }
219
220  /* Look through all of the keys for one that begins with Minimal GNU...
221     Try to get the latest version by doing a string compare although that
222     string never really works with version number sorting. */
223  for (i = 0; RegEnumKey (hKey, i, namebuf, maxlen) == ERROR_SUCCESS; i++)
224    {
225      int match = strcasecmp (namebuf, MINGW_NAME);
226      if (match < 0)
227	continue;
228      if (match > 0 && strncasecmp (namebuf, MINGW_NAME, MINGW_NAME_LEN) > 0)
229	continue;
230      if (strcasecmp (namebuf, foundbuf) > 0)
231	strcpy (foundbuf, namebuf);
232    }
233  free (namebuf);
234
235  /* If foundbuf is empty, we didn't find anything.  Punt. */
236  if (!foundbuf[0])
237    {
238      free (foundbuf);
239      RegCloseKey (hKey);
240      return executable;
241    }
242
243  /* Open the key that we wanted */
244  res = RegOpenKey (hKey, foundbuf, &hTmp);
245  RegCloseKey (hKey);
246  free (foundbuf);
247
248  /* Don't know why this would fail, but you gotta check */
249  if (res != ERROR_SUCCESS)
250    return executable;
251
252  maxlen = 0;
253  /* Get the length of the value pointed to by InstallLocation */
254  if (RegQueryValueEx (hTmp, "InstallLocation", 0, NULL, NULL,
255		       &maxlen) != ERROR_SUCCESS || maxlen == 0)
256    {
257      RegCloseKey (hTmp);
258      return executable;
259    }
260
261  /* Allocate space for the install location */
262  foundbuf = XNEWVEC (char, maxlen + strlen (executable));
263  if (!foundbuf)
264    {
265      free (foundbuf);
266      RegCloseKey (hTmp);
267    }
268
269  /* Read the install location into the buffer */
270  res = RegQueryValueEx (hTmp, "InstallLocation", 0, NULL, (LPBYTE) foundbuf,
271			 &maxlen);
272  RegCloseKey (hTmp);
273  if (res != ERROR_SUCCESS)
274    {
275      free (foundbuf);
276      return executable;
277    }
278
279  /* Concatenate the install location and the executable, turn all slashes
280     to backslashes, and return that. */
281  return tack_on_executable (foundbuf, executable);
282}
283
284/* Read the install location of msys from it's installation file and
285   rootify the executable based on that. */
286static const char *
287msys_rootify (const char *executable)
288{
289  size_t bufsize = 64;
290  size_t execlen = strlen (executable) + 1;
291  char *buf;
292  DWORD res = 0;
293  for (;;)
294    {
295      buf = XNEWVEC (char, bufsize + execlen);
296      if (!buf)
297	break;
298      res = GetPrivateProfileString ("InstallSettings", "InstallPath", NULL,
299				     buf, bufsize, "msys.ini");
300      if (!res)
301	break;
302      if (strlen (buf) < bufsize)
303	break;
304      res = 0;
305      free (buf);
306      bufsize *= 2;
307      if (bufsize > 65536)
308	{
309	  buf = NULL;
310	  break;
311	}
312    }
313
314  if (res)
315    return tack_on_executable (buf, executable);
316
317  /* failed */
318  if (buf)
319    free (buf);
320  return executable;
321}
322#endif
323
324/* Return a Windows command-line from ARGV.  It is the caller's
325   responsibility to free the string returned.  */
326
327static char *
328argv_to_cmdline (char *const *argv)
329{
330  char *cmdline;
331  char *p;
332  size_t cmdline_len;
333  int i, j, k;
334
335  cmdline_len = 0;
336  for (i = 0; argv[i]; i++)
337    {
338      /* We quote every last argument.  This simplifies the problem;
339	 we need only escape embedded double-quotes and immediately
340	 preceeding backslash characters.  A sequence of backslach characters
341	 that is not follwed by a double quote character will not be
342	 escaped.  */
343      for (j = 0; argv[i][j]; j++)
344	{
345	  if (argv[i][j] == '"')
346	    {
347	      /* Escape preceeding backslashes.  */
348	      for (k = j - 1; k >= 0 && argv[i][k] == '\\'; k--)
349		cmdline_len++;
350	      /* Escape the qote character.  */
351	      cmdline_len++;
352	    }
353	}
354      /* Trailing backslashes also need to be escaped because they will be
355         followed by the terminating quote.  */
356      for (k = j - 1; k >= 0 && argv[i][k] == '\\'; k--)
357	cmdline_len++;
358      cmdline_len += j;
359      cmdline_len += 3;  /* for leading and trailing quotes and space */
360    }
361  cmdline = xmalloc (cmdline_len);
362  p = cmdline;
363  for (i = 0; argv[i]; i++)
364    {
365      *p++ = '"';
366      for (j = 0; argv[i][j]; j++)
367	{
368	  if (argv[i][j] == '"')
369	    {
370	      for (k = j - 1; k >= 0 && argv[i][k] == '\\'; k--)
371		*p++ = '\\';
372	      *p++ = '\\';
373	    }
374	  *p++ = argv[i][j];
375	}
376      for (k = j - 1; k >= 0 && argv[i][k] == '\\'; k--)
377	*p++ = '\\';
378      *p++ = '"';
379      *p++ = ' ';
380    }
381  p[-1] = '\0';
382  return cmdline;
383}
384
385static const char *const
386std_suffixes[] = {
387  ".com",
388  ".exe",
389  ".bat",
390  ".cmd",
391  0
392};
393static const char *const
394no_suffixes[] = {
395  "",
396  0
397};
398
399/* Returns the full path to PROGRAM.  If SEARCH is true, look for
400   PROGRAM in each directory in PATH.  */
401
402static char *
403find_executable (const char *program, BOOL search)
404{
405  char *full_executable;
406  char *e;
407  size_t fe_len;
408  const char *path = 0;
409  const char *const *ext;
410  const char *p, *q;
411  size_t proglen = strlen (program);
412  int has_extension = !!strchr (program, '.');
413  int has_slash = (strchr (program, '/') || strchr (program, '\\'));
414  HANDLE h;
415
416  if (has_slash)
417    search = FALSE;
418
419  if (search)
420    path = getenv ("PATH");
421  if (!path)
422    path = "";
423
424  fe_len = 0;
425  for (p = path; *p; p = q)
426    {
427      q = p;
428      while (*q != ';' && *q != '\0')
429	q++;
430      if ((size_t)(q - p) > fe_len)
431	fe_len = q - p;
432      if (*q == ';')
433	q++;
434    }
435  fe_len = fe_len + 1 + proglen + (has_extension ? 1 : 5);
436  full_executable = xmalloc (fe_len);
437
438  p = path;
439  do
440    {
441      q = p;
442      while (*q != ';' && *q != '\0')
443	q++;
444
445      e = full_executable;
446      memcpy (e, p, q - p);
447      e += (q - p);
448      if (q - p)
449	*e++ = '\\';
450      strcpy (e, program);
451
452      if (*q == ';')
453	q++;
454
455      for (e = full_executable; *e; e++)
456	if (*e == '/')
457	  *e = '\\';
458
459      /* At this point, e points to the terminating NUL character for
460         full_executable.  */
461      for (ext = has_extension ? no_suffixes : std_suffixes; *ext; ext++)
462	{
463	  /* Remove any current extension.  */
464	  *e = '\0';
465	  /* Add the new one.  */
466	  strcat (full_executable, *ext);
467
468	  /* Attempt to open this file.  */
469	  h = CreateFile (full_executable, GENERIC_READ,
470			  FILE_SHARE_READ | FILE_SHARE_WRITE,
471			  0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
472	  if (h != INVALID_HANDLE_VALUE)
473	    goto found;
474	}
475      p = q;
476    }
477  while (*p);
478  free (full_executable);
479  return 0;
480
481 found:
482  CloseHandle (h);
483  return full_executable;
484}
485
486/* Low-level process creation function and helper.  */
487
488static int
489env_compare (const void *a_ptr, const void *b_ptr)
490{
491  const char *a;
492  const char *b;
493  unsigned char c1;
494  unsigned char c2;
495
496  a = *(const char **) a_ptr;
497  b = *(const char **) b_ptr;
498
499  /* a and b will be of the form: VAR=VALUE
500     We compare only the variable name part here using a case-insensitive
501     comparison algorithm.  It might appear that in fact strcasecmp () can
502     take the place of this whole function, and indeed it could, save for
503     the fact that it would fail in cases such as comparing A1=foo and
504     A=bar (because 1 is less than = in the ASCII character set).
505     (Environment variables containing no numbers would work in such a
506     scenario.)  */
507
508  do
509    {
510      c1 = (unsigned char) tolower (*a++);
511      c2 = (unsigned char) tolower (*b++);
512
513      if (c1 == '=')
514        c1 = '\0';
515
516      if (c2 == '=')
517        c2 = '\0';
518    }
519  while (c1 == c2 && c1 != '\0');
520
521  return c1 - c2;
522}
523
524static long
525win32_spawn (const char *executable,
526	     BOOL search,
527	     char *const *argv,
528             char *const *env, /* array of strings of the form: VAR=VALUE */
529	     DWORD dwCreationFlags,
530	     LPSTARTUPINFO si,
531	     LPPROCESS_INFORMATION pi)
532{
533  char *full_executable;
534  char *cmdline;
535  char **env_copy;
536  char *env_block = NULL;
537
538  full_executable = NULL;
539  cmdline = NULL;
540
541  if (env)
542    {
543      int env_size;
544
545      /* Count the number of environment bindings supplied.  */
546      for (env_size = 0; env[env_size]; env_size++)
547        continue;
548
549      /* Assemble an environment block, if required.  This consists of
550         VAR=VALUE strings juxtaposed (with one null character between each
551         pair) and an additional null at the end.  */
552      if (env_size > 0)
553        {
554          int var;
555          int total_size = 1; /* 1 is for the final null.  */
556          char *bufptr;
557
558          /* Windows needs the members of the block to be sorted by variable
559             name.  */
560          env_copy = alloca (sizeof (char *) * env_size);
561          memcpy (env_copy, env, sizeof (char *) * env_size);
562          qsort (env_copy, env_size, sizeof (char *), env_compare);
563
564          for (var = 0; var < env_size; var++)
565            total_size += strlen (env[var]) + 1;
566
567          env_block = malloc (total_size);
568          bufptr = env_block;
569          for (var = 0; var < env_size; var++)
570            bufptr = stpcpy (bufptr, env_copy[var]) + 1;
571
572          *bufptr = '\0';
573        }
574    }
575
576  full_executable = find_executable (executable, search);
577  if (!full_executable)
578    goto error;
579  cmdline = argv_to_cmdline (argv);
580  if (!cmdline)
581    goto error;
582
583  /* Create the child process.  */
584  if (!CreateProcess (full_executable, cmdline,
585		      /*lpProcessAttributes=*/NULL,
586		      /*lpThreadAttributes=*/NULL,
587		      /*bInheritHandles=*/TRUE,
588		      dwCreationFlags,
589		      (LPVOID) env_block,
590		      /*lpCurrentDirectory=*/NULL,
591		      si,
592		      pi))
593    {
594      if (env_block)
595        free (env_block);
596
597      free (full_executable);
598
599      return -1;
600    }
601
602  /* Clean up.  */
603  CloseHandle (pi->hThread);
604  free (full_executable);
605  if (env_block)
606    free (env_block);
607
608  return (long) pi->hProcess;
609
610 error:
611  if (env_block)
612    free (env_block);
613  if (cmdline)
614    free (cmdline);
615  if (full_executable)
616    free (full_executable);
617
618  return -1;
619}
620
621static long
622spawn_script (const char *executable, char *const *argv,
623              char* const *env,
624	      DWORD dwCreationFlags,
625	      LPSTARTUPINFO si,
626	      LPPROCESS_INFORMATION pi)
627{
628  int pid = -1;
629  int save_errno = errno;
630  int fd = _open (executable, _O_RDONLY);
631
632  if (fd >= 0)
633    {
634      char buf[MAX_PATH + 5];
635      int len = _read (fd, buf, sizeof (buf) - 1);
636      _close (fd);
637      if (len > 3)
638	{
639	  char *eol;
640	  buf[len] = '\0';
641	  eol = strchr (buf, '\n');
642	  if (eol && strncmp (buf, "#!", 2) == 0)
643	    {
644	      char *executable1;
645	      const char ** avhere = (const char **) --argv;
646	      do
647		*eol = '\0';
648	      while (*--eol == '\r' || *eol == ' ' || *eol == '\t');
649	      for (executable1 = buf + 2; *executable1 == ' ' || *executable1 == '\t'; executable1++)
650		continue;
651
652	      backslashify (executable1);
653	      *avhere = executable1;
654#ifndef USE_MINGW_MSYS
655	      executable = strrchr (executable1, '\\') + 1;
656	      if (!executable)
657		executable = executable1;
658	      pid = win32_spawn (executable, TRUE, argv, env,
659				 dwCreationFlags, si, pi);
660#else
661	      if (strchr (executable1, '\\') == NULL)
662		pid = win32_spawn (executable1, TRUE, argv, env,
663				   dwCreationFlags, si, pi);
664	      else if (executable1[0] != '\\')
665		pid = win32_spawn (executable1, FALSE, argv, env,
666				   dwCreationFlags, si, pi);
667	      else
668		{
669		  const char *newex = mingw_rootify (executable1);
670		  *avhere = newex;
671		  pid = win32_spawn (newex, FALSE, argv, env,
672				     dwCreationFlags, si, pi);
673		  if (executable1 != newex)
674		    free ((char *) newex);
675		  if (pid < 0)
676		    {
677		      newex = msys_rootify (executable1);
678		      if (newex != executable1)
679			{
680			  *avhere = newex;
681			  pid = win32_spawn (newex, FALSE, argv, env,
682					     dwCreationFlags, si, pi);
683			  free ((char *) newex);
684			}
685		    }
686		}
687#endif
688	    }
689	}
690    }
691  if (pid < 0)
692    errno = save_errno;
693  return pid;
694}
695
696/* Execute a child.  */
697
698static long
699pex_win32_exec_child (struct pex_obj *obj ATTRIBUTE_UNUSED, int flags,
700		      const char *executable, char * const * argv,
701                      char* const* env,
702		      int in, int out, int errdes,
703		      int toclose ATTRIBUTE_UNUSED,
704		      const char **errmsg,
705		      int *err)
706{
707  long pid;
708  HANDLE stdin_handle;
709  HANDLE stdout_handle;
710  HANDLE stderr_handle;
711  DWORD dwCreationFlags;
712  OSVERSIONINFO version_info;
713  STARTUPINFO si;
714  PROCESS_INFORMATION pi;
715
716  stdin_handle = INVALID_HANDLE_VALUE;
717  stdout_handle = INVALID_HANDLE_VALUE;
718  stderr_handle = INVALID_HANDLE_VALUE;
719
720  stdin_handle = (HANDLE) _get_osfhandle (in);
721  stdout_handle = (HANDLE) _get_osfhandle (out);
722  if (!(flags & PEX_STDERR_TO_STDOUT))
723    stderr_handle = (HANDLE) _get_osfhandle (errdes);
724  else
725    stderr_handle = stdout_handle;
726
727  /* Determine the version of Windows we are running on.  */
728  version_info.dwOSVersionInfoSize = sizeof (version_info);
729  GetVersionEx (&version_info);
730  if (version_info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
731    /* On Windows 95/98/ME the CREATE_NO_WINDOW flag is not
732       supported, so we cannot avoid creating a console window.  */
733    dwCreationFlags = 0;
734  else
735    {
736      HANDLE conout_handle;
737
738      /* Determine whether or not we have an associated console.  */
739      conout_handle = CreateFile("CONOUT$",
740				 GENERIC_WRITE,
741				 FILE_SHARE_WRITE,
742				 /*lpSecurityAttributes=*/NULL,
743				 OPEN_EXISTING,
744				 FILE_ATTRIBUTE_NORMAL,
745				 /*hTemplateFile=*/NULL);
746      if (conout_handle == INVALID_HANDLE_VALUE)
747	/* There is no console associated with this process.  Since
748	   the child is a console process, the OS would normally
749	   create a new console Window for the child.  Since we'll be
750	   redirecting the child's standard streams, we do not need
751	   the console window.  */
752	dwCreationFlags = CREATE_NO_WINDOW;
753      else
754	{
755	  /* There is a console associated with the process, so the OS
756	     will not create a new console.  And, if we use
757	     CREATE_NO_WINDOW in this situation, the child will have
758	     no associated console.  Therefore, if the child's
759	     standard streams are connected to the console, the output
760	     will be discarded.  */
761	  CloseHandle(conout_handle);
762	  dwCreationFlags = 0;
763	}
764    }
765
766  /* Since the child will be a console process, it will, by default,
767     connect standard input/output to its console.  However, we want
768     the child to use the handles specifically designated above.  In
769     addition, if there is no console (such as when we are running in
770     a Cygwin X window), then we must redirect the child's
771     input/output, as there is no console for the child to use.  */
772  memset (&si, 0, sizeof (si));
773  si.cb = sizeof (si);
774  si.dwFlags = STARTF_USESTDHANDLES;
775  si.hStdInput = stdin_handle;
776  si.hStdOutput = stdout_handle;
777  si.hStdError = stderr_handle;
778
779  /* Create the child process.  */
780  pid = win32_spawn (executable, (flags & PEX_SEARCH) != 0,
781		     argv, env, dwCreationFlags, &si, &pi);
782  if (pid == -1)
783    pid = spawn_script (executable, argv, env, dwCreationFlags,
784                        &si, &pi);
785  if (pid == -1)
786    {
787      *err = ENOENT;
788      *errmsg = "CreateProcess";
789    }
790
791  /* Close the standard output and standard error handles in the
792     parent.  */
793  if (out != STDOUT_FILENO)
794    obj->funcs->close (obj, out);
795  if (errdes != STDERR_FILENO)
796    obj->funcs->close (obj, errdes);
797
798  return pid;
799}
800
801/* Wait for a child process to complete.  MS CRTDLL doesn't return
802   enough information in status to decide if the child exited due to a
803   signal or not, rather it simply returns an integer with the exit
804   code of the child; eg., if the child exited with an abort() call
805   and didn't have a handler for SIGABRT, it simply returns with
806   status == 3.  We fix the status code to conform to the usual WIF*
807   macros.  Note that WIFSIGNALED will never be true under CRTDLL. */
808
809static int
810pex_win32_wait (struct pex_obj *obj ATTRIBUTE_UNUSED, long pid,
811		int *status, struct pex_time *time, int done ATTRIBUTE_UNUSED,
812		const char **errmsg, int *err)
813{
814  DWORD termstat;
815  HANDLE h;
816
817  if (time != NULL)
818    memset (time, 0, sizeof *time);
819
820  h = (HANDLE) pid;
821
822  /* FIXME: If done is non-zero, we should probably try to kill the
823     process.  */
824  if (WaitForSingleObject (h, INFINITE) != WAIT_OBJECT_0)
825    {
826      CloseHandle (h);
827      *err = ECHILD;
828      *errmsg = "WaitForSingleObject";
829      return -1;
830    }
831
832  GetExitCodeProcess (h, &termstat);
833  CloseHandle (h);
834
835  /* A value of 3 indicates that the child caught a signal, but not
836     which one.  Since only SIGABRT, SIGFPE and SIGINT do anything, we
837     report SIGABRT.  */
838  if (termstat == 3)
839    *status = SIGABRT;
840  else
841    *status = (termstat & 0xff) << 8;
842
843  return 0;
844}
845
846/* Create a pipe.  */
847
848static int
849pex_win32_pipe (struct pex_obj *obj ATTRIBUTE_UNUSED, int *p,
850		int binary)
851{
852  return _pipe (p, 256, binary ? _O_BINARY : _O_TEXT);
853}
854
855/* Get a FILE pointer to read from a file descriptor.  */
856
857static FILE *
858pex_win32_fdopenr (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd,
859		   int binary)
860{
861  return fdopen (fd, binary ? "rb" : "r");
862}
863
864static FILE *
865pex_win32_fdopenw (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd,
866		   int binary)
867{
868  HANDLE h = (HANDLE) _get_osfhandle (fd);
869  if (h == INVALID_HANDLE_VALUE)
870    return NULL;
871  if (! SetHandleInformation (h, HANDLE_FLAG_INHERIT, 0))
872    return NULL;
873  return fdopen (fd, binary ? "wb" : "w");
874}
875
876#ifdef MAIN
877#include <stdio.h>
878
879int
880main (int argc ATTRIBUTE_UNUSED, char **argv)
881{
882  char const *errmsg;
883  int err;
884  argv++;
885  printf ("%ld\n", pex_win32_exec_child (NULL, PEX_SEARCH, argv[0], argv, NULL, 0, 0, 1, 2, &errmsg, &err));
886  exit (0);
887}
888#endif
889