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