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