(last_event_timestamp): Declare, but don't define.
[bpt/emacs.git] / src / w32proc.c
index ee0f5f7..01ffd33 100644 (file)
@@ -45,6 +45,25 @@ Boston, MA 02111-1307, USA.
 #include "syswait.h"
 #include "process.h"
 
+/* Control whether spawnve quotes arguments as necessary to ensure
+   correct parsing by child process.  Because not all uses of spawnve
+   are careful about constructing argv arrays, we make this behaviour
+   conditional (off by default). */
+Lisp_Object Vwin32_quote_process_args;
+
+/* Time to sleep before reading from a subprocess output pipe - this
+   avoids the inefficiency of frequently reading small amounts of data.
+   This is primarily necessary for handling DOS processes on Windows 95,
+   but is useful for Win32 processes on both Win95 and NT as well.  */
+Lisp_Object Vwin32_pipe_read_delay;
+
+/* Control conversion of upper case file names to lower case.
+   nil means no, t means yes. */
+Lisp_Object Vwin32_downcase_file_names;
+
+/* Keep track of whether we have already started a DOS program. */
+BOOL dos_process_running;
+
 #ifndef SYS_SIGLIST_DECLARED
 extern char *sys_siglist[];
 #endif
@@ -361,6 +380,11 @@ reap_subprocess (child_process *cp)
       cp->procinfo.hProcess = NULL;
       CloseHandle (cp->procinfo.hThread);
       cp->procinfo.hThread = NULL;
+
+      /* If this was a DOS process, indicate that it is now safe to
+        start a new one.  */
+      if (cp->is_dos_process)
+       dos_process_running = FALSE;
     }
 
   /* For asynchronous children, the child_proc resources will be freed
@@ -498,6 +522,54 @@ sys_wait (int *status)
   return pid;
 }
 
+int
+win32_is_dos_binary (char * filename)
+{
+  IMAGE_DOS_HEADER dos_header;
+  DWORD signature;
+  int fd;
+  int is_dos_binary = FALSE;
+
+  fd = open (filename, O_RDONLY | O_BINARY, 0);
+  if (fd >= 0)
+    {
+      char * p = strrchr (filename, '.');
+
+      /* We can only identify DOS .com programs from the extension. */
+      if (p && stricmp (p, ".com") == 0)
+       is_dos_binary = TRUE;
+      else if (p && stricmp (p, ".bat") == 0)
+       {
+         /* A DOS shell script - it appears that CreateProcess is happy
+            to accept this (somewhat surprisingly); presumably it looks
+            at COMSPEC to determine what executable to actually invoke.
+            Therefore, we have to do the same here as well. */
+         p = getenv ("COMSPEC");
+         if (p)
+           is_dos_binary = win32_is_dos_binary (p);
+       }
+      else
+       {
+         /* Look for DOS .exe signature - if found, we must also check
+            that it isn't really a 16- or 32-bit Windows exe, since
+            both formats start with a DOS program stub.  Note that
+            16-bit Windows executables use the OS/2 1.x format. */
+         if (read (fd, &dos_header, sizeof (dos_header)) == sizeof (dos_header)
+             && dos_header.e_magic == IMAGE_DOS_SIGNATURE
+             && lseek (fd, dos_header.e_lfanew, SEEK_SET) != -1)
+           {
+             if (read (fd, &signature, sizeof (signature)) != sizeof (signature)
+                 || (signature != IMAGE_NT_SIGNATURE &&
+                     LOWORD (signature) != IMAGE_OS2_SIGNATURE))
+               is_dos_binary = TRUE;
+           }
+       }
+      close (fd);
+    }
+
+  return is_dos_binary;
+}
+
 /* We pass our process ID to our children by setting up an environment
    variable in their environment.  */
 char ppid_env_var_buffer[64];
@@ -512,6 +584,7 @@ sys_spawnve (int mode, char *cmdname, char **argv, char **envp)
   int arglen;
   int pid;
   child_process *cp;
+  int is_dos_binary;
   
   /* We don't care about the other modes */
   if (mode != _P_NOWAIT)
@@ -543,6 +616,15 @@ sys_spawnve (int mode, char *cmdname, char **argv, char **envp)
   strcpy (cmdname = alloca (strlen (cmdname) + 1), argv[0]);
   unixtodos_filename (cmdname);
   argv[0] = cmdname;
