+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)
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
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
<!-- ##### 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
#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
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
/* 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,
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;
}
/**
+ * 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
*
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);
+ }
}
/**
/**
* 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().
* 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
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);
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);
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);
* 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
*/
}
/**
- * 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
*
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;
}
* 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().
* g_match_info_next (match_info, NULL);
* }
* g_match_info_free (match_info);
- * g_regex_free (regex);
+ * g_regex_unref (regex);
* }
* </programlisting></informalexample>
*
* 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().
* 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);
* 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
*
* 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
*
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;
}
}
static gboolean
-interpolate_replacement (const GRegex *regex,
- const GMatchInfo *match_info,
- const gchar *string,
+interpolate_replacement (const GMatchInfo *match_info,
GString *result,
gpointer data)
{
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
}
static gboolean
-literal_replacement (const GRegex *regex,
- const GMatchInfo *match_info,
- const gchar *string,
+literal_replacement (const GMatchInfo *match_info,
GString *result,
gpointer data)
{
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);
}
}
/**
+ * 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
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);
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);
{
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;
{
g_print ("failed \t(pattern: \"%s\", compile: %d)\n",
pattern, compile_opts);
- g_regex_free (regex);
+ g_regex_unref (regex);
return FALSE;
}
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;
}
{
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;
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);
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);
}
g_match_info_free (match_info);
- g_regex_free (regex);
+ g_regex_unref (regex);
verbose ("passed\n");
return TRUE;
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;
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);
{
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;
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);
{
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;
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);
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);
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);
}
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;
{
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;
{
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;
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)
{
}
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);
}
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);
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");
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");