1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T 29 * All Rights Reserved 30 */ 31 32 /* 33 * University Copyright- Copyright (c) 1982, 1986, 1988 34 * The Regents of the University of California 35 * All Rights Reserved 36 * 37 * University Acknowledgment- Portions of this document are derived from 38 * software developed by the University of California, Berkeley, and its 39 * contributors. 40 */ 41 42 /* 43 * PR command (print files in pages and columns, with headings) 44 * 2+head+2+page[56]+5 45 */ 46 47 #include <stdio.h> 48 #include <signal.h> 49 #include <ctype.h> 50 #include <sys/types.h> 51 #include <sys/stat.h> 52 #include <unistd.h> 53 #include <stdlib.h> 54 #include <locale.h> 55 #include <string.h> 56 #include <limits.h> 57 #include <wchar.h> 58 #include <errno.h> 59 60 #define ESC '\033' 61 #define LENGTH 66 62 #define LINEW 72 63 #define NUMW 5 64 #define MARGIN 10 65 #define DEFTAB 8 66 #define NFILES 10 67 #define STDINNAME() nulls 68 #define PROMPT() (void) putc('\7', stderr) /* BEL */ 69 #define NOFILE nulls 70 #define ETABS (Inpos % Etabn) 71 #define NSEPC '\t' 72 #define HEAD gettext("%s %s Page %d\n\n\n"), date, head, Page 73 #define cerror(S) (void) fprintf(stderr, "pr: %s", gettext(S)) 74 #define done() if (Ttyout) (void) chmod(Ttyout, Mode) 75 #define ALL_NUMS(s) (strspn(s, "0123456789") == strlen(s)) 76 #define REMOVE_ARG(argc, argp) \ 77 { \ 78 char **p = argp; \ 79 while (*p != NULL) \ 80 { \ 81 *p = *(p + 1); \ 82 p++; \ 83 } \ 84 argc--; \ 85 } 86 #define SQUEEZE_ARG(argp, ind, n) \ 87 { \ 88 int i; \ 89 for (i = ind; argp[i]; i++) \ 90 argp[i] = argp[i + n]; \ 91 } 92 93 /* 94 * ---date time format--- 95 * b -- abbreviated month name 96 * e -- day of month 97 * H -- Hour (24 hour version) 98 * M -- Minute 99 * Y -- Year in the form ccyy 100 */ 101 #define FORMAT "%b %e %H:%M %Y" 102 103 typedef int ANY; 104 typedef unsigned int UNS; 105 typedef struct { FILE *f_f; char *f_name; wchar_t f_nextc; } FILS; 106 typedef struct {int fold; int skip; int eof; } foldinf; 107 typedef struct { wchar_t *c_ptr, *c_ptr0; long c_lno; int c_skip; } *COLP; 108 typedef struct err { struct err *e_nextp; char *e_mess; } ERR; 109 110 /* 111 * Global data. 112 */ 113 static FILS *Files; 114 static mode_t Mode; 115 static int Multi = 0; 116 static int Nfiles = 0; 117 static int Error = 0; 118 static char nulls[] = ""; 119 static char *Ttyout; 120 static char obuf[BUFSIZ]; 121 static char time_buf[50]; /* array to hold the time and date */ 122 static long Lnumb = 0; 123 static FILE *Ttyin = stdin; 124 static int Dblspace = 1; 125 static int Fpage = 1; 126 static int Formfeed = 0; 127 static int Length = LENGTH; 128 static int Linew = 0; 129 static int Offset = 0; 130 static int Ncols = 0; 131 static int Pause = 0; 132 static wchar_t Sepc = 0; 133 static int Colw; 134 static int Plength; 135 static int Margin = MARGIN; 136 static int Numw; 137 static int Nsepc = NSEPC; 138 static int Report = 1; 139 static int Etabn = 0; 140 static wchar_t Etabc = '\t'; 141 static int Itabn = 0; 142 static wchar_t Itabc = '\t'; 143 static int fold = 0; 144 static int foldcol = 0; 145 static int alleof = 0; 146 static char *Head = NULL; 147 static wchar_t *Buffer = NULL, *Bufend, *Bufptr; 148 static UNS Buflen; 149 static COLP Colpts; 150 static foldinf *Fcol; 151 static int Page; 152 static wchar_t C = '\0'; 153 static int Nspace; 154 static int Inpos; 155 static int Outpos; 156 static int Lcolpos; 157 static int Pcolpos; 158 static int Line; 159 static ERR *Err = NULL; 160 static ERR *Lasterr = (ERR *)&Err; 161 static int mbcurmax = 1; 162 163 /* 164 * Function prototypes. 165 */ 166 static void onintr(); 167 static ANY *getspace(); 168 static int findopt(int, char **); 169 static void fixtty(); 170 static char *GETDATE(); 171 static char *ffiler(char *); 172 static int print(char *); 173 static void putpage(); 174 static void foldpage(); 175 static void nexbuf(); 176 static void foldbuf(); 177 static void balance(int); 178 static int readbuf(wchar_t **, int, COLP); 179 static wint_t get(int); 180 static int put(wchar_t); 181 static void putspace(); 182 static void unget(int); 183 static FILE *mustopen(char *, FILS *); 184 static void die(char *); 185 static void errprint(); 186 static void usage(int); 187 static wint_t _fgetwc_pr(FILE *, int *); 188 static size_t freadw(wchar_t *, size_t, FILE *); 189 190 191 int 192 main(int argc, char **argv) 193 { 194 FILS fstr[NFILES]; 195 int nfdone = 0; 196 197 198 /* Get locale variables for environment */ 199 (void) setlocale(LC_ALL, ""); 200 201 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 202 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 203 #endif 204 (void) textdomain(TEXT_DOMAIN); 205 206 mbcurmax = MB_CUR_MAX; 207 Files = fstr; 208 for (argc = findopt(argc, argv); argc > 0; --argc, ++argv) { 209 if (Multi == 'm') { 210 if (Nfiles >= NFILES - 1) die("too many files"); 211 if (mustopen(*argv, &Files[Nfiles++]) == NULL) 212 ++nfdone; /* suppress printing */ 213 } else { 214 if (print(*argv)) 215 (void) fclose(Files->f_f); 216 ++nfdone; 217 } 218 } 219 if (!nfdone) /* no files named, use stdin */ 220 (void) print(NOFILE); /* on GCOS, use current file, if any */ 221 222 if (Report) { 223 errprint(); /* print accumulated error reports */ 224 exit(Error); 225 } 226 227 return (Error); 228 } 229 230 231 /* 232 * findopt() returns argc modified to be the number of explicitly supplied 233 * filenames, including '-', the explicit request to use stdin. 234 * argc == 0 implies that no filenames were supplied and stdin should be used. 235 * Options are striped from argv and only file names are returned. 236 */ 237 238 static int 239 findopt(int argc, char **argv) 240 { 241 int eargc = 0; 242 int c; 243 int mflg = 0; 244 int aflg = 0; 245 int optnum; 246 int argv_ind; 247 int end_opt; 248 int i; 249 int isarg = 0; 250 251 fixtty(); 252 253 /* Handle page number option */ 254 for (optnum = 1, end_opt = 0; optnum < argc && !end_opt; optnum++) { 255 switch (*argv[optnum]) { 256 case '+': 257 /* check for all digits */ 258 if (strlen(&argv[optnum][1]) != 259 strspn(&argv[optnum][1], "0123456789")) { 260 (void) fprintf(stderr, gettext( 261 "pr: Badly formed number\n")); 262 exit(1); 263 } 264 265 if ((Fpage = (int)strtol(&argv[optnum][1], 266 (char **)NULL, 10)) < 0) { 267 (void) fprintf(stderr, gettext( 268 "pr: Badly formed number\n")); 269 exit(1); 270 } 271 REMOVE_ARG(argc, &argv[optnum]); 272 optnum--; 273 break; 274 275 case '-': 276 /* Check for end of options */ 277 if (argv[optnum][1] == '-') { 278 end_opt++; 279 break; 280 } 281 282 if (argv[optnum][1] == 'h' || argv[optnum][1] == 'l' || 283 argv[optnum][1] == 'o' || argv[optnum][1] == 'w') 284 isarg = 1; 285 else 286 isarg = 0; 287 288 break; 289 290 default: 291 if (isarg == 0) 292 end_opt++; 293 else 294 isarg = 0; 295 break; 296 } 297 } 298 299 /* 300 * Handle options with optional arguments. 301 * If optional arguments are present they may not be separated 302 * from the option letter. 303 */ 304 305 for (optnum = 1; optnum < argc; optnum++) { 306 if (argv[optnum][0] == '-' && argv[optnum][1] == '-') 307 /* End of options */ 308 break; 309 310 if (argv[optnum][0] == '-' && argv[optnum][1] == '\0') 311 /* stdin file name */ 312 continue; 313 314 if (argv[optnum][0] != '-') 315 /* not option */ 316 continue; 317 318 for (argv_ind = 1; argv[optnum][argv_ind] != '\0'; argv_ind++) { 319 switch (argv[optnum][argv_ind]) { 320 case 'e': 321 SQUEEZE_ARG(argv[optnum], argv_ind, 1); 322 if ((c = argv[optnum][argv_ind]) != '\0' && 323 !isdigit(c)) { 324 int r; 325 wchar_t wc; 326 r = mbtowc(&wc, &argv[optnum][argv_ind], 327 mbcurmax); 328 if (r == -1) { 329 (void) fprintf(stderr, gettext( 330 "pr: Illegal character in -e option\n")); 331 exit(1); 332 } 333 Etabc = wc; 334 SQUEEZE_ARG(argv[optnum], argv_ind, r); 335 } 336 if (isdigit(argv[optnum][argv_ind])) { 337 Etabn = (int)strtol(&argv[optnum] 338 [argv_ind], (char **)NULL, 10); 339 while (isdigit(argv[optnum][argv_ind])) 340 SQUEEZE_ARG(argv[optnum], 341 argv_ind, 1); 342 } 343 if (Etabn <= 0) 344 Etabn = DEFTAB; 345 argv_ind--; 346 break; 347 348 case 'i': 349 SQUEEZE_ARG(argv[optnum], argv_ind, 1); 350 if ((c = argv[optnum][argv_ind]) != '\0' && 351 !isdigit(c)) { 352 int r; 353 wchar_t wc; 354 r = mbtowc(&wc, &argv[optnum][argv_ind], 355 mbcurmax); 356 if (r == -1) { 357 (void) fprintf(stderr, gettext( 358 "pr: Illegal character in -i option\n")); 359 exit(1); 360 } 361 Itabc = wc; 362 SQUEEZE_ARG(argv[optnum], argv_ind, r); 363 } 364 if (isdigit(argv[optnum][argv_ind])) { 365 Itabn = (int)strtol(&argv[optnum] 366 [argv_ind], (char **)NULL, 10); 367 while (isdigit(argv[optnum][argv_ind])) 368 SQUEEZE_ARG(argv[optnum], 369 argv_ind, 1); 370 } 371 if (Itabn <= 0) 372 Itabn = DEFTAB; 373 argv_ind--; 374 break; 375 376 377 case 'n': 378 ++Lnumb; 379 SQUEEZE_ARG(argv[optnum], argv_ind, 1); 380 if ((c = argv[optnum][argv_ind]) != '\0' && 381 !isdigit(c)) { 382 int r; 383 wchar_t wc; 384 r = mbtowc(&wc, &argv[optnum][argv_ind], 385 mbcurmax); 386 if (r == -1) { 387 (void) fprintf(stderr, gettext( 388 "pr: Illegal character in -n option\n")); 389 exit(1); 390 } 391 Nsepc = wc; 392 SQUEEZE_ARG(argv[optnum], argv_ind, r); 393 } 394 if (isdigit(argv[optnum][argv_ind])) { 395 Numw = (int)strtol(&argv[optnum] 396 [argv_ind], (char **)NULL, 10); 397 while (isdigit(argv[optnum][argv_ind])) 398 SQUEEZE_ARG(argv[optnum], 399 argv_ind, 1); 400 } 401 argv_ind--; 402 if (!Numw) 403 Numw = NUMW; 404 break; 405 406 case 's': 407 SQUEEZE_ARG(argv[optnum], argv_ind, 1); 408 if ((Sepc = argv[optnum][argv_ind]) == '\0') 409 Sepc = '\t'; 410 else { 411 int r; 412 wchar_t wc; 413 r = mbtowc(&wc, &argv[optnum][argv_ind], 414 mbcurmax); 415 if (r == -1) { 416 (void) fprintf(stderr, gettext( 417 "pr: Illegal character in -s option\n")); 418 exit(1); 419 } 420 Sepc = wc; 421 SQUEEZE_ARG(argv[optnum], argv_ind, r); 422 } 423 argv_ind--; 424 break; 425 426 default: 427 break; 428 } 429 } 430 if (argv[optnum][0] == '-' && argv[optnum][1] == '\0') { 431 REMOVE_ARG(argc, &argv[optnum]); 432 optnum--; 433 } 434 } 435 436 /* Now get the other options */ 437 while ((c = getopt(argc, argv, "0123456789adfFh:l:mo:prtw:")) 438 != EOF) { 439 switch (c) { 440 case '0': 441 case '1': 442 case '2': 443 case '3': 444 case '4': 445 case '5': 446 case '6': 447 case '7': 448 case '8': 449 case '9': 450 Ncols *= 10; 451 Ncols += c - '0'; 452 break; 453 454 case 'a': 455 aflg++; 456 if (!Multi) 457 Multi = c; 458 break; 459 460 case 'd': 461 Dblspace = 2; 462 break; 463 464 case 'f': 465 ++Formfeed; 466 ++Pause; 467 break; 468 469 case 'h': 470 Head = optarg; 471 break; 472 473 case 'l': 474 if (strlen(optarg) != strspn(optarg, "0123456789")) 475 usage(1); 476 Length = (int)strtol(optarg, (char **)NULL, 10); 477 break; 478 479 case 'm': 480 mflg++; 481 Multi = c; 482 break; 483 484 case 'o': 485 if (strlen(optarg) != strspn(optarg, "0123456789")) 486 usage(1); 487 Offset = (int)strtol(optarg, (char **)NULL, 10); 488 break; 489 490 case 'p': 491 ++Pause; 492 break; 493 494 case 'r': 495 Report = 0; 496 break; 497 498 case 't': 499 Margin = 0; 500 break; 501 502 case 'w': 503 if (strlen(optarg) != strspn(optarg, "0123456789")) 504 usage(1); 505 Linew = (int)strtol(optarg, (char **)NULL, 10); 506 break; 507 508 case 'F': 509 #ifdef XPG4 510 ++Formfeed; 511 #else 512 fold++; 513 #endif 514 break; 515 516 case '?': 517 usage(2); 518 break; 519 520 default : 521 usage(2); 522 } 523 } 524 525 /* Count the file names and strip options */ 526 for (i = 1; i < argc; i++) { 527 /* Check for explicit stdin */ 528 if ((argv[i][0] == '-') && (argv[i][1] == '\0')) { 529 argv[eargc++][0] = '\0'; 530 REMOVE_ARG(argc, &argv[i]); 531 if (i < optind) 532 optind--; 533 } 534 } 535 for (i = eargc; optind < argc; i++, optind++) { 536 argv[i] = argv[optind]; 537 eargc++; 538 } 539 540 /* Check options */ 541 if (Ncols == 0) 542 Ncols = 1; 543 544 if (mflg && (Ncols > 1)) { 545 (void) fprintf(stderr, 546 gettext("pr: only one of either -m or -column allowed\n")); 547 usage(1); 548 } 549 550 if (Ncols == 1 && fold) 551 Multi = 'm'; 552 553 if (Length <= 0) 554 Length = LENGTH; 555 556 if (Length <= Margin) 557 Margin = 0; 558 559 Plength = Length - Margin/2; 560 561 if (Multi == 'm') 562 Ncols = eargc; 563 564 switch (Ncols) { 565 case 0: 566 Ncols = 1; 567 break; 568 569 case 1: 570 break; 571 572 default: 573 if (Etabn == 0) /* respect explicit tab specification */ 574 Etabn = DEFTAB; 575 if (Itabn == 0) 576 Itabn = DEFTAB; 577 } 578 579 if ((Fcol = (foldinf *) malloc(sizeof (foldinf) * Ncols)) == NULL) { 580 (void) fprintf(stderr, gettext("pr: malloc failed\n")); 581 exit(1); 582 } 583 for (i = 0; i < Ncols; i++) 584 Fcol[i].fold = Fcol[i].skip = 0; 585 586 if (Linew == 0) 587 Linew = Ncols != 1 && Sepc == 0 ? LINEW : 512; 588 589 if (Lnumb) { 590 int numw; 591 592 if (Nsepc == '\t') { 593 if (Itabn == 0) 594 numw = Numw + DEFTAB - (Numw % DEFTAB); 595 else 596 numw = Numw + Itabn - (Numw % Itabn); 597 } else { 598 numw = Numw + ((iswprint(Nsepc)) ? 1 : 0); 599 } 600 Linew -= (Multi == 'm') ? numw : numw * Ncols; 601 } 602 603 if ((Colw = (Linew - Ncols + 1)/Ncols) < 1) 604 die("width too small"); 605 606 if (Ncols != 1 && Multi == 0) { 607 /* Buflen should take the number of wide characters */ 608 /* Not the size for Buffer */ 609 Buflen = ((UNS) (Plength / Dblspace + 1)) * 610 2 * (Linew + 1); 611 /* Should allocate Buflen * sizeof (wchar_t) */ 612 Buffer = (wchar_t *)getspace(Buflen * sizeof (wchar_t)); 613 Bufptr = Bufend = &Buffer[Buflen]; 614 Colpts = (COLP) getspace((UNS) ((Ncols + 1) * 615 sizeof (*Colpts))); 616 Colpts[0].c_lno = 0; 617 } 618 619 /* is stdin not a tty? */ 620 if (Ttyout && (Pause || Formfeed) && !ttyname(fileno(stdin))) 621 Ttyin = fopen("/dev/tty", "r"); 622 623 return (eargc); 624 } 625 626 627 static int 628 print(char *name) 629 { 630 static int notfirst = 0; 631 char *date = NULL; 632 char *head = NULL; 633 int c; 634 635 if (Multi != 'm' && mustopen(name, &Files[0]) == NULL) 636 return (0); 637 if (Multi == 'm' && Nfiles == 0 && mustopen(name, &Files[0]) == NULL) 638 die("cannot open stdin"); 639 if (Buffer) 640 (void) ungetwc(Files->f_nextc, Files->f_f); 641 if (Lnumb) 642 Lnumb = 1; 643 for (Page = 0; ; putpage()) { 644 if (C == WEOF && !(fold && Buffer)) 645 break; 646 if (Buffer) 647 nexbuf(); 648 Inpos = 0; 649 if (get(0) == WEOF) 650 break; 651 (void) fflush(stdout); 652 if (++Page >= Fpage) { 653 /* Pause if -p and not first page */ 654 if (Ttyout && Pause && !notfirst++) { 655 PROMPT(); /* prompt with bell and pause */ 656 while ((c = getc(Ttyin)) != EOF && c != '\n') 657 ; 658 } 659 if (Margin == 0) 660 continue; 661 if (date == NULL) 662 date = GETDATE(); 663 if (head == NULL) 664 head = Head != NULL ? Head : 665 Nfiles < 2 ? Files->f_name : nulls; 666 (void) printf("\n\n"); 667 Nspace = Offset; 668 putspace(); 669 (void) printf(HEAD); 670 } 671 } 672 C = '\0'; 673 return (1); 674 } 675 676 677 static void 678 putpage() 679 { 680 int colno; 681 682 if (fold) { 683 foldpage(); 684 return; 685 } 686 for (Line = Margin / 2; ; (void) get(0)) { 687 for (Nspace = Offset, colno = 0, Outpos = 0; C != '\f'; ) { 688 if (Lnumb && (C != WEOF) && 689 (((colno == 0) && (Multi == 'm')) || 690 (Multi != 'm'))) { 691 if (Page >= Fpage) { 692 putspace(); 693 (void) printf("%*ld%wc", Numw, Buffer ? 694 Colpts[colno].c_lno++ : 695 Lnumb, Nsepc); 696 697 /* Move Outpos for number field */ 698 Outpos += Numw; 699 if (Nsepc == '\t') 700 Outpos += 701 DEFTAB - (Outpos % DEFTAB); 702 else 703 Outpos++; 704 } 705 ++Lnumb; 706 } 707 for (Lcolpos = 0, Pcolpos = 0; 708 C != '\n' && C != '\f' && C != WEOF; 709 (void) get(colno)) 710 (void) put(C); 711 712 if ((C == WEOF) || (++colno == Ncols) || 713 ((C == '\n') && (get(colno) == WEOF))) 714 break; 715 716 if (Sepc) 717 (void) put(Sepc); 718 else if ((Nspace += Colw - Lcolpos + 1) < 1) 719 Nspace = 1; 720 } 721 722 if (C == WEOF) { 723 if (Margin != 0) 724 break; 725 if (colno != 0) 726 (void) put('\n'); 727 return; 728 } 729 if (C == '\f') 730 break; 731 (void) put('\n'); 732 if (Dblspace == 2 && Line < Plength) 733 (void) put('\n'); 734 if (Line >= Plength) 735 break; 736 } 737 if (Formfeed) 738 (void) put('\f'); 739 else 740 while (Line < Length) 741 (void) put('\n'); 742 } 743 744 745 static void 746 foldpage() 747 { 748 int colno; 749 int keep; 750 int i; 751 int pLcolpos; 752 static int sl; 753 754 for (Line = Margin / 2; ; (void) get(0)) { 755 for (Nspace = Offset, colno = 0, Outpos = 0; C != '\f'; ) { 756 if (Lnumb && Multi == 'm' && foldcol) { 757 if (!Fcol[colno].skip) { 758 unget(colno); 759 putspace(); 760 if (!colno) { 761 for (i = 0; i <= Numw; i++) 762 (void) printf(" "); 763 (void) printf("%wc", Nsepc); 764 } 765 for (i = 0; i <= Colw; i++) 766 (void) printf(" "); 767 (void) put(Sepc); 768 if (++colno == Ncols) 769 break; 770 (void) get(colno); 771 continue; 772 } else if (!colno) 773 Lnumb = sl; 774 } 775 776 if (Lnumb && (C != WEOF) && 777 ((colno == 0 && Multi == 'm') || (Multi != 'm'))) { 778 if (Page >= Fpage) { 779 putspace(); 780 if ((foldcol && 781 Fcol[colno].skip && Multi != 'a') || 782 (Fcol[0].fold && Multi == 'a') || 783 (Buffer && Colpts[colno].c_skip)) { 784 for (i = 0; i < Numw; i++) 785 (void) printf(" "); 786 (void) printf("%wc", Nsepc); 787 if (Buffer) { 788 Colpts[colno].c_lno++; 789 Colpts[colno].c_skip = 790 0; 791 } 792 } 793 else 794 (void) printf("%*ld%wc", Numw, Buffer ? 795 Colpts[colno].c_lno++ : 796 Lnumb, Nsepc); 797 } 798 sl = Lnumb++; 799 } 800 pLcolpos = 0; 801 for (Lcolpos = 0, Pcolpos = 0; 802 C != '\n' && C != '\f' && C != WEOF; 803 (void) get(colno)) { 804 if (put(C)) { 805 unget(colno); 806 Fcol[(Multi == 'a') ? 0 : colno].fold 807 = 1; 808 break; 809 } else if (Multi == 'a') { 810 Fcol[0].fold = 0; 811 } 812 pLcolpos = Lcolpos; 813 } 814 if (Buffer) { 815 alleof = 1; 816 for (i = 0; i < Ncols; i++) 817 if (!Fcol[i].eof) 818 alleof = 0; 819 if (alleof || ++colno == Ncols) 820 break; 821 } else if (C == EOF || ++colno == Ncols) 822 break; 823 keep = C; 824 (void) get(colno); 825 if (keep == '\n' && C == WEOF) 826 break; 827 if (Sepc) 828 (void) put(Sepc); 829 else if ((Nspace += Colw - pLcolpos + 1) < 1) 830 Nspace = 1; 831 } 832 foldcol = 0; 833 if (Lnumb && Multi != 'a') { 834 for (i = 0; i < Ncols; i++) { 835 Fcol[i].skip = Fcol[i].fold; 836 foldcol += Fcol[i].fold; 837 Fcol[i].fold = 0; 838 } 839 } 840 if (C == WEOF) { 841 if (Margin != 0) 842 break; 843 if (colno != 0) 844 (void) put('\n'); 845 return; 846 } 847 if (C == '\f') 848 break; 849 (void) put('\n'); 850 (void) fflush(stdout); 851 if (Dblspace == 2 && Line < Plength) 852 (void) put('\n'); 853 if (Line >= Plength) 854 break; 855 } 856 if (Formfeed) 857 (void) put('\f'); 858 else while (Line < Length) 859 (void) put('\n'); 860 } 861 862 863 static void 864 nexbuf() 865 { 866 wchar_t *s = Buffer; 867 COLP p = Colpts; 868 int j; 869 int c; 870 int bline = 0; 871 wchar_t wc; 872 873 if (fold) { 874 foldbuf(); 875 return; 876 } 877 for (; ; ) { 878 p->c_ptr0 = p->c_ptr = s; 879 if (p == &Colpts[Ncols]) 880 return; 881 (p++)->c_lno = Lnumb + bline; 882 for (j = (Length - Margin)/Dblspace; --j >= 0; ++bline) { 883 for (Inpos = 0; ; ) { 884 errno = 0; 885 wc = _fgetwc_pr(Files->f_f, &c); 886 if (wc == WEOF) { 887 /* If there is an illegal character, */ 888 /* handle it as a byte sequence. */ 889 if (errno == EILSEQ) { 890 if (Inpos < Colw - 1) { 891 *s = c; 892 if (++s >= Bufend) 893 die("page-buffer overflow"); 894 } 895 Inpos++; 896 Error++; 897 return; 898 } else { 899 /* Real EOF */ 900 for (*s = WEOF; p <= &Colpts[Ncols]; ++p) 901 p->c_ptr0 = p->c_ptr = s; 902 balance(bline); 903 return; 904 } 905 } 906 907 if (isascii(wc)) { 908 if (isprint(wc)) 909 Inpos++; 910 } else if (iswprint(wc)) { 911 Inpos += wcwidth(wc); 912 } 913 914 if (Inpos <= Colw || wc == '\n') { 915 *s = wc; 916 if (++s >= Bufend) 917 die("page-buffer overflow"); 918 } 919 if (wc == '\n') 920 break; 921 switch (wc) { 922 case '\b': 923 if (Inpos == 0) 924 --s; 925 926 /*FALLTHROUGH*/ 927 928 case ESC: 929 if (Inpos > 0) 930 --Inpos; 931 } 932 } 933 } 934 } 935 } 936 937 938 static void 939 foldbuf() 940 { 941 int num; 942 int i; 943 int colno = 0; 944 int size = Buflen; 945 wchar_t *s; 946 wchar_t *d; 947 COLP p = Colpts; 948 949 for (i = 0; i < Ncols; i++) 950 Fcol[i].eof = 0; 951 d = Buffer; 952 if (Bufptr != Bufend) { 953 s = Bufptr; 954 while (s < Bufend) 955 *d++ = *s++; 956 size -= (Bufend - Bufptr); 957 } 958 Bufptr = Buffer; 959 p->c_ptr0 = p->c_ptr = Buffer; 960 if (p->c_lno == 0) { 961 p->c_lno = Lnumb; 962 p->c_skip = 0; 963 } else { 964 p->c_lno = Colpts[Ncols-1].c_lno; 965 p->c_skip = Colpts[Ncols].c_skip; 966 if (p->c_skip) 967 p->c_lno--; 968 } 969 if ((num = freadw(d, size, Files->f_f)) != size) { 970 for (*(d+num) = WEOF; (++p) <= &Colpts[Ncols]; ) { 971 p->c_ptr0 = p->c_ptr = (d+num); 972 } 973 balance(0); 974 return; 975 } 976 i = (Length - Margin) / Dblspace; 977 do { 978 (void) readbuf(&Bufptr, i, p++); 979 } while (++colno < Ncols); 980 } 981 982 983 static void 984 balance(int bline) /* line balancing for last page */ 985 { 986 wchar_t *s = Buffer; 987 COLP p = Colpts; 988 int colno = 0; 989 int j; 990 int c; 991 int l; 992 int lines; 993 994 if (!fold) { 995 c = bline % Ncols; 996 l = (bline + Ncols - 1)/Ncols; 997 bline = 0; 998 do { 999 for (j = 0; j < l; ++j) 1000 while (*s++ != '\n') 1001 ; 1002 (++p)->c_lno = Lnumb + (bline += l); 1003 p->c_ptr0 = p->c_ptr = s; 1004 if (++colno == c) 1005 --l; 1006 } while (colno < Ncols - 1); 1007 } else { 1008 lines = readbuf(&s, 0, 0); 1009 l = (lines + Ncols - 1)/Ncols; 1010 if (l > ((Length - Margin) / Dblspace)) { 1011 l = (Length - Margin) / Dblspace; 1012 c = Ncols; 1013 } else { 1014 c = lines % Ncols; 1015 } 1016 s = Buffer; 1017 do { 1018 (void) readbuf(&s, l, p++); 1019 if (++colno == c) 1020 --l; 1021 } while (colno < Ncols); 1022 Bufptr = s; 1023 } 1024 } 1025 1026 1027 static int 1028 readbuf(wchar_t **s, int lincol, COLP p) 1029 { 1030 int lines = 0; 1031 int chars = 0; 1032 int width; 1033 int nls = 0; 1034 int move; 1035 int skip = 0; 1036 int decr = 0; 1037 1038 width = (Ncols == 1) ? Linew : Colw; 1039 while (**s != WEOF) { 1040 switch (**s) { 1041 case '\n': 1042 lines++; nls++; chars = 0; skip = 0; 1043 break; 1044 1045 case '\b': 1046 case ESC: 1047 if (chars) chars--; 1048 break; 1049 1050 case '\t': 1051 move = Itabn - ((chars + Itabn) % Itabn); 1052 move = (move < width-chars) ? move : 1053 width-chars; 1054 chars += move; 1055 /* FALLTHROUGH */ 1056 1057 default: 1058 if (isascii(**s)) { 1059 if (isprint(**s)) 1060 chars++; 1061 } else if (iswprint(**s)) { 1062 chars += wcwidth(**s); 1063 } 1064 } 1065 if (chars > width) { 1066 lines++; 1067 skip++; 1068 decr++; 1069 chars = 0; 1070 } 1071 if (lincol && lines == lincol) { 1072 (p+1)->c_lno = p->c_lno + nls; 1073 (++p)->c_skip = skip; 1074 if (**s == '\n') (*s)++; 1075 p->c_ptr0 = p->c_ptr = (wchar_t *)*s; 1076 return (0); 1077 } 1078 if (decr) 1079 decr = 0; 1080 else 1081 (*s)++; 1082 } 1083 return (lines); 1084 } 1085 1086 1087 static wint_t 1088 get(int colno) 1089 { 1090 static int peekc = 0; 1091 COLP p; 1092 FILS *q; 1093 int c; 1094 wchar_t wc, w; 1095 1096 if (peekc) { 1097 peekc = 0; 1098 wc = Etabc; 1099 } else if (Buffer) { 1100 p = &Colpts[colno]; 1101 if (p->c_ptr >= (p+1)->c_ptr0) 1102 wc = WEOF; 1103 else if ((wc = *p->c_ptr) != WEOF) 1104 ++p->c_ptr; 1105 if (fold && wc == WEOF) 1106 Fcol[colno].eof = 1; 1107 } else if ((wc = 1108 (q = &Files[Multi == 'a' ? 0 : colno])->f_nextc) == WEOF) { 1109 for (q = &Files[Nfiles]; --q >= Files && q->f_nextc == WEOF; ) 1110 ; 1111 if (q >= Files) 1112 wc = '\n'; 1113 } else { 1114 errno = 0; 1115 w = _fgetwc_pr(q->f_f, &c); 1116 if (w == WEOF && errno == EILSEQ) { 1117 q->f_nextc = (wchar_t)c; 1118 } else { 1119 q->f_nextc = w; 1120 } 1121 } 1122 1123 if (Etabn != 0 && wc == Etabc) { 1124 ++Inpos; 1125 peekc = ETABS; 1126 wc = ' '; 1127 return (C = wc); 1128 } 1129 1130 if (wc == WEOF) 1131 return (C = wc); 1132 1133 if (isascii(wc)) { 1134 if (isprint(wc)) { 1135 Inpos++; 1136 return (C = wc); 1137 } 1138 } else if (iswprint(wc)) { 1139 Inpos += wcwidth(wc); 1140 return (C = wc); 1141 } 1142 1143 switch (wc) { 1144 case '\b': 1145 case ESC: 1146 if (Inpos > 0) 1147 --Inpos; 1148 break; 1149 case '\f': 1150 if (Ncols == 1) 1151 break; 1152 wc = '\n'; 1153 /* FALLTHROUGH */ 1154 case '\n': 1155 case '\r': 1156 Inpos = 0; 1157 break; 1158 } 1159 return (C = wc); 1160 } 1161 1162 1163 static int 1164 put(wchar_t wc) 1165 { 1166 int move = 0; 1167 int width = Colw; 1168 int sp = Lcolpos; 1169 1170 if (fold && Ncols == 1) 1171 width = Linew; 1172 1173 switch (wc) { 1174 case ' ': 1175 /* If column not full or this is separator char */ 1176 if ((!fold && Ncols < 2) || (Lcolpos < width) || 1177 ((Sepc == wc) && (Lcolpos == width))) { 1178 ++Nspace; 1179 ++Lcolpos; 1180 } 1181 if (fold && sp == Lcolpos) 1182 if (Lcolpos >= width) 1183 return (1); 1184 1185 return (0); 1186 1187 case '\t': 1188 if (Itabn == 0) 1189 break; 1190 1191 /* If column not full or this is separator char */ 1192 if ((Lcolpos < width) || 1193 ((Sepc == wc) && (Lcolpos == width))) { 1194 move = Itabn - ((Lcolpos + Itabn) % Itabn); 1195 move = (move < width-Lcolpos) ? move : width-Lcolpos; 1196 Nspace += move; 1197 Lcolpos += move; 1198 } 1199 if (fold && sp == Lcolpos) 1200 if (Lcolpos >= width) 1201 return (1); 1202 return (0); 1203 1204 case '\b': 1205 if (Lcolpos == 0) 1206 return (0); 1207 if (Nspace > 0) { 1208 --Nspace; 1209 --Lcolpos; 1210 return (0); 1211 } 1212 if (Lcolpos > Pcolpos) { 1213 --Lcolpos; 1214 return (0); 1215 } 1216 1217 /*FALLTHROUGH*/ 1218 1219 case ESC: 1220 move = -1; 1221 break; 1222 1223 case '\n': 1224 ++Line; 1225 1226 /*FALLTHROUGH*/ 1227 1228 case '\r': 1229 case '\f': 1230 Pcolpos = 0; 1231 Lcolpos = 0; 1232 Nspace = 0; 1233 Outpos = 0; 1234 /* FALLTHROUGH */ 1235 default: 1236 if (isascii(wc)) { 1237 if (isprint(wc)) 1238 move = 1; 1239 else 1240 move = 0; 1241 } else if (iswprint(wc)) { 1242 move = wcwidth(wc); 1243 } else { 1244 move = 0; 1245 } 1246 break; 1247 } 1248 if (Page < Fpage) 1249 return (0); 1250 if (Lcolpos > 0 || move > 0) 1251 Lcolpos += move; 1252 1253 putspace(); 1254 1255 /* If column not full or this is separator char */ 1256 if ((!fold && Ncols < 2) || (Lcolpos <= width) || 1257 ((Sepc == wc) && (Lcolpos > width))) { 1258 (void) fputwc(wc, stdout); 1259 Outpos += move; 1260 Pcolpos = Lcolpos; 1261 } 1262 1263 if (fold && Lcolpos > width) 1264 return (1); 1265 1266 return (0); 1267 } 1268 1269 1270 static void 1271 putspace(void) 1272 { 1273 int nc = 0; 1274 1275 for (; Nspace > 0; Outpos += nc, Nspace -= nc) { 1276 #ifdef XPG4 1277 /* XPG4: -i: replace multiple SPACE chars with tab chars */ 1278 if ((Nspace >= 2 && Itabn > 0 && 1279 Nspace >= (nc = Itabn - Outpos % Itabn)) && !fold) { 1280 #else 1281 /* Solaris: -i: replace white space with tab chars */ 1282 if ((Itabn > 0 && Nspace >= (nc = Itabn - Outpos % Itabn)) && 1283 !fold) { 1284 #endif 1285 (void) fputwc(Itabc, stdout); 1286 } else { 1287 nc = 1; 1288 (void) putchar(' '); 1289 } 1290 } 1291 } 1292 1293 1294 static void 1295 unget(int colno) 1296 { 1297 if (Buffer) { 1298 if (*(Colpts[colno].c_ptr-1) != '\t') 1299 --(Colpts[colno].c_ptr); 1300 if (Colpts[colno].c_lno) 1301 Colpts[colno].c_lno--; 1302 } else { 1303 if ((Multi == 'm' && colno == 0) || Multi != 'm') 1304 if (Lnumb && !foldcol) 1305 Lnumb--; 1306 colno = (Multi == 'a') ? 0 : colno; 1307 (void) ungetwc(Files[colno].f_nextc, Files[colno].f_f); 1308 Files[colno].f_nextc = C; 1309 } 1310 } 1311 1312 1313 /* 1314 * Defer message about failure to open file to prevent messing up 1315 * alignment of page with tear perforations or form markers. 1316 * Treat empty file as special case and report as diagnostic. 1317 */ 1318 1319 static FILE * 1320 mustopen(char *s, FILS *f) 1321 { 1322 char *empty_file_msg = gettext("%s -- empty file"); 1323 int c; 1324 1325 if (*s == '\0') { 1326 f->f_name = STDINNAME(); 1327 f->f_f = stdin; 1328 } else if ((f->f_f = fopen(f->f_name = s, "r")) == NULL) { 1329 s = ffiler(f->f_name); 1330 s = strcpy((char *)getspace((UNS) strlen(s) + 1), s); 1331 } 1332 if (f->f_f != NULL) { 1333 errno = 0; 1334 f->f_nextc = _fgetwc_pr(f->f_f, &c); 1335 if (f->f_nextc != WEOF) { 1336 return (f->f_f); 1337 } else { /* WEOF */ 1338 if (errno == EILSEQ) { 1339 f->f_nextc = (wchar_t)c; 1340 return (f->f_f); 1341 } 1342 if (Multi == 'm') 1343 return (f->f_f); 1344 } 1345 (void) sprintf(s = (char *)getspace((UNS) strlen(f->f_name) 1346 + 1 + (UNS) strlen(empty_file_msg)), 1347 empty_file_msg, f->f_name); 1348 (void) fclose(f->f_f); 1349 } 1350 Error = 1; 1351 if (Report) 1352 if (Ttyout) { /* accumulate error reports */ 1353 Lasterr = Lasterr->e_nextp = 1354 (ERR *) getspace((UNS) sizeof (ERR)); 1355 Lasterr->e_nextp = NULL; 1356 Lasterr->e_mess = s; 1357 } else { /* ok to print error report now */ 1358 cerror(s); 1359 (void) putc('\n', stderr); 1360 } 1361 return ((FILE *)NULL); 1362 } 1363 1364 1365 static ANY * 1366 getspace(UNS n) 1367 { 1368 ANY *t; 1369 1370 if ((t = (ANY *) malloc(n)) == NULL) 1371 die("out of space"); 1372 return (t); 1373 } 1374 1375 1376 static void 1377 die(char *s) 1378 { 1379 ++Error; 1380 errprint(); 1381 cerror(s); 1382 (void) putc('\n', stderr); 1383 exit(1); 1384 1385 /*NOTREACHED*/ 1386 } 1387 1388 1389 static void 1390 errprint() /* print accumulated error reports */ 1391 { 1392 (void) fflush(stdout); 1393 for (; Err != NULL; Err = Err->e_nextp) { 1394 cerror(Err->e_mess); 1395 (void) putc('\n', stderr); 1396 } 1397 done(); 1398 } 1399 1400 1401 static void 1402 fixtty() 1403 { 1404 struct stat sbuf; 1405 1406 setbuf(stdout, obuf); 1407 if (signal(SIGINT, SIG_IGN) != SIG_IGN) 1408 (void) signal(SIGINT, onintr); 1409 if (Ttyout = ttyname(fileno(stdout))) { /* is stdout a tty? */ 1410 (void) stat(Ttyout, &sbuf); 1411 Mode = sbuf.st_mode; /* save permissions */ 1412 (void) chmod(Ttyout, (S_IREAD|S_IWRITE)); 1413 } 1414 } 1415 1416 1417 static void 1418 onintr() 1419 { 1420 ++Error; 1421 errprint(); 1422 _exit(1); 1423 } 1424 1425 1426 static char * 1427 GETDATE() /* return date file was last modified */ 1428 { 1429 static char *now = NULL; 1430 static struct stat sbuf; 1431 static struct stat nbuf; 1432 1433 if (Nfiles > 1 || Files->f_name == nulls) { 1434 if (now == NULL) { 1435 (void) time(&nbuf.st_mtime); 1436 (void) cftime(time_buf, 1437 dcgettext(NULL, FORMAT, LC_TIME), 1438 &nbuf.st_mtime); 1439 now = time_buf; 1440 } 1441 return (now); 1442 } else { 1443 (void) stat(Files->f_name, &sbuf); 1444 (void) cftime(time_buf, dcgettext(NULL, FORMAT, LC_TIME), 1445 &sbuf.st_mtime); 1446 return (time_buf); 1447 } 1448 } 1449 1450 1451 static char * 1452 ffiler(char *s) 1453 { 1454 static char buf[100]; 1455 1456 (void) sprintf(buf, gettext("can't open %s"), s); 1457 return (buf); 1458 } 1459 1460 1461 static void 1462 usage(int rc) 1463 { 1464 (void) fprintf(stderr, gettext( 1465 "usage: pr [-# [-w #] [-a]] [-e[c][#]] [-i[c][#]] [-drtfp] [-n[c][#]] \\\n" 1466 " [-o #] [-l #] [-s[char]] [-h header] [-F] [+#] [file ...]\n\n" 1467 " pr [-m [-w #]] [-e[c][#]] [-i[c][#]] [-drtfp] [-n[c][#]] [-0 #] \\\n" 1468 " [-l #] [-s[char]] [-h header] [-F] [+#] file1 file2 ...\n" 1469 )); 1470 exit(rc); 1471 } 1472 1473 static wint_t 1474 _fgetwc_pr(FILE *f, int *ic) 1475 { 1476 int i; 1477 int len; 1478 char mbuf[MB_LEN_MAX]; 1479 int c; 1480 wchar_t wc; 1481 1482 c = getc(f); 1483 1484 if (c == EOF) 1485 return (WEOF); 1486 if (mbcurmax == 1 || isascii(c)) { 1487 return ((wint_t)c); 1488 } 1489 mbuf[0] = (char)c; 1490 for (i = 1; i < mbcurmax; i++) { 1491 c = getc(f); 1492 if (c == EOF) { 1493 break; 1494 } else { 1495 mbuf[i] = (char)c; 1496 } 1497 } 1498 mbuf[i] = 0; 1499 1500 len = mbtowc(&wc, mbuf, i); 1501 if (len == -1) { 1502 /* Illegal character */ 1503 /* Set the first byte to *ic */ 1504 *ic = mbuf[0]; 1505 /* Push back remaining characters */ 1506 for (i--; i > 0; i--) { 1507 (void) ungetc(mbuf[i], f); 1508 } 1509 errno = EILSEQ; 1510 return (WEOF); 1511 } else { 1512 /* Push back over-read characters */ 1513 for (i--; i >= len; i--) { 1514 (void) ungetc(mbuf[i], f); 1515 } 1516 return ((wint_t)wc); 1517 } 1518 } 1519 1520 static size_t 1521 freadw(wchar_t *ptr, size_t nitems, FILE *f) 1522 { 1523 size_t i; 1524 size_t ret; 1525 int c; 1526 wchar_t *p; 1527 wint_t wc; 1528 1529 if (feof(f)) { 1530 return (0); 1531 } 1532 1533 p = ptr; 1534 ret = 0; 1535 for (i = 0; i < nitems; i++) { 1536 errno = 0; 1537 wc = _fgetwc_pr(f, &c); 1538 if (wc == WEOF) { 1539 if (errno == EILSEQ) { 1540 *p++ = (wchar_t)c; 1541 ret++; 1542 } else { 1543 return (ret); 1544 } 1545 } else { 1546 *p++ = (wchar_t)wc; 1547 ret++; 1548 } 1549 } 1550 return (ret); 1551 } 1552