Commit Diff


commit - 5256e868ed7f0d40ccaf480bd903ef722df4a052
commit + ea03bfa556eeaf3f10a455e6b5f529257bee028b
blob - 8afb905eb12229c1215e032fca1f8fd5d3e47465
blob + 1e49819533239f64b0c2f0d005cdc4802c9cfd2d
--- include/lfstring.h
+++ include/lfstring.h
@@ -7,4 +7,18 @@ int find_substrings(const char* haystack, const char* 
 
 char* substr(const char* str, size_t idx, size_t len);
 
+char *str_trim(const char *str);
+
+char *str_join(const char **strs, size_t count, const char *sep);
+
+int str_starts_with(const char *str, const char *prefix);
+
+int str_ends_with(const char *str, const char *suffix);
+
+char *str_replace(const char *str, const char *old, const char *new);
+
+char *str_to_upper(const char *str);
+
+char *str_to_lower(const char *str);
+
 #endif // LIBFLINT_H_STRING
blob - 2cb1d2572d1af0dac58eb454a6b5905d9111c579
blob + a39bd8b1795b4f994bca76be4054f8820f1fa1c0
--- src/string.c
+++ src/string.c
@@ -1,5 +1,6 @@
 #include <string.h>
 #include <stdlib.h>
+#include <ctype.h>
 
 #include "lfstring.h"
 
@@ -56,3 +57,130 @@ char* substr(const char* str, size_t idx, size_t len) 
 
    return substr;
 }
