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