diff options
author | Tavian Barnes <tavianator@tavianator.com> | 2022-03-26 19:42:13 -0400 |
---|---|---|
committer | Tavian Barnes <tavianator@tavianator.com> | 2022-03-26 19:42:13 -0400 |
commit | c2139e2e03cbcee9a1ae03956b1f06d3a9c269b0 (patch) | |
tree | b6bda303b95d1f783fa9ae885e14c864943a94ea | |
parent | 5026a144add526567771a75b414a4d9873054620 (diff) | |
download | bfs-c2139e2e03cbcee9a1ae03956b1f06d3a9c269b0.tar.xz |
util: New xstrwidth() function
-rw-r--r-- | util.c | 36 | ||||
-rw-r--r-- | util.h | 10 |
2 files changed, 46 insertions, 0 deletions
@@ -29,6 +29,7 @@ #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> +#include <wchar.h> #if BFS_HAS_SYS_PARAM # include <sys/param.h> @@ -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; } @@ -208,6 +208,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. */ bool is_nonexistence_error(int error); |