byron@archone.tamu.edu (Byron Rakitzis) (05/22/91)
Submitted-by: Byron Rakitzis <byron@archone.tamu.edu> Posting-number: Volume 20, Issue 11 Archive-name: rc/part02 #! /bin/sh # This is a shell archive. Remove anything before this line, then feed it # into a shell via "sh file" or similar. To overwrite existing files, # type "sh file -c". # The tool that generated this appeared in the comp.sources.unix newsgroup; # send mail to comp-sources-unix@uunet.uu.net if you want that tool. # Contents: fn.c footobar.c glom.c input.c lex.c walk.c # Wrapped by kent@sparky on Wed May 22 01:21:49 1991 PATH=/bin:/usr/bin:/usr/ucb ; export PATH echo If this archive is complete, you will see the following message: echo ' "shar: End of archive 2 (of 4)."' if test -f 'fn.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'fn.c'\" else echo shar: Extracting \"'fn.c'\" \(4435 characters\) sed "s/^X//" >'fn.c' <<'END_OF_FILE' X/* X fn.c: functions for adding and deleting functions from the symbol table. X Support for signal handlers is also found here. X*/ X X#include <signal.h> X#include "rc.h" X#include "utils.h" X#include "status.h" X#include "tree.h" X#include "hash.h" X#include "footobar.h" X#include "walk.h" X#include "nalloc.h" X#include "sigmsgs.h" X#include "builtins.h" X#include "input.h" X Xstatic void fn_handler(int); X Xstatic Node *handlers[NUMOFSIGNALS], null_function; Xstatic boolean runexit = FALSE; X X/* set signals to default values for rc. this means that interactive shells ignore SIGQUIT, etc. */ X Xvoid inithandler() { X if (interactive) { X signal(SIGINT, sig); X handlers[SIGINT] = &null_function; X if (!dashdee) { X signal(SIGQUIT, SIG_IGN); X handlers[SIGQUIT] = &null_function; X } X } X null_function.type = BODY; X null_function.u[0].p = null_function.u[1].p = NULL; X} X X/* only run this in a child process! resets signals to their default values */ X Xvoid setsigdefaults(void) { X int i; X X /* X General housekeeping: (setsigdefaults happens after fork(), so it's a convenient X place to clean up) X */ X X interactive = FALSE; X if (histstr != NULL) /* Close an open history file */ X close(histfd); X X /* Restore signals to SIG_DFL */ X X for (i = 1; i < NUMOFSIGNALS; i++) { /* signal 0 is never used (bogus) */ X if (handlers[i] != NULL) { X handlers[i] = NULL; X signal(i, SIG_DFL); X } X } X runexit = FALSE; /* No sigexit on subshells */ X} X X/* rc's exit. if runexit is set, run the sigexit function. */ X Xvoid rc_exit(int stat) { X static char *sigexit[2] = { "sigexit", NULL }; X X if (runexit) { X runexit = FALSE; X funcall(sigexit); X exit(getstatus()); X } else { X exit(stat); X } X} X X/* the signal handler for all functions. calls walk() */ X Xstatic void fn_handler(int s) { X if (s < 0 || s >= NUMOFSIGNALS) X rc_error("unknown signal!?"); X X signal(s, fn_handler); /* sgi seems to require re-signalling */ X walk(handlers[s], TRUE); X} X X/* X assign a function in Node form. Check to see if the function is also a signal, and set the X signal vectors appropriately. X*/ X Xvoid fnassign(char *name, Node *def) { X Node *newdef = treecpy(def == NULL ? &null_function : def, ealloc); /* important to do the treecopy first */ X Function *new = get_fn_place(name); X int i; X X new->def = newdef; X new->extdef = NULL; X X if (strncmp(name, "sig", sizeof "sig" - 3) == 0) { /* slight optimization */ X if (streq(name, "sigexit")) X runexit = TRUE; X for (i = 1; i < NUMOFSIGNALS; i++) /* zero is a bogus signal */ X if (streq(signals[i][0], name)) { X if (newdef != NULL) { X handlers[i] = newdef; X signal(i, fn_handler); X } else { X handlers[i] = &null_function; X signal(i, SIG_IGN); X } X break; X } X } X} X X/* assign a function from the environment. store just the external representation */ X Xvoid fnassign_string(char *extdef) { X char *name = get_name(extdef+3); /* +3 to skip over "fn_" */ X Function *new; X X if (name == NULL) X return; X X new = get_fn_place(name); X new->def = NULL; X new->extdef = ecpy(extdef); X} X X/* return a function in Node form, evaluating an entry from the environment if necessary */ X XNode *fnlookup(char *name) { X Function *look = lookup_fn(name); X Node *ret; X X if (look == NULL) X return NULL; /* not found */ X if (look->def != NULL) X return look->def; X if (look->extdef == NULL) /* function was set to null, e.g., fn foo {} */ X return &null_function; X X ret = parse_fn(name, look->extdef); X X if (ret == NULL) { X efree(look->extdef); X look->extdef = NULL; X return &null_function; X } else { X return look->def = treecpy(ret, ealloc); /* Need to take it out of talloc space */ X } X} X X/* return a function in string form (used by makeenv) */ X Xchar *fnlookup_string(char *name) { X Function *look = lookup_fn(name); X X if (look == NULL) X return NULL; X if (look->extdef != NULL) X return look->extdef; X return look->extdef = fun2str(name, look->def); X} X X/* X remove a function from the symbol table. If it also defines a signal handler, restore the signal handler X to its default value. X*/ X Xvoid fnrm(char *name) { X int i; X X for (i = 1; i < NUMOFSIGNALS; i++) /* signal 0 unused */ X if (streq(signals[i][0], name)) { X handlers[i] = NULL; X if (i == SIGINT) X signal(i, sig); /* restore default signal handler for rc */ X else if (i == SIGQUIT && !dashdee) X signal(i, SIG_IGN); /* ditto */ X else X signal(i, SIG_DFL); X } X X if (streq(name, "sigexit")) X runexit = FALSE; X X delete_fn(name); X} END_OF_FILE if test 4435 -ne `wc -c <'fn.c'`; then echo shar: \"'fn.c'\" unpacked with wrong size! fi # end of 'fn.c' fi if test -f 'footobar.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'footobar.c'\" else echo shar: Extracting \"'footobar.c'\" \(7836 characters\) sed "s/^X//" >'footobar.c' <<'END_OF_FILE' X/* X footobar.c: a collection of functions to convert internal representations of X variables and functions to external representations, and vice versa X*/ X X#include "rc.h" X#include "utils.h" X#include "lex.h" X#include "footobar.h" X#include "nalloc.h" X#include "input.h" X#include "list.h" X X#define FSCHAR '\1' X#define FSSTRING "\1" X Xstatic char *getenvw(char *, boolean); Xstatic void funcat(char *); Xstatic void strtree(Node *); X Xstatic char *fun; Xstatic SIZE_T funsize, funpos; X X/* a specialized strcat, used in strtree */ X Xstatic void funcat(char *s) { X SIZE_T l = strlen(s); X char *new; X X if (l + funpos > funsize) { X new = nalloc(funsize *= 2); X memcpy(new, fun, funpos); X new[funpos] = 0; X fun = new; X } X strcpy(fun + funpos, s); X funpos += l; X} X X/* used to turn a function in Node * form into something we can export to the environment */ X Xchar *fun2str(char *name, Node *s) { X fun = nalloc(funsize = 512); X funpos = 0; X funcat("fn_"); X funcat(name); X funcat("={"); X strtree(s); X funcat("}"); X return ecpy(fun); /* put into malloc space */ X} X X/* ptree is used by whatis in order to print the definition of a function to the terminal */ X Xchar *ptree(Node *s) { X fun = nalloc(funsize = 512); X funpos = 0; X fun[0] = 0; X strtree(s); X return fun; X} X X/* save some code space by gathering this operation in a function */ X Xstatic void catredir(int i) { X switch (i) { X case CREATE: funcat(">"); break; X case APPEND: funcat(">>"); break; X case HEREDOC: funcat("<<"); break; X case HERESTRING: funcat("<<<"); break; X case FROM: funcat("<"); break; X } X} X X/* convert a function in Node * form into something rc can parse (and humans can read?) */ X Xstatic void strtree(Node *n) { X int defaultfd; X char b[16]; X X if (n == NULL) { X funcat("()"); X return; X } X X switch (n->type) { X case rDUP: X catredir(n->u[0].i); X if (n->u[2].i != -1) X sprint(b, "[%d=%d]", n->u[1].i, n->u[2].i); X else X sprint(b, "[%d=]", n->u[1].i); X funcat(b); X break; X case rWORD: X if (*n->u[0].s == '\'') X funcat(strprint(n->u[0].s + 1, TRUE, FALSE)); X else X funcat(strprint(n->u[0].s, FALSE, FALSE)); X break; X case BACKQ: X if (n->u[0].p != NULL && n->u[0].p->type == VAR X && n->u[0].p->u[0].p != NULL && n->u[0].p->u[0].p->type == rWORD X && streq(n->u[0].p->u[0].p->u[0].s,"ifs")) { X funcat("`{"); X } else { X funcat("``"); X strtree(n->u[0].p); X funcat("{"); X } X strtree(n->u[1].p); X funcat("}"); X break; X case rBANG: X funcat("! "); X strtree(n->u[0].p); X break; X case NOWAIT: X strtree(n->u[0].p); X funcat("&"); X break; X case rCOUNT: X funcat("$#"); X strtree(n->u[0].p); X break; X case rFLAT: X funcat("$^"); X strtree(n->u[0].p); X break; X case RMFN: X funcat("fn "); X strtree(n->u[0].p); X break; X case rSUBSHELL: X funcat("@ "); X strtree(n->u[0].p); X break; X case VAR: X funcat("$"); X strtree(n->u[0].p); X break; X case rANDAND: X strtree(n->u[0].p); X funcat("&&"); X strtree(n->u[1].p); X break; X case ASSIGN: X strtree(n->u[0].p); X funcat("="); X strtree(n->u[1].p); X break; X case BODY: X if (n->u[1].p != NULL) X funcat("{"); X strtree(n->u[0].p); X if (n->u[1].p != NULL) { X funcat(";"); X strtree(n->u[1].p); X funcat("}"); X } X break; X case BRACE: X funcat("{"); X strtree(n->u[0].p); X funcat("}"); X strtree(n->u[1].p); X break; X case CONCAT: X strtree(n->u[0].p); X funcat("^"); X strtree(n->u[1].p); X break; X case rELSE: X funcat("{"); X strtree(n->u[0].p); X funcat("}else "); X strtree(n->u[1].p); X break; X case EPILOG: X case PRE: X strtree(n->u[0].p); X funcat(" "); X strtree(n->u[1].p); X break; X case NEWFN: X funcat("fn "); X strtree(n->u[0].p); X funcat(" {"); X strtree(n->u[1].p); X funcat("}"); X break; X case rIF: X funcat("if("); X strtree(n->u[0].p); X funcat(")"); X strtree(n->u[1].p); X break; X case rOROR: X strtree(n->u[0].p); X funcat("||"); X strtree(n->u[1].p); X break; X case ARGS: X strtree(n->u[0].p); X funcat(" "); X strtree(n->u[1].p); X break; X case rSWITCH: X funcat("switch("); X strtree(n->u[0].p); X funcat(")"); X strtree(n->u[1].p); X break; X case MATCH: X funcat("~ "); X strtree(n->u[0].p); X funcat(" "); X strtree(n->u[1].p); X break; X case VARSUB: X funcat("$"); X strtree(n->u[0].p); X funcat("("); X strtree(n->u[1].p); X funcat(")"); X break; X case rWHILE: X funcat("while("); X strtree(n->u[0].p); X funcat(")"); X strtree(n->u[1].p); X break; X case LAPPEND: X funcat("("); X strtree(n->u[0].p); X funcat(" "); X strtree(n->u[1].p); X funcat(")"); X break; X case FORIN: X funcat("for("); X strtree(n->u[0].p); X funcat(" in "); X strtree(n->u[1].p); X funcat(")"); X strtree(n->u[2].p); X break; X case rPIPE: X funcat("{"); X strtree(n->u[2].p); X if (n->u[0].i == 1) { X if (n->u[1].i == 0) X sprint(b, "}|{"); X else X sprint(b, "}|[1=%d]{", n->u[1].p); X } else { X if (n->u[1].i == 0) X sprint(b, "}|[%d]{", n->u[0].p); X else X sprint(b, "}|[%d=%d]{", n->u[0].i, n->u[1].i); X } X funcat(b); X strtree(n->u[3].p); X funcat("}"); X break; X case NMPIPE: X catredir(n->u[0].i); X if (n->u[1].i != 1) { X sprint(b, "[%d]{", n->u[1].i); X funcat(b); X } else X funcat("{"); X strtree(n->u[2].p); X funcat("}"); X break; X case rREDIR: X defaultfd = (n->u[0].i == CREATE || n->u[0].i == APPEND); X catredir(n->u[0].i); X if (n->u[1].i != defaultfd) { X sprint(b, "[%d]", n->u[1].i); X funcat(b); X } X strtree(n->u[2].p); X break; X } X} X X/* convert a List to a string, separating it with ^A characters. Used for exporting variables to the environment */ X Xchar *list2str(char *name, List *s) { X SIZE_T size; X List *t; X char *w; X X size = listlen(s); X size += strlen(name); X X w = ealloc(size + 2); X t = s; X strcpy(w, name); X strcat(w, "="); X strcat(w, t->w); X for (s = s->n; s != NULL; s = s->n) { X strcat(w, FSSTRING); X strcat(w, s->w); X } X return w; X} X X/* convert a List to an array, for execve() */ X Xchar **list2array(List *s, boolean print) { X char **av; X int i; X X av = nalloc((listnel(s) + 1) * sizeof (char *)); X X for (i = 0; s != NULL; i++) { X av[i] = s->w; X if (print) X fprint(2,"%s",s->w); X s = s->n; X if (print) { X if (s == NULL) X fprint(2,"\n"); X else X fprint(2," "); X } X } X av[i] = NULL; X return av; X} X X/* figure out the name of a variable given an environment string. copy this into malloc space */ X Xchar *get_name(char *s) { X char *r; X SIZE_T i; X X for (i = 0; s[i] != '\0' && s[i] != '='; i++) X ; X X if (s[i] == '\0') X return NULL; X X r = ealloc(i + 1); X X r[i] = '\0'; X X while (i > 0) { X --i; X r[i] = s[i]; X } X return r; X} X X/* get the next word from a variable's value as represented in the environment. */ X Xstatic char *getenvw(char *s, boolean saw_alpha) { X char *r; X SIZE_T i,j; X X for (i = j = 0; s[i] != '\0' && s[i] != FSCHAR; i++) X ; X X if (i == 0) X if(s[i] == '\0' && !saw_alpha) X return NULL; X else { X r = enew(char); X *r = '\0'; X return r; X } X X r = ealloc(i + j + 1); X X r[i + j] = '\0'; X X while (i > 0) { X --i; X r[i + j] = s[i]; X } X return r; X} X X/* take an environment entry for a variable (elements ^A separated) and turn it into a List */ X XList *parse_var(char *name, char *extdef) { X List *r, *top = r; X char *f; X boolean saw_alpha; X X top = r = enew(List); X extdef += strlen(name) + 1; X X X if ((f = getenvw(extdef, FALSE)) == NULL) X return NULL; X X while (1) { X r->w = f; X r->m = NULL; X extdef += strlen(f); X if (*extdef == FSCHAR) { X extdef++; X saw_alpha = TRUE; X } else { X saw_alpha = FALSE; X } X if ((f = getenvw(extdef, saw_alpha)) == NULL) { X r->n = NULL; X break; X } X r = r->n = enew(List); X } X return top; X} X X/* get an environment entry for a function and have rc parse it. */ X XNode *parse_fn(char *name, char *extdef) { X Node *def; X int len; X X len = strlen(name); X extdef[2] = ' '; X extdef[3 + len] = ' '; X def = parseline(extdef); X extdef[2] = '_'; X extdef[3 + len] = '='; X X if (def == NULL || def->type != NEWFN) X return NULL; X else X return def->u[1].p; X} END_OF_FILE if test 7836 -ne `wc -c <'footobar.c'`; then echo shar: \"'footobar.c'\" unpacked with wrong size! fi # end of 'footobar.c' fi if test -f 'glom.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'glom.c'\" else echo shar: Extracting \"'glom.c'\" \(8367 characters\) sed "s/^X//" >'glom.c' <<'END_OF_FILE' X/* glom.c: builds an argument list out of words, variables, etc. */ X X#ifndef NONMPIPES X#include <sys/types.h> X#include <sys/stat.h> X#endif X#include "rc.h" X#include "utils.h" X#include "nalloc.h" X#include "glom.h" X#include "hash.h" X#include "walk.h" X#include "status.h" X#include "exec.h" X#include "lex.h" X#include "open.h" X#include "list.h" X Xstatic List *backq(Node *, Node *); Xstatic List *bqinput(List *, int); Xstatic List *count(List *); Xstatic List *mknmpipe(Node *); X XRq *redirq = NULL; XList *fifoq = NULL; X XList *word(char *w, char *m) { X List *s; X X if (w == NULL) X return NULL; X if (*w == '\'') { X m = NULL; /* m is null, or had better be! */ X w++; X } X X s = nnew(List); X s->w = w; X s->m = m; X s->n = NULL; X return s; X} X X/* X Append list s2 to list s1 by copying s1 and making the new copy X point at s2. X*/ X XList *append(List *s1, List *s2) { X List *r, *top; X X if (s1 == NULL) X return s2; X if (s2 == NULL) X return s1; X X r = top = nnew(List); X while (1) { X r->w = s1->w; X r->m = s1->m; X if ((s1 = s1->n) == NULL) X break; X r = r->n = nnew(List); X } X X r->n = s2; X X return top; X} X XList *concat(List *s1, List *s2) { X int n1, n2; X SIZE_T y,z; X List *n, *s; X X if (s1 == NULL) X return s2; X if (s2 == NULL) X return s1; X X n1 = listnel(s1); X n2 = listnel(s2); X X if (n1 != n2 && n1 != 1 && n2 != 1) X rc_error("bad concatenation"); X X n = s = nnew(List); X X while (1) { X z = strlen(s1->w) + strlen(s2->w) + 1; X n->w = nalloc(z); X strcpy(n->w,s1->w); X strcat(n->w,s2->w); X if (s1->m == NULL && s2->m == NULL) { X n->m = NULL; X } else { X n->m = nalloc(z); X y = strlen(s1->w); X if (s1->m == NULL) X clear(n->m, y); X else X memcpy(n->m, s1->m, y); X if (s2->m == NULL) X clear(n->m + y, strlen(s2->w)); X else X memcpy(n->m + y, s2->m, strlen(s2->w)); X n->m[z] = 0; X } X if (n1 > 1) X s1 = s1->n; X if (n2 > 1) X s2 = s2->n; X if (s1 == NULL || s2 == NULL || (n1 == 1 && n2 == 1)) { X n->n = NULL; X return s; X } X n->n = nnew(List); X n = n->n; X } X} X XList *varsub(List *v, List *subs) { X int i,j; X int n; X List *r,*s; X List *top,*cat; X X n = listnel(v); X X top = cat = NULL; X X for (s = subs; s != NULL; s = s->n) { X i = a2u(s->w); X if (i < 1) X rc_error("bad subscript"); X if (i <= n) { X for (j = 1, r = v; j != i; j++, r = r->n) X ; /* loop until r == v(i) */ X if (top == NULL) { X top = cat = nnew(List); X } else { X cat->n = nnew(List); X cat = cat->n; X } X cat->w = r->w; X cat->m = r->m; X } X } X X if (top == NULL) X return NULL; X X cat->n = NULL; X return top; X} X XList *flatten(List *s) { X List *r; X X if (s == NULL || s->n == NULL) X return s; X X r = nnew(List); X r->w = nalloc(listlen(s) + 1); X r->m = NULL; /* flattened lists come from variables, so no meta */ X r->n = NULL; X X strcpy(r->w, s->w); X X do { X s = s->n; X strcat(r->w, " "); X strcat(r->w, s->w); X } while (s->n != NULL); X X return r; X} X Xstatic List *count(List *l) { X List *s = nnew(List); X char buf[16]; X X s->w = ncpy(sprint(buf, "%d", listnel(l))); X s->n = NULL; X s->m = NULL; X return s; X} X Xvoid assign(List *s1, List *s2, boolean stack) { X List *val = s2; X X if (s1 == NULL) X rc_error("null variable name"); X if (s1->n != NULL) X rc_error("multi-word variable name"); X if (*s1->w == '\0') X rc_error("zero-length variable name"); X if (a2u(s1->w) != -1) X rc_error("numeric variable name"); X if (s1->w[0] == '*' && s1->w[1] == '\0') X val = append(varlookup("0"), s2); /* preserve $0 when * is assigned explicitly */ X X if (s2 != NULL || stack) { X varassign(s1->w, val, stack); X alias(s1->w, val, stack); X } else X varrm(s1->w, stack); X} X X/* X The following two functions are by the courtesy of Paul Haahr, X who could not stand the incompetence of my own backquote implementation. X*/ X X#define BUFSIZE ((SIZE_T) 1000) X Xstatic List *bqinput(List *list, int fd) { X char *s, *bufend; X List *lp, *prev; X SIZE_T nremain, bufsize; X static char isifs[256]; X X clear(isifs, sizeof isifs); X isifs['\0'] = 1; X for (lp = list; list != NULL; list = list->n) X for (s = list->w; *s != '\0'; s++) X isifs[*(unsigned char *)s] = 1; X X nremain = bufsize = BUFSIZE; X lp = list = nnew(List); X s = list->w = nalloc(bufsize + 1); X list->m = NULL; X prev = NULL; X while (1) { X int n; X X if (nremain == 0) { X SIZE_T m = s - lp->w; X char *buf; X X while (bufsize < m + BUFSIZE) X bufsize *= 2; X buf = nalloc(bufsize + 1); X memcpy(buf, lp->w, m); X lp->w = buf; X lp->m = NULL; X s = &buf[m]; X nremain = bufsize - m; X } X n = read(fd, s, nremain); X if (n <= 0) { X if (n == -1) { X uerror("backquote read"); X rc_error(NULL); X } X /* break */ break; X } X nremain -= n; X for (bufend = &s[n]; s < bufend; s++) X if (isifs[*(unsigned char *)s]) { X *s = '\0'; X prev = lp; X lp = nnew(List); X lp->w = s + 1; X lp->m = NULL; X prev->n = lp; X } X } X X if (s == lp->w) { X if (prev == NULL) X list = NULL; X else X prev->n = NULL; X } else { X lp->n = NULL; X *s = '\0'; X } X X return list; X} X Xstatic List *backq(Node *ifsnode, Node *n) { X int p[2], pid, sp; X List *result, *ifs; X X if (n == NULL) X return NULL; X X if (pipe(p) < 0) { X uerror("pipe"); X rc_error(NULL); X } X X switch (pid = fork()) { X case -1: X uerror("fork"); X rc_error(NULL); X /* NOTREACHED */ X case 0: X setsigdefaults(); X dup2(p[1],1); X close(p[0]); X close(p[1]); X redirq = NULL; X fifoq = NULL; X walk(n, FALSE); X exit(getstatus()); X /* NOTREACHED */ X default: X ifs = glom(ifsnode); X close(p[1]); X result = bqinput(ifs, p[0]); X close(p[0]); X while (pid != wait(&sp)) X ; X statprint(sp); X return result; X } X} X Xvoid qredir(Node *n) { X Rq *next; X X if (redirq == NULL) { X next = redirq = nnew(Rq); X } else { X for (next = redirq; next->n != NULL; next = next->n) X ; X next->n = nnew(Rq); X next = next->n; X } X X next->r = n; X next->n = NULL; X} X Xstatic List *mknmpipe(Node *n) { X#ifdef NONMPIPES X rc_error("named pipes are not supported"); X return NULL; X#else X int fd; X char *fifoname, buf[32]; X List *fifolist = nnew(List); X List *f = nnew(List); X static int fifonumber = 0; X X fifoname = ncpy(sprint(buf,"/tmp/rc%d.%d", getpid(), fifonumber++)); X X if (mknod(fifoname, S_IFIFO | 0644, 0) < 0) { X uerror("mknod"); X return NULL; X } X X switch (fork()) { X case -1: X uerror("fork"); X rc_error(NULL); X /* NOTREACHED */ X case 0: X setsigdefaults(); X fd = rc_open(fifoname, CREATE); X if (fd < 0) { X uerror("open"); X exit(1); X } X if (dup2(fd, (n->u[0].i == FROM)) < 0) { /* stupid hack */ X uerror("dup2"); X exit(1); X } X close(fd); X redirq = NULL; X fifoq = NULL; X walk(n->u[2].p, FALSE); X exit(getstatus()); X /* NOTREACHED */ X default: X f->w = fifoname; X f->n = fifoq; X fifoq = f; X X fifolist->w = fifoname; X fifolist->m = NULL; X fifolist->n = NULL; X X return fifolist; X } X#endif X} X XList *glom(Node *n) { X Node *words; X List *v, *first, *last; X boolean dollarstar; X X if (n == NULL) X return NULL; X X switch (n->type) { X case ARGS: X case LAPPEND: X words = n->u[0].p; X last = NULL; X while (words != NULL && (words->type == ARGS || words->type == LAPPEND)) { X if (words->u[1].p != NULL && words->u[1].p->type != rWORD) X break; X first = glom(words->u[1].p); X if (first != NULL) { X first->n = last; X last = first; X } X words = words->u[0].p; X } X return append(append(glom(words), last), glom(n->u[1].p)); X case BACKQ: X return backq(n->u[0].p,n->u[1].p); X case CONCAT: X first = glom(n->u[0].p); /* force left-to-right evaluation */ X return concat(first, glom(n->u[1].p)); X case rDUP: X case rREDIR: X qredir(n); X return NULL; X case rWORD: X return word(n->u[0].s,n->u[1].s); X case NMPIPE: X return mknmpipe(n); X default: X break; X } X X /* X the next three operations depend on the left-child of glom X to be a variable name. Therefore they are all treated here. X (previously each function looked up and checked the validity X of a variable name) X */ X X v = glom(n->u[0].p); X if (v == NULL) X rc_error("null variable name"); X if (v->n != NULL) X rc_error("multi-word variable name"); X if (*v->w == '\0') X rc_error("zero-length variable name"); X X dollarstar = (v->w[0] == '*' && v->w[1] == '\0'); X v = varlookup(v->w); X if (dollarstar) X v = v->n; X X switch (n->type) { X default: X fprint(2,"glom: this can't happen\n"); X exit(1); X /* NOTREACHED */ X case rCOUNT: X return count(v); X case rFLAT: X return flatten(v); X case VAR: X return v; X case VARSUB: X return varsub(v, glom(n->u[1].p)); X } X X} END_OF_FILE if test 8367 -ne `wc -c <'glom.c'`; then echo shar: \"'glom.c'\" unpacked with wrong size! fi # end of 'glom.c' fi if test -f 'input.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'input.c'\" else echo shar: Extracting \"'input.c'\" \(6937 characters\) sed "s/^X//" >'input.c' <<'END_OF_FILE' X/* input.c: i/o routines for files and pseudo-files (strings) */ X X#include <errno.h> X#include <setjmp.h> X#include <stdarg.h> X#include "rc.h" X#include "input.h" X#include "utils.h" X#include "walk.h" X#include "hash.h" X#include "lex.h" X#include "open.h" X#include "nalloc.h" X#include "except.h" X#include "glom.h" X#include "builtins.h" X#include "parse.h" X#include "tree.h" X X/* X warning, changes have been made to (fd|string)(gchar|ugchar) so that X you cannot unget EOF more than once. lex.c never does this, so I'm X safe, but if you unget EOF more than once, expect to be able to read X it back only once. The reason is that EOF is an int, whereas the buffers X are character buffers. X*/ X Xtypedef struct Input { X enum inputtype t; X char *ibuf; X int fd; X int index; X int read; X int lineno; X} Input; X X#define BUFSIZE ((SIZE_T) 256) X X#ifdef READLINE Xextern char *readline(char *); Xextern void add_history(char *); Xstatic char *rlinebuf; X#endif X Xchar *prompt, *prompt2; Xboolean rcrc; Xchar *histstr; Xint histfd; X Xstatic int dead(void); Xstatic int fdgchar(void); Xstatic int stringgchar(void); Xstatic void history(void); Xstatic void ugdead(int); X Xstatic char *inbuf; Xstatic SIZE_T istacksize, chars_out, chars_in; Xstatic boolean eofread = FALSE; Xstatic Input *istack, *itop; X Xstatic int (*realgchar)(void); Xstatic void (*realugchar)(int); X Xint last; X Xint gchar(void) { X if (eofread) { X eofread = FALSE; X return last = EOF; X } X return realgchar(); X} X Xvoid ugchar(int c) { X realugchar(c); X} X Xstatic int dead() { X return last = EOF; X} X Xstatic void ugdead(int c) { X return; X} X Xstatic void ugalive(int c) { X if (c == EOF) X eofread = TRUE; X else X inbuf[--chars_out] = c; X} X X/* get the next character from a string. */ X Xstatic int stringgchar() { X return last = (inbuf[chars_out] == '\0' ? EOF : inbuf[chars_out++]); X} X X/* X read a character from a file-descriptor. If GNU readline is defined, add a newline and doctor X the buffer to look like a regular fdgchar buffer. X*/ X Xstatic int fdgchar() { X if (chars_out >= chars_in + 2) { /* has the buffer been exhausted? if so, replenish it */ X do { X#ifdef READLINE X if (interactive && istack->fd == 0) { X rlinebuf = readline(prompt); X if (rlinebuf == NULL) { X chars_in = 0; X } else { X if (*rlinebuf != '\0') X add_history(rlinebuf); X chars_in = strlen(rlinebuf) + 1; X efree(inbuf); X inbuf = ealloc(chars_in + 3); X strcpy(inbuf+2, rlinebuf); X strcat(inbuf+2, "\n"); X efree(rlinebuf); X } X } else X#endif X chars_in = read(istack->fd, inbuf + 2, BUFSIZE); X } while (chars_in == -1 && errno == EINTR); /* Suppose it was interrupted by a signal */ X X switch (chars_in) { X case 0: X return last = EOF; X case -1: X uerror("read"); X rc_exit(1); X /* NOTREACHED */ X default: X chars_out = 2; X if (dashvee) X writeall(2, inbuf + 2, chars_in); X history(); X } X } X return last = inbuf[chars_out++]; X} X X/* set up the input stack, and put a "dead" input at the bottom, so that yyparse will always read eof */ X Xvoid initinput() { X istack = itop = ealloc(istacksize = 256 * sizeof (Input)); X istack->t = FD; X istack->fd = -1; X realugchar = ugalive; X} X X/* push an input source onto the stack. set up a new input buffer, and set the right getchar() */ X Xvoid pushinput(int /*enum inputtype*/ t,...) { X SIZE_T count, idiff; X char **a; X va_list ap; X X va_start(ap, t); X X istack->index = chars_out; X istack->read = chars_in; X istack->ibuf = inbuf; X istack->lineno = lineno; X istack++; X X idiff = istack - itop; X X if (idiff >= istacksize / sizeof (Input)) { X itop = erealloc(itop, istacksize *= 2); X istack = itop + idiff; X } X X istack->t = t; X if (t == FD) { X istack->fd = va_arg(ap, int); X realgchar = fdgchar; X inbuf = ealloc(BUFSIZE + 2); X } else { X count = strarraylen(a = va_arg(ap, char **)); X sprint((inbuf = ealloc(count + 3)) + 2, "%a", a); X realgchar = stringgchar; X } X X va_end(ap); X X realugchar = ugalive; X chars_out = 2; X chars_in = 0; X lineno = 1; X} X X/* remove an input source from the stack. restore the right kind of getchar (string,fd) etc. */ X Xvoid popinput() { X if (istack->t == FD) X close(istack->fd); X efree(inbuf); X X --istack; X X realgchar = (istack->t == STRING ? stringgchar : fdgchar); X X if (istack->fd == -1) { /* top of input stack */ X realgchar = dead; X realugchar = ugdead; X } X X inbuf = istack->ibuf; X chars_out = istack->index; X chars_in = istack->read; X lineno = istack->lineno; X} X X/* flush input characters upto newline. Used by scanerror() */ X Xvoid flushu() { X int c; X X if (last == '\n' || last == EOF) X return; X X while ((c = gchar()) != '\n' && c != EOF) X ; /* skip to newline */ X X if (c == EOF) X ugchar(c); X} X X/* the wrapper loop in rc: prompt for commands until EOF, calling yyparse and walk() */ X XNode *doit(boolean execit) { X boolean eof; X jmp_buf j; X Estack e1, e2; X X setjmp(j); X except(ERROR, j, &e1); X X for (eof = FALSE; !eof;) { X except(ARENA, NULL, &e2); X X if (dashell) { X char *fname[3]; X X fname[1] = concat(varlookup("home"),word("/.rcrc",NULL))->w; X fname[2] = NULL; X rcrc = TRUE; X dashell = FALSE; X b_dot(fname); X } X X if (interactive) { X Node *f; X List *s; X X if ((f = fnlookup("prompt")) != NULL) X walk(treecpy(f, nalloc), TRUE); X X if ((s = varlookup("prompt")) != NULL) { X#ifdef READLINE X prompt = s->w; X#else X fprint(2,"%s",s->w); X#endif X prompt2 = (s->n == NULL ? "" : s->n->w); X } X } X X inityy(); X X if (yyparse() < 0) X rc_raise(ERROR); X X eof = (last == EOF); /* "last" can be clobbered during a walk() */ X X if (execit && parsetree != NULL) X walk(parsetree, TRUE); X X unexcept(); /* ARENA */ X } X X popinput(); X unexcept(); /* ERROR */ X return parsetree; X} X X/* parse a function imported from the environment */ X XNode *parseline(char *extdef) { X char *in[2]; X int i = interactive; X Node *ret; X X in[0] = extdef; X in[1] = NULL; X interactive = FALSE; X pushinput(STRING, in); X ret = doit(FALSE); X interactive = i; X return ret; X} X X/* write last command out to a file. Check to see if $history has changed, also */ X Xstatic void history() { X List *histlist; X SIZE_T a; X X if (!interactive) X return; X X if ((histlist = varlookup("history")) == NULL) { X if (histstr != NULL) { X efree(histstr); X close(histfd); X histstr = NULL; X } X return; X } X X if (histstr == NULL || !streq(histstr, histlist->w)) { /* open new file */ X if (histstr != NULL) { X efree(histstr); X close(histfd); X } X histstr = ecpy(histlist->w); X histfd = rc_open(histstr, APPEND); X if (histfd < 0) { X uerror(histstr); X efree(histstr); X histstr = NULL; X } X } X X /* X small unix hack: since read() reads only up to a newline from a terminal, then X presumably this write() will write at most only one input line at a time. X */ X X for (a = 2; a < chars_in + 2; a++) { /* skip empty lines and comments in history. */ X if (inbuf[a] == '#' || inbuf[a] == '\n') X return; X if (inbuf[a] != ' ' && inbuf[a] != '\t') X break; X } X writeall(histfd, inbuf + 2, chars_in); X} END_OF_FILE if test 6937 -ne `wc -c <'input.c'`; then echo shar: \"'input.c'\" unpacked with wrong size! fi # end of 'input.c' fi if test -f 'lex.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'lex.c'\" else echo shar: Extracting \"'lex.c'\" \(10083 characters\) sed "s/^X//" >'lex.c' <<'END_OF_FILE' X/* lex.c: rc's lexical analyzer */ X X#include "rc.h" X#include "lex.h" X#include "y.tab.h" X#include "nalloc.h" X#include "input.h" X#include "utils.h" X#include "hash.h" X#include "heredoc.h" X X/* X Special characters (i.e., "non-word") in rc: X \t \n # ; & | ^ $ = ~ ` ' { } @ ! ( ) < > \ X X The lexical analyzer is fairly straightforward. The only really unclean part X concerns backslash continuation and "double backslashes". A backslash followed by X a newline is treated as a space, otherwise backslash is not a special characeter X (i.e., it can be part of a word). This introduces a host of unwanted special X cases. In our case, \ cannot be a word character, since we wish to read in all X word characters in a tight loop. X X Note: to save the trouble of declaring these arrays with TRUEs and FALSEs, I am assuming X that FALSE = 0, TRUE = 1. (and so is it declared in rc.h) X*/ X X#define BUFSIZE ((SIZE_T) 1000) /* malloc hates power of 2 buffers? */ X#define BUFMAX (8 * BUFSIZE) /* How big the buffer can get before we re-allocate the X space at BUFSIZE again. Premature optimization? Maybe. X */ X Xenum wordstates { NW, RW, KW }; /* "nonword", "realword", "keyword" */ X Xstatic void getpair(int); X Xint lineno; X Xconst char nw[] = { X 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, X 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, X 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, X 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, X 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, X 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, X 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, X 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 X}; X Xconst char dnw[] = { X 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, X 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, X 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, X 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, X 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, X 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, X 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, X 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 X}; X Xstatic SIZE_T bufsize = BUFSIZE; Xstatic char *realbuf = NULL; Xstatic boolean newline = FALSE; Xstatic enum wordstates word = NW; Xstatic int fd_left, fd_right; X X#define checkfreecaret {if (*wp != NW) { *wp = NW; ugchar(c); return '^'; }} X Xenum filedescriptors { UNSET = -9, CLOSED = -1 }; X Xint yylex() { X static boolean dollar = FALSE; X boolean saw_meta = FALSE; X int c; X SIZE_T i; /* The purpose of all these local assignments is to */ X const char *meta; /* allow optimizing compilers like gcc to load these */ X char *buf = realbuf; /* values into registers. On a sparc this is a big */ X YYSTYPE *y = &yylval; /* win, in code size *and* execution time */ X enum wordstates *wp = &word; X X /* rc variable-names may contain only alnum, '*' and '_', so use dnw if we are scanning one. */ X meta = (dollar ? dnw : nw); X dollar = FALSE; X X if (newline) { X --lineno; /* slight space optimization; print_prompt2() always increments lineno */ X print_prompt2(); X newline = FALSE; X } X Xtop: while ((c = gchar()) == ' ' || c == '\t') X *wp = NW; X X if (c == EOF) X return END; X X if (!meta[c]) { /* it's a word or keyword. */ X checkfreecaret; X *wp = RW; X i = 0; X read: do { X buf[i++] = c; X if (c == '?' || c == '[' || c == '*') X saw_meta = TRUE; X if (i >= bufsize) X buf = realbuf = erealloc(buf, bufsize *= 2); X } while ((c = gchar()) != EOF && !meta[c]); X if (c == '\\') { X if ((c = gchar()) == '\n') { X print_prompt2(); X c = ' '; /* Pretend a space was read */ X } else { X bs: buf[i++] = '\\'; X if (!meta[c] || c == '\\') X goto read; X } X } X ugchar(c); X buf[i] = '\0'; X *wp = KW; X if (i == 2) { X if (*buf == 'i' && buf[1] == 'f') return IF; X if (*buf == 'f' && buf[1] == 'n') return FN; X if (*buf == 'i' && buf[1] == 'n') return IN; X } X if (streq(buf,"for")) return FOR; X if (streq(buf,"else")) return ELSE; X if (streq(buf,"switch")) return SWITCH; X if (streq(buf,"while")) return WHILE; X *wp = RW; X y->word.w = ncpy(buf); X if (saw_meta) { X char *r, *s; X X y->word.m = nalloc(strlen(buf) + 1); X for (r = buf, s = y->word.m; *r != '\0'; r++, s++) X *s = (*r == '?' || *r == '[' || *r == '*'); X } else { X y->word.m = NULL; X } X return WORD; X } X X if (c == '`' || c == '!' || c == '@' || c == '~' || c == '$' || c == '\'') { X checkfreecaret; X if (c == '!' || c == '@' || c == '~') X *wp = KW; X } X X switch (c) { X case '\0': X scanerror("null character"); X /* NOTREACHED */ X case '!': X return BANG; X case '@': X return SUBSHELL; X case '~': X return TWIDDLE; X case '`': X c = gchar(); X if (c == '`') X return BACKBACK; X ugchar(c); X return '`'; X case '$': X dollar = TRUE; X c = gchar(); X if (c == '#') X return COUNT; X if (c == '^') X return FLAT; X ugchar(c); X return '$'; X case '\'': X *wp = RW; X i = 0; X do { X buf[i++] = c; X if (c == '\n') X print_prompt2(); X if (c == EOF) X scanerror("eof in quoted string"); X if (i >= bufsize) X buf = realbuf = erealloc(buf, bufsize *= 2); X } while ((c = gchar()) != '\'' || (c = gchar()) == '\''); /* quote "'" thus: 'how''s it going?' */ X ugchar(c); X buf[i] = '\0'; X y->word.w = ncpy(buf); X y->word.m = NULL; X return WORD; X case '\\': X if ((c = gchar()) == '\n') { X print_prompt2(); X goto top; /* Pretend it was just another space. */ X } X ugchar(c); X c = '\\'; X checkfreecaret; X c = gchar(); X i = 0; X goto bs; X case '(': X if (*wp == RW) /* SUB's happen only after real words, not keyowrds, so if () and while () work */ X c = SUB; X *wp = NW; X return c; X case '#': X while ((c = gchar()) != '\n') /* skip comment until newline */ X if (c == EOF) X return END; X /* FALLTHROUGH */ X case '\n': X lineno++; X newline = TRUE; X /* FALLTHROUGH */ X case ';': X case '^': X case ')': X case '=': X case '{': case '}': X *wp = NW; X return c; X case '&': X *wp = NW; X c = gchar(); X if (c == '&') X return ANDAND; X ugchar(c); X return '&'; X case '|': X *wp = NW; X c = gchar(); X if (c == '|') X return OROR; X getpair(c); X if ((y->pipe.left = fd_left) == UNSET) X y->pipe.left = 1; /* default to fd 1 */ X if ((y->pipe.right = fd_right) == UNSET) X y->pipe.right = 0; /* default to fd 0 */ X if (y->pipe.right == CLOSED) X scanerror("expected digit after '='"); /* can't close a pipe */ X return PIPE; X case '>': X c = gchar(); X if (c == '>') { X c = gchar(); X y->redir.type = APPEND; X } else X y->redir.type = CREATE; X y->redir.fd = 1; X goto common; X case '<': X c = gchar(); X if (c == '<') { X c = gchar(); X if (c == '<') { X c = gchar(); X y->redir.type = HERESTRING; X } else { X y->redir.type = HEREDOC; X } X } else X y->redir.type = FROM; X y->redir.fd = 0; X common: X *wp = NW; X getpair(c); X if (fd_right == UNSET) { /* redirection, not dup */ X if (fd_left != UNSET) X y->redir.fd = fd_left; X return REDIR; X } else { /* dup; recast yylval */ X y->dup.type = y->redir.type; X y->dup.left = fd_left; X y->dup.right = fd_right; X return DUP; X } X default: X *wp = NW; X return c; /* don't know what it is, let yacc barf on it */ X } X} X Xvoid skipnl(void) { X int c; X X while ((c = gchar()) == ' ' || c == '\t' || c == '#' || c == '\n') { X if (c == '\n' || c == '#') { X for (; c != '\n'; c = gchar()) /* skip comments */ X if (c == EOF) { X ugchar(c); X return; X } X print_prompt2(); X } X } X ugchar(c); X} X Xvoid yyerror(const char *s) { X char *tok; X char tokbuf[128]; X X if (!interactive) { X if (word != NW) X tok = realbuf; X else if (last == EOF) X tok = "end of input"; X else if (last == '\n') X tok = "end of line"; X else X sprint(tok = tokbuf, (last < 32 || last > 126) ? "(decimal %d)" : "'%c'",last); X fprint(2,"line %d: %s near %s\n", lineno - (last == '\n'), s, tok); X } else X fprint(2,"%s\n",s); X} X Xvoid scanerror(char *s) { X flushu(); /* flush upto newline */ X rc_error(s); X} X Xvoid inityy(void) { X newline = FALSE; X word = NW; X hq = NULL; X X /* return memory to the system if the buffer got too large */ X X if (bufsize > BUFMAX && realbuf != NULL) { X efree(realbuf); X bufsize = BUFSIZE; X realbuf = ealloc(bufsize); X } else if (realbuf == NULL) X realbuf = ealloc(bufsize); X} X Xvoid print_prompt2() { X lineno++; X#ifdef READLINE X prompt = prompt2; X#else X if (interactive) X fprint(2,"%s",prompt2); X#endif X} X X/* X Scan in a pair of integers for redirections like >[2=1]. CLOSED represents a closed file X descriptor (i.e., >[2=]) and UNSET represents an undesignated file descriptor (e.g., X >[2] is represented as (2,UNSET). X X This function makes use of unsigned compares to make range tests in one compare operation. X*/ X Xstatic void getpair(int c) { X int n; X X fd_left = fd_right = UNSET; X X if (c != '[') { X ugchar(c); X return; X } X X if ((unsigned int) (n = gchar() - '0') > 9) X scanerror("expected digit after '['"); X X while ((unsigned int) (c = gchar() - '0') <= 9) X n = n * 10 + c; X X fd_left = n; X c += '0'; X X switch (c) { X default: X scanerror("expected '=' or ']' after digit"); X /* NOTREACHED */ X case ']': X return; X case '=': X if ((unsigned int) (n = gchar() - '0') > 9) { X if (n != ']' - '0') X scanerror("expected digit or ']' after '='"); X fd_right = CLOSED; X } else { X while ((unsigned int) (c = gchar() - '0') <= 9) X n = n * 10 + c; X if (c != ']' - '0') X scanerror("expected ']' after digit"); X fd_right = n; X } X } X} END_OF_FILE if test 10083 -ne `wc -c <'lex.c'`; then echo shar: \"'lex.c'\" unpacked with wrong size! fi # end of 'lex.c' fi if test -f 'walk.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'walk.c'\" else echo shar: Extracting \"'walk.c'\" \(8357 characters\) sed "s/^X//" >'walk.c' <<'END_OF_FILE' X/* walk.c: walks the parse tree. */ X X#include <setjmp.h> X#include <signal.h> X#include "rc.h" X#include "utils.h" X#include "status.h" X#include "exec.h" X#include "walk.h" X#include "glom.h" X#include "hash.h" X#include "glob.h" X#include "lex.h" X#include "open.h" X#include "except.h" X Xboolean cond = FALSE; X Xstatic boolean iscase(Node *); Xstatic boolean isallpre(Node *); Xstatic boolean dofork(void); X X/* walk the parse-tree. "obvious". */ X Xboolean walk(Node *n, boolean parent) { X if (n == NULL) { X if (!parent) X exit(0); X set(TRUE); X return TRUE; X } X X switch (n->type) { X case ARGS: case BACKQ: case CONCAT: case rCOUNT: case rFLAT: X case LAPPEND: case rREDIR: case VAR: case VARSUB: case rWORD: X exec(glob(glom(n)), parent); /* simple command */ X break; X case BODY: X walk(n->u[0].p, TRUE); X walk(n->u[1].p, TRUE); X break; X case NOWAIT: { X int pid; X char apid[8]; X X switch (pid = fork()) { X case -1: X uerror("fork"); X rc_error(NULL); X /* NOTREACHED */ X case 0: X setsigdefaults(); X#ifdef NOJOB X signal(SIGINT, SIG_IGN); /* traditional backgrounding procedure: ignore SIGINT */ X#else X signal(SIGTTOU, SIG_IGN); /* Berkeleyized version: put it in a new pgroup. */ X signal(SIGTTIN, SIG_IGN); X signal(SIGTSTP, SIG_IGN); X setpgrp(0, getpid()); X#endif X dup2(rc_open("/dev/null", FROM), 0); X walk(n->u[0].p, FALSE); X exit(getstatus()); X /* NOTREACHED */ X default: X if (interactive) X fprint(2,"%d\n",pid); X varassign("apid", word(sprint(apid,"%d",pid), NULL), FALSE); X redirq = NULL; /* kill pre-redir queue */ X fifoq = NULL; X } X break; X } X case rANDAND: { X boolean oldcond = cond; X X cond = TRUE; X if (walk(n->u[0].p, TRUE)) { X cond = oldcond; X walk(n->u[1].p, TRUE); X } else X cond = oldcond; X break; X } X case rOROR: { X boolean oldcond = cond; X X cond = TRUE; X if (!walk(n->u[0].p, TRUE)) { X cond = oldcond; X walk(n->u[1].p, TRUE); X } else X cond = oldcond; X break; X } X case rBANG: X set(!walk(n->u[0].p, TRUE)); X break; X case rIF: { X boolean oldcond = cond; X Node *true_cmd = n->u[1].p, *false_cmd = NULL; X X if (true_cmd != NULL && true_cmd->type == rELSE) { X false_cmd = true_cmd->u[1].p; X true_cmd = true_cmd->u[0].p; X } X cond = TRUE; X if (!walk(n->u[0].p, TRUE)) X true_cmd = false_cmd; /* run the else clause */ X cond = oldcond; X walk(true_cmd, TRUE); X break; X } X case rWHILE: { X jmp_buf j; X boolean oldcond = cond; X Estack e1,e2; X X cond = TRUE; X X if (!walk(n->u[0].p, TRUE)) { /* prevent spurious breaks inside test */ X cond = oldcond; X break; X } X X if (setjmp(j)) X break; X X except(BREAK, j, &e1); X do { X cond = oldcond; X except(ARENA, NULL, &e2); X walk(n->u[1].p, TRUE); X unexcept(); /* ARENA */ X cond = TRUE; X } while (walk(n->u[0].p, TRUE)); X cond = oldcond; X unexcept(); /* BREAK */ X break; X } X case FORIN: { X List *l; X jmp_buf j; X Estack e1,e2; X X if (setjmp(j)) X break; X X except(BREAK, j, &e1); X for (l = glob(glom(n->u[1].p)); l != NULL; l = l->n) { X assign(glom(n->u[0].p), word(l->w, NULL), FALSE); X except(ARENA, NULL, &e2); X walk(n->u[2].p, TRUE); X unexcept(); /* ARENA */ X } X unexcept(); /* BREAK */ X break; X } X case rSUBSHELL: X if (dofork()) { X setsigdefaults(); X walk(n->u[0].p, TRUE); X rc_exit(getstatus()); X } X break; X case ASSIGN: X if (n->u[0].p == NULL) X rc_error("null variable name"); X assign(glom(n->u[0].p), glob(glom(n->u[1].p)), FALSE); X set(TRUE); X break; X case rPIPE: { X int i, j, k, sp, pid, wpid, fd_prev, fd_out, pids[512], stats[512], p[2]; X void (*handler)(int); X Node *r; X X fd_prev = fd_out = 1; X X for (r = n, i = 0; r != NULL && r->type == rPIPE; r = r->u[2].p, i++) { X if (i > 500) /* the only hard-wired limit in rc? */ X rc_error("pipe too long"); X X if (pipe(p) < 0) { X uerror("pipe"); X rc_error(NULL); X } X X switch (pid = fork()) { X case -1: X uerror("fork"); X rc_error(NULL); X /* NOTREACHED */ X case 0: X setsigdefaults(); X redirq = NULL; /* clear preredir queue */ X fifoq = NULL; X dup2(p[0],r->u[1].i); X if (fd_prev != 1) { X dup2(fd_prev, fd_out); X close(fd_prev); X } X close(p[0]); X close(p[1]); X walk(r->u[3].p, FALSE); X exit(getstatus()); X /* NOTREACHED */ X default: X if (fd_prev != 1) X close(fd_prev); /* parent must close all pipe fd's */ X pids[i] = pid; X fd_prev = p[1]; X fd_out = r->u[0].i; X close(p[0]); X } X } X X switch (pid = fork()) { X case -1: X uerror("fork"); X rc_error(NULL); X /* NOTREACHED */ X case 0: X setsigdefaults(); X dup2(fd_prev, fd_out); X close(fd_prev); X walk(r, FALSE); X exit(getstatus()); X /* NOTREACHED */ X default: X redirq = NULL; /* clear preredir queue */ X fifoq = NULL; X close(fd_prev); X pids[i++] = pid; X X /* collect statuses */ X X if ((handler = signal(SIGINT, SIG_IGN)) == SIG_DFL) X signal(SIGINT, SIG_DFL); /* don't ignore interrupts in noninteractive mode */ X X for (k = i; k != 0;) { X if ((wpid = wait(&sp)) < 0) X uerror("wait"); X for (j = 0; j < i; j++) X if (wpid == pids[j]) { X stats[j] = sp; X pids[j] = 0; X --k; X } X } X X setpipestatus(stats, i); X signal(SIGINT, handler); X } X break; X } X case NEWFN: { X List *l = glom(n->u[0].p); X X if (l == NULL) X rc_error("null function name"); X while (l != NULL) { X fnassign(l->w, n->u[1].p); X l = l->n; X } X set(TRUE); X break; X } X case RMFN: { X List *l = glom(n->u[0].p); X X while (l != NULL) { X fnrm(l->w); X l = l->n; X } X set(TRUE); X break; X } X case rDUP: X break; /* Null command */ X case MATCH: X set(lmatch(glob(glom(n->u[0].p)), glom(n->u[1].p))); X break; X case rSWITCH: { X List *v = glom(n->u[0].p); X X while (1) { X do { X n = n->u[1].p; X if (n == NULL || n->type != BODY) X return istrue(); X } while (!iscase(n->u[0].p)); X if (lmatch(v, glom(n->u[0].p)->n)) { X for (n = n->u[1].p; n != NULL && !iscase(n->u[0].p); n = n->u[1].p) { X if (n->type != BODY) { /* special case at the end of BODY subtree */ X walk(n, TRUE); X break; X } X walk(n->u[0].p, TRUE); X } X break; X } X } X break; X } X case PRE: { X List *v; X X if (n->u[0].p->type == rREDIR) { X qredir(n->u[0].p); X walk(n->u[1].p, TRUE); X break; X } else if (n->u[0].p->type == ASSIGN) { X if (isallpre(n->u[1].p)) { X walk(n->u[0].p, TRUE); X walk(n->u[1].p, TRUE); X break; X } else { X v = glom(n->u[0].p->u[0].p); X assign(v, glob(glom(n->u[0].p->u[1].p)), TRUE); X walk(n->u[1].p, TRUE); X varrm(v->w, TRUE); X } X } else X rc_error("walk: node other than assign or redir in PRE. help!"); X break; X } X case BRACE: X if (dofork()) { X setsigdefaults(); X walk(n->u[1].p, TRUE); /* Do redirections */ X redirq = NULL; /* Reset redirection queue */ X walk(n->u[0].p, TRUE); /* Do commands */ X rc_exit(getstatus()); X /* NOTREACHED */ X } X break; X case EPILOG: X qredir(n->u[0].p); X if (n->u[1].p != NULL) X walk(n->u[1].p, TRUE); /* Do more redirections. */ X else X doredirs(); /* Okay, we hit the bottom. */ X break; X case NMPIPE: X rc_error("named pipes cannot be executed as commands"); X /* NOTREACHED */ X default: X rc_error("walk: unknown node; this can't happen"); X /* NOTREACHED */ X } X return istrue(); X} X X/* checks a "command-line" (in parsetree form) to see if it contains the fake keyword "case". */ X Xstatic boolean iscase(Node *n) { X if (n == NULL) X return FALSE; X X switch (n->type) { X case rWORD: X return streq(n->u[0].s, "case"); X case ARGS: case LAPPEND: X return iscase(n->u[0].p); X default: X return FALSE; X } X} X X/* checks to see whether a subtree is all pre-command directives, i.e., assignments and redirs only */ X Xstatic boolean isallpre(Node *n) { X if (n == NULL) X return TRUE; X X switch (n->type) { X case PRE: X return isallpre(n->u[1].p); X case rREDIR: case ASSIGN: case rDUP: X return TRUE; X default: X return FALSE; X } X} X X/* X A code-saver. Forks, child returns (for further processing in walk()), and the parent X waits for the child to finish, setting $status appropriately. X*/ X Xstatic boolean dofork() { X int pid, sp; X X switch (pid = fork()) { X case -1: X uerror("fork"); X rc_error(NULL); X /* NOTREACHED */ X case 0: X return TRUE; X default: X redirq = NULL; /* clear out the pre-redirection queue in the parent */ X fifoq = NULL; X while (pid != wait(&sp)) X if (pid < 0) X uerror("wait"); X setstatus(sp); X return FALSE; X } X} X END_OF_FILE if test 8357 -ne `wc -c <'walk.c'`; then echo shar: \"'walk.c'\" unpacked with wrong size! fi # end of 'walk.c' fi echo shar: End of archive 2 \(of 4\). cp /dev/null ark2isdone MISSING="" for I in 1 2 3 4 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 4 archives. rm -f ark[1-9]isdone else echo You still must unpack the following archives: echo " " ${MISSING} fi exit 0 exit 0 # Just in case... -- Kent Landfield INTERNET: kent@sparky.IMD.Sterling.COM Sterling Software, IMD UUCP: uunet!sparky!kent Phone: (402) 291-8300 FAX: (402) 291-4362 Please send comp.sources.misc-related mail to kent@uunet.uu.net.