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