+Fri Oct 12 18:24:02 2001 Tim Janik <timj@gtk.org>
+
+ * glib/gpattern.[hc]: make struct _GPatternSpec and GMatchType
+ private.
+ (g_pattern_equal): new function to return equality of two patterns
+ (required because GPatternSpec is private now).
+ (g_pattern_spec_new): fix bug wrg wildcard counting which produced
+ incorrect pattern specs (discovered by Matthias Clasen).
+ optimized code so we just keep one compiled pattern string now.
+ correctly canonicalize patterns. reduce string walks, optimize
+ decision about MATCH_ALL vs. MATCH_ALL_TAIL.
+ (g_pattern_match_string): call just g_pattern_match() with NULL
+ reversed string.
+ (g_pattern_match): allow NULL reversed strings now, reverse_dup
+ strings on demand.
+
+ * tests/patterntest.c (test_compilation): added an extended testcase
+ for pattern matching from Matthias Clasen <matthiasc@poet.de>.
+
2001-10-11 Raja R Harinath <harinath@cs.umn.edu>
* configure.in (ac_cv_sizeof_long_long): Avoid '==' and '-a' in
+Fri Oct 12 18:24:02 2001 Tim Janik <timj@gtk.org>
+
+ * glib/gpattern.[hc]: make struct _GPatternSpec and GMatchType
+ private.
+ (g_pattern_equal): new function to return equality of two patterns
+ (required because GPatternSpec is private now).
+ (g_pattern_spec_new): fix bug wrg wildcard counting which produced
+ incorrect pattern specs (discovered by Matthias Clasen).
+ optimized code so we just keep one compiled pattern string now.
+ correctly canonicalize patterns. reduce string walks, optimize
+ decision about MATCH_ALL vs. MATCH_ALL_TAIL.
+ (g_pattern_match_string): call just g_pattern_match() with NULL
+ reversed string.
+ (g_pattern_match): allow NULL reversed strings now, reverse_dup
+ strings on demand.
+
+ * tests/patterntest.c (test_compilation): added an extended testcase
+ for pattern matching from Matthias Clasen <matthiasc@poet.de>.
+
2001-10-11 Raja R Harinath <harinath@cs.umn.edu>
* configure.in (ac_cv_sizeof_long_long): Avoid '==' and '-a' in
+Fri Oct 12 18:24:02 2001 Tim Janik <timj@gtk.org>
+
+ * glib/gpattern.[hc]: make struct _GPatternSpec and GMatchType
+ private.
+ (g_pattern_equal): new function to return equality of two patterns
+ (required because GPatternSpec is private now).
+ (g_pattern_spec_new): fix bug wrg wildcard counting which produced
+ incorrect pattern specs (discovered by Matthias Clasen).
+ optimized code so we just keep one compiled pattern string now.
+ correctly canonicalize patterns. reduce string walks, optimize
+ decision about MATCH_ALL vs. MATCH_ALL_TAIL.
+ (g_pattern_match_string): call just g_pattern_match() with NULL
+ reversed string.
+ (g_pattern_match): allow NULL reversed strings now, reverse_dup
+ strings on demand.
+
+ * tests/patterntest.c (test_compilation): added an extended testcase
+ for pattern matching from Matthias Clasen <matthiasc@poet.de>.
+
2001-10-11 Raja R Harinath <harinath@cs.umn.edu>
* configure.in (ac_cv_sizeof_long_long): Avoid '==' and '-a' in
+Fri Oct 12 18:24:02 2001 Tim Janik <timj@gtk.org>
+
+ * glib/gpattern.[hc]: make struct _GPatternSpec and GMatchType
+ private.
+ (g_pattern_equal): new function to return equality of two patterns
+ (required because GPatternSpec is private now).
+ (g_pattern_spec_new): fix bug wrg wildcard counting which produced
+ incorrect pattern specs (discovered by Matthias Clasen).
+ optimized code so we just keep one compiled pattern string now.
+ correctly canonicalize patterns. reduce string walks, optimize
+ decision about MATCH_ALL vs. MATCH_ALL_TAIL.
+ (g_pattern_match_string): call just g_pattern_match() with NULL
+ reversed string.
+ (g_pattern_match): allow NULL reversed strings now, reverse_dup
+ strings on demand.
+
+ * tests/patterntest.c (test_compilation): added an extended testcase
+ for pattern matching from Matthias Clasen <matthiasc@poet.de>.
+
2001-10-11 Raja R Harinath <harinath@cs.umn.edu>
* configure.in (ac_cv_sizeof_long_long): Avoid '==' and '-a' in
+Fri Oct 12 18:24:02 2001 Tim Janik <timj@gtk.org>
+
+ * glib/gpattern.[hc]: make struct _GPatternSpec and GMatchType
+ private.
+ (g_pattern_equal): new function to return equality of two patterns
+ (required because GPatternSpec is private now).
+ (g_pattern_spec_new): fix bug wrg wildcard counting which produced
+ incorrect pattern specs (discovered by Matthias Clasen).
+ optimized code so we just keep one compiled pattern string now.
+ correctly canonicalize patterns. reduce string walks, optimize
+ decision about MATCH_ALL vs. MATCH_ALL_TAIL.
+ (g_pattern_match_string): call just g_pattern_match() with NULL
+ reversed string.
+ (g_pattern_match): allow NULL reversed strings now, reverse_dup
+ strings on demand.
+
+ * tests/patterntest.c (test_compilation): added an extended testcase
+ for pattern matching from Matthias Clasen <matthiasc@poet.de>.
+
2001-10-11 Raja R Harinath <harinath@cs.umn.edu>
* configure.in (ac_cv_sizeof_long_long): Avoid '==' and '-a' in
+Fri Oct 12 18:24:02 2001 Tim Janik <timj@gtk.org>
+
+ * glib/gpattern.[hc]: make struct _GPatternSpec and GMatchType
+ private.
+ (g_pattern_equal): new function to return equality of two patterns
+ (required because GPatternSpec is private now).
+ (g_pattern_spec_new): fix bug wrg wildcard counting which produced
+ incorrect pattern specs (discovered by Matthias Clasen).
+ optimized code so we just keep one compiled pattern string now.
+ correctly canonicalize patterns. reduce string walks, optimize
+ decision about MATCH_ALL vs. MATCH_ALL_TAIL.
+ (g_pattern_match_string): call just g_pattern_match() with NULL
+ reversed string.
+ (g_pattern_match): allow NULL reversed strings now, reverse_dup
+ strings on demand.
+
+ * tests/patterntest.c (test_compilation): added an extended testcase
+ for pattern matching from Matthias Clasen <matthiasc@poet.de>.
+
2001-10-11 Raja R Harinath <harinath@cs.umn.edu>
* configure.in (ac_cv_sizeof_long_long): Avoid '==' and '-a' in
+Fri Oct 12 18:24:02 2001 Tim Janik <timj@gtk.org>
+
+ * glib/gpattern.[hc]: make struct _GPatternSpec and GMatchType
+ private.
+ (g_pattern_equal): new function to return equality of two patterns
+ (required because GPatternSpec is private now).
+ (g_pattern_spec_new): fix bug wrg wildcard counting which produced
+ incorrect pattern specs (discovered by Matthias Clasen).
+ optimized code so we just keep one compiled pattern string now.
+ correctly canonicalize patterns. reduce string walks, optimize
+ decision about MATCH_ALL vs. MATCH_ALL_TAIL.
+ (g_pattern_match_string): call just g_pattern_match() with NULL
+ reversed string.
+ (g_pattern_match): allow NULL reversed strings now, reverse_dup
+ strings on demand.
+
+ * tests/patterntest.c (test_compilation): added an extended testcase
+ for pattern matching from Matthias Clasen <matthiasc@poet.de>.
+
2001-10-11 Raja R Harinath <harinath@cs.umn.edu>
* configure.in (ac_cv_sizeof_long_long): Avoid '==' and '-a' in
+Fri Oct 12 18:24:02 2001 Tim Janik <timj@gtk.org>
+
+ * glib/gpattern.[hc]: make struct _GPatternSpec and GMatchType
+ private.
+ (g_pattern_equal): new function to return equality of two patterns
+ (required because GPatternSpec is private now).
+ (g_pattern_spec_new): fix bug wrg wildcard counting which produced
+ incorrect pattern specs (discovered by Matthias Clasen).
+ optimized code so we just keep one compiled pattern string now.
+ correctly canonicalize patterns. reduce string walks, optimize
+ decision about MATCH_ALL vs. MATCH_ALL_TAIL.
+ (g_pattern_match_string): call just g_pattern_match() with NULL
+ reversed string.
+ (g_pattern_match): allow NULL reversed strings now, reverse_dup
+ strings on demand.
+
+ * tests/patterntest.c (test_compilation): added an extended testcase
+ for pattern matching from Matthias Clasen <matthiasc@poet.de>.
+
2001-10-11 Raja R Harinath <harinath@cs.umn.edu>
* configure.in (ac_cv_sizeof_long_long): Avoid '==' and '-a' in
+Sat Oct 13 06:58:23 2001 Tim Janik <timj@gtk.org>
+
+ * glib/tmpl/patterns.sgml: amended documentation.
+
2001-10-11 Matthias Clasen <matthiasc@poet.de>
* glib/tmpl/patterns.sgml, glib/tmpl/shell.sgml: Updates.
GPatternSpec
g_pattern_spec_new
g_pattern_spec_free
+g_pattern_spec_equal
g_pattern_match
g_pattern_match_string
g_pattern_match_simple
@G_IO_FILE_MODE_READ_WRITE_TRUNCATE:
@G_IO_FILE_MODE_READ_WRITE_APPEND:
+<!-- ##### ENUM GMatchType ##### -->
+<para>
+Enumeration representing different kinds of patterns. This is only used
+internally for optimizing the match algorithm.
+</para>
+
+@G_MATCH_ALL: a general pattern.
+@G_MATCH_ALL_TAIL: a general pattern which contains a fixed part matching
+the end of the string.
+@G_MATCH_HEAD: a pattern matching every string with a certain prefix.
+@G_MATCH_TAIL: a pattern matching every string with a certain suffix.
+@G_MATCH_EXACT: a pattern matching exactly one string.
+@G_MATCH_LAST:
+
<!-- ##### USER_FUNCTION GWarningFunc ##### -->
<para>
Specifies the type of function passed to g_set_warning_handler().
<!-- ##### SECTION Short_Description ##### -->
-Matches strings against patterns containing '*' and '?' wildcards.
+Matches strings against patterns containing '*' (wildcard) and '?' (joker).
<!-- ##### SECTION Long_Description ##### -->
<para>
<para>
Note that in contrast to <function>glob()</function>, the '/' character
<emphasis>can</emphasis> be matched by the wildcards, there are no
-'[...]' character ranges and '*', '?' and '[' can <emphasis>not</emphasis>
+'[...]' character ranges and '*' and '?' can <emphasis>not</emphasis>
be escaped to include them literally in a pattern.
</para>
<para>
multibyte UTF-8 characters in the pattern or in the string to match.
</para>
<para>
-When multiple string must be matched against the same pattern, it
+When multiple strings must be matched against the same pattern, it
is better to compile the pattern to a #GPatternSpec using
g_pattern_spec_new() and use g_pattern_match_string() instead of
g_pattern_match_simple(). This avoids the overhead of repeated
<!-- ##### SECTION See_Also ##### -->
<para>
-
+g_strreverse()
</para>
-<!-- ##### ENUM GMatchType ##### -->
-<para>
-Enumeration representing different kinds of patterns. This is only used
-internally for optimizing the match algorithm.
-</para>
-
-@G_MATCH_ALL: a general pattern.
-@G_MATCH_ALL_TAIL: a general pattern which contains a fixed part matching
-the end of the string.
-@G_MATCH_HEAD: a pattern matching every string with a certain prefix.
-@G_MATCH_TAIL: a pattern matching every string with a certain suffix.
-@G_MATCH_EXACT: a pattern matching exactly one string.
-@G_MATCH_LAST:
-
<!-- ##### STRUCT GPatternSpec ##### -->
<para>
A <structname>GPatternSpec</structname> is the 'compiled' form of a pattern.
-There should be no need to access its fields.
+This structure is opaque and its fields and cannot be accessed.
</para>
-@match_type: the #GMatchType of the pattern.
-@pattern_length: the length of the pattern.
-@pattern: the pattern. Note that this may be different from the @pattern
-used to construct this <structname>GPatternSpec</structname>.
-@pattern_reversed: the reverse of @pattern.
<!-- ##### FUNCTION g_pattern_spec_new ##### -->
<para>
Compiles a pattern to a #GPatternSpec.
</para>
-@pattern: a string.
+@pattern: a zero terminated string.
@Returns: a newly-allocated #GPatternSpec.
@pspec: a #GPatternSpec.
+<!-- ##### FUNCTION g_pattern_spec_equal ##### -->
+<para>
+Compares two compiled pattern specs and returns whether they
+will match the same set of strings.
+</para>
+
+@pspec1: a #GPatternSpec.
+@pspec2: another #GPatternSpec.
+@Returns: Whether the compiled patterns are equal.
+
+
<!-- ##### FUNCTION g_pattern_match ##### -->
<para>
-Matches a string against a compiled pattern.
+Matches a string against a compiled pattern. Passing the correct length of the
+string given is mandatory. The reversed string can be omitted by passing %NULL,
+this is more efficient if the reversed version of the string to be matched is
+not at hand, as g_pattern_match() will only construct it if the compiled pattern
+requires reverse matches.
+Note that, if the user code will (possibly) match a string against a multitude of
+patterns containing wildcards, chances are high that some patterns will require
+a reversed string. In this case, it's more efficient to provide the reversed
+string to avoid multiple constructions thereof in the various calls to
+g_pattern_match().
</para>
@pspec: a #GPatternSpec.
@string_length: the length of @string.
@string: the string to match.
-@string_reversed: the reverse of @string.
+@string_reversed: the reverse of @string or %NULL.
@Returns: %TRUE if @string matches @pspec.
<!-- ##### FUNCTION g_pattern_match_string ##### -->
<para>
-Matches a string against a compiled pattern.
+Matches a string against a compiled pattern. If the string is to
+be matched against more than one pattern, consider using
+g_pattern_match() instead while supplying the reversed string.
</para>
@pspec: a #GPatternSpec.
<!-- ##### FUNCTION g_pattern_match_simple ##### -->
<para>
-Matches a string against a pattern.
+Matches a string against a pattern given as a string.
+If this function is to be called in a loop, it's more efficient to compile
+the pattern once with g_pattern_spec_new() and call g_pattern_match_string()
+repetively.
</para>
@pattern: the pattern.
<!-- ##### FUNCTION g_strreverse ##### -->
<para>
Reverses all of the characters in a string.
-For example, g_strreverse ("abcdef") would be "fedcba".
+For example, g_strreverse ("abcdef") will result in "fedcba".
</para>
@string: the string to reverse.
-@Returns:
+@Returns: the same pointer passed in as @string.
<!-- ##### FUNCTION g_strtod ##### -->
@G_TYPE_PARAM_UINT: Identifier for the "#GParamSpecUInt" type.
@G_TYPE_PARAM_LONG: Identifier for the "#GParamSpecLong" type.
@G_TYPE_PARAM_ULONG: Identifier for the "#GParamSpecULong" type.
+@G_TYPE_PARAM_INT64:
+@G_TYPE_PARAM_UINT64:
@G_TYPE_PARAM_UNICHAR:
@G_TYPE_PARAM_ENUM: Identifier for the "#GParamSpecEnum" type.
@G_TYPE_PARAM_FLAGS: Identifier for the "#GParamSpecFlags" type.
@G_TYPE_PARAM_VALUE_ARRAY: Identifier for the "#GParamSpecValueArray" type.
@G_TYPE_PARAM_CLOSURE: Identifier for the "#GParamClosure" type.
@G_TYPE_PARAM_OBJECT: Identifier for the "#GParamSpecObject" type.
-@G_TYPE_PARAM_INT64:
-@G_TYPE_PARAM_UINT64:
<!-- ##### STRUCT GTypeInterface ##### -->
<para>
#include "gutils.h" /* inline hassle */
#include <string.h>
+/* keep enum and structure of gpattern.c and patterntest.c in sync */
+typedef enum
+{
+ G_MATCH_ALL, /* "*A?A*" */
+ G_MATCH_ALL_TAIL, /* "*A?AA" */
+ G_MATCH_HEAD, /* "AAAA*" */
+ G_MATCH_TAIL, /* "*AAAA" */
+ G_MATCH_EXACT, /* "AAAAA" */
+ G_MATCH_LAST
+} GMatchType;
+struct _GPatternSpec
+{
+ GMatchType match_type;
+ guint pattern_length;
+ gchar *pattern;
+};
+
/* --- functions --- */
+static inline void
+instring_reverse (guint length,
+ gchar *str)
+{
+ gchar *f, *l, *b;
+
+ f = str;
+ l = str + length - 1;
+ b = str + length / 2;
+ while (f < b)
+ {
+ gchar tmp = *l;
+
+ *l-- = *f;
+ *f++ = tmp;
+ }
+}
+
+static inline gchar*
+strdup_reverse (guint length,
+ const gchar *str)
+{
+ gchar *t, *dest = g_new (gchar, length + 1);
+
+ t = dest + length;
+ *t-- = 0;
+ while (t >= dest)
+ *t-- = *str++;
+ return dest;
+}
+
static inline gboolean
g_pattern_ph_match (const gchar *match_pattern,
const gchar *match_string)
{
g_return_val_if_fail (pspec != NULL, FALSE);
g_return_val_if_fail (string != NULL, FALSE);
- g_return_val_if_fail (string_reversed != NULL, FALSE);
switch (pspec->match_type)
{
+ gboolean result;
+ gchar *tmp;
case G_MATCH_ALL:
return g_pattern_ph_match (pspec->pattern, string);
-
case G_MATCH_ALL_TAIL:
- return g_pattern_ph_match (pspec->pattern_reversed, string_reversed);
-
+ if (string_reversed)
+ return g_pattern_ph_match (pspec->pattern, string_reversed);
+ else
+ {
+ tmp = strdup_reverse (string_length, string);
+ result = g_pattern_ph_match (pspec->pattern, tmp);
+ g_free (tmp);
+ return result;
+ }
case G_MATCH_HEAD:
if (pspec->pattern_length > string_length)
return FALSE;
return strncmp (pspec->pattern, string, pspec->pattern_length) == 0;
else
return TRUE;
-
case G_MATCH_TAIL:
if (pspec->pattern_length > string_length)
return FALSE;
else if (pspec->pattern_length == string_length)
- return strcmp (pspec->pattern_reversed, string_reversed) == 0;
+ {
+ if (string_reversed)
+ return strcmp (pspec->pattern, string_reversed) == 0;
+ else
+ {
+ tmp = strdup_reverse (string_length, string);
+ result = strcmp (pspec->pattern, tmp) == 0;
+ g_free (tmp);
+ return result;
+ }
+ }
else if (pspec->pattern_length)
- return strncmp (pspec->pattern_reversed,
- string_reversed,
- pspec->pattern_length) == 0;
+ {
+ if (string_reversed)
+ return strncmp (pspec->pattern, string_reversed, pspec->pattern_length) == 0;
+ else
+ {
+ tmp = strdup_reverse (string_length, string);
+ result = strncmp (pspec->pattern, tmp, pspec->pattern_length) == 0;
+ g_free (tmp);
+ return result;
+ }
+ }
else
return TRUE;
-
case G_MATCH_EXACT:
if (pspec->pattern_length != string_length)
return FALSE;
else
- return strcmp (pspec->pattern_reversed, string_reversed) == 0;
-
+ return strcmp (pspec->pattern, string) == 0;
default:
g_return_val_if_fail (pspec->match_type < G_MATCH_LAST, FALSE);
return FALSE;
g_pattern_spec_new (const gchar *pattern)
{
GPatternSpec *pspec;
- gchar *p, *t;
- const gchar *h;
- guint hw = 0, tw = 0, hj = 0, tj = 0;
-
+ gboolean seen_joker = FALSE, seen_wildcard = FALSE, more_wildcards = FALSE;
+ gint hw_pos = -1, tw_pos = -1, hj_pos = -1, tj_pos = -1;
+ gboolean follows_wildcard = FALSE;
+ const gchar *s;
+ gchar *d;
+ guint i;
+
g_return_val_if_fail (pattern != NULL, NULL);
+ /* canonicalize pattern and collect necessary stats */
pspec = g_new (GPatternSpec, 1);
pspec->pattern_length = strlen (pattern);
- pspec->pattern = strcpy (g_new (gchar, pspec->pattern_length + 1), pattern);
- pspec->pattern_reversed = g_new (gchar, pspec->pattern_length + 1);
- t = pspec->pattern_reversed + pspec->pattern_length;
- *(t--) = 0;
- h = pattern;
- while (t >= pspec->pattern_reversed)
+ pspec->pattern = g_new (gchar, pspec->pattern_length + 1);
+ d = pspec->pattern;
+ for (i = 0, s = pattern; *s != 0; s++)
{
- register gchar c = *(h++);
-
- if (c == '*')
- {
- if (t < h)
- hw++;
- else
- tw++;
- }
- else if (c == '?')
+ switch (*s)
{
- if (t < h)
- hj++;
- else
- tj++;
+ case '*':
+ if (follows_wildcard) /* compress multiple wildcards */
+ {
+ pspec->pattern_length--;
+ continue;
+ }
+ follows_wildcard = TRUE;
+ if (hw_pos < 0)
+ hw_pos = i;
+ tw_pos = i;
+ break;
+ case '?':
+ if (hj_pos < 0)
+ hj_pos = i;
+ tj_pos = i;
+ /* fall through */
+ default:
+ follows_wildcard = FALSE;
+ break;
}
-
- *(t--) = c;
- }
- pspec->match_type = hw > tw || (hw == tw && hj > tj) ? G_MATCH_ALL_TAIL : G_MATCH_ALL;
-
- if (hj || tj)
- return pspec;
-
- if (hw == 0 && tw == 0)
- {
- pspec->match_type = G_MATCH_EXACT;
- return pspec;
+ *d++ = *s;
+ i++;
}
+ *d++ = 0;
+ seen_joker = hj_pos >= 0;
+ seen_wildcard = hw_pos >= 0;
+ more_wildcards = seen_wildcard && hw_pos != tw_pos;
- if (hw)
+ /* special case sole head/tail wildcard or exact matches */
+ if (!seen_joker && !more_wildcards)
{
- p = pspec->pattern;
- while (*p == '*')
- p++;
- if (p > pspec->pattern && !strchr (p, '*'))
+ if (pspec->pattern[0] == '*')
{
- gchar *tmp;
-
pspec->match_type = G_MATCH_TAIL;
- pspec->pattern_length = strlen (p);
- tmp = pspec->pattern;
- pspec->pattern = strcpy (g_new (gchar, pspec->pattern_length + 1), p);
- g_free (tmp);
- g_free (pspec->pattern_reversed);
- pspec->pattern_reversed = g_new (gchar, pspec->pattern_length + 1);
- t = pspec->pattern_reversed + pspec->pattern_length;
- *(t--) = 0;
- h = pspec->pattern;
- while (t >= pspec->pattern_reversed)
- *(t--) = *(h++);
+ instring_reverse (pspec->pattern_length, pspec->pattern);
+ pspec->pattern[--pspec->pattern_length] = 0;
return pspec;
}
- }
-
- if (tw)
- {
- p = pspec->pattern_reversed;
- while (*p == '*')
- p++;
- if (p > pspec->pattern_reversed && !strchr (p, '*'))
+ if (pspec->pattern[pspec->pattern_length - 1] == '*')
{
- gchar *tmp;
-
pspec->match_type = G_MATCH_HEAD;
- pspec->pattern_length = strlen (p);
- tmp = pspec->pattern_reversed;
- pspec->pattern_reversed = strcpy (g_new (gchar, pspec->pattern_length + 1), p);
- g_free (tmp);
- g_free (pspec->pattern);
- pspec->pattern = g_new (gchar, pspec->pattern_length + 1);
- t = pspec->pattern + pspec->pattern_length;
- *(t--) = 0;
- h = pspec->pattern_reversed;
- while (t >= pspec->pattern)
- *(t--) = *(h++);
+ pspec->pattern[--pspec->pattern_length] = 0;
+ return pspec;
+ }
+ if (!seen_wildcard)
+ {
+ pspec->match_type = G_MATCH_EXACT;
+ return pspec;
}
}
+ /* now just need to distinguish between head or tail match start */
+ tw_pos = pspec->pattern_length - 1 - tw_pos; /* last pos to tail distance */
+ tj_pos = pspec->pattern_length - 1 - tj_pos; /* last pos to tail distance */
+ if (seen_wildcard)
+ pspec->match_type = tw_pos > hw_pos ? G_MATCH_ALL_TAIL : G_MATCH_ALL;
+ else /* seen_joker */
+ pspec->match_type = tj_pos > hj_pos ? G_MATCH_ALL_TAIL : G_MATCH_ALL;
+ if (pspec->match_type == G_MATCH_ALL_TAIL)
+ instring_reverse (pspec->pattern_length, pspec->pattern);
return pspec;
}
+void
+g_pattern_spec_free (GPatternSpec *pspec)
+{
+ g_return_if_fail (pspec != NULL);
+
+ g_free (pspec->pattern);
+ g_free (pspec);
+}
+
+gboolean
+g_pattern_spec_equal (GPatternSpec *pspec1,
+ GPatternSpec *pspec2)
+{
+ g_return_val_if_fail (pspec1 != NULL, FALSE);
+ g_return_val_if_fail (pspec2 != NULL, FALSE);
+
+ return (pspec1->pattern_length == pspec2->pattern_length &&
+ pspec1->match_type == pspec2->match_type &&
+ strcmp (pspec1->pattern, pspec2->pattern) == 0);
+}
+
gboolean
g_pattern_match_string (GPatternSpec *pspec,
const gchar *string)
{
- gchar *string_reversed, *t;
- const gchar *h;
- guint length;
- gboolean ergo;
-
g_return_val_if_fail (pspec != NULL, FALSE);
g_return_val_if_fail (string != NULL, FALSE);
- length = strlen (string);
- string_reversed = g_new (gchar, length + 1);
- t = string_reversed + length;
- *(t--) = 0;
- h = string;
- while (t >= string_reversed)
- *(t--) = *(h++);
-
- ergo = g_pattern_match (pspec, length, string, string_reversed);
- g_free (string_reversed);
-
- return ergo;
+ return g_pattern_match (pspec, strlen (string), string, NULL);
}
gboolean
g_return_val_if_fail (string != NULL, FALSE);
pspec = g_pattern_spec_new (pattern);
- ergo = g_pattern_match_string (pspec, string);
+ ergo = g_pattern_match (pspec, strlen (string), string, NULL);
g_pattern_spec_free (pspec);
return ergo;
}
-
-void
-g_pattern_spec_free (GPatternSpec *pspec)
-{
- g_return_if_fail (pspec != NULL);
-
- g_free (pspec->pattern);
- g_free (pspec->pattern_reversed);
- g_free (pspec);
-}
G_BEGIN_DECLS
-typedef enum
-{
- G_MATCH_ALL, /* "*A?A*" */
- G_MATCH_ALL_TAIL, /* "*A?AA" */
- G_MATCH_HEAD, /* "AAAA*" */
- G_MATCH_TAIL, /* "*AAAA" */
- G_MATCH_EXACT, /* "AAAAA" */
- G_MATCH_LAST
-} GMatchType;
typedef struct _GPatternSpec GPatternSpec;
-struct _GPatternSpec
-{
- GMatchType match_type;
- guint pattern_length;
- gchar *pattern;
- gchar *pattern_reversed;
-};
GPatternSpec* g_pattern_spec_new (const gchar *pattern);
void g_pattern_spec_free (GPatternSpec *pspec);
+gboolean g_pattern_spec_equal (GPatternSpec *pspec1,
+ GPatternSpec *pspec2);
gboolean g_pattern_match (GPatternSpec *pspec,
guint string_length,
const gchar *string,
unicode-caseconv
unicode-collate
unicode-normalize
+patterntest
if ENABLE_TIMELOOP
timeloop = timeloop timeloop-closure
endif
-noinst_PROGRAMS = testglib testgdate testgdateparser unicode-normalize unicode-collate $(timeloop)
+noinst_PROGRAMS = testglib patterntest testgdate testgdateparser unicode-normalize unicode-collate $(timeloop)
testglib_LDADD = $(libglib)
+patterntest_LDADD = $(libglib)
testgdate_LDADD = $(libglib)
testgdateparser_LDADD = $(libglib)
unicode_normalize_LDADD = $(libglib)
--- /dev/null
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 2001 Matthias Clasen <matthiasc@poet.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#include "glib.h"
+
+
+/* keep enum and structure of gpattern.c and patterntest.c in sync */
+typedef enum
+{
+ G_MATCH_ALL, /* "*A?A*" */
+ G_MATCH_ALL_TAIL, /* "*A?AA" */
+ G_MATCH_HEAD, /* "AAAA*" */
+ G_MATCH_TAIL, /* "*AAAA" */
+ G_MATCH_EXACT, /* "AAAAA" */
+ G_MATCH_LAST
+} GMatchType;
+struct _GPatternSpec
+{
+ GMatchType match_type;
+ guint pattern_length;
+ gchar *pattern;
+};
+
+
+static gchar *
+match_type_name (GMatchType match_type)
+{
+ switch (match_type)
+ {
+ case G_MATCH_ALL:
+ return "G_MATCH_ALL";
+ break;
+ case G_MATCH_ALL_TAIL:
+ return "G_MATCH_ALL_TAIL";
+ break;
+ case G_MATCH_HEAD:
+ return "G_MATCH_HEAD";
+ break;
+ case G_MATCH_TAIL:
+ return "G_MATCH_TAIL";
+ break;
+ case G_MATCH_EXACT:
+ return "G_MATCH_EXACT";
+ break;
+ default:
+ return "unknown GMatchType";
+ break;
+ }
+}
+
+static gboolean
+test_compilation (gchar *src,
+ GMatchType match_type,
+ gchar *pattern)
+{
+ GPatternSpec *spec;
+
+ g_print ("compiling \"%s\" \t", src);
+ spec = g_pattern_spec_new (src);
+
+ if (spec->match_type != match_type)
+ {
+ g_print ("failed \t(match_type: %s, expected %s)\n",
+ match_type_name (spec->match_type),
+ match_type_name (match_type));
+ return FALSE;
+ }
+
+ if (strcmp (spec->pattern, pattern) != 0)
+ {
+ g_print ("failed \t(pattern: \"%s\", expected \"%s\")\n",
+ spec->pattern,
+ pattern);
+ return FALSE;
+ }
+
+ if (spec->pattern_length != strlen (spec->pattern))
+ {
+ g_print ("failed \t(pattern_length: %d, expected %d)\n",
+ spec->pattern_length,
+ strlen (spec->pattern));
+ return FALSE;
+ }
+
+ g_print ("passed (%s: \"%s\")\n",
+ match_type_name (spec->match_type),
+ spec->pattern);
+
+ return TRUE;
+}
+
+static gboolean
+test_match (gchar *pattern,
+ gchar *string,
+ gboolean match)
+{
+ g_print ("matching \"%s\" against \"%s\" \t", string, pattern);
+
+ if (g_pattern_match_simple (pattern, string) != match)
+ {
+ g_print ("failed \t(unexpected %s)\n", (match ? "mismatch" : "match"));
+ return FALSE;
+ }
+
+ g_print ("passed (%s)\n", match ? "match" : "nomatch");
+ return TRUE;
+}
+
+static gboolean
+test_equal (gchar *pattern1,
+ gchar *pattern2,
+ gboolean expected)
+{
+ GPatternSpec *p1 = g_pattern_spec_new (pattern1);
+ GPatternSpec *p2 = g_pattern_spec_new (pattern2);
+ gboolean equal = g_pattern_spec_equal (p1, p2);
+
+ g_print ("comparing \"%s\" with \"%s\" \t", pattern1, pattern2);
+
+ if (expected != equal)
+ {
+ g_print ("failed \t{%s, %u, \"%s\"} %s {%s, %u, \"%s\"}\n",
+ match_type_name (p1->match_type), p1->pattern_length, p1->pattern,
+ expected ? "!=" : "==",
+ match_type_name (p2->match_type), p2->pattern_length, p2->pattern);
+ }
+ else
+ g_print ("passed (%s)\n", equal ? "equal" : "unequal");
+
+ g_pattern_spec_free (p1);
+ g_pattern_spec_free (p2);
+
+ return expected == equal;
+}
+
+#define TEST_COMPILATION(src, type, pattern) { \
+ total++; \
+ if (test_compilation (src, type, pattern)) \
+ passed++; \
+ else \
+ failed++; \
+}
+
+#define TEST_MATCH(pattern, string, match) { \
+ total++; \
+ if (test_match (pattern, string, match)) \
+ passed++; \
+ else \
+ failed++; \
+}
+
+#define TEST_EQUAL(pattern1, pattern2, match) { \
+ total++; \
+ if (test_equal (pattern1, pattern2, match)) \
+ passed++; \
+ else \
+ failed++; \
+}
+
+int
+main (int argc, char** argv)
+{
+ gint total = 0;
+ gint passed = 0;
+ gint failed = 0;
+ gchar *string;
+
+ TEST_COMPILATION("*A?B*", G_MATCH_ALL, "*A?B*");
+ TEST_COMPILATION("ABC*DEFGH", G_MATCH_ALL_TAIL, "HGFED*CBA");
+ TEST_COMPILATION("ABCDEF*GH", G_MATCH_ALL, "ABCDEF*GH");
+ TEST_COMPILATION("ABC**?***??**DEF*GH", G_MATCH_ALL, "ABC*?*??*DEF*GH");
+ TEST_COMPILATION("*A?AA", G_MATCH_ALL_TAIL, "AA?A*");
+ TEST_COMPILATION("ABCD*", G_MATCH_HEAD, "ABCD");
+ TEST_COMPILATION("*ABCD", G_MATCH_TAIL, "DCBA");
+ TEST_COMPILATION("ABCDE", G_MATCH_EXACT, "ABCDE");
+
+ TEST_EQUAL ("*A?B*", "*A?B*", TRUE);
+ TEST_EQUAL ("A*BCD", "A*BCD", TRUE);
+ TEST_EQUAL ("ABCD*", "ABCD****", TRUE);
+ TEST_EQUAL ("A1*", "A1*", TRUE);
+ TEST_EQUAL ("*YZ", "*YZ", TRUE);
+ TEST_EQUAL ("A1x", "A1x", TRUE);
+ TEST_EQUAL ("AB*CD", "AB**CD", TRUE);
+ TEST_EQUAL ("AB*CD", "AB*?*CD", FALSE);
+ TEST_EQUAL ("ABC*", "ABC?", FALSE);
+
+ TEST_MATCH("*x", "x", TRUE);
+ TEST_MATCH("*x", "xx", TRUE);
+ TEST_MATCH("*x", "yyyx", TRUE);
+ TEST_MATCH("*x", "yyxy", FALSE);
+ TEST_MATCH("?x", "x", FALSE);
+ TEST_MATCH("?x", "xx", TRUE);
+ TEST_MATCH("?x", "yyyx", FALSE);
+ TEST_MATCH("?x", "yyxy", FALSE);
+ TEST_MATCH("*?x", "xx", TRUE);
+ TEST_MATCH("?*x", "xx", TRUE);
+ TEST_MATCH("*?x", "x", FALSE);
+ TEST_MATCH("?*x", "x", FALSE);
+ TEST_MATCH("*?*x", "yx", TRUE);
+ TEST_MATCH("*?*x", "xxxx", TRUE);
+
+ string = g_convert ("Äx", -1, "UTF-8", "Latin1", NULL, NULL, NULL);
+ TEST_MATCH("*x", string, TRUE);
+ TEST_MATCH("?x", string, TRUE);
+ TEST_MATCH("??x", string, FALSE);
+
+ g_print ("\n%u tests passed, %u failed\n", passed, failed);
+
+ return 0;
+}