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