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