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