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

builtins.c

/*
 *    Copyright 1990 by Rayan S. Zachariassen, all rights reserved.
 *    This will be free software, but only when it is finished.
 *    Some functions Copyright 1991-2003 Matti Aarnio.
 */

#include "hostenv.h"
#ifdef      MAILER
#include "sift.h"
#endif      /* MAILER */
#include "mailer.h"
#include <ctype.h>
#include <fcntl.h>
#include <sys/times.h>
#include "interpret.h"
#include "io.h"         /* redefines stdio routines */
#include "shconfig.h"
#include "sysexits.h"

#include "libz.h"
#include "libc.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     HZ
#define     HZ    60
#endif      /* HZ */

#include <errno.h>
extern int errno;

#define CSARGS2 __((conscell *, conscell *))

extern conscell *sh_car       CSARGS2;
static conscell *sh_cdr       CSARGS2;
static conscell *sh_list      CSARGS2;
static conscell *sh_grind     CSARGS2;
static conscell *sh_elements  CSARGS2;
static conscell *sh_get       CSARGS2;
static conscell *sh_length    CSARGS2;
static conscell *sh_last      CSARGS2;
static conscell *sh_lappend     CSARGS2;
static conscell *sh_lreplace    CSARGS2;
static conscell *sh_ifssplit    CSARGS2;
extern conscell *sh_glob      CSARGS2; /* expand.c */

#define CSARGV2 __((int argc, const char *argv[]))

static int sh_read      CSARGV2;
static int sh_echo      CSARGV2;
static int sh_cd  CSARGV2;
static int sh_colon     CSARGV2;
static int sh_hash      CSARGV2;
static int sh_bce CSARGV2;
static int sh_eval      CSARGV2;
static int sh_export    CSARGV2;
static int sh_getopts   CSARGV2;
static int sh_shift     CSARGV2;
static int sh_umask     CSARGV2;
static int sh_set CSARGV2;
static int sh_unset     CSARGV2;
static int sh_wait      CSARGV2;
static int sh_times     CSARGV2;
static int sh_type      CSARGV2;
static int sh_sleep     CSARGV2;
static int sh_true      CSARGV2;
static int sh_false     CSARGV2;


struct shCmd builtins[] = {
{     "car",            NULL, sh_car,           NULL, SH_ARGV           },
{     "first",    NULL, sh_car,/*sic*/    NULL, SH_ARGV           },
{     "cdr",            NULL, sh_cdr,           NULL, SH_ARGV           },
{     "rest",           NULL, sh_cdr,/*sic*/    NULL, SH_ARGV           },
{     "last",           NULL, sh_last,    NULL, SH_ARGV           },
{     "list",           NULL, sh_list,    NULL, SH_ARGV           },
{     "grind",    NULL, sh_grind,   NULL, SH_ARGV           },
{     "elements", NULL, sh_elements,      NULL, SH_ARGV           },
{     "get",            NULL, sh_get,           NULL, SH_ARGV           },
{     "length",   NULL, sh_length,  NULL, SH_ARGV           },
{     "ifssplit", NULL, sh_ifssplit,      NULL, SH_ARGV           },
{     "[",        sh_test,    NULL, NULL, 0           },
{     "test",           sh_test,    NULL, NULL, 0           },
{     "echo",           sh_echo,    NULL, NULL, 0           },
{     "cd",       sh_cd,            NULL, NULL, 0           },
{     "hash",           sh_hash,    NULL, NULL, 0           },
{     "read",           sh_read,    NULL, NULL, 0           },
{     ":",        sh_colon,   NULL, NULL, 0           },
{     ".",        sh_include, NULL, NULL, 0           },
{     "break",    sh_bce,           NULL, NULL, SH_INTERNAL },
{     "continue", sh_bce,           NULL, NULL, SH_INTERNAL },
{     "return",   NULL, NULL, sh_return,  SH_ARGV           },
{     "returns",  NULL, NULL, sh_returns, SH_ARGV           },
{     "exit",           sh_bce,           NULL, NULL, SH_INTERNAL },
{     "eval",           sh_eval,    NULL, NULL, 0           },
{     "export",   sh_export,  NULL, NULL, 0           },
{     "getopts",  sh_getopts, NULL, NULL, 0           },
{     "shift",    sh_shift,   NULL, NULL, 0           },
{     "umask",    sh_umask,   NULL, NULL, 0           },
{     "set",            sh_set,           NULL, NULL, 0           },
{     "unset",    sh_unset,   NULL, NULL, 0           },
{     "wait",           sh_wait,    NULL, NULL, 0           },
{     "times",    sh_times,   NULL, NULL, 0           },
{     "trap",           sh_trap,    NULL, NULL, 0           },
{     "type",           sh_type,    NULL, NULL, 0           },
{     "builtin",  sh_builtin, NULL, NULL, 0           },
{     "sleep",    sh_sleep,   NULL, NULL, 0           },
{     "true",           sh_true,    NULL, NULL, 0           },
{     "false",    sh_false,   NULL, NULL, 0           },
{     "lappend",  NULL, sh_lappend, NULL, SH_ARGV           },
{     "lreplace", NULL, sh_lreplace,      NULL, SH_ARGV           },
{     "glob",           NULL, sh_glob,    NULL, SH_ARGV           },
{     NULL,       NULL,       NULL, NULL, 0           },
};

