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