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