diff --git a/glib/gregex.c b/glib/gregex.c index 50abeee89..41ad675a7 100644 --- a/glib/gregex.c +++ b/glib/gregex.c @@ -444,8 +444,25 @@ get_pcre2_bsr_match_options (GRegexMatchFlags match_flags) return 0; } +static char * +get_pcre2_error_string (int errcode) +{ + PCRE2_UCHAR8 error_msg[2048]; + int err_length; + + err_length = pcre2_get_error_message (errcode, error_msg, + G_N_ELEMENTS (error_msg)); + + if (err_length <= 0) + return NULL; + + /* The array is always filled with a trailing zero */ + g_assert ((size_t) err_length < G_N_ELEMENTS (error_msg)); + return g_memdup2 (error_msg, err_length + 1); +} + static const gchar * -match_error (gint errcode) +translate_match_error (gint errcode) { switch (errcode) { @@ -454,7 +471,7 @@ match_error (gint errcode) break; case PCRE2_ERROR_NULL: /* NULL argument, this should not happen in GRegex */ - g_warning ("A NULL argument was passed to PCRE"); + g_critical ("A NULL argument was passed to PCRE"); break; case PCRE2_ERROR_BADOPTION: return "bad options"; @@ -499,7 +516,24 @@ match_error (gint errcode) default: break; } - return _("unknown error"); + return NULL; +} + +static char * +get_match_error_message (int errcode) +{ + const char *msg = translate_match_error (errcode); + char *error_string; + + if (msg) + return g_strdup (msg); + + error_string = get_pcre2_error_string (errcode); + + if (error_string) + return error_string; + + return g_strdup (_("unknown error")); } static void @@ -731,7 +765,6 @@ translate_compile_error (gint *errcode, const gchar **errmsg) case PCRE2_ERROR_INTERNAL_BAD_CODE: case PCRE2_ERROR_INTERNAL_BAD_CODE_IN_SKIP: *errcode = G_REGEX_ERROR_INTERNAL; - *errmsg = _("internal error"); break; case PCRE2_ERROR_INVALID_SUBPATTERN_NAME: case PCRE2_ERROR_CLASS_INVALID_RANGE: @@ -760,12 +793,10 @@ translate_compile_error (gint *errcode, const gchar **errmsg) case PCRE2_ERROR_BAD_LITERAL_OPTIONS: default: *errcode = G_REGEX_ERROR_COMPILE; - *errmsg = _("internal error"); break; } g_assert (*errcode != -1); - g_assert (*errmsg != NULL); } /* GMatchInfo */ @@ -1084,9 +1115,12 @@ g_match_info_next (GMatchInfo *match_info, if (IS_PCRE2_ERROR (match_info->matches)) { + gchar *error_msg = get_match_error_message (match_info->matches); + g_set_error (error, G_REGEX_ERROR, G_REGEX_ERROR_MATCH, _("Error while matching regular expression %s: %s"), - match_info->regex->pattern, match_error (match_info->matches)); + match_info->regex->pattern, error_msg); + g_clear_pointer (&error_msg, g_free); return FALSE; } else if (match_info->matches == 0) @@ -1791,11 +1825,20 @@ regex_compile (const gchar *pattern, { GError *tmp_error; gchar *offset_str; + gchar *pcre2_errmsg = NULL; + int original_errcode; /* Translate the PCRE error code to GRegexError and use a translated * error message if possible */ + original_errcode = errcode; translate_compile_error (&errcode, &errmsg); + if (!errmsg) + { + errmsg = _("unknown error"); + pcre2_errmsg = get_pcre2_error_string (original_errcode); + } + /* PCRE uses byte offsets but we want to show character offsets */ erroffset = g_utf8_pointer_to_offset (pattern, &pattern[erroffset]); @@ -1803,9 +1846,11 @@ regex_compile (const gchar *pattern, tmp_error = g_error_new (G_REGEX_ERROR, errcode, _("Error while compiling regular expression ā€˜%sā€™ " "at char %s: %s"), - pattern, offset_str, errmsg); + pattern, offset_str, + pcre2_errmsg ? pcre2_errmsg : errmsg); g_propagate_error (error, tmp_error); g_free (offset_str); + g_clear_pointer (&pcre2_errmsg, g_free); return NULL; } @@ -2393,9 +2438,12 @@ g_regex_match_all_full (const GRegex *regex, } else if (IS_PCRE2_ERROR (info->matches)) { + gchar *error_msg = get_match_error_message (info->matches); + g_set_error (error, G_REGEX_ERROR, G_REGEX_ERROR_MATCH, _("Error while matching regular expression %s: %s"), - regex->pattern, match_error (info->matches)); + regex->pattern, error_msg); + g_clear_pointer (&error_msg, g_free); } else if (info->matches != PCRE2_ERROR_NOMATCH) { diff --git a/glib/tests/regex.c b/glib/tests/regex.c index f2e1a04ad..847676e1b 100644 --- a/glib/tests/regex.c +++ b/glib/tests/regex.c @@ -2564,6 +2564,7 @@ G_GNUC_END_IGNORE_DEPRECATIONS TEST_NEW_FAIL ("[a-z", 0, G_REGEX_ERROR_UNTERMINATED_CHARACTER_CLASS); TEST_NEW_FAIL ("[\\B]", 0, G_REGEX_ERROR_INVALID_ESCAPE_IN_CHARACTER_CLASS); TEST_NEW_FAIL ("[z-a]", 0, G_REGEX_ERROR_RANGE_OUT_OF_ORDER); + TEST_NEW_FAIL ("^[[:alnum:]-_.]+$", 0, G_REGEX_ERROR_COMPILE); TEST_NEW_FAIL ("{2,4}", 0, G_REGEX_ERROR_NOTHING_TO_REPEAT); TEST_NEW_FAIL ("a(?u)", 0, G_REGEX_ERROR_UNRECOGNIZED_CHARACTER); TEST_NEW_FAIL ("a(?<$foo)bar", 0, G_REGEX_ERROR_MISSING_SUBPATTERN_NAME); @@ -2640,6 +2641,7 @@ G_GNUC_END_IGNORE_DEPRECATIONS TEST_MATCH_SIMPLE("a", "a", G_REGEX_CASELESS, 0, TRUE); TEST_MATCH_SIMPLE("a", "A", G_REGEX_CASELESS, 0, TRUE); TEST_MATCH_SIMPLE("\\C\\C", "ab", G_REGEX_OPTIMIZE | G_REGEX_RAW, 0, TRUE); + TEST_MATCH_SIMPLE("^[[:alnum:]\\-_.]+$", "admin-foo", 0, 0, TRUE); /* These are needed to test extended properties. */ TEST_MATCH_SIMPLE(AGRAVE, AGRAVE, G_REGEX_CASELESS, 0, TRUE); TEST_MATCH_SIMPLE(AGRAVE, AGRAVE_UPPER, G_REGEX_CASELESS, 0, TRUE);