diff --git a/libcxx/docs/ReleaseNotes/20.rst b/libcxx/docs/ReleaseNotes/20.rst index a72fd2d5db9e26..c8a07fb8b73348 100644 --- a/libcxx/docs/ReleaseNotes/20.rst +++ b/libcxx/docs/ReleaseNotes/20.rst @@ -152,6 +152,9 @@ ABI Affecting Changes - When using the MSVC ABI, this change results in some classes having a completely different memory layout, so this is a genuine ABI break. However, the library does not currently guarantee ABI stability on MSVC platforms. +- The localization support base API has been reimplemented, leading to different functions being exported from the + libc++ built library on Windows and Windows-like platforms. + Build System Changes -------------------- diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index 10e5a15c855c81..ba392e5883ff39 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -503,12 +503,12 @@ set(files __locale_dir/locale_base_api/ibm.h __locale_dir/locale_base_api/musl.h __locale_dir/locale_base_api/openbsd.h - __locale_dir/locale_base_api/win32.h __locale_dir/locale_guard.h __locale_dir/pad_and_output.h __locale_dir/support/apple.h __locale_dir/support/bsd_like.h __locale_dir/support/freebsd.h + __locale_dir/support/windows.h __math/abs.h __math/copysign.h __math/error_functions.h diff --git a/libcxx/include/__locale_dir/locale_base_api.h b/libcxx/include/__locale_dir/locale_base_api.h index 8ed4c29cb8732f..7e3c205c04a780 100644 --- a/libcxx/include/__locale_dir/locale_base_api.h +++ b/libcxx/include/__locale_dir/locale_base_api.h @@ -98,15 +98,15 @@ # include <__locale_dir/support/apple.h> #elif defined(__FreeBSD__) # include <__locale_dir/support/freebsd.h> +#elif defined(_LIBCPP_MSVCRT_LIKE) +# include <__locale_dir/support/windows.h> #else // TODO: This is a temporary definition to bridge between the old way we defined the locale base API // (by providing global non-reserved names) and the new API. As we move individual platforms // towards the new way of defining the locale base API, this should disappear since each platform // will define those directly. -# if defined(_LIBCPP_MSVCRT_LIKE) -# include <__locale_dir/locale_base_api/win32.h> -# elif defined(_AIX) || defined(__MVS__) +# if defined(_AIX) || defined(__MVS__) # include <__locale_dir/locale_base_api/ibm.h> # elif defined(__ANDROID__) # include <__locale_dir/locale_base_api/android.h> diff --git a/libcxx/include/__locale_dir/locale_base_api/win32.h b/libcxx/include/__locale_dir/locale_base_api/win32.h deleted file mode 100644 index f488a0dc0d69b3..00000000000000 --- a/libcxx/include/__locale_dir/locale_base_api/win32.h +++ /dev/null @@ -1,235 +0,0 @@ -// -*- C++ -*- -//===-----------------------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#ifndef _LIBCPP___LOCALE_DIR_LOCALE_BASE_API_WIN32_H -#define _LIBCPP___LOCALE_DIR_LOCALE_BASE_API_WIN32_H - -#include <__config> -#include -#include // _locale_t -#include -#include - -#define _X_ALL LC_ALL -#define _X_COLLATE LC_COLLATE -#define _X_CTYPE LC_CTYPE -#define _X_MONETARY LC_MONETARY -#define _X_NUMERIC LC_NUMERIC -#define _X_TIME LC_TIME -#define _X_MAX LC_MAX -#define _X_MESSAGES 6 -#define _NCAT (_X_MESSAGES + 1) - -#define _CATMASK(n) ((1 << (n)) >> 1) -#define _M_COLLATE _CATMASK(_X_COLLATE) -#define _M_CTYPE _CATMASK(_X_CTYPE) -#define _M_MONETARY _CATMASK(_X_MONETARY) -#define _M_NUMERIC _CATMASK(_X_NUMERIC) -#define _M_TIME _CATMASK(_X_TIME) -#define _M_MESSAGES _CATMASK(_X_MESSAGES) -#define _M_ALL (_CATMASK(_NCAT) - 1) - -#define LC_COLLATE_MASK _M_COLLATE -#define LC_CTYPE_MASK _M_CTYPE -#define LC_MONETARY_MASK _M_MONETARY -#define LC_NUMERIC_MASK _M_NUMERIC -#define LC_TIME_MASK _M_TIME -#define LC_MESSAGES_MASK _M_MESSAGES -#define LC_ALL_MASK \ - (LC_COLLATE_MASK | LC_CTYPE_MASK | LC_MESSAGES_MASK | LC_MONETARY_MASK | LC_NUMERIC_MASK | LC_TIME_MASK) - -class __lconv_storage { -public: - __lconv_storage(const lconv* __lc_input) { - __lc_ = *__lc_input; - - __decimal_point_ = __lc_input->decimal_point; - __thousands_sep_ = __lc_input->thousands_sep; - __grouping_ = __lc_input->grouping; - __int_curr_symbol_ = __lc_input->int_curr_symbol; - __currency_symbol_ = __lc_input->currency_symbol; - __mon_decimal_point_ = __lc_input->mon_decimal_point; - __mon_thousands_sep_ = __lc_input->mon_thousands_sep; - __mon_grouping_ = __lc_input->mon_grouping; - __positive_sign_ = __lc_input->positive_sign; - __negative_sign_ = __lc_input->negative_sign; - - __lc_.decimal_point = const_cast(__decimal_point_.c_str()); - __lc_.thousands_sep = const_cast(__thousands_sep_.c_str()); - __lc_.grouping = const_cast(__grouping_.c_str()); - __lc_.int_curr_symbol = const_cast(__int_curr_symbol_.c_str()); - __lc_.currency_symbol = const_cast(__currency_symbol_.c_str()); - __lc_.mon_decimal_point = const_cast(__mon_decimal_point_.c_str()); - __lc_.mon_thousands_sep = const_cast(__mon_thousands_sep_.c_str()); - __lc_.mon_grouping = const_cast(__mon_grouping_.c_str()); - __lc_.positive_sign = const_cast(__positive_sign_.c_str()); - __lc_.negative_sign = const_cast(__negative_sign_.c_str()); - } - - lconv* __get() { return &__lc_; } - -private: - lconv __lc_; - std::string __decimal_point_; - std::string __thousands_sep_; - std::string __grouping_; - std::string __int_curr_symbol_; - std::string __currency_symbol_; - std::string __mon_decimal_point_; - std::string __mon_thousands_sep_; - std::string __mon_grouping_; - std::string __positive_sign_; - std::string __negative_sign_; -}; - -class locale_t { -public: - locale_t() : __locale_(nullptr), __locale_str_(nullptr), __lc_(nullptr) {} - locale_t(std::nullptr_t) : __locale_(nullptr), __locale_str_(nullptr), __lc_(nullptr) {} - locale_t(_locale_t __xlocale, const char* __xlocale_str) - : __locale_(__xlocale), __locale_str_(__xlocale_str), __lc_(nullptr) {} - locale_t(const locale_t& __l) : __locale_(__l.__locale_), __locale_str_(__l.__locale_str_), __lc_(nullptr) {} - - ~locale_t() { delete __lc_; } - - locale_t& operator=(const locale_t& __l) { - __locale_ = __l.__locale_; - __locale_str_ = __l.__locale_str_; - // __lc_ not copied - return *this; - } - - friend bool operator==(const locale_t& __left, const locale_t& __right) { - return __left.__locale_ == __right.__locale_; - } - - friend bool operator==(const locale_t& __left, int __right) { return __left.__locale_ == nullptr && __right == 0; } - - friend bool operator==(const locale_t& __left, long long __right) { - return __left.__locale_ == nullptr && __right == 0; - } - - friend bool operator==(const locale_t& __left, std::nullptr_t) { return __left.__locale_ == nullptr; } - - friend bool operator==(int __left, const locale_t& __right) { return __left == 0 && nullptr == __right.__locale_; } - - friend bool operator==(std::nullptr_t, const locale_t& __right) { return nullptr == __right.__locale_; } - - friend bool operator!=(const locale_t& __left, const locale_t& __right) { return !(__left == __right); } - - friend bool operator!=(const locale_t& __left, int __right) { return !(__left == __right); } - - friend bool operator!=(const locale_t& __left, long long __right) { return !(__left == __right); } - - friend bool operator!=(const locale_t& __left, std::nullptr_t __right) { return !(__left == __right); } - - friend bool operator!=(int __left, const locale_t& __right) { return !(__left == __right); } - - friend bool operator!=(std::nullptr_t __left, const locale_t& __right) { return !(__left == __right); } - - operator bool() const { return __locale_ != nullptr; } - - const char* __get_locale() const { return __locale_str_; } - - operator _locale_t() const { return __locale_; } - - lconv* __store_lconv(const lconv* __input_lc) { - delete __lc_; - __lc_ = new __lconv_storage(__input_lc); - return __lc_->__get(); - } - -private: - _locale_t __locale_; - const char* __locale_str_; - __lconv_storage* __lc_ = nullptr; -}; - -// Locale management functions -#define freelocale _free_locale -// FIXME: base currently unused. Needs manual work to construct the new locale -locale_t newlocale(int __mask, const char* __locale, locale_t __base); -// uselocale can't be implemented on Windows because Windows allows partial modification -// of thread-local locale and so _get_current_locale() returns a copy while uselocale does -// not create any copies. -// We can still implement raii even without uselocale though. - -lconv* localeconv_l(locale_t& __loc); -size_t mbrlen_l(const char* __restrict __s, size_t __n, mbstate_t* __restrict __ps, locale_t __loc); -size_t mbsrtowcs_l( - wchar_t* __restrict __dst, const char** __restrict __src, size_t __len, mbstate_t* __restrict __ps, locale_t __loc); -size_t wcrtomb_l(char* __restrict __s, wchar_t __wc, mbstate_t* __restrict __ps, locale_t __loc); -size_t mbrtowc_l( - wchar_t* __restrict __pwc, const char* __restrict __s, size_t __n, mbstate_t* __restrict __ps, locale_t __loc); -size_t mbsnrtowcs_l(wchar_t* __restrict __dst, - const char** __restrict __src, - size_t __nms, - size_t __len, - mbstate_t* __restrict __ps, - locale_t __loc); -size_t wcsnrtombs_l(char* __restrict __dst, - const wchar_t** __restrict __src, - size_t __nwc, - size_t __len, - mbstate_t* __restrict __ps, - locale_t __loc); -wint_t btowc_l(int __c, locale_t __loc); -int wctob_l(wint_t __c, locale_t __loc); - -decltype(MB_CUR_MAX) MB_CUR_MAX_L(locale_t __l); - -// the *_l functions are prefixed on Windows, only available for msvcr80+, VS2005+ -#define mbtowc_l _mbtowc_l -#define strtoll_l _strtoi64_l -#define strtoull_l _strtoui64_l -#define strtod_l _strtod_l -#if defined(_LIBCPP_MSVCRT) -# define strtof_l _strtof_l -# define strtold_l _strtold_l -#else -_LIBCPP_EXPORTED_FROM_ABI float strtof_l(const char*, char**, locale_t); -_LIBCPP_EXPORTED_FROM_ABI long double strtold_l(const char*, char**, locale_t); -#endif -inline _LIBCPP_HIDE_FROM_ABI int islower_l(int __c, _locale_t __loc) { return _islower_l((int)__c, __loc); } - -inline _LIBCPP_HIDE_FROM_ABI int isupper_l(int __c, _locale_t __loc) { return _isupper_l((int)__c, __loc); } - -#define isdigit_l _isdigit_l -#define isxdigit_l _isxdigit_l -#define strcoll_l _strcoll_l -#define strxfrm_l _strxfrm_l -#define wcscoll_l _wcscoll_l -#define wcsxfrm_l _wcsxfrm_l -#define toupper_l _toupper_l -#define tolower_l _tolower_l -#define iswspace_l _iswspace_l -#define iswprint_l _iswprint_l -#define iswcntrl_l _iswcntrl_l -#define iswupper_l _iswupper_l -#define iswlower_l _iswlower_l -#define iswalpha_l _iswalpha_l -#define iswdigit_l _iswdigit_l -#define iswpunct_l _iswpunct_l -#define iswxdigit_l _iswxdigit_l -#define towupper_l _towupper_l -#define towlower_l _towlower_l -#if defined(__MINGW32__) && __MSVCRT_VERSION__ < 0x0800 -_LIBCPP_EXPORTED_FROM_ABI size_t strftime_l(char* ret, size_t n, const char* format, const struct tm* tm, locale_t loc); -#else -# define strftime_l _strftime_l -#endif -#define sscanf_l(__s, __l, __f, ...) _sscanf_l(__s, __f, __l, __VA_ARGS__) -_LIBCPP_EXPORTED_FROM_ABI int snprintf_l(char* __ret, size_t __n, locale_t __loc, const char* __format, ...); -_LIBCPP_EXPORTED_FROM_ABI int asprintf_l(char** __ret, locale_t __loc, const char* __format, ...); -_LIBCPP_EXPORTED_FROM_ABI int vasprintf_l(char** __ret, locale_t __loc, const char* __format, va_list __ap); - -// not-so-pressing FIXME: use locale to determine blank characters -inline int iswblank_l(wint_t __c, locale_t /*loc*/) { return (__c == L' ' || __c == L'\t'); } - -#endif // _LIBCPP___LOCALE_DIR_LOCALE_BASE_API_WIN32_H diff --git a/libcxx/include/__locale_dir/locale_guard.h b/libcxx/include/__locale_dir/locale_guard.h index e0c414c001c41f..93d38334f31323 100644 --- a/libcxx/include/__locale_dir/locale_guard.h +++ b/libcxx/include/__locale_dir/locale_guard.h @@ -21,7 +21,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD #if defined(_LIBCPP_MSVCRT_LIKE) struct __locale_guard { - __locale_guard(locale_t __l) : __status(_configthreadlocale(_ENABLE_PER_THREAD_LOCALE)) { + __locale_guard(__locale::__locale_t __l) : __status(_configthreadlocale(_ENABLE_PER_THREAD_LOCALE)) { // Setting the locale can be expensive even when the locale given is // already the current locale, so do an explicit check to see if the // current locale is already the one we want. diff --git a/libcxx/include/__locale_dir/support/windows.h b/libcxx/include/__locale_dir/support/windows.h new file mode 100644 index 00000000000000..fd6aab8de9ac8d --- /dev/null +++ b/libcxx/include/__locale_dir/support/windows.h @@ -0,0 +1,294 @@ +//===-----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___LOCALE_DIR_SUPPORT_WINDOWS_H +#define _LIBCPP___LOCALE_DIR_SUPPORT_WINDOWS_H + +#include <__config> +#include <__cstddef/nullptr_t.h> +#include <__utility/forward.h> +#include // std::lconv & friends +#include +#include // ::_isupper_l & friends +#include // ::_locale_t +#include // ::_sscanf_l +#include // ::_strtod_l & friends +#include // ::_strcoll_l +#include +#include // ::_strftime_l + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +#define _CATMASK(n) ((1 << (n)) >> 1) +#define LC_COLLATE_MASK _CATMASK(LC_COLLATE) +#define LC_CTYPE_MASK _CATMASK(LC_CTYPE) +#define LC_MONETARY_MASK _CATMASK(LC_MONETARY) +#define LC_NUMERIC_MASK _CATMASK(LC_NUMERIC) +#define LC_TIME_MASK _CATMASK(LC_TIME) +#define LC_MESSAGES_MASK _CATMASK(6) +#define LC_ALL_MASK \ + (LC_COLLATE_MASK | LC_CTYPE_MASK | LC_MESSAGES_MASK | LC_MONETARY_MASK | LC_NUMERIC_MASK | LC_TIME_MASK) + +_LIBCPP_BEGIN_NAMESPACE_STD +namespace __locale { + +class __lconv_storage { +public: + __lconv_storage(const lconv* __lc_input) { + __lc_ = *__lc_input; + + __decimal_point_ = __lc_input->decimal_point; + __thousands_sep_ = __lc_input->thousands_sep; + __grouping_ = __lc_input->grouping; + __int_curr_symbol_ = __lc_input->int_curr_symbol; + __currency_symbol_ = __lc_input->currency_symbol; + __mon_decimal_point_ = __lc_input->mon_decimal_point; + __mon_thousands_sep_ = __lc_input->mon_thousands_sep; + __mon_grouping_ = __lc_input->mon_grouping; + __positive_sign_ = __lc_input->positive_sign; + __negative_sign_ = __lc_input->negative_sign; + + __lc_.decimal_point = const_cast(__decimal_point_.c_str()); + __lc_.thousands_sep = const_cast(__thousands_sep_.c_str()); + __lc_.grouping = const_cast(__grouping_.c_str()); + __lc_.int_curr_symbol = const_cast(__int_curr_symbol_.c_str()); + __lc_.currency_symbol = const_cast(__currency_symbol_.c_str()); + __lc_.mon_decimal_point = const_cast(__mon_decimal_point_.c_str()); + __lc_.mon_thousands_sep = const_cast(__mon_thousands_sep_.c_str()); + __lc_.mon_grouping = const_cast(__mon_grouping_.c_str()); + __lc_.positive_sign = const_cast(__positive_sign_.c_str()); + __lc_.negative_sign = const_cast(__negative_sign_.c_str()); + } + + std::lconv* __get() { return &__lc_; } + +private: + std::lconv __lc_; + std::string __decimal_point_; + std::string __thousands_sep_; + std::string __grouping_; + std::string __int_curr_symbol_; + std::string __currency_symbol_; + std::string __mon_decimal_point_; + std::string __mon_thousands_sep_; + std::string __mon_grouping_; + std::string __positive_sign_; + std::string __negative_sign_; +}; + +// +// Locale management +// +class __locale_t { +public: + __locale_t() : __locale_(nullptr), __locale_str_(nullptr), __lc_(nullptr) {} + __locale_t(std::nullptr_t) : __locale_(nullptr), __locale_str_(nullptr), __lc_(nullptr) {} + __locale_t(::_locale_t __loc, const char* __loc_str) : __locale_(__loc), __locale_str_(__loc_str), __lc_(nullptr) {} + __locale_t(const __locale_t& __loc) + : __locale_(__loc.__locale_), __locale_str_(__loc.__locale_str_), __lc_(nullptr) {} + + ~__locale_t() { delete __lc_; } + + __locale_t& operator=(const __locale_t& __loc) { + __locale_ = __loc.__locale_; + __locale_str_ = __loc.__locale_str_; + // __lc_ not copied + return *this; + } + + friend bool operator==(const __locale_t& __left, const __locale_t& __right) { + return __left.__locale_ == __right.__locale_; + } + + friend bool operator==(const __locale_t& __left, int __right) { return __left.__locale_ == nullptr && __right == 0; } + + friend bool operator==(const __locale_t& __left, long long __right) { + return __left.__locale_ == nullptr && __right == 0; + } + + friend bool operator==(const __locale_t& __left, std::nullptr_t) { return __left.__locale_ == nullptr; } + + friend bool operator==(int __left, const __locale_t& __right) { return __left == 0 && nullptr == __right.__locale_; } + + friend bool operator==(std::nullptr_t, const __locale_t& __right) { return nullptr == __right.__locale_; } + + friend bool operator!=(const __locale_t& __left, const __locale_t& __right) { return !(__left == __right); } + + friend bool operator!=(const __locale_t& __left, int __right) { return !(__left == __right); } + + friend bool operator!=(const __locale_t& __left, long long __right) { return !(__left == __right); } + + friend bool operator!=(const __locale_t& __left, std::nullptr_t __right) { return !(__left == __right); } + + friend bool operator!=(int __left, const __locale_t& __right) { return !(__left == __right); } + + friend bool operator!=(std::nullptr_t __left, const __locale_t& __right) { return !(__left == __right); } + + operator bool() const { return __locale_ != nullptr; } + + const char* __get_locale() const { return __locale_str_; } + + operator ::_locale_t() const { return __locale_; } + + std::lconv* __store_lconv(const std::lconv* __input_lc) { + delete __lc_; + __lc_ = new __lconv_storage(__input_lc); + return __lc_->__get(); + } + +private: + ::_locale_t __locale_; + const char* __locale_str_; + __lconv_storage* __lc_ = nullptr; +}; + +// __uselocale can't be implemented on Windows because Windows allows partial modification +// of thread-local locale and so _get_current_locale() returns a copy while __uselocale does +// not create any copies. We can still implement RAII even without __uselocale though. +__locale_t __uselocale(__locale_t) = delete; +_LIBCPP_EXPORTED_FROM_ABI __locale_t __newlocale(int __mask, const char* __locale, __locale_t __base); +inline _LIBCPP_HIDE_FROM_ABI void __freelocale(__locale_t __loc) { ::_free_locale(__loc); } +_LIBCPP_EXPORTED_FROM_ABI lconv* __localeconv(__locale_t& __loc); + +// +// Strtonum functions +// + +// the *_l functions are prefixed on Windows, only available for msvcr80+, VS2005+ +#if defined(_LIBCPP_MSVCRT) +inline _LIBCPP_HIDE_FROM_ABI float __strtof(const char* __nptr, char** __endptr, __locale_t __loc) { + return ::_strtof_l(__nptr, __endptr, __loc); +} +inline _LIBCPP_HIDE_FROM_ABI long double __strtold(const char* __nptr, char** __endptr, __locale_t __loc) { + return ::_strtold_l(__nptr, __endptr, __loc); +} +#else +_LIBCPP_EXPORTED_FROM_ABI float __strtof(const char*, char**, __locale_t); +_LIBCPP_EXPORTED_FROM_ABI long double __strtold(const char*, char**, __locale_t); +#endif + +inline _LIBCPP_HIDE_FROM_ABI double __strtod(const char* __nptr, char** __endptr, __locale_t __loc) { + return ::_strtod_l(__nptr, __endptr, __loc); +} + +inline _LIBCPP_HIDE_FROM_ABI long long __strtoll(const char* __nptr, char** __endptr, int __base, __locale_t __loc) { + return ::_strtoi64_l(__nptr, __endptr, __base, __loc); +} +inline _LIBCPP_HIDE_FROM_ABI unsigned long long +__strtoull(const char* __nptr, char** __endptr, int __base, __locale_t __loc) { + return ::_strtoui64_l(__nptr, __endptr, __base, __loc); +} + +// +// Character manipulation functions +// +inline _LIBCPP_HIDE_FROM_ABI int __islower(int __c, __locale_t __loc) { return _islower_l(__c, __loc); } + +inline _LIBCPP_HIDE_FROM_ABI int __isupper(int __c, __locale_t __loc) { return _isupper_l(__c, __loc); } + +inline _LIBCPP_HIDE_FROM_ABI int __isdigit(int __c, __locale_t __loc) { return _isdigit_l(__c, __loc); } + +inline _LIBCPP_HIDE_FROM_ABI int __isxdigit(int __c, __locale_t __loc) { return _isxdigit_l(__c, __loc); } + +inline _LIBCPP_HIDE_FROM_ABI int __toupper(int __c, __locale_t __loc) { return ::_toupper_l(__c, __loc); } + +inline _LIBCPP_HIDE_FROM_ABI int __tolower(int __c, __locale_t __loc) { return ::_tolower_l(__c, __loc); } + +inline _LIBCPP_HIDE_FROM_ABI int __strcoll(const char* __s1, const char* __s2, __locale_t __loc) { + return ::_strcoll_l(__s1, __s2, __loc); +} + +inline _LIBCPP_HIDE_FROM_ABI size_t __strxfrm(char* __dest, const char* __src, size_t __n, __locale_t __loc) { + return ::_strxfrm_l(__dest, __src, __n, __loc); +} + +#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS +inline _LIBCPP_HIDE_FROM_ABI int __iswspace(wint_t __c, __locale_t __loc) { return ::_iswspace_l(__c, __loc); } +inline _LIBCPP_HIDE_FROM_ABI int __iswprint(wint_t __c, __locale_t __loc) { return ::_iswprint_l(__c, __loc); } +inline _LIBCPP_HIDE_FROM_ABI int __iswcntrl(wint_t __c, __locale_t __loc) { return ::_iswcntrl_l(__c, __loc); } +inline _LIBCPP_HIDE_FROM_ABI int __iswupper(wint_t __c, __locale_t __loc) { return ::_iswupper_l(__c, __loc); } +inline _LIBCPP_HIDE_FROM_ABI int __iswlower(wint_t __c, __locale_t __loc) { return ::_iswlower_l(__c, __loc); } +inline _LIBCPP_HIDE_FROM_ABI int __iswalpha(wint_t __c, __locale_t __loc) { return ::_iswalpha_l(__c, __loc); } +// TODO: use locale to determine blank characters +inline _LIBCPP_HIDE_FROM_ABI int __iswblank(wint_t __c, __locale_t /*loc*/) { return (__c == L' ' || __c == L'\t'); } +inline _LIBCPP_HIDE_FROM_ABI int __iswdigit(wint_t __c, __locale_t __loc) { return ::_iswdigit_l(__c, __loc); } +inline _LIBCPP_HIDE_FROM_ABI int __iswpunct(wint_t __c, __locale_t __loc) { return ::_iswpunct_l(__c, __loc); } +inline _LIBCPP_HIDE_FROM_ABI int __iswxdigit(wint_t __c, __locale_t __loc) { return ::_iswxdigit_l(__c, __loc); } +inline _LIBCPP_HIDE_FROM_ABI wint_t __towupper(wint_t __c, __locale_t __loc) { return ::_towupper_l(__c, __loc); } +inline _LIBCPP_HIDE_FROM_ABI wint_t __towlower(wint_t __c, __locale_t __loc) { return ::_towlower_l(__c, __loc); } + +inline _LIBCPP_HIDE_FROM_ABI int __wcscoll(const wchar_t* __ws1, const wchar_t* __ws2, __locale_t __loc) { + return ::_wcscoll_l(__ws1, __ws2, __loc); +} + +inline _LIBCPP_HIDE_FROM_ABI size_t __wcsxfrm(wchar_t* __dest, const wchar_t* __src, size_t __n, __locale_t __loc) { + return ::_wcsxfrm_l(__dest, __src, __n, __loc); +} +#endif // !_LIBCPP_HAS_NO_WIDE_CHARACTERS + +#if defined(__MINGW32__) && __MSVCRT_VERSION__ < 0x0800 +_LIBCPP_EXPORTED_FROM_ABI size_t __strftime(char*, size_t, const char*, const struct tm*, __locale_t); +#else +inline _LIBCPP_HIDE_FROM_ABI size_t +__strftime(char* __ret, size_t __n, const char* __format, const struct tm* __tm, __locale_t __loc) { + return ::_strftime_l(__ret, __n, __format, __tm, __loc); +} +#endif + +// +// Other functions +// +_LIBCPP_EXPORTED_FROM_ABI decltype(MB_CUR_MAX) __mb_len_max(__locale_t); +_LIBCPP_EXPORTED_FROM_ABI wint_t __btowc(int, __locale_t); +_LIBCPP_EXPORTED_FROM_ABI int __wctob(wint_t, __locale_t); +_LIBCPP_EXPORTED_FROM_ABI size_t +__wcsnrtombs(char* __restrict, const wchar_t** __restrict, size_t, size_t, mbstate_t* __restrict, __locale_t); +_LIBCPP_EXPORTED_FROM_ABI size_t __wcrtomb(char* __restrict, wchar_t, mbstate_t* __restrict, __locale_t); +_LIBCPP_EXPORTED_FROM_ABI size_t +__mbsnrtowcs(wchar_t* __restrict, const char** __restrict, size_t, size_t, mbstate_t* __restrict, __locale_t); +_LIBCPP_EXPORTED_FROM_ABI size_t +__mbrtowc(wchar_t* __restrict, const char* __restrict, size_t, mbstate_t* __restrict, __locale_t); + +inline _LIBCPP_HIDE_FROM_ABI int __mbtowc(wchar_t* __pwc, const char* __pmb, size_t __max, __locale_t __loc) { + return ::_mbtowc_l(__pwc, __pmb, __max, __loc); +} + +_LIBCPP_EXPORTED_FROM_ABI size_t __mbrlen(const char* __restrict, size_t, mbstate_t* __restrict, __locale_t); + +_LIBCPP_EXPORTED_FROM_ABI size_t +__mbsrtowcs(wchar_t* __restrict, const char** __restrict, size_t, mbstate_t* __restrict, __locale_t); + +_LIBCPP_EXPORTED_FROM_ABI _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 4, 5) int __snprintf( + char* __ret, size_t __n, __locale_t __loc, const char* __format, ...); + +_LIBCPP_EXPORTED_FROM_ABI +_LIBCPP_ATTRIBUTE_FORMAT(__printf__, 3, 4) int __asprintf(char** __ret, __locale_t __loc, const char* __format, ...); + +_LIBCPP_DIAGNOSTIC_PUSH +_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wgcc-compat") +_LIBCPP_GCC_DIAGNOSTIC_IGNORED("-Wformat-nonliteral") // GCC doesn't support [[gnu::format]] on variadic templates +#ifdef _LIBCPP_COMPILER_CLANG_BASED +# define _LIBCPP_VARIADIC_ATTRIBUTE_FORMAT(...) _LIBCPP_ATTRIBUTE_FORMAT(__VA_ARGS__) +#else +# define _LIBCPP_VARIADIC_ATTRIBUTE_FORMAT(...) /* nothing */ +#endif + +template +_LIBCPP_HIDE_FROM_ABI _LIBCPP_VARIADIC_ATTRIBUTE_FORMAT(__scanf__, 3, 4) int __sscanf( + const char* __dest, __locale_t __loc, const char* __format, _Args&&... __args) { + return ::_sscanf_l(__dest, __format, __loc, std::forward<_Args>(__args)...); +} +_LIBCPP_DIAGNOSTIC_POP +#undef _LIBCPP_VARIADIC_ATTRIBUTE_FORMAT + +} // namespace __locale +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___LOCALE_DIR_SUPPORT_WINDOWS_H diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap index 8d862e9f9ba361..ba45af273f4842 100644 --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -1479,6 +1479,7 @@ module std [system] { textual header "__locale_dir/support/apple.h" textual header "__locale_dir/support/bsd_like.h" textual header "__locale_dir/support/freebsd.h" + textual header "__locale_dir/support/windows.h" } module locale_base_api { @@ -1489,7 +1490,6 @@ module std [system] { textual header "__locale_dir/locale_base_api/ibm.h" textual header "__locale_dir/locale_base_api/musl.h" textual header "__locale_dir/locale_base_api/openbsd.h" - textual header "__locale_dir/locale_base_api/win32.h" } export * } diff --git a/libcxx/src/support/win32/locale_win32.cpp b/libcxx/src/support/win32/locale_win32.cpp index 2a08e97b8645b4..0a537d6997ca23 100644 --- a/libcxx/src/support/win32/locale_win32.cpp +++ b/libcxx/src/support/win32/locale_win32.cpp @@ -6,125 +6,181 @@ // //===----------------------------------------------------------------------===// -#include // va_start, va_end -#include -#include -#include - #include <__locale_dir/locale_guard.h> +#include <__locale_dir/support/windows.h> +#include // std::localeconv() & friends +#include // va_start & friends +#include +#include // std::vsnprintf & friends +#include // std::strtof & friends +#include // std::strftime +#include // wide char manipulation -int __libcpp_vasprintf(char** sptr, const char* __restrict fmt, va_list ap); +_LIBCPP_BEGIN_NAMESPACE_STD +namespace __locale { +// +// Locale management +// // FIXME: base and mask currently unused. Needs manual work to construct the new locale -locale_t newlocale(int /*mask*/, const char* locale, locale_t /*base*/) { - return {_create_locale(LC_ALL, locale), locale}; +__locale_t __newlocale(int /*mask*/, const char* locale, __locale_t /*base*/) { + return {::_create_locale(LC_ALL, locale), locale}; +} + +lconv* __localeconv(__locale_t& loc) { + std::__locale_guard __current(loc); + lconv* lc = std::localeconv(); + if (!lc) + return lc; + return loc.__store_lconv(lc); +} + +// +// Strtonum functions +// +#if !defined(_LIBCPP_MSVCRT) +float __strtof(const char* nptr, char** endptr, __locale_t loc) { + std::__locale_guard __current(loc); + return std::strtof(nptr, endptr); +} + +long double __strtold(const char* nptr, char** endptr, __locale_t loc) { + std::__locale_guard __current(loc); + return std::strtold(nptr, endptr); } +#endif -decltype(MB_CUR_MAX) MB_CUR_MAX_L(locale_t __l) { +// +// Character manipulation functions +// +#if defined(__MINGW32__) && __MSVCRT_VERSION__ < 0x0800 +size_t __strftime(char* ret, size_t n, const char* format, const struct tm* tm, __locale_t loc) { + std::__locale_guard __current(loc); + return std::strftime(ret, n, format, tm); +} +#endif + +// +// Other functions +// +decltype(MB_CUR_MAX) __mb_len_max(__locale_t __l) { #if defined(_LIBCPP_MSVCRT) - return ___mb_cur_max_l_func(__l); + return ::___mb_cur_max_l_func(__l); #else std::__locale_guard __current(__l); return MB_CUR_MAX; #endif } -lconv* localeconv_l(locale_t& loc) { +wint_t __btowc(int c, __locale_t loc) { std::__locale_guard __current(loc); - lconv* lc = localeconv(); - if (!lc) - return lc; - return loc.__store_lconv(lc); + return std::btowc(c); } -size_t mbrlen_l(const char* __restrict s, size_t n, mbstate_t* __restrict ps, locale_t loc) { - std::__locale_guard __current(loc); - return mbrlen(s, n, ps); -} -size_t -mbsrtowcs_l(wchar_t* __restrict dst, const char** __restrict src, size_t len, mbstate_t* __restrict ps, locale_t loc) { + +int __wctob(wint_t c, __locale_t loc) { std::__locale_guard __current(loc); - return mbsrtowcs(dst, src, len, ps); + return std::wctob(c); } -size_t wcrtomb_l(char* __restrict s, wchar_t wc, mbstate_t* __restrict ps, locale_t loc) { + +size_t __wcsnrtombs(char* __restrict dst, + const wchar_t** __restrict src, + size_t nwc, + size_t len, + mbstate_t* __restrict ps, + __locale_t loc) { std::__locale_guard __current(loc); - return wcrtomb(s, wc, ps); + return ::wcsnrtombs(dst, src, nwc, len, ps); } -size_t mbrtowc_l(wchar_t* __restrict pwc, const char* __restrict s, size_t n, mbstate_t* __restrict ps, locale_t loc) { + +size_t __wcrtomb(char* __restrict s, wchar_t wc, mbstate_t* __restrict ps, __locale_t loc) { std::__locale_guard __current(loc); - return mbrtowc(pwc, s, n, ps); + return std::wcrtomb(s, wc, ps); } -size_t mbsnrtowcs_l(wchar_t* __restrict dst, + +size_t __mbsnrtowcs(wchar_t* __restrict dst, const char** __restrict src, size_t nms, size_t len, mbstate_t* __restrict ps, - locale_t loc) { + __locale_t loc) { std::__locale_guard __current(loc); - return mbsnrtowcs(dst, src, nms, len, ps); + return ::mbsnrtowcs(dst, src, nms, len, ps); } -size_t wcsnrtombs_l(char* __restrict dst, - const wchar_t** __restrict src, - size_t nwc, - size_t len, - mbstate_t* __restrict ps, - locale_t loc) { + +size_t +__mbrtowc(wchar_t* __restrict pwc, const char* __restrict s, size_t n, mbstate_t* __restrict ps, __locale_t loc) { std::__locale_guard __current(loc); - return wcsnrtombs(dst, src, nwc, len, ps); + return std::mbrtowc(pwc, s, n, ps); } -wint_t btowc_l(int c, locale_t loc) { + +size_t __mbrlen(const char* __restrict s, size_t n, mbstate_t* __restrict ps, __locale_t loc) { std::__locale_guard __current(loc); - return btowc(c); + return std::mbrlen(s, n, ps); } -int wctob_l(wint_t c, locale_t loc) { + +size_t __mbsrtowcs( + wchar_t* __restrict dst, const char** __restrict src, size_t len, mbstate_t* __restrict ps, __locale_t loc) { std::__locale_guard __current(loc); - return wctob(c); + return std::mbsrtowcs(dst, src, len, ps); } -int snprintf_l(char* ret, size_t n, locale_t loc, const char* format, ...) { +int __snprintf(char* ret, size_t n, __locale_t loc, const char* format, ...) { va_list ap; va_start(ap, format); #if defined(_LIBCPP_MSVCRT) // FIXME: Remove usage of internal CRT function and globals. - int result = __stdio_common_vsprintf( + int result = ::__stdio_common_vsprintf( _CRT_INTERNAL_LOCAL_PRINTF_OPTIONS | _CRT_INTERNAL_PRINTF_STANDARD_SNPRINTF_BEHAVIOR, ret, n, format, loc, ap); #else std::__locale_guard __current(loc); _LIBCPP_DIAGNOSTIC_PUSH _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wformat-nonliteral") - int result = vsnprintf(ret, n, format, ap); + int result = std::vsnprintf(ret, n, format, ap); _LIBCPP_DIAGNOSTIC_POP #endif va_end(ap); return result; } -int asprintf_l(char** ret, locale_t loc, const char* format, ...) { +// Like sprintf, but when return value >= 0 it returns +// a pointer to a malloc'd string in *sptr. +// If return >= 0, use free to delete *sptr. +int __libcpp_vasprintf(char** sptr, const char* __restrict format, va_list ap) { + *sptr = nullptr; + // Query the count required. + va_list ap_copy; + va_copy(ap_copy, ap); + _LIBCPP_DIAGNOSTIC_PUSH + _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wformat-nonliteral") + int count = vsnprintf(nullptr, 0, format, ap_copy); + _LIBCPP_DIAGNOSTIC_POP + va_end(ap_copy); + if (count < 0) + return count; + size_t buffer_size = static_cast(count) + 1; + char* p = static_cast(malloc(buffer_size)); + if (!p) + return -1; + // If we haven't used exactly what was required, something is wrong. + // Maybe bug in vsnprintf. Report the error and return. + _LIBCPP_DIAGNOSTIC_PUSH + _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wformat-nonliteral") + if (vsnprintf(p, buffer_size, format, ap) != count) { + _LIBCPP_DIAGNOSTIC_POP + free(p); + return -1; + } + // All good. This is returning memory to the caller not freeing it. + *sptr = p; + return count; +} + +int __asprintf(char** ret, __locale_t loc, const char* format, ...) { va_list ap; va_start(ap, format); - int result = vasprintf_l(ret, loc, format, ap); - va_end(ap); - return result; -} -int vasprintf_l(char** ret, locale_t loc, const char* format, va_list ap) { std::__locale_guard __current(loc); return __libcpp_vasprintf(ret, format, ap); } -#if !defined(_LIBCPP_MSVCRT) -float strtof_l(const char* nptr, char** endptr, locale_t loc) { - std::__locale_guard __current(loc); - return strtof(nptr, endptr); -} - -long double strtold_l(const char* nptr, char** endptr, locale_t loc) { - std::__locale_guard __current(loc); - return strtold(nptr, endptr); -} -#endif - -#if defined(__MINGW32__) && __MSVCRT_VERSION__ < 0x0800 -size_t strftime_l(char* ret, size_t n, const char* format, const struct tm* tm, locale_t loc) { - std::__locale_guard __current(loc); - return strftime(ret, n, format, tm); -} -#endif +} // namespace __locale +_LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/src/support/win32/support.cpp b/libcxx/src/support/win32/support.cpp index ccf5ff5acb4581..7ac508301b526c 100644 --- a/libcxx/src/support/win32/support.cpp +++ b/libcxx/src/support/win32/support.cpp @@ -13,39 +13,6 @@ #include // strcpy, wcsncpy #include // mbstate_t -// Like sprintf, but when return value >= 0 it returns -// a pointer to a malloc'd string in *sptr. -// If return >= 0, use free to delete *sptr. -int __libcpp_vasprintf(char** sptr, const char* __restrict format, va_list ap) { - *sptr = nullptr; - // Query the count required. - va_list ap_copy; - va_copy(ap_copy, ap); - _LIBCPP_DIAGNOSTIC_PUSH - _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wformat-nonliteral") - int count = vsnprintf(nullptr, 0, format, ap_copy); - _LIBCPP_DIAGNOSTIC_POP - va_end(ap_copy); - if (count < 0) - return count; - size_t buffer_size = static_cast(count) + 1; - char* p = static_cast(malloc(buffer_size)); - if (!p) - return -1; - // If we haven't used exactly what was required, something is wrong. - // Maybe bug in vsnprintf. Report the error and return. - _LIBCPP_DIAGNOSTIC_PUSH - _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wformat-nonliteral") - if (vsnprintf(p, buffer_size, format, ap) != count) { - _LIBCPP_DIAGNOSTIC_POP - free(p); - return -1; - } - // All good. This is returning memory to the caller not freeing it. - *sptr = p; - return count; -} - // Returns >= 0: the number of wide characters found in the // multi byte sequence src (of src_size_bytes), that fit in the buffer dst // (of max_dest_chars elements size). The count returned excludes the