xref: /openbsd-src/usr.bin/pr/pr.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$OpenBSD: pr.c,v 1.10 2001/05/24 02:58:50 pvalchev 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.10 2001/05/24 02:58:50 pvalchev 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	*ferr;		/* 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(ferr,
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 one space if necessary
1240 		 */
1241 		if (ips - ops == 1) {
1242 			putchar(' ');
1243 			break;
1244 		}
1245 		/*
1246 		 * use as many ochar as will fit
1247 		 */
1248 		if ((tbps = ops + gap - (ops % gap)) > ips)
1249 		    break;
1250 		if (putchar(ochar) == EOF) {
1251 		    pfail();
1252 		    return(1);
1253 		}
1254 		ops = tbps;
1255 	    }
1256 
1257 	    while (ops < ips) {
1258 		/*
1259 		 * finish off with spaces
1260 		 */
1261 		if (putchar(' ') == EOF) {
1262 		    pfail();
1263 		    return(1);
1264 		}
1265 		++ops;
1266 	    }
1267 
1268 	    /*
1269 	     * output non space char
1270 	     */
1271 	    if (putchar(*buf++) == EOF) {
1272 		pfail();
1273 		return(1);
1274 	    }
1275 	    ++ips;
1276 	    ++ops;
1277 	}
1278 
1279 	if (mor > 0) {
1280 	    /*
1281 	     * if incomplete line, save position counts
1282 	     */
1283 	    *svops = ops;
1284 	    *svips = ips;
1285 	    return(0);
1286 	}
1287 
1288 	if (mor < 0) {
1289 	    while (ops < ips) {
1290 		/*
1291 		 * use one space if necessary
1292 		 */
1293 		if (ips - ops == 1) {
1294 			putchar(' ');
1295 			break;
1296 		}
1297 		/*
1298 		 * use as many ochar as will fit
1299 		 */
1300 		if ((tbps = ops + gap - (ops % gap)) > ips)
1301 		    break;
1302 		if (putchar(ochar) == EOF) {
1303 		    pfail();
1304 		    return(1);
1305 		}
1306 		ops = tbps;
1307 	    }
1308 
1309 	    while (ops < ips) {
1310 		/*
1311 		 * finish off with spaces
1312 		 */
1313 		if (putchar(' ') == EOF) {
1314 		    pfail();
1315 		    return(1);
1316 		}
1317 		++ops;
1318 	    }
1319 	    return(0);
1320 	}
1321     } else {
1322 	/*
1323 	 * output is not contracted
1324 	 */
1325 	if (cnt && (fwrite(buf, sizeof(char), cnt, stdout) <= 0)) {
1326 	    pfail();
1327 	    return(1);
1328 	}
1329 	if (mor != 0)
1330 	    return(0);
1331     }
1332 
1333     /*
1334      * process line end and double space as required
1335      */
1336     if ((putchar('\n') == EOF) || (dspace && (putchar('\n') == EOF))) {
1337 	pfail();
1338 	return(1);
1339     }
1340     return(0);
1341 }
1342 
1343 #ifdef notused
1344 /*
1345  * inskip():    skip over pgcnt pages with lncnt lines per page
1346  *        file is closed at EOF (if not stdin).
1347  *
1348  *    inf    FILE * to read from
1349  *    pgcnt    number of pages to skip
1350  *    lncnt    number of lines per page
1351  */
1352 int
1353 inskip(inf, pgcnt, lncnt)
1354     FILE *inf;
1355     register int pgcnt;
1356     register int lncnt;
1357 {
1358     register int c;
1359     register int cnt;
1360 
1361     while(--pgcnt > 0) {
1362 	cnt = lncnt;
1363 	while ((c = getc(inf)) != EOF) {
1364 	    if ((c == '\n') && (--cnt == 0))
1365 		break;
1366 	}
1367 	if (c == EOF) {
1368 	    if (inf != stdin)
1369 		(void)fclose(inf);
1370 	    return(1);
1371 	}
1372     }
1373     return(0);
1374 }
1375 #endif
1376 
1377 /*
1378  * nxtfile:    returns a FILE * to next file in arg list and sets the
1379  *        time field for this file (or current date).
1380  *
1381  *    buf    array to store proper date for the header.
1382  *    dt    if set skips the date processing (used with -m)
1383  */
1384 FILE *
1385 nxtfile(argc, argv, fname, buf, dt)
1386     int argc;
1387     char **argv;
1388     char **fname;
1389     char *buf;
1390     int dt;
1391 {
1392     FILE *inf = NULL;
1393     struct timeval tv;
1394     struct timezone tz;
1395     struct tm *timeptr = NULL;
1396     struct stat statbuf;
1397     time_t curtime;
1398     static int twice = -1;
1399 
1400     ++twice;
1401     if (eoptind >= argc) {
1402 	/*
1403 	 * no file listed; default, use standard input
1404 	 */
1405 	if (twice)
1406 	    return(NULL);
1407 	clearerr(stdin);
1408 	inf = stdin;
1409 	if (header != NULL)
1410 	    *fname = header;
1411 	else
1412 	    *fname = FNAME;
1413 	if (nohead)
1414 	    return(inf);
1415 	if (gettimeofday(&tv, &tz) < 0) {
1416 	    ++errcnt;
1417 	    (void)fprintf(ferr, "pr: cannot get time of day, %s\n",
1418 		strerror(errno));
1419 	    eoptind = argc - 1;
1420 	    return(NULL);
1421 	}
1422 	curtime = tv.tv_sec;
1423 	timeptr = localtime(&curtime);
1424     }
1425     for (; eoptind < argc; ++eoptind) {
1426 	if (strcmp(argv[eoptind], "-") == 0) {
1427 	    /*
1428 	     * process a "-" for filename
1429 	     */
1430 	    clearerr(stdin);
1431 	    inf = stdin;
1432 	    if (header != NULL)
1433 		*fname = header;
1434 	    else
1435 		*fname = FNAME;
1436 	    ++eoptind;
1437 	    if (nohead || (dt && twice))
1438 		return(inf);
1439 	    if (gettimeofday(&tv, &tz) < 0) {
1440 		++errcnt;
1441 		(void)fprintf(ferr,
1442 		    "pr: cannot get time of day, %s\n",
1443 		    strerror(errno));
1444 		return(NULL);
1445 	    }
1446 	    curtime = tv.tv_sec;
1447 	    timeptr = localtime(&curtime);
1448 	} else {
1449 	    /*
1450 	     * normal file processing
1451 	     */
1452 	    if ((inf = fopen(argv[eoptind], "r")) == NULL) {
1453 		++errcnt;
1454 		if (nodiag)
1455 		    continue;
1456 		(void)fprintf(ferr, "pr: Cannot open %s, %s\n",
1457 		    argv[eoptind], strerror(errno));
1458 		continue;
1459 	    }
1460 	    if (header != NULL)
1461 		*fname = header;
1462 	    else if (dt)
1463 		*fname = FNAME;
1464 	    else
1465 		*fname = argv[eoptind];
1466 	    ++eoptind;
1467 	    if (nohead || (dt && twice))
1468 		return(inf);
1469 
1470 	    if (dt) {
1471 		if (gettimeofday(&tv, &tz) < 0) {
1472 		    ++errcnt;
1473 		    (void)fprintf(ferr,
1474 			 "pr: cannot get time of day, %s\n",
1475 			 strerror(errno));
1476 		    return(NULL);
1477 		}
1478 		curtime = tv.tv_sec;
1479 		timeptr = localtime(&curtime);
1480 	    } else {
1481 		if (fstat(fileno(inf), &statbuf) < 0) {
1482 		    ++errcnt;
1483 		    (void)fclose(inf);
1484 		    (void)fprintf(ferr,
1485 			"pr: Cannot stat %s, %s\n",
1486 			argv[eoptind], strerror(errno));
1487 		    return(NULL);
1488 		}
1489 		timeptr = localtime(&(statbuf.st_mtime));
1490 	    }
1491 	}
1492 	break;
1493     }
1494     if (inf == NULL)
1495 	return(NULL);
1496 
1497     /*
1498      * set up time field used in header
1499      */
1500     if (strftime(buf, HDBUF, timefrmt, timeptr) <= 0) {
1501 	++errcnt;
1502 	if (inf != stdin)
1503 	    (void)fclose(inf);
1504 	(void)fputs("pr: time conversion failed\n", ferr);
1505 	return(NULL);
1506     }
1507     return(inf);
1508 }
1509 
1510 /*
1511  * addnum():    adds the line number to the column
1512  *        Truncates from the front or pads with spaces as required.
1513  *        Numbers are right justified.
1514  *
1515  *    buf    buffer to store the number
1516  *    wdth    width of buffer to fill
1517  *    line    line number
1518  *
1519  *        NOTE: numbers occupy part of the column. The posix
1520  *        spec does not specify if -i processing should or should not
1521  *        occur on number padding. The spec does say it occupies
1522  *        part of the column. The usage of addnum    currently treats
1523  *        numbers as part of the column so spaces may be replaced.
1524  */
1525 void
1526 addnum(buf, wdth, line)
1527     register char *buf;
1528     register int wdth;
1529     register int line;
1530 {
1531     register char *pt = buf + wdth;
1532 
1533     do {
1534 	*--pt = digs[line % 10];
1535 	line /= 10;
1536     } while (line && (pt > buf));
1537 
1538     /*
1539      * pad with space as required
1540      */
1541     while (pt > buf)
1542 	*--pt = ' ';
1543 }
1544 
1545 /*
1546  * prhead():    prints the top of page header
1547  *
1548  *    buf    buffer with time field (and offset)
1549  *    cnt    number of chars in buf
1550  *    fname    fname field for header
1551  *    pagcnt    page number
1552  *
1553  * prhead() should be used carefully, we don't want to print out headers
1554  * for null input files or orphan headers at the end of files, and also
1555  * trailer processing is typically conditional on whether you've called
1556  * prhead() at least once for a file and incremented pagecnt..  Exactly
1557  * how to determine whether to print a header is a little different in
1558  * the context each output mode, but we let the caller figure that out.
1559  */
1560 int
1561 prhead(buf, fname, pagcnt)
1562     char *buf;
1563     char *fname;
1564     int pagcnt;
1565 {
1566     int ips = 0;
1567     int ops = 0;
1568 
1569     beheaded = 1;
1570 
1571     if (skipping && pagcnt >= pgnm)
1572 	skipping = 0;
1573 
1574     if (nohead || skipping)
1575 	return (0);
1576 
1577     if ((putchar('\n') == EOF) || (putchar('\n') == EOF)) {
1578 	pfail();
1579 	return(1);
1580     }
1581     /*
1582      * posix is not clear if the header is subject to line length
1583      * restrictions. The specification for header line format
1584      * in the spec clearly does not limit length. No pr currently
1585      * restricts header length. However if we need to truncate in
1586      * an reasonable way, adjust the length of the printf by
1587      * changing HDFMT to allow a length max as an arguement printf.
1588      * buf (which contains the offset spaces and time field could
1589      * also be trimmed
1590      *
1591      * note only the offset (if any) is processed for tab expansion
1592      */
1593     if (offst && otln(buf, offst, &ips, &ops, -1))
1594 	return(1);
1595     (void)printf(HDFMT,buf+offst, fname, pagcnt);
1596     return(0);
1597 }
1598 
1599 /*
1600  * prtail():    pad page with empty lines (if required) and print page trailer
1601  *        if requested
1602  *
1603  *    cnt    	number of lines of padding needed
1604  *    incomp    was a '\n' missing from last line output
1605  *
1606  * prtail() can now be invoked unconditionally, with the notion that if
1607  * we haven't printed a hearder, these no need for a trailer
1608  */
1609 int
1610 prtail(cnt, incomp)
1611     register int cnt;
1612     int incomp;
1613 {
1614     /*
1615      * if were's skipping to page N or haven't put out anything yet just exit
1616      */
1617     if (skipping || beheaded == 0)
1618 	return (0);
1619     beheaded = 0;
1620 
1621     /*
1622      * if noheaders, only terminate an incomplete last line
1623      */
1624     if (nohead) {
1625 
1626 	if (incomp) {
1627 	    if (dspace)
1628 		if (putchar('\n') == EOF) {
1629 		    pfail();
1630 		    return(1);
1631 		}
1632 	    if (putchar('\n') == EOF) {
1633 		pfail();
1634 		return(1);
1635 	     }
1636 	}
1637 	/*
1638 	 * but honor the formfeed request
1639 	 */
1640 	if (formfeed)
1641 	    if (putchar(OUTFF) == EOF) {
1642 		pfail();
1643 		return(1);
1644 	    }
1645 
1646     } else {
1647 
1648 	/*
1649 	 * if double space output two \n
1650 	 *
1651   	 * XXX this all seems bogus, why are we doing it here???
1652 	 * page length is in terms of output lines and only the input is
1653 	 * supposed to be double spaced...  otln() users should be doing
1654 	 * something like linect+=(dspace ? 2:1).
1655 	 */
1656 	if (dspace)
1657 	    cnt *= 2;
1658 
1659 	/*
1660 	 * if an odd number of lines per page, add an extra \n
1661 	 */
1662 	if (addone)
1663 	    ++cnt;
1664 
1665 	/*
1666 	 * either put out a form-feed or pad page with blanks
1667 	 */
1668 	if (formfeed) {
1669 	    if (incomp)
1670 		if (putchar('\n') == EOF) {
1671 		    pfail();
1672 		    return(1);
1673 		}
1674 	    if (putchar(OUTFF) == EOF) {
1675 		    pfail();
1676 		    return(1);
1677 	    }
1678 
1679 	} else {
1680 
1681 	    if (incomp)
1682 		cnt++;
1683 
1684 	    cnt += TAILLEN;
1685 	    while (--cnt >= 0) {
1686 		if (putchar('\n') == EOF) {
1687 		    pfail();
1688 		    return(1);
1689 		}
1690 	    }
1691 	}
1692     }
1693 
1694     return(0);
1695 }
1696 
1697 /*
1698  * terminate():    when a SIGINT is recvd
1699  */
1700 void
1701 terminate(which_sig)
1702     int which_sig;
1703 {
1704     flsh_errs();
1705     exit(1);
1706 }
1707 
1708 
1709 /*
1710  * flsh_errs():    output saved up diagnostic messages after all normal
1711  *        processing has completed
1712  */
1713 void
1714 flsh_errs()
1715 {
1716     char buf[BUFSIZ];
1717 
1718     (void)fflush(stdout);
1719     (void)fflush(ferr);
1720     if (ferr == stderr)
1721 	return;
1722     rewind(ferr);
1723     while (fgets(buf, BUFSIZ, ferr) != NULL)
1724 	(void)fputs(buf, stderr);
1725 }
1726 
1727 void
1728 mfail()
1729 {
1730     (void)fputs("pr: memory allocation failed\n", ferr);
1731 }
1732 
1733 void
1734 pfail()
1735 {
1736     (void)fprintf(ferr, "pr: write failure, %s\n", strerror(errno));
1737 }
1738 
1739 void
1740 usage()
1741 {
1742     (void)fputs(
1743      "usage: pr [+page] [-col] [-adfFmrt] [-e[ch][gap]] [-h header]\n", ferr);
1744     (void)fputs(
1745      "          [-i[ch][gap]] [-l line] [-n[ch][width]] [-o offset]\n", ferr);
1746     (void)fputs(
1747      "          [-s[ch]] [-w width] [-] [file ...]\n", ferr);
1748 }
1749 
1750 /*
1751  * setup:    Validate command args, initialize and perform sanity
1752  *        checks on options
1753  */
1754 int
1755 setup(argc, argv)
1756     register int argc;
1757     register char **argv;
1758 {
1759     register int c;
1760     int eflag = 0;
1761     int iflag = 0;
1762     int wflag = 0;
1763     int cflag = 0;
1764 
1765     if (isatty(fileno(stdout))) {
1766 	/*
1767 	 * defer diagnostics until processing is done
1768 	 */
1769 	if ((ferr = tmpfile()) == NULL) {
1770 	       (void)fputs("Cannot defer diagnostic messages\n",stderr);
1771 	       return(1);
1772 	}
1773     } else
1774 	ferr = stderr;
1775     while ((c = egetopt(argc, argv, "#adfFmrte?h:i?l:n?o:s?w:")) != -1) {
1776 	switch (c) {
1777 	case '+':
1778 	    if ((pgnm = atoi(eoptarg)) < 1) {
1779 		(void)fputs("pr: +page number must be 1 or more\n",
1780 		    ferr);
1781 		return(1);
1782 	    }
1783 	    ++skipping;
1784 	    break;
1785 	case '-':
1786 	    if ((clcnt = atoi(eoptarg)) < 1) {
1787 		(void)fputs("pr: -columns must be 1 or more\n",ferr);
1788 		return(1);
1789 	    }
1790 	    if (clcnt > 1)
1791 		++cflag;
1792 	    break;
1793 	case 'a':
1794 	    ++across;
1795 	    break;
1796 	case 'd':
1797 	    ++dspace;
1798 	    break;
1799 	case 'e':
1800 	    ++eflag;
1801 	    if ((eoptarg != NULL) && !isdigit(*eoptarg))
1802 		inchar = *eoptarg++;
1803 	    else
1804 		inchar = INCHAR;
1805 	    if ((eoptarg != NULL) && isdigit(*eoptarg)) {
1806 		if ((ingap = atoi(eoptarg)) < 0) {
1807 		    (void)fputs(
1808 		    "pr: -e gap must be 0 or more\n", ferr);
1809 		    return(1);
1810 		}
1811 		if (ingap == 0)
1812 		    ingap = INGAP;
1813 	    } else if ((eoptarg != NULL) && (*eoptarg != '\0')) {
1814 		(void)fprintf(ferr,
1815 		      "pr: invalid value for -e %s\n", eoptarg);
1816 		return(1);
1817 	    } else
1818 		ingap = INGAP;
1819 	    break;
1820 	case 'f':
1821 	case 'F':
1822 	    ++formfeed;
1823 	    break;
1824 	case 'h':
1825 	    header = eoptarg;
1826 	    break;
1827 	case 'i':
1828 	    ++iflag;
1829 	    if ((eoptarg != NULL) && !isdigit(*eoptarg))
1830 		ochar = *eoptarg++;
1831 	    else
1832 		ochar = OCHAR;
1833 	    if ((eoptarg != NULL) && isdigit(*eoptarg)) {
1834 		if ((ogap = atoi(eoptarg)) < 0) {
1835 		    (void)fputs(
1836 		    "pr: -i gap must be 0 or more\n", ferr);
1837 		    return(1);
1838 		}
1839 		if (ogap == 0)
1840 		    ogap = OGAP;
1841 	    } else if ((eoptarg != NULL) && (*eoptarg != '\0')) {
1842 		(void)fprintf(ferr,
1843 		      "pr: invalid value for -i %s\n", eoptarg);
1844 		return(1);
1845 	    } else
1846 		ogap = OGAP;
1847 	    break;
1848 	case 'l':
1849 	    if (!isdigit(*eoptarg) || ((lines=atoi(eoptarg)) < 1)) {
1850 		(void)fputs(
1851 		 "pr: Number of lines must be 1 or more\n",ferr);
1852 		return(1);
1853 	    }
1854 	    break;
1855 	case 'm':
1856 	    ++merge;
1857 	    break;
1858 	case 'n':
1859 	    if ((eoptarg != NULL) && !isdigit(*eoptarg))
1860 		nmchar = *eoptarg++;
1861 	    else
1862 		nmchar = NMCHAR;
1863 	    if ((eoptarg != NULL) && isdigit(*eoptarg)) {
1864 		if ((nmwd = atoi(eoptarg)) < 1) {
1865 		    (void)fputs(
1866 		    "pr: -n width must be 1 or more\n",ferr);
1867 		    return(1);
1868 		}
1869 	    } else if ((eoptarg != NULL) && (*eoptarg != '\0')) {
1870 		(void)fprintf(ferr,
1871 		      "pr: invalid value for -n %s\n", eoptarg);
1872 		return(1);
1873 	    } else
1874 		nmwd = NMWD;
1875 	    break;
1876 	case 'o':
1877 	    if (!isdigit(*eoptarg) || ((offst = atoi(eoptarg))< 1)){
1878 		(void)fputs("pr: -o offset must be 1 or more\n",
1879 		    ferr);
1880 		return(1);
1881 	    }
1882 	    break;
1883 	case 'r':
1884 	    ++nodiag;
1885 	    break;
1886 	case 's':
1887 	    ++sflag;
1888 	    if (eoptarg == NULL)
1889 		schar = SCHAR;
1890 	    else {
1891 		schar = *eoptarg++;
1892 		if (*eoptarg != '\0') {
1893 		    (void)fprintf(ferr,
1894 		        "pr: invalid value for -s %s\n", eoptarg);
1895 		    return(1);
1896 		}
1897 	    }
1898 	    break;
1899 	case 't':
1900 	    ++nohead;
1901 	    break;
1902 	case 'w':
1903 	    ++wflag;
1904 	    if (!isdigit(*eoptarg) || ((pgwd = atoi(eoptarg)) < 1)){
1905 		(void)fputs(
1906 		   "pr: -w width must be 1 or more \n",ferr);
1907 		return(1);
1908 	    }
1909 	    break;
1910 	case '?':
1911 	default:
1912 	    return(1);
1913 	}
1914     }
1915 
1916     /*
1917      * default and sanity checks
1918      */
1919     inform++;
1920 
1921     if (!clcnt) {
1922 	if (merge) {
1923 	    if ((clcnt = argc - eoptind) <= 1) {
1924 		clcnt = CLCNT;
1925 #ifdef stupid
1926 		merge = 0;
1927 #endif
1928 	    }
1929 	} else
1930 	    clcnt = CLCNT;
1931     }
1932     if (across) {
1933 	if (clcnt == 1) {
1934 	    (void)fputs("pr: -a flag requires multiple columns\n",
1935 		ferr);
1936 	    return(1);
1937 	}
1938 	if (merge) {
1939 	    (void)fputs("pr: -m cannot be used with -a\n", ferr);
1940 	    return(1);
1941 	}
1942     }
1943     if (!wflag) {
1944 	if (sflag)
1945 	    pgwd = SPGWD;
1946 	else
1947 	    pgwd = PGWD;
1948     }
1949     if (cflag || merge) {
1950 	if (!eflag) {
1951 	    inchar = INCHAR;
1952 	    ingap = INGAP;
1953 	}
1954 	if (!iflag) {
1955 	    ochar = OCHAR;
1956 	    ogap = OGAP;
1957 	}
1958     }
1959     if (cflag) {
1960 	if (merge) {
1961 	    (void)fputs(
1962 	      "pr: -m cannot be used with multiple columns\n", ferr);
1963 	    return(1);
1964 	}
1965 	if (nmwd) {
1966 	    colwd = (pgwd + 1 - (clcnt * (nmwd + 2)))/clcnt;
1967 	    pgwd = ((colwd + nmwd + 2) * clcnt) - 1;
1968 	} else {
1969 	    colwd = (pgwd + 1 - clcnt)/clcnt;
1970 	    pgwd = ((colwd + 1) * clcnt) - 1;
1971 	}
1972 	if (colwd < 1) {
1973 	    (void)fprintf(ferr,
1974 	      "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