changeset 15145:86a1f426871e

Include config.h after CRT headers. Include fcntl.h. (child_process, MAX_CHILDREN, CHILD_ACTIVE): Moved to nt.h. (DebugPrint): New macro. (new_child): Create input event structures for child processes. (delete_child): New function. (reader_thread): Return nonzero upon failure. Use _sys_read_ahead. (create_child): Add child_process and parent PID arguments. Don't create input event or thread structures here. Fixup Win95 negative process IDs. (register_child): Don't set consumed event; instead, set thread status so that select will release it. (remove_child): Renamed to reap_process. Only free resources of synchronous children. (win32_wait): Renamed to sys_wait. Sanity check to make sure handle to subprocess exists. Don't reclaim thread resources here. (win32_spawnve): Renamed to sys_spawnve. Check for proper wait mode first thing. Convert command name to DOS format. Quote whitespace and quotes in arguments to subprocess. Use alloca instead of malloc. If max subprocesses, return EAGAIN in hopes of another completing. (sys_select): Add support for socket input. Remove dead code. Check input from children fairly. (sys_select) [HAVE_TIMEVAL]: Remove conditional check and code. (win32_kill_process): Renamed to sys_kill. Use OpenProcess to terminate process. (prepate_standard_handles, reset_standard_handles): Moved here from nt.c.
author Geoff Voelker <voelker@cs.washington.edu>
date Fri, 03 May 1996 18:35:50 +0000
parents 9dd18e9e0362
children be6560506cce
files src/w32proc.c
diffstat 1 files changed, 528 insertions(+), 351 deletions(-) [+]
line wrap: on
line diff
--- a/src/w32proc.c	Fri May 03 18:35:06 1996 +0000
+++ b/src/w32proc.c	Fri May 03 18:35:50 1996 +0000
@@ -22,14 +22,21 @@
      Adapted from alarm.c by Tim Fleehart
 */
 
-#include <config.h>
-
 #include <stdio.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <io.h>
+#include <fcntl.h>
 #include <signal.h>
 
+/* must include CRT headers *before* config.h */
+#include "config.h"
+#undef signal
+#undef wait
+#undef spawnve
+#undef select
+#undef kill
+
 #include <windows.h>
 
 #include "lisp.h"
@@ -42,32 +49,10 @@
 extern char *sys_siglist[];
 #endif
 
-/* #define FULL_DEBUG */
-
-typedef void (_CALLBACK_ *signal_handler)(int);
-
-/* Defined in process.h which conflicts with the local copy */
-#define	_P_NOWAIT 1
-
-typedef struct _child_process
+#ifdef EMACSDEBUG
+void _DebPrint (const char *fmt, ...)
 {
-  int fd;
-  HANDLE char_avail;
-  HANDLE char_consumed;
-  char chr;
-  BOOL status;
-  HANDLE process;
-  DWORD pid;
-  HANDLE thrd;
-} child_process;
-
-#define MAX_CHILDREN MAXDESC
-
-#ifdef EMACSDEBUG
-void _CRTAPI1
-_DebPrint (char *fmt, ...)
-{
-  char buf[256];
+  char buf[1024];
   va_list args;
 
   va_start (args, fmt);
@@ -77,20 +62,14 @@
 }
 #endif
 
-/* Child process management list.  */
-static int child_proc_count = 0;
-static child_process child_procs[MAX_CHILDREN];
-static child_process *dead_child = NULL;
-
-#define CHILD_ACTIVE(cp) ((cp)->process != NULL)
-#define DEACTIVATE_CHILD(cp) ((cp)->process = NULL)
+typedef void (_CALLBACK_ *signal_handler)(int);
 
 /* Signal handlers...SIG_DFL == 0 so this is initialized correctly.  */
 static signal_handler sig_handlers[NSIG];
 
 /* Fake signal implementation to record the SIGCHLD handler.  */
 signal_handler 
-win32_signal (int sig, signal_handler handler)
+sys_signal (int sig, signal_handler handler)
 {
   signal_handler old;
   
@@ -104,19 +83,109 @@
   return old;
 }
 
+/* Defined in <process.h> which conflicts with the local copy */
+#define _P_NOWAIT 1
+
+/* Child process management list.  */
+int child_proc_count = 0;
+child_process child_procs[ MAX_CHILDREN ];
+child_process *dead_child = NULL;
+
+DWORD WINAPI reader_thread (void *arg);
+
 /* Find an unused process slot.  */
