Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: utzoo!mnetor!uunet!seismo!mcvax!botter!ast From: ast@cs.vu.nl (Andy Tanenbaum) Newsgroups: comp.os.minix Subject: MINIX 1.2 shell (part 1 of 2) Message-ID: <1554@botter.cs.vu.nl> Date: Wed, 5-Aug-87 11:16:36 EDT Article-I.D.: botter.1554 Posted: Wed Aug 5 11:16:36 1987 Date-Received: Sat, 8-Aug-87 08:52:11 EDT Reply-To: ast@cs.vu.nl (Andy Tanenbaum) Distribution: world Organization: VU Informatica, Amsterdam Lines: 2145 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