Home | History | Annotate | Line # | Download | only in pr
pr.c revision 1.14
      1 /*	$NetBSD: pr.c,v 1.14 2006/04/02 04:17:08 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.14 2006/04/02 04:17:08 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 						return(1);
    513 					tvc = i;
    514 
    515 					for (j = 0; j < clcnt; ++j) {
    516 						/*
    517 						 * determine column length
    518 						 */
    519 						if (j == mclcnt) {
    520 							/*
    521 							 * last column
    522 							 */
    523 							cnt = vc[tvc].cnt;
    524 							if (nmwd)
    525 								cnt += cw;
    526 						} else if (sflag) {
    527 							/*
    528 							 * single ch between
    529 							 */
    530 							cnt = vc[tvc].cnt + 1;
    531 							if (nmwd)
    532 								cnt += cw;
    533 						} else
    534 							cnt = fullcol;
    535 						if (otln(vc[tvc].pt, cnt, &ips,
    536 								&ops, 1))
    537 							return(1);
    538 						tvc += pln;
    539 						if (tvc >= cvc)
    540 							break;
    541 					}
    542 					/*
    543 					 * terminate line
    544 					 */
    545 					if (otln(buf, 0, &ips, &ops, 0))
    546 						goto out;
    547 				}
    548 				/*
    549 				 * pad to end of page
    550 				 */
    551 				if (prtail((lines - pln), 0))
    552 					goto out;
    553 				/*
    554 				 * done with output, go to next file
    555 				 */
    556 				break;
    557 			}
    558 
    559 			/*
    560 			 * determine how many lines to output
    561 			 */
    562 			if (i > 0)
    563 				pln = lines;
    564 			else
    565 				pln = j;
    566 
    567 			/*
    568 			 * print header
    569 			 */
    570 			if (pln && !nohead && prhead(hbuf, fname, pagecnt))
    571 				goto out;
    572 
    573 			/*
    574 			 * output each line
    575 			 */
    576 			for (i = 0; i < pln; ++i) {
    577 				ptbf = buf + lindy[i];
    578 				if ((j = lstdat[i] - ptbf) <= offst)
    579 					break;
    580 				if (otln(ptbf, j, &ips, &ops, 0))
    581 					goto out;
    582 			}
    583 
    584 			/*
    585 			 * pad to end of page
    586 			 */
    587 			if (pln && prtail((lines - pln), 0))
    588 				goto out;
    589 
    590 			/*
    591 			 * if EOF go to next file
    592 			 */
    593 			if (cnt < 0)
    594 				break;
    595 			++pagecnt;
    596 		}
    597 		if (inf != stdin)
    598 			(void)fclose(inf);
    599 	}
    600 	if (eoptind < argc)
    601 		goto out;
    602 	error = 0;
    603 	goto out;
    604 oomem:
    605 	mfail();
    606 out:
    607 	if (buf)
    608 		free(buf);
    609 	if (hbuf)
    610 		free(hbuf);
    611 	if (vc)
    612 		free(vc);
    613 	if (lstdat)
    614 		free(lstdat);
    615 	if (indy)
    616 		free(lindy);
    617 	if (inf != NULL && inf != stdin)
    618 		(void)fclose(inf);
    619 	return error;
    620 }
    621 
    622 /*
    623  * horzcol:	print files with more than one column of output across a page
    624  */
    625 int
    626 horzcol(argc, argv)
    627         int argc;
    628         char *argv[];
    629 {
    630 	char *ptbf;
    631 	int pln;
    632 	int cnt = -1;
    633 	char *lstdat;
    634 	int col = colwd + 1;
    635 	int j;
    636 	int i;
    637 	int lncnt;
    638 	int pagecnt;
    639 	char *buf = NULL;
    640 	char *hbuf = NULL;
    641 	char *ohbuf;
    642 	char *fname;
    643 	FILE *inf = NULL;
    644 	int ips = 0;
    645 	int cps = 0;
    646 	int ops = 0;
    647 	int mor = 0;
    648 	int error = 1;
    649 
    650 	if ((buf = malloc((unsigned)(pgwd+offst+1)*sizeof(char))) == NULL)
    651 		goto oomem;
    652 
    653 	/*
    654 	 * page header
    655 	 */
    656 	if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL)
    657 		goto oomem;
    658 	ohbuf = hbuf + offst;
    659 	if (offst) {
    660 		(void)memset(buf, (int)' ', offst);
    661 		(void)memset(hbuf, (int)' ', offst);
    662 	}
    663 
    664 	/*
    665 	 * loop by file
    666 	 */
    667 	while ((inf = nxtfile(argc, argv, &fname, ohbuf, 0)) != NULL) {
    668 		if (pgnm) {
    669 			if (inskip(inf, pgnm, lines))
    670 				continue;
    671 			pagecnt = pgnm;
    672 		} else
    673 			pagecnt = 1;
    674 		lncnt = 0;
    675 
    676 		/*
    677 		 * loop by page
    678 		 */
    679 		for(;;) {
    680 			/*
    681 			 * loop by line
    682 			 */
    683 			for (i = 0; i < lines; ++i) {
    684 				ptbf = buf + offst;
    685 				lstdat = ptbf;
    686 				j = 0;
    687 				/*
    688 				 * loop by col
    689 				 */
    690 				for(;;) {
    691 					if (nmwd) {
    692 						/*
    693 						 * add number to column
    694 						 */
    695 						addnum(ptbf, nmwd, ++lncnt);
    696 						ptbf += nmwd;
    697 						*ptbf++ = nmchar;
    698 					}
    699 					/*
    700 					 * input line
    701 					 */
    702 					if ((cnt = inln(inf,ptbf,colwd,&cps,1,
    703 							&mor)) < 0)
    704 						break;
    705 					ptbf += cnt;
    706 					lstdat = ptbf;
    707 
    708 					/*
    709 					 * if last line skip padding
    710 					 */
    711 					if (++j >= clcnt)
    712 						break;
    713 
    714 					/*
    715 					 * pad to end of column
    716 					 */
    717 					if (sflag)
    718 						*ptbf++ = schar;
    719 					else if ((pln = col - cnt) > 0) {
    720 						(void)memset(ptbf,(int)' ',pln);
    721 						ptbf += pln;
    722 					}
    723 				}
    724 
    725 				/*
    726 				 * determine line length
    727 				 */
    728 				if ((j = lstdat - buf) <= offst)
    729 					break;
    730 				if (!i && !nohead &&
    731 					prhead(hbuf, fname, pagecnt))
    732 					goto out;
    733 				/*
    734 				 * output line
    735 				 */
    736 				if (otln(buf, j, &ips, &ops, 0))
    737 					goto out;
    738 			}
    739 
    740 			/*
    741 			 * pad to end of page
    742 			 */
    743 			if (i && prtail(lines-i, 0))
    744 				goto out;
    745 
    746 			/*
    747 			 * if EOF go to next file
    748 			 */
    749 			if (cnt < 0)
    750 				break;
    751 			++pagecnt;
    752 		}
    753 		if (inf != stdin)
    754 			(void)fclose(inf);
    755 	}
    756 	if (eoptind < argc)
    757 		goto out;
    758 	error = 0;
    759 	goto out;
    760 oomem:
    761 	mfail();
    762 out:
    763 	if (buf)
    764 		free(buf);
    765 	if (hbuf)
    766 		free(hbuf);
    767 	if (inf != NULL && inf != stdin)
    768 		(void)fclose(inf);
    769 	return error;
    770 }
    771 
    772 /*
    773  * mulfile:	print files with more than one column of output and
    774  *		more than one file concurrently
    775  */
    776 int
    777 mulfile(argc, argv)
    778         int argc;
    779         char *argv[];
    780 {
    781 	char *ptbf;
    782 	int j;
    783 	int pln;
    784 	int cnt;
    785 	char *lstdat;
    786 	int i;
    787 	FILE **fbuf = NULL;
    788 	int actf;
    789 	int lncnt;
    790 	int col;
    791 	int pagecnt;
    792 	int fproc;
    793 	char *buf = NULL;
    794 	char *hbuf = NULL;
    795 	char *ohbuf;
    796 	char *fname;
    797 	int ips = 0;
    798 	int cps = 0;
    799 	int ops = 0;
    800 	int mor = 0;
    801 	int error = 1;
    802 
    803 	/*
    804 	 * array of FILE *, one for each operand
    805 	 */
    806 	if ((fbuf = calloc(clcnt, sizeof(FILE *))) == NULL)
    807 		goto oomem;
    808 
    809 	/*
    810 	 * page header
    811 	 */
    812 	if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL)
    813 		goto oomem;
    814 	ohbuf = hbuf + offst;
    815 
    816 	/*
    817 	 * do not know how many columns yet. The number of operands provide an
    818 	 * upper bound on the number of columns. We use the number of files
    819 	 * we can open successfully to set the number of columns. The operation
    820 	 * of the merge operation (-m) in relation to unsuccesful file opens
    821 	 * is unspecified by posix.
    822 	 */
    823 	j = 0;
    824 	while (j < clcnt) {
    825 		if ((fbuf[j] = nxtfile(argc, argv, &fname, ohbuf, 1)) == NULL)
    826 			break;
    827 		if (pgnm && (inskip(fbuf[j], pgnm, lines)))
    828 			fbuf[j] = NULL;
    829 		++j;
    830 	}
    831 
    832 	/*
    833 	 * if no files, exit
    834 	 */
    835 	if (!j)
    836 		goto out;
    837 
    838 	/*
    839 	 * calculate page boundries based on open file count
    840 	 */
    841 	clcnt = j;
    842 	if (nmwd) {
    843 		colwd = (pgwd - clcnt - nmwd)/clcnt;
    844 		pgwd = ((colwd + 1) * clcnt) - nmwd - 2;
    845 	} else {
    846 		colwd = (pgwd + 1 - clcnt)/clcnt;
    847 		pgwd = ((colwd + 1) * clcnt) - 1;
    848 	}
    849 	if (colwd < 1) {
    850 		(void)fprintf(err,
    851 		  "pr: page width too small for %d columns\n", clcnt);
    852 		goto out;
    853 	}
    854 	actf = clcnt;
    855 	col = colwd + 1;
    856 
    857 	/*
    858 	 * line buffer
    859 	 */
    860 	if ((buf = malloc((unsigned)(pgwd+offst+1)*sizeof(char))) == NULL)
    861 		goto out;
    862 	if (offst) {
    863 		(void)memset(buf, (int)' ', offst);
    864 		(void)memset(hbuf, (int)' ', offst);
    865 	}
    866 	if (pgnm)
    867 		pagecnt = pgnm;
    868 	else
    869 		pagecnt = 1;
    870 	lncnt = 0;
    871 
    872 	/*
    873 	 * continue to loop while any file still has data
    874 	 */
    875 	while (actf > 0) {
    876 		/*
    877 		 * loop by line
    878 		 */
    879 		for (i = 0; i < lines; ++i) {
    880 			ptbf = buf + offst;
    881 			lstdat = ptbf;
    882 			if (nmwd) {
    883 				/*
    884 				 * add line number to line
    885 				 */
    886 				addnum(ptbf, nmwd, ++lncnt);
    887 				ptbf += nmwd;
    888 				*ptbf++ = nmchar;
    889 			}
    890 			j = 0;
    891 			fproc = 0;
    892 
    893 			/*
    894 			 * loop by column
    895 			 */
    896 			for (j = 0; j < clcnt; ++j) {
    897 				if (fbuf[j] == NULL) {
    898 					/*
    899 					 * empty column; EOF
    900 					 */
    901 					cnt = 0;
    902 				} else if ((cnt = inln(fbuf[j], ptbf, colwd,
    903 							&cps, 1, &mor)) < 0) {
    904 					/*
    905 					 * EOF hit; no data
    906 					 */
    907 					if (fbuf[j] != stdin)
    908 						(void)fclose(fbuf[j]);
    909 					fbuf[j] = NULL;
    910 					--actf;
    911 					cnt = 0;
    912 				} else {
    913 					/*
    914 					 * process file data
    915 					 */
    916 					ptbf += cnt;
    917 					lstdat = ptbf;
    918 					fproc++;
    919 				}
    920 
    921 				/*
    922 				 * if last ACTIVE column, done with line
    923 				 */
    924 				if (fproc >= actf)
    925 					break;
    926 
    927 				/*
    928 				 * pad to end of column
    929 				 */
    930 				if (sflag) {
    931 					*ptbf++ = schar;
    932 				} else if ((pln = col - cnt) > 0) {
    933 					(void)memset(ptbf, (int)' ', pln);
    934 					ptbf += pln;
    935 				}
    936 			}
    937 
    938 			/*
    939 			 * calculate data in line
    940 			 */
    941 			if ((j = lstdat - buf) <= offst)
    942 				break;
    943 
    944 			if (!i && !nohead && prhead(hbuf, fname, pagecnt))
    945 				goto out;
    946 
    947 			/*
    948 			 * output line
    949 			 */
    950 			if (otln(buf, j, &ips, &ops, 0))
    951 				goto out;
    952 
    953 			/*
    954 			 * if no more active files, done
    955 			 */
    956 			if (actf <= 0) {
    957 				++i;
    958 				break;
    959 			}
    960 		}
    961 
    962 		/*
    963 		 * pad to end of page
    964 		 */
    965 		if (i && prtail(lines-i, 0))
    966 			goto out;
    967 		++pagecnt;
    968 	}
    969 	if (eoptind < argc)
    970 		goto out;
    971 	error = 0;
    972 	goto out;
    973 oomem:
    974 	mfail();
    975 out:
    976 	if (fbuf) {
    977 		for (j = 0; j < clcnt; j++)
    978 			if (fbuf[j] && fbuf[j] != stdin)
    979 				(void)fclose(fbuf[j]);
    980 		free(fbuf);
    981 	}
    982 	if (hbuf)
    983 		free(hbuf);
    984 	if (buf)
    985 		free(buf);
    986 	return error;
    987 }
    988 
    989 /*
    990  * inln():	input a line of data (unlimited length lines supported)
    991  *		Input is optionally expanded to spaces
    992  *
    993  *	inf:	file
    994  *	buf:	buffer
    995  *	lim:	buffer length
    996  *	cps:	column positon 1st char in buffer (large line support)
    997  *	trnc:	throw away data more than lim up to \n
    998  *	mor:	set if more data in line (not truncated)
    999  */
   1000 int
   1001 inln(inf, buf, lim, cps, trnc, mor)
   1002 	FILE *inf;
   1003 	char *buf;
   1004 	int lim;
   1005 	int *cps;
   1006 	int trnc;
   1007 	int *mor;
   1008 {
   1009 	int col;
   1010 	int gap = ingap;
   1011 	int ch = EOF;
   1012 	char *ptbuf;
   1013 	int chk = (int)inchar;
   1014 
   1015 	ptbuf = buf;
   1016 
   1017 	if (gap) {
   1018 		/*
   1019 		 * expanding input option
   1020 		 */
   1021 		while ((--lim >= 0) && ((ch = getc(inf)) != EOF)) {
   1022 			/*
   1023 			 * is this the input "tab" char
   1024 			 */
   1025 			if (ch == chk) {
   1026 				/*
   1027 				 * expand to number of spaces
   1028 				 */
   1029 				col = (ptbuf - buf) + *cps;
   1030 				col = gap - (col % gap);
   1031 
   1032 				/*
   1033 				 * if more than this line, push back
   1034 				 */
   1035 				if ((col > lim) && (ungetc(ch, inf) == EOF))
   1036 					return(1);
   1037 
   1038 				/*
   1039 				 * expand to spaces
   1040 				 */
   1041 				while ((--col >= 0) && (--lim >= 0))
   1042 					*ptbuf++ = ' ';
   1043 				continue;
   1044 			}
   1045 			if (ch == '\n')
   1046 				break;
   1047 			*ptbuf++ = ch;
   1048 		}
   1049 	} else {
   1050 		/*
   1051 		 * no expansion
   1052 		 */
   1053 		while ((--lim >= 0) && ((ch = getc(inf)) != EOF)) {
   1054 			if (ch == '\n')
   1055 				break;
   1056 			*ptbuf++ = ch;
   1057 		}
   1058 	}
   1059 	col = ptbuf - buf;
   1060 	if (ch == EOF) {
   1061 		*mor = 0;
   1062 		*cps = 0;
   1063 		if (!col)
   1064 			return(-1);
   1065 		return(col);
   1066 	}
   1067 	if (ch == '\n') {
   1068 		/*
   1069 		 * entire line processed
   1070 		 */
   1071 		*mor = 0;
   1072 		*cps = 0;
   1073 		return(col);
   1074 	}
   1075 
   1076 	/*
   1077 	 * line was larger than limit
   1078 	 */
   1079 	if (trnc) {
   1080 		/*
   1081 		 * throw away rest of line
   1082 		 */
   1083 		while ((ch = getc(inf)) != EOF) {
   1084 			if (ch == '\n')
   1085 				break;
   1086 		}
   1087 		*cps = 0;
   1088 		*mor = 0;
   1089 	} else {
   1090 		/*
   1091 		 * save column offset if not truncated
   1092 		 */
   1093 		*cps += col;
   1094 		*mor = 1;
   1095 	}
   1096 
   1097 	return(col);
   1098 }
   1099 
   1100 /*
   1101  * otln():	output a line of data. (Supports unlimited length lines)
   1102  *		output is optionally contracted to tabs
   1103  *
   1104  *	buf:	output buffer with data
   1105  *	cnt:	number of chars of valid data in buf
   1106  *	svips:	buffer input column position (for large lines)
   1107  *	svops:	buffer output column position (for large lines)
   1108  *	mor:	output line not complete in this buf; more data to come.
   1109  *		1 is more, 0 is complete, -1 is no \n's
   1110  */
   1111 int
   1112 otln(buf, cnt, svips, svops, mor)
   1113 	char *buf;
   1114 	int cnt;
   1115 	int *svops;
   1116 	int *svips;
   1117 	int mor;
   1118 {
   1119 	int ops;		/* last col output */
   1120 	int ips;		/* last col in buf examined */
   1121 	int gap = ogap;
   1122 	int tbps;
   1123 	char *endbuf;
   1124 
   1125 	if (ogap) {
   1126 		/*
   1127 		 * contracting on output
   1128 		 */
   1129 		endbuf = buf + cnt;
   1130 		ops = *svops;
   1131 		ips = *svips;
   1132 		while (buf < endbuf) {
   1133 			/*
   1134 			 * count number of spaces and ochar in buffer
   1135 			 */
   1136 			if (*buf == ' ') {
   1137 				++ips;
   1138 				++buf;
   1139 				continue;
   1140 			}
   1141 
   1142 			/*
   1143 			 * simulate ochar processing
   1144 			 */
   1145 			if (*buf == ochar) {
   1146 				ips += gap - (ips % gap);
   1147 				++buf;
   1148 				continue;
   1149 			}
   1150 
   1151 			/*
   1152 			 * got a non space char; contract out spaces
   1153 			 */
   1154 			while (ops < ips) {
   1155 				/*
   1156 				 * use one space if necessary
   1157 				 */
   1158 				if (ips - ops == 1) {
   1159 					putchar(' ');
   1160 					break;
   1161 				}
   1162 				/*
   1163 				 * use as many ochar as will fit
   1164 				 */
   1165 				if ((tbps = ops + gap - (ops % gap)) > ips)
   1166 					break;
   1167 				if (putchar(ochar) == EOF) {
   1168 					pfail();
   1169 					return(1);
   1170 				}
   1171 				ops = tbps;
   1172 			}
   1173 
   1174 			while (ops < ips) {
   1175 				/*
   1176 				 * finish off with spaces
   1177 				 */
   1178 				if (putchar(' ') == EOF) {
   1179 					pfail();
   1180 					return(1);
   1181 				}
   1182 				++ops;
   1183 			}
   1184 
   1185 			/*
   1186 			 * output non space char
   1187 			 */
   1188 			if (putchar(*buf++) == EOF) {
   1189 				pfail();
   1190 				return(1);
   1191 			}
   1192 			++ips;
   1193 			++ops;
   1194 		}
   1195 
   1196 		if (mor > 0) {
   1197 			/*
   1198 			 * if incomplete line, save position counts
   1199 			 */
   1200 			*svops = ops;
   1201 			*svips = ips;
   1202 			return(0);
   1203 		}
   1204 
   1205 		if (mor < 0) {
   1206 			while (ops < ips) {
   1207 				/*
   1208 				 * use one space if necessary
   1209 				 */
   1210 				if (ips - ops == 1) {
   1211 					putchar(' ');
   1212 					break;
   1213 				}
   1214 				/*
   1215 				 * use as many ochar as will fit
   1216 				 */
   1217 				if ((tbps = ops + gap - (ops % gap)) > ips)
   1218 					break;
   1219 				if (putchar(ochar) == EOF) {
   1220 					pfail();
   1221 					return(1);
   1222 				}
   1223 				ops = tbps;
   1224 			}
   1225 			while (ops < ips) {
   1226 				/*
   1227 				 * finish off with spaces
   1228 				 */
   1229 				if (putchar(' ') == EOF) {
   1230 					pfail();
   1231 					return(1);
   1232 				}
   1233 				++ops;
   1234 			}
   1235 			return(0);
   1236 		}
   1237 	} else {
   1238 		/*
   1239 		 * output is not contracted
   1240 		 */
   1241 		if (cnt && (fwrite(buf, sizeof(char), cnt, stdout) <= 0)) {
   1242 			pfail();
   1243 			return(1);
   1244 		}
   1245 		if (mor != 0)
   1246 			return(0);
   1247 	}
   1248 
   1249 	/*
   1250 	 * process line end and double space as required
   1251 	 */
   1252 	if ((putchar('\n') == EOF) || (dspace && (putchar('\n') == EOF))) {
   1253 		pfail();
   1254 		return(1);
   1255 	}
   1256 	return(0);
   1257 }
   1258 
   1259 /*
   1260  * inskip():	skip over pgcnt pages with lncnt lines per page
   1261  *		file is closed at EOF (if not stdin).
   1262  *
   1263  *	inf	FILE * to read from
   1264  *	pgcnt	number of pages to skip
   1265  *	lncnt	number of lines per page
   1266  */
   1267 int
   1268 inskip(inf, pgcnt, lncnt)
   1269 	FILE *inf;
   1270 	int pgcnt;
   1271 	int lncnt;
   1272 {
   1273 	int c;
   1274 	int cnt;
   1275 
   1276 	while(--pgcnt > 0) {
   1277 		cnt = lncnt;
   1278 		while ((c = getc(inf)) != EOF) {
   1279 			if ((c == '\n') && (--cnt == 0))
   1280 				break;
   1281 		}
   1282 		if (c == EOF) {
   1283 			if (inf != stdin)
   1284 				(void)fclose(inf);
   1285 			return(1);
   1286 		}
   1287 	}
   1288 	return(0);
   1289 }
   1290 
   1291 /*
   1292  * nxtfile:	returns a FILE * to next file in arg list and sets the
   1293  *		time field for this file (or current date).
   1294  *
   1295  *	buf	array to store proper date for the header.
   1296  *	dt	if set skips the date processing (used with -m)
   1297  */
   1298 FILE *
   1299 nxtfile(argc, argv, fname, buf, dt)
   1300 	int argc;
   1301 	char **argv;
   1302 	char **fname;
   1303 	char *buf;
   1304 	int dt;
   1305 {
   1306 	FILE *inf = NULL;
   1307 	struct timeval tv;
   1308 	struct timezone tz;
   1309 	struct tm *timeptr = NULL;
   1310 	struct stat statbuf;
   1311 	time_t curtime;
   1312 	static int twice = -1;
   1313 
   1314 	++twice;
   1315 	if (eoptind >= argc) {
   1316 		/*
   1317 		 * no file listed; default, use standard input
   1318 		 */
   1319 		if (twice)
   1320 			return(NULL);
   1321 		clearerr(stdin);
   1322 		inf = stdin;
   1323 		if (header != NULL)
   1324 			*fname = header;
   1325 		else
   1326 			*fname = FNAME;
   1327 		if (nohead)
   1328 			return(inf);
   1329 		if (gettimeofday(&tv, &tz) < 0) {
   1330 			++errcnt;
   1331 			(void)fprintf(err, "pr: cannot get time of day, %s\n",
   1332 				strerror(errno));
   1333 			eoptind = argc - 1;
   1334 			return(NULL);
   1335 		}
   1336 		curtime = tv.tv_sec;
   1337 		timeptr = localtime(&curtime);
   1338 	}
   1339 	for (; eoptind < argc; ++eoptind) {
   1340 		if (strcmp(argv[eoptind], "-") == 0) {
   1341 			/*
   1342 			 * process a "-" for filename
   1343 			 */
   1344 			clearerr(stdin);
   1345 			inf = stdin;
   1346 			if (header != NULL)
   1347 				*fname = header;
   1348 			else
   1349 				*fname = FNAME;
   1350 			++eoptind;
   1351 			if (nohead || (dt && twice))
   1352 				return(inf);
   1353 			if (gettimeofday(&tv, &tz) < 0) {
   1354 				++errcnt;
   1355 				(void)fprintf(err,
   1356 					"pr: cannot get time of day, %s\n",
   1357 					strerror(errno));
   1358 				return(NULL);
   1359 			}
   1360 			curtime = tv.tv_sec;
   1361 			timeptr = localtime(&curtime);
   1362 		} else {
   1363 			/*
   1364 			 * normal file processing
   1365 			 */
   1366 			if ((inf = fopen(argv[eoptind], "r")) == NULL) {
   1367 				++errcnt;
   1368 				if (nodiag)
   1369 					continue;
   1370 				(void)fprintf(err, "pr: Cannot open %s, %s\n",
   1371 					argv[eoptind], strerror(errno));
   1372 				continue;
   1373 			}
   1374 			if (header != NULL)
   1375 				*fname = header;
   1376 			else if (dt)
   1377 				*fname = FNAME;
   1378 			else
   1379 				*fname = argv[eoptind];
   1380 			++eoptind;
   1381 			if (nohead || (dt && twice))
   1382 				return(inf);
   1383 
   1384 			if (dt) {
   1385 				if (gettimeofday(&tv, &tz) < 0) {
   1386 					++errcnt;
   1387 					(void)fprintf(err,
   1388 					     "pr: cannot get time of day, %s\n",
   1389 					     strerror(errno));
   1390 					return(NULL);
   1391 				}
   1392 				curtime = tv.tv_sec;
   1393 				timeptr = localtime(&curtime);
   1394 			} else {
   1395 				if (fstat(fileno(inf), &statbuf) < 0) {
   1396 					++errcnt;
   1397 					(void)fclose(inf);
   1398 					(void)fprintf(err,
   1399 						"pr: Cannot stat %s, %s\n",
   1400 						argv[eoptind], strerror(errno));
   1401 					return(NULL);
   1402 				}
   1403 				timeptr = localtime(&(statbuf.st_mtime));
   1404 			}
   1405 		}
   1406 		break;
   1407 	}
   1408 	if (inf == NULL)
   1409 		return(NULL);
   1410 
   1411 	/*
   1412 	 * set up time field used in header
   1413 	 */
   1414 	if (strftime(buf, HDBUF, timefrmt, timeptr) <= 0) {
   1415 		++errcnt;
   1416 		if (inf != stdin)
   1417 			(void)fclose(inf);
   1418 		(void)fputs("pr: time conversion failed\n", err);
   1419 		return(NULL);
   1420 	}
   1421 	return(inf);
   1422 }
   1423 
   1424 /*
   1425  * addnum():	adds the line number to the column
   1426  *		Truncates from the front or pads with spaces as required.
   1427  *		Numbers are right justified.
   1428  *
   1429  *	buf	buffer to store the number
   1430  *	wdth	width of buffer to fill
   1431  *	line	line number
   1432  *
   1433  *		NOTE: numbers occupy part of the column. The posix
   1434  *		spec does not specify if -i processing should or should not
   1435  *		occur on number padding. The spec does say it occupies
   1436  *		part of the column. The usage of addnum	currently treats
   1437  *		numbers as part of the column so spaces may be replaced.
   1438  */
   1439 void
   1440 addnum(buf, wdth, line)
   1441 	char *buf;
   1442 	int wdth;
   1443 	int line;
   1444 {
   1445 	char *pt = buf + wdth;
   1446 
   1447 	do {
   1448 		*--pt = digs[line % 10];
   1449 		line /= 10;
   1450 	} while (line && (pt > buf));
   1451 
   1452 	/*
   1453 	 * pad with space as required
   1454 	 */
   1455 	while (pt > buf)
   1456 		*--pt = ' ';
   1457 }
   1458 
   1459 /*
   1460  * prhead():	prints the top of page header
   1461  *
   1462  *	buf	buffer with time field (and offset)
   1463  *	cnt	number of chars in buf
   1464  *	fname	fname field for header
   1465  *	pagcnt	page number
   1466  */
   1467 int
   1468 prhead(buf, fname, pagcnt)
   1469 	char *buf;
   1470 	char *fname;
   1471 	int pagcnt;
   1472 {
   1473 	int ips = 0;
   1474 	int ops = 0;
   1475 
   1476 	if ((putchar('\n') == EOF) || (putchar('\n') == EOF)) {
   1477 		pfail();
   1478 		return(1);
   1479 	}
   1480 	/*
   1481 	 * posix is not clear if the header is subject to line length
   1482 	 * restrictions. The specification for header line format
   1483 	 * in the spec clearly does not limit length. No pr currently
   1484 	 * restricts header length. However if we need to truncate in
   1485 	 * an reasonable way, adjust the length of the printf by
   1486 	 * changing HDFMT to allow a length max as an arguement printf.
   1487 	 * buf (which contains the offset spaces and time field could
   1488 	 * also be trimmed
   1489 	 *
   1490 	 * note only the offset (if any) is processed for tab expansion
   1491 	 */
   1492 	if (offst && otln(buf, offst, &ips, &ops, -1))
   1493 		return(1);
   1494 	(void)printf(HDFMT,buf+offst, fname, pagcnt);
   1495 	return(0);
   1496 }
   1497 
   1498 /*
   1499  * prtail():	pad page with empty lines (if required) and print page trailer
   1500  *		if requested
   1501  *
   1502  *	cnt	number of lines of padding needed
   1503  *	incomp	was a '\n' missing from last line output
   1504  */
   1505 int
   1506 prtail(cnt, incomp)
   1507 	int cnt;
   1508 	int incomp;
   1509 {
   1510 	if (nohead) {
   1511 		/*
   1512 		 * only pad with no headers when incomplete last line
   1513 		 */
   1514 		if (!incomp)
   1515 			return(0);
   1516 		if ((dspace && (putchar('\n') == EOF)) ||
   1517 		    (putchar('\n') == EOF)) {
   1518 			pfail();
   1519 			return(1);
   1520 		}
   1521 		return(0);
   1522 	}
   1523 
   1524 	/*
   1525 	 * if double space output two \n
   1526 	 */
   1527 	if (dspace)
   1528 		cnt *= 2;
   1529 
   1530 	/*
   1531 	 * if an odd number of lines per page, add an extra \n
   1532 	 */
   1533 	if (addone)
   1534 		++cnt;
   1535 
   1536 	/*
   1537 	 * pad page
   1538 	 */
   1539 	if (formfeed) {
   1540 		if ((incomp && (putchar('\n') == EOF)) ||
   1541 		    (putchar('\f') == EOF)) {
   1542 			pfail();
   1543 			return(1);
   1544 		}
   1545 		return(0);
   1546 	}
   1547 	cnt += TAILLEN;
   1548 	while (--cnt >= 0) {
   1549 		if (putchar('\n') == EOF) {
   1550 			pfail();
   1551 			return(1);
   1552 		}
   1553 	}
   1554 	return(0);
   1555 }
   1556 
   1557 /*
   1558  * terminate():	when a SIGINT is recvd
   1559  */
   1560 void
   1561 terminate(which_sig)
   1562 	int which_sig;
   1563 {
   1564 	flsh_errs();
   1565 	exit(1);
   1566 }
   1567 
   1568 
   1569 /*
   1570  * flsh_errs():	output saved up diagnostic messages after all normal
   1571  *		processing has completed
   1572  */
   1573 void
   1574 flsh_errs()
   1575 {
   1576 	char buf[BUFSIZ];
   1577 
   1578 	(void)fflush(stdout);
   1579 	(void)fflush(err);
   1580 	if (err == stderr)
   1581 		return;
   1582 	rewind(err);
   1583 	while (fgets(buf, BUFSIZ, err) != NULL)
   1584 		(void)fputs(buf, stderr);
   1585 }
   1586 
   1587 void
   1588 mfail()
   1589 {
   1590 	(void)fputs("pr: memory allocation failed\n", err);
   1591 }
   1592 
   1593 void
   1594 pfail()
   1595 {
   1596 	(void)fprintf(err, "pr: write failure, %s\n", strerror(errno));
   1597 }
   1598 
   1599 void
   1600 usage()
   1601 {
   1602 	(void)fputs(
   1603 	 "usage: pr [+page] [-col] [-adFmrt] [-e[ch][gap]] [-h header]\n",err);
   1604 	(void)fputs(
   1605 	 "          [-i[ch][gap]] [-l line] [-n[ch][width]] [-o offset]\n",err);
   1606 	(void)fputs(
   1607 	 "          [-s[ch]] [-w width] [-] [file ...]\n", err);
   1608 }
   1609 
   1610 /*
   1611  * setup:	Validate command args, initialize and perform sanity
   1612  *		checks on options
   1613  */
   1614 int
   1615 setup(argc, argv)
   1616 	int argc;
   1617 	char **argv;
   1618 {
   1619 	int c;
   1620 	int eflag = 0;
   1621 	int iflag = 0;
   1622 	int wflag = 0;
   1623 	int cflag = 0;
   1624 
   1625 	if (isatty(fileno(stdout))) {
   1626 		/*
   1627 		 * defer diagnostics until processing is done
   1628 		 */
   1629 		if ((err = tmpfile()) == NULL) {
   1630 		       (void)fputs("Cannot defer diagnostic messages\n",stderr);
   1631 		       return(1);
   1632 		}
   1633 	} else
   1634 		err = stderr;
   1635 	while ((c = egetopt(argc, argv, "#adFmrte?h:i?l:n?o:s?T:w:")) != -1) {
   1636 		switch (c) {
   1637 		case '+':
   1638 			if ((pgnm = atoi(eoptarg)) < 1) {
   1639 			    (void)fputs("pr: +page number must be 1 or more\n",
   1640 				err);
   1641 			    return(1);
   1642 			}
   1643 			break;
   1644 		case '-':
   1645 			if ((clcnt = atoi(eoptarg)) < 1) {
   1646 			    (void)fputs("pr: -columns must be 1 or more\n",err);
   1647 			    return(1);
   1648 			}
   1649 			if (clcnt > 1)
   1650 				++cflag;
   1651 			break;
   1652 		case 'a':
   1653 			++across;
   1654 			break;
   1655 		case 'd':
   1656 			++dspace;
   1657 			break;
   1658 		case 'e':
   1659 			++eflag;
   1660 			if ((eoptarg != NULL) &&
   1661 			    !isdigit((unsigned char)*eoptarg))
   1662 				inchar = *eoptarg++;
   1663 			else
   1664 				inchar = INCHAR;
   1665 			if ((eoptarg != NULL) &&
   1666 			    isdigit((unsigned char)*eoptarg)) {
   1667 				if ((ingap = atoi(eoptarg)) < 0) {
   1668 					(void)fputs(
   1669 					"pr: -e gap must be 0 or more\n", err);
   1670 					return(1);
   1671 				}
   1672 				if (ingap == 0)
   1673 					ingap = INGAP;
   1674 			} else if ((eoptarg != NULL) && (*eoptarg != '\0')) {
   1675 				(void)fprintf(err,
   1676 				      "pr: invalid value for -e %s\n", eoptarg);
   1677 				return(1);
   1678 			} else
   1679 				ingap = INGAP;
   1680 			break;
   1681 		case 'F':
   1682 			++formfeed;
   1683 			break;
   1684 		case 'h':
   1685 			header = eoptarg;
   1686 			break;
   1687 		case 'i':
   1688 			++iflag;
   1689 			if ((eoptarg != NULL) &&
   1690 			    !isdigit((unsigned char)*eoptarg))
   1691 				ochar = *eoptarg++;
   1692 			else
   1693 				ochar = OCHAR;
   1694 			if ((eoptarg != NULL) &&
   1695 			    isdigit((unsigned char)*eoptarg)) {
   1696 				if ((ogap = atoi(eoptarg)) < 0) {
   1697 					(void)fputs(
   1698 					"pr: -i gap must be 0 or more\n", err);
   1699 					return(1);
   1700 				}
   1701 				if (ogap == 0)
   1702 					ogap = OGAP;
   1703 			} else if ((eoptarg != NULL) && (*eoptarg != '\0')) {
   1704 				(void)fprintf(err,
   1705 				      "pr: invalid value for -i %s\n", eoptarg);
   1706 				return(1);
   1707 			} else
   1708 				ogap = OGAP;
   1709 			break;
   1710 		case 'l':
   1711 			if (!isdigit((unsigned char)*eoptarg) ||
   1712 			    ((lines=atoi(eoptarg)) < 1)) {
   1713 				(void)fputs(
   1714 				 "pr: Number of lines must be 1 or more\n",err);
   1715 				return(1);
   1716 			}
   1717 			break;
   1718 		case 'm':
   1719 			++merge;
   1720 			break;
   1721 		case 'n':
   1722 			if ((eoptarg != NULL) &&
   1723 			    !isdigit((unsigned char)*eoptarg))
   1724 				nmchar = *eoptarg++;
   1725 			else
   1726 				nmchar = NMCHAR;
   1727 			if ((eoptarg != NULL) &&
   1728 			    isdigit((unsigned char)*eoptarg)) {
   1729 				if ((nmwd = atoi(eoptarg)) < 1) {
   1730 					(void)fputs(
   1731 					"pr: -n width must be 1 or more\n",err);
   1732 					return(1);
   1733 				}
   1734 			} else if ((eoptarg != NULL) && (*eoptarg != '\0')) {
   1735 				(void)fprintf(err,
   1736 				      "pr: invalid value for -n %s\n", eoptarg);
   1737 				return(1);
   1738 			} else
   1739 				nmwd = NMWD;
   1740 			break;
   1741 		case 'o':
   1742 			if (!isdigit((unsigned char)*eoptarg) ||
   1743 			    ((offst = atoi(eoptarg))< 1)){
   1744 				(void)fputs("pr: -o offset must be 1 or more\n",
   1745 					err);
   1746 				return(1);
   1747 			}
   1748 			break;
   1749 		case 'r':
   1750 			++nodiag;
   1751 			break;
   1752 		case 's':
   1753 			++sflag;
   1754 			if (eoptarg == NULL)
   1755 				schar = SCHAR;
   1756 			else
   1757 				schar = *eoptarg++;
   1758 			if (eoptarg && *eoptarg != '\0') {
   1759 				(void)fprintf(err,
   1760 				      "pr: invalid value for -s %s\n", eoptarg);
   1761 				return(1);
   1762 			}
   1763 			break;
   1764 		case 'T':
   1765 			timefrmt = eoptarg;
   1766 			break;
   1767 		case 't':
   1768 			++nohead;
   1769 			break;
   1770 		case 'w':
   1771 			++wflag;
   1772 			if (!isdigit((unsigned char)*eoptarg) ||
   1773 			    ((pgwd = atoi(eoptarg)) < 1)){
   1774 				(void)fputs(
   1775 				   "pr: -w width must be 1 or more \n",err);
   1776 				return(1);
   1777 			}
   1778 			break;
   1779 		case '?':
   1780 		default:
   1781 			return(1);
   1782 		}
   1783 	}
   1784 
   1785 	/*
   1786 	 * default and sanity checks
   1787 	 */
   1788 	if (!clcnt) {
   1789 		if (merge) {
   1790 			if ((clcnt = argc - eoptind) <= 1) {
   1791 				clcnt = CLCNT;
   1792 				merge = 0;
   1793 			}
   1794 		} else
   1795 			clcnt = CLCNT;
   1796 	}
   1797 	if (across) {
   1798 		if (clcnt == 1) {
   1799 			(void)fputs("pr: -a flag requires multiple columns\n",
   1800 				err);
   1801 			return(1);
   1802 		}
   1803 		if (merge) {
   1804 			(void)fputs("pr: -m cannot be used with -a\n", err);
   1805 			return(1);
   1806 		}
   1807 	}
   1808 	if (!wflag) {
   1809 		if (sflag)
   1810 			pgwd = SPGWD;
   1811 		else
   1812 			pgwd = PGWD;
   1813 	}
   1814 	if (cflag || merge) {
   1815 		if (!eflag) {
   1816 			inchar = INCHAR;
   1817 			ingap = INGAP;
   1818 		}
   1819 		if (!iflag) {
   1820 			ochar = OCHAR;
   1821 			ogap = OGAP;
   1822 		}
   1823 	}
   1824 	if (cflag) {
   1825 		if (merge) {
   1826 			(void)fputs(
   1827 			  "pr: -m cannot be used with multiple columns\n", err);
   1828 			return(1);
   1829 		}
   1830 		if (nmwd) {
   1831 			colwd = (pgwd + 1 - (clcnt * (nmwd + 2)))/clcnt;
   1832 			pgwd = ((colwd + nmwd + 2) * clcnt) - 1;
   1833 		} else {
   1834 			colwd = (pgwd + 1 - clcnt)/clcnt;
   1835 			pgwd = ((colwd + 1) * clcnt) - 1;
   1836 		}
   1837 		if (colwd < 1) {
   1838 			(void)fprintf(err,
   1839 			  "pr: page width is too small for %d columns\n",clcnt);
   1840 			return(1);
   1841 		}
   1842 	}
   1843 	if (!lines)
   1844 		lines = LINES;
   1845 
   1846 	/*
   1847 	 * make sure long enough for headers. if not disable
   1848 	 */
   1849 	if (lines <= HEADLEN + TAILLEN)
   1850 		++nohead;
   1851 	else if (!nohead)
   1852 		lines -= HEADLEN + TAILLEN;
   1853 
   1854 	/*
   1855 	 * adjust for double space on odd length pages
   1856 	 */
   1857 	if (dspace) {
   1858 		if (lines == 1)
   1859 			dspace = 0;
   1860 		else {
   1861 			if (lines & 1)
   1862 				++addone;
   1863 			lines /= 2;
   1864 		}
   1865 	}
   1866 
   1867 	return(0);
   1868 }
   1869