#define Extern extern
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/dir.h>
#include <limits.h>
#include <signal.h>
#include <errno.h>
#include <setjmp.h>
#include "sh.h"

/* -------- eval.c -------- */
/* #include "sh.h" */
/* #include "word.h" */

/*
 * ${}
 * `command`
 * blank interpretation
 * quoting
 * glob
 */

_PROTOTYPE(static int expand, (char *cp, struct wdblock **wbp, int f ));
_PROTOTYPE(static char *blank, (int f ));
_PROTOTYPE(static int dollar, (int quoted ));
_PROTOTYPE(static int grave, (int quoted ));
_PROTOTYPE(void globname, (char *we, char *pp ));
_PROTOTYPE(static char *generate, (char *start1, char *end1, char *middle, char *end ));
_PROTOTYPE(static int anyspcl, (struct wdblock *wb ));
_PROTOTYPE(static int xstrcmp, (char *p1, char *p2 ));
_PROTOTYPE(void glob0, (char *a0, unsigned int a1, int a2, int (*a3)(char *, char *)));
_PROTOTYPE(void glob1, (char *base, char *lim ));
_PROTOTYPE(void glob2, (char *i, char *j ));
_PROTOTYPE(void glob3, (char *i, char *j, char *k ));
_PROTOTYPE(char *memcopy, (char *ato, char *from, int nb ));

char **
eval(ap, f)
register char **ap;
int f;
{
	struct wdblock *wb;
	char **wp;
	char **wf;
	jmp_buf ev;

	wp = NULL;
	wb = NULL;
	wf = NULL;
	if (newenv(setjmp(errpt = ev)) == 0) {
		while (*ap && isassign(*ap))
			expand(*ap++, &wb, f & ~DOGLOB);
		if (flag['k']) {
			for (wf = ap; *wf; wf++) {
				if (isassign(*wf))
					expand(*wf, &wb, f & ~DOGLOB);
			}
		}
		for (wb = addword((char *)0, wb); *ap; ap++) {
			if (!flag['k'] || !isassign(*ap))
				expand(*ap, &wb, f & ~DOKEY);
		}
		wb = addword((char *)0, wb);
		wp = getwords(wb);
		quitenv();
	} else
		gflg = 1;
	return(gflg? (char **)NULL: wp);
}

/*
 * Make the exported environment from the exported
 * names in the dictionary. Keyword assignments
 * will already have been done.
 */
char **
makenv()

{
	register struct wdblock *wb;
	register struct var *vp;

	wb = NULL;
	for (vp = vlist; vp; vp = vp->next)
		if (vp->status & EXPORT)
			wb = addword(vp->name, wb);
	wb = addword((char *)0, wb);
	return(getwords(wb));
}

char *
evalstr(cp, f)
register char *cp;
int f;
{
	struct wdblock *wb;

	wb = NULL;
	if (expand(cp, &wb, f)) {
		if (wb == NULL || wb->w_nword == 0 || (cp = wb->w_words[0]) == NULL)
			cp = "";
		DELETE(wb);
	} else
		cp = NULL;
	return(cp);
}

static int
expand(cp, wbp, f)
register char *cp;
register struct wdblock **wbp;
int f;
{
	jmp_buf ev;

	gflg = 0;
	if (cp == NULL)
		return(0);
	if (!anys("$`'\"", cp) &&
	    !anys(ifs->value, cp) &&
	    ((f&DOGLOB)==0 || !anys("[*?", cp))) {
		cp = strsave(cp, areanum);
		if (f & DOTRIM)
			unquote(cp);
		*wbp = addword(cp, *wbp);
		return(1);
	}
	if (newenv(setjmp(errpt = ev)) == 0) {
		PUSHIO(aword, cp, strchar);
		e.iobase = e.iop;
		while ((cp = blank(f)) && gflg == 0) {
			e.linep = cp;
			cp = strsave(cp, areanum);
			if ((f&DOGLOB) == 0) {
				if (f & DOTRIM)
					unquote(cp);
				*wbp = addword(cp, *wbp);
			} else
				*wbp = glob(cp, *wbp);
		}
		quitenv();
	} else
		gflg = 1;
	return(gflg == 0);
}

/*
 * Blank interpretation and quoting
 */
