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