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