v20i011: rc - A Plan 9 shell reimplementation, Part02/04
Byron Rakitzis
byron at archone.tamu.edu
Thu May 23 01:42:11 AEST 1991
Submitted-by: Byron Rakitzis <byron at 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 at uunet.uu.net if you want that tool.
# Contents: fn.c footobar.c glom.c input.c lex.c walk.c
# Wrapped by kent at 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 at 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 at uunet.uu.net.
More information about the Comp.sources.misc
mailing list