This commit was generated by cvs2svn to compensate for changes in r1383,
[chise/xemacs-chise.git.1] / src / process-nt.c
index 4435a25..f7ab6a5 100644 (file)
@@ -1,4 +1,4 @@
-/* Asynchronous subprocess implemenation for Win32
+/* Asynchronous subprocess implementation for Win32
    Copyright (C) 1985, 1986, 1987, 1988, 1992, 1993, 1994, 1995
    Free Software Foundation, Inc.
    Copyright (C) 1995 Sun Microsystems, Inc.
@@ -32,20 +32,27 @@ Boston, MA 02111-1307, USA.  */
 #include "procimpl.h"
 #include "sysdep.h"
 
-#include <windows.h>
+#ifndef __MINGW32__
 #include <shellapi.h>
+#else
+#include <errno.h>
+#endif
 #include <signal.h>
 #ifdef HAVE_SOCKETS
 #include <winsock.h>
 #endif
 
+/* 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;
 
-/* Implemenation-specific data. Pointed to by Lisp_Process->process_data */
+/* Implementation-specific data. Pointed to by Lisp_Process->process_data */
 struct nt_process_data
 {
   HANDLE h_process;
+  int need_enable_child_signals;
 };
 
 #define NT_DATA(p) ((struct nt_process_data*)((p)->process_data))
@@ -57,7 +64,7 @@ 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
-get_nt_process_handle (struct Lisp_Process *p)
+get_nt_process_handle (Lisp_Process *p)
 {
   return (NT_DATA (p)->h_process);
 }
@@ -143,8 +150,8 @@ free_process_memory (process_memory* pmc)
 
 /*
  * Run ROUTINE in the context of process determined by H_PROCESS. The
- * routine is passed the address of DATA as parameter. CODE_END is the 
- * address immediately after ROUTINE's code. DATA_SIZE is the size of
+ * routine is passed the address of DATA as parameter. The ROUTINE must
+ * not be longer than ROUTINE_CODE_SIZE bytes. DATA_SIZE is the size of
  * DATA structure.
  *
  * Note that the code must be positionally independent, and compiled
@@ -157,11 +164,11 @@ free_process_memory (process_memory* pmc)
  */
 static DWORD
 run_in_other_process (HANDLE h_process,
-                     LPTHREAD_START_ROUTINE routine, LPVOID code_end,
+                     LPTHREAD_START_ROUTINE routine,
                      LPVOID data, size_t data_size)
 {
   process_memory pm;
-  size_t code_size = (LPBYTE)code_end - (LPBYTE)routine;
+  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;
@@ -223,6 +230,11 @@ run_in_other_process (HANDLE h_process,
  * SIGKILL, SIGTERM, SIGQUIT, SIGHUP - These four translate to ExitProcess
  *    executed by the remote process
  * SIGINT - The remote process is sent CTRL_BREAK_EVENT
+ *
+ * The MSVC5.0 compiler feels free to re-order functions within a
+ * compilation unit, so we have no way of finding out the size of the
+ * following functions. Therefore these functions must not be larger than
+ * FRAGMENT_CODE_SIZE.
  */
 
 /*
@@ -240,12 +252,6 @@ sigkill_proc (sigkill_data* data)
   return 1;
 }
 
-/* Watermark in code space */
-static void
-sigkill_code_end (void)
-{
-}
-
 /*
  * Sending break or control c
  */
@@ -261,12 +267,6 @@ sigint_proc (sigint_data* data)
   return (*data->adr_GenerateConsoleCtrlEvent) (data->event, 0);
 }
 
-/* Watermark in code space */
-static void
-sigint_code_end (void)
-{
-}
-
 /*
  * Enabling signals
  */
@@ -282,12 +282,6 @@ sig_enable_proc (sig_enable_data* data)
   return 1;
 }
 
