summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2023-10-05 12:56:36 -0400
committerTavian Barnes <tavianator@tavianator.com>2023-10-05 13:22:57 -0400
commit428cf9c206beee3407ea3c5480b00f4cfbea95f5 (patch)
treeb5abbb4ad7fc8ee450ea0abfdb7419ca89b8a8ca
parent634359bb169311646f6369b21f0c90a9819fe2ce (diff)
downloadbfs-428cf9c206beee3407ea3c5480b00f4cfbea95f5.tar.xz
bfstd: Add a thread-safe wrapper for strerror()
-rw-r--r--src/bfstd.c43
-rw-r--r--src/bfstd.h11
-rw-r--r--src/color.c2
-rw-r--r--src/eval.c2
-rw-r--r--src/parse.c4
-rw-r--r--src/printf.c2
-rw-r--r--src/thread.h3
-rw-r--r--src/xregex.c3
-rw-r--r--tests/bfstd.c4
-rw-r--r--tests/mksock.c2
-rw-r--r--tests/xtouch.c6
11 files changed, 69 insertions, 13 deletions
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 <errno.h>
#include <fcntl.h>
#include <langinfo.h>
+#include <locale.h>
#include <nl_types.h>
#include <stdint.h>
#include <stdio.h>
@@ -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
@@ -167,6 +167,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--).
*
* @param mode
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 <errno.h>
@@ -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
diff --git a/tests/bfstd.c b/tests/bfstd.c
index 33b3792..c386279 100644
--- a/tests/bfstd.c
+++ b/tests/bfstd.c
@@ -15,12 +15,12 @@
/** Check the result of xdirname()/xbasename(). */
static void check_base_dir(const char *path, const char *dir, const char *base) {
char *xdir = xdirname(path);
- bfs_verify(xdir, "xdirname(): %s", strerror(errno));
+ bfs_verify(xdir, "xdirname(): %s", xstrerror(errno));
bfs_verify(strcmp(xdir, dir) == 0, "xdirname('%s') == '%s' (!= '%s')", path, xdir, dir);
free(xdir);
char *xbase = xbasename(path);
- bfs_verify(xbase, "xbasename(): %s", strerror(errno));
+ bfs_verify(xbase, "xbasename(): %s", xstrerror(errno));
bfs_verify(strcmp(xbase, base) == 0, "xbasename('%s') == '%s' (!= '%s')", path, xbase, base);
free(xbase);
}
diff --git a/tests/mksock.c b/tests/mksock.c
index 7023b4f..f3b61da 100644
--- a/tests/mksock.c
+++ b/tests/mksock.c
@@ -19,7 +19,7 @@
* Print an error message.
*/
static void errmsg(const char *cmd, const char *path) {
- fprintf(stderr, "%s: '%s': %s.\n", cmd, path, strerror(errno));
+ fprintf(stderr, "%s: '%s': %s.\n", cmd, path, xstrerror(errno));
}
/**
diff --git a/tests/xtouch.c b/tests/xtouch.c
index 80fad8d..a4c4d40 100644
--- a/tests/xtouch.c
+++ b/tests/xtouch.c
@@ -197,14 +197,14 @@ int main(int argc, char *argv[]) {
if (rarg) {
struct stat buf;
if (fstatat(AT_FDCWD, rarg, &buf, at_flags(&args)) != 0) {
- fprintf(stderr, "%s: '%s': %s\n", cmd, rarg, strerror(errno));
+ fprintf(stderr, "%s: '%s': %s\n", cmd, rarg, xstrerror(errno));
return EXIT_FAILURE;
}
times[0] = buf.st_atim;
times[1] = buf.st_mtim;
} else if (darg) {
if (xgetdate(darg, &times[0]) != 0) {
- fprintf(stderr, "%s: Parsing time '%s' failed: %s\n", cmd, darg, strerror(errno));
+ fprintf(stderr, "%s: Parsing time '%s' failed: %s\n", cmd, darg, xstrerror(errno));
return EXIT_FAILURE;
}
times[1] = times[0];
@@ -237,7 +237,7 @@ int main(int argc, char *argv[]) {
for (; optind < argc; ++optind) {
const char *path = argv[optind];
if (xtouch(&args, path) != 0) {
- fprintf(stderr, "%s: '%s': %s\n", cmd, path, strerror(errno));
+ fprintf(stderr, "%s: '%s': %s\n", cmd, path, xstrerror(errno));
ret = EXIT_FAILURE;
}
}