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