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