]> code.delx.au - gnu-emacs/blobdiff - src/callproc.c
Update copyright.
[gnu-emacs] / src / callproc.c
index 3b63a271870c9dbe5a973391284fdeec3b345c08..aff272aae0b40ea50f953c7dcb290de3409d3e7f 100644 (file)
@@ -1,5 +1,5 @@
 /* Synchronous subprocess invocation for GNU Emacs.
-   Copyright (C) 1985, 1986, 1987, 1988, 1993, 1994 Free Software Foundation, Inc.
+   Copyright (C) 1985, 86, 87, 88, 93, 94, 95 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -22,6 +22,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include <errno.h>
 
 #include <config.h>
+#include <stdio.h>
 
 extern int errno;
 extern char *strerror ();
@@ -36,10 +37,22 @@ extern char *strerror ();
 
 #include <sys/file.h>
 #ifdef USG5
+#define INCLUDED_FCNTL
 #include <fcntl.h>
 #endif
 
+#ifdef WINDOWSNT
+#define NOMINMAX
+#include <windows.h>
+#include <stdlib.h>    /* for proper declaration of environ */
+#include <fcntl.h>
+#include "nt.h"
+#define _P_NOWAIT 1    /* from process.h */
+#endif
+
 #ifdef MSDOS   /* Demacs 1.1.1 91/10/16 HIRANO Satoshi */
+#include "msdos.h"
+#define INCLUDED_FCNTL
 #include <fcntl.h>
 #include <sys/stat.h>
 #include <sys/param.h>
@@ -70,17 +83,26 @@ extern char **environ;
 
 #define max(a, b) ((a) > (b) ? (a) : (b))
 
-#ifdef MSDOS
-Lisp_Object Vbinary_process;
-#endif
+#ifdef DOS_NT
+/* When we are starting external processes we need to know whether they
+   take binary input (no conversion) or text input (\n is converted to
+   \r\n).  Similar for output: if newlines are written as \r\n then it's
+   text process output, otherwise it's binary.  */
+Lisp_Object Vbinary_process_input;
+Lisp_Object Vbinary_process_output;
+#endif /* DOS_NT */
 
-Lisp_Object Vexec_path, Vexec_directory, Vdata_directory;
+Lisp_Object Vexec_path, Vexec_directory, Vdata_directory, Vdoc_directory;
 Lisp_Object Vconfigure_info_directory;
 
 Lisp_Object Vshell_file_name;
 
 Lisp_Object Vprocess_environment;
 
+#ifdef DOS_NT
+Lisp_Object Qbuffer_file_type;
+#endif /* DOS_NT */
+
 /* True iff we are about to fork off a synchronous process or if we
    are waiting for it.  */
 int synch_process_alive;
@@ -94,6 +116,13 @@ int synch_process_retcode;
 
 extern Lisp_Object Vdoc_file_name;
 \f
+/* Clean up when exiting Fcall_process.
+   On MSDOS, delete the temporary file on any kind of termination.
+   On Unix, kill the process and any children on termination by signal.  */
+
+/* Nonzero if this is termination due to exit.  */
+static int call_process_exited;
+
 #ifndef VMS  /* VMS version is in vmsproc.c.  */
 
 static Lisp_Object
