From e8fe84e27c3af48d3517d23582ad49d614919d21 Mon Sep 17 00:00:00 2001 From: huntedbytheirs Date: Mon, 31 Aug 2026 21:14:52 -0400 Subject: [PATCH] feat(string): add GNU strcasestr at level 3 Case-insensitive ASCII-fold substring search, locale-free, no allocation. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- src/string/strcasestr.c | 59 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 src/string/strcasestr.c diff --git a/src/string/strcasestr.c b/src/string/strcasestr.c new file mode 100644 index 0000000..49240b1 --- /dev/null +++ b/src/string/strcasestr.c @@ -0,0 +1,59 @@ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +/* + * Fold an ASCII uppercase letter to lowercase; every other character + * (including NUL) passes through unchanged. Kept local and locale-free: + * strcasestr must never depend on ctype tables or the active locale. + */ +static inline char +fold_ascii(char c) +{ + if (c >= 'A' && c <= 'Z') + { + return (char)(c + ('a' - 'A')); + } + + return c; +} + +/* + * Return a pointer to the first case-insensitive (ASCII fold) occurrence of + * needle in haystack, or NULL if absent. An empty needle matches haystack. + * + * Naive two-pointer scan: for each position in haystack, compare folded + * characters until the needle is exhausted (match) or a mismatch occurs + * (advance). The inner loop stops at the NUL terminator of either string, + * so the comparison never reads past the end of haystack when needle is + * longer than the remaining tail. + */ +char * +strcasestr(const char *haystack, const char *needle) // NOLINT(bugprone-easily-swappable-parameters) +{ + if (*needle == '\0') + { + return (char *)haystack; + } + + for (; *haystack != '\0'; haystack++) + { + const char *h = haystack; + const char *n = needle; + + while (*n != '\0' && fold_ascii(*h) == fold_ascii(*n)) + { + h++; + n++; + } + + if (*n == '\0') + { + return (char *)haystack; + } + } + + return NULL; +}