BSD 1 development
authorBill Joy <wnj@ucbvax.Berkeley.EDU>
Wed, 23 Nov 1977 20:45:02 +0000 (12:45 -0800)
committerBill Joy <wnj@ucbvax.Berkeley.EDU>
Wed, 23 Nov 1977 20:45:02 +0000 (12:45 -0800)
Work on file ashell/sh_func.c
Work on file ashell/sh_glob.c
Work on file ashell/errs
Work on file ashell/printsh
Work on file ashell/sh_parse.c
Work on file ashell/makesh
Work on file ashell/install
Work on file ashell/sh_exec.c
Work on file ashell/sh.c
Work on file ashell/sh.h
Work on file ashell/sh_lex.c
Work on file ashell/sh_set.c
Work on file ashell/sh_sem.c
Work on file ashell/sh_wait.c
Work on file ashell/READ_ME
Work on file pxref/READ_ME
Work on file pxref/printpxref
Work on file pxref/pxref.p
Work on file pxref/pxref.c
Work on file pxref/install
Work on file pxref/makepxref

Synthesized-from: 1bsd

21 files changed:
ashell/READ_ME [new file with mode: 0644]
ashell/errs [new file with mode: 0644]
ashell/install [new file with mode: 0644]
ashell/makesh [new file with mode: 0644]
ashell/printsh [new file with mode: 0755]
ashell/sh.c [new file with mode: 0644]
ashell/sh.h [new file with mode: 0644]
ashell/sh_exec.c [new file with mode: 0644]
ashell/sh_func.c [new file with mode: 0644]
ashell/sh_glob.c [new file with mode: 0644]
ashell/sh_lex.c [new file with mode: 0644]
ashell/sh_parse.c [new file with mode: 0644]
ashell/sh_sem.c [new file with mode: 0644]
ashell/sh_set.c [new file with mode: 0644]
ashell/sh_wait.c [new file with mode: 0644]
pxref/READ_ME [new file with mode: 0644]
pxref/install [new file with mode: 0755]
pxref/makepxref [new file with mode: 0755]
pxref/printpxref [new file with mode: 0755]
pxref/pxref.c [new file with mode: 0644]
pxref/pxref.p [new file with mode: 0644]

