From 904c42896f3311b70cc36f9a58985a51c775e12b Mon Sep 17 00:00:00 2001 From: Yevgen Muntyan Date: Sun, 3 Jun 2007 05:48:17 +0000 Subject: [PATCH] New functions: g_regex_ref(), g_regex_unref() which replaces 2007-06-03 Yevgen Muntyan * 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 --- ChangeLog | 16 +++ docs/reference/glib/glib-sections.txt | 6 +- docs/reference/glib/tmpl/gregex.sgml | 15 ++- glib/glib.symbols | 6 +- glib/gregex.c | 240 ++++++++++++++++++++++++++-------- glib/gregex.h | 17 ++- tests/regex-test.c | 133 ++++++++++++++----- 7 files changed, 328 insertions(+), 105 deletions(-) diff --git a/ChangeLog b/ChangeLog index b4e6e83..8839d95 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +2007-06-03 Yevgen Muntyan + + * 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 * README.win32: Fix a typo. (#423708, Olivier Delhomme) diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt index 1af7da8..04a5542 100644 --- a/docs/reference/glib/glib-sections.txt +++ b/docs/reference/glib/glib-sections.txt @@ -873,7 +873,8 @@ GRegexMatchFlags GRegex GRegexEvalCallback g_regex_new -g_regex_free +g_regex_ref +g_regex_unref g_regex_get_pattern g_regex_get_max_backref g_regex_get_capture_count @@ -890,7 +891,10 @@ g_regex_split_full g_regex_replace g_regex_replace_literal g_regex_replace_eval +g_regex_check_replacement GMatchInfo +g_match_info_get_regex +g_match_info_get_string g_match_info_free g_match_info_next g_match_info_get_match_count diff --git a/docs/reference/glib/tmpl/gregex.sgml b/docs/reference/glib/tmpl/gregex.sgml index 718631e..6ec64c6 100644 --- a/docs/reference/glib/tmpl/gregex.sgml +++ b/docs/reference/glib/tmpl/gregex.sgml @@ -203,15 +203,16 @@ structure is opaque and its fields cannot be accessed directly. 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 -should append the replacement to @result. +It is called for each occurance of the pattern in the string passed +to g_regex_replace_eval(), and it should append the replacement to +@result. -@Param1: the #GRegex passed to g_regex_replace_eval() -@Param2: the #GMatchInfo generated by the match -@Param3: the string used to perform matches against -@Param4: a #GString containing the new string -@Param5: user data passed to g_regex_replace_eval() +@Param1: the #GMatchInfo generated by the match. Use g_match_info_get_regex() +and g_match_info_get_string() if you need the #GRegex or the matched +string. +@Param2: a #GString containing the new string +@Param3: user data passed to g_regex_replace_eval() @Returns: %FALSE to continue the replacement process, %TRUE to stop it @Since: 2.14 diff --git a/glib/glib.symbols b/glib/glib.symbols index 45c2388..9725f20 100644 --- a/glib/glib.symbols +++ b/glib/glib.symbols @@ -1427,7 +1427,8 @@ g_get_codeset #if IN_FILE(__G_REGEX_C__) g_regex_error_quark g_regex_new -g_regex_free +g_regex_ref +g_regex_unref g_regex_get_pattern g_regex_get_max_backref g_regex_get_capture_count @@ -1444,6 +1445,9 @@ g_regex_split_full g_regex_replace g_regex_replace_literal 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_next g_match_info_matches diff --git a/glib/gregex.c b/glib/gregex.c index 4c173e0..953cd26 100644 --- a/glib/gregex.c +++ b/glib/gregex.c @@ -98,13 +98,9 @@ struct _GRegex /* TRUE if ret is an error code, FALSE otherwise. */ #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; -static gboolean interpolate_replacement (const GRegex *regex, - const GMatchInfo *match_info, - const gchar *string, +static gboolean interpolation_list_needs_match (GList *list); +static gboolean interpolate_replacement (const GMatchInfo *match_info, GString *result, gpointer data); static GList *split_replacement (const gchar *replacement, @@ -195,7 +191,7 @@ match_info_new (const GRegex *regex, string_len = strlen (string); 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_len = string_len; match_info->matches = PCRE_ERROR_NOMATCH; @@ -223,6 +219,44 @@ match_info_new (const GRegex *regex, } /** + * 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: * @match_info: a #GMatchInfo * @@ -233,10 +267,13 @@ match_info_new (const GRegex *regex, void g_match_info_free (GMatchInfo *match_info) { - regex_unref (match_info->regex); - g_free (match_info->offsets); - g_free (match_info->workspace); - g_free (match_info); + if (match_info) + { + g_regex_unref (match_info->regex); + 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: - * @match_info: a #GMatchInfo + * @match_info: a #GMatchInfo or %NULL * @string_to_expand: the string to expand * @error: location to store the error occuring, or %NULL to ignore errors * * Returns a new string containing the text in @string_to_expand with - * references expanded. References refer to the last match done with - * @string against @regex and have the same syntax used by g_regex_replace(). + * references and escape sequences expanded. References refer to the last + * 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 * 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 * 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 * * Since: 2.14 @@ -430,7 +475,6 @@ g_match_info_expand_references (const GMatchInfo *match_info, GList *list; 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 (error == NULL || *error == NULL, NULL); @@ -441,9 +485,16 @@ g_match_info_expand_references (const GMatchInfo *match_info, 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)); - interpolate_replacement (match_info->regex, match_info, - match_info->string, result, list); + interpolate_replacement (match_info, result, list); g_list_foreach (list, (GFunc)free_interpolation_data, NULL); g_list_free (list); @@ -724,16 +775,38 @@ g_regex_error_quark (void) 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*) ®ex->ref_count); 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 *) ®ex->ref_count, -1) - 1 == 0) { g_free (regex->pattern); @@ -755,7 +828,8 @@ regex_unref (GRegex *regex) * Compiles the regular expression to an internal form, and does the initial * 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 */ @@ -874,23 +948,6 @@ g_regex_new (const gchar *pattern, } /** - * 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: * @regex: a #GRegex structure * @@ -988,7 +1045,7 @@ g_regex_match_simple (const gchar *pattern, if (!regex) return FALSE; result = g_regex_match_full (regex, string, -1, 0, match_options, NULL, NULL); - g_regex_free (regex); + g_regex_unref (regex); return result; } @@ -1006,7 +1063,9 @@ g_regex_match_simple (const gchar *pattern, * structures. * * 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 * 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_free (match_info); - * g_regex_free (regex); + * g_regex_unref (regex); * } * * @@ -1068,7 +1127,13 @@ g_regex_match (const GRegex *regex, * with any kind of lookbehind assertion, such as "\b". * * 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 * 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_free (match_info); - * g_regex_free (regex); + * g_regex_unref (regex); * if (error != NULL) * { * 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(). * * 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 * @@ -1203,7 +1270,9 @@ g_regex_match_all (const GRegex *regex, * with any kind of lookbehind assertion, such as "\b". * * 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 * @@ -1353,7 +1422,7 @@ g_regex_split_simple (const gchar *pattern, if (!regex) return NULL; result = g_regex_split_full (regex, string, -1, 0, match_options, 0, NULL); - g_regex_free (regex); + g_regex_unref (regex); return result; } @@ -1955,9 +2024,7 @@ string_append (GString *string, } static gboolean -interpolate_replacement (const GRegex *regex, - const GMatchInfo *match_info, - const gchar *string, +interpolate_replacement (const GMatchInfo *match_info, GString *result, gpointer data) { @@ -2004,6 +2071,28 @@ interpolate_replacement (const GRegex *regex, 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: * @regex: a #GRegex structure @@ -2108,9 +2197,7 @@ g_regex_replace (const GRegex *regex, } static gboolean -literal_replacement (const GRegex *regex, - const GMatchInfo *match_info, - const gchar *string, +literal_replacement (const GMatchInfo *match_info, GString *result, gpointer data) { @@ -2217,7 +2304,7 @@ g_regex_replace_eval (const GRegex *regex, g_string_append_len (result, string + 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]; g_match_info_next (match_info, &tmp_error); } @@ -2234,6 +2321,51 @@ g_regex_replace_eval (const GRegex *regex, } /** + * 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: * @string: the string to escape * @length: the length of @string, or -1 if @string is nul-terminated diff --git a/glib/gregex.h b/glib/gregex.h index 3807ece..7e09224 100644 --- a/glib/gregex.h +++ b/glib/gregex.h @@ -77,18 +77,17 @@ typedef enum typedef struct _GRegex GRegex; typedef struct _GMatchInfo GMatchInfo; -typedef gboolean (*GRegexEvalCallback) (const GRegex *, - const GMatchInfo *, - const gchar *, - GString *, - gpointer); +typedef gboolean (*GRegexEvalCallback) (const GMatchInfo *match_info, + GString *result, + gpointer user_data); GRegex *g_regex_new (const gchar *pattern, GRegexCompileFlags compile_options, GRegexMatchFlags match_options, 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); gint g_regex_get_max_backref (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, gpointer user_data, GError **error); +gboolean g_regex_check_replacement (const gchar *replacement, + gboolean *has_references, + GError **error); /* 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); gboolean g_match_info_next (GMatchInfo *match_info, GError **error); diff --git a/tests/regex-test.c b/tests/regex-test.c index 9a18e76..111ca58 100644 --- a/tests/regex-test.c +++ b/tests/regex-test.c @@ -112,13 +112,11 @@ test_new (const gchar *pattern, { g_print ("failed \t(pattern: \"%s\")\n", pattern); - g_regex_free (regex); + g_regex_unref (regex); return FALSE; } - g_regex_free (regex); - /* Free a null string. */ - g_regex_free (NULL); + g_regex_unref (regex); verbose ("passed\n"); return TRUE; @@ -146,7 +144,7 @@ test_new_fail (const gchar *pattern, { g_print ("failed \t(pattern: \"%s\", compile: %d)\n", pattern, compile_opts); - g_regex_free (regex); + g_regex_unref (regex); 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_free (e1); g_free (e2); - g_regex_free (regex); + g_regex_unref (regex); return FALSE; } @@ -231,12 +229,12 @@ test_match (const gchar *pattern, { g_print ("failed \t(pattern: \"%s\", string: \"%s\")\n", pattern, string); - g_regex_free (regex); + g_regex_unref (regex); return FALSE; } } - g_regex_free (regex); + g_regex_unref (regex); verbose ("passed (%s)\n", match ? "match" : "nomatch"); return TRUE; @@ -319,6 +317,8 @@ test_match_next (const gchar *pattern, matches = g_slist_prepend (matches, match); 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); matches = g_slist_reverse (matches); @@ -366,7 +366,7 @@ exit: 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_free (expected); g_slist_foreach (matches, free_match, NULL); @@ -450,7 +450,7 @@ test_match_count (const gchar *pattern, } g_match_info_free (match_info); - g_regex_free (regex); + g_regex_unref (regex); verbose ("passed\n"); return TRUE; @@ -481,26 +481,26 @@ test_partial (const gchar *pattern, if (expected != g_match_info_is_partial_match (match_info)) { g_print ("failed \t(got %d, expected: %d)\n", !expected, expected); - g_regex_free (regex); + g_regex_unref (regex); return FALSE; } if (expected && g_match_info_fetch_pos (match_info, 0, NULL, NULL)) { g_print ("failed \t(got sub-pattern 0)\n"); - g_regex_free (regex); + g_regex_unref (regex); return FALSE; } if (expected && g_match_info_fetch_pos (match_info, 1, NULL, NULL)) { g_print ("failed \t(got sub-pattern 1)\n"); - g_regex_free (regex); + g_regex_unref (regex); return FALSE; } g_match_info_free (match_info); - g_regex_free (regex); + g_regex_unref (regex); verbose ("passed\n"); return TRUE; @@ -540,7 +540,7 @@ test_sub_pattern (const gchar *pattern, g_print ("failed \t(got \"%s\", expected \"%s\")\n", sub_expr, expected_sub); g_free (sub_expr); - g_regex_free (regex); + g_regex_unref (regex); return FALSE; } 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", start, end, expected_start, expected_end); - g_regex_free (regex); + g_regex_unref (regex); return FALSE; } g_match_info_free (match_info); - g_regex_free (regex); + g_regex_unref (regex); verbose ("passed\n"); return TRUE; @@ -598,7 +598,7 @@ test_named_sub_pattern (const gchar *pattern, g_print ("failed \t(got \"%s\", expected \"%s\")\n", sub_expr, expected_sub); g_free (sub_expr); - g_regex_free (regex); + g_regex_unref (regex); return FALSE; } 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", start, end, expected_start, expected_end); - g_regex_free (regex); + g_regex_unref (regex); return FALSE; } g_match_info_free (match_info); - g_regex_free (regex); + g_regex_unref (regex); verbose ("passed\n"); return TRUE; @@ -704,7 +704,7 @@ test_fetch_all (const gchar *pattern, exit: g_match_info_free (match_info); - g_regex_free (regex); + g_regex_unref (regex); g_slist_foreach (expected, (GFunc)g_free, NULL); g_slist_free (expected); g_strfreev (matches); @@ -907,7 +907,7 @@ test_split_full (const gchar *pattern, token_count == 1 ? "token" : "tokens"); exit: - g_regex_free (regex); + g_regex_unref (regex); g_slist_foreach (expected, (GFunc)g_free, NULL); g_slist_free (expected); g_strfreev (tokens); @@ -976,7 +976,7 @@ test_split (const gchar *pattern, token_count == 1 ? "token" : "tokens"); exit: - g_regex_free (regex); + g_regex_unref (regex); g_slist_foreach (expected, (GFunc)g_free, NULL); g_slist_free (expected); g_strfreev (tokens); @@ -1049,33 +1049,77 @@ 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 test_expand (const gchar *pattern, const gchar *string, const gchar *string_to_expand, gboolean raw, const gchar *expected) { - GRegex *regex; - GMatchInfo *match_info; + GRegex *regex = NULL; + GMatchInfo *match_info = NULL; gchar *res; verbose ("expanding the references in \"%s\" (pattern: \"%s\", string: \"%s\") \t", string_to_expand, pattern, string); - regex = g_regex_new (pattern, raw ? G_REGEX_RAW : 0, 0, NULL); - g_regex_match (regex, string, 0, &match_info); + if (pattern) + { + 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); if (!streq (res, expected)) { g_print ("failed \t(got \"%s\", expected \"%s\")\n", res, expected); g_free (res); - g_regex_free (regex); + g_match_info_free (match_info); + g_regex_unref (regex); return FALSE; } g_free (res); g_match_info_free (match_info); - g_regex_free (regex); + if (regex) + g_regex_unref (regex); verbose ("passed\n"); return TRUE; @@ -1108,12 +1152,12 @@ test_replace (const gchar *pattern, { g_print ("failed \t(got \"%s\", expected \"%s\")\n", res, expected); g_free (res); - g_regex_free (regex); + g_regex_unref (regex); return FALSE; } g_free (res); - g_regex_free (regex); + g_regex_unref (regex); verbose ("passed\n"); return TRUE; @@ -1147,12 +1191,12 @@ test_replace_lit (const gchar *pattern, { g_print ("failed \t(got \"%s\", expected \"%s\")\n", res, expected); g_free (res); - g_regex_free (regex); + g_regex_unref (regex); return FALSE; } g_free (res); - g_regex_free (regex); + g_regex_unref (regex); verbose ("passed\n"); return TRUE; @@ -1179,7 +1223,7 @@ test_get_string_number (const gchar *pattern, regex = g_regex_new (pattern, 0, 0, NULL); num = g_regex_get_string_number (regex, name); - g_regex_free (regex); + g_regex_unref (regex); if (num != expected_num) { @@ -1336,7 +1380,7 @@ exit: } g_match_info_free (match_info); - g_regex_free (regex); + g_regex_unref (regex); g_slist_foreach (expected, free_match, NULL); g_slist_free (expected); @@ -1442,7 +1486,7 @@ exit: } g_match_info_free (match_info); - g_regex_free (regex); + g_regex_unref (regex); g_slist_foreach (expected, free_match, NULL); 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, 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", 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("a", "a", "", FALSE, ""); TEST_EXPAND("a", "a", "\\0", FALSE, "a"); @@ -1918,6 +1973,12 @@ main (int argc, char *argv[]) TEST_EXPAND("", "", "\\", FALSE, NULL); TEST_EXPAND("a", "a", "\\x{61", 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("a", "ababa", 0, "A", "AbAbA"); -- 2.7.4