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