make struct _GPatternSpec and GMatchType private. (g_pattern_equal): new

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.
This commit is contained in:
Tim Janik 2001-10-13 05:54:10 +00:00 committed by Tim Janik
parent 96bc4f0c3b
commit 92dfa96114
19 changed files with 607 additions and 168 deletions

View 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> 2001-10-11 Raja R Harinath <harinath@cs.umn.edu>
* configure.in (ac_cv_sizeof_long_long): Avoid '==' and '-a' in * configure.in (ac_cv_sizeof_long_long): Avoid '==' and '-a' in

View 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> 2001-10-11 Raja R Harinath <harinath@cs.umn.edu>
* configure.in (ac_cv_sizeof_long_long): Avoid '==' and '-a' in * configure.in (ac_cv_sizeof_long_long): Avoid '==' and '-a' in

View 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> 2001-10-11 Raja R Harinath <harinath@cs.umn.edu>
* configure.in (ac_cv_sizeof_long_long): Avoid '==' and '-a' in * configure.in (ac_cv_sizeof_long_long): Avoid '==' and '-a' in

View 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> 2001-10-11 Raja R Harinath <harinath@cs.umn.edu>
* configure.in (ac_cv_sizeof_long_long): Avoid '==' and '-a' in * configure.in (ac_cv_sizeof_long_long): Avoid '==' and '-a' in

View 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> 2001-10-11 Raja R Harinath <harinath@cs.umn.edu>
* configure.in (ac_cv_sizeof_long_long): Avoid '==' and '-a' in * configure.in (ac_cv_sizeof_long_long): Avoid '==' and '-a' in

View 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> 2001-10-11 Raja R Harinath <harinath@cs.umn.edu>
* configure.in (ac_cv_sizeof_long_long): Avoid '==' and '-a' in * configure.in (ac_cv_sizeof_long_long): Avoid '==' and '-a' in

View 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> 2001-10-11 Raja R Harinath <harinath@cs.umn.edu>
* configure.in (ac_cv_sizeof_long_long): Avoid '==' and '-a' in * configure.in (ac_cv_sizeof_long_long): Avoid '==' and '-a' in

View 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> 2001-10-11 Raja R Harinath <harinath@cs.umn.edu>
* configure.in (ac_cv_sizeof_long_long): Avoid '==' and '-a' in * configure.in (ac_cv_sizeof_long_long): Avoid '==' and '-a' in

View 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> 2001-10-11 Matthias Clasen <matthiasc@poet.de>
* glib/tmpl/patterns.sgml, glib/tmpl/shell.sgml: Updates. * glib/tmpl/patterns.sgml, glib/tmpl/shell.sgml: Updates.

View File

@ -748,6 +748,7 @@ GMatchType
GPatternSpec GPatternSpec
g_pattern_spec_new g_pattern_spec_new
g_pattern_spec_free g_pattern_spec_free
g_pattern_spec_equal
g_pattern_match g_pattern_match
g_pattern_match_string g_pattern_match_string
g_pattern_match_simple g_pattern_match_simple

View 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_TRUNCATE:
@G_IO_FILE_MODE_READ_WRITE_APPEND: @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 ##### --> <!-- ##### USER_FUNCTION GWarningFunc ##### -->
<para> <para>
Specifies the type of function passed to g_set_warning_handler(). Specifies the type of function passed to g_set_warning_handler().

View File

