ratpoison-devel
[Top][All Lists]
Advanced

[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 &trade; 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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]