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