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: #if !defined(lint) && defined(DOSCCS) 8: char copyright[] = 9: "@(#) Copyright (c) 1980 Regents of the University of California.\n\ 10: All rights reserved.\n"; 11: 12: static char sccsid[] = "@(#)more.c 5.4.2 (2.11BSD) 1997/10/18"; 13: #endif 14: 15: /* 16: ** more.c - General purpose tty output filter and file perusal program 17: ** 18: ** by Eric Shienbrood, UC Berkeley 19: ** 20: ** modified by Geoff Peck, UCB to add underlining, single spacing 21: ** modified by John Foderaro, UCB to add -c and MORE environment variable 22: */ 23: 24: #include <stdio.h> 25: #undef putchar /* force use of function rather than macro */ 26: #include <sys/types.h> 27: #include <ctype.h> 28: #include <signal.h> 29: #include <errno.h> 30: #include <paths.h> 31: #include <sgtty.h> 32: #include <setjmp.h> 33: #include <sys/stat.h> 34: #include <stdlib.h> 35: #include <unistd.h> 36: 37: #define HELPFILE "/usr/share/misc/more.help" 38: #define VI "/usr/ucb/vi" 39: 40: #define Fopen(s,m) (Currline = 0,file_pos=0,fopen(s,m)) 41: #define Ftell(f) file_pos 42: #define Fseek(f,off) (file_pos=off,fseek(f,off,0)) 43: #define Getc(f) (++file_pos, getc(f)) 44: #define Ungetc(c,f) (--file_pos, ungetc(c,f)) 45: 46: #define MBIT CBREAK 47: #define stty(fd,argp) ioctl(fd,TIOCSETN,argp) 48: 49: #define TBUFSIZ 1024 50: #define LINSIZ 256 51: #define ctrl(letter) ('letter' & 077) 52: #define RUBOUT '\177' 53: #define ESC '\033' 54: #define QUIT '\034' 55: 56: struct sgttyb otty, savetty; 57: long file_pos, file_size; 58: int fnum, no_intty, no_tty, slow_tty; 59: int dum_opt, dlines, onquit(), end_it(), chgwinsz(); 60: int onsusp(); 61: int nscroll = 11; /* Number of lines scrolled by 'd' */ 62: int fold_opt = 1; /* Fold long lines */ 63: int stop_opt = 1; /* Stop after form feeds */ 64: int ssp_opt = 0; /* Suppress white space */ 65: int ul_opt = 1; /* Underline as best we can */ 66: int promptlen; 67: int Currline; /* Line we are currently at */ 68: int startup = 1; 69: int firstf = 1; 70: int notell = 1; 71: int docrterase = 0; 72: int docrtkill = 0; 73: int bad_so; /* True if overwriting does not turn off standout */ 74: int inwait, Pause, errors; 75: int within; /* true if we are within a file, 76: false if we are between files */ 77: int hard, dumb, noscroll, hardtabs, clreol; 78: int catch_susp; /* We should catch the SIGTSTP signal */ 79: char **fnames; /* The list of file names */ 80: int nfiles; /* Number of files left to process */ 81: char *shell; /* The name of the shell to use */ 82: int shellp; /* A previous shell command exists */ 83: char ch; 84: jmp_buf restore; 85: char Line[LINSIZ]; /* Line buffer */ 86: int Lpp = 24; /* lines per page */ 87: char *Clear; /* clear screen */ 88: char *eraseln; /* erase line */ 89: char *Senter, *Sexit;/* enter and exit standout mode */ 90: char *ULenter, *ULexit; /* enter and exit underline mode */ 91: char *chUL; /* underline character */ 92: char *chBS; /* backspace character */ 93: char *Home; /* go to home */ 94: char *cursorm; /* cursor movement */ 95: char cursorhome[40]; /* contains cursor movement to home */ 96: char *EodClr; /* clear rest of screen */ 97: char *tgetstr(); 98: int Mcol = 80; /* number of columns */ 99: int Wrap = 1; /* set if automargins */ 100: int soglitch; /* terminal has standout mode glitch */ 101: int ulglitch; /* terminal has underline mode glitch */ 102: int pstate = 0; /* current UL state */ 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: register int n; 597: { 598: register 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: register int n; 622: { 623: register int a; 624: 625: if (a = n/10) 626: Sprintf (a); 627: *sptr++ = n % 10 + '0'; 628: } 629: 630: static char bell = ctrl(G); 631: 632: /* See whether the last component of the path name "path" is equal to the 633: ** string "string" 634: */ 635: 636: tailequ (path, string) 637: char *path; 638: register char *string; 639: { 640: register char *tail; 641: 642: tail = path + strlen(path); 643: while (tail >= path) 644: if (*(--tail) == '/') 645: break; 646: ++tail; 647: while (*tail++ == *string++) 648: if (*tail == '\0') 649: return(1); 650: return(0); 651: } 652: 653: prompt (filename) 654: char *filename; 655: { 656: if (clreol) 657: cleareol (); 658: else if (promptlen > 0) 659: kill_line (); 660: if (!hard) { 661: promptlen = 8; 662: if (Senter && Sexit) { 663: tputs (Senter, 1, putch); 664: promptlen += (2 * soglitch); 665: } 666: if (clreol) 667: cleareol (); 668: pr("--More--"); 669: if (filename != NULL) { 670: promptlen += printf ("(Next file: %s)", filename); 671: } 672: else if (!no_intty) { 673: promptlen += printf ("(%d%%)", (int)((file_pos * 100) / file_size)); 674: } 675: if (dum_opt) { 676: promptlen += pr("[Press space to continue, 'q' to quit.]"); 677: } 678: if (Senter && Sexit) 679: tputs (Sexit, 1, putch); 680: if (clreol) 681: clreos (); 682: fflush(stdout); 683: } 684: else 685: write (2, &bell, 1); 686: inwait++; 687: } 688: 689: /* 690: ** Get a logical line 691: */ 692: 693: getline(f, length) 694: register FILE *f; 695: int *length; 696: { 697: register int c; 698: register char *p; 699: register int column; 700: static int colflg; 701: 702: p = Line; 703: column = 0; 704: c = Getc (f); 705: if (colflg && c == '\n') { 706: Currline++; 707: c = Getc (f); 708: } 709: while (p < &Line[LINSIZ - 1]) { 710: if (c == EOF) { 711: if (p > Line) { 712: *p = '\0'; 713: *length = p - Line; 714: return (column); 715: } 716: *length = p - Line; 717: return (EOF); 718: } 719: if (c == '\n') { 720: Currline++; 721: break; 722: } 723: *p++ = c; 724: if (c == '\t') 725: if (hardtabs && column < promptlen && !hard) { 726: if (eraseln && !dumb) { 727: column = 1 + (column | 7); 728: tputs (eraseln, 1, putch); 729: promptlen = 0; 730: } 731: else { 732: for (--p; column & 7 && p < &Line[LINSIZ - 1]; column++) { 733: *p++ = ' '; 734: } 735: if (column >= promptlen) promptlen = 0; 736: } 737: } 738: else 739: column = 1 + (column | 7); 740: else if (c == '\b' && column > 0) 741: column--; 742: else if (c == '\r') 743: column = 0; 744: else if (c == '\f' && stop_opt) { 745: p[-1] = '^'; 746: *p++ = 'L'; 747: column += 2; 748: Pause++; 749: } 750: else if (c == EOF) { 751: *length = p - Line; 752: return (column); 753: } 754: else if (c >= ' ' && c != RUBOUT) 755: column++; 756: if (column >= Mcol && fold_opt) break; 757: c = Getc (f); 758: } 759: if (column >= Mcol && Mcol > 0) { 760: if (!Wrap) { 761: *p++ = '\n'; 762: } 763: } 764: colflg = column == Mcol && fold_opt; 765: *length = p - Line; 766: *p = 0; 767: return (column); 768: } 769: 770: /* 771: ** Erase the rest of the prompt, assuming we are starting at column col. 772: */ 773: 774: erase (col) 775: register int col; 776: { 777: 778: if (promptlen == 0) 779: return; 780: if (hard) { 781: putchar ('\n'); 782: } 783: else { 784: if (col == 0) 785: putchar ('\r'); 786: if (!dumb && eraseln) 787: tputs (eraseln, 1, putch); 788: else 789: for (col = promptlen - col; col > 0; col--) 790: putchar (' '); 791: } 792: promptlen = 0; 793: } 794: 795: /* 796: ** Erase the current line entirely 797: */ 798: 799: kill_line () 800: { 801: erase (0); 802: if (!eraseln || dumb) putchar ('\r'); 803: } 804: 805: /* 806: * force clear to end of line 807: */ 808: cleareol() 809: { 810: tputs(eraseln, 1, putch); 811: } 812: 813: clreos() 814: { 815: tputs(EodClr, 1, putch); 816: } 817: 818: /* 819: ** Print string and return number of characters 820: */ 821: 822: pr(s1) 823: char *s1; 824: { 825: register char *s; 826: register char c; 827: 828: for (s = s1; c = *s++; ) 829: putchar(c); 830: return (s - s1 - 1); 831: } 832: 833: 834: /* Print a buffer of n characters */ 835: 836: prbuf (s, n) 837: register char *s; 838: register int n; 839: { 840: register char c; /* next output character */ 841: register int state; /* next output char's UL state */ 842: #define wouldul(s,n) ((n) >= 2 && (((s)[0] == '_' && (s)[1] == '\b') || ((s)[1] == '\b' && (s)[2] == '_'))) 843: 844: while (--n >= 0) 845: if (!ul_opt) 846: putchar (*s++); 847: else { 848: if (*s == ' ' && pstate == 0 && ulglitch && wouldul(s+1, n-1)) { 849: s++; 850: continue; 851: } 852: if (state = wouldul(s, n)) { 853: c = (*s == '_')? s[2] : *s ; 854: n -= 2; 855: s += 3; 856: } else 857: c = *s++; 858: if (state != pstate) { 859: if (c == ' ' && state == 0 && ulglitch && wouldul(s, n-1)) 860: state = 1; 861: else 862: tputs(state ? ULenter : ULexit, 1, putch); 863: } 864: if (c != ' ' || pstate == 0 || state != 0 || ulglitch == 0) 865: putchar(c); 866: if (state && *chUL) { 867: pr(chBS); 868: tputs(chUL, 1, putch); 869: } 870: pstate = state; 871: } 872: } 873: 874: /* 875: ** Clear the screen 876: */ 877: 878: doclear() 879: { 880: if (Clear && !hard) { 881: tputs(Clear, 1, putch); 882: 883: /* Put out carriage return so that system doesn't 884: ** get confused by escape sequences when expanding tabs 885: */ 886: putchar ('\r'); 887: promptlen = 0; 888: } 889: } 890: 891: /* 892: * Go to home position 893: */ 894: home() 895: { 896: tputs(Home,1,putch); 897: } 898: 899: static int lastcmd, lastarg, lastp; 900: static int lastcolon; 901: char shell_line[132]; 902: 903: /* 904: ** Read a command and do it. A command consists of an optional integer 905: ** argument followed by the command character. Return the number of lines 906: ** to display in the next screenful. If there is nothing more to display 907: ** in the current file, zero is returned. 908: */ 909: 910: command (filename, f) 911: char *filename; 912: register FILE *f; 913: { 914: register int nlines; 915: register int retval; 916: register char c; 917: char colonch; 918: FILE *helpf; 919: int done; 920: char comchar, cmdbuf[80], *p; 921: 922: #define ret(val) retval=val;done++;break 923: 924: done = 0; 925: if (!errors) 926: prompt (filename); 927: else 928: errors = 0; 929: if (MBIT == RAW && slow_tty) { 930: otty.sg_flags |= MBIT; 931: stty(fileno(stderr), &otty); 932: } 933: for (;;) { 934: nlines = number (&comchar); 935: lastp = colonch = 0; 936: if (comchar == '.') { /* Repeat last command */ 937: lastp++; 938: comchar = lastcmd; 939: nlines = lastarg; 940: if (lastcmd == ':') 941: colonch = lastcolon; 942: } 943: lastcmd = comchar; 944: lastarg = nlines; 945: if (comchar == otty.sg_erase) { 946: kill_line (); 947: prompt (filename); 948: continue; 949: } 950: switch (comchar) { 951: case ':': 952: retval = colon (filename, colonch, nlines); 953: if (retval >= 0) 954: done++; 955: break; 956: case 'b': 957: case ctrl(B): 958: { 959: register int initline; 960: 961: if (no_intty) { 962: write(2, &bell, 1); 963: return (-1); 964: } 965: 966: if (nlines == 0) nlines++; 967: 968: putchar ('\r'); 969: erase (0); 970: printf ("\n"); 971: if (clreol) 972: cleareol (); 973: printf ("...back %d page", nlines); 974: if (nlines > 1) 975: pr ("s\n"); 976: else 977: pr ("\n"); 978: 979: if (clreol) 980: cleareol (); 981: pr ("\n"); 982: 983: initline = Currline - dlines * (nlines + 1); 984: if (! noscroll) 985: --initline; 986: if (initline < 0) initline = 0; 987: Fseek(f, 0L); 988: Currline = 0; /* skiplns() will make Currline correct */ 989: skiplns(initline, f); 990: if (! noscroll) { 991: ret(dlines + 1); 992: } 993: else { 994: ret(dlines); 995: } 996: } 997: case ' ': 998: case 'z': 999: if (nlines == 0) nlines = dlines; 1000: else if (comchar == 'z') dlines = nlines; 1001: ret (nlines); 1002: case 'd': 1003: case ctrl(D): 1004: if (nlines != 0) nscroll = nlines; 1005: ret (nscroll); 1006: case 'q': 1007: case 'Q': 1008: end_it (); 1009: case 's': 1010: case 'f': 1011: if (nlines == 0) nlines++; 1012: if (comchar == 'f') 1013: nlines *= dlines; 1014: putchar ('\r'); 1015: erase (0); 1016: printf ("\n"); 1017: if (clreol) 1018: cleareol (); 1019: printf ("...skipping %d line", nlines); 1020: if (nlines > 1) 1021: pr ("s\n"); 1022: else 1023: pr ("\n"); 1024: 1025: if (clreol) 1026: cleareol (); 1027: pr ("\n"); 1028: 1029: while (nlines > 0) { 1030: while ((c = Getc (f)) != '\n') 1031: if (c == EOF) { 1032: retval = 0; 1033: done++; 1034: goto endsw; 1035: } 1036: Currline++; 1037: nlines--; 1038: } 1039: ret (dlines); 1040: case '\n': 1041: if (nlines != 0) 1042: dlines = nlines; 1043: else 1044: nlines = 1; 1045: ret (nlines); 1046: case '\f': 1047: if (!no_intty) { 1048: doclear (); 1049: Fseek (f, screen_start.chrctr); 1050: Currline = screen_start.line; 1051: ret (dlines); 1052: } 1053: else { 1054: write (2, &bell, 1); 1055: break; 1056: } 1057: case '\'': 1058: if (!no_intty) { 1059: kill_line (); 1060: pr ("\n***Back***\n\n"); 1061: Fseek (f, context.chrctr); 1062: Currline = context.line; 1063: ret (dlines); 1064: } 1065: else { 1066: write (2, &bell, 1); 1067: break; 1068: } 1069: case '=': 1070: kill_line (); 1071: promptlen = printd (Currline); 1072: fflush (stdout); 1073: break; 1074: case 'n': 1075: lastp++; 1076: case '/': 1077: if (nlines == 0) nlines++; 1078: kill_line (); 1079: pr ("/"); 1080: promptlen = 1; 1081: fflush (stdout); 1082: if (lastp) { 1083: write (2,"\r", 1); 1084: search (NULL, f, nlines); /* Use previous r.e. */ 1085: } 1086: else { 1087: ttyin (cmdbuf, 78, '/'); 1088: write (2, "\r", 1); 1089: search (cmdbuf, f, nlines); 1090: } 1091: ret (dlines-1); 1092: case '!': 1093: do_shell (filename); 1094: break; 1095: case '?': 1096: case 'h': 1097: if ((helpf = fopen (HELPFILE, "r")) == NULL) 1098: error ("Can't open help file"); 1099: if (noscroll) doclear (); 1100: copy_file (helpf); 1101: fclose (helpf); 1102: prompt (filename); 1103: break; 1104: case 'v': /* This case should go right before default */ 1105: if (!no_intty) { 1106: kill_line (); 1107: cmdbuf[0] = '+'; 1108: scanstr (Currline - dlines < 0 ? 0 1109: : Currline - (dlines + 1) / 2, &cmdbuf[1]); 1110: pr ("vi "); pr (cmdbuf); putchar (' '); pr (fnames[fnum]); 1111: execute (filename, VI, "vi", cmdbuf, fnames[fnum], 0); 1112: break; 1113: } 1114: default: 1115: if (dum_opt) { 1116: kill_line (); 1117: if (Senter && Sexit) { 1118: tputs (Senter, 1, putch); 1119: promptlen = pr ("[Press 'h' for instructions.]") + (2 * soglitch); 1120: tputs (Sexit, 1, putch); 1121: } 1122: else 1123: promptlen = pr ("[Press 'h' for instructions.]"); 1124: fflush (stdout); 1125: } 1126: else 1127: write (2, &bell, 1); 1128: break; 1129: } 1130: if (done) break; 1131: } 1132: putchar ('\r'); 1133: endsw: 1134: inwait = 0; 1135: notell++; 1136: if (MBIT == RAW && slow_tty) { 1137: otty.sg_flags &= ~MBIT; 1138: stty(fileno(stderr), &otty); 1139: } 1140: return (retval); 1141: } 1142: 1143: char ch; 1144: 1145: /* 1146: * Execute a colon-prefixed command. 1147: * Returns <0 if not a command that should cause 1148: * more of the file to be printed. 1149: */ 1150: 1151: colon (filename, cmd, nlines) 1152: char *filename; 1153: int cmd; 1154: int nlines; 1155: { 1156: if (cmd == 0) 1157: ch = readch (); 1158: else 1159: ch = cmd; 1160: lastcolon = ch; 1161: switch (ch) { 1162: case 'f': 1163: kill_line (); 1164: if (!no_intty) 1165: promptlen = printf ("\"%s\" line %d", fnames[fnum], Currline); 1166: else 1167: promptlen = printf ("[Not a file] line %d", Currline); 1168: fflush (stdout); 1169: return (-1); 1170: case 'n': 1171: if (nlines == 0) { 1172: if (fnum >= nfiles - 1) 1173: end_it (); 1174: nlines++; 1175: } 1176: putchar ('\r'); 1177: erase (0); 1178: skipf (nlines); 1179: return (0); 1180: case 'p': 1181: if (no_intty) { 1182: write (2, &bell, 1); 1183: return (-1); 1184: } 1185: putchar ('\r'); 1186: erase (0); 1187: if (nlines == 0) 1188: nlines++; 1189: skipf (-nlines); 1190: return (0); 1191: case '!': 1192: do_shell (filename); 1193: return (-1); 1194: case 'q': 1195: case 'Q': 1196: end_it (); 1197: default: 1198: write (2, &bell, 1); 1199: return (-1); 1200: } 1201: } 1202: 1203: /* 1204: ** Read a decimal number from the terminal. Set cmd to the non-digit which 1205: ** terminates the number. 1206: */ 1207: 1208: number(cmd) 1209: char *cmd; 1210: { 1211: register int i; 1212: 1213: i = 0; ch = otty.sg_kill; 1214: for (;;) { 1215: ch = readch (); 1216: if (ch >= '0' && ch <= '9') 1217: i = i*10 + ch - '0'; 1218: else if (ch == otty.sg_kill) 1219: i = 0; 1220: else { 1221: *cmd = ch; 1222: break; 1223: } 1224: } 1225: return (i); 1226: } 1227: 1228: do_shell (filename) 1229: char *filename; 1230: { 1231: char cmdbuf[80]; 1232: 1233: kill_line (); 1234: pr ("!"); 1235: fflush (stdout); 1236: promptlen = 1; 1237: if (lastp) 1238: pr (shell_line); 1239: else { 1240: ttyin (cmdbuf, 78, '!'); 1241: if (expand (shell_line, cmdbuf)) { 1242: kill_line (); 1243: promptlen = printf ("!%s", shell_line); 1244: } 1245: } 1246: fflush (stdout); 1247: write (2, "\n", 1); 1248: promptlen = 0; 1249: shellp = 1; 1250: execute (filename, shell, shell, "-c", shell_line, 0); 1251: } 1252: 1253: /* 1254: ** Search for nth ocurrence of regular expression contained in buf in the file 1255: */ 1256: 1257: search (buf, file, n) 1258: char buf[]; 1259: FILE *file; 1260: register int n; 1261: { 1262: long startline = Ftell (file); 1263: register long line1 = startline; 1264: register long line2 = startline; 1265: register long line3 = startline; 1266: register int lncount; 1267: int saveln, rv; 1268: char *s; 1269: 1270: context.line = saveln = Currline; 1271: context.chrctr = startline; 1272: lncount = 0; 1273: if ((s = re_comp (buf)) != 0) 1274: error (s); 1275: while (!feof (file)) { 1276: line3 = line2; 1277: line2 = line1; 1278: line1 = Ftell (file); 1279: rdline (file); 1280: lncount++; 1281: if ((rv = re_exec (Line)) == 1) 1282: if (--n == 0) { 1283: if (lncount > 3 || (lncount > 1 && no_intty)) 1284: { 1285: pr ("\n"); 1286: if (clreol) 1287: cleareol (); 1288: pr("...skipping\n"); 1289: } 1290: if (!no_intty) { 1291: Currline -= (lncount >= 3 ? 3 : lncount); 1292: Fseek (file, line3); 1293: if (noscroll) 1294: if (clreol) { 1295: home (); 1296: cleareol (); 1297: } 1298: else 1299: doclear (); 1300: } 1301: else { 1302: kill_line (); 1303: if (noscroll) 1304: if (clreol) { 1305: home (); 1306: cleareol (); 1307: } 1308: else 1309: doclear (); 1310: pr (Line); 1311: putchar ('\n'); 1312: } 1313: break; 1314: } 1315: else if (rv == -1) 1316: error ("Regular expression botch"); 1317: } 1318: if (feof (file)) { 1319: if (!no_intty) { 1320: file->_flag &= ~_IOEOF; /* why doesn't fseek do this ??!!??! */ 1321: Currline = saveln; 1322: Fseek (file, startline); 1323: } 1324: else { 1325: pr ("\nPattern not found\n"); 1326: end_it (); 1327: } 1328: error ("Pattern not found"); 1329: } 1330: } 1331: 1332: execute (filename, cmd, args) 1333: char *filename; 1334: char *cmd, *args; 1335: { 1336: int id; 1337: int n; 1338: 1339: fflush (stdout); 1340: reset_tty (); 1341: for (n = 10; (id = fork ()) < 0 && n > 0; n--) 1342: sleep (5); 1343: if (id == 0) { 1344: if (!isatty(0)) { 1345: close(0); 1346: open("/dev/tty", 0); 1347: } 1348: execv (cmd, &args); 1349: write (2, "exec failed\n", 12); 1350: exit (1); 1351: } 1352: if (id > 0) { 1353: signal (SIGINT, SIG_IGN); 1354: signal (SIGQUIT, SIG_IGN); 1355: if (catch_susp) 1356: signal(SIGTSTP, SIG_DFL); 1357: while (wait(0) > 0); 1358: signal (SIGINT, end_it); 1359: signal (SIGQUIT, onquit); 1360: if (catch_susp) 1361: signal(SIGTSTP, onsusp); 1362: } else 1363: write(2, "can't fork\n", 11); 1364: set_tty (); 1365: pr ("------------------------\n"); 1366: prompt (filename); 1367: } 1368: /* 1369: ** Skip n lines in the file f 1370: */ 1371: 1372: skiplns (n, f) 1373: register int n; 1374: register FILE *f; 1375: { 1376: register char c; 1377: 1378: while (n > 0) { 1379: while ((c = Getc (f)) != '\n') 1380: if (c == EOF) 1381: return; 1382: n--; 1383: Currline++; 1384: } 1385: } 1386: 1387: /* 1388: ** Skip nskip files in the file list (from the command line). Nskip may be 1389: ** negative. 1390: */ 1391: 1392: skipf (nskip) 1393: register int nskip; 1394: { 1395: if (nskip == 0) return; 1396: if (nskip > 0) { 1397: if (fnum + nskip > nfiles - 1) 1398: nskip = nfiles - fnum - 1; 1399: } 1400: else if (within) 1401: ++fnum; 1402: fnum += nskip; 1403: if (fnum < 0) 1404: fnum = 0; 1405: pr ("\n...Skipping "); 1406: pr ("\n"); 1407: if (clreol) 1408: cleareol (); 1409: pr ("...Skipping "); 1410: pr (nskip > 0 ? "to file " : "back to file "); 1411: pr (fnames[fnum]); 1412: pr ("\n"); 1413: if (clreol) 1414: cleareol (); 1415: pr ("\n"); 1416: --fnum; 1417: } 1418: 1419: /*----------------------------- Terminal I/O -------------------------------*/ 1420: 1421: initterm () 1422: { 1423: char buf[TBUFSIZ]; 1424: static char clearbuf[TBUFSIZ]; 1425: char *clearptr, *padstr; 1426: int ldisc; 1427: int lmode; 1428: char *term; 1429: int tgrp; 1430: struct winsize win; 1431: 1432: retry: 1433: if (!(no_tty = gtty(fileno(stdout), &otty))) { 1434: if (ioctl(fileno(stdout), TIOCLGET, &lmode) < 0) { 1435: perror("TIOCLGET"); 1436: exit(1); 1437: } 1438: docrterase = ((lmode & LCRTERA) != 0); 1439: docrtkill = ((lmode & LCRTKIL) != 0); 1440: /* 1441: * Wait until we're in the foreground before we save the 1442: * the terminal modes. 1443: */ 1444: if (ioctl(fileno(stdout), TIOCGPGRP, &tgrp) < 0) { 1445: perror("TIOCGPGRP"); 1446: exit(1); 1447: } 1448: if (tgrp != getpgrp(0)) { 1449: kill(0, SIGTTOU); 1450: goto retry; 1451: } 1452: if ((term = getenv("TERM")) == 0 || tgetent(buf, term) <= 0) { 1453: dumb++; ul_opt = 0; 1454: } 1455: else { 1456: if (ioctl(fileno(stdout), TIOCGWINSZ, &win) < 0) { 1457: Lpp = tgetnum("li"); 1458: Mcol = tgetnum("co"); 1459: } else { 1460: if ((Lpp = win.ws_row) == 0) 1461: Lpp = tgetnum("li"); 1462: if ((Mcol = win.ws_col) == 0) 1463: Mcol = tgetnum("co"); 1464: } 1465: if ((Lpp <= 0) || tgetflag("hc")) { 1466: hard++; /* Hard copy terminal */ 1467: Lpp = 24; 1468: } 1469: if (Mcol <= 0) 1470: Mcol = 80; 1471: 1472: if (tailequ (fnames[0], "page") || !hard && tgetflag("ns")) 1473: noscroll++; 1474: Wrap = tgetflag("am"); 1475: bad_so = tgetflag ("xs"); 1476: clearptr = clearbuf; 1477: eraseln = tgetstr("ce",&clearptr); 1478: Clear = tgetstr("cl", &clearptr); 1479: Senter = tgetstr("so", &clearptr); 1480: Sexit = tgetstr("se", &clearptr); 1481: if ((soglitch = tgetnum("sg")) < 0) 1482: soglitch = 0; 1483: 1484: /* 1485: * Set up for underlining: some terminals don't need it; 1486: * others have start/stop sequences, still others have an 1487: * underline char sequence which is assumed to move the 1488: * cursor forward one character. If underline sequence 1489: * isn't available, settle for standout sequence. 1490: */ 1491: 1492: if (tgetflag("ul") || tgetflag("os")) 1493: ul_opt = 0; 1494: if ((chUL = tgetstr("uc", &clearptr)) == NULL ) 1495: chUL = ""; 1496: if (((ULenter = tgetstr("us", &clearptr)) == NULL || 1497: (ULexit = tgetstr("ue", &clearptr)) == NULL) && !*chUL) { 1498: if ((ULenter = Senter) == NULL || (ULexit = Sexit) == NULL) { 1499: ULenter = ""; 1500: ULexit = ""; 1501: } else 1502: ulglitch = soglitch; 1503: } else { 1504: if ((ulglitch = tgetnum("ug")) < 0) 1505: ulglitch = 0; 1506: } 1507: 1508: if (padstr = tgetstr("pc", &clearptr)) 1509: PC = *padstr; 1510: Home = tgetstr("ho",&clearptr); 1511: if (Home == 0 || *Home == '\0') 1512: { 1513: if ((cursorm = tgetstr("cm", &clearptr)) != NULL) { 1514: strcpy(cursorhome, tgoto(cursorm, 0, 0)); 1515: Home = cursorhome; 1516: } 1517: } 1518: EodClr = tgetstr("cd", &clearptr); 1519: if ((chBS = tgetstr("bc", &clearptr)) == NULL) 1520: chBS = "\b"; 1521: 1522: } 1523: if ((shell = getenv("SHELL")) == NULL) 1524: shell = _PATH_BSHELL; 1525: } 1526: no_intty = gtty(fileno(stdin), &otty); 1527: gtty(fileno(stderr), &otty); 1528: savetty = otty; 1529: ospeed = otty.sg_ospeed; 1530: slow_tty = ospeed < B1200; 1531: hardtabs = !(otty.sg_flags & XTABS); 1532: if (!no_tty) { 1533: otty.sg_flags &= ~ECHO; 1534: if (MBIT == CBREAK || !slow_tty) 1535: otty.sg_flags |= MBIT; 1536: } 1537: } 1538: 1539: readch () 1540: { 1541: char ch; 1542: 1543: if (read (2, &ch, 1) <= 0) 1544: if (errno != EINTR) 1545: exit(0); 1546: else 1547: ch = otty.sg_kill; 1548: return (ch); 1549: } 1550: 1551: static char BS = '\b'; 1552: static char *BSB = "\b \b"; 1553: static char CARAT = '^'; 1554: #define ERASEONECHAR \ 1555: if (docrterase) \ 1556: write (2, BSB, sizeof(BSB)); \ 1557: else \ 1558: write (2, &BS, sizeof(BS)); 1559: 1560: ttyin (buf, nmax, pchar) 1561: char buf[]; 1562: register int nmax; 1563: char pchar; 1564: { 1565: register char *sptr; 1566: register char ch; 1567: register int slash = 0; 1568: int maxlen; 1569: char cbuf; 1570: 1571: sptr = buf; 1572: maxlen = 0; 1573: while (sptr - buf < nmax) { 1574: if (promptlen > maxlen) maxlen = promptlen; 1575: ch = readch (); 1576: if (ch == '\\') { 1577: slash++; 1578: } 1579: else if ((ch == otty.sg_erase) && !slash) { 1580: if (sptr > buf) { 1581: --promptlen; 1582: ERASEONECHAR 1583: --sptr; 1584: if ((*sptr < ' ' && *sptr != '\n') || *sptr == RUBOUT) { 1585: --promptlen; 1586: ERASEONECHAR 1587: } 1588: continue; 1589: } 1590: else { 1591: if (!eraseln) promptlen = maxlen; 1592: longjmp (restore, 1); 1593: } 1594: } 1595: else if ((ch == otty.sg_kill) && !slash) { 1596: if (hard) { 1597: show (ch); 1598: putchar ('\n'); 1599: putchar (pchar); 1600: } 1601: else { 1602: putchar ('\r'); 1603: putchar (pchar); 1604: if (eraseln) 1605: erase (1); 1606: else if (docrtkill) 1607: while (promptlen-- > 1) 1608: write (2, BSB, sizeof(BSB)); 1609: promptlen = 1; 1610: } 1611: sptr = buf; 1612: fflush (stdout); 1613: continue; 1614: } 1615: if (slash && (ch == otty.sg_kill || ch == otty.sg_erase)) { 1616: ERASEONECHAR 1617: --sptr; 1618: } 1619: if (ch != '\\') 1620: slash = 0; 1621: *sptr++ = ch; 1622: if ((ch < ' ' && ch != '\n' && ch != ESC) || ch == RUBOUT) { 1623: ch += ch == RUBOUT ? -0100 : 0100; 1624: write (2, &CARAT, 1); 1625: promptlen++; 1626: } 1627: cbuf = ch; 1628: if (ch != '\n' && ch != ESC) { 1629: write (2, &cbuf, 1); 1630: promptlen++; 1631: } 1632: else 1633: break; 1634: } 1635: *--sptr = '\0'; 1636: if (!eraseln) promptlen = maxlen; 1637: if (sptr - buf >= nmax - 1) 1638: error ("Line too long"); 1639: } 1640: 1641: expand (outbuf, inbuf) 1642: char *outbuf; 1643: char *inbuf; 1644: { 1645: register char *instr; 1646: register char *outstr; 1647: register char ch; 1648: char temp[200]; 1649: int changed = 0; 1650: 1651: instr = inbuf; 1652: outstr = temp; 1653: while ((ch = *instr++) != '\0') 1654: switch (ch) { 1655: case '%': 1656: if (!no_intty) { 1657: strcpy (outstr, fnames[fnum]); 1658: outstr += strlen (fnames[fnum]); 1659: changed++; 1660: } 1661: else 1662: *outstr++ = ch; 1663: break; 1664: case '!': 1665: if (!shellp) 1666: error ("No previous command to substitute for"); 1667: strcpy (outstr, shell_line); 1668: outstr += strlen (shell_line); 1669: changed++; 1670: break; 1671: case '\\': 1672: if (*instr == '%' || *instr == '!') { 1673: *outstr++ = *instr++; 1674: break; 1675: } 1676: default: 1677: *outstr++ = ch; 1678: } 1679: *outstr++ = '\0'; 1680: strcpy (outbuf, temp); 1681: return (changed); 1682: } 1683: 1684: show (ch) 1685: register char ch; 1686: { 1687: char cbuf; 1688: 1689: if ((ch < ' ' && ch != '\n' && ch != ESC) || ch == RUBOUT) { 1690: ch += ch == RUBOUT ? -0100 : 0100; 1691: write (2, &CARAT, 1); 1692: promptlen++; 1693: } 1694: cbuf = ch; 1695: write (2, &cbuf, 1); 1696: promptlen++; 1697: } 1698: 1699: error (mess) 1700: char *mess; 1701: { 1702: if (clreol) 1703: cleareol (); 1704: else 1705: kill_line (); 1706: promptlen += strlen (mess); 1707: if (Senter && Sexit) { 1708: tputs (Senter, 1, putch); 1709: pr(mess); 1710: tputs (Sexit, 1, putch); 1711: } 1712: else 1713: pr (mess); 1714: fflush(stdout); 1715: errors++; 1716: longjmp (restore, 1); 1717: } 1718: 1719: 1720: set_tty () 1721: { 1722: otty.sg_flags |= MBIT; 1723: otty.sg_flags &= ~ECHO; 1724: stty(fileno(stderr), &otty); 1725: } 1726: 1727: reset_tty () 1728: { 1729: if (pstate) { 1730: tputs(ULexit, 1, putch); 1731: fflush(stdout); 1732: pstate = 0; 1733: } 1734: otty.sg_flags |= ECHO; 1735: otty.sg_flags &= ~MBIT; 1736: stty(fileno(stderr), &savetty); 1737: } 1738: 1739: rdline (f) 1740: register FILE *f; 1741: { 1742: register char c; 1743: register char *p; 1744: 1745: p = Line; 1746: while ((c = Getc (f)) != '\n' && c != EOF && p - Line < LINSIZ - 1) 1747: *p++ = c; 1748: if (c == '\n') 1749: Currline++; 1750: *p = '\0'; 1751: } 1752: 1753: /* Come here when we get a suspend signal from the terminal */ 1754: 1755: onsusp () 1756: { 1757: /* ignore SIGTTOU so we don't get stopped if csh grabs the tty */ 1758: signal(SIGTTOU, SIG_IGN); 1759: reset_tty (); 1760: fflush (stdout); 1761: signal(SIGTTOU, SIG_DFL); 1762: /* Send the TSTP signal to suspend our process group */ 1763: signal(SIGTSTP, SIG_DFL); 1764: sigsetmask(0L); 1765: kill (0, SIGTSTP); 1766: /* Pause for station break */ 1767: 1768: /* We're back */ 1769: signal (SIGTSTP, onsusp); 1770: set_tty (); 1771: if (inwait) 1772: longjmp (restore); 1773: }