/* $Id: fnmatch.c,v 1.5 2003/02/11 11:55:16 aa5779 Exp $ */
/* fnmatch.c -- a special implementation for `fnmatch' */
/* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
 * 			 (C) 2002 Artem V. Andreev

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

This library 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
Library General Public License for more details.

You should have received a copy of the GNU Library General Public
License along with this library; see the file COPYING.LIB.  If
not, write to the Free Software Foundation, Inc., 675 Mass Ave,
Cambridge, MA 02139, USA.  */

#include <errno.h>
#include <string.h>
#include <gfnmatch.h>
#include <ctype.h>


/* Comment out all this code if we are using the GNU C Library, and are not
   actually compiling the library itself.  This code is part of the GNU C
   Library, but also included in many other GNU distributions.  Compiling
   and linking in this code is a waste when using the GNU C library
   (especially if it is a shared library).  Rather than having every GNU
   program understand `configure --with-gnu-libc' and omit the object files,
   it is simpler to just do this in the source for each such file.  */

#if !defined(__GNU_LIBRARY__) && !defined(STDC_HEADERS)
extern int errno;
#endif

static int match_char_class(const char *class, int ch);
/* Match STRING against the filename pattern PATTERN, returning zero if
   it matches, nonzero if not.  */
static int
cs_fnmatch (const char *pattern, const char *string, int flags);

static int
ci_fnmatch (const char *pattern, const char *string, int flags);

int
quick_fnmatch (const char *pattern, const char *string, int flags)
{
	return ((flags & FNM_CASEFOLD) ? ci_fnmatch : cs_fnmatch)(pattern, string, flags);
}


static int
cs_fnmatch (const char *pattern, const char *string, int flags)
{
  register const char *p = pattern, *n = string;
  register char c;

/* Note that this evalutes C many times.  */

  while ((c = *p++) != '\0')
    {
      switch (c)
	{
	case '?':
	  if (*n == '\0')
	    return FNM_NOMATCH;
	  break;

	case '\\':
	      c = *p++;
	  if (*n != c)
	    return FNM_NOMATCH;
	  break;

	case '*':
	  for (c = *p++; c == '?' || c == '*'; c = *p++, ++n)
	    if (c == '?' && *n == '\0')
	      return FNM_NOMATCH;

	  if (c == '\0')
	    return 0;

	  {
	    char c1 = c == '\\' ? *p : c;
	    for (--p; *n != '\0'; ++n)
	      if ((c == '[' || *n == c1) &&
		  fnmatch (p, n, flags) == 0)
		return 0;
	    return FNM_NOMATCH;
	  }

	case '[':
	  {
	    /* Nonzero if the sense of the character class is inverted.  */
	    register int not, fg;

	    if (*n == '\0')
	      return FNM_NOMATCH;

	    not = (*p == '!' || *p == '^');
	    if (not)
	      ++p;

	    c = *p++;
	    for (;;)
	      {
		register char cstart = c, cend = c;

		if (c == '\\')
		  cstart = cend = *p++;
		else if( c == '[' && *p == ':')
		{
			fg = match_char_class(p, *n);
			p += 7;
			if(*p != ']')
				p++;
			if(*p != ']')
				return FNM_NOMATCH;
			p++;
			c = *p++;
			if(fg)
				goto matched;
			else if(c == ']') break; else continue;
		}

		if (c == '\0')
		  /* [ (unterminated) loses.  */
		  return FNM_NOMATCH;

		c = *p++;

		if (c == '-' && *p != ']')
		  {
		    cend = *p++;
		    if (cend == '\\')
		      cend = *p++;
		    if (cend == '\0')
		      return FNM_NOMATCH;
		    c = *p++;
		  }
		if (*n >= cstart && *n <= cend)
		  goto matched;

		if (c == ']')
		  break;
	      }
	    if (!not)
	      return FNM_NOMATCH;
	    break;

	  matched:;
	    /* Skip the rest of the [...] that already matched.  */
	    if (not)
	      return FNM_NOMATCH;
	    while (c != ']')
	      {
		if (c == '\0')
		  /* [... (unterminated) loses.  */
		  return FNM_NOMATCH;
		if (c == '[' && *p == ':')
		{
			p++;
			c = *p++;
			while(c != ':')
				c = *p++;
			c= *p++;
		}
		c = *p++;
		if (c == '\\')
		  /* XXX 1003.2d11 is unclear if this is right.  */
		  ++p;
		  }
	  }
	  break;

	default:
	  if (c != *n)
	    return FNM_NOMATCH;
	}

      ++n;
    }

  if (*n == '\0')
    return 0;

  return FNM_NOMATCH;
}

