]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
string-util: add helper for extracting n'th line of a string
authorLennart Poettering <lennart@poettering.net>
Mon, 13 Jan 2020 15:20:27 +0000 (16:20 +0100)
committerLennart Poettering <lennart@poettering.net>
Mon, 13 Jan 2020 15:37:42 +0000 (16:37 +0100)
src/basic/string-util.c
src/basic/string-util.h
src/test/test-string-util.c

index 30eafc0897a335dbaaa71338460bce2ac778629a..61809ab06559376f6c29a1893993ee1593ae4302 100644 (file)
@@ -1131,3 +1131,64 @@ int string_truncate_lines(const char *s, size_t n_lines, char **ret) {
         *ret = copy;
         return truncation_applied;
 }
+
+int string_extract_line(const char *s, size_t i, char **ret) {
+        const char *p = s;
+        size_t c = 0;
+
+        /* Extract the i'nth line from the specified string. Returns > 0 if there are more lines after that,
+         * and == 0 if we are looking at the last line or already beyond the last line. As special
+         * optimization, if the first line is requested and the string only consists of one line we return
+         * NULL, indicating the input string should be used as is, and avoid a memory allocation for a very
+         * common case. */
+
+        for (;;) {
+                const char *q;
+
+                q = strchr(p, '\n');
+                if (i == c) {
+                        /* The line we are looking for! */
+
+                        if (q) {
+                                char *m;
+
+                                m = strndup(p, q - p);
+                                if (!m)
+                                        return -ENOMEM;
+
+                                *ret = m;
+                                return !isempty(q + 1); /* more coming? */
+                        } else {
+                                if (p == s)
+                                        *ret = NULL; /* Just use the input string */
+                                else {
+                                        char *m;
+
+                                        m = strdup(p);
+                                        if (!m)
+                                                return -ENOMEM;
+
+                                        *ret = m;
+                                }
+
+                                return 0; /* The end */
+                        }
+                }
+
+                if (!q) {
+                        char *m;
+
+                        /* No more lines, return empty line */
+
+                        m = strdup("");
+                        if (!m)
+                                return -ENOMEM;
+
+                        *ret = m;
+                        return 0; /* The end */
+                }
+
+                p = q + 1;
+                c++;
+        }
+}
index ebab694b93dcb34f379acf313bd4df6b67100296..f98fbddddadd411afdecaa5d2fed636c8ffb6dd9 100644 (file)
@@ -282,3 +282,4 @@ static inline char* str_realloc(char **p) {
 char* string_erase(char *x);
 
 int string_truncate_lines(const char *s, size_t n_lines, char **ret);
+int string_extract_line(const char *s, size_t i, char **ret);
index 7a65ff83c40cb20019a1843f3c94c3fa1e5fe2c2..13936f6d25f23e2233e4a373c46fa6930567c5b1 100644 (file)
@@ -634,6 +634,82 @@ static void test_string_truncate_lines(void) {
         test_string_truncate_lines_one("\n\nx\n", 3, "\n\nx", false);
 }
 
+static void test_string_extract_lines_one(const char *input, size_t i, const char *output, bool more) {
+        _cleanup_free_ char *b = NULL;
+        int k;
+
+        assert_se((k = string_extract_line(input, i, &b)) >= 0);
+        assert_se(streq(b ?: input, output));
+        assert_se(!!k == more);
+}
+
+static void test_string_extract_line(void) {
+        test_string_extract_lines_one("", 0, "", false);
+        test_string_extract_lines_one("", 1, "", false);
+        test_string_extract_lines_one("", 2, "", false);
+        test_string_extract_lines_one("", 3, "", false);
+
+        test_string_extract_lines_one("x", 0, "x", false);
+        test_string_extract_lines_one("x", 1, "", false);
+        test_string_extract_lines_one("x", 2, "", false);
+        test_string_extract_lines_one("x", 3, "", false);
+
+        test_string_extract_lines_one("x\n", 0, "x", false);
+        test_string_extract_lines_one("x\n", 1, "", false);
+        test_string_extract_lines_one("x\n", 2, "", false);
+        test_string_extract_lines_one("x\n", 3, "", false);
+
+        test_string_extract_lines_one("x\ny", 0, "x", true);
+        test_string_extract_lines_one("x\ny", 1, "y", false);
+        test_string_extract_lines_one("x\ny", 2, "", false);
+        test_string_extract_lines_one("x\ny", 3, "", false);
+
+        test_string_extract_lines_one("x\ny\n", 0, "x", true);
+        test_string_extract_lines_one("x\ny\n", 1, "y", false);
+        test_string_extract_lines_one("x\ny\n", 2, "", false);
+        test_string_extract_lines_one("x\ny\n", 3, "", false);
+
+        test_string_extract_lines_one("x\ny\nz", 0, "x", true);
+        test_string_extract_lines_one("x\ny\nz", 1, "y", true);
+        test_string_extract_lines_one("x\ny\nz", 2, "z", false);
+        test_string_extract_lines_one("x\ny\nz", 3, "", false);
+
+        test_string_extract_lines_one("\n", 0, "", false);
+        test_string_extract_lines_one("\n", 1, "", false);
+        test_string_extract_lines_one("\n", 2, "", false);
+        test_string_extract_lines_one("\n", 3, "", false);
+
+        test_string_extract_lines_one("\n\n", 0, "", true);
+        test_string_extract_lines_one("\n\n", 1, "", false);
+        test_string_extract_lines_one("\n\n", 2, "", false);
+        test_string_extract_lines_one("\n\n", 3, "", false);
+
+        test_string_extract_lines_one("\n\n\n", 0, "", true);
+        test_string_extract_lines_one("\n\n\n", 1, "", true);
+        test_string_extract_lines_one("\n\n\n", 2, "", false);
+        test_string_extract_lines_one("\n\n\n", 3, "", false);
+
+        test_string_extract_lines_one("\n\n\n\n", 0, "", true);
+        test_string_extract_lines_one("\n\n\n\n", 1, "", true);
+        test_string_extract_lines_one("\n\n\n\n", 2, "", true);
+        test_string_extract_lines_one("\n\n\n\n", 3, "", false);
+
+        test_string_extract_lines_one("\nx\n\n\n", 0, "", true);
+        test_string_extract_lines_one("\nx\n\n\n", 1, "x", true);
+        test_string_extract_lines_one("\nx\n\n\n", 2, "", true);
+        test_string_extract_lines_one("\nx\n\n\n", 3, "", false);
+
+        test_string_extract_lines_one("\n\nx\n\n", 0, "", true);
+        test_string_extract_lines_one("\n\nx\n\n", 1, "", true);
+        test_string_extract_lines_one("\n\nx\n\n", 2, "x", true);
+        test_string_extract_lines_one("\n\nx\n\n", 3, "", false);
+
+        test_string_extract_lines_one("\n\n\nx\n", 0, "", true);
+        test_string_extract_lines_one("\n\n\nx\n", 1, "", true);
+        test_string_extract_lines_one("\n\n\nx\n", 2, "", true);
+        test_string_extract_lines_one("\n\n\nx\n", 3, "x", false);
+}
+
 int main(int argc, char *argv[]) {
         test_setup_logging(LOG_DEBUG);
 
@@ -667,6 +743,7 @@ int main(int argc, char *argv[]) {
         test_memory_startswith();
         test_memory_startswith_no_case();
         test_string_truncate_lines();
+        test_string_extract_line();
 
         return 0;
 }