Contents in 1999-06-04-13 of release-21-2.
[chise/xemacs-chise.git.1] / src / ntproc.c
index f144b04..5618847 100644 (file)
@@ -42,14 +42,18 @@ Boston, MA 02111-1307, USA.
 
 #include <windows.h>
 #include <sys/socket.h>
-
+#ifdef HAVE_A_OUT_H
+#include <a.out.h>
+#endif
 #include "lisp.h"
 #include "sysproc.h"
 #include "nt.h"
 #include "ntheap.h" /* From 19.34.6 */
 #include "systime.h"
 #include "syssignal.h"
+#include "sysfile.h"
 #include "syswait.h"
+#include "buffer.h"
 #include "process.h"
 /*#include "w32term.h"*/ /* From 19.34.6: sync in ? --marcpa */
 
@@ -58,7 +62,7 @@ Boston, MA 02111-1307, USA.
 
 /* 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
+   are careful about constructing argv arrays, we make this behavior
    conditional (off by default). */
 Lisp_Object Vwin32_quote_process_args;
 
@@ -86,6 +90,8 @@ Lisp_Object Vwin32_generate_fake_inodes;
 
 Lisp_Object Qhigh, Qlow;
 
+extern Lisp_Object Vlisp_EXEC_SUFFIXES;
+
 #ifndef DEBUG_XEMACS
 __inline
 #endif
@@ -132,6 +138,8 @@ new_child (void)
   xzero (*cp);
   cp->fd = -1;
   cp->pid = -1;
+  if (cp->procinfo.hProcess)
+    CloseHandle(cp->procinfo.hProcess);
   cp->procinfo.hProcess = NULL;
   cp->status = STATUS_READ_ERROR;
 
@@ -221,6 +229,63 @@ find_child_pid (DWORD pid)
   return NULL;
 }
 
+/* Function to do blocking read of one byte, needed to implement
+   select.  It is only allowed on sockets and pipes. */
+static int
+_sys_read_ahead (int fd)
+{
+  child_process * cp;
+  int rc = 0;
+
+  if (fd < 0 || fd >= MAXDESC)
+    return STATUS_READ_ERROR;
+
+  cp = fd_info[fd].cp;
+
+  if (cp == NULL || cp->fd != fd || cp->status != STATUS_READ_READY)
+    return STATUS_READ_ERROR;
+
+  if ((fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET)) == 0
+      || (fd_info[fd].flags & FILE_READ) == 0)
+    {
+      /* fd is not a pipe or socket */
+      abort ();
+    }
+  
+  cp->status = STATUS_READ_IN_PROGRESS;
+  
+  if (fd_info[fd].flags & FILE_PIPE)
+    {
+      rc = _read (fd, &cp->chr, sizeof (char));
+
+      /* Give subprocess time to buffer some more output for us before
+        reporting that input is available; we need this because Win95
+        connects DOS programs to pipes by making the pipe appear to be
+        the normal console stdout - as a result most DOS programs will
+        write to stdout without buffering, ie.  one character at a
+        time.  Even some Win32 programs do this - "dir" in a command
+        shell on NT is very slow if we don't do this. */
+      if (rc > 0)
+       {
+         int wait = XINT (Vwin32_pipe_read_delay);
+
+         if (wait > 0)
+           Sleep (wait);
+         else if (wait < 0)
+           while (++wait <= 0)
+             /* Yield remainder of our time slice, effectively giving a
+                temporary priority boost to the child process. */
+             Sleep (0);
+       }
+    }
+
+  if (rc == sizeof (char))
+    cp->status = STATUS_READ_SUCCEEDED;
+  else
+    cp->status = STATUS_READ_FAILED;
+
+  return cp->status;
+}
 
 /* Thread proc for child process and socket reader threads. Each thread
    is normally blocked until woken by select() to check for input by
@@ -234,10 +299,19 @@ reader_thread (void *arg)
   /* Our identity */
   cp = (child_process *)arg;
   
