]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
string-util: add strnspn()
authorSyed Mohammed Nayyar <jmestwa@gmail.com>
Mon, 29 Jun 2026 06:02:14 +0000 (11:32 +0530)
committerSyed Mohammed Nayyar <jmestwa@gmail.com>
Thu, 16 Jul 2026 10:23:37 +0000 (15:53 +0530)
Like strspn(), but reads at most 'n' bytes from the input. strspn()
is bounded only by a non-matching byte or a NUL, so it over-reads a
buffer that is all matching bytes and not NUL terminated within 'n'.

src/basic/string-util.c
src/basic/string-util.h
src/test/test-string-util.c

index 1dc90d538ad01f5f3e259ebdddb264832de9a30d..f9383e958fe418297295d200fcfff28299cd92c9 100644 (file)
@@ -1389,6 +1389,23 @@ size_t strspn_from_end(const char *str, const char *accept) {
         return n;
 }
 
+size_t strnspn(const char *str, const char *accept, size_t n) {
+        size_t i;
+
+        /* Like strspn(), but reads at most 'n' bytes from 'str'. Returns the length of the initial
+         * run of 'str' (capped at 'n') that consists solely of bytes found in 'accept'. Stops at a
+         * NUL byte too. Unlike strspn() this is safe on a buffer that is not NUL terminated within
+         * 'n' bytes. */
+
+        assert(str || n == 0);
+        assert(accept);
+
+        for (i = 0; i < n && str[i] != '\0' && strchr(accept, str[i]); i++)
+                ;
+
+        return i;
+}
+
 char* strdupspn(const char *a, const char *accept) {
         if (isempty(a) || isempty(accept))
                 return strdup("");
index 68ab38c32113b192a431ac9a25a95ad065f9d915..355f7478c3dbf9279f21d0f49f24afc2420d1cc4 100644 (file)
@@ -286,6 +286,7 @@ typedef enum MakeCStringMode {
 int make_cstring(const void *s, size_t n, MakeCStringMode mode, char **ret);
 
 size_t strspn_from_end(const char *str, const char *accept) _pure_;
+size_t strnspn(const char *str, const char *accept, size_t n) _pure_ _nonnull_if_nonzero_(1, 3);
 
 char* strdupspn(const char *a, const char *accept);
 char* strdupcspn(const char *a, const char *reject);
index 970421c7abb261f4200c1a381a421844404c01b5..2ebcb4404521a8205a8ac61bc1b9a08fa6d1b43d 100644 (file)
@@ -1286,6 +1286,26 @@ TEST(strspn_from_end) {
         assert_se(strspn_from_end("aaa12aa34", DIGITS) == 2);
 }
 
+TEST(strnspn) {
+        assert_se(strnspn(NULL, WHITESPACE, 0) == 0);
+        assert_se(strnspn("", WHITESPACE, 0) == 0);
+        assert_se(strnspn("hoge", WHITESPACE, 0) == 0);
+        assert_se(strnspn("hoge", WHITESPACE, 4) == 0);
+        assert_se(strnspn("   hoge", WHITESPACE, 7) == 3);
+        assert_se(strnspn("   hoge", WHITESPACE, 2) == 2);
+        assert_se(strnspn("    ", WHITESPACE, 4) == 4);
+
+        /* 'str' is all whitespace and not NUL terminated within 'n': at most 'n' bytes may be read,
+         * otherwise this reads off the end of the buffer (caught by the sanitizers). */
+        for (size_t n = 1; n <= 64; n++) {
+                _cleanup_free_ char *str = NULL;
+
+                assert_se(str = new(char, n));
+                memset(str, ' ', n);
+                assert_se(strnspn(str, WHITESPACE, n) == n);
+        }
+}
+
 TEST(streq_skip_trailing_chars) {
         /* NULL is WHITESPACE by default */
         assert_se(streq_skip_trailing_chars("foo bar", "foo bar", NULL));