return i;
}
+#undef str_begins_suffix
+bool str_begins_suffix(const char *haystack, const char *needle, const char **suffix_r)
+{
+ size_t prefix_len = str_match(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;
+bool str_begins_suffix(const char *haystack, const char *needle,
+ const char **suffix_r);
static inline ATTR_PURE bool
str_begins_with(const char *haystack, const char *needle)
{
#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. */
+static inline bool
+str_begins_builtin_success(const char *haystack, size_t needle_len,
+ const char **suffix_r)
+{
+ *suffix_r = haystack + needle_len;
+ return TRUE;
+}
# define str_begins_with(h, n) \
(__builtin_constant_p(n) ? strncmp((h), (n), strlen(n))==0 : \
(str_begins_with)((h), (n)))
+# define str_begins_suffix(h, n, suffix_r) \
+ (!__builtin_constant_p(n) ? (str_begins_suffix)((h), (n), (suffix_r)) : \
+ (strncmp((h), (n), strlen(n)) != 0 ? FALSE : \
+ str_begins_builtin_success((h), strlen(n), suffix_r)))
#endif
#define str_begins(h, n) str_begins_with(h, n)
MATCH_TEST("blahblahblah", "foo", "bar"),
#undef MATCH_TEST
};
-
+ const char *suffix;
unsigned int i;
test_begin("str_match");
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. */
- test_assert_idx(str_begins(tests[i].s1, tests[i].s2) ==
- (strncmp(tests[i].s1, tests[i].s2, strlen(tests[i].s2)) == 0), i);
- test_assert_idx(str_begins(tests[i].s1, tests[i].s2) ==
+ bool equals = strncmp(tests[i].s1, tests[i].s2, strlen(tests[i].s2)) == 0;
+ test_assert_idx(str_begins_with(tests[i].s1, tests[i].s2) == equals, i);
+ test_assert_idx(str_begins_suffix(tests[i].s1, tests[i].s2, &suffix) == equals &&
+ (!equals || suffix == tests[i].s1 + strlen(tests[i].s2)), i);
+ test_assert_idx(str_begins_suffix(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_suffix("", "", &suffix) && suffix[0] == '\0');
+ test_assert(str_begins_suffix("123", "", &suffix) && strcmp(suffix, "123") == 0);
+ test_assert(str_begins_suffix("123", "1", &suffix) && strcmp(suffix, "23") == 0);
+ test_assert(str_begins_suffix("123", "123", &suffix) && suffix[0] == '\0');
+ suffix = NULL;
+ test_assert(!str_begins_suffix("123", "1234", &suffix) && suffix == NULL);
+ test_assert(!str_begins_suffix("", "123", &suffix) && suffix == NULL);
+ test_assert(!str_begins_suffix("12", "123", &suffix) && suffix == NULL);
+
test_assert(str_begins_with("", ""));
test_assert(str_begins_with("123", ""));
test_assert(str_begins_with("123", "1"));