-/* Watermark in code space */
-static void
-sig_enable_code_end (void)
-{
-}
-
 /*
  * Send signal SIGNO to process H_PROCESS.
  * Return nonzero if successful.
@@ -316,8 +310,8 @@ send_signal (HANDLE h_process, int signo)
        sigkill_data d;
        d.adr_ExitProcess = GetProcAddress (h_kernel, "ExitProcess");
        assert (d.adr_ExitProcess);
-       retval = run_in_other_process (h_process,
-                                      sigkill_proc, sigkill_code_end,
+       retval = run_in_other_process (h_process, 
+                                      (LPTHREAD_START_ROUTINE)sigkill_proc,
                                       &d, sizeof (d));
        break;
       }
@@ -328,8 +322,8 @@ send_signal (HANDLE h_process, int signo)
          GetProcAddress (h_kernel, "GenerateConsoleCtrlEvent");
        assert (d.adr_GenerateConsoleCtrlEvent);
        d.event = CTRL_C_EVENT;
-       retval = run_in_other_process (h_process,
-                                      sigint_proc, sigint_code_end,
+       retval = run_in_other_process (h_process, 
+                                      (LPTHREAD_START_ROUTINE)sigint_proc,
                                       &d, sizeof (d));
        break;
       }
@@ -353,8 +347,7 @@ enable_child_signals (HANDLE h_process)
   d.adr_SetConsoleCtrlHandler =
     GetProcAddress (h_kernel, "SetConsoleCtrlHandler");
   assert (d.adr_SetConsoleCtrlHandler);
-  run_in_other_process (h_process,
-                       sig_enable_proc, sig_enable_code_end,
+  run_in_other_process (h_process, (LPTHREAD_START_ROUTINE)sig_enable_proc,
                        &d, sizeof (d));
 }
   
@@ -381,13 +374,13 @@ validate_signal_number (int signo)
  */
 
 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
-nt_finalize_process_data (struct Lisp_Process *p, int for_disksave)
+nt_finalize_process_data (Lisp_Process *p, int for_disksave)
 {
   assert (!for_disksave);
   if (NT_DATA(p)->h_process)
@@ -395,7 +388,7 @@ nt_finalize_process_data (struct Lisp_Process *p, int for_disksave)
 }
 
 /*
- * Initialize XEmacs process implemenation once
+ * Initialize XEmacs process implementation once
  */
 static void
 nt_init_process (void)
@@ -411,13 +404,11 @@ nt_init_process (void)
  * object. If this function signals, the caller is responsible for
  * deleting (and finalizing) the process object.
  *