conscell *
sh_car(avl, il)
      conscell *avl, *il; /* Input conscells are gc-protected! */
{
      il = cdar(avl);
      if (il == NULL)
            return NULL;
      if (STRING(il)) {
            il = copycell(il);
            cdr(il) = NULL;
            return il;
      }
      if (car(il) == NULL) {
            return il;
      }
      car(il) = copycell(car(il));  /* don't modify malloc'ed memory! */
      cdar(il) = NULL;
      return car(il);
}

static conscell *
sh_cdr(avl, il)
      conscell *avl, *il; /* Input conscells are gc-protected! */
{
      il = cdar(avl);
      if (il == NULL || STRING(il) || car(il) == NULL)
            return NULL;
      car(il) = cdar(il);
      return il;
}

static conscell *
sh_last(avl, il)
      conscell *avl, *il; /* Input conscells are gc-protected! */
{
      il = cdar(avl);
      if (il == NULL || STRING(il) || car(il) == NULL)
            return NULL;
      while (cdar(il) != NULL)
            car(il) = cdar(il);
      return il;
}

static conscell *
sh_list(avl, il)
      conscell *avl, *il; /* Input conscells are gc-protected! */
{
      car(avl) = cdar(avl);
      return avl;
}

static conscell *
sh_grind(avl, il)
      conscell *avl, *il; /* Input conscells are gc-protected! */
{
      return cdar(avl);
}

static conscell *
sh_elements(avl, il)
      conscell *avl, *il; /* Input conscells are gc-protected! */
{
      conscell *p;

      if ((p = cdar(avl)) == NULL || !LIST(p))
            return p;
      il = cadar(avl);

      il = s_copy_chain(il); /* Creates new cells, but this is
                          also last call to the creator here. */

      for (p = il; p != NULL; p = cdr(p))
            p->flags |= ELEMENT;
      return il;
}


/*
 *  call: lappend varname $value
 *  The varname is looked up, and appended with supplied value;
 *  approach 
 *
 */

static conscell *
sh_lappend(avl, il)
      conscell *avl, *il; /* Input conscells are gc-protected! */
{
      conscell *key, *d, *tmp, *data;
      memtypes omem = stickymem;

      key = cdar(avl);
      if (key == NULL 
          || !STRING(key)) {
            fprintf(stderr, "Usage: %s variable-name $moredata\n",
                        car(avl)->string);
            return NULL;
      }
      d = v_find(key->string); /* no new objects allocated */
      if (!d) return NULL;

      d = cdr(d); /* This is variable content pointing object */

      stickymem = MEM_MALLOC;

      tmp = cdr(key); /* data is the next elt after the variable name */
      if (LIST(tmp))  /* If it is a LIST object, descend into it */
        tmp = car(tmp);
      data = s_copy_chain(tmp); /* The only cell-allocator in here */

      if (car(d) != NULL) {
        d = car(d);

        while (cdr(d)) d = cdr(d); /* Scan to the end of the list */
      
        cdr(d) = data;
      } else {
        car(d) = data; /* the first entry into the list */
      }

      stickymem = omem;
      return NULL; /* Be quiet, don't force the caller
                  to store the result into heap just
                    for later discard... */
}

/*
 *  call: lreplace list_var_name fieldidx $new_value
 *  The varname is looked up, and indicated element of it
 *  is replaced with a new value.
 *
 *  This processes 1) linear lists where index is decimal value,
 *  and 2) key/value pair lists where 'index' is key name
 *
 *  If you are referring beyond end of the list (or to a key
 *  which does not exist), the value (or key + value) is (are)
 *  appended to the variable.
 */

