Public Domain Korn Shell - Part.04 of 7

USENET Administration netnews at netcom.UUCP
Wed Dec 12 22:37:20 AEST 1990


#!/bin/sh
# This is part 04 of ksh-pd
# ============= src/tree.c ==============
if test ! -d 'src'; then
    echo 'x - creating directory src'
    mkdir 'src'
fi
if test -f 'src/tree.c' -a X"$1" != X"-c"; then
	echo 'x - skipping src/tree.c (File already exists)'
else
echo 'x - extracting src/tree.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'src/tree.c' &&
X/*
X * command tree climbing
X */
X
Xstatic char *RCSid = "$Header: /tmp/egisin/src/RCS/tree.c,v 3.1 88/11/03 09:18:05 egisin Exp $";
X
X#include <stddef.h>
X#include <string.h>
X#include <stdio.h>
X#include <errno.h>
X#include <setjmp.h>
X#include <varargs.h>
X#include "sh.h"
X#include "tree.h"
X
X#define	FSTRING	(FILE*)NULL
X
Xstatic	int	tputc ARGS((int c, FILE *f));
Xstatic	void	tputC ARGS((int c, FILE *f));
Xstatic	void	tputS ARGS((char *wp, FILE *f));
X
X/*
X * print a command tree
X */
X
Xvoid
Xptree(t, f)
X	register struct op *t;
X	register FILE *f;
X{
X	register char **w;
X	struct ioword **ioact;
X	struct op *t1;
X
X Chain:
X	if (t == NULL)
X		return;
X	switch (t->type) {
X	  case TCOM:
X		for (w = t->vars; *w != NULL; )
X			fptreef(f, "%S ", *w++);
X		for (w = t->args; *w != NULL; )
X			fptreef(f, "%S ", *w++);
X		break;
X	  case TEXEC:
X		t = t->left;
X		goto Chain;
X	  case TPAREN:
X		fptreef(f, "(%T)", t->left);
X		break;
X	  case TPIPE:
X		fptreef(f, "%T | ", t->left);
X		t = t->right;
X		goto Chain;
X	  case TLIST:
X		fptreef(f, "%T%;", t->left);
X		t = t->right;
X		goto Chain;
X	  case TOR:
X	  case TAND:
X		fptreef(f, "%T %s %T",
X			t->left, (t->type==TOR) ? "||" : "&&", t->right);
X		break;
X	  case TFOR:
X		fptreef(f, "for %s ", t->str);
X		if (t->vars != NULL) {
X			fptreef(f, "in ");
X			for (w = t->vars; *w; )
X				fptreef(f, "%S ", *w++);
X			fptreef(f, "%;");
X		}
X		fptreef(f, "do %T%;done ", t->left);
X		break;
X	  case TCASE:
X		fptreef(f, "case %S in%;", t->str);
X		for (t1 = t->left; t1 != NULL; t1 = t1->right) {
X			fptreef(f, "(");
X			for (w = t1->vars; *w != NULL; w++)
X				fptreef(f, "%S%c", *w, (w[1] != NULL) ? '|' : ')');
X			fptreef(f, " %T;;%;", t1->left);
X		}
X		fptreef(f, "esac ");
X		break;
X	  case TIF:
X		fptreef(f, "if %T%;", t->left);
X		t = t->right;
X		if (t->left != NULL)
X			fptreef(f, "then %T%;", t->left);
X		if (t->right != NULL)
X			fptreef(f, "else %T%;", t->right);
X		fptreef(f, "fi ");
X		break;
X	  case TWHILE:
X	  case TUNTIL:
X		fptreef(f, "%s %T%;do %T%;done ",
X			(t->type==TWHILE) ? "while" : "until",
X			t->left, t->right);
X		break;
X	  case TBRACE:
X		fptreef(f, "{%;%T%;} ", t->left);
X		break;
X	  case TASYNC:
X		fptreef(f, "%T &", t->left);
X		break;
X	  case TFUNCT:
X		fptreef(f, "function %s %T", t->str, t->left);
X		break;
X	  case TTIME:
X		fptreef(f, "time %T", t->left);
X		break;
X	  default:
X		fptreef(f, "<botch>");
X		break;
X	}
X	if ((ioact = t->ioact) != NULL)
X		while (*ioact != NULL)
X			pioact(f, *ioact++);
X}
X
Xpioact(f, iop)
X	register FILE *f;
X	register struct ioword *iop;
X{
X	fptreef(f, "%c><%S ", '0' + iop->unit, iop->name); /* todo: fix */
X}
X
X
X/*
X * variants of fputc, fputs for ptreef and snptreef
X */
X
Xstatic	char   *snpf_s;		/* snptreef string */
Xstatic	int	snpf_n;		/* snptreef length */
X
Xstatic int
Xtputc(c, f)
X	int c;
X	register FILE *f;
X{
X	if (f != NULL)
X		putc(c, f);
X	else
X		if (--snpf_n >= 0)
X			*snpf_s++ = c;
X	return c;
X}
X
Xstatic void
XtputC(c, f)
X	register int c;
X	register FILE *f;
X{
X	if ((c&0x60) == 0) {		/* C0|C1 */
X		tputc((c&0x80) ? '$' : '^', f);
X		tputc((c&0x7F|0x40), f);
X	} else if ((c&0x7F) == 0x7F) {	/* DEL */
X		tputc((c&0x80) ? '$' : '^', f);
X		tputc('?', f);
X	} else
X		tputc(c, f);
X}
X
Xstatic void
XtputS(wp, f)
X	register char *wp;
X	register FILE *f;
X{
X	register int c;
X
X	while (1)
X		switch ((c = *wp++)) {
X		  case EOS:
X			return;
X		  case CHAR:
X			tputC(*wp++, f);
X			break;
X		  case QCHAR:
X			tputc('\\', f);
X			tputC(*wp++, f);
X			break;
X		  case OQUOTE:
X		  case CQUOTE:
X			tputc('"', f);
X			break;
X		  case OSUBST:
X			tputc('$', f);
X			tputc('{', f);
X			while ((c = *wp++) != 0)
X				tputc(c, f);
X			if (*wp != CSUBST)
X				tputC(*wp++, f);
X			break;
X		  case CSUBST:
X			tputc('}', f);
X			break;
X		  case COMSUB:
X			tputc('$', f);
X			tputc('(', f);
X			while (*wp != 0)
X				tputC(*wp++, f);
X			tputc(')', f);
X			break;
X		}
X}
X
X/* TODO: use varargs properly */
X
X/* VARARGS */ int
Xfptreef(f, va_alist) va_dcl
X	register FILE *f;
X{
X	va_list va;
X	char *fmt;
X
X	va_start(va);
X	fmt = va_arg(va, char *);
X	vfptreef(f, fmt, va);
X	va_end(va);
X	return 0;
X}
X
X/* VARARGS */ int
Xsnptreef(s, n, va_alist) va_dcl
X	char *s;
X	int n;
X{
X	va_list va;
X	char *fmt;
X
X	snpf_s = s;
X	snpf_n = n;
X	va_start(va);
X	fmt = va_arg(va, char *);
X	vfptreef(FSTRING, fmt, va);
X	tputc('\0', FSTRING);
X	va_end(va);
X	return 0;
X}
X
Xvfptreef(f, fmt, va)
X	register FILE *f;
X	register char *fmt;
X	register va_list va;
X{
X	register int c;
X
X	while ((c = *fmt++))
X	    if (c == '%') {
X		register long n;
X		register char *p;
X		int neg;
X
X		switch ((c = *fmt++)) {
X		  case 'c':
X			tputc(va_arg(va, int), f);
X			break;
X		  case 's':
X			p = va_arg(va, char *);
X			while (*p)
X				tputc(*p++, f);
X			break;
X		  case 'S':	/* word */
X			p = va_arg(va, char *);
X			tputS(p, f);
X			break;
X		  case 'd': case 'u': /* decimal */
X			n = (c == 'd') ? va_arg(va, int) : va_arg(va, unsigned int);
X			neg = c=='d' && n<0;
X			p = ulton((neg) ? -n : n, 10);
X			if (neg)
X				*--p = '-';
X			while (*p)
X				tputc(*p++, f);
X			break;
X		  case 'T':	/* format tree */
X			ptree(va_arg(va, struct op *), f);
X			break;
X		  case ';':	/* newline or ; */
X			p = (f == FSTRING) ? "; " : "\n";
X			while (*p)
X				tputc(*p++, f);
X			break;
X		  default:
X			tputc(c, f);
X			break;
X		}
X	    } else
X		tputc(c, f);
X}
X
X/*
X * copy tree (for function definition)
X */
X
Xstatic	struct ioword **iocopy();
X
Xstruct op *
Xtcopy(t, ap)
X	register struct op *t;
X	Area *ap;
X{
X	register struct op *r;
X	register char **tw, **rw;
X
X	if (t == NULL)
X		return NULL;
X
X	r = (struct op *) alloc(sizeof(struct op), ap);
X
X	r->type = t->type;
X
X	/* this will copy function and for identifiers quite accidently */
X	r->str = (t->str == NULL) ? NULL : wdcopy(t->str, ap);
X
X	if (t->vars == NULL)
X		r->vars = NULL;
X	else {
X		for (tw = t->vars; *tw++ != NULL; )
X			;
X		rw = r->vars = (char **)
X			alloc((int)(tw - t->vars) * sizeof(*tw), ap);
X		for (tw = t->vars; *tw != NULL; )
X			*rw++ = wdcopy(*tw++, ap);
X		*rw = NULL;
X	}
X
X	if (t->args == NULL)
X		r->args = NULL;
X	else {
X		for (tw = t->args; *tw++ != NULL; )
X			;
X		rw = r->args = (char **)
X			alloc((int)(tw - t->args) * sizeof(*tw), ap);
X		for (tw = t->args; *tw != NULL; )
X			*rw++ = wdcopy(*tw++, ap);
X		*rw = NULL;
X	}
X
X	r->ioact = (t->ioact == NULL) ? NULL : iocopy(t->ioact, ap);
X
X	r->left = tcopy(t->left, ap);
X	r->right = tcopy(t->right, ap);
X
X	return r;
X}
X
Xchar *
Xwdcopy(wp, ap)
X	char *wp;
X	Area *ap;
X{
X	size_t len = wdscan(wp, EOS) - wp;
X	return memcpy(alloc(len, ap), wp, len);
X}
X
X/* return the position of prefix c in wp plus 1 */
Xchar *
Xwdscan(wp, c)
X	register char *wp;
X	register int c;
X{
X	register int nest = 0;
X
X	while (1)
X		switch (*wp++) {
X		  case EOS:
X			return wp;
X		  case CHAR:
X		  case QCHAR:
X			wp++;
X			break;
X		  case OQUOTE:
X		  case CQUOTE:
X			break;
X		  case OSUBST:
X			nest++;
X			while (*wp++ != 0)
X				;
X			if (*wp != CSUBST)
X				wp++;
X			break;
X		  case CSUBST:
X			if (c == CSUBST && nest == 0)
X				return wp;
X			nest--;
X			break;
X		  case COMSUB:
X			while (*wp++ != 0)
X				;
X			break;
X		}
X}
X
Xstatic	struct ioword **
Xiocopy(iow, ap)
X	register struct ioword **iow;
X	Area *ap;
X{
X	register struct ioword **ior;
X	register int i;
X
X	for (ior = iow; *ior++ != NULL; )
X		;
X	ior = (struct ioword **) alloc((int)(ior - iow) * sizeof(*ior), ap);
X
X	for (i = 0; iow[i] != NULL; i++) {
X		register struct ioword *p, *q;
X
X		p = iow[i];
X		q = (struct ioword *) alloc(sizeof(*p), ap);
X		ior[i] = q;
X		*q = *p;
X		if (p->name != NULL)
X			q->name = wdcopy(p->name, ap);
X	}
X	ior[i] = NULL;
X
X	return ior;
X}
X
X/*
X * free tree (for function definition)
X */
X
Xstatic	void iofree();
X
Xvoid
Xtfree(t, ap)
X	register struct op *t;
X	Area *ap;
X{
X	register char **w;
X
X	if (t == NULL)
X		return;
X
X	if (t->str != NULL)
X		afree((Void*)t->str, ap);
X
X	if (t->vars != NULL) {
X		for (w = t->vars; *w != NULL; w++)
X			afree((Void*)*w, ap);
X		afree((Void*)t->vars, ap);
X	}
X
X	if (t->args != NULL) {
X		for (w = t->args; *w != NULL; w++)
X			afree((Void*)*w, ap);
X		afree((Void*)t->args, ap);
X	}
X
X	if (t->ioact != NULL)
X		iofree(t->ioact, ap);
X
X	tfree(t->left, ap);
X	tfree(t->right, ap);
X
X	afree((Void*)t, ap);
X}
X
Xstatic	void
Xiofree(iow, ap)
X	struct ioword **iow;
X	Area *ap;
X{
X	register struct ioword **iop;
X	register struct ioword *p;
X
X	for (iop = iow; (p = *iop++) != NULL; ) {
X		if (p->name != NULL)
X			afree((Void*)p->name, ap);
X		afree((Void*)p, ap);
X	}
X}
X
SHAR_EOF
true || echo 'restore of src/tree.c failed'
fi
# ============= src/exec.c ==============
if test -f 'src/exec.c' -a X"$1" != X"-c"; then
	echo 'x - skipping src/exec.c (File already exists)'
