This is jprocess.c in view mode; [Download] [Up]
#include <config.h>
#include <stdio.h>
#include <slang.h>
#include <string.h>
#include <errno.h>
#include <signal.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
#ifndef WEXITSTATUS
# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
#endif
#ifndef WIFEXITED
# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
#endif
#include "buffer.h"
#include "ins.h"
#include "ledit.h"
#include "misc.h"
#include "jprocess.h"
#include "paste.h"
int Num_Subprocesses;
int Max_Subprocess_FD;
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 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 */
SLuser_Object_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);
* The flags parameter corresponds to
* the flags field in this struct and
* the pid is NOT the pid of this struct
*/
} Process_Type;
static Process_Type Processes[MAX_PROCESSES];
static Process_Type *get_process (int fd)
{
Process_Type *p;
if ((fd >= 0) && (fd < MAX_PROCESSES)
&& (p = &Processes[fd], p->flags != 0)) return p;
msg_error ("process does not exist.");
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);
SLexecute_function (p->status_change_fun);
touch_screen ();
if (CBuf != cbuf) switch_to_buffer (cbuf);
cbuf->locked--;
}
#if 0
static 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;
}
#endif
/* 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. */
if (p->buffer != NULL) p->buffer->subprocess = 0;
slfd = (int) (p - Processes);
fd = Subprocess_Read_fds [slfd][0];
p->flags = 0;
if (p->umark != NULL) jed_free_user_object_mark (p->umark);
/* Adjust the array of read descriptors */
i = 0;
while (i < Num_Subprocesses)
{
if (Subprocess_Read_fds[i][0] == fd)
{
while (i < Num_Subprocesses - 1)
{
Subprocess_Read_fds[i][0] = Subprocess_Read_fds[i + 1][0];
Subprocess_Read_fds[i][1] = Subprocess_Read_fds[i + 1][1];
i++;
}
break;
}
i++;
}
Num_Subprocesses--;
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 (p->rd);
close (p->wd);
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, *pmin;
Child_Status_Changed_Flag--;
pmin = Processes; p = pmin + Num_Subprocesses;
while (p > pmin)
{
p--;
if (p->status_changed)
{
p->status_changed--;
get_process_status (p);
}
}
}
static void child_signal_handler (int sig)
{
int status;
int return_status;
int pid;
Process_Type *p, *pmax;
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))
{
status = PROCESS_SIGNALLED;
return_status = WTERMSIG (status);
}
else if (WIFSTOPPED (status))
{
status = PROCESS_STOPPED;
}
/* What else?? */
p = Processes;
pmax = p + Num_Subprocesses;
while (p < pmax)
{
if (p->pid == pid)
{
p->flags = status;
p->status_changed++;
p->return_status = return_status;
break;
}
p++;
}
}
signal (SIGCHLD, child_signal_handler);
Child_Status_Changed_Flag++;
}
static int open_process (char *pgm, char **argv)
{
int spfd, val;
int pd;
int fds0[2], fds1[2];
int pid, i;
Process_Type *p;
SLuser_Object_Type *uo;
pd = 0; while ((pd < MAX_PROCESSES) && Processes[pd].flags) pd++;
if (pd == MAX_PROCESSES) return -1;
p = &Processes[pd];
MEMSET ((char *) p, 0, sizeof (Process_Type));
if (0 == pipe (fds0))
{
if (-1 == pipe (fds1))
{
close (fds0[0]);
close (fds0[1]);
return -1;
}
}
else return -1;
if (NULL == (uo = jed_make_user_object_mark (SLANG_IVARIABLE))) return -1;
signal (SIGCHLD, child_signal_handler);
if ((pid = fork ()) < 0)
{
close (fds1[0]); close (fds1[1]);
close (fds0[0]); close (fds0[1]);
p->flags = 0;
jed_free_user_object_mark (uo);
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.
*/
setpgid(pid, pid);
if (pid == 0)
{
char ch;
/* child code */
/* Wait here for the parent to initialize its structures */
/* This will block */
read (fds0[0], &ch, 1);
for (i = 0; i < 32; i++) signal(i, SIG_DFL);
close (fds0[1]); /* close write end of 0 */
close (fds1[0]); /* close read end of 1 */
if ((dup2(fds0[0], 0) < 0) /* stdin */
|| (dup2 (fds1[1], 1) < 0) /* stdout */
|| (dup2 (fds1[1], 2) < 0)) /* stderr */
{
fprintf (stderr, "Dups failed!\n");
exit (-1);
}
if (execvp (pgm, argv) < 0)
{
fprintf (stderr, "exec failed!\n");
exit (-1);
}
}
/* parent */
close (fds0[0]); /* close read of child read */
close (fds1[1]); /* close write of child write */
p->flags = PROCESS_RUNNING;
spfd = p->rd = fds1[0];
p->wd = fds0[1];
Subprocess_Read_fds[Num_Subprocesses][0] = spfd;
Subprocess_Read_fds[Num_Subprocesses][1] = pd;
if (spfd > Max_Subprocess_FD) Max_Subprocess_FD = spfd;
Num_Subprocesses += 1;
val = fcntl (spfd, F_GETFL, 0);
val |= O_NONBLOCK;
fcntl (spfd, F_SETFL, val);
CBuf->subprocess = pd + 1;
/* Processing options */
p->buffer = CBuf;
p->output_type = PROCESS_USE_BUFFER;
p->umark = uo;
/* Tell child it is ok to go. */
write (p->wd, "&", 1);
return pd;
}
/* 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 ();
ins_chars (buf, n);
jed_move_user_object_mark (p->umark);
}
else if (otype == PROCESS_USE_SLANG)
{
buf[n] = 0;
SLang_push_string ((char *) buf);
SLang_push_integer ((int) (p - Processes));
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) touch_screen ();
}
int jed_send_process (int *fd, char *str)
{
Process_Type *p = get_process (*fd);
if ((p == NULL) || (p->wd == -1)) return -1;
write (p->wd, str, strlen(str));
return 0;
}
void jed_send_process_eof (int *fd)
{
Process_Type *p = get_process (*fd);
if (p == NULL) return;
if (p->wd != -1) close (p->wd);
p->wd = -1;
}
void jed_set_process (int *pd, char *what, char *s)
{
Process_Type *p;
SLang_Name_Type *f;
if (NULL == (p = get_process (*pd))) return;
if (!strcmp (what, "output"))
{
if (*s == '.') p->output_type = PROCESS_AT_POINT | PROCESS_USE_BUFFER;
else if (*s == '@') p->output_type = PROCESS_SAVE_POINT | PROCESS_USE_BUFFER;
else if (*s && (NULL != (f = SLang_get_function (s))))
{
p->output_type = PROCESS_USE_SLANG;
p->slang_fun = f;
}
else p->output_type = PROCESS_USE_BUFFER;
}
else if (!strcmp (what, "signal"))
{
if (*s && (NULL != (f = SLang_get_function (s))))
{
p->status_change_fun = f;
}
}
}
void jed_get_process_mark (int *fd)
{
Process_Type *p;
if (NULL == (p = get_process (*fd))) return;
SLang_push_user_object (p->umark);
}
int jed_open_process (int *np)
{
int fd = -1;
char *argv[502];
int do_free[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_string (&argv[n], &do_free[n]))
{
n++;
goto free_return;
}
}
n = 0;
if ((fd = open_process(argv[0], argv)) < 0)
{
msg_error ("Unable to open process.");
}
/* free up the argument strings */
free_return:
while (n <= *np)
{
if (do_free[n]) SLFREE (argv[n]);
n++;
}
return fd;
}
/* 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')
{
close(pfd[0]);
if (pfd[1] != 1) dup2(pfd[1], 1);
close(pfd[1]);
}
else
{
close(pfd[1]);
if (pfd[0] != 0)
{
dup2(pfd[0], STDIN_FILENO);
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) close(i);
}
execl("/bin/sh", "sh", "-c", cmd, (char *) 0);
_exit(127);
}
/* parent */
if (*type == 'r')
{
close(pfd[1]);
if (NULL == (fp = fdopen(pfd[0], type)))
return(NULL);
}
else
{
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;
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 (waitpid(pid, &stat, 0) < 0)
{
if (errno != EINTR) return(-1);
}
return(stat);
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.