Logo Search packages:      
Sourcecode: zmailer version File versions  Download package

execute.c

/*
 *    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;
}

Generated by  Doxygen 1.6.0   Back to index