static conscell *
sh_lreplace(avl, il)
      conscell *avl, *il;
{
      conscell *key, *d, *tmp, *data, **dp;
      memtypes omem = stickymem;
      int fieldidx, fieldnamelen;
      char *fieldname;

      GCVARS4;

      const char *lreplace_usage =
        "Usage: %s variable-name fieldidx $new_value\n\
    where 'fieldidx' can be 1) numeric for index in a list (0..),\n\
    or 2) key/value list's key name\n";

      key = cdar(avl);
      if (key == NULL 
          || !STRING(key)) {
            fprintf(stderr, lreplace_usage, car(avl)->string);
            return NULL;
      }
      d = v_find(key->string);
      if (!d) {
        fprintf(stderr, lreplace_usage, car(avl)->string);
        return NULL;
      }

      tmp = cdr(key);  /* Numeric value for field index */
      if (!tmp || !STRING(tmp)) {
            fprintf(stderr, lreplace_usage, car(avl)->string);
            return NULL;
      }
      if ('0' <= tmp->string[0] && tmp->string[0] <= '9') {
        fieldidx = -1;
        if (sscanf(tmp->string,"%i",&fieldidx) != 1 ||
            fieldidx < 0 || fieldidx > 99) {
          fprintf(stderr, lreplace_usage, car(avl)->string);
          return NULL;
        }
        fieldname = NULL;
        fieldnamelen = 0;
      } else {
        fieldname    = tmp->string;
        fieldnamelen = tmp->slen;
      }

      stickymem = MEM_MALLOC;

      tmp = cdr(tmp); /* data is the next elt after the field index */
#if 0
      if (LIST(tmp))  /* If it is a LIST object, descend into it */
        tmp = car(tmp);
#endif

      data = NULL;
      GCPRO4(data, tmp, d, key);

      data = s_copy_chain(tmp);

      dp = &car(cdr(d)); /* This is variable pointing object */

      /* Must make copy of this chain here! */
      *dp = s_copy_chain(*dp);

      d = *dp;
      if (fieldname) {
        while (d != NULL) {
          /* Ok, this is key/value pairs in a linear list */
          if (STRING(d) && fieldnamelen == d->slen &&
            memcmp(d->string,fieldname,fieldnamelen)==0) {
            /* Move pointers to the value */
            dp = &cdr(d);
            d = *dp;
            break;
          }
          dp = &cdr(d);
          d = *dp;
          if (d != NULL) { /* To be safe in case not key/value pairs */
            dp = &cdr(d);
            d = *dp;
          }
        }
        if (d == NULL) {
          /* Append the pair */
          int slen = fieldnamelen;
          d = newstring(dupnstr(fieldname,slen),slen);
          *dp = d;
          dp = &cdr(d);
          d = NULL;
        }
      } else {
        while(fieldidx-- > 0 && d != NULL) {
          dp = &cdr(d);
          d = *dp;
        }
      }

      *dp = data;       /* Replace the element */
      if (d) {
        cdr(data) = cdr(d);
        cdr(d) = NULL;  /* Disconnect and discard old data */
        /* s_free_tree(d); -- GC cleans it out later .. */
      }

      UNGCPRO4;

      stickymem = omem;
      return NULL; /* Be quiet, don't force the caller
                  to store the result into heap just
                    for later discard... */
}



static conscell *
sh_get(avl, il)
      conscell *avl, *il;
{
      conscell *plist, *key, *d = NULL;
      GCVARS3;

      if ((plist = cdar(avl)) == NULL
          || (key = cddar(avl)) == NULL
          || !STRING(key)) {
            fprintf(stderr, "Usage: %s variable-name key\n",
                        car(avl)->string);
            return NULL;
      }
      if (STRING(plist)) {
            d = v_find((const char *)plist->string);
            if (d == NULL) {
                  /* (setq plist '(key nil)) */
                  GCPRO3(avl,plist,d);
                  if (ISCONST(key))
                    d = conststring(key->string, key->slen);
                  else
                    d = copycell(key);
                  cdr(d) = NIL;
                  d = ncons(d);
                  assign(plist, d, (struct osCmd *)NULL);
                  UNGCPRO3;
                  return cdar(d);
            }
            plist = cdr(d);
            if (!LIST(plist))
                  return NULL;
            plist = car(plist);
      }
      /* now we have a property list in plist, scan it */
      /* printf("plist = "); s_grind(plist, stdout); putchar('\n'); */
      for (d = plist; d != NULL; d = cdr(d)) {
            /* if (STRING(d))
                  printf("comparing '%s' and '%s'\n",
                        d->string, key->string);      */
            if (STRING(d) && strcmp(d->string, key->string) == 0) {
                  d = copycell(cdr(d)); /* This input is plist elt */
                  cdr(d) = NULL;
                  return d;
            }
            d = cdr(d);
            plist = d;
            if (d == NULL) {
                  plist = NULL;
                  break;
            }
      }
      if (plist) {
            memtypes oval = stickymem;

            /* plist is now s_last(cadr(v_find(key->string))) */
            cdr(plist) = copycell(key);
            d = NIL;
            cddr(plist) = d;

            stickymem = MEM_MALLOC;
            cdr(plist) = s_copy_chain(cdr(plist)); /* input gc-protected */
            stickymem = oval;
      }

      return d;
}

/* This is NUMBER OF ELEMENTS in a chain! */
static conscell *
sh_length(avl, il)
      conscell *avl, *il;
{
      char buf[10];
      int len = 0;

      il = cdar(avl);
      if (il && LIST(il)) {
            for (il = car(il); il != NULL; il = cdr(il))
                  ++len;
      }
      sprintf(buf, "%d", len);
      len = strlen(buf);

      avl = newstring(dupnstr(buf,len),len);
      return avl;
}

