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