|
|
1.1 ! root 1: #include <whoami.h> ! 2: #include <stdio.h> ! 3: #include <signal.h> ! 4: #include <sgtty.h> ! 5: #include <setjmp.h> ! 6: #include <sys/types.h> ! 7: #include <sys/dir.h> ! 8: #include <sys/stat.h> ! 9: ! 10: #ifdef CORY ! 11: #define MBIT RAW ! 12: #else ! 13: #include <ctype.h> ! 14: #define MBIT CBREAK ! 15: #endif ! 16: ! 17: #define TBUFSIZ 1024 ! 18: #define LINSIZ 256 ! 19: #define ctrl(letter) ('letter' & 077) ! 20: #define RUBOUT '\177' ! 21: #define ESC '\033' ! 22: #define QUIT '\034' ! 23: ! 24: struct sgttyb otty; ! 25: int fnum, no_intty, no_tty, slow_tty; ! 26: int dum_opt, dlines, onquit(), end_it(); ! 27: int stop_opt = 1; ! 28: int promptlen; ! 29: int startup = 1; ! 30: int firstf = 1; ! 31: int notell = 1; ! 32: int inwait, pause, errors; ! 33: int within; /* true if we are within a file, ! 34: false if we are between files */ ! 35: int hard, dumb, noscroll, hardtabs; ! 36: char **fnames; ! 37: int nfiles; ! 38: char *shell; ! 39: char ch; ! 40: jmp_buf restore; ! 41: char obuf[BUFSIZ]; /* stdout buffer */ ! 42: char Line[LINSIZ]; ! 43: int Lpp = 24; /* lines per page */ ! 44: char *Clear; /* clear screen */ ! 45: char *eraseln; /* erase line */ ! 46: char *Senter, *Sexit;/* enter and exit standout mode */ ! 47: char *tgetstr(); ! 48: int Mcol = 80; /* number of columns */ ! 49: int Wrap = 1; /* set if automargins */ ! 50: extern char PC; /* pad character */ ! 51: extern short ospeed; ! 52: ! 53: ! 54: main(argc, argv) ! 55: int argc; ! 56: char *argv[]; ! 57: { ! 58: register FILE *f; ! 59: register char *s; ! 60: register char *p; ! 61: register char ch; ! 62: register int left; ! 63: int prnames = 0; ! 64: int initopt = 0; ! 65: int srchopt = 0; ! 66: int initline; ! 67: char buf[TBUFSIZ]; ! 68: char clearbuf[100]; ! 69: char initbuf[80]; ! 70: char *clearptr; ! 71: char *getenv(); ! 72: FILE *checkf(); ! 73: ! 74: nfiles = argc; ! 75: fnames = argv; ! 76: /* Put terminal setup stuff in separate procedure ?? (From here...) */ ! 77: setbuf(stdout, obuf); ! 78: if (!(no_tty = gtty(1, &otty))) { ! 79: if (tgetent(buf, getenv("TERM")) <= 0) { ! 80: dumb++; ! 81: } ! 82: else { ! 83: if (((Lpp = tgetnum("li")) < 0) || tgetflag("hc")) { ! 84: hard++; /* Hard copy terminal */ ! 85: Lpp = 24; ! 86: } ! 87: if (!hard && tgetflag("ns")) ! 88: noscroll++; ! 89: if ((Mcol = tgetnum("co")) < 0) ! 90: Mcol = 80; ! 91: Wrap = tgetflag("am"); ! 92: clearptr = clearbuf; ! 93: eraseln = tgetstr("ce",&clearptr); ! 94: Clear = tgetstr("cl", &clearptr); ! 95: Senter = tgetstr("so", &clearptr); ! 96: Sexit = tgetstr("se", &clearptr); ! 97: PC = *tgetstr("pc", &clearptr); ! 98: } ! 99: if ((shell = getenv("SHELL")) == NULL) ! 100: shell = "/bin/sh"; ! 101: } ! 102: no_intty = gtty(0, &otty); ! 103: gtty(2, &otty); ! 104: ospeed = otty.sg_ospeed; ! 105: slow_tty = ospeed < B1200; ! 106: hardtabs = !(otty.sg_flags & XTABS); ! 107: if (!no_tty) { ! 108: otty.sg_flags &= ~ECHO; ! 109: if (MBIT == CBREAK || !slow_tty) ! 110: otty.sg_flags |= MBIT; ! 111: } ! 112: /* ... until here or so */ ! 113: while (--nfiles > 0) { ! 114: if ((ch = (*++fnames)[0]) == '-') { ! 115: for (s = fnames[0] + 1, dlines = 0; *s != '\0'; s++) ! 116: if (isdigit(*s)) ! 117: dlines = dlines*10 + *s - '0'; ! 118: else if (*s == 'd') ! 119: dum_opt = 1; ! 120: else if (*s == 'l') ! 121: stop_opt = 0; ! 122: } ! 123: else if (ch == '+') { ! 124: s = *fnames; ! 125: if (*++s == '/') { ! 126: srchopt++; ! 127: for (++s, p = initbuf; p < initbuf + 79 && *s != '\0';) ! 128: *p++ = *s++; ! 129: *p = '\0'; ! 130: } ! 131: else { ! 132: initopt++; ! 133: for (initline = 0; *s != '\0'; s++) ! 134: if (isdigit (*s)) ! 135: initline = initline*10 + *s -'0'; ! 136: --initline; ! 137: } ! 138: } ! 139: else break; ! 140: } ! 141: if (dlines == 0) ! 142: dlines = Lpp - 2; ! 143: left = dlines; ! 144: if (nfiles > 1) ! 145: prnames++; ! 146: if (!no_intty && nfiles == 0) { ! 147: fputs("Usage: ",stderr); ! 148: fputs(argv[0],stderr); ! 149: fputs(" [-dn] name1 name2 ...\n",stderr); ! 150: exit(1); ! 151: } ! 152: else ! 153: f = stdin; ! 154: if (!no_tty) { ! 155: signal(SIGQUIT, onquit); ! 156: signal(SIGINT, end_it); ! 157: stty (2, &otty); ! 158: } ! 159: if (no_intty) { ! 160: if (no_tty) ! 161: copy_file (stdin); ! 162: else { ! 163: if (srchopt) ! 164: search (initbuf, stdin, 1); ! 165: else if (initopt) ! 166: skiplns (initline, stdin); ! 167: screen (stdin, left); ! 168: } ! 169: end_it (); ! 170: } ! 171: ! 172: while (fnum < nfiles) { ! 173: if ((f = checkf (fnames[fnum])) != NULL) { ! 174: if (firstf) setjmp (restore); ! 175: if (firstf) { ! 176: firstf = 0; ! 177: if (srchopt) ! 178: search (initbuf, f, 1); ! 179: else if (initopt) ! 180: skiplns (initline, f); ! 181: } ! 182: else if (fnum < nfiles && !no_tty) { ! 183: setjmp (restore); ! 184: left = command (fnames[fnum], f); ! 185: } ! 186: if (left != 0) { ! 187: if (prnames) { ! 188: pr("::::::::::::::"); ! 189: if (promptlen > 14) ! 190: erase (14); ! 191: putchar ('\n'); ! 192: pr(fnames[fnum]); ! 193: pr("\n::::::::::::::\n"); ! 194: if (left > Lpp - 4) ! 195: left = Lpp - 4; ! 196: } ! 197: if (no_tty) ! 198: copy_file (f); ! 199: else { ! 200: within++; ! 201: screen(f, left); ! 202: within = 0; ! 203: } ! 204: } ! 205: setjmp (restore); ! 206: fflush(stdout); ! 207: fclose(f); ! 208: } ! 209: fnum++; ! 210: firstf = 0; ! 211: } ! 212: otty.sg_flags |= ECHO; ! 213: otty.sg_flags &= ~MBIT; ! 214: stty(2, &otty); ! 215: exit(0); ! 216: } ! 217: ! 218: /* ! 219: ** Check whether the file named by fs is an ASCII file which the user may ! 220: ** access. If it is, return the opened file. Otherwise return NULL. ! 221: */ ! 222: ! 223: FILE * ! 224: checkf (fs) ! 225: register char *fs; ! 226: { ! 227: #ifdef CORY ! 228: int space[3]; /* Why doesn't libretro have a V7 stat? */ ! 229: #endif ! 230: struct stat stbuf; ! 231: register FILE *f; ! 232: char c; ! 233: ! 234: if (stat (fs, &stbuf) == -1) { ! 235: fflush(stdout); ! 236: perror(fs); ! 237: return (NULL); ! 238: } ! 239: if (stbuf.st_mode & S_IFDIR) { ! 240: pr("\n*** "); ! 241: pr(fs); ! 242: pr(": directory ***\n\n"); ! 243: return (NULL); ! 244: } ! 245: if ((f=fopen(fs, "r")) == NULL) { ! 246: fflush(stdout); ! 247: perror(fs); ! 248: return (NULL); ! 249: } ! 250: c = getc(f); ! 251: ! 252: /* Try to see whether it is an ASCII file */ ! 253: ! 254: switch ((c | *f->_ptr << 8) & 0177777) { ! 255: case 0405: ! 256: case 0407: ! 257: case 0410: ! 258: case 0411: ! 259: case 0177545: ! 260: pr("\n******** "); ! 261: pr(fs); ! 262: pr(": Not a text file ********\n\n"); ! 263: fclose (f); ! 264: return (NULL); ! 265: default: ! 266: break; ! 267: } ! 268: if (c == '\f') { ! 269: c = 0; ! 270: doclear (); ! 271: } ! 272: ungetc (c, f); ! 273: return (f); ! 274: } ! 275: ! 276: /* ! 277: ** A real function, for the tputs routine in termlib ! 278: */ ! 279: ! 280: putch (ch) ! 281: register char ch; ! 282: { ! 283: putchar (ch); ! 284: } ! 285: ! 286: /* ! 287: ** Print out the contents of the file f, one screenful at a time. ! 288: */ ! 289: ! 290: #define STOP -10 ! 291: ! 292: screen (f, num_lines) ! 293: register FILE *f; ! 294: register int num_lines; ! 295: { ! 296: register int c; ! 297: int nchars; ! 298: ! 299: for (;;) { ! 300: while (num_lines > 0 && !pause) { ! 301: if ((nchars = getline (f)) == EOF) ! 302: return; ! 303: if (Senter && *Senter == ' ' && promptlen > 0) ! 304: erase (0); ! 305: pr (Line); ! 306: if (nchars < promptlen) ! 307: erase (nchars); /* erase () sets promptlen to 0 */ ! 308: else promptlen = 0; ! 309: if (nchars < Mcol) ! 310: putchar('\n'); ! 311: if (nchars == STOP) ! 312: break; ! 313: num_lines--; ! 314: } ! 315: fflush(stdout); ! 316: if ((c = getc(f)) == EOF) { ! 317: if (noscroll) ! 318: doclear(); ! 319: else ! 320: erase (0); ! 321: return; ! 322: } ! 323: ungetc (c, f); ! 324: setjmp (restore); ! 325: pause = 0; startup = 0; ! 326: if ((num_lines = command (NULL, f)) == 0) ! 327: return; ! 328: } ! 329: } ! 330: ! 331: /* ! 332: ** Come here if a quit signal is received ! 333: */ ! 334: ! 335: onquit() ! 336: { ! 337: signal(SIGQUIT, SIG_IGN); ! 338: if (!inwait) { ! 339: putchar ('\n'); ! 340: if (!startup) { ! 341: signal(SIGQUIT, onquit); ! 342: longjmp (restore, 1); ! 343: } ! 344: else ! 345: pause++; ! 346: } ! 347: else if (!dum_opt && notell) { ! 348: write (2, "[Use q or Q to quit]", 20); ! 349: promptlen += 20; ! 350: notell = 0; ! 351: } ! 352: signal(SIGQUIT, onquit); ! 353: } ! 354: ! 355: /* ! 356: ** Clean up terminal state and exit. Also come here if interrupt signal received ! 357: */ ! 358: ! 359: end_it () ! 360: { ! 361: ! 362: otty.sg_flags &= ~MBIT; ! 363: otty.sg_flags |= ECHO; ! 364: stty(2, &otty); ! 365: if (promptlen > 0) ! 366: kill_line (); ! 367: else ! 368: putchar ('\n'); ! 369: exit(0); ! 370: } ! 371: ! 372: copy_file(f) ! 373: register FILE *f; ! 374: { ! 375: register int c; ! 376: ! 377: while ((c = getc(f)) != EOF) ! 378: putchar(c); ! 379: } ! 380: ! 381: ! 382: printd (n) ! 383: register int n; ! 384: { ! 385: register int a; ! 386: ! 387: if (a = n/10) ! 388: printd(a); ! 389: putchar(n % 10 + '0'); ! 390: } ! 391: ! 392: static char bell = ctrl(G); ! 393: ! 394: strlen (s) ! 395: char *s; ! 396: { ! 397: register char *p; ! 398: ! 399: p = s; ! 400: while (*p++) ! 401: ; ! 402: return (p - s - 1); ! 403: } ! 404: ! 405: prompt (filename) ! 406: char *filename; ! 407: { ! 408: if (promptlen > 0) ! 409: kill_line (); ! 410: if (!hard) { ! 411: promptlen = 8; ! 412: if (Senter && Sexit) ! 413: tputs (Senter, 1, putch); ! 414: pr("--More--"); ! 415: if (filename != NULL) { ! 416: pr("(Next file: "); ! 417: pr(filename); ! 418: putchar(')'); ! 419: promptlen += 13 + strlen(filename); ! 420: } ! 421: if (dum_opt) { ! 422: pr("[Hit space to continue, Rubout to abort]"); ! 423: promptlen += 40; ! 424: } ! 425: if (Senter && Sexit) ! 426: tputs (Sexit, 1, putch); ! 427: fflush(stdout); ! 428: } ! 429: else ! 430: write (2, &bell, 1); ! 431: inwait++; ! 432: } ! 433: ! 434: /* ! 435: ** Get a logical line ! 436: */ ! 437: ! 438: getline(f) ! 439: register FILE *f; ! 440: { ! 441: register char c; ! 442: register char *p; ! 443: register int column; ! 444: register int i; ! 445: static int colflg; ! 446: ! 447: p = Line; ! 448: i = column = 0; ! 449: c = getc (f); ! 450: if (colflg && c == '\n') c = getc (f); ! 451: for (i = 1; i < LINSIZ; i++) { ! 452: if (c == EOF) { ! 453: if (p > Line) { ! 454: *p = '\0'; ! 455: return (column); ! 456: } ! 457: return (EOF); ! 458: } ! 459: if (c == '\n') ! 460: break; ! 461: *p++ = c; ! 462: if (c == '\t') ! 463: if (hardtabs && column < promptlen && !hard) { ! 464: if (eraseln && !dumb) { ! 465: tputs (eraseln, 1, putch); ! 466: promptlen = 0; ! 467: } ! 468: else { ! 469: for (--p; column & 7; column++) ! 470: *p++ = ' '; ! 471: if (column >= promptlen) promptlen = 0; ! 472: } ! 473: } ! 474: else ! 475: column = 1 + (column | 7); ! 476: else if (c == '\b') ! 477: column--; ! 478: else if (c == '\r') ! 479: column = 0; ! 480: else if (c == '\f' && stop_opt) { ! 481: p[-1] = '^'; ! 482: *p++ = 'L'; ! 483: break; ! 484: } ! 485: else if (c == EOF) ! 486: return (column); ! 487: else if (c >= ' ') ! 488: column++; ! 489: if (column >= Mcol) break; ! 490: c = getc (f); ! 491: } ! 492: if (Mcol > 0 && column >= Mcol) { ! 493: if (!Wrap) { ! 494: *p++ = '\n'; ! 495: i++; ! 496: } ! 497: } ! 498: colflg = (column == Mcol) || c == '\f'; ! 499: *p = 0; ! 500: if (c == '\f' && stop_opt) ! 501: return (STOP); ! 502: return (column); ! 503: } ! 504: ! 505: /* ! 506: ** Erase the rest of the prompt, assuming we are starting column col. ! 507: */ ! 508: ! 509: erase (col) ! 510: register int col; ! 511: { ! 512: ! 513: if (hard || promptlen == 0) ! 514: return; ! 515: if (col == 0) ! 516: putchar ('\r'); ! 517: if (!dumb && eraseln) ! 518: tputs (eraseln, 1, putch); ! 519: else ! 520: for (col = promptlen - col; col > 0; col--) ! 521: putchar (' '); ! 522: promptlen = 0; ! 523: } ! 524: ! 525: /* ! 526: ** Erase the current line entirely ! 527: */ ! 528: ! 529: kill_line () ! 530: { ! 531: erase (0); ! 532: if (!eraseln || dumb) putchar ('\r'); ! 533: } ! 534: ! 535: /* ! 536: ** Print string ! 537: */ ! 538: ! 539: pr(s1) ! 540: char *s1; ! 541: { ! 542: register char *s; ! 543: register char c; ! 544: ! 545: for (s = s1; c = *s++; ) ! 546: putchar(c); ! 547: } ! 548: ! 549: /* ! 550: ** Clear the screen ! 551: */ ! 552: ! 553: doclear() ! 554: { ! 555: if (Clear && Lpp > 0) ! 556: tputs(Clear, 1, putch); ! 557: } ! 558: ! 559: ! 560: /* ! 561: ** Read a command and do it. A command consists of an optional integer ! 562: ** argument followed by the command character. Return the number of lines ! 563: ** to display in the next screenful. If there is nothing more to display ! 564: ** in the current file, zero is returned. ! 565: */ ! 566: ! 567: command (filename, f) ! 568: char *filename; ! 569: register FILE *f; ! 570: { ! 571: register int nlines; ! 572: register int retval; ! 573: register char c; ! 574: int id, done; ! 575: char comchar, cmdbuf[80], *p; ! 576: ! 577: #define ret(val) retval=val;done++;break ! 578: ! 579: done = 0; ! 580: if (!errors) ! 581: prompt (filename); ! 582: else ! 583: errors = 0; ! 584: if (MBIT == RAW && slow_tty) { ! 585: otty.sg_flags |= MBIT; ! 586: stty(2, &otty); ! 587: } ! 588: for (;;) { ! 589: nlines = number (&comchar); ! 590: switch (comchar) { ! 591: case ' ': ! 592: case 'z': ! 593: if (nlines == 0) nlines = dlines; ! 594: else if (comchar == 'z') dlines = nlines; ! 595: ret (nlines); ! 596: case 'd': ! 597: case ctrl(D): ! 598: ret (11); ! 599: case RUBOUT: ! 600: case 'q': ! 601: case 'Q': ! 602: end_it (); ! 603: case 's': ! 604: case 'f': ! 605: if (nlines == 0) nlines++; ! 606: if (comchar == 'f') ! 607: nlines *= dlines; ! 608: putchar ('\r'); ! 609: erase (0); ! 610: pr("\n...skipping "); ! 611: printd(nlines); ! 612: pr(" line"); ! 613: if (nlines > 1) ! 614: pr("s\n\n"); ! 615: else ! 616: pr("\n\n"); ! 617: while (nlines > 0) { ! 618: while ((c = getc (f)) != '\n') ! 619: if (c == EOF) { ! 620: retval = 0; ! 621: done++; ! 622: goto endsw; ! 623: } ! 624: nlines--; ! 625: } ! 626: ret (dlines); ! 627: break; ! 628: case '\n': ! 629: ret (1); ! 630: case 'n': ! 631: if (nlines == 0) ! 632: nlines++; ! 633: putchar ('\r'); ! 634: erase (0); ! 635: skipf (nlines); ! 636: ret (0); ! 637: case 'p': ! 638: if (no_intty) { ! 639: write (2, &bell, 1); ! 640: break; ! 641: } ! 642: putchar ('\r'); ! 643: erase (0); ! 644: if (nlines == 0) ! 645: nlines++; ! 646: skipf (-nlines); ! 647: ret (0); ! 648: case '/': ! 649: kill_line (); ! 650: pr ("/"); ! 651: promptlen = 1; ! 652: fflush (stdout); ! 653: ttyin (cmdbuf, 78, '/'); ! 654: if (nlines == 0) nlines++; ! 655: write (2, "\r", 1); ! 656: search (cmdbuf, f, nlines); ! 657: ret (dlines); ! 658: case '!': ! 659: kill_line (); ! 660: pr ("!"); ! 661: promptlen = 1; ! 662: fflush (stdout); ! 663: ttyin (cmdbuf, 78, '!'); ! 664: write (2, "\n", 1); ! 665: promptlen = 0; ! 666: otty.sg_flags |= ECHO; ! 667: otty.sg_flags &= ~MBIT; ! 668: stty(2, &otty); ! 669: while ((id = fork ()) < 0) ! 670: ; ! 671: if (id == 0) { ! 672: execl (shell, shell, "-c", cmdbuf, 0); ! 673: write (2, "exec failed\n", 12); ! 674: exit (1); ! 675: } ! 676: signal (SIGINT, SIG_IGN); ! 677: signal (SIGQUIT, SIG_IGN); ! 678: wait (0); ! 679: signal (SIGINT, end_it); ! 680: signal (SIGQUIT, onquit); ! 681: otty.sg_flags |= MBIT; ! 682: otty.sg_flags &= ~ECHO; ! 683: stty(2, &otty); ! 684: pr ("----------\n(continue)\n"); ! 685: fflush (stdout); ! 686: break; ! 687: default: ! 688: write (2, &bell, 1); ! 689: break; ! 690: } ! 691: if (done) break; ! 692: } ! 693: putchar ('\r'); ! 694: endsw: ! 695: inwait = 0; ! 696: notell++; ! 697: if (MBIT == RAW && slow_tty) { ! 698: otty.sg_flags &= ~MBIT; ! 699: stty(2, &otty); ! 700: } ! 701: return (retval); ! 702: } ! 703: ! 704: char ch; ! 705: ! 706: /* ! 707: ** Read a decimal number from the terminal. Set cmd to the non-digit which ! 708: ** terminates the number. ! 709: */ ! 710: ! 711: number(cmd) ! 712: char *cmd; ! 713: { ! 714: register int i; ! 715: ! 716: i = 0; ch = otty.sg_kill; ! 717: for (;;) { ! 718: read (2, &ch, 1); ! 719: if (ch >= '0' && ch <= '9') ! 720: i = i*10 + ch - '0'; ! 721: else if (ch == otty.sg_kill) ! 722: i = 0; ! 723: else { ! 724: *cmd = ch; ! 725: break; ! 726: } ! 727: } ! 728: return (i); ! 729: } ! 730: ! 731: /* ! 732: ** Skip n lines in the file f ! 733: */ ! 734: ! 735: skiplns (n, f) ! 736: register int n; ! 737: register FILE *f; ! 738: { ! 739: register char c; ! 740: ! 741: while (n > 0) { ! 742: while ((c = getc (f)) != '\n') ! 743: if (c == EOF) ! 744: return; ! 745: n--; ! 746: } ! 747: } ! 748: ! 749: /* ! 750: ** Skip nskip files in the file list (from the command line). Nskip may be ! 751: ** negative. ! 752: */ ! 753: ! 754: skipf (nskip) ! 755: register int nskip; ! 756: { ! 757: if (nskip == 0) return; ! 758: if (nskip > 0) { ! 759: if (fnum > nfiles - 1) ! 760: end_it (); ! 761: } ! 762: else if (within) ! 763: ++fnum; ! 764: fnum += nskip; ! 765: if (fnum < 0) ! 766: fnum = 0; ! 767: else if (fnum > nfiles - 1) ! 768: fnum = nfiles -1; ! 769: pr ("\n...Skipping "); ! 770: pr (nskip > 0 ? "to file " : "back to file "); ! 771: pr (fnames[fnum]); ! 772: pr ("\n\n"); ! 773: --fnum; ! 774: } ! 775: ! 776: readch () ! 777: { ! 778: char ch; ! 779: ! 780: read (2, &ch, 1); ! 781: return (ch); ! 782: } ! 783: ! 784: static char BS = '\b'; ! 785: static char CARAT = '^'; ! 786: ! 787: ttyin (buf, nmax, pchar) ! 788: char buf[]; ! 789: register int nmax; ! 790: char pchar; ! 791: { ! 792: register char *sptr; ! 793: register char ch; ! 794: register int slash = 0; ! 795: int maxlen; ! 796: char cbuf; ! 797: ! 798: sptr = buf; ! 799: maxlen = 0; ! 800: while (sptr - buf < nmax) { ! 801: if (promptlen > maxlen) maxlen = promptlen; ! 802: ch = readch (); ! 803: if (ch == '\\') { ! 804: slash++; ! 805: } ! 806: else if (ch == otty.sg_erase && !slash) { ! 807: if (sptr > buf) { ! 808: --promptlen; ! 809: write (2, &BS, 1); ! 810: --sptr; ! 811: if (*sptr < ' ' && *sptr != '\n') { ! 812: --promptlen; ! 813: write (2, &BS, 1); ! 814: } ! 815: continue; ! 816: } ! 817: else { ! 818: if (!eraseln) promptlen = maxlen; ! 819: longjmp (restore, 1); ! 820: } ! 821: } ! 822: else if (ch == otty.sg_kill && !slash) { ! 823: if (hard) ! 824: pr (" XXX\n"); ! 825: else { ! 826: putchar ('\r'); ! 827: putchar (pchar); ! 828: if (eraseln) ! 829: erase (1); ! 830: promptlen = 1; ! 831: sptr = buf; ! 832: } ! 833: fflush (stdout); ! 834: continue; ! 835: } ! 836: if (slash && (ch == otty.sg_kill || ch == otty.sg_erase)) { ! 837: write (2, &BS, 1); ! 838: --sptr; ! 839: } ! 840: if (ch != '\\') ! 841: slash = 0; ! 842: *sptr++ = ch; ! 843: if (ch < ' ' && ch != '\n' && ch != ESC) { ! 844: ch += 0100; ! 845: write (2, &CARAT, 1); ! 846: promptlen++; ! 847: } ! 848: cbuf = ch; ! 849: if (ch != '\n' && ch != ESC) { ! 850: write (2, &cbuf, 1); ! 851: promptlen++; ! 852: } ! 853: else break; ! 854: } ! 855: *--sptr = '\0'; ! 856: if (!eraseln) promptlen = maxlen; ! 857: if (sptr - buf >= nmax - 1) ! 858: error ("Line too long"); ! 859: } ! 860: ! 861: /* ! 862: ** Search for nth ocurrence of regular expression contained in buf in the file ! 863: */ ! 864: ! 865: search (buf, file, n) ! 866: char buf[]; ! 867: FILE *file; ! 868: register int n; ! 869: { ! 870: long startline = ftell (file); ! 871: register long line1 = startline; ! 872: register long line2 = startline; ! 873: register long line3 = startline; ! 874: register int lncount; ! 875: ! 876: lncount = 0; ! 877: compile (buf); ! 878: while (!feof (file)) { ! 879: line3 = line2; ! 880: line2 = line1; ! 881: line1 = ftell (file); ! 882: rdline (file); ! 883: lncount++; ! 884: if (execute (Line)) ! 885: if (--n == 0) { ! 886: if (lncount > 3 || (lncount > 1 && no_intty)) ! 887: pr ("\n...skipping\n"); ! 888: if (!no_intty) ! 889: fseek (file, line3, 0); ! 890: else { ! 891: kill_line (); ! 892: pr (Line); ! 893: putchar ('\n'); ! 894: } ! 895: break; ! 896: } ! 897: } ! 898: if (feof (file)) { ! 899: if (!no_intty) { ! 900: #ifdef CORY ! 901: file->_flag &= ~_IOEOF; /* why doesn't fseek do this ??!!??! */ ! 902: #endif ! 903: fseek (file, startline, 0); ! 904: } ! 905: else { ! 906: pr ("\nPattern not found\n"); ! 907: end_it (); ! 908: } ! 909: error ("Pattern not found"); ! 910: } ! 911: } ! 912: ! 913: /* ! 914: * The following are adapted from the editor ! 915: */ ! 916: ! 917: /* ! 918: * Internal form of regular expressions. ! 919: */ ! 920: #define CBRA 1 /* left \( bracket */ ! 921: #define CCHR 2 /* a particular character */ ! 922: #define CDOT 4 /* any char (.) */ ! 923: #define CCL 6 /* begin class ([) */ ! 924: #define NCCL 8 /* begin not class ([^) */ ! 925: #define CDOL 10 /* end of line ($) */ ! 926: #define CEOF 11 /* end of pattern */ ! 927: #define CKET 12 /* right \) bracket */ ! 928: #define CBACK 14 /* repeat previous match (\1, etc on lhs) */ ! 929: ! 930: #define STAR 01 /* or'ed with some symbols to indicate * suffix */ ! 931: ! 932: #define NBRA 5 /* max # of \( \) pairs */ ! 933: ! 934: char expbuf[BUFSIZ]; ! 935: char *braslist[NBRA]; ! 936: char *braelist[NBRA]; ! 937: int nbra; ! 938: int circfl; ! 939: char *loc1; ! 940: char *loc2; ! 941: char *locs; ! 942: ! 943: ! 944: /* ! 945: * compile: convert typed in regular expression into internal form. ! 946: * eof is the char that delimits the r.e. ! 947: * General structure of compiled r.e. in expbuf: A sequence of codes ! 948: * from #defines above (CCHR, CDOT, etc). Some of these take arguments ! 949: * which follow in line (e.g. CCHR is followed by the particular character ! 950: * it is required to match.) CEOF terminates the r.e. ! 951: */ ! 952: compile(inbuf) ! 953: char inbuf[]; ! 954: { ! 955: register char c; ! 956: register char *ep; ! 957: register char *bp = inbuf; ! 958: char *lastep; ! 959: char bracket[NBRA], *bracketp; ! 960: int cclcnt; ! 961: ! 962: /* comerr: compilation error. Don't leave half baked r.e. around. */ ! 963: #define comerr(msg) {expbuf[0] = 0; nbra = 0; error(msg); } ! 964: ep = expbuf; ! 965: bracketp = bracket; ! 966: if ((c = *bp++) == '\0') { ! 967: /* null r.e.: just re-use last r.e., which is still there */ ! 968: if (*ep==0) ! 969: error("No previous regular expression"); ! 970: return; ! 971: } ! 972: nbra = 0; ! 973: /* circfl: true if have ^ (anchored search). */ ! 974: circfl = 0; ! 975: if (c == '^') { ! 976: c = *bp++; ! 977: circfl++; ! 978: } ! 979: lastep = 0; ! 980: --bp; ! 981: for (;;) { /* for each character in the r.e. */ ! 982: if (ep >= &expbuf[BUFSIZ]) ! 983: comerr("r.e. too long"); ! 984: c = *bp++; ! 985: if (c == '\0') { ! 986: /* Hit trailing delim: clean up and quit */ ! 987: if (bracketp != bracket) ! 988: comerr("unmatched \\("); ! 989: *ep++ = CEOF; ! 990: *ep++ = 0; ! 991: return; ! 992: } ! 993: if (c!='*') ! 994: lastep = ep; ! 995: switch (c) { ! 996: ! 997: case '\\': ! 998: if ((c = *bp++)=='(') { ! 999: /* \(: start of subexpression */ ! 1000: if (nbra >= NBRA) ! 1001: comerr("too many \\(\\) pairs"); ! 1002: *bracketp++ = nbra; ! 1003: *ep++ = CBRA; ! 1004: *ep++ = nbra++; ! 1005: continue; ! 1006: } ! 1007: if (c == ')') { ! 1008: /* \): end of sub exp */ ! 1009: if (bracketp <= bracket) ! 1010: comerr("unmatched \\)"); ! 1011: *ep++ = CKET; ! 1012: *ep++ = *--bracketp; ! 1013: continue; ! 1014: } ! 1015: if (c>='1' && c<'1'+NBRA) { ! 1016: /* \1, \2, ...: rematch previous subexp */ ! 1017: *ep++ = CBACK; ! 1018: *ep++ = c-'1'; ! 1019: continue; ! 1020: } ! 1021: /* Otherwise just force that char, not specially */ ! 1022: *ep++ = CCHR; ! 1023: if (c=='\n') ! 1024: /* Newlines can't possibly be in lines */ ! 1025: comerr("multi line r.e. not allowed"); ! 1026: *ep++ = c; ! 1027: continue; ! 1028: ! 1029: case '.': ! 1030: /* .: match any character */ ! 1031: *ep++ = CDOT; ! 1032: continue; ! 1033: ! 1034: case '*': ! 1035: /* *: Repeat last char indefinitely */ ! 1036: if (lastep==0 || *lastep==CBRA || *lastep==CKET) ! 1037: /* Not that smart, so treat * as nonspecial */ ! 1038: goto defchar; ! 1039: *lastep |= STAR; ! 1040: continue; ! 1041: ! 1042: case '$': ! 1043: /* $: match end of line */ ! 1044: if (*bp != '\0') ! 1045: /* $ only special at end of r.e. */ ! 1046: goto defchar; ! 1047: *ep++ = CDOL; ! 1048: continue; ! 1049: ! 1050: case '[': ! 1051: /* ! 1052: * [...]: any of chars enclosed in brackets. ! 1053: * Compiled form: CCL or NCCL, # of possible chars, ! 1054: * then each char. -'s are expanded. ! 1055: */ ! 1056: *ep++ = CCL; ! 1057: *ep++ = 0; ! 1058: cclcnt = 1; ! 1059: if ((c = *bp++) == '^') { ! 1060: /* [^...]: reverse sense of match */ ! 1061: c = *bp++; ! 1062: ep[-2] = NCCL; ! 1063: } ! 1064: do { /* for each char in brackets */ ! 1065: if (c=='\n') ! 1066: comerr("missing ]"); ! 1067: if (c == '-' && ep[-1] != 0) { ! 1068: /* form ...a-z... but [- not special */ ! 1069: if ((c = *bp++) == ']') { ! 1070: /* -] not special either */ ! 1071: *ep++ = '-'; ! 1072: cclcnt++; ! 1073: break; ! 1074: } ! 1075: while (ep[-1]<c) { ! 1076: /* insert all chars between */ ! 1077: *ep = ep[-1]+1; ! 1078: ep++; ! 1079: cclcnt++; ! 1080: if (ep>=&expbuf[BUFSIZ]) ! 1081: comerr("Too long"); ! 1082: } ! 1083: } ! 1084: *ep++ = c; ! 1085: cclcnt++; ! 1086: if (ep >= &expbuf[BUFSIZ]) ! 1087: comerr("Too long"); ! 1088: } while ((c = *bp++) != ']'); ! 1089: lastep[1] = cclcnt; /* backpatch count */ ! 1090: continue; ! 1091: ! 1092: defchar: ! 1093: default: ! 1094: /* ! 1095: * An ordinary char or one treated as ordinary. ! 1096: * Store CCHR followed by that char, rather than ! 1097: * just the char. This causes most r.e.'s to take ! 1098: * up about twice the space you would expect. ! 1099: * On the other hand, it makes r.e.'s beautifully ! 1100: * portable, even though the codes could be real ! 1101: * characters. ! 1102: */ ! 1103: *ep++ = CCHR; ! 1104: *ep++ = c; ! 1105: } ! 1106: } ! 1107: } ! 1108: ! 1109: /* ! 1110: * execute: look for the compiled r.e. on line addr. ! 1111: * gf is 0 if this is the first time on this line, otherwise nonzero. ! 1112: * If not first, start looking at locs, otherwise at beg of linebuf. ! 1113: * loc1 and loc2 are set to the ends of the pattern found, if any. ! 1114: * 1 is returned if successful, otherwise 0. ! 1115: */ ! 1116: execute(lptr) ! 1117: char *lptr; ! 1118: { ! 1119: register char *p1, *p2; ! 1120: register int c; ! 1121: ! 1122: for (c=0; c<NBRA; c++) { ! 1123: braslist[c] = 0; ! 1124: braelist[c] = 0; ! 1125: } ! 1126: p1 = lptr; ! 1127: p2 = expbuf; ! 1128: if (circfl) { ! 1129: /* anchored search (^): just try one advance. */ ! 1130: loc1 = p1; ! 1131: return(advance(p1, p2)); ! 1132: } ! 1133: /* fast check for first character */ ! 1134: if (*p2==CCHR) { ! 1135: c = p2[1]; ! 1136: do { ! 1137: if (*p1!=c) ! 1138: continue; ! 1139: if (advance(p1, p2)) { ! 1140: loc1 = p1; ! 1141: return(1); ! 1142: } ! 1143: } while (*p1++); ! 1144: return(0); ! 1145: } ! 1146: /* regular algorithm, try advance starting at each char position. */ ! 1147: do { ! 1148: if (advance(p1, p2)) { ! 1149: loc1 = p1; ! 1150: return(1); ! 1151: } ! 1152: } while (*p1++); ! 1153: return(0); ! 1154: } ! 1155: ! 1156: /* ! 1157: * advance: does an anchored search for expression starting at ep, ! 1158: * looking in line starting at lp. Returns 1 if matches, else 0. ! 1159: * If found, loc2 is set to end of pattern. ! 1160: */ ! 1161: advance(lp, ep) ! 1162: register char *ep, *lp; ! 1163: { ! 1164: register char *curlp; ! 1165: int i; ! 1166: ! 1167: for (;;) switch (*ep++) { /* for each code in r.e., look at it..*/ ! 1168: ! 1169: case CCHR: ! 1170: if (*ep++ == *lp++) ! 1171: continue; ! 1172: return(0); ! 1173: ! 1174: case CDOT: ! 1175: if (*lp++) ! 1176: continue; ! 1177: return(0); ! 1178: ! 1179: case CDOL: ! 1180: if (*lp==0) ! 1181: continue; ! 1182: return(0); ! 1183: ! 1184: case CEOF: ! 1185: loc2 = lp; ! 1186: return(1); ! 1187: ! 1188: case CCL: ! 1189: if (cclass(ep, *lp++, 1)) { ! 1190: ep += *ep; ! 1191: continue; ! 1192: } ! 1193: return(0); ! 1194: ! 1195: case NCCL: ! 1196: if (cclass(ep, *lp++, 0)) { ! 1197: ep += *ep; ! 1198: continue; ! 1199: } ! 1200: return(0); ! 1201: ! 1202: case CBRA: ! 1203: braslist[*ep++] = lp; ! 1204: continue; ! 1205: ! 1206: case CKET: ! 1207: braelist[*ep++] = lp; ! 1208: continue; ! 1209: ! 1210: case CBACK: ! 1211: if (braelist[i = *ep++]==0) ! 1212: error("bad back reference"); ! 1213: if (backref(i, lp)) { ! 1214: lp += braelist[i] - braslist[i]; ! 1215: continue; ! 1216: } ! 1217: return(0); ! 1218: ! 1219: case CBACK|STAR: ! 1220: if (braelist[i = *ep++] == 0) ! 1221: error("bad back reference"); ! 1222: curlp = lp; ! 1223: while (backref(i, lp)) ! 1224: lp += braelist[i] - braslist[i]; ! 1225: while (lp >= curlp) { ! 1226: if (advance(lp, ep)) ! 1227: return(1); ! 1228: lp -= braelist[i] - braslist[i]; ! 1229: } ! 1230: continue; ! 1231: ! 1232: case CDOT|STAR: ! 1233: curlp = lp; ! 1234: while (*lp++) ! 1235: ; ! 1236: goto star; ! 1237: ! 1238: case CCHR|STAR: ! 1239: curlp = lp; ! 1240: while (*lp++ == *ep) ! 1241: ; ! 1242: ep++; ! 1243: goto star; ! 1244: ! 1245: case CCL|STAR: ! 1246: case NCCL|STAR: ! 1247: curlp = lp; ! 1248: while (cclass(ep, *lp++, ep[-1]==(CCL|STAR))) ! 1249: ; ! 1250: ep += *ep; ! 1251: goto star; ! 1252: ! 1253: star: ! 1254: /* ! 1255: * star: special treatment. We have found as many of them ! 1256: * as there are to find. Maybe this was too many, as dictated ! 1257: * by what follows in the pattern. Try, starting from the ! 1258: * end, to recursively advance after each char found, ! 1259: * and return after first successful advance (thus finding ! 1260: * largest possible string that matches). ! 1261: */ ! 1262: do { ! 1263: lp--; ! 1264: if (lp==locs) ! 1265: break; ! 1266: if (advance(lp, ep)) ! 1267: return(1); ! 1268: } while (lp > curlp); ! 1269: /* star failed at all attempts, so whole pattern fails. */ ! 1270: return(0); ! 1271: ! 1272: default: ! 1273: longjmp (restore, 1); ! 1274: } ! 1275: } ! 1276: ! 1277: /* ! 1278: * backref: checks to see that text starting at lp matches previous ! 1279: * sub-expression #i. Returns 1 if successful, else 0. (Used for \k ! 1280: * on lhs.) ! 1281: */ ! 1282: backref(i, lp) ! 1283: register int i; ! 1284: register char *lp; ! 1285: { ! 1286: register char *bp; ! 1287: ! 1288: bp = braslist[i]; ! 1289: while (*bp++ == *lp++) ! 1290: if (bp >= braelist[i]) ! 1291: return(1); ! 1292: return(0); ! 1293: } ! 1294: ! 1295: /* ! 1296: * cclass: check to see if character c is in class starting at set. ! 1297: * ([...] construction on lhs of r.e.) af is sense of success/failure: ! 1298: * af=1 is normal (success returns 1), af=0 is reversed for [^ (success ! 1299: * returns 0). ! 1300: */ ! 1301: int ! 1302: cclass(set, c, af) ! 1303: register char *set, c; ! 1304: int af; ! 1305: { ! 1306: register n; ! 1307: ! 1308: if (c==0) ! 1309: return(0); ! 1310: n = *set++; ! 1311: while (--n) ! 1312: if (*set++ == c) ! 1313: return(af); ! 1314: return(!af); ! 1315: } ! 1316: ! 1317: error (mess) ! 1318: char *mess; ! 1319: { ! 1320: if (promptlen > 0) ! 1321: if (hard) ! 1322: putchar ('\n'); ! 1323: else ! 1324: kill_line (); ! 1325: promptlen += strlen (mess); ! 1326: if (Senter && Sexit) { ! 1327: tputs (Senter, 1, putch); ! 1328: pr(mess); ! 1329: tputs (Sexit, 1, putch); ! 1330: } ! 1331: else ! 1332: pr (mess); ! 1333: if (hard) ! 1334: putchar ('\n'); ! 1335: fflush(stdout); ! 1336: errors++; ! 1337: longjmp (restore, 1); ! 1338: } ! 1339: ! 1340: rdline (f) ! 1341: register FILE *f; ! 1342: { ! 1343: register char c; ! 1344: register char *p; ! 1345: ! 1346: p = Line; ! 1347: while ((c = getc (f)) != '\n' && c != EOF && p - Line < LINSIZ - 1) ! 1348: *p++ = c; ! 1349: *p = '\0'; ! 1350: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.