summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2018-06-01 21:45:23 +0200
committerSven Eden <yamakuzure@gmx.net>2018-08-24 16:47:08 +0200
commitae22dea58699350462ac7100283bec6919a60765 (patch)
tree7d0b7aa2912b02c823a147f8291a9b4186e80f74
parent5acc15fbb0ce306d4a2f4e0d47f4c5d5ac433b08 (diff)
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.
-rw-r--r--src/basic/string-util.c57
-rw-r--r--src/test/test-string-util.c78
2 files changed, 113 insertions, 22 deletions
diff --git a/src/basic/string-util.c b/src/basic/string-util.c
index f241a3376..cb6428e8a 100644
--- a/src/basic/string-util.c
+++ b/src/basic/string-util.c
@@ -631,25 +631,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 8c7226e5f..4ff792c4c 100644
--- a/src/test/test-string-util.c
+++ b/src/test/test-string-util.c
@@ -83,21 +83,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"));