fnmatch.c 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. #include <ctype.h>
  2. #include <fnmatch.h>
  3. #include <string.h>
  4. #define NOTFIRST 128
  5. #define STRUCT_CHARCLASS(c) { #c , is##c }
  6. static struct charclass {
  7. char * class;
  8. int (*istype)(int);
  9. } allclasses[] = {
  10. STRUCT_CHARCLASS(alnum),
  11. STRUCT_CHARCLASS(alpha),
  12. STRUCT_CHARCLASS(blank),
  13. STRUCT_CHARCLASS(cntrl),
  14. STRUCT_CHARCLASS(digit),
  15. STRUCT_CHARCLASS(graph),
  16. STRUCT_CHARCLASS(lower),
  17. STRUCT_CHARCLASS(print),
  18. STRUCT_CHARCLASS(punct),
  19. STRUCT_CHARCLASS(space),
  20. STRUCT_CHARCLASS(upper),
  21. STRUCT_CHARCLASS(xdigit),
  22. };
  23. /* look for "class:]" in pattern */
  24. static struct charclass *charclass_lookup(const char *pattern) {
  25. unsigned int i;
  26. for (i = 0; i< sizeof(allclasses)/sizeof(*allclasses); i++) {
  27. int len = strlen(allclasses[i].class);
  28. if (!strncmp(pattern, allclasses[i].class, len)) {
  29. pattern += len;
  30. if (strncmp(pattern, ":]", 2)) goto noclass;
  31. return &allclasses[i];
  32. }
  33. }
  34. noclass:
  35. return NULL;
  36. }
  37. static int match(char c,char d,int flags) {
  38. if (flags&FNM_CASEFOLD)
  39. return (tolower(c)==tolower(d));
  40. else
  41. return (c==d);
  42. }
  43. int fnmatch(const char *pattern, const char *string, int flags) {
  44. if (*string==0) {
  45. while (*pattern=='*') ++pattern;
  46. return (!!*pattern);
  47. }
  48. if (*string=='.' && *pattern!='.' && (flags&FNM_PERIOD)) {
  49. /* don't match if FNM_PERIOD and this is the first char */
  50. if (!(flags&NOTFIRST))
  51. return FNM_NOMATCH;
  52. /* don't match if FNM_PERIOD and FNM_PATHNAME and previous was '/' */
  53. if ((flags&(FNM_PATHNAME)) && string[-1]=='/')
  54. return FNM_NOMATCH;
  55. }
  56. flags|=NOTFIRST;
  57. switch (*pattern) {
  58. case '[':
  59. {
  60. int neg=0;
  61. const char* start; /* first member of character class */
  62. ++pattern;
  63. if (*string=='/' && flags&FNM_PATHNAME) return FNM_NOMATCH;
  64. if (*pattern=='!') { neg=1; ++pattern; }
  65. start=pattern;
  66. while (*pattern) {
  67. int res=0;
  68. if (*pattern==']' && pattern!=start) break;
  69. if (*pattern=='[' && pattern[1]==':') {
  70. /* MEMBER - stupid POSIX char classes */
  71. const struct charclass *cc;
  72. if (!(cc = charclass_lookup(pattern+2))) goto invalidclass;
  73. pattern += strlen(cc->class) + 4;
  74. if (flags&FNM_CASEFOLD
  75. && (cc->istype == isupper || cc->istype == islower)) {
  76. res = islower(tolower(*string));
  77. } else {
  78. res = ((*(cc->istype))(*string));
  79. }
  80. } else {
  81. invalidclass:
  82. if (pattern[1]=='-' && pattern[2]!=']') {
  83. /* MEMBER - character range */
  84. if (*string>=*pattern && *string<=pattern[2]) res=1;
  85. if (flags&FNM_CASEFOLD) {
  86. if (tolower(*string)>=tolower(*pattern) && tolower(*string)<=tolower(pattern[2])) res=1;
  87. }
  88. pattern+=3;
  89. } else {
  90. /* MEMBER - literal character match */
  91. res=match(*pattern,*string,flags);
  92. ++pattern;
  93. }
  94. }
  95. if ((res&&!neg) || ((neg&&!res) && *pattern==']')) {
  96. while (*pattern && *pattern!=']') ++pattern;
  97. return fnmatch(pattern+!!*pattern,string+1,flags);
  98. } else if (res && neg)
  99. return FNM_NOMATCH;
  100. }
  101. }
  102. break;
  103. case '\\':
  104. if (flags&FNM_NOESCAPE) {
  105. if (*string=='\\')
  106. return fnmatch(pattern+1,string+1,flags);
  107. } else {
  108. if (*string==pattern[1])
  109. return fnmatch(pattern+2,string+1,flags);
  110. }
  111. break;
  112. case '*':
  113. if ((*string=='/' && flags&FNM_PATHNAME) || fnmatch(pattern,string+1,flags))
  114. return fnmatch(pattern+1,string,flags);
  115. return 0;
  116. case 0:
  117. if (*string==0 || (*string=='/' && (flags&FNM_LEADING_DIR)))
  118. return 0;
  119. break;
  120. case '?':
  121. if (*string=='/' && flags&FNM_PATHNAME) break;
  122. return fnmatch(pattern+1,string+1,flags);
  123. default:
  124. if (match(*pattern,*string,flags))
  125. return fnmatch(pattern+1,string+1,flags);
  126. break;
  127. }
  128. return FNM_NOMATCH;
  129. }