diff --git a/src/basic/mountpoint-util.c b/src/basic/mountpoint-util.c index ad22894a59..751d5a35ce 100644 --- a/src/basic/mountpoint-util.c +++ b/src/basic/mountpoint-util.c @@ -125,14 +125,9 @@ static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *ret_mn if (r < 0) return r; - p = startswith(fdinfo, "mnt_id:"); - if (!p) { - p = strstr(fdinfo, "\nmnt_id:"); - if (!p) /* The mnt_id field is a relatively new addition */ - return -EOPNOTSUPP; - - p += 8; - } + p = find_line_startswith(fdinfo, "mnt_id:"); + if (!p) /* The mnt_id field is a relatively new addition */ + return -EOPNOTSUPP; p += strspn(p, WHITESPACE); p[strcspn(p, WHITESPACE)] = 0; diff --git a/src/basic/process-util.c b/src/basic/process-util.c index cf2dca1dc4..c891f74be8 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -1453,14 +1453,9 @@ int pidfd_get_pid(int fd, pid_t *ret) { if (r < 0) return r; - p = startswith(fdinfo, "Pid:"); - if (!p) { - p = strstr(fdinfo, "\nPid:"); - if (!p) - return -ENOTTY; /* not a pidfd? */ - - p += 5; - } + p = find_line_startswith(fdinfo, "Pid:"); + if (!p) + return -ENOTTY; /* not a pidfd? */ p += strspn(p, WHITESPACE); p[strcspn(p, WHITESPACE)] = 0; diff --git a/src/basic/string-util.c b/src/basic/string-util.c index ad8c9863bd..cc2f8ecdab 100644 --- a/src/basic/string-util.c +++ b/src/basic/string-util.c @@ -1260,3 +1260,26 @@ char *strdupcspn(const char *a, const char *reject) { return strndup(a, strcspn(a, reject)); } + +char *find_line_startswith(const char *haystack, const char *needle) { + char *p; + + assert(haystack); + assert(needle); + + /* Finds the first line in 'haystack' that starts with the specified string. Returns a pointer to the + * first character after it */ + + p = strstr(haystack, needle); + if (!p) + return NULL; + + if (p > haystack) + while (p[-1] != '\n') { + p = strstr(p + 1, needle); + if (!p) + return NULL; + } + + return p + strlen(needle); +} diff --git a/src/basic/string-util.h b/src/basic/string-util.h index e0a47a21a9..1755b3275a 100644 --- a/src/basic/string-util.h +++ b/src/basic/string-util.h @@ -253,3 +253,5 @@ size_t strspn_from_end(const char *str, const char *accept); char *strdupspn(const char *a, const char *accept); char *strdupcspn(const char *a, const char *reject); + +char *find_line_startswith(const char *haystack, const char *needle); diff --git a/src/import/importd.c b/src/import/importd.c index ed757019cf..a58587beb2 100644 --- a/src/import/importd.c +++ b/src/import/importd.c @@ -554,9 +554,9 @@ static int manager_on_notify(sd_event_source *s, int fd, uint32_t revents, void }; struct ucred *ucred; Manager *m = userdata; - char *p, *e; Transfer *t; ssize_t n; + char *p; int r; n = recvmsg_safe(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC); @@ -590,17 +590,11 @@ static int manager_on_notify(sd_event_source *s, int fd, uint32_t revents, void buf[n] = 0; - p = startswith(buf, "X_IMPORT_PROGRESS="); - if (!p) { - p = strstr(buf, "\nX_IMPORT_PROGRESS="); - if (!p) - return 0; + p = find_line_startswith(buf, "X_IMPORT_PROGRESS="); + if (!p) + return 0; - p += 19; - } - - e = strchrnul(p, '\n'); - *e = 0; + truncate_nl(p); r = parse_percent(p); if (r < 0) { diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c index 6ed9a6890f..d372e096c4 100644 --- a/src/shared/logs-show.c +++ b/src/shared/logs-show.c @@ -93,14 +93,9 @@ static int url_from_catalog(sd_journal *j, char **ret) { if (r < 0) return log_error_errno(r, "Failed to find catalog entry: %m"); - weblink = startswith(t, "Documentation:"); - if (!weblink) { - weblink = strstr(t + 1, "\nDocumentation:"); - if (!weblink) - goto notfound; - - weblink += 15; - } + weblink = find_line_startswith(t, "Documentation:"); + if (!weblink) + goto notfound; /* Skip whitespace to value */ weblink += strspn(weblink, " \t"); diff --git a/src/test/test-string-util.c b/src/test/test-string-util.c index b3ff7d65c1..9eb048adfa 100644 --- a/src/test/test-string-util.c +++ b/src/test/test-string-util.c @@ -1218,4 +1218,33 @@ TEST(make_cstring) { TEST_MAKE_CSTRING_ONE(test8, -EINVAL, MAKE_CSTRING_REQUIRE_TRAILING_NUL, NULL); } +TEST(find_line_startswith) { + static const char text[] = + "foobar\n" + "this is a test\n" + "foobar: waldo\n" + "more\n" + "\n" + "piff\n" + "foobarfoobar\n" + "iff\n"; + static const char emptystring[] = ""; + + assert_se(find_line_startswith(text, "") == text); + assert_se(find_line_startswith(text, "f") == text+1); + assert_se(find_line_startswith(text, "foobar") == text+6); + assert_se(!find_line_startswith(text, "foobarx")); + assert_se(!find_line_startswith(text, "oobar")); + assert_se(find_line_startswith(text, "t") == text + 8); + assert_se(find_line_startswith(text, "th") == text + 9); + assert_se(find_line_startswith(text, "this") == text + 11); + assert_se(find_line_startswith(text, "foobarf") == text + 54); + assert_se(find_line_startswith(text, "more\n") == text + 41); + assert_se(find_line_startswith(text, "\n") == text + 42); + assert_se(find_line_startswith(text, "iff") == text + 63); + + assert_se(find_line_startswith(emptystring, "") == emptystring); + assert_se(!find_line_startswith(emptystring, "x")); +} + DEFINE_TEST_MAIN(LOG_DEBUG);