Merge branch '1882_esc_seq_in_replace_field'

* 1882_esc_seq_in_replace_field:
  Remove replace_prepare_t type and use defined constants for use just one type (int) in functions
  Despite of the fact that algorithm now ignores the absence of closing curly brace '}' (which probably isn't 100% correct), this should be checked in tests for replace_handle_esc_seq function, not process_escape_sequence - it is the replace_handle_esq_seq who decides whether it is an escape sequence or not.
  added tests for mc_search_regex__replace_handle_esc_seq() function
  str_isutf8() function now returns gboolean value
  Ticket #1882: PCRE search: escape sequence support in replacements, UTF8 support (just a flag for libPCRE)
This commit is contained in:
Slava Zanko 2011-07-08 19:39:35 +03:00
commit ba09144ce5
9 changed files with 472 additions and 32 deletions

View File

@ -598,6 +598,7 @@ if test x$enable_tests != xno; then
AC_CONFIG_FILES([
lib/tests/Makefile
lib/tests/mcconfig/Makefile
lib/tests/search/Makefile
lib/tests/vfs/Makefile
])

View File

@ -91,6 +91,7 @@ typedef struct mc_search_struct
off_t start_buffer;
/* some data for regexp */
int num_results;
gboolean is_utf8;
mc_search_matchinfo_t *regex_match_info;
GString *regex_buffer;
#ifdef SEARCH_TYPE_PCRE

View File

@ -5,7 +5,8 @@
Copyright (C) 2009 The Free Software Foundation, Inc.
Written by:
Slava Zanko <slavazanko@gmail.com>, 2009.
Slava Zanko <slavazanko@gmail.com>, 2009,2010,2011
Vitaliy Filippov <vitalif@yourcmc.ru>, 2011
This file is part of the Midnight Commander.
@ -42,6 +43,10 @@
/*** file scope macro definitions ****************************************************************/
#define REPLACE_PREPARE_T_NOTHING_SPECIAL -1
#define REPLACE_PREPARE_T_REPLACE_FLAG -2
#define REPLACE_PREPARE_T_ESCAPE_SEQ -3
/*** file scope type declarations ****************************************************************/
typedef enum
@ -53,6 +58,7 @@ typedef enum
REPLACE_T_LOW_TRANSFORM = 8
} replace_transform_type_t;
/*** file scope variables ************************************************************************/
/*** file scope functions ************************************************************************/
@ -374,6 +380,85 @@ mc_search_regex__get_token_by_num (const mc_search_t * lc_mc_search, gsize lc_in
/* --------------------------------------------------------------------------------------------- */
static gboolean
mc_search_regex__replace_handle_esc_seq (const GString * replace_str, const gsize current_pos,
gsize * skip_len, int *ret)
{
char *curr_str = &(replace_str->str[current_pos]);
char c = *(curr_str + 1);
if (replace_str->len > current_pos + 2)
{
if (c == '{')
{
for (*skip_len = 2; /* \{ */
current_pos + *skip_len < replace_str->len
&& *(curr_str + *skip_len) >= '0'
&& *(curr_str + *skip_len) <= '7'; (*skip_len)++);
if (current_pos + *skip_len < replace_str->len && *(curr_str + *skip_len) == '}')
{
(*skip_len)++;
*ret = REPLACE_PREPARE_T_ESCAPE_SEQ;
return FALSE;
}
else
{
*ret = REPLACE_PREPARE_T_NOTHING_SPECIAL;
return TRUE;
}
}
if (c == 'x')
{
*skip_len = 2; /* \x */
c = *(curr_str + 2);
if (c == '{')
{
for (*skip_len = 3; /* \x{ */
current_pos + *skip_len < replace_str->len
&& g_ascii_isxdigit ((guchar) * (curr_str + *skip_len)); (*skip_len)++);
if (current_pos + *skip_len < replace_str->len && *(curr_str + *skip_len) == '}')
{
(*skip_len)++;
*ret = REPLACE_PREPARE_T_ESCAPE_SEQ;
return FALSE;
}
else
{
*ret = REPLACE_PREPARE_T_NOTHING_SPECIAL;
return TRUE;
}
}
else if (!g_ascii_isxdigit ((guchar) c))
{
*skip_len = 2; /* \x without number behind */
*ret = REPLACE_PREPARE_T_NOTHING_SPECIAL;
return FALSE;
}
else
{
c = *(curr_str + 3);
if (!g_ascii_isxdigit ((guchar) c))
*skip_len = 3; /* \xH */
else
*skip_len = 4; /* \xHH */
*ret = REPLACE_PREPARE_T_ESCAPE_SEQ;
return FALSE;
}
}
}
if (strchr ("ntvbrfa", c) != NULL)
{
*skip_len = 2;
*ret = REPLACE_PREPARE_T_ESCAPE_SEQ;
return FALSE;
}
return TRUE;
}
/* --------------------------------------------------------------------------------------------- */
static int
mc_search_regex__process_replace_str (const GString * replace_str, const gsize current_pos,
gsize * skip_len, replace_transform_type_t * replace_flags)
@ -383,16 +468,17 @@ mc_search_regex__process_replace_str (const GString * replace_str, const gsize c
const char *curr_str = &(replace_str->str[current_pos]);
if (current_pos > replace_str->len)
return -1;
return REPLACE_PREPARE_T_NOTHING_SPECIAL;
*skip_len = 0;
if (*curr_str == '$' && *(curr_str + 1) == '{' && (*(curr_str + 2) & (char) 0xf0) == 0x30)
if ((*curr_str == '$') && (*(curr_str + 1) == '{') && ((*(curr_str + 2) & (char) 0xf0) == 0x30)
&& (replace_str->len > current_pos + 2))
{
if (strutils_is_char_escaped (replace_str->str, curr_str))
{
*skip_len = 1;
return -1;
return REPLACE_PREPARE_T_NOTHING_SPECIAL;
}
for (*skip_len = 0;
@ -400,34 +486,38 @@ mc_search_regex__process_replace_str (const GString * replace_str, const gsize c
&& (*(curr_str + 2 + *skip_len) & (char) 0xf0) == 0x30; (*skip_len)++);
if (*(curr_str + 2 + *skip_len) != '}')
return -1;
return REPLACE_PREPARE_T_NOTHING_SPECIAL;
tmp_str = g_strndup (curr_str + 2, *skip_len);
if (tmp_str == NULL)
return -1;
return REPLACE_PREPARE_T_NOTHING_SPECIAL;
ret = atoi (tmp_str);
g_free (tmp_str);
*skip_len += 3; /* ${} */
return ret;
return ret; /* capture buffer index >= 0 */
}
if (*curr_str == '\\')
if ((*curr_str == '\\') && (replace_str->len > current_pos + 1))
{
if (strutils_is_char_escaped (replace_str->str, curr_str))
{
*skip_len = 1;
return -1;
return REPLACE_PREPARE_T_NOTHING_SPECIAL;
}
if (g_ascii_isdigit (*(curr_str + 1)))
{
ret = g_ascii_digit_value (*(curr_str + 1));
ret = g_ascii_digit_value (*(curr_str + 1)); /* capture buffer index >= 0 */
*skip_len = 2; /* \\ and one digit */
return ret;
}
ret = -2;
if (!mc_search_regex__replace_handle_esc_seq (replace_str, current_pos, skip_len, &ret))
return ret;
ret = REPLACE_PREPARE_T_REPLACE_FLAG;
*skip_len += 2;
switch (*(curr_str + 1))
{
@ -449,13 +539,15 @@ mc_search_regex__process_replace_str (const GString * replace_str, const gsize c
*replace_flags = REPLACE_T_NO_TRANSFORM;
break;
default:
ret = -1;
ret = REPLACE_PREPARE_T_NOTHING_SPECIAL;
break;
}
}
return ret;
}
/* --------------------------------------------------------------------------------------------- */
static void
mc_search_regex__process_append_str (GString * dest_str, const char *from, gsize len,
replace_transform_type_t * replace_flags)
@ -517,7 +609,111 @@ mc_search_regex__process_append_str (GString * dest_str, const char *from, gsize
}
/* --------------------------------------------------------------------------------------------- */
static void
mc_search_regex__process_escape_sequence (GString * dest_str, const char *from, gsize len,
replace_transform_type_t * replace_flags,
gboolean is_utf8)
{
gsize i = 0;
unsigned int c = 0;
char b;
if (len == (gsize) (-1))
len = strlen (from);
if (len == 0)
return;
if (from[i] == '{')
i++;
if (i >= len)
return;
if (from[i] == 'x')
{
i++;
if (i < len && from[i] == '{')
i++;
for (; i < len; i++)
{
if (from[i] >= '0' && from[i] <= '9')
c = c * 16 + from[i] - '0';
else if (from[i] >= 'a' && from[i] <= 'f')
c = c * 16 + 10 + from[i] - 'a';
else if (from[i] >= 'A' && from[i] <= 'F')
c = c * 16 + 10 + from[i] - 'A';
else
break;
}
}
else if (from[i] >= '0' && from[i] <= '7')
for (; i < len && from[i] >= '0' && from[i] <= '7'; i++)
c = c * 8 + from[i] - '0';
else
{
switch (from[i])
{
case 'n':
c = '\n';
break;
case 't':
c = '\t';
break;
case 'v':
c = '\v';
break;
case 'b':
c = '\b';
break;
case 'r':
c = '\r';
break;
case 'f':
c = '\f';
break;
case 'a':
c = '\a';
break;
default:
mc_search_regex__process_append_str (dest_str, from, len, replace_flags);
return;
}
}
if (c < 0x80 || !is_utf8)
g_string_append_c (dest_str, (char) c);
else if (c < 0x800)
{
b = 0xC0 | (c >> 6);
g_string_append_c (dest_str, b);
b = 0x80 | (c & 0x3F);
g_string_append_c (dest_str, b);
}
else if (c < 0x10000)
{
b = 0xE0 | (c >> 12);
g_string_append_c (dest_str, b);
b = 0x80 | ((c >> 6) & 0x3F);
g_string_append_c (dest_str, b);
b = 0x80 | (c & 0x3F);
g_string_append_c (dest_str, b);
}
else if (c < 0x10FFFF)
{
b = 0xF0 | (c >> 16);
g_string_append_c (dest_str, b);
b = 0x80 | ((c >> 12) & 0x3F);
g_string_append_c (dest_str, b);
b = 0x80 | ((c >> 6) & 0x3F);
g_string_append_c (dest_str, b);
b = 0x80 | (c & 0x3F);
g_string_append_c (dest_str, b);
}
}
/* --------------------------------------------------------------------------------------------- */
/*** public functions ****************************************************************************/
/* --------------------------------------------------------------------------------------------- */
void
mc_search__cond_struct_new_init_regex (const char *charset, mc_search_t * lc_mc_search,
@ -525,10 +721,6 @@ mc_search__cond_struct_new_init_regex (const char *charset, mc_search_t * lc_mc_
{
#ifdef SEARCH_TYPE_GLIB
GError *error = NULL;
#else /* SEARCH_TYPE_GLIB */
const char *error;
int erroffset;
#endif /* SEARCH_TYPE_GLIB */
if (!lc_mc_search->is_case_sensitive)
{
@ -538,10 +730,9 @@ mc_search__cond_struct_new_init_regex (const char *charset, mc_search_t * lc_mc_
mc_search_cond->str = mc_search__cond_struct_new_regex_ci_str (charset, tmp);
g_string_free (tmp, TRUE);
}
#ifdef SEARCH_TYPE_GLIB
mc_search_cond->regex_handle =
g_regex_new (mc_search_cond->str->str, G_REGEX_OPTIMIZE | G_REGEX_RAW | G_REGEX_DOTALL, 0,
&error);
g_regex_new (mc_search_cond->str->str, G_REGEX_OPTIMIZE | G_REGEX_RAW | G_REGEX_DOTALL,
0, &error);
if (error != NULL)
{
@ -551,8 +742,30 @@ mc_search__cond_struct_new_init_regex (const char *charset, mc_search_t * lc_mc_
return;
}
#else /* SEARCH_TYPE_GLIB */
const char *error;
int erroffset;
int pcre_options = PCRE_EXTRA | PCRE_MULTILINE;
if (str_isutf8 (charset))
{
pcre_options |= PCRE_UTF8;
if (lc_mc_search->is_case_sensitive)
pcre_options |= PCRE_CASELESS;
}
else
{
if (!lc_mc_search->is_case_sensitive)
{
GString *tmp;
tmp = mc_search_cond->str;
mc_search_cond->str = mc_search__cond_struct_new_regex_ci_str (charset, tmp);
g_string_free (tmp, TRUE);
}
}
mc_search_cond->regex_handle =
pcre_compile (mc_search_cond->str->str, PCRE_EXTRA, &error, &erroffset, NULL);
pcre_compile (mc_search_cond->str->str, pcre_options, &error, &erroffset, NULL);
if (mc_search_cond->regex_handle == NULL)
{
lc_mc_search->error = MC_SEARCH_E_REGEX_COMPILE;
@ -572,6 +785,7 @@ mc_search__cond_struct_new_init_regex (const char *charset, mc_search_t * lc_mc_
}
}
#endif /* SEARCH_TYPE_GLIB */
lc_mc_search->is_utf8 = str_isutf8 (charset);
}
/* --------------------------------------------------------------------------------------------- */
@ -708,7 +922,7 @@ mc_search_regex_prepare_replace_str (mc_search_t * lc_mc_search, GString * repla
{
lc_index = mc_search_regex__process_replace_str (replace_str, loop, &len, &replace_flags);
if (lc_index == -1)
if (lc_index == REPLACE_PREPARE_T_NOTHING_SPECIAL)
{
if (len != 0)
{
@ -723,7 +937,7 @@ mc_search_regex_prepare_replace_str (mc_search_t * lc_mc_search, GString * repla
continue;
}
if (lc_index == -2)
if (lc_index == REPLACE_PREPARE_T_REPLACE_FLAG)
{
if (loop)
mc_search_regex__process_append_str (ret, prev_str,
@ -734,6 +948,21 @@ mc_search_regex_prepare_replace_str (mc_search_t * lc_mc_search, GString * repla
continue;
}
/* escape sequence */
if (lc_index == REPLACE_PREPARE_T_ESCAPE_SEQ)
{
mc_search_regex__process_append_str (ret, prev_str,
replace_str->str + loop - prev_str,
&replace_flags);
/* call process_escape_sequence without starting '\\' */
mc_search_regex__process_escape_sequence (ret, replace_str->str + loop + 1, len - 1,
&replace_flags, lc_mc_search->is_utf8);
prev_str = replace_str->str + loop + len;
loop += len - 1;
continue;
}
/* invalid capture buffer number */
if (lc_index > lc_mc_search->num_results)
{
g_string_free (ret, TRUE);

View File

@ -511,10 +511,10 @@ int str_key_collate (const char *t1, const char *t2, int case_sen);
*/
void str_release_key (char *key, int case_sen);
/* return 1 if codeset_name is utf8 or utf-8
/* return TRUE if codeset_name is utf8 or utf-8
* I
*/
int str_isutf8 (const char *codeset_name);
gboolean str_isutf8 (const char *codeset_name);
const char *str_detect_termencoding (void);

View File

@ -354,15 +354,10 @@ str_choose_str_functions (void)
}
}
int
gboolean
str_isutf8 (const char *codeset_name)
{
int result = 0;
if (str_test_encoding_class (codeset_name, str_utf8_encodings))
{
result = 1;
}
return result;
return (str_test_encoding_class (codeset_name, str_utf8_encodings) != 0);
}
void

View File

@ -1,4 +1,4 @@
SUBDIRS = . mcconfig vfs
SUBDIRS = . mcconfig search vfs
AM_CFLAGS = $(GLIB_CFLAGS) -I$(top_srcdir) @CHECK_CFLAGS@
LIBS=@CHECK_LIBS@ $(top_builddir)/lib/libmc.la

View File

@ -0,0 +1,14 @@
AM_CFLAGS = -I$(top_srcdir)/lib/search $(GLIB_CFLAGS) -I$(top_srcdir) @CHECK_CFLAGS@
LIBS=@CHECK_LIBS@ $(top_builddir)/lib/libmc.la
TESTS = \
regex_replace_esc_seq \
regex_process_escape_sequence
check_PROGRAMS = $(TESTS)
regex_replace_esc_seq_SOURCES = \
regex_replace_esc_seq.c
regex_process_escape_sequence_SOURCES = \
regex_process_escape_sequence.c

View File

@ -0,0 +1,82 @@
/* libmc - checks for processing esc sequences in replace string
Copyright (C) 2011 Free Software Foundation, Inc.
Written by:
Slava Zanko <slavazanko@gmail.com>, 2011
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License
as published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#define TEST_SUITE_NAME "lib/search/regex"
#include <check.h>
#include "regex.c" /* for testing static functions*/
/* --------------------------------------------------------------------------------------------- */
#define test_helper_valid_data(from, etalon, dest_str, replace_flags, utf) { \
dest_str = g_string_new(""); \
mc_search_regex__process_escape_sequence (dest_str, from, -1, &replace_flags, utf); \
fail_if (strcmp(dest_str->str, etalon), "dest_str(%s) != %s", dest_str->str, etalon); \
g_string_free(dest_str, TRUE); \
}
/* --------------------------------------------------------------------------------------------- */
START_TEST (test_regex_process_escape_sequence_valid)
{
GString *dest_str;
replace_transform_type_t replace_flags = REPLACE_T_NO_TRANSFORM;
test_helper_valid_data("{101}", "A", dest_str, replace_flags, FALSE);
test_helper_valid_data("x42", "B", dest_str, replace_flags, FALSE);
test_helper_valid_data("x{444}", "D", dest_str, replace_flags, FALSE);
test_helper_valid_data("x{444}", "ф", dest_str, replace_flags, TRUE);
test_helper_valid_data("n", "\n", dest_str, replace_flags, FALSE);
test_helper_valid_data("t", "\t", dest_str, replace_flags, FALSE);
test_helper_valid_data("v", "\v", dest_str, replace_flags, FALSE);
test_helper_valid_data("b", "\b", dest_str, replace_flags, FALSE);
test_helper_valid_data("r", "\r", dest_str, replace_flags, FALSE);
test_helper_valid_data("f", "\f", dest_str, replace_flags, FALSE);
test_helper_valid_data("a", "\a", dest_str, replace_flags, FALSE);
}
END_TEST
/* --------------------------------------------------------------------------------------------- */
int
main (void)
{
int number_failed;
Suite *s = suite_create (TEST_SUITE_NAME);
TCase *tc_core = tcase_create ("Core");
SRunner *sr;
/* Add new tests here: *************** */
tcase_add_test (tc_core, test_regex_process_escape_sequence_valid);
/* *********************************** */
suite_add_tcase (s, tc_core);
sr = srunner_create (s);
srunner_run_all (sr, CK_NORMAL);
number_failed = srunner_ntests_failed (sr);
srunner_free (sr);
return (number_failed == 0) ? 0 : 1;
}
/* --------------------------------------------------------------------------------------------- */

View File

@ -0,0 +1,118 @@
/* libmc - checks for processing esc sequences in replace string
Copyright (C) 2011 Free Software Foundation, Inc.
Written by:
Slava Zanko <slavazanko@gmail.com>, 2011
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License
as published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#define TEST_SUITE_NAME "lib/search/regex"
#include <check.h>
#include "regex.c" /* for testing static functions*/
/* --------------------------------------------------------------------------------------------- */
#define test_helper_check_valid_data( a, b, c, d, e, f ) \
{ \
fail_unless( a == b, "ret_value != %s", (b) ? "TRUE": "FALSE" ); \
fail_unless( c == d, "skip_len(%d) != %d", c, d ); \
if (f!=0) fail_unless( e == f, "ret(%d) != %d", e, f ); \
}
#define test_helper_handle_esc_seq( pos, r, skip, flag ) \
{ \
skip_len = 0;\
test_helper_check_valid_data(\
mc_search_regex__replace_handle_esc_seq( replace_str, pos, &skip_len, &ret ), r,\
skip_len, skip,\
ret, flag\
); \
}
/* --------------------------------------------------------------------------------------------- */
START_TEST (test_regex_replace_esc_seq_prepare_valid)
{
GString *replace_str;
gsize skip_len;
int ret;
replace_str = g_string_new("bla-bla\\{123}bla-bla\\xabc234 bla-bla\\x{456abcd}bla-bla\\xtre\\n\\t\\v\\b\\r\\f\\a");
test_helper_handle_esc_seq( 7, FALSE, 6, REPLACE_PREPARE_T_ESCAPE_SEQ ); /* \\{123} */
test_helper_handle_esc_seq( 20, FALSE, 4, REPLACE_PREPARE_T_ESCAPE_SEQ ); /* \\xab */
test_helper_handle_esc_seq( 36, FALSE, 11, REPLACE_PREPARE_T_ESCAPE_SEQ ); /* \\x{456abcd} */
test_helper_handle_esc_seq( 54, FALSE, 2, REPLACE_PREPARE_T_NOTHING_SPECIAL ); /* \\xtre */
test_helper_handle_esc_seq( 59, FALSE, 2, REPLACE_PREPARE_T_ESCAPE_SEQ ); /* \\n */
test_helper_handle_esc_seq( 61, FALSE, 2, REPLACE_PREPARE_T_ESCAPE_SEQ ); /* \\t */
test_helper_handle_esc_seq( 63, FALSE, 2, REPLACE_PREPARE_T_ESCAPE_SEQ ); /* \\v */
test_helper_handle_esc_seq( 65, FALSE, 2, REPLACE_PREPARE_T_ESCAPE_SEQ ); /* \\b */
test_helper_handle_esc_seq( 67, FALSE, 2, REPLACE_PREPARE_T_ESCAPE_SEQ ); /* \\r */
test_helper_handle_esc_seq( 69, FALSE, 2, REPLACE_PREPARE_T_ESCAPE_SEQ ); /* \\f */
test_helper_handle_esc_seq( 71, FALSE, 2, REPLACE_PREPARE_T_ESCAPE_SEQ ); /* \\a */
g_string_free(replace_str, TRUE);
}
END_TEST
/* --------------------------------------------------------------------------------------------- */
START_TEST (test_regex_replace_esc_seq_prepare_invalid)
{
GString *replace_str;
gsize skip_len;
int ret;
replace_str = g_string_new("\\{123 \\x{qwerty} \\12} \\x{456a-bcd}bla-bla\\satre");
test_helper_handle_esc_seq( 0, TRUE, 5, REPLACE_PREPARE_T_NOTHING_SPECIAL ); /* \\{123 */
test_helper_handle_esc_seq( 6, TRUE, 3, REPLACE_PREPARE_T_NOTHING_SPECIAL ); /* \\x{qwerty} */
test_helper_handle_esc_seq( 17, TRUE, 0, REPLACE_PREPARE_T_NOTHING_SPECIAL ); /* \\12} */
test_helper_handle_esc_seq( 22, TRUE, 7, REPLACE_PREPARE_T_NOTHING_SPECIAL ); /* \\x{456a-bcd} */
test_helper_handle_esc_seq( 41, TRUE, 0, REPLACE_PREPARE_T_NOTHING_SPECIAL ); /* \\satre */
g_string_free(replace_str, TRUE);
}
END_TEST
/* --------------------------------------------------------------------------------------------- */
int
main (void)
{
int number_failed;
Suite *s = suite_create (TEST_SUITE_NAME);
TCase *tc_core = tcase_create ("Core");
SRunner *sr;
/* Add new tests here: *************** */
tcase_add_test (tc_core, test_regex_replace_esc_seq_prepare_valid);
tcase_add_test (tc_core, test_regex_replace_esc_seq_prepare_invalid);
/* *********************************** */
suite_add_tcase (s, tc_core);
sr = srunner_create (s);
srunner_run_all (sr, CK_NORMAL);
number_failed = srunner_ntests_failed (sr);
srunner_free (sr);
return (number_failed == 0) ? 0 : 1;
}
/* --------------------------------------------------------------------------------------------- */