twhy not? - plan9port - [fork] Plan 9 from user space
 (HTM) git clone git://src.adamsgaard.dk/plan9port
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 53db2d0f6bfc971f44183f360f3db5a72edd948a
 (DIR) parent f80f53a9424c26f28c8819558541d19cd690a440
 (HTM) Author: rsc <devnull@localhost>
       Date:   Wed, 21 Apr 2004 22:37:11 +0000
       
       why not?
       
       Diffstat:
         A src/cmd/p.c                         |      90 +++++++++++++++++++++++++++++++
         A src/cmd/pr.c                        |     663 +++++++++++++++++++++++++++++++
       
       2 files changed, 753 insertions(+), 0 deletions(-)
       ---
 (DIR) diff --git a/src/cmd/p.c b/src/cmd/p.c
       t@@ -0,0 +1,90 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <bio.h>
       +
       +#define        DEF        22        /* lines in chunk: 3*DEF == 66, #lines per nroff page */
       +
       +Biobuf *cons;
       +Biobuf bout;
       +
       +int pglen = DEF;
       +
       +void printfile(int);
       +
       +void
       +main(int argc, char *argv[])
       +{
       +        int n;
       +        int f;
       +
       +        if((cons = Bopen("/dev/cons", OREAD)) == 0) {
       +                fprint(2, "p: can't open /dev/cons\n");
       +                exits("missing /dev/cons");
       +        }
       +        Binit(&bout, 1, OWRITE);
       +        n = 0;
       +        while(argc > 1) {
       +                --argc; argv++;
       +                if(*argv[0] == '-'){
       +                        pglen = atoi(&argv[0][1]);
       +                        if(pglen <= 0)
       +                                pglen = DEF;
       +                } else {
       +                        n++;
       +                        f = open(argv[0], OREAD);
       +                        if(f < 0){
       +                                fprint(2, "p: can't open %s\n", argv[0]);
       +                                continue;
       +                        }
       +                        printfile(f);
       +                        close(f);
       +                }
       +        }
       +        if(n == 0)
       +                printfile(0);
       +        exits(0);
       +}
       +
       +void
       +printfile(int f)
       +{
       +        int i, j, n;
       +        char *s, *cmd;
       +        Biobuf *b;
       +
       +        b = malloc(sizeof(Biobuf));
       +        Binit(b, f, OREAD);
       +        for(;;){
       +                for(i=1; i <= pglen; i++) {
       +                        s = Brdline(b, '\n');
       +                        if(s == 0){
       +                                n = Blinelen(b);
       +                                if(n > 0)        /* line too long for Brdline */
       +                                        for(j=0; j<n; j++)
       +                                                Bputc(&bout, Bgetc(b));
       +                                else{                /* true EOF */
       +                                        free(b);
       +                                        return;
       +                                }
       +                        }else{
       +                                Bwrite(&bout, s, Blinelen(b)-1);
       +                                if(i < pglen)
       +                                        Bwrite(&bout, "\n", 1);
       +                        }
       +                }
       +                Bflush(&bout);
       +            getcmd:
       +                cmd = Brdline(cons, '\n');
       +                if(cmd == 0 || *cmd == 'q')
       +                        exits(0);
       +                cmd[Blinelen(cons)-1] = 0;
       +                if(*cmd == '!'){
       +                        if(fork() == 0){
       +                                dup(Bfildes(cons), 0);
       +                                execl("/bin/rc", "rc", "-c", cmd+1, 0);
       +                        }
       +                        waitpid();
       +                        goto getcmd;
       +                }
       +        }
       +}
 (DIR) diff --git a/src/cmd/pr.c b/src/cmd/pr.c
       t@@ -0,0 +1,663 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <bio.h>
       +#include <ctype.h>
       +
       +/*
       + *        PR command (print files in pages and columns, with headings)
       + *        2+head+2+page[56]+5
       + */
       +
       +#define        ISPRINT(c)        ((c) >= ' ')
       +#define ESC                '\033'
       +#define LENGTH                66
       +#define LINEW                72
       +#define NUMW                5
       +#define MARGIN                10
       +#define DEFTAB                8
       +#define NFILES                10
       +#define HEAD                "%12.12s %4.4s  %s Page %d\n\n\n", date+4, date+24, head, Page
       +#define TOLOWER(c)        (isupper(c) ? tolower(c) : c)        /* ouch! */
       +#define cerror(S)        fprint(2, "pr: %s", S)
       +#define STDINNAME()        nulls
       +#define TTY                "/dev/cons", 0
       +#define PROMPT()        fprint(2, "\a") /* BEL */
       +#define TABS(N,C)        if((N = intopt(argv, &C)) < 0) N = DEFTAB
       +#define ETABS                (Inpos % Etabn)
       +#define ITABS                (Itabn > 0 && Nspace > 1 && Nspace >= (nc = Itabn - Outpos % Itabn))
       +#define NSEPC                '\t'
       +#define EMPTY                14        /* length of " -- empty file" */
       +
       +typedef        struct        Fils        Fils;
       +typedef        struct        Colp*        Colp;
       +typedef        struct        Err        Err;
       +
       +struct        Fils
       +{
       +        Biobuf*        f_f;
       +        char*        f_name;
       +        long        f_nextc;
       +};
       +struct        Colp
       +{
       +        Rune*        c_ptr;
       +        Rune*        c_ptr0;
       +        long        c_lno;
       +};
       +struct        Err
       +{
       +        Err*        e_nextp;
       +        char*        e_mess;
       +};
       +
       +int        Balance = 0;
       +Biobuf        bout;
       +Rune*        Bufend;
       +Rune*        Buffer = 0;
       +int        C = '\0';
       +Colp        Colpts;
       +int        Colw;
       +int        Dblspace = 1;
       +Err*        err = 0;
       +int        error = 0;
       +int        Etabc = '\t';
       +int        Etabn = 0;
       +Fils*        Files;
       +int        Formfeed = 0;
       +int        Fpage = 1;
       +char*        Head = 0;
       +int        Inpos;
       +int        Itabc = '\t';
       +int        Itabn = 0;
       +Err*        Lasterr = (Err*)&err;
       +int        Lcolpos;
       +int        Len = LENGTH;
       +int        Line;
       +int        Linew = 0;
       +long        Lnumb = 0;
       +int        Margin = MARGIN;
       +int        Multi = 0;
       +int        Ncols = 1;
       +int        Nfiles = 0;
       +int        Nsepc = NSEPC;
       +int        Nspace;
       +char        nulls[] = "";
       +int        Numw;
       +int        Offset = 0;
       +int        Outpos;
       +int        Padodd;
       +int        Page;
       +int        Pcolpos;
       +int        Plength;
       +int        Sepc = 0;
       +
       +extern        int        atoix(char**);
       +extern        void        balance(int);
       +extern        void        die(char*);
       +extern        void        errprint(void);
       +extern        char*        ffiler(char*);
       +extern        int        findopt(int, char**);
       +extern        int        get(int);
       +extern        void*        getspace(ulong);
       +extern        int        intopt(char**, int*);
       +extern        void        main(int, char**);
       +extern        Biobuf*        mustopen(char*, Fils*);
       +extern        void        nexbuf(void);
       +extern        int        pr(char*);
       +extern        void        put(long);
       +extern        void        putpage(void);
       +extern        void        putspace(void);
       +
       +/*
       + * return date file was last modified
       + */
       +char*
       +getdate(void)
       +{
       +        static char *now = 0;
       +        static Dir *sbuf;
       +        ulong mtime;
       +
       +        if(Nfiles > 1 || Files->f_name == nulls) {
       +                if(now == 0) {
       +                        mtime = time(0);
       +                        now = ctime(mtime);
       +                }
       +                return now;
       +        }
       +        mtime = 0;
       +        sbuf = dirstat(Files->f_name);
       +        if(sbuf){
       +                mtime = sbuf->mtime;
       +                free(sbuf);
       +        }
       +        return ctime(mtime);
       +}
       +
       +char*
       +ffiler(char *s)
       +{
       +        return smprint("can't open %s\n", s);
       +}
       +
       +void
       +main(int argc, char *argv[])
       +{
       +        Fils fstr[NFILES];
       +        int nfdone = 0;
       +
       +        Binit(&bout, 1, OWRITE);
       +        Files = fstr;
       +        for(argc = findopt(argc, argv); argc > 0; --argc, ++argv)
       +                if(Multi == 'm') {
       +                        if(Nfiles >= NFILES - 1)
       +                                die("too many files");
       +                        if(mustopen(*argv, &Files[Nfiles++]) == 0)
       +                                nfdone++; /* suppress printing */
       +                } else {
       +                        if(pr(*argv))
       +                                Bterm(Files->f_f);
       +                        nfdone++;
       +                }
       +        if(!nfdone)                        /* no files named, use stdin */
       +                pr(nulls);                /* on GCOS, use current file, if any */
       +        errprint();                        /* print accumulated error reports */
       +        exits(error? "error": 0);
       +}
       +
       +int
       +findopt(int argc, char *argv[])
       +{
       +        char **eargv = argv;
       +        int eargc = 0, c;
       +
       +        while(--argc > 0) {
       +                switch(c = **++argv) {
       +                case '-':
       +                        if((c = *++*argv) == '\0')
       +                                break;
       +                case '+':
       +                        do {
       +                                if(isdigit(c)) {
       +                                        --*argv;
       +                                        Ncols = atoix(argv);
       +                                } else
       +                                switch(c = TOLOWER(c)) {
       +                                case '+':
       +                                        if((Fpage = atoix(argv)) < 1)
       +                                                Fpage = 1;
       +                                        continue;
       +                                case 'd':
       +                                        Dblspace = 2;
       +                                        continue;
       +                                case 'e':
       +                                        TABS(Etabn, Etabc);
       +                                        continue;
       +                                case 'f':
       +                                        Formfeed++;
       +                                        continue;
       +                                case 'h':
       +                                        if(--argc > 0)
       +                                                Head = argv[1];
       +                                        continue;
       +                                case 'i':
       +                                        TABS(Itabn, Itabc);
       +                                        continue;
       +                                case 'l':
       +                                        Len = atoix(argv);
       +                                        continue;
       +                                case 'a':
       +                                case 'm':
       +                                        Multi = c;
       +                                        continue;
       +                                case 'o':
       +                                        Offset = atoix(argv);
       +                                        continue;
       +                                case 's':
       +                                        if((Sepc = (*argv)[1]) != '\0')
       +                                                ++*argv;
       +                                        else
       +                                                Sepc = '\t';
       +                                        continue;
       +                                case 't':
       +                                        Margin = 0;
       +                                        continue;
       +                                case 'w':
       +                                        Linew = atoix(argv);
       +                                        continue;
       +                                case 'n':
       +                                        Lnumb++;
       +                                        if((Numw = intopt(argv, &Nsepc)) <= 0)
       +                                                Numw = NUMW;
       +                                case 'b':
       +                                        Balance = 1;
       +                                        continue;
       +                                case 'p':
       +                                        Padodd = 1;
       +                                        continue;
       +                                default:
       +                                        die("bad option");
       +                                }
       +                        } while((c = *++*argv) != '\0');
       +                        if(Head == argv[1])
       +                                argv++;
       +                        continue;
       +                }
       +                *eargv++ = *argv;
       +                eargc++;
       +        }
       +        if(Len == 0)
       +                Len = LENGTH;
       +        if(Len <= Margin)
       +                Margin = 0;
       +        Plength = Len - Margin/2;
       +        if(Multi == 'm')
       +                Ncols = eargc;
       +        switch(Ncols) {
       +        case 0:
       +                Ncols = 1;
       +        case 1:
       +                break;
       +        default:
       +                if(Etabn == 0)                /* respect explicit tab specification */
       +                        Etabn = DEFTAB;
       +        }
       +        if(Linew == 0)
       +                Linew = Ncols != 1 && Sepc == 0? LINEW: 512;
       +        if(Lnumb)
       +                Linew -= Multi == 'm'? Numw: Numw*Ncols;
       +        if((Colw = (Linew - Ncols + 1)/Ncols) < 1)
       +                die("width too small");
       +        if(Ncols != 1 && Multi == 0) {
       +                ulong buflen = ((ulong)(Plength/Dblspace + 1))*(Linew+1)*sizeof(char);
       +                Buffer = getspace(buflen*sizeof(*Buffer));
       +                Bufend = &Buffer[buflen];
       +                Colpts = getspace((Ncols+1)*sizeof(*Colpts));
       +        }
       +        return eargc;
       +}
       +
       +int
       +intopt(char *argv[], int *optp)
       +{
       +        int c;
       +
       +        if((c = (*argv)[1]) != '\0' && !isdigit(c)) {
       +                *optp = c;
       +                (*argv)++;
       +        }
       +        c = atoix(argv);
       +        return c != 0? c: -1;
       +}
       +
       +int
       +pr(char *name)
       +{
       +        char *date = 0, *head = 0;
       +
       +        if(Multi != 'm' && mustopen(name, &Files[0]) == 0)
       +                return 0;
       +        if(Buffer)
       +                Bungetc(Files->f_f);
       +        if(Lnumb)
       +                Lnumb = 1;
       +        for(Page = 0;; putpage()) {
       +                if(C == -1)
       +                        break;
       +                if(Buffer)
       +                        nexbuf();
       +                Inpos = 0;
       +                if(get(0) == -1)
       +                        break;
       +                Bflush(&bout);
       +                Page++;
       +                if(Page >= Fpage) {
       +                        if(Margin == 0)
       +                                continue;
       +                        if(date == 0)
       +                                date = getdate();
       +                        if(head == 0)
       +                                head = Head != 0 ? Head :
       +                                        Nfiles < 2? Files->f_name: nulls;
       +                        Bprint(&bout, "\n\n");
       +                        Nspace = Offset;
       +                        putspace();
       +                        Bprint(&bout, HEAD);
       +                }
       +        }
       +        if(Padodd && (Page&1) == 1) {
       +                Line = 0;
       +                if(Formfeed)
       +                        put('\f');
       +                else
       +                        while(Line < Len)
       +                                put('\n');
       +        }
       +        C = '\0';
       +        return 1;
       +}
       +
       +void
       +putpage(void)
       +{
       +        int colno;
       +
       +        for(Line = Margin/2;; get(0)) {
       +                for(Nspace = Offset, colno = 0, Outpos = 0; C != '\f';) {
       +                        if(Lnumb && C != -1 && (colno == 0 || Multi == 'a')) {
       +                                if(Page >= Fpage) {
       +                                        putspace();
       +                                        Bprint(&bout, "%*ld", Numw, Buffer?
       +                                                Colpts[colno].c_lno++: Lnumb);
       +                                        Outpos += Numw;
       +                                        put(Nsepc);
       +                                }
       +                                Lnumb++;
       +                        }
       +                        for(Lcolpos=0, Pcolpos=0; C!='\n' && C!='\f' && C!=-1; get(colno))
       +                                        put(C);
       +                        if(C==-1 || ++colno==Ncols || C=='\n' && get(colno)==-1)
       +                                break;
       +                        if(Sepc)
       +                                put(Sepc);
       +                        else
       +                                if((Nspace += Colw - Lcolpos + 1) < 1)
       +                                        Nspace = 1;
       +                }
       +        /*
       +                if(C == -1) {
       +                        if(Margin != 0)
       +                                break;
       +                        if(colno != 0)
       +                                put('\n');
       +                        return;
       +                }
       +        */
       +                if(C == -1 && colno == 0) {
       +                        if(Margin != 0)
       +                                break;
       +                        return;
       +                }
       +                if(C == '\f')
       +                        break;
       +                put('\n');
       +                if(Dblspace == 2 && Line < Plength)
       +                        put('\n');
       +                if(Line >= Plength)
       +                        break;
       +        }
       +        if(Formfeed)
       +                put('\f');
       +        else
       +                while(Line < Len)
       +                        put('\n');
       +}
       +
       +void
       +nexbuf(void)
       +{
       +        Rune *s = Buffer;
       +        Colp p = Colpts;
       +        int j, c, bline = 0;
       +
       +        for(;;) {
       +                p->c_ptr0 = p->c_ptr = s;
       +                if(p == &Colpts[Ncols])
       +                        return;
       +                (p++)->c_lno = Lnumb + bline;
       +                for(j = (Len - Margin)/Dblspace; --j >= 0; bline++)
       +                        for(Inpos = 0;;) {
       +                                if((c = Bgetrune(Files->f_f)) == -1) {
       +                                        for(*s = -1; p <= &Colpts[Ncols]; p++)
       +                                                p->c_ptr0 = p->c_ptr = s;
       +                                        if(Balance)
       +                                                balance(bline);
       +                                        return;
       +                                }
       +                                if(ISPRINT(c))
       +                                        Inpos++;
       +                                if(Inpos <= Colw || c == '\n') {
       +                                        *s = c;
       +                                        if(++s >= Bufend)
       +                                                die("page-buffer overflow");
       +                                }
       +                                if(c == '\n')
       +                                        break;
       +                                switch(c) {
       +                                case '\b':
       +                                        if(Inpos == 0)
       +                                                s--;
       +                                case ESC:
       +                                        if(Inpos > 0)
       +                                                Inpos--;
       +                                }
       +                        }
       +        }
       +}
       +
       +/*
       + * line balancing for last page
       + */
       +void
       +balance(int bline)
       +{
       +        Rune *s = Buffer;
       +        Colp p = Colpts;
       +        int colno = 0, j, c, l;
       +
       +        c = bline % Ncols;
       +        l = (bline + Ncols - 1)/Ncols;
       +        bline = 0;
       +        do {
       +                for(j = 0; j < l; ++j)
       +                        while(*s++ != '\n')
       +                                ;
       +                (++p)->c_lno = Lnumb + (bline += l);
       +                p->c_ptr0 = p->c_ptr = s;
       +                if(++colno == c)
       +                        l--;
       +        } while(colno < Ncols - 1);
       +}
       +
       +int
       +get(int colno)
       +{
       +        static int peekc = 0;
       +        Colp p;
       +        Fils *q;
       +        long c;
       +
       +        if(peekc) {
       +                peekc = 0;
       +                c = Etabc;
       +        } else
       +        if(Buffer) {
       +                p = &Colpts[colno];
       +                if(p->c_ptr >= (p+1)->c_ptr0)
       +                        c = -1;
       +                else
       +                        if((c = *p->c_ptr) != -1)
       +                                p->c_ptr++;
       +        } else
       +        if((c = (q = &Files[Multi == 'a'? 0: colno])->f_nextc) == -1) {
       +                for(q = &Files[Nfiles]; --q >= Files && q->f_nextc == -1;)
       +                        ;
       +                if(q >= Files)
       +                        c = '\n';
       +        } else
       +                q->f_nextc = Bgetrune(q->f_f);
       +        if(Etabn != 0 && c == Etabc) {
       +                Inpos++;
       +                peekc = ETABS;
       +                c = ' ';
       +        } else
       +        if(ISPRINT(c))
       +                Inpos++;
       +        else
       +                switch(c) {
       +                case '\b':
       +                case ESC:
       +                        if(Inpos > 0)
       +                                Inpos--;
       +                        break;
       +                case '\f':
       +                        if(Ncols == 1)
       +                                break;
       +                        c = '\n';
       +                case '\n':
       +                case '\r':
       +                        Inpos = 0;
       +                }
       +        return C = c;
       +}
       +
       +void
       +put(long c)
       +{
       +        int move;
       +
       +        switch(c) {
       +        case ' ':
       +                Nspace++;
       +                Lcolpos++;
       +                return;
       +        case '\b':
       +                if(Lcolpos == 0)
       +                        return;
       +                if(Nspace > 0) {
       +                        Nspace--;
       +                        Lcolpos--;
       +                        return;
       +                }
       +                if(Lcolpos > Pcolpos) {
       +                        Lcolpos--;
       +                        return;
       +                }
       +        case ESC:
       +                move = -1;
       +                break;
       +        case '\n':
       +                Line++;
       +        case '\r':
       +        case '\f':
       +                Pcolpos = 0;
       +                Lcolpos = 0;
       +                Nspace = 0;
       +                Outpos = 0;
       +        default:
       +                move = (ISPRINT(c) != 0);
       +        }
       +        if(Page < Fpage)
       +                return;
       +        if(Lcolpos > 0 || move > 0)
       +                Lcolpos += move;
       +        if(Lcolpos <= Colw) {
       +                putspace();
       +                Bputrune(&bout, c);
       +                Pcolpos = Lcolpos;
       +                Outpos += move;
       +        }
       +}
       +
       +void
       +putspace(void)
       +{
       +        int nc;
       +
       +        for(; Nspace > 0; Outpos += nc, Nspace -= nc)
       +                if(ITABS)
       +                        Bputc(&bout, Itabc);
       +                else {
       +                        nc = 1;
       +                        Bputc(&bout, ' ');
       +                }
       +}
       +
       +int
       +atoix(char **p)
       +{
       +        int n = 0, c;
       +
       +        while(isdigit(c = *++*p))
       +                n = 10*n + c - '0';
       +        (*p)--;
       +        return n;
       +}
       +
       +/*
       + * Defer message about failure to open file to prevent messing up
       + * alignment of page with tear perforations or form markers.
       + * Treat empty file as special case and report as diagnostic.
       + */
       +Biobuf*
       +mustopen(char *s, Fils *f)
       +{
       +        char *tmp;
       +
       +        if(*s == '\0') {
       +                f->f_name = STDINNAME();
       +                f->f_f = malloc(sizeof(Biobuf));
       +                if(f->f_f == 0)
       +                        cerror("no memory");
       +                Binit(f->f_f, 0, OREAD);
       +        } else
       +        if((f->f_f = Bopen(f->f_name = s, OREAD)) == 0) {
       +                tmp = ffiler(f->f_name);
       +                s = strcpy((char*)getspace(strlen(tmp) + 1), tmp);
       +                free(tmp);
       +        }
       +        if(f->f_f != 0) {
       +                if((f->f_nextc = Bgetrune(f->f_f)) >= 0 || Multi == 'm')
       +                        return f->f_f;
       +                sprint(s = (char*)getspace(strlen(f->f_name) + 1 + EMPTY),
       +                        "%s -- empty file\n", f->f_name);
       +                Bterm(f->f_f);
       +        }
       +        error = 1;
       +        cerror(s);
       +        fprint(2, "\n");
       +        return 0;
       +}
       +
       +void*
       +getspace(ulong n)
       +{
       +        void *t;
       +
       +        if((t = malloc(n)) == 0)
       +                die("out of space");
       +        return t;
       +}
       +
       +void
       +die(char *s)
       +{
       +        error++;
       +        errprint();
       +        cerror(s);
       +        Bputc(&bout, '\n');
       +        exits("error");
       +}
       +
       +/*
       +void
       +onintr(void)
       +{
       +        error++;
       +        errprint();
       +        exits("error");
       +}
       +/**/
       +
       +/*
       + * print accumulated error reports
       + */
       +void
       +errprint(void)
       +{
       +        Bflush(&bout);
       +        for(; err != 0; err = err->e_nextp) {
       +                cerror(err->e_mess);
       +                fprint(2, "\n");
       +        }
       +}