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