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