Home | History | Annotate | Line # | Download | only in vgrind
regexp.c revision 1.13.24.1
      1  1.13.24.1    martin /*	$NetBSD: regexp.c,v 1.13.24.1 2020/04/08 14:09:18 martin Exp $	*/
      2        1.3       jtc 
      3        1.1       cgd /*
      4        1.3       jtc  * Copyright (c) 1980, 1993
      5        1.3       jtc  *	The Regents of the University of California.  All rights reserved.
      6        1.3       jtc  *
      7        1.1       cgd  *
      8        1.1       cgd  * Redistribution and use in source and binary forms, with or without
      9        1.1       cgd  * modification, are permitted provided that the following conditions
     10        1.1       cgd  * are met:
     11        1.1       cgd  * 1. Redistributions of source code must retain the above copyright
     12        1.1       cgd  *    notice, this list of conditions and the following disclaimer.
     13        1.1       cgd  * 2. Redistributions in binary form must reproduce the above copyright
     14        1.1       cgd  *    notice, this list of conditions and the following disclaimer in the
     15        1.1       cgd  *    documentation and/or other materials provided with the distribution.
     16        1.7       agc  * 3. Neither the name of the University nor the names of its contributors
     17        1.1       cgd  *    may be used to endorse or promote products derived from this software
     18        1.1       cgd  *    without specific prior written permission.
     19        1.1       cgd  *
     20        1.1       cgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     21        1.1       cgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     22        1.1       cgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     23        1.1       cgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     24        1.1       cgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     25        1.1       cgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     26        1.1       cgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     27        1.1       cgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     28        1.1       cgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     29        1.1       cgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     30        1.1       cgd  * SUCH DAMAGE.
     31        1.1       cgd  */
     32        1.1       cgd 
     33       1.13  christos #if HAVE_NBTOOL_CONFIG_H
     34       1.13  christos #include "nbtool_config.h"
     35       1.13  christos #endif
     36       1.13  christos 
     37        1.4     lukem #include <sys/cdefs.h>
     38        1.1       cgd #ifndef lint
     39       1.11     lukem __COPYRIGHT("@(#) Copyright (c) 1980, 1993\
     40       1.11     lukem  The Regents of the University of California.  All rights reserved.");
     41        1.3       jtc #endif /* not lint */
     42        1.3       jtc 
     43        1.3       jtc #ifndef lint
     44        1.3       jtc #if 0
     45        1.3       jtc static char sccsid[] = "@(#)regexp.c	8.1 (Berkeley) 6/6/93";
     46        1.3       jtc #endif
     47  1.13.24.1    martin __RCSID("$NetBSD: regexp.c,v 1.13.24.1 2020/04/08 14:09:18 martin Exp $");
     48        1.1       cgd #endif /* not lint */
     49        1.1       cgd 
     50       1.10  christos #include <assert.h>
     51        1.1       cgd #include <ctype.h>
     52        1.3       jtc #include <stdlib.h>
     53       1.13  christos #include <stdbool.h>
     54        1.3       jtc #include <string.h>
     55        1.3       jtc #include "extern.h"
     56        1.1       cgd 
     57       1.13  christos static void	expconv (void);
     58        1.3       jtc 
     59       1.13  christos bool	 x_escaped;	/* true if we are currently x_escaped */
     60        1.5  christos char	*x_start;	/* start of string */
     61       1.13  christos bool	 l_onecase;	/* true if upper and lower equivalent */
     62        1.1       cgd 
     63        1.8       dsl #define makelower(c) (isupper((unsigned char)(c)) ? tolower((unsigned char)(c)) : (c))
     64        1.1       cgd 
     65       1.13  christos static char
     66       1.13  christos tocc(ptrdiff_t x) {
     67       1.13  christos 	if (x & ~0xff)
     68       1.13  christos 		abort();
     69       1.13  christos 	return (char)x;
     70       1.13  christos }
     71       1.13  christos 
     72        1.1       cgd /*  STRNCMP -	like strncmp except that we convert the
     73        1.1       cgd  *	 	first string to lower case before comparing
     74        1.1       cgd  *		if l_onecase is set.
     75        1.1       cgd  */
     76        1.1       cgd 
     77        1.3       jtc int
     78       1.12      matt STRNCMP(char *s1, char *s2, int len)
     79        1.1       cgd {
     80        1.1       cgd 	if (l_onecase) {
     81        1.1       cgd 	    do
     82        1.1       cgd 		if (*s2 - makelower(*s1))
     83       1.13  christos 			return *s2 - makelower(*s1);
     84        1.1       cgd 		else {
     85        1.1       cgd 			s2++;
     86        1.1       cgd 			s1++;
     87        1.1       cgd 		}
     88        1.1       cgd 	    while (--len);
     89        1.1       cgd 	} else {
     90        1.1       cgd 	    do
     91        1.1       cgd 		if (*s2 - *s1)
     92       1.13  christos 			return *s2 - *s1;
     93        1.1       cgd 		else {
     94        1.1       cgd 			s2++;
     95        1.1       cgd 			s1++;
     96        1.1       cgd 		}
     97        1.1       cgd 	    while (--len);
     98        1.1       cgd 	}
     99        1.1       cgd 	return(0);
    100        1.1       cgd }
    101        1.1       cgd 
    102        1.1       cgd /*	The following routine converts an irregular expression to
    103        1.1       cgd  *	internal format.
    104        1.1       cgd  *
    105        1.1       cgd  *	Either meta symbols (\a \d or \p) or character strings or
    106        1.1       cgd  *	operations ( alternation or perenthesizing ) can be
    107        1.1       cgd  *	specified.  Each starts with a descriptor byte.  The descriptor
    108        1.1       cgd  *	byte has STR set for strings, META set for meta symbols
    109        1.1       cgd  *	and OPER set for operations.
    110        1.1       cgd  *	The descriptor byte can also have the OPT bit set if the object
    111        1.1       cgd  *	defined is optional.  Also ALT can be set to indicate an alternation.
    112        1.1       cgd  *
    113        1.1       cgd  *	For metasymbols the byte following the descriptor byte identities
    114        1.1       cgd  *	the meta symbol (containing an ascii 'a', 'd', 'p', '|', or '(').  For
    115        1.1       cgd  *	strings the byte after the descriptor is a character count for
    116        1.1       cgd  *	the string:
    117        1.1       cgd  *
    118        1.1       cgd  *		meta symbols := descriptor
    119        1.1       cgd  *				symbol
    120        1.1       cgd  *
    121        1.1       cgd  *		strings :=	descriptor
    122        1.1       cgd  *				character count
    123        1.1       cgd  *				the string
    124        1.1       cgd  *
    125        1.1       cgd  *		operatins :=	descriptor
    126        1.1       cgd  *				symbol
    127        1.1       cgd  *				character count
    128        1.1       cgd  */
    129        1.1       cgd 
    130        1.1       cgd /*
    131        1.1       cgd  *  handy macros for accessing parts of match blocks
    132        1.1       cgd  */
    133        1.1       cgd #define MSYM(A) (*(A+1))	/* symbol in a meta symbol block */
    134        1.1       cgd #define MNEXT(A) (A+2)		/* character following a metasymbol block */
    135        1.1       cgd 
    136        1.1       cgd #define OSYM(A) (*(A+1))	/* symbol in an operation block */
    137        1.1       cgd #define OCNT(A) (*(A+2))	/* character count */
    138        1.1       cgd #define ONEXT(A) (A+3)		/* next character after the operation */
    139        1.1       cgd #define OPTR(A) (A+*(A+2))	/* place pointed to by the operator */
    140        1.1       cgd 
    141        1.1       cgd #define SCNT(A) (*(A+1))	/* byte count of a string */
    142        1.1       cgd #define SSTR(A) (A+2)		/* address of the string */
    143        1.1       cgd #define SNEXT(A) (A+2+*(A+1))	/* character following the string */
    144        1.1       cgd 
    145        1.1       cgd /*
    146        1.1       cgd  *  bit flags in the descriptor
    147        1.1       cgd  */
    148        1.1       cgd #define OPT 1
    149        1.1       cgd #define STR 2
    150        1.1       cgd #define META 4
    151        1.1       cgd #define ALT 8
    152        1.1       cgd #define OPER 16
    153        1.1       cgd 
    154        1.3       jtc static char *ccre;	/* pointer to current position in converted exp*/
    155        1.3       jtc static char *ure;	/* pointer current position in unconverted exp */
    156        1.1       cgd 
    157        1.1       cgd char *
    158       1.12      matt convexp(char *re)	/* unconverted irregular expression */
    159        1.1       cgd {
    160        1.4     lukem     char *cre;		/* pointer to converted regular expression */
    161        1.1       cgd 
    162        1.1       cgd     /* allocate room for the converted expression */
    163       1.13  christos     if (re == NULL)
    164       1.13  christos 	return NULL;
    165        1.1       cgd     if (*re == '\0')
    166       1.13  christos 	return NULL;
    167        1.6    itojun     cre = malloc(4 * strlen(re) + 3);
    168        1.1       cgd     ccre = cre;
    169        1.1       cgd     ure = re;
    170        1.1       cgd 
    171        1.1       cgd     /* start the conversion with a \a */
    172        1.1       cgd     *cre = META | OPT;
    173        1.1       cgd     MSYM(cre) = 'a';
    174        1.1       cgd     ccre = MNEXT(cre);
    175        1.1       cgd 
    176        1.1       cgd     /* start the conversion (its recursive) */
    177        1.6    itojun     expconv();
    178        1.1       cgd     *ccre = 0;
    179       1.13  christos     return cre;
    180        1.1       cgd }
    181        1.1       cgd 
    182        1.3       jtc static void
    183       1.12      matt expconv(void)
    184        1.1       cgd {
    185        1.4     lukem     char *cs;		/* pointer to current symbol in converted exp */
    186        1.4     lukem     char c;		/* character being processed */
    187        1.4     lukem     char *acs;		/* pinter to last alternate */
    188        1.4     lukem     int temp;
    189        1.1       cgd 
    190        1.1       cgd     /* let the conversion begin */
    191       1.13  christos     acs = NULL;
    192       1.13  christos     cs = NULL;
    193       1.13  christos     while (*ure) {
    194        1.1       cgd 	switch (c = *ure++) {
    195        1.1       cgd 
    196        1.1       cgd 	case '\\':
    197        1.1       cgd 	    switch (c = *ure++) {
    198        1.1       cgd 
    199        1.1       cgd 	    /* escaped characters are just characters */
    200        1.1       cgd 	    default:
    201       1.13  christos 		if (cs == NULL || (*cs & STR) == 0) {
    202        1.1       cgd 		    cs = ccre;
    203        1.1       cgd 		    *cs = STR;
    204        1.1       cgd 		    SCNT(cs) = 1;
    205        1.1       cgd 		    ccre += 2;
    206        1.1       cgd 		} else
    207        1.1       cgd 		    SCNT(cs)++;
    208        1.1       cgd 		*ccre++ = c;
    209        1.1       cgd 		break;
    210        1.1       cgd 
    211        1.1       cgd 	    /* normal(?) metacharacters */
    212        1.1       cgd 	    case 'a':
    213        1.1       cgd 	    case 'd':
    214        1.1       cgd 	    case 'e':
    215        1.1       cgd 	    case 'p':
    216       1.13  christos 		if (acs != NULL && acs != cs) {
    217        1.1       cgd 		    do {
    218        1.1       cgd 			temp = OCNT(acs);
    219       1.13  christos 			OCNT(acs) = tocc(ccre - acs);
    220        1.1       cgd 			acs -= temp;
    221        1.1       cgd 		    } while (temp != 0);
    222       1.13  christos 		    acs = NULL;
    223        1.1       cgd 		}
    224        1.1       cgd 		cs = ccre;
    225        1.1       cgd 		*cs = META;
    226        1.1       cgd 		MSYM(cs) = c;
    227        1.1       cgd 		ccre = MNEXT(cs);
    228        1.1       cgd 		break;
    229        1.1       cgd 	    }
    230        1.1       cgd 	    break;
    231        1.1       cgd 
    232        1.1       cgd 	/* just put the symbol in */
    233        1.1       cgd 	case '^':
    234        1.1       cgd 	case '$':
    235       1.13  christos 	    if (acs != NULL && acs != cs) {
    236        1.1       cgd 		do {
    237        1.1       cgd 		    temp = OCNT(acs);
    238       1.13  christos 		    OCNT(acs) = tocc(ccre - acs);
    239        1.1       cgd 		    acs -= temp;
    240        1.1       cgd 		} while (temp != 0);
    241       1.13  christos 		acs = NULL;
    242        1.1       cgd 	    }
    243        1.1       cgd 	    cs = ccre;
    244        1.1       cgd 	    *cs = META;
    245        1.1       cgd 	    MSYM(cs) = c;
    246        1.1       cgd 	    ccre = MNEXT(cs);
    247        1.1       cgd 	    break;
    248        1.1       cgd 
    249        1.1       cgd 	/* mark the last match sequence as optional */
    250        1.1       cgd 	case '?':
    251        1.1       cgd 	    if (cs)
    252        1.1       cgd 	    	*cs = *cs | OPT;
    253        1.1       cgd 	    break;
    254        1.1       cgd 
    255        1.1       cgd 	/* recurse and define a subexpression */
    256        1.1       cgd 	case '(':
    257       1.13  christos 	    if (acs != NULL && acs != cs) {
    258        1.1       cgd 		do {
    259        1.1       cgd 		    temp = OCNT(acs);
    260       1.13  christos 		    OCNT(acs) = tocc(ccre - acs);
    261        1.1       cgd 		    acs -= temp;
    262        1.1       cgd 		} while (temp != 0);
    263       1.13  christos 		acs = NULL;
    264        1.1       cgd 	    }
    265        1.1       cgd 	    cs = ccre;
    266        1.1       cgd 	    *cs = OPER;
    267        1.1       cgd 	    OSYM(cs) = '(';
    268        1.1       cgd 	    ccre = ONEXT(cs);
    269        1.6    itojun 	    expconv();
    270       1.13  christos 	    OCNT(cs) = tocc(ccre - cs);		/* offset to next symbol */
    271        1.1       cgd 	    break;
    272        1.1       cgd 
    273        1.3       jtc 	/* reurn from a recursion */
    274        1.1       cgd 	case ')':
    275       1.13  christos 	    if (acs != NULL) {
    276        1.1       cgd 		do {
    277        1.1       cgd 		    temp = OCNT(acs);
    278       1.13  christos 		    OCNT(acs) = tocc(ccre - acs);
    279        1.1       cgd 		    acs -= temp;
    280        1.1       cgd 		} while (temp != 0);
    281       1.13  christos 		acs = NULL;
    282        1.1       cgd 	    }
    283        1.1       cgd 	    cs = ccre;
    284        1.1       cgd 	    *cs = META;
    285        1.1       cgd 	    MSYM(cs) = c;
    286        1.1       cgd 	    ccre = MNEXT(cs);
    287        1.1       cgd 	    return;
    288        1.1       cgd 
    289        1.1       cgd 	/* mark the last match sequence as having an alternate */
    290        1.1       cgd 	/* the third byte will contain an offset to jump over the */
    291        1.1       cgd 	/* alternate match in case the first did not fail */
    292        1.1       cgd 	case '|':
    293       1.13  christos 	    if (acs != NULL && acs != cs)
    294       1.13  christos 		OCNT(ccre) = tocc(ccre - acs);	/* make a back pointer */
    295        1.1       cgd 	    else
    296        1.1       cgd 		OCNT(ccre) = 0;
    297        1.9  christos 	    assert(cs != NULL);
    298        1.1       cgd 	    *cs |= ALT;
    299        1.1       cgd 	    cs = ccre;
    300        1.1       cgd 	    *cs = OPER;
    301        1.1       cgd 	    OSYM(cs) = '|';
    302        1.1       cgd 	    ccre = ONEXT(cs);
    303        1.1       cgd 	    acs = cs;	/* remember that the pointer is to be filles */
    304        1.1       cgd 	    break;
    305        1.1       cgd 
    306        1.1       cgd 	/* if its not a metasymbol just build a scharacter string */
    307        1.1       cgd 	default:
    308       1.13  christos 	    if (cs == NULL || (*cs & STR) == 0) {
    309        1.1       cgd 		cs = ccre;
    310        1.1       cgd 		*cs = STR;
    311        1.1       cgd 		SCNT(cs) = 1;
    312        1.1       cgd 		ccre = SSTR(cs);
    313        1.1       cgd 	    } else
    314        1.1       cgd 		SCNT(cs)++;
    315        1.1       cgd 	    *ccre++ = c;
    316        1.1       cgd 	    break;
    317        1.1       cgd 	}
    318        1.1       cgd     }
    319       1.13  christos     if (acs != NULL) {
    320        1.1       cgd 	do {
    321        1.1       cgd 	    temp = OCNT(acs);
    322       1.13  christos 	    OCNT(acs) = tocc(ccre - acs);
    323        1.1       cgd 	    acs -= temp;
    324        1.1       cgd 	} while (temp != 0);
    325       1.13  christos 	acs = NULL;
    326        1.1       cgd     }
    327        1.1       cgd     return;
    328        1.1       cgd }
    329        1.1       cgd /* end of convertre */
    330        1.1       cgd 
    331        1.1       cgd 
    332        1.1       cgd /*
    333  1.13.24.1    martin  *	The following routine recognises an irregular expression
    334        1.1       cgd  *	with the following special characters:
    335        1.1       cgd  *
    336        1.1       cgd  *		\?	-	means last match was optional
    337        1.1       cgd  *		\a	-	matches any number of characters
    338        1.1       cgd  *		\d	-	matches any number of spaces and tabs
    339        1.1       cgd  *		\p	-	matches any number of alphanumeric
    340        1.1       cgd  *				characters. The
    341        1.1       cgd  *				characters matched will be copied into
    342        1.1       cgd  *				the area pointed to by 'name'.
    343        1.1       cgd  *		\|	-	alternation
    344        1.1       cgd  *		\( \)	-	grouping used mostly for alternation and
    345        1.1       cgd  *				optionality
    346        1.1       cgd  *
    347        1.1       cgd  *	The irregular expression must be translated to internal form
    348        1.1       cgd  *	prior to calling this routine
    349        1.1       cgd  *
    350        1.1       cgd  *	The value returned is the pointer to the first non \a
    351        1.1       cgd  *	character matched.
    352        1.1       cgd  */
    353        1.1       cgd 
    354        1.1       cgd char *
    355       1.12      matt expmatch(
    356       1.12      matt     char *s,		/* string to check for a match in */
    357       1.12      matt     char *re,		/* a converted irregular expression */
    358       1.12      matt     char *mstring)	/* where to put whatever matches a \p */
    359        1.1       cgd {
    360        1.4     lukem     char *cs;		/* the current symbol */
    361        1.4     lukem     char *ptr,*s1;	/* temporary pointer */
    362       1.13  christos     bool matched;	/* a temporary bool */
    363        1.1       cgd 
    364        1.1       cgd     /* initial conditions */
    365       1.13  christos     if (re == NULL)
    366       1.13  christos 	return NULL;
    367        1.1       cgd     cs = re;
    368       1.13  christos     matched = false;
    369        1.1       cgd 
    370        1.1       cgd     /* loop till expression string is exhausted (or at least pretty tired) */
    371        1.1       cgd     while (*cs) {
    372        1.1       cgd 	switch (*cs & (OPER | STR | META)) {
    373        1.1       cgd 
    374        1.1       cgd 	/* try to match a string */
    375        1.1       cgd 	case STR:
    376        1.1       cgd 	    matched = !STRNCMP (s, SSTR(cs), SCNT(cs));
    377        1.1       cgd 	    if (matched) {
    378        1.1       cgd 
    379        1.1       cgd 		/* hoorah it matches */
    380        1.1       cgd 		s += SCNT(cs);
    381        1.1       cgd 		cs = SNEXT(cs);
    382        1.1       cgd 	    } else if (*cs & ALT) {
    383        1.1       cgd 
    384        1.1       cgd 		/* alternation, skip to next expression */
    385        1.1       cgd 		cs = SNEXT(cs);
    386        1.1       cgd 	    } else if (*cs & OPT) {
    387        1.1       cgd 
    388        1.1       cgd 		/* the match is optional */
    389        1.1       cgd 		cs = SNEXT(cs);
    390        1.1       cgd 		matched = 1;		/* indicate a successful match */
    391        1.1       cgd 	    } else {
    392        1.1       cgd 
    393        1.1       cgd 		/* no match, error return */
    394       1.13  christos 		return NULL;
    395        1.1       cgd 	    }
    396        1.1       cgd 	    break;
    397        1.1       cgd 
    398        1.1       cgd 	/* an operator, do something fancy */
    399        1.1       cgd 	case OPER:
    400        1.1       cgd 	    switch (OSYM(cs)) {
    401        1.1       cgd 
    402        1.1       cgd 	    /* this is an alternation */
    403        1.1       cgd 	    case '|':
    404        1.1       cgd 		if (matched)
    405        1.1       cgd 
    406        1.1       cgd 		    /* last thing in the alternation was a match, skip ahead */
    407        1.1       cgd 		    cs = OPTR(cs);
    408        1.1       cgd 		else
    409        1.1       cgd 
    410        1.1       cgd 		    /* no match, keep trying */
    411        1.1       cgd 		    cs = ONEXT(cs);
    412        1.1       cgd 		break;
    413        1.1       cgd 
    414        1.1       cgd 	    /* this is a grouping, recurse */
    415        1.1       cgd 	    case '(':
    416        1.6    itojun 		ptr = expmatch(s, ONEXT(cs), mstring);
    417       1.13  christos 		if (ptr != NULL) {
    418        1.1       cgd 
    419        1.1       cgd 		    /* the subexpression matched */
    420        1.1       cgd 		    matched = 1;
    421        1.1       cgd 		    s = ptr;
    422        1.1       cgd 		} else if (*cs & ALT) {
    423        1.1       cgd 
    424        1.1       cgd 		    /* alternation, skip to next expression */
    425        1.1       cgd 		    matched = 0;
    426        1.1       cgd 		} else if (*cs & OPT) {
    427        1.1       cgd 
    428        1.1       cgd 		    /* the match is optional */
    429        1.1       cgd 		    matched = 1;	/* indicate a successful match */
    430        1.1       cgd 		} else {
    431        1.1       cgd 
    432        1.1       cgd 		    /* no match, error return */
    433       1.13  christos 		    return NULL;
    434        1.1       cgd 		}
    435        1.1       cgd 		cs = OPTR(cs);
    436        1.1       cgd 		break;
    437        1.1       cgd 	    }
    438        1.1       cgd 	    break;
    439        1.1       cgd 
    440        1.1       cgd 	/* try to match a metasymbol */
    441        1.1       cgd 	case META:
    442        1.1       cgd 	    switch (MSYM(cs)) {
    443        1.1       cgd 
    444        1.1       cgd 	    /* try to match anything and remember what was matched */
    445        1.1       cgd 	    case 'p':
    446        1.1       cgd 		/*
    447        1.1       cgd 		 *  This is really the same as trying the match the
    448        1.1       cgd 		 *  remaining parts of the expression to any subset
    449        1.1       cgd 		 *  of the string.
    450        1.1       cgd 		 */
    451        1.1       cgd 		s1 = s;
    452        1.1       cgd 		do {
    453        1.6    itojun 		    ptr = expmatch(s1, MNEXT(cs), mstring);
    454       1.13  christos 		    if (ptr != NULL && s1 != s) {
    455        1.1       cgd 
    456        1.1       cgd 			/* we have a match, remember the match */
    457       1.13  christos 			strncpy(mstring, s, (size_t)(s1 - s));
    458        1.1       cgd 			mstring[s1 - s] = '\0';
    459       1.13  christos 			return ptr;
    460       1.13  christos 		    } else if (ptr != NULL && (*cs & OPT)) {
    461        1.1       cgd 
    462        1.1       cgd 			/* it was aoptional so no match is ok */
    463       1.13  christos 			return ptr;
    464       1.13  christos 		    } else if (ptr != NULL) {
    465        1.1       cgd 
    466        1.1       cgd 			/* not optional and we still matched */
    467       1.13  christos 			return NULL;
    468        1.1       cgd 		    }
    469        1.5  christos 		    if (!isalnum((unsigned char)*s1) && *s1 != '_')
    470       1.13  christos 			return NULL;
    471        1.1       cgd 		    if (*s1 == '\\')
    472       1.13  christos 			x_escaped = x_escaped ? false : true;
    473        1.1       cgd 		    else
    474       1.13  christos 			x_escaped = false;
    475        1.1       cgd 		} while (*s1++);
    476       1.13  christos 		return NULL;
    477        1.1       cgd 
    478        1.1       cgd 	    /* try to match anything */
    479        1.1       cgd 	    case 'a':
    480        1.1       cgd 		/*
    481        1.1       cgd 		 *  This is really the same as trying the match the
    482        1.1       cgd 		 *  remaining parts of the expression to any subset
    483        1.1       cgd 		 *  of the string.
    484        1.1       cgd 		 */
    485        1.1       cgd 		s1 = s;
    486        1.1       cgd 		do {
    487        1.6    itojun 		    ptr = expmatch(s1, MNEXT(cs), mstring);
    488       1.13  christos 		    if (ptr != NULL && s1 != s) {
    489        1.1       cgd 
    490        1.1       cgd 			/* we have a match */
    491       1.13  christos 			return ptr;
    492       1.13  christos 		    } else if (ptr != NULL && (*cs & OPT)) {
    493        1.1       cgd 
    494        1.1       cgd 			/* it was aoptional so no match is ok */
    495       1.13  christos 			return ptr;
    496       1.13  christos 		    } else if (ptr != NULL) {
    497        1.1       cgd 
    498        1.1       cgd 			/* not optional and we still matched */
    499       1.13  christos 			return NULL;
    500        1.1       cgd 		    }
    501        1.1       cgd 		    if (*s1 == '\\')
    502       1.13  christos 			x_escaped = x_escaped ? false : true;
    503        1.1       cgd 		    else
    504       1.13  christos 			x_escaped = false;
    505        1.1       cgd 		} while (*s1++);
    506       1.13  christos 		return NULL;
    507        1.1       cgd 
    508        1.5  christos 	    /* fail if we are currently x_escaped */
    509        1.1       cgd 	    case 'e':
    510        1.5  christos 		if (x_escaped)
    511       1.13  christos 		    return NULL;
    512        1.1       cgd 		cs = MNEXT(cs);
    513        1.1       cgd 		break;
    514        1.1       cgd 
    515        1.1       cgd 	    /* match any number of tabs and spaces */
    516        1.1       cgd 	    case 'd':
    517        1.1       cgd 		ptr = s;
    518        1.1       cgd 		while (*s == ' ' || *s == '\t')
    519        1.1       cgd 		    s++;
    520        1.5  christos 		if (s != ptr || s == x_start) {
    521        1.1       cgd 
    522        1.1       cgd 		    /* match, be happy */
    523        1.1       cgd 		    matched = 1;
    524        1.1       cgd 		    cs = MNEXT(cs);
    525        1.1       cgd 		} else if (*s == '\n' || *s == '\0') {
    526        1.1       cgd 
    527        1.1       cgd 		    /* match, be happy */
    528        1.1       cgd 		    matched = 1;
    529        1.1       cgd 		    cs = MNEXT(cs);
    530        1.1       cgd 		} else if (*cs & ALT) {
    531        1.1       cgd 
    532        1.1       cgd 		    /* try the next part */
    533        1.1       cgd 		    matched = 0;
    534        1.1       cgd 		    cs = MNEXT(cs);
    535        1.1       cgd 		} else if (*cs & OPT) {
    536        1.1       cgd 
    537        1.1       cgd 		    /* doesn't matter */
    538        1.1       cgd 		    matched = 1;
    539        1.1       cgd 		    cs = MNEXT(cs);
    540        1.1       cgd 		} else
    541        1.1       cgd 
    542        1.1       cgd 		    /* no match, error return */
    543       1.13  christos 		    return NULL;
    544        1.1       cgd 		break;
    545        1.1       cgd 
    546        1.1       cgd 	    /* check for end of line */
    547        1.1       cgd 	    case '$':
    548        1.1       cgd 		if (*s == '\0' || *s == '\n') {
    549        1.1       cgd 
    550        1.1       cgd 		    /* match, be happy */
    551        1.1       cgd 		    s++;
    552        1.1       cgd 		    matched = 1;
    553        1.1       cgd 		    cs = MNEXT(cs);
    554        1.1       cgd 		} else if (*cs & ALT) {
    555        1.1       cgd 
    556        1.1       cgd 		    /* try the next part */
    557        1.1       cgd 		    matched = 0;
    558        1.1       cgd 		    cs = MNEXT(cs);
    559        1.1       cgd 		} else if (*cs & OPT) {
    560        1.1       cgd 
    561        1.1       cgd 		    /* doesn't matter */
    562        1.1       cgd 		    matched = 1;
    563        1.1       cgd 		    cs = MNEXT(cs);
    564        1.1       cgd 		} else
    565        1.1       cgd 
    566        1.1       cgd 		    /* no match, error return */
    567       1.13  christos 		    return NULL;
    568        1.1       cgd 		break;
    569        1.1       cgd 
    570        1.1       cgd 	    /* check for start of line */
    571        1.1       cgd 	    case '^':
    572        1.5  christos 		if (s == x_start) {
    573        1.1       cgd 
    574        1.1       cgd 		    /* match, be happy */
    575        1.1       cgd 		    matched = 1;
    576        1.1       cgd 		    cs = MNEXT(cs);
    577        1.1       cgd 		} else if (*cs & ALT) {
    578        1.1       cgd 
    579        1.1       cgd 		    /* try the next part */
    580        1.1       cgd 		    matched = 0;
    581        1.1       cgd 		    cs = MNEXT(cs);
    582        1.1       cgd 		} else if (*cs & OPT) {
    583        1.1       cgd 
    584        1.1       cgd 		    /* doesn't matter */
    585        1.1       cgd 		    matched = 1;
    586        1.1       cgd 		    cs = MNEXT(cs);
    587        1.1       cgd 		} else
    588        1.1       cgd 
    589        1.1       cgd 		    /* no match, error return */
    590       1.13  christos 		    return NULL;
    591        1.1       cgd 		break;
    592        1.1       cgd 
    593        1.1       cgd 	    /* end of a subexpression, return success */
    594        1.1       cgd 	    case ')':
    595       1.13  christos 		return s;
    596        1.1       cgd 	    }
    597        1.1       cgd 	    break;
    598        1.1       cgd 	}
    599        1.1       cgd     }
    600       1.13  christos     return s;
    601        1.1       cgd }
    602