xref: /trueos/contrib/gcclibs/libiberty/pex-win32.c (revision 97ee9c3f29d2708e2a9383c5bfe5a3dd7dfe60dd)
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
backslashify(char * s)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 *
pex_init(int flags,const char * pname,const char * tempbase)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
pex_win32_open_read(struct pex_obj * obj ATTRIBUTE_UNUSED,const char * name,int binary)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
pex_win32_open_write(struct pex_obj * obj ATTRIBUTE_UNUSED,const char * name,int binary)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
pex_win32_close(struct pex_obj * obj ATTRIBUTE_UNUSED,int fd)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 *
tack_on_executable(char * buf,const char * executable)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
openkey(HKEY hStart,const char * keys[])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 *
mingw_rootify(const char * executable)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 *
msys_rootify(const char * executable)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 *
argv_to_cmdline(char * const * argv)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 *
find_executable(const char * program,BOOL search)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
env_compare(const void * a_ptr,const void * b_ptr)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
win32_spawn(const char * executable,BOOL search,char * const * argv,char * const * env,DWORD dwCreationFlags,LPSTARTUPINFO si,LPPROCESS_INFORMATION pi)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
spawn_script(const char * executable,char * const * argv,char * const * env,DWORD dwCreationFlags,LPSTARTUPINFO si,LPPROCESS_INFORMATION pi)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
pex_win32_exec_child(struct pex_obj * obj ATTRIBUTE_UNUSED,int flags,const char * executable,char * const * argv,char * const * env,int in,int out,int errdes,int toclose ATTRIBUTE_UNUSED,const char ** errmsg,int * err)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
pex_win32_wait(struct pex_obj * obj ATTRIBUTE_UNUSED,long pid,int * status,struct pex_time * time,int done ATTRIBUTE_UNUSED,const char ** errmsg,int * err)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
pex_win32_pipe(struct pex_obj * obj ATTRIBUTE_UNUSED,int * p,int binary)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 *
pex_win32_fdopenr(struct pex_obj * obj ATTRIBUTE_UNUSED,int fd,int binary)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 *
pex_win32_fdopenw(struct pex_obj * obj ATTRIBUTE_UNUSED,int fd,int binary)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
main(int argc ATTRIBUTE_UNUSED,char ** argv)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
889