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