1 /* $OpenBSD: pr.c,v 1.8 2000/11/10 15:33:12 provos 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.8 2000/11/10 15:33:12 provos 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 *err; /* 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(err, 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 as many ochar as will fit 1240 */ 1241 if ((tbps = ops + gap - (ops % gap)) > ips) 1242 break; 1243 if (putchar(ochar) == EOF) { 1244 pfail(); 1245 return(1); 1246 } 1247 ops = tbps; 1248 } 1249 1250 while (ops < ips) { 1251 /* 1252 * finish off with spaces 1253 */ 1254 if (putchar(' ') == EOF) { 1255 pfail(); 1256 return(1); 1257 } 1258 ++ops; 1259 } 1260 1261 /* 1262 * output non space char 1263 */ 1264 if (putchar(*buf++) == EOF) { 1265 pfail(); 1266 return(1); 1267 } 1268 ++ips; 1269 ++ops; 1270 } 1271 1272 if (mor > 0) { 1273 /* 1274 * if incomplete line, save position counts 1275 */ 1276 *svops = ops; 1277 *svips = ips; 1278 return(0); 1279 } 1280 1281 if (mor < 0) { 1282 while (ops < ips) { 1283 /* 1284 * use as many ochar as will fit 1285 */ 1286 if ((tbps = ops + gap - (ops % gap)) > ips) 1287 break; 1288 if (putchar(ochar) == EOF) { 1289 pfail(); 1290 return(1); 1291 } 1292 ops = tbps; 1293 } 1294 while (ops < ips) { 1295 /* 1296 * finish off with spaces 1297 */ 1298 if (putchar(' ') == EOF) { 1299 pfail(); 1300 return(1); 1301 } 1302 ++ops; 1303 } 1304 return(0); 1305 } 1306 } else { 1307 /* 1308 * output is not contracted 1309 */ 1310 if (cnt && (fwrite(buf, sizeof(char), cnt, stdout) <= 0)) { 1311 pfail(); 1312 return(1); 1313 } 1314 if (mor != 0) 1315 return(0); 1316 } 1317 1318 /* 1319 * process line end and double space as required 1320 */ 1321 if ((putchar('\n') == EOF) || (dspace && (putchar('\n') == EOF))) { 1322 pfail(); 1323 return(1); 1324 } 1325 return(0); 1326 } 1327 1328 #ifdef notused 1329 /* 1330 * inskip(): skip over pgcnt pages with lncnt lines per page 1331 * file is closed at EOF (if not stdin). 1332 * 1333 * inf FILE * to read from 1334 * pgcnt number of pages to skip 1335 * lncnt number of lines per page 1336 */ 1337 int 1338 inskip(inf, pgcnt, lncnt) 1339 FILE *inf; 1340 register int pgcnt; 1341 register int lncnt; 1342 { 1343 register int c; 1344 register int cnt; 1345 1346 while(--pgcnt > 0) { 1347 cnt = lncnt; 1348 while ((c = getc(inf)) != EOF) { 1349 if ((c == '\n') && (--cnt == 0)) 1350 break; 1351 } 1352 if (c == EOF) { 1353 if (inf != stdin) 1354 (void)fclose(inf); 1355 return(1); 1356 } 1357 } 1358 return(0); 1359 } 1360 #endif 1361 1362 /* 1363 * nxtfile: returns a FILE * to next file in arg list and sets the 1364 * time field for this file (or current date). 1365 * 1366 * buf array to store proper date for the header. 1367 * dt if set skips the date processing (used with -m) 1368 */ 1369 FILE * 1370 nxtfile(argc, argv, fname, buf, dt) 1371 int argc; 1372 char **argv; 1373 char **fname; 1374 char *buf; 1375 int dt; 1376 { 1377 FILE *inf = NULL; 1378 struct timeval tv; 1379 struct timezone tz; 1380 struct tm *timeptr = NULL; 1381 struct stat statbuf; 1382 time_t curtime; 1383 static int twice = -1; 1384 1385 ++twice; 1386 if (eoptind >= argc) { 1387 /* 1388 * no file listed; default, use standard input 1389 */ 1390 if (twice) 1391 return(NULL); 1392 clearerr(stdin); 1393 inf = stdin; 1394 if (header != NULL) 1395 *fname = header; 1396 else 1397 *fname = FNAME; 1398 if (nohead) 1399 return(inf); 1400 if (gettimeofday(&tv, &tz) < 0) { 1401 ++errcnt; 1402 (void)fprintf(err, "pr: cannot get time of day, %s\n", 1403 strerror(errno)); 1404 eoptind = argc - 1; 1405 return(NULL); 1406 } 1407 curtime = tv.tv_sec; 1408 timeptr = localtime(&curtime); 1409 } 1410 for (; eoptind < argc; ++eoptind) { 1411 if (strcmp(argv[eoptind], "-") == 0) { 1412 /* 1413 * process a "-" for filename 1414 */ 1415 clearerr(stdin); 1416 inf = stdin; 1417 if (header != NULL) 1418 *fname = header; 1419 else 1420 *fname = FNAME; 1421 ++eoptind; 1422 if (nohead || (dt && twice)) 1423 return(inf); 1424 if (gettimeofday(&tv, &tz) < 0) { 1425 ++errcnt; 1426 (void)fprintf(err, 1427 "pr: cannot get time of day, %s\n", 1428 strerror(errno)); 1429 return(NULL); 1430 } 1431 curtime = tv.tv_sec; 1432 timeptr = localtime(&curtime); 1433 } else { 1434 /* 1435 * normal file processing 1436 */ 1437 if ((inf = fopen(argv[eoptind], "r")) == NULL) { 1438 ++errcnt; 1439 if (nodiag) 1440 continue; 1441 (void)fprintf(err, "pr: Cannot open %s, %s\n", 1442 argv[eoptind], strerror(errno)); 1443 continue; 1444 } 1445 if (header != NULL) 1446 *fname = header; 1447 else if (dt) 1448 *fname = FNAME; 1449 else 1450 *fname = argv[eoptind]; 1451 ++eoptind; 1452 if (nohead || (dt && twice)) 1453 return(inf); 1454 1455 if (dt) { 1456 if (gettimeofday(&tv, &tz) < 0) { 1457 ++errcnt; 1458 (void)fprintf(err, 1459 "pr: cannot get time of day, %s\n", 1460 strerror(errno)); 1461 return(NULL); 1462 } 1463 curtime = tv.tv_sec; 1464 timeptr = localtime(&curtime); 1465 } else { 1466 if (fstat(fileno(inf), &statbuf) < 0) { 1467 ++errcnt; 1468 (void)fclose(inf); 1469 (void)fprintf(err, 1470 "pr: Cannot stat %s, %s\n", 1471 argv[eoptind], strerror(errno)); 1472 return(NULL); 1473 } 1474 timeptr = localtime(&(statbuf.st_mtime)); 1475 } 1476 } 1477 break; 1478 } 1479 if (inf == NULL) 1480 return(NULL); 1481 1482 /* 1483 * set up time field used in header 1484 */ 1485 if (strftime(buf, HDBUF, timefrmt, timeptr) <= 0) { 1486 ++errcnt; 1487 if (inf != stdin) 1488 (void)fclose(inf); 1489 (void)fputs("pr: time conversion failed\n", err); 1490 return(NULL); 1491 } 1492 return(inf); 1493 } 1494 1495 /* 1496 * addnum(): adds the line number to the column 1497 * Truncates from the front or pads with spaces as required. 1498 * Numbers are right justified. 1499 * 1500 * buf buffer to store the number 1501 * wdth width of buffer to fill 1502 * line line number 1503 * 1504 * NOTE: numbers occupy part of the column. The posix 1505 * spec does not specify if -i processing should or should not 1506 * occur on number padding. The spec does say it occupies 1507 * part of the column. The usage of addnum currently treats 1508 * numbers as part of the column so spaces may be replaced. 1509 */ 1510 void 1511 addnum(buf, wdth, line) 1512 register char *buf; 1513 register int wdth; 1514 register int line; 1515 { 1516 register char *pt = buf + wdth; 1517 1518 do { 1519 *--pt = digs[line % 10]; 1520 line /= 10; 1521 } while (line && (pt > buf)); 1522 1523 /* 1524 * pad with space as required 1525 */ 1526 while (pt > buf) 1527 *--pt = ' '; 1528 } 1529 1530 /* 1531 * prhead(): prints the top of page header 1532 * 1533 * buf buffer with time field (and offset) 1534 * cnt number of chars in buf 1535 * fname fname field for header 1536 * pagcnt page number 1537 * 1538 * prhead() should be used carefully, we don't want to print out headers 1539 * for null input files or orphan headers at the end of files, and also 1540 * trailer processing is typically conditional on whether you've called 1541 * prhead() at least once for a file and incremented pagecnt.. Exactly 1542 * how to determine whether to print a header is a little different in 1543 * the context each output mode, but we let the caller figure that out. 1544 */ 1545 int 1546 prhead(buf, fname, pagcnt) 1547 char *buf; 1548 char *fname; 1549 int pagcnt; 1550 { 1551 int ips = 0; 1552 int ops = 0; 1553 1554 beheaded = 1; 1555 1556 if (skipping && pagcnt >= pgnm) 1557 skipping = 0; 1558 1559 if (nohead || skipping) 1560 return (0); 1561 1562 if ((putchar('\n') == EOF) || (putchar('\n') == EOF)) { 1563 pfail(); 1564 return(1); 1565 } 1566 /* 1567 * posix is not clear if the header is subject to line length 1568 * restrictions. The specification for header line format 1569 * in the spec clearly does not limit length. No pr currently 1570 * restricts header length. However if we need to truncate in 1571 * an reasonable way, adjust the length of the printf by 1572 * changing HDFMT to allow a length max as an arguement printf. 1573 * buf (which contains the offset spaces and time field could 1574 * also be trimmed 1575 * 1576 * note only the offset (if any) is processed for tab expansion 1577 */ 1578 if (offst && otln(buf, offst, &ips, &ops, -1)) 1579 return(1); 1580 (void)printf(HDFMT,buf+offst, fname, pagcnt); 1581 return(0); 1582 } 1583 1584 /* 1585 * prtail(): pad page with empty lines (if required) and print page trailer 1586 * if requested 1587 * 1588 * cnt number of lines of padding needed 1589 * incomp was a '\n' missing from last line output 1590 * 1591 * prtail() can now be invoked unconditionally, with the notion that if 1592 * we haven't printed a hearder, these no need for a trailer 1593 */ 1594 int 1595 prtail(cnt, incomp) 1596 register int cnt; 1597 int incomp; 1598 { 1599 /* 1600 * if were's skipping to page N or haven't put out anything yet just exit 1601 */ 1602 if (skipping || beheaded == 0) 1603 return (0); 1604 beheaded = 0; 1605 1606 /* 1607 * if noheaders, only terminate an incomplete last line 1608 */ 1609 if (nohead) { 1610 1611 if (incomp) { 1612 if (dspace) 1613 if (putchar('\n') == EOF) { 1614 pfail(); 1615 return(1); 1616 } 1617 if (putchar('\n') == EOF) { 1618 pfail(); 1619 return(1); 1620 } 1621 } 1622 /* 1623 * but honor the formfeed request 1624 */ 1625 if (formfeed) 1626 if (putchar(OUTFF) == EOF) { 1627 pfail(); 1628 return(1); 1629 } 1630 1631 } else { 1632 1633 /* 1634 * if double space output two \n 1635 * 1636 * XXX this all seems bogus, why are we doing it here??? 1637 * page length is in terms of output lines and only the input is 1638 * supposed to be double spaced... otln() users should be doing 1639 * something like linect+=(dspace ? 2:1). 1640 */ 1641 if (dspace) 1642 cnt *= 2; 1643 1644 /* 1645 * if an odd number of lines per page, add an extra \n 1646 */ 1647 if (addone) 1648 ++cnt; 1649 1650 /* 1651 * either put out a form-feed or pad page with blanks 1652 */ 1653 if (formfeed) { 1654 if (incomp) 1655 if (putchar('\n') == EOF) { 1656 pfail(); 1657 return(1); 1658 } 1659 if (putchar(OUTFF) == EOF) { 1660 pfail(); 1661 return(1); 1662 } 1663 1664 } else { 1665 1666 if (incomp) 1667 cnt++; 1668 1669 cnt += TAILLEN; 1670 while (--cnt >= 0) { 1671 if (putchar('\n') == EOF) { 1672 pfail(); 1673 return(1); 1674 } 1675 } 1676 } 1677 } 1678 1679 return(0); 1680 } 1681 1682 /* 1683 * terminate(): when a SIGINT is recvd 1684 */ 1685 void 1686 terminate(which_sig) 1687 int which_sig; 1688 { 1689 flsh_errs(); 1690 exit(1); 1691 } 1692 1693 1694 /* 1695 * flsh_errs(): output saved up diagnostic messages after all normal 1696 * processing has completed 1697 */ 1698 void 1699 flsh_errs() 1700 { 1701 char buf[BUFSIZ]; 1702 1703 (void)fflush(stdout); 1704 (void)fflush(err); 1705 if (err == stderr) 1706 return; 1707 rewind(err); 1708 while (fgets(buf, BUFSIZ, err) != NULL) 1709 (void)fputs(buf, stderr); 1710 } 1711 1712 void 1713 mfail() 1714 { 1715 (void)fputs("pr: memory allocation failed\n", err); 1716 } 1717 1718 void 1719 pfail() 1720 { 1721 (void)fprintf(err, "pr: write failure, %s\n", strerror(errno)); 1722 } 1723 1724 void 1725 usage() 1726 { 1727 (void)fputs( 1728 "usage: pr [+page] [-col] [-adfFmrt] [-e[ch][gap]] [-h header]\n",err); 1729 (void)fputs( 1730 " [-i[ch][gap]] [-l line] [-n[ch][width]] [-o offset]\n",err); 1731 (void)fputs( 1732 " [-s[ch]] [-w width] [-] [file ...]\n", err); 1733 } 1734 1735 /* 1736 * setup: Validate command args, initialize and perform sanity 1737 * checks on options 1738 */ 1739 int 1740 setup(argc, argv) 1741 register int argc; 1742 register char **argv; 1743 { 1744 register int c; 1745 int eflag = 0; 1746 int iflag = 0; 1747 int wflag = 0; 1748 int cflag = 0; 1749 1750 if (isatty(fileno(stdout))) { 1751 /* 1752 * defer diagnostics until processing is done 1753 */ 1754 if ((err = tmpfile()) == NULL) { 1755 (void)fputs("Cannot defer diagnostic messages\n",stderr); 1756 return(1); 1757 } 1758 } else 1759 err = stderr; 1760 while ((c = egetopt(argc, argv, "#adfFmrte?h:i?l:n?o:s?w:")) != -1) { 1761 switch (c) { 1762 case '+': 1763 if ((pgnm = atoi(eoptarg)) < 1) { 1764 (void)fputs("pr: +page number must be 1 or more\n", 1765 err); 1766 return(1); 1767 } 1768 ++skipping; 1769 break; 1770 case '-': 1771 if ((clcnt = atoi(eoptarg)) < 1) { 1772 (void)fputs("pr: -columns must be 1 or more\n",err); 1773 return(1); 1774 } 1775 if (clcnt > 1) 1776 ++cflag; 1777 break; 1778 case 'a': 1779 ++across; 1780 break; 1781 case 'd': 1782 ++dspace; 1783 break; 1784 case 'e': 1785 ++eflag; 1786 if ((eoptarg != NULL) && !isdigit(*eoptarg)) 1787 inchar = *eoptarg++; 1788 else 1789 inchar = INCHAR; 1790 if ((eoptarg != NULL) && isdigit(*eoptarg)) { 1791 if ((ingap = atoi(eoptarg)) < 0) { 1792 (void)fputs( 1793 "pr: -e gap must be 0 or more\n", err); 1794 return(1); 1795 } 1796 if (ingap == 0) 1797 ingap = INGAP; 1798 } else if ((eoptarg != NULL) && (*eoptarg != '\0')) { 1799 (void)fprintf(err, 1800 "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 if ((ogap = atoi(eoptarg)) < 0) { 1820 (void)fputs( 1821 "pr: -i gap must be 0 or more\n", err); 1822 return(1); 1823 } 1824 if (ogap == 0) 1825 ogap = OGAP; 1826 } else if ((eoptarg != NULL) && (*eoptarg != '\0')) { 1827 (void)fprintf(err, 1828 "pr: invalid value for -i %s\n", eoptarg); 1829 return(1); 1830 } else 1831 ogap = OGAP; 1832 break; 1833 case 'l': 1834 if (!isdigit(*eoptarg) || ((lines=atoi(eoptarg)) < 1)) { 1835 (void)fputs( 1836 "pr: Number of lines must be 1 or more\n",err); 1837 return(1); 1838 } 1839 break; 1840 case 'm': 1841 ++merge; 1842 break; 1843 case 'n': 1844 if ((eoptarg != NULL) && !isdigit(*eoptarg)) 1845 nmchar = *eoptarg++; 1846 else 1847 nmchar = NMCHAR; 1848 if ((eoptarg != NULL) && isdigit(*eoptarg)) { 1849 if ((nmwd = atoi(eoptarg)) < 1) { 1850 (void)fputs( 1851 "pr: -n width must be 1 or more\n",err); 1852 return(1); 1853 } 1854 } else if ((eoptarg != NULL) && (*eoptarg != '\0')) { 1855 (void)fprintf(err, 1856 "pr: invalid value for -n %s\n", eoptarg); 1857 return(1); 1858 } else 1859 nmwd = NMWD; 1860 break; 1861 case 'o': 1862 if (!isdigit(*eoptarg) || ((offst = atoi(eoptarg))< 1)){ 1863 (void)fputs("pr: -o offset must be 1 or more\n", 1864 err); 1865 return(1); 1866 } 1867 break; 1868 case 'r': 1869 ++nodiag; 1870 break; 1871 case 's': 1872 ++sflag; 1873 if (eoptarg == NULL) 1874 schar = SCHAR; 1875 else { 1876 schar = *eoptarg++; 1877 if (*eoptarg != '\0') { 1878 (void)fprintf(err, 1879 "pr: invalid value for -s %s\n", eoptarg); 1880 return(1); 1881 } 1882 } 1883 break; 1884 case 't': 1885 ++nohead; 1886 break; 1887 case 'w': 1888 ++wflag; 1889 if (!isdigit(*eoptarg) || ((pgwd = atoi(eoptarg)) < 1)){ 1890 (void)fputs( 1891 "pr: -w width must be 1 or more \n",err); 1892 return(1); 1893 } 1894 break; 1895 case '?': 1896 default: 1897 return(1); 1898 } 1899 } 1900 1901 /* 1902 * default and sanity checks 1903 */ 1904 inform++; 1905 1906 if (!clcnt) { 1907 if (merge) { 1908 if ((clcnt = argc - eoptind) <= 1) { 1909 clcnt = CLCNT; 1910 #ifdef stupid 1911 merge = 0; 1912 #endif 1913 } 1914 } else 1915 clcnt = CLCNT; 1916 } 1917 if (across) { 1918 if (clcnt == 1) { 1919 (void)fputs("pr: -a flag requires multiple columns\n", 1920 err); 1921 return(1); 1922 } 1923 if (merge) { 1924 (void)fputs("pr: -m cannot be used with -a\n", err); 1925 return(1); 1926 } 1927 } 1928 if (!wflag) { 1929 if (sflag) 1930 pgwd = SPGWD; 1931 else 1932 pgwd = PGWD; 1933 } 1934 if (cflag || merge) { 1935 if (!eflag) { 1936 inchar = INCHAR; 1937 ingap = INGAP; 1938 } 1939 if (!iflag) { 1940 ochar = OCHAR; 1941 ogap = OGAP; 1942 } 1943 } 1944 if (cflag) { 1945 if (merge) { 1946 (void)fputs( 1947 "pr: -m cannot be used with multiple columns\n", err); 1948 return(1); 1949 } 1950 if (nmwd) { 1951 colwd = (pgwd + 1 - (clcnt * (nmwd + 2)))/clcnt; 1952 pgwd = ((colwd + nmwd + 2) * clcnt) - 1; 1953 } else { 1954 colwd = (pgwd + 1 - clcnt)/clcnt; 1955 pgwd = ((colwd + 1) * clcnt) - 1; 1956 } 1957 if (colwd < 1) { 1958 (void)fprintf(err, 1959 "pr: page width is too small for %d columns\n",clcnt); 1960 return(1); 1961 } 1962 } 1963 if (!lines) 1964 lines = LINES; 1965 1966 /* 1967 * make sure long enough for headers. if not disable 1968 */ 1969 if (lines <= HEADLEN + TAILLEN) 1970 ++nohead; 1971 else if (!nohead) 1972 lines -= HEADLEN + TAILLEN; 1973 1974 /* 1975 * adjust for double space on odd length pages 1976 */ 1977 if (dspace) { 1978 if (lines == 1) 1979 dspace = 0; 1980 else { 1981 if (lines & 1) 1982 ++addone; 1983 lines /= 2; 1984 } 1985 } 1986 1987 if ((timefrmt = getenv("LC_TIME")) == NULL) 1988 timefrmt = TIMEFMT; 1989 return(0); 1990 } 1991