ast@cs.vu.nl (Andy Tanenbaum) (08/05/87)
Here is the 1.2 shell in 2 parts. It has a number of improvements over the 1.1 shell, and should be fairly close to the shell to be used on the Atari MINIX system. I have just noticed a bug in it that I can't find. The bug may well also have been in the 1.1 shell, but I can't check it out because 1.1 doesn't run on the hard disk I used to test 1.2. Anyway, I made a shell script 'doit' consisting of: ck /usr/ast/minix/kernel ck /usr/ast/minix/kernel/MINIX ck /usr/ast/minix/lib ... In short, ck is run with each directory on the hard disk as an argument. The shell script 'ck' is: ls -l * */* >/dev/null 2>&1 Finally, from the console I started the command while true do sh -v doit done >log 2>&1 This command generates quite a few shells floating around. It runs fine for a few minutes, but when doit hits the line containing ck /usr/ast/minix/lib it hangs. You can't kill it with DEL or CTRL-\. F1 works, and from looking at several samples, the shell seems to be looping in getcell or sometimes in execve. I suspect that it ran out of memory somehow. When I chmem'd from the standard 8000 bytes to 16000, the problem remained. After trying this test several times and having to reboot after each test, I made a small change to tty.c and mm/signal.c. Now the F9 key does the equivalent of the super-user typing: kill -1 9. In other words, it sends a signal 9 to every process in the system. While this is definitely a sledge hammer approach, it de-hangs the system instantly. It also kills your shell and logs you out. So be it. I would prefer to have found the bug in the shell, but not having done so, this will have to do for the time being. The F9 key will be in 1.2, which I am almost done with. I will post the definitive diff listing between 1.1 and 1.2 in perhaps a week. If anyone can reproduce, find, and fix the shell bug, please post it. Andy Tanenbaum ------------------------------MINIX 1.2 shell (part 1 of 2) ------------- : This is a shar archive. Extract with sh, not csh. : This archive ends with exit, so do not worry about trailing junk. : --------------------------- cut here -------------------------- PATH=/bin:/usr/bin echo Extracting \m\a\k\e\f\i\l\e sed 's/^X//' > \m\a\k\e\f\i\l\e << '+ END-OF-FILE '\m\a\k\e\f\i\l\e XCFLAGS=-F Xl=../../lib X Xshobj = sh1.s sh2.s sh3.s sh4.s sh5.s sh6.s Xsh: $(shobj) sh.h X @asld -i -o sh -T. $l/crtso.s $(shobj) $l/libc.a $l/end.s X @chmem =8000 sh X + END-OF-FILE makefile chmod 'u=rw,g=r,o=r' \m\a\k\e\f\i\l\e set `sum \m\a\k\e\f\i\l\e` sum=$1 case $sum in 45952) :;; *) echo 'Bad sum in '\m\a\k\e\f\i\l\e >&2 esac echo Extracting \s\h\.\h sed 's/^X//' > \s\h\.\h << '+ END-OF-FILE '\s\h\.\h X/* -------- sh.h -------- */ X/* X * shell X */ X X#define NULL 0 X#define LINELIM 1000 X#define NPUSH 8 /* limit to input nesting */ X X#define NOFILE 20 /* Number of open files */ X#define NUFILE 10 /* Number of user-accessible files */ X#define FDBASE 10 /* First file usable by Shell */ X X/* X * values returned by wait X */ X#define WAITSIG(s) ((s)&0177) X#define WAITVAL(s) (((s)>>8)&0377) X#define WAITCORE(s) (((s)&0200)!=0) X X/* X * library and system defintions X */ Xtypedef int xint; /* base type of jmp_buf, for broken compilers */ X X/* X * shell components X */ X/* #include "area.h" */ X/* #include "word.h" */ X/* #include "io.h" */ X/* #include "var.h" */ X X#define QUOTE 0200 X X#define NOBLOCK ((struct op *)NULL) X#define NOWORD ((char *)NULL) X#define NOWORDS ((char **)NULL) X#define NOPIPE ((int *)NULL) X X/* X * Description of a command or an operation on commands. X * Might eventually use a union. X */ Xstruct op { X int type; /* operation type, see below */ X char **words; /* arguments to a command */ X struct ioword **ioact; /* IO actions (eg, < > >>) */ X struct op *left; X struct op *right; X char *str; /* identifier for case and for */ X}; X X#define TCOM 1 /* command */ X#define TPAREN 2 /* (c-list) */ X#define TPIPE 3 /* a | b */ X#define TLIST 4 /* a [&;] b */ X#define TOR 5 /* || */ X#define TAND 6 /* && */ X#define TFOR 7 X#define TDO 8 X#define TCASE 9 X#define TIF 10 X#define TWHILE 11 X#define TUNTIL 12 X#define TELIF 13 X#define TPAT 14 /* pattern in case */ X#define TBRACE 15 /* {c-list} */ X#define TASYNC 16 /* c & */ X X/* X * actions determining the environment of a process X */ X#define BIT(i) (1<<(i)) X#define FEXEC BIT(0) /* execute without forking */ X X/* X * flags to control evaluation of words X */ X#define DOSUB 1 /* interpret $, `, and quotes */ X#define DOBLANK 2 /* perform blank interpretation */ X#define DOGLOB 4 /* interpret [?* */ X#define DOKEY 8 /* move words with `=' to 2nd arg. list */ X#define DOTRIM 16 /* trim resulting string */ X X#define DOALL (DOSUB|DOBLANK|DOGLOB|DOKEY|DOTRIM) X XExtern char **dolv; XExtern int dolc; XExtern int exstat; XExtern char gflg; XExtern int talking; /* interactive (talking-type wireless) */ XExtern int execflg; XExtern int multiline; /* \n changed to ; */ XExtern struct op *outtree; /* result from parser */ X XExtern xint *failpt; XExtern xint *errpt; X Xstruct brkcon { X jmp_buf brkpt; X struct brkcon *nextlev; X} ; XExtern struct brkcon *brklist; XExtern int isbreak; X X/* X * redirection X */ Xstruct ioword { X short io_unit; /* unit affected */ X short io_flag; /* action (below) */ X union { X char *io_name; /* file name */ X struct block *io_here; /* here structure pointer */ X } io_un; X}; X#define IOREAD 1 /* < */ X#define IOHERE 2 /* << (here file) */ X#define IOWRITE 4 /* > */ X#define IOCAT 8 /* >> */ X#define IOXHERE 16 /* ${}, ` in << */ X#define IODUP 32 /* >&digit */ X#define IOCLOSE 64 /* >&- */ X X#define IODEFAULT (-1) /* token for default IO unit */ X XExtern struct wdblock *wdlist; XExtern struct wdblock *iolist; X X/* X * parsing & execution environment X */ Xextern struct env { X char *linep; X struct io *iobase; X struct io *iop; X xint *errpt; X int iofd; X struct env *oenv; X} e; X X/* X * flags: X * -e: quit on error X * -k: look for name=value everywhere on command line X * -n: no execution X * -t: exit after reading and executing one command X * -v: echo as read X * -x: trace X * -u: unset variables net diagnostic X */ Xextern char *flag; X Xextern char *null; /* null value for variable */ Xextern int intr; /* interrupt pending */ X XExtern char *trap[NSIG]; XExtern char ourtrap[NSIG]; XExtern int trapset; /* trap pending */ X Xextern int inword; /* defer traps and interrupts */ X XExtern int yynerrs; /* yacc */ X XExtern char line[LINELIM]; Xextern char *elinep; X X/* X * other functions X */ Xint (*inbuilt())(); /* find builtin command */ Xchar *rexecve(); Xchar *space(); Xchar *getwd(); Xchar *strsave(); Xchar *evalstr(); Xchar *putn(); Xchar *itoa(); Xchar *unquote(); Xstruct var *lookup(); Xstruct wdblock *add2args(); Xstruct wdblock *glob(); Xchar **makenv(); Xstruct ioword *addio(); Xchar **eval(); Xint setstatus(); Xint waitfor(); X Xint onintr(); /* SIGINT handler */ X X/* X * error handling X */ Xvoid leave(); /* abort shell (or fail in subshell) */ Xvoid fail(); /* fail but return to process next command */ Xint sig(); /* default signal handler */ X X/* X * library functions and system calls X */ Xlong lseek(); Xchar *strncpy(); Xint strlen(); Xextern int errno; X X/* -------- var.h -------- */ X Xstruct var { X char *value; X char *name; X struct var *next; X char status; X}; X#define COPYV 1 /* flag to setval, suggesting copy */ X#define RONLY 01 /* variable is read-only */ X#define EXPORT 02 /* variable is to be exported */ X#define GETCELL 04 /* name & value space was got with getcell */ X XExtern struct var *vlist; /* dictionary */ X XExtern struct var *homedir; /* home directory */ XExtern struct var *prompt; /* main prompt */ XExtern struct var *cprompt; /* continuation prompt */ XExtern struct var *path; /* search path for commands */ XExtern struct var *shell; /* shell to interpret command files */ XExtern struct var *ifs; /* field separators */ X Xstruct var *lookup(/* char *s */); Xvoid setval(/* struct var *, char * */); Xvoid nameval(/* struct var *, char *val, *name */); Xvoid export(/* struct var * */); Xvoid ronly(/* struct var * */); Xint isassign(/* char *s */); Xint checkname(/* char *name */); Xint assign(/* char *s, int copyflag */); Xvoid putvlist(/* int key, int fd */); Xint eqname(/* char *n1, char *n2 */); X X/* -------- io.h -------- */ X/* possible arguments to an IO function */ Xstruct ioarg { X char *aword; X char **awordlist; X int afile; /* file descriptor */ X}; X X/* an input generator's state */ Xstruct io { X int (*iofn)(); X struct ioarg arg; X int peekc; X char nlcount; /* for `'s */ X char xchar; /* for `'s */ X char task; /* reason for pushed IO */ X}; XExtern struct io iostack[NPUSH]; X#define XOTHER 0 /* none of the below */ X#define XDOLL 1 /* expanding ${} */ X#define XGRAVE 2 /* expanding `'s */ X#define XIO 4 /* file IO */ X#define XHERE 0x80 /* Any of the above inside a here document */ X#define XMASK 0x7f /* Get the actual task */ X X/* in substitution */ X#define INSUB() ((e.iop->task&XMASK)==XGRAVE||(e.iop->task&XMASK)==XDOLL) X X/* X * input generators for IO structure X */ Xint nlchar(); Xint strchar(); Xint filechar(); Xint linechar(); Xint nextchar(); Xint gravechar(); Xint qgravechar(); Xint dolchar(); Xint wdchar(); X X/* X * IO functions X */ Xint getc(); Xint readc(); Xvoid unget(); Xvoid ioecho(); Xvoid prs(); Xvoid putc(); Xvoid prn(); Xvoid closef(); Xvoid closeall(); X X/* X * IO control X */ Xvoid pushio(/* struct ioarg arg, int (*gen)() */); Xint remap(); Xint openpipe(); Xvoid closepipe(); Xstruct io *setbase(/* struct io * */); X XExtern struct ioarg temparg; /* temporary for PUSHIO */ X#define PUSHIO(what,arg,gen) ((temparg.what = (arg)),pushio(temparg,(gen))) X#define RUN(what,arg,gen) ((temparg.what = (arg)), run(temparg,(gen))) X X/* -------- word.h -------- */ X#ifndef WORD_H X#define WORD_H 1 Xstruct wdblock { X short w_bsize; X short w_nword; X /* bounds are arbitrary */ X char *w_words[1]; X}; X Xstruct wdblock *addword(); Xstruct wdblock *newword(); Xchar **getwords(); X#endif X X/* -------- area.h -------- */ X X/* X * storage allocation X */ Xchar *getcell(/* unsigned size */); Xvoid garbage(); Xvoid setarea(/* char *obj, int to */); Xvoid freearea(/* int area */); Xvoid freecell(/* char *obj */); X XExtern int areanum; /* current allocation area */ X X#define NEW(type) (type *)getcell(sizeof(type)) X#define DELETE(obj) freecell((char *)obj) X + END-OF-FILE sh.h chmod 'u=rw,g=r,o=r' \s\h\.\h set `sum \s\h\.\h` sum=$1 case $sum in 29038) :;; *) echo 'Bad sum in '\s\h\.\h >&2 esac echo Extracting \s\h\1\.\c sed 's/^X//' > \s\h\1\.\c << '+ END-OF-FILE '\s\h\1\.\c X#define Extern extern X#include "signal.h" X#include "errno.h" X#include "setjmp.h" X#include "sh.h" X/* -------- sh.c -------- */ X/* X * shell X */ X X/* #include "sh.h" */ X Xint intr; Xint inparse; Xchar flags['z'-'a'+1]; Xchar *flag = flags-'a'; Xchar *elinep = line+sizeof(line)-5; Xchar *null = ""; Xint inword =1; Xstruct env e ={line, iostack, iostack-1, NULL, FDBASE, NULL}; X Xextern char **environ; /* environment pointer */ X X/* X * default shell, search rules X */ Xchar shellname[] = "/bin/sh"; Xchar search[] = ":/bin:/usr/bin"; X Xint (*qflag)() = SIG_IGN; X Xmain(argc, argv) Xint argc; Xregister char **argv; X{ X register int f; X register char *s; X int cflag; X char *name, **ap; X int (*iof)(); X X initarea(); X if ((ap = environ) != NULL) { X while (*ap) X assign(*ap++, !COPYV); X for (ap = environ; *ap;) X export(lookup(*ap++)); X } X closeall(); X areanum = 1; X X shell = lookup("SHELL"); X if (shell->value == null) X setval(shell, shellname); X export(shell); X X homedir = lookup("HOME"); X if (homedir->value == null) X setval(homedir, "/"); X export(homedir); X X setval(lookup("$"), itoa(getpid(), 5)); X X path = lookup("PATH"); X if (path->value == null) X setval(path, search); X export(path); X X ifs = lookup("IFS"); X if (ifs->value == null) X setval(ifs, " \t\n"); X X prompt = lookup("PS1"); X if (prompt->value == null) X#ifndef UNIXSHELL X setval(prompt, "$ "); X#else X setval(prompt, "% "); X#endif X if (geteuid() == 0) { X setval(prompt, "# "); X prompt->status &= ~EXPORT; X } X cprompt = lookup("PS2"); X if (cprompt->value == null) X setval(cprompt, "> "); X X iof = filechar; X cflag = 0; X name = *argv++; X if (--argc >= 1) { X if(argv[0][0] == '-' && argv[0][1] != '\0') { X for (s = argv[0]+1; *s; s++) X switch (*s) { X case 'c': X prompt->status &= ~EXPORT; X cprompt->status &= ~EXPORT; X setval(prompt, ""); X setval(cprompt, ""); X cflag = 1; X if (--argc > 0) X PUSHIO(aword, *++argv, iof = nlchar); X break; X X case 'q': X qflag = SIG_DFL; X break; X X case 's': X /* standard input */ X break; X X case 't': X prompt->status &= ~EXPORT; X setval(prompt, ""); X iof = linechar; X break; X X case 'i': X talking++; X default: X if (*s>='a' && *s<='z') X flag[*s]++; X } X } else { X argv--; X argc++; X } X if (iof == filechar && --argc > 0) { X setval(prompt, ""); X setval(cprompt, ""); X prompt->status &= ~EXPORT; X cprompt->status &= ~EXPORT; X if (newfile(*++argv)) X exit(1); X } X } X setdash(); X if (e.iop < iostack) { X PUSHIO(afile, 0, iof); X if (isatty(0) && isatty(1) && !cflag) X talking++; X } X signal(SIGQUIT, qflag); X if (name[0] == '-') { X talking++; X if ((f = open("/etc/profile", 0)) >= 0) X next(remap(f)); X if ((f = open(".profile", 0)) >= 0) X next(remap(f)); X } X if (talking) { X signal(SIGTERM, sig); X signal(SIGINT, SIG_IGN); X } X dolv = argv; X dolc = argc; X dolv[0] = name; X if (dolc > 1) X for (ap = ++argv; --argc > 0;) X if (assign(*ap = *argv++, !COPYV)) X dolc--; /* keyword */ X else X ap++; X setval(lookup("#"), putn(dolc-1)); X X for (;;) { X if (talking && e.iop <= iostack) X prs(prompt->value); X onecommand(); X } X} X Xsetdash() X{ X register char *cp, c; X char m['z'-'a'+1]; X X cp = m; X for (c='a'; c<='z'; c++) X if (flag[c]) X *cp++ = c; X *cp = 0; X setval(lookup("-"), m); X} X Xnewfile(s) Xregister char *s; X{ X register f; X X if (strcmp(s, "-") != 0) { X f = open(s, 0); X if (f < 0) { X prs(s); X err(": cannot open"); X return(1); X } X } else X f = 0; X next(remap(f)); X return(0); X} X Xonecommand() X{ X register i; X jmp_buf m1; X X inword++; X while (e.oenv) X quitenv(); X freearea(areanum = 1); X garbage(); X wdlist = 0; X iolist = 0; X e.errpt = 0; X e.linep = line; X yynerrs = 0; X multiline = 0; X inparse = 1; X if (talking) X signal(SIGINT, onintr); X if (setjmp(failpt = m1) || yyparse() || intr) { X while (e.oenv) X quitenv(); X scraphere(); X inparse = 0; X intr = 0; X return; X } X inparse = 0; X inword = 0; X if ((i = trapset) != 0) { X trapset = 0; X runtrap(i); X } X brklist = 0; X intr = 0; X execflg = 0; X if (!flag['n']) { X if (talking) X signal(SIGINT, onintr); X execute(outtree, NOPIPE, NOPIPE, 0); X intr = 0; X if (talking) X signal(SIGINT, SIG_IGN); X } X} X Xvoid Xfail() X{ X longjmp(failpt, 1); X /* NOTREACHED */ X} X Xvoid Xleave() X{ X if (execflg) X fail(); X runtrap(0); X sync(); X exit(exstat); X /* NOTREACHED */ X} X Xwarn(s) Xregister char *s; X{ X if(*s) { X prs(s); X exstat = -1; X } X prs("\n"); X if (flag['e']) X leave(); X} X Xerr(s) Xchar *s; X{ X warn(s); X if (flag['n']) X return; X if (!talking) X leave(); X if (e.errpt) X longjmp(e.errpt, 1); X closeall(); X e.iop = e.iobase = iostack; X} X Xnewenv(f) X{ X register struct env *ep; X X if (f) { X quitenv(); X return(1); X } X ep = (struct env *) space(sizeof(*ep)); X if (ep == NULL) { X while (e.oenv) X quitenv(); X fail(); X } X *ep = e; X e.oenv = ep; X e.errpt = errpt; X return(0); X} X Xquitenv() X{ X register struct env *ep; X register fd; X X if ((ep = e.oenv) != NULL) { X fd = e.iofd; X e = *ep; X /* should close `'d files */ X DELETE(ep); X while (--fd >= e.iofd) X close(fd); X } X} X X/* X * Is any character from s1 in s2? X */ Xint Xanys(s1, s2) Xregister char *s1, *s2; X{ X while (*s1) X if (any(*s1++, s2)) X return(1); X return(0); X} X X/* X * Is character c in s? X */ Xint Xany(c, s) Xregister int c; Xregister char *s; X{ X while (*s) X if (*s++ == c) X return(1); X return(0); X} X Xchar * Xputn(n) Xregister n; X{ X return(itoa(n, -1)); X} X Xchar * Xitoa(u, n) Xregister unsigned u; X{ X register char *cp; X static char s[20]; X int m; X X m = 0; X if (n < 0 && (int) u < 0) { X m++; X u = -u; X } X cp = s+sizeof(s); X *--cp = 0; X do { X *--cp = u%10 + '0'; X u /= 10; X } while (--n > 0 || u); X if (m) X *--cp = '-'; X return(cp); X} X Xnext(f) X{ X PUSHIO(afile, f, nextchar); X} X Xonintr() X{ X signal(SIGINT, SIG_IGN); X if (inparse) { X prs("\n"); X fail(); X } X intr++; X} X Xletter(c) Xregister c; X{ X return(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '_'); X} X Xdigit(c) Xregister c; X{ X return(c >= '0' && c <= '9'); X} X Xletnum(c) Xregister c; X{ X return(letter(c) || digit(c)); X} X Xchar * Xspace(n) Xint n; X{ X register char *cp; X X inword++; X if ((cp = getcell(n)) == 0) X err("out of string space"); X inword--; X return(cp); X} X Xchar * Xstrsave(s, a) Xregister char *s; X{ X register char *cp, *xp; X X if ((cp = space(strlen(s)+1)) != NULL) { X setarea((char *)cp, a); X for (xp = cp; (*xp++ = *s++) != '\0';) X ; X return(cp); X } X return(""); X} X X/* X * if inword is set, traps X * are delayed, avoiding X * having two people allocating X * at once. X */ Xxfree(s) Xregister char *s; X{ X inword++; X DELETE(s); X inword--; X} X X/* X * trap handling X */ Xsig(i) Xregister i; X{ X if (inword == 0) { X signal(i, SIG_IGN); X runtrap(i); X } else X trapset = i; X signal(i, sig); X} X Xruntrap(i) X{ X char *trapstr; X X if ((trapstr = trap[i]) == NULL) X return; X if (i == 0) X trap[i] = 0; X RUN(aword, trapstr, nlchar); X} X X/* -------- var.c -------- */ X/* #include "sh.h" */ X Xstatic char *findeq(); X X/* X * Find the given name in the dictionary X * and return its value. If the name was X * not previously there, enter it now and X * return a null value. X */ Xstruct var * Xlookup(n) Xregister char *n; X{ X register struct var *vp; X register char *cp; X register int c; X static struct var dummy; X X if (digit(*n)) { X dummy.name = n; X for (c = 0; digit(*n) && c < 1000; n++) X c = c*10 + *n-'0'; X dummy.status = RONLY; X dummy.value = c <= dolc? dolv[c]: null; X return(&dummy); X } X for (vp = vlist; vp; vp = vp->next) X if (eqname(vp->name, n)) X return(vp); X cp = findeq(n); X vp = (struct var *)space(sizeof(*vp)); X if (vp == 0 || (vp->name = space((int)(cp-n)+2)) == 0) { X dummy.name = dummy.value = ""; X return(&dummy); X } X for (cp = vp->name; (*cp = *n++) && *cp != '='; cp++) X ; X if (*cp == 0) X *cp = '='; X *++cp = 0; X setarea((char *)vp, 0); X setarea((char *)vp->name, 0); X vp->value = null; X vp->next = vlist; X vp->status = GETCELL; X vlist = vp; X return(vp); X} X X/* X * give variable at `vp' the value `val'. X */ Xvoid Xsetval(vp, val) Xstruct var *vp; Xchar *val; X{ X nameval(vp, val, (char *)NULL); X} X X/* X * if name is not NULL, it must be X * a prefix of the space `val', X * and end with `='. X * this is all so that exporting X * values is reasonably painless. X */ Xvoid Xnameval(vp, val, name) Xregister struct var *vp; Xchar *val, *name; X{ X register char *cp, *xp; X char *nv; X int fl; X X if (vp->status & RONLY) { X for (xp = vp->name; *xp && *xp != '=';) X putc(*xp++); X err(" is read-only"); X return; X } X fl = 0; X if (name == NULL) { X xp = space(strlen(vp->name)+strlen(val)+2); X if (xp == 0) X return; X /* make string: name=value */ X setarea((char *)xp, 0); X name = xp; X for (cp = vp->name; (*xp = *cp++) && *xp!='='; xp++) X ; X if (*xp++ == 0) X xp[-1] = '='; X nv = xp; X for (cp = val; (*xp++ = *cp++) != '\0';) X ; X val = nv; X fl = GETCELL; X } X if (vp->status & GETCELL) X xfree(vp->name); /* form new string `name=value' */ X vp->name = name; X vp->value = val; X vp->status |= fl; X} X Xvoid Xexport(vp) Xstruct var *vp; X{ X vp->status |= EXPORT; X} X Xvoid Xronly(vp) Xstruct var *vp; X{ X if (letter(vp->name[0])) /* not an internal symbol ($# etc) */ X vp->status |= RONLY; X} X Xint Xisassign(s) Xregister char *s; X{ X if (!letter(*s)) X return(0); X for (; *s != '='; s++) X if (*s == 0 || !letnum(*s)) X return(0); X return(1); X} X Xint Xassign(s, cf) Xregister char *s; Xint cf; X{ X register char *cp; X struct var *vp; X X if (!letter(*s)) X return(0); X for (cp = s; *cp != '='; cp++) X if (*cp == 0 || !letnum(*cp)) X return(0); X vp = lookup(s); X nameval(vp, ++cp, cf == COPYV? NULL: s); X if (cf != COPYV) X vp->status &= ~GETCELL; X return(1); X} X Xint Xcheckname(cp) Xregister char *cp; X{ X if (!letter(*cp++)) X return(0); X while (*cp) X if (!letnum(*cp++)) X return(0); X return(1); X} X Xvoid Xputvlist(f, out) Xregister int f, out; X{ X register struct var *vp; X X for (vp = vlist; vp; vp = vp->next) X if (vp->status & f && letter(*vp->name)) { X if (vp->status & EXPORT) X write(out, "export ", 7); X if (vp->status & RONLY) X write(out, "readonly ", 9); X write(out, vp->name, (int)(findeq(vp->name) - vp->name)); X write(out, "\n", 1); X } X} X Xint Xeqname(n1, n2) Xregister char *n1, *n2; X{ X for (; *n1 != '=' && *n1 != 0; n1++) X if (*n2++ != *n1) X return(0); X return(*n2 == 0 || *n2 == '='); X} X Xstatic char * Xfindeq(cp) Xregister char *cp; X{ X while (*cp != '\0' && *cp != '=') X cp++; X return(cp); X} X X/* -------- gmatch.c -------- */ X/* X * int gmatch(string, pattern) X * char *string, *pattern; X * X * Match a pattern as in sh(1). X */ X X#define NULL 0 X#define CMASK 0377 X#define QUOTE 0200 X#define QMASK (CMASK&~QUOTE) X#define NOT '!' /* might use ^ */ X Xstatic char *cclass(); X Xint Xgmatch(s, p) Xregister char *s, *p; X{ X register int sc, pc; X X if (s == NULL || p == NULL) X return(0); X while ((pc = *p++ & CMASK) != '\0') { X sc = *s++ & QMASK; X switch (pc) { X case '[': X if ((p = cclass(p, sc)) == NULL) X return(0); X break; X X case '?': X if (sc == 0) X return(0); X break; X X case '*': X s--; X do { X if (*p == '\0' || gmatch(s, p)) X return(1); X } while (*s++ != '\0'); X return(0); X X default: X if (sc != (pc&~QUOTE)) X return(0); X } X } X return(*s == 0); X} X Xstatic char * Xcclass(p, sub) Xregister char *p; Xregister int sub; X{ X register int c, d, not, found; X X if ((not = *p == NOT) != 0) X p++; X found = not; X do { X if (*p == '\0') X return(NULL); X c = *p & CMASK; X if (p[1] == '-' && p[2] != ']') { X d = p[2] & CMASK; X p++; X } else X d = c; X if (c == sub || c <= sub && sub <= d) X found = !not; X } while (*++p != ']'); X return(found? p+1: NULL); X} X X/* -------- area.c -------- */ X#define REGSIZE sizeof(struct region) X#define GROWBY 256 X#undef SHRINKBY 64 X#define FREE 32767 X#define BUSY 0 X#define ALIGN (sizeof(int)-1) X X/* #include "area.h" */ X#define NULL 0 X Xstruct region { X struct region *next; X int area; X}; X X/* X * All memory between (char *)areabot and (char *)(areatop+1) is X * exclusively administered by the area management routines. X * It is assumed that sbrk() and brk() manipulate the high end. X */ Xstatic struct region *areabot; /* bottom of area */ Xstatic struct region *areatop; /* top of area */ Xstatic struct region *areanxt; /* starting point of scan */ Xchar *sbrk(); Xchar *brk(); X Xinitarea() X{ X while ((int)sbrk(0) & ALIGN) X sbrk(1); X areabot = (struct region *)sbrk(REGSIZE); X areabot->next = areabot; X areabot->area = BUSY; X areatop = areabot; X areanxt = areabot; X} X Xchar * Xgetcell(nbytes) Xunsigned nbytes; X{ X register int nregio; X register struct region *p, *q; X register i; X X if (nbytes == 0) X abort(); /* silly and defeats the algorithm */ X /* X * round upwards and add administration area X */ X nregio = (nbytes+(REGSIZE-1))/REGSIZE + 1; X for (p = areanxt;;) { X if (p->area > areanum) { X /* X * merge free cells X */ X while ((q = p->next)->area > areanum) X p->next = q->next; X /* X * exit loop if cell big enough X */ X if (q >= p + nregio) X goto found; X } X p = p->next; X if (p == areanxt) X break; X } X i = nregio >= GROWBY ? nregio : GROWBY; X p = (struct region *)sbrk(i * REGSIZE); X if ((int)p == -1) X return(NULL); X p--; X if (p != areatop) X abort(); /* allocated areas are contiguous */ X q = p + i; X p->next = q; X p->area = FREE; X q->next = areabot; X q->area = BUSY; X areatop = q; Xfound: X /* X * we found a FREE area big enough, pointed to by 'p', and up to 'q' X */ X areanxt = p + nregio; X if (areanxt < q) { X /* X * split into requested area and rest X */ X if (areanxt+1 > q) X abort(); /* insufficient space left for admin */ X areanxt->next = q; X areanxt->area = FREE; X p->next = areanxt; X } X p->area = areanum; X return((char *)(p+1)); X} X Xvoid Xfreecell(cp) Xchar *cp; X{ X register struct region *p; X X if ((p = (struct region *)cp) != NULL) { X p--; X if (p < areanxt) X areanxt = p; X p->area = FREE; X } X} X Xvoid Xfreearea(a) Xregister int a; X{ X register struct region *p, *top; X X top = areatop; X for (p = areabot; p != top; p = p->next) X if (p->area >= a) X p->area = FREE; X} X Xvoid Xsetarea(cp,a) Xchar *cp; Xint a; X{ X register struct region *p; X X if ((p = (struct region *)cp) != NULL) X (p-1)->area = a; X} X Xvoid Xgarbage() X{ X register struct region *p, *q, *top; X X top = areatop; X for (p = areabot; p != top; p = p->next) { X if (p->area > areanum) { X while ((q = p->next)->area > areanum) X p->next = q->next; X areanxt = p; X } X } X#ifdef SHRINKBY X if (areatop >= q + SHRINKBY && q->area > areanum) { X brk((char *)(q+1)); X q->next = areabot; X q->area = BUSY; X areatop = q; X } X#endif X} + END-OF-FILE sh1.c chmod 'u=rw,g=r,o=r' \s\h\1\.\c set `sum \s\h\1\.\c` sum=$1 case $sum in 04453) :;; *) echo 'Bad sum in '\s\h\1\.\c >&2 esac echo Extracting \s\h\2\.\c sed 's/^X//' > \s\h\2\.\c << '+ END-OF-FILE '\s\h\2\.\c X#define Extern extern X#include "signal.h" X#include "errno.h" X#include "setjmp.h" X#include "sh.h" X X/* -------- csyn.c -------- */ X/* X * shell: syntax (C version) X */ X Xtypedef union { X char *cp; X char **wp; X int i; X struct op *o; X} YYSTYPE; X#define WORD 256 X#define LOGAND 257 X#define LOGOR 258 X#define BREAK 259 X#define IF 260 X#define THEN 261 X#define ELSE 262 X#define ELIF 263 X#define FI 264 X#define CASE 265 X#define ESAC 266 X#define FOR 267 X#define WHILE 268 X#define UNTIL 269 X#define DO 270 X#define DONE 271 X#define IN 272 X#define YYERRCODE 300 X X/* flags to yylex */ X#define CONTIN 01 /* skip new lines to complete command */ X X/* #include "sh.h" */ X#define SYNTAXERR zzerr() Xstatic int startl = 1; Xstatic int peeksym = 0; Xstatic void zzerr(); Xstatic void word(); Xstatic char **copyw(); Xstatic struct op *block(), *namelist(), *list(), *newtp(); Xstatic struct op *pipeline(), *andor(), *command(); Xstatic struct op *nested(), *simple(), *c_list(); Xstatic struct op *dogroup(), *thenpart(), *casepart(), *caselist(); Xstatic struct op *elsepart(); Xstatic char **wordlist(), **pattern(); Xstatic void musthave(); Xstatic int yylex(); Xstatic struct ioword *io(); Xstatic struct ioword **copyio(); Xstatic char *tree(); Xstatic void diag(); Xstatic int nlseen; Xstatic int iounit = IODEFAULT; Xstatic struct op *tp; Xstruct op *newtp(); X Xstatic YYSTYPE yylval; X Xint Xyyparse() X{ X peeksym = 0; X yynerrs = 0; X outtree = c_list(); X musthave('\n', 0); X return(yynerrs!=0); X} X Xstatic struct op * Xpipeline(cf) Xint cf; X{ X register struct op *t, *p; X register int c; X X t = command(cf); X if (t != NULL) { X while ((c = yylex(0)) == '|') { X if ((p = command(CONTIN)) == NULL) X SYNTAXERR; X if (t->type != TPAREN && t->type != TCOM) { X /* shell statement */ X t = block(TPAREN, t, NOBLOCK, NOWORDS); X } X t = block(TPIPE, t, p, NOWORDS); X } X peeksym = c; X } X return(t); X} X Xstatic struct op * Xandor() X{ X register struct op *t, *p; X register int c; X X t = pipeline(0); X if (t != NULL) { X while ((c = yylex(0)) == LOGAND || c == LOGOR) { X if ((p = pipeline(CONTIN)) == NULL) X SYNTAXERR; X t = block(c == LOGAND? TAND: TOR, t, p, NOWORDS); X } X peeksym = c; X } X return(t); X} X Xstatic struct op * Xc_list() X{ X register struct op *t, *p; X register int c; X X t = andor(); X if (t != NULL) { X while ((c = yylex(0)) == ';' || c == '&' || multiline && c == '\n') { X if (c == '&') X t = block(TASYNC, t, NOBLOCK, NOWORDS); X if ((p = andor()) == NULL) X return(t); X t = list(t, p); X } X peeksym = c; X } X return(t); X} X Xstatic int Xsynio(cf) Xint cf; X{ X register struct ioword *iop; X register int i; X register int c; X X if ((c = yylex(cf)) != '<' && c != '>') { X peeksym = c; X return(0); X } X i = yylval.i; X musthave(WORD, 0); X iop = io(iounit, i, yylval.cp); X iounit = IODEFAULT; X if (i & IOHERE) X markhere(yylval.cp, iop); X} X Xstatic void Xmusthave(c, cf) Xint c, cf; X{ X if ((peeksym = yylex(cf)) != c) X SYNTAXERR; X peeksym = 0; X} X Xstatic struct op * Xsimple() X{ X register struct op *t; X X t = NULL; X for (;;) { X switch (peeksym = yylex(0)) { X case '<': X case '>': X (void) synio(0); X break; X X case WORD: X if (t == NULL) { X t = newtp(); X t->type = TCOM; X } X peeksym = 0; X word(yylval.cp); X break; X X default: X return(t); X } X } X} X Xstatic struct op * Xnested(type, mark) Xint type, mark; X{ X register struct op *t; X X multiline++; X t = c_list(); X musthave(mark, 0); X multiline--; X return(block(type, t, NOBLOCK, NOWORDS)); X} X Xstatic struct op * Xcommand(cf) Xint cf; X{ X register struct ioword *io; X register struct op *t; X struct wdblock *iosave; X register int c; X X iosave = iolist; X iolist = NULL; X if (multiline) X cf |= CONTIN; X while (synio(cf)) X cf = 0; X switch (c = yylex(cf)) { X default: X peeksym = c; X if ((t = simple()) == NULL) { X if (iolist == NULL) X return(NULL); X t = newtp(); X t->type = TCOM; X } X break; X X case '(': X t = nested(TPAREN, ')'); X break; X X case '{': X t = nested(TBRACE, '}'); X break; X X case FOR: X t = newtp(); X t->type = TFOR; X musthave(WORD, 0); X startl = 1; X t->str = yylval.cp; X multiline++; X t->words = wordlist(); X if ((c = yylex(0)) != '\n' && c != ';') X SYNTAXERR; X t->left = dogroup(0); X multiline--; X break; X X case WHILE: X case UNTIL: X multiline++; X t = newtp(); X t->type = c == WHILE? TWHILE: TUNTIL; X t->left = c_list(); X t->right = dogroup(1); X t->words = NULL; X multiline--; X break; X X case CASE: X t = newtp(); X t->type = TCASE; X musthave(WORD, 0); X t->str = yylval.cp; X startl++; X multiline++; X musthave(IN, CONTIN); X startl++; X t->left = caselist(); X musthave(ESAC, 0); X multiline--; X break; X X case IF: X multiline++; X t = newtp(); X t->type = TIF; X t->left = c_list(); X t->right = thenpart(); X musthave(FI, 0); X multiline--; X break; X } X while (synio(0)) X ; X t = namelist(t); X iolist = iosave; X return(t); X} X Xstatic struct op * Xdogroup(onlydone) Xint onlydone; X{ X register int c; X register struct op *list; X X c = yylex(CONTIN); X if (c == DONE && onlydone) X return(NULL); X if (c != DO) X SYNTAXERR; X list = c_list(); X musthave(DONE, 0); X return(list); X} X Xstatic struct op * Xthenpart() X{ X register int c; X register struct op *t; X X if ((c = yylex(0)) != THEN) { X peeksym = c; X return(NULL); X } X t = newtp(); X t->type = 0; X t->left = c_list(); X if (t->left == NULL) X SYNTAXERR; X t->right = elsepart(); X return(t); X} X Xstatic struct op * Xelsepart() X{ X register int c; X register struct op *t; X X switch (c = yylex(0)) { X case ELSE: X if ((t = c_list()) == NULL) X SYNTAXERR; X return(t); X X case ELIF: X t = newtp(); X t->type = TELIF; X t->left = c_list(); X t->right = thenpart(); X return(t); X X default: X peeksym = c; X return(NULL); X } X} X Xstatic struct op * Xcaselist() X{ X register struct op *t; X register int c; X X t = NULL; X while ((peeksym = yylex(CONTIN)) != ESAC) X t = list(t, casepart()); X return(t); X} X Xstatic struct op * Xcasepart() X{ X register struct op *t; X register int c; X X t = newtp(); X t->type = TPAT; X t->words = pattern(); X musthave(')', 0); X t->left = c_list(); X if ((peeksym = yylex(CONTIN)) != ESAC) X musthave(BREAK, CONTIN); X return(t); X} X Xstatic char ** Xpattern() X{ X register int c, cf; X X cf = CONTIN; X do { X musthave(WORD, cf); X word(yylval.cp); X cf = 0; X } while ((c = yylex(0)) == '|'); X peeksym = c; X word(NOWORD); X return(copyw()); X} X Xstatic char ** Xwordlist() X{ X register int c; X X if ((c = yylex(0)) != IN) { X peeksym = c; X return(NULL); X } X startl = 0; X while ((c = yylex(0)) == WORD) X word(yylval.cp); X word(NOWORD); X peeksym = c; X return(copyw()); X} X X/* X * supporting functions X */ Xstatic struct op * Xlist(t1, t2) Xregister struct op *t1, *t2; X{ X if (t1 == NULL) X return(t2); X if (t2 == NULL) X return(t1); X return(block(TLIST, t1, t2, NOWORDS)); X} X Xstatic struct op * Xblock(type, t1, t2, wp) Xstruct op *t1, *t2; Xchar **wp; X{ X register struct op *t; X X t = newtp(); X t->type = type; X t->left = t1; X t->right = t2; X t->words = wp; X return(t); X} X Xstruct res { X char *r_name; X int r_val; X} restab[] = { X "for", FOR, X "case", CASE, X "esac", ESAC, X "while", WHILE, X "do", DO, X "done", DONE, X "if", IF, X "in", IN, X "then", THEN, X "else", ELSE, X "elif", ELIF, X "until", UNTIL, X "fi", FI, X X ";;", BREAK, X "||", LOGOR, X "&&", LOGAND, X "{", '{', X "}", '}', X X 0, X}; X Xrlookup(n) Xregister char *n; X{ X register struct res *rp; X X for (rp = restab; rp->r_name; rp++) X if (strcmp(rp->r_name, n) == 0) X return(rp->r_val); X return(0); X} X Xstatic struct op * Xnewtp() X{ X register struct op *t; X X t = (struct op *)tree(sizeof(*t)); X t->type = 0; X t->words = NULL; X t->ioact = NULL; X t->left = NULL; X t->right = NULL; X t->str = NULL; X return(t); X} X Xstatic struct op * Xnamelist(t) Xregister struct op *t; X{ X if (iolist) { X iolist = addword((char *)NULL, iolist); X t->ioact = copyio(); X } else X t->ioact = NULL; X if (t->type != TCOM) { X if (t->type != TPAREN && t->ioact != NULL) { X t = block(TPAREN, t, NOBLOCK, NOWORDS); X t->ioact = t->left->ioact; X t->left->ioact = NULL; X } X return(t); X } X word(NOWORD); X t->words = copyw(); X return(t); X} X Xstatic char ** Xcopyw() X{ X register char **wd; X X wd = getwords(wdlist); X wdlist = 0; X return(wd); X} X Xstatic void Xword(cp) Xchar *cp; X{ X wdlist = addword(cp, wdlist); X} X Xstatic struct ioword ** Xcopyio() X{ X register struct ioword **iop; X X iop = (struct ioword **) getwords(iolist); X iolist = 0; X return(iop); X} X Xstatic struct ioword * Xio(u, f, cp) Xchar *cp; X{ X register struct ioword *iop; X X iop = (struct ioword *) tree(sizeof(*iop)); X iop->io_unit = u; X iop->io_flag = f; X iop->io_un.io_name = cp; X iolist = addword((char *)iop, iolist); X return(iop); X} X Xstatic void Xzzerr() X{ X yyerror("syntax error"); X} X Xyyerror(s) Xchar *s; X{ X yynerrs++; X if (talking) { X if (multiline && nlseen) X unget('\n'); X multiline = 0; X while (yylex(0) != '\n') X ; X } X err(s); X fail(); X} X Xstatic int Xyylex(cf) Xint cf; X{ X register int c, c1; X int atstart; X X if ((c = peeksym) > 0) { X peeksym = 0; X if (c == '\n') X startl = 1; X return(c); X } X nlseen = 0; X e.linep = line; X atstart = startl; X startl = 0; X yylval.i = 0; X Xloop: X while ((c = getc(0)) == ' ' || c == '\t') X ; X switch (c) { X default: X if (any(c, "0123456789")) { X unget(c1 = getc(0)); X if (c1 == '<' || c1 == '>') { X iounit = c - '0'; X goto loop; X } X *e.linep++ = c; X c = c1; X } X break; X X case '#': X while ((c = getc(0)) != 0 && c != '\n') X ; X unget(c); X goto loop; X X case 0: X return(c); X X case '$': X *e.linep++ = c; X if ((c = getc(0)) == '{') { X if ((c = collect(c, '}')) != '\0') X return(c); X goto pack; X } X break; X X case '`': X case '\'': X case '"': X if ((c = collect(c, c)) != '\0') X return(c); X goto pack; X X case '|': X case '&': X case ';': X if ((c1 = dual(c)) != '\0') { X startl = 1; X return(c1); X } X startl = 1; X return(c); X case '^': X startl = 1; X return('|'); X case '>': X case '<': X diag(c); X return(c); X X case '\n': X nlseen++; X gethere(); X startl = 1; X if (multiline || cf & CONTIN) { X if (talking && e.iop <= iostack) X prs(cprompt->value); X if (cf & CONTIN) X goto loop; X } X return(c); X X case '(': X case ')': X startl = 1; X return(c); X } X X unget(c); X Xpack: X while ((c = getc(0)) != 0 && !any(c, "`$ '\"\t;&<>()|^\n")) X if (e.linep >= elinep) X err("word too long"); X else X *e.linep++ = c; X unget(c); X if(any(c, "\"'`$")) X goto loop; X *e.linep++ = '\0'; X if (atstart && (c = rlookup(line))!=0) { X startl = 1; X return(c); X } X yylval.cp = strsave(line, areanum); X return(WORD); X} X Xint Xcollect(c, c1) Xregister c, c1; X{ X char s[2]; X X *e.linep++ = c; X while ((c = getc(c1)) != c1) { X if (c == 0) { X unget(c); X s[0] = c1; X s[1] = 0; X prs("no closing "); yyerror(s); X return(YYERRCODE); X } X if (talking && c == '\n' && e.iop <= iostack) X prs(cprompt->value); X *e.linep++ = c; X } X *e.linep++ = c; X return(0); X} X Xint Xdual(c) Xregister c; X{ X char s[3]; X register char *cp = s; X X *cp++ = c; X *cp++ = getc(0); X *cp = 0; X if ((c = rlookup(s)) == 0) X unget(*--cp); X return(c); X} X Xstatic void Xdiag(ec) Xregister int ec; X{ X register int c; X X c = getc(0); X if (c == '>' || c == '<') { X if (c != ec) X zzerr(); X yylval.i = ec == '>'? IOWRITE|IOCAT: IOHERE; X c = getc(0); X } else X yylval.i = ec == '>'? IOWRITE: IOREAD; X if (c != '&' || yylval.i == IOHERE) X unget(c); X else X yylval.i |= IODUP; X} X Xstatic char * Xtree(size) Xunsigned size; X{ X register char *t; X X if ((t = getcell(size)) == NULL) { X prs("command line too complicated\n"); X fail(); X /* NOTREACHED */ X } X return(t); X} X X/* VARARGS1 */ X/* ARGSUSED */ Xprintf(s) /* yyparse calls it */ Xchar *s; X{ X} X + END-OF-FILE sh2.c chmod 'u=rw,g=r,o=r' \s\h\2\.\c set `sum \s\h\2\.\c` sum=$1 case $sum in 34787) :;; *) echo 'Bad sum in '\s\h\2\.\c >&2 esac exit 0
mmdf@udel.UUCP (08/08/87)
Andy Tanenbaum's recent posting of the 1.2 Minix shell was in two parts. I never got the second of the two. Can anybody out there help? Many thanks! -Frank mstan!frank@uunet.UU.NET
egisin@orchid.waterloo.edu (Eric Gisin) (08/08/87)
I've haven't received part two either, but this version contains patches that Jim Paradis posted several weeks ago. He tries to implement "<<" redirection with temp files but does it incorrectly. Consider the following shell script. $ for x in a b; do > cat <<. x=$x . > done The shell has to create a temp file with 'x=$x' during parsing, then read the first temp file and do dollar expansion into a second temp file before "cat" is executed. Jim's fixes try to do this with one pass, which is impossible so his version doesn't work for the above example. I reported this bug to ast about a week ago, but I don't think he received my mail. I do have a correct fix for << redirection that uses temp files.
ast@cs.vu.nl (Andy Tanenbaum) (08/09/87)
In article <10150@orchid.waterloo.edu> egisin@orchid.waterloo.edu (Eric Gisin) writes: >I've haven't received part two either, but this version contains >patches that Jim Paradis posted several weeks ago. > The 1.2 shell incorporates fixes from multiple sources, so it is not identical to Jim's. If Part 2 got lost somewhere, I can repost it. Will a couple of people please let me know (but not all of you-- 12,000 people on USENET according to the July Arbitron ratings, plus ? on Bitnet, Arpanet, ...) Andy Tanenbaum (ast@cs.vu.nl)
randy@umn-cs.UUCP (Randy Orrison) (08/10/87)
Part 2 did not make it to umn-cs either. Please send a copy to ...!umn-cs!local-minix if you don't re-post. -randy -- Randy Orrison, University of Minnesota School of Mathematics UUCP: {ihnp4, seismo!rutgers!umnd-cs, sun}!umn-cs!randy ARPA: randy@ux.acss.umn.edu (Yes, these are three BITNET: randy@umnacvx different machines)
ast@cs.vu.nl (Andy Tanenbaum) (08/10/87)
: This is a shar archive. Extract with sh, not csh. : This archive ends with exit, so do not worry about trailing junk. : --------------------------- cut here -------------------------- PATH=/bin:/usr/bin echo Extracting \m\a\k\e\f\i\l\e sed 's/^X//' > \m\a\k\e\f\i\l\e << '+ END-OF-FILE '\m\a\k\e\f\i\l\e XCFLAGS=-F Xl=../../lib X Xshobj = sh1.s sh2.s sh3.s sh4.s sh5.s sh6.s Xsh: $(shobj) sh.h X @asld -i -o sh -T. $l/crtso.s $(shobj) $l/libc.a $l/end.s X @chmem =8000 sh X + END-OF-FILE makefile chmod 'u=rw,g=r,o=r' \m\a\k\e\f\i\l\e set `sum \m\a\k\e\f\i\l\e` sum=$1 case $sum in 45952) :;; *) echo 'Bad sum in '\m\a\k\e\f\i\l\e >&2 esac echo Extracting \s\h\.\h sed 's/^X//' > \s\h\.\h << '+ END-OF-FILE '\s\h\.\h X/* -------- sh.h -------- */ X/* X * shell X */ X X#define NULL 0 X#define LINELIM 1000 X#define NPUSH 8 /* limit to input nesting */ X X#define NOFILE 20 /* Number of open files */ X#define NUFILE 10 /* Number of user-accessible files */ X#define FDBASE 10 /* First file usable by Shell */ X X/* X * values returned by wait X */ X#define WAITSIG(s) ((s)&0177) X#define WAITVAL(s) (((s)>>8)&0377) X#define WAITCORE(s) (((s)&0200)!=0) X X/* X * library and system defintions X */ Xtypedef int xint; /* base type of jmp_buf, for broken compilers */ X X/* X * shell components X */ X/* #include "area.h" */ X/* #include "word.h" */ X/* #include "io.h" */ X/* #include "var.h" */ X X#define QUOTE 0200 X X#define NOBLOCK ((struct op *)NULL) X#define NOWORD ((char *)NULL) X#define NOWORDS ((char **)NULL) X#define NOPIPE ((int *)NULL) X X/* X * Description of a command or an operation on commands. X * Might eventually use a union. X */ Xstruct op { X int type; /* operation type, see below */ X char **words; /* arguments to a command */ X struct ioword **ioact; /* IO actions (eg, < > >>) */ X struct op *left; X struct op *right; X char *str; /* identifier for case and for */ X}; X X#define TCOM 1 /* command */ X#define TPAREN 2 /* (c-list) */ X#define TPIPE 3 /* a | b */ X#define TLIST 4 /* a [&;] b */ X#define TOR 5 /* || */ X#define TAND 6 /* && */ X#define TFOR 7 X#define TDO 8 X#define TCASE 9 X#define TIF 10 X#define TWHILE 11 X#define TUNTIL 12 X#define TELIF 13 X#define TPAT 14 /* pattern in case */ X#define TBRACE 15 /* {c-list} */ X#define TASYNC 16 /* c & */ X X/* X * actions determining the environment of a process X */ X#define BIT(i) (1<<(i)) X#define FEXEC BIT(0) /* execute without forking */ X X/* X * flags to control evaluation of words X */ X#define DOSUB 1 /* interpret $, `, and quotes */ X#define DOBLANK 2 /* perform blank interpretation */ X#define DOGLOB 4 /* interpret [?* */ X#define DOKEY 8 /* move words with `=' to 2nd arg. list */ X#define DOTRIM 16 /* trim resulting string */ X X#define DOALL (DOSUB|DOBLANK|DOGLOB|DOKEY|DOTRIM) X XExtern char **dolv; XExtern int dolc; XExtern int exstat; XExtern char gflg; XExtern int talking; /* interactive (talking-type wireless) */ XExtern int execflg; XExtern int multiline; /* \n changed to ; */ XExtern struct op *outtree; /* result from parser */ X XExtern xint *failpt; XExtern xint *errpt; X Xstruct brkcon { X jmp_buf brkpt; X struct brkcon *nextlev; X} ; XExtern struct brkcon *brklist; XExtern int isbreak; X X/* X * redirection X */ Xstruct ioword { X short io_unit; /* unit affected */ X short io_flag; /* action (below) */ X union { X char *io_name; /* file name */ X struct block *io_here; /* here structure pointer */ X } io_un; X}; X#define IOREAD 1 /* < */ X#define IOHERE 2 /* << (here file) */ X#define IOWRITE 4 /* > */ X#define IOCAT 8 /* >> */ X#define IOXHERE 16 /* ${}, ` in << */ X#define IODUP 32 /* >&digit */ X#define IOCLOSE 64 /* >&- */ X X#define IODEFAULT (-1) /* token for default IO unit */ X XExtern struct wdblock *wdlist; XExtern struct wdblock *iolist; X X/* X * parsing & execution environment X */ Xextern struct env { X char *linep; X struct io *iobase; X struct io *iop; X xint *errpt; X int iofd; X struct env *oenv; X} e; X X/* X * flags: X * -e: quit on error X * -k: look for name=value everywhere on command line X * -n: no execution X * -t: exit after reading and executing one command X * -v: echo as read X * -x: trace X * -u: unset variables net diagnostic X */ Xextern char *flag; X Xextern char *null; /* null value for variable */ Xextern int intr; /* interrupt pending */ X XExtern char *trap[NSIG]; XExtern char ourtrap[NSIG]; XExtern int trapset; /* trap pending */ X Xextern int inword; /* defer traps and interrupts */ X XExtern int yynerrs; /* yacc */ X XExtern char line[LINELIM]; Xextern char *elinep; X X/* X * other functions X */ Xint (*inbuilt())(); /* find builtin command */ Xchar *rexecve(); Xchar *space(); Xchar *getwd(); Xchar *strsave(); Xchar *evalstr(); Xchar *putn(); Xchar *itoa(); Xchar *unquote(); Xstruct var *lookup(); Xstruct wdblock *add2args(); Xstruct wdblock *glob(); Xchar **makenv(); Xstruct ioword *addio(); Xchar **eval(); Xint setstatus(); Xint waitfor(); X Xint onintr(); /* SIGINT handler */ X X/* X * error handling X */ Xvoid leave(); /* abort shell (or fail in subshell) */ Xvoid fail(); /* fail but return to process next command */ Xint sig(); /* default signal handler */ X X/* X * library functions and system calls X */ Xlong lseek(); Xchar *strncpy(); Xint strlen(); Xextern int errno; X X/* -------- var.h -------- */ X Xstruct var { X char *value; X char *name; X struct var *next; X char status; X}; X#define COPYV 1 /* flag to setval, suggesting copy */ X#define RONLY 01 /* variable is read-only */ X#define EXPORT 02 /* variable is to be exported */ X#define GETCELL 04 /* name & value space was got with getcell */ X XExtern struct var *vlist; /* dictionary */ X XExtern struct var *homedir; /* home directory */ XExtern struct var *prompt; /* main prompt */ XExtern struct var *cprompt; /* continuation prompt */ XExtern struct var *path; /* search path for commands */ XExtern struct var *shell; /* shell to interpret command files */ XExtern struct var *ifs; /* field separators */ X Xstruct var *lookup(/* char *s */); Xvoid setval(/* struct var *, char * */); Xvoid nameval(/* struct var *, char *val, *name */); Xvoid export(/* struct var * */); Xvoid ronly(/* struct var * */); Xint isassign(/* char *s */); Xint checkname(/* char *name */); Xint assign(/* char *s, int copyflag */); Xvoid putvlist(/* int key, int fd */); Xint eqname(/* char *n1, char *n2 */); X X/* -------- io.h -------- */ X/* possible arguments to an IO function */ Xstruct ioarg { X char *aword; X char **awordlist; X int afile; /* file descriptor */ X}; X X/* an input generator's state */ Xstruct io { X int (*iofn)(); X struct ioarg arg; X int peekc; X char nlcount; /* for `'s */ X char xchar; /* for `'s */ X char task; /* reason for pushed IO */ X}; XExtern struct io iostack[NPUSH]; X#define XOTHER 0 /* none of the below */ X#define XDOLL 1 /* expanding ${} */ X#define XGRAVE 2 /* expanding `'s */ X#define XIO 4 /* file IO */ X#define XHERE 0x80 /* Any of the above inside a here document */ X#define XMASK 0x7f /* Get the actual task */ X X/* in substitution */ X#define INSUB() ((e.iop->task&XMASK)==XGRAVE||(e.iop->task&XMASK)==XDOLL) X X/* X * input generators for IO structure X */ Xint nlchar(); Xint strchar(); Xint filechar(); Xint linechar(); Xint nextchar(); Xint gravechar(); Xint qgravechar(); Xint dolchar(); Xint wdchar(); X X/* X * IO functions X */ Xint getc(); Xint readc(); Xvoid unget(); Xvoid ioecho(); Xvoid prs(); Xvoid putc(); Xvoid prn(); Xvoid closef(); Xvoid closeall(); X X/* X * IO control X */ Xvoid pushio(/* struct ioarg arg, int (*gen)() */); Xint remap(); Xint openpipe(); Xvoid closepipe(); Xstruct io *setbase(/* struct io * */); X XExtern struct ioarg temparg; /* temporary for PUSHIO */ X#define PUSHIO(what,arg,gen) ((temparg.what = (arg)),pushio(temparg,(gen))) X#define RUN(what,arg,gen) ((temparg.what = (arg)), run(temparg,(gen))) X X/* -------- word.h -------- */ X#ifndef WORD_H X#define WORD_H 1 Xstruct wdblock { X short w_bsize; X short w_nword; X /* bounds are arbitrary */ X char *w_words[1]; X}; X Xstruct wdblock *addword(); Xstruct wdblock *newword(); Xchar **getwords(); X#endif X X/* -------- area.h -------- */ X X/* X * storage allocation X */ Xchar *getcell(/* unsigned size */); Xvoid garbage(); Xvoid setarea(/* char *obj, int to */); Xvoid freearea(/* int area */); Xvoid freecell(/* char *obj */); X XExtern int areanum; /* current allocation area */ X X#define NEW(type) (type *)getcell(sizeof(type)) X#define DELETE(obj) freecell((char *)obj) X + END-OF-FILE sh.h chmod 'u=rw,g=r,o=r' \s\h\.\h set `sum \s\h\.\h` sum=$1 case $sum in 29038) :;; *) echo 'Bad sum in '\s\h\.\h >&2 esac echo Extracting \s\h\1\.\c sed 's/^X//' > \s\h\1\.\c << '+ END-OF-FILE '\s\h\1\.\c X#define Extern extern X#include "signal.h" X#include "errno.h" X#include "setjmp.h" X#include "sh.h" X/* -------- sh.c -------- */ X/* X * shell X */ X X/* #include "sh.h" */ X Xint intr; Xint inparse; Xchar flags['z'-'a'+1]; Xchar *flag = flags-'a'; Xchar *elinep = line+sizeof(line)-5; Xchar *null = ""; Xint inword =1; Xstruct env e ={line, iostack, iostack-1, NULL, FDBASE, NULL}; X Xextern char **environ; /* environment pointer */ X X/* X * default shell, search rules X */ Xchar shellname[] = "/bin/sh"; Xchar search[] = ":/bin:/usr/bin"; X Xint (*qflag)() = SIG_IGN; X Xmain(argc, argv) Xint argc; Xregister char **argv; X{ X register int f; X register char *s; X int cflag; X char *name, **ap; X int (*iof)(); X X initarea(); X if ((ap = environ) != NULL) { X while (*ap) X assign(*ap++, !COPYV); X for (ap = environ; *ap;) X export(lookup(*ap++)); X } X closeall(); X areanum = 1; X X shell = lookup("SHELL"); X if (shell->value == null) X setval(shell, shellname); X export(shell); X X homedir = lookup("HOME"); X if (homedir->value == null) X setval(homedir, "/"); X export(homedir); X X setval(lookup("$"), itoa(getpid(), 5)); X X path = lookup("PATH"); X if (path->value == null) X setval(path, search); X export(path); X X ifs = lookup("IFS"); X if (ifs->value == null) X setval(ifs, " \t\n"); X X prompt = lookup("PS1"); X if (prompt->value == null) X#ifndef UNIXSHELL X setval(prompt, "$ "); X#else X setval(prompt, "% "); X#endif X if (geteuid() == 0) { X setval(prompt, "# "); X prompt->status &= ~EXPORT; X } X cprompt = lookup("PS2"); X if (cprompt->value == null) X setval(cprompt, "> "); X X iof = filechar; X cflag = 0; X name = *argv++; X if (--argc >= 1) { X if(argv[0][0] == '-' && argv[0][1] != '\0') { X for (s = argv[0]+1; *s; s++) X switch (*s) { X case 'c': X prompt->status &= ~EXPORT; X cprompt->status &= ~EXPORT; X setval(prompt, ""); X setval(cprompt, ""); X cflag = 1; X if (--argc > 0) X PUSHIO(aword, *++argv, iof = nlchar); X break; X X case 'q': X qflag = SIG_DFL; X break; X X case 's': X /* standard input */ X break; X X case 't': X prompt->status &= ~EXPORT; X setval(prompt, ""); X iof = linechar; X break; X X case 'i': X talking++; X default: X if (*s>='a' && *s<='z') X flag[*s]++; X } X } else { X argv--; X argc++; X } X if (iof == filechar && --argc > 0) { X setval(prompt, ""); X setval(cprompt, ""); X prompt->status &= ~EXPORT; X cprompt->status &= ~EXPORT; X if (newfile(*++argv)) X exit(1); X } X } X setdash(); X if (e.iop < iostack) { X PUSHIO(afile, 0, iof); X if (isatty(0) && isatty(1) && !cflag) X talking++; X } X signal(SIGQUIT, qflag); X if (name[0] == '-') { X talking++; X if ((f = open("/etc/profile", 0)) >= 0) X next(remap(f)); X if ((f = open(".profile", 0)) >= 0) X next(remap(f)); X } X if (talking) { X signal(SIGTERM, sig); X signal(SIGINT, SIG_IGN); X } X dolv = argv; X dolc = argc; X dolv[0] = name; X if (dolc > 1) X for (ap = ++argv; --argc > 0;) X if (assign(*ap = *argv++, !COPYV)) X dolc--; /* keyword */ X else X ap++; X setval(lookup("#"), putn(dolc-1)); X X for (;;) { X if (talking && e.iop <= iostack) X prs(prompt->value); X onecommand(); X } X} X Xsetdash() X{ X register char *cp, c; X char m['z'-'a'+1]; X X cp = m; X for (c='a'; c<='z'; c++) X if (flag[c]) X *cp++ = c; X *cp = 0; X setval(lookup("-"), m); X} X Xnewfile(s) Xregister char *s; X{ X register f; X X if (strcmp(s, "-") != 0) { X f = open(s, 0); X if (f < 0) { X prs(s); X err(": cannot open"); X return(1); X } X } else X f = 0; X next(remap(f)); X return(0); X} X Xonecommand() X{ X register i; X jmp_buf m1; X X inword++; X while (e.oenv) X quitenv(); X freearea(areanum = 1); X garbage(); X wdlist = 0; X iolist = 0; X e.errpt = 0; X e.linep = line; X yynerrs = 0; X multiline = 0; X inparse = 1; X if (talking) X signal(SIGINT, onintr); X if (setjmp(failpt = m1) || yyparse() || intr) { X while (e.oenv) X quitenv(); X scraphere(); X inparse = 0; X intr = 0; X return; X } X inparse = 0; X inword = 0; X if ((i = trapset) != 0) { X trapset = 0; X runtrap(i); X } X brklist = 0; X intr = 0; X execflg = 0; X if (!flag['n']) { X if (talking) X signal(SIGINT, onintr); X execute(outtree, NOPIPE, NOPIPE, 0); X intr = 0; X if (talking) X signal(SIGINT, SIG_IGN); X } X} X Xvoid Xfail() X{ X longjmp(failpt, 1); X /* NOTREACHED */ X} X Xvoid Xleave() X{ X if (execflg) X fail(); X runtrap(0); X sync(); X exit(exstat); X /* NOTREACHED */ X} X Xwarn(s) Xregister char *s; X{ X if(*s) { X prs(s); X exstat = -1; X } X prs("\n"); X if (flag['e']) X leave(); X} X Xerr(s) Xchar *s; X{ X warn(s); X if (flag['n']) X return; X if (!talking) X leave(); X if (e.errpt) X longjmp(e.errpt, 1); X closeall(); X e.iop = e.iobase = iostack; X} X Xnewenv(f) X{ X register struct env *ep; X X if (f) { X quitenv(); X return(1); X } X ep = (struct env *) space(sizeof(*ep)); X if (ep == NULL) { X while (e.oenv) X quitenv(); X fail(); X } X *ep = e; X e.oenv = ep; X e.errpt = errpt; X return(0); X} X Xquitenv() X{ X register struct env *ep; X register fd; X X if ((ep = e.oenv) != NULL) { X fd = e.iofd; X e = *ep; X /* should close `'d files */ X DELETE(ep); X while (--fd >= e.iofd) X close(fd); X } X} X X/* X * Is any character from s1 in s2? X */ Xint Xanys(s1, s2) Xregister char *s1, *s2; X{ X while (*s1) X if (any(*s1++, s2)) X return(1); X return(0); X} X X/* X * Is character c in s? X */ Xint Xany(c, s) Xregister int c; Xregister char *s; X{ X while (*s) X if (*s++ == c) X return(1); X return(0); X} X Xchar * Xputn(n) Xregister n; X{ X return(itoa(n, -1)); X} X Xchar * Xitoa(u, n) Xregister unsigned u; X{ X register char *cp; X static char s[20]; X int m; X X m = 0; X if (n < 0 && (int) u < 0) { X m++; X u = -u; X } X cp = s+sizeof(s); X *--cp = 0; X do { X *--cp = u%10 + '0'; X u /= 10; X } while (--n > 0 || u); X if (m) X *--cp = '-'; X return(cp); X} X Xnext(f) X{ X PUSHIO(afile, f, nextchar); X} X Xonintr() X{ X signal(SIGINT, SIG_IGN); X if (inparse) { X prs("\n"); X fail(); X } X intr++; X} X Xletter(c) Xregister c; X{ X return(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '_'); X} X Xdigit(c) Xregister c; X{ X return(c >= '0' && c <= '9'); X} X Xletnum(c) Xregister c; X{ X return(letter(c) || digit(c)); X} X Xchar * Xspace(n) Xint n; X{ X register char *cp; X X inword++; X if ((cp = getcell(n)) == 0) X err("out of string space"); X inword--; X return(cp); X} X Xchar * Xstrsave(s, a) Xregister char *s; X{ X register char *cp, *xp; X X if ((cp = space(strlen(s)+1)) != NULL) { X setarea((char *)cp, a); X for (xp = cp; (*xp++ = *s++) != '\0';) X ; X return(cp); X } X return(""); X} X X/* X * if inword is set, traps X * are delayed, avoiding X * having two people allocating X * at once. X */ Xxfree(s) Xregister char *s; X{ X inword++; X DELETE(s); X inword--; X} X X/* X * trap handling X */ Xsig(i) Xregister i; X{ X if (inword == 0) { X signal(i, SIG_IGN); X runtrap(i); X } else X trapset = i; X signal(i, sig); X} X Xruntrap(i) X{ X char *trapstr; X X if ((trapstr = trap[i]) == NULL) X return; X if (i == 0) X trap[i] = 0; X RUN(aword, trapstr, nlchar); X} X X/* -------- var.c -------- */ X/* #include "sh.h" */ X Xstatic char *findeq(); X X/* X * Find the given name in the dictionary X * and return its value. If the name was X * not previously there, enter it now and X * return a null value. X */ Xstruct var * Xlookup(n) Xregister char *n; X{ X register struct var *vp; X register char *cp; X register int c; X static struct var dummy; X X if (digit(*n)) { X dummy.name = n; X for (c = 0; digit(*n) && c < 1000; n++) X c = c*10 + *n-'0'; X dummy.status = RONLY; X dummy.value = c <= dolc? dolv[c]: null; X return(&dummy); X } X for (vp = vlist; vp; vp = vp->next) X if (eqname(vp->name, n)) X return(vp); X cp = findeq(n); X vp = (struct var *)space(sizeof(*vp)); X if (vp == 0 || (vp->name = space((int)(cp-n)+2)) == 0) { X dummy.name = dummy.value = ""; X return(&dummy); X } X for (cp = vp->name; (*cp = *n++) && *cp != '='; cp++) X ; X if (*cp == 0) X *cp = '='; X *++cp = 0; X setarea((char *)vp, 0); X setarea((char *)vp->name, 0); X vp->value = null; X vp->next = vlist; X vp->status = GETCELL; X vlist = vp; X return(vp); X} X X/* X * give variable at `vp' the value `val'. X */ Xvoid Xsetval(vp, val) Xstruct var *vp; Xchar *val; X{ X nameval(vp, val, (char *)NULL); X} X X/* X * if name is not NULL, it must be X * a prefix of the space `val', X * and end with `='. X * this is all so that exporting X * values is reasonably painless. X */ Xvoid Xnameval(vp, val, name) Xregister struct var *vp; Xchar *val, *name; X{ X register char *cp, *xp; X char *nv; X int fl; X X if (vp->status & RONLY) { X for (xp = vp->name; *xp && *xp != '=';) X putc(*xp++); X err(" is read-only"); X return; X } X fl = 0; X if (name == NULL) { X xp = space(strlen(vp->name)+strlen(val)+2); X if (xp == 0) X return; X /* make string: name=value */ X setarea((char *)xp, 0); X name = xp; X for (cp = vp->name; (*xp = *cp++) && *xp!='='; xp++) X ; X if (*xp++ == 0) X xp[-1] = '='; X nv = xp; X for (cp = val; (*xp++ = *cp++) != '\0';) X ; X val = nv; X fl = GETCELL; X } X if (vp->status & GETCELL) X xfree(vp->name); /* form new string `name=value' */ X vp->name = name; X vp->value = val; X vp->status |= fl; X} X Xvoid Xexport(vp) Xstruct var *vp; X{ X vp->status |= EXPORT; X} X Xvoid Xronly(vp) Xstruct var *vp; X{ X if (letter(vp->name[0])) /* not an internal symbol ($# etc) */ X vp->status |= RONLY; X} X Xint Xisassign(s) Xregister char *s; X{ X if (!letter(*s)) X return(0); X for (; *s != '='; s++) X if (*s == 0 || !letnum(*s)) X return(0); X return(1); X} X Xint Xassign(s, cf) Xregister char *s; Xint cf; X{ X register char *cp; X struct var *vp; X X if (!letter(*s)) X return(0); X for (cp = s; *cp != '='; cp++) X if (*cp == 0 || !letnum(*cp)) X return(0); X vp = lookup(s); X nameval(vp, ++cp, cf == COPYV? NULL: s); X if (cf != COPYV) X vp->status &= ~GETCELL; X return(1); X} X Xint Xcheckname(cp) Xregister char *cp; X{ X if (!letter(*cp++)) X return(0); X while (*cp) X if (!letnum(*cp++)) X return(0); X return(1); X} X Xvoid Xputvlist(f, out) Xregister int f, out; X{ X register struct var *vp; X X for (vp = vlist; vp; vp = vp->next) X if (vp->status & f && letter(*vp->name)) { X if (vp->status & EXPORT) X write(out, "export ", 7); X if (vp->status & RONLY) X write(out, "readonly ", 9); X write(out, vp->name, (int)(findeq(vp->name) - vp->name)); X write(out, "\n", 1); X } X} X Xint Xeqname(n1, n2) Xregister char *n1, *n2; X{ X for (; *n1 != '=' && *n1 != 0; n1++) X if (*n2++ != *n1) X return(0); X return(*n2 == 0 || *n2 == '='); X} X Xstatic char * Xfindeq(cp) Xregister char *cp; X{ X while (*cp != '\0' && *cp != '=') X cp++; X return(cp); X} X X/* -------- gmatch.c -------- */ X/* X * int gmatch(string, pattern) X * char *string, *pattern; X * X * Match a pattern as in sh(1). X */ X X#define NULL 0 X#define CMASK 0377 X#define QUOTE 0200 X#define QMASK (CMASK&~QUOTE) X#define NOT '!' /* might use ^ */ X Xstatic char *cclass(); X Xint Xgmatch(s, p) Xregister char *s, *p; X{ X register int sc, pc; X X if (s == NULL || p == NULL) X return(0); X while ((pc = *p++ & CMASK) != '\0') { X sc = *s++ & QMASK; X switch (pc) { X case '[': X if ((p = cclass(p, sc)) == NULL) X return(0); X break; X X case '?': X if (sc == 0) X return(0); X break; X X case '*': X s--; X do { X if (*p == '\0' || gmatch(s, p)) X return(1); X } while (*s++ != '\0'); X return(0); X X default: X if (sc != (pc&~QUOTE)) X return(0); X } X } X return(*s == 0); X} X Xstatic char * Xcclass(p, sub) Xregister char *p; Xregister int sub; X{ X register int c, d, not, found; X X if ((not = *p == NOT) != 0) X p++; X found = not; X do { X if (*p == '\0') X return(NULL); X c = *p & CMASK; X if (p[1] == '-' && p[2] != ']') { X d = p[2] & CMASK; X p++; X } else X d = c; X if (c == sub || c <= sub && sub <= d) X found = !not; X } while (*++p != ']'); X return(found? p+1: NULL); X} X X/* -------- area.c -------- */ X#define REGSIZE sizeof(struct region) X#define GROWBY 256 X#undef SHRINKBY 64 X#define FREE 32767 X#define BUSY 0 X#define ALIGN (sizeof(int)-1) X X/* #include "area.h" */ X#define NULL 0 X Xstruct region { X struct region *next; X int area; X}; X X/* X * All memory between (char *)areabot and (char *)(areatop+1) is X * exclusively administered by the area management routines. X * It is assumed that sbrk() and brk() manipulate the high end. X */ Xstatic struct region *areabot; /* bottom of area */ Xstatic struct region *areatop; /* top of area */ Xstatic struct region *areanxt; /* starting point of scan */ Xchar *sbrk(); Xchar *brk(); X Xinitarea() X{ X while ((int)sbrk(0) & ALIGN) X sbrk(1); X areabot = (struct region *)sbrk(REGSIZE); X areabot->next = areabot; X areabot->area = BUSY; X areatop = areabot; X areanxt = areabot; X} X Xchar * Xgetcell(nbytes) Xunsigned nbytes; X{ X register int nregio; X register struct region *p, *q; X register i; X X if (nbytes == 0) X abort(); /* silly and defeats the algorithm */ X /* X * round upwards and add administration area X */ X nregio = (nbytes+(REGSIZE-1))/REGSIZE + 1; X for (p = areanxt;;) { X if (p->area > areanum) { X /* X * merge free cells X */ X while ((q = p->next)->area > areanum) X p->next = q->next; X /* X * exit loop if cell big enough X */ X if (q >= p + nregio) X goto found; X } X p = p->next; X if (p == areanxt) X break; X } X i = nregio >= GROWBY ? nregio : GROWBY; X p = (struct region *)sbrk(i * REGSIZE); X if ((int)p == -1) X return(NULL); X p--; X if (p != areatop) X abort(); /* allocated areas are contiguous */ X q = p + i; X p->next = q; X p->area = FREE; X q->next = areabot; X q->area = BUSY; X areatop = q; Xfound: X /* X * we found a FREE area big enough, pointed to by 'p', and up to 'q' X */ X areanxt = p + nregio; X if (areanxt < q) { X /* X * split into requested area and rest X */ X if (areanxt+1 > q) X abort(); /* insufficient space left for admin */ X areanxt->next = q; X areanxt->area = FREE; X p->next = areanxt; X } X p->area = areanum; X return((char *)(p+1)); X} X Xvoid Xfreecell(cp) Xchar *cp; X{ X register struct region *p; X X if ((p = (struct region *)cp) != NULL) { X p--; X if (p < areanxt) X areanxt = p; X p->area = FREE; X } X} X Xvoid Xfreearea(a) Xregister int a; X{ X register struct region *p, *top; X X top = areatop; X for (p = areabot; p != top; p = p->next) X if (p->area >= a) X p->area = FREE; X} X Xvoid Xsetarea(cp,a) Xchar *cp; Xint a; X{ X register struct region *p; X X if ((p = (struct region *)cp) != NULL) X (p-1)->area = a; X} X Xvoid Xgarbage() X{ X register struct region *p, *q, *top; X X top = areatop; X for (p = areabot; p != top; p = p->next) { X if (p->area > areanum) { X while ((q = p->next)->area > areanum) X p->next = q->next; X areanxt = p; X } X } X#ifdef SHRINKBY X if (areatop >= q + SHRINKBY && q->area > areanum) { X brk((char *)(q+1)); X q->next = areabot; X q->area = BUSY; X areatop = q; X } X#endif X} + END-OF-FILE sh1.c chmod 'u=rw,g=r,o=r' \s\h\1\.\c set `sum \s\h\1\.\c` sum=$1 case $sum in 04453) :;; *) echo 'Bad sum in '\s\h\1\.\c >&2 esac echo Extracting \s\h\2\.\c sed 's/^X//' > \s\h\2\.\c << '+ END-OF-FILE '\s\h\2\.\c X#define Extern extern X#include "signal.h" X#include "errno.h" X#include "setjmp.h" X#include "sh.h" X X/* -------- csyn.c -------- */ X/* X * shell: syntax (C version) X */ X Xtypedef union { X char *cp; X char **wp; X int i; X struct op *o; X} YYSTYPE; X#define WORD 256 X#define LOGAND 257 X#define LOGOR 258 X#define BREAK 259 X#define IF 260 X#define THEN 261 X#define ELSE 262 X#define ELIF 263 X#define FI 264 X#define CASE 265 X#define ESAC 266 X#define FOR 267 X#define WHILE 268 X#define UNTIL 269 X#define DO 270 X#define DONE 271 X#define IN 272 X#define YYERRCODE 300 X X/* flags to yylex */ X#define CONTIN 01 /* skip new lines to complete command */ X X/* #include "sh.h" */ X#define SYNTAXERR zzerr() Xstatic int startl = 1; Xstatic int peeksym = 0; Xstatic void zzerr(); Xstatic void word(); Xstatic char **copyw(); Xstatic struct op *block(), *namelist(), *list(), *newtp(); Xstatic struct op *pipeline(), *andor(), *command(); Xstatic struct op *nested(), *simple(), *c_list(); Xstatic struct op *dogroup(), *thenpart(), *casepart(), *caselist(); Xstatic struct op *elsepart(); Xstatic char **wordlist(), **pattern(); Xstatic void musthave(); Xstatic int yylex(); Xstatic struct ioword *io(); Xstatic struct ioword **copyio(); Xstatic char *tree(); Xstatic void diag(); Xstatic int nlseen; Xstatic int iounit = IODEFAULT; Xstatic struct op *tp; Xstruct op *newtp(); X Xstatic YYSTYPE yylval; X Xint Xyyparse() X{ X peeksym = 0; X yynerrs = 0; X outtree = c_list(); X musthave('\n', 0); X return(yynerrs!=0); X} X Xstatic struct op * Xpipeline(cf) Xint cf; X{ X register struct op *t, *p; X register int c; X X t = command(cf); X if (t != NULL) { X while ((c = yylex(0)) == '|') { X if ((p = command(CONTIN)) == NULL) X SYNTAXERR; X if (t->type != TPAREN && t->type != TCOM) { X /* shell statement */ X t = block(TPAREN, t, NOBLOCK, NOWORDS); X } X t = block(TPIPE, t, p, NOWORDS); X } X peeksym = c; X } X return(t); X} X Xstatic struct op * Xandor() X{ X register struct op *t, *p; X register int c; X X t = pipeline(0); X if (t != NULL) { X while ((c = yylex(0)) == LOGAND || c == LOGOR) { X if ((p = pipeline(CONTIN)) == NULL) X SYNTAXERR; X t = block(c == LOGAND? TAND: TOR, t, p, NOWORDS); X } X peeksym = c; X } X return(t); X} X Xstatic struct op * Xc_list() X{ X register struct op *t, *p; X register int c; X X t = andor(); X if (t != NULL) { X while ((c = yylex(0)) == ';' || c == '&' || multiline && c == '\n') { X if (c == '&') X t = block(TASYNC, t, NOBLOCK, NOWORDS); X if ((p = andor()) == NULL) X return(t); X t = list(t, p); X } X peeksym = c; X } X return(t); X} X Xstatic int Xsynio(cf) Xint cf; X{ X register struct ioword *iop; X register int i; X register int c; X X if ((c = yylex(cf)) != '<' && c != '>') { X peeksym = c; X return(0); X } X i = yylval.i; X musthave(WORD, 0); X iop = io(iounit, i, yylval.cp); X iounit = IODEFAULT; X if (i & IOHERE) X markhere(yylval.cp, iop); X} X Xstatic void Xmusthave(c, cf) Xint c, cf; X{ X if ((peeksym = yylex(cf)) != c) X SYNTAXERR; X peeksym = 0; X} X Xstatic struct op * Xsimple() X{ X register struct op *t; X X t = NULL; X for (;;) { X switch (peeksym = yylex(0)) { X case '<': X case '>': X (void) synio(0); X break; X X case WORD: X if (t == NULL) { X t = newtp(); X t->type = TCOM; X } X peeksym = 0; X word(yylval.cp); X break; X X default: X return(t); X } X } X} X Xstatic struct op * Xnested(type, mark) Xint type, mark; X{ X register struct op *t; X X multiline++; X t = c_list(); X musthave(mark, 0); X multiline--; X return(block(type, t, NOBLOCK, NOWORDS)); X} X Xstatic struct op * Xcommand(cf) Xint cf; X{ X register struct ioword *io; X register struct op *t; X struct wdblock *iosave; X register int c; X X iosave = iolist; X iolist = NULL; X if (multiline) X cf |= CONTIN; X while (synio(cf)) X cf = 0; X switch (c = yylex(cf)) { X default: X peeksym = c; X if ((t = simple()) == NULL) { X if (iolist == NULL) X return(NULL); X t = newtp(); X t->type = TCOM; X } X break; X X case '(': X t = nested(TPAREN, ')'); X break; X X case '{': X t = nested(TBRACE, '}'); X break; X X case FOR: X t = newtp(); X t->type = TFOR; X musthave(WORD, 0); X startl = 1; X t->str = yylval.cp; X multiline++; X t->words = wordlist(); X if ((c = yylex(0)) != '\n' && c != ';') X SYNTAXERR; X t->left = dogroup(0); X multiline--; X break; X X case WHILE: X case UNTIL: X multiline++; X t = newtp(); X t->type = c == WHILE? TWHILE: TUNTIL; X t->left = c_list(); X t->right = dogroup(1); X t->words = NULL; X multiline--; X break; X X case CASE: X t = newtp(); X t->type = TCASE; X musthave(WORD, 0); X t->str = yylval.cp; X startl++; X multiline++; X musthave(IN, CONTIN); X startl++; X t->left = caselist(); X musthave(ESAC, 0); X multiline--; X break; X X case IF: X multiline++; X t = newtp(); X t->type = TIF; X t->left = c_list(); X t->right = thenpart(); X musthave(FI, 0); X multiline--; X break; X } X while (synio(0)) X ; X t = namelist(t); X iolist = iosave; X return(t); X} X Xstatic struct op * Xdogroup(onlydone) Xint onlydone; X{ X register int c; X register struct op *list; X X c = yylex(CONTIN); X if (c == DONE && onlydone) X return(NULL); X if (c != DO) X SYNTAXERR; X list = c_list(); X musthave(DONE, 0); X return(list); X} X Xstatic struct op * Xthenpart() X{ X register int c; X register struct op *t; X X if ((c = yylex(0)) != THEN) { X peeksym = c; X return(NULL); X } X t = newtp(); X t->type = 0; X t->left = c_list(); X if (t->left == NULL) X SYNTAXERR; X t->right = elsepart(); X return(t); X} X Xstatic struct op * Xelsepart() X{ X register int c; X register struct op *t; X X switch (c = yylex(0)) { X case ELSE: X if ((t = c_list()) == NULL) X SYNTAXERR; X return(t); X X case ELIF: X t = newtp(); X t->type = TELIF; X t->left = c_list(); X t->right = thenpart(); X return(t); X X default: X peeksym = c; X return(NULL); X } X} X Xstatic struct op * Xcaselist() X{ X register struct op *t; X register int c; X X t = NULL; X while ((peeksym = yylex(CONTIN)) != ESAC) X t = list(t, casepart()); X return(t); X} X Xstatic struct op * Xcasepart() X{ X register struct op *t; X register int c; X X t = newtp(); X t->type = TPAT; X t->words = pattern(); X musthave(')', 0); X t->left = c_list(); X if ((peeksym = yylex(CONTIN)) != ESAC) X musthave(BREAK, CONTIN); X return(t); X} X Xstatic char ** Xpattern() X{ X register int c, cf; X X cf = CONTIN; X do { X musthave(WORD, cf); X word(yylval.cp); X cf = 0; X } while ((c = yylex(0)) == '|'); X peeksym = c; X word(NOWORD); X return(copyw()); X} X Xstatic char ** Xwordlist() X{ X register int c; X X if ((c = yylex(0)) != IN) { X peeksym = c; X return(NULL); X } X startl = 0; X while ((c = yylex(0)) == WORD) X word(yylval.cp); X word(NOWORD); X peeksym = c; X return(copyw()); X} X X/* X * supporting functions X */ Xstatic struct op * Xlist(t1, t2) Xregister struct op *t1, *t2; X{ X if (t1 == NULL) X return(t2); X if (t2 == NULL) X return(t1); X return(block(TLIST, t1, t2, NOWORDS)); X} X Xstatic struct op * Xblock(type, t1, t2, wp) Xstruct op *t1, *t2; Xchar **wp; X{ X register struct op *t; X X t = newtp(); X t->type = type; X t->left = t1; X t->right = t2; X t->words = wp; X return(t); X} X Xstruct res { X char *r_name; X int r_val; X} restab[] = { X "for", FOR, X "case", CASE, X "esac", ESAC, X "while", WHILE, X "do", DO, X "done", DONE, X "if", IF, X "in", IN, X "then", THEN, X "else", ELSE, X "elif", ELIF, X "until", UNTIL, X "fi", FI, X X ";;", BREAK, X "||", LOGOR, X "&&", LOGAND, X "{", '{', X "}", '}', X X 0, X}; X Xrlookup(n) Xregister char *n; X{ X register struct res *rp; X X for (rp = restab; rp->r_name; rp++) X if (strcmp(rp->r_name, n) == 0) X return(rp->r_val); X return(0); X} X Xstatic struct op * Xnewtp() X{ X register struct op *t; X X t = (struct op *)tree(sizeof(*t)); X t->type = 0; X t->words = NULL; X t->ioact = NULL; X t->left = NULL; X t->right = NULL; X t->str = NULL; X return(t); X} X Xstatic struct op * Xnamelist(t) Xregister struct op *t; X{ X if (iolist) { X iolist = addword((char *)NULL, iolist); X t->ioact = copyio(); X } else X t->ioact = NULL; X if (t->type != TCOM) { X if (t->type != TPAREN && t->ioact != NULL) { X t = block(TPAREN, t, NOBLOCK, NOWORDS); X t->ioact = t->left->ioact; X t->left->ioact = NULL; X } X return(t); X } X word(NOWORD); X t->words = copyw(); X return(t); X} X Xstatic char ** Xcopyw() X{ X register char **wd; X X wd = getwords(wdlist); X wdlist = 0; X return(wd); X} X Xstatic void Xword(cp) Xchar *cp; X{ X wdlist = addword(cp, wdlist); X} X Xstatic struct ioword ** Xcopyio() X{ X register struct ioword **iop; X X iop = (struct ioword **) getwords(iolist); X iolist = 0; X return(iop); X} X Xstatic struct ioword * Xio(u, f, cp) Xchar *cp; X{ X register struct ioword *iop; X X iop = (struct ioword *) tree(sizeof(*iop)); X iop->io_unit = u; X iop->io_flag = f; X iop->io_un.io_name = cp; X iolist = addword((char *)iop, iolist); X return(iop); X} X Xstatic void Xzzerr() X{ X yyerror("syntax error"); X} X Xyyerror(s) Xchar *s; X{ X yynerrs++; X if (talking) { X if (multiline && nlseen) X unget('\n'); X multiline = 0; X while (yylex(0) != '\n') X ; X } X err(s); X fail(); X} X Xstatic int Xyylex(cf) Xint cf; X{ X register int c, c1; X int atstart; X X if ((c = peeksym) > 0) { X peeksym = 0; X if (c == '\n') X startl = 1; X return(c); X } X nlseen = 0; X e.linep = line; X atstart = startl; X startl = 0; X yylval.i = 0; X Xloop: X while ((c = getc(0)) == ' ' || c == '\t') X ; X switch (c) { X default: X if (any(c, "0123456789")) { X unget(c1 = getc(0)); X if (c1 == '<' || c1 == '>') { X iounit = c - '0'; X goto loop; X } X *e.linep++ = c; X c = c1; X } X break; X X case '#': X while ((c = getc(0)) != 0 && c != '\n') X ; X unget(c); X goto loop; X X case 0: X return(c); X X case '$': X *e.linep++ = c; X if ((c = getc(0)) == '{') { X if ((c = collect(c, '}')) != '\0') X return(c); X goto pack; X } X break; X X case '`': X case '\'': X case '"': X if ((c = collect(c, c)) != '\0') X return(c); X goto pack; X X case '|': X case '&': X case ';': X if ((c1 = dual(c)) != '\0') { X startl = 1; X return(c1); X } X startl = 1; X return(c); X case '^': X startl = 1; X return('|'); X case '>': X case '<': X diag(c); X return(c); X X case '\n': X nlseen++; X gethere(); X startl = 1; X if (multiline || cf & CONTIN) { X if (talking && e.iop <= iostack) X prs(cprompt->value); X if (cf & CONTIN) X goto loop; X } X return(c); X X case '(': X case ')': X startl = 1; X return(c); X } X X unget(c); X Xpack: X while ((c = getc(0)) != 0 && !any(c, "`$ '\"\t;&<>()|^\n")) X if (e.linep >= elinep) X err("word too long"); X else X *e.linep++ = c; X unget(c); X if(any(c, "\"'`$")) X goto loop; X *e.linep++ = '\0'; X if (atstart && (c = rlookup(line))!=0) { X startl = 1; X return(c); X } X yylval.cp = strsave(line, areanum); X return(WORD); X} X Xint Xcollect(c, c1) Xregister c, c1; X{ X char s[2]; X X *e.linep++ = c; X while ((c = getc(c1)) != c1) { X if (c == 0) { X unget(c); X s[0] = c1; X s[1] = 0; X prs("no closing "); yyerror(s); X return(YYERRCODE); X } X if (talking && c == '\n' && e.iop <= iostack) X prs(cprompt->value); X *e.linep++ = c; X } X *e.linep++ = c; X return(0); X} X Xint Xdual(c) Xregister c; X{ X char s[3]; X register char *cp = s; X X *cp++ = c; X *cp++ = getc(0); X *cp = 0; X if ((c = rlookup(s)) == 0) X unget(*--cp); X return(c); X} X Xstatic void Xdiag(ec) Xregister int ec; X{ X register int c; X X c = getc(0); X if (c == '>' || c == '<') { X if (c != ec) X zzerr(); X yylval.i = ec == '>'? IOWRITE|IOCAT: IOHERE; X c = getc(0); X } else X yylval.i = ec == '>'? IOWRITE: IOREAD; X if (c != '&' || yylval.i == IOHERE) X unget(c); X else X yylval.i |= IODUP; X} X Xstatic char * Xtree(size) Xunsigned size; X{ X register char *t; X X if ((t = getcell(size)) == NULL) { X prs("command line too complicated\n"); X fail(); X /* NOTREACHED */ X } X return(t); X} X X/* VARARGS1 */ X/* ARGSUSED */ Xprintf(s) /* yyparse calls it */ Xchar *s; X{ X} X + END-OF-FILE sh2.c chmod 'u=rw,g=r,o=r' \s\h\2\.\c set `sum \s\h\2\.\c` sum=$1 case $sum in 34787) :;; *) echo 'Bad sum in '\s\h\2\.\c >&2 esac exit 0
ast@cs.vu.nl (Andy Tanenbaum) (08/10/87)
: This is a shar archive. Extract with sh, not csh. : This archive ends with exit, so do not worry about trailing junk. : --------------------------- cut here -------------------------- PATH=/bin:/usr/bin echo Extracting \s\h\3\.\c sed 's/^X//' > \s\h\3\.\c << '+ END-OF-FILE '\s\h\3\.\c X#define Extern extern X#include "signal.h" X#include "errno.h" X#include "setjmp.h" X#include "sh.h" X X/* -------- exec.c -------- */ X/* #include "sh.h" */ X X/* X * execute tree X */ X Xstatic char *signame[] = { X "Signal 0", X "Hangup", X NULL, /* interrupt */ X "Quit", X "Illegal instruction", X "Trace/BPT trap", X "abort", X "EMT trap", X "Floating exception", X "Killed", X "Bus error", X "Memory fault", X "Bad system call", X NULL, /* broken pipe */ X "Alarm clock", X "Terminated", X}; X#define NSIGNAL (sizeof(signame)/sizeof(signame[0])) X Xstatic struct op *findcase(); Xstatic void brkset(); Xstatic void echo(); Xstatic int forkexec(); Xstatic int parent(); X Xint Xexecute(t, pin, pout, act) Xregister struct op *t; Xint *pin, *pout; Xint act; X{ X register struct op *t1; X int i, pv[2], rv, child, a; X char *cp, **wp, **wp2; X struct var *vp; X struct brkcon bc; X X if (t == NULL) X return(0); X rv = 0; X a = areanum++; X wp = (wp2 = t->words) != NULL? eval(wp2, DOALL): NULL; X X switch(t->type) { X case TPAREN: X case TCOM: X rv = forkexec(t, pin, pout, act, wp, &child); X if (child) { X exstat = rv; X leave(); X } X break; X X case TPIPE: X if ((rv = openpipe(pv)) < 0) X break; X pv[0] = remap(pv[0]); X pv[1] = remap(pv[1]); X (void) execute(t->left, pin, pv, 0); X rv = execute(t->right, pv, pout, 0); X break; X X case TLIST: X (void) execute(t->left, pin, pout, 0); X rv = execute(t->right, pin, pout, 0); X break; X X case TASYNC: X i = parent(); X if (i != 0) { X if (i != -1) { X if (pin != NULL) X closepipe(pin); X if (talking) { X prs(putn(i)); X prs("\n"); X } X } else X rv = -1; X setstatus(rv); X } else { X signal(SIGINT, SIG_IGN); X signal(SIGQUIT, SIG_IGN); X if (talking) X signal(SIGTERM, SIG_DFL); X talking = 0; X if (pin == NULL) { X close(0); X open("/dev/null", 0); X } X exit(execute(t->left, pin, pout, FEXEC)); X } X break; X X case TOR: X case TAND: X rv = execute(t->left, pin, pout, 0); X if ((t1 = t->right)!=NULL && (rv == 0) == (t->type == TAND)) X rv = execute(t1, pin, pout, 0); X break; X X case TFOR: X if (wp == NULL) { X wp = dolv+1; X if ((i = dolc-1) < 0) X i = 0; X } else X i = -1; X vp = lookup(t->str); X while (setjmp(bc.brkpt)) X if (isbreak) X goto broken; X brkset(&bc); X for (t1 = t->left; i-- && *wp != NULL;) { X setval(vp, *wp++); X rv = execute(t1, pin, pout, 0); X } X brklist = brklist->nextlev; X break; X X case TWHILE: X case TUNTIL: X while (setjmp(bc.brkpt)) X if (isbreak) X goto broken; X brkset(&bc); X t1 = t->left; X while ((execute(t1, pin, pout, 0) == 0) == (t->type == TWHILE)) X rv = execute(t->right, pin, pout, 0); X brklist = brklist->nextlev; X break; X X case TIF: X case TELIF: X rv = !execute(t->left, pin, pout, 0)? X execute(t->right->left, pin, pout, 0): X execute(t->right->right, pin, pout, 0); X break; X X case TCASE: X if ((cp = evalstr(t->str, DOSUB|DOTRIM)) == 0) X cp = ""; X if ((t1 = findcase(t->left, cp)) != NULL) X rv = execute(t1, pin, pout, 0); X break; X X case TBRACE: X/* X if (iopp = t->ioact) X while (*iopp) X if (iosetup(*iopp++, pin!=NULL, pout!=NULL)) { X rv = -1; X break; X } X*/ X if (rv >= 0 && (t1 = t->left)) X rv = execute(t1, pin, pout, 0); X break; X } X Xbroken: X t->words = wp2; X isbreak = 0; X freearea(areanum); X areanum = a; X if (intr) { X closeall(); X fail(); X } X return(rv); X} X Xstatic int Xforkexec(t, pin, pout, act, wp, pforked) Xregister struct op *t; Xint *pin, *pout; Xint act; Xchar **wp; Xint *pforked; X{ X int i, rv, (*shcom)(); X int doexec(); X register int f; X char *cp; X struct ioword **iopp; X int resetsig; X X resetsig = 0; X *pforked = 0; X shcom = NULL; X rv = -1; /* system-detected error */ X if (t->type == TCOM) { X /* strip all initial assignments */ X /* not correct wrt PATH=yyy command etc */ X if (flag['x']) X echo(wp); X while ((cp = *wp++) != NULL && assign(cp, COPYV)) X ; X wp--; X if (cp == NULL && t->ioact == NULL) X return(setstatus(0)); X else X shcom = inbuilt(cp); X } X t->words = wp; X f = act; X if (shcom == NULL && (f & FEXEC) == 0) { X i = parent(); X if (i != 0) { X if (i == -1) X return(rv); X if (pin != NULL) X closepipe(pin); X return(pout==NULL? setstatus(waitfor(i,0)): 0); X } X if (talking) { X signal(SIGINT, SIG_IGN); X signal(SIGQUIT, SIG_IGN); X resetsig = 1; X } X talking = 0; X intr = 0; X (*pforked)++; X brklist = 0; X execflg = 0; X } X#ifdef COMPIPE X if ((pin != NULL || pout != NULL) && shcom != NULL && shcom != doexec) { X err("piping to/from shell builtins not yet done"); X return(-1); X } X#endif X if (pin != NULL) { X dup2(pin[0], 0); X closepipe(pin); X } X if (pout != NULL) { X dup2(pout[1], 1); X closepipe(pout); X } X if ((iopp = t->ioact) != NULL) { X if (shcom != NULL && shcom != doexec) { X prs(cp); X err(": cannot redirect shell command"); X return(-1); X } X while (*iopp) X if (iosetup(*iopp++, pin!=NULL, pout!=NULL)) X return(rv); X } X if (shcom) X return(setstatus((*shcom)(t))); X /* should use FIOCEXCL */ X for (i=FDBASE; i<NOFILE; i++) X close(i); X if (t->type == TPAREN) X exit(execute(t->left, NOPIPE, NOPIPE, FEXEC)); X if (resetsig) { X signal(SIGINT, SIG_DFL); X signal(SIGQUIT, SIG_DFL); X } X if (wp[0] == NULL) X exit(0); X cp = rexecve(wp[0], wp, makenv(wp)); X prs(wp[0]); prs(": "); warn(cp); X if (!execflg) X trap[0] = NULL; X leave(); X /* NOTREACHED */ X} X X/* X * common actions when creating a new child X */ Xstatic int Xparent() X{ X register int i; X X i = fork(); X if (i != 0) { X if (i == -1) X warn("try again"); X setval(lookup("!"), putn(i)); X } X return(i); X} X X/* X * 0< 1> are ignored as required X * within pipelines. X */ Xiosetup(iop, pipein, pipeout) Xregister struct ioword *iop; Xint pipein, pipeout; X{ X register u; X char *cp, *msg; X X if (iop->io_unit == IODEFAULT) /* take default */ X iop->io_unit = iop->io_flag&(IOREAD|IOHERE)? 0: 1; X if (pipein && iop->io_unit == 0) X return(0); X if (pipeout && iop->io_unit == 1) X return(0); X msg = iop->io_flag&(IOREAD|IOHERE)? "open": "create"; X if ((iop->io_flag & IOHERE) == 0) { X cp = iop->io_un.io_name; X if ((cp = evalstr(cp, DOSUB|DOTRIM)) == NULL) X return(1); X } X if (iop->io_flag & IODUP) { X if (cp[1] || !digit(*cp) && *cp != '-') { X prs(cp); X err(": illegal >& argument"); X return(1); X } X if (*cp == '-') X iop->io_flag = IOCLOSE; X iop->io_flag &= ~(IOREAD|IOWRITE); X } X switch (iop->io_flag) { X case IOREAD: X u = open(cp, 0); X break; X X case IOHERE: X case IOHERE|IOXHERE: X u = herein(iop->io_un.io_here, iop->io_flag&IOXHERE); X cp = "here file"; X break; X X case IOWRITE|IOCAT: X if ((u = open(cp, 1)) >= 0) { X lseek(u, (long)0, 2); X break; X } X case IOWRITE: X u = creat(cp, 0666); X break; X X case IODUP: X u = dup2(*cp-'0', iop->io_unit); X break; X X case IOCLOSE: X close(iop->io_unit); X return(0); X } X if (u < 0) { X prs(cp); X prs(": cannot "); X warn(msg); X return(1); X } else { X if (u != iop->io_unit) { X dup2(u, iop->io_unit); X close(u); X } X } X return(0); X} X Xstatic void Xecho(wp) Xregister char **wp; X{ X register i; X X prs("+"); X for (i=0; wp[i]; i++) { X if (i) X prs(" "); X prs(wp[i]); X } X prs("\n"); X} X Xstatic struct op ** Xfind1case(t, w) Xstruct op *t; Xchar *w; X{ X register struct op *t1; X struct op **tp; X register char **wp, *cp; X X if (t == NULL) X return(NULL); X if (t->type == TLIST) { X if ((tp = find1case(t->left, w)) != NULL) X return(tp); X t1 = t->right; /* TPAT */ X } else X t1 = t; X for (wp = t1->words; *wp;) X if ((cp = evalstr(*wp++, DOSUB)) && gmatch(w, cp)) X return(&t1->left); X return(NULL); X} X Xstatic struct op * Xfindcase(t, w) Xstruct op *t; Xchar *w; X{ X register struct op **tp; X X return((tp = find1case(t, w)) != NULL? *tp: NULL); X} X X/* X * Enter a new loop level (marked for break/continue). X */ Xstatic void Xbrkset(bc) Xstruct brkcon *bc; X{ X bc->nextlev = brklist; X brklist = bc; X} X X/* X * Wait for the last process created. X * Print a message for each process found X * that was killed by a signal. X * Ignore interrupt signals while waiting X * unless `canintr' is true. X */ Xint Xwaitfor(lastpid, canintr) Xregister int lastpid; Xint canintr; X{ X register int pid, rv; X int s; X X rv = 0; X do { X pid = wait(&s); X if (pid == -1) { X if (errno != EINTR || canintr) X break; X } else { X if ((rv = WAITSIG(s)) != 0) { X if (rv < NSIGNAL) { X if (signame[rv] != NULL) { X if (pid != lastpid) { X prn(pid); X prs(": "); X } X prs(signame[rv]); X } X } else { X if (pid != lastpid) { X prn(pid); X prs(": "); X } X prs("Signal "); prn(rv); prs(" "); X } X if (WAITCORE(s)) X prs(" - core dumped"); X prs("\n"); X rv = -1; X } else X rv = WAITVAL(s); X } X/* Special patch for MINIX: sync before each command */ X sync(); X } while (pid != lastpid); X return(rv); X} X Xint Xsetstatus(s) Xregister int s; X{ X exstat = s; X setval(lookup("?"), putn(s)); X return(s); X} X X/* X * PATH-searching interface to execve. X * If getenv("PATH") were kept up-to-date, X * execvp might be used. X */ Xchar * Xrexecve(c, v, envp) Xchar *c, **v, **envp; X{ X register int i; X register char *sp, *tp; X int eacces = 0, asis = 0; X extern int errno; X X sp = any('/', c)? "": path->value; X asis = *sp == '\0'; X while (asis || *sp != '\0') { X asis = 0; X tp = e.linep; X for (; *sp != '\0'; tp++) X if ((*tp = *sp++) == ':') { X asis = *sp == '\0'; X break; X } X if (tp != e.linep) X *tp++ = '/'; X for (i = 0; (*tp++ = c[i++]) != '\0';) X ; X execve(e.linep, v, envp); X switch (errno) { X case ENOEXEC: X *v = e.linep; X tp = *--v; X *v = e.linep; X execve("/bin/sh", v, envp); X *v = tp; X return("no Shell"); X X case ENOMEM: X return("program too big"); X X case E2BIG: X return("argument list too long"); X X case EACCES: X eacces++; X break; X } X } X return(errno==ENOENT ? "not found" : "cannot execute"); X} X X/* X * Run the command produced by generator `f' X * applied to stream `arg'. X */ Xrun(arg, f) Xstruct ioarg arg; Xint (*f)(); X{ X struct op *otree; X struct wdblock *swdlist; X struct wdblock *siolist; X jmp_buf ev, rt; X xint *ofail; X int rv; X X areanum++; X swdlist = wdlist; X siolist = iolist; X otree = outtree; X ofail = failpt; X rv = -1; X if (newenv(setjmp(errpt = ev)) == 0) { X wdlist = 0; X iolist = 0; X pushio(arg, f); X e.iobase = e.iop; X yynerrs = 0; X if (setjmp(failpt = rt) == 0 && yyparse() == 0) X rv = execute(outtree, NOPIPE, NOPIPE, 0); X quitenv(); X } X wdlist = swdlist; X iolist = siolist; X failpt = ofail; X outtree = otree; X freearea(areanum--); X return(rv); X} X X/* -------- do.c -------- */ X/* #include "sh.h" */ X X/* X * built-in commands: doX X */ X Xstatic void rdexp(); Xstatic void badid(); Xstatic int brkcontin(); X Xdolabel() X{ X return(0); X} X Xdochdir(t) Xregister struct op *t; X{ X register char *cp, *er; X X if ((cp = t->words[1]) == NULL && (cp = homedir->value) == NULL) X er = ": no home directory"; X else if(chdir(cp) < 0) X er = ": bad directory"; X else X return(0); X prs(cp != NULL? cp: "cd"); X err(er); X return(1); X} X Xdoshift(t) Xregister struct op *t; X{ X register n; X X n = t->words[1]? getn(t->words[1]): 1; X if(dolc < n) { X err("nothing to shift"); X return(1); X } X dolv[n] = dolv[0]; X dolv += n; X dolc -= n; X setval(lookup("#"), putn(dolc)); X return(0); X} X X/* X * execute login and newgrp directly X */ Xdologin(t) Xstruct op *t; X{ X register char *cp; X X if (talking) { X signal(SIGINT, SIG_DFL); X signal(SIGQUIT, SIG_DFL); X } X cp = rexecve(t->words[0], t->words, makenv(t->words)); X prs(t->words[0]); prs(": "); err(cp); X return(1); X} X Xdoumask(t) Xregister struct op *t; X{ X register int i, n; X register char *cp; X X if ((cp = t->words[1]) == NULL) { X i = umask(0); X umask(i); X for (n=3*4; (n-=3) >= 0;) X putc('0'+((i>>n)&07)); X putc('\n'); X } else { X for (n=0; *cp>='0' && *cp<='9'; cp++) X n = n*8 + (*cp-'0'); X umask(n); X } X return(0); X} X Xdoexec(t) Xregister struct op *t; X{ X register i; X jmp_buf ex; X xint *ofail; X X t->ioact = NULL; X for(i = 0; (t->words[i]=t->words[i+1]) != NULL; i++) X ; X if (i == 0) X return(1); X execflg = 1; X ofail = failpt; X if (setjmp(failpt = ex) == 0) X execute(t, NOPIPE, NOPIPE, FEXEC); X failpt = ofail; X execflg = 0; X return(1); X} X Xdodot(t) Xstruct op *t; X{ X register i; X register char *sp, *tp; X char *cp; X X if ((cp = t->words[1]) == NULL) X return(0); X sp = any('/', cp)? ":": path->value; X while (*sp) { X tp = e.linep; X while (*sp && (*tp = *sp++) != ':') X tp++; X if (tp != e.linep) X *tp++ = '/'; X for (i = 0; (*tp++ = cp[i++]) != '\0';) X ; X if ((i = open(e.linep, 0)) >= 0) { X exstat = 0; X next(remap(i)); X return(exstat); X } X } X prs(cp); X err(": not found"); X return(-1); X} X Xdowait(t) Xstruct op *t; X{ X register i; X register char *cp; X X if ((cp = t->words[1]) != NULL) { X i = getn(cp); X if (i == 0) X return(0); X } else X i = -1; X if (talking) X signal(SIGINT, onintr); X setstatus(waitfor(i, 1)); X if (talking) X signal(SIGINT, SIG_IGN); X return(0); X} X Xdoread(t) Xstruct op *t; X{ X register char *cp, **wp; X register nb; X X if (t->words[1] == NULL) { X err("Usage: read name ..."); X return(1); X } X for (wp = t->words+1; *wp; wp++) { X for (cp = e.linep; cp < elinep-1; cp++) X if ((nb = read(0, cp, sizeof(*cp))) != sizeof(*cp) || X *cp == '\n' || X wp[1] && any(*cp, ifs->value)) X break; X *cp = 0; X if (nb <= 0) X break; X setval(lookup(*wp), e.linep); X } X return(nb <= 0); X} X Xdoeval(t) Xregister struct op *t; X{ X int wdchar(); X X return(RUN(awordlist, t->words+1, wdchar)); X} X Xdotrap(t) Xregister struct op *t; X{ X register char *s; X register n, i; X X if (t->words[1] == NULL) { X for (i=0; i<NSIG; i++) X if (trap[i]) { X prn(i); X prs(": "); X prs(trap[i]); X prs("\n"); X } X return(0); X } X n = getsig((s = t->words[2])!=NULL? s: t->words[1]); X xfree(trap[n]); X trap[n] = 0; X if (s != NULL) { X if ((i = strlen(s = t->words[1])) != 0) { X trap[n] = strsave(s, 0); X setsig(n, sig); X } else X setsig(n, SIG_IGN); X } else X setsig(n, (n == SIGINT || n == SIGQUIT) && talking? SIG_IGN: SIG_DFL); X return(0); X} X Xgetsig(s) Xchar *s; X{ X register int n; X X if ((n = getn(s)) < 0 || n >= NSIG) { X err("trap: bad signal number"); X n = 0; X } X return(n); X} X Xsetsig(n, f) Xregister n; Xint (*f)(); X{ X if (n == 0) X return; X if (signal(n, SIG_IGN) != SIG_IGN || ourtrap[n]) { X ourtrap[n] = 1; X signal(n, f); X } X} X Xgetn(as) Xchar *as; X{ X register char *s; X register n, m; X X s = as; X m = 1; X if (*s == '-') { X m = -1; X s++; X } X for (n = 0; digit(*s); s++) X n = (n*10) + (*s-'0'); X if (*s) { X prs(as); X err(": bad number"); X } X return(n*m); X} X Xdobreak(t) Xstruct op *t; X{ X return(brkcontin(t->words[1], 1)); X} X Xdocontinue(t) Xstruct op *t; X{ X return(brkcontin(t->words[1], 0)); X} X Xstatic int Xbrkcontin(cp, val) Xregister char *cp; X{ X register struct brkcon *bc; X register nl; X X nl = cp == NULL? 1: getn(cp); X if (nl <= 0) X nl = 999; X do { X if ((bc = brklist) == NULL) X break; X brklist = bc->nextlev; X } while (--nl); X if (nl) { X err("bad break/continue level"); X return(1); X } X isbreak = val; X longjmp(bc->brkpt, 1); X /* NOTREACHED */ X} X Xdoexit(t) Xstruct op *t; X{ X register char *cp; X X execflg = 0; X if ((cp = t->words[1]) != NULL) X exstat = getn(cp); X leave(); X} X Xdoexport(t) Xstruct op *t; X{ X rdexp(t->words+1, export, EXPORT); X return(0); X} X Xdoreadonly(t) Xstruct op *t; X{ X rdexp(t->words+1, ronly, RONLY); X return(0); X} X Xstatic void Xrdexp(wp, f, key) Xregister char **wp; Xvoid (*f)(); Xint key; X{ X if (*wp != NULL) { X for (; *wp != NULL; wp++) X if (checkname(*wp)) X (*f)(lookup(*wp)); X else X badid(*wp); X } else X putvlist(key, 1); X} X Xstatic void Xbadid(s) Xregister char *s; X{ X prs(s); X err(": bad identifier"); X} X Xdoset(t) Xregister struct op *t; X{ X register struct var *vp; X register char *cp; X register n; X X if ((cp = t->words[1]) == NULL) { X for (vp = vlist; vp; vp = vp->next) X varput(vp->name, 1); X return(0); X } X if (*cp == '-') { X t->words++; X if (*++cp == 0) X flag['x'] = flag['v'] = 0; X else X for (; *cp; cp++) X switch (*cp) { X case 'e': X if (!talking) X flag['e']++; X break; X X default: X if (*cp>='a' && *cp<='z') X flag[*cp]++; X break; X } X setdash(); X } X if (t->words[1]) { X t->words[0] = dolv[0]; X for (n=1; t->words[n]; n++) X setarea((char *)t->words[n], 0); X dolc = n-1; X dolv = t->words; X setval(lookup("#"), putn(dolc)); X setarea((char *)(dolv-1), 0); X } X return(0); X} X Xvarput(s, out) Xregister char *s; X{ X if (letnum(*s)) { X write(out, s, strlen(s)); X write(out, "\n", 1); X } X} X X Xstruct builtin { X char *command; X int (*fn)(); X}; Xstatic struct builtin builtin[] = { X ":", dolabel, X "cd", dochdir, X "shift", doshift, X "exec", doexec, X "wait", dowait, X "read", doread, X "eval", doeval, X "trap", dotrap, X "break", dobreak, X "continue", docontinue, X "exit", doexit, X "export", doexport, X "readonly", doreadonly, X "set", doset, X ".", dodot, X "umask", doumask, X "login", dologin, X "newgrp", dologin, X 0, X}; X Xint (*inbuilt(s))() Xregister char *s; X{ X register struct builtin *bp; X X for (bp = builtin; bp->command != NULL; bp++) X if (strcmp(bp->command, s) == 0) X return(bp->fn); X return(NULL); X} X + END-OF-FILE sh3.c chmod 'u=rw,g=r,o=r' \s\h\3\.\c set `sum \s\h\3\.\c` sum=$1 case $sum in 63046) :;; *) echo 'Bad sum in '\s\h\3\.\c >&2 esac echo Extracting \s\h\4\.\c sed 's/^X//' > \s\h\4\.\c << '+ END-OF-FILE '\s\h\4\.\c X#define Extern extern X#include "signal.h" X#include "errno.h" X#include "setjmp.h" X#include "stat.h" X#include "sh.h" X X/* -------- eval.c -------- */ X/* #include "sh.h" */ X/* #include "word.h" */ X X/* X * ${} X * `command` X * blank interpretation X * quoting X * glob X */ X Xstatic char *blank(); Xstatic int grave(); Xstatic int expand(); Xstatic int dollar(); X Xchar ** Xeval(ap, f) Xregister char **ap; X{ X struct wdblock *wb; X char **wp; X jmp_buf ev; X X inword++; X wp = NULL; X wb = NULL; X if (newenv(setjmp(errpt = ev)) == 0) { X wb = addword((char *)0, wb); /* space for shell name, if command file */ X while (expand(*ap++, &wb, f)) X ; X wb = addword((char *)0, wb); X wp = getwords(wb) + 1; X quitenv(); X } else X gflg = 1; X inword--; X return(gflg? NULL: wp); X} X X/* X * Make the exported environment from the exported X * names in the dictionary. Keyword assignments X * ought to be taken from wp (the list of words on the command line) X * but aren't, yet. Until then: ARGSUSED X */ Xchar ** Xmakenv(wp) Xchar **wp; X{ X register struct wdblock *wb; X register struct var *vp; X X wb = NULL; X for (vp = vlist; vp; vp = vp->next) X if (vp->status & EXPORT) X wb = addword(vp->name, wb); X wb = addword((char *)0, wb); X return(getwords(wb)); X} X Xchar * Xevalstr(cp, f) Xregister char *cp; Xint f; X{ X struct wdblock *wb; X X inword++; X wb = NULL; X if (expand(cp, &wb, f)) { X if (wb == NULL || wb->w_nword == 0 || (cp = wb->w_words[0]) == NULL) X cp = ""; X DELETE(wb); X } else X cp = NULL; X inword--; X return(cp); X} X Xstatic int Xexpand(cp, wbp, f) Xregister char *cp; Xregister struct wdblock **wbp; X{ X jmp_buf ev; X X gflg = 0; X if (cp == NULL) X return(0); X if (!anys("$`'\"", cp) && X !anys(ifs->value, cp) && X ((f&DOGLOB)==0 || !anys("[*?", cp))) { X cp = strsave(cp, areanum); X if (f & DOTRIM) X unquote(cp); X *wbp = addword(cp, *wbp); X return(1); X } X if (newenv(setjmp(errpt = ev)) == 0) { X PUSHIO(aword, cp, strchar); X e.iobase = e.iop; X while ((cp = blank(f)) && gflg == 0) { X e.linep = cp; X cp = strsave(cp, areanum); X if ((f&DOGLOB) == 0) { X if (f & DOTRIM) X unquote(cp); X *wbp = addword(cp, *wbp); X } else X *wbp = glob(cp, *wbp); X } X quitenv(); X } else X gflg = 1; X return(gflg == 0); X} X X/* X * Blank interpretation and quoting X */ Xstatic char * Xblank(f) X{ X register c, c1; X register char *sp; X X sp = e.linep; X Xloop: X switch (c = subgetc('"', 0)) { X case 0: X if (sp == e.linep) X return(0); X *e.linep++ = 0; X return(sp); X X default: X if (f & DOBLANK && any(c, ifs->value)) X goto loop; X break; X X case '"': X case '\'': X if (INSUB()) X break; X for (c1 = c; (c = subgetc(c1, 1)) != c1;) { X if (c == 0) X break; X if (c == '\'' || !any(c, "$`\"")) X c |= QUOTE; X *e.linep++ = c; X } X c = 0; X } X unget(c); X for (;;) { X c = subgetc('"', 0); X if (c == 0 || X f & DOBLANK && any(c, ifs->value) || X !INSUB() && any(c, "\"'`")) { X unget(c); X if (any(c, "\"'`")) X goto loop; X break; X } X *e.linep++ = c; X } X *e.linep++ = 0; X return(sp); X} X X/* X * Get characters, substituting for ` and $ X */ Xint Xsubgetc(ec, quoted) Xregister char ec; Xint quoted; X{ X register char c; X Xagain: X c = getc(ec); X if (!INSUB() && ec != '\'') { X if (c == '`') { X if (grave(quoted) == 0) X return(0); X e.iop->task = XGRAVE; X goto again; X } X if (c == '$' && (c = dollar(quoted)) == 0) { X e.iop->task = XDOLL; X goto again; X } X } X return(c); X} X X/* X * Prepare to generate the string returned by ${} substitution. X */ Xstatic int Xdollar(quoted) Xint quoted; X{ X int otask; X struct io *oiop; X char *dolp; X register char *s, c, *cp; X struct var *vp; X X c = readc(); X s = e.linep; X if (c != '{') { X *e.linep++ = c; X if (letter(c)) { X while ((c = readc())!=0 && letnum(c)) X if (e.linep < elinep) X *e.linep++ = c; X unget(c); X } X c = 0; X } else { X oiop = e.iop; X otask = e.iop->task; X e.iop->task = XOTHER; X while ((c = subgetc('"', 0))!=0 && c!='}' && c!='\n') X if (e.linep < elinep) X *e.linep++ = c; X if (oiop == e.iop) X e.iop->task = otask; X if (c != '}') { X err("unclosed ${"); X gflg++; X return(c); X } X } X if (e.linep >= elinep) { X err("string in ${} too long"); X gflg++; X e.linep -= 10; X } X *e.linep = 0; X if (*s) X for (cp = s+1; *cp; cp++) X if (any(*cp, "=-+?")) { X c = *cp; X *cp++ = 0; X break; X } X if (s[1] == 0 && (*s == '*' || *s == '@')) { X if (dolc > 1) { X /* currently this does not distinguish $* and $@ */ X /* should check dollar */ X e.linep = s; X PUSHIO(awordlist, dolv+1, dolchar); X return(0); X } else { /* trap the nasty ${=} */ X s[0] = '1'; X s[1] = 0; X } X } X vp = lookup(s); X if ((dolp = vp->value) == null) { X switch (c) { X case '=': X if (digit(*s)) { X err("cannot use ${...=...} with $n"); X gflg++; X break; X } X setval(vp, cp); X dolp = vp->value; X break; X X case '-': X dolp = strsave(cp, areanum); X break; X X case '?': X if (*cp == 0) { X prs("missing value for "); X err(s); X } else X err(cp); X gflg++; X break; X } X } else if (c == '+') X dolp = strsave(cp, areanum); X if (flag['u'] && dolp == null) { X prs("unset variable: "); X err(s); X gflg++; X } X e.linep = s; X PUSHIO(aword, dolp, strchar); X return(0); X} X X/* X * Run the command in `...` and read its output. X */ Xstatic int Xgrave(quoted) Xint quoted; X{ X register char *cp; X register int i; X int pf[2]; X X for (cp = e.iop->arg.aword; *cp != '`'; cp++) X if (*cp == 0) { X err("no closing `"); X return(0); X } X if (openpipe(pf) < 0) X return(0); X if ((i = fork()) == -1) { X closepipe(pf); X err("try again"); X return(0); X } X if (i != 0) { X e.iop->arg.aword = ++cp; X close(pf[1]); X PUSHIO(afile, remap(pf[0]), quoted? qgravechar: gravechar); X return(1); X } X *cp = 0; X /* allow trapped signals */ X for (i=0; i<NSIG; i++) X if (ourtrap[i] && signal(i, SIG_IGN) != SIG_IGN) X signal(i, SIG_DFL); X dup2(pf[1], 1); X closepipe(pf); X flag['e'] = 0; X flag['v'] = 0; X flag['n'] = 0; X cp = strsave(e.iop->arg.aword, 0); X X /* jrp debug */ X scraphere(); X X freearea(areanum = 1); /* free old space */ X e.oenv = NULL; X e.iop = (e.iobase = iostack) - 1; X unquote(cp); X talking = 0; X PUSHIO(aword, cp, nlchar); X onecommand(); X exit(1); X} X Xchar * Xunquote(as) Xregister char *as; X{ X register char *s; X X if ((s = as) != NULL) X while (*s) X *s++ &= ~QUOTE; X return(as); X} X X/* -------- glob.c -------- */ X/* #include "sh.h" */ X X#define DIRSIZ 14 Xstruct direct X{ X unsigned short d_ino; X char d_name[DIRSIZ]; X}; X/* X * glob X */ X X#define scopy(x) strsave((x), areanum) X#define BLKSIZ 512 X#define NDENT ((BLKSIZ+sizeof(struct direct)-1)/sizeof(struct direct)) X Xstatic struct wdblock *cl, *nl; Xstatic char spcl[] = "[?*"; Xstatic int xstrcmp(); Xstatic char *generate(); Xstatic int anyspcl(); X Xstruct wdblock * Xglob(cp, wb) Xchar *cp; Xstruct wdblock *wb; X{ X register i; X register char *pp; X X if (cp == 0) X return(wb); X i = 0; X for (pp = cp; *pp; pp++) X if (any(*pp, spcl)) X i++; X else if (!any(*pp & ~QUOTE, spcl)) X *pp &= ~QUOTE; X if (i != 0) { X for (cl = addword(scopy(cp), (struct wdblock *)0); anyspcl(cl); cl = nl) { X nl = newword(cl->w_nword*2); X for(i=0; i<cl->w_nword; i++) { /* for each argument */ X for (pp = cl->w_words[i]; *pp; pp++) X if (any(*pp, spcl)) { X globname(cl->w_words[i], pp); X break; X } X if (*pp == '\0') X nl = addword(scopy(cl->w_words[i]), nl); X } X for(i=0; i<cl->w_nword; i++) X DELETE(cl->w_words[i]); X DELETE(cl); X } X for(i=0; i<cl->w_nword; i++) X unquote(cl->w_words[i]); X glob0((char *)cl->w_words, cl->w_nword, sizeof(char *), xstrcmp); X if (cl->w_nword) { X for (i=0; i<cl->w_nword; i++) X wb = addword(cl->w_words[i], wb); X DELETE(cl); X return(wb); X } X } X wb = addword(unquote(cp), wb); X return(wb); X} X Xglobname(we, pp) Xchar *we; Xregister char *pp; X{ X register char *np, *cp; X char *name, *gp, *dp; X int dn, j, n, k; X struct direct ent[NDENT]; X char dname[DIRSIZ+1]; X struct stat dbuf; X X for (np = we; np != pp; pp--) X if (pp[-1] == '/') X break; X for (dp = cp = space((int)(pp-np)+3); np < pp;) X *cp++ = *np++; X *cp++ = '.'; X *cp = '\0'; X for (gp = cp = space(strlen(pp)+1); *np && *np != '/';) X *cp++ = *np++; X *cp = '\0'; X dn = open(dp, 0); X if (dn < 0) { X DELETE(dp); X DELETE(gp); X return; X } X dname[DIRSIZ] = '\0'; X while ((n = read(dn, (char *)ent, sizeof(ent))) >= sizeof(*ent)) { X n /= sizeof(*ent); X for (j=0; j<n; j++) { X if (ent[j].d_ino == 0) X continue; X strncpy(dname, ent[j].d_name, DIRSIZ); X if (dname[0] == '.' && X (dname[1] == '\0' || dname[1] == '.' && dname[2] == '\0')) X if (*gp != '.') X continue; X for(k=0; k<DIRSIZ; k++) X if (any(dname[k], spcl)) X dname[k] |= QUOTE; X if (gmatch(dname, gp)) { X name = generate(we, pp, dname, np); X if (*np && !anys(np, spcl)) { X if (stat(name,&dbuf)) { X DELETE(name); X continue; X } X } X nl = addword(name, nl); X } X } X } X close(dn); X DELETE(dp); X DELETE(gp); X} X X/* X * generate a pathname as below. X * start..end1 / middle end X * the slashes come for free X */ Xstatic char * Xgenerate(start1, end1, middle, end) Xchar *start1; Xregister char *end1; Xchar *middle, *end; X{ X char *p; X register char *op, *xp; X X p = op = space((int)(end1-start1)+strlen(middle)+strlen(end)+2); X for (xp = start1; xp != end1;) X *op++ = *xp++; X for (xp = middle; (*op++ = *xp++) != '\0';) X ; X op--; X for (xp = end; (*op++ = *xp++) != '\0';) X ; X return(p); X} X Xstatic int Xanyspcl(wb) Xregister struct wdblock *wb; X{ X register i; X register char **wd; X X wd = wb->w_words; X for (i=0; i<wb->w_nword; i++) X if (anys(spcl, *wd++)) X return(1); X return(0); X} X Xstatic int Xxstrcmp(p1, p2) Xchar *p1, *p2; X{ X return(strcmp(*(char **)p1, *(char **)p2)); X} X X/* -------- word.c -------- */ X/* #include "sh.h" */ X/* #include "word.h" */ Xchar *memcpy(); X X#define NSTART 16 /* default number of words to allow for initially */ X Xstruct wdblock * Xnewword(nw) Xregister nw; X{ X register struct wdblock *wb; X X wb = (struct wdblock *) space(sizeof(*wb) + nw*sizeof(char *)); X wb->w_bsize = nw; X wb->w_nword = 0; X return(wb); X} X Xstruct wdblock * Xaddword(wd, wb) Xchar *wd; Xregister struct wdblock *wb; X{ X register struct wdblock *wb2; X register nw; X X if (wb == NULL) X wb = newword(NSTART); X if ((nw = wb->w_nword) >= wb->w_bsize) { X wb2 = newword(nw * 2); X memcpy((char *)wb2->w_words, (char *)wb->w_words, nw*sizeof(char *)); X wb2->w_nword = nw; X DELETE(wb); X wb = wb2; X } X wb->w_words[wb->w_nword++] = wd; X return(wb); X} X Xchar ** Xgetwords(wb) Xregister struct wdblock *wb; X{ X register char **wd; X register nb; X X if (wb == NULL) X return(NULL); X if (wb->w_nword == 0) { X DELETE(wb); X return(NULL); X } X wd = (char **) space(nb = sizeof(*wd) * wb->w_nword); X memcpy((char *)wd, (char *)wb->w_words, nb); X DELETE(wb); /* perhaps should done by caller */ X return(wd); X} X Xint (*func)(); Xint globv; X Xglob0(a0, a1, a2, a3) Xchar *a0; Xunsigned a1; Xint a2; Xint (*a3)(); X{ X func = a3; X globv = a2; X glob1(a0, a0 + a1 * a2); X} X Xglob1(base, lim) Xchar *base, *lim; X{ X register char *i, *j; X int v2; X char **k; X char *lptr, *hptr; X int c; X unsigned n; X X X v2 = globv; X Xtop: X if ((n=(int)(lim-base)) <= v2) X return; X n = v2 * (n / (2*v2)); X hptr = lptr = base+n; X i = base; X j = lim-v2; X for(;;) { X if (i < lptr) { X if ((c = (*func)(i, lptr)) == 0) { X glob2(i, lptr -= v2); X continue; X } X if (c < 0) { X i += v2; X continue; X } X } X Xbegin: X if (j > hptr) { X if ((c = (*func)(hptr, j)) == 0) { X glob2(hptr += v2, j); X goto begin; X } X if (c > 0) { X if (i == lptr) { X glob3(i, hptr += v2, j); X i = lptr += v2; X goto begin; X } X glob2(i, j); X j -= v2; X i += v2; X continue; X } X j -= v2; X goto begin; X } X X X if (i == lptr) { X if (lptr-base >= lim-hptr) { X glob1(hptr+v2, lim); X lim = lptr; X } else { X glob1(base, lptr); X base = hptr+v2; X } X goto top; X } X X X glob3(j, lptr -= v2, i); X j = hptr -= v2; X } X} X Xglob2(i, j) Xchar *i, *j; X{ X register char *index1, *index2, c; X int m; X X m = globv; X index1 = i; X index2 = j; X do { X c = *index1; X *index1++ = *index2; X *index2++ = c; X } while(--m); X} X Xglob3(i, j, k) Xchar *i, *j, *k; X{ X register char *index1, *index2, *index3; X int c; X int m; X X m = globv; X index1 = i; X index2 = j; X index3 = k; X do { X c = *index1; X *index1++ = *index3; X *index3++ = *index2; X *index2++ = c; X } while(--m); X} + END-OF-FILE sh4.c chmod 'u=rw,g=r,o=r' \s\h\4\.\c set `sum \s\h\4\.\c` sum=$1 case $sum in 59682) :;; *) echo 'Bad sum in '\s\h\4\.\c >&2 esac echo Extracting \s\h\5\.\c sed 's/^X//' > \s\h\5\.\c << '+ END-OF-FILE '\s\h\5\.\c X#define Extern extern X#include "signal.h" X#include "errno.h" X#include "setjmp.h" X#include "sh.h" X X/* -------- io.c -------- */ X/* #include "sh.h" */ X X/* X * shell IO X */ X X Xint Xgetc(ec) Xregister int ec; X{ X register int c; X X if(e.linep > elinep) { X while((c=readc()) != '\n' && c) X ; X err("input line too long"); X gflg++; X return(c); X } X c = readc(); X if ((ec != '"') && (ec != '\'')) { X if(c == '\\') { X c = readc(); X if (c == '\n' && ec != '\"') X return(getc(ec)); X c |= QUOTE; X } X } X return(c); X} X Xvoid Xunget(c) X{ X if (e.iop >= e.iobase) X e.iop->peekc = c; X} X Xint Xreadc() X{ X register c; X static int eofc; X X for (; e.iop >= e.iobase; e.iop--) X if ((c = e.iop->peekc) != '\0') { X e.iop->peekc = 0; X return(c); X } else if ((c = (*e.iop->iofn)(&e.iop->arg, e.iop)) != '\0') { X if (c == -1) { X e.iop++; X continue; X } X if (e.iop == iostack) X ioecho(c); X return(c); X } X if (e.iop >= iostack || X multiline && eofc++ < 3) X return(0); X leave(); X /* NOTREACHED */ X} X Xvoid Xioecho(c) Xchar c; X{ X if (flag['v']) X write(2, &c, sizeof c); X} X Xvoid Xpushio(arg, fn) Xstruct ioarg arg; Xint (*fn)(); X{ X if (++e.iop >= &iostack[NPUSH]) { X e.iop--; X err("Shell input nested too deeply"); X gflg++; X return; X } X e.iop->iofn = fn; X e.iop->arg = arg; X e.iop->peekc = 0; X e.iop->xchar = 0; X e.iop->nlcount = 0; X if (fn == filechar || fn == linechar || fn == nextchar) X e.iop->task = XIO; X else if (fn == gravechar || fn == qgravechar) X e.iop->task = XGRAVE; X else X e.iop->task = XOTHER; X} X Xstruct io * Xsetbase(ip) Xstruct io *ip; X{ X register struct io *xp; X X xp = e.iobase; X e.iobase = ip; X return(xp); X} X X/* X * Input generating functions X */ X X/* X * Produce the characters of a string, then a newline, then EOF. X */ Xint Xnlchar(ap) Xregister struct ioarg *ap; X{ X register int c; X X if (ap->aword == NULL) X return(0); X if ((c = *ap->aword++) == 0) { X ap->aword = NULL; X return('\n'); X } X return(c); X} X X/* X * Given a list of words, produce the characters X * in them, with a space after each word. X */ Xint Xwdchar(ap) Xregister struct ioarg *ap; X{ X register char c; X register char **wl; X X if ((wl = ap->awordlist) == NULL) X return(0); X if (*wl != NULL) { X if ((c = *(*wl)++) != 0) X return(c & 0177); X ap->awordlist++; X return(' '); X } X ap->awordlist = NULL; X return('\n'); X} X X/* X * Return the characters of a list of words, X * producing a space between them. X */ Xstatic int xxchar(), qqchar(); X Xint Xdolchar(ap) Xregister struct ioarg *ap; X{ X register char *wp; X X if ((wp = *ap->awordlist++) != NULL) { X PUSHIO(aword, wp, *ap->awordlist == NULL? qqchar: xxchar); X return(-1); X } X return(0); X} X Xstatic int Xxxchar(ap) Xregister struct ioarg *ap; X{ X register int c; X X if (ap->aword == NULL) X return(0); X if ((c = *ap->aword++) == '\0') { X ap->aword = NULL; X return(' '); X } X return(c); X} X Xstatic int Xqqchar(ap) Xregister struct ioarg *ap; X{ X register int c; X X if (ap->aword == NULL || (c = *ap->aword++) == '\0') X return(0); X return(c); X} X X/* X * Produce the characters from a single word (string). X */ Xint Xstrchar(ap) Xregister struct ioarg *ap; X{ X register int c; X X if (ap->aword == 0 || (c = *ap->aword++) == 0) X return(0); X return(c); X} X X/* X * Return the characters from a file. X */ Xint Xfilechar(ap) Xregister struct ioarg *ap; X{ X register int i; X char c; X extern int errno; X X do { X i = read(ap->afile, &c, sizeof(c)); X } while (i < 0 && errno == EINTR); X return(i == sizeof(c)? c&0177: (closef(ap->afile), 0)); X} X X/* X * Return the characters produced by a process (`...`). X * Quote them if required, and remove any trailing newline characters. X */ Xint Xgravechar(ap, iop) Xstruct ioarg *ap; Xstruct io *iop; X{ X register int c; X X if ((c = qgravechar(ap, iop)&~QUOTE) == '\n') X c = ' '; X return(c); X} X Xint Xqgravechar(ap, iop) Xregister struct ioarg *ap; Xstruct io *iop; X{ X register int c; X X if (iop->xchar) { X if (iop->nlcount) { X iop->nlcount--; X return('\n'|QUOTE); X } X c = iop->xchar; X iop->xchar = 0; X } else if ((c = filechar(ap)) == '\n') { X iop->nlcount = 1; X while ((c = filechar(ap)) == '\n') X iop->nlcount++; X iop->xchar = c; X if (c == 0) X return(c); X iop->nlcount--; X c = '\n'; X } X return(c!=0? c|QUOTE: 0); X} X X/* X * Return a single command (usually the first line) from a file. X */ Xint Xlinechar(ap) Xregister struct ioarg *ap; X{ X register int c; X X if ((c = filechar(ap)) == '\n') { X if (!multiline) { X closef(ap->afile); X ap->afile = -1; /* illegal value */ X } X } X return(c); X} X X/* X * Return the next character from the command source, X * prompting when required. X */ Xint Xnextchar(ap) Xregister struct ioarg *ap; X{ X register int c; X X if ((c = filechar(ap)) != 0) X return(c); X if (talking && e.iop <= iostack+1) X prs(prompt->value); X return(0); X} X Xvoid Xprs(s) Xregister char *s; X{ X if (*s) X write(2, s, strlen(s)); X} X Xvoid Xputc(c) Xchar c; X{ X write(2, &c, sizeof c); X} X Xvoid Xprn(u) Xunsigned u; X{ X prs(itoa(u, 0)); X} X Xvoid Xclosef(i) Xregister i; X{ X if (i > 2) X close(i); X} X Xvoid Xcloseall() X{ X register u; X X for (u=NUFILE; u<NOFILE;) X close(u++); X} X X/* X * remap fd into Shell's fd space X */ Xint Xremap(fd) Xregister int fd; X{ X register int i; X int map[NOFILE]; X X if (fd < e.iofd) { X for (i=0; i<NOFILE; i++) X map[i] = 0; X do { X map[fd] = 1; X fd = dup(fd); X } while (fd >= 0 && fd < e.iofd); X for (i=0; i<NOFILE; i++) X if (map[i]) X close(i); X if (fd < 0) X err("too many files open in shell"); X } X return(fd); X} X Xint Xopenpipe(pv) Xregister int *pv; X{ X register int i; X X if ((i = pipe(pv)) < 0) X err("can't create pipe - try again"); X return(i); X} X Xvoid Xclosepipe(pv) Xregister int *pv; X{ X if (pv != NULL) { X close(*pv++); X close(*pv); X } X} X X/* -------- here.c -------- */ X/* #include "sh.h" */ X X/* X * here documents X */ X Xstruct here { X char *h_tag; X int h_dosub; X struct ioword *h_iop; X struct here *h_next; X} *herelist; X Xstruct block { X char *b_linebuf; X char *b_next; X char b_tmpfile[50]; X int b_fd; X}; X Xstatic struct block *readhere(); X X#define NCPB 2048 /* here text block allocation unit */ X Xmarkhere(s, iop) Xregister char *s; Xstruct ioword *iop; X{ X register struct here *h, *lh; X X h = (struct here *) space(sizeof(struct here)); X if (h == 0) X return; X h->h_tag = evalstr(s, DOSUB); X if (h->h_tag == 0) X return; X h->h_iop = iop; X h->h_iop->io_un.io_here = NULL; X h->h_next = NULL; X if (herelist == 0) X herelist = h; X else X for (lh = herelist; lh!=NULL; lh = lh->h_next) X if (lh->h_next == 0) { X lh->h_next = h; X break; X } X iop->io_flag |= IOHERE|IOXHERE; X for (s = h->h_tag; *s; s++) X if (*s & QUOTE) { X iop->io_flag &= ~ IOXHERE; X *s &= ~ QUOTE; X } X h->h_dosub = iop->io_flag & IOXHERE; X} X Xgethere() X{ X register struct here *h; X X for (h = herelist; h != NULL; h = h->h_next) { X h->h_iop->io_un.io_here = X readhere(h->h_tag, h->h_dosub? 0: '\'', X h->h_iop->io_flag & IOXHERE); X } X herelist = NULL; X} X Xstatic struct block * Xreadhere(s, ec, nolit) Xregister char *s; X{ X register struct block *bp; X register c; X jmp_buf ev; X X bp = (struct block *) space(sizeof(*bp)); X if (bp == 0) X return(0); X bp->b_linebuf = (char *)space(NCPB); X if (bp->b_linebuf == 0) { X /* jrp - should release bp here... */ X return(0); X } X if (newenv(setjmp(errpt = ev)) == 0) { X if (e.iop == iostack && e.iop->iofn == filechar) { X pushio(e.iop->arg, filechar); X e.iobase = e.iop; X } X X /* jrp changes */ X bp->b_linebuf[0] = 0; X bp->b_next = bp->b_linebuf; X bp->b_tmpfile[0] = 0; X bp->b_fd = -1; X for (;;) { X while ((c = getc(ec)) != '\n' && c) { X if (ec == '\'') X c &= ~ QUOTE; X if (savec(c, bp, nolit) == 0) { X c = 0; X break; X } X } X savec(0, bp, nolit); X if (strcmp(s, bp->b_linebuf) == 0 || c == 0) X break; X savec('\n', bp, nolit); X } X *bp->b_linebuf = 0; X if (c == 0) { X prs("here document `"); prs(s); err("' unclosed"); X } X quitenv(); X } X return(bp); X} X Xstatic Xsavec(c, bp, nolit) Xregister struct block *bp; X{ X /* jrp - gutted routine completely, modified to use temp file. */ X X /* If the file is not open, see if a filename needs to be X * created. If so, create one. Then create the file. X */ X char * lp; X char * cp; X static int inc; X int len; X X if(bp->b_fd < 0) { X if(bp->b_tmpfile[0] == 0) { X /* Key this by the PID plus a tag... */ X for (cp = bp->b_tmpfile, lp = "/tmp/shtm"; X (*cp = *lp++) != '\0'; cp++) X ; X X inc = (inc + 1) % 100; X lp = putn(getpid()*100 + inc); X for (; (*cp = *lp++) != '\0'; cp++) X ; X } X X /* Create the file, then open it for X * read/write access. After opening the X * file, unlink it to it'll go away when X * we're through using it. X */ X bp->b_fd = creat(bp->b_tmpfile, 0600); X close(bp->b_fd); X bp->b_fd = open(bp->b_tmpfile, 2); X unlink(bp->b_tmpfile); X if(bp->b_fd < 0) { X return(0); X } X } X X /* Stuff the character into the line buffer. If it's a X * newline, then insert it before the trailing null, write X * out the line, and reset the line buffer. X */ X if(c == '\n') { X bp->b_next[-1] = '\n'; X bp->b_next[0] = '\0'; X len = strlen(bp->b_linebuf); X X /* Write this out, unless the line ended X * with a backslash... X */ X if((len > 1) && (bp->b_next[-2] != '\\')) { X write_linebuf(bp, nolit); X } X X return(1); X } X else { X if(bp->b_next == &(bp->b_linebuf[NCPB - 1])) { X prs("here: line buffer full\n"); X return(0); X } X *(bp->b_next++) = c; X return(1); X } X} X Xwrite_linebuf(bp, nolit) Xstruct block * bp; X{ X X char c; X jmp_buf ev; X X if(nolit) { X if (newenv(setjmp(errpt = ev)) == 0) { X PUSHIO(aword, bp->b_linebuf, strchar); X setbase(e.iop); X e.iop->task |= XHERE; X while ((c = subgetc(0, 0)) != 0) { X c &= ~ QUOTE; X write(bp->b_fd, &c, sizeof c); X } X quitenv(); X X } X } X else { X write(bp->b_fd, bp->b_linebuf, strlen(bp->b_linebuf)); X } X X /* Zap the line buffer for next time... */ X bp->b_next = bp->b_linebuf; X bp->b_linebuf[0] = 0; X} X Xherein(bp, xdoll) Xstruct block *bp; X{ X int ret_fd; X X if (bp == 0) X return(-1); X X /* If we have a temp file, then rewind it to the beginning */ X if(bp->b_fd < 0) { X return(-1); X } X X lseek(bp->b_fd, 0L, 0); X X /* Free up this block pointer, as we're X * not going to need it anymore. X */ X xfree(bp->b_linebuf); X xfree(bp); X X return(bp->b_fd); X} X Xscraphere() X{ X struct here * h; X struct here * nexth; X struct block * bp; X X X /* Close and unlink any files associated with X * heres in progress, and free up all the X * associated structures. X */ X h = herelist; X while(h != NULL) { X nexth = h->h_next; X bp = (struct block *)h->h_iop->io_un.io_here; X if(bp != NULL) { X if(bp->b_fd >= 0) { close(bp->b_fd); } X if(*bp->b_tmpfile) { unlink(bp->b_tmpfile); } X xfree(bp->b_linebuf); X xfree(bp); X } X xfree(h); X h = nexth; X } X X herelist = NULL; X} X Xchar * Xmemcpy(ato, from, nb) Xregister char *ato, *from; Xregister int nb; X{ X register char *to; X X to = ato; X while (--nb >= 0) X *to++ = *from++; X return(ato); X} + END-OF-FILE sh5.c chmod 'u=rw,g=r,o=r' \s\h\5\.\c set `sum \s\h\5\.\c` sum=$1 case $sum in 38678) :;; *) echo 'Bad sum in '\s\h\5\.\c >&2 esac echo Extracting \s\h\6\.\c sed 's/^X//' > \s\h\6\.\c << '+ END-OF-FILE '\s\h\6\.\c X#define Extern X X#include <signal.h> X#include <errno.h> X#include <setjmp.h> X#include "sh.h" X + END-OF-FILE sh6.c chmod 'u=rw,g=r,o=r' \s\h\6\.\c set `sum \s\h\6\.\c` sum=$1 case $sum in 30298) :;; *) echo 'Bad sum in '\s\h\6\.\c >&2 esac exit 0