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