URI: 
       tnew utilities. the .C files compile but are renamed to avoid building automatically. - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit bc7cb1a15a67c859c8c71c4b52bb35fe9425a63d
   DIR parent f08fdedcee12c06e3ce9ac9bec363915978e8289
  HTML Author: rsc <devnull@localhost>
       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(-)
       ---
   DIR diff --git a/src/cmd/ascii.c b/src/cmd/ascii.c
       t@@ -0,0 +1,181 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <bio.h>
       +
       +#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<nchars;i++){
       +                        put(i);
       +                        if((i&7)==7)
       +                                Bprint(&bin, "|\n");
       +                }
       +        }else{
       +                if(text==1)
       +                        text=isnum(argv[0]);
       +                while(argc--)
       +                        if(text)
       +                                puttext(*argv++);
       +                        else
       +                                putnum(*argv++);
       +        }
       +        Bputc(&bin, '\n');
       +        exits(0);
       +}
       +void
       +usage(void)
       +{
       +        fprint(2, "usage: %s [-8] [-xod | -b8] [-ncst] [--] [text]\n", argv0);
       +        exits("usage");
       +}
       +void
       +put(int i)
       +{
       +        Bputc(&bin, '|');
       +        putn(i, ncol);
       +        Bprint(&bin, " %s", str[i]);
       +}
       +char dig[]="0123456789abcdefghijklmnopqrstuvwxyz";
       +void
       +putn(int n, int ndig)
       +{
       +        if(ndig==0)
       +                return;
       +        putn(n/base, ndig-1);
       +        Bputc(&bin, dig[n%base]);
       +}
       +void
       +puttext(char *s)
       +{
       +        int n;
       +        n=btoi(s)&0377;
       +        if(strip)
       +                Bputc(&bin, n);
       +        else
       +                Bprint(&bin, "%s\n", str[n]);
       +}
       +void
       +putnum(char *s)
       +{
       +        while(*s){
       +                putn(*s++&0377, ncol);
       +                Bputc(&bin, '\n');
       +        }
       +}
       +int
       +btoi(char *s)
       +{
       +        int n;
       +        n=0;
       +        while(*s)
       +                n=n*base+value(*s++, 0);
       +        return(n);
       +}
       +int
       +value(int c, int f)
       +{
       +        char *s;
       +        for(s=dig; s<dig+base; s++)
       +                if(*s==c)
       +                        return(s-dig);
       +        if(f)
       +                return(-1);
       +        fprint(2, "%s: bad input char %c\n", argv0, c);
       +        exits("bad");
       +        return 0;        /* to keep ken happy */
       +}
       +int
       +isnum(char *s)
       +{
       +        while(*s)
       +                if(value(*s++, 1)==-1)
       +                        return(0);
       +        return(1);
       +}
   DIR diff --git a/src/cmd/basename.c b/src/cmd/basename.c
       t@@ -0,0 +1,41 @@
       +#include <u.h>
       +#include <libc.h>
       +
       +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);
       +}
   DIR diff --git a/src/cmd/cal.c b/src/cmd/cal.c
       t@@ -0,0 +1,313 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <bio.h>
       +
       +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<m; i++)
       +                d += mon[i];
       +        d %= 7;
       +        s += 3*d;
       +        for(i=1; i<=mon[m]; i++) {
       +                if(i==3 && mon[m]==19) {
       +                        i += 11;
       +                        mon[m] += 11;
       +                }
       +                if(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;
       +}
   DIR diff --git a/src/cmd/calendar.c b/src/cmd/calendar.c
       t@@ -0,0 +1,195 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <bio.h>
       +#include <regexp.h>
       +#include <ctype.h>
       +
       +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; i<argc || (i==0 && argc==0); i++){
       +                if(i==0 && argc==0)
       +                        snprint(buf, sizeof(buf),
       +                                "/usr/%s/lib/calendar", getuser());
       +                else
       +                        strcpy(buf, argv[i]);
       +                fd = open(buf, OREAD);
       +                if(fd<0 || Binit(&in, fd, OREAD)<0){
       +                        fprint(2, "calendar: can't open %s: %r\n", buf);
       +                        exits("open");
       +                }
       +
       +                /* go through the file */
       +                while(line = Brdline(&in, '\n')){
       +                        line[Blinelen(&in) - 1] = 0;
       +                        upper2lower(buf, line, sizeof buf);
       +                        for(d=first; d; d=d->next)
       +                                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;
       +}
   DIR diff --git a/src/cmd/cat.c b/src/cmd/cat.c
       t@@ -0,0 +1,36 @@
       +#include <u.h>
       +#include <libc.h>
       +
       +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, "<stdin>");
       +        else for(i=1; i<argc; i++){
       +                f = open(argv[i], OREAD);
       +                if(f < 0)
       +                        sysfatal("can't open %s: %r", argv[i]);
       +                else{
       +                        cat(f, argv[i]);
       +                        close(f);
       +                }
       +        }
       +        exits(0);
       +}
       +
   DIR diff --git a/src/cmd/cleanname.c b/src/cmd/cleanname.c
       t@@ -0,0 +1,44 @@
       +#include <u.h>
       +#include <libc.h>
       +
       +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<argc; i++) {
       +                if(dir == nil || argv[i][0] == '/') {
       +                        cleanname(argv[i]);
       +                        print("%s\n", argv[i]);
       +                } else {
       +                        name = malloc(strlen(argv[i])+1+strlen(dir)+1);
       +                        if(name == nil) {
       +                                fprint(2, "cleanname: out of memory\n");
       +                                exits("out of memory");
       +                        }
       +                        sprint(name, "%s/%s", dir, argv[i]);
       +                        cleanname(name);
       +                        print("%s\n", name);
       +                        free(name);
       +                }
       +        }
       +        exits(0);
       +}
   DIR diff --git a/src/cmd/cmp.c b/src/cmd/cmp.c
       t@@ -0,0 +1,112 @@
       +#include <u.h>
       +#include <libc.h>
       +
       +#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");
       +}
   DIR diff --git a/src/cmd/comm.c b/src/cmd/comm.c
       t@@ -0,0 +1,178 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <bio.h>
       +
       +#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 */
       +}
   DIR diff --git a/src/cmd/date.c b/src/cmd/date.c
       t@@ -0,0 +1,30 @@
       +#include <u.h>
       +#include <libc.h>
       +
       +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);
       +}
   DIR diff --git a/src/cmd/dc.c b/src/cmd/dc.c
       t@@ -0,0 +1,2300 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <bio.h>
       +
       +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<sk2)
       +                                        sk = sk2;
       +                                if(sk<k)
       +                                        sk = k;
       +                                p = removc(p,savk-sk);
       +                                savk = sk;
       +                                sputc(p,savk);
       +                        }
       +                        pushp(p);
       +                        continue;
       +                case '/':
       +                casediv:
       +                        if(dscale() != 0)
       +                                continue;
       +                        binop('/');
       +                        if(irem != 0)
       +                                release(irem);
       +                        release(rem);
       +                        continue;
       +                case '%':
       +                        if(dscale() != 0)
       +                                continue;
       +                        binop('/');
       +                        p = pop();
       +                        release(p);
       +                        if(irem == 0) {
       +                                sputc(rem,skr+k);
       +                                pushp(rem);
       +                                continue;
       +                        }
       +                        p = add0(rem,skd-(skr+k));
       +                        q = add(p,irem);
       +                        release(p);
       +                        release(irem);
       +                        sputc(q,skd);
       +                        pushp(q);
       +                        continue;
       +                case 'v':
       +                        p = pop();
       +                        EMPTY;
       +                        savk = sunputc(p);
       +                        if(length(p) == 0) {
       +                                sputc(p,savk);
       +                                pushp(p);
       +                                continue;
       +                        }
       +                        if(sbackc(p)<0) {
       +                                error("sqrt of neg number\n");
       +                        }
       +                        if(k<savk)
       +                                n = savk;
       +                        else {
       +                                n = k*2-savk;
       +                                savk = k;
       +                        }
       +                        arg1 = add0(p,n);
       +                        arg2 = dcsqrt(arg1);
       +                        sputc(arg2,savk);
       +                        pushp(arg2);
       +                        continue;
       +
       +                case '^':
       +                        neg = 0;
       +                        arg1 = pop();
       +                        EMPTY;
       +                        if(sunputc(arg1) != 0)
       +                                error("exp not an integer\n");
       +                        arg2 = pop();
       +                        EMPTYR(arg1);
       +                        if(sfbeg(arg1) == 0 && sbackc(arg1)<0) {
       +                                neg++;
       +                                chsign(arg1);
       +                        }
       +                        if(length(arg1)>=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<d) {
       +                                        q = removc(p,d-n);
       +                                        sputc(q,n);
       +                                        pushp(q);
       +                                } else {
       +                                        sputc(p,d);
       +                                        pushp(p);
       +                                }
       +                /*        } else { this is disaster for exp <-127 */
       +                /*                sputc(p,d);                */
       +                /*                pushp(p);                */
       +                /*        }                                */
       +                        if(neg == 0)
       +                                continue;
       +                        p = pop();
       +                        q = salloc(2);
       +                        sputc(q,1);
       +                        sputc(q,0);
       +                        pushp(q);
       +                        pushp(p);
       +                        goto casediv;
       +                case 'z':
       +                        p = salloc(2);
       +                        n = stkptr - stkbeg;
       +                        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(h<hfree+(HEADSZ/BLK))
       +                (h++)->rd = (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(ptr<hdr->last)
       +                *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;
       +}
   DIR diff --git a/src/cmd/dd.c b/src/cmd/dd.c
       t@@ -0,0 +1,660 @@
       +#include <u.h>
       +#include <libc.h>
       +
       +#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; c++) {
       +                key = argv[c++];
       +                if(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<ibs; c++)
       +                                if(ibuf[c] != 0)
       +                                        ibc = c;
       +                        stats();
       +                }
       +                if(ibc == 0 && --files<=0) {
       +                        flsh();
       +                        term();
       +                }
       +                if(ibc != ibs) {
       +                        nipr++;
       +                        if(cflag&SYNC)
       +                                ibc = ibs;
       +                } else
       +                        nifr++;
       +                ip = ibuf;
       +                c = (ibc>>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,
       +};
   DIR diff --git a/src/cmd/deroff.c b/src/cmd/deroff.c
       t@@ -0,0 +1,969 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <bio.h>
       +
       +/*
       + * 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;
       +}
   DIR diff --git a/src/cmd/du.C b/src/cmd/du.C
       t@@ -0,0 +1,194 @@
       +#include <u.h>
       +#include <libc.h>
       +
       +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; i<argc; i++)
       +                        print(fmt, du(argv[i], dirstat(argv[i])), argv[i]);
       +        exits(0);
       +}
       +
       +vlong
       +du(char *name, Dir *dir)
       +{
       +        int fd, i, n;
       +        Dir *buf, *d;
       +        char file[256];
       +        vlong nk, t;
       +
       +        if(dir == nil)
       +                return warn(name);
       +
       +        fd = open(name, OREAD);
       +        if(fd < 0)
       +                return warn(name);
       +
       +        if((dir->qid.type&QTDIR) == 0)
       +                nk = k(dir->length);
       +        else{
       +                nk = 0;
       +                while((n=dirread(fd, &buf)) > 0) {
       +                        d = buf;
       +                        for(i=0; i<n; i++, d++) {
       +                                if((d->qid.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; i<c->n; 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;
       +}
   DIR diff --git a/src/cmd/echo.c b/src/cmd/echo.c
       t@@ -0,0 +1,38 @@
       +#include <u.h>
       +#include <libc.h>
       +
       +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);
       +}
   DIR diff --git a/src/cmd/ed.c b/src/cmd/ed.c
       t@@ -0,0 +1,1608 @@
       +/*
       + * Editor
       + */
       +#include <u.h>
       +#include <libc.h>
       +#include <bio.h>
       +#include <regexp.h>
       +
       +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<ad1) {
       +                dot = adt + (ad2-ad1);
       +                if((++adt)==ad1)
       +                        return;
       +                reverse(adt, ad1);
       +                reverse(ad1, ad2);
       +                reverse(adt, ad2);
       +        } else
       +        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);
       +}
   DIR diff --git a/src/cmd/factor.c b/src/cmd/factor.c
       t@@ -0,0 +1,96 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <bio.h>
       +
       +#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<argc; i++) {
       +                        n = atof(argv[i]);
       +                        factor(n);
       +                }
       +                exits(0);
       +        }
       +
       +        Binit(&bin, 0, OREAD);
       +        for(;;) {
       +                l = Brdline(&bin, '\n');
       +                if(l ==  0)
       +                        break;
       +                n = atof(l);
       +                if(n <= 0)
       +                        break;
       +                factor(n);
       +        }
       +        exits(0);
       +}
       +
       +void
       +factor(double n)
       +{
       +        double quot, d, s;
       +        int i;
       +
       +        print("%.0f\n", n);
       +        if(n == 0)
       +                return;
       +        s = sqrt(n) + 1;
       +        while(modf(n/2, &quot) == 0) {
       +                print("     2\n");
       +                n = quot;
       +                s = sqrt(n) + 1;
       +        }
       +        while(modf(n/3, &quot) == 0) {
       +                print("     3\n");
       +                n = quot;
       +                s = sqrt(n) + 1;
       +        }
       +        while(modf(n/5, &quot) == 0) {
       +                print("     5\n");
       +                n = quot;
       +                s = sqrt(n) + 1;
       +        }
       +        while(modf(n/7, &quot) == 0) {
       +                print("     7\n");
       +                n = quot;
       +                s = sqrt(n) + 1;
       +        }
       +        d = 1;
       +        for(i=1;;) {
       +                d += wheel[i];
       +                while(modf(n/d, &quot) == 0) {
       +                        print("     %.0f\n", d);
       +                        n = quot;
       +                        s = sqrt(n) + 1;
       +                }
       +                i++;
       +                if(i >= whsiz) {
       +                        i = 0;
       +                        if(d > s)
       +                                break;
       +                }
       +        }
       +        if(n > 1)
       +                print("     %.0f\n",n);
       +        print("\n");
       +}
   DIR diff --git a/src/cmd/freq.c b/src/cmd/freq.c
       t@@ -0,0 +1,111 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <bio.h>
       +
       +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<argc; i++) {
       +                f = open(argv[i], 0);
       +                if(f < 0) {
       +                        fprint(2, "cannot open %s\n", argv[i]);
       +                        continue;
       +                }
       +                freq(f, argv[i]);
       +                close(f);
       +        }
       +        exits(0);
       +}
       +
       +void
       +freq(int f, char *s)
       +{
       +        Biobuf bin;
       +        long c, i;
       +
       +        memset(count, 0, sizeof(count));
       +        Binit(&bin, f, OREAD);
       +        if(flag & Frune) {
       +                for(;;) {
       +                        c = Bgetrune(&bin);
       +                        if(c < 0)
       +                                break;
       +                        count[c]++;
       +                }
       +        } else {
       +                for(;;) {
       +                        c = Bgetc(&bin);
       +                        if(c < 0)
       +                                break;
       +                        count[c]++;
       +                }
       +        }
       +        Bterm(&bin);
       +        if(c != Beof)
       +                fprint(2, "freq: read error on %s\n", s);
       +
       +        for(i=0; i<nelem(count); i++) {
       +                if(count[i] == 0)
       +                        continue;
       +                if(flag & Fdec)
       +                        Bprint(&bout, "%3ld ", i);
       +                if(flag & Foct)
       +                        Bprint(&bout, "%.3lo ", i);
       +                if(flag & Fhex)
       +                        Bprint(&bout, "%.2lx ", i);
       +                if(flag & Fchar) {
       +                        if(i <= 0x20 ||
       +                           i >= 0x7f && i < 0xa0 ||
       +                           i > 0xff && !(flag & Frune))
       +                                Bprint(&bout, "- ");
       +                        else
       +                                Bprint(&bout, "%C ", (int)i);
       +                }
       +                Bprint(&bout, "%8ld\n", count[i]);
       +        }
       +        Bflush(&bout);
       +}
   DIR diff --git a/src/cmd/fsize.c b/src/cmd/fsize.c
       t@@ -0,0 +1,32 @@
       +#include <u.h>
       +#include <libc.h>
       +
       +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; i<argc; i++){
       +                if((d = dirstat(argv[i])) == nil)
       +                        fprint(2, "dirstat %s: %r", argv[i]);
       +                else{
       +                        print("%s: %lld\n", argv[i], d->length);
       +                        free(d);
       +                }
       +        }
       +}
   DIR 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 <u.h>
       +#include <libc.h>
       +#include <bio.h>
       +
       +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, "<stdout>");
       +        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<n; i++){
       +                while(Brdline(b, '\n')==nil){
       +                        if(Blinelen(b) <= 0)
       +                                sysfatal("early end of file on %s", name);
       +                        Bseek(b, Blinelen(b), 1);
       +                }
       +        }
       +}
       +
       +void
       +copylines(Biobuf *bin, char *nin, Biobuf *bout, char *nout, int n)
       +{
       +        char buf[4096], *p;
       +        int i, m;
       +
       +        for(i=0; i<n; i++){
       +                while((p=Brdline(bin, '\n'))==nil){
       +                        if(Blinelen(bin) <= 0)
       +                                sysfatal("early end of file on %s", nin);
       +                        m = Blinelen(bin);
       +                        if(m > 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, "<stderr>", 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);
       +}
   DIR diff --git a/src/cmd/join.c b/src/cmd/join.c
       t@@ -0,0 +1,369 @@
       +/*        join F1 F2 on stuff */
       +#include <u.h>
       +#include <libc.h>
       +#include <stdio.h>
       +#include <ctype.h>
       +#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++;
       +        }
       +}
   DIR diff --git a/src/cmd/ls.C b/src/cmd/ls.C
       t@@ -0,0 +1,305 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <bio.h>
       +
       +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; i<argc; i++)
       +                errs |= ls(argv[i], 1);
       +        output();
       +        exits(errs? "errors" : 0);
       +}
       +
       +int
       +ls(char *s, int multi)
       +{
       +        int fd;
       +        long i, n;
       +        char *p;
       +        Dir *db;
       +
       +        for(;;) {
       +                p = utfrrune(s, '/');
       +                if(p == 0 || p[1] != 0 || p == s)
       +                        break;
       +                *p = 0;
       +        }
       +        db = dirstat(s);
       +        if(db == nil){
       +    error:
       +                fprint(2, "ls: %s: %r\n", s);
       +                return 1;
       +        }
       +        if(db->qid.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; i<n; i++){
       +                        dirbuf[ndir+i].d = db+i;
       +                        dirbuf[ndir+i].prefix = multi? s : 0;
       +                }
       +                ndir += n;
       +                close(fd);
       +                output();
       +        }else{
       +                growto(ndir+1);
       +                dirbuf[ndir].d = db;
       +                dirbuf[ndir].prefix = 0;
       +                p = utfrrune(s, '/');
       +                if(p){
       +                        dirbuf[ndir].prefix = s;
       +                        *p = 0;
       +                        /* restore original name; don't use result of stat */
       +                        dirbuf[ndir].d->name = 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; i<ndir; i++)
       +                dowidths(dirbuf[i].d);
       +        for(i=0; i<ndir; i++) {
       +                if(!pflag && (s = dirbuf[i].prefix)) {
       +                        if(strcmp(s, "/") ==0)        /* / is a special case */
       +                                s = "";
       +                        sprint(buf, "%s/%s", s, dirbuf[i].d->name);
       +                        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); i<mwidth; i++)
       +                        Bprint(&bin, " ");
       +        }
       +        if(qflag)
       +                Bprint(&bin, "(%.16llux %*lud %.2ux) ",
       +                        db->qid.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<b? -1 : 1);
       +        if(rflag)
       +                i = -i;
       +        return i;
       +}
       +
       +char*
       +asciitime(long l)
       +{
       +        static char buf[32];
       +        char *t;
       +
       +        t = ctime(l);
       +        /* 6 months in the past or a day in the future */
       +        if(l<clk-180L*24*60*60 || clk+24L*60*60<l){
       +                memmove(buf, t+4, 7);                /* month and day */
       +                memmove(buf+7, t+23, 5);                /* year */
       +        }else
       +                memmove(buf, t+4, 12);                /* skip day of week */
       +        buf[12] = 0;
       +        return buf;
       +}
       +
   DIR diff --git a/src/cmd/md5sum.C b/src/cmd/md5sum.C
       t@@ -0,0 +1,61 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <bio.h>
       +#include <libsec.h>
       +
       +static int
       +digestfmt(Fmt *fmt)
       +{
       +        char buf[MD5dlen*2+1];
       +        uchar *p;
       +        int i;
       +
       +        p = va_arg(fmt->args, uchar*);
       +        for(i=0; i<MD5dlen; i++)
       +                sprint(buf+2*i, "%.2ux", p[i]);
       +        return fmtstrcpy(fmt, buf);
       +}
       +
       +static void
       +sum(int fd, char *name)
       +{
       +        int n;
       +        uchar buf[8192], digest[MD5dlen];
       +        DigestState *s;
       +
       +        s = md5(nil, 0, nil, nil);
       +        while((n = read(fd, buf, sizeof buf)) > 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);
       +}
   DIR diff --git a/src/cmd/mkdir.C b/src/cmd/mkdir.C
       t@@ -0,0 +1,26 @@
       +#include <u.h>
       +#include <libc.h>
       +
       +void
       +main(int argc, char *argv[])
       +{
       +        int i, f;
       +        char *e;
       +
       +        e = nil;
       +        for(i=1; i<argc; i++){
       +                if(access(argv[i], 0) == AEXIST){
       +                        fprint(2, "mkdir: %s already exists\n", argv[i]);
       +                        e = "error";
       +                        continue;
       +                }
       +                f = create(argv[i], OREAD, DMDIR | 0777L);
       +                if(f < 0){
       +                        fprint(2, "mkdir: can't create %s: %r\n", argv[i]);
       +                        e = "error";
       +                        continue;
       +                }
       +                close(f);
       +        }
       +        exits(e);
       +}
   DIR 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
       +
   DIR diff --git a/src/cmd/rm.c b/src/cmd/rm.c
       t@@ -0,0 +1,104 @@
       +#include <u.h>
       +#include <libc.h>
       +
       +#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; i<n; i++){
       +                snprint(name, nname, "%s/%s", f, dirbuf[i].name);
       +                if(remove(name) != -1)
       +                        dirbuf[i].qid.type = QTFILE;        /* so we won't recurse */
       +                else{
       +                        if(dirbuf[i].qid.type & QTDIR)
       +                                ndir++;
       +                        else
       +                                err(name);
       +                }
       +        }
       +        if(ndir)
       +                for(j=0; j<n; j++)
       +                        if(dirbuf[j].qid.type & QTDIR){
       +                                snprint(name, nname, "%s/%s", f, dirbuf[j].name);
       +                                rmdir(name);
       +                        }
       +        if(remove(f) == -1)
       +                err(f);
       +        free(name);
       +        free(dirbuf);
       +}
       +void
       +main(int argc, char *argv[])
       +{
       +        int i;
       +        int recurse;
       +        char *f;
       +        Dir *db;
       +
       +        ignerr = 0;
       +        recurse = 0;
       +        ARGBEGIN{
       +        case 'r':
       +                recurse = 1;
       +                break;
       +        case 'f':
       +                ignerr = 1;
       +                break;
       +        default:
       +                fprint(2, "usage: rm [-fr] file ...\n");
       +                exits("usage");
       +        }ARGEND
       +        for(i=0; i<argc; i++){
       +                f = argv[i];
       +                if(remove(f) != -1)
       +                        continue;
       +                db = nil;
       +                if(recurse && (db=dirstat(f))!=nil && (db->qid.type&QTDIR))
       +                        rmdir(f);
       +                else
       +                        err(f);
       +                free(db);
       +        }
       +        exits(errbuf);
       +}
   DIR diff --git a/src/cmd/seq.c b/src/cmd/seq.c
       t@@ -0,0 +1,92 @@
       +#include <u.h>
       +#include <libc.h>
       +
       +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);
       +}
   DIR diff --git a/src/cmd/sha1sum.c b/src/cmd/sha1sum.c
       t@@ -0,0 +1,61 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <bio.h>
       +#include <libsec.h>
       +
       +static int
       +digestfmt(Fmt *fmt)
       +{
       +        char buf[SHA1dlen*2+1];
       +        uchar *p;
       +        int i;
       +
       +        p = va_arg(fmt->args, uchar*);
       +        for(i=0; i<SHA1dlen; i++)
       +                sprint(buf+2*i, "%.2ux", p[i]);
       +        return fmtstrcpy(fmt, buf);
       +}
       +
       +static void
       +sum(int fd, char *name)
       +{
       +        int n;
       +        uchar buf[8192], digest[SHA1dlen];
       +        DigestState *s;
       +
       +        s = sha1(nil, 0, nil, nil);
       +        while((n = read(fd, buf, sizeof buf)) > 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);
       +}
   DIR diff --git a/src/cmd/sleep.c b/src/cmd/sleep.c
       t@@ -0,0 +1,13 @@
       +#include <u.h>
       +#include <libc.h>
       +
       +void
       +main(int argc, char *argv[])
       +{
       +        long secs;
       +
       +        if(argc>1)
       +                for(secs = atol(argv[1]); secs > 0; secs--)
       +                        sleep(1000);
       +        exits(0);
       +}
   DIR diff --git a/src/cmd/sort.c b/src/cmd/sort.c
       t@@ -0,0 +1,1767 @@
       +#include        <u.h>
       +#include        <libc.h>
       +#include        <bio.h>
       +
       +/*
       +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; i<argc; i++) {
       +                s = argv[i];
       +                if(s == 0)
       +                        continue;
       +                if(strcmp(s, "-") == 0) {
       +                        Binit(&bbuf, 0, OREAD);
       +                        dofile(&bbuf);
       +                        Bterm(&bbuf);
       +                        continue;
       +                }
       +                f = open(s, OREAD);
       +                if(f < 0) {
       +                        fprint(2, "sort: open %s: %r\n", s);
       +                        done("open");
       +                }
       +                Binit(&bbuf, f, OREAD);
       +                dofile(&bbuf);
       +                Bterm(&bbuf);
       +                close(f);
       +        }
       +        if(args.nfile == 0) {
       +                Binit(&bbuf, 0, OREAD);
       +                dofile(&bbuf);
       +                Bterm(&bbuf);
       +        }
       +        if(args.cflag)
       +                done(0);
       +        if(args.vflag)
       +                fprint(2, "=========\n");
       +
       +        f = 1;
       +        if(args.ofile) {
       +                f = create(args.ofile, OWRITE, 0666);
       +                if(f < 0) {
       +                        fprint(2, "sort: create %s: %r\n", args.ofile);
       +                        done("create");
       +                }
       +        }
       +
       +        Binit(&bbuf, f, OWRITE);
       +        if(args.ntemp) {
       +                tempout();
       +                mergeout(&bbuf);
       +        } else {
       +                printout(&bbuf);
       +        }
       +        Bterm(&bbuf);
       +        done(0);
       +}
       +
       +void
       +dofile(Biobuf *b)
       +{
       +        Line *l, *ol;
       +        int n;
       +
       +        if(args.cflag) {
       +                ol = newline(b);
       +                if(ol == 0)
       +                        return;
       +                for(;;) {
       +                        l = newline(b);
       +                        if(l == 0)
       +                                break;
       +                        n = kcmp(ol->key, 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<args.ntemp; i++)
       +                remove(tempfile(i));
       +        exits(xs);
       +}
       +
       +void
       +nomem(void)
       +{
       +        fprint(2, "sort: out of memory\n");
       +        done("mem");
       +}
       +
       +char*
       +tempfile(int n)
       +{
       +        static char file[100];
       +        static uint pid;
       +        char *dir;
       +
       +        dir = "/tmp";
       +        if(args.tname)
       +                dir = args.tname;
       +        if(strlen(dir) >= 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<args.ntemp; i+=n) {
       +                n = args.ntemp - i;
       +                if(n > 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; i<n; i++,m++) {
       +                tf = tempfile(t+i);
       +                f = open(tf, OREAD);
       +                if(f < 0) {
       +                        fprint(2, "sort: reopen %s: %r\n", tf);
       +                        done("open");
       +                }
       +                m->fd = 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; i<n; i++,m++) {
       +                Bterm(&m->b);
       +                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<argc; i++) {
       +                s = argv[i];
       +                c = *s++;
       +                if(c == '-') {
       +                        c = *s;
       +                        if(c == 0)                /* forced end of arg marker */
       +                                break;
       +                        argv[i] = 0;                /* clobber args processed */
       +                        if(c == '.' || (c >= '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; i<k->klen; 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; i<nelem(f->mapto); 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; i<nelem(f->mapto); 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; t<ea; t++) {
       +                k = **t;
       +                n = k->klen;
       +                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;
       +        }
       +}
   DIR diff --git a/src/cmd/split.c b/src/cmd/split.c
       t@@ -0,0 +1,189 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <bio.h>
       +#include <ctype.h>
       +#include <regexp.h>
       +
       +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");
       +}
   DIR diff --git a/src/cmd/strings.c b/src/cmd/strings.c
       t@@ -0,0 +1,88 @@
       +#include        <u.h>
       +#include         <libc.h>
       +#include        <bio.h>
       +
       +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;
       +}
   DIR diff --git a/src/cmd/sum.c b/src/cmd/sum.c
       t@@ -0,0 +1,215 @@
       +#include <u.h>
       +#include <libc.h>
       +
       +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:"<stdin>", 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<send; s++)
       +                sum += 0xffff & *s;
       +        return sum;
       +}
       +
       +#define        RBSIZE                1024                /* research */
       +
       +ulong
       +sumr(ulong sum, void *buf, uvlong uvn)
       +{
       +        uchar *s, *send;
       +        int n;
       +
       +        if(buf == 0){
       +                sum &= 0xFFFF;
       +                print("%.5lud%6lld", sum, (uvn+(RBSIZE-1))/RBSIZE);
       +                return 0;
       +        }
       +        n = uvn;
       +        for(s=buf, send=s+n; s<send; s++)
       +                if(sum & 1)
       +                        sum = 0xffff & ((sum>>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,
       +};
   DIR diff --git a/src/cmd/tail.c b/src/cmd/tail.c
       t@@ -0,0 +1,362 @@
       +#include        <u.h>
       +#include        <libc.h>
       +#include        <ctype.h>
       +#include        <bio.h>
       +
       +/*
       + * 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<Bsize? count: Bsize;
       +                        if(!(n = tread(buf, n)))
       +                                return;
       +                }
       +        } else /*units == LINES*/ {
       +                n = i = 0;
       +                while(count > 0) {
       +                        if(!(n = tread(buf, Bsize)))
       +                                return;
       +                        for(i=0; i<n && count>0; 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<bufsiz; len+=n)
       +                        n = tread(buf+len, bufsiz-len);
       +                if(count >= 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");
       +}
   DIR diff --git a/src/cmd/tar.C b/src/cmd/tar.C
       t@@ -0,0 +1,640 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <bio.h>
       +
       +#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);
       +}
   DIR diff --git a/src/cmd/tee.c b/src/cmd/tee.c
       t@@ -0,0 +1,75 @@
       +/*
       + * tee-- pipe fitting
       + */
       +
       +#include <u.h>
       +#include <libc.h>
       +
       +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<n; i++)
       +                        write(openf[i], in, r);
       +        }
       +}
       +
       +int
       +intignore(void *a, char *msg)
       +{
       +        USED(a);
       +        if(strcmp(msg, "interrupt") == 0)
       +                return 1;
       +        return 0;
       +}
   DIR 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 <u.h>
       +#include <libc.h>
       +#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 && isint(av[ap], pans)){
       +                ap++;
       +                return 1;
       +        }
       +        return 0;
       +}
       +
       +int
       +e(void) {
       +        int p1;
       +
       +        p1 = e1();
       +        if (EQ(nxtarg(1), "-o")) return(p1 || e());
       +        ap--;
       +        return(p1);
       +}
       +
       +int
       +e1(void) {
       +        int p1;
       +
       +        p1 = e2();
       +        if (EQ(nxtarg(1), "-a")) return (p1 && e1());
       +        ap--;
       +        return(p1);
       +}
       +
       +int
       +e2(void) {
       +        if (EQ(nxtarg(0), "!"))
       +                return(!e2());
       +        ap--;
       +        return(e3());
       +}
       +
       +int
       +e3(void) {
       +        int p1;
       +        char *a;
       +        char *p2;
       +        int int1, int2;
       +
       +        a = nxtarg(0);
       +        if(EQ(a, "(")) {
       +                p1 = e();
       +                if(!EQ(nxtarg(0), ")")) synbad(") expected","");
       +                return(p1);
       +        }
       +
       +        if(EQ(a, "-A"))
       +                return(hasmode(nxtarg(0), DMAPPEND));
       +
       +        if(EQ(a, "-L"))
       +                return(hasmode(nxtarg(0), DMEXCL));
       +
       +        if(EQ(a, "-f"))
       +                return(isreg(nxtarg(0)));
       +
       +        if(EQ(a, "-d"))
       +                return(isdir(nxtarg(0)));
       +
       +        if(EQ(a, "-r"))
       +                return(tio(nxtarg(0), 4));
       +
       +        if(EQ(a, "-w"))
       +                return(tio(nxtarg(0), 2));
       +
       +        if(EQ(a, "-x"))
       +                return(tio(nxtarg(0), 1));
       +
       +        if(EQ(a, "-e"))
       +                return(tio(nxtarg(0), 0));
       +
       +        if(EQ(a, "-c"))
       +                return(0);
       +
       +        if(EQ(a, "-b"))
       +                return(0);
       +
       +        if(EQ(a, "-u"))
       +                return(0);
       +
       +        if(EQ(a, "-g"))
       +                return(0);
       +
       +        if(EQ(a, "-s"))
       +                return(fsizep(nxtarg(0)));
       +
       +        if(EQ(a, "-t"))
       +                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, "-ge"))
       +                        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);
       +}
   DIR diff --git a/src/cmd/time.c b/src/cmd/time.c
       t@@ -0,0 +1,101 @@
       +#include <u.h>
       +#include <libc.h>
       +
       +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; i<argc; i++){
       +                add("%s", argv[i], 0);
       +                if(i>4){
       +                        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);
       +}
   DIR diff --git a/src/cmd/touch.c b/src/cmd/touch.c
       t@@ -0,0 +1,62 @@
       +#include <u.h>
       +#include <libc.h>
       +
       +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;
       +}
   DIR diff --git a/src/cmd/tr.c b/src/cmd/tr.c
       t@@ -0,0 +1,356 @@
       +#include         <u.h>
       +#include         <libc.h>
       +
       +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);
       +}
   DIR diff --git a/src/cmd/unicode.c b/src/cmd/unicode.c
       t@@ -0,0 +1,122 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <bio.h>
       +
       +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 || max<min || *q!=0)
       +                        goto err;
       +                i = 0;
       +                do{
       +                        Bprint(&bout, "%.4x %C", min, min);
       +                        i++;
       +                        if(min==max || (i&7)==0)
       +                                Bprint(&bout, "\n");
       +                        else
       +                                Bprint(&bout, "\t");
       +                        min++;
       +                }while(min<=max);
       +                argv++;
       +        }
       +        return 0;
       +}
       +
       +char*
       +nums(char *argv[])
       +{
       +        char *q;
       +        Rune r;
       +        int w;
       +
       +        while(*argv){
       +                q = *argv;
       +                while(*q){
       +                        w = chartorune(&r, q);
       +                        if(r==0x80 && (q[0]&0xFF)!=0x80){
       +                                fprint(2, "unicode: invalid utf string %s\n", *argv);
       +                                return "bad utf";
       +                        }
       +                        Bprint(&bout, "%.4x\n", r);
       +                        q += w;
       +                }
       +                argv++;
       +        }
       +        return 0;
       +}
       +
       +char*
       +chars(char *argv[])
       +{
       +        char *q;
       +        int m;
       +
       +        while(*argv){
       +                q = *argv;
       +                if(strchr(hex, q[0]) == 0){
       +    err:
       +                        fprint(2, "unicode: bad unicode value %s\n", *argv);
       +                        return "bad char";
       +                }
       +                m = strtoul(q, &q, 16);
       +                if(m<0 || m>0xFFFF || *q!=0)
       +                        goto err;
       +                Bprint(&bout, "%C", m);
       +                if(!text)
       +                        Bprint(&bout, "\n");
       +                argv++;
       +        }
       +        return 0;
       +}
   DIR 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 <u.h>
       +#include <libc.h>
       +#include <bio.h>
       +#include <ctype.h>
       +
       +#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;
       +}
   DIR diff --git a/src/cmd/unutf.c b/src/cmd/unutf.c
       t@@ -0,0 +1,16 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <bio.h>
       +
       +Biobuf bin;
       +
       +void
       +main(void)
       +{
       +        int c;
       +
       +        Binit(&bin, 0, OREAD);
       +        while((c = Bgetrune(&bin)) >= 0)
       +                print("0x%ux\n", c);
       +        exits(0);
       +}
   DIR 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 <u.h>
       +#include <libc.h>
       +#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;i<argc;i++){
       +                        f=open(argv[i], OREAD);
       +                        if(f<0){
       +                                perror(argv[i]);
       +                                status="can't open";
       +                        }
       +                        else{
       +                                count(f, argv[i]);
       +                                tnline+=nline;
       +                                tnword+=nword;
       +                                tnrune+=nrune;
       +                                tnbadr+=nbadr;
       +                                tnchar+=nchar;
       +                                close(f);
       +                        }
       +                }
       +                if(argc>1)
       +                        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);
       +}
   DIR diff --git a/src/cmd/xd.c b/src/cmd/xd.c
       t@@ -0,0 +1,355 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <bio.h>
       +
       +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<argc; i++)
       +                err |= xd(argv[i], 1);
       +        exits(err? "error" : 0);
       +}
       +
       +void
       +initarg(void)
       +{
       +        Arg *ap;
       +
       +        ap = &arg[narg++];
       +        if(narg >= 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<ndata; i++)
       +                xprint(f, data[i]);
       +}
       +
       +void
       +fmt1(char *f)
       +{
       +        int i;
       +        for(i=0; i<ndata; i+=sizeof(unsigned short))
       +                xprint(f, (data[i]<<8)|data[i+1]);
       +}
       +
       +void
       +fmt2(char *f)
       +{
       +        int i;
       +        for(i=0; i<ndata; i+=sizeof(unsigned long))
       +                xprint(f, (data[i]<<24)|(data[i+1]<<16)|(data[i+2]<<8)|data[i+3]);
       +}
       +
       +void
       +fmt3(char *f)
       +{
       +        int i;
       +        unsigned long long v;
       +        for(i=0; i<ndata; i+=sizeof(unsigned long long)){
       +                v = (data[i]<<24)|(data[i+1]<<16)|(data[i+2]<<8)|data[i+3];
       +                v <<= 32;
       +                v |= (data[i+4]<<24)|(data[i+1+4]<<16)|(data[i+2+4]<<8)|data[i+3+4];
       +                if(Bprint(&bout, f, v)<0){
       +                        fprint(2, "xd: i/o error\n");
       +                        exits("i/o error");
       +                }
       +        }
       +}
       +
       +void
       +fmtc(char *f)
       +{
       +        int i;
       +
       +        USED(f);
       +        for(i=0; i<ndata; i++)
       +                switch(data[i]){
       +                case '\t':
       +                        xprint(cfmt[1][2], (long)"\\t");
       +                        break;
       +                case '\r':
       +                        xprint(cfmt[1][2], (long)"\\r");
       +                        break;
       +                case '\n':
       +                        xprint(cfmt[1][2], (long)"\\n");
       +                        break;
       +                case '\b':
       +                        xprint(cfmt[1][2], (long)"\\b");
       +                        break;
       +                default:
       +                        if(data[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");
       +        }
       +}
   DIR diff --git a/src/cmd/yacc.c b/src/cmd/yacc.c
       t@@ -0,0 +1,2939 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <bio.h>
       +#include <ctype.h>
       +
       +#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<nprod; i++)
       +#define SLOOP(i)        for(i=0; i<nstate; i++)
       +#define WSBUMP(x)        x++
       +#define WSLOOP(s,j)        for(j=s; j<cwp; j++)
       +#define ITMLOOP(i,p,q)        for(q=pstate[i+1], p=pstate[i]; p<q; p++)
       +#define SETLOOP(i)        for(i=0; i<tbitset; i++)
       +
       +        /* command to clobber tempfiles after use */
       +
       +#define        ZAPFILE(x)        if(x) remove(x)
       +
       +        /* I/O descriptors */
       +
       +Biobuf*        faction;        /* file for saving actions */
       +Biobuf*        fdefine;        /* file for #defines */
       +Biobuf*        fdebug;                /* y.debug for strings for debugging */
       +Biobuf*        ftable;                /* y.tab.c file */
       +Biobuf*        ftemp;                /* tempfile to pass 2 */
       +Biobuf*        finput;                /* input file */
       +Biobuf*        foutput;        /* y.output file */
       +
       +        /* communication variables between various I/O routines */
       +
       +char*        infile;                        /* input file name */
       +int        numbval;                /* value of an input number */
       +char        tokname[NAMESIZE+4];        /* input token name, slop for runes and 0 */
       +
       +        /* structure declarations */
       +
       +typedef
       +struct
       +{
       +        int        lset[TBITSET];
       +} Lkset;
       +
       +typedef
       +struct
       +{
       +        int*        pitem;
       +        Lkset*        look;
       +} Item;
       +
       +typedef
       +struct
       +{
       +        char*        name;
       +        int        value;
       +} Symb;
       +
       +typedef
       +struct
       +{
       +        int*        pitem;
       +        int        flag;
       +        Lkset        ws;
       +} Wset;
       +
       +        /* storage of names */
       +
       +char        cnames[CNAMSZ];                /* place where token and nonterminal names are stored */
       +int        cnamsz = CNAMSZ;        /* size of cnames */
       +char*        cnamp = cnames;                /* place where next name is to be put in */
       +int        ndefout = 4;                /* number of defined symbols output */
       +char*        tempname;
       +char*        actname;
       +char        ttempname[] = TEMPNAME;
       +char        tactname[] = ACTNAME;
       +char*        parser = PARSER;
       +char*        yydebug;
       +
       +        /* storage of types */
       +int        ntypes;                        /* number of types defined */
       +char*        typeset[NTYPES];        /* pointers to type tags */
       +
       +        /* token information */
       +
       +int        ntokens = 0 ;                /* number of tokens */
       +Symb        tokset[NTERMS];
       +int        toklev[NTERMS];                /* vector with the precedence of the terminals */
       +
       +        /* nonterminal information */
       +
       +int        nnonter = -1;                /* the number of nonterminals */
       +Symb        nontrst[NNONTERM];
       +int        start;                        /* start symbol */
       +
       +        /* assigned token type values */
       +int        extval = 0;
       +
       +char*        ytabc = OFILE;        /* name of y.tab.c */
       +
       +        /* grammar rule information */
       +
       +int        mem0[MEMSIZE] ;                /* production storage */
       +int*        mem = mem0;
       +int        nprod = 1;                /* number of productions */
       +int*        prdptr[NPROD];                /* pointers to descriptions of productions */
       +int        levprd[NPROD];                /* precedence levels for the productions */
       +int        rlines[NPROD];                /* line number for this rule */
       +
       +        /* state information */
       +
       +int        nstate = 0;                /* number of states */
       +Item*        pstate[NSTATES+2];        /* pointers to the descriptions of the states */
       +int        tystate[NSTATES];        /* contains type information about the states */
       +int        defact[NSTATES];        /* the default actions of states */
       +int        tstates[NTERMS];        /* states generated by terminal gotos */
       +int        ntstates[NNONTERM];         /* states generated by nonterminal gotos */
       +int        mstates[NSTATES];        /* chain of overflows of term/nonterm generation lists  */
       +int        lastred;                 /* the number of the last reduction of a state */
       +
       +        /* lookahead set information */
       +
       +Lkset        lkst[LSETSIZE];
       +int        nolook;                        /* flag to turn off lookahead computations */
       +int        tbitset;                /* size of lookahead sets */
       +int        nlset = 0;                /* next lookahead set index */
       +int        nolook = 0;                /* flag to suppress lookahead computations */
       +Lkset        clset;                  /* temporary storage for lookahead computations */
       +
       +        /* working set information */
       +
       +Wset        wsets[WSETSIZE];
       +Wset*        cwp;
       +
       +        /* storage for action table */
       +
       +int        amem[ACTSIZE];                /* action table storage */
       +int*        memp = amem;                /* next free action table position */
       +int        indgo[NSTATES];                /* index to the stored goto table */
       +
       +        /* temporary vector, indexable by states, terms, or ntokens */
       +
       +int        temp1[TEMPSIZE];        /* temporary storage, indexed by terms + ntokens or states */
       +int        lineno = 1;                /* current input line number */
       +int        fatfl = 1;                  /* if on, error is fatal */
       +int        nerrors = 0;                /* number of errors */
       +
       +        /* statistics collection variables */
       +
       +int        zzgoent;
       +int        zzgobest;
       +int        zzacent;
       +int        zzexcp;
       +int        zzclose;
       +int        zzrrconf;
       +int        zzsrconf;
       +
       +int*        ggreed = lkst[0].lset;
       +int*        pgo = wsets[0].ws.lset;
       +int*        yypgo = &nontrst[0].value;
       +
       +int        maxspr = 0;                  /* maximum spread of any entry */
       +int        maxoff = 0;                  /* maximum offset into a array */
       +int*        pmem = mem0;
       +int*        maxa;
       +int        nxdb = 0;
       +int        adb = 0;
       +
       +
       +        /* storage for information about the nonterminals */
       +
       +int**        pres[NNONTERM+2];          /* vector of pointers to productions yielding each nonterminal */
       +Lkset*        pfirst[NNONTERM+2];        /* vector of pointers to first sets for each nonterminal */
       +int        pempty[NNONTERM+1];        /* vector of nonterminals nontrivially deriving e */
       +
       +        /* random stuff picked out from between functions */
       +
       +int        indebug = 0;
       +Wset*        zzcwp = wsets;
       +int        zzgoent = 0;
       +int        zzgobest = 0;
       +int        zzacent = 0;
       +int        zzexcp = 0;
       +int        zzclose = 0;
       +int        zzsrconf = 0;
       +int*        zzmemsz = mem0;
       +int        zzrrconf = 0;
       +int        pidebug = 0;                /* debugging flag for putitem */
       +int        gsdebug = 0;
       +int        cldebug = 0;                /* debugging flag for closure */
       +int        pkdebug = 0;
       +int        g2debug = 0;
       +
       +struct
       +{
       +        char*        name;
       +        long        value;
       +} resrv[] =
       +{
       +        "binary",        BINARY,
       +        "left",                LEFT,
       +        "nonassoc",        BINARY,
       +        "prec",                PREC,
       +        "right",        RIGHT,
       +        "start",        START,
       +        "term",                TERM,
       +        "token",        TERM,
       +        "type",                TYPEDEF,
       +        "union",        UNION,
       +        0,
       +};
       +
       +        /* define functions */
       +
       +void        main(int, char**);
       +void        others(void);
       +char*        chcopy(char*, char*);
       +char*        writem(int*);
       +char*        symnam(int);
       +void        summary(void);
       +void        error(char*, ...);
       +void        aryfil(int*, int, int);
       +int        setunion(int*, int*);
       +void        prlook(Lkset*);
       +void        cpres(void);
       +void        cpfir(void);
       +int        state(int);
       +void        putitem(int*, Lkset*);
       +void        cempty(void);
       +void        stagen(void);
       +void        closure(int);
       +Lkset*        flset(Lkset*);
       +void        cleantmp(void);
       +void        intr(void);
       +void        setup(int, char**);
       +void        finact(void);
       +int        defin(int, char*);
       +void        defout(int);
       +char*        cstash(char*);
       +long        gettok(void);
       +int        fdtype(int);
       +int        chfind(int, char*);
       +void        cpyunion(void);
       +void        cpycode(void);
       +int        skipcom(void);
       +void        cpyact(int);
       +void        openup(char*, int, int, int, char*);
       +void        output(void);
       +int        apack(int*, int);
       +void        go2out(void);
       +void        go2gen(int);
       +void        precftn(int, int, int);
       +void        wract(int);
       +void        wrstate(int);
       +void        warray(char*, int*, int);
       +void        hideprod(void);
       +void        callopt(void);
       +void        gin(int);
       +void        stin(int);
       +int        nxti(void);
       +void        osummary(void);
       +void        aoutput(void);
       +void        arout(char*, int*, int);
       +int        gtnm(void);
       +
       +void
       +main(int argc, char *argv[])
       +{
       +
       +        setup(argc, argv);        /* initialize and read productions */
       +        tbitset = NWORDS(ntokens);
       +        cpres();                /* make table of which productions yield a given nonterminal */
       +        cempty();                /* make a table of which nonterminals can match the empty string */
       +        cpfir();                /* make a table of firsts of nonterminals */
       +        stagen();                /* generate the states */
       +        output();                /* write the states and the tables */
       +        go2out();
       +        hideprod();
       +        summary();
       +        callopt();
       +        others();
       +        exits(0);
       +}
       +
       +/*
       + * put out other arrays, copy the parsers
       + */
       +void
       +others(void)
       +{
       +        int c, i, j;
       +
       +        finput = Bopen(parser, OREAD);
       +        if(finput == 0)
       +                error("cannot find parser %s", parser);
       +        warray("yyr1", levprd, nprod);
       +        aryfil(temp1, nprod, 0);
       +        PLOOP(1, i)
       +                temp1[i] = prdptr[i+1]-prdptr[i]-2;
       +        warray("yyr2", temp1, nprod);
       +
       +        aryfil(temp1, nstate, -1000);
       +        TLOOP(i)
       +                for(j=tstates[i]; j!=0; j=mstates[j])
       +                        temp1[j] = i;
       +        NTLOOP(i)
       +                for(j=ntstates[i]; j!=0; j=mstates[j])
       +                        temp1[j] = -i;
       +        warray("yychk", temp1, nstate);
       +        warray("yydef", defact, nstate);
       +
       +        /* put out token translation tables */
       +        /* table 1 has 0-256 */
       +        aryfil(temp1, 256, 0);
       +        c = 0;
       +        TLOOP(i) {
       +                j = tokset[i].value;
       +                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("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; i<n; i++)
       +                v[i] = c;
       +}
       +
       +/*
       + * set a to the union of a and b
       + * return 1 if b is not a subset of a, 0 otherwise
       + */
       +int
       +setunion(int *a, int *b)
       +{
       +        int i, x, sub;
       +
       +        sub = 0;
       +        SETLOOP(i) {
       +                x = *a;
       +                *a |= *b;
       +                if(*a != x)
       +                        sub = 1;
       +                a++;
       +                b++;
       +        }
       +        return sub;
       +}
       +
       +void
       +prlook(Lkset* p)
       +{
       +        int j, *pp;
       +
       +        pp = p->lset;
       +        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<t; ++s)
       +                        for(p = *s; (ch = *p) > 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 $<ident> 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<nstate; j++) {
       +                        if(indgo[j] == n) {
       +
       +                                /* we have some disagreement */
       +                                if(flag)
       +                                        goto nextn;
       +                                if(temp1[j+1]+temp1[i] == temp1[j]+temp1[i+1]) {
       +
       +                                        /* states are equal */
       +                                        indgo[i] = n;
       +                                        if(adb > 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;
       +}