xref: /dragonfly/usr.bin/pr/pr.c (revision 984263bc)
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: src/usr.bin/pr/pr.c,v 1.9.2.4 2002/04/15 17:16:57 jmallett Exp $
38  */
39 
40 #ifndef lint
41 static 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
47 static 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  */
80 int	pgnm;			/* starting page number */
81 int	clcnt;			/* number of columns */
82 int	colwd;			/* column data width - multiple columns */
83 int	across;			/* mult col flag; write across page */
84 int	dspace;			/* double space flag */
85 char	inchar;			/* expand input char */
86 int	ingap;			/* expand input gap */
87 int	pausefst;		/* Pause before first page */
88 int	pauseall;		/* Pause before each page */
89 int	formfeed;		/* use formfeed as trailer */
90 char	*header;		/* header name instead of file name */
91 char	ochar;			/* contract output char */
92 int	ogap;			/* contract output gap */
93 int	lines;			/* number of lines per page */
94 int	merge;			/* merge multiple files in output */
95 char	nmchar;			/* line numbering append char */
96 int	nmwd;			/* width of line number field */
97 int	offst;			/* number of page offset spaces */
98 int	nodiag;			/* do not report file open errors */
99 char	schar;			/* text column separation character */
100 int	sflag;			/* -s option for multiple columns */
101 int	nohead;			/* do not write head and trailer */
102 int	pgwd;			/* page width with multiple col output */
103 char	*timefrmt;		/* time conversion string */
104 
105 /*
106  * misc globals
107  */
108 FILE	*err;			/* error message file pointer */
109 int	addone;			/* page length is odd with double space */
110 int	errcnt;			/* error count on file processing */
111 char	digs[] = "0123456789";	/* page number translation map */
112 
113 int
114 main(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  */
147 void
148 ttypause(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  */
169 int
170 onecol(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  */
316 int
317 vertcol(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  */
639 int
640 horzcol(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  */
784 int
785 mulfile(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  */
1000 int
1001 inln(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  */
1111 int
1112 otln(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 (ops < ips) {
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 (ops < ips) {
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  */
1253 int
1254 inskip(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  */
1284 FILE *
1285 nxtfile(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  */
1425 void
1426 addnum(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  */
1453 int
1454 prhead(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  */
1491 int
1492 prtail(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  */
1553 void
1554 terminate(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  */
1566 void
1567 flsh_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 
1580 void
1581 mfail()
1582 {
1583 	(void)fputs("pr: memory allocation failed\n", err);
1584 }
1585 
1586 void
1587 pfail()
1588 {
1589 	(void)fprintf(err, "pr: write failure, %s\n", strerror(errno));
1590 }
1591 
1592 void
1593 usage()
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  */
1608 int
1609 setup(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