Logo Search packages:      
Sourcecode: jed version File versions

jprocess.c

/* -*- mode: C; mode: fold; -*- */
/* Copyright (c) 1992, 1998, 2000, 2002 John E. Davis
 * This file is part of JED editor library source.
 *
 * You may distribute this file under the terms the GNU General Public
 * License.  See the file COPYING for more information.
 */
#include "config.h"
#include "jed-feat.h"

#include <stdio.h>

#if defined(__QNX__) && defined(__WATCOMC__)
# include <env.h>
#endif

#if JED_HAS_SUBPROCESSES
/* Everything else here is in this '#if' */

/*{{{ Include Files */

#include <string.h>
#include <errno.h>
#include <signal.h>

#ifdef __os2__
# include <io.h>
# include <process.h>
#endif

#if defined(__BORLANDC__) && (__BORLANDC__>>8)==5 /* [JMS:MISC] - needed for open/close in BC v5 */
# include <io.h>
# include <dos.h>
#endif

#include <slang.h>

#include "jdmacros.h"

#ifdef HAVE_UNISTD_H
# include <unistd.h>
# include <fcntl.h>
#endif

#include <sys/types.h>
#ifdef HAVE_SYS_WAIT_H
# include <sys/wait.h>
#endif
#ifdef HAVE_SYS_FCNTL_H
# include <sys/fcntl.h>
#endif
#ifndef WEXITSTATUS
# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
#endif
#ifndef WIFEXITED
# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
#endif

#ifdef REALLY_HAVE_TERMIOS_H
# ifndef __os2__
#  define USE_PTY
# endif
#endif

#include "buffer.h"
#include "ins.h"
#include "ledit.h"
#include "misc.h"
#include "jprocess.h"
#include "paste.h"
#include "sig.h"
#include "cmds.h"

/*}}}*/

#define MAX_TTY_SLAVE_NAME    256

int Num_Subprocesses;
int Max_Subprocess_FD;

/* This also servers as a lookup table for actual system pids to the 
 * pseudo-pids used here.  See, e.g., jed_get_child_status for usage in this
 * fashion.
 */
int Subprocess_Read_fds [MAX_PROCESSES][2];   /* 0 is actual fd, 1 is our rep */

volatile int Child_Status_Changed_Flag;/* if this is non-zero, editor
                              * should call the appropriate
                              * function below to call slang
                              * handlers.
                              */
typedef struct /*{{{*/
{
   int flags;                        /* This is zero if the process is gone
                              * and the status is nolonger avail */
#define PROCESS_RUNNING       1
#define PROCESS_STOPPED       2
#define PROCESS_ALIVE         3
#define PROCESS_EXITED        4
#define PROCESS_SIGNALLED     8
   int return_status;                /* This value depends on the flags */
   
   int status_changed;               /* non-zero if status changed. */
   int rd, wd;                       /* read/write descriptors */
   int is_pty;
   int pid;                    /* real process pid */
   int output_type;
#define PROCESS_USE_BUFFER    1
#define PROCESS_USE_SLANG     2
#define PROCESS_SAVE_POINT    4
#define PROCESS_AT_POINT      8
   Buffer *buffer;                   /* buffer associated with process */
   SLang_Name_Type *slang_fun;             /* function to pass output to */
   SLang_MMT_Type *umark;            /* marks point of last output */
   
   SLang_Name_Type *status_change_fun; /* call this if process status changes 
                              * The function should be declared like
                              * define fun (pid, flags, status);
                              * The flags parameter corresponds to
                              * the flags field in this struct and 
                              * the pid is NOT the pid of this struct.
                              * status depends upon flags.
                              */
   int quietly_kill_on_exit;
} 
/*}}}*/
Process_Type;

static Process_Type Processes[MAX_PROCESSES];

static int signal_safe_close (int fd)
{
   while (-1 == close (fd))
     {
#ifdef EINTR
      if (errno == EINTR)
        continue;
#endif
      return -1;
     }
   return 0;
}

static int signal_safe_open (char *file, int mode)
{
   int fd;

   while (-1 == (fd = open (file, mode)))
     {
#ifdef EINTR
      if (errno == EINTR)
        continue;
#endif
#ifdef EAGAIN
      if (errno == EAGAIN)
        {
           sleep (1);
           continue;
        }
#endif
      return -1;
     }
   return fd;
}