static int
ci_fnmatch (const char *pattern, const char *string, int flags)
{
  register const char *p = pattern, *n = string;
  register char c;

  while ((c = *p++) != '\0')
    {
      switch (c)
	{
	case '?':
	  if (*n == '\0')
	    return FNM_NOMATCH;
	  break;

	case '\\':
	      c = *p++;
	  if (tolower(*n) != tolower(c))
	    return FNM_NOMATCH;
	  break;

	case '*':
	  for (c = *p++; c == '?' || c == '*'; c = *p++, ++n)
	    if (c == '?' && *n == '\0')
	      return FNM_NOMATCH;

	  if (c == '\0')
	    return 0;

	  {
	    char c1 = tolower(c == '\\' ? *p : c);
	    for (--p; *n != '\0'; ++n)
	      if ((c == '[' || tolower(*n) == c1) &&
		  fnmatch (p, n, flags) == 0)
		return 0;
	    return FNM_NOMATCH;
	  }

	case '[':
	  {
	    /* Nonzero if the sense of the character class is inverted.  */
	    register int not, fg;

	    if (*n == '\0')
	      return FNM_NOMATCH;

	    not = (*p == '!' || *p == '^');
	    if (not)
	      ++p;

	    c = *p++;
	    for (;;)
	      {
		register char cstart = c, cend = c;

		if (c == '\\')
		  cstart = cend = *p++;
		else if( c == '[' && *p == ':')
		{
			fg = match_char_class(p, *n);
			p += 7;
			if(*p != ']')
				p++;
			if(*p != ']')
				return FNM_NOMATCH;
			p++;
			c = *p++;
			if(fg)
				goto matched;
			else if(c == ']') break; else continue;
		}

		if (c == '\0')
		  /* [ (unterminated) loses.  */
		  return FNM_NOMATCH;

		c = *p++;

		if (c == '-' && *p != ']')
		  {
		    cend = *p++;
		    if (cend == '\\')
		      cend = *p++;
		    if (cend == '\0')
		      return FNM_NOMATCH;
		    c = *p++;
		  }
		if (tolower(*n) >= tolower(cstart) && tolower(*n) <= tolower(cend))
		  goto matched;

		if (c == ']')
		  break;
	      }
	    if (!not)
	      return FNM_NOMATCH;
	    break;

	  matched:;
	    /* Skip the rest of the [...] that already matched.  */
	    if (not)
	      return FNM_NOMATCH;
	    while (c != ']')
	      {
		if (c == '\0')
		  /* [... (unterminated) loses.  */
		  return FNM_NOMATCH;
		if (c == '[' && *p == ':')
		{
			p++;
			c = *p++;
			while(c != ':')
				c = *p++;
			c= *p++;
		}
		c = *p++;
		if (c == '\\')
		  /* XXX 1003.2d11 is unclear if this is right.  */
		  ++p;
		  }
	  }
	  break;

	default:
	  if (tolower(c) != tolower(*n))
	    return FNM_NOMATCH;
	}

      ++n;
    }

  if (*n == '\0')
    return 0;

  return FNM_NOMATCH;
}


static int match_char_class(const char *class, int ch)
{
	if(!strncmp(class, ":alpha:", 7))
		return isalpha(ch);
	else if(!strncmp(class, ":digit:", 7))
		return isdigit(ch);
	else if(!strncmp(class, ":alnum:", 7))
		return isalnum(ch);
	else if(!strncmp(class, ":upper:", 7))
		return isupper(ch);
	else if(!strncmp(class, ":lower:", 7))
		return islower(ch);
	else if(!strncmp(class, ":space:", 7))
		return isspace(ch);
	else if(!strncmp(class, ":punct:", 7))
		return ispunct(ch);
	else if(!strncmp(class, ":graph:", 7))
		return isgraph(ch);
	else if(!strncmp(class, ":cntrl:", 7))
		return iscntrl(ch);
	else if(!strncmp(class, ":print:", 7))
		return isprint(ch);
	else if(!strncmp(class, ":xdigit:", 8))
		return isxdigit(ch);
	else if(!strncmp(class, ":blank:", 7))
		return ch == ' ' || ch == '\t';
	else return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1