-  /* We have to wait for the go-ahead before we can start */
+  /* <matts@tibco.com> - I think the test below is wrong - we don't
+     want to wait for someone to signal char_consumed, as we haven't
+     read anything for them to consume yet! */
+
+  /*
   if (cp == NULL ||
       WaitForSingleObject (cp->char_consumed, INFINITE) != WAIT_OBJECT_0)
-    return 1;
+  */
+
+  if (cp == NULL)
+  {
+      return 1;
+  }
 
   for (;;)
     {
@@ -255,7 +329,28 @@ reader_thread (void *arg)
        }
 
       if (rc == STATUS_READ_ERROR)
-       return 1;
+      {
+        /* We are finished, so clean up handles and set to NULL so
+           that CHILD_ACTIVE will see what is going on */
+        if (cp->char_avail) {
+          CloseHandle (cp->char_avail);
+          cp->char_avail = NULL;
+        }
+        if (cp->thrd) {
+          CloseHandle (cp->thrd);
+          cp->thrd = NULL;
+        }
+        if (cp->char_consumed) {
+          CloseHandle(cp->char_consumed);
+          cp->char_consumed = NULL;
+        }
+        if (cp->procinfo.hProcess)
+        {
+          CloseHandle (cp->procinfo.hProcess);
+          cp->procinfo.hProcess=NULL;
+        }
+        return 1;
+      }
         
       /* If the read died, the child has died so let the thread die */
       if (rc == STATUS_READ_FAILED)
@@ -269,6 +364,26 @@ reader_thread (void *arg)
          break;
         }
     }
+  /* We are finished, so clean up handles and set to NULL so that
+     CHILD_ACTIVE will see what is going on */
+  if (cp->char_avail) {
+    CloseHandle (cp->char_avail);
+    cp->char_avail = NULL;
+  }
+  if (cp->thrd) {
+    CloseHandle (cp->thrd);
+    cp->thrd = NULL;
+  }
+  if (cp->char_consumed) {
+    CloseHandle(cp->char_consumed);
+    cp->char_consumed = NULL;
+  }
+  if (cp->procinfo.hProcess)
+  {
+    CloseHandle (cp->procinfo.hProcess);
+    cp->procinfo.hProcess=NULL;
+  }
+  
   return 0;
 }
 
@@ -278,7 +393,7 @@ reader_thread (void *arg)
 static const char * process_dir;
 
 static BOOL 