static char *
blank(f)
int f;
{
	register c, c1;
	register char *sp;
	int scanequals, foundequals;

	sp = e.linep;
	scanequals = f & DOKEY;
	foundequals = 0;

loop:
	switch (c = subgetc('"', foundequals)) {
	case 0:
		if (sp == e.linep)
			return(0);
		*e.linep++ = 0;
		return(sp);

	default:
		if (f & DOBLANK && any(c, ifs->value))
			goto loop;
		break;

	case '"':
	case '\'':
		scanequals = 0;
		if (INSUB())
			break;
		for (c1 = c; (c = subgetc(c1, 1)) != c1;) {
			if (c == 0)
				break;
			if (c == '\'' || !any(c, "$`\""))
				c |= QUOTE;
			*e.linep++ = c;
		}
		c = 0;
	}
	unget(c);
	if (!letter(c))
		scanequals = 0;
	for (;;) {
		c = subgetc('"', foundequals);
		if (c == 0 ||
		    (f & DOBLANK && any(c, ifs->value)) ||
		    (!INSUB() && any(c, "\"'"))) {
		        scanequals = 0;
			unget(c);
			if (any(c, "\"'"))
				goto loop;
			break;
		}
		if (scanequals)
			if (c == '=') {
				foundequals = 1;
				scanequals  = 0;
			}
			else if (!letnum(c))
				scanequals = 0;
		*e.linep++ = c;
	}
	*e.linep++ = 0;
	return(sp);
}

/*
 * Get characters, substituting for ` and $
 */
int
subgetc(ec, quoted)
register char ec;
int quoted;
{
	register char c;

again:
	c = getc(ec);
	if (!INSUB() && ec != '\'') {
		if (c == '`') {
			if (grave(quoted) == 0)
				return(0);
			e.iop->task = XGRAVE;
			goto again;
		}
		if (c == '$' && (c = dollar(quoted)) == 0) {
			e.iop->task = XDOLL;
			goto again;
		}
	}
	return(c);
}

/*
 * Prepare to generate the string returned by ${} substitution.
 */
static int
dollar(quoted)
int quoted;
{
	int otask;
	struct io *oiop;
	char *dolp;
	register char *s, c, *cp;
	struct var *vp;

	c = readc();
	s = e.linep;
	if (c != '{') {
		*e.linep++ = c;
		if (letter(c)) {
			while ((c = readc())!=0 && letnum(c))
				if (e.linep < elinep)
					*e.linep++ = c;
			unget(c);
		}
		c = 0;
	} else {
		oiop = e.iop;
		otask = e.iop->task;
		e.iop->task = XOTHER;
		while ((c = subgetc('"', 0))!=0 && c!='}' && c!='\n')
			if (e.linep < elinep)
				*e.linep++ = c;
		if (oiop == e.iop)
			e.iop->task = otask;
		if (c != '}') {
			err("unclosed ${");
			gflg++;
			return(c);
		}
	}
	if (e.linep >= elinep) {
		err("string in ${} too long");
		gflg++;
		e.linep -= 10;
	}
	*e.linep = 0;
	if (*s)
		for (cp = s+1; *cp; cp++)
			if (any(*cp, "=-+?")) {
				c = *cp;
				*cp++ = 0;
				break;
			}
	if (s[1] == 0 && (*s == '*' || *s == '@')) {
		if (dolc > 1) {
			/* currently this does not distinguish $* and $@ */
			/* should check dollar */
			e.linep = s;
			PUSHIO(awordlist, dolv+1, dolchar);
			return(0);
		} else {	/* trap the nasty ${=} */
			s[0] = '1';
			s[1] = 0;
		}
	}
	e.linep = s;
	vp = lookup(s);
	if ((dolp = vp->value) == null) {
		switch (c) {
		case '=':
			if (digit(*s)) {
				err("cannot use ${...=...} with $n");
				gflg++;
				break;
			}
			cp = evalstr(strsave(cp, areanum),DOSUB);
			setval(vp, cp);
			dolp = vp->value;
			break;

		case '-':
			dolp = evalstr(strsave(cp, areanum),DOSUB);
			break;

		case '?':
			if (*cp == 0) {
				prs("missing value for ");
				err(s);
			} else
			        err(evalstr(strsave(cp, areanum),DOSUB));
			gflg++;
			break;
		}
	} else if (c == '+') {
		dolp = evalstr(strsave(cp, areanum),DOSUB);
	}
	if (flag['u'] && dolp == null) {
		prs("unset variable: ");
		err(s);
		gflg++;
	}
	PUSHIO(aword, dolp, quoted ? qstrchar : strchar);
	return(0);
}

/*
 * Run the command in `...` and read its output.
 */
