# /* * UC berkeley shell * * Modified by Bill Joy Oct 14, 1976 * * library "lib" * cd for "chdir" * interruptible waits * new accounting scheme * interactive (login shell) option "-i" * shells with input and output tty's ignore interrupts * verbose option "-v" causes input commands to be echoed * $* for all arguments $1 to end * >* >>* and |* to put unit 2 there also * `cmd1 ; ... ; cmdn &*' means `( cmd1 ; ... ; cmdn ) &' * pascal objects are recognized and px's are forked * gimme and normal * working directory not searched for root */ char shell[] "/bin/sh"; char glob[] "/etc/glob2"; char px[] "/bin/px"; #define PCXN 0404 #define INTR 2 #define QUIT 3 #define LINSIZ 1000 #define ARGSIZ 50 #define TRESIZ 100 #define LIBSIZ 100 #define QUOTE 0200 #define FAND 1 #define FCAT 2 #define FPIN 4 #define FPOU 8 #define FPAR 16 #define FINT 32 #define FPRS 64 #define FDIAG 128 #define TCOM 1 #define TPAR 2 #define TFIL 3 #define TLST 4 #define DTYP 0 #define DLEF 1 #define DRIT 2 #define DFLG 3 #define DSPR 4 #define DCOM 5 #define ENOEXEC 8 #define ENOMEM 12 int niceness; char *dolp; int *dolnxt; char pidp[7]; int dolc; char **dolv; char *promp "% "; char *linep; char **argp; int *treep; char peekc; char gflg; char nofile; char error; char acctf; char *accpt; char *accbuf; int acct; char acnm[] "/usr/adm/sha\0\0\0"; char ty; char tyed; char echo; int uid; char setintr; char *arginp; char onelflg; char *mesg[] { 0, "Hangup", 0, "Quit", "Illegal instruction", "Trace/BPT trap", "IOT trap", "EMT trap", "Floating exception", "Killed", "Bus error", "Memory fault", "Bad system call", 0, "Alarm clock", "Sig 15", "Sig 16", }; char line[LINSIZ]; char *args[ARGSIZ]; int trebuf[TRESIZ]; char *lib; char *numpt &pidp[sizeof pidp - 1]; main(c, av) int c; char **av; { register f; register char **v, *cp; for(f=3; f<15; f++) close(f); numb(numb(getpid())); if ((uid = getuid()) == 0) promp[0] = '#'; v = av; dolnxt = &v[c]; if (c > 1) { if (*(cp = v[1]) == '-') { do switch(*cp++) { case 'v': echo++; break; case 'c': if (c > 2) arginp = v[2]; goto l1; case 't': onelflg = 2; case '\0': l1: *promp = '\0'; case 'i': **v = '-'; nofile++; break; } while (*cp); v++; c--; } if (nofile == 0 && c > 1) { close(0); if (open(cp = v[1], 0) < 0) { prs(v[1]); err(": cannot open"); } *promp = '\0'; } } if ((ty = ttyn(0)) != 'x' && ttyn(1) != 'x') **v = '-'; if (**av == '-') { setintr++; signal(QUIT, 1); signal(INTR, 1); } dolv = v+1; dolc = c-1; numpt = &acnm[sizeof acnm - 1]; numb((uid>>8) & 0377); numb(uid & 0377); if ((acctf = open(acnm, 1)) >= 0 || echo) accbuf = sbrk(256); setexit(); for(;;) { prs(promp); if ((cp = accbuf) != 0) { *cp++ = ty; tyed++; *cp++ = ' '; accpt = cp; acct = 254; } peekc = getc(); main1(); } } numb(a) { register i, j; i = a; j = 3; do { *--numpt = (i % 10) | '0'; i =/ 10; } while(--j); return(i); } acflush() { register f, c; register char *b; if ((b = accbuf) != 0) { c = 256 - acct; if ((f = acctf)>= 0) { seek(f, 0, 2); write(f, b, c); } if (echo) { if (tyed) b[0] = uid ? '%' : '#'; seek(2, 0, 2); write(2, b, c); } accpt = b; acct = 256; } } main1() { register char c, *cp; register *t; argp = args; linep = line; error = 0; gflg = 0; do { cp = linep; word(); } while(*cp != '\n'); treep = trebuf; if (gflg == 0) { if (error == 0) t = syntax(args, argp); if (error != 0) err("syntax error"); else { acflush(); execute(t); } } } word() { register char c, c1; *argp++ = linep; loop: switch(c = getc()) { case ' ': case '\t': goto loop; case '\'': case '"': c1 = c; while((c = readc()) != c1) { if (c == '\n') { error++; peekc = c; return; } *linep++ = c|QUOTE; } goto pack; case '&': *linep++ = c; if ((c = getc()) == '*') *linep++ = c; else peekc = c; *linep++ = '\0'; return; case ';': case '<': case '>': case '(': case ')': case '|': case '^': case '\n': *linep++ = c; *linep++ = '\0'; return; } peekc = c; pack: for(;;) { c = getc(); if (any(c, " '\"\t;&<>()|^\n")) { peekc = c; if (any(c, "\"'")) goto loop; *linep++ = '\0'; return; } *linep++ = c; } } tree(n) int n; { register *t; t = treep; treep =+ n; if (treep > &trebuf[TRESIZ]) { prs("Command line overflow\n"); error++; reset(); /* UGH */ } return(t); } getc() { register c; if (peekc) { c = peekc; peekc = 0; return(c); } if (argp > &args[ARGSIZ-5]) { argp =- 10; while((c=getc()) != '\n'); argp =+ 10; err("Too many args"); gflg++; return(c); } if (linep > &line[LINSIZ-5]) { linep =- 10; while((c=getc()) != '\n'); linep =+ 10; err("Too many characters"); gflg++; return(c); } for(;;) { if (dolp) { if ((c = *dolp++) != '\0') return(c); dolp = *dolnxt; c = dolp; if (c != -1) { dolnxt++; return(' '); } dolp = 0; } if ((c = readc()) == '\\') if ((c = readc()) == '\n') return(' '); else return(c | QUOTE); else if (c == '$') { c = readc(); if (c >= '0' && c <= '9') { if (c - '0' < dolc) dolp = dolv[c - '0']; continue; } else if (c == '$') { dolp = &pidp[1]; continue; } else if (c == '*') { if (dolc > 1) { dolp = dolv[1]; dolnxt = &dolv[2]; } continue; } } return(c&0177); } } readc() { char cc; register c; register char *cp; if (arginp) { if (arginp == 1) exit(0); else if ((c = *arginp++) == '\0') { arginp = 1; c = '\n'; } } else if (onelflg == 1 || read(0, &cc, 1) != 1) exit(0); else if ((c = cc) == '\n' && onelflg) onelflg--; if ((cp = accpt) != 0) { *cp++ = c; if (--acct == 0) acflush(); else accpt = cp; } return(c); } /* * syntax * empty * syn1 */ syntax(p1, p2) char **p1, **p2; { while(p1 != p2) { if (any(**p1, ";&\n")) p1++; else return(syn1(p1, p2, 1)); } return(0); } /* * syn1@fl=0 * syn2 * syn2 ; syntax * syn2 & * * When fl=1 syn1 handles the vagarities of &* */ syn1(p1, p2, fl) char **p1, **p2; { register char **p; register *t, *t1; int l; top: l = 0; for(p=p1; p!=p2; p++) switch(**p) { case '(': l++; continue; case ')': l--; if (l < 0) error++; /* too many ')'s */ continue; case ';': case '\n': if (fl) continue; case '&': if (l == 0) { if (fl) { l = (*p)[1]; if (l == '*') **p = ';'; t1 = syn1(p1, p+1, 0); if (l == '*') if (t1[DTYP] == TLST) { t = tree(5); t[DTYP] = TPAR; t[DLEF] = 0; t[DRIT] = 0; t[DFLG] = FAND|FPRS|FINT; t[DSPR] = t1; t1 = t; } else { t = t1[DLEF]; t[DFLG] =| FAND|FPRS|FINT; } } else { l = **p; t1 = syn2(p1, p); if (l == '&') t1[DFLG] =| FAND|FPRS|FINT; } t = tree(4); t[DTYP] = TLST; t[DFLG] = 0; t[DLEF] = t1; t[DRIT] = syntax(p+1, p2); return(t); } } if (l == 0) if (fl) { fl = 0; goto top; } else return(syn2(p1, p2)); error++; } /* * syn2 * syn3 * syn3 | syn2 */ syn2(p1, p2) char **p1, **p2; { register char **p; register int l, *t; l = 0; for(p=p1; p!=p2; p++) switch(**p) { case '(': l++; continue; case ')': l--; continue; case '|': case '^': if (l == 0) { t = tree(4); t[DTYP] = TFIL; t[DLEF] = syn3(p1, p); p++; if (p != p2 && **p == '*') { t[DFLG] = FDIAG; if ((*p)[1] != '\0') { (*p)++; p--; } } else { t[DFLG] = 0; p--; } t[DRIT] = syn2(p+1, p2); return(t); } } return(syn3(p1, p2)); } /* * syn3 * ( syn1@1 ) [ < in ] [ > out ] * word word* [ < in ] [ > out ] */ syn3(p1, p2) char **p1, **p2; { register char **p; char **lp, **rp; register *t; int n, l, i, o, c, flg; flg = 0; if (**p2 == ')') flg =| FPAR; lp = 0; rp = 0; i = 0; o = 0; n = 0; l = 0; for(p=p1; p!=p2; p++) switch(c = **p) { case '(': if (l == 0) { if (lp != 0) error++; lp = p+1; } l++; continue; case ')': l--; if (l == 0) rp = p; continue; case '>': p++; if (p!=p2 && **p=='>') flg =| FCAT; else p--; p++; if (p != p2 && **p == '*') { flg =| FDIAG; if ((*p)[1] != '\0') { (*p)++; p--; } } else p--; case '<': if (l == 0) { p++; if (p == p2) { error++; p--; } if (any(**p, "<>(")) error++; if (c == '<') { if (i != 0) error++; i = *p; } else { if (o != 0) error++; o = *p; } } continue; default: if (l == 0) p1[n++] = *p; } if (lp != 0) { if (n != 0) error++; t = tree(5); t[DTYP] = TPAR; t[DSPR] = syn1(lp, rp, 1); } else { if (n == 0) error++; p1[n++] = 0; t = tree(n+5); t[DTYP] = TCOM; for(l=0; l<n; l++) t[l+DCOM] = p1[l]; } t[DFLG] = flg; t[DLEF] = i; t[DRIT] = o; return(t); } scan(at, f) int *at; int (*f)(); { register char *p, c; register *t; t = at+DCOM; while(p = *t++) while(c = *p) *p++ = (*f)(c); } tglob(c) int c; { if (any(c, "[?*")) gflg = 1; return(c); } trim(c) int c; { return(c&0177); } endwait() { signal(INTR, 1); prs("wait: interrupted\n"); reset(); /* URK! */ } execute(t, pf1, pf2) int *t, *pf1, *pf2; { int i, f, pv[2]; register *t1; register char *cp1, *cp2; char *scp; extern errno; if (t != 0) switch(t[DTYP]) { case TCOM: cp1 = t[DCOM]; if (uid == 0) { if (equal(cp1, "gimme")) { nice(-20); return; } if (equal(cp1, "normal")) { nice(0); return; } } if (equal(cp1, "chdir") || equal(cp1, "cd")) { if (t[DCOM+1] != 0) { scan(t, &trim); if (chdir(t[DCOM+1]) < 0) { prs(cp1); err(": bad directory"); } } else { prs(cp1); err(": arg count"); } return; } if (equal(cp1, "shift")) { if (dolc < 1) { prs("shift: no args\n"); return; } dolv[1] = dolv[0]; dolv++; dolc--; return; } if (equal(cp1, "lib")) { if (cp2 = *(t1 = &t[DCOM+1])) { if (!(cp1 = lib)) cp1 = lib = sbrk(LIBSIZ+1); i = LIBSIZ; for(;;) { while(*cp1++ = (*cp2++ & 0177)) if (--i == 0) { err("lib: path too long"); lib[0] = 0; return; } if (!(cp2 = *++t1)) return; *(cp1-1) = ' '; } } else { prs(lib); prs("\n"); } return; } if (equal(cp1, "wait")) { if (setintr && *promp) { signal(INTR, endwait); pwait(-1); signal(INTR, 1); } else pwait(-1); return; } if (equal(cp1, ":")) return; case TPAR: t1 = t; f = t1[DFLG]; i = 0; if ((f&FPAR) == 0) i = fork(); if (i == -1) { err("try again"); return; } if (i != 0) { if ((f&FPIN) != 0) { close(pf1[0]); close(pf1[1]); } if ((f&FPRS) != 0) { prn(i); prs("\n"); } if ((f&FAND) != 0) return; if ((f&FPOU) == 0) pwait(i); return; } if ((cp1 = t1[DLEF]) != 0) { close(0); if (open(cp1, 0) < 0) { prs(cp1); err(": cannot open"); exit(1); } } if ((cp2 = t1[DRIT]) != 0) { close(1); if ((f&FCAT) != 0 && open(cp2, 1) >= 0) seek(1, 0, 2); else if (creat(cp2, 0644) < 0) { prs(t1[DRIT]); err(": cannot create"); exit(1); } } if ((f&FPIN) != 0) { close(0); dup(pf1[0]); close(pf1[0]); close(pf1[1]); } if ((f&FPOU) != 0) { close(1); dup(pf2[1]); close(pf2[0]); close(pf2[1]); } if (f&FDIAG) { close(2); dup(1); } if ((f&FINT)!=0 && t1[DLEF]==0 && (f&FPIN)==0) { close(0); open("/dev/null", 0); } if ((f&FINT) == 0 && setintr) { signal(INTR, 0); signal(QUIT, 0); } if (t1[DTYP] == TPAR) { if (t1 = t1[DSPR]) t1[DFLG] =| f&FINT; execute(t1); exit(1); } close(acctf); gflg = 0; scan(t1+1, &tglob); if (gflg) { t1 = &t1[DSPR-1]; *t1++ = glob; if ((*t1 = lib) == 0) *t1 = &""; t1--; execv(*t1, t1); prs("glob: cannot execute\n"); exit(1); } scan(t1, &trim); cp1 = t1[DCOM]; while(*cp1 && *cp1 != '/') cp1++; if (*cp1 || uid) texec(t1[DCOM], t1); if (!*cp1) { if (cp2 = lib) for(;;) { while (*cp2 == ' ') cp2++; if (!*cp2) break; cp1 = linep; while(*cp2) if (*cp2 == ' ') break; else *cp1++ = *cp2++; scp = cp2; *cp1++ = '/'; cp2 = t[DCOM]; while(*cp1++ = *cp2++) continue; texec(linep, t1); cp2 = scp; } cp1 = linep; cp2 = "/usr/bin/"; while(*cp1 = *cp2++) cp1++; cp2 = t1[DCOM]; while(*cp1++ = *cp2++); texec(linep+4, t1); texec(linep, t1); } prs(t1[DCOM]); err(": not found"); exit(1); case TFIL: f = t[DFLG]; pipe(pv); t1 = t[DLEF]; t1[DFLG] =| FPOU | (f&(FPIN|FINT|FPRS|FDIAG)); execute(t1, pf1, pv); t1 = t[DRIT]; t1[DFLG] =| FPIN | (f&(FPOU|FINT|FAND|FPRS)); execute(t1, pv, pf2); return; case TLST: f = t[DFLG]&FINT; if (t1 = t[DLEF]) t1[DFLG] =| f; execute(t1); if (t1 = t[DRIT]) t1[DFLG] =| f; execute(t1); return; } } texec(f, at) int *at; { extern errno; register int *t, *t1; register i; int w; t = at; execv(f, (t1 = &t[DCOM])); if (errno == ENOEXEC) { *t1 = f; *--t1 = shell; i = open(f, 0); if (i >= 0) { if (read(i, &w, 2) == 2 && w == PCXN) *t1 = px; close(i); } execv(*t1, t1); prs("No "); prs(*t1); err("!!\n"); exit(1); } else if (errno==ENOMEM) { prs(*t1); err(": too large"); exit(1); } } err(s) char *s; { prs(s); prs("\n"); if (*promp == '\0') { seek(0, 0, 2); exit(); } } prs(as) char *as; { register char *s; if (s = as) { while(*s) s++; write(2, as, s-as); } } prn(n) int n; { register a; if (a = n/10) prn(a); n = n % 10 + '0'; write(2, &n, 1); } getn(acp) char *acp; { register i; register char c, *cp; cp = acp; i = 0; while ((c = *cp++) != '\0') if (c >= '0' && c <= '9') i = 10 * i + c - '0'; return(i); } any(c, as) int c; char *as; { register char *s; s = as; while(*s) if (*s++ == c) return(1); return(0); } equal(as1, as2) char *as1, *as2; { register char *s1, *s2; s1 = as1; s2 = as2; while(*s1++ == *s2) if (*s2++ == '\0') return(1); return(0); } pwait(ai) int ai; { register p, e, i; int s; if ((i = ai) != 0) do { p = wait(&s); if (p == -1) break; e = s & 0177; if (mesg[e] != 0) { if (p != i) { prn(p); prs(": "); } prs(mesg[e]); if (s & 0200) prs(" -- Core dumped"); } if (e != 0) err(""); } while (i != p); }