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