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