GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/pr/pr.c Lines: 0 819 0.0 %
Date: 2016-12-06 Branches: 0 683 0.0 %

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