-static child_process *
+child_process *
 new_child (void)
 {
   child_process *cp;
-  
-  if (child_proc_count == MAX_CHILDREN)
-    return NULL;
+  DWORD id;
   
   for (cp = child_procs+(child_proc_count-1); cp >= child_procs; cp--)
     if (!CHILD_ACTIVE (cp))
-      return cp;
-  return &child_procs[child_proc_count++];
+      goto Initialise;
+  if (child_proc_count == MAX_CHILDREN)
+    return NULL;
+  cp = &child_procs[child_proc_count++];
+
+ Initialise:
+  memset (cp, 0, sizeof(*cp));
+  cp->fd = -1;
+  cp->pid = -1;
+  cp->procinfo.hProcess = NULL;
+  cp->status = STATUS_READ_ERROR;
+
+  /* use manual reset event so that select() will function properly */
+  cp->char_avail = CreateEvent (NULL, TRUE, FALSE, NULL);
+  if (cp->char_avail)
+    {
+      cp->char_consumed = CreateEvent (NULL, FALSE, FALSE, NULL);
+      if (cp->char_consumed)
+        {
+	  cp->thrd = CreateThread (NULL, 1024, reader_thread, cp, 0, &id);
+	  if (cp->thrd)
+	    return cp;
+	}
+    }
+  delete_child (cp);
+  return NULL;
+}
+
+void 
+delete_child (child_process *cp)
+{
+  int i;
+
+  /* Should not be deleting a child that is still needed. */
+  for (i = 0; i < MAXDESC; i++)
+    if (fd_info[i].cp == cp)
+      abort ();
+
+  if (!CHILD_ACTIVE (cp))
+    return;
+
+  /* reap thread if necessary */
+  if (cp->thrd)
+    {
+      DWORD rc;
+
+      if (GetExitCodeThread (cp->thrd, &rc) && rc == STILL_ACTIVE)
+        {
+	  /* let the thread exit cleanly if possible */
+	  cp->status = STATUS_READ_ERROR;
+	  SetEvent (cp->char_consumed);
+	  if (WaitForSingleObject (cp->thrd, 1000) != WAIT_OBJECT_0)
+	    {
+	      DebPrint (("delete_child.WaitForSingleObject (thread) failed "
+			 "with %lu for fd %ld\n", GetLastError (), cp->fd));
+	      TerminateThread (cp->thrd, 0);
+	    }
+	}
+      CloseHandle (cp->thrd);
+      cp->thrd = NULL;
+    }
+  if (cp->char_avail)
+    {
+      CloseHandle (cp->char_avail);
+      cp->char_avail = NULL;
+    }
+  if (cp->char_consumed)
+    {
+      CloseHandle (cp->char_consumed);
+      cp->char_consumed = NULL;
+    }
+
+  /* update child_proc_count (highest numbered slot in use plus one) */
+  if (cp == child_procs + child_proc_count - 1)
+    {
+      for (i = child_proc_count-1; i >= 0; i--)
+	if (CHILD_ACTIVE (&child_procs[i]))
+	  {
+	    child_proc_count = i + 1;
+	    break;
+	  }
+    }
+  if (i < 0)
+    child_proc_count = 0;
 }
 
 /* Find a child by pid.  */
@@ -124,31 +193,18 @@
 find_child_pid (DWORD pid)
 {
   child_process *cp;
-  
+
   for (cp = child_procs+(child_proc_count-1); cp >= child_procs; cp--)
     if (CHILD_ACTIVE (cp) && pid == cp->pid)
       return cp;
   return NULL;
 }
 
-/* Find a child by fd.  */
-static child_process *
-find_child_fd (int fd)
-{
-  child_process *cp;
-  
-  for (cp = child_procs+(child_proc_count-1); cp >= child_procs; cp--)
-    if (CHILD_ACTIVE (cp) && fd == cp->fd)
-      return cp;
-  return NULL;
-}
 
