gpattern.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. /*
  2. * Simple pattern matching
  3. *
  4. * Author:
  5. * Gonzalo Paniagua Javier ([email protected]
  6. *
  7. * (C) 2006 Novell, Inc.
  8. *
  9. * Permission is hereby granted, free of charge, to any person obtaining
  10. * a copy of this software and associated documentation files (the
  11. * "Software"), to deal in the Software without restriction, including
  12. * without limitation the rights to use, copy, modify, merge, publish,
  13. * distribute, sublicense, and/or sell copies of the Software, and to
  14. * permit persons to whom the Software is furnished to do so, subject to
  15. * the following conditions:
  16. *
  17. * The above copyright notice and this permission notice shall be
  18. * included in all copies or substantial portions of the Software.
  19. *
  20. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  21. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  22. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  23. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  24. * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  25. * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  26. * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  27. */
  28. #include <glib.h>
  29. #include <stdio.h>
  30. #include <stdlib.h>
  31. #include <errno.h>
  32. #ifndef _MSC_VER
  33. #include <unistd.h>
  34. #endif
  35. typedef enum {
  36. MATCH_LITERAL,
  37. MATCH_ANYCHAR,
  38. MATCH_ANYTHING,
  39. MATCH_ANYTHING_END,
  40. MATCH_INVALID = -1
  41. } MatchType;
  42. typedef struct {
  43. MatchType type;
  44. gchar *str;
  45. } PData;
  46. struct _GPatternSpec {
  47. GSList *pattern;
  48. };
  49. static GSList *
  50. compile_pattern (const gchar *pattern)
  51. {
  52. GSList *list;
  53. size_t i, len;
  54. PData *data;
  55. gchar c;
  56. MatchType last = MATCH_INVALID;
  57. GString *str;
  58. gboolean free_str;
  59. if (pattern == NULL)
  60. return NULL;
  61. data = NULL;
  62. list = NULL;
  63. free_str = TRUE;
  64. str = g_string_new ("");
  65. for (i = 0, len = strlen (pattern); i < len; i++) {
  66. c = pattern [i];
  67. if (c == '*' || c == '?') {
  68. if (str->len > 0) {
  69. data = g_new0 (PData, 1);
  70. data->type = MATCH_LITERAL;
  71. data->str = g_string_free (str, FALSE);
  72. list = g_slist_append (list, data);
  73. str = g_string_new ("");
  74. }
  75. if (last == MATCH_ANYTHING && c == '*')
  76. continue;
  77. data = g_new0 (PData, 1);
  78. data->type = (c == '*') ? MATCH_ANYTHING : MATCH_ANYCHAR;
  79. list = g_slist_append (list, data);
  80. last = data->type;
  81. } else {
  82. g_string_append_c (str, c);
  83. last = MATCH_LITERAL;
  84. }
  85. }
  86. if (last == MATCH_ANYTHING && str->len == 0) {
  87. data->type = MATCH_ANYTHING_END;
  88. free_str = TRUE;
  89. } else if (str->len > 0) {
  90. data = g_new0 (PData, 1);
  91. data->type = MATCH_LITERAL;
  92. data->str = str->str;
  93. free_str = FALSE;
  94. list = g_slist_append (list, data);
  95. }
  96. g_string_free (str, free_str);
  97. return list;
  98. }
  99. #ifdef DEBUG_PATTERN
  100. static void
  101. print_pattern (gpointer data, gpointer user_data)
  102. {
  103. PData *d = (PData *) data;
  104. printf ("Type: %s", d->type == MATCH_LITERAL ? "literal" : d->type == MATCH_ANYCHAR ? "any char" : "anything");
  105. if (d->type == MATCH_LITERAL)
  106. printf (" String: %s", d->str);
  107. printf ("\n");
  108. }
  109. #endif
  110. GPatternSpec *
  111. g_pattern_spec_new (const gchar *pattern)
  112. {
  113. GPatternSpec *spec;
  114. g_return_val_if_fail (pattern != NULL, NULL);
  115. spec = g_new0 (GPatternSpec, 1);
  116. if (pattern) {
  117. spec->pattern = compile_pattern (pattern);
  118. #ifdef DEBUG_PATTERN
  119. g_slist_foreach (spec->pattern, print_pattern, NULL);
  120. printf ("\n");
  121. #endif
  122. }
  123. return spec;
  124. }
  125. static void
  126. free_pdata (gpointer data, gpointer user_data)
  127. {
  128. PData *d = (PData *) data;
  129. if (d->str)
  130. g_free (d->str);
  131. g_free (d);
  132. }
  133. void
  134. g_pattern_spec_free (GPatternSpec *pspec)
  135. {
  136. if (pspec) {
  137. g_slist_foreach (pspec->pattern, free_pdata, NULL);
  138. g_slist_free (pspec->pattern);
  139. pspec->pattern = NULL;
  140. }
  141. g_free (pspec);
  142. }
  143. static gboolean
  144. match_string (GSList *list, const gchar *str, size_t idx, size_t max)
  145. {
  146. size_t len;
  147. while (list && idx < max) {
  148. PData *data = (PData *) list->data;
  149. if (data->type == MATCH_ANYTHING_END)
  150. return TRUE;
  151. if (data->type == MATCH_LITERAL) {
  152. len = strlen (data->str);
  153. if (strncmp (&str [idx], data->str, len) != 0)
  154. return FALSE;
  155. idx += len;
  156. list = list->next;
  157. if (list) {
  158. /*
  159. * When recursing, we need this to avoid returning FALSE
  160. * because 'list' will not be NULL
  161. */
  162. data = (PData *) list->data;
  163. if (data->type == MATCH_ANYTHING_END)
  164. return TRUE;
  165. }
  166. } else if (data->type == MATCH_ANYCHAR) {
  167. idx++;
  168. list = list->next;
  169. } else if (data->type == MATCH_ANYTHING) {
  170. while (idx < max) {
  171. if (match_string (list->next, str, idx++, max))
  172. return TRUE;
  173. }
  174. return FALSE;
  175. } else {
  176. g_assert_not_reached ();
  177. }
  178. }
  179. return (list == NULL && idx >= max);
  180. }
  181. gboolean
  182. g_pattern_match_string (GPatternSpec *pspec, const gchar *string)
  183. {
  184. g_return_val_if_fail (pspec != NULL, FALSE);
  185. g_return_val_if_fail (string != NULL, FALSE);
  186. if (pspec->pattern == NULL)
  187. return FALSE;
  188. return match_string (pspec->pattern, string, 0, strlen (string));
  189. }