From 14ef89a442f7a027f52fd688b438c5fa627b6af7 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 27 Oct 2023 10:58:47 -0400 Subject: bfstd: Expose xmbrtowc() and use it in eval_status() --- src/bfstd.c | 26 +++++++++++++------------- src/bfstd.h | 19 ++++++++++++++++++- src/eval.c | 39 +++++++++++++++------------------------ 3 files changed, 46 insertions(+), 38 deletions(-) diff --git a/src/bfstd.c b/src/bfstd.c index cdc33f1..a5a7e54 100644 --- a/src/bfstd.c +++ b/src/bfstd.c @@ -586,18 +586,18 @@ int xstrtofflags(const char **str, unsigned long long *set, unsigned long long * #endif } -/** mbrtowc() wrapper. */ -static int xmbrtowc(wchar_t *wc, size_t *i, const char *str, size_t len, mbstate_t *mb) { - size_t mblen = mbrtowc(wc, str + *i, len - *i, mb); +wint_t xmbrtowc(const char *str, size_t *i, size_t len, mbstate_t *mb) { + wchar_t wc; + size_t mblen = mbrtowc(&wc, str + *i, len - *i, mb); switch (mblen) { case -1: // Invalid byte sequence case -2: // Incomplete byte sequence *i += 1; memset(mb, 0, sizeof(*mb)); - return -1; + return WEOF; default: *i += mblen; - return 0; + return wc; } } @@ -609,12 +609,12 @@ size_t xstrwidth(const char *str) { memset(&mb, 0, sizeof(mb)); for (size_t i = 0; i < len;) { - wchar_t wc; - if (xmbrtowc(&wc, &i, str, len, &mb) == 0) { - ret += wcwidth(wc); - } else { + wint_t wc = xmbrtowc(str, &i, len, &mb); + if (wc == WEOF) { // Assume a single-width '?' ++ret; + } else { + ret += wcwidth(wc); } } @@ -729,8 +729,8 @@ multibyte: memset(&mb, 0, sizeof(mb)); for (size_t j = i; i < len; i = j) { - wchar_t wc; - if (xmbrtowc(&wc, &j, str, len, &mb) != 0) { + wint_t wc = xmbrtowc(str, &j, len, &mb); + if (wc == WEOF) { break; } if (!xiswprint(wc, flags)) { @@ -781,8 +781,8 @@ static char *dollar_quote(char *dest, char *end, const char *str, size_t len, en size_t start = i; bool safe = false; - wchar_t wc; - if (xmbrtowc(&wc, &i, str, len, &mb) == 0) { + wint_t wc = xmbrtowc(str, &i, len, &mb); + if (wc != WEOF) { safe = xiswprint(wc, flags); } diff --git a/src/bfstd.h b/src/bfstd.h index abde24e..db558c6 100644 --- a/src/bfstd.h +++ b/src/bfstd.h @@ -316,7 +316,24 @@ char *xconfstr(int name); */ int xstrtofflags(const char **str, unsigned long long *set, unsigned long long *clear); -// #include +#include + +/** + * Error-recovering mbrtowc() wrapper. + * + * @param str + * The string to convert. + * @param i + * The current index. + * @param len + * The length of the string. + * @param mb + * The multi-byte decoding state. + * @return + * The wide character at index *i, or WEOF if decoding fails. In either + * case, *i will be advanced to the next multi-byte character. + */ +wint_t xmbrtowc(const char *str, size_t *i, size_t len, mbstate_t *mb); /** * wcswidth() variant that works on narrow strings. diff --git a/src/eval.c b/src/eval.c index 3d396fa..6230353 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1114,6 +1114,7 @@ static void eval_status(struct bfs_eval *state, struct bfs_bar *bar, struct time const struct BFTW *ftwbuf = state->ftwbuf; + dchar *status = NULL; dchar *rhs = dstrprintf(" (visited: %zu, depth: %2zu)", count, ftwbuf->depth); if (!rhs) { return; @@ -1125,9 +1126,9 @@ static void eval_status(struct bfs_eval *state, struct bfs_bar *bar, struct time rhslen = 0; } - dchar *status = dstralloc(0); + status = dstralloc(0); if (!status) { - goto out_rhs; + goto out; } const char *path = ftwbuf->path; @@ -1139,20 +1140,14 @@ static void eval_status(struct bfs_eval *state, struct bfs_bar *bar, struct time // Try to make sure even wide characters fit in the status bar size_t pathmax = width - rhslen - 3; size_t pathwidth = 0; + size_t lhslen = 0; mbstate_t mb; memset(&mb, 0, sizeof(mb)); - while (pathlen > 0) { - wchar_t wc; - size_t len = mbrtowc(&wc, path, pathlen, &mb); + for (size_t i = lhslen; lhslen < pathlen; lhslen = i) { + wint_t wc = xmbrtowc(path, &i, pathlen, &mb); int cwidth; - if (len == (size_t)-1) { + if (wc == WEOF) { // Invalid byte sequence, assume a single-width '?' - len = 1; - cwidth = 1; - memset(&mb, 0, sizeof(mb)); - } else if (len == (size_t)-2) { - // Incomplete byte sequence, assume a single-width '?' - len = pathlen; cwidth = 1; } else { cwidth = wcwidth(wc); @@ -1164,35 +1159,31 @@ static void eval_status(struct bfs_eval *state, struct bfs_bar *bar, struct time if (pathwidth + cwidth > pathmax) { break; } - - if (dstrncat(&status, path, len) != 0) { - goto out_rhs; - } - - path += len; - pathlen -= len; pathwidth += cwidth; } + if (dstrncat(&status, path, lhslen) != 0) { + goto out; + } if (dstrcat(&status, "...") != 0) { - goto out_rhs; + goto out; } while (pathwidth < pathmax) { if (dstrapp(&status, ' ') != 0) { - goto out_rhs; + goto out; } ++pathwidth; } - if (dstrcat(&status, rhs) != 0) { - goto out_rhs; + if (dstrdcat(&status, rhs) != 0) { + goto out; } bfs_bar_update(bar, status); +out: dstrfree(status); -out_rhs: dstrfree(rhs); } -- cgit v1.2.3