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
39static 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
46static 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 */
74extern 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 */
92int	pgnm;			/* starting page number */
93int	clcnt;			/* number of columns */
94int	colwd;			/* column data width - multiple columns */
95int	across;			/* mult col flag; write across page */
96int	dspace;			/* double space flag */
97char	inchar;			/* expand input char */
98int	ingap;			/* expand input gap */
99int	pausefst;		/* Pause before first page */
100int	pauseall;		/* Pause before each page */
101int	formfeed;		/* use formfeed as trailer */
102char	*header;		/* header name instead of file name */
103char	ochar;			/* contract output char */
104int	ogap;			/* contract output gap */
105int	lines;			/* number of lines per page */
106int	merge;			/* merge multiple files in output */
107char	nmchar;			/* line numbering append char */
108int	nmwd;			/* width of line number field */
109int	offst;			/* number of page offset spaces */
110int	nodiag;			/* do not report file open errors */
111char	schar;			/* text column separation character */
112int	sflag;			/* -s option for multiple columns */
113int	nohead;			/* do not write head and trailer */
114int	pgwd;			/* page width with multiple col output */
115const char *timefrmt;		/* time conversion string */
116
117/*
118 * misc globals
119 */
120FILE	*err;			/* error message file pointer */
121int	addone;			/* page length is odd with double space */
122int	errcnt;			/* error count on file processing */
123char	digs[] = "0123456789";	/* page number translation map */
124
125char	fnamedefault[] = FNAME;
126
127static char first_char;	/* first fill character */
128
129int
130main(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 */
162static void
163ttypause(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 */
183int
184onecol(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 */
328int
329vertcol(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 */
649int
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;
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 */
792int
793mulfile(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 */
1006int
1007inln(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 */
1117int
1118otln(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 */
1256int
1257inskip(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 */
1287FILE *
1288nxtfile(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 */
1423void
1424addnum(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 */
1448int
1449prhead(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 */
1483int
1484prtail(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 */
1543void
1544terminate(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 */
1555void
1556flsh_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
1569void
1570mfail(void)
1571{
1572	(void)fputs("pr: memory allocation failed\n", err);
1573}
1574
1575void
1576pfail(void)
1577{
1578	(void)fprintf(err, "pr: write failure, %s\n", strerror(errno));
1579}
1580
1581void
1582usage(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 */
1597int
1598setup(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}
1857