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