BSD 4_4_Lite2 development
authorCSRG <csrg@ucbvax.Berkeley.EDU>
Fri, 30 Apr 1993 05:49:06 +0000 (21:49 -0800)
committerCSRG <csrg@ucbvax.Berkeley.EDU>
Fri, 30 Apr 1993 05:49:06 +0000 (21:49 -0800)
Work on file usr/src/contrib/rc-1.4/except.c
Work on file usr/src/contrib/rc-1.4/execve.c
Work on file usr/src/contrib/rc-1.4/exec.c
Work on file usr/src/contrib/rc-1.4/getopt.c
Work on file usr/src/contrib/rc-1.4/heredoc.c
Work on file usr/src/contrib/rc-1.4/list.c
Work on file usr/src/contrib/rc-1.4/match.c
Work on file usr/src/contrib/rc-1.4/nalloc.c
Work on file usr/src/contrib/rc-1.4/open.c
Work on file usr/src/contrib/rc-1.4/redir.c
Work on file usr/src/contrib/rc-1.4/status.c
Work on file usr/src/contrib/rc-1.4/tree.c
Work on file usr/src/contrib/rc-1.4/utils.c

Synthesized-from: CSRG/cd3/4.4BSD-Lite2

13 files changed:
usr/src/contrib/rc-1.4/except.c [new file with mode: 0644]
usr/src/contrib/rc-1.4/exec.c [new file with mode: 0644]
usr/src/contrib/rc-1.4/execve.c [new file with mode: 0644]
usr/src/contrib/rc-1.4/getopt.c [new file with mode: 0644]
usr/src/contrib/rc-1.4/heredoc.c [new file with mode: 0644]
usr/src/contrib/rc-1.4/list.c [new file with mode: 0644]
usr/src/contrib/rc-1.4/match.c [new file with mode: 0644]
usr/src/contrib/rc-1.4/nalloc.c [new file with mode: 0644]
usr/src/contrib/rc-1.4/open.c [new file with mode: 0644]
usr/src/contrib/rc-1.4/redir.c [new file with mode: 0644]
usr/src/contrib/rc-1.4/status.c [new file with mode: 0644]
usr/src/contrib/rc-1.4/tree.c [new file with mode: 0644]
usr/src/contrib/rc-1.4/utils.c [new file with mode: 0644]

