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