fnmatch.c 3.8 KB
Newer Older
B
bernard.xiong 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
#include <ctype.h>
#include <fnmatch.h>
#include <string.h>

#define NOTFIRST 128

#define STRUCT_CHARCLASS(c) { #c , is##c }

static struct charclass {
  char * class;
  int (*istype)(int);
} allclasses[] = {
  STRUCT_CHARCLASS(alnum),
  STRUCT_CHARCLASS(alpha),
  STRUCT_CHARCLASS(blank),
  STRUCT_CHARCLASS(cntrl),
  STRUCT_CHARCLASS(digit),
  STRUCT_CHARCLASS(graph),
  STRUCT_CHARCLASS(lower),
  STRUCT_CHARCLASS(print),
  STRUCT_CHARCLASS(punct),
  STRUCT_CHARCLASS(space),
  STRUCT_CHARCLASS(upper),
  STRUCT_CHARCLASS(xdigit),
};

/* look for "class:]" in pattern */
static struct charclass *charclass_lookup(const char *pattern) {
  unsigned int i;

  for (i = 0; i< sizeof(allclasses)/sizeof(*allclasses); i++) {
    int len = strlen(allclasses[i].class);
    if (!strncmp(pattern, allclasses[i].class, len)) {
      pattern += len;
      if (strncmp(pattern, ":]", 2)) goto noclass;
      return &allclasses[i];
    }
  }
noclass:
  return NULL;
}

static int match(char c,char d,int flags) {
  if (flags&FNM_CASEFOLD)
    return (tolower(c)==tolower(d));
  else
    return (c==d);
}

int fnmatch(const char *pattern, const char *string, int flags) {
  if (*string==0) {
    while (*pattern=='*') ++pattern;
    return (!!*pattern);
  }
  if (*string=='.' && *pattern!='.' && (flags&FNM_PERIOD)) {
    /* don't match if FNM_PERIOD and this is the first char */
    if (!(flags&NOTFIRST))
      return FNM_NOMATCH;
    /* don't match if FNM_PERIOD and FNM_PATHNAME and previous was '/' */
    if ((flags&(FNM_PATHNAME)) && string[-1]=='/')
      return FNM_NOMATCH;
  }
  flags|=NOTFIRST;
  switch (*pattern) {
  case '[':
    {
      int neg=0;
      const char* start;	/* first member of character class */

      ++pattern;
      if (*string=='/' && flags&FNM_PATHNAME) return FNM_NOMATCH;
      if (*pattern=='!') { neg=1; ++pattern; }
      start=pattern;
      while (*pattern) {
	int res=0;

	if (*pattern==']' && pattern!=start) break;
	if (*pattern=='[' && pattern[1]==':') {
	  /* MEMBER - stupid POSIX char classes */
	  const struct charclass *cc;

	  if (!(cc = charclass_lookup(pattern+2))) goto invalidclass;
	  pattern += strlen(cc->class) + 4;
	  if (flags&FNM_CASEFOLD
	      && (cc->istype == isupper || cc->istype == islower)) {
	    res = islower(tolower(*string));
	  } else {
            res = ((*(cc->istype))(*string));
	  }
	} else {
invalidclass:
	  if (pattern[1]=='-' && pattern[2]!=']') {
	    /* MEMBER - character range */
	    if (*string>=*pattern && *string<=pattern[2]) res=1;
	    if (flags&FNM_CASEFOLD) {
	      if (tolower(*string)>=tolower(*pattern) && tolower(*string)<=tolower(pattern[2])) res=1;
	    }
	    pattern+=3;
	  } else {
	    /* MEMBER - literal character match */
	    res=match(*pattern,*string,flags);
	    ++pattern;
	  }
	}
	if ((res&&!neg) || ((neg&&!res) && *pattern==']')) {
	  while (*pattern && *pattern!=']') ++pattern;
	  return fnmatch(pattern+!!*pattern,string+1,flags);
	} else if (res && neg)
	  return FNM_NOMATCH;
      }
    }
    break;
  case '\\':
    if (flags&FNM_NOESCAPE) {
      if (*string=='\\')
	return fnmatch(pattern+1,string+1,flags);
    } else {
      if (*string==pattern[1])
	return fnmatch(pattern+2,string+1,flags);
    }
    break;
  case '*':
    if ((*string=='/' && flags&FNM_PATHNAME) || fnmatch(pattern,string+1,flags))
      return fnmatch(pattern+1,string,flags);
    return 0;
  case 0:
    if (*string==0 || (*string=='/' && (flags&FNM_LEADING_DIR)))
      return 0;
    break;
  case '?':
    if (*string=='/' && flags&FNM_PATHNAME) break;
    return fnmatch(pattern+1,string+1,flags);
  default:
    if (match(*pattern,*string,flags))
      return fnmatch(pattern+1,string+1,flags);
    break;
  }
  return FNM_NOMATCH;
}