+#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);
+}