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