123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672 |
- /*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
- #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 20
- #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;
- int32_t f_nextc;
- };
- struct Colp
- {
- Rune* c_ptr;
- Rune* c_ptr0;
- int32_t 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;
- int32_t 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(uint32_t);
- 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(int32_t);
- extern void putpage(void);
- extern void putspace(void);
- /*
- * return date file was last modified
- */
- char*
- getdate(void)
- {
- static char *now = 0;
- static Dir *sbuf;
- uint32_t 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) {
- uint32_t buflen = ((uint32_t)(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;
- int32_t 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(int32_t 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(uint32_t 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");
- }
- }
|