Bug Summary

File:src/usr.bin/pr/pr.c
Warning:line 507, column 15
Null pointer passed as 1st argument to memory set function

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name pr.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.bin/pr/obj -resource-dir /usr/local/lib/clang/13.0.0 -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/pr/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.bin/pr/pr.c
1/* $OpenBSD: pr.c,v 1.44 2020/12/13 15:36:36 jmc 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/stat.h>
38
39#include <ctype.h>
40#include <errno(*__errno()).h>
41#include <limits.h>
42#include <signal.h>
43#include <stdio.h>
44#include <stdarg.h>
45#include <stdlib.h>
46#include <string.h>
47#include <time.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. Adding 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 */
104int pgnm; /* starting page number */
105int skipping; /* we're skipping to page pgnum */
106int clcnt; /* number of columns */
107int colwd; /* column data width - multiple columns */
108int across; /* mult col flag; write across page */
109int dspace; /* double space flag */
110char inchar; /* expand input char */
111int ingap; /* expand input gap */
112int formfeed; /* use formfeed as trailer */
113int inform; /* grok formfeeds in input */
114char *header; /* header name instead of file name */
115char ochar; /* contract output char */
116int ogap; /* contract output gap */
117int lines; /* number of lines per page */
118int merge; /* merge multiple files in output */
119char nmchar; /* line numbering append char */
120int nmwd; /* width of line number field */
121int offst; /* number of page offset spaces */
122int nodiag; /* do not report file open errors */
123char schar; /* text column separation character */
124int sflag; /* -s option for multiple columns */
125int nohead; /* do not write head and trailer */
126int pgwd; /* page width with multiple col output */
127
128/*
129 * misc globals
130 */
131volatile sig_atomic_t ferr; /* error message delayed */
132int addone = 0; /* page length is odd with double space */
133int errcnt = 0; /* error count on file processing */
134int beheaded = 0; /* header / trailer link */
135char digs[] = "0123456789"; /* page number translation map */
136
137int
138main(int argc, char *argv[])
139{
140 int ret_val;
141
142 if (pledge("stdio rpath", NULL((void *)0)) == -1) {
143 perror("pledge");
144 exit(1);
145 }
146
147 if (signal(SIGINT2, SIG_IGN(void (*)(int))1) != SIG_IGN(void (*)(int))1)
148 (void)signal(SIGINT2, 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 */
174int
175onecol(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((void *)0);
189 char *lbuf;
190 char *nbuf;
191 char *hbuf = NULL((void *)0);
192 char *ohbuf;
193 FILE *inf = NULL((void *)0);
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)(LBUF8192 + off)*sizeof(char))) == NULL((void *)0))
208 goto oomem;
209
210 /*
211 * allocate header buffer
212 */
213 if ((hbuf = malloc((unsigned)(HDBUF512 + offst)*sizeof(char))) == NULL((void *)0))
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((void *)0)) {
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,LBUF8192,&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 != NORMAL0)
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-1)
306 break;
307 }
308
309 /*
310 * On EOF go to next file
311 */
312 if (rc == END-1)
313 break;
314 }
315
316 if (inf != stdin(&__sF[0]))
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
329oomem:
330 mfail();
331out:
332 free(obuf);
333 free(hbuf);
334 if (inf != NULL((void *)0) && inf != stdin(&__sF[0]))
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 */
343int
344vertcol(int argc, char *argv[])
345{
346 char *ptbf;
347 char **lstdat = NULL((void *)0);
348 int i;
349 int j;
350 int pln;
351 int *indy = NULL((void *)0);
352 int cnt;
353 int rc;
354 int cvc;
355 int *lindy = NULL((void *)0);
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((void *)0);
363 int mvc;
364 int tvc;
365 int cw = nmwd + 1;
366 int fullcol;
367 char *buf = NULL((void *)0);
368 char *hbuf = NULL((void *)0);
369 char *ohbuf;
370 char *fname;
371 FILE *inf = NULL((void *)0);
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((void *)0))
1
Assuming the condition is false
2
Taking false branch
382 goto oomem;
383
384 /*
385 * allocate page header
386 */
387 if ((hbuf = malloc((unsigned)HDBUF512 + offst)) == NULL((void *)0))
3
Assuming the condition is false
4
Taking false branch
388 goto oomem;
389
390 ohbuf = hbuf + offst;
391 if (offst)
5
Assuming 'offst' is 0
6
Taking false branch
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((void *)0))
7
Assuming the condition is false
8
Taking false branch
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((void *)0))
9
Assuming the condition is false
10
Taking false branch
405 goto oomem;
406
407 /*
408 * fast index lookups to locate start of lines
409 */
410 if ((indy = calloc((unsigned)lines, sizeof(int))) == NULL((void *)0))
11
Assuming the condition is false
12
Taking false branch
411 goto oomem;
412 if ((lindy = calloc((unsigned)lines, sizeof(int))) == NULL((void *)0))
13
Assuming the condition is false
14
Taking false branch
413 goto oomem;
414
415 if (nmwd)
15
Assuming 'nmwd' is 0
16
Taking false branch
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) {
17
Assuming 'j' is >= 'lines'
18
Loop condition is false. Execution continues on line 438
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((void *)0)) {
19
Assuming the condition is true
20
Loop condition is true. Entering loop body
439 pagecnt = 0;
440 lncnt = 0;
441
442 /*
443 * loop by "form"
444 */
445 for (;;) {
21
Loop condition is true. Entering loop body
446
447 /*
448 * loop by page
449 */
450 for(;;) {
22
Loop condition is true. Entering loop body
451
452 /*
453 * loop by column
454 */
455 cvc = 0;
456 for (i = 0; i < clcnt; ++i) {
23
Assuming 'i' is < 'clcnt'
24
Loop condition is true. Entering loop body
43
Assuming 'i' is < 'clcnt'
44
Loop condition is true. Entering loop body
457 j = 0;
458 /*
459 * if last column, do not pad
460 */
461 if (i == mclcnt)
25
Assuming 'i' is not equal to 'mclcnt'
26
Taking false branch
45
Assuming 'i' is not equal to 'mclcnt'
46
Taking false branch
462 stp = 1;
463 else
464 stp = 0;
465
466 /*
467 * loop by line
468 */
469 for(;;) {
27
Loop condition is true. Entering loop body
47
Loop condition is true. Entering loop body
62
Loop condition is true. Entering loop body
470 /*
471 * is this first column
472 */
473 if (!i
27.1
'i' is 0
47.1
'i' is 1
62.1
'i' is 1
) {
28
Taking true branch
48
Taking false branch
63
Taking false branch
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) {
29
Assuming 'nmwd' is 0
30
Taking false branch
49
Assuming 'nmwd' is 0
50
Taking false branch
64
Assuming 'nmwd' is 0
65
Taking false branch
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);
66
Value assigned to 'cnt'
493 vc[cvc++].cnt = cnt;
494 if (cnt >= 0) {
31
Assuming 'cnt' is >= 0
32
Taking true branch
51
Assuming 'cnt' is >= 0
52
Taking true branch
67
Assuming 'cnt' is >= 0
68
Taking true branch
495 ptbf += cnt;
69
Null pointer value stored to 'ptbf'
496
497 /*
498 * pad all but last column on page
499 */
500 if (!stp
32.1
'stp' is 0
52.1
'stp' is 0
69.1
'stp' is 0
) {
33
Taking true branch
53
Taking true branch
70
Taking true branch
501 /*
502 * pad to end of column
503 */
504 if (sflag)
34
Assuming 'sflag' is 0
35
Taking false branch
54
Assuming 'sflag' is 0
55
Taking false branch
71
Assuming 'sflag' is 0
72
Taking false branch
505 *ptbf++ = schar;
506 else if ((pln = col-cnt) > 0) {
36
Assuming the condition is false
37
Taking false branch
56
Assuming the condition is false
57
Taking false branch
73
Assuming the condition is true
74
Taking true branch
507 (void)memset(ptbf,
75
Null pointer passed as 1st argument to memory set function
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)
38
Assuming the condition is true
39
Taking true branch
58
Assuming the condition is false
59
Taking false branch
518 break;
40
Execution continues on line 525
519 } /* end of if cnt >= 0 */
520
521 if (rc != NORMAL0)
60
Assuming 'rc' is equal to NORMAL
61
Taking false branch
522 break;
523 } /* end of for line */
524
525 if (rc != NORMAL0)
41
Assuming 'rc' is equal to NORMAL
42
Taking false branch
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 != NORMAL0) && 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 != NORMAL0)
635 break;
636 }
637
638 /*
639 * if EOF go to next file
640 */
641 if (rc == END-1)
642 break;
643 }
644
645 if (inf != stdin(&__sF[0]))
646 (void)fclose(inf);
647 }
648
649 if (eoptind < argc){
650 goto out;
651 } else {
652 error = 0;
653 goto out;
654 }
655
656oomem:
657 mfail();
658out:
659 free(buf);
660 free(hbuf);
661 free(vc);
662 free(lstdat);
663 free(lindy);
664 if (inf != NULL((void *)0) && inf != stdin(&__sF[0]))
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 */
673int
674horzcol(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((void *)0);
687 char *hbuf = NULL((void *)0);
688 char *ohbuf;
689 char *fname;
690 FILE *inf = NULL((void *)0);
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((void *)0))
698 goto oomem;
699
700 /*
701 * page header
702 */
703 if ((hbuf = malloc((unsigned)HDBUF512 + offst)) == NULL((void *)0))
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((void *)0)) {
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 != NORMAL0)
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 != NORMAL0)
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-1)
802 break;
803 }
804 /*
805 * if EOF go to next file
806 */
807 if (rc == END-1)
808 break;
809 }
810 if (inf != stdin(&__sF[0]))
811 (void)fclose(inf);
812 }
813 if (eoptind < argc){
814 goto out;
815 } else {
816 error = 0;
817 goto out;
818 }
819
820oomem:
821 mfail();
822out:
823 free(buf);
824 free(hbuf);
825 if (inf != NULL((void *)0) && inf != stdin(&__sF[0]))
826 (void)fclose(inf);
827 return error;
828}
829
830struct ferrlist {
831 struct ferrlist *next;
832 char *buf;
833};
834struct ferrlist *ferrhead, *ferrtail;
835
836/*
837 * flsh_errs(): output saved up diagnostic messages after all normal
838 * processing has completed
839 */
840void
841flsh_errs(void)
842{
843 struct ferrlist *f;
844
845 if (ferr) {
846 for (f = ferrhead; f; f = f->next)
847 (void)write(STDERR_FILENO2, f->buf, strlen(f->buf));
848 }
849}
850
851static void ferrout(char *fmt, ...) __attribute__((format (printf, 1, 2)));
852static void
853ferrout(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)__builtin_va_start(ap, fmt);
861 if (ferr == 0)
862 vfprintf(stderr(&__sF[2]), fmt, ap);
863 else {
864 sigemptyset(&block);
865 sigaddset(&block, SIGINT2);
866 sigprocmask(SIG_BLOCK1, &block, &oblock);
867
868 if (vasprintf(&p, fmt, ap) == -1 || (f = malloc(sizeof(*f))) == NULL((void *)0)) {
869 va_end(ap)__builtin_va_end(ap);
870 va_start(ap, fmt)__builtin_va_start(ap, fmt);
871 flsh_errs();
872 vfprintf(stderr(&__sF[2]), fmt, ap);
873 fputs("pr: memory allocation failed\n", stderr(&__sF[2]));
874 exit(1);
875 }
876
877 f->next = NULL((void *)0);
878 f->buf = p;
879 if (ferrhead == NULL((void *)0))
880 ferrhead = f;
881 if (ferrtail)
882 ferrtail->next = f;
883 ferrtail = f;
884 sigprocmask(SIG_SETMASK3, &oblock, NULL((void *)0));
885 }
886 va_end(ap)__builtin_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 */
893int
894mulfile(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((void *)0);
904 int actf;
905 int lncnt;
906 int col;
907 int pagecnt;
908 int fproc;
909 char *buf = NULL((void *)0);
910 char *hbuf = NULL((void *)0);
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((void *)0))
923 goto oomem;
924
925 /*
926 * array of int *, one for each operand
927 */
928 if ((rc = calloc((unsigned)clcnt, sizeof(int))) == NULL((void *)0))
929 goto oomem;
930
931 /*
932 * page header
933 */
934 if ((hbuf = malloc((unsigned)HDBUF512 + offst)) == NULL((void *)0))
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((void *)0)) {
954 rc[j] = NORMAL0;
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((void *)0))
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] == NORMAL0 ) {
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-1) {
1041 /*
1042 * EOF close file
1043 */
1044 if (fbuf[j] != stdin(&__sF[0]))
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-1)
1098 rc[j] = NORMAL0;
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
1113oomem:
1114 mfail();
1115out:
1116 if (fbuf) {
1117 for (j = 0; j < clcnt; j++) {
1118 if (fbuf[j] && fbuf[j] != stdin(&__sF[0]))
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 */
1141int
1142inln(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)(!__isthreaded ? (--(inf)->_r < 0 ? __srget(inf) : (int
)(*(inf)->_p++)) : (getc)(inf))
) != EOF(-1))) {
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(-1))) {
1171 *cnt = -1;
1172 return(END-1); /* 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'\f'))
1183 break;
1184 *ptbuf++ = ch;
1185 }
1186 } else {
1187 /*
1188 * no expansion
1189 */
1190 while ((--lim >= 0) && ((ch = getc(inf)(!__isthreaded ? (--(inf)->_r < 0 ? __srget(inf) : (int
)(*(inf)->_p++)) : (getc)(inf))
) != EOF(-1))) {
1191 if (ch == '\n' || (inform && ch == INFF'\f'))
1192 break;
1193 *ptbuf++ = ch;
1194 }
1195 }
1196 col = ptbuf - buf;
1197 if (ch == EOF(-1)) {
1198 *mor = 0;
1199 *cps = 0;
1200 *cnt = col ? col : -1;
1201 return(END-1);
1202 }
1203 if (inform && ch == INFF'\f') {
1204 *mor = 0;
1205 *cps = 0;
1206 *cnt = col;
1207 return(FORM1);
1208 }
1209 if (ch == '\n') {
1210 /*
1211 * entire line processed
1212 */
1213 *mor = 0;
1214 *cps = 0;
1215 *cnt = col;
1216 return(NORMAL0);
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)(!__isthreaded ? (--(inf)->_r < 0 ? __srget(inf) : (int
)(*(inf)->_p++)) : (getc)(inf))
) != EOF(-1)) {
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(NORMAL0);
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 */
1255int
1256otln(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(' ')(!__isthreaded ? __sputc(' ', (&__sF[1])) : (putc)(' ', (
&__sF[1])))
;
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)(!__isthreaded ? __sputc(ochar, (&__sF[1])) : (putc)(ochar
, (&__sF[1])))
== EOF(-1)) {
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(' ')(!__isthreaded ? __sputc(' ', (&__sF[1])) : (putc)(' ', (
&__sF[1])))
== EOF(-1)) {
1322 pfail();
1323 return(1);
1324 }
1325 ++ops;
1326 }
1327
1328 /*
1329 * output non space char
1330 */
1331 if (putchar(*buf++)(!__isthreaded ? __sputc(*buf++, (&__sF[1])) : (putc)(*buf
++, (&__sF[1])))
== EOF(-1)) {
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(' ')(!__isthreaded ? __sputc(' ', (&__sF[1])) : (putc)(' ', (
&__sF[1])))
;
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)(!__isthreaded ? __sputc(ochar, (&__sF[1])) : (putc)(ochar
, (&__sF[1])))
== EOF(-1)) {
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(' ')(!__isthreaded ? __sputc(' ', (&__sF[1])) : (putc)(' ', (
&__sF[1])))
== EOF(-1)) {
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(&__sF[1])) < 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')(!__isthreaded ? __sputc('\n', (&__sF[1])) : (putc)('\n',
(&__sF[1])))
== EOF(-1)) || (dspace && (putchar('\n')(!__isthreaded ? __sputc('\n', (&__sF[1])) : (putc)('\n',
(&__sF[1])))
== EOF(-1)))) {
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 */
1412int
1413inskip(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)(!__isthreaded ? (--(inf)->_r < 0 ? __srget(inf) : (int
)(*(inf)->_p++)) : (getc)(inf))
) != EOF(-1)) {
1421 if ((c == '\n') && (--cnt == 0))
1422 break;
1423 }
1424 if (c == EOF(-1)) {
1425 if (inf != stdin(&__sF[0]))
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 */
1441FILE *
1442nxtfile(int argc, char *argv[], char **fname, char *buf, int dt)
1443{
1444 FILE *inf = NULL((void *)0);
1445 struct tm *timeptr = NULL((void *)0);
1446 struct stat statbuf;
1447 time_t curtime;
1448 static int twice = -1;
1449
1450 ++twice;
1451 if (eoptind >= argc) {
1452 /*
1453 * no file listed; default, use standard input
1454 */
1455 if (twice)
1456 return(NULL((void *)0));
1457 clearerr(stdin)(!__isthreaded ? ((void)(((&__sF[0]))->_flags &= ~
(0x0040|0x0020))) : (clearerr)((&__sF[0])))
;
1458 inf = stdin(&__sF[0]);
1459 if (header != NULL((void *)0))
1460 *fname = header;
1461 else
1462 *fname = FNAME"";
1463 if (nohead)
1464 return(inf);
1465 curtime = time(NULL((void *)0));
1466 timeptr = localtime(&curtime);
1467 }
1468 for (; eoptind < argc; ++eoptind) {
1469 if (strcmp(argv[eoptind], "-") == 0) {
1470 /*
1471 * process a "-" for filename
1472 */
1473 clearerr(stdin)(!__isthreaded ? ((void)(((&__sF[0]))->_flags &= ~
(0x0040|0x0020))) : (clearerr)((&__sF[0])))
;
1474 inf = stdin(&__sF[0]);
1475 if (header != NULL((void *)0))
1476 *fname = header;
1477 else
1478 *fname = FNAME"";
1479 ++eoptind;
1480 if (nohead || (dt && twice))
1481 return(inf);
1482 curtime = time(NULL((void *)0));
1483 timeptr = localtime(&curtime);
1484 } else {
1485 /*
1486 * normal file processing
1487 */
1488 if ((inf = fopen(argv[eoptind], "r")) == NULL((void *)0)) {
1489 ++errcnt;
1490 if (nodiag)
1491 continue;
1492 ferrout("pr: Cannot open %s, %s\n",
1493 argv[eoptind], strerror(errno(*__errno())));
1494 continue;
1495 }
1496 if (header != NULL((void *)0))
1497 *fname = header;
1498 else if (dt)
1499 *fname = FNAME"";
1500 else
1501 *fname = argv[eoptind];
1502 ++eoptind;
1503 if (nohead || (dt && twice))
1504 return(inf);
1505
1506 if (dt) {
1507 curtime = time(NULL((void *)0));
1508 timeptr = localtime(&curtime);
1509 } else {
1510 if (fstat(fileno(inf)(!__isthreaded ? ((inf)->_file) : (fileno)(inf)), &statbuf) == -1) {
1511 ++errcnt;
1512 (void)fclose(inf);
1513 ferrout("pr: Cannot stat %s, %s\n",
1514 argv[eoptind], strerror(errno(*__errno())));
1515 return(NULL((void *)0));
1516 }
1517 timeptr = localtime(&(statbuf.st_mtimest_mtim.tv_sec));
1518 }
1519 }
1520 break;
1521 }
1522 if (inf == NULL((void *)0))
1523 return(NULL((void *)0));
1524
1525 /*
1526 * set up time field used in header
1527 */
1528 if (strftime(buf, HDBUF512, TIMEFMT"%b %e %H:%M %Y", timeptr) == 0) {
1529 ++errcnt;
1530 if (inf != stdin(&__sF[0]))
1531 (void)fclose(inf);
1532 ferrout("pr: time conversion failed\n");
1533 return(NULL((void *)0));
1534 }
1535 return(inf);
1536}
1537
1538/*
1539 * addnum(): adds the line number to the column
1540 * Truncates from the front or pads with spaces as required.
1541 * Numbers are right justified.
1542 *
1543 * buf buffer to store the number
1544 * wdth width of buffer to fill
1545 * line line number
1546 *
1547 * NOTE: numbers occupy part of the column. The posix
1548 * spec does not specify if -i processing should or should not
1549 * occur on number padding. The spec does say it occupies
1550 * part of the column. The usage of addnum currently treats
1551 * numbers as part of the column so spaces may be replaced.
1552 */
1553void
1554addnum(char *buf, int wdth, int line)
1555{
1556 char *pt = buf + wdth;
1557
1558 do {
1559 *--pt = digs[line % 10];
1560 line /= 10;
1561 } while (line && (pt > buf));
1562
1563 /*
1564 * pad with space as required
1565 */
1566 while (pt > buf)
1567 *--pt = ' ';
1568}
1569
1570/*
1571 * prhead(): prints the top of page header
1572 *
1573 * buf buffer with time field (and offset)
1574 * cnt number of chars in buf
1575 * fname fname field for header
1576 * pagcnt page number
1577 *
1578 * prhead() should be used carefully, we don't want to print out headers
1579 * for null input files or orphan headers at the end of files, and also
1580 * trailer processing is typically conditional on whether you've called
1581 * prhead() at least once for a file and incremented pagecnt. Exactly
1582 * how to determine whether to print a header is a little different in
1583 * the context each output mode, but we let the caller figure that out.
1584 */
1585int
1586prhead(char *buf, char *fname, int pagcnt)
1587{
1588 int ips = 0;
1589 int ops = 0;
1590
1591 beheaded = 1;
1592
1593 if (skipping && pagcnt >= pgnm)
1594 skipping = 0;
1595
1596 if (nohead || skipping)
1597 return (0);
1598
1599 if ((putchar('\n')(!__isthreaded ? __sputc('\n', (&__sF[1])) : (putc)('\n',
(&__sF[1])))
== EOF(-1)) || (putchar('\n')(!__isthreaded ? __sputc('\n', (&__sF[1])) : (putc)('\n',
(&__sF[1])))
== EOF(-1))) {
1600 pfail();
1601 return(1);
1602 }
1603 /*
1604 * posix is not clear if the header is subject to line length
1605 * restrictions. The specification for header line format
1606 * in the spec clearly does not limit length. No pr currently
1607 * restricts header length. However if we need to truncate in
1608 * an reasonable way, adjust the length of the printf by
1609 * changing HDFMT to allow a length max as an argument printf.
1610 * buf (which contains the offset spaces and time field could
1611 * also be trimmed
1612 *
1613 * note only the offset (if any) is processed for tab expansion
1614 */
1615 if (offst && otln(buf, offst, &ips, &ops, -1))
1616 return(1);
1617 (void)printf(HDFMT"%s %s Page %d\n\n\n",buf+offst, fname, pagcnt);
1618 return(0);
1619}
1620
1621/*
1622 * prtail(): pad page with empty lines (if required) and print page trailer
1623 * if requested
1624 *
1625 * cnt number of lines of padding needed
1626 * incomp was a '\n' missing from last line output
1627 *
1628 * prtail() can now be invoked unconditionally, with the notion that if
1629 * we haven't printed a header, there is no need for a trailer
1630 */
1631int
1632prtail(int cnt, int incomp)
1633{
1634 /*
1635 * if were's skipping to page N or haven't put out anything yet just exit
1636 */
1637 if (skipping || beheaded == 0)
1638 return (0);
1639 beheaded = 0;
1640
1641 /*
1642 * if noheaders, only terminate an incomplete last line
1643 */
1644 if (nohead) {
1645
1646 if (incomp) {
1647 if (dspace)
1648 if (putchar('\n')(!__isthreaded ? __sputc('\n', (&__sF[1])) : (putc)('\n',
(&__sF[1])))
== EOF(-1)) {
1649 pfail();
1650 return(1);
1651 }
1652 if (putchar('\n')(!__isthreaded ? __sputc('\n', (&__sF[1])) : (putc)('\n',
(&__sF[1])))
== EOF(-1)) {
1653 pfail();
1654 return(1);
1655 }
1656 }
1657 /*
1658 * but honor the formfeed request
1659 */
1660 if (formfeed)
1661 if (putchar(OUTFF)(!__isthreaded ? __sputc('\f', (&__sF[1])) : (putc)('\f',
(&__sF[1])))
== EOF(-1)) {
1662 pfail();
1663 return(1);
1664 }
1665
1666 } else {
1667
1668 /*
1669 * if double space output two \n
1670 *
1671 * XXX this all seems bogus, why are we doing it here???
1672 * page length is in terms of output lines and only the input is
1673 * supposed to be double spaced... otln() users should be doing
1674 * something like linect+=(dspace ? 2:1).
1675 */
1676 if (dspace)
1677 cnt *= 2;
1678
1679 /*
1680 * if an odd number of lines per page, add an extra \n
1681 */
1682 if (addone)
1683 ++cnt;
1684
1685 /*
1686 * either put out a form-feed or pad page with blanks
1687 */
1688 if (formfeed) {
1689 if (incomp)
1690 if (putchar('\n')(!__isthreaded ? __sputc('\n', (&__sF[1])) : (putc)('\n',
(&__sF[1])))
== EOF(-1)) {
1691 pfail();
1692 return(1);
1693 }
1694 if (putchar(OUTFF)(!__isthreaded ? __sputc('\f', (&__sF[1])) : (putc)('\f',
(&__sF[1])))
== EOF(-1)) {
1695 pfail();
1696 return(1);
1697 }
1698
1699 } else {
1700
1701 if (incomp)
1702 cnt++;
1703
1704 cnt += TAILLEN5;
1705 while (--cnt >= 0) {
1706 if (putchar('\n')(!__isthreaded ? __sputc('\n', (&__sF[1])) : (putc)('\n',
(&__sF[1])))
== EOF(-1)) {
1707 pfail();
1708 return(1);
1709 }
1710 }
1711 }
1712 }
1713
1714 return(0);
1715}
1716
1717/*
1718 * terminate(): when a SIGINT is recvd
1719 */
1720/*ARGSUSED*/
1721void
1722terminate(int which_sig)
1723{
1724 flsh_errs();
1725 _exit(1);
1726}
1727
1728void
1729mfail(void)
1730{
1731 ferrout("pr: memory allocation failed\n");
1732}
1733
1734void
1735pfail(void)
1736{
1737 ferrout("pr: write failure, %s\n", strerror(errno(*__errno())));
1738}
1739
1740void
1741usage(void)
1742{
1743 ferrout(
1744 "usage: pr [+page] [-column] [-adFfmrt] [-e[char][gap]] [-h header]\n");
1745 ferrout(
1746 "\t[-i[char][gap]] [-l lines] [-n[char][width]] [-o offset] [-s[char]]\n");
1747 ferrout(
1748 "\t[-w width] [file ...]\n");
1749}
1750
1751/*
1752 * setup: Validate command args, initialize and perform sanity
1753 * checks on options
1754 */
1755int
1756setup(int argc, char *argv[])
1757{
1758 int c;
1759 int eflag = 0;
1760 int iflag = 0;
1761 int wflag = 0;
1762 int cflag = 0;
1763 const char *errstr;
1764
1765 if (isatty(fileno(stdout)(!__isthreaded ? (((&__sF[1]))->_file) : (fileno)((&
__sF[1])))
))
1766 ferr = 1;
1767
1768 while ((c = egetopt(argc, argv, "#adfFmrte?h:i?l:n?o:s?w:")) != -1) {
1769 switch (c) {
1770 case '+':
1771 pgnm = strtonum(eoptarg, 1, INT_MAX2147483647, &errstr);
1772 if (errstr) {
1773 ferrout("pr: +page number is %s: %s\n", errstr, eoptarg);
1774 return(1);
1775 }
1776 skipping = 1;
1777 break;
1778 case '-':
1779 clcnt = strtonum(eoptarg, 1, INT_MAX2147483647, &errstr);
1780 if (errstr) {
1781 ferrout("pr: -columns number is %s: %s\n", errstr, eoptarg);
1782 return(1);
1783 }
1784 if (clcnt > 1)
1785 cflag = 1;
1786 break;
1787 case 'a':
1788 across = 1;
1789 break;
1790 case 'd':
1791 dspace = 1;
1792 break;
1793 case 'e':
1794 eflag = 1;
1795 if ((eoptarg != NULL((void *)0)) && !isdigit((unsigned char)*eoptarg))
1796 inchar = *eoptarg++;
1797 else
1798 inchar = INCHAR'\t';
1799 if ((eoptarg != NULL((void *)0)) && isdigit((unsigned char)*eoptarg)) {
1800 ingap = strtonum(eoptarg, 0, INT_MAX2147483647, &errstr);
1801 if (errstr) {
1802 ferrout("pr: -e gap is %s: %s\n", errstr, eoptarg);
1803 return(1);
1804 }
1805 if (ingap == 0)
1806 ingap = INGAP8;
1807 } else if ((eoptarg != NULL((void *)0)) && (*eoptarg != '\0')) {
1808 ferrout("pr: invalid value for -e %s\n", eoptarg);
1809 return(1);
1810 } else
1811 ingap = INGAP8;
1812 break;
1813 case 'f':
1814 case 'F':
1815 formfeed = 1;
1816 break;
1817 case 'h':
1818 header = eoptarg;
1819 break;
1820 case 'i':
1821 iflag = 1;
1822 if ((eoptarg != NULL((void *)0)) && !isdigit((unsigned char)*eoptarg))
1823 ochar = *eoptarg++;
1824 else
1825 ochar = OCHAR'\t';
1826 if ((eoptarg != NULL((void *)0)) && isdigit((unsigned char)*eoptarg)) {
1827 ogap = strtonum(eoptarg, 0, INT_MAX2147483647, &errstr);
1828 if (errstr) {
1829 ferrout("pr: -i gap is %s: %s\n", errstr, eoptarg);
1830 return(1);
1831 }
1832 if (ogap == 0)
1833 ogap = OGAP8;
1834 } else if ((eoptarg != NULL((void *)0)) && (*eoptarg != '\0')) {
1835 ferrout("pr: invalid value for -i %s\n", eoptarg);
1836 return(1);
1837 } else
1838 ogap = OGAP8;
1839 break;
1840 case 'l':
1841 lines = strtonum(eoptarg, 1, INT_MAX2147483647, &errstr);
1842 if (errstr) {
1843 ferrout("pr: number of lines is %s: %s\n", errstr, eoptarg);
1844 return(1);
1845 }
1846 break;
1847 case 'm':
1848 merge = 1;
1849 break;
1850 case 'n':
1851 if ((eoptarg != NULL((void *)0)) && !isdigit((unsigned char)*eoptarg))
1852 nmchar = *eoptarg++;
1853 else
1854 nmchar = NMCHAR'\t';
1855 if ((eoptarg != NULL((void *)0)) && isdigit((unsigned char)*eoptarg)) {
1856 nmwd = strtonum(eoptarg, 1, INT_MAX2147483647, &errstr);
1857 if (errstr) {
1858 ferrout("pr: -n width is %s: %s\n", errstr, eoptarg);
1859 return(1);
1860 }
1861 } else if ((eoptarg != NULL((void *)0)) && (*eoptarg != '\0')) {
1862 ferrout("pr: invalid value for -n %s\n", eoptarg);
1863 return(1);
1864 } else
1865 nmwd = NMWD5;
1866 break;
1867 case 'o':
1868 offst = strtonum(eoptarg, 1, INT_MAX2147483647, &errstr);
1869 if (errstr) {
1870 ferrout("pr: -o offset is %s: %s\n", errstr, eoptarg);
1871 return(1);
1872 }
1873 break;
1874 case 'r':
1875 nodiag = 1;
1876 break;
1877 case 's':
1878 sflag = 1;
1879 if (eoptarg == NULL((void *)0))
1880 schar = SCHAR'\t';
1881 else {
1882 schar = *eoptarg++;
1883 if (*eoptarg != '\0') {
1884 ferrout("pr: invalid value for -s %s\n", eoptarg);
1885 return(1);
1886 }
1887 }
1888 break;
1889 case 't':
1890 nohead = 1;
1891 break;
1892 case 'w':
1893 wflag = 1;
1894 pgwd = strtonum(eoptarg, 1, INT_MAX2147483647, &errstr);
1895 if (errstr) {
1896 ferrout("pr: -w width is %s: %s\n", errstr, eoptarg);
1897 return(1);
1898 }
1899 break;
1900 default:
1901 return(1);
1902 }
1903 }
1904
1905 /*
1906 * default and sanity checks
1907 */
1908 inform++;
1909
1910 if (!clcnt) {
1911 if (merge) {
1912 if ((clcnt = argc - eoptind) <= 1) {
1913 clcnt = CLCNT1;
1914#ifdef stupid
1915 merge = 0;
1916#endif
1917 }
1918 } else
1919 clcnt = CLCNT1;
1920 }
1921 if (across) {
1922 if (clcnt == 1) {
1923 ferrout("pr: -a flag requires multiple columns\n");
1924 return(1);
1925 }
1926 if (merge) {
1927 ferrout("pr: -m cannot be used with -a\n");
1928 return(1);
1929 }
1930 }
1931 if (!wflag) {
1932 if (sflag)
1933 pgwd = SPGWD512;
1934 else
1935 pgwd = PGWD72;
1936 }
1937 if (cflag || merge) {
1938 if (!eflag) {
1939 inchar = INCHAR'\t';
1940 ingap = INGAP8;
1941 }
1942 if (!iflag) {
1943 ochar = OCHAR'\t';
1944 ogap = OGAP8;
1945 }
1946 }
1947 if (cflag) {
1948 if (merge) {
1949 ferrout("pr: -m cannot be used with multiple columns\n");
1950 return(1);
1951 }
1952 if (nmwd) {
1953 colwd = (pgwd + 1 - (clcnt * (nmwd + 2)))/clcnt;
1954 pgwd = ((colwd + nmwd + 2) * clcnt) - 1;
1955 } else {
1956 colwd = (pgwd + 1 - clcnt)/clcnt;
1957 pgwd = ((colwd + 1) * clcnt) - 1;
1958 }
1959 if (colwd < 1) {
1960 ferrout("pr: page width is too small for %d columns\n",clcnt);
1961 return(1);
1962 }
1963 }
1964 if (!lines)
1965 lines = LINES66;
1966
1967 /*
1968 * make sure long enough for headers. if not disable
1969 */
1970 if (lines <= HEADLEN5 + TAILLEN5)
1971 nohead = 1;
1972 else if (!nohead)
1973 lines -= HEADLEN5 + TAILLEN5;
1974
1975 /*
1976 * adjust for double space on odd length pages
1977 */
1978 if (dspace) {
1979 if (lines == 1)
1980 dspace = 0;
1981 else {
1982 if (lines & 1)
1983 ++addone;
1984 lines /= 2;
1985 }
1986 }
1987
1988 return(0);
1989}