static int
grave(quoted)
int quoted;
{
        int otask;
        struct io *oiop;
	register char *cp,*s;
	register int i,c;
	int pf[2];

	c = readc();
        s = e.linep;
        *e.linep++ = c;
	oiop = e.iop;
	otask = e.iop->task;
	e.iop->task = XOTHER;
	while ((c = subgetc('\'', 0))!=0 && c!='`')
		if (e.linep < elinep)
			*e.linep++ = c;
	if (oiop == e.iop)
		e.iop->task = otask;
	if (c != '`') {
		err("no closing `");
		return(0);
	}
	if (openpipe(pf) < 0)
		return(0);
	if ((i = fork()) == -1) {
		closepipe(pf);
		err("try again");
		return(0);
	}
	if (i != 0) {
		e.linep = s;
		close(pf[1]);
		PUSHIO(afile, remap(pf[0]), quoted? qgravechar: gravechar);
		return(1);
	}
	*e.linep = 0;
	/* allow trapped signals */
	for (i=0; i<=_NSIG; i++)
		if (ourtrap[i] && signal(i, SIG_IGN) != SIG_IGN)
			signal(i, SIG_DFL);
	dup2(pf[1], 1);
	closepipe(pf);
	flag['e'] = 0;
	flag['v'] = 0;
	flag['n'] = 0;
	cp = strsave(e.linep = s, 0);
	areanum = 1;
	inithere();
	freearea(areanum);	/* free old space */
	e.oenv = NULL;
	e.iop = (e.iobase = iostack) - 1;
	unquote(cp);
	talking = 0;
	PUSHIO(aword, cp, nlchar);
	onecommand();
	exit(1);
}

char *
unquote(as)
register char *as;
{
	register char *s;

	if ((s = as) != NULL)
		while (*s)
			*s++ &= ~QUOTE;
	return(as);
}

/* -------- glob.c -------- */
/* #include "sh.h" */

/*
 * glob
 */

#define	scopy(x) strsave((x), areanum)
#define	BLKSIZ	512
#define	NDENT	((BLKSIZ+sizeof(struct direct)-1)/sizeof(struct direct))

static	struct wdblock	*cl, *nl;
static	char	spcl[] = "[?*";

struct wdblock *
glob(cp, wb)
char *cp;
struct wdblock *wb;
{
	register i;
	register char *pp;

	if (cp == 0)
		return(wb);
	i = 0;
	for (pp = cp; *pp; pp++)
		if (any(*pp, spcl))
			i++;
		else if (!any(*pp & ~QUOTE, spcl))
			*pp &= ~QUOTE;
	if (i != 0) {
		for (cl = addword(scopy(cp), (struct wdblock *)0); anyspcl(cl); cl = nl) {
			nl = newword(cl->w_nword*2);
			for(i=0; i<cl->w_nword; i++) { /* for each argument */
				for (pp = cl->w_words[i]; *pp; pp++)
					if (any(*pp, spcl)) {
						globname(cl->w_words[i], pp);
						break;
					}
				if (*pp == '\0')
					nl = addword(scopy(cl->w_words[i]), nl);
			}
			for(i=0; i<cl->w_nword; i++)
				DELETE(cl->w_words[i]);
			DELETE(cl);
		}
		for(i=0; i<cl->w_nword; i++)
			unquote(cl->w_words[i]);
		glob0((char *)cl->w_words, cl->w_nword, sizeof(char *), xstrcmp);
		if (cl->w_nword) {
			for (i=0; i<cl->w_nword; i++)
				wb = addword(cl->w_words[i], wb);
			DELETE(cl);
			return(wb);
		}
	}
	wb = addword(unquote(cp), wb);
	return(wb);
}

void
globname(we, pp)
char *we;
register char *pp;
{
	register char *np, *cp;
	char *name, *gp, *dp;
	int dn, j, n, k;
	struct direct ent[NDENT];
	char dname[NAME_MAX+1];
	struct stat dbuf;

	for (np = we; np != pp; pp--)
		if (pp[-1] == '/')
			break;
	for (dp = cp = space((int)(pp-np)+3); np < pp;)
		*cp++ = *np++;
	*cp++ = '.';
	*cp = '\0';
	for (gp = cp = space(strlen(pp)+1); *np && *np != '/';)
		*cp++ = *np++;
	*cp = '\0';
	dn = open(dp, 0);
	if (dn < 0) {
		DELETE(dp);
		DELETE(gp);
		return;
	}
	dname[NAME_MAX] = '\0';
	while ((n = read(dn, (char *)ent, sizeof(ent))) >= sizeof(*ent)) {
		n /= sizeof(*ent);
		for (j=0; j<n; j++) {
			if (ent[j].d_ino == 0)
				continue;
			strncpy(dname, ent[j].d_name, NAME_MAX);
			if (dname[0] == '.')
				if (*gp != '.')
					continue;
			for(k=0; k<NAME_MAX; k++)
				if (any(dname[k], spcl))
					dname[k] |= QUOTE;
			if (gmatch(dname, gp)) {
				name = generate(we, pp, dname, np);
				if (*np && !anys(np, spcl)) {
					if (stat(name,&dbuf)) {
						DELETE(name);
						continue;
					}
				}
				nl = addword(name, nl);
			}
		}
	}
	close(dn);
	DELETE(dp);
	DELETE(gp);
}

/*
 * generate a pathname as below.
 * start..end1 / middle end
 * the slashes come for free
 */