-/* Thread proc for child process reader threads
-   The threads just sit in a loop waiting for input
-   When they detect input, they signal the char_avail input to
-   wake up the select emulator
-   When the select emulator processes their input, it pulses
-   char_consumed so that the reader thread goes back to reading.  */
+/* Thread proc for child process and socket reader threads. Each thread
+   is normally blocked until woken by select() to check for input by
+   reading one char.  When the read completes, char_avail is signalled
+   to wake up the select emulator and the thread blocks itself again. */
 DWORD WINAPI 
 reader_thread (void *arg)
 {
@@ -158,37 +214,30 @@
   cp = (child_process *)arg;
   
   /* We have to wait for the go-ahead before we can start */
-  if (WaitForSingleObject (cp->char_consumed, INFINITE) != WAIT_OBJECT_0)
-    return 0;
-  /* If something went wrong, quit */
-  if (!cp->status)
-    return 0;
-  
+  if (cp == NULL ||
+      WaitForSingleObject (cp->char_consumed, INFINITE) != WAIT_OBJECT_0)
+    return 1;
+
   for (;;)
     {
-      /* Use read to get CRLF translation */
-      if (read (cp->fd, &cp->chr, sizeof (char)) == sizeof (char))
-        {
-	  cp->status = TRUE;
-        }
-      else
-        {
-#ifdef FULL_DEBUG
-	  DebPrint (("reader_thread.read failed with %lu for fd %ld\n",
-		     GetLastError (), cp->fd));
-#endif
-	  cp->status = FALSE;
-        }
-        
+      int rc;
+
+      rc = _sys_read_ahead (cp->fd);
+
+      /* The name char_avail is a misnomer - it really just means the
+	 read-ahead has completed, whether successfully or not. */
       if (!SetEvent (cp->char_avail))
         {
 	  DebPrint (("reader_thread.SetEvent failed with %lu for fd %ld\n",
 		     GetLastError (), cp->fd));
-	  break;
-        }
+	  return 1;
+	}
+
+      if (rc == STATUS_READ_ERROR)
+	return 1;
         
       /* If the read died, the child has died so let the thread die */
-      if (!cp->status)
+      if (rc == STATUS_READ_FAILED)
 	break;
         
       /* Wait until our input is acknowledged before reading again */
@@ -204,31 +253,13 @@
 
 static BOOL 
 create_child (char *exe, char *cmdline, char *env,
-	     PROCESS_INFORMATION *info)
+	      int * pPid, child_process *cp)
 {
-  child_process *cp;
-  DWORD id;
   STARTUPINFO start;
   SECURITY_ATTRIBUTES sec_attrs;
   SECURITY_DESCRIPTOR sec_desc;
   
-  cp = new_child ();
-  if (cp == NULL)
-    goto EH_Fail;
-  
-  cp->fd = -1;
-  
-  cp->char_avail = CreateEvent (NULL, FALSE, FALSE, NULL);
-  if (cp->char_avail == NULL)
-    goto EH_Fail;
-  
-  cp->char_consumed = CreateEvent (NULL, FALSE, FALSE, NULL);
-  if (cp->char_consumed == NULL)
-    goto EH_char_avail;
-  
-  cp->thrd = CreateThread (NULL, 1024, reader_thread, cp, 0, &id);
-  if (cp->thrd == NULL)
-    goto EH_char_consumed;
+  if (cp == NULL) abort ();
   
   memset (&start, 0, sizeof (start));
   start.cb = sizeof (start);
@@ -244,32 +275,35 @@
 
   /* Explicitly specify no security */
   if (!InitializeSecurityDescriptor (&sec_desc, SECURITY_DESCRIPTOR_REVISION))
-    goto EH_thrd;
+    goto EH_Fail;
   if (!SetSecurityDescriptorDacl (&sec_desc, TRUE, NULL, FALSE))
-    goto EH_thrd;
+    goto EH_Fail;
   sec_attrs.nLength = sizeof (sec_attrs);
   sec_attrs.lpSecurityDescriptor = &sec_desc;
   sec_attrs.bInheritHandle = FALSE;
   
   if (!CreateProcess (exe, cmdline, &sec_attrs, NULL, TRUE,
-		      CREATE_NEW_PROCESS_GROUP, env, NULL,
-		      &start, info))
-    goto EH_thrd;
-  cp->process = info->hProcess;
-  cp->pid = info->dwProcessId;
+		      CREATE_NEW_PROCESS_GROUP,
+		      env, NULL,
+		      &start, &cp->procinfo))
+    goto EH_Fail;
+
+  cp->pid = (int) cp->procinfo.dwProcessId;
+
+  /* Hack for Windows 95, which assigns large (ie negative) pids */
+  if (cp->pid < 0)
+    cp->pid = -cp->pid;
+
+  /* pid must fit in a Lisp_Int */
+  cp->pid = (cp->pid & VALMASK);
+
+
+  *pPid = cp->pid;
   
   return TRUE;
   
- EH_thrd:
-  id = GetLastError ();
-  
-  cp->status = FALSE;
-  SetEvent (cp->char_consumed);
- EH_char_consumed:
-  CloseHandle (cp->char_consumed);
- EH_char_avail:
-  CloseHandle (cp->char_avail);
  EH_Fail:
+  DebPrint (("create_child.CreateProcess failed: %ld\n", GetLastError()););
   return FALSE;
 }
 
@@ -295,14 +329,19 @@
 #endif
   
   cp->fd = fd;
-  cp->status = TRUE;
+
+  /* thread is initially blocked until select is called; set status so
+     that select will release thread */
+  cp->status = STATUS_READ_ACKNOWLEDGED;
 
-  /* Tell the reader thread to start */
-  if (!SetEvent (cp->char_consumed))
+  /* attach child_process to fd_info */
+  if (fd_info[fd].cp != NULL)
     {
-      DebPrint (("register_child.SetEvent failed with %lu for fd %ld\n",
-		 GetLastError (), cp->fd));
+      DebPrint (("register_child: fd_info[%d] apparently in use!\n", fd));
+      abort ();
     }
+
+  fd_info[fd].cp = cp;
 }
 
 /* When a process dies its pipe will break so the reader thread will
@@ -310,27 +349,26 @@
    The select emulator then calls this routine to clean up.
    Since the thread signaled failure we can assume it is exiting.  */
 static void 
-remove_child (child_process *cp)
+reap_subprocess (child_process *cp)
 {
-  /* Reap the thread */
-  if (WaitForSingleObject (cp->thrd, INFINITE) != WAIT_OBJECT_0)
+  if (cp->procinfo.hProcess)
     {
-      DebPrint (("remove_child.WaitForSingleObject (thread) failed "
-		 "with %lu for fd %ld\n", GetLastError (), cp->fd));
+      /* Reap the process */
+      if (WaitForSingleObject (cp->procinfo.hProcess, INFINITE) != WAIT_OBJECT_0)
+	DebPrint (("reap_subprocess.WaitForSingleObject (process) failed "
+		   "with %lu for fd %ld\n", GetLastError (), cp->fd));
+      CloseHandle (cp->procinfo.hProcess);
+      cp->procinfo.hProcess = NULL;
+      CloseHandle (cp->procinfo.hThread);
+      cp->procinfo.hThread = NULL;
     }
-  CloseHandle (cp->thrd);
-  CloseHandle (cp->char_consumed);
-  CloseHandle (cp->char_avail);
-  
-  /* Reap the process */
-  if (WaitForSingleObject (cp->process, INFINITE) != WAIT_OBJECT_0)
-    {
-      DebPrint (("remove_child.WaitForSingleObject (process) failed "
-		 "with %lu for fd %ld\n", GetLastError (), cp->fd));
-    }
-  CloseHandle (cp->process);
-  
-  DEACTIVATE_CHILD (cp);
+
+  /* For asynchronous children, the child_proc resources will be freed
+     when the last pipe read descriptor is closed; for synchronous
+     children, we must explicitly free the resources now because
+     register_child has not been called. */
+  if (cp->fd == -1)
+    delete_child (cp);
 }
 
 /* Wait for any of our existing child processes to die
@@ -338,10 +376,11 @@
    Return the pid and fill in the status if non-NULL.  */
 
 int 
