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