/* This splits incoming string at WHITESPACEs to a list of strings */

static conscell *
sh_ifssplit(avl, il)
      conscell *avl, *il;
{
      int len, l;
      conscell *tmp, *reply, *t;
      const char *p, *p0;
      GCVARS1;

      if (ifs == NULL)
            ifs_flush();

      il = cdar(avl);
      /* 'il' is supposedly a single string */

      if (!il || !STRING(il)) return il;
      p0  = il->cstring;
      len = il->slen;
      if (!p0) return il;


      reply = t = NULL;
      GCPRO1(reply);

      for (;len > 0; ++p0, --len) {

        if (WHITESPACE(*p0)) continue;
        p = p0;
        while (len > 0 && !WHITESPACE(*p)) ++p, --len;

        l = p - p0; /* Will always be at least 1 ! */

        tmp = newstring(dupnstr(p0, l), l);
        tmp->flags |= QUOTEDSTRING|ELEMENT;

        p0 = p;

        if (!t)
          reply = t = tmp;
        else {
          cdr(t) = tmp;
          t = tmp;
        }

      }

      UNGCPRO1;
      return reply;
}

/* returns -- return ALWAYS a string [mea] */
conscell *
sh_returns(avl, il, statusp)
      conscell *avl, *il;
      int *statusp;
{
      if ((il = cdar(avl)) && LIST(il) && cdr(il) == NULL)
            return il;
      else if (il == NULL)
            return NULL;
      else
            return il;
}

conscell *
sh_return(avl, il, statusp)
      conscell *avl, *il;
      int *statusp;
{
      int n, l;
      const char *cp;

      if ((il = cdar(avl)) && LIST(il) && cdr(il) == NULL)
            return il;
      else if (il == NULL)
            return NULL;
      else if (STRING(il)) {
            cp = il->cstring;
            l  = il->slen;
            n  = 0;
            for (;l > 0; ++cp, --l) {
              int c = (*cp) & 0xFF;
              if (!isascii(c) || !isdigit(c))
                break;
              n = n * 10 + c - '0';
            }
            if (l > 0) /* all not consumed! */
                  return il;
#if 0 /* can't happen... */
            if (n < 0) {
                  fprintf(stderr, "%s: %s: %d\n", car(avl)->string,
                              NEGATIVE_VALUE, n);
                  n = 1;
            }
#endif
            *statusp = n;
            return NULL;
      }
      /* NOTREACHED */
      return NULL;
}

/*-*/

static int
sh_colon(argc, argv)
      int argc;
      const char *argv[];
{
      return 0;
}

int
sh_builtin(argc, argv)
      int argc;
      const char *argv[];
{
      return 0;
}

static int
sh_echo(argc, argv)
      int argc;
      const char *argv[];
{
      int n;

      --argc, ++argv;
      /* '-n' ?? */
      if (argc > 0
          && argv[0][0] == '-' && argv[0][1] == 'n' && argv[0][2] == '\0') {
            --argc, ++argv;
            n = 0;
      } else
            n = 1;
      /* '--' ?? */
      if (argc > 0
          && argv[0][0] == '-' && argv[0][1] == '-' && argv[0][2] == '\0') {
            --argc, ++argv;
      }

      while (--argc >= 0) {
            fputs(*argv++, stdout);
            if (argc > 0)
                  putchar(' ');
      }
      if (n)
            putchar('\n');
      fflush(stdout);
      return 0;
}

static int
sh_cd(argc, argv)
      int argc;
      const char *argv[];
{
      const char *dir;
      char *cddir, *path;
      conscell *d;
      u_int pathlen;

      if (argc == 1) {
            /* cd $HOME */
            d = v_find(HOME);
            if (d == NULL || cdr(d) == NULL || LIST(cdr(d))) {
                  fprintf(stderr, "%s: %s\n", argv[0], NO_HOME_DIRECTORY);
                  return 1;
            }
            dir = cdr(d)->string;
      } else if (argc == 2) {
            /* cd argv[1] */
            dir = argv[1];
      } else {
            fprintf(stderr, USAGE_CD, argv[0]);
            return EX_USAGE;
      }
      if (!(dir[0] == '/' ||
            (dir[0] == '.' &&
             (dir[1] == '\0' || dir[1] == '/' ||
            (dir[1] == '.' && (dir[2] == '\0' || dir[2] == '/'))))) &&
          (d = v_find(CDPATH)) != NULL &&
          cdr(d) != NULL && STRING(cdr(d))) {
            cddir = cdr(d)->string;
            pathlen = strlen(cddir)+strlen(dir)+1+1;
            path = tmalloc(pathlen);
            while (cddir != NULL) {
                  cddir = prepath(cddir, dir, path, pathlen);
                  if (chdir(path) == 0) {
                        if (!(path[0] == '.' && path[1] == '/'))
                              printf("%s\n", path);
                        return 0;
                  }
            }
      } else if (chdir(dir) == 0)
            return 0;

      fprintf(stderr, "%s: %s: %s\n", argv[0], dir, strerror(errno));
      return 2;
}

