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