-win32_wait (int *status)
+sys_wait (int *status)
 {
   DWORD active, retval;
   int nh;
+  int pid;
   child_process *cp, *cps[MAX_CHILDREN];
   HANDLE wait_hnd[MAX_CHILDREN];
   
@@ -349,17 +388,20 @@
   if (dead_child != NULL)
     {
       /* We want to wait for a specific child */
-      wait_hnd[nh] = dead_child->process;
+      wait_hnd[nh] = dead_child->procinfo.hProcess;
       cps[nh] = dead_child;
+      if (!wait_hnd[nh]) abort ();
       nh++;
     }
   else
     {
       for (cp = child_procs+(child_proc_count-1); cp >= child_procs; cp--)
-	if (CHILD_ACTIVE (cp))
+	/* some child_procs might be sockets; ignore them */
+	if (CHILD_ACTIVE (cp) && cp->procinfo.hProcess)
 	  {
-	    wait_hnd[nh] = cp->process;
+	    wait_hnd[nh] = cp->procinfo.hProcess;
 	    cps[nh] = cp;
+	    if (!wait_hnd[nh]) abort ();
 	    nh++;
 	  }
     }
@@ -416,8 +458,12 @@
     retval = SIGINT;
   else
     retval <<= 8;
-
+  
   cp = cps[active];
+  pid = cp->pid;
+#ifdef FULL_DEBUG
+  DebPrint (("Wait signaled with process pid %d\n", cp->pid));
+#endif
 
   if (status)
     {
@@ -445,15 +491,11 @@
 
 	  synch_process_death = signame;
 	}
-      TerminateThread (cp->thrd, 0);
-      CloseHandle (cp->thrd);
-      CloseHandle (cp->char_consumed);
-      CloseHandle (cp->char_avail);
-      CloseHandle (cp->process);
-      DEACTIVATE_CHILD (cp);
+
+      reap_subprocess (cp);
     }
   
-  return cp->pid;
+  return pid;
 }
 
 /* We pass our process ID to our children by setting up an environment
@@ -463,12 +505,20 @@
 /* When a new child process is created we need to register it in our list,
    so intercept spawn requests.  */
 int 
-win32_spawnve (int mode, char *cmdname, char **argv, char **envp)
+sys_spawnve (int mode, char *cmdname, char **argv, char **envp)
 {
   Lisp_Object program, full;
   char *cmdline, *env, *parg, **targ;
   int arglen;
-  PROCESS_INFORMATION pi;
+  int pid;
+  child_process *cp;
+  
+  /* We don't care about the other modes */
+  if (mode != _P_NOWAIT)
+    {
+      errno = EINVAL;
+      return -1;
+    }
 
   /* Handle executable names without an executable suffix.  */
   program = make_string (cmdname, strlen (cmdname));
@@ -489,23 +539,24 @@
       argv[0] = cmdname;
     }
 
-  if (child_proc_count == MAX_CHILDREN)
-    {
-      errno = EAGAIN;
-      return -1;
-    }
-  
-  /* We don't care about the other modes */
-  if (mode != _P_NOWAIT)
-    {
-      errno = EINVAL;
-      return -1;
-    }
+  /* make sure cmdname is in DOS format */
+  strcpy (cmdname = alloca (strlen (cmdname) + 1), argv[0]);
+  unixtodos_filename (cmdname);
+  argv[0] = cmdname;
   
   /* we have to do some conjuring here to put argv and envp into the
      form CreateProcess wants...  argv needs to be a space separated/null
      terminated list of parameters, and envp is a null
      separated/double-null terminated list of parameters.
+
+     Additionally, zero-length args and args containing whitespace need
+     to be wrapped in double quotes.  Args containing embedded double
+     quotes (as opposed to enclosing quotes, which we leave alone) are
+     usually illegal (most Win32 programs do not implement escaping of
+     double quotes - sad but true, at least for programs compiled with
+     MSVC), but we will escape quotes anyway for those programs that can
+     handle it.  The Win32 gcc library from Cygnus doubles quotes to
+     escape them, so we will use that convention.
    
      Since I have no idea how large argv and envp are likely to be
      we figure out list lengths on the fly and allocate them.  */
@@ -515,21 +566,69 @@
   targ = argv;
   while (*targ)
     {
+      char * p = *targ;
+      int add_quotes = 0;
+
+      if (*p == 0)
+	add_quotes = 1;
+      while (*p)
+	if (*p++ == '"')
+	  {
+	    /* allow for embedded quotes to be doubled - we won't
+	       actually double quotes that aren't embedded though */
+	    arglen++;
+	    add_quotes = 1;
+	  }
+      else if (*p == ' ' || *p == '\t')
+	add_quotes = 1;
+      if (add_quotes)
+	arglen += 2;
       arglen += strlen (*targ++) + 1;
     }
