xref: /freebsd/usr.bin/pr/pr.c (revision 22cf89c938886d14f5796fc49f9f020c23ea8eaf)
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
41 static 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
48 static char sccsid[] = "@(#)pr.c	8.2 (Berkeley) 4/16/94";
49 #endif /* not lint */
50 #endif
51 
52 #include <sys/cdefs.h>
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  */
83 static int	pgnm;		/* starting page number */
84 static int	clcnt;		/* number of columns */
85 static int	colwd;		/* column data width - multiple columns */
86 static int	across;		/* mult col flag; write across page */
87 static int	dspace;		/* double space flag */
88 static char	inchar;		/* expand input char */
89 static int	ingap;		/* expand input gap */
90 static int	pausefst;	/* Pause before first page */
91 static int	pauseall;	/* Pause before each page */
92 static int	formfeed;	/* use formfeed as trailer */
93 static char	*header;	/* header name instead of file name */
94 static char	ochar;		/* contract output char */
95 static int	ogap;		/* contract output gap */
96 static int	lines;		/* number of lines per page */
97 static int	merge;		/* merge multiple files in output */
98 static char	nmchar;		/* line numbering append char */
99 static int	nmwd;		/* width of line number field */
100 static int	offst;		/* number of page offset spaces */
101 static int	nodiag;		/* do not report file open errors */
102 static char	schar;		/* text column separation character */
103 static int	sflag;		/* -s option for multiple columns */
104 static int	nohead;		/* do not write head and trailer */
105 static int	pgwd;		/* page width with multiple col output */
106 static char	*timefrmt;	/* time conversion string */
107 
108 /*
109  * misc globals
110  */
111 static FILE	*err;		/* error message file pointer */
112 static int	addone;		/* page length is odd with double space */
113 static int	errcnt;		/* error count on file processing */
114 static char	digs[] = "0123456789"; /* page number translation map */
115 
116 static char	fnamedefault[] = FNAME;
117 
118 int
119 main(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  */
151 static void
152 ttypause(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  */
172 int
173 onecol(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);
315 err:
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  */
324 int
325 vertcol(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;
642 out:
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  */
655 int
656 horzcol(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);
795 err:
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  */
805 int
806 mulfile(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;
1007 out:
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  */
1025 int
1026 inln(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  */
1130 int
1131 otln(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  */
1267 int
1268 inskip(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  */
1295 FILE *
1296 nxtfile(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  */
1427 void
1428 addnum(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  */
1452 int
1453 prhead(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  */
1487 int
1488 prtail(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  */
1547 void
1548 terminate(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  */
1559 void
1560 flsh_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 
1573 void
1574 mfail(void)
1575 {
1576 	(void)fputs("pr: memory allocation failed\n", err);
1577 }
1578 
1579 void
1580 pfail(void)
1581 {
1582 	(void)fprintf(err, "pr: write failure, %s\n", strerror(errno));
1583 }
1584 
1585 void
1586 usage(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  */
1601 int
1602 setup(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