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