]> code.delx.au - gnu-emacs/blobdiff - src/w32proc.c
Fix removal of variables from process-environment
[gnu-emacs] / src / w32proc.c
index c571726d70f21676629e94b3f4f3200d70b3e2ce..4a6f7862801777aafff8dbb5eb611daa1f26cd98 100644 (file)
@@ -1,13 +1,13 @@
 /* Process support for GNU Emacs on the Microsoft Windows API.
 
 /* Process support for GNU Emacs on the Microsoft Windows API.
 
-Copyright (C) 1992, 1995, 1999-2014 Free Software Foundation, Inc.
+Copyright (C) 1992, 1995, 1999-2016 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
 GNU Emacs is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 
 This file is part of GNU Emacs.
 
 GNU Emacs is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
 
 GNU Emacs is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 GNU Emacs is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -29,6 +29,7 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include <ctype.h>
 #include <io.h>
 #include <fcntl.h>
 #include <ctype.h>
 #include <io.h>
 #include <fcntl.h>
+#include <unistd.h>
 #include <signal.h>
 #include <sys/file.h>
 #include <mbstring.h>
 #include <signal.h>
 #include <sys/file.h>
 #include <mbstring.h>
@@ -59,12 +60,9 @@ extern BOOL WINAPI IsValidLocale (LCID, DWORD);
 #include "w32.h"
 #include "w32common.h"
 #include "w32heap.h"
 #include "w32.h"
 #include "w32common.h"
 #include "w32heap.h"
-#include "systime.h"
-#include "syswait.h"
-#include "process.h"
+#include "syswait.h"   /* for WNOHANG */
 #include "syssignal.h"
 #include "w32term.h"
 #include "syssignal.h"
 #include "w32term.h"
-#include "dispextern.h"                /* for xstrcasecmp */
 #include "coding.h"
 
 #define RVA_TO_PTR(var,section,filedata) \
 #include "coding.h"
 
 #define RVA_TO_PTR(var,section,filedata) \
@@ -72,8 +70,6 @@ extern BOOL WINAPI IsValidLocale (LCID, DWORD);
            + ((DWORD_PTR)(var) - (section)->VirtualAddress)            \
            + (filedata).file_base))
 
            + ((DWORD_PTR)(var) - (section)->VirtualAddress)            \
            + (filedata).file_base))
 
-Lisp_Object Qhigh, Qlow;
-
 /* Signal handlers...SIG_DFL == 0 so this is initialized correctly.  */
 static signal_handler sig_handlers[NSIG];
 
 /* Signal handlers...SIG_DFL == 0 so this is initialized correctly.  */
 static signal_handler sig_handlers[NSIG];
 
@@ -788,6 +784,138 @@ alarm (int seconds)
 #endif
 }
 
 #endif
 }
 
