|
|
1.1 root 1: #include <stdio.h>
2: /*
3: * PR command (print files in pages and columns, with headings)
4: * 2+head+2+page[56]+5
5: */
6:
7: #define ESC '\033'
8: #define LENGTH 66
9: #define LINEW 72
10: #define NUMW 5
11: #define MARGIN 10
12: #define DEFTAB 8
13:
14: FILE *fopen(), *mustopen();
15: char nulls[] = "";
16: typedef struct { FILE *f_f; char *f_name; int f_nextc; } FILS;
17: FILS *Files;
18: int Multi = 0, Nfiles = 0, Error = 0, Balance = 0, onintr();
19:
20: #include <signal.h>
21: #include <ctype.h>
22: #include <sys/types.h>
23: #include <sys/stat.h>
24: typedef char CHAR;
25: typedef int ANY;
26: typedef unsigned UNS;
27: #define NFILES 10
28: int Mode;
29: char *ttyname(), *Ttyout, obuf[BUFSIZ];
30: #define istty(F) ttyname(fileno(F))
31: /* ARGSUSED */
32: fixtty(argc, argv) char **argv;
33: {
34: struct stat sbuf;
35:
36: setbuf(stdout, obuf);
37: if (signal(SIGINT, SIG_IGN) != SIG_IGN) signal(SIGINT, onintr);
38: if (Ttyout= istty(stdout)) {
39: stat(Ttyout, &sbuf);
40: Mode = sbuf.st_mode&0777;
41: chmod(Ttyout, 0600);
42: }
43: return (argc);
44: }
45:
46: #define done() if (Ttyout) chmod(Ttyout, Mode)
47: #define INTREXIT _exit
48: char *GETDATE() /* return date file was last modified */
49: {
50: char *ctime();
51: static char *now = NULL;
52: static struct stat sbuf, nbuf;
53:
54: if (Nfiles > 1 || Files->f_name == nulls) {
55: if (now == NULL)
56: { time(&nbuf.st_mtime); now = ctime(&nbuf.st_mtime); }
57: return (now);
58: } else {
59: stat(Files->f_name, &sbuf);
60: return (ctime(&sbuf.st_mtime));
61: }
62: }
63:
64: #define CADDID()
65: #define HEAD "%12.12s %4.4s %s Page %d\n\n\n", date+4, date+20, head, Page
66: #define TOLOWER(c) (isupper(c) ? tolower(c) : c) /* ouch! */
67: #define cerror(S) fprintf(stderr, "pr: %s", S)
68:
69: char *ffiler(s) char *s;
70: {
71: static char buf[100];
72:
73: sprintf(buf, "can't open %s", s);
74: return (buf);
75: }
76:
77: #define STDINNAME() nulls
78: #define TTY "/dev/tty", "r"
79: #define PROMPT() putc('\7', stderr) /* BEL */
80: #define TABS(N,C) if ((N = intopt(argv, &C)) < 0) N = DEFTAB
81: #define INSTABS() if (Itabn == 0 && Ttyout) Itabn = DEFTAB
82: #define ETABS (Inpos % Etabn)
83: #define ITABS (Itabn > 0 && Nspace >= (nc = Itabn - Outpos % Itabn))
84: #define NSEPC '\t'
85:
86: ANY *getspace();
87:
88: main(argc, argv) char *argv[];
89: {
90: FILS fstr[NFILES];
91: int nfdone = 0;
92:
93: Files = fstr;
94: for (argc = findopt(argc, argv); argc > 0; --argc, ++argv)
95: if (Multi == 'm') {
96: if (Nfiles >= NFILES - 1) die("too many files");
97: if (mustopen(*argv, &Files[Nfiles++]) == NULL)
98: ++nfdone; /* suppress printing */
99: } else {
100: if (print(*argv))
101: fclose(Files->f_f);
102: ++nfdone;
103: }
104: if (!nfdone) /* no files named, use stdin */
105: print(nulls); /* on GCOS, use current file, if any */
106: errprint(); /* print accumulated error reports */
107: exit(Error);
108: }
109:
110:
111: long Lnumb = 0;
112: FILE *Ttyin = stdin;
113: int Dblspace = 1, Fpage = 1, Formfeed = 0,
114: Length = LENGTH, Linew = 0, Offset = 0, Ncols = 1, Pause = 0, Sepc = 0,
115: Colw, Plength, Margin = MARGIN, Numw, Nsepc = NSEPC, Report = 1,
116: Etabn = 0, Etabc = '\t', Itabn = 0, Itabc = '\t';
117: char *Head = NULL;
118: CHAR *Buffer = NULL, *Bufend;
119: typedef struct { CHAR *c_ptr, *c_ptr0; long c_lno; } *COLP;
120: COLP Colpts;
121:
122: findopt(argc, argv) char *argv[];
123: {
124: char **eargv = argv;
125: int eargc = 0, c;
126:
127: argc = fixtty(argc, argv);
128: while (--argc > 0) {
129: switch (c = **++argv) {
130: case '-':
131: if ((c = *++*argv) == '\0') break;
132: case '+':
133: do {
134: if (isdigit(c))
135: { --*argv; Ncols = atoix(argv); }
136: else switch (c = TOLOWER(c)) {
137: case '+': if ((Fpage = atoix(argv)) < 1)
138: Fpage = 1;
139: continue;
140: case 'd': Dblspace = 2; continue;
141: case 'e': TABS(Etabn, Etabc); continue;
142: case 'f': ++Formfeed; continue;
143: case 'h': if (--argc > 0) Head = argv[1];
144: continue;
145: case 'i': TABS(Itabn, Itabc); continue;
146: case 'l': Length = atoix(argv); continue;
147: case 'a':
148: case 'm': Multi = c; continue;
149: case 'o': Offset = atoix(argv); continue;
150: case 'p': ++Pause; continue;
151: case 'r': Report = 0; continue;
152: case 's':
153: if ((Sepc = (*argv)[1]) != '\0') ++*argv;
154: else Sepc = '\t';
155: continue;
156: case 't': Margin = 0; continue;
157: case 'w': Linew = atoix(argv); continue;
158: case 'n':
159: case 'x': /* retained for historical reasons */
160: ++Lnumb;
161: if ((Numw = intopt(argv, &Nsepc)) <= 0)
162: Numw = NUMW;
163: case 'b': Balance = 1; continue;
164: case 'q': /* retained for historical reasons */
165: case 'j': /* ignore GCOS jprint option */
166: continue;
167: default : die("bad option");
168: }
169: } while ((c = *++*argv) != '\0');
170: if (Head == argv[1]) ++argv;
171: continue;
172: }
173: *eargv++ = *argv;
174: ++eargc;
175: }
176: if (Length == 0) Length = LENGTH;
177: if (Length <= Margin) Margin = 0;
178: Plength = Length - Margin/2;
179: if (Multi == 'm') Ncols = eargc;
180: switch (Ncols) {
181: case 0:
182: Ncols = 1;
183: case 1:
184: break;
185: default:
186: if (Etabn == 0) /* respect explicit tab specification */
187: Etabn = DEFTAB;
188: INSTABS();
189: }
190: if (Linew == 0) Linew = Ncols != 1 && Sepc == 0 ? LINEW : 512;
191: if (Lnumb) Linew -= Multi == 'm' ? Numw : Numw * Ncols;
192: if ((Colw = (Linew - Ncols + 1)/Ncols) < 1)
193: die("width too small");
194: if (Ncols != 1 && Multi == 0) {
195: UNS buflen = ((UNS)(Plength/Dblspace + 1))*(Linew+1)*sizeof(CHAR);
196: Buffer = (CHAR *)getspace(buflen);
197: Bufend = &Buffer[buflen];
198: Colpts = (COLP)getspace((UNS)((Ncols+1)*sizeof(*Colpts)));
199: }
200: if (Ttyout && (Pause || Formfeed) && !istty(stdin))
201: Ttyin = fopen(TTY);
202: return (eargc);
203: }
204:
205: intopt(argv, optp) char *argv[]; int *optp;
206: {
207: int c;
208:
209: if ((c = (*argv)[1]) != '\0' && !isdigit(c)) { *optp = c; ++*argv; }
210: return ((c = atoix(argv)) != 0 ? c : -1);
211: }
212:
213: int Page, C = '\0', Nspace, Inpos;
214:
215: print(name) char *name;
216: {
217: static int notfirst = 0;
218: char *date = NULL, *head = NULL;
219: int c;
220:
221: if (Multi != 'm' && mustopen(name, &Files[0]) == NULL) return (0);
222: if (Buffer) ungetc(Files->f_nextc, Files->f_f);
223: if (Lnumb) Lnumb = 1;
224: for (Page = 0; ; putpage()) {
225: if (C == EOF) break;
226: if (Buffer) nexbuf();
227: Inpos = 0;
228: if (get(0) == EOF) break;
229: fflush(stdout);
230: if (++Page >= Fpage) {
231: if (Ttyout && (Pause || Formfeed && !notfirst++)) {
232: PROMPT(); /* prompt with bell and pause */
233: while ((c = getc(Ttyin)) != EOF && c != '\n') ;
234: }
235: if (Margin == 0) continue;
236: CADDID();
237: if (date == NULL) date = GETDATE();
238: if (head == NULL) head = Head != NULL ? Head :
239: Nfiles < 2 ? Files->f_name : nulls;
240: printf("\n\n");
241: Nspace = Offset;
242: putspace();
243: printf(HEAD);
244: }
245: }
246: C = '\0';
247: return (1);
248: }
249:
250: int Outpos, Lcolpos, Pcolpos, Line;
251:
252: putpage()
253: {
254: register int colno;
255:
256: for (Line = Margin/2; ; get(0)) {
257: for (Nspace = Offset, colno = 0, Outpos = 0; C != '\f'; ) {
258: if (Lnumb && C != EOF && (colno == 0 || Multi == 'a')) {
259: if (Page >= Fpage) {
260: putspace();
261: printf("%*ld", Numw, Buffer ?
262: Colpts[colno].c_lno++ : Lnumb);
263: Outpos += Numw;
264: put(Nsepc);
265: }
266: ++Lnumb;
267: }
268: for (Lcolpos = 0, Pcolpos = 0;
269: C != '\n' && C != '\f' && C != EOF; get(colno))
270: put(C);
271: if (C == EOF || ++colno == Ncols ||
272: C == '\n' && get(colno) == EOF) break;
273: if (Sepc) put(Sepc);
274: else if ((Nspace += Colw - Lcolpos + 1) < 1) Nspace = 1;
275: }
276: /*
277: if (C == EOF) {
278: if (Margin != 0) break;
279: if (colno != 0) put('\n');
280: return;
281: }
282: */
283: if (C == EOF && colno == 0) {
284: if (Margin != 0) break;
285: return;
286: }
287: if (C == '\f') break;
288: put('\n');
289: if (Dblspace == 2 && Line < Plength) put('\n');
290: if (Line >= Plength) break;
291: }
292: if (Formfeed) put('\f');
293: else while (Line < Length) put('\n');
294: }
295:
296: nexbuf()
297: {
298: register CHAR *s = Buffer;
299: register COLP p = Colpts;
300: int j, c, bline = 0;
301:
302: for ( ; ; ) {
303: p->c_ptr0 = p->c_ptr = s;
304: if (p == &Colpts[Ncols]) return;
305: (p++)->c_lno = Lnumb + bline;
306: for (j = (Length - Margin)/Dblspace; --j >= 0; ++bline)
307: for (Inpos = 0; ; ) {
308: if ((c = getc(Files->f_f)) == EOF) {
309: for (*s = EOF; p <= &Colpts[Ncols]; ++p)
310: p->c_ptr0 = p->c_ptr = s;
311: if (Balance)
312: balance(bline);
313: return;
314: }
315: if (isprint(c)) ++Inpos;
316: if (Inpos <= Colw || c == '\n') {
317: *s = c;
318: if (++s >= Bufend)
319: die("page-buffer overflow");
320: }
321: if (c == '\n') break;
322: switch (c) {
323: case '\b': if (Inpos == 0) --s;
324: case ESC: if (Inpos > 0) --Inpos;
325: }
326: }
327: }
328: }
329:
330: balance(bline) /* line balancing for last page */
331: {
332: register CHAR *s = Buffer;
333: register COLP p = Colpts;
334: int colno = 0, j, c, l;
335:
336: c = bline % Ncols;
337: l = (bline + Ncols - 1)/Ncols;
338: bline = 0;
339: do {
340: for (j = 0; j < l; ++j)
341: while (*s++ != '\n') ;
342: (++p)->c_lno = Lnumb + (bline += l);
343: p->c_ptr0 = p->c_ptr = s;
344: if (++colno == c) --l;
345: } while (colno < Ncols - 1);
346: }
347:
348: get(colno)
349: {
350: static int peekc = 0;
351: register COLP p;
352: register FILS *q;
353: register int c;
354:
355: if (peekc)
356: { peekc = 0; c = Etabc; }
357: else if (Buffer) {
358: p = &Colpts[colno];
359: if (p->c_ptr >= (p+1)->c_ptr0) c = EOF;
360: else if ((c = *p->c_ptr) != EOF) ++p->c_ptr;
361: } else if ((c =
362: (q = &Files[Multi == 'a' ? 0 : colno])->f_nextc) == EOF) {
363: for (q = &Files[Nfiles]; --q >= Files && q->f_nextc == EOF; ) ;
364: if (q >= Files) c = '\n';
365: } else
366: q->f_nextc = getc(q->f_f);
367: if (Etabn != 0 && c == Etabc) {
368: ++Inpos;
369: peekc = ETABS;
370: c = ' ';
371: } else if (isprint(c))
372: ++Inpos;
373: else
374: switch (c) {
375: case '\b':
376: case ESC:
377: if (Inpos > 0) --Inpos;
378: break;
379: case '\f':
380: if (Ncols == 1) break;
381: c = '\n';
382: case '\n':
383: case '\r':
384: Inpos = 0;
385: }
386: return (C = c);
387: }
388:
389: put(c)
390: {
391: int move;
392:
393: switch (c) {
394: case ' ':
395: ++Nspace; ++Lcolpos; return;
396: case '\b':
397: if (Lcolpos == 0) return;
398: if (Nspace > 0) { --Nspace; --Lcolpos; return; }
399: if (Lcolpos > Pcolpos) { --Lcolpos; return; }
400: case ESC:
401: move = -1;
402: break;
403: case '\n':
404: ++Line;
405: case '\r':
406: case '\f':
407: Pcolpos = 0; Lcolpos = 0; Nspace = 0; Outpos = 0;
408: default:
409: move = (isprint(c) != 0);
410: }
411: if (Page < Fpage) return;
412: if (Lcolpos > 0 || move > 0) Lcolpos += move;
413: if (Lcolpos <= Colw) {
414: putspace();
415: putchar(c);
416: Pcolpos = Lcolpos;
417: Outpos += move;
418: }
419: }
420:
421: putspace()
422: {
423: int nc;
424:
425: for ( ; Nspace > 0; Outpos += nc, Nspace -= nc)
426: if (ITABS)
427: putchar(Itabc);
428: else {
429: nc = 1;
430: putchar(' ');
431: }
432: }
433:
434: atoix(p) register char **p;
435: {
436: register int n = 0, c;
437:
438: while (isdigit(c = *++*p)) n = 10*n + c - '0';
439: --*p;
440: return (n);
441: }
442:
443: /* Defer message about failure to open file to prevent messing up
444: alignment of page with tear perforations or form markers.
445: Treat empty file as special case and report as diagnostic.
446: */
447: #define EMPTY 14 /* length of " -- empty file" */
448: typedef struct err { struct err *e_nextp; char *e_mess; } ERR;
449: ERR *Err = NULL, *Lasterr = (ERR *)&Err;
450:
451: FILE *mustopen(s, f) char *s; register FILS *f;
452: {
453: if (*s == '\0') {
454: f->f_name = STDINNAME();
455: f->f_f = stdin;
456: } else if ((f->f_f = fopen(f->f_name = s, "r")) == NULL) {
457: char *strcpy();
458: s = ffiler(f->f_name);
459: s = strcpy((char *)getspace((UNS)(strlen(s) + 1)), s);
460: }
461: if (f->f_f != NULL) {
462: if ((f->f_nextc = getc(f->f_f)) != EOF || Multi == 'm')
463: return (f->f_f);
464: sprintf(s = (char *)getspace((UNS)(strlen(f->f_name) + 1 + EMPTY)),
465: "%s -- empty file", f->f_name);
466: fclose(f->f_f);
467: }
468: Error = 1;
469: if (Report)
470: if (Ttyout) { /* accumulate error reports */
471: Lasterr = Lasterr->e_nextp = (ERR *)getspace((UNS)sizeof(ERR));
472: Lasterr->e_nextp = NULL;
473: Lasterr->e_mess = s;
474: } else { /* ok to print error report now */
475: cerror(s);
476: putc('\n', stderr);
477: }
478: return ((FILE *)NULL);
479: }
480:
481: ANY *getspace(n) UNS n;
482: {
483: ANY *t;
484:
485: if ((t = (ANY *)malloc(n)) == NULL) die("out of space");
486: return (t);
487: }
488:
489: die(s) char *s;
490: {
491: ++Error;
492: errprint();
493: cerror(s);
494: putc('\n', stderr);
495: exit(1);
496: }
497:
498: onintr()
499: {
500: ++Error;
501: errprint();
502: INTREXIT(1);
503: }
504:
505: errprint() /* print accumulated error reports */
506: {
507: fflush(stdout);
508: for ( ; Err != NULL; Err = Err->e_nextp) {
509: cerror(Err->e_mess);
510: putc('\n', stderr);
511: }
512: done();
513: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.