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