File: fnmatch.c

package info (click to toggle)
dietlibc 0.34~cvs20160606-10
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 11,336 kB
  • sloc: ansic: 71,631; asm: 13,006; cpp: 1,860; makefile: 799; sh: 292; perl: 62
file content (139 lines) | stat: -rw-r--r-- 3,738 bytes parent folder | download | duplicates (11)
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;
}