static int signal_safe_fcntl (int fd, int cmd, int arg)
{
   int ret;

   while (-1 == (ret = fcntl (fd, cmd, arg)))
     {
#ifdef EINTR
      if (errno == EINTR)
        continue;
#endif
#ifdef EAGAIN
      if (errno == EAGAIN)
        {
           sleep (1);
           continue;
        }
#endif
      return -1;
     }
   return ret;
}

static int signal_safe_dup2 (int fd1, int fd2)
{
   while (-1 == dup2 (fd1, fd2))
     {
#ifdef EINTR
      if (errno == EINTR)
        continue;
#endif
      return -1;
     }
   return 0;
}


static Process_Type *get_process (int fd) /*{{{*/
{
   Process_Type *p;
   
   if ((fd >= 0) && (fd < MAX_PROCESSES)
       && (p = &Processes[fd], p->flags != 0))
     return p;
   
   jed_verror ("process '%d' does not exist.", fd);
   return NULL;
}

/*}}}*/

static void call_slang_status_change_hook (Process_Type *p) /*{{{*/
{
   Buffer *cbuf = CBuf;
   if ((p->status_change_fun == NULL) || (p->buffer == NULL)) return;
   
   cbuf->locked++;
   switch_to_buffer (p->buffer);
   SLang_push_integer ((int) (p - Processes));
   SLang_push_integer (p->flags);
   SLang_push_integer (p->return_status);
   SLexecute_function (p->status_change_fun);
   touch_screen ();
   if (CBuf != cbuf) switch_to_buffer (cbuf);
   cbuf->locked--;
}

/*}}}*/

#if 1
int jed_signal_process (int *fd, int *sig) /*{{{*/
{
   Process_Type *p;
   if (NULL == (p = get_process (*fd))) return -1;

   kill (p->pid, *sig);
   return 0;
}

int jed_signal_fg_process (int *fd, int *sig) /*{{{*/
{
   int pid;

   Process_Type *p;
   if (NULL == (p = get_process (*fd))) return -1;

#ifdef HAVE_TCGETPGRP
   pid = tcgetpgrp (p->rd);
   if (pid == -1)
#endif
     pid = p->pid;

   (void) kill (-pid, *sig);
   return 0;
}

/*}}}*/
#endif

static void close_rd_and_wd (Process_Type *p) /*{{{*/
{   
   if (p->rd != -1) 
     {
      signal_safe_close (p->rd);
      p->rd = -1;
     }
   if (p->wd != -1)
     {
      if (p->is_pty == 0) signal_safe_close (p->wd); 
      p->wd = -1;
     }
}

/*}}}*/

/* This routine is called to clean up after the process has exited.  
 * After getting the exit status, we call a slang hook and if the
 * process is dead, adjust the process arrays to delete the process.
 */
static void get_process_status (Process_Type *p) /*{{{*/
{
   int i;
   int fd, slfd;
   
   /* Call slang to let it know what happened.  Do it first before we 
    * really shut it down to give the hook a chance to query the state of
    * it before it returns.
    */
   call_slang_status_change_hook (p);
   if (p->flags & PROCESS_ALIVE) return;

   /* Process is dead.  So perform clean up. */
   close_rd_and_wd (p);
   
   if (p->buffer != NULL) p->buffer->subprocess = 0;
   slfd = (int) (p - Processes);

   if (p->umark != NULL) SLang_free_mmt (p->umark);

#if SLANG_VERSION > 10400
   SLang_free_function (p->slang_fun);
   SLang_free_function (p->status_change_fun);
#endif

   memset ((char *)p, 0, sizeof (Process_Type));
   
   /* Adjust the array of read descriptors */

   i = 0;
   
   while (i < Num_Subprocesses)
     {
      if (Subprocess_Read_fds[i][0] == slfd)
        break;
        
      i++;
     }
   fd = Subprocess_Read_fds [i][0];

   Num_Subprocesses--;
   while (i < Num_Subprocesses)
     {
      Subprocess_Read_fds[i][0] = Subprocess_Read_fds[i + 1][0];
      Subprocess_Read_fds[i][1] = Subprocess_Read_fds[i + 1][1];
      i++;
     }
   
   
   if (Max_Subprocess_FD == fd)
     {
      i = 0;
      fd = -1;
      while (i < Num_Subprocesses)
        {
           if (Subprocess_Read_fds[i][0] > fd) 
             fd = Subprocess_Read_fds[i][0];
           i++;
        }
      Max_Subprocess_FD = fd;
     }
}

/*}}}*/

