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