commit - 5256e868ed7f0d40ccaf480bd903ef722df4a052
commit + ea03bfa556eeaf3f10a455e6b5f529257bee028b
blob - 8afb905eb12229c1215e032fca1f8fd5d3e47465
blob + 1e49819533239f64b0c2f0d005cdc4802c9cfd2d
--- include/lfstring.h
+++ include/lfstring.h
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
#include <string.h>
#include <stdlib.h>
+#include <ctype.h>
#include "lfstring.h"
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
#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[] = {
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();
}