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