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