/*
* Copyright 1989 by Rayan S. Zachariassen, all rights reserved.
* This will be free software, but only when it is finished.
*/
/*
* Runtime execution and I/O control.
*/
#include "hostenv.h"
#ifdef MAILER
#include "sift.h" /* Include this BEFORE "mailer.h" ! */
#endif /* MAILER */
#include <ctype.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include "mailer.h"
#include "zmsignal.h"
/* #define static
#define register */
#define RUNIO(X) (/*fprintf(runiofp, "runio(%s,%d)\n", __FILE__, __LINE__), */runio(&X))
#include "interpret.h"
#include "io.h"
#include "shconfig.h"
#include "libsh.h"
#ifdef HAVE_WAITPID
# include <sys/wait.h>
#else
# ifdef HAVE_WAIT3
# include <sys/wait.h> /* Has BSD wait3() */
# else
# ifdef HAVE_SYS_WAIT_H /* POSIX.1 compatible */
# include <sys/wait.h>
# else /* Not POSIX.1 compatible, lets fake it.. */
extern int wait();
# endif
# endif
#endif
#ifndef WEXITSTATUS
# define WEXITSTATUS(s) (((s) >> 8) & 0377)
#endif
#ifndef WSIGNALSTATUS
# define WSIGNALSTATUS(s) ((s) & 0177)
#endif
#ifndef SIGCHLD
#define SIGCHLD SIGCLD
#endif /* SIGCHLD */
/* The parent function of whatever is about to be executed, for $@ access */
struct osCmd *globalcaller = NULL;
/* Default file mode for open() (output redirection) */
int smask = DEFAULT_OPEN_MASK;
/*
* When we need to run an independent process to feed data, we do a
* double detach using the following macros.
*/
#define BEGINGRANDCHILD(P) if ((P = fork()) == 0) { /* child */\
/* detach completely from parent */\
if ((P = fork()) == 0) {\
/* grandchild */
#define ENDGRANDCHILD(P) _exit(0);\
} else if (P < 0)\
perror("fork");\
_exit(0);\
} else if (P > 0) { \
int sTaTuS; /* reap the child */ \
wait(&sTaTuS); \
} else
typedef int status_t;
int reapableTop = 0; /* top of stack of repable processes */
STATIC int reapable[MAXNPROC]; /* stack of reapable process ids */
STATIC status_t reapstatus; /* status of last reaped process */
/*
* SIGCHLD signal handler; it maintains the list of outstanding child
* processes. The reapstatus is only updated if the very last process
* (sort of top of stack) returned. This has to do with the requirement
* for exit status of a pipeline.
*/
STATIC void reapchild __((int));
STATIC void
reapchild(sig)
int sig;
{
status_t status;
register int i, pid;
pid = wait(&status);
for (i = 0; i < reapableTop; ++i) {
if (reapable[i] == pid) {
--reapableTop;
if (i == reapableTop) {
reapstatus = status;
} else {
while (i < reapableTop) {
reapable[i] = reapable[i+1];
++i;
}
}
}
}
}
STATIC int bgsetup __((struct osCmd *));
STATIC int
bgsetup(c)
struct osCmd *c;
{
int fd;
SIGNAL_IGNORE(SIGINT); /* ignore interrupt */
SIGNAL_IGNORE(SIGQUIT); /* ignore quit */
if (!(c->fdmask & 1)) { /* /dev/null is default input */
fd = open("/dev/null", O_RDONLY, 0);
if (fd > 0) {
dup2(fd, 0/* stdin */);
close(fd);
}
}
return 0;
}
/*
* All function executions pass through here. The environment is set up
* and appropriate I/O manipulations controlled, and status code retrieved
* and returned as the value of the execute() function.
*/
int
execute(c, caller, oretcode, name)
struct osCmd *c, /* function we are about to execute */
*caller; /* the function we are currently in */
int oretcode; /* default value for return code */
const char *name; /* Redundant function name info */
{
int ac = 0; /* argument count in av[] */
const char **av = NULL; /* argument vector, with ac entries */
struct sslfuncdef *sfdp; /* defined function descriptor */
int pid; /* process id of child (unix program) */
int retcode; /* the new integer-valued return code */
int nofork; /* flag: don't fork shell before exec */
status_t status; /* return code from wait() */
int n;
RETSIGTYPE (*oquit_handler) __((int)),
(*oint_handler) __((int)),
(*oterm_handler) __((int));
RETSIGTYPE (*ochld_handler) __((int));
conscell *sl, *l, *tmp;
GCVARS3;
status = 0;
sl = l = tmp = NULL;
GCPRO3(sl, l, tmp); /* 'osCmd c' related conscells are caller
protected */
ochld_handler = SIG_DFL;
globalcaller = caller;
ac = 0;
nofork = 0;
sfdp = NULL;
if (c->shcmdp != NULL && c->shcmdp->sptr == sh_builtin) {
car(c->argv) = cdar(c->argv);
if (car(c->argv) == NULL || LIST(car(c->argv)))
c->argv = NULL;
else
functype(car(c->argv)->cstring, &c->shcmdp, NULL);
}
if (c->argv && LIST(c->argv) && STRING(car(c->argv))) {
/* there are string arguments */
if (isset('I')||isset('J')) {
grindef("Command = ", c->argv);
fprintf(runiofp, "Run:");
}
if (c->shcmdp == NULL) {
functype((car(c->argv))->cstring, NULL, &sfdp);
if (sfdp == NULL)
path_hash((car(c->argv))->string);
}
if ((c->shcmdp == NULL && sfdp == NULL)
|| (c->shcmdp != NULL && c->shcmdp->sptr != NULL)) {
/*
* It must be an external command, or an internal
* command that takes (argc, argv), so assemble
* the argv[] array now.
*/
/* first count how many arguments we got */
ac = 1; /* trailing NULL */
for (sl = car(c->argv); sl != NULL; sl = cdr(sl))
if (STRING(sl))
ac++;
/* allocate space */
/* This lifetime seems to be longish, too early free
leads to "mysterious" crashes */
#ifdef USE_ALLOCA
av = (const char **)alloca(ac * sizeof (char *));
#else
av = (const char **)malloc(ac * sizeof (char *));
#endif
/* set them up */
ac = 0;
for (sl = car(c->argv); sl != NULL; sl = cdr(sl)) {
if (STRING(sl))
av[ac++] = (char *)sl->string;
}
if (isset('I')) {
for (sl = car(c->argv); sl != NULL; sl = cdr(sl)) {
if (STRING(sl)) {
putc(' ', runiofp);
s_grind(sl, runiofp);
}
}
}
av[ac] = NULL;
if (av[0][0] == 'e' && strcmp(av[0], "exec") == 0) {
--ac, ++av;
if (ac == 0 && c->doio)
c->undoio = NULL;
nofork = 1;
}
} else if (isset('I'))
s_grind(c->argv, runiofp);
if (isset('I'))
putc('\n', runiofp);
} else {
/*
* There is no command, so the "temporary" environment
* variable settings should be kept.
*/
/* if (c->envold != NULL)
s_free_tree(c->envold); */
c->envold = NULL;
sfdp = NULL;
}
/*
* These are needed so builtins that write to stdout/err don't stomp
* on previous data. Notice also the flushes below.
*/
fflush(stdout); fflush(stderr);
sl = c->rval;
c->rval = NULL;
#define NO_RETCODE -12345 /* anything < -128 or > 255 */
retcode = NO_RETCODE;
if (c->doio && RUNIO(c->doio))
/*fprintf(stderr, "%s: runio(doio) failed\n", progname)*/;
else if ((c->pgrpp != NULL && (c->shcmdp != NULL || sfdp != NULL)) &&
/* we have a builtin command that should be backgrounded */
((pid = fork()) != 0 /* if in parent, do if stmt body */
|| (setopt('t', 1), bgsetup(c)))) {
/* in parent, set up process group leader */
jc_newproc(c->pgrpp, pid, ac, av);
} else if (c->shcmdp != NULL) {
/*
* Builtin command
*/
if (c->shcmdp->lptr != NULL ||
c->shcmdp->rptr != NULL) {
/*
* List-valued builtin
*/
*((int *)&status) = 0;
if (c->prev == NULL || c->prev->shcmdp->sptr != NULL) {
/* translate stdin into a list argument */
if (c->shcmdp->flag & SH_STDIN)
sl = s_read(stdin);
}
if (isset('x')) {
fprintf(stderr,"+ %s ", c->shcmdp->name);
s_grind(c->argv, stderr);
putc(' ', stderr);
s_grind(sl, stderr);
putc('\n', stderr);
fflush(stderr);
}
#ifdef MAILER
if (D_functions) {
fprintf(stderr, "%*s%s ", 4*funclevel, " ",
c->shcmdp->name);
s_grind(c->argv, stderr);
fputc(' ', stderr);
s_grind(sl, stderr);
fputc('\n', stderr);
fflush(stderr);
}
#endif /* MAILER */
if (c->shcmdp->rptr != NULL)
c->rval = (c->shcmdp->rptr)(c->argv, sl, &retcode);
else
c->rval = (c->shcmdp->lptr)(c->argv, sl);
/* it's important that c->rval is NOT permanent mem. */
if (retcode == NO_RETCODE)
retcode = (c->rval == NULL);
if (isset('t'))
trapexit(retcode);
if (c->flag & OSCMD_QUOTEOUTPUT) {
for (tmp = c->rval; tmp != NULL; tmp = cdr(tmp))
if (STRING(tmp))
tmp->flags |= QUOTEDSTRING;
}
if (c->next != NULL &&
(c->next->shcmdp->lptr != NULL ||
c->next->shcmdp->rptr != NULL) ) {
;/* next command takes list input, keep mum! */
} else if ((c->fdmask & (1<<1)) && _FILEIO(stdout)) {
/* stdout is a pipe to another command */
BEGINGRANDCHILD(pid)
while (c->rval != NULL) {
s_grind(c->rval, stdout);
if ((c->rval = cdr(c->rval)))
putchar(' ');
else
break;
}
putchar('\n');
ENDGRANDCHILD(pid) {
perror("fork");
}
c->rval = 0;
} else if (1
#ifdef MAILER
&& return_valuep == NULL && c->rval != NULL
#endif /* MAILER */
) {
/*
* There is a potential deadlock here if this
* code is executed instead of the detached
* write above, if we are sending data through
* a pipe to an internal function, e.g.
* ( builtincmd ) | builtincmd
* I don't think the general case (think about
* control flow) is solvable.
*/
tmp = c->rval;
while (c->rval != NULL) {
s_grind(c->rval, stdout);
if ((c->rval = cdr(c->rval)))
putchar(' ');
else
break;
}
putchar('\n');
/* assert c->rval = 0; */
c->rval = tmp;
}
#if 0
else if (c->rval != NULL) {
fprintf(stderr, "rval = ");
_grind(c->rval);
}
#endif
} else if (c->shcmdp->sptr != NULL) {
/*
* Normal argc,argv builtin command
*/
if (isset('x')) {
putc('+',stderr);
for (n = 0; n < ac; ++n)
fprintf(stderr," %s", av[n]);
putc('\n',stderr);
fflush(stderr);
}
#ifdef MAILER
if (D_functions) {
fprintf(stderr, "%*s%s", 4*funclevel, " ",
av[0]);
for (n = 1; n < ac; ++n)
fprintf(stderr, " %s", av[n]);
fputc('\n', stderr);
fflush(stderr);
}
#endif /* MAILER */
retcode = (c->shcmdp->sptr)(ac, av);
fflush(stdout); fflush(stderr);
if (isset('t'))
trapexit(retcode);
}
if (interrupted)
putchar('\n');
} else if (sfdp != NULL) {
/*
* Defined function
*/
if (isset('x')) {
putc('+', stderr);
for (tmp = car(c->argv); tmp != NULL; tmp = cdr(tmp))
putc(' ', stderr), s_grind(tmp, stderr);
putc('\n', stderr);
fflush(stderr);
}
#ifdef MAILER
if (D_functions) {
fprintf(stderr, "%*s", 4*funclevel, " ");
for (tmp = car(c->argv); tmp != NULL; tmp = cdr(tmp)) {
if (tmp != car(c->argv))
fputc(' ', stderr);
s_grind(tmp, stderr);
}
fputc('\n', stderr);
fflush(stderr);
}
#endif /* MAILER */
interpret(sfdp->tabledesc->table,
sfdp->eot, sfdp->pos, c, &retcode,
sfdp->tabledesc);
if (isset('t'))
trapexit(retcode);
if (interrupted)
putchar('\n');
} else if (ac == 0) {
/*
* I/O side-effects are the raison d'etre
*/
retcode = oretcode;
c->rval = sl;
if (nofork && c->execio)
RUNIO(c->execio);
} else if (nofork || (pid = fork()) == 0) {
/*
* Unix program (child of shell)
*/
if (isset('x')) {
putc('+', stderr);
for (n = 0; n < ac; ++n)
fprintf(stderr," %s", av[n]);
putc('\n',stderr);
fflush(stderr);
}
#ifdef MAILER
if (D_functions) {
fprintf(stderr, "%*s%s", 4*funclevel, " ", av[0]);
for (n = 1; n < ac; ++n)
fprintf(stderr, " %s", av[n]);
fputc('\n', stderr);
fflush(stderr);
}
#endif /* MAILER */
if (!nofork && c->execio && RUNIO(c->execio)) {
if (c->undoio)
RUNIO(c->undoio);
fprintf(stderr, "%s: runio(execio) failed\n", progname);
if (!nofork)
_exit(1);
}
/*
* Any temporary variable assignments may have been done
* to non-exported variables, they must be exported to
* retain semantics of NAME=value program.
*/
for (sl = c->envold; sl != NULL; sl = cddr(sl))
v_export(sl->string);
if (c->pgrpp != NULL)
bgsetup(c);
/*
* We restore original signal handlers unless if we are
* explicitly ignoring the signal in question.
*/
SIGNAL_HANDLESAVE(SIGTERM,orig_handler[SIGTERM],oterm_handler);
if (oterm_handler == SIG_IGN && orig_handler[SIGTERM]!=SIG_IGN)
SIGNAL_IGNORE(SIGTERM);
SIGNAL_HANDLESAVE(SIGINT,orig_handler[SIGINT],oint_handler);
if (oint_handler == SIG_IGN && orig_handler[SIGINT] != SIG_IGN)
SIGNAL_IGNORE(SIGINT);
SIGNAL_HANDLESAVE(SIGQUIT,orig_handler[SIGQUIT],oquit_handler);
if (oquit_handler == SIG_IGN && orig_handler[SIGQUIT]!=SIG_IGN)
SIGNAL_IGNORE(SIGQUIT);
/*
* Go fish
*/
execvp(av[0], (char *const*)av);
if (!nofork && c->undoio)
RUNIO(c->undoio);
fprintf(stderr, "%s: %s\n", av[0], NOT_FOUND);
if (!nofork)
_exit(1);
SIGNAL_HANDLE(SIGTERM, oterm_handler);
SIGNAL_HANDLE(SIGINT, oint_handler);
SIGNAL_HANDLE(SIGQUIT, oquit_handler);
} else if (pid > 0) {
/*
* In the shell, if the child is doing output to a pipe,
* the child *must* complete all its O before we wait on it
* here, since nothing is reading from the pipe. Whence
* we need to do an asynchronous wait in some circumstances.
*/
if (c->pgrpp != NULL)
jc_newproc(c->pgrpp, pid, ac, av);
else if (c->reaperTop < 0) {
while ((n = wait(&status)) != pid && n > 0)
continue;
} else { /* asynchronous wait */
reapable[reapableTop++] = pid;
SIGNAL_HANDLESAVE(SIGCHLD, reapchild, ochld_handler);
}
} else { /* fork failed */
retcode = 0200;
fprintf(stderr, "%s: %s\n", progname, CANNOT_FORK);
}
#ifndef USE_ALLOCA
if (av) free(av);
#endif
#ifdef MAILER
if (D_functions && retcode != 0 && retcode != NO_RETCODE)
fprintf(stderr, "?=%d\n", retcode);
#endif /* MAILER */
/*
* Undo the I/O manipulations needed for child setup.
*/
if (c->undoio) {
RUNIO(c->undoio);
if (c->iocmd == ioIntoBuffer) {
SIGNAL_HANDLE(SIGCHLD, ochld_handler);
while (c->reaperTop >= 0 && reapableTop > c->reaperTop)
reapchild(0);
/* one way or another it has been reaped */
status = reapstatus;
}
}
/*
* Undo any temporary variable values
*/
for (sl = c->envold; sl != NULL; sl = cddr(sl)) {
if (cadr(sl) == NULL) {
if (isset('I'))
fprintf(runiofp, "purge(%s)\n", sl->string);
v_purge(sl->string);
} else {
if (isset('I'))
fprintf(runiofp, "revert(%s)\n", sl->string);
l = v_find(sl->string);
/* if (ISNEW(cdr(l)))
free((char *)cdr(l)->string);
else
s_free_tree(cadr(l));
*/
cadr(l) = cadr(sl);
#ifdef MAILER
if (v_accessed)
v_written(l);
#endif /* MAILER */
}
/* be sure to update internal dependencies */
v_sync(sl->string);
}
if (c->envold != NULL) {
if (isset('I'))
grindef("Freeing = ", c->envold);
/* s_free_tree(c->envold); */
c->envold = NULL;
}
if (retcode != NO_RETCODE) {
if (isset('t'))
trapexit(retcode);
goto out_exit;
}
if (WSIGNALSTATUS(status) != 0) {
if (WSIGNALSTATUS(status) != SIGINT) {
fprintf(stderr, "%s", strsignal(WSIGNALSTATUS(status)));
if (status&0200)
fprintf(stderr, CORE_DUMPED);
}
fprintf(stderr, "\n");
retcode = 0200 + WSIGNALSTATUS(status);
} else
retcode = WEXITSTATUS(status);
if (isset('t'))
trapexit(retcode);
out_exit:;
UNGCPRO3;
return retcode;
}
static int addbuffer __((char *, int, int, struct osCmd *));
static int
addbuffer(buf, len, state, command)
char *buf;
register int len, state;
struct osCmd *command;
{
register char *ncp, *cp;
conscell *tmp;
ncp = cp = buf;
for (cp = buf; --len >= 0; ++cp) {
int c = (*cp) & 0xFF;
if (!isascii(c))
*ncp++ = c, state = 1;
else if (state) { /* break on whitespace */
if (isspace(c)) {
*ncp++ = (c == '\n') ? c : ' ';
state = 0;
} else
*ncp++ = c;
} else if (!isspace(c)) /* ignoring whitespace */
*ncp++ = c, state = 1;
}
/* wrap up the stuff we got so far into a string buffer */
if (ncp > buf) {
int c = ncp[-1] & 0xFF;
if (isascii(c) && isspace(c))
--ncp;
if (ncp > buf) {
int slen = ncp-buf;
tmp = newstring(dupnstr(buf, slen), slen);
if (isset('I') || isset('R'))
fprintf(stderr,
"readstring: '%s'\n", ncp);
*command->bufferp = tmp;
command->bufferp = &cdr(tmp);
}
}
return state;
}
/*
* Read a byte-stream from fd into a linked list of buffers in command->buffer.
*/
STATIC void readstring __((int, struct osCmd *));
STATIC void
readstring(fd, command)
int fd;
struct osCmd *command;
{
int n, state;
char buf[BUFSIZ]; /* read this size chunk at a time */
GCVARS1;
state = 0;
GCPRO1(command->buffer); /* Should not need ... */
while ((n = read(fd, &buf[0], sizeof(buf))) > 0)
state = addbuffer(&buf[0], n, state, command);
UNGCPRO1;
}
/*
* String Buffer routines (for lack of a better name).
* We want a stack on each fd, with the TOS marking whether I/O should
* happen to something internal or to a real fd. An siobuf with a flag
* of -1 (and a null stack) indicate I/O to fd.
*
* In order to deal with dup()s, we must share a single siobuf between
* the source and destination fd. That happens when we sb_push(destination,
* source), essentially.
*/
STATIC void sb_push __((struct siobuf **, struct siobuf *));
STATIC void
sb_push(siopp, sioptop)
struct siobuf **siopp, *sioptop;
{
struct siobuf *siop;
siop = (struct siobuf *)emalloc(sizeof (struct siobuf));
/*std_printf("sb_push(%x)\n", siop);*/
siop->_sb_data = siop;
siop->sb_cnt = -1;
siop->sb_ptr = NULL;
siop->sb_base = NULL;
siop->sb_bufsiz = -1;
siop->sb_refcnt = 0;
siop->sb_flag = (short)-1; /* magic value */
if (sioptop != NULL) {
while (sioptop != NULL && sioptop != sioptop->_sb_data)
sioptop = sioptop->_sb_data;
siop->_sb_data = sioptop;
siop->sb_refcnt += 1;
}
siop->sb_next = *siopp;
*siopp = siop;
/*sb_pr();*/
}
/* read from string buffer by replacing new read buffer with old write buffer */
STATIC int sb_in __((int, int));
STATIC int
sb_in(n, outfd)
int n, outfd;
{
struct siobuf *siop;
/*
* XX: keep this in sync with _FILEIO, which we cant use because
* it uses a global...
*/
if (siofds[outfd] == NULL || siofds[outfd]->sb_flag < 0) {
fprintf(stderr, "%s: no siofds to read!\n",
progname);
return 1;
}
siop = siofds[n];
siofds[n] = siofds[outfd];
siofds[outfd] = siofds[outfd]->sb_next;
siofds[n]->sb_next = siop;
siop = siofds[n];
siop->sb_cnt = siop->sb_bufsiz - (siop->sb_ptr - siop->sb_base);
siop->sb_ptr = siop->sb_base;
siop->sb_flag = O_RDONLY|(siop->sb_flag & O_CREAT);
/*std_printf("POP(%d) = %x (in)\n", outfd, siofds[outfd]);*/
/*std_printf("PUSH(%d) = %x (in)\n", n, siofds[n]);*/
/*sb_pr();*/
return 0;
}
/* allocate a string buffer to write to */
STATIC void sb_out __((int));
STATIC void
sb_out(n)
int n;
{
struct siobuf *siop;
siop = siofds[n];
siofds[n] = (struct siobuf *)emalloc(sizeof (struct siobuf));
siofds[n]->_sb_data = siofds[n];
siofds[n]->sb_next = siop;
siop = siofds[n];
siop->sb_cnt = 0;
siop->sb_ptr = siop->sb_base = NULL;
siop->sb_bufsiz = 0;
siop->sb_flag = O_WRONLY|O_APPEND;
siop->sb_refcnt = 0;
/*std_printf("PUSH(%d) = %x (out)\n", n, siofds[n]);*/
/*sb_pr();*/
}
/* free a string buffer */
STATIC void sb_free __((int));
STATIC void
sb_free(n)
int n;
{
struct siobuf *siop;
int flag, i;
/*std_printf("FREE(%d) = %x\n", n, siofds[n]);*/
siop = siofds[n];
if (siop == NULL) {
fprintf(stderr, "%s: no siofds to free (fd=%d)\n",
progname, n);
return;
}
siofds[n] = siop->sb_next;
flag = siop->_sb_data != siop;
siop->sb_refcnt -= 1;
if (siop->sb_refcnt >= 0) {
/*std_printf("sb_free: refcnt = %d, flag = %d\n", siop->sb_refcnt, flag);*/
if (flag) {
free((char *)siop);
return;
}
/*std_printf("sb_free: sb_flag = %d\n", siop->sb_flag);*/
if (siop->sb_flag != (short)-1)
return;
for (i = 0; i < MAXNFILE ; ++i) { /* XX: inefficient! */
/*if (siofds[i] != NULL) std_printf("sb_free: siofds[%d] = %x\n", i, siofds[i]);*/
if (i == n || siofds[i] == NULL)
continue;
#if 1
if (siofds[i]->_sb_flag == (short)-1)
if (siofds[i]->_sb_refcnt == (short)0)
continue;
#endif
/*std_printf( "sb_free: siofds[%d]->_sb_data == siop: %d\n", siofds[i]->_sb_data == siop);*/
if (siofds[i]->_sb_data == siop)
sb_free(i);
}
/*std_printf( "sb_free: returning\n");*/
return;
}
/* we want to free whatever we're pointing to, perhaps us */
if (siop->sb_base)
if (siop->sb_flag & O_CREAT)
/* data is malloc'ed */
free((char *)siop->sb_base);
if (flag) /* we're pointing at another siobuf */
free((char *)siop->_sb_data);
/*fprintf(runiofp, "sb_free: done\n");*/
free((char *)siop);/* can do this because nothing refers to us */
}
void
sb_external(n)
int n;
{
sb_out(n);
if (stickymem == MEM_SHCMD)
abort(); /* must be either MEM_TEMP or MEM_PERM */
siomore(siofds[n]);
/* we now have a safe buffer in siop->sb_base */
}
char *
sb_retrieve(n)
int n;
{
struct siobuf *siop;
char *cp;
siop = siofds[n];
if (siop->sb_flag & O_CREAT) { /* turn malloc'ed data to MEM_TEMP */
if (stickymem == MEM_SHCMD)
abort(); /* must be either MEM_TEMP or MEM_PERM */
cp = strnsave((char *)siop->sb_base,
siop->sb_ptr - siop->sb_base);
} else
cp = (char *)siop->sb_base;
sb_free(n);
return cp;
}
/*
sb_pr()
{
int i;
struct siobuf *siop;
std_printf("\n");
for (i = 0; i < MAXNFILE; ++i) {
if (siofds[i] == NULL)
continue;
std_printf("%2d:", i);
for (siop = siofds[i]; siop != NULL; siop = siop->sb_next) {
std_printf(" %x", siop);
if (siop->_sb_data != siop)
std_printf("(%x)", siop->_sb_data);
}
std_printf("\n");
}
}
*/
/*
* Data-driven I/O manipulations. This routine is called to set up or undo
* the file descriptors as specified by the user and as required to make
* stdin/out/err point at the right thing. Sometimes file descriptors are
* faked for the benefit of internally run functions (builtins or defined
* functions) by using a growable string buffer mechanism. This is how one
* can (e.g.) pipe output from one builtin to the input of another without
* doing any syscalls or forking.
*/
int
runio(ioopp)
struct IOop **ioopp;
{
struct IOop *ioprev, *ionext, *ioop;
int errflag, fd = 0, pid, p[2];
struct stat stbuf;
struct siobuf *siop = NULL;
GCVARS1;
/*
* The list of actions is stored in reverse order. Since each such
* list is only ever executed once, we can do inline reversal before
* interpreting the list.
*/
ioop = *ioopp;
for (ioprev = NULL; ioop != NULL; ioop = ionext) {
ionext = ioop->next;
ioop->next = ioprev;
ioprev = ioop;
}
errflag = 0;
for (ioop = ioprev, ionext = NULL; ioop != NULL; ioop = ioop->next) {
#ifdef DEBUG_xxx
fprintf(stderr,"runio(@%p) ioop=%p &ioop->command->buffer=%p\n",
__builtin_return_address(0), ioop, &ioop->command->buffer);
#endif
switch (ioop->cmd) {
#ifdef S_IFIFO
case sIOopenPortal: /* open named pipe */
if (ioop->ioflags & O_CREAT) {
if (stat(ioop->name, &stbuf) == 0
&& !(stbuf.st_mode & S_IFIFO)) {
fprintf(stderr, "%s: %d %s\n",
progname, ioop->name,
EXISTS_BUT_NOT_FIFO);
++errflag;
break;
}
unlink(ioop->name);
/* if the pipe already exists, don't bother */
if (mknod(ioop->name, S_IFIFO, 0) < 0) {
fprintf(stderr, "%s: %s %s\n", progname,
CANNOT_MKNOD,
ioop->name);
++errflag;
break;
}
}
/* FALL THROUGH */
#endif /* S_IFIFO */
case sIOopen: /* open file */
fd = open(ioop->name, ioop->ioflags, smask);
if (fd < 0) {
fprintf(stderr, "%s: %s %s\n", progname,
CANNOT_OPEN, ioop->name);
++errflag;
} else if (fd != ioop->fd
&& (dup2(fd, ioop->fd) < 0 || close(fd) < 0)) {
fprintf(stderr,
"%s: prediction error: got %d not %d\n",
progname, fd, ioop->fd);
++errflag;
}
sb_push(&siofds[ioop->fd], (struct siobuf *)NULL);
/*std_printf( "PUSH(%d) = %x (%d)\n", ioop->fd, siofds[ioop->fd], __LINE__);*/
if (isset('R'))
fprintf(stderr,
"open(%s, %x, %o) = %d\n",
ioop->name, ioop->ioflags,
smask, fd);
break;
case sIOopenString:
/* fork off a process to feed other proc in pipe */
if (pipe(p) < 0) {
fprintf(stderr, "%s: %s: %s\n",
progname, PIPE,
strerror(errno));
++errflag;
} else if (p[0] != ioop->fd) {
if (p[1] == ioop->fd)
fd = dup(p[1]);
else
fd = p[1];
dup2(p[0], ioop->fd);
close(p[0]);
} else
fd = p[1];
if (!errflag) {
BEGINGRANDCHILD(pid)
write(fd, ioop->name,
strlen(ioop->name));
ENDGRANDCHILD(pid) { /* error */
fprintf(stderr, "%s: %s\n",
progname, CANNOT_FORK);
++errflag;
}
}
close(fd);
if (isset('R'))
fprintf(stderr,
"write(%d, '%.40s', %d)\n",
ioop->fd, ioop->name,
strlen(ioop->name));
break;
case sIOopenPipe:
if (pipe(p) < 0) {
fprintf(stderr, "%s: %s: %s\n",
progname, PIPE,
strerror(errno));
++errflag;
} else if (p[1] != ioop->fd || p[0] != ioop->fd2) {
fprintf(stderr, "%s: pipe prediction wrong: got %d|%d not %d|%d\n",
progname, p[1], p[0],
ioop->fd, ioop->fd2);
++errflag;
close(p[1]);
close(p[0]);
}
sb_push(&siofds[p[0]], (struct siobuf *)NULL);
/*std_printf( "PUSH(%d) = %x (%d)\n", p[0], siofds[p[0]], __LINE__);*/
sb_push(&siofds[p[1]], (struct siobuf *)NULL);
/*std_printf( "PUSH(%d) = %x (%d)\n", p[1], siofds[p[1]], __LINE__);*/
if (isset('R'))
fprintf(stderr, "pipe(%d|%d)\n", p[1], p[0]);
break;
case sIOintoBuffer:
GCPRO1(ioop->command->buffer);
readstring(ioop->fd, ioop->command);
UNGCPRO1;
if (isset('R'))
fprintf(stderr, "intoBuffer '%s'\n",
ioop->command->buffer ?
ioop->command->buffer->string : "");
break;
case sIOdup:
/*putc('\n', runiofp);*/
fd = dup2(ioop->fd, ioop->fd2);
if (siofds[ioop->fd2] != NULL)
if (siofds[ioop->fd2]->sb_refcnt == 0)
sb_free(ioop->fd2)/*, sb_pr()*/;
sb_push(&siofds[ioop->fd2], siofds[ioop->fd]);
/*std_printf( "PUSH(%d) = %x %x (%d)\n", ioop->fd2, siofds[ioop->fd2], siofds[ioop->fd], __LINE__);*/
if (isset('R'))
fprintf(stderr, "dup2(%d,%d) = %d\n",
ioop->fd, ioop->fd2, fd);
break;
case sIOclose:
/*putc('\n', runiofp);*/
fd = close(ioop->fd);
if (siofds[ioop->fd])
sb_free(ioop->fd)/*, sb_pr()*/;
if (isset('R'))
fprintf(stderr, "close(%d) = %d\n",
ioop->fd, fd);
break;
case sIObufIn:
errflag += sb_in(ioop->fd, ioop->fd2);
if (isset('R'))
fprintf(stderr, "sb_in(%d,%d) err=%d\n",
ioop->fd, ioop->fd2, errflag);
break;
case sIObufOut:
/* allocate a string buffer to write to */
sb_out(ioop->fd);
if (isset('R'))
fprintf(stderr, "sb_out(%d)\n", ioop->fd);
break;
case sIObufFree:
/* free a string buffer */
sb_free(ioop->fd)/*, sb_pr()*/;
if (isset('R'))
fprintf(stderr, "sb_free(%d)\n", ioop->fd);
break;
case sIObufString:
/* move string buffer contents to command->buffer */
if (ioop->command->rval != NULL) {
/* if rval exists, use it in preference */
*(ioop->command->bufferp) = ioop->command->rval;
ioop->command->bufferp = &cdr(*ioop->command->bufferp);
ioop->command->rval = NULL;
} else if ((siop = siofds[ioop->fd]) == NULL
|| siop->sb_base == NULL) {
break;
} else {
siop = siofds[ioop->fd];
*(siop->sb_base + siop->sb_bufsiz
- siop->sb_cnt) = '\0';
if (*siop->sb_base == '(') {
FILE f;
/*
* The FILE stuff in s_read() will
* get info from the string buffer
*/
#ifdef _HPUX_SOURCE
f.__fileL = ioop->fd % 256;
f.__fileH = ioop->fd / 256;
#else /* Other non-portable.. */
#if defined(__GNU_LIBRARY__) || defined(__GLIBC__)
/* GNU LIBC systems */
f._fileno = ioop->fd;
#else
/* classic SysIII derived systems */
f._file = ioop->fd;/* XX: nonportable */
#endif
#endif
GCPRO1(ioop->command->buffer);
*(ioop->command->bufferp) = s_read(&f);
UNGCPRO1;
ioop->command->bufferp = &cdr(*ioop->command->bufferp);
} else {
GCPRO1(ioop->command->buffer);
addbuffer(siop->sb_base, siop->sb_bufsiz - siop->sb_cnt, 0, ioop->command);
UNGCPRO1;
}
}
break;
default:
break;
}
}
*ioopp = ioprev;
return errflag;
}
syntax highlighted by Code2HTML, v. 0.9.1