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