@ -3,7 +3,7 @@ Glob-style pattern matching
<!-- ##### SECTION Short_Description ##### --> <!-- ##### SECTION Short_Description ##### -->
Matches strings against patterns containing '*' and '?' wildcards. Matches strings against patterns containing '*' (wildcard) and '?' (joker).
<!-- ##### SECTION Long_Description ##### --> <!-- ##### SECTION Long_Description ##### -->
<para> <para>
@ -15,7 +15,7 @@ arbitrary, possibly empty, string, '?' matches an arbitrary character.
<para> <para>
Note that in contrast to <function>glob()</function>, the '/' character Note that in contrast to <function>glob()</function>, the '/' character
<emphasis>can</emphasis> be matched by the wildcards, there are no <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. be escaped to include them literally in a pattern.
</para> </para>
<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. multibyte UTF-8 characters in the pattern or in the string to match.
</para> </para>
<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 is better to compile the pattern to a #GPatternSpec using
g_pattern_spec_new() and use g_pattern_match_string() instead of g_pattern_spec_new() and use g_pattern_match_string() instead of
g_pattern_match_simple(). This avoids the overhead of repeated g_pattern_match_simple(). This avoids the overhead of repeated
@ -32,41 +32,22 @@ pattern compilation.
<!-- ##### SECTION See_Also ##### --> <!-- ##### SECTION See_Also ##### -->
<para> <para>
g_strreverse()
</para> </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 ##### --> <!-- ##### STRUCT GPatternSpec ##### -->
<para> <para>
A <structname>GPatternSpec</structname> is the 'compiled' form of a pattern. 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> </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 ##### --> <!-- ##### FUNCTION g_pattern_spec_new ##### -->
<para> <para>
Compiles a pattern to a #GPatternSpec. Compiles a pattern to a #GPatternSpec.
</para> </para>
@pattern: a string. @pattern: a zero terminated string.
@Returns: a newly-allocated #GPatternSpec. @Returns: a newly-allocated #GPatternSpec.
@ -78,21 +59,43 @@ Frees the memory allocated for the #GPatternSpec.
@pspec: a #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 ##### --> <!-- ##### FUNCTION g_pattern_match ##### -->
<para> <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> </para>
@pspec: a #GPatternSpec. @pspec: a #GPatternSpec.
@string_length: the length of @string. @string_length: the length of @string.
@string: the string to match. @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. @Returns: %TRUE if @string matches @pspec.
<!-- ##### FUNCTION g_pattern_match_string ##### --> <!-- ##### FUNCTION g_pattern_match_string ##### -->
<para> <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> </para>
@pspec: a #GPatternSpec. @pspec: a #GPatternSpec.
@ -102,7 +105,10 @@ Matches a string against a compiled pattern.
<!-- ##### FUNCTION g_pattern_match_simple ##### --> <!-- ##### FUNCTION g_pattern_match_simple ##### -->
<para> <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> </para>
@pattern: the pattern. @pattern: the pattern.

View File

@ -555,11 +555,11 @@ value if @s1 > @s2.
<!-- ##### FUNCTION g_strreverse ##### --> <!-- ##### FUNCTION g_strreverse ##### -->
<para> <para>
Reverses all of the characters in a string. 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> </para>
@string: the string to reverse. @string: the string to reverse.
@Returns: @Returns: the same pointer passed in as @string.
<!-- ##### FUNCTION g_strtod ##### --> <!-- ##### FUNCTION g_strtod ##### -->

View 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_UINT: Identifier for the "#GParamSpecUInt" type.
@G_TYPE_PARAM_LONG: Identifier for the "#GParamSpecLong" type. @G_TYPE_PARAM_LONG: Identifier for the "#GParamSpecLong" type.
@G_TYPE_PARAM_ULONG: Identifier for the "#GParamSpecULong" 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_UNICHAR:
@G_TYPE_PARAM_ENUM: Identifier for the "#GParamSpecEnum" type. @G_TYPE_PARAM_ENUM: Identifier for the "#GParamSpecEnum" type.
@G_TYPE_PARAM_FLAGS: Identifier for the "#GParamSpecFlags" 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_VALUE_ARRAY: Identifier for the "#GParamSpecValueArray" type.
@G_TYPE_PARAM_CLOSURE: Identifier for the "#GParamClosure" type. @G_TYPE_PARAM_CLOSURE: Identifier for the "#GParamClosure" type.
@G_TYPE_PARAM_OBJECT: Identifier for the "#GParamSpecObject" type. @G_TYPE_PARAM_OBJECT: Identifier for the "#GParamSpecObject" type.
@G_TYPE_PARAM_INT64:
@G_TYPE_PARAM_UINT64:
<!-- ##### STRUCT GTypeInterface ##### --> <!-- ##### STRUCT GTypeInterface ##### -->
<para> <para>

View File

