#include #include #include #include /* * PR command (print files in pages and columns, with headings) * 2+head+2+page[56]+5 */ #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 >= (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; int f_nextc; }; struct Colp { char* c_ptr; char* c_ptr0; long c_lno; }; struct Err { Err* e_nextp; char* e_mess; }; Fils* Files; int Multi = 0; int Nfiles = 0; int error = 0; int Balance = 0; int Outpos; int Lcolpos; int Pcolpos; int Line; char nulls[] = ""; long Lnumb = 0; int Dblspace = 1; int Fpage = 1; int Formfeed = 0; int Len = LENGTH; int Linew = 0; int Offset = 0; int Ncols = 1; int Sepc = 0; int Colw; int Plength; int Margin = MARGIN; int Numw; int Nsepc = NSEPC; int Report = 1; int Etabn = 0; int Etabc = '\t'; int Itabn = 0; int Itabc = '\t'; char* Head = 0; char* Buffer = 0; char* Bufend; Colp Colpts; int Page; int C = '\0'; int Nspace; int Inpos; Err* err = 0; Err* Lasterr = (Err*)&err; Biobuf bout; extern char* ffiler(char*); extern void main(int, char**); extern int findopt(int, char**); extern int intopt(char**, int*); extern int pr(char*); extern void putpage(void); extern void nexbuf(void); extern void balance(int); extern int get(int); extern void put(int); extern void putspace(void); extern int atoix(char**); extern Biobuf* mustopen(char*, Fils*); extern void* getspace(unsigned long int); extern void die(char*); extern void errprint(void); /* * return date file was last modified */ char* getdate(void) { static char *now = 0; static Dir sbuf, nbuf; if(Nfiles > 1 || Files->f_name == nulls) { if(now == 0) { nbuf.mtime = time(0); now = ctime(nbuf.mtime); } return now; } dirstat(Files->f_name, &sbuf); return ctime(sbuf.mtime); } char* ffiler(char *s) { static char buf[100]; sprint(buf, "can't open %s\n", s); return buf; } 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)) Bclose(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 'r': Report = 0; 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': case 'x': /* retained for historical reasons */ ++Lnumb; if((Numw = intopt(argv, &Nsepc)) <= 0) Numw = NUMW; case 'b': Balance = 1; continue; case 'q': /* retained for historical reasons */ case 'j': /* ignore GCOS jprint option */ 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); Bufend = &Buffer[buflen]; Colpts = getspace((ulong)((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); 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); } } 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) { char *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 = Bgetc(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) { char *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; int 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 = Bgetc(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(int 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(); Bputc(&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) { 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) { s = ffiler(f->f_name); s = strcpy((char *)getspace((ulong)(strlen(s) + 1)), s); } if(f->f_f != 0) { if((f->f_nextc = Bgetc(f->f_f)) >= 0 || Multi == 'm') return f->f_f; sprint(s = (char*)getspace((ulong)(strlen(f->f_name) + 1 + EMPTY)), "%s -- empty file\n", f->f_name); Bclose(f->f_f); } error = 1; if(Report) { cerror(s); Bputc(&bout, '\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"); } }