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