-create_child (char *exe, char *cmdline, char *env,
+create_child (CONST char *exe, char *cmdline, char *env,
              int * pPid, child_process *cp)
 {
   STARTUPINFO start;
@@ -325,6 +440,11 @@ create_child (char *exe, char *cmdline, char *env,
 
   cp->pid = (int) cp->procinfo.dwProcessId;
 
+  CloseHandle (cp->procinfo.hThread);
+  CloseHandle (cp->procinfo.hProcess);
+  cp->procinfo.hThread=NULL;
+  cp->procinfo.hProcess=NULL;
+
   /* Hack for Windows 95, which assigns large (ie negative) pids */
   if (cp->pid < 0)
     cp->pid = -cp->pid;
@@ -345,8 +465,30 @@ create_child (char *exe, char *cmdline, char *env,
   return FALSE;
 }
 
+#ifndef __MINGW32__
+/* Return pointer to section header for section containing the given
+   relative virtual address. */
+static IMAGE_SECTION_HEADER *
+rva_to_section (DWORD rva, IMAGE_NT_HEADERS * nt_header)
+{
+  PIMAGE_SECTION_HEADER section;
+  int i;
+
+  section = IMAGE_FIRST_SECTION (nt_header);
+
+  for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
+    {
+      if (rva >= section->VirtualAddress
+         && rva < section->VirtualAddress + section->SizeOfRawData)
+       return section;
+      section++;
+    }
+  return NULL;
+}
+#endif
+
 void
-win32_executable_type (char * filename, int * is_dos_app, int * is_cygnus_app)
+win32_executable_type (CONST char * filename, int * is_dos_app, int * is_cygnus_app)
 {
   file_data executable;
   char * p;
@@ -373,9 +515,9 @@ win32_executable_type (char * filename, int * is_dos_app, int * is_cygnus_app)
       /* Actually, I think it uses the program association for that
         extension, which is defined in the registry.  */
       p = egetenv ("COMSPEC");
-         if (p)
+      if (p)
        win32_executable_type (p, is_dos_app, is_cygnus_app);
-       }
+    }
       else
        {
       /* Look for DOS .exe signature - if found, we must also check that
@@ -383,57 +525,77 @@ win32_executable_type (char * filename, int * is_dos_app, int * is_cygnus_app)
         start with a DOS program stub.  Note that 16-bit Windows
         executables use the OS/2 1.x format. */
 
-      IMAGE_DOS_HEADER * dos_header;
-      IMAGE_NT_HEADERS * nt_header;
-
-      dos_header = (PIMAGE_DOS_HEADER) executable.file_base;
-      if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
-       goto unwind;
-
-      nt_header = (PIMAGE_NT_HEADERS) ((char *) dos_header + dos_header->e_lfanew);
-
-      if ((char *) nt_header > (char *) dos_header + executable.size) 
-       {
-         /* Some dos headers (pkunzip) have bogus e_lfanew fields.  */
-         *is_dos_app = TRUE;
-       } 
-      else if (nt_header->Signature != IMAGE_NT_SIGNATURE &&
-                LOWORD (nt_header->Signature) != IMAGE_OS2_SIGNATURE)
-       {
-         *is_dos_app = TRUE;
-       }
-      else if (nt_header->Signature == IMAGE_NT_SIGNATURE)
-       {
-         /* Look for cygwin.dll in DLL import list. */
-         IMAGE_DATA_DIRECTORY import_dir =
-           nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
-         IMAGE_IMPORT_DESCRIPTOR * imports;
-         IMAGE_SECTION_HEADER * section;
+#ifdef __MINGW32__
+         /* mingw32 doesn't have enough headers to detect cygwin
+             apps, just do what we can. */
+         FILHDR * exe_header;
 
-         section = rva_to_section (import_dir.VirtualAddress, nt_header);
-         imports = RVA_TO_PTR (import_dir.VirtualAddress, section, executable);
+         exe_header = (FILHDR*) executable.file_base;
+         if (exe_header->e_magic != DOSMAGIC)
+           goto unwind;
 
-         for ( ; imports->Name; imports++)
+         if ((char *) exe_header->e_lfanew > (char *) executable.size)
            {
-             char * dllname = RVA_TO_PTR (imports->Name, section, executable);
-
-             if (strcmp (dllname, "cygwin.dll") == 0)
+             /* Some dos headers (pkunzip) have bogus e_lfanew fields.  */
+             *is_dos_app = TRUE;
+           } 
+         else if (exe_header->nt_signature != NT_SIGNATURE)
+           {
+             *is_dos_app = TRUE;
+           }
+#else
+         IMAGE_DOS_HEADER * dos_header;
+         IMAGE_NT_HEADERS * nt_header;
+
+         dos_header = (PIMAGE_DOS_HEADER) executable.file_base;
+         if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
+           goto unwind;
+         
+         nt_header = (PIMAGE_NT_HEADERS) ((char *) dos_header + dos_header->e_lfanew);
+         
+         if ((char *) nt_header > (char *) dos_header + executable.size) 
+           {
+             /* Some dos headers (pkunzip) have bogus e_lfanew fields.  */
+             *is_dos_app = TRUE;
+           } 
+         else if (nt_header->Signature != IMAGE_NT_SIGNATURE &&
+                  LOWORD (nt_header->Signature) != IMAGE_OS2_SIGNATURE)
+           {
+             *is_dos_app = TRUE;
+           }
+         else if (nt_header->Signature == IMAGE_NT_SIGNATURE)
            {
-                 *is_cygnus_app = TRUE;
-                 break;
+             /* Look for cygwin.dll in DLL import list. */
+             IMAGE_DATA_DIRECTORY import_dir =
+               nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
+             IMAGE_IMPORT_DESCRIPTOR * imports;
+             IMAGE_SECTION_HEADER * section;
+
+             section = rva_to_section (import_dir.VirtualAddress, nt_header);
+             imports = RVA_TO_PTR (import_dir.VirtualAddress, section, executable);
+             
+             for ( ; imports->Name; imports++)
+               {
+                 char * dllname = RVA_TO_PTR (imports->Name, section, executable);
+
+                 if (strcmp (dllname, "cygwin.dll") == 0)
+                   {
+                     *is_cygnus_app = TRUE;
+                     break;
+                   }
                }
            }
+#endif
        }
-    }
 
-unwind:
-  close_file_data (&executable);
+ unwind:
+      close_file_data (&executable);
 }
 
 int
-compare_env (const char **strp1, const char **strp2)
+compare_env (const void *strp1, const void *strp2)
 {
-  const char *str1 = *strp1, *str2 = *strp2;
+  const char *str1 = *(const char**)strp1, *str2 = *(const char**)strp2;
 
   while (*str1 && *str2 && *str1 != '=' && *str2 != '=')
     {
@@ -487,7 +649,7 @@ sys_spawnve (int mode, CONST char *cmdname,
   child_process *cp;
   int is_dos_app, is_cygnus_app;
   int do_quoting = 0;
-  char escape_char;
+  char escape_char = 0;
   /* We pass our process ID to our children by setting up an environment
      variable in their environment.  */
   char ppid_env_var_buffer[64];
@@ -507,24 +669,26 @@ sys_spawnve (int mode, CONST char *cmdname,
   if (NILP (Ffile_executable_p (program)))
     {
       full = Qnil;
-      locate_file (Vexec_path, program, EXEC_SUFFIXES, &full, 1);
+      locate_file (Vexec_path, program, Vlisp_EXEC_SUFFIXES, &full, 1);
       if (NILP (full))
        {
          UNGCPRO;
          errno = EINVAL;
          return -1;
        }
-      cmdname = XSTRING_DATA (full);
-      /* #### KLUDGE */
-      *(char**)(argv[0]) = cmdname;
+      GET_C_STRING_FILENAME_DATA_ALLOCA (full, cmdname);
+    }
+  else
+    {
+      (char*)cmdname = alloca (strlen (argv[0]) + 1);
+      strcpy ((char*)cmdname, argv[0]);
     }
   UNGCPRO;
 
   /* make sure argv[0] and cmdname are both in DOS format */
-  strcpy (cmdname = alloca (strlen (cmdname) + 1), argv[0]);
-  unixtodos_filename (cmdname);
+  unixtodos_filename ((char*)cmdname);
   /* #### KLUDGE */
-  *(char**)(argv[0]) = cmdname;
+  ((CONST char**)argv)[0] = cmdname;
 
   /* Determine whether program is a 16-bit DOS executable, or a Win32
      executable that is implicitly linked to the Cygnus dll (implying it
@@ -540,13 +704,13 @@ sys_spawnve (int mode, CONST char *cmdname,
     {
       cmdname = alloca (MAXPATHLEN);
       if (egetenv ("CMDPROXY"))
-       strcpy (cmdname, egetenv ("CMDPROXY"));
+       strcpy ((char*)cmdname, egetenv ("CMDPROXY"));
       else
     {
-         strcpy (cmdname, XSTRING_DATA (Vinvocation_directory));
-         strcat (cmdname, "cmdproxy.exe");
+         strcpy ((char*)cmdname, XSTRING_DATA (Vinvocation_directory));
+         strcat ((char*)cmdname, "cmdproxy.exe");
        }
-      unixtodos_filename (cmdname);
+      unixtodos_filename ((char*)cmdname);
     }
   
   /* we have to do some conjuring here to put argv and envp into the
@@ -563,7 +727,7 @@ sys_spawnve (int mode, CONST char *cmdname,
 
      The Win32 GNU-based library from Cygnus doubles quotes to escape
      them, while MSVC uses backslash for escaping.  (Actually the MSVC
-     startup code does attempt to recognise doubled quotes and accept
+     startup code does attempt to recognize doubled quotes and accept
      them, but gets it wrong and ends up requiring three quotes to get a
      single embedded quote!)  So by default we decide whether to use
      quote or backslash as the escape character based on whether the
@@ -571,7 +735,7 @@ sys_spawnve (int mode, CONST char *cmdname,
 
      Note that using backslash to escape embedded quotes requires
      additional special handling if an embedded quote is already
-     preceeded by backslash, or if an arg requiring quoting ends with
+     preceded by backslash, or if an arg requiring quoting ends with
      backslash.  In such cases, the run of escape characters needs to be
      doubled.  For consistency, we apply this special handling as long
      as the escape character is not quote.
@@ -592,7 +756,7 @@ sys_spawnve (int mode, CONST char *cmdname,
   
   /* do argv...  */
   arglen = 0;
-  targ = argv;
+  targ = (char**)argv;
   while (*targ)
     {
       char * p = *targ;
@@ -638,7 +802,7 @@ sys_spawnve (int mode, CONST char *cmdname,
       arglen += strlen (*targ++) + 1;
     }
   cmdline = alloca (arglen);
-  targ = argv;
+  targ = (char**)argv;
   parg = cmdline;
   while (*targ)
     {
@@ -667,7 +831,7 @@ sys_spawnve (int mode, CONST char *cmdname,
 #if 0
          /* This version does not escape quotes if they occur at the
             beginning or end of the arg - this could lead to incorrect
-            behaviour when the arg itself represents a command line
+            behavior when the arg itself represents a command line
             containing quoted args.  I believe this was originally done
             as a hack to make some things work, before
             `win32-quote-process-args' was added.  */
@@ -719,7 +883,7 @@ sys_spawnve (int mode, CONST char *cmdname,
   
   /* and envp...  */
   arglen = 1;
-  targ = envp;
+  targ = (char**)envp;
   numenv = 1; /* for end null */
   while (*targ)
     {
@@ -734,7 +898,7 @@ sys_spawnve (int mode, CONST char *cmdname,
 
   /* 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);
+  merge_and_sort_env ((char**)envp, extra_env, targ);
 
   /* concatenate env entries.  */
   env = alloca (arglen);
@@ -825,7 +989,7 @@ sys_kill (int pid, int sig)
       pid = cp->procinfo.dwProcessId;
 
       /* Try to locate console window for process. */
-      EnumWindows (find_child_console, (LPARAM) cp);
+      EnumWindows ((WNDENUMPROC)find_child_console, (LPARAM) cp);
     }
   
   if (sig == SIGINT)
@@ -1136,7 +1300,7 @@ If successful, the return value is t, otherwise nil.
 DEFUN ("win32-get-locale-info", Fwin32_get_locale_info, 1, 2, "", /*
   "Return information about the Windows locale LCID.
 By default, return a three letter locale code which encodes the default
-language as the first two characters, and the country or regionial variant
+language as the first two characters, and the country or regional variant
 as the third letter.  For example, ENU refers to `English (United States)',
 while ENC means `English (Canadian)'.
 
@@ -1278,9 +1442,6 @@ If successful, the new locale id is returned, otherwise nil.
 void
 syms_of_ntproc ()
 {
-  Qhigh = intern ("high");
-  Qlow = intern ("low");
-
   DEFSUBR (Fwin32_short_file_name);
   DEFSUBR (Fwin32_long_file_name);
   DEFSUBR (Fwin32_set_process_priority);
@@ -1289,6 +1450,14 @@ syms_of_ntproc ()
   DEFSUBR (Fwin32_get_default_locale_id);
   DEFSUBR (Fwin32_get_valid_locale_ids);
   DEFSUBR (Fwin32_set_current_locale);
+}
+
+
+void
+vars_of_ntproc (void)
+{
+  Qhigh = intern ("high");
+  Qlow = intern ("low");
 
   DEFVAR_LISP ("win32-quote-process-args", &Vwin32_quote_process_args /*
     Non-nil enables quoting of process arguments to ensure correct parsing.
@@ -1319,7 +1488,7 @@ 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.
 */ );
-  Vwin32_start_process_share_console = Qnil;
+  Vwin32_start_process_share_console = Qt;
 
   DEFVAR_LISP ("win32-pipe-read-delay", &Vwin32_pipe_read_delay /*
     Forced delay before reading subprocess output.
@@ -1338,10 +1507,11 @@ process temporarily).  A value of zero disables waiting entirely.
     "Non-nil means attempt to fake realistic inode values.
 This works by hashing the truename of files, and should detect 
 aliasing between long and short (8.3 DOS) names, but can have
-false positives because of hash collisions.  Note that determing
+false positives because of hash collisions.  Note that determining
 the truename of a file can be slow.
 */ );
   Vwin32_generate_fake_inodes = Qnil;
 #endif
 }
+
 /* end of ntproc.c */