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