[libc++] Refactor the Windows and MinGW implementation of the locale base API (#115752)

This patch reimplements the locale base support for Windows flavors in a
way that is more modules-friendly and without defining non-internal
names.

Since this changes the name of some types and entry points in the built
library, this is effectively an ABI break on Windows (which is
acceptable after checking with the Windows/libc++ maintainers).
This commit is contained in:
Louis Dionne 2024-12-16 17:46:05 -05:00 committed by GitHub
parent 8c16323757
commit 084309a0ef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 436 additions and 351 deletions

View File

@ -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
--------------------

View File

@ -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

View File

@ -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>

View File

@ -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 <cstddef>
#include <locale.h> // _locale_t
#include <stdio.h>
#include <string>
#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<char*>(__decimal_point_.c_str());
__lc_.thousands_sep = const_cast<char*>(__thousands_sep_.c_str());
__lc_.grouping = const_cast<char*>(__grouping_.c_str());
__lc_.int_curr_symbol = const_cast<char*>(__int_curr_symbol_.c_str());
__lc_.currency_symbol = const_cast<char*>(__currency_symbol_.c_str());
__lc_.mon_decimal_point = const_cast<char*>(__mon_decimal_point_.c_str());
__lc_.mon_thousands_sep = const_cast<char*>(__mon_thousands_sep_.c_str());
__lc_.mon_grouping = const_cast<char*>(__mon_grouping_.c_str());
__lc_.positive_sign = const_cast<char*>(__positive_sign_.c_str());
__lc_.negative_sign = const_cast<char*>(__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

View File

@ -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.

View File

@ -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 <clocale> // std::lconv & friends
#include <cstddef>
#include <ctype.h> // ::_isupper_l & friends
#include <locale.h> // ::_locale_t
#include <stdio.h> // ::_sscanf_l
#include <stdlib.h> // ::_strtod_l & friends
#include <string.h> // ::_strcoll_l
#include <string>
#include <time.h> // ::_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<char*>(__decimal_point_.c_str());
__lc_.thousands_sep = const_cast<char*>(__thousands_sep_.c_str());
__lc_.grouping = const_cast<char*>(__grouping_.c_str());
__lc_.int_curr_symbol = const_cast<char*>(__int_curr_symbol_.c_str());
__lc_.currency_symbol = const_cast<char*>(__currency_symbol_.c_str());
__lc_.mon_decimal_point = const_cast<char*>(__mon_decimal_point_.c_str());
__lc_.mon_thousands_sep = const_cast<char*>(__mon_thousands_sep_.c_str());
__lc_.mon_grouping = const_cast<char*>(__mon_grouping_.c_str());
__lc_.positive_sign = const_cast<char*>(__positive_sign_.c_str());
__lc_.negative_sign = const_cast<char*>(__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 <class... _Args>
_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

View File

@ -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 *
}

View File

@ -6,125 +6,181 @@
//
//===----------------------------------------------------------------------===//
#include <cstdarg> // va_start, va_end
#include <locale>
#include <memory>
#include <type_traits>
#include <__locale_dir/locale_guard.h>
#include <__locale_dir/support/windows.h>
#include <clocale> // std::localeconv() & friends
#include <cstdarg> // va_start & friends
#include <cstddef>
#include <cstdio> // std::vsnprintf & friends
#include <cstdlib> // std::strtof & friends
#include <ctime> // std::strftime
#include <cwchar> // 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};
}
decltype(MB_CUR_MAX) MB_CUR_MAX_L(locale_t __l) {
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
//
// 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) {
int __wctob(wint_t c, __locale_t loc) {
std::__locale_guard __current(loc);
return mbrlen(s, n, ps);
return std::wctob(c);
}
size_t
mbsrtowcs_l(wchar_t* __restrict dst, const char** __restrict src, size_t len, mbstate_t* __restrict ps, locale_t loc) {
std::__locale_guard __current(loc);
return mbsrtowcs(dst, src, len, ps);
}
size_t wcrtomb_l(char* __restrict s, wchar_t wc, mbstate_t* __restrict ps, locale_t loc) {
std::__locale_guard __current(loc);
return wcrtomb(s, wc, ps);
}
size_t mbrtowc_l(wchar_t* __restrict pwc, const char* __restrict s, size_t n, mbstate_t* __restrict ps, locale_t loc) {
std::__locale_guard __current(loc);
return mbrtowc(pwc, s, n, ps);
}
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) {
std::__locale_guard __current(loc);
return mbsnrtowcs(dst, src, nms, len, ps);
}
size_t wcsnrtombs_l(char* __restrict dst,
size_t __wcsnrtombs(char* __restrict dst,
const wchar_t** __restrict src,
size_t nwc,
size_t len,
mbstate_t* __restrict ps,
locale_t loc) {
__locale_t loc) {
std::__locale_guard __current(loc);
return wcsnrtombs(dst, src, nwc, len, ps);
}
wint_t btowc_l(int c, locale_t loc) {
std::__locale_guard __current(loc);
return btowc(c);
}
int wctob_l(wint_t c, locale_t loc) {
std::__locale_guard __current(loc);
return wctob(c);
return ::wcsnrtombs(dst, src, nwc, len, ps);
}
int snprintf_l(char* ret, size_t n, locale_t loc, const char* format, ...) {
size_t __wcrtomb(char* __restrict s, wchar_t wc, mbstate_t* __restrict ps, __locale_t loc) {
std::__locale_guard __current(loc);
return std::wcrtomb(s, wc, ps);
}
size_t __mbsnrtowcs(wchar_t* __restrict dst,
const char** __restrict src,
size_t nms,
size_t len,
mbstate_t* __restrict ps,
__locale_t loc) {
std::__locale_guard __current(loc);
return ::mbsnrtowcs(dst, src, nms, len, ps);
}
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 std::mbrtowc(pwc, s, n, ps);
}
size_t __mbrlen(const char* __restrict s, size_t n, mbstate_t* __restrict ps, __locale_t loc) {
std::__locale_guard __current(loc);
return std::mbrlen(s, n, ps);
}
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 std::mbsrtowcs(dst, src, len, ps);
}
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<size_t>(count) + 1;
char* p = static_cast<char*>(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

View File

@ -13,39 +13,6 @@
#include <cstring> // strcpy, wcsncpy
#include <cwchar> // 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<size_t>(count) + 1;
char* p = static_cast<char*>(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