match.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. /*
  2. * match.c
  3. * wildcard matching functions
  4. * (rename to reg.c for ircII)
  5. *
  6. * $Id: match.c,v 1.5 1999/12/22 20:30:03 guppy Exp $
  7. */
  8. /*
  9. * Once this code was working, I added support for % so that I could
  10. * use the same code both in Eggdrop and in my IrcII client.
  11. * Pleased with this, I added the option of a fourth wildcard, ~,
  12. * which matches varying amounts of whitespace (at LEAST one space,
  13. * though, for sanity reasons).
  14. *
  15. * This code would not have been possible without the prior work and
  16. * suggestions of various sourced. Special thanks to Robey for
  17. * all his time/help tracking down bugs and his ever-helpful advice.
  18. *
  19. * 04/09: Fixed the "*\*" against "*a" bug (caused an endless loop)
  20. *
  21. * Chris Fuller (aka Fred1@IRC & Fwitz@IRC)
  22. * crf@cfox.bchs.uh.edu
  23. *
  24. * I hereby release this code into the public domain
  25. *
  26. */
  27. /*
  28. * This will get us around most of the mess and replace the chunk that
  29. * was removed from the middle of this file. --+ Dagmar
  30. */
  31. /*
  32. * You'll also want to grab the rfc1459.c file or change all rfc_*()
  33. * calls to the standard library call to make this work with ircII
  34. * derivatives now.
  35. */
  36. #ifdef HAVE_CONFIG_H
  37. # include <config.h>
  38. #endif
  39. /* Remove the next line to use this in IrcII */
  40. #define EGGDROP
  41. /*
  42. * Best to leave stuff after this point alone, but go on and change
  43. * it if you're adventurous...
  44. */
  45. /* The quoting character -- what overrides wildcards (do not undef) */
  46. #define QUOTE '\\'
  47. /* The "matches ANYTHING" wildcard (do not undef) */
  48. #define WILDS '*'
  49. /* The "matches ANY NUMBER OF NON-SPACE CHARS" wildcard (do not undef) */
  50. #define WILDP '%'
  51. /* The "matches EXACTLY ONE CHARACTER" wildcard (do not undef) */
  52. #define WILDQ '?'
  53. /* The "matches AT LEAST ONE SPACE" wildcard (undef me to disable!) */
  54. #define WILDT '~'
  55. /*
  56. * This makes sure WILDT doesn't get used in in the IrcII version of
  57. * this code. If ya wanna live dangerously, you can remove these 3
  58. * lines, but WARNING: IT WOULD MAKE THIS CODE INCOMPATIBLE WITH THE
  59. * CURRENT reg.c OF IrcII!!! Support for ~ is NOT in the reg.c of
  60. * IrcII, and adding it may cause compatibility problems, especially
  61. * in scripts. If you don't think you have to worry about that, go
  62. * for it!
  63. */
  64. #ifndef EGGDROP
  65. #undef WILDT
  66. #endif
  67. /*
  68. * If you edit below this line and it stops working, don't even THINK
  69. * about whining to *ME* about it!
  70. */
  71. /*
  72. * No problem, you got it wrong anyway, Chris. You should have gone to
  73. * uppercase instead of lowercase. (A really minor mistake)
  74. */
  75. /* Changing these is probably counter-productive :) */
  76. #define MATCH (match+saved+sofar)
  77. #define NOMATCH 0
  78. /*
  79. * EGGDROP: wild_match_per(char *m, char *n)
  80. * IrcII: wild_match(char *m, char *n)
  81. *
  82. * Features: Forward, case-insensitive, ?, *, %, ~(optional)
  83. * Best use: Generic string matching, such as in IrcII-esque bindings
  84. */
  85. #ifdef EGGDROP
  86. static int wild_match_per(register unsigned char *m, register unsigned char *n)
  87. #else
  88. int wild_match(register unsigned char *m, register unsigned char *n)
  89. #endif
  90. {
  91. unsigned char *ma = m, *lsm = 0, *lsn = 0, *lpm = 0, *lpn = 0;
  92. int match = 1, saved = 0;
  93. register unsigned int sofar = 0;
  94. #ifdef WILDT
  95. int space;
  96. #endif
  97. /* take care of null strings (should never match) */
  98. if ((m == 0) || (n == 0) || (!*n))
  99. return NOMATCH;
  100. /* (!*m) test used to be here, too, but I got rid of it. After all,
  101. * If (!*n) was false, there must be a character in the name (the
  102. * second string), so if the mask is empty it is a non-match. Since
  103. * the algorithm handles this correctly without testing for it here
  104. * and this shouldn't be called with null masks anyway, it should be
  105. * a bit faster this way */
  106. while (*n) {
  107. /* Used to test for (!*m) here, but this scheme seems to work better */
  108. #ifdef WILDT
  109. if (*m == WILDT) { /* Match >=1 space */
  110. space = 0; /* Don't need any spaces */
  111. do {
  112. m++;
  113. space++;
  114. } /* Tally 1 more space ... */
  115. while ((*m == WILDT) || (*m == ' ')); /* for each space or ~ */
  116. sofar += space; /* Each counts as exact */
  117. while (*n == ' ') {
  118. n++;
  119. space--;
  120. } /* Do we have enough? */
  121. if (space <= 0)
  122. continue; /* Had enough spaces! */
  123. }
  124. /* Do the fallback */
  125. else {
  126. #endif
  127. switch (*m) {
  128. case 0:
  129. do
  130. m--; /* Search backwards */
  131. while ((m > ma) && (*m == '?')); /* For first non-? char */
  132. if ((m > ma) ? ((*m == '*') && (m[-1] != QUOTE)) : (*m == '*'))
  133. return MATCH; /* nonquoted * = match */
  134. break;
  135. case WILDP:
  136. while (*(++m) == WILDP); /* Zap redundant %s */
  137. if (*m != WILDS) { /* Don't both if next=* */
  138. if (*n != ' ') { /* WILDS can't match ' ' */
  139. lpm = m;
  140. lpn = n; /* Save '%' fallback spot */
  141. saved += sofar;
  142. sofar = 0; /* And save tally count */
  143. }
  144. continue; /* Done with '%' */
  145. }
  146. /* FALL THROUGH */
  147. case WILDS:
  148. do
  149. m++; /* Zap redundant wilds */
  150. while ((*m == WILDS) || (*m == WILDP));
  151. lsm = m;
  152. lsn = n;
  153. lpm = 0; /* Save '*' fallback spot */
  154. match += (saved + sofar); /* Save tally count */
  155. saved = sofar = 0;
  156. continue; /* Done with '*' */
  157. case WILDQ:
  158. m++;
  159. n++;
  160. continue; /* Match one char */
  161. case QUOTE:
  162. m++; /* Handle quoting */
  163. }
  164. if (rfc_toupper(*m) == rfc_toupper(*n)) { /* If matching */
  165. m++;
  166. n++;
  167. sofar++;
  168. continue; /* Tally the match */
  169. }
  170. #ifdef WILDT
  171. }
  172. #endif
  173. if (lpm) { /* Try to fallback on '%' */
  174. n = ++lpn;
  175. m = lpm;
  176. sofar = 0; /* Restore position */
  177. if ((*n | 32) == 32)
  178. lpm = 0; /* Can't match 0 or ' ' */
  179. continue; /* Next char, please */
  180. }
  181. if (lsm) { /* Try to fallback on '*' */
  182. n = ++lsn;
  183. m = lsm; /* Restore position */
  184. /* Used to test for (!*n) here but it wasn't necessary so it's gone */
  185. saved = sofar = 0;
  186. continue; /* Next char, please */
  187. }
  188. return NOMATCH; /* No fallbacks=No match */
  189. }
  190. while ((*m == WILDS) || (*m == WILDP))
  191. m++; /* Zap leftover %s & *s */
  192. return (*m) ? NOMATCH : MATCH; /* End of both = match */
  193. }
  194. #ifndef EGGDROP
  195. /* For IrcII compatibility */
  196. int _wild_match(ma, na)
  197. register unsigned char *ma, *na;
  198. {
  199. return wild_match(ma, na) - 1; /* Don't think IrcII's code
  200. * actually uses this directly,
  201. * but just in case */
  202. }
  203. int match(ma, na)
  204. register unsigned char *ma, *na;
  205. {
  206. return wild_match(ma, na) ? 1 : 0; /* Returns 1 for match,
  207. * 0 for non-match */
  208. }
  209. #else
  210. /*
  211. * Remaining code is not used by IrcII
  212. */
  213. /*
  214. * For this matcher, sofar's high bit is used as a flag of whether or
  215. * not we are quoting. The other matchers don't need this because
  216. * when you're going forward, you just skip over the quote char.
  217. */
  218. #define UNQUOTED (0x7FFF)
  219. #define QUOTED (0x8000)
  220. #undef MATCH
  221. #define MATCH ((match+sofar)&UNQUOTED)
  222. /*
  223. * EGGDROP: wild_match(char *ma, char *na)
  224. * IrcII: NOT USED
  225. *
  226. * Features: Backwards, case-insensitive, ?, *
  227. * Best use: Matching of hostmasks (since they are likely to begin
  228. * with a * rather than end with one).
  229. */
  230. int _wild_match(register unsigned char *m, register unsigned char *n)
  231. {
  232. unsigned char *ma = m, *na = n, *lsm = 0, *lsn = 0;
  233. int match = 1;
  234. register int sofar = 0;
  235. /* take care of null strings (should never match) */
  236. if ((ma == 0) || (na == 0) || (!*ma) || (!*na))
  237. return NOMATCH;
  238. /* find the end of each string */
  239. while (*(++m));
  240. m--;
  241. while (*(++n));
  242. n--;
  243. while (n >= na) {
  244. if ((m <= ma) || (m[-1] != QUOTE)) { /* Only look if no quote */
  245. switch (*m) {
  246. case WILDS: /* Matches anything */
  247. do
  248. m--; /* Zap redundant wilds */
  249. while ((m >= ma) && ((*m == WILDS) || (*m == WILDP)));
  250. if ((m >= ma) && (*m == '\\'))
  251. m++; /* Keep quoted wildcard! */
  252. lsm = m;
  253. lsn = n;
  254. match += sofar;
  255. sofar = 0; /* Update fallback pos */
  256. continue; /* Next char, please */
  257. case WILDQ:
  258. m--;
  259. n--;
  260. continue; /* '?' always matches */
  261. }
  262. sofar &= UNQUOTED; /* Remember not quoted */
  263. } else
  264. sofar |= QUOTED; /* Remember quoted */
  265. if (rfc_toupper(*m) == rfc_toupper(*n)) { /* If matching char */
  266. m--;
  267. n--;
  268. sofar++; /* Tally the match */
  269. if (sofar & QUOTED)
  270. m--; /* Skip the quote char */
  271. continue; /* Next char, please */
  272. }
  273. if (lsm) { /* To to fallback on '*' */
  274. n = --lsn;
  275. m = lsm;
  276. if (n < na)
  277. lsm = 0; /* Rewind to saved pos */
  278. sofar = 0;
  279. continue; /* Next char, please */
  280. }
  281. return NOMATCH; /* No fallback=No match */
  282. }
  283. while ((m >= ma) && ((*m == WILDS) || (*m == WILDP)))
  284. m--; /* Zap leftover %s & *s */
  285. return (m >= ma) ? NOMATCH : MATCH; /* Start of both = match */
  286. }
  287. /*
  288. * For this matcher, no "saved" is used to track "%" and no special quoting
  289. * ability is needed, so we just have (match+sofar) as the result.
  290. */
  291. #endif