diff --git a/usr/src/contrib/rc-1.4/except.c b/usr/src/contrib/rc-1.4/except.c
new file mode 100644 (file)
index 0000000..48ea008
--- /dev/null
@@ -0,0 +1,140 @@
+#include <setjmp.h>
+#include <signal.h>
+#include "rc.h"
+#include "jbwrap.h"
+
+/*
+   A return goes back stack frames to the last return. A break does
+   not. A signal goes to the last interactive level. (see below)
+*/
+
+bool nl_on_intr = TRUE;
+
+static Estack *estack;
+
+/* add an exception to the input stack. */
+
+extern void except(ecodes e, Edata data, Estack *ex) {
+       ex->prev = estack;
+       estack = ex;
+       estack->e = e;
+       estack->data = data;
+       if (e == eError || e == eBreak || e == eReturn)
+               estack->interactive = interactive;
+}
+
+/* remove an exception, restore last interactive value */
+
+extern void unexcept() {
+       switch (estack->e) {
+       default:
+               break;
+       case eError:
+               interactive = estack->interactive;
+               break;
+       case eArena:
+               restoreblock(estack->data.b);
+               break;
+       case eFifo:
+               unlink(estack->data.name);
+               break;
+       case eFd:
+               close(estack->data.fd);
+               break;
+       }
+       estack = estack->prev;
+}
+
+/*
+   Raise an exception. The rules are pretty complicated: you can return
+   from a loop inside a function, but you can't break from a function
+   inside of a loop. On errors, rc_raise() goes back to the LAST
+   INTERACTIVE stack frame. If no such frame exists, then rc_raise()
+   exits the shell.  This is what happens, say, when there is a syntax
+   error in a noninteractive shell script. While traversing the
+   exception stack backwards, rc_raise() also removes input sources
+   (closing file-descriptors, etc.) and pops instances of variables
+   that have been pushed onto the variable stack (e.g., for a function
+   call (for $*) or a local assignment).
+*/
+
+extern void rc_raise(ecodes e) {
+       if (e == eError && rc_pid != getpid())
+               exit(1); /* child processes exit on an error/signal */
+       for (; estack != NULL; estack = estack->prev)
+               if (estack->e != e) {
+                       if (e == eBreak && estack->e != eArena)
+                               rc_error("break outside of loop");
+                       else if (e == eReturn && estack->e == eError) /* can return from loops inside functions */
+                               rc_error("return outside of function");
+                       switch (estack->e) {
+                       default:
+                               break;
+                       case eVarstack:
+                               varrm(estack->data.name, TRUE);
+                               break;
+                       case eArena:
+                               restoreblock(estack->data.b);
+                               break;
+                       case eFifo:
+                               unlink(estack->data.name);
+                               break;
+                       case eFd:
+                               close(estack->data.fd);
+                               break;
+                       }
+               } else {
+                       if (e == eError && !estack->interactive) {
+                               popinput();
+                       } else {
+                               Jbwrap *j = estack->data.jb;
+
+                               interactive = estack->interactive;
+                               estack = estack->prev;
+                               longjmp(j->j, 1);
+                       }
+               }
+       rc_exit(1); /* top of exception stack */
+}
+
+extern bool outstanding_cmdarg() {
+       return estack->e == eFifo || estack->e == eFd;
+}
+
+extern void pop_cmdarg(bool remove) {
+       for (; estack != NULL; estack = estack->prev)
+               switch (estack->e) {
+               case eFifo:
+                       if (remove)
+                               unlink(estack->data.name);
+                       break;
+               case eFd:
+                       if (remove)
+                               close(estack->data.fd);
+                       break;
+               default:
+                       return;
+               }
+}
+
+/* exception handlers */
+
+extern void rc_error(char *s) {
+       pr_error(s);
+       set(FALSE);
+       redirq = NULL;
+       cond = FALSE; /* no longer inside conditional */
+       rc_raise(eError);
+}
+
+extern void sigint(int s) {
+       if (s != SIGINT)
+               panic("s != SIGINT in sigint catcher");
+       /* this is the newline you see when you hit ^C while typing a command */
+       if (nl_on_intr)
+               fprint(2, "\n");
+       nl_on_intr = TRUE;
+       redirq = NULL;
+       cond = FALSE;
+       rc_raise(eError);
+}
diff --git a/usr/src/contrib/rc-1.4/exec.c b/usr/src/contrib/rc-1.4/exec.c
new file mode 100644 (file)
index 0000000..68da964
--- /dev/null
@@ -0,0 +1,108 @@
+/* exec.c */
+#include <signal.h>
+#include <errno.h>
+#include <setjmp.h>
+#include "rc.h"
+#include "jbwrap.h"
+
+/*
+   Takes an argument list and does the appropriate thing (calls a
+   builtin, calls a function, etc.)
+*/
+
+extern void exec(List *s, bool parent) {
+       char **av, **ev = NULL;
+       int pid, stat;
+       builtin_t *b;
+       char *path = NULL;
+       bool didfork, returning, saw_exec, saw_builtin;
+       av = list2array(s, dashex);
+       saw_builtin = saw_exec = FALSE;
+       do {
+               if (*av == NULL || isabsolute(*av))
+                       b = NULL;
+               else if (!saw_builtin && fnlookup(*av) != NULL)
+                       b = funcall;
+               else
+                       b = isbuiltin(*av);
+
+               /*
+                  a builtin applies only to the immmediately following
+                  command, e.g., builtin exec echo hi
+               */
+               saw_builtin = FALSE;
+
+               if (b == b_exec) {
+                       av++;
+                       saw_exec = TRUE;
+                       parent = FALSE;
+               } else if (b == b_builtin) {
+                       av++;
+                       saw_builtin = TRUE;
+               }
+       } while (b == b_exec || b == b_builtin);
+       if (*av == NULL && saw_exec) { /* do redirs and return on a null exec */
+               doredirs();
+               return;
+       }
+       /* force an exit on exec with any rc_error, but not for null commands as above */
+       if (saw_exec)
+               rc_pid = -1;
+       if (b == NULL) {
+               path = which(*av, TRUE);
+               if (path == NULL && *av != NULL) { /* perform null commands for redirections */
+                       set(FALSE);
+                       redirq = NULL;
+                       if (parent)
+                               return;
+                       rc_exit(1);
+               }
+               ev = makeenv(); /* environment only needs to be built for execve() */
+       }
+       /*
+          If parent & the redirq is nonnull, builtin or not it has to fork.
+          If the fifoq is nonnull, then it must be emptied at the end so we
+          must fork no matter what.
+        */
+       if ((parent && (b == NULL || redirq != NULL)) || outstanding_cmdarg()) {
+               pid = rc_fork();
+               didfork = TRUE;
+       } else {
+               pid = 0;
+               didfork = FALSE;
+       }
+       returning = (!didfork && parent);
+       switch (pid) {
+       case -1:
+               uerror("fork");
+               rc_error(NULL);
+               /* NOTREACHED */
+       case 0:
+               if (!returning)
+                       setsigdefaults(FALSE);
+               pop_cmdarg(FALSE);
+               doredirs();
+
+               /* null commands performed for redirections */
+               if (*av == NULL || b != NULL) {
+                       if (b != NULL)
+                               (*b)(av);
+                       if (returning)
+                               return;
+                       rc_exit(getstatus());
+               }
+               execve(path, (char * const *) av, (char * const *) ev);
+               uerror(*av);
+               rc_exit(1);
+               /* NOTREACHED */
+       default:
+               redirq = NULL;
+               rc_wait4(pid, &stat, TRUE);
+               setstatus(-1, stat);
+               if ((stat & 0xff) == 0)
+                       nl_on_intr = FALSE;
+               SIGCHK;
+               nl_on_intr = TRUE;
+               pop_cmdarg(TRUE);
+       }
+}
diff --git a/usr/src/contrib/rc-1.4/execve.c b/usr/src/contrib/rc-1.4/execve.c
new file mode 100644 (file)
index 0000000..b21b30c
--- /dev/null
@@ -0,0 +1,61 @@
+/* execve.c: an execve() for geriatric unices without #! */
+
+/*
+   NOTE: this file depends on a hack in footobar.c which places two free spots before
+   av[][] so that execve does not have to call malloc.
+*/
+
+#include <errno.h>
+#include "rc.h"
+
+#define giveupif(x) { if (x) goto fail; }
+
+extern int my_execve(const char *path, const char **av, const char **ev) {
+       int fd, len, fst, snd, end;
+       bool noarg;
+       char pb[256]; /* arbitrary but generous limit */
+       execve(path, av, ev);
+       if (errno != ENOEXEC)
+               return -1;
+       fd = rc_open(path, rFrom);
+       giveupif(fd < 0);
+       len = read(fd, pb, sizeof pb);
+       close(fd);
+       /* reject scripts which don't begin with #! */
+       giveupif(len <= 0 || pb[0] != '#' || pb[1] != '!');
+       for (fst = 2; fst < len && (pb[fst] == ' ' || pb[fst] == '\t'); fst++)
+               ; /* skip leading whitespace */
+       giveupif(fst == len);
+       for (snd = fst; snd < len && pb[snd] != ' ' && pb[snd] != '\t' && pb[snd] != '\n'; snd++)
+               ; /* skip first arg */
+       giveupif(snd == len);
+       noarg = (pb[snd] == '\n');
+       pb[snd++] = '\0'; /* null terminate the first arg */
+       if (!noarg) {
+               while (snd < len && (pb[snd] == ' ' || pb[snd] == '\t'))
+                       snd++; /* skip whitespace to second arg */
+               giveupif(snd == len);
+               noarg = (pb[snd] == '\n'); /* could have trailing whitespace after only one arg */
+               if (!noarg) {
+                       for (end = snd; end < len && pb[end] != ' ' && pb[end] != '\t' && pb[end] != '\n'; end++)
+                               ; /* skip to the end of the second arg */
+                       giveupif(end == len);
+                       if (pb[end] == '\n') {
+                               pb[end] = '\0'; /* null terminate the first arg */
+                       } else {                /* else check for a spurious third arg */
+                               pb[end++] = '\0';
+                               while (end < len && (pb[end] == ' ' || pb[end] == '\t'))
+                                       end++;
+                               giveupif(end == len || pb[end] != '\n');
+                       }
+               }
+       }
+       *av = path;
+       if (!noarg)
+               *--av = pb + snd;
+       *--av = pb + fst;
+       execve(*av, av, ev);
+       return -1;
+fail:  errno = ENOEXEC;
+       return -1;
+}
diff --git a/usr/src/contrib/rc-1.4/getopt.c b/usr/src/contrib/rc-1.4/getopt.c
new file mode 100644 (file)
index 0000000..423b84a
--- /dev/null
@@ -0,0 +1,50 @@
+#include "rc.h"
+
+int     rc_opterr = 1;
+int     rc_optind = 1;
+int     rc_optopt;
+char    *rc_optarg;
+
+/* getopt routine courtesy of David Sanderson */
+extern int rc_getopt(int argc, char **argv, char *opts) {
+        static int sp = 1;
+        int c;
+        char *cp;
+       if (rc_optind == 0) /* reset rc_getopt() */
+               rc_optind = sp = 1;
+        if (sp == 1)
+                if (rc_optind >= argc || argv[rc_optind][0] != '-' || argv[rc_optind][1] == '\0') {
+                        return -1;
+                } else if (strcmp(argv[rc_optind], "--") == 0) {
+                        rc_optind++;
+                        return -1;
+                }
+        rc_optopt = c = argv[rc_optind][sp];
+        if (c == ':' || (cp=strchr(opts, c)) == 0) {
+                fprint(2, "%s: bad option: -%c\n", argv[0], c);
+                if (argv[rc_optind][++sp] == '\0') {
+                        rc_optind++;
+                        sp = 1;
+                }
+                return '?';
+        }
+        if (*++cp == ':') {
+                if (argv[rc_optind][sp+1] != '\0') {
+                        rc_optarg = &argv[rc_optind++][sp+1];
+                } else if (++rc_optind >= argc) {
+                        fprint(2, "%s: option requires an argument -- %c\n", argv[0], c);
+                        sp = 1;
+                        return '?';
+                } else
+                        rc_optarg = argv[rc_optind++];
+                sp = 1;
+        } else {
+                if (argv[rc_optind][++sp] == '\0') {
+                        sp = 1;
+                        rc_optind++;
+                }
+                rc_optarg = NULL;
+        }
+        return c;
+}
diff --git a/usr/src/contrib/rc-1.4/heredoc.c b/usr/src/contrib/rc-1.4/heredoc.c
new file mode 100644 (file)
index 0000000..ba4fd3a
--- /dev/null
@@ -0,0 +1,156 @@
+/* heredoc.c: heredoc slurping is done here */
+
+#include "rc.h"
+
+struct Hq {
+       Node *doc;
+       char *name;
+       Hq *n;
+       bool quoted;
+} *hq;
+
+static bool dead = FALSE;
+
+/*
+ * read in a heredocument. A clever trick: skip over any partially matched end-of-file
+ * marker storing only the number of characters matched. If the whole marker is matched,
+ * return from readheredoc(). If only part of the marker is matched, copy that part into
+ * the heredocument.
+ *
+ * BUG: if the eof string contains a newline, the state can get confused, and the
+ * heredoc may continue past where it should.  on the other hand, /bin/sh seems to
+ * never get out of its readheredoc() when the heredoc string contains a newline
+ */
+
+static char *readheredoc(char *eof) {
+       int c;
+       char *t, *buf, *bufend;
+       unsigned char *s;
+       size_t bufsize;
+       t = buf = nalloc(bufsize = 512);
+       bufend = &buf[bufsize];
+       dead = FALSE;
+#define        RESIZE(extra) { \
+               char *nbuf; \
+               bufsize = bufsize * 2 + extra; \
+               nbuf = nalloc(bufsize); \
+               memcpy(nbuf, buf, (size_t) (t - buf)); \
+               t = nbuf + (t - buf); \
+               buf = nbuf; \
+               bufend = &buf[bufsize]; \
+       }
+       for (;;) {
+               print_prompt2();
+               for (s = (unsigned char *) eof; (c = gchar()) == *s; s++)
+                       ;
+               if (*s == '\0' && (c == '\n' || c == EOF)) {
+                       *t++ = '\0';
+                       return buf;
+               }
+               if (s != (unsigned char *) eof) {
+                       size_t len = s - (unsigned char *) eof;
+                       if (t + len >= bufend)
+                               RESIZE(len);
+                       memcpy(t, eof, len);
+                       t += len;
+               }
+               for (;; c = gchar()) {
+                       if (c == EOF) {
+                               yyerror("heredoc incomplete");
+                               dead = TRUE;
+                               return NULL;
+                       }
+                       if (t + 1 >= bufend)
+                               RESIZE(0);
+                       *t++ = c;
+                       if (c == '\n')
+                               break;
+               }
+       }
+}
+
+/* parseheredoc -- turn a heredoc with variable references into a node chain */
+
+static Node *parseheredoc(char *s) {
+       int c = *s;
+       Node *result = NULL;
+       while (TRUE) {
+               Node *node;
+               switch (c) {
+               default: {
+                       char *begin = s;
+                       while ((c = *s++) != '\0' && c != '$')
+                               ;
+                       *--s = '\0';
+                       node = mk(nQword, begin, NULL);
+                       break;
+               }
+               case '$': {
+                       char *begin = ++s, *var;
+                       c = *s++;
+                       if (c == '$') {
+                               node = mk(nQword, "$", NULL);
+                               c = *s;
+                       } else {
+                               size_t len = 0;
+                               do
+                                       len++;
+                               while (!dnw[c = *(unsigned char *) s++]);
+                               if (c == '^')
+                                       c = *s;
+                               else
+                                       s--;
+                               var = nalloc(len + 1);
+                               var[len] = '\0';
+                               memcpy(var, begin, len);
+                               node = mk(nFlat, mk(nWord, var, NULL));
+                       }
+                       break;
+               }
+               case '\0':
+                       return result;
+               }
+               result = (result == NULL) ? node : mk(nConcat, result, node);
+       }
+}
+
+/* read in heredocs when yyparse hits a newline. called from yyparse */
+
+extern int heredoc(int end) {
+       Hq *here;
+       if ((here = hq) != NULL) {
+               hq = NULL;
+               if (end) {
+                       yyerror("heredoc incomplete");
+                       return FALSE;
+               }
+               do {
+                       Node *n = here->doc;
+                       char *s = readheredoc(here->name);
+                       if (dead)
+                               return FALSE;
+                       n->u[2].p = here->quoted ? mk(nQword, s, NULL) : parseheredoc(s);
+                       n->u[0].i = rHerestring;
+               } while ((here = here->n) != NULL);
+       }
+       return TRUE;
+}
+
+/* queue pending heredocs into a queue. called from yyparse */
+
+extern int qdoc(Node *name, Node *n) {
+       Hq *new, **prev;
+       if (name->type != nWord && name->type != nQword) {
+               yyerror("eof-marker not a single literal word");
+               flushu();
+               return FALSE;
+       }
+       for (prev = &hq; (new = *prev) != NULL; prev = &new->n)
+               ;
+       *prev = new = nnew(Hq);
+       new->name = name->u[0].s;
+       new->quoted = (name->type == nQword);
+       new->doc = n;
+       new->n = NULL;
+       return TRUE;
+}
diff --git a/usr/src/contrib/rc-1.4/list.c b/usr/src/contrib/rc-1.4/list.c
new file mode 100644 (file)
index 0000000..fba6f6a
--- /dev/null
@@ -0,0 +1,57 @@
+/* list.c: routines for manipulating the List type */
+
+#include "rc.h"
+
+/*
+   These list routines assign meta values of null to the resulting lists;
+   it is impossible to glob with the value of a variable unless this value
+   is rescanned with eval---therefore it is safe to throw away the meta-ness
+   of the list.
+*/
+
+/* free a list from malloc space */
+
+extern void listfree(List *p) {
+       while (p != NULL) {
+               List *n = p->n;
+               efree(p->w);
+               efree(p);
+               p = n;
+       }
+}
+
+/* Copy list into malloc space (for storing a variable) */
+
+extern List *listcpy(List *s, void *(*alloc)(size_t)) {
+       List *top, *r;
+       for (top = r = NULL; s != NULL; s = s->n) {
+               if (top == NULL)
+                       r = top = (*alloc)(sizeof (List));
+               else
+                       r = r->n = (*alloc)(sizeof (List));
+               r->w = (*alloc)(strlen(s->w) + 1);
+               strcpy(r->w, s->w);
+               r->m = NULL;
+       }
+       if (r != NULL)
+               r->n = NULL;
+       return top;
+}
+
+/* Length of list */
+
+extern size_t listlen(List *s) {
+       size_t size;
+       for (size = 0; s != NULL; s = s->n)
+               size += strlen(s->w) + 1;
+       return size;
+}
+
+/* Number of elements in list */
+
+extern int listnel(List *s) {
+       int nel;
+       for (nel = 0; s != NULL; s = s->n)
+               nel++;
+       return nel;
+}
diff --git a/usr/src/contrib/rc-1.4/match.c b/usr/src/contrib/rc-1.4/match.c
new file mode 100644 (file)
index 0000000..6f87614
--- /dev/null
@@ -0,0 +1,99 @@
+/* match.c: pattern matching routines */
+
+#include "rc.h"
+
+static int rangematch(char *, char);
+
+enum { RANGE_FAIL = -1, RANGE_ERROR = -2 };
+
+/* match() matches a single pattern against a single string. */
+
+extern bool match(char *p, char *m, char *s) {
+       int i, j;
+       if (m == NULL)
+               return streq(p, s);
+       i = 0;
+       while (1) {
+               if (p[i] == '\0')
+                       return *s == '\0';
+               else if (m[i]) {
+                       switch (p[i++]) {
+                       case '?':
+                               if (*s++ == '\0')
+                                       return FALSE;
+                               break;
+                       case '*':
+                               while (p[i] == '*' && m[i] == 1)        /* collapse multiple stars */
+                                       i++;
+                               if (p[i] == '\0')       /* star at end of pattern? */
+                                       return TRUE;
+                               while (*s != '\0')
+                                       if (match(p + i, m + i, s++))
+                                               return TRUE;
+                               return FALSE;
+                       case '[':
+                               if (*s == '\0')
+                                       return FALSE;
+                               switch (j = rangematch(p + i, *s)) {
+                               default:
+                                       i += j;
+                                       break;
+                               case RANGE_FAIL:
+                                       return FALSE;
+                               case RANGE_ERROR:
+                                       if (*s != '[')
+                                               return FALSE;
+                               }
+                               s++;
+                               break;
+                       default:
+                               panic("bad metacharacter in match");
+                               /* NOTREACHED */
+                               return FALSE; /* hush up gcc -Wall */
+                       }
+               } else if (p[i++] != *s++)
+                       return FALSE;
+       }
+}
+
+/*
+   From the ed(1) man pages (on ranges):
+
+       The `-' is treated as an ordinary character if it occurs first
+       (or first after an initial ^) or last in the string.
+
+       The right square bracket does not terminate the enclosed string
+       if it is the first character (after an initial `^', if any), in
+       the bracketed string.
+
+   rangematch() matches a single character against a class, and returns
+   an integer offset to the end of the range on success, or -1 on
+   failure.
+*/
+
+static int rangematch(char *p, char c) {
+       char *orig = p;
+       bool neg = (*p == '~');
+       bool matched = FALSE;
+       if (neg)
+               p++;
+       if (*p == ']') {
+               p++;
+               matched = (c == ']');
+       }
+       for (; *p != ']'; p++) {
+               if (*p == '\0')
+                       return RANGE_ERROR;     /* bad syntax */
+               if (p[1] == '-' && p[2] != ']') { /* check for [..-..] but ignore [..-] */
+                       if (c >= *p)
+                               matched |= (c <= p[2]);
+                       p += 2;
+               } else {
+                       matched |= (*p == c);
+               }
+       }
+       if (matched ^ neg)
+               return p - orig + 1; /* skip the right-bracket */
+       else
+               return RANGE_FAIL;
+}
diff --git a/usr/src/contrib/rc-1.4/nalloc.c b/usr/src/contrib/rc-1.4/nalloc.c
new file mode 100644 (file)
index 0000000..c88dc39
--- /dev/null
@@ -0,0 +1,137 @@
+/* nalloc.c: a simple single-arena allocator for command-line-lifetime allocation */
+#include "rc.h"
+
+static struct Block {
+       size_t used, size;
+       char *mem;
+       Block *n;
+} *fl, *ul;
+
+/* alignto() works only with power of 2 blocks and assumes 2's complement arithmetic */
+#define alignto(m, n)   ((m + n - 1) & ~(n - 1))
+#define BLOCKSIZE ((size_t) 4096)
+
+/* Allocate a block from the free list or malloc one if none in the fl fit */
+
+static void getblock(size_t n) {
+       Block *r, *p;
+       for (r = fl, p = NULL; r != NULL; p = r, r = r->n)
+               if (n <= r->size)
+                       break;  /* look for a block which fits the request */
+       if (r != NULL) {        /* if one is found, take it off the free list */        
+               if (p != NULL)
+                       p->n = r->n;
+               else
+                       fl = r->n;
+       } else {                /* else allocate a new block */
+               r = enew(Block);
+               r->mem = ealloc(r->size = alignto(n, BLOCKSIZE));
+       }
+       r->used = 0;
+       r->n = ul;
+       ul = r;
+}
+
+/*
+   A fast single-arena allocator. Looks at the current block, and if
+   there is not enough room, it goes to getblock() for more. "ul"
+   stands for "used list", and the head of the list is the current
+   block. "ulp" is a register cache for "ul"; this routine is hacked
+   for speed. (sigh, optimizing RISC compilers still can't cache the
+   address of a global in a register)
+*/
+
+extern void *nalloc(size_t n) {
+       size_t base;
+       Block *ulp;
+        n = alignto(n, sizeof (ALIGN_T));
+       ulp = ul;
+       if (ulp != NULL && n + (base = ulp->used) < ulp->size) {
+               ulp->used = base + n;
+               return &ulp->mem[base];
+       } else {
+               getblock(n); /* assert(ul->used) == 0 */
+               (ulp = ul)->used = n;
+               return &ulp->mem[0];
+       }
+}
+
+/*
+   Frees memory from nalloc space by putting it on the free list.
+   Returns free blocks to the system, retaining at least MAXMEM bytes
+   worth of blocks for nalloc.
+*/
+
+#define MAXMEM 500000
+
+extern void nfree() {
+       size_t count;
+       Block *r;
+       if (ul == NULL)
+               return;
+       for (r = ul; r->n != NULL; r = r->n)
+               ;       /* get to end of used list */
+       r->n = fl;      /* tack free list onto it */
+       fl = ul;        /* and make it the free list */
+       ul = NULL;      /* finally, zero out the used list */
+       for (r = fl, count = r->size; r->n != NULL; r = r->n, count += r->size) {
+               if (count >= MAXMEM) {
+                       Block *tmp = r;
+                       r = r->n;
+                       tmp->n = NULL;          /* terminate the free list */
+                       while (r != NULL) {     /* free memory off the tail of the free list */
+                               tmp = r->n;
+                               efree(r->mem);
+                               efree(r);
+                               r = tmp;
+                       }
+               return;
+               }
+       }
+}
+
+/*
+   "Allocates" a new arena by zeroing out the old one. Up to the
+   calling routine to keep the old value of the block around.
+*/
+
+extern Block *newblock() {
+       Block *old = ul;
+       ul = NULL;
+       return old;
+}
+
+/* "Restores" an arena to its saved value. */
+
+extern void restoreblock(Block *old) {
+       nfree();
+       ul = old;
+}
+
+/* generic memory allocation functions */
+
+extern void *ealloc(size_t n) {
+       extern void *malloc(size_t);
+       void *p = malloc(n);
+       if (p == NULL) {
+               uerror("malloc");
+               rc_exit(1);
+       }
+       return p;
+}
+
+extern void *erealloc(void *p, size_t n) {
+       extern void *realloc(void *, size_t);
+       if ((p = realloc(p, n)) == NULL) {
+               uerror("realloc");
+               rc_exit(1);
+       }
+       return p;
+}
+
+extern void efree(void *p) {
+       extern void free(void *);
+       if (p != NULL)
+               free(p);
+}
+
diff --git a/usr/src/contrib/rc-1.4/open.c b/usr/src/contrib/rc-1.4/open.c
new file mode 100644 (file)
index 0000000..7287e0e
--- /dev/null
@@ -0,0 +1,29 @@
+/* open.c: to insulate <fcntl.h> from the rest of rc. */
+
+#include <fcntl.h>
+#include "rc.h"
+
+/* prototype for open() follows. comment out if necessary */
+
+/*extern int open(const char *, int,...);*/
+
+/*
+   Opens a file with the necessary flags. Assumes the following
+   declaration for redirtype:
+
+       enum redirtype {
+               rFrom, rCreate, rAppend, rHeredoc, rHerestring
+       };
+*/
+
+static const int mode_masks[] = {
+       /* rFrom */     O_RDONLY,
+       /* rCreate */   O_TRUNC | O_CREAT | O_WRONLY,
+       /* rAppend */   O_APPEND | O_CREAT | O_WRONLY
+};
+
+extern int rc_open(const char *name, redirtype m) {
+       if ((unsigned) m >= arraysize(mode_masks))
+               panic("bad mode passed to rc_open");
+       return open(name, mode_masks[m], 0666);
+}
diff --git a/usr/src/contrib/rc-1.4/redir.c b/usr/src/contrib/rc-1.4/redir.c
new file mode 100644 (file)
index 0000000..b582341
--- /dev/null
@@ -0,0 +1,77 @@
+/* redir.c: code for opening files and piping heredocs after fork but before exec. */
+
+#include "rc.h"
+
+/*
+   Walk the redirection queue, and open files and dup2 to them. Also,
+   here-documents are treated here by dumping them down a pipe. (this
+   should make here-documents fast on systems with lots of memory which
+   do pipes right. Under sh, a file is copied to /tmp, and then read
+   out of /tmp again. I'm interested in knowing how much faster, say,
+   shar runs when unpacking when invoked with rc instead of sh. On my
+   sun4/280, it runs in about 60-75% of the time of sh for unpacking
+   the rc source distribution.)
+*/
+
+extern void doredirs() {
+       List *fname;
+       int fd, p[2];
+       Rq *r;
+       for (r = redirq; r != NULL; r = r->n) {
+               switch(r->r->type) {
+               default:
+                       panic("unexpected node in doredirs");
+                       /* NOTREACHED */
+               case nRedir:
+                       if (r->r->u[0].i == rHerestring) {
+                               fname = flatten(glom(r->r->u[2].p)); /* fname is really a string */
+                               if (pipe(p) < 0) {
+                                       uerror("pipe");
+                                       rc_error(NULL);
+                               }
+                               if (rc_fork() == 0) { /* child writes to pipe */
+                                       setsigdefaults(FALSE);
+                                       close(p[0]);
+                                       if (fname != NULL)
+                                               writeall(p[1], fname->w, strlen(fname->w));
+                                       exit(0);
+                               } else {
+                                       close(p[1]);
+                                       if (mvfd(p[0], r->r->u[1].i) < 0)
+                                               rc_error(NULL);
+                               }
+                       } else {
+                               fname = glob(glom(r->r->u[2].p));
+                               if (fname == NULL)
+                                       rc_error("null filename in redirection");
+                               if (fname->n != NULL)
+                                       rc_error("multi-word filename in redirection");
+                               switch (r->r->u[0].i) {
+                               default:
+                                       panic("unexpected node in doredirs");
+                                       /* NOTREACHED */
+                               case rCreate: case rAppend: case rFrom:
+                                       fd = rc_open(fname->w, r->r->u[0].i);
+                                       break;
+                               }
+                               if (fd < 0) {
+                                       uerror(fname->w);
+                                       rc_error(NULL);
+                               }
+                               if (mvfd(fd, r->r->u[1].i) < 0)
+                                       rc_error(NULL);
+                       }
+                       break;
+               case nDup:
+                       if (r->r->u[2].i == -1)
+                               close(r->r->u[1].i);
+                       else if (r->r->u[2].i != r->r->u[1].i) {
+                               if (dup2(r->r->u[2].i, r->r->u[1].i) < 0) {
+                                       uerror("dup2");
+                                       rc_error(NULL);
+                               }
+                       }
+               }
+       }
+       redirq = NULL;
+}
diff --git a/usr/src/contrib/rc-1.4/status.c b/usr/src/contrib/rc-1.4/status.c
new file mode 100644 (file)
index 0000000..bf2dfda
--- /dev/null
@@ -0,0 +1,139 @@
+/* status.c: functions for printing fancy status messages in rc */
+
+#include "rc.h"
+#include "sigmsgs.h"
+
+/* status == the wait() value of the last command in the pipeline, or the last command */
+
+static int statuses[512];
+static int pipelength = 1;
+
+/*
+   Test to see if rc's status is true. According to td, status is true
+   if and only if every pipe-member has an exit status of zero.
+*/
+
+extern int istrue() {
+       int i;
+       for (i = 0; i < pipelength; i++)
+               if (statuses[i] != 0)
+                       return FALSE;
+       return TRUE;
+}
+
+/*
+   Return the status as an integer. A status which has low-bits set is
+   a signal number, whereas a status with high bits set is a value set
+   from exit(). The presence of a signal just sets status to 1. Also,
+   a pipeline with nonzero exit statuses in it just sets status to 1.
+*/
+
+extern int getstatus() {
+       int s;
+       if (pipelength > 1)
+               return !istrue();
+       s = statuses[0];
+       return (s&0xff) ? 1 : (s >> 8) & 0xff;
+}
+
+extern void set(bool code) {
+       setstatus(-1, (!code) << 8); /* exit status 1 == 0x100 */
+}
+
+/* take a pipeline and store the exit statuses. Check to see whether any of the children dumped core */
+
+extern void setpipestatus(int stats[], int num) {
+       int i;
+       for (i = 0; i < (pipelength = num); i++) {
+               statprint(-1, stats[i]);
+               statuses[i] = stats[i];
+       }
+}
+
+/* set a simple status, as opposed to a pipeline */
+
+extern void setstatus(int pid, int i) {
+       pipelength = 1;
+       statuses[0] = i;
+       statprint(pid, i);
+}
+
+/* print a message if termination was with a signal, and if the child dumped core. exit on error if -e is set */
+
+extern void statprint(int pid, int i) {
+       if (i & 0xff) {
+               char *msg = ((i & 0x7f) < NUMOFSIGNALS ? signals[i & 0x7f].msg : "");
+               if (pid != -1)
+                       fprint(2, "%d: ", pid);
+               if (i & 0x80) {
+                       if (*msg == '\0')
+                               fprint(2, "core dumped\n");
+                       else
+                               fprint(2, "%s--core dumped\n", msg);
+               } else if (*msg != '\0')
+                       fprint(2, "%s\n", msg);
+       }
+       if (i != 0 && dashee && !cond)
+               rc_exit(getstatus());
+}
+
+/* prepare a list to be passed back. Used whenever $status is dereferenced */
+
+extern List *sgetstatus() {
+       List *r;
+       int i;
+       for (r = NULL, i = 0; i < pipelength; i++) {
+               List *q = nnew(List);
+               int s = statuses[i];
+               int t;
+               q->n = r;
+               r = q;
+               if ((t = s & 0x7f) != 0) {
+                       const char *core = (s & 0x80) ? "+core" : "";
+                       if (t < NUMOFSIGNALS && *signals[t].name != '\0')
+                               r->w = nprint("%s%s", signals[t].name, core);
+                       else
+                               r->w = nprint("-%d%s", t, core); /* unknown signals are negated */
+               } else
+                       r->w = nprint("%d", (s >> 8) & 0xff);
+               r->m = NULL;
+       }
+       return r;
+}
+
+extern void ssetstatus(char **av) {
+       int i, j, k, l;
+       bool found;
+       for (l = 0; av[l] != NULL; l++)
+               ; /* count up array length */
+       --l;
+       for (i = 0; av[i] != NULL; i++) {
+               j = a2u(av[i]);
+               if (j >= 0) {
+                       statuses[l - i] = j << 8;
+                       continue;
+               }
+               found = FALSE;
+               for (k = 0; k < NUMOFSIGNALS; k++) {
+                       if (streq(signals[k].name, av[i])) {
+                               statuses[l - i] = k;
+                               found = TRUE;
+                               break;
+                       } 
+                       else {
+                               size_t len = strlen(signals[k].name);
+                               if (strncmp(signals[k].name, av[i], len) == 0 && streq(av[i] + len, "+core")) {
+                                       statuses[l - i] = k + 0x80;
+                                       found = TRUE;
+                                       break;
+                               }
+                       }
+               }
+               if (!found) {
+                       fprint(2, "bad status\n");
+                       set(FALSE);
+                       return;
+               }
+       }
+       pipelength = i;
+}
diff --git a/usr/src/contrib/rc-1.4/tree.c b/usr/src/contrib/rc-1.4/tree.c
new file mode 100644 (file)
index 0000000..aaea645
--- /dev/null
@@ -0,0 +1,172 @@
+/* tree.c: functions for manipulating parse-trees. (create, copy, delete) */
+
+#include "rc.h"
+
+/* make a new node, pass it back to yyparse. Used to generate the parsetree. */
+
+extern Node *mk(int /*nodetype*/ t,...) {
+       va_list ap;
+       Node *n;
+       va_start(ap, t);
+       switch (t) {
+       default:
+               panic("unexpected node in mk");
+               /* NOTREACHED */
+       case nDup:
+               n = nalloc(offsetof(Node, u[3]));
+               n->u[0].i = va_arg(ap, int);
+               n->u[1].i = va_arg(ap, int);
+               n->u[2].i = va_arg(ap, int);
+               break;
+       case nWord: case nQword:
+               n = nalloc(offsetof(Node, u[2]));
+               n->u[0].s = va_arg(ap, char *);
+               n->u[1].s = va_arg(ap, char *);
+               break;
+       case nBang: case nNowait:
+       case nCount: case nFlat: case nRmfn: case nSubshell:
+       case nVar: case nCase:
+               n = nalloc(offsetof(Node, u[1]));
+               n->u[0].p = va_arg(ap, Node *);
+               break;
+       case nAndalso: case nAssign: case nBackq: case nBody: case nBrace: case nConcat:
+       case nElse: case nEpilog: case nIf: case nNewfn: case nCbody:
+       case nOrelse: case nPre: case nArgs: case nSwitch:
+       case nMatch: case nVarsub: case nWhile: case nLappend:
+               n = nalloc(offsetof(Node, u[2]));
+               n->u[0].p = va_arg(ap, Node *);
+               n->u[1].p = va_arg(ap, Node *);
+               break;
+       case nForin:
+               n = nalloc(offsetof(Node, u[3]));
+               n->u[0].p = va_arg(ap, Node *);
+               n->u[1].p = va_arg(ap, Node *);
+               n->u[2].p = va_arg(ap, Node *);
+               break;
+       case nPipe:
+               n = nalloc(offsetof(Node, u[4]));
+               n->u[0].i = va_arg(ap, int);
+               n->u[1].i = va_arg(ap, int);
+               n->u[2].p = va_arg(ap, Node *);
+               n->u[3].p = va_arg(ap, Node *);
+               break;
+       case nRedir:
+       case nNmpipe:
+               n = nalloc(offsetof(Node, u[3]));
+               n->u[0].i = va_arg(ap, int);
+               n->u[1].i = va_arg(ap, int);
+               n->u[2].p = va_arg(ap, Node *);
+               break;
+       }
+       n->type = t;
+       va_end(ap);
+       return n;
+}
+
+/* copy a tree to malloc space. Used when storing the definition of a function */
+
+extern Node *treecpy(Node *s, void *(*alloc)(size_t)) {
+       Node *n;
+       if (s == NULL)
+               return NULL;
+       switch (s->type) {
+       default:
+               panic("unexpected node in treecpy");
+               /* NOTREACHED */
+       case nDup:
+               n = (*alloc)(offsetof(Node, u[3]));
+               n->u[0].i = s->u[0].i;
+               n->u[1].i = s->u[1].i;
+               n->u[2].i = s->u[2].i;
+               break;
+       case nWord: case nQword:
+               n = (*alloc)(offsetof(Node, u[2]));
+               n->u[0].s = strcpy((char *) (*alloc)(strlen(s->u[0].s) + 1), s->u[0].s);
+               if (s->u[1].s != NULL) {
+                       size_t i = strlen(s->u[0].s);
+                       n->u[1].s = (*alloc)(i);
+                       memcpy(n->u[1].s, s->u[1].s, i);
+               } else
+                       n->u[1].s = NULL;
+               break;
+       case nBang: case nNowait: case nCase:
+       case nCount: case nFlat: case nRmfn: case nSubshell: case nVar:
+               n = (*alloc)(offsetof(Node, u[1]));
+               n->u[0].p = treecpy(s->u[0].p, alloc);
+               break;
+       case nAndalso: case nAssign: case nBackq: case nBody: case nBrace: case nConcat:
+       case nElse: case nEpilog: case nIf: case nNewfn: case nCbody:
+       case nOrelse: case nPre: case nArgs: case nSwitch:
+       case nMatch: case nVarsub: case nWhile: case nLappend:
+               n = (*alloc)(offsetof(Node, u[2]));
+               n->u[0].p = treecpy(s->u[0].p, alloc);
+               n->u[1].p = treecpy(s->u[1].p, alloc);
+               break;
+       case nForin:
+               n = (*alloc)(offsetof(Node, u[3]));
+               n->u[0].p = treecpy(s->u[0].p, alloc);
+               n->u[1].p = treecpy(s->u[1].p, alloc);
+               n->u[2].p = treecpy(s->u[2].p, alloc);
+               break;
+       case nPipe:
+               n = (*alloc)(offsetof(Node, u[4]));
+               n->u[0].i = s->u[0].i;
+               n->u[1].i = s->u[1].i;
+               n->u[2].p = treecpy(s->u[2].p, alloc);
+               n->u[3].p = treecpy(s->u[3].p, alloc);
+               break;
+       case nRedir:
+       case nNmpipe:
+               n = (*alloc)(offsetof(Node, u[3]));
+               n->u[0].i = s->u[0].i;
+               n->u[1].i = s->u[1].i;
+               n->u[2].p = treecpy(s->u[2].p, alloc);
+               break;
+       }
+       n->type = s->type;
+       return n;
+}
+
+/* free a function definition that is no longer needed */
+
+extern void treefree(Node *s) {
+       if (s == NULL)
+               return;
+       switch (s->type) {
+       default:
+               panic("unexpected node in treefree");
+               /* NOTREACHED */
+       case nDup:
+               break;
+       case nWord: case nQword:
+               efree(s->u[0].s);
+               efree(s->u[1].s);
+               break;
+       case nBang: case nNowait:
+       case nCount: case nFlat: case nRmfn:
+       case nSubshell: case nVar: case nCase:
+               treefree(s->u[0].p);
+               break;
+       case nAndalso: case nAssign: case nBackq: case nBody: case nBrace: case nConcat:
+       case nElse: case nEpilog: case nIf: case nNewfn:
+       case nOrelse: case nPre: case nArgs: case nCbody:
+       case nSwitch: case nMatch:  case nVarsub: case nWhile:
+       case nLappend:
+               treefree(s->u[1].p);
+               treefree(s->u[0].p);
+               break;
+       case nForin:
+               treefree(s->u[2].p);
+               treefree(s->u[1].p);
+               treefree(s->u[0].p);
+               break;
+       case nPipe:
+               treefree(s->u[2].p);
+               treefree(s->u[3].p);
+               break;
+       case nRedir:
+       case nNmpipe:
+               treefree(s->u[2].p);
+       }
+       efree(s);
+}
diff --git a/usr/src/contrib/rc-1.4/utils.c b/usr/src/contrib/rc-1.4/utils.c
new file mode 100644 (file)
index 0000000..52b90e4
--- /dev/null
@@ -0,0 +1,125 @@
+/* utils.c: functions of general utility */
+
+#include <errno.h>
+#include <setjmp.h>
+#include "rc.h"
+#include "jbwrap.h"
+
+/* print error with line number on noninteractive shells (i.e., scripts) */
+
+extern void pr_error(char *s) {
+       if (s != NULL) {
+               if (interactive)
+                       fprint(2, "%s\n", s);
+               else
+                       fprint(2, "line %d: %s\n", lineno - 1, s);
+       }
+}
+
+/* our perror */
+
+extern void uerror(char *s) {
+       extern int sys_nerr;
+       extern char *sys_errlist[];
+       if (errno > sys_nerr)
+               return;
+       if (s != NULL)
+               fprint(2, "%s: %s\n", s, sys_errlist[errno]);
+       else
+               fprint(2, "%s\n", sys_errlist[errno]);
+}
+
+/* Die horribly. This should never get called. Please let me know if it does. */
+
+#define PANICMSG "rc panic: "
+
+extern void panic(char *s) {
+       write(2, PANICMSG, conststrlen(PANICMSG));
+       write(2, s, strlen(s));
+       write(2, "!\n", 2);
+       exit(1);
+}
+
+/* ascii -> unsigned conversion routines. -1 indicates conversion error. */
+
+extern int n2u(char *s, unsigned int base) {
+       unsigned int i;
+       for (i = 0; *s != '\0'; s++) {
+               unsigned int j = (unsigned int) *s - '0';
+               if (j >= base) /* small hack with unsigned ints -- one compare for range test */
+                       return -1;
+               i = i * base + j;
+       }
+       return (int) i;
+}
+
+/* The last word in portable ANSI: a strcmp wrapper for qsort */
+
+extern int starstrcmp(const void *s1, const void *s2) {
+       return strcmp(*(char **)s1, *(char **)s2);
+}
+
+/* tests to see if pathname begins with "/", "./", or "../" */
+
+extern bool isabsolute(char *path) {
+       return path[0] == '/' || (path[0] == '.' && (path[1] == '/' || (path[1] == '.' && path[2] == '/')));
+}
+
+/* signal-safe read and write (for BSD slow devices). writeall also allows partial writes */
+
+extern void writeall(int fd, char *buf, size_t remain) {
+       int i;
+       for (i = 0; remain > 0; buf += i, remain -= i) {
+               interrupt_happened = FALSE;
+               if (!setjmp(slowbuf.j)) {
+                       slow = TRUE;
+                       if (interrupt_happened)
+                               break;
+                       else if ((i = write(fd, buf, remain)) <= 0)
+                               break; /* abort silently on errors in write() */
+               } else
+                       break;
+               slow = FALSE;
+       }
+       slow = FALSE;
+       SIGCHK;
+}
+
+extern int rc_read(int fd, char *buf, size_t n) {
+       long /*ssize_t*/ r;
+       interrupt_happened = FALSE;
+       if (!setjmp(slowbuf.j)) {
+               slow = TRUE;
+               if (!interrupt_happened)
+                       r = read(fd, buf, n);
+               else
+                       r = -2;
+       } else
+               r = -2;
+       slow = FALSE;
+       if (r == -2) {
+               errno = EINTR;
+               r = -1;
+       }
+       SIGCHK;
+       return r;
+}
+
+/* clear out z bytes from character string s */
+
+extern char *clear(char *s, size_t z) {
+       while (z != 0)
+               s[--z] = 0;
+       return s;
+}
+
+/* duplicate a fd and close the old one only if necessary */
+
+extern int mvfd(int i, int j) {
+       if (i != j) {
+               int s = dup2(i, j);
+               close(i);
+               return s;
+       }
+       return 0;
+}