@@ -112,13 +141,21 @@ call_process_cleanup (fdpid)
 {
 #ifdef MSDOS
   /* for MSDOS fdpid is really (fd . tempfile)  */
-  register Lisp_Object file = Fcdr (fdpid);
+  register Lisp_Object file;
+  file = Fcdr (fdpid);
   close (XFASTINT (Fcar (fdpid)));
   if (strcmp (XSTRING (file)-> data, NULL_DEVICE) != 0)
     unlink (XSTRING (file)->data);
 #else /* not MSDOS */
   register int pid = XFASTINT (Fcdr (fdpid));
 
+
+  if (call_process_exited)
+    {
+      close (XFASTINT (Fcar (fdpid)));
+      return Qnil;
+    }
+
   if (EMACS_KILLPG (pid, SIGINT) == 0)
     {
       int count = specpdl_ptr - specpdl;
@@ -142,10 +179,17 @@ DEFUN ("call-process", Fcall_process, Scall_process, 1, MANY, 0,
 The program's input comes from file INFILE (nil means `/dev/null').\n\
 Insert output in BUFFER before point; t means current buffer;\n\
  nil for BUFFER means discard it; 0 means discard and don't wait.\n\
+BUFFER can also have the form (REAL-BUFFER STDERR-FILE); in that case,\n\
+REAL-BUFFER says what to do with standard output, as above,\n\
+while STDERR-FILE says what to do with standard error in the child.\n\
+STDERR-FILE may be nil (discard standard error output),\n\
+t (mix it with ordinary output), or a file name string.\n\
+\n\
 Fourth arg DISPLAY non-nil means redisplay buffer as output is inserted.\n\
 Remaining arguments are strings passed as command arguments to PROGRAM.\n\
-If BUFFER is 0, returns immediately with value nil.\n\
-Otherwise waits for PROGRAM to terminate\n\
+\n\
+If BUFFER is 0, `call-process' returns immediately with value nil.\n\
+Otherwise it waits for PROGRAM to terminate\n\
 and returns a numeric exit status or a signal description string.\n\
 If you quit, the process is killed with SIGINT, or SIGKILL if you quit again.")
   (nargs, args)
@@ -161,6 +205,9 @@ If you quit, the process is killed with SIGINT, or SIGKILL if you quit again.")
   register unsigned char **new_argv
     = (unsigned char **) alloca ((max (2, nargs - 2)) * sizeof (char *));
   struct buffer *old = current_buffer;
+  /* File to use for stderr in the child.
+     t means use same as standard output.  */
+  Lisp_Object error_file;
 #ifdef MSDOS   /* Demacs 1.1.1 91/10/16 HIRANO Satoshi */
   char *outf, *tempfile;
   int outfilefd;
@@ -170,9 +217,11 @@ If you quit, the process is killed with SIGINT, or SIGKILL if you quit again.")
 #endif
   CHECK_STRING (args[0], 0);
 
+  error_file = Qt;
+
 #ifndef subprocesses
   /* Without asynchronous processes we cannot have BUFFER == 0.  */
-  if (nargs >= 3 && XTYPE (args[2]) == Lisp_Int)
+  if (nargs >= 3 && INTEGERP (args[2]))
     error ("Operating system cannot handle asynchronous subprocesses");
 #endif /* subprocesses */
 
@@ -186,14 +235,27 @@ If you quit, the process is killed with SIGINT, or SIGKILL if you quit again.")
 
   if (nargs >= 3)
     {
-      register Lisp_Object tem;
+      buffer = args[2];
+
+      /* If BUFFER is a list, its meaning is
+        (BUFFER-FOR-STDOUT FILE-FOR-STDERR).  */
+      if (CONSP (buffer))
+       {
+         if (CONSP (XCONS (buffer)->cdr))
+           error_file = XCONS (XCONS (buffer)->cdr)->car;
+         buffer = XCONS (buffer)->car;
+       }
 
-      buffer = tem = args[2];
-      if (!(EQ (tem, Qnil)
-           || EQ (tem, Qt)
-           || XFASTINT (tem) == 0))
+      if (!(EQ (buffer, Qnil)
+           || EQ (buffer, Qt)
+           || XFASTINT (buffer) == 0))
        {
-         buffer = Fget_buffer (tem);
+         Lisp_Object spec_buffer;
+         spec_buffer = buffer;
+         buffer = Fget_buffer (buffer);
+         /* Mention the buffer name for a better error message.  */
+         if (NILP (buffer))
+           CHECK_BUFFER (spec_buffer, 2);
          CHECK_BUFFER (buffer, 2);
        }
     }
@@ -217,9 +279,9 @@ If you quit, the process is killed with SIGINT, or SIGKILL if you quit again.")
 
     GCPRO3 (infile, buffer, current_dir);
 
-    current_dir = 
-      expand_and_dir_to_file
-       (Funhandled_file_name_directory (current_dir), Qnil);
+    current_dir
+      = expand_and_dir_to_file (Funhandled_file_name_directory (current_dir),
+                               Qnil);
     if (NILP (Ffile_accessible_directory_p (current_dir)))
       report_file_error ("Setting current directory",
                         Fcons (current_buffer->directory, Qnil));
@@ -229,31 +291,34 @@ If you quit, the process is killed with SIGINT, or SIGKILL if you quit again.")
 
   display = nargs >= 4 ? args[3] : Qnil;
 
-  {
-    register int i;
-    for (i = 4; i < nargs; i++)
-      {
-       CHECK_STRING (args[i], i);
-       new_argv[i - 3] = XSTRING (args[i])->data;
-      }
-    /* Program name is first command arg */
-    new_argv[0] = XSTRING (args[0])->data;
-    new_argv[i - 3] = 0;
-  }
-
   filefd = open (XSTRING (infile)->data, O_RDONLY, 0);
   if (filefd < 0)
     {
       report_file_error ("Opening process input file", Fcons (infile, Qnil));
     }
   /* Search for program; barf if not found.  */
-  openp (Vexec_path, args[0], EXEC_SUFFIXES, &path, 1);
+  {
+    struct gcpro gcpro1;
+
+    GCPRO1 (current_dir);
+    openp (Vexec_path, args[0], EXEC_SUFFIXES, &path, 1);
+    UNGCPRO;
+  }
   if (NILP (path))
     {
       close (filefd);
       report_file_error ("Searching for program", Fcons (args[0], Qnil));
     }
   new_argv[0] = XSTRING (path)->data;
+  {
+    register int i;
+    for (i = 4; i < nargs; i++)
+      {
+       CHECK_STRING (args[i], i);
+       new_argv[i - 3] = XSTRING (args[i])->data;
+      }
+    new_argv[i - 3] = 0;
+  }
 
 #ifdef MSDOS /* MW, July 1993 */
   /* These vars record information from process termination.
@@ -283,12 +348,16 @@ If you quit, the process is killed with SIGINT, or SIGKILL if you quit again.")
     }
 #endif
 
-  if (XTYPE (buffer) == Lisp_Int)
+  if (INTEGERP (buffer))
     fd[1] = open (NULL_DEVICE, O_WRONLY), fd[0] = -1;
   else
     {
 #ifndef MSDOS
+#ifdef WINDOWSNT
+      pipe_with_inherited_out (fd);
+#else  /* not WINDOWSNT */
       pipe (fd);
+#endif /* not WINDOWSNT */
 #endif
 #if 0
       /* Replaced by close_process_descs */
@@ -301,6 +370,7 @@ If you quit, the process is killed with SIGINT, or SIGKILL if you quit again.")
        Protect it from permanent change.  */
     register char **save_environ = environ;
     register int fd1 = fd[1];
+    int fd_error = fd1;
 
 #if 0  /* Some systems don't have sigblock.  */
     mask = sigblock (sigmask (SIGCHLD));
@@ -316,16 +386,45 @@ If you quit, the process is killed with SIGINT, or SIGKILL if you quit again.")
     synch_process_retcode = 0;
 
 #ifdef MSDOS /* MW, July 1993 */
+    /* ??? Someone who knows MSDOG needs to check whether this properly
+       closes all descriptors that it opens.  */
     pid = run_msdos_command (new_argv, current_dir, filefd, outfilefd);
     close (outfilefd);
     fd1 = -1; /* No harm in closing that one!  */
-    fd[0] = open (tempfile, NILP (Vbinary_process) ? O_TEXT : O_BINARY);
+    fd[0] = open (tempfile, NILP (Vbinary_process_output) ? O_TEXT : O_BINARY);
     if (fd[0] < 0)
       {
        unlink (tempfile);
+       close (filefd);
        report_file_error ("Cannot re-open temporary file", Qnil);
       }
 #else /* not MSDOS */
+
+    if (NILP (error_file))
+      fd_error = open (NULL_DEVICE, O_WRONLY);
+    else if (STRINGP (error_file))
+      {
+#ifdef DOS_NT
+       fd_error = open (XSTRING (error_file)->data,
+                        O_WRONLY | O_TRUNC | O_CREAT | O_TEXT,
+                        S_IREAD | S_IWRITE);
+#else  /* not DOS_NT */
+       fd_error = creat (XSTRING (error_file)->data, 0666);
+#endif /* not DOS_NT */
+      }
+
+    if (fd_error < 0)
+      {
+       close (filefd);
+       close (fd[0]);
+       if (fd1 >= 0)
+         close (fd1);
+       report_file_error ("Cannot open", error_file);
+      }
+
+#ifdef WINDOWSNT
+    pid = child_setup (filefd, fd1, fd_error, new_argv, 0, current_dir);
+#else  /* not WINDOWSNT */
     pid = vfork ();
 
     if (pid == 0)
@@ -337,19 +436,15 @@ If you quit, the process is killed with SIGINT, or SIGKILL if you quit again.")
 #else
         setpgrp (pid, pid);
 #endif /* USG */
-       child_setup (filefd, fd1, fd1, new_argv, 0, current_dir);
+       child_setup (filefd, fd1, fd_error, new_argv, 0, current_dir);
       }
 #endif /* not MSDOS */
-
-#if 0
-    /* Tell SIGCHLD handler to look for this pid.  */
-    synch_process_pid = pid;
-    /* Now let SIGCHLD come through.  */
-    sigsetmask (mask);
-#endif
+#endif /* not WINDOWSNT */
 
     environ = save_environ;
 
+    /* Close most of our fd's, but not fd[0]
+       since we will use that to read input from.  */
     close (filefd);
     if (fd1 >= 0)
       close (fd1);
@@ -357,12 +452,15 @@ If you quit, the process is killed with SIGINT, or SIGKILL if you quit again.")
 
   if (pid < 0)
     {
-      close (fd[0]);
+      if (fd[0] >= 0)
+       close (fd[0]);
       report_file_error ("Doing vfork", Qnil);
     }
 
-  if (XTYPE (buffer) == Lisp_Int)
+  if (INTEGERP (buffer))
     {
+      if (fd[0] >= 0)
+       close (fd[0]);
 #ifndef subprocesses
       /* If Emacs has been built with asynchronous subprocess support,
         we don't need to do this, I think because it will then have
@@ -372,6 +470,9 @@ If you quit, the process is killed with SIGINT, or SIGKILL if you quit again.")
       return Qnil;
     }
 
+  /* Enable sending signal if user quits below.  */
+  call_process_exited = 0;
+
 #ifdef MSDOS
   /* MSDOS needs different cleanup information.  */
   record_unwind_protect (call_process_cleanup,
@@ -382,7 +483,7 @@ If you quit, the process is killed with SIGINT, or SIGKILL if you quit again.")
 #endif /* not MSDOS */
 
 
-  if (XTYPE (buffer) == Lisp_Buffer)
+  if (BUFFERP (buffer))
     Fset_buffer (buffer);
 
   immediate_quit = 1;
@@ -392,8 +493,16 @@ If you quit, the process is killed with SIGINT, or SIGKILL if you quit again.")
     register int nread;
     int first = 1;
 
-    while ((nread = read (fd[0], buf, sizeof buf)) > 0)
+    while ((nread = read (fd[0], buf, sizeof buf)) != 0)
       {
+       if (nread < 0)
+         {
+#if defined (__osf__) && defined (__alpha)
+           continue;           /* Work around bug in DEC OSF/1 V3.0.  */
+#else
+           break;
+#endif
+         }
        immediate_quit = 0;
        if (!NILP (buffer))
          insert (buf, nread);
@@ -416,6 +525,10 @@ If you quit, the process is killed with SIGINT, or SIGKILL if you quit again.")
 
   set_buffer_internal (old);
 
+  /* Don't kill any children that the subprocess may have left behind
+     when exiting.  */
+  call_process_exited = 1;
+
   unbind_to (count, Qnil);
 
   if (synch_process_death)
@@ -428,33 +541,45 @@ static Lisp_Object
 delete_temp_file (name)
      Lisp_Object name;
 {
-  unlink (XSTRING (name)->data);
+  /* Use Fdelete_file (indirectly) because that runs a file name handler.
+     We did that when writing the file, so we should do so when deleting.  */
+  internal_delete_file (name);
 }
 
 DEFUN ("call-process-region", Fcall_process_region, Scall_process_region,
   3, MANY, 0,
   "Send text from START to END to a synchronous process running PROGRAM.\n\
 Delete the text if fourth arg DELETE is non-nil.\n\
+\n\
 Insert output in BUFFER before point; t means current buffer;\n\
  nil for BUFFER means discard it; 0 means discard and don't wait.\n\
+BUFFER can also have the form (REAL-BUFFER STDERR-FILE); in that case,\n\
+REAL-BUFFER says what to do with standard output, as above,\n\
+while STDERR-FILE says what to do with standard error in the child.\n\
+STDERR-FILE may be nil (discard standard error output),\n\
+t (mix it with ordinary output), or a file name string.\n\
+\n\
 Sixth arg DISPLAY non-nil means redisplay buffer as output is inserted.\n\
 Remaining args are passed to PROGRAM at startup as command args.\n\
-If BUFFER is nil, returns immediately with value nil.\n\
-Otherwise waits for PROGRAM to terminate\n\
+\n\
+If BUFFER is nil, `call-process-region' returns immediately with value nil.\n\
+Otherwise it waits for PROGRAM to terminate\n\
 and returns a numeric exit status or a signal description string.\n\
 If you quit, the process is killed with SIGINT, or SIGKILL if you quit again.")
   (nargs, args)
      int nargs;
      register Lisp_Object *args;
 {
-  register Lisp_Object filename_string, start, end;
-#ifdef MSDOS
+  struct gcpro gcpro1;
+  Lisp_Object filename_string;
+  register Lisp_Object start, end;
+#ifdef DOS_NT
   char *tempfile;
 #else
   char tempfile[20];
 #endif
   int count = specpdl_ptr - specpdl;
-#ifdef MSDOS
+#ifdef DOS_NT
   char *outf = '\0';
 
   if ((outf = egetenv ("TMP")) || (outf = egetenv ("TEMP")))
@@ -468,21 +593,29 @@ If you quit, the process is killed with SIGINT, or SIGKILL if you quit again.")
   if (tempfile[strlen (tempfile) - 1] != '/')
     strcat (tempfile, "/");
   strcat (tempfile, "detmp.XXX");
-#else /* not MSDOS */
+#else /* not DOS_NT */
 
 #ifdef VMS
   strcpy (tempfile, "tmp:emacsXXXXXX.");
 #else
   strcpy (tempfile, "/tmp/emacsXXXXXX");
 #endif
-#endif /* not MSDOS */
+#endif /* not DOS_NT */
 
   mktemp (tempfile);
 
   filename_string = build_string (tempfile);
+  GCPRO1 (filename_string);
   start = args[0];
   end = args[1];
+#ifdef DOS_NT
+  specbind (Qbuffer_file_type, Vbinary_process_input);
   Fwrite_region (start, end, filename_string, Qnil, Qlambda);
+  unbind_to (count, Qnil);
+#else  /* not DOS_NT */
+  Fwrite_region (start, end, filename_string, Qnil, Qlambda);
+#endif /* not DOS_NT */
+
   record_unwind_protect (delete_temp_file, filename_string);
 
   if (!NILP (args[3]))
@@ -490,7 +623,7 @@ If you quit, the process is killed with SIGINT, or SIGKILL if you quit again.")
 
   args[3] = filename_string;
 
-  return unbind_to (count, Fcall_process (nargs - 2, args + 2));
+  RETURN_UNGCPRO (unbind_to (count, Fcall_process (nargs - 2, args + 2)));
 }
 \f
 #ifndef VMS /* VMS version is in vmsproc.c.  */
@@ -526,19 +659,28 @@ child_setup (in, out, err, new_argv, set_pgrp, current_dir)
      instead.  */
 #else /* not MSDOS */
   char **env;
+  char *pwd_var;
+#ifdef WINDOWSNT
+  int cpid;
+  HANDLE handles[4];
+#endif /* WINDOWSNT */
 
   int pid = getpid ();
 
+#ifdef SET_EMACS_PRIORITY
   {
     extern int emacs_priority;
 
-    nice (- emacs_priority);
+    if (emacs_priority < 0)
+      nice (- emacs_priority);
   }
+#endif
 
 #ifdef subprocesses
   /* Close Emacs's descriptors that this process should not have.  */
   close_process_descs ();
 #endif
+  close_load_descs ();
 
   /* Note that use of alloca is always safe here.  It's obvious for systems
      that do not have true vfork or that have true (stack) alloca.
@@ -546,13 +688,15 @@ child_setup (in, out, err, new_argv, set_pgrp, current_dir)
      the superior's static variables as if the superior had done alloca
      and will be cleaned up in the usual way.  */
   {
-    register unsigned char *temp;
+    register char *temp;
     register int i;
 
     i = XSTRING (current_dir)->size;
-    temp = (unsigned char *) alloca (i + 2);
+    pwd_var = (char *) alloca (i + 6);
+    temp = pwd_var + 4;
+    bcopy ("PWD=", pwd_var, 4);
     bcopy (XSTRING (current_dir)->data, temp, i);
-    if (temp[i - 1] != '/') temp[i++] = '/';
+    if (!IS_DIRECTORY_SEP (temp[i - 1])) temp[i++] = DIRECTORY_SEP;
     temp[i] = 0;
 
     /* We can't signal an Elisp error here; we're in a vfork.  Since
@@ -561,7 +705,11 @@ child_setup (in, out, err, new_argv, set_pgrp, current_dir)
        are changed between the check and this chdir, but we should
        at least check.  */
     if (chdir (temp) < 0)
-      exit (errno);
+      _exit (errno);
+
+    /* Strip trailing slashes for PWD, but leave "/" and "//" alone.  */
+    while (i > 2 && IS_DIRECTORY_SEP (temp[i - 1]))
+      temp[--i] = 0;
   }
 
   /* Set `env' to a vector of the strings in Vprocess_environment.  */
@@ -572,18 +720,21 @@ child_setup (in, out, err, new_argv, set_pgrp, current_dir)
 
     new_length = 0;
     for (tem = Vprocess_environment;
-        (XTYPE (tem) == Lisp_Cons
-         && XTYPE (XCONS (tem)->car) == Lisp_String);
+        CONSP (tem) && STRINGP (XCONS (tem)->car);
         tem = XCONS (tem)->cdr)
       new_length++;
 
-    /* new_length + 1 to include terminating 0.  */
-    env = new_env = (char **) alloca ((new_length + 1) * sizeof (char *));
+    /* new_length + 2 to include PWD and terminating 0.  */
+    env = new_env = (char **) alloca ((new_length + 2) * sizeof (char *));
+
+    /* If we have a PWD envvar, pass one down,
+       but with corrected value.  */
+    if (getenv ("PWD"))
+      *new_env++ = pwd_var;
 
     /* Copy the Vprocess_environment strings into new_env.  */
     for (tem = Vprocess_environment;
-        (XTYPE (tem) == Lisp_Cons
-         && XTYPE (XCONS (tem)->car) == Lisp_String);
+        CONSP (tem) && STRINGP (XCONS (tem)->car);
         tem = XCONS (tem)->cdr)
       {
        char **ep = env;
@@ -612,14 +763,21 @@ child_setup (in, out, err, new_argv, set_pgrp, current_dir)
       }
     *new_env = 0;
   }
-
+#ifdef WINDOWSNT
+  prepare_standard_handles (in, out, err, handles);
+#else  /* not WINDOWSNT */
   /* Make sure that in, out, and err are not actually already in
      descriptors zero, one, or two; this could happen if Emacs is
      started with its standard in, out, or error closed, as might
      happen under X.  */
   in = relocate_fd (in, 3);
-  out = relocate_fd (out, 3);
-  err = relocate_fd (err, 3);
+  if (out == err)
+    err = out = relocate_fd (out, 3);
+  else
+    {
+      out = relocate_fd (out, 3);
+      err = relocate_fd (err, 3);
+    }
 
   close (0);
   close (1);
@@ -631,6 +789,7 @@ child_setup (in, out, err, new_argv, set_pgrp, current_dir)
   close (in);
   close (out);
   close (err);
+#endif /* not WINDOWSNT */
 
 #ifdef USG
 #ifndef SETPGRP_RELEASES_CTTY
@@ -646,6 +805,15 @@ child_setup (in, out, err, new_argv, set_pgrp, current_dir)
   something missing here;
 #endif /* vipc */
 
+#ifdef WINDOWSNT
+  /* Spawn the child.  (See ntproc.c:Spawnve).  */
+  cpid = spawnve (_P_NOWAIT, new_argv[0], new_argv, env);
+  if (cpid == -1) {    ????
+      report_file_error ("Spawning child process", Qnil);
+  }
+  reset_standard_handles (in, out, err, handles);
+  return cpid;
+#else /* not WINDOWSNT */
   /* execvp does not accept an environment arg so the only way
      to pass this environment is to set environ.  Our caller
      is responsible for restoring the ambient value of environ.  */
@@ -655,6 +823,7 @@ child_setup (in, out, err, new_argv, set_pgrp, current_dir)
   write (1, "Couldn't exec the program ", 26);
   write (1, new_argv[0], strlen (new_argv[0]));
   _exit (1);
+#endif /* not WINDOWSNT */
 #endif /* not MSDOS */
 }
 
@@ -698,12 +867,18 @@ getenv_internal (var, varlen, value, valuelen)
 
   for (scan = Vprocess_environment; CONSP (scan); scan = XCONS (scan)->cdr)
     {
-      Lisp_Object entry = XCONS (scan)->car;
-      
-      if (XTYPE (entry) == Lisp_String
+      Lisp_Object entry;
+
+      entry = XCONS (scan)->car;
+      if (STRINGP (entry)
          && XSTRING (entry)->size > varlen
          && XSTRING (entry)->data[varlen] == '='
+#ifdef WINDOWSNT
+         /* NT environment variables are case insensitive.  */
+         && ! strnicmp (XSTRING (entry)->data, var, varlen))
+#else  /* not WINDOWSNT */
          && ! bcmp (XSTRING (entry)->data, var, varlen))
+#endif /* not WINDOWSNT */
        {
          *value    = (char *) XSTRING (entry)->data + (varlen + 1);
          *valuelen = XSTRING (entry)->size - (varlen + 1);
@@ -754,10 +929,14 @@ egetenv (var)
 init_callproc_1 ()
 {
   char *data_dir = egetenv ("EMACSDATA");
-    
+  char *doc_dir = egetenv ("EMACSDOC");
+
   Vdata_directory
     = Ffile_name_as_directory (build_string (data_dir ? data_dir 
                                             : PATH_DATA));
+  Vdoc_directory
+    = Ffile_name_as_directory (build_string (doc_dir ? doc_dir
+                                            : PATH_DOC));
 
   /* Check the EMACSPATH environment variable, defaulting to the
      PATH_EXEC path from paths.h.  */
@@ -775,47 +954,70 @@ init_callproc ()
   register char * sh;
   Lisp_Object tempdir;
 
-  if (initialized && !NILP (Vinvocation_directory))
+  if (initialized && !NILP (Vinstallation_directory))
     {
-      /* Add to the path the ../lib-src dir of the Emacs executable,
-        if that dir exists.  */
-      Lisp_Object tem, tem1;
-      tem = Fexpand_file_name (build_string ("../lib-src"),
-                              Vinvocation_directory);
-      tem1 = Ffile_exists_p (tem);
-      if (!NILP (tem1) && NILP (Fmember (tem, Vexec_path)))
+      /* Add to the path the lib-src subdir of the installation dir.  */
+      Lisp_Object tem;
+      tem = Fexpand_file_name (build_string ("lib-src"),
+                              Vinstallation_directory);
+      if (NILP (Fmember (tem, Vexec_path)))
        {
+#ifndef DOS_NT
+         /* MSDOS uses wrapped binaries, so don't do this.  */
          Vexec_path = nconc2 (Vexec_path, Fcons (tem, Qnil));
          Vexec_directory = Ffile_name_as_directory (tem);
+#endif /* not DOS_NT */
 
          /* If we use ../lib-src, maybe use ../etc as well.
             Do so if ../etc exists and has our DOC-... file in it.  */
          if (data_dir == 0)
            {
-             Lisp_Object tem, tem2, tem3;
-             tem = Fexpand_file_name (build_string ("../etc"),
-                                      Vinvocation_directory);
-             tem2 = Fexpand_file_name (Vdoc_file_name, tem);
-             tem3 = Ffile_exists_p (tem2);
-             if (!NILP (tem3))
-               Vdata_directory = Ffile_name_as_directory (tem);
+             tem = Fexpand_file_name (build_string ("etc"),
+                                      Vinstallation_directory);
+             Vdoc_directory = Ffile_name_as_directory (tem);
            }
        }
     }
 
+  /* Look for the files that should be in etc.  We don't use
+     Vinstallation_directory, because these files are never installed
+     in /bin near the executable, and they are never in the build
+     directory when that's different from the source directory.
+
+     Instead, if these files are not in the nominal place, we try the
+     source directory.  */
+  if (data_dir == 0)
+    {
+      Lisp_Object tem, tem1, newdir;
+
+      tem = Fexpand_file_name (build_string ("GNU"), Vdata_directory);
+      tem1 = Ffile_exists_p (tem);
+      if (NILP (tem1))
+       {
+         newdir = Fexpand_file_name (build_string ("../etc/"),
+                                     build_string (PATH_DUMPLOADSEARCH));
+         tem = Fexpand_file_name (build_string ("GNU"), newdir);
+         tem1 = Ffile_exists_p (tem);
+         if (!NILP (tem1))
+           Vdata_directory = newdir;
+       }
+    }
+
   tempdir = Fdirectory_file_name (Vexec_directory);
   if (access (XSTRING (tempdir)->data, 0) < 0)
     {
-      printf ("Warning: arch-dependent data dir (%s) does not exist.\n",
-             XSTRING (Vexec_directory)->data);
+      fprintf (stderr,
+              "Warning: arch-dependent data dir (%s) does not exist.\n",
+              XSTRING (Vexec_directory)->data);
       sleep (2);
     }
 
   tempdir = Fdirectory_file_name (Vdata_directory);
   if (access (XSTRING (tempdir)->data, 0) < 0)
     {
-      printf ("Warning: arch-independent data dir (%s) does not exist.\n",
-             XSTRING (Vdata_directory)->data);
+      fprintf (stderr,
+              "Warning: arch-independent data dir (%s) does not exist.\n",
+              XSTRING (Vdata_directory)->data);
       sleep (2);
     }
 
@@ -842,11 +1044,18 @@ set_process_environment ()
 
 syms_of_callproc ()
 {
-#ifdef MSDOS
-  DEFVAR_LISP ("binary-process", &Vbinary_process,
+#ifdef DOS_NT
+  Qbuffer_file_type = intern ("buffer-file-type");
+  staticpro (&Qbuffer_file_type);
+
+  DEFVAR_LISP ("binary-process-input", &Vbinary_process_input,
+    "*If non-nil then new subprocesses are assumed to take binary input.");
+  Vbinary_process_input = Qnil;
+
+  DEFVAR_LISP ("binary-process-output", &Vbinary_process_output,
     "*If non-nil then new subprocesses are assumed to produce binary output.");
-  Vbinary_process = Qnil;
-#endif
+  Vbinary_process_output = Qnil;
+#endif /* DOS_NT */
 
   DEFVAR_LISP ("shell-file-name", &Vshell_file_name,
     "*File name to load inferior shells from.\n\
@@ -864,6 +1073,10 @@ especially executable programs intended for Emacs to invoke.");
     "Directory of architecture-independent files that come with GNU Emacs,\n\
 intended for Emacs to use.");
 
+  DEFVAR_LISP ("doc-directory", &Vdoc_directory,
+    "Directory containing the DOC file that comes with GNU Emacs.\n\
+This is usually the same as data-directory.");
+
   DEFVAR_LISP ("configure-info-directory", &Vconfigure_info_directory,
     "For internal use by the build procedure only.\n\
 This is the name of the directory in which the build procedure installed\n\