1219019Sgabor/*
2219019Sgabor * Copyright (c) 1980, 1993
3219019Sgabor *	The Regents of the University of California.  All rights reserved.
4219019Sgabor *
5219019Sgabor *
6219019Sgabor * Redistribution and use in source and binary forms, with or without
7219019Sgabor * modification, are permitted provided that the following conditions
8219019Sgabor * are met:
9219019Sgabor * 1. Redistributions of source code must retain the above copyright
10219019Sgabor *    notice, this list of conditions and the following disclaimer.
11219019Sgabor * 2. Redistributions in binary form must reproduce the above copyright
12219019Sgabor *    notice, this list of conditions and the following disclaimer in the
13219019Sgabor *    documentation and/or other materials provided with the distribution.
14219019Sgabor * 4. Neither the name of the University nor the names of its contributors
15219019Sgabor *    may be used to endorse or promote products derived from this software
16219019Sgabor *    without specific prior written permission.
17219019Sgabor *
18219019Sgabor * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19219019Sgabor * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20219019Sgabor * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21219019Sgabor * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22219019Sgabor * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23219019Sgabor * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24219019Sgabor * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25219019Sgabor * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26219019Sgabor * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27219019Sgabor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28219019Sgabor * SUCH DAMAGE.
29219019Sgabor */
30219019Sgabor
31219019Sgabor#include <sys/cdefs.h>
32219019Sgabor
33219019Sgabor__FBSDID("$FreeBSD$");
34219019Sgabor
35219019Sgabor#ifndef lint
36219019Sgaborstatic const char copyright[] =
37219019Sgabor"@(#) Copyright (c) 1980, 1993\n\
38219019Sgabor	The Regents of the University of California.  All rights reserved.\n";
39219019Sgabor#endif
40219019Sgabor
41219019Sgabor#ifndef lint
42219019Sgaborstatic const char sccsid[] = "@(#)regexp.c	8.1 (Berkeley) 6/6/93";
43219019Sgabor#endif
44219019Sgabor
45219019Sgabor#include <ctype.h>
46219019Sgabor#include <stdlib.h>
47219019Sgabor#include <string.h>
48219019Sgabor
49219019Sgabor#include "extern.h"
50219019Sgabor
51219019Sgabor#define FALSE	0
52219019Sgabor#define TRUE	!(FALSE)
53219019Sgabor#define NIL	0
54219019Sgabor
55219019Sgaborstatic void	expconv(void);
56219019Sgabor
57219019Sgaborboolean	 _escaped;	/* true if we are currently _escaped */
58219019Sgaborchar	*s_start;	/* start of string */
59219019Sgaborboolean	 l_onecase;	/* true if upper and lower equivalent */
60219019Sgabor
61219019Sgabor#define makelower(c) (isupper((c)) ? tolower((c)) : (c))
62219019Sgabor
63219019Sgabor/*  STRNCMP -	like strncmp except that we convert the
64219019Sgabor *	 	first string to lower case before comparing
65219019Sgabor *		if l_onecase is set.
66219019Sgabor */
67219019Sgabor
68219019Sgaborint
69219019SgaborSTRNCMP(s1, s2, len)
70219019Sgabor	register char *s1,*s2;
71219019Sgabor	register int len;
72219019Sgabor{
73219019Sgabor	if (l_onecase) {
74219019Sgabor	    do
75219019Sgabor		if (*s2 - makelower(*s1))
76219019Sgabor			return (*s2 - makelower(*s1));
77219019Sgabor		else {
78219019Sgabor			s2++;
79219019Sgabor			s1++;
80219019Sgabor		}
81219019Sgabor	    while (--len);
82219019Sgabor	} else {
83219019Sgabor	    do
84219019Sgabor		if (*s2 - *s1)
85219019Sgabor			return (*s2 - *s1);
86219019Sgabor		else {
87219019Sgabor			s2++;
88219019Sgabor			s1++;
89219019Sgabor		}
90219019Sgabor	    while (--len);
91219019Sgabor	}
92219019Sgabor	return(0);
93219019Sgabor}
94219019Sgabor
95219019Sgabor/*	The following routine converts an irregular expression to
96219019Sgabor *	internal format.
97219019Sgabor *
98219019Sgabor *	Either meta symbols (\a \d or \p) or character strings or
99219019Sgabor *	operations ( alternation or perenthesizing ) can be
100219019Sgabor *	specified.  Each starts with a descriptor byte.  The descriptor
101219019Sgabor *	byte has STR set for strings, META set for meta symbols
102219019Sgabor *	and OPER set for operations.
103219019Sgabor *	The descriptor byte can also have the OPT bit set if the object
104219019Sgabor *	defined is optional.  Also ALT can be set to indicate an alternation.
105219019Sgabor *
106219019Sgabor *	For metasymbols the byte following the descriptor byte identities
107219019Sgabor *	the meta symbol (containing an ascii 'a', 'd', 'p', '|', or '(').  For
108219019Sgabor *	strings the byte after the descriptor is a character count for
109219019Sgabor *	the string:
110219019Sgabor *
111219019Sgabor *		meta symbols := descriptor
112219019Sgabor *				symbol
113219019Sgabor *
114219019Sgabor *		strings :=	descriptor
115219019Sgabor *				character count
116219019Sgabor *				the string
117219019Sgabor *
118219019Sgabor *		operatins :=	descriptor
119219019Sgabor *				symbol
120219019Sgabor *				character count
121219019Sgabor */
122219019Sgabor
123219019Sgabor/*
124219019Sgabor *  handy macros for accessing parts of match blocks
125219019Sgabor */
126219019Sgabor#define MSYM(A) (*(A+1))	/* symbol in a meta symbol block */
127219019Sgabor#define MNEXT(A) (A+2)		/* character following a metasymbol block */
128219019Sgabor
129219019Sgabor#define OSYM(A) (*(A+1))	/* symbol in an operation block */
130219019Sgabor#define OCNT(A) (*(A+2))	/* character count */
131219019Sgabor#define ONEXT(A) (A+3)		/* next character after the operation */
132219019Sgabor#define OPTR(A) (A+*(A+2))	/* place pointed to by the operator */
133219019Sgabor
134219019Sgabor#define SCNT(A) (*(A+1))	/* byte count of a string */
135219019Sgabor#define SSTR(A) (A+2)		/* address of the string */
136219019Sgabor#define SNEXT(A) (A+2+*(A+1))	/* character following the string */
137219019Sgabor
138219019Sgabor/*
139219019Sgabor *  bit flags in the descriptor
140219019Sgabor */
141219019Sgabor#define OPT 1
142219019Sgabor#define STR 2
143219019Sgabor#define META 4
144219019Sgabor#define ALT 8
145219019Sgabor#define OPER 16
146219019Sgabor
147219019Sgaborstatic char *ccre;	/* pointer to current position in converted exp*/
148219019Sgaborstatic char *ure;	/* pointer current position in unconverted exp */
149219019Sgabor
150219019Sgaborchar *
151219019Sgaborconvexp(re)
152219019Sgabor    char *re;		/* unconverted irregular expression */
153219019Sgabor{
154219019Sgabor    register char *cre;		/* pointer to converted regular expression */
155219019Sgabor
156219019Sgabor    /* allocate room for the converted expression */
157219019Sgabor    if (re == NIL)
158219019Sgabor	return (NIL);
159219019Sgabor    if (*re == '\0')
160219019Sgabor	return (NIL);
161219019Sgabor    cre = malloc (4 * strlen(re) + 3);
162219019Sgabor    ccre = cre;
163219019Sgabor    ure = re;
164219019Sgabor
165219019Sgabor    /* start the conversion with a \a */
166219019Sgabor    *cre = META | OPT;
167219019Sgabor    MSYM(cre) = 'a';
168219019Sgabor    ccre = MNEXT(cre);
169219019Sgabor
170219019Sgabor    /* start the conversion (its recursive) */
171219019Sgabor    expconv ();
172219019Sgabor    *ccre = 0;
173219019Sgabor    return (cre);
174219019Sgabor}
175219019Sgabor
176219019Sgaborstatic void
177219019Sgaborexpconv()
178219019Sgabor{
179219019Sgabor    register char *cs;		/* pointer to current symbol in converted exp */
180219019Sgabor    register char c;		/* character being processed */
181219019Sgabor    register char *acs;		/* pinter to last alternate */
182219019Sgabor    register int temp;
183219019Sgabor
184219019Sgabor    /* let the conversion begin */
185219019Sgabor    acs = NIL;
186219019Sgabor    cs = NIL;
187219019Sgabor    while (*ure != NIL) {
188219019Sgabor	switch (c = *ure++) {
189219019Sgabor
190219019Sgabor	case '\\':
191219019Sgabor	    switch (c = *ure++) {
192219019Sgabor
193219019Sgabor	    /* escaped characters are just characters */
194219019Sgabor	    default:
195219019Sgabor		if (cs == NIL || (*cs & STR) == 0) {
196219019Sgabor		    cs = ccre;
197219019Sgabor		    *cs = STR;
198219019Sgabor		    SCNT(cs) = 1;
199219019Sgabor		    ccre += 2;
200219019Sgabor		} else
201219019Sgabor		    SCNT(cs)++;
202219019Sgabor		*ccre++ = c;
203219019Sgabor		break;
204219019Sgabor
205219019Sgabor	    /* normal(?) metacharacters */
206219019Sgabor	    case 'a':
207219019Sgabor	    case 'd':
208219019Sgabor	    case 'e':
209219019Sgabor	    case 'p':
210219019Sgabor		if (acs != NIL && acs != cs) {
211219019Sgabor		    do {
212219019Sgabor			temp = OCNT(acs);
213219019Sgabor			OCNT(acs) = ccre - acs;
214219019Sgabor			acs -= temp;
215219019Sgabor		    } while (temp != 0);
216219019Sgabor		    acs = NIL;
217219019Sgabor		}
218219019Sgabor		cs = ccre;
219219019Sgabor		*cs = META;
220219019Sgabor		MSYM(cs) = c;
221219019Sgabor		ccre = MNEXT(cs);
222219019Sgabor		break;
223219019Sgabor	    }
224219019Sgabor	    break;
225219019Sgabor
226219019Sgabor	/* just put the symbol in */
227219019Sgabor	case '^':
228219019Sgabor	case '$':
229219019Sgabor	    if (acs != NIL && acs != cs) {
230219019Sgabor		do {
231219019Sgabor		    temp = OCNT(acs);
232219019Sgabor		    OCNT(acs) = ccre - acs;
233219019Sgabor		    acs -= temp;
234219019Sgabor		} while (temp != 0);
235219019Sgabor		acs = NIL;
236219019Sgabor	    }
237219019Sgabor	    cs = ccre;
238219019Sgabor	    *cs = META;
239219019Sgabor	    MSYM(cs) = c;
240219019Sgabor	    ccre = MNEXT(cs);
241219019Sgabor	    break;
242219019Sgabor
243219019Sgabor	/* mark the last match sequence as optional */
244219019Sgabor	case '?':
245219019Sgabor	    if (cs)
246219019Sgabor	    	*cs = *cs | OPT;
247219019Sgabor	    break;
248219019Sgabor
249219019Sgabor	/* recurse and define a subexpression */
250219019Sgabor	case '(':
251219019Sgabor	    if (acs != NIL && acs != cs) {
252219019Sgabor		do {
253219019Sgabor		    temp = OCNT(acs);
254219019Sgabor		    OCNT(acs) = ccre - acs;
255219019Sgabor		    acs -= temp;
256219019Sgabor		} while (temp != 0);
257219019Sgabor		acs = NIL;
258219019Sgabor	    }
259219019Sgabor	    cs = ccre;
260219019Sgabor	    *cs = OPER;
261219019Sgabor	    OSYM(cs) = '(';
262219019Sgabor	    ccre = ONEXT(cs);
263219019Sgabor	    expconv ();
264219019Sgabor	    OCNT(cs) = ccre - cs;		/* offset to next symbol */
265219019Sgabor	    break;
266219019Sgabor
267219019Sgabor	/* return from a recursion */
268219019Sgabor	case ')':
269219019Sgabor	    if (acs != NIL) {
270219019Sgabor		do {
271219019Sgabor		    temp = OCNT(acs);
272219019Sgabor		    OCNT(acs) = ccre - acs;
273219019Sgabor		    acs -= temp;
274219019Sgabor		} while (temp != 0);
275219019Sgabor		acs = NIL;
276219019Sgabor	    }
277219019Sgabor	    cs = ccre;
278219019Sgabor	    *cs = META;
279219019Sgabor	    MSYM(cs) = c;
280219019Sgabor	    ccre = MNEXT(cs);
281219019Sgabor	    return;
282219019Sgabor
283219019Sgabor	/* mark the last match sequence as having an alternate */
284219019Sgabor	/* the third byte will contain an offset to jump over the */
285219019Sgabor	/* alternate match in case the first did not fail */
286219019Sgabor	case '|':
287219019Sgabor	    if (acs != NIL && acs != cs)
288219019Sgabor		OCNT(ccre) = ccre - acs;	/* make a back pointer */
289219019Sgabor	    else
290219019Sgabor		OCNT(ccre) = 0;
291219019Sgabor	    *cs |= ALT;
292219019Sgabor	    cs = ccre;
293219019Sgabor	    *cs = OPER;
294219019Sgabor	    OSYM(cs) = '|';
295219019Sgabor	    ccre = ONEXT(cs);
296219019Sgabor	    acs = cs;	/* remember that the pointer is to be filles */
297219019Sgabor	    break;
298219019Sgabor
299219019Sgabor	/* if its not a metasymbol just build a scharacter string */
300219019Sgabor	default:
301219019Sgabor	    if (cs == NIL || (*cs & STR) == 0) {
302219019Sgabor		cs = ccre;
303219019Sgabor		*cs = STR;
304219019Sgabor		SCNT(cs) = 1;
305219019Sgabor		ccre = SSTR(cs);
306219019Sgabor	    } else
307219019Sgabor		SCNT(cs)++;
308219019Sgabor	    *ccre++ = c;
309219019Sgabor	    break;
310219019Sgabor	}
311219019Sgabor    }
312219019Sgabor    if (acs != NIL) {
313219019Sgabor	do {
314219019Sgabor	    temp = OCNT(acs);
315219019Sgabor	    OCNT(acs) = ccre - acs;
316219019Sgabor	    acs -= temp;
317219019Sgabor	} while (temp != 0);
318219019Sgabor	acs = NIL;
319219019Sgabor    }
320219019Sgabor    return;
321219019Sgabor}
322219019Sgabor/* end of convertre */
323219019Sgabor
324219019Sgabor
325219019Sgabor/*
326219019Sgabor *	The following routine recognises an irregular expresion
327219019Sgabor *	with the following special characters:
328219019Sgabor *
329219019Sgabor *		\?	-	means last match was optional
330219019Sgabor *		\a	-	matches any number of characters
331219019Sgabor *		\d	-	matches any number of spaces and tabs
332219019Sgabor *		\p	-	matches any number of alphanumeric
333219019Sgabor *				characters. The
334219019Sgabor *				characters matched will be copied into
335219019Sgabor *				the area pointed to by 'name'.
336219019Sgabor *		\|	-	alternation
337219019Sgabor *		\( \)	-	grouping used mostly for alternation and
338219019Sgabor *				optionality
339219019Sgabor *
340219019Sgabor *	The irregular expression must be translated to internal form
341219019Sgabor *	prior to calling this routine
342219019Sgabor *
343219019Sgabor *	The value returned is the pointer to the first non \a
344219019Sgabor *	character matched.
345219019Sgabor */
346219019Sgabor
347219019Sgaborchar *
348219019Sgaborexpmatch (s, re, mstring)
349219019Sgabor    register char *s;		/* string to check for a match in */
350219019Sgabor    register char *re;		/* a converted irregular expression */
351219019Sgabor    register char *mstring;	/* where to put whatever matches a \p */
352219019Sgabor{
353219019Sgabor    register char *cs;		/* the current symbol */
354219019Sgabor    register char *ptr,*s1;	/* temporary pointer */
355219019Sgabor    boolean matched;		/* a temporary boolean */
356219019Sgabor
357219019Sgabor    /* initial conditions */
358219019Sgabor    if (re == NIL)
359219019Sgabor	return (NIL);
360219019Sgabor    cs = re;
361219019Sgabor    matched = FALSE;
362219019Sgabor
363219019Sgabor    /* loop till expression string is exhausted (or at least pretty tired) */
364219019Sgabor    while (*cs) {
365219019Sgabor	switch (*cs & (OPER | STR | META)) {
366219019Sgabor
367219019Sgabor	/* try to match a string */
368219019Sgabor	case STR:
369219019Sgabor	    matched = !STRNCMP (s, SSTR(cs), SCNT(cs));
370219019Sgabor	    if (matched) {
371219019Sgabor
372219019Sgabor		/* hoorah it matches */
373219019Sgabor		s += SCNT(cs);
374219019Sgabor		cs = SNEXT(cs);
375219019Sgabor	    } else if (*cs & ALT) {
376219019Sgabor
377219019Sgabor		/* alternation, skip to next expression */
378219019Sgabor		cs = SNEXT(cs);
379219019Sgabor	    } else if (*cs & OPT) {
380219019Sgabor
381219019Sgabor		/* the match is optional */
382219019Sgabor		cs = SNEXT(cs);
383219019Sgabor		matched = 1;		/* indicate a successful match */
384219019Sgabor	    } else {
385219019Sgabor
386219019Sgabor		/* no match, error return */
387219019Sgabor		return (NIL);
388219019Sgabor	    }
389219019Sgabor	    break;
390219019Sgabor
391219019Sgabor	/* an operator, do something fancy */
392219019Sgabor	case OPER:
393219019Sgabor	    switch (OSYM(cs)) {
394219019Sgabor
395219019Sgabor	    /* this is an alternation */
396219019Sgabor	    case '|':
397219019Sgabor		if (matched)
398219019Sgabor
399219019Sgabor		    /* last thing in the alternation was a match, skip ahead */
400219019Sgabor		    cs = OPTR(cs);
401219019Sgabor		else
402219019Sgabor
403219019Sgabor		    /* no match, keep trying */
404219019Sgabor		    cs = ONEXT(cs);
405219019Sgabor		break;
406219019Sgabor
407219019Sgabor	    /* this is a grouping, recurse */
408219019Sgabor	    case '(':
409219019Sgabor		ptr = expmatch (s, ONEXT(cs), mstring);
410219019Sgabor		if (ptr != NIL) {
411219019Sgabor
412219019Sgabor		    /* the subexpression matched */
413219019Sgabor		    matched = 1;
414219019Sgabor		    s = ptr;
415219019Sgabor		} else if (*cs & ALT) {
416219019Sgabor
417219019Sgabor		    /* alternation, skip to next expression */
418219019Sgabor		    matched = 0;
419219019Sgabor		} else if (*cs & OPT) {
420219019Sgabor
421219019Sgabor		    /* the match is optional */
422219019Sgabor		    matched = 1;	/* indicate a successful match */
423219019Sgabor		} else {
424219019Sgabor
425219019Sgabor		    /* no match, error return */
426219019Sgabor		    return (NIL);
427219019Sgabor		}
428219019Sgabor		cs = OPTR(cs);
429219019Sgabor		break;
430219019Sgabor	    }
431219019Sgabor	    break;
432219019Sgabor
433219019Sgabor	/* try to match a metasymbol */
434219019Sgabor	case META:
435219019Sgabor	    switch (MSYM(cs)) {
436219019Sgabor
437219019Sgabor	    /* try to match anything and remember what was matched */
438219019Sgabor	    case 'p':
439219019Sgabor		/*
440219019Sgabor		 *  This is really the same as trying the match the
441219019Sgabor		 *  remaining parts of the expression to any subset
442219019Sgabor		 *  of the string.
443219019Sgabor		 */
444219019Sgabor		s1 = s;
445219019Sgabor		do {
446219019Sgabor		    ptr = expmatch (s1, MNEXT(cs), mstring);
447219019Sgabor		    if (ptr != NIL && s1 != s) {
448219019Sgabor
449219019Sgabor			/* we have a match, remember the match */
450219019Sgabor			strncpy (mstring, s, s1 - s);
451219019Sgabor			mstring[s1 - s] = '\0';
452219019Sgabor			return (ptr);
453219019Sgabor		    } else if (ptr != NIL && (*cs & OPT)) {
454219019Sgabor
455219019Sgabor			/* it was aoptional so no match is ok */
456219019Sgabor			return (ptr);
457219019Sgabor		    } else if (ptr != NIL) {
458219019Sgabor
459219019Sgabor			/* not optional and we still matched */
460219019Sgabor			return (NIL);
461219019Sgabor		    }
462219019Sgabor		    if (!(isalnum(*s1) || *s1 == '_' ||
463219019Sgabor			  /* C++ destructor */
464219019Sgabor			  *s1 == '~' ||
465219019Sgabor			  /* C++ scope operator */
466219019Sgabor			  (strlen(s1) > 1 && *s1 == ':' && s1[1] == ':' &&
467219019Sgabor			   (s1++, TRUE))))
468219019Sgabor			return (NIL);
469219019Sgabor		    if (*s1 == '\\')
470219019Sgabor			_escaped = _escaped ? FALSE : TRUE;
471219019Sgabor		    else
472219019Sgabor			_escaped = FALSE;
473219019Sgabor		} while (*s1++);
474219019Sgabor		return (NIL);
475219019Sgabor
476219019Sgabor	    /* try to match anything */
477219019Sgabor	    case 'a':
478219019Sgabor		/*
479219019Sgabor		 *  This is really the same as trying the match the
480219019Sgabor		 *  remaining parts of the expression to any subset
481219019Sgabor		 *  of the string.
482219019Sgabor		 */
483219019Sgabor		s1 = s;
484219019Sgabor		do {
485219019Sgabor		    ptr = expmatch (s1, MNEXT(cs), mstring);
486219019Sgabor		    if (ptr != NIL && s1 != s) {
487219019Sgabor
488219019Sgabor			/* we have a match */
489219019Sgabor			return (ptr);
490219019Sgabor		    } else if (ptr != NIL && (*cs & OPT)) {
491219019Sgabor
492219019Sgabor			/* it was aoptional so no match is ok */
493219019Sgabor			return (ptr);
494219019Sgabor		    } else if (ptr != NIL) {
495219019Sgabor
496219019Sgabor			/* not optional and we still matched */
497219019Sgabor			return (NIL);
498219019Sgabor		    }
499219019Sgabor		    if (*s1 == '\\')
500219019Sgabor			_escaped = _escaped ? FALSE : TRUE;
501219019Sgabor		    else
502219019Sgabor			_escaped = FALSE;
503219019Sgabor		} while (*s1++);
504219019Sgabor		return (NIL);
505219019Sgabor
506219019Sgabor	    /* fail if we are currently _escaped */
507219019Sgabor	    case 'e':
508219019Sgabor		if (_escaped)
509219019Sgabor		    return(NIL);
510219019Sgabor		cs = MNEXT(cs);
511219019Sgabor		break;
512219019Sgabor
513219019Sgabor	    /* match any number of tabs and spaces */
514219019Sgabor	    case 'd':
515219019Sgabor		ptr = s;
516219019Sgabor		while (*s == ' ' || *s == '\t')
517219019Sgabor		    s++;
518219019Sgabor		if (s != ptr || s == s_start) {
519219019Sgabor
520219019Sgabor		    /* match, be happy */
521219019Sgabor		    matched = 1;
522219019Sgabor		    cs = MNEXT(cs);
523219019Sgabor		} else if (*s == '\n' || *s == '\0') {
524219019Sgabor
525219019Sgabor		    /* match, be happy */
526219019Sgabor		    matched = 1;
527219019Sgabor		    cs = MNEXT(cs);
528219019Sgabor		} else if (*cs & ALT) {
529219019Sgabor
530219019Sgabor		    /* try the next part */
531219019Sgabor		    matched = 0;
532219019Sgabor		    cs = MNEXT(cs);
533219019Sgabor		} else if (*cs & OPT) {
534219019Sgabor
535219019Sgabor		    /* doesn't matter */
536219019Sgabor		    matched = 1;
537219019Sgabor		    cs = MNEXT(cs);
538219019Sgabor		} else
539219019Sgabor
540219019Sgabor		    /* no match, error return */
541219019Sgabor		    return (NIL);
542219019Sgabor		break;
543219019Sgabor
544219019Sgabor	    /* check for end of line */
545219019Sgabor	    case '$':
546219019Sgabor		if (*s == '\0' || *s == '\n') {
547219019Sgabor
548219019Sgabor		    /* match, be happy */
549219019Sgabor		    s++;
550219019Sgabor		    matched = 1;
551219019Sgabor		    cs = MNEXT(cs);
552219019Sgabor		} else if (*cs & ALT) {
553219019Sgabor
554219019Sgabor		    /* try the next part */
555219019Sgabor		    matched = 0;
556219019Sgabor		    cs = MNEXT(cs);
557219019Sgabor		} else if (*cs & OPT) {
558219019Sgabor
559219019Sgabor		    /* doesn't matter */
560219019Sgabor		    matched = 1;
561219019Sgabor		    cs = MNEXT(cs);
562219019Sgabor		} else
563219019Sgabor
564219019Sgabor		    /* no match, error return */
565219019Sgabor		    return (NIL);
566219019Sgabor		break;
567219019Sgabor
568219019Sgabor	    /* check for start of line */
569219019Sgabor	    case '^':
570219019Sgabor		if (s == s_start) {
571219019Sgabor
572219019Sgabor		    /* match, be happy */
573219019Sgabor		    matched = 1;
574219019Sgabor		    cs = MNEXT(cs);
575219019Sgabor		} else if (*cs & ALT) {
576219019Sgabor
577219019Sgabor		    /* try the next part */
578219019Sgabor		    matched = 0;
579219019Sgabor		    cs = MNEXT(cs);
580219019Sgabor		} else if (*cs & OPT) {
581219019Sgabor
582219019Sgabor		    /* doesn't matter */
583219019Sgabor		    matched = 1;
584219019Sgabor		    cs = MNEXT(cs);
585219019Sgabor		} else
586219019Sgabor
587219019Sgabor		    /* no match, error return */
588219019Sgabor		    return (NIL);
589219019Sgabor		break;
590219019Sgabor
591219019Sgabor	    /* end of a subexpression, return success */
592219019Sgabor	    case ')':
593219019Sgabor		return (s);
594219019Sgabor	    }
595219019Sgabor	    break;
596219019Sgabor	}
597219019Sgabor    }
598219019Sgabor    return (s);
599219019Sgabor}
600219019Sgabor