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