regexp.c revision 293290
1103285Sikob/*
2103285Sikob * Copyright (c) 1980, 1993
3103285Sikob *	The Regents of the University of California.  All rights reserved.
4103285Sikob *
5103285Sikob *
6103285Sikob * Redistribution and use in source and binary forms, with or without
7103285Sikob * modification, are permitted provided that the following conditions
8103285Sikob * are met:
9103285Sikob * 1. Redistributions of source code must retain the above copyright
10103285Sikob *    notice, this list of conditions and the following disclaimer.
11103285Sikob * 2. Redistributions in binary form must reproduce the above copyright
12103285Sikob *    notice, this list of conditions and the following disclaimer in the
13103285Sikob *    documentation and/or other materials provided with the distribution.
14103285Sikob * 4. Neither the name of the University nor the names of its contributors
15103285Sikob *    may be used to endorse or promote products derived from this software
16103285Sikob *    without specific prior written permission.
17103285Sikob *
18103285Sikob * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19103285Sikob * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20103285Sikob * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21103285Sikob * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22103285Sikob * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23103285Sikob * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24103285Sikob * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25103285Sikob * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26103285Sikob * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27103285Sikob * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28103285Sikob * SUCH DAMAGE.
29103285Sikob */
30103285Sikob
31103285Sikob#include <sys/cdefs.h>
32103285Sikob
33103285Sikob__FBSDID("$FreeBSD: stable/10/usr.bin/vgrind/regexp.c 293290 2016-01-07 00:40:51Z bdrewery $");
34103285Sikob
35103285Sikob#ifndef lint
36103285Sikobstatic const char copyright[] =
37103285Sikob"@(#) Copyright (c) 1980, 1993\n\
38103285Sikob	The Regents of the University of California.  All rights reserved.\n";
39103285Sikob#endif
40103285Sikob
41103285Sikob#ifndef lint
42103285Sikobstatic const char sccsid[] = "@(#)regexp.c	8.1 (Berkeley) 6/6/93";
43103285Sikob#endif
44103285Sikob
45103285Sikob#include <ctype.h>
46103285Sikob#include <stdlib.h>
47103285Sikob#include <stdbool.h>
48103285Sikob#include <string.h>
49103285Sikob
50103285Sikob#include "extern.h"
51103285Sikob
52103285Sikobstatic void	expconv(void);
53103285Sikob
54103285Sikobbool	 _escaped;	/* true if we are currently x_escaped */
55103285Sikobchar	*s_start;	/* start of string */
56103285Sikobbool	 l_onecase;	/* true if upper and lower equivalent */
57103285Sikob
58103285Sikob#define makelower(c) (isupper((c)) ? tolower((c)) : (c))
59103285Sikob
60103285Sikob/*  STRNCMP -	like strncmp except that we convert the
61108281Ssimokawa *	 	first string to lower case before comparing
62103285Sikob *		if l_onecase is set.
63108281Ssimokawa */
64103285Sikob
65103285Sikobint
66103285SikobSTRNCMP(register char *s1, register char *s2, register int len)
67103285Sikob{
68103285Sikob	if (l_onecase) {
69103285Sikob	    do
70103285Sikob		if (*s2 - makelower(*s1))
71103285Sikob			return (*s2 - makelower(*s1));
72103285Sikob		else {
73103285Sikob			s2++;
74103285Sikob			s1++;
75103285Sikob		}
76103285Sikob	    while (--len);
77103285Sikob	} else {
78103285Sikob	    do
79103285Sikob		if (*s2 - *s1)
80103285Sikob			return (*s2 - *s1);
81103285Sikob		else {
82103285Sikob			s2++;
83103285Sikob			s1++;
84103285Sikob		}
85103285Sikob	    while (--len);
86103285Sikob	}
87106543Ssimokawa	return(0);
88103285Sikob}
89106543Ssimokawa
90103285Sikob/*	The following routine converts an irregular expression to
91105620Ssimokawa *	internal format.
92103285Sikob *
93103285Sikob *	Either meta symbols (\a \d or \p) or character strings or
94103285Sikob *	operations ( alternation or perenthesizing ) can be
95103285Sikob *	specified.  Each starts with a descriptor byte.  The descriptor
96103285Sikob *	byte has STR set for strings, META set for meta symbols
97103285Sikob *	and OPER set for operations.
98108642Ssimokawa *	The descriptor byte can also have the OPT bit set if the object
99108642Ssimokawa *	defined is optional.  Also ALT can be set to indicate an alternation.
100103285Sikob *
101103285Sikob *	For metasymbols the byte following the descriptor byte identities
102103285Sikob *	the meta symbol (containing an ascii 'a', 'd', 'p', '|', or '(').  For
103103285Sikob *	strings the byte after the descriptor is a character count for
104103285Sikob *	the string:
105103285Sikob *
106103285Sikob *		meta symbols := descriptor
107103285Sikob *				symbol
108103285Sikob *
109103285Sikob *		strings :=	descriptor
110103285Sikob *				character count
111103285Sikob *				the string
112103285Sikob *
113106813Ssimokawa *		operatins :=	descriptor
114106813Ssimokawa *				symbol
115106813Ssimokawa *				character count
116103285Sikob */
117103285Sikob
118103285Sikob/*
119103285Sikob *  handy macros for accessing parts of match blocks
120103285Sikob */
121103285Sikob#define MSYM(A) (*(A+1))	/* symbol in a meta symbol block */
122103285Sikob#define MNEXT(A) (A+2)		/* character following a metasymbol block */
123103285Sikob
124103285Sikob#define OSYM(A) (*(A+1))	/* symbol in an operation block */
125103285Sikob#define OCNT(A) (*(A+2))	/* character count */
126103285Sikob#define ONEXT(A) (A+3)		/* next character after the operation */
127103285Sikob#define OPTR(A) (A+*(A+2))	/* place pointed to by the operator */
128103285Sikob
129103285Sikob#define SCNT(A) (*(A+1))	/* byte count of a string */
130103285Sikob#define SSTR(A) (A+2)		/* address of the string */
131103285Sikob#define SNEXT(A) (A+2+*(A+1))	/* character following the string */
132109179Ssimokawa
133103285Sikob/*
134103285Sikob *  bit flags in the descriptor
135103285Sikob */
136103285Sikob#define OPT 1
137103285Sikob#define STR 2
138103285Sikob#define META 4
139103285Sikob#define ALT 8
140103285Sikob#define OPER 16
141103285Sikob
142103285Sikobstatic char *ccre;	/* pointer to current position in converted exp*/
143103285Sikobstatic char *ure;	/* pointer current position in unconverted exp */
144103285Sikob
145103285Sikob/* re: unconverted irregular expression */
146103285Sikobchar *
147103285Sikobconvexp(char *re)
148103285Sikob{
149103285Sikob    register char *cre;		/* pointer to converted regular expression */
150103285Sikob
151103285Sikob    /* allocate room for the converted expression */
152103285Sikob    if (re == NULL)
153103285Sikob	return (NULL);
154103285Sikob    if (*re == '\0')
155103285Sikob	return (NULL);
156103285Sikob    cre = malloc(4 * strlen(re) + 3);
157103285Sikob    ccre = cre;
158103285Sikob    ure = re;
159103285Sikob
160103285Sikob    /* start the conversion with a \a */
161103285Sikob    *cre = META | OPT;
162103285Sikob    MSYM(cre) = 'a';
163103285Sikob    ccre = MNEXT(cre);
164103285Sikob
165103285Sikob    /* start the conversion (its recursive) */
166103285Sikob    expconv ();
167103285Sikob    *ccre = 0;
168103285Sikob    return (cre);
169103285Sikob}
170103285Sikob
171103285Sikobstatic void
172103285Sikobexpconv()
173103285Sikob{
174103285Sikob    register char *cs;		/* pointer to current symbol in converted exp */
175103285Sikob    register char c;		/* character being processed */
176103285Sikob    register char *acs;		/* pinter to last alternate */
177103285Sikob    register int temp;
178103285Sikob
179109227Ssimokawa    /* let the conversion begin */
180103285Sikob    acs = NULL;
181103285Sikob    cs = NULL;
182103285Sikob    while (*ure) {
183103285Sikob	switch (c = *ure++) {
184103285Sikob
185109227Ssimokawa	case '\\':
186103285Sikob	    switch (c = *ure++) {
187109282Ssimokawa
188109282Ssimokawa	    /* escaped characters are just characters */
189109282Ssimokawa	    default:
190109282Ssimokawa		if (cs == NULL || (*cs & STR) == 0) {
191109282Ssimokawa		    cs = ccre;
192109179Ssimokawa		    *cs = STR;
193109179Ssimokawa		    SCNT(cs) = 1;
194109179Ssimokawa		    ccre += 2;
195109179Ssimokawa		} else
196109179Ssimokawa		    SCNT(cs)++;
197109179Ssimokawa		*ccre++ = c;
198109179Ssimokawa		break;
199109179Ssimokawa
200109179Ssimokawa	    /* normal(?) metacharacters */
201109179Ssimokawa	    case 'a':
202109179Ssimokawa	    case 'd':
203109179Ssimokawa	    case 'e':
204109179Ssimokawa	    case 'p':
205109179Ssimokawa		if (acs != NULL && acs != cs) {
206109179Ssimokawa		    do {
207109179Ssimokawa			temp = OCNT(acs);
208103285Sikob			OCNT(acs) = ccre - acs;
209103285Sikob			acs -= temp;
210103285Sikob		    } while (temp != 0);
211103285Sikob		    acs = NULL;
212103285Sikob		}
213103285Sikob		cs = ccre;
214103285Sikob		*cs = META;
215103285Sikob		MSYM(cs) = c;
216103285Sikob		ccre = MNEXT(cs);
217103285Sikob		break;
218103285Sikob	    }
219103285Sikob	    break;
220103285Sikob
221103285Sikob	/* just put the symbol in */
222103285Sikob	case '^':
223103285Sikob	case '$':
224103285Sikob	    if (acs != NULL && acs != cs) {
225103285Sikob		do {
226103285Sikob		    temp = OCNT(acs);
227103285Sikob		    OCNT(acs) = ccre - acs;
228103285Sikob		    acs -= temp;
229103285Sikob		} while (temp != 0);
230103285Sikob		acs = NULL;
231103285Sikob	    }
232103285Sikob	    cs = ccre;
233103285Sikob	    *cs = META;
234103285Sikob	    MSYM(cs) = c;
235103285Sikob	    ccre = MNEXT(cs);
236103285Sikob	    break;
237103285Sikob
238103285Sikob	/* mark the last match sequence as optional */
239103285Sikob	case '?':
240103285Sikob	    if (cs)
241103285Sikob	    	*cs = *cs | OPT;
242103285Sikob	    break;
243103285Sikob
244103285Sikob	/* recurse and define a subexpression */
245109227Ssimokawa	case '(':
246109227Ssimokawa	    if (acs != NULL && acs != cs) {
247103285Sikob		do {
248103285Sikob		    temp = OCNT(acs);
249103285Sikob		    OCNT(acs) = ccre - acs;
250103285Sikob		    acs -= temp;
251103285Sikob		} while (temp != 0);
252103285Sikob		acs = NULL;
253103285Sikob	    }
254103285Sikob	    cs = ccre;
255103285Sikob	    *cs = OPER;
256103285Sikob	    OSYM(cs) = '(';
257103285Sikob	    ccre = ONEXT(cs);
258103285Sikob	    expconv();
259103285Sikob	    OCNT(cs) = ccre - cs;		/* offset to next symbol */
260103285Sikob	    break;
261103285Sikob
262103285Sikob	/* return from a recursion */
263103285Sikob	case ')':
264103285Sikob	    if (acs != NULL) {
265103285Sikob		do {
266103285Sikob		    temp = OCNT(acs);
267103285Sikob		    OCNT(acs) = ccre - acs;
268103285Sikob		    acs -= temp;
269103285Sikob		} while (temp != 0);
270103285Sikob		acs = NULL;
271103285Sikob	    }
272103285Sikob	    cs = ccre;
273103285Sikob	    *cs = META;
274103285Sikob	    MSYM(cs) = c;
275103285Sikob	    ccre = MNEXT(cs);
276103285Sikob	    return;
277103285Sikob
278103285Sikob	/* mark the last match sequence as having an alternate */
279103285Sikob	/* the third byte will contain an offset to jump over the */
280103285Sikob	/* alternate match in case the first did not fail */
281103285Sikob	case '|':
282103285Sikob	    if (acs != NULL && acs != cs)
283108995Ssimokawa		OCNT(ccre) = ccre - acs;	/* make a back pointer */
284103285Sikob	    else
285103285Sikob		OCNT(ccre) = 0;
286103285Sikob	    *cs |= ALT;
287103285Sikob	    cs = ccre;
288103285Sikob	    *cs = OPER;
289103285Sikob	    OSYM(cs) = '|';
290103285Sikob	    ccre = ONEXT(cs);
291103285Sikob	    acs = cs;	/* remember that the pointer is to be filles */
292103285Sikob	    break;
293103285Sikob
294103285Sikob	/* if its not a metasymbol just build a scharacter string */
295103285Sikob	default:
296103285Sikob	    if (cs == NULL || (*cs & STR) == 0) {
297106810Ssimokawa		cs = ccre;
298106790Ssimokawa		*cs = STR;
299103285Sikob		SCNT(cs) = 1;
300103285Sikob		ccre = SSTR(cs);
301103285Sikob	    } else
302103285Sikob		SCNT(cs)++;
303103285Sikob	    *ccre++ = c;
304103285Sikob	    break;
305103285Sikob	}
306103285Sikob    }
307106810Ssimokawa    if (acs != NULL) {
308106810Ssimokawa	do {
309106810Ssimokawa	    temp = OCNT(acs);
310103285Sikob	    OCNT(acs) = ccre - acs;
311106813Ssimokawa	    acs -= temp;
312103285Sikob	} while (temp != 0);
313103285Sikob	acs = NULL;
314103285Sikob    }
315103285Sikob    return;
316103285Sikob}
317103285Sikob/* end of convertre */
318103285Sikob
319103285Sikob
320103285Sikob/*
321103285Sikob *	The following routine recognises an irregular expression
322103285Sikob *	with the following special characters:
323103285Sikob *
324103285Sikob *		\?	-	means last match was optional
325103285Sikob *		\a	-	matches any number of characters
326108701Ssimokawa *		\d	-	matches any number of spaces and tabs
327103285Sikob *		\p	-	matches any number of alphanumeric
328103285Sikob *				characters. The
329103285Sikob *				characters matched will be copied into
330103285Sikob *				the area pointed to by 'name'.
331103285Sikob *		\|	-	alternation
332103285Sikob *		\( \)	-	grouping used mostly for alternation and
333103285Sikob *				optionality
334103285Sikob *
335103285Sikob *	The irregular expression must be translated to internal form
336103285Sikob *	prior to calling this routine
337103285Sikob *
338103285Sikob *	The value returned is the pointer to the first non \a
339103285Sikob *	character matched.
340103285Sikob */
341103285Sikob
342103285Sikob/*
343103285Sikob *  s: string to check for a match in
344103285Sikob *  re: a converted irregular expression
345103285Sikob *  mstring: where to put whatever matches a \p
346103285Sikob */
347103285Sikobchar *
348103285Sikobexpmatch (register char *s, register char *re, register char *mstring)
349103285Sikob{
350103285Sikob    register char *cs;		/* the current symbol */
351103285Sikob    register char *ptr,*s1;	/* temporary pointer */
352103285Sikob    bool matched;	/* a temporary bool */
353103285Sikob
354103285Sikob    /* initial conditions */
355103285Sikob    if (re == NULL)
356103285Sikob	return (NULL);
357106790Ssimokawa    cs = re;
358103285Sikob    matched = false;
359103285Sikob
360103285Sikob    /* loop till expression string is exhausted (or at least pretty tired) */
361103285Sikob    while (*cs) {
362103285Sikob	switch (*cs & (OPER | STR | META)) {
363108655Ssimokawa
364108655Ssimokawa	/* try to match a string */
365103285Sikob	case STR:
366103285Sikob	    matched = !STRNCMP (s, SSTR(cs), SCNT(cs));
367103285Sikob	    if (matched) {
368103285Sikob
369103285Sikob		/* hoorah it matches */
370103285Sikob		s += SCNT(cs);
371103285Sikob		cs = SNEXT(cs);
372103285Sikob	    } else if (*cs & ALT) {
373103285Sikob
374103285Sikob		/* alternation, skip to next expression */
375103285Sikob		cs = SNEXT(cs);
376103285Sikob	    } else if (*cs & OPT) {
377103285Sikob
378103285Sikob		/* the match is optional */
379103285Sikob		cs = SNEXT(cs);
380103285Sikob		matched = 1;		/* indicate a successful match */
381103285Sikob	    } else {
382103285Sikob
383103285Sikob		/* no match, error return */
384103285Sikob		return (NULL);
385103285Sikob	    }
386103285Sikob	    break;
387103285Sikob
388103285Sikob	/* an operator, do something fancy */
389103285Sikob	case OPER:
390103285Sikob	    switch (OSYM(cs)) {
391103285Sikob
392103285Sikob	    /* this is an alternation */
393103285Sikob	    case '|':
394103285Sikob		if (matched)
395103285Sikob
396103285Sikob		    /* last thing in the alternation was a match, skip ahead */
397103285Sikob		    cs = OPTR(cs);
398106815Ssimokawa		else
399103285Sikob
400103285Sikob		    /* no match, keep trying */
401103285Sikob		    cs = ONEXT(cs);
402103285Sikob		break;
403103285Sikob
404103285Sikob	    /* this is a grouping, recurse */
405103285Sikob	    case '(':
406103285Sikob		ptr = expmatch(s, ONEXT(cs), mstring);
407103285Sikob		if (ptr != NULL) {
408103285Sikob
409103285Sikob		    /* the subexpression matched */
410103285Sikob		    matched = 1;
411103285Sikob		    s = ptr;
412103285Sikob		} else if (*cs & ALT) {
413103285Sikob
414103285Sikob		    /* alternation, skip to next expression */
415103285Sikob		    matched = 0;
416103285Sikob		} else if (*cs & OPT) {
417103285Sikob
418103285Sikob		    /* the match is optional */
419103285Sikob		    matched = 1;	/* indicate a successful match */
420103285Sikob		} else {
421103285Sikob
422103285Sikob		    /* no match, error return */
423103285Sikob		    return (NULL);
424103285Sikob		}
425103285Sikob		cs = OPTR(cs);
426103285Sikob		break;
427103285Sikob	    }
428103285Sikob	    break;
429103285Sikob
430103285Sikob	/* try to match a metasymbol */
431103285Sikob	case META:
432106790Ssimokawa	    switch (MSYM(cs)) {
433106790Ssimokawa
434106790Ssimokawa	    /* try to match anything and remember what was matched */
435103285Sikob	    case 'p':
436103285Sikob		/*
437103285Sikob		 *  This is really the same as trying the match the
438103285Sikob		 *  remaining parts of the expression to any subset
439103285Sikob		 *  of the string.
440103285Sikob		 */
441103285Sikob		s1 = s;
442103285Sikob		do {
443103285Sikob		    ptr = expmatch(s1, MNEXT(cs), mstring);
444103285Sikob		    if (ptr != NULL && s1 != s) {
445103285Sikob
446103285Sikob			/* we have a match, remember the match */
447103285Sikob			strncpy (mstring, s, s1 - s);
448103285Sikob			mstring[s1 - s] = '\0';
449103285Sikob			return (ptr);
450103285Sikob		    } else if (ptr != NULL && (*cs & OPT)) {
451103285Sikob
452103285Sikob			/* it was aoptional so no match is ok */
453103285Sikob			return (ptr);
454103285Sikob		    } else if (ptr != NULL) {
455103285Sikob
456103285Sikob			/* not optional and we still matched */
457103285Sikob			return (NULL);
458103285Sikob		    }
459103285Sikob		    if (!(isalnum(*s1) || *s1 == '_' ||
460103285Sikob			  /* C++ destructor */
461103285Sikob			  *s1 == '~' ||
462103285Sikob			  /* C++ scope operator */
463103285Sikob			  (strlen(s1) > 1 && *s1 == ':' && s1[1] == ':' &&
464103285Sikob			   (s1++, true))))
465103285Sikob			return (NULL);
466106790Ssimokawa		    if (*s1 == '\\')
467103285Sikob			_escaped = _escaped ? false : true;
468103285Sikob		    else
469103285Sikob			_escaped = false;
470108281Ssimokawa		} while (*s1++);
471103285Sikob		return (NULL);
472103285Sikob
473106790Ssimokawa	    /* try to match anything */
474103285Sikob	    case 'a':
475103285Sikob		/*
476103285Sikob		 *  This is really the same as trying the match the
477103285Sikob		 *  remaining parts of the expression to any subset
478103285Sikob		 *  of the string.
479103285Sikob		 */
480103285Sikob		s1 = s;
481103285Sikob		do {
482103285Sikob		    ptr = expmatch(s1, MNEXT(cs), mstring);
483103285Sikob		    if (ptr != NULL && s1 != s) {
484103285Sikob
485103285Sikob			/* we have a match */
486103285Sikob			return (ptr);
487103285Sikob		    } else if (ptr != NULL && (*cs & OPT)) {
488103285Sikob
489103285Sikob			/* it was aoptional so no match is ok */
490103285Sikob			return (ptr);
491103285Sikob		    } else if (ptr != NULL) {
492103285Sikob
493103285Sikob			/* not optional and we still matched */
494103285Sikob			return (NULL);
495103285Sikob		    }
496108276Ssimokawa		    if (*s1 == '\\')
497103285Sikob			_escaped = _escaped ? false : true;
498103285Sikob		    else
499103285Sikob			_escaped = false;
500103285Sikob		} while (*s1++);
501103285Sikob		return (NULL);
502103285Sikob
503103285Sikob	    /* fail if we are currently _escaped */
504103285Sikob	    case 'e':
505103285Sikob		if (_escaped)
506103285Sikob		    return(NULL);
507103285Sikob		cs = MNEXT(cs);
508108276Ssimokawa		break;
509103285Sikob
510103285Sikob	    /* match any number of tabs and spaces */
511103285Sikob	    case 'd':
512103285Sikob		ptr = s;
513103285Sikob		while (*s == ' ' || *s == '\t')
514103285Sikob		    s++;
515103285Sikob		if (s != ptr || s == s_start) {
516103285Sikob
517108853Ssimokawa		    /* match, be happy */
518108853Ssimokawa		    matched = 1;
519108853Ssimokawa		    cs = MNEXT(cs);
520108853Ssimokawa		} else if (*s == '\n' || *s == '\0') {
521108853Ssimokawa
522108853Ssimokawa		    /* match, be happy */
523108853Ssimokawa		    matched = 1;
524103285Sikob		    cs = MNEXT(cs);
525103285Sikob		} else if (*cs & ALT) {
526103285Sikob
527103285Sikob		    /* try the next part */
528103285Sikob		    matched = 0;
529103285Sikob		    cs = MNEXT(cs);
530103285Sikob		} else if (*cs & OPT) {
531103285Sikob
532103285Sikob		    /* doesn't matter */
533103285Sikob		    matched = 1;
534103285Sikob		    cs = MNEXT(cs);
535103285Sikob		} else
536103285Sikob
537103285Sikob		    /* no match, error return */
538103285Sikob		    return (NULL);
539103285Sikob		break;
540103285Sikob
541103285Sikob	    /* check for end of line */
542103285Sikob	    case '$':
543103285Sikob		if (*s == '\0' || *s == '\n') {
544103285Sikob
545103285Sikob		    /* match, be happy */
546103285Sikob		    s++;
547103285Sikob		    matched = 1;
548103285Sikob		    cs = MNEXT(cs);
549103285Sikob		} else if (*cs & ALT) {
550103285Sikob
551103285Sikob		    /* try the next part */
552103285Sikob		    matched = 0;
553103285Sikob		    cs = MNEXT(cs);
554106790Ssimokawa		} else if (*cs & OPT) {
555103285Sikob
556103285Sikob		    /* doesn't matter */
557103285Sikob		    matched = 1;
558103285Sikob		    cs = MNEXT(cs);
559103285Sikob		} else
560103285Sikob
561103285Sikob		    /* no match, error return */
562103285Sikob		    return (NULL);
563103285Sikob		break;
564106790Ssimokawa
565103285Sikob	    /* check for start of line */
566103285Sikob	    case '^':
567103285Sikob		if (s == s_start) {
568103285Sikob
569103285Sikob		    /* match, be happy */
570103285Sikob		    matched = 1;
571103285Sikob		    cs = MNEXT(cs);
572103285Sikob		} else if (*cs & ALT) {
573103285Sikob
574103285Sikob		    /* try the next part */
575103285Sikob		    matched = 0;
576103285Sikob		    cs = MNEXT(cs);
577103285Sikob		} else if (*cs & OPT) {
578103285Sikob
579103285Sikob		    /* doesn't matter */
580103285Sikob		    matched = 1;
581103285Sikob		    cs = MNEXT(cs);
582103285Sikob		} else
583103285Sikob
584103285Sikob		    /* no match, error return */
585103285Sikob		    return (NULL);
586103285Sikob		break;
587103285Sikob
588106790Ssimokawa	    /* end of a subexpression, return success */
589103285Sikob	    case ')':
590106790Ssimokawa		return (s);
591103285Sikob	    }
592106790Ssimokawa	    break;
593106790Ssimokawa	}
594103285Sikob    }
595103285Sikob    return (s);
596103285Sikob}
597103285Sikob