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