-  cmdline = malloc (arglen);
-  if (cmdline == NULL)
-    {
-      errno = ENOMEM;
-      goto EH_Fail;
-    }
+  cmdline = alloca (arglen);
   targ = argv;
   parg = cmdline;
   while (*targ)
     {
-      strcpy (parg, *targ);
-      parg += strlen (*targ++);
+      char * p = *targ;
+      int add_quotes = 0;
+
+      if (*p == 0)
+	add_quotes = 1;
+#if 0
+      /* Unfortunately, this causes more problems than it solves,
+	 because argv arrays are not always carefully constructed.
+	 grep, for instance, passes the whole command line as one
+	 argument, so it becomes impossible to pass a regexp which
+	 contains spaces. */
+      for ( ; *p; p++)
+	if (*p == ' ' || *p == '\t' || *p == '"')
+	  add_quotes = 1;
+#endif
+      if (add_quotes)
+	{
+	  char * first;
+	  char * last;
+
+	  p = *targ;
+	  first = p;
+	  last = p + strlen (p) - 1;
+	  *parg++ = '"';
+	  while (*p)
+	    {
+	      if (*p == '"' && p > first && p < last)
+		*parg++ = '"';	/* double up embedded quotes only */
+	      *parg++ = *p++;
+	    }
+	  *parg++ = '"';
+	}
+      else
+	{
+	  strcpy (parg, *targ);
+	  parg += strlen (*targ);
+	}
       *parg++ = ' ';
+      targ++;
     }
   *--parg = '\0';
   
@@ -544,12 +643,7 @@
 	   GetCurrentProcessId ());
   arglen += strlen (ppid_env_var_buffer) + 1;
 
-  env = malloc (arglen);
-  if (env == NULL)
-    {
-      errno = ENOMEM;
-      goto EH_cmdline;
-    }
+  env = alloca (arglen);
   targ = envp;
   parg = env;
   while (*targ)
@@ -562,22 +656,23 @@
   parg += strlen (ppid_env_var_buffer);
   *parg++ = '\0';
   *parg = '\0';
-  
-  /* Now create the process.  */
-  if (!create_child (cmdname, cmdline, env, &pi))
+
+  cp = new_child ();
+  if (cp == NULL)
     {
-      errno = ENOEXEC;
-      goto EH_env;
+      errno = EAGAIN;
+      return -1;
     }
   
-  return pi.dwProcessId;
+  /* Now create the process.  */
+  if (!create_child (cmdname, cmdline, env, &pid, cp))
+    {
+      delete_child (cp);
+      errno = ENOEXEC;
+      return -1;
+    }
   
- EH_env:
-  free (env);
- EH_cmdline:
-  free (cmdline);
- EH_Fail:
-  return -1;
+  return pid;
 }
 
 /* Emulate the select call
@@ -598,20 +693,14 @@
   DWORD timeout_ms;
   int i, nh, nr;
   DWORD active;
-  child_process *cp, *cps[MAX_CHILDREN + 1];
-  HANDLE wait_hnd[MAX_CHILDREN + 1];
-#ifdef HAVE_NTGUI1
-  BOOL keyboardwait = FALSE ;
-#endif /* HAVE_NTGUI */
+  child_process *cp;
+  HANDLE wait_hnd[MAXDESC];
+  int fdindex[MAXDESC];   /* mapping from wait handles back to descriptors */
   
   /* If the descriptor sets are NULL but timeout isn't, then just Sleep.  */
   if (rfds == NULL && wfds == NULL && efds == NULL && timeout != NULL) 
     {
-#ifdef HAVE_TIMEVAL
       Sleep (timeout->tv_sec * 1000 + timeout->tv_usec / 1000);
-#else
-      Sleep ((*timeout) * 1000);
-#endif
       return 0;
     }
 
