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