- * The method must return PID of the new proces, a (positive??? ####) number
+ * The method must return PID of the new process, a (positive??? ####) number
  * which fits into Lisp_Int. No return value indicates an error, the method
  * must signal an error instead.
  */
 
-/* #### This function completely ignores Vprocess_environment */
-
 static void
 signal_cannot_launch (Lisp_Object image_file, DWORD err)
 {
@@ -426,13 +417,14 @@ signal_cannot_launch (Lisp_Object image_file, DWORD err)
 }
 
 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)
 {
-  HANDLE hmyshove, hmyslurp, hprocin, hprocout;
+  HANDLE hmyshove, hmyslurp, hprocin, hprocout, hprocerr;
   LPTSTR command_line;
   BOOL do_io, windowed;
+  char *proc_env;
 
   /* Find out whether the application is windowed or not */
   {
@@ -480,6 +472,10 @@ nt_create_process (struct Lisp_Process *p,
       CreatePipe (&hprocin, &hmyshove, &sa, 0);
       CreatePipe (&hmyslurp, &hprocout, &sa, 0);
 
+      /* Duplicate the stdout handle for use as stderr */
+      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. */
@@ -520,6 +516,80 @@ nt_create_process (struct Lisp_Process *p,
     UNGCPRO; /* args_or_ret */
   }
 
+  /* Set `proc_env' to a nul-separated array of the strings in
+     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;
+    REGISTER int new_length = 0, i, new_space;
+    char *penv;
+    
+    for (tem = Vprocess_environment;
+        (CONSP (tem)
+         && STRINGP (XCAR (tem)));
+        tem = XCDR (tem))
+      new_length++;
+    
+    /* new_length + 1 to include terminating 0.  */
+    env = new_env = alloca_array (char *, new_length + 1);
+    /* Copy the Vprocess_environment strings into new_env.  */
+    for (tem = Vprocess_environment;
+        (CONSP (tem)
+         && STRINGP (XCAR (tem)));
+        tem = XCDR (tem))
+      {
+       char **ep = env;
+       char *string = (char *) XSTRING_DATA (XCAR (tem));
+       /* See if this string duplicates any string already in the env.
+          If so, don't put it in.
+          When an env var has multiple definitions,
+          we keep the definition that comes first in process-environment.  */
+       for (; ep != new_env; ep++)
+         {
+           char *p = *ep, *q = string;
+           while (1)
+             {
+               if (*q == 0)
+                 /* The string is malformed; might as well drop it.  */
+                 goto duplicate;
+               if (*q != *p)
+                 break;
+               if (*q == '=')
+                 goto duplicate;
+               p++, q++;
+             }
+         }
+       *new_env++ = string;
+      duplicate: ;
+      }
+    *new_env = 0;
+    
+    /* Sort the environment variables */
+    new_length = new_env - env;
+    qsort (env, new_length, sizeof (char *), compare_env);
+    
+    /* Work out how much space to allocate */
+    new_space = 0;
+    for (i = 0; i < new_length; i++)
+      {
+       new_space += strlen(env[i]) + 1;
+      }
+    new_space++;
+    
+    /* Allocate space and copy variables into it */
+    penv = proc_env = (char*) alloca(new_space);
+    for (i = 0; i < new_length; i++)
+      {
+       strcpy(penv, env[i]);
+       penv += strlen(env[i]) + 1;
+      }
+    *penv = 0;
+  }
+  
   /* Create process */
   {
     STARTUPINFO si;
@@ -533,14 +603,14 @@ nt_create_process (struct Lisp_Process *p,
       {
        si.hStdInput = hprocin;
        si.hStdOutput = hprocout;
-       si.hStdError = hprocout;
+       si.hStdError = hprocerr;
        si.dwFlags |= STARTF_USESTDHANDLES;
       }
 
     err = (CreateProcess (NULL, command_line, NULL, NULL, TRUE,
                          CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP
                          | CREATE_SUSPENDED,
-                         NULL, (char *) XSTRING_DATA (cur_dir), &si, &pi)
+                         proc_env, (char *) XSTRING_DATA (cur_dir), &si, &pi)
           ? 0 : GetLastError ());
 
     if (do_io)
@@ -548,6 +618,7 @@ nt_create_process (struct Lisp_Process *p,
        /* These just have been inherited; we do not need a copy */
        CloseHandle (hprocin);
        CloseHandle (hprocout);
+       CloseHandle (hprocerr);
       }
     
     /* Handle process creation failure */
@@ -574,15 +645,19 @@ nt_create_process (struct Lisp_Process *p,
        CloseHandle (pi.hProcess);
       }
 
-    if (!windowed)
-      enable_child_signals (pi.hProcess);
-
     ResumeThread (pi.hThread);
     CloseHandle (pi.hThread);
 
-    /* Hack to support Windows 95 negative pids */
-    return ((int)pi.dwProcessId < 0
-           ? -(int)pi.dwProcessId : (int)pi.dwProcessId);
+    /* 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);
   }
 }
 
@@ -595,9 +670,21 @@ nt_create_process (struct Lisp_Process *p,
  */
 
 static void
-nt_update_status_if_terminated (struct Lisp_Process* p)
+nt_update_status_if_terminated (Lisp_Process* p)
 {
   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)
     {
@@ -620,7 +707,7 @@ nt_update_status_if_terminated (struct Lisp_Process* p)
 }
 
 /*
- * Stuff the entire contents of LSTREAM to the process ouptut pipe
+ * Stuff the entire contents of LSTREAM to the process output pipe
  */
 
 /* #### If only this function could be somehow merged with
@@ -629,19 +716,20 @@ nt_update_status_if_terminated (struct Lisp_Process* p)
 static void
 nt_send_process (Lisp_Object proc, struct lstream* lstream)
 {
-  struct Lisp_Process *p = XPROCESS (proc);
+  volatile Lisp_Object vol_proc = 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. */
-  Bufbyte chunkbuf[512];
+  Bufbyte chunkbuf[128];
   Bytecount chunklen;
 
   while (1)
     {
-      int writeret;
+      ssize_t writeret;
 
-      chunklen = Lstream_read (lstream, chunkbuf, 512);
+      chunklen = Lstream_read (lstream, chunkbuf, 128);
       if (chunklen <= 0)
        break; /* perhaps should abort() if < 0?
                  This should never happen. */
@@ -659,7 +747,7 @@ nt_send_process (Lisp_Object proc, struct lstream* lstream)
          p->core_dumped = 0;
          p->tick++;
          process_tick++;
-         deactivate_process (proc);
+         deactivate_process (*((Lisp_Object *) (&vol_proc)));
          error ("Broken pipe error sending to process %s; closed it",
                 XSTRING_DATA (p->name));
        }
@@ -697,7 +785,15 @@ static void
 nt_kill_child_process (Lisp_Object proc, int signo,
                       int current_group, int nomsg)
 {
-  struct Lisp_Process *p = XPROCESS (proc);
+  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;
+    }
 
   /* Signal error if SIGNO cannot be sent */
   validate_signal_number (signo);
@@ -792,6 +888,12 @@ get_internet_address (Lisp_Object host, struct sockaddr_in *address,
          /* Ok, got an answer */
          if (WSAGETASYNCERROR(msg.lParam) == NO_ERROR)
            success = 1;
+         else
+           {
+             warn_when_safe(Qstream, Qwarning,
+                            "cannot get IP address for host \"%s\"",
+                            XSTRING_DATA (host));
+           }
          goto done;
        }
       else if (msg.message == WM_TIMER && msg.wParam == SOCK_TIMER_ID)
@@ -841,7 +943,7 @@ nt_canonicalize_host_name (Lisp_Object host)
 
 static void
 nt_open_network_stream (Lisp_Object name, Lisp_Object host, Lisp_Object service,
-                       Lisp_Object family, void** vinfd, void** voutfd)
+                       Lisp_Object protocol, void** vinfd, void** voutfd)
 {
   struct sockaddr_in address;
   SOCKET s;
@@ -850,9 +952,9 @@ nt_open_network_stream (Lisp_Object name, Lisp_Object host, Lisp_Object service,
 
   CHECK_STRING (host);
 
-  if (!EQ (family, Qtcpip))
-    error ("Unsupported protocol family \"%s\"",
-          string_data (symbol_name (XSYMBOL (family))));
+  if (!EQ (protocol, Qtcp))
+    error ("Unsupported protocol \"%s\"",
+          string_data (symbol_name (XSYMBOL (protocol))));
 
   if (INTP (service))
     port = htons ((unsigned short) XINT (service));
@@ -875,14 +977,13 @@ nt_open_network_stream (Lisp_Object name, Lisp_Object host, Lisp_Object service,
 
   /* We don't want to be blocked on connect */
   {
-    unsigned int nonblock = 1;
+    unsigned long nonblock = 1;
     ioctlsocket (s, FIONBIO, &nonblock);
   }
   
   retval = connect (s, (struct sockaddr *) &address, sizeof (address));
   if (retval != NO_ERROR && WSAGetLastError() != WSAEWOULDBLOCK)
     goto connect_failed;
-
   /* Wait while connection is established */
   while (1)
     {
@@ -906,7 +1007,7 @@ nt_open_network_stream (Lisp_Object name, Lisp_Object host, Lisp_Object service,
 
       if (nsel > 0)
        {
-         /* Check was connnection successful or not */
+         /* Check: was connection successful or not? */
          tv.tv_usec = 0;
          nsel = select (0, NULL, NULL, &fdset, &tv);
          if (nsel > 0)
@@ -925,6 +1026,18 @@ nt_open_network_stream (Lisp_Object name, Lisp_Object host, Lisp_Object service,
 
  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));
+  }
   report_file_error ("connection failed", list2 (host, name));
 }