+
+char *str_trim(const char *str) {
+    const char *start = str;
+    while (*start && isspace((unsigned char)*start)) {
+        start++;
+    }
+    const char *end = str + strlen(str);
+    while (end > start && isspace((unsigned char)*(end - 1))) {
+        end--;
+    }
+    size_t len = end - start;
+    char *out = malloc(len + 1);
+    if (out == NULL) {
+        return NULL;
+    }
+    memcpy(out, start, len);
+    out[len] = '\0';
+    return out;
+}
+
+char *str_join(const char **strs, size_t count, const char *sep) {
+    if (count == 0) {
+        char *out = malloc(1);
+        if (out != NULL) {
+            out[0] = '\0';
+        }
+        return out;
+    }
+    size_t sep_len = strlen(sep);
+    size_t total = 0;
+    for (size_t i = 0; i < count; i++) {
+        total += strlen(strs[i]);
+    }
+    total += sep_len * (count - 1);
+
+    char *out = malloc(total + 1);
+    if (out == NULL) {
+        return NULL;
+    }
+    char *p = out;
+    for (size_t i = 0; i < count; i++) {
+        size_t len = strlen(strs[i]);
+        memcpy(p, strs[i], len);
+        p += len;
+        if (i < count - 1) {
+            memcpy(p, sep, sep_len);
+            p += sep_len;
+        }
+    }
+    *p = '\0';
+    return out;
+}
+
+int str_starts_with(const char *str, const char *prefix) {
+    return strncmp(str, prefix, strlen(prefix)) == 0;
+}
+
+int str_ends_with(const char *str, const char *suffix) {
+    size_t str_len = strlen(str);
+    size_t suf_len = strlen(suffix);
+    if (suf_len > str_len) {
+        return 0;
+    }
+    return strcmp(str + str_len - suf_len, suffix) == 0;
+}
+
+char *str_replace(const char *str, const char *old, const char *new) {
+    size_t old_len = strlen(old);
+    size_t new_len = strlen(new);
+    if (old_len == 0) {
+        return strdup(str);
+    }
+
+    /* count occurrences */
+    size_t count = 0;
+    const char *p = str;
+    while ((p = strstr(p, old)) != NULL) {
+        count++;
+        p += old_len;
+    }
+
+    size_t out_len = strlen(str) + count * (new_len - old_len);
+    char *out = malloc(out_len + 1);
+    if (out == NULL) {
+        return NULL;
+    }
+
+    char *w = out;
+    p = str;
+    while (*p) {
+        if (strncmp(p, old, old_len) == 0) {
+            memcpy(w, new, new_len);
+            w += new_len;
+            p += old_len;
+        } else {
+            *w++ = *p++;
+        }
+    }
+    *w = '\0';
+    return out;
+}
+
+char *str_to_upper(const char *str) {
+    size_t len = strlen(str);
+    char *out = malloc(len + 1);
+    if (out == NULL) {
+        return NULL;
+    }
+    for (size_t i = 0; i < len; i++) {
+        out[i] = toupper((unsigned char)str[i]);
+    }
+    out[len] = '\0';
+    return out;
+}
+
+char *str_to_lower(const char *str) {
+    size_t len = strlen(str);
+    char *out = malloc(len + 1);
+    if (out == NULL) {
+        return NULL;
+    }
+    for (size_t i = 0; i < len; i++) {
+        out[i] = tolower((unsigned char)str[i]);
+    }
+    out[len] = '\0';
+    return out;
+}
blob - ba720d22e8bd1104d7cf1fc2952c5df00d8f4b50
blob + 8e29b506d9e134285c55909bb2bf72a0651d9280
--- tests/test_string.c
+++ tests/test_string.c
@@ -5,6 +5,7 @@
 #include "lfstring.h"
 
 int main() {
+    /* existing find_substrings tests */
     const char *haystack =
             "Test one two one and also maybe two but not Gabe's least favorite number, which is not one.";
     const char *needles[] = {
@@ -48,5 +49,79 @@ int main() {
     free(subs);
     subs = NULL;
 
+    /* str_trim */
+    s = str_trim("  hello world  ");
+    ASSERT_STR_EQ(s, "hello world");
+    free(s);
+
+    s = str_trim("no_spaces");
+    ASSERT_STR_EQ(s, "no_spaces");
+    free(s);
+
+    s = str_trim("   ");
+    ASSERT_STR_EQ(s, "");
+    free(s);
+
+    s = str_trim("\t\n hi \r\n");
+    ASSERT_STR_EQ(s, "hi");
+    free(s);
+
+    /* str_join */
+    const char *parts[] = {"foo", "bar", "baz"};
+    s = str_join(parts, 3, ", ");
+    ASSERT_STR_EQ(s, "foo, bar, baz");
+    free(s);
+
+    s = str_join(parts, 1, ", ");
+    ASSERT_STR_EQ(s, "foo");
+    free(s);
+
+    s = str_join(parts, 0, ", ");
+    ASSERT_STR_EQ(s, "");
+    free(s);
+
+    s = str_join(parts, 3, "");
+    ASSERT_STR_EQ(s, "foobarbaz");
+    free(s);
+
+    /* str_starts_with */
+    ASSERT_TRUE(str_starts_with("hello world", "hello"));
+    ASSERT_TRUE(str_starts_with("hello", "hello"));
+    ASSERT_FALSE(str_starts_with("hello", "world"));
+    ASSERT_TRUE(str_starts_with("hello", ""));
+
+    /* str_ends_with */
+    ASSERT_TRUE(str_ends_with("hello world", "world"));
+    ASSERT_TRUE(str_ends_with("hello", "hello"));
+    ASSERT_FALSE(str_ends_with("hello", "world"));
+    ASSERT_TRUE(str_ends_with("hello", ""));
+    ASSERT_FALSE(str_ends_with("hi", "longer"));
+
+    /* str_replace */
+    s = str_replace("foo bar foo baz foo", "foo", "qux");
+    ASSERT_STR_EQ(s, "qux bar qux baz qux");
+    free(s);
+
+    s = str_replace("aaa", "a", "bb");
+    ASSERT_STR_EQ(s, "bbbbbb");
+    free(s);
+
+    s = str_replace("hello", "xyz", "abc");
+    ASSERT_STR_EQ(s, "hello");
+    free(s);
+
+    s = str_replace("abcabc", "abc", "");
+    ASSERT_STR_EQ(s, "");
+    free(s);
+
+    /* str_to_upper / str_to_lower */
+    s = str_to_upper("hello World 123");
+    ASSERT_STR_EQ(s, "HELLO WORLD 123");
+    free(s);
+
+    s = str_to_lower("HELLO World 123");
+    ASSERT_STR_EQ(s, "hello world 123");
+    free(s);
+
     TEST_REPORT();
 }