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