New functions: g_regex_ref(), g_regex_unref() which replaces

2007-06-03  Yevgen Muntyan  <muntyan@tamu.edu>

	* glib/gregex.c:
	* glib/gregex.h: New functions: g_regex_ref(), g_regex_unref() which
	replaces g_regex_free(); g_match_info_get_regex(), g_match_info_get_string();
	g_regex_check_replacement().
	Made g_match_info_expand_references() accept NULL; changed GRegexEvalCallback
	to take only arguments which are likely to be actualy used.

	* docs/reference/glib/glib-sections.txt:
	* glib/glib.symbols: Added new functions.

	* tests/regex-test.c: Test them.

	* docs/reference/glib/tmpl/gregex.sgml: Updated GRegexEvalCallback docs.


svn path=/trunk/; revision=5524
This commit is contained in:
Yevgen Muntyan 2007-06-03 05:48:17 +00:00 committed by Yevgen Muntyan
parent 458c58ae43
commit 904c42896f
7 changed files with 328 additions and 105 deletions

View File

@ -1,3 +1,19 @@
2007-06-03 Yevgen Muntyan <muntyan@tamu.edu>
* glib/gregex.c:
* glib/gregex.h: New functions: g_regex_ref(), g_regex_unref() which
replaces g_regex_free(); g_match_info_get_regex(), g_match_info_get_string();
g_regex_check_replacement().
Made g_match_info_expand_references() accept NULL; changed GRegexEvalCallback
to take only arguments which are likely to be actualy used.
* docs/reference/glib/glib-sections.txt:
* glib/glib.symbols: Added new functions.
* tests/regex-test.c: Test them.
* docs/reference/glib/tmpl/gregex.sgml: Updated GRegexEvalCallback docs.
2007-05-31 Matthias Clasen <mclasen@redhat.com> 2007-05-31 Matthias Clasen <mclasen@redhat.com>
* README.win32: Fix a typo. (#423708, Olivier Delhomme) * README.win32: Fix a typo. (#423708, Olivier Delhomme)

View File

@ -873,7 +873,8 @@ GRegexMatchFlags
GRegex GRegex
GRegexEvalCallback GRegexEvalCallback
g_regex_new g_regex_new
g_regex_free g_regex_ref
g_regex_unref
g_regex_get_pattern g_regex_get_pattern
g_regex_get_max_backref g_regex_get_max_backref
g_regex_get_capture_count g_regex_get_capture_count
@ -890,7 +891,10 @@ g_regex_split_full
g_regex_replace g_regex_replace
g_regex_replace_literal g_regex_replace_literal
g_regex_replace_eval g_regex_replace_eval
g_regex_check_replacement
GMatchInfo GMatchInfo
g_match_info_get_regex
g_match_info_get_string
g_match_info_free g_match_info_free
g_match_info_next g_match_info_next
g_match_info_get_match_count g_match_info_get_match_count

View File

@ -203,15 +203,16 @@ structure is opaque and its fields cannot be accessed directly.
<!-- ##### USER_FUNCTION GRegexEvalCallback ##### --> <!-- ##### USER_FUNCTION GRegexEvalCallback ##### -->
<para> <para>
Specifies the type of the function passed to g_regex_replace_eval(). Specifies the type of the function passed to g_regex_replace_eval().
It is called for each occurance of the pattern @regex in @string, and it It is called for each occurance of the pattern in the string passed
should append the replacement to @result. to g_regex_replace_eval(), and it should append the replacement to
@result.
</para> </para>
@Param1: the #GRegex passed to g_regex_replace_eval() @Param1: the #GMatchInfo generated by the match. Use g_match_info_get_regex()
@Param2: the #GMatchInfo generated by the match and g_match_info_get_string() if you need the #GRegex or the matched
@Param3: the string used to perform matches against string.
@Param4: a #GString containing the new string @Param2: a #GString containing the new string
@Param5: user data passed to g_regex_replace_eval() @Param3: user data passed to g_regex_replace_eval()
@Returns: %FALSE to continue the replacement process, %TRUE to stop it @Returns: %FALSE to continue the replacement process, %TRUE to stop it
@Since: 2.14 @Since: 2.14

View File

@ -1427,7 +1427,8 @@ g_get_codeset
#if IN_FILE(__G_REGEX_C__) #if IN_FILE(__G_REGEX_C__)
g_regex_error_quark g_regex_error_quark
g_regex_new g_regex_new
g_regex_free g_regex_ref
g_regex_unref
g_regex_get_pattern g_regex_get_pattern
g_regex_get_max_backref g_regex_get_max_backref
g_regex_get_capture_count g_regex_get_capture_count
@ -1444,6 +1445,9 @@ g_regex_split_full
g_regex_replace g_regex_replace
g_regex_replace_literal g_regex_replace_literal
g_regex_replace_eval g_regex_replace_eval
g_regex_check_replacement
g_match_info_get_regex
g_match_info_get_string
g_match_info_free g_match_info_free
g_match_info_next g_match_info_next
g_match_info_matches g_match_info_matches

View File

@ -98,13 +98,9 @@ struct _GRegex
/* TRUE if ret is an error code, FALSE otherwise. */ /* TRUE if ret is an error code, FALSE otherwise. */
#define IS_PCRE_ERROR(ret) ((ret) < PCRE_ERROR_NOMATCH && (ret) != PCRE_ERROR_PARTIAL) #define IS_PCRE_ERROR(ret) ((ret) < PCRE_ERROR_NOMATCH && (ret) != PCRE_ERROR_PARTIAL)
static GRegex *regex_ref (GRegex *regex);
static void regex_unref (GRegex *regex);
typedef struct _InterpolationData InterpolationData; typedef struct _InterpolationData InterpolationData;
static gboolean interpolate_replacement (const GRegex *regex, static gboolean interpolation_list_needs_match (GList *list);
const GMatchInfo *match_info, static gboolean interpolate_replacement (const GMatchInfo *match_info,
const gchar *string,
GString *result, GString *result,
gpointer data); gpointer data);
static GList *split_replacement (const gchar *replacement, static GList *split_replacement (const gchar *replacement,
@ -195,7 +191,7 @@ match_info_new (const GRegex *regex,
string_len = strlen (string); string_len = strlen (string);
match_info = g_new0 (GMatchInfo, 1); match_info = g_new0 (GMatchInfo, 1);
match_info->regex = regex_ref ((GRegex *)regex); match_info->regex = g_regex_ref ((GRegex *)regex);
match_info->string = string; match_info->string = string;
match_info->string_len = string_len; match_info->string_len = string_len;
match_info->matches = PCRE_ERROR_NOMATCH; match_info->matches = PCRE_ERROR_NOMATCH;
@ -222,6 +218,44 @@ match_info_new (const GRegex *regex,
return match_info; return match_info;
} }
/**
* g_match_info_get_regex:
* @match_info: a #GMatchInfo
*
* Returns #GRegex object used in @match_info. It belongs to glib
* and must not be freed. Use g_regex_ref() if you need to keep it
* after you free @match_info object.
*
* Returns: #GRegex object used in @match_info
*
* Since: 2.14
*/
GRegex *
g_match_info_get_regex (const GMatchInfo *match_info)
{
g_return_val_if_fail (match_info != NULL, NULL);
return match_info->regex;
}
/**
* g_match_info_get_string:
* @match_info: a #GMatchInfo
*
* Returns the string searched with @match_info. This is the
* string passed to g_regex_match() or g_regex_replace() so
* you may not free it before calling this function.
*
* Returns: the string searched with @match_info
*
* Since: 2.14
*/
const gchar *
g_match_info_get_string (const GMatchInfo *match_info)
{
g_return_val_if_fail (match_info != NULL, NULL);
return match_info->string;
}
/** /**
* g_match_info_free: * g_match_info_free:
* @match_info: a #GMatchInfo * @match_info: a #GMatchInfo
@ -233,10 +267,13 @@ match_info_new (const GRegex *regex,
void void
g_match_info_free (GMatchInfo *match_info) g_match_info_free (GMatchInfo *match_info)
{ {
regex_unref (match_info->regex); if (match_info)
g_free (match_info->offsets); {
g_free (match_info->workspace); g_regex_unref (match_info->regex);
g_free (match_info); g_free (match_info->offsets);
g_free (match_info->workspace);
g_free (match_info);
}
} }
/** /**
@ -403,13 +440,14 @@ g_match_info_is_partial_match (const GMatchInfo *match_info)
/** /**
* g_match_info_expand_references: * g_match_info_expand_references:
* @match_info: a #GMatchInfo * @match_info: a #GMatchInfo or %NULL
* @string_to_expand: the string to expand * @string_to_expand: the string to expand
* @error: location to store the error occuring, or %NULL to ignore errors * @error: location to store the error occuring, or %NULL to ignore errors
* *
* Returns a new string containing the text in @string_to_expand with * Returns a new string containing the text in @string_to_expand with
* references expanded. References refer to the last match done with * references and escape sequences expanded. References refer to the last
* @string against @regex and have the same syntax used by g_regex_replace(). * match done with @string against @regex and have the same syntax used by
* g_regex_replace().
* *
* The @string_to_expand must be UTF-8 encoded even if #G_REGEX_RAW was * The @string_to_expand must be UTF-8 encoded even if #G_REGEX_RAW was
* passed to g_regex_new(). * passed to g_regex_new().
@ -417,6 +455,13 @@ g_match_info_is_partial_match (const GMatchInfo *match_info)
* The backreferences are extracted from the string passed to the match * The backreferences are extracted from the string passed to the match
* function, so you cannot call this function after freeing the string. * function, so you cannot call this function after freeing the string.
* *
* @match_info may be %NULL in which case @string_to_expand must not
* contain references. For instance "foo\n" does not refer to an actual
* pattern and '\n' merely will be replaced with \n character,
* while to expand "\0" (whole match) one needs the result of a match.
* Use g_regex_check_replacement() to find out whether @string_to_expand
* contains references.
*
* Returns: the expanded string, or %NULL if an error occurred * Returns: the expanded string, or %NULL if an error occurred
* *
* Since: 2.14 * Since: 2.14
@ -430,7 +475,6 @@ g_match_info_expand_references (const GMatchInfo *match_info,
GList *list; GList *list;
GError *tmp_error = NULL; GError *tmp_error = NULL;
g_return_val_if_fail (match_info != NULL, NULL);
g_return_val_if_fail (string_to_expand != NULL, NULL); g_return_val_if_fail (string_to_expand != NULL, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL);
@ -441,9 +485,16 @@ g_match_info_expand_references (const GMatchInfo *match_info,
return NULL; return NULL;
} }
if (!match_info && interpolation_list_needs_match (list))
{
g_critical ("String '%s' contains references to the match, can't "
"expand references without GMatchInfo object",
string_to_expand);
return NULL;
}
result = g_string_sized_new (strlen (string_to_expand)); result = g_string_sized_new (strlen (string_to_expand));
interpolate_replacement (match_info->regex, match_info, interpolate_replacement (match_info, result, list);
match_info->string, result, list);
g_list_foreach (list, (GFunc)free_interpolation_data, NULL); g_list_foreach (list, (GFunc)free_interpolation_data, NULL);
g_list_free (list); g_list_free (list);
@ -724,16 +775,38 @@ g_regex_error_quark (void)
return error_quark; return error_quark;
} }
static GRegex * /**
regex_ref (GRegex *regex) * g_regex_ref:
* @regex: a #GRegex
*
* Increases reference count of @regex by 1.
*
* Returns: @regex
*
* Since: 2.14
*/
GRegex *
g_regex_ref (GRegex *regex)
{ {
g_return_val_if_fail (regex != NULL, NULL);
g_atomic_int_inc ((gint*) &regex->ref_count); g_atomic_int_inc ((gint*) &regex->ref_count);
return regex; return regex;
} }
static void /**
regex_unref (GRegex *regex) * g_regex_unref:
* @regex: a #GRegex
*
* Decreases reference count of @regex by 1. When reference count drops
* to zero, it frees all the memory associated with the regex structure.
*
* Since: 2.14
*/
void
g_regex_unref (GRegex *regex)
{ {
g_return_if_fail (regex != NULL);
if (g_atomic_int_exchange_and_add ((gint *) &regex->ref_count, -1) - 1 == 0) if (g_atomic_int_exchange_and_add ((gint *) &regex->ref_count, -1) - 1 == 0)
{ {
g_free (regex->pattern); g_free (regex->pattern);
@ -755,7 +828,8 @@ regex_unref (GRegex *regex)
* Compiles the regular expression to an internal form, and does the initial * Compiles the regular expression to an internal form, and does the initial
* setup of the #GRegex structure. * setup of the #GRegex structure.
* *
* Returns: a #GRegex structure * Returns: a #GRegex structure. Call g_regex_unref() when you are done
* with it.
* *
* Since: 2.14 * Since: 2.14
*/ */
@ -873,23 +947,6 @@ g_regex_new (const gchar *pattern,
return regex; return regex;
} }
/**
* g_regex_free:
* @regex: a #GRegex
*
* Frees all the memory associated with the regex structure.
*
* Since: 2.14
*/
void
g_regex_free (GRegex *regex)
{
if (regex == NULL)
return;
regex_unref (regex);
}
/** /**
* g_regex_get_pattern: * g_regex_get_pattern:
* @regex: a #GRegex structure * @regex: a #GRegex structure
@ -988,7 +1045,7 @@ g_regex_match_simple (const gchar *pattern,
if (!regex) if (!regex)
return FALSE; return FALSE;
result = g_regex_match_full (regex, string, -1, 0, match_options, NULL, NULL); result = g_regex_match_full (regex, string, -1, 0, match_options, NULL, NULL);
g_regex_free (regex); g_regex_unref (regex);
return result; return result;
} }
@ -1006,7 +1063,9 @@ g_regex_match_simple (const gchar *pattern,
* structures. * structures.
* *
* A #GMatchInfo structure, used to get information on the match, is stored * A #GMatchInfo structure, used to get information on the match, is stored
* in @match_info if not %NULL. * in @match_info if not %NULL. Note that if @match_info is not %NULL then
* it is created even if the function returns %FALSE, i.e. you must free it
* regardless if regular expression actually matched.
* *
* To retrieve all the non-overlapping matches of the pattern in string you * To retrieve all the non-overlapping matches of the pattern in string you
* can use g_match_info_next(). * can use g_match_info_next().
@ -1029,7 +1088,7 @@ g_regex_match_simple (const gchar *pattern,
* g_match_info_next (match_info, NULL); * g_match_info_next (match_info, NULL);
* } * }
* g_match_info_free (match_info); * g_match_info_free (match_info);
* g_regex_free (regex); * g_regex_unref (regex);
* } * }
* </programlisting></informalexample> * </programlisting></informalexample>
* *
@ -1068,7 +1127,13 @@ g_regex_match (const GRegex *regex,
* with any kind of lookbehind assertion, such as "\b". * with any kind of lookbehind assertion, such as "\b".
* *
* A #GMatchInfo structure, used to get information on the match, is stored * A #GMatchInfo structure, used to get information on the match, is stored
* in @match_info if not %NULL. * in @match_info if not %NULL. Note that if @match_info is not %NULL then
* it is created even if the function returns %FALSE, i.e. you must free it
* regardless if regular expression actually matched.
*
* @string is not copied and is used in #GMatchInfo internally. If you use
* any #GMatchInfo method (except g_match_info_free()) after freeing or
* modifying @string then the behaviour is undefined.
* *
* To retrieve all the non-overlapping matches of the pattern in string you * To retrieve all the non-overlapping matches of the pattern in string you
* can use g_match_info_next(). * can use g_match_info_next().
@ -1092,7 +1157,7 @@ g_regex_match (const GRegex *regex,
* g_match_info_next (match_info, &error); * g_match_info_next (match_info, &error);
* } * }
* g_match_info_free (match_info); * g_match_info_free (match_info);
* g_regex_free (regex); * g_regex_unref (regex);
* if (error != NULL) * if (error != NULL)
* { * {
* g_printerr ("Error while matching: %s\n", error->message); * g_printerr ("Error while matching: %s\n", error->message);
@ -1148,7 +1213,9 @@ g_regex_match_full (const GRegex *regex,
* For more documentation see g_regex_match_all_full(). * For more documentation see g_regex_match_all_full().
* *
* A #GMatchInfo structure, used to get information on the match, is stored * A #GMatchInfo structure, used to get information on the match, is stored
* in @match_info if not %NULL. * in @match_info if not %NULL. Note that if @match_info is not %NULL then
* it is created even if the function returns %FALSE, i.e. you must free it
* regardless if regular expression actually matched.
* *
* Returns: %TRUE is the string matched, %FALSE otherwise * Returns: %TRUE is the string matched, %FALSE otherwise
* *
@ -1203,7 +1270,9 @@ g_regex_match_all (const GRegex *regex,
* with any kind of lookbehind assertion, such as "\b". * with any kind of lookbehind assertion, such as "\b".
* *
* A #GMatchInfo structure, used to get information on the match, is stored * A #GMatchInfo structure, used to get information on the match, is stored
* in @match_info if not %NULL. * in @match_info if not %NULL. Note that if @match_info is not %NULL then
* it is created even if the function returns %FALSE, i.e. you must free it
* regardless if regular expression actually matched.
* *
* Returns: %TRUE is the string matched, %FALSE otherwise * Returns: %TRUE is the string matched, %FALSE otherwise
* *
@ -1353,7 +1422,7 @@ g_regex_split_simple (const gchar *pattern,
if (!regex) if (!regex)
return NULL; return NULL;
result = g_regex_split_full (regex, string, -1, 0, match_options, 0, NULL); result = g_regex_split_full (regex, string, -1, 0, match_options, 0, NULL);
g_regex_free (regex); g_regex_unref (regex);
return result; return result;
} }
@ -1955,9 +2024,7 @@ string_append (GString *string,
} }
static gboolean static gboolean
interpolate_replacement (const GRegex *regex, interpolate_replacement (const GMatchInfo *match_info,
const GMatchInfo *match_info,
const gchar *string,
GString *result, GString *result,
gpointer data) gpointer data)
{ {
@ -2004,6 +2071,28 @@ interpolate_replacement (const GRegex *regex,
return FALSE; return FALSE;
} }
/* whether actual match_info is needed for replacement, i.e.
* whether there are references
*/
static gboolean
interpolation_list_needs_match (GList *list)
{
while (list != NULL)
{
InterpolationData *data = list->data;
if (data->type == REPL_TYPE_SYMBOLIC_REFERENCE ||
data->type == REPL_TYPE_NUMERIC_REFERENCE)
{
return TRUE;
}
list = list->next;
}
return FALSE;
}
/** /**
* g_regex_replace: * g_regex_replace:
* @regex: a #GRegex structure * @regex: a #GRegex structure
@ -2108,9 +2197,7 @@ g_regex_replace (const GRegex *regex,
} }
static gboolean static gboolean
literal_replacement (const GRegex *regex, literal_replacement (const GMatchInfo *match_info,
const GMatchInfo *match_info,
const gchar *string,
GString *result, GString *result,
gpointer data) gpointer data)
{ {
@ -2217,7 +2304,7 @@ g_regex_replace_eval (const GRegex *regex,
g_string_append_len (result, g_string_append_len (result,
string + str_pos, string + str_pos,
match_info->offsets[0] - str_pos); match_info->offsets[0] - str_pos);
done = (*eval) (regex, match_info, string, result, user_data); done = (*eval) (match_info, result, user_data);
str_pos = match_info->offsets[1]; str_pos = match_info->offsets[1];
g_match_info_next (match_info, &tmp_error); g_match_info_next (match_info, &tmp_error);
} }
@ -2233,6 +2320,51 @@ g_regex_replace_eval (const GRegex *regex,
return g_string_free (result, FALSE); return g_string_free (result, FALSE);
} }
/**
* g_regex_check_replacement:
* @replacement: the replacement string
* @has_references: location to store information about
* references in @replacement or %NULL
* @error: location to store error
*
* Checks whether @replacement is a valid replacement string (see g_regex_replace()),
* i.e. that all escape sequences in it are valid.
*
* If @has_references is not %NULL then @replacement is checked for
* pattern references. For instance, replacement text 'foo\n'
* does not contain references and may be evaluated without information
* about actual match, but '\0\1' (whole match followed by first subpattern)
* requires valid #GMatchInfo object.
*
* Returns: whether @replacement is a valid replacement string
*
* Since: 2.14
*/
gboolean
g_regex_check_replacement (const gchar *replacement,
gboolean *has_references,
GError **error)
{
GList *list;
GError *tmp = NULL;
list = split_replacement (replacement, &tmp);
if (tmp)
{
g_propagate_error (error, tmp);
return FALSE;
}
if (has_references)
*has_references = interpolation_list_needs_match (list);
g_list_foreach (list, (GFunc) free_interpolation_data, NULL);
g_list_free (list);
return TRUE;
}
/** /**
* g_regex_escape_string: * g_regex_escape_string:
* @string: the string to escape * @string: the string to escape

View File

@ -77,18 +77,17 @@ typedef enum
typedef struct _GRegex GRegex; typedef struct _GRegex GRegex;
typedef struct _GMatchInfo GMatchInfo; typedef struct _GMatchInfo GMatchInfo;
typedef gboolean (*GRegexEvalCallback) (const GRegex *, typedef gboolean (*GRegexEvalCallback) (const GMatchInfo *match_info,
const GMatchInfo *, GString *result,
const gchar *, gpointer user_data);
GString *,
gpointer);
GRegex *g_regex_new (const gchar *pattern, GRegex *g_regex_new (const gchar *pattern,
GRegexCompileFlags compile_options, GRegexCompileFlags compile_options,
GRegexMatchFlags match_options, GRegexMatchFlags match_options,
GError **error); GError **error);
void g_regex_free (GRegex *regex); GRegex *g_regex_ref (GRegex *regex);
void g_regex_unref (GRegex *regex);
const gchar *g_regex_get_pattern (const GRegex *regex); const gchar *g_regex_get_pattern (const GRegex *regex);
gint g_regex_get_max_backref (const GRegex *regex); gint g_regex_get_max_backref (const GRegex *regex);
gint g_regex_get_capture_count (const GRegex *regex); gint g_regex_get_capture_count (const GRegex *regex);
@ -164,8 +163,14 @@ gchar *g_regex_replace_eval (const GRegex *regex,
GRegexEvalCallback eval, GRegexEvalCallback eval,
gpointer user_data, gpointer user_data,
GError **error); GError **error);
gboolean g_regex_check_replacement (const gchar *replacement,
gboolean *has_references,
GError **error);
/* Match info */ /* Match info */
GRegex *g_match_info_get_regex (const GMatchInfo *match_info);
const gchar *g_match_info_get_string (const GMatchInfo *match_info);
void g_match_info_free (GMatchInfo *match_info); void g_match_info_free (GMatchInfo *match_info);
gboolean g_match_info_next (GMatchInfo *match_info, gboolean g_match_info_next (GMatchInfo *match_info,
GError **error); GError **error);

View File

@ -112,13 +112,11 @@ test_new (const gchar *pattern,
{ {
g_print ("failed \t(pattern: \"%s\")\n", g_print ("failed \t(pattern: \"%s\")\n",
pattern); pattern);
g_regex_free (regex); g_regex_unref (regex);
return FALSE; return FALSE;
} }
g_regex_free (regex); g_regex_unref (regex);
/* Free a null string. */
g_regex_free (NULL);
verbose ("passed\n"); verbose ("passed\n");
return TRUE; return TRUE;
@ -146,7 +144,7 @@ test_new_fail (const gchar *pattern,
{ {
g_print ("failed \t(pattern: \"%s\", compile: %d)\n", g_print ("failed \t(pattern: \"%s\", compile: %d)\n",
pattern, compile_opts); pattern, compile_opts);
g_regex_free (regex); g_regex_unref (regex);
return FALSE; return FALSE;
} }
@ -220,7 +218,7 @@ test_match (const gchar *pattern,
g_print ("failed \t(unexpected %s) '%s' against '%s'\n", match ? "match" : "mismatch", e1, e2); g_print ("failed \t(unexpected %s) '%s' against '%s'\n", match ? "match" : "mismatch", e1, e2);
g_free (e1); g_free (e1);
g_free (e2); g_free (e2);
g_regex_free (regex); g_regex_unref (regex);
return FALSE; return FALSE;
} }
@ -231,12 +229,12 @@ test_match (const gchar *pattern,
{ {
g_print ("failed \t(pattern: \"%s\", string: \"%s\")\n", g_print ("failed \t(pattern: \"%s\", string: \"%s\")\n",
pattern, string); pattern, string);
g_regex_free (regex); g_regex_unref (regex);
return FALSE; return FALSE;
} }
} }
g_regex_free (regex); g_regex_unref (regex);
verbose ("passed (%s)\n", match ? "match" : "nomatch"); verbose ("passed (%s)\n", match ? "match" : "nomatch");
return TRUE; return TRUE;
@ -319,6 +317,8 @@ test_match_next (const gchar *pattern,
matches = g_slist_prepend (matches, match); matches = g_slist_prepend (matches, match);
g_match_info_next (match_info, NULL); g_match_info_next (match_info, NULL);
} }
g_assert (regex == g_match_info_get_regex (match_info));
g_assert (string == g_match_info_get_string (match_info));
g_match_info_free (match_info); g_match_info_free (match_info);
matches = g_slist_reverse (matches); matches = g_slist_reverse (matches);
@ -366,7 +366,7 @@ exit:
verbose ("passed (%d %s)\n", count, count == 1 ? "match" : "matches"); verbose ("passed (%d %s)\n", count, count == 1 ? "match" : "matches");
} }
g_regex_free (regex); g_regex_unref (regex);
g_slist_foreach (expected, free_match, NULL); g_slist_foreach (expected, free_match, NULL);
g_slist_free (expected); g_slist_free (expected);
g_slist_foreach (matches, free_match, NULL); g_slist_foreach (matches, free_match, NULL);
@ -450,7 +450,7 @@ test_match_count (const gchar *pattern,
} }
g_match_info_free (match_info); g_match_info_free (match_info);
g_regex_free (regex); g_regex_unref (regex);
verbose ("passed\n"); verbose ("passed\n");
return TRUE; return TRUE;
@ -481,26 +481,26 @@ test_partial (const gchar *pattern,
if (expected != g_match_info_is_partial_match (match_info)) if (expected != g_match_info_is_partial_match (match_info))
{ {
g_print ("failed \t(got %d, expected: %d)\n", !expected, expected); g_print ("failed \t(got %d, expected: %d)\n", !expected, expected);
g_regex_free (regex); g_regex_unref (regex);
return FALSE; return FALSE;
} }
if (expected && g_match_info_fetch_pos (match_info, 0, NULL, NULL)) if (expected && g_match_info_fetch_pos (match_info, 0, NULL, NULL))
{ {
g_print ("failed \t(got sub-pattern 0)\n"); g_print ("failed \t(got sub-pattern 0)\n");
g_regex_free (regex); g_regex_unref (regex);
return FALSE; return FALSE;
} }
if (expected && g_match_info_fetch_pos (match_info, 1, NULL, NULL)) if (expected && g_match_info_fetch_pos (match_info, 1, NULL, NULL))
{ {
g_print ("failed \t(got sub-pattern 1)\n"); g_print ("failed \t(got sub-pattern 1)\n");
g_regex_free (regex); g_regex_unref (regex);
return FALSE; return FALSE;
} }
g_match_info_free (match_info); g_match_info_free (match_info);
g_regex_free (regex); g_regex_unref (regex);
verbose ("passed\n"); verbose ("passed\n");
return TRUE; return TRUE;
@ -540,7 +540,7 @@ test_sub_pattern (const gchar *pattern,
g_print ("failed \t(got \"%s\", expected \"%s\")\n", g_print ("failed \t(got \"%s\", expected \"%s\")\n",
sub_expr, expected_sub); sub_expr, expected_sub);
g_free (sub_expr); g_free (sub_expr);
g_regex_free (regex); g_regex_unref (regex);
return FALSE; return FALSE;
} }
g_free (sub_expr); g_free (sub_expr);
@ -550,12 +550,12 @@ test_sub_pattern (const gchar *pattern,
{ {
g_print ("failed \t(got [%d, %d], expected [%d, %d])\n", g_print ("failed \t(got [%d, %d], expected [%d, %d])\n",
start, end, expected_start, expected_end); start, end, expected_start, expected_end);
g_regex_free (regex); g_regex_unref (regex);
return FALSE; return FALSE;
} }
g_match_info_free (match_info); g_match_info_free (match_info);
g_regex_free (regex); g_regex_unref (regex);
verbose ("passed\n"); verbose ("passed\n");
return TRUE; return TRUE;
@ -598,7 +598,7 @@ test_named_sub_pattern (const gchar *pattern,
g_print ("failed \t(got \"%s\", expected \"%s\")\n", g_print ("failed \t(got \"%s\", expected \"%s\")\n",
sub_expr, expected_sub); sub_expr, expected_sub);
g_free (sub_expr); g_free (sub_expr);
g_regex_free (regex); g_regex_unref (regex);
return FALSE; return FALSE;
} }
g_free (sub_expr); g_free (sub_expr);
@ -608,12 +608,12 @@ test_named_sub_pattern (const gchar *pattern,
{ {
g_print ("failed \t(got [%d, %d], expected [%d, %d])\n", g_print ("failed \t(got [%d, %d], expected [%d, %d])\n",
start, end, expected_start, expected_end); start, end, expected_start, expected_end);
g_regex_free (regex); g_regex_unref (regex);
return FALSE; return FALSE;
} }
g_match_info_free (match_info); g_match_info_free (match_info);
g_regex_free (regex); g_regex_unref (regex);
verbose ("passed\n"); verbose ("passed\n");
return TRUE; return TRUE;
@ -704,7 +704,7 @@ test_fetch_all (const gchar *pattern,
exit: exit:
g_match_info_free (match_info); g_match_info_free (match_info);
g_regex_free (regex); g_regex_unref (regex);
g_slist_foreach (expected, (GFunc)g_free, NULL); g_slist_foreach (expected, (GFunc)g_free, NULL);
g_slist_free (expected); g_slist_free (expected);
g_strfreev (matches); g_strfreev (matches);
@ -907,7 +907,7 @@ test_split_full (const gchar *pattern,
token_count == 1 ? "token" : "tokens"); token_count == 1 ? "token" : "tokens");
exit: exit:
g_regex_free (regex); g_regex_unref (regex);
g_slist_foreach (expected, (GFunc)g_free, NULL); g_slist_foreach (expected, (GFunc)g_free, NULL);
g_slist_free (expected); g_slist_free (expected);
g_strfreev (tokens); g_strfreev (tokens);
@ -976,7 +976,7 @@ test_split (const gchar *pattern,
token_count == 1 ? "token" : "tokens"); token_count == 1 ? "token" : "tokens");
exit: exit:
g_regex_free (regex); g_regex_unref (regex);
g_slist_foreach (expected, (GFunc)g_free, NULL); g_slist_foreach (expected, (GFunc)g_free, NULL);
g_slist_free (expected); g_slist_free (expected);
g_strfreev (tokens); g_strfreev (tokens);
@ -1048,6 +1048,44 @@ exit:
} \ } \
} }
static gboolean
test_check_replacement (const gchar *string_to_expand,
gboolean expected,
gboolean expected_refs)
{
gboolean result;
gboolean has_refs;
verbose ("checking replacement string \"%s\" \t", string_to_expand);
result = g_regex_check_replacement (string_to_expand, &has_refs, NULL);
if (expected != result)
{
g_print ("failed \t(got \"%s\", expected \"%s\")\n",
result ? "TRUE" : "FALSE",
expected ? "TRUE" : "FALSE");
return FALSE;
}
if (expected && expected_refs != has_refs)
{
g_print ("failed \t(got has_references \"%s\", expected \"%s\")\n",
has_refs ? "TRUE" : "FALSE",
expected_refs ? "TRUE" : "FALSE");
return FALSE;
}
verbose ("passed\n");
return TRUE;
}
#define TEST_CHECK_REPLACEMENT(string_to_expand, expected, expected_refs) { \
total++; \
if (test_check_replacement (string_to_expand, expected, expected_refs)) \
PASS; \
else \
FAIL; \
}
static gboolean static gboolean
test_expand (const gchar *pattern, test_expand (const gchar *pattern,
const gchar *string, const gchar *string,
@ -1055,27 +1093,33 @@ test_expand (const gchar *pattern,
gboolean raw, gboolean raw,
const gchar *expected) const gchar *expected)
{ {
GRegex *regex; GRegex *regex = NULL;
GMatchInfo *match_info; GMatchInfo *match_info = NULL;
gchar *res; gchar *res;
verbose ("expanding the references in \"%s\" (pattern: \"%s\", string: \"%s\") \t", verbose ("expanding the references in \"%s\" (pattern: \"%s\", string: \"%s\") \t",
string_to_expand, pattern, string); string_to_expand, pattern, string);
regex = g_regex_new (pattern, raw ? G_REGEX_RAW : 0, 0, NULL); if (pattern)
g_regex_match (regex, string, 0, &match_info); {
regex = g_regex_new (pattern, raw ? G_REGEX_RAW : 0, 0, NULL);
g_regex_match (regex, string, 0, &match_info);
}
res = g_match_info_expand_references (match_info, string_to_expand, NULL); res = g_match_info_expand_references (match_info, string_to_expand, NULL);
if (!streq (res, expected)) if (!streq (res, expected))
{ {
g_print ("failed \t(got \"%s\", expected \"%s\")\n", res, expected); g_print ("failed \t(got \"%s\", expected \"%s\")\n", res, expected);
g_free (res); g_free (res);
g_regex_free (regex); g_match_info_free (match_info);
g_regex_unref (regex);
return FALSE; return FALSE;
} }
g_free (res); g_free (res);
g_match_info_free (match_info); g_match_info_free (match_info);
g_regex_free (regex); if (regex)
g_regex_unref (regex);
verbose ("passed\n"); verbose ("passed\n");
return TRUE; return TRUE;
@ -1108,12 +1152,12 @@ test_replace (const gchar *pattern,
{ {
g_print ("failed \t(got \"%s\", expected \"%s\")\n", res, expected); g_print ("failed \t(got \"%s\", expected \"%s\")\n", res, expected);
g_free (res); g_free (res);
g_regex_free (regex); g_regex_unref (regex);
return FALSE; return FALSE;
} }
g_free (res); g_free (res);
g_regex_free (regex); g_regex_unref (regex);
verbose ("passed\n"); verbose ("passed\n");
return TRUE; return TRUE;
@ -1147,12 +1191,12 @@ test_replace_lit (const gchar *pattern,
{ {
g_print ("failed \t(got \"%s\", expected \"%s\")\n", res, expected); g_print ("failed \t(got \"%s\", expected \"%s\")\n", res, expected);
g_free (res); g_free (res);
g_regex_free (regex); g_regex_unref (regex);
return FALSE; return FALSE;
} }
g_free (res); g_free (res);
g_regex_free (regex); g_regex_unref (regex);
verbose ("passed\n"); verbose ("passed\n");
return TRUE; return TRUE;
@ -1179,7 +1223,7 @@ test_get_string_number (const gchar *pattern,
regex = g_regex_new (pattern, 0, 0, NULL); regex = g_regex_new (pattern, 0, 0, NULL);
num = g_regex_get_string_number (regex, name); num = g_regex_get_string_number (regex, name);
g_regex_free (regex); g_regex_unref (regex);
if (num != expected_num) if (num != expected_num)
{ {
@ -1336,7 +1380,7 @@ exit:
} }
g_match_info_free (match_info); g_match_info_free (match_info);
g_regex_free (regex); g_regex_unref (regex);
g_slist_foreach (expected, free_match, NULL); g_slist_foreach (expected, free_match, NULL);
g_slist_free (expected); g_slist_free (expected);
@ -1442,7 +1486,7 @@ exit:
} }
g_match_info_free (match_info); g_match_info_free (match_info);
g_regex_free (regex); g_regex_unref (regex);
g_slist_foreach (expected, free_match, NULL); g_slist_foreach (expected, free_match, NULL);
g_slist_free (expected); g_slist_free (expected);
@ -1852,6 +1896,17 @@ main (int argc, char *argv[])
TEST_SPLIT3(" *", "ab c", 0, 3, "a", "b", "c"); TEST_SPLIT3(" *", "ab c", 0, 3, "a", "b", "c");
TEST_SPLIT3(" *", "ab c", 0, 4, "a", "b", "c"); TEST_SPLIT3(" *", "ab c", 0, 4, "a", "b", "c");
/* TEST_CHECK_REPLACEMENT(string_to_expand, expected, expected_refs) */
TEST_CHECK_REPLACEMENT("", TRUE, FALSE);
TEST_CHECK_REPLACEMENT("a", TRUE, FALSE);
TEST_CHECK_REPLACEMENT("\\t\\n\\v\\r\\f\\a\\b\\\\\\x{61}", TRUE, FALSE);
TEST_CHECK_REPLACEMENT("\\0", TRUE, TRUE);
TEST_CHECK_REPLACEMENT("\\n\\2", TRUE, TRUE);
TEST_CHECK_REPLACEMENT("\\g<foo>", TRUE, TRUE);
/* Invalid strings */
TEST_CHECK_REPLACEMENT("\\Q", FALSE, FALSE);
TEST_CHECK_REPLACEMENT("x\\Ay", FALSE, FALSE);
/* TEST_EXPAND(pattern, string, string_to_expand, raw, expected) */ /* TEST_EXPAND(pattern, string, string_to_expand, raw, expected) */
TEST_EXPAND("a", "a", "", FALSE, ""); TEST_EXPAND("a", "a", "", FALSE, "");
TEST_EXPAND("a", "a", "\\0", FALSE, "a"); TEST_EXPAND("a", "a", "\\0", FALSE, "a");
@ -1918,6 +1973,12 @@ main (int argc, char *argv[])
TEST_EXPAND("", "", "\\", FALSE, NULL); TEST_EXPAND("", "", "\\", FALSE, NULL);
TEST_EXPAND("a", "a", "\\x{61", FALSE, NULL); TEST_EXPAND("a", "a", "\\x{61", FALSE, NULL);
TEST_EXPAND("a", "a", "\\x6X", FALSE, NULL); TEST_EXPAND("a", "a", "\\x6X", FALSE, NULL);
/* Pattern-less. */
TEST_EXPAND(NULL, NULL, "", FALSE, "");
TEST_EXPAND(NULL, NULL, "\\n", FALSE, "\n");
/* Invalid strings */
TEST_EXPAND(NULL, NULL, "\\Q", FALSE, NULL);
TEST_EXPAND(NULL, NULL, "x\\Ay", FALSE, NULL);
/* TEST_REPLACE(pattern, string, start_position, replacement, expected) */ /* TEST_REPLACE(pattern, string, start_position, replacement, expected) */
TEST_REPLACE("a", "ababa", 0, "A", "AbAbA"); TEST_REPLACE("a", "ababa", 0, "A", "AbAbA");