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