@@ -633,67 +722,96 @@
       {
 	if (i == 0)
 	  {
-#ifdef HAVE_NTGUI1
-	    keyboardwait = TRUE ;
-#else
-	    /* Handle stdin specially */
-	    wait_hnd[nh] = keyboard_handle;
-	    cps[nh] = NULL;
-	    nh++;
-#endif /* HAVE_NTGUI */
+	    if (keyboard_handle)
+	      {
+		/* Handle stdin specially */
+		wait_hnd[nh] = keyboard_handle;
+		fdindex[nh] = i;
+		nh++;
+	      }
 
 	    /* Check for any emacs-generated input in the queue since
 	       it won't be detected in the wait */
 	    if (detect_input_pending ())
 	      {
 		FD_SET (i, rfds);
-		nr++;
+		return 1;
 	      }
 	  }
 	else
 	  {
-	    /* Child process input */
-	    cp = find_child_fd (i);
+	    /* Child process and socket input */
+	    cp = fd_info[i].cp;
 	    if (cp)
 	      {
+		int current_status = cp->status;
+
+		if (current_status == STATUS_READ_ACKNOWLEDGED)
+		  {
+		    /* Tell reader thread which file handle to use. */
+		    cp->fd = i;
+		    /* Wake up the reader thread for this process */
+		    cp->status = STATUS_READ_READY;
+		    if (!SetEvent (cp->char_consumed))
+		      DebPrint (("nt_select.SetEvent failed with "
+				 "%lu for fd %ld\n", GetLastError (), i));
+		  }
+
+#ifdef CHECK_INTERLOCK
+		/* slightly crude cross-checking of interlock between threads */
+
+		current_status = cp->status;
+		if (WaitForSingleObject (cp->char_avail, 0) == WAIT_OBJECT_0)
+		  {
+		    /* char_avail has been signalled, so status (which may
+		       have changed) should indicate read has completed
+		       but has not been acknowledged. */
+		    current_status = cp->status;
+		    if (current_status != STATUS_READ_SUCCEEDED &&
+			current_status != STATUS_READ_FAILED)
+		      DebPrint (("char_avail set, but read not completed: status %d\n",
+				 current_status));
+		  }
+		else
+		  {
+		    /* char_avail has not been signalled, so status should
+		       indicate that read is in progress; small possibility
+		       that read has completed but event wasn't yet signalled
+		       when we tested it (because a context switch occurred
+		       or if running on separate CPUs). */
+		    if (current_status != STATUS_READ_READY &&
+			current_status != STATUS_READ_IN_PROGRESS &&
+			current_status != STATUS_READ_SUCCEEDED &&
+			current_status != STATUS_READ_FAILED)
+		      DebPrint (("char_avail reset, but read status is bad: %d\n",
+				 current_status));
+		  }
+#endif
+		wait_hnd[nh] = cp->char_avail;
+		fdindex[nh] = i;
+		if (!wait_hnd[nh]) abort ();
+		nh++;
 #ifdef FULL_DEBUG
 		DebPrint (("select waiting on child %d fd %d\n",
 			   cp-child_procs, i));
 #endif
-		wait_hnd[nh] = cp->char_avail;
-		cps[nh] = cp;
-		nh++;
 	      }
 	    else
 	      {
-		/* Unable to find something to wait on for this fd, fail */
-		DebPrint (("select unable to find child process "
-			   "for fd %ld\n", i));
-		nh = 0;
-		break;
+		/* Unable to find something to wait on for this fd, skip */
+		DebPrint (("sys_select: fd %ld is invalid! ignoring\n", i));
+		abort ();
 	      }
 	  }
       }
   
-  /* Never do this in win32 since we will not get paint messages */
-
-#ifndef HAVE_NTGUI1
   /* Nothing to look for, so we didn't find anything */
   if (nh == 0) 
     {
       if (timeout)
-#ifdef HAVE_TIMEVAL
 	Sleep (timeout->tv_sec * 1000 + timeout->tv_usec / 1000);
-#else
-	Sleep ((*timeout) * 1000);
-#endif
       return 0;
     }
-#endif /* !HAVE_NTGUI */
-  
-  /* Check for immediate return without waiting */
-  if (nr > 0)
-    return nr;
   
   /*
      Wait for input
@@ -701,41 +819,25 @@
      so the reader thread will signal an error condition, thus, the wait
      will wake up
      */
-#ifdef HAVE_TIMEVAL
   timeout_ms = timeout ? (timeout->tv_sec * 1000 + timeout->tv_usec / 1000) : INFINITE;
-#else
-  timeout_ms = timeout ? *timeout*1000 : INFINITE;
-#endif
-#ifdef HAVE_NTGUI1
-  active = MsgWaitForMultipleObjects (nh, wait_hnd, FALSE, timeout_ms,QS_ALLINPUT);
-#else
+
   active = WaitForMultipleObjects (nh, wait_hnd, FALSE, timeout_ms);
-#endif /* HAVE_NTGUI */
+
   if (active == WAIT_FAILED)
     {
       DebPrint (("select.WaitForMultipleObjects (%d, %lu) failed with %lu\n",
 		 nh, timeout_ms, GetLastError ()));
-      /* Is there a better error? */
-      errno = EBADF;
+      /* don't return EBADF - this causes wait_reading_process_input to
+	 abort; WAIT_FAILED is returned when single-stepping under
+	 Windows 95 after switching thread focus in debugger, and
+	 possibly at other times. */
+      errno = EINTR;
       return -1;
     }
   else if (active == WAIT_TIMEOUT)
     {
       return 0;
     }
