New functions: g_regex_ref(), g_regex_unref() which replaces
authorYevgen Muntyan <muntyan@tamu.edu>
Sun, 3 Jun 2007 05:48:17 +0000 (05:48 +0000)
committerYevgen Muntyan <muntyan@src.gnome.org>
Sun, 3 Jun 2007 05:48:17 +0000 (05:48 +0000)
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

ChangeLog
docs/reference/glib/glib-sections.txt
docs/reference/glib/tmpl/gregex.sgml
glib/glib.symbols
glib/gregex.c
glib/gregex.h
tests/regex-test.c

index b4e6e83..8839d95 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -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>
 
        * README.win32: Fix a typo.  (#423708, Olivier Delhomme)
index 1af7da8..04a5542 100644 (file)
@@ -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
index 718631e..6ec64c6 100644 (file)
@@ -203,15 +203,16 @@ structure is opaque and its fields cannot be accessed directly.
 <!-- ##### USER_FUNCTION GRegexEvalCallback ##### -->
 <para>
 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.
 </para>
 
-@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
 
index 45c2388..9725f20 100644 (file)
@@ -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
index 4c173e0..953cd26 100644 (file)
@@ -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*) &regex->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 *) &regex->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);
  * }
  * </programlisting></informalexample>
  *
@@ -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
index 3807ece..7e09224 100644 (file)
@@ -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);
index 9a18e76..111ca58 100644 (file)
@@ -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<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("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");