static int
sh_hash(argc, argv)
      int argc;
      const char *argv[];
{
      if (argc == 1) {
            printf(NO_HASHING_INFORMATION);
            return 0;
      } else if (strcmp(argv[1], "-r") == 0) {
            path_flush();
            --argc, ++argv;
      }
      --argc, ++argv;
      while (argc-- > 0) {
            /* printf("hash '%s'\n", *argv); */
            path_hash(*argv++);
      }
      return 0;
}


/*

The "man sh" tells us:

     read name ...
          One line is read from the standard input and, using the
          internal  field separator, IFS (normally space or tab),
          to delimit word boundaries, the first word is  assigned
          to  the first name, the second word to the second name,
          and so forth, with leftover words assigned to the  last
          name.   Lines can be continued using \newline.  Charac-
          ters other than newline can be quoted by preceding them
          with a backslash.  These backslashes are removed before
          words are assigned to names, and no  interpretation  is
          done  on the character that follows the backslash.  The
          return code is 0, unless an EOF is encountered.

We add a note:

        We ignore backslash quoting completely, no line continuations!

        If there are fewer input "words" than parameter names,
        rest of the names will be assigned empty strings.

This is tested with ZMSH script:

  oldIFS=$IFS;IFS=":";while read user pass rest;do echo "u: $user";echo "p: $pass";echo "r: $rest";done < /etc/passwd;IFS=$oldIFS

which shows:

  u: root
  p: x
  r: 0:0:root:/root:/bin/bash
...etc...

*/

/* Store a char into buffer, allocate it, if necessary. */

static void sh_read_cput __((int, int *, char **, int *));
static void sh_read_cput(c, ip, bufp, sizep)
      int c, *ip, *sizep;
      char **bufp;
{
      char *buf   = *bufp;
      int bufsize = *sizep;
      int i       = *ip;

      if (i >= bufsize || !buf) {
        /* Need to grow ... */
        bufsize <<= 1;
        buf = erealloc(buf, bufsize+3);
      }

      buf[i]   = c;

      buf[++i] = 0; /* Keep the string terminated.. */

      *ip = i;
      *sizep = bufsize;
      *bufp  = buf;
}

static int
sh_read(argc, argv)
      int argc;
      const char *argv[];
{
      static char *buf     = NULL;
      static u_int bufsize = (BUFSIZ / 2) - 12;

      int eoinp, i, flag;

      if (argc == 1) {
          fprintf(stderr, USAGE_READ, argv[0]);
          return EX_USAGE;
      }
      if (ifs == NULL)
          ifs_flush();

      --argc, ++argv;

      eoinp = 0;

      for ( ; argc > 0; ) {

          int c;

          i = 0; /* Collect one variable full at the time into
                  the buffer.  Reuse the buffer for next variable
                  content. */

          flag = 0; /* 0 == SKIP IFS;  0 !=  ARGC rules */

          for (;;) {
            c = fgetc(stdin);
            if (c == EOF) break;
            ++eoinp;
            if (c == '\r' || c == 0) continue; /* ignore those */
            if (c == '\n') break;
            if (!flag) {
                /* Skipping leading IFSs */
                if (WHITESPACE(c)) continue;
                /* Non IFS character! */
                flag = 1;
            }
            if (argc > 1) {
                /* Scan over all non-IFS chars - stop at IFS chars */
                if (WHITESPACE(c)) break;
            } else {
                /* Scan until end of line (or EOF), as
                   the last variable collects the tail... */
                ;
            }
            /* Store this character into the buffer */
            sh_read_cput(c, &i, &buf, &bufsize);
          }

          /* Store collected value */
          if (argc > 0 && buf && i > 0) {
            --argc;
            v_set(*argv++, buf);
          }

          if ((c == EOF && i == 0) || c == '\n') break; /* Urgh.. EOF */

      }

      /* Left-over variables to be zeroed: */
      while (argc-- > 0) v_set(*argv++, "");

      return (0 == eoinp);
}