int jed_close_process (int *fd) /*{{{*/
{
   Process_Type *p;
   
   if (NULL == (p = get_process (*fd))) return -1;
   
   close_rd_and_wd (p);
   
   kill (-p->pid, SIGINT);
   
   /* This is probably a bad idea.  It is better to check to see if it still 
    * around and the set a flag indicating that the user wants it killed.
    */
   
   /* Did we kill it? Make sure. */
   kill (-p->pid, SIGKILL);

   if (p->buffer != NULL) p->buffer->subprocess = 0;
   
   /* This next function wraps things up --- no need to.  Let handler do it. */
   /* get_process_status (p); */
   return 0;
}

/*}}}*/

void jed_kill_process (int fd) /*{{{*/
{
   /* This function is called when the buffer is going to be destroyed */
   Processes[fd].buffer = NULL;
   jed_close_process (&fd);
}

/*}}}*/

void jed_get_child_status (void) /*{{{*/
{
   Process_Type *p, *pmax;
   
   Child_Status_Changed_Flag--;
   
   get_process_input (&Number_Zero);
   p = Processes;
   pmax = p + MAX_PROCESSES;
   
   while (p < pmax)
     {
      if (p->flags && p->status_changed)
        {
           read_process_input ((int) (p - Processes));
           p->status_changed--;
           get_process_status (p);
        }
      p++;
     }
}

/*}}}*/

static void child_signal_handler (int sig) /*{{{*/
{
   int status;
   int return_status;
   int pid;
   Process_Type *p, *pmax;
   int save_errno = errno;
   
   (void) sig;

   while (1)
     {
      pid = (int) waitpid (-1, &status, WNOHANG | WUNTRACED);
      if (pid == -1) 
        {
           if (errno == ECHILD) break;
           continue;
        }
      
      if (pid == 0) break;
      
      return_status = 0;
      
      if (WIFEXITED (status))
        {
           return_status = WEXITSTATUS (status);
           status = PROCESS_EXITED;
        }
      else if (WIFSIGNALED (status))
        {
           return_status = WTERMSIG (status);
           status = PROCESS_SIGNALLED;
        }
#ifdef WIFSTOPPED
      else if (WIFSTOPPED (status))
        {
           return_status = WSTOPSIG(status);
           status = PROCESS_STOPPED;
        }
#endif
      /* What else?? */
      
      p = Processes;
      pmax = p + MAX_PROCESSES;
      while (p < pmax)
        {
           if (p->pid == pid)
             {
              p->flags = status;
              p->status_changed++;
              p->return_status = return_status;
              break;
             }
           p++;
        }
     }
   SLsignal_intr (SIGCHLD, child_signal_handler);
   errno = save_errno;
   Child_Status_Changed_Flag++;
}

/*}}}*/

#ifdef USE_PTY
# include "pty.c"
#endif

static int get_master_slave_fds (int *slave_read, int *slave_write,
                         int *master_read, int *master_write,
                         char *slave_tty_name, int *is_pty)
{
#ifdef USE_PTY
   int master;
   
   if (-1 == pty_open_master_pty (&master, slave_tty_name))
     return -1;

   *master_read = *master_write = master;
   *slave_read = *slave_write = -1;
   
   *is_pty = 1;
   return 0;
#else

   int fds0[2], fds1[2];
   
   if (-1 == pipe (fds0))
     return -1;
   
   if (-1 == pipe (fds1))
     {
      signal_safe_close (fds0[0]);
      signal_safe_close (fds0[1]);
      return -1;
     }
   *master_read = fds0[0];
   *slave_write = fds0[1];
   *master_write = fds1[1];
   *slave_read = fds1[0];
   *slave_tty_name = 0;
   *is_pty = 0;
   return 0;
#endif
}


static void my_setenv (char *what, char *value)
{
#ifdef HAVE_SETENV
   (void) setenv (what, value, 1);
#else
# ifdef HAVE_PUTENV
   char buf[512];
   _SLsnprintf (buf, sizeof (buf), "%s=%s", what, value);
   (void) putenv (buf);
# endif
#endif
}

