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