Index: head/include/xlocale/_string.h =================================================================== --- head/include/xlocale/_string.h (revision 368691) +++ head/include/xlocale/_string.h (revision 368692) @@ -1,61 +1,62 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2011, 2012 The FreeBSD Foundation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _LOCALE_T_DEFINED #define _LOCALE_T_DEFINED typedef struct _xlocale *locale_t; #endif /* * This file is included from both string.h and xlocale.h. We need to expose * the declarations unconditionally if we are included from xlocale.h, but only * if we are in POSIX2008 mode if included from string.h. */ #ifndef _XLOCALE_STRING1_H #define _XLOCALE_STRING1_H /* * POSIX2008 functions */ int strcoll_l(const char *, const char *, locale_t); +char *strerror_l(int num, locale_t); size_t strxfrm_l(char *, const char *, size_t, locale_t); #endif /* _XLOCALE_STRING1_H */ /* * xlocale extensions */ #ifdef _XLOCALE_H_ #ifndef _XLOCALE_STRING2_H #define _XLOCALE_STRING2_H char *strcasestr_l(const char *, const char *, locale_t); #endif /* _XLOCALE_STRING2_H */ #endif /* _XLOCALE_H_ */ Index: head/lib/libc/include/libc_private.h =================================================================== --- head/lib/libc/include/libc_private.h (revision 368691) +++ head/lib/libc/include/libc_private.h (revision 368692) @@ -1,434 +1,439 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1998 John Birrell . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ * * Private definitions for libc, libc_r and libpthread. * */ #ifndef _LIBC_PRIVATE_H_ #define _LIBC_PRIVATE_H_ #include #include /* * This global flag is non-zero when a process has created one * or more threads. It is used to avoid calling locking functions * when they are not required. */ #ifndef __LIBC_ISTHREADED_DECLARED #define __LIBC_ISTHREADED_DECLARED extern int __isthreaded; #endif /* * Elf_Auxinfo *__elf_aux_vector, the pointer to the ELF aux vector * provided by kernel. Either set for us by rtld, or found at runtime * on stack for static binaries. * * Type is void to avoid polluting whole libc with ELF types. */ extern void *__elf_aux_vector; /* * libc should use libc_dlopen internally, which respects a global * flag where loading of new shared objects can be restricted. */ void *libc_dlopen(const char *, int); /* * For dynamic linker. */ void _rtld_error(const char *fmt, ...); /* * File lock contention is difficult to diagnose without knowing * where locks were set. Allow a debug library to be built which * records the source file and line number of each lock call. */ #ifdef _FLOCK_DEBUG #define _FLOCKFILE(x) _flockfile_debug(x, __FILE__, __LINE__) #else #define _FLOCKFILE(x) _flockfile(x) #endif /* * Macros for locking and unlocking FILEs. These test if the * process is threaded to avoid locking when not required. */ #define FLOCKFILE(fp) if (__isthreaded) _FLOCKFILE(fp) #define FUNLOCKFILE(fp) if (__isthreaded) _funlockfile(fp) struct _spinlock; extern struct _spinlock __stdio_thread_lock __hidden; #define STDIO_THREAD_LOCK() \ do { \ if (__isthreaded) \ _SPINLOCK(&__stdio_thread_lock); \ } while (0) #define STDIO_THREAD_UNLOCK() \ do { \ if (__isthreaded) \ _SPINUNLOCK(&__stdio_thread_lock); \ } while (0) void __libc_spinlock_stub(struct _spinlock *); void __libc_spinunlock_stub(struct _spinlock *); /* * Indexes into the pthread jump table. * * Warning! If you change this type, you must also change the threads * libraries that reference it (libc_r, libpthread). */ typedef enum { PJT_ATFORK, PJT_ATTR_DESTROY, PJT_ATTR_GETDETACHSTATE, PJT_ATTR_GETGUARDSIZE, PJT_ATTR_GETINHERITSCHED, PJT_ATTR_GETSCHEDPARAM, PJT_ATTR_GETSCHEDPOLICY, PJT_ATTR_GETSCOPE, PJT_ATTR_GETSTACKADDR, PJT_ATTR_GETSTACKSIZE, PJT_ATTR_INIT, PJT_ATTR_SETDETACHSTATE, PJT_ATTR_SETGUARDSIZE, PJT_ATTR_SETINHERITSCHED, PJT_ATTR_SETSCHEDPARAM, PJT_ATTR_SETSCHEDPOLICY, PJT_ATTR_SETSCOPE, PJT_ATTR_SETSTACKADDR, PJT_ATTR_SETSTACKSIZE, PJT_CANCEL, PJT_CLEANUP_POP, PJT_CLEANUP_PUSH, PJT_COND_BROADCAST, PJT_COND_DESTROY, PJT_COND_INIT, PJT_COND_SIGNAL, PJT_COND_TIMEDWAIT, PJT_COND_WAIT, PJT_DETACH, PJT_EQUAL, PJT_EXIT, PJT_GETSPECIFIC, PJT_JOIN, PJT_KEY_CREATE, PJT_KEY_DELETE, PJT_KILL, PJT_MAIN_NP, PJT_MUTEXATTR_DESTROY, PJT_MUTEXATTR_INIT, PJT_MUTEXATTR_SETTYPE, PJT_MUTEX_DESTROY, PJT_MUTEX_INIT, PJT_MUTEX_LOCK, PJT_MUTEX_TRYLOCK, PJT_MUTEX_UNLOCK, PJT_ONCE, PJT_RWLOCK_DESTROY, PJT_RWLOCK_INIT, PJT_RWLOCK_RDLOCK, PJT_RWLOCK_TRYRDLOCK, PJT_RWLOCK_TRYWRLOCK, PJT_RWLOCK_UNLOCK, PJT_RWLOCK_WRLOCK, PJT_SELF, PJT_SETCANCELSTATE, PJT_SETCANCELTYPE, PJT_SETSPECIFIC, PJT_SIGMASK, PJT_TESTCANCEL, PJT_CLEANUP_POP_IMP, PJT_CLEANUP_PUSH_IMP, PJT_CANCEL_ENTER, PJT_CANCEL_LEAVE, PJT_MUTEX_CONSISTENT, PJT_MUTEXATTR_GETROBUST, PJT_MUTEXATTR_SETROBUST, PJT_GETTHREADID_NP, PJT_ATTR_GET_NP, PJT_MAX } pjt_index_t; typedef int (*pthread_func_t)(void); typedef pthread_func_t pthread_func_entry_t[2]; extern pthread_func_entry_t __thr_jtable[]; void __set_error_selector(int *(*arg)(void)); int _pthread_mutex_init_calloc_cb_stub(pthread_mutex_t *mutex, void *(calloc_cb)(__size_t, __size_t)); typedef int (*interpos_func_t)(void); interpos_func_t *__libc_interposing_slot(int interposno); extern interpos_func_t __libc_interposing[] __hidden; enum { INTERPOS_accept, INTERPOS_accept4, INTERPOS_aio_suspend, INTERPOS_close, INTERPOS_connect, INTERPOS_fcntl, INTERPOS_fsync, INTERPOS_fork, INTERPOS_msync, INTERPOS_nanosleep, INTERPOS_openat, INTERPOS_poll, INTERPOS_pselect, INTERPOS_recvfrom, INTERPOS_recvmsg, INTERPOS_select, INTERPOS_sendmsg, INTERPOS_sendto, INTERPOS_setcontext, INTERPOS_sigaction, INTERPOS_sigprocmask, INTERPOS_sigsuspend, INTERPOS_sigwait, INTERPOS_sigtimedwait, INTERPOS_sigwaitinfo, INTERPOS_swapcontext, INTERPOS_system, INTERPOS_tcdrain, INTERPOS_read, INTERPOS_readv, INTERPOS_wait4, INTERPOS_write, INTERPOS_writev, INTERPOS__pthread_mutex_init_calloc_cb, INTERPOS_spinlock, INTERPOS_spinunlock, INTERPOS_kevent, INTERPOS_wait6, INTERPOS_ppoll, INTERPOS_map_stacks_exec, INTERPOS_fdatasync, INTERPOS_clock_nanosleep, INTERPOS_distribute_static_tls, INTERPOS_MAX }; /* * yplib internal interfaces */ #ifdef YP int _yp_check(char **); #endif /* * Initialise TLS for static programs */ void _init_tls(void); /* * Provides pthread_once()-like functionality for both single-threaded * and multi-threaded applications. */ int _once(pthread_once_t *, void (*)(void)); /* * Set the TLS thread pointer */ void _set_tp(void *tp); /* * This is a pointer in the C run-time startup code. It is used * by getprogname() and setprogname(). */ extern const char *__progname; /* * This function is used by the threading libraries to notify malloc that a * thread is exiting. */ void _malloc_thread_cleanup(void); /* * This function is used by the threading libraries to notify libc that a * thread is exiting, so its thread-local dtors should be called. */ void __cxa_thread_call_dtors(void); int __cxa_thread_atexit_hidden(void (*dtor_func)(void *), void *obj, void *dso_symbol) __hidden; /* * These functions are used by the threading libraries in order to protect * malloc across fork(). */ void _malloc_prefork(void); void _malloc_postfork(void); void _malloc_first_thread(void); /* * Function to clean up streams, called from abort() and exit(). */ extern void (*__cleanup)(void) __hidden; /* * Get kern.osreldate to detect ABI revisions. Explicitly * ignores value of $OSVERSION and caches result. */ int __getosreldate(void); #include #include struct aiocb; struct fd_set; struct iovec; struct kevent; struct msghdr; struct pollfd; struct rusage; struct sigaction; struct sockaddr; struct stat; struct statfs; struct timespec; struct timeval; struct timezone; struct __siginfo; struct __ucontext; struct __wrusage; enum idtype; int __sys_aio_suspend(const struct aiocb * const[], int, const struct timespec *); int __sys_accept(int, struct sockaddr *, __socklen_t *); int __sys_accept4(int, struct sockaddr *, __socklen_t *, int); int __sys_clock_gettime(__clockid_t, struct timespec *ts); int __sys_clock_nanosleep(__clockid_t, int, const struct timespec *, struct timespec *); int __sys_close(int); int __sys_close_range(unsigned, unsigned, int); int __sys_connect(int, const struct sockaddr *, __socklen_t); int __sys_fcntl(int, int, ...); int __sys_fdatasync(int); int __sys_fstat(int fd, struct stat *); int __sys_fstatfs(int fd, struct statfs *); int __sys_fstatat(int, const char *, struct stat *, int); int __sys_fsync(int); __pid_t __sys_fork(void); int __sys_ftruncate(int, __off_t); __ssize_t __sys_getdirentries(int, char *, __size_t, __off_t *); int __sys_getfsstat(struct statfs *, long, int); int __sys_gettimeofday(struct timeval *, struct timezone *); int __sys_kevent(int, const struct kevent *, int, struct kevent *, int, const struct timespec *); __off_t __sys_lseek(int, __off_t, int); void *__sys_mmap(void *, __size_t, int, int, int, __off_t); int __sys_msync(void *, __size_t, int); int __sys_nanosleep(const struct timespec *, struct timespec *); int __sys_open(const char *, int, ...); int __sys_openat(int, const char *, int, ...); int __sys_pselect(int, struct fd_set *, struct fd_set *, struct fd_set *, const struct timespec *, const __sigset_t *); int __sys_ptrace(int, __pid_t, char *, int); int __sys_poll(struct pollfd *, unsigned, int); int __sys_ppoll(struct pollfd *, unsigned, const struct timespec *, const __sigset_t *); __ssize_t __sys_pread(int, void *, __size_t, __off_t); __ssize_t __sys_pwrite(int, const void *, __size_t, __off_t); __ssize_t __sys_read(int, void *, __size_t); __ssize_t __sys_readv(int, const struct iovec *, int); __ssize_t __sys_recv(int, void *, __size_t, int); __ssize_t __sys_recvfrom(int, void *, __size_t, int, struct sockaddr *, __socklen_t *); __ssize_t __sys_recvmsg(int, struct msghdr *, int); int __sys_select(int, struct fd_set *, struct fd_set *, struct fd_set *, struct timeval *); __ssize_t __sys_sendmsg(int, const struct msghdr *, int); __ssize_t __sys_sendto(int, const void *, __size_t, int, const struct sockaddr *, __socklen_t); int __sys_setcontext(const struct __ucontext *); int __sys_sigaction(int, const struct sigaction *, struct sigaction *); int __sys_sigprocmask(int, const __sigset_t *, __sigset_t *); int __sys_sigsuspend(const __sigset_t *); int __sys_sigtimedwait(const __sigset_t *, struct __siginfo *, const struct timespec *); int __sys_sigwait(const __sigset_t *, int *); int __sys_sigwaitinfo(const __sigset_t *, struct __siginfo *); int __sys_statfs(const char *, struct statfs *); int __sys_swapcontext(struct __ucontext *, const struct __ucontext *); int __sys_thr_kill(long, int); int __sys_thr_self(long *); int __sys_truncate(const char *, __off_t); __pid_t __sys_wait4(__pid_t, int *, int, struct rusage *); __pid_t __sys_wait6(enum idtype, __id_t, int *, int, struct __wrusage *, struct __siginfo *); __ssize_t __sys_write(int, const void *, __size_t); __ssize_t __sys_writev(int, const struct iovec *, int); int __sys_shm_open2(const char *, int, __mode_t, int, const char *); int __libc_sigaction(int, const struct sigaction *, struct sigaction *) __hidden; int __libc_sigprocmask(int, const __sigset_t *, __sigset_t *) __hidden; int __libc_sigsuspend(const __sigset_t *) __hidden; int __libc_sigwait(const __sigset_t * __restrict, int * restrict sig); int __libc_system(const char *); int __libc_tcdrain(int); int __fcntl_compat(int fd, int cmd, ...); int __sys_futimens(int fd, const struct timespec *times) __hidden; int __sys_utimensat(int fd, const char *path, const struct timespec *times, int flag) __hidden; /* execve() with PATH processing to implement posix_spawnp() */ int _execvpe(const char *, char * const *, char * const *); int _elf_aux_info(int aux, void *buf, int buflen); struct dl_phdr_info; int __elf_phdr_match_addr(struct dl_phdr_info *, void *); void __init_elf_aux_vector(void); void __libc_map_stacks_exec(void); void __libc_distribute_static_tls(__size_t, void *, __size_t, __size_t); __uintptr_t __libc_static_tls_base(__size_t); void _pthread_cancel_enter(int); void _pthread_cancel_leave(int); struct _pthread_cleanup_info; void ___pthread_cleanup_push_imp(void (*)(void *), void *, struct _pthread_cleanup_info *); void ___pthread_cleanup_pop_imp(int); void __throw_constraint_handler_s(const char * restrict msg, int error); +struct __nl_cat_d; +struct _xlocale; +struct __nl_cat_d *__catopen_l(const char *name, int type, + struct _xlocale *locale); + #endif /* _LIBC_PRIVATE_H_ */ Index: head/lib/libc/nls/msgcat.c =================================================================== --- head/lib/libc/nls/msgcat.c (revision 368691) +++ head/lib/libc/nls/msgcat.c (revision 368692) @@ -1,477 +1,484 @@ /*********************************************************** Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts. Copyright 2010, Gabor Kovesdan All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that Alfalfa's name not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. If you make any modifications, bugfixes or other changes to this software we'd appreciate it if you could send a copy to us so we can keep things up-to-date. Many thanks. Kee Hinckley Alfalfa Software, Inc. 267 Allston St., #3 Cambridge, MA 02139 USA nazgul@alfalfa.com ******************************************************************/ #include __FBSDID("$FreeBSD$"); #define _NLS_PRIVATE #include "namespace.h" #include #include #include #include #include /* for ntohl() */ #include #include #include #include #include #include #include #include #include #include #include #include "un-namespace.h" #include "../locale/xlocale_private.h" +#include "libc_private.h" #define _DEFAULT_NLS_PATH "/usr/share/nls/%L/%N.cat:/usr/share/nls/%N/%L:" \ _PATH_LOCALBASE "/share/nls/%L/%N.cat:" \ _PATH_LOCALBASE "/share/nls/%N/%L" #define RLOCK(fail) { int ret; \ if (__isthreaded && \ ((ret = _pthread_rwlock_rdlock(&rwlock)) != 0)) { \ errno = ret; \ return (fail); \ }} #define WLOCK(fail) { int ret; \ if (__isthreaded && \ ((ret = _pthread_rwlock_wrlock(&rwlock)) != 0)) { \ errno = ret; \ return (fail); \ }} #define UNLOCK { if (__isthreaded) \ _pthread_rwlock_unlock(&rwlock); } #define NLERR ((nl_catd) -1) #define NLRETERR(errc) { errno = errc; return (NLERR); } #define SAVEFAIL(n, l, e) { np = calloc(1, sizeof(struct catentry)); \ if (np != NULL) { \ np->name = strdup(n); \ np->catd = NLERR; \ np->lang = (l == NULL) ? NULL : \ strdup(l); \ np->caterrno = e; \ if (np->name == NULL || \ (l != NULL && np->lang == NULL)) { \ free(np->name); \ free(np->lang); \ free(np); \ } else { \ WLOCK(NLERR); \ SLIST_INSERT_HEAD(&cache, np, \ list); \ UNLOCK; \ } \ } \ errno = e; \ } static nl_catd load_msgcat(const char *, const char *, const char *); static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; struct catentry { SLIST_ENTRY(catentry) list; char *name; char *path; int caterrno; nl_catd catd; char *lang; int refcount; }; SLIST_HEAD(listhead, catentry) cache = SLIST_HEAD_INITIALIZER(cache); nl_catd catopen(const char *name, int type) { + return (__catopen_l(name, type, __get_locale())); +} + +nl_catd +__catopen_l(const char *name, int type, locale_t locale) +{ struct stat sbuf; struct catentry *np; char *base, *cptr, *cptr1, *nlspath, *pathP, *pcode; char *plang, *pter; int saverr, spcleft; const char *lang, *tmpptr; char path[PATH_MAX]; /* sanity checking */ if (name == NULL || *name == '\0') NLRETERR(EINVAL); if (strchr(name, '/') != NULL) /* have a pathname */ lang = NULL; else { if (type == NL_CAT_LOCALE) - lang = querylocale(LC_MESSAGES_MASK, __get_locale()); + lang = querylocale(LC_MESSAGES_MASK, locale); else lang = getenv("LANG"); if (lang == NULL || *lang == '\0' || strlen(lang) > ENCODING_LEN || (lang[0] == '.' && (lang[1] == '\0' || (lang[1] == '.' && lang[2] == '\0'))) || strchr(lang, '/') != NULL) lang = "C"; } /* Try to get it from the cache first */ RLOCK(NLERR); SLIST_FOREACH(np, &cache, list) { if ((strcmp(np->name, name) == 0) && ((lang != NULL && np->lang != NULL && strcmp(np->lang, lang) == 0) || (np->lang == lang))) { if (np->caterrno != 0) { /* Found cached failing entry */ UNLOCK; NLRETERR(np->caterrno); } else { /* Found cached successful entry */ atomic_add_int(&np->refcount, 1); UNLOCK; return (np->catd); } } } UNLOCK; /* is it absolute path ? if yes, load immediately */ if (strchr(name, '/') != NULL) return (load_msgcat(name, name, lang)); /* sanity checking */ if ((plang = cptr1 = strdup(lang)) == NULL) return (NLERR); if ((cptr = strchr(cptr1, '@')) != NULL) *cptr = '\0'; pter = pcode = ""; if ((cptr = strchr(cptr1, '_')) != NULL) { *cptr++ = '\0'; pter = cptr1 = cptr; } if ((cptr = strchr(cptr1, '.')) != NULL) { *cptr++ = '\0'; pcode = cptr; } if ((nlspath = getenv("NLSPATH")) == NULL || issetugid()) nlspath = _DEFAULT_NLS_PATH; if ((base = cptr = strdup(nlspath)) == NULL) { saverr = errno; free(plang); errno = saverr; return (NLERR); } while ((nlspath = strsep(&cptr, ":")) != NULL) { pathP = path; if (*nlspath) { for (; *nlspath; ++nlspath) { if (*nlspath == '%') { switch (*(nlspath + 1)) { case 'l': tmpptr = plang; break; case 't': tmpptr = pter; break; case 'c': tmpptr = pcode; break; case 'L': tmpptr = lang; break; case 'N': tmpptr = (char *)name; break; case '%': ++nlspath; /* FALLTHROUGH */ default: if (pathP - path >= sizeof(path) - 1) goto too_long; *(pathP++) = *nlspath; continue; } ++nlspath; put_tmpptr: spcleft = sizeof(path) - (pathP - path) - 1; if (strlcpy(pathP, tmpptr, spcleft) >= spcleft) { too_long: free(plang); free(base); SAVEFAIL(name, lang, ENAMETOOLONG); NLRETERR(ENAMETOOLONG); } pathP += strlen(tmpptr); } else { if (pathP - path >= sizeof(path) - 1) goto too_long; *(pathP++) = *nlspath; } } *pathP = '\0'; if (stat(path, &sbuf) == 0) { free(plang); free(base); return (load_msgcat(path, name, lang)); } } else { tmpptr = (char *)name; --nlspath; goto put_tmpptr; } } free(plang); free(base); SAVEFAIL(name, lang, ENOENT); NLRETERR(ENOENT); } char * catgets(nl_catd catd, int set_id, int msg_id, const char *s) { struct _nls_cat_hdr *cat_hdr; struct _nls_msg_hdr *msg_hdr; struct _nls_set_hdr *set_hdr; int i, l, r, u; if (catd == NULL || catd == NLERR) { errno = EBADF; /* LINTED interface problem */ return ((char *)s); } cat_hdr = (struct _nls_cat_hdr *)catd->__data; set_hdr = (struct _nls_set_hdr *)(void *)((char *)catd->__data + sizeof(struct _nls_cat_hdr)); /* binary search, see knuth algorithm b */ l = 0; u = ntohl((u_int32_t)cat_hdr->__nsets) - 1; while (l <= u) { i = (l + u) / 2; r = set_id - ntohl((u_int32_t)set_hdr[i].__setno); if (r == 0) { msg_hdr = (struct _nls_msg_hdr *) (void *)((char *)catd->__data + sizeof(struct _nls_cat_hdr) + ntohl((u_int32_t)cat_hdr->__msg_hdr_offset)); l = ntohl((u_int32_t)set_hdr[i].__index); u = l + ntohl((u_int32_t)set_hdr[i].__nmsgs) - 1; while (l <= u) { i = (l + u) / 2; r = msg_id - ntohl((u_int32_t)msg_hdr[i].__msgno); if (r == 0) { return ((char *) catd->__data + sizeof(struct _nls_cat_hdr) + ntohl((u_int32_t) cat_hdr->__msg_txt_offset) + ntohl((u_int32_t) msg_hdr[i].__offset)); } else if (r < 0) { u = i - 1; } else { l = i + 1; } } /* not found */ goto notfound; } else if (r < 0) { u = i - 1; } else { l = i + 1; } } notfound: /* not found */ errno = ENOMSG; /* LINTED interface problem */ return ((char *)s); } static void catfree(struct catentry *np) { if (np->catd != NULL && np->catd != NLERR) { munmap(np->catd->__data, (size_t)np->catd->__size); free(np->catd); } SLIST_REMOVE(&cache, np, catentry, list); free(np->name); free(np->path); free(np->lang); free(np); } int catclose(nl_catd catd) { struct catentry *np; /* sanity checking */ if (catd == NULL || catd == NLERR) { errno = EBADF; return (-1); } /* Remove from cache if not referenced any more */ WLOCK(-1); SLIST_FOREACH(np, &cache, list) { if (catd == np->catd) { if (atomic_fetchadd_int(&np->refcount, -1) == 1) catfree(np); break; } } UNLOCK; return (0); } /* * Internal support functions */ static nl_catd load_msgcat(const char *path, const char *name, const char *lang) { struct stat st; nl_catd catd; struct catentry *np; void *data; char *copy_path, *copy_name, *copy_lang; int fd; /* path/name will never be NULL here */ /* * One more try in cache; if it was not found by name, * it might still be found by absolute path. */ RLOCK(NLERR); SLIST_FOREACH(np, &cache, list) { if ((np->path != NULL) && (strcmp(np->path, path) == 0)) { atomic_add_int(&np->refcount, 1); UNLOCK; return (np->catd); } } UNLOCK; if ((fd = _open(path, O_RDONLY | O_CLOEXEC)) == -1) { SAVEFAIL(name, lang, errno); NLRETERR(errno); } if (_fstat(fd, &st) != 0) { _close(fd); SAVEFAIL(name, lang, EFTYPE); NLRETERR(EFTYPE); } /* * If the file size cannot be held in size_t we cannot mmap() * it to the memory. Probably, this will not be a problem given * that catalog files are usually small. */ if (st.st_size > SIZE_T_MAX) { _close(fd); SAVEFAIL(name, lang, EFBIG); NLRETERR(EFBIG); } if ((data = mmap(0, (size_t)st.st_size, PROT_READ, MAP_FILE|MAP_SHARED, fd, (off_t)0)) == MAP_FAILED) { int saved_errno = errno; _close(fd); SAVEFAIL(name, lang, saved_errno); NLRETERR(saved_errno); } _close(fd); if (ntohl((u_int32_t)((struct _nls_cat_hdr *)data)->__magic) != _NLS_MAGIC) { munmap(data, (size_t)st.st_size); SAVEFAIL(name, lang, EFTYPE); NLRETERR(EFTYPE); } copy_name = strdup(name); copy_path = strdup(path); copy_lang = (lang == NULL) ? NULL : strdup(lang); catd = malloc(sizeof (*catd)); np = calloc(1, sizeof(struct catentry)); if (copy_name == NULL || copy_path == NULL || (lang != NULL && copy_lang == NULL) || catd == NULL || np == NULL) { free(copy_name); free(copy_path); free(copy_lang); free(catd); free(np); munmap(data, (size_t)st.st_size); SAVEFAIL(name, lang, ENOMEM); NLRETERR(ENOMEM); } catd->__data = data; catd->__size = (int)st.st_size; /* Caching opened catalog */ np->name = copy_name; np->path = copy_path; np->catd = catd; np->lang = copy_lang; atomic_store_int(&np->refcount, 1); WLOCK(NLERR); SLIST_INSERT_HEAD(&cache, np, list); UNLOCK; return (catd); } Index: head/lib/libc/string/Makefile.inc =================================================================== --- head/lib/libc/string/Makefile.inc (revision 368691) +++ head/lib/libc/string/Makefile.inc (revision 368692) @@ -1,103 +1,104 @@ # @(#)Makefile.inc 8.1 (Berkeley) 6/4/93 # $FreeBSD$ .PATH: ${LIBC_SRCTOP}/${LIBC_ARCH}/string ${LIBC_SRCTOP}/string .PATH: ${SRCTOP}/sys/libkern CFLAGS+= -I${LIBC_SRCTOP}/locale # machine-independent string sources MISRCS+=bcmp.c bcopy.c bzero.c explicit_bzero.c \ ffs.c ffsl.c ffsll.c fls.c flsl.c flsll.c \ memccpy.c memchr.c memrchr.c memcmp.c \ memcpy.c memmem.c memmove.c memset.c memset_s.c \ stpcpy.c stpncpy.c strcasecmp.c \ strcat.c strcasestr.c strchr.c strchrnul.c strcmp.c strcoll.c strcpy.c\ strcspn.c strdup.c strerror.c strlcat.c strlcpy.c strlen.c strmode.c \ strncat.c strncmp.c strncpy.c strndup.c strnlen.c strnstr.c \ strpbrk.c strrchr.c strsep.c strsignal.c strspn.c strstr.c strtok.c \ strxfrm.c swab.c \ timingsafe_bcmp.c \ timingsafe_memcmp.c \ wcpcpy.c wcpncpy.c wcscasecmp.c wcscat.c \ wcschr.c wcscmp.c wcscoll.c wcscpy.c wcscspn.c wcsdup.c \ wcslcat.c wcslcpy.c wcslen.c wcsncasecmp.c wcsncat.c wcsncmp.c \ wcsncpy.c wcsnlen.c wcspbrk.c \ wcsrchr.c wcsspn.c wcsstr.c wcstok.c wcswidth.c wcsxfrm.c wmemchr.c \ wmemcmp.c \ wmemcpy.c wmemmove.c wmemset.c SYM_MAPS+= ${LIBC_SRCTOP}/string/Symbol.map # machine-dependent string sources .sinclude "${LIBC_SRCTOP}/${LIBC_ARCH}/string/Makefile.inc" MAN+= bcmp.3 bcopy.3 bstring.3 bzero.3 ffs.3 index.3 memccpy.3 memchr.3 \ memcmp.3 memcpy.3 memmem.3 memmove.3 memset.3 strcasecmp.3 strcat.3 \ strchr.3 strcmp.3 strcoll.3 strcpy.3 strdup.3 strerror.3 \ string.3 strlcpy.3 strlen.3 strmode.3 strpbrk.3 strsep.3 \ strspn.3 strstr.3 strtok.3 strxfrm.3 swab.3 \ timingsafe_bcmp.3 \ wcscoll.3 wcstok.3 \ wcswidth.3 wcsxfrm.3 wmemchr.3 MLINKS+=bzero.3 explicit_bzero.3 MLINKS+=ffs.3 ffsl.3 \ ffs.3 ffsll.3 \ ffs.3 fls.3 \ ffs.3 flsl.3 \ ffs.3 flsll.3 MLINKS+=index.3 rindex.3 MLINKS+=memchr.3 memrchr.3 MLINKS+=memset.3 memset_s.3 MLINKS+=strcasecmp.3 strncasecmp.3 \ strcasecmp.3 strcasecmp_l.3 \ strcasecmp.3 strncasecmp_l.3 MLINKS+=strcat.3 strncat.3 MLINKS+=strchr.3 strrchr.3 \ strchr.3 strchrnul.3 MLINKS+=strcmp.3 strncmp.3 MLINKS+=strcoll.3 strcoll_l.3 MLINKS+=strcpy.3 stpcpy.3 \ strcpy.3 stpncpy.3 \ strcpy.3 strncpy.3 MLINKS+=strdup.3 strndup.3 MLINKS+=strerror.3 perror.3 \ + strerror.3 strerror_l.3 \ strerror.3 strerror_r.3 \ strerror.3 sys_errlist.3 \ strerror.3 sys_nerr.3 MLINKS+=strlcpy.3 strlcat.3 MLINKS+=strlen.3 strnlen.3 MLINKS+=strspn.3 strcspn.3 MLINKS+=strstr.3 strcasestr.3 \ strstr.3 strnstr.3 \ strstr.3 strcasestr_l.3 MLINKS+=strtok.3 strtok_r.3 MLINKS+=strxfrm.3 strxfrm_l.3 MLINKS+=timingsafe_bcmp.3 timingsafe_memcmp.3 MLINKS+=wmemchr.3 wcpcpy.3 \ wmemchr.3 wcpncpy.3 \ wmemchr.3 wcscasecmp.3 \ wmemchr.3 wcscat.3 \ wmemchr.3 wcschr.3 \ wmemchr.3 wcscmp.3 \ wmemchr.3 wcscpy.3 \ wmemchr.3 wcscspn.3 \ wmemchr.3 wcsdup.3 \ wmemchr.3 wcslcat.3 \ wmemchr.3 wcslcpy.3 \ wmemchr.3 wcslen.3 \ wmemchr.3 wcsncasecmp.3 \ wmemchr.3 wcsncat.3 \ wmemchr.3 wcsncmp.3 \ wmemchr.3 wcsncpy.3 \ wmemchr.3 wcsnlen.3 \ wmemchr.3 wcspbrk.3 \ wmemchr.3 wcsrchr.3 \ wmemchr.3 wcsspn.3 \ wmemchr.3 wcsstr.3 \ wmemchr.3 wmemcmp.3 \ wmemchr.3 wmemcpy.3 \ wmemchr.3 wmemmove.3 \ wmemchr.3 wmemset.3 Index: head/lib/libc/string/Symbol.map =================================================================== --- head/lib/libc/string/Symbol.map (revision 368691) +++ head/lib/libc/string/Symbol.map (revision 368692) @@ -1,115 +1,119 @@ /* * $FreeBSD$ */ FBSD_1.0 { bcmp; bcopy; memcpy; memmove; ffs; ffsl; fls; flsl; index; strchr; memccpy; memchr; memcmp; memmem; bzero; memset; strrchr; rindex; stpcpy; strcasecmp; strncasecmp; strcasestr; strcat; strcmp; strcoll; strcpy; strcspn; strdup; strerror_r; strerror; strlcat; strlcpy; strlen; strmode; strncat; strncmp; strncpy; strnstr; strpbrk; strsep; strsignal; strspn; strstr; strtok_r; strtok; strxfrm; swab; wcscat; wcschr; wcscmp; wcscoll; wcscpy; wcscspn; wcsdup; wcslcat; wcslcpy; wcslen; wcsncat; wcsncmp; wcsncpy; wcspbrk; wcsrchr; wcsspn; wcsstr; wcstok; wcswidth; wcsxfrm; wmemchr; wmemcmp; wmemcpy; wmemmove; wmemset; }; FBSD_1.1 { ffsll; flsll; memrchr; stpncpy; strndup; strnlen; wcpcpy; wcpncpy; wcscasecmp; wcsncasecmp; wcsnlen; }; FBSD_1.3 { strcasecmp_l; strcasestr_l; strchrnul; strncasecmp_l; wcswidth_l; wcwidth_l; }; FBSD_1.4 { explicit_bzero; }; FBSD_1.5 { memset_s; timingsafe_bcmp; timingsafe_memcmp; }; +FBSD_1.6 { + strerror_l; +}; + FBSDprivate_1.0 { __strtok_r; }; Index: head/lib/libc/string/strerror.3 =================================================================== --- head/lib/libc/string/strerror.3 (revision 368691) +++ head/lib/libc/string/strerror.3 (revision 368692) @@ -1,190 +1,222 @@ .\" Copyright (c) 1980, 1991, 1993 .\" The Regents of the University of California. All rights reserved. .\" .\" This code is derived from software contributed to Berkeley by .\" the American National Standards Committee X3, on Information .\" Processing Systems. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" @(#)strerror.3 8.1 (Berkeley) 6/9/93 .\" $FreeBSD$ .\" -.Dd April 5, 2011 +.Dd December 7, 2020 .Dt STRERROR 3 .Os .Sh NAME .Nm perror , .Nm strerror , +.Nm strerror_l , .Nm strerror_r , .Nm sys_errlist , .Nm sys_nerr .Nd system error messages .Sh LIBRARY .Lb libc .Sh SYNOPSIS .In stdio.h .Ft void .Fn perror "const char *string" .Vt extern const char * const sys_errlist[] ; .Vt extern const int sys_nerr ; .In string.h .Ft "char *" .Fn strerror "int errnum" +.Ft "char *" +.Fn strerror_l "int errnum" "locale_t" .Ft int .Fn strerror_r "int errnum" "char *strerrbuf" "size_t buflen" .Sh DESCRIPTION The .Fn strerror , -.Fn strerror_r +.Fn strerror_l , +.Fn strerror_r , and .Fn perror functions look up the error message string corresponding to an error number. .Pp The .Fn strerror function accepts an error number argument .Fa errnum -and returns a pointer to the corresponding -message string. +and returns a pointer to the corresponding message string +in the current locale. +.Fn strerror +is not thread-safe. +It returns a pointer to an internal static buffer that could be +overwritten by a +.Fn strerror +call from another thread. .Pp The +.Fn strerror_l +function accepts +.Fa errnum +error number and +.Fa locale +locale handle arguments and returns a pointer to a string +corresponding to the specified error in the given locale. +.Fn strerror_l +is thread-safe, its result can be only overwritten by +another call to +.Fn strerror_l +from the current thread. +.Pp +The .Fn strerror_r function renders the same result into .Fa strerrbuf for a maximum of .Fa buflen characters and returns 0 upon success. .Pp The .Fn perror function finds the error message corresponding to the current value of the global variable .Va errno .Pq Xr intro 2 and writes it, followed by a newline, to the standard error file descriptor. If the argument .Fa string is .Pf non- Dv NULL and does not point to the null character, this string is prepended to the message string and separated from it by a colon and space .Pq Dq Li ":\ " ; otherwise, only the error message string is printed. .Pp If the error number is not recognized, these functions return an error message string containing .Dq Li "Unknown error:\ " followed by the error number in decimal. The .Fn strerror and .Fn strerror_r functions return .Er EINVAL as a warning. Error numbers recognized by this implementation fall in the range 0 < .Fa errnum < .Fa sys_nerr . The number 0 is also recognized, although applications that take advantage of this are likely to use unspecified values of .Va errno . .Pp If insufficient storage is provided in .Fa strerrbuf (as specified in .Fa buflen ) to contain the error string, .Fn strerror_r returns .Er ERANGE and .Fa strerrbuf will contain an error message that has been truncated and .Dv NUL terminated to fit the length specified by .Fa buflen . .Pp The message strings can be accessed directly using the external array .Va sys_errlist . The external value .Va sys_nerr contains a count of the messages in .Va sys_errlist . The use of these variables is deprecated; -.Fn strerror +.Fn strerror , +.Fn strerror_l , or .Fn strerror_r should be used instead. .Sh SEE ALSO .Xr intro 2 , .Xr err 3 , .Xr psignal 3 .Sh STANDARDS The .Fn perror and .Fn strerror functions conform to .St -isoC-99 . The .Fn strerror_r function conforms to .St -p1003.1-2001 . +The +.Fn strerror_l +function conforms to +.St -p1003.1-2008 . .Sh HISTORY The .Fn strerror and .Fn perror functions first appeared in .Bx 4.4 . The .Fn strerror_r function was implemented in .Fx 4.4 by .An Wes Peters Aq Mt wes@FreeBSD.org . +The +.Fn strerror_l +function was added in +.Fx 13.0 . .Sh BUGS The .Fn strerror function returns its result in a static buffer which will be overwritten by subsequent calls. .Pp -The return type for -.Fn strerror -is missing a type-qualifier; it should actually be -.Vt const char * . -.Pp Programs that use the deprecated .Va sys_errlist variable often fail to compile because they declare it inconsistently. +Size of the +.Va sys_errlist +object might increase during FreeBSD lifetime, +breaking some ABI stability guarantees. Index: head/lib/libc/string/strerror.c =================================================================== --- head/lib/libc/string/strerror.c (revision 368691) +++ head/lib/libc/string/strerror.c (revision 368692) @@ -1,127 +1,151 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)strerror.c 8.1 (Berkeley) 6/4/93"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); #if defined(NLS) #include #endif #include #include #include #include #include "errlst.h" +#include "../locale/xlocale_private.h" +#include "libc_private.h" /* * Define buffer big enough to contain delimiter (": ", 2 bytes), * 64-bit signed integer converted to ASCII decimal (19 bytes) with * optional leading sign (1 byte), and a trailing NUL. */ #define EBUFSIZE (2 + 19 + 1 + 1) /* * Doing this by hand instead of linking with stdio(3) avoids bloat for * statically linked binaries. */ static void errstr(int num, const char *uprefix, char *buf, size_t len) { char *t; unsigned int uerr; char tmp[EBUFSIZE]; t = tmp + sizeof(tmp); *--t = '\0'; uerr = (num >= 0) ? num : -num; do { *--t = "0123456789"[uerr % 10]; } while (uerr /= 10); if (num < 0) *--t = '-'; *--t = ' '; *--t = ':'; strlcpy(buf, uprefix, len); strlcat(buf, t, len); } -int -strerror_r(int errnum, char *strerrbuf, size_t buflen) +static int +strerror_rl(int errnum, char *strerrbuf, size_t buflen, locale_t locale) { int retval = 0; #if defined(NLS) int saved_errno = errno; nl_catd catd; - catd = catopen("libc", NL_CAT_LOCALE); + + catd = __catopen_l("libc", NL_CAT_LOCALE, locale); #endif if (errnum < 0 || errnum >= __hidden_sys_nerr) { errstr(errnum, #if defined(NLS) - catgets(catd, 1, 0xffff, __uprefix), + catgets(catd, 1, 0xffff, __uprefix), #else - __uprefix, + __uprefix, #endif - strerrbuf, buflen); + strerrbuf, buflen); retval = EINVAL; } else { if (strlcpy(strerrbuf, #if defined(NLS) - catgets(catd, 1, errnum, __hidden_sys_errlist[errnum]), + catgets(catd, 1, errnum, __hidden_sys_errlist[errnum]), #else - __hidden_sys_errlist[errnum], + __hidden_sys_errlist[errnum], #endif - buflen) >= buflen) - retval = ERANGE; + buflen) >= buflen) + retval = ERANGE; } #if defined(NLS) catclose(catd); errno = saved_errno; #endif return (retval); } +int +strerror_r(int errnum, char *strerrbuf, size_t buflen) +{ + return (strerror_rl(errnum, strerrbuf, buflen, __get_locale())); +} + char * +strerror_l(int num, locale_t locale) +{ +#ifndef __NO_TLS + static _Thread_local char ebuf[NL_TEXTMAX]; + + if (strerror_rl(num, ebuf, sizeof(ebuf), locale) != 0) + errno = EINVAL; + return (ebuf); +#else + errno = ENOTSUP; + return (NULL); +#endif +} + +char * strerror(int num) { static char ebuf[NL_TEXTMAX]; - if (strerror_r(num, ebuf, sizeof(ebuf)) != 0) + if (strerror_rl(num, ebuf, sizeof(ebuf), __get_locale()) != 0) errno = EINVAL; return (ebuf); }