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