|
|
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.