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