1 static char *SCCS_ID = "@(#)more.c 4.1 (Berkeley) 10/17/80"; 2 /* 3 ** more.c - General purpose tty output filter and file perusal program 4 ** 5 ** by Eric Shienbrood, UC Berkeley 6 */ 7 8 #include <whoami.h> 9 #ifdef V6 10 #include <retrofit.h> 11 #endif 12 #include <stdio.h> 13 #include <ctype.h> 14 #include <signal.h> 15 #include <errno.h> 16 #include <sgtty.h> 17 #include <setjmp.h> 18 #include <sys/types.h> 19 #include <sys/dir.h> 20 #include <sys/stat.h> 21 #include <local/uparm.h> 22 23 /* Help file will eventually go in libpath(more.help) on all systems */ 24 25 #ifdef INGRES 26 #define VI "/usr/bin/vi" 27 #define HELPFILE "/mntp/doucette/more/more.help" 28 #endif 29 30 #ifndef INGRES 31 #ifndef HELPFILE 32 #define HELPFILE libpath(more.help) 33 #endif 34 #define VI binpath(vi) 35 #endif 36 37 #define Fopen(s,m) (Currline = 0,file_pos=0,fopen(s,m)) 38 #define Ftell(f) file_pos 39 #define Fseek(f,off) (file_pos=off,fseek(f,off,0)) 40 #define Getc(f) (++file_pos, getc(f)) 41 #define Ungetc(c,f) (--file_pos, ungetc(c,f)) 42 43 #ifdef V6 44 #define MBIT RAW 45 #define CBREAK ~RAW 46 #else 47 #define MBIT CBREAK 48 #define stty(fd,argp) ioctl(fd,TIOCSETN,argp) 49 #endif 50 51 #define TBUFSIZ 1024 52 #define LINSIZ 256 53 #define ctrl(letter) ('letter' & 077) 54 #define RUBOUT '\177' 55 #define ESC '\033' 56 #define QUIT '\034' 57 58 struct sgttyb otty; 59 long file_pos, file_size; 60 int fnum, no_intty, no_tty, slow_tty; 61 int dum_opt, dlines, onquit(), end_it(); 62 #ifdef SIGTSTP 63 int onsusp(); 64 #endif 65 int nscroll = 11; /* Number of lines scrolled by 'd' */ 66 int fold_opt = 1; /* Fold long lines */ 67 int stop_opt = 1; /* Stop after form feeds */ 68 int promptlen; 69 int Currline; /* Line we are currently at */ 70 int startup = 1; 71 int firstf = 1; 72 int notell = 1; 73 int bad_so; /* True if overwriting does not turn off standout */ 74 int inwait, Pause, errors; 75 int within; /* true if we are within a file, 76 false if we are between files */ 77 int hard, dumb, noscroll, hardtabs; 78 int catch_susp; /* We should catch the SIGTSTP signal */ 79 char **fnames; /* The list of file names */ 80 int nfiles; /* Number of files left to process */ 81 char *shell; /* The name of the shell to use */ 82 int shellp; /* A previous shell command exists */ 83 char ch; 84 jmp_buf restore; 85 char obuf[BUFSIZ]; /* stdout buffer */ 86 char Line[LINSIZ]; /* Line buffer */ 87 int Lpp = 24; /* lines per page */ 88 char *Clear; /* clear screen */ 89 char *eraseln; /* erase line */ 90 char *Senter, *Sexit;/* enter and exit standout mode */ 91 char *tgetstr(); 92 int Mcol = 80; /* number of columns */ 93 int Wrap = 1; /* set if automargins */ 94 long fseek(); 95 struct { 96 long chrctr, line; 97 } context, screen_start; 98 extern char PC; /* pad character */ 99 extern short ospeed; 100 101 102 main(argc, argv) 103 int argc; 104 char *argv[]; 105 { 106 register FILE *f; 107 register char *s; 108 register char *p; 109 register char ch; 110 register int left; 111 int prnames = 0; 112 int initopt = 0; 113 int srchopt = 0; 114 int clearit = 0; 115 int initline; 116 char initbuf[80]; 117 FILE *checkf(); 118 119 nfiles = argc; 120 fnames = argv; 121 initterm (); 122 while (--nfiles > 0) { 123 if ((ch = (*++fnames)[0]) == '-') { 124 for (s = fnames[0] + 1, dlines = 0; *s != '\0'; s++) 125 if (isdigit(*s)) 126 dlines = dlines*10 + *s - '0'; 127 else if (*s == 'd') 128 dum_opt = 1; 129 else if (*s == 'l') 130 stop_opt = 0; 131 else if (*s == 'f') 132 fold_opt = 0; 133 } 134 else if (ch == '+') { 135 s = *fnames; 136 if (*++s == '/') { 137 srchopt++; 138 for (++s, p = initbuf; p < initbuf + 79 && *s != '\0';) 139 *p++ = *s++; 140 *p = '\0'; 141 } 142 else { 143 initopt++; 144 for (initline = 0; *s != '\0'; s++) 145 if (isdigit (*s)) 146 initline = initline*10 + *s -'0'; 147 --initline; 148 } 149 } 150 else break; 151 } 152 if (dlines == 0) 153 dlines = Lpp - (noscroll ? 1 : 2); 154 left = dlines; 155 if (nfiles > 1) 156 prnames++; 157 if (!no_intty && nfiles == 0) { 158 fputs("Usage: ",stderr); 159 fputs(argv[0],stderr); 160 fputs(" [-dfln] [+linenum | +/pattern] name1 name2 ...\n",stderr); 161 exit(1); 162 } 163 else 164 f = stdin; 165 if (!no_tty) { 166 signal(SIGQUIT, onquit); 167 signal(SIGINT, end_it); 168 #ifdef SIGTSTP 169 if (signal (SIGTSTP, SIG_IGN) == SIG_DFL) { 170 signal(SIGTSTP, onsusp); 171 catch_susp++; 172 } 173 #endif 174 stty (2, &otty); 175 } 176 if (no_intty) { 177 if (no_tty) 178 copy_file (stdin); 179 else { 180 if ((ch = Getc (f)) == '\f') 181 doclear (); 182 else { 183 Ungetc (ch, f); 184 if (noscroll) 185 doclear (); 186 } 187 if (srchopt) 188 search (initbuf, stdin, 1); 189 else if (initopt) 190 skiplns (initline, stdin); 191 screen (stdin, left); 192 } 193 no_intty = 0; 194 prnames++; 195 firstf = 0; 196 } 197 198 while (fnum < nfiles) { 199 if ((f = checkf (fnames[fnum], &clearit)) != NULL) { 200 context.line = context.chrctr = 0; 201 Currline = 0; 202 if (firstf) setjmp (restore); 203 if (firstf) { 204 firstf = 0; 205 if (srchopt) 206 search (initbuf, f, 1); 207 else if (initopt) 208 skiplns (initline, f); 209 } 210 else if (fnum < nfiles && !no_tty) { 211 setjmp (restore); 212 left = command (fnames[fnum], f); 213 } 214 if (left != 0) { 215 if (noscroll || clearit) 216 doclear (); 217 if (prnames) { 218 if (bad_so) 219 erase (0); 220 pr("::::::::::::::"); 221 if (promptlen > 14) 222 erase (14); 223 printf ("\n%s\n::::::::::::::\n", fnames[fnum]); 224 if (left > Lpp - 4) 225 left = Lpp - 4; 226 } 227 if (no_tty) 228 copy_file (f); 229 else { 230 within++; 231 screen(f, left); 232 within = 0; 233 } 234 } 235 setjmp (restore); 236 fflush(stdout); 237 fclose(f); 238 screen_start.line = screen_start.chrctr = 0L; 239 context.line = context.chrctr = 0L; 240 } 241 fnum++; 242 firstf = 0; 243 } 244 reset_tty (); 245 exit(0); 246 } 247 248 /* 249 ** Check whether the file named by fs is an ASCII file which the user may 250 ** access. If it is, return the opened file. Otherwise return NULL. 251 */ 252 253 FILE * 254 checkf (fs, clearfirst) 255 register char *fs; 256 int *clearfirst; 257 { 258 struct stat stbuf; 259 register FILE *f; 260 char c; 261 262 if (stat (fs, &stbuf) == -1) { 263 fflush(stdout); 264 perror(fs); 265 return (NULL); 266 } 267 if ((stbuf.st_mode & S_IFMT) == S_IFDIR) { 268 printf("\n*** %s: directory ***\n\n", fs); 269 return (NULL); 270 } 271 if ((f=Fopen(fs, "r")) == NULL) { 272 fflush(stdout); 273 perror(fs); 274 return (NULL); 275 } 276 c = Getc(f); 277 278 /* Try to see whether it is an ASCII file */ 279 280 switch ((c | *f->_ptr << 8) & 0177777) { 281 case 0405: 282 case 0407: 283 case 0410: 284 case 0411: 285 case 0413: 286 case 0177545: 287 printf("\n******** %s: Not a text file ********\n\n", fs); 288 fclose (f); 289 return (NULL); 290 default: 291 break; 292 } 293 if (c == '\f') 294 *clearfirst = 1; 295 else { 296 *clearfirst = 0; 297 Ungetc (c, f); 298 } 299 if ((file_size = stbuf.st_size) == 0) 300 file_size = 0x7fffffffffffffffL; 301 return (f); 302 } 303 304 /* 305 ** A real function, for the tputs routine in termlib 306 */ 307 308 putch (ch) 309 char ch; 310 { 311 putchar (ch); 312 } 313 314 /* 315 ** Print out the contents of the file f, one screenful at a time. 316 */ 317 318 #define STOP -10 319 320 screen (f, num_lines) 321 register FILE *f; 322 register int num_lines; 323 { 324 register int c; 325 register int nchars; 326 int length; 327 328 for (;;) { 329 while (num_lines > 0 && !Pause) { 330 if ((nchars = getline (f, &length)) == EOF) 331 return; 332 if (bad_so || (Senter && *Senter == ' ') && promptlen > 0) 333 erase (0); 334 prbuf (Line, length); 335 if (nchars < promptlen) 336 erase (nchars); /* erase () sets promptlen to 0 */ 337 else promptlen = 0; 338 if (nchars < Mcol || !fold_opt) 339 putchar('\n'); 340 if (nchars == STOP) 341 break; 342 num_lines--; 343 } 344 fflush(stdout); 345 if ((c = Getc(f)) == EOF) 346 return; 347 Ungetc (c, f); 348 setjmp (restore); 349 Pause = 0; startup = 0; 350 if ((num_lines = command (NULL, f)) == 0) 351 return; 352 if (hard && promptlen > 0) 353 erase (0); 354 if (noscroll && num_lines == dlines) 355 doclear (); 356 screen_start.line = Currline; 357 screen_start.chrctr = Ftell (f); 358 } 359 } 360 361 /* 362 ** Come here if a quit signal is received 363 */ 364 365 onquit() 366 { 367 signal(SIGQUIT, SIG_IGN); 368 if (!inwait) { 369 putchar ('\n'); 370 if (!startup) { 371 signal(SIGQUIT, onquit); 372 longjmp (restore, 1); 373 } 374 else 375 Pause++; 376 } 377 else if (!dum_opt && notell) { 378 write (2, "[Use q or Q to quit]", 20); 379 promptlen += 20; 380 notell = 0; 381 } 382 signal(SIGQUIT, onquit); 383 } 384 385 /* 386 ** Clean up terminal state and exit. Also come here if interrupt signal received 387 */ 388 389 end_it () 390 { 391 392 reset_tty (); 393 if (promptlen > 0) { 394 kill_line (); 395 fflush (stdout); 396 } 397 else 398 write (2, "\n", 1); 399 _exit(0); 400 } 401 402 copy_file(f) 403 register FILE *f; 404 { 405 register int c; 406 407 while ((c = getc(f)) != EOF) 408 putchar(c); 409 } 410 411 /* Simplified printf function */ 412 413 printf (fmt, args) 414 register char *fmt; 415 int args; 416 { 417 register int *argp; 418 register char ch; 419 register int ccount; 420 421 ccount = 0; 422 argp = &args; 423 while (*fmt) { 424 while ((ch = *fmt++) != '%') { 425 if (ch == '\0') 426 return (ccount); 427 ccount++; 428 putchar (ch); 429 } 430 switch (*fmt++) { 431 case 'd': 432 ccount += printd (*argp); 433 break; 434 case 's': 435 ccount += pr ((char *)*argp); 436 break; 437 case '%': 438 ccount++; 439 argp--; 440 putchar ('%'); 441 break; 442 case '0': 443 return (ccount); 444 default: 445 break; 446 } 447 ++argp; 448 } 449 return (ccount); 450 451 } 452 453 /* 454 ** Print an integer as a string of decimal digits, 455 ** returning the length of the print representation. 456 */ 457 458 printd (n) 459 int n; 460 { 461 int a, nchars; 462 463 if (a = n/10) 464 nchars = 1 + printd(a); 465 else 466 nchars = 1; 467 putchar (n % 10 + '0'); 468 return (nchars); 469 } 470 471 /* Put the print representation of an integer into a string */ 472 static char *sptr; 473 474 scanstr (n, str) 475 int n; 476 char *str; 477 { 478 sptr = str; 479 sprintf (n); 480 *sptr = '\0'; 481 } 482 483 sprintf (n) 484 { 485 int a; 486 487 if (a = n/10) 488 sprintf (a); 489 *sptr++ = n % 10 + '0'; 490 } 491 492 static char bell = ctrl(G); 493 494 strlen (s) 495 char *s; 496 { 497 register char *p; 498 499 p = s; 500 while (*p++) 501 ; 502 return (p - s - 1); 503 } 504 505 /* See whether the last component of the path name "path" is equal to the 506 ** string "string" 507 */ 508 509 tailequ (path, string) 510 char *path; 511 register char *string; 512 { 513 register char *tail; 514 515 tail = path + strlen(path); 516 while (tail >= path) 517 if (*(--tail) == '/') 518 break; 519 ++tail; 520 while (*tail++ == *string++) 521 if (*tail == '\0') 522 return(1); 523 return(0); 524 } 525 526 prompt (filename) 527 char *filename; 528 { 529 if (promptlen > 0) 530 kill_line (); 531 if (!hard) { 532 promptlen = 8; 533 if (Senter && Sexit) 534 tputs (Senter, 1, putch); 535 pr("--More--"); 536 if (filename != NULL) { 537 promptlen += printf ("(Next file: %s)", filename); 538 } 539 else if (!no_intty) { 540 promptlen += printf ("(%d%%)", (int)((file_pos * 100) / file_size)); 541 } 542 if (dum_opt) { 543 promptlen += pr("[Hit space to continue, Rubout to abort]"); 544 } 545 if (Senter && Sexit) 546 tputs (Sexit, 1, putch); 547 fflush(stdout); 548 } 549 else 550 write (2, &bell, 1); 551 inwait++; 552 } 553 554 /* 555 ** Get a logical line 556 */ 557 558 getline(f, length) 559 register FILE *f; 560 int *length; 561 { 562 register int c; 563 register char *p; 564 register int column; 565 static int colflg; 566 567 p = Line; 568 column = 0; 569 c = Getc (f); 570 if (colflg && c == '\n') { 571 Currline++; 572 c = Getc (f); 573 } 574 while (p < &Line[LINSIZ - 1]) { 575 if (c == EOF) { 576 if (p > Line) { 577 *p = '\0'; 578 *length = p - Line; 579 return (column); 580 } 581 *length = p - Line; 582 return (EOF); 583 } 584 if (c == '\n') { 585 Currline++; 586 break; 587 } 588 *p++ = c; 589 if (c == '\t') 590 if (hardtabs && column < promptlen && !hard) { 591 if (eraseln && !dumb) { 592 column = 1 + (column | 7); 593 tputs (eraseln, 1, putch); 594 promptlen = 0; 595 } 596 else { 597 for (--p; column & 7 && p < &Line[LINSIZ - 1]; column++) { 598 *p++ = ' '; 599 } 600 if (column >= promptlen) promptlen = 0; 601 } 602 } 603 else 604 column = 1 + (column | 7); 605 else if (c == '\b') 606 column--; 607 else if (c == '\r') 608 column = 0; 609 else if (c == '\f' && stop_opt) { 610 p[-1] = '^'; 611 *p++ = 'L'; 612 column += 2; 613 Pause++; 614 } 615 else if (c == EOF) { 616 *length = p - Line; 617 return (column); 618 } 619 else if (c >= ' ' && c != RUBOUT) 620 column++; 621 if (column >= Mcol && fold_opt) break; 622 c = Getc (f); 623 } 624 if (column >= Mcol && Mcol > 0) { 625 if (!Wrap) { 626 *p++ = '\n'; 627 } 628 } 629 colflg = column == Mcol && fold_opt; 630 *length = p - Line; 631 *p = 0; 632 return (column); 633 } 634 635 /* 636 ** Erase the rest of the prompt, assuming we are starting at column col. 637 */ 638 639 erase (col) 640 register int col; 641 { 642 643 if (promptlen == 0) 644 return; 645 if (hard) { 646 putchar ('\n'); 647 } 648 else { 649 if (col == 0) 650 putchar ('\r'); 651 if (!dumb && eraseln) 652 tputs (eraseln, 1, putch); 653 else 654 for (col = promptlen - col; col > 0; col--) 655 putchar (' '); 656 } 657 promptlen = 0; 658 } 659 660 /* 661 ** Erase the current line entirely 662 */ 663 664 kill_line () 665 { 666 erase (0); 667 if (!eraseln || dumb) putchar ('\r'); 668 } 669 670 /* 671 ** Print string and return number of characters 672 */ 673 674 pr(s1) 675 char *s1; 676 { 677 register char *s; 678 register char c; 679 680 for (s = s1; c = *s++; ) 681 putchar(c); 682 return (s - s1 - 1); 683 } 684 685 686 /* Print a buffer of n characters */ 687 688 prbuf (s, n) 689 register char *s; 690 register int n; 691 { 692 while (n-- > 0) 693 putchar (*s++); 694 } 695 696 /* 697 ** Clear the screen 698 */ 699 700 doclear() 701 { 702 if (Clear && !hard) { 703 tputs(Clear, 1, putch); 704 705 /* Put out carriage return so that system doesn't 706 ** get confused by escape sequences when expanding tabs 707 */ 708 putchar ('\r'); 709 promptlen = 0; 710 } 711 } 712 713 static int lastcmd, lastarg, lastp; 714 static int lastcolon; 715 char shell_line[132]; 716 717 /* 718 ** Read a command and do it. A command consists of an optional integer 719 ** argument followed by the command character. Return the number of lines 720 ** to display in the next screenful. If there is nothing more to display 721 ** in the current file, zero is returned. 722 */ 723 724 command (filename, f) 725 char *filename; 726 register FILE *f; 727 { 728 register int nlines; 729 register int retval; 730 register char c; 731 char colonch; 732 FILE *helpf; 733 int done; 734 char comchar, cmdbuf[80], *p; 735 736 #define ret(val) retval=val;done++;break 737 738 done = 0; 739 if (!errors) 740 prompt (filename); 741 else 742 errors = 0; 743 if (MBIT == RAW && slow_tty) { 744 otty.sg_flags |= MBIT; 745 stty(2, &otty); 746 } 747 for (;;) { 748 nlines = number (&comchar); 749 lastp = colonch = 0; 750 if (comchar == '.') { /* Repeat last command */ 751 lastp++; 752 comchar = lastcmd; 753 nlines = lastarg; 754 if (lastcmd == ':') 755 colonch = lastcolon; 756 } 757 lastcmd = comchar; 758 lastarg = nlines; 759 if (comchar == otty.sg_erase) { 760 kill_line (); 761 prompt (filename); 762 continue; 763 } 764 switch (comchar) { 765 case ':': 766 retval = colon (filename, colonch, nlines); 767 if (retval >= 0) 768 done++; 769 break; 770 case ' ': 771 case 'z': 772 if (nlines == 0) nlines = dlines; 773 else if (comchar == 'z') dlines = nlines; 774 ret (nlines); 775 case 'd': 776 case ctrl(D): 777 if (nlines != 0) nscroll = nlines; 778 ret (nscroll); 779 case RUBOUT: 780 case 'q': 781 case 'Q': 782 end_it (); 783 case 's': 784 case 'f': 785 if (nlines == 0) nlines++; 786 if (comchar == 'f') 787 nlines *= dlines; 788 putchar ('\r'); 789 erase (0); 790 printf("\n...skipping %d line", nlines); 791 if (nlines > 1) 792 pr("s\n\n"); 793 else 794 pr("\n\n"); 795 while (nlines > 0) { 796 while ((c = Getc (f)) != '\n') 797 if (c == EOF) { 798 retval = 0; 799 done++; 800 goto endsw; 801 } 802 Currline++; 803 nlines--; 804 } 805 ret (dlines); 806 case '\n': 807 if (nlines != 0) 808 dlines = nlines; 809 else 810 nlines = 1; 811 ret (nlines); 812 case '\f': 813 if (!no_intty) { 814 doclear (); 815 Fseek (f, screen_start.chrctr); 816 Currline = screen_start.line; 817 ret (dlines); 818 } 819 else { 820 write (2, &bell, 1); 821 break; 822 } 823 case '\'': 824 if (!no_intty) { 825 kill_line (); 826 pr ("\n***Back***\n\n"); 827 Fseek (f, context.chrctr); 828 Currline = context.line; 829 ret (dlines); 830 } 831 else { 832 write (2, &bell, 1); 833 break; 834 } 835 case '=': 836 kill_line (); 837 promptlen = printd (Currline); 838 fflush (stdout); 839 break; 840 case 'n': 841 lastp++; 842 case '/': 843 if (nlines == 0) nlines++; 844 kill_line (); 845 pr ("/"); 846 promptlen = 1; 847 fflush (stdout); 848 if (lastp) { 849 write (2,"\r", 1); 850 search (NULL, f, nlines); /* Use previous r.e. */ 851 } 852 else { 853 ttyin (cmdbuf, 78, '/'); 854 write (2, "\r", 1); 855 search (cmdbuf, f, nlines); 856 } 857 ret (dlines); 858 case '!': 859 do_shell (filename); 860 break; 861 case 'h': 862 if ((helpf = fopen (HELPFILE, "r")) == NULL) 863 error ("Can't open help file"); 864 if (noscroll) doclear (); 865 copy_file (helpf); 866 close (helpf); 867 prompt (filename); 868 break; 869 case 'v': /* This case should go right before default */ 870 if (!no_intty) { 871 kill_line (); 872 cmdbuf[0] = '+'; 873 scanstr (Currline, &cmdbuf[1]); 874 pr ("vi "); pr (cmdbuf); putchar (' '); pr (fnames[fnum]); 875 execute (filename, VI, "vi", cmdbuf, fnames[fnum], 0); 876 break; 877 } 878 default: 879 write (2, &bell, 1); 880 break; 881 } 882 if (done) break; 883 } 884 putchar ('\r'); 885 endsw: 886 inwait = 0; 887 notell++; 888 if (MBIT == RAW && slow_tty) { 889 otty.sg_flags &= ~MBIT; 890 stty(2, &otty); 891 } 892 return (retval); 893 } 894 895 char ch; 896 897 /* 898 * Execute a colon-prefixed command. 899 * Returns <0 if not a command that should cause 900 * more of the file to be printed. 901 */ 902 903 colon (filename, cmd, nlines) 904 char *filename; 905 int cmd; 906 int nlines; 907 { 908 if (cmd == 0) 909 ch = readch (); 910 else 911 ch = cmd; 912 lastcolon = ch; 913 switch (ch) { 914 case 'f': 915 kill_line (); 916 if (!no_intty) 917 promptlen = printf ("\"%s\" line %d", fnames[fnum], Currline); 918 else 919 promptlen = printf ("[Not a file] line %d", Currline); 920 fflush (stdout); 921 return (-1); 922 case 'n': 923 if (nlines == 0) { 924 if (fnum >= nfiles - 1) 925 end_it (); 926 nlines++; 927 } 928 putchar ('\r'); 929 erase (0); 930 skipf (nlines); 931 return (0); 932 case 'p': 933 if (no_intty) { 934 write (2, &bell, 1); 935 return (-1); 936 } 937 putchar ('\r'); 938 erase (0); 939 if (nlines == 0) 940 nlines++; 941 skipf (-nlines); 942 return (0); 943 case '!': 944 do_shell (filename); 945 return (-1); 946 case 'q': 947 case 'Q': 948 end_it (); 949 default: 950 write (2, &bell, 1); 951 return (-1); 952 } 953 } 954 955 /* 956 ** Read a decimal number from the terminal. Set cmd to the non-digit which 957 ** terminates the number. 958 */ 959 960 number(cmd) 961 char *cmd; 962 { 963 register int i; 964 965 i = 0; ch = otty.sg_kill; 966 for (;;) { 967 ch = readch (); 968 if (ch >= '0' && ch <= '9') 969 i = i*10 + ch - '0'; 970 else if (ch == otty.sg_kill) 971 i = 0; 972 else { 973 *cmd = ch; 974 break; 975 } 976 } 977 return (i); 978 } 979 980 do_shell (filename) 981 char *filename; 982 { 983 char cmdbuf[80]; 984 985 kill_line (); 986 pr ("!"); 987 fflush (stdout); 988 promptlen = 1; 989 if (lastp) 990 pr (shell_line); 991 else { 992 ttyin (cmdbuf, 78, '!'); 993 if (expand (shell_line, cmdbuf)) { 994 kill_line (); 995 promptlen = printf ("!%s", shell_line); 996 } 997 } 998 fflush (stdout); 999 write (2, "\n", 1); 1000 promptlen = 0; 1001 shellp = 1; 1002 execute (filename, shell, shell, "-c", shell_line, 0); 1003 } 1004 1005 /* 1006 ** Search for nth ocurrence of regular expression contained in buf in the file 1007 */ 1008 1009 search (buf, file, n) 1010 char buf[]; 1011 FILE *file; 1012 register int n; 1013 { 1014 long startline = Ftell (file); 1015 register long line1 = startline; 1016 register long line2 = startline; 1017 register long line3 = startline; 1018 register int lncount; 1019 int saveln, rv, re_exec(); 1020 char *s, *re_comp(); 1021 1022 context.line = saveln = Currline; 1023 context.chrctr = startline; 1024 lncount = 0; 1025 if ((s = re_comp (buf)) != 0) 1026 error (s); 1027 while (!feof (file)) { 1028 line3 = line2; 1029 line2 = line1; 1030 line1 = Ftell (file); 1031 rdline (file); 1032 lncount++; 1033 if ((rv = re_exec (Line)) == 1) 1034 if (--n == 0) { 1035 if (lncount > 3 || (lncount > 1 && no_intty)) 1036 pr ("\n...skipping\n"); 1037 if (!no_intty) { 1038 Currline -= (lncount >= 3 ? 3 : lncount); 1039 Fseek (file, line3); 1040 } 1041 else { 1042 kill_line (); 1043 pr (Line); 1044 putchar ('\n'); 1045 } 1046 break; 1047 } 1048 else if (rv == -1) 1049 error ("Regular expression botch"); 1050 } 1051 if (feof (file)) { 1052 if (!no_intty) { 1053 #ifdef V6 1054 file->_flag &= ~_IOEOF; /* why doesn't fseek do this ??!!??! */ 1055 #endif 1056 Currline = saveln; 1057 Fseek (file, startline); 1058 } 1059 else { 1060 pr ("\nPattern not found\n"); 1061 end_it (); 1062 } 1063 error ("Pattern not found"); 1064 } 1065 } 1066 1067 execute (filename, cmd, args) 1068 char *filename; 1069 char *cmd, *args; 1070 { 1071 int id; 1072 1073 fflush (stdout); 1074 reset_tty (); 1075 while ((id = fork ()) < 0) 1076 sleep (5); 1077 if (id == 0) { 1078 execv (cmd, &args); 1079 write (2, "exec failed\n", 12); 1080 exit (1); 1081 } 1082 signal (SIGINT, SIG_IGN); 1083 signal (SIGQUIT, SIG_IGN); 1084 #ifdef SIGTSTP 1085 if (catch_susp) 1086 signal(SIGTSTP, SIG_DFL); 1087 #endif 1088 wait (0); 1089 signal (SIGINT, end_it); 1090 signal (SIGQUIT, onquit); 1091 #ifdef SIGTSTP 1092 if (catch_susp) 1093 signal(SIGTSTP, onsusp); 1094 #endif 1095 set_tty (); 1096 pr ("------------------------\n"); 1097 prompt (filename); 1098 } 1099 /* 1100 ** Skip n lines in the file f 1101 */ 1102 1103 skiplns (n, f) 1104 register int n; 1105 register FILE *f; 1106 { 1107 register char c; 1108 1109 while (n > 0) { 1110 while ((c = Getc (f)) != '\n') 1111 if (c == EOF) 1112 return; 1113 n--; 1114 Currline++; 1115 } 1116 } 1117 1118 /* 1119 ** Skip nskip files in the file list (from the command line). Nskip may be 1120 ** negative. 1121 */ 1122 1123 skipf (nskip) 1124 register int nskip; 1125 { 1126 if (nskip == 0) return; 1127 if (nskip > 0) { 1128 if (fnum + nskip > nfiles - 1) 1129 nskip = nfiles - fnum - 1; 1130 } 1131 else if (within) 1132 ++fnum; 1133 fnum += nskip; 1134 if (fnum < 0) 1135 fnum = 0; 1136 pr ("\n...Skipping "); 1137 pr (nskip > 0 ? "to file " : "back to file "); 1138 pr (fnames[fnum]); 1139 pr ("\n\n"); 1140 --fnum; 1141 } 1142 1143 /*----------------------------- Terminal I/O -------------------------------*/ 1144 1145 initterm () 1146 { 1147 char buf[TBUFSIZ]; 1148 char clearbuf[100]; 1149 char *clearptr, *padstr; 1150 char *getenv(); 1151 int ldisc; 1152 1153 setbuf(stdout, obuf); 1154 if (!(no_tty = gtty(1, &otty))) { 1155 if (tgetent(buf, getenv("TERM")) <= 0) { 1156 dumb++; 1157 } 1158 else { 1159 if (((Lpp = tgetnum("li")) < 0) || tgetflag("hc")) { 1160 hard++; /* Hard copy terminal */ 1161 Lpp = 24; 1162 } 1163 if (tailequ (fnames[0], "page") || !hard && tgetflag("ns")) 1164 noscroll++; 1165 if ((Mcol = tgetnum("co")) < 0) 1166 Mcol = 80; 1167 Wrap = tgetflag("am"); 1168 bad_so = tgetflag ("xs"); 1169 clearptr = clearbuf; 1170 eraseln = tgetstr("ce",&clearptr); 1171 Clear = tgetstr("cl", &clearptr); 1172 Senter = tgetstr("so", &clearptr); 1173 Sexit = tgetstr("se", &clearptr); 1174 if (padstr = tgetstr("pc", &clearptr)) 1175 PC = *padstr; 1176 } 1177 if ((shell = getenv("SHELL")) == NULL) 1178 shell = "/bin/sh"; 1179 } 1180 no_intty = gtty(0, &otty); 1181 gtty(2, &otty); 1182 ospeed = otty.sg_ospeed; 1183 slow_tty = ospeed < B1200; 1184 hardtabs = !(otty.sg_flags & XTABS); 1185 if (!no_tty) { 1186 otty.sg_flags &= ~ECHO; 1187 if (MBIT == CBREAK || !slow_tty) 1188 otty.sg_flags |= MBIT; 1189 } 1190 } 1191 1192 readch () 1193 { 1194 char ch; 1195 extern int errno; 1196 1197 if (read (2, &ch, 1) <= 0) 1198 if (errno != EINTR) 1199 exit(0); 1200 else 1201 ch = otty.sg_kill; 1202 return (ch); 1203 } 1204 1205 static char BS = '\b'; 1206 static char CARAT = '^'; 1207 1208 ttyin (buf, nmax, pchar) 1209 char buf[]; 1210 register int nmax; 1211 char pchar; 1212 { 1213 register char *sptr; 1214 register char ch; 1215 register int slash = 0; 1216 int maxlen; 1217 char cbuf; 1218 1219 sptr = buf; 1220 maxlen = 0; 1221 while (sptr - buf < nmax) { 1222 if (promptlen > maxlen) maxlen = promptlen; 1223 ch = readch (); 1224 if (ch == '\\') { 1225 slash++; 1226 } 1227 else if ((ch == otty.sg_erase) && !slash) { 1228 if (sptr > buf) { 1229 --promptlen; 1230 write (2, &BS, 1); 1231 --sptr; 1232 if ((*sptr < ' ' && *sptr != '\n') || *sptr == RUBOUT) { 1233 --promptlen; 1234 write (2, &BS, 1); 1235 } 1236 continue; 1237 } 1238 else { 1239 if (!eraseln) promptlen = maxlen; 1240 longjmp (restore, 1); 1241 } 1242 } 1243 else if ((ch == otty.sg_kill) && !slash) { 1244 if (hard) { 1245 show (ch); 1246 putchar ('\n'); 1247 putchar (pchar); 1248 } 1249 else { 1250 putchar ('\r'); 1251 putchar (pchar); 1252 if (eraseln) 1253 erase (1); 1254 promptlen = 1; 1255 } 1256 sptr = buf; 1257 fflush (stdout); 1258 continue; 1259 } 1260 if (slash && (ch == otty.sg_kill || ch == otty.sg_erase)) { 1261 write (2, &BS, 1); 1262 --sptr; 1263 } 1264 if (ch != '\\') 1265 slash = 0; 1266 *sptr++ = ch; 1267 if ((ch < ' ' && ch != '\n' && ch != ESC) || ch == RUBOUT) { 1268 ch += ch == RUBOUT ? -0100 : 0100; 1269 write (2, &CARAT, 1); 1270 promptlen++; 1271 } 1272 cbuf = ch; 1273 if (ch != '\n' && ch != ESC) { 1274 write (2, &cbuf, 1); 1275 promptlen++; 1276 } 1277 else 1278 break; 1279 } 1280 *--sptr = '\0'; 1281 if (!eraseln) promptlen = maxlen; 1282 if (sptr - buf >= nmax - 1) 1283 error ("Line too long"); 1284 } 1285 1286 expand (outbuf, inbuf) 1287 char *outbuf; 1288 char *inbuf; 1289 { 1290 register char *instr; 1291 register char *outstr; 1292 register char ch; 1293 char temp[200]; 1294 int changed = 0; 1295 1296 instr = inbuf; 1297 outstr = temp; 1298 while ((ch = *instr++) != '\0') 1299 switch (ch) { 1300 case '%': 1301 if (!no_intty) { 1302 strcpy (outstr, fnames[fnum]); 1303 outstr += strlen (fnames[fnum]); 1304 changed++; 1305 } 1306 else 1307 *outstr++ = ch; 1308 break; 1309 case '!': 1310 if (!shellp) 1311 error ("No previous command to substitute for"); 1312 strcpy (outstr, shell_line); 1313 outstr += strlen (shell_line); 1314 changed++; 1315 break; 1316 case '\\': 1317 if (*instr == '%' || *instr == '!') { 1318 *outstr++ = *instr++; 1319 break; 1320 } 1321 default: 1322 *outstr++ = ch; 1323 } 1324 *outstr++ = '\0'; 1325 strcpy (outbuf, temp); 1326 return (changed); 1327 } 1328 1329 show (ch) 1330 register char ch; 1331 { 1332 char cbuf; 1333 1334 if ((ch < ' ' && ch != '\n' && ch != ESC) || ch == RUBOUT) { 1335 ch += ch == RUBOUT ? -0100 : 0100; 1336 write (2, &CARAT, 1); 1337 promptlen++; 1338 } 1339 cbuf = ch; 1340 write (2, &cbuf, 1); 1341 promptlen++; 1342 } 1343 1344 error (mess) 1345 char *mess; 1346 { 1347 kill_line (); 1348 promptlen += strlen (mess); 1349 if (Senter && Sexit) { 1350 tputs (Senter, 1, putch); 1351 pr(mess); 1352 tputs (Sexit, 1, putch); 1353 } 1354 else 1355 pr (mess); 1356 fflush(stdout); 1357 errors++; 1358 longjmp (restore, 1); 1359 } 1360 1361 1362 set_tty () 1363 { 1364 otty.sg_flags |= MBIT; 1365 otty.sg_flags &= ~ECHO; 1366 stty(2, &otty); 1367 } 1368 1369 reset_tty () 1370 { 1371 otty.sg_flags |= ECHO; 1372 otty.sg_flags &= ~MBIT; 1373 stty(2, &otty); 1374 } 1375 1376 rdline (f) 1377 register FILE *f; 1378 { 1379 register char c; 1380 register char *p; 1381 1382 p = Line; 1383 while ((c = Getc (f)) != '\n' && c != EOF && p - Line < LINSIZ - 1) 1384 *p++ = c; 1385 if (c == '\n') 1386 Currline++; 1387 *p = '\0'; 1388 } 1389 1390 /* Come here when we get a suspend signal from the terminal */ 1391 1392 #ifdef SIGTSTP 1393 onsusp () 1394 { 1395 reset_tty (); 1396 fflush (stdout); 1397 /* Send the TSTP signal to suspend our process group */ 1398 kill (0, SIGTSTP); 1399 /* Pause for station break */ 1400 1401 /* We're back */ 1402 signal (SIGTSTP, onsusp); 1403 set_tty (); 1404 if (inwait) 1405 longjmp (restore); 1406 } 1407 #endif 1408