+\f
+
+/* Here's an overview of how support for subprocesses and
+   network/serial streams is implemented on MS-Windows.
+
+   The management of both subprocesses and network/serial streams
+   circles around the child_procs[] array, which can record up to the
+   grand total of MAX_CHILDREN (= 32) of these.  (The reasons for the
+   32 limitation will become clear below.)  Each member of
+   child_procs[] is a child_process structure, defined on w32.h.
+
+   A related data structure is the fd_info[] array, which holds twice
+   as many members, 64, and records the information about file
+   descriptors used for communicating with subprocesses and
+   network/serial devices.  Each member of the array is the filedesc
+   structure, which records the Windows handle for communications,
+   such as the read end of the pipe to a subprocess, a socket handle,
+   etc.
+
+   Both these arrays reference each other: there's a member of
+   child_process structure that records the corresponding file
+   descriptor, and there's a member of filedesc structure that holds a
+   pointer to the corresponding child_process.
+
+   Whenever Emacs starts a subprocess or opens a network/serial
+   stream, the function new_child is called to prepare a new
+   child_process structure.  new_child looks for the first vacant slot
+   in the child_procs[] array, initializes it, and starts a "reader
+   thread" that will watch the output of the subprocess/stream and its
+   status.  (If no vacant slot can be found, new_child returns a
+   failure indication to its caller, and the higher-level Emacs
+   primitive that called it will then fail with EMFILE or EAGAIN.)
+
+   The reader thread started by new_child communicates with the main
+   (a.k.a. "Lisp") thread via two event objects and a status, all of
+   them recorded by the members of the child_process structure in
+   child_procs[].  The event objects serve as semaphores between the
+   reader thread and the 'pselect' emulation in sys_select, as follows:
+
+     . Initially, the reader thread is waiting for the char_consumed
+       event to become signaled by sys_select, which is an indication
+       for the reader thread to go ahead and try reading more stuff
+       from the subprocess/stream.
+
+     . The reader thread then attempts to read by calling a
+       blocking-read function.  When the read call returns, either
+       successfully or with some failure indication, the reader thread
+       updates the status of the read accordingly, and signals the 2nd
+       event object, char_avail, on whose handle sys_select is
+       waiting.  This tells sys_select that the file descriptor
+       allocated for the subprocess or the the stream is ready to be
+       read from.
+
+   When the subprocess exits or the network/serial stream is closed,
+   the reader thread sets the status accordingly and exits.  It also
+   exits when the main thread sets the status to STATUS_READ_ERROR
+   and/or the char_avail and char_consumed event handles become NULL;
+   this is how delete_child, called by Emacs when a subprocess or a
+   stream is terminated, terminates the reader thread as part of
+   deleting the child_process object.
+
+   The sys_select function emulates the Posix 'pselect' function; it
+   is needed because the Windows 'select' function supports only
+   network sockets, while Emacs expects 'pselect' to work for any file
+   descriptor, including pipes and serial streams.
+
+   When sys_select is called, it uses the information in fd_info[]
+   array to convert the file descriptors which it was asked to watch
+   into Windows handles.  In general, the handle to watch is the
+   handle of the char_avail event of the child_process structure that
+   corresponds to the file descriptor.  In addition, for subprocesses,
+   sys_select watches one more handle: the handle for the subprocess,
+   so that it could emulate the SIGCHLD signal when the subprocess
+   exits.
+
+   If file descriptor zero (stdin) doesn't have its bit set in the
+   'rfds' argument to sys_select, the function always watches for
+   keyboard interrupts, to be able to interrupt the wait and return
+   when the user presses C-g.
+
+   Having collected the handles to watch, sys_select calls
+   WaitForMultipleObjects to wait for any one of them to become
+   signaled.  Since WaitForMultipleObjects can only watch up to 64
+   handles, Emacs on Windows is limited to maximum 32 child_process
+   objects (since a subprocess consumes 2 handles to be watched, see
+   above).
+
+   When any of the handles become signaled, sys_select does whatever
+   is appropriate for the corresponding child_process object:
+
+     . If it's a handle to the char_avail event, sys_select marks the
+       corresponding bit in 'rfds', and Emacs will then read from that
+       file descriptor.
+
+     . If it's a handle to the process, sys_select calls the SIGCHLD
+       handler, to inform Emacs of the fact that the subprocess
+       exited.
+
+   The waitpid emulation works very similar to sys_select, except that
+   it only watches handles of subprocesses, and doesn't synchronize
+   with the reader thread.
+
+   Because socket descriptors on Windows are handles, while Emacs
+   expects them to be file descriptors, all low-level I/O functions,
+   such as 'read' and 'write', and all socket operations, like
+   'connect', 'recvfrom', 'accept', etc., are redirected to the
+   corresponding 'sys_*' functions, which must convert a file
+   descriptor to a handle using the fd_info[] array, and then invoke
+   the corresponding Windows API on the handle.  Most of these
+   redirected 'sys_*' functions are implemented on w32.c.
+
+   When the file descriptor was produced by functions such as 'open',
+   the corresponding handle is obtained by calling _get_osfhandle.  To
+   produce a file descriptor for a socket handle, which has no file
+   descriptor as far as Windows is concerned, the function
+   socket_to_fd opens the null device; the resulting file descriptor
+   will never be used directly in any I/O API, but serves as an index
+   into the fd_info[] array, where the socket handle is stored.  The
+   SOCK_HANDLE macro retrieves the handle when given the file
+   descriptor.
+
+   The function sys_kill emulates the Posix 'kill' functionality to
+   terminate other processes.  It does that by attaching to the
+   foreground window of the process and sending a Ctrl-C or Ctrl-BREAK
+   signal to the process; if that doesn't work, then it calls
+   TerminateProcess to forcibly terminate the process.  Note that this
+   only terminates the immediate process whose PID was passed to
+   sys_kill; it doesn't terminate the child processes of that process.
+   This means, for example, that an Emacs subprocess run through a
+   shell might not be killed, because sys_kill will only terminate the
+   shell.  (In practice, however, such problems are very rare.)  */
+
 /* Defined in <process.h> which conflicts with the local copy */
 #define _P_NOWAIT 1
 
 /* Defined in <process.h> which conflicts with the local copy */
 #define _P_NOWAIT 1
 
@@ -1013,7 +1141,9 @@ reader_thread (void *arg)
     {
       int rc;
 
     {
       int rc;
 
-      if (cp->fd >= 0 && fd_info[cp->fd].flags & FILE_LISTEN)
+      if (cp->fd >= 0 && (fd_info[cp->fd].flags & FILE_CONNECT) != 0)
+       rc = _sys_wait_connect (cp->fd);
+      else if (cp->fd >= 0 && (fd_info[cp->fd].flags & FILE_LISTEN) != 0)
        rc = _sys_wait_accept (cp->fd);
       else
        rc = _sys_read_ahead (cp->fd);
        rc = _sys_wait_accept (cp->fd);
       else
        rc = _sys_read_ahead (cp->fd);
@@ -1033,8 +1163,8 @@ reader_thread (void *arg)
          return 1;
        }
 
          return 1;
        }
 
-      if (rc == STATUS_READ_ERROR)
-       return 1;
+      if (rc == STATUS_READ_ERROR || rc == STATUS_CONNECT_FAILED)
+       return 2;
 
       /* If the read died, the child has died so let the thread die */
       if (rc == STATUS_READ_FAILED)
 
       /* If the read died, the child has died so let the thread die */
       if (rc == STATUS_READ_FAILED)
@@ -1395,22 +1525,25 @@ waitpid (pid_t pid, int *status, int options)
 
 /* Implementation note: This function works with file names encoded in
    the current ANSI codepage.  */
 
 /* Implementation note: This function works with file names encoded in
    the current ANSI codepage.  */
