Annotation of src/usr.bin/pr/pr.c, Revision 1.2
1.2 ! deraadt 1: /* $OpenBSD$ */
! 2:
1.1 deraadt 3: /*-
4: * Copyright (c) 1991 Keith Muller.
5: * Copyright (c) 1993
6: * The Regents of the University of California. All rights reserved.
7: *
8: * This code is derived from software contributed to Berkeley by
9: * Keith Muller of the University of California, San Diego.
10: *
11: * Redistribution and use in source and binary forms, with or without
12: * modification, are permitted provided that the following conditions
13: * are met:
14: * 1. Redistributions of source code must retain the above copyright
15: * notice, this list of conditions and the following disclaimer.
16: * 2. Redistributions in binary form must reproduce the above copyright
17: * notice, this list of conditions and the following disclaimer in the
18: * documentation and/or other materials provided with the distribution.
19: * 3. All advertising materials mentioning features or use of this software
20: * must display the following acknowledgement:
21: * This product includes software developed by the University of
22: * California, Berkeley and its contributors.
23: * 4. Neither the name of the University nor the names of its contributors
24: * may be used to endorse or promote products derived from this software
25: * without specific prior written permission.
26: *
27: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37: * SUCH DAMAGE.
38: */
39:
40: #ifndef lint
41: static char copyright[] =
42: "@(#) Copyright (c) 1993\n\
43: The Regents of the University of California. All rights reserved.\n";
44: #endif /* not lint */
45:
46: #ifndef lint
47: /* from: static char sccsid[] = "@(#)pr.c 8.1 (Berkeley) 6/6/93"; */
1.2 ! deraadt 48: static char *rcsid = "$OpenBSD: pr.c,v 1.1.1.1 1995/10/18 08:45:56 deraadt Exp $";
1.1 deraadt 49: #endif /* not lint */
50:
51: #include <sys/types.h>
52: #include <sys/time.h>
53: #include <sys/stat.h>
54:
55: #include <ctype.h>
56: #include <errno.h>
57: #include <signal.h>
58: #include <stdio.h>
59: #include <stdlib.h>
60: #include <string.h>
61: #include <unistd.h>
62:
63: #include "pr.h"
64: #include "extern.h"
65:
66: /*
67: * pr: a printing and pagination filter. If multiple input files
68: * are specified, each is read, formatted, and written to standard
69: * output. By default, input is seperated into 66-line pages, each
70: * with a header that includes the page number, date, time and the
71: * files pathname.
72: *
73: * Complies with posix P1003.2/D11
74: */
75:
76: /*
77: * parameter variables
78: */
79: int pgnm; /* starting page number */
80: int clcnt; /* number of columns */
81: int colwd; /* column data width - multiple columns */
82: int across; /* mult col flag; write across page */
83: int dspace; /* double space flag */
84: char inchar; /* expand input char */
85: int ingap; /* expand input gap */
86: int formfeed; /* use formfeed as trailer */
87: char *header; /* header name instead of file name */
88: char ochar; /* contract output char */
89: int ogap; /* contract output gap */
90: int lines; /* number of lines per page */
91: int merge; /* merge multiple files in output */
92: char nmchar; /* line numbering append char */
93: int nmwd; /* width of line number field */
94: int offst; /* number of page offset spaces */
95: int nodiag; /* do not report file open errors */
96: char schar; /* text column separation character */
97: int sflag; /* -s option for multiple columns */
98: int nohead; /* do not write head and trailer */
99: int pgwd; /* page width with multiple col output */
100: char *timefrmt; /* time conversion string */
101:
102: /*
103: * misc globals
104: */
105: FILE *err; /* error message file pointer */
106: int addone; /* page length is odd with double space */
107: int errcnt; /* error count on file processing */
108: char digs[] = "0123456789"; /* page number translation map */
109:
110: int
111: main(argc, argv)
112: int argc;
113: char *argv[];
114: {
115: int ret_val;
116:
117: if (signal(SIGINT, SIG_IGN) != SIG_IGN)
118: (void)signal(SIGINT, terminate);
119: ret_val = setup(argc, argv);
120: if (!ret_val) {
121: /*
122: * select the output format based on options
123: */
124: if (merge)
125: ret_val = mulfile(argc, argv);
126: else if (clcnt == 1)
127: ret_val = onecol(argc, argv);
128: else if (across)
129: ret_val = horzcol(argc, argv);
130: else
131: ret_val = vertcol(argc, argv);
132: } else
133: usage();
134: flsh_errs();
135: if (errcnt || ret_val)
136: exit(1);
137: return(0);
138: }
139:
140: /*
141: * onecol: print files with only one column of output.
142: * Line length is unlimited.
143: */
144: int
145: onecol(argc, argv)
146: int argc;
147: char *argv[];
148: {
149: register int cnt = -1;
150: register int off;
151: register int lrgln;
152: register int linecnt;
153: register int num;
154: int lncnt;
155: int pagecnt;
156: int ips;
157: int ops;
158: int cps;
159: char *obuf;
160: char *lbuf;
161: char *nbuf;
162: char *hbuf;
163: char *ohbuf;
164: FILE *inf;
165: char *fname;
166: int mor;
167:
168: if (nmwd)
169: num = nmwd + 1;
170: else
171: num = 0;
172: off = num + offst;
173:
174: /*
175: * allocate line buffer
176: */
177: if ((obuf = malloc((unsigned)(LBUF + off)*sizeof(char))) == NULL) {
178: mfail();
179: return(1);
180: }
181: /*
182: * allocate header buffer
183: */
184: if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL) {
185: mfail();
186: return(1);
187: }
188:
189: ohbuf = hbuf + offst;
190: nbuf = obuf + offst;
191: lbuf = nbuf + num;
192: if (num)
193: nbuf[--num] = nmchar;
194: if (offst) {
195: (void)memset(obuf, (int)' ', offst);
196: (void)memset(hbuf, (int)' ', offst);
197: }
198:
199: /*
200: * loop by file
201: */
202: while ((inf = nxtfile(argc, argv, &fname, ohbuf, 0)) != NULL) {
203: if (pgnm) {
204: /*
205: * skip to specified page
206: */
207: if (inskip(inf, pgnm, lines))
208: continue;
209: pagecnt = pgnm;
210: } else
211: pagecnt = 1;
212: lncnt = 0;
213:
214: /*
215: * loop by page
216: */
217: for(;;) {
218: linecnt = 0;
219: lrgln = 0;
220: ops = 0;
221: ips = 0;
222: cps = 0;
223:
224: /*
225: * loop by line
226: */
227: while (linecnt < lines) {
228: /*
229: * input next line
230: */
231: if ((cnt = inln(inf,lbuf,LBUF,&cps,0,&mor)) < 0)
232: break;
233: if (!linecnt && !nohead &&
234: prhead(hbuf, fname, pagecnt))
235: return(1);
236:
237: /*
238: * start of new line.
239: */
240: if (!lrgln) {
241: if (num)
242: addnum(nbuf, num, ++lncnt);
243: if (otln(obuf,cnt+off, &ips, &ops, mor))
244: return(1);
245: } else if (otln(lbuf, cnt, &ips, &ops, mor))
246: return(1);
247:
248: /*
249: * if line bigger than buffer, get more
250: */
251: if (mor) {
252: lrgln = 1;
253: continue;
254: }
255:
256: /*
257: * whole line rcvd. reset tab proc. state
258: */
259: ++linecnt;
260: lrgln = 0;
261: ops = 0;
262: ips = 0;
263: }
264:
265: /*
266: * fill to end of page
267: */
268: if (linecnt && prtail(lines-linecnt-lrgln, lrgln))
269: return(1);
270:
271: /*
272: * On EOF go to next file
273: */
274: if (cnt < 0)
275: break;
276: ++pagecnt;
277: }
278: if (inf != stdin)
279: (void)fclose(inf);
280: }
281: if (eoptind < argc)
282: return(1);
283: return(0);
284: }
285:
286: /*
287: * vertcol: print files with more than one column of output down a page
288: */
289: int
290: vertcol(argc, argv)
291: int argc;
292: char *argv[];
293: {
294: register char *ptbf;
295: register char **lstdat;
296: register int i;
297: register int j;
298: register int cnt = -1;
299: register int pln;
300: register int *indy;
301: int cvc;
302: int *lindy;
303: int lncnt;
304: int stp;
305: int pagecnt;
306: int col = colwd + 1;
307: int mxlen = pgwd + offst + 1;
308: int mclcnt = clcnt - 1;
309: struct vcol *vc;
310: int mvc;
311: int tvc;
312: int cw = nmwd + 1;
313: int fullcol;
314: char *buf;
315: char *hbuf;
316: char *ohbuf;
317: char *fname;
318: FILE *inf;
319: int ips = 0;
320: int cps = 0;
321: int ops = 0;
322: int mor = 0;
323:
324: /*
325: * allocate page buffer
326: */
327: if ((buf = malloc((unsigned)lines*mxlen*sizeof(char))) == NULL) {
328: mfail();
329: return(1);
330: }
331:
332: /*
333: * allocate page header
334: */
335: if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL) {
336: mfail();
337: return(1);
338: }
339: ohbuf = hbuf + offst;
340: if (offst)
341: (void)memset(hbuf, (int)' ', offst);
342:
343: /*
344: * col pointers when no headers
345: */
346: mvc = lines * clcnt;
347: if ((vc =
348: (struct vcol *)malloc((unsigned)mvc*sizeof(struct vcol))) == NULL) {
349: mfail();
350: return(1);
351: }
352:
353: /*
354: * pointer into page where last data per line is located
355: */
356: if ((lstdat = (char **)malloc((unsigned)lines*sizeof(char *))) == NULL){
357: mfail();
358: return(1);
359: }
360:
361: /*
362: * fast index lookups to locate start of lines
363: */
364: if ((indy = (int *)malloc((unsigned)lines*sizeof(int))) == NULL) {
365: mfail();
366: return(1);
367: }
368: if ((lindy = (int *)malloc((unsigned)lines*sizeof(int))) == NULL) {
369: mfail();
370: return(1);
371: }
372:
373: if (nmwd)
374: fullcol = col + cw;
375: else
376: fullcol = col;
377:
378: /*
379: * initialize buffer lookup indexes and offset area
380: */
381: for (j = 0; j < lines; ++j) {
382: lindy[j] = j * mxlen;
383: indy[j] = lindy[j] + offst;
384: if (offst) {
385: ptbf = buf + lindy[j];
386: (void)memset(ptbf, (int)' ', offst);
387: ptbf += offst;
388: } else
389: ptbf = buf + indy[j];
390: lstdat[j] = ptbf;
391: }
392:
393: /*
394: * loop by file
395: */
396: while ((inf = nxtfile(argc, argv, &fname, ohbuf, 0)) != NULL) {
397: if (pgnm) {
398: /*
399: * skip to requested page
400: */
401: if (inskip(inf, pgnm, lines))
402: continue;
403: pagecnt = pgnm;
404: } else
405: pagecnt = 1;
406: lncnt = 0;
407:
408: /*
409: * loop by page
410: */
411: for(;;) {
412: /*
413: * loop by column
414: */
415: cvc = 0;
416: for (i = 0; i < clcnt; ++i) {
417: j = 0;
418: /*
419: * if last column, do not pad
420: */
421: if (i == mclcnt)
422: stp = 1;
423: else
424: stp = 0;
425: /*
426: * loop by line
427: */
428: for(;;) {
429: /*
430: * is this first column
431: */
432: if (!i) {
433: ptbf = buf + indy[j];
434: lstdat[j] = ptbf;
435: } else
436: ptbf = lstdat[j];
437: vc[cvc].pt = ptbf;
438:
439: /*
440: * add number
441: */
442: if (nmwd) {
443: addnum(ptbf, nmwd, ++lncnt);
444: ptbf += nmwd;
445: *ptbf++ = nmchar;
446: }
447:
448: /*
449: * input next line
450: */
451: cnt = inln(inf,ptbf,colwd,&cps,1,&mor);
452: vc[cvc++].cnt = cnt;
453: if (cnt < 0)
454: break;
455: ptbf += cnt;
456:
457: /*
458: * pad all but last column on page
459: */
460: if (!stp) {
461: /*
462: * pad to end of column
463: */
464: if (sflag)
465: *ptbf++ = schar;
466: else if ((pln = col-cnt) > 0) {
467: (void)memset(ptbf,
468: (int)' ',pln);
469: ptbf += pln;
470: }
471: }
472: /*
473: * remember last char in line
474: */
475: lstdat[j] = ptbf;
476: if (++j >= lines)
477: break;
478: }
479: if (cnt < 0)
480: break;
481: }
482:
483: /*
484: * when -t (no header) is specified the spec requires
485: * the min number of lines. The last page may not have
486: * balanced length columns. To fix this we must reorder
487: * the columns. This is a very slow technique so it is
488: * only used under limited conditions. Without -t, the
489: * balancing of text columns is unspecified. To NOT
490: * balance the last page, add the global variable
491: * nohead to the if statement below e.g.
492: *
493: * if ((cnt < 0) && nohead && cvc ......
494: */
495: --cvc;
496:
497: /*
498: * check to see if last page needs to be reordered
499: */
500: if ((cnt < 0) && cvc && ((mvc-cvc) >= clcnt)){
501: pln = cvc/clcnt;
502: if (cvc % clcnt)
503: ++pln;
504:
505: /*
506: * print header
507: */
508: if (!nohead && prhead(hbuf, fname, pagecnt))
509: return(1);
510: for (i = 0; i < pln; ++i) {
511: ips = 0;
512: ops = 0;
513: if (offst&& otln(buf,offst,&ips,&ops,1))
514: return(1);
515: tvc = i;
516:
517: for (j = 0; j < clcnt; ++j) {
518: /*
519: * determine column length
520: */
521: if (j == mclcnt) {
522: /*
523: * last column
524: */
525: cnt = vc[tvc].cnt;
526: if (nmwd)
527: cnt += cw;
528: } else if (sflag) {
529: /*
530: * single ch between
531: */
532: cnt = vc[tvc].cnt + 1;
533: if (nmwd)
534: cnt += cw;
535: } else
536: cnt = fullcol;
537: if (otln(vc[tvc].pt, cnt, &ips,
538: &ops, 1))
539: return(1);
540: tvc += pln;
541: if (tvc >= cvc)
542: break;
543: }
544: /*
545: * terminate line
546: */
547: if (otln(buf, 0, &ips, &ops, 0))
548: return(1);
549: }
550: /*
551: * pad to end of page
552: */
553: if (prtail((lines - pln), 0))
554: return(1);
555: /*
556: * done with output, go to next file
557: */
558: break;
559: }
560:
561: /*
562: * determine how many lines to output
563: */
564: if (i > 0)
565: pln = lines;
566: else
567: pln = j;
568:
569: /*
570: * print header
571: */
572: if (pln && !nohead && prhead(hbuf, fname, pagecnt))
573: return(1);
574:
575: /*
576: * output each line
577: */
578: for (i = 0; i < pln; ++i) {
579: ptbf = buf + lindy[i];
580: if ((j = lstdat[i] - ptbf) <= offst)
581: break;
582: if (otln(ptbf, j, &ips, &ops, 0))
583: return(1);
584: }
585:
586: /*
587: * pad to end of page
588: */
589: if (pln && prtail((lines - pln), 0))
590: return(1);
591:
592: /*
593: * if EOF go to next file
594: */
595: if (cnt < 0)
596: break;
597: ++pagecnt;
598: }
599: if (inf != stdin)
600: (void)fclose(inf);
601: }
602: if (eoptind < argc)
603: return(1);
604: return(0);
605: }
606:
607: /*
608: * horzcol: print files with more than one column of output across a page
609: */
610: int
611: horzcol(argc, argv)
612: int argc;
613: char *argv[];
614: {
615: register char *ptbf;
616: register int pln;
617: register int cnt = -1;
618: register char *lstdat;
619: register int col = colwd + 1;
620: register int j;
621: register int i;
622: int lncnt;
623: int pagecnt;
624: char *buf;
625: char *hbuf;
626: char *ohbuf;
627: char *fname;
628: FILE *inf;
629: int ips = 0;
630: int cps = 0;
631: int ops = 0;
632: int mor = 0;
633:
634: if ((buf = malloc((unsigned)(pgwd+offst+1)*sizeof(char))) == NULL) {
635: mfail();
636: return(1);
637: }
638:
639: /*
640: * page header
641: */
642: if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL) {
643: mfail();
644: return(1);
645: }
646: ohbuf = hbuf + offst;
647: if (offst) {
648: (void)memset(buf, (int)' ', offst);
649: (void)memset(hbuf, (int)' ', offst);
650: }
651:
652: /*
653: * loop by file
654: */
655: while ((inf = nxtfile(argc, argv, &fname, ohbuf, 0)) != NULL) {
656: if (pgnm) {
657: if (inskip(inf, pgnm, lines))
658: continue;
659: pagecnt = pgnm;
660: } else
661: pagecnt = 1;
662: lncnt = 0;
663:
664: /*
665: * loop by page
666: */
667: for(;;) {
668: /*
669: * loop by line
670: */
671: for (i = 0; i < lines; ++i) {
672: ptbf = buf + offst;
673: lstdat = ptbf;
674: j = 0;
675: /*
676: * loop by col
677: */
678: for(;;) {
679: if (nmwd) {
680: /*
681: * add number to column
682: */
683: addnum(ptbf, nmwd, ++lncnt);
684: ptbf += nmwd;
685: *ptbf++ = nmchar;
686: }
687: /*
688: * input line
689: */
690: if ((cnt = inln(inf,ptbf,colwd,&cps,1,
691: &mor)) < 0)
692: break;
693: ptbf += cnt;
694: lstdat = ptbf;
695:
696: /*
697: * if last line skip padding
698: */
699: if (++j >= clcnt)
700: break;
701:
702: /*
703: * pad to end of column
704: */
705: if (sflag)
706: *ptbf++ = schar;
707: else if ((pln = col - cnt) > 0) {
708: (void)memset(ptbf,(int)' ',pln);
709: ptbf += pln;
710: }
711: }
712:
713: /*
714: * determine line length
715: */
716: if ((j = lstdat - buf) <= offst)
717: break;
718: if (!i && !nohead &&
719: prhead(hbuf, fname, pagecnt))
720: return(1);
721: /*
722: * output line
723: */
724: if (otln(buf, j, &ips, &ops, 0))
725: return(1);
726: }
727:
728: /*
729: * pad to end of page
730: */
731: if (i && prtail(lines-i, 0))
732: return(1);
733:
734: /*
735: * if EOF go to next file
736: */
737: if (cnt < 0)
738: break;
739: ++pagecnt;
740: }
741: if (inf != stdin)
742: (void)fclose(inf);
743: }
744: if (eoptind < argc)
745: return(1);
746: return(0);
747: }
748:
749: /*
750: * mulfile: print files with more than one column of output and
751: * more than one file concurrently
752: */
753: int
754: mulfile(argc, argv)
755: int argc;
756: char *argv[];
757: {
758: register char *ptbf;
759: register int j;
760: register int pln;
761: register int cnt;
762: register char *lstdat;
763: register int i;
764: FILE **fbuf;
765: int actf;
766: int lncnt;
767: int col;
768: int pagecnt;
769: int fproc;
770: char *buf;
771: char *hbuf;
772: char *ohbuf;
773: char *fname;
774: int ips = 0;
775: int cps = 0;
776: int ops = 0;
777: int mor = 0;
778:
779: /*
780: * array of FILE *, one for each operand
781: */
782: if ((fbuf = (FILE **)malloc((unsigned)clcnt*sizeof(FILE *))) == NULL) {
783: mfail();
784: return(1);
785: }
786:
787: /*
788: * page header
789: */
790: if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL) {
791: mfail();
792: return(1);
793: }
794: ohbuf = hbuf + offst;
795:
796: /*
797: * do not know how many columns yet. The number of operands provide an
798: * upper bound on the number of columns. We use the number of files
799: * we can open successfully to set the number of columns. The operation
800: * of the merge operation (-m) in relation to unsuccesful file opens
801: * is unspecified by posix.
802: */
803: j = 0;
804: while (j < clcnt) {
805: if ((fbuf[j] = nxtfile(argc, argv, &fname, ohbuf, 1)) == NULL)
806: break;
807: if (pgnm && (inskip(fbuf[j], pgnm, lines)))
808: fbuf[j] = NULL;
809: ++j;
810: }
811:
812: /*
813: * if no files, exit
814: */
815: if (!j)
816: return(1);
817:
818: /*
819: * calculate page boundries based on open file count
820: */
821: clcnt = j;
822: if (nmwd) {
823: colwd = (pgwd - clcnt - nmwd)/clcnt;
824: pgwd = ((colwd + 1) * clcnt) - nmwd - 2;
825: } else {
826: colwd = (pgwd + 1 - clcnt)/clcnt;
827: pgwd = ((colwd + 1) * clcnt) - 1;
828: }
829: if (colwd < 1) {
830: (void)fprintf(err,
831: "pr: page width too small for %d columns\n", clcnt);
832: return(1);
833: }
834: actf = clcnt;
835: col = colwd + 1;
836:
837: /*
838: * line buffer
839: */
840: if ((buf = malloc((unsigned)(pgwd+offst+1)*sizeof(char))) == NULL) {
841: mfail();
842: return(1);
843: }
844: if (offst) {
845: (void)memset(buf, (int)' ', offst);
846: (void)memset(hbuf, (int)' ', offst);
847: }
848: if (pgnm)
849: pagecnt = pgnm;
850: else
851: pagecnt = 1;
852: lncnt = 0;
853:
854: /*
855: * continue to loop while any file still has data
856: */
857: while (actf > 0) {
858: /*
859: * loop by line
860: */
861: for (i = 0; i < lines; ++i) {
862: ptbf = buf + offst;
863: lstdat = ptbf;
864: if (nmwd) {
865: /*
866: * add line number to line
867: */
868: addnum(ptbf, nmwd, ++lncnt);
869: ptbf += nmwd;
870: *ptbf++ = nmchar;
871: }
872: j = 0;
873: fproc = 0;
874:
875: /*
876: * loop by column
877: */
878: for (j = 0; j < clcnt; ++j) {
879: if (fbuf[j] == NULL) {
880: /*
881: * empty column; EOF
882: */
883: cnt = 0;
884: } else if ((cnt = inln(fbuf[j], ptbf, colwd,
885: &cps, 1, &mor)) < 0) {
886: /*
887: * EOF hit; no data
888: */
889: if (fbuf[j] != stdin)
890: (void)fclose(fbuf[j]);
891: fbuf[j] = NULL;
892: --actf;
893: cnt = 0;
894: } else {
895: /*
896: * process file data
897: */
898: ptbf += cnt;
899: lstdat = ptbf;
900: fproc++;
901: }
902:
903: /*
904: * if last ACTIVE column, done with line
905: */
906: if (fproc >= actf)
907: break;
908:
909: /*
910: * pad to end of column
911: */
912: if (sflag) {
913: *ptbf++ = schar;
914: } else if ((pln = col - cnt) > 0) {
915: (void)memset(ptbf, (int)' ', pln);
916: ptbf += pln;
917: }
918: }
919:
920: /*
921: * calculate data in line
922: */
923: if ((j = lstdat - buf) <= offst)
924: break;
925:
926: if (!i && !nohead && prhead(hbuf, fname, pagecnt))
927: return(1);
928:
929: /*
930: * output line
931: */
932: if (otln(buf, j, &ips, &ops, 0))
933: return(1);
934:
935: /*
936: * if no more active files, done
937: */
938: if (actf <= 0) {
939: ++i;
940: break;
941: }
942: }
943:
944: /*
945: * pad to end of page
946: */
947: if (i && prtail(lines-i, 0))
948: return(1);
949: ++pagecnt;
950: }
951: if (eoptind < argc)
952: return(1);
953: return(0);
954: }
955:
956: /*
957: * inln(): input a line of data (unlimited length lines supported)
958: * Input is optionally expanded to spaces
959: *
960: * inf: file
961: * buf: buffer
962: * lim: buffer length
963: * cps: column positon 1st char in buffer (large line support)
964: * trnc: throw away data more than lim up to \n
965: * mor: set if more data in line (not truncated)
966: */
967: int
968: inln(inf, buf, lim, cps, trnc, mor)
969: FILE *inf;
970: char *buf;
971: register int lim;
972: int *cps;
973: int trnc;
974: int *mor;
975: {
976: register int col;
977: register int gap = ingap;
978: register int ch = EOF;
979: register char *ptbuf;
980: register int chk = (int)inchar;
981:
982: ptbuf = buf;
983:
984: if (gap) {
985: /*
986: * expanding input option
987: */
988: while ((--lim >= 0) && ((ch = getc(inf)) != EOF)) {
989: /*
990: * is this the input "tab" char
991: */
992: if (ch == chk) {
993: /*
994: * expand to number of spaces
995: */
996: col = (ptbuf - buf) + *cps;
997: col = gap - (col % gap);
998:
999: /*
1000: * if more than this line, push back
1001: */
1002: if ((col > lim) && (ungetc(ch, inf) == EOF))
1003: return(1);
1004:
1005: /*
1006: * expand to spaces
1007: */
1008: while ((--col >= 0) && (--lim >= 0))
1009: *ptbuf++ = ' ';
1010: continue;
1011: }
1012: if (ch == '\n')
1013: break;
1014: *ptbuf++ = ch;
1015: }
1016: } else {
1017: /*
1018: * no expansion
1019: */
1020: while ((--lim >= 0) && ((ch = getc(inf)) != EOF)) {
1021: if (ch == '\n')
1022: break;
1023: *ptbuf++ = ch;
1024: }
1025: }
1026: col = ptbuf - buf;
1027: if (ch == EOF) {
1028: *mor = 0;
1029: *cps = 0;
1030: if (!col)
1031: return(-1);
1032: return(col);
1033: }
1034: if (ch == '\n') {
1035: /*
1036: * entire line processed
1037: */
1038: *mor = 0;
1039: *cps = 0;
1040: return(col);
1041: }
1042:
1043: /*
1044: * line was larger than limit
1045: */
1046: if (trnc) {
1047: /*
1048: * throw away rest of line
1049: */
1050: while ((ch = getc(inf)) != EOF) {
1051: if (ch == '\n')
1052: break;
1053: }
1054: *cps = 0;
1055: *mor = 0;
1056: } else {
1057: /*
1058: * save column offset if not truncated
1059: */
1060: *cps += col;
1061: *mor = 1;
1062: }
1063:
1064: return(col);
1065: }
1066:
1067: /*
1068: * otln(): output a line of data. (Supports unlimited length lines)
1069: * output is optionally contracted to tabs
1070: *
1071: * buf: output buffer with data
1072: * cnt: number of chars of valid data in buf
1073: * svips: buffer input column position (for large lines)
1074: * svops: buffer output column position (for large lines)
1075: * mor: output line not complete in this buf; more data to come.
1076: * 1 is more, 0 is complete, -1 is no \n's
1077: */
1078: int
1079: otln(buf, cnt, svips, svops, mor)
1080: register char *buf;
1081: int cnt;
1082: int *svops;
1083: int *svips;
1084: int mor;
1085: {
1086: register int ops; /* last col output */
1087: register int ips; /* last col in buf examined */
1088: register int gap = ogap;
1089: register int tbps;
1090: register char *endbuf;
1091:
1092: if (ogap) {
1093: /*
1094: * contracting on output
1095: */
1096: endbuf = buf + cnt;
1097: ops = *svops;
1098: ips = *svips;
1099: while (buf < endbuf) {
1100: /*
1101: * count number of spaces and ochar in buffer
1102: */
1103: if (*buf == ' ') {
1104: ++ips;
1105: ++buf;
1106: continue;
1107: }
1108:
1109: /*
1110: * simulate ochar processing
1111: */
1112: if (*buf == ochar) {
1113: ips += gap - (ips % gap);
1114: ++buf;
1115: continue;
1116: }
1117:
1118: /*
1119: * got a non space char; contract out spaces
1120: */
1121: while (ops < ips) {
1122: /*
1123: * use as many ochar as will fit
1124: */
1125: if ((tbps = ops + gap - (ops % gap)) > ips)
1126: break;
1127: if (putchar(ochar) == EOF) {
1128: pfail();
1129: return(1);
1130: }
1131: ops = tbps;
1132: }
1133:
1134: while (ops < ips) {
1135: /*
1136: * finish off with spaces
1137: */
1138: if (putchar(' ') == EOF) {
1139: pfail();
1140: return(1);
1141: }
1142: ++ops;
1143: }
1144:
1145: /*
1146: * output non space char
1147: */
1148: if (putchar(*buf++) == EOF) {
1149: pfail();
1150: return(1);
1151: }
1152: ++ips;
1153: ++ops;
1154: }
1155:
1156: if (mor > 0) {
1157: /*
1158: * if incomplete line, save position counts
1159: */
1160: *svops = ops;
1161: *svips = ips;
1162: return(0);
1163: }
1164:
1165: if (mor < 0) {
1166: while (ops < ips) {
1167: /*
1168: * use as many ochar as will fit
1169: */
1170: if ((tbps = ops + gap - (ops % gap)) > ips)
1171: break;
1172: if (putchar(ochar) == EOF) {
1173: pfail();
1174: return(1);
1175: }
1176: ops = tbps;
1177: }
1178: while (ops < ips) {
1179: /*
1180: * finish off with spaces
1181: */
1182: if (putchar(' ') == EOF) {
1183: pfail();
1184: return(1);
1185: }
1186: ++ops;
1187: }
1188: return(0);
1189: }
1190: } else {
1191: /*
1192: * output is not contracted
1193: */
1194: if (cnt && (fwrite(buf, sizeof(char), cnt, stdout) <= 0)) {
1195: pfail();
1196: return(1);
1197: }
1198: if (mor != 0)
1199: return(0);
1200: }
1201:
1202: /*
1203: * process line end and double space as required
1204: */
1205: if ((putchar('\n') == EOF) || (dspace && (putchar('\n') == EOF))) {
1206: pfail();
1207: return(1);
1208: }
1209: return(0);
1210: }
1211:
1212: /*
1213: * inskip(): skip over pgcnt pages with lncnt lines per page
1214: * file is closed at EOF (if not stdin).
1215: *
1216: * inf FILE * to read from
1217: * pgcnt number of pages to skip
1218: * lncnt number of lines per page
1219: */
1220: int
1221: inskip(inf, pgcnt, lncnt)
1222: FILE *inf;
1223: register int pgcnt;
1224: register int lncnt;
1225: {
1226: register int c;
1227: register int cnt;
1228:
1229: while(--pgcnt > 0) {
1230: cnt = lncnt;
1231: while ((c = getc(inf)) != EOF) {
1232: if ((c == '\n') && (--cnt == 0))
1233: break;
1234: }
1235: if (c == EOF) {
1236: if (inf != stdin)
1237: (void)fclose(inf);
1238: return(1);
1239: }
1240: }
1241: return(0);
1242: }
1243:
1244: /*
1245: * nxtfile: returns a FILE * to next file in arg list and sets the
1246: * time field for this file (or current date).
1247: *
1248: * buf array to store proper date for the header.
1249: * dt if set skips the date processing (used with -m)
1250: */
1251: FILE *
1252: nxtfile(argc, argv, fname, buf, dt)
1253: int argc;
1254: char **argv;
1255: char **fname;
1256: char *buf;
1257: int dt;
1258: {
1259: FILE *inf = NULL;
1260: struct timeval tv;
1261: struct timezone tz;
1262: struct tm *timeptr = NULL;
1263: struct stat statbuf;
1264: time_t curtime;
1265: static int twice = -1;
1266:
1267: ++twice;
1268: if (eoptind >= argc) {
1269: /*
1270: * no file listed; default, use standard input
1271: */
1272: if (twice)
1273: return(NULL);
1274: clearerr(stdin);
1275: inf = stdin;
1276: if (header != NULL)
1277: *fname = header;
1278: else
1279: *fname = FNAME;
1280: if (nohead)
1281: return(inf);
1282: if (gettimeofday(&tv, &tz) < 0) {
1283: ++errcnt;
1284: (void)fprintf(err, "pr: cannot get time of day, %s\n",
1285: strerror(errno));
1286: eoptind = argc - 1;
1287: return(NULL);
1288: }
1289: curtime = tv.tv_sec;
1290: timeptr = localtime(&curtime);
1291: }
1292: for (; eoptind < argc; ++eoptind) {
1293: if (strcmp(argv[eoptind], "-") == 0) {
1294: /*
1295: * process a "-" for filename
1296: */
1297: clearerr(stdin);
1298: inf = stdin;
1299: if (header != NULL)
1300: *fname = header;
1301: else
1302: *fname = FNAME;
1303: ++eoptind;
1304: if (nohead || (dt && twice))
1305: return(inf);
1306: if (gettimeofday(&tv, &tz) < 0) {
1307: ++errcnt;
1308: (void)fprintf(err,
1309: "pr: cannot get time of day, %s\n",
1310: strerror(errno));
1311: return(NULL);
1312: }
1313: curtime = tv.tv_sec;
1314: timeptr = localtime(&curtime);
1315: } else {
1316: /*
1317: * normal file processing
1318: */
1319: if ((inf = fopen(argv[eoptind], "r")) == NULL) {
1320: ++errcnt;
1321: if (nodiag)
1322: continue;
1323: (void)fprintf(err, "pr: Cannot open %s, %s\n",
1324: argv[eoptind], strerror(errno));
1325: continue;
1326: }
1327: if (header != NULL)
1328: *fname = header;
1329: else if (dt)
1330: *fname = FNAME;
1331: else
1332: *fname = argv[eoptind];
1333: ++eoptind;
1334: if (nohead || (dt && twice))
1335: return(inf);
1336:
1337: if (dt) {
1338: if (gettimeofday(&tv, &tz) < 0) {
1339: ++errcnt;
1340: (void)fprintf(err,
1341: "pr: cannot get time of day, %s\n",
1342: strerror(errno));
1343: return(NULL);
1344: }
1345: curtime = tv.tv_sec;
1346: timeptr = localtime(&curtime);
1347: } else {
1348: if (fstat(fileno(inf), &statbuf) < 0) {
1349: ++errcnt;
1350: (void)fclose(inf);
1351: (void)fprintf(err,
1352: "pr: Cannot stat %s, %s\n",
1353: argv[eoptind], strerror(errno));
1354: return(NULL);
1355: }
1356: timeptr = localtime(&(statbuf.st_mtime));
1357: }
1358: }
1359: break;
1360: }
1361: if (inf == NULL)
1362: return(NULL);
1363:
1364: /*
1365: * set up time field used in header
1366: */
1367: if (strftime(buf, HDBUF, timefrmt, timeptr) <= 0) {
1368: ++errcnt;
1369: if (inf != stdin)
1370: (void)fclose(inf);
1371: (void)fputs("pr: time conversion failed\n", err);
1372: return(NULL);
1373: }
1374: return(inf);
1375: }
1376:
1377: /*
1378: * addnum(): adds the line number to the column
1379: * Truncates from the front or pads with spaces as required.
1380: * Numbers are right justified.
1381: *
1382: * buf buffer to store the number
1383: * wdth width of buffer to fill
1384: * line line number
1385: *
1386: * NOTE: numbers occupy part of the column. The posix
1387: * spec does not specify if -i processing should or should not
1388: * occur on number padding. The spec does say it occupies
1389: * part of the column. The usage of addnum currently treats
1390: * numbers as part of the column so spaces may be replaced.
1391: */
1392: void
1393: addnum(buf, wdth, line)
1394: register char *buf;
1395: register int wdth;
1396: register int line;
1397: {
1398: register char *pt = buf + wdth;
1399:
1400: do {
1401: *--pt = digs[line % 10];
1402: line /= 10;
1403: } while (line && (pt > buf));
1404:
1405: /*
1406: * pad with space as required
1407: */
1408: while (pt > buf)
1409: *--pt = ' ';
1410: }
1411:
1412: /*
1413: * prhead(): prints the top of page header
1414: *
1415: * buf buffer with time field (and offset)
1416: * cnt number of chars in buf
1417: * fname fname field for header
1418: * pagcnt page number
1419: */
1420: int
1421: prhead(buf, fname, pagcnt)
1422: char *buf;
1423: char *fname;
1424: int pagcnt;
1425: {
1426: int ips = 0;
1427: int ops = 0;
1428:
1429: if ((putchar('\n') == EOF) || (putchar('\n') == EOF)) {
1430: pfail();
1431: return(1);
1432: }
1433: /*
1434: * posix is not clear if the header is subject to line length
1435: * restrictions. The specification for header line format
1436: * in the spec clearly does not limit length. No pr currently
1437: * restricts header length. However if we need to truncate in
1438: * an reasonable way, adjust the length of the printf by
1439: * changing HDFMT to allow a length max as an arguement printf.
1440: * buf (which contains the offset spaces and time field could
1441: * also be trimmed
1442: *
1443: * note only the offset (if any) is processed for tab expansion
1444: */
1445: if (offst && otln(buf, offst, &ips, &ops, -1))
1446: return(1);
1447: (void)printf(HDFMT,buf+offst, fname, pagcnt);
1448: return(0);
1449: }
1450:
1451: /*
1452: * prtail(): pad page with empty lines (if required) and print page trailer
1453: * if requested
1454: *
1455: * cnt number of lines of padding needed
1456: * incomp was a '\n' missing from last line output
1457: */
1458: int
1459: prtail(cnt, incomp)
1460: register int cnt;
1461: int incomp;
1462: {
1463: if (nohead) {
1464: /*
1465: * only pad with no headers when incomplete last line
1466: */
1467: if (!incomp)
1468: return(0);
1469: if ((dspace && (putchar('\n') == EOF)) ||
1470: (putchar('\n') == EOF)) {
1471: pfail();
1472: return(1);
1473: }
1474: return(0);
1475: }
1476:
1477: /*
1478: * if double space output two \n
1479: */
1480: if (dspace)
1481: cnt *= 2;
1482:
1483: /*
1484: * if an odd number of lines per page, add an extra \n
1485: */
1486: if (addone)
1487: ++cnt;
1488:
1489: /*
1490: * pad page
1491: */
1492: if (formfeed) {
1493: if ((incomp && (putchar('\n') == EOF)) ||
1494: (putchar('\f') == EOF)) {
1495: pfail();
1496: return(1);
1497: }
1498: return(0);
1499: }
1500: cnt += TAILLEN;
1501: while (--cnt >= 0) {
1502: if (putchar('\n') == EOF) {
1503: pfail();
1504: return(1);
1505: }
1506: }
1507: return(0);
1508: }
1509:
1510: /*
1511: * terminate(): when a SIGINT is recvd
1512: */
1513: void
1514: terminate(which_sig)
1515: int which_sig;
1516: {
1517: flsh_errs();
1518: exit(1);
1519: }
1520:
1521:
1522: /*
1523: * flsh_errs(): output saved up diagnostic messages after all normal
1524: * processing has completed
1525: */
1526: void
1527: flsh_errs()
1528: {
1529: char buf[BUFSIZ];
1530:
1531: (void)fflush(stdout);
1532: (void)fflush(err);
1533: if (err == stderr)
1534: return;
1535: rewind(err);
1536: while (fgets(buf, BUFSIZ, err) != NULL)
1537: (void)fputs(buf, stderr);
1538: }
1539:
1540: void
1541: mfail()
1542: {
1543: (void)fputs("pr: memory allocation failed\n", err);
1544: }
1545:
1546: void
1547: pfail()
1548: {
1549: (void)fprintf(err, "pr: write failure, %s\n", strerror(errno));
1550: }
1551:
1552: void
1553: usage()
1554: {
1555: (void)fputs(
1556: "usage: pr [+page] [-col] [-adFmrt] [-e[ch][gap]] [-h header]\n",err);
1557: (void)fputs(
1558: " [-i[ch][gap]] [-l line] [-n[ch][width]] [-o offset]\n",err);
1559: (void)fputs(
1560: " [-s[ch]] [-w width] [-] [file ...]\n", err);
1561: }
1562:
1563: /*
1564: * setup: Validate command args, initialize and perform sanity
1565: * checks on options
1566: */
1567: int
1568: setup(argc, argv)
1569: register int argc;
1570: register char **argv;
1571: {
1572: register int c;
1573: int eflag = 0;
1574: int iflag = 0;
1575: int wflag = 0;
1576: int cflag = 0;
1577:
1578: if (isatty(fileno(stdout))) {
1579: /*
1580: * defer diagnostics until processing is done
1581: */
1582: if ((err = tmpfile()) == NULL) {
1583: (void)fputs("Cannot defer diagnostic messages\n",stderr);
1584: return(1);
1585: }
1586: } else
1587: err = stderr;
1588: while ((c = egetopt(argc, argv, "#adFmrte?h:i?l:n?o:s?w:")) != EOF) {
1589: switch (c) {
1590: case '+':
1591: if ((pgnm = atoi(eoptarg)) < 1) {
1592: (void)fputs("pr: +page number must be 1 or more\n",
1593: err);
1594: return(1);
1595: }
1596: break;
1597: case '-':
1598: if ((clcnt = atoi(eoptarg)) < 1) {
1599: (void)fputs("pr: -columns must be 1 or more\n",err);
1600: return(1);
1601: }
1602: if (clcnt > 1)
1603: ++cflag;
1604: break;
1605: case 'a':
1606: ++across;
1607: break;
1608: case 'd':
1609: ++dspace;
1610: break;
1611: case 'e':
1612: ++eflag;
1613: if ((eoptarg != NULL) && !isdigit(*eoptarg))
1614: inchar = *eoptarg++;
1615: else
1616: inchar = INCHAR;
1617: if ((eoptarg != NULL) && isdigit(*eoptarg)) {
1618: if ((ingap = atoi(eoptarg)) < 0) {
1619: (void)fputs(
1620: "pr: -e gap must be 0 or more\n", err);
1621: return(1);
1622: }
1623: if (ingap == 0)
1624: ingap = INGAP;
1625: } else if ((eoptarg != NULL) && (*eoptarg != '\0')) {
1626: (void)fprintf(err,
1627: "pr: invalid value for -e %s\n", eoptarg);
1628: return(1);
1629: } else
1630: ingap = INGAP;
1631: break;
1632: case 'F':
1633: ++formfeed;
1634: break;
1635: case 'h':
1636: header = eoptarg;
1637: break;
1638: case 'i':
1639: ++iflag;
1640: if ((eoptarg != NULL) && !isdigit(*eoptarg))
1641: ochar = *eoptarg++;
1642: else
1643: ochar = OCHAR;
1644: if ((eoptarg != NULL) && isdigit(*eoptarg)) {
1645: if ((ogap = atoi(eoptarg)) < 0) {
1646: (void)fputs(
1647: "pr: -i gap must be 0 or more\n", err);
1648: return(1);
1649: }
1650: if (ogap == 0)
1651: ogap = OGAP;
1652: } else if ((eoptarg != NULL) && (*eoptarg != '\0')) {
1653: (void)fprintf(err,
1654: "pr: invalid value for -i %s\n", eoptarg);
1655: return(1);
1656: } else
1657: ogap = OGAP;
1658: break;
1659: case 'l':
1660: if (!isdigit(*eoptarg) || ((lines=atoi(eoptarg)) < 1)) {
1661: (void)fputs(
1662: "pr: Number of lines must be 1 or more\n",err);
1663: return(1);
1664: }
1665: break;
1666: case 'm':
1667: ++merge;
1668: break;
1669: case 'n':
1670: if ((eoptarg != NULL) && !isdigit(*eoptarg))
1671: nmchar = *eoptarg++;
1672: else
1673: nmchar = NMCHAR;
1674: if ((eoptarg != NULL) && isdigit(*eoptarg)) {
1675: if ((nmwd = atoi(eoptarg)) < 1) {
1676: (void)fputs(
1677: "pr: -n width must be 1 or more\n",err);
1678: return(1);
1679: }
1680: } else if ((eoptarg != NULL) && (*eoptarg != '\0')) {
1681: (void)fprintf(err,
1682: "pr: invalid value for -n %s\n", eoptarg);
1683: return(1);
1684: } else
1685: nmwd = NMWD;
1686: break;
1687: case 'o':
1688: if (!isdigit(*eoptarg) || ((offst = atoi(eoptarg))< 1)){
1689: (void)fputs("pr: -o offset must be 1 or more\n",
1690: err);
1691: return(1);
1692: }
1693: break;
1694: case 'r':
1695: ++nodiag;
1696: break;
1697: case 's':
1698: ++sflag;
1699: if (eoptarg == NULL)
1700: schar = SCHAR;
1701: else
1702: schar = *eoptarg++;
1703: if (*eoptarg != '\0') {
1704: (void)fprintf(err,
1705: "pr: invalid value for -s %s\n", eoptarg);
1706: return(1);
1707: }
1708: break;
1709: case 't':
1710: ++nohead;
1711: break;
1712: case 'w':
1713: ++wflag;
1714: if (!isdigit(*eoptarg) || ((pgwd = atoi(eoptarg)) < 1)){
1715: (void)fputs(
1716: "pr: -w width must be 1 or more \n",err);
1717: return(1);
1718: }
1719: break;
1720: case '?':
1721: default:
1722: return(1);
1723: }
1724: }
1725:
1726: /*
1727: * default and sanity checks
1728: */
1729: if (!clcnt) {
1730: if (merge) {
1731: if ((clcnt = argc - eoptind) <= 1) {
1732: clcnt = CLCNT;
1733: merge = 0;
1734: }
1735: } else
1736: clcnt = CLCNT;
1737: }
1738: if (across) {
1739: if (clcnt == 1) {
1740: (void)fputs("pr: -a flag requires multiple columns\n",
1741: err);
1742: return(1);
1743: }
1744: if (merge) {
1745: (void)fputs("pr: -m cannot be used with -a\n", err);
1746: return(1);
1747: }
1748: }
1749: if (!wflag) {
1750: if (sflag)
1751: pgwd = SPGWD;
1752: else
1753: pgwd = PGWD;
1754: }
1755: if (cflag || merge) {
1756: if (!eflag) {
1757: inchar = INCHAR;
1758: ingap = INGAP;
1759: }
1760: if (!iflag) {
1761: ochar = OCHAR;
1762: ogap = OGAP;
1763: }
1764: }
1765: if (cflag) {
1766: if (merge) {
1767: (void)fputs(
1768: "pr: -m cannot be used with multiple columns\n", err);
1769: return(1);
1770: }
1771: if (nmwd) {
1772: colwd = (pgwd + 1 - (clcnt * (nmwd + 2)))/clcnt;
1773: pgwd = ((colwd + nmwd + 2) * clcnt) - 1;
1774: } else {
1775: colwd = (pgwd + 1 - clcnt)/clcnt;
1776: pgwd = ((colwd + 1) * clcnt) - 1;
1777: }
1778: if (colwd < 1) {
1779: (void)fprintf(err,
1780: "pr: page width is too small for %d columns\n",clcnt);
1781: return(1);
1782: }
1783: }
1784: if (!lines)
1785: lines = LINES;
1786:
1787: /*
1788: * make sure long enough for headers. if not disable
1789: */
1790: if (lines <= HEADLEN + TAILLEN)
1791: ++nohead;
1792: else if (!nohead)
1793: lines -= HEADLEN + TAILLEN;
1794:
1795: /*
1796: * adjust for double space on odd length pages
1797: */
1798: if (dspace) {
1799: if (lines == 1)
1800: dspace = 0;
1801: else {
1802: if (lines & 1)
1803: ++addone;
1804: lines /= 2;
1805: }
1806: }
1807:
1808: if ((timefrmt = getenv("LC_TIME")) == NULL)
1809: timefrmt = TIMEFMT;
1810: return(0);
1811: }