Back to index

cell-binutils  2.17cvs20070401
pex-win32.c
Go to the documentation of this file.
00001 /* Utilities to execute a program in a subprocess (possibly linked by pipes
00002    with other subprocesses), and wait for it.  Generic Win32 specialization.
00003    Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006
00004    Free Software Foundation, Inc.
00005 
00006 This file is part of the libiberty library.
00007 Libiberty is free software; you can redistribute it and/or
00008 modify it under the terms of the GNU Library General Public
00009 License as published by the Free Software Foundation; either
00010 version 2 of the License, or (at your option) any later version.
00011 
00012 Libiberty is distributed in the hope that it will be useful,
00013 but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015 Library General Public License for more details.
00016 
00017 You should have received a copy of the GNU Library General Public
00018 License along with libiberty; see the file COPYING.LIB.  If not,
00019 write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
00020 Boston, MA 02110-1301, USA.  */
00021 
00022 #include "pex-common.h"
00023 
00024 #include <windows.h>
00025 
00026 #ifdef HAVE_STDLIB_H
00027 #include <stdlib.h>
00028 #endif
00029 #ifdef HAVE_STRING_H
00030 #include <string.h>
00031 #endif
00032 #ifdef HAVE_UNISTD_H
00033 #include <unistd.h>
00034 #endif
00035 #ifdef HAVE_SYS_WAIT_H
00036 #include <sys/wait.h>
00037 #endif
00038 
00039 #include <assert.h>
00040 #include <process.h>
00041 #include <io.h>
00042 #include <fcntl.h>
00043 #include <signal.h>
00044 #include <sys/stat.h>
00045 #include <errno.h>
00046 #include <ctype.h>
00047 
00048 /* mingw32 headers may not define the following.  */
00049 
00050 #ifndef _P_WAIT
00051 #  define _P_WAIT    0
00052 #  define _P_NOWAIT  1
00053 #  define _P_OVERLAY 2
00054 #  define _P_NOWAITO 3
00055 #  define _P_DETACH  4
00056 
00057 #  define WAIT_CHILD        0
00058 #  define WAIT_GRANDCHILD   1
00059 #endif
00060 
00061 #define MINGW_NAME "Minimalist GNU for Windows"
00062 #define MINGW_NAME_LEN (sizeof(MINGW_NAME) - 1)
00063 
00064 extern char *stpcpy (char *dst, const char *src);
00065 
00066 /* Ensure that the executable pathname uses Win32 backslashes. This
00067    is not necessary on NT, but on W9x, forward slashes causes
00068    failure of spawn* and exec* functions (and probably any function
00069    that calls CreateProcess) *iff* the executable pathname (argv[0])
00070    is a quoted string.  And quoting is necessary in case a pathname
00071    contains embedded white space.  You can't win.  */
00072 static void
00073 backslashify (char *s)
00074 {
00075   while ((s = strchr (s, '/')) != NULL)
00076     *s = '\\';
00077   return;
00078 }
00079 
00080 static int pex_win32_open_read (struct pex_obj *, const char *, int);
00081 static int pex_win32_open_write (struct pex_obj *, const char *, int);
00082 static long pex_win32_exec_child (struct pex_obj *, int, const char *,
00083                               char * const *, char * const *,
00084                                   int, int, int, int,
00085                               const char **, int *);
00086 static int pex_win32_close (struct pex_obj *, int);
00087 static int pex_win32_wait (struct pex_obj *, long, int *,
00088                         struct pex_time *, int, const char **, int *);
00089 static int pex_win32_pipe (struct pex_obj *, int *, int);
00090 static FILE *pex_win32_fdopenr (struct pex_obj *, int, int);
00091 static FILE *pex_win32_fdopenw (struct pex_obj *, int, int);
00092 
00093 /* The list of functions we pass to the common routines.  */
00094 
00095 const struct pex_funcs funcs =
00096 {
00097   pex_win32_open_read,
00098   pex_win32_open_write,
00099   pex_win32_exec_child,
00100   pex_win32_close,
00101   pex_win32_wait,
00102   pex_win32_pipe,
00103   pex_win32_fdopenr,
00104   pex_win32_fdopenw,
00105   NULL /* cleanup */
00106 };
00107 
00108 /* Return a newly initialized pex_obj structure.  */
00109 
00110 struct pex_obj *
00111 pex_init (int flags, const char *pname, const char *tempbase)
00112 {
00113   return pex_init_common (flags, pname, tempbase, &funcs);
00114 }
00115 
00116 /* Open a file for reading.  */
00117 
00118 static int
00119 pex_win32_open_read (struct pex_obj *obj ATTRIBUTE_UNUSED, const char *name,
00120                    int binary)
00121 {
00122   return _open (name, _O_RDONLY | (binary ? _O_BINARY : _O_TEXT));
00123 }
00124 
00125 /* Open a file for writing.  */
00126 
00127 static int
00128 pex_win32_open_write (struct pex_obj *obj ATTRIBUTE_UNUSED, const char *name,
00129                     int binary)
00130 {
00131   /* Note that we can't use O_EXCL here because gcc may have already
00132      created the temporary file via make_temp_file.  */
00133   return _open (name,
00134               (_O_WRONLY | _O_CREAT | _O_TRUNC
00135                | (binary ? _O_BINARY : _O_TEXT)),
00136               _S_IREAD | _S_IWRITE);
00137 }
00138 
00139 /* Close a file.  */
00140 
00141 static int
00142 pex_win32_close (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd)
00143 {
00144   return _close (fd);
00145 }
00146 
00147 #ifdef USE_MINGW_MSYS
00148 static const char *mingw_keys[] = {"SOFTWARE", "Microsoft", "Windows", "CurrentVersion", "Uninstall", NULL};
00149 
00150 /* Tack the executable on the end of a (possibly slash terminated) buffer
00151    and convert everything to \. */
00152 static const char *
00153 tack_on_executable (char *buf, const char *executable)
00154 {
00155   char *p = strchr (buf, '\0');
00156   if (p > buf && (p[-1] == '\\' || p[-1] == '/'))
00157     p[-1] = '\0';
00158   backslashify (strcat (buf, executable));
00159   return buf;
00160 }
00161 
00162 /* Walk down a registry hierarchy until the end.  Return the key. */
00163 static HKEY
00164 openkey (HKEY hStart, const char *keys[])
00165 {
00166   HKEY hKey, hTmp;
00167   for (hKey = hStart; *keys; keys++)
00168     {
00169       LONG res;
00170       hTmp = hKey;
00171       res = RegOpenKey (hTmp, *keys, &hKey);
00172 
00173       if (hTmp != HKEY_LOCAL_MACHINE)
00174        RegCloseKey (hTmp);
00175 
00176       if (res != ERROR_SUCCESS)
00177        return NULL;
00178     }
00179   return hKey;
00180 }
00181 
00182 /* Return the "mingw root" as derived from the mingw uninstall information. */
00183 static const char *
00184 mingw_rootify (const char *executable)
00185 {
00186   HKEY hKey, hTmp;
00187   DWORD maxlen;
00188   char *namebuf, *foundbuf;
00189   DWORD i;
00190   LONG res;
00191 
00192   /* Open the uninstall "directory". */
00193   hKey = openkey (HKEY_LOCAL_MACHINE, mingw_keys);
00194 
00195   /* Not found. */
00196   if (!hKey)
00197     return executable;
00198 
00199   /* Need to enumerate all of the keys here looking for one the most recent
00200      one for MinGW. */
00201   if (RegQueryInfoKey (hKey, NULL, NULL, NULL, NULL, &maxlen, NULL, NULL,
00202                      NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
00203     {
00204       RegCloseKey (hKey);
00205       return executable;
00206     }
00207   namebuf = XNEWVEC (char, ++maxlen);
00208   foundbuf = XNEWVEC (char, maxlen);
00209   foundbuf[0] = '\0';
00210   if (!namebuf || !foundbuf)
00211     {
00212       RegCloseKey (hKey);
00213       if (namebuf)
00214        free (namebuf);
00215       if (foundbuf)
00216        free (foundbuf);
00217       return executable;
00218     }
00219 
00220   /* Look through all of the keys for one that begins with Minimal GNU...
00221      Try to get the latest version by doing a string compare although that
00222      string never really works with version number sorting. */
00223   for (i = 0; RegEnumKey (hKey, i, namebuf, maxlen) == ERROR_SUCCESS; i++)
00224     {
00225       int match = strcasecmp (namebuf, MINGW_NAME);
00226       if (match < 0)
00227        continue;
00228       if (match > 0 && strncasecmp (namebuf, MINGW_NAME, MINGW_NAME_LEN) > 0)
00229        continue;
00230       if (strcasecmp (namebuf, foundbuf) > 0)
00231        strcpy (foundbuf, namebuf);
00232     }
00233   free (namebuf);
00234 
00235   /* If foundbuf is empty, we didn't find anything.  Punt. */
00236   if (!foundbuf[0])
00237     {
00238       free (foundbuf);
00239       RegCloseKey (hKey);
00240       return executable;
00241     }
00242 
00243   /* Open the key that we wanted */
00244   res = RegOpenKey (hKey, foundbuf, &hTmp);
00245   RegCloseKey (hKey);
00246   free (foundbuf);
00247 
00248   /* Don't know why this would fail, but you gotta check */
00249   if (res != ERROR_SUCCESS)
00250     return executable;
00251 
00252   maxlen = 0;
00253   /* Get the length of the value pointed to by InstallLocation */
00254   if (RegQueryValueEx (hTmp, "InstallLocation", 0, NULL, NULL,
00255                      &maxlen) != ERROR_SUCCESS || maxlen == 0)
00256     {
00257       RegCloseKey (hTmp);
00258       return executable;
00259     }
00260 
00261   /* Allocate space for the install location */
00262   foundbuf = XNEWVEC (char, maxlen + strlen (executable));
00263   if (!foundbuf)
00264     {
00265       free (foundbuf);
00266       RegCloseKey (hTmp);
00267     }
00268 
00269   /* Read the install location into the buffer */
00270   res = RegQueryValueEx (hTmp, "InstallLocation", 0, NULL, (LPBYTE) foundbuf,
00271                       &maxlen);
00272   RegCloseKey (hTmp);
00273   if (res != ERROR_SUCCESS)
00274     {
00275       free (foundbuf);
00276       return executable;
00277     }
00278 
00279   /* Concatenate the install location and the executable, turn all slashes
00280      to backslashes, and return that. */
00281   return tack_on_executable (foundbuf, executable);
00282 }
00283 
00284 /* Read the install location of msys from it's installation file and
00285    rootify the executable based on that. */
00286 static const char *
00287 msys_rootify (const char *executable)
00288 {
00289   size_t bufsize = 64;
00290   size_t execlen = strlen (executable) + 1;
00291   char *buf;
00292   DWORD res = 0;
00293   for (;;)
00294     {
00295       buf = XNEWVEC (char, bufsize + execlen);
00296       if (!buf)
00297        break;
00298       res = GetPrivateProfileString ("InstallSettings", "InstallPath", NULL,
00299                                  buf, bufsize, "msys.ini");
00300       if (!res)
00301        break;
00302       if (strlen (buf) < bufsize)
00303        break;
00304       res = 0;
00305       free (buf);
00306       bufsize *= 2;
00307       if (bufsize > 65536)
00308        {
00309          buf = NULL;
00310          break;
00311        }
00312     }
00313 
00314   if (res)
00315     return tack_on_executable (buf, executable);
00316 
00317   /* failed */
00318   if (buf)
00319     free (buf);
00320   return executable;
00321 }
00322 #endif
00323 
00324 /* Return a Windows command-line from ARGV.  It is the caller's
00325    responsibility to free the string returned.  */
00326 
00327 static char *
00328 argv_to_cmdline (char *const *argv)
00329 {
00330   char *cmdline;
00331   char *p;
00332   size_t cmdline_len;
00333   int i, j, k;
00334 
00335   cmdline_len = 0;
00336   for (i = 0; argv[i]; i++)
00337     {
00338       /* We quote every last argument.  This simplifies the problem;
00339         we need only escape embedded double-quotes and immediately
00340         preceeding backslash characters.  A sequence of backslach characters
00341         that is not follwed by a double quote character will not be
00342         escaped.  */
00343       for (j = 0; argv[i][j]; j++)
00344        {
00345          if (argv[i][j] == '"')
00346            {
00347              /* Escape preceeding backslashes.  */
00348              for (k = j - 1; k >= 0 && argv[i][k] == '\\'; k--)
00349               cmdline_len++;
00350              /* Escape the qote character.  */
00351              cmdline_len++;
00352            }
00353        }
00354       /* Trailing backslashes also need to be escaped because they will be
00355          followed by the terminating quote.  */
00356       for (k = j - 1; k >= 0 && argv[i][k] == '\\'; k--)
00357        cmdline_len++;
00358       cmdline_len += j;
00359       cmdline_len += 3;  /* for leading and trailing quotes and space */
00360     }
00361   cmdline = XNEWVEC (char, cmdline_len);
00362   p = cmdline;
00363   for (i = 0; argv[i]; i++)
00364     {
00365       *p++ = '"';
00366       for (j = 0; argv[i][j]; j++)
00367        {
00368          if (argv[i][j] == '"')
00369            {
00370              for (k = j - 1; k >= 0 && argv[i][k] == '\\'; k--)
00371               *p++ = '\\';
00372              *p++ = '\\';
00373            }
00374          *p++ = argv[i][j];
00375        }
00376       for (k = j - 1; k >= 0 && argv[i][k] == '\\'; k--)
00377        *p++ = '\\';
00378       *p++ = '"';
00379       *p++ = ' ';
00380     }
00381   p[-1] = '\0';
00382   return cmdline;
00383 }
00384 
00385 /* We'll try the passed filename with all the known standard
00386    extensions, and then without extension.  We try no extension
00387    last so that we don't try to run some random extension-less
00388    file that might be hanging around.  We try both extension
00389    and no extension so that we don't need any fancy logic
00390    to determine if a file has extension.  */
00391 static const char *const
00392 std_suffixes[] = {
00393   ".com",
00394   ".exe",
00395   ".bat",
00396   ".cmd",
00397   "",
00398   0
00399 };
00400 
00401 /* Returns the full path to PROGRAM.  If SEARCH is true, look for
00402    PROGRAM in each directory in PATH.  */
00403 
00404 static char *
00405 find_executable (const char *program, BOOL search)
00406 {
00407   char *full_executable;
00408   char *e;
00409   size_t fe_len;
00410   const char *path = 0;
00411   const char *const *ext;
00412   const char *p, *q;
00413   size_t proglen = strlen (program);
00414   int has_slash = (strchr (program, '/') || strchr (program, '\\'));
00415   HANDLE h;
00416 
00417   if (has_slash)
00418     search = FALSE;
00419 
00420   if (search)
00421     path = getenv ("PATH");
00422   if (!path)
00423     path = "";
00424 
00425   fe_len = 0;
00426   for (p = path; *p; p = q)
00427     {
00428       q = p;
00429       while (*q != ';' && *q != '\0')
00430        q++;
00431       if ((size_t)(q - p) > fe_len)
00432        fe_len = q - p;
00433       if (*q == ';')
00434        q++;
00435     }
00436   fe_len = fe_len + 1 + proglen + 5 /* space for extension */;
00437   full_executable = XNEWVEC (char, fe_len);
00438 
00439   p = path;
00440   do
00441     {
00442       q = p;
00443       while (*q != ';' && *q != '\0')
00444        q++;
00445 
00446       e = full_executable;
00447       memcpy (e, p, q - p);
00448       e += (q - p);
00449       if (q - p)
00450        *e++ = '\\';
00451       strcpy (e, program);
00452 
00453       if (*q == ';')
00454        q++;
00455 
00456       for (e = full_executable; *e; e++)
00457        if (*e == '/')
00458          *e = '\\';
00459 
00460       /* At this point, e points to the terminating NUL character for
00461          full_executable.  */
00462       for (ext = std_suffixes; *ext; ext++)
00463        {
00464          /* Remove any current extension.  */
00465          *e = '\0';
00466          /* Add the new one.  */
00467          strcat (full_executable, *ext);
00468 
00469          /* Attempt to open this file.  */
00470          h = CreateFile (full_executable, GENERIC_READ,
00471                        FILE_SHARE_READ | FILE_SHARE_WRITE,
00472                        0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
00473          if (h != INVALID_HANDLE_VALUE)
00474            goto found;
00475        }
00476       p = q;
00477     }
00478   while (*p);
00479   free (full_executable);
00480   return 0;
00481 
00482  found:
00483   CloseHandle (h);
00484   return full_executable;
00485 }
00486 
00487 /* Low-level process creation function and helper.  */
00488 
00489 static int
00490 env_compare (const void *a_ptr, const void *b_ptr)
00491 {
00492   const char *a;
00493   const char *b;
00494   unsigned char c1;
00495   unsigned char c2;
00496 
00497   a = *(const char **) a_ptr;
00498   b = *(const char **) b_ptr;
00499 
00500   /* a and b will be of the form: VAR=VALUE
00501      We compare only the variable name part here using a case-insensitive
00502      comparison algorithm.  It might appear that in fact strcasecmp () can
00503      take the place of this whole function, and indeed it could, save for
00504      the fact that it would fail in cases such as comparing A1=foo and
00505      A=bar (because 1 is less than = in the ASCII character set).
00506      (Environment variables containing no numbers would work in such a
00507      scenario.)  */
00508 
00509   do
00510     {
00511       c1 = (unsigned char) tolower (*a++);
00512       c2 = (unsigned char) tolower (*b++);
00513 
00514       if (c1 == '=')
00515         c1 = '\0';
00516 
00517       if (c2 == '=')
00518         c2 = '\0';
00519     }
00520   while (c1 == c2 && c1 != '\0');
00521 
00522   return c1 - c2;
00523 }
00524 
00525 static long
00526 win32_spawn (const char *executable,
00527             BOOL search,
00528             char *const *argv,
00529              char *const *env, /* array of strings of the form: VAR=VALUE */
00530             DWORD dwCreationFlags,
00531             LPSTARTUPINFO si,
00532             LPPROCESS_INFORMATION pi)
00533 {
00534   char *full_executable;
00535   char *cmdline;
00536   char **env_copy;
00537   char *env_block = NULL;
00538 
00539   full_executable = NULL;
00540   cmdline = NULL;
00541 
00542   if (env)
00543     {
00544       int env_size;
00545 
00546       /* Count the number of environment bindings supplied.  */
00547       for (env_size = 0; env[env_size]; env_size++)
00548         continue;
00549     
00550       /* Assemble an environment block, if required.  This consists of
00551          VAR=VALUE strings juxtaposed (with one null character between each
00552          pair) and an additional null at the end.  */
00553       if (env_size > 0)
00554         {
00555           int var;
00556           int total_size = 1; /* 1 is for the final null.  */
00557           char *bufptr;
00558     
00559           /* Windows needs the members of the block to be sorted by variable
00560              name.  */
00561           env_copy = (char **) alloca (sizeof (char *) * env_size);
00562           memcpy (env_copy, env, sizeof (char *) * env_size);
00563           qsort (env_copy, env_size, sizeof (char *), env_compare);
00564     
00565           for (var = 0; var < env_size; var++)
00566             total_size += strlen (env[var]) + 1;
00567     
00568           env_block = XNEWVEC (char, total_size);
00569           bufptr = env_block;
00570           for (var = 0; var < env_size; var++)
00571             bufptr = stpcpy (bufptr, env_copy[var]) + 1;
00572     
00573           *bufptr = '\0';
00574         }
00575     }
00576 
00577   full_executable = find_executable (executable, search);
00578   if (!full_executable)
00579     goto error;
00580   cmdline = argv_to_cmdline (argv);
00581   if (!cmdline)
00582     goto error;
00583     
00584   /* Create the child process.  */  
00585   if (!CreateProcess (full_executable, cmdline, 
00586                     /*lpProcessAttributes=*/NULL,
00587                     /*lpThreadAttributes=*/NULL,
00588                     /*bInheritHandles=*/TRUE,
00589                     dwCreationFlags,
00590                     (LPVOID) env_block,
00591                     /*lpCurrentDirectory=*/NULL,
00592                     si,
00593                     pi))
00594     {
00595       if (env_block)
00596         free (env_block);
00597 
00598       free (full_executable);
00599 
00600       return -1;
00601     }
00602 
00603   /* Clean up.  */
00604   CloseHandle (pi->hThread);
00605   free (full_executable);
00606   if (env_block)
00607     free (env_block);
00608 
00609   return (long) pi->hProcess;
00610 
00611  error:
00612   if (env_block)
00613     free (env_block);
00614   if (cmdline)
00615     free (cmdline);
00616   if (full_executable)
00617     free (full_executable);
00618 
00619   return -1;
00620 }
00621 
00622 static long
00623 spawn_script (const char *executable, char *const *argv,
00624               char* const *env,
00625              DWORD dwCreationFlags,
00626              LPSTARTUPINFO si,
00627              LPPROCESS_INFORMATION pi)
00628 {
00629   int pid = -1;
00630   int save_errno = errno;
00631   int fd = _open (executable, _O_RDONLY);
00632 
00633   if (fd >= 0)
00634     {
00635       char buf[MAX_PATH + 5];
00636       int len = _read (fd, buf, sizeof (buf) - 1);
00637       _close (fd);
00638       if (len > 3)
00639        {
00640          char *eol;
00641          buf[len] = '\0';
00642          eol = strchr (buf, '\n');
00643          if (eol && strncmp (buf, "#!", 2) == 0)
00644            {
00645              char *executable1;
00646              const char ** avhere = (const char **) --argv;
00647              do
00648               *eol = '\0';
00649              while (*--eol == '\r' || *eol == ' ' || *eol == '\t');
00650              for (executable1 = buf + 2; *executable1 == ' ' || *executable1 == '\t'; executable1++)
00651               continue;
00652 
00653              backslashify (executable1);
00654              *avhere = executable1;
00655 #ifndef USE_MINGW_MSYS
00656              executable = strrchr (executable1, '\\') + 1;
00657              if (!executable)
00658               executable = executable1;
00659              pid = win32_spawn (executable, TRUE, argv, env,
00660                              dwCreationFlags, si, pi);
00661 #else
00662              if (strchr (executable1, '\\') == NULL)
00663               pid = win32_spawn (executable1, TRUE, argv, env,
00664                                dwCreationFlags, si, pi);
00665              else if (executable1[0] != '\\')
00666               pid = win32_spawn (executable1, FALSE, argv, env,
00667                                dwCreationFlags, si, pi);
00668              else
00669               {
00670                 const char *newex = mingw_rootify (executable1);
00671                 *avhere = newex;
00672                 pid = win32_spawn (newex, FALSE, argv, env,
00673                                  dwCreationFlags, si, pi);
00674                 if (executable1 != newex)
00675                   free ((char *) newex);
00676                 if (pid < 0)
00677                   {
00678                     newex = msys_rootify (executable1);
00679                     if (newex != executable1)
00680                      {
00681                        *avhere = newex;
00682                        pid = win32_spawn (newex, FALSE, argv, env,
00683                                         dwCreationFlags, si, pi);
00684                        free ((char *) newex);
00685                      }
00686                   }
00687               }
00688 #endif
00689            }
00690        }
00691     }
00692   if (pid < 0)
00693     errno = save_errno;
00694   return pid;
00695 }
00696 
00697 /* Execute a child.  */
00698 
00699 static long
00700 pex_win32_exec_child (struct pex_obj *obj ATTRIBUTE_UNUSED, int flags,
00701                     const char *executable, char * const * argv,
00702                       char* const* env,
00703                     int in, int out, int errdes,
00704                     int toclose ATTRIBUTE_UNUSED,
00705                     const char **errmsg,
00706                     int *err)
00707 {
00708   long pid;
00709   HANDLE stdin_handle;
00710   HANDLE stdout_handle;
00711   HANDLE stderr_handle;
00712   DWORD dwCreationFlags;
00713   OSVERSIONINFO version_info;
00714   STARTUPINFO si;
00715   PROCESS_INFORMATION pi;
00716 
00717   stdin_handle = INVALID_HANDLE_VALUE;
00718   stdout_handle = INVALID_HANDLE_VALUE;
00719   stderr_handle = INVALID_HANDLE_VALUE;
00720 
00721   stdin_handle = (HANDLE) _get_osfhandle (in);
00722   stdout_handle = (HANDLE) _get_osfhandle (out);
00723   if (!(flags & PEX_STDERR_TO_STDOUT))
00724     stderr_handle = (HANDLE) _get_osfhandle (errdes);
00725   else
00726     stderr_handle = stdout_handle;
00727 
00728   /* Determine the version of Windows we are running on.  */
00729   version_info.dwOSVersionInfoSize = sizeof (version_info); 
00730   GetVersionEx (&version_info);
00731   if (version_info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
00732     /* On Windows 95/98/ME the CREATE_NO_WINDOW flag is not
00733        supported, so we cannot avoid creating a console window.  */
00734     dwCreationFlags = 0;
00735   else
00736     {
00737       HANDLE conout_handle;
00738 
00739       /* Determine whether or not we have an associated console.  */
00740       conout_handle = CreateFile("CONOUT$", 
00741                              GENERIC_WRITE,
00742                              FILE_SHARE_WRITE,
00743                              /*lpSecurityAttributes=*/NULL,
00744                              OPEN_EXISTING,
00745                              FILE_ATTRIBUTE_NORMAL,
00746                              /*hTemplateFile=*/NULL);
00747       if (conout_handle == INVALID_HANDLE_VALUE)
00748        /* There is no console associated with this process.  Since
00749           the child is a console process, the OS would normally
00750           create a new console Window for the child.  Since we'll be
00751           redirecting the child's standard streams, we do not need
00752           the console window.  */ 
00753        dwCreationFlags = CREATE_NO_WINDOW;
00754       else 
00755        {
00756          /* There is a console associated with the process, so the OS
00757             will not create a new console.  And, if we use
00758             CREATE_NO_WINDOW in this situation, the child will have
00759             no associated console.  Therefore, if the child's
00760             standard streams are connected to the console, the output
00761             will be discarded.  */
00762          CloseHandle(conout_handle);
00763          dwCreationFlags = 0;
00764        }
00765     }
00766 
00767   /* Since the child will be a console process, it will, by default,
00768      connect standard input/output to its console.  However, we want
00769      the child to use the handles specifically designated above.  In
00770      addition, if there is no console (such as when we are running in
00771      a Cygwin X window), then we must redirect the child's
00772      input/output, as there is no console for the child to use.  */
00773   memset (&si, 0, sizeof (si));
00774   si.cb = sizeof (si);
00775   si.dwFlags = STARTF_USESTDHANDLES;
00776   si.hStdInput = stdin_handle;
00777   si.hStdOutput = stdout_handle;
00778   si.hStdError = stderr_handle;
00779 
00780   /* Create the child process.  */  
00781   pid = win32_spawn (executable, (flags & PEX_SEARCH) != 0,
00782                    argv, env, dwCreationFlags, &si, &pi);
00783   if (pid == -1)
00784     pid = spawn_script (executable, argv, env, dwCreationFlags,
00785                         &si, &pi);
00786   if (pid == -1)
00787     {
00788       *err = ENOENT;
00789       *errmsg = "CreateProcess";
00790     }
00791 
00792   /* Close the standard output and standard error handles in the
00793      parent.  */ 
00794   if (out != STDOUT_FILENO)
00795     obj->funcs->close (obj, out);
00796   if (errdes != STDERR_FILENO)
00797     obj->funcs->close (obj, errdes);
00798 
00799   return pid;
00800 }
00801 
00802 /* Wait for a child process to complete.  MS CRTDLL doesn't return
00803    enough information in status to decide if the child exited due to a
00804    signal or not, rather it simply returns an integer with the exit
00805    code of the child; eg., if the child exited with an abort() call
00806    and didn't have a handler for SIGABRT, it simply returns with
00807    status == 3.  We fix the status code to conform to the usual WIF*
00808    macros.  Note that WIFSIGNALED will never be true under CRTDLL. */
00809 
00810 static int
00811 pex_win32_wait (struct pex_obj *obj ATTRIBUTE_UNUSED, long pid,
00812               int *status, struct pex_time *time, int done ATTRIBUTE_UNUSED,
00813               const char **errmsg, int *err)
00814 {
00815   DWORD termstat;
00816   HANDLE h;
00817 
00818   if (time != NULL)
00819     memset (time, 0, sizeof *time);
00820 
00821   h = (HANDLE) pid;
00822 
00823   /* FIXME: If done is non-zero, we should probably try to kill the
00824      process.  */
00825   if (WaitForSingleObject (h, INFINITE) != WAIT_OBJECT_0)
00826     {
00827       CloseHandle (h);
00828       *err = ECHILD;
00829       *errmsg = "WaitForSingleObject";
00830       return -1;
00831     }
00832 
00833   GetExitCodeProcess (h, &termstat);
00834   CloseHandle (h);
00835  
00836   /* A value of 3 indicates that the child caught a signal, but not
00837      which one.  Since only SIGABRT, SIGFPE and SIGINT do anything, we
00838      report SIGABRT.  */
00839   if (termstat == 3)
00840     *status = SIGABRT;
00841   else
00842     *status = (termstat & 0xff) << 8;
00843 
00844   return 0;
00845 }
00846 
00847 /* Create a pipe.  */
00848 
00849 static int
00850 pex_win32_pipe (struct pex_obj *obj ATTRIBUTE_UNUSED, int *p,
00851               int binary)
00852 {
00853   return _pipe (p, 256, binary ? _O_BINARY : _O_TEXT);
00854 }
00855 
00856 /* Get a FILE pointer to read from a file descriptor.  */
00857 
00858 static FILE *
00859 pex_win32_fdopenr (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd,
00860                  int binary)
00861 {
00862   return fdopen (fd, binary ? "rb" : "r");
00863 }
00864 
00865 static FILE *
00866 pex_win32_fdopenw (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd,
00867                  int binary)
00868 {
00869   HANDLE h = (HANDLE) _get_osfhandle (fd);
00870   if (h == INVALID_HANDLE_VALUE)
00871     return NULL;
00872   if (! SetHandleInformation (h, HANDLE_FLAG_INHERIT, 0))
00873     return NULL;
00874   return fdopen (fd, binary ? "wb" : "w");
00875 }
00876 
00877 #ifdef MAIN
00878 #include <stdio.h>
00879 
00880 int
00881 main (int argc ATTRIBUTE_UNUSED, char **argv)
00882 {
00883   char const *errmsg;
00884   int err;
00885   argv++;
00886   printf ("%ld\n", pex_win32_exec_child (NULL, PEX_SEARCH, argv[0], argv, NULL, 0, 0, 1, 2, &errmsg, &err));
00887   exit (0);
00888 }
00889 #endif