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