@ -24,8 +24,56 @@
#include "gutils.h" /* inline hassle */ #include "gutils.h" /* inline hassle */
#include <string.h> #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 --- */ /* --- 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 static inline gboolean
g_pattern_ph_match (const gchar *match_pattern, g_pattern_ph_match (const gchar *match_pattern,
const gchar *match_string) 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 (pspec != NULL, FALSE);
g_return_val_if_fail (string != NULL, FALSE); g_return_val_if_fail (string != NULL, FALSE);
g_return_val_if_fail (string_reversed != NULL, FALSE);
switch (pspec->match_type) switch (pspec->match_type)
{ {
gboolean result;
gchar *tmp;
case G_MATCH_ALL: case G_MATCH_ALL:
return g_pattern_ph_match (pspec->pattern, string); return g_pattern_ph_match (pspec->pattern, string);
case G_MATCH_ALL_TAIL: 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: case G_MATCH_HEAD:
if (pspec->pattern_length > string_length) if (pspec->pattern_length > string_length)
return FALSE; return FALSE;
@ -120,25 +175,40 @@ g_pattern_match (GPatternSpec *pspec,
return strncmp (pspec->pattern, string, pspec->pattern_length) == 0; return strncmp (pspec->pattern, string, pspec->pattern_length) == 0;
else else
return TRUE; return TRUE;
case G_MATCH_TAIL: case G_MATCH_TAIL:
if (pspec->pattern_length > string_length) if (pspec->pattern_length > string_length)
return FALSE; return FALSE;
else if (pspec->pattern_length == string_length) 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) else if (pspec->pattern_length)
return strncmp (pspec->pattern_reversed, {
string_reversed, if (string_reversed)
pspec->pattern_length) == 0; 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 else
return TRUE; return TRUE;
case G_MATCH_EXACT: case G_MATCH_EXACT:
if (pspec->pattern_length != string_length) if (pspec->pattern_length != string_length)
return FALSE; return FALSE;
else else
return strcmp (pspec->pattern_reversed, string_reversed) == 0; return strcmp (pspec->pattern, string) == 0;
default: default:
g_return_val_if_fail (pspec->match_type < G_MATCH_LAST, FALSE); g_return_val_if_fail (pspec->match_type < G_MATCH_LAST, FALSE);
return FALSE; return FALSE;
@ -149,127 +219,116 @@ GPatternSpec*
g_pattern_spec_new (const gchar *pattern) g_pattern_spec_new (const gchar *pattern)
{ {
GPatternSpec *pspec; GPatternSpec *pspec;
gchar *p, *t; gboolean seen_joker = FALSE, seen_wildcard = FALSE, more_wildcards = FALSE;
const gchar *h; gint hw_pos = -1, tw_pos = -1, hj_pos = -1, tj_pos = -1;
guint hw = 0, tw = 0, hj = 0, tj = 0; gboolean follows_wildcard = FALSE;
const gchar *s;
gchar *d;
guint i;
g_return_val_if_fail (pattern != NULL, NULL); g_return_val_if_fail (pattern != NULL, NULL);
/* canonicalize pattern and collect necessary stats */
pspec = g_new (GPatternSpec, 1); pspec = g_new (GPatternSpec, 1);
pspec->pattern_length = strlen (pattern); pspec->pattern_length = strlen (pattern);
pspec->pattern = strcpy (g_new (gchar, pspec->pattern_length + 1), pattern); pspec->pattern = g_new (gchar, pspec->pattern_length + 1);
pspec->pattern_reversed = g_new (gchar, pspec->pattern_length + 1); d = pspec->pattern;
t = pspec->pattern_reversed + pspec->pattern_length; for (i = 0, s = pattern; *s != 0; s++)
*(t--) = 0;
h = pattern;
while (t >= pspec->pattern_reversed)
{ {
register gchar c = *(h++); switch (*s)
if (c == '*')
{ {
if (t < h) case '*':
hw++; if (follows_wildcard) /* compress multiple wildcards */
else
tw++;
}
else if (c == '?')
{ {
if (t < h) pspec->pattern_length--;
hj++; continue;
else
tj++;
} }
follows_wildcard = TRUE;
*(t--) = c; 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;
} }
pspec->match_type = hw > tw || (hw == tw && hj > tj) ? G_MATCH_ALL_TAIL : G_MATCH_ALL; *d++ = *s;
i++;
}
*d++ = 0;
seen_joker = hj_pos >= 0;
seen_wildcard = hw_pos >= 0;
more_wildcards = seen_wildcard && hw_pos != tw_pos;
if (hj || tj) /* special case sole head/tail wildcard or exact matches */
if (!seen_joker && !more_wildcards)
{
if (pspec->pattern[0] == '*')
{
pspec->match_type = G_MATCH_TAIL;
instring_reverse (pspec->pattern_length, pspec->pattern);
pspec->pattern[--pspec->pattern_length] = 0;
return pspec; return pspec;
}
if (hw == 0 && tw == 0) if (pspec->pattern[pspec->pattern_length - 1] == '*')
{
pspec->match_type = G_MATCH_HEAD;
pspec->pattern[--pspec->pattern_length] = 0;
return pspec;
}
if (!seen_wildcard)
{ {
pspec->match_type = G_MATCH_EXACT; pspec->match_type = G_MATCH_EXACT;
return pspec; return pspec;
} }
}
if (hw) /* now just need to distinguish between head or tail match start */
{ tw_pos = pspec->pattern_length - 1 - tw_pos; /* last pos to tail distance */
p = pspec->pattern; tj_pos = pspec->pattern_length - 1 - tj_pos; /* last pos to tail distance */
while (*p == '*') if (seen_wildcard)
p++; pspec->match_type = tw_pos > hw_pos ? G_MATCH_ALL_TAIL : G_MATCH_ALL;
if (p > pspec->pattern && !strchr (p, '*')) else /* seen_joker */
{ pspec->match_type = tj_pos > hj_pos ? G_MATCH_ALL_TAIL : G_MATCH_ALL;
gchar *tmp; if (pspec->match_type == G_MATCH_ALL_TAIL)
instring_reverse (pspec->pattern_length, pspec->pattern);
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++);
return pspec; return pspec;
} }
}
if (tw) void
g_pattern_spec_free (GPatternSpec *pspec)
{ {
p = pspec->pattern_reversed; g_return_if_fail (pspec != NULL);
while (*p == '*')
p++;
if (p > pspec->pattern_reversed && !strchr (p, '*'))
{
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); g_free (pspec->pattern);
pspec->pattern = g_new (gchar, pspec->pattern_length + 1); g_free (pspec);
t = pspec->pattern + pspec->pattern_length;
*(t--) = 0;
h = pspec->pattern_reversed;
while (t >= pspec->pattern)
*(t--) = *(h++);
}
} }
return 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 gboolean
g_pattern_match_string (GPatternSpec *pspec, g_pattern_match_string (GPatternSpec *pspec,
const gchar *string) 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 (pspec != NULL, FALSE);
g_return_val_if_fail (string != NULL, FALSE); g_return_val_if_fail (string != NULL, FALSE);
length = strlen (string); return g_pattern_match (pspec, strlen (string), string, NULL);
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;
} }
gboolean gboolean
@ -283,18 +342,8 @@ g_pattern_match_simple (const gchar *pattern,
g_return_val_if_fail (string != NULL, FALSE); g_return_val_if_fail (string != NULL, FALSE);
pspec = g_pattern_spec_new (pattern); 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); g_pattern_spec_free (pspec);
return ergo; 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);
}

