commit ed998daafc3b46899a4c59419a9ac584d3a5e883
parent 960afa1642f23d71c07be587ccf25c1a41b2fe98
Author: tim <tim>
Date: Wed, 14 May 1997 16:24:26 +0000
Initial revision
Diffstat:
A | glom.c | | | 426 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1 file changed, 426 insertions(+), 0 deletions(-)
diff --git a/glom.c b/glom.c
@@ -0,0 +1,426 @@
+/* glom.c: builds an argument list out of words, variables, etc. */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <errno.h>
+#include "rc.h"
+#if !defined(S_IFIFO) && !defined(DEVFD)
+#define NOCMDARG
+#endif
+
+static List *backq(Node *, Node *);
+static List *bqinput(List *, int);
+static List *count(List *);
+static List *mkcmdarg(Node *);
+
+Rq *redirq = NULL;
+
+extern List *word(char *w, char *m) {
+ List *s = NULL;
+ if (w != NULL) {
+ s = nnew(List);
+ s->w = w;
+ s->m = m;
+ s->n = NULL;
+ }
+ return s;
+}
+
+/*
+ Append list s2 to list s1 by copying s1 and making the new copy
+ point at s2.
+*/
+
+extern List *append(List *s1, List *s2) {
+ List *r, *top;
+ if (s1 == NULL)
+ return s2;
+ if (s2 == NULL)
+ return s1;
+ for (r = top = nnew(List); 1; r = r->n = nnew(List)) {
+ r->w = s1->w;
+ r->m = s1->m;
+ if ((s1 = s1->n) == NULL)
+ break;
+ }
+ r->n = s2;
+ return top;
+}
+
+extern List *concat(List *s1, List *s2) {
+ int n1, n2;
+ List *r, *top;
+ if (s1 == NULL)
+ return s2;
+ if (s2 == NULL)
+ return s1;
+ if ((n1 = listnel(s1)) != (n2 = listnel(s2)) && n1 != 1 && n2 != 1)
+ rc_error("bad concatenation");
+ for (r = top = nnew(List); 1; r = r->n = nnew(List)) {
+ SIZE_T x = strlen(s1->w);
+ SIZE_T y = strlen(s2->w);
+ SIZE_T z = x + y + 1;
+ r->w = nalloc(z);
+ strcpy(r->w, s1->w);
+ strcat(r->w, s2->w);
+ if (s1->m == NULL && s2->m == NULL) {
+ r->m = NULL;
+ } else {
+ r->m = nalloc(z);
+ if (s1->m == NULL)
+ memzero(r->m, x);
+ else
+ memcpy(r->m, s1->m, x);
+ if (s2->m == NULL)
+ memzero(&r->m[x], y);
+ else
+ memcpy(&r->m[x], s2->m, y);
+ r->m[z] = 0;
+ }
+ if (n1 > 1)
+ s1 = s1->n;
+ if (n2 > 1)
+ s2 = s2->n;
+ if (s1 == NULL || s2 == NULL || (n1 == 1 && n2 == 1))
+ break;
+ }
+ r->n = NULL;
+ return top;
+}
+
+extern List *varsub(List *var, List *subs) {
+ List *r, *top;
+ int n = listnel(var);
+ for (top = r = NULL; subs != NULL; subs = subs->n) {
+ int i = a2u(subs->w);
+ if (i < 1)
+ rc_error("bad subscript");
+ if (i <= n) {
+ List *sub = var;
+ while (--i)
+ sub = sub->n; /* loop until sub == var(i) */
+ if (top == NULL)
+ top = r = nnew(List);
+ else
+ r = r->n = nnew(List);
+ r->w = sub->w;
+ r->m = sub->m;
+ }
+ }
+ if (top != NULL)
+ r->n = NULL;
+ return top;
+}
+
+extern List *flatten(List *s) {
+ List *r;
+ SIZE_T step;
+ char *f;
+ if (s == NULL || s->n == NULL)
+ return s;
+ r = nnew(List);
+ f = r->w = nalloc(listlen(s) + 1);
+ r->m = NULL; /* flattened lists come from variables, so no meta */
+ r->n = NULL;
+ strcpy(f, s->w);
+ f += strlen(s->w);
+ do {
+ *f++ = ' ';
+ s = s->n;
+ step = strlen(s->w);
+ memcpy(f, s->w, step);
+ f += step;
+ } while (s->n != NULL);
+ *f = '\0';
+ return r;
+}
+
+static List *count(List *l) {
+ List *s = nnew(List);
+ s->w = nprint("%d", listnel(l));
+ s->n = NULL;
+ s->m = NULL;
+ return s;
+}
+
+extern void assign(List *s1, List *s2, bool stack) {
+ List *val = s2;
+ if (s1 == NULL)
+ rc_error("null variable name");
+ if (s1->n != NULL)
+ rc_error("multi-word variable name");
+ if (*s1->w == '\0')
+ rc_error("zero-length variable name");
+ if (a2u(s1->w) != -1)
+ rc_error("numeric variable name");
+ if (strchr(s1->w, '=') != NULL)
+ rc_error("'=' in variable name");
+ if (*s1->w == '*' && s1->w[1] == '\0')
+ val = append(varlookup("0"), s2); /* preserve $0 when * is assigned explicitly */
+ if (s2 != NULL || stack) {
+ if (dashex)
+ prettyprint_var(2, s1->w, val);
+ varassign(s1->w, val, stack);
+ alias(s1->w, varlookup(s1->w), stack);
+ } else {
+ if (dashex)
+ prettyprint_var(2, s1->w, NULL);
+ varrm(s1->w, stack);
+ }
+}
+
+/*
+ The following two functions are by the courtesy of Paul Haahr,
+ who could not stand the incompetence of my own backquote implementation.
+*/
+
+#define BUFSIZE ((SIZE_T) 1000)
+
+static List *bqinput(List *ifs, int fd) {
+ char *end, *bufend, *s;
+ List *r, *top, *prev;
+ SIZE_T remain, bufsize;
+ char isifs[256];
+ int n, state; /* a simple FSA is used to read in data */
+
+ memzero(isifs, sizeof isifs);
+ for (isifs['\0'] = TRUE; ifs != NULL; ifs = ifs->n)
+ for (s = ifs->w; *s != '\0'; s++)
+ isifs[*(unsigned char *)s] = TRUE;
+ remain = bufsize = BUFSIZE;
+ top = r = nnew(List);
+ r->w = end = nalloc(bufsize + 1);
+ r->m = NULL;
+ state = 0;
+ prev = NULL;
+
+ while (1) {
+ if (remain == 0) { /* is the string bigger than the buffer? */
+ SIZE_T m = end - r->w;
+ char *buf;
+ while (bufsize < m + BUFSIZE)
+ bufsize *= 2;
+ buf = nalloc(bufsize + 1);
+ memcpy(buf, r->w, m);
+ r->w = buf;
+ end = &buf[m];
+ remain = bufsize - m;
+ }
+ if ((n = rc_read(fd, end, remain)) <= 0) {
+ if (n == 0)
+ /* break */ break;
+ else if (errno == EINTR)
+ return NULL; /* interrupted, wait for subproc */
+ else {
+ uerror("backquote read");
+ rc_error(NULL);
+ }
+ }
+ remain -= n;
+ for (bufend = &end[n]; end < bufend; end++)
+ if (state == 0) {
+ if (!isifs[*(unsigned char *)end]) {
+ state = 1;
+ r->w = end;
+ r->m = NULL;
+ }
+ } else {
+ if (isifs[*(unsigned char *)end]) {
+ state = 0;
+ *end = '\0';
+ prev = r;
+ r = r->n = nnew(List);
+ r->w = end+1;
+ r->m = NULL;
+ }
+ }
+ }
+ if (state == 1) { /* terminate last string */
+ *end = '\0';
+ r->n = NULL;
+ } else {
+ if (prev == NULL) /* no input at all? */
+ top = NULL;
+ else
+ prev->n = NULL; /* else terminate list */
+ }
+ return top;
+}
+
+static List *backq(Node *ifs, Node *n) {
+ int p[2], pid, sp;
+ List *bq;
+ if (n == NULL)
+ return NULL;
+ if (pipe(p) < 0) {
+ uerror("pipe");
+ rc_error(NULL);
+ }
+ if ((pid = rc_fork()) == 0) {
+ mvfd(p[1], 1);
+ close(p[0]);
+ redirq = NULL;
+ walk(n, FALSE);
+ exit(getstatus());
+ }
+ close(p[1]);
+ bq = bqinput(glom(ifs), p[0]);
+ close(p[0]);
+ rc_wait4(pid, &sp, TRUE);
+ statprint(-1, sp);
+ varassign("bqstatus", word(strstatus(sp), NULL), FALSE);
+ sigchk();
+ return bq;
+}
+
+extern void qredir(Node *n) {
+ Rq *next;
+ if (redirq == NULL) {
+ next = redirq = nnew(Rq);
+ } else {
+ for (next = redirq; next->n != NULL; next = next->n)
+ ;
+ next->n = nnew(Rq);
+ next = next->n;
+ }
+ next->r = n;
+ next->n = NULL;
+}
+
+#ifdef NOCMDARG
+static List *mkcmdarg(Node *n) {
+ rc_error("named pipes are not supported");
+ return NULL;
+}
+#else
+#ifndef DEVFD
+static List *mkcmdarg(Node *n) {
+ int fd;
+ char *name;
+ Edata efifo;
+ Estack *e = enew(Estack);
+ List *ret = nnew(List);
+ static int fifonumber = 0;
+ name = nprint("%s/rc%d.%d", TMPDIR, getpid(), fifonumber++);
+ if (mknod(name, S_IFIFO | 0666, 0) < 0) {
+ uerror("mknod");
+ return NULL;
+ }
+ if (rc_fork() == 0) {
+ setsigdefaults(FALSE);
+ fd = rc_open(name, (n->u[0].i != rFrom) ? rFrom : rCreate); /* stupid hack */
+ if (fd < 0) {
+ uerror("open");
+ exit(1);
+ }
+ if (mvfd(fd, (n->u[0].i == rFrom)) < 0) /* same stupid hack */
+ exit(1);
+ redirq = NULL;
+ walk(n->u[2].p, FALSE);
+ exit(getstatus());
+ }
+ efifo.name = name;
+ except(eFifo, efifo, e);
+ ret->w = name;
+ ret->m = NULL;
+ ret->n = NULL;
+ return ret;
+}
+#else
+static List *mkcmdarg(Node *n) {
+ char *name;
+ List *ret = nnew(List);
+ Estack *e = nnew(Estack);
+ Edata efd;
+ int p[2];
+ if (pipe(p) < 0) {
+ uerror("pipe");
+ return NULL;
+ }
+ if (rc_fork() == 0) {
+ setsigdefaults(FALSE);
+ if (mvfd(p[n->u[0].i == rFrom], n->u[0].i == rFrom) < 0) /* stupid hack */
+ exit(1);
+ close(p[n->u[0].i != rFrom]);
+ redirq = NULL;
+ walk(n->u[2].p, FALSE);
+ exit(getstatus());
+ }
+ name = nprint("/dev/fd/%d", p[n->u[0].i != rFrom]);
+ efd.fd = p[n->u[0].i != rFrom];
+ except(eFd, efd, e);
+ close(p[n->u[0].i == rFrom]);
+ ret->w = name;
+ ret->m = NULL;
+ ret->n = NULL;
+ return ret;
+}
+#endif /* DEVFD */
+#endif /* !NOCMDARG */
+
+extern List *glom(Node *n) {
+ List *v, *head, *tail;
+ Node *words;
+ if (n == NULL)
+ return NULL;
+ switch (n->type) {
+ case nArgs:
+ case nLappend:
+ words = n->u[0].p;
+ tail = NULL;
+ while (words != NULL && (words->type == nArgs || words->type == nLappend)) {
+ if (words->u[1].p != NULL && words->u[1].p->type != nWord && words->u[1].p->type != nQword)
+ break;
+ head = glom(words->u[1].p);
+ if (head != NULL) {
+ head->n = tail;
+ tail = head;
+ }
+ words = words->u[0].p;
+ }
+ v = append(glom(words), tail); /* force left to right evaluation */
+ return append(v, glom(n->u[1].p));
+ case nBackq:
+ return backq(n->u[0].p, n->u[1].p);
+ case nConcat:
+ head = glom(n->u[0].p); /* force left-to-right evaluation */
+ return concat(head, glom(n->u[1].p));
+ case nDup:
+ case nRedir:
+ qredir(n);
+ return NULL;
+ case nWord:
+ case nQword:
+ return word(n->u[0].s, n->u[1].s);
+ case nNmpipe:
+ return mkcmdarg(n);
+ default:
+ /*
+ The next four operations depend on the left-child of glom
+ to be a variable name. Therefore the variable is looked up
+ here.
+ */
+ if ((v = glom(n->u[0].p)) == NULL)
+ rc_error("null variable name");
+ if (v->n != NULL)
+ rc_error("multi-word variable name");
+ if (*v->w == '\0')
+ rc_error("zero-length variable name");
+ v = (*v->w == '*' && v->w[1] == '\0') ? varlookup(v->w)->n : varlookup(v->w);
+ switch (n->type) {
+ default:
+ panic("unexpected node in glom");
+ exit(1);
+ /* NOTREACHED */
+ case nCount:
+ return count(v);
+ case nFlat:
+ return flatten(v);
+ case nVar:
+ return v;
+ case nVarsub:
+ return varsub(v, glom(n->u[1].p));
+ }
+ }
+}