From e7c99e310a2af2f336503fe26416cb0c8095c1d6 Mon Sep 17 00:00:00 2001 From: Manu Garg Date: Wed, 24 May 2023 15:45:44 -0700 Subject: [PATCH] Improve str_replace and add tests for it (#158) --- src/Makefile | 6 +++++- src/pac_utils.h | 50 +++++++++++++++++++++++++++++++------------- src/pac_utils_test.c | 40 +++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 16 deletions(-) create mode 100644 src/pac_utils_test.c diff --git a/src/Makefile b/src/Makefile index af108901..18132c79 100644 --- a/src/Makefile +++ b/src/Makefile @@ -87,7 +87,11 @@ jsapi_buildstamp: spidermonkey/js/src spidermonkey/libjs.a: spidermonkey/js/src cd spidermonkey && SMCFLAGS="$(SHFLAGS) $(SMCFLAGS)" $(MAKE) jslib -pacparser.o: pacparser.c pac_utils.h pacparser.h jsapi_buildstamp +pac_utils_test: pac_utils_test.c pac_utils.h + $(CC) $(MAINT_CFLAGS) $(CFLAGS) $(SHFLAGS) pac_utils_test.c -o pac_utils_test -lm -L. -I. + +pacparser.o: pacparser.c pac_utils.h pacparser.h pac_utils_test jsapi_buildstamp + ./pac_utils_test $(CC) $(MAINT_CFLAGS) $(CFLAGS) $(SHFLAGS) -c pacparser.c -o pacparser.o touch pymod/pacparser_o_buildstamp diff --git a/src/pac_utils.h b/src/pac_utils.h index 1a70e5ad..d788a392 100644 --- a/src/pac_utils.h +++ b/src/pac_utils.h @@ -26,6 +26,9 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, // USA +#include +#include + static const char *pacUtils = "function dnsDomainIs(host, domain) {\n" " return (host.length >= domain.length &&\n" @@ -335,22 +338,37 @@ static const char *pacUtils = // You must free the result if result is non-NULL. -char *str_replace(const char *orig, char *rep, char *with) { - char *result; // the return string - char *ins; // the next insert point - char *tmp; // varies - int count; // number of replacements - int len_front; // distance between rep and end of last rep - int len_rep = strlen(rep); - int len_with = strlen(with); +char *str_replace(const char *orig, const char *rep, const char *with) { + if (orig == NULL || rep == NULL || with == NULL) { + return NULL; + } + + size_t len_orig = strnlen(orig, 1024); + size_t len_rep = strnlen(rep, 1024); + size_t len_with = strnlen(with, 1024); + + if (len_orig == 0 || len_rep == 0) { + char *result = malloc(len_orig + 1); + strcpy(result, orig); + return result; + } - // Get the count of replacements - ins = orig; - for (count = 0; (tmp = strstr(ins, rep)); ++count) { - ins = tmp + len_rep; + // Count replacements needed + int count; // number of replacements + char const *start = orig; + // Cursor moves through the string, looking for rep. + char const *cursor; + for (count = 0;; ++count) { + cursor = strstr(start, rep); + if (cursor == NULL) { + break; + } + start = cursor + len_rep; } - tmp = result = malloc(strlen(orig) + (len_with - len_rep) * count + 1); + char *tmp; + char *result; + tmp = result = malloc(len_orig + (len_with - len_rep) * count + 1); // first time through the loop, all the variable are set correctly // from here on, @@ -358,8 +376,10 @@ char *str_replace(const char *orig, char *rep, char *with) { // ins points to the next occurrence of rep in orig // orig points to the remainder of orig after "end of rep" while (count--) { - ins = strstr(orig, rep); - len_front = ins - orig; + const char *ins = strstr(orig, rep); + int len_front = (int)(ins - orig); // How far have we moved + // Into the tmp, copy everything until we reach the rep. + // and move tmp forward. tmp = strncpy(tmp, orig, len_front) + len_front; tmp = strcpy(tmp, with) + len_with; orig += len_front + len_rep; // move to next "end of rep" diff --git a/src/pac_utils_test.c b/src/pac_utils_test.c new file mode 100644 index 00000000..14ba2756 --- /dev/null +++ b/src/pac_utils_test.c @@ -0,0 +1,40 @@ +#include +#include +#include +#include +#include "pac_utils.h" + +#define STREQ(s1, s2) (strcmp((s1), (s2)) == 0) + +// Unit tests +int main() { + // Test case 1: Single replacement + assert(STREQ("Hello, universe!", str_replace("Hello, world!", "world", "universe"))); + + // Test case 2: Multiple replacements + assert(STREQ("one cat, two cat, red cat, blue cat", + str_replace("one fish, two fish, red fish, blue fish", "fish", "cat"))); + // Expected output: "one cat, two cat, red cat, blue cat" + + // Test case 3: No replacements + assert(STREQ("AI is amazing", str_replace("AI is amazing", "robot", "AI"))); + + // Test case 4: Empty original string + assert(STREQ("", str_replace("", "hello", "world"))); + + // Test case 5: Empty replacement string + assert(STREQ("Hello, world!", str_replace("Hello, world!", "", "universe"))); + + // Test case 6: Empty "with" string + assert(STREQ("Hello, !", str_replace("Hello, world!", "world", ""))); + + // Test case 7: Complex replacements + assert(STREQ("abcdeXYZcba", str_replace("abcdeedcba", "ed", "XYZ"))); + + // Test case 8: Null inputs + assert(str_replace(NULL, "hello", "world") == NULL); + assert(str_replace("Hello hello", NULL, "world") == NULL); + assert(str_replace("Hello hello", "hello", NULL) == NULL); + + return 0; +} \ No newline at end of file