View File

@ -23,27 +23,13 @@
G_BEGIN_DECLS 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; 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); GPatternSpec* g_pattern_spec_new (const gchar *pattern);
void g_pattern_spec_free (GPatternSpec *pspec); void g_pattern_spec_free (GPatternSpec *pspec);
gboolean g_pattern_spec_equal (GPatternSpec *pspec1,
GPatternSpec *pspec2);
gboolean g_pattern_match (GPatternSpec *pspec, gboolean g_pattern_match (GPatternSpec *pspec,
guint string_length, guint string_length,
const gchar *string, const gchar *string,

View File

@ -52,3 +52,4 @@ uri-test
unicode-caseconv unicode-caseconv
unicode-collate unicode-collate
unicode-normalize unicode-normalize
patterntest

View File

@ -39,8 +39,9 @@ endif
if ENABLE_TIMELOOP if ENABLE_TIMELOOP
timeloop = timeloop timeloop-closure timeloop = timeloop timeloop-closure
endif 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) testglib_LDADD = $(libglib)
patterntest_LDADD = $(libglib)
testgdate_LDADD = $(libglib) testgdate_LDADD = $(libglib)
testgdateparser_LDADD = $(libglib) testgdateparser_LDADD = $(libglib)
unicode_normalize_LDADD = $(libglib) unicode_normalize_LDADD = $(libglib)

225
tests/patterntest.c Normal file
View File

@ -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;
}