+
+  /* Check if program is a DOS executable, and if so whether we are
+     allowed to start it. */
+  is_dos_binary = win32_is_dos_binary (cmdname);
+  if (is_dos_binary && dos_process_running)
+    {
+      errno = EAGAIN;
+      return -1;
+    }
   
   /* 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
@@ -595,16 +677,18 @@ sys_spawnve (int mode, char *cmdname, char **argv, char **envp)
 
       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 (!NILP (Vwin32_quote_process_args))
+       {
+         /* This is conditional because it sometimes causes more
+            problems than it solves, since argv arrays are not always
+            carefully constructed.  M-x 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;
+       }
       if (add_quotes)
        {
          char * first;
@@ -671,6 +755,12 @@ sys_spawnve (int mode, char *cmdname, char **argv, char **envp)
       errno = ENOEXEC;
       return -1;
     }
+
+  if (is_dos_binary)
+    {
+      cp->is_dos_process = TRUE;
+      dos_process_running = TRUE;
+    }
   
   return pid;
 }
@@ -963,9 +1053,11 @@ sys_kill (int pid, int sig)
   else
     {
       /* 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 (proc_hand, 0xff))
+        so it doesn't work very well for shells which is why it's not
+        used in every case.  Also, don't try to terminate DOS processes
+        (on Win95), because this will hang Emacs. */
+      if (!(cp && cp->is_dos_process)
+         && !TerminateProcess (proc_hand, 0xff))
         {
          DebPrint (("sys_kill.TerminateProcess returned %d "
                     "for pid %lu\n", GetLastError (), pid));
@@ -1064,4 +1156,108 @@ reset_standard_handles (int in, int out, int err, HANDLE handles[3])
   SetStdHandle (STD_ERROR_HANDLE, handles[2]);
 }
 
+#ifdef HAVE_SOCKETS
+
+/* To avoid problems with winsock implementations that work over dial-up
+   connections causing or requiring a connection to exist while Emacs is
+   running, Emacs no longer automatically loads winsock on startup if it
+   is present.  Instead, it will be loaded when open-network-stream is
+   first called.
+
+   To allow full control over when winsock is loaded, we provide these
+   two functions to dynamically load and unload winsock.  This allows
+   dial-up users to only be connected when they actually need to use
+   socket services.  */
+
+/* From nt.c */
+extern HANDLE winsock_lib;
+extern BOOL term_winsock (void);
+extern BOOL init_winsock (int load_now);
+
+extern Lisp_Object Vsystem_name;
+
+DEFUN ("win32-has-winsock", Fwin32_has_winsock, Swin32_has_winsock, 0, 1, 0,
+  "Test for presence of the Windows socket library `winsock'.\n\
+Returns non-nil if winsock support is present, nil otherwise.\n\
+\n\
+If the optional argument LOAD-NOW is non-nil, the winsock library is\n\
+also loaded immediately if not already loaded.  If winsock is loaded,\n\
+the winsock local hostname is returned (since this may be different from\n\
+the value of `system-name' and should supplant it), otherwise t is\n\
+returned to indicate winsock support is present.")
+  (load_now)
+     Lisp_Object load_now;
+{
+  int have_winsock;
+
+  have_winsock = init_winsock (!NILP (load_now));
+  if (have_winsock)
+    {
+      if (winsock_lib != NULL)
+       {
+         /* Return new value for system-name.  The best way to do this
+            is to call init_system_name, saving and restoring the
+            original value to avoid side-effects.  */
+         Lisp_Object orig_hostname = Vsystem_name;
+         Lisp_Object hostname;
+
+         init_system_name ();
+         hostname = Vsystem_name;
+         Vsystem_name = orig_hostname;
+         return hostname;
+       }
+      return Qt;
+    }
+  return Qnil;
+}
+
+DEFUN ("win32-unload-winsock", Fwin32_unload_winsock, Swin32_unload_winsock,
+       0, 0, 0,
+  "Unload the Windows socket library `winsock' if loaded.\n\
+This is provided to allow dial-up socket connections to be disconnected\n\
+when no longer needed.  Returns nil without unloading winsock if any\n\
+socket connections still exist.")
+  ()
+{
+  return term_winsock () ? Qt : Qnil;
+}
+
+#endif /* HAVE_SOCKETS */
+
+\f
+syms_of_ntproc ()
+{
+#ifdef HAVE_SOCKETS
+  defsubr (&Swin32_has_winsock);
+  defsubr (&Swin32_unload_winsock);
+#endif
+
+  DEFVAR_LISP ("win32-quote-process-args", &Vwin32_quote_process_args,
+    "Non-nil enables quoting of process arguments to ensure correct parsing.\n\
+Because Windows does not directly pass argv arrays to child processes,\n\
+programs have to reconstruct the argv array by parsing the command\n\
+line string.  For an argument to contain a space, it must be enclosed\n\
+in double quotes or it will be parsed as multiple arguments.\n\
+\n\
+However, the argument list to call-process is not always correctly\n\
+constructed (or arguments have already been quoted), so enabling this\n\
+option may cause unexpected behavior.");
+  Vwin32_quote_process_args = Qnil;
+
+  DEFVAR_INT ("win32-pipe-read-delay", &Vwin32_pipe_read_delay,
+    "Forced delay before reading subprocess output.\n\
+This is done to improve the buffering of subprocess output, by\n\
+avoiding the inefficiency of frequently reading small amounts of data.\n\
+\n\
+If positive, the value is the number of milliseconds to sleep before\n\
+reading the subprocess output.  If negative, the magnitude is the number\n\
+of time slices to wait (effectively boosting the priority of the child\n\
+process temporarily).  A value of zero disables waiting entirely.");
+  Vwin32_pipe_read_delay = 50;
+
+  DEFVAR_LISP ("win32-downcase-file-names", &Vwin32_downcase_file_names,
+    "Non-nil means convert all-upper case file names to lower case.\n\
+This applies when performing completions and file name expansion.");
+  Vwin32_downcase_file_names = Qnil;
+}
 /* end of ntproc.c */