tnew utilities. the .C files compile but are renamed to avoid building automatically. - plan9port - [fork] Plan 9 from user space
git clone git://src.adamsgaard.dk/plan9port
Log
Files
Refs
README
LICENSE
---
commit bc7cb1a15a67c859c8c71c4b52bb35fe9425a63d
parent f08fdedcee12c06e3ce9ac9bec363915978e8289
Author: rsc 
Date:   Sun, 23 Nov 2003 18:04:47 +0000

new utilities.
tthe .C files compile but are renamed to avoid building automatically.

Diffstat:
  A src/cmd/ascii.c                     |     181 +++++++++++++++++++++++++++++++
  A src/cmd/basename.c                  |      41 +++++++++++++++++++++++++++++++
  A src/cmd/cal.c                       |     313 +++++++++++++++++++++++++++++++
  A src/cmd/calendar.c                  |     195 +++++++++++++++++++++++++++++++
  A src/cmd/cat.c                       |      36 +++++++++++++++++++++++++++++++
  A src/cmd/cleanname.c                 |      44 +++++++++++++++++++++++++++++++
  A src/cmd/cmp.c                       |     112 +++++++++++++++++++++++++++++++
  A src/cmd/comm.c                      |     178 +++++++++++++++++++++++++++++++
  A src/cmd/date.c                      |      30 ++++++++++++++++++++++++++++++
  A src/cmd/dc.c                        |    2300 +++++++++++++++++++++++++++++++
  A src/cmd/dd.c                        |     660 +++++++++++++++++++++++++++++++
  A src/cmd/deroff.c                    |     969 +++++++++++++++++++++++++++++++
  A src/cmd/du.C                        |     194 ++++++++++++++++++++++++++++++
  A src/cmd/echo.c                      |      38 +++++++++++++++++++++++++++++++
  A src/cmd/ed.c                        |    1608 ++++++++++++++++++++++++++++++
  A src/cmd/factor.c                    |      96 +++++++++++++++++++++++++++++++
  A src/cmd/freq.c                      |     111 ++++++++++++++++++++++++++++++
  A src/cmd/fsize.c                     |      32 +++++++++++++++++++++++++++++++
  A src/cmd/idiff.c                     |     335 +++++++++++++++++++++++++++++++
  A src/cmd/join.c                      |     369 ++++++++++++++++++++++++++++++
  A src/cmd/ls.C                        |     305 +++++++++++++++++++++++++++++++
  A src/cmd/md5sum.C                    |      61 +++++++++++++++++++++++++++++++
  A src/cmd/mkdir.C                     |      26 ++++++++++++++++++++++++++
  A src/cmd/mkfile                      |      13 +++++++++++++
  A src/cmd/rm.c                        |     104 +++++++++++++++++++++++++++++++
  A src/cmd/seq.c                       |      92 +++++++++++++++++++++++++++++++
  A src/cmd/sha1sum.c                   |      61 +++++++++++++++++++++++++++++++
  A src/cmd/sleep.c                     |      13 +++++++++++++
  A src/cmd/sort.c                      |    1767 +++++++++++++++++++++++++++++++
  A src/cmd/split.c                     |     189 +++++++++++++++++++++++++++++++
  A src/cmd/strings.c                   |      88 +++++++++++++++++++++++++++++++
  A src/cmd/sum.c                       |     215 +++++++++++++++++++++++++++++++
  A src/cmd/tail.c                      |     362 +++++++++++++++++++++++++++++++
  A src/cmd/tar.C                       |     640 +++++++++++++++++++++++++++++++
  A src/cmd/tee.c                       |      75 +++++++++++++++++++++++++++++++
  A src/cmd/test.c                      |     303 +++++++++++++++++++++++++++++++
  A src/cmd/time.c                      |     101 +++++++++++++++++++++++++++++++
  A src/cmd/touch.c                     |      62 +++++++++++++++++++++++++++++++
  A src/cmd/tr.c                        |     356 +++++++++++++++++++++++++++++++
  A src/cmd/unicode.c                   |     122 +++++++++++++++++++++++++++++++
  A src/cmd/uniq.c                      |     169 +++++++++++++++++++++++++++++++
  A src/cmd/unutf.c                     |      16 ++++++++++++++++
  A src/cmd/wc.c                        |     309 +++++++++++++++++++++++++++++++
  A src/cmd/xd.c                        |     355 +++++++++++++++++++++++++++++++
  A src/cmd/yacc.c                      |    2939 +++++++++++++++++++++++++++++++

45 files changed, 16585 insertions(+), 0 deletions(-)
---
diff --git a/src/cmd/ascii.c b/src/cmd/ascii.c
t@@ -0,0 +1,181 @@
+#include 
+#include 
+#include 
+
+#define        MAXBASE        36
+
+void        usage(void);
+void        put(int);
+void        putn(int, int);
+void        puttext(char *);
+void        putnum(char *);
+int        btoi(char *);
+int        value(int, int);
+int        isnum(char *);
+
+char *str[256]={
+        "nul",        "soh",        "stx",        "etx",        "eot",        "enq",        "ack",        "bel",
+        "bs ",        "ht ",        "nl ",        "vt ",        "np ",        "cr ",        "so ",        "si ",
+        "dle",        "dc1",        "dc2",        "dc3",        "dc4",        "nak",        "syn",        "etb",
+        "can",        "em ",        "sub",        "esc",        "fs ",        "gs ",        "rs ",        "us ",
+        "sp ",        " ! ",        " \" ",        " # ",        " $ ",        " % ",        " & ",        " ' ",
+        " ( ",        " ) ",        " * ",        " + ",        " , ",        " - ",        " . ",        " / ",
+        " 0 ",        " 1 ",        " 2 ",        " 3 ",        " 4 ",        " 5 ",        " 6 ",        " 7 ",
+        " 8 ",        " 9 ",        " : ",        " ; ",        " < ",        " = ",        " > ",        " ? ",
+        " @ ",        " A ",        " B ",        " C ",        " D ",        " E ",        " F ",        " G ",
+        " H ",        " I ",        " J ",        " K ",        " L ",        " M ",        " N ",        " O ",
+        " P ",        " Q ",        " R ",        " S ",        " T ",        " U ",        " V ",        " W ",
+        " X ",        " Y ",        " Z ",        " [ ",        " \\ ",        " ] ",        " ^ ",        " _ ",
+        " ` ",        " a ",        " b ",        " c ",        " d ",        " e ",        " f ",        " g ",
+        " h ",        " i ",        " j ",        " k ",        " l ",        " m ",        " n ",        " o ",
+        " p ",        " q ",        " r ",        " s ",        " t ",        " u ",        " v ",        " w ",
+        " x ",        " y ",        " z ",        " { ",        " | ",        " } ",        " ~ ",        "del",
+        "x80",        "x81",        "x82",        "x83",        "x84",        "x85",        "x86",        "x87",
+        "x88",        "x89",        "x8a",        "x8b",        "x8c",        "x8d",        "x8e",        "x8f",
+        "x90",        "x91",        "x92",        "x93",        "x94",        "x95",        "x96",        "x97",
+        "x98",        "x99",        "x9a",        "x9b",        "x9c",        "x9d",        "x9e",        "x9f",
+        "xa0",        " ¡ ",        " ¢ ",        " £ ",        " ¤ ",        " ¥ ",        " ¦ ",        " § ",
+        " ¨ ",        " © ",        " ª ",        " « ",        " ¬ ",        " ­ ",        " ® ",        " ¯ ",
+        " ° ",        " ± ",        " ² ",        " ³ ",        " ´ ",        " µ ",        " ¶ ",        " · ",
+        " ¸ ",        " ¹ ",        " º ",        " » ",        " ¼ ",        " ½ ",        " ¾ ",        " ¿ ",
+        " À ",        " Á ",        " Â ",        " Ã ",        " Ä ",        " Å ",        " Æ ",        " Ç ",
+        " È ",        " É ",        " Ê ",        " Ë ",        " Ì ",        " Í ",        " Î ",        " Ï ",
+        " Ð ",        " Ñ ",        " Ò ",        " Ó ",        " Ô ",        " Õ ",        " Ö ",        " × ",
+        " Ø ",        " Ù ",        " Ú ",        " Û ",        " Ü ",        " Ý ",        " Þ ",        " ß ",
+        " à ",        " á ",        " â ",        " ã ",        " ä ",        " å ",        " æ ",        " ç ",
+        " è ",        " é ",        " ê ",        " ë ",        " ì ",        " í ",        " î ",        " ï ",
+        " ð ",        " ñ ",        " ò ",        " ó ",        " ô ",        " õ ",        " ö ",        " ÷ ",
+        " ø ",        " ù ",        " ú ",        " û ",        " ü ",        " ý ",        " þ ",        " ÿ "
+};
+
+char Ncol[]={
+    0,0,7,5,4,4,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+};
+
+int         nchars=128;
+int         base=16;
+int         ncol;
+int         text=1;
+int        strip=0;
+Biobuf        bin;
+
+void
+main(int argc, char **argv)
+{
+        int i;
+
+        Binit(&bin, 1, OWRITE);
+        ARGBEGIN{
+        case '8':
+                nchars=256; break;
+        case 'x':
+                base=16; break;
+        case 'o':
+                base=8; break;
+        case 'd':
+                base=10; break;
+        case 'b':
+                base=strtoul(EARGF(usage()), 0, 0);
+                if(base<2||base>MAXBASE)
+                        usage();
+                break;
+        case 'n':
+                text=0; break;
+        case 't':
+                strip=1;
+                /* fall through */
+        case 'c':
+                text=2; break;
+        default:
+                usage();
+        }ARGEND
+
+        ncol=Ncol[base];
+        if(argc==0){
+                for(i=0;i
diff --git a/src/cmd/basename.c b/src/cmd/basename.c
t@@ -0,0 +1,41 @@
+#include 
+#include 
+
+void
+main(int argc, char *argv[])
+{
+        char *pr;
+        int n, dflag;
+
+        dflag = 0;
+        if(argc>1 && strcmp(argv[1], "-d") == 0){
+                --argc;
+                ++argv;
+                dflag = 1;
+        }
+        if(argc < 2 || argc > 3){
+                fprint(2, "usage: basename [-d] string [suffix]\n");
+                exits("usage");
+        }
+        pr = utfrrune(argv[1], '/');
+        if(dflag){
+                if(pr){
+                        *pr = 0;
+                        print("%s\n", argv[1]);
+                        exits(0);
+                }
+                print(".\n");
+                exits(0);
+        }
+        if(pr)
+                pr++;
+        else
+                pr = argv[1];
+        if(argc==3){
+                n = strlen(pr)-strlen(argv[2]);
+                if(n >= 0 && !strcmp(pr+n, argv[2]))
+                        pr[n] = 0;
+        }
+        print("%s\n", pr);
+        exits(0);
+}
diff --git a/src/cmd/cal.c b/src/cmd/cal.c
t@@ -0,0 +1,313 @@
+#include 
+#include 
+#include 
+
+char        dayw[] =
+{
+        " S  M Tu  W Th  F  S"
+};
+char        *smon[] =
+{
+        "January", "February", "March", "April",
+        "May", "June", "July", "August",
+        "September", "October", "November", "December",
+};
+char        mon[] =
+{
+        0,
+        31, 29, 31, 30,
+        31, 30, 31, 31,
+        30, 31, 30, 31,
+};
+char        string[432];
+Biobuf        bout;
+
+void        main(int argc, char *argv[]);
+int        number(char *str);
+void        pstr(char *str, int n);
+void        cal(int m, int y, char *p, int w);
+int        jan1(int yr);
+int        curmo(void);
+int        curyr(void);
+
+void
+main(int argc, char *argv[])
+{
+        int y, i, j, m;
+
+        if(argc > 3) {
+                fprint(2, "usage: cal [month] [year]\n");
+                exits("usage");
+        }
+        Binit(&bout, 1, OWRITE);
+
+/*
+ * no arg, print current month
+ */
+        if(argc == 1) {
+                m = curmo();
+                y = curyr();
+                goto xshort;
+        }
+
+/*
+ * one arg
+ *        if looks like a month, print month
+ *        else print year
+ */
+        if(argc == 2) {
+                y = number(argv[1]);
+                if(y < 0)
+                        y = -y;
+                if(y >= 1 && y <= 12) {
+                        m = y;
+                        y = curyr();
+                        goto xshort;
+                }
+                goto xlong;
+        }
+
+/*
+ * two arg, month and year
+ */
+        m = number(argv[1]);
+        if(m < 0)
+                m = -m;
+        y = number(argv[2]);
+        goto xshort;
+
+/*
+ *        print out just month
+ */
+xshort:
+        if(m < 1 || m > 12)
+                goto badarg;
+        if(y < 1 || y > 9999)
+                goto badarg;
+        Bprint(&bout, "   %s %ud\n", smon[m-1], y);
+        Bprint(&bout, "%s\n", dayw);
+        cal(m, y, string, 24);
+        for(i=0; i<6*24; i+=24)
+                pstr(string+i, 24);
+        exits(0);
+
+/*
+ *        print out complete year
+ */
+xlong:
+        y = number(argv[1]);
+        if(y<1 || y>9999)
+                goto badarg;
+        Bprint(&bout, "\n\n\n");
+        Bprint(&bout, "                                %ud\n", y);
+        Bprint(&bout, "\n");
+        for(i=0; i<12; i+=3) {
+                for(j=0; j<6*72; j++)
+                        string[j] = '\0';
+                Bprint(&bout, "         %.3s", smon[i]);
+                Bprint(&bout, "                    %.3s", smon[i+1]);
+                Bprint(&bout, "                    %.3s\n", smon[i+2]);
+                Bprint(&bout, "%s   %s   %s\n", dayw, dayw, dayw);
+                cal(i+1, y, string, 72);
+                cal(i+2, y, string+23, 72);
+                cal(i+3, y, string+46, 72);
+                for(j=0; j<6*72; j+=72)
+                        pstr(string+j, 72);
+        }
+        Bprint(&bout, "\n\n\n");
+        exits(0);
+
+badarg:
+        Bprint(&bout, "cal: bad argument\n");
+}
+
+struct
+{
+        char*        word;
+        int        val;
+} dict[] =
+{
+        "jan",                1,
+        "january",        1,
+        "feb",                2,
+        "february",        2,
+        "mar",                3,
+        "march",        3,
+        "apr",                4,
+        "april",        4,
+        "may",                5,
+        "jun",                6,
+        "june",                6,
+        "jul",                7,
+        "july",                7,
+        "aug",                8,
+        "august",        8,
+        "sep",                9,
+        "sept",                9,
+        "september",        9,
+        "oct",                10,
+        "october",        10,
+        "nov",                11,
+        "november",        11,
+        "dec",                12,
+        "december",        12,
+        0
+};
+
+/*
+ * convert to a number.
+ * if its a dictionary word,
+ * return negative  number
+ */
+int
+number(char *str)
+{
+        int n, c;
+        char *s;
+
+        for(n=0; s=dict[n].word; n++)
+                if(strcmp(s, str) == 0)
+                        return -dict[n].val;
+        n = 0;
+        s = str;
+        while(c = *s++) {
+                if(c<'0' || c>'9')
+                        return 0;
+                n = n*10 + c-'0';
+        }
+        return n;
+}
+
+void
+pstr(char *str, int n)
+{
+        int i;
+        char *s;
+
+        s = str;
+        i = n;
+        while(i--)
+                if(*s++ == '\0')
+                        s[-1] = ' ';
+        i = n+1;
+        while(i--)
+                if(*--s != ' ')
+                        break;
+        s[1] = '\0';
+        Bprint(&bout, "%s\n", str);
+}
+
+void
+cal(int m, int y, char *p, int w)
+{
+        int d, i;
+        char *s;
+
+        s = p;
+        d = jan1(y);
+        mon[2] = 29;
+        mon[9] = 30;
+
+        switch((jan1(y+1)+7-d)%7) {
+
+        /*
+         *        non-leap year
+         */
+        case 1:
+                mon[2] = 28;
+                break;
+
+        /*
+         *        1752
+         */
+        default:
+                mon[9] = 19;
+                break;
+
+        /*
+         *        leap year
+         */
+        case 2:
+                ;
+        }
+        for(i=1; i 9)
+                        *s = i/10+'0';
+                s++;
+                *s++ = i%10+'0';
+                s++;
+                if(++d == 7) {
+                        d = 0;
+                        s = p+w;
+                        p = s;
+                }
+        }
+}
+
+/*
+ *        return day of the week
+ *        of jan 1 of given year
+ */
+int
+jan1(int yr)
+{
+        int y, d;
+
+/*
+ *        normal gregorian calendar
+ *        one extra day per four years
+ */
+
+        y = yr;
+        d = 4+y+(y+3)/4;
+
+/*
+ *        julian calendar
+ *        regular gregorian
+ *        less three days per 400
+ */
+
+        if(y > 1800) {
+                d -= (y-1701)/100;
+                d += (y-1601)/400;
+        }
+
+/*
+ *        great calendar changeover instant
+ */
+
+        if(y > 1752)
+                d += 3;
+
+        return d%7;
+}
+
+/*
+ * system dependent
+ * get current month and year
+ */
+int
+curmo(void)
+{
+        Tm *tm;
+
+        tm = localtime(time(0));
+        return tm->mon+1;
+}
+
+int
+curyr(void)
+{
+        Tm *tm;
+
+        tm = localtime(time(0));
+        return tm->year+1900;
+}
diff --git a/src/cmd/calendar.c b/src/cmd/calendar.c
t@@ -0,0 +1,195 @@
+#include 
+#include 
+#include 
+#include 
+#include 
+
+typedef struct Date        Date;
+struct Date {
+        Reprog *p;        /* an RE to match this date */
+        Date *next;        /* pointer to next in list */
+};
+
+enum{
+        Secondsperday = 24*60*60
+};
+
+Biobuf in;
+int debug, matchyear;
+
+Date *dates(Date**, Tm*);
+void upper2lower(char*, char*, int);
+void *alloc(unsigned int);
+
+void
+main(int argc, char *argv[])
+{
+        int i, fd, ahead;
+        long now;
+        char *line;
+        Tm *tm;
+        Date *first, *last, *d;
+        char buf[1024];
+
+        ahead = 0;
+        ARGBEGIN{
+        case 'y':
+                matchyear = 1;
+                break;
+        case 'd':
+                debug = 1;
+                break;
+        case 'p':
+                ahead = atoi(ARGF());
+                break;
+        default:
+                fprint(2, "usage: calendar [-y] [-d] [files ...]\n");
+                exits("usage");
+        }ARGEND;
+
+        /* make a list of dates */
+        now = time(0);
+        tm = localtime(now);
+        last = nil;
+        first = dates(&last, tm);
+        now += Secondsperday;
+        tm = localtime(now);
+        dates(&last, tm);
+        if(tm->wday == 6){
+                now += Secondsperday;
+                tm = localtime(now);
+                dates(&last, tm);
+        }
+        if(tm->wday == 0){
+                now += Secondsperday;
+                tm = localtime(now);
+                dates(&last, tm);
+        }
+        if(ahead){
+                now = time(0);
+                now += ahead * Secondsperday;
+                tm = localtime(now);
+                dates(&last, tm);
+        }
+
+        for(i=0; inext)
+                                if(regexec(d->p, buf, 0, 0)){
+                                        print("%s\n", line);
+                                        break;
+                                }
+                }
+                close(fd);
+        }
+        exits("");
+}
+
+char *months[] = 
+{
+        "january",
+        "february",
+        "march",
+        "april",
+        "may",
+        "june",
+        "july",
+        "august",
+        "september",
+        "october",
+        "november",
+        "december"
+};
+
+/*
+ * Generate two Date structures.  First has month followed by day;
+ * second has day followed by month.  Link them into list after
+ * last, and return the first.
+ */
+Date*
+dates(Date **last, Tm *tm)
+{
+        Date *first;
+        Date *nd;
+        char mo[128], buf[128];
+
+        if(utflen(months[tm->mon]) > 3)
+                snprint(mo, sizeof mo, "%3.3s(%s)?",
+                        months[tm->mon], months[tm->mon]+3);
+        else
+                snprint(mo, sizeof mo, "%3.3s", months[tm->mon]);
+        if (matchyear)
+                snprint(buf, sizeof buf,
+                        "(^| |\t)((%s( |\t)+)|(%d/))%d( |\t|$)(((%d|%d)( |\t|$))|[^0-9]|([0-9]+[^0-9 \t]))",
+                        mo, tm->mon+1, tm->mday, tm->year+1900, tm->year%100);
+        else
+                snprint(buf, sizeof buf,
+                        "(^| |\t)((%s( |\t)+)|(%d/))%d( |\t|$)",
+                        mo, tm->mon+1, tm->mday);
+        if(debug)
+                print("%s\n", buf);
+
+        first = alloc(sizeof(Date));
+        if(*last)
+                (*last)->next = first;
+        first->p = regcomp(buf);        
+
+        if (matchyear)
+                snprint(buf, sizeof buf,
+                        "(^| |\t)%d( |\t)+(%s)( |\t|$)(((%d|%d)( |\t|$))|[^0-9]|([0-9]+[^0-9 \t]))",
+                        tm->mday, mo, tm->year+1900, tm->year%100);
+        else
+                snprint(buf, sizeof buf,
+                        "(^| |\t)%d( |\t)+(%s)( |\t|$)",
+                        tm->mday, mo);
+        if(debug)
+                print("%s\n", buf);
+        nd = alloc(sizeof(Date));
+        nd->p = regcomp(buf);        
+        nd->next = 0;
+        first->next = nd;
+        *last = nd;
+
+        return first;
+}
+
+/*
+ * Copy 'from' to 'to', converting to lower case
+ */
+void
+upper2lower(char *to, char *from, int len)
+{
+        while(--len>0 && *from!='\0')
+                *to++ = tolower(*from++);
+        *to = 0;
+}
+
+/*
+ * Call malloc and check for errors
+ */
+void*
+alloc(unsigned int n)
+{
+        void *p;
+
+        p = malloc(n);
+        if(p == 0){
+                fprint(2, "calendar: malloc failed: %r\n");
+                exits("malloc");
+        }
+        return p;
+}
diff --git a/src/cmd/cat.c b/src/cmd/cat.c
t@@ -0,0 +1,36 @@
+#include 
+#include 
+
+void
+cat(int f, char *s)
+{
+        char buf[8192];
+        long n;
+
+        while((n=read(f, buf, (long)sizeof buf))>0)
+                if(write(1, buf, n)!=n)
+                        sysfatal("write error copying %s: %r", s);
+        if(n < 0)
+                sysfatal("error reading %s: %r", s);
+}
+
+void
+main(int argc, char *argv[])
+{
+        int f, i;
+
+        argv0 = "cat";
+        if(argc == 1)
+                cat(0, "");
+        else for(i=1; i
diff --git a/src/cmd/cleanname.c b/src/cmd/cleanname.c
t@@ -0,0 +1,44 @@
+#include 
+#include 
+
+void
+main(int argc, char **argv)
+{
+        char *dir;
+        char *name;
+        int i;
+
+        dir = nil;
+        ARGBEGIN{
+        case 'd':
+                if((dir=ARGF()) == nil)
+                        goto Usage;
+                break;
+        default:
+                goto Usage;
+        }ARGEND;
+
+        if(argc < 1) {
+        Usage:
+                fprint(2, "usage: cleanname [-d pwd] name...\n");
+                exits("usage");
+        }
+
+        for(i=0; i
diff --git a/src/cmd/cmp.c b/src/cmd/cmp.c
t@@ -0,0 +1,112 @@
+#include 
+#include 
+
+#define                BUF                65536
+
+int sflag = 0;
+int lflag = 0;
+int Lflag = 0;
+
+static void usage(void);
+
+void
+main(int argc, char *argv[])
+{
+        int n, i;
+        uchar *p, *q;
+        uchar buf1[BUF], buf2[BUF];
+        int f1, f2;
+        vlong nc = 1, o, l = 1;
+        char *name1, *name2;
+        uchar *b1s, *b1e, *b2s, *b2e;
+
+        ARGBEGIN{
+        case 's':        sflag = 1; break;
+        case 'l':        lflag = 1; break;
+        case 'L':        Lflag = 1; break;
+        default:        usage();
+        }ARGEND
+        if(argc < 2)
+                usage();
+        if((f1 = open(name1 = *argv++, OREAD)) == -1){
+                if(!sflag) perror(name1);
+                exits("open");
+        }
+        if((f2 = open(name2 = *argv++, OREAD)) == -1){
+                if(!sflag) perror(name2);
+                exits("open");
+        }
+        if(*argv){
+                o = strtoll(*argv++, 0, 0);
+                if(seek(f1, o, 0) < 0){
+                        if(!sflag) perror("cmp: seek by offset1");
+                        exits("seek 1");
+                }
+        }
+        if(*argv){
+                o = strtoll(*argv++, 0, 0);
+                if(seek(f2, o, 0) < 0){
+                        if(!sflag) perror("cmp: seek by offset2");
+                        exits("seek 2");
+                }
+        }
+        if(*argv)
+                usage();
+        b1s = b1e = buf1;
+        b2s = b2e = buf2;
+        for(;;){
+                if(b1s >= b1e){
+                        if(b1s >= &buf1[BUF])
+                                b1s = buf1;
+                        n = read(f1, b1s,  &buf1[BUF] - b1s);
+                        b1e = b1s + n;
+                }
+                if(b2s >= b2e){
+                        if(b2s >= &buf2[BUF])
+                                b2s = buf2;
+                        n = read(f2, b2s,  &buf2[BUF] - b2s);
+                        b2e = b2s + n;
+                }
+                n = b2e - b2s;
+                if(n > b1e - b1s)
+                        n = b1e - b1s;
+                if(n <= 0)
+                        break;
+                if(memcmp((void *)b1s, (void *)b2s, n) != 0){
+                        if(sflag)
+                                exits("differ");
+                        for(p = b1s, q = b2s, i = 0; i < n; p++, q++, i++) {
+                                if(*p == '\n')
+                                        l++;
+                                if(*p != *q){
+                                        if(!lflag){
+                                                print("%s %s differ: char %lld",
+                                                    name1, name2, nc+i);
+                                                print(Lflag?" line %lld\n":"\n", l);
+                                                exits("differ");
+                                        }
+                                        print("%6lld 0x%.2x 0x%.2x\n", nc+i, *p, *q);
+                                }
+                        }
+                }                
+                if(Lflag)
+                        for(p = b1s; p < b1e;)
+                                if(*p++ == '\n')
+                                        l++;
+                nc += n;
+                b1s += n;
+                b2s += n;
+        }
+        if(b1e - b1s == b2e - b2s)
+                exits((char *)0);
+        if(!sflag)
+                print("EOF on %s\n", (b1e - b1s > b2e - b2s)? name2 : name1);
+        exits("EOF");
+}
+
+static void
+usage(void)
+{
+        print("Usage: cmp [-lsL] file1 file2 [offset1 [offset2] ]\n");
+        exits("usage");
+}
diff --git a/src/cmd/comm.c b/src/cmd/comm.c
t@@ -0,0 +1,178 @@
+#include 
+#include 
+#include 
+
+#define LB 2048
+int        one;
+int        two;
+int        three;
+
+char        *ldr[3];
+
+Biobuf *ib1;
+Biobuf *ib2;
+Biobuf *openfil(char*);
+int        rd(Biobuf*, char*);
+void        wr(char*, int);
+void        copy(Biobuf*, char*, int);
+int        compare(char*, char*);
+
+void
+main(int argc, char *argv[])
+{
+        int l;
+        char        lb1[LB],lb2[LB];
+
+        ldr[0] = "";
+        ldr[1] = "\t";
+        ldr[2] = "\t\t";
+        l = 2;
+        ARGBEGIN{
+        case '1':
+                if(!one) {
+                        one = 1;
+                        ldr[1][0] =
+                        ldr[2][l--] = '\0';
+                }
+                break;
+
+        case '2':
+                if(!two) {
+                        two = 1;
+                        ldr[2][l--] = '\0';
+                }
+                break;
+
+        case '3':
+                three = 1;
+                break;
+
+        default:
+                goto Usage;
+
+        }ARGEND
+
+        if(argc < 2) {
+    Usage:
+                fprint(2, "usage: comm [-123] file1 file2\n");
+                exits("usage");
+        }
+
+        ib1 = openfil(argv[0]);
+        ib2 = openfil(argv[1]);
+
+
+        if(rd(ib1,lb1) < 0){
+                if(rd(ib2,lb2) < 0)
+                        exits(0);
+                copy(ib2,lb2,2);
+        }
+        if(rd(ib2,lb2) < 0)
+                copy(ib1,lb1,1);
+
+        for(;;){
+                switch(compare(lb1,lb2)) {
+                case 0:
+                        wr(lb1,3);
+                        if(rd(ib1,lb1) < 0) {
+                                if(rd(ib2,lb2) < 0)
+                                        exits(0);
+                                copy(ib2,lb2,2);
+                        }
+                        if(rd(ib2,lb2) < 0)
+                                copy(ib1,lb1,1);
+                        continue;
+
+                case 1:
+                        wr(lb1,1);
+                        if(rd(ib1,lb1) < 0)
+                                copy(ib2,lb2,2);
+                        continue;
+
+                case 2:
+                        wr(lb2,2);
+                        if(rd(ib2,lb2) < 0)
+                                copy(ib1,lb1,1);
+                        continue;
+                }
+        }
+        exits(0);
+}
+
+int
+rd(Biobuf *file, char *buf)
+{
+        int i, c;
+
+        i = 0;
+        while((c = Bgetc(file)) != Beof) {
+                *buf = c;
+                if(c == '\n' || i > LB-2) {
+                        *buf = '\0';
+                        return 0;
+                }
+                i++;
+                buf++;
+        }
+        return -1;
+}
+
+void
+wr(char *str, int n)
+{
+
+        switch(n){
+                case 1:
+                        if(one)
+                                return;
+                        break;
+
+                case 2:
+                        if(two)
+                                return;
+                        break;
+
+                case 3:
+                        if(three)
+                                return;
+        }
+        print("%s%s\n", ldr[n-1],str);
+}
+
+void
+copy(Biobuf *ibuf, char *lbuf, int n)
+{
+        do
+                wr(lbuf,n);
+        while(rd(ibuf,lbuf) >= 0);
+        exits(0);
+}
+
+int
+compare(char *a, char *b)
+{
+        while(*a == *b){
+                if(*a == '\0')
+                        return 0;
+                a++;
+                b++;
+        }
+        if(*a < *b)
+                return 1;
+        return 2;
+}
+
+Biobuf*
+openfil(char *s)
+{
+        Biobuf *b;
+
+        if(s[0]=='-' && s[1]==0)
+                s = "/fd/0";
+        b = Bopen(s, OREAD);
+        if(b)
+                return b;
+        fprint(2,"comm: cannot open %s: %r\n",s);
+        exits("open");
+        return 0;        /* shut up ken */
+}
diff --git a/src/cmd/date.c b/src/cmd/date.c
t@@ -0,0 +1,30 @@
+#include 
+#include 
+
+int uflg, nflg;
+
+void
+main(int argc, char *argv[])
+{
+        ulong now;
+
+        ARGBEGIN{
+        case 'n':        nflg = 1; break;
+        case 'u':        uflg = 1; break;
+        default:        fprint(2, "usage: date [-un] [seconds]\n"); exits("usage");
+        }ARGEND
+
+        if(argc == 1)
+                now = strtoul(*argv, 0, 0);
+        else
+                now = time(0);
+
+        if(nflg)
+                print("%ld\n", now);
+        else if(uflg)
+                print("%s", asctime(gmtime(now)));
+        else
+                print("%s", ctime(now));
+        
+        exits(0);
+}
diff --git a/src/cmd/dc.c b/src/cmd/dc.c
t@@ -0,0 +1,2300 @@
+#include 
+#include 
+#include 
+
+typedef        void*        pointer;
+
+#define div        dcdiv
+
+#define FATAL 0
+#define NFATAL 1
+#define BLK sizeof(Blk)
+#define PTRSZ sizeof(int*)
+#define HEADSZ 1024
+#define STKSZ 100
+#define RDSKSZ 100
+#define TBLSZ 256
+#define ARRAYST 221
+#define MAXIND 2048
+#define NL 1
+#define NG 2
+#define NE 3
+#define length(p)        ((p)->wt-(p)->beg)
+#define rewind(p)        (p)->rd=(p)->beg
+#define create(p)        (p)->rd = (p)->wt = (p)->beg
+#define fsfile(p)        (p)->rd = (p)->wt
+#define truncate(p)        (p)->wt = (p)->rd
+#define sfeof(p)        (((p)->rd==(p)->wt)?1:0)
+#define sfbeg(p)        (((p)->rd==(p)->beg)?1:0)
+#define sungetc(p,c)        *(--(p)->rd)=c
+#define sgetc(p)        (((p)->rd==(p)->wt)?-1:*(p)->rd++)
+#define skipc(p)        {if((p)->rd<(p)->wt)(p)->rd++;}
+#define slookc(p)        (((p)->rd==(p)->wt)?-1:*(p)->rd)
+#define sbackc(p)        (((p)->rd==(p)->beg)?-1:*(--(p)->rd))
+#define backc(p)        {if((p)->rd>(p)->beg) --(p)->rd;}
+#define sputc(p,c)        {if((p)->wt==(p)->last)more(p);\
+                                *(p)->wt++ = c; }
+#define salterc(p,c)        {if((p)->rd==(p)->last)more(p);\
+                                *(p)->rd++ = c;\
+                                if((p)->rd>(p)->wt)(p)->wt=(p)->rd;}
+#define sunputc(p)        (*((p)->rd = --(p)->wt))
+#define sclobber(p)        ((p)->rd = --(p)->wt)
+#define zero(p)                for(pp=(p)->beg;pp<(p)->last;)\
+                                *pp++='\0'
+#define OUTC(x)                {Bputc(&bout,x); if(--count == 0){Bprint(&bout,"\\\n"); count=ll;} }
+#define TEST2                {if((count -= 2) <=0){Bprint(&bout,"\\\n");count=ll;}}
+#define EMPTY                if(stkerr != 0){Bprint(&bout,"stack empty\n"); continue; }
+#define EMPTYR(x)        if(stkerr!=0){pushp(x);Bprint(&bout,"stack empty\n");continue;}
+#define EMPTYS                if(stkerr != 0){Bprint(&bout,"stack empty\n"); return(1);}
+#define EMPTYSR(x)        if(stkerr !=0){Bprint(&bout,"stack empty\n");pushp(x);return(1);}
+#define error(p)        {Bprint(&bout,p); continue; }
+#define errorrt(p)        {Bprint(&bout,p); return(1); }
+#define LASTFUN 026
+
+typedef        struct        Blk        Blk;
+struct        Blk
+{
+        char        *rd;
+        char        *wt;
+        char        *beg;
+        char        *last;
+};
+typedef        struct        Sym        Sym;
+struct        Sym
+{
+        Sym        *next;
+        Blk        *val;
+};
+typedef        struct        Wblk        Wblk;
+struct        Wblk
+{
+        Blk        **rdw;
+        Blk        **wtw;
+        Blk        **begw;
+        Blk        **lastw;
+};
+
+Biobuf        *curfile, *fsave;
+Blk        *arg1, *arg2;
+uchar        savk;
+int        dbg;
+int        ifile;
+Blk        *scalptr, *basptr, *tenptr, *inbas;
+Blk        *sqtemp, *chptr, *strptr, *divxyz;
+Blk        *stack[STKSZ];
+Blk        **stkptr,**stkbeg;
+Blk        **stkend;
+Blk        *hfree;
+int        stkerr;
+int        lastchar;
+Blk        *readstk[RDSKSZ];
+Blk        **readptr;
+Blk        *rem;
+int        k;
+Blk        *irem;
+int        skd,skr;
+int        neg;
+Sym        symlst[TBLSZ];
+Sym        *stable[TBLSZ];
+Sym        *sptr, *sfree;
+long        rel;
+long        nbytes;
+long        all;
+long        headmor;
+long        obase;
+int        fw,fw1,ll;
+void        (*outdit)(Blk *p, int flg);
+int        logo;
+int        logten;
+int        count;
+char        *pp;
+char        *dummy;
+long        longest, maxsize, active;
+int        lall, lrel, lcopy, lmore, lbytes;
+int        inside;
+Biobuf        bin;
+Biobuf        bout;
+
+void        main(int argc, char *argv[]);
+void        commnds(void);
+Blk*        readin(void);
+Blk*        div(Blk *ddivd, Blk *ddivr);
+int        dscale(void);
+Blk*        removr(Blk *p, int n);
+Blk*        dcsqrt(Blk *p);
+void        init(int argc, char *argv[]);
+void        onintr(void);
+void        pushp(Blk *p);
+Blk*        pop(void);
+Blk*        readin(void);
+Blk*        add0(Blk *p, int ct);
+Blk*        mult(Blk *p, Blk *q);
+void        chsign(Blk *p);
+int        readc(void);
+void        unreadc(char c);
+void        binop(char c);
+void        dcprint(Blk *hptr);
+Blk*        dcexp(Blk *base, Blk *ex);
+Blk*        getdec(Blk *p, int sc);
+void        tenot(Blk *p, int sc);
+void        oneot(Blk *p, int sc, char ch);
+void        hexot(Blk *p, int flg);
+void        bigot(Blk *p, int flg);
+Blk*        add(Blk *a1, Blk *a2);
+int        eqk(void);
+Blk*        removc(Blk *p, int n);
+Blk*        scalint(Blk *p);
+Blk*        scale(Blk *p, int n);
+int        subt(void);
+int        command(void);
+int        cond(char c);
+void        load(void);
+int        log2(long n);
+Blk*        salloc(int size);
+Blk*        morehd(void);
+Blk*        copy(Blk *hptr, int size);
+void        sdump(char *s1, Blk *hptr);
+void        seekc(Blk *hptr, int n);
+void        salterwd(Blk *hptr, Blk *n);
+void        more(Blk *hptr);
+void        ospace(char *s);
+void        garbage(char *s);
+void        release(Blk *p);
+Blk*        dcgetwd(Blk *p);
+void        putwd(Blk *p, Blk *c);
+Blk*        lookwd(Blk *p);
+char*        nalloc(char *p, unsigned nbytes);
+int        getstk(void);
+
+/********debug only**/
+void
+tpr(char *cp, Blk *bp)
+{
+        print("%s-> ", cp);
+        print("beg: %lx rd: %lx wt: %lx last: %lx\n", bp->beg, bp->rd,
+                bp->wt, bp->last);
+        for (cp = bp->beg; cp != bp->wt; cp++) {
+                print("%d", *cp);
+                if (cp != bp->wt-1)
+                        print("/");
+        }
+        print("\n");
+}
+/************/
+
+void
+main(int argc, char *argv[])
+{
+        Binit(&bin, 0, OREAD);
+        Binit(&bout, 1, OWRITE);
+        init(argc,argv);
+        commnds();
+        exits(0);
+}
+
+void
+commnds(void)
+{
+        Blk *p, *q, **ptr, *s, *t;
+        long l;
+        Sym *sp;
+        int sk, sk1, sk2, c, sign, n, d;
+
+        while(1) {
+                Bflush(&bout);
+                if(((c = readc())>='0' && c <= '9') ||
+                    (c>='A' && c <='F') || c == '.') {
+                        unreadc(c);
+                        p = readin();
+                        pushp(p);
+                        continue;
+                }
+                switch(c) {
+                case ' ':
+                case '\n':
+                case -1:
+                        continue;
+                case 'Y':
+                        sdump("stk",*stkptr);
+                        Bprint(&bout, "all %ld rel %ld headmor %ld\n",all,rel,headmor);
+                        Bprint(&bout, "nbytes %ld\n",nbytes);
+                        Bprint(&bout, "longest %ld active %ld maxsize %ld\n", longest,
+                                active, maxsize);
+                        Bprint(&bout, "new all %d rel %d copy %d more %d lbytes %d\n",
+                                lall, lrel, lcopy, lmore, lbytes);
+                        lall = lrel = lcopy = lmore = lbytes = 0;
+                        continue;
+                case '_':
+                        p = readin();
+                        savk = sunputc(p);
+                        chsign(p);
+                        sputc(p,savk);
+                        pushp(p);
+                        continue;
+                case '-':
+                        subt();
+                        continue;
+                case '+':
+                        if(eqk() != 0)
+                                continue;
+                        binop('+');
+                        continue;
+                case '*':
+                        arg1 = pop();
+                        EMPTY;
+                        arg2 = pop();
+                        EMPTYR(arg1);
+                        sk1 = sunputc(arg1);
+                        sk2 = sunputc(arg2);
+                        savk = sk1+sk2;
+                        binop('*');
+                        p = pop();
+                        if(savk>k && savk>sk1 && savk>sk2) {
+                                sclobber(p);
+                                sk = sk1;
+                                if(sk=3) {
+                                error("exp too big\n");
+                        }
+                        savk = sunputc(arg2);
+                        p = dcexp(arg2,arg1);
+                        release(arg2);
+                        rewind(arg1);
+                        c = sgetc(arg1);
+                        if(c == -1)
+                                c = 0;
+                        else
+                        if(sfeof(arg1) == 0)
+                                c = sgetc(arg1)*100 + c;
+                        d = c*savk;
+                        release(arg1);
+                /*        if(neg == 0) {                removed to fix -exp bug*/
+                                if(k>=savk)
+                                        n = k;
+                                else
+                                        n = savk;
+                                if(n= 100) {
+                                sputc(p,n/100);
+                                n %= 100;
+                        }
+                        sputc(p,n);
+                        sputc(p,0);
+                        pushp(p);
+                        continue;
+                case 'Z':
+                        p = pop();
+                        EMPTY;
+                        n = (length(p)-1)<<1;
+                        fsfile(p);
+                        backc(p);
+                        if(sfbeg(p) == 0) {
+                                if((c = sbackc(p))<0) {
+                                        n -= 2;
+                                        if(sfbeg(p) == 1)
+                                                n++;
+                                        else {
+                                                if((c = sbackc(p)) == 0)
+                                                        n++;
+                                                else
+                                                if(c > 90)
+                                                        n--;
+                                        }
+                                } else
+                                if(c < 10)
+                                        n--;
+                        }
+                        release(p);
+                        q = salloc(1);
+                        if(n >= 100) {
+                                sputc(q,n%100);
+                                n /= 100;
+                        }
+                        sputc(q,n);
+                        sputc(q,0);
+                        pushp(q);
+                        continue;
+                case 'i':
+                        p = pop();
+                        EMPTY;
+                        p = scalint(p);
+                        release(inbas);
+                        inbas = p;
+                        continue;
+                case 'I':
+                        p = copy(inbas,length(inbas)+1);
+                        sputc(p,0);
+                        pushp(p);
+                        continue;
+                case 'o':
+                        p = pop();
+                        EMPTY;
+                        p = scalint(p);
+                        sign = 0;
+                        n = length(p);
+                        q = copy(p,n);
+                        fsfile(q);
+                        l = c = sbackc(q);
+                        if(n != 1) {
+                                if(c<0) {
+                                        sign = 1;
+                                        chsign(q);
+                                        n = length(q);
+                                        fsfile(q);
+                                        l = c = sbackc(q);
+                                }
+                                if(n != 1) {
+                                        while(sfbeg(q) == 0)
+                                                l = l*100+sbackc(q);
+                                }
+                        }
+                        logo = log2(l);
+                        obase = l;
+                        release(basptr);
+                        if(sign == 1)
+                                obase = -l;
+                        basptr = p;
+                        outdit = bigot;
+                        if(n == 1 && sign == 0) {
+                                if(c <= 16) {
+                                        outdit = hexot;
+                                        fw = 1;
+                                        fw1 = 0;
+                                        ll = 70;
+                                        release(q);
+                                        continue;
+                                }
+                        }
+                        n = 0;
+                        if(sign == 1)
+                                n++;
+                        p = salloc(1);
+                        sputc(p,-1);
+                        t = add(p,q);
+                        n += length(t)*2;
+                        fsfile(t);
+                        if(sbackc(t)>9)
+                                n++;
+                        release(t);
+                        release(q);
+                        release(p);
+                        fw = n;
+                        fw1 = n-1;
+                        ll = 70;
+                        if(fw>=ll)
+                                continue;
+                        ll = (70/fw)*fw;
+                        continue;
+                case 'O':
+                        p = copy(basptr,length(basptr)+1);
+                        sputc(p,0);
+                        pushp(p);
+                        continue;
+                case '[':
+                        n = 0;
+                        p = salloc(0);
+                        for(;;) {
+                                if((c = readc()) == ']') {
+                                        if(n == 0)
+                                                break;
+                                        n--;
+                                }
+                                sputc(p,c);
+                                if(c == '[')
+                                        n++;
+                        }
+                        pushp(p);
+                        continue;
+                case 'k':
+                        p = pop();
+                        EMPTY;
+                        p = scalint(p);
+                        if(length(p)>1) {
+                                error("scale too big\n");
+                        }
+                        rewind(p);
+                        k = 0;
+                        if(!sfeof(p))
+                                k = sgetc(p);
+                        release(scalptr);
+                        scalptr = p;
+                        continue;
+                case 'K':
+                        p = copy(scalptr,length(scalptr)+1);
+                        sputc(p,0);
+                        pushp(p);
+                        continue;
+                case 'X':
+                        p = pop();
+                        EMPTY;
+                        fsfile(p);
+                        n = sbackc(p);
+                        release(p);
+                        p = salloc(2);
+                        sputc(p,n);
+                        sputc(p,0);
+                        pushp(p);
+                        continue;
+                case 'Q':
+                        p = pop();
+                        EMPTY;
+                        if(length(p)>2) {
+                                error("Q?\n");
+                        }
+                        rewind(p);
+                        if((c =  sgetc(p))<0) {
+                                error("neg Q\n");
+                        }
+                        release(p);
+                        while(c-- > 0) {
+                                if(readptr == &readstk[0]) {
+                                        error("readstk?\n");
+                                }
+                                if(*readptr != 0)
+                                        release(*readptr);
+                                readptr--;
+                        }
+                        continue;
+                case 'q':
+                        if(readptr <= &readstk[1])
+                                exits(0);
+                        if(*readptr != 0)
+                                release(*readptr);
+                        readptr--;
+                        if(*readptr != 0)
+                                release(*readptr);
+                        readptr--;
+                        continue;
+                case 'f':
+                        if(stkptr == &stack[0])
+                                Bprint(&bout,"empty stack\n");
+                        else {
+                                for(ptr = stkptr; ptr > &stack[0];) {
+                                        dcprint(*ptr--);
+                                }
+                        }
+                        continue;
+                case 'p':
+                        if(stkptr == &stack[0])
+                                Bprint(&bout,"empty stack\n");
+                        else {
+                                dcprint(*stkptr);
+                        }
+                        continue;
+                case 'P':
+                        p = pop();
+                        EMPTY;
+                        sputc(p,0);
+                        Bprint(&bout,"%s",p->beg);
+                        release(p);
+                        continue;
+                case 'd':
+                        if(stkptr == &stack[0]) {
+                                Bprint(&bout,"empty stack\n");
+                                continue;
+                        }
+                        q = *stkptr;
+                        n = length(q);
+                        p = copy(*stkptr,n);
+                        pushp(p);
+                        continue;
+                case 'c':
+                        while(stkerr == 0) {
+                                p = pop();
+                                if(stkerr == 0)
+                                        release(p);
+                        }
+                        continue;
+                case 'S':
+                        if(stkptr == &stack[0]) {
+                                error("save: args\n");
+                        }
+                        c = getstk() & 0377;
+                        sptr = stable[c];
+                        sp = stable[c] = sfree;
+                        sfree = sfree->next;
+                        if(sfree == 0)
+                                goto sempty;
+                        sp->next = sptr;
+                        p = pop();
+                        EMPTY;
+                        if(c >= ARRAYST) {
+                                q = copy(p,length(p)+PTRSZ);
+                                for(n = 0;n < PTRSZ;n++) {
+                                        sputc(q,0);
+                                }
+                                release(p);
+                                p = q;
+                        }
+                        sp->val = p;
+                        continue;
+                sempty:
+                        error("symbol table overflow\n");
+                case 's':
+                        if(stkptr == &stack[0]) {
+                                error("save:args\n");
+                        }
+                        c = getstk() & 0377;
+                        sptr = stable[c];
+                        if(sptr != 0) {
+                                p = sptr->val;
+                                if(c >= ARRAYST) {
+                                        rewind(p);
+                                        while(sfeof(p) == 0)
+                                                release(dcgetwd(p));
+                                }
+                                release(p);
+                        } else {
+                                sptr = stable[c] = sfree;
+                                sfree = sfree->next;
+                                if(sfree == 0)
+                                        goto sempty;
+                                sptr->next = 0;
+                        }
+                        p = pop();
+                        sptr->val = p;
+                        continue;
+                case 'l':
+                        load();
+                        continue;
+                case 'L':
+                        c = getstk() & 0377;
+                        sptr = stable[c];
+                        if(sptr == 0) {
+                                error("L?\n");
+                        }
+                        stable[c] = sptr->next;
+                        sptr->next = sfree;
+                        sfree = sptr;
+                        p = sptr->val;
+                        if(c >= ARRAYST) {
+                                rewind(p);
+                                while(sfeof(p) == 0) {
+                                        q = dcgetwd(p);
+                                        if(q != 0)
+                                                release(q);
+                                }
+                        }
+                        pushp(p);
+                        continue;
+                case ':':
+                        p = pop();
+                        EMPTY;
+                        q = scalint(p);
+                        fsfile(q);
+                        c = 0;
+                        if((sfbeg(q) == 0) && ((c = sbackc(q))<0)) {
+                                error("neg index\n");
+                        }
+                        if(length(q)>2) {
+                                error("index too big\n");
+                        }
+                        if(sfbeg(q) == 0)
+                                c = c*100+sbackc(q);
+                        if(c >= MAXIND) {
+                                error("index too big\n");
+                        }
+                        release(q);
+                        n = getstk() & 0377;
+                        sptr = stable[n];
+                        if(sptr == 0) {
+                                sptr = stable[n] = sfree;
+                                sfree = sfree->next;
+                                if(sfree == 0)
+                                        goto sempty;
+                                sptr->next = 0;
+                                p = salloc((c+PTRSZ)*PTRSZ);
+                                zero(p);
+                        } else {
+                                p = sptr->val;
+                                if(length(p)-PTRSZ < c*PTRSZ) {
+                                        q = copy(p,(c+PTRSZ)*PTRSZ);
+                                        release(p);
+                                        p = q;
+                                }
+                        }
+                        seekc(p,c*PTRSZ);
+                        q = lookwd(p);
+                        if(q!=0)
+                                release(q);
+                        s = pop();
+                        EMPTY;
+                        salterwd(p, s);
+                        sptr->val = p;
+                        continue;
+                case ';':
+                        p = pop();
+                        EMPTY;
+                        q = scalint(p);
+                        fsfile(q);
+                        c = 0;
+                        if((sfbeg(q) == 0) && ((c = sbackc(q))<0)) {
+                                error("neg index\n");
+                        }
+                        if(length(q)>2) {
+                                error("index too big\n");
+                        }
+                        if(sfbeg(q) == 0)
+                                c = c*100+sbackc(q);
+                        if(c >= MAXIND) {
+                                error("index too big\n");
+                        }
+                        release(q);
+                        n = getstk() & 0377;
+                        sptr = stable[n];
+                        if(sptr != 0){
+                                p = sptr->val;
+                                if(length(p)-PTRSZ >= c*PTRSZ) {
+                                        seekc(p,c*PTRSZ);
+                                        s = dcgetwd(p);
+                                        if(s != 0) {
+                                                q = copy(s,length(s));
+                                                pushp(q);
+                                                continue;
+                                        }
+                                }
+                        }
+                        q = salloc(1);        /*so uninitialized array elt prints as 0*/
+                        sputc(q, 0);
+                        pushp(q);
+                        continue;
+                case 'x':
+                execute:
+                        p = pop();
+                        EMPTY;
+                        if((readptr != &readstk[0]) && (*readptr != 0)) {
+                                if((*readptr)->rd == (*readptr)->wt)
+                                        release(*readptr);
+                                else {
+                                        if(readptr++ == &readstk[RDSKSZ]) {
+                                                error("nesting depth\n");
+                                        }
+                                }
+                        } else
+                                readptr++;
+                        *readptr = p;
+                        if(p != 0)
+                                rewind(p);
+                        else {
+                                if((c = readc()) != '\n')
+                                        unreadc(c);
+                        }
+                        continue;
+                case '?':
+                        if(++readptr == &readstk[RDSKSZ]) {
+                                error("nesting depth\n");
+                        }
+                        *readptr = 0;
+                        fsave = curfile;
+                        curfile = &bin;
+                        while((c = readc()) == '!')
+                                command();
+                        p = salloc(0);
+                        sputc(p,c);
+                        while((c = readc()) != '\n') {
+                                sputc(p,c);
+                                if(c == '\\')
+                                        sputc(p,readc());
+                        }
+                        curfile = fsave;
+                        *readptr = p;
+                        continue;
+                case '!':
+                        if(command() == 1)
+                                goto execute;
+                        continue;
+                case '<':
+                case '>':
+                case '=':
+                        if(cond(c) == 1)
+                                goto execute;
+                        continue;
+                default:
+                        Bprint(&bout,"%o is unimplemented\n",c);
+                }
+        }
+}
+
+Blk*
+div(Blk *ddivd, Blk *ddivr)
+{
+        int divsign, remsign, offset, divcarry,
+                carry, dig, magic, d, dd, under, first;
+        long c, td, cc;
+        Blk *ps, *px, *p, *divd, *divr;
+
+        dig = 0;
+        under = 0;
+        divcarry = 0;
+        rem = 0;
+        p = salloc(0);
+        if(length(ddivr) == 0) {
+                pushp(ddivr);
+                Bprint(&bout,"divide by 0\n");
+                return(p);
+        }
+        divsign = remsign = first = 0;
+        divr = ddivr;
+        fsfile(divr);
+        if(sbackc(divr) == -1) {
+                divr = copy(ddivr,length(ddivr));
+                chsign(divr);
+                divsign = ~divsign;
+        }
+        divd = copy(ddivd,length(ddivd));
+        fsfile(divd);
+        if(sfbeg(divd) == 0 && sbackc(divd) == -1) {
+                chsign(divd);
+                divsign = ~divsign;
+                remsign = ~remsign;
+        }
+        offset = length(divd) - length(divr);
+        if(offset < 0)
+                goto ddone;
+        seekc(p,offset+1);
+        sputc(divd,0);
+        magic = 0;
+        fsfile(divr);
+        c = sbackc(divr);
+        if(c < 10)
+                magic++;
+        c = c * 100 + (sfbeg(divr)?0:sbackc(divr));
+        if(magic>0){
+                c = (c * 100 +(sfbeg(divr)?0:sbackc(divr)))*2;
+                c /= 25;
+        }
+        while(offset >= 0) {
+                first++;
+                fsfile(divd);
+                td = sbackc(divd) * 100;
+                dd = sfbeg(divd)?0:sbackc(divd);
+                td = (td + dd) * 100;
+                dd = sfbeg(divd)?0:sbackc(divd);
+                td = td + dd;
+                cc = c;
+                if(offset == 0)
+                        td++;
+                else
+                        cc++;
+                if(magic != 0)
+                        td = td<<3;
+                dig = td/cc;
+                under=0;
+                if(td%cc < 8  && dig > 0 && magic) {
+                        dig--;
+                        under=1;
+                }
+                rewind(divr);
+                rewind(divxyz);
+                carry = 0;
+                while(sfeof(divr) == 0) {
+                        d = sgetc(divr)*dig+carry;
+                        carry = d / 100;
+                        salterc(divxyz,d%100);
+                }
+                salterc(divxyz,carry);
+                rewind(divxyz);
+                seekc(divd,offset);
+                carry = 0;
+                while(sfeof(divd) == 0) {
+                        d = slookc(divd);
+                        d = d-(sfeof(divxyz)?0:sgetc(divxyz))-carry;
+                        carry = 0;
+                        if(d < 0) {
+                                d += 100;
+                                carry = 1;
+                        }
+                        salterc(divd,d);
+                }
+                divcarry = carry;
+                backc(p);
+                salterc(p,dig);
+                backc(p);
+                fsfile(divd);
+                d=sbackc(divd);
+                if((d != 0) && /*!divcarry*/ (offset != 0)) {
+                        d = sbackc(divd) + 100;
+                        salterc(divd,d);
+                }
+                if(--offset >= 0)
+                        divd->wt--;
+        }
+        if(under) {        /* undershot last - adjust*/
+                px = copy(divr,length(divr));        /*11/88 don't corrupt ddivr*/
+                chsign(px);
+                ps = add(px,divd);
+                fsfile(ps);
+                if(length(ps) > 0 && sbackc(ps) < 0) {
+                        release(ps);        /*only adjust in really undershot*/
+                } else {
+                        release(divd);
+                        salterc(p, dig+1);
+                        divd=ps;
+                }
+        }
+        if(divcarry != 0) {
+                salterc(p,dig-1);
+                salterc(divd,-1);
+                ps = add(divr,divd);
+                release(divd);
+                divd = ps;
+        }
+
+        rewind(p);
+        divcarry = 0;
+        while(sfeof(p) == 0){
+                d = slookc(p)+divcarry;
+                divcarry = 0;
+                if(d >= 100){
+                        d -= 100;
+                        divcarry = 1;
+                }
+                salterc(p,d);
+        }
+        if(divcarry != 0)salterc(p,divcarry);
+        fsfile(p);
+        while(sfbeg(p) == 0) {
+                if(sbackc(p) != 0)
+                        break;
+                truncate(p);
+        }
+        if(divsign < 0)
+                chsign(p);
+        fsfile(divd);
+        while(sfbeg(divd) == 0) {
+                if(sbackc(divd) != 0)
+                        break;
+                truncate(divd);
+        }
+ddone:
+        if(remsign<0)
+                chsign(divd);
+        if(divr != ddivr)
+                release(divr);
+        rem = divd;
+        return(p);
+}
+
+int
+dscale(void)
+{
+        Blk *dd, *dr, *r;
+        int c;
+
+        dr = pop();
+        EMPTYS;
+        dd = pop();
+        EMPTYSR(dr);
+        fsfile(dd);
+        skd = sunputc(dd);
+        fsfile(dr);
+        skr = sunputc(dr);
+        if(sfbeg(dr) == 1 || (sfbeg(dr) == 0 && sbackc(dr) == 0)) {
+                sputc(dr,skr);
+                pushp(dr);
+                Bprint(&bout,"divide by 0\n");
+                return(1);
+        }
+        if(sfbeg(dd) == 1 || (sfbeg(dd) == 0 && sbackc(dd) == 0)) {
+                sputc(dd,skd);
+                pushp(dd);
+                return(1);
+        }
+        c = k-skd+skr;
+        if(c < 0)
+                r = removr(dd,-c);
+        else {
+                r = add0(dd,c);
+                irem = 0;
+        }
+        arg1 = r;
+        arg2 = dr;
+        savk = k;
+        return(0);
+}
+
+Blk*
+removr(Blk *p, int n)
+{
+        int nn, neg;
+        Blk *q, *s, *r;
+
+        fsfile(p);
+        neg = sbackc(p);
+        if(neg < 0)
+                chsign(p);
+        rewind(p);
+        nn = (n+1)/2;
+        q = salloc(nn);
+        while(n>1) {
+                sputc(q,sgetc(p));
+                n -= 2;
+        }
+        r = salloc(2);
+        while(sfeof(p) == 0)
+                sputc(r,sgetc(p));
+        release(p);
+        if(n == 1){
+                s = div(r,tenptr);
+                release(r);
+                rewind(rem);
+                if(sfeof(rem) == 0)
+                        sputc(q,sgetc(rem));
+                release(rem);
+                if(neg < 0){
+                        chsign(s);
+                        chsign(q);
+                        irem = q;
+                        return(s);
+                }
+                irem = q;
+                return(s);
+        }
+        if(neg < 0) {
+                chsign(r);
+                chsign(q);
+                irem = q;
+                return(r);
+        }
+        irem = q;
+        return(r);
+}
+
+Blk*
+dcsqrt(Blk *p)
+{
+        Blk *t, *r, *q, *s;
+        int c, n, nn;
+
+        n = length(p);
+        fsfile(p);
+        c = sbackc(p);
+        if((n&1) != 1)
+                c = c*100+(sfbeg(p)?0:sbackc(p));
+        n = (n+1)>>1;
+        r = salloc(n);
+        zero(r);
+        seekc(r,n);
+        nn=1;
+        while((c -= nn)>=0)
+                nn+=2;
+        c=(nn+1)>>1;
+        fsfile(r);
+        backc(r);
+        if(c>=100) {
+                c -= 100;
+                salterc(r,c);
+                sputc(r,1);
+        } else
+                salterc(r,c);
+        for(;;){
+                q = div(p,r);
+                s = add(q,r);
+                release(q);
+                release(rem);
+                q = div(s,sqtemp);
+                release(s);
+                release(rem);
+                s = copy(r,length(r));
+                chsign(s);
+                t = add(s,q);
+                release(s);
+                fsfile(t);
+                nn = sfbeg(t)?0:sbackc(t);
+                if(nn>=0)
+                        break;
+                release(r);
+                release(t);
+                r = q;
+        }
+        release(t);
+        release(q);
+        release(p);
+        return(r);
+}
+
+Blk*
+dcexp(Blk *base, Blk *ex)
+{
+        Blk *r, *e, *p, *e1, *t, *cp;
+        int temp, c, n;
+
+        r = salloc(1);
+        sputc(r,1);
+        p = copy(base,length(base));
+        e = copy(ex,length(ex));
+        fsfile(e);
+        if(sfbeg(e) != 0)
+                goto edone;
+        temp=0;
+        c = sbackc(e);
+        if(c<0) {
+                temp++;
+                chsign(e);
+        }
+        while(length(e) != 0) {
+                e1=div(e,sqtemp);
+                release(e);
+                e = e1;
+                n = length(rem);
+                release(rem);
+                if(n != 0) {
+                        e1=mult(p,r);
+                        release(r);
+                        r = e1;
+                }
+                t = copy(p,length(p));
+                cp = mult(p,t);
+                release(p);
+                release(t);
+                p = cp;
+        }
+        if(temp != 0) {
+                if((c = length(base)) == 0) {
+                        goto edone;
+                }
+                if(c>1)
+                        create(r);
+                else {
+                        rewind(base);
+                        if((c = sgetc(base))<=1) {
+                                create(r);
+                                sputc(r,c);
+                        } else
+                                create(r);
+                }
+        }
+edone:
+        release(p);
+        release(e);
+        return(r);
+}
+
+void
+init(int argc, char *argv[])
+{
+        Sym *sp;
+        Dir *d;
+
+        ARGBEGIN {
+        default:
+                dbg = 1;
+                break;
+        } ARGEND
+        ifile = 1;
+        curfile = &bin;
+        if(*argv){
+                d = dirstat(*argv);
+                if(d == nil) {
+                        fprint(2, "dc: can't open file %s\n", *argv);
+                        exits("open");
+                }
+                if(d->mode & DMDIR) {
+                        fprint(2, "dc: file %s is a directory\n", *argv);
+                        exits("open");
+                }
+                free(d);
+                if((curfile = Bopen(*argv, OREAD)) == 0) {
+                        fprint(2,"dc: can't open file %s\n", *argv);
+                        exits("open");
+                }
+        }
+/*        dummy = malloc(0);  *//* prepare for garbage-collection */
+        scalptr = salloc(1);
+        sputc(scalptr,0);
+        basptr = salloc(1);
+        sputc(basptr,10);
+        obase=10;
+        logten=log2(10L);
+        ll=70;
+        fw=1;
+        fw1=0;
+        tenptr = salloc(1);
+        sputc(tenptr,10);
+        obase=10;
+        inbas = salloc(1);
+        sputc(inbas,10);
+        sqtemp = salloc(1);
+        sputc(sqtemp,2);
+        chptr = salloc(0);
+        strptr = salloc(0);
+        divxyz = salloc(0);
+        stkbeg = stkptr = &stack[0];
+        stkend = &stack[STKSZ];
+        stkerr = 0;
+        readptr = &readstk[0];
+        k=0;
+        sp = sptr = &symlst[0];
+        while(sptr < &symlst[TBLSZ]) {
+                sptr->next = ++sp;
+                sptr++;
+        }
+        sptr->next=0;
+        sfree = &symlst[0];
+}
+
+void
+pushp(Blk *p)
+{
+        if(stkptr == stkend) {
+                Bprint(&bout,"out of stack space\n");
+                return;
+        }
+        stkerr=0;
+        *++stkptr = p;
+        return;
+}
+
+Blk*
+pop(void)
+{
+        if(stkptr == stack) {
+                stkerr=1;
+                return(0);
+        }
+        return(*stkptr--);
+}
+
+Blk*
+readin(void)
+{
+        Blk *p, *q;
+        int dp, dpct, c;
+
+        dp = dpct=0;
+        p = salloc(0);
+        for(;;){
+                c = readc();
+                switch(c) {
+                case '.':
+                        if(dp != 0)
+                                goto gotnum;
+                        dp++;
+                        continue;
+                case '\\':
+                        readc();
+                        continue;
+                default:
+                        if(c >= 'A' && c <= 'F')
+                                c = c - 'A' + 10;
+                        else
+                        if(c >= '0' && c <= '9')
+                                c -= '0';
+                        else
+                                goto gotnum;
+                        if(dp != 0) {
+                                if(dpct >= 99)
+                                        continue;
+                                dpct++;
+                        }
+                        create(chptr);
+                        if(c != 0)
+                                sputc(chptr,c);
+                        q = mult(p,inbas);
+                        release(p);
+                        p = add(chptr,q);
+                        release(q);
+                }
+        }
+gotnum:
+        unreadc(c);
+        if(dp == 0) {
+                sputc(p,0);
+                return(p);
+        } else {
+                q = scale(p,dpct);
+                return(q);
+        }
+}
+
+/*
+ * returns pointer to struct with ct 0's & p
+ */
+Blk*
+add0(Blk *p, int ct)
+{
+        Blk *q, *t;
+
+        q = salloc(length(p)+(ct+1)/2);
+        while(ct>1) {
+                sputc(q,0);
+                ct -= 2;
+        }
+        rewind(p);
+        while(sfeof(p) == 0) {
+                sputc(q,sgetc(p));
+        }
+        release(p);
+        if(ct == 1) {
+                t = mult(tenptr,q);
+                release(q);
+                return(t);
+        }
+        return(q);
+}
+
+Blk*
+mult(Blk *p, Blk *q)
+{
+        Blk *mp, *mq, *mr;
+        int sign, offset, carry;
+        int cq, cp, mt, mcr;
+
+        offset = sign = 0;
+        fsfile(p);
+        mp = p;
+        if(sfbeg(p) == 0) {
+                if(sbackc(p)<0) {
+                        mp = copy(p,length(p));
+                        chsign(mp);
+                        sign = ~sign;
+                }
+        }
+        fsfile(q);
+        mq = q;
+        if(sfbeg(q) == 0){
+                if(sbackc(q)<0) {
+                        mq = copy(q,length(q));
+                        chsign(mq);
+                        sign = ~sign;
+                }
+        }
+        mr = salloc(length(mp)+length(mq));
+        zero(mr);
+        rewind(mq);
+        while(sfeof(mq) == 0) {
+                cq = sgetc(mq);
+                rewind(mp);
+                rewind(mr);
+                mr->rd += offset;
+                carry=0;
+                while(sfeof(mp) == 0) {
+                        cp = sgetc(mp);
+                        mcr = sfeof(mr)?0:slookc(mr);
+                        mt = cp*cq + carry + mcr;
+                        carry = mt/100;
+                        salterc(mr,mt%100);
+                }
+                offset++;
+                if(carry != 0) {
+                        mcr = sfeof(mr)?0:slookc(mr);
+                        salterc(mr,mcr+carry);
+                }
+        }
+        if(sign < 0) {
+                chsign(mr);
+        }
+        if(mp != p)
+                release(mp);
+        if(mq != q)
+                release(mq);
+        return(mr);
+}
+
+void
+chsign(Blk *p)
+{
+        int carry;
+        char ct;
+
+        carry=0;
+        rewind(p);
+        while(sfeof(p) == 0) {
+                ct=100-slookc(p)-carry;
+                carry=1;
+                if(ct>=100) {
+                        ct -= 100;
+                        carry=0;
+                }
+                salterc(p,ct);
+        }
+        if(carry != 0) {
+                sputc(p,-1);
+                fsfile(p);
+                backc(p);
+                ct = sbackc(p);
+                if(ct == 99 /*&& !sfbeg(p)*/) {
+                        truncate(p);
+                        sputc(p,-1);
+                }
+        } else{
+                fsfile(p);
+                ct = sbackc(p);
+                if(ct == 0)
+                        truncate(p);
+        }
+        return;
+}
+
+int
+readc(void)
+{
+loop:
+        if((readptr != &readstk[0]) && (*readptr != 0)) {
+                if(sfeof(*readptr) == 0)
+                        return(lastchar = sgetc(*readptr));
+                release(*readptr);
+                readptr--;
+                goto loop;
+        }
+        lastchar = Bgetc(curfile);
+        if(lastchar != -1)
+                return(lastchar);
+        if(readptr != &readptr[0]) {
+                readptr--;
+                if(*readptr == 0)
+                        curfile = &bin;
+                goto loop;
+        }
+        if(curfile != &bin) {
+                Bterm(curfile);
+                curfile = &bin;
+                goto loop;
+        }
+        exits(0);
+        return 0;        /* shut up ken */
+}
+
+void
+unreadc(char c)
+{
+
+        if((readptr != &readstk[0]) && (*readptr != 0)) {
+                sungetc(*readptr,c);
+        } else
+                Bungetc(curfile);
+        return;
+}
+
+void
+binop(char c)
+{
+        Blk *r;
+
+        r = 0;
+        switch(c) {
+        case '+':
+                r = add(arg1,arg2);
+                break;
+        case '*':
+                r = mult(arg1,arg2);
+                break;
+        case '/':
+                r = div(arg1,arg2);
+                break;
+        }
+        release(arg1);
+        release(arg2);
+        sputc(r,savk);
+        pushp(r);
+}
+
+void
+dcprint(Blk *hptr)
+{
+        Blk *p, *q, *dec;
+        int dig, dout, ct, sc;
+
+        rewind(hptr);
+        while(sfeof(hptr) == 0) {
+                if(sgetc(hptr)>99) {
+                        rewind(hptr);
+                        while(sfeof(hptr) == 0) {
+                                Bprint(&bout,"%c",sgetc(hptr));
+                        }
+                        Bprint(&bout,"\n");
+                        return;
+                }
+        }
+        fsfile(hptr);
+        sc = sbackc(hptr);
+        if(sfbeg(hptr) != 0) {
+                Bprint(&bout,"0\n");
+                return;
+        }
+        count = ll;
+        p = copy(hptr,length(hptr));
+        sclobber(p);
+        fsfile(p);
+        if(sbackc(p)<0) {
+                chsign(p);
+                OUTC('-');
+        }
+        if((obase == 0) || (obase == -1)) {
+                oneot(p,sc,'d');
+                return;
+        }
+        if(obase == 1) {
+                oneot(p,sc,'1');
+                return;
+        }
+        if(obase == 10) {
+                tenot(p,sc);
+                return;
+        }
+        /* sleazy hack to scale top of stack - divide by 1 */
+        pushp(p);
+        sputc(p, sc);
+        p=salloc(0);
+        create(p);
+        sputc(p, 1);
+        sputc(p, 0);
+        pushp(p);
+        if(dscale() != 0)
+                return;
+        p = div(arg1, arg2);
+        release(arg1);
+        release(arg2);
+        sc = savk;
+
+        create(strptr);
+        dig = logten*sc;
+        dout = ((dig/10) + dig) / logo;
+        dec = getdec(p,sc);
+        p = removc(p,sc);
+        while(length(p) != 0) {
+                q = div(p,basptr);
+                release(p);
+                p = q;
+                (*outdit)(rem,0);
+        }
+        release(p);
+        fsfile(strptr);
+        while(sfbeg(strptr) == 0)
+                OUTC(sbackc(strptr));
+        if(sc == 0) {
+                release(dec);
+                Bprint(&bout,"\n");
+                return;
+        }
+        create(strptr);
+        OUTC('.');
+        ct=0;
+        do {
+                q = mult(basptr,dec);
+                release(dec);
+                dec = getdec(q,sc);
+                p = removc(q,sc);
+                (*outdit)(p,1);
+        } while(++ct < dout);
+        release(dec);
+        rewind(strptr);
+        while(sfeof(strptr) == 0)
+                OUTC(sgetc(strptr));
+        Bprint(&bout,"\n");
+}
+
+Blk*
+getdec(Blk *p, int sc)
+{
+        int cc;
+        Blk *q, *t, *s;
+
+        rewind(p);
+        if(length(p)*2 < sc) {
+                q = copy(p,length(p));
+                return(q);
+        }
+        q = salloc(length(p));
+        while(sc >= 1) {
+                sputc(q,sgetc(p));
+                sc -= 2;
+        }
+        if(sc != 0) {
+                t = mult(q,tenptr);
+                s = salloc(cc = length(q));
+                release(q);
+                rewind(t);
+                while(cc-- > 0)
+                        sputc(s,sgetc(t));
+                sputc(s,0);
+                release(t);
+                t = div(s,tenptr);
+                release(s);
+                release(rem);
+                return(t);
+        }
+        return(q);
+}
+
+void
+tenot(Blk *p, int sc)
+{
+        int c, f;
+
+        fsfile(p);
+        f=0;
+        while((sfbeg(p) == 0) && ((p->rd-p->beg-1)*2 >= sc)) {
+                c = sbackc(p);
+                if((c<10) && (f == 1))
+                        Bprint(&bout,"0%d",c);
+                else
+                        Bprint(&bout,"%d",c);
+                f=1;
+                TEST2;
+        }
+        if(sc == 0) {
+                Bprint(&bout,"\n");
+                release(p);
+                return;
+        }
+        if((p->rd-p->beg)*2 > sc) {
+                c = sbackc(p);
+                Bprint(&bout,"%d.",c/10);
+                TEST2;
+                OUTC(c%10 +'0');
+                sc--;
+        } else {
+                OUTC('.');
+        }
+        while(sc>(p->rd-p->beg)*2) {
+                OUTC('0');
+                sc--;
+        }
+        while(sc > 1) {
+                c = sbackc(p);
+                if(c<10)
+                        Bprint(&bout,"0%d",c);
+                else
+                        Bprint(&bout,"%d",c);
+                sc -= 2;
+                TEST2;
+        }
+        if(sc == 1) {
+                OUTC(sbackc(p)/10 +'0');
+        }
+        Bprint(&bout,"\n");
+        release(p);
+}
+
+void
+oneot(Blk *p, int sc, char ch)
+{
+        Blk *q;
+
+        q = removc(p,sc);
+        create(strptr);
+        sputc(strptr,-1);
+        while(length(q)>0) {
+                p = add(strptr,q);
+                release(q);
+                q = p;
+                OUTC(ch);
+        }
+        release(q);
+        Bprint(&bout,"\n");
+}
+
+void
+hexot(Blk *p, int flg)
+{
+        int c;
+
+        USED(flg);
+        rewind(p);
+        if(sfeof(p) != 0) {
+                sputc(strptr,'0');
+                release(p);
+                return;
+        }
+        c = sgetc(p);
+        release(p);
+        if(c >= 16) {
+                Bprint(&bout,"hex digit > 16");
+                return;
+        }
+        sputc(strptr,c<10?c+'0':c-10+'a');
+}
+
+void
+bigot(Blk *p, int flg)
+{
+        Blk *t, *q;
+        int neg, l;
+
+        if(flg == 1) {
+                t = salloc(0);
+                l = 0;
+        } else {
+                t = strptr;
+                l = length(strptr)+fw-1;
+        }
+        neg=0;
+        if(length(p) != 0) {
+                fsfile(p);
+                if(sbackc(p)<0) {
+                        neg=1;
+                        chsign(p);
+                }
+                while(length(p) != 0) {
+                        q = div(p,tenptr);
+                        release(p);
+                        p = q;
+                        rewind(rem);
+                        sputc(t,sfeof(rem)?'0':sgetc(rem)+'0');
+                        release(rem);
+                }
+        }
+        release(p);
+        if(flg == 1) {
+                l = fw1-length(t);
+                if(neg != 0) {
+                        l--;
+                        sputc(strptr,'-');
+                }
+                fsfile(t);
+                while(l-- > 0)
+                        sputc(strptr,'0');
+                while(sfbeg(t) == 0)
+                        sputc(strptr,sbackc(t));
+                release(t);
+        } else {
+                l -= length(strptr);
+                while(l-- > 0)
+                        sputc(strptr,'0');
+                if(neg != 0) {
+                        sclobber(strptr);
+                        sputc(strptr,'-');
+                }
+        }
+        sputc(strptr,' ');
+}
+
+Blk*
+add(Blk *a1, Blk *a2)
+{
+        Blk *p;
+        int carry, n, size, c, n1, n2;
+
+        size = length(a1)>length(a2)?length(a1):length(a2);
+        p = salloc(size);
+        rewind(a1);
+        rewind(a2);
+        carry=0;
+        while(--size >= 0) {
+                n1 = sfeof(a1)?0:sgetc(a1);
+                n2 = sfeof(a2)?0:sgetc(a2);
+                n = n1 + n2 + carry;
+                if(n>=100) {
+                        carry=1;
+                        n -= 100;
+                } else
+                if(n<0) {
+                        carry = -1;
+                        n += 100;
+                } else
+                        carry = 0;
+                sputc(p,n);
+        }
+        if(carry != 0)
+                sputc(p,carry);
+        fsfile(p);
+        if(sfbeg(p) == 0) {
+                c = 0;
+                while(sfbeg(p) == 0 && (c = sbackc(p)) == 0)
+                        ;
+                if(c != 0)
+                        salterc(p,c);
+                truncate(p);
+        }
+        fsfile(p);
+        if(sfbeg(p) == 0 && sbackc(p) == -1) {
+                while((c = sbackc(p)) == 99) {
+                        if(c == -1)
+                                break;
+                }
+                skipc(p);
+                salterc(p,-1);
+                truncate(p);
+        }
+        return(p);
+}
+
+int
+eqk(void)
+{
+        Blk *p, *q;
+        int skp, skq;
+
+        p = pop();
+        EMPTYS;
+        q = pop();
+        EMPTYSR(p);
+        skp = sunputc(p);
+        skq = sunputc(q);
+        if(skp == skq) {
+                arg1=p;
+                arg2=q;
+                savk = skp;
+                return(0);
+        }
+        if(skp < skq) {
+                savk = skq;
+                p = add0(p,skq-skp);
+        } else {
+                savk = skp;
+                q = add0(q,skp-skq);
+        }
+        arg1=p;
+        arg2=q;
+        return(0);
+}
+
+Blk*
+removc(Blk *p, int n)
+{
+        Blk *q, *r;
+
+        rewind(p);
+        while(n>1) {
+                skipc(p);
+                n -= 2;
+        }
+        q = salloc(2);
+        while(sfeof(p) == 0)
+                sputc(q,sgetc(p));
+        if(n == 1) {
+                r = div(q,tenptr);
+                release(q);
+                release(rem);
+                q = r;
+        }
+        release(p);
+        return(q);
+}
+
+Blk*
+scalint(Blk *p)
+{
+        int n;
+
+        n = sunputc(p);
+        p = removc(p,n);
+        return(p);
+}
+
+Blk*
+scale(Blk *p, int n)
+{
+        Blk *q, *s, *t;
+
+        t = add0(p,n);
+        q = salloc(1);
+        sputc(q,n);
+        s = dcexp(inbas,q);
+        release(q);
+        q = div(t,s);
+        release(t);
+        release(s);
+        release(rem);
+        sputc(q,n);
+        return(q);
+}
+
+int
+subt(void)
+{
+        arg1=pop();
+        EMPTYS;
+        savk = sunputc(arg1);
+        chsign(arg1);
+        sputc(arg1,savk);
+        pushp(arg1);
+        if(eqk() != 0)
+                return(1);
+        binop('+');
+        return(0);
+}
+
+int
+command(void)
+{
+        char line[100], *sl;
+        int pid, p, c;
+
+        switch(c = readc()) {
+        case '<':
+                return(cond(NL));
+        case '>':
+                return(cond(NG));
+        case '=':
+                return(cond(NE));
+        default:
+                sl = line;
+                *sl++ = c;
+                while((c = readc()) != '\n')
+                        *sl++ = c;
+                *sl = 0;
+                if((pid = fork()) == 0) {
+                        execl("/bin/rc","rc","-c",line,0);
+                        exits("shell");
+                }
+                for(;;) {
+                        if((p = waitpid()) < 0)
+                                break;
+                        if(p== pid)
+                                break;
+                }
+                Bprint(&bout,"!\n");
+                return(0);
+        }
+}
+
+int
+cond(char c)
+{
+        Blk *p;
+        int cc;
+
+        if(subt() != 0)
+                return(1);
+        p = pop();
+        sclobber(p);
+        if(length(p) == 0) {
+                release(p);
+                if(c == '<' || c == '>' || c == NE) {
+                        getstk();
+                        return(0);
+                }
+                load();
+                return(1);
+        }
+        if(c == '='){
+                release(p);
+                getstk();
+                return(0);
+        }
+        if(c == NE) {
+                release(p);
+                load();
+                return(1);
+        }
+        fsfile(p);
+        cc = sbackc(p);
+        release(p);
+        if((cc<0 && (c == '<' || c == NG)) ||
+           (cc >0) && (c == '>' || c == NL)) {
+                getstk();
+                return(0);
+        }
+        load();
+        return(1);
+}
+
+void
+load(void)
+{
+        int c;
+        Blk *p, *q, *t, *s;
+
+        c = getstk() & 0377;
+        sptr = stable[c];
+        if(sptr != 0) {
+                p = sptr->val;
+                if(c >= ARRAYST) {
+                        q = salloc(length(p));
+                        rewind(p);
+                        while(sfeof(p) == 0) {
+                                s = dcgetwd(p);
+                                if(s == 0) {
+                                        putwd(q, (Blk*)0);
+                                } else {
+                                        t = copy(s,length(s));
+                                        putwd(q,t);
+                                }
+                        }
+                        pushp(q);
+                } else {
+                        q = copy(p,length(p));
+                        pushp(q);
+                }
+        } else {
+                q = salloc(1);
+                if(c <= LASTFUN) {
+                        Bprint(&bout,"function %c undefined\n",c+'a'-1);
+                        sputc(q,'c');
+                        sputc(q,'0');
+                        sputc(q,' ');
+                        sputc(q,'1');
+                        sputc(q,'Q');
+                }
+                else
+                        sputc(q,0);
+                pushp(q);
+        }
+}
+
+int
+log2(long n)
+{
+        int i;
+
+        if(n == 0)
+                return(0);
+        i=31;
+        if(n<0)
+                return(i);
+        while((n= n<<1) >0)
+                i--;
+        return i-1;
+}
+
+Blk*
+salloc(int size)
+{
+        Blk *hdr;
+        char *ptr;
+
+        all++;
+        lall++;
+        if(all - rel > active)
+                active = all - rel;
+        nbytes += size;
+        lbytes += size;
+        if(nbytes >maxsize)
+                maxsize = nbytes;
+        if(size > longest)
+                longest = size;
+        ptr = malloc((unsigned)size);
+        if(ptr == 0){
+                garbage("salloc");
+                if((ptr = malloc((unsigned)size)) == 0)
+                        ospace("salloc");
+        }
+        if((hdr = hfree) == 0)
+                hdr = morehd();
+        hfree = (Blk *)hdr->rd;
+        hdr->rd = hdr->wt = hdr->beg = ptr;
+        hdr->last = ptr+size;
+        return(hdr);
+}
+
+Blk*
+morehd(void)
+{
+        Blk *h, *kk;
+
+        headmor++;
+        nbytes += HEADSZ;
+        hfree = h = (Blk *)malloc(HEADSZ);
+        if(hfree == 0) {
+                garbage("morehd");
+                if((hfree = h = (Blk*)malloc(HEADSZ)) == 0)
+                        ospace("headers");
+        }
+        kk = h;
+        while(hrd = (char*)++kk;
+        (h-1)->rd=0;
+        return(hfree);
+}
+
+Blk*
+copy(Blk *hptr, int size)
+{
+        Blk *hdr;
+        unsigned sz;
+        char *ptr;
+
+        all++;
+        lall++;
+        lcopy++;
+        nbytes += size;
+        lbytes += size;
+        if(size > longest)
+                longest = size;
+        if(size > maxsize)
+                maxsize = size;
+        sz = length(hptr);
+        ptr = nalloc(hptr->beg, size);
+        if(ptr == 0) {
+                garbage("copy");
+                if((ptr = nalloc(hptr->beg, size)) == 0) {
+                        Bprint(&bout,"copy size %d\n",size);
+                        ospace("copy");
+                }
+        }
+        if((hdr = hfree) == 0)
+                hdr = morehd();
+        hfree = (Blk *)hdr->rd;
+        hdr->rd = hdr->beg = ptr;
+        hdr->last = ptr+size;
+        hdr->wt = ptr+sz;
+        ptr = hdr->wt;
+        while(ptrlast)
+                *ptr++ = '\0';
+        return(hdr);
+}
+
+void
+sdump(char *s1, Blk *hptr)
+{
+        char *p;
+
+        Bprint(&bout,"%s %lx rd %lx wt %lx beg %lx last %lx\n",
+                s1,hptr,hptr->rd,hptr->wt,hptr->beg,hptr->last);
+        p = hptr->beg;
+        while(p < hptr->wt)
+                Bprint(&bout,"%d ",*p++);
+        Bprint(&bout,"\n");
+}
+
+void
+seekc(Blk *hptr, int n)
+{
+        char *nn,*p;
+
+        nn = hptr->beg+n;
+        if(nn > hptr->last) {
+                nbytes += nn - hptr->last;
+                if(nbytes > maxsize)
+                        maxsize = nbytes;
+                lbytes += nn - hptr->last;
+                if(n > longest)
+                        longest = n;
+/*                free(hptr->beg); *//**/
+                p = realloc(hptr->beg, n);
+                if(p == 0) {
+/*                        hptr->beg = realloc(hptr->beg, hptr->last-hptr->beg);
+**                        garbage("seekc");
+**                        if((p = realloc(hptr->beg, n)) == 0)
+*/                                ospace("seekc");
+                }
+                hptr->beg = p;
+                hptr->wt = hptr->last = hptr->rd = p+n;
+                return;
+        }
+        hptr->rd = nn;
+        if(nn>hptr->wt)
+                hptr->wt = nn;
+}
+
+void
+salterwd(Blk *ahptr, Blk *n)
+{
+        Wblk *hptr;
+
+        hptr = (Wblk*)ahptr;
+        if(hptr->rdw == hptr->lastw)
+                more(ahptr);
+        *hptr->rdw++ = n;
+        if(hptr->rdw > hptr->wtw)
+                hptr->wtw = hptr->rdw;
+}
+
+void
+more(Blk *hptr)
+{
+        unsigned size;
+        char *p;
+
+        if((size=(hptr->last-hptr->beg)*2) == 0)
+                size=2;
+        nbytes += size/2;
+        if(nbytes > maxsize)
+                maxsize = nbytes;
+        if(size > longest)
+                longest = size;
+        lbytes += size/2;
+        lmore++;
+/*        free(hptr->beg);*//**/
+        p = realloc(hptr->beg, size);
+
+        if(p == 0) {
+/*                hptr->beg = realloc(hptr->beg, (hptr->last-hptr->beg));
+**                garbage("more");
+**                if((p = realloc(hptr->beg,size)) == 0)
+*/                        ospace("more");
+        }
+        hptr->rd = p + (hptr->rd - hptr->beg);
+        hptr->wt = p + (hptr->wt - hptr->beg);
+        hptr->beg = p;
+        hptr->last = p+size;
+}
+
+void
+ospace(char *s)
+{
+        Bprint(&bout,"out of space: %s\n",s);
+        Bprint(&bout,"all %ld rel %ld headmor %ld\n",all,rel,headmor);
+        Bprint(&bout,"nbytes %ld\n",nbytes);
+        sdump("stk",*stkptr);
+        abort();
+}
+
+void
+garbage(char *s)
+{
+        USED(s);
+}
+
+void
+release(Blk *p)
+{
+        rel++;
+        lrel++;
+        nbytes -= p->last - p->beg;
+        p->rd = (char*)hfree;
+        hfree = p;
+        free(p->beg);
+}
+
+Blk*
+dcgetwd(Blk *p)
+{
+        Wblk *wp;
+
+        wp = (Wblk*)p;
+        if(wp->rdw == wp->wtw)
+                return(0);
+        return(*wp->rdw++);
+}
+
+void
+putwd(Blk *p, Blk *c)
+{
+        Wblk *wp;
+
+        wp = (Wblk*)p;
+        if(wp->wtw == wp->lastw)
+                more(p);
+        *wp->wtw++ = c;
+}
+
+Blk*
+lookwd(Blk *p)
+{
+        Wblk *wp;
+
+        wp = (Wblk*)p;
+        if(wp->rdw == wp->wtw)
+                return(0);
+        return(*wp->rdw);
+}
+
+char*
+nalloc(char *p, unsigned nbytes)
+{
+        char *q, *r;
+
+        q = r = malloc(nbytes);
+        if(q==0)
+                return(0);
+        while(nbytes--)
+                *q++ = *p++;
+        return(r);
+}
+
+int
+getstk(void)
+{
+        int n;
+        uchar c;
+
+        c = readc();
+        if(c != '<')
+                return c;
+        n = 0;
+        while(1) {
+                c = readc();
+                if(c == '>')
+                        break;
+                n = n*10+c-'0';
+        }
+        return n;
+}
diff --git a/src/cmd/dd.c b/src/cmd/dd.c
t@@ -0,0 +1,660 @@
+#include 
+#include 
+
+#define        BIG        2147483647
+#define        LCASE        (1<<0)
+#define        UCASE        (1<<1)
+#define        SWAB        (1<<2)
+#define NERR        (1<<3)
+#define SYNC        (1<<4)
+int        cflag;
+int        fflag;
+char        *string;
+char        *ifile;
+char        *ofile;
+char        *ibuf;
+char        *obuf;
+vlong        skip;
+vlong        oseekn;
+vlong        iseekn;
+vlong        count;
+long        files        = 1;
+long        ibs        = 512;
+long        obs        = 512;
+long        bs;
+long        cbs;
+long        ibc;
+long        obc;
+long        cbc;
+long        nifr;
+long        nipr;
+long        nofr;
+long        nopr;
+long        ntrunc;
+int dotrunc = 1;
+int        ibf;
+int        obf;
+char        *op;
+int        nspace;
+uchar        etoa[256];
+uchar        atoe[256];
+uchar        atoibm[256];
+
+void        flsh(void);
+int        match(char *s);
+vlong        number(long big);
+void        cnull(int cc);
+void        null(int c);
+void        ascii(int cc);
+void        unblock(int cc);
+void        ebcdic(int cc);
+void        ibm(int cc);
+void        block(int cc);
+void        term(void);
+void        stats(void);
+
+#define        iskey(s)        ((key[0] == '-') && (strcmp(key+1, s) == 0))
+
+void
+main(int argc, char *argv[])
+{
+        void (*conv)(int);
+        char *ip;
+        char *key;
+        int a, c;
+
+        conv = null;
+        for(c=1; c= argc){
+                        fprint(2, "dd: arg %s needs a value\n", key);
+                        exits("arg");
+                }
+                string = argv[c];
+                if(iskey("ibs")) {
+                        ibs = number(BIG);
+                        continue;
+                }
+                if(iskey("obs")) {
+                        obs = number(BIG);
+                        continue;
+                }
+                if(iskey("cbs")) {
+                        cbs = number(BIG);
+                        continue;
+                }
+                if(iskey("bs")) {
+                        bs = number(BIG);
+                        continue;
+                }
+                if(iskey("if")) {
+                        ifile = string;
+                        continue;
+                }
+                if(iskey("of")) {
+                        ofile = string;
+                        continue;
+                }
+                if(iskey("trunc")) {
+                        dotrunc = number(BIG);
+                        continue;
+                }
+                if(iskey("skip")) {
+                        skip = number(BIG);
+                        continue;
+                }
+                if(iskey("seek") || iskey("oseek")) {
+                        oseekn = number(BIG);
+                        continue;
+                }
+                if(iskey("iseek")) {
+                        iseekn = number(BIG);
+                        continue;
+                }
+                if(iskey("count")) {
+                        count = number(BIG);
+                        continue;
+                }
+                if(iskey("files")) {
+                        files = number(BIG);
+                        continue;
+                }
+                if(iskey("conv")) {
+                cloop:
+                        if(match(","))
+                                goto cloop;
+                        if(*string == '\0')
+                                continue;
+                        if(match("ebcdic")) {
+                                conv = ebcdic;
+                                goto cloop;
+                        }
+                        if(match("ibm")) {
+                                conv = ibm;
+                                goto cloop;
+                        }
+                        if(match("ascii")) {
+                                conv = ascii;
+                                goto cloop;
+                        }
+                        if(match("block")) {
+                                conv = block;
+                                goto cloop;
+                        }
+                        if(match("unblock")) {
+                                conv = unblock;
+                                goto cloop;
+                        }
+                        if(match("lcase")) {
+                                cflag |= LCASE;
+                                goto cloop;
+                        }
+                        if(match("ucase")) {
+                                cflag |= UCASE;
+                                goto cloop;
+                        }
+                        if(match("swab")) {
+                                cflag |= SWAB;
+                                goto cloop;
+                        }
+                        if(match("noerror")) {
+                                cflag |= NERR;
+                                goto cloop;
+                        }
+                        if(match("sync")) {
+                                cflag |= SYNC;
+                                goto cloop;
+                        }
+                }
+                fprint(2, "dd: bad arg: %s\n", key);
+                exits("arg");
+        }
+        if(conv == null && cflag&(LCASE|UCASE))
+                conv = cnull;
+        if(ifile)
+                ibf = open(ifile, 0);
+        else
+                ibf = dup(0, -1);
+        if(ibf < 0) {
+                fprint(2, "dd: open %s: %r\n", ifile);
+                exits("open");
+        }
+        if(ofile){
+                if(dotrunc)
+                        obf = create(ofile, 1, 0664);
+                else
+                        obf = open(ofile, 1);
+                if(obf < 0) {
+                        fprint(2, "dd: create %s: %r\n", ofile);
+                        exits("create");
+                }
+        }else{
+                obf = dup(1, -1);
+                if(obf < 0) {
+                        fprint(2, "dd: can't dup file descriptor: %s: %r\n", ofile);
+                        exits("dup");
+                }
+        }
+        if(bs)
+                ibs = obs = bs;
+        if(ibs == obs && conv == null)
+                fflag++;
+        if(ibs == 0 || obs == 0) {
+                fprint(2, "dd: counts: cannot be zero\n");
+                exits("counts");
+        }
+        ibuf = sbrk(ibs);
+        if(fflag)
+                obuf = ibuf;
+        else
+                obuf = sbrk(obs);
+        sbrk(64);        /* For good measure */
+        if(ibuf == (char *)-1 || obuf == (char *)-1) {
+                fprint(2, "dd: not enough memory: %r\n");
+                exits("memory");
+        }
+        ibc = 0;
+        obc = 0;
+        cbc = 0;
+        op = obuf;
+
+/*
+        if(signal(SIGINT, SIG_IGN) != SIG_IGN)
+                signal(SIGINT, term);
+*/
+        seek(obf, obs*oseekn, 1);
+        seek(ibf, ibs*iseekn, 1);
+        while(skip) {
+                read(ibf, ibuf, ibs);
+                skip--;
+        }
+
+        ip = 0;
+loop:
+        if(ibc-- == 0) {
+                ibc = 0;
+                if(count==0 || nifr+nipr!=count) {
+                        if(cflag&(NERR|SYNC))
+                        for(ip=ibuf+ibs; ip>ibuf;)
+                                *--ip = 0;
+                        ibc = read(ibf, ibuf, ibs);
+                }
+                if(ibc == -1) {
+                        perror("read");
+                        if((cflag&NERR) == 0) {
+                                flsh();
+                                term();
+                        }
+                        ibc = 0;
+                        for(c=0; c>1) & ~1;
+                if(cflag&SWAB && c)
+                do {
+                        a = *ip++;
+                        ip[-1] = *ip;
+                        *ip++ = a;
+                } while(--c);
+                ip = ibuf;
+                if(fflag) {
+                        obc = ibc;
+                        flsh();
+                        ibc = 0;
+                }
+                goto loop;
+        }
+        c = 0;
+        c |= *ip++;
+        c &= 0377;
+        (*conv)(c);
+        goto loop;
+}
+
+void
+flsh(void)
+{
+        int c;
+
+        if(obc) {
+                c = write(obf, obuf, obc);
+                if(c != obc) {
+                        if(c > 0)
+                                ++nopr;
+                        perror("write");
+                        term();
+                }
+                if(obc == obs)
+                        nofr++;
+                else
+                        nopr++;
+                obc = 0;
+        }
+}
+
+int
+match(char *s)
+{
+        char *cs;
+
+        cs = string;
+        while(*cs++ == *s)
+                if(*s++ == '\0')
+                        goto true;
+        if(*s != '\0')
+                return 0;
+
+true:
+        cs--;
+        string = cs;
+        return 1;
+}
+
+vlong
+number(long big)
+{
+        char *cs;
+        vlong n;
+
+        cs = string;
+        n = 0;
+        while(*cs >= '0' && *cs <= '9')
+                n = n*10 + *cs++ - '0';
+        for(;;)
+        switch(*cs++) {
+
+        case 'k':
+                n *= 1024;
+                continue;
+
+/*        case 'w':
+                n *= sizeof(int);
+                continue;
+*/
+
+        case 'b':
+                n *= 512;
+                continue;
+
+/*        case '*':*/
+        case 'x':
+                string = cs;
+                n *= number(BIG);
+
+        case '\0':
+                if(n>=big || n<0) {
+                        fprint(2, "dd: argument %lld out of range\n", n);
+                        exits("range");
+                }
+                return n;
+        }
+        /* never gets here */
+}
+
+void
+cnull(int cc)
+{
+        int c;
+
+        c = cc;
+        if((cflag&UCASE) && c>='a' && c<='z')
+                c += 'A'-'a';
+        if((cflag&LCASE) && c>='A' && c<='Z')
+                c += 'a'-'A';
+        null(c);
+}
+
+void
+null(int c)
+{
+
+        *op = c;
+        op++;
+        if(++obc >= obs) {
+                flsh();
+                op = obuf;
+        }
+}
+
+void
+ascii(int cc)
+{
+        int c;
+
+        c = etoa[cc];
+        if(cbs == 0) {
+                cnull(c);
+                return;
+        }
+        if(c == ' ') {
+                nspace++;
+                goto out;
+        }
+        while(nspace > 0) {
+                null(' ');
+                nspace--;
+        }
+        cnull(c);
+
+out:
+        if(++cbc >= cbs) {
+                null('\n');
+                cbc = 0;
+                nspace = 0;
+        }
+}
+
+void
+unblock(int cc)
+{
+        int c;
+
+        c = cc & 0377;
+        if(cbs == 0) {
+                cnull(c);
+                return;
+        }
+        if(c == ' ') {
+                nspace++;
+                goto out;
+        }
+        while(nspace > 0) {
+                null(' ');
+                nspace--;
+        }
+        cnull(c);
+
+out:
+        if(++cbc >= cbs) {
+                null('\n');
+                cbc = 0;
+                nspace = 0;
+        }
+}
+
+void
+ebcdic(int cc)
+{
+        int c;
+
+        c = cc;
+        if(cflag&UCASE && c>='a' && c<='z')
+                c += 'A'-'a';
+        if(cflag&LCASE && c>='A' && c<='Z')
+                c += 'a'-'A';
+        c = atoe[c];
+        if(cbs == 0) {
+                null(c);
+                return;
+        }
+        if(cc == '\n') {
+                while(cbc < cbs) {
+                        null(atoe[' ']);
+                        cbc++;
+                }
+                cbc = 0;
+                return;
+        }
+        if(cbc == cbs)
+                ntrunc++;
+        cbc++;
+        if(cbc <= cbs)
+                null(c);
+}
+
+void
+ibm(int cc)
+{
+        int c;
+
+        c = cc;
+        if(cflag&UCASE && c>='a' && c<='z')
+                c += 'A'-'a';
+        if(cflag&LCASE && c>='A' && c<='Z')
+                c += 'a'-'A';
+        c = atoibm[c] & 0377;
+        if(cbs == 0) {
+                null(c);
+                return;
+        }
+        if(cc == '\n') {
+                while(cbc < cbs) {
+                        null(atoibm[' ']);
+                        cbc++;
+                }
+                cbc = 0;
+                return;
+        }
+        if(cbc == cbs)
+                ntrunc++;
+        cbc++;
+        if(cbc <= cbs)
+                null(c);
+}
+
+void
+block(int cc)
+{
+        int c;
+
+        c = cc;
+        if(cflag&UCASE && c>='a' && c<='z')
+                c += 'A'-'a';
+        if(cflag&LCASE && c>='A' && c<='Z')
+                c += 'a'-'A';
+        c &= 0377;
+        if(cbs == 0) {
+                null(c);
+                return;
+        }
+        if(cc == '\n') {
+                while(cbc < cbs) {
+                        null(' ');
+                        cbc++;
+                }
+                cbc = 0;
+                return;
+        }
+        if(cbc == cbs)
+                ntrunc++;
+        cbc++;
+        if(cbc <= cbs)
+                null(c);
+}
+
+void
+term(void)
+{
+
+        stats();
+        exits(0);
+}
+
+void
+stats(void)
+{
+
+        fprint(2, "%lud+%lud records in\n", nifr, nipr);
+        fprint(2, "%lud+%lud records out\n", nofr, nopr);
+        if(ntrunc)
+                fprint(2, "%lud truncated records\n", ntrunc);
+}
+
+uchar        etoa[] =
+{
+        0000,0001,0002,0003,0234,0011,0206,0177,
+        0227,0215,0216,0013,0014,0015,0016,0017,
+        0020,0021,0022,0023,0235,0205,0010,0207,
+        0030,0031,0222,0217,0034,0035,0036,0037,
+        0200,0201,0202,0203,0204,0012,0027,0033,
+        0210,0211,0212,0213,0214,0005,0006,0007,
+        0220,0221,0026,0223,0224,0225,0226,0004,
+        0230,0231,0232,0233,0024,0025,0236,0032,
+        0040,0240,0241,0242,0243,0244,0245,0246,
+        0247,0250,0133,0056,0074,0050,0053,0041,
+        0046,0251,0252,0253,0254,0255,0256,0257,
+        0260,0261,0135,0044,0052,0051,0073,0136,
+        0055,0057,0262,0263,0264,0265,0266,0267,
+        0270,0271,0174,0054,0045,0137,0076,0077,
+        0272,0273,0274,0275,0276,0277,0300,0301,
+        0302,0140,0072,0043,0100,0047,0075,0042,
+        0303,0141,0142,0143,0144,0145,0146,0147,
+        0150,0151,0304,0305,0306,0307,0310,0311,
+        0312,0152,0153,0154,0155,0156,0157,0160,
+        0161,0162,0313,0314,0315,0316,0317,0320,
+        0321,0176,0163,0164,0165,0166,0167,0170,
+        0171,0172,0322,0323,0324,0325,0326,0327,
+        0330,0331,0332,0333,0334,0335,0336,0337,
+        0340,0341,0342,0343,0344,0345,0346,0347,
+        0173,0101,0102,0103,0104,0105,0106,0107,
+        0110,0111,0350,0351,0352,0353,0354,0355,
+        0175,0112,0113,0114,0115,0116,0117,0120,
+        0121,0122,0356,0357,0360,0361,0362,0363,
+        0134,0237,0123,0124,0125,0126,0127,0130,
+        0131,0132,0364,0365,0366,0367,0370,0371,
+        0060,0061,0062,0063,0064,0065,0066,0067,
+        0070,0071,0372,0373,0374,0375,0376,0377,
+};
+uchar        atoe[] =
+{
+        0000,0001,0002,0003,0067,0055,0056,0057,
+        0026,0005,0045,0013,0014,0015,0016,0017,
+        0020,0021,0022,0023,0074,0075,0062,0046,
+        0030,0031,0077,0047,0034,0035,0036,0037,
+        0100,0117,0177,0173,0133,0154,0120,0175,
+        0115,0135,0134,0116,0153,0140,0113,0141,
+        0360,0361,0362,0363,0364,0365,0366,0367,
+        0370,0371,0172,0136,0114,0176,0156,0157,
+        0174,0301,0302,0303,0304,0305,0306,0307,
+        0310,0311,0321,0322,0323,0324,0325,0326,
+        0327,0330,0331,0342,0343,0344,0345,0346,
+        0347,0350,0351,0112,0340,0132,0137,0155,
+        0171,0201,0202,0203,0204,0205,0206,0207,
+        0210,0211,0221,0222,0223,0224,0225,0226,
+        0227,0230,0231,0242,0243,0244,0245,0246,
+        0247,0250,0251,0300,0152,0320,0241,0007,
+        0040,0041,0042,0043,0044,0025,0006,0027,
+        0050,0051,0052,0053,0054,0011,0012,0033,
+        0060,0061,0032,0063,0064,0065,0066,0010,
+        0070,0071,0072,0073,0004,0024,0076,0341,
+        0101,0102,0103,0104,0105,0106,0107,0110,
+        0111,0121,0122,0123,0124,0125,0126,0127,
+        0130,0131,0142,0143,0144,0145,0146,0147,
+        0150,0151,0160,0161,0162,0163,0164,0165,
+        0166,0167,0170,0200,0212,0213,0214,0215,
+        0216,0217,0220,0232,0233,0234,0235,0236,
+        0237,0240,0252,0253,0254,0255,0256,0257,
+        0260,0261,0262,0263,0264,0265,0266,0267,
+        0270,0271,0272,0273,0274,0275,0276,0277,
+        0312,0313,0314,0315,0316,0317,0332,0333,
+        0334,0335,0336,0337,0352,0353,0354,0355,
+        0356,0357,0372,0373,0374,0375,0376,0377,
+};
+uchar        atoibm[] =
+{
+        0000,0001,0002,0003,0067,0055,0056,0057,
+        0026,0005,0045,0013,0014,0015,0016,0017,
+        0020,0021,0022,0023,0074,0075,0062,0046,
+        0030,0031,0077,0047,0034,0035,0036,0037,
+        0100,0132,0177,0173,0133,0154,0120,0175,
+        0115,0135,0134,0116,0153,0140,0113,0141,
+        0360,0361,0362,0363,0364,0365,0366,0367,
+        0370,0371,0172,0136,0114,0176,0156,0157,
+        0174,0301,0302,0303,0304,0305,0306,0307,
+        0310,0311,0321,0322,0323,0324,0325,0326,
+        0327,0330,0331,0342,0343,0344,0345,0346,
+        0347,0350,0351,0255,0340,0275,0137,0155,
+        0171,0201,0202,0203,0204,0205,0206,0207,
+        0210,0211,0221,0222,0223,0224,0225,0226,
+        0227,0230,0231,0242,0243,0244,0245,0246,
+        0247,0250,0251,0300,0117,0320,0241,0007,
+        0040,0041,0042,0043,0044,0025,0006,0027,
+        0050,0051,0052,0053,0054,0011,0012,0033,
+        0060,0061,0032,0063,0064,0065,0066,0010,
+        0070,0071,0072,0073,0004,0024,0076,0341,
+        0101,0102,0103,0104,0105,0106,0107,0110,
+        0111,0121,0122,0123,0124,0125,0126,0127,
+        0130,0131,0142,0143,0144,0145,0146,0147,
+        0150,0151,0160,0161,0162,0163,0164,0165,
+        0166,0167,0170,0200,0212,0213,0214,0215,
+        0216,0217,0220,0232,0233,0234,0235,0236,
+        0237,0240,0252,0253,0254,0255,0256,0257,
+        0260,0261,0262,0263,0264,0265,0266,0267,
+        0270,0271,0272,0273,0274,0275,0276,0277,
+        0312,0313,0314,0315,0316,0317,0332,0333,
+        0334,0335,0336,0337,0352,0353,0354,0355,
+        0356,0357,0372,0373,0374,0375,0376,0377,
+};
diff --git a/src/cmd/deroff.c b/src/cmd/deroff.c
t@@ -0,0 +1,969 @@
+#include 
+#include 
+#include 
+
+/*
+ * Deroff command -- strip troff, eqn, and tbl sequences from
+ * a file.  Has three flags argument, -w, to cause output one word per line
+ * rather than in the original format.
+ * -mm (or -ms) causes the corresponding macro's to be interpreted
+ * so that just sentences are output
+ * -ml  also gets rid of lists.
+ * -i causes deroff to ignore .so and .nx commands.
+ * Deroff follows .so and .nx commands, removes contents of macro
+ * definitions, equations (both .EQ ... .EN and $...$),
+ * Tbl command sequences, and Troff backslash vconstructions.
+ * 
+ * All input is through the C macro; the most recently read character is in c.
+ */
+
+/*
+#define        C        ((c = Bgetrune(infile)) < 0?\
+                        eof():\
+                        ((c == ldelim) && (filesp == files)?\
+                                skeqn():\
+                                (c == '\n'?\
+                                        (linect++,c):\
+                                                c)))
+
+#define        C1        ((c = Bgetrune(infile)) == Beof?\
+                        eof():\
+                        (c == '\n'?\
+                                (linect++,c):\
+                                c))
+*/
+
+/* lose those macros! */
+#define        C        fC()
+#define        C1        fC1()
+
+#define        SKIP        while(C != '\n') 
+#define SKIP1        while(C1 != '\n')
+#define SKIP_TO_COM                SKIP;\
+                                SKIP;\
+                                pc=c;\
+                                while(C != '.' || pc != '\n' || C > 'Z')\
+                                                pc=c
+
+#define YES                1
+#define NO                0
+#define MS                0
+#define MM                1
+#define ONE                1
+#define TWO                2
+
+#define NOCHAR                -2
+#define        EXTENDED        -1                /* All runes above 0x7F */
+#define SPECIAL                0
+#define APOS                1
+#define PUNCT                2
+#define DIGIT                3
+#define LETTER                4
+
+
+int        linect        = 0;
+int        wordflag= NO;
+int        underscoreflag = NO;
+int        msflag        = NO;
+int        iflag        = NO;
+int        mac        = MM;
+int        disp        = 0;
+int        inmacro        = NO;
+int        intable        = NO;
+int        eqnflag        = 0;
+
+#define        MAX_ASCII        0X80
+
+char        chars[MAX_ASCII];        /* SPECIAL, PUNCT, APOS, DIGIT, or LETTER */
+
+Rune        line[30000];
+Rune*        lp;
+
+long        c;
+long        pc;
+int        ldelim        = NOCHAR;
+int        rdelim        = NOCHAR;
+
+
+char**        argv;
+
+char        fname[50];
+Biobuf*        files[15];
+Biobuf**filesp;
+Biobuf*        infile;
+char*        devnull        = "/dev/null";
+Biobuf        *infile;
+Biobuf        bout;
+
+long        skeqn(void);
+Biobuf*        opn(char *p);
+int        eof(void);
+int        charclass(int);
+void        getfname(void);
+void        fatal(char *s, char *p);
+void        usage(void);
+void        work(void);
+void        putmac(Rune *rp, int vconst);
+void        regline(int macline, int vconst);
+void        putwords(void);
+void        comline(void);
+void        macro(void);
+void        eqn(void);
+void        tbl(void);
+void        stbl(void);
+void        sdis(char a1, char a2);
+void        sce(void);
+void        backsl(void);
+char*        copys(char *s);
+void        refer(int c1);
+void        inpic(void);
+
+int
+fC(void)
+{
+        c = Bgetrune(infile);
+        if(c < 0)
+                return eof();
+        if(c == ldelim && filesp == files)
+                return skeqn();
+        if(c == '\n')
+                linect++;
+        return c;
+}
+
+int
+fC1(void)
+{
+        c = Bgetrune(infile);
+        if(c == Beof)
+                return eof();
+        if(c == '\n')
+                linect++;
+        return c;
+}
+
+void
+main(int argc, char *av[])
+{
+        int i;
+        char *f;
+
+        argv = av;
+        Binit(&bout, 1, OWRITE);
+        ARGBEGIN{
+        case 'w':
+                wordflag = YES;
+                break;
+        case '_':
+                wordflag = YES;
+                underscoreflag = YES;
+                break;
+        case 'm':
+                msflag = YES;
+                if(f = ARGF())
+                        switch(*f)
+                        {
+                        case 'm':        mac = MM; break;
+                        case 's':        mac = MS; break;
+                        case 'l':        disp = 1; break;
+                        default:        usage();
+                        }
+                else
+                        usage();
+                break;
+        case 'i':
+                iflag = YES;
+                break;
+        default:
+                usage();
+        }ARGEND
+        if(*argv)
+                infile = opn(*argv++);
+        else{
+                infile = malloc(sizeof(Biobuf));
+                Binit(infile, 0, OREAD);
+        }
+        files[0] = infile;
+        filesp = &files[0];
+
+        for(i='a'; i<='z' ; ++i)
+                chars[i] = LETTER;
+        for(i='A'; i<='Z'; ++i)
+                chars[i] = LETTER;
+        for(i='0'; i<='9'; ++i)
+                chars[i] = DIGIT;
+        chars['\''] = APOS;
+        chars['&'] = APOS;
+        chars['\b'] = APOS;
+        chars['.'] = PUNCT;
+        chars[','] = PUNCT;
+        chars[';'] = PUNCT;
+        chars['?'] = PUNCT;
+        chars[':'] = PUNCT;
+        work();
+}
+
+long
+skeqn(void)
+{
+        while(C1 != rdelim)
+                if(c == '\\')
+                        c = C1;
+                else if(c == '"')
+                        while(C1 != '"')
+                                if(c == '\\') 
+                                        C1;
+        if (msflag)
+                eqnflag = 1;
+        return(c = ' ');
+}
+
+Biobuf*
+opn(char *p)
+{
+        Biobuf *fd;
+
+        while ((fd = Bopen(p, OREAD)) == 0) {
+                if(msflag || p == devnull)
+                        fatal("Cannot open file %s - quitting\n", p);
+                else {
+                        fprint(2, "Deroff: Cannot open file %s - continuing\n", p);
+                        p = devnull;
+                }
+        }
+        linect = 0;
+        return(fd);
+}
+
+int
+eof(void)
+{
+        if(Bfildes(infile) != 0)
+                Bterm(infile);
+        if(filesp > files)
+                infile = *--filesp;
+        else
+        if(*argv)
+                infile = opn(*argv++);
+        else
+                exits(0);
+        return(C);
+}
+
+void
+getfname(void)
+{
+        char *p;
+        Rune r;
+        Dir *dir;
+        struct chain
+        { 
+                struct        chain*        nextp; 
+                char*        datap; 
+        } *q;
+
+        static struct chain *namechain= 0;
+
+        while(C == ' ')
+                ;
+        for(p = fname; (r=c) != '\n' && r != ' ' && r != '\t' && r != '\\'; C)
+                p += runetochar(p, &r);
+        *p = '\0';
+        while(c != '\n')
+                C;
+        if(!strcmp(fname, "/sys/lib/tmac/tmac.cs")
+                        || !strcmp(fname, "/sys/lib/tmac/tmac.s")) {
+                fname[0] = '\0';
+                return;
+        }
+        dir = dirstat(fname);
+        if(dir!=nil && ((dir->mode & DMDIR) || dir->type != 'M')) {
+                free(dir);
+                fname[0] = '\0';
+                return;
+        }
+        free(dir);
+        /*
+         * see if this name has already been used
+         */
+
+        for(q = namechain; q; q = q->nextp)
+                if( !strcmp(fname, q->datap)) {
+                        fname[0] = '\0';
+                        return;
+                }
+        q = (struct chain*)malloc(sizeof(struct chain));
+        q->nextp = namechain;
+        q->datap = copys(fname);
+        namechain = q;
+}
+
+void
+usage(void)
+{
+        fprint(2,"usage: deroff [-nw_pi] [-m (m s l)] [file ...] \n");
+        exits("usage");
+}
+
+void
+fatal(char *s, char *p)
+{
+        fprint(2, "deroff: ");
+        fprint(2, s, p);
+        exits(s);
+}
+
+void
+work(void)
+{
+
+        for(;;) {
+                eqnflag = 0;
+                if(C == '.'  ||  c == '\'')
+                        comline();
+                else
+                        regline(NO, TWO);
+        }
+}
+
+void
+regline(int macline, int vconst)
+{
+        line[0] = c;
+        lp = line;
+        for(;;) {
+                if(c == '\\') {
+                        *lp = ' ';
+                        backsl();
+                        if(c == '%')        /* no blank for hyphenation char */
+                                lp--;
+                }
+                if(c == '\n')
+                        break;
+                if(intable && c=='T') {
+                        *++lp = C;
+                        if(c=='{' || c=='}') {
+                                lp[-1] = ' ';
+                                *lp = C;
+                        }
+                } else {
+                        if(msflag == 1 && eqnflag == 1) {
+                                eqnflag = 0;
+                                *++lp = 'x';
+                        }
+                        *++lp = C;
+                }
+        }
+        *lp = '\0';
+        if(lp != line) {
+                if(wordflag)
+                        putwords();
+                else
+                if(macline)
+                        putmac(line,vconst);
+                else
+                        Bprint(&bout, "%S\n", line);
+        }
+}
+
+void
+putmac(Rune *rp, int vconst)
+{
+        Rune *t;
+        int found;
+        Rune last;
+
+        found = 0;
+        last = 0;
+        while(*rp) {
+                while(*rp == ' ' || *rp == '\t')
+                        Bputrune(&bout, *rp++);
+                for(t = rp; *t != ' ' && *t != '\t' && *t != '\0'; t++)
+                        ;
+                if(*rp == '\"')
+                        rp++;
+                if(t > rp+vconst && charclass(*rp) == LETTER
+                                && charclass(rp[1]) == LETTER) {
+                        while(rp < t)
+                                if(*rp == '\"')
+                                        rp++;
+                                else
+                                        Bputrune(&bout, *rp++);
+                        last = t[-1];
+                        found++;
+                } else
+                if(found && charclass(*rp) == PUNCT && rp[1] == '\0')
+                        Bputrune(&bout, *rp++);
+                else {
+                        last = t[-1];
+                        rp = t;
+                }
+        }
+        Bputc(&bout, '\n');
+        if(msflag && charclass(last) == PUNCT)
+                Bprint(&bout, " %C\n", last);
+}
+
+/*
+ * break into words for -w option
+ */
+void
+putwords(void)
+{
+        Rune *p, *p1;
+        int i, nlet;
+
+
+        for(p1 = line;;) {
+                /*
+                 * skip initial specials ampersands and apostrophes
+                 */
+                while((i = charclass(*p1)) != EXTENDED && i < DIGIT)
+                        if(*p1++ == '\0')
+                                return;
+                nlet = 0;
+                for(p = p1; (i = charclass(*p)) != SPECIAL || (underscoreflag && *p=='_'); p++)
+                        if(i == LETTER || (underscoreflag && *p == '_'))
+                                nlet++;
+                /*
+                 * MDM definition of word
+                 */
+                if(nlet > 1) {
+                        /*
+                         * delete trailing ampersands and apostrophes
+                         */
+                        while(*--p == '\'' || *p == '&'
+                                           || charclass(*p) == PUNCT)
+                                ;
+                        while(p1 <= p)
+                                Bputrune(&bout, *p1++);
+                        Bputc(&bout, '\n');
+                } else
+                        p1 = p;
+        }
+}
+
+void
+comline(void)
+{
+        long c1, c2;
+
+        while(C==' ' || c=='\t')
+                ;
+comx:
+        if((c1=c) == '\n')
+                return;
+        c2 = C;
+        if(c1=='.' && c2!='.')
+                inmacro = NO;
+        if(msflag && c1 == '['){
+                refer(c2);
+                return;
+        }
+        if(c2 == '\n')
+                return;
+        if(c1 == '\\' && c2 == '\"')
+                SKIP;
+        else
+        if (filesp==files && c1=='E' && c2=='Q')
+                        eqn();
+        else
+        if(filesp==files && c1=='T' && (c2=='S' || c2=='C' || c2=='&')) {
+                if(msflag)
+                        stbl(); 
+                else
+                        tbl();
+        }
+        else
+        if(c1=='T' && c2=='E')
+                intable = NO;
+        else if (!inmacro &&
+                        ((c1 == 'd' && c2 == 'e') ||
+                            (c1 == 'i' && c2 == 'g') ||
+                            (c1 == 'a' && c2 == 'm')))
+                                macro();
+        else
+        if(c1=='s' && c2=='o') {
+                if(iflag)
+                        SKIP;
+                else {
+                        getfname();
+                        if(fname[0]) {
+                                if(infile = opn(fname))
+                                        *++filesp = infile;
+                                else infile = *filesp;
+                        }
+                }
+        }
+        else
+        if(c1=='n' && c2=='x')
+                if(iflag)
+                        SKIP;
+                else {
+                        getfname();
+                        if(fname[0] == '\0')
+                                exits(0);
+                        if(Bfildes(infile) != 0)
+                                Bterm(infile);
+                        infile = *filesp = opn(fname);
+                }
+        else
+        if(c1 == 't' && c2 == 'm')
+                SKIP;
+        else
+        if(c1=='h' && c2=='w')
+                SKIP; 
+        else
+        if(msflag && c1 == 'T' && c2 == 'L') {
+                SKIP_TO_COM;
+                goto comx; 
+        }
+        else
+        if(msflag && c1=='N' && c2 == 'R')
+                SKIP;
+        else
+        if(msflag && c1 == 'A' && (c2 == 'U' || c2 == 'I')){
+                if(mac==MM)SKIP;
+                else {
+                        SKIP_TO_COM;
+                        goto comx; 
+                }
+        } else
+        if(msflag && c1=='F' && c2=='S') {
+                SKIP_TO_COM;
+                goto comx; 
+        }
+        else
+        if(msflag && (c1=='S' || c1=='N') && c2=='H') {
+                SKIP_TO_COM;
+                goto comx; 
+        } else
+        if(c1 == 'U' && c2 == 'X') {
+                if(wordflag)
+                        Bprint(&bout, "UNIX\n");
+                else
+                        Bprint(&bout, "UNIX ");
+        } else
+        if(msflag && c1=='O' && c2=='K') {
+                SKIP_TO_COM;
+                goto comx; 
+        } else
+        if(msflag && c1=='N' && c2=='D')
+                SKIP;
+        else
+        if(msflag && mac==MM && c1=='H' && (c2==' '||c2=='U'))
+                SKIP;
+        else
+        if(msflag && mac==MM && c2=='L') {
+                if(disp || c1=='R')
+                        sdis('L', 'E');
+                else {
+                        SKIP;
+                        Bprint(&bout, " .");
+                }
+        } else
+        if(!msflag && c1=='P' && c2=='S') {
+                inpic();
+        } else
+        if(msflag && (c1=='D' || c1=='N' || c1=='K'|| c1=='P') && c2=='S') { 
+                sdis(c1, 'E'); 
+        } else
+        if(msflag && (c1 == 'K' && c2 == 'F')) { 
+                sdis(c1,'E'); 
+        } else
+        if(msflag && c1=='n' && c2=='f')
+                sdis('f','i');
+        else
+        if(msflag && c1=='c' && c2=='e')
+                sce();
+        else {
+                if(c1=='.' && c2=='.') {
+                        if(msflag) {
+                                SKIP;
+                                return;
+                        }
+                        while(C == '.')
+                                ;
+                }
+                inmacro++;
+                if(c1 <= 'Z' && msflag)
+                        regline(YES,ONE);
+                else {
+                        if(wordflag)
+                                C;
+                        regline(YES,TWO);
+                }
+                inmacro--;
+        }
+}
+
+void
+macro(void)
+{
+        if(msflag) {
+                do { 
+                        SKIP1; 
+                } while(C1 != '.' || C1 != '.' || C1 == '.');
+                if(c != '\n')
+                        SKIP;
+                return;
+        }
+        SKIP;
+        inmacro = YES;
+}
+
+void
+sdis(char a1, char a2)
+{
+        int c1, c2;
+        int eqnf;
+        int lct;
+
+        if(a1 == 'P'){
+                while(C1 == ' ')
+                        ;
+                if(c == '<') {
+                        SKIP1;
+                        return;
+                }
+        }
+        lct = 0;
+        eqnf = 1;
+        if(c != '\n')
+                SKIP1;
+        for(;;) {
+                while(C1 != '.')
+                        if(c == '\n')
+                                continue;
+                        else
+                                SKIP1;
+                if((c1=C1) == '\n')
+                        continue;
+                if((c2=C1) == '\n') {
+                        if(a1 == 'f' && (c1 == 'P' || c1 == 'H'))
+                                return;
+                        continue;
+                }
+                if(c1==a1 && c2 == a2) {
+                        SKIP1;
+                        if(lct != 0){
+                                lct--;
+                                continue;
+                        }
+                        if(eqnf)
+                                Bprint(&bout, " .");
+                        Bputc(&bout, '\n');
+                        return;
+                } else
+                if(a1 == 'L' && c2 == 'L') {
+                        lct++;
+                        SKIP1;
+                } else
+                if(a1 == 'D' && c1 == 'E' && c2 == 'Q') {
+                        eqn(); 
+                        eqnf = 0;
+                } else
+                if(a1 == 'f') {
+                        if((mac == MS && c2 == 'P') ||
+                                (mac == MM && c1 == 'H' && c2 == 'U')){
+                                SKIP1;
+                                return;
+                        }
+                        SKIP1;
+                }
+                else
+                        SKIP1;
+        }
+}
+
+void
+tbl(void)
+{
+        while(C != '.')
+                ;
+        SKIP;
+        intable = YES;
+}
+
+void
+stbl(void)
+{
+        while(C != '.')
+                ;
+        SKIP_TO_COM;
+        if(c != 'T' || C != 'E') {
+                SKIP;
+                pc = c;
+                while(C != '.' || pc != '\n' || C != 'T' || C != 'E')
+                        pc = c;
+        }
+}
+
+void
+eqn(void)
+{
+        long c1, c2;
+        int dflg;
+        char last;
+
+        last = 0;
+        dflg = 1;
+        SKIP;
+
+        for(;;) {
+                if(C1 == '.'  || c == '\'') {
+                        while(C1==' ' || c=='\t')
+                                ;
+                        if(c=='E' && C1=='N') {
+                                SKIP;
+                                if(msflag && dflg) {
+                                        Bputc(&bout, 'x');
+                                        Bputc(&bout, ' ');
+                                        if(last) {
+                                                Bputc(&bout, last); 
+                                                Bputc(&bout, '\n'); 
+                                        }
+                                }
+                                return;
+                        }
+                } else
+                if(c == 'd') {
+                        if(C1=='e' && C1=='l')
+                                if(C1=='i' && C1=='m') {
+                                        while(C1 == ' ')
+                                                ;
+                                        if((c1=c)=='\n' || (c2=C1)=='\n' ||
+                                          (c1=='o' && c2=='f' && C1=='f')) {
+                                                ldelim = NOCHAR;
+                                                rdelim = NOCHAR;
+                                        } else {
+                                                ldelim = c1;
+                                                rdelim = c2;
+                                        }
+                                }
+                        dflg = 0;
+                }
+                if(c != '\n')
+                        while(C1 != '\n') { 
+                                if(chars[c] == PUNCT)
+                                        last = c;
+                                else
+                                if(c != ' ')
+                                        last = 0;
+                        }
+        }
+}
+
+/*
+ * skip over a complete backslash vconstruction
+ */
+void
+backsl(void)
+{
+        int bdelim;
+
+sw:  
+        switch(C1)
+        {
+        case '"':
+                SKIP1;
+                return;
+
+        case 's':
+                if(C1 == '\\')
+                        backsl();
+                else {
+                        while(C1>='0' && c<='9')
+                                ;
+                        Bungetrune(infile);
+                        c = '0';
+                }
+                lp--;
+                return;
+
+        case 'f':
+        case 'n':
+        case '*':
+                if(C1 != '(')
+                        return;
+
+        case '(':
+                if(msflag) {
+                        if(C == 'e') {
+                                if(C1 == 'm') {
+                                        *lp = '-';
+                                        return;
+                                }
+                        } else
+                        if(c != '\n')
+                                C1;
+                        return;
+                }
+                if(C1 != '\n')
+                        C1;
+                return;
+
+        case '$':
+                C1;        /* discard argument number */
+                return;
+
+        case 'b':
+        case 'x':
+        case 'v':
+        case 'h':
+        case 'w':
+        case 'o':
+        case 'l':
+        case 'L':
+                if((bdelim=C1) == '\n')
+                        return;
+                while(C1!='\n' && c!=bdelim)
+                        if(c == '\\')
+                                backsl();
+                return;
+
+        case '\\':
+                if(inmacro)
+                        goto sw;
+        default:
+                return;
+        }
+}
+
+char*
+copys(char *s)
+{
+        char *t, *t0;
+
+        if((t0 = t = malloc((strlen(s)+1))) == 0)
+                fatal("Cannot allocate memory", (char*)0);
+        while(*t++ = *s++)
+                ;
+        return(t0);
+}
+
+void
+sce(void)
+{
+        int n = 1;
+
+        while (C != L'\n' && !(L'0' <= c && c <= L'9'))
+                ;
+        if (c != L'\n') {
+                for (n = c-L'0';'0' <= C && c <= L'9';)
+                        n = n*10 + c-L'0';
+        }
+        while(n) {
+                if(C == '.') {
+                        if(C == 'c') {
+                                if(C == 'e') {
+                                        while(C == ' ')
+                                                ;
+                                        if(c == '0') {
+                                                SKIP;
+                                                break;
+                                        } else
+                                                SKIP;
+                                } else
+                                        SKIP;
+                        } else
+                        if(c == 'P' || C == 'P') {
+                                if(c != '\n')
+                                        SKIP;
+                                break;
+                        } else
+                                if(c != '\n')
+                                        SKIP;
+                } else {
+                        SKIP;
+                        n--;
+                }
+        }
+}
+
+void
+refer(int c1)
+{
+        int c2;
+
+        if(c1 != '\n')
+                SKIP;
+        c2 = 0;
+        for(;;) {
+                if(C != '.')
+                        SKIP;
+                else {
+                        if(C != ']')
+                                SKIP;
+                        else {
+                                while(C != '\n')
+                                        c2 = c;
+                                if(charclass(c2) == PUNCT)
+                                        Bprint(&bout, " %C",c2);
+                                return;
+                        }
+                }
+        }
+}
+
+void
+inpic(void)
+{
+        int c1;
+        Rune *p1;
+
+/*        SKIP1;*/
+        while(C1 != '\n')
+                if(c == '<'){
+                        SKIP1;
+                        return;
+                }
+        p1 = line;
+        c = '\n';
+        for(;;) {
+                c1 = c;
+                if(C1 == '.' && c1 == '\n') {
+                        if(C1 != 'P' || C1 != 'E') {
+                                if(c != '\n'){
+                                        SKIP1;
+                                        c = '\n';
+                                }
+                                continue;
+                        }
+                        SKIP1;
+                        return;
+                } else
+                if(c == '\"') {
+                        while(C1 != '\"') {
+                                if(c == '\\') {
+                                        if(C1 == '\"')
+                                                continue;
+                                        Bungetrune(infile);
+                                        backsl();
+                                } else
+                                        *p1++ = c;
+                        }
+                        *p1++ = ' ';
+                } else
+                if(c == '\n' && p1 != line) {
+                        *p1 = '\0';
+                        if(wordflag)
+                                putwords();
+                        else
+                                Bprint(&bout, "%S\n\n", line);
+                        p1 = line;
+                }
+        }
+}
+
+int
+charclass(int c)
+{
+        if(c < MAX_ASCII)
+                return chars[c];
+        switch(c){
+        case 0x2013: case 0x2014:        /* en dash, em dash */
+                return SPECIAL;
+        }
+        return EXTENDED;
+}
diff --git a/src/cmd/du.C b/src/cmd/du.C
t@@ -0,0 +1,194 @@
+#include 
+#include 
+
+extern        vlong        du(char*, Dir*);
+extern        vlong        k(vlong);
+extern        void        err(char*);
+extern        int        warn(char*);
+extern        int        seen(Dir*);
+
+int        aflag;
+int        fflag;
+int        nflag;
+int        sflag;
+int        tflag;
+int        uflag;
+int        qflag;
+char        *fmt = "%llud\t%s\n";
+vlong        blocksize = 1024LL;
+
+void
+main(int argc, char *argv[])
+{
+        int i;
+        char *s, *ss;
+
+        ARGBEGIN {
+        case 'a':        /* all files */
+                aflag = 1;
+                break;
+        case 's':        /* only top level */
+                sflag = 1;
+                break;
+        case 'f':        /* ignore errors */
+                fflag = 1;
+                break;
+        case 'n':        /* all files, number of bytes */
+                aflag = 1;
+                nflag = 1;
+                break;
+        case 't':        /* return modified/accessed time */
+                tflag = 1;
+                break;
+        case 'u':        /* accessed time */
+                uflag = 1;
+                break;
+        case 'q':        /* qid */
+                fmt = "%.16llux\t%s\n";
+                qflag = 1;
+                break;
+        case 'b':        /* block size */
+                s = ARGF();
+                if(s) {
+                        blocksize = strtoul(s, &ss, 0);
+                        if(s == ss)
+                                blocksize = 1;
+                        if(*ss == 'k')
+                                blocksize *= 1024;
+                }
+                break;
+        } ARGEND
+        if(argc==0)
+                print(fmt, du(".", dirstat(".")), ".");
+        else
+                for(i=0; iqid.type&QTDIR) == 0)
+                nk = k(dir->length);
+        else{
+                nk = 0;
+                while((n=dirread(fd, &buf)) > 0) {
+                        d = buf;
+                        for(i=0; iqid.type&QTDIR) == 0) {
+                                        t = k(d->length);
+                                        nk += t;
+                                        if(aflag) {
+                                                sprint(file, "%s/%s", name, d->name);
+                                                if(tflag) {
+                                                        t = d->mtime;
+                                                        if(uflag)
+                                                                t = d->atime;
+                                                }
+                                                if(qflag)
+                                                        t = d->qid.path;
+                                                print(fmt, t, file);
+                                        }
+                                        continue;
+                                }
+                                if(strcmp(d->name, ".") == 0 ||
+                                   strcmp(d->name, "..") == 0 ||
+                                   seen(d))
+                                        continue;
+                                sprint(file, "%s/%s", name, d->name);
+                                t = du(file, d);
+                                nk += t;
+                                if(tflag) {
+                                        t = d->mtime;
+                                        if(uflag)
+                                                t = d->atime;
+                                }
+                                if(qflag)
+                                        t = d->qid.path;
+                                if(!sflag)
+                                        print(fmt, t, file);
+                        }
+                        free(buf);
+                }
+                if(n < 0)
+                        warn(name);
+        }
+        close(fd);
+        if(tflag) {
+                if(uflag)
+                        return dir->atime;
+                return dir->mtime;
+        }
+        if(qflag)
+                return dir->qid.path;
+        return nk;
+}
+
+#define        NCACHE        128        /* must be power of two */
+typedef        struct        Cache        Cache;
+struct        Cache
+{
+        Dir*        cache;
+        int        n;
+        int        max;
+} cache[NCACHE];
+
+int
+seen(Dir *dir)
+{
+        Dir *dp;
+        int i;
+        Cache *c;
+
+        c = &cache[dir->qid.path&(NCACHE-1)];
+        dp = c->cache;
+        for(i=0; in; i++, dp++)
+                if(dir->qid.path == dp->qid.path &&
+                   dir->type == dp->type &&
+                   dir->dev == dp->dev)
+                        return 1;
+        if(c->n == c->max){
+                c->cache = realloc(c->cache, (c->max+=20)*sizeof(Dir));
+                if(cache == 0)
+                        err("malloc failure");
+        }
+        c->cache[c->n++] = *dir;
+        return 0;
+}
+
+void
+err(char *s)
+{
+        fprint(2, "du: %s: %r\n", s);
+        exits(s);
+}
+
+int
+warn(char *s)
+{
+        if(fflag == 0)
+                fprint(2, "du: %s: %r\n", s);
+        return 0;
+}
+
+vlong
+k(vlong n)
+{
+        if(nflag)
+                return n;
+        n = (n+blocksize-1)/blocksize;
+        return n*blocksize/1024LL;
+}
diff --git a/src/cmd/echo.c b/src/cmd/echo.c
t@@ -0,0 +1,38 @@
+#include 
+#include 
+
+void
+main(int argc, char *argv[])
+{
+        int nflag;
+        int i, len;
+        char *buf, *p;
+
+        nflag = 0;
+        if(argc > 1 && strcmp(argv[1], "-n") == 0)
+                nflag = 1;
+
+        len = 1;
+        for(i = 1+nflag; i < argc; i++)
+                len += strlen(argv[i])+1;
+
+        buf = malloc(len);
+        if(buf == 0)
+                exits("no memory");
+
+        p = buf;
+        for(i = 1+nflag; i < argc; i++){
+                strcpy(p, argv[i]);
+                p += strlen(p);
+                if(i < argc-1)
+                        *p++ = ' ';
+        }
+                
+        if(!nflag)
+                *p++ = '\n';
+
+        if(write(1, buf, p-buf) < 0)
+                fprint(2, "echo: write error: %r\n");
+
+        exits((char *)0);
+}
diff --git a/src/cmd/ed.c b/src/cmd/ed.c
t@@ -0,0 +1,1608 @@
+/*
+ * Editor
+ */
+#include 
+#include 
+#include 
+#include 
+
+enum
+{
+        FNSIZE        = 128,                /* file name */
+        LBSIZE        = 4096,                /* max line size */
+        BLKSIZE        = 4096,                /* block size in temp file */
+        NBLK        = 8191,                /* max size of temp file */
+        ESIZE        = 256,                /* max size of reg exp */
+        GBSIZE        = 256,                /* max size of global command */
+        MAXSUB        = 9,                /* max number of sub reg exp */
+        ESCFLG        = 0xFFFF,        /* escape Rune - user defined code */
+        EOF        = -1,
+};
+
+void        (*oldhup)(int);
+void        (*oldquit)(int);
+int*        addr1;
+int*        addr2;
+int        anymarks;
+int        col;
+long        count;
+int*        dol;
+int*        dot;
+int        fchange;
+char        file[FNSIZE];
+Rune        genbuf[LBSIZE];
+int        given;
+Rune*        globp;
+int        iblock;
+int        ichanged;
+int        io;
+Biobuf        iobuf;
+int        lastc;
+char        line[70];
+Rune*        linebp;
+Rune        linebuf[LBSIZE];
+int        listf;
+int        listn;
+Rune*        loc1;
+Rune*        loc2;
+int        names[26];
+int        nleft;
+int        oblock;
+int        oflag;
+Reprog        *pattern;
+int        peekc;
+int        pflag;
+int        rescuing;
+Rune        rhsbuf[LBSIZE/2];
+char        savedfile[FNSIZE];
+jmp_buf        savej;
+int        subnewa;
+int        subolda;
+Resub        subexp[MAXSUB];
+char*        tfname;
+int        tline;
+int        waiting;
+int        wrapp;
+int*        zero;
+
+char        Q[]        = "";
+char        T[]        = "TMP";
+char        WRERR[]        = "WRITE ERROR";
+int        bpagesize = 20;
+char        hex[]        = "0123456789abcdef";
+char*        linp        = line;
+ulong        nlall = 128;
+int        tfile        = -1;
+int        vflag        = 1;
+
+void        add(int);
+int*        address(void);
+int        append(int(*)(void), int*);
+void        browse(void);
+void        callunix(void);
+void        commands(void);
+void        compile(int);
+int        compsub(void);
+void        dosub(void);
+void        error(char*);
+int        match(int*);
+void        exfile(int);
+void        filename(int);
+Rune*        getblock(int, int);
+int        getchr(void);
+int        getcopy(void);
+int        getfile(void);
+Rune*        getline(int);
+int        getnum(void);
+int        getsub(void);
+int        gettty(void);
+void        global(int);
+void        init(void);
+void        join(void);
+void        move(int);
+void        newline(void);
+void        nonzero(void);
+void        notifyf(void*, char*);
+Rune*        place(Rune*, Rune*, Rune*);
+void        printcom(void);
+void        putchr(int);
+void        putd(void);
+void        putfile(void);
+int        putline(void);
+void        putshst(Rune*);
+void        putst(char*);
+void        quit(void);
+void        rdelete(int*, int*);
+void        regerror(char *);
+void        reverse(int*, int*);
+void        setnoaddr(void);
+void        setwide(void);
+void        squeeze(int);
+void        substitute(int);
+
+Rune La[] = { 'a', 0 };
+Rune Lr[] = { 'r', 0 };
+
+char tmp[] = "/tmp/eXXXXX";
+
+void
+main(int argc, char *argv[])
+{
+        char *p1, *p2;
+
+        notify(notifyf);
+        ARGBEGIN {
+        case 'o':
+                oflag = 1;
+                vflag = 0;
+                break;
+        } ARGEND
+
+        USED(argc);
+        if(*argv && (strcmp(*argv, "-") == 0)) {
+                argv++;
+                vflag = 0;
+        }
+        if(oflag) {
+                p1 = "/fd/1";
+                p2 = savedfile;
+                while(*p2++ = *p1++)
+                        ;
+                globp = La;
+        } else
+        if(*argv) {
+                p1 = *argv;
+                p2 = savedfile;
+                while(*p2++ = *p1++)
+                        if(p2 >= &savedfile[sizeof(savedfile)])
+                                p2--;
+                globp = Lr;
+        }
+        zero = malloc((nlall+5)*sizeof(int*));
+        tfname = mktemp(tmp);
+        init();
+        setjmp(savej);
+        commands();
+        quit();
+}
+
+void
+commands(void)
+{
+        int *a1, c, temp;
+        char lastsep;
+        Dir *d;
+
+        for(;;) {
+                if(pflag) {
+                        pflag = 0;
+                        addr1 = addr2 = dot;
+                        printcom();
+                }
+                c = '\n';
+                for(addr1 = 0;;) {
+                        lastsep = c;
+                        a1 = address();
+                        c = getchr();
+                        if(c != ',' && c != ';')
+                                break;
+                        if(lastsep == ',')
+                                error(Q);
+                        if(a1 == 0) {
+                                a1 = zero+1;
+                                if(a1 > dol)
+                                        a1--;
+                        }
+                        addr1 = a1;
+                        if(c == ';')
+                                dot = a1;
+                }
+                if(lastsep != '\n' && a1 == 0)
+                        a1 = dol;
+                if((addr2=a1) == 0) {
+                        given = 0;
+                        addr2 = dot;        
+                } else
+                        given = 1;
+                if(addr1 == 0)
+                        addr1 = addr2;
+                switch(c) {
+
+                case 'a':
+                        add(0);
+                        continue;
+
+                case 'b':
+                        nonzero();
+                        browse();
+                        continue;
+
+                case 'c':
+                        nonzero();
+                        newline();
+                        rdelete(addr1, addr2);
+                        append(gettty, addr1-1);
+                        continue;
+
+                case 'd':
+                        nonzero();
+                        newline();
+                        rdelete(addr1, addr2);
+                        continue;
+
+                case 'E':
+                        fchange = 0;
+                        c = 'e';
+                case 'e':
+                        setnoaddr();
+                        if(vflag && fchange) {
+                                fchange = 0;
+                                error(Q);
+                        }
+                        filename(c);
+                        init();
+                        addr2 = zero;
+                        goto caseread;
+
+                case 'f':
+                        setnoaddr();
+                        filename(c);
+                        putst(savedfile);
+                        continue;
+
+                case 'g':
+                        global(1);
+                        continue;
+
+                case 'i':
+                        add(-1);
+                        continue;
+
+
+                case 'j':
+                        if(!given)
+                                addr2++;
+                        newline();
+                        join();
+                        continue;
+
+                case 'k':
+                        nonzero();
+                        c = getchr();
+                        if(c < 'a' || c > 'z')
+                                error(Q);
+                        newline();
+                        names[c-'a'] = *addr2 & ~01;
+                        anymarks |= 01;
+                        continue;
+
+                case 'm':
+                        move(0);
+                        continue;
+
+                case 'n':
+                        listn++;
+                        newline();
+                        printcom();
+                        continue;
+
+                case '\n':
+                        if(a1==0) {
+                                a1 = dot+1;
+                                addr2 = a1;
+                                addr1 = a1;
+                        }
+                        if(lastsep==';')
+                                addr1 = a1;
+                        printcom();
+                        continue;
+
+                case 'l':
+                        listf++;
+                case 'p':
+                case 'P':
+                        newline();
+                        printcom();
+                        continue;
+
+                case 'Q':
+                        fchange = 0;
+                case 'q':
+                        setnoaddr();
+                        newline();
+                        quit();
+
+                case 'r':
+                        filename(c);
+                caseread:
+                        if((io=open(file, OREAD)) < 0) {
+                                lastc = '\n';
+                                error(file);
+                        }
+                        if((d = dirfstat(io)) != nil){
+                                if(d->mode & DMAPPEND)
+                                        print("warning: %s is append only\n", file);
+                                free(d);
+                        }
+                        Binit(&iobuf, io, OREAD);
+                        setwide();
+                        squeeze(0);
+                        c = zero != dol;
+                        append(getfile, addr2);
+                        exfile(OREAD);
+
+                        fchange = c;
+                        continue;
+
+                case 's':
+                        nonzero();
+                        substitute(globp != 0);
+                        continue;
+
+                case 't':
+                        move(1);
+                        continue;
+
+                case 'u':
+                        nonzero();
+                        newline();
+                        if((*addr2&~01) != subnewa)
+                                error(Q);
+                        *addr2 = subolda;
+                        dot = addr2;
+                        continue;
+
+                case 'v':
+                        global(0);
+                        continue;
+
+                case 'W':
+                        wrapp++;
+                case 'w':
+                        setwide();
+                        squeeze(dol>zero);
+                        temp = getchr();
+                        if(temp != 'q' && temp != 'Q') {
+                                peekc = temp;
+                                temp = 0;
+                        }
+                        filename(c);
+                        if(!wrapp ||
+                          ((io = open(file, OWRITE)) == -1) ||
+                          ((seek(io, 0L, 2)) == -1))
+                                if((io = create(file, OWRITE, 0666)) < 0)
+                                        error(file);
+                        Binit(&iobuf, io, OWRITE);
+                        wrapp = 0;
+                        if(dol > zero)
+                                putfile();
+                        exfile(OWRITE);
+                        if(addr1<=zero+1 && addr2==dol)
+                                fchange = 0;
+                        if(temp == 'Q')
+                                fchange = 0;
+                        if(temp)
+                                quit();
+                        continue;
+
+                case '=':
+                        setwide();
+                        squeeze(0);
+                        newline();
+                        count = addr2 - zero;
+                        putd();
+                        putchr(L'\n');
+                        continue;
+
+                case '!':
+                        callunix();
+                        continue;
+
+                case EOF:
+                        return;
+
+                }
+                error(Q);
+        }
+}
+
+void
+printcom(void)
+{
+        int *a1;
+
+        nonzero();
+        a1 = addr1;
+        do {
+                if(listn) {
+                        count = a1-zero;
+                        putd();
+                        putchr(L'\t');
+                }
+                putshst(getline(*a1++));
+        } while(a1 <= addr2);
+        dot = addr2;
+        listf = 0;
+        listn = 0;
+        pflag = 0;
+}
+
+int*
+address(void)
+{
+        int sign, *a, opcnt, nextopand, *b, c;
+
+        nextopand = -1;
+        sign = 1;
+        opcnt = 0;
+        a = dot;
+        do {
+                do {
+                        c = getchr();
+                } while(c == ' ' || c == '\t');
+                if(c >= '0' && c <= '9') {
+                        peekc = c;
+                        if(!opcnt)
+                                a = zero;
+                        a += sign*getnum();
+                } else
+                switch(c) {
+                case '$':
+                        a = dol;
+                case '.':
+                        if(opcnt)
+                                error(Q);
+                        break;
+                case '\'':
+                        c = getchr();
+                        if(opcnt || c < 'a' || c > 'z')
+                                error(Q);
+                        a = zero;
+                        do {
+                                a++;
+                        } while(a <= dol && names[c-'a'] != (*a & ~01));
+                        break;
+                case '?':
+                        sign = -sign;
+                case '/':
+                        compile(c);
+                        b = a;
+                        for(;;) {
+                                a += sign;
+                                if(a <= zero)
+                                        a = dol;
+                                if(a > dol)
+                                        a = zero;
+                                if(match(a))
+                                        break;
+                                if(a == b)
+                                        error(Q);
+                        }
+                        break;
+                default:
+                        if(nextopand == opcnt) {
+                                a += sign;
+                                if(a < zero || dol < a)
+                                        continue;       /* error(Q); */
+                        }
+                        if(c != '+' && c != '-' && c != '^') {
+                                peekc = c;
+                                if(opcnt == 0)
+                                        a = 0;
+                                return a;
+                        }
+                        sign = 1;
+                        if(c != '+')
+                                sign = -sign;
+                        nextopand = ++opcnt;
+                        continue;
+                }
+                sign = 1;
+                opcnt++;
+        } while(zero <= a && a <= dol);
+        error(Q);
+        return 0;
+}
+
+int
+getnum(void)
+{
+        int r, c;
+
+        r = 0;
+        for(;;) {
+                c = getchr();
+                if(c < '0' || c > '9')
+                        break;
+                r = r*10 + (c-'0');
+        }
+        peekc = c;
+        return r;
+}
+
+void
+setwide(void)
+{
+        if(!given) {
+                addr1 = zero + (dol>zero);
+                addr2 = dol;
+        }
+}
+
+void
+setnoaddr(void)
+{
+        if(given)
+                error(Q);
+}
+
+void
+nonzero(void)
+{
+        squeeze(1);
+}
+
+void
+squeeze(int i)
+{
+        if(addr1 < zero+i || addr2 > dol || addr1 > addr2)
+                error(Q);
+}
+
+void
+newline(void)
+{
+        int c;
+
+        c = getchr();
+        if(c == '\n' || c == EOF)
+                return;
+        if(c == 'p' || c == 'l' || c == 'n') {
+                pflag++;
+                if(c == 'l')
+                        listf++;
+                else
+                if(c == 'n')
+                        listn++;
+                c = getchr();
+                if(c == '\n')
+                        return;
+        }
+        error(Q);
+}
+
+void
+filename(int comm)
+{
+        char *p1, *p2;
+        Rune rune;
+        int c;
+
+        count = 0;
+        c = getchr();
+        if(c == '\n' || c == EOF) {
+                p1 = savedfile;
+                if(*p1 == 0 && comm != 'f')
+                        error(Q);
+                p2 = file;
+                while(*p2++ = *p1++)
+                        ;
+                return;
+        }
+        if(c != ' ')
+                error(Q);
+        while((c=getchr()) == ' ')
+                ;
+        if(c == '\n')
+                error(Q);
+        p1 = file;
+        do {
+                if(p1 >= &file[sizeof(file)-6] || c == ' ' || c == EOF)
+                        error(Q);
+                rune = c;
+                p1 += runetochar(p1, &rune);
+        } while((c=getchr()) != '\n');
+        *p1 = 0;
+        if(savedfile[0] == 0 || comm == 'e' || comm == 'f') {
+                p1 = savedfile;
+                p2 = file;
+                while(*p1++ = *p2++)
+                        ;
+        }
+}
+
+void
+exfile(int om)
+{
+
+        if(om == OWRITE)
+                if(Bflush(&iobuf) < 0)
+                        error(Q);
+        close(io);
+        io = -1;
+        if(vflag) {
+                putd();
+                putchr(L'\n');
+        }
+}
+
+void
+error1(char *s)
+{
+        int c;
+
+        wrapp = 0;
+        listf = 0;
+        listn = 0;
+        count = 0;
+        seek(0, 0, 2);
+        pflag = 0;
+        if(globp)
+                lastc = '\n';
+        globp = 0;
+        peekc = lastc;
+        if(lastc)
+                for(;;) {
+                        c = getchr();
+                        if(c == '\n' || c == EOF)
+                                break;
+                }
+        if(io > 0) {
+                close(io);
+                io = -1;
+        }
+        putchr(L'?');
+        putst(s);
+}
+
+void
+error(char *s)
+{
+        error1(s);
+        longjmp(savej, 1);
+}
+
+void
+rescue(void)
+{
+        rescuing = 1;
+        if(dol > zero) {
+                addr1 = zero+1;
+                addr2 = dol;
+                io = create("ed.hup", OWRITE, 0666);
+                if(io > 0){
+                        Binit(&iobuf, io, OWRITE);
+                        putfile();
+                }
+        }
+        fchange = 0;
+        quit();
+}
+
+void
+notifyf(void *a, char *s)
+{
+        if(strcmp(s, "interrupt") == 0){
+                if(rescuing || waiting)
+                        noted(NCONT);
+                putchr(L'\n');
+                lastc = '\n';
+                error1(Q);
+                notejmp(a, savej, 0);
+        }
+        if(strcmp(s, "hangup") == 0){
+                if(rescuing)
+                        noted(NDFLT);
+                rescue();
+        }
+        fprint(2, "ed: note: %s\n", s);
+        abort();
+}
+
+int
+getchr(void)
+{
+        char s[UTFmax];
+        int i;
+        Rune r;
+
+        if(lastc = peekc) {
+                peekc = 0;
+                return lastc;
+        }
+        if(globp) {
+                if((lastc=*globp++) != 0)
+                        return lastc;
+                globp = 0;
+                return EOF;
+        }
+        for(i=0;;) {
+                if(read(0, s+i, 1) <= 0)
+                        return lastc = EOF;
+                i++;
+                if(fullrune(s, i))
+                        break;
+                
+        }
+        chartorune(&r, s);
+        lastc = r;
+        return lastc;
+}
+
+int
+gety(void)
+{
+        int c;
+        Rune *gf, *p;
+
+        p = linebuf;
+        gf = globp;
+        for(;;) {
+                c = getchr();
+                if(c == '\n') {
+                        *p = 0;
+                        return 0;
+                }
+                if(c == EOF) {
+                        if(gf)
+                                peekc = c;
+                        return c;
+                }
+                if(c == 0)
+                        continue;
+                *p++ = c;
+                if(p >= &linebuf[LBSIZE-2])
+                        error(Q);
+        }
+        return 0;
+}
+
+int
+gettty(void)
+{
+        int rc;
+
+        rc = gety();
+        if(rc)
+                return rc;
+        if(linebuf[0] == '.' && linebuf[1] == 0)
+                return EOF;
+        return 0;
+}
+
+int
+getfile(void)
+{
+        int c;
+        Rune *lp;
+
+        lp = linebuf;
+        do {
+                c = Bgetrune(&iobuf);
+                if(c < 0) {
+                        if(lp > linebuf) {
+                                putst("'\\n' appended");
+                                c = '\n';
+                        } else
+                                return EOF;
+                }
+                if(lp >= &linebuf[LBSIZE]) {
+                        lastc = '\n';
+                        error(Q);
+                }
+                *lp++ = c;
+                count++;
+        } while(c != '\n');
+        lp[-1] = 0;
+        return 0;
+}
+
+void
+putfile(void)
+{
+        int *a1;
+        Rune *lp;
+        long c;
+
+        a1 = addr1;
+        do {
+                lp = getline(*a1++);
+                for(;;) {
+                        count++;
+                        c = *lp++;
+                        if(c == 0) {
+                                if(Bputrune(&iobuf, '\n') < 0)
+                                        error(Q);
+                                break;
+                        }
+                        if(Bputrune(&iobuf, c) < 0)
+                                error(Q);
+                }
+        } while(a1 <= addr2);
+        if(Bflush(&iobuf) < 0)
+                error(Q);
+}
+
+int
+append(int (*f)(void), int *a)
+{
+        int *a1, *a2, *rdot, nline, tl;
+
+        nline = 0;
+        dot = a;
+        while((*f)() == 0) {
+                if((dol-zero) >= nlall) {
+                        nlall += 512;
+                        a1 = realloc(zero, (nlall+5)*sizeof(int*));
+                        if(a1 == 0) {
+                                error("MEM?");
+                                rescue();
+                        }
+                        tl = a1 - zero;        /* relocate pointers */
+                        zero += tl;
+                        addr1 += tl;
+                        addr2 += tl;
+                        dol += tl;
+                        dot += tl;
+                }
+                tl = putline();
+                nline++;
+                a1 = ++dol;
+                a2 = a1+1;
+                rdot = ++dot;
+                while(a1 > rdot)
+                        *--a2 = *--a1;
+                *rdot = tl;
+        }
+        return nline;
+}
+
+void
+add(int i)
+{
+        if(i && (given || dol > zero)) {
+                addr1--;
+                addr2--;
+        }
+        squeeze(0);
+        newline();
+        append(gettty, addr2);
+}
+
+void
+browse(void)
+{
+        int forward, n;
+        static int bformat, bnum; /* 0 */
+
+        forward = 1;
+        peekc = getchr();
+        if(peekc != '\n'){
+                if(peekc == '-' || peekc == '+') {
+                        if(peekc == '-')
+                                forward = 0;
+                        getchr();
+                }
+                n = getnum();
+                if(n > 0)
+                        bpagesize = n;
+        }
+        newline();
+        if(pflag) {
+                bformat = listf;
+                bnum = listn;
+        } else {
+                listf = bformat;
+                listn = bnum;
+        }
+        if(forward) {
+                addr1 = addr2;
+                addr2 += bpagesize;
+                if(addr2 > dol)
+                        addr2 = dol;
+        } else {
+                addr1 = addr2-bpagesize;
+                if(addr1 <= zero)
+                        addr1 = zero+1;
+        }
+        printcom();
+}
+
+void
+callunix(void)
+{
+        int c, pid;
+        Rune rune;
+        char buf[512];
+        char *p;
+
+        setnoaddr();
+        p = buf;
+        while((c=getchr()) != EOF && c != '\n')
+                if(p < &buf[sizeof(buf) - 6]) {
+                        rune = c;
+                        p += runetochar(p, &rune);
+                }
+        *p = 0;
+        pid = fork();
+        if(pid == 0) {
+                execl("/bin/rc", "rc", "-c", buf, 0);
+                exits("execl failed");
+        }
+        waiting = 1;
+        while(waitpid() != pid)
+                ;
+        waiting = 0;
+        if(vflag)
+                putst("!");
+}
+
+void
+quit(void)
+{
+        if(vflag && fchange && dol!=zero) {
+                fchange = 0;
+                error(Q);
+        }
+        remove(tfname);
+        exits(0);
+}
+
+void
+onquit(int sig)
+{
+        USED(sig);
+        quit();
+}
+
+void
+rdelete(int *ad1, int *ad2)
+{
+        int *a1, *a2, *a3;
+
+        a1 = ad1;
+        a2 = ad2+1;
+        a3 = dol;
+        dol -= a2 - a1;
+        do {
+                *a1++ = *a2++;
+        } while(a2 <= a3);
+        a1 = ad1;
+        if(a1 > dol)
+                a1 = dol;
+        dot = a1;
+        fchange = 1;
+}
+
+void
+gdelete(void)
+{
+        int *a1, *a2, *a3;
+
+        a3 = dol;
+        for(a1=zero; (*a1&01)==0; a1++)
+                if(a1>=a3)
+                        return;
+        for(a2=a1+1; a2<=a3;) {
+                if(*a2 & 01) {
+                        a2++;
+                        dot = a1;
+                } else
+                        *a1++ = *a2++;
+        }
+        dol = a1-1;
+        if(dot > dol)
+                dot = dol;
+        fchange = 1;
+}
+
+Rune*
+getline(int tl)
+{
+        Rune *lp, *bp;
+        int nl;
+
+        lp = linebuf;
+        bp = getblock(tl, OREAD);
+        nl = nleft;
+        tl &= ~((BLKSIZE/2) - 1);
+        while(*lp++ = *bp++) {
+                nl -= sizeof(Rune);
+                if(nl == 0) {
+                        bp = getblock(tl += BLKSIZE/2, OREAD);
+                        nl = nleft;
+                }
+        }
+        return linebuf;
+}
+
+int
+putline(void)
+{
+        Rune *lp, *bp;
+        int nl, tl;
+
+        fchange = 1;
+        lp = linebuf;
+        tl = tline;
+        bp = getblock(tl, OWRITE);
+        nl = nleft;
+        tl &= ~((BLKSIZE/2)-1);
+        while(*bp = *lp++) {
+                if(*bp++ == '\n') {
+                        bp[-1] = 0;
+                        linebp = lp;
+                        break;
+                }
+                nl -= sizeof(Rune);
+                if(nl == 0) {
+                        tl += BLKSIZE/2;
+                        bp = getblock(tl, OWRITE);
+                        nl = nleft;
+                }
+        }
+        nl = tline;
+        tline += ((lp-linebuf) + 03) & 077776;
+        return nl;
+}
+
+void
+blkio(int b, uchar *buf, int isread)
+{
+        int n;
+
+        seek(tfile, b*BLKSIZE, 0);
+        if(isread)
+                n = read(tfile, buf, BLKSIZE);
+        else
+                n = write(tfile, buf, BLKSIZE);
+        if(n != BLKSIZE)
+                error(T);
+}
+
+Rune*
+getblock(int atl, int iof)
+{
+        int bno, off;
+        
+        static uchar ibuff[BLKSIZE];
+        static uchar obuff[BLKSIZE];
+
+        bno = atl / (BLKSIZE/2);
+        off = (atl<<1) & (BLKSIZE-1) & ~03;
+        if(bno >= NBLK) {
+                lastc = '\n';
+                error(T);
+        }
+        nleft = BLKSIZE - off;
+        if(bno == iblock) {
+                ichanged |= iof;
+                return (Rune*)(ibuff+off);
+        }
+        if(bno == oblock)
+                return (Rune*)(obuff+off);
+        if(iof == OREAD) {
+                if(ichanged)
+                        blkio(iblock, ibuff, 0);
+                ichanged = 0;
+                iblock = bno;
+                blkio(bno, ibuff, 1);
+                return (Rune*)(ibuff+off);
+        }
+        if(oblock >= 0)
+                blkio(oblock, obuff, 0);
+        oblock = bno;
+        return (Rune*)(obuff+off);
+}
+
+void
+init(void)
+{
+        int *markp;
+
+        close(tfile);
+        tline = 2;
+        for(markp = names; markp < &names[26]; )
+                *markp++ = 0;
+        subnewa = 0;
+        anymarks = 0;
+        iblock = -1;
+        oblock = -1;
+        ichanged = 0;
+        if((tfile = create(tfname, ORDWR, 0600)) < 0){
+                error1(T);
+                exits(0);
+        }
+        dot = dol = zero;
+}
+
+void
+global(int k)
+{
+        Rune *gp, globuf[GBSIZE];
+        int c, *a1;
+
+        if(globp)
+                error(Q);
+        setwide();
+        squeeze(dol > zero);
+        c = getchr();
+        if(c == '\n')
+                error(Q);
+        compile(c);
+        gp = globuf;
+        while((c=getchr()) != '\n') {
+                if(c == EOF)
+                        error(Q);
+                if(c == '\\') {
+                        c = getchr();
+                        if(c != '\n')
+                                *gp++ = '\\';
+                }
+                *gp++ = c;
+                if(gp >= &globuf[GBSIZE-2])
+                        error(Q);
+        }
+        if(gp == globuf)
+                *gp++ = 'p';
+        *gp++ = '\n';
+        *gp = 0;
+        for(a1=zero; a1<=dol; a1++) {
+                *a1 &= ~01;
+                if(a1 >= addr1 && a1 <= addr2 && match(a1) == k)
+                        *a1 |= 01;
+        }
+
+        /*
+         * Special case: g/.../d (avoid n^2 algorithm)
+         */
+        if(globuf[0] == 'd' && globuf[1] == '\n' && globuf[2] == 0) {
+                gdelete();
+                return;
+        }
+        for(a1=zero; a1<=dol; a1++) {
+                if(*a1 & 01) {
+                        *a1 &= ~01;
+                        dot = a1;
+                        globp = globuf;
+                        commands();
+                        a1 = zero;
+                }
+        }
+}
+
+void
+join(void)
+{
+        Rune *gp, *lp;
+        int *a1;
+
+        nonzero();
+        gp = genbuf;
+        for(a1=addr1; a1<=addr2; a1++) {
+                lp = getline(*a1);
+                while(*gp = *lp++)
+                        if(gp++ >= &genbuf[LBSIZE-2])
+                                error(Q);
+        }
+        lp = linebuf;
+        gp = genbuf;
+        while(*lp++ = *gp++)
+                ;
+        *addr1 = putline();
+        if(addr1 < addr2)
+                rdelete(addr1+1, addr2);
+        dot = addr1;
+}
+
+void
+substitute(int inglob)
+{
+        int *mp, *a1, nl, gsubf, n;
+
+        n = getnum();        /* OK even if n==0 */
+        gsubf = compsub();
+        for(a1 = addr1; a1 <= addr2; a1++) {
+                if(match(a1)){
+                        int *ozero;
+                        int m = n;
+
+                        do {
+                                int span = loc2-loc1;
+
+                                if(--m <= 0) {
+                                        dosub();
+                                        if(!gsubf)
+                                                break;
+                                        if(span == 0) {        /* null RE match */
+                                                if(*loc2 == 0)
+                                                        break;
+                                                loc2++;
+                                        }
+                                }
+                        } while(match(0));
+                        if(m <= 0) {
+                                inglob |= 01;
+                                subnewa = putline();
+                                *a1 &= ~01;
+                                if(anymarks) {
+                                        for(mp=names; mp<&names[26]; mp++)
+                                                if(*mp == *a1)
+                                                        *mp = subnewa;
+                                }
+                                subolda = *a1;
+                                *a1 = subnewa;
+                                ozero = zero;
+                                nl = append(getsub, a1);
+                                addr2 += nl;
+                                nl += zero-ozero;
+                                a1 += nl;
+                        }
+                }
+        }
+        if(inglob == 0)
+                error(Q);
+}
+
+int
+compsub(void)
+{
+        int seof, c;
+        Rune *p;
+
+        seof = getchr();
+        if(seof == '\n' || seof == ' ')
+                error(Q);
+        compile(seof);
+        p = rhsbuf;
+        for(;;) {
+                c = getchr();
+                if(c == '\\') {
+                        c = getchr();
+                        *p++ = ESCFLG;
+                        if(p >= &rhsbuf[LBSIZE/2])
+                                error(Q);
+                } else
+                if(c == '\n' && (!globp || !globp[0])) {
+                        peekc = c;
+                        pflag++;
+                        break;
+                } else
+                if(c == seof)
+                        break;
+                *p++ = c;
+                if(p >= &rhsbuf[LBSIZE/2])
+                        error(Q);
+        }
+        *p = 0;
+        peekc = getchr();
+        if(peekc == 'g') {
+                peekc = 0;
+                newline();
+                return 1;
+        }
+        newline();
+        return 0;
+}
+
+int
+getsub(void)
+{
+        Rune *p1, *p2;
+
+        p1 = linebuf;
+        if((p2 = linebp) == 0)
+                return EOF;
+        while(*p1++ = *p2++)
+                ;
+        linebp = 0;
+        return 0;
+}
+
+void
+dosub(void)
+{
+        Rune *lp, *sp, *rp;
+        int c, n;
+
+        lp = linebuf;
+        sp = genbuf;
+        rp = rhsbuf;
+        while(lp < loc1)
+                *sp++ = *lp++;
+        while(c = *rp++) {
+                if(c == '&'){
+                        sp = place(sp, loc1, loc2);
+                        continue;
+                }
+                if(c == ESCFLG && (c = *rp++) >= '1' && c < MAXSUB+'0') {
+                        n = c-'0';
+                        if(subexp[n].s.rsp && subexp[n].e.rep) {
+                                sp = place(sp, subexp[n].s.rsp, subexp[n].e.rep);
+                                continue;
+                        }
+                        error(Q);
+                }
+                *sp++ = c;
+                if(sp >= &genbuf[LBSIZE])
+                        error(Q);
+        }
+        lp = loc2;
+        loc2 = sp - genbuf + linebuf;
+        while(*sp++ = *lp++)
+                if(sp >= &genbuf[LBSIZE])
+                        error(Q);
+        lp = linebuf;
+        sp = genbuf;
+        while(*lp++ = *sp++)
+                ;
+}
+
+Rune*
+place(Rune *sp, Rune *l1, Rune *l2)
+{
+
+        while(l1 < l2) {
+                *sp++ = *l1++;
+                if(sp >= &genbuf[LBSIZE])
+                        error(Q);
+        }
+        return sp;
+}
+
+void
+move(int cflag)
+{
+        int *adt, *ad1, *ad2;
+
+        nonzero();
+        if((adt = address())==0)        /* address() guarantees addr is in range */
+                error(Q);
+        newline();
+        if(cflag) {
+                int *ozero, delta;
+                ad1 = dol;
+                ozero = zero;
+                append(getcopy, ad1++);
+                ad2 = dol;
+                delta = zero - ozero;
+                ad1 += delta;
+                adt += delta;
+        } else {
+                ad2 = addr2;
+                for(ad1 = addr1; ad1 <= ad2;)
+                        *ad1++ &= ~01;
+                ad1 = addr1;
+        }
+        ad2++;
+        if(adt= ad2) {
+                dot = adt++;
+                reverse(ad1, ad2);
+                reverse(ad2, adt);
+                reverse(ad1, adt);
+        } else
+                error(Q);
+        fchange = 1;
+}
+
+void
+reverse(int *a1, int *a2)
+{
+        int t;
+
+        for(;;) {
+                t = *--a2;
+                if(a2 <= a1)
+                        return;
+                *a2 = *a1;
+                *a1++ = t;
+        }
+}
+
+int
+getcopy(void)
+{
+        if(addr1 > addr2)
+                return EOF;
+        getline(*addr1++);
+        return 0;
+}
+
+void
+compile(int eof)
+{
+        Rune c;
+        char *ep;
+        char expbuf[ESIZE];
+
+        if((c = getchr()) == '\n') {
+                peekc = c;
+                c = eof;
+        }
+        if(c == eof) {
+                if(!pattern)
+                        error(Q);
+                return;
+        }
+        if(pattern) {
+                free(pattern);
+                pattern = 0;
+        }
+        ep = expbuf;
+        do {
+                if(c == '\\') {
+                        if(ep >= expbuf+sizeof(expbuf)) {
+                                error(Q);
+                                return;
+                        }
+                        ep += runetochar(ep, &c);
+                        if((c = getchr()) == '\n') {
+                                error(Q);
+                                return;
+                        }
+                }
+                if(ep >= expbuf+sizeof(expbuf)) {
+                        error(Q);
+                        return;
+                }
+                ep += runetochar(ep, &c);
+        } while((c = getchr()) != eof && c != '\n');
+        if(c == '\n')
+                peekc = c;
+        *ep = 0;
+        pattern = regcomp(expbuf);
+}
+
+int
+match(int *addr)
+{
+        if(!pattern)
+                return 0;
+        if(addr){
+                if(addr == zero)
+                        return 0;
+                subexp[0].s.rsp = getline(*addr);
+        } else
+                subexp[0].s.rsp = loc2;
+        subexp[0].e.rep = 0;
+        if(rregexec(pattern, linebuf, subexp, MAXSUB)) {
+                loc1 = subexp[0].s.rsp;
+                loc2 = subexp[0].e.rep;
+                return 1;
+        }
+        loc1 = loc2 = 0;
+        return 0;
+        
+}
+
+void
+putd(void)
+{
+        int r;
+
+        r = count%10;
+        count /= 10;
+        if(count)
+                putd();
+        putchr(r + L'0');
+}
+
+void
+putst(char *sp)
+{
+        Rune r;
+
+        col = 0;
+        for(;;) {
+                sp += chartorune(&r, sp);
+                if(r == 0)
+                        break;
+                putchr(r);
+        }
+        putchr(L'\n');
+}
+
+void
+putshst(Rune *sp)
+{
+        col = 0;
+        while(*sp)
+                putchr(*sp++);
+        putchr(L'\n');
+}
+
+void
+putchr(int ac)
+{
+        char *lp;
+        int c;
+        Rune rune;
+
+        lp = linp;
+        c = ac;
+        if(listf) {
+                if(c == '\n') {
+                        if(linp != line && linp[-1] == ' ') {
+                                *lp++ = '\\';
+                                *lp++ = 'n';
+                        }
+                } else {
+                        if(col > (72-6-2)) {
+                                col = 8;
+                                *lp++ = '\\';
+                                *lp++ = '\n';
+                                *lp++ = '\t';
+                        }
+                        col++;
+                        if(c=='\b' || c=='\t' || c=='\\') {
+                                *lp++ = '\\';
+                                if(c == '\b')
+                                        c = 'b';
+                                else
+                                if(c == '\t')
+                                        c = 't';
+                                col++;
+                        } else
+                        if(c<' ' || c>='\177') {
+                                *lp++ = '\\';
+                                *lp++ = 'x';
+                                *lp++ =  hex[c>>12];
+                                *lp++ =  hex[c>>8&0xF];
+                                *lp++ =  hex[c>>4&0xF];
+                                c     =  hex[c&0xF];
+                                col += 5;
+                        }
+                }
+        }
+
+        rune = c;
+        lp += runetochar(lp, &rune);
+
+        if(c == '\n' || lp >= &line[sizeof(line)-5]) {
+                linp = line;
+                write(oflag? 2: 1, line, lp-line);
+                return;
+        }
+        linp = lp;
+}
+
+char*
+mktemp(char *as)
+{
+        char *s;
+        unsigned pid;
+        int i;
+
+        pid = getpid();
+        s = as;
+        while(*s++)
+                ;
+        s--;
+        while(*--s == 'X') {
+                *s = pid % 10 + '0';
+                pid /= 10;
+        }
+        s++;
+        i = 'a';
+        while(access(as, 0) != -1) {
+                if(i == 'z')
+                        return "/";
+                *s = i++;
+        }
+        return as;
+}
+
+void
+regerror(char *s)
+{
+        USED(s);
+        error(Q);
+}
diff --git a/src/cmd/factor.c b/src/cmd/factor.c
t@@ -0,0 +1,96 @@
+#include 
+#include 
+#include 
+
+#define        whsiz        (sizeof(wheel)/sizeof(wheel[0]))
+
+double        wheel[] =
+{
+         2,10, 2, 4, 2, 4, 6, 2, 6, 4,
+         2, 4, 6, 6, 2, 6, 4, 2, 6, 4,
+         6, 8, 4, 2, 4, 2, 4, 8, 6, 4,
+         6, 2, 4, 6, 2, 6, 6, 4, 2, 4,
+         6, 2, 6, 4, 2, 4, 2,10,
+};
+
+Biobuf        bin;
+
+void        factor(double);
+
+void
+main(int argc, char *argv[])
+{
+        double n;
+        int i;
+        char *l;
+
+        if(argc > 1) {
+                for(i=1; i= whsiz) {
+                        i = 0;
+                        if(d > s)
+                                break;
+                }
+        }
+        if(n > 1)
+                print("     %.0f\n",n);
+        print("\n");
+}
diff --git a/src/cmd/freq.c b/src/cmd/freq.c
t@@ -0,0 +1,111 @@
+#include 
+#include 
+#include 
+
+long        count[1<<16];
+Biobuf        bout;
+
+void        freq(int, char*);
+long        flag;
+enum
+{
+        Fdec        = 1<<0,
+        Fhex        = 1<<1,
+        Foct        = 1<<2,
+        Fchar        = 1<<3,
+        Frune        = 1<<4,
+};
+
+void
+main(int argc, char *argv[])
+{
+        int f, i;
+
+        flag = 0;
+        Binit(&bout, 1, OWRITE);
+        ARGBEGIN{
+        default:
+                fprint(2, "freq: unknown option %c\n", ARGC());
+                exits("usage");
+        case 'd':
+                flag |= Fdec;
+                break;
+        case 'x':
+                flag |= Fhex;
+                break;
+        case 'o':
+                flag |= Foct;
+                break;
+        case 'c':
+                flag |= Fchar;
+                break;
+        case 'r':
+                flag |= Frune;
+                break;
+        }ARGEND
+        if((flag&(Fdec|Fhex|Foct|Fchar)) == 0)
+                flag |= Fdec | Fhex | Foct | Fchar;
+        if(argc < 1) {
+                freq(0, "-");
+                exits(0);
+        }
+        for(i=0; i= 0x7f && i < 0xa0 ||
+                           i > 0xff && !(flag & Frune))
+                                Bprint(&bout, "- ");
+                        else
+                                Bprint(&bout, "%C ", (int)i);
+                }
+                Bprint(&bout, "%8ld\n", count[i]);
+        }
+        Bflush(&bout);
+}
diff --git a/src/cmd/fsize.c b/src/cmd/fsize.c
t@@ -0,0 +1,32 @@
+#include 
+#include 
+
+void
+usage(void)
+{
+        fprint(2, "usage: fsize file...\n");
+        exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+        int i;
+        Dir *d;
+
+        ARGBEGIN{
+        default:
+                usage();
+        }ARGEND
+        if(argc == 0)
+                usage();
+
+        for(i=0; ilength);
+                        free(d);
+                }
+        }
+}
diff --git a/src/cmd/idiff.c b/src/cmd/idiff.c
t@@ -0,0 +1,335 @@
+/*
+ * interactive diff, inspired/stolen from
+ * kernighan and pike, _unix programming environment_.
+ */
+
+#include 
+#include 
+#include 
+
+int diffbflag;
+int diffwflag;
+
+void copy(Biobuf*, char*, Biobuf*, char*);
+void idiff(Biobuf*, char*, Biobuf*, char*, Biobuf*, char*, Biobuf*, char*);
+int opentemp(char*, int, long);
+void rundiff(char*, char*, int);
+
+void
+usage(void)
+{
+        fprint(2, "usage: idiff [-bw] file1 file2\n");
+        exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+        int fd, ofd;
+        char diffout[40], idiffout[40];
+        Biobuf *b1, *b2, bdiff, bout, bstdout;
+        Dir *d;
+
+        ARGBEGIN{
+        default:
+                usage();
+        case 'b':
+                diffbflag++;
+                break;
+        case 'w':
+                diffwflag++;
+                break;
+        }ARGEND
+
+        if(argc != 2)
+                usage();
+
+        if((d = dirstat(argv[0])) == nil)
+                sysfatal("stat %s: %r", argv[0]);
+        if(d->mode&DMDIR)
+                sysfatal("%s is a directory", argv[0]);
+        free(d);
+        if((d = dirstat(argv[1])) == nil)
+                sysfatal("stat %s: %r", argv[1]);
+        if(d->mode&DMDIR)
+                sysfatal("%s is a directory", argv[1]);
+        free(d);
+
+        if((b1 = Bopen(argv[0], OREAD)) == nil)
+                sysfatal("open %s: %r", argv[0]);
+        if((b2 = Bopen(argv[1], OREAD)) == nil)
+                sysfatal("open %s: %r", argv[1]);
+
+        strcpy(diffout, "/tmp/idiff.XXXXXX");
+        fd = opentemp(diffout, ORDWR|ORCLOSE, 0);
+        strcpy(idiffout, "/tmp/idiff.XXXXXX");
+        ofd = opentemp(idiffout, ORDWR|ORCLOSE, 0);
+        rundiff(argv[0], argv[1], fd);
+        seek(fd, 0, 0);
+        Binit(&bdiff, fd, OREAD);
+        Binit(&bout, ofd, OWRITE);
+        idiff(b1, argv[0], b2, argv[1], &bdiff, diffout, &bout, idiffout);
+        Bterm(&bdiff);
+        Bflush(&bout);
+        seek(ofd, 0, 0);
+        Binit(&bout, ofd, OREAD);
+        Binit(&bstdout, 1, OWRITE);
+        copy(&bout, idiffout, &bstdout, "");
+        exits(nil);
+}
+
+int
+opentemp(char *template, int mode, long perm)
+{
+        int fd, i;
+        char *p;        
+
+        p = strdup(template);
+        if(p == nil)
+                sysfatal("strdup out of memory");
+        fd = -1;
+        for(i=0; i<10; i++){
+                mktemp(p);
+                if(access(p, 0) < 0 && (fd=create(p, mode, perm)) >= 0)
+                        break;
+                strcpy(p, template);
+        }
+        if(fd < 0)
+                sysfatal("could not create temporary file");
+        strcpy(template, p);
+        free(p);
+
+        return fd;
+}
+
+void
+rundiff(char *arg1, char *arg2, int outfd)
+{
+        char *arg[10], *p;
+        int narg, pid;
+        Waitmsg *w;
+
+        narg = 0;
+        arg[narg++] = "/bin/diff";
+        arg[narg++] = "-n";
+        if(diffbflag)
+                arg[narg++] = "-b";
+        if(diffwflag)
+                arg[narg++] = "-w";
+        arg[narg++] = arg1;
+        arg[narg++] = arg2;
+        arg[narg] = nil;
+
+        switch(pid = fork()){
+        case -1:
+                sysfatal("fork: %r");
+
+        case 0:
+                dup(outfd, 1);
+                close(0);
+                exec("/bin/diff", arg);
+                sysfatal("exec: %r");
+
+        default:
+                w = wait();
+                if(w==nil)
+                        sysfatal("wait: %r");
+                if(w->pid != pid)
+                        sysfatal("wait got unexpected pid %d", w->pid);
+                if((p = strchr(w->msg, ':')) && strcmp(p, ": some") != 0)
+                        sysfatal("%s", w->msg);
+                free(w);
+        }
+}
+
+void
+runcmd(char *cmd)
+{
+        char *arg[10];
+        int narg, pid, wpid;
+
+        narg = 0;
+        arg[narg++] = "/bin/rc";
+        arg[narg++] = "-c";
+        arg[narg++] = cmd;
+        arg[narg] = nil;
+
+        switch(pid = fork()){
+        case -1:
+                sysfatal("fork: %r");
+
+        case 0:
+                exec("/bin/rc", arg);
+                sysfatal("exec: %r");
+
+        default:
+                wpid = waitpid();
+                if(wpid < 0)
+                        sysfatal("wait: %r");
+                if(wpid != pid)
+                        sysfatal("wait got unexpected pid %d", wpid);
+        }
+}
+
+void
+parse(char *s, int *pfrom1, int *pto1, int *pcmd, int *pfrom2, int *pto2)
+{
+        *pfrom1 = *pto1 = *pfrom2 = *pto2 = 0;
+
+        s = strchr(s, ':');
+        if(s == nil)
+                sysfatal("bad diff output0");
+        s++;
+        *pfrom1 = strtol(s, &s, 10);
+        if(*s == ','){
+                s++;
+                *pto1 = strtol(s, &s, 10);
+        }else
+                *pto1 = *pfrom1;
+        if(*s++ != ' ')
+                sysfatal("bad diff output1");
+        *pcmd = *s++;
+        if(*s++ != ' ')
+                sysfatal("bad diff output2");
+        s = strchr(s, ':');
+        if(s == nil)
+                sysfatal("bad diff output3");
+        s++;
+        *pfrom2 = strtol(s, &s, 10);
+        if(*s == ','){
+                s++;
+                *pto2 = strtol(s, &s, 10);
+        }else
+                *pto2 = *pfrom2;
+}
+
+void
+skiplines(Biobuf *b, char *name, int n)
+{
+        int i;
+
+        for(i=0; i sizeof buf)
+                                m = sizeof buf;
+                        m = Bread(bin, buf, m);
+                        if(Bwrite(bout, buf, m) != m)
+                                sysfatal("error writing %s: %r", nout);
+                }
+                if(Bwrite(bout, p, Blinelen(bin)) != Blinelen(bin))
+                        sysfatal("error writing %s: %r", nout);
+        }
+}
+
+void
+copy(Biobuf *bin, char *nin, Biobuf *bout, char *nout)
+{
+        char buf[4096];
+        int m;
+
+        USED(nin);
+        while((m = Bread(bin, buf, sizeof buf)) > 0)
+                if(Bwrite(bout, buf, m) != m)
+                        sysfatal("error writing %s: %r", nout);
+}
+
+void
+idiff(Biobuf *b1, char *name1, Biobuf *b2, char *name2, Biobuf *bdiff, char *namediff, Biobuf *bout, char *nameout)
+{
+        char buf[256], *p;
+        int interactive, defaultanswer, cmd, diffoffset;
+        int n, from1, to1, from2, to2, nf1, nf2;
+        Biobuf berr;
+
+        nf1 = 1;
+        nf2 = 1;
+        interactive = 1;
+        defaultanswer = 0;
+        Binit(&berr, 2, OWRITE);
+        while(diffoffset = Boffset(bdiff), p = Brdline(bdiff, '\n')){
+                p[Blinelen(bdiff)-1] = '\0';
+                parse(p, &from1, &to1, &cmd, &from2, &to2);
+                p[Blinelen(bdiff)-1] = '\n';
+                n = to1-from1 + to2-from2 + 1;        /* #lines from diff */
+                if(cmd == 'c')
+                        n += 2;
+                else if(cmd == 'a')
+                        from1++;
+                else if(cmd == 'd')
+                        from2++;
+                to1++;        /* make half-open intervals */
+                to2++;
+                if(interactive){
+                        p[Blinelen(bdiff)-1] = '\0';
+                        fprint(2, "%s\n", p);
+                        p[Blinelen(bdiff)-1] = '\n';
+                        copylines(bdiff, namediff, &berr, "", n);
+                        Bflush(&berr);
+                }else
+                        skiplines(bdiff, namediff, n);
+                do{
+                        if(interactive){
+                                fprint(2, "? ");
+                                memset(buf, 0, sizeof buf);
+                                if(read(0, buf, sizeof buf - 1) < 0)
+                                        sysfatal("read console: %r");
+                        }else
+                                buf[0] = defaultanswer;
+
+                        switch(buf[0]){
+                        case '>':
+                                copylines(b1, name1, bout, nameout, from1-nf1);
+                                skiplines(b1, name1, to1-from1);
+                                skiplines(b2, name2, from2-nf2);
+                                copylines(b2, name2, bout, nameout, to2-from2);
+                                break;
+                        case '<':
+                                copylines(b1, name1, bout, nameout, to1-nf1);
+                                skiplines(b2, name2, to2-nf2);
+                                break;
+                        case '=':
+                                copylines(b1, name1, bout, nameout, from1-nf1);
+                                skiplines(b1, name1, to1-from1);
+                                skiplines(b2, name2, to2-nf2);
+                                if(Bseek(bdiff, diffoffset, 0) != diffoffset)
+                                        sysfatal("seek in diff output: %r");
+                                copylines(bdiff, namediff, bout, nameout, n+1);
+                                break;
+                        case '!':
+                                runcmd(buf+1);
+                                break;
+                        case 'q':
+                                if(buf[1]=='<' || buf[1]=='>' || buf[1]=='='){
+                                        interactive = 0;
+                                        defaultanswer = buf[1];
+                                }else
+                                        fprint(2, "must be q<, q>, or q=\n");
+                                break;
+                        default:
+                                fprint(2, "expect: <, >, =, q<, q>, q=, !cmd\n");
+                                break;
+                        }
+                }while(buf[0] != '<' && buf[0] != '>' && buf[0] != '=');
+                nf1 = to1;
+                nf2 = to2;
+        }
+        copy(b1, name1, bout, nameout);
+}
diff --git a/src/cmd/join.c b/src/cmd/join.c
t@@ -0,0 +1,369 @@
+/*        join F1 F2 on stuff */
+#include 
+#include 
+#include 
+#include 
+#define F1 0
+#define F2 1
+#define F0 3
+#define        NFLD        100        /* max field per line */
+#define comp() runecmp(ppi[F1][j1],ppi[F2][j2])
+FILE *f[2];
+Rune buf[2][BUFSIZ];        /*input lines */
+Rune *ppi[2][NFLD+1];        /* pointers to fields in lines */
+Rune *s1,*s2;
+#define j1 joinj1
+#define j2 joinj2
+
+int        j1        = 1;        /* join of this field of file 1 */
+int        j2        = 1;        /* join of this field of file 2 */
+int        olist[2*NFLD];        /* output these fields */
+int        olistf[2*NFLD];        /* from these files */
+int        no;                /* number of entries in olist */
+Rune        sep1        = ' ';        /* default field separator */
+Rune        sep2        = '\t';
+char *sepstr=" ";
+int        discard;        /* count of truncated lines */
+Rune        null[BUFSIZ]/*        = L""*/;
+int        a1;
+int         a2;
+
+char *getoptarg(int*, char***);
+void output(int, int);
+int input(int);
+void oparse(char*);
+void error(char*, char*);
+void seek1(void), seek2(void);
+Rune *strtorune(Rune *, char *);
+
+
+void
+main(int argc, char **argv)
+{
+        int i;
+
+        while (argc > 1 && argv[1][0] == '-') {
+                if (argv[1][1] == '\0')
+                        break;
+                switch (argv[1][1]) {
+                case '-':
+                        argc--;
+                        argv++;
+                        goto proceed;
+                case 'a':
+                        switch(*getoptarg(&argc, &argv)) {
+                        case '1':
+                                a1++;
+                                break;
+                        case '2':
+                                a2++;
+                                break;
+                        default:
+                                error("incomplete option -a","");
+                        }
+                        break;
+                case 'e':
+                        strtorune(null, getoptarg(&argc, &argv));
+                        break;
+                case 't':
+                        sepstr=getoptarg(&argc, &argv);
+                        chartorune(&sep1, sepstr);
+                        sep2 = sep1;
+                        break;
+                case 'o':
+                        if(argv[1][2]!=0 ||
+                           argc>2 && strchr(argv[2],',')!=0)
+                                oparse(getoptarg(&argc, &argv));
+                        else for (no = 0; no<2*NFLD && argc>2; no++){
+                                if (argv[2][0] == '1' && argv[2][1] == '.') {
+                                        olistf[no] = F1;
+                                        olist[no] = atoi(&argv[2][2]);
+                                } else if (argv[2][0] == '2' && argv[2][1] == '.') {
+                                        olist[no] = atoi(&argv[2][2]);
+                                        olistf[no] = F2;
+                                } else if (argv[2][0] == '0')
+                                        olistf[no] = F0;
+                                else
+                                        break;
+                                argc--;
+                                argv++;
+                        }
+                        break;
+                case 'j':
+                        if(argc <= 2)
+                                break;
+                        if (argv[1][2] == '1')
+                                j1 = atoi(argv[2]);
+                        else if (argv[1][2] == '2')
+                                j2 = atoi(argv[2]);
+                        else
+                                j1 = j2 = atoi(argv[2]);
+                        argc--;
+                        argv++;
+                        break;
+                case '1':
+                        j1 = atoi(getoptarg(&argc, &argv));
+                        break;
+                case '2':
+                        j2 = atoi(getoptarg(&argc, &argv));
+                        break;
+                }
+                argc--;
+                argv++;
+        }
+proceed:
+        for (i = 0; i < no; i++)
+                if (olist[i]-- > NFLD)        /* 0 origin */
+                        error("field number too big in -o","");
+        if (argc != 3)
+                error("usage: join [-1 x -2 y] [-o list] file1 file2","");
+        j1--;
+        j2--;        /* everyone else believes in 0 origin */
+        s1 = ppi[F1][j1];
+        s2 = ppi[F2][j2];
+        if (strcmp(argv[1], "-") == 0)
+                f[F1] = stdin;
+        else if ((f[F1] = fopen(argv[1], "r")) == 0)
+                error("can't open %s", argv[1]);
+        if(strcmp(argv[2], "-") == 0) {
+                f[F2] = stdin;
+        } else if ((f[F2] = fopen(argv[2], "r")) == 0)
+                error("can't open %s", argv[2]);
+
+        if(ftell(f[F2]) >= 0)
+                seek2();
+        else if(ftell(f[F1]) >= 0)
+                seek1();
+        else
+                error("neither file is randomly accessible","");
+        if (discard)
+                error("some input line was truncated", "");
+        exits("");
+}
+int runecmp(Rune *a, Rune *b){
+        while(*a==*b){
+                if(*a=='\0') return 0;
+                a++;
+                b++;
+        }
+        if(*a<*b) return -1;
+        return 1;
+}
+char *runetostr(char *buf, Rune *r){
+        char *s;
+        for(s=buf;*r;r++) s+=runetochar(s, r);
+        *s='\0';
+        return buf;
+}
+Rune *strtorune(Rune *buf, char *s){
+        Rune *r;
+        for(r=buf;*s;r++) s+=chartorune(r, s);
+        *r='\0';
+        return buf;
+}
+/* lazy.  there ought to be a clean way to combine seek1 & seek2 */
+#define get1() n1=input(F1)
+#define get2() n2=input(F2)
+void
+seek2()
+{
+        int n1, n2;
+        int top2=0;
+        int bot2 = ftell(f[F2]);
+        get1();
+        get2();
+        while(n1>0 && n2>0 || (a1||a2) && n1+n2>0) {
+                if(n1>0 && n2>0 && comp()>0 || n1==0) {
+                        if(a2) output(0, n2);
+                        bot2 = ftell(f[F2]);
+                        get2();
+                } else if(n1>0 && n2>0 && comp()<0 || n2==0) {
+                        if(a1) output(n1, 0);
+                        get1();
+                } else /*(n1>0 && n2>0 && comp()==0)*/ {
+                        while(n2>0 && comp()==0) {
+                                output(n1, n2);
+                                top2 = ftell(f[F2]);
+                                get2();
+                        }
+                        fseek(f[F2], bot2, 0);
+                        get2();
+                        get1();
+                        for(;;) {
+                                if(n1>0 && n2>0 && comp()==0) {
+                                        output(n1, n2);
+                                        get2();
+                                } else if(n1>0 && n2>0 && comp()<0 || n2==0) {
+                                        fseek(f[F2], bot2, 0);
+                                        get2();
+                                        get1();
+                                } else /*(n1>0 && n2>0 && comp()>0 || n1==0)*/{
+                                        fseek(f[F2], top2, 0);
+                                        bot2 = top2;
+                                        get2();
+                                        break;
+                                }
+                        }
+                }
+        }
+}
+void
+seek1()
+{
+        int n1, n2;
+        int top1=0;
+        int bot1 = ftell(f[F1]);
+        get1();
+        get2();
+        while(n1>0 && n2>0 || (a1||a2) && n1+n2>0) {
+                if(n1>0 && n2>0 && comp()>0 || n1==0) {
+                        if(a2) output(0, n2);
+                        get2();
+                } else if(n1>0 && n2>0 && comp()<0 || n2==0) {
+                        if(a1) output(n1, 0);
+                        bot1 = ftell(f[F1]);
+                        get1();
+                } else /*(n1>0 && n2>0 && comp()==0)*/ {
+                        while(n2>0 && comp()==0) {
+                                output(n1, n2);
+                                top1 = ftell(f[F1]);
+                                get1();
+                        }
+                        fseek(f[F1], bot1, 0);
+                        get2();
+                        get1();
+                        for(;;) {
+                                if(n1>0 && n2>0 && comp()==0) {
+                                        output(n1, n2);
+                                        get1();
+                                } else if(n1>0 && n2>0 && comp()>0 || n1==0) {
+                                        fseek(f[F1], bot1, 0);
+                                        get2();
+                                        get1();
+                                } else /*(n1>0 && n2>0 && comp()<0 || n2==0)*/{
+                                        fseek(f[F1], top1, 0);
+                                        bot1 = top1;
+                                        get1();
+                                        break;
+                                }
+                        }
+                }
+        }
+}
+
+int
+input(int n)                /* get input line and split into fields */
+{
+        register int i, c;
+        Rune *bp;
+        Rune **pp;
+        char line[BUFSIZ];
+
+        bp = buf[n];
+        pp = ppi[n];
+        if (fgets(line, BUFSIZ, f[n]) == 0)
+                return(0);
+        strtorune(bp, line);
+        i = 0;
+        do {
+                i++;
+                if (sep1 == ' ')        /* strip multiples */
+                        while ((c = *bp) == sep1 || c == sep2)
+                                bp++;        /* skip blanks */
+                *pp++ = bp;        /* record beginning */
+                while ((c = *bp) != sep1 && c != '\n' && c != sep2 && c != '\0')
+                        bp++;
+                *bp++ = '\0';        /* mark end by overwriting blank */
+        } while (c != '\n' && c != '\0' && i < NFLD-1);
+        if (c != '\n')
+                discard++;
+
+        *pp = 0;
+        return(i);
+}
+
+void
+output(int on1, int on2)        /* print items from olist */
+{
+        int i;
+        Rune *temp;
+        char buf[BUFSIZ];
+
+        if (no <= 0) {        /* default case */
+                printf("%s", runetostr(buf, on1? ppi[F1][j1]: ppi[F2][j2]));
+                for (i = 0; i < on1; i++)
+                        if (i != j1)
+                                printf("%s%s", sepstr, runetostr(buf, ppi[F1][i]));
+                for (i = 0; i < on2; i++)
+                        if (i != j2)
+                                printf("%s%s", sepstr, runetostr(buf, ppi[F2][i]));
+                printf("\n");
+        } else {
+                for (i = 0; i < no; i++) {
+                        if (olistf[i]==F0 && on1>j1)
+                                temp = ppi[F1][j1];
+                        else if (olistf[i]==F0 && on2>j2)
+                                temp = ppi[F2][j2];
+                        else {
+                                temp = ppi[olistf[i]][olist[i]];
+                                if(olistf[i]==F1 && on1<=olist[i] ||
+                                   olistf[i]==F2 && on2<=olist[i] ||
+                                   *temp==0)
+                                        temp = null;
+                        }
+                        printf("%s", runetostr(buf, temp));
+                        if (i == no - 1)
+                                printf("\n");
+                        else
+                                printf("%s", sepstr);
+                }
+        }
+}
+
+void
+error(char *s1, char *s2)
+{
+        fprintf(stderr, "join: ");
+        fprintf(stderr, s1, s2);
+        fprintf(stderr, "\n");
+        exits(s1);
+}
+
+char *
+getoptarg(int *argcp, char ***argvp)
+{
+        int argc = *argcp;
+        char **argv = *argvp;
+        if(argv[1][2] != 0)
+                return &argv[1][2];
+        if(argc<=2 || argv[2][0]=='-')
+                error("incomplete option %s", argv[1]);
+        *argcp = argc-1;
+        *argvp = ++argv;
+        return argv[1];
+}
+
+void
+oparse(char *s)
+{
+        for (no = 0; no<2*NFLD && *s; no++, s++) {
+                switch(*s) {
+                case 0:
+                        return;
+                case '0':
+                        olistf[no] = F0;
+                        break;
+                case '1':
+                case '2':
+                        if(s[1] == '.' && isdigit(s[2])) {
+                                olistf[no] = *s=='1'? F1: F2;
+                                olist[no] = atoi(s += 2);
+                                break;
+                        } /* fall thru */
+                default:
+                        error("invalid -o list", "");
+                }
+                if(s[1] == ',')
+                        s++;
+        }
+}
diff --git a/src/cmd/ls.C b/src/cmd/ls.C
t@@ -0,0 +1,305 @@
+#include 
+#include 
+#include 
+
+typedef struct NDir NDir;
+struct NDir
+{
+        Dir *d;
+        char        *prefix;
+};
+
+int        errs = 0;
+int        dflag;
+int        lflag;
+int        mflag;
+int        nflag;
+int        pflag;
+int        qflag;
+int        Qflag;
+int        rflag;
+int        sflag;
+int        tflag;
+int        uflag;
+int        Fflag;
+int        ndirbuf;
+int        ndir;
+NDir*        dirbuf;
+int        ls(char*, int);
+int        compar(NDir*, NDir*);
+char*        asciitime(long);
+char*        darwx(long);
+void        rwx(long, char*);
+void        growto(long);
+void        dowidths(Dir*);
+void        format(Dir*, char*);
+void        output(void);
+ulong        clk;
+int        swidth;                        /* max width of -s size */
+int        qwidth;                        /* max width of -q version */
+int        vwidth;                        /* max width of dev */
+int        uwidth;                        /* max width of userid */
+int        mwidth;                        /* max width of muid */
+int        glwidth;                /* max width of groupid and length */
+Biobuf        bin;
+
+void
+main(int argc, char *argv[])
+{
+        int i;
+
+        Binit(&bin, 1, OWRITE);
+        ARGBEGIN{
+        case 'F':        Fflag++; break;
+        case 'd':        dflag++; break;
+        case 'l':        lflag++; break;
+        case 'm':        mflag++; break;
+        case 'n':        nflag++; break;
+        case 'p':        pflag++; break;
+        case 'q':        qflag++; break;
+        case 'Q':        Qflag++; break;
+        case 'r':        rflag++; break;
+        case 's':        sflag++; break;
+        case 't':        tflag++; break;
+        case 'u':        uflag++; break;
+        default:        fprint(2, "usage: ls [-dlmnpqrstuFQ] [file ...]\n");
+                        exits("usage");
+        }ARGEND
+
+        doquote = needsrcquote;
+        quotefmtinstall();
+        fmtinstall('M', dirmodefmt);
+
+        if(lflag)
+                clk = time(0);
+        if(argc == 0)
+                errs = ls(".", 0);
+        else for(i=0; iqid.type&QTDIR && dflag==0){
+                output();
+                fd = open(s, OREAD);
+                if(fd == -1)
+                        goto error;
+                n = dirreadall(fd, &db);
+                if(n < 0)
+                        goto error;
+                growto(ndir+n);
+                for(i=0; iname = strdup(p+1);
+                }
+                ndir++;
+        }
+        return 0;
+}
+
+void
+output(void)
+{
+        int i;
+        char buf[4096];
+        char *s;
+
+        if(!nflag)
+                qsort(dirbuf, ndir, sizeof dirbuf[0], (int (*)(const void*, const void*))compar);
+        for(i=0; iname);
+                        format(dirbuf[i].d, buf);
+                } else
+                        format(dirbuf[i].d, dirbuf[i].d->name);
+        }
+        ndir = 0;
+        Bflush(&bin);
+}
+
+void
+dowidths(Dir *db)
+{
+        char buf[256];
+        int n;
+
+        if(sflag) {
+                n = sprint(buf, "%llud", (db->length+1023)/1024);
+                if(n > swidth)
+                        swidth = n;
+        }
+        if(qflag) {
+                n = sprint(buf, "%lud", db->qid.vers);
+                if(n > qwidth)
+                        qwidth = n;
+        }
+        if(mflag) {
+                n = snprint(buf, sizeof buf, "[%s]", db->muid);
+                if(n > mwidth)
+                        mwidth = n;
+        }
+        if(lflag) {
+                n = sprint(buf, "%ud", db->dev);
+                if(n > vwidth)
+                        vwidth = n;
+                n = strlen(db->uid);
+                if(n > uwidth)
+                        uwidth = n;
+                n = sprint(buf, "%llud", db->length);
+                n += strlen(db->gid);
+                if(n > glwidth)
+                        glwidth = n;
+        }
+}
+
+char*
+fileflag(Dir *db)
+{
+        if(Fflag == 0)
+                return "";
+        if(QTDIR & db->qid.type)
+                return "/";
+        if(0111 & db->mode)
+                return "*";
+        return "";
+}
+
+void
+format(Dir *db, char *name)
+{
+        int i;
+
+        if(sflag)
+                Bprint(&bin, "%*llud ",
+                        swidth, (db->length+1023)/1024);
+        if(mflag){
+                Bprint(&bin, "[%s] ", db->muid);
+                for(i=2+strlen(db->muid); iqid.path,
+                        qwidth, db->qid.vers,
+                        db->qid.type);
+        if(lflag)
+                Bprint(&bin,
+                        Qflag? "%M %C %*ud %*s %s %*llud %s %s\n" : "%M %C %*ud %*s %s %*llud %s %q\n",
+                        db->mode, db->type,
+                        vwidth, db->dev,
+                        -uwidth, db->uid,
+                        db->gid,
+                        (int)(glwidth-strlen(db->gid)), db->length,
+                        asciitime(uflag? db->atime : db->mtime), name);
+        else
+                Bprint(&bin,
+                        Qflag? "%s%s\n" : "%q%s\n",
+                        name, fileflag(db));
+}
+
+void
+growto(long n)
+{
+        if(n <= ndirbuf)
+                return;
+        ndirbuf = n;
+        dirbuf=(NDir *)realloc(dirbuf, ndirbuf*sizeof(NDir));
+        if(dirbuf == 0){
+                fprint(2, "ls: malloc fail\n");
+                exits("malloc fail");
+        }                
+}
+
+int
+compar(NDir *a, NDir *b)
+{
+        long i;
+        Dir *ad, *bd;
+
+        ad = a->d;
+        bd = b->d;
+
+        if(tflag){
+                if(uflag)
+                        i = bd->atime-ad->atime;
+                else
+                        i = bd->mtime-ad->mtime;
+        }else{
+                if(a->prefix && b->prefix){
+                        i = strcmp(a->prefix, b->prefix);
+                        if(i == 0)
+                                i = strcmp(ad->name, bd->name);
+                }else if(a->prefix){
+                        i = strcmp(a->prefix, bd->name);
+                        if(i == 0)
+                                i = 1;        /* a is longer than b */
+                }else if(b->prefix){
+                        i = strcmp(ad->name, b->prefix);
+                        if(i == 0)
+                                i = -1;        /* b is longer than a */
+                }else
+                        i = strcmp(ad->name, bd->name);
+        }
+        if(i == 0)
+                i = (a
diff --git a/src/cmd/md5sum.C b/src/cmd/md5sum.C
t@@ -0,0 +1,61 @@
+#include 
+#include 
+#include 
+#include 
+
+static int
+digestfmt(Fmt *fmt)
+{
+        char buf[MD5dlen*2+1];
+        uchar *p;
+        int i;
+
+        p = va_arg(fmt->args, uchar*);
+        for(i=0; i 0)
+                md5(buf, n, nil, s);
+        md5(nil, 0, digest, s);
+        if(name == nil)
+                print("%M\n", digest);
+        else
+                print("%M\t%s\n", digest, name);
+}
+
+void
+main(int argc, char *argv[])
+{
+        int i, fd;
+
+        ARGBEGIN{
+        default:
+                fprint(2, "usage: md5sum [file...]\n");
+                exits("usage");
+        }ARGEND
+
+        fmtinstall('M', digestfmt);
+
+        if(argc == 0)
+                sum(0, nil);
+        else for(i = 0; i < argc; i++){
+                fd = open(argv[i], OREAD);
+                if(fd < 0){
+                        fprint(2, "md5sum: can't open %s: %r\n", argv[i]);
+                        continue;
+                }
+                sum(fd, argv[i]);
+                close(fd);
+        }
+        exits(nil);
+}
diff --git a/src/cmd/mkdir.C b/src/cmd/mkdir.C
t@@ -0,0 +1,26 @@
+#include 
+#include 
+
+void
+main(int argc, char *argv[])
+{
+        int i, f;
+        char *e;
+
+        e = nil;
+        for(i=1; i
diff --git a/src/cmd/mkfile b/src/cmd/mkfile
t@@ -0,0 +1,13 @@
+PLAN9=../..
+<$PLAN9/src/mkhdr
+
+TARG=`ls *.c | sed 's/\.c//'`
+LDFLAGS=$LDFLAGS -lsec -lregexp9 -l9 -lbio -lfmt -lutf
+
+<$PLAN9/src/mkmany
+
+BUGGERED='CVS|oplumb|plumb|plumb2|mk|vac'
+DIRS=`ls -l |sed -n 's/^d.* //p' |egrep -v "$BUGGERED"`
+
+<$PLAN9/src/mkdirs
+
diff --git a/src/cmd/rm.c b/src/cmd/rm.c
t@@ -0,0 +1,104 @@
+#include 
+#include 
+
+#define rmdir p9rmdir
+
+char        errbuf[ERRMAX];
+int        ignerr = 0;
+
+void
+err(char *f)
+{
+        if(!ignerr){
+                errbuf[0] = '\0';
+                errstr(errbuf, sizeof errbuf);
+                fprint(2, "rm: %s: %s\n", f, errbuf);
+        }
+}
+
+/*
+ * f is a non-empty directory. Remove its contents and then it.
+ */
+void
+rmdir(char *f)
+{
+        char *name;
+        int fd, i, j, n, ndir, nname;
+        Dir *dirbuf;
+
+        fd = open(f, OREAD);
+        if(fd < 0){
+                err(f);
+                return;
+        }
+        n = dirreadall(fd, &dirbuf);
+        close(fd);
+        if(n < 0){
+                err("dirreadall");
+                return;
+        }
+
+        nname = strlen(f)+1+STATMAX+1;        /* plenty! */
+        name = malloc(nname);
+        if(name == 0){
+                err("memory allocation");
+                return;
+        }
+
+        ndir = 0;
+        for(i=0; iqid.type&QTDIR))
+                        rmdir(f);
+                else
+                        err(f);
+                free(db);
+        }
+        exits(errbuf);
+}
diff --git a/src/cmd/seq.c b/src/cmd/seq.c
t@@ -0,0 +1,92 @@
+#include 
+#include 
+
+double        min = 1.0;
+double        max = 0.0;
+double        incr = 1.0;
+int        constant = 0;
+int        nsteps;
+char        *format;
+
+void
+usage(void)
+{
+        fprint(2, "usage: seq [-fformat] [-w] [first [incr]] last\n");
+        exits("usage");
+}
+
+void
+buildfmt(void)
+{
+        int i;
+        char *dp;
+        int w, p, maxw, maxp;
+        static char fmt[16];
+        char buf[32];
+
+        format = "%g\n";
+        if(!constant)
+                return;
+        maxw = 0;
+        maxp = 0;
+        for(i=0; i<=nsteps; i++){
+                sprint(buf, "%g", min+i*incr);
+                if(strchr(buf, 'e')!=0)
+                        return;
+                dp = strchr(buf,'.');
+                w = dp==0? strlen(buf): dp-buf;
+                p = dp==0? 0: strlen(strchr(buf,'.')+1);
+                if(w>maxw)
+                        maxw = w;
+                if(p>maxp)
+                        maxp = p;
+        }
+        if(maxp > 0)
+                maxw += maxp+1;
+        sprint(fmt,"%%%d.%df\n", maxw, maxp);
+        format = fmt;
+}
+
+void
+main(int argc, char *argv[]){
+        int i, j, n;
+        char buf[256], ffmt[4096];
+
+        ARGBEGIN{
+        case 'w':
+                constant++;
+                break;
+        case 'f':
+                format = ARGF();
+                if(format[strlen(format)-1] != '\n'){
+                        sprint(ffmt, "%s\n", format);
+                        format = ffmt;
+                }
+                break;
+        default:
+                goto out;
+        }ARGEND
+    out:
+        if(argc<1 || argc>3)
+                usage();
+        max = atof(argv[argc-1]);
+        if(argc > 1)
+                min = atof(argv[0]);
+        if(argc > 2)
+                incr = atof(argv[1]);
+        if(incr == 0){
+                fprint(2, "seq: zero increment\n");
+                exits("zero increment");
+        }
+        nsteps = (max-min)/incr+.5;
+        if(!format)
+                buildfmt();
+        for(i=0; i<=nsteps; i++){
+                n = sprint(buf, format, min+i*incr);
+                if(constant)
+                        for(j=0; buf[j]==' '; j++)
+                                buf[j] ='0';
+                write(1, buf, n);
+        }
+        exits(0);
+}
diff --git a/src/cmd/sha1sum.c b/src/cmd/sha1sum.c
t@@ -0,0 +1,61 @@
+#include 
+#include 
+#include 
+#include 
+
+static int
+digestfmt(Fmt *fmt)
+{
+        char buf[SHA1dlen*2+1];
+        uchar *p;
+        int i;
+
+        p = va_arg(fmt->args, uchar*);
+        for(i=0; i 0)
+                sha1(buf, n, nil, s);
+        sha1(nil, 0, digest, s);
+        if(name == nil)
+                print("%M\n", digest);
+        else
+                print("%M\t%s\n", digest, name);
+}
+
+void
+main(int argc, char *argv[])
+{
+        int i, fd;
+
+        ARGBEGIN{
+        default:
+                fprint(2, "usage: sha1sum [file...]\n");
+                exits("usage");
+        }ARGEND
+
+        fmtinstall('M', digestfmt);
+
+        if(argc == 0)
+                sum(0, nil);
+        else for(i = 0; i < argc; i++){
+                fd = open(argv[i], OREAD);
+                if(fd < 0){
+                        fprint(2, "sha1sum: can't open %s: %r\n", argv[i]);
+                        continue;
+                }
+                sum(fd, argv[i]);
+                close(fd);
+        }
+        exits(nil);
+}
diff --git a/src/cmd/sleep.c b/src/cmd/sleep.c
t@@ -0,0 +1,13 @@
+#include 
+#include 
+
+void
+main(int argc, char *argv[])
+{
+        long secs;
+
+        if(argc>1)
+                for(secs = atol(argv[1]); secs > 0; secs--)
+                        sleep(1000);
+        exits(0);
+}
diff --git a/src/cmd/sort.c b/src/cmd/sort.c
t@@ -0,0 +1,1767 @@
+#include        
+#include        
+#include        
+
+/*
+bugs:
+        00/ff for end of file can conflict with 00/ff characters
+*/
+
+enum
+{
+        Nline        = 100000,                /* default max number of lines saved in memory */
+        Nmerge        = 10,                        /* max number of temporary files merged */
+        Nfield        = 20,                        /* max number of argument fields */
+
+        Bflag        = 1<<0,                        /* flags per field */
+        B1flag        = 1<<1,
+
+        Dflag        = 1<<2,
+        Fflag        = 1<<3,
+        Gflag        = 1<<4,
+        Iflag        = 1<<5,
+        Mflag        = 1<<6,
+        Nflag        = 1<<7,
+        Rflag        = 1<<8,
+        Wflag        = 1<<9,
+
+        NSstart        = 0,                        /* states for number to key decoding */
+        NSsign,
+        NSzero,
+        NSdigit,
+        NSpoint,
+        NSfract,
+        NSzerofract,
+        NSexp,
+        NSexpsign,
+        NSexpdigit,
+};
+
+typedef        struct        Line        Line;
+typedef        struct        Key        Key;
+typedef        struct        Merge        Merge;
+typedef        struct        Field        Field;
+
+struct        Line
+{
+        Key*        key;
+        int        llen;                /* always >= 1 */
+        uchar        line[1];        /* always ends in '\n' */
+};
+
+struct        Merge
+{
+        Key*        key;                /* copy of line->key so (Line*) looks like (Merge*) */
+        Line*        line;                /* line at the head of a merged temp file */
+        int        fd;                /* file descriptor */
+        Biobuf        b;                /* iobuf for reading a temp file */
+};
+
+struct        Key
+{
+        int        klen;
+        uchar        key[1];
+};
+
+struct        Field
+{
+        int        beg1;
+        int        beg2;
+        int        end1;
+        int        end2;
+
+        long        flags;
+        uchar        mapto[256];
+
+        void        (*dokey)(Key*, uchar*, uchar*, Field*);
+};
+
+struct args
+{
+        char*        ofile;
+        char*        tname;
+        Rune        tabchar;
+        char        cflag;
+        char        uflag;
+        char        vflag;
+        int        nfield;
+        int        nfile;
+        Field        field[Nfield];
+
+        Line**        linep;
+        long        nline;                        /* number of lines in this temp file */
+        long        lineno;                        /* overall ordinal for -s option */
+        int        ntemp;
+        long        mline;                        /* max lines per file */
+} args;
+
+extern        int        latinmap[];
+extern        Rune*        month[12];
+
+void        buildkey(Line*);
+void        doargs(int, char*[]);
+void        dofield(char*, int*, int*, int, int);
+void        dofile(Biobuf*);
+void        dokey_(Key*, uchar*, uchar*, Field*);
+void        dokey_dfi(Key*, uchar*, uchar*, Field*);
+void        dokey_gn(Key*, uchar*, uchar*, Field*);
+void        dokey_m(Key*, uchar*, uchar*, Field*);
+void        dokey_r(Key*, uchar*, uchar*, Field*);
+void        done(char*);
+int        kcmp(Key*, Key*);
+void        makemapd(Field*);
+void        makemapm(Field*);
+void        mergefiles(int, int, Biobuf*);
+void        mergeout(Biobuf*);
+void        newfield(void);
+Line*        newline(Biobuf*);
+void        nomem(void);
+void        notifyf(void*, char*);
+void        printargs(void);
+void        printout(Biobuf*);
+void        setfield(int, int);
+uchar*        skip(uchar*, int, int, int, int);
+void        sort4(void*, ulong);
+char*        tempfile(int);
+void        tempout(void);
+void        lineout(Biobuf*, Line*);
+
+void
+main(int argc, char *argv[])
+{
+        int i, f;
+        char *s;
+        Biobuf bbuf;
+
+        notify(notifyf);        /**/
+        doargs(argc, argv);
+        if(args.vflag)
+                printargs();
+
+        for(i=1; ikey, l->key);
+                        if(n > 0 || (n == 0 && args.uflag)) {
+                                fprint(2, "sort: -c file not in sort\n"); /**/
+                                done("order");
+                        }
+                        free(ol->key);
+                        free(ol);
+                        ol = l;
+                }
+                return;
+        }
+
+        if(args.linep == 0) {
+                args.linep = malloc(args.mline * sizeof(args.linep));
+                if(args.linep == 0)
+                        nomem();
+        }
+        for(;;) {
+                l = newline(b);
+                if(l == 0)
+                        break;
+                if(args.nline >= args.mline)
+                        tempout();
+                args.linep[args.nline] = l;
+                args.nline++;
+                args.lineno++;
+        }
+}
+
+void
+notifyf(void *a, char *s)
+{
+        USED(a);
+        if(strcmp(s, "interrupt") == 0)
+                done(0);
+        if(strcmp(s, "hangup") == 0)
+                done(0);
+        if(strcmp(s, "kill") == 0)
+                done(0);
+        if(strncmp(s, "sys: write on closed pipe", 25) == 0)
+                done(0);
+        fprint(2, "sort: note: %s\n", s);
+        abort();
+}
+
+Line*
+newline(Biobuf *b)
+{
+        Line *l;
+        char *p;
+        int n, c;
+
+        p = Brdline(b, '\n');
+        n = Blinelen(b);
+        if(p == 0) {
+                if(n == 0)
+                        return 0;
+                l = 0;
+                for(n=0;;) {
+                        if((n & 31) == 0) {
+                                l = realloc(l, sizeof(Line) +
+                                        (n+31)*sizeof(l->line[0]));
+                                if(l == 0)
+                                        nomem();
+                        }
+                        c = Bgetc(b);
+                        if(c < 0) {
+                                fprint(2, "sort: newline added\n");
+                                c = '\n';
+                        }
+                        l->line[n++] = c;
+                        if(c == '\n')
+                                break;
+                }
+                l->llen = n;
+                buildkey(l);
+                return l;
+        }
+        l = malloc(sizeof(Line) +
+                (n-1)*sizeof(l->line[0]));
+        if(l == 0)
+                nomem();
+        l->llen = n;
+        memmove(l->line, p, n);
+        buildkey(l);
+        return l;
+}
+
+void
+lineout(Biobuf *b, Line *l)
+{
+        int n, m;
+
+        n = l->llen;
+        m = Bwrite(b, l->line, n);
+        if(n != m)
+                exits("write");
+}
+
+void
+tempout(void)
+{
+        long n;
+        Line **lp, *l;
+        char *tf;
+        int f;
+        Biobuf tb;
+
+        sort4(args.linep, args.nline);
+        tf = tempfile(args.ntemp);
+        args.ntemp++;
+        f = create(tf, OWRITE, 0666);
+        if(f < 0) {
+                fprint(2, "sort: create %s: %r\n", tf);
+                done("create");
+        }
+
+        Binit(&tb, f, OWRITE);
+        lp = args.linep;
+        for(n=args.nline; n>0; n--) {
+                l = *lp++;
+                lineout(&tb, l);
+                free(l->key);
+                free(l);
+        }
+        args.nline = 0;
+        Bterm(&tb);
+        close(f);
+}
+
+void
+done(char *xs)
+{
+        int i;
+
+        for(i=0; i= nelem(file)-20) {
+                fprint(2, "temp file directory name is too long: %s\n", dir);
+                done("tdir");
+        }
+
+        if(pid == 0) {
+                pid = getpid();
+                if(pid == 0) {
+                        pid = time(0);
+                        if(pid == 0)
+                                pid = 1;
+                }
+        }
+
+        sprint(file, "%s/sort.%.4d.%.4d", dir, pid%10000, n);
+        return file;
+}
+
+void
+mergeout(Biobuf *b)
+{
+        int n, i, f;
+        char *tf;
+        Biobuf tb;
+
+        for(i=0; i Nmerge) {
+                        tf = tempfile(args.ntemp);
+                        args.ntemp++;
+                        f = create(tf, OWRITE, 0666);
+                        if(f < 0) {
+                                fprint(2, "sort: create %s: %r\n", tf);
+                                done("create");
+                        }
+                        Binit(&tb, f, OWRITE);
+
+                        n = Nmerge;
+                        mergefiles(i, n, &tb);
+
+                        Bterm(&tb);
+                        close(f);
+                } else
+                        mergefiles(i, n, b);
+        }
+}
+
+void
+mergefiles(int t, int n, Biobuf *b)
+{
+        Merge *m, *mp, **mmp;
+        Key *ok;
+        Line *l;
+        char *tf;
+        int i, f, nn;
+
+        mmp = malloc(n*sizeof(*mmp));
+        mp = malloc(n*sizeof(*mp));
+        if(mmp == 0 || mp == 0)
+                nomem();
+
+        nn = 0;
+        m = mp;
+        for(i=0; ifd = f;
+                Binit(&m->b, f, OREAD);
+                mmp[nn] = m;
+
+                l = newline(&m->b);
+                if(l == 0)
+                        continue;
+                nn++;
+                m->line = l;
+                m->key = l->key;
+        }
+
+        ok = 0;
+        for(;;) {
+                sort4(mmp, nn);
+                m = *mmp;
+                if(nn == 0)
+                        break;
+                for(;;) {
+                        l = m->line;
+                        if(args.uflag && ok && kcmp(ok, l->key) == 0) {
+                                free(l->key);
+                                free(l);
+                        } else {
+                                lineout(b, l);
+                                if(ok)
+                                        free(ok);
+                                ok = l->key;
+                                free(l);
+                        }
+
+                        l = newline(&m->b);
+                        if(l == 0) {
+                                nn--;
+                                mmp[0] = mmp[nn];
+                                break;
+                        }
+                        m->line = l;
+                        m->key = l->key;
+                        if(nn > 1 && kcmp(mmp[0]->key, mmp[1]->key) > 0)
+                                break;
+                }
+        }
+        if(ok)
+                free(ok);
+
+        m = mp;
+        for(i=0; ib);
+                close(m->fd);
+        }
+
+        free(mp);
+        free(mmp);
+}
+
+int
+kcmp(Key *ka, Key *kb)
+{
+        int n, m;
+
+        /*
+         * set n to length of smaller key
+         */
+        n = ka->klen;
+        m = kb->klen;
+        if(n > m)
+                n = m;
+        return memcmp(ka->key, kb->key, n);
+}
+
+void
+printout(Biobuf *b)
+{
+        long n;
+        Line **lp, *l;
+        Key *ok;
+
+        sort4(args.linep, args.nline);
+        lp = args.linep;
+        ok = 0;
+        for(n=args.nline; n>0; n--) {
+                l = *lp++;
+                if(args.uflag && ok && kcmp(ok, l->key) == 0)
+                        continue;
+                lineout(b, l);
+                ok = l->key;
+        }
+}
+
+void
+setfield(int n, int c)
+{
+        Field *f;
+
+        f = &args.field[n];
+        switch(c) {
+        default:
+                fprint(2, "sort: unknown option: field.%C\n", c);
+                done("option");
+        case 'b':        /* skip blanks */
+                f->flags |= Bflag;
+                break;
+        case 'd':        /* directory order */
+                f->flags |= Dflag;
+                break;
+        case 'f':        /* fold case */
+                f->flags |= Fflag;
+                break;
+        case 'g':        /* floating point -n case */
+                f->flags |= Gflag;
+                break;
+        case 'i':        /* ignore non-ascii */
+                f->flags |= Iflag;
+                break;
+        case 'M':        /* month */
+                f->flags |= Mflag;
+                break;
+        case 'n':        /* numbers */
+                f->flags |= Nflag;
+                break;
+        case 'r':        /* reverse */
+                f->flags |= Rflag;
+                break;
+        case 'w':        /* ignore white */
+                f->flags |= Wflag;
+                break;
+        }
+}
+
+void
+dofield(char *s, int *n1, int *n2, int off1, int off2)
+{
+        int c, n;
+
+        c = *s++;
+        if(c >= '0' && c <= '9') {
+                n = 0;
+                while(c >= '0' && c <= '9') {
+                        n = n*10 + (c-'0');
+                        c = *s++;
+                }
+                n -= off1;        /* posix committee: rot in hell */
+                if(n < 0) {
+                        fprint(2, "sort: field offset must be positive\n");
+                        done("option");
+                }
+                *n1 = n;
+        }
+        if(c == '.') {
+                c = *s++;
+                if(c >= '0' && c <= '9') {
+                        n = 0;
+                        while(c >= '0' && c <= '9') {
+                                n = n*10 + (c-'0');
+                                c = *s++;
+                        }
+                        n -= off2;
+                        if(n < 0) {
+                                fprint(2, "sort: character offset must be positive\n");
+                                done("option");
+                        }
+                        *n2 = n;
+                }
+        }
+        while(c != 0) {
+                setfield(args.nfield, c);
+                c = *s++;
+        }
+}
+
+void
+printargs(void)
+{
+        int i, n;
+        Field *f;
+        char *prefix;
+
+        fprint(2, "sort");
+        for(i=0; i<=args.nfield; i++) {
+                f = &args.field[i];
+                prefix = " -";
+                if(i) {
+                        n = f->beg1;
+                        if(n >= 0)
+                                fprint(2, " +%d", n);
+                        else
+                                fprint(2, " +*");
+                        n = f->beg2;
+                        if(n >= 0)
+                                fprint(2, ".%d", n);
+                        else
+                                fprint(2, ".*");
+
+                        if(f->flags & B1flag)
+                                fprint(2, "b");
+
+                        n = f->end1;
+                        if(n >= 0)
+                                fprint(2, " -%d", n);
+                        else
+                                fprint(2, " -*");
+                        n = f->end2;
+                        if(n >= 0)
+                                fprint(2, ".%d", n);
+                        else
+                                fprint(2, ".*");
+                        prefix = "";
+                }
+                if(f->flags & Bflag)
+                        fprint(2, "%sb", prefix);
+                if(f->flags & Dflag)
+                        fprint(2, "%sd", prefix);
+                if(f->flags & Fflag)
+                        fprint(2, "%sf", prefix);
+                if(f->flags & Gflag)
+                        fprint(2, "%sg", prefix);
+                if(f->flags & Iflag)
+                        fprint(2, "%si", prefix);
+                if(f->flags & Mflag)
+                        fprint(2, "%sM", prefix);
+                if(f->flags & Nflag)
+                        fprint(2, "%sn", prefix);
+                if(f->flags & Rflag)
+                        fprint(2, "%sr", prefix);
+                if(f->flags & Wflag)
+                        fprint(2, "%sw", prefix);
+        }
+        if(args.cflag)
+                fprint(2, " -c");
+        if(args.uflag)
+                fprint(2, " -u");
+        if(args.ofile)
+                fprint(2, " -o %s", args.ofile);
+        if(args.mline != Nline)
+                fprint(2, " -l %ld", args.mline);
+        fprint(2, "\n");
+}
+
+void
+newfield(void)
+{
+        int n;
+        Field *f;
+
+        n = args.nfield + 1;
+        if(n >= Nfield) {
+                fprint(2, "sort: too many fields specified\n");
+                done("option");
+        }
+        args.nfield = n;
+        f = &args.field[n];
+        f->beg1 = -1;
+        f->beg2 = -1;
+        f->end1 = -1;
+        f->end2 = -1;
+}
+
+void
+doargs(int argc, char *argv[])
+{
+        int i, c, hadplus;
+        char *s, *p, *q;
+        Field *f;
+
+        hadplus = 0;
+        args.mline = Nline;
+        for(i=1; i= '0' && c <= '9')) {
+                                if(!hadplus)
+                                        newfield();
+                                f = &args.field[args.nfield];
+                                dofield(s, &f->end1, &f->end2, 0, 0);
+                                hadplus = 0;
+                                continue;
+                        }
+
+                        while(c = *s++)
+                        switch(c) {
+                        case '-':        /* end of options */
+                                i = argc;
+                                continue;
+                        case 'T':        /* temp directory */
+                                if(*s == 0) {
+                                        i++;
+                                        if(i < argc) {
+                                                args.tname = argv[i];
+                                                argv[i] = 0;
+                                        }
+                                } else
+                                        args.tname = s;
+                                s = strchr(s, 0);
+                                break;
+                        case 'o':        /* output file */
+                                if(*s == 0) {
+                                        i++;
+                                        if(i < argc) {
+                                                args.ofile = argv[i];
+                                                argv[i] = 0;
+                                        }
+                                } else
+                                        args.ofile = s;
+                                s = strchr(s, 0);
+                                break;
+                        case 'k':        /* posix key (what were they thinking?) */
+                                p = 0;
+                                if(*s == 0) {
+                                        i++;
+                                        if(i < argc) {
+                                                p = argv[i];
+                                                argv[i] = 0;
+                                        }
+                                } else
+                                        p = s;
+                                s = strchr(s, 0);
+                                if(p == 0)
+                                        break;
+
+                                newfield();
+                                q = strchr(p, ',');
+                                if(q)
+                                        *q++ = 0;
+                                f = &args.field[args.nfield];
+                                dofield(p, &f->beg1, &f->beg2, 1, 1);
+                                if(f->flags & Bflag) {
+                                        f->flags |= B1flag;
+                                        f->flags &= ~Bflag;
+                                }
+                                if(q) {
+                                        dofield(q, &f->end1, &f->end2, 1, 0);
+                                        if(f->end2 <= 0)
+                                                f->end1++;
+                                }
+                                hadplus = 0;
+                                break;
+                        case 't':        /* tab character */
+                                if(*s == 0) {
+                                        i++;
+                                        if(i < argc) {
+                                                chartorune(&args.tabchar, argv[i]);
+                                                argv[i] = 0;
+                                        }
+                                } else
+                                        s += chartorune(&args.tabchar, s);
+                                if(args.tabchar == '\n') {
+                                        fprint(2, "aw come on, rob\n");
+                                        done("rob");
+                                }
+                                break;
+                        case 'c':        /* check order */
+                                args.cflag = 1;
+                                break;
+                        case 'u':        /* unique */
+                                args.uflag = 1;
+                                break;
+                        case 'v':        /* debugging noise */
+                                args.vflag = 1;
+                                break;
+                        case 'l':
+                                if(*s == 0) {
+                                        i++;
+                                        if(i < argc) {
+                                                args.mline = atol(argv[i]);
+                                                argv[i] = 0;
+                                        }
+                                } else
+                                        args.mline = atol(s);
+                                s = strchr(s, 0);
+                                break;
+
+                        case 'M':        /* month */
+                        case 'b':        /* skip blanks */
+                        case 'd':        /* directory order */
+                        case 'f':        /* fold case */
+                        case 'g':        /* floating numbers */
+                        case 'i':        /* ignore non-ascii */
+                        case 'n':        /* numbers */
+                        case 'r':        /* reverse */
+                        case 'w':        /* ignore white */
+                                if(args.nfield > 0)
+                                        fprint(2, "sort: global field set after -k\n");
+                                setfield(0, c);
+                                break;
+                        case 'm':
+                                /* option m silently ignored but required by posix */
+                                break;
+                        default:
+                                fprint(2, "sort: unknown option: -%C\n", c);
+                                done("option");
+                        }
+                        continue;
+                }
+                if(c == '+') {
+                        argv[i] = 0;                /* clobber args processed */
+                        c = *s;
+                        if(c == '.' || (c >= '0' && c <= '9')) {
+                                newfield();
+                                f = &args.field[args.nfield];
+                                dofield(s, &f->beg1, &f->beg2, 0, 0);
+                                if(f->flags & Bflag) {
+                                        f->flags |= B1flag;
+                                        f->flags &= ~Bflag;
+                                }
+                                hadplus = 1;
+                                continue;
+                        }
+                        fprint(2, "sort: unknown option: +%C\n", c);
+                        done("option");
+                }
+                args.nfile++;
+        }
+
+        for(i=0; i<=args.nfield; i++) {
+                f = &args.field[i];
+
+                /*
+                 * global options apply to fields that
+                 * specify no options
+                 */
+                if(f->flags == 0) {
+                        f->flags = args.field[0].flags;
+                        if(args.field[0].flags & Bflag)
+                                f->flags |= B1flag;
+                }
+
+
+                /*
+                 * build buildkey specification
+                 */
+                switch(f->flags & ~(Bflag|B1flag)) {
+                default:
+                        fprint(2, "sort: illegal combination of flags: %lx\n", f->flags);
+                        done("option");
+                case 0:
+                        f->dokey = dokey_;
+                        break;
+                case Rflag:
+                        f->dokey = dokey_r;
+                        break;
+                case Gflag:
+                case Nflag:
+                case Gflag|Nflag:
+                case Gflag|Rflag:
+                case Nflag|Rflag:
+                case Gflag|Nflag|Rflag:
+                        f->dokey = dokey_gn;
+                        break;
+                case Mflag:
+                case Mflag|Rflag:
+                        f->dokey = dokey_m;
+                        makemapm(f);
+                        break;
+                case Dflag:
+                case Dflag|Fflag:
+                case Dflag|Fflag|Iflag:
+                case Dflag|Fflag|Iflag|Rflag:
+                case Dflag|Fflag|Iflag|Rflag|Wflag:
+                case Dflag|Fflag|Iflag|Wflag:
+                case Dflag|Fflag|Rflag:
+                case Dflag|Fflag|Rflag|Wflag:
+                case Dflag|Fflag|Wflag:
+                case Dflag|Iflag:
+                case Dflag|Iflag|Rflag:
+                case Dflag|Iflag|Rflag|Wflag:
+                case Dflag|Iflag|Wflag:
+                case Dflag|Rflag:
+                case Dflag|Rflag|Wflag:
+                case Dflag|Wflag:
+                case Fflag:
+                case Fflag|Iflag:
+                case Fflag|Iflag|Rflag:
+                case Fflag|Iflag|Rflag|Wflag:
+                case Fflag|Iflag|Wflag:
+                case Fflag|Rflag:
+                case Fflag|Rflag|Wflag:
+                case Fflag|Wflag:
+                case Iflag:
+                case Iflag|Rflag:
+                case Iflag|Rflag|Wflag:
+                case Iflag|Wflag:
+                case Wflag:
+                        f->dokey = dokey_dfi;
+                        makemapd(f);
+                        break;
+                }
+        }
+
+        /*
+         * random spot checks
+         */
+        if(args.nfile > 1 && args.cflag) {
+                fprint(2, "sort: -c can have at most one input file\n");
+                done("option");
+        }
+        return;
+}
+
+uchar*
+skip(uchar *l, int n1, int n2, int bflag, int endfield)
+{
+        int i, c, tc;
+        Rune r;
+
+        if(endfield && n1 < 0)
+                return 0;
+
+        c = *l++;
+        tc = args.tabchar;
+        if(tc) {
+                if(tc < Runeself) {
+                        for(i=n1; i>0; i--) {
+                                while(c != tc) {
+                                        if(c == '\n')
+                                                return 0;
+                                        c = *l++;
+                                }
+                                if(!(endfield && i == 1))
+                                        c = *l++;
+                        }
+                } else {
+                        l--;
+                        l += chartorune(&r, (char*)l);
+                        for(i=n1; i>0; i--) {
+                                while(r != tc) {
+                                        if(r == '\n')
+                                                return 0;
+                                        l += chartorune(&r, (char*)l);
+                                }
+                                if(!(endfield && i == 1))
+                                        l += chartorune(&r, (char*)l);
+                        }
+                        c = r;
+                }
+        } else {
+                for(i=n1; i>0; i--) {
+                        while(c == ' ' || c == '\t')
+                                c = *l++;
+                        while(c != ' ' && c != '\t') {
+                                if(c == '\n')
+                                        return 0;
+                                c = *l++;
+                        }
+                }
+        }
+
+        if(bflag)
+                while(c == ' ' || c == '\t')
+                        c = *l++;
+
+        l--;
+        for(i=n2; i>0; i--) {
+                c = *l;
+                if(c < Runeself) {
+                        if(c == '\n')
+                                return 0;
+                        l++;
+                        continue;
+                }
+                l += chartorune(&r, (char*)l);
+        }
+        return l;
+}
+
+void
+dokey_gn(Key *k, uchar *lp, uchar *lpe, Field *f)
+{
+        uchar *kp;
+        int c, cl, dp;
+        int state, nzero, exp, expsign, rflag;
+
+        cl = k->klen + 3;
+        kp = k->key + cl;        /* skip place for sign, exponent[2] */
+
+        nzero = 0;                /* number of trailing zeros */
+        exp = 0;                /* value of the exponent */
+        expsign = 0;                /* sign of the exponent */
+        dp = 0x4040;                /* location of decimal point */
+        rflag = f->flags&Rflag;        /* xor of rflag and - sign */
+        state = NSstart;
+
+        for(;; lp++) {
+                if(lp >= lpe)
+                        break;
+                c = *lp;
+
+                if(c == ' ' || c == '\t') {
+                        switch(state) {
+                        case NSstart:
+                        case NSsign:
+                                continue;
+                        }
+                        break;
+                }
+                if(c == '+' || c == '-') {
+                        switch(state) {
+                        case NSstart:
+                                state = NSsign;
+                                if(c == '-')
+                                        rflag = !rflag;
+                                continue;
+                        case NSexp:
+                                state = NSexpsign;
+                                if(c == '-')
+                                        expsign = 1;
+                                continue;
+                        }
+                        break;
+                }
+                if(c == '0') {
+                        switch(state) {
+                        case NSdigit:
+                                if(rflag)
+                                        c = ~c;
+                                *kp++ = c;
+                                cl++;
+                                nzero++;
+                                dp++;
+                                state = NSdigit;
+                                continue;
+                        case NSfract:
+                                if(rflag)
+                                        c = ~c;
+                                *kp++ = c;
+                                cl++;
+                                nzero++;
+                                state = NSfract;
+                                continue;
+                        case NSstart:
+                        case NSsign:
+                        case NSzero:
+                                state = NSzero;
+                                continue;
+                        case NSzerofract:
+                        case NSpoint:
+                                dp--;
+                                state = NSzerofract;
+                                continue;
+                        case NSexpsign:
+                        case NSexp:
+                        case NSexpdigit:
+                                exp = exp*10 + (c - '0');
+                                state = NSexpdigit;
+                                continue;
+                        }
+                        break;
+                }
+                if(c >= '1' && c <= '9') {
+                        switch(state) {
+                        case NSzero:
+                        case NSstart:
+                        case NSsign:
+                        case NSdigit:
+                                if(rflag)
+                                        c = ~c;
+                                *kp++ = c;
+                                cl++;
+                                nzero = 0;
+                                dp++;
+                                state = NSdigit;
+                                continue;
+                        case NSzerofract:
+                        case NSpoint:
+                        case NSfract:
+                                if(rflag)
+                                        c = ~c;
+                                *kp++ = c;
+                                cl++;
+                                nzero = 0;
+                                state = NSfract;
+                                continue;
+                        case NSexpsign:
+                        case NSexp:
+                        case NSexpdigit:
+                                exp = exp*10 + (c - '0');
+                                state = NSexpdigit;
+                                continue;
+                        }
+                        break;
+                }
+                if(c == '.') {
+                        switch(state) {
+                        case NSstart:
+                        case NSsign:
+                                state = NSpoint;
+                                continue;
+                        case NSzero:
+                                state = NSzerofract;
+                                continue;
+                        case NSdigit:
+                                state = NSfract;
+                                continue;
+                        }
+                        break;
+                }
+                if((f->flags & Gflag) && (c == 'e' || c == 'E')) {
+                        switch(state) {
+                        case NSdigit:
+                        case NSfract:
+                                state = NSexp;
+                                continue;
+                        }
+                        break;
+                }
+                break;
+        }
+
+        switch(state) {
+        /*
+         * result is zero
+         */
+        case NSstart:
+        case NSsign:
+        case NSzero:
+        case NSzerofract:
+        case NSpoint:
+                kp = k->key + k->klen;
+                k->klen += 2;
+                kp[0] = 0x20;        /* between + and - */
+                kp[1] = 0;
+                return;
+        /*
+         * result has exponent
+         */
+        case NSexpsign:
+        case NSexp:
+        case NSexpdigit:
+                if(expsign)
+                        exp = -exp;
+                dp += exp;
+
+        /*
+         * result is fixed point number
+         */
+        case NSdigit:
+        case NSfract:
+                kp -= nzero;
+                cl -= nzero;
+                break;
+        }
+
+        /*
+         * end of number
+         */
+        c = 0;
+        if(rflag)
+                c = ~c;
+        *kp = c;
+
+        /*
+         * sign and exponent
+         */
+        c = 0x30;
+        if(rflag) {
+                c = 0x10;
+                dp = ~dp;
+        }
+        kp = k->key + k->klen;
+        kp[0] = c;
+        kp[1] = (dp >> 8);
+        kp[2] = dp;
+        k->klen = cl+1;
+}
+
+void
+dokey_m(Key *k, uchar *lp, uchar *lpe, Field *f)
+{
+        uchar *kp;
+        Rune r, place[3];
+        int c, cl, pc;
+        int rflag;
+
+        rflag = f->flags&Rflag;
+        pc = 0;
+
+        cl = k->klen;
+        kp = k->key + cl;
+
+        for(;;) {
+                /*
+                 * get the character
+                 */
+                if(lp >= lpe)
+                        break;
+                c = *lp;
+                if(c >= Runeself) {
+                        lp += chartorune(&r, (char*)lp);
+                        c = r;
+                } else
+                        lp++;
+
+                if(c < nelem(f->mapto)) {
+                        c = f->mapto[c];
+                        if(c == 0)
+                                continue;
+                }
+                place[pc++] = c;
+                if(pc < 3)
+                        continue;
+                for(c=11; c>=0; c--)
+                        if(memcmp(month[c], place, sizeof(place)) == 0)
+                                break;
+                c += 10;
+                if(rflag)
+                        c = ~c;
+                *kp++ = c;
+                cl++;
+                break;
+        }
+
+        c = 0;
+        if(rflag)
+                c = ~c;
+        *kp = c;
+        k->klen = cl+1;
+}
+
+void
+dokey_dfi(Key *k, uchar *lp, uchar *lpe, Field *f)
+{
+        uchar *kp;
+        Rune r;
+        int c, cl, n, rflag;
+
+        cl = k->klen;
+        kp = k->key + cl;
+        rflag = f->flags & Rflag;
+
+        for(;;) {
+                /*
+                 * get the character
+                 */
+                if(lp >= lpe)
+                        break;
+                c = *lp;
+                if(c >= Runeself) {
+                        lp += chartorune(&r, (char*)lp);
+                        c = r;
+                } else
+                        lp++;
+
+                /*
+                 * do the various mappings.
+                 * the common case is handled
+                 * completely by the table.
+                 */
+                if(c != 0 && c < Runeself) {
+                        c = f->mapto[c];
+                        if(c) {
+                                *kp++ = c;
+                                cl++;
+                        }
+                        continue;
+                }
+
+                /*
+                 * for characters out of range,
+                 * the table does not do Rflag.
+                 * ignore is based on mapto[255]
+                 */
+                if(c != 0 && c < nelem(f->mapto)) {
+                        c = f->mapto[c];
+                        if(c == 0)
+                                continue;
+                } else
+                        if(f->mapto[nelem(f->mapto)-1] == 0)
+                                continue;
+
+                /*
+                 * put it in the key
+                 */
+                r = c;
+                n = runetochar((char*)kp, &r);
+                kp += n;
+                cl += n;
+                if(rflag)
+                        while(n > 0) {
+                                kp[-n] = ~kp[-n];
+                                n--;
+                        }
+        }
+
+        /*
+         * end of key
+         */
+        k->klen = cl+1;
+        if(rflag) {
+                *kp = ~0;
+                return;
+        }
+        *kp = 0;
+}
+
+void
+dokey_r(Key *k, uchar *lp, uchar *lpe, Field *f)
+{
+        int cl, n;
+        uchar *kp;
+
+        USED(f);
+        n = lpe - lp;
+        if(n < 0)
+                n = 0;
+        cl = k->klen;
+        kp = k->key + cl;
+        k->klen = cl+n+1;
+
+        lpe -= 3;
+        while(lp < lpe) {
+                kp[0] = ~lp[0];
+                kp[1] = ~lp[1];
+                kp[2] = ~lp[2];
+                kp[3] = ~lp[3];
+                kp += 4;
+                lp += 4;
+        }
+
+        lpe += 3;
+        while(lp < lpe)
+                *kp++ = ~*lp++;
+        *kp = ~0;
+}
+
+void
+dokey_(Key *k, uchar *lp, uchar *lpe, Field *f)
+{
+        int n, cl;
+        uchar *kp;
+
+        USED(f);
+        n = lpe - lp;
+        if(n < 0)
+                n = 0;
+        cl = k->klen;
+        kp = k->key + cl;
+        k->klen = cl+n+1;
+        memmove(kp, lp, n);
+        kp[n] = 0;
+}
+
+void
+buildkey(Line *l)
+{
+        Key *k;
+        uchar *lp, *lpe;
+        int ll, kl, cl, i, n;
+        Field *f;
+
+        ll = l->llen - 1;
+        kl = 0;                        /* allocated length */
+        cl = 0;                        /* current length */
+        k = 0;
+
+        for(i=1; i<=args.nfield; i++) {
+                f = &args.field[i];
+                lp = skip(l->line, f->beg1, f->beg2, f->flags&B1flag, 0);
+                if(lp == 0)
+                        lp = l->line + ll;
+                lpe = skip(l->line, f->end1, f->end2, f->flags&Bflag, 1);
+                if(lpe == 0)
+                        lpe = l->line + ll;
+                n = (lpe - lp) + 1;
+                if(n <= 0)
+                        n = 1;
+                if(cl+(n+4) > kl) {
+                        kl = cl+(n+4);
+                        k = realloc(k, sizeof(Key) +
+                                (kl-1)*sizeof(k->key[0]));
+                        if(k == 0)
+                                nomem();
+                }
+                k->klen = cl;
+                (*f->dokey)(k, lp, lpe, f);
+                cl = k->klen;
+        }
+
+        /*
+         * global comparisons
+         */
+        if(!(args.uflag && cl > 0)) {
+                f = &args.field[0];
+                if(cl+(ll+4) > kl) {
+                        kl = cl+(ll+4);
+                        k = realloc(k, sizeof(Key) +
+                                (kl-1)*sizeof(k->key[0]));
+                        if(k == 0)
+                                nomem();
+                }
+                k->klen = cl;
+                (*f->dokey)(k, l->line, l->line+ll, f);
+                cl = k->klen;
+        }
+
+        l->key = k;
+        k->klen = cl;
+
+        if(args.vflag) {
+                write(2, l->line, l->llen);
+                for(i=0; iklen; i++) {
+                        fprint(2, " %.2x", k->key[i]);
+                        if(k->key[i] == 0x00 || k->key[i] == 0xff)
+                                fprint(2, "\n");
+                }
+        }
+}
+
+void
+makemapm(Field *f)
+{
+        int i, c;
+
+        for(i=0; imapto); i++) {
+                c = 1;
+                if(i == ' ' || i == '\t')
+                        c = 0;
+                if(i >= 'a' && i <= 'z')
+                        c = i + ('A' - 'a');
+                if(i >= 'A' && i <= 'Z')
+                        c = i;
+                f->mapto[i] = c;
+                if(args.vflag) {
+                        if((i & 15) == 0)
+                                fprint(2, "        ");
+                        fprint(2, " %.2x", c);
+                        if((i & 15) == 15)
+                                fprint(2, "\n");
+                }
+        }
+}
+
+void
+makemapd(Field *f)
+{
+        int i, j, c;
+
+        for(i=0; imapto); i++) {
+                c = i;
+                if(f->flags & Iflag)
+                        if(c < 040 || c > 0176)
+                                c = -1;
+                if((f->flags & Wflag) && c >= 0)
+                        if(c == ' ' || c == '\t')
+                                c = -1;
+                if((f->flags & Dflag) && c >= 0)
+                        if(!(c == ' ' || c == '\t' ||
+                            (c >= 'a' && c <= 'z') ||
+                            (c >= 'A' && c <= 'Z') ||
+                            (c >= '0' && c <= '9'))) {
+                                for(j=0; latinmap[j]; j+=3)
+                                        if(c == latinmap[j+0] ||
+                                           c == latinmap[j+1])
+                                                break;
+                                if(latinmap[j] == 0)
+                                        c = -1;
+                        }
+                if((f->flags & Fflag) && c >= 0) {
+                        if(c >= 'a' && c <= 'z')
+                                c += 'A' - 'a';
+                        for(j=0; latinmap[j]; j+=3)
+                                if(c == latinmap[j+0] ||
+                                   c == latinmap[j+1]) {
+                                        c = latinmap[j+2];
+                                        break;
+                                }
+                }
+                if((f->flags & Rflag) && c >= 0 && i > 0 && i < Runeself)
+                        c = ~c & 0xff;
+                if(c < 0)
+                        c = 0;
+                f->mapto[i] = c;
+                if(args.vflag) {
+                        if((i & 15) == 0)
+                                fprint(2, "        ");
+                        fprint(2, " %.2x", c);
+                        if((i & 15) == 15)
+                                fprint(2, "\n");
+                }
+        }
+}
+
+int        latinmap[] =
+{
+/*        lcase        ucase        fold        */
+        0xe0,        0xc0,        0x41,                /*         L'à',        L'À',        L'A',         */
+        0xe1,        0xc1,        0x41,                /*         L'á',        L'Á',        L'A',         */
+        0xe2,        0xc2,        0x41,                /*         L'â',        L'Â',        L'A',         */
+        0xe4,        0xc4,        0x41,                /*         L'ä',        L'Ä',        L'A',         */
+        0xe3,        0xc3,        0x41,                /*         L'ã',        L'Ã',        L'A',         */
+        0xe5,        0xc5,        0x41,                /*         L'å',        L'Å',        L'A',         */
+        0xe8,        0xc8,        0x45,                /*         L'è',        L'È',        L'E',         */
+        0xe9,        0xc9,        0x45,                /*         L'é',        L'É',        L'E',         */
+        0xea,        0xca,        0x45,                /*         L'ê',        L'Ê',        L'E',         */
+        0xeb,        0xcb,        0x45,                /*         L'ë',        L'Ë',        L'E',         */
+        0xec,        0xcc,        0x49,                /*         L'ì',        L'Ì',        L'I',         */
+        0xed,        0xcd,        0x49,                /*         L'í',        L'Í',        L'I',         */
+        0xee,        0xce,        0x49,                /*         L'î',        L'Î',        L'I',         */
+        0xef,        0xcf,        0x49,                /*         L'ï',        L'Ï',        L'I',         */
+        0xf2,        0xd2,        0x4f,                /*         L'ò',        L'Ò',        L'O',         */
+        0xf3,        0xd3,        0x4f,                /*         L'ó',        L'Ó',        L'O',         */
+        0xf4,        0xd4,        0x4f,                /*         L'ô',        L'Ô',        L'O',         */
+        0xf6,        0xd6,        0x4f,                /*         L'ö',        L'Ö',        L'O',         */
+        0xf5,        0xd5,        0x4f,                /*         L'õ',        L'Õ',        L'O',         */
+        0xf8,        0xd8,        0x4f,                /*         L'ø',        L'Ø',        L'O',         */
+        0xf9,        0xd9,        0x55,                /*         L'ù',        L'Ù',        L'U',         */
+        0xfa,        0xda,        0x55,                /*         L'ú',        L'Ú',        L'U',         */
+        0xfb,        0xdb,        0x55,                /*         L'û',        L'Û',        L'U',         */
+        0xfc,        0xdc,        0x55,                /*         L'ü',        L'Ü',        L'U',         */
+        0xe6,        0xc6,        0x41,                /*         L'æ',        L'Æ',        L'A',         */
+        0xf0,        0xd0,        0x44,                /*         L'ð',        L'Ð',        L'D',         */
+        0xf1,        0xd1,        0x4e,                /*         L'ñ',        L'Ñ',        L'N',         */
+        0xfd,        0xdd,        0x59,                /*         L'ý',        L'Ý',        L'Y',         */
+        0xe7,        0xc7,        0x43,                /*         L'ç',        L'Ç',        L'C',         */
+        0,
+};
+
+Rune LJAN[] = { 'J', 'A', 'N', 0 };
+Rune LFEB[] = { 'F', 'E', 'B', 0 };
+Rune LMAR[] = { 'M', 'A', 'R', 0 };
+Rune LAPR[] = { 'A', 'P', 'R', 0 };
+Rune LMAY[] = { 'M', 'A', 'Y', 0 };
+Rune LJUN[] = { 'J', 'U', 'N', 0 };
+Rune LJUL[] = { 'J', 'U', 'L', 0 };
+Rune LAUG[] = { 'A', 'U', 'G', 0 };
+Rune LSEP[] = { 'S', 'E', 'P', 0 };
+Rune LOCT[] = { 'O', 'C', 'T', 0 };
+Rune LNOV[] = { 'N', 'O', 'V', 0 };
+Rune LDEC[] = { 'D', 'E', 'C', 0 };
+
+Rune*        month[12] =
+{
+        LJAN,
+        LFEB,
+        LMAR,
+        LAPR,
+        LMAY,
+        LJUN,
+        LJUL,
+        LAUG,
+        LSEP,
+        LOCT,
+        LNOV,
+        LDEC,
+};
+
+/************** radix sort ***********/
+
+enum
+{
+        Threshold        = 14,
+};
+
+void        rsort4(Key***, ulong, int);
+void        bsort4(Key***, ulong, int);
+
+void
+sort4(void *a, ulong n)
+{
+        if(n > Threshold)
+                rsort4((Key***)a, n, 0);
+        else
+                bsort4((Key***)a, n, 0);
+}
+
+void
+rsort4(Key ***a, ulong n, int b)
+{
+        Key ***ea, ***t, ***u, **t1, **u1, *k;
+        Key ***part[257];
+        static long count[257];
+        long clist[257+257], *cp, *cp1;
+        int c, lowc, higc;
+
+        /*
+         * pass 1 over all keys,
+         * count the number of each key[b].
+         * find low count and high count.
+         */
+        lowc = 256;
+        higc = 0;
+        ea = a+n;
+        for(t=a; tklen;
+                if(b >= n) {
+                        count[256]++;
+                        continue;
+                }
+                c = k->key[b];
+                n = count[c]++;
+                if(n == 0) {
+                        if(c < lowc)
+                                lowc = c;
+                        if(c > higc)
+                                higc = c;
+                }
+        }
+
+        /*
+         * pass 2 over all counts,
+         * put partition pointers in part[c].
+         * save compacted indexes and counts
+         * in clist[].
+         */
+        t = a;
+        n = count[256];
+        clist[0] = n;
+        part[256] = t;
+        t += n;
+
+        cp1 = clist+1;
+        cp = count+lowc;
+        for(c=lowc; c<=higc; c++,cp++) {
+                n = *cp;
+                if(n) {
+                        cp1[0] = n;
+                        cp1[1] = c;
+                        cp1 += 2;
+                        part[c] = t;
+                        t += n;
+                }
+        }
+        *cp1 = 0;
+
+        /*
+         * pass 3 over all counts.
+         * chase lowest pointer in each partition
+         * around a permutation until it comes
+         * back and is stored where it started.
+         * static array, count[], should be
+         * reduced to zero entries except maybe
+         * count[256].
+         */
+        for(cp1=clist+1; cp1[0]; cp1+=2) {
+                c = cp1[1];
+                cp = count+c;
+                while(*cp) {
+                        t1 = *part[c];
+                        for(;;) {
+                                k = *t1;
+                                n = 256;
+                                if(b < k->klen)
+                                        n = k->key[b];
+                                u = part[n]++;
+                                count[n]--;
+                                u1 = *u;
+                                *u = t1;
+                                if(n == c)
+                                        break;
+                                t1 = u1;
+                        }
+                }
+        }
+
+        /*
+         * pass 4 over all partitions.
+         * call recursively.
+         */
+        b++;
+        t = a + clist[0];
+        count[256] = 0;
+        for(cp1=clist+1; n=cp1[0]; cp1+=2) {
+                if(n > Threshold)
+                        rsort4(t, n, b);
+                else
+                if(n > 1)
+                        bsort4(t, n, b);
+                t += n;
+        }
+}
+
+/*
+ * bubble sort to pick up
+ * the pieces.
+ */
+void
+bsort4(Key ***a, ulong n, int b)
+{
+        Key ***i, ***j, ***k, ***l, **t;
+        Key *ka, *kb;
+        int n1, n2;
+
+        l = a+n;
+        j = a;
+
+loop:
+        i = j;
+        j++;
+        if(j >= l)
+                return;
+
+        ka = **i;
+        kb = **j;
+        n1 = ka->klen - b;
+        n2 = kb->klen - b;
+        if(n1 > n2)
+                n1 = n2;
+        if(n1 <= 0)
+                goto loop;
+        n2 = ka->key[b] - kb->key[b];
+        if(n2 == 0)
+                n2 = memcmp(ka->key+b, kb->key+b, n1);
+        if(n2 <= 0)
+                goto loop;
+
+        for(;;) {
+                k = i+1;
+
+                t = *k;
+                *k = *i;
+                *i = t;
+
+                if(i <= a)
+                        goto loop;
+
+                i--;
+                ka = **i;
+                kb = *t;
+                n1 = ka->klen - b;
+                n2 = kb->klen - b;
+                if(n1 > n2)
+                        n1 = n2;
+                if(n1 <= 0)
+                        goto loop;
+                n2 = ka->key[b] - kb->key[b];
+                if(n2 == 0)
+                        n2 = memcmp(ka->key+b, kb->key+b, n1);
+                if(n2 <= 0)
+                        goto loop;
+        }
+}
diff --git a/src/cmd/split.c b/src/cmd/split.c
t@@ -0,0 +1,189 @@
+#include 
+#include 
+#include 
+#include 
+#include 
+
+char        digit[] = "0123456789";
+char        *suffix = "";
+char        *stem = "x";
+char        suff[] = "aa";
+char        name[200];
+Biobuf        bout;
+Biobuf        *output = &bout;
+
+extern int nextfile(void);
+extern int matchfile(Resub*);
+extern void openf(void);
+extern char *fold(char*,int);
+extern void usage(void);
+extern void badexp(void);
+
+void
+main(int argc, char *argv[])
+{
+        Reprog *exp;
+        char *pattern = 0;
+        int n = 1000;
+        char *line;
+        int xflag = 0;
+        int iflag = 0;
+        Biobuf bin;
+        Biobuf *b = &bin;
+        char buf[256];
+
+        ARGBEGIN {
+        case 'l':
+        case 'n':
+                n=atoi(EARGF(usage()));
+                break;
+        case 'e':
+                pattern = strdup(EARGF(usage()));
+                break;
+        case 'f':
+                stem = strdup(EARGF(usage()));
+                break;
+        case 's':
+                suffix = strdup(EARGF(usage()));
+                break;
+        case 'x':
+                xflag++;
+                break;
+        case 'i':
+                iflag++;
+                break;
+        default:
+                usage();
+                break;
+
+        } ARGEND;
+
+        if(argc < 0 || argc > 1)
+                usage();
+
+        if(argc != 0) {
+                b = Bopen(argv[0], OREAD);
+                if(b == nil) {
+                        fprint(2, "split: can't open %s: %r\n", argv[0]);
+                        exits("open");
+                }
+        } else
+                Binit(b, 0, OREAD);
+
+        if(pattern) {
+                if(!(exp = regcomp(iflag? fold(pattern,strlen(pattern)): pattern)))
+                        badexp();
+                while((line=Brdline(b,'\n')) != 0) {
+                        Resub match[2];
+                        memset(match, 0, sizeof match);
+                        line[Blinelen(b)-1] = 0;
+                        if(regexec(exp,iflag?fold(line,Blinelen(b)-1):line,match,2)) {
+                                if(matchfile(match) && xflag)
+                                        continue;
+                        } else if(output == 0)
+                                nextfile();        /* at most once */
+                        Bwrite(output, line, Blinelen(b)-1);
+                        Bputc(output, '\n');
+                }
+        } else {
+                int linecnt = n;
+
+                while((line=Brdline(b,'\n')) != 0) {
+                        if(++linecnt > n) {
+                                nextfile();
+                                linecnt = 1;
+                        }
+                        Bwrite(output, line, Blinelen(b));
+                }
+
+                /*
+                 * in case we didn't end with a newline, tack whatever's 
+                 * left onto the last file
+                 */
+                while((n = Bread(b, buf, sizeof(buf))) > 0)
+                        Bwrite(output, buf, n);
+        }
+        if(b != nil)
+                Bterm(b);
+        exits(0);
+}
+
+int
+nextfile(void)
+{
+        static int canopen = 1;
+        if(suff[0] > 'z') {
+                if(canopen)
+                        fprint(2, "split: file %szz not split\n",stem);
+                canopen = 0;
+        } else {
+                strcpy(name, stem);
+                strcat(name, suff);
+                if(++suff[1] > 'z') 
+                        suff[1] = 'a', ++suff[0];
+                openf();
+        }
+        return canopen;
+}
+
+int
+matchfile(Resub *match)
+{
+        if(match[1].s.sp) {
+                int len = match[1].e.ep - match[1].s.sp;
+                strncpy(name, match[1].s.sp, len);
+                strcpy(name+len, suffix);
+                openf();
+                return 1;
+        } 
+        return nextfile();
+}
+
+void
+openf(void)
+{
+        static int fd = 0;
+        Bflush(output);
+        Bterm(output);
+        if(fd > 0)
+                close(fd);
+        fd = create(name,OWRITE,0666);
+        if(fd < 0) {
+                fprint(2, "grep: can't create %s: %r\n", name);
+                exits("create");
+        }
+        Binit(output, fd, OWRITE);
+}
+
+char *
+fold(char *s, int n)
+{
+        static char *fline;
+        static int linesize = 0;
+        char *t;
+
+        if(linesize < n+1){
+                fline = realloc(fline,n+1);
+                linesize = n+1;
+        }
+        for(t=fline; *t++ = tolower(*s++); )
+                continue;
+                /* we assume the 'A'-'Z' only appear as themselves
+                 * in a utf encoding.
+                 */
+        return fline;
+}
+
+void
+usage(void)
+{
+        fprint(2, "usage: split [-n num] [-e exp] [-f stem] [-s suff] [-x] [-i] [file]\n");
+        exits("usage");
+}
+
+void
+badexp(void)
+{
+        fprint(2, "split: bad regular expression\n");
+        exits("bad regular expression");
+}
diff --git a/src/cmd/strings.c b/src/cmd/strings.c
t@@ -0,0 +1,88 @@
+#include        
+#include         
+#include        
+
+Biobuf        *fin;
+Biobuf        fout;
+
+#define        MINSPAN                6                /* Min characters in string */
+
+#define BUFSIZE                70
+
+void stringit(char *);
+int isprint(Rune);
+
+void
+main(int argc, char **argv)
+{
+        int i;
+
+        Binit(&fout, 1, OWRITE);
+        if(argc < 2) {
+                stringit("/fd/0");
+                exits(0);
+        }
+
+        for(i = 1; i < argc; i++) {
+                if(argc > 2)
+                        print("%s:\n", argv[i]);
+
+                stringit(argv[i]);
+        }
+
+        exits(0);
+}
+
+void
+stringit(char *str)
+{
+        long posn, start;
+        int cnt = 0;
+        long c;
+
+        Rune buf[BUFSIZE];
+
+        if ((fin = Bopen(str, OREAD)) == 0) {
+                perror("open");
+                return;
+        }
+
+        start = 0;
+        posn = Boffset(fin);
+        while((c = Bgetrune(fin)) >= 0) {
+                if(isprint(c)) {
+                        if(start == 0)
+                                start = posn;
+                        buf[cnt++] = c;
+                        if(cnt == BUFSIZE-1) {
+                                buf[cnt] = 0;
+                                Bprint(&fout, "%8ld: %S ...\n", start, buf);
+                                start = 0;
+                                cnt = 0;
+                        }
+                } else {
+                         if(cnt >= MINSPAN) {
+                                buf[cnt] = 0;
+                                Bprint(&fout, "%8ld: %S\n", start, buf);
+                        }
+                        start = 0;
+                        cnt = 0;
+                }        
+                posn = Boffset(fin);
+        }
+
+        if(cnt >= MINSPAN){
+                buf[cnt] = 0;
+                Bprint(&fout, "%8ld: %S\n", start, buf);
+        }
+        Bterm(fin);
+}
+
+int
+isprint(Rune r)
+{
+        if ((r >= ' ' && r <0x7f) || r > 0xA0)
+                return 1;
+        else
+                return 0;
+}
diff --git a/src/cmd/sum.c b/src/cmd/sum.c
t@@ -0,0 +1,215 @@
+#include 
+#include 
+
+typedef ulong        Sumfn(ulong, void*, uvlong);
+extern Sumfn        sumr, sum5, sum32;
+char                *sumfile(char*, Sumfn*);
+
+void
+usage(void)
+{
+        fprint(2, "Usage: %s [-r5] [files]\n", argv0);
+        exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+        Sumfn *fn = sum32;
+        char *exitstr=0, *s;
+
+        ARGBEGIN{
+        case 'r':
+                fn = sumr;
+                break;
+        case '5':
+                fn = sum5;
+                break;
+        default:
+                usage();
+                break;
+        }ARGEND
+        if(*argv){
+                while(*argv)
+                        if(s = sumfile(*argv++, fn))        /* assign = */
+                                exitstr = s;
+        }else
+                exitstr = sumfile(0, fn);
+        exits(exitstr);
+}
+
+char*
+sumfile(char *file, Sumfn *fn)
+{
+        int fd;
+        int n;
+        ulong sum;
+        uvlong fsize;
+        char buf[8*1024];
+
+        if(file){
+                if((fd = open(file, OREAD)) < 0){
+                        errstr(buf, sizeof buf);
+                        fprint(2, "%s: %s: %s\n", argv0, file, buf);
+                        return "can't open";
+                }
+        }else
+                fd = 0;
+        fsize = 0;
+        sum = 0;
+        while((n=read(fd, buf, sizeof buf)) > 0){
+                fsize += n;
+                sum = (*fn)(sum, buf, n);
+        }
+        if(n < 0){
+                errstr(buf, sizeof buf);
+                fprint(2, "%s: %s: read error: %s\n", argv0, file? file:"", buf);
+                if(file)
+                        close(fd);
+                return "read error";
+        }
+        if(file)
+                close(fd);
+        (*fn)(sum, (char*)0, fsize);
+        if(file)
+                print(" %s", file);
+        print("\n");
+        return 0;
+}
+
+#define        VBSIZE                512                /* system v */
+
+ulong
+sum5(ulong sum, void *buf, uvlong uvn)
+{
+        uchar *s, *send;
+        int n;
+
+        if(buf == 0){
+                sum = ((sum>>16)+sum) & 0xFFFF;
+                print("%.5lud%6lld", sum, (uvn+(VBSIZE-1))/VBSIZE);
+                return 0;
+        }
+        n = uvn;
+        for(s=buf, send=s+n; s>1)+*s+0x8000);
+                else
+                        sum = 0xffff & ((sum>>1)+*s);
+        return sum;
+}
+
+extern ulong crc_table[256];
+
+ulong
+sum32(ulong lcrc, void *buf, uvlong uvn)
+{
+        uchar *s = buf;
+        ulong crc = lcrc;
+        int n;
+
+        n = uvn;
+        if(buf == 0){
+                char x[4];
+
+                x[0] = (n>>24)^0xCC;        /* encode the length but make n==0 not 0 */
+                x[1] = (n>>16)^0x55;
+                x[2] = (n>>8)^0xCC;
+                x[3] = (n)^0x55;
+                crc = sum32(lcrc, x, 4);
+                print("%.8lux %6lld", crc, uvn);
+                return 0;
+        }
+        while(n-- > 0)
+                crc = crc_table[(crc^*s++)&0xff] ^ (crc>>8);
+        return crc;
+}
+
+/*
+ *        CRC 035556101440
+ */
+ulong crc_table[256] = {
+        0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
+        0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
+        0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+        0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
+        0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
+        0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+        0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
+        0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
+        0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+        0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+        0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
+        0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+        0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
+        0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
+        0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+        0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
+        0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
+        0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+        0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
+        0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+        0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+        0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
+        0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
+        0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+        0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
+        0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
+        0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+        0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
+        0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
+        0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+        0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
+        0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
+        0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+        0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
+        0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
+        0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+        0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
+        0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
+        0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+        0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+        0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
+        0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+        0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
+        0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
+        0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+        0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
+        0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
+        0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+        0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
+        0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+        0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+        0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
+        0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
+        0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+        0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
+        0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
+        0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+        0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
+        0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
+        0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+        0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
+        0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
+        0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+        0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
+};
diff --git a/src/cmd/tail.c b/src/cmd/tail.c
t@@ -0,0 +1,362 @@
+#include        
+#include        
+#include        
+#include        
+
+/*
+ * tail command, posix plus v10 option -r.
+ * the simple command tail -c, legal in v10, is illegal
+ */
+
+long        count;
+int        anycount;
+int        follow;
+int        file        = 0;
+char*        umsg        = "usage: tail [-n N] [-c N] [-f] [-r] [+-N[bc][fr]] [file]";
+
+Biobuf        bout;
+enum
+{
+        BEG,
+        END
+} origin = END;
+enum
+{
+        CHARS,
+        LINES
+} units = LINES;
+enum
+{
+        FWD,
+        REV
+} dir = FWD;
+
+extern        void        copy(void);
+extern        void        fatal(char*);
+extern        int        getnumber(char*);
+extern        void        keep(void);
+extern        void        reverse(void);
+extern        void        skip(void);
+extern        void        suffix(char*);
+extern        long        tread(char*, long);
+extern        void        trunc(Dir*, Dir**);
+extern        long        tseek(long, int);
+extern        void        twrite(char*, long);
+extern        void        usage(void);
+
+#define JUMP(o,p) tseek(o,p), copy()
+
+void
+main(int argc, char **argv)
+{
+        int seekable, c;
+
+        Binit(&bout, 1, OWRITE);
+        for(; argc > 1 && ((c=*argv[1])=='-'||c=='+'); argc--,argv++ ) {
+                if(getnumber(argv[1])) {
+                        suffix(argv[1]);
+                        continue;
+                } else
+                if(c == '-')
+                        switch(argv[1][1]) {
+                        case 'c':
+                                units = CHARS;
+                        case 'n':
+                                if(getnumber(argv[1]+2))
+                                        continue;
+                                else
+                                if(argc > 2 && getnumber(argv[2])) {
+                                        argc--, argv++;
+                                        continue;
+                                } else
+                                        usage();
+                        case 'r':
+                                dir = REV;
+                                continue;
+                        case 'f':
+                                follow++;
+                                continue;
+                        case '-':
+                                argc--, argv++;
+                        }
+                break;
+        }
+        if(dir==REV && (units==CHARS || follow || origin==BEG))
+                fatal("incompatible options");
+        if(!anycount)
+                count = dir==REV? ~0UL>>1: 10;
+        if(origin==BEG && units==LINES && count>0)
+                count--;
+        if(argc > 2)
+                usage();
+        if(argc > 1 && (file=open(argv[1],0)) < 0)
+                fatal(argv[1]);
+        seekable = seek(file,0L,0) == 0;
+
+        if(!seekable && origin==END)
+                keep();
+        else
+        if(!seekable && origin==BEG)
+                skip();
+        else
+        if(units==CHARS && origin==END)
+                JUMP(-count, 2);
+        else
+        if(units==CHARS && origin==BEG)
+                JUMP(count, 0);
+        else
+        if(units==LINES && origin==END)
+                reverse();
+        else
+        if(units==LINES && origin==BEG)
+                skip();
+        if(follow && seekable)
+                for(;;) {
+                        static Dir *sb0, *sb1;
+                        trunc(sb1, &sb0);
+                        copy();
+                        trunc(sb0, &sb1);
+                        sleep(5000);
+                }
+        exits(0);
+}
+
+void
+trunc(Dir *old, Dir **new)
+{
+        Dir *d;
+        ulong olength;
+
+        d = dirfstat(file);
+        if(d == nil)
+                return;
+        olength = 0;
+        if(old)
+                olength = old->length;
+        if(d->length < olength)
+                d->length = tseek(0L, 0);
+        free(*new);
+        *new = d;
+}
+
+void
+suffix(char *s)
+{
+        while(*s && strchr("0123456789+-", *s))
+                s++;
+        switch(*s) {
+        case 'b':
+                if((count *= 1024) < 0)
+                        fatal("too big");
+        case 'c':
+                units = CHARS;
+        case 'l':
+                s++;
+        }
+        switch(*s) {
+        case 'r':
+                dir = REV;
+                return;
+        case 'f':
+                follow++;
+                return;
+        case 0:
+                return;
+        }
+        usage();
+}
+
+/*
+ * read past head of the file to find tail
+ */
+void
+skip(void)
+{
+        int i;
+        long n;
+        char buf[Bsize];
+        if(units == CHARS) {
+                for( ; count>0; count -=n) {
+                        n = count 0) {
+                        if(!(n = tread(buf, Bsize)))
+                                return;
+                        for(i=0; i0; i++)
+                                if(buf[i]=='\n')
+                                        count--;
+                }
+                twrite(buf+i, n-i);
+        }
+        copy();
+}
+
+void
+copy(void)
+{
+        long n;
+        char buf[Bsize];
+        while((n=tread(buf, Bsize)) > 0) {
+                twrite(buf, n);
+                Bflush(&bout);        /* for FWD on pipe; else harmless */
+        }
+}
+
+/*
+ * read whole file, keeping the tail
+ *        complexity is length(file)*length(tail).
+ *        could be linear.
+ */
+void
+keep(void)
+{
+        int len = 0;
+        long bufsiz = 0;
+        char *buf = 0;
+        int j, k, n;
+
+        for(n=1; n;) {
+                if(len+Bsize > bufsiz) {
+                        bufsiz += 2*Bsize;
+                        if(!(buf = realloc(buf, bufsiz+1)))
+                                fatal("out of space");
+                }
+                for(; n && len= len)
+                        continue;
+                if(units == CHARS)
+                        j = len - count;
+                else {
+                        /* units == LINES */
+                        j = buf[len-1]=='\n'? len-1: len;
+                        for(k=0; j>0; j--)
+                                if(buf[j-1] == '\n')
+                                        if(++k >= count)
+                                                break;
+                }
+                memmove(buf, buf+j, len-=j);
+        }
+        if(dir == REV) {
+                if(len>0 && buf[len-1]!='\n')
+                        buf[len++] = '\n';
+                for(j=len-1 ; j>0; j--)
+                        if(buf[j-1] == '\n') {
+                                twrite(buf+j, len-j);
+                                if(--count <= 0)
+                                        return;
+                                len = j;
+                        }
+        }
+        if(count > 0)
+                twrite(buf, len);
+}
+
+/*
+ * count backward and print tail of file
+ */
+void
+reverse(void)
+{
+        int first;
+        long len = 0;
+        long n = 0;
+        long bufsiz = 0;
+        char *buf = 0;
+        long pos = tseek(0L, 2);
+
+        for(first=1; pos>0 && count>0; first=0) {
+                n = pos>Bsize? Bsize: (int)pos;
+                pos -= n;
+                if(len+n > bufsiz) {
+                        bufsiz += 2*Bsize;
+                        if(!(buf = realloc(buf, bufsiz+1)))
+                                fatal("out of space");
+                }
+                memmove(buf+n, buf, len);
+                len += n;
+                tseek(pos, 0);
+                if(tread(buf, n) != n)
+                        fatal("length error");
+                if(first && buf[len-1]!='\n')
+                        buf[len++] = '\n';
+                for(n=len-1 ; n>0 && count>0; n--)
+                        if(buf[n-1] == '\n') {
+                                count--;
+                                if(dir == REV)
+                                        twrite(buf+n, len-n);
+                                len = n;
+                        }
+        }
+        if(dir == FWD) {
+                tseek(n==0? 0 : pos+n+1, 0);
+                copy();
+        } else
+        if(count > 0)
+                twrite(buf, len);
+}
+
+long
+tseek(long o, int p)
+{
+        o = seek(file, o, p);
+        if(o == -1)
+                fatal("");
+        return o;
+}
+
+long
+tread(char *buf, long n)
+{
+        int r = read(file, buf, n);
+        if(r == -1)
+                fatal("");
+        return r;
+}
+
+void
+twrite(char *s, long n)
+{
+        if(Bwrite(&bout, s, n) != n)
+                fatal("");
+}
+
+int
+getnumber(char *s)
+{
+        if(*s=='-' || *s=='+')
+                s++;
+        if(!isdigit(*s))
+                return 0;
+        if(s[-1] == '+')
+                origin = BEG;
+        if(anycount++)
+                fatal("excess option");
+        count = atol(s);
+
+        /* check range of count */
+        if(count < 0 ||        (int)count != count)
+                fatal("too big");
+        return 1;
+}        
+
+void                
+fatal(char *s)
+{
+        char buf[ERRMAX];
+
+        errstr(buf, sizeof buf);
+        fprint(2, "tail: %s: %s\n", s, buf);
+        exits(s);
+}
+
+void
+usage(void)
+{
+        fprint(2, "%s\n", umsg);
+        exits("usage");
+}
diff --git a/src/cmd/tar.C b/src/cmd/tar.C
t@@ -0,0 +1,640 @@
+#include 
+#include 
+#include 
+
+#define TBLOCK        512
+#define NBLOCK        40        /* maximum blocksize */
+#define DBLOCK        20        /* default blocksize */
+#define NAMSIZ        100
+union        hblock
+{
+        char        dummy[TBLOCK];
+        struct        header
+        {
+                char        name[NAMSIZ];
+                char        mode[8];
+                char        uid[8];
+                char        gid[8];
+                char        size[12];
+                char        mtime[12];
+                char        chksum[8];
+                char        linkflag;
+                char        linkname[NAMSIZ];
+        } dbuf;
+} dblock, tbuf[NBLOCK];
+
+Dir *stbuf;
+Biobuf bout;
+
+int        rflag, xflag, vflag, tflag, mt, cflag, fflag, Tflag, Rflag;
+int        uflag, gflag;
+int        chksum, recno, first;
+int        nblock = DBLOCK;
+
+void        usage(void);
+void        dorep(char **);
+int        endtar(void);
+void        getdir(void);
+void        passtar(void);
+void        putfile(char*, char *, char *);
+void        doxtract(char **);
+void        dotable(void);
+void        putempty(void);
+void        longt(Dir *);
+int        checkdir(char *, int, Qid*);
+void        tomodes(Dir *);
+int        checksum(void);
+int        checkupdate(char *);
+int        prefix(char *, char *);
+int        readtar(char *);
+int        writetar(char *);
+void        backtar(void);
+void        flushtar(void);
+void        affix(int, char *);
+int        volprompt(void);
+void
+main(int argc, char **argv)
+{
+        char *usefile;
+        char *cp, *ap;
+
+        if (argc < 2)
+                usage();
+
+        Binit(&bout, 1, OWRITE);
+        usefile =  0;
+        argv[argc] = 0;
+        argv++;
+        for (cp = *argv++; *cp; cp++) 
+                switch(*cp) {
+                case 'f':
+                        usefile = *argv++;
+                        if(!usefile)
+                                usage();
+                        fflag++;
+                        break;
+                case 'u':
+                        ap = *argv++;
+                        if(!ap)
+                                usage();
+                        uflag = strtoul(ap, 0, 0);
+                        break;
+                case 'g':
+                        ap = *argv++;
+                        if(!ap)
+                                usage();
+                        gflag = strtoul(ap, 0, 0);
+                        break;
+                case 'c':
+                        cflag++;
+                        rflag++;
+                        break;
+                case 'r':
+                        rflag++;
+                        break;
+                case 'v':
+                        vflag++;
+                        break;
+                case 'x':
+                        xflag++;
+                        break;
+                case 'T':
+                        Tflag++;
+                        break;
+                case 't':
+                        tflag++;
+                        break;
+                case 'R':
+                        Rflag++;
+                        break;
+                case '-':
+                        break;
+                default:
+                        fprint(2, "tar: %c: unknown option\n", *cp);
+                        usage();
+                }
+
+        fmtinstall('M', dirmodefmt);
+
+        if (rflag) {
+                if (!usefile) {
+                        if (cflag == 0) {
+                                fprint(2, "tar: can only create standard output archives\n");
+                                exits("arg error");
+                        }
+                        mt = dup(1, -1);
+                        nblock = 1;
+                }
+                else if ((mt = open(usefile, ORDWR)) < 0) {
+                        if (cflag == 0 || (mt = create(usefile, OWRITE, 0666)) < 0) {
+                                fprint(2, "tar: cannot open %s: %r\n", usefile);
+                                exits("open");
+                        }
+                }
+                dorep(argv);
+        }
+        else if (xflag)  {
+                if (!usefile) {
+                        mt = dup(0, -1);
+                        nblock = 1;
+                }
+                else if ((mt = open(usefile, OREAD)) < 0) {
+                        fprint(2, "tar: cannot open %s: %r\n", usefile);
+                        exits("open");
+                }
+                doxtract(argv);
+        }
+        else if (tflag) {
+                if (!usefile) {
+                        mt = dup(0, -1);
+                        nblock = 1;
+                }
+                else if ((mt = open(usefile, OREAD)) < 0) {
+                        fprint(2, "tar: cannot open %s: %r\n", usefile);
+                        exits("open");
+                }
+                dotable();
+        }
+        else
+                usage();
+        exits(0);
+}
+
+void
+usage(void)
+{
+        fprint(2, "tar: usage  tar {txrc}[Rvf] [tarfile] file1 file2...\n");
+        exits("usage");
+}
+
+void
+dorep(char **argv)
+{
+        char cwdbuf[2048], *cwd, thisdir[2048];
+        char *cp, *cp2;
+        int cd;
+
+        if (getwd(cwdbuf, sizeof(cwdbuf)) == 0) {
+                fprint(2, "tar: can't find current directory: %r\n");
+                exits("cwd");
+        }
+        cwd = cwdbuf;
+
+        if (!cflag) {
+                getdir();
+                do {
+                        passtar();
+                        getdir();
+                } while (!endtar());
+        }
+
+        while (*argv) {
+                cp2 = *argv;
+                if (!strcmp(cp2, "-C") && argv[1]) {
+                        argv++;
+                        if (chdir(*argv) < 0)
+                                perror(*argv);
+                        cwd = *argv;
+                        argv++;
+                        continue;
+                }
+                cd = 0;
+                for (cp = *argv; *cp; cp++)
+                        if (*cp == '/')
+                                cp2 = cp;
+                if (cp2 != *argv) {
+                        *cp2 = '\0';
+                        chdir(*argv);
+                        if(**argv == '/')
+                                strncpy(thisdir, *argv, sizeof(thisdir));
+                        else
+                                snprint(thisdir, sizeof(thisdir), "%s/%s", cwd, *argv);
+                        *cp2 = '/';
+                        cp2++;
+                        cd = 1;
+                } else
+                        strncpy(thisdir, cwd, sizeof(thisdir));
+                putfile(thisdir, *argv++, cp2);
+                if(cd && chdir(cwd) < 0) {
+                        fprint(2, "tar: can't cd back to %s: %r\n", cwd);
+                        exits("cwd");
+                }
+        }
+        putempty();
+        putempty();
+        flushtar();
+}
+
+int
+endtar(void)
+{
+        if (dblock.dbuf.name[0] == '\0') {
+                backtar();
+                return(1);
+        }
+        else
+                return(0);
+}
+
+void
+getdir(void)
+{
+        Dir *sp;
+
+        readtar((char*)&dblock);
+        if (dblock.dbuf.name[0] == '\0')
+                return;
+        if(stbuf == nil){
+                stbuf = malloc(sizeof(Dir));
+                if(stbuf == nil) {
+                        fprint(2, "tar: can't malloc: %r\n");
+                        exits("malloc");
+                }
+        }
+        sp = stbuf;
+        sp->mode = strtol(dblock.dbuf.mode, 0, 8);
+        sp->uid = "adm";
+        sp->gid = "adm";
+        sp->length = strtol(dblock.dbuf.size, 0, 8);
+        sp->mtime = strtol(dblock.dbuf.mtime, 0, 8);
+        chksum = strtol(dblock.dbuf.chksum, 0, 8);
+        if (chksum != checksum()) {
+                fprint(2, "directory checksum error\n");
+                exits("checksum error");
+        }
+        sp->qid.type = 0;
+        /* the mode test is ugly but sometimes necessary */
+        if (dblock.dbuf.linkflag == '5' || (sp->mode&0170000) == 040000) {
+                sp->qid.type |= QTDIR;
+                sp->mode |= DMDIR;
+        }
+}
+
+void
+passtar(void)
+{
+        long blocks;
+        char buf[TBLOCK];
+
+        if (dblock.dbuf.linkflag == '1' || dblock.dbuf.linkflag == 's')
+                return;
+        blocks = stbuf->length;
+        blocks += TBLOCK-1;
+        blocks /= TBLOCK;
+
+        while (blocks-- > 0)
+                readtar(buf);
+}
+
+void
+putfile(char *dir, char *longname, char *sname)
+{
+        int infile;
+        long blocks;
+        char buf[TBLOCK];
+        char curdir[4096];
+        char shortname[4096];
+        char *cp, *cp2;
+        Dir *db;
+        int i, n;
+
+        if(strlen(sname) > sizeof shortname - 3){
+                fprint(2, "tar: %s: name too long (max %d)\n", sname, sizeof shortname - 3);
+                return;
+        }
+        
+        snprint(shortname, sizeof shortname, "./%s", sname);
+        infile = open(shortname, OREAD);
+        if (infile < 0) {
+                fprint(2, "tar: %s: cannot open file - %r\n", longname);
+                return;
+        }
+
+        if(stbuf != nil)
+                free(stbuf);
+        stbuf = dirfstat(infile);
+
+        if (stbuf->qid.type & QTDIR) {
+                /* Directory */
+                for (i = 0, cp = buf; *cp++ = longname[i++];);
+                *--cp = '/';
+                *++cp = 0;
+                if( (cp - buf) >= NAMSIZ) {
+                        fprint(2, "tar: %s: file name too long\n", longname);
+                        close(infile);
+                        return;
+                }
+                stbuf->length = 0;
+                tomodes(stbuf);
+                strcpy(dblock.dbuf.name,buf);
+                dblock.dbuf.linkflag = '5';                /* Directory */
+                sprint(dblock.dbuf.chksum, "%6o", checksum());
+                writetar( (char *) &dblock);
+                if (chdir(shortname) < 0) {
+                        fprint(2, "tar: can't cd to %s: %r\n", shortname);
+                        snprint(curdir, sizeof(curdir), "cd %s", shortname);
+                        exits(curdir);
+                }
+                sprint(curdir, "%s/%s", dir, sname);
+                while ((n = dirread(infile, &db)) > 0) {
+                        for(i = 0; i < n; i++){
+                                strncpy(cp, db[i].name, sizeof buf - (cp-buf));
+                                putfile(curdir, buf, db[i].name);
+                        }free(db);
+                }
+                close(infile);
+                if (chdir(dir) < 0 && chdir("..") < 0) {
+                        fprint(2, "tar: can't cd to ..(%s): %r\n", dir);
+                        snprint(curdir, sizeof(curdir), "cd ..(%s)", dir);
+                        exits(curdir);
+                }
+                return;
+        }
+
+
+        tomodes(stbuf);
+
+        cp2 = longname;
+        for (cp = dblock.dbuf.name, i=0; (*cp++ = *cp2++) && i < NAMSIZ; i++);
+        if (i >= NAMSIZ) {
+                fprint(2, "%s: file name too long\n", longname);
+                close(infile);
+                return;
+        }
+
+        blocks = (stbuf->length + (TBLOCK-1)) / TBLOCK;
+        if (vflag) {
+                fprint(2, "a %s ", longname);
+                fprint(2, "%ld blocks\n", blocks);
+        }
+        dblock.dbuf.linkflag = 0;                        /* Regular file */
+        sprint(dblock.dbuf.chksum, "%6o", checksum());
+        writetar( (char *) &dblock);
+
+        while ((i = readn(infile, buf, TBLOCK)) > 0 && blocks > 0) {
+                writetar(buf);
+                blocks--;
+        }
+        close(infile);
+        if (blocks != 0 || i != 0)
+                fprint(2, "%s: file changed size\n", longname);
+        while (blocks-- >  0)
+                putempty();
+}
+
+
+void
+doxtract(char **argv)
+{
+        Dir null;
+        long blocks, bytes;
+        char buf[TBLOCK], outname[NAMSIZ+4];
+        char **cp;
+        int ofile;
+
+        for (;;) {
+                getdir();
+                if (endtar())
+                        break;
+
+                if (*argv == 0)
+                        goto gotit;
+
+                for (cp = argv; *cp; cp++)
+                        if (prefix(*cp, dblock.dbuf.name))
+                                goto gotit;
+                passtar();
+                continue;
+
+gotit:
+                if(checkdir(dblock.dbuf.name, stbuf->mode, &(stbuf->qid)))
+                        continue;
+
+                if (dblock.dbuf.linkflag == '1') {
+                        fprint(2, "tar: can't link %s %s\n",
+                                dblock.dbuf.linkname, dblock.dbuf.name);
+                        remove(dblock.dbuf.name);
+                        continue;
+                }
+                if (dblock.dbuf.linkflag == 's') {
+                        fprint(2, "tar: %s: cannot symlink\n", dblock.dbuf.name);
+                        continue;
+                }
+                if(dblock.dbuf.name[0] != '/' || Rflag)
+                        sprint(outname, "./%s", dblock.dbuf.name);
+                else
+                        strcpy(outname, dblock.dbuf.name);
+                if ((ofile = create(outname, OWRITE, stbuf->mode & 0777)) < 0) {
+                        fprint(2, "tar: %s - cannot create: %r\n", outname);
+                        passtar();
+                        continue;
+                }
+
+                blocks = ((bytes = stbuf->length) + TBLOCK-1)/TBLOCK;
+                if (vflag)
+                        fprint(2, "x %s, %ld bytes\n",
+                                dblock.dbuf.name, bytes);
+                while (blocks-- > 0) {
+                        readtar(buf);
+                        if (bytes > TBLOCK) {
+                                if (write(ofile, buf, TBLOCK) < 0) {
+                                        fprint(2, "tar: %s: HELP - extract write error: %r\n", dblock.dbuf.name);
+                                        exits("extract write");
+                                }
+                        } else
+                                if (write(ofile, buf, bytes) < 0) {
+                                        fprint(2, "tar: %s: HELP - extract write error: %r\n", dblock.dbuf.name);
+                                        exits("extract write");
+                                }
+                        bytes -= TBLOCK;
+                }
+                if(Tflag){
+                        nulldir(&null);
+                        null.mtime = stbuf->mtime;
+                        dirfwstat(ofile, &null);
+                }
+                close(ofile);
+        }
+}
+
+void
+dotable(void)
+{
+        for (;;) {
+                getdir();
+                if (endtar())
+                        break;
+                if (vflag)
+                        longt(stbuf);
+                Bprint(&bout, "%s", dblock.dbuf.name);
+                if (dblock.dbuf.linkflag == '1')
+                        Bprint(&bout, " linked to %s", dblock.dbuf.linkname);
+                if (dblock.dbuf.linkflag == 's')
+                        Bprint(&bout, " -> %s", dblock.dbuf.linkname);
+                Bprint(&bout, "\n");
+                passtar();
+        }
+}
+
+void
+putempty(void)
+{
+        char buf[TBLOCK];
+
+        memset(buf, 0, TBLOCK);
+        writetar(buf);
+}
+
+void
+longt(Dir *st)
+{
+        char *cp;
+
+        Bprint(&bout, "%M %4d/%1d ", st->mode, 0, 0);        /* 0/0 uid/gid */
+        Bprint(&bout, "%8lld", st->length);
+        cp = ctime(st->mtime);
+        Bprint(&bout, " %-12.12s %-4.4s ", cp+4, cp+24);
+}
+
+int
+checkdir(char *name, int mode, Qid *qid)
+{
+        char *cp;
+        int f;
+        Dir *d, null;
+
+        if(Rflag && *name == '/')
+                name++;
+        cp = name;
+        if(*cp == '/')
+                cp++;
+        for (; *cp; cp++) {
+                if (*cp == '/') {
+                        *cp = '\0';
+                        if (access(name, 0) < 0) {
+                                f = create(name, OREAD, DMDIR + 0775L);
+                                if(f < 0)
+                                        fprint(2, "tar: mkdir %s failed: %r\n", name);
+                                close(f);
+                        }
+                        *cp = '/';
+                }
+        }
+
+        /* if this is a directory, chmod it to the mode in the tar plus 700 */
+        if(cp[-1] == '/' || (qid->type&QTDIR)){
+                if((d=dirstat(name)) != 0){
+                        nulldir(&null);
+                        null.mode = DMDIR | (mode & 0777) | 0700;
+                        dirwstat(name, &null);
+                        free(d);
+                }
+                return 1;
+        } else
+                return 0;
+}
+
+void
+tomodes(Dir *sp)
+{
+        char *cp;
+
+        for (cp = dblock.dummy; cp < &dblock.dummy[TBLOCK]; cp++)
+                *cp = '\0';
+        sprint(dblock.dbuf.mode, "%6lo ", sp->mode & 0777);
+        sprint(dblock.dbuf.uid, "%6o ", uflag);
+        sprint(dblock.dbuf.gid, "%6o ", gflag);
+        sprint(dblock.dbuf.size, "%11llo ", sp->length);
+        sprint(dblock.dbuf.mtime, "%11lo ", sp->mtime);
+}
+
+int
+checksum(void)
+{
+        int i;
+        char *cp;
+
+        for (cp = dblock.dbuf.chksum; cp < &dblock.dbuf.chksum[sizeof(dblock.dbuf.chksum)]; cp++)
+                *cp = ' ';
+        i = 0;
+        for (cp = dblock.dummy; cp < &dblock.dummy[TBLOCK]; cp++)
+                i += *cp & 0xff;
+        return(i);
+}
+
+int
+prefix(char *s1, char *s2)
+{
+        while (*s1)
+                if (*s1++ != *s2++)
+                        return(0);
+        if (*s2)
+                return(*s2 == '/');
+        return(1);
+}
+
+int
+readtar(char *buffer)
+{
+        int i;
+
+        if (recno >= nblock || first == 0) {
+                if ((i = readn(mt, tbuf, TBLOCK*nblock)) <= 0) {
+                        fprint(2, "tar: archive read error: %r\n");
+                        exits("archive read");
+                }
+                if (first == 0) {
+                        if ((i % TBLOCK) != 0) {
+                                fprint(2, "tar: archive blocksize error: %r\n");
+                                exits("blocksize");
+                        }
+                        i /= TBLOCK;
+                        if (i != nblock) {
+                                fprint(2, "tar: blocksize = %d\n", i);
+                                nblock = i;
+                        }
+                }
+                recno = 0;
+        }
+        first = 1;
+        memmove(buffer, &tbuf[recno++], TBLOCK);
+        return(TBLOCK);
+}
+
+int
+writetar(char *buffer)
+{
+        first = 1;
+        if (recno >= nblock) {
+                if (write(mt, tbuf, TBLOCK*nblock) != TBLOCK*nblock) {
+                        fprint(2, "tar: archive write error: %r\n");
+                        exits("write");
+                }
+                recno = 0;
+        }
+        memmove(&tbuf[recno++], buffer, TBLOCK);
+        if (recno >= nblock) {
+                if (write(mt, tbuf, TBLOCK*nblock) != TBLOCK*nblock) {
+                        fprint(2, "tar: archive write error: %r\n");
+                        exits("write");
+                }
+                recno = 0;
+        }
+        return(TBLOCK);
+}
+
+/*
+ * backup over last tar block
+ */
+void
+backtar(void)
+{
+        seek(mt, -TBLOCK*nblock, 1);
+        recno--;
+}
+
+void
+flushtar(void)
+{
+        write(mt, tbuf, TBLOCK*nblock);
+}
diff --git a/src/cmd/tee.c b/src/cmd/tee.c
t@@ -0,0 +1,75 @@
+/*
+ * tee-- pipe fitting
+ */
+
+#include 
+#include 
+
+int        uflag;
+int        aflag;
+int        openf[100];
+
+char in[8192];
+
+int        intignore(void*, char*);
+
+void
+main(int argc, char **argv)
+{
+        int i;
+        int r, n;
+
+        ARGBEGIN {
+        case 'a':
+                aflag++;
+                break;
+
+        case 'i':
+                atnotify(intignore, 1);
+                break;
+
+        case 'u':
+                uflag++;
+                /* uflag is ignored and undocumented; it's a relic from Unix */
+                break;
+
+        default:
+                fprint(2, "usage: tee [-ai] [file ...]\n");
+                exits("usage");
+        } ARGEND
+
+        USED(argc);
+        n = 0;
+        while(*argv) {
+                if(aflag) {
+                        openf[n] = open(argv[0], OWRITE);
+                        if(openf[n] < 0)
+                                openf[n] = create(argv[0], OWRITE, 0666);
+                        seek(openf[n], 0L, 2);
+                } else
+                        openf[n] = create(argv[0], OWRITE, 0666);
+                if(openf[n] < 0) {
+                        fprint(2, "tee: cannot open %s: %r\n", argv[0]);
+                } else
+                        n++;
+                argv++;
+        }
+        openf[n++] = 1;
+
+        for(;;) {
+                r = read(0, in, sizeof in);
+                if(r <= 0)
+                        exits(nil);
+                for(i=0; i
diff --git a/src/cmd/test.c b/src/cmd/test.c
t@@ -0,0 +1,303 @@
+/*
+ * POSIX standard
+ *        test expression
+ *        [ expression ]
+ *
+ * Plan 9 additions:
+ *        -A file exists and is append-only
+ *        -L file exists and is exclusive-use
+ */
+
+#include 
+#include 
+#define EQ(a,b)        ((tmp=a)==0?0:(strcmp(tmp,b)==0))
+
+int        ap;
+int        ac;
+char        **av;
+char        *tmp;
+
+void        synbad(char *, char *);
+int        fsizep(char *);
+int        isdir(char *);
+int        isreg(char *);
+int        isatty(int);
+int        isint(char *, int *);
+int        hasmode(char *, ulong);
+int        tio(char *, int);
+int        e(void), e1(void), e2(void), e3(void);
+
+void
+main(int argc, char *argv[])
+{
+
+        ac = argc; av = argv; ap = 1;
+        if(EQ(argv[0],"[")) {
+                if(!EQ(argv[--ac],"]"))
+                        synbad("] missing","");
+        }
+        argv[ac] = 0;
+        if (ac<=1) exits("usage");
+        exits(e()?0:"false");
+}
+
+char *
+nxtarg(int mt)
+{
+        if(ap>=ac){
+                if(mt){
+                        ap++;
+                        return(0);
+                }
+                synbad("argument expected","");
+        }
+        return(av[ap++]);
+}
+
+int
+nxtintarg(int *pans)
+{
+        if(ap=ac || !nxtintarg(&int1))
+                        return(isatty(1));
+                else
+                        return(isatty(int1));
+
+        if(EQ(a, "-n"))
+                return(!EQ(nxtarg(0), ""));
+        if(EQ(a, "-z"))
+                return(EQ(nxtarg(0), ""));
+
+        p2 = nxtarg(1);
+        if (p2==0)
+                return(!EQ(a,""));
+        if(EQ(p2, "="))
+                return(EQ(nxtarg(0), a));
+
+        if(EQ(p2, "!="))
+                return(!EQ(nxtarg(0), a));
+
+        if(!isint(a, &int1))
+                return(!EQ(a,""));
+
+        if(nxtintarg(&int2)){
+                if(EQ(p2, "-eq"))
+                        return(int1==int2);
+                if(EQ(p2, "-ne"))
+                        return(int1!=int2);
+                if(EQ(p2, "-gt"))
+                        return(int1>int2);
+                if(EQ(p2, "-lt"))
+                        return(int1=int2);
+                if(EQ(p2, "-le"))
+                        return(int1<=int2);
+        }
+
+        synbad("unknown operator ",p2);
+        return 0;                /* to shut ken up */
+}
+
+int
+tio(char *a, int f)
+{
+        return access (a, f) >= 0;
+}
+
+/* copy to local memory; clear names for safety */
+int
+localstat(char *f, Dir *dir)
+{
+        Dir *d;
+
+        d = dirstat(f);
+        if(d == 0)
+                return(-1);
+        *dir = *d;
+        dir->name = 0;
+        dir->uid = 0;
+        dir->gid = 0;
+        dir->muid = 0;
+        return 0;
+}
+
+/* copy to local memory; clear names for safety */
+int
+localfstat(int f, Dir *dir)
+{
+        Dir *d;
+
+        d = dirfstat(f);
+        if(d == 0)
+                return(-1);
+        *dir = *d;
+        dir->name = 0;
+        dir->uid = 0;
+        dir->gid = 0;
+        dir->muid = 0;
+        return 0;
+}
+
+int
+hasmode(char *f, ulong m)
+{
+        Dir dir;
+
+        if(localstat(f,&dir)<0)
+                return(0);
+        return(dir.mode&m);
+}
+
+int
+isdir(char *f)
+{
+        Dir dir;
+
+        if(localstat(f,&dir)<0)
+                return(0);
+        return(dir.mode&DMDIR);
+}
+
+int
+isreg(char *f)
+{
+        Dir dir;
+
+        if(localstat(f,&dir)<0)
+                return(0);
+        return(!(dir.mode&DMDIR));
+}
+
+int
+isatty(int fd)
+{
+        Dir d1, d2;
+
+        if(localfstat(fd, &d1) < 0)
+                return 0;
+        if(localstat("/dev/cons", &d2) < 0)
+                return 0;
+        return d1.type==d2.type && d1.dev==d2.dev && d1.qid.path==d2.qid.path;
+}
+
+int
+fsizep(char *f)
+{
+        Dir dir;
+
+        if(localstat(f,&dir)<0)
+                return(0);
+        return(dir.length>0);
+}
+
+void
+synbad(char *s1, char *s2)
+{
+        int len;
+
+        write(2, "test: ", 6);
+        if ((len = strlen(s1)) != 0)
+                write(2, s1, len);
+        if ((len = strlen(s2)) != 0)
+                write(2, s2, len);
+        write(2, "\n", 1);
+        exits("bad syntax");
+}
+
+int
+isint(char *s, int *pans)
+{
+        char *ep;
+
+        *pans = strtol(s, &ep, 0);
+        return (*ep == 0);
+}
diff --git a/src/cmd/time.c b/src/cmd/time.c
t@@ -0,0 +1,101 @@
+#include 
+#include 
+
+char        output[4096];
+void        add(char*, ...);
+void        error(char*);
+void        notifyf(void*, char*);
+
+void
+main(int argc, char *argv[])
+{
+        int i;
+        Waitmsg *w;
+        long l;
+        char *p;
+        char err[ERRMAX];
+
+        if(argc <= 1){
+                fprint(2, "usage: time command\n");
+                exits("usage");
+        }
+
+        switch(fork()){
+        case -1:
+                error("fork");
+        case 0:
+                exec(argv[1], &argv[1]);
+                if(argv[1][0] != '/' && strncmp(argv[1], "./", 2) &&
+                   strncmp(argv[1], "../", 3)){
+                        sprint(output, "/bin/%s", argv[1]);
+                        exec(output, &argv[1]);
+                }
+                error(argv[1]);
+        }
+
+        notify(notifyf);
+
+    loop:
+        w = wait();
+        if(w == nil){
+                errstr(err, sizeof err);
+                if(strcmp(err, "interrupted") == 0)
+                        goto loop;
+                error("wait");
+        }
+        l = w->time[0];
+        add("%ld.%.2ldu", l/1000, (l%1000)/10);
+        l = w->time[1];
+        add("%ld.%.2lds", l/1000, (l%1000)/10);
+        l = w->time[2];
+        add("%ld.%.2ldr", l/1000, (l%1000)/10);
+        add("\t");
+        for(i=1; i4){
+                        add("...");
+                        break;
+                }
+        }
+        if(w->msg[0]){
+                p = utfrune(w->msg, ':');
+                if(p && p[1])
+                        p++;
+                else
+                        p = w->msg;
+                add(" # status=%s", p);
+        }
+        fprint(2, "%s\n", output);
+        exits(w->msg);
+}
+
+void
+add(char *a, ...)
+{
+        static int beenhere=0;
+        va_list arg;
+
+        if(beenhere)
+                strcat(output, " ");
+        va_start(arg, a);
+        vseprint(output+strlen(output), output+sizeof(output), a, arg);
+        va_end(arg);
+        beenhere++;
+}
+
+void
+error(char *s)
+{
+
+        fprint(2, "time: %s: %r\n", s);
+        exits(s);
+}
+
+void
+notifyf(void *a, char *s)
+{
+        USED(a);
+        if(strcmp(s, "interrupt") == 0)
+                noted(NCONT);
+        noted(NDFLT);
+}
diff --git a/src/cmd/touch.c b/src/cmd/touch.c
t@@ -0,0 +1,62 @@
+#include 
+#include 
+
+int touch(int, char *);
+ulong now;
+
+void
+usage(void)
+{
+        fprint(2, "usage: touch [-c] [-t time] files\n");
+        exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+        int nocreate = 0;
+        int status = 0;
+
+        now = time(0);
+        ARGBEGIN{
+        case 't':
+                now = strtoul(EARGF(usage()), 0, 0);
+                break;
+        case 'c':
+                nocreate = 1;
+                break;
+        default:        
+                usage();
+        }ARGEND
+
+        if(!*argv)
+                usage();
+        while(*argv)
+                status += touch(nocreate, *argv++);
+        if(status)
+                exits("touch");
+        exits(0);
+}
+
+int
+touch(int nocreate, char *name)
+{
+        Dir stbuff;
+        int fd;
+
+        nulldir(&stbuff);
+        stbuff.mtime = now;
+        if(dirwstat(name, &stbuff) >= 0)
+                return 0;
+        if(nocreate){
+                fprint(2, "touch: %s: cannot wstat: %r\n", name);
+                return 1;
+        }
+        if ((fd = create(name, OREAD, 0666)) < 0) {
+                fprint(2, "touch: %s: cannot create: %r\n", name);
+                return 1;
+        }
+        dirfwstat(fd, &stbuff);
+        close(fd);
+        return 0;
+}
diff --git a/src/cmd/tr.c b/src/cmd/tr.c
t@@ -0,0 +1,356 @@
+#include         
+#include         
+
+typedef struct PCB        /* Control block controlling specification parse */
+{
+        char        *base;                /* start of specification */
+        char        *current;        /* current parse point */
+        long        last;                /* last Rune returned */
+        long        final;                /* final Rune in a span */
+} Pcb;
+
+uchar        bits[] = { 1, 2, 4, 8, 16, 32, 64, 128 };
+
+#define        SETBIT(a, c)                ((a)[(c)/8] |= bits[(c)&07])
+#define        CLEARBIT(a,c)                ((a)[(c)/8] &= ~bits[(c)&07])
+#define        BITSET(a,c)                ((a)[(c)/8] & bits[(c)&07])
+
+#define        MAXRUNE        0xFFFF
+
+uchar        f[(MAXRUNE+1)/8];
+uchar        t[(MAXRUNE+1)/8];
+char         wbuf[4096];
+char        *wptr;
+
+Pcb pfrom, pto;
+
+int cflag;
+int dflag;
+int sflag;
+
+void        complement(void);
+void        delete(void);
+void        squeeze(void);
+void        translit(void);
+void        error(char*);
+long        canon(Pcb*);
+char        *getrune(char*, Rune*);
+void        Pinit(Pcb*, char*);
+void        Prewind(Pcb *p);
+int        readrune(int, long*);
+void        wflush(int);
+void        writerune(int, Rune);
+
+void
+main(int argc, char **argv)
+{
+        ARGBEGIN{
+        case 's':        sflag++; break;
+        case 'd':        dflag++; break;
+        case 'c':        cflag++; break;
+        default:        error("bad option");
+        }ARGEND
+        if(argc>0)
+                Pinit(&pfrom, argv[0]);
+        if(argc>1)
+                Pinit(&pto, argv[1]);
+        if(argc>2)
+                error("arg count");
+        if(dflag) {
+                if ((sflag && argc != 2) || (!sflag && argc != 1))
+                        error("arg count");
+                delete();
+        } else {
+                if (argc != 2)
+                        error("arg count");
+                if (cflag)
+                        complement();
+                else translit();
+        }
+        exits(0);
+}
+
+void
+delete(void)
+{
+        long c, last;
+
+        if (cflag) {
+                memset((char *) f, 0xff, sizeof f);
+                while ((c = canon(&pfrom)) >= 0)
+                        CLEARBIT(f, c);
+        } else {
+                while ((c = canon(&pfrom)) >= 0)
+                        SETBIT(f, c);
+        }
+        if (sflag) {
+                while ((c = canon(&pto)) >= 0)
+                        SETBIT(t, c);
+        }
+
+        last = 0x10000;
+        while (readrune(0, &c) > 0) {
+                if(!BITSET(f, c) && (c != last || !BITSET(t,c))) {
+                        last = c;
+                        writerune(1, (Rune) c);
+                }
+        }
+        wflush(1);
+}
+
+void
+complement(void)
+{
+        Rune *p;
+        int i;
+        long from, to, lastc, high;
+
+        lastc = 0;
+        high = 0;
+        while ((from = canon(&pfrom)) >= 0) {
+                if (from > high) high = from;
+                SETBIT(f, from);
+        }
+        while ((to = canon(&pto)) > 0) {
+                if (to > high) high = to;
+                SETBIT(t,to);
+        }
+        Prewind(&pto);
+        if ((p = (Rune *) malloc((high+1)*sizeof(Rune))) == 0)
+                error("can't allocate memory");
+        for (i = 0; i <= high; i++){
+                if (!BITSET(f,i)) {
+                        if ((to = canon(&pto)) < 0)
+                                to = lastc;
+                        else lastc = to;
+                        p[i] = to;
+                }
+                else p[i] = i;
+        }
+        if (sflag){
+                lastc = 0x10000;
+                while (readrune(0, &from) > 0) {
+                        if (from > high)
+                                from = to;
+                        else
+                                from = p[from];
+                        if (from != lastc || !BITSET(t,from)) {
+                                lastc = from;
+                                writerune(1, (Rune) from);
+                        }
+                }
+                                
+        } else {
+                while (readrune(0, &from) > 0){
+                        if (from > high)
+                                from = to;
+                        else
+                                from = p[from];
+                        writerune(1, (Rune) from);
+                }
+        }
+        wflush(1);
+}
+
+void
+translit(void)
+{
+        Rune *p;
+        int i;
+        long from, to, lastc, high;
+
+        lastc = 0;
+        high = 0;
+        while ((from = canon(&pfrom)) >= 0)
+                if (from > high) high = from;
+        Prewind(&pfrom);
+        if ((p = (Rune *) malloc((high+1)*sizeof(Rune))) == 0)
+                error("can't allocate memory");
+        for (i = 0; i <= high; i++)
+                p[i] = i;
+        while ((from = canon(&pfrom)) >= 0) {
+                if ((to = canon(&pto)) < 0)
+                        to = lastc;
+                else lastc = to;
+                if (BITSET(f,from) && p[from] != to)
+                        error("ambiguous translation");
+                SETBIT(f,from);
+                p[from] = to;
+                SETBIT(t,to);
+        }
+        while ((to = canon(&pto)) >= 0) {
+                SETBIT(t,to);
+        }
+        if (sflag){
+                lastc = 0x10000;
+                while (readrune(0, &from) > 0) {
+                        if (from <= high)
+                                from = p[from];
+                        if (from != lastc || !BITSET(t,from)) {
+                                lastc = from;
+                                writerune(1, (Rune) from);
+                        }
+                }
+                                
+        } else {
+                while (readrune(0, &from) > 0) {
+                        if (from <= high)
+                                from = p[from];
+                        writerune(1, (Rune) from);
+                }
+        }
+        wflush(1);
+}
+
+int
+readrune(int fd, long *rp)
+{
+        Rune r;
+        int j;
+        static int i, n;
+        static char buf[4096];
+
+        j = i;
+        for (;;) {
+                if (i >= n) {
+                        wflush(1);
+                        if (j != i)
+                                memcpy(buf, buf+j, n-j);
+                        i = n-j;
+                        n = read(fd, &buf[i], sizeof(buf)-i);
+                        if (n < 0)
+                                error("read error");
+                        if (n == 0)
+                                return 0;
+                        j = 0;
+                        n += i;
+                }
+                i++;
+                if (fullrune(&buf[j], i-j))
+                        break;
+        }
+        chartorune(&r, &buf[j]);
+        *rp = r;
+        return 1;
+}
+
+void
+writerune(int fd, Rune r)
+{
+        char buf[UTFmax];
+        int n;
+
+        if (!wptr)
+                wptr = wbuf;
+        n = runetochar(buf, (Rune*)&r);
+        if (wptr+n >= wbuf+sizeof(wbuf))
+                wflush(fd);
+        memcpy(wptr, buf, n);
+        wptr += n;
+}
+
+void
+wflush(int fd)
+{
+        if (wptr && wptr > wbuf)
+                if (write(fd, wbuf, wptr-wbuf) != wptr-wbuf)
+                        error("write error");
+        wptr = wbuf;
+}
+
+char *
+getrune(char *s, Rune *rp)
+{
+        Rune r;
+        char *save;
+        int i, n;
+
+        s += chartorune(rp, s);
+        if((r = *rp) == '\\' && *s){
+                n = 0;
+                if (*s == 'x') {
+                        s++;
+                        for (i = 0; i < 4; i++) {
+                                save = s;
+                                s += chartorune(&r, s);
+                                if ('0' <= r && r <= '9')
+                                        n = 16*n + r - '0';
+                                else if ('a' <= r && r <= 'f')
+                                        n = 16*n + r - 'a' + 10;
+                                else if ('A' <= r && r <= 'F')
+                                        n = 16*n + r - 'A' + 10;
+                                else {
+                                        if (i == 0)
+                                                *rp = 'x';
+                                        else *rp = n;
+                                        return save;
+                                }
+                        }
+                } else {
+                        for(i = 0; i < 3; i++) {
+                                save = s;
+                                s += chartorune(&r, s);
+                                if('0' <= r && r <= '7')
+                                        n = 8*n + r - '0';
+                                else {
+                                        if (i == 0)
+                                        {
+                                                *rp = r;
+                                                return s;
+                                        }
+                                        *rp = n;
+                                        return save;
+                                }
+                        }
+                        if(n > 0377)
+                                error("char>0377");
+                }
+                *rp = n;
+        }
+        return s;
+}
+
+long
+canon(Pcb *p)
+{
+        Rune r;
+
+        if (p->final >= 0) {
+                if (p->last < p->final)
+                        return ++p->last;
+                p->final = -1;
+        }
+        if (*p->current == '\0')
+                return -1;
+        if(*p->current == '-' && p->last >= 0 && p->current[1]){
+                p->current = getrune(p->current+1, &r);
+                if (r < p->last)
+                        error ("Invalid range specification");
+                if (r > p->last) {
+                        p->final = r;
+                        return ++p->last;
+                }
+        }
+        p->current = getrune(p->current, &r);
+        p->last = r;
+        return p->last;
+}
+
+void
+Pinit(Pcb *p, char *cp)
+{
+        p->current = p->base = cp;
+        p->last = p->final = -1;
+}
+void
+Prewind(Pcb *p)
+{
+        p->current = p->base;
+        p->last = p->final = -1;
+}
+void
+error(char *s)
+{
+        fprint(2, "%s: %s\n", argv0, s);
+        exits(s);
+}
diff --git a/src/cmd/unicode.c b/src/cmd/unicode.c
t@@ -0,0 +1,122 @@
+#include 
+#include 
+#include 
+
+char        usage[] = "unicode { [-t] hex hex ... | hexmin-hexmax ... | [-n] char ... }";
+char        hex[] = "0123456789abcdefABCDEF";
+int        numout = 0;
+int        text = 0;
+char        *err;
+Biobuf        bout;
+
+char        *range(char*[]);
+char        *nums(char*[]);
+char        *chars(char*[]);
+
+void
+main(int argc, char *argv[])
+{
+        ARGBEGIN{
+        case 'n':
+                numout = 1;
+                break;
+        case 't':
+                text = 1;
+                break;
+        }ARGEND
+        Binit(&bout, 1, OWRITE);
+        if(argc == 0){
+                fprint(2, "usage: %s\n", usage);
+                exits("usage");
+        }
+        if(!numout && utfrune(argv[0], '-'))
+                exits(range(argv));
+        if(numout || strchr(hex, argv[0][0])==0)
+                exits(nums(argv));
+        exits(chars(argv));
+}
+
+char*
+range(char *argv[])
+{
+        char *q;
+        int min, max;
+        int i;
+
+        while(*argv){
+                q = *argv;
+                if(strchr(hex, q[0]) == 0){
+    err:
+                        fprint(2, "unicode: bad range %s\n", *argv);
+                        return "bad range";
+                }
+                min = strtoul(q, &q, 16);
+                if(min<0 || min>0xFFFF || *q!='-')
+                        goto err;
+                q++;
+                if(strchr(hex, *q) == 0)
+                        goto err;
+                max = strtoul(q, &q, 16);
+                if(max<0 || max>0xFFFF || max0xFFFF || *q!=0)
+                        goto err;
+                Bprint(&bout, "%C", m);
+                if(!text)
+                        Bprint(&bout, "\n");
+                argv++;
+        }
+        return 0;
+}
diff --git a/src/cmd/uniq.c b/src/cmd/uniq.c
t@@ -0,0 +1,169 @@
+/*
+ * Deal with duplicated lines in a file
+ */
+#include 
+#include 
+#include 
+#include 
+
+#define        SIZE        8000
+
+int        fields        = 0;
+int        letters        = 0;
+int        linec        = 0;
+char        mode;
+int        uniq;
+char        *b1, *b2;
+long        bsize;
+Biobuf        fin;
+Biobuf        fout;
+
+int        gline(char *buf);
+void        pline(char *buf);
+int        equal(char *b1, char *b2);
+char*        skip(char *s);
+
+void
+main(int argc, char *argv[])
+{
+        int f;
+
+        bsize = SIZE;
+        b1 = malloc(bsize);
+        b2 = malloc(bsize);
+        f = 0;
+        while(argc > 1) {
+                if(*argv[1] == '-') {
+                        if(isdigit(argv[1][1]))
+                                fields = atoi(&argv[1][1]);
+                        else
+                                mode = argv[1][1];
+                        argc--;
+                        argv++;
+                        continue;
+                }
+                if(*argv[1] == '+') {
+                        letters = atoi(&argv[1][1]);
+                        argc--;
+                        argv++;
+                        continue;
+                }
+                f = open(argv[1], 0);
+                if(f < 0) {
+                        fprint(2, "cannot open %s\n", argv[1]);
+                        exits("open");
+                }
+                break;
+        }
+        if(argc > 2) {
+                fprint(2, "unexpected argument %s\n", argv[2]);
+                exits("arg");
+        }
+        Binit(&fin, f, OREAD);
+        Binit(&fout, 1, OWRITE);
+
+        if(gline(b1))
+                exits(0);
+        for(;;) {
+                linec++;
+                if(gline(b2)) {
+                        pline(b1);
+                        exits(0);
+                }
+                if(!equal(b1, b2)) {
+                        pline(b1);
+                        linec = 0;
+                        do {
+                                linec++;
+                                if(gline(b1)) {
+                                        pline(b2);
+                                        exits(0);
+                                }
+                        } while(equal(b2, b1));
+                        pline(b2);
+                        linec = 0;
+                }
+        }
+}
+
+int
+gline(char *buf)
+{
+        char *p;
+
+        p = Brdline(&fin, '\n');
+        if(p == 0)
+                return 1;
+        if(fin.rdline >= bsize-1) {
+                fprint(2, "line too long\n");
+                exits("too long");
+        }
+        memmove(buf, p, fin.rdline);
+        buf[fin.rdline-1] = 0;
+        return 0;
+}
+
+void
+pline(char *buf)
+{
+
+        switch(mode) {
+
+        case 'u':
+                if(uniq) {
+                        uniq = 0;
+                        return;
+                }
+                break;
+
+        case 'd':
+                if(uniq)
+                        break;
+                return;
+
+        case 'c':
+                Bprint(&fout, "%4d ", linec);
+        }
+        uniq = 0;
+        Bprint(&fout, "%s\n", buf);
+}
+
+int
+equal(char *b1, char *b2)
+{
+        char c;
+
+        if(fields || letters) {
+                b1 = skip(b1);
+                b2 = skip(b2);
+        }
+        for(;;) {
+                c = *b1++;
+                if(c != *b2++) {
+                        if(c == 0 && mode == 's')
+                                return 1;
+                        return 0;
+                }
+                if(c == 0) {
+                        uniq++;
+                        return 1;
+                }
+        }
+}
+
+char*
+skip(char *s)
+{
+        int nf, nl;
+
+        nf = nl = 0;
+        while(nf++ < fields) {
+                while(*s == ' ' || *s == '\t')
+                        s++;
+                while(!(*s == ' ' || *s == '\t' || *s == 0) ) 
+                        s++;
+        }
+        while(nl++ < letters && *s != 0) 
+                        s++;
+        return s;
+}
diff --git a/src/cmd/unutf.c b/src/cmd/unutf.c
t@@ -0,0 +1,16 @@
+#include 
+#include 
+#include 
+
+Biobuf bin;
+
+void
+main(void)
+{
+        int c;
+
+        Binit(&bin, 0, OREAD);
+        while((c = Bgetrune(&bin)) >= 0)
+                print("0x%ux\n", c);
+        exits(0);
+}
diff --git a/src/cmd/wc.c b/src/cmd/wc.c
t@@ -0,0 +1,309 @@
+/*
+ * wc -- count things in utf-encoded text files
+ * Bugs:
+ *        The only white space characters recognized are ' ', '\t' and '\n', even though
+ *        ISO 10646 has many more blanks scattered through it.
+ *        Should count characters that cannot occur in any rune (hex f0-ff) separately.
+ *        Should count non-canonical runes (e.g. hex c1,80 instead of hex 40).
+ */
+#include 
+#include 
+#define        NBUF        (8*1024)
+uvlong nline, tnline, pline;
+uvlong nword, tnword, pword;
+uvlong nrune, tnrune, prune;
+uvlong nbadr, tnbadr, pbadr;
+uvlong nchar, tnchar, pchar;
+void count(int, char *);
+void report(uvlong, uvlong, uvlong, uvlong, uvlong, char *);
+void
+main(int argc, char *argv[])
+{
+        char *status="";
+        int i, f;
+        ARGBEGIN {
+        case 'l': pline++; break;
+        case 'w': pword++; break;
+        case 'r': prune++; break;
+        case 'b': pbadr++; break;
+        case 'c': pchar++; break;
+        default:
+                fprint(2, "Usage: %s [-lwrbc] [file ...]\n", argv0);
+                exits("usage");
+        } ARGEND
+        if(pline+pword+prune+pbadr+pchar == 0) {
+                pline = 1;
+                pword = 1;
+                pchar = 1;
+        }
+        if(argc==0)
+                count(0, 0);
+        else{
+                for(i=0;i1)
+                        report(tnline, tnword, tnrune, tnbadr, tnchar, "total");
+        }
+        exits(status);
+}
+void
+report(uvlong nline, uvlong nword, uvlong nrune, uvlong nbadr, uvlong nchar, char *fname)
+{
+        char line[1024], word[128];
+        line[0] = '\0';
+        if(pline){
+                sprint(word, " %7llud", nline);
+                strcat(line, word);
+        }
+        if(pword){
+                sprint(word, " %7llud", nword);
+                strcat(line, word);
+        }
+        if(prune){
+                sprint(word, " %7llud", nrune);
+                strcat(line, word);
+        }
+        if(pbadr){
+                sprint(word, " %7llud", nbadr);
+                strcat(line, word);
+        }
+        if(pchar){
+                sprint(word, " %7llud", nchar);
+                strcat(line, word);
+        }
+        if(fname){
+                sprint(word, " %s",   fname);
+                strcat(line, word);
+        }
+        print("%s\n", line+1);
+}
+/*
+ * How it works.  Start in statesp.  Each time we read a character,
+ * increment various counts, and do state transitions according to the
+ * following table.  If we're not in statesp or statewd when done, the
+ * file ends with a partial rune.
+ *        |                character
+ *  state |09,20| 0a  |00-7f|80-bf|c0-df|e0-ef|f0-ff
+ * -------+-----+-----+-----+-----+-----+-----+-----
+ * statesp|ASP  |ASPN |AWDW |AWDWX|AC2W |AC3W |AWDWX
+ * statewd|ASP  |ASPN |AWD  |AWDX |AC2  |AC3  |AWDX
+ * statec2|ASPX |ASPNX|AWDX |AWDR |AC2X |AC3X |AWDX
+ * statec3|ASPX |ASPNX|AWDX |AC2R |AC2X |AC3X |AWDX
+ */
+enum{                        /* actions */
+        AC2,                /* enter statec2 */
+        AC2R,                /* enter statec2, don't count a rune */
+        AC2W,                /* enter statec2, count a word */
+        AC2X,                /* enter statec2, count a bad rune */
+        AC3,                /* enter statec3 */
+        AC3W,                /* enter statec3, count a word */
+        AC3X,                /* enter statec3, count a bad rune */
+        ASP,                /* enter statesp */
+        ASPN,                /* enter statesp, count a newline */
+        ASPNX,                /* enter statesp, count a newline, count a bad rune */
+        ASPX,                /* enter statesp, count a bad rune */
+        AWD,                /* enter statewd */
+        AWDR,                /* enter statewd, don't count a rune */
+        AWDW,                /* enter statewd, count a word */
+        AWDWX,                /* enter statewd, count a word, count a bad rune */
+        AWDX,                /* enter statewd, count a bad rune */
+};
+uchar statesp[256]={        /* looking for the start of a word */
+AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,        /* 00-07 */
+AWDW, ASP,  ASPN, AWDW, AWDW, AWDW, AWDW, AWDW,        /* 08-0f */
+AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,        /* 10-17 */
+AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,        /* 18-1f */
+ASP,  AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,        /* 20-27 */
+AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,        /* 28-2f */
+AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,        /* 30-37 */
+AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,        /* 38-3f */
+AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,        /* 40-47 */
+AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,        /* 48-4f */
+AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,        /* 50-57 */
+AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,        /* 58-5f */
+AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,        /* 60-67 */
+AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,        /* 68-6f */
+AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,        /* 70-77 */
+AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,        /* 78-7f */
+AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* 80-87 */
+AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* 88-8f */
+AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* 90-97 */
+AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* 98-9f */
+AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* a0-a7 */
+AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* a8-af */
+AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* b0-b7 */
+AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* b8-bf */
+AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W,        /* c0-c7 */
+AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W,        /* c8-cf */
+AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W,        /* d0-d7 */
+AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W,        /* d8-df */
+AC3W, AC3W, AC3W, AC3W, AC3W, AC3W, AC3W, AC3W,        /* e0-e7 */
+AC3W, AC3W, AC3W, AC3W, AC3W, AC3W, AC3W, AC3W,        /* e8-ef */
+AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* f0-f7 */
+AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* f8-ff */
+};
+uchar statewd[256]={        /* looking for the next character in a word */
+AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,        /* 00-07 */
+AWD,  ASP,  ASPN, AWD,  AWD,  AWD,  AWD,  AWD,        /* 08-0f */
+AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,        /* 10-17 */
+AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,        /* 18-1f */
+ASP,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,        /* 20-27 */
+AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,        /* 28-2f */
+AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,        /* 30-37 */
+AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,        /* 38-3f */
+AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,        /* 40-47 */
+AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,        /* 48-4f */
+AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,        /* 50-57 */
+AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,        /* 58-5f */
+AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,        /* 60-67 */
+AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,        /* 68-6f */
+AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,        /* 70-77 */
+AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,        /* 78-7f */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* 80-87 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* 88-8f */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* 90-97 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* 98-9f */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* a0-a7 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* a8-af */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* b0-b7 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* b8-bf */
+AC2,  AC2,  AC2,  AC2,  AC2,  AC2,  AC2,  AC2,        /* c0-c7 */
+AC2,  AC2,  AC2,  AC2,  AC2,  AC2,  AC2,  AC2,        /* c8-cf */
+AC2,  AC2,  AC2,  AC2,  AC2,  AC2,  AC2,  AC2,        /* d0-d7 */
+AC2,  AC2,  AC2,  AC2,  AC2,  AC2,  AC2,  AC2,        /* d8-df */
+AC3,  AC3,  AC3,  AC3,  AC3,  AC3,  AC3,  AC3,        /* e0-e7 */
+AC3,  AC3,  AC3,  AC3,  AC3,  AC3,  AC3,  AC3,        /* e8-ef */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* f0-f7 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* f8-ff */
+};
+uchar statec2[256]={        /* looking for 10xxxxxx to complete a rune */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* 00-07 */
+AWDX, ASPX, ASPNX,AWDX, AWDX, AWDX, AWDX, AWDX,        /* 08-0f */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* 10-17 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* 18-1f */
+ASPX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* 20-27 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* 28-2f */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* 30-37 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* 38-3f */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* 40-47 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* 48-4f */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* 50-57 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* 58-5f */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* 60-67 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* 68-6f */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* 70-77 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* 78-7f */
+AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR,        /* 80-87 */
+AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR,        /* 88-8f */
+AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR,        /* 90-97 */
+AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR,        /* 98-9f */
+AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR,        /* a0-a7 */
+AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR,        /* a8-af */
+AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR,        /* b0-b7 */
+AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR,        /* b8-bf */
+AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X,        /* c0-c7 */
+AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X,        /* c8-cf */
+AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X,        /* d0-d7 */
+AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X,        /* d8-df */
+AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X,        /* e0-e7 */
+AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X,        /* e8-ef */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* f0-f7 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* f8-ff */
+};
+uchar statec3[256]={        /* looking for 10xxxxxx,10xxxxxx to complete a rune */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* 00-07 */
+AWDX, ASPX, ASPNX,AWDX, AWDX, AWDX, AWDX, AWDX,        /* 08-0f */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* 10-17 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* 18-1f */
+ASPX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* 20-27 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* 28-2f */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* 30-37 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* 38-3f */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* 40-47 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* 48-4f */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* 50-57 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* 58-5f */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* 60-67 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* 68-6f */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* 70-77 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* 78-7f */
+AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R,        /* 80-87 */
+AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R,        /* 88-8f */
+AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R,        /* 90-97 */
+AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R,        /* 98-9f */
+AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R,        /* a0-a7 */
+AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R,        /* a8-af */
+AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R,        /* b0-b7 */
+AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R,        /* b8-bf */
+AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X,        /* c0-c7 */
+AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X,        /* c8-cf */
+AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X,        /* d0-d7 */
+AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X,        /* d8-df */
+AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X,        /* e0-e7 */
+AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X,        /* e8-ef */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* f0-f7 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,        /* f8-ff */
+};
+void
+count(int f, char *name)
+{
+        int n;
+        uchar buf[NBUF];
+        uchar *bufp, *ebuf;
+        uchar *state=statesp;
+
+        nline = 0;
+        nword = 0;
+        nrune = 0;
+        nbadr = 0;
+        nchar = 0;
+
+        for(;;){
+                n=read(f, buf, NBUF);
+                if(n<=0)
+                        break;
+                nchar+=n;
+                nrune+=n;        /* might be too large, gets decreased later */
+                bufp=buf;
+                ebuf=buf+n;
+                do{
+                        switch(state[*bufp]){
+                        case AC2:   state=statec2;                   break;
+                        case AC2R:  state=statec2; --nrune;          break;
+                        case AC2W:  state=statec2; nword++;          break;
+                        case AC2X:  state=statec2;          nbadr++; break;
+                        case AC3:   state=statec3;                   break;
+                        case AC3W:  state=statec3; nword++;          break;
+                        case AC3X:  state=statec3;          nbadr++; break;
+                        case ASP:   state=statesp;                   break;
+                        case ASPN:  state=statesp; nline++;          break;
+                        case ASPNX: state=statesp; nline++; nbadr++; break;
+                        case ASPX:  state=statesp;          nbadr++; break;
+                        case AWD:   state=statewd;                   break;
+                        case AWDR:  state=statewd; --nrune;          break;
+                        case AWDW:  state=statewd; nword++;          break;
+                        case AWDWX: state=statewd; nword++; nbadr++; break;
+                        case AWDX:  state=statewd;          nbadr++; break;
+                        }
+                }while(++bufp!=ebuf);
+        }
+        if(state!=statesp && state!=statewd)
+                nbadr++;
+        if(n<0)
+                perror(name);
+        report(nline, nword, nrune, nbadr, nchar, name);
+}
diff --git a/src/cmd/xd.c b/src/cmd/xd.c
t@@ -0,0 +1,355 @@
+#include 
+#include 
+#include 
+
+unsigned char        odata[16];
+unsigned char        data[16];
+int                ndata;
+unsigned long        addr;
+int                repeats;
+int                swizzle;
+int                flush;
+int                abase=2;
+int                xd(char *, int);
+void                xprint(char *, long);
+void                initarg(void), swizz(void);
+enum{
+        Narg=10
+};
+typedef struct Arg Arg;
+typedef void fmtfn(char *);
+struct Arg
+{
+        int        ascii;                /* 0==none, 1==ascii */
+        int        loglen;                /* 0==1, 1==2, 2==4, 3==8 */
+        int        base;                /* 0==8, 1==10, 2==16 */
+        fmtfn        *fn;                /* function to call with data */
+        char        *afmt;                /* format to use to print address */
+        char        *fmt;                /* format to use to print data */
+}arg[Narg];
+int        narg;
+
+fmtfn        fmt0, fmt1, fmt2, fmt3, fmtc;
+fmtfn *fmt[4] = {
+        fmt0,
+        fmt1,
+        fmt2,
+        fmt3
+};
+
+char *dfmt[4][3] = {
+        " %.3uo",        " %.3ud",        " %.2ux",
+        " %.6uo",        " %.5ud",        " %.4ux",
+        " %.11luo",        " %.10lud",        " %.8lux",
+        " %.22lluo",        " %.20llud",        " %.16llux",
+};
+
+char *cfmt[3][3] = {
+        "   %c",        "   %c",         "  %c",
+        " %.3s",        " %.3s",        " %.2s",
+        " %.3uo",        " %.3ud",        " %.2ux",
+};
+
+char *afmt[2][3] = {
+        "%.7luo ",        "%.7lud ",        "%.7lux ",
+        "%7luo ",        "%7lud ",        "%7lux ",
+};
+
+Biobuf        bin;
+Biobuf        bout;
+
+void
+main(int argc, char *argv[])
+{
+        int i, err;
+        Arg *ap;
+
+        Binit(&bout, 1, OWRITE);
+        err = 0;
+        ap = 0;
+        while(argc>1 && argv[1][0]=='-' && argv[1][1]){
+                --argc;
+                argv++;
+                argv[0]++;
+                if(argv[0][0] == 'r'){
+                        repeats = 1;
+                        if(argv[0][1])
+                                goto Usage;
+                        continue;
+                }
+                if(argv[0][0] == 's'){
+                        swizzle = 1;
+                        if(argv[0][1])
+                                goto Usage;
+                        continue;
+                }
+                if(argv[0][0] == 'u'){
+                        flush = 1;
+                        if(argv[0][1])
+                                goto Usage;
+                        continue;
+                }
+                if(argv[0][0] == 'a'){
+                        argv[0]++;
+                        switch(argv[0][0]){
+                        case 'o':
+                                abase = 0;
+                                break;
+                        case 'd':
+                                abase = 1;
+                                break;
+                        case 'x':
+                                abase = 2;
+                                break;
+                        default:
+                                goto Usage;
+                        }
+                        if(argv[0][1])
+                                goto Usage;
+                        continue;
+                }
+                ap = &arg[narg];
+                initarg();
+                while(argv[0][0]){
+                        switch(argv[0][0]){
+                        case 'c':
+                                ap->ascii = 1;
+                                ap->loglen = 0;
+                                if(argv[0][1] || argv[0][-1]!='-')
+                                        goto Usage;
+                                break;
+                        case 'o':
+                                ap->base = 0;
+                                break;
+                        case 'd':
+                                ap->base = 1;
+                                break;
+                        case 'x':
+                                ap->base = 2;
+                                break;
+                        case 'b':
+                        case '1':
+                                ap->loglen = 0;
+                                break;
+                        case 'w':
+                        case '2':
+                                ap->loglen = 1;
+                                break;
+                        case 'l':
+                        case '4':
+                                ap->loglen = 2;
+                                break;
+                        case 'v':
+                        case '8':
+                                ap->loglen = 3;
+                                break;
+                        default:
+                        Usage:
+   fprint(2, "usage: xd [-u] [-r] [-s] [-a{odx}] [-c|{b1w2l4v8}{odx}] ... file ...\n");
+                                exits("usage");
+                        }
+                        argv[0]++;
+                }
+                if(ap->ascii)
+                        ap->fn = fmtc;
+                else
+                        ap->fn = fmt[ap->loglen];
+                ap->fmt = dfmt[ap->loglen][ap->base];
+                ap->afmt = afmt[ap>arg][abase];
+        }
+        if(narg == 0)
+                initarg();
+        if(argc == 1)
+                err = xd(0, 0);
+        else if(argc == 2)
+                err = xd(argv[1], 0);
+        else for(i=1; i= Narg){
+                fprint(2, "xd: too many formats (max %d)\n", Narg);
+                exits("usage");
+        }
+        ap->ascii = 0;
+        ap->loglen = 2;
+        ap->base = 2;
+        ap->fn = fmt2;
+        ap->fmt = dfmt[ap->loglen][ap->base];
+        ap->afmt = afmt[narg>1][abase];
+}
+
+int
+xd(char *name, int title)
+{
+        int fd;
+        int i, star;
+        Arg *ap;
+        Biobuf *bp;
+
+        fd = 0;
+        if(name){
+                bp = Bopen(name, OREAD);
+                if(bp == 0){
+                        fprint(2, "xd: can't open %s\n", name);
+                        return 1;
+                }
+        }else{
+                bp = &bin;
+                Binit(bp, fd, OREAD);
+        }
+        if(title)
+                xprint("%s\n", (long)name);
+        addr = 0;
+        star = 0;
+        while((ndata=Bread(bp, data, 16)) >= 0){
+                if(ndata < 16)
+                        for(i=ndata; i<16; i++)
+                                data[i] = 0;
+                if(swizzle)
+                        swizz();
+                if(ndata==16 && repeats){
+                        if(addr>0 && data[0]==odata[0]){
+                                for(i=1; i<16; i++)
+                                        if(data[i] != odata[i])
+                                                break;
+                                if(i == 16){
+                                        addr += 16;
+                                        if(star == 0){
+                                                star++;
+                                                xprint("*\n", 0);
+                                        }
+                                        continue;
+                                }
+                        }
+                        for(i=0; i<16; i++)
+                                odata[i] = data[i];
+                        star = 0;
+                }
+                for(ap=arg; ap<&arg[narg]; ap++){
+                        xprint(ap->afmt, addr);
+                        (*ap->fn)(ap->fmt);
+                        xprint("\n", 0);
+                        if(flush)
+                                Bflush(&bout);
+                }
+                addr += ndata;
+                if(ndata<16){
+                        xprint(afmt[0][abase], addr);
+                        xprint("\n", 0);
+                        if(flush)
+                                Bflush(&bout);
+                        break;
+                }
+        }
+        Bterm(bp);
+        return 0;
+}
+
+void
+swizz(void)
+{
+        uchar *p, *q;
+        int i;
+        uchar swdata[16];
+
+        p = data;
+        q = swdata;
+        for(i=0; i<16; i++)
+                *q++ = *p++;
+        p = data;
+        q = swdata;
+        for(i=0; i<4; i++){
+                p[0] = q[3];
+                p[1] = q[2];
+                p[2] = q[1];
+                p[3] = q[0];
+                p += 4;
+                q += 4;
+        }
+}
+
+void
+fmt0(char *f)
+{
+        int i;
+        for(i=0; i=0x7F || ' '>data[i])
+                                xprint(cfmt[2][2], data[i]);
+                        else
+                                xprint(cfmt[0][2], data[i]);
+                        break;
+                }
+}
+
+void
+xprint(char *fmt, long d)
+{
+        if(Bprint(&bout, fmt, d)<0){
+                fprint(2, "xd: i/o error\n");
+                exits("i/o error");
+        }
+}
diff --git a/src/cmd/yacc.c b/src/cmd/yacc.c
t@@ -0,0 +1,2939 @@
+#include 
+#include 
+#include 
+#include 
+
+#define        Bungetrune        Bungetc                /* ok for now. */
+
+/*
+ * all these are 32 bit
+ */
+#define TBITSET                ((32+NTERMS)/32)        /* BOTCH?? +31 */
+#define BIT(a,i)        ((a)[(i)>>5] & (1<<((i)&037)))
+#define SETBIT(a,i)        ((a)[(i)>>5] |= (1<<((i)&037)))
+#define NWORDS(n)        (((n)+32)/32)
+
+#define PARSER                "/sys/lib/yaccpar"
+#define PARSERS                "/sys/lib/yaccpars"
+#define TEMPNAME        "y.tmp.XXXXXX"
+#define ACTNAME                "y.acts.XXXXXX"
+#define OFILE                "tab.c"
+#define FILEU                "output"
+#define FILED                "tab.h"
+#define FILEDEBUG        "debug"
+
+enum
+{
+/*
+ * the following are adjustable
+ * according to memory size
+ */
+        ACTSIZE                = 40000,
+        MEMSIZE                = 40000,
+        NSTATES                = 2000,
+        NTERMS                = 511,
+        NPROD                = 1600,
+        NNONTERM        = 600,
+        TEMPSIZE        = 2000,
+        CNAMSZ                = 10000,
+        LSETSIZE        = 2400,
+        WSETSIZE        = 350,
+
+        NAMESIZE        = 50,
+        NTYPES                = 63,
+        ISIZE                = 400,
+
+        PRIVATE                = 0xE000,        /* unicode private use */
+
+        /* relationships which must hold:
+                TBITSET ints must hold NTERMS+1 bits...
+                WSETSIZE >= NNONTERM
+                LSETSIZE >= NNONTERM
+                TEMPSIZE >= NTERMS + NNONTERM + 1
+                TEMPSIZE >= NSTATES
+        */
+
+        NTBASE                = 010000,
+        ERRCODE                = 8190,
+        ACCEPTCODE        = 8191,
+
+        NOASC                = 0,        /* no assoc. */
+        LASC                = 1,        /* left assoc. */
+        RASC                = 2,        /* right assoc. */
+        BASC                = 3,        /* binary assoc. */
+
+        /* flags for state generation */
+
+        DONE                = 0,
+        MUSTDO                = 1,
+        MUSTLOOKAHEAD        = 2,
+
+        /* flags for a rule having an action, and being reduced */
+
+        ACTFLAG                = 04,
+        REDFLAG                = 010,
+
+        /* output parser flags */
+        YYFLAG1                = -1000,
+
+        /* parse tokens */
+        IDENTIFIER        = PRIVATE,
+        MARK,
+        TERM,
+        LEFT,
+        RIGHT,
+        BINARY,
+        PREC,
+        LCURLY,
+        IDENTCOLON,
+        NUMBER,
+        START,
+        TYPEDEF,
+        TYPENAME,
+        UNION,
+
+        ENDFILE                = 0,
+
+        EMPTY                = 1,
+        WHOKNOWS        = 0,
+        OK                = 1,
+        NOMORE                = -1000,
+};
+
+        /* macros for getting associativity and precedence levels */
+
+#define ASSOC(i)        ((i)&03)
+#define PLEVEL(i)        (((i)>>4)&077)
+#define TYPE(i)                (((i)>>10)&077)
+
+        /* macros for setting associativity and precedence levels */
+
+#define SETASC(i,j)        i |= j
+#define SETPLEV(i,j)        i |= (j<<4)
+#define SETTYPE(i,j)        i |= (j<<10)
+
+        /* looping macros */
+
+#define TLOOP(i)        for(i=1; i<=ntokens; i++)
+#define NTLOOP(i)        for(i=0; i<=nnonter; i++)
+#define PLOOP(s,i)        for(i=s; i= 0 && j < 256) {
+                        if(temp1[j]) {
+                                print("yacc bug -- cant have 2 different Ts with same value\n");
+                                print("        %s and %s\n", tokset[i].name, tokset[temp1[j]].name);
+                                nerrors++;
+                        }
+                        temp1[j] = i;
+                        if(j > c)
+                                c = j;
+                }
+        }
+        warray("yytok1", temp1, c+1);
+
+        /* table 2 has PRIVATE-PRIVATE+256 */
+        aryfil(temp1, 256, 0);
+        c = 0;
+        TLOOP(i) {
+                j = tokset[i].value - PRIVATE;
+                if(j >= 0 && j < 256) {
+                        if(temp1[j]) {
+                                print("yacc bug -- cant have 2 different Ts with same value\n");
+                                print("        %s and %s\n", tokset[i].name, tokset[temp1[j]].name);
+                                nerrors++;
+                        }
+                        temp1[j] = i;
+                        if(j > c)
+                                c = j;
+                }
+        }
+        warray("yytok2", temp1, c+1);
+
+        /* table 3 has everything else */
+        Bprint(ftable, "long        yytok3[] =\n{\n");
+        c = 0;
+        TLOOP(i) {
+                j = tokset[i].value;
+                if(j >= 0 && j < 256)
+                        continue;
+                if(j >= PRIVATE && j < 256+PRIVATE)
+                        continue;
+
+                Bprint(ftable, "%4d,%4d,", j, i);
+                c++;
+                if(c%5 == 0)
+                        Bprint(ftable, "\n");
+        }
+        Bprint(ftable, "%4d\n};\n", 0);
+
+        /* copy parser text */
+        while((c=Bgetrune(finput)) != Beof) {
+                if(c == '$') {
+                        if((c = Bgetrune(finput)) != 'A')
+                                Bputrune(ftable, '$');
+                        else { /* copy actions */
+                                faction = Bopen(actname, OREAD);
+                                if(faction == 0)
+                                        error("cannot reopen action tempfile");
+                                while((c=Bgetrune(faction)) != Beof)
+                                        Bputrune(ftable, c);
+                                Bterm(faction);
+                                ZAPFILE(actname);
+                                c = Bgetrune(finput);
+                        }
+                }
+                Bputrune(ftable, c);
+        }
+        Bterm(ftable);
+}
+
+/*
+ * copies string q into p, returning next free char ptr
+ */
+char*
+chcopy(char* p, char* q)
+{
+        int c;
+
+        while(c = *q) {
+                if(c == '"')
+                        *p++ = '\\';
+                *p++ = c;
+                q++;
+        }
+        *p = 0;
+        return p;
+}
+
+/*
+ * creates output string for item pointed to by pp
+ */
+char*
+writem(int *pp)
+{
+        int i,*p;
+        static char sarr[ISIZE];
+        char* q;
+
+        for(p=pp; *p>0; p++)
+                ;
+        p = prdptr[-*p];
+        q = chcopy(sarr, nontrst[*p-NTBASE].name);
+        q = chcopy(q, ": ");
+        for(;;) {
+                *q = ' ';
+                p++;
+                if(p == pp)
+                        *q = '.';
+                q++;
+                *q = '\0';
+                i = *p;
+                if(i <= 0)
+                        break;
+                q = chcopy(q, symnam(i));
+                if(q > &sarr[ISIZE-30])
+                        error("item too big");
+        }
+
+        /* an item calling for a reduction */
+        i = *pp;
+        if(i < 0 ) {
+                q = chcopy(q, "    (");
+                sprint(q, "%d)", -i);
+        }
+        return sarr;
+}
+
+/*
+ * return a pointer to the name of symbol i
+ */
+char*
+symnam(int i)
+{
+        char* cp;
+
+        cp = (i >= NTBASE)? nontrst[i-NTBASE].name: tokset[i].name;
+        if(*cp == ' ')
+                cp++;
+        return cp;
+}
+
+/*
+ * output the summary on y.output
+ */
+void
+summary(void)
+{
+
+        if(foutput != 0) {
+                Bprint(foutput, "\n%d/%d terminals, %d/%d nonterminals\n",
+                        ntokens, NTERMS, nnonter, NNONTERM);
+                Bprint(foutput, "%d/%d grammar rules, %d/%d states\n",
+                        nprod, NPROD, nstate, NSTATES);
+                Bprint(foutput, "%d shift/reduce, %d reduce/reduce conflicts reported\n",
+                        zzsrconf, zzrrconf);
+                Bprint(foutput, "%d/%d working sets used\n",
+                        (int)(zzcwp-wsets), WSETSIZE);
+                Bprint(foutput, "memory: states,etc. %d/%d, parser %d/%d\n",
+                        (int)(zzmemsz-mem0), MEMSIZE, (int)(memp-amem), ACTSIZE);
+                Bprint(foutput, "%d/%d distinct lookahead sets\n", nlset, LSETSIZE);
+                Bprint(foutput, "%d extra closures\n", zzclose - 2*nstate);
+                Bprint(foutput, "%d shift entries, %d exceptions\n", zzacent, zzexcp);
+                Bprint(foutput, "%d goto entries\n", zzgoent);
+                Bprint(foutput, "%d entries saved by goto default\n", zzgobest);
+        }
+        if(zzsrconf != 0 || zzrrconf != 0) {
+                print("\nconflicts: ");
+                if(zzsrconf)
+                        print("%d shift/reduce", zzsrconf);
+                if(zzsrconf && zzrrconf)
+                        print(", ");
+                if(zzrrconf)
+                        print("%d reduce/reduce", zzrrconf);
+                print("\n");
+        }
+        if(ftemp != 0) {
+                Bterm(ftemp);
+                ftemp = 0;
+        }
+        if(fdefine != 0) {
+                Bterm(fdefine);
+                fdefine = 0;
+        }
+}
+
+/*
+ * write out error comment -- NEEDS WORK
+ */
+void
+error(char *s, ...)
+{
+
+        nerrors++;
+        fprint(2, "\n fatal error:");
+        fprint(2, s, (&s)[1]);
+        fprint(2, ", %s:%d\n", infile, lineno);
+        if(!fatfl)
+                return;
+        summary();
+        cleantmp();
+        exits("error");
+}
+
+/*
+ * set elements 0 through n-1 to c
+ */
+void
+aryfil(int *v, int n, int c)
+{
+        int i;
+
+        for(i=0; ilset;
+        if(pp == 0)
+                Bprint(foutput, "\tNULL");
+        else {
+                Bprint(foutput, " { ");
+                TLOOP(j)
+                        if(BIT(pp,j))
+                                Bprint(foutput, "%s ", symnam(j));
+                Bprint(foutput, "}");
+        }
+}
+
+/*
+ * compute an array with the beginnings of  productions yielding given nonterminals
+ * The array pres points to these lists
+ * the array pyield has the lists: the total size is only NPROD+1
+ */
+void
+cpres(void)
+{
+        int c, j, i, **pmem;
+        static int *pyield[NPROD];
+
+        pmem = pyield;
+        NTLOOP(i) {
+                c = i+NTBASE;
+                pres[i] = pmem;
+                fatfl = 0;          /* make undefined  symbols  nonfatal */
+                PLOOP(0, j)
+                        if(*prdptr[j] == c)
+                                *pmem++ =  prdptr[j]+1;
+                if(pres[i] == pmem)
+                        error("nonterminal %s not defined!", nontrst[i].name);
+        }
+        pres[i] = pmem;
+        fatfl = 1;
+        if(nerrors) {
+                summary();
+                cleantmp();
+                exits("error");
+        }
+        if(pmem != &pyield[nprod])
+                error("internal Yacc error: pyield %d", pmem-&pyield[nprod]);
+}
+
+/*
+ * compute an array with the first of nonterminals
+ */
+void
+cpfir(void)
+{
+        int *p, **s, i, **t, ch, changes;
+
+        zzcwp = &wsets[nnonter];
+        NTLOOP(i) {
+                aryfil(wsets[i].ws.lset, tbitset, 0);
+                t = pres[i+1];
+                /* initially fill the sets */
+                for(s=pres[i]; s 0; ++p) {
+                                if(ch < NTBASE) {
+                                        SETBIT(wsets[i].ws.lset, ch);
+                                        break;
+                                }
+                                if(!pempty[ch-NTBASE])
+                                        break;
+                        }
+        }
+
+        /* now, reflect transitivity */
+        changes = 1;
+        while(changes) {
+                changes = 0;
+                NTLOOP(i) {
+                        t = pres[i+1];
+                        for(s = pres[i]; s < t; ++s)
+                                for(p = *s; (ch = (*p-NTBASE)) >= 0; ++p) {
+                                        changes |= setunion(wsets[i].ws.lset, wsets[ch].ws.lset);
+                                        if(!pempty[ch])
+                                                break;
+                                }
+                }
+        }
+
+        NTLOOP(i)
+                pfirst[i] = flset(&wsets[i].ws);
+        if(!indebug)
+                return;
+        if(foutput != 0)
+                NTLOOP(i) {
+                        Bprint(foutput, "\n%s: ", nontrst[i].name);
+                        prlook(pfirst[i]);
+                        Bprint(foutput, " %d\n", pempty[i]);
+                }
+}
+
+/*
+ * sorts last state,and sees if it equals earlier ones. returns state number
+ */
+int
+state(int c)
+{
+        Item *p1, *p2, *k, *l, *q1, *q2;
+        int size1, size2, i;
+
+        p1 = pstate[nstate];
+        p2 = pstate[nstate+1];
+        if(p1 == p2)
+                return 0;        /* null state */
+        /* sort the items */
+        for(k = p2-1; k > p1; k--)        /* make k the biggest */
+                for(l = k-1; l >= p1; --l)
+                        if(l->pitem > k->pitem) {
+                                int *s;
+                                Lkset *ss;
+
+                                s = k->pitem;
+                                k->pitem = l->pitem;
+                                l->pitem = s;
+                                ss = k->look;
+                                k->look = l->look;
+                                l->look = ss;
+                        }
+        size1 = p2 - p1;        /* size of state */
+
+        for(i = (c>=NTBASE)? ntstates[c-NTBASE]: tstates[c]; i != 0; i = mstates[i]) {
+                /* get ith state */
+                q1 = pstate[i];
+                q2 = pstate[i+1];
+                size2 = q2 - q1;
+                if(size1 != size2)
+                        continue;
+                k = p1;
+                for(l = q1; l < q2; l++) {
+                        if(l->pitem != k->pitem)
+                                break;
+                        k++;
+                }
+                if(l != q2)
+                        continue;
+                /* found it */
+                pstate[nstate+1] = pstate[nstate];        /* delete last state */
+                /* fix up lookaheads */
+                if(nolook)
+                        return i;
+                for(l = q1, k = p1; l < q2; ++l, ++k ) {
+                        int s;
+
+                        SETLOOP(s)
+                                clset.lset[s] = l->look->lset[s];
+                        if(setunion(clset.lset, k->look->lset)) {
+                                tystate[i] = MUSTDO;
+                                /* register the new set */
+                                l->look = flset( &clset );
+                        }
+                }
+                return i;
+        }
+        /* state is new */
+        if(nolook)
+                error("yacc state/nolook error");
+        pstate[nstate+2] = p2;
+        if(nstate+1 >= NSTATES)
+                error("too many states");
+        if(c >= NTBASE) {
+                mstates[nstate] = ntstates[c-NTBASE];
+                ntstates[c-NTBASE] = nstate;
+        } else {
+                mstates[nstate] = tstates[c];
+                tstates[c] = nstate;
+        }
+        tystate[nstate] = MUSTDO;
+        return nstate++;
+}
+
+void
+putitem(int *ptr, Lkset *lptr)
+{
+        Item *j;
+
+        if(pidebug && foutput != 0)
+                Bprint(foutput, "putitem(%s), state %d\n", writem(ptr), nstate);
+        j = pstate[nstate+1];
+        j->pitem = ptr;
+        if(!nolook)
+                j->look = flset(lptr);
+        pstate[nstate+1] = ++j;
+        if((int*)j > zzmemsz) {
+                zzmemsz = (int*)j;
+                if(zzmemsz >=  &mem0[MEMSIZE])
+                        error("out of state space");
+        }
+}
+
+/*
+ * mark nonterminals which derive the empty string
+ * also, look for nonterminals which don't derive any token strings
+ */
+void
+cempty(void)
+{
+
+        int i, *p;
+
+        /* first, use the array pempty to detect productions that can never be reduced */
+        /* set pempty to WHONOWS */
+        aryfil(pempty, nnonter+1, WHOKNOWS);
+
+        /* now, look at productions, marking nonterminals which derive something */
+more:
+        PLOOP(0, i) {
+                if(pempty[*prdptr[i] - NTBASE])
+                        continue;
+                for(p = prdptr[i]+1; *p >= 0; ++p)
+                        if(*p >= NTBASE && pempty[*p-NTBASE] == WHOKNOWS)
+                                break;
+                /* production can be derived */
+                if(*p < 0) {
+                        pempty[*prdptr[i]-NTBASE] = OK;
+                        goto more;
+                }
+        }
+
+        /* now, look at the nonterminals, to see if they are all OK */
+        NTLOOP(i) {
+                /* the added production rises or falls as the start symbol ... */
+                if(i == 0)
+                        continue;
+                if(pempty[i] != OK) {
+                        fatfl = 0;
+                        error("nonterminal %s never derives any token string", nontrst[i].name);
+                }
+        }
+
+        if(nerrors) {
+                summary();
+                cleantmp();
+                exits("error");
+        }
+
+        /* now, compute the pempty array, to see which nonterminals derive the empty string */
+        /* set pempty to WHOKNOWS */
+        aryfil( pempty, nnonter+1, WHOKNOWS);
+
+        /* loop as long as we keep finding empty nonterminals */
+
+again:
+        PLOOP(1, i) {
+                /* not known to be empty */
+                if(pempty[*prdptr[i]-NTBASE] == WHOKNOWS) {
+                        for(p = prdptr[i]+1; *p >= NTBASE && pempty[*p-NTBASE] == EMPTY ; ++p)
+                                ;
+                        /* we have a nontrivially empty nonterminal */
+                        if(*p < 0) {
+                                pempty[*prdptr[i]-NTBASE] = EMPTY;
+                                /* got one ... try for another */
+                                goto again;
+                        }
+                }
+        }
+}
+
+/*
+ * generate the states
+ */
+void
+stagen(void)
+{
+
+        int c, i, j, more;
+        Wset *p, *q;
+
+        /* initialize */
+        nstate = 0;
+
+        /* THIS IS FUNNY from the standpoint of portability
+         * it represents the magic moment when the mem0 array, which has
+         * been holding the productions, starts to hold item pointers, of a
+         * different type...
+         * someday, alloc should be used to allocate all this stuff... for now, we
+         * accept that if pointers don't fit in integers, there is a problem...
+         */
+
+        pstate[0] = pstate[1] = (Item*)mem;
+        aryfil(clset.lset, tbitset, 0);
+        putitem(prdptr[0]+1, &clset);
+        tystate[0] = MUSTDO;
+        nstate = 1;
+        pstate[2] = pstate[1];
+
+        aryfil(amem, ACTSIZE, 0);
+
+        /* now, the main state generation loop */
+        for(more=1; more;) {
+                more = 0;
+                SLOOP(i) {
+                        if(tystate[i] != MUSTDO)
+                                continue;
+                        tystate[i] = DONE;
+                        aryfil(temp1, nnonter+1, 0);
+                        /* take state i, close it, and do gotos */
+                        closure(i);
+                        /* generate goto's */
+                        WSLOOP(wsets, p) {
+                                if(p->flag)
+                                        continue;
+                                p->flag = 1;
+                                c = *(p->pitem);
+                                if(c <= 1) {
+                                        if(pstate[i+1]-pstate[i] <= p-wsets)
+                                                tystate[i] = MUSTLOOKAHEAD;
+                                        continue;
+                                }
+                                /* do a goto on c */
+                                WSLOOP(p, q)
+                                        /* this item contributes to the goto */
+                                        if(c == *(q->pitem)) {
+                                                putitem(q->pitem+1, &q->ws);
+                                                q->flag = 1;
+                                        }
+                                if(c < NTBASE)
+                                        state(c);        /* register new state */
+                                else
+                                        temp1[c-NTBASE] = state(c);
+                        }
+                        if(gsdebug && foutput != 0) {
+                                Bprint(foutput, "%d: ", i);
+                                NTLOOP(j)
+                                        if(temp1[j])
+                                                Bprint(foutput, "%s %d, ",
+                                                nontrst[j].name, temp1[j]);
+                                Bprint(foutput, "\n");
+                        }
+                        indgo[i] = apack(&temp1[1], nnonter-1) - 1;
+                        /* do some more */
+                        more = 1;
+                }
+        }
+}
+
+/*
+ * generate the closure of state i
+ */
+void
+closure(int i)
+{
+
+        Wset *u, *v;
+        Item *p, *q;
+        int c, ch, work, k, *pi, **s, **t;
+
+        zzclose++;
+
+        /* first, copy kernel of state i to wsets */
+        cwp = wsets;
+        ITMLOOP(i, p, q) {
+                cwp->pitem = p->pitem;
+                cwp->flag = 1;                        /* this item must get closed */
+                SETLOOP(k)
+                        cwp->ws.lset[k] = p->look->lset[k];
+                WSBUMP(cwp);
+        }
+
+        /* now, go through the loop, closing each item */
+        work = 1;
+        while(work) {
+                work = 0;
+                WSLOOP(wsets, u) {
+                        if(u->flag == 0)
+                                continue;
+                        /* dot is before c */
+                        c = *(u->pitem);
+                        if(c < NTBASE) {
+                                u->flag = 0;
+                                /* only interesting case is where . is before nonterminal */
+                                continue;
+                        }
+
+                        /* compute the lookahead */
+                        aryfil(clset.lset, tbitset, 0);
+
+                        /* find items involving c */
+                        WSLOOP(u, v)
+                                if(v->flag == 1 && *(pi=v->pitem) == c) {
+                                        v->flag = 0;
+                                        if(nolook)
+                                                continue;
+                                        while((ch = *++pi) > 0) {
+                                                /* terminal symbol */
+                                                if(ch < NTBASE) {
+                                                        SETBIT(clset.lset, ch);
+                                                        break;
+                                                }
+                                                /* nonterminal symbol */
+                                                setunion(clset.lset, pfirst[ch-NTBASE]->lset);
+                                                if(!pempty[ch-NTBASE])
+                                                        break;
+                                        }
+                                        if(ch <= 0)
+                                                setunion(clset.lset, v->ws.lset);
+                                }
+
+                        /*
+                         * now loop over productions derived from c
+                         * c is now nonterminal number
+                         */
+                        c -= NTBASE;
+                        t = pres[c+1];
+                        for(s = pres[c]; s < t; ++s) {
+                                /*
+                                 * put these items into the closure
+                                 * is the item there
+                                 */
+                                WSLOOP(wsets, v)
+                                        /* yes, it is there */
+                                        if(v->pitem == *s) {
+                                                if(nolook)
+                                                        goto nexts;
+                                                if(setunion(v->ws.lset, clset.lset))
+                                                        v->flag = work = 1;
+                                                goto nexts;
+                                        }
+
+                                /*  not there; make a new entry */
+                                if(cwp-wsets+1 >= WSETSIZE)
+                                        error( "working set overflow");
+                                cwp->pitem = *s;
+                                cwp->flag = 1;
+                                if(!nolook) {
+                                        work = 1;
+                                        SETLOOP(k) cwp->ws.lset[k] = clset.lset[k];
+                                }
+                                WSBUMP(cwp);
+
+                        nexts:;
+                        }
+                }
+        }
+
+        /* have computed closure; flags are reset; return */
+        if(cwp > zzcwp)
+                zzcwp = cwp;
+        if(cldebug && foutput != 0) {
+                Bprint(foutput, "\nState %d, nolook = %d\n", i, nolook);
+                WSLOOP(wsets, u) {
+                        if(u->flag)
+                                Bprint(foutput, "flag set!\n");
+                        u->flag = 0;
+                        Bprint(foutput, "\t%s", writem(u->pitem));
+                        prlook(&u->ws);
+                        Bprint(foutput, "\n");
+                }
+        }
+}
+
+/*
+ * decide if the lookahead set pointed to by p is known
+ * return pointer to a perminent location for the set
+ */
+Lkset*
+flset(Lkset *p)
+{
+        Lkset *q;
+        int *u, *v, *w, j;
+
+        for(q = &lkst[nlset]; q-- > lkst;) {
+                u = p->lset;
+                v = q->lset;
+                w = &v[tbitset];
+                while(v < w)
+                        if(*u++ != *v++)
+                                goto more;
+                /* we have matched */
+                return q;
+        more:;
+        }
+        /* add a new one */
+        q = &lkst[nlset++];
+        if(nlset >= LSETSIZE)
+                error("too many lookahead sets");
+        SETLOOP(j)
+                q->lset[j] = p->lset[j];
+        return q;
+}
+
+void
+cleantmp(void)
+{
+        ZAPFILE(actname);
+        ZAPFILE(tempname);
+}
+
+void
+intr(void)
+{
+        cleantmp();
+        exits("interrupted");
+}
+
+void
+setup(int argc, char *argv[])
+{
+        long c, t;
+        int i, j, fd, lev, ty, ytab, *p;
+        int vflag, dflag, stem;
+        char actnm[8], *stemc, *s, dirbuf[128];
+
+        ytab = 0;
+        vflag = 0;
+        dflag = 0;
+        stem = 0;
+        stemc = "y";
+        foutput = 0;
+        fdefine = 0;
+        fdebug = 0;
+        ARGBEGIN{
+        case 'v':
+        case 'V':
+                vflag++;
+                break;
+        case 'D':
+                yydebug = ARGF();
+                break;
+        case 'd':
+                dflag++;
+                break;
+        case 'o':
+                ytab++;
+                ytabc = ARGF();
+                break;
+        case 's':
+                stem++;
+                stemc = ARGF();
+                break;
+        case 'S':
+                parser = PARSERS;
+                break;
+        default:
+                error("illegal option: %c", ARGC());
+        }ARGEND
+        openup(stemc, dflag, vflag, ytab, ytabc);
+
+        if((fd = mkstemp(ttempname)) >= 0){
+                tempname = ttempname;
+                ftemp = Bfdopen(fd, OWRITE);
+        }
+        if((fd = mkstemp(tactname)) >= 0){
+                actname = tactname;
+                faction = Bfdopen(fd, OWRITE);
+        }
+        if(ftemp == 0 || faction == 0)
+                error("cannot open temp file");
+        if(argc < 1)
+                error("no input file");
+        infile = argv[0];
+        if(infile[0] != '/' && getwd(dirbuf, sizeof dirbuf)!=nil){
+                i = strlen(infile)+1+strlen(dirbuf)+1+10;
+                s = malloc(i);
+                if(s != nil){
+                        snprint(s, i, "%s/%s", dirbuf, infile);
+                        cleanname(s);
+                        infile = s;
+                }
+        }
+        finput = Bopen(infile, OREAD);
+        if(finput == 0)
+                error("cannot open '%s'", argv[0]);
+        cnamp = cnames;
+
+        defin(0, "$end");
+        extval = PRIVATE;        /* tokens start in unicode 'private use' */
+        defin(0, "error");
+        defin(1, "$accept");
+        defin(0, "$unk");
+        mem = mem0;
+        i = 0;
+
+        for(t = gettok(); t != MARK && t != ENDFILE;)
+        switch(t) {
+        case ';':
+                t = gettok();
+                break;
+
+        case START:
+                if(gettok() != IDENTIFIER)
+                        error("bad %%start construction");
+                start = chfind(1, tokname);
+                t = gettok();
+                continue;
+
+        case TYPEDEF:
+                if(gettok() != TYPENAME)
+                        error("bad syntax in %%type");
+                ty = numbval;
+                for(;;) {
+                        t = gettok();
+                        switch(t) {
+                        case IDENTIFIER:
+                                if((t=chfind(1, tokname)) < NTBASE) {
+                                        j = TYPE(toklev[t]);
+                                        if(j != 0 && j != ty)
+                                                error("type redeclaration of token %s",
+                                                        tokset[t].name);
+                                        else
+                                                SETTYPE(toklev[t], ty);
+                                } else {
+                                        j = nontrst[t-NTBASE].value;
+                                        if(j != 0 && j != ty)
+                                                error("type redeclaration of nonterminal %s",
+                                                        nontrst[t-NTBASE].name );
+                                        else
+                                                nontrst[t-NTBASE].value = ty;
+                                }
+                        case ',':
+                                continue;
+                        case ';':
+                                t = gettok();
+                        default:
+                                break;
+                        }
+                        break;
+                }
+                continue;
+
+        case UNION:
+                /* copy the union declaration to the output */
+                cpyunion();
+                t = gettok();
+                continue;
+
+        case LEFT:
+        case BINARY:
+        case RIGHT:
+                i++;
+
+        case TERM:
+                /* nonzero means new prec. and assoc. */
+                lev = t-TERM;
+                ty = 0;
+
+                /* get identifiers so defined */
+                t = gettok();
+
+                /* there is a type defined */
+                if(t == TYPENAME) {
+                        ty = numbval;
+                        t = gettok();
+                }
+                for(;;) {
+                        switch(t) {
+                        case ',':
+                                t = gettok();
+                                continue;
+
+                        case ';':
+                                break;
+
+                        case IDENTIFIER:
+                                j = chfind(0, tokname);
+                                if(j >= NTBASE)
+                                        error("%s defined earlier as nonterminal", tokname);
+                                if(lev) {
+                                        if(ASSOC(toklev[j]))
+                                                error("redeclaration of precedence of %s", tokname);
+                                        SETASC(toklev[j], lev);
+                                        SETPLEV(toklev[j], i);
+                                }
+                                if(ty) {
+                                        if(TYPE(toklev[j]))
+                                                error("redeclaration of type of %s", tokname);
+                                        SETTYPE(toklev[j],ty);
+                                }
+                                t = gettok();
+                                if(t == NUMBER) {
+                                        tokset[j].value = numbval;
+                                        if(j < ndefout && j > 3)
+                                                error("please define type number of %s earlier",
+                                                        tokset[j].name);
+                                        t = gettok();
+                                }
+                                continue;
+                        }
+                        break;
+                }
+                continue;
+
+        case LCURLY:
+                defout(0);
+                cpycode();
+                t = gettok();
+                continue;
+
+        default:
+                error("syntax error");
+        }
+        if(t == ENDFILE)
+                error("unexpected EOF before %%");
+
+        /* t is MARK */
+        Bprint(ftable, "extern        int        yyerrflag;\n");
+        Bprint(ftable, "#ifndef        YYMAXDEPTH\n");
+        Bprint(ftable, "#define        YYMAXDEPTH        150\n");
+        Bprint(ftable, "#endif\n" );
+        if(!ntypes) {
+                Bprint(ftable, "#ifndef        YYSTYPE\n");
+                Bprint(ftable, "#define        YYSTYPE        int\n");
+                Bprint(ftable, "#endif\n");
+        }
+        Bprint(ftable, "YYSTYPE        yylval;\n");
+        Bprint(ftable, "YYSTYPE        yyval;\n");
+
+        prdptr[0] = mem;
+
+        /* added production */
+        *mem++ = NTBASE;
+
+        /* if start is 0, we will overwrite with the lhs of the first rule */
+        *mem++ = start;
+        *mem++ = 1;
+        *mem++ = 0;
+        prdptr[1] = mem;
+        while((t=gettok()) == LCURLY)
+                cpycode();
+        if(t != IDENTCOLON)
+                error("bad syntax on first rule");
+
+        if(!start)
+                prdptr[0][1] = chfind(1, tokname);
+
+        /* read rules */
+        while(t != MARK && t != ENDFILE) {
+                /* process a rule */
+                rlines[nprod] = lineno;
+                if(t == '|')
+                        *mem++ = *prdptr[nprod-1];
+                else
+                        if(t == IDENTCOLON) {
+                                *mem = chfind(1, tokname);
+                                if(*mem < NTBASE)
+                                        error("token illegal on LHS of grammar rule");
+                                mem++;
+                        } else
+                                error("illegal rule: missing semicolon or | ?");
+                /* read rule body */
+                t = gettok();
+
+        more_rule:
+                while(t == IDENTIFIER) {
+                        *mem = chfind(1, tokname);
+                        if(*mem < NTBASE)
+                                levprd[nprod] = toklev[*mem];
+                        mem++;
+                        t = gettok();
+                }
+                if(t == PREC) {
+                        if(gettok() != IDENTIFIER)
+                                error("illegal %%prec syntax");
+                        j = chfind(2, tokname);
+                        if(j >= NTBASE)
+                                error("nonterminal %s illegal after %%prec",
+                                        nontrst[j-NTBASE].name);
+                        levprd[nprod] = toklev[j];
+                        t = gettok();
+                }
+                if(t == '=') {
+                        levprd[nprod] |= ACTFLAG;
+                        Bprint(faction, "\ncase %d:", nprod);
+                        cpyact(mem-prdptr[nprod]-1);
+                        Bprint(faction, " break;");
+                        if((t=gettok()) == IDENTIFIER) {
+
+                                /* action within rule... */
+                                sprint(actnm, "$$%d", nprod);
+
+                                /* make it a nonterminal */
+                                j = chfind(1, actnm);
+
+                                /*
+                                 * the current rule will become rule number nprod+1
+                                 * move the contents down, and make room for the null
+                                 */
+                                for(p = mem; p >= prdptr[nprod]; --p)
+                                        p[2] = *p;
+                                mem += 2;
+
+                                /* enter null production for action */
+                                p = prdptr[nprod];
+                                *p++ = j;
+                                *p++ = -nprod;
+
+                                /* update the production information */
+                                levprd[nprod+1] = levprd[nprod] & ~ACTFLAG;
+                                levprd[nprod] = ACTFLAG;
+                                if(++nprod >= NPROD)
+                                        error("more than %d rules", NPROD);
+                                prdptr[nprod] = p;
+
+                                /* make the action appear in the original rule */
+                                *mem++ = j;
+
+                                /* get some more of the rule */
+                                goto more_rule;
+                        }
+                }
+
+                while(t == ';')
+                        t = gettok();
+                *mem++ = -nprod;
+
+                /* check that default action is reasonable */
+                if(ntypes && !(levprd[nprod]&ACTFLAG) && nontrst[*prdptr[nprod]-NTBASE].value) {
+
+                        /* no explicit action, LHS has value */
+                        int tempty;
+
+                        tempty = prdptr[nprod][1];
+                        if(tempty < 0)
+                                error("must return a value, since LHS has a type");
+                        else
+                                if(tempty >= NTBASE)
+                                        tempty = nontrst[tempty-NTBASE].value;
+                                else
+                                        tempty = TYPE(toklev[tempty]);
+                        if(tempty != nontrst[*prdptr[nprod]-NTBASE].value)
+                                error("default action causes potential type clash");
+                }
+                nprod++;
+                if(nprod >= NPROD)
+                        error("more than %d rules", NPROD);
+                prdptr[nprod] = mem;
+                levprd[nprod] = 0;
+        }
+
+        /* end of all rules */
+        defout(1);
+
+        finact();
+        if(t == MARK) {
+                Bprint(ftable, "\n#line\t%d\t\"%s\"\n", lineno, infile);
+                while((c=Bgetrune(finput)) != Beof)
+                        Bputrune(ftable, c);
+        }
+        Bterm(finput);
+}
+
+/*
+ * finish action routine
+ */
+void
+finact(void)
+{
+
+        Bterm(faction);
+        Bprint(ftable, "#define YYEOFCODE %d\n", 1);
+        Bprint(ftable, "#define YYERRCODE %d\n", 2);
+}
+
+/*
+ * define s to be a terminal if t=0
+ * or a nonterminal if t=1
+ */
+int
+defin(int nt, char *s)
+{
+        int val;
+        Rune rune;
+
+        val = 0;
+        if(nt) {
+                nnonter++;
+                if(nnonter >= NNONTERM)
+                        error("too many nonterminals, limit %d",NNONTERM);
+                nontrst[nnonter].name = cstash(s);
+                return NTBASE + nnonter;
+        }
+
+        /* must be a token */
+        ntokens++;
+        if(ntokens >= NTERMS)
+                error("too many terminals, limit %d", NTERMS);
+        tokset[ntokens].name = cstash(s);
+
+        /* establish value for token */
+        /* single character literal */
+        if(s[0] == ' ') {
+                val = chartorune(&rune, &s[1]);
+                if(s[val+1] == 0) {
+                        val = rune;
+                        goto out;
+                }
+        }
+
+        /* escape sequence */
+        if(s[0] == ' ' && s[1] == '\\') {
+                if(s[3] == 0) {
+                        /* single character escape sequence */
+                        switch(s[2]) {
+                        case 'n':        val = '\n'; break;
+                        case 'r':        val = '\r'; break;
+                        case 'b':        val = '\b'; break;
+                        case 't':        val = '\t'; break;
+                        case 'f':        val = '\f'; break;
+                        case '\'':        val = '\''; break;
+                        case '"':        val = '"'; break;
+                        case '\\':        val = '\\'; break;
+                        default:        error("invalid escape");
+                        }
+                        goto out;
+                }
+
+                /* \nnn sequence */
+                if(s[2] >= '0' && s[2] <= '7') {
+                        if(s[3] < '0' ||
+                           s[3] > '7' ||
+                           s[4] < '0' ||
+                           s[4] > '7' ||
+                           s[5] != 0)
+                                error("illegal \\nnn construction");
+                        val = 64*s[2] + 8*s[3] + s[4] - 73*'0';
+                        if(val == 0)
+                                error("'\\000' is illegal");
+                        goto out;
+                }
+                error("unknown escape");
+        }
+        val = extval++;
+
+out:
+        tokset[ntokens].value = val;
+        toklev[ntokens] = 0;
+        return ntokens;
+}
+
+/*
+ * write out the defines (at the end of the declaration section)
+ */
+void
+defout(int last)
+{
+        int i, c;
+        char sar[NAMESIZE+10];
+
+        for(i=ndefout; i<=ntokens; i++) {
+                /* non-literals */
+                c = tokset[i].name[0];
+                if(c != ' ' && c != '$') {
+                        Bprint(ftable, "#define        %s        %d\n",
+                                tokset[i].name, tokset[i].value);
+                        if(fdefine)
+                                Bprint(fdefine, "#define\t%s\t%d\n",
+                                        tokset[i].name, tokset[i].value);
+                }
+        }
+        ndefout = ntokens+1;
+        if(last && fdebug) {
+                Bprint(fdebug, "char*        yytoknames[] =\n{\n");
+                TLOOP(i) {
+                        if(tokset[i].name) {
+                                chcopy(sar, tokset[i].name);
+                                Bprint(fdebug, "\t\"%s\",\n", sar);
+                                continue;
+                        }
+                        Bprint(fdebug, "\t0,\n");
+                }
+                Bprint(fdebug, "};\n");
+        }
+}
+
+char*
+cstash(char *s)
+{
+        char *temp;
+
+        temp = cnamp;
+        do {
+                if(cnamp >= &cnames[cnamsz])
+                        error("too many characters in id's and literals");
+                else
+                        *cnamp++ = *s;
+        } while(*s++);
+        return temp;
+}
+
+long
+gettok(void)
+{
+        long c;
+        Rune rune;
+        int i, base, match, reserve;
+        static int peekline;
+
+begin:
+        reserve = 0;
+        lineno += peekline;
+        peekline = 0;
+        c = Bgetrune(finput);
+        while(c == ' ' || c == '\n' || c == '\t' || c == '\f') {
+                if(c == '\n')
+                        lineno++;
+                c = Bgetrune(finput);
+        }
+
+        /* skip comment */
+        if(c == '/') {
+                lineno += skipcom();
+                goto begin;
+        }
+        switch(c) {
+        case Beof:
+                return ENDFILE;
+
+        case '{':
+                Bungetrune(finput);
+                return '=';
+
+        case '<':
+                /* get, and look up, a type name (union member name) */
+                i = 0;
+                while((c=Bgetrune(finput)) != '>' && c >= 0 && c != '\n') {
+                        rune = c;
+                        c = runetochar(&tokname[i], &rune);
+                        if(i < NAMESIZE)
+                                i += c;
+                }
+                if(c != '>')
+                        error("unterminated < ... > clause");
+                tokname[i] = 0;
+                for(i=1; i<=ntypes; i++)
+                        if(!strcmp(typeset[i], tokname)) {
+                                numbval = i;
+                                return TYPENAME;
+                        }
+                ntypes++;
+                numbval = ntypes;
+                typeset[numbval] = cstash(tokname);
+                return TYPENAME;
+
+        case '"':
+        case '\'':
+                match = c;
+                tokname[0] = ' ';
+                i = 1;
+                for(;;) {
+                        c = Bgetrune(finput);
+                        if(c == '\n' || c <= 0)
+                                error("illegal or missing ' or \"" );
+                        if(c == '\\') {
+                                tokname[i] = '\\';
+                                if(i < NAMESIZE)
+                                        i++;
+                                c = Bgetrune(finput);
+                        } else
+                                if(c == match)
+                                        break;
+                        rune = c;
+                        c = runetochar(&tokname[i], &rune);
+                        if(i < NAMESIZE)
+                                i += c;
+                }
+                break;
+
+        case '%':
+        case '\\':
+                switch(c = Bgetrune(finput)) {
+                case '0':        return TERM;
+                case '<':        return LEFT;
+                case '2':        return BINARY;
+                case '>':        return RIGHT;
+                case '%':
+                case '\\':        return MARK;
+                case '=':        return PREC;
+                case '{':        return LCURLY;
+                default:        reserve = 1;
+                }
+
+        default:
+                /* number */
+                if(isdigit(c)) {
+                        numbval = c-'0';
+                        base = (c=='0')? 8: 10;
+                        for(c = Bgetrune(finput); isdigit(c); c = Bgetrune(finput))
+                                numbval = numbval*base + (c-'0');
+                        Bungetrune(finput);
+                        return NUMBER;
+                }
+                if(islower(c) || isupper(c) || c=='_' || c=='.' || c=='$')  {
+                        i = 0;
+                        while(islower(c) || isupper(c) || isdigit(c) ||
+                            c == '-' || c=='_' || c=='.' || c=='$') {
+                                if(reserve && isupper(c))
+                                        c += 'a'-'A';
+                                rune = c;
+                                c = runetochar(&tokname[i], &rune);
+                                if(i < NAMESIZE)
+                                        i += c;
+                                c = Bgetrune(finput);
+                        }
+                } else
+                        return c;
+                Bungetrune(finput);
+        }
+        tokname[i] = 0;
+
+        /* find a reserved word */
+        if(reserve) {
+                for(c=0; resrv[c].name; c++)
+                        if(strcmp(tokname, resrv[c].name) == 0)
+                                return resrv[c].value;
+                error("invalid escape, or illegal reserved word: %s", tokname);
+        }
+
+        /* look ahead to distinguish IDENTIFIER from IDENTCOLON */
+        c = Bgetrune(finput);
+        while(c == ' ' || c == '\t'|| c == '\n' || c == '\f' || c == '/') {
+                if(c == '\n')
+                        peekline++;
+                /* look for comments */
+                if(c == '/')
+                        peekline += skipcom();
+                c = Bgetrune(finput);
+        }
+        if(c == ':')
+                return IDENTCOLON;
+        Bungetrune(finput);
+        return IDENTIFIER;
+}
+
+/*
+ * determine the type of a symbol
+ */
+int
+fdtype(int t)
+{
+        int v;
+
+        if(t >= NTBASE)
+                v = nontrst[t-NTBASE].value;
+        else
+                v = TYPE(toklev[t]);
+        if(v <= 0)
+                error("must specify type for %s", (t>=NTBASE)?
+                        nontrst[t-NTBASE].name: tokset[t].name);
+        return v;
+}
+
+int
+chfind(int t, char *s)
+{
+        int i;
+
+        if(s[0] == ' ')
+                t = 0;
+        TLOOP(i)
+                if(!strcmp(s, tokset[i].name))
+                        return i;
+        NTLOOP(i)
+                if(!strcmp(s, nontrst[i].name))
+                        return NTBASE+i;
+
+        /* cannot find name */
+        if(t > 1)
+                error("%s should have been defined earlier", s);
+        return defin(t, s);
+}
+
+/*
+ * copy the union declaration to the output, and the define file if present
+ */
+void
+cpyunion(void)
+{
+        long c;
+        int level;
+
+        Bprint(ftable, "\n#line\t%d\t\"%s\"\n", lineno, infile);
+        Bprint(ftable, "typedef union ");
+        if(fdefine != 0)
+                Bprint(fdefine, "\ntypedef union ");
+
+        level = 0;
+        for(;;) {
+                if((c=Bgetrune(finput)) == Beof)
+                        error("EOF encountered while processing %%union");
+                Bputrune(ftable, c);
+                if(fdefine != 0)
+                        Bputrune(fdefine, c);
+                switch(c) {
+                case '\n':
+                        lineno++;
+                        break;
+                case '{':
+                        level++;
+                        break;
+                case '}':
+                        level--;
+
+                        /* we are finished copying */
+                        if(level == 0) {
+                                Bprint(ftable, " YYSTYPE;\n");
+                                if(fdefine != 0)
+                                        Bprint(fdefine, "\tYYSTYPE;\nextern\tYYSTYPE\tyylval;\n");
+                                return;
+                        }
+                }
+        }
+}
+
+/*
+ * copies code between \{ and \}
+ */
+void
+cpycode(void)
+{
+
+        long c;
+
+        c = Bgetrune(finput);
+        if(c == '\n') {
+                c = Bgetrune(finput);
+                lineno++;
+        }
+        Bprint(ftable, "\n#line\t%d\t\"%s\"\n", lineno, infile);
+        while(c != Beof) {
+                if(c == '\\') {
+                        if((c=Bgetrune(finput)) == '}')
+                                return;
+                        Bputc(ftable, '\\');
+                }
+                if(c == '%') {
+                        if((c=Bgetrune(finput)) == '}')
+                                return;
+                        Bputc(ftable, '%');
+                }
+                Bputrune(ftable, c);
+                if(c == '\n')
+                        lineno++;
+                c = Bgetrune(finput);
+        }
+        error("eof before %%}");
+}
+
+/*
+ * skip over comments
+ * skipcom is called after reading a '/'
+ */
+int
+skipcom(void)
+{
+        long c;
+        int i;
+
+        /* i is the number of lines skipped */
+        i = 0;
+        if(Bgetrune(finput) != '*')
+                error("illegal comment");
+        c = Bgetrune(finput);
+        while(c != Beof) {
+                while(c == '*')
+                        if((c=Bgetrune(finput)) == '/')
+                                return i;
+                if(c == '\n')
+                        i++;
+                c = Bgetrune(finput);
+        }
+        error("EOF inside comment");
+        return 0;
+}
+
+/*
+ * copy C action to the next ; or closing }
+ */
+void
+cpyact(int offset)
+{
+        long c;
+        int brac, match, j, s, fnd, tok;
+
+        Bprint(faction, "\n#line\t%d\t\"%s\"\n", lineno, infile);
+        brac = 0;
+
+loop:
+        c = Bgetrune(finput);
+swt:
+        switch(c) {
+        case ';':
+                if(brac == 0) {
+                        Bputrune(faction, c);
+                        return;
+                }
+                goto lcopy;
+
+        case '{':
+                brac++;
+                goto lcopy;
+
+        case '$':
+                s = 1;
+                tok = -1;
+                c = Bgetrune(finput);
+
+                /* type description */
+                if(c == '<') {
+                        Bungetrune(finput);
+                        if(gettok() != TYPENAME)
+                                error("bad syntax on $ clause");
+                        tok = numbval;
+                        c = Bgetrune(finput);
+                }
+                if(c == '$') {
+                        Bprint(faction, "yyval");
+
+                        /* put out the proper tag... */
+                        if(ntypes) {
+                                if(tok < 0)
+                                        tok = fdtype(*prdptr[nprod]);
+                                Bprint(faction, ".%s", typeset[tok]);
+                        }
+                        goto loop;
+                }
+                if(c == '-') {
+                        s = -s;
+                        c = Bgetrune(finput);
+                }
+                if(isdigit(c)) {
+                        j = 0;
+                        while(isdigit(c)) {
+                                j = j*10 + (c-'0');
+                                c = Bgetrune(finput);
+                        }
+                        Bungetrune(finput);
+                        j = j*s - offset;
+                        if(j > 0)
+                                error("Illegal use of $%d", j+offset);
+
+                dollar:
+                        Bprint(faction, "yypt[-%d].yyv", -j);
+
+                        /* put out the proper tag */
+                        if(ntypes) {
+                                if(j+offset <= 0 && tok < 0)
+                                        error("must specify type of $%d", j+offset);
+                                if(tok < 0)
+                                        tok = fdtype(prdptr[nprod][j+offset]);
+                                Bprint(faction, ".%s", typeset[tok]);
+                        }
+                        goto loop;
+                }
+                if(isupper(c) || islower(c) || c == '_' || c == '.') {
+                        int tok; /* tok used oustide for type info */
+
+                        /* look for $name */
+                        Bungetrune(finput);
+                        if(gettok() != IDENTIFIER)
+                                error("$ must be followed by an identifier");
+                        tok = chfind(2, tokname);
+                        if((c = Bgetrune(finput)) != '#') {
+                                Bungetrune(finput);
+                                fnd = -1;
+                        } else
+                                if(gettok() != NUMBER) {
+                                        error("# must be followed by number");
+                                        fnd = -1;
+                                } else
+                                        fnd = numbval;
+                        for(j=1; j<=offset; ++j)
+                                if(tok == prdptr[nprod][j]) {
+                                        if(--fnd <= 0) {
+                                                j -= offset;
+                                                goto dollar;
+                                        }
+                                }
+                        error("$name or $name#number not found");
+                }
+                Bputc(faction, '$');
+                if(s < 0 )
+                        Bputc(faction, '-');
+                goto swt;
+
+        case '}':
+                brac--;
+                if(brac)
+                        goto lcopy;
+                Bputrune(faction, c);
+                return;
+
+        case '/':
+                /* look for comments */
+                Bputrune(faction, c);
+                c = Bgetrune(finput);
+                if(c != '*')
+                        goto swt;
+
+                /* it really is a comment */
+                Bputrune(faction, c);
+                c = Bgetrune(finput);
+                while(c >= 0) {
+                        while(c == '*') {
+                                Bputrune(faction, c);
+                                if((c=Bgetrune(finput)) == '/')
+                                        goto lcopy;
+                        }
+                        Bputrune(faction, c);
+                        if(c == '\n')
+                                lineno++;
+                        c = Bgetrune(finput);
+                }
+                error("EOF inside comment");
+
+        case '\'':
+                /* character constant */
+                match = '\'';
+                goto string;
+
+        case '"':
+                /* character string */
+                match = '"';
+
+        string:
+                Bputrune(faction, c);
+                while(c = Bgetrune(finput)) {
+                        if(c == '\\') {
+                                Bputrune(faction, c);
+                                c = Bgetrune(finput);
+                                if(c == '\n')
+                                        lineno++;
+                        } else
+                                if(c == match)
+                                        goto lcopy;
+                                if(c == '\n')
+                                        error("newline in string or char. const.");
+                        Bputrune(faction, c);
+                }
+                error("EOF in string or character constant");
+
+        case Beof:
+                error("action does not terminate");
+
+        case '\n':
+                lineno++;
+                goto lcopy;
+        }
+
+lcopy:
+        Bputrune(faction, c);
+        goto loop;
+}
+
+void
+openup(char *stem, int dflag, int vflag, int ytab, char *ytabc)
+{
+        char buf[256];
+
+        if(vflag) {
+                sprint(buf, "%s.%s", stem, FILEU);
+                foutput = Bopen(buf, OWRITE);
+                if(foutput == 0)
+                        error("cannot open %s", buf);
+        }
+        if(yydebug) {
+                sprint(buf, "%s.%s", stem, FILEDEBUG);
+                if((fdebug = Bopen(buf, OWRITE)) == 0)
+                        error("can't open %s", buf);
+        }
+        if(dflag) {
+                sprint(buf, "%s.%s", stem, FILED);
+                fdefine = Bopen(buf, OWRITE);
+                if(fdefine == 0)
+                        error("can't create %s", buf);
+        }
+        if(ytab == 0)
+                sprint(buf, "%s.%s", stem, OFILE);
+        else
+                strcpy(buf, ytabc);
+        ftable = Bopen(buf, OWRITE);
+        if(ftable == 0)
+                error("cannot open table file %s", buf);
+}
+
+/*
+ * print the output for the states
+ */
+void
+output(void)
+{
+        int i, k, c;
+        Wset *u, *v;
+
+        Bprint(ftable, "short        yyexca[] =\n{");
+        if(fdebug)
+                Bprint(fdebug, "char*        yystates[] =\n{\n");
+
+        /* output the stuff for state i */
+        SLOOP(i) {
+                nolook = tystate[i]!=MUSTLOOKAHEAD;
+                closure(i);
+
+                /* output actions */
+                nolook = 1;
+                aryfil(temp1, ntokens+nnonter+1, 0);
+                WSLOOP(wsets, u) {
+                        c = *(u->pitem);
+                        if(c > 1 && c < NTBASE && temp1[c] == 0) {
+                                WSLOOP(u, v)
+                                        if(c == *(v->pitem))
+                                                putitem(v->pitem+1, (Lkset*)0);
+                                temp1[c] = state(c);
+                        } else
+                                if(c > NTBASE && temp1[(c -= NTBASE) + ntokens] == 0)
+                                        temp1[c+ntokens] = amem[indgo[i]+c];
+                }
+                if(i == 1)
+                        temp1[1] = ACCEPTCODE;
+
+                /* now, we have the shifts; look at the reductions */
+                lastred = 0;
+                WSLOOP(wsets, u) {
+                        c = *u->pitem;
+
+                        /* reduction */
+                        if(c <= 0) {
+                                lastred = -c;
+                                TLOOP(k)
+                                        if(BIT(u->ws.lset, k)) {
+                                                if(temp1[k] == 0)
+                                                        temp1[k] = c;
+                                                else
+                                                if(temp1[k] < 0) { /* reduce/reduce conflict */
+                                                        if(foutput)
+                                                                Bprint(foutput,
+                                                                        "\n%d: reduce/reduce conflict"
+                                                                        " (red'ns %d and %d ) on %s",
+                                                                        i, -temp1[k], lastred,
+                                                                        symnam(k));
+                                                        if(-temp1[k] > lastred)
+                                                                temp1[k] = -lastred;
+                                                        zzrrconf++;
+                                                } else
+                                                        /* potential shift/reduce conflict */
+                                                        precftn( lastred, k, i );
+                                        }
+                                }
+                }
+                wract(i);
+        }
+
+        if(fdebug)
+                Bprint(fdebug, "};\n");
+        Bprint(ftable, "};\n");
+        Bprint(ftable, "#define        YYNPROD        %d\n", nprod);
+        Bprint(ftable, "#define        YYPRIVATE %d\n", PRIVATE);
+        if(yydebug)
+                Bprint(ftable, "#define        yydebug        %s\n", yydebug);
+}
+
+/*
+ * pack state i from temp1 into amem
+ */
+int
+apack(int *p, int n)
+{
+        int *pp, *qq, *rr, off, *q, *r;
+
+        /* we don't need to worry about checking because
+         * we will only look at entries known to be there...
+         * eliminate leading and trailing 0's
+         */
+
+        q = p+n;
+        for(pp = p, off = 0; *pp == 0 && pp <= q; ++pp, --off)
+                ;
+         /* no actions */
+        if(pp > q)
+                return 0;
+        p = pp;
+
+        /* now, find a place for the elements from p to q, inclusive */
+        r = &amem[ACTSIZE-1];
+        for(rr = amem; rr <= r; rr++, off++) {
+                for(qq = rr, pp = p; pp <= q; pp++, qq++)
+                        if(*pp != 0)
+                                if(*pp != *qq && *qq != 0)
+                                        goto nextk;
+
+                /* we have found an acceptable k */
+                if(pkdebug && foutput != 0)
+                        Bprint(foutput, "off = %d, k = %d\n", off, (int)(rr-amem));
+                for(qq = rr, pp = p; pp <= q; pp++, qq++)
+                        if(*pp) {
+                                if(qq > r)
+                                        error("action table overflow");
+                                if(qq > memp)
+                                        memp = qq;
+                                *qq = *pp;
+                        }
+                if(pkdebug && foutput != 0)
+                        for(pp = amem; pp <= memp; pp += 10) {
+                                Bprint(foutput, "\t");
+                                for(qq = pp; qq <= pp+9; qq++)
+                                        Bprint(foutput, "%d ", *qq);
+                                Bprint(foutput, "\n");
+                        }
+                return(off);
+        nextk:;
+        }
+        error("no space in action table");
+        return 0;
+}
+
+/*
+ * output the gotos for the nontermninals
+ */
+void
+go2out(void)
+{
+        int i, j, k, best, count, cbest, times;
+
+        /* mark begining of gotos */
+        Bprint(ftemp, "$\n");
+        for(i = 1; i <= nnonter; i++) {
+                go2gen(i);
+
+                /* find the best one to make default */
+                best = -1;
+                times = 0;
+
+                /* is j the most frequent */
+                for(j = 0; j <= nstate; j++) {
+                        if(tystate[j] == 0)
+                                continue;
+                        if(tystate[j] == best)
+                                continue;
+
+                        /* is tystate[j] the most frequent */
+                        count = 0;
+                        cbest = tystate[j];
+                        for(k = j; k <= nstate; k++)
+                                if(tystate[k] == cbest)
+                                        count++;
+                        if(count > times) {
+                                best = cbest;
+                                times = count;
+                        }
+                }
+
+                /* best is now the default entry */
+                zzgobest += times-1;
+                for(j = 0; j <= nstate; j++)
+                        if(tystate[j] != 0 && tystate[j] != best) {
+                                Bprint(ftemp, "%d,%d,", j, tystate[j]);
+                                zzgoent++;
+                        }
+
+                /* now, the default */
+                if(best == -1)
+                        best = 0;
+                zzgoent++;
+                Bprint(ftemp, "%d\n", best);
+        }
+}
+
+/*
+ * output the gotos for nonterminal c
+ */
+void
+go2gen(int c)
+{
+        int i, work, cc;
+        Item *p, *q;
+
+
+        /* first, find nonterminals with gotos on c */
+        aryfil(temp1, nnonter+1, 0);
+        temp1[c] = 1;
+        work = 1;
+        while(work) {
+                work = 0;
+                PLOOP(0, i)
+
+                        /* cc is a nonterminal */
+                        if((cc=prdptr[i][1]-NTBASE) >= 0)
+                                /* cc has a goto on c */
+                                if(temp1[cc] != 0) {
+
+                                        /* thus, the left side of production i does too */
+                                        cc = *prdptr[i]-NTBASE;
+                                        if(temp1[cc] == 0) {
+                                                  work = 1;
+                                                  temp1[cc] = 1;
+                                        }
+                                }
+        }
+
+        /* now, we have temp1[c] = 1 if a goto on c in closure of cc */
+        if(g2debug && foutput != 0) {
+                Bprint(foutput, "%s: gotos on ", nontrst[c].name);
+                NTLOOP(i)
+                        if(temp1[i])
+                                Bprint(foutput, "%s ", nontrst[i].name);
+                Bprint(foutput, "\n");
+        }
+
+        /* now, go through and put gotos into tystate */
+        aryfil(tystate, nstate, 0);
+        SLOOP(i)
+                ITMLOOP(i, p, q)
+                        if((cc = *p->pitem) >= NTBASE)
+                                /* goto on c is possible */
+                                if(temp1[cc-NTBASE]) {
+                                        tystate[i] = amem[indgo[i]+c];
+                                        break;
+                                }
+}
+
+/*
+ * decide a shift/reduce conflict by precedence.
+ * r is a rule number, t a token number
+ * the conflict is in state s
+ * temp1[t] is changed to reflect the action
+ */
+void
+precftn(int r, int t, int s)
+{
+        int lp, lt, action;
+
+        lp = levprd[r];
+        lt = toklev[t];
+        if(PLEVEL(lt) == 0 || PLEVEL(lp) == 0) {
+
+                /* conflict */
+                if(foutput != 0)
+                        Bprint(foutput,
+                                "\n%d: shift/reduce conflict (shift %d(%d), red'n %d(%d)) on %s",
+                                s, temp1[t], PLEVEL(lt), r, PLEVEL(lp), symnam(t));
+                zzsrconf++;
+                return;
+        }
+        if(PLEVEL(lt) == PLEVEL(lp))
+                action = ASSOC(lt);
+        else
+                if(PLEVEL(lt) > PLEVEL(lp))
+                        action = RASC;  /* shift */
+                else
+                        action = LASC;  /* reduce */
+        switch(action) {
+        case BASC:  /* error action */
+                temp1[t] = ERRCODE;
+                break;
+        case LASC:  /* reduce */
+                temp1[t] = -r;
+                break;
+        }
+}
+
+/*
+ * output state i
+ * temp1 has the actions, lastred the default
+ */
+void
+wract(int i)
+{
+        int p, p0, p1, ntimes, tred, count, j, flag;
+
+        /* find the best choice for lastred */
+        lastred = 0;
+        ntimes = 0;
+        TLOOP(j) {
+                if(temp1[j] >= 0)
+                        continue;
+                if(temp1[j]+lastred == 0)
+                        continue;
+                /* count the number of appearances of temp1[j] */
+                count = 0;
+                tred = -temp1[j];
+                levprd[tred] |= REDFLAG;
+                TLOOP(p)
+                        if(temp1[p]+tred == 0)
+                                count++;
+                if(count > ntimes) {
+                        lastred = tred;
+                        ntimes = count;
+                }
+        }
+
+        /*
+         * for error recovery, arrange that, if there is a shift on the
+         * error recovery token, `error', that the default be the error action
+         */
+        if(temp1[2] > 0)
+                lastred = 0;
+
+        /* clear out entries in temp1 which equal lastred */
+        TLOOP(p)
+                if(temp1[p]+lastred == 0)
+                        temp1[p] = 0;
+
+        wrstate(i);
+        defact[i] = lastred;
+        flag = 0;
+        TLOOP(p0)
+                if((p1=temp1[p0]) != 0) {
+                        if(p1 < 0) {
+                                p1 = -p1;
+                                goto exc;
+                        }
+                        if(p1 == ACCEPTCODE) {
+                                p1 = -1;
+                                goto exc;
+                        }
+                        if(p1 == ERRCODE) {
+                                p1 = 0;
+                        exc:
+                                if(flag++ == 0)
+                                        Bprint(ftable, "-1, %d,\n", i);
+                                Bprint(ftable, "\t%d, %d,\n", p0, p1);
+                                zzexcp++;
+                                continue;
+                        }
+                        Bprint(ftemp, "%d,%d,", p0, p1);
+                        zzacent++;
+                }
+        if(flag) {
+                defact[i] = -2;
+                Bprint(ftable, "\t-2, %d,\n", lastred);
+        }
+        Bprint(ftemp, "\n");
+}
+
+/*
+ * writes state i
+ */
+void
+wrstate(int i)
+{
+        int j0, j1;
+        Item *pp, *qq;
+        Wset *u;
+
+        if(fdebug) {
+                if(lastred) {
+                        Bprint(fdebug, "        0, /*%d*/\n", i);
+                } else {
+                        Bprint(fdebug, "        \"");
+                        ITMLOOP(i, pp, qq)
+                                Bprint(fdebug, "%s\\n", writem(pp->pitem));
+                        if(tystate[i] == MUSTLOOKAHEAD)
+                                WSLOOP(wsets + (pstate[i+1] - pstate[i]), u)
+                                        if(*u->pitem < 0)
+                                                Bprint(fdebug, "%s\\n", writem(u->pitem));
+                        Bprint(fdebug, "\", /*%d*/\n", i);
+                }
+        }
+        if(foutput == 0)
+                return;
+        Bprint(foutput, "\nstate %d\n", i);
+        ITMLOOP(i, pp, qq)
+                Bprint(foutput, "\t%s\n", writem(pp->pitem));
+        if(tystate[i] == MUSTLOOKAHEAD)
+                /* print out empty productions in closure */
+                WSLOOP(wsets+(pstate[i+1]-pstate[i]), u)
+                        if(*u->pitem < 0)
+                                Bprint(foutput, "\t%s\n", writem(u->pitem));
+
+        /* check for state equal to another */
+        TLOOP(j0)
+                if((j1=temp1[j0]) != 0) {
+                        Bprint(foutput, "\n\t%s  ", symnam(j0));
+                        /* shift, error, or accept */
+                        if(j1 > 0) {
+                                if(j1 == ACCEPTCODE)
+                                        Bprint(foutput,  "accept");
+                                else
+                                        if(j1 == ERRCODE)
+                                                Bprint(foutput, "error");
+                                        else
+                                                Bprint(foutput, "shift %d", j1);
+                        } else
+                                Bprint(foutput, "reduce %d (src line %d)", -j1, rlines[-j1]);
+                }
+
+        /* output the final production */
+        if(lastred)
+                Bprint(foutput, "\n\t.  reduce %d (src line %d)\n\n",
+                        lastred, rlines[lastred]);
+        else
+                Bprint(foutput, "\n\t.  error\n\n");
+
+        /* now, output nonterminal actions */
+        j1 = ntokens;
+        for(j0 = 1; j0 <= nnonter; j0++) {
+                j1++;
+                if(temp1[j1])
+                        Bprint(foutput, "\t%s  goto %d\n", symnam(j0+NTBASE), temp1[j1]);
+        }
+}
+
+void
+warray(char *s, int *v, int n)
+{
+        int i;
+
+        Bprint(ftable, "short        %s[] =\n{", s);
+        for(i=0;;) {
+                if(i%10 == 0)
+                        Bprint(ftable, "\n");
+                Bprint(ftable, "%4d", v[i]);
+                i++;
+                if(i >= n) {
+                        Bprint(ftable, "\n};\n");
+                        break;
+                }
+                Bprint(ftable, ",");
+        }
+}
+
+/*
+ * in order to free up the mem and amem arrays for the optimizer,
+ * and still be able to output yyr1, etc., after the sizes of
+ * the action array is known, we hide the nonterminals
+ * derived by productions in levprd.
+ */
+
+void
+hideprod(void)
+{
+        int i, j;
+
+        j = 0;
+        levprd[0] = 0;
+        PLOOP(1, i) {
+                if(!(levprd[i] & REDFLAG)) {
+                        j++;
+                        if(foutput != 0)
+                                Bprint(foutput, "Rule not reduced:   %s\n", writem(prdptr[i]));
+                }
+                levprd[i] = *prdptr[i] - NTBASE;
+        }
+        if(j)
+                print("%d rules never reduced\n", j);
+}
+
+void
+callopt(void)
+{
+        int i, *p, j, k, *q;
+
+        /* read the arrays from tempfile and set parameters */
+        finput = Bopen(tempname, OREAD);
+        if(finput == 0)
+                error("optimizer cannot open tempfile");
+
+        pgo[0] = 0;
+        temp1[0] = 0;
+        nstate = 0;
+        nnonter = 0;
+        for(;;) {
+                switch(gtnm()) {
+                case '\n':
+                        nstate++;
+                        pmem--;
+                        temp1[nstate] = pmem - mem0;
+                case ',':
+                        continue;
+                case '$':
+                        break;
+                default:
+                        error("bad tempfile");
+                }
+                break;
+        }
+
+        pmem--;
+        temp1[nstate] = yypgo[0] = pmem - mem0;
+        for(;;) {
+                switch(gtnm()) {
+                case '\n':
+                        nnonter++;
+                        yypgo[nnonter] = pmem-mem0;
+                case ',':
+                        continue;
+                case -1:
+                        break;
+                default:
+                        error("bad tempfile");
+                }
+                break;
+        }
+        pmem--;
+        yypgo[nnonter--] = pmem - mem0;
+        for(i = 0; i < nstate; i++) {
+                k = 32000;
+                j = 0;
+                q = mem0 + temp1[i+1];
+                for(p = mem0 + temp1[i]; p < q ; p += 2) {
+                        if(*p > j)
+                                j = *p;
+                        if(*p < k)
+                                k = *p;
+                }
+                /* nontrivial situation */
+                if(k <= j) {
+                        /* j is now the range */
+/*                        j -= k;                        *//* call scj */
+                        if(k > maxoff)
+                                maxoff = k;
+                }
+                tystate[i] = (temp1[i+1]-temp1[i]) + 2*j;
+                if(j > maxspr)
+                        maxspr = j;
+        }
+
+        /* initialize ggreed table */
+        for(i = 1; i <= nnonter; i++) {
+                ggreed[i] = 1;
+                j = 0;
+
+                /* minimum entry index is always 0 */
+                q = mem0 + yypgo[i+1] - 1;
+                for(p = mem0+yypgo[i]; p < q ; p += 2) {
+                        ggreed[i] += 2;
+                        if(*p > j)
+                                j = *p;
+                }
+                ggreed[i] = ggreed[i] + 2*j;
+                if(j > maxoff)
+                        maxoff = j;
+        }
+
+        /* now, prepare to put the shift actions into the amem array */
+        for(i = 0; i < ACTSIZE; i++)
+                amem[i] = 0;
+        maxa = amem;
+        for(i = 0; i < nstate; i++) {
+                if(tystate[i] == 0 && adb > 1)
+                        Bprint(ftable, "State %d: null\n", i);
+                indgo[i] = YYFLAG1;
+        }
+        while((i = nxti()) != NOMORE)
+                if(i >= 0)
+                        stin(i);
+                else
+                        gin(-i);
+
+        /* print amem array */
+        if(adb > 2 )
+                for(p = amem; p <= maxa; p += 10) {
+                        Bprint(ftable, "%4d  ", (int)(p-amem));
+                        for(i = 0; i < 10; ++i)
+                                Bprint(ftable, "%4d  ", p[i]);
+                        Bprint(ftable, "\n");
+                }
+
+        /* write out the output appropriate to the language */
+        aoutput();
+        osummary();
+        ZAPFILE(tempname);
+}
+
+void
+gin(int i)
+{
+        int *p, *r, *s, *q1, *q2;
+
+        /* enter gotos on nonterminal i into array amem */
+        ggreed[i] = 0;
+
+        q2 = mem0+ yypgo[i+1] - 1;
+        q1 = mem0 + yypgo[i];
+
+        /* now, find amem place for it */
+        for(p = amem; p < &amem[ACTSIZE]; p++) {
+                if(*p)
+                        continue;
+                for(r = q1; r < q2; r += 2) {
+                        s = p + *r + 1;
+                        if(*s)
+                                goto nextgp;
+                        if(s > maxa)
+                                if((maxa = s) > &amem[ACTSIZE])
+                                        error("a array overflow");
+                }
+                /* we have found amem spot */
+                *p = *q2;
+                if(p > maxa)
+                        if((maxa = p) > &amem[ACTSIZE])
+                                error("a array overflow");
+                for(r = q1; r < q2; r += 2) {
+                        s = p + *r + 1;
+                        *s = r[1];
+                }
+                pgo[i] = p-amem;
+                if(adb > 1)
+                        Bprint(ftable, "Nonterminal %d, entry at %d\n", i, pgo[i]);
+                return;
+
+        nextgp:;
+        }
+        error("cannot place goto %d\n", i);
+}
+
+void
+stin(int i)
+{
+        int *r, *s, n, flag, j, *q1, *q2;
+
+        tystate[i] = 0;
+
+        /* enter state i into the amem array */
+        q2 = mem0+temp1[i+1];
+        q1 = mem0+temp1[i];
+        /* find an acceptable place */
+        for(n = -maxoff; n < ACTSIZE; n++) {
+                flag = 0;
+                for(r = q1; r < q2; r += 2) {
+                        if((s = *r + n + amem) < amem)
+                                goto nextn;
+                        if(*s == 0)
+                                flag++;
+                        else
+                                if(*s != r[1])
+                                        goto nextn;
+                }
+
+                /* check that the position equals another only if the states are identical */
+                for(j=0; j 1)
+                                                Bprint(ftable,
+                                                "State %d: entry at %d equals state %d\n",
+                                                i, n, j);
+                                        return;
+                                }
+
+                                /* we have some disagreement */
+                                goto nextn;
+                        }
+                }
+
+                for(r = q1; r < q2; r += 2) {
+                        if((s = *r+n+amem) >= &amem[ACTSIZE])
+                                error("out of space in optimizer a array");
+                        if(s > maxa)
+                                maxa = s;
+                        if(*s != 0 && *s != r[1])
+                                error("clobber of a array, pos'n %d, by %d", s-amem, r[1]);
+                        *s = r[1];
+                }
+                indgo[i] = n;
+                if(adb > 1)
+                        Bprint(ftable, "State %d: entry at %d\n", i, indgo[i]);
+                return;
+        nextn:;
+        }
+        error("Error; failure to place state %d\n", i);
+}
+
+/*
+ * finds the next i
+ */
+int
+nxti(void)
+{
+        int i, max, maxi;
+
+        max = 0;
+        maxi = 0;
+        for(i = 1; i <= nnonter; i++)
+                if(ggreed[i] >= max) {
+                        max = ggreed[i];
+                        maxi = -i;
+                }
+        for(i = 0; i < nstate; ++i)
+                if(tystate[i] >= max) {
+                        max = tystate[i];
+                        maxi = i;
+                }
+        if(nxdb)
+                Bprint(ftable, "nxti = %d, max = %d\n", maxi, max);
+        if(max == 0)
+                return NOMORE;
+        return maxi;
+}
+
+/*
+ * write summary
+ */
+void
+osummary(void)
+{
+
+        int i, *p;
+
+        if(foutput == 0)
+                return;
+        i = 0;
+        for(p = maxa; p >= amem; p--)
+                if(*p == 0)
+                        i++;
+
+        Bprint(foutput, "Optimizer space used: input %d/%d, output %d/%d\n",
+                (int)(pmem-mem0+1), MEMSIZE, (int)(maxa-amem+1), ACTSIZE);
+        Bprint(foutput, "%d table entries, %d zero\n", (int)(maxa-amem+1), i);
+        Bprint(foutput, "maximum spread: %d, maximum offset: %d\n", maxspr, maxoff);
+}
+
+/*
+ * this version is for C
+ * write out the optimized parser
+ */
+void
+aoutput(void)
+{
+        Bprint(ftable, "#define\tYYLAST\t%d\n", (int)(maxa-amem+1));
+        arout("yyact", amem, (maxa-amem)+1);
+        arout("yypact", indgo, nstate);
+        arout("yypgo", pgo, nnonter+1);
+}
+
+void
+arout(char *s, int *v, int n)
+{
+        int i;
+
+        Bprint(ftable, "short        %s[] =\n{", s);
+        for(i = 0; i < n;) {
+                if(i%10 == 0)
+                        Bprint(ftable, "\n");
+                Bprint(ftable, "%4d", v[i]);
+                i++;
+                if(i == n)
+                        Bprint(ftable, "\n};\n");
+                else
+                        Bprint(ftable, ",");
+        }
+}
+
+/*
+ * read and convert an integer from the standard input
+ * return the terminating character
+ * blanks, tabs, and newlines are ignored
+ */
+int
+gtnm(void)
+{
+        int sign, val, c;
+
+        sign = 0;
+        val = 0;
+        while((c=Bgetrune(finput)) != Beof) {
+                if(isdigit(c)) {
+                        val = val*10 + c-'0';
+                        continue;
+                }
+                if(c == '-') {
+                        sign = 1;
+                        continue;
+                }
+                break;
+        }
+        if(sign)
+                val = -val;
+        *pmem++ = val;
+        if(pmem >= &mem0[MEMSIZE])
+                error("out of space");
+        return c;
+}