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