int
sh_include(argc, argv)
      int argc;
      const char *argv[];
{
      int fd;
      int status;
      conscell *d;
      struct stat stbuf;
      u_int pathlen;
      char *dir, *buf, *path = NULL;
      const char *rpath = NULL;

      if (argc != 2 || argv[1][0] == '\0') {
            fprintf(stderr, USAGE_INCLUDE, argv[0]);
            return EX_USAGE;
      }
      fd = -1;
      if (strchr(argv[1], '/') == NULL) {
            d = v_find(PATH);
            if (d != NULL && cdr(d) != NULL && STRING(cdr(d))) {
                  dir = cdr(d)->string;
                  pathlen = strlen(dir)+strlen(argv[1])+1+1;
                  path = tmalloc(pathlen);
                  rpath = path;
                  while (dir != NULL) {
                        dir = prepath(dir, argv[1], path, pathlen);
                        rpath = path;
                        fd = open(rpath, O_RDONLY, 0);
                        if (fd >= 0)
                              break;
                  }
            }
      } else {
            rpath = argv[1];
            fd = open(rpath, O_RDONLY, 0);
      }
      if (fd < 0) {
            fprintf(stderr, "%s: %s\n", argv[1], NOT_FOUND);
            return 1;
      }
      if (fstat(fd, &stbuf) < 0) {
            perror("fstat");
            close(fd);
            return 1;
      }
      status = (int) loadeval(fd, rpath, &stbuf);
      if (status >= 0) {
            /* loadeval closes fd */
            return status;
      }
      buf = tmalloc((u_int)stbuf.st_size + 1);
      status = (int) read(fd, buf, (int)stbuf.st_size);
      if (status != (int) stbuf.st_size) {
            perror("read");
            close(fd);
            return 2;
      }
      close(fd);
      buf[(u_int)stbuf.st_size] = '\0';
      return eval(buf, argv[1], rpath, &stbuf);
}

static int
sh_bce(argc, argv)            /* handle break, continue, and exit */
      int argc;
      const char *argv[];
{
      int n;

      if (argc > 2 || (argc == 2 && !isdigit(argv[1][0])))
            fprintf(stderr, USAGE_BCE, argv[0]);
      else if (argc == 1)
            /* 0 for "return" and "exit", 1 o/w */
            return !(argv[0][0] == 'r' || argv[0][0] == 'e');
      if ((n = atoi(argv[1])) < 0) {
            fprintf(stderr, "%s: %s: %d\n", argv[0], NEGATIVE_VALUE, n);
            n = 1;
      } else if (n == 0) {
            switch (argv[0][0]) {
            case 'b': case 'c':     /* 0 for break and continue means 1 */
                  n = 1;
                  break;
            }
      }
      return n;
}

static int
sh_eval(argc, argv)
      int argc;
      const char *argv[];
{
      int len, i;
      int status;
      void *table, *eotable;
      char *cp, *buf;

      for (len = 0, i = 1; i < argc; ++i) {
            len += strlen(argv[i]);
            ++len;
      }
      if (len < argc)
            return 0;
      buf = tmalloc(len+1+1/* trailing \n*/);
      for (cp = buf, i = 1; i < argc; ++i) {
            strcpy(cp, argv[i]);
            cp += strlen(argv[i]);
            *cp++ = ' ';
      }
      *cp++ = '\n';
      *cp = '\0';
      commandline = s_pushstack(commandline, buf);
      table = SslWalker("eval string", stdout, &eotable);
      status = 7;
      if (table != NULL) {
            if (isset('O')) {
                  table = optimize(0, table, &eotable);
                  if (isset('V'))
                        table = optimize(1, table, &eotable);
            }
            interpret(table, eotable, NULL, globalcaller,
                    &status, (struct codedesc *)NULL);
      }
      return status;
}

static int
sh_export(argc, argv)
      int argc;
      const char *argv[];
{
      conscell *elist, *glist, *scope;

      if (argc > 1) {
            /* export the listed variables */
            --argc, ++argv;
            while (argc > 0) {
                  v_export(argv[0]);
                  --argc, ++argv;
            }
            return 0;
      }
      /* print which variables have been exported */
      for (scope = car(envarlist); cddr(scope) != NULL; scope = cdr(scope))
            continue;
      for (elist = cadr(scope); elist != NULL; elist = cddr(elist)) {
            if (LIST(elist) || elist->string == NULL)
                  continue;
            for (glist = car(scope); glist != NULL; glist = cddr(glist)) {
                  if (STRING(glist) && glist->slen == elist->slen &&
                      memcmp(glist->string, elist->string, elist->slen) == 0)
                        break;
            }
            if (glist == NULL)
                  printf("%s %s\n", EXPORT, elist->string);
      }
      return 0;
}

static int
sh_getopts(argc, argv)
      int argc;
      const char *argv[];
{
      char buf[20];
      const char *optstring, *name;
      const char **av;
      int c;
      conscell *d;

      if (argc < 3) {
            fprintf(stderr, USAGE_GETOPTS, argv[0]);
            return EX_USAGE;
      }
      zopterr = 0;
      --argc, ++argv;
      optstring = argv[0];
      --argc, ++argv;
      name = argv[0];
      if (argc == 1) {
            for (d = cdar(globalcaller->argv); d != NULL ; d = cdr(d))
                  ++argc;
            av = (const char **)tmalloc((argc+1)*sizeof (char *));
            argv = av;
            for (d = car(globalcaller->argv); d != NULL ; d = cdr(d))
                  if (STRING(d)) {
                        *av++ = d->string;
                        ++argc;
                  }
            *av = NULL;
      }
      c = zgetopt(argc, (char*const*)argv,
                optstring);   /* this MUST be our getopt() */
      sprintf(buf, "%d", zoptind);
      v_set(OPTIND, buf);
      if (c == EOF)
            return 1;
      if (zoptarg != NULL)    /* our zgetopt() makes this reliable */
            v_set(OPTARG, zoptarg);
      sprintf(buf, "%c", c);
      v_set(name, buf);
      return 0;
}