static void my_unsetenv (char *what)
{
#ifdef HAVE_UNSETENV
   unsetenv (what);
#endif
}

   
#ifdef __os2__
static int open_process (char *pgm, char **argv) /*{{{*/
{
    int val;
    int pd;
   int slave_read, slave_write, master_read, master_write, org_fd[3];
   int pid, i;
   Process_Type *p;
   SLang_MMT_Type *mmt;
   char slave_tty_name [MAX_TTY_SLAVE_NAME];
   int max_write_tries;
   SLSig_Fun_Type *sig_orig[NSIG];

   if (Jed_Secure_Mode)
     {
      msg_error ("Access to shell denied.");
      return -1;
     }
   
   pd = 0; while ((pd < MAX_PROCESSES) && Processes[pd].flags) pd++;
   if (pd == MAX_PROCESSES) return -1;
   p = &Processes[pd];

   SLMEMSET ((char *) p, 0, sizeof (Process_Type));

   if (NULL == (mmt = jed_make_user_object_mark ()))
     return -1;
   
   if (-1 == get_master_slave_fds (&slave_read, &slave_write,
                           &master_read, &master_write,
                           slave_tty_name, &p->is_pty))
     {
      SLang_free_mmt (mmt);
      return -1;
     }

   SLsignal_intr (SIGCHLD, child_signal_handler);
   
     {
      char ch;
      
      /* At this point the slave tty is in raw mode.  Make sure that
       * the read to synchronize with the parent blocks.
       */
      val = signal_safe_fcntl (slave_read, F_GETFL, 0);
      signal_safe_fcntl (slave_read, F_SETFL, val & ~O_NONBLOCK);

      signal_safe_fcntl (slave_read, F_SETFL, val);

      
      org_fd[0] = dup(0);
      org_fd[1] = dup(1);
      org_fd[2] = dup(2);

      signal_safe_close (0);
      signal_safe_close (1);
      signal_safe_close (2);
      
      signal_safe_dup2(slave_read, 0);    /* stdin */
      signal_safe_dup2 (slave_write, 1);  /* stdout */
      signal_safe_dup2 (slave_write, 2);  /* stderr */

      signal_safe_close (slave_read);
      signal_safe_close (slave_write);
      setvbuf (stdout, NULL, _IONBF, 0);
      
      for (i=1 ; i<NSIG ; i++)
        sig_orig[i] = SLsignal(i, SIG_DFL);
      
      pid = spawnvp (P_SESSION | P_MINIMIZE | P_BACKGROUND, pgm, argv);

      for (i=1 ; i<NSIG ; i++)
        SLsignal(i, sig_orig[i]);
      signal_safe_close (0);
      signal_safe_close (1);
      signal_safe_close (2);
      signal_safe_dup2(org_fd[0], 0);
      signal_safe_dup2(org_fd[1], 1);
      signal_safe_dup2(org_fd[2], 2);
      signal_safe_close (org_fd[0]); 
      signal_safe_close (org_fd[1]); 
      signal_safe_close (org_fd[2]); 
      signal_safe_close (slave_read);
      signal_safe_close (slave_write);
      
      if (pid < 0)
        {
           signal_safe_close (master_read);
           signal_safe_close (master_write); 
           p->flags = 0;
           SLang_free_mmt (mmt);
           return -1;
           
        }
      p->pid = pid;
     }

   

   p->flags = PROCESS_RUNNING;
   p->rd = master_read;
   p->wd = master_write;
   
   Subprocess_Read_fds[Num_Subprocesses][0] = master_read;
   Subprocess_Read_fds[Num_Subprocesses][1] = pd;
   if (master_read > Max_Subprocess_FD) Max_Subprocess_FD = master_read;
   Num_Subprocesses += 1;
   
   val = signal_safe_fcntl (master_read, F_GETFL, 0);
   val |= O_NONBLOCK;
   signal_safe_fcntl (master_read, F_SETFL, val);

   CBuf->subprocess = pd + 1;

   /* Processing options */
   p->buffer = CBuf;
   p->output_type = PROCESS_USE_BUFFER;
   p->umark = mmt;
   SLang_inc_mmt (mmt);        /* tell slang we are keeping a copy */
   
   return pd;
}
/*}}}*/
#else /* unix and compatible OS */

static volatile int SigUsr_Flag;
static void sigusr_handler (int sig)
{
   (void) sig;

   SigUsr_Flag = 1;
}

static sigset_t Old_Signal_Mask, Zero_Signal_Mask;

static int init_child_parent_sync (void)
{
   sigset_t new_mask;

   if (SIG_ERR == SLsignal (SIGUSR1, sigusr_handler))
     return -1;
   
   /* Get a copy of the current signal mask */
   while ((-1 == sigprocmask (SIG_BLOCK, NULL, &Zero_Signal_Mask))
        && (errno == EINTR))
     ;
   /* and remove SIGUSR1 from it. */
   sigdelset (&Zero_Signal_Mask, SIGUSR1);

   /* Add SIGUSR1 to the set of currently blocked signals */
   sigemptyset (&new_mask);
   sigaddset (&new_mask, SIGUSR1);

   while ((-1 == sigprocmask (SIG_BLOCK, &new_mask, &Old_Signal_Mask))
        && (errno == EINTR))
     ;
   
   SigUsr_Flag = 0;
   return 0;
}