static char *
generate(start1, end1, middle, end)
char *start1;
register char *end1;
char *middle, *end;
{
	char *p;
	register char *op, *xp;

	p = op = space((int)(end1-start1)+strlen(middle)+strlen(end)+2);
	for (xp = start1; xp != end1;)
		*op++ = *xp++;
	for (xp = middle; (*op++ = *xp++) != '\0';)
		;
	op--;
	for (xp = end; (*op++ = *xp++) != '\0';)
		;
	return(p);
}

static int
anyspcl(wb)
register struct wdblock *wb;
{
	register i;
	register char **wd;

	wd = wb->w_words;
	for (i=0; i<wb->w_nword; i++)
		if (anys(spcl, *wd++))
			return(1);
	return(0);
}

static int
xstrcmp(p1, p2)
char *p1, *p2;
{
	return(strcmp(*(char **)p1, *(char **)p2));
}

/* -------- word.c -------- */
/* #include "sh.h" */
/* #include "word.h" */

#define	NSTART	16	/* default number of words to allow for initially */

struct wdblock *
newword(nw)
register int nw;
{
	register struct wdblock *wb;

	wb = (struct wdblock *) space(sizeof(*wb) + nw*sizeof(char *));
	wb->w_bsize = nw;
	wb->w_nword = 0;
	return(wb);
}

struct wdblock *
addword(wd, wb)
char *wd;
register struct wdblock *wb;
{
	register struct wdblock *wb2;
	register nw;

	if (wb == NULL)
		wb = newword(NSTART);
	if ((nw = wb->w_nword) >= wb->w_bsize) {
		wb2 = newword(nw * 2);
		memcopy((char *)wb2->w_words, (char *)wb->w_words, nw*sizeof(char *));
		wb2->w_nword = nw;
		DELETE(wb);
		wb = wb2;
	}
	wb->w_words[wb->w_nword++] = wd;
	return(wb);
}

char **
getwords(wb)
register struct wdblock *wb;
{
	register char **wd;
	register nb;

	if (wb == NULL)
		return((char **)NULL);
	if (wb->w_nword == 0) {
		DELETE(wb);
		return((char **)NULL);
	}
	wd = (char **) space(nb = sizeof(*wd) * wb->w_nword);
	memcopy((char *)wd, (char *)wb->w_words, nb);
	DELETE(wb);	/* perhaps should done by caller */
	return(wd);
}

_PROTOTYPE(int (*func), (char *, char *));
int	globv;

void
glob0(a0, a1, a2, a3)
char *a0;
unsigned a1;
int a2;
_PROTOTYPE(int (*a3), (char *, char *));
{
	func = a3;
	globv = a2;
	glob1(a0, a0 + a1 * a2);
}

void
glob1(base, lim)
char *base, *lim;
{
	register char *i, *j;
	int v2;
	char *lptr, *hptr;
	int c;
	unsigned n;


	v2 = globv;

top:
	if ((n=(int)(lim-base)) <= v2)
		return;
	n = v2 * (n / (2*v2));
	hptr = lptr = base+n;
	i = base;
	j = lim-v2;
	for(;;) {
		if (i < lptr) {
			if ((c = (*func)(i, lptr)) == 0) {
				glob2(i, lptr -= v2);
				continue;
			}
			if (c < 0) {
				i += v2;
				continue;
			}
		}

begin:
		if (j > hptr) {
			if ((c = (*func)(hptr, j)) == 0) {
				glob2(hptr += v2, j);
				goto begin;
			}
			if (c > 0) {
				if (i == lptr) {
					glob3(i, hptr += v2, j);
					i = lptr += v2;
					goto begin;
				}
				glob2(i, j);
				j -= v2;
				i += v2;
				continue;
			}
			j -= v2;
			goto begin;
		}


		if (i == lptr) {
			if (lptr-base >= lim-hptr) {
				glob1(hptr+v2, lim);
				lim = lptr;
			} else {
				glob1(base, lptr);
				base = hptr+v2;
			}
			goto top;
		}


		glob3(j, lptr -= v2, i);
		j = hptr -= v2;
	}
}

void
glob2(i, j)
char *i, *j;
{
	register char *index1, *index2, c;
	int m;

	m = globv;
	index1 = i;
	index2 = j;
	do {
		c = *index1;
		*index1++ = *index2;
		*index2++ = c;
	} while(--m);
}

void
glob3(i, j, k)
char *i, *j, *k;
{
	register char *index1, *index2, *index3;
	int c;
	int m;

	m = globv;
	index1 = i;
	index2 = j;
	index3 = k;
	do {
		c = *index1;
		*index1++ = *index3;
		*index3++ = *index2;
		*index2++ = c;
	} while(--m);
}

char *
memcopy(ato, from, nb)
register char *ato, *from;
register int nb;
{
	register char *to;

	to = ato;
	while (--nb >= 0)
		*to++ = *from++;
	return(ato);
}
