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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 27 /* All Rights Reserved */ 28 29 #include <signal.h> 30 #include <setjmp.h> 31 #include <sys/types.h> 32 #include <sys/dirent.h> 33 #include <sys/stat.h> 34 #include <fcntl.h> 35 #include <ctype.h> 36 #include <stdio.h> 37 #include <wchar.h> 38 #include <curses.h> 39 #include <term.h> 40 #include <errno.h> 41 #include <stdlib.h> 42 #include <regexpr.h> 43 #include <limits.h> 44 #include <locale.h> 45 #include <wctype.h> /* iswprint() */ 46 #include <string.h> 47 #include <unistd.h> 48 #include <wait.h> 49 #include <libw.h> 50 #include <regexpr.h> 51 52 53 /* 54 * pg -- paginator for crt terminals 55 * 56 * Includes the ability to display pages that have 57 * already passed by. Also gives the user the ability 58 * to search forward and backwards for regular expressions. 59 * This works for piped input by copying to a temporary file, 60 * and resolving backreferences from there. 61 * 62 * Note: The reason that there are so many commands to do 63 * the same types of things is to try to accommodate 64 * users of other paginators. 65 */ 66 67 #define LINSIZ 1024 68 #define QUIT '\034' 69 #define BOF (EOF - 1) /* Begining of File */ 70 #define STOP (EOF - 2) 71 #define PROMPTSIZE 256 72 73 /* 74 * Function definitions 75 */ 76 static void lineset(int); 77 static char *setprompt(); 78 static int set_state(int *, wchar_t, char *); 79 static void help(); 80 static void copy_file(FILE *, FILE *); 81 static void re_error(int); 82 static void save_input(FILE *); 83 static void save_pipe(); 84 static void newdol(FILE *); 85 static void erase_line(int); 86 static void kill_line(); 87 static void doclear(); 88 static void sopr(char *, int); 89 static void prompt(char *); 90 static void error(char *); 91 static void terminit(); 92 static void compact(); 93 static off_t getaline(FILE *); 94 static int mrdchar(); 95 static off_t find(int, off_t); 96 static int search(char *, off_t); 97 static FILE *checkf(char *); 98 static int skipf(int); 99 static int readch(); 100 static int ttyin(); 101 static int number(); 102 static int command(char *); 103 static int screen(char *); 104 static int fgetputc(); 105 static char *pg_strchr(); 106 107 108 struct line { /* how line addresses are stored */ 109 off_t l_addr; /* file offset */ 110 off_t l_no; /* line number in file */ 111 }; 112 113 typedef struct line LINE; 114 115 static LINE *zero = NULL, /* first line */ 116 *dot, /* current line */ 117 *dol, /* last line */ 118 *contig; /* where contiguous (non-aged) lines start */ 119 static long nlall; /* room for how many LINEs in memory */ 120 121 static FILE *in_file, /* current input stream */ 122 *tmp_fin, /* pipe temporary file in */ 123 *tmp_fou; /* pipe temporary file out */ 124 static char tmp_name[] = "/tmp/pgXXXXXX"; 125 126 static short sign; /* sign of command input */ 127 128 static int fnum, /* which file argument we're in */ 129 pipe_in, /* set when stdin is a pipe */ 130 out_is_tty; /* set if stdout is a tty */ 131 static pid_t my_pgid; 132 133 static void on_brk(), 134 end_it(); 135 static short brk_hit; /* interrupt handling is pending flag */ 136 137 static int window = 0; /* window size in lines */ 138 static short eof_pause = 1; /* pause w/ prompt at end of files */ 139 static short rmode = 0; /* deny shell escape in restricted mode */ 140 static short soflag = 0; /* output all messages in standout mode */ 141 static short promptlen; /* length of the current prompt */ 142 static short firstf = 1; /* set before first file has been processed */ 143 static short inwait, /* set while waiting for user input */ 144 errors; /* set if error message has been printed. */ 145 /* if so, need to erase it and prompt */ 146 147 static char **fnames; 148 static short status = 0; /* set > 0 if error detected */ 149 static short fflag = 0; /* set if the f option is used */ 150 static short nflag = 0; /* set for "no newline" input option */ 151 static short clropt = 0; /* set if the clear option is used */ 152 static int initopt = 0; /* set if the line option is used */ 153 static int srchopt = 0; /* set if the search option is used */ 154 static int initline; 155 static char initbuf[BUFSIZ]; 156 static wchar_t leave_search = L't'; 157 /* where on the page to leave a found string */ 158 static short nfiles; 159 static char *shell; 160 static char *promptstr = ":"; 161 static off_t nchars; /* return from getaline in find() */ 162 static jmp_buf restore; 163 static char Line[LINSIZ+2]; 164 165 static int catch_susp; 166 167 static void onsusp(); 168 169 struct screen_stat { 170 off_t first_line; 171 off_t last_line; 172 short is_eof; 173 }; 174 175 static struct screen_stat old_ss = { 0, 0, 0 }; 176 static struct screen_stat new_ss; 177 static struct termio otty; /* to save old terminal settings */ 178 179 static short termflg = 0; /* set once terminal is initialized */ 180 static short eoflag; /* set whenever at end of current file */ 181 static short doliseof; /* set when last line of file is known */ 182 static off_t eofl_no; /* what the last line of the file is */ 183 static void usage(void); 184 static FILE *pg_stdin; 185 186 int 187 main(int argc, char **argv) 188 { 189 char *s; 190 char *p; 191 int prnames = 0; 192 int opt; 193 int i; 194 195 (void) setlocale(LC_ALL, ""); 196 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 197 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 198 #endif 199 (void) textdomain(TEXT_DOMAIN); 200 201 /* check for non-standard "-#" option */ 202 for (i = 1; i < argc; i++) { 203 if (strcmp(argv[i], "--") == 0) 204 break; 205 206 if ((argv[i][0] == '-') && isdigit(argv[i][1])) { 207 if (strlen(&argv[i][1]) != 208 strspn(&argv[i][1], "0123456789")) { 209 (void) fprintf(stderr, gettext( 210 "pg: Badly formed number\n")); 211 usage(); 212 } 213 214 window = (int)strtol(&argv[i][1], (char **)NULL, 10); 215 216 while (i < argc) { 217 argv[i] = argv[i + 1]; 218 i++; 219 } 220 i--; 221 argc--; 222 } 223 } 224 225 /* check for non-standard + option */ 226 for (i = 1; i < argc; i++) { 227 if (strcmp(argv[i], "--") == 0) 228 break; 229 230 if (argv[i][0] == '+') { 231 if (argv[i][1] == '/') { 232 srchopt++; 233 initopt = 0; 234 for (s = &argv[i][2], p = initbuf; *s != '\0'; ) 235 if (p < initbuf + sizeof (initbuf)) 236 *p++ = *s++; 237 else { 238 (void) fprintf(stderr, gettext( 239 "pg: pattern too long\n")); 240 return (1); 241 } 242 *p = '\0'; 243 } else { 244 initopt++; 245 srchopt = 0; 246 s = &argv[i][2]; 247 for (; isdigit(*s); s++) 248 initline = initline*10 + *s -'0'; 249 if (*s != '\0') 250 usage(); 251 } 252 253 while (i < argc) { 254 argv[i] = argv[i + 1]; 255 i++; 256 } 257 i--; 258 argc--; 259 } 260 } 261 262 while ((opt = getopt(argc, argv, "cefnrsp:")) != EOF) { 263 switch (opt) { 264 case 'c': 265 clropt = 1; 266 break; 267 268 case 'e': 269 eof_pause = 0; 270 break; 271 272 case 'f': 273 fflag = 1; 274 break; 275 276 case 'n': 277 nflag = 1; 278 break; 279 280 case 'r': 281 rmode = 1; /* restricted mode */ 282 break; 283 284 case 's': 285 soflag = 1; /* standout mode */ 286 break; 287 288 case 'p': 289 promptstr = setprompt(optarg); 290 break; 291 292 default: 293 usage(); 294 } 295 } 296 297 nfiles = argc - optind; 298 fnames = &argv[optind]; 299 300 (void) signal(SIGQUIT, end_it); 301 (void) signal(SIGINT, end_it); 302 out_is_tty = isatty(1); 303 my_pgid = getpgrp(); 304 if (out_is_tty) { 305 terminit(); 306 (void) signal(SIGQUIT, on_brk); 307 (void) signal(SIGINT, on_brk); 308 if (signal(SIGTSTP, SIG_IGN) == SIG_DFL) { 309 (void) signal(SIGTSTP, onsusp); 310 catch_susp++; 311 } 312 } 313 if (window == 0) 314 window = lines - 1; 315 if (window <= 1) 316 window = 2; 317 if (initline <= 0) 318 initline = 1; 319 if (nfiles > 1) 320 prnames++; 321 322 if (nfiles == 0) { 323 fnames[0] = "-"; 324 nfiles++; 325 } 326 while (fnum < nfiles) { 327 if (strcmp(fnames[fnum], "") == 0) 328 fnames[fnum] = "-"; 329 if ((in_file = checkf(fnames[fnum])) == NULL) { 330 status = 2; 331 fnum++; 332 } else { 333 status = 0; 334 if (out_is_tty) 335 fnum += screen(fnames[fnum]); 336 else { 337 if (prnames) { 338 (void) fputs("::::::::::::::\n", 339 stdout); 340 (void) fputs(fnames[fnum], stdout); 341 (void) fputs("\n::::::::::::::\n", 342 stdout); 343 } 344 copy_file(in_file, stdout); 345 fnum++; 346 } 347 (void) fflush(stdout); 348 if (pipe_in) 349 save_pipe(); 350 else 351 if (in_file != tmp_fin) 352 (void) fclose(in_file); 353 } 354 } 355 end_it(); 356 357 /*NOTREACHED*/ 358 return (0); 359 } 360 361 static char * 362 setprompt(s) 363 char *s; 364 { 365 int i = 0; 366 int pct_d = 0; 367 static char pstr[PROMPTSIZE]; 368 369 while (i < PROMPTSIZE - 2) 370 switch (pstr[i++] = *s++) { 371 case '\0': 372 return (pstr); 373 case '%': 374 if (*s == 'd' && !pct_d) { 375 pct_d++; 376 } else if (*s != '%') 377 pstr[i++] = '%'; 378 if ((pstr[i++] = *s++) == '\0') 379 return (pstr); 380 break; 381 default: 382 break; 383 } 384 (void) fprintf(stderr, gettext("pg: prompt too long\n")); 385 exit(1); 386 /*NOTREACHED*/ 387 } 388 389 390 /* 391 * Print out the contents of the file f, one screenful at a time. 392 */ 393 394 static int 395 screen(file_name) 396 char *file_name; 397 { 398 int cmd_ret = 0; 399 off_t start; 400 short hadchance = 0; 401 402 old_ss.is_eof = 0; 403 old_ss.first_line = 0; 404 old_ss.last_line = 0; 405 new_ss = old_ss; 406 if (!firstf) 407 cmd_ret = command(file_name); 408 else { 409 firstf = 0; 410 if (initopt) { 411 initopt = 0; 412 new_ss.first_line = initline; 413 new_ss.last_line = initline + (off_t)window - 1; 414 } else if (srchopt) { 415 srchopt = 0; 416 if (!search(initbuf, (off_t)1)) 417 cmd_ret = command(file_name); 418 } else { 419 new_ss.first_line = 1; 420 new_ss.last_line = (off_t)window; 421 } 422 } 423 424 for (;;) { 425 if (cmd_ret) 426 return (cmd_ret); 427 if (hadchance && new_ss.last_line >= eofl_no) 428 return (1); 429 hadchance = 0; 430 431 if (new_ss.last_line < (off_t)window) 432 new_ss.last_line = (off_t)window; 433 if (find(0, new_ss.last_line + 1) != EOF) 434 new_ss.is_eof = 0; 435 else { 436 new_ss.is_eof = 1; 437 new_ss.last_line = eofl_no - 1; 438 new_ss.first_line = new_ss.last_line - 439 (off_t)window + 1; 440 } 441 442 if (new_ss.first_line < 1) 443 new_ss.first_line = 1; 444 if (clropt) { 445 doclear(); 446 start = new_ss.first_line; 447 } else { 448 if (new_ss.first_line == old_ss.last_line) 449 start = new_ss.first_line + 1; 450 else 451 if (new_ss.first_line > old_ss.last_line) 452 start = new_ss.first_line; 453 else 454 if (old_ss.first_line < new_ss.first_line) 455 start = old_ss.last_line + 1; 456 else 457 start = new_ss.first_line; 458 459 if (start < old_ss.first_line) 460 sopr(gettext("...skipping backward\n"), 0); 461 else 462 if (start > old_ss.last_line + 1) 463 sopr(gettext("...skipping forward\n"), 0); 464 } 465 466 for (; start <= new_ss.last_line; start++) { 467 (void) find(0, start); 468 (void) fputs(Line, stdout); 469 if (brk_hit) { 470 new_ss.last_line = find(1, 0); 471 new_ss.is_eof = 0; 472 break; 473 } 474 } 475 476 brk_hit = 0; 477 (void) fflush(stdout); 478 if (new_ss.is_eof) { 479 if (!eof_pause || eofl_no == 1) 480 return (1); 481 hadchance++; 482 error("(EOF)"); 483 } 484 old_ss = new_ss; 485 cmd_ret = command((char *)NULL); 486 } 487 } 488 489 static char cmdbuf[LINSIZ], *cmdptr; 490 #define BEEP() if (bell) { (void) putp(bell); (void) fflush(stdout); } 491 #define BLANKS(p) while (*p == ' ' || *p == '\t') p++ 492 #define CHECKEND() BLANKS(cmdptr); if (*cmdptr) { BEEP(); break; } 493 494 /* 495 * Read a command and do it. A command consists of an optional integer 496 * argument followed by the command character. Return the number of files 497 * to skip, 0 if we're still talking about the same file. 498 */ 499 500 static int 501 command(filename) 502 char *filename; 503 { 504 off_t nlines; 505 FILE *sf; 506 char *cmdend; 507 pid_t id; 508 int skip; 509 int len; 510 wchar_t wc; 511 wchar_t wc_e; 512 wchar_t wc_e1; 513 char *p; 514 515 for (;;) { 516 /* 517 * Wait for output to drain before going on. 518 * This is done so that the user will not hit 519 * break and quit before he has seen the prompt. 520 */ 521 (void) ioctl(1, TCSBRK, 1); 522 if (setjmp(restore) > 0) 523 end_it(); 524 inwait = 1; 525 brk_hit = 0; 526 if (errors) 527 errors = 0; 528 else { 529 kill_line(); 530 prompt(filename); 531 } 532 (void) fflush(stdout); 533 if (ttyin()) 534 continue; 535 cmdptr = cmdbuf; 536 nlines = number(); 537 BLANKS(cmdptr); 538 539 if ((len = mbtowc(&wc, cmdptr, MB_CUR_MAX)) <= 0) { 540 wc = *cmdptr; 541 len = 1; 542 } 543 cmdptr += len; 544 switch (wc) { 545 case 'h': 546 CHECKEND(); 547 help(); 548 break; 549 case '\014': /* ^L */ 550 case '.': /* redisplay current window */ 551 CHECKEND(); 552 new_ss.first_line = old_ss.first_line; 553 new_ss.last_line = old_ss.last_line; 554 inwait = 0; 555 return (0); 556 case 'w': /* set window size */ 557 case 'z': 558 if (sign == -1) { 559 BEEP(); 560 break; 561 } 562 CHECKEND(); 563 if (nlines == 0) 564 nlines = (off_t)window; 565 else 566 if (nlines > 1) 567 window = (int)nlines; 568 else { 569 BEEP(); 570 break; 571 } 572 new_ss.first_line = old_ss.last_line; 573 new_ss.last_line = new_ss.first_line + 574 (off_t)window - 1; 575 inwait = 0; 576 return (0); 577 case '\004': /* ^D */ 578 case 'd': 579 CHECKEND(); 580 if (sign == 0) 581 sign = 1; 582 new_ss.last_line = old_ss.last_line + 583 (off_t)sign*window/2; 584 new_ss.first_line = new_ss.last_line - 585 (off_t)window + 1; 586 inwait = 0; 587 return (0); 588 case 's': 589 /* 590 * save input in filename. 591 * Check for filename, access, etc. 592 */ 593 BLANKS(cmdptr); 594 if (!*cmdptr) { 595 BEEP(); 596 break; 597 } 598 if (setjmp(restore) > 0) { 599 BEEP(); 600 } else { 601 char outstr[PROMPTSIZE]; 602 if ((sf = fopen(cmdptr, "w")) == NULL) { 603 error("cannot open save file"); 604 break; 605 } 606 kill_line(); 607 (void) sprintf(outstr, gettext( 608 "saving file %s"), cmdptr); 609 sopr(outstr, 1); 610 (void) fflush(stdout); 611 save_input(sf); 612 error("saved"); 613 } 614 (void) fclose(sf); 615 break; 616 case 'q': 617 case 'Q': 618 CHECKEND(); 619 inwait = 0; 620 end_it(); 621 /*FALLTHROUGH*/ 622 623 case 'f': /* skip forward screenfuls */ 624 CHECKEND(); 625 if (sign == 0) 626 sign++; /* skips are always relative */ 627 if (nlines == 0) 628 nlines++; 629 nlines = nlines * (window - 1); 630 if (sign == 1) 631 new_ss.first_line = old_ss.last_line + nlines; 632 else 633 new_ss.first_line = old_ss.first_line - nlines; 634 new_ss.last_line = new_ss.first_line + 635 (off_t)window - 1; 636 inwait = 0; 637 return (0); 638 case 'l': /* get a line */ 639 CHECKEND(); 640 if (nlines == 0) { 641 nlines++; 642 if (sign == 0) 643 sign = 1; 644 } 645 switch (sign) { 646 case 1: 647 new_ss.last_line = old_ss.last_line + nlines; 648 new_ss.first_line = 649 new_ss.last_line - (off_t)window + 1; 650 break; 651 case 0: /* leave addressed line at top */ 652 new_ss.first_line = nlines; 653 new_ss.last_line = nlines + (off_t)window - 1; 654 break; 655 case -1: 656 new_ss.first_line = old_ss.first_line - nlines; 657 new_ss.last_line = 658 new_ss.first_line + (off_t)window - 1; 659 break; 660 } 661 inwait = 0; 662 return (0); 663 case '\0': /* \n or blank */ 664 if (nlines == 0) { 665 nlines++; 666 if (sign == 0) 667 sign = 1; 668 } 669 nlines = (nlines - 1) * (window - 1); 670 switch (sign) { 671 case 1: 672 new_ss.first_line = old_ss.last_line + nlines; 673 new_ss.last_line = 674 new_ss.first_line + (off_t)window - 1; 675 break; 676 case 0: 677 new_ss.first_line = nlines + 1; 678 new_ss.last_line = nlines + (off_t)window; 679 /* 680 * This if statement is to fix the obscure bug 681 * where you have a file that has less lines 682 * than a screen holds, and the user types '1', 683 * expecting to have the 1st page (re)displayed. 684 * If we didn't set the new last_line to 685 * eofl_no-1, the screen() routine 686 * would cause pg to exit. 687 */ 688 if (new_ss.first_line == 1 && 689 new_ss.last_line >= eofl_no) 690 new_ss.last_line = eofl_no - 1; 691 break; 692 case -1: 693 new_ss.last_line = old_ss.first_line - nlines; 694 new_ss.first_line = 695 new_ss.last_line - (off_t)window + 1; 696 break; 697 } 698 inwait = 0; 699 return (0); 700 case 'n': /* switch to next file in arglist */ 701 CHECKEND(); 702 if (sign == 0) 703 sign = 1; 704 if (nlines == 0) 705 nlines++; 706 if ((skip = skipf(sign *nlines)) == 0) { 707 BEEP(); 708 break; 709 } 710 inwait = 0; 711 return (skip); 712 case 'p': /* switch to previous file in arglist */ 713 CHECKEND(); 714 if (sign == 0) 715 sign = 1; 716 if (nlines == 0) 717 nlines++; 718 if ((skip = skipf(-sign * nlines)) == 0) { 719 BEEP(); 720 break; 721 } 722 inwait = 0; 723 return (skip); 724 case '$': /* go to end of file */ 725 CHECKEND(); 726 sign = 1; 727 while (find(1, (off_t)10000) != EOF) 728 /* any large number will do */; 729 new_ss.last_line = eofl_no - 1; 730 new_ss.first_line = eofl_no - (off_t)window; 731 inwait = 0; 732 return (0); 733 case '/': /* search forward for r.e. */ 734 case '?': /* " backwards */ 735 case '^': /* this ones a ? for regent100s */ 736 if (sign < 0) { 737 BEEP(); 738 break; 739 } 740 if (nlines == 0) 741 nlines++; 742 cmdptr--; 743 cmdend = cmdptr + (strlen(cmdptr) - 1); 744 wc_e1 = -1; 745 wc_e = -1; 746 for (p = cmdptr; p <= cmdend; p += len) { 747 wc_e1 = wc_e; 748 if ((len = mbtowc(&wc_e, p, MB_CUR_MAX)) <= 0) { 749 wc_e = *p; 750 len = 1; 751 } 752 } 753 754 if (cmdend > cmdptr + 1) { 755 if ((wc_e1 == *cmdptr) && 756 ((wc_e == L't') || 757 (wc_e == L'm') || (wc_e == L'b'))) { 758 leave_search = wc_e; 759 wc_e = wc_e1; 760 cmdend--; 761 } 762 } 763 if ((cmdptr < cmdend) && (wc_e == *cmdptr)) 764 *cmdend = '\0'; 765 if (*cmdptr != '/') /* signify back search by - */ 766 nlines = -nlines; 767 if (!search(++cmdptr, (off_t)nlines)) 768 break; 769 else { 770 inwait = 0; 771 return (0); 772 } 773 case '!': /* shell escape */ 774 if (rmode) { /* restricted mode */ 775 (void) fprintf(stderr, gettext( 776 "!command not allowed in restricted mode.\n")); 777 break; 778 } 779 if (!hard_copy) { /* redisplay the command */ 780 (void) fputs(cmdbuf, stdout); 781 (void) fputs("\n", stdout); 782 } 783 if ((id = fork()) < 0) { 784 error("cannot fork, try again later"); 785 break; 786 } 787 if (id == (pid_t)0) { 788 /* 789 * if stdin is a pipe, need to close it so 790 * that the terminal is really stdin for 791 * the command 792 */ 793 (void) fclose(stdin); 794 (void) fclose(pg_stdin); 795 (void) dup(fileno(stdout)); 796 (void) execl(shell, shell, "-c", cmdptr, 0); 797 (void) perror("exec"); 798 exit(1); 799 } 800 (void) signal(SIGINT, SIG_IGN); 801 (void) signal(SIGQUIT, SIG_IGN); 802 if (catch_susp) 803 (void) signal(SIGTSTP, SIG_DFL); 804 while (wait((int *)0) != id); 805 { 806 if (errno == ECHILD) 807 break; 808 else 809 errno = 0; 810 } 811 (void) fputs("!\n", stdout); 812 (void) fflush(stdout); 813 (void) signal(SIGINT, on_brk); 814 (void) signal(SIGQUIT, on_brk); 815 if (catch_susp) 816 (void) signal(SIGTSTP, onsusp); 817 break; 818 default: 819 BEEP(); 820 break; 821 } 822 } 823 } 824 825 static int 826 number() 827 { 828 int i; 829 char *p; 830 831 i = 0; 832 sign = 0; 833 p = cmdptr; 834 BLANKS(p); 835 if (*p == '+') { 836 p++; 837 sign = 1; 838 } 839 else 840 if (*p == '-') { 841 p++; 842 sign = -1; 843 } 844 while (isdigit(*p)) 845 i = i * 10 + *p++ - '0'; 846 cmdptr = p; 847 return (i); 848 } 849 850 static int 851 ttyin() 852 { 853 char *sptr, *p; 854 wchar_t ch; 855 int slash = 0; 856 int state = 0; 857 int width, length; 858 char multic[MB_LEN_MAX]; 859 int len; 860 861 (void) fixterm(); 862 /* initialize state processing */ 863 (void) set_state(&state, ' ', (char *)0); 864 sptr = cmdbuf; 865 while (state != 10) { 866 if ((ch = readch()) < 0 || !iswascii(ch) && !iswprint(ch)) { 867 BEEP(); 868 continue; 869 } 870 871 if ((length = wctomb(multic, ch)) < 0) 872 length = 0; 873 multic[length] = 0; 874 875 if (ch == '\n' && !slash) 876 break; 877 if (ch == erasechar() && !slash) { 878 if (sptr > cmdbuf) { 879 char *oldp = cmdbuf; 880 wchar_t wchar; 881 p = cmdbuf; 882 while (p < sptr) { 883 oldp = p; 884 len = mbtowc(&wchar, p, MB_CUR_MAX); 885 if (len <= 0) { 886 wchar = (unsigned char)*p; 887 len = 1; 888 } 889 p += len; 890 } 891 if ((width = wcwidth(wchar)) <= 0) 892 /* ascii control character */ 893 width = 2; 894 promptlen -= width; 895 while (width--) 896 (void) fputs("\b \b", stdout); 897 sptr = oldp; 898 } 899 (void) set_state(&state, ch, sptr); 900 (void) fflush(stdout); 901 continue; 902 } 903 else 904 if (ch == killchar() && !slash) { 905 if (hard_copy) 906 (void) putwchar(ch); 907 (void) resetterm(); 908 return (1); 909 } 910 if (ch < ' ') 911 width = 2; 912 else 913 if ((width = wcwidth(ch)) <= 0) 914 width = 0; 915 if (slash) { 916 slash = 0; 917 (void) fputs("\b \b", stdout); 918 sptr--; 919 promptlen--; 920 } else /* is there room to keep this character? */ 921 if (sptr >= cmdbuf + sizeof (cmdbuf) || 922 promptlen + width >= columns) { 923 BEEP(); 924 continue; 925 } 926 else 927 if (ch == '\\') 928 slash++; 929 if (set_state(&state, ch, sptr) == 0) { 930 BEEP(); 931 continue; 932 } 933 (void) strncpy(sptr, multic, (size_t)length); 934 sptr += length; 935 if (ch < ' ') { 936 ch += 0100; 937 multic[0] = '^'; 938 multic[1] = ch; 939 length = 2; 940 } 941 p = multic; 942 while (length--) 943 (void) putchar(*p++); 944 promptlen += width; 945 (void) fflush(stdout); 946 } 947 948 *sptr = '\0'; 949 kill_line(); 950 (void) fflush(stdout); 951 (void) resetterm(); 952 return (0); 953 } 954 955 static int 956 set_state(pstate, c, pc) 957 int *pstate; 958 wchar_t c; 959 char *pc; 960 { 961 static char *psign; 962 static char *pnumber; 963 static char *pcommand; 964 static int slash; 965 966 if (*pstate == 0) { 967 psign = (char *)NULL; 968 pnumber = (char *)NULL; 969 pcommand = (char *)NULL; 970 *pstate = 1; 971 slash = 0; 972 return (1); 973 } 974 if (c == '\\' && !slash) { 975 slash++; 976 return (1); 977 } 978 if (c == erasechar() && !slash) 979 switch (*pstate) { 980 case 4: 981 if (pc > pcommand) 982 return (1); 983 pcommand = (char *)NULL; 984 /*FALLTHROUGH*/ 985 986 case 3: 987 if (pnumber && pc > pnumber) { 988 *pstate = 3; 989 return (1); 990 } 991 pnumber = (char *)NULL; 992 /*FALLTHROUGH*/ 993 994 case 2: 995 if (psign && pc > psign) { 996 *pstate = 2; 997 return (1); 998 } 999 psign = (char *)NULL; 1000 /*FALLTHROUGH*/ 1001 1002 case 1: 1003 *pstate = 1; 1004 return (1); 1005 } 1006 1007 slash = 0; 1008 switch (*pstate) { 1009 case 1: /* before recieving anything interesting */ 1010 if (c == '\t' || (!nflag && c == ' ')) 1011 return (1); 1012 if (c == '+' || c == '-') { 1013 psign = pc; 1014 *pstate = 2; 1015 return (1); 1016 } 1017 /*FALLTHROUGH*/ 1018 1019 case 2: /* recieved sign, waiting for digit */ 1020 if (iswascii(c) && isdigit(c)) { 1021 pnumber = pc; 1022 *pstate = 3; 1023 return (1); 1024 } 1025 /*FALLTHROUGH*/ 1026 1027 case 3: /* recieved digit, waiting for the rest of the number */ 1028 if (iswascii(c) && isdigit(c)) 1029 return (1); 1030 if (iswascii(c) && pg_strchr("h\014.wz\004dqQfl np$", c)) { 1031 pcommand = pc; 1032 if (nflag) 1033 *pstate = 10; 1034 else 1035 *pstate = 4; 1036 return (1); 1037 } 1038 if (iswascii(c) && pg_strchr("s/^?!", c)) { 1039 pcommand = pc; 1040 *pstate = 4; 1041 return (1); 1042 } 1043 return (0); 1044 case 4: 1045 return (1); 1046 } 1047 return (0); 1048 } 1049 1050 static int 1051 readch() 1052 { 1053 return (fgetwc(pg_stdin)); 1054 } 1055 1056 static void 1057 help() 1058 { 1059 if (clropt) 1060 doclear(); 1061 1062 (void) fputs(gettext( 1063 "-------------------------------------------------------\n" 1064 " h help\n" 1065 " q or Q quit\n" 1066 " <blank> or <newline> next page\n" 1067 " l next line\n" 1068 " d or <^D> display half a page more\n" 1069 " . or <^L> redisplay current page\n" 1070 " f skip the next page forward\n" 1071 " n next file\n" 1072 " p previous file\n" 1073 " $ last page\n" 1074 " w or z set window size and display next page\n" 1075 " s savefile save current file in savefile\n" 1076 " /pattern/ search forward for pattern\n" 1077 " ?pattern? or\n" 1078 " ^pattern^ search backward for pattern\n" 1079 " !command execute command\n" 1080 "\n" 1081 "Most commands can be preceeded by a number, as in:\n" 1082 "+1<newline> (next page); -1<newline> (previous page); 1<newline> (page 1).\n" 1083 "\n" 1084 "See the manual page for more detail.\n" 1085 "-------------------------------------------------------\n"), 1086 stdout); 1087 } 1088 1089 /* 1090 * Skip nskip files in the file list (from the command line). Nskip may be 1091 * negative. 1092 */ 1093 1094 static int 1095 skipf(nskip) 1096 int nskip; 1097 { 1098 if (fnum + nskip < 0) { 1099 nskip = -fnum; 1100 if (nskip == 0) 1101 error("No previous file"); 1102 } 1103 else 1104 if (fnum + nskip > nfiles - 1) { 1105 nskip = (nfiles - 1) - fnum; 1106 if (nskip == 0) 1107 error("No next file"); 1108 } 1109 return (nskip); 1110 } 1111 1112 /* 1113 * Check whether the file named by fs is a file which the user may 1114 * access. If it is, return the opened file. Otherwise return NULL. 1115 */ 1116 1117 static FILE * 1118 checkf(fs) 1119 char *fs; 1120 { 1121 struct stat stbuf; 1122 FILE *f; 1123 int fd; 1124 int f_was_opened; 1125 1126 pipe_in = 0; 1127 if (strcmp(fs, "-") == 0) { 1128 if (tmp_fin == NULL) 1129 f = stdin; 1130 else { 1131 rewind(tmp_fin); 1132 f = tmp_fin; 1133 } 1134 f_was_opened = 0; 1135 } else { 1136 if ((f = fopen(fs, "r")) == (FILE *)NULL) { 1137 (void) fflush(stdout); 1138 perror(fs); 1139 return ((FILE *)NULL); 1140 } 1141 f_was_opened = 1; 1142 } 1143 if (fstat(fileno(f), &stbuf) == -1) { 1144 if (f_was_opened) 1145 (void) fclose(f); 1146 (void) fflush(stdout); 1147 perror(fs); 1148 return ((FILE *)NULL); 1149 } 1150 if ((stbuf.st_mode & S_IFMT) == S_IFDIR) { 1151 if (f_was_opened) 1152 (void) fclose(f); 1153 (void) fprintf(stderr, "pg: "); 1154 (void) fprintf(stderr, gettext("%s is a directory\n"), fs); 1155 return ((FILE *)NULL); 1156 } 1157 if ((stbuf.st_mode & S_IFMT) == S_IFREG) { 1158 if (f == stdin) /* It may have been read from */ 1159 rewind(f); /* already, and not reopened */ 1160 } else { 1161 if (f != stdin) { 1162 if (f_was_opened) 1163 (void) fclose(f); 1164 (void) fprintf(stderr, "pg: "); 1165 (void) fprintf(stderr, gettext( 1166 "special files only handled as standard input\n")); 1167 return ((FILE *)NULL); 1168 } else { 1169 if ((fd = mkstemp(tmp_name)) < 0) { 1170 (void) perror(tmp_name); 1171 return ((FILE *)NULL); 1172 } 1173 (void) close(fd); 1174 if ((tmp_fou = fopen(tmp_name, "w")) == NULL) { 1175 (void) perror(tmp_name); 1176 return ((FILE *)NULL); 1177 } 1178 if ((tmp_fin = fopen(tmp_name, "r")) == NULL) { 1179 (void) perror(tmp_name); 1180 return ((FILE *)NULL); 1181 } 1182 pipe_in = 1; 1183 } 1184 } 1185 lineset(BOF); 1186 return (f); 1187 } 1188 1189 static void 1190 copy_file(f, out) 1191 FILE *f, *out; 1192 { 1193 int c; 1194 1195 while ((c = getc(f)) != EOF) 1196 (void) putc(c, out); 1197 1198 } 1199 1200 static void 1201 re_error(i) 1202 int i; 1203 { 1204 int j; 1205 static struct messages { 1206 char *message; 1207 int number; 1208 } re_errmsg[] = { 1209 "Pattern not found", 1, 1210 "Range endpoint too large", 11, 1211 "Bad number", 16, 1212 "`\\digit' out of range", 25, 1213 "No remembered search string", 41, 1214 "\\( \\) imbalance", 42, 1215 "Too many \\(", 43, 1216 "More than two numbers given in \\{ \\}", 44, 1217 "} expected after \\", 45, 1218 "First number exceeds second in \\{ \\}", 46, 1219 "[] imbalance", 49, 1220 "Regular expression overflow", 50, 1221 "Illegal byte sequence", 67, 1222 "Bad regular expression", 0 1223 }; 1224 1225 for (j = 0; re_errmsg[j].number != 0; j++) 1226 if (re_errmsg[j].number == i) 1227 break; 1228 error(re_errmsg[j].message); 1229 longjmp(restore, 1); /* restore to search() */ 1230 } 1231 1232 /* 1233 * Search for nth ocurrence of regular expression contained in buf in the file 1234 * negative n implies backward search 1235 * n 'guaranteed' non-zero 1236 */ 1237 1238 1239 static int 1240 search(buf, n) 1241 char buf[]; 1242 off_t n; 1243 { 1244 int direction; 1245 static char *expbuf; 1246 char *nexpbuf; 1247 int END_COND; 1248 1249 if (setjmp(restore) <= 0) { 1250 nexpbuf = compile(buf, (char *)0, (char *)0); 1251 if (regerrno) { 1252 if (regerrno != 41 || expbuf == NULL) 1253 re_error(regerrno); 1254 } else { 1255 if (expbuf) 1256 free(expbuf); 1257 expbuf = nexpbuf; 1258 } 1259 1260 if (n < 0) { /* search back */ 1261 direction = -1; 1262 (void) find(0, old_ss.first_line); 1263 END_COND = BOF; 1264 } else { 1265 direction = 1; 1266 (void) find(0, old_ss.last_line); 1267 END_COND = EOF; 1268 } 1269 1270 while (find(1, direction) != END_COND) { 1271 if (brk_hit) 1272 break; 1273 if (step(Line, expbuf)) 1274 if ((n -= direction) == 0) { 1275 switch (leave_search) { 1276 case 't': 1277 new_ss.first_line = 1278 find(1, (off_t)0); 1279 new_ss.last_line = 1280 new_ss.first_line + 1281 (off_t)window 1282 - 1; 1283 break; 1284 case 'b': 1285 new_ss.last_line = 1286 find(1, (off_t)0); 1287 new_ss.first_line = 1288 new_ss.last_line - 1289 (off_t)window 1290 + 1; 1291 break; 1292 case 'm': 1293 new_ss.first_line = 1294 find(1, (off_t)0) - 1295 ((off_t)window - 1)/2; 1296 new_ss.last_line = 1297 new_ss.first_line + 1298 (off_t)window 1299 - 1; 1300 break; 1301 } 1302 return (1); 1303 } 1304 } 1305 re_error(1); /* Pattern not found */ 1306 } 1307 BEEP(); 1308 return (0); 1309 } 1310 1311 /* 1312 * find -- find line in file f, subject to certain constraints. 1313 * 1314 * This is the reason for all the funny stuff with sign and nlines. 1315 * We need to be able to differentiate between relative and abosolute 1316 * address specifications. 1317 * 1318 * So...there are basically three cases that this routine 1319 * handles. Either line is zero, which means there is to be 1320 * no motion (because line numbers start at one), or 1321 * how and line specify a number, or line itself is negative, 1322 * which is the same as having how == -1 and line == abs(line). 1323 * 1324 * Then, figure where exactly it is that we are going (an absolute 1325 * line number). Find out if it is within what we have read, 1326 * if so, go there without further ado. Otherwise, do some 1327 * magic to get there, saving all the intervening lines, 1328 * in case the user wants to see them some time later. 1329 * 1330 * In any case, return the line number that we end up at. 1331 * (This is used by search() and screen()). If we go past EOF, 1332 * return EOF. 1333 * This EOF will go away eventually, as pg is expanded to 1334 * handle multiple files as one huge one. Then EOF will 1335 * mean we have run off the file list. 1336 * If the requested line number is too far back, return BOF. 1337 */ 1338 1339 static off_t 1340 find(how, line) /* find the line and seek there */ 1341 int how; 1342 off_t line; 1343 { 1344 /* no compacted memory yet */ 1345 FILE *f = in_file; 1346 off_t where; 1347 1348 if (how == 0) 1349 where = line; 1350 else 1351 if (dot == zero - 1) 1352 where = how * line; 1353 else 1354 where = how * line + dot->l_no; 1355 1356 /* now, where is either at, before, or after dol */ 1357 /* most likely case is after, so do it first */ 1358 1359 eoflag = 0; 1360 if (where >= dol->l_no) { 1361 if (doliseof) { 1362 dot = dol; 1363 eoflag++; 1364 return (EOF); 1365 } 1366 if (pipe_in) 1367 in_file = f = stdin; 1368 else 1369 (void) fseeko(f, (off_t)dol->l_addr, SEEK_SET); 1370 dot = dol - 1; 1371 while ((nchars = getaline(f)) != EOF) { 1372 dot++; 1373 newdol(f); 1374 if (where == dot->l_no || brk_hit) 1375 break; 1376 } 1377 if (nchars != EOF) 1378 return (dot->l_no); 1379 else { /* EOF */ 1380 dot = dol; 1381 eoflag++; 1382 doliseof++; 1383 eofl_no = dol->l_no; 1384 return (EOF); 1385 } 1386 } else { /* where < dol->l_no */ 1387 if (pipe_in) { 1388 (void) fflush(tmp_fou); 1389 in_file = f = tmp_fin; 1390 } 1391 if (where < zero->l_no) { 1392 (void) fseeko(f, (off_t)zero->l_addr, SEEK_SET); 1393 dot = zero - 1; 1394 return (BOF); 1395 } else { 1396 dot = zero + where - 1; 1397 (void) fseeko(f, (off_t)dot->l_addr, SEEK_SET); 1398 nchars = getaline(f); 1399 return (dot->l_no); 1400 } 1401 } 1402 } 1403 1404 static FILE *fileptr; 1405 static int (*rdchar)(); 1406 1407 static int 1408 mrdchar() 1409 { 1410 return (rdchar(fileptr)); 1411 } 1412 1413 /* 1414 * Get a logical line 1415 */ 1416 1417 static off_t 1418 getaline(f) 1419 FILE *f; 1420 { 1421 char *p; 1422 int column; 1423 static char multic[MB_LEN_MAX]; 1424 static int savlength; 1425 wchar_t c; 1426 int length, width; 1427 1428 if (pipe_in && f == stdin) 1429 rdchar = fgetputc; 1430 else 1431 rdchar = (int (*)())fgetwc; 1432 1433 fileptr = f; 1434 /* copy overlap from previous call to getaline */ 1435 if (savlength) 1436 (void) strncpy(Line, multic, (size_t)savlength); 1437 for (column = 0, p = Line + savlength; ; ) { 1438 if ((c = mrdchar()) <= 0) { 1439 clearerr(f); 1440 if (p > Line) { /* last line doesn't have '\n', */ 1441 *p++ = '\n'; 1442 *p = '\0'; /* print it any way */ 1443 return (column); 1444 } 1445 return (EOF); 1446 } 1447 length = wctomb(multic, c); 1448 if (length < 0) { 1449 length = -length; 1450 c = 0; 1451 } 1452 if ((width = wcwidth(c)) < 0) 1453 width = 0; 1454 if (column + width > columns && !fflag) 1455 break; 1456 1457 if (p + length > &Line[LINSIZ - 2] && c != '\n') 1458 break; 1459 (void) strncpy(p, multic, (size_t)length); 1460 p += length; 1461 column += width; 1462 /* don't have any overlap here */ 1463 length = 0; 1464 switch (c) { 1465 case '\t': /* just a guess */ 1466 column = 1 + (column | 7); 1467 break; 1468 case '\b': 1469 if (column > 0) 1470 column--; 1471 break; 1472 case '\r': 1473 column = 0; 1474 break; 1475 } 1476 if (c == '\n') 1477 break; 1478 if (column >= columns && !fflag) 1479 break; 1480 } 1481 if (c != '\n') { /* We're stopping in the middle of the line */ 1482 if (column != columns || !auto_right_margin) 1483 *p++ = '\n'; /* for the display */ 1484 /* save overlap for next call to getaline */ 1485 savlength = length; 1486 if (savlength == 0) { 1487 /* 1488 * check if following byte is newline and get 1489 * it if it is 1490 */ 1491 c = fgetwc(f); 1492 if (c == '\n') { 1493 /* gobble and copy (if necessary) newline */ 1494 (void) ungetwc(c, f); 1495 (void) (*rdchar)(f); 1496 } else if (c == EOF) 1497 clearerr(f); 1498 else 1499 (void) ungetwc(c, f); 1500 } 1501 } else 1502 savlength = 0; 1503 *p = 0; 1504 return (column); 1505 } 1506 1507 static void 1508 save_input(f) 1509 FILE *f; 1510 { 1511 if (pipe_in) { 1512 save_pipe(); 1513 in_file = tmp_fin; 1514 pipe_in = 0; 1515 } 1516 (void) fseeko(in_file, (off_t)0, SEEK_SET); 1517 copy_file(in_file, f); 1518 } 1519 1520 static void 1521 save_pipe() 1522 { 1523 if (!doliseof) 1524 while (fgetputc(stdin) != EOF) 1525 if (brk_hit) { 1526 brk_hit = 0; 1527 error("Piped input only partially saved"); 1528 break; 1529 } 1530 (void) fclose(tmp_fou); 1531 } 1532 1533 static int 1534 fgetputc(f) /* copy anything read from a pipe to tmp_fou */ 1535 FILE *f; 1536 { 1537 int c; 1538 1539 if ((c = fgetwc(f)) != EOF) 1540 (void) fputwc(c, tmp_fou); 1541 return (c); 1542 } 1543 1544 static void 1545 lineset(how) /* initialize line memory */ 1546 int how; 1547 { 1548 if (zero == NULL) { 1549 nlall = 128; 1550 zero = (LINE *) malloc(nlall * sizeof (LINE)); 1551 } 1552 dol = contig = zero; 1553 zero->l_no = 1; 1554 zero->l_addr = 0l; 1555 if (how == BOF) { 1556 dot = zero - 1; 1557 eoflag = 0; 1558 doliseof = 0; 1559 eofl_no = -1; 1560 } else { 1561 dot = dol; 1562 eoflag = 1; 1563 doliseof = 1; 1564 eofl_no = 1; 1565 } 1566 } 1567 1568 static void 1569 newdol(f) /* add address of new 'dol' */ 1570 /* assumes that f is currently at beginning of said line */ 1571 /* updates dol */ 1572 FILE *f; 1573 { 1574 int diff; 1575 1576 if ((dol - zero) + 1 >= nlall) { 1577 LINE *ozero = zero; 1578 1579 nlall += 512; 1580 if ((zero = (LINE *)realloc((char *)zero, 1581 (unsigned)(nlall * sizeof (LINE)))) == NULL) { 1582 zero = ozero; 1583 compact(); 1584 } 1585 diff = (int)((int)zero - (int)ozero); 1586 dot = (LINE *)((int)dot + diff); 1587 dol = (LINE *)((int)dol + diff); 1588 contig = (LINE *)((int)contig + diff); 1589 } 1590 dol++; 1591 if (!pipe_in) 1592 dol->l_addr = (off_t)ftello(f); 1593 else { 1594 (void) fflush(tmp_fou); 1595 dol->l_addr = (off_t)ftello(tmp_fou); 1596 } 1597 dol->l_no = (dol-1)->l_no + 1; 1598 } 1599 1600 static void 1601 compact() 1602 { 1603 (void) perror("realloc"); 1604 end_it(); 1605 1606 } 1607 1608 static void 1609 terminit() /* set up terminal dependencies from termlib */ 1610 { 1611 int err_ret; 1612 struct termio ntty; 1613 1614 for (;;) { 1615 pid_t my_tgid; 1616 my_tgid = tcgetpgrp(1); 1617 if (my_tgid == -1 || my_tgid == my_pgid) 1618 break; 1619 (void) kill(-my_pgid, SIGTTOU); 1620 } 1621 1622 if ((freopen("/dev/tty", "r+", stdout)) == NULL) { 1623 (void) perror("open"); 1624 exit(1); 1625 } 1626 (void) ioctl(fileno(stdout), TCGETA, &otty); 1627 termflg = 1; 1628 1629 (void) setupterm(0, fileno(stdout), &err_ret); 1630 (void) ioctl(fileno(stdout), TCGETA, &ntty); 1631 ntty.c_lflag &= ~(ECHONL | ECHO | ICANON); 1632 ntty.c_cc[VMIN] = 1; 1633 ntty.c_cc[VTIME] = 1; 1634 (void) ioctl(fileno(stdout), TCSETAW, &ntty); 1635 pg_stdin = fdopen(dup(fileno(stdout)), "r"); 1636 (void) saveterm(); 1637 (void) resetterm(); 1638 if (lines <= 0 || hard_copy) { 1639 hard_copy = 1; 1640 lines = 24; 1641 } 1642 if (columns <= 0) 1643 columns = 80; 1644 if (clropt && !clear_screen) 1645 clropt = 0; 1646 if ((shell = getenv("SHELL")) == (char *)NULL) 1647 shell = "/usr/bin/sh"; 1648 } 1649 1650 static void 1651 error(mess) 1652 char *mess; 1653 { 1654 kill_line(); 1655 sopr(gettext(mess), 1); 1656 prompt((char *)NULL); 1657 errors++; 1658 } 1659 1660 static void 1661 prompt(filename) 1662 char *filename; 1663 { 1664 char outstr[PROMPTSIZE+6]; 1665 int pagenum; 1666 if (filename != NULL) { 1667 /* 1668 * TRANSLATION_NOTE 1669 * %s is a filename. 1670 */ 1671 (void) sprintf(outstr, gettext("(Next file: %s)"), filename); 1672 } else { 1673 if ((pagenum = (int)((new_ss.last_line-2)/(window-1)+1)) 1674 > 999999) 1675 pagenum = 999999; 1676 (void) sprintf(outstr, promptstr, pagenum); 1677 } 1678 sopr(outstr, 1); 1679 (void) fflush(stdout); 1680 } 1681 1682 /* 1683 * sopr puts out the message (please no \n's) surrounded by standout 1684 * begins and ends 1685 */ 1686 1687 static void 1688 sopr(m, count) 1689 char *m; 1690 int count; 1691 { 1692 wchar_t wc; 1693 int len, n; 1694 char *p; 1695 1696 if (count) { 1697 p = m; 1698 for (; *p; p += len) { 1699 if ((len = mbtowc(&wc, p, MB_CUR_MAX)) <= 0) { 1700 len = 1; 1701 continue; 1702 } 1703 if ((n = wcwidth(wc)) > 0) 1704 promptlen += n; 1705 } 1706 } 1707 if (soflag && enter_standout_mode && exit_standout_mode) { 1708 (void) putp(enter_standout_mode); 1709 (void) fputs(m, stdout); 1710 (void) putp(exit_standout_mode); 1711 } 1712 else 1713 (void) fputs(m, stdout); 1714 } 1715 1716 static void 1717 doclear() 1718 { 1719 if (clear_screen) 1720 (void) putp(clear_screen); 1721 (void) putchar('\r'); /* this resets the terminal drivers character */ 1722 /* count in case it is trying to expand tabs */ 1723 1724 } 1725 1726 static void 1727 kill_line() 1728 { 1729 erase_line(0); 1730 if (!clr_eol) (void) putchar('\r'); 1731 1732 } 1733 1734 /* erase from after col to end of prompt */ 1735 static void 1736 erase_line(col) 1737 int col; 1738 { 1739 1740 if (promptlen == 0) 1741 return; 1742 if (hard_copy) 1743 (void) putchar('\n'); 1744 else { 1745 if (col == 0) 1746 (void) putchar('\r'); 1747 if (clr_eol) { 1748 (void) putp(clr_eol); 1749 /* for the terminal driver again */ 1750 (void) putchar('\r'); 1751 } 1752 else 1753 for (col = promptlen - col; col > 0; col--) 1754 (void) putchar(' '); 1755 } 1756 promptlen = 0; 1757 } 1758 1759 /* 1760 * Come here if a quit or interrupt signal is received 1761 */ 1762 1763 static void 1764 on_brk(sno) 1765 int sno; /* signal number generated */ 1766 { 1767 (void) signal(sno, on_brk); 1768 if (!inwait) { 1769 BEEP(); 1770 brk_hit = 1; 1771 } else { 1772 brk_hit = 0; 1773 longjmp(restore, 1); 1774 } 1775 } 1776 1777 /* 1778 * Clean up terminal state and exit. 1779 */ 1780 1781 void 1782 end_it() 1783 { 1784 1785 if (out_is_tty) { 1786 kill_line(); 1787 (void) resetterm(); 1788 if (termflg) 1789 (void) ioctl(fileno(stdout), TCSETAW, &otty); 1790 } 1791 if (tmp_fin) 1792 (void) fclose(tmp_fin); 1793 if (tmp_fou) 1794 (void) fclose(tmp_fou); 1795 if (tmp_fou || tmp_fin) 1796 (void) unlink(tmp_name); 1797 exit(status); 1798 } 1799 1800 void 1801 onsusp() 1802 { 1803 int ttou_is_dfl; 1804 1805 /* ignore SIGTTOU so following resetterm and flush works */ 1806 ttou_is_dfl = (signal(SIGTTOU, SIG_IGN) == SIG_DFL); 1807 (void) resetterm(); 1808 (void) fflush(stdout); 1809 if (ttou_is_dfl) 1810 (void) signal(SIGTTOU, SIG_DFL); 1811 1812 /* send SIGTSTP to stop this process group */ 1813 (void) signal(SIGTSTP, SIG_DFL); 1814 (void) kill(-my_pgid, SIGTSTP); 1815 1816 /* continued - reset the terminal */ 1817 #ifdef __STDC__ 1818 (void) signal(SIGTSTP, (void (*)(int))onsusp); 1819 #else 1820 (void) signal(SIGTSTP, (void (*))onsusp); 1821 #endif 1822 (void) resetterm(); 1823 if (inwait) 1824 longjmp(restore, -1); 1825 1826 } 1827 1828 static char * 1829 pg_strchr(str, c) 1830 char *str; 1831 wchar_t c; 1832 { 1833 while (*str) { 1834 if (c == *str) 1835 return (str); 1836 str++; 1837 } 1838 return (0); 1839 } 1840 1841 void 1842 usage() 1843 { 1844 (void) fprintf(stderr, gettext( 1845 "Usage: pg [-number] [-p string] [-cefnrs] [+line] [+/pattern/] files\n")); 1846 exit(1); 1847 } 1848