Bug Summary

File:src/usr.bin/pr/pr.c
Warning:line 1116, column 9
Potential leak of memory pointed to by 'rc'

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) {
1
Assuming the condition is false
2
Taking false branch
143 perror("pledge");
144 exit(1);
145 }
146
147 if (signal(SIGINT2, SIG_IGN(void (*)(int))1) != SIG_IGN(void (*)(int))1)
3
Assuming the condition is false
4
Taking false branch
148 (void)signal(SIGINT2, terminate);
149 ret_val = setup(argc, argv);
150 if (!ret_val) {
5
Assuming 'ret_val' is 0
6
Taking true branch
151 /*
152 * select the output format based on options
153 */
154 if (merge)
7
Assuming 'merge' is not equal to 0
8
Taking true branch
155 ret_val = mulfile(argc, argv);
9
Calling 'mulfile'
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))
382 goto oomem;
383
384 /*
385 * allocate page header
386 */
387 if ((hbuf = malloc((unsigned)HDBUF512 + offst)) == NULL((void *)0))
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((void *)0))
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))
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))
411 goto oomem;
412 if ((lindy = calloc((unsigned)lines, sizeof(int))) == NULL((void *)0))
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((void *)0)) {
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 != NORMAL0)
522 break;
523 } /* end of for line */
524
525 if (rc != NORMAL0)
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))
10
Assuming the condition is false
11
Taking false branch
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))
12
Memory is allocated
13
Assuming the condition is false
14
Taking false branch
929 goto oomem;
930
931 /*
932 * page header
933 */
934 if ((hbuf = malloc((unsigned)HDBUF512 + offst)) == NULL((void *)0))
15
Assuming the condition is false
16
Taking false branch
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) {
17
Assuming 'j' is < 'clcnt'
18
Loop condition is true. Entering loop body
20
Assuming 'j' is >= 'clcnt'
21
Loop condition is false. Execution continues on line 962
953 if ((fbuf[j] = nxtfile(argc, argv, &fname, ohbuf, 1)) != NULL((void *)0)) {
19
Taking false branch
954 rc[j] = NORMAL0;
955 j++;
956 }
957 }
958
959 /*
960 * if no files, exit
961 */
962 if (j
21.1
'j' is 0
)
22
Taking false branch
963 clcnt = j;
964 else
965 goto out;
23
Control jumps to line 1116
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) {
24
Potential leak of memory pointed to by 'rc'
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}