else
echo 'x - extracting src/exec.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'src/exec.c' &&
X/*
X * execute command tree
X */
X
Xstatic char *RCSid = "$Header: /tmp/egisin/src/RCS/exec.c,v 3.3 88/12/17 21:19:29 egisin Exp $";
X
X#include <stddef.h>
X#include <stdio.h>
X#include <string.h>
X#include <errno.h>
X#include <signal.h>
X#include <setjmp.h>
X#include <unistd.h>
X#include <fcntl.h>
X#include "sh.h"
X#include "lex.h"
X#include "tree.h"
X#include "table.h"
X
Xstatic	int	comexec ARGS((struct op *t, char **vp, char **ap, int flags));
Xstatic	void	iosetup ARGS((struct ioword *iop));
Xstatic	void	echo ARGS((char **, char **));
Xstatic	int	herein ARGS((char *name, int sub));
X
X/*
X * execute command tree
X */
Xint
Xexecute(t, flags)
X	register struct op *t;
X	Volatile int flags;	/* if XEXEC don't fork */
X{
X	int i;
X	int Volatile rv = 0;
X	int pv[2];
X	register char **ap;
X	char *s, *cp;
X	struct ioword **iowp;
X
X	if (t == NULL)
X		return 0;
X
X	if ((flags&XFORK) && !(flags&XEXEC) && t->type != TPIPE)
X		return exchild(t, flags); /* run in sub-process */
X
X	newenv(E_EXEC);
X	if (trap)
X		runtraps();
X 
X	if (t->ioact != NULL || t->type == TPIPE) {
X		e.savefd = alloc(sizeofN(short, NUFILE), ATEMP);
X		for (i = 0; i < NUFILE; i++)
X			e.savefd[i] = 0; /* not redirected */
X	}
X
X	/* do redirection, to be restored in quitenv() */
X	if (t->ioact != NULL)
X		for (iowp = t->ioact; *iowp != NULL; iowp++) {
X			if ((flags&XPIPEI) && (*iowp)->unit == 0 ||
X			    (flags&XPIPEO) && (*iowp)->unit == 1)
X				errorf("attempt to redirect fd 0/1 in pipe\n");
X			iosetup(*iowp);
X		}
X
X	switch(t->type) {
X	  case TCOM:
X		e.type = E_TCOM;
X		rv = comexec(t, eval(t->vars, DOTILDE),
X			     eval(t->args, DOBLANK|DOGLOB|DOTILDE), flags);
X		break;
X
X	  case TPAREN:
X		rv = execute(t->left, flags|XFORK);
X		break;
X
X	  case TPIPE:
X		flags |= XFORK;
X		e.savefd[0] = savefd(0);
X		e.savefd[1] = savefd(1);
X		flags |= XPIPEO;
X		(void) dup2(e.savefd[0], 0); /* stdin of first */
X		while (t->type == TPIPE) {
X			openpipe(pv);
X			(void) dup2(pv[1], 1);	/* stdout of curr */
X			exchild(t->left, flags);
X			(void) dup2(pv[0], 0);	/* stdin of next */
X			closepipe(pv);
X			flags |= XPIPEI;
X			t = t->right;
X		}
X		flags &= ~ XPIPEO;
X		(void) dup2(e.savefd[1], 1); /* stdout of last */
X		exchild(t, flags);
X		(void) dup2(e.savefd[0], 0); /* close pipe in */
X		rv = waitlast();
X		break;
X
X	  case TLIST:
X		while (t->type == TLIST) {
X			execute(t->left, 0);
X			t = t->right;
X		}
X		rv = execute(t, 0);
X		break;
X
X	  case TASYNC:
X		rv = execute(t->left, flags|XBGND|XFORK);
X		break;
X
X	  case TOR:
X	  case TAND:
X		rv = execute(t->left, 0);
X		if (t->right != NULL && (rv == 0) == (t->type == TAND))
X			rv = execute(t->right, 0);
X		break;
X
X	  case TFOR:
X		e.type = E_LOOP;
X		ap = (t->vars != NULL) ?
X			eval(t->vars, DOBLANK|DOGLOB|DOTILDE) : e.loc->argv + 1;
X		while ((i = setjmp(e.jbuf)))
X			if (i == LBREAK)
X				goto Break1;
X		while (*ap != NULL) {
X			setstr(global(t->str), *ap++);
X			rv = execute(t->left, 0);
X		}
X	  Break1:
X		break;
X
X	  case TWHILE:
X	  case TUNTIL:
X		e.type = E_LOOP;
X		while ((i = setjmp(e.jbuf)))
X			if (i == LBREAK)
X				goto Break2;
X		while ((execute(t->left, 0) == 0) == (t->type == TWHILE))
X			rv = execute(t->right, 0);
X	  Break2:
X		break;
X
X	  case TIF:
X	  case TELIF:
X		if (t->right == NULL)
X			break;	/* should be error */
X		rv = execute(t->left, 0) == 0 ?
X			execute(t->right->left, 0) :
X			execute(t->right->right, 0);
X		break;
X
X	  case TCASE:
X		cp = evalstr(t->str, 0);
X		for (t = t->left; t != NULL && t->type == TPAT; t = t->right)
X		    for (ap = t->vars; *ap; ap++)
X			if ((s = evalstr(*ap, DOPAT)) && gmatch(cp, s))
X				goto Found;
X		break;
X	  Found:
X		rv = execute(t->left, 0);
X		break;
X
X	  case TBRACE:
X		rv = execute(t->left, 0);
X		break;
X
X	  case TFUNCT:
X		rv = define(t->str, t->left);
X		break;
X
X	  case TTIME:
X		rv = timex(t, flags);
X		break;
X
X	  case TEXEC:		/* an eval'd TCOM */
X		s = t->args[0];
X		ap = makenv();
X#if _MINIX				/* no F_SETFD close-on-exec */
X		for (i = 10; i < 20; i++)
X			close(i);
X#endif
X		execve(t->str, t->args, ap);
X		if (errno == ENOEXEC) {
X			*t->args-- = t->str;
X			*t->args = s;
X			execve(SHELL, t->args, ap);
X			errorf("No shell\n");
X		}
X		errorf("%s: %s\n", s, strerror(errno));
X	}
X
X	quitenv();		/* restores IO */
X	if (e.interactive) {	/* flush stdout, shlout */
X		fflush(shf[1]);
X		fflush(shf[2]);
X	}
X	if ((flags&XEXEC))
X		exit(rv);	/* exit child */
X	return rv;
X}
X
X/*
X * execute simple command
X */
X
Xstatic int
Xcomexec(t, vp, ap, flags)
X	struct op *t;
X	register char **ap, **vp;
X	int flags;
X{
X	int i;
X	int rv = 0;
X	register char *cp;
X	register struct tbl *tp = NULL;
X	register struct block *l;
X	static struct op texec = {TEXEC};
X	extern int c_exec(), c_builtin();
X
X	if (flag[FXTRACE])
X		echo(vp, ap);
X
X	/* create new variable/function block */
X	l = alloc(sizeof(struct block), ATEMP);
X	l->next = e.loc; e.loc = l;
X	newblock();
X
X Doexec:
X	if ((cp = *ap) == NULL)
X		cp = ":";
X	tp = findcom(cp, 1);
X
X	switch (tp->type) {
X	  case CSHELL:			/* shell built-in */
X		while (tp->val.f == c_builtin) {
X			if ((cp = *++ap) == NULL)
X				break;
X			tp = tsearch(&builtins, cp, hash(cp));
X			if (tp == NULL)
X				errorf("%s: not builtin\n", cp);
X		}
X		if (tp->val.f == c_exec) {
X			if (*++ap == NULL) {
X				e.savefd = NULL; /* don't restore redirection */
X				break;
X			}
X			flags |= XEXEC;
X			goto Doexec;
X		}
X		if ((tp->flag&TRACE))
X			e.loc = l->next; /* no local block */
X		i = (tp->flag&TRACE) ? 0 : LOCAL;
X		while (*vp != NULL)
X			(void) typeset(*vp++, i, 0);
X		rv = (*tp->val.f)(ap);
X		break;
X
X	case CFUNC:			/* function call */
X		if (!(tp->flag&ISSET))
X			errorf("%s: undefined function", cp);
X		l->argv = ap;
X		for (i = 0; *ap++ != NULL; i++)
X			;
X		l->argc = i - 1;
X		resetopts();
X		while (*vp != NULL)
X			(void) typeset(*vp++, LOCAL, 0);
X		e.type = E_FUNC;
X		if (setjmp(e.jbuf))
X			rv = exstat; /* return # */
X		else
X			rv = execute(tp->val.t, 0);
X		break;
X
X	case CEXEC:		/* executable command */
X		if (!(tp->flag&ISSET)) {
X			shellf("%s: not found\n", cp);
X			rv = 1;
X			break;
X		}
X
X		/* set $_ to program's full path */
X		setstr(typeset("_", LOCAL|EXPORT, 0), tp->val.s);
X		while (*vp != NULL)
X			(void) typeset(*vp++, LOCAL|EXPORT, 0);
X
X		if ((flags&XEXEC)) {
X			j_exit();
X			if (flag[FMONITOR] || !(flags&XBGND)) {
X				signal(SIGINT, SIG_DFL);
X				signal(SIGQUIT, SIG_DFL);
X			}
X		}
X
X		/* to fork we set up a TEXEC node and call execute */
X		texec.left = t;	/* for tprint */
X		texec.str = tp->val.s;
X		texec.args = ap;
X		rv = exchild(&texec, flags);
X		break;
X	}
X	if (rv != 0 && flag[FERREXIT])
X		leave(rv);
X	return (exstat = rv);
X}
X
Xint
Xshcomexec(wp)
X	register char **wp;
X{
X	register struct tbl *tp;
X
X	tp = tsearch(&builtins, *wp, hash(*wp));
X	if (tp == NULL)
X		errorf("%s: shcomexec botch\n", *wp);
X	return (*tp->val.f)(wp);
X}
X
X/*
X * define function
X */
Xint
Xdefine(name, t)
X	char	*name;
X	struct op *t;
X{
X	register struct block *l;
X	register struct tbl *tp;
X
X	for (l = e.loc; l != NULL; l = l->next) {
X		lastarea = &l->area;
X		tp = tsearch(&l->funs, name, hash(name));
X		if (tp != NULL && (tp->flag&DEFINED))
X			break;
X		if (l->next == NULL) {
X			tp = tenter(&l->funs, name, hash(name));
X			tp->flag = DEFINED|FUNCT;
X			tp->type = CFUNC;
X		}
X	}
X
X	if ((tp->flag&ALLOC))
X		tfree(tp->val.t, lastarea);
X	tp->flag &= ~(ISSET|ALLOC);
X
X	if (t == NULL)		/* undefine */
X		return 0;
X
X	tp->val.t = tcopy(t, lastarea);
X	tp->flag |= (ISSET|ALLOC);
X
X	return 0;
X}
X
X/*
X * add builtin
X */
Xbuiltin(name, func)
X	char *name;
X	int (*func)();
X{
X	register struct tbl *tp;
X	int flag = DEFINED;
X
X	if (*name == '=') {		/* sets keyword variables */
X		name++;
X		flag |= TRACE;	/* command does variable assignment */
X	}
X
X	tp = tenter(&builtins, name, hash(name));
X	tp->flag |= flag;
X	tp->type = CSHELL;
X	tp->val.f = func;
X}
X
X/*
X * find command
X * either function, hashed command, or built-in (in that order)
X */
Xstruct tbl *
Xfindcom(name, insert)
X	char	*name;
X	int	insert;			/* insert if not found */
X{
X	register struct block *l = e.loc;
X	unsigned int h = hash(name);
X	register struct	tbl *tp = NULL;
X	static struct tbl temp;
X
X	if (strchr(name, '/') != NULL) {
X		tp = &temp;
X		tp->type = CEXEC;
X		tp->flag = 0;	/* make ~ISSET */
X		goto Search;
X	}
X	for (l = e.loc; l != NULL; l = l->next) {
X		tp = tsearch(&l->funs, name, h);
X		if (tp != NULL && (tp->flag&DEFINED))
X			break;
X	}
X	if (tp == NULL)
X		tp = tsearch(&commands, name, h);
X	if (tp == NULL)
X		tp = tsearch(&builtins, name, h);
X	if (tp == NULL && insert) {
X		tp = tenter(&commands, name, h);
X		tp->type = CEXEC;
X		tp->flag = DEFINED;
X	}
X  Search:
X	if (tp->type == CEXEC && !(tp->flag&ISSET)) {
X		if (!flag[FHASHALL]) {
X			tp = &temp;
X			tp->type = CEXEC;
X			tp->flag = 0;	/* make ~ISSET */
X		}
X		name = search(name, path, 1);
X		if (name != NULL) {
X			tp->val.s = strsave(name,
X					    (tp == &temp) ? ATEMP : APERM);
X			tp->flag |= ISSET|ALLOC;
X		}
X	}
X	return tp;
X}
X
X/*
X * flush executable commands with relative paths
X */
Xflushcom(all)
X	int all;		/* just relative or all */
X{
X	register struct tbl *tp;
X
X	for (twalk(&commands); (tp = tnext()) != NULL; )
X		if ((tp->flag&ISSET) && (all || tp->val.s[0] != '/')) {
X			if ((tp->flag&ALLOC))
X				afree(tp->val.s, commands.areap);
X			tp->flag = DEFINED; /* make ~ISSET */
X		}
X}
X
X/*
X * search for command with PATH
X */
Xchar *
Xsearch(name, path, mode)
X	char *name, *path;
X	int mode;		/* 0: readable; 1: executable */
X{
X	register int i;
X	register char *sp, *tp;
X
X	if (strchr(name, '/'))
X		return (eaccess(name, mode) == 0) ? name : NULL;
X
X	sp = path;
X	do {
X		tp = line;
X		for (; *sp != '\0'; tp++)
X			if ((*tp = *sp++) == ':')
X				break;
X		if (tp != line)
X			*tp++ = '/';
X		for (i = 0; (*tp++ = name[i++]) != '\0';)
X			;
X		i = eaccess(line, mode);
X		if (i == 0)
X			return line;
X		/* what should we do about EACCES? */
X	} while (*sp != '\0');
X	return NULL;
X}
X
X/*
X * set up redirection, saving old fd's in e.savefd
X */
Xstatic void
Xiosetup(iop)
X	register struct ioword *iop;
X{
X	register int u = -1;
X	char *cp = iop->name;
X	extern long lseek();
X
X	if (iop->unit == 0 || iop->unit == 1 || iop->unit == 2)
X		e.interactive = 0;
X	e.savefd[iop->unit] = savefd(iop->unit);
X
X	if ((iop->flag&IOTYPE) != IOHERE)
X		cp = evalstr(cp, DOTILDE);
X
X	switch (iop->flag&IOTYPE) {
X	  case IOREAD:
X		u = open(cp, 0);
X		break;
X
X	  case IOCAT:
X		if ((u = open(cp, 1)) >= 0) {
X			(void) lseek(u, (long)0, 2);
X			break;
X		}
X		/* FALLTHROUGH */
X	  case IOWRITE:
X		u = creat(cp, 0666);
X		break;
X
X	  case IORDWR:
X		u = open(cp, 2);
X		break;
X
X	  case IOHERE:
X		u = herein(cp, iop->flag&IOEVAL);
X		/* cp may have wrong name */
X		break;
X
X	  case IODUP:
X		if (*cp == '-')
X			close(iop->unit);
X		else
X		if (digit(*cp))
X			u = *cp - '0';
X		else
X			errorf("%s: illegal >& argument\n", cp);
X		break;
X	}
X	if (u < 0)
X		errorf("%s: cannot %s\n", cp,
X		       (iop->flag&IOTYPE) == IOWRITE ? "create" : "open");
X	if (u != iop->unit) {
X		(void) dup2(u, iop->unit);
X		if (iop->flag != IODUP)
X			close(u);
X	}
X
X	fopenshf(iop->unit);
X}
X
X/*
X * open here document temp file.
X * if unquoted here, expand here temp file into second temp file.
X */
Xstatic int
Xherein(hname, sub)
X	char *hname;
X	int sub;
X{
X	int fd;
X	FILE * Volatile f = NULL;
X
X	f = fopen(hname, "r");
X	if (f == NULL)
X		return -1;
X	setvbuf(f, (char *)NULL, _IOFBF, BUFSIZ);
X
X	if (sub) {
X		char *cp;
X		struct source *s;
X		struct temp *h;
X
X		newenv(E_ERRH);
X		if (setjmp(e.jbuf)) {
X			if (f != NULL)
X				fclose(f);
X			quitenv();
X			return -1; /* todo: error()? */
X		}
X
X		/* set up yylex input from here file */
X		s = pushs(SFILE);
X		s->u.file = f;
X		source = s;
X		if (yylex(ONEWORD) != LWORD)
X			errorf("exec:herein error\n");
X		cp = evalstr(yylval.cp, 0);
X
X		/* write expanded input to another temp file */
X		h = maketemp(ATEMP);
X		h->next = e.temps; e.temps = h;
X		if (h == NULL)
X			error();
X		f = fopen(h->name, "w+");
X		if (f == NULL)
X			error();
X		setvbuf(f, (char *)NULL, _IOFBF, BUFSIZ);
X		fputs(cp, f);
X		rewind(f);
X
X		quitenv();
X	}
X	fd = dup(fileno(f));
X	fclose(f);
X	return fd;
X}
X
Xstatic void
Xecho(vp, ap)
X	register char **vp, **ap;
X{
X	shellf("+");
X	while (*vp != NULL)
X		shellf(" %s", *vp++);
X	while (*ap != NULL)
X		shellf(" %s", *ap++);
X	shellf("\n");
X}
X
SHAR_EOF
true || echo 'restore of src/exec.c failed'
fi
# ============= src/jobs.c ==============
if test -f 'src/jobs.c' -a X"$1" != X"-c"; then
	echo 'x - skipping src/jobs.c (File already exists)'