-#ifdef HAVE_NTGUI1
-  else if (active == WAIT_OBJECT_0 + nh)
-    {
-      /* Keyboard input available */
-      FD_SET (0, rfds);
-
-      /* This shouldn't be necessary, but apparently just setting the input
-	 fd is not good enough for emacs */
-//      read_input_waiting ();
-
-      return (1) ;
-    }
-#endif /* HAVE_NTGUI */
   else if (active >= WAIT_OBJECT_0 &&
 	   active < WAIT_OBJECT_0+MAXIMUM_WAIT_OBJECTS)
     {
@@ -746,63 +848,82 @@
     {
       active -= WAIT_ABANDONED_0;
     }
-  
-  if (cps[active] == NULL)
+
+  /* Loop over all handles after active (now officially documented as
+     being the first signalled handle in the array).  We do this to
+     ensure fairness, so that all channels with data available will be
+     processed - otherwise higher numbered channels could be starved. */
+  do
     {
-      /* Keyboard input available */
-      FD_SET (0, rfds);
-      nr++;
-
-      /* This shouldn't be necessary, but apparently just setting the input
-	 fd is not good enough for emacs */
-      read_input_waiting ();
-    }
-  else
-    {
-      /* Child process */
-      cp = cps[active];
-
-      /* If status is FALSE the read failed so don't report input */
-      if (cp->status)
-        {
-	  FD_SET (cp->fd, rfds);
-	  proc_buffered_char[cp->fd] = cp->chr;
+      if (fdindex[active] == 0)
+	{
+	  /* Keyboard input available */
+	  FD_SET (0, rfds);
 	  nr++;
-        }
+	}
       else
-        {
-	  /* The SIGCHLD handler will do a Wait so we know it won't
-	     return until the process is dead
-	     We force Wait to only wait for this process to avoid it
-	     picking up other children that happen to be dead but that
-	     we haven't noticed yet
-	     SIG_DFL for SIGCHLD is ignore? */
-	  if (sig_handlers[SIGCHLD] != SIG_DFL &&
-	      sig_handlers[SIGCHLD] != SIG_IGN)
-            {
+	{
+	  /* must be a socket or pipe */
+	  int current_status;
+
+	  cp = fd_info[ fdindex[active] ].cp;
+
+	  /* Read ahead should have completed, either succeeding or failing. */
+	  FD_SET (fdindex[active], rfds);
+	  nr++;
+	  current_status = cp->status;
+	  if (current_status != STATUS_READ_SUCCEEDED)
+	    {
+	      if (current_status != STATUS_READ_FAILED)
+		DebPrint (("internal error: subprocess pipe signalled "
+			   "at the wrong time (status %d)\n!", current_status));
+
+	      /* The child_process entry for a socket or pipe will be
+		 freed when the last descriptor using it is closed; for
+		 pipes, we call the SIGCHLD handler. */
+	      if (fd_info[ fdindex[active] ].flags & FILE_PIPE)
+		{
+		  /* The SIGCHLD handler will do a Wait so we know it won't
+		     return until the process is dead
+		     We force Wait to only wait for this process to avoid it
+		     picking up other children that happen to be dead but that
+		     we haven't noticed yet
+		     SIG_DFL for SIGCHLD is ignore? */
+		  if (sig_handlers[SIGCHLD] != SIG_DFL &&
+		      sig_handlers[SIGCHLD] != SIG_IGN)
+		    {
 #ifdef FULL_DEBUG
-	      DebPrint (("select calling SIGCHLD handler for pid %d\n",
-			 cp->pid));
+		      DebPrint (("select calling SIGCHLD handler for pid %d\n",
+				 cp->pid));
 #endif
-	      dead_child = cp;
-	      sig_handlers[SIGCHLD](SIGCHLD);
-	      dead_child = NULL;
-            }
-            
-	  /* Clean up the child process entry in the table */
-	  remove_child (cp);
-        }
-    }
+		      dead_child = cp;
+		      sig_handlers[SIGCHLD] (SIGCHLD);
+		      dead_child = NULL;
+		    }
+
+		  /* Clean up the child process entry in the table */
+		  reap_subprocess (cp);
+		}
+	    }
+	}
+
+      /* Test for input on remaining channels. */
+      while (++active < nh)
+	if (WaitForSingleObject (wait_hnd[active], 0) == WAIT_OBJECT_0)
+	  break;
+    } while (active < nh);
+
   return nr;
 }
 
-/*
-   Substitute for certain kill () operations
-   */
+/* Substitute for certain kill () operations */
 int 
-win32_kill_process (int pid, int sig)
+sys_kill (int pid, int sig)
 {
   child_process *cp;
+  HANDLE proc_hand;
+  int need_to_free = 0;
+  int rc = 0;
   
   /* Only handle signals that will result in the process dying */
   if (sig != SIGINT && sig != SIGKILL && sig != SIGQUIT && sig != SIGHUP)
@@ -810,24 +931,33 @@
       errno = EINVAL;
       return -1;
     }
