(U-0002195D): Add `ideographic-structure'; add `sound@ja/on'; add
[chise/xemacs-chise.git.1] / src / process-nt.c
index 2afa597..9c0a4ef 100644 (file)
@@ -2,7 +2,7 @@
    Copyright (C) 1985, 1986, 1987, 1988, 1992, 1993, 1994, 1995
    Free Software Foundation, Inc.
    Copyright (C) 1995 Sun Microsystems, Inc.
    Copyright (C) 1985, 1986, 1987, 1988, 1992, 1993, 1994, 1995
    Free Software Foundation, Inc.
    Copyright (C) 1995 Sun Microsystems, Inc.
-   Copyright (C) 1995, 1996 Ben Wing.
+   Copyright (C) 1995, 1996, 2000 Ben Wing.
 
 This file is part of XEmacs.
 
 
 This file is part of XEmacs.
 
@@ -26,36 +26,47 @@ Boston, MA 02111-1307, USA.  */
 #include <config.h>
 #include "lisp.h"
 
 #include <config.h>
 #include "lisp.h"
 
+#include "buffer.h"
+#include "console-msw.h"
 #include "hash.h"
 #include "lstream.h"
 #include "hash.h"
 #include "lstream.h"
+#include "nt.h"
 #include "process.h"
 #include "procimpl.h"
 #include "sysdep.h"
 
 #include "process.h"
 #include "procimpl.h"
 #include "sysdep.h"
 
-#include <windows.h>
-#ifndef __MINGW32__
 #include <shellapi.h>
 #include <shellapi.h>
-#else
 #include <errno.h>
 #include <errno.h>
-#endif
 #include <signal.h>
 #ifdef HAVE_SOCKETS
 #include <winsock.h>
 #endif
 
 #include <signal.h>
 #ifdef HAVE_SOCKETS
 #include <winsock.h>
 #endif
 
+/* Bound by win32-native.el */
+Lisp_Object Qmswindows_construct_process_command_line;
+
 /* Arbitrary size limit for code fragments passed to run_in_other_process */
 #define FRAGMENT_CODE_SIZE 32
 
 /* Arbitrary size limit for code fragments passed to run_in_other_process */
 #define FRAGMENT_CODE_SIZE 32
 
-/* Bound by winnt.el */
-Lisp_Object Qnt_quote_process_args;
-
 /* Implementation-specific data. Pointed to by Lisp_Process->process_data */
 struct nt_process_data
 {
   HANDLE h_process;
 /* Implementation-specific data. Pointed to by Lisp_Process->process_data */
 struct nt_process_data
 {
   HANDLE h_process;
-  int need_enable_child_signals;
+  DWORD dwProcessId;
+  HWND hwnd; /* console window */
 };
 
 };
 
+/* Control whether create_child causes the process to inherit Emacs'
+   console window, or be given a new one of its own.  The default is
+   nil, to allow multiple DOS programs to run on Win95.  Having separate
+   consoles also allows Emacs to cleanly terminate process groups.  */
+Lisp_Object Vmswindows_start_process_share_console;
+
+/* Control whether create_child cause the process to inherit Emacs'
+   error mode setting.  The default is t, to minimize the possibility of
+   subprocesses blocking when accessing unmounted drives.  */
+Lisp_Object Vmswindows_start_process_inherit_error_mode;
+
 #define NT_DATA(p) ((struct nt_process_data*)((p)->process_data))
 \f
 /*-----------------------------------------------------------------------*/
 #define NT_DATA(p) ((struct nt_process_data*)((p)->process_data))
 \f
 /*-----------------------------------------------------------------------*/
@@ -65,10 +76,25 @@ struct nt_process_data
 /* This one breaks process abstraction. Prototype is in console-msw.h,
    used by select_process method in event-msw.c */
 HANDLE
 /* This one breaks process abstraction. Prototype is in console-msw.h,
    used by select_process method in event-msw.c */
 HANDLE
-get_nt_process_handle (struct Lisp_Process *p)
+get_nt_process_handle (Lisp_Process *p)
 {
   return (NT_DATA (p)->h_process);
 }
 {
   return (NT_DATA (p)->h_process);
 }
+
+static struct Lisp_Process *
+find_process_from_pid (DWORD pid)
+{
+  Lisp_Object tail, proc;
+
+  for (tail = Vprocess_list; CONSP (tail); tail = XCDR (tail))
+    {
+      proc = XCAR (tail);
+      if (NT_DATA (XPROCESS (proc))->dwProcessId == pid)
+       return XPROCESS (proc);
+    }
+  return 0;
+}
+
 \f
 /*-----------------------------------------------------------------------*/
 /* Running remote threads. See Microsoft Systems Journal 1994 Number 5  */
 \f
 /*-----------------------------------------------------------------------*/
 /* Running remote threads. See Microsoft Systems Journal 1994 Number 5  */
