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