static int
sh_shift(argc, argv)
      int argc;
      const char *argv[];
{
      int n;
      conscell *d;

      if (argc > 2 || (argc == 2 && !isdigit(argv[1][0]))) {
            fprintf(stderr, USAGE_SHIFT, argv[0]);
            return EX_USAGE;
      }
      if (argc == 2) {
            if ((n = atoi(argv[1])) < 1)
                  return 0;
      } else
            n = 1;
      for (d = cdar(globalcaller->argv); d != NULL && n > 0; d = cdr(d))
            --n;
      cdar(globalcaller->argv) = d;       /* XX: possible malloc leak */
      return 0;
}

static int
sh_umask(argc, argv)
      int argc;
      const char *argv[];
{
      int mask;
      const char *cp;

      if (argc > 2 || (argc == 2 && !isdigit(argv[1][0]))) {
            fprintf(stderr, USAGE_UMASK, argv[0]);
            return EX_USAGE;
      }
      if (argc == 1) {
            mask = umask(077);
            printf("%#o\n", mask);
            umask(mask);
            return 0;
      }
      mask = 0;
      for (cp = argv[1]; *cp != '\0'; ++cp)
            mask = mask * 8 + (*cp - '0');            /* XX: ebcdic?? */
      umask(mask);
      return 0;
}

static int
sh_set(argc, argv)
      int argc;
      const char *argv[];
{
      int n, i;
      memtypes oval;
      const char *cp;
      char **kk, *ep;
      conscell *d, *pd = NULL, *scope;
      char ebuf[32];

      if (argc == 1) {
            /* print variable values */
            /* first count how many there might be */
            n = 0;
            for (scope = car(envarlist); scope != NULL; scope = cdr(scope))
                  for (d = car(scope); d != NULL; d = cddr(d))
                        ++n;
            /* allocate enough space to hold cache ptrs */
#ifdef      USE_ALLOCA
            kk = (char **)alloca((n+1) * sizeof (char *));
#else
            kk = (char **)emalloc((n+1) * sizeof (char *));
#endif
            kk[0] = NULL;
            /* iterate through all variables */
            for (scope = car(envarlist); scope != NULL; scope = cdr(scope))
                  for (d = car(scope); d != NULL; d = cddr(d)) {
                        if (!STRING(d) || cdr(d) == envarlist)
                              continue;
                        /* if we already saw it, skip this one O(N^2) */
                        for (i = 0; kk[i] != NULL && i < n; ++i)
                              if (strcmp(kk[i], (char *)d->string) == 0)
                                    break;
                        if (kk[i] == NULL) {
                              kk[i] = (char *)d->string;
                              kk[i+1] = NULL;
                              printf("%s=", (char*)d->string);
                              s_grind(cdr(d), stdout);
                              putchar('\n');
                        }
                  }
#ifndef USE_ALLOCA
            free(kk);
#endif
            return 0;
      }
      --argc, ++argv;
      if (argv[0][0] == '-' || argv[0][0] == '+') {
            i = (argv[0][0] == '-');
            ep = ebuf;
            for (cp = &argv[0][1]; *cp != '\0'; ++cp) {
                  switch (*cp) {
                  case 'a':/* automatically export changed variables */
                  case 'e':/* exit on error exit status of any command */
                  case 'f':/* disable filename generation (no globbing) */
                  case 'h':/* hash program locations */
                  case 'n':/* read commands but do not execute them */
                  case 't':/* read and execute one command only */
                  case 'u':/* unset variables are error on substitution */
                  case 'v':/* print shell input lines as they are read */
                  case 'x':/* print commands as they are executed */
                  case 'L':/* Trace LEXER processing (sslWalker) */
                  case 'C':
                  case 'P':
                  case 'S':
                        setopt(*cp, i);
                        break;
                  case '-':   /* do nothing */
                        break;
                  case 'k':   /* we do not support the k option */
                  default:    /* complain */
                        if (ep < (ebuf + sizeof(ebuf)-2))
                          *ep++ = *cp;
                        break;
                  }
            }
            if (ep > ebuf) {
                  *ep = '\0';
                  fprintf(stderr, "%s: %s: %s\n",
                              argv[-1], BAD_OPTIONS, ebuf);
            }
            --argc, ++argv;
      }
      if (argc == 0)
            return 0;
      /* the remaining arguments should replace $@ */
      oval = stickymem;
      stickymem = MEM_MALLOC;
      if (globalcaller != NULL && globalcaller->argv != NULL)
            pd = car(globalcaller->argv);
      while (argc-- > 0) {
            int slen = strlen(*argv);
            d = newstring(dupnstr(*argv,slen),slen);
            /* These go into  globalcaller->  protected storage */
            if (pd != NULL)
                  cdr(pd) = d;
            ++argv;
            pd = d;
      }
      stickymem = oval;
      return 0;
}

