From c2139e2e03cbcee9a1ae03956b1f06d3a9c269b0 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Sat, 26 Mar 2022 19:42:13 -0400 Subject: util: New xstrwidth() function --- util.c | 36 ++++++++++++++++++++++++++++++++++++ util.h | 10 ++++++++++ 2 files changed, 46 insertions(+) diff --git a/util.c b/util.c index f67406c..a62e66c 100644 --- a/util.c +++ b/util.c @@ -29,6 +29,7 @@ #include #include #include +#include #if BFS_HAS_SYS_PARAM # include @@ -252,6 +253,41 @@ int xstrtofflags(const char **str, unsigned long long *set, unsigned long long * #endif } +size_t xstrwidth(const char *str) { + size_t len = strlen(str); + size_t ret = 0; + + mbstate_t mb; + memset(&mb, 0, sizeof(mb)); + + while (len > 0) { + wchar_t wc; + size_t mblen = mbrtowc(&wc, str, len, &mb); + int cwidth; + if (mblen == (size_t)-1) { + // Invalid byte sequence, assume a single-width '?' + mblen = 1; + cwidth = 1; + memset(&mb, 0, sizeof(mb)); + } else if (mblen == (size_t)-2) { + // Incomplete byte sequence, assume a single-width '?' + mblen = len; + cwidth = 1; + } else { + cwidth = wcwidth(wc); + if (cwidth < 0) { + cwidth = 0; + } + } + + str += mblen; + len -= mblen; + ret += cwidth; + } + + return ret; +} + bool is_nonexistence_error(int error) { return error == ENOENT || errno == ENOTDIR; } diff --git a/util.h b/util.h index b780624..b5c7d80 100644 --- a/util.h +++ b/util.h @@ -207,6 +207,16 @@ int xfaccessat(int fd, const char *path, int amode); */ int xstrtofflags(const char **str, unsigned long long *set, unsigned long long *clear); +/** + * wcswidth() variant that works on narrow strings. + * + * @param str + * The string to measure. + * @return + * The likely width of that string in a terminal. + */ +size_t xstrwidth(const char *str); + /** * Return whether an error code is due to a path not existing. */ -- cgit v1.2.3