From 808988b1f1c7508094a2171d4802fa44f9fa04bc Mon Sep 17 00:00:00 2001 From: Slava Zanko Date: Thu, 10 Jan 2013 22:44:14 +0300 Subject: [PATCH] Add lib/strutil/replace:str_replace_all() function. Signed-off-by: Slava Zanko --- configure.ac | 1 + lib/strutil.h | 2 + lib/strutil/Makefile.am | 1 + lib/strutil/replace.c | 117 ++++++++++++++++ tests/lib/Makefile.am | 2 +- tests/lib/strutil/Makefile.am | 16 +++ tests/lib/strutil/replace__str_replace_all.c | 199 +++++++++++++++++++++++++++ 7 files changed, 337 insertions(+), 1 deletion(-) create mode 100644 lib/strutil/replace.c create mode 100644 tests/lib/strutil/Makefile.am create mode 100644 tests/lib/strutil/replace__str_replace_all.c diff --git a/configure.ac b/configure.ac index 09332567f..6e2bc486f 100644 --- a/configure.ac +++ b/configure.ac @@ -643,6 +643,7 @@ tests/Makefile tests/lib/Makefile tests/lib/mcconfig/Makefile tests/lib/search/Makefile +tests/lib/strutil/Makefile tests/lib/vfs/Makefile tests/lib/widget/Makefile tests/src/Makefile diff --git a/lib/strutil.h b/lib/strutil.h index 833e7eda7..b015c6c4c 100644 --- a/lib/strutil.h +++ b/lib/strutil.h @@ -538,6 +538,8 @@ void str_msg_term_size (const char *text, int *lines, int *columns); char *strrstr_skip_count (const char *haystack, const char *needle, size_t skip_count); +char *str_replace_all (const char *haystack, const char *needle, const char *replacement); + /*** inline functions ****************************************************************************/ static inline void diff --git a/lib/strutil/Makefile.am b/lib/strutil/Makefile.am index cf907489e..fa09f31d8 100644 --- a/lib/strutil/Makefile.am +++ b/lib/strutil/Makefile.am @@ -1,6 +1,7 @@ noinst_LTLIBRARIES = libmcstrutil.la libmcstrutil_la_SOURCES = \ + replace.c \ strescape.c \ strutil8bit.c \ strutilascii.c \ diff --git a/lib/strutil/replace.c b/lib/strutil/replace.c new file mode 100644 index 000000000..6d1c56691 --- /dev/null +++ b/lib/strutil/replace.c @@ -0,0 +1,117 @@ +/* + Functions for replacing substrings in strings. + + Copyright (C) 2013 + The Free Software Foundation, Inc. + + Written by: + Slava Zanko , 2013; + + This file is part of the Midnight Commander. + + The Midnight Commander is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + The Midnight Commander 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#include "lib/strutil.h" +#include "lib/strescape.h" + +/*** global variables ****************************************************************************/ + +/*** file scope macro definitions ****************************************************************/ + +/*** file scope type declarations ****************************************************************/ + +/*** file scope variables ************************************************************************/ + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +static char * +str_ptr_array_join (GPtrArray * str_splints) +{ + GString *return_str; + guint i; + + return_str = g_string_sized_new (32); + for (i = 0; i < str_splints->len; i++) + g_string_append (return_str, g_ptr_array_index (str_splints, i)); + + return g_string_free (return_str, FALSE); +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ +/** + * Replace all substrings 'needle' in string 'haystack' by 'replacement'. + * If the 'needle' in the 'haystack' will be escaped by backslash, + * then this occurence isn't be replaced. + * + * @param haystack string contains substrings for replacement + * @param needle string for search + * @param replacement string for replace + * @return newly allocated string with replaced substrings + */ + +char * +str_replace_all (const char *haystack, const char *needle, const char *replacement) +{ + size_t needle_len; + GPtrArray *str_splints; + char *return_str; + + needle_len = strlen (needle); + + str_splints = g_ptr_array_new (); + + while (TRUE) + { + char *needle_in_str; + + needle_in_str = strstr (haystack, needle); + if (needle_in_str == NULL) + { + if (*haystack != '\0') + g_ptr_array_add (str_splints, g_strdup (haystack)); + break; + } + + if (strutils_is_char_escaped (haystack, needle_in_str)) + { + char *backslash = needle_in_str - 1; + + if (haystack != backslash) + g_ptr_array_add (str_splints, g_strndup (haystack, backslash - haystack)); + + g_ptr_array_add (str_splints, g_strndup (backslash + 1, needle_in_str - backslash)); + haystack = needle_in_str + 1; + continue; + } + if (needle_in_str - haystack > 0) + g_ptr_array_add (str_splints, g_strndup (haystack, needle_in_str - haystack)); + g_ptr_array_add (str_splints, g_strdup (replacement)); + haystack = needle_in_str + needle_len; + } + return_str = str_ptr_array_join (str_splints); + + g_ptr_array_foreach (str_splints, (GFunc) g_free, NULL); + g_ptr_array_free (str_splints, TRUE); + + return return_str; +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/tests/lib/Makefile.am b/tests/lib/Makefile.am index a2f38dbc8..f1ca1443e 100644 --- a/tests/lib/Makefile.am +++ b/tests/lib/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = . mcconfig search vfs widget +SUBDIRS = . mcconfig search strutil vfs widget AM_CPPFLAGS = $(GLIB_CFLAGS) -I$(top_srcdir) @CHECK_CFLAGS@ diff --git a/tests/lib/strutil/Makefile.am b/tests/lib/strutil/Makefile.am new file mode 100644 index 000000000..25c94a9a1 --- /dev/null +++ b/tests/lib/strutil/Makefile.am @@ -0,0 +1,16 @@ + +AM_CPPFLAGS = \ + $(GLIB_CFLAGS) \ + -I$(top_srcdir) \ + -I$(top_srcdir)/lib/search \ + @CHECK_CFLAGS@ + +LIBS = @CHECK_LIBS@ $(top_builddir)/lib/libmc.la + +TESTS = \ + replace__str_replace_all + +check_PROGRAMS = $(TESTS) + +replace__str_replace_all_SOURCES = \ + replace__str_replace_all.c diff --git a/tests/lib/strutil/replace__str_replace_all.c b/tests/lib/strutil/replace__str_replace_all.c new file mode 100644 index 000000000..2742879a2 --- /dev/null +++ b/tests/lib/strutil/replace__str_replace_all.c @@ -0,0 +1,199 @@ +/* + lib/strutil - tests for lib/strutil/replace:str_replace_all() function. + + Copyright (C) 2013 + The Free Software Foundation, Inc. + + Written by: + Slava Zanko , 2013 + + This file is part of the Midnight Commander. + + The Midnight Commander is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + The Midnight Commander 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#define TEST_SUITE_NAME "/lib/strutil" + +#include +#include + +#include "lib/global.h" +#include "lib/strutil.h" + +/* --------------------------------------------------------------------------------------------- */ + +/* @Before */ +static void +setup (void) +{ + str_init_strings (NULL); +} + +/* --------------------------------------------------------------------------------------------- */ + +/* @After */ +static void +teardown (void) +{ + str_uninit_strings (); +} + +/* --------------------------------------------------------------------------------------------- */ + +/* @DataSource("str_replace_all_test_ds") */ +/* *INDENT-OFF* */ +static const struct str_replace_all_test_ds +{ + const char *haystack; + const char *needle; + const char *replacement; + const char *expected_result; +} str_replace_all_test_ds[] = +{ + { + /*needle not found*/ + "needle not found", + "blablablabla", + "1234567890", + "needle not found", + }, + { + /* replacement is less rather that needle */ + "some string blablablabla string", + "blablablabla", + "1234", + "some string 1234 string", + }, + { + /* replacement is great rather that needle */ + "some string bla string", + "bla", + "1234567890", + "some string 1234567890 string", + }, + { + /* replace few substrings in a start of string */ + "blabla blabla string", + "blabla", + "111111111", + "111111111 111111111 string", + }, + { + /* replace few substrings in a middle of string */ + "some string blabla str blabla string", + "blabla", + "111111111", + "some string 111111111 str 111111111 string", + }, + { + /* replace few substrings in an end of string */ + "some string blabla str blabla", + "blabla", + "111111111", + "some string 111111111 str 111111111", + }, + { + /* escaped substring */ + "\\blabla blabla", + "blabla", + "111111111", + "blabla 111111111", + }, + { + /* escaped substring */ + "str \\blabla blabla", + "blabla", + "111111111", + "str blabla 111111111", + }, + { + /* escaped substring */ + "str \\\\\\blabla blabla", + "blabla", + "111111111", + "str \\\\blabla 111111111", + }, + { + /* double-escaped substring (actually non-escaped) */ + "\\\\blabla blabla", + "blabla", + "111111111", + "\\\\111111111 111111111", + }, + { + /* partial substring */ + "blablabla", + "blabla", + "111111111", + "111111111bla", + }, + { + /* special symbols */ + "bla bla", + "bla", + "111\t1 1\n1111", + "111\t1 1\n1111 111\t1 1\n1111", + }, +}; +/* *INDENT-ON* */ + +/* @Test(dataSource = "str_replace_all_test_ds") */ +/* *INDENT-OFF* */ +START_TEST (str_replace_all_test) +/* *INDENT-ON* */ +{ + /* given */ + char *actual_result; + const struct str_replace_all_test_ds *data = &str_replace_all_test_ds[_i]; + + /* when */ + actual_result = str_replace_all (data->haystack, data->needle, data->replacement); + + /* then */ + g_assert_cmpstr (actual_result, ==, data->expected_result); + g_free (actual_result); + +} +/* *INDENT-OFF* */ +END_TEST +/* *INDENT-ON* */ + +/* --------------------------------------------------------------------------------------------- */ + +int +main (void) +{ + int number_failed; + + Suite *s = suite_create (TEST_SUITE_NAME); + TCase *tc_core = tcase_create ("Core"); + SRunner *sr; + + tcase_add_checked_fixture (tc_core, setup, teardown); + + /* Add new tests here: *************** */ + tcase_add_loop_test (tc_core, str_replace_all_test, 0, + sizeof (str_replace_all_test_ds) / sizeof (str_replace_all_test_ds[0])); + /* *********************************** */ + + suite_add_tcase (s, tc_core); + sr = srunner_create (s); + srunner_set_log (sr, "replace__str_replace_all.log"); + srunner_run_all (sr, CK_NOFORK); + number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? 0 : 1; +} + +/* --------------------------------------------------------------------------------------------- */ -- 2.11.4.GIT