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