diff src/callproc.c @ 296:558b874b5259

Initial revision
author Jim Blandy <jimb@redhat.com>
date Mon, 24 Jun 1991 23:36:56 +0000
parents
children 9e60d5c117db
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/callproc.c	Mon Jun 24 23:36:56 1991 +0000
@@ -0,0 +1,525 @@
+/* Synchronous subprocess invocation for GNU Emacs.
+   Copyright (C) 1985, 1986, 1987, 1988 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs; see the file COPYING.  If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+
+#include <signal.h>
+
+#include "config.h"
+
+/* Define SIGCHLD as an alias for SIGCLD.  */
+
+#if !defined (SIGCHLD) && defined (SIGCLD)
+#define SIGCHLD SIGCLD
+#endif /* SIGCLD */
+
+#include <sys/types.h>
+#define PRIO_PROCESS 0
+#include <sys/file.h>
+#ifdef USG5
+#include <fcntl.h>
+#endif
+
+#ifndef O_RDONLY
+#define O_RDONLY 0
+#endif
+
+#ifndef O_WRONLY
+#define O_WRONLY 1
+#endif
+
+#include "lisp.h"
+#include "commands.h"
+#include "buffer.h"
+#include "paths.h"
+#include "process.h"
+
+#ifdef VMS
+extern noshare char **environ;
+#else
+extern char **environ;
+#endif
+
+#define max(a, b) ((a) > (b) ? (a) : (b))
+
+Lisp_Object Vexec_path, Vexec_directory;
+
+Lisp_Object Vshell_file_name;
+
+#ifndef MAINTAIN_ENVIRONMENT
+/* List of strings to append to front of environment of
+   all subprocesses when they are started.  */
+
+Lisp_Object Vprocess_environment;
+#endif
+
+/* True iff we are about to fork off a synchronous process or if we
+   are waiting for it.  */
+int synch_process_alive;
+
+/* Nonzero => this is a string explaining death of synchronous subprocess.  */
+char *synch_process_death;
+
+/* If synch_process_death is zero,
+   this is exit code of synchronous subprocess.  */
+int synch_process_retcode;
+
+#ifndef VMS  /* VMS version is in vmsproc.c.  */
+
+Lisp_Object
+call_process_cleanup (fdpid)
+     Lisp_Object fdpid;
+{
+  register Lisp_Object fd, pid;
+  fd = Fcar (fdpid);
+  pid = Fcdr (fdpid);
+  close (XFASTINT (fd));
+  kill (XFASTINT (pid), SIGKILL);
+  synch_process_alive = 0;
+  return Qnil;
+}
+
+DEFUN ("call-process", Fcall_process, Scall_process, 1, MANY, 0,
+  "Call PROGRAM synchronously in separate process.\n\
+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\
+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 nil or 0, returns immediately with value nil.\n\
+Otherwise waits for PROGRAM to terminate\n\
+and returns a numeric exit status or a signal name as a string.\n\
+If you quit, the process is killed with SIGKILL.")
+  (nargs, args)
+     int nargs;
+     register Lisp_Object *args;
+{
+  Lisp_Object display, buffer, path;
+  int fd[2];
+  int filefd;
+  register int pid;
+  char buf[1024];
+  int count = specpdl_ptr - specpdl;
+  register unsigned char **new_argv
+    = (unsigned char **) alloca ((max (2, nargs - 2)) * sizeof (char *));
+  struct buffer *old = current_buffer;
+#if 0
+  int mask;
+#endif
+  struct gcpro gcpro1;
+
+  GCPRO1 (*args);
+  gcpro1.nvars = nargs;
+
+  CHECK_STRING (args[0], 0);
+
+  if (nargs <= 1 || NULL (args[1]))
+    args[1] = build_string ("/dev/null");
+  else
+    args[1] = Fexpand_file_name (args[1], current_buffer->directory);
+
+  CHECK_STRING (args[1], 1);
+
+  {
+    register Lisp_Object tem;
+    buffer = tem = args[2];
+    if (nargs <= 2)
+      buffer = Qnil;
+    else if (!(EQ (tem, Qnil) || EQ (tem, Qt)
+	       || XFASTINT (tem) == 0))
+      {
+	buffer = Fget_buffer (tem);
+	CHECK_BUFFER (buffer, 2);
+      }
+  }
+
+  display = nargs >= 3 ? 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 (args[1])->data, O_RDONLY, 0);
+  if (filefd < 0)
+    {
+      report_file_error ("Opening process input file", Fcons (args[1], Qnil));
+    }
+  /* Search for program; barf if not found.  */
+  openp (Vexec_path, args[0], "", &path, 1);
+  if (NULL (path))
+    {
+      close (filefd);
+      report_file_error ("Searching for program", Fcons (args[0], Qnil));
+    }
+  new_argv[0] = XSTRING (path)->data;
+
+  if (XTYPE (buffer) == Lisp_Int)
+    fd[1] = open ("/dev/null", O_WRONLY), fd[0] = -1;
+  else
+    {
+      pipe (fd);
+#if 0
+      /* Replaced by close_process_descs */
+      set_exclusive_use (fd[0]);
+#endif
+    }
+
+  {
+    /* child_setup must clobber environ in systems with true vfork.
+       Protect it from permanent change.  */
+    register char **save_environ = environ;
+    register int fd1 = fd[1];
+    char **env;
+
+#ifdef MAINTAIN_ENVIRONMENT
+    env = (char **) alloca (size_of_current_environ ());
+    get_current_environ (env);
+#else
+    env = environ;
+#endif /* MAINTAIN_ENVIRONMENT */
+
+#if 0  /* Some systems don't have sigblock.  */
+    mask = sigblock (sigmask (SIGCHLD));
+#endif
+
+    /* Record that we're about to create a synchronous process.  */
+    synch_process_alive = 1;
+
+    pid = vfork ();
+
+    if (pid == 0)
+      {
+	if (fd[0] >= 0)
+	  close (fd[0]);
+#ifdef USG
+        setpgrp ();
+#else
+        setpgrp (pid, pid);
+#endif /* USG */
+	child_setup (filefd, fd1, fd1, new_argv, env, 0);
+      }
+
+#if 0
+    /* Tell SIGCHLD handler to look for this pid.  */
+    synch_process_pid = pid;
+    /* Now let SIGCHLD come through.  */
+    sigsetmask (mask);
+#endif
+
+    environ = save_environ;
+
+    close (filefd);
+    close (fd1);
+  }
+
+  if (pid < 0)
+    {
+      close (fd[0]);
+      report_file_error ("Doing vfork", Qnil);
+    }
+
+  if (XTYPE (buffer) == Lisp_Int)
+    {
+#ifndef subprocesses
+      wait_without_blocking ();
+#endif /* subprocesses */
+
+      UNGCPRO;
+      return Qnil;
+    }
+
+  record_unwind_protect (call_process_cleanup,
+			 Fcons (make_number (fd[0]), make_number (pid)));
+
+
+  if (XTYPE (buffer) == Lisp_Buffer)
+    Fset_buffer (buffer);
+
+  immediate_quit = 1;
+  QUIT;
+
+  {
+    register int nread;
+
+    while ((nread = read (fd[0], buf, sizeof buf)) > 0)
+      {
+	immediate_quit = 0;
+	if (!NULL (buffer))
+	  insert (buf, nread);
+	if (!NULL (display) && INTERACTIVE)
+	  redisplay_preserve_echo_area ();
+	immediate_quit = 1;
+	QUIT;
+      }
+  }
+
+  /* Wait for it to terminate, unless it already has.  */
+  wait_for_termination (pid);
+
+  immediate_quit = 0;
+
+  set_buffer_internal (old);
+
+  unbind_to (count, Qnil);
+
+  UNGCPRO;
+
+  if (synch_process_death)
+    return build_string (synch_process_death);
+  return make_number (synch_process_retcode);
+}
+#endif
+
+static void
+delete_temp_file (name)
+     Lisp_Object name;
+{
+  unlink (XSTRING (name)->data);
+}
+
+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\
+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\
+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\
+and returns a numeric exit status or a signal name as a string.\n\
+If you quit, the process is killed with SIGKILL.")
+  (nargs, args)
+     int nargs;
+     register Lisp_Object *args;
+{
+  register Lisp_Object filename_string, start, end;
+  char tempfile[20];
+  int count = specpdl_ptr - specpdl;
+  struct gcpro gcpro1;
+
+  GCPRO1 (*args);
+  gcpro1.nvars = 2;
+
+#ifdef VMS
+  strcpy (tempfile, "tmp:emacsXXXXXX.");
+#else
+  strcpy (tempfile, "/tmp/emacsXXXXXX");
+#endif
+  mktemp (tempfile);
+
+  filename_string = build_string (tempfile);
+  start = args[0];
+  end = args[1];
+  Fwrite_region (start, end, filename_string, Qnil, Qlambda);
+  record_unwind_protect (delete_temp_file, filename_string);
+
+  if (!NULL (args[3]))
+    Fdelete_region (start, end);
+
+  args[3] = filename_string;
+  Fcall_process (nargs - 2, args + 2);
+
+  UNGCPRO;
+  return unbind_to (count, Qnil);
+}
+
+#ifndef VMS /* VMS version is in vmsproc.c.  */
+
+/* This is the last thing run in a newly forked inferior
+   either synchronous or asynchronous.
+   Copy descriptors IN, OUT and ERR as descriptors 0, 1 and 2.
+   Initialize inferior's priority, pgrp, connected dir and environment.
+   then exec another program based on new_argv.
+
+   This function may change environ for the superior process.
+   Therefore, the superior process must save and restore the value
+   of environ around the vfork and the call to this function.
+
+   ENV is the environment for the subprocess.
+
+   SET_PGRP is nonzero if we should put the subprocess into a separate
+   process group.  */
+
+child_setup (in, out, err, new_argv, env, set_pgrp)
+     int in, out, err;
+     register char **new_argv;
+     char **env;
+     int set_pgrp;
+{
+  register int pid = getpid();
+
+  setpriority (PRIO_PROCESS, pid, 0);
+
+#ifdef subprocesses
+  /* Close Emacs's descriptors that this process should not have.  */
+  close_process_descs ();
+#endif
+
+  /* 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.
+     If using vfork and C_ALLOCA it is safe because that changes
+     the superior's static variables as if the superior had done alloca
+     and will be cleaned up in the usual way.  */
+
+  if (XTYPE (current_buffer->directory) == Lisp_String)
+    {
+      register unsigned char *temp;
+      register int i;
+
+      i = XSTRING (current_buffer->directory)->size;
+      temp = (unsigned char *) alloca (i + 2);
+      bcopy (XSTRING (current_buffer->directory)->data, temp, i);
+      if (temp[i - 1] != '/') temp[i++] = '/';
+      temp[i] = 0;
+      /* Switch to that directory, and report any error.  */
+      if (chdir (temp) < 0)
+	report_file_error ("In chdir",
+			   Fcons (current_buffer->directory, Qnil));
+    }
+
+#ifndef MAINTAIN_ENVIRONMENT
+  /* Set `env' to a vector of the strings in Vprocess_environment.  */
+  {
+    register Lisp_Object tem;
+    register char **new_env;
+    register int new_length;
+
+    new_length = 0;
+    for (tem = Vprocess_environment;
+	 (XTYPE (tem) == Lisp_Cons
+	  && XTYPE (XCONS (tem)->car) == Lisp_String);
+	 tem = XCONS (tem)->cdr)
+      new_length++;
+
+    /* new_length + 1 to include terminating 0 */
+    env = new_env = (char **) alloca ((new_length + 1) * sizeof (char *));
+
+    /* Copy the env strings into new_env.  */
+    for (tem = Vprocess_environment;
+	 (XTYPE (tem) == Lisp_Cons
+	  && XTYPE (XCONS (tem)->car) == Lisp_String);
+	 tem = XCONS (tem)->cdr)
+      *new_env++ = (char *) XSTRING (XCONS (tem)->car)->data;
+    *new_env = 0;
+  }
+#endif /* Not MAINTAIN_ENVIRONMENT */
+
+  close (0);
+  close (1);
+  close (2);
+
+  dup2 (in, 0);
+  dup2 (out, 1);
+  dup2 (err, 2);
+  close (in);
+  close (out);
+  close (err);
+
+  setpgrp_of_tty (pid);
+
+#ifdef vipc
+  something missing here;
+#endif /* vipc */
+
+  /* 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.  */
+  environ = env;
+  execvp (new_argv[0], new_argv);
+
+  write (1, "Couldn't exec the program ", 26);
+  write (1, new_argv[0], strlen (new_argv[0]));
+  _exit (1);
+}
+
+#endif /* not VMS */
+
+init_callproc ()
+{
+  register char * sh;
+  register char **envp;
+  Lisp_Object execdir;
+
+  /* Turn PATH_EXEC into a path.  `==' is just a string which we know
+     will not be the name of an environment variable.  */
+  Vexec_path = decode_env_path ("==", PATH_EXEC);
+  Vexec_directory = Ffile_name_as_directory (Fcar (Vexec_path));
+  Vexec_path = nconc2 (decode_env_path ("PATH", ""), Vexec_path);
+
+  execdir = Fdirectory_file_name (Vexec_directory);
+  if (access (XSTRING (execdir)->data, 0) < 0)
+    {
+      printf ("Warning: executable/documentation dir (%s) does not exist.\n",
+	      XSTRING (Vexec_directory)->data);
+      sleep (2);
+    }
+
+#ifdef VMS
+  Vshell_file_name = build_string ("*dcl*");
+#else
+  sh = (char *) egetenv ("SHELL");
+  Vshell_file_name = build_string (sh ? sh : "/bin/sh");
+#endif
+
+#ifndef MAINTAIN_ENVIRONMENT
+  /* The equivalent of this operation was done
+     in init_environ in environ.c if MAINTAIN_ENVIRONMENT */
+  Vprocess_environment = Qnil;
+#ifndef CANNOT_DUMP
+  if (initialized)
+#endif
+    for (envp = environ; *envp; envp++)
+      Vprocess_environment = Fcons (build_string (*envp),
+				    Vprocess_environment);
+#endif /* MAINTAIN_ENVIRONMENT */
+}
+
+syms_of_callproc ()
+{
+  DEFVAR_LISP ("shell-file-name", &Vshell_file_name,
+    "*File name to load inferior shells from.\n\
+Initialized from the SHELL environment variable.");
+
+  DEFVAR_LISP ("exec-path", &Vexec_path,
+    "*List of directories to search programs to run in subprocesses.\n\
+Each element is a string (directory name) or nil (try default directory).");
+
+  DEFVAR_LISP ("exec-directory", &Vexec_directory,
+    "Directory that holds programs that come with GNU Emacs,\n\
+intended for Emacs to invoke.");
+
+#ifndef MAINTAIN_ENVIRONMENT
+  DEFVAR_LISP ("process-environment", &Vprocess_environment,
+    "List of strings to append to environment of subprocesses that are started.\n\
+Each string should have the format ENVVARNAME=VALUE.");
+#endif
+
+#ifndef VMS
+  defsubr (&Scall_process);
+#endif
+  defsubr (&Scall_process_region);
+}