Also static-ified said functions so they do not have namespace conflicts with the libc regex functions.
		
			
				
	
	
		
			790 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			790 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* vi: set sw=4 ts=4: */
 | 
						|
/* regexp.c */
 | 
						|
 | 
						|
#include "internal.h"
 | 
						|
#include "regexp.h"
 | 
						|
#include <setjmp.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <ctype.h>
 | 
						|
 | 
						|
 | 
						|
#define NSUBEXP  10
 | 
						|
typedef struct regexp {
 | 
						|
	char	*startp[NSUBEXP];
 | 
						|
	char	*endp[NSUBEXP];
 | 
						|
	int	minlen;		/* length of shortest possible match */
 | 
						|
	char	first;		/* first character, if known; else \0 */
 | 
						|
	char	bol;		/* boolean: must start at beginning of line? */
 | 
						|
	char	program[1];	/* Unwarranted chumminess with compiler. */
 | 
						|
} regexp;
 | 
						|
 | 
						|
 | 
						|
static regexp *regcomp(char* text);
 | 
						|
static int regexec(struct regexp* re, char* str, int bol, int ignoreCase);
 | 
						|
static void regsub(struct regexp* re, char* src, char* dst);
 | 
						|
 | 
						|
#if ( defined BB_GREP || defined BB_SED)
 | 
						|
 | 
						|
/* This also tries to find a needle in a haystack, but uses
 | 
						|
 * real regular expressions....  The fake regular expression
 | 
						|
 * version of find_match lives in utility.c.  Using this version
 | 
						|
 * will add 3.9k to busybox...
 | 
						|
 *  -Erik Andersen
 | 
						|
 */
 | 
						|
extern int find_match(char *haystack, char *needle, int ignoreCase)
 | 
						|
{
 | 
						|
	int status;
 | 
						|
	struct regexp *re;
 | 
						|
 | 
						|
	re = regcomp(needle);
 | 
						|
	status = regexec(re, haystack, FALSE, ignoreCase);
 | 
						|
	free(re);
 | 
						|
	return (status);
 | 
						|
}
 | 
						|
 | 
						|
#if defined BB_SED
 | 
						|
/* This performs substitutions after a regexp match has been found.  
 | 
						|
 * The new string is returned.  It is malloc'ed, and do must be freed. */
 | 
						|
