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