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.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 */ 104 int pgnm; /* starting page number */ 105 int skipping; /* we're skipping to page pgnum */ 106 int clcnt; /* number of columns */ 107 int colwd; /* column data width - multiple columns */ 108 int across; /* mult col flag; write across page */ 109 int dspace; /* double space flag */ 110 char inchar; /* expand input char */ 111 int ingap; /* expand input gap */ 112 int formfeed; /* use formfeed as trailer */ 113 int inform; /* grok formfeeds in input */ 114 char *header; /* header name instead of file name */ 115 char ochar; /* contract output char */ 116 int ogap; /* contract output gap */ 117 int lines; /* number of lines per page */ 118 int merge; /* merge multiple files in output */ 119 char nmchar; /* line numbering append char */ 120 int nmwd; /* width of line number field */ 121 int offst; /* number of page offset spaces */ 122 int nodiag; /* do not report file open errors */ 123 char schar; /* text column separation character */ 124 int sflag; /* -s option for multiple columns */ 125 int nohead; /* do not write head and trailer */ 126 int pgwd; /* page width with multiple col output */ 127 128 /* 129 * misc globals 130 */ 131 volatile sig_atomic_t ferr; /* error message delayed */ 132 int addone = 0; /* page length is odd with double space */ 133 int errcnt = 0; /* error count on file processing */ 134 int beheaded = 0; /* header / trailer link */ 135 char digs[] = "0123456789"; /* page number translation map */ 136 137 int 138 main(int argc, char *argv[]) 139 { 140 int ret_val; 141 142 if (pledge("stdio rpath", NULL) == -1) { 143 perror("pledge"); 144 exit(1); 145 } 146 147 if (signal(SIGINT, SIG_IGN) != SIG_IGN) 148 (void)signal(SIGINT, terminate); 149 ret_val = setup(argc, argv); 150 if (!ret_val) { 151 /* 152 * select the output format based on options 153 */ 154 if (merge) 155 ret_val = mulfile(argc, argv); 156 else if (clcnt == 1) 157 ret_val = onecol(argc, argv); 158 else if (across) 159 ret_val = horzcol(argc, argv); 160 else 161 ret_val = vertcol(argc, argv); 162 } else 163 usage(); 164 flsh_errs(); 165 if (errcnt || ret_val) 166 exit(1); 167 return(0); 168 } 169 170 /* 171 * onecol: print files with only one column of output. 172 * Line length is unlimited. 173 */ 174 int 175 onecol(int argc, char *argv[]) 176 { 177 int off; 178 int lrgln; 179 int linecnt; 180 int num; 181 int cnt; 182 int rc; 183 int lncnt; 184 int pagecnt; 185 int ips; 186 int ops; 187 int cps; 188 char *obuf = NULL; 189 char *lbuf; 190 char *nbuf; 191 char *hbuf = NULL; 192 char *ohbuf; 193 FILE *inf = NULL; 194 char *fname; 195 int mor; 196 int error = 1; 197 198 if (nmwd) 199 num = nmwd + 1; 200 else 201 num = 0; 202 off = num + offst; 203 204 /* 205 * allocate line buffer 206 */ 207 if ((obuf = malloc((unsigned)(LBUF + off)*sizeof(char))) == NULL) 208 goto oomem; 209 210 /* 211 * allocate header buffer 212 */ 213 if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL) 214 goto oomem; 215 216 ohbuf = hbuf + offst; 217 nbuf = obuf + offst; 218 lbuf = nbuf + num; 219 220 if (num) 221 nbuf[--num] = nmchar; 222 223 if (offst) { 224 (void)memset(obuf, (int)' ', offst); 225 (void)memset(hbuf, (int)' ', offst); 226 } 227 228 /* 229 * loop by file 230 */ 231 while ((inf = nxtfile(argc, argv, &fname, ohbuf, 0)) != NULL) { 232 pagecnt = 0; 233 lncnt = 0; 234 235 /* 236 * loop by "form" 237 */ 238 for(;;) { 239 240 /* 241 * loop by page 242 */ 243 for(;;) { 244 linecnt = 0; 245 lrgln = 0; 246 ops = 0; 247 ips = 0; 248 cps = 0; 249 250 /* 251 * loop by line 252 */ 253 while (linecnt < lines) { 254 255 /* 256 * input next line 257 */ 258 rc = inln(inf,lbuf,LBUF,&cnt,&cps,0,&mor); 259 if (cnt >= 0) { 260 if (!lrgln) 261 if (!linecnt && prhead(hbuf, fname, ++pagecnt)) 262 goto out; 263 264 /* 265 * start new line or continue a long one 266 */ 267 if (!lrgln) { 268 if (num) 269 addnum(nbuf, num, ++lncnt); 270 if (otln(obuf,cnt+off, &ips, &ops, mor)) 271 goto out; 272 } else 273 if (otln(lbuf, cnt, &ips, &ops, mor)) 274 goto out; 275 276 /* 277 * if line bigger than buffer, get more 278 */ 279 if (mor) { 280 lrgln = 1; 281 } else { 282 /* 283 * whole line rcvd. reset tab proc. state 284 */ 285 ++linecnt; 286 lrgln = 0; 287 ops = 0; 288 ips = 0; 289 } 290 } 291 292 if (rc != NORMAL) 293 break; 294 } 295 296 /* 297 * fill to end of page 298 */ 299 if (prtail(lines - linecnt, lrgln)) 300 goto out; 301 302 /* 303 * unless END continue 304 */ 305 if (rc == END) 306 break; 307 } 308 309 /* 310 * On EOF go to next file 311 */ 312 if (rc == END) 313 break; 314 } 315 316 if (inf != stdin) 317 (void)fclose(inf); 318 } 319 /* 320 * If we didn't process all the files, return error 321 */ 322 if (eoptind < argc) { 323 goto out; 324 } else { 325 error = 0; 326 goto out; 327 } 328 329 oomem: 330 mfail(); 331 out: 332 free(obuf); 333 free(hbuf); 334 if (inf != NULL && inf != stdin) 335 (void)fclose(inf); 336 return error; 337 } 338 339 /* 340 * vertcol: print files with more than one column of output down a page 341 * the general approach is to buffer a page of data, then print 342 */ 343 int 344 vertcol(int argc, char *argv[]) 345 { 346 char *ptbf; 347 char **lstdat = NULL; 348 int i; 349 int j; 350 int pln; 351 int *indy = NULL; 352 int cnt; 353 int rc; 354 int cvc; 355 int *lindy = NULL; 356 int lncnt; 357 int stp; 358 int pagecnt; 359 int col = colwd + 1; 360 int mxlen = pgwd + offst + 1; 361 int mclcnt = clcnt - 1; 362 struct vcol *vc = NULL; 363 int mvc; 364 int tvc; 365 int cw = nmwd + 1; 366 int fullcol; 367 char *buf = NULL; 368 char *hbuf = NULL; 369 char *ohbuf; 370 char *fname; 371 FILE *inf = NULL; 372 int ips = 0; 373 int cps = 0; 374 int ops = 0; 375 int mor = 0; 376 int error = 1; 377 378 /* 379 * allocate page buffer 380 */ 381 if ((buf = calloc((unsigned)lines, mxlen)) == NULL) 382 goto oomem; 383 384 /* 385 * allocate page header 386 */ 387 if ((hbuf = malloc((unsigned)HDBUF + offst)) == NULL) 388 goto oomem; 389 390 ohbuf = hbuf + offst; 391 if (offst) 392 (void)memset(hbuf, (int)' ', offst); 393 394 /* 395 * col pointers when no headers 396 */ 397 mvc = lines * clcnt; 398 if ((vc = calloc((unsigned)mvc, sizeof(struct vcol))) == NULL) 399 goto oomem; 400 401 /* 402 * pointer into page where last data per line is located 403 */ 404 if ((lstdat = calloc((unsigned)lines, sizeof(char *))) == NULL) 405 goto oomem; 406 407 /* 408 * fast index lookups to locate start of lines 409 */ 410 if ((indy = calloc((unsigned)lines, sizeof(int))) == NULL) 411 goto oomem; 412 if ((lindy = calloc((unsigned)lines, sizeof(int))) == NULL) 413 goto oomem; 414 415 if (nmwd) 416 fullcol = col + cw; 417 else 418 fullcol = col; 419 420 /* 421 * initialize buffer lookup indexes and offset area 422 */ 423 for (j = 0; j < lines; ++j) { 424 lindy[j] = j * mxlen; 425 indy[j] = lindy[j] + offst; 426 if (offst) { 427 ptbf = buf + lindy[j]; 428 (void)memset(ptbf, (int)' ', offst); 429 ptbf += offst; 430 } else 431 ptbf = buf + indy[j]; 432 lstdat[j] = ptbf; 433 } 434 435 /* 436 * loop by file 437 */ 438 while ((inf = nxtfile(argc, argv, &fname, ohbuf, 0)) != NULL) { 439 pagecnt = 0; 440 lncnt = 0; 441 442 /* 443 * loop by "form" 444 */ 445 for (;;) { 446 447 /* 448 * loop by page 449 */ 450 for(;;) { 451 452 /* 453 * loop by column 454 */ 455 cvc = 0; 456 for (i = 0; i < clcnt; ++i) { 457 j = 0; 458 /* 459 * if last column, do not pad 460 */ 461 if (i == mclcnt) 462 stp = 1; 463 else 464 stp = 0; 465 466 /* 467 * loop by line 468 */ 469 for(;;) { 470 /* 471 * is this first column 472 */ 473 if (!i) { 474 ptbf = buf + indy[j]; 475 lstdat[j] = ptbf; 476 } else 477 ptbf = lstdat[j]; 478 vc[cvc].pt = ptbf; 479 480 /* 481 * add number 482 */ 483 if (nmwd) { 484 addnum(ptbf, nmwd, ++lncnt); 485 ptbf += nmwd; 486 *ptbf++ = nmchar; 487 } 488 489 /* 490 * input next line 491 */ 492 rc = inln(inf,ptbf,colwd,&cnt,&cps,1,&mor); 493 vc[cvc++].cnt = cnt; 494 if (cnt >= 0) { 495 ptbf += cnt; 496 497 /* 498 * pad all but last column on page 499 */ 500 if (!stp) { 501 /* 502 * pad to end of column 503 */ 504 if (sflag) 505 *ptbf++ = schar; 506 else if ((pln = col-cnt) > 0) { 507 (void)memset(ptbf, 508 (int)' ',pln); 509 ptbf += pln; 510 } 511 } 512 513 /* 514 * remember last char in line 515 */ 516 lstdat[j] = ptbf; 517 if (++j >= lines) 518 break; 519 } /* end of if cnt >= 0 */ 520 521 if (rc != NORMAL) 522 break; 523 } /* end of for line */ 524 525 if (rc != NORMAL) 526 break; 527 } /* end of for column */ 528 529 /* 530 * when -t (no header) is specified the spec requires 531 * the min number of lines. The last page may not have 532 * balanced length columns. To fix this we must reorder 533 * the columns. This is a very slow technique so it is 534 * only used under limited conditions. Without -t, the 535 * balancing of text columns is unspecified. To NOT 536 * balance the last page, add the global variable 537 * nohead to the if statement below e.g. 538 */ 539 540 /* 541 * print header iff we got anything on the first read 542 */ 543 if (vc[0].cnt >= 0) { 544 if (prhead(hbuf, fname, ++pagecnt)) 545 goto out; 546 547 /* 548 * check to see if "last" page needs to be reordered 549 */ 550 --cvc; 551 if ((rc != NORMAL) && cvc && ((mvc-cvc) >= clcnt)){ 552 pln = cvc/clcnt; 553 if (cvc % clcnt) 554 ++pln; 555 556 for (i = 0; i < pln; ++i) { 557 ips = 0; 558 ops = 0; 559 if (offst && otln(buf,offst,&ips,&ops,1)) 560 goto out; 561 tvc = i; 562 563 for (j = 0; j < clcnt; ++j) { 564 /* 565 * determine column length 566 */ 567 if (j == mclcnt) { 568 /* 569 * last column 570 */ 571 cnt = vc[tvc].cnt; 572 if (nmwd) 573 cnt += cw; 574 } else if (sflag) { 575 /* 576 * single ch between 577 */ 578 cnt = vc[tvc].cnt + 1; 579 if (nmwd) 580 cnt += cw; 581 } else 582 cnt = fullcol; 583 584 if (otln(vc[tvc].pt, cnt, &ips, &ops, 1)) 585 goto out; 586 tvc += pln; 587 if (tvc > cvc) 588 break; 589 } 590 /* 591 * terminate line 592 */ 593 if (otln(buf, 0, &ips, &ops, 0)) 594 goto out; 595 } 596 597 } else { 598 599 /* 600 * just a normal page... 601 * determine how many lines to output 602 */ 603 if (i > 0) 604 pln = lines; 605 else 606 pln = j; 607 608 /* 609 * output each line 610 */ 611 for (i = 0; i < pln; ++i) { 612 ptbf = buf + lindy[i]; 613 if ((j = lstdat[i] - ptbf) <= offst) 614 break; 615 else { 616 ips = 0; 617 ops = 0; 618 if (otln(ptbf, j, &ips, &ops, 0)) 619 goto out; 620 } 621 } 622 } 623 } 624 625 /* 626 * pad to end of page 627 */ 628 if (prtail((lines - pln), 0)) 629 goto out; 630 631 /* 632 * if FORM continue 633 */ 634 if (rc != NORMAL) 635 break; 636 } 637 638 /* 639 * if EOF go to next file 640 */ 641 if (rc == END) 642 break; 643 } 644 645 if (inf != stdin) 646 (void)fclose(inf); 647 } 648 649 if (eoptind < argc){ 650 goto out; 651 } else { 652 error = 0; 653 goto out; 654 } 655 656 oomem: 657 mfail(); 658 out: 659 free(buf); 660 free(hbuf); 661 free(vc); 662 free(lstdat); 663 free(lindy); 664 if (inf != NULL && inf != stdin) 665 (void)fclose(inf); 666 return error; 667 668 } 669 670 /* 671 * horzcol: print files with more than one column of output across a page 672 */ 673 int 674 horzcol(int argc, char *argv[]) 675 { 676 char *ptbf; 677 int pln; 678 char *lstdat; 679 int col = colwd + 1; 680 int j; 681 int i; 682 int cnt; 683 int rc; 684 int lncnt; 685 int pagecnt; 686 char *buf = NULL; 687 char *hbuf = NULL; 688 char *ohbuf; 689 char *fname; 690 FILE *inf = NULL; 691 int cps = 0; 692 int mor = 0; 693 int ips = 0; 694 int ops = 0; 695 int error = 1; 696 697 if ((buf = malloc((unsigned)pgwd + offst + 1)) == NULL) 698 goto oomem; 699 700 /* 701 * page header 702 */ 703 if ((hbuf = malloc((unsigned)HDBUF + offst)) == NULL) 704 goto oomem; 705 706 ohbuf = hbuf + offst; 707 if (offst) { 708 (void)memset(buf, (int)' ', offst); 709 (void)memset(hbuf, (int)' ', offst); 710 } 711 712 /* 713 * loop by file 714 */ 715 while ((inf = nxtfile(argc, argv, &fname, ohbuf, 0)) != NULL) { 716 pagecnt = 0; 717 lncnt = 0; 718 719 /* 720 * loop by form 721 */ 722 for (;;) { 723 724 /* 725 * loop by page 726 */ 727 for(;;) { 728 729 /* 730 * loop by line 731 */ 732 for (i = 0; i < lines; ++i) { 733 ptbf = buf + offst; 734 lstdat = ptbf; 735 j = 0; 736 737 /* 738 * loop by col 739 */ 740 for(;;) { 741 if (nmwd) { 742 /* 743 * add number to column 744 */ 745 addnum(ptbf, nmwd, ++lncnt); 746 ptbf += nmwd; 747 *ptbf++ = nmchar; 748 } 749 /* 750 * input line 751 */ 752 rc = inln(inf,ptbf,colwd,&cnt,&cps,1, &mor); 753 if (cnt >= 0) { 754 if (!i && !j && prhead(hbuf, fname, ++pagecnt)) 755 goto out; 756 757 ptbf += cnt; 758 lstdat = ptbf; 759 760 /* 761 * if last line skip padding 762 */ 763 if (++j >= clcnt) 764 break; 765 766 /* 767 * pad to end of column 768 */ 769 if (sflag) 770 *ptbf++ = schar; 771 else if ((pln = col - cnt) > 0) { 772 (void)memset(ptbf,(int)' ',pln); 773 ptbf += pln; 774 } 775 } 776 if (rc != NORMAL) 777 break; 778 } 779 780 /* 781 * output line if any columns on it 782 */ 783 if (j) { 784 if (otln(buf, lstdat-buf, &ips, &ops, 0)) 785 goto out; 786 } 787 788 if (rc != NORMAL) 789 break; 790 } 791 792 /* 793 * pad to end of page 794 */ 795 if (prtail(lines - i, 0)) 796 return(1); 797 798 /* 799 * if FORM continue 800 */ 801 if (rc == END) 802 break; 803 } 804 /* 805 * if EOF go to next file 806 */ 807 if (rc == END) 808 break; 809 } 810 if (inf != stdin) 811 (void)fclose(inf); 812 } 813 if (eoptind < argc){ 814 goto out; 815 } else { 816 error = 0; 817 goto out; 818 } 819 820 oomem: 821 mfail(); 822 out: 823 free(buf); 824 free(hbuf); 825 if (inf != NULL && inf != stdin) 826 (void)fclose(inf); 827 return error; 828 } 829 830 struct ferrlist { 831 struct ferrlist *next; 832 char *buf; 833 }; 834 struct ferrlist *ferrhead, *ferrtail; 835 836 /* 837 * flsh_errs(): output saved up diagnostic messages after all normal 838 * processing has completed 839 */ 840 void 841 flsh_errs(void) 842 { 843 struct ferrlist *f; 844 845 if (ferr) { 846 for (f = ferrhead; f; f = f->next) 847 (void)write(STDERR_FILENO, f->buf, strlen(f->buf)); 848 } 849 } 850 851 static void ferrout(char *fmt, ...) __attribute__((format (printf, 1, 2))); 852 static void 853 ferrout(char *fmt, ...) 854 { 855 sigset_t block, oblock; 856 struct ferrlist *f; 857 va_list ap; 858 char *p; 859 860 va_start(ap, fmt); 861 if (ferr == 0) 862 vfprintf(stderr, fmt, ap); 863 else { 864 sigemptyset(&block); 865 sigaddset(&block, SIGINT); 866 sigprocmask(SIG_BLOCK, &block, &oblock); 867 868 if (vasprintf(&p, fmt, ap) == -1 || (f = malloc(sizeof(*f))) == NULL) { 869 va_end(ap); 870 va_start(ap, fmt); 871 flsh_errs(); 872 vfprintf(stderr, fmt, ap); 873 fputs("pr: memory allocation failed\n", stderr); 874 exit(1); 875 } 876 877 f->next = NULL; 878 f->buf = p; 879 if (ferrhead == NULL) 880 ferrhead = f; 881 if (ferrtail) 882 ferrtail->next = f; 883 ferrtail = f; 884 sigprocmask(SIG_SETMASK, &oblock, NULL); 885 } 886 va_end(ap); 887 } 888 889 /* 890 * mulfile: print files with more than one column of output and 891 * more than one file concurrently 892 */ 893 int 894 mulfile(int argc, char *argv[]) 895 { 896 char *ptbf; 897 int j; 898 int pln; 899 int *rc; 900 int cnt; 901 char *lstdat; 902 int i; 903 FILE **fbuf = NULL; 904 int actf; 905 int lncnt; 906 int col; 907 int pagecnt; 908 int fproc; 909 char *buf = NULL; 910 char *hbuf = NULL; 911 char *ohbuf; 912 char *fname; 913 int ips = 0; 914 int cps = 0; 915 int ops = 0; 916 int mor = 0; 917 int error = 1; 918 919 /* 920 * array of FILE *, one for each operand 921 */ 922 if ((fbuf = calloc((unsigned)clcnt, sizeof(FILE *))) == NULL) 923 goto oomem; 924 925 /* 926 * array of int *, one for each operand 927 */ 928 if ((rc = calloc((unsigned)clcnt, sizeof(int))) == NULL) 929 goto oomem; 930 931 /* 932 * page header 933 */ 934 if ((hbuf = malloc((unsigned)HDBUF + offst)) == NULL) 935 goto oomem; 936 937 ohbuf = hbuf + offst; 938 939 /* 940 * do not know how many columns yet. The number of operands provide an 941 * upper bound on the number of columns. We use the number of files 942 * we can open successfully to set the number of columns. The operation 943 * of the merge operation (-m) in relation to unsuccessful file opens 944 * is unspecified by posix. 945 * 946 * XXX - this seems moderately bogus, you'd think that specifying 947 * "pr -2 a b c d" would run though all the files in pairs, but 948 * the existing code says up two files, or fewer if one is bogus. 949 * fixing it would require modifying the looping structure, so be it. 950 */ 951 j = 0; 952 while (j < clcnt) { 953 if ((fbuf[j] = nxtfile(argc, argv, &fname, ohbuf, 1)) != NULL) { 954 rc[j] = NORMAL; 955 j++; 956 } 957 } 958 959 /* 960 * if no files, exit 961 */ 962 if (j) 963 clcnt = j; 964 else 965 goto out; 966 967 /* 968 * calculate page boundaries based on open file count 969 */ 970 if (nmwd) { 971 colwd = (pgwd - clcnt - nmwd)/clcnt; 972 pgwd = ((colwd + 1) * clcnt) - nmwd - 2; 973 } else { 974 colwd = (pgwd + 1 - clcnt)/clcnt; 975 pgwd = ((colwd + 1) * clcnt) - 1; 976 } 977 if (colwd < 1) { 978 ferrout("pr: page width too small for %d columns\n", clcnt); 979 goto out; 980 } 981 col = colwd + 1; 982 983 /* 984 * line buffer 985 */ 986 if ((buf = malloc((unsigned)pgwd + offst + 1)) == NULL) 987 goto oomem; 988 989 if (offst) { 990 (void)memset(buf, (int)' ', offst); 991 (void)memset(hbuf, (int)' ', offst); 992 } 993 994 pagecnt = 0; 995 lncnt = 0; 996 actf = clcnt; 997 998 /* 999 * continue to loop while any file still has data 1000 */ 1001 while (actf > 0) { 1002 1003 /* 1004 * loop on "form" 1005 */ 1006 for (;;) { 1007 1008 /* 1009 * loop by line 1010 */ 1011 for (i = 0; i < lines; ++i) { 1012 ptbf = buf + offst; 1013 lstdat = ptbf; 1014 if (nmwd) { 1015 /* 1016 * add line number to line 1017 */ 1018 addnum(ptbf, nmwd, ++lncnt); 1019 ptbf += nmwd; 1020 *ptbf++ = nmchar; 1021 } 1022 1023 fproc = 0; 1024 /* 1025 * loop by column 1026 */ 1027 for (j = 0; j < clcnt; ++j) { 1028 if (rc[j] == NORMAL ) { 1029 rc[j] = inln(fbuf[j], ptbf, colwd, &cnt, &cps, 1, &mor); 1030 if (cnt >= 0) { 1031 /* 1032 * process file data 1033 */ 1034 ptbf += cnt; 1035 lstdat = ptbf; 1036 fproc++; 1037 } else 1038 cnt = 0; 1039 1040 if (rc[j] == END) { 1041 /* 1042 * EOF close file 1043 */ 1044 if (fbuf[j] != stdin) 1045 (void)fclose(fbuf[j]); 1046 --actf; 1047 } 1048 } else 1049 cnt = 0; 1050 1051 /* 1052 * if last ACTIVE column, done with line 1053 */ 1054 if (fproc >= actf) 1055 break; 1056 1057 /* 1058 * pad to end of column 1059 */ 1060 if (sflag) { 1061 *ptbf++ = schar; 1062 } else { 1063 if (cnt >= 0) 1064 pln = col - cnt; 1065 else 1066 pln = col; 1067 if (pln > 0) { 1068 (void)memset(ptbf, (int)' ', pln); 1069 ptbf += pln; 1070 } 1071 } 1072 } 1073 1074 /* 1075 * if there was anything to do, print it 1076 */ 1077 if (fproc != 0) { 1078 if (!i && prhead(hbuf, fname, ++pagecnt)) 1079 goto out; 1080 1081 /* 1082 * output line 1083 */ 1084 if (otln(buf, lstdat-buf, &ips, &ops, 0)) 1085 goto out; 1086 } else 1087 break; 1088 } 1089 1090 /* 1091 * pad to end of page 1092 */ 1093 if (prtail(lines - i, 0)) 1094 return(1); 1095 1096 for (j = 0; j < clcnt; ++j) 1097 if (rc[j] != END) 1098 rc[j] = NORMAL; 1099 1100 if (actf <= 0) 1101 break; 1102 } 1103 if (actf <= 0) 1104 break; 1105 } 1106 if (eoptind < argc){ 1107 goto out; 1108 } else { 1109 error = 0; 1110 goto out; 1111 } 1112 1113 oomem: 1114 mfail(); 1115 out: 1116 if (fbuf) { 1117 for (j = 0; j < clcnt; j++) { 1118 if (fbuf[j] && fbuf[j] != stdin) 1119 (void)fclose(fbuf[j]); 1120 } 1121 free(fbuf); 1122 } 1123 free(hbuf); 1124 free(buf); 1125 return error; 1126 } 1127 1128 /* 1129 * inln(): input a line of data (unlimited length lines supported) 1130 * Input is optionally expanded to spaces 1131 * Returns 0 if normal LF, FORM on Formfeed, and END on EOF 1132 * 1133 * inf: file 1134 * buf: buffer 1135 * lim: buffer length 1136 * cnt: line length or -1 if no line (EOF for example) 1137 * cps: column position 1st char in buffer (large line support) 1138 * trnc: throw away data more than lim up to \n 1139 * mor: set if more data in line (not truncated) 1140 */ 1141 int 1142 inln(FILE *inf, char *buf, int lim, int *cnt, int *cps, int trnc, int *mor) 1143 { 1144 int col; 1145 int gap = ingap; 1146 int ch = -1; 1147 char *ptbuf; 1148 int chk = (int)inchar; 1149 1150 ptbuf = buf; 1151 1152 if (gap) { 1153 /* 1154 * expanding input option 1155 */ 1156 while ((--lim >= 0) && ((ch = getc(inf)) != EOF)) { 1157 /* 1158 * is this the input "tab" char 1159 */ 1160 if (ch == chk) { 1161 /* 1162 * expand to number of spaces 1163 */ 1164 col = (ptbuf - buf) + *cps; 1165 col = gap - (col % gap); 1166 1167 /* 1168 * if more than this line, push back 1169 */ 1170 if ((col > lim) && (ungetc(ch, inf) == EOF)) { 1171 *cnt = -1; 1172 return(END); /* shouldn't happen */ 1173 } 1174 1175 /* 1176 * expand to spaces 1177 */ 1178 while ((--col >= 0) && (--lim >= 0)) 1179 *ptbuf++ = ' '; 1180 continue; 1181 } 1182 if (ch == '\n' || (inform && ch == INFF)) 1183 break; 1184 *ptbuf++ = ch; 1185 } 1186 } else { 1187 /* 1188 * no expansion 1189 */ 1190 while ((--lim >= 0) && ((ch = getc(inf)) != EOF)) { 1191 if (ch == '\n' || (inform && ch == INFF)) 1192 break; 1193 *ptbuf++ = ch; 1194 } 1195 } 1196 col = ptbuf - buf; 1197 if (ch == EOF) { 1198 *mor = 0; 1199 *cps = 0; 1200 *cnt = col ? col : -1; 1201 return(END); 1202 } 1203 if (inform && ch == INFF) { 1204 *mor = 0; 1205 *cps = 0; 1206 *cnt = col; 1207 return(FORM); 1208 } 1209 if (ch == '\n') { 1210 /* 1211 * entire line processed 1212 */ 1213 *mor = 0; 1214 *cps = 0; 1215 *cnt = col; 1216 return(NORMAL); 1217 } 1218 1219 /* 1220 * line was larger than limit 1221 */ 1222 if (trnc) { 1223 /* 1224 * throw away rest of line 1225 */ 1226 while ((ch = getc(inf)) != EOF) { 1227 if (ch == '\n') 1228 break; 1229 } 1230 *cps = 0; 1231 *mor = 0; 1232 } else { 1233 /* 1234 * save column offset if not truncated 1235 */ 1236 *cps += col; 1237 *mor = 1; 1238 } 1239 1240 *cnt = col; 1241 return(NORMAL); 1242 } 1243 1244 /* 1245 * otln(): output a line of data. (Supports unlimited length lines) 1246 * output is optionally contracted to tabs 1247 * 1248 * buf: output buffer with data 1249 * cnt: number of chars of valid data in buf 1250 * svips: buffer input column position (for large lines) 1251 * svops: buffer output column position (for large lines) 1252 * mor: output line not complete in this buf; more data to come. 1253 * 1 is more, 0 is complete, -1 is no \n's 1254 */ 1255 int 1256 otln(char *buf, int cnt, int *svips, int *svops, int mor) 1257 { 1258 int ops; /* last col output */ 1259 int ips; /* last col in buf examined */ 1260 int gap = ogap; 1261 int tbps; 1262 char *endbuf; 1263 1264 /* skipping is only changed at header time not mid-line! */ 1265 if (skipping) 1266 return (0); 1267 1268 if (ogap) { 1269 /* 1270 * contracting on output 1271 */ 1272 endbuf = buf + cnt; 1273 ops = *svops; 1274 ips = *svips; 1275 while (buf < endbuf) { 1276 /* 1277 * count number of spaces and ochar in buffer 1278 */ 1279 if (*buf == ' ') { 1280 ++ips; 1281 ++buf; 1282 continue; 1283 } 1284 1285 /* 1286 * simulate ochar processing 1287 */ 1288 if (*buf == ochar) { 1289 ips += gap - (ips % gap); 1290 ++buf; 1291 continue; 1292 } 1293 1294 /* 1295 * got a non space char; contract out spaces 1296 */ 1297 while (ops < ips) { 1298 /* 1299 * use one space if necessary 1300 */ 1301 if (ips - ops == 1) { 1302 putchar(' '); 1303 break; 1304 } 1305 /* 1306 * use as many ochar as will fit 1307 */ 1308 if ((tbps = ops + gap - (ops % gap)) > ips) 1309 break; 1310 if (putchar(ochar) == EOF) { 1311 pfail(); 1312 return(1); 1313 } 1314 ops = tbps; 1315 } 1316 1317 while (ops < ips) { 1318 /* 1319 * finish off with spaces 1320 */ 1321 if (putchar(' ') == EOF) { 1322 pfail(); 1323 return(1); 1324 } 1325 ++ops; 1326 } 1327 1328 /* 1329 * output non space char 1330 */ 1331 if (putchar(*buf++) == EOF) { 1332 pfail(); 1333 return(1); 1334 } 1335 ++ips; 1336 ++ops; 1337 } 1338 1339 if (mor > 0) { 1340 /* 1341 * if incomplete line, save position counts 1342 */ 1343 *svops = ops; 1344 *svips = ips; 1345 return(0); 1346 } 1347 1348 if (mor < 0) { 1349 while (ops < ips) { 1350 /* 1351 * use one space if necessary 1352 */ 1353 if (ips - ops == 1) { 1354 putchar(' '); 1355 break; 1356 } 1357 /* 1358 * use as many ochar as will fit 1359 */ 1360 if ((tbps = ops + gap - (ops % gap)) > ips) 1361 break; 1362 if (putchar(ochar) == EOF) { 1363 pfail(); 1364 return(1); 1365 } 1366 ops = tbps; 1367 } 1368 1369 while (ops < ips) { 1370 /* 1371 * finish off with spaces 1372 */ 1373 if (putchar(' ') == EOF) { 1374 pfail(); 1375 return(1); 1376 } 1377 ++ops; 1378 } 1379 return(0); 1380 } 1381 } else { 1382 /* 1383 * output is not contracted 1384 */ 1385 if (cnt && (fwrite(buf, sizeof(char), cnt, stdout) < cnt)) { 1386 pfail(); 1387 return(1); 1388 } 1389 if (mor != 0) 1390 return(0); 1391 } 1392 1393 /* 1394 * process line end and double space as required 1395 */ 1396 if ((putchar('\n') == EOF) || (dspace && (putchar('\n') == EOF))) { 1397 pfail(); 1398 return(1); 1399 } 1400 return(0); 1401 } 1402 1403 #ifdef notused 1404 /* 1405 * inskip(): skip over pgcnt pages with lncnt lines per page 1406 * file is closed at EOF (if not stdin). 1407 * 1408 * inf FILE * to read from 1409 * pgcnt number of pages to skip 1410 * lncnt number of lines per page 1411 */ 1412 int 1413 inskip(FILE *inf, int pgcnt, int lncnt) 1414 { 1415 int c; 1416 int cnt; 1417 1418 while(--pgcnt > 0) { 1419 cnt = lncnt; 1420 while ((c = getc(inf)) != EOF) { 1421 if ((c == '\n') && (--cnt == 0)) 1422 break; 1423 } 1424 if (c == EOF) { 1425 if (inf != stdin) 1426 (void)fclose(inf); 1427 return(1); 1428 } 1429 } 1430 return(0); 1431 } 1432 #endif 1433 1434 /* 1435 * nxtfile: returns a FILE * to next file in arg list and sets the 1436 * time field for this file (or current date). 1437 * 1438 * buf array to store proper date for the header. 1439 * dt if set skips the date processing (used with -m) 1440 */ 1441 FILE * 1442 nxtfile(int argc, char *argv[], char **fname, char *buf, int dt) 1443 { 1444 FILE *inf = NULL; 1445 struct tm *timeptr = NULL; 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); 1457 clearerr(stdin); 1458 inf = stdin; 1459 if (header != NULL) 1460 *fname = header; 1461 else 1462 *fname = FNAME; 1463 if (nohead) 1464 return(inf); 1465 curtime = time(NULL); 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); 1474 inf = stdin; 1475 if (header != NULL) 1476 *fname = header; 1477 else 1478 *fname = FNAME; 1479 ++eoptind; 1480 if (nohead || (dt && twice)) 1481 return(inf); 1482 curtime = time(NULL); 1483 timeptr = localtime(&curtime); 1484 } else { 1485 /* 1486 * normal file processing 1487 */ 1488 if ((inf = fopen(argv[eoptind], "r")) == NULL) { 1489 ++errcnt; 1490 if (nodiag) 1491 continue; 1492 ferrout("pr: Cannot open %s, %s\n", 1493 argv[eoptind], strerror(errno)); 1494 continue; 1495 } 1496 if (header != NULL) 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); 1508 timeptr = localtime(&curtime); 1509 } else { 1510 if (fstat(fileno(inf), &statbuf) == -1) { 1511 ++errcnt; 1512 (void)fclose(inf); 1513 ferrout("pr: Cannot stat %s, %s\n", 1514 argv[eoptind], strerror(errno)); 1515 return(NULL); 1516 } 1517 timeptr = localtime(&(statbuf.st_mtime)); 1518 } 1519 } 1520 break; 1521 } 1522 if (inf == NULL) 1523 return(NULL); 1524 1525 /* 1526 * set up time field used in header 1527 */ 1528 if (strftime(buf, HDBUF, TIMEFMT, timeptr) == 0) { 1529 ++errcnt; 1530 if (inf != stdin) 1531 (void)fclose(inf); 1532 ferrout("pr: time conversion failed\n"); 1533 return(NULL); 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 */ 1553 void 1554 addnum(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 */ 1585 int 1586 prhead(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') == EOF) || (putchar('\n') == EOF)) { 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,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 */ 1631 int 1632 prtail(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') == EOF) { 1649 pfail(); 1650 return(1); 1651 } 1652 if (putchar('\n') == EOF) { 1653 pfail(); 1654 return(1); 1655 } 1656 } 1657 /* 1658 * but honor the formfeed request 1659 */ 1660 if (formfeed) 1661 if (putchar(OUTFF) == EOF) { 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') == EOF) { 1691 pfail(); 1692 return(1); 1693 } 1694 if (putchar(OUTFF) == EOF) { 1695 pfail(); 1696 return(1); 1697 } 1698 1699 } else { 1700 1701 if (incomp) 1702 cnt++; 1703 1704 cnt += TAILLEN; 1705 while (--cnt >= 0) { 1706 if (putchar('\n') == EOF) { 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*/ 1721 void 1722 terminate(int which_sig) 1723 { 1724 flsh_errs(); 1725 _exit(1); 1726 } 1727 1728 void 1729 mfail(void) 1730 { 1731 ferrout("pr: memory allocation failed\n"); 1732 } 1733 1734 void 1735 pfail(void) 1736 { 1737 ferrout("pr: write failure, %s\n", strerror(errno)); 1738 } 1739 1740 void 1741 usage(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 */ 1755 int 1756 setup(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))) 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_MAX, &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_MAX, &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) && !isdigit((unsigned char)*eoptarg)) 1796 inchar = *eoptarg++; 1797 else 1798 inchar = INCHAR; 1799 if ((eoptarg != NULL) && isdigit((unsigned char)*eoptarg)) { 1800 ingap = strtonum(eoptarg, 0, INT_MAX, &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 = INGAP; 1807 } else if ((eoptarg != NULL) && (*eoptarg != '\0')) { 1808 ferrout("pr: invalid value for -e %s\n", eoptarg); 1809 return(1); 1810 } else 1811 ingap = INGAP; 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) && !isdigit((unsigned char)*eoptarg)) 1823 ochar = *eoptarg++; 1824 else 1825 ochar = OCHAR; 1826 if ((eoptarg != NULL) && isdigit((unsigned char)*eoptarg)) { 1827 ogap = strtonum(eoptarg, 0, INT_MAX, &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 = OGAP; 1834 } else if ((eoptarg != NULL) && (*eoptarg != '\0')) { 1835 ferrout("pr: invalid value for -i %s\n", eoptarg); 1836 return(1); 1837 } else 1838 ogap = OGAP; 1839 break; 1840 case 'l': 1841 lines = strtonum(eoptarg, 1, INT_MAX, &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) && !isdigit((unsigned char)*eoptarg)) 1852 nmchar = *eoptarg++; 1853 else 1854 nmchar = NMCHAR; 1855 if ((eoptarg != NULL) && isdigit((unsigned char)*eoptarg)) { 1856 nmwd = strtonum(eoptarg, 1, INT_MAX, &errstr); 1857 if (errstr) { 1858 ferrout("pr: -n width is %s: %s\n", errstr, eoptarg); 1859 return(1); 1860 } 1861 } else if ((eoptarg != NULL) && (*eoptarg != '\0')) { 1862 ferrout("pr: invalid value for -n %s\n", eoptarg); 1863 return(1); 1864 } else 1865 nmwd = NMWD; 1866 break; 1867 case 'o': 1868 offst = strtonum(eoptarg, 1, INT_MAX, &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) 1880 schar = SCHAR; 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_MAX, &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 = CLCNT; 1914 #ifdef stupid 1915 merge = 0; 1916 #endif 1917 } 1918 } else 1919 clcnt = CLCNT; 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 = SPGWD; 1934 else 1935 pgwd = PGWD; 1936 } 1937 if (cflag || merge) { 1938 if (!eflag) { 1939 inchar = INCHAR; 1940 ingap = INGAP; 1941 } 1942 if (!iflag) { 1943 ochar = OCHAR; 1944 ogap = OGAP; 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 = LINES; 1966 1967 /* 1968 * make sure long enough for headers. if not disable 1969 */ 1970 if (lines <= HEADLEN + TAILLEN) 1971 nohead = 1; 1972 else if (!nohead) 1973 lines -= HEADLEN + TAILLEN; 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 } 1990