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
45 changes: 30 additions & 15 deletions stl/inc/xlocinfo
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,24 @@ private:
void* _Timeptr; // pointer to time information
};

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

const size_t _Bytes = (char_traits<_Elem>::length(_Ptr) + 1) * sizeof(_Elem);

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

return _Result;
}

extern "C++" template <class _Elem>
class _CRTIMP2_PURE_IMPORT _Yarn { // wrap a NTCTS
public:
Expand All @@ -156,26 +174,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 +396,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*`. We should use `_Yarn<wchar_t>`.
_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 @@ -221,9 +221,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 @@ -232,8 +231,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 @@ -277,6 +277,7 @@ tests\GH_005504_avoid_function_call_wrapping
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\GH_005800_stable_sort_large_alignment
tests\GH_005968_headers_provide_begin_end
tests\LWG2381_num_get_floating_point
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
44 changes: 44 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,44 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <algorithm>
#include <cassert>
#include <clocale>
#include <iomanip>
#include <iostream>
#include <locale>
#include <string>
using namespace std;

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

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

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

// GH-5780 <iostream>: Printing with std::cout changes the global locale permanently
void test_gh_5780() {
// https://learn.microsoft.com/en-us/cpp/c-runtime-library/language-strings#supported-language-strings
const string locale_name = set_locale("norwegian-bokmal.437");
assert_string_non_ascii(locale_name);

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

assert(query_locale() == locale_name);
}

int main() {
test_gh_5780();
}