|
|
1.1 root 1: /*
2: * pr -- print files
3: *
4: * All references to page length exclude possible margin lines.
5: */
6:
7: #include <stdio.h>
8:
9: #ifdef COHERENT
10: #include <sys/types.h>
11: #include <errno.h>
12: #endif
13:
14: #ifdef MSDOS
15: #include <sys/timeb.h> /* typedef long time_t; */
16: #endif
17:
18: #define LSIZE 256 /* line size */
19: #define LENGTH (66-MARGIN) /* default page length */
20: #define WIDTH 80 /* default page width */
21: #define TMAR 5 /* five lines of top margin */
22: #define BMAR 5 /* five lines of bottom margin */
23: #define HEADER 2 /* line # where header appears */
24:
25: #define MARGIN (TMAR+BMAR)
26:
27:
28: /*
29: * info on input file streams
30: * Unless -m is in effect, only f[0] is used.
31: */
32: struct f {
33: int f_ff; /* '\f' received */
34: FILE *f_stream; /* input stream */
35: };
36:
37:
38: int ncol = 1, /* # of columns */
39: nskip, /* # pages to skip of each file */
40: length = LENGTH, /* page length */
41: width = WIDTH, /* page width */
42: lno, /* current input line # */
43: fwidth; /* field width */
44: char schar, /* separator char */
45: tflag, /* -t: no header or margins */
46: mflag, /* -m: multiple file output */
47: nflag, /* -n: line number output */
48: *date, /* date string */
49: *header, /* -h: header text */
50: **lines; /* buffer addr for multicolumn */
51:
52: struct f f[20]; /* input file info */
53:
54: char **init( ),
55: *malloc( ),
56: *ctime( );
57: int page1( ), page2( ),
58: putl( ), nop( );
59: FILE *openf( );
60:
61: int (*page)( ) = page1;
62:
63:
64: /*
65: * paginate files to standard output
66: * If no files are given, use standard input. The file name "-" also
67: * means standard input.
68: */
69: main( argc, argv)
70: char **argv;
71: {
72: #ifdef MSDOS
73: msdoscvt("pr", &argc, &argv);
74: #endif
75: argv = init( argc, argv);
76:
77: if (*argv)
78: while (*argv) {
79: f[0].f_stream = openf( *argv);
80: print( *argv++);
81: fclose( f[0].f_stream);
82: }
83: else
84: print( "");
85:
86: return (0);
87: }
88:
89:
90: /*
91: * initialize & get options
92: * Flags are recognized up to the first file name. If multi-column (-N),
93: * allocate line array. If printing multiple files (-m), open all files.
94: * There are two paging algorithms: one file per column (page1), and
95: * many columns per file (page2). The latter requires page buffering.
96: * init( ) makes this selection.
97: */
98: char **
99: init( ac, av)
100: register char **av;
101: {
102: register mar = MARGIN;
103: static char obuf[BUFSIZ];
104: time_t tvec;
105:
106: setbuf( stdout, obuf);
107:
108: while (++av, --ac) {
109: if (av[0][0] == '+')
110: if ((nskip=atoi( &av[0][1])) <= 0)
111: fatal( "bad skip");
112: else
113: continue;
114: if (av[0][0] != '-')
115: break;
116: switch (av[0][1]) {
117: case '\0':
118: break;
119: case 'l':
120: length = atoi( &av[0][2]) - mar;
121: continue;
122: case 'w':
123: width = atoi( &av[0][2]);
124: continue;
125: case 'h':
126: if (av[0][2])
127: header = &av[0][2];
128: else {
129: if (--ac <= 0)
130: fatal( "missing header arg");
131: header = (++av)[0];
132: }
133: continue;
134: case 's':
135: if ((schar=av[0][2]) == '\0')
136: schar = '\t';
137: continue;
138: case 'm':
139: ++mflag;
140: continue;
141: case 't':
142: ++tflag;
143: length += mar;
144: mar = 0;
145: continue;
146: case 'n':
147: ++nflag;
148: continue;
149: default:
150: if ('0'<=av[0][1] && av[0][1]<='9')
151: ncol = atoi( &av[0][1]);
152: else
153: fatal( "no such switch %s", av[0]);
154: continue;
155: }
156: break;
157:
158: }
159:
160: f[0].f_stream = stdin;
161: if (mflag && av[0]) {
162: ncol = 0;
163: do {
164: f[ncol++].f_stream = openf( av++[0]);
165: } while (av[0]);
166: }
167:
168: /*
169: * check that all options jive
170: */
171: if (length <= 0)
172: if (length == -mar) { /* gunja artifice */
173: length = 1;
174: ++tflag;
175: }
176: else
177: fatal( "length too small");
178: fwidth = width / ncol;
179: if (schar)
180: --fwidth;
181: if (fwidth <= 0 || (nflag && fwidth < 10))
182: fatal( "width too small");
183: if (fwidth >= LSIZE-1)
184: fatal( "too wide");
185: if (ncol>1 && mflag==0) {
186: if ((lines=malloc( (ncol-1)*length*sizeof( char *))) == NULL)
187: fatal( "insufficient core");
188: page = page2;
189: }
190:
191: time( &tvec);
192: date = ctime( &tvec);
193:
194: return (av);
195: }
196:
197:
198: /*
199: * open input file
200: * Failure to open is fatal. openf( "-") returns stdin.
201: */
202: FILE *
203: openf( file)
204: register char *file;
205: {
206: register FILE *stream;
207:
208: lno = 0;
209: if (file[0]=='-' && file[1]=='\0')
210: return (stdin);
211: if ((stream=fopen( file, "r")) == NULL)
212: #ifdef COHERENT
213: if (errno == EMFILE)
214: fatal( "too many files for -m");
215: else
216: #endif
217: fatal( "can't open %s", file);
218: return (stream);
219: }
220:
221:
222: /*
223: * print from open input streams
224: * Control output of pages, perhaps provide header/footer margins and title.
225: * The paging routine is expected to give an eof warning on the last page.
226: */
227: print( file)
228: char *file;
229: {
230: register i,
231: pg,
232: eof;
233:
234: for (pg=1; pg<=nskip; ++pg)
235: if ((*page)( nop))
236: return;
237:
238: do {
239: if (tflag == 0)
240: for (i=0; i<TMAR; ++i) {
241: if (i != HEADER) {
242: putchar( '\n');
243: continue;
244: }
245: printf( "%.12s%.5s %s Page %d, line %ld\n",
246: date+4, date+19,
247: header? header: file,
248: pg,
249: (long)(pg-1)*(mflag?1:ncol)*length+1);
250: }
251: eof = (*page)( putl);
252: if (tflag == 0)
253: for (i=0; i<BMAR; ++i)
254: putchar( '\n');
255: } while (++pg, eof==0);
256: }
257:
258:
259: /*
260: * page one stream per column
261: * This handles all cases of one column per page.
262: * Formfeed advances output of that stream to the next page.
263: * Eof is only indicated when all streams give this condition.
264: */
265: page1( putline)
266: int (*putline)( );
267: {
268: register i,
269: j;
270: char lbuf[LSIZE];
271:
272: for (i=0; i<length; ++i) {
273: for (j=0; j<ncol; ++j) {
274: getline( &f[j], lbuf);
275: (*putline)( lbuf, j<ncol-1? schar: 0);
276: }
277: (*putline)( (char *)0);
278: }
279:
280: i = 0;
281: for (j=0; j<ncol; ++j) {
282: f[j].f_ff = 0;
283: if (feof( f[j].f_stream))
284: ++i;
285: else if (ungetc( getc( f[j].f_stream), f[j].f_stream) == EOF)
286: ++i;
287: }
288: return (i == j);
289: }
290:
291:
292: /*
293: * page one stream across multiple columns
294: * The first `ncol'-1 columns of text are buffered; the last is simply
295: * read as needed. Formfeeds advance output to the next column.
296: */
297: page2( putline)
298: int (*putline)( );
299: {
300: register i,
301: j,
302: k;
303: char lbuf[LSIZE];
304:
305: for (i=0; i<(ncol-1)*length; ++i) {
306: if ((i%length) == 0)
307: f[0].f_ff = 0;
308: if (k = getline( &f[0], lbuf)) {
309: if ((lines[i]=malloc( k+1)) == NULL)
310: fatal( "out of core");
311: strcpy( lines[i], lbuf);
312: }
313: else
314: lines[i] = NULL;
315: }
316: f[0].f_ff = 0;
317:
318: for (i=0; i<length; ++i) {
319: for (j=0; j<ncol-1; ++j) {
320: k = j*length + i;
321: if (lines[k]) {
322: (*putline)( lines[k], schar);
323: free( lines[k]);
324: }
325: else
326: (*putline)( "", schar);
327: }
328: getline( &f[0], lbuf);
329: (*putline)( lbuf, 0);
330: (*putline)( (char *)0);
331: }
332:
333: if (feof( f[0].f_stream))
334: return (1);
335: if (ungetc( getc( f[0].f_stream), f[0].f_stream) == EOF)
336: return (1);
337: return (0);
338: }
339:
340:
341: /*
342: * read a line
343: * Simple char processing is done, including tab expansion. getline( )
344: * will return an empty line if f_ff is set. Lines are truncated to
345: * fit the field width.
346: */
347: getline( fp, lbuf)
348: register struct f *fp;
349: char *lbuf;
350: {
351: register col;
352: register char *p;
353: char c;
354:
355: col = 0;
356: p = lbuf;
357: if (feof( fp->f_stream)==0 && fp->f_ff==0) {
358: if (nflag) {
359: sprintf(p, "%4d: ", ++lno);
360: p += 6;
361: }
362: for (; ; ) {
363: switch (c = getc(fp->f_stream)) {
364: case '\f':
365: ++fp->f_ff;
366: case EOF:
367: if (nflag && (p == &lbuf[6]))
368: p = lbuf;
369: break;
370: case '\n':
371: break;
372: case '\r':
373: continue;
374: case '\t':
375: do {
376: if (p<&lbuf[LSIZE-1] && col<fwidth)
377: *p++ = ' ';
378: } while (++col & 7);
379: continue;
380: case '\b':
381: if (col) {
382: --col;
383: if (p<&lbuf[LSIZE-1] && col<fwidth)
384: *p++ = c;
385: }
386: continue;
387: default:
388: if (p<&lbuf[LSIZE-1] && col<fwidth)
389: *p++ = c;
390: ++col;
391: continue;
392: }
393: break;
394: }
395:
396: }
397: *p = '\0';
398: return (p - lbuf);
399: }
400:
401:
402: /*
403: * write a line, incrementally
404: * A line of output can be built by successive calls to putl( ). A line
405: * address of 0 puts the newline. Simple char processing is done,
406: * including tab optimization. Each line segment is padded to the
407: * field width, unless a there is a field separator char.
408: */
409: putl( lbuf, schr)
410: char *lbuf;
411: {
412: register char *p;
413: register c,
414: nextxcol;
415: static col,
416: xcol;
417:
418: if ((p=lbuf) == NULL) {
419: col = 0;
420: xcol = 0;
421: putchar( '\n');
422: return;
423: }
424: nextxcol = xcol + fwidth;
425:
426: for (; ; ) {
427: if ((c= *p++) == '\0') {
428: if ((c=schr) == '\0')
429: break;
430: schr = 0;
431: --p;
432: nextxcol = xcol + 1;
433: }
434: if (c == ' ') {
435: ++xcol;
436: continue;
437: }
438: if (c == '\b') {
439: --xcol;
440: continue;
441: }
442: while ((col|7)+1 <= xcol) {
443: col = (col|7) + 1;
444: putchar( '\t');
445: }
446: while (col < xcol) {
447: ++col;
448: putchar( ' ');
449: }
450: while (col > xcol) {
451: --col;
452: putchar( '\b');
453: }
454: putchar( c);
455: xcol = ++col;
456: }
457:
458: xcol = nextxcol;
459: }
460:
461:
462: /*
463: * throw away output line
464: * Used when skipping the first pages of input.
465: */
466: nop( )
467: {
468:
469: }
470:
471:
472: /*
473: * print error message, exit
474: */
475: fatal( arg0)
476: {
477:
478: fflush( stdout);
479: fprintf( stderr, "pr: %r\n", &arg0);
480: exit (1);
481: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.