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