7 * PR command (print files in pages and columns, with headings)
11 #define ISPRINT(c) ((c) >= ' ')
19 #define HEAD "%12.12s %4.4s %s Page %d\n\n\n", date+4, date+24, head, Page
20 #define TOLOWER(c) (isupper(c) ? tolower(c) : c) /* ouch! */
21 #define cerror(S) fprint(2, "pr: %s", S)
22 #define STDINNAME() nulls
23 #define TTY "/dev/cons", 0
24 #define PROMPT() fprint(2, "\a") /* BEL */
25 #define TABS(N,C) if((N = intopt(argv, &C)) < 0) N = DEFTAB
26 #define ETABS (Inpos % Etabn)
27 #define ITABS (Itabn > 0 && Nspace > 1 && Nspace >= (nc = Itabn - Outpos % Itabn))
29 #define EMPTY 14 /* length of " -- empty file" */
31 typedef struct Fils Fils;
32 typedef struct Colp* Colp;
33 typedef struct Err Err;
72 Err* Lasterr = (Err*)&err;
94 extern int atoix(char**);
95 extern void balance(int);
96 extern void die(char*);
97 extern void errprint(void);
98 extern char* ffiler(char*);
99 extern int findopt(int, char**);
101 extern void* getspace(ulong);
102 extern int intopt(char**, int*);
103 extern void main(int, char**);
104 extern Biobuf* mustopen(char*, Fils*);
105 extern void nexbuf(void);
106 extern int pr(char*);
107 extern void put(long);
108 extern void putpage(void);
109 extern void putspace(void);
112 * return date file was last modified
117 static char *now = 0;
121 if(Nfiles > 1 || Files->f_name == nulls) {
129 sbuf = dirstat(Files->f_name);
140 return smprint("can't open %s\n", s);
144 main(int argc, char *argv[])
149 Binit(&bout, 1, OWRITE);
152 for(argc = findopt(argc, argv); argc > 0; --argc, ++argv)
154 if(Nfiles >= NFILES - 1)
155 die("too many files");
156 if(mustopen(*argv, &Files[Nfiles++]) == 0)
157 nfdone++; /* suppress printing */
163 if(!nfdone) /* no files named, use stdin */
164 pr(nulls); /* on GCOS, use current file, if any */
165 errprint(); /* print accumulated error reports */
166 exits(error? "error": 0);
170 findopt(int argc, char *argv[])
176 switch(c = **++argv) {
178 if((c = *++*argv) == '\0')
186 switch(c = TOLOWER(c)) {
188 if((Fpage = atoix(argv)) < 1)
215 Offset = atoix(argv);
218 if((Sepc = (*argv)[1]) != '\0')
231 if((Numw = intopt(argv, &Nsepc)) <= 0)
242 } while((c = *++*argv) != '\0');
254 Plength = Len - Margin/2;
263 if(Etabn == 0) /* respect explicit tab specification */
267 Linew = Ncols != 1 && Sepc == 0? LINEW: 512;
269 Linew -= Multi == 'm'? Numw: Numw*Ncols;
270 if((Colw = (Linew - Ncols + 1)/Ncols) < 1)
271 die("width too small");
272 if(Ncols != 1 && Multi == 0) {
273 ulong buflen = ((ulong)(Plength/Dblspace + 1))*(Linew+1)*sizeof(char);
274 Buffer = getspace(buflen*sizeof(*Buffer));
275 Bufend = &Buffer[buflen];
276 Colpts = getspace((Ncols+1)*sizeof(*Colpts));
282 intopt(char *argv[], int *optp)
286 if((c = (*argv)[1]) != '\0' && !isdigit(c)) {
291 return c != 0? c: -1;
297 char *date = 0, *head = 0;
299 if(Multi != 'm' && mustopen(name, &Files[0]) == 0)
305 for(Page = 0;; putpage()) {
321 head = Head != 0 ? Head :
322 Nfiles < 2? Files->f_name: nulls;
323 Bprint(&bout, "\n\n");
329 if(Padodd && (Page&1) == 1) {
346 for(Line = Margin/2;; get(0)) {
347 for(Nspace = Offset, colno = 0, Outpos = 0; C != '\f';) {
348 if(Lnumb && C != -1 && (colno == 0 || Multi == 'a')) {
351 Bprint(&bout, "%*ld", Numw, Buffer?
352 Colpts[colno].c_lno++: Lnumb);
358 for(Lcolpos=0, Pcolpos=0; C!='\n' && C!='\f' && C!=-1; get(colno))
360 if(C==-1 || ++colno==Ncols || C=='\n' && get(colno)==-1)
365 if((Nspace += Colw - Lcolpos + 1) < 1)
377 if(C == -1 && colno == 0) {
385 if(Dblspace == 2 && Line < Plength)
405 p->c_ptr0 = p->c_ptr = s;
406 if(p == &Colpts[Ncols])
408 (p++)->c_lno = Lnumb + bline;
409 for(j = (Len - Margin)/Dblspace; --j >= 0; bline++)
411 if((c = Bgetrune(Files->f_f)) == -1) {
412 for(*s = -1; p <= &Colpts[Ncols]; p++)
413 p->c_ptr0 = p->c_ptr = s;
420 if(Inpos <= Colw || c == '\n') {
423 die("page-buffer overflow");
440 * line balancing for last page
447 int colno = 0, j, c, l;
450 l = (bline + Ncols - 1)/Ncols;
453 for(j = 0; j < l; ++j)
456 (++p)->c_lno = Lnumb + (bline += l);
457 p->c_ptr0 = p->c_ptr = s;
460 } while(colno < Ncols - 1);
466 static int peekc = 0;
477 if(p->c_ptr >= (p+1)->c_ptr0)
480 if((c = *p->c_ptr) != -1)
483 if((c = (q = &Files[Multi == 'a'? 0: colno])->f_nextc) == -1) {
484 for(q = &Files[Nfiles]; --q >= Files && q->f_nextc == -1;)
489 q->f_nextc = Bgetrune(q->f_f);
490 if(Etabn != 0 && c == Etabc) {
533 if(Lcolpos > Pcolpos) {
549 move = (ISPRINT(c) != 0);
553 if(Lcolpos > 0 || move > 0)
555 if(Lcolpos <= Colw) {
568 for(; Nspace > 0; Outpos += nc, Nspace -= nc)
582 while(isdigit(c = *++*p))
589 * Defer message about failure to open file to prevent messing up
590 * alignment of page with tear perforations or form markers.
591 * Treat empty file as special case and report as diagnostic.
594 mustopen(char *s, Fils *f)
599 f->f_name = STDINNAME();
600 f->f_f = malloc(sizeof(Biobuf));
603 Binit(f->f_f, 0, OREAD);
604 Blethal(f->f_f, nil);
606 if((f->f_f = Bopen(f->f_name = s, OREAD)) == 0) {
607 tmp = ffiler(f->f_name);
608 s = strcpy((char*)getspace(strlen(tmp) + 1), tmp);
612 Blethal(f->f_f, nil);
613 if((f->f_nextc = Bgetrune(f->f_f)) >= 0 || Multi == 'm')
615 sprint(s = (char*)getspace(strlen(f->f_name) + 1 + EMPTY),
616 "%s -- empty file\n", f->f_name);
630 if((t = malloc(n)) == 0)
656 * print accumulated error reports
662 for(; err != 0; err = err->e_nextp) {