[libc++] Fix support for multibyte thousands_sep and decimal_point in moneypunct_byname and numpunct_byname.
Summary: The underlying C locales provide the `thousands_sep` and `decimal_point` as strings, possible with more than one character. We currently don't handle this case even for `wchar_t`. This patch properly converts the mbs -> wide character for `moneypunct_byname<wchar_t>`. For the `moneypunct_byname<char>` case we attempt to narrow the WC and if that fails we also attempt to translate it to some reasonable value. For example we translate U00A0 (non-breaking space) into U0020 (regular space). If none of these conversions succeed then we simply allow the base class to provide a fallback value. Reviewers: mclow.lists, EricWF Subscribers: vangyzen, george.burgess.iv, cfe-commits Differential Revision: https://reviews.llvm.org/D24218 git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@289347 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
133
src/locale.cpp
133
src/locale.cpp
@@ -4183,6 +4183,54 @@ __widen_from_utf8<32>::~__widen_from_utf8()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
static bool checked_string_to_wchar_convert(wchar_t& dest,
|
||||
const char* ptr,
|
||||
__locale_struct* loc) {
|
||||
if (*ptr == '\0')
|
||||
return false;
|
||||
mbstate_t mb = {};
|
||||
wchar_t out;
|
||||
size_t ret = __libcpp_mbrtowc_l(&out, ptr, strlen(ptr), &mb, loc);
|
||||
if (ret == static_cast<size_t>(-1) || ret == static_cast<size_t>(-2)) {
|
||||
return false;
|
||||
}
|
||||
dest = out;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool checked_string_to_char_convert(char& dest,
|
||||
const char* ptr,
|
||||
__locale_struct* __loc) {
|
||||
if (*ptr == '\0')
|
||||
return false;
|
||||
if (!ptr[1]) {
|
||||
dest = *ptr;
|
||||
return true;
|
||||
}
|
||||
// First convert the MBS into a wide char then attempt to narrow it using
|
||||
// wctob_l.
|
||||
wchar_t wout;
|
||||
if (!checked_string_to_wchar_convert(wout, ptr, __loc))
|
||||
return false;
|
||||
int res;
|
||||
if ((res = __libcpp_wctob_l(wout, __loc)) != char_traits<char>::eof()) {
|
||||
dest = res;
|
||||
return true;
|
||||
}
|
||||
// FIXME: Work around specific multibyte sequences that we can reasonable
|
||||
// translate into a different single byte.
|
||||
switch (wout) {
|
||||
case L'\u00A0': // non-breaking space
|
||||
dest = ' ';
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
_LIBCPP_UNREACHABLE();
|
||||
}
|
||||
|
||||
|
||||
// numpunct<char> && numpunct<wchar_t>
|
||||
|
||||
locale::id numpunct< char >::id;
|
||||
@@ -4254,10 +4302,10 @@ numpunct_byname<char>::__init(const char* nm)
|
||||
" failed to construct for " + string(nm));
|
||||
|
||||
lconv* lc = __libcpp_localeconv_l(loc.get());
|
||||
if (*lc->decimal_point)
|
||||
__decimal_point_ = *lc->decimal_point;
|
||||
if (*lc->thousands_sep)
|
||||
__thousands_sep_ = *lc->thousands_sep;
|
||||
checked_string_to_char_convert(__decimal_point_, lc->decimal_point,
|
||||
loc.get());
|
||||
checked_string_to_char_convert(__thousands_sep_, lc->thousands_sep,
|
||||
loc.get());
|
||||
__grouping_ = lc->grouping;
|
||||
// localization for truename and falsename is not available
|
||||
}
|
||||
@@ -4288,16 +4336,16 @@ numpunct_byname<wchar_t>::__init(const char* nm)
|
||||
{
|
||||
__locale_unique_ptr loc(newlocale(LC_ALL_MASK, nm, 0), freelocale);
|
||||
if (loc == nullptr)
|
||||
__throw_runtime_error("numpunct_byname<char>::numpunct_byname"
|
||||
__throw_runtime_error("numpunct_byname<wchar_t>::numpunct_byname"
|
||||
" failed to construct for " + string(nm));
|
||||
|
||||
lconv* lc = __libcpp_localeconv_l(loc.get());
|
||||
if (*lc->decimal_point)
|
||||
__decimal_point_ = *lc->decimal_point;
|
||||
if (*lc->thousands_sep)
|
||||
__thousands_sep_ = *lc->thousands_sep;
|
||||
checked_string_to_wchar_convert(__decimal_point_, lc->decimal_point,
|
||||
loc.get());
|
||||
checked_string_to_wchar_convert(__thousands_sep_, lc->thousands_sep,
|
||||
loc.get());
|
||||
__grouping_ = lc->grouping;
|
||||
// locallization for truename and falsename is not available
|
||||
// localization for truename and falsename is not available
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5779,14 +5827,15 @@ moneypunct_byname<char, false>::init(const char* nm)
|
||||
" failed to construct for " + string(nm));
|
||||
|
||||
lconv* lc = __libcpp_localeconv_l(loc.get());
|
||||
if (*lc->mon_decimal_point)
|
||||
__decimal_point_ = *lc->mon_decimal_point;
|
||||
else
|
||||
__decimal_point_ = base::do_decimal_point();
|
||||
if (*lc->mon_thousands_sep)
|
||||
__thousands_sep_ = *lc->mon_thousands_sep;
|
||||
else
|
||||
__thousands_sep_ = base::do_thousands_sep();
|
||||
if (!checked_string_to_char_convert(__decimal_point_,
|
||||
lc->mon_decimal_point,
|
||||
loc.get()))
|
||||
__decimal_point_ = base::do_decimal_point();
|
||||
if (!checked_string_to_char_convert(__thousands_sep_,
|
||||
lc->mon_thousands_sep,
|
||||
loc.get()))
|
||||
__thousands_sep_ = base::do_thousands_sep();
|
||||
|
||||
__grouping_ = lc->mon_grouping;
|
||||
__curr_symbol_ = lc->currency_symbol;
|
||||
if (lc->frac_digits != CHAR_MAX)
|
||||
@@ -5822,14 +5871,14 @@ moneypunct_byname<char, true>::init(const char* nm)
|
||||
" failed to construct for " + string(nm));
|
||||
|
||||
lconv* lc = __libcpp_localeconv_l(loc.get());
|
||||
if (*lc->mon_decimal_point)
|
||||
__decimal_point_ = *lc->mon_decimal_point;
|
||||
else
|
||||
__decimal_point_ = base::do_decimal_point();
|
||||
if (*lc->mon_thousands_sep)
|
||||
__thousands_sep_ = *lc->mon_thousands_sep;
|
||||
else
|
||||
__thousands_sep_ = base::do_thousands_sep();
|
||||
if (!checked_string_to_char_convert(__decimal_point_,
|
||||
lc->mon_decimal_point,
|
||||
loc.get()))
|
||||
__decimal_point_ = base::do_decimal_point();
|
||||
if (!checked_string_to_char_convert(__thousands_sep_,
|
||||
lc->mon_thousands_sep,
|
||||
loc.get()))
|
||||
__thousands_sep_ = base::do_thousands_sep();
|
||||
__grouping_ = lc->mon_grouping;
|
||||
__curr_symbol_ = lc->int_curr_symbol;
|
||||
if (lc->int_frac_digits != CHAR_MAX)
|
||||
@@ -5881,14 +5930,14 @@ moneypunct_byname<wchar_t, false>::init(const char* nm)
|
||||
__throw_runtime_error("moneypunct_byname"
|
||||
" failed to construct for " + string(nm));
|
||||
lconv* lc = __libcpp_localeconv_l(loc.get());
|
||||
if (*lc->mon_decimal_point)
|
||||
__decimal_point_ = static_cast<wchar_t>(*lc->mon_decimal_point);
|
||||
else
|
||||
__decimal_point_ = base::do_decimal_point();
|
||||
if (*lc->mon_thousands_sep)
|
||||
__thousands_sep_ = static_cast<wchar_t>(*lc->mon_thousands_sep);
|
||||
else
|
||||
__thousands_sep_ = base::do_thousands_sep();
|
||||
if (!checked_string_to_wchar_convert(__decimal_point_,
|
||||
lc->mon_decimal_point,
|
||||
loc.get()))
|
||||
__decimal_point_ = base::do_decimal_point();
|
||||
if (!checked_string_to_wchar_convert(__thousands_sep_,
|
||||
lc->mon_thousands_sep,
|
||||
loc.get()))
|
||||
__thousands_sep_ = base::do_thousands_sep();
|
||||
__grouping_ = lc->mon_grouping;
|
||||
wchar_t wbuf[100];
|
||||
mbstate_t mb = {0};
|
||||
@@ -5947,14 +5996,14 @@ moneypunct_byname<wchar_t, true>::init(const char* nm)
|
||||
" failed to construct for " + string(nm));
|
||||
|
||||
lconv* lc = __libcpp_localeconv_l(loc.get());
|
||||
if (*lc->mon_decimal_point)
|
||||
__decimal_point_ = static_cast<wchar_t>(*lc->mon_decimal_point);
|
||||
else
|
||||
__decimal_point_ = base::do_decimal_point();
|
||||
if (*lc->mon_thousands_sep)
|
||||
__thousands_sep_ = static_cast<wchar_t>(*lc->mon_thousands_sep);
|
||||
else
|
||||
__thousands_sep_ = base::do_thousands_sep();
|
||||
if (!checked_string_to_wchar_convert(__decimal_point_,
|
||||
lc->mon_decimal_point,
|
||||
loc.get()))
|
||||
__decimal_point_ = base::do_decimal_point();
|
||||
if (!checked_string_to_wchar_convert(__thousands_sep_,
|
||||
lc->mon_thousands_sep,
|
||||
loc.get()))
|
||||
__thousands_sep_ = base::do_thousands_sep();
|
||||
__grouping_ = lc->mon_grouping;
|
||||
wchar_t wbuf[100];
|
||||
mbstate_t mb = {0};
|
||||
|
||||
Reference in New Issue
Block a user