935 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			935 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| /* regexp.c */
 | |
| 
 | |
| /* This file contains the code that compiles regular expressions and executes
 | |
|  * them.  It supports the same syntax and features as vi's regular expression
 | |
|  * code.  Specifically, the meta characters are:
 | |
|  *	^	matches the beginning of a line
 | |
|  *	$	matches the end of a line
 | |
|  *	\<	matches the beginning of a word
 | |
|  *	\>	matches the end of a word
 | |
|  *	.	matches any single character
 | |
|  *	[]	matches any character in a character class
 | |
|  *	\(	delimits the start of a subexpression
 | |
|  *	\)	delimits the end of a subexpression
 | |
|  *	*	repeats the preceding 0 or more times
 | |
|  * NOTE: You cannot follow a \) with a *.
 | |
|  *
 | |
|  * The physical structure of a compiled RE is as follows:
 | |
|  *	- First, there is a one-byte value that says how many character classes
 | |
|  *	  are used in this regular expression
 | |
|  *	- Next, each character class is stored as a bitmap that is 256 bits
 | |
|  *	  (32 bytes) long.
 | |
|  *	- A mixture of literal characters and compiled meta characters follows.
 | |
|  *	  This begins with M_BEGIN(0) and ends with M_END(0).  All meta chars
 | |
|  *	  are stored as a \n followed by a one-byte code, so they take up two
 | |
|  *	  bytes apiece.  Literal characters take up one byte apiece.  \n can't
 | |
|  *	  be used as a literal character.
 | |
|  *
 | |
|  * If NO_MAGIC is defined, then a different set of functions is used instead.
 | |
|  * That right, this file contains TWO versions of the code.
 | |
|  */
 | |
| 
 | |
| #include <setjmp.h>
 | |
| #include "config.h"
 | |
| #include "ctype.h"
 | |
| #include "vi.h"
 | |
| #include "regexp.h"
 | |
| 
 | |
| 
 | |
| 
 | |
| static char	*previous;	/* the previous regexp, used when null regexp is given */
 | |
| 
 | |
| 
 | |
| #ifndef NO_MAGIC
 | |
| /* THE REAL REGEXP PACKAGE IS USED UNLESS "NO_MAGIC" IS DEFINED */
 | |
| 
 | |
| /* These are used to classify or recognize meta-characters */
 | |
| #define META		'\0'
 | |
| #define BASE_META(m)	((m) - 256)
 | |
| #define INT_META(c)	((c) + 256)
 | |
| #define IS_META(m)	((m) >= 256)
 | |
| #define IS_CLASS(m)	((m) >= M_CLASS(0) && (m) <= M_CLASS(9))
 | |
| #define IS_START(m)	((m) >= M_START(0) && (m) <= M_START(9))
 | |
| #define IS_END(m)	((m) >= M_END(0) && (m) <= M_END(9))
 | |
| #define IS_CLOSURE(m)	((m) >= M_SPLAT && (m) <= M_RANGE)
 | |
| #define ADD_META(s,m)	(*(s)++ = META, *(s)++ = BASE_META(m))
 | |
| #define GET_META(s)	(*(s) == META ? INT_META(*++(s)) : *s)
 | |
| 
 | |
| /* These are the internal codes used for each type of meta-character */
 | |
| #define M_BEGLINE	256		/* internal code for ^ */
 | |
| #define M_ENDLINE	257		/* internal code for $ */
 | |
| #define M_BEGWORD	258		/* internal code for \< */
 | |
| #define M_ENDWORD	259		/* internal code for \> */
 | |
| #define M_ANY		260		/* internal code for . */
 | |
| #define M_SPLAT		261		/* internal code for * */
 | |
| #define M_PLUS		262		/* internal code for \+ */
 | |
| #define M_QMARK		263		/* internal code for \? */
 | |
| #define M_RANGE		264		/* internal code for \{ */
 | |
| #define M_CLASS(n)	(265+(n))	/* internal code for [] */
 | |
| #define M_START(n)	(275+(n))	/* internal code for \( */
 | |
| #define M_END(n)	(285+(n))	/* internal code for \) */
 | |
| 
 | |
| /* These are used during compilation */
 | |
| static int	class_cnt;	/* used to assign class IDs */
 | |