extern int replace_match(char *haystack, char *needle, char *newNeedle,
 | 
						|
						 int ignoreCase)
 | 
						|
{
 | 
						|
	int status;
 | 
						|
	struct regexp *re;
 | 
						|
	char *s, buf[BUF_SIZE], *d = buf;
 | 
						|
 | 
						|
	re = regcomp(needle);
 | 
						|
	status = regexec(re, haystack, FALSE, ignoreCase);
 | 
						|
	if (status == TRUE) {
 | 
						|
		s = haystack;
 | 
						|
 | 
						|
		do {
 | 
						|
			/* copy stuff from before the match */
 | 
						|
			while (s < re->startp[0])
 | 
						|
				*d++ = *s++;
 | 
						|
			/* substitute for the matched part */
 | 
						|
			regsub(re, newNeedle, d);
 | 
						|
			s = re->endp[0];
 | 
						|
			d += strlen(d);
 | 
						|
		} while (regexec(re, s, FALSE, ignoreCase) == TRUE);
 | 
						|
		/* copy stuff from after the match */
 | 
						|
		while ((*d++ = *s++)) {
 | 
						|
		}
 | 
						|
		d[0] = '\0';
 | 
						|
		strcpy(haystack, buf);
 | 
						|
	}
 | 
						|
	free(re);
 | 
						|
	return (status);
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
/* code swiped from elvis-tiny 1.4 (a clone of vi) and adjusted to 
 | 
						|
 * suit the needs of busybox by Erik Andersen.
 | 
						|
 *
 | 
						|
 * From the README:
 | 
						|
 * "Elvis is freely redistributable, in either source form or executable form.
 | 
						|
 * There are no restrictions on how you may use it".
 | 
						|
 * Elvis was written by Steve Kirkendall <kirkenda@cs.pdx.edu>
 | 
						|
 *
 | 
						|
 *
 | 
						|
 * 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.
 | 
						|
 *
 | 
						|
 */
 | 
						|
 | 
						|
 | 
						|
 | 
						|
static char *previous;			/* the previous regexp, used when null regexp is given */
 | 
						|
 | 
						|
#if defined BB_SED
 | 
						|
static char *previous1;			/* a copy of the text from the previous substitution for regsub() */
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
/* 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_QMARK)
 | 
						|
#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_CLASS(n)	(264+(n))	/* internal code for [] */
 | 
						|
#define M_START(n)	(274+(n))	/* internal code for \( */
 | 
						|
#define M_END(n)	(284+(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)  do {fprintf(stderr, why); longjmp(errorhandler, 1);} while (0)
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/* This function builds a bitmap for a particular class */
 | 
						|
/* text -- start of the class */
 | 
						|
/* bmap -- the bitmap */
 | 
						|
static char *makeclass(char *text, char *bmap)
 | 
						|
{
 | 
						|
	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 M_SPLAT;
 | 
						|
 | 
						|
		case '.':
 | 
						|
			return M_ANY;
 | 
						|
 | 
						|
		case '+':
 | 
						|
			return M_PLUS;
 | 
						|
 | 
						|
		case '?':
 | 
						|
			return M_QMARK;
 | 
						|
 | 
						|
		default:
 | 
						|
			return c;
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		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;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 /*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;
 | 
						|
		} else if (IS_META(token)) {
 | 
						|
			size += 2;
 | 
						|
		} else {
 | 
						|
			size++;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return size;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/*---------------------------------------------------------------------------*/
 | 
						|
 | 
						|
 | 
						|
/* 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.
 | 
						|
 */
 | 
						|
static int match1(regexp * re, char ch, int token, int ignoreCase)
 | 
						|
{
 | 
						|
	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;
 | 
						|
	}
 | 
						|
//fprintf(stderr, "match1: ch='%c' token='%c': ", ch, token);
 | 
						|
	if (ch == token || (ignoreCase == TRUE && tolower(ch) == tolower(token))) {
 | 
						|
//fprintf(stderr, "match\n");
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
//fprintf(stderr, "no match\n");
 | 
						|
	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.
 | 
						|
 */
 | 
						|
/* re   -- the regular expression */
 | 
						|
/* str  -- the string */
 | 
						|
/* prog -- a portion of re->program, an compiled RE */
 | 
						|
/* here -- a portion of str, the string to compare it to */
 | 
						|
static int match(regexp * re, char *str, char *prog, char *here,
 | 
						|
				 int ignoreCase)
 | 
						|
{
 | 
						|
	int token;
 | 
						|
	int nmatched;
 | 
						|
	int 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] == '_' ||
 | 
						|
				 (isascii(here[-1]) && isalnum(here[-1])))) return 1;
 | 
						|
			break;
 | 
						|
 | 
						|
		case M_ENDWORD:
 | 
						|
			if ((here[0] == '_' || isascii(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, ignoreCase) != 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
 | 
						|
	 * the the remainder of the compiled RE.
 | 
						|
	 */
 | 
						|
	closure = token;
 | 
						|
	prog++, token = GET_META(prog);
 | 
						|
	prog++;
 | 
						|
 | 
						|
	/* step 2: see how many times we can match that token against the string */
 | 
						|
	for (nmatched = 0;
 | 
						|
		 (closure != M_QMARK || nmatched < 1) && *here
 | 
						|
		 && match1(re, *here, token, ignoreCase) == 0; nmatched++, here++) {
 | 
						|
	}
 | 
						|
 | 
						|
	/* step 3: try to match the remainder, and back off if it doesn't */
 | 
						|
	while (nmatched >= 0 && match(re, str, prog, here, ignoreCase) != 0) {
 | 
						|
		nmatched--;
 | 
						|
		here--;
 | 
						|
	}
 | 
						|
 | 
						|
	/* so how did it work out? */
 | 
						|
	if (nmatched >= ((closure == M_PLUS) ? 1 : 0))
 | 
						|
		return 0;
 | 
						|
	return 1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* This function compiles a regexp. */
 | 
						|
static regexp *regcomp(char *text)
 | 
						|
{
 | 
						|
	int needfirst;
 | 
						|
	unsigned size;
 | 
						|
	int token;
 | 
						|
	int peek;
 | 
						|
	char *build;
 | 
						|
	regexp *re;
 | 
						|
 | 
						|
 | 
						|
	/* 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 (*text == 0) {
 | 
						|
		if (!previous) {
 | 
						|
			FAIL("No previous RE");
 | 
						|
		}
 | 
						|
		text = previous;
 | 
						|
	} else {					/* non-empty regexp given, so remember it */
 | 
						|
 | 
						|
		if (previous)
 | 
						|
			free(previous);
 | 
						|
		previous = (char *) malloc((unsigned) (strlen(text) + 1));
 | 
						|
		if (previous)
 | 
						|
			strcpy(previous, text);
 | 
						|
	}
 | 
						|
 | 
						|
	/* allocate memory */
 | 
						|
	class_cnt = 0;
 | 
						|
	start_cnt = 1;
 | 
						|
	end_sp = 0;
 | 
						|
	retext = text;
 | 
						|
	size = calcsize(text) + sizeof(regexp);
 | 
						|
	re = (regexp *) malloc((unsigned) size);
 | 
						|
 | 
						|
	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 = text;
 | 
						|
	for (token = M_START(0), peek = gettoken(&text, re);
 | 
						|
		 token; token = peek, peek = gettoken(&text, re)) {
 | 
						|
 | 
						|
		/* special processing for the closure operator */
 | 
						|
		if (IS_CLOSURE(peek)) {
 | 
						|
 | 
						|
			/* detect misuse of closure operator */
 | 
						|
			if (IS_START(token)) {
 | 
						|
				FAIL("* or \\+ or \\? follows nothing");
 | 
						|
			} else if (IS_META(token) && token != M_ANY && !IS_CLASS(token)) {
 | 
						|
				FAIL("* or \\+ or \\? can only follow a normal character or . or []");
 | 
						|
			}
 | 
						|
 | 
						|
			/* it is okay -- make it prefix instead of postfix */
 | 
						|
			ADD_META(build, peek);
 | 
						|
 | 
						|
			/* 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(&text, 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 searches through a string for text that matches an RE. */
 | 
						|
/* re  -- the compiled regexp to search for */
 | 
						|
/* str -- the string to search through */
 | 
						|
/* bol -- does str start at the beginning of a line? (boolean) */
 | 
						|
/* ignoreCase -- ignoreCase or not */
 | 
						|
static int regexec(struct regexp *re, char *str, int bol, int ignoreCase)
 | 
						|
{
 | 
						|
	char *prog;					/* the entry point of re->program */
 | 
						|
	int len;					/* length of the string */
 | 
						|
	char *here;
 | 
						|
 | 
						|
	/* if must start at the beginning of a line, and this isn't, then fail */
 | 
						|
	if (re->bol && bol == TRUE) {
 | 
						|
		return FALSE;
 | 
						|
	}
 | 
						|
 | 
						|
	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, ignoreCase))	/* wrong first letter? */
 | 
						|
			||len < re->minlen	/* not long enough? */
 | 
						|
			|| match(re, (char *) str, prog, str, ignoreCase))	/* doesn't match? */
 | 
						|
			return FALSE;		/* THEN FAIL! */
 | 
						|
	} else if (ignoreCase == FALSE) {
 | 
						|
		/* can occur anywhere in the line, noignorecase */
 | 
						|
		for (here = (char *) str; (re->first && re->first != *here)
 | 
						|
			 || match(re, (char *) str, prog, here, ignoreCase);
 | 
						|
			 here++, len--) {
 | 
						|
			if (len < re->minlen)
 | 
						|
				return FALSE;
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		/* can occur anywhere in the line, ignorecase */
 | 
						|
		for (here = (char *) str;
 | 
						|
			 (re->first && match1(re, *here, (int) re->first, ignoreCase))
 | 
						|
			 || match(re, (char *) str, prog, here, ignoreCase);
 | 
						|
			 here++, len--) {
 | 
						|
			if (len < re->minlen)
 | 
						|
				return FALSE;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* if we didn't fail, then we must have succeeded */
 | 
						|
	return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#if defined BB_SED
 | 
						|
/* This performs substitutions after a regexp match has been found.  */
 | 
						|
static void regsub(regexp * re, char *src, char *dst)
 | 
						|
{
 | 
						|
	char *cpy;
 | 
						|
	char *end;
 | 
						|
	char c;
 | 
						|
	char *start;
 | 
						|
	int mod;
 | 
						|
 | 
						|
	mod = 0;
 | 
						|
 | 
						|
	start = src;
 | 
						|
	while ((c = *src++) != '\0') {
 | 
						|
		/* recognize any meta characters */
 | 
						|
		if (c == '&') {
 | 
						|
			cpy = re->startp[0];
 | 
						|
			end = re->endp[0];
 | 
						|
		} else if (c == '~') {
 | 
						|
			cpy = previous1;
 | 
						|
			if (cpy)
 | 
						|
				end = cpy + strlen(cpy);
 | 
						|
		} else if (c == '\\') {
 | 
						|
			c = *src++;
 | 
						|
			switch (c) {
 | 
						|
			case '0':
 | 
						|
			case '1':
 | 
						|
			case '2':
 | 
						|
			case '3':
 | 
						|
			case '4':
 | 
						|
			case '5':
 | 
						|
			case '6':
 | 
						|
			case '7':
 | 
						|
			case '8':
 | 
						|
			case '9':
 | 
						|
				/* \0 thru \9 mean "copy subexpression" */
 | 
						|
				c -= '0';
 | 
						|
				cpy = re->startp[(int) c];
 | 
						|
				end = re->endp[(int) c];
 | 
						|
				break;
 | 
						|
			case 'U':
 | 
						|
			case 'u':
 | 
						|
			case 'L':
 | 
						|
			case 'l':
 | 
						|
				/* \U and \L mean "convert to upper/lowercase" */
 | 
						|
				mod = c;
 | 
						|
				continue;
 | 
						|
 | 
						|
			case 'E':
 | 
						|
			case 'e':
 | 
						|
				/* \E ends the \U or \L */
 | 
						|
				mod = 0;
 | 
						|
				continue;
 | 
						|
			case '&':
 | 
						|
				/* "\&" means "original text" */
 | 
						|
				*dst++ = c;
 | 
						|
				continue;
 | 
						|
 | 
						|
			case '~':
 | 
						|
				/* "\~" means "previous text, if any" */
 | 
						|
				*dst++ = c;
 | 
						|
				continue;
 | 
						|
			default:
 | 
						|
				/* ordinary char preceded by backslash */
 | 
						|
				*dst++ = c;
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			/* ordinary character, so just copy it */
 | 
						|
			*dst++ = c;
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		/* Note: to reach this point in the code, we must have evaded
 | 
						|
		 * all "continue" statements.  To do that, we must have hit
 | 
						|
		 * a metacharacter that involves copying.
 | 
						|
		 */
 | 
						|
 | 
						|
		/* if there is nothing to copy, loop */
 | 
						|
		if (!cpy)
 | 
						|
			continue;
 | 
						|
 | 
						|
		/* copy over a portion of the original */
 | 
						|
		while (cpy < end) {
 | 
						|
			switch (mod) {
 | 
						|
			case 'U':
 | 
						|
			case 'u':
 | 
						|
				/* convert to uppercase */
 | 
						|
				if (isascii(*cpy) && islower(*cpy)) {
 | 
						|
					*dst++ = toupper(*cpy);
 | 
						|
					cpy++;
 | 
						|
				} else {
 | 
						|
					*dst++ = *cpy++;
 | 
						|
				}
 | 
						|
				break;
 | 
						|
 | 
						|
			case 'L':
 | 
						|
			case 'l':
 | 
						|
				/* convert to lowercase */
 | 
						|
				if (isascii(*cpy) && isupper(*cpy)) {
 | 
						|
					*dst++ = tolower(*cpy);
 | 
						|
					cpy++;
 | 
						|
				} else {
 | 
						|
					*dst++ = *cpy++;
 | 
						|
				}
 | 
						|
				break;
 | 
						|
 | 
						|
			default:
 | 
						|
				/* copy without any conversion */
 | 
						|
				*dst++ = *cpy++;
 | 
						|
			}
 | 
						|
 | 
						|
			/* \u and \l end automatically after the first char */
 | 
						|
			if (mod && (mod == 'u' || mod == 'l')) {
 | 
						|
				mod = 0;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	*dst = '\0';
 | 
						|
 | 
						|
	/* remember what text we inserted this time */
 | 
						|
	if (previous1)
 | 
						|
		free(previous1);
 | 
						|
	previous1 = (char *) malloc((unsigned) (strlen(start) + 1));
 | 
						|
	if (previous1)
 | 
						|
		strcpy(previous1, start);
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
#endif							/* BB_REGEXP */
 |