1331722Seadler/*
21590Srgrimes * Copyright (c) 1980, 1993
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes *
61590Srgrimes * Redistribution and use in source and binary forms, with or without
71590Srgrimes * modification, are permitted provided that the following conditions
81590Srgrimes * are met:
91590Srgrimes * 1. Redistributions of source code must retain the above copyright
101590Srgrimes *    notice, this list of conditions and the following disclaimer.
111590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
121590Srgrimes *    notice, this list of conditions and the following disclaimer in the
131590Srgrimes *    documentation and/or other materials provided with the distribution.
141590Srgrimes * 4. Neither the name of the University nor the names of its contributors
151590Srgrimes *    may be used to endorse or promote products derived from this software
161590Srgrimes *    without specific prior written permission.
171590Srgrimes *
181590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
191590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
201590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
211590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
221590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
231590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
241590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
251590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
261590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
271590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
281590Srgrimes * SUCH DAMAGE.
291590Srgrimes */
301590Srgrimes
3187693Smarkm#include <sys/cdefs.h>
3287693Smarkm
3387693Smarkm__FBSDID("$FreeBSD$");
3487693Smarkm
351590Srgrimes#ifndef lint
3687693Smarkmstatic const char copyright[] =
371590Srgrimes"@(#) Copyright (c) 1980, 1993\n\
381590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
3987693Smarkm#endif
401590Srgrimes
411590Srgrimes#ifndef lint
4287693Smarkmstatic const char sccsid[] = "@(#)regexp.c	8.1 (Berkeley) 6/6/93";
4387693Smarkm#endif
441590Srgrimes
451590Srgrimes#include <ctype.h>
461590Srgrimes#include <stdlib.h>
47282457Sbapt#include <stdbool.h>
481590Srgrimes#include <string.h>
4987693Smarkm
501590Srgrimes#include "extern.h"
511590Srgrimes
5292922Simpstatic void	expconv(void);
531590Srgrimes
54282457Sbaptbool	 _escaped;	/* true if we are currently x_escaped */
5536053Sjbchar	*s_start;	/* start of string */
56282457Sbaptbool	 l_onecase;	/* true if upper and lower equivalent */
571590Srgrimes
581590Srgrimes#define makelower(c) (isupper((c)) ? tolower((c)) : (c))
591590Srgrimes
601590Srgrimes/*  STRNCMP -	like strncmp except that we convert the
611590Srgrimes *	 	first string to lower case before comparing
621590Srgrimes *		if l_onecase is set.
631590Srgrimes */
641590Srgrimes
651590Srgrimesint
66282449SbaptSTRNCMP(register char *s1, register char *s2, register int len)
671590Srgrimes{
681590Srgrimes	if (l_onecase) {
691590Srgrimes	    do
701590Srgrimes		if (*s2 - makelower(*s1))
711590Srgrimes			return (*s2 - makelower(*s1));
721590Srgrimes		else {
731590Srgrimes			s2++;
741590Srgrimes			s1++;
751590Srgrimes		}
761590Srgrimes	    while (--len);
771590Srgrimes	} else {
781590Srgrimes	    do
791590Srgrimes		if (*s2 - *s1)
801590Srgrimes			return (*s2 - *s1);
811590Srgrimes		else {
821590Srgrimes			s2++;
831590Srgrimes			s1++;
841590Srgrimes		}
851590Srgrimes	    while (--len);
861590Srgrimes	}
871590Srgrimes	return(0);
881590Srgrimes}
891590Srgrimes
901590Srgrimes/*	The following routine converts an irregular expression to
911590Srgrimes *	internal format.
921590Srgrimes *
931590Srgrimes *	Either meta symbols (\a \d or \p) or character strings or
94284047Sbapt *	operations ( alternation or parenthesizing ) can be
951590Srgrimes *	specified.  Each starts with a descriptor byte.  The descriptor
961590Srgrimes *	byte has STR set for strings, META set for meta symbols
971590Srgrimes *	and OPER set for operations.
981590Srgrimes *	The descriptor byte can also have the OPT bit set if the object
991590Srgrimes *	defined is optional.  Also ALT can be set to indicate an alternation.
1001590Srgrimes *
1011590Srgrimes *	For metasymbols the byte following the descriptor byte identities
1021590Srgrimes *	the meta symbol (containing an ascii 'a', 'd', 'p', '|', or '(').  For
1031590Srgrimes *	strings the byte after the descriptor is a character count for
1041590Srgrimes *	the string:
1051590Srgrimes *
1061590Srgrimes *		meta symbols := descriptor
1071590Srgrimes *				symbol
1081590Srgrimes *
1091590Srgrimes *		strings :=	descriptor
1101590Srgrimes *				character count
1111590Srgrimes *				the string
1121590Srgrimes *
113284047Sbapt *		operations :=	descriptor
1141590Srgrimes *				symbol
1151590Srgrimes *				character count
1161590Srgrimes */
1171590Srgrimes
1181590Srgrimes/*
1191590Srgrimes *  handy macros for accessing parts of match blocks
1201590Srgrimes */
1211590Srgrimes#define MSYM(A) (*(A+1))	/* symbol in a meta symbol block */
1221590Srgrimes#define MNEXT(A) (A+2)		/* character following a metasymbol block */
1231590Srgrimes
1241590Srgrimes#define OSYM(A) (*(A+1))	/* symbol in an operation block */
1251590Srgrimes#define OCNT(A) (*(A+2))	/* character count */
1261590Srgrimes#define ONEXT(A) (A+3)		/* next character after the operation */
1271590Srgrimes#define OPTR(A) (A+*(A+2))	/* place pointed to by the operator */
1281590Srgrimes
1291590Srgrimes#define SCNT(A) (*(A+1))	/* byte count of a string */
1301590Srgrimes#define SSTR(A) (A+2)		/* address of the string */
1311590Srgrimes#define SNEXT(A) (A+2+*(A+1))	/* character following the string */
1321590Srgrimes
1331590Srgrimes/*
1348874Srgrimes *  bit flags in the descriptor
1351590Srgrimes */
1361590Srgrimes#define OPT 1
1371590Srgrimes#define STR 2
1381590Srgrimes#define META 4
1391590Srgrimes#define ALT 8
1401590Srgrimes#define OPER 16
1411590Srgrimes
1421590Srgrimesstatic char *ccre;	/* pointer to current position in converted exp*/
1431590Srgrimesstatic char *ure;	/* pointer current position in unconverted exp */
1441590Srgrimes
145282449Sbapt/* re: unconverted irregular expression */
1461590Srgrimeschar *
147282449Sbaptconvexp(char *re)
1481590Srgrimes{
1491590Srgrimes    register char *cre;		/* pointer to converted regular expression */
1501590Srgrimes
1511590Srgrimes    /* allocate room for the converted expression */
152282459Sbapt    if (re == NULL)
153282459Sbapt	return (NULL);
1541590Srgrimes    if (*re == '\0')
155282459Sbapt	return (NULL);
156282461Sbapt    cre = malloc(4 * strlen(re) + 3);
1571590Srgrimes    ccre = cre;
1581590Srgrimes    ure = re;
1591590Srgrimes
1601590Srgrimes    /* start the conversion with a \a */
1611590Srgrimes    *cre = META | OPT;
1621590Srgrimes    MSYM(cre) = 'a';
1631590Srgrimes    ccre = MNEXT(cre);
1641590Srgrimes
1651590Srgrimes    /* start the conversion (its recursive) */
1661590Srgrimes    expconv ();
1671590Srgrimes    *ccre = 0;
1681590Srgrimes    return (cre);
1691590Srgrimes}
1701590Srgrimes
1711590Srgrimesstatic void
1721590Srgrimesexpconv()
1731590Srgrimes{
1741590Srgrimes    register char *cs;		/* pointer to current symbol in converted exp */
1751590Srgrimes    register char c;		/* character being processed */
1761590Srgrimes    register char *acs;		/* pinter to last alternate */
1771590Srgrimes    register int temp;
1781590Srgrimes
1791590Srgrimes    /* let the conversion begin */
180282459Sbapt    acs = NULL;
181282459Sbapt    cs = NULL;
182282459Sbapt    while (*ure) {
1831590Srgrimes	switch (c = *ure++) {
1841590Srgrimes
1851590Srgrimes	case '\\':
1861590Srgrimes	    switch (c = *ure++) {
1871590Srgrimes
1881590Srgrimes	    /* escaped characters are just characters */
1891590Srgrimes	    default:
190282459Sbapt		if (cs == NULL || (*cs & STR) == 0) {
1911590Srgrimes		    cs = ccre;
1921590Srgrimes		    *cs = STR;
1931590Srgrimes		    SCNT(cs) = 1;
1941590Srgrimes		    ccre += 2;
1958874Srgrimes		} else
1961590Srgrimes		    SCNT(cs)++;
1971590Srgrimes		*ccre++ = c;
1981590Srgrimes		break;
1991590Srgrimes
2001590Srgrimes	    /* normal(?) metacharacters */
2011590Srgrimes	    case 'a':
2021590Srgrimes	    case 'd':
2031590Srgrimes	    case 'e':
2041590Srgrimes	    case 'p':
205282459Sbapt		if (acs != NULL && acs != cs) {
2061590Srgrimes		    do {
2071590Srgrimes			temp = OCNT(acs);
2088874Srgrimes			OCNT(acs) = ccre - acs;
2091590Srgrimes			acs -= temp;
2101590Srgrimes		    } while (temp != 0);
211282459Sbapt		    acs = NULL;
2121590Srgrimes		}
2131590Srgrimes		cs = ccre;
2141590Srgrimes		*cs = META;
2151590Srgrimes		MSYM(cs) = c;
2161590Srgrimes		ccre = MNEXT(cs);
2171590Srgrimes		break;
2181590Srgrimes	    }
2191590Srgrimes	    break;
2208874Srgrimes
2211590Srgrimes	/* just put the symbol in */
2221590Srgrimes	case '^':
2231590Srgrimes	case '$':
224282459Sbapt	    if (acs != NULL && acs != cs) {
2251590Srgrimes		do {
2261590Srgrimes		    temp = OCNT(acs);
2271590Srgrimes		    OCNT(acs) = ccre - acs;
2281590Srgrimes		    acs -= temp;
2291590Srgrimes		} while (temp != 0);
230282459Sbapt		acs = NULL;
2311590Srgrimes	    }
2321590Srgrimes	    cs = ccre;
2331590Srgrimes	    *cs = META;
2341590Srgrimes	    MSYM(cs) = c;
2351590Srgrimes	    ccre = MNEXT(cs);
2361590Srgrimes	    break;
2371590Srgrimes
2381590Srgrimes	/* mark the last match sequence as optional */
2391590Srgrimes	case '?':
2401590Srgrimes	    if (cs)
2411590Srgrimes	    	*cs = *cs | OPT;
2421590Srgrimes	    break;
2431590Srgrimes
2441590Srgrimes	/* recurse and define a subexpression */
2451590Srgrimes	case '(':
246282459Sbapt	    if (acs != NULL && acs != cs) {
2471590Srgrimes		do {
2481590Srgrimes		    temp = OCNT(acs);
2491590Srgrimes		    OCNT(acs) = ccre - acs;
2501590Srgrimes		    acs -= temp;
2511590Srgrimes		} while (temp != 0);
252282459Sbapt		acs = NULL;
2531590Srgrimes	    }
2541590Srgrimes	    cs = ccre;
2551590Srgrimes	    *cs = OPER;
2561590Srgrimes	    OSYM(cs) = '(';
2571590Srgrimes	    ccre = ONEXT(cs);
258282461Sbapt	    expconv();
2591590Srgrimes	    OCNT(cs) = ccre - cs;		/* offset to next symbol */
2601590Srgrimes	    break;
2611590Srgrimes
26219012Sjoerg	/* return from a recursion */
2631590Srgrimes	case ')':
264282459Sbapt	    if (acs != NULL) {
2651590Srgrimes		do {
2661590Srgrimes		    temp = OCNT(acs);
2671590Srgrimes		    OCNT(acs) = ccre - acs;
2681590Srgrimes		    acs -= temp;
2691590Srgrimes		} while (temp != 0);
270282459Sbapt		acs = NULL;
2711590Srgrimes	    }
2721590Srgrimes	    cs = ccre;
2731590Srgrimes	    *cs = META;
2741590Srgrimes	    MSYM(cs) = c;
2751590Srgrimes	    ccre = MNEXT(cs);
2761590Srgrimes	    return;
2771590Srgrimes
2781590Srgrimes	/* mark the last match sequence as having an alternate */
2791590Srgrimes	/* the third byte will contain an offset to jump over the */
2801590Srgrimes	/* alternate match in case the first did not fail */
2811590Srgrimes	case '|':
282282459Sbapt	    if (acs != NULL && acs != cs)
2831590Srgrimes		OCNT(ccre) = ccre - acs;	/* make a back pointer */
2841590Srgrimes	    else
2851590Srgrimes		OCNT(ccre) = 0;
2861590Srgrimes	    *cs |= ALT;
2871590Srgrimes	    cs = ccre;
2881590Srgrimes	    *cs = OPER;
2891590Srgrimes	    OSYM(cs) = '|';
2901590Srgrimes	    ccre = ONEXT(cs);
2911590Srgrimes	    acs = cs;	/* remember that the pointer is to be filles */
2921590Srgrimes	    break;
2931590Srgrimes
2941590Srgrimes	/* if its not a metasymbol just build a scharacter string */
2951590Srgrimes	default:
296282459Sbapt	    if (cs == NULL || (*cs & STR) == 0) {
2971590Srgrimes		cs = ccre;
2981590Srgrimes		*cs = STR;
2991590Srgrimes		SCNT(cs) = 1;
3001590Srgrimes		ccre = SSTR(cs);
3011590Srgrimes	    } else
3021590Srgrimes		SCNT(cs)++;
3031590Srgrimes	    *ccre++ = c;
3041590Srgrimes	    break;
3051590Srgrimes	}
3061590Srgrimes    }
307282459Sbapt    if (acs != NULL) {
3081590Srgrimes	do {
3091590Srgrimes	    temp = OCNT(acs);
3101590Srgrimes	    OCNT(acs) = ccre - acs;
3111590Srgrimes	    acs -= temp;
3121590Srgrimes	} while (temp != 0);
313282459Sbapt	acs = NULL;
3141590Srgrimes    }
3151590Srgrimes    return;
3161590Srgrimes}
3171590Srgrimes/* end of convertre */
3181590Srgrimes
3191590Srgrimes
3201590Srgrimes/*
321289677Seadler *	The following routine recognises an irregular expression
3221590Srgrimes *	with the following special characters:
3231590Srgrimes *
3241590Srgrimes *		\?	-	means last match was optional
3251590Srgrimes *		\a	-	matches any number of characters
3261590Srgrimes *		\d	-	matches any number of spaces and tabs
3271590Srgrimes *		\p	-	matches any number of alphanumeric
3281590Srgrimes *				characters. The
3291590Srgrimes *				characters matched will be copied into
3301590Srgrimes *				the area pointed to by 'name'.
3311590Srgrimes *		\|	-	alternation
3321590Srgrimes *		\( \)	-	grouping used mostly for alternation and
3331590Srgrimes *				optionality
3341590Srgrimes *
3351590Srgrimes *	The irregular expression must be translated to internal form
3361590Srgrimes *	prior to calling this routine
3371590Srgrimes *
3388874Srgrimes *	The value returned is the pointer to the first non \a
3391590Srgrimes *	character matched.
3401590Srgrimes */
3411590Srgrimes
342282449Sbapt/*
343282449Sbapt *  s: string to check for a match in
344282449Sbapt *  re: a converted irregular expression
345282449Sbapt *  mstring: where to put whatever matches a \p
346282449Sbapt */
3471590Srgrimeschar *
348282449Sbaptexpmatch (register char *s, register char *re, register char *mstring)
3491590Srgrimes{
3501590Srgrimes    register char *cs;		/* the current symbol */
3511590Srgrimes    register char *ptr,*s1;	/* temporary pointer */
352282457Sbapt    bool matched;	/* a temporary bool */
3531590Srgrimes
3541590Srgrimes    /* initial conditions */
355282459Sbapt    if (re == NULL)
356282459Sbapt	return (NULL);
3571590Srgrimes    cs = re;
358282457Sbapt    matched = false;
3591590Srgrimes
3601590Srgrimes    /* loop till expression string is exhausted (or at least pretty tired) */
3611590Srgrimes    while (*cs) {
3621590Srgrimes	switch (*cs & (OPER | STR | META)) {
3631590Srgrimes
3641590Srgrimes	/* try to match a string */
3651590Srgrimes	case STR:
3661590Srgrimes	    matched = !STRNCMP (s, SSTR(cs), SCNT(cs));
3671590Srgrimes	    if (matched) {
3681590Srgrimes
3691590Srgrimes		/* hoorah it matches */
3701590Srgrimes		s += SCNT(cs);
3711590Srgrimes		cs = SNEXT(cs);
3721590Srgrimes	    } else if (*cs & ALT) {
3731590Srgrimes
3741590Srgrimes		/* alternation, skip to next expression */
3751590Srgrimes		cs = SNEXT(cs);
3761590Srgrimes	    } else if (*cs & OPT) {
3771590Srgrimes
3781590Srgrimes		/* the match is optional */
3791590Srgrimes		cs = SNEXT(cs);
3801590Srgrimes		matched = 1;		/* indicate a successful match */
3811590Srgrimes	    } else {
3821590Srgrimes
3831590Srgrimes		/* no match, error return */
384282459Sbapt		return (NULL);
3851590Srgrimes	    }
3861590Srgrimes	    break;
3871590Srgrimes
3881590Srgrimes	/* an operator, do something fancy */
3891590Srgrimes	case OPER:
3901590Srgrimes	    switch (OSYM(cs)) {
3911590Srgrimes
3921590Srgrimes	    /* this is an alternation */
3931590Srgrimes	    case '|':
3941590Srgrimes		if (matched)
3951590Srgrimes
3961590Srgrimes		    /* last thing in the alternation was a match, skip ahead */
3971590Srgrimes		    cs = OPTR(cs);
3981590Srgrimes		else
3991590Srgrimes
4001590Srgrimes		    /* no match, keep trying */
4011590Srgrimes		    cs = ONEXT(cs);
4021590Srgrimes		break;
4031590Srgrimes
4041590Srgrimes	    /* this is a grouping, recurse */
4051590Srgrimes	    case '(':
406282461Sbapt		ptr = expmatch(s, ONEXT(cs), mstring);
407282459Sbapt		if (ptr != NULL) {
4081590Srgrimes
4091590Srgrimes		    /* the subexpression matched */
4101590Srgrimes		    matched = 1;
4111590Srgrimes		    s = ptr;
4121590Srgrimes		} else if (*cs & ALT) {
4131590Srgrimes
4141590Srgrimes		    /* alternation, skip to next expression */
4151590Srgrimes		    matched = 0;
4161590Srgrimes		} else if (*cs & OPT) {
4171590Srgrimes
4181590Srgrimes		    /* the match is optional */
4191590Srgrimes		    matched = 1;	/* indicate a successful match */
4201590Srgrimes		} else {
4211590Srgrimes
4221590Srgrimes		    /* no match, error return */
423282459Sbapt		    return (NULL);
4241590Srgrimes		}
4251590Srgrimes		cs = OPTR(cs);
4261590Srgrimes		break;
4271590Srgrimes	    }
4281590Srgrimes	    break;
4291590Srgrimes
4301590Srgrimes	/* try to match a metasymbol */
4311590Srgrimes	case META:
4321590Srgrimes	    switch (MSYM(cs)) {
4331590Srgrimes
4341590Srgrimes	    /* try to match anything and remember what was matched */
4351590Srgrimes	    case 'p':
4361590Srgrimes		/*
4371590Srgrimes		 *  This is really the same as trying the match the
4381590Srgrimes		 *  remaining parts of the expression to any subset
4391590Srgrimes		 *  of the string.
4401590Srgrimes		 */
4411590Srgrimes		s1 = s;
4421590Srgrimes		do {
443282461Sbapt		    ptr = expmatch(s1, MNEXT(cs), mstring);
444282459Sbapt		    if (ptr != NULL && s1 != s) {
4451590Srgrimes
4461590Srgrimes			/* we have a match, remember the match */
4471590Srgrimes			strncpy (mstring, s, s1 - s);
4481590Srgrimes			mstring[s1 - s] = '\0';
4491590Srgrimes			return (ptr);
450282459Sbapt		    } else if (ptr != NULL && (*cs & OPT)) {
4511590Srgrimes
4521590Srgrimes			/* it was aoptional so no match is ok */
4531590Srgrimes			return (ptr);
454282459Sbapt		    } else if (ptr != NULL) {
4551590Srgrimes
4561590Srgrimes			/* not optional and we still matched */
457282459Sbapt			return (NULL);
4581590Srgrimes		    }
45919019Sjoerg		    if (!(isalnum(*s1) || *s1 == '_' ||
46019019Sjoerg			  /* C++ destructor */
46119019Sjoerg			  *s1 == '~' ||
46219019Sjoerg			  /* C++ scope operator */
46319019Sjoerg			  (strlen(s1) > 1 && *s1 == ':' && s1[1] == ':' &&
464282457Sbapt			   (s1++, true))))
465282459Sbapt			return (NULL);
4661590Srgrimes		    if (*s1 == '\\')
467282457Sbapt			_escaped = _escaped ? false : true;
4681590Srgrimes		    else
469282457Sbapt			_escaped = false;
4701590Srgrimes		} while (*s1++);
471282459Sbapt		return (NULL);
4721590Srgrimes
4731590Srgrimes	    /* try to match anything */
4741590Srgrimes	    case 'a':
4751590Srgrimes		/*
4761590Srgrimes		 *  This is really the same as trying the match the
4771590Srgrimes		 *  remaining parts of the expression to any subset
4781590Srgrimes		 *  of the string.
4791590Srgrimes		 */
4801590Srgrimes		s1 = s;
4811590Srgrimes		do {
482282461Sbapt		    ptr = expmatch(s1, MNEXT(cs), mstring);
483282459Sbapt		    if (ptr != NULL && s1 != s) {
4841590Srgrimes
4851590Srgrimes			/* we have a match */
4861590Srgrimes			return (ptr);
487282459Sbapt		    } else if (ptr != NULL && (*cs & OPT)) {
4881590Srgrimes
4891590Srgrimes			/* it was aoptional so no match is ok */
4901590Srgrimes			return (ptr);
491282459Sbapt		    } else if (ptr != NULL) {
4921590Srgrimes
4931590Srgrimes			/* not optional and we still matched */
494282459Sbapt			return (NULL);
4951590Srgrimes		    }
4961590Srgrimes		    if (*s1 == '\\')
497282457Sbapt			_escaped = _escaped ? false : true;
4981590Srgrimes		    else
499282457Sbapt			_escaped = false;
5001590Srgrimes		} while (*s1++);
501282459Sbapt		return (NULL);
5021590Srgrimes
5031590Srgrimes	    /* fail if we are currently _escaped */
5041590Srgrimes	    case 'e':
5051590Srgrimes		if (_escaped)
506282459Sbapt		    return(NULL);
5078874Srgrimes		cs = MNEXT(cs);
5081590Srgrimes		break;
5091590Srgrimes
5101590Srgrimes	    /* match any number of tabs and spaces */
5111590Srgrimes	    case 'd':
5121590Srgrimes		ptr = s;
5131590Srgrimes		while (*s == ' ' || *s == '\t')
5141590Srgrimes		    s++;
51536053Sjb		if (s != ptr || s == s_start) {
5161590Srgrimes
5171590Srgrimes		    /* match, be happy */
5181590Srgrimes		    matched = 1;
5198874Srgrimes		    cs = MNEXT(cs);
5201590Srgrimes		} else if (*s == '\n' || *s == '\0') {
5211590Srgrimes
5221590Srgrimes		    /* match, be happy */
5231590Srgrimes		    matched = 1;
5248874Srgrimes		    cs = MNEXT(cs);
5251590Srgrimes		} else if (*cs & ALT) {
5261590Srgrimes
5271590Srgrimes		    /* try the next part */
5281590Srgrimes		    matched = 0;
5291590Srgrimes		    cs = MNEXT(cs);
5301590Srgrimes		} else if (*cs & OPT) {
5311590Srgrimes
5321590Srgrimes		    /* doesn't matter */
5331590Srgrimes		    matched = 1;
5341590Srgrimes		    cs = MNEXT(cs);
5351590Srgrimes		} else
5361590Srgrimes
5371590Srgrimes		    /* no match, error return */
538282459Sbapt		    return (NULL);
5391590Srgrimes		break;
5401590Srgrimes
5411590Srgrimes	    /* check for end of line */
5421590Srgrimes	    case '$':
5431590Srgrimes		if (*s == '\0' || *s == '\n') {
5441590Srgrimes
5451590Srgrimes		    /* match, be happy */
5461590Srgrimes		    s++;
5471590Srgrimes		    matched = 1;
5481590Srgrimes		    cs = MNEXT(cs);
5491590Srgrimes		} else if (*cs & ALT) {
5501590Srgrimes
5511590Srgrimes		    /* try the next part */
5521590Srgrimes		    matched = 0;
5531590Srgrimes		    cs = MNEXT(cs);
5541590Srgrimes		} else if (*cs & OPT) {
5551590Srgrimes
5561590Srgrimes		    /* doesn't matter */
5571590Srgrimes		    matched = 1;
5581590Srgrimes		    cs = MNEXT(cs);
5591590Srgrimes		} else
5601590Srgrimes
5611590Srgrimes		    /* no match, error return */
562282459Sbapt		    return (NULL);
5631590Srgrimes		break;
5641590Srgrimes
5651590Srgrimes	    /* check for start of line */
5661590Srgrimes	    case '^':
56736053Sjb		if (s == s_start) {
5681590Srgrimes
5691590Srgrimes		    /* match, be happy */
5701590Srgrimes		    matched = 1;
5711590Srgrimes		    cs = MNEXT(cs);
5721590Srgrimes		} else if (*cs & ALT) {
5731590Srgrimes
5741590Srgrimes		    /* try the next part */
5751590Srgrimes		    matched = 0;
5761590Srgrimes		    cs = MNEXT(cs);
5771590Srgrimes		} else if (*cs & OPT) {
5781590Srgrimes
5791590Srgrimes		    /* doesn't matter */
5801590Srgrimes		    matched = 1;
5811590Srgrimes		    cs = MNEXT(cs);
5821590Srgrimes		} else
5831590Srgrimes
5841590Srgrimes		    /* no match, error return */
585282459Sbapt		    return (NULL);
5861590Srgrimes		break;
5871590Srgrimes
5881590Srgrimes	    /* end of a subexpression, return success */
5891590Srgrimes	    case ')':
5901590Srgrimes		return (s);
5911590Srgrimes	    }
5921590Srgrimes	    break;
5931590Srgrimes	}
5941590Srgrimes    }
5951590Srgrimes    return (s);
5961590Srgrimes}
597