When formatting text for display in the window list, it is possible to specify a limit to truncate at. This is useful for example with %t when you have a long title in the window.
The prior implementation truncated counting by bytes. This was problematic if the limit happened to be in the middle of a multibyte character. When that happened, the window list text cut off starting at the invalid character. We now count by characters rather than bytes. This ensures we always include a full multibyte character. It is possible to see the problem with this test case: set winfmt %n%s%10t set winliststyle row set winname title Then create a window such that we truncate in the middle of a multibyte character. This is possible with the following HTML document: <!DOCTYPE html> <meta charset="utf-8"> <title>testing ™ 1 2 3</title> Assuming you are using UTF-8 encoding, if your browser's title has only this text, then truncating at 10 will truncate on the second of the three bytes in the trademark symbol. --- src/format.c | 46 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/src/format.c b/src/format.c index caf8781..f9c0ddb 100644 --- a/src/format.c +++ b/src/format.c @@ -24,6 +24,7 @@ #include <stdlib.h> #include <string.h> #include "ratpoison.h" +#include <wchar.h> /* Function prototypes for format char expanders. */ #define RP_FMT(fn) static void fmt_ ## fn (rp_window_elem *elem, struct sbuf *buf) @@ -76,20 +77,47 @@ struct fmt_item fmt_items[] = { { 0, NULL } }; -/* if width >= 0 then limit the width of s to width chars. */ +/* Append s to buf. + If width >= 0 then limit the width of s to width characters. */ static void concat_width (struct sbuf *buf, char *s, int width) { - if (width >= 0) + mbstate_t state = { 0 }; + /* A counter for how many characters we've taken. */ + int i = 0; + /* The size of a character in bytes. */ + int char_len = 0; + /* Index of the current character in the string. */ + int char_idx = 0; + + /* Are we not enforcing a limit? */ + if (width < 0) { - char *s1 = xsprintf ("%%.%ds", width); - char *s2 = xsprintf (s1, s); - sbuf_concat (buf, s2); - free (s1); - free (s2); + sbuf_concat (buf, s); + return; + } + + char_idx = 0; + for (i = 0; i < width; i++) + { + /* Find how many bytes make up the next character. + Allow at most 4 bytes for a character. Because UTF-8. */ + char_len = mbrlen(s+char_idx, 4, &state); + + /* Invalid character? */ + if (char_len == -1) + break; + + /* Null character? */ + if (char_len == 0) + break; + + /* Append the character's bytes. */ + sbuf_nconcat(buf, s+char_idx, char_len); + + /* Advance to where the next character starts. */ + char_idx += char_len; } - else - sbuf_concat (buf, s); } void -- 2.11.0 _______________________________________________ Ratpoison-devel mailing list Ratpoison-devel@nongnu.org https://lists.nongnu.org/mailman/listinfo/ratpoison-devel