static void wait_for_parent (void)
{
   /* Suspend this process and wait for a signal.  All signals are allowed
    * here.  
    */
   while (SigUsr_Flag == 0)
     sigsuspend (&Zero_Signal_Mask);
   
   SigUsr_Flag = 0;
   
   while ((-1 == sigprocmask (SIG_SETMASK, &Old_Signal_Mask, NULL))
        && (errno == EINTR))
     ;
}

static void tell_child_to_go (pid_t pid)
{   
   kill (pid, SIGUSR1);

   SigUsr_Flag = 0;
   while ((-1 == sigprocmask (SIG_SETMASK, &Old_Signal_Mask, NULL))
        && (errno == EINTR))
     ;
}

static int set_non_blocking_io (int fd)
{   
   int val;
   
   val = signal_safe_fcntl (fd, F_GETFL, 0);
   return signal_safe_fcntl (fd, F_SETFL, val | O_NONBLOCK);
}

#ifdef SIGTRAP
static void sigtrap_handler (int sig)
{
   (void) sig;
}
#endif
   
static int open_process (char *pgm, char **argv) /*{{{*/
{
   int pd;
   int slave_read, slave_write, master_read, master_write;
   int pid, i;
   Process_Type *p;
   SLang_MMT_Type *mmt;
   char slave_tty_name [MAX_TTY_SLAVE_NAME];

   if (Jed_Secure_Mode)
     {
      msg_error ("Access to shell denied.");
      return -1;
     }
   
   pd = 0; while ((pd < MAX_PROCESSES) && Processes[pd].flags) pd++;
   if (pd == MAX_PROCESSES) return -1;
   p = &Processes[pd];

   SLMEMSET ((char *) p, 0, sizeof (Process_Type));

   if (NULL == (mmt = jed_make_user_object_mark ()))
     return -1;
   
   if (-1 == get_master_slave_fds (&slave_read, &slave_write,
                           &master_read, &master_write,
                           slave_tty_name, &p->is_pty))
     {
      SLang_free_mmt (mmt);
      return -1;
     }

   SLsignal_intr (SIGCHLD, child_signal_handler);

   if ((-1 == init_child_parent_sync ())
       || ((pid = fork ()) < 0))
     {
      signal_safe_close (master_read);
      if (p->is_pty == 0)
        {
           signal_safe_close (slave_read);
           signal_safe_close (master_write); 
           signal_safe_close (slave_write);
        }
      p->flags = 0;
      SLang_free_mmt (mmt);
      return -1;
     }
   p->pid = pid;
   
   /* Make the child its own process group leader.  Do it here too because
    * we are not sure which one will run first.  We have to do this because
    * if not, a ^G will be sent to ALL child subprocesses possibly killing 
    * them unless they catch the signal.  This call means that the INTR signal
    * will not be sent to any child processes sent by this fork.
    */
   if (p->is_pty == 0) 
     setpgid(pid, pid);

   if (pid == 0)
     {
      /* child code */
      wait_for_parent ();
      
      if (p->is_pty == 0)
        signal_safe_close (master_write);        /* close write end of 0 */
      signal_safe_close (master_read);           /* close read end of 1 */

      for (i = 0; i < 32; i++) 
        {
#ifdef SIGTRAP
      /* Under Linux, subprocesses fail if run under the debugger because
       * something sends this child a SIGTRAP.  Let's try ignoring it.
       */
           if (i == SIGTRAP)
             SLsignal (i, sigtrap_handler);
           else
#endif
             SLsignal (i, SIG_DFL);
        }
#ifdef USE_PTY    
      /* Call set setsid so that the child will become the session leader.
       * This has the side effect that we will loose the controlling
       * terminal.  For this reason, the pty slave is opened after setsid
       * and then for good luck, the controlling terminal is set 
       * via the TIOCSCTTY ioctl.
       */
      if (p->is_pty)
        {
           if (-1 == setsid ())
             fprintf (stderr, "child: setsid failed.\n");
      
           if (-1 == pty_open_slave_pty (slave_tty_name, &slave_read))
             {
              fprintf (stderr, "child: failed to open slave.");
              _exit (1);
             }
           slave_write = slave_read;

           /* (void) pty_setup_slave_term (slave_read, 0); */
        }
#endif      /* USE_PTY */

#ifdef USE_PTY
      /* Put tty back into cbreak mode. */
      if (p->is_pty) (void) pty_setup_slave_term (slave_read, 0);
#endif
      if ((signal_safe_dup2(slave_read, 0) < 0)     /* stdin */
          || (signal_safe_dup2 (slave_write, 1) < 0) /* stdout */
          || (signal_safe_dup2 (slave_write, 2) < 0)) /* stderr */
        {
           fprintf (stderr, "dup2 failed. errno = %d\n", errno);
           _exit (1);
        }

      my_setenv ("TERM", "unknown");
      my_unsetenv ("TERMCAP");

#ifdef SIGTRAP
      SLsignal (SIGTRAP, SIG_DFL);
#endif
      if (execvp (pgm, argv) < 0)
        {
           fprintf (stderr, "execvp of %s failed!\r\n", pgm);
           _exit (1);
        }
     }
   
   /* parent */
   if (p->is_pty == 0) 
     {
      signal_safe_close (slave_read);
      signal_safe_close (slave_write);
     }

   p->flags = PROCESS_RUNNING;
   p->rd = master_read;
   p->wd = master_write;
   
   Subprocess_Read_fds[Num_Subprocesses][0] = master_read;
   Subprocess_Read_fds[Num_Subprocesses][1] = pd;
   if (master_read > Max_Subprocess_FD) Max_Subprocess_FD = master_read;
   Num_Subprocesses += 1;
   
   CBuf->subprocess = pd + 1;

   /* Processing options */
   p->buffer = CBuf;
   p->output_type = PROCESS_USE_BUFFER;
   p->umark = mmt;
   SLang_inc_mmt (mmt);        /* tell slang we are keeping a copy */
   
   set_non_blocking_io (master_read);
   set_non_blocking_io (master_write);

   /* Tell child it is ok to go. */
   tell_child_to_go (pid);
   return pd;
}

