make struct _GPatternSpec and GMatchType private. (g_pattern_equal): new
authorTim Janik <timj@gtk.org>
Sat, 13 Oct 2001 05:54:10 +0000 (05:54 +0000)
committerTim Janik <timj@src.gnome.org>
Sat, 13 Oct 2001 05:54:10 +0000 (05:54 +0000)
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>.

Sat Oct 13 06:58:23 2001  Tim Janik  <timj@gtk.org>

        * glib/tmpl/patterns.sgml: amended documentation.

19 files changed:
ChangeLog
ChangeLog.pre-2-0
ChangeLog.pre-2-10
ChangeLog.pre-2-12
ChangeLog.pre-2-2
ChangeLog.pre-2-4
ChangeLog.pre-2-6
ChangeLog.pre-2-8
docs/reference/ChangeLog
docs/reference/glib/glib-sections.txt
docs/reference/glib/tmpl/glib-unused.sgml
docs/reference/glib/tmpl/patterns.sgml
docs/reference/glib/tmpl/string_utils.sgml
docs/reference/gobject/tmpl/types.sgml
glib/gpattern.c
glib/gpattern.h
tests/.cvsignore
tests/Makefile.am
tests/patterntest.c [new file with mode: 0644]

index 49e0eb9299391bcdc7a9d228d6e7a6a757186071..2a00a456d195163736b58a485bec809f82482999 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,22 @@
+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
index 49e0eb9299391bcdc7a9d228d6e7a6a757186071..2a00a456d195163736b58a485bec809f82482999 100644 (file)
@@ -1,3 +1,22 @@
+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
index 49e0eb9299391bcdc7a9d228d6e7a6a757186071..2a00a456d195163736b58a485bec809f82482999 100644 (file)
@@ -1,3 +1,22 @@
+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
index 49e0eb9299391bcdc7a9d228d6e7a6a757186071..2a00a456d195163736b58a485bec809f82482999 100644 (file)
@@ -1,3 +1,22 @@
+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
index 49e0eb9299391bcdc7a9d228d6e7a6a757186071..2a00a456d195163736b58a485bec809f82482999 100644 (file)
@@ -1,3 +1,22 @@
+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
index 49e0eb9299391bcdc7a9d228d6e7a6a757186071..2a00a456d195163736b58a485bec809f82482999 100644 (file)
@@ -1,3 +1,22 @@
+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
index 49e0eb9299391bcdc7a9d228d6e7a6a757186071..2a00a456d195163736b58a485bec809f82482999 100644 (file)
@@ -1,3 +1,22 @@
+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
index 49e0eb9299391bcdc7a9d228d6e7a6a757186071..2a00a456d195163736b58a485bec809f82482999 100644 (file)
@@ -1,3 +1,22 @@
+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
index 8cca5f537ae68a3b9586f0bdb6e775512efd684c..d87f7aa9c97d5e07131ab13ec1c4cae7be9d26ad 100644 (file)
@@ -1,3 +1,7 @@
+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.
index 5968f5a725b25d610e432fa1cb23998fd31a24e9..d42467a798b6a4fe6a27e29992224e10f89bb26d 100644 (file)
@@ -748,6 +748,7 @@ GMatchType
 GPatternSpec
 g_pattern_spec_new
 g_pattern_spec_free
+g_pattern_spec_equal
 g_pattern_match
 g_pattern_match_string
 g_pattern_match_simple
index 4ea5f2d8d0d709102365131ab17a7637aa41ed2c..321291813990ae4b3b83fd82579a1a022b2da060 100644 (file)
@@ -78,6 +78,20 @@ Specifies the type of function passed to g_set_error_handler().
 @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().
index 89344d87597e6ae35838f365e88dfa99161ca98d..0ded6e9604ec4ae9d526dc1dfc59680862d35f3e 100644 (file)
@@ -3,7 +3,7 @@ Glob-style pattern matching
 
 <!-- ##### SECTION Short_Description ##### -->
 
-Matches strings against patterns containing '*' and '?' wildcards.
+Matches strings against patterns containing '*' (wildcard) and '?' (joker).
 
 <!-- ##### SECTION Long_Description ##### -->
 <para>
@@ -15,7 +15,7 @@ arbitrary, possibly empty, string, '?' matches an arbitrary character.
 <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>
@@ -23,7 +23,7 @@ The pattern matcher is restricted to ASCII and will not work correctly with
 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 
@@ -32,41 +32,22 @@ pattern compilation.
 
 <!-- ##### 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.
 
 
@@ -78,21 +59,43 @@ Frees the memory allocated for the #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.
@@ -102,7 +105,10 @@ Matches a string against a compiled pattern.
 
 <!-- ##### 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.
index 11d3719ce13f470ea180fd278622a16fa8ecec3a..065b0b4ecafc12587ec53fdc770d3e278afadb61 100644 (file)
@@ -555,11 +555,11 @@ value if @s1 > @s2.
 <!-- ##### 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 ##### -->
index b70f16f4f45ac99597dd94141b0a4f3c81550ab6..f6fb3125e2089d542f3df21bd05095fd48f6e459 100644 (file)
@@ -213,6 +213,8 @@ The predefined identifiers of the reserved fundamental types.
 @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.
@@ -225,8 +227,6 @@ The predefined identifiers of the reserved fundamental types.
 @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>
index a149273a0d3be29b98d5c7dd2f758e4133b10931..c73bb53cded60392998aec945363b5280f06a249 100644 (file)
 #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)
@@ -101,16 +149,23 @@ g_pattern_match (GPatternSpec *pspec,
 {
   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;
@@ -120,25 +175,40 @@ g_pattern_match (GPatternSpec *pspec,
        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;
@@ -149,127 +219,116 @@ GPatternSpec*
 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
@@ -283,18 +342,8 @@ g_pattern_match_simple (const gchar *pattern,
   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);
-}
index bd29ad7ac7cfa36222c60c1ec25d7c652b1c7c01..5c89668777153e549bef79085c0772e8faf63f0e 100644 (file)
 
 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,
index e008092d80168484d9d363a7ae76a1c460022e62..37cd77cc8dd4902b2751cb748464de752a960bf8 100644 (file)
@@ -52,3 +52,4 @@ uri-test
 unicode-caseconv
 unicode-collate
 unicode-normalize
+patterntest
index 46765b3a14ffb1db32a9c938437b7e26df7e7cf7..5a2910d2c1128417b34b273cbaabb701ecc69bcd 100644 (file)
@@ -39,8 +39,9 @@ endif
 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)
diff --git a/tests/patterntest.c b/tests/patterntest.c
new file mode 100644 (file)
index 0000000..f881098
--- /dev/null
@@ -0,0 +1,225 @@
+/* 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;
+}