]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - contrib/gcclibs/libiberty/pex-win32.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / contrib / gcclibs / libiberty / pex-win32.c
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
6 This file is part of the libiberty library.
7 Libiberty is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public
9 License as published by the Free Software Foundation; either
10 version 2 of the License, or (at your option) any later version.
11
12 Libiberty is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 Library General Public License for more details.
16
17 You should have received a copy of the GNU Library General Public
18 License along with libiberty; see the file COPYING.LIB.  If not,
19 write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
20 Boston, 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
64 extern 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.  */
72 static void
73 backslashify (char *s)
74 {
75   while ((s = strchr (s, '/')) != NULL)
76     *s = '\\';
77   return;
78 }
79
80 static int pex_win32_open_read (struct pex_obj *, const char *, int);
81 static int pex_win32_open_write (struct pex_obj *, const char *, int);
82 static 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 *);
86 static int pex_win32_close (struct pex_obj *, int);
87 static int pex_win32_wait (struct pex_obj *, long, int *,
88                            struct pex_time *, int, const char **, int *);
89 static int pex_win32_pipe (struct pex_obj *, int *, int);
90 static FILE *pex_win32_fdopenr (struct pex_obj *, int, int);
91 static FILE *pex_win32_fdopenw (struct pex_obj *, int, int);
92
93 /* The list of functions we pass to the common routines.  */
94
95 const 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
110 struct pex_obj *
111 pex_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
118 static int
119 pex_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
127 static int
128 pex_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
141 static int
142 pex_win32_close (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd)
143 {
144   return _close (fd);
145 }
146
147 #ifdef USE_MINGW_MSYS
148 static 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 \. */
152 static const char *
153 tack_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. */
163 static HKEY
164 openkey (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. */
183 static const char *
184 mingw_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. */
286 static const char *
287 msys_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
327 static char *
328 argv_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
385 static const char *const
386 std_suffixes[] = {
387   ".com",
388   ".exe",
389   ".bat",
390   ".cmd",
391   0
392 };
393 static const char *const
394 no_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
402 static char *
403 find_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
488 static int
489 env_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
524 static long
525 win32_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
621 static long
622 spawn_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
698 static long
699 pex_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
809 static int
810 pex_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
848 static int
849 pex_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
857 static FILE *
858 pex_win32_fdopenr (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd,
859                    int binary)
860 {
861   return fdopen (fd, binary ? "rb" : "r");
862 }
863
864 static FILE *
865 pex_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
879 int
880 main (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