(root)/
m4-1.4.19/
lib/
windows-spawn.c
       1  /* Auxiliary functions for the creation of subprocesses.  Native Windows API.
       2     Copyright (C) 2001, 2003-2021 Free Software Foundation, Inc.
       3     Written by Bruno Haible <bruno@clisp.org>, 2003.
       4  
       5     This program is free software: you can redistribute it and/or modify
       6     it under the terms of the GNU General Public License as published by
       7     the Free Software Foundation; either version 3 of the License, or
       8     (at your option) any later version.
       9  
      10     This program is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13     GNU General Public License for more details.
      14  
      15     You should have received a copy of the GNU General Public License
      16     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      17  
      18  #include <config.h>
      19  
      20  /* Specification.  */
      21  #include "windows-spawn.h"
      22  
      23  /* Get declarations of the native Windows API functions.  */
      24  #define WIN32_LEAN_AND_MEAN
      25  #include <windows.h>
      26  
      27  #include <stdbool.h>
      28  #include <stdio.h>
      29  #include <stdlib.h>
      30  #include <string.h>
      31  #include <errno.h>
      32  
      33  /* Get _get_osfhandle().  */
      34  #if GNULIB_MSVC_NOTHROW
      35  # include "msvc-nothrow.h"
      36  #else
      37  # include <io.h>
      38  #endif
      39  #include <process.h>
      40  
      41  #include "findprog.h"
      42  
      43  /* Don't assume that UNICODE is not defined.  */
      44  #undef STARTUPINFO
      45  #define STARTUPINFO STARTUPINFOA
      46  #undef CreateProcess
      47  #define CreateProcess CreateProcessA
      48  
      49  #define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037*?"
      50  #define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
      51  
      52  /* Returns the length of a quoted argument string.  */
      53  static size_t
      54  quoted_arg_length (const char *string)
      55  {
      56    bool quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL);
      57    size_t length;
      58    unsigned int backslashes;
      59    const char *s;
      60  
      61    length = 0;
      62    backslashes = 0;
      63    if (quote_around)
      64      length++;
      65    for (s = string; *s != '\0'; s++)
      66      {
      67        char c = *s;
      68        if (c == '"')
      69          length += backslashes + 1;
      70        length++;
      71        if (c == '\\')
      72          backslashes++;
      73        else
      74          backslashes = 0;
      75      }
      76    if (quote_around)
      77      length += backslashes + 1;
      78  
      79    return length;
      80  }
      81  
      82  /* Produces a quoted argument string.
      83     Stores exactly quoted_arg_length (STRING) + 1 bytes, including the final
      84     NUL byte, at MEM.
      85     Returns a pointer past the stored quoted argument string.  */
      86  static char *
      87  quoted_arg_string (const char *string, char *mem)
      88  {
      89    bool quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL);
      90    char *p;
      91    unsigned int backslashes;
      92    const char *s;
      93  
      94    p = mem;
      95    backslashes = 0;
      96    if (quote_around)
      97      *p++ = '"';
      98    for (s = string; *s != '\0'; s++)
      99      {
     100        char c = *s;
     101        if (c == '"')
     102          {
     103            unsigned int j;
     104            for (j = backslashes + 1; j > 0; j--)
     105              *p++ = '\\';
     106          }
     107        *p++ = c;
     108        if (c == '\\')
     109          backslashes++;
     110        else
     111          backslashes = 0;
     112      }
     113    if (quote_around)
     114      {
     115        unsigned int j;
     116        for (j = backslashes; j > 0; j--)
     117          *p++ = '\\';
     118        *p++ = '"';
     119      }
     120    *p++ = '\0';
     121  
     122    return p;
     123  }
     124  
     125  const char **
     126  prepare_spawn (const char * const *argv, char **mem_to_free)
     127  {
     128    size_t argc;
     129    const char **new_argv;
     130    size_t i;
     131  
     132    /* Count number of arguments.  */
     133    for (argc = 0; argv[argc] != NULL; argc++)
     134      ;
     135  
     136    /* Allocate new argument vector.  */
     137    new_argv = (const char **) malloc ((1 + argc + 1) * sizeof (const char *));
     138  
     139    /* Add an element upfront that can be used when argv[0] turns out to be a
     140       script, not a program.
     141       On Unix, this would be "/bin/sh". On native Windows, "sh" is actually
     142       "sh.exe".  We have to omit the directory part and rely on the search in
     143       PATH, because the mingw "mount points" are not visible inside Windows
     144       CreateProcess().  */
     145    new_argv[0] = "sh.exe";
     146  
     147    /* Put quoted arguments into the new argument vector.  */
     148    size_t needed_size = 0;
     149    for (i = 0; i < argc; i++)
     150      {
     151        const char *string = argv[i];
     152        size_t length;
     153  
     154        if (string[0] == '\0')
     155          length = strlen ("\"\"");
     156        else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL)
     157          length = quoted_arg_length (string);
     158        else
     159          length = strlen (string);
     160        needed_size += length + 1;
     161      }
     162  
     163    char *mem;
     164    if (needed_size == 0)
     165      mem = NULL;
     166    else
     167      {
     168        mem = (char *) malloc (needed_size);
     169        if (mem == NULL)
     170          {
     171            /* Memory allocation failure.  */
     172            free (new_argv);
     173            errno = ENOMEM;
     174            return NULL;
     175          }
     176      }
     177    *mem_to_free = mem;
     178  
     179    for (i = 0; i < argc; i++)
     180      {
     181        const char *string = argv[i];
     182  
     183        new_argv[1 + i] = mem;
     184        if (string[0] == '\0')
     185          {
     186            size_t length = strlen ("\"\"");
     187            memcpy (mem, "\"\"", length + 1);
     188            mem += length + 1;
     189          }
     190        else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL)
     191          {
     192            mem = quoted_arg_string (string, mem);
     193          }
     194        else
     195          {
     196            size_t length = strlen (string);
     197            memcpy (mem, string, length + 1);
     198            mem += length + 1;
     199          }
     200      }
     201    new_argv[1 + argc] = NULL;
     202  
     203    return new_argv;
     204  }
     205  
     206  char *
     207  compose_command (const char * const *argv)
     208  {
     209    /* Just concatenate the argv[] strings, separated by spaces.  */
     210    char *command;
     211  
     212    /* Determine the size of the needed block of memory.  */
     213    size_t total_size = 0;
     214    const char * const *ap;
     215    const char *p;
     216    for (ap = argv; (p = *ap) != NULL; ap++)
     217      total_size += strlen (p) + 1;
     218    size_t command_size = (total_size > 0 ? total_size : 1);
     219  
     220    /* Allocate the block of memory.  */
     221    command = (char *) malloc (command_size);
     222    if (command == NULL)
     223      {
     224        errno = ENOMEM;
     225        return NULL;
     226      }
     227  
     228    /* Fill it.  */
     229    if (total_size > 0)
     230      {
     231        char *cp = command;
     232        for (ap = argv; (p = *ap) != NULL; ap++)
     233          {
     234            size_t size = strlen (p) + 1;
     235            memcpy (cp, p, size - 1);
     236            cp += size;
     237            cp[-1] = ' ';
     238          }
     239        cp[-1] = '\0';
     240      }
     241    else
     242      *command = '\0';
     243  
     244    return command;
     245  }
     246  
     247  char *
     248  compose_envblock (const char * const *envp)
     249  {
     250    /* This is a bit hairy, because we don't have a lock that would prevent other
     251       threads from making modifications in ENVP.  So, just make sure we don't
     252       crash; but if other threads are making modifications, part of the result
     253       may be wrong.  */
     254   retry:
     255    {
     256      /* Guess the size of the needed block of memory.
     257         The guess will be exact if other threads don't make modifications.  */
     258      size_t total_size = 0;
     259      const char * const *ep;
     260      const char *p;
     261      for (ep = envp; (p = *ep) != NULL; ep++)
     262        total_size += strlen (p) + 1;
     263      size_t envblock_size = total_size;
     264  
     265      /* Allocate the block of memory.  */
     266      char *envblock = (char *) malloc (envblock_size + 1);
     267      if (envblock == NULL)
     268        {
     269          errno = ENOMEM;
     270          return NULL;
     271        }
     272      size_t envblock_used = 0;
     273      for (ep = envp; (p = *ep) != NULL; ep++)
     274        {
     275          size_t size = strlen (p) + 1;
     276          if (envblock_used + size > envblock_size)
     277            {
     278              /* Other threads did modifications.  Need more memory.  */
     279              envblock_size += envblock_size / 2;
     280              if (envblock_used + size > envblock_size)
     281                envblock_size = envblock_used + size;
     282  
     283              char *new_envblock = (char *) realloc (envblock, envblock_size + 1);
     284              if (new_envblock == NULL)
     285                {
     286                  free (envblock);
     287                  errno = ENOMEM;
     288                  return NULL;
     289                }
     290              envblock = new_envblock;
     291            }
     292          memcpy (envblock + envblock_used, p, size);
     293          envblock_used += size;
     294          if (envblock[envblock_used - 1] != '\0')
     295            {
     296              /* Other threads did modifications.  Restart.  */
     297              free (envblock);
     298              goto retry;
     299            }
     300        }
     301      envblock[envblock_used] = '\0';
     302      return envblock;
     303    }
     304  }
     305  
     306  int
     307  init_inheritable_handles (struct inheritable_handles *inh_handles,
     308                            bool duplicate)
     309  {
     310    /* Determine the minimal count of handles we need to care about.  */
     311    size_t handles_count;
     312    {
     313      /* _getmaxstdio
     314         <https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/getmaxstdio>
     315         Default value is 512.  */
     316      unsigned int fdmax = _getmaxstdio ();
     317      if (fdmax < 3)
     318        fdmax = 3;
     319      for (; fdmax > 3; fdmax--)
     320        {
     321          unsigned int fd = fdmax - 1;
     322          /* _get_osfhandle
     323             <https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/get-osfhandle>  */
     324          HANDLE handle = (HANDLE) _get_osfhandle (fd);
     325          if (handle != INVALID_HANDLE_VALUE)
     326            {
     327              DWORD hflags;
     328              /* GetHandleInformation
     329                 <https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-gethandleinformation>  */
     330              if (GetHandleInformation (handle, &hflags))
     331                {
     332                  if ((hflags & HANDLE_FLAG_INHERIT) != 0)
     333                    /* fd denotes an inheritable descriptor.  */
     334                    break;
     335                }
     336            }
     337        }
     338      handles_count = fdmax;
     339    }
     340    /* Note: handles_count >= 3.  */
     341  
     342    /* Allocate the arrays.  */
     343    size_t handles_allocated = handles_count;
     344    HANDLE *handles_array =
     345      (HANDLE *) malloc (handles_allocated * sizeof (HANDLE));
     346    if (handles_array == NULL)
     347      {
     348        errno = ENOMEM;
     349        return -1;
     350      }
     351    unsigned char *flags_array =
     352      (unsigned char *) malloc (handles_allocated * sizeof (unsigned char));
     353    if (flags_array == NULL)
     354      {
     355        free (handles_array);
     356        errno = ENOMEM;
     357        return -1;
     358      }
     359  
     360    /* Fill in the two arrays.  */
     361    {
     362      HANDLE curr_process = (duplicate ? GetCurrentProcess () : INVALID_HANDLE_VALUE);
     363      unsigned int fd;
     364      for (fd = 0; fd < handles_count; fd++)
     365        {
     366          handles_array[fd] = INVALID_HANDLE_VALUE;
     367          /* _get_osfhandle
     368             <https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/get-osfhandle>  */
     369          HANDLE handle = (HANDLE) _get_osfhandle (fd);
     370          if (handle != INVALID_HANDLE_VALUE)
     371            {
     372              DWORD hflags;
     373              /* GetHandleInformation
     374                 <https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-gethandleinformation>  */
     375              if (GetHandleInformation (handle, &hflags))
     376                {
     377                  if ((hflags & HANDLE_FLAG_INHERIT) != 0)
     378                    {
     379                      /* fd denotes an inheritable descriptor.  */
     380                      if (duplicate)
     381                        {
     382                          if (!DuplicateHandle (curr_process, handle,
     383                                                curr_process, &handles_array[fd],
     384                                                0, TRUE, DUPLICATE_SAME_ACCESS))
     385                            {
     386                              unsigned int i;
     387                              for (i = 0; i < fd; i++)
     388                                if (handles_array[i] != INVALID_HANDLE_VALUE)
     389                                  CloseHandle (handles_array[i]);
     390                              free (flags_array);
     391                              free (handles_array);
     392                              errno = EBADF; /* arbitrary */
     393                              return -1;
     394                            }
     395                        }
     396                      else
     397                        handles_array[fd] = handle;
     398  
     399                      flags_array[fd] = 0;
     400                    }
     401                }
     402            }
     403        }
     404    }
     405  
     406    /* Return the result.  */
     407    inh_handles->count = handles_count;
     408    inh_handles->allocated = handles_allocated;
     409    inh_handles->handles = handles_array;
     410    inh_handles->flags = flags_array;
     411    return 0;
     412  }
     413  
     414  int
     415  compose_handles_block (const struct inheritable_handles *inh_handles,
     416                         STARTUPINFO *sinfo)
     417  {
     418    /* STARTUPINFO
     419       <https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa>  */
     420    sinfo->dwFlags = STARTF_USESTDHANDLES;
     421    sinfo->hStdInput  = inh_handles->handles[0];
     422    sinfo->hStdOutput = inh_handles->handles[1];
     423    sinfo->hStdError  = inh_handles->handles[2];
     424  
     425    /* On newer versions of Windows, more file descriptors / handles than the
     426       first three can be passed.
     427       The format is as follows: Let N be an exclusive upper bound for the file
     428       descriptors to be passed. Two arrays are constructed in memory:
     429         - flags[0..N-1], of element type 'unsigned char',
     430         - handles[0..N-1], of element type 'HANDLE' or 'intptr_t'.
     431       For used entries, handles[i] is the handle, and flags[i] is a set of flags,
     432       a combination of:
     433          1 for open file descriptors,
     434         64 for handles of type FILE_TYPE_CHAR,
     435          8 for handles of type FILE_TYPE_PIPE,
     436         32 for O_APPEND.
     437       For unused entries - this may include any of the first three, since they
     438       are already passed above -, handles[i] is INVALID_HANDLE_VALUE and flags[i]
     439       is zero.
     440       lpReserved2 now is a pointer to the concatenation (without padding) of:
     441         - an 'unsigned int' whose value is N,
     442         - the contents of the flags[0..N-1] array,
     443         - the contents of the handles[0..N-1] array.
     444       cbReserved2 is the size (in bytes) of the object at lpReserved2.  */
     445  
     446    size_t handles_count = inh_handles->count;
     447  
     448    sinfo->cbReserved2 =
     449      sizeof (unsigned int)
     450      + handles_count * sizeof (unsigned char)
     451      + handles_count * sizeof (HANDLE);
     452    /* Add some padding, so that we can work with a properly aligned HANDLE
     453       array.  */
     454    char *hblock = (char *) malloc (sinfo->cbReserved2 + (sizeof (HANDLE) - 1));
     455    if (hblock == NULL)
     456      {
     457        errno = ENOMEM;
     458        return -1;
     459      }
     460    unsigned char *flags = (unsigned char *) (hblock + sizeof (unsigned int));
     461    char *handles = (char *) (flags + handles_count);
     462    HANDLE *handles_aligned =
     463      (HANDLE *) (((uintptr_t) handles + (sizeof (HANDLE) - 1))
     464                  & - (uintptr_t) sizeof (HANDLE));
     465  
     466    * (unsigned int *) hblock = handles_count;
     467    {
     468      unsigned int fd;
     469      for (fd = 0; fd < handles_count; fd++)
     470        {
     471          handles_aligned[fd] = INVALID_HANDLE_VALUE;
     472          flags[fd] = 0;
     473  
     474          HANDLE handle = inh_handles->handles[fd];
     475          if (handle != INVALID_HANDLE_VALUE
     476              /* The first three are possibly already passed above.
     477                 But they need to passed here as well, if they have some flags.  */
     478              && (fd >= 3 || inh_handles->flags[fd] != 0))
     479            {
     480              DWORD hflags;
     481              /* GetHandleInformation
     482                 <https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-gethandleinformation>  */
     483              if (GetHandleInformation (handle, &hflags))
     484                {
     485                  if ((hflags & HANDLE_FLAG_INHERIT) != 0)
     486                    {
     487                      /* fd denotes an inheritable descriptor.  */
     488                      handles_aligned[fd] = handle;
     489                      /* On Microsoft Windows, it would be sufficient to set
     490                         flags[fd] = 1.  But on ReactOS or Wine, adding the bit
     491                         that indicates the handle type may be necessary.  So,
     492                         just do it everywhere.  */
     493                      flags[fd] = 1 | inh_handles->flags[fd];
     494                      switch (GetFileType (handle))
     495                        {
     496                        case FILE_TYPE_CHAR:
     497                          flags[fd] |= 64;
     498                          break;
     499                        case FILE_TYPE_PIPE:
     500                          flags[fd] |= 8;
     501                          break;
     502                        default:
     503                          break;
     504                        }
     505                    }
     506                  else
     507                    /* We shouldn't have any non-inheritable handles in
     508                       inh_handles->handles.  */
     509                    abort ();
     510                }
     511            }
     512        }
     513    }
     514    if (handles != (char *) handles_aligned)
     515      memmove (handles, (char *) handles_aligned, handles_count * sizeof (HANDLE));
     516  
     517    sinfo->lpReserved2 = (BYTE *) hblock;
     518  
     519    return 0;
     520  }
     521  
     522  void
     523  free_inheritable_handles (struct inheritable_handles *inh_handles)
     524  {
     525    free (inh_handles->flags);
     526    free (inh_handles->handles);
     527  }
     528  
     529  int
     530  convert_CreateProcess_error (DWORD error)
     531  {
     532    /* Some of these errors probably cannot happen.  But who knows...  */
     533    switch (error)
     534      {
     535      case ERROR_FILE_NOT_FOUND:
     536      case ERROR_PATH_NOT_FOUND:
     537      case ERROR_BAD_PATHNAME:
     538      case ERROR_BAD_NET_NAME:
     539      case ERROR_INVALID_NAME:
     540      case ERROR_DIRECTORY:
     541        return ENOENT;
     542        break;
     543  
     544      case ERROR_ACCESS_DENIED:
     545      case ERROR_SHARING_VIOLATION:
     546        return EACCES;
     547        break;
     548  
     549      case ERROR_OUTOFMEMORY:
     550        return ENOMEM;
     551        break;
     552  
     553      case ERROR_BUFFER_OVERFLOW:
     554      case ERROR_FILENAME_EXCED_RANGE:
     555        return ENAMETOOLONG;
     556        break;
     557  
     558      case ERROR_BAD_FORMAT:
     559      case ERROR_BAD_EXE_FORMAT:
     560        return ENOEXEC;
     561        break;
     562  
     563      default:
     564        return EINVAL;
     565        break;
     566      }
     567  }
     568  
     569  intptr_t
     570  spawnpvech (int mode,
     571              const char *progname, const char * const *argv,
     572              const char * const *envp,
     573              const char *currdir,
     574              HANDLE stdin_handle, HANDLE stdout_handle, HANDLE stderr_handle)
     575  {
     576    /* Validate the arguments.  */
     577    if (!(mode == P_WAIT
     578          || mode == P_NOWAIT
     579          || mode == P_DETACH
     580          || mode == P_OVERLAY)
     581        || progname == NULL || argv == NULL)
     582      {
     583        errno = EINVAL;
     584        return -1;
     585      }
     586  
     587    /* Implement the 'p' letter: search for PROGNAME in getenv ("PATH").  */
     588    const char *resolved_progname =
     589      find_in_given_path (progname, getenv ("PATH"), NULL, false);
     590    if (resolved_progname == NULL)
     591      return -1;
     592  
     593    /* Compose the command.  */
     594    char *command = compose_command (argv);
     595    if (command == NULL)
     596      goto out_of_memory_1;
     597  
     598    /* Copy *ENVP into a contiguous block of memory.  */
     599    char *envblock;
     600    if (envp == NULL)
     601      envblock = NULL;
     602    else
     603      {
     604        envblock = compose_envblock (envp);
     605        if (envblock == NULL)
     606          goto out_of_memory_2;
     607      }
     608  
     609    /* Collect the inheritable handles.  */
     610    struct inheritable_handles inh_handles;
     611    if (init_inheritable_handles (&inh_handles, false) < 0)
     612      {
     613        int saved_errno = errno;
     614        if (envblock != NULL)
     615          free (envblock);
     616        free (command);
     617        if (resolved_progname != progname)
     618          free ((char *) resolved_progname);
     619        errno = saved_errno;
     620        return -1;
     621      }
     622    inh_handles.handles[0] = stdin_handle;  inh_handles.flags[0] = 0;
     623    inh_handles.handles[1] = stdout_handle; inh_handles.flags[1] = 0;
     624    inh_handles.handles[2] = stderr_handle; inh_handles.flags[2] = 0;
     625  
     626    /* CreateProcess
     627       <https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa>  */
     628    /* <https://docs.microsoft.com/en-us/windows/win32/procthread/process-creation-flags>  */
     629    DWORD process_creation_flags = (mode == P_DETACH ? DETACHED_PROCESS : 0);
     630    /* STARTUPINFO
     631       <https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa>  */
     632    STARTUPINFO sinfo;
     633    sinfo.cb = sizeof (STARTUPINFO);
     634    sinfo.lpReserved = NULL;
     635    sinfo.lpDesktop = NULL;
     636    sinfo.lpTitle = NULL;
     637    if (compose_handles_block (&inh_handles, &sinfo) < 0)
     638      {
     639        int saved_errno = errno;
     640        free_inheritable_handles (&inh_handles);
     641        if (envblock != NULL)
     642          free (envblock);
     643        free (command);
     644        if (resolved_progname != progname)
     645          free ((char *) resolved_progname);
     646        errno = saved_errno;
     647        return -1;
     648      }
     649  
     650    PROCESS_INFORMATION pinfo;
     651    if (!CreateProcess (resolved_progname, command, NULL, NULL, TRUE,
     652                        process_creation_flags, envblock, currdir, &sinfo,
     653                        &pinfo))
     654      {
     655        DWORD error = GetLastError ();
     656  
     657        free (sinfo.lpReserved2);
     658        free_inheritable_handles (&inh_handles);
     659        if (envblock != NULL)
     660          free (envblock);
     661        free (command);
     662        if (resolved_progname != progname)
     663          free ((char *) resolved_progname);
     664  
     665        errno = convert_CreateProcess_error (error);
     666        return -1;
     667      }
     668  
     669    if (pinfo.hThread)
     670      CloseHandle (pinfo.hThread);
     671    free (sinfo.lpReserved2);
     672    free_inheritable_handles (&inh_handles);
     673    if (envblock != NULL)
     674      free (envblock);
     675    free (command);
     676    if (resolved_progname != progname)
     677      free ((char *) resolved_progname);
     678  
     679    switch (mode)
     680      {
     681      case P_WAIT:
     682        {
     683          /* Wait until it terminates.  Then get its exit status code.  */
     684          switch (WaitForSingleObject (pinfo.hProcess, INFINITE))
     685            {
     686            case WAIT_OBJECT_0:
     687              break;
     688            case WAIT_FAILED:
     689              errno = ECHILD;
     690              return -1;
     691            default:
     692              abort ();
     693            }
     694  
     695          DWORD exit_code;
     696          if (!GetExitCodeProcess (pinfo.hProcess, &exit_code))
     697            {
     698              errno = ECHILD;
     699              return -1;
     700            }
     701          CloseHandle (pinfo.hProcess);
     702          return exit_code;
     703        }
     704  
     705      case P_NOWAIT:
     706        /* Return pinfo.hProcess, not pinfo.dwProcessId.  */
     707        return (intptr_t) pinfo.hProcess;
     708  
     709      case P_DETACH:
     710      case P_OVERLAY:
     711        CloseHandle (pinfo.hProcess);
     712        return 0;
     713  
     714      default:
     715        /* Already checked above.  */
     716        abort ();
     717      }
     718  
     719    /*NOTREACHED*/
     720   out_of_memory_2:
     721    free (command);
     722   out_of_memory_1:
     723    if (resolved_progname != progname)
     724      free ((char *) resolved_progname);
     725    errno = ENOMEM;
     726    return -1;
     727  }