xref: /openbsd-src/usr.bin/pr/pr.c (revision 4c1e55dc91edd6e69ccc60ce855900fbc12cf34f)
1 /*	$OpenBSD: pr.c,v 1.30 2010/08/25 18:20:11 chl Exp $	*/
2 
3 /*-
4  * Copyright (c) 1991 Keith Muller.
5  * Copyright (c) 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Keith Muller of the University of California, San Diego.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #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
848 ferrout(char *fmt, ...)
849 {
850     sigset_t block, oblock;
851     struct ferrlist *f;
852     va_list ap;
853     char *p;
854 
855     va_start(ap, fmt);
856     if (ferr == 0)
857         vfprintf(stderr, fmt, ap);
858     else {
859 	sigemptyset(&block);
860 	sigaddset(&block, SIGINT);
861 	sigprocmask(SIG_BLOCK, &block, &oblock);
862 
863 	if (vasprintf(&p, fmt, ap) == -1 || (f = malloc(sizeof(*f))) == NULL) {
864 		flsh_errs();
865 		fprintf(stderr, fmt, ap);
866 		fputs("pr: memory allocation failed\n", stderr);
867 		exit(1);
868 	}
869 
870 	f->next = NULL;
871 	f->buf = p;
872 	if (ferrhead == NULL)
873 	    ferrhead = f;
874 	if (ferrtail)
875 		ferrtail->next = f;
876 	ferrtail = f;
877 	sigprocmask(SIG_SETMASK, &oblock, NULL);
878     }
879     va_end(ap);
880 }
881 
882 /*
883  * mulfile:    print files with more than one column of output and
884  *        more than one file concurrently
885  */
886 int
887 mulfile(int argc, char *argv[])
888 {
889     char *ptbf;
890     int j;
891     int pln;
892     int *rc;
893     int cnt;
894     char *lstdat;
895     int i;
896     FILE **fbuf = NULL;
897     int actf;
898     int lncnt;
899     int col;
900     int pagecnt;
901     int fproc;
902     char *buf = NULL;
903     char *hbuf = NULL;
904     char *ohbuf;
905     char *fname;
906     int ips = 0;
907     int cps = 0;
908     int ops = 0;
909     int mor = 0;
910     int error = 1;
911 
912     /*
913      * array of FILE *, one for each operand
914      */
915     if ((fbuf = (FILE **)calloc((unsigned)clcnt, sizeof(FILE *))) == NULL)
916 	goto oomem;
917 
918     /*
919      * array of int *, one for each operand
920      */
921     if ((rc = (int *)calloc((unsigned)clcnt, sizeof(int))) == NULL)
922 	goto oomem;
923 
924     /*
925      * page header
926      */
927     if ((hbuf = malloc((unsigned)HDBUF + offst)) == NULL)
928 	goto oomem;
929 
930     ohbuf = hbuf + offst;
931 
932     /*
933      * do not know how many columns yet. The number of operands provide an
934      * upper bound on the number of columns. We use the number of files
935      * we can open successfully to set the number of columns. The operation
936      * of the merge operation (-m) in relation to unsuccessful file opens
937      * is unspecified by posix.
938      *
939      * XXX - this seems moderately bogus, you'd think that specifying
940      * "pr -2 a b c d" would run though all the files in pairs, but
941      * the existing code says up two files, or fewer if one is bogus.
942      * fixing it would require modifying the looping structure, so be it.
943      */
944     j = 0;
945     while (j < clcnt) {
946 	if ((fbuf[j] = nxtfile(argc, argv, &fname, ohbuf, 1)) != NULL) {
947 	    rc[j] = NORMAL;
948 	    j++;
949 	}
950     }
951 
952     /*
953      * if no files, exit
954      */
955     if (j)
956 	clcnt = j;
957     else
958 	goto out;
959 
960     /*
961      * calculate page boundaries based on open file count
962      */
963     if (nmwd) {
964 	colwd = (pgwd - clcnt - nmwd)/clcnt;
965 	pgwd = ((colwd + 1) * clcnt) - nmwd - 2;
966     } else {
967 	colwd = (pgwd + 1 - clcnt)/clcnt;
968 	pgwd = ((colwd + 1) * clcnt) - 1;
969     }
970     if (colwd < 1) {
971 	ferrout("pr: page width too small for %d columns\n", clcnt);
972 	goto out;
973     }
974     col = colwd + 1;
975 
976     /*
977      * line buffer
978      */
979     if ((buf = malloc((unsigned)pgwd + offst + 1)) == NULL)
980 	goto oomem;
981 
982     if (offst) {
983 	(void)memset(buf, (int)' ', offst);
984 	(void)memset(hbuf, (int)' ', offst);
985     }
986 
987     pagecnt = 0;
988     lncnt = 0;
989     actf = clcnt;
990 
991     /*
992      * continue to loop while any file still has data
993      */
994     while (actf > 0) {
995 
996 	/*
997 	 * loop on "form"
998 	 */
999 	for (;;) {
1000 
1001 	    /*
1002 	     * loop by line
1003 	     */
1004 	    for (i = 0; i < lines; ++i) {
1005 		ptbf = buf + offst;
1006 		lstdat = ptbf;
1007 		if (nmwd) {
1008 		    /*
1009 		     * add line number to line
1010 		     */
1011 		    addnum(ptbf, nmwd, ++lncnt);
1012 		    ptbf += nmwd;
1013 		    *ptbf++ = nmchar;
1014 		}
1015 
1016 		fproc = 0;
1017 		/*
1018 		 * loop by column
1019 		 */
1020 		for (j = 0; j < clcnt; ++j) {
1021 		    if (rc[j] == NORMAL ) {
1022 			rc[j] = inln(fbuf[j], ptbf, colwd, &cnt, &cps, 1, &mor);
1023 			if (cnt >= 0) {
1024 			    /*
1025 			     * process file data
1026 			     */
1027 			    ptbf += cnt;
1028 			    lstdat = ptbf;
1029 			    fproc++;
1030 			} else
1031 			    cnt = 0;
1032 
1033 			if (rc[j] == END) {
1034 			    /*
1035 			     * EOF close file
1036 			     */
1037 			    if (fbuf[j] != stdin)
1038 				(void)fclose(fbuf[j]);
1039 			    --actf;
1040 			}
1041 		    } else
1042 			cnt = 0;
1043 
1044 		    /*
1045 		     * if last ACTIVE column, done with line
1046 		     */
1047 		    if (fproc >= actf)
1048 			break;
1049 
1050 		    /*
1051 		     * pad to end of column
1052 		     */
1053 		    if (sflag) {
1054 			*ptbf++ = schar;
1055 		    } else {
1056 			if (cnt >= 0)
1057 			    pln = col - cnt;
1058 			else
1059 			    pln = col;
1060 			if (pln > 0) {
1061 			    (void)memset(ptbf, (int)' ', pln);
1062 			    ptbf += pln;
1063 			}
1064 		    }
1065 		}
1066 
1067 		/*
1068 		 * if there was anything to do, print it
1069 		 */
1070 		if (fproc != 0) {
1071 		    if (!i && prhead(hbuf, fname, ++pagecnt))
1072 			goto out;
1073 
1074 		    /*
1075 		     * output line
1076 		     */
1077 		    if (otln(buf, lstdat-buf, &ips, &ops, 0))
1078 			goto out;
1079 		} else
1080 		    break;
1081 	    }
1082 
1083 	    /*
1084 	     * pad to end of page
1085 	     */
1086 	    if (prtail(lines - i, 0))
1087 		return(1);
1088 
1089 	    for (j = 0; j < clcnt; ++j)
1090 		if (rc[j] != END)
1091 		    rc[j] = NORMAL;
1092 
1093 	    if (actf <= 0)
1094 		break;
1095 	}
1096 	if (actf <= 0)
1097 	break;
1098     }
1099     if (eoptind < argc){
1100 	goto out;
1101     } else {
1102 	error = 0;
1103 	goto out;
1104     }
1105 
1106 oomem:
1107 	mfail();
1108 out:
1109     if (fbuf) {
1110 	for (j = 0; j < clcnt; j++) {
1111 	    if (fbuf[j] && fbuf[j] != stdin)
1112 		(void)fclose(fbuf[j]);
1113 	}
1114 	free(fbuf);
1115     }
1116     free(hbuf);
1117     free(buf);
1118     return error;
1119 }
1120 
1121 /*
1122  * inln():    input a line of data (unlimited length lines supported)
1123  *        Input is optionally expanded to spaces
1124  *        Returns 0 if normal LF, FORM on Formfeed, and END on EOF
1125  *
1126  *    inf:    file
1127  *    buf:    buffer
1128  *    lim:    buffer length
1129  *    cnt:    line length or -1 if no line (EOF for example)
1130  *    cps:    column position 1st char in buffer (large line support)
1131  *    trnc:    throw away data more than lim up to \n
1132  *    mor:    set if more data in line (not truncated)
1133  */
1134 int
1135 inln(FILE *inf, char *buf, int lim, int *cnt, int *cps, int trnc, int *mor)
1136 {
1137     int col;
1138     int gap = ingap;
1139     int ch = -1;
1140     char *ptbuf;
1141     int chk = (int)inchar;
1142 
1143     ptbuf = buf;
1144 
1145     if (gap) {
1146 	/*
1147 	 * expanding input option
1148 	 */
1149 	while ((--lim >= 0) && ((ch = getc(inf)) != EOF)) {
1150 	    /*
1151 	     * is this the input "tab" char
1152 	     */
1153 	    if (ch == chk) {
1154 		/*
1155 		 * expand to number of spaces
1156 		 */
1157 		col = (ptbuf - buf) + *cps;
1158 		col = gap - (col % gap);
1159 
1160 		/*
1161 		 * if more than this line, push back
1162 		 */
1163 		if ((col > lim) && (ungetc(ch, inf) == EOF)) {
1164 		    *cnt = -1;
1165 		    return(END);    /* shouldn't happen */
1166 		}
1167 
1168 		/*
1169 		 * expand to spaces
1170 		 */
1171 		while ((--col >= 0) && (--lim >= 0))
1172 		    *ptbuf++ = ' ';
1173 		continue;
1174 	    }
1175 	    if (ch == '\n' || (inform && ch == INFF))
1176 		break;
1177 	    *ptbuf++ = ch;
1178 	}
1179     } else {
1180 	/*
1181 	 * no expansion
1182 	 */
1183 	while ((--lim >= 0) && ((ch = getc(inf)) != EOF)) {
1184 	    if (ch == '\n' || (inform && ch == INFF))
1185 		break;
1186 	    *ptbuf++ = ch;
1187 	}
1188     }
1189     col = ptbuf - buf;
1190     if (ch == EOF) {
1191 	*mor = 0;
1192 	*cps = 0;
1193 	*cnt = col ? col : -1;
1194 	return(END);
1195     }
1196     if (inform && ch == INFF) {
1197 	*mor = 0;
1198 	*cps = 0;
1199 	*cnt = col;
1200 	return(FORM);
1201     }
1202     if (ch == '\n') {
1203 	/*
1204 	 * entire line processed
1205 	 */
1206 	*mor = 0;
1207 	*cps = 0;
1208 	*cnt = col;
1209 	return(NORMAL);
1210     }
1211 
1212     /*
1213      * line was larger than limit
1214      */
1215     if (trnc) {
1216 	/*
1217 	 * throw away rest of line
1218 	 */
1219 	while ((ch = getc(inf)) != EOF) {
1220 	    if (ch == '\n')
1221 		break;
1222 	}
1223 	*cps = 0;
1224 	*mor = 0;
1225     } else {
1226 	/*
1227 	 * save column offset if not truncated
1228 	 */
1229 	*cps += col;
1230 	*mor = 1;
1231     }
1232 
1233     *cnt = col;
1234     return(NORMAL);
1235 }
1236 
1237 /*
1238  * otln():    output a line of data. (Supports unlimited length lines)
1239  *        output is optionally contracted to tabs
1240  *
1241  *    buf:    output buffer with data
1242  *    cnt:    number of chars of valid data in buf
1243  *    svips:    buffer input column position (for large lines)
1244  *    svops:    buffer output column position (for large lines)
1245  *    mor:    output line not complete in this buf; more data to come.
1246  *        1 is more, 0 is complete, -1 is no \n's
1247  */
1248 int
1249 otln(char *buf, int cnt, int *svips, int *svops, int mor)
1250 {
1251     int ops;        /* last col output */
1252     int ips;        /* last col in buf examined */
1253     int gap = ogap;
1254     int tbps;
1255     char *endbuf;
1256 
1257     /* skipping is only changed at header time not mid-line! */
1258     if (skipping)
1259 	return (0);
1260 
1261     if (ogap) {
1262 	/*
1263 	 * contracting on output
1264 	 */
1265 	endbuf = buf + cnt;
1266 	ops = *svops;
1267 	ips = *svips;
1268 	while (buf < endbuf) {
1269 	    /*
1270 	     * count number of spaces and ochar in buffer
1271 	     */
1272 	    if (*buf == ' ') {
1273 		++ips;
1274 		++buf;
1275 		continue;
1276 	    }
1277 
1278 	    /*
1279 	     * simulate ochar processing
1280 	     */
1281 	    if (*buf == ochar) {
1282 		ips += gap - (ips % gap);
1283 		++buf;
1284 		continue;
1285 	    }
1286 
1287 	    /*
1288 	     * got a non space char; contract out spaces
1289 	     */
1290 	    while (ops < ips) {
1291 		/*
1292 		 * use one space if necessary
1293 		 */
1294 		if (ips - ops == 1) {
1295 			putchar(' ');
1296 			break;
1297 		}
1298 		/*
1299 		 * use as many ochar as will fit
1300 		 */
1301 		if ((tbps = ops + gap - (ops % gap)) > ips)
1302 		    break;
1303 		if (putchar(ochar) == EOF) {
1304 		    pfail();
1305 		    return(1);
1306 		}
1307 		ops = tbps;
1308 	    }
1309 
1310 	    while (ops < ips) {
1311 		/*
1312 		 * finish off with spaces
1313 		 */
1314 		if (putchar(' ') == EOF) {
1315 		    pfail();
1316 		    return(1);
1317 		}
1318 		++ops;
1319 	    }
1320 
1321 	    /*
1322 	     * output non space char
1323 	     */
1324 	    if (putchar(*buf++) == EOF) {
1325 		pfail();
1326 		return(1);
1327 	    }
1328 	    ++ips;
1329 	    ++ops;
1330 	}
1331 
1332 	if (mor > 0) {
1333 	    /*
1334 	     * if incomplete line, save position counts
1335 	     */
1336 	    *svops = ops;
1337 	    *svips = ips;
1338 	    return(0);
1339 	}
1340 
1341 	if (mor < 0) {
1342 	    while (ops < ips) {
1343 		/*
1344 		 * use one space if necessary
1345 		 */
1346 		if (ips - ops == 1) {
1347 			putchar(' ');
1348 			break;
1349 		}
1350 		/*
1351 		 * use as many ochar as will fit
1352 		 */
1353 		if ((tbps = ops + gap - (ops % gap)) > ips)
1354 		    break;
1355 		if (putchar(ochar) == EOF) {
1356 		    pfail();
1357 		    return(1);
1358 		}
1359 		ops = tbps;
1360 	    }
1361 
1362 	    while (ops < ips) {
1363 		/*
1364 		 * finish off with spaces
1365 		 */
1366 		if (putchar(' ') == EOF) {
1367 		    pfail();
1368 		    return(1);
1369 		}
1370 		++ops;
1371 	    }
1372 	    return(0);
1373 	}
1374     } else {
1375 	/*
1376 	 * output is not contracted
1377 	 */
1378 	if (cnt && (fwrite(buf, sizeof(char), cnt, stdout) < cnt)) {
1379 	    pfail();
1380 	    return(1);
1381 	}
1382 	if (mor != 0)
1383 	    return(0);
1384     }
1385 
1386     /*
1387      * process line end and double space as required
1388      */
1389     if ((putchar('\n') == EOF) || (dspace && (putchar('\n') == EOF))) {
1390 	pfail();
1391 	return(1);
1392     }
1393     return(0);
1394 }
1395 
1396 #ifdef notused
1397 /*
1398  * inskip():    skip over pgcnt pages with lncnt lines per page
1399  *        file is closed at EOF (if not stdin).
1400  *
1401  *    inf    FILE * to read from
1402  *    pgcnt    number of pages to skip
1403  *    lncnt    number of lines per page
1404  */
1405 int
1406 inskip(FILE *inf, int pgcnt, int lncnt)
1407 {
1408     int c;
1409     int cnt;
1410 
1411     while(--pgcnt > 0) {
1412 	cnt = lncnt;
1413 	while ((c = getc(inf)) != EOF) {
1414 	    if ((c == '\n') && (--cnt == 0))
1415 		break;
1416 	}
1417 	if (c == EOF) {
1418 	    if (inf != stdin)
1419 		(void)fclose(inf);
1420 	    return(1);
1421 	}
1422     }
1423     return(0);
1424 }
1425 #endif
1426 
1427 /*
1428  * nxtfile:    returns a FILE * to next file in arg list and sets the
1429  *        time field for this file (or current date).
1430  *
1431  *    buf    array to store proper date for the header.
1432  *    dt    if set skips the date processing (used with -m)
1433  */
1434 FILE *
1435 nxtfile(int argc, char *argv[], char **fname, char *buf, int dt)
1436 {
1437     FILE *inf = NULL;
1438     struct timeval tv;
1439     struct timezone tz;
1440     struct tm *timeptr = NULL;
1441     struct stat statbuf;
1442     time_t curtime;
1443     static int twice = -1;
1444 
1445     ++twice;
1446     if (eoptind >= argc) {
1447 	/*
1448 	 * no file listed; default, use standard input
1449 	 */
1450 	if (twice)
1451 	    return(NULL);
1452 	clearerr(stdin);
1453 	inf = stdin;
1454 	if (header != NULL)
1455 	    *fname = header;
1456 	else
1457 	    *fname = FNAME;
1458 	if (nohead)
1459 	    return(inf);
1460 	if (gettimeofday(&tv, &tz) < 0) {
1461 	    ++errcnt;
1462 	    ferrout("pr: cannot get time of day, %s\n",
1463 		strerror(errno));
1464 	    eoptind = argc - 1;
1465 	    return(NULL);
1466 	}
1467 	curtime = tv.tv_sec;
1468 	timeptr = localtime(&curtime);
1469     }
1470     for (; eoptind < argc; ++eoptind) {
1471 	if (strcmp(argv[eoptind], "-") == 0) {
1472 	    /*
1473 	     * process a "-" for filename
1474 	     */
1475 	    clearerr(stdin);
1476 	    inf = stdin;
1477 	    if (header != NULL)
1478 		*fname = header;
1479 	    else
1480 		*fname = FNAME;
1481 	    ++eoptind;
1482 	    if (nohead || (dt && twice))
1483 		return(inf);
1484 	    if (gettimeofday(&tv, &tz) < 0) {
1485 		++errcnt;
1486 		ferrout("pr: cannot get time of day, %s\n",
1487 		    strerror(errno));
1488 		return(NULL);
1489 	    }
1490 	    curtime = tv.tv_sec;
1491 	    timeptr = localtime(&curtime);
1492 	} else {
1493 	    /*
1494 	     * normal file processing
1495 	     */
1496 	    if ((inf = fopen(argv[eoptind], "r")) == NULL) {
1497 		++errcnt;
1498 		if (nodiag)
1499 		    continue;
1500 		ferrout("pr: Cannot open %s, %s\n",
1501 		    argv[eoptind], strerror(errno));
1502 		continue;
1503 	    }
1504 	    if (header != NULL)
1505 		*fname = header;
1506 	    else if (dt)
1507 		*fname = FNAME;
1508 	    else
1509 		*fname = argv[eoptind];
1510 	    ++eoptind;
1511 	    if (nohead || (dt && twice))
1512 		return(inf);
1513 
1514 	    if (dt) {
1515 		if (gettimeofday(&tv, &tz) < 0) {
1516 		    ++errcnt;
1517 		    ferrout("pr: cannot get time of day, %s\n",
1518 			 strerror(errno));
1519 		    return(NULL);
1520 		}
1521 		curtime = tv.tv_sec;
1522 		timeptr = localtime(&curtime);
1523 	    } else {
1524 		if (fstat(fileno(inf), &statbuf) < 0) {
1525 		    ++errcnt;
1526 		    (void)fclose(inf);
1527 		    ferrout("pr: Cannot stat %s, %s\n",
1528 			argv[eoptind], strerror(errno));
1529 		    return(NULL);
1530 		}
1531 		timeptr = localtime(&(statbuf.st_mtime));
1532 	    }
1533 	}
1534 	break;
1535     }
1536     if (inf == NULL)
1537 	return(NULL);
1538 
1539     /*
1540      * set up time field used in header
1541      */
1542     if (strftime(buf, HDBUF, timefrmt, timeptr) == 0) {
1543 	++errcnt;
1544 	if (inf != stdin)
1545 	    (void)fclose(inf);
1546 	ferrout("pr: time conversion failed\n");
1547 	return(NULL);
1548     }
1549     return(inf);
1550 }
1551 
1552 /*
1553  * addnum():    adds the line number to the column
1554  *        Truncates from the front or pads with spaces as required.
1555  *        Numbers are right justified.
1556  *
1557  *    buf    buffer to store the number
1558  *    wdth    width of buffer to fill
1559  *    line    line number
1560  *
1561  *        NOTE: numbers occupy part of the column. The posix
1562  *        spec does not specify if -i processing should or should not
1563  *        occur on number padding. The spec does say it occupies
1564  *        part of the column. The usage of addnum    currently treats
1565  *        numbers as part of the column so spaces may be replaced.
1566  */
1567 void
1568 addnum(char *buf, int wdth, int line)
1569 {
1570     char *pt = buf + wdth;
1571 
1572     do {
1573 	*--pt = digs[line % 10];
1574 	line /= 10;
1575     } while (line && (pt > buf));
1576 
1577     /*
1578      * pad with space as required
1579      */
1580     while (pt > buf)
1581 	*--pt = ' ';
1582 }
1583 
1584 /*
1585  * prhead():    prints the top of page header
1586  *
1587  *    buf    buffer with time field (and offset)
1588  *    cnt    number of chars in buf
1589  *    fname    fname field for header
1590  *    pagcnt    page number
1591  *
1592  * prhead() should be used carefully, we don't want to print out headers
1593  * for null input files or orphan headers at the end of files, and also
1594  * trailer processing is typically conditional on whether you've called
1595  * prhead() at least once for a file and incremented pagecnt.  Exactly
1596  * how to determine whether to print a header is a little different in
1597  * the context each output mode, but we let the caller figure that out.
1598  */
1599 int
1600 prhead(char *buf, char *fname, int pagcnt)
1601 {
1602     int ips = 0;
1603     int ops = 0;
1604 
1605     beheaded = 1;
1606 
1607     if (skipping && pagcnt >= pgnm)
1608 	skipping = 0;
1609 
1610     if (nohead || skipping)
1611 	return (0);
1612 
1613     if ((putchar('\n') == EOF) || (putchar('\n') == EOF)) {
1614 	pfail();
1615 	return(1);
1616     }
1617     /*
1618      * posix is not clear if the header is subject to line length
1619      * restrictions. The specification for header line format
1620      * in the spec clearly does not limit length. No pr currently
1621      * restricts header length. However if we need to truncate in
1622      * an reasonable way, adjust the length of the printf by
1623      * changing HDFMT to allow a length max as an argument printf.
1624      * buf (which contains the offset spaces and time field could
1625      * also be trimmed
1626      *
1627      * note only the offset (if any) is processed for tab expansion
1628      */
1629     if (offst && otln(buf, offst, &ips, &ops, -1))
1630 	return(1);
1631     (void)printf(HDFMT,buf+offst, fname, pagcnt);
1632     return(0);
1633 }
1634 
1635 /*
1636  * prtail():    pad page with empty lines (if required) and print page trailer
1637  *        if requested
1638  *
1639  *    cnt    	number of lines of padding needed
1640  *    incomp    was a '\n' missing from last line output
1641  *
1642  * prtail() can now be invoked unconditionally, with the notion that if
1643  * we haven't printed a header, there is no need for a trailer
1644  */
1645 int
1646 prtail(int cnt, int incomp)
1647 {
1648     /*
1649      * if were's skipping to page N or haven't put out anything yet just exit
1650      */
1651     if (skipping || beheaded == 0)
1652 	return (0);
1653     beheaded = 0;
1654 
1655     /*
1656      * if noheaders, only terminate an incomplete last line
1657      */
1658     if (nohead) {
1659 
1660 	if (incomp) {
1661 	    if (dspace)
1662 		if (putchar('\n') == EOF) {
1663 		    pfail();
1664 		    return(1);
1665 		}
1666 	    if (putchar('\n') == EOF) {
1667 		pfail();
1668 		return(1);
1669 	     }
1670 	}
1671 	/*
1672 	 * but honor the formfeed request
1673 	 */
1674 	if (formfeed)
1675 	    if (putchar(OUTFF) == EOF) {
1676 		pfail();
1677 		return(1);
1678 	    }
1679 
1680     } else {
1681 
1682 	/*
1683 	 * if double space output two \n
1684 	 *
1685   	 * XXX this all seems bogus, why are we doing it here???
1686 	 * page length is in terms of output lines and only the input is
1687 	 * supposed to be double spaced...  otln() users should be doing
1688 	 * something like linect+=(dspace ? 2:1).
1689 	 */
1690 	if (dspace)
1691 	    cnt *= 2;
1692 
1693 	/*
1694 	 * if an odd number of lines per page, add an extra \n
1695 	 */
1696 	if (addone)
1697 	    ++cnt;
1698 
1699 	/*
1700 	 * either put out a form-feed or pad page with blanks
1701 	 */
1702 	if (formfeed) {
1703 	    if (incomp)
1704 		if (putchar('\n') == EOF) {
1705 		    pfail();
1706 		    return(1);
1707 		}
1708 	    if (putchar(OUTFF) == EOF) {
1709 		    pfail();
1710 		    return(1);
1711 	    }
1712 
1713 	} else {
1714 
1715 	    if (incomp)
1716 		cnt++;
1717 
1718 	    cnt += TAILLEN;
1719 	    while (--cnt >= 0) {
1720 		if (putchar('\n') == EOF) {
1721 		    pfail();
1722 		    return(1);
1723 		}
1724 	    }
1725 	}
1726     }
1727 
1728     return(0);
1729 }
1730 
1731 /*
1732  * terminate():    when a SIGINT is recvd
1733  */
1734 /*ARGSUSED*/
1735 void
1736 terminate(int which_sig)
1737 {
1738     flsh_errs();
1739     _exit(1);
1740 }
1741 
1742 void
1743 mfail(void)
1744 {
1745     ferrout("pr: memory allocation failed\n");
1746 }
1747 
1748 void
1749 pfail(void)
1750 {
1751     ferrout("pr: write failure, %s\n", strerror(errno));
1752 }
1753 
1754 void
1755 usage(void)
1756 {
1757     ferrout(
1758      "usage: pr [+page] [-column] [-adFfmrt] [-e [char] [gap]] [-h header]\n");
1759     ferrout(
1760      "\t[-i [char] [gap]] [-l lines] [-n [char] [width]] [-o offset]\n");
1761     ferrout(
1762      "\t[-s [char]] [-w width] [-] [file ...]\n");
1763 }
1764 
1765 /*
1766  * setup:    Validate command args, initialize and perform sanity
1767  *        checks on options
1768  */
1769 int
1770 setup(int argc, char *argv[])
1771 {
1772     int c;
1773     int eflag = 0;
1774     int iflag = 0;
1775     int wflag = 0;
1776     int cflag = 0;
1777     const char *errstr;
1778 
1779     if (isatty(fileno(stdout)))
1780 	ferr = 1;
1781 
1782     while ((c = egetopt(argc, argv, "#adfFmrte?h:i?l:n?o:s?w:")) != -1) {
1783 	switch (c) {
1784 	case '+':
1785 	    pgnm = strtonum(eoptarg, 1, INT_MAX, &errstr);
1786 	    if (errstr) {
1787 		ferrout("pr: +page number is %s: %s\n", errstr, eoptarg);
1788 		return(1);
1789 	    }
1790 	    ++skipping;
1791 	    break;
1792 	case '-':
1793 	    clcnt = strtonum(eoptarg, 1, INT_MAX, &errstr);
1794 	    if (errstr) {
1795 		ferrout("pr: -columns number is %s: %s\n", errstr, eoptarg);
1796 		return(1);
1797 	    }
1798 	    if (clcnt > 1)
1799 		++cflag;
1800 	    break;
1801 	case 'a':
1802 	    ++across;
1803 	    break;
1804 	case 'd':
1805 	    ++dspace;
1806 	    break;
1807 	case 'e':
1808 	    ++eflag;
1809 	    if ((eoptarg != NULL) && !isdigit(*eoptarg))
1810 		inchar = *eoptarg++;
1811 	    else
1812 		inchar = INCHAR;
1813 	    if ((eoptarg != NULL) && isdigit(*eoptarg)) {
1814 		ingap = strtonum(eoptarg, 0, INT_MAX, &errstr);
1815 		if (errstr) {
1816 		    ferrout("pr: -e gap is %s: %s\n", errstr, eoptarg);
1817 		    return(1);
1818 		}
1819 		if (ingap == 0)
1820 		    ingap = INGAP;
1821 	    } else if ((eoptarg != NULL) && (*eoptarg != '\0')) {
1822 		ferrout("pr: invalid value for -e %s\n", eoptarg);
1823 		return(1);
1824 	    } else
1825 		ingap = INGAP;
1826 	    break;
1827 	case 'f':
1828 	case 'F':
1829 	    ++formfeed;
1830 	    break;
1831 	case 'h':
1832 	    header = eoptarg;
1833 	    break;
1834 	case 'i':
1835 	    ++iflag;
1836 	    if ((eoptarg != NULL) && !isdigit(*eoptarg))
1837 		ochar = *eoptarg++;
1838 	    else
1839 		ochar = OCHAR;
1840 	    if ((eoptarg != NULL) && isdigit(*eoptarg)) {
1841 		ogap = strtonum(eoptarg, 0, INT_MAX, &errstr);
1842 		if (errstr) {
1843 		    ferrout("pr: -i gap is %s: %s\n", errstr, eoptarg);
1844 		    return(1);
1845 		}
1846 		if (ogap == 0)
1847 		    ogap = OGAP;
1848 	    } else if ((eoptarg != NULL) && (*eoptarg != '\0')) {
1849 		ferrout("pr: invalid value for -i %s\n", eoptarg);
1850 		return(1);
1851 	    } else
1852 		ogap = OGAP;
1853 	    break;
1854 	case 'l':
1855 	    lines = strtonum(eoptarg, 1, INT_MAX, &errstr);
1856 	    if (errstr) {
1857 		ferrout("pr: number of lines is %s: %s\n", errstr, eoptarg);
1858 		return(1);
1859 	    }
1860 	    break;
1861 	case 'm':
1862 	    ++merge;
1863 	    break;
1864 	case 'n':
1865 	    if ((eoptarg != NULL) && !isdigit(*eoptarg))
1866 		nmchar = *eoptarg++;
1867 	    else
1868 		nmchar = NMCHAR;
1869 	    if ((eoptarg != NULL) && isdigit(*eoptarg)) {
1870 		nmwd = strtonum(eoptarg, 1, INT_MAX, &errstr);
1871 		if (errstr) {
1872 		    ferrout("pr: -n width is %s: %s\n", errstr, eoptarg);
1873 		    return(1);
1874 		}
1875 	    } else if ((eoptarg != NULL) && (*eoptarg != '\0')) {
1876 		ferrout("pr: invalid value for -n %s\n", eoptarg);
1877 		return(1);
1878 	    } else
1879 		nmwd = NMWD;
1880 	    break;
1881 	case 'o':
1882 	    offst = strtonum(eoptarg, 1, INT_MAX, &errstr);
1883 	    if (errstr) {
1884 		ferrout("pr: -o offset is %s: %s\n", errstr, eoptarg);
1885 		return(1);
1886 	    }
1887 	    break;
1888 	case 'r':
1889 	    ++nodiag;
1890 	    break;
1891 	case 's':
1892 	    ++sflag;
1893 	    if (eoptarg == NULL)
1894 		schar = SCHAR;
1895 	    else {
1896 		schar = *eoptarg++;
1897 		if (*eoptarg != '\0') {
1898 		    ferrout("pr: invalid value for -s %s\n", eoptarg);
1899 		    return(1);
1900 		}
1901 	    }
1902 	    break;
1903 	case 't':
1904 	    ++nohead;
1905 	    break;
1906 	case 'w':
1907 	    ++wflag;
1908 	    pgwd = strtonum(eoptarg, 1, INT_MAX, &errstr);
1909 	    if (errstr) {
1910 		ferrout("pr: -w width is %s: %s\n", errstr, eoptarg);
1911 		return(1);
1912 	    }
1913 	    break;
1914 	default:
1915 	    return(1);
1916 	}
1917     }
1918 
1919     /*
1920      * default and sanity checks
1921      */
1922     inform++;
1923 
1924     if (!clcnt) {
1925 	if (merge) {
1926 	    if ((clcnt = argc - eoptind) <= 1) {
1927 		clcnt = CLCNT;
1928 #ifdef stupid
1929 		merge = 0;
1930 #endif
1931 	    }
1932 	} else
1933 	    clcnt = CLCNT;
1934     }
1935     if (across) {
1936 	if (clcnt == 1) {
1937 	    ferrout("pr: -a flag requires multiple columns\n");
1938 	    return(1);
1939 	}
1940 	if (merge) {
1941 	    ferrout("pr: -m cannot be used with -a\n");
1942 	    return(1);
1943 	}
1944     }
1945     if (!wflag) {
1946 	if (sflag)
1947 	    pgwd = SPGWD;
1948 	else
1949 	    pgwd = PGWD;
1950     }
1951     if (cflag || merge) {
1952 	if (!eflag) {
1953 	    inchar = INCHAR;
1954 	    ingap = INGAP;
1955 	}
1956 	if (!iflag) {
1957 	    ochar = OCHAR;
1958 	    ogap = OGAP;
1959 	}
1960     }
1961     if (cflag) {
1962 	if (merge) {
1963 	    ferrout("pr: -m cannot be used with multiple columns\n");
1964 	    return(1);
1965 	}
1966 	if (nmwd) {
1967 	    colwd = (pgwd + 1 - (clcnt * (nmwd + 2)))/clcnt;
1968 	    pgwd = ((colwd + nmwd + 2) * clcnt) - 1;
1969 	} else {
1970 	    colwd = (pgwd + 1 - clcnt)/clcnt;
1971 	    pgwd = ((colwd + 1) * clcnt) - 1;
1972 	}
1973 	if (colwd < 1) {
1974 	    ferrout("pr: page width is too small for %d columns\n",clcnt);
1975 	    return(1);
1976 	}
1977     }
1978     if (!lines)
1979 	lines = LINES;
1980 
1981     /*
1982      * make sure long enough for headers. if not disable
1983      */
1984     if (lines <= HEADLEN + TAILLEN)
1985 	++nohead;
1986     else if (!nohead)
1987 	lines -= HEADLEN + TAILLEN;
1988 
1989     /*
1990      * adjust for double space on odd length pages
1991      */
1992     if (dspace) {
1993 	if (lines == 1)
1994 	    dspace = 0;
1995 	else {
1996 	    if (lines & 1)
1997 		++addone;
1998 	    lines /= 2;
1999 	}
2000     }
2001 
2002     if ((timefrmt = getenv("LC_TIME")) == NULL)
2003 	timefrmt = TIMEFMT;
2004     return(0);
2005 }
2006