[comp.sources.misc] v20i011: rc - A Plan 9 shell reimplementation, Part02/04

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.