Improve c_strreplace implementation
Return successfuly if there is nothing to replace. Allow for replacement with superset pattern. Simplified example without fix: c_strreplace("ABCD", "A", "AX") -> "AAAAAA...AAAXBCD" Simplified example with fix: c_strreplace("ABCD", "A", "AX") -> "AXBCD" Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
This commit is contained in:
parent
390b9c1895
commit
54b3af4631
29
src/cmocka.c
29
src/cmocka.c
|
@ -468,32 +468,41 @@ static int c_strreplace(char *src,
|
|||
{
|
||||
char *p = NULL;
|
||||
|
||||
p = strstr(src, pattern);
|
||||
if (p == NULL) {
|
||||
// Terminate if there is no valid data
|
||||
if (src == NULL || src_len == 0 || pattern == NULL || repl == NULL) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
p = strstr(src, pattern);
|
||||
/* There is nothing to replace */
|
||||
if (p == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const size_t pattern_len = strlen(pattern);
|
||||
const size_t repl_len = strlen(repl);
|
||||
do {
|
||||
size_t of = p - src;
|
||||
size_t offset = p - src;
|
||||
size_t l = strlen(src);
|
||||
size_t pl = strlen(pattern);
|
||||
size_t rl = strlen(repl);
|
||||
|
||||
/* overflow check */
|
||||
if (src_len <= l + MAX(pl, rl) + 1) {
|
||||
if (src_len <= l + MAX(pattern_len, repl_len) + 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rl != pl) {
|
||||
memmove(src + of + rl, src + of + pl, l - of - pl + 1);
|
||||
if (repl_len != pattern_len) {
|
||||
memmove(src + offset + repl_len,
|
||||
src + offset + pattern_len,
|
||||
l - offset - pattern_len + 1);
|
||||
}
|
||||
|
||||
memcpy(src + of, repl, rl);
|
||||
memcpy(src + offset, repl, repl_len);
|
||||
|
||||
if (str_replaced != NULL) {
|
||||
*str_replaced = 1;
|
||||
}
|
||||
p = strstr(src, pattern);
|
||||
p = strstr(src + offset + repl_len, pattern);
|
||||
} while (p != NULL);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -46,6 +46,7 @@ set(CMOCKA_TESTS
|
|||
test_stop
|
||||
test_stop_fail
|
||||
test_strmatch
|
||||
test_strreplace
|
||||
test_setup_fail
|
||||
test_ordering
|
||||
test_ordering_fail
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* Copyright 2024 Jakub Czapiga <mordijc@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <cmocka.h>
|
||||
|
||||
#include "../src/cmocka.c"
|
||||
|
||||
static void test_strreplace_null(void **state)
|
||||
{
|
||||
int rc;
|
||||
char data[64] = "DATA";
|
||||
int out = 0;
|
||||
|
||||
(void)state;
|
||||
|
||||
rc = c_strreplace(NULL, sizeof(data), "A", "B", &out);
|
||||
assert_int_equal(rc, -1);
|
||||
assert_int_equal(out, 0);
|
||||
assert_int_equal(errno, EINVAL);
|
||||
|
||||
rc = c_strreplace(data, 0, "A", "B", &out);
|
||||
assert_int_equal(rc, -1);
|
||||
assert_int_equal(out, 0);
|
||||
assert_int_equal(errno, EINVAL);
|
||||
|
||||
rc = c_strreplace(data, sizeof(data), NULL, "B", &out);
|
||||
assert_int_equal(rc, -1);
|
||||
assert_int_equal(out, 0);
|
||||
assert_int_equal(errno, EINVAL);
|
||||
|
||||
rc = c_strreplace(data, sizeof(data), "A", NULL, &out);
|
||||
assert_int_equal(rc, -1);
|
||||
assert_int_equal(out, 0);
|
||||
assert_int_equal(errno, EINVAL);
|
||||
}
|
||||
|
||||
static void test_strreplace_no_pattern(void **state)
|
||||
{
|
||||
int rc;
|
||||
char data[64] = "DATA";
|
||||
int out = 0;
|
||||
|
||||
(void) state;
|
||||
|
||||
rc = c_strreplace(data, sizeof(data), "X", "Y", &out);
|
||||
assert_int_equal(rc, 0);
|
||||
assert_int_equal(out, 0);
|
||||
}
|
||||
|
||||
static void test_strreplace_patterns(void **state)
|
||||
{
|
||||
int rc;
|
||||
const char base_data[] = "THIS IS THE DATA";
|
||||
char data[64] = "THIS IS THE DATA";
|
||||
int out = 0;
|
||||
|
||||
(void) state;
|
||||
|
||||
// Simple character substitution
|
||||
rc = c_strreplace(data, sizeof(data), "T", "D", &out);
|
||||
assert_int_equal(rc, 0);
|
||||
assert_int_equal(out, 1);
|
||||
assert_string_equal(data, "DHIS IS DHE DADA");
|
||||
|
||||
// Reset data
|
||||
memcpy(data, base_data, sizeof(base_data));
|
||||
|
||||
// Superset pattern
|
||||
out = 0;
|
||||
rc = c_strreplace(data, sizeof(data), " IS", " ISN'T", &out);
|
||||
assert_int_equal(rc, 0);
|
||||
assert_int_equal(out, 1);
|
||||
assert_string_equal(data, "THIS ISN'T THE DATA");
|
||||
|
||||
// Subset pattern
|
||||
memcpy(data, base_data, sizeof(base_data));
|
||||
out = 0;
|
||||
rc = c_strreplace(data, sizeof(data), "THIS", "TIS", &out);
|
||||
assert_int_equal(rc, 0);
|
||||
assert_int_equal(out, 1);
|
||||
assert_string_equal(data, "TIS IS THE DATA");
|
||||
|
||||
// Partial replacement
|
||||
memcpy(data, base_data, sizeof(base_data));
|
||||
out = 0;
|
||||
rc = c_strreplace(data, sizeof(data), "THIS", "THOSE", &out);
|
||||
assert_int_equal(rc, 0);
|
||||
assert_int_equal(out, 1);
|
||||
assert_string_equal(data, "THOSE IS THE DATA");
|
||||
|
||||
// Outer extension
|
||||
memcpy(data, base_data, sizeof(base_data));
|
||||
out = 0;
|
||||
rc = c_strreplace(data, sizeof(data), "THE", "_THE_", &out);
|
||||
assert_int_equal(rc, 0);
|
||||
assert_int_equal(out, 1);
|
||||
assert_string_equal(data, "THIS IS _THE_ DATA");
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
const struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test(test_strreplace_null),
|
||||
cmocka_unit_test(test_strreplace_no_pattern),
|
||||
cmocka_unit_test(test_strreplace_patterns),
|
||||
};
|
||||
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
Loading…
Reference in New Issue