-  
+
   cp = find_child_pid (pid);
   if (cp == NULL)
     {
-      DebPrint (("win32_kill_process didn't find a child with pid %lu\n", pid));
-      errno = ECHILD;
-      return -1;
+      proc_hand = OpenProcess (PROCESS_TERMINATE, 0, pid);
+      if (proc_hand == NULL)
+        {
+	  errno = EPERM;
+	  return -1;
+	}
+      need_to_free = 1;
+    }
+  else
+    {
+      proc_hand = cp->procinfo.hProcess;
+      pid = cp->procinfo.dwProcessId;
     }
   
   if (sig == SIGINT)
     {
-      /* Fake Ctrl-Break.  */
+      /* Ctrl-Break is NT equivalent of SIGINT.  */
       if (!GenerateConsoleCtrlEvent (CTRL_BREAK_EVENT, pid))
         {
-	  DebPrint (("win32_kill_process.GenerateConsoleCtrlEvent return %d "
+	  DebPrint (("sys_kill.GenerateConsoleCtrlEvent return %d "
 		     "for pid %lu\n", GetLastError (), pid));
 	  errno = EINVAL;
-	  return -1;
+	  rc = -1;
         }
     }
   else
@@ -835,56 +965,103 @@
       /* Kill the process.  On Win32 this doesn't kill child processes
 	 so it doesn't work very well for shells which is why it's
 	 not used in every case.  */
-      if (!TerminateProcess (cp->process, 0xff))
+      if (!TerminateProcess (proc_hand, 0xff))
         {
-	  DebPrint (("win32_kill_process.TerminateProcess returned %d "
+	  DebPrint (("sys_kill.TerminateProcess returned %d "
 		     "for pid %lu\n", GetLastError (), pid));
 	  errno = EINVAL;
-	  return -1;
+	  rc = -1;
         }
     }
-  return 0;
+
+  if (need_to_free)
+    CloseHandle (proc_hand);
+
+  return rc;
 }
 
-/* If the channel is a pipe this read might block since we don't
-   know how many characters are available, so check and read only
-   what's there
-   We also need to wake up the reader thread once we've read our data.  */
-int 
-read_child_output (int fd, char *buf, int max)
+extern int report_file_error (char *, Lisp_Object);
+
+/* The following two routines are used to manipulate stdin, stdout, and
+   stderr of our child processes.
+
+   Assuming that in, out, and err are *not* inheritable, we make them
+   stdin, stdout, and stderr of the child as follows:
+
+   - Save the parent's current standard handles.
+   - Set the std handles to inheritable duplicates of the ones being passed in.
+     (Note that _get_osfhandle() is an io.h procedure that retrieves the
+     NT file handle for a crt file descriptor.)
+   - Spawn the child, which inherits in, out, and err as stdin,
+     stdout, and stderr. (see Spawnve)
+   - Close the std handles passed to the child.
+   - Reset the parent's standard handles to the saved handles.
+     (see reset_standard_handles)
+   We assume that the caller closes in, out, and err after calling us.  */
+
+void
+prepare_standard_handles (int in, int out, int err, HANDLE handles[3])
 {
-  HANDLE h;
-  int to_read, nchars;
-  DWORD waiting;
-  child_process *cp;
-  
-  h = (HANDLE)_get_osfhandle (fd);
-  if (GetFileType (h) == FILE_TYPE_PIPE)
-    {
-      PeekNamedPipe (h, NULL, 0, NULL, &waiting, NULL);
-      to_read = min (waiting, (DWORD)max);
-    }
-  else
-    to_read = max;
+  HANDLE parent;
+  HANDLE newstdin, newstdout, newstderr;
+
+  parent = GetCurrentProcess ();
+
+  handles[0] = GetStdHandle (STD_INPUT_HANDLE);
+  handles[1] = GetStdHandle (STD_OUTPUT_HANDLE);
+  handles[2] = GetStdHandle (STD_ERROR_HANDLE);
+
+  /* make inheritable copies of the new handles */
+  if (!DuplicateHandle (parent, 
+		       (HANDLE) _get_osfhandle (in),
+		       parent,
+		       &newstdin, 
+		       0, 
+		       TRUE, 
+		       DUPLICATE_SAME_ACCESS))
+    report_file_error ("Duplicating input handle for child", Qnil);
   
-  /* Use read to get CRLF translation */
-  nchars = read (fd, buf, to_read);
+  if (!DuplicateHandle (parent,
+		       (HANDLE) _get_osfhandle (out),
+		       parent,
+		       &newstdout,
+		       0,
+		       TRUE,
+		       DUPLICATE_SAME_ACCESS))
+    report_file_error ("Duplicating output handle for child", Qnil);
+  
+  if (!DuplicateHandle (parent,
+		       (HANDLE) _get_osfhandle (err),
+		       parent,
+		       &newstderr,
+		       0,
+		       TRUE,
+		       DUPLICATE_SAME_ACCESS))
+    report_file_error ("Duplicating error handle for child", Qnil);
+
+  /* and store them as our std handles */
+  if (!SetStdHandle (STD_INPUT_HANDLE, newstdin))
+    report_file_error ("Changing stdin handle", Qnil);
   
-  if (GetFileType (h) == FILE_TYPE_PIPE)
-    {
-      /* Wake up the reader thread
-	 for this process */
-      cp = find_child_fd (fd);
-      if (cp)
-        {
-	  if (!SetEvent (cp->char_consumed))
-	    DebPrint (("read_child_output.SetEvent failed with "
-		       "%lu for fd %ld\n", GetLastError (), fd));
-        }
-      else
-	DebPrint (("read_child_output couldn't find a child with fd %d\n",
-		   fd));
-    }
-  
-  return nchars;
+  if (!SetStdHandle (STD_OUTPUT_HANDLE, newstdout))
+    report_file_error ("Changing stdout handle", Qnil);
+
+  if (!SetStdHandle (STD_ERROR_HANDLE, newstderr))
+    report_file_error ("Changing stderr handle", Qnil);
 }
+
+void
+reset_standard_handles (int in, int out, int err, HANDLE handles[3])
+{
+  /* close the duplicated handles passed to the child */
+  CloseHandle (GetStdHandle (STD_INPUT_HANDLE));
+  CloseHandle (GetStdHandle (STD_OUTPUT_HANDLE));
+  CloseHandle (GetStdHandle (STD_ERROR_HANDLE));
+
+  /* now restore parent's saved std handles */
+  SetStdHandle (STD_INPUT_HANDLE, handles[0]);
+  SetStdHandle (STD_OUTPUT_HANDLE, handles[1]);
+  SetStdHandle (STD_ERROR_HANDLE, handles[2]);
+}
+
+/* end of ntproc.c */