xref: /original-bsd/usr.bin/ex/ex_re.c (revision bdd86a84)
1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 static char *sccsid = "@(#)ex_re.c	7.6 (Berkeley) 03/09/87";
9 #endif not lint
10 
11 #include "ex.h"
12 #include "ex_re.h"
13 
14 /*
15  * Global, substitute and regular expressions.
16  * Very similar to ed, with some re extensions and
17  * confirmed substitute.
18  */
19 global(k)
20 	bool k;
21 {
22 	register char *gp;
23 	register int c;
24 	register line *a1;
25 	char globuf[GBSIZE], *Cwas;
26 	int lines = lineDOL();
27 	int oinglobal = inglobal;
28 	char *oglobp = globp;
29 
30 	Cwas = Command;
31 	/*
32 	 * States of inglobal:
33 	 *  0: ordinary - not in a global command.
34 	 *  1: text coming from some buffer, not tty.
35 	 *  2: like 1, but the source of the buffer is a global command.
36 	 * Hence you're only in a global command if inglobal==2. This
37 	 * strange sounding convention is historically derived from
38 	 * everybody simulating a global command.
39 	 */
40 	if (inglobal==2)
41 		error("Global within global@not allowed");
42 	markDOT();
43 	setall();
44 	nonzero();
45 	if (skipend())
46 		error("Global needs re|Missing regular expression for global");
47 	c = ex_getchar();
48 	ignore(compile(c, 1));
49 	savere(scanre);
50 	gp = globuf;
51 	while ((c = ex_getchar()) != '\n') {
52 		switch (c) {
53 
54 		case EOF:
55 			c = '\n';
56 			goto brkwh;
57 
58 		case '\\':
59 			c = ex_getchar();
60 			switch (c) {
61 
62 			case '\\':
63 				ungetchar(c);
64 				break;
65 
66 			case '\n':
67 				break;
68 
69 			default:
70 				*gp++ = '\\';
71 				break;
72 			}
73 			break;
74 		}
75 		*gp++ = c;
76 		if (gp >= &globuf[GBSIZE - 2])
77 			error("Global command too long");
78 	}
79 brkwh:
80 	ungetchar(c);
81 	newline();
82 	*gp++ = c;
83 	*gp++ = 0;
84 	saveall();
85 	inglobal = 2;
86 	for (a1 = one; a1 <= dol; a1++) {
87 		*a1 &= ~01;
88 		if (a1 >= addr1 && a1 <= addr2 && execute(0, a1) == k)
89 			*a1 |= 01;
90 	}
91 #ifdef notdef
92 /*
93  * This code is commented out for now.  The problem is that we don't
94  * fix up the undo area the way we should.  Basically, I think what has
95  * to be done is to copy the undo area down (since we shrunk everything)
96  * and move the various pointers into it down too.  I will do this later
97  * when I have time. (Mark, 10-20-80)
98  */
99 	/*
100 	 * Special case: g/.../d (avoid n^2 algorithm)
101 	 */
102 	if (globuf[0]=='d' && globuf[1]=='\n' && globuf[2]=='\0') {
103 		gdelete();
104 		return;
105 	}
106 #endif
107 	if (inopen)
108 		inopen = -1;
109 	/*
110 	 * Now for each marked line, set dot there and do the commands.
111 	 * Note the n^2 behavior here for lots of lines matching.
112 	 * This is really needed: in some cases you could delete lines,
113 	 * causing a marked line to be moved before a1 and missed if
114 	 * we didn't restart at zero each time.
115 	 */
116 	for (a1 = one; a1 <= dol; a1++) {
117 		if (*a1 & 01) {
118 			*a1 &= ~01;
119 			dot = a1;
120 			globp = globuf;
121 			commands(1, 1);
122 			a1 = zero;
123 		}
124 	}
125 	globp = oglobp;
126 	inglobal = oinglobal;
127 	endline = 1;
128 	Command = Cwas;
129 	netchHAD(lines);
130 	setlastchar(EOF);
131 	if (inopen) {
132 		ungetchar(EOF);
133 		inopen = 1;
134 	}
135 }
136 
137 /*
138  * gdelete: delete inside a global command. Handles the
139  * special case g/r.e./d. All lines to be deleted have
140  * already been marked. Squeeze the remaining lines together.
141  * Note that other cases such as g/r.e./p, g/r.e./s/r.e.2/rhs/,
142  * and g/r.e./.,/r.e.2/d are not treated specially.  There is no
143  * good reason for this except the question: where to you draw the line?
144  */
145 gdelete()
146 {
147 	register line *a1, *a2, *a3;
148 
149 	a3 = dol;
150 	/* find first marked line. can skip all before it */
151 	for (a1=zero; (*a1&01)==0; a1++)
152 		if (a1>=a3)
153 			return;
154 	/* copy down unmarked lines, compacting as we go. */
155 	for (a2=a1+1; a2<=a3;) {
156 		if (*a2&01) {
157 			a2++;		/* line is marked, skip it */
158 			dot = a1;	/* dot left after line deletion */
159 		} else
160 			*a1++ = *a2++;	/* unmarked, copy it */
161 	}
162 	dol = a1-1;
163 	if (dot>dol)
164 		dot = dol;
165 	change();
166 }
167 
168 bool	cflag;
169 int	scount, slines, stotal;
170 
171 substitute(c)
172 	int c;
173 {
174 	register line *addr;
175 	register int n;
176 	int gsubf, hopcount;
177 
178 	gsubf = compsub(c);
179 	if(FIXUNDO)
180 		save12(), undkind = UNDCHANGE;
181 	stotal = 0;
182 	slines = 0;
183 	for (addr = addr1; addr <= addr2; addr++) {
184 		scount = hopcount = 0;
185 		if (dosubcon(0, addr) == 0)
186 			continue;
187 		if (gsubf) {
188 			/*
189 			 * The loop can happen from s/\</&/g
190 			 * but we don't want to break other, reasonable cases.
191 			 */
192 			while (*loc2) {
193 				if (++hopcount > sizeof linebuf)
194 					error("substitution loop");
195 				if (dosubcon(1, addr) == 0)
196 					break;
197 			}
198 		}
199 		if (scount) {
200 			stotal += scount;
201 			slines++;
202 			putmark(addr);
203 			n = append(getsub, addr);
204 			addr += n;
205 			addr2 += n;
206 		}
207 	}
208 	if (stotal == 0 && !inglobal && !cflag)
209 		error("Fail|Substitute pattern match failed");
210 	snote(stotal, slines);
211 	return (stotal);
212 }
213 
214 compsub(ch)
215 {
216 	register int seof, c, uselastre;
217 	static int gsubf;
218 
219 	if (!value(EDCOMPATIBLE))
220 		gsubf = cflag = 0;
221 	uselastre = 0;
222 	switch (ch) {
223 
224 	case 's':
225 		ignore(skipwh());
226 		seof = ex_getchar();
227 		if (endcmd(seof) || any(seof, "gcr")) {
228 			ungetchar(seof);
229 			goto redo;
230 		}
231 		if (isalpha(seof) || isdigit(seof))
232 			error("Substitute needs re|Missing regular expression for substitute");
233 		seof = compile(seof, 1);
234 		uselastre = 1;
235 		comprhs(seof);
236 		gsubf = 0;
237 		cflag = 0;
238 		break;
239 
240 	case '~':
241 		uselastre = 1;
242 		/* fall into ... */
243 	case '&':
244 	redo:
245 		if (re.Expbuf[0] == 0)
246 			error("No previous re|No previous regular expression");
247 		if (subre.Expbuf[0] == 0)
248 			error("No previous substitute re|No previous substitute to repeat");
249 		break;
250 	}
251 	for (;;) {
252 		c = ex_getchar();
253 		switch (c) {
254 
255 		case 'g':
256 			gsubf = !gsubf;
257 			continue;
258 
259 		case 'c':
260 			cflag = !cflag;
261 			continue;
262 
263 		case 'r':
264 			uselastre = 1;
265 			continue;
266 
267 		default:
268 			ungetchar(c);
269 			setcount();
270 			newline();
271 			if (uselastre)
272 				savere(subre);
273 			else
274 				resre(subre);
275 			return (gsubf);
276 		}
277 	}
278 }
279 
280 comprhs(seof)
281 	int seof;
282 {
283 	register char *rp, *orp;
284 	register int c;
285 	char orhsbuf[RHSSIZE];
286 
287 	rp = rhsbuf;
288 	CP(orhsbuf, rp);
289 	for (;;) {
290 		c = ex_getchar();
291 		if (c == seof)
292 			break;
293 		switch (c) {
294 
295 		case '\\':
296 			c = ex_getchar();
297 			if (c == EOF) {
298 				ungetchar(c);
299 				break;
300 			}
301 			if (value(MAGIC)) {
302 				/*
303 				 * When "magic", \& turns into a plain &,
304 				 * and all other chars work fine quoted.
305 				 */
306 				if (c != '&')
307 					c |= QUOTE;
308 				break;
309 			}
310 magic:
311 			if (c == '~') {
312 				for (orp = orhsbuf; *orp; *rp++ = *orp++)
313 					if (rp >= &rhsbuf[RHSSIZE - 1])
314 						goto toobig;
315 				continue;
316 			}
317 			c |= QUOTE;
318 			break;
319 
320 		case '\n':
321 		case EOF:
322 			if (!(globp && globp[0])) {
323 				ungetchar(c);
324 				goto endrhs;
325 			}
326 
327 		case '~':
328 		case '&':
329 			if (value(MAGIC))
330 				goto magic;
331 			break;
332 		}
333 		if (rp >= &rhsbuf[RHSSIZE - 1]) {
334 toobig:
335 			*rp = 0;
336 			error("Replacement pattern too long@- limit 256 characters");
337 		}
338 		*rp++ = c;
339 	}
340 endrhs:
341 	*rp++ = 0;
342 }
343 
344 getsub()
345 {
346 	register char *p;
347 
348 	if ((p = linebp) == 0)
349 		return (EOF);
350 	strcLIN(p);
351 	linebp = 0;
352 	return (0);
353 }
354 
355 dosubcon(f, a)
356 	bool f;
357 	line *a;
358 {
359 
360 	if (execute(f, a) == 0)
361 		return (0);
362 	if (confirmed(a)) {
363 		dosub();
364 		scount++;
365 	}
366 	return (1);
367 }
368 
369 confirmed(a)
370 	line *a;
371 {
372 	register int c, ch;
373 
374 	if (cflag == 0)
375 		return (1);
376 	pofix();
377 	pline(lineno(a));
378 	if (inopen)
379 		ex_putchar('\n' | QUOTE);
380 	c = column(loc1 - 1);
381 	ugo(c - 1 + (inopen ? 1 : 0), ' ');
382 	ugo(column(loc2 - 1) - c, '^');
383 	flush();
384 	ch = c = getkey();
385 again:
386 	if (c == '\r')
387 		c = '\n';
388 	if (inopen)
389 		ex_putchar(c), flush();
390 	if (c != '\n' && c != EOF) {
391 		c = getkey();
392 		goto again;
393 	}
394 	noteinp();
395 	return (ch == 'y');
396 }
397 
398 getch()
399 {
400 	char c;
401 
402 	if (read(2, &c, 1) != 1)
403 		return (EOF);
404 	return (c & TRIM);
405 }
406 
407 ugo(cnt, with)
408 	int with;
409 	int cnt;
410 {
411 
412 	if (cnt > 0)
413 		do
414 			ex_putchar(with);
415 		while (--cnt > 0);
416 }
417 
418 int	casecnt;
419 bool	destuc;
420 
421 dosub()
422 {
423 	register char *lp, *sp, *rp;
424 	int c;
425 
426 	lp = linebuf;
427 	sp = genbuf;
428 	rp = rhsbuf;
429 	while (lp < loc1)
430 		*sp++ = *lp++;
431 	casecnt = 0;
432 	while (c = *rp++) {
433 		/* ^V <return> from vi to split lines */
434 		if (c == '\r')
435 			c = '\n';
436 
437 		if (c & QUOTE)
438 			switch (c & TRIM) {
439 
440 			case '&':
441 				sp = place(sp, loc1, loc2);
442 				if (sp == 0)
443 					goto ovflo;
444 				continue;
445 
446 			case 'l':
447 				casecnt = 1;
448 				destuc = 0;
449 				continue;
450 
451 			case 'L':
452 				casecnt = LBSIZE;
453 				destuc = 0;
454 				continue;
455 
456 			case 'u':
457 				casecnt = 1;
458 				destuc = 1;
459 				continue;
460 
461 			case 'U':
462 				casecnt = LBSIZE;
463 				destuc = 1;
464 				continue;
465 
466 			case 'E':
467 			case 'e':
468 				casecnt = 0;
469 				continue;
470 			}
471 		if (c < 0 && (c &= TRIM) >= '1' && c < nbra + '1') {
472 			sp = place(sp, braslist[c - '1'], braelist[c - '1']);
473 			if (sp == 0)
474 				goto ovflo;
475 			continue;
476 		}
477 		if (casecnt)
478 			*sp++ = fixcase(c & TRIM);
479 		else
480 			*sp++ = c & TRIM;
481 		if (sp >= &genbuf[LBSIZE])
482 ovflo:
483 			error("Line overflow@in substitute");
484 	}
485 	lp = loc2;
486 	loc2 = sp + (linebuf - genbuf);
487 	while (*sp++ = *lp++)
488 		if (sp >= &genbuf[LBSIZE])
489 			goto ovflo;
490 	strcLIN(genbuf);
491 }
492 
493 fixcase(c)
494 	register int c;
495 {
496 
497 	if (casecnt == 0)
498 		return (c);
499 	casecnt--;
500 	if (destuc) {
501 		if (islower(c))
502 			c = toupper(c);
503 	} else
504 		if (isupper(c))
505 			c = tolower(c);
506 	return (c);
507 }
508 
509 char *
510 place(sp, l1, l2)
511 	register char *sp, *l1, *l2;
512 {
513 
514 	while (l1 < l2) {
515 		*sp++ = fixcase(*l1++);
516 		if (sp >= &genbuf[LBSIZE])
517 			return (0);
518 	}
519 	return (sp);
520 }
521 
522 snote(total, lines)
523 	register int total, lines;
524 {
525 
526 	if (!notable(total))
527 		return;
528 	ex_printf(mesg("%d subs|%d substitutions"), total);
529 	if (lines != 1 && lines != total)
530 		ex_printf(" on %d lines", lines);
531 	noonl();
532 	flush();
533 }
534 
535 compile(eof, oknl)
536 	int eof;
537 	int oknl;
538 {
539 	register int c;
540 	register char *ep;
541 	char *lastep;
542 	char bracket[NBRA], *bracketp, *rhsp;
543 	int cclcnt;
544 
545 	if (isalpha(eof) || isdigit(eof))
546 		error("Regular expressions cannot be delimited by letters or digits");
547 	ep = expbuf;
548 	c = ex_getchar();
549 	if (eof == '\\')
550 		switch (c) {
551 
552 		case '/':
553 		case '?':
554 			if (scanre.Expbuf[0] == 0)
555 error("No previous scan re|No previous scanning regular expression");
556 			resre(scanre);
557 			return (c);
558 
559 		case '&':
560 			if (subre.Expbuf[0] == 0)
561 error("No previous substitute re|No previous substitute regular expression");
562 			resre(subre);
563 			return (c);
564 
565 		default:
566 			error("Badly formed re|Regular expression \\ must be followed by / or ?");
567 		}
568 	if (c == eof || c == '\n' || c == EOF) {
569 		if (*ep == 0)
570 			error("No previous re|No previous regular expression");
571 		if (c == '\n' && oknl == 0)
572 			error("Missing closing delimiter@for regular expression");
573 		if (c != eof)
574 			ungetchar(c);
575 		return (eof);
576 	}
577 	bracketp = bracket;
578 	nbra = 0;
579 	circfl = 0;
580 	if (c == '^') {
581 		c = ex_getchar();
582 		circfl++;
583 	}
584 	ungetchar(c);
585 	for (;;) {
586 		if (ep >= &expbuf[ESIZE - 2])
587 complex:
588 			cerror("Re too complex|Regular expression too complicated");
589 		c = ex_getchar();
590 		if (c == eof || c == EOF) {
591 			if (bracketp != bracket)
592 cerror("Unmatched \\(|More \\('s than \\)'s in regular expression");
593 			*ep++ = CEOFC;
594 			if (c == EOF)
595 				ungetchar(c);
596 			return (eof);
597 		}
598 		if (value(MAGIC)) {
599 			if (c != '*' || ep == expbuf)
600 				lastep = ep;
601 		} else
602 			if (c != '\\' || peekchar() != '*' || ep == expbuf)
603 				lastep = ep;
604 		switch (c) {
605 
606 		case '\\':
607 			c = ex_getchar();
608 			switch (c) {
609 
610 			case '(':
611 				if (nbra >= NBRA)
612 cerror("Awash in \\('s!|Too many \\('d subexressions in a regular expression");
613 				*bracketp++ = nbra;
614 				*ep++ = CBRA;
615 				*ep++ = nbra++;
616 				continue;
617 
618 			case ')':
619 				if (bracketp <= bracket)
620 cerror("Extra \\)|More \\)'s than \\('s in regular expression");
621 				*ep++ = CKET;
622 				*ep++ = *--bracketp;
623 				continue;
624 
625 			case '<':
626 				*ep++ = CBRC;
627 				continue;
628 
629 			case '>':
630 				*ep++ = CLET;
631 				continue;
632 			}
633 			if (value(MAGIC) == 0)
634 magic:
635 			switch (c) {
636 
637 			case '.':
638 				*ep++ = CDOT;
639 				continue;
640 
641 			case '~':
642 				rhsp = rhsbuf;
643 				while (*rhsp) {
644 					if (*rhsp & QUOTE) {
645 						c = *rhsp & TRIM;
646 						if (c == '&')
647 error("Replacement pattern contains &@- cannot use in re");
648 						if (c >= '1' && c <= '9')
649 error("Replacement pattern contains \\d@- cannot use in re");
650 					}
651 					if (ep >= &expbuf[ESIZE-2])
652 						goto complex;
653 					*ep++ = CCHR;
654 					*ep++ = *rhsp++ & TRIM;
655 				}
656 				continue;
657 
658 			case '*':
659 				if (ep == expbuf)
660 					break;
661 				if (*lastep == CBRA || *lastep == CKET)
662 cerror("Illegal *|Can't * a \\( ... \\) in regular expression");
663 				if (*lastep == CCHR && (lastep[1] & QUOTE))
664 cerror("Illegal *|Can't * a \\n in regular expression");
665 				*lastep |= STAR;
666 				continue;
667 
668 			case '[':
669 				*ep++ = CCL;
670 				*ep++ = 0;
671 				cclcnt = 1;
672 				c = ex_getchar();
673 				if (c == '^') {
674 					c = ex_getchar();
675 					ep[-2] = NCCL;
676 				}
677 				if (c == ']')
678 cerror("Bad character class|Empty character class '[]' or '[^]' cannot match");
679 				while (c != ']') {
680 					if (c == '\\' && any(peekchar(), "]-^\\"))
681 						c = ex_getchar() | QUOTE;
682 					if (c == '\n' || c == EOF)
683 						cerror("Missing ]");
684 					*ep++ = c;
685 					cclcnt++;
686 					if (ep >= &expbuf[ESIZE])
687 						goto complex;
688 					c = ex_getchar();
689 				}
690 				lastep[1] = cclcnt;
691 				continue;
692 			}
693 			if (c == EOF) {
694 				ungetchar(EOF);
695 				c = '\\';
696 				goto defchar;
697 			}
698 			*ep++ = CCHR;
699 			if (c == '\n')
700 cerror("No newlines in re's|Can't escape newlines into regular expressions");
701 /*
702 			if (c < '1' || c > NBRA + '1') {
703 */
704 				*ep++ = c;
705 				continue;
706 /*
707 			}
708 			c -= '1';
709 			if (c >= nbra)
710 cerror("Bad \\n|\\n in regular expression with n greater than the number of \\('s");
711 			*ep++ = c | QUOTE;
712 			continue;
713 */
714 
715 		case '\n':
716 			if (oknl) {
717 				ungetchar(c);
718 				*ep++ = CEOFC;
719 				return (eof);
720 			}
721 cerror("Badly formed re|Missing closing delimiter for regular expression");
722 
723 		case '$':
724 			if (peekchar() == eof || peekchar() == EOF || oknl && peekchar() == '\n') {
725 				*ep++ = CDOL;
726 				continue;
727 			}
728 			goto defchar;
729 
730 		case '.':
731 		case '~':
732 		case '*':
733 		case '[':
734 			if (value(MAGIC))
735 				goto magic;
736 defchar:
737 		default:
738 			*ep++ = CCHR;
739 			*ep++ = c;
740 			continue;
741 		}
742 	}
743 }
744 
745 cerror(s)
746 	char *s;
747 {
748 
749 	expbuf[0] = 0;
750 	error(s);
751 }
752 
753 same(a, b)
754 	register int a, b;
755 {
756 
757 	return (a == b || value(IGNORECASE) &&
758 	   ((islower(a) && toupper(a) == b) || (islower(b) && toupper(b) == a)));
759 }
760 
761 char	*locs;
762 
763 /* VARARGS1 */
764 execute(gf, addr)
765 	line *addr;
766 {
767 	register char *p1, *p2;
768 	register int c;
769 
770 	if (gf) {
771 		if (circfl)
772 			return (0);
773 		locs = p1 = loc2;
774 	} else {
775 		if (addr == zero)
776 			return (0);
777 		p1 = linebuf;
778 		getline(*addr);
779 		locs = 0;
780 	}
781 	p2 = expbuf;
782 	if (circfl) {
783 		loc1 = p1;
784 		return (advance(p1, p2));
785 	}
786 	/* fast check for first character */
787 	if (*p2 == CCHR) {
788 		c = p2[1];
789 		do {
790 			if (c != *p1 && (!value(IGNORECASE) ||
791 			   !((islower(c) && toupper(c) == *p1) ||
792 			   (islower(*p1) && toupper(*p1) == c))))
793 				continue;
794 			if (advance(p1, p2)) {
795 				loc1 = p1;
796 				return (1);
797 			}
798 		} while (*p1++);
799 		return (0);
800 	}
801 	/* regular algorithm */
802 	do {
803 		if (advance(p1, p2)) {
804 			loc1 = p1;
805 			return (1);
806 		}
807 	} while (*p1++);
808 	return (0);
809 }
810 
811 #define	uletter(c)	(isalpha(c) || c == '_')
812 
813 advance(lp, ep)
814 	register char *lp, *ep;
815 {
816 	register char *curlp;
817 
818 	for (;;) switch (*ep++) {
819 
820 	case CCHR:
821 /* useless
822 		if (*ep & QUOTE) {
823 			c = *ep++ & TRIM;
824 			sp = braslist[c];
825 			sp1 = braelist[c];
826 			while (sp < sp1) {
827 				if (!same(*sp, *lp))
828 					return (0);
829 				sp++, lp++;
830 			}
831 			continue;
832 		}
833 */
834 		if (!same(*ep, *lp))
835 			return (0);
836 		ep++, lp++;
837 		continue;
838 
839 	case CDOT:
840 		if (*lp++)
841 			continue;
842 		return (0);
843 
844 	case CDOL:
845 		if (*lp == 0)
846 			continue;
847 		return (0);
848 
849 	case CEOFC:
850 		loc2 = lp;
851 		return (1);
852 
853 	case CCL:
854 		if (cclass(ep, *lp++, 1)) {
855 			ep += *ep;
856 			continue;
857 		}
858 		return (0);
859 
860 	case NCCL:
861 		if (cclass(ep, *lp++, 0)) {
862 			ep += *ep;
863 			continue;
864 		}
865 		return (0);
866 
867 	case CBRA:
868 		braslist[*ep++] = lp;
869 		continue;
870 
871 	case CKET:
872 		braelist[*ep++] = lp;
873 		continue;
874 
875 	case CDOT|STAR:
876 		curlp = lp;
877 		while (*lp++)
878 			continue;
879 		goto star;
880 
881 	case CCHR|STAR:
882 		curlp = lp;
883 		while (same(*lp, *ep))
884 			lp++;
885 		lp++;
886 		ep++;
887 		goto star;
888 
889 	case CCL|STAR:
890 	case NCCL|STAR:
891 		curlp = lp;
892 		while (cclass(ep, *lp++, ep[-1] == (CCL|STAR)))
893 			continue;
894 		ep += *ep;
895 		goto star;
896 star:
897 		do {
898 			lp--;
899 			if (lp == locs)
900 				break;
901 			if (advance(lp, ep))
902 				return (1);
903 		} while (lp > curlp);
904 		return (0);
905 
906 	case CBRC:
907 		if (lp == linebuf)
908 			continue;
909 		if ((isdigit(*lp) || uletter(*lp)) && !uletter(lp[-1]) && !isdigit(lp[-1]))
910 			continue;
911 		return (0);
912 
913 	case CLET:
914 		if (!uletter(*lp) && !isdigit(*lp))
915 			continue;
916 		return (0);
917 
918 	default:
919 		error("Re internal error");
920 	}
921 }
922 
923 cclass(set, c, af)
924 	register char *set;
925 	register int c;
926 	int af;
927 {
928 	register int n;
929 
930 	if (c == 0)
931 		return (0);
932 	if (value(IGNORECASE) && isupper(c))
933 		c = tolower(c);
934 	n = *set++;
935 	while (--n)
936 		if (n > 2 && set[1] == '-') {
937 			if (c >= (set[0] & TRIM) && c <= (set[2] & TRIM))
938 				return (af);
939 			set += 3;
940 			n -= 2;
941 		} else
942 			if ((*set++ & TRIM) == c)
943 				return (af);
944 	return (!af);
945 }
946