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