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