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