[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[RP] [PATCH 1/3] Limit width of formatted text by characters rather than
From: |
Will Storey |
Subject: |
[RP] [PATCH 1/3] Limit width of formatted text by characters rather than bytes |
Date: |
Sun, 27 Aug 2017 12:05:53 -0700 |
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
- [RP] [PATCH 1/3] Limit width of formatted text by characters rather than bytes,
Will Storey <=