/**************************************************************************************************
	$Id: wildcard.c,v 1.7 2005/04/20 16:49:11 bboy Exp $

	wildcard.c: Wildcard matching routines.

	Originally authored and placed in the public domain by J. Kercheval.

	Copyright (C) 2002-2005  Don Moore <bboy@bboy.net>

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at Your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
**************************************************************************************************/

#include "mydnsutil.h"

#define _WC_MATCH_PATTERN  6    /* bad pattern */
#define _WC_MATCH_LITERAL  5    /* match failure on literal match */
#define _WC_MATCH_RANGE    4    /* match failure on [..] construct */
#define _WC_MATCH_ABORT    3    /* premature end of text string */
#define _WC_MATCH_END      2    /* premature end of pattern string */
#define _WC_MATCH_VALID    1    /* valid match */


/**************************************************************************************************
	WILDCARD_VALID
	Returns 1 if the specified string is valid, 0 if it contains a syntax error.
**************************************************************************************************/
int
wildcard_valid(char *p)
{
	/* loop through pattern to EOS */
	while (*p)
	{
		switch (*p)
		{
			case '\\':
				if (!*++p)
					return 0;
				p++;
				break;

			case '[':
				p++;
				if (*p == ']')
					return 0;
				if (!*p)
					return 0;

				while (*p != ']')
				{
					if (*p == '\\')
					{
						p++;

						if (!*p++)
							return 0;
					}
					else
						p++;

					if (!*p)
						return 0;

					if (*p == '-')
					{
						if (!*++p || *p == ']')
							return 0;
						else
						{
							if (*p == '\\')
								p++;
							if (!*p++)
								return 0;
						}
					}
				}
				break;

				case '*':
				case '?':
				default:
					p++;
					break;
		}
	}
	return 1;
}
/*--- wildcard_valid() --------------------------------------------------------------------------*/


/**************************************************************************************************
	MATCH_AFTER_STAR
	Recursively call wildcard_match() with final segment of PATTERN and of TEXT.
**************************************************************************************************/
static int
match_after_star(register char *p, register char *t)
{
	register int match = 0, nextp;

	while (*p == '?' || *p == '*')
	{
		if (*p == '?')
		{
			if (!*t++)
				return _WC_MATCH_ABORT;
		}
		p++;
	}

	if (!*p)
		return _WC_MATCH_VALID;

	nextp = *p;
	if (nextp == '\\')
	{
		nextp = p[1];

		if (!nextp)
			return _WC_MATCH_PATTERN;
	}

	do
	{
		if (nextp == *t || nextp == '[')
			match = wildcard_match(p, t);

		if (!*t++)
			match = _WC_MATCH_ABORT;
	}
	while (match != _WC_MATCH_VALID && match != _WC_MATCH_ABORT && match != _WC_MATCH_PATTERN);

	return match;
}
/*--- match_after_star() ------------------------------------------------------------------------*/


/**************************************************************************************************
	_WILDCARD_MATCH
	Match pattern (p) against text (t).
**************************************************************************************************/
int
_wildcard_match(register char *p, register char *t)
{
	register char range_start, range_end;	/* start and end in range */

	int invert;			/* is this [..] or [!..] */
	int member_match;		/* have I matched the [..] construct? */
	int loop;			/* should I terminate? */

    for (; *p; p++, t++)
    {
	/* if this is the end of the text
	   then this is the end of the match */

	if (!*t)
	{
	    return (*p == '*' && *++p == '\0') ? _WC_MATCH_VALID : _WC_MATCH_ABORT;
	}

	/* determine and react to pattern type */

	switch (*p)
	{
	case '?':		/* single any character match */
	    break;

	case '*':		/* multiple any character match */
	    return match_after_star(p, t);

	    /* [..] construct, single member/exclusion character match */
	case '[':
	    {
		/* move to beginning of range */

		p++;

		/* check if this is a member match or exclusion match */

		invert = 0;
		if (*p == '!' || *p == '^')
		{
		    invert = 1;
		    p++;
		}

		/* if closing bracket here or at range start then we have a
		   malformed pattern */

		if (*p == ']')
		{
		    return _WC_MATCH_PATTERN;
		}

		member_match = 0;
		loop = 1;

		while (loop)
		{
		    /* if end of construct then loop is done */

		    if (*p == ']')
		    {
			loop = 0;
			continue;
		    }

		    /* matching a '!', '^', '-', '\' or a ']' */

		    if (*p == '\\')
		    {
			range_start = range_end = *++p;
		    } else
			range_start = range_end = *p;

		    /* if end of pattern then bad pattern (Missing ']') */

		    if (!*p)
			return _WC_MATCH_PATTERN;

		    /* check for range bar */
		    if (*++p == '-')
		    {
			/* get the range end */

			range_end = *++p;

			/* if end of pattern or construct
			   then bad pattern */

			if (range_end == '\0' || range_end == ']')
			    return _WC_MATCH_PATTERN;

			/* special character range end */
			if (range_end == '\\')
			{
			    range_end = *++p;

			    /* if end of text then
			       we have a bad pattern */
			    if (!range_end)
				return _WC_MATCH_PATTERN;
			}

			/* move just beyond this range */
			p++;
		    }

		    /* if the text character is in range then match found.
		       make sure the range letters have the proper
		       relationship to one another before comparison */

		    if (range_start < range_end)
		    {
			if (*t >= range_start && *t <= range_end)
			{
			    member_match = 1;
			    loop = 0;
			}
		    } else
		    {
			if (*t >= range_end && *t <= range_start)
			{
			    member_match = 1;
			    loop = 0;
			}
		    }
		}

		/* if there was a match in an exclusion set then no match */
		/* if there was no match in a member set then no match */

		if ((invert && member_match) || !(invert || member_match))
		    return _WC_MATCH_RANGE;

		/* if this is not an exclusion then skip the rest of
		   the [...] construct that already matched. */

		if (member_match)
		{
		    while (*p != ']')
		    {
			/* bad pattern (Missing ']') */
			if (!*p)
			    return _WC_MATCH_PATTERN;

			/* skip exact match */
			if (*p == '\\')
			{
			    p++;

			    /* if end of text then
			       we have a bad pattern */

			    if (!*p)
				return _WC_MATCH_PATTERN;
			}

			/* move to next pattern char */

			p++;
		    }
		}
		break;
	    }
	case '\\':		/* next character is quoted and must match exactly */

	    /* move pattern pointer to quoted char and fall through */

	    p++;

	    /* if end of text then we have a bad pattern */

	    if (!*p)
		return _WC_MATCH_PATTERN;

	    /* must match this character exactly */

	default:
	    if (*p != *t)
		return _WC_MATCH_LITERAL;
	}
    }
    /* if end of text not reached then the pattern fails */

    if (*t)
	return _WC_MATCH_END;
    else
	return _WC_MATCH_VALID;
}
/*--- _wildcard_match() -------------------------------------------------------------------------*/


/**************************************************************************************************
	WILDCARD_MATCH
	Match pattern (p) against text (t).
**************************************************************************************************/
int
wildcard_match(register char *p, register char *t)
{
	return (_wildcard_match(p, t) == _WC_MATCH_VALID);
}
/*--- wildcard_match() --------------------------------------------------------------------------*/

/* vi:set ts=3: */


syntax highlighted by Code2HTML, v. 0.9.1