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