]> code.delx.au - gnu-emacs/blobdiff - src/w32proc.c
(GETPGRP_NO_ARG): If Glibc 2.1 or later,
[gnu-emacs] / src / w32proc.c
index 42b5b6729f86e9f63a20d839cd707efc54cf378b..fec9a9ddeeee1cf58f659b7a5537fc92878a1394 100644 (file)
@@ -1,4 +1,4 @@
-/* Process support for Windows NT port of GNU EMACS.
+/* Process support for GNU Emacs on the Microsoft W32 API.
    Copyright (C) 1992, 1995 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
@@ -40,7 +40,7 @@ Boston, MA 02111-1307, USA.
 #include <windows.h>
 
 #include "lisp.h"
-#include "nt.h"
+#include "w32.h"
 #include "systime.h"
 #include "syswait.h"
 #include "process.h"
@@ -49,7 +49,24 @@ Boston, MA 02111-1307, USA.
    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;
+Lisp_Object Vw32_quote_process_args;
+
+/* Control whether create_child causes the process' window to be
+   hidden.  The default is nil. */
+Lisp_Object Vw32_start_process_show_window;
+
+/* 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 W32 processes on both Windows 95 and NT as well.  */
+Lisp_Object Vw32_pipe_read_delay;
+
+/* Control conversion of upper case file names to lower case.
+   nil means no, t means yes. */
+Lisp_Object Vw32_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[];
@@ -271,7 +288,10 @@ create_child (char *exe, char *cmdline, char *env,
   start.cb = sizeof (start);
   
 #ifdef HAVE_NTGUI
-  start.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
+  if (NILP (Vw32_start_process_show_window))
+    start.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
+  else
+    start.dwFlags = STARTF_USESTDHANDLES;
   start.wShowWindow = SW_HIDE;
 
   start.hStdInput = GetStdHandle (STD_INPUT_HANDLE);
@@ -367,6 +387,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
@@ -504,9 +529,97 @@ sys_wait (int *status)
   return pid;
 }
 
-/* We pass our process ID to our children by setting up an environment
-   variable in their environment.  */
-char ppid_env_var_buffer[64];
+int
+w32_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 = w32_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;
+}
+
+int
+compare_env (const char **strp1, const char **strp2)
+{
+  const char *str1 = *strp1, *str2 = *strp2;
+
+  while (*str1 && *str2 && *str1 != '=' && *str2 != '=')
+    {
+      if (tolower (*str1) > tolower (*str2))
+       return 1;
+      else if (tolower (*str1) < tolower (*str2))
+       return -1;
+      str1++, str2++;
+    }
+
+  if (*str1 == '=' && *str2 == '=')
+    return 0;
+  else if (*str1 == '=')
+    return -1;
+  else
+    return 1;
+}
+
+void
+merge_and_sort_env (char **envp1, char **envp2, char **new_envp)
+{
+  char **optr, **nptr;
+  int num;
+
+  nptr = new_envp;
+  optr = envp1;
+  while (*optr)
+    *nptr++ = *optr++;
+  num = optr - envp1;
+
+  optr = envp2;
+  while (*optr)
+    *nptr++ = *optr++;
+  num += optr - envp2;
+
+  qsort (new_envp, num, sizeof (char *), compare_env);
+
+  *nptr = NULL;
+}
 
 /* When a new child process is created we need to register it in our list,
    so intercept spawn requests.  */
@@ -515,10 +628,15 @@ sys_spawnve (int mode, char *cmdname, char **argv, char **envp)
 {
   Lisp_Object program, full;
   char *cmdline, *env, *parg, **targ;
-  int arglen;
+  int arglen, numenv;
   int pid;
   child_process *cp;
-  
+  int 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];
+  char *extra_env[] = {ppid_env_var_buffer, NULL};
+
   /* We don't care about the other modes */
   if (mode != _P_NOWAIT)
     {
@@ -549,6 +667,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 = w32_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
@@ -558,10 +685,10 @@ sys_spawnve (int mode, char *cmdname, char **argv, char **envp)
      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
+     usually illegal (most W32 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
+     handle it.  The W32 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
@@ -602,7 +729,7 @@ sys_spawnve (int mode, char *cmdname, char **argv, char **envp)
       if (*p == 0)
        add_quotes = 1;
 
-      if (!NILP (Vwin32_quote_process_args))
+      if (!NILP (Vw32_quote_process_args))
        {
          /* This is conditional because it sometimes causes more
             problems than it solves, since argv arrays are not always
@@ -643,16 +770,24 @@ sys_spawnve (int mode, char *cmdname, char **argv, char **envp)
   /* and envp...  */
   arglen = 1;
   targ = envp;
+  numenv = 1; /* for end null */
   while (*targ)
     {
       arglen += strlen (*targ++) + 1;
+      numenv++;
     }
+  /* extra env vars... */
   sprintf (ppid_env_var_buffer, "__PARENT_PROCESS_ID=%d", 
           GetCurrentProcessId ());
   arglen += strlen (ppid_env_var_buffer) + 1;
+  numenv++;
 
+  /* merge env passed in and extra env into one, and sort it.  */
+  targ = (char **) alloca (numenv * sizeof (char *));
+  merge_and_sort_env (envp, extra_env, targ);
+
+  /* concatenate env entries.  */
   env = alloca (arglen);
-  targ = envp;
   parg = env;
   while (*targ)
     {
@@ -660,8 +795,6 @@ sys_spawnve (int mode, char *cmdname, char **argv, char **envp)
       parg += strlen (*targ++);
       *parg++ = '\0';
     }
-  strcpy (parg, ppid_env_var_buffer);
-  parg += strlen (ppid_env_var_buffer);
   *parg++ = '\0';
   *parg = '\0';
 
@@ -679,6 +812,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;
 }
@@ -970,10 +1109,12 @@ 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))
+      /* 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.  Also, don't try to terminate DOS processes
+        (on Windows 95), 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));
@@ -1072,10 +1213,83 @@ 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 ("w32-has-winsock", Fw32_has_winsock, Sw32_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 ("w32-unload-winsock", Fw32_unload_winsock, Sw32_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 ()
 {
-  DEFVAR_LISP ("win32-quote-process-args", &Vwin32_quote_process_args,
+#ifdef HAVE_SOCKETS
+  defsubr (&Sw32_has_winsock);
+  defsubr (&Sw32_unload_winsock);
+#endif
+
+  DEFVAR_LISP ("w32-quote-process-args", &Vw32_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\
@@ -1085,6 +1299,28 @@ in double quotes or it will be parsed as multiple arguments.\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;
+  Vw32_quote_process_args = Qnil;
+
+  DEFVAR_LISP ("w32-start-process-show-window",
+              &Vw32_start_process_show_window,
+    "When nil, processes started via start-process hide their windows.\n\
+When non-nil, they show their window in the method of their choice.");
+  Vw32_start_process_show_window = Qnil;
+
+  DEFVAR_INT ("w32-pipe-read-delay", &Vw32_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.");
+  Vw32_pipe_read_delay = 50;
+
+  DEFVAR_LISP ("w32-downcase-file-names", &Vw32_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.");
+  Vw32_downcase_file_names = Qnil;
 }
 /* end of ntproc.c */