From 0aaf5f0bd4d4eb76c17c37371e6706830f0857bb Mon Sep 17 00:00:00 2001
From: Tavian Barnes <tavianator@tavianator.com>
Date: Wed, 4 Nov 2020 15:24:29 -0500
Subject: eval: Make a crude attempt to handle double-width chars in the status
 bar

---
 eval.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 49 insertions(+), 11 deletions(-)

diff --git a/eval.c b/eval.c
index a6282bf..e0298af 100644
--- a/eval.c
+++ b/eval.c
@@ -51,6 +51,7 @@
 #include <sys/stat.h>
 #include <time.h>
 #include <unistd.h>
+#include <wchar.h>
 
 struct eval_state {
 	/** Data about the current file. */
@@ -1064,27 +1065,64 @@ static void eval_status(struct eval_state *state, struct bfs_bar *bar, struct ti
 		rhslen = 0;
 	}
 
-	size_t pathmax = width - rhslen - 3;
-	size_t pathlen = ftwbuf->nameoff;
-	if (ftwbuf->depth == 0) {
-		pathlen = strlen(ftwbuf->path);
-	}
-	if (pathlen > pathmax) {
-		pathlen = pathmax;
-	}
-
-	char *status = dstrndup(ftwbuf->path, pathlen);
+	char *status = dstralloc(0);
 	if (!status) {
 		goto out_rhs;
 	}
+
+	// Try to make sure even wide characters fit in the status bar
+	size_t pathmax = width - rhslen - 3;
+	size_t pathwidth = 0;
+
+	const char *path = ftwbuf->path;
+	size_t pathlen = strlen(path);
+
+	mbstate_t mb;
+	memset(&mb, 0, sizeof(mb));
+	while (*path) {
+		wchar_t wc;
+		size_t len = mbrtowc(&wc, path, pathlen, &mb);
+		int width;
+		if (len == (size_t)-1) {
+			// Invalid byte sequence, assume a single-width ?
+			len = 1;
+			width = 1;
+			memset(&mb, 0, sizeof(mb));
+		} else if (len == (size_t)-2) {
+			// Incomplete byte sequence, assume a single-width ?
+			len = pathlen;
+			width = 1;
+		} else {
+			width = wcwidth(wc);
+			if (width < 0) {
+				width = 0;
+			}
+		}
+
+		if (pathwidth + width > pathmax) {
+			break;
+		}
+
+		if (dstrncat(&status, path, len) != 0) {
+			goto out_rhs;
+		}
+
+		path += len;
+		pathlen -= len;
+		pathwidth += width;
+	}
+
 	if (dstrcat(&status, "...") != 0) {
 		goto out_rhs;
 	}
-	while (dstrlen(status) < pathmax + 3) {
+
+	while (pathwidth < pathmax) {
 		if (dstrapp(&status, ' ') != 0) {
 			goto out_rhs;
 		}
+		++pathwidth;
 	}
+
 	if (dstrcat(&status, rhs) != 0) {
 		goto out_rhs;
 	}
-- 
cgit v1.2.3