netnews@netcom.UUCP (USENET Administration) (12/12/90)
#!/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