#include "sh.h" /* * Shell * * Modified by Bill Joy * UC Berkeley 1976/1977 * Features remaining to be fixed up and/or implemented ======== ========= == == ===== == === == =========== * Commands insert and delete on argument lists should be * implemented also set 3= when have 2 arguments should be * handled somehow (perhaps optionally valid). * Also note that $12 should really be done correctly. * A notion of terse ? * time time ... time time date etc. are funny * These should be implemented correctly with the internal versions * of exit repeat if goto etc. Probably want to maintain a timed * process number list and check this when processes die (this uses * less processes than forking a time). * Better facilities for string manipulation ? * The exit status variable should be implemented (status) * and set too by the internal commands. When do this get * rid of kludge about prompt not set for exit on error or * fix up in some way. Also should rewrite the argument * processing section to be cleaner and make some sense. * Do verbose more cleanly. * Implement next (recursively!) * Implement shell filters and <<. This probably involves some more * modularization of the scanner and deciphering of the two different * options available in the bell shell. * Other stuff from Bell shell - * * Any more control facilities ? * || && * <>, ><, >2, 2 >, etc. * Terminate * Can we build high level commands out of gotos which have * optional defaults and ifs that set variables which one * can test ? Think so ! * but what about "for" ? * Prompt for more input ? * Command substitution ` ` ? * Trap ? * Exec ? * Eval ? * .acct ? * Some way to set stdin to be input when filters can happen. * What about file name substitution and quoting in set. * Conditional expressions should be fixed up with set name@ * and to allow quoting at least between ? ... : ... }. * internal if, repeat, nohup, goto, exit, echo, echononl * Repeat, time, nohup, etc. should allow * * repeat 10 { sleep 5; echo hi } & * time { pc -l *.p ; obj } * or perhaps with ('s since that is illegal now anyways * although the above is more like if. * Changes to glob to allow ~, prevent too long path, and prevent * perhaps running out of directories. Also change it so it doesn't * exit so ungracefully, i.e. on: * chdir /asdf/adfas/a* * In error diagnostics the command name is often not set or set * wrong on calls to bferr. Could have more calls to bferr2. * Should be able to distinguish between ``No more processes'' and * ``Too many processes.'' * Shell flags to exit on error, make undefined variables an error. * Interrupted waits and errors in glob cost storage. * Scratch should go away. * Set a="abc" should print back similarly. * Last, but certainly not least, some of the INTERLISP redo etc features. */ char prompt[] "prompt"; char pcs[] "pcs"; char shell[] "shell"; char pid[] "pid"; char home[] "home"; char path[] "path"; char n_args[] "nargs"; char tim[] "time"; char *narginp, nonelflg, nverbose; main(c, av) int c; char **av; { register f; register char **v, *cp; settimes(); v = av; uid = getuid(); loginsh = **v == '-'; set(home, gethome() == 0 ? savestr(hentry.home) : "."); set1("0404", "/usr/bin/px", &interps); set(prompt, uid == 0 ? "#\240" : "\246\240"); set(shell, "/usr/bin/ashell"); set(pid, putn(getpid())); set1(tim, "tyme", &aliases); for (f = 3; f < 15; f++) close(f); if (c > 1) { if (*(cp = v[1]) == '-') { do switch (*cp++) { case 'V': verbose = 1; case 'v': nverbose = 1; break; case 'c': if (c > 2) narginp = v[2]; goto l1; case 't': nonelflg = 2; case '\0': l1: set(prompt, ""); unsetv(prompt); case 'i': **v = '-'; nofile++; break; } while (*cp); v++; c--; } if (nofile == 0 && c > 1) { set(prompt, ""); unsetv(prompt); close(0); if (open(cp = v[1], 0) < 0) { prs(v[1]); err(": Cannot open"); exit(1); } } } if (gtty(0, scratch) == 0 && gtty(1, scratch) == 0) **v = '-'; c--; v++; if (c == 0) { c = 1; v = av; } setargs(c, v); set(path, "-/bin-/usr/bin"); pfile("/.profile"); if (loginsh) pfile("/.login"); arginp = narginp; onelflg = nonelflg; verbose = nverbose; if (**av == '-') { setintr++; signal(QUIT, 1); signal(INTR, 1); } /* mask the name ?? */ process(1); if (loginsh) prs("logout\n"); goodbye(loginsh); } pfile(cp) char *cp; { int oldinput, c; strcpy(scratch, hentry.home); strcat(scratch, cp); oldinput = dup(0); close(0); c = open(scratch, 0); if (c == 0) { process(0); close(0); } dup(oldinput); close(oldinput); } goodbye(f) int f; { if (f) { signal(QUIT, 1); signal(INTR, 1); setintr = 0; pfile("/.logout"); } exit(0); } process(pro) { register char *cp; setexit(); if (doneinp) { doneinp = 0; return; } for (;;) { cp = value(prompt); if (pro) prs(cp); for (; *cp; cp++) echo(*cp); main1(); } } main1() { register int *t; error = 0; lex(¶ml); if (error) { err(error); freelex(¶ml); return; } t = syntax(paraml.next, ¶ml); if (error) err(error); else execute(t); freesyn(t); freelex(¶ml); } echo(c) char c; { c =& 0177; if (verbose) write(2, &c, 1); return (c); } err(s) char *s; { prs(s); prs("\n"); s = value(prompt); if (*s == 0) { seek(0, 0, 2); exit(1); } } prs(os) register char *os; { register char *s; s = os; if (s != 0) { while (*s) s++; write(2, os, s-os); } } digit(c) char c; { c =& 0177; return (c >= '0' && c <= '9'); } letter(c) char c; { c =& 0177; return (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z'); } any(c, s) int c; register char *s; { while (*s) if (*s++ == c) return(1); return(0); } char htmp[] "/etc/htmp"; int tty; gethome() { char buf[100]; register int i; register char *bufp, *cp; if (loginsh) exechome(); tty = ttyn(2); if (readhome() == 0 && hentry.uid == uid) return (0); if (getpw(uid, buf) && sleep(15), getpw(uid, buf)) return (-1); bufp = buf; for (i = 1; i < 6; i++) { while (*bufp != ':') if (*bufp++ == 0) return (-1); bufp++; } /* printf("buf %s bufp %s", buf, bufp); */ for (cp = bufp; *cp && *cp != ':'; cp++) continue; *cp = 0; /* printf("bufp %s\n", bufp); */ hentry.uid == uid; for (i = 0; i < (sizeof hentry.home - 1); i++) hentry.home[i] = *bufp ? *bufp++ : 0; return (0); } readhome() { int htmpf; htmpf = open(htmp, 0); if (htmpf < 0) return (-1); if (seek(htmpf, tty * sizeof hentry, 0) < 0) { close(htmpf); return (-1); } if (read(htmpf, &hentry, sizeof hentry) != sizeof hentry) { close(htmpf); return (-1); } close (htmpf); return (0); } exechome() { int status; static reenter; if (reenter) return; reenter = 1; /* if (fork()) { wait(&status); if (tty == 'x') tty = status >> 8; } else { execl("/usr/bin/sethome", loginsh ? "-" : "x", 0); exit(0177); } */ } getpw(uid, buf) int uid; char buf[]; { char pbuf[512]; register int n; register char *bp, *pbufp; int pwf, m, pbufc; pwf = open("/etc/passwd", 0); if (pwf < 0) return (1); pbufc = 0; for (;;) { bp = buf; do { if (pbufc == 0) { pbufc = read(pwf, &pbuf, 512); if (pbufc <= 0) return (1); pbufp = pbuf; } pbufc--; } while ((*bp++ = *pbufp++) != '\n'); *bp++ = '\0'; /* printf("uid %d xid %d buf %s", uid, xid(buf), buf); */ if (xid(buf) == uid) return (0); } } xid(buf) char buf[]; { register int i; int uid; register char *bp; register c; bp = buf; for (i = 1; i < 3; i++) { while (*bp != ':') if (*bp++ == 0) return (-1); bp++; } i = 0; while ((c = *bp++) != ':') if (c == 0) return (-1); else if (digit(c)) i = i * 10 + c - '0'; uid = i; i = 0; while ((c = *bp++) != ':') if (c == 0) return (-1); else if (digit(c)) i = i * 10 + c - '0'; return (i << 8 | uid); }