static const char *sacred[] = { PATH, PS1, PS2, IFS, MAILCHECK, 0 };

static int
sh_unset(argc, argv)
      int argc;
      const char *argv[];
{
      conscell *d, *pd = NULL, *scope, *next;
      const char **cpp, *av0;

      if (argc == 1) {
            fprintf(stderr, USAGE_UNSET, argv[0]);
            return EX_USAGE;
      }
      av0 = argv[0];
      --argc, ++argv;
      while (argc-- > 0) {
            for (cpp = sacred; *cpp != NULL; ++cpp) {
                  if (strcmp(*cpp, *argv) == 0) {
                        fprintf(stderr, "%s: %s %s\n",
                              av0, CANNOT_UNSET, *argv);
                        break;
                  }
            }
            if (*cpp != NULL) {     /* couldn't unset that one */
                  ++argv;
                  continue;
            }
            for (scope = car(envarlist); scope != NULL; scope = cdr(scope))
                  for (d = car(scope); d != NULL; pd = d, d = next) {
                        next = cddr(d);
                        if (LIST(d) || strcmp((char *)d->string, *argv) != 0)
                              continue;
                        /* now we've got it! unlink and free */
                        if (d == car(scope))
                              car(scope) = next;
                        else
                              cddr(pd) = next;
                        cddr(d) = NULL;
                        /* s_free_tree(d); -- GC does it.. */ 
                        /* no point doing anything else in this scope */
                        break;
                  }
            ++argv;
      }
      return 0;
}

static int
sh_wait(argc, argv)
      int argc;
      const char *argv[];
{
      int retcode, pid;
      int status = 0;

      if (argc > 2 || (argc == 2 && !isdigit(argv[1][0]))) {
            fprintf(stderr, USAGE_WAIT, argv[0]);
            return EX_USAGE;
      }
      while ((pid = wait(&status)) > 0) {
            if (argc == 2 && pid == atoi(argv[1]))
                  break;
      }
      if (pid < 0)            /* no more children */
            return 0;
      if (WSIGNALSTATUS(status) != 0) {
            fprintf(stderr, "%s", strsignal(WSIGNALSTATUS(status)));
            if (status&0200)
                  fprintf(stderr, CORE_DUMPED);
            fprintf(stderr, "\n");
            retcode = 0200 + WSIGNALSTATUS(status);
      } else
            retcode = WEXITSTATUS(status);
      return retcode;
}

static int
sh_times(argc, argv)
      int argc;
      const char *argv[];
{
      struct tms foo;

      if (argc > 1) {
            fprintf(stderr, USAGE_TIMES, argv[0]);
            return EX_USAGE;
      }
      if (times(&foo) < 0)
            return 1;
      printf("%dm%ds %dm%ds\n",
             foo.tms_cutime / HZ / 60, (foo.tms_cutime / HZ) % 60,
             foo.tms_cstime / HZ / 60, (foo.tms_cstime / HZ) % 60);
      return 0;
}

static int
sh_type(argc, argv)
      int argc;
      const char *argv[];
{
      char *dir, *path;
      struct shCmd *shcmdp;
      struct sslfuncdef *sfdp;
      u_int pathlen;

      for (--argc, ++argv; argc-- > 0 ; ++argv) {
            if (*argv == '\0') {
                  printf(NULL_NAME);
                  continue;
            }
            functype(*argv, &shcmdp, &sfdp);
            /* defined function ? */
            if (sfdp != NULL) {
                  printf("%s %s\n", *argv, IS_A_SHELL_FUNCTION);
                  continue;
            }
            /* builtin ? */
            if (shcmdp != NULL) {
                  printf("%s %s\n", *argv, IS_A_SHELL_BUILTIN);
                  continue;
            }
            /* unix command */
            dir = path_hash(*argv);
            if (dir != NULL) {
                  pathlen = strlen(dir)+strlen(*argv)+1+1;
                  path = tmalloc(pathlen);
                  dir = prepath(dir, *argv, path, pathlen);
                  printf("%s %s %s\n", *argv, IS, path);
                  continue;
            }
            printf("%s %s\n", *argv, NOT_FOUND);
      }
      return 0;
}


static int
sh_sleep(argc, argv)
      int argc;
      const char *argv[];
{
      if (argc != 2 || atoi(argv[1]) <= 0) {
            fprintf(stderr, USAGE_SLEEP, argv[0]);
            return EX_USAGE;
      }
      sleep(atoi(argv[1]));
      return 0;
}

static int
sh_true(argc, argv)
      int argc;
      const char *argv[];
{
      return 0;
}

static int
sh_false(argc, argv)
      int argc;
      const char *argv[];
{
      return 1;
}

Generated by  Doxygen 1.6.0   Back to index