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