merge from gcc
[external/binutils.git] / 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 <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.  */
68 static void
69 backslashify (char *s)
70 {
71   while ((s = strchr (s, '/')) != NULL)
72     *s = '\\';
73   return;
74 }
75
76 static int pex_win32_open_read (struct pex_obj *, const char *, int);
77 static int pex_win32_open_write (struct pex_obj *, const char *, int);
78 static long pex_win32_exec_child (struct pex_obj *, int, const char *,
79                                   char * const *, int, int, int,
80                                   const char **, int *);
81 static int pex_win32_close (struct pex_obj *, int);
82 static int pex_win32_wait (struct pex_obj *, long, int *,
83                            struct pex_time *, int, const char **, int *);
84 static int pex_win32_pipe (struct pex_obj *, int *, int);
85 static FILE *pex_win32_fdopenr (struct pex_obj *, int, int);
86
87 /* The list of functions we pass to the common routines.  */
88
89 const struct pex_funcs funcs =
90 {
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
103 struct pex_obj *
104 pex_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
111 static int
112 pex_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
120 static int
121 pex_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
134 static int
135 pex_win32_close (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd)
136 {
137   return _close (fd);
138 }
139
140 #ifdef USE_MINGW_MSYS
141 static 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 \. */
145 static const char *
146 tack_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. */
156 static HKEY
157 openkey (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. */
176 static const char *
177 mingw_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. */
279 static const char *
280 msys_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
317 /* Return a Windows command-line from ARGV.  It is the caller's
318    responsibility to free the string returned.  */
319
320 static char *
321 argv_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
378 static const char *const
379 std_suffixes[] = {
380   ".com",
381   ".exe",
382   ".bat",
383   ".cmd",
384   0
385 };
386 static const char *const
387 no_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
395 static char *
396 find_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
481 static long
482 win32_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
531 static long
532 spawn_script (const char *executable, char *const *argv,
533               DWORD dwCreationFlags,
534               LPSTARTUPINFO si,
535               LPPROCESS_INFORMATION pi)
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;
567               pid = win32_spawn (executable, TRUE, argv, 
568                                  dwCreationFlags, si, pi);
569 #else
570               if (strchr (executable1, '\\') == NULL)
571                 pid = win32_spawn (executable1, TRUE, argv, 
572                                    dwCreationFlags, si, pi);
573               else if (executable1[0] != '\\')
574                 pid = win32_spawn (executable1, FALSE, argv, 
575                                    dwCreationFlags, si, pi);
576               else
577                 {
578                   const char *newex = mingw_rootify (executable1);
579                   *avhere = newex;
580                   pid = win32_spawn (newex, FALSE, argv, 
581                                      dwCreationFlags, si, pi);
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;
590                           pid = win32_spawn (newex, FALSE, argv, 
591                                              dwCreationFlags, si, pi);
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
605 /* Execute a child.  */
606
607 static long
608 pex_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 {
613   long pid;
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
641     {
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 
660         {
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;
669         }
670     }
671
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);
688   if (pid == -1)
689     pid = spawn_script (executable, argv, dwCreationFlags, &si, &pi);
690   if (pid == -1)
691     {
692       *err = ENOENT;
693       *errmsg = "CreateProcess";
694     }
695
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);
702
703   return pid;
704 }
705
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
714 static int
715 pex_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)
718 {
719   DWORD termstat;
720   HANDLE h;
721
722   if (time != NULL)
723     memset (time, 0, sizeof *time);
724
725   h = (HANDLE) pid;
726
727   /* FIXME: If done is non-zero, we should probably try to kill the
728      process.  */
729   if (WaitForSingleObject (h, INFINITE) != WAIT_OBJECT_0)
730     {
731       CloseHandle (h);
732       *err = ECHILD;
733       *errmsg = "WaitForSingleObject";
734       return -1;
735     }
736
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.  */
743   if (termstat == 3)
744     *status = SIGABRT;
745   else
746     *status = (termstat & 0xff) << 8;
747
748   return 0;
749 }
750
751 /* Create a pipe.  */
752
753 static int
754 pex_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
762 static FILE *
763 pex_win32_fdopenr (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd,
764                    int binary)
765 {
766   return fdopen (fd, binary ? "rb" : "r");
767 }
768
769 #ifdef MAIN
770 #include <stdio.h>
771
772 int
773 main (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