#include #include #include #include "c_ArrayList.h" // Reverses a string in-place void c_StrUtil_Reverse(char* str) { if (!str) return; const c_size_t len = strlen(str); if (len <= 1) return; c_size_t i = 0; c_size_t j = len - 1; while (i < j) { char temp = str[i]; str[i] = str[j]; str[j] = temp; i++; j--; } } // Converts a string to lowercase into a destination buffer c_err_t c_StrUtil_ToLower(char* out_dest, const char* src, c_size_t dest_capacity) { if (!out_dest || !src || dest_capacity == 0) return C_ERR_PARAM; c_size_t i = 0; while (src[i] != '\0') { if (i >= dest_capacity - 1) { out_dest[i] = '\0'; return C_ERR_INDEX; } out_dest[i] = (char)tolower((unsigned char)src[i]); i++; } out_dest[i] = '\0'; return C_ERR_SUCCESS; } // Converts a string to uppercase into a destination buffer c_err_t c_StrUtil_ToUpper(char* out_dest, const char* src, c_size_t dest_capacity) { if (!out_dest || !src || dest_capacity == 0) return C_ERR_PARAM; c_size_t i = 0; while (src[i] != '\0') { if (i >= dest_capacity - 1) { out_dest[i] = '\0'; return C_ERR_INDEX; } out_dest[i] = (char)toupper((unsigned char)src[i]); i++; } out_dest[i] = '\0'; return C_ERR_SUCCESS; } // Removes leading and trailing whitespaces into a destination buffer c_err_t c_StrUtil_Trim(char* out_dest, const char* src, c_size_t dest_capacity) { if (!out_dest || !src || dest_capacity == 0) return C_ERR_PARAM; // Find first non-whitespace character while (*src && isspace((unsigned char)*src)) { src++; } // Find the end of the string c_size_t len = strlen(src); while (len > 0 && isspace((unsigned char)src[len - 1])) { len--; } if (len >= dest_capacity) { return C_ERR_INDEX; } // Copy trimmed substring memmove(out_dest, src, len); out_dest[len] = '\0'; return C_ERR_SUCCESS; } c_bool_t c_StrUtil_StartsWith(const char* str, const char* prefix) { if (!str || !prefix) return C_FALSE; return strncmp(str, prefix, strlen(prefix)) == 0 ? C_TRUE : C_FALSE; } c_bool_t c_StrUtil_EndsWith(const char* str, const char* suffix) { if (!str || !suffix) return C_FALSE; c_size_t str_len = strlen(str); c_size_t suf_len = strlen(suffix); if (suf_len > str_len) return C_FALSE; return strcmp(str + str_len - suf_len, suffix) == 0 ? C_TRUE : C_FALSE; } // Safely extracts a substring with full bounds verification c_err_t c_StrUtil_Substring(char* out_dest, const char* src, c_size_t start, c_size_t len, c_size_t dest_capacity) { if (!out_dest || !src || dest_capacity == 0) return C_ERR_PARAM; c_size_t src_len = strlen(src); if (start > src_len) return C_ERR_INDEX; // Adjust requested length if it overflows source string boundaries if (start + len > src_len) { len = src_len - start; } if (len >= dest_capacity) { return C_ERR_INDEX; } memcpy(out_dest, src + start, len); out_dest[len] = '\0'; return C_ERR_SUCCESS; } // Replaces occurrences of a substring inside a buffer safely c_err_t c_StrUtil_Replace(char* out_dest, const char* src, const char* find, const char* replace_with, c_size_t dest_capacity) { if (!out_dest || !src || !find || !replace_with || dest_capacity == 0) return C_ERR_PARAM; c_size_t find_len = strlen(find); c_size_t replace_len = strlen(replace_with); c_size_t dest_len = 0; if (find_len == 0) return C_ERR_PARAM; while (*src) { // If match found, inject replacement string if (strncmp(src, find, find_len) == 0) { if (dest_len + replace_len >= dest_capacity) { out_dest[dest_len] = '\0'; return C_ERR_INDEX; } strcpy(out_dest + dest_len, replace_with); dest_len += replace_len; src += find_len; } else { // Otherwise, inject individual source character if (dest_len + 1 >= dest_capacity) { out_dest[dest_len] = '\0'; return C_ERR_INDEX; } out_dest[dest_len] = *src; dest_len++; src++; } } out_dest[dest_len] = '\0'; return C_ERR_SUCCESS; } c_err_t c_StrUtil_Split(c_ArrayList_t* out_list, const char* src, const char* delimiter) { if (!out_list || !src || !delimiter) return C_ERR_PARAM; if (out_list->obj_size != sizeof(c_StrToken_t)) return C_ERR_PARAM; // Type safety assert const c_size_t delim_len = strlen(delimiter); if (delim_len == 0) return C_ERR_PARAM; const char* current = src; const char* next_match; while ((next_match = strstr(current, delimiter)) != NULL) { size_t token_len = next_match - current; if (token_len > 0) { c_StrToken_t new_token; // Bound checking to ensure long strings don't cause buffer overflows size_t copy_len = (token_len >= C_STR_TOKEN_MAX_LEN) ? (C_STR_TOKEN_MAX_LEN - 1) : token_len; memcpy(new_token.text, current, copy_len); new_token.text[copy_len] = '\0'; // Deep-copy token structure payload straight into the dynamic ArrayList c_ArrayList_Add(out_list, &new_token); } current = next_match + delim_len; } // Capture the final token remaining after the last delimiter match if (*current != '\0') { c_StrToken_t new_token; size_t token_len = strlen(current); size_t copy_len = (token_len >= C_STR_TOKEN_MAX_LEN) ? (C_STR_TOKEN_MAX_LEN - 1) : token_len; memcpy(new_token.text, current, copy_len); new_token.text[copy_len] = '\0'; c_ArrayList_Add(out_list, &new_token); } return C_ERR_SUCCESS; }