-static void
+static int
 w32_executable_type (char * filename,
                     int * is_dos_app,
                     int * is_cygnus_app,
 w32_executable_type (char * filename,
                     int * is_dos_app,
                     int * is_cygnus_app,
+                    int * is_msys_app,
                     int * is_gui_app)
 {
   file_data executable;
   char * p;
                     int * is_gui_app)
 {
   file_data executable;
   char * p;
+  int retval = 0;
 
   /* Default values in case we can't tell for sure.  */
   *is_dos_app = FALSE;
   *is_cygnus_app = FALSE;
 
   /* Default values in case we can't tell for sure.  */
   *is_dos_app = FALSE;
   *is_cygnus_app = FALSE;
+  *is_msys_app = FALSE;
   *is_gui_app = FALSE;
 
   if (!open_input_file (&executable, filename))
   *is_gui_app = FALSE;
 
   if (!open_input_file (&executable, filename))
-    return;
+    return -1;
 
   p = strrchr (filename, '.');
 
 
   p = strrchr (filename, '.');
 
@@ -1428,7 +1561,8 @@ w32_executable_type (char * filename,
         extension, which is defined in the registry.  */
       p = egetenv ("COMSPEC");
       if (p)
         extension, which is defined in the registry.  */
       p = egetenv ("COMSPEC");
       if (p)
-       w32_executable_type (p, is_dos_app, is_cygnus_app, is_gui_app);
+       retval = w32_executable_type (p, is_dos_app, is_cygnus_app, is_msys_app,
+                                     is_gui_app);
     }
   else
     {
     }
   else
     {
@@ -1481,29 +1615,40 @@ w32_executable_type (char * filename,
 #endif
           if (data_dir)
             {
 #endif
           if (data_dir)
             {
-              /* Look for cygwin.dll in DLL import list. */
+              /* Look for Cygwin DLL in the DLL import list. */
               IMAGE_DATA_DIRECTORY import_dir =
                 data_dir[IMAGE_DIRECTORY_ENTRY_IMPORT];
               IMAGE_DATA_DIRECTORY import_dir =
                 data_dir[IMAGE_DIRECTORY_ENTRY_IMPORT];
-              IMAGE_IMPORT_DESCRIPTOR * imports;
-              IMAGE_SECTION_HEADER * section;
-
-              section = rva_to_section (import_dir.VirtualAddress, nt_header);
-              imports = RVA_TO_PTR (import_dir.VirtualAddress, section,
-                                    executable);
+              IMAGE_IMPORT_DESCRIPTOR * imports =
+               RVA_TO_PTR (import_dir.VirtualAddress,
+                           rva_to_section (import_dir.VirtualAddress,
+                                           nt_header),
+                           executable);
 
               for ( ; imports->Name; imports++)
                 {
 
               for ( ; imports->Name; imports++)
                 {
+                 IMAGE_SECTION_HEADER * section =
+                   rva_to_section (imports->Name, nt_header);
                   char * dllname = RVA_TO_PTR (imports->Name, section,
                                                executable);
 
                   char * dllname = RVA_TO_PTR (imports->Name, section,
                                                executable);
 
-                  /* The exact name of the cygwin dll has changed with
-                     various releases, but hopefully this will be reasonably
-                     future proof.  */
+                  /* The exact name of the Cygwin DLL has changed with
+                     various releases, but hopefully this will be
+                     reasonably future-proof.  */
                   if (strncmp (dllname, "cygwin", 6) == 0)
                     {
                       *is_cygnus_app = TRUE;
                       break;
                     }
                   if (strncmp (dllname, "cygwin", 6) == 0)
                     {
                       *is_cygnus_app = TRUE;
                       break;
                     }
+                 else if (strncmp (dllname, "msys-", 5) == 0)
+                   {
+                     /* This catches both MSYS 1.x and MSYS2
+                        executables (the DLL name is msys-1.0.dll and
+                        msys-2.0.dll, respectively).  There doesn't
+                        seem to be a reason to distinguish between
+                        the two, for now.  */
+                     *is_msys_app = TRUE;
+                     break;
+                   }
                 }
             }
        }
                 }
             }
        }
@@ -1511,6 +1656,7 @@ w32_executable_type (char * filename,
 
 unwind:
   close_file_data (&executable);
 
 unwind:
   close_file_data (&executable);
+  return retval;
 }
 
 static int
 }
 
 static int
@@ -1569,7 +1715,7 @@ sys_spawnve (int mode, char *cmdname, char **argv, char **envp)
   int arglen, numenv;
   pid_t pid;
   child_process *cp;
   int arglen, numenv;
   pid_t pid;
   child_process *cp;
-  int is_dos_app, is_cygnus_app, is_gui_app;
+  int is_dos_app, is_cygnus_app, is_msys_app, is_gui_app;
   int do_quoting = 0;
   /* We pass our process ID to our children by setting up an environment
      variable in their environment.  */
   int do_quoting = 0;
   /* We pass our process ID to our children by setting up an environment
      variable in their environment.  */
@@ -1580,10 +1726,10 @@ sys_spawnve (int mode, char *cmdname, char **argv, char **envp)
      argument being split into two or more. Arguments with wildcards
      are also quoted, for consistency with posix platforms, where wildcards
      are not expanded if we run the program directly without a shell.
      argument being split into two or more. Arguments with wildcards
      are also quoted, for consistency with posix platforms, where wildcards
      are not expanded if we run the program directly without a shell.
-     Some extra whitespace characters need quoting in Cygwin programs,
+     Some extra whitespace characters need quoting in Cygwin/MSYS programs,
      so this list is conditionally modified below.  */
   char *sepchars = " \t*?";
      so this list is conditionally modified below.  */
   char *sepchars = " \t*?";
-  /* This is for native w32 apps; modified below for Cygwin apps.  */
+  /* This is for native w32 apps; modified below for Cygwin/MSUS apps.  */
   char escape_char = '\\';
   char cmdname_a[MAX_PATH];
 
   char escape_char = '\\';
   char cmdname_a[MAX_PATH];
 
@@ -1600,20 +1746,16 @@ sys_spawnve (int mode, char *cmdname, char **argv, char **envp)
      absolute.  So we double-check this here, just in case.  */
   if (faccessat (AT_FDCWD, cmdname, X_OK, AT_EACCESS) != 0)
     {
      absolute.  So we double-check this here, just in case.  */
   if (faccessat (AT_FDCWD, cmdname, X_OK, AT_EACCESS) != 0)
     {
-      struct gcpro gcpro1;
-
       program = build_string (cmdname);
       full = Qnil;
       program = build_string (cmdname);
       full = Qnil;
-      GCPRO1 (program);
       openp (Vexec_path, program, Vexec_suffixes, &full, make_number (X_OK), 0);
       openp (Vexec_path, program, Vexec_suffixes, &full, make_number (X_OK), 0);
-      UNGCPRO;
       if (NILP (full))
        {
          errno = EINVAL;
          return -1;
        }
       program = ENCODE_FILE (full);
       if (NILP (full))
        {
          errno = EINVAL;
          return -1;
        }
       program = ENCODE_FILE (full);
-      cmdname = SDATA (program);
+      cmdname = SSDATA (program);
     }
   else
     {
     }
   else
     {
@@ -1635,7 +1777,7 @@ sys_spawnve (int mode, char *cmdname, char **argv, char **envp)
   /* We explicitly require that the command's file name be encodable
      in the current ANSI codepage, because we will be invoking it via
      the ANSI APIs.  */
   /* We explicitly require that the command's file name be encodable
      in the current ANSI codepage, because we will be invoking it via
      the ANSI APIs.  */
-  if (_mbspbrk (cmdname_a, "?"))
+  if (_mbspbrk ((unsigned char *)cmdname_a, (const unsigned char *)"?"))
     {
       errno = ENOENT;
       return -1;
     {
       errno = ENOENT;
       return -1;
@@ -1644,15 +1786,17 @@ sys_spawnve (int mode, char *cmdname, char **argv, char **envp)
   cmdname = cmdname_a;
   argv[0] = cmdname;
 
   cmdname = cmdname_a;
   argv[0] = cmdname;
 
-  /* Determine whether program is a 16-bit DOS executable, or a 32-bit Windows
-     executable that is implicitly linked to the Cygnus dll (implying it
-     was compiled with the Cygnus GNU toolchain and hence relies on
-     cygwin.dll to parse the command line - we use this to decide how to
-     escape quote chars in command line args that must be quoted).
+  /* Determine whether program is a 16-bit DOS executable, or a 32-bit
+     Windows executable that is implicitly linked to the Cygnus or
+     MSYS dll (implying it was compiled with the Cygnus/MSYS GNU
+     toolchain and hence relies on cygwin.dll or MSYS DLL to parse the
+     command line - we use this to decide how to escape quote chars in
+     command line args that must be quoted).
 
      Also determine whether it is a GUI app, so that we don't hide its
      initial window unless specifically requested.  */
 
      Also determine whether it is a GUI app, so that we don't hide its
      initial window unless specifically requested.  */
-  w32_executable_type (cmdname, &is_dos_app, &is_cygnus_app, &is_gui_app);
+  w32_executable_type (cmdname, &is_dos_app, &is_cygnus_app, &is_msys_app,
+                      &is_gui_app);
 
   /* On Windows 95, if cmdname is a DOS app, we invoke a helper
      application to start it by specifying the helper app as cmdname,
 
   /* On Windows 95, if cmdname is a DOS app, we invoke a helper
      application to start it by specifying the helper app as cmdname,
@@ -1663,9 +1807,27 @@ sys_spawnve (int mode, char *cmdname, char **argv, char **envp)
 
       cmdname = alloca (MAX_PATH);
       if (egetenv ("CMDPROXY"))
 
       cmdname = alloca (MAX_PATH);
       if (egetenv ("CMDPROXY"))
-       strcpy (cmdname, egetenv ("CMDPROXY"));
+       {
+         /* Implementation note: since process-environment, where
+            'egetenv' looks, is encoded in the system codepage, we
+            don't need to encode the cmdproxy file name if we get it
+            from the environment.  */
+         strcpy (cmdname, egetenv ("CMDPROXY"));
+       }
       else
       else
-       strcpy (lispstpcpy (cmdname, Vinvocation_directory), "cmdproxy.exe");
+       {
+         char *q = lispstpcpy (cmdname,
+                               /* exec-directory needs to be encoded.  */
+                               ansi_encode_filename (Vexec_directory));
+         /* If we are run from the source tree, use cmdproxy.exe from
+            the same source tree.  */
+         for (p = q - 2; p > cmdname; p = CharPrevA (cmdname, p))
+           if (*p == '/')
+             break;
+         if (*p == '/' && xstrcasecmp (p, "/lib-src/") == 0)
+           q = stpcpy (p, "/nt/");
+         strcpy (q, "cmdproxy.exe");
+       }
 
       /* Can't use unixtodos_filename here, since that needs its file
         name argument encoded in UTF-8.  */
 
       /* Can't use unixtodos_filename here, since that needs its file
         name argument encoded in UTF-8.  */
@@ -1712,10 +1874,10 @@ sys_spawnve (int mode, char *cmdname, char **argv, char **envp)
       if (INTEGERP (Vw32_quote_process_args))
        escape_char = XINT (Vw32_quote_process_args);
       else
       if (INTEGERP (Vw32_quote_process_args))
        escape_char = XINT (Vw32_quote_process_args);
       else
-       escape_char = is_cygnus_app ? '"' : '\\';
+       escape_char = (is_cygnus_app || is_msys_app) ? '"' : '\\';
     }
 
     }
 
-  /* Cygwin apps needs quoting a bit more often.  */
+  /* Cygwin/MSYS apps need quoting a bit more often.  */
   if (escape_char == '"')
     sepchars = "\r\n\t\f '";
 
   if (escape_char == '"')
     sepchars = "\r\n\t\f '";
 
@@ -1733,7 +1895,7 @@ sys_spawnve (int mode, char *cmdname, char **argv, char **envp)
       for ( ; *p; p++)
        {
          if (escape_char == '"' && *p == '\\')
       for ( ; *p; p++)
        {
          if (escape_char == '"' && *p == '\\')
-           /* If it's a Cygwin app, \ needs to be escaped.  */
+           /* If it's a Cygwin/MSYS app, \ needs to be escaped.  */
            arglen++;
          else if (*p == '"')
            {
            arglen++;
          else if (*p == '"')
            {
@@ -1900,10 +2062,11 @@ sys_spawnve (int mode, char *cmdname, char **argv, char **envp)
   return pid;
 }
 
   return pid;
 }
 
-/* Emulate the select call
+/* Emulate the select call.
    Wait for available input on any of the given rfds, or timeout if
    Wait for available input on any of the given rfds, or timeout if
-   a timeout is given and no input is detected
-   wfds and efds are not supported and must be NULL.
+   a timeout is given and no input is detected.  wfds are supported
+   only for asynchronous 'connect' calls.  efds are not supported
+   and must be NULL.
 
    For simplicity, we detect the death of child processes here and
    synchronously call the SIGCHLD handler.  Since it is possible for
 
    For simplicity, we detect the death of child processes here and
    synchronously call the SIGCHLD handler.  Since it is possible for
@@ -1931,7 +2094,7 @@ int
 sys_select (int nfds, SELECT_TYPE *rfds, SELECT_TYPE *wfds, SELECT_TYPE *efds,
            struct timespec *timeout, void *ignored)
 {
 sys_select (int nfds, SELECT_TYPE *rfds, SELECT_TYPE *wfds, SELECT_TYPE *efds,
            struct timespec *timeout, void *ignored)
 {
-  SELECT_TYPE orfds;
+  SELECT_TYPE orfds, owfds;
   DWORD timeout_ms, start_time;
   int i, nh, nc, nr;
   DWORD active;
   DWORD timeout_ms, start_time;
   int i, nh, nc, nr;
   DWORD active;
@@ -1949,15 +2112,27 @@ sys_select (int nfds, SELECT_TYPE *rfds, SELECT_TYPE *wfds, SELECT_TYPE *efds,
       return 0;
     }
 
       return 0;
     }
 
-  /* Otherwise, we only handle rfds, so fail otherwise.  */
-  if (rfds == NULL || wfds != NULL || efds != NULL)
+  /* Otherwise, we only handle rfds and wfds, so fail otherwise.  */
+  if ((rfds == NULL && wfds == NULL) || efds != NULL)
     {
       errno = EINVAL;
       return -1;
     }
 
     {
       errno = EINVAL;
       return -1;
     }
 
-  orfds = *rfds;
-  FD_ZERO (rfds);
+  if (rfds)
+    {
+      orfds = *rfds;
+      FD_ZERO (rfds);
+    }
+  else
+    FD_ZERO (&orfds);
+  if (wfds)
+    {
+      owfds = *wfds;
+      FD_ZERO (wfds);
+    }
+  else
+    FD_ZERO (&owfds);
   nr = 0;
 
   /* If interrupt_handle is available and valid, always wait on it, to
   nr = 0;
 
   /* If interrupt_handle is available and valid, always wait on it, to
@@ -1972,7 +2147,7 @@ sys_select (int nfds, SELECT_TYPE *rfds, SELECT_TYPE *wfds, SELECT_TYPE *efds,
 
   /* Build a list of pipe handles to wait on.  */
   for (i = 0; i < nfds; i++)
 
   /* Build a list of pipe handles to wait on.  */
   for (i = 0; i < nfds; i++)
-    if (FD_ISSET (i, &orfds))
+    if (FD_ISSET (i, &orfds) || FD_ISSET (i, &owfds))
       {
        if (i == 0)
          {
       {
        if (i == 0)
          {
@@ -1986,7 +2161,7 @@ sys_select (int nfds, SELECT_TYPE *rfds, SELECT_TYPE *wfds, SELECT_TYPE *efds,
 
            /* Check for any emacs-generated input in the queue since
               it won't be detected in the wait */
 
            /* Check for any emacs-generated input in the queue since
               it won't be detected in the wait */
-           if (detect_input_pending ())
+           if (rfds && detect_input_pending ())
              {
                FD_SET (i, rfds);
                return 1;
              {
                FD_SET (i, rfds);
                return 1;
@@ -2001,6 +2176,13 @@ sys_select (int nfds, SELECT_TYPE *rfds, SELECT_TYPE *wfds, SELECT_TYPE *efds,
          {
            /* Child process and socket/comm port input.  */
            cp = fd_info[i].cp;
          {
            /* Child process and socket/comm port input.  */
            cp = fd_info[i].cp;
+           if (FD_ISSET (i, &owfds)
+               && cp
+               && (fd_info[i].flags & FILE_CONNECT) == 0)
+             {
+               DebPrint (("sys_select: fd %d is in wfds, but FILE_CONNECT is reset!\n", i));
+               cp = NULL;
+             }
            if (cp)
              {
                int current_status = cp->status;
            if (cp)
              {
                int current_status = cp->status;
@@ -2009,6 +2191,8 @@ sys_select (int nfds, SELECT_TYPE *rfds, SELECT_TYPE *wfds, SELECT_TYPE *efds,
                  {
                    /* Tell reader thread which file handle to use. */
                    cp->fd = i;
                  {
                    /* Tell reader thread which file handle to use. */
                    cp->fd = i;
+                   /* Zero out the error code.  */
+                   cp->errcode = 0;
                    /* Wake up the reader thread for this process */
                    cp->status = STATUS_READ_READY;
                    if (!SetEvent (cp->char_consumed))
                    /* Wake up the reader thread for this process */
                    cp->status = STATUS_READ_READY;
                    if (!SetEvent (cp->char_consumed))
@@ -2199,7 +2383,7 @@ count_children:
 
          if (cp->fd >= 0 && (fd_info[cp->fd].flags & FILE_AT_EOF) == 0)
            fd_info[cp->fd].flags |= FILE_SEND_SIGCHLD;
 
          if (cp->fd >= 0 && (fd_info[cp->fd].flags & FILE_AT_EOF) == 0)
            fd_info[cp->fd].flags |= FILE_SEND_SIGCHLD;
-         /* SIG_DFL for SIGCHLD is ignore */
+         /* SIG_DFL for SIGCHLD is ignored */
          else if (sig_handlers[SIGCHLD] != SIG_DFL &&
                   sig_handlers[SIGCHLD] != SIG_IGN)
            {
          else if (sig_handlers[SIGCHLD] != SIG_DFL &&
                   sig_handlers[SIGCHLD] != SIG_IGN)
            {
@@ -2216,7 +2400,7 @@ count_children:
          errno = EINTR;
          return -1;
        }
          errno = EINTR;
          return -1;
        }
-      else if (fdindex[active] == 0)
+      else if (rfds && fdindex[active] == 0)
        {
          /* Keyboard input available */
          FD_SET (0, rfds);
        {
          /* Keyboard input available */
          FD_SET (0, rfds);
@@ -2224,9 +2408,33 @@ count_children:
        }
       else
        {
        }
       else
        {
-         /* must be a socket or pipe - read ahead should have
-             completed, either succeeding or failing.  */
-         FD_SET (fdindex[active], rfds);
+         /* Must be a socket or pipe - read ahead should have
+             completed, either succeeding or failing.  If this handle
+             was waiting for an async 'connect', reset the connect
+             flag, so it could read from now on.  */
+         if (wfds && (fd_info[fdindex[active]].flags & FILE_CONNECT) != 0)
+           {
+             cp = fd_info[fdindex[active]].cp;
+             if (cp)
+               {
+                 /* Don't reset the FILE_CONNECT bit and don't
+                    acknowledge the read if the status is
+                    STATUS_CONNECT_FAILED or some other
+                    failure. That's because the thread exits in those
+                    cases, so it doesn't need the ACK, and we want to
+                    keep the FILE_CONNECT bit as evidence that the
+                    connect failed, to be checked in sys_read.  */
+                 if (cp->status == STATUS_READ_SUCCEEDED)
+                   {
+                     fd_info[cp->fd].flags &= ~FILE_CONNECT;
+                     cp->status = STATUS_READ_ACKNOWLEDGED;
+                   }
+                 ResetEvent (cp->char_avail);
+               }
+             FD_SET (fdindex[active], wfds);
+           }
+         else if (rfds)
+           FD_SET (fdindex[active], rfds);
          nr++;
        }
 
          nr++;
        }
 
@@ -2672,7 +2880,7 @@ All path elements in FILENAME are converted to their short names.  */)
   filename = Fexpand_file_name (filename, Qnil);
 
   /* luckily, this returns the short version of each element in the path.  */
   filename = Fexpand_file_name (filename, Qnil);
 
   /* luckily, this returns the short version of each element in the path.  */
-  if (w32_get_short_filename (SDATA (ENCODE_FILE (filename)),
+  if (w32_get_short_filename (SSDATA (ENCODE_FILE (filename)),
                              shortname, MAX_PATH) == 0)
     return Qnil;
 
                              shortname, MAX_PATH) == 0)
     return Qnil;
 
@@ -2702,7 +2910,7 @@ All path elements in FILENAME are converted to their long names.  */)
   /* first expand it.  */
   filename = Fexpand_file_name (filename, Qnil);
 
   /* first expand it.  */
   filename = Fexpand_file_name (filename, Qnil);
 
-  if (!w32_get_long_filename (SDATA (ENCODE_FILE (filename)), longname,
+  if (!w32_get_long_filename (SSDATA (ENCODE_FILE (filename)), longname,
                              MAX_UTF8_PATH))
     return Qnil;
 
                              MAX_UTF8_PATH))
     return Qnil;
 
@@ -2769,6 +2977,59 @@ If successful, the return value is t, otherwise nil.  */)
   return result;
 }
 
   return result;
 }
 
+DEFUN ("w32-application-type", Fw32_application_type,
+       Sw32_application_type, 1, 1, 0,
+       doc: /* Return the type of an MS-Windows PROGRAM.
+
+Knowing the type of an executable could be useful for formatting
+file names passed to it or for quoting its command-line arguments.
+
+PROGRAM should specify an executable file, including the extension.
+
+The value is one of the following:
+
+`dos'        -- a DOS .com program or some other non-PE executable
+`cygwin'     -- a Cygwin program that depends on Cygwin DLL
+`msys'       -- an MSYS 1.x or MSYS2 program
+`w32-native' -- a native Windows application
+`unknown'    -- a file that doesn't exist, or cannot be open, or whose
+                name is not encodable in the current ANSI codepage.
+
+Note that for .bat and .cmd batch files the function returns the type
+of their command interpreter, as specified by the \"COMSPEC\"
+environment variable.
+
+This function returns `unknown' for programs whose file names
+include characters not supported by the current ANSI codepage, as
+such programs cannot be invoked by Emacs anyway.  */)
+     (Lisp_Object program)
+{
+  int is_dos_app, is_cygwin_app, is_msys_app, dummy;
+  Lisp_Object encoded_progname;
+  char *progname, progname_a[MAX_PATH];
+
+  program = Fexpand_file_name (program, Qnil);
+  encoded_progname = ENCODE_FILE (program);
+  progname = SSDATA (encoded_progname);
+  unixtodos_filename (progname);
+  filename_to_ansi (progname, progname_a);
+  /* Reject file names that cannot be encoded in the current ANSI
+     codepage.  */
+  if (_mbspbrk ((unsigned char *)progname_a, (const unsigned char *)"?"))
+    return Qunknown;
+
+  if (w32_executable_type (progname_a, &is_dos_app, &is_cygwin_app,
+                          &is_msys_app, &dummy) != 0)
+    return Qunknown;
+  if (is_dos_app)
+    return Qdos;
+  if (is_cygwin_app)
+    return Qcygwin;
+  if (is_msys_app)
+    return Qmsys;
+  return Qw32_native;
+}
+
 #ifdef HAVE_LANGINFO_CODESET
 /* Emulation of nl_langinfo.  Used in fns.c:Flocale_info.  */
 char *
 #ifdef HAVE_LANGINFO_CODESET
 /* Emulation of nl_langinfo.  Used in fns.c:Flocale_info.  */
 char *
@@ -3070,17 +3331,27 @@ If successful, the new CP is returned, otherwise nil.  */)
 DEFUN ("w32-get-codepage-charset", Fw32_get_codepage_charset,
        Sw32_get_codepage_charset, 1, 1, 0,
        doc: /* Return charset ID corresponding to codepage CP.
 DEFUN ("w32-get-codepage-charset", Fw32_get_codepage_charset,
        Sw32_get_codepage_charset, 1, 1, 0,
        doc: /* Return charset ID corresponding to codepage CP.
-Returns nil if the codepage is not valid.  */)
+Returns nil if the codepage is not valid or its charset ID could
+not be determined.
+
+Note that this function is only guaranteed to work with ANSI
+codepages; most console codepages are not supported and will
+yield nil.  */)
   (Lisp_Object cp)
 {
   CHARSETINFO info;
   (Lisp_Object cp)
 {
   CHARSETINFO info;
+  DWORD_PTR dwcp;
 
   CHECK_NUMBER (cp);
 
   if (!IsValidCodePage (XINT (cp)))
     return Qnil;
 
 
   CHECK_NUMBER (cp);
 
   if (!IsValidCodePage (XINT (cp)))
     return Qnil;
 
-  if (TranslateCharsetInfo ((DWORD *) XINT (cp), &info, TCI_SRCCODEPAGE))
+  /* Going through a temporary DWORD_PTR variable avoids compiler warning
+     about cast to pointer from integer of different size, when
+     building --with-wide-int or building for 64bit.  */
+  dwcp = XINT (cp);
+  if (TranslateCharsetInfo ((DWORD *) dwcp, &info, TCI_SRCCODEPAGE))
     return make_number (info.ciCharset);
 
   return Qnil;
     return make_number (info.ciCharset);
 
   return Qnil;
@@ -3139,8 +3410,8 @@ If successful, the new layout id is returned, otherwise nil.  */)
   CHECK_NUMBER_CAR (layout);
   CHECK_NUMBER_CDR (layout);
 
   CHECK_NUMBER_CAR (layout);
   CHECK_NUMBER_CDR (layout);
 
kl = (HKL) ((XINT (XCAR (layout)) & 0xffff)
-            | (XINT (XCDR (layout)) << 16));
 kl = (HKL) (UINT_PTR) ((XINT (XCAR (layout)) & 0xffff)
+                        | (XINT (XCDR (layout)) << 16));
 
   /* Synchronize layout with input thread.  */
   if (dwWindowsThreadId)
 
   /* Synchronize layout with input thread.  */
   if (dwWindowsThreadId)
@@ -3234,13 +3505,16 @@ get_lcid (const char *locale_name)
   return found_lcid;
 }
 
   return found_lcid;
 }
 
-#ifndef _NSLCMPERROR
-# define _NSLCMPERROR INT_MAX
+#ifndef _NLSCMPERROR
+# define _NLSCMPERROR INT_MAX
 #endif
 #ifndef LINGUISTIC_IGNORECASE
 # define LINGUISTIC_IGNORECASE  0x00000010
 #endif
 
 #endif
 #ifndef LINGUISTIC_IGNORECASE
 # define LINGUISTIC_IGNORECASE  0x00000010
 #endif
 
+typedef int (WINAPI *CompareStringW_Proc)
+  (LCID, DWORD, LPCWSTR, int, LPCWSTR, int);
+
 int
 w32_compare_strings (const char *s1, const char *s2, char *locname,
                     int ignore_case)
 int
 w32_compare_strings (const char *s1, const char *s2, char *locname,
                     int ignore_case)
@@ -3249,7 +3523,7 @@ w32_compare_strings (const char *s1, const char *s2, char *locname,
   wchar_t *string1_w, *string2_w;
   int val, needed;
   extern BOOL g_b_init_compare_string_w;
   wchar_t *string1_w, *string2_w;
   int val, needed;
   extern BOOL g_b_init_compare_string_w;
-  static int (WINAPI *pCompareStringW)(LCID, DWORD, LPCWSTR, int, LPCWSTR, int);
+  static CompareStringW_Proc pCompareStringW;
   DWORD flags = 0;
 
   USE_SAFE_ALLOCA;
   DWORD flags = 0;
 
   USE_SAFE_ALLOCA;
@@ -3265,14 +3539,15 @@ w32_compare_strings (const char *s1, const char *s2, char *locname,
     {
       if (os_subtype == OS_9X)
        {
     {
       if (os_subtype == OS_9X)
        {
-         pCompareStringW = GetProcAddress (LoadLibrary ("Unicows.dll"),
-                                           "CompareStringW");
+         pCompareStringW =
+            (CompareStringW_Proc) GetProcAddress (LoadLibrary ("Unicows.dll"),
+                                                  "CompareStringW");
          if (!pCompareStringW)
            {
              errno = EINVAL;
              /* This return value is compatible with wcscoll and
                 other MS CRT functions.  */
          if (!pCompareStringW)
            {
              errno = EINVAL;
              /* This return value is compatible with wcscoll and
                 other MS CRT functions.  */
-             return _NSLCMPERROR;
+             return _NLSCMPERROR;
            }
        }
       else
            }
        }
       else
@@ -3291,7 +3566,7 @@ w32_compare_strings (const char *s1, const char *s2, char *locname,
   else
     {
       errno = EINVAL;
   else
     {
       errno = EINVAL;
-      return _NSLCMPERROR;
+      return _NLSCMPERROR;
     }
 
   needed = pMultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, s2, -1, NULL, 0);
     }
 
   needed = pMultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, s2, -1, NULL, 0);
@@ -3305,7 +3580,7 @@ w32_compare_strings (const char *s1, const char *s2, char *locname,
     {
       SAFE_FREE ();
       errno = EINVAL;
     {
       SAFE_FREE ();
       errno = EINVAL;
-      return _NSLCMPERROR;
+      return _NLSCMPERROR;
     }
 
   if (locname)
     }
 
   if (locname)
@@ -3342,7 +3617,7 @@ w32_compare_strings (const char *s1, const char *s2, char *locname,
   if (!val)
     {
       errno = EINVAL;
   if (!val)
     {
       errno = EINVAL;
-      return _NSLCMPERROR;
+      return _NLSCMPERROR;
     }
   return val - 2;
 }
     }
   return val - 2;
 }
@@ -3353,6 +3628,9 @@ syms_of_ntproc (void)
 {
   DEFSYM (Qhigh, "high");
   DEFSYM (Qlow, "low");
 {
   DEFSYM (Qhigh, "high");
   DEFSYM (Qlow, "low");
+  DEFSYM (Qcygwin, "cygwin");
+  DEFSYM (Qmsys, "msys");
+  DEFSYM (Qw32_native, "w32-native");
 
   defsubr (&Sw32_has_winsock);
   defsubr (&Sw32_unload_winsock);
 
   defsubr (&Sw32_has_winsock);
   defsubr (&Sw32_unload_winsock);
@@ -3360,6 +3638,7 @@ syms_of_ntproc (void)
   defsubr (&Sw32_short_file_name);
   defsubr (&Sw32_long_file_name);
   defsubr (&Sw32_set_process_priority);
   defsubr (&Sw32_short_file_name);
   defsubr (&Sw32_long_file_name);
   defsubr (&Sw32_set_process_priority);
+  defsubr (&Sw32_application_type);
   defsubr (&Sw32_get_locale_info);
   defsubr (&Sw32_get_current_locale_id);
   defsubr (&Sw32_get_default_locale_id);
   defsubr (&Sw32_get_locale_info);
   defsubr (&Sw32_get_current_locale_id);
   defsubr (&Sw32_get_default_locale_id);
@@ -3424,6 +3703,13 @@ of time slices to wait (effectively boosting the priority of the child
 process temporarily).  A value of zero disables waiting entirely.  */);
   w32_pipe_read_delay = 50;
 
 process temporarily).  A value of zero disables waiting entirely.  */);
   w32_pipe_read_delay = 50;
 
+  DEFVAR_INT ("w32-pipe-buffer-size", w32_pipe_buffer_size,
+             doc: /* Size of buffer for pipes created to communicate with subprocesses.
+The size is in bytes, and must be non-negative.  The default is zero,
+which lets the OS use its default size, usually 4KB (4096 bytes).
+Any negative value means to use the default value of zero.  */);
+  w32_pipe_buffer_size = 0;
+
   DEFVAR_LISP ("w32-downcase-file-names", Vw32_downcase_file_names,
               doc: /* Non-nil means convert all-upper case file names to lower case.
 This applies when performing completions and file name expansion.
   DEFVAR_LISP ("w32-downcase-file-names", Vw32_downcase_file_names,
               doc: /* Non-nil means convert all-upper case file names to lower case.
 This applies when performing completions and file name expansion.