return i;
}
+size_t str_match_icase(const char *p1, const char *p2)
+{
+ size_t i = 0;
+
+ while (p1[i] != '\0' && i_tolower(p1[i]) == i_tolower(p2[i]))
+ i++;
+
+ return i;
+}
+
#undef str_begins
bool str_begins(const char *haystack, const char *needle, const char **suffix_r)
{
return TRUE;
}
+#undef str_begins_icase
+bool str_begins_icase(const char *haystack, const char *needle,
+ const char **suffix_r)
+{
+ size_t prefix_len = str_match_icase(haystack, needle);
+ if (needle[prefix_len] != '\0')
+ return FALSE;
+ *suffix_r = haystack + prefix_len;
+ return TRUE;
+}
+
size_t i_memspn(const void *data, size_t data_len,
const void *accept, size_t accept_len)
{
bool str_equals_timing_almost_safe(const char *s1, const char *s2);
size_t str_match(const char *p1, const char *p2) ATTR_PURE;
+size_t str_match_icase(const char *p1, const char *p2) ATTR_PURE;
bool str_begins(const char *haystack, const char *needle,
const char **suffix_r);
+bool str_begins_icase(const char *haystack, const char *needle,
+ const char **suffix_r);
static inline ATTR_PURE bool
str_begins_with(const char *haystack, const char *needle)
{
return needle[str_match(haystack, needle)] == '\0';
}
+static inline ATTR_PURE bool
+str_begins_icase_with(const char *haystack, const char *needle)
+{
+ return needle[str_match_icase(haystack, needle)] == '\0';
+}
#if defined(__GNUC__) && (__GNUC__ >= 2)
/* GCC (and Clang) are known to have a compile-time strlen("literal") shortcut, and
an optimised strncmp(), so use that by default. Macro is multi-evaluation safe. */
(!__builtin_constant_p(n) ? (str_begins)((h), (n), (suffix_r)) : \
(strncmp((h), (n), strlen(n)) != 0 ? FALSE : \
str_begins_builtin_success((h), strlen(n), suffix_r)))
+
+# define str_begins_icase_with(h, n) \
+ (__builtin_constant_p(n) ? strncasecmp((h), (n), strlen(n))==0 : \
+ (str_begins_icase_with)((h), (n)))
+# define str_begins_icase(h, n, suffix_r) \
+ (!__builtin_constant_p(n) ? (str_begins_icase)((h), (n), (suffix_r)) : \
+ (strncasecmp((h), (n), strlen(n)) != 0 ? FALSE : \
+ str_begins_builtin_success((h), strlen(n), suffix_r)))
#endif
/* Get length of a prefix segment.
static const struct {
const char*s1, *s2; size_t match;
} tests[] = {
+ { "", "a", 0 },
+ { "a", "a", 1 },
+ { "a", "A", 0 },
+ { "ab", "a", 1 },
+ { "ab", "A", 0 },
+ { "B", "AB", 0 },
+ { "ab", "AB", 0 },
#define MATCH_TEST(common, left, right) { common left, common right, sizeof(common)-1 }
MATCH_TEST("", "", ""),
MATCH_TEST("", "x", ""),
test_end();
}
+static void
+test_str_match_icase(void)
+{
+ const struct {
+ const char *s1, *s2;
+ size_t match;
+ } tests[] = {
+ { "", "a", 0 },
+ { "a", "a", 1 },
+ { "a", "A", 1 },
+ { "ab", "a", 1 },
+ { "ab", "A", 1 },
+ { "B", "AB", 0 },
+ { "ab", "AB", 2 },
+ };
+ const char *suffix;
+ unsigned int i;
+
+ test_begin("str_match_icase");
+ for (i = 0; i < N_ELEMENTS(tests); i++)
+ test_assert_idx(str_match_icase(tests[i].s1, tests[i].s2) == tests[i].match, i);
+ test_end();
+
+ test_begin("str_begins_icase");
+ for (i = 0; i < N_ELEMENTS(tests); i++) {
+ /* This is just 2 ways of wording the same test, but that also
+ sanity tests the match values above. */
+ bool equals = strncasecmp(tests[i].s1, tests[i].s2, strlen(tests[i].s2)) == 0;
+ test_assert_idx(str_begins_icase_with(tests[i].s1, tests[i].s2) == equals, i);
+ test_assert_idx(str_begins_icase(tests[i].s1, tests[i].s2, &suffix) == equals &&
+ (!equals || suffix == tests[i].s1 + strlen(tests[i].s2)), i);
+ test_assert_idx(str_begins_icase(tests[i].s1, tests[i].s2, &suffix) ==
+ (strlen(tests[i].s2) == tests[i].match), i);
+ }
+ /* test literal-optimized versions of these */
+ test_assert(str_begins_icase("", "", &suffix) && suffix[0] == '\0');
+ test_assert(str_begins_icase("aBc", "", &suffix) && strcmp(suffix, "aBc") == 0);
+ test_assert(str_begins_icase("aBc", "a", &suffix) && strcmp(suffix, "Bc") == 0);
+ test_assert(str_begins_icase("aBc", "A", &suffix) && strcmp(suffix, "Bc") == 0);
+ test_assert(str_begins_icase("aBc", "AbC", &suffix) && suffix[0] == '\0');
+ suffix = NULL;
+ test_assert(!str_begins_icase("aBc", "AbCd", &suffix) && suffix == NULL);
+ test_assert(!str_begins_icase("", "aBc", &suffix) && suffix == NULL);
+ test_assert(!str_begins_icase("aB", "AbC", &suffix) && suffix == NULL);
+
+ test_assert(str_begins_icase_with("", ""));
+ test_assert(str_begins_icase_with("aBc", ""));
+ test_assert(str_begins_icase_with("aBc", "A"));
+ test_assert(str_begins_icase_with("aBc", "AbC"));
+ test_assert(!str_begins_icase_with("aBc", "aBcD"));
+ test_assert(!str_begins_icase_with("", "aBc"));
+ test_assert(!str_begins_icase_with("aB", "AbC"));
+ test_end();
+}
+
static void test_memspn(void)
{
#undef TEST_CASE
test_str_equals_timing_almost_safe();
test_dec2str_buf();
test_str_match();
+ test_str_match_icase();
test_memspn();
test_memcspn();
}