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