@@ -169,7 +195,7 @@ run_in_other_process (HANDLE h_process,
                      LPVOID data, size_t data_size)
 {
   process_memory pm;
                      LPVOID data, size_t data_size)
 {
   process_memory pm;
-  CONST size_t code_size = FRAGMENT_CODE_SIZE;
+  const size_t code_size = FRAGMENT_CODE_SIZE;
   /* Need at most 3 extra bytes of memory, for data alignment */
   size_t total_size = code_size + data_size + 3;
   LPVOID remote_data;
   /* Need at most 3 extra bytes of memory, for data alignment */
   size_t total_size = code_size + data_size + 3;
   LPVOID remote_data;
@@ -225,6 +251,8 @@ run_in_other_process (HANDLE h_process,
 /* Sending signals                                                      */
 /*-----------------------------------------------------------------------*/
 
 /* Sending signals                                                      */
 /*-----------------------------------------------------------------------*/
 
+/* ---------------------------- the NT way ------------------------------- */
+
 /*
  * We handle the following signals:
  *
 /*
  * We handle the following signals:
  *
@@ -288,19 +316,34 @@ sig_enable_proc (sig_enable_data* data)
  * Return nonzero if successful.
  */
 
  * Return nonzero if successful.
  */
 
-/* This code assigns a return value of GetProcAddress to function pointers
-   of many different types. Instead of heavy obscure casts, we just disable
-   warnings about assignments to different function pointer types. */
-#pragma warning (disable : 4113)
-
 static int
 static int
-send_signal (HANDLE h_process, int signo)
+send_signal_the_nt_way (struct nt_process_data *cp, int pid, int signo)
 {
 {
+  HANDLE h_process;
   HMODULE h_kernel = GetModuleHandle ("kernel32");
   HMODULE h_kernel = GetModuleHandle ("kernel32");
+  int close_process = 0;
   DWORD retval;
   
   assert (h_kernel != NULL);
   
   DWORD retval;
   
   assert (h_kernel != NULL);
   
+  if (cp)
+    {
+      pid = cp->dwProcessId;
+      h_process = cp->h_process;
+    }
+  else
+    {
+      close_process = 1;
+      /* Try to open the process with required privileges */
+      h_process = OpenProcess (PROCESS_CREATE_THREAD
+                              | PROCESS_QUERY_INFORMATION 
+                              | PROCESS_VM_OPERATION
+                              | PROCESS_VM_WRITE,
+                              FALSE, pid);
+      if (!h_process)
+       return 0;
+    }
+
   switch (signo)
     {
     case SIGKILL:
   switch (signo)
     {
     case SIGKILL:
@@ -309,7 +352,9 @@ send_signal (HANDLE h_process, int signo)
     case SIGHUP:
       {
        sigkill_data d;
     case SIGHUP:
       {
        sigkill_data d;
-       d.adr_ExitProcess = GetProcAddress (h_kernel, "ExitProcess");
+
+       d.adr_ExitProcess =
+         (void (WINAPI *) (UINT)) GetProcAddress (h_kernel, "ExitProcess");
        assert (d.adr_ExitProcess);
        retval = run_in_other_process (h_process, 
                                       (LPTHREAD_START_ROUTINE)sigkill_proc,
        assert (d.adr_ExitProcess);
        retval = run_in_other_process (h_process, 
                                       (LPTHREAD_START_ROUTINE)sigkill_proc,
@@ -320,6 +365,7 @@ send_signal (HANDLE h_process, int signo)
       {
        sigint_data d;
        d.adr_GenerateConsoleCtrlEvent =
       {
        sigint_data d;
        d.adr_GenerateConsoleCtrlEvent =
+         (BOOL (WINAPI *) (DWORD, DWORD))
          GetProcAddress (h_kernel, "GenerateConsoleCtrlEvent");
        assert (d.adr_GenerateConsoleCtrlEvent);
        d.event = CTRL_C_EVENT;
          GetProcAddress (h_kernel, "GenerateConsoleCtrlEvent");
        assert (d.adr_GenerateConsoleCtrlEvent);
        d.event = CTRL_C_EVENT;
@@ -332,6 +378,8 @@ send_signal (HANDLE h_process, int signo)
       assert (0);
     }
 
       assert (0);
     }
 
+  if (close_process)
+    CloseHandle (h_process);
   return (int)retval > 0 ? 1 : 0;
 }
 
   return (int)retval > 0 ? 1 : 0;
 }
 
@@ -346,6 +394,7 @@ enable_child_signals (HANDLE h_process)
   
   assert (h_kernel != NULL);
   d.adr_SetConsoleCtrlHandler =
   
   assert (h_kernel != NULL);
   d.adr_SetConsoleCtrlHandler =
+    (BOOL (WINAPI *) (LPVOID, BOOL))
     GetProcAddress (h_kernel, "SetConsoleCtrlHandler");
   assert (d.adr_SetConsoleCtrlHandler);
   run_in_other_process (h_process, (LPTHREAD_START_ROUTINE)sig_enable_proc,
     GetProcAddress (h_kernel, "SetConsoleCtrlHandler");
   assert (d.adr_SetConsoleCtrlHandler);
   run_in_other_process (h_process, (LPTHREAD_START_ROUTINE)sig_enable_proc,
@@ -354,6 +403,215 @@ enable_child_signals (HANDLE h_process)
   
 #pragma warning (default : 4113)
 
   
 #pragma warning (default : 4113)
 
+/* ---------------------------- the 95 way ------------------------------- */
+
+static BOOL CALLBACK
+find_child_console (HWND hwnd, long putada)
+{
+  DWORD thread_id;
+  DWORD process_id;
+  struct nt_process_data *cp = (struct nt_process_data *) putada;
+
+  thread_id = GetWindowThreadProcessId (hwnd, &process_id);
+  if (process_id == cp->dwProcessId)
+    {
+      char window_class[32];
+
+      GetClassName (hwnd, window_class, sizeof (window_class));
+      if (strcmp (window_class,
+                 mswindows_windows9x_p ()
+                 ? "tty"
+                 : "ConsoleWindowClass") == 0)
+       {
+         cp->hwnd = hwnd;
+         return FALSE;
+       }
+    }
+  /* keep looking */
+  return TRUE;
+}
+
+static int
+send_signal_the_95_way (struct nt_process_data *cp, int pid, int signo)
+{
+  HANDLE h_process;
+  int close_process = 0;
+  int rc = 1;
+  
+  if (cp)
+    {
+      pid = cp->dwProcessId;
+      h_process = cp->h_process;
+
+      /* Try to locate console window for process. */
+      EnumWindows (find_child_console, (LPARAM) cp);
+    }
+  else
+    {
+      close_process = 1;
+      /* Try to open the process with required privileges */
+      h_process = OpenProcess (PROCESS_TERMINATE, FALSE, pid);
+      if (!h_process)
+       return 0;
+    }
+    
+  if (signo == SIGINT)
+    {
+      if (NILP (Vmswindows_start_process_share_console) && cp && cp->hwnd)
+       {
+         BYTE control_scan_code = (BYTE) MapVirtualKey (VK_CONTROL, 0);
+         BYTE vk_break_code = VK_CANCEL;
+         BYTE break_scan_code = (BYTE) MapVirtualKey (vk_break_code, 0);
+         HWND foreground_window;
+
+         if (break_scan_code == 0)
+           {
+             /* Fake Ctrl-C if we can't manage Ctrl-Break. */
+             vk_break_code = 'C';
+             break_scan_code = (BYTE) MapVirtualKey (vk_break_code, 0);
+           }
+
+         foreground_window = GetForegroundWindow ();
+         if (foreground_window)
+           {
+              /* NT 5.0, and apparently also Windows 98, will not allow
+                a Window to be set to foreground directly without the
+                user's involvement. The workaround is to attach
+                ourselves to the thread that owns the foreground
+                window, since that is the only thread that can set the
+                foreground window.  */
+              DWORD foreground_thread, child_thread;
+              foreground_thread =
+               GetWindowThreadProcessId (foreground_window, NULL);
+             if (foreground_thread == GetCurrentThreadId ()
+                  || !AttachThreadInput (GetCurrentThreadId (),
+                                         foreground_thread, TRUE))
+                foreground_thread = 0;
+
+              child_thread = GetWindowThreadProcessId (cp->hwnd, NULL);
+             if (child_thread == GetCurrentThreadId ()
+                  || !AttachThreadInput (GetCurrentThreadId (),
+                                         child_thread, TRUE))
+                child_thread = 0;
+
+              /* Set the foreground window to the child.  */
+              if (SetForegroundWindow (cp->hwnd))
+                {
+                  /* Generate keystrokes as if user had typed Ctrl-Break or
+                     Ctrl-C.  */
+                  keybd_event (VK_CONTROL, control_scan_code, 0, 0);
+                  keybd_event (vk_break_code, break_scan_code,
+                   (vk_break_code == 'C' ? 0 : KEYEVENTF_EXTENDEDKEY), 0);
+                  keybd_event (vk_break_code, break_scan_code,
+                    (vk_break_code == 'C' ? 0 : KEYEVENTF_EXTENDEDKEY)
+                    | KEYEVENTF_KEYUP, 0);
+                  keybd_event (VK_CONTROL, control_scan_code,
+                               KEYEVENTF_KEYUP, 0);
+
+                  /* Sleep for a bit to give time for Emacs frame to respond
+                     to focus change events (if Emacs was active app).  */
+                  Sleep (100);
+
+                  SetForegroundWindow (foreground_window);
+                }
+              /* Detach from the foreground and child threads now that
+                 the foreground switching is over.  */
+              if (foreground_thread)
+                AttachThreadInput (GetCurrentThreadId (),
+                                   foreground_thread, FALSE);
+              if (child_thread)
+                AttachThreadInput (GetCurrentThreadId (),
+                                   child_thread, FALSE);
+            }
+        }
+      /* Ctrl-Break is NT equivalent of SIGINT.  */
+      else if (!GenerateConsoleCtrlEvent (CTRL_BREAK_EVENT, pid))
+        {
+#if 0 /* FSF Emacs */
+         DebPrint (("sys_kill.GenerateConsoleCtrlEvent return %d "
+                    "for pid %lu\n", GetLastError (), pid));
+         errno = EINVAL;
+#endif
+         rc = 0;
+       }
+    }
+  else
+    {
+      if (NILP (Vmswindows_start_process_share_console) && cp && cp->hwnd)
+       {
+#if 1
+         if (mswindows_windows9x_p ())
+           {
+/*
+   Another possibility is to try terminating the VDM out-right by
+   calling the Shell VxD (id 0x17) V86 interface, function #4
+   "SHELL_Destroy_VM", ie.
+
+     mov edx,4
+     mov ebx,vm_handle
+     call shellapi
+
+   First need to determine the current VM handle, and then arrange for
+   the shellapi call to be made from the system vm (by using
+   Switch_VM_and_callback).
+
+   Could try to invoke DestroyVM through CallVxD.
+
+*/
+#if 0
+             /* On Win95, posting WM_QUIT causes the 16-bit subsystem
+                to hang when cmdproxy is used in conjunction with
+                command.com for an interactive shell.  Posting
+                WM_CLOSE pops up a dialog that, when Yes is selected,
+                does the same thing.  TerminateProcess is also less
+                than ideal in that subprocesses tend to stick around
+                until the machine is shutdown, but at least it
+                doesn't freeze the 16-bit subsystem.  */
+             PostMessage (cp->hwnd, WM_QUIT, 0xff, 0);
+#endif
+             if (!TerminateProcess (h_process, 0xff))
+               {
+#if 0 /* FSF Emacs */
+                 DebPrint (("sys_kill.TerminateProcess returned %d "
+                            "for pid %lu\n", GetLastError (), pid));
+                 errno = EINVAL;
+#endif
+                 rc = 0;
+               }
+           }
+         else
+#endif
+           PostMessage (cp->hwnd, WM_CLOSE, 0, 0);
+       }
+      /* Kill the process.  On W32 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.  */
+      else if (!TerminateProcess (h_process, 0xff))
+        {
+#if 0 /* FSF Emacs */
+         DebPrint (("sys_kill.TerminateProcess returned %d "
+                    "for pid %lu\n", GetLastError (), pid));
+         errno = EINVAL;
+#endif
+         rc = 0;
+        }
+    }
+
+  if (close_process)
+    CloseHandle (h_process);
+
+  return rc;
+}
+
+/* -------------------------- all-OS functions ---------------------------- */
+
+static int
+send_signal (struct nt_process_data *cp, int pid, int signo)
+{
+  return (!mswindows_windows9x_p () && send_signal_the_nt_way (cp, pid, signo))
+    || send_signal_the_95_way (cp, pid, signo);
+}
+
 /*
  * Signal error if SIGNO is not supported
  */
 /*
  * Signal error if SIGNO is not supported
  */
@@ -363,7 +621,7 @@ validate_signal_number (int signo)
   if (signo != SIGKILL && signo != SIGTERM
       && signo != SIGQUIT && signo != SIGINT
       && signo != SIGHUP)
   if (signo != SIGKILL && signo != SIGTERM
       && signo != SIGQUIT && signo != SIGINT
       && signo != SIGHUP)
-    signal_simple_error ("Signal number not supported", make_int (signo));
+    invalid_argument ("Signal number not supported", make_int (signo));
 }
 \f  
 /*-----------------------------------------------------------------------*/
 }
 \f  
 /*-----------------------------------------------------------------------*/
@@ -375,17 +633,20 @@ validate_signal_number (int signo)
  */
 
 static void
  */
 
 static void
-nt_alloc_process_data (struct Lisp_Process *p)
+nt_alloc_process_data (Lisp_Process *p)
 {
   p->process_data = xnew_and_zero (struct nt_process_data);
 }
 
 static void
 {
   p->process_data = xnew_and_zero (struct nt_process_data);
 }
 
 static void
-nt_finalize_process_data (struct Lisp_Process *p, int for_disksave)
+nt_finalize_process_data (Lisp_Process *p, int for_disksave)
 {
   assert (!for_disksave);
 {
   assert (!for_disksave);
-  if (NT_DATA(p)->h_process)
-    CloseHandle (NT_DATA(p)->h_process);
+  /* If it's still in the list of processes we are waiting on delete
+     it.  */
+  mswindows_unwait_process (p);
+  if (NT_DATA (p)->h_process)
+    CloseHandle (NT_DATA (p)->h_process);
 }
 
 /*
 }
 
 /*
@@ -414,45 +675,82 @@ static void
 signal_cannot_launch (Lisp_Object image_file, DWORD err)
 {
   mswindows_set_errno (err);
 signal_cannot_launch (Lisp_Object image_file, DWORD err)
 {
   mswindows_set_errno (err);
-  signal_simple_error_2 ("Error starting", image_file, lisp_strerror (errno));
+  report_file_error ("Error starting", image_file);
+}
+
+static void
+ensure_console_window_exists (void)
+{
+  if (mswindows_windows9x_p ())
+    mswindows_hide_console ();
+}
+
+int
+compare_env (const void *strp1, const void *strp2)
+{
+  const char *str1 = *(const char**)strp1, *str2 = *(const char**)strp2;
+
+  while (*str1 && *str2 && *str1 != '=' && *str2 != '=')
+    {
+      if ((*str1) > (*str2))
+       return 1;
+      else if ((*str1) < (*str2))
+       return -1;
+      str1++, str2++;
+    }
+
+  if (*str1 == '=' && *str2 == '=')
+    return 0;
+  else if (*str1 == '=')
+    return -1;
+  else
+    return 1;
 }
 
 static int
 }
 
 static int
-nt_create_process (struct Lisp_Process *p,
+nt_create_process (Lisp_Process *p,
                   Lisp_Object *argv, int nargv,
                   Lisp_Object program, Lisp_Object cur_dir)
 {
                   Lisp_Object *argv, int nargv,
                   Lisp_Object program, Lisp_Object cur_dir)
 {
+  /* Synched up with sys_spawnve in FSF 20.6.  Significantly different
+     but still synchable. */
   HANDLE hmyshove, hmyslurp, hprocin, hprocout, hprocerr;
   HANDLE hmyshove, hmyslurp, hprocin, hprocout, hprocerr;
-  LPTSTR command_line;
+  Extbyte *command_line;
   BOOL do_io, windowed;
   char *proc_env;
 
   BOOL do_io, windowed;
   char *proc_env;
 
+  /* No need to DOS-ize the filename; expand-file-name (called prior)
+     already does this. */
+
   /* Find out whether the application is windowed or not */
   /* Find out whether the application is windowed or not */
-  {
-    /* SHGetFileInfo tends to return ERROR_FILE_NOT_FOUND on most
-       errors. This leads to bogus error message. */
-    DWORD image_type;
-    char *p = strrchr ((char *)XSTRING_DATA (program), '.');
-    if (p != NULL &&
-       (stricmp (p, ".exe") == 0 ||
-        stricmp (p, ".com") == 0 ||
-        stricmp (p, ".bat") == 0 ||
-        stricmp (p, ".cmd") == 0))
-      {
-       image_type = SHGetFileInfo ((char *)XSTRING_DATA (program), 0,NULL,
-                                   0, SHGFI_EXETYPE);
-      }
-    else
-      {
-       char progname[MAX_PATH];
-       sprintf (progname, "%s.exe", (char *)XSTRING_DATA (program));
-       image_type = SHGetFileInfo (progname, 0, NULL, 0, SHGFI_EXETYPE);
-      }
-    if (image_type == 0)
-      signal_cannot_launch (program, (GetLastError () == ERROR_FILE_NOT_FOUND
-                                     ? ERROR_BAD_FORMAT : GetLastError ()));
-    windowed = HIWORD (image_type) != 0;
-  }
+  if (xSHGetFileInfoA)
+    {
+      /* SHGetFileInfo tends to return ERROR_FILE_NOT_FOUND on most
+        errors. This leads to bogus error message. */
+      DWORD image_type;
+      char *p = strrchr ((char *)XSTRING_DATA (program), '.');
+      if (p != NULL &&
+         (stricmp (p, ".exe") == 0 ||
+          stricmp (p, ".com") == 0 ||
+          stricmp (p, ".bat") == 0 ||
+          stricmp (p, ".cmd") == 0))
+       {
+         image_type = xSHGetFileInfoA ((char *)XSTRING_DATA (program), 0,NULL,
+                                       0, SHGFI_EXETYPE);
+       }
+      else
+       {
+         char progname[MAX_PATH];
+         sprintf (progname, "%s.exe", (char *)XSTRING_DATA (program));
+         image_type = xSHGetFileInfoA (progname, 0, NULL, 0, SHGFI_EXETYPE);
+       }
+      if (image_type == 0)
+       signal_cannot_launch (program, (GetLastError () == ERROR_FILE_NOT_FOUND
+                                       ? ERROR_BAD_FORMAT : GetLastError ()));
+      windowed = HIWORD (image_type) != 0;
+    }
+  else /* NT 3.5; we have no idea so just guess. */
+    windowed = 0;
 
   /* Decide whether to do I/O on process handles, or just mark the
      process exited immediately upon successful launching. We do I/O if the
 
   /* Decide whether to do I/O on process handles, or just mark the
      process exited immediately upon successful launching. We do I/O if the
@@ -474,22 +772,25 @@ nt_create_process (struct Lisp_Process *p,
       CreatePipe (&hmyslurp, &hprocout, &sa, 0);
 
       /* Duplicate the stdout handle for use as stderr */
       CreatePipe (&hmyslurp, &hprocout, &sa, 0);
 
       /* Duplicate the stdout handle for use as stderr */
-      DuplicateHandle(GetCurrentProcess(), hprocout, GetCurrentProcess(), &hprocerr,
-       0, TRUE, DUPLICATE_SAME_ACCESS);
+      DuplicateHandle(GetCurrentProcess(), hprocout, GetCurrentProcess(),
+                      &hprocerr, 0, TRUE, DUPLICATE_SAME_ACCESS);
 
       /* Stupid Win32 allows to create a pipe with *both* ends either
         inheritable or not. We need process ends inheritable, and local
         ends not inheritable. */
 
       /* Stupid Win32 allows to create a pipe with *both* ends either
         inheritable or not. We need process ends inheritable, and local
         ends not inheritable. */
-      DuplicateHandle (GetCurrentProcess(), hmyshove, GetCurrentProcess(), &htmp,
-                      0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);
+      DuplicateHandle (GetCurrentProcess(), hmyshove, GetCurrentProcess(),
+                      &htmp, 0, FALSE,
+                      DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);
       hmyshove = htmp;
       hmyshove = htmp;
-      DuplicateHandle (GetCurrentProcess(), hmyslurp, GetCurrentProcess(), &htmp,
-                      0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);
+      DuplicateHandle (GetCurrentProcess(), hmyslurp, GetCurrentProcess(),
+                      &htmp, 0, FALSE,
+                      DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);
       hmyslurp = htmp;
     }
 
   /* Convert an argv vector into Win32 style command line by a call to
       hmyslurp = htmp;
     }
 
   /* Convert an argv vector into Win32 style command line by a call to
-     lisp function `nt-quote-process-args' which see (in winnt.el)*/
+     lisp function `mswindows-construct-process-command-line'
+     (in win32-native.el) */
   {
     int i;
     Lisp_Object args_or_ret = Qnil;
   {
     int i;
     Lisp_Object args_or_ret = Qnil;
@@ -502,17 +803,16 @@ nt_create_process (struct Lisp_Process *p,
     args_or_ret = Fnreverse (args_or_ret);
     args_or_ret = Fcons (program, args_or_ret);
 
     args_or_ret = Fnreverse (args_or_ret);
     args_or_ret = Fcons (program, args_or_ret);
 
-    args_or_ret = call1 (Qnt_quote_process_args, args_or_ret);
+    args_or_ret = call1 (Qmswindows_construct_process_command_line,
+                        args_or_ret);
 
     if (!STRINGP (args_or_ret))
       /* Luser wrote his/her own clever version */
 
     if (!STRINGP (args_or_ret))
       /* Luser wrote his/her own clever version */
-      error ("Bogus return value from `nt-quote-process-args'");
+      invalid_argument
+       ("Bogus return value from `mswindows-construct-process-command-line'",
+        args_or_ret);
 
 
-    command_line = alloca_array (char, (XSTRING_LENGTH (program)
-                                       + XSTRING_LENGTH (args_or_ret) + 2));
-    strcpy (command_line, XSTRING_DATA (program));
-    strcat (command_line, " ");
-    strcat (command_line, XSTRING_DATA (args_or_ret));
+    LISP_STRING_TO_EXTERNAL (args_or_ret, command_line, Qmswindows_tstr);
 
     UNGCPRO; /* args_or_ret */
   }
 
     UNGCPRO; /* args_or_ret */
   }
@@ -521,7 +821,6 @@ nt_create_process (struct Lisp_Process *p,
      Vprocess_environment terminated by 2 nuls.  */
  
   {
      Vprocess_environment terminated by 2 nuls.  */
  
   {
-    extern int compare_env (const char **strp1, const char **strp2);
     char **env;
     REGISTER Lisp_Object tem;
     REGISTER char **new_env;
     char **env;
     REGISTER Lisp_Object tem;
     REGISTER char **new_env;
@@ -533,6 +832,17 @@ nt_create_process (struct Lisp_Process *p,
          && STRINGP (XCAR (tem)));
         tem = XCDR (tem))
       new_length++;
          && STRINGP (XCAR (tem)));
         tem = XCDR (tem))
       new_length++;
+
+    /* FSF adds an extra env var to hold the current process ID of the
+       Emacs process.  Apparently this is used only by emacsserver.c,
+       which we have superseded to gnuserv.c. (#### Does it work under
+       MS Windows?)
+
+       sprintf (ppid_env_var_buffer, "EM_PARENT_PROCESS_ID=%d", 
+         GetCurrentProcessId ());
+       arglen += strlen (ppid_env_var_buffer) + 1;
+       numenv++;
+    */
     
     /* new_length + 1 to include terminating 0.  */
     env = new_env = alloca_array (char *, new_length + 1);
     
     /* new_length + 1 to include terminating 0.  */
     env = new_env = alloca_array (char *, new_length + 1);
@@ -582,7 +892,7 @@ nt_create_process (struct Lisp_Process *p,
     new_space++;
     
     /* Allocate space and copy variables into it */
     new_space++;
     
     /* Allocate space and copy variables into it */
-    penv = proc_env = alloca(new_space);
+    penv = proc_env = (char*) alloca(new_space);
     for (i = 0; i < new_length; i++)
       {
        strcpy(penv, env[i]);
     for (i = 0; i < new_length; i++)
       {
        strcpy(penv, env[i]);
@@ -590,12 +900,31 @@ nt_create_process (struct Lisp_Process *p,
       }
     *penv = 0;
   }
       }
     *penv = 0;
   }
+
+#if 0
+    /* #### we need to port this. */
+    /* On Windows 95, if cmdname is a DOS app, we invoke a helper
+       application to start it by specifying the helper app as cmdname,
+       while leaving the real app name as argv[0].  */
+    if (is_dos_app)
+      {
+       cmdname = (char*) alloca (MAXPATHLEN);
+       if (egetenv ("CMDPROXY"))
+         strcpy ((char*)cmdname, egetenv ("CMDPROXY"));
+       else
+         {
+           strcpy ((char*)cmdname, XSTRING_DATA (Vinvocation_directory));
+           strcat ((char*)cmdname, "cmdproxy.exe");
+         }
+      }
+#endif
   
   /* Create process */
   {
     STARTUPINFO si;
     PROCESS_INFORMATION pi;
     DWORD err;
   
   /* Create process */
   {
     STARTUPINFO si;
     PROCESS_INFORMATION pi;
     DWORD err;
+    DWORD flags;
 
     xzero (si);
     si.dwFlags = STARTF_USESHOWWINDOW;
 
     xzero (si);
     si.dwFlags = STARTF_USESHOWWINDOW;
@@ -608,9 +937,19 @@ nt_create_process (struct Lisp_Process *p,
        si.dwFlags |= STARTF_USESTDHANDLES;
       }
 
        si.dwFlags |= STARTF_USESTDHANDLES;
       }
 
-    err = (CreateProcess (NULL, command_line, NULL, NULL, TRUE,
-                         CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP
-                         | CREATE_SUSPENDED,
+    flags = CREATE_SUSPENDED;
+    if (mswindows_windows9x_p ())
+      flags |= (!NILP (Vmswindows_start_process_share_console)
+               ? CREATE_NEW_PROCESS_GROUP
+               : CREATE_NEW_CONSOLE);
+    else
+      flags |= CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP;
+    if (NILP (Vmswindows_start_process_inherit_error_mode))
+      flags |= CREATE_DEFAULT_ERROR_MODE;
+
+    ensure_console_window_exists ();
+
+    err = (CreateProcess (NULL, command_line, NULL, NULL, TRUE, flags,
                          proc_env, (char *) XSTRING_DATA (cur_dir), &si, &pi)
           ? 0 : GetLastError ());
 
                          proc_env, (char *) XSTRING_DATA (cur_dir), &si, &pi)
           ? 0 : GetLastError ());
 
@@ -637,6 +976,7 @@ nt_create_process (struct Lisp_Process *p,
     if (do_io)
       {
        NT_DATA(p)->h_process = pi.hProcess;
     if (do_io)
       {
        NT_DATA(p)->h_process = pi.hProcess;
+       NT_DATA(p)->dwProcessId = pi.dwProcessId;
        init_process_io_handles (p, (void*)hmyslurp, (void*)hmyshove, 0);
       }
     else
        init_process_io_handles (p, (void*)hmyslurp, (void*)hmyshove, 0);
       }
     else
@@ -646,18 +986,12 @@ nt_create_process (struct Lisp_Process *p,
        CloseHandle (pi.hProcess);
       }
 
        CloseHandle (pi.hProcess);
       }
 
+    if (!windowed)
+      enable_child_signals (pi.hProcess);
+
     ResumeThread (pi.hThread);
     CloseHandle (pi.hThread);
 
     ResumeThread (pi.hThread);
     CloseHandle (pi.hThread);
 
-    /* Remember to enable child signals later if this is not a windowed
-       app.  Can't do it right now because that screws up the MKS Toolkit
-       shell. */
-    if (!windowed)
-      {
-       NT_DATA(p)->need_enable_child_signals = 10;
-       kick_status_notify ();
-      }
-
     return ((int)pi.dwProcessId);
   }
 }
     return ((int)pi.dwProcessId);
   }
 }
@@ -671,21 +1005,9 @@ nt_create_process (struct Lisp_Process *p,
  */
 
 static void
  */
 
 static void
-nt_update_status_if_terminated (struct Lisp_Process* p)
+nt_update_status_if_terminated (Lisp_Process* p)
 {
   DWORD exit_code;
 {
   DWORD exit_code;
-
-  if (NT_DATA(p)->need_enable_child_signals > 1)
-    {
-      NT_DATA(p)->need_enable_child_signals -= 1;
-      kick_status_notify ();
-    }
-  else if (NT_DATA(p)->need_enable_child_signals == 1)
-    {
-      enable_child_signals(NT_DATA(p)->h_process);
-      NT_DATA(p)->need_enable_child_signals = 0;
-    }
-
   if (GetExitCodeProcess (NT_DATA(p)->h_process, &exit_code)
       && exit_code != STILL_ACTIVE)
     {
   if (GetExitCodeProcess (NT_DATA(p)->h_process, &exit_code)
       && exit_code != STILL_ACTIVE)
     {
@@ -718,21 +1040,21 @@ static void
 nt_send_process (Lisp_Object proc, struct lstream* lstream)
 {
   volatile Lisp_Object vol_proc = proc;
 nt_send_process (Lisp_Object proc, struct lstream* lstream)
 {
   volatile Lisp_Object vol_proc = proc;
-  struct Lisp_Process *volatile p = XPROCESS (proc);
+  Lisp_Process *volatile p = XPROCESS (proc);
 
   /* use a reasonable-sized buffer (somewhere around the size of the
      stream buffer) so as to avoid inundating the stream with blocked
      data. */
 
   /* use a reasonable-sized buffer (somewhere around the size of the
      stream buffer) so as to avoid inundating the stream with blocked
      data. */
-  Bufbyte chunkbuf[128];
+  Bufbyte chunkbuf[512];
   Bytecount chunklen;
 
   while (1)
     {
   Bytecount chunklen;
 
   while (1)
     {
-      ssize_t writeret;
+      Lstream_data_count writeret;
 
 
-      chunklen = Lstream_read (lstream, chunkbuf, 128);
+      chunklen = Lstream_read (lstream, chunkbuf, 512);
       if (chunklen <= 0)
       if (chunklen <= 0)
-       break; /* perhaps should abort() if < 0?
+       break; /* perhaps should ABORT() if < 0?
                  This should never happen. */
 
       /* Lstream_write() will never successfully write less than the
                  This should never happen. */
 
       /* Lstream_write() will never successfully write less than the
@@ -749,8 +1071,8 @@ nt_send_process (Lisp_Object proc, struct lstream* lstream)
          p->tick++;
          process_tick++;
          deactivate_process (*((Lisp_Object *) (&vol_proc)));
          p->tick++;
          process_tick++;
          deactivate_process (*((Lisp_Object *) (&vol_proc)));
-         error ("Broken pipe error sending to process %s; closed it",
-                XSTRING_DATA (p->name));
+         invalid_operation ("Broken pipe error sending to process; closed it",
+                            p->name);
        }
 
       {
        }
 
       {
@@ -786,26 +1108,18 @@ static void
 nt_kill_child_process (Lisp_Object proc, int signo,
                       int current_group, int nomsg)
 {
 nt_kill_child_process (Lisp_Object proc, int signo,
                       int current_group, int nomsg)
 {
-  struct Lisp_Process *p = XPROCESS (proc);
-
-  /* Enable child signals if necessary.  This may lose the first
-     but it's better than nothing. */
-  if (NT_DATA(p)->need_enable_child_signals > 0)
-    {
-      enable_child_signals(NT_DATA(p)->h_process);
-      NT_DATA(p)->need_enable_child_signals = 0;
-    }
+  Lisp_Process *p = XPROCESS (proc);
 
   /* Signal error if SIGNO cannot be sent */
   validate_signal_number (signo);
 
   /* Send signal */
 
   /* Signal error if SIGNO cannot be sent */
   validate_signal_number (signo);
 
   /* Send signal */
-  if (!send_signal (NT_DATA(p)->h_process, signo))
-    error ("Cannot send signal to process");
+  if (!send_signal (NT_DATA (p), 0, signo))
+    invalid_operation ("Cannot send signal to process", proc);
 }
 
 /*
 }
 
 /*
- * Kill any process in the system given its PID.
+ * Kill any process in the system given its PID
  *
  * Returns zero if a signal successfully sent, or
  * negative number upon failure
  *
  * Returns zero if a signal successfully sent, or
  * negative number upon failure
@@ -813,26 +1127,13 @@ nt_kill_child_process (Lisp_Object proc, int signo,
 static int
 nt_kill_process_by_pid (int pid, int signo)
 {
 static int
 nt_kill_process_by_pid (int pid, int signo)
 {
-  HANDLE h_process;
-  int send_result;
-  
+  struct Lisp_Process *p;
+
   /* Signal error if SIGNO cannot be sent */
   validate_signal_number (signo);
 
   /* Signal error if SIGNO cannot be sent */
   validate_signal_number (signo);
 
-  /* Try to open the process with required privileges */
-  h_process = OpenProcess (PROCESS_CREATE_THREAD
-                          | PROCESS_QUERY_INFORMATION 
-                          | PROCESS_VM_OPERATION
-                          | PROCESS_VM_WRITE,
-                          FALSE, pid);
-  if (h_process == NULL)
-    return -1;
-  
-  send_result = send_signal (h_process, signo);
-  
-  CloseHandle (h_process);
-
-  return send_result ? 0 : -1;
+  p = find_process_from_pid (pid);
+  return send_signal (p ? NT_DATA (p) : 0, pid, signo) ? 0 : -1;
 }
 \f
 /*-----------------------------------------------------------------------*/
 }
 \f
 /*-----------------------------------------------------------------------*/
@@ -943,9 +1244,11 @@ nt_canonicalize_host_name (Lisp_Object host)
    deactivate and close it via delete-process */
 
 static void
    deactivate and close it via delete-process */
 
 static void
-nt_open_network_stream (Lisp_Object name, Lisp_Object host, Lisp_Object service,
+nt_open_network_stream (Lisp_Object name, Lisp_Object host,
+                       Lisp_Object service,
                        Lisp_Object protocol, void** vinfd, void** voutfd)
 {
                        Lisp_Object protocol, void** vinfd, void** voutfd)
 {
+  /* !!#### not Mule-ized */
   struct sockaddr_in address;
   SOCKET s;
   int port;
   struct sockaddr_in address;
   SOCKET s;
   int port;
@@ -954,8 +1257,7 @@ nt_open_network_stream (Lisp_Object name, Lisp_Object host, Lisp_Object service,
   CHECK_STRING (host);
 
   if (!EQ (protocol, Qtcp))
   CHECK_STRING (host);
 
   if (!EQ (protocol, Qtcp))
-    error ("Unsupported protocol \"%s\"",
-          string_data (symbol_name (XSYMBOL (protocol))));
+    invalid_argument ("Unsupported protocol", protocol);
 
   if (INTP (service))
     port = htons ((unsigned short) XINT (service));
 
   if (INTP (service))
     port = htons ((unsigned short) XINT (service));
@@ -965,7 +1267,7 @@ nt_open_network_stream (Lisp_Object name, Lisp_Object host, Lisp_Object service,
       CHECK_STRING (service);
       svc_info = getservbyname ((char *) XSTRING_DATA (service), "tcp");
       if (svc_info == 0)
       CHECK_STRING (service);
       svc_info = getservbyname ((char *) XSTRING_DATA (service), "tcp");
       if (svc_info == 0)
-       error ("Unknown service \"%s\"", XSTRING_DATA (service));
+       invalid_argument ("Unknown service", service);
       port = svc_info->s_port;
     }
 
       port = svc_info->s_port;
     }
 
@@ -1027,18 +1329,20 @@ nt_open_network_stream (Lisp_Object name, Lisp_Object host, Lisp_Object service,
 
  connect_failed:  
   closesocket (s);
 
  connect_failed:  
   closesocket (s);
-  if (INTP (service)) {
-    warn_when_safe(Qstream, Qwarning,
-                  "failure to open network stream to host \"%s\" for service \"%d\"",
-                  XSTRING_DATA (host),
-                  (unsigned short) XINT (service));
-  }
-  else {
-    warn_when_safe(Qstream, Qwarning,
-                  "failure to open network stream to host \"%s\" for service \"%s\"",
-                  XSTRING_DATA (host),
-                  XSTRING_DATA (service));
-  }
+  if (INTP (service))
+    {
+      warn_when_safe (Qstream, Qwarning,
+                     "failure to open network stream to host \"%s\" for service \"%d\"",
+                     XSTRING_DATA (host),
+                     (unsigned short) XINT (service));
+    }
+  else
+    {
+      warn_when_safe (Qstream, Qwarning,
+                     "failure to open network stream to host \"%s\" for service \"%s\"",
+                     XSTRING_DATA (host),
+                     XSTRING_DATA (service));
+    }
   report_file_error ("connection failed", list2 (host, name));
 }
 
   report_file_error ("connection failed", list2 (host, name));
 }
 
@@ -1072,10 +1376,28 @@ process_type_create_nt (void)
 void
 syms_of_process_nt (void)
 {
 void
 syms_of_process_nt (void)
 {
-  defsymbol (&Qnt_quote_process_args, "nt-quote-process-args");
+  DEFSYMBOL (Qmswindows_construct_process_command_line);
 }
 
 void
 vars_of_process_nt (void)
 {
 }
 
 void
 vars_of_process_nt (void)
 {
+  DEFVAR_LISP ("mswindows-start-process-share-console",
+              &Vmswindows_start_process_share_console /*
+When nil, new child processes are given a new console.
+When non-nil, they share the Emacs console; this has the limitation of
+allowing only one DOS subprocess to run at a time (whether started directly
+or indirectly by Emacs), and preventing Emacs from cleanly terminating the
+subprocess group, but may allow Emacs to interrupt a subprocess that doesn't
+otherwise respond to interrupts from Emacs.
+*/ );
+  Vmswindows_start_process_share_console = Qnil;
+
+  DEFVAR_LISP ("mswindows-start-process-inherit-error-mode",
+              &Vmswindows_start_process_inherit_error_mode /*
+    "When nil, new child processes revert to the default error mode.
+When non-nil, they inherit their error mode setting from Emacs, which stops
+them blocking when trying to access unmounted drives etc.
+*/ );
+  Vmswindows_start_process_inherit_error_mode = Qt;
 }
 }