From ae22dea58699350462ac7100283bec6919a60765 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 1 Jun 2018 21:45:23 +0200 Subject: 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. --- src/basic/string-util.c | 57 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 12 deletions(-) (limited to 'src/basic') 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; -- cgit v1.2.3