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