Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 34 additions & 15 deletions stl/inc/xlocinfo
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,28 @@ private:
void* _Timeptr; // pointer to time information
};

template <class _Elem>
_Elem* _Ntcts_dup_dbg(const _Elem* const _Ptr) noexcept {
_STL_INTERNAL_STATIC_ASSERT(_Is_any_of_v<_Elem, char, wchar_t, unsigned short>);

auto _Iter = _Ptr;
while (*_Iter != _Elem{}) {
++_Iter;
}
const size_t _Count = static_cast<size_t>(_Iter - _Ptr + 1) * sizeof(_Elem);

#ifdef _DEBUG
const auto _Result = static_cast<_Elem*>(_CSTD _malloc_dbg(_Count, _CRT_BLOCK, __FILE__, __LINE__));
#else
const auto _Result = static_cast<_Elem*>(_CSTD malloc(_Count));
#endif
if (_Result) {
_CSTD memcpy(_Result, _Ptr, _Count);
}

return _Result;
}

extern "C++" template <class _Elem>
class _CRTIMP2_PURE_IMPORT _Yarn { // wrap a NTCTS
public:
Expand All @@ -156,26 +178,22 @@ public:
_Tidy();

if (_Right) { // new is not empty, copy it
const _Elem* _Ptr = _Right;
while (*_Ptr != _Elem{}) {
++_Ptr;
}
_Myptr = _STD _Ntcts_dup_dbg(_Right);
}
}

const auto _Count = (++_Ptr - _Right) * sizeof(_Elem);
return *this;
}

#ifdef _DEBUG
_Myptr = static_cast<_Elem*>(_malloc_dbg(_Count, _CRT_BLOCK, __FILE__, __LINE__));
#else
_Myptr = static_cast<_Elem*>(_CSTD malloc(_Count));
#endif
template <class _Elem2 = _Elem, enable_if_t<is_same_v<_Elem2, char>, int> = 0>
void _From_wide(const wchar_t* const _Right) noexcept {
if (reinterpret_cast<wchar_t*>(_Myptr) != _Right) { // new value, discard old and copy new
_Tidy();

if (_Myptr) {
_CSTD memcpy(_Myptr, _Right, _Count);
}
if (_Right) { // new is not empty, copy it
_Myptr = reinterpret_cast<char*>(_STD _Ntcts_dup_dbg(_Right));
}
}

return *this;
}

__CLR_OR_THIS_CALL ~_Yarn() noexcept {
Expand Down Expand Up @@ -382,6 +400,7 @@ private:
_Yarn<char> _Months; // month names
_Yarn<wchar_t> _W_Days; // wide weekday names
_Yarn<wchar_t> _W_Months; // wide month names
// TRANSITION, ABI, `_Oldlocname._Myptr` is reinterpreted as `wchar_t*`. `wchar` should be wrapped instead.
_Yarn<char> _Oldlocname; // old locale name to revert to on destruction
_Yarn<char> _Newlocname; // new locale name for this object
};
Expand Down
4 changes: 1 addition & 3 deletions stl/src/locale.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,7 @@ void __CLRCALL_PURE_OR_CDECL locale::_Locimp::_Locimp_Addfac(

void __CLRCALL_PURE_OR_CDECL _Locinfo::_Locinfo_ctor(
_Locinfo* pLocinfo, int cat, const char* locname) { // capture a named locale
const char* oldlocname = setlocale(LC_ALL, nullptr);

pLocinfo->_Oldlocname = oldlocname == nullptr ? "" : oldlocname;
pLocinfo->_Oldlocname._From_wide(_wsetlocale(LC_ALL, nullptr));
_Locinfo_Addcats(pLocinfo, cat, locname);
}

Expand Down
11 changes: 7 additions & 4 deletions stl/src/locale0.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -222,9 +222,8 @@ void __CLRCALL_PURE_OR_CDECL locale::_Locimp::_Locimp_dtor(_Locimp* _This) { //

void __CLRCALL_PURE_OR_CDECL _Locinfo::_Locinfo_ctor(
_Locinfo* pLocinfo, const char* locname) { // switch to a named locale
const char* oldlocname = setlocale(LC_ALL, nullptr);
pLocinfo->_Oldlocname._From_wide(_wsetlocale(LC_ALL, nullptr));

pLocinfo->_Oldlocname = oldlocname == nullptr ? "" : oldlocname;
if (locname != nullptr) {
locname = setlocale(LC_ALL, locname);
}
Expand All @@ -233,8 +232,12 @@ void __CLRCALL_PURE_OR_CDECL _Locinfo::_Locinfo_ctor(
}

void __CLRCALL_PURE_OR_CDECL _Locinfo::_Locinfo_dtor(_Locinfo* pLocinfo) { // destroy a _Locinfo object, revert locale
if (!pLocinfo->_Oldlocname._Empty()) {
setlocale(LC_ALL, pLocinfo->_Oldlocname._C_str());
if (pLocinfo->_Oldlocname._Empty()) {
// `pLocinfo->_Oldlocname._C_str()` points to a single `char` of value 0 in this case,
// so reinterpret_cast is not reliable.
_wsetlocale(LC_ALL, L"");
} else {
_wsetlocale(LC_ALL, reinterpret_cast<const wchar_t*>(pLocinfo->_Oldlocname._C_str()));
}
}
_STD_END
Expand Down
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ tests\GH_005472_do_not_overlap
tests\GH_005546_containers_size_type_cast
tests\GH_005553_regex_character_translation
tests\GH_005768_pow_accuracy
tests\GH_005780_non_ascii_locales
tests\LWG2381_num_get_floating_point
tests\LWG2510_tag_classes
tests\LWG2597_complex_branch_cut
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/GH_005780_non_ascii_locales/env.lst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\usual_matrix.lst
41 changes: 41 additions & 0 deletions tests/std/tests/GH_005780_non_ascii_locales/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <algorithm>
#include <cassert>
#include <clocale>
#include <iomanip>
#include <iostream>
#include <string>

std::string set_locale(const std::string& locale_name) {
const char* ret = std::setlocale(LC_ALL, locale_name.c_str());
assert(ret != nullptr);
return ret;
}

std::string query_locale() {
const char* ret = std::setlocale(LC_ALL, nullptr);
assert(ret != nullptr);
return ret;
}

void assert_string_non_ascii(const std::string& string) {
const auto char_not_ascii = [](const char c) { return (c & 0x80) != 0; };
assert(std::any_of(string.begin(), string.end(), char_not_ascii));
}

void test_gh_5780() {
// https://learn.microsoft.com/en-us/cpp/c-runtime-library/language-strings#supported-language-strings
std::string locale_name = set_locale("norwegian-bokmal.437");
assert_string_non_ascii(locale_name);

std::cerr.imbue(std::locale::classic());
std::cerr << std::setprecision(2) << 0.1 << std::endl;

assert(query_locale() == locale_name);
}

int main() {
test_gh_5780();
}