From: Lennart Poettering Date: Fri, 1 Jun 2018 19:45:23 +0000 (+0200) Subject: string-util: tweak cellescape() a bit X-Git-Tag: v239~167^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=61f6e276714c0bc5c43f7f0c9daf1863f3958987;p=thirdparty%2Fsystemd.git string-util: tweak cellescape() a bit For short buffer sizes cellescape() was a bit wasteful, as it might suffice to to drop a single character to find enough place for the full four byte ellipsis, if that one character was a four character escape. With this rework we'll guarantee to drop the minimum number of characters from the end to fit in the ellipsis. If the buffers we write to are large this doesn't matter much. However, if they are short (as they are when talking about the process comm field) then it starts to matter that we put as much information as we can in the space we get. --- diff --git a/src/basic/string-util.c b/src/basic/string-util.c index b06b50397ee..4c7ab3c4d38 100644 --- a/src/basic/string-util.c +++ b/src/basic/string-util.c @@ -627,25 +627,58 @@ char *cellescape(char *buf, size_t len, const char *s) { * very end. */ - size_t i; - const char *t = s; + size_t i = 0, last_char_width[4] = {}, k = 0, j; + + assert(len > 0); /* at least a terminating NUL */ - assert(len > 4 + 4 + 1); /* two chars and the terminator */ + for (;;) { + char four[4]; + int w; - for (i = 0; i < len - 9; t++) { - if (!*t) + if (*s == 0) /* terminating NUL detected? then we are done! */ goto done; - i += cescape_char(*t, buf + i); + + w = cescape_char(*s, four); + if (i + w + 1 > len) /* This character doesn't fit into the buffer anymore? In that case let's + * ellipsize at the previous location */ + break; + + /* OK, there was space, let's add this escaped character to the buffer */ + memcpy(buf + i, four, w); + i += w; + + /* And remember its width in the ring buffer */ + last_char_width[k] = w; + k = (k + 1) % 4; + + s++; } - /* We have space for one more char and terminating nul at this point */ - if (*t) { - if (*(t+1)) - i += write_ellipsis(buf + i, false); - else - i += cescape_char(*t, buf + i); + /* Ellipsation is necessary. This means we might need to truncate the string again to make space for 4 + * characters ideally, but the buffer is shorter than that in the first place take what we can get */ + for (j = 0; j < ELEMENTSOF(last_char_width); j++) { + + if (i + 4 <= len) /* nice, we reached our space goal */ + break; + + k = k == 0 ? 3 : k - 1; + if (last_char_width[k] == 0) /* bummer, we reached the beginning of the strings */ + break; + + assert(i >= last_char_width[k]); + i -= last_char_width[k]; } + if (i + 4 <= len) /* yay, enough space */ + i += write_ellipsis(buf + i, false); + else if (i + 3 <= len) { /* only space for ".." */ + buf[i++] = '.'; + buf[i++] = '.'; + } else if (i + 2 <= len) /* only space for a single "." */ + buf[i++] = '.'; + else + assert(i + 1 <= len); + done: buf[i] = '\0'; return buf; diff --git a/src/test/test-string-util.c b/src/test/test-string-util.c index 413adfda7de..8b176781de9 100644 --- a/src/test/test-string-util.c +++ b/src/test/test-string-util.c @@ -81,21 +81,79 @@ static void test_ascii_strcasecmp_nn(void) { static void test_cellescape(void) { char buf[40]; - assert_se(streq(cellescape(buf, 10, "1"), "1")); - assert_se(streq(cellescape(buf, 10, "12"), "12")); - assert_se(streq(cellescape(buf, 10, "123"), is_locale_utf8() ? "1…" : "1...")); - - assert_se(streq(cellescape(buf, 10, "1\011"), "1\\t")); - assert_se(streq(cellescape(buf, 10, "1\020"), "1\\020")); - assert_se(streq(cellescape(buf, 10, "1\020x"), is_locale_utf8() ? "1…" : "1...")); + assert_se(streq(cellescape(buf, 1, ""), "")); + assert_se(streq(cellescape(buf, 1, "1"), "")); + assert_se(streq(cellescape(buf, 1, "12"), "")); + + assert_se(streq(cellescape(buf, 2, ""), "")); + assert_se(streq(cellescape(buf, 2, "1"), "1")); + assert_se(streq(cellescape(buf, 2, "12"), ".")); + assert_se(streq(cellescape(buf, 2, "123"), ".")); + + assert_se(streq(cellescape(buf, 3, ""), "")); + assert_se(streq(cellescape(buf, 3, "1"), "1")); + assert_se(streq(cellescape(buf, 3, "12"), "12")); + assert_se(streq(cellescape(buf, 3, "123"), "..")); + assert_se(streq(cellescape(buf, 3, "1234"), "..")); + + assert_se(streq(cellescape(buf, 4, ""), "")); + assert_se(streq(cellescape(buf, 4, "1"), "1")); + assert_se(streq(cellescape(buf, 4, "12"), "12")); + assert_se(streq(cellescape(buf, 4, "123"), "123")); + assert_se(streq(cellescape(buf, 4, "1234"), is_locale_utf8() ? "…" : "...")); + assert_se(streq(cellescape(buf, 4, "12345"), is_locale_utf8() ? "…" : "...")); + + assert_se(streq(cellescape(buf, 5, ""), "")); + assert_se(streq(cellescape(buf, 5, "1"), "1")); + assert_se(streq(cellescape(buf, 5, "12"), "12")); + assert_se(streq(cellescape(buf, 5, "123"), "123")); + assert_se(streq(cellescape(buf, 5, "1234"), "1234")); + assert_se(streq(cellescape(buf, 5, "12345"), is_locale_utf8() ? "1…" : "1...")); + assert_se(streq(cellescape(buf, 5, "123456"), is_locale_utf8() ? "1…" : "1...")); + + assert_se(streq(cellescape(buf, 1, "\020"), "")); + assert_se(streq(cellescape(buf, 2, "\020"), ".")); + assert_se(streq(cellescape(buf, 3, "\020"), "..")); + assert_se(streq(cellescape(buf, 4, "\020"), "…")); + assert_se(streq(cellescape(buf, 5, "\020"), "\\020")); + + assert_se(streq(cellescape(buf, 5, "1234\020"), "1…")); + assert_se(streq(cellescape(buf, 6, "1234\020"), "12…")); + assert_se(streq(cellescape(buf, 7, "1234\020"), "123…")); + assert_se(streq(cellescape(buf, 8, "1234\020"), "1234…")); + assert_se(streq(cellescape(buf, 9, "1234\020"), "1234\\020")); + + assert_se(streq(cellescape(buf, 1, "\t\n"), "")); + assert_se(streq(cellescape(buf, 2, "\t\n"), ".")); + assert_se(streq(cellescape(buf, 3, "\t\n"), "..")); + assert_se(streq(cellescape(buf, 4, "\t\n"), "…")); + assert_se(streq(cellescape(buf, 5, "\t\n"), "\\t\\n")); + + assert_se(streq(cellescape(buf, 5, "1234\t\n"), "1…")); + assert_se(streq(cellescape(buf, 6, "1234\t\n"), "12…")); + assert_se(streq(cellescape(buf, 7, "1234\t\n"), "123…")); + assert_se(streq(cellescape(buf, 8, "1234\t\n"), "1234…")); + assert_se(streq(cellescape(buf, 9, "1234\t\n"), "1234\\t\\n")); + + assert_se(streq(cellescape(buf, 4, "x\t\020\n"), "…")); + assert_se(streq(cellescape(buf, 5, "x\t\020\n"), "x…")); + assert_se(streq(cellescape(buf, 6, "x\t\020\n"), "x…")); + assert_se(streq(cellescape(buf, 7, "x\t\020\n"), "x\\t…")); + assert_se(streq(cellescape(buf, 8, "x\t\020\n"), "x\\t…")); + assert_se(streq(cellescape(buf, 9, "x\t\020\n"), "x\\t…")); + assert_se(streq(cellescape(buf, 10, "x\t\020\n"), "x\\t\\020\\n")); + + assert_se(streq(cellescape(buf, 6, "1\011"), "1\\t")); + assert_se(streq(cellescape(buf, 6, "1\020"), "1\\020")); + assert_se(streq(cellescape(buf, 6, "1\020x"), is_locale_utf8() ? "1…" : "1...")); assert_se(streq(cellescape(buf, 40, "1\020"), "1\\020")); assert_se(streq(cellescape(buf, 40, "1\020x"), "1\\020x")); assert_se(streq(cellescape(buf, 40, "\a\b\f\n\r\t\v\\\"'"), "\\a\\b\\f\\n\\r\\t\\v\\\\\\\"\\'")); - assert_se(streq(cellescape(buf, 10, "\a\b\f\n\r\t\v\\\"'"), is_locale_utf8() ? "\\a…" : "\\a...")); - assert_se(streq(cellescape(buf, 11, "\a\b\f\n\r\t\v\\\"'"), is_locale_utf8() ? "\\a…" : "\\a...")); - assert_se(streq(cellescape(buf, 12, "\a\b\f\n\r\t\v\\\"'"), is_locale_utf8() ? "\\a\\b…" : "\\a\\b...")); + assert_se(streq(cellescape(buf, 6, "\a\b\f\n\r\t\v\\\"'"), is_locale_utf8() ? "\\a…" : "\\a...")); + assert_se(streq(cellescape(buf, 7, "\a\b\f\n\r\t\v\\\"'"), is_locale_utf8() ? "\\a…" : "\\a...")); + assert_se(streq(cellescape(buf, 8, "\a\b\f\n\r\t\v\\\"'"), is_locale_utf8() ? "\\a\\b…" : "\\a\\b...")); assert_se(streq(cellescape(buf, sizeof buf, "1\020"), "1\\020")); assert_se(streq(cellescape(buf, sizeof buf, "1\020x"), "1\\020x"));