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