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