diff options
author | rsc <devnull@localhost> | 2004-04-21 22:37:11 +0000 |
---|---|---|
committer | rsc <devnull@localhost> | 2004-04-21 22:37:11 +0000 |
commit | 53db2d0f6bfc971f44183f360f3db5a72edd948a (patch) | |
tree | 8b219cc7e666cd924e5eda263698b8263a782727 /src/cmd/pr.c | |
parent | f80f53a9424c26f28c8819558541d19cd690a440 (diff) | |
download | plan9port-53db2d0f6bfc971f44183f360f3db5a72edd948a.tar.gz plan9port-53db2d0f6bfc971f44183f360f3db5a72edd948a.tar.bz2 plan9port-53db2d0f6bfc971f44183f360f3db5a72edd948a.zip |
why not?
Diffstat (limited to 'src/cmd/pr.c')
-rw-r--r-- | src/cmd/pr.c | 663 |
1 files changed, 663 insertions, 0 deletions
diff --git a/src/cmd/pr.c b/src/cmd/pr.c new file mode 100644 index 00000000..934cfa5e --- /dev/null +++ b/src/cmd/pr.c @@ -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"); + } +} |