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