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