1 /* $OpenBSD: pr.c,v 1.34 2014/04/15 17:27:37 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/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 ferrout(char *fmt, ...) __attribute__((format (printf, 1, 2))); 848 static void 849 ferrout(char *fmt, ...) 850 { 851 sigset_t block, oblock; 852 struct ferrlist *f; 853 va_list ap; 854 char *p; 855 856 va_start(ap, fmt); 857 if (ferr == 0) 858 vfprintf(stderr, fmt, ap); 859 else { 860 sigemptyset(&block); 861 sigaddset(&block, SIGINT); 862 sigprocmask(SIG_BLOCK, &block, &oblock); 863 864 if (vasprintf(&p, fmt, ap) == -1 || (f = malloc(sizeof(*f))) == NULL) { 865 va_end(ap); 866 va_start(ap, fmt); 867 flsh_errs(); 868 vfprintf(stderr, fmt, ap); 869 fputs("pr: memory allocation failed\n", stderr); 870 exit(1); 871 } 872 873 f->next = NULL; 874 f->buf = p; 875 if (ferrhead == NULL) 876 ferrhead = f; 877 if (ferrtail) 878 ferrtail->next = f; 879 ferrtail = f; 880 sigprocmask(SIG_SETMASK, &oblock, NULL); 881 } 882 va_end(ap); 883 } 884 885 /* 886 * mulfile: print files with more than one column of output and 887 * more than one file concurrently 888 */ 889 int 890 mulfile(int argc, char *argv[]) 891 { 892 char *ptbf; 893 int j; 894 int pln; 895 int *rc; 896 int cnt; 897 char *lstdat; 898 int i; 899 FILE **fbuf = NULL; 900 int actf; 901 int lncnt; 902 int col; 903 int pagecnt; 904 int fproc; 905 char *buf = NULL; 906 char *hbuf = NULL; 907 char *ohbuf; 908 char *fname; 909 int ips = 0; 910 int cps = 0; 911 int ops = 0; 912 int mor = 0; 913 int error = 1; 914 915 /* 916 * array of FILE *, one for each operand 917 */ 918 if ((fbuf = (FILE **)calloc((unsigned)clcnt, sizeof(FILE *))) == NULL) 919 goto oomem; 920 921 /* 922 * array of int *, one for each operand 923 */ 924 if ((rc = (int *)calloc((unsigned)clcnt, sizeof(int))) == NULL) 925 goto oomem; 926 927 /* 928 * page header 929 */ 930 if ((hbuf = malloc((unsigned)HDBUF + offst)) == NULL) 931 goto oomem; 932 933 ohbuf = hbuf + offst; 934 935 /* 936 * do not know how many columns yet. The number of operands provide an 937 * upper bound on the number of columns. We use the number of files 938 * we can open successfully to set the number of columns. The operation 939 * of the merge operation (-m) in relation to unsuccessful file opens 940 * is unspecified by posix. 941 * 942 * XXX - this seems moderately bogus, you'd think that specifying 943 * "pr -2 a b c d" would run though all the files in pairs, but 944 * the existing code says up two files, or fewer if one is bogus. 945 * fixing it would require modifying the looping structure, so be it. 946 */ 947 j = 0; 948 while (j < clcnt) { 949 if ((fbuf[j] = nxtfile(argc, argv, &fname, ohbuf, 1)) != NULL) { 950 rc[j] = NORMAL; 951 j++; 952 } 953 } 954 955 /* 956 * if no files, exit 957 */ 958 if (j) 959 clcnt = j; 960 else 961 goto out; 962 963 /* 964 * calculate page boundaries based on open file count 965 */ 966 if (nmwd) { 967 colwd = (pgwd - clcnt - nmwd)/clcnt; 968 pgwd = ((colwd + 1) * clcnt) - nmwd - 2; 969 } else { 970 colwd = (pgwd + 1 - clcnt)/clcnt; 971 pgwd = ((colwd + 1) * clcnt) - 1; 972 } 973 if (colwd < 1) { 974 ferrout("pr: page width too small for %d columns\n", clcnt); 975 goto out; 976 } 977 col = colwd + 1; 978 979 /* 980 * line buffer 981 */ 982 if ((buf = malloc((unsigned)pgwd + offst + 1)) == NULL) 983 goto oomem; 984 985 if (offst) { 986 (void)memset(buf, (int)' ', offst); 987 (void)memset(hbuf, (int)' ', offst); 988 } 989 990 pagecnt = 0; 991 lncnt = 0; 992 actf = clcnt; 993 994 /* 995 * continue to loop while any file still has data 996 */ 997 while (actf > 0) { 998 999 /* 1000 * loop on "form" 1001 */ 1002 for (;;) { 1003 1004 /* 1005 * loop by line 1006 */ 1007 for (i = 0; i < lines; ++i) { 1008 ptbf = buf + offst; 1009 lstdat = ptbf; 1010 if (nmwd) { 1011 /* 1012 * add line number to line 1013 */ 1014 addnum(ptbf, nmwd, ++lncnt); 1015 ptbf += nmwd; 1016 *ptbf++ = nmchar; 1017 } 1018 1019 fproc = 0; 1020 /* 1021 * loop by column 1022 */ 1023 for (j = 0; j < clcnt; ++j) { 1024 if (rc[j] == NORMAL ) { 1025 rc[j] = inln(fbuf[j], ptbf, colwd, &cnt, &cps, 1, &mor); 1026 if (cnt >= 0) { 1027 /* 1028 * process file data 1029 */ 1030 ptbf += cnt; 1031 lstdat = ptbf; 1032 fproc++; 1033 } else 1034 cnt = 0; 1035 1036 if (rc[j] == END) { 1037 /* 1038 * EOF close file 1039 */ 1040 if (fbuf[j] != stdin) 1041 (void)fclose(fbuf[j]); 1042 --actf; 1043 } 1044 } else 1045 cnt = 0; 1046 1047 /* 1048 * if last ACTIVE column, done with line 1049 */ 1050 if (fproc >= actf) 1051 break; 1052 1053 /* 1054 * pad to end of column 1055 */ 1056 if (sflag) { 1057 *ptbf++ = schar; 1058 } else { 1059 if (cnt >= 0) 1060 pln = col - cnt; 1061 else 1062 pln = col; 1063 if (pln > 0) { 1064 (void)memset(ptbf, (int)' ', pln); 1065 ptbf += pln; 1066 } 1067 } 1068 } 1069 1070 /* 1071 * if there was anything to do, print it 1072 */ 1073 if (fproc != 0) { 1074 if (!i && prhead(hbuf, fname, ++pagecnt)) 1075 goto out; 1076 1077 /* 1078 * output line 1079 */ 1080 if (otln(buf, lstdat-buf, &ips, &ops, 0)) 1081 goto out; 1082 } else 1083 break; 1084 } 1085 1086 /* 1087 * pad to end of page 1088 */ 1089 if (prtail(lines - i, 0)) 1090 return(1); 1091 1092 for (j = 0; j < clcnt; ++j) 1093 if (rc[j] != END) 1094 rc[j] = NORMAL; 1095 1096 if (actf <= 0) 1097 break; 1098 } 1099 if (actf <= 0) 1100 break; 1101 } 1102 if (eoptind < argc){ 1103 goto out; 1104 } else { 1105 error = 0; 1106 goto out; 1107 } 1108 1109 oomem: 1110 mfail(); 1111 out: 1112 if (fbuf) { 1113 for (j = 0; j < clcnt; j++) { 1114 if (fbuf[j] && fbuf[j] != stdin) 1115 (void)fclose(fbuf[j]); 1116 } 1117 free(fbuf); 1118 } 1119 free(hbuf); 1120 free(buf); 1121 return error; 1122 } 1123 1124 /* 1125 * inln(): input a line of data (unlimited length lines supported) 1126 * Input is optionally expanded to spaces 1127 * Returns 0 if normal LF, FORM on Formfeed, and END on EOF 1128 * 1129 * inf: file 1130 * buf: buffer 1131 * lim: buffer length 1132 * cnt: line length or -1 if no line (EOF for example) 1133 * cps: column position 1st char in buffer (large line support) 1134 * trnc: throw away data more than lim up to \n 1135 * mor: set if more data in line (not truncated) 1136 */ 1137 int 1138 inln(FILE *inf, char *buf, int lim, int *cnt, int *cps, int trnc, int *mor) 1139 { 1140 int col; 1141 int gap = ingap; 1142 int ch = -1; 1143 char *ptbuf; 1144 int chk = (int)inchar; 1145 1146 ptbuf = buf; 1147 1148 if (gap) { 1149 /* 1150 * expanding input option 1151 */ 1152 while ((--lim >= 0) && ((ch = getc(inf)) != EOF)) { 1153 /* 1154 * is this the input "tab" char 1155 */ 1156 if (ch == chk) { 1157 /* 1158 * expand to number of spaces 1159 */ 1160 col = (ptbuf - buf) + *cps; 1161 col = gap - (col % gap); 1162 1163 /* 1164 * if more than this line, push back 1165 */ 1166 if ((col > lim) && (ungetc(ch, inf) == EOF)) { 1167 *cnt = -1; 1168 return(END); /* shouldn't happen */ 1169 } 1170 1171 /* 1172 * expand to spaces 1173 */ 1174 while ((--col >= 0) && (--lim >= 0)) 1175 *ptbuf++ = ' '; 1176 continue; 1177 } 1178 if (ch == '\n' || (inform && ch == INFF)) 1179 break; 1180 *ptbuf++ = ch; 1181 } 1182 } else { 1183 /* 1184 * no expansion 1185 */ 1186 while ((--lim >= 0) && ((ch = getc(inf)) != EOF)) { 1187 if (ch == '\n' || (inform && ch == INFF)) 1188 break; 1189 *ptbuf++ = ch; 1190 } 1191 } 1192 col = ptbuf - buf; 1193 if (ch == EOF) { 1194 *mor = 0; 1195 *cps = 0; 1196 *cnt = col ? col : -1; 1197 return(END); 1198 } 1199 if (inform && ch == INFF) { 1200 *mor = 0; 1201 *cps = 0; 1202 *cnt = col; 1203 return(FORM); 1204 } 1205 if (ch == '\n') { 1206 /* 1207 * entire line processed 1208 */ 1209 *mor = 0; 1210 *cps = 0; 1211 *cnt = col; 1212 return(NORMAL); 1213 } 1214 1215 /* 1216 * line was larger than limit 1217 */ 1218 if (trnc) { 1219 /* 1220 * throw away rest of line 1221 */ 1222 while ((ch = getc(inf)) != EOF) { 1223 if (ch == '\n') 1224 break; 1225 } 1226 *cps = 0; 1227 *mor = 0; 1228 } else { 1229 /* 1230 * save column offset if not truncated 1231 */ 1232 *cps += col; 1233 *mor = 1; 1234 } 1235 1236 *cnt = col; 1237 return(NORMAL); 1238 } 1239 1240 /* 1241 * otln(): output a line of data. (Supports unlimited length lines) 1242 * output is optionally contracted to tabs 1243 * 1244 * buf: output buffer with data 1245 * cnt: number of chars of valid data in buf 1246 * svips: buffer input column position (for large lines) 1247 * svops: buffer output column position (for large lines) 1248 * mor: output line not complete in this buf; more data to come. 1249 * 1 is more, 0 is complete, -1 is no \n's 1250 */ 1251 int 1252 otln(char *buf, int cnt, int *svips, int *svops, int mor) 1253 { 1254 int ops; /* last col output */ 1255 int ips; /* last col in buf examined */ 1256 int gap = ogap; 1257 int tbps; 1258 char *endbuf; 1259 1260 /* skipping is only changed at header time not mid-line! */ 1261 if (skipping) 1262 return (0); 1263 1264 if (ogap) { 1265 /* 1266 * contracting on output 1267 */ 1268 endbuf = buf + cnt; 1269 ops = *svops; 1270 ips = *svips; 1271 while (buf < endbuf) { 1272 /* 1273 * count number of spaces and ochar in buffer 1274 */ 1275 if (*buf == ' ') { 1276 ++ips; 1277 ++buf; 1278 continue; 1279 } 1280 1281 /* 1282 * simulate ochar processing 1283 */ 1284 if (*buf == ochar) { 1285 ips += gap - (ips % gap); 1286 ++buf; 1287 continue; 1288 } 1289 1290 /* 1291 * got a non space char; contract out spaces 1292 */ 1293 while (ops < ips) { 1294 /* 1295 * use one space if necessary 1296 */ 1297 if (ips - ops == 1) { 1298 putchar(' '); 1299 break; 1300 } 1301 /* 1302 * use as many ochar as will fit 1303 */ 1304 if ((tbps = ops + gap - (ops % gap)) > ips) 1305 break; 1306 if (putchar(ochar) == EOF) { 1307 pfail(); 1308 return(1); 1309 } 1310 ops = tbps; 1311 } 1312 1313 while (ops < ips) { 1314 /* 1315 * finish off with spaces 1316 */ 1317 if (putchar(' ') == EOF) { 1318 pfail(); 1319 return(1); 1320 } 1321 ++ops; 1322 } 1323 1324 /* 1325 * output non space char 1326 */ 1327 if (putchar(*buf++) == EOF) { 1328 pfail(); 1329 return(1); 1330 } 1331 ++ips; 1332 ++ops; 1333 } 1334 1335 if (mor > 0) { 1336 /* 1337 * if incomplete line, save position counts 1338 */ 1339 *svops = ops; 1340 *svips = ips; 1341 return(0); 1342 } 1343 1344 if (mor < 0) { 1345 while (ops < ips) { 1346 /* 1347 * use one space if necessary 1348 */ 1349 if (ips - ops == 1) { 1350 putchar(' '); 1351 break; 1352 } 1353 /* 1354 * use as many ochar as will fit 1355 */ 1356 if ((tbps = ops + gap - (ops % gap)) > ips) 1357 break; 1358 if (putchar(ochar) == EOF) { 1359 pfail(); 1360 return(1); 1361 } 1362 ops = tbps; 1363 } 1364 1365 while (ops < ips) { 1366 /* 1367 * finish off with spaces 1368 */ 1369 if (putchar(' ') == EOF) { 1370 pfail(); 1371 return(1); 1372 } 1373 ++ops; 1374 } 1375 return(0); 1376 } 1377 } else { 1378 /* 1379 * output is not contracted 1380 */ 1381 if (cnt && (fwrite(buf, sizeof(char), cnt, stdout) < cnt)) { 1382 pfail(); 1383 return(1); 1384 } 1385 if (mor != 0) 1386 return(0); 1387 } 1388 1389 /* 1390 * process line end and double space as required 1391 */ 1392 if ((putchar('\n') == EOF) || (dspace && (putchar('\n') == EOF))) { 1393 pfail(); 1394 return(1); 1395 } 1396 return(0); 1397 } 1398 1399 #ifdef notused 1400 /* 1401 * inskip(): skip over pgcnt pages with lncnt lines per page 1402 * file is closed at EOF (if not stdin). 1403 * 1404 * inf FILE * to read from 1405 * pgcnt number of pages to skip 1406 * lncnt number of lines per page 1407 */ 1408 int 1409 inskip(FILE *inf, int pgcnt, int lncnt) 1410 { 1411 int c; 1412 int cnt; 1413 1414 while(--pgcnt > 0) { 1415 cnt = lncnt; 1416 while ((c = getc(inf)) != EOF) { 1417 if ((c == '\n') && (--cnt == 0)) 1418 break; 1419 } 1420 if (c == EOF) { 1421 if (inf != stdin) 1422 (void)fclose(inf); 1423 return(1); 1424 } 1425 } 1426 return(0); 1427 } 1428 #endif 1429 1430 /* 1431 * nxtfile: returns a FILE * to next file in arg list and sets the 1432 * time field for this file (or current date). 1433 * 1434 * buf array to store proper date for the header. 1435 * dt if set skips the date processing (used with -m) 1436 */ 1437 FILE * 1438 nxtfile(int argc, char *argv[], char **fname, char *buf, int dt) 1439 { 1440 FILE *inf = NULL; 1441 struct timeval tv; 1442 struct tm *timeptr = NULL; 1443 struct stat statbuf; 1444 time_t curtime; 1445 static int twice = -1; 1446 1447 ++twice; 1448 if (eoptind >= argc) { 1449 /* 1450 * no file listed; default, use standard input 1451 */ 1452 if (twice) 1453 return(NULL); 1454 clearerr(stdin); 1455 inf = stdin; 1456 if (header != NULL) 1457 *fname = header; 1458 else 1459 *fname = FNAME; 1460 if (nohead) 1461 return(inf); 1462 if (gettimeofday(&tv, NULL) < 0) { 1463 ++errcnt; 1464 ferrout("pr: cannot get time of day, %s\n", 1465 strerror(errno)); 1466 eoptind = argc - 1; 1467 return(NULL); 1468 } 1469 curtime = tv.tv_sec; 1470 timeptr = localtime(&curtime); 1471 } 1472 for (; eoptind < argc; ++eoptind) { 1473 if (strcmp(argv[eoptind], "-") == 0) { 1474 /* 1475 * process a "-" for filename 1476 */ 1477 clearerr(stdin); 1478 inf = stdin; 1479 if (header != NULL) 1480 *fname = header; 1481 else 1482 *fname = FNAME; 1483 ++eoptind; 1484 if (nohead || (dt && twice)) 1485 return(inf); 1486 if (gettimeofday(&tv, NULL) < 0) { 1487 ++errcnt; 1488 ferrout("pr: cannot get time of day, %s\n", 1489 strerror(errno)); 1490 return(NULL); 1491 } 1492 curtime = tv.tv_sec; 1493 timeptr = localtime(&curtime); 1494 } else { 1495 /* 1496 * normal file processing 1497 */ 1498 if ((inf = fopen(argv[eoptind], "r")) == NULL) { 1499 ++errcnt; 1500 if (nodiag) 1501 continue; 1502 ferrout("pr: Cannot open %s, %s\n", 1503 argv[eoptind], strerror(errno)); 1504 continue; 1505 } 1506 if (header != NULL) 1507 *fname = header; 1508 else if (dt) 1509 *fname = FNAME; 1510 else 1511 *fname = argv[eoptind]; 1512 ++eoptind; 1513 if (nohead || (dt && twice)) 1514 return(inf); 1515 1516 if (dt) { 1517 if (gettimeofday(&tv, NULL) < 0) { 1518 ++errcnt; 1519 ferrout("pr: cannot get time of day, %s\n", 1520 strerror(errno)); 1521 return(NULL); 1522 } 1523 curtime = tv.tv_sec; 1524 timeptr = localtime(&curtime); 1525 } else { 1526 if (fstat(fileno(inf), &statbuf) < 0) { 1527 ++errcnt; 1528 (void)fclose(inf); 1529 ferrout("pr: Cannot stat %s, %s\n", 1530 argv[eoptind], strerror(errno)); 1531 return(NULL); 1532 } 1533 timeptr = localtime(&(statbuf.st_mtime)); 1534 } 1535 } 1536 break; 1537 } 1538 if (inf == NULL) 1539 return(NULL); 1540 1541 /* 1542 * set up time field used in header 1543 */ 1544 if (strftime(buf, HDBUF, timefrmt, timeptr) == 0) { 1545 ++errcnt; 1546 if (inf != stdin) 1547 (void)fclose(inf); 1548 ferrout("pr: time conversion failed\n"); 1549 return(NULL); 1550 } 1551 return(inf); 1552 } 1553 1554 /* 1555 * addnum(): adds the line number to the column 1556 * Truncates from the front or pads with spaces as required. 1557 * Numbers are right justified. 1558 * 1559 * buf buffer to store the number 1560 * wdth width of buffer to fill 1561 * line line number 1562 * 1563 * NOTE: numbers occupy part of the column. The posix 1564 * spec does not specify if -i processing should or should not 1565 * occur on number padding. The spec does say it occupies 1566 * part of the column. The usage of addnum currently treats 1567 * numbers as part of the column so spaces may be replaced. 1568 */ 1569 void 1570 addnum(char *buf, int wdth, int line) 1571 { 1572 char *pt = buf + wdth; 1573 1574 do { 1575 *--pt = digs[line % 10]; 1576 line /= 10; 1577 } while (line && (pt > buf)); 1578 1579 /* 1580 * pad with space as required 1581 */ 1582 while (pt > buf) 1583 *--pt = ' '; 1584 } 1585 1586 /* 1587 * prhead(): prints the top of page header 1588 * 1589 * buf buffer with time field (and offset) 1590 * cnt number of chars in buf 1591 * fname fname field for header 1592 * pagcnt page number 1593 * 1594 * prhead() should be used carefully, we don't want to print out headers 1595 * for null input files or orphan headers at the end of files, and also 1596 * trailer processing is typically conditional on whether you've called 1597 * prhead() at least once for a file and incremented pagecnt. Exactly 1598 * how to determine whether to print a header is a little different in 1599 * the context each output mode, but we let the caller figure that out. 1600 */ 1601 int 1602 prhead(char *buf, char *fname, int pagcnt) 1603 { 1604 int ips = 0; 1605 int ops = 0; 1606 1607 beheaded = 1; 1608 1609 if (skipping && pagcnt >= pgnm) 1610 skipping = 0; 1611 1612 if (nohead || skipping) 1613 return (0); 1614 1615 if ((putchar('\n') == EOF) || (putchar('\n') == EOF)) { 1616 pfail(); 1617 return(1); 1618 } 1619 /* 1620 * posix is not clear if the header is subject to line length 1621 * restrictions. The specification for header line format 1622 * in the spec clearly does not limit length. No pr currently 1623 * restricts header length. However if we need to truncate in 1624 * an reasonable way, adjust the length of the printf by 1625 * changing HDFMT to allow a length max as an argument printf. 1626 * buf (which contains the offset spaces and time field could 1627 * also be trimmed 1628 * 1629 * note only the offset (if any) is processed for tab expansion 1630 */ 1631 if (offst && otln(buf, offst, &ips, &ops, -1)) 1632 return(1); 1633 (void)printf(HDFMT,buf+offst, fname, pagcnt); 1634 return(0); 1635 } 1636 1637 /* 1638 * prtail(): pad page with empty lines (if required) and print page trailer 1639 * if requested 1640 * 1641 * cnt number of lines of padding needed 1642 * incomp was a '\n' missing from last line output 1643 * 1644 * prtail() can now be invoked unconditionally, with the notion that if 1645 * we haven't printed a header, there is no need for a trailer 1646 */ 1647 int 1648 prtail(int cnt, int incomp) 1649 { 1650 /* 1651 * if were's skipping to page N or haven't put out anything yet just exit 1652 */ 1653 if (skipping || beheaded == 0) 1654 return (0); 1655 beheaded = 0; 1656 1657 /* 1658 * if noheaders, only terminate an incomplete last line 1659 */ 1660 if (nohead) { 1661 1662 if (incomp) { 1663 if (dspace) 1664 if (putchar('\n') == EOF) { 1665 pfail(); 1666 return(1); 1667 } 1668 if (putchar('\n') == EOF) { 1669 pfail(); 1670 return(1); 1671 } 1672 } 1673 /* 1674 * but honor the formfeed request 1675 */ 1676 if (formfeed) 1677 if (putchar(OUTFF) == EOF) { 1678 pfail(); 1679 return(1); 1680 } 1681 1682 } else { 1683 1684 /* 1685 * if double space output two \n 1686 * 1687 * XXX this all seems bogus, why are we doing it here??? 1688 * page length is in terms of output lines and only the input is 1689 * supposed to be double spaced... otln() users should be doing 1690 * something like linect+=(dspace ? 2:1). 1691 */ 1692 if (dspace) 1693 cnt *= 2; 1694 1695 /* 1696 * if an odd number of lines per page, add an extra \n 1697 */ 1698 if (addone) 1699 ++cnt; 1700 1701 /* 1702 * either put out a form-feed or pad page with blanks 1703 */ 1704 if (formfeed) { 1705 if (incomp) 1706 if (putchar('\n') == EOF) { 1707 pfail(); 1708 return(1); 1709 } 1710 if (putchar(OUTFF) == EOF) { 1711 pfail(); 1712 return(1); 1713 } 1714 1715 } else { 1716 1717 if (incomp) 1718 cnt++; 1719 1720 cnt += TAILLEN; 1721 while (--cnt >= 0) { 1722 if (putchar('\n') == EOF) { 1723 pfail(); 1724 return(1); 1725 } 1726 } 1727 } 1728 } 1729 1730 return(0); 1731 } 1732 1733 /* 1734 * terminate(): when a SIGINT is recvd 1735 */ 1736 /*ARGSUSED*/ 1737 void 1738 terminate(int which_sig) 1739 { 1740 flsh_errs(); 1741 _exit(1); 1742 } 1743 1744 void 1745 mfail(void) 1746 { 1747 ferrout("pr: memory allocation failed\n"); 1748 } 1749 1750 void 1751 pfail(void) 1752 { 1753 ferrout("pr: write failure, %s\n", strerror(errno)); 1754 } 1755 1756 void 1757 usage(void) 1758 { 1759 ferrout( 1760 "usage: pr [+page] [-column] [-adFfmrt] [-e[char][gap]] [-h header]\n"); 1761 ferrout( 1762 "\t[-i[char][gap]] [-l lines] [-n[char][width]] [-o offset] [-s[char]]\n"); 1763 ferrout( 1764 "\t[-w width] [file ...]\n"); 1765 } 1766 1767 /* 1768 * setup: Validate command args, initialize and perform sanity 1769 * checks on options 1770 */ 1771 int 1772 setup(int argc, char *argv[]) 1773 { 1774 int c; 1775 int eflag = 0; 1776 int iflag = 0; 1777 int wflag = 0; 1778 int cflag = 0; 1779 const char *errstr; 1780 1781 if (isatty(fileno(stdout))) 1782 ferr = 1; 1783 1784 while ((c = egetopt(argc, argv, "#adfFmrte?h:i?l:n?o:s?w:")) != -1) { 1785 switch (c) { 1786 case '+': 1787 pgnm = strtonum(eoptarg, 1, INT_MAX, &errstr); 1788 if (errstr) { 1789 ferrout("pr: +page number is %s: %s\n", errstr, eoptarg); 1790 return(1); 1791 } 1792 ++skipping; 1793 break; 1794 case '-': 1795 clcnt = strtonum(eoptarg, 1, INT_MAX, &errstr); 1796 if (errstr) { 1797 ferrout("pr: -columns number is %s: %s\n", errstr, eoptarg); 1798 return(1); 1799 } 1800 if (clcnt > 1) 1801 ++cflag; 1802 break; 1803 case 'a': 1804 ++across; 1805 break; 1806 case 'd': 1807 ++dspace; 1808 break; 1809 case 'e': 1810 ++eflag; 1811 if ((eoptarg != NULL) && !isdigit((unsigned char)*eoptarg)) 1812 inchar = *eoptarg++; 1813 else 1814 inchar = INCHAR; 1815 if ((eoptarg != NULL) && isdigit((unsigned char)*eoptarg)) { 1816 ingap = strtonum(eoptarg, 0, INT_MAX, &errstr); 1817 if (errstr) { 1818 ferrout("pr: -e gap is %s: %s\n", errstr, eoptarg); 1819 return(1); 1820 } 1821 if (ingap == 0) 1822 ingap = INGAP; 1823 } else if ((eoptarg != NULL) && (*eoptarg != '\0')) { 1824 ferrout("pr: invalid value for -e %s\n", eoptarg); 1825 return(1); 1826 } else 1827 ingap = INGAP; 1828 break; 1829 case 'f': 1830 case 'F': 1831 ++formfeed; 1832 break; 1833 case 'h': 1834 header = eoptarg; 1835 break; 1836 case 'i': 1837 ++iflag; 1838 if ((eoptarg != NULL) && !isdigit((unsigned char)*eoptarg)) 1839 ochar = *eoptarg++; 1840 else 1841 ochar = OCHAR; 1842 if ((eoptarg != NULL) && isdigit((unsigned char)*eoptarg)) { 1843 ogap = strtonum(eoptarg, 0, INT_MAX, &errstr); 1844 if (errstr) { 1845 ferrout("pr: -i gap is %s: %s\n", errstr, eoptarg); 1846 return(1); 1847 } 1848 if (ogap == 0) 1849 ogap = OGAP; 1850 } else if ((eoptarg != NULL) && (*eoptarg != '\0')) { 1851 ferrout("pr: invalid value for -i %s\n", eoptarg); 1852 return(1); 1853 } else 1854 ogap = OGAP; 1855 break; 1856 case 'l': 1857 lines = strtonum(eoptarg, 1, INT_MAX, &errstr); 1858 if (errstr) { 1859 ferrout("pr: number of lines is %s: %s\n", errstr, eoptarg); 1860 return(1); 1861 } 1862 break; 1863 case 'm': 1864 ++merge; 1865 break; 1866 case 'n': 1867 if ((eoptarg != NULL) && !isdigit((unsigned char)*eoptarg)) 1868 nmchar = *eoptarg++; 1869 else 1870 nmchar = NMCHAR; 1871 if ((eoptarg != NULL) && isdigit((unsigned char)*eoptarg)) { 1872 nmwd = strtonum(eoptarg, 1, INT_MAX, &errstr); 1873 if (errstr) { 1874 ferrout("pr: -n width is %s: %s\n", errstr, eoptarg); 1875 return(1); 1876 } 1877 } else if ((eoptarg != NULL) && (*eoptarg != '\0')) { 1878 ferrout("pr: invalid value for -n %s\n", eoptarg); 1879 return(1); 1880 } else 1881 nmwd = NMWD; 1882 break; 1883 case 'o': 1884 offst = strtonum(eoptarg, 1, INT_MAX, &errstr); 1885 if (errstr) { 1886 ferrout("pr: -o offset is %s: %s\n", errstr, eoptarg); 1887 return(1); 1888 } 1889 break; 1890 case 'r': 1891 ++nodiag; 1892 break; 1893 case 's': 1894 ++sflag; 1895 if (eoptarg == NULL) 1896 schar = SCHAR; 1897 else { 1898 schar = *eoptarg++; 1899 if (*eoptarg != '\0') { 1900 ferrout("pr: invalid value for -s %s\n", eoptarg); 1901 return(1); 1902 } 1903 } 1904 break; 1905 case 't': 1906 ++nohead; 1907 break; 1908 case 'w': 1909 ++wflag; 1910 pgwd = strtonum(eoptarg, 1, INT_MAX, &errstr); 1911 if (errstr) { 1912 ferrout("pr: -w width is %s: %s\n", errstr, eoptarg); 1913 return(1); 1914 } 1915 break; 1916 default: 1917 return(1); 1918 } 1919 } 1920 1921 /* 1922 * default and sanity checks 1923 */ 1924 inform++; 1925 1926 if (!clcnt) { 1927 if (merge) { 1928 if ((clcnt = argc - eoptind) <= 1) { 1929 clcnt = CLCNT; 1930 #ifdef stupid 1931 merge = 0; 1932 #endif 1933 } 1934 } else 1935 clcnt = CLCNT; 1936 } 1937 if (across) { 1938 if (clcnt == 1) { 1939 ferrout("pr: -a flag requires multiple columns\n"); 1940 return(1); 1941 } 1942 if (merge) { 1943 ferrout("pr: -m cannot be used with -a\n"); 1944 return(1); 1945 } 1946 } 1947 if (!wflag) { 1948 if (sflag) 1949 pgwd = SPGWD; 1950 else 1951 pgwd = PGWD; 1952 } 1953 if (cflag || merge) { 1954 if (!eflag) { 1955 inchar = INCHAR; 1956 ingap = INGAP; 1957 } 1958 if (!iflag) { 1959 ochar = OCHAR; 1960 ogap = OGAP; 1961 } 1962 } 1963 if (cflag) { 1964 if (merge) { 1965 ferrout("pr: -m cannot be used with multiple columns\n"); 1966 return(1); 1967 } 1968 if (nmwd) { 1969 colwd = (pgwd + 1 - (clcnt * (nmwd + 2)))/clcnt; 1970 pgwd = ((colwd + nmwd + 2) * clcnt) - 1; 1971 } else { 1972 colwd = (pgwd + 1 - clcnt)/clcnt; 1973 pgwd = ((colwd + 1) * clcnt) - 1; 1974 } 1975 if (colwd < 1) { 1976 ferrout("pr: page width is too small for %d columns\n",clcnt); 1977 return(1); 1978 } 1979 } 1980 if (!lines) 1981 lines = LINES; 1982 1983 /* 1984 * make sure long enough for headers. if not disable 1985 */ 1986 if (lines <= HEADLEN + TAILLEN) 1987 ++nohead; 1988 else if (!nohead) 1989 lines -= HEADLEN + TAILLEN; 1990 1991 /* 1992 * adjust for double space on odd length pages 1993 */ 1994 if (dspace) { 1995 if (lines == 1) 1996 dspace = 0; 1997 else { 1998 if (lines & 1) 1999 ++addone; 2000 lines /= 2; 2001 } 2002 } 2003 2004 if ((timefrmt = getenv("LC_TIME")) == NULL) 2005 timefrmt = TIMEFMT; 2006 return(0); 2007 } 2008