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