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