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