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