diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt index 0f750c653..0a129adaa 100644 --- a/docs/reference/glib/glib-sections.txt +++ b/docs/reference/glib/glib-sections.txt @@ -981,6 +981,7 @@ g_regex_get_string_number g_regex_get_compile_flags g_regex_get_match_flags g_regex_escape_string +g_regex_escape_nul g_regex_match_simple g_regex_match g_regex_match_full diff --git a/glib/glib.symbols b/glib/glib.symbols index abe216a60..a0c902f5f 100644 --- a/glib/glib.symbols +++ b/glib/glib.symbols @@ -1352,6 +1352,7 @@ g_regex_get_string_number g_regex_get_compile_flags g_regex_get_match_flags g_regex_escape_string +g_regex_escape_nul g_regex_match_simple g_regex_match g_regex_match_full diff --git a/glib/gregex.c b/glib/gregex.c index 7603b5f4f..0e8a429ad 100644 --- a/glib/gregex.c +++ b/glib/gregex.c @@ -2832,6 +2832,74 @@ g_regex_check_replacement (const gchar *replacement, return TRUE; } +/** + * g_regex_escape_nul: + * @string: the string to escape + * @length: the length of @string + * + * Escapes the nul characters in @string to "\x00". It can be used + * to compile a regex with embedded nul characters. + * + * For completeness, @length can be -1 for a nul-terminated string. + * In this case the output string will be of course equal to @string. + * + * Returns: a newly-allocated escaped string + * + * Since: 2.30 + */ +gchar * +g_regex_escape_nul (const gchar *string, + gint length) +{ + GString *escaped; + const gchar *p, *piece_start, *end; + gint backslashes; + + g_return_val_if_fail (string != NULL, NULL); + + if (length < 0) + return g_strdup (string); + + end = string + length; + p = piece_start = string; + escaped = g_string_sized_new (length + 1); + + backslashes = 0; + while (p < end) + { + switch (*p) + { + case '\0': + if (p != piece_start) + { + /* copy the previous piece. */ + g_string_append_len (escaped, piece_start, p - piece_start); + } + if ((backslashes & 1) == 0) + g_string_append_c (escaped, '\\'); + g_string_append_c (escaped, 'x'); + g_string_append_c (escaped, '0'); + g_string_append_c (escaped, '0'); + piece_start = ++p; + backslashes = 0; + break; + case '\\': + backslashes++; + ++p; + break; + default: + backslashes = 0; + p = g_utf8_next_char (p); + break; + } + } + + if (piece_start < end) + g_string_append_len (escaped, piece_start, end - piece_start); + + return g_string_free (escaped, FALSE); +} + /** * g_regex_escape_string: * @string: (array length=length): the string to escape diff --git a/glib/gregex.h b/glib/gregex.h index c3fb753ab..666b48738 100644 --- a/glib/gregex.h +++ b/glib/gregex.h @@ -364,6 +364,8 @@ gint g_regex_get_string_number (const GRegex *regex, const gchar *name); gchar *g_regex_escape_string (const gchar *string, gint length); +gchar *g_regex_escape_nul (const gchar *string, + gint length); GRegexCompileFlags g_regex_get_compile_flags (const GRegex *regex); GRegexMatchFlags g_regex_get_match_flags (const GRegex *regex); diff --git a/glib/tests/regex.c b/glib/tests/regex.c index 9a8dbc247..7f7cc6582 100644 --- a/glib/tests/regex.c +++ b/glib/tests/regex.c @@ -1129,6 +1129,31 @@ test_escape (gconstpointer d) g_free (path); \ } +static void +test_escape_nul (gconstpointer d) +{ + const TestEscapeData *data = d; + gchar *escaped; + + escaped = g_regex_escape_nul (data->string, data->length); + + g_assert_cmpstr (escaped, ==, data->expected); + + g_free (escaped); +} + +#define TEST_ESCAPE_NUL(_string, _length, _expected) { \ + TestEscapeData *data; \ + gchar *path; \ + data = g_new0 (TestEscapeData, 1); \ + data->string = _string; \ + data->length = _length; \ + data->expected = _expected; \ + path = g_strdup_printf ("/regex/escape_nul/%d", ++total); \ + g_test_add_data_func (path, data, test_escape_nul); \ + g_free (path); \ +} + typedef struct { const gchar *pattern; const gchar *string; @@ -2574,6 +2599,23 @@ main (int argc, char *argv[]) TEST_GET_STRING_NUMBER("(?:a)(?P.)", "A", 1); TEST_GET_STRING_NUMBER("(?:a)(?P.)", "B", -1); + /* TEST_ESCAPE_NUL(string, length, expected) */ + TEST_ESCAPE_NUL("hello world", -1, "hello world"); + TEST_ESCAPE_NUL("hello\0world", -1, "hello"); + TEST_ESCAPE_NUL("\0world", -1, ""); + TEST_ESCAPE_NUL("hello world", 5, "hello"); + TEST_ESCAPE_NUL("hello.world", 11, "hello.world"); + TEST_ESCAPE_NUL("a(b\\b.$", 7, "a(b\\b.$"); + TEST_ESCAPE_NUL("hello\0", 6, "hello\\x00"); + TEST_ESCAPE_NUL("\0world", 6, "\\x00world"); + TEST_ESCAPE_NUL("\0\0", 2, "\\x00\\x00"); + TEST_ESCAPE_NUL("hello\0world", 11, "hello\\x00world"); + TEST_ESCAPE_NUL("hello\0world\0", 12, "hello\\x00world\\x00"); + TEST_ESCAPE_NUL("hello\\\0world", 12, "hello\\x00world"); + TEST_ESCAPE_NUL("hello\\\\\0world", 13, "hello\\\\\\x00world"); + TEST_ESCAPE_NUL("|()[]{}^$*+?.", 13, "|()[]{}^$*+?."); + TEST_ESCAPE_NUL("|()[]{}^$*+?.\\\\", 15, "|()[]{}^$*+?.\\\\"); + /* TEST_ESCAPE(string, length, expected) */ TEST_ESCAPE("hello world", -1, "hello world"); TEST_ESCAPE("hello world", 5, "hello");