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