| static int	start_cnt;	/* used to assign start IDs */
 | |
| static int	end_stk[NSUBEXP];/* used to assign end IDs */
 | |
| static int	end_sp;
 | |
| static char	*retext;	/* points to the text being compiled */
 | |
| 
 | |
| /* error-handling stuff */
 | |
| jmp_buf	errorhandler;
 | |
| #define FAIL(why)	regerror(why); longjmp(errorhandler, 1)
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /* This function builds a bitmap for a particular class */
 | |
| static char *makeclass(text, bmap)
 | |
| 	REG char	*text;	/* start of the class */
 | |
| 	REG char	*bmap;	/* the bitmap */
 | |
| {
 | |
| 	REG int		i;
 | |
| 	int		complement = 0;
 | |
| 
 | |
| 
 | |
| 	/* zero the bitmap */
 | |
| 	for (i = 0; bmap && i < 32; i++)
 | |
| 	{
 | |
| 		bmap[i] = 0;
 | |
| 	}
 | |
| 
 | |
| 	/* see if we're going to complement this class */
 | |
| 	if (*text == '^')
 | |
| 	{
 | |
| 		text++;
 | |
| 		complement = 1;
 | |
| 	}
 | |
| 
 | |
| 	/* add in the characters */
 | |
| 	while (*text && *text != ']')
 | |
| 	{
 | |
| 		/* is this a span of characters? */
 | |
| 		if (text[1] == '-' && text[2])
 | |
| 		{
 | |
| 			/* spans can't be backwards */
 | |
| 			if (text[0] > text[2])
 | |
| 			{
 | |
| 				FAIL("Backwards span in []");
 | |
| 			}
 | |
| 
 | |
| 			/* add each character in the span to the bitmap */
 | |
| 			for (i = text[0]; bmap && i <= text[2]; i++)
 | |
| 			{
 | |
| 				bmap[i >> 3] |= (1 << (i & 7));
 | |
| 			}
 | |
| 
 | |
| 			/* move past this span */
 | |
| 			text += 3;
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			/* add this single character to the span */
 | |
| 			i = *text++;
 | |
| 			if (bmap)
 | |
| 			{
 | |
| 				bmap[i >> 3] |= (1 << (i & 7));
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* make sure the closing ] is missing */
 | |
| 	if (*text++ != ']')
 | |
| 	{
 | |
| 		FAIL("] missing");
 | |
| 	}
 | |
| 
 | |
| 	/* if we're supposed to complement this class, then do so */
 | |
| 	if (complement && bmap)
 | |
| 	{
 | |
| 		for (i = 0; i < 32; i++)
 | |
| 		{
 | |
| 			bmap[i] = ~bmap[i];
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return text;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /* This function gets the next character or meta character from a string.
 | |
|  * The pointer is incremented by 1, or by 2 for \-quoted characters.  For [],
 | |
|  * a bitmap is generated via makeclass() (if re is given), and the
 | |
|  * character-class text is skipped.
 | |
|  */
 | |
| static int gettoken(sptr, re)
 | |
| 	char	**sptr;
 | |
| 	regexp	*re;
 | |
| {
 | |
| 	int	c;
 | |
| 
 | |
| 	c = **sptr;
 | |
| 	++*sptr;
 | |
| 	if (c == '\\')
 | |
| 	{
 | |
| 		c = **sptr;
 | |
| 		++*sptr;
 | |
| 		switch (c)
 | |
| 		{
 | |
| 		  case '<':
 | |
| 			return M_BEGWORD;
 | |
| 
 | |
| 		  case '>':
 | |
| 			return M_ENDWORD;
 | |
| 
 | |
| 		  case '(':
 | |
| 			if (start_cnt >= NSUBEXP)
 | |
| 			{
 | |
| 				FAIL("Too many \\(s");
 | |
| 			}
 | |
| 			end_stk[end_sp++] = start_cnt;
 | |
| 			return M_START(start_cnt++);
 | |
| 
 | |
| 		  case ')':
 | |
| 			if (end_sp <= 0)
 | |
| 			{
 | |
| 				FAIL("Mismatched \\)");
 | |
| 			}
 | |
| 			return M_END(end_stk[--end_sp]);
 | |
| 
 | |
| 		  case '*':
 | |
| 			return (*o_magic ? c : M_SPLAT);
 | |
| 
 | |
| 		  case '.':
 | |
| 			return (*o_magic ? c : M_ANY);
 | |
| 
 | |
| 		  case '+':
 | |
| 			return M_PLUS;
 | |
| 
 | |
| 		  case '?':
 | |
| 			return M_QMARK;
 | |
| #ifndef CRUNCH
 | |
| 		  case '{':
 | |
| 			return M_RANGE;
 | |
| #endif
 | |
| 		  default:
 | |
| 			return c;
 | |
| 		}
 | |
| 	}
 | |
| 	else if (*o_magic)
 | |
| 	{
 | |
| 		switch (c)
 | |
| 		{
 | |
| 		  case '^':
 | |
| 			if (*sptr == retext + 1)
 | |
| 			{
 | |
| 				return M_BEGLINE;
 | |
| 			}
 | |
| 			return c;
 | |
| 
 | |
| 		  case '$':
 | |
| 			if (!**sptr)
 | |
| 			{
 | |
| 				return M_ENDLINE;
 | |
| 			}
 | |
| 			return c;
 | |
| 
 | |
| 		  case '.':
 | |
| 			return M_ANY;
 | |
| 
 | |
| 		  case '*':
 | |
| 			return M_SPLAT;
 | |
| 
 | |
| 		  case '[':
 | |
| 			/* make sure we don't have too many classes */
 | |
| 			if (class_cnt >= 10)
 | |
| 			{
 | |
| 				FAIL("Too many []s");
 | |
| 			}
 | |
| 
 | |
| 			/* process the character list for this class */
 | |
| 			if (re)
 | |
| 			{
 | |
| 				/* generate the bitmap for this class */
 | |
| 				*sptr = makeclass(*sptr, re->program + 1 + 32 * class_cnt);
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				/* skip to end of the class */
 | |
| 				*sptr = makeclass(*sptr, (char *)0);
 | |
| 			}
 | |
| 			return M_CLASS(class_cnt++);
 | |
| 
 | |
| 		  default:
 | |
| 			return c;
 | |
| 		}
 | |
| 	}
 | |
| 	else	/* unquoted nomagic */
 | |
| 	{
 | |
| 		switch (c)
 | |
| 		{
 | |
| 		  case '^':
 | |
| 			if (*sptr == retext + 1)
 | |
| 			{
 | |
| 				return M_BEGLINE;
 | |
| 			}
 | |
| 			return c;
 | |
| 
 | |
| 		  case '$':
 | |
| 			if (!**sptr)
 | |
| 			{
 | |
| 				return M_ENDLINE;
 | |
| 			}
 | |
| 			return c;
 | |
| 
 | |
| 		  default:
 | |
| 			return c;
 | |
| 		}
 | |
| 	}
 | |
| 	/*NOTREACHED*/
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /* This function calculates the number of bytes that will be needed for a
 | |
|  * compiled RE.  Its argument is the uncompiled version.  It is not clever
 | |
|  * about catching syntax errors; that is done in a later pass.
 | |
|  */
 | |
| static unsigned calcsize(text)
 | |
| 	char		*text;
 | |
| {
 | |
| 	unsigned	size;
 | |
| 	int		token;
 | |
| 
 | |
| 	retext = text;
 | |
| 	class_cnt = 0;
 | |
| 	start_cnt = 1;
 | |
| 	end_sp = 0;
 | |
| 	size = 5;
 | |
| 	while ((token = gettoken(&text, (regexp *)0)) != 0)
 | |
| 	{
 | |
| 		if (IS_CLASS(token))
 | |
| 		{
 | |
| 			size += 34;
 | |
| 		}
 | |
| #ifndef CRUNCH
 | |
| 		else if (token == M_RANGE)
 | |
| 		{
 | |
| 			size += 4;
 | |
| 			while ((token = gettoken(&text, (regexp *)0)) != 0
 | |
| 			    && token != '}')
 | |
| 			{
 | |
| 			}
 | |
| 			if (!token)
 | |
| 			{
 | |
| 				return size;
 | |
| 			}
 | |
| 		}
 | |
| #endif
 | |
| 		else if (IS_META(token))
 | |
| 		{
 | |
| 			size += 2;
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			size++;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return size;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /* This function compiles a regexp. */
 | |
| regexp *regcomp(exp)
 | |
| 	char		*exp;
 | |
| {
 | |
| 	int		needfirst;
 | |
| 	unsigned	size;
 | |
| 	int		token;
 | |
| 	int		peek;
 | |
| 	char		*build;
 | |
| 	regexp		*re;
 | |
| #ifndef CRUNCH
 | |
| 	int		from;
 | |
| 	int		to;
 | |
| 	int		digit;
 | |
| #endif
 | |
| 
 | |
| 
 | |
| 	/* prepare for error handling */
 | |
| 	re = (regexp *)0;
 | |
| 	if (setjmp(errorhandler))
 | |
| 	{
 | |
| 		if (re)
 | |
| 		{
 | |
| 			free(re);
 | |
| 		}
 | |
| 		return (regexp *)0;
 | |
| 	}
 | |
| 
 | |
| 	/* if an empty regexp string was given, use the previous one */
 | |
| 	if (*exp == 0)
 | |
| 	{
 | |
| 		if (!previous)
 | |
| 		{
 | |
| 			FAIL("No previous RE");
 | |
| 		}
 | |
| 		exp = previous;
 | |
| 	}
 | |
| 	else /* non-empty regexp given, so remember it */
 | |
| 	{
 | |
| 		if (previous)
 | |
| 			free(previous);
 | |
| 		previous = (char *)malloc((unsigned)(strlen(exp) + 1));
 | |
| 		if (previous)
 | |
| 			strcpy(previous, exp);
 | |
| 	}
 | |
| 
 | |
| 	/* allocate memory */
 | |
| 	class_cnt = 0;
 | |
| 	start_cnt = 1;
 | |
| 	end_sp = 0;
 | |
| 	retext = exp;
 | |
| 	size = calcsize(exp) + sizeof(regexp) + 10; /* !!! 10 bytes for slop */
 | |
| #ifdef lint
 | |
| 	re = ((regexp *)0) + size;
 | |
| #else
 | |
| 	re = (regexp *)malloc((unsigned)size);
 | |
| #endif
 | |
| 	if (!re)
 | |
| 	{
 | |
| 		FAIL("Not enough memory for this RE");
 | |
| 	}
 | |
| 
 | |
| 	/* compile it */
 | |
| 	build = &re->program[1 + 32 * class_cnt];
 | |
| 	re->program[0] = class_cnt;
 | |
| 	for (token = 0; token < NSUBEXP; token++)
 | |
| 	{
 | |
| 		re->startp[token] = re->endp[token] = (char *)0;
 | |
| 	}
 | |
| 	re->first = 0;
 | |
| 	re->bol = 0;
 | |
| 	re->minlen = 0;
 | |
| 	needfirst = 1;
 | |
| 	class_cnt = 0;
 | |
| 	start_cnt = 1;
 | |
| 	end_sp = 0;
 | |
| 	retext = exp;
 | |
| 	for (token = M_START(0), peek = gettoken(&exp, re);
 | |
| 	     token;
 | |
| 	     token = peek, peek = gettoken(&exp, re))
 | |
| 	{
 | |
| 		/* special processing for the closure operator */
 | |
| 		if (IS_CLOSURE(peek))
 | |
| 		{
 | |
| 			/* detect misuse of closure operator */
 | |
| 			if (IS_START(token))
 | |
| 			{
 | |
| 				FAIL("Closure operator follows nothing");
 | |
| 			}
 | |
| 			else if (IS_META(token) && token != M_ANY && !IS_CLASS(token))
 | |
| 			{
 | |
| 				FAIL("Closure operators can only follow a normal character or . or []");
 | |
| 			}
 | |
| 
 | |
| #ifndef CRUNCH
 | |
| 			/* if \{ \} then read the range */
 | |
| 			if (peek == M_RANGE)
 | |
| 			{
 | |
| 				from = 0;
 | |
| 				for (digit = gettoken(&exp, re);
 | |
| 				     !IS_META(digit) && isdigit(digit);
 | |
| 				     digit = gettoken(&exp, re))
 | |
| 				{
 | |
| 					from = from * 10 + digit - '0';
 | |
| 				}
 | |
| 				if (digit == '}')
 | |
| 				{
 | |
| 					to = from;
 | |
| 				}
 | |
| 				else if (digit == ',')
 | |
| 				{
 | |
| 					to = 0;
 | |
| 					for (digit = gettoken(&exp, re);
 | |
| 					     !IS_META(digit) && isdigit(digit);
 | |
| 					     digit = gettoken(&exp, re))
 | |
| 					{
 | |
| 						to = to * 10 + digit - '0';
 | |
| 					}
 | |
| 					if (to == 0)
 | |
| 					{
 | |
| 						to = 255;
 | |
| 					}
 | |
| 				}
 | |
| 				if (digit != '}')
 | |
| 				{
 | |
| 					FAIL("Bad characters after \\{");
 | |
| 				}
 | |
| 				else if (to < from || to == 0 || from >= 255)
 | |
| 				{
 | |
| 					FAIL("Invalid range for \\{ \\}");
 | |
| 				}
 | |
| 				re->minlen += from;
 | |
| 			}
 | |
| 			else
 | |
| #endif
 | |
| 			if (peek != M_SPLAT)
 | |
| 			{
 | |
| 				re->minlen++;
 | |
| 			}
 | |
| 
 | |
| 			/* it is okay -- make it prefix instead of postfix */
 | |
| 			ADD_META(build, peek);
 | |
| #ifndef CRUNCH
 | |
| 			if (peek == M_RANGE)
 | |
| 			{
 | |
| 				*build++ = from;
 | |
| 				*build++ = (to < 255 ? to : 255);
 | |
| 			}
 | |
| #endif
 | |
| 			
 | |
| 
 | |
| 			/* take care of "needfirst" - is this the first char? */
 | |
| 			if (needfirst && peek == M_PLUS && !IS_META(token))
 | |
| 			{
 | |
| 				re->first = token;
 | |
| 			}
 | |
| 			needfirst = 0;
 | |
| 
 | |
| 			/* we used "peek" -- need to refill it */
 | |
| 			peek = gettoken(&exp, re);
 | |
| 			if (IS_CLOSURE(peek))
 | |
| 			{
 | |
| 				FAIL("* or \\+ or \\? doubled up");
 | |
| 			}
 | |
| 		}
 | |
| 		else if (!IS_META(token))
 | |
| 		{
 | |
| 			/* normal char is NOT argument of closure */
 | |
| 			if (needfirst)
 | |
| 			{
 | |
| 				re->first = token;
 | |
| 				needfirst = 0;
 | |
| 			}
 | |
| 			re->minlen++;
 | |
| 		}
 | |
| 		else if (token == M_ANY || IS_CLASS(token))
 | |
| 		{
 | |
| 			/* . or [] is NOT argument of closure */
 | |
| 			needfirst = 0;
 | |
| 			re->minlen++;
 | |
| 		}
 | |
| 
 | |
| 		/* the "token" character is not closure -- process it normally */
 | |
| 		if (token == M_BEGLINE)
 | |
| 		{
 | |
| 			/* set the BOL flag instead of storing M_BEGLINE */
 | |
| 			re->bol = 1;
 | |
| 		}
 | |
| 		else if (IS_META(token))
 | |
| 		{
 | |
| 			ADD_META(build, token);
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			*build++ = token;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* end it with a \) which MUST MATCH the opening \( */
 | |
| 	ADD_META(build, M_END(0));
 | |
| 	if (end_sp > 0)
 | |
| 	{
 | |
| 		FAIL("Not enough \\)s");
 | |
| 	}
 | |
| 
 | |
| 	return re;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /*---------------------------------------------------------------------------*/
 | |
| 
 | |
| 
 | |
| /* This function checks for a match between a character and a token which is
 | |
|  * known to represent a single character.  It returns 0 if they match, or
 | |
|  * 1 if they don't.
 | |
|  */
 | |
| int match1(re, ch, token)
 | |
| 	regexp		*re;
 | |
| 	REG char	ch;
 | |
| 	REG int		token;
 | |
| {
 | |
| 	if (!ch)
 | |
| 	{
 | |
| 		/* the end of a line can't match any RE of width 1 */
 | |
| 		return 1;
 | |
| 	}
 | |
| 	if (token == M_ANY)
 | |
| 	{
 | |
| 		return 0;
 | |
| 	}
 | |
| 	else if (IS_CLASS(token))
 | |
| 	{
 | |
| 		if (re->program[1 + 32 * (token - M_CLASS(0)) + (ch >> 3)] & (1 << (ch & 7)))
 | |
| 			return 0;
 | |
| 	}
 | |
| 	else if (ch == token || *o_ignorecase && tolower(ch) == tolower(token))
 | |
| 	{
 | |
| 		return 0;
 | |
| 	}
 | |
| 	return 1;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /* This function checks characters up to and including the next closure, at
 | |
|  * which point it does a recursive call to check the rest of it.  This function
 | |
|  * returns 0 if everything matches, or 1 if something doesn't match.
 | |
|  */
 | |
| int match(re, str, prog, here)
 | |
| 	regexp		*re;	/* the regular expression */
 | |
| 	char		*str;	/* the string */
 | |
| 	REG char	*prog;	/* a portion of re->program, an compiled RE */
 | |
| 	REG char	*here;	/* a portion of str, the string to compare it to */
 | |
| {
 | |
| 	REG int		token;	/* the roken pointed to by prog */
 | |
| 	REG int		nmatched;/* counter, used during closure matching */ 
 | |
| 	REG int		closure;/* the token denoting the type of closure */
 | |
| 	int		from;	/* minimum number of matches in closure */
 | |
| 	int		to;	/* maximum number of matches in closure */
 | |
| 
 | |
| 	for (token = GET_META(prog); !IS_CLOSURE(token); prog++, token = GET_META(prog))
 | |
| 	{
 | |
| 		switch (token)
 | |
| 		{
 | |
| 		/*case M_BEGLINE: can't happen; re->bol is used instead */
 | |
| 		  case M_ENDLINE:
 | |
| 			if (*here)
 | |
| 				return 1;
 | |
| 			break;
 | |
| 
 | |
| 		  case M_BEGWORD:
 | |
| 			if (here != str &&
 | |
| 			   (here[-1] == '_' || isalnum(here[-1])))
 | |
| 				return 1;
 | |
| 			break;
 | |
| 
 | |
| 		  case M_ENDWORD:
 | |
| 			if (here[0] == '_' || isalnum(here[0]))
 | |
| 				return 1;
 | |
| 			break;
 | |
| 
 | |
| 		  case M_START(0):
 | |
| 		  case M_START(1):
 | |
| 		  case M_START(2):
 | |
| 		  case M_START(3):
 | |
| 		  case M_START(4):
 | |
| 		  case M_START(5):
 | |
| 		  case M_START(6):
 | |
| 		  case M_START(7):
 | |
| 		  case M_START(8):
 | |
| 		  case M_START(9):
 | |
| 			re->startp[token - M_START(0)] = (char *)here;
 | |
| 			break;
 | |
| 
 | |
| 		  case M_END(0):
 | |
| 		  case M_END(1):
 | |
| 		  case M_END(2):
 | |
| 		  case M_END(3):
 | |
| 		  case M_END(4):
 | |
| 		  case M_END(5):
 | |
| 		  case M_END(6):
 | |
| 		  case M_END(7):
 | |
| 		  case M_END(8):
 | |
| 		  case M_END(9):
 | |
| 			re->endp[token - M_END(0)] = (char *)here;
 | |
| 			if (token == M_END(0))
 | |
| 			{
 | |
| 				return 0;
 | |
| 			}
 | |
| 			break;
 | |
| 
 | |
| 		  default: /* literal, M_CLASS(n), or M_ANY */
 | |
| 			if (match1(re, *here, token) != 0)
 | |
| 				return 1;
 | |
| 			here++;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* C L O S U R E */
 | |
| 
 | |
| 	/* step 1: see what we have to match against, and move "prog" to point
 | |
| 	 * to the remainder of the compiled RE.
 | |
| 	 */
 | |
| 	closure = token;
 | |
| 	prog++;
 | |
| 	switch (closure)
 | |
| 	{
 | |
| 	  case M_SPLAT:
 | |
| 		from = 0;
 | |
| 		to = strlen(str);	/* infinity */
 | |
| 		break;
 | |
| 
 | |
| 	  case M_PLUS:
 | |
| 		from = 1;
 | |
| 		to = strlen(str);	/* infinity */
 | |
| 		break;
 | |
| 
 | |
| 	  case M_QMARK:
 | |
| 		from = 0;
 | |
| 		to = 1;
 | |
| 		break;
 | |
| 
 | |
| #ifndef CRUNCH
 | |
| 	  case M_RANGE:
 | |
| 		from = UCHAR(*prog++);
 | |
| 		to = UCHAR(*prog++);
 | |
| 		if (to == 255)
 | |
| 		{
 | |
| 			to = strlen(str); /* infinity */
 | |
| 		}
 | |
| 		break;
 | |
| #endif
 | |
| 	}
 | |
| 	token = GET_META(prog);
 | |
| 	prog++;
 | |
| 
 | |
| 	/* step 2: see how many times we can match that token against the string */
 | |
| 	for (nmatched = 0;
 | |
| 	     nmatched < to && *here && match1(re, *here, token) == 0;
 | |
| 	     nmatched++, here++)
 | |
| 	{
 | |
| 	}
 | |
| 
 | |
| 	/* step 3: try to match the remainder, and back off if it doesn't */
 | |
| 	while (nmatched >= from && match(re, str, prog, here) != 0)
 | |
| 	{
 | |
| 		nmatched--;
 | |
| 		here--;
 | |
| 	}
 | |
| 
 | |
| 	/* so how did it work out? */
 | |
| 	if (nmatched >= from)
 | |
| 		return 0;
 | |
| 	return 1;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /* This function searches through a string for text that matches an RE. */
 | |
| int regexec(re, str, bol)
 | |
| 	regexp	*re;	/* the compiled regexp to search for */
 | |
| 	char	*str;	/* the string to search through */
 | |
| 	int	bol;	/* boolean: does str start at the beginning of a line? */
 | |
| {
 | |
| 	char	*prog;	/* the entry point of re->program */
 | |
| 	int	len;	/* length of the string */
 | |
| 	REG char	*here;
 | |
| 
 | |
| 	/* if must start at the beginning of a line, and this isn't, then fail */
 | |
| 	if (re->bol && !bol)
 | |
| 	{
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	len = strlen(str);
 | |
| 	prog = re->program + 1 + 32 * re->program[0];
 | |
| 
 | |
| 	/* search for the RE in the string */
 | |
| 	if (re->bol)
 | |
| 	{
 | |
| 		/* must occur at BOL */
 | |
| 		if ((re->first
 | |
| 			&& match1(re, *(char *)str, re->first))/* wrong first letter? */
 | |
| 		 || len < re->minlen			/* not long enough? */
 | |
| 		 || match(re, (char *)str, prog, str))	/* doesn't match? */
 | |
| 			return 0;			/* THEN FAIL! */
 | |
| 	}
 | |
| #ifndef CRUNCH
 | |
| 	else if (!*o_ignorecase)
 | |
| 	{
 | |
| 		/* can occur anywhere in the line, noignorecase */
 | |
| 		for (here = (char *)str;
 | |
| 		     (re->first && re->first != *here)
 | |
| 			|| match(re, (char *)str, prog, here);
 | |
| 		     here++, len--)
 | |
| 		{
 | |
| 			if (len < re->minlen)
 | |
| 				return 0;
 | |
| 		}
 | |
| 	}
 | |
| #endif
 | |
| 	else
 | |
| 	{
 | |
| 		/* can occur anywhere in the line, ignorecase */
 | |
| 		for (here = (char *)str;
 | |
| 		     (re->first && match1(re, *here, (int)re->first))
 | |
| 			|| match(re, (char *)str, prog, here);
 | |
| 		     here++, len--)
 | |
| 		{
 | |
| 			if (len < re->minlen)
 | |
| 				return 0;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* if we didn't fail, then we must have succeeded */
 | |
| 	return 1;
 | |
| }
 | |
| 
 | |
| /*============================================================================*/
 | |
| #else /* NO_MAGIC */
 | |
| 
 | |
| regexp *regcomp(exp)
 | |
| 	char	*exp;
 | |
| {
 | |
| 	char	*src;
 | |
| 	char	*dest;
 | |
| 	regexp	*re;
 | |
| 	int	i;
 | |
| 
 | |
| 	/* allocate a big enough regexp structure */
 | |
| #ifdef lint
 | |
| 	re = (regexp *)0;
 | |
| #else
 | |
| 	re = (regexp *)malloc((unsigned)(strlen(exp) + 1 + sizeof(struct regexp)));
 | |
| #endif
 | |
| 	if (!re)
 | |
| 	{
 | |
| 		regerror("Could not malloc a regexp structure");
 | |
| 		return (regexp *)0;
 | |
| 	}
 | |
| 
 | |
| 	/* initialize all fields of the structure */
 | |
| 	for (i = 0; i < NSUBEXP; i++)
 | |
| 	{
 | |
| 		re->startp[i] = re->endp[i] = (char *)0;
 | |
| 	}
 | |
| 	re->minlen = 0;
 | |
| 	re->first = 0;
 | |
| 	re->bol = 0;
 | |
| 
 | |
| 	/* copy the string into it, translating ^ and $ as needed */
 | |
| 	for (src = exp, dest = re->program + 1; *src; src++)
 | |
| 	{
 | |
| 		switch (*src)
 | |
| 		{
 | |
| 		  case '^':
 | |
| 			if (src == exp)
 | |
| 			{
 | |
| 				re->bol += 1;
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				*dest++ = '^';
 | |
| 				re->minlen++;
 | |
| 			}
 | |
| 			break;
 | |
| 
 | |
| 		  case '$':
 | |
| 			if (!src[1])
 | |
| 			{
 | |
| 				re->bol += 2;
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				*dest++ = '$';
 | |
| 				re->minlen++;
 | |
| 			}
 | |
| 			break;
 | |
| 
 | |
| 		  case '\\':
 | |
| 			if (src[1])
 | |
| 			{
 | |
| 				*dest++ = *++src;
 | |
| 				re->minlen++;
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				regerror("extra \\ at end of regular expression");
 | |
| 			}
 | |
| 			break;
 | |
| 
 | |
| 		  default:
 | |
| 			*dest++ = *src;
 | |
| 			re->minlen++;
 | |
| 		}
 | |
| 	}
 | |
| 	*dest = '\0';
 | |
| 
 | |
| 	return re;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* This "helper" function checks for a match at a given location.  It returns
 | |
|  * 1 if it matches, 0 if it doesn't match here but might match later on in the
 | |
|  * string, or -1 if it could not possibly match
 | |
|  */
 | |
| static int reghelp(prog, string, bolflag)
 | |
| 	struct regexp	*prog;
 | |
| 	char		*string;
 | |
| 	int		bolflag;
 | |
| {
 | |
| 	char		*scan;
 | |
| 	char		*str;
 | |
| 
 | |
| 	/* if ^, then require bolflag */
 | |
| 	if ((prog->bol & 1) && !bolflag)
 | |
| 	{
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	/* if it matches, then it will start here */
 | |
| 	prog->startp[0] = string;
 | |
| 
 | |
| 	/* compare, possibly ignoring case */
 | |
| 	if (*o_ignorecase)
 | |
| 	{
 | |
| 		for (scan = &prog->program[1]; *scan; scan++, string++)
 | |
| 			if (tolower(*scan) != tolower(*string))
 | |
| 				return *string ? 0 : -1;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		for (scan = &prog->program[1]; *scan; scan++, string++)
 | |
| 			if (*scan != *string)
 | |
| 				return *string ? 0 : -1;
 | |
| 	}
 | |
| 
 | |
| 	/* if $, then require string to end here, too */
 | |
| 	if ((prog->bol & 2) && *string)
 | |
| 	{
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	/* if we get to here, it matches */
 | |
| 	prog->endp[0] = string;
 | |
| 	return 1;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| int regexec(prog, string, bolflag)
 | |
| 	struct regexp	*prog;
 | |
| 	char		*string;
 | |
| 	int		bolflag;
 | |
| {
 | |
| 	int		rc;
 | |
| 
 | |
| 	/* keep trying to match it */
 | |
| 	for (rc = reghelp(prog, string, bolflag); rc == 0; rc = reghelp(prog, string, 0))
 | |
| 	{
 | |
| 		string++;
 | |
| 	}
 | |
| 
 | |
| 	/* did we match? */
 | |
| 	return rc == 1;
 | |
| }
 | |
| #endif
 | 
