PageRenderTime 48ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/xbmc/lib/libmms/glib-2.20.4/glib/gpattern.c

http://github.com/elan/plex
C | 343 lines | 281 code | 33 blank | 29 comment | 70 complexity | bbeaff4e3f9fcf2c7486d214fa7be018 MD5 | raw file
Possible License(s): LGPL-2.0, LGPL-3.0, CC-BY-SA-3.0, 0BSD, LGPL-2.1, GPL-3.0, BSD-3-Clause, CC0-1.0, Unlicense, GPL-2.0, AGPL-1.0
  1. /* GLIB - Library of useful routines for C programming
  2. * Copyright (C) 1995-1997, 1999 Peter Mattis, Red Hat, Inc.
  3. *
  4. * This library is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU Lesser General Public
  6. * License as published by the Free Software Foundation; either
  7. * version 2 of the License, or (at your option) any later version.
  8. *
  9. * This library is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * Lesser General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Lesser General Public
  15. * License along with this library; if not, write to the
  16. * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  17. * Boston, MA 02111-1307, USA.
  18. */
  19. #include "config.h"
  20. #include <string.h>
  21. #include "gpattern.h"
  22. #include "gmacros.h"
  23. #include "gmessages.h"
  24. #include "gmem.h"
  25. #include "gunicode.h"
  26. #include "gutils.h"
  27. #include "galias.h"
  28. /* keep enum and structure of gpattern.c and patterntest.c in sync */
  29. typedef enum
  30. {
  31. G_MATCH_ALL, /* "*A?A*" */
  32. G_MATCH_ALL_TAIL, /* "*A?AA" */
  33. G_MATCH_HEAD, /* "AAAA*" */
  34. G_MATCH_TAIL, /* "*AAAA" */
  35. G_MATCH_EXACT, /* "AAAAA" */
  36. G_MATCH_LAST
  37. } GMatchType;
  38. struct _GPatternSpec
  39. {
  40. GMatchType match_type;
  41. guint pattern_length;
  42. guint min_length;
  43. guint max_length;
  44. gchar *pattern;
  45. };
  46. /* --- functions --- */
  47. static inline gboolean
  48. g_pattern_ph_match (const gchar *match_pattern,
  49. const gchar *match_string,
  50. gboolean *wildcard_reached_p)
  51. {
  52. register const gchar *pattern, *string;
  53. register gchar ch;
  54. pattern = match_pattern;
  55. string = match_string;
  56. ch = *pattern;
  57. pattern++;
  58. while (ch)
  59. {
  60. switch (ch)
  61. {
  62. case '?':
  63. if (!*string)
  64. return FALSE;
  65. string = g_utf8_next_char (string);
  66. break;
  67. case '*':
  68. *wildcard_reached_p = TRUE;
  69. do
  70. {
  71. ch = *pattern;
  72. pattern++;
  73. if (ch == '?')
  74. {
  75. if (!*string)
  76. return FALSE;
  77. string = g_utf8_next_char (string);
  78. }
  79. }
  80. while (ch == '*' || ch == '?');
  81. if (!ch)
  82. return TRUE;
  83. do
  84. {
  85. gboolean next_wildcard_reached = FALSE;
  86. while (ch != *string)
  87. {
  88. if (!*string)
  89. return FALSE;
  90. string = g_utf8_next_char (string);
  91. }
  92. string++;
  93. if (g_pattern_ph_match (pattern, string, &next_wildcard_reached))
  94. return TRUE;
  95. if (next_wildcard_reached)
  96. /* the forthcoming pattern substring up to the next wildcard has
  97. * been matched, but a mismatch occoured for the rest of the
  98. * pattern, following the next wildcard.
  99. * there's no need to advance the current match position any
  100. * further if the rest pattern will not match.
  101. */
  102. return FALSE;
  103. }
  104. while (*string);
  105. break;
  106. default:
  107. if (ch == *string)
  108. string++;
  109. else
  110. return FALSE;
  111. break;
  112. }
  113. ch = *pattern;
  114. pattern++;
  115. }
  116. return *string == 0;
  117. }
  118. gboolean
  119. g_pattern_match (GPatternSpec *pspec,
  120. guint string_length,
  121. const gchar *string,
  122. const gchar *string_reversed)
  123. {
  124. g_return_val_if_fail (pspec != NULL, FALSE);
  125. g_return_val_if_fail (string != NULL, FALSE);
  126. if (string_length < pspec->min_length ||
  127. string_length > pspec->max_length)
  128. return FALSE;
  129. switch (pspec->match_type)
  130. {
  131. gboolean dummy;
  132. case G_MATCH_ALL:
  133. return g_pattern_ph_match (pspec->pattern, string, &dummy);
  134. case G_MATCH_ALL_TAIL:
  135. if (string_reversed)
  136. return g_pattern_ph_match (pspec->pattern, string_reversed, &dummy);
  137. else
  138. {
  139. gboolean result;
  140. gchar *tmp;
  141. tmp = g_utf8_strreverse (string, string_length);
  142. result = g_pattern_ph_match (pspec->pattern, tmp, &dummy);
  143. g_free (tmp);
  144. return result;
  145. }
  146. case G_MATCH_HEAD:
  147. if (pspec->pattern_length == string_length)
  148. return strcmp (pspec->pattern, string) == 0;
  149. else if (pspec->pattern_length)
  150. return strncmp (pspec->pattern, string, pspec->pattern_length) == 0;
  151. else
  152. return TRUE;
  153. case G_MATCH_TAIL:
  154. if (pspec->pattern_length)
  155. return strcmp (pspec->pattern, string + (string_length - pspec->pattern_length)) == 0;
  156. else
  157. return TRUE;
  158. case G_MATCH_EXACT:
  159. if (pspec->pattern_length != string_length)
  160. return FALSE;
  161. else
  162. return strcmp (pspec->pattern, string) == 0;
  163. default:
  164. g_return_val_if_fail (pspec->match_type < G_MATCH_LAST, FALSE);
  165. return FALSE;
  166. }
  167. }
  168. GPatternSpec*
  169. g_pattern_spec_new (const gchar *pattern)
  170. {
  171. GPatternSpec *pspec;
  172. gboolean seen_joker = FALSE, seen_wildcard = FALSE, more_wildcards = FALSE;
  173. gint hw_pos = -1, tw_pos = -1, hj_pos = -1, tj_pos = -1;
  174. gboolean follows_wildcard = FALSE;
  175. guint pending_jokers = 0;
  176. const gchar *s;
  177. gchar *d;
  178. guint i;
  179. g_return_val_if_fail (pattern != NULL, NULL);
  180. /* canonicalize pattern and collect necessary stats */
  181. pspec = g_new (GPatternSpec, 1);
  182. pspec->pattern_length = strlen (pattern);
  183. pspec->min_length = 0;
  184. pspec->max_length = 0;
  185. pspec->pattern = g_new (gchar, pspec->pattern_length + 1);
  186. d = pspec->pattern;
  187. for (i = 0, s = pattern; *s != 0; s++)
  188. {
  189. switch (*s)
  190. {
  191. case '*':
  192. if (follows_wildcard) /* compress multiple wildcards */
  193. {
  194. pspec->pattern_length--;
  195. continue;
  196. }
  197. follows_wildcard = TRUE;
  198. if (hw_pos < 0)
  199. hw_pos = i;
  200. tw_pos = i;
  201. break;
  202. case '?':
  203. pending_jokers++;
  204. pspec->min_length++;
  205. pspec->max_length += 4; /* maximum UTF-8 character length */
  206. continue;
  207. default:
  208. for (; pending_jokers; pending_jokers--, i++) {
  209. *d++ = '?';
  210. if (hj_pos < 0)
  211. hj_pos = i;
  212. tj_pos = i;
  213. }
  214. follows_wildcard = FALSE;
  215. pspec->min_length++;
  216. pspec->max_length++;
  217. break;
  218. }
  219. *d++ = *s;
  220. i++;
  221. }
  222. for (; pending_jokers; pending_jokers--) {
  223. *d++ = '?';
  224. if (hj_pos < 0)
  225. hj_pos = i;
  226. tj_pos = i;
  227. }
  228. *d++ = 0;
  229. seen_joker = hj_pos >= 0;
  230. seen_wildcard = hw_pos >= 0;
  231. more_wildcards = seen_wildcard && hw_pos != tw_pos;
  232. if (seen_wildcard)
  233. pspec->max_length = G_MAXUINT;
  234. /* special case sole head/tail wildcard or exact matches */
  235. if (!seen_joker && !more_wildcards)
  236. {
  237. if (pspec->pattern[0] == '*')
  238. {
  239. pspec->match_type = G_MATCH_TAIL;
  240. memmove (pspec->pattern, pspec->pattern + 1, --pspec->pattern_length);
  241. pspec->pattern[pspec->pattern_length] = 0;
  242. return pspec;
  243. }
  244. if (pspec->pattern_length > 0 &&
  245. pspec->pattern[pspec->pattern_length - 1] == '*')
  246. {
  247. pspec->match_type = G_MATCH_HEAD;
  248. pspec->pattern[--pspec->pattern_length] = 0;
  249. return pspec;
  250. }
  251. if (!seen_wildcard)
  252. {
  253. pspec->match_type = G_MATCH_EXACT;
  254. return pspec;
  255. }
  256. }
  257. /* now just need to distinguish between head or tail match start */
  258. tw_pos = pspec->pattern_length - 1 - tw_pos; /* last pos to tail distance */
  259. tj_pos = pspec->pattern_length - 1 - tj_pos; /* last pos to tail distance */
  260. if (seen_wildcard)
  261. pspec->match_type = tw_pos > hw_pos ? G_MATCH_ALL_TAIL : G_MATCH_ALL;
  262. else /* seen_joker */
  263. pspec->match_type = tj_pos > hj_pos ? G_MATCH_ALL_TAIL : G_MATCH_ALL;
  264. if (pspec->match_type == G_MATCH_ALL_TAIL) {
  265. gchar *tmp = pspec->pattern;
  266. pspec->pattern = g_utf8_strreverse (pspec->pattern, pspec->pattern_length);
  267. g_free (tmp);
  268. }
  269. return pspec;
  270. }
  271. void
  272. g_pattern_spec_free (GPatternSpec *pspec)
  273. {
  274. g_return_if_fail (pspec != NULL);
  275. g_free (pspec->pattern);
  276. g_free (pspec);
  277. }
  278. gboolean
  279. g_pattern_spec_equal (GPatternSpec *pspec1,
  280. GPatternSpec *pspec2)
  281. {
  282. g_return_val_if_fail (pspec1 != NULL, FALSE);
  283. g_return_val_if_fail (pspec2 != NULL, FALSE);
  284. return (pspec1->pattern_length == pspec2->pattern_length &&
  285. pspec1->match_type == pspec2->match_type &&
  286. strcmp (pspec1->pattern, pspec2->pattern) == 0);
  287. }
  288. gboolean
  289. g_pattern_match_string (GPatternSpec *pspec,
  290. const gchar *string)
  291. {
  292. g_return_val_if_fail (pspec != NULL, FALSE);
  293. g_return_val_if_fail (string != NULL, FALSE);
  294. return g_pattern_match (pspec, strlen (string), string, NULL);
  295. }
  296. gboolean
  297. g_pattern_match_simple (const gchar *pattern,
  298. const gchar *string)
  299. {
  300. GPatternSpec *pspec;
  301. gboolean ergo;
  302. g_return_val_if_fail (pattern != NULL, FALSE);
  303. g_return_val_if_fail (string != NULL, FALSE);
  304. pspec = g_pattern_spec_new (pattern);
  305. ergo = g_pattern_match (pspec, strlen (string), string, NULL);
  306. g_pattern_spec_free (pspec);
  307. return ergo;
  308. }
  309. #define __G_PATTERN_C__
  310. #include "galiasdef.c"