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