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