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