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