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