From 428cf9c206beee3407ea3c5480b00f4cfbea95f5 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 5 Oct 2023 12:56:36 -0400 Subject: bfstd: Add a thread-safe wrapper for strerror() --- src/bfstd.c | 43 +++++++++++++++++++++++++++++++++++++++++++ src/bfstd.h | 11 +++++++++++ src/color.c | 2 +- src/eval.c | 2 +- src/parse.c | 4 ++-- src/printf.c | 2 +- src/thread.h | 3 ++- src/xregex.c | 3 ++- 8 files changed, 63 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/bfstd.c b/src/bfstd.c index fcf4a6d..e9214d4 100644 --- a/src/bfstd.c +++ b/src/bfstd.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -276,6 +277,48 @@ char *xstpencpy(char *dest, char *end, const char *src, size_t n) { } } +const char *xstrerror(int errnum) { + int saved = errno; + const char *ret = NULL; + static thread_local char buf[256]; + +#if __APPLE__ + // No strerror_l() on macOS + if (strerror_r(errnum, buf, sizeof(buf)) == 0) { + ret = buf; + } +#else +# if __NetBSD__ + // NetBSD has no thread-specific locales + locale_t loc = LC_GLOBAL_LOCALE; +# else + locale_t loc = uselocale((locale_t)0); +# endif + + locale_t copy = loc; + if (copy == LC_GLOBAL_LOCALE) { + copy = duplocale(copy); + } + + if (copy != (locale_t)0) { + ret = strerror_l(errnum, loc); + } + + if (loc == LC_GLOBAL_LOCALE) { + freelocale(copy); + } +#endif + + if (!ret) { + // Fallback for strerror_[lr]() or duplocale() failures + snprintf(buf, sizeof(buf), "Unknown error %d", errnum); + ret = buf; + } + + errno = saved; + return ret; +} + void xstrmode(mode_t mode, char str[11]) { strcpy(str, "----------"); diff --git a/src/bfstd.h b/src/bfstd.h index fb77399..abde24e 100644 --- a/src/bfstd.h +++ b/src/bfstd.h @@ -166,6 +166,17 @@ char *xstpecpy(char *dest, char *end, const char *src); */ char *xstpencpy(char *dest, char *end, const char *src, size_t n); +/** + * Thread-safe strerror(). + * + * @param errnum + * An error number. + * @return + * A string describing that error, which remains valid until the next + * xstrerror() call in the same thread. + */ +const char *xstrerror(int errnum); + /** * Format a mode like ls -l (e.g. -rw-r--r--). * diff --git a/src/color.c b/src/color.c index 788d35d..fbb5edf 100644 --- a/src/color.c +++ b/src/color.c @@ -1239,7 +1239,7 @@ static int cvbuff(CFILE *cfile, const char *format, va_list args) { break; case 'm': - if (dstrcat(&cfile->buffer, strerror(error)) != 0) { + if (dstrcat(&cfile->buffer, xstrerror(error)) != 0) { return -1; } break; diff --git a/src/eval.c b/src/eval.c index 9f4896a..e0dd97b 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1376,7 +1376,7 @@ static enum bftw_action eval_callback(const struct BFTW *ftwbuf, void *ptr) { if (ftwbuf->type == BFS_ERROR) { if (!eval_should_ignore(&state, ftwbuf->error)) { - eval_error(&state, "%s.\n", strerror(ftwbuf->error)); + eval_error(&state, "%s.\n", xstrerror(ftwbuf->error)); } state.action = BFTW_PRUNE; goto done; diff --git a/src/parse.c b/src/parse.c index 7766a7b..976f7cb 100644 --- a/src/parse.c +++ b/src/parse.c @@ -1127,7 +1127,7 @@ static struct bfs_expr *parse_color(struct parser_state *state, int color, int a if (color) { if (!colors) { - parse_expr_error(state, expr, "Error parsing $$LS_COLORS: %s.\n", strerror(ctx->colors_error)); + parse_expr_error(state, expr, "Error parsing $$LS_COLORS: %s.\n", xstrerror(ctx->colors_error)); bfs_expr_free(expr); return NULL; } @@ -3741,7 +3741,7 @@ struct bfs_ctx *bfs_parse_cmdline(int argc, char *argv[]) { } if (state.use_color == COLOR_AUTO && !ctx->colors) { - bfs_warning(ctx, "Error parsing $$LS_COLORS: %s.\n\n", strerror(ctx->colors_error)); + bfs_warning(ctx, "Error parsing $$LS_COLORS: %s.\n\n", xstrerror(ctx->colors_error)); } if (bfs_optimize(ctx) != 0) { diff --git a/src/printf.c b/src/printf.c index 98bcb0f..704e26d 100644 --- a/src/printf.c +++ b/src/printf.c @@ -744,7 +744,7 @@ int bfs_printf_parse(const struct bfs_ctx *ctx, struct bfs_expr *expr, const cha if (!directive.ptr) { int error = errno; bfs_expr_error(ctx, expr); - bfs_error(ctx, "Couldn't parse the mount table: %s.\n", strerror(error)); + bfs_error(ctx, "Couldn't parse the mount table: %s.\n", xstrerror(error)); goto directive_error; } break; diff --git a/src/thread.h b/src/thread.h index ab95a79..a59033c 100644 --- a/src/thread.h +++ b/src/thread.h @@ -8,6 +8,7 @@ #ifndef BFS_THREAD_H #define BFS_THREAD_H +#include "bfstd.h" #include "config.h" #include "diag.h" #include @@ -23,7 +24,7 @@ #endif #define thread_verify(expr, cond) \ - bfs_verify((errno = (expr), (cond)), "%s: %s", #expr, strerror(errno)) + bfs_verify((errno = (expr), (cond)), "%s: %s", #expr, xstrerror(errno)) /** * Wrapper for pthread_create(). diff --git a/src/xregex.c b/src/xregex.c index 87b692e..3df27f0 100644 --- a/src/xregex.c +++ b/src/xregex.c @@ -3,6 +3,7 @@ #include "xregex.h" #include "alloc.h" +#include "bfstd.h" #include "config.h" #include "diag.h" #include "sanity.h" @@ -274,7 +275,7 @@ void bfs_regfree(struct bfs_regex *regex) { char *bfs_regerror(const struct bfs_regex *regex) { if (!regex) { - return strdup(strerror(ENOMEM)); + return strdup(xstrerror(ENOMEM)); } #if BFS_USE_ONIGURUMA -- cgit v1.2.3