Index: head/include/xlocale/_string.h =================================================================== --- head/include/xlocale/_string.h +++ head/include/xlocale/_string.h @@ -46,6 +46,7 @@ * 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 */ Index: head/lib/libc/include/libc_private.h =================================================================== --- head/lib/libc/include/libc_private.h +++ head/lib/libc/include/libc_private.h @@ -431,4 +431,9 @@ 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 +++ head/lib/libc/nls/msgcat.c @@ -58,6 +58,7 @@ #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:" \ @@ -122,6 +123,12 @@ 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; @@ -139,7 +146,7 @@ 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"); Index: head/lib/libc/string/Makefile.inc =================================================================== --- head/lib/libc/string/Makefile.inc +++ head/lib/libc/string/Makefile.inc @@ -64,6 +64,7 @@ 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 Index: head/lib/libc/string/Symbol.map =================================================================== --- head/lib/libc/string/Symbol.map +++ head/lib/libc/string/Symbol.map @@ -110,6 +110,10 @@ 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 +++ head/lib/libc/string/strerror.3 @@ -32,12 +32,13 @@ .\" @(#)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 @@ -53,12 +54,15 @@ .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 @@ -68,10 +72,30 @@ .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 @@ -141,7 +165,8 @@ 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. @@ -160,6 +185,10 @@ .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 @@ -173,18 +202,21 @@ .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 +++ head/lib/libc/string/strerror.c @@ -45,6 +45,8 @@ #include #include "errlst.h" +#include "../locale/xlocale_private.h" +#include "libc_private.h" /* * Define buffer big enough to contain delimiter (": ", 2 bytes), @@ -78,34 +80,35 @@ 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) @@ -116,12 +119,33 @@ 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); }