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);
151 for(argc = findopt(argc, argv); argc > 0; --argc, ++argv)
153 if(Nfiles >= NFILES - 1)
154 die("too many files");
155 if(mustopen(*argv, &Files[Nfiles++]) == 0)
156 nfdone++; /* suppress printing */
162 if(!nfdone) /* no files named, use stdin */
163 pr(nulls); /* on GCOS, use current file, if any */
164 errprint(); /* print accumulated error reports */
165 exits(error? "error": 0);
169 findopt(int argc, char *argv[])
175 switch(c = **++argv) {
177 if((c = *++*argv) == '\0')
185 switch(c = TOLOWER(c)) {
187 if((Fpage = atoix(argv)) < 1)
214 Offset = atoix(argv);
217 if((Sepc = (*argv)[1]) != '\0')
230 if((Numw = intopt(argv, &Nsepc)) <= 0)
241 } while((c = *++*argv) != '\0');
253 Plength = Len - Margin/2;
262 if(Etabn == 0) /* respect explicit tab specification */
266 Linew = Ncols != 1 && Sepc == 0? LINEW: 512;
268 Linew -= Multi == 'm'? Numw: Numw*Ncols;
269 if((Colw = (Linew - Ncols + 1)/Ncols) < 1)
270 die("width too small");
271 if(Ncols != 1 && Multi == 0) {
272 ulong buflen = ((ulong)(Plength/Dblspace + 1))*(Linew+1)*sizeof(char);
273 Buffer = getspace(buflen*sizeof(*Buffer));
274 Bufend = &Buffer[buflen];
275 Colpts = getspace((Ncols+1)*sizeof(*Colpts));
281 intopt(char *argv[], int *optp)
285 if((c = (*argv)[1]) != '\0' && !isdigit(c)) {
290 return c != 0? c: -1;
296 char *date = 0, *head = 0;
298 if(Multi != 'm' && mustopen(name, &Files[0]) == 0)
304 for(Page = 0;; putpage()) {
320 head = Head != 0 ? Head :
321 Nfiles < 2? Files->f_name: nulls;
322 Bprint(&bout, "\n\n");
328 if(Padodd && (Page&1) == 1) {
345 for(Line = Margin/2;; get(0)) {
346 for(Nspace = Offset, colno = 0, Outpos = 0; C != '\f';) {
347 if(Lnumb && C != -1 && (colno == 0 || Multi == 'a')) {
350 Bprint(&bout, "%*ld", Numw, Buffer?
351 Colpts[colno].c_lno++: Lnumb);
357 for(Lcolpos=0, Pcolpos=0; C!='\n' && C!='\f' && C!=-1; get(colno))
359 if(C==-1 || ++colno==Ncols || C=='\n' && get(colno)==-1)
364 if((Nspace += Colw - Lcolpos + 1) < 1)
376 if(C == -1 && colno == 0) {
384 if(Dblspace == 2 && Line < Plength)
404 p->c_ptr0 = p->c_ptr = s;
405 if(p == &Colpts[Ncols])
407 (p++)->c_lno = Lnumb + bline;
408 for(j = (Len - Margin)/Dblspace; --j >= 0; bline++)
410 if((c = Bgetrune(Files->f_f)) == -1) {
411 for(*s = -1; p <= &Colpts[Ncols]; p++)
412 p->c_ptr0 = p->c_ptr = s;
419 if(Inpos <= Colw || c == '\n') {
422 die("page-buffer overflow");
439 * line balancing for last page
446 int colno = 0, j, c, l;
449 l = (bline + Ncols - 1)/Ncols;
452 for(j = 0; j < l; ++j)
455 (++p)->c_lno = Lnumb + (bline += l);
456 p->c_ptr0 = p->c_ptr = s;
459 } while(colno < Ncols - 1);
465 static int peekc = 0;
476 if(p->c_ptr >= (p+1)->c_ptr0)
479 if((c = *p->c_ptr) != -1)
482 if((c = (q = &Files[Multi == 'a'? 0: colno])->f_nextc) == -1) {
483 for(q = &Files[Nfiles]; --q >= Files && q->f_nextc == -1;)
488 q->f_nextc = Bgetrune(q->f_f);
489 if(Etabn != 0 && c == Etabc) {
532 if(Lcolpos > Pcolpos) {
548 move = (ISPRINT(c) != 0);
552 if(Lcolpos > 0 || move > 0)
554 if(Lcolpos <= Colw) {
567 for(; Nspace > 0; Outpos += nc, Nspace -= nc)
581 while(isdigit(c = *++*p))
588 * Defer message about failure to open file to prevent messing up
589 * alignment of page with tear perforations or form markers.
590 * Treat empty file as special case and report as diagnostic.
593 mustopen(char *s, Fils *f)
598 f->f_name = STDINNAME();
599 f->f_f = malloc(sizeof(Biobuf));
602 Binit(f->f_f, 0, OREAD);
604 if((f->f_f = Bopen(f->f_name = s, OREAD)) == 0) {
605 tmp = ffiler(f->f_name);
606 s = strcpy((char*)getspace(strlen(tmp) + 1), tmp);
610 if((f->f_nextc = Bgetrune(f->f_f)) >= 0 || Multi == 'm')
612 sprint(s = (char*)getspace(strlen(f->f_name) + 1 + EMPTY),
613 "%s -- empty file\n", f->f_name);
627 if((t = malloc(n)) == 0)
653 * print accumulated error reports
659 for(; err != 0; err = err->e_nextp) {