setlocale (category, NULL)_can return NULL in two situations: - when the category argument is invalid, or - on Android (tested on Android 4.3).
2019-12-18 Bruno Haible <br...@clisp.org> setlocale-null: Handle NULL result from setlocale. * lib/locale.in.h (setlocale_null): Document EINVAL return value. * lib/setlocale_null.c (setlocale_null_unlocked): Handle NULL result from setlocale or _wsetlocale. diff --git a/lib/locale.in.h b/lib/locale.in.h index 5986683..67e6020 100644 --- a/lib/locale.in.h +++ b/lib/locale.in.h @@ -228,10 +228,10 @@ _GL_WARN_ON_USE (setlocale, "setlocale works differently on native Windows - " The recommended minimum buffer size is - SETLOCALE_NULL_MAX for CATEGORY != LC_ALL, and - SETLOCALE_NULL_ALL_MAX for CATEGORY == LC_ALL. - The return value is an error code: 0 if the call is successful, ERANGE if - BUFSIZE is smaller than the length needed size (including the trailing NUL - byte). In the latter case, a truncated result is returned in BUF, but - still NUL-terminated if BUFSIZE > 0. + The return value is an error code: 0 if the call is successful, EINVAL if + CATEGORY is invalid, or ERANGE if BUFSIZE is smaller than the length needed + size (including the trailing NUL byte). In the latter case, a truncated + result is returned in BUF, but still NUL-terminated if BUFSIZE > 0. For this call to be multithread-safe, *all* calls to setlocale (CATEGORY, NULL) in all other threads must have been converted to use setlocale_null as well, and the other threads must not make other diff --git -w a/lib/setlocale_null.c b/lib/setlocale_null.c index 350eca7..8072a45 100644 --- a/lib/setlocale_null.c +++ b/lib/setlocale_null.c @@ -63,6 +63,19 @@ setlocale_null_unlocked (int category, char *buf, size_t bufsize) on _wsetlocale() and uses malloc() for the result. We are better off using _wsetlocale() directly. */ const wchar_t *result = _wsetlocale (category, NULL); + + if (result == NULL) + { + /* CATEGORY is invalid. */ + if (bufsize > 0) + /* Return an empty string in BUF. + This is a convenience for callers that don't want to write explicit + code for handling EINVAL. */ + buf[0] = '\0'; + return EINVAL; + } + else + { size_t length = wcslen (result); if (length < bufsize) { @@ -90,8 +103,45 @@ setlocale_null_unlocked (int category, char *buf, size_t bufsize) } return ERANGE; } + } #else const char *result = setlocale (category, NULL); + +# ifdef __ANDROID__ + if (result == NULL) + switch (category) + { + case LC_CTYPE: + case LC_NUMERIC: + case LC_TIME: + case LC_COLLATE: + case LC_MONETARY: + case LC_MESSAGES: + case LC_ALL: + case LC_PAPER: + case LC_NAME: + case LC_ADDRESS: + case LC_TELEPHONE: + case LC_MEASUREMENT: + result = "C"; + break; + default: + break; + } +# endif + + if (result == NULL) + { + /* CATEGORY is invalid. */ + if (bufsize > 0) + /* Return an empty string in BUF. + This is a convenience for callers that don't want to write explicit + code for handling EINVAL. */ + buf[0] = '\0'; + return EINVAL; + } + else + { size_t length = strlen (result); if (length < bufsize) { @@ -110,6 +160,7 @@ setlocale_null_unlocked (int category, char *buf, size_t bufsize) } return ERANGE; } + } #endif }