else
echo 'x - extracting src/jobs.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'src/jobs.c' &&
X/*
X * Process and job control
X */
X
Xstatic char *RCSid = "$Header: /tmp/egisin/src/RCS/jobs.c,v 3.4 88/12/14 19:49:17 egisin Exp $";
X
X/*
X * based on version by Ron Natalie, BRL
X *
X * TODO:
X *	change Proc table to Job table, with array of pids.
X *	make %+ be jobs, %- be jobs->next.
X *	do not JFREE members of pipeline.
X *	consider procs[] related critical sections.
X *	signal(SIGCHLD, j_sigchld) should be
X *	sigaction(SIGCHLD, sigchld, NULL),
X *	with sigchld.sa_flags = SA_RESTART.
X *	There is a simple work-around if there is no SA_RESTART.
X */
X
X#include <stddef.h>
X#include <string.h>
X#include <stdio.h>
X#include <errno.h>
X#include <unistd.h>
X#include <signal.h>
X#include <setjmp.h>
X#include <sys/types.h>
X#include <sys/times.h>
X#include <sys/wait.h>
X#if JOBS
X#if _BSD
X#include <sys/ioctl.h>
X#else
X#include "termios.h"
X#endif
X#endif
X#include "sh.h"
X#include "tree.h"
X
X#ifndef WIFCORED
X#define	WIFCORED(x)	(!!((x)&0x80)) /* non-standard */
X#endif
X
X/* as of P1003.1 Draft 12.3:
X *	pid_t getpgrp(void);		// Get process group id
X *	pid_t setsid(void);		// Create session and Set process group id
X *	int setpgid(pid_t pid, pid_t pgid); // Set process group id for job control
X */
X
X#if JOBS
X#if _BSD			/* _BSD 4.* */
X#define	setpgid(p, pg)	setpgrp(p, pg)
X#define	getpgid(p)	getpgrp(p)
X#define	tcsetpgrp(fd,p)	ioctl(fd, TIOCSPGRP, &(p))
X#else				/* POSIX-compatible */
X#define	getpgid(p)	getpgrp() /* 1003.1 stupidity */
X#define	killpg(p, s)	kill(-(p), s)
X#endif
X#endif
X
X#ifndef	SIGCHLD
X#define	SIGCHLD	SIGCLD
X#endif
X
Xtypedef struct Proc Proc;
Xstruct Proc {
X	Proc   *next;		/* `procs' list link */
X	int	job;		/* job number: %n */
X	short	Volatile state;	/* proc state */
X	short	Volatile notify; /* proc state has changed */
X	Proc   *prev;		/* prev member of pipeline */
X	pid_t	proc;		/* process id */
X	pid_t	pgrp;		/* process group if flag[FMONITOR] */
X	short	flags;		/* execute flags */
X	int	status;		/* wait status */
X	clock_t	utime, stime;	/* user/system time when JDONE */
X	char	com [48];	/* command */
X};
X
X/* proc states */
X#define	JFREE	0		/* unused proc */
X#define	JRUN	1		/* foreground */
X#define JEXIT	2		/* exit termination */
X#define	JSIGNAL	3		/* signal termination */
X#define	JSTOP	4		/* stopped */
X
Xstatic	Proc *procs = NULL;	/* job/process table */
X
Xclock_t	j_utime, j_stime;	/* user and system time for last job a-waited */
X#if JOBS
Xstatic	int	sm_default, sm_sigchld;	/* signal masks */
Xstatic	int	our_pgrp;		/* shell's pgrp */
X#endif
Xstatic	Proc   *j_lastj;		/* last proc created by exchild */
Xstatic	int	j_lastjob = 0;		/* last created job */
Xstatic	int	j_current = 0;		/* current job */
Xstatic	int	j_previous = 0;		/* previous job */
X
Xstatic	int	j_newjob ARGS((void));
Xstatic	void	j_print ARGS((Proc *j));
Xstatic	Proc   *j_search ARGS((int job));
Xstatic	int	j_waitj ARGS((Proc *j, int intr));
Xstatic	void	j_sigchld ARGS((int sig));
X	
X/* initialize job control */
Xvoid
Xj_init()
X{
X#if JOBS
X#ifdef NTTYDISC
X	int ldisc = NTTYDISC;	/* BSD brain damage */
X
X	if (ttyfd >= 0)
X		ioctl(ttyfd, TIOCSETD, &ldisc);
X#endif
X	our_pgrp = getpgid(0);
X	sm_default = 0;
X	sm_sigchld = sigmask(SIGCHLD);
X#endif
X}
X
X/* job cleanup before shell exit */
Xvoid
Xj_exit()
X{
X	register Proc *j;
X	int killed = 0;
X
X#if JOBS
X	/* kill stopped jobs */
X	for (j = procs; j != NULL; j = j->next)
X		if (j->state == JSTOP) {
X			killed ++;
X			killpg(j->pgrp, SIGHUP);
X			killpg(j->pgrp, SIGCONT);
X		}
X	if (killed)
X		sleep(1);
X#endif
X	j_notify();
X
X#if JOBS
X	if (flag[FMONITOR]) {
X		flag[FMONITOR] = 0;
X		j_change();
X	}
X#endif
X}
X
X#if JOBS
X/* turn job control on or off according to flag[FMONITOR] */
Xvoid
Xj_change()
X{
X	static handler_t old_tstp, old_ttin, old_ttou;
X
X	if (flag[FMONITOR]) {
X		if (ttyfd < 0) {
X			flag[FMONITOR] = 0;
X			shellf("job control requires tty\n");
X			return;
X		}
X		(void) signal(SIGCHLD, j_sigchld);
X		sigtraps[SIGCHLD].sig_dfl = 1; /* restore on fork */
X		old_tstp = signal(SIGTSTP, SIG_IGN);
X		sigtraps[SIGTSTP].sig_dfl = 1;
X		old_ttin = signal(SIGTTIN, SIG_IGN);
X		sigtraps[SIGTTIN].sig_dfl = 1;
X		old_ttou = signal(SIGTTOU, SIG_IGN);
X		sigtraps[SIGTTOU].sig_dfl = 1;
X		sigsetmask(sm_default);
X		tcsetpgrp(ttyfd, our_pgrp);
X	} else {
X		(void) signal(SIGCHLD, SIG_DFL);
X		(void) signal(SIGTSTP, old_tstp);
X		sigtraps[SIGTSTP].sig_dfl = 0;
X		(void) signal(SIGTTIN, old_ttin);
X		sigtraps[SIGTTIN].sig_dfl = 0;
X		(void) signal(SIGTTOU, old_ttou);
X		sigtraps[SIGTTOU].sig_dfl = 0;
X	}
X}
X#endif
X
X/* execute tree in child subprocess */
Xint
Xexchild(t, flags)
X	struct op *t;
X	int flags;
X{
X	register int i;
X	register Proc *j;
X	int rv = 0;
X
X	flags &= ~XFORK;
X	if ((flags&XEXEC))
X		return execute(t, flags);
X
X	/* get free Proc entry */
X	for (j = procs; j != NULL; j = j->next)
X		if (j->state == JFREE)
X			goto Found;
X	j = alloc(sizeof(Proc), APERM);
X	j->next = procs;
X	j->state = JFREE;
X	procs = j;
X  Found:
X
X	j->prev = ((flags&XPIPEI)) ? j_lastj : NULL;
X	j->proc = j->pgrp = 0;
X	j->flags = flags;
X	j->job = (flags&XPIPEI) ? j_lastjob : j_newjob();
X	snptreef(j->com, sizeof(j->com), "%T", t); /* save proc's command */
X	j->com[sizeof(j->com)-1] = '\0';
X	j->state = JRUN;
X
X	/* stdio buffer must be flushed and invalidated */
X	for (i = 0; i < NUFILE; i++)
X		flushshf(i);
X
X	/* create child process */
X	if ((i = fork()) < 0) {
X		/* todo: try a few times with exp-incr delay */
X		j->state = JFREE;
X		errorf("cannot fork - try again\n");
X	}
X	j->proc = (i != 0) ? i : getpid();
X
X#if JOBS
X	/* job control set up */
X	if (flag[FMONITOR] && !(flags&XXCOM)) {
X		j->pgrp = !(flags&XPIPEI) ? j->proc : j_lastj->pgrp;
X		/* do in both parent and child to avoid fork race condition */
X		if (!(flags&XBGND))
X			tcsetpgrp(ttyfd, j->pgrp); /* could be trouble */
X		setpgid(j->proc, j->pgrp);
X	}
X#endif
X	j_lastj = j;
X
X	if (i == 0) {		/* child */
X		e.oenv = NULL;
X		if (flag[FTALKING])
X			restoresigs();
X		if ((flags&XBGND) && !flag[FMONITOR]) {
X			signal(SIGINT, SIG_IGN);
X			signal(SIGQUIT, SIG_IGN);
X			if (flag[FTALKING])
X				signal(SIGTERM, SIG_DFL);
X			i = open("/dev/null", 0);
X			(void) dup2(i, 0);
X			close(i);
X		}
X		for (j = procs; j != NULL; j = j->next)
X			j->state = JFREE;
X		ttyfd = -1;
X		flag[FMONITOR] = flag[FTALKING] = 0;
X		cleartraps();
X		execute(t, flags|XEXEC); /* no return */
X		/* NOTREACHED */
X	}
X
X	/* shell (parent) stuff */
X	if ((flags&XBGND)) { /* async statement */
X		async = j->proc;
X		j_previous = j_current;
X		j_current = j->job;
X		if (flag[FTALKING])
X			j_print(j);
X	} else {		/* sync statement */
X		if (!(flags&XPIPE))
X			rv = j_waitj(j, 0);
X	}
X
X	return rv;
X}
X
X/* wait for last job: pipeline or $() sub-process */
Xint
Xwaitlast()
X{
X	return j_waitj(j_lastj, 0);
X}
X
X/* wait for job to complete or change state */
Xstatic int
Xj_waitj(aj, intr)
X	Proc *aj;
X	int intr;		/* interruptable */
X{
X	register Proc *j;
X	int rv = 1;
X	int ttysig = 0;
X
X#if JOBS
X	if (flag[FMONITOR])
X		sigsetmask(sm_sigchld);
X#endif
X	/* wait for all members of pipeline */
X	for (j = aj; j != NULL; j = j->prev) {
X		/* wait for job to finish, stop, or ^C of built-in wait */
X		while (j->state == JRUN) {
X#if JOBS
X			if (flag[FMONITOR])
X				sigpause(sm_default);
X			else
X#endif
X				j_sigchld(SIGCHLD);
X			if (sigtraps[SIGINT].set && intr)
X				goto Break;
X		}
X		if (j->state == JEXIT) { /* exit termination */
X			if (!(j->flags&XPIPEO))
X				rv = WEXITSTATUS(j->status);
X			j->notify = 0;
X		} else
X		if (j->state == JSIGNAL) { /* signalled to death */
X			if (!(j->flags&XPIPEO))
X				rv = 0x80 + WTERMSIG(j->status);
X			if (WTERMSIG(j->status) == SIGINT ||
X			    WTERMSIG(j->status) == SIGPIPE && (j->flags&XPIPEO))
X				j->notify = 0;
X			if (WTERMSIG(j->status) == SIGINT ||
X			    WTERMSIG(j->status) == SIGQUIT)
X				ttysig = 1;
X		} else
X#if JOBS
X		if (j->state == JSTOP)
X			if (WSTOPSIG(j->status) == SIGTSTP)
X				ttysig = 1;
X#else
X		;
X#endif
X	}
X
X	/* compute total child time for time statement */
X	for (j = aj; j != NULL; j = j->prev)
X		j_utime += j->utime, j_stime += j->stime;
X
X	/* find new current job */
X#if JOBS
X	if (aj->state == JSTOP) {
X		j_previous = j_current;
X		j_current = aj->job;
X	} else {
X#else
X	if (1) {
X#endif
X		int hijob = 0;
X
X		/* todo: this needs to be done in j_notify */
X		/* todo: figure out what to do with j_previous */
X		j_current = 0;
X		for (j = procs; j != NULL; j = j->next)
X			if ((j->state == JRUN || j->state == JSTOP)
X			    && j->job > hijob) {
X				hijob = j->job;
X				j_current = j->job;
X			}
X	}
X
X  Break:
X#if JOBS
X	if (flag[FMONITOR]) {
X		/* reset shell job control state */
X		sigsetmask(sm_default);
X		tcsetpgrp(ttyfd, our_pgrp);
X	}
X#endif
X	if (ttysig)
X		fputc('\n', shlout);
X	j_notify();
X
X	return rv;
X}
X
X/* SIGCHLD handler to reap children */
Xstatic void
Xj_sigchld(sig)
X	int sig;
X{
X	int errno_ = errno;
X	struct tms t0, t1;
X
X	(void) times(&t0);
X	do {
X		register Proc *j;
X		int pid, status;
X#if JOBS
X		if (flag[FMONITOR])
X			pid = waitpid(-1, &status, (WNOHANG|WUNTRACED));
X		else
X#endif
X			pid = wait(&status);
X		if (pid <= 0)	/* return if would block (0) ... */
X			break;	/* ... or no children or interrupted (-1) */
X		(void) times(&t1);
X
X		for (j = procs; j != NULL; j = j->next)
X			if (j->state != JFREE && pid == j->proc)
X				goto Found;
X		continue;
X
X	  Found:
X		j->notify = 1;
X		j->status = status;
X#if JOBS
X		if (WIFSTOPPED(status))
X			j->state = JSTOP;
X		else
X#endif
X		if (WIFEXITED(status))
X			j->state = JEXIT;
X		else
X		if (WIFSIGNALED(status))
X			j->state = JSIGNAL;
X
X		/* compute child's time */
X		/* todo: what does a stopped job do? */
X		j->utime = t1.tms_cutime - t0.tms_cutime;
X		j->stime = t1.tms_cstime - t0.tms_cstime;
X		t0 = t1;
X#if JOBS
X	} while (flag[FMONITOR]);
X#else
X	} while (0);		/* only once if wait()ing */
X#endif
X
X	errno = errno_;
X}
X
X/* wait for child, interruptable */
Xint
Xwaitfor(job)
X	int job;
X{
X	register Proc *j;
X
X	if (job == 0 && j_current == 0)
X		errorf("no current job\n");
X	j = j_search((job == 0) ? j_current : job);
X	if (j == NULL)
X		errorf("no such job: %d\n", job);
X	if (flag[FTALKING])
X		j_print(j);
X	if (e.interactive) {	/* flush stdout, shlout */
X		fflush(shf[1]);
X		fflush(shf[2]);
X	}
X	return j_waitj(j, 1);
X}
X
X/* kill (built-in) a job */
Xvoid
Xj_kill(job, sig)
X	int job;
X	int sig;
X{
X	register Proc *j;
X
X	j = j_search(job);
X	if (j == NULL)
X		errorf("cannot find job\n");
X	if (j->pgrp == 0) {	/* !flag[FMONITOR] */
X		if (kill(j->proc, sig) < 0) /* todo: all member of pipeline */
X			errorf("kill: %s\n", strerror(errno));
X#if JOBS
X	} else {
X		if (sig == SIGTERM || sig == SIGHUP)
X			(void) killpg(j->pgrp, SIGCONT);
X		if (killpg(j->pgrp, sig) < 0)
X			errorf("killpg: %s\n", strerror(errno));
X#endif
X	}
X}
X
X#if JOBS
X
X/* fg and bg built-ins */
Xint
Xj_resume(job, bg)
X	int job;
X	int bg;
X{
X	register Proc *j; 
X	
X	j = j_search((job == 0) ? j_current : job);
X	if (j == NULL)
X		errorf("cannot find job\n", job);
X	if (j->pgrp == 0)
X		errorf("job not job-controlled\n");
X
X	j->state = JRUN;
X	j_print(j);
X	flushshf(2);
X
X	if (!bg)
X  		tcsetpgrp(ttyfd, j->pgrp); /* attach shell to job */
X	if (killpg(j->pgrp, SIGCONT) < 0)
X		errorf("cannot continue job %%%d\n", job);
X	if (!bg)
X		return j_waitj(j, 0);
X	return 0;
X}
X
X#endif
X
X/* list jobs for jobs built-in */
Xvoid
Xj_jobs()
X{
X	register Proc *j; 
X
X	for (j = procs; j != NULL; j = j->next)
X		if (j->state != JFREE)
X			j_print(j);
X}
X
X/* list jobs for top-level notification */
Xvoid
Xj_notify()
X{
X	register Proc *j; 
X
X	for (j = procs; j != NULL; j = j->next) {
X		if (j->state == JEXIT && !flag[FTALKING])
X			j->notify = 0;
X		if (j->state != JFREE && j->notify)
X			j_print(j);
X		if (j->state == JEXIT || j->state == JSIGNAL)
X			j->state = JFREE;
X		j->notify = 0;
X	}
X}
X
Xstatic void
Xj_print(j)
X	register Proc *j;
X{
X	char buf [64], *s = buf;
X
X	switch (j->state) {
X	  case JRUN:
X		s = "Running";
X		break;
X
X#if JOBS
X	  case JSTOP:
X		strcpy(buf, "Stopped ");
X		s = strchr(sigtraps[WSTOPSIG(j->status)].mess, '(');
X		if (s != NULL)
X			strcat(buf, s);
X		s = buf;
X		break;
X#endif
X
X	  case JEXIT: {
X		int rv;
X		rv = WEXITSTATUS(j->status);
X		sprintf(buf, "Done (%d)", rv);
X		if (rv == 0)
X			*strchr(buf, '(') = 0;
X		j->state = JFREE;
X		} break;
X
X	  case JSIGNAL: {
X		int sig = WTERMSIG(j->status);
X		char *n = sigtraps[sig].mess;
X		if (n != NULL)
X			sprintf(buf, "%s", n);
X		else
X			sprintf(buf, "Signal %d", sig);
X		if (WIFCORED(j->status))
X			strcat(buf, " - core dumped");
X		j->state = JFREE;
X		} break;
X
X	  default:
X		s = "Hideous job state";
X		j->state = JFREE;
X		break;
X	}
X	shellf("%%%-2d%c %5d %-20s %s%s\n", j->job,
X	       (j_current==j->job) ? '+' : (j_previous==j->job) ? '-' : ' ',
X	       j->proc, s, j->com, (j->flags&XPIPEO) ? "|" : "");
X}
X
X/* convert % sequence to job number */
Xint
Xj_lookup(cp)
X	char *cp;
X{
X	register Proc *j;
X	int len, job = 0;
X
X	if (*cp == '%')		/* leading % is optional */
X		cp++;
X	switch (*cp) {
X	  case '\0':
X	  case '+':
X		job = j_current;
X		break;
X
X	  case '-':
X		job = j_previous;
X		break;
X
X	  case '0': case '1': case '2': case '3': case '4':
X	  case '5': case '6': case '7': case '8': case '9': 
X		job = atoi(cp);
X		break;
X
X	  case '?':		/* %?string */
X		for (j = procs; j != NULL; j = j->next)
X			if (j->state != JFREE && strstr(j->com, cp+1) != NULL)
X				job = j->job;
X		break;
X
X	  default:		/* %string */
X		len = strlen(cp);
X		for (j = procs; j != NULL; j = j->next)
X			if (j->state != JFREE && strncmp(cp, j->com, len) == 0)
X				job = j->job;
X		break;
X	}
X	if (job == 0)
X		errorf("%s: no such job\n", cp);
X	return job;
X}
X
X/* are any stopped jobs ? */
X#if JOBS
Xint
Xj_stopped()
X{
X	register Proc *j; 
X
X	for (j = procs; j != NULL; j = j->next)
X		if (j->state == JSTOP)
X			return 1;
X	return 0;
X}
X#endif
X
X/* create new job number */
Xstatic int
Xj_newjob()
X{
X	register Proc *j; 
X	register int max = 0;
X	
X	j_lastjob ++;
X	for (j = procs; j != NULL; j = j->next)
X		if (j->state != JFREE && j->job)
X			if (j->job > max)
X				max = j->job;
X	if (j_lastjob > max)
X		j_lastjob = max + 1;
X	return j_lastjob;
X}
X
X/* search for job by job number */
Xstatic Proc *
Xj_search(job)
X	int job;
X{
X	register Proc *j;
X
X	for (j = procs; j != NULL; j = j->next)
X		if (j->state != JFREE && job == j->job && !(j->flags&XPIPEO))
X			return j;
X	return NULL;
X}
X
SHAR_EOF
true || echo 'restore of src/jobs.c failed'
fi
# ============= src/c_sh.c ==============
if test -f 'src/c_sh.c' -a X"$1" != X"-c"; then
	echo 'x - skipping src/c_sh.c (File already exists)'
