]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
string-util: let's add helper for truncating string after a specified number of lines
authorLennart Poettering <lennart@poettering.net>
Mon, 13 Jan 2020 15:07:06 +0000 (16:07 +0100)
committerLennart Poettering <lennart@poettering.net>
Mon, 13 Jan 2020 15:36:47 +0000 (16:36 +0100)
src/basic/string-util.c
src/basic/string-util.h
src/test/test-string-util.c

index b477a5153420d8ed11fda8aa43f10fcd39a4af85..30eafc0897a335dbaaa71338460bce2ac778629a 100644 (file)
@@ -1074,3 +1074,60 @@ char* string_erase(char *x) {
         explicit_bzero_safe(x, strlen(x));
         return x;
 }
+
+int string_truncate_lines(const char *s, size_t n_lines, char **ret) {
+        const char *p = s, *e = s;
+        bool truncation_applied = false;
+        char *copy;
+        size_t n = 0;
+
+        assert(s);
+
+        /* Truncate after the specified number of lines. Returns > 0 if a truncation was applied or == 0 if
+         * there were fewer lines in the string anyway. Trailing newlines on input are ignored, and not
+         * generated either. */
+
+        for (;;) {
+                size_t k;
+
+                k = strcspn(p, "\n");
+
+                if (p[k] == 0) {
+                        if (k == 0) /* final empty line */
+                                break;
+
+                        if (n >= n_lines) /* above threshold */
+                                break;
+
+                        e = p + k; /* last line to include */
+                        break;
+                }
+
+                assert(p[k] == '\n');
+
+                if (n >= n_lines)
+                        break;
+
+                if (k > 0)
+                        e = p + k;
+
+                p += k + 1;
+                n++;
+        }
+
+        /* e points after the last character we want to keep */
+        if (isempty(e))
+                copy = strdup(s);
+        else {
+                if (!in_charset(e, "\n")) /* We only consider things truncated if we remove something that
+                                           * isn't a new-line or a series of them */
+                        truncation_applied = true;
+
+                copy = strndup(s, e - s);
+        }
+        if (!copy)
+                return -ENOMEM;
+
+        *ret = copy;
+        return truncation_applied;
+}
index f10af9ad2f82dd4ab5983a72c37811f3fac7966b..ebab694b93dcb34f379acf313bd4df6b67100296 100644 (file)
@@ -280,3 +280,5 @@ 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);
index 7a05afb4acaadb3313c1414444c13418a8eca815..7a65ff83c40cb20019a1843f3c94c3fa1e5fe2c2 100644 (file)
@@ -563,6 +563,77 @@ static void test_memory_startswith_no_case(void) {
         assert_se(memory_startswith_no_case((char[2]){'X', 'X'}, 2, "XX"));
 }
 
+static void test_string_truncate_lines_one(const char *input, size_t n_lines, const char *output, bool truncation) {
+        _cleanup_free_ char *b = NULL;
+        int k;
+
+        assert_se((k = string_truncate_lines(input, n_lines, &b)) >= 0);
+        assert_se(streq(b, output));
+        assert_se(!!k == truncation);
+}
+
+static void test_string_truncate_lines(void) {
+        test_string_truncate_lines_one("", 0, "", false);
+        test_string_truncate_lines_one("", 1, "", false);
+        test_string_truncate_lines_one("", 2, "", false);
+        test_string_truncate_lines_one("", 3, "", false);
+
+        test_string_truncate_lines_one("x", 0, "", true);
+        test_string_truncate_lines_one("x", 1, "x", false);
+        test_string_truncate_lines_one("x", 2, "x", false);
+        test_string_truncate_lines_one("x", 3, "x", false);
+
+        test_string_truncate_lines_one("x\n", 0, "", true);
+        test_string_truncate_lines_one("x\n", 1, "x", false);
+        test_string_truncate_lines_one("x\n", 2, "x", false);
+        test_string_truncate_lines_one("x\n", 3, "x", false);
+
+        test_string_truncate_lines_one("x\ny", 0, "", true);
+        test_string_truncate_lines_one("x\ny", 1, "x", true);
+        test_string_truncate_lines_one("x\ny", 2, "x\ny", false);
+        test_string_truncate_lines_one("x\ny", 3, "x\ny", false);
+
+        test_string_truncate_lines_one("x\ny\n", 0, "", true);
+        test_string_truncate_lines_one("x\ny\n", 1, "x", true);
+        test_string_truncate_lines_one("x\ny\n", 2, "x\ny", false);
+        test_string_truncate_lines_one("x\ny\n", 3, "x\ny", false);
+
+        test_string_truncate_lines_one("x\ny\nz", 0, "", true);
+        test_string_truncate_lines_one("x\ny\nz", 1, "x", true);
+        test_string_truncate_lines_one("x\ny\nz", 2, "x\ny", true);
+        test_string_truncate_lines_one("x\ny\nz", 3, "x\ny\nz", false);
+
+        test_string_truncate_lines_one("x\ny\nz\n", 0, "", true);
+        test_string_truncate_lines_one("x\ny\nz\n", 1, "x", true);
+        test_string_truncate_lines_one("x\ny\nz\n", 2, "x\ny", true);
+        test_string_truncate_lines_one("x\ny\nz\n", 3, "x\ny\nz", false);
+
+        test_string_truncate_lines_one("\n", 0, "", false);
+        test_string_truncate_lines_one("\n", 1, "", false);
+        test_string_truncate_lines_one("\n", 2, "", false);
+        test_string_truncate_lines_one("\n", 3, "", false);
+
+        test_string_truncate_lines_one("\n\n", 0, "", false);
+        test_string_truncate_lines_one("\n\n", 1, "", false);
+        test_string_truncate_lines_one("\n\n", 2, "", false);
+        test_string_truncate_lines_one("\n\n", 3, "", false);
+
+        test_string_truncate_lines_one("\n\n\n", 0, "", false);
+        test_string_truncate_lines_one("\n\n\n", 1, "", false);
+        test_string_truncate_lines_one("\n\n\n", 2, "", false);
+        test_string_truncate_lines_one("\n\n\n", 3, "", false);
+
+        test_string_truncate_lines_one("\nx\n\n", 0, "", true);
+        test_string_truncate_lines_one("\nx\n\n", 1, "", true);
+        test_string_truncate_lines_one("\nx\n\n", 2, "\nx", false);
+        test_string_truncate_lines_one("\nx\n\n", 3, "\nx", false);
+
+        test_string_truncate_lines_one("\n\nx\n", 0, "", true);
+        test_string_truncate_lines_one("\n\nx\n", 1, "", true);
+        test_string_truncate_lines_one("\n\nx\n", 2, "", true);
+        test_string_truncate_lines_one("\n\nx\n", 3, "\n\nx", false);
+}
+
 int main(int argc, char *argv[]) {
         test_setup_logging(LOG_DEBUG);
 
@@ -595,6 +666,7 @@ int main(int argc, char *argv[]) {
         test_strlen_ptr();
         test_memory_startswith();
         test_memory_startswith_no_case();
+        test_string_truncate_lines();
 
         return 0;
 }