/*}}}*/
#endif                               /* __os2__ */


/* This function is only called when we are reading characters from the 
 * keyboard.  Keyboard input has the highest priority and this is called only
 * if there is no input ready.
 */
void read_process_input (int fd) /*{{{*/
{
   unsigned char buf[513];           /* last byte for 0 char */
   int n;
   Buffer *b = CBuf, *pbuf;
   Process_Type *p;
   int otype, total;
   
   /* Should never happen */
   if (NULL == (p = get_process (fd))) 
     {
      return;
     }
   
   otype = p->output_type;
   pbuf = p->buffer;
   
   if (pbuf != NULL)
     {
      switch_to_buffer (pbuf);
      pbuf->locked++;
     }
   
   total = 0;
   if (otype & PROCESS_SAVE_POINT) push_spot ();
   while ((n = read (p->rd, buf, 512)) > 0)
     {
      total += n;
      if (p->buffer == NULL) continue;
      
      if (otype & PROCESS_USE_BUFFER) 
        {
           if (0 == (otype & PROCESS_AT_POINT)) eob ();
           (void) jed_ins_chars (buf, n);
           jed_move_user_object_mark (p->umark);
        }
      else if (otype == PROCESS_USE_SLANG)
        {
           buf[n] = 0;
           SLang_push_integer ((int) (p - Processes));
           SLang_push_string ((char *) buf);
           SLexecute_function (p->slang_fun);    /* function to pass output to */
        }
     }
   
   if (otype & PROCESS_SAVE_POINT) pop_spot ();
   else if (otype & PROCESS_USE_BUFFER) move_window_marks (0);
   
   if (p->buffer != NULL)
     {
      if (b != CBuf) switch_to_buffer (b);
      pbuf->locked--;
     }
   if (total)
     {
#ifdef __os2__
      /* Why does OS/2 need this?? -- JED */
      JWindow->trashed = 1;
      update ((Line *)NULL, 0, 1, 0);
#endif
      touch_screen ();
     }
}


/*}}}*/

static int write_to_process (int fd, char *buf, unsigned int len)
{
   if (len == 0) 
     return 0;
   
   /* FIXME!! check for write errors!! */
   while (-1 == write (fd, buf, len))
     {
      if (errno != EINTR)
        break;
     }
   
   return 0;
}

