GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/pr/pr.c Lines: 0 727 0.0 %
Date: 2017-11-13 Branches: 0 648 0.0 %

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