else
echo 'x - extracting src/c_sh.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'src/c_sh.c' &&
X/*
X * built-in Bourne commands
X */
X
Xstatic char *RCSid = "Id: /u/egisin/sh/src/RCS/c_sh.c,v 3.1 88/11/03 09:14:31 egisin Exp $";
X
X#include <stddef.h>
X#include <stdio.h>
X#include <string.h>
X#include <errno.h>
X#include <signal.h>
X#include <setjmp.h>
X#include <sys/times.h>
X#include <unistd.h>		/* getcwd */
X#include "sh.h"
X#include "lex.h"
X#include "tree.h"
X#include "table.h"
X
Xstatic	char *clocktos();
X
Xint
Xc_label(wp)
X	char **wp;
X{
X	return 0;
X}
X
X/* todo: add symlink hacks */
Xint
Xc_cd(wp)
X	register char **wp;
X{
X	char path [PATH];
X	register char *cp;
X	register struct tbl *vp;
X
X	if ((cp = wp[1]) == NULL && (cp = strval(global("HOME"))) == NULL)
X		errorf("no home directory");
X	if (strcmp(cp, "-") == 0) {
X		cp = strval(global("OLDPWD"));
X		shellf("%s\n", cp);
X	}
X	if (chdir(cp) < 0)
X		errorf("%s: bad directory\n", cp);
X	flushcom(0);
X
X	/* maintain $PWD and $OLDPWD */
X	vp = global("PWD");
X	cp = strval(vp);
X	if (cp != null)
X		setstr(global("OLDPWD"), cp);
X	cp = getcwd(path, (size_t)PATH);
X	if (cp == NULL)
X		unset(vp);
X	else
X		setstr(vp, cp);
X
X	return 0;
X}
X
Xint
Xc_shift(wp)
X	register char **wp;
X{
X	register struct block *l = e.loc;
X	register int n;
X
X	n = wp[1] ? evaluate(wp[1]) : 1;
X	if (l->argc < n) {
X		errorf("nothing to shift\n");
X		return (1);
X	}
X	l->argv[n] = l->argv[0];
X	l->argv += n;
X	l->argc -= n;
X	return 0;
X}
X
Xint
Xc_umask(wp)
X	register char **wp;
X{
X	register int i;
X	register char *cp;
X
X	if ((cp = wp[1]) == NULL) {
X		i = umask(0);
X		umask(i);
X		printf("%#3.3o\n", i);	/* should this be shell output? */
X	} else {
X		for (i = 0; *cp>='0' && *cp<='7'; cp++)
X			i = i*8 + (*cp-'0');
X		umask(i);
X	}
X	return 0;
X}
X
Xint
Xc_dot(wp)
X	char **wp;
X{
X	char *file, *cp;
X
X	if ((cp = wp[1]) == NULL)
X		return 0;
X	file = search(cp, path, 0);
X	if (file == NULL)
X		errorf("%s: not found\n", cp);
X	if (include(file))
X		return exstat;
X	return -1;
X}
X
Xint
Xc_wait(wp)
X	char **wp;
X{
X	register char *cp;
X
X	wp++;
X	cp = *wp;
X	if (cp == NULL) cp = "%";
X	/* todo: print status ? */
X	return waitfor(j_lookup(cp));
X}
X
Xint
Xc_read(wp)
X	register char **wp;
X{
X	register int c = 0;
X	FILE *f = stdin;
X	int expand = 1;
X	register char *cp;
X
X	for (wp++; (cp = *wp) != NULL && *cp++ == '-'; wp++) {
X		while (*cp) switch (*cp++) {
X		  case 'e':
X			expand = 1;
X			break;
X		  case 'r':
X			expand = 0;
X			break;
X		  case 'u':
X			if (!digit(*cp) || (f = shf[*cp++-'0']) == NULL)
X				errorf("bad -u argument\n");
X			break;
X		}
X	}
X
X	if (*wp == NULL)
X		errorf("missing name\n");
X	if ((cp = strchr(*wp, '?')) != NULL) {
X		*cp = 0;
X		if (flag[FTALKING]) {
X			shellf("%s ", cp+1);
X			fflush(shlout);
X		}
X	}
X
X	for (; *wp != NULL; wp++) {
X		for (cp = line; cp <= line+LINE; ) {
X			if (c == '\n')
X				break;
X			c = getc(f);
X			if (c == EOF)
X				return 1;
X			if (expand && c == '\\') {
X				c = getc(f);
X				if (c == '\n')
X					c = 0;
X				else
X					*cp++ = c;
X				continue;
X			}
X			if (c == '\n' || wp[1] && ctype(c, C_IFS))
X				break;
X			*cp++ = c;
X		}
X		*cp = 0;
X		setstr(global(*wp), line);
X	}
X	return 0;
X}
X
Xint
Xc_eval(wp)
X	register char **wp;
X{
X	register struct source *s;
X
X	s = pushs(SWORDS);
X	s->u.strv = wp+1;
X	return shell(s);
X}
X
Xvoid setsig ARGS((struct trap *p, handler_t f));
X
Xint
Xc_trap(wp)
X	register char **wp;
X{
X	int i;
X	char *s;
X	register struct trap *p;
X
X	wp++;
X	if (*wp == NULL) {
X		for (p = sigtraps, i = SIGNALS; --i >= 0; p++) {
X			if (p->trap != NULL)
X				shellf("%s: %s\n", p->name, p->trap);
X		}
X		return 0;
X	}
X
X	s = (gettrap(*wp) == NULL) ? *wp++ : NULL; /* get command */
X	if (s != NULL && s[0] == '-' && s[1] == '\0')
X		s = NULL;
X
X	/* set/clear traps */
X	while (*wp != NULL) {
X		p = gettrap(*wp++);
X		if (p == NULL)
X			errorf("trap: bad signal %s\n", wp[-1]);
X		if (p->trap != NULL)
X			afree((Void*)p->trap, APERM);
X		p->trap = NULL;
X		if (s != NULL) {
X			if (strlen(s) != 0) {
X				p->trap = strsave(s, APERM);
X				setsig(p, trapsig);
X			} else
X				setsig(p, (handler_t)SIG_IGN);
X		} else
X			/* todo: restore to orginal value */
X		    setsig(p,
X		       (p->signal==SIGINT || p->signal==SIGQUIT) && flag[FTALKING]
X			   ? (handler_t)SIG_IGN : (handler_t)SIG_DFL);
X	}
X	return 0;
X}
X
Xvoid
Xsetsig(p, f)
X	register struct trap *p;
X	void (*f)();
X{
X	if (p->signal == 0)
X		return;
X	if (signal(p->signal, SIG_IGN) != SIG_IGN || p->ourtrap) {
X		p->ourtrap = 1;
X		signal(p->signal, f);
X	}
X}
X
Xint
Xc_return(wp)
X	char **wp;
X{
X	wp++;
X	if (*wp != NULL)
X		exstat = getn(*wp);
X	quitenv();		/* pop E_TCOM */
X	while (e.type == E_LOOP || e.type == E_EXEC)
X		quitenv();
X	if (e.type == E_FUNC)
X		longjmp(e.jbuf, 1);
X	leave(exstat);
X}
X
Xint
Xc_brkcont(wp)
X	register char **wp;
X{
X	int quit;
X
X	quit = wp[1] == NULL ? 1 : getn(wp[1]);
X	quitenv();		/* pop E_TCOM */
X	while (e.type == E_LOOP || e.type == E_EXEC) {
X		if (e.type == E_LOOP && --quit <= 0)
X			longjmp(e.jbuf, (*wp[0] == 'b') ? LBREAK : LCONTIN);
X		quitenv();
X	}
X	errorf("cannot %s\n", wp[0]);
X}
X
Xint
Xc_exit(wp)
X	char **wp;
X{
X	register char *cp;
X
X	e.oenv = NULL;
X	if ((cp = wp[1]) != NULL)
X		exstat = getn(cp);
X#if JOBS
X	if (flag[FMONITOR] && j_stopped()) /* todo: only once */
X		errorf("There are stopped jobs\n");
X#endif
X	leave(exstat);
X}
X
Xint
Xc_set(wp)
X	register char **wp;
X{
X	struct block *l = e.loc;
X	register struct tbl *vp, **p;
X	register char **owp = wp;
X	register char *cp;
X	int old_fmonitor = flag[FMONITOR];
X
X	if ((cp = *++wp) == NULL) {
X		static char * Const args [] = {"set", "-", NULL};
X		extern int c_typeset ARGS((char **args));
X		return c_typeset(args);
X	}
X	
X	for (; (cp = *wp) != NULL && (*cp == '-' || *cp == '+');) {
X		int i, n = *cp++ == '-'; /* set or clear flag */
X		wp++;
X		if (*cp == '\0') {
X			if (n)
X				flag[FXTRACE] = flag[FVERBOSE] = 0;
X			break;
X		}
X		if (*cp == '-')
X			goto setargs;
X		for (; *cp != '\0'; cp++)
X			if (*cp == 'o') {
X				if (*wp == NULL) {
X					printoptions();
X					return 0;
X				}
X				i = option(*wp++);
X				if (i == 0)
X					shellf("%s: unknown option\n", *--wp);
X				flag[i] = n;
X			} else if (*cp>='a' && *cp<='z')
X				flag[FLAG(*cp)] = n;
X			else
X				errorf("%c: bad flag\n", *cp);
X		if (flag[FTALKING])
X			flag[FERREXIT] = 0;
X	}
X
X#if JOBS
X	if (old_fmonitor != flag[FMONITOR])
X		j_change();
X#endif
X
X	/* set $# and $* */
X	if (*wp != NULL) {
X	  setargs:
X		owp = --wp;
X		wp[0] = l->argv[0]; /* save $0 */
X		while (*++wp != NULL)
X			*wp = strsave(*wp, &l->area);
X		l->argc = wp - owp - 1;
X		l->argv = (char **) alloc(sizeofN(char *, l->argc+2), &l->area);
X		for (wp = l->argv; (*wp++ = *owp++) != NULL; )
X			;
X		resetopts();
X	}
X	return 0;
X}
X
Xint
Xc_unset(wp)
X	register char **wp;
X{
X	register char *id;
X	int flagf = 0;
X
X	for (wp++; (id = *wp) != NULL && *id == '-'; wp++)
X		if (*++id == 'f')
X			flagf++;
X	for (; (id = *wp) != NULL; wp++)
X		if (!flagf) {	/* unset variable */
X			unset(local(id));
X		} else {	/* unset function */
X			register struct tbl *tp;
X			tp = tsearch(&e.loc->funs, id, hash(id));
X			if (tp != NULL)
X				define(tp, (struct op *)NULL);
X		}
X	return 0;
X}
X
Xint
Xc_ulimit(wp)
X	register char **wp;
X{
X	extern int do_ulimit();
X
X	return do_ulimit(wp[1], wp[2]);
X}
X
Xint
Xc_times(wp)
X	char **wp;
X{
X	struct tms all;
X
X	(void) times(&all);
X	printf("Shell: ");
X	printf("%8s user ", clocktos(all.tms_utime));
X	printf("%8s system\n", clocktos(all.tms_stime));
X	printf("Kids:  ");
X	printf("%8s user ", clocktos(all.tms_cutime));
X	printf("%8s system\n", clocktos(all.tms_cstime));
X
X	return 0;
X}
X
X/*
X * time pipeline (really a statement, not a built-in comman)
X */
Xint
Xtimex(t, f)
X	struct op *t;
X	int f;
X{
X	int rv;
X	struct tms t0, t1;
X	clock_t t0t, t1t;
X	extern clock_t j_utime, j_stime; /* computed by j_wait */
X
X	j_utime = j_stime = 0;
X	t0t = times(&t0);
X	rv = execute(t->left, f);
X	t1t = times(&t1);
X
X	shellf("%8s real ", clocktos(t1t - t0t));
X	shellf("%8s user ",
X	       clocktos(t1.tms_utime - t0.tms_utime + j_utime));
X	shellf("%8s system ",
X	       clocktos(t1.tms_stime - t0.tms_stime + j_stime));
X	shellf("\n");
X
X	return rv;
X}
X
Xstatic char *
Xclocktos(t)
X	clock_t t;
X{
X	static char temp[20];
X	register int i;
X	register char *cp = temp + sizeof(temp);
X
X#if CLK_TCK != 100		/* convert to 1/100'ths */
X	t = (t < 1000000000/CLK_TCK) ?
X		(t * 100) / CLK_TCK : (t / CLK_TCK) * 100;
X#endif
X
X	*--cp = '\0';
X	*--cp = 's';
X	for (i = -2; i <= 0 || t > 0; i++) {
X		if (i == 0)
X			*--cp = '.';
X		*--cp = '0' + (char)(t%10);
X		t /= 10;
X	}
X	return cp;
X}
X
X/* dummy function, special case in comexec() */
Xint
Xc_exec(wp)
X	char ** wp;
X{
X	return 0;
X}
X
X/* dummy function, special case in comexec() */
Xint
Xc_builtin(wp)
X	char ** wp;
X{
X	return 0;
X}
X
Xextern	int c_test();		/* in test.c */
X
XConst struct builtin shbuiltins [] = {
X	{"=:", c_label},
X	{"=.", c_dot},
X	{"[", c_test},
X	{"=cd", c_cd},
X	{"builtin", c_builtin},
X	{"=exec", c_exec},
X	{"=shift", c_shift},
X	{"wait", c_wait},
X	{"read", c_read},
X	{"=eval", c_eval},
X	{"trap", c_trap},
X	{"break", c_brkcont},
X	{"continue", c_brkcont},
X	{"exit", c_exit},
X	{"=return", c_return},
X	{"=set", c_set},
X	{"=unset", c_unset},
X	{"umask", c_umask},
X	{"test", c_test},
X	{"times", c_times},
X	{"ulimit", c_ulimit},
X	{NULL, NULL}
X};
X
SHAR_EOF
true || echo 'restore of src/c_sh.c failed'
fi
true || echo 'restore of src/c_ksh.c failed'
echo End of part 4, continue with part 5
exit 0



More information about the Alt.sources mailing list