int jed_send_process (int *fd, char *str) /*{{{*/
{
   unsigned int len;
   Process_Type *p = get_process (*fd);
   if ((p == NULL) || (p->wd == -1)) return -1;
   
   len = strlen (str);
   
   return write_to_process (p->wd, str, len);
}

/*}}}*/

void jed_send_process_eof (int *fd) /*{{{*/
{
   Process_Type *p = get_process (*fd);
   
   if (p == NULL) 
     return;
   
   if (p->wd == -1)
     return;
   
   if (p->is_pty)
     write_to_process (p->wd, "\004", 1);
   else
     {
      signal_safe_close (p->wd);
      p->wd = -1;
     }
}


/*}}}*/

/* If s is NULL, then f != NULL, or visa-versa */
static int set_process (Process_Type *p, char *what, char *s, SLang_Name_Type *f) /*{{{*/
{
   if (!strcmp (what, "output"))
     {
      if (s != NULL)
        {
           if (0 == strcmp (s, "."))
             {
              p->output_type = PROCESS_AT_POINT | PROCESS_USE_BUFFER;
              return 0;
             }
           
           if (0 == strcmp (s, "@"))
             {
              p->output_type = PROCESS_SAVE_POINT | PROCESS_USE_BUFFER;
              return 0;
             }
           
           if (*s == 0)
             {
              p->output_type = PROCESS_USE_BUFFER;
              return 0;
             }

           if (NULL == (f = SLang_get_function (s)))
             return -1;
        }
      
      p->output_type = PROCESS_USE_SLANG;
      p->slang_fun = f;
      return 0;
     }
   else if (!strcmp (what, "signal"))
     {
      if ((s != NULL)
          && (NULL == (f = SLang_get_function (s))))
        return -1;

      p->status_change_fun = f;
      return 0;
     }
   
   jed_verror ("set_process: %s not supported", what);
   return -1;
}

/*}}}*/

void jed_set_process (void)
{
   int pd;
   char *what, *s;
   Process_Type *p;
   SLang_Name_Type *f;

   f = NULL;
   what = NULL;
   s = NULL;

   if (SLang_peek_at_stack () == SLANG_REF_TYPE)
     {
      if (NULL == (f = SLang_pop_function ()))
        return;
     }
   else if (-1 == SLang_pop_slstring (&s))
     return;
   
   if ((0 == SLang_pop_slstring (&what))
       && (0 == SLang_pop_integer (&pd))
       && (NULL != (p = get_process (pd)))
       && (0 == set_process (p, what, s, f)))
     f = NULL;
     
   SLang_free_slstring (what);
   SLang_free_slstring (s);
#if SLANG_VERSION > 10400
   SLang_free_function (f);
#endif
}


void jed_get_process_mark (int *fd) /*{{{*/
{
   Process_Type *p;
   if (NULL == (p = get_process (*fd))) return;
   
   SLang_push_mmt (p->umark);
}

/*}}}*/

int jed_open_process (int *np) /*{{{*/
{
   int fd = -1;
   char *argv[502];
   int n = *np;

   if (CBuf->subprocess)
     {
      msg_error ("There is already a process attached to this buffer.");
      return -1;
     }
   
   if ((n > 500) || (n < 0))
     {
      msg_error ("Arguments out of range.");
      return -1;
     }
   
   n++;                              /* for argv0 since *np does not include
                              * it. 
                              */
   argv[n] = NULL;
   while (n--)
     {
      if (SLang_pop_slstring (&argv[n]))
        {
           n++;
           goto free_return;
        }
     }
   n = 0;
   
   if ((fd = open_process(argv[0], argv)) < 0)
     {
      jed_verror ("Unable to open %s process.", argv[0]);
     }
   
   /* free up the argument strings */
   free_return:
   
   while (n <= *np)
     {
      SLang_free_slstring (argv[n]);
      n++;
     }
   
   return fd;
}

/*}}}*/

void jed_block_child_signal (int block) /*{{{*/
{
   static sigset_t new_mask, old_mask;
   
   if (block)
     {
      sigemptyset (&new_mask);
      sigaddset (&new_mask, SIGCHLD);
      (void) sigprocmask (SIG_BLOCK, &new_mask, &old_mask);
      return;
     }
   
   (void) sigprocmask (SIG_SETMASK, &old_mask, NULL);
}

/*}}}*/

/* Jed only calls these in pairs so that this should be fine. */
FILE *jed_popen (char *cmd, char *type) /*{{{*/
{
   FILE *pp;
   
   jed_block_child_signal (1);
   pp = popen (cmd, type);
   if (pp == NULL)
     jed_block_child_signal (0);
   return pp;
}