diff --git a/ashell/READ_ME b/ashell/READ_ME
new file mode 100644 (file)
index 0000000..16072b1
--- /dev/null
@@ -0,0 +1,24 @@
+Wed Oct 19, 1977
+
+This directory contains the source for a shell.
+It requires floating point to do the time command which is built-in
+so you will have to cc it -f on machines without floating point.
+It also requires a version 7 C compiler.
+
+Accurate documentation is in the file "sh.6" to be nroffed with
+/usr/man/man0/naa and a new "version 7" nroff.
+
+This shell requires the "htmp" data base also used by the editor "ex".
+If you do not set it up so that the "sethome" command is done by "login"
+then you should use the old "osethome" routine in ../s6 rather than "sethome"
+and reenable the execl of this sethome in the file "sh.c" (with the correct
+pathname).
+
+                               Bill Joy
+                               CS Division
+                               Department of EE and CS
+                               UC Berkeley
+                               Berkeley, California  94704
+
+                               (415) 524-4510          [HOME]
+                               (415) 642-4948          [SCHOOL]
diff --git a/ashell/errs b/ashell/errs
new file mode 100644 (file)
index 0000000..1a8c89f
--- /dev/null
@@ -0,0 +1,10 @@
+cc -f -n -O *.c ../lib/libX.a -lS
+sh.c:
+sh_exec.c:
+sh_func.c:
+sh_glob.c:
+sh_lex.c:
+sh_parse.c:
+sh_sem.c:
+sh_set.c:
+sh_wait.c:
diff --git a/ashell/install b/ashell/install
new file mode 100644 (file)
index 0000000..0ff8bf3
--- /dev/null
@@ -0,0 +1,2 @@
+cp a.out /usr/bin/ashell
+echo New ashell installed
diff --git a/ashell/makesh b/ashell/makesh
new file mode 100644 (file)
index 0000000..7a31370
--- /dev/null
@@ -0,0 +1 @@
+cc -f -n -O *.c ../lib/libX.a -lS
diff --git a/ashell/printsh b/ashell/printsh
new file mode 100755 (executable)
index 0000000..8e795ca
--- /dev/null
@@ -0,0 +1,2 @@
+pr printsh
+pr sh.h sh.c sh_*.c
diff --git a/ashell/sh.c b/ashell/sh.c
new file mode 100644 (file)
index 0000000..c638bbc
--- /dev/null
@@ -0,0 +1,452 @@
+#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(&paraml);
+       if (error) {
+               err(error);
+               freelex(&paraml);
+               return;
+       }
+       t = syntax(paraml.next, &paraml);
+       if (error)
+               err(error);
+       else
+               execute(t);
+       freesyn(t);
+       freelex(&paraml);
+}
+
+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);
+}
diff --git a/ashell/sh.h b/ashell/sh.h
new file mode 100644 (file)
index 0000000..b645ebf
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Shell header file
+ */
+#define        E
+#define        INTR    2
+#define        QUIT    3
+
+#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        ENOENT  2
+#define        ENOEXEC 8
+#define        ENOMEM  12
+#define        EACCES  13
+#define        ENOTDIR 20
+
+struct shvar {
+       char    *value;
+       char    *name;
+       struct  shvar *next;
+} shvhed, paraml, aliases, interps;
+
+struct shvar2 {
+       char    *value;
+       struct  shvar *prev;
+       struct  shvar *next;
+};
+
+char   verbose;
+char   nofile;
+char   *error;
+int    uid;
+char   loginsh;
+char   doneinp;
+char   setintr;
+char   *arginp;
+char   onelflg;
+char   scratch[100];
+
+char   prompt[], shell[], pid[], pcs[], home[], path[], n_args[], tim[];
+
+char   *value(), *value1();
+char   **glob();
+struct shvar *adrof(), *adrof1();
+
+#define        seterr(s)       if (error == 0) error = s;
+int    errno;
+
+struct htmp {
+       int     uid;
+       char    home[28];
+       int     ttytype;
+} hentry;
diff --git a/ashell/sh_exec.c b/ashell/sh_exec.c
new file mode 100644 (file)
index 0000000..609796d
--- /dev/null
@@ -0,0 +1,180 @@
+#include "sh.h"
+
+int    tglob(), trim();
+static int gflag;
+static char *exerr, *expath;
+
+char   nic[]   "nice";
+
+doexec(t)
+       int *t;
+{
+       register char *cp, *pp;
+       register char **av;
+       char slash, pathc;
+       char *vp;
+
+       if (adrof(nic))
+               nice(getn(value(nic)));
+       cp = globone(t[DCOM]);
+       if (cp == 0)
+               exit(1);
+       exerr = 0;
+       expath = t[DCOM] = cp;
+       slash = gflag || any ('/', cp);
+       gflag = 0;
+       av = &t[DCOM+1];
+       scan(av, &tglob);
+       if (gflag) {
+               av = glob(av);
+               if (av == 0) {
+                       prs("No match\n");
+                       exit(1);
+               }
+       }
+       *--av = t[DCOM];
+       scan(av, &trim);
+       pp = value(path);
+       if (*pp == 0 || slash)
+               pp = "";
+       do {
+               for (cp = pp; *pp && *pp != '-' ; pp++)
+                       *pp =& ~QUOTE;
+               pathc = *pp;
+               *pp = 0;
+               vp = strcpy(calloc(1, strlen(cp) + strlen(*av) + 2), cp);
+               if (cp == pp || slash == 0) {
+                       if (cp[0])
+                               strcat(vp, "/");
+                       strcat(vp, *av);
+                       texec(vp, av-DCOM);
+                       xfree(vp);
+               }
+       } while ((*pp++ = pathc) != 0);
+       prs(expath);
+       if (exerr) {
+               err(exerr);
+               xfree(exerr);
+       } else
+               err(": Cannot find");
+       exit(1);
+}
+
+texec(f, t)
+       register int *t;
+{
+       extern errno;
+       register int *t1;
+       register i;
+       int w;
+
+       execv(f, (t1 = &t[DCOM]));
+       switch (errno) {
+               case ENOEXEC:
+                       *t1 = f;
+                       i = open(f, 0);
+                       if (i < 0) {
+                               prs(*t1);
+                               prs(": Cannot open");
+                               exit(1);
+                       }
+                       w = 0;
+                       read(i, &w, 2);
+                       close(i);
+                       f = interp(w);
+                       if (f != 0)
+                               *--t1 = f;
+                       else if (w & 0100200) {
+                               prs(*t1);
+                               prs(": No interpreter\n");
+                               exit(1);
+                       } else
+                               *--t1 = value(shell);
+                       execv(*t1, t1);
+                       prs(*t1);
+                       /*
+                        * should give better message but can't goto
+                        * top because could loop
+                        */
+                       err(": Execute failed");
+                       exit(1);
+               case ENOMEM:
+                       prs(*t1);
+                       err(": Not enough core");
+                       exit(1);
+               case EACCES:
+                       if (exerr == 0) {
+                               expath = savestr(f);
+                               exerr = ": No execute access";
+                       }
+                       return;
+       }
+}
+
+
+scan(t, f)
+       register int *t;
+       int (*f)();
+{
+       register char *p, c;
+
+       while (p = *t++)
+               while (c = *p)
+                       *p++ = (*f)(c);
+}
+
+tglob(c)
+       char c;
+{
+       if (any(c, "[?*"))
+               gflag = 1;
+       return (c);
+}
+
+trim(c)
+       char c;
+{
+
+       return (c & 0177);
+}
+
+prn(n)
+       int n;
+{
+       register a;
+
+       a = n / 10;
+       if (a != 0)
+               prn(a);
+       n = n % 10 + '0';
+       write(2, &n, 1);
+}
+
+globone(str)
+       register char *str;
+{
+       int gv[2];
+       register char **gvp;
+
+       setname(str);
+       gv[0] = str;
+       gv[1] = 0;
+       gflag = 0;
+       scan(gv, &tglob);
+       if (gflag) {
+               gvp = glob(gv);
+               if (gvp == 0) {
+                       bferr(": No match");
+                       return (0);
+               }
+               str = *gvp++;
+               if (*gvp) {
+                       bferr(": Ambiguous");
+                       return (0);
+               }
+       } else {
+               scan(gv, &trim);
+               str = gv[0];
+       }
+       return (str);
+}
diff --git a/ashell/sh_func.c b/ashell/sh_func.c
new file mode 100644 (file)
index 0000000..816e3b1
--- /dev/null
@@ -0,0 +1,363 @@
+#include "sh.h"
+
+int gimme(), normal(); 
+int chngd(), shift(), doset(), goodbye(), await(), dozip();
+int rewind(), doalias(), unalias(), dotimes(), doalloc();
+int dointerp(), uninterp(), noquit(), quit(), abort();
+
+#define INF    127
+#define        FRET    1
+
+static struct biltins {
+       char    *bname;
+       int     (*bfunct)();
+       char    minargs, maxargs;
+       int     flags;
+} bfunc[] {
+       "chdir",        &chngd,         0,      1,      0,
+       "wait",         &await,         0,      1,      0,
+       "shift",        &shift,         0,      1,      0,
+       "rewind",       &rewind,        0,      0,      0,
+       "set",          &doset,         0,      INF,    0,
+       "unset",        &unset,         1,      INF,    0,
+       "alias",        &doalias,       0,      2,      0,
+       "unalias",      &unalias,       1,      INF,    0,
+       "interp",       &dointerp,      0,      2,      0,
+       "uninterp",     &uninterp,      1,      INF,    0,
+       "logout",       &goodbye,       0,      0,      0,
+       "quit",         &quit,          0,      0,      0,
+       "noquit",       &noquit,        0,      0,      0,
+       "alloc",        &doalloc,       0,      0,      0,
+       "abort",        &abort,         0,      0,      0,
+       ":",            &dozip,         0,      INF,    0,
+       /* "verbose" */
+       "tyme",         &dotimes,       0,      INF,    FRET,
+       /* "nohup" */
+       /* "repeat" */
+       /* "if" */
+       /* "goto" */
+       /* "exit" */
+       0,              0,              0,      0,      0
+};
+static char *bname;
+
+func(t)
+       register int *t;
+{
+       register struct biltins *bp;
+       register int *tp;
+       int i;
+
+       bp = bfunc;
+       for (;;) {
+               if (bp->bname == 0)
+                       return(0);
+               if (strcmp(bp->bname, t[DCOM]) == 0)
+                       break;
+               bp++;
+       }
+       bname = bp->bname;
+       i = 0;
+       for (tp = &t[DCOM+1]; *tp; tp++)
+               i++;
+       if (i < bp->minargs) {
+               bferr(": Too few arguments");
+               return (1);
+       }
+       if (i > bp->maxargs) {
+               bferr(": Too many arguments");
+               return (1);
+       }
+       i = (*bp->bfunct)(&t[DCOM]);
+       return (bp->flags & FRET ? i : 1);
+}
+
+noquit()
+{
+
+       signal(QUIT, 1);
+}
+
+quit()
+{
+
+       signal(QUIT, 0);
+}
+
+abort()
+{
+
+       abort();
+}
+
+dozip()
+{
+       ;
+}
+
+chngd(vp)
+       register char **vp;
+{
+       register char *dp;
+
+       vp++;
+       dp = *vp++;
+       if (dp) {
+               dp = globone(dp);
+               if (dp == 0)
+                       return;
+       } else {
+               dp = value(home);
+               if (*dp == 0) {
+                       bferr(": No home");
+                       return;
+               }
+       }
+       if (chdir(dp) < 0)
+               switch (errno) {
+                       case ENOTDIR:
+                               bferr2(dp, ": Not a directory");
+                               return;
+                       case ENOENT:
+                               bferr2(dp, ": No such file or directory");
+                               return;
+                       case EACCES:
+                               bferr2(dp, ": No execute access");
+                               return;
+                       default:
+                               prs("errno = ");
+                               prn(errno);
+                               prs("\n");
+                               bferr2(dp, ": bad directory !?!");
+                               return;
+               }
+}
+
+prvars()
+{
+
+       plist(&shvhed);
+}
+/*
+prvars()
+{
+       register struct shvar *vp;
+       register char *cp;
+       register int col;
+
+       vp = shvhed.next;
+       if (vp == 0)
+               return;
+       col = 0;
+       cp = unquote(vp->value);
+       for(;;) {
+               prs(vp->name);
+               col =+ strlen(vp->name);
+               if (cp[0]) {
+                       prs("=");
+                       prs(cp);
+                       col =+ strlen(cp) + 1;
+               }
+               xfree(cp);
+               col++;
+               vp = vp->next;
+               if (vp == 0)
+                       break;
+               cp = unquote(vp->value);
+               if (col + strlen(vp->name) + strlen(cp) + 1 > 79) {
+                       prs("\n");
+                       col = 0;
+               } else
+                       prs(" ");
+       }
+       if (col)
+               prs("\n");
+}
+*/
+
+unquote(s)
+       char *s;
+{
+       char *s2;
+       int i;
+       register char *cp, *dp;
+       register c;
+
+       i = 0;
+       for (cp = s; c = *cp++;) {
+               if ((c & QUOTE) == 0)
+                       continue;
+               c =& 0177;
+               i++;
+               if (c == '"' || (*cp & QUOTE) == 0)
+                       continue;
+               while ((c = *cp) && (c & QUOTE)) {
+                       c =& 0177;
+                       if (c == '"')
+                               break;
+                       cp++;
+               }
+               i++;
+       }
+       s2 = calloc(1, strlen(s) + i + 1);
+       for (dp = s2, cp = s; c = *cp++;) {
+               if ((c & QUOTE) == 0) {
+                       *dp++ = c;
+                       continue;
+               }
+               c =& 0177;
+               if (c == '"' || (*cp & QUOTE) == 0) {
+                       *dp++ = '\\';
+                       *dp++ = c;
+                       continue;
+               }
+               *dp++ = '"';
+               *dp++ = c;
+               while ((c = *cp) && (c & QUOTE)) {
+                       c =& 0177;
+                       if (c == '"')
+                               break;
+                       cp++;
+                       *dp++ = c;
+               }
+               *dp++ = '"';
+       }
+       return (s2);
+}
+
+setname(cp)
+       char *cp;
+{
+
+       bname = cp;
+}
+
+bferr(cp)
+       char *cp;
+{
+       prs(bname);
+       err(cp);
+}
+
+bferr2(cp, cp2)
+       char *cp, *cp2;
+{
+       prs(cp);
+       err(cp2);
+}
+
+doalias(v)
+       register char *v[];
+{
+       register struct shvar *vp;
+       register char *p;
+
+       v++;
+       p = *v++;
+       if (p == 0)
+               plist(&aliases);
+       else if (*v == 0) {
+               p = value1(p, &aliases);
+               if (*p != 0) {
+                       prs(p);
+                       prs("\n");
+               }
+       } else
+               set1(p, savestr(*v), &aliases);
+}
+
+unalias(v)
+       register char *v[];
+{
+
+       unset1(v, &aliases);
+}
+
+dointerp(v)
+       register char *v[];
+{
+       register int i;
+       register char *p;
+
+       v++;
+       p = *v++;
+       if (p == 0) {
+               plist(&interps);
+               return;
+       }
+       if (*v == 0) {
+               p = value1(p, &interps);
+               if (*p != 0) {
+                       prs(p);
+                       prs("\n");
+               }
+               return;
+       }
+       i = getn(p);
+       if (i == 0)
+               return;
+       set1(p, savestr(*v), &interps);
+}
+
+uninterp(v)
+       register char *v[];
+{
+       unset1(v, &interps);
+}
+
+interp(w)
+       int w;
+{
+       register struct shvar *vp;
+
+       for (vp = interps.next; vp != 0; vp = vp->next)
+               if (getn(vp->name) == w)
+                       return (vp->value);
+       return (0);
+}
+
+plist(vp)
+       register struct shvar *vp;
+{
+       register char *cp;
+
+       for (vp = vp->next; vp != 0; vp = vp->next) {
+               prs(vp->name);
+               if (vp->value[0]) {
+                       cp = unquote(vp->value);
+                       prs("\t");
+                       prs(cp);
+                       xfree(cp);
+               }
+               prs("\n");
+       }
+}
+
+alias(t)
+       int *t;
+{
+       register struct shvar *ap;
+
+       for (ap = aliases.next; ap != 0; ap = ap->next)
+               if (strcmp(ap->name, *t) == 0) {
+                       xfree(*t);
+                       if (ap->value[0] == 0) {
+                               bferr2(*t, ": Restricted");
+                               *t = ":";
+                       } else
+                               *t = savestr(ap->value);
+                       return (1);
+               }
+       return (0);
+}
+
+extern char end[];
+
+doalloc()
+{
+       char *cp;
+
+       cp = sbrk(0);
+       prn(cp - &end);
+       prs("\n");
+}
diff --git a/ashell/sh_glob.c b/ashell/sh_glob.c
new file mode 100644 (file)
index 0000000..4af2ac1
--- /dev/null
@@ -0,0 +1,260 @@
+#include "sh.h"
+
+#define        STRSIZ  522
+#define        PTHSIZ  100
+
+static char ab[STRSIZ];
+static char *ava[200];
+static char **av;
+static char *string;
+static int ncoll;
+
+static char gpath[100], *gpathp;
+static int globbed;
+static char *entp;
+
+static int stbuff[18];
+
+char **
+glob(v)
+       register char *v[];
+{
+
+       if (adrof("noglob"))
+               return (v);
+       ginit();
+       while (*v)
+               collect(*v++);
+       *av++ = 0;
+       return (ncoll == 0 ? 0 : &ava[2]);
+}
+
+ginit()
+{
+       av = &ava[2];
+       string = ab;
+       ncoll = 0;
+       globbed = 0;
+       gpathp = gpath;
+}
+
+collect(as)
+{
+       char **oav;
+
+       oav = av;
+       globbed = 0;
+       expand(as);
+       sort(oav);
+}
+
+sort(oav)
+char **oav;
+{
+       register char **p1, **p2, **c;
+
+       p1 = oav;
+       while (p1 < av-1) {
+               p2 = p1;
+               while(++p2 < av) {
+                       if (strcmp(*p1, *p2) > 0) {
+                               c = *p1;
+                               *p1 = *p2;
+                               *p2 = c;
+                       }
+               }
+               p1++;
+       }
+}
+
+expand(as)
+char *as;
+{
+       register char *cs;
+       char *sgpathp;
+       register int dirf;
+       static struct {
+               int     ino;
+               char    name[16];
+       } entry;
+
+       sgpathp = gpathp;
+       cs = as;
+       while (*cs != '*' && *cs != '?' && *cs != '[') {
+               if (gpathp >= &gpath[PTHSIZ])
+                       gpatherr();
+               else if ((*gpathp++ = *cs++) == 0) {
+                       if (!globbed)
+                               *av++ = cat(as, "");
+                       else if (stat(gpath, &stbuff) >= 0) {
+                               *av++ = cat(gpath, "");
+                               ncoll++;
+                       }
+                       goto endit;
+               }
+       }
+       gpathp = sgpathp;
+       cs--;
+       while (cs >= as && *cs != '/')
+               cs--;
+       while (as <= cs)
+               if (gpathp >= &gpath[PTHSIZ])
+                       gpatherr();
+               else
+                       *gpathp++ = *as++;
+       *gpathp = 0;
+       dirf = open(gpath, 0);
+       if (dirf<0)
+               if (globbed)
+                       goto endit;
+               else {
+                       prs(gpath);
+                       panic(": cannot open");
+               }
+       globbed++;
+       cs++;
+       while (read(dirf, &entry, 16) == 16) {
+               if (entry.ino==0)
+                       continue;
+               if (match(entry.name, cs)) {
+                       *av++ = cat(gpath, entry.name);
+                       ncoll++;
+               }
+       }
+       close(dirf);
+endit:
+       gpathp = sgpathp;
+       *gpathp = 0;
+}
+
+toolong()
+{
+       panic("Arg list too long");
+}
+
+gpatherr()
+{
+       prs("Path too long: ");
+       panic(gpath);
+}
+
+match(s, p)
+char *s, *p;
+{
+       register c, sentp;
+
+       if (*s == '.' && *p != '.')
+               return(0);
+       sentp = entp;
+       entp = s;
+       c = amatch(s, p);
+       entp = sentp;
+       return(c);
+}
+
+amatch(as, ap)
+char *as, *ap;
+{
+       register char *s, *p;
+       register scc;
+       int c, cc, ok, lc;
+       char *sgpathp;
+
+       s = as;
+       p = ap;
+nextc:
+       if(scc = *s++ & 0177)
+               if ((scc =& 0177) == 0)
+                       scc = 0200;
+       switch (c = *p++) {
+               case '[':
+                       ok = 0;
+                       lc = 077777;
+                       while (cc = *p++) {
+                               if (cc == ']') {
+                                       if (ok)
+                                               goto nextc;
+                                       else
+                                               return(0);
+                               } else if (cc == '-') {
+                                       if (lc <= scc && scc <= *p++)
+                                               ok++;
+                               } else
+                                       if (scc == (lc = cc))
+                                               ok++;
+                       }
+                       panic("missing ]");
+               case '*':
+                       if(*p == '\0')
+                               return(1);
+                       else if (*p == '/') {
+                               p++;
+                               goto slash;
+                       }
+                       s--;
+                       while(*s)
+                               if (amatch(s, p))
+                                       return(1);
+                               else
+                                       s++;
+                       return(0);
+               case '\0':
+                       return(scc == '\0');
+               default:
+                       if (c == scc)
+                               goto nextc;
+                       else
+                               return(0);
+               case '?':
+                       if (scc != '\0')
+                               goto nextc;
+                       else
+                               return(0);
+               case '/':
+                       if (scc == '\0') {
+slash:
+                               s = entp;
+                               sgpathp = gpathp;
+                               while (*gpathp = *s++)
+                                       gpathp++;
+                               *gpathp++ = '/';
+                               *gpathp = 0;
+                               if (stat(gpath, &stbuff) == 0)
+                                       if ((stbuff[2] & 060000) == 040000)
+                                               if (*p == 0) {
+                                                       *av++ = cat(gpath, "");
+                                                       ncoll++;
+                                               } else
+                                                       expand(p);
+                               gpathp = sgpathp;
+                               *gpathp = 0;
+                       }
+                       return(0);
+       }
+}
+
+cat(as1, as2)
+char *as1, *as2;
+{
+       register char *s1, *s2;
+
+       s2 = string;
+       s1 = as1;
+       while (*s2++ = (*s1++ & 0177))
+               if (s2 >= &ab[STRSIZ])
+                       toolong();
+       s1 = as2;
+       s2--;
+       while (*s2++ = *s1++)
+               if (s2 > &ab[STRSIZ])
+                       toolong();
+       s1 = string;
+       string = s2;
+       return(s1);
+}
+
+panic(str)
+{
+       err(str);
+       reset();
+}
diff --git a/ashell/sh_lex.c b/ashell/sh_lex.c
new file mode 100644 (file)
index 0000000..e6735b9
--- /dev/null
@@ -0,0 +1,470 @@
+#include "sh.h"
+/*
+ * Shell:
+ *     Lexical and argument processing routines
+ *
+ *     lex   - driver for lexical analysis
+ *     word  - reads next word into line and pointer thereto via args
+ *     getc  - gets a character from the logical input stream
+ *     readc - reads a character from the 'input device'
+ *     setargs - sets up the parameter variables initially
+ *     rewind - backs up the shell arguments to their original values
+ *     setnargs - resets nargs variable after changes to arg list
+ *     shift - manipulates the shell parameters
+ */
+struct shvar2 *word();
+
+/*
+ * lex is the driver routine for the lexical input of the shell.
+ * Basic strategy is to read a logical line into linebuf
+ * with successive words pointed to by successive elements of args.
+ * Termination condition is a newline.
+ * Returns a pointer to a linked list of the words.
+ */
+lex(hp)
+       register struct shvar2 *hp;
+{
+       register struct shvar2 *wdp;
+
+       wdp = hp;
+       do {
+               wdp->next = calloc(1, sizeof *wdp);
+               wdp->next->prev = wdp;
+               wdp = wdp->next;
+               wdp->value = word();
+       } while (wdp->value[0] != '\n');
+       hp->prev = wdp;
+       wdp->next = hp;
+}
+
+freelex(vp)
+       register struct shvar *vp;
+{
+       register struct shvar *fp;
+
+       while (vp->next != vp) {
+               fp = vp->next;
+               vp->next = fp->next;
+               xfree(fp->value);
+               cfree(fp);
+       }
+       vp->prev = vp;
+}
+
+/* static */ char peekc, peekx;
+/*
+ * word breaks the input character stream into words.
+ * Blanks and tabs in the input are ignored, the characters
+ *     &;<>()|^ and \n
+ * are considered to be separators.
+ * Characters may be escaped here by surrounding them with
+ * 's or "s.  This causes the QUOTE (high order) bit of the
+ * corresponding character to be set so the character will
+ * fail subsequent comparisons.  The quoting is eventually
+ * stripped off.  More quoting by QUOTE is also done in readc.
+ * Note importantly that quoted character strings do not undergo
+ * parameter substitution!
+ * Return value is a pointer to a structure containing the word.
+ */
+struct shvar2 *
+word()
+{
+       register c;
+       char c1;
+       register char *lp;
+       char lbuf[514];
+       int i;
+
+       lp = lbuf;
+       i = 512;
+       /*
+        * Loop to get something solid
+        */
+       for (;;) {
+               c = getc();
+               switch (c) {
+                       case ' ':
+                       case '\t':
+                               continue;
+                       case '\'':
+                       case '"':
+                               c1 = c;
+                               while ((c = echo(readc())) != c1) {
+                                       if (c == '\n') {
+                                               seterr("Unmatched ' or \"");
+                                               *lp++ = 0;
+                                               peekc = c;
+                                               goto ret;
+                                       }
+                                       if (--i == 0)
+                                               goto toochars;
+                                       *lp++ = c | QUOTE;
+                               }
+                               break;
+                       case '&':
+                       case ';':
+                       case '<':
+                       case '>':
+                       case '(':
+                       case ')':
+                       case '|':
+                       case '^':
+                       case '\n':
+                               *lp++ = c;
+                               *lp++ = '\0';
+                               goto ret;
+                       default:
+                               peekc = c;
+                               break;
+               }
+               /*
+                * We have discovered something solid (not a separator).
+                * We want to gather in as many characters
+                * as possible but don't want to grab a separator.
+                * If we find another quotation in this word we go back to
+                * the top to take it.
+                */
+               for (;;) {
+                       c = getc();
+                       if (any(c, " '\"\t;&<>()|^\n")) {
+                               peekc = c;
+                               if (any(c, "\"'"))
+                                       break;
+                               *lp++ = '\0';
+                               goto ret;
+                       }
+                       if (--i == 0)
+                               goto toochars;
+                       *lp++ = c;
+               }
+       }
+toochars:
+       seterr("Too many characters");
+       lbuf[1] = 0;
+ret:
+       return (savestr(lbuf));
+}
+
+/* static */ char *dolp;
+/* static */ struct shvar2 paramhd, *paramp, *dolnxt;
+/* static */ int dolc;
+/*
+ * setargs sets up the initial argument linked list.
+ * paramp is a working pointer to the front of the list (actually
+ * one before the front), paramhd the actual origin which contains
+ * the true value of $0.
+ *
+ * dolnxt is used in expanding $*.
+ * dolc is maintained by setnargs who also maintains the nargs variable
+ * dolp is the pointer into the expanding string in getc
+ */
+setargs(c, v)
+       int c;
+       char *v[];
+{
+       register struct shvar2 *vp, *lvp;
+
+       vp = &paramhd;
+       for (;;) {
+               vp->value = *v++;
+               c--;
+               if (c == 0)
+                       break;
+               lvp = vp;
+               vp = calloc(1, sizeof *vp);
+               lvp->next = vp;
+               vp->prev = lvp;
+       }
+       rewind();
+}
+
+/*
+ * rewind the shell arguments
+ */
+rewind()
+{
+       paramp = &paramhd;
+       setnargs();
+}
+
+/*
+ * set up nargs variable after a parameter list change
+ */
+setnargs()
+{
+       register struct shvar2 *vp;
+
+       dolc = 0;
+       for (vp = paramp; vp != 0; vp = vp->next)
+               dolc++;
+       set(n_args, putn(dolc - 1));
+       if (dolc == 1)
+               unsetv(n_args);
+}
+
+/*
+ * shift the shell arguments
+ */
+shift(v)
+       register char *v[];
+{
+       register int n;
+       register struct shvar2 *vp;
+
+       v++;
+       n = *v == 0 ? 1 : getn(*v++);
+       for (vp = paramp; vp && n;)
+               if (n > 0) {
+                       n--;
+                       vp = vp->next;
+               } else {
+                       n++;
+                       vp = vp->prev;
+               }
+       if (n || vp == 0) {
+               bferr(": Count too large");
+               return;
+       }
+       paramp = vp;
+       setnargs();
+}
+
+/* static */   char dol2bra;
+/*
+ * getc gets a character from the logical input stream.
+ * It handles parameter expansion via $[0-9], all parameters
+ * via $*, shell variables via $[A-Za-z], and the process number via $$.
+ * Also handled is the trimming of the sufficies from expanded
+ * names via the . notation.  For example if $1 is "foo.p" then
+ * $.1 will be "foo".
+ *
+ * The variable dol2bra's value has the following meaning:
+ *
+ *     2       echo characters to : or }, if : discard chars to }
+ *     1       echo characters to :
+ *     -1      discard characters to }
+ *     -2      discard characters to : or }, if : echo to }
+ *
+ * This handles the constructs
+ *
+ *     ${name?str1:str2}       name set -> str1 ; t -> str2
+ *     ${name:default}         name set -> $name ; t -> default
+ *     ${name?string}          name set -> strings ; t -> ""
+ */
+getc()
+{
+       register c;
+       static char doldot;
+
+       if (peekc) {
+               c = peekc;
+               peekc = 0;
+               return (c);
+       }
+       for (;;) {
+               if (dolp) {
+                       c = *dolp++;
+                       if (c && (c != '.' || !doldot || any('.', dolp)))
+                               return (echo(c));
+                       if (dolnxt && (dolnxt = dolnxt->next)) {
+                               dolp = dolnxt->value;
+                               return (echo(' '));
+                       }
+                       dolp = 0;
+                       echo(']');
+                       continue;
+               }
+               if (peekx) {
+                       c = peekx;
+                       peekx = 0;
+               } else
+                       c = readc();
+               if (c == '\\') {
+                       echo(c);
+                       c = readc();
+                       if (c == '\n')
+                               c = ' ';
+                       else
+                               c =| QUOTE;
+               }
+               if (dol2bra) {
+                       switch (c) {
+                               case '}':
+                                       if (dol2bra > 0)
+                                               echo(']');
+                                       dol2bra = 0;
+                                       echo('}');
+                                       continue;
+                               case '\n':
+                                       dol2bra = 0;
+                                       seterr("Missing }");
+                                       return (echo('\n'));
+                               case ':':
+                                       switch (dol2bra) {
+                                               case 2:
+                                                       dol2bra = -1;
+                                                       echo(']');
+                                                       echo(':');
+                                                       continue;
+                                               case -2:
+                                                       dol2bra = 1;
+                                                       echo(':');
+                                                       echo('[');
+                                                       continue;
+                                       }
+                               default:
+                                       echo(c);
+                       }
+                       if (dol2bra < 0)
+                               continue;
+                       return (c);
+               }
+               if (c == '$') {
+                       echo(c);
+                       doldot = 0;
+                       c = readc();
+                       echo(c);
+                       if (c == '.') {
+                               doldot = 1;
+                               c = readc();
+                               echo(c);
+                       }
+                       switch (c) {
+                               default:
+                                       if (digit(c)) {
+                                               dolp = rgadrof(c);
+                                               if (dolp == 0)
+                                                       continue;
+                                               dolp = dolp->value;
+                                               break;
+                                       }
+                                       if (c == '{' || letter(c)) {
+                                               dolp = dolvbl(c);
+                                               if (dolp || dol2bra > 0)
+                                                       break;
+                                               continue;
+                                       }
+                                       return (c & 0177);
+                               case '$':
+                                       dolp = value(pid);
+                                       break;
+                               case '*':
+                                       if (dolc <= 1)
+                                               break;
+                                       dolnxt = paramp->next;
+                                       dolp = dolnxt->value;
+                                       break;
+                       }
+                       echo('[');
+                       continue;
+               }
+               echo(c);
+               return (c);
+       }
+}
+
+dolvbl(sc)
+       char sc;
+{
+       register char *np;
+       register struct shvar *vp;
+       char name[20], c;
+
+       np = name;
+       if (sc != '{')
+               *np++ = sc;
+       for (c = readc(); letter(c); c = readc())
+               if (np < &name[sizeof name - 1]) {
+                       echo(c);
+                       *np++ = c;
+               }
+       *np++ = 0;
+       vp = adrof(name);
+       if (sc != '{')
+               peekx = c;
+       else {
+               switch (c) {
+                       case ':':
+                               if (vp)
+                                       dol2bra = -1;
+                               else
+                                       dol2bra = -2;
+                               peekx = ':';
+                               break;
+                       case '}':
+                               peekx = c;
+                               dol2bra = -1;
+                               break;
+                       case '?':
+                               echo('?');
+                               if (vp)
+                                       dol2bra = 2;
+                               else
+                                       dol2bra = -2;
+                               return (0);
+                       default:
+                               echo(c);
+                               seterr("Variable syntax");
+                               return (0);
+               }
+       }
+       if (vp == 0) {
+               seterr("Undefined variable");
+               return (0);
+       }
+       return (vp->value);
+}
+
+/*
+ * read a character from the input device.
+ * this may be an argument e.g. for sh -c.
+ * also for sh -t stop after one line.
+ */
+readc()
+{
+       char cc;
+       register c;
+       register char *cp;
+
+again:
+       if (arginp) {
+               if (arginp == 1)
+                       exit(0);
+               else if ((c = *arginp++) == '\0') {
+                       arginp = 1;
+                       c = '\n';
+               }
+       } else if (onelflg == 1)
+               exit(0);
+       else if (read(0, &cc, 1) != 1) {
+               doneinp = 1;
+               reset();
+       } else if ((c = cc) == '\n' && onelflg)
+               onelflg--;
+       if (c == 0)
+               goto again;
+       return (c);
+}
+
+rgadrof(r)
+       register int r;
+{
+       register struct shvar *tp;
+
+       if (!digit(r) || (r =- '0') > dolc)
+               return (0);
+       if (r == 0)
+               return (&paramhd);
+       for (tp = paramp; r > 0; tp = tp->next)
+               r--;
+       return (tp);
+}
+
+rgvalue(r)
+       int r;
+{
+       register struct shvar *tp;
+
+       tp = rgadrof(r);
+       return (tp ? tp->value : "");
+}
diff --git a/ashell/sh_parse.c b/ashell/sh_parse.c
new file mode 100644 (file)
index 0000000..071c4f9
--- /dev/null
@@ -0,0 +1,303 @@
+#include "sh.h"
+
+/*
+ * syntax
+ *     empty
+ *     syn0
+ */
+syntax(p1, p2)
+       register struct shvar2 *p1, *p2;
+{
+
+       while (p1 != p2)
+               if (any(p1->value[0], ";&\n"))
+                       p1 = p1->next;
+               else
+                       return (syn0(p1, p2));
+       return (0);
+}
+
+/*
+ * syn0
+ *     syn1
+ *     syn1 & syntax
+ */
+syn0(p1, p2)
+       struct shvar2 *p1, *p2;
+{
+       register struct shvar2 *p;
+       register *t, *t1;
+       int l;
+
+       l = 0;
+       for (p = p1; p != p2; p = p->next)
+               switch (p->value[0]) {
+                       case '(':
+                               l++;
+                               continue;
+                       case ')':
+                               l--;
+                               if (l < 0)
+                                       seterr("Too many )'s");
+                               continue;
+                       case '&':
+                               if (l != 0)
+                                       break;
+                               t1 = syn1(p1, p);
+                               if (t1[DTYP] == TLST) {
+                                       t = calloc(2, 5);
+                                       t[DTYP] = TPAR;
+                                       t[DFLG] = FAND|FPRS|FINT;
+                                       t[DSPR] = t1;
+                                       t1 = t;
+                               } else
+                                       t1[DFLG] =| FAND|FPRS|FINT;
+                               t = calloc(2, 4);
+                               t[DTYP] = TLST;
+                               t[DFLG] = 0;
+                               t[DLEF] = t1;
+                               t[DRIT] = syntax(p, p2);
+                               return(t);
+               }
+       if (l == 0)
+               return (syn1(p1, p2));
+       seterr("Too many ('s");
+       return(0);
+}
+
+/*
+ * syn1
+ *     syn2
+ *     syn2 ; syn1
+ */
+syn1(p1, p2)
+       struct shvar2 *p1, *p2;
+{
+       register struct shvar *p;
+       register *t;
+       int l;
+
+       l = 0;
+       for (p = p1; p != p2; p = p->next)
+               switch (p->value[0]) {
+                       case '(':
+                               l++;
+                               continue;
+                       case ')':
+                               l--;
+                               continue;
+                       case ';':
+                       case '\n':
+                               if (l != 0)
+                                       break;
+                               t = calloc(2, 4);
+                               t[DTYP] = TLST;
+                               t[DLEF] = syn2(p1, p);
+                               t[DRIT] = syntax(p->next, p2);
+                               return (t);
+               }
+       return (syn2(p1, p2));
+}
+
+/*
+ * syn2
+ *     syn3
+ *     syn3 | syn2
+ *     syn3 |* syn2
+ */
+syn2(p1, p2)
+       struct shvar2 *p1, *p2;
+{
+       register struct shvar2 *p, *pn;
+       register int *t;
+       int l;
+
+       l = 0;
+       for (p = p1; p != p2; p = p->next)
+               switch (p->value[0]) {
+                       case '(':
+                               l++;
+                               continue;
+                       case ')':
+                               l--;
+                               continue;
+                       case '|':
+                       case '^':
+                               if (l != 0)
+                                       continue;
+                               t = calloc(2, 4);
+                               t[DTYP] = TFIL;
+                               t[DLEF] = syn3(p1, p);
+                               pn = p->next;
+                               if (pn != p2 && pn->value[0] == '*') {
+                                       t[DFLG] = FDIAG;
+                                       if (pn->value[1] != 0)
+                                               pn->value++;
+                                       else
+                                               p = pn;
+                               }
+                               t[DRIT] = syn2(p->next, p2);
+                               return (t);
+               }
+       return (syn3(p1, p2));
+}
+
+/*
+ * syn3
+ *     ( syn0 ) [ < in  ] [ > out ]
+ *     word word* [ < in ] [ > out ]
+ */
+syn3(p1, p2)
+       struct shvar2 *p1, *p2;
+{
+       register struct shvar2 *p;
+       struct shvar *lp, *rp;
+       register int *t;
+       int n, l, c;
+
+       n = 0;
+       l = 0;
+       for (p = p1; p != p2; p = p->next)
+               switch (p->value[0]) {
+                       case '(':
+                               l++;
+                               continue;
+                       case ')':
+                               l--;
+                               continue;
+                       case '>':
+                       case '<':
+                               if (l != 0)
+                                       continue;
+                               if (p->next == p2)
+                                       continue;
+                               if (any(p->next->value[0], "<>(*"))
+                                       continue;
+                               n--;
+                               continue;
+                       default:
+                               if (l != 0)
+                                       continue;
+                               n++;
+                               continue;
+               }
+       if (n < 0)
+               n = 0;
+       t = calloc(2, 5 + n + 1);
+       n = 0;
+       if (p2->value[0] == ')')
+               t[DFLG] = FPAR;
+       lp = 0;
+       rp = 0;
+       l = 0;
+       for (p = p1; p != p2; p = p->next) {
+               c = p->value[0];
+               switch (c) {
+                       case '(':
+                               if (l == 0) {
+                                       if (lp != 0)
+                                               seterr("Badly placed (");
+                                       lp = p->next;
+                               }
+                               l++;
+                               continue;
+                       case ')':
+                               l--;
+                               if (l == 0)
+                                       rp = p;
+                               continue;
+                       case '>':
+                               if (l != 0)
+                                       continue;
+                               if (p->next != p2) {
+                                       p = p->next;
+                                       if (p->value[0] == '>')
+                                               t[DFLG] =| FCAT;
+                                       else
+                                               p = p->prev;
+                               }
+                               if (p->next != p2) {
+                                       p = p->next;
+                                       if (p->value[0] == '*') {
+                                               t[DFLG] =| FDIAG;
+                                               if (p->value[1]) {
+                                                       p->value++;
+                                                       p = p->prev;
+                                               }
+                                       } else
+                                               p = p->prev;
+                               }
+                       case '<':
+                               if (l != 0)
+                                       continue;
+                               if (p->next == p2) {
+                                       seterr("Missing file for redirect");
+                                       continue;
+                               }
+                               p = p->next;
+                               if (any(p->value[0], "<>(")) {
+                                       seterr("Syntax error in redirection");
+                                       continue;
+                               }
+                               if (c == '<') {
+                                       if (t[DLEF] != 0) {
+                                               seterr("Multiple < redirect");
+                                               continue;
+                                       }
+                                       t[DLEF] = p->value;
+                                       continue;
+                               }
+                               if (t[DRIT] != 0) {
+                                       seterr("Multiple output redirect");
+                                       continue;
+                               }
+                               t[DRIT] = p->value;
+                               continue;
+                       default:
+                               if (l != 0)
+                                       continue;
+                               t[DCOM + n] = p->value;
+                               n++;
+                               continue;
+               }
+       }
+       if (lp != 0) {
+               if (n != 0)
+                       seterr("Only redirect allowed outside ()'s");
+               t[DTYP] = TPAR;
+               t[DSPR] = syn0(lp, rp);
+       } else {
+               if (n == 0)
+                       seterr("Inappropriate empty command");
+               t[DCOM + n] = 0;
+               t[DTYP] = TCOM;
+       }
+       return (t);
+}
+
+freesyn(t)
+       register int *t;
+{
+       register char **v;
+
+       if (t == 0)
+               return;
+       switch (t[DTYP]) {
+               case TCOM:
+                       for (v = &t[DCOM]; *v; v++)
+                               xfree(*v);
+                       goto lr;
+               case TPAR:
+                       freesyn(t[DSPR]);
+lr:
+                       xfree(t[DLEF]);
+                       xfree(t[DRIT]);
+                       break;
+               case TFIL:
+               case TLST:
+                       freesyn(t[DLEF]);
+                       freesyn(t[DRIT]);
+                       break;
+       }
+       cfree(t);
+}
diff --git a/ashell/sh_sem.c b/ashell/sh_sem.c
new file mode 100644 (file)
index 0000000..0091abd
--- /dev/null
@@ -0,0 +1,138 @@
+#include "sh.h"
+
+execute(t, pf1, pf2)
+       int *t, *pf1, *pf2;
+{
+       int i, f, pv[2];
+       register *t1;
+       char *cp1, *cp2;
+       char *scp;
+
+       if (t == 0)
+               return;
+       switch(t[DTYP]) {
+               case TCOM:
+                       i = 100;
+                       do
+                               if (func(t))
+                                       return;
+                       while (--i && alias(&t[DCOM]));
+                       if (i == 0) {
+                               bferr2(t[DCOM], ": Alias loop detected");
+                               return;
+                       }
+               case TPAR:
+                       t1 = t;
+                       f = t1[DFLG];
+                       i = 0;
+                       if ((f&FPAR) == 0)
+                               i = fork();
+                       if (i == -1) {
+                               err("No more processes");
+                               return;
+                       }
+                       if (i != 0) {
+                               if ((f&FPIN) != 0) {
+                                       close(pf1[0]);
+                                       close(pf1[1]);
+                               }
+                               if ((f&FPRS) != 0) {
+                                       prn(i);
+                                       prs("\n");
+                                       set(pcs, putn(i));
+                               }
+                               if ((f&FAND) != 0)
+                                       return;
+                               if ((f&FPOU) == 0)
+                                       pwait(i);
+                               return;
+                       }
+                       set(prompt,"");
+                       unsetv(prompt);
+                       if ((cp1 = t1[DLEF]) != 0) {
+                               close(0);
+                               cp1 = globone(cp1);
+                               if (cp1 == 0)
+                                       exit(1);
+                               strip(cp1);
+                               if (open(cp1, 0) < 0) {
+                                       prs(cp1);
+                                       err(": Cannot open");
+                                       exit(1);
+                               }
+                       }
+                       if ((cp2 = t1[DRIT]) != 0) {
+                               while (*cp2)
+                                       *cp2++ =& 0177;
+                               strip(cp2);
+                               cp2 = t1[DRIT];
+                               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);
+                       }
+                       doexec(t1);
+                       /* no return */
+               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;
+       }
+}
+
+strip(cp)
+       register char *cp;
+{
+
+       while (*cp++ =& 0177)
+               continue;
+}
diff --git a/ashell/sh_set.c b/ashell/sh_set.c
new file mode 100644 (file)
index 0000000..38c201a
--- /dev/null
@@ -0,0 +1,346 @@
+#include "sh.h"
+
+/*
+ * Shell:
+ *     "set" and associated routines
+ *
+ *----------
+ *     doset - performs the logic of the set command
+ *     operate - performs arithmetic function of the set command
+ *     unset - undefines variables
+ *     xfree - frees argument storage if it is dynamic
+ *     savestr - salts away a copy of the argument string in a safe place
+ *     putn  - returns a pointer to a string representing the argument integer
+ *     getn  - returns the number which the string argument represents or 0
+ *----------
+ *     value - returns the value of a variable which is argument
+ *     adrof - returns a pointer to the structure for the variable argument
+ *     set   - sets the argument variable to the second argument value
+ */
+
+/*
+ * doset performs the logic of the set command.
+ * command syntax is
+ *     set [ rOPvalue ] *
+ * current ops are += -= ++ -- &= |= *= /= %= and =
+ *
+ * Magic operator @ actually does an unset.  Good for use conditionally!
+ */
+doset(v)
+       register char *v[];
+{
+       register int *rp;
+       register char *p;
+       char *vp, c, op;
+
+       v++;
+       p = *v++;
+       if (p == 0) {
+               prvars();
+               return;
+       }
+       do {
+               if (digit(*p)) {
+                       vp = p;
+                       p++;
+               } else
+                       for (vp = p; letter(*p); p++)
+                               continue;
+               if (vp == p) {
+                       bferr(": No variable");
+                       return;
+               }
+               op = *p;
+               *p++ = 0;
+               switch (op) {
+                       case 0:
+                               p--;
+                       case '=':
+                               set(vp, savestr(p));
+                               continue;
+                       case '@':
+                               if (*p != 0) {
+                                       bferr(": Garbage after @");
+                                       return;
+                               }
+                               unsetv(vp);
+                               continue;
+               }
+               c = *p++;
+               if (c != '=') {
+                       if (!any(op, "+-") || c != op || *p) {
+                               bferr(": Missing =");
+                               return;
+                       }
+                       *--p = '1';
+               }
+               set(vp, operate(op, vp, p));
+       } while (p = *v++);
+}
+
+operate(op, vp, p)
+       char op, *vp, *p;
+{
+       register int vl, vr;
+
+       vl = getn(value(vp));
+       vr = getn(p);
+       switch (op & 0177) {
+               case '+':
+                       vl =+ vr;
+                       break;
+               case '-':
+                       vl =- vr;
+                       break;
+               case '&':
+                       vl =& vr;
+                       break;
+               case '|':
+                       vl =| vr;
+                       break;
+               case '/':
+                       if (vr == 0)
+                               bferr(": Divide check");
+                       else
+                               vl =/ vr;
+                       break;
+               case '%':
+                       if (vr == 0)
+                               bferr(": Mod check");
+                       else
+                               vl =% vr;
+                       break;
+               case '*':
+                       vl =* vr;
+                       break;
+               default:
+                       bferr(": Bad operator");
+       }
+       return (putn(vl));
+}
+
+/*
+ * xfree frees possibly non-dynamic storage.
+ * It insures thats its argument is in the heap
+ * before freeing it.
+ */
+xfree(cp)
+       char *cp;
+{
+       extern char end[];
+
+       cp = (cp + 1) &~ 1;
+       if (cp >= end && cp < &cp)
+               cfree(cp);
+}
+
+savestr(s)
+       char *s;
+{
+
+       if (s == 0)
+               s = "";
+       return (strcpy(calloc(1, strlen(s) + 1), s));
+}
+
+/* static */char *putp;
+/*
+ * putn takes an integer and returns a pointer to
+ * its string representation.
+ * putp indexes the number as it is formed.
+ */
+putn(n)
+{
+       static char number[7];
+
+/*
+       sprintf(number, "%d", n);
+*/
+       putp = number;
+       if (n < 0) {
+               n = -n;
+               *putp++ = '-';
+       }
+       putn1(n);
+       *putp = 0;
+       return (savestr(number));
+}
+
+putn1(n)
+       int n;
+{
+       if (n > 9)
+               putn1(n / 10);
+       *putp++ = n % 10 + '0';
+}
+
+/*
+ * getn is used by doset to get numbers from strings.
+ * null string is considered to be 0 (ala SNOBOL).
+ */
+getn(cp)
+       register char *cp;
+{
+       register n;
+       int sign, base;
+
+       sign = 0;
+       base = 10;
+       if (*cp == '-') {
+               sign++;
+               cp++;
+               if (!digit(*cp))
+                       goto badnum;
+       } else if (*cp == '0')
+               base = 8;
+       n = 0;
+       while (digit(*cp))
+               n = n * base + *cp++ - '0';
+       if (*cp)
+               goto badnum;
+       return (sign ? -n : n);
+badnum:
+       bferr(": Badly formed number");
+       return (0);
+}
+
+/*
+ * value takes a string name of shell
+ * variable and returns a pointer to its value
+ */
+char *
+value(var)
+       char *var;
+{
+       return (value1(var, &shvhed));
+}
+
+char *
+value1(var, head)
+       char *var;
+       struct shvar *head;
+{
+       register struct shvar *vp;
+
+       vp = adrof1(var, head);
+       return (vp == 0 ? "" : vp->value);
+}
+
+/* static */ struct shvar *shprev;
+/*
+ * adrof takes a variable name and returns
+ * a pointer to its structure or 0.
+ * A side effect is to make shprev point to the
+ * structure before this one for obvious reasons.
+ */
+struct shvar *
+adrof(var)
+       char *var;
+{
+       register struct shvar *vp;
+
+       if (digit(var[0])) {
+               vp = rgadrof(var[0]);
+               if (vp == 0) {
+                       bferr(": Bad parameter");
+                       vp = &shvhed;
+               }
+               return (vp);
+       }
+       return (adrof1(var, &shvhed));
+}
+
+struct shvar *
+adrof1(var, head)
+       char *var;
+       struct shvar *head;
+{
+       register struct shvar *vp;
+       int cmp;
+
+       shprev = head;
+       for (vp = shprev->next; vp != 0; vp = vp->next) {
+               cmp = strcmp(vp->name, var);
+               if (cmp == 0)
+                       return (vp);
+               else if (cmp > 0)
+                       return (0);
+               shprev = vp;
+       }
+       return (0);
+}
+
+/*
+ * set sets the variable argument to
+ * the given value.
+ * The caller is responsible for putting value
+ * in a safe place!
+ */
+set(var, value)
+       char *var, *value;
+{
+
+       set1(var, value, &shvhed);
+}
+
+set1(var, value, head)
+       char *var, *value;
+       struct shvar *head;
+{
+       register struct shvar *vp;
+
+       vp = adrof1(var, head);
+       if (vp == 0) {
+               vp = calloc(1, sizeof *vp);
+               vp->name = savestr(var);
+               vp->next = shprev->next;
+               shprev->next = vp;
+       }
+       if (value == 0)
+               value = "";
+       else
+               xfree(vp->value);
+       vp->value = value;
+}
+
+unset(v)
+       register char *v[];
+{
+
+       unset1(v, &shvhed);
+}
+
+unset1(v, head)
+       register char *v[];
+       struct shvar *head;
+{
+       register char *var;
+
+       v++;
+       while (var = *v++)
+               unsetv1(var, head);
+}
+
+unsetv(var)
+       char *var;
+{
+
+       unsetv1(var, &shvhed);
+}
+
+unsetv1(var, head)
+       char *var;
+       struct shvar *head;
+{
+       register struct shvar *vp;
+
+       vp = adrof1(var, head);
+       if (vp == 0) {
+               bferr2(var, ": Undefined");
+               return;
+       }
+       vp = shprev->next;
+       shprev->next = vp->next;
+       xfree(vp->value);
+       xfree(vp);
+}
diff --git a/ashell/sh_wait.c b/ashell/sh_wait.c
new file mode 100644 (file)
index 0000000..067f18e
--- /dev/null
@@ -0,0 +1,189 @@
+#include "sh.h"
+
+static char *mesg[] {
+       0,
+       "Hangup",
+       0,
+       "Quit",
+       "Illegal instruction",
+       "Trace/BPT trap",
+       "IOT trap",
+       "EMT trap",
+       "Floating exception",
+       "Killed",
+       "Bus error",
+       "Segmentation violation",
+       "Bad system call",
+       0,
+       "Alarm clock",
+       "Terminate",
+       "Sig 16",
+};
+
+static struct tbuffer {
+       int     put, pst;
+       long    cut, cst;
+} times0;
+static long time0;
+static char timflg;
+
+double
+secs(bef, aft)
+       struct tbuffer *bef, *aft;
+{
+
+       return ((aft->cut - bef->cut + aft->cst - bef->cst) / 60.0);
+}
+
+settimes()
+{
+
+       time(&time0);
+       times(&times0);
+}
+
+dotimes(v0)
+       char *v0[];
+{
+       register char **v;
+       struct tbuffer timesdol;
+       long timedol;
+
+       v = v0;
+       if (v[1] != 0) {
+               timflg = 1;
+               do {
+                       v[0] = v[1];
+                       v++;
+               } while (*v != 0);
+               return (func(v0 - DCOM));
+       }
+       time(&timedol);
+       times(&timesdol);
+       ptimes(timedol - time0, &times0, &timesdol);
+       return (1);
+}
+
+pwait(i)
+       register int i;
+{
+       register p, e;
+       int s;
+       struct tbuffer bef, aft;
+       long btim, atim;
+
+       if (i == 0)
+               return;
+       time(&btim);
+       do {
+               times(&bef);
+               p = wait(&s);
+               if (p == getn(value(pcs)))
+                       unsetv(pcs);
+               time(&atim);
+               if (p == -1)
+                       return;
+               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);
+       times(&aft);
+       if (timflg || (adrof(tim) && secs(&bef, &aft) >= getn(value(tim)))) {
+               timflg = 0;
+               ptimes(atim - btim, &bef, &aft);
+       }
+}
+
+ptimes(sec, bef, aft)
+       long sec;
+       struct tbuffer *bef, *aft;
+{
+       register int i;
+
+       p60ths(aft->cut - bef->cut);
+       prs("u ");
+       p60ths(aft->cst - bef->cst);
+       prs("s ");
+       psecs(sec);
+       prs(" ");
+       i = 100.0 * secs(bef, aft) / (sec ? sec : 1);
+       prn(i);
+       prs("%\n");
+}
+
+p60ths(l)
+       long l;
+{
+       register int i;
+
+       l =+ 3;
+       i = l / 60;
+       prn(i);
+       prs(".");
+       i = l % 60;
+       prn(i / 6);
+}
+
+psecs(l)
+       long l;
+{
+       register int i;
+
+       i = l / 3600.0;
+       if (i) {
+               prn(i);
+               prs(":");
+               i = l % 3600;
+               p2dig(i / 60);
+               goto minsec;
+       }
+       i = l;
+       prn(i / 60);
+minsec:
+       i =% 60;
+       prs(":");
+       p2dig(i);
+}
+
+p2dig(i)
+       int i;
+{
+       prn(i / 10);
+       prn(i % 10);
+}
+
+endwait()
+{
+       signal(INTR, 1);
+       prs("wait: Interrupted\n");
+       reset();                /* URK! */
+}
+
+await(v)
+       register char *v[];
+{
+       register int i;
+
+       v++;
+       if (*v) {
+               i = getn(*v);
+               if (i == 0)
+                       return;
+       } else
+               i = -1;
+       if (setintr && *value(prompt)) {
+               signal(INTR, endwait);
+               pwait(i);
+               signal(INTR, 1);
+       } else
+               pwait(i);
+}
diff --git a/pxref/READ_ME b/pxref/READ_ME
new file mode 100644 (file)
index 0000000..6951881
--- /dev/null
@@ -0,0 +1,10 @@
+To make a pxref do
+       makepxref
+It can be installed using
+       install
+If you have a shell which recognizes Pascal objects it is not necessary
+to use both the Pascal and C programs... the Pascal object can be put in
+/usr/bin... but it will have the same status as shell scripts (essentially)
+thus
+       time pxref pxref.p
+would not work then.
diff --git a/pxref/install b/pxref/install
new file mode 100755 (executable)
index 0000000..18ce0ad
--- /dev/null
@@ -0,0 +1,6 @@
+cp a.out /usr/bin/pxref
+cp obj /usr/lib/pxref
+chown bin /usr/bin/pxref /usr/lib/pxref
+chmod 711 /usr/bin/pxref
+chmod 644 /usr/lib/pxref
+rm -f a.out obj
diff --git a/pxref/makepxref b/pxref/makepxref
new file mode 100755 (executable)
index 0000000..e6efd9b
--- /dev/null
@@ -0,0 +1,2 @@
+cc -O pxref.c
+pi -pbt pxref.p
diff --git a/pxref/printpxref b/pxref/printpxref
new file mode 100755 (executable)
index 0000000..4a4b133
--- /dev/null
@@ -0,0 +1,3 @@
+pr pxref.c
+pi -l pxref.p
+rm obj
diff --git a/pxref/pxref.c b/pxref/pxref.c
new file mode 100644 (file)
index 0000000..1f2cf9d
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * pxref - driver to put in /usr/bin/ to call pxref as a Pascal program
+ *
+ * Bill Joy UCB July 29, 1977
+ *
+ * This program is not needed if the shell in use understands Pascal
+ * objects.
+ */
+
+char *progname;
+
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+
+       argc--, progname = *argv++;
+       if (argc == 0 || argc > 2) {
+               printf("usage: %s [ - ] file\n", progname);
+               exit(1);
+       }
+       if (argc == 1) {
+               argv--;
+               argv[0] = argv[1];
+               argv[1] = 0;
+       }
+       execl("/bin/px", "px", "/usr/lib/pxref", argv[0], argv[1], 0);
+       execl("/usr/bin/px", "pcx", "/usr/lib/pxref", argv[0], argv[1], 0);
+       write(2, "Can't find px\n", 14);
+       exit(1);
+}
diff --git a/pxref/pxref.p b/pxref/pxref.p
new file mode 100644 (file)
index 0000000..08c1696
--- /dev/null
@@ -0,0 +1,344 @@
+program xref(input, output);
+label
+    99, 100;
+const
+    p = 797;
+    nk = 36;
+    empty = '          ';
+type
+    index = 0..p;
+    ref = ^item;
+    word = 
+      record
+       key: alfa;
+       first, last: ref;
+       fol: index
+      end;
+    item = packed 
+      record
+       lno: 0..9999;
+       next: ref
+      end;
+var
+    i, top: index;
+    scr: alfa;
+    list: boolean;
+    k, k1: integer;
+    n: integer;
+    c1, c2: integer;
+    id: 
+      record
+       case boolean of
+         false:(
+           a: alfa
+         );
+         true:(
+           ord: integer
+         )
+      end;
+    a: array [1..10] of char;
+    t: array [index] of word;
+    key: array [1..nk] of alfa;
+
+    function letter(ch: char): Boolean;
+    begin
+       letter := (ch >= 'a') and (ch <= 'z') or (ch >= 'A') and (ch <= 'Z')
+    end { letter };
+
+    function digit(ch: char): Boolean;
+    begin
+       digit := (ch >= '0') and (ch <= '9')
+    end { digit };
+
+    function nokey(x: alfa): Boolean;
+    var
+       i, j, k: integer;
+    begin
+       i := 1;
+       j := nk;
+       repeat
+           k := (i + j) div 2;
+           if key[k] <= x then 
+               i := k + 1;
+           if key[k] >= x then 
+               j := k - 1
+       until i > j;
+       nokey := key[k] <> x
+    end { nokey };
+
+    procedure newline;
+    begin
+       if n < 9999 then begin
+           n := n + 1;
+           if list then 
+               write(n: 6, '  ')
+       end else begin
+           writeln(' text too long');
+           goto 99
+       end
+    end { newline };
+
+    procedure search;
+    var
+       h, d: index;
+       x: ref;
+       f: Boolean;
+    begin
+       h := id.ord div 4096 mod p;
+       f := false;
+       d := 1;
+       c2 := c2 + 1;
+       new(x);
+       x^.lno := n;
+       x^.next := nil;
+       repeat
+           if t[h].key = id.a then begin
+               f := true;
+               t[h].last^.next := x;
+               t[h].last := x
+           end else if t[h].key = empty then begin
+               f := true;
+               c1 := c1 + 1;
+               t[h].key := id.a;
+               t[h].first := x;
+               t[h].last := x;
+               t[h].fol := top;
+               top := h
+           end else begin
+               h := h + d;
+               d := d + 2;
+               if h >= p then 
+                   h := h - p;
+               if d = p then begin
+                   writeln;
+                   writeln(' **** table full');
+                   goto 99
+               end
+           end
+       until f
+    end { search };
+
+    procedure printword(w: word);
+    var
+       l: integer;
+       x: ref;
+    begin
+       write(' ', w.key);
+       x := w.first;
+       l := 0;
+       repeat
+           if l = 20 then begin
+               l := 0;
+               writeln;
+               write(' ', empty)
+           end;
+           l := l + 1;
+           write(x^.lno: 6);
+           x := x^.next
+       until x = nil;
+       writeln
+    end { printword };
+
+    procedure printtable;
+    var
+       i, j, m: index;
+    begin
+       i := top;
+       while i <> p do begin
+           m := i;
+           j := t[i].fol;
+           while j <> p do begin
+               if t[j].key < t[m].key then 
+                   m := j;
+               j := t[j].fol
+           end;
+           printword(t[m]);
+           if m <> i then begin
+               t[m].key := t[i].key;
+               t[m].first := t[i].first;
+               t[m].last := t[i].last
+           end;
+           i := t[i].fol
+       end
+    end { printtable };
+
+    procedure openinput(i: integer);
+    var
+       filename: array [1..64] of char;
+    begin
+       argv(i, filename);
+       reset(input, filename)
+    end { openinput };
+
+    procedure lwriteln;
+    begin
+       if list then 
+           writeln
+    end { lwriteln };
+
+    procedure lwrite(c: char);
+    begin
+       if list then 
+           write(c)
+    end { lwrite };
+
+begin { xref }
+    list := true;
+    if argc = 3 then begin
+       argv(1, scr);
+       if (scr[1] <> '-') or (scr[2] <> ' ') then begin
+           writeln('usage: pxref [ - ] file');
+           goto 100
+       end;
+       list := false
+    end;
+    if (argc < 2) or (argc > 3) then begin
+       writeln('usage: pxref [ - ] file');
+       goto 100
+    end;
+    if list then 
+       openinput(1)
+    else 
+       openinput(2);
+    for i := 0 to p - 1 do 
+       t[i].key := empty;
+    c1 := 0;
+    c2 := 0;
+    key[1] := 'and';
+    key[2] := 'array';
+    key[3] := 'assert';
+    key[4] := 'begin';
+    key[5] := 'case';
+    key[6] := 'const';
+    key[7] := 'div';
+    key[8] := 'do';
+    key[9] := 'downto';
+    key[10] := 'else';
+    key[11] := 'end';
+    key[12] := 'file';
+    key[13] := 'for';
+    key[14] := 'function';
+    key[15] := 'hex';
+    key[16] := 'if';
+    key[17] := 'in';
+    key[18] := 'mod';
+    key[19] := 'nil';
+    key[20] := 'not';
+    key[21] := 'oct';
+    key[22] := 'of';
+    key[23] := 'or';
+    key[24] := 'packed';
+    key[25] := 'procedure';
+    key[26] := 'program';
+    key[27] := 'record';
+    key[28] := 'repeat';
+    key[29] := 'set';
+    key[30] := 'then';
+    key[31] := 'to';
+    key[32] := 'type';
+    key[33] := 'until';
+    key[34] := 'var';
+    key[35] := 'while';
+    key[36] := 'with';
+    n := 0;
+    top := p;
+    k1 := 10;
+    while not eof(input) do begin
+       if not eoln(input) then 
+           newline
+       else 
+           n := n + 1;
+       if input^ = '#' then begin
+           while not eoln(input) do begin
+               lwrite(input^);
+               get(input)
+           end;
+           id.a := '#include';
+           search
+       end else 
+           while not eoln(input) do begin
+               if (input^ = ' ') or (input^ = tab) then begin
+                   lwrite(input^);
+                   get(input)
+               end else if letter(input^) then begin
+                   k := 0;
+                   repeat
+                       lwrite(input^);
+                       if k < 10 then begin
+                           k := k + 1;
+                           a[k] := input^
+                       end;
+                       get(input)
+                   until not (letter(input^) or digit(input^));
+                   if k >= k1 then 
+                       k1 := k
+                   else 
+                       repeat
+                           a[k1] := ' ';
+                           k1 := k1 - 1
+                       until k1 = k;
+                   pack(a, 1, id.a);
+                   if nokey(id.a) then 
+                       search
+               end else if digit(input^) then 
+                   repeat
+                       lwrite(input^);
+                       get(input)
+                   until not digit(input^)
+               else if input^ = '''' then begin
+                   repeat
+                       lwrite(input^);
+                       get(input)
+                   until input^ = '''';
+                   lwrite('''');
+                   get(input)
+               end else if input^ = '{' then begin
+                   repeat
+                       lwrite(input^);
+                       get(input);
+                       while eoln(input) do begin
+                           lwriteln;
+                           get(input);
+                           newline
+                       end
+                   until input^ = '}';
+                   lwrite('}');
+                   get(input)
+               end else if input^ = '(' then begin
+                   lwrite('(');
+                   get(input);
+                   if input^ = '*' then begin
+                       lwrite('*');
+                       get(input);
+                       repeat
+                           while input^ <> '*' do begin
+                               if eoln(input) then begin
+                                   lwriteln;
+                                   newline
+                               end else 
+                                   lwrite(input^);
+                               get(input)
+                           end;
+                           lwrite('*');
+                           get(input)
+                       until input^ = ')';
+                       lwrite(')');
+                       get(input)
+                   end
+               end else begin
+                   lwrite(input^);
+                   get(input)
+               end
+           end;
+       lwriteln;
+       get(input)
+    end;
+99:
+    if list then 
+       page(output);
+    printtable;
+    lwriteln;
+    writeln(c1, ' identifiers', c2, ' occurrences');
+100:
+    {nil}
+end { xref }.