match.c 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. /*
  2. * ngIRCd -- The Next Generation IRC Daemon
  3. * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. * Please read the file COPYING, README and AUTHORS for more information.
  10. */
  11. #include "portab.h"
  12. /**
  13. * @file
  14. * Wildcard pattern matching
  15. */
  16. #include "imp.h"
  17. #include <assert.h>
  18. #include <string.h>
  19. #include "exp.h"
  20. #include "match.h"
  21. #include "defines.h"
  22. #include "tool.h"
  23. /*
  24. * The pattern matching functions [Matche(), Matche_After_Star()] are based
  25. * on code of J. Kercheval. Version 1.1 has been released on 1991-03-12 as
  26. * "public domain": <http://c.snippets.org/snip_lister.php?fname=match.c>
  27. */
  28. static int Matche PARAMS(( const char *p, const char *t ));
  29. static int Matche_After_Star PARAMS(( const char *p, const char *t ));
  30. #define MATCH_PATTERN 6 /**< bad pattern */
  31. #define MATCH_LITERAL 5 /**< match failure on literal match */
  32. #define MATCH_RANGE 4 /**< match failure on [..] construct */
  33. #define MATCH_ABORT 3 /**< premature end of text string */
  34. #define MATCH_END 2 /**< premature end of pattern string */
  35. #define MATCH_VALID 1 /**< valid match */
  36. /**
  37. * Match string with pattern.
  38. *
  39. * @param Pattern Pattern to match with
  40. * @param String Input string
  41. * @return true if pattern matches
  42. */
  43. GLOBAL bool
  44. Match( const char *Pattern, const char *String )
  45. {
  46. if( Matche( Pattern, String ) == MATCH_VALID ) return true;
  47. else return false;
  48. } /* Match */
  49. /**
  50. * Match string with pattern case-insensitive.
  51. *
  52. * @param Pattern Pattern to match with
  53. * @param String Input string, at most COMMAND_LEN-1 characters long
  54. * @return true if pattern matches
  55. */
  56. GLOBAL bool
  57. MatchCaseInsensitive(const char *Pattern, const char *String)
  58. {
  59. char haystack[COMMAND_LEN];
  60. strlcpy(haystack, String, sizeof(haystack));
  61. return Match(Pattern, ngt_LowerStr(haystack));
  62. } /* MatchCaseInsensitive */
  63. /**
  64. * Match string with pattern case-insensitive.
  65. *
  66. * @param pattern Pattern to match with
  67. * @param String Input string, at most COMMAND_LEN-1 characters long
  68. * @param Separator Character separating the individual patterns in the list
  69. * @return true if pattern matches
  70. */
  71. GLOBAL bool
  72. MatchCaseInsensitiveList(const char *Pattern, const char *String,
  73. const char *Separator)
  74. {
  75. char tmp_pattern[COMMAND_LEN], haystack[COMMAND_LEN], *ptr;
  76. strlcpy(tmp_pattern, Pattern, sizeof(tmp_pattern));
  77. strlcpy(haystack, String, sizeof(haystack));
  78. ngt_LowerStr(haystack);
  79. ptr = strtok(tmp_pattern, Separator);
  80. while (ptr) {
  81. ngt_TrimStr(ptr);
  82. if (Match(ptr, haystack))
  83. return true;
  84. ptr = strtok(NULL, Separator);
  85. }
  86. return false;
  87. } /* MatchCaseInsensitive */
  88. static int
  89. Matche( const char *p, const char *t )
  90. {
  91. register char range_start, range_end;
  92. bool invert;
  93. bool member_match;
  94. bool loop;
  95. for( ; *p; p++, t++ )
  96. {
  97. /* if this is the end of the text then this is the end of the match */
  98. if( ! *t )
  99. {
  100. return ( *p == '*' && *++p == '\0' ) ? MATCH_VALID : MATCH_ABORT;
  101. }
  102. /* determine and react to pattern type */
  103. switch( *p )
  104. {
  105. case '?': /* single any character match */
  106. break;
  107. case '*': /* multiple any character match */
  108. return Matche_After_Star( p, t );
  109. case '[': /* [..] construct, single member/exclusion character match */
  110. /* move to beginning of range */
  111. p++;
  112. /* check if this is a member match or exclusion match */
  113. invert = false;
  114. if( *p == '!' || *p == '^' )
  115. {
  116. invert = true;
  117. p++;
  118. }
  119. /* if closing bracket here or at range start then we have a malformed pattern */
  120. if ( *p == ']' ) return MATCH_PATTERN;
  121. member_match = false;
  122. loop = true;
  123. while( loop )
  124. {
  125. /* if end of construct then loop is done */
  126. if( *p == ']' )
  127. {
  128. loop = false;
  129. continue;
  130. }
  131. /* matching a '!', '^', '-', '\' or a ']' */
  132. if( *p == '\\' ) range_start = range_end = *++p;
  133. else range_start = range_end = *p;
  134. /* if end of pattern then bad pattern (Missing ']') */
  135. if( ! *p ) return MATCH_PATTERN;
  136. /* check for range bar */
  137. if( *++p == '-' )
  138. {
  139. /* get the range end */
  140. range_end = *++p;
  141. /* if end of pattern or construct then bad pattern */
  142. if( range_end == '\0' || range_end == ']' ) return MATCH_PATTERN;
  143. /* special character range end */
  144. if( range_end == '\\' )
  145. {
  146. range_end = *++p;
  147. /* if end of text then we have a bad pattern */
  148. if ( ! range_end ) return MATCH_PATTERN;
  149. }
  150. /* move just beyond this range */
  151. p++;
  152. }
  153. /* if the text character is in range then match found. make sure the range
  154. * letters have the proper relationship to one another before comparison */
  155. if( range_start < range_end )
  156. {
  157. if( *t >= range_start && *t <= range_end )
  158. {
  159. member_match = true;
  160. loop = false;
  161. }
  162. }
  163. else
  164. {
  165. if( *t >= range_end && *t <= range_start )
  166. {
  167. member_match = true;
  168. loop = false;
  169. }
  170. }
  171. }
  172. /* if there was a match in an exclusion set then no match */
  173. /* if there was no match in a member set then no match */
  174. if(( invert && member_match ) || ! ( invert || member_match )) return MATCH_RANGE;
  175. /* if this is not an exclusion then skip the rest of the [...]
  176. * construct that already matched. */
  177. if( member_match )
  178. {
  179. while( *p != ']' )
  180. {
  181. /* bad pattern (Missing ']') */
  182. if( ! *p ) return MATCH_PATTERN;
  183. /* skip exact match */
  184. if( *p == '\\' )
  185. {
  186. p++;
  187. /* if end of text then we have a bad pattern */
  188. if( ! *p ) return MATCH_PATTERN;
  189. }
  190. /* move to next pattern char */
  191. p++;
  192. }
  193. }
  194. break;
  195. case '\\': /* next character is quoted and must match exactly */
  196. /* move pattern pointer to quoted char and fall through */
  197. p++;
  198. /* if end of text then we have a bad pattern */
  199. if( ! *p ) return MATCH_PATTERN;
  200. /* must match this character exactly */
  201. default:
  202. if( *p != *t ) return MATCH_LITERAL;
  203. }
  204. }
  205. /* if end of text not reached then the pattern fails */
  206. if( *t ) return MATCH_END;
  207. else return MATCH_VALID;
  208. } /* Matche */
  209. static int
  210. Matche_After_Star( const char *p, const char *t )
  211. {
  212. register int nextp, match = 0;
  213. /* pass over existing ? and * in pattern */
  214. while( *p == '?' || *p == '*' )
  215. {
  216. /* take one char for each ? and + */
  217. if (*p == '?')
  218. {
  219. /* if end of text then no match */
  220. if( ! *t++ ) return MATCH_ABORT;
  221. }
  222. /* move to next char in pattern */
  223. p++;
  224. }
  225. /* if end of pattern we have matched regardless of text left */
  226. if( ! *p ) return MATCH_VALID;
  227. /* get the next character to match which must be a literal or '[' */
  228. nextp = *p;
  229. if( nextp == '\\' )
  230. {
  231. nextp = p[1];
  232. /* if end of text then we have a bad pattern */
  233. if( ! nextp ) return MATCH_PATTERN;
  234. }
  235. /* Continue until we run out of text or definite result seen */
  236. do
  237. {
  238. /* a precondition for matching is that the next character
  239. * in the pattern match the next character in the text or that
  240. * the next pattern char is the beginning of a range. Increment
  241. * text pointer as we go here */
  242. if( nextp == *t || nextp == '[' ) match = Matche( p, t );
  243. /* if the end of text is reached then no match */
  244. if( ! *t++ ) match = MATCH_ABORT;
  245. } while( match != MATCH_VALID && match != MATCH_ABORT && match != MATCH_PATTERN );
  246. /* return result */
  247. return match;
  248. } /* Matche_After_Star */
  249. /* -eof- */