/*}}}*/

int jed_pclose (FILE *fp) /*{{{*/
{
   int ret;
   
   if (fp == NULL)
     return -1;
   
   ret = pclose (fp);
   jed_block_child_signal (0);
   
   return ret;
}

/*}}}*/

#if 0
/* These are my versions of popen/pclose.  For some reason, the popen/pclose 
 * do not work on SunOS when there are subprocesses.  I think it has 
 * something to do with the way pclose is waiting.
 * See Steven's book for more information.
 */
#ifndef OPEN_MAX
#define OPEN_MAX 256
#endif
static pid_t Popen_Child_Pids[OPEN_MAX];
FILE *jed_popen(char *cmd, char *type) /*{{{*/
{
   int i, pfd[2], fd;
   pid_t pid;
   FILE     *fp;

   if (((*type != 'r') && (*type != 'w')) || (*(type + 1) != 0))
     {
      errno = EINVAL;         /* required by POSIX.2 */
      return(NULL);
     }

   if (pipe(pfd) < 0) return(NULL); /* errno set by pipe() or fork() */
   if ((pid = fork()) < 0) return(NULL);
   
   if (pid == 0) 
     {                               /* child */
      if (*type == 'r') 
        {
           signal_safe_close(pfd[0]);
           if (pfd[1] != 1) signal_safe_dup2(pfd[1], 1);
           signal_safe_close(pfd[1]);
        }
      else
        {
           signal_safe_close(pfd[1]);
           if (pfd[0] != 0)
             {
              signal_safe_dup2(pfd[0], STDIN_FILENO);
              signal_safe_close(pfd[0]);
             }
        }
      
      /* POSIX requires that all streams open by previous popen
       * be closed.
       */
      for (i = 0; i < OPEN_MAX; i++)
        {
           if (Popen_Child_Pids[i] > 0) signal_safe_close(i);
        }

      execl("/bin/sh", "sh", "-c", cmd, (char *) 0);
      _exit(127);
     }
   
   /* parent */
   if (*type == 'r') 
     {
      signal_safe_close(pfd[1]);
      if (NULL == (fp = fdopen(pfd[0], type)))
        return(NULL);
     } 
   else 
     {
      signal_safe_close(pfd[0]);
      if (NULL == (fp = fdopen(pfd[1], type)))
        return(NULL);
     }
   
   fd = fileno (fp);
   if (fd >= OPEN_MAX)
     {
#ifdef EMFILE
      errno = EMFILE;
#endif
      fclose (fp);
      return NULL;
     }
   Popen_Child_Pids [fd] = pid;
   return(fp);
}

/*}}}*/
int jed_pclose(FILE *fp) /*{{{*/
{
   int      fd, stat;
   pid_t pid;
   int ret;

   fd = fileno(fp);
   if ((fd >= OPEN_MAX) ||
       (0 == (pid = Popen_Child_Pids[fd]))) 
     return -1;

   Popen_Child_Pids [fd] = 0;
   
   if (fclose(fp) == EOF) return(-1);

   /* This is the part that the SunOS pclose was apparantly screwing up. */
   while (pid != (ret = waitpid(pid, &stat, 0)))
     {
      if ((ret == -1) && (errno != EINTR))
        return -1;
     }
   
   ret = WEXITSTATUS(stat);
   if (WIFEXITED (stat))
     return ret;
   
   return -1;
}

/*}}}*/
#endif


void jed_query_process_at_exit (int *pid, int *query)
{
   Process_Type *p;

   if (NULL == (p = get_process (*pid)))
     return;
   
   p->quietly_kill_on_exit = (*query == 0);
}

   
int jed_processes_ok_to_exit (void)
{     
   Process_Type *p, *pmax;
   int num;
   char buf[64];

   if (Num_Subprocesses == 0)
     return 1;

   num = 0;
   p = Processes;
   pmax = p + MAX_PROCESSES;
   
   while (p < pmax)
     {
      if ((p->flags & PROCESS_ALIVE)
          && (0 == p->quietly_kill_on_exit))
        num++;
      p++;
     }
   
   if (num == 0)
     return 1;
   
   sprintf (buf, "%d Subprocesses exist.  Exit anyway", num);
   return jed_get_y_n (buf);
}

#endif                               /* JED_HAS_SUBPROCESSES */

Generated by  Doxygen 1.6.0   Back to index