diff --git a/libcxx/src/locale.cpp b/libcxx/src/locale.cpp --- a/libcxx/src/locale.cpp +++ b/libcxx/src/locale.cpp @@ -174,6 +174,7 @@ void install(facet* f, long id); template void install(F* f) {install(f, f->id.__get());} template void install_from(const __imp& other); + string build_name(string other, string one, locale::category c); }; locale::__imp::__imp(size_t refs) @@ -323,10 +324,11 @@ : facets_(N), name_("*") { - facets_ = other.facets_; - for (unsigned i = 0; i < facets_.size(); ++i) - if (facets_[i]) - facets_[i]->__add_shared(); + name_ = build_name(other.name_, name, c); + facets_ = other.facets_; + for (unsigned i = 0; i < facets_.size(); ++i) + if (facets_[i]) + facets_[i]->__add_shared(); #ifndef _LIBCPP_NO_EXCEPTIONS try { @@ -416,10 +418,11 @@ : facets_(N), name_("*") { - facets_ = other.facets_; - for (unsigned i = 0; i < facets_.size(); ++i) - if (facets_[i]) - facets_[i]->__add_shared(); + name_ = build_name(other.name_, one.name_, c); + facets_ = other.facets_; + for (unsigned i = 0; i < facets_.size(); ++i) + if (facets_[i]) + facets_[i]->__add_shared(); #ifndef _LIBCPP_NO_EXCEPTIONS try { @@ -552,6 +555,150 @@ return facets_[static_cast(id)]; } +string locale::__imp::build_name(string other, string one, locale::category c) { + if (c == locale::all) + return one; + if (c == locale::none) + return other; + if (other == one) + return other; + if (other == "*" || one == "*") + return "*"; + + // Find out user preferred locale + if (one == "") + one = setlocale(LC_ALL, ""); + + // When the locale have different name for different categories, + // format for locale name on AIX and z/OS is: en_US,C,en_US,en_US,en_US,en_US,en_US,en_US + // format for locale name on Linux is: + // LC_CTYPE=en_US;LC_NUMERIC=C;LC_TIME=C;LC_COLLATE=en_US;LC_MONETARY=C;LC_MESSAGES=C; + // LC_PAPER=C;LC_NAME=C;LC_ADDRESS=C;LC_TELEPHONE=C;LC_MEASUREMENT=C;LC_IDENTIFICATION=C + + vector other_cat; + vector one_cat; + size_t separator_pos; + char separator; + +#if defined(_AIX) || defined(__MVS__) + separator = ','; +#else + separator = ';'; +#endif + + separator_pos = other.find(separator); + if (separator_pos != string::npos) { + // Parse string "other" to get locale for each category + do { +#if defined(_AIX) || defined(__MVS__) + size_t start_pos = 0; +#else + size_t start_pos = other.find("=") + 1; +#endif + size_t length = separator_pos - start_pos; + string token = other.substr(start_pos, length); + other_cat.push_back(token); + other.erase(0, separator_pos + 1); + separator_pos = other.find(separator); + } while (separator_pos != string::npos); + + // Return "*" when the locale name we get from other is invalid, + // because construction of this locale will fail at later stage anyway. +#if defined(_AIX) && defined(__MVS__) + if (other_cat.size() < 5) + return "*"; +#else + if (other_cat.size() < 11) + return "*"; + size_t equal_pos = other.find("="); + other.erase(0, equal_pos + 1); +#endif + other_cat.push_back(other); + } else { + // "other" is a string applicable for all category +#if defined(_AIX) + other_cat = vector(6, other); +#elif defined(__MVS__) + other_cat = vector(8, other); +#else + other_cat = vector(12, other); +#endif + } + + separator_pos = one.find(separator); + if (separator_pos != string::npos) { + // Parse string "one" to get locale for each category + do { +#if defined(_AIX) || defined(__MVS__) + size_t start_pos = 0; +#else + size_t start_pos = one.find("=") + 1; +#endif + size_t length = separator_pos - start_pos; + string token = one.substr(start_pos, length); + one_cat.push_back(token); + one.erase(0, separator_pos + 1); + separator_pos = one.find(separator); + } while (separator_pos != string::npos); +#if !(defined(_AIX) && defined(__MVS__)) + size_t equal_pos = other.find("="); + one.erase(0, equal_pos + 1); +#endif + one_cat.push_back(one); + } else { + // "one" is a string applicable for all category + one_cat = vector(6, one); + } + +#if defined(_AIX) || defined(__MVS__) + enum cat_order_t { lc_collate, lc_ctype, lc_messages, lc_monetary, lc_numeric, lc_time }; +#else + enum cat_order_t { lc_ctype, lc_numeric, lc_time, lc_collate, lc_monetary, lc_messages }; +#endif + + // Use "one" to replace the applicable locale category in "other" + if (c & locale::collate) { + other_cat[lc_collate] = one_cat[lc_collate]; + } + if (c & locale::ctype) { + other_cat[lc_ctype] = one_cat[lc_ctype]; + } + if (c & locale::monetary) { + other_cat[lc_monetary] = one_cat[lc_monetary]; + } + if (c & locale::numeric) { + other_cat[lc_numeric] = one_cat[lc_numeric]; + } + if (c & locale::time) { + other_cat[lc_time] = one_cat[lc_time]; + } + if (c & locale::messages) { + other_cat[lc_messages] = one_cat[lc_messages]; + } + + for (auto s : other_cat) { + // If the locale in one of the category does not match the rest, return locale name for each category + if (s != other_cat[0]) { + string result; +#if defined(_AIX) || defined(__MVS__) + for (auto s : other_cat) { + result = result + s + ","; + } + result = result.substr(0, result.length() - 1); +#else + result = "LC_CTYPE=" + other_cat[0] + ";" + "LC_NUMERIC=" + other_cat[1] + ";" + "LC_TIME=" + other_cat[2] + ";" + + "LC_COLLATE=" + other_cat[3] + ";" + "LC_MONETARY=" + other_cat[4] + ";" + + "LC_MESSAGES=" + other_cat[5] + ";" + "LC_PAPER=" + other_cat[6] + ";" + "LC_NAME=" + other_cat[7] + + ";" + "LC_ADDRESS=" + other_cat[8] + ";" + "LC_TELEPHONE=" + other_cat[9] + ";" + + "LC_MEASUREMENT=" + other_cat[10] + ";" + "LC_IDENTIFICATION=" + other_cat[11]; +#endif + return result; + } + } + // Return one locale name for all the category + return other_cat[0]; +} + // locale const locale& diff --git a/libcxx/test/std/localization/locales/locale/locale.cons/name_construction.pass.cpp b/libcxx/test/std/localization/locales/locale/locale.cons/name_construction.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/localization/locales/locale/locale.cons/name_construction.pass.cpp @@ -0,0 +1,73 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: locale.en_US.UTF-8 +// REQUIRES: locale.fr_FR.UTF-8 + +// + +// Test locale name construction for the following constructors +// locale( const locale& other, const char* std_name, category cat ); +// locale( const locale& other, const std::string& std_name, category cat ); +// locale( const locale& other, const locale& one, category cat ); + +#include +#include +#include "platform_support.h" // locale name macros + +int main(int, char**) { + std::locale loc1(LOCALE_en_US_UTF_8); + std::locale loc2(LOCALE_fr_FR_UTF_8); + std::locale loc3(std::locale(), new std::ctype); + { + std::locale loc(loc1, loc2, std::locale::all); + assert(loc.name() == loc2.name()); + } + { + std::locale loc(loc1, loc2, std::locale::none); + assert(loc.name() == loc1.name()); + } + { + std::locale loc(loc1, "", std::locale::none); + assert(loc.name() == loc1.name()); + } + { + std::locale loc(loc1, LOCALE_en_US_UTF_8, std::locale::time); + assert(loc.name() == loc1.name()); + } + { + std::locale loc(loc3, loc1, std::locale::time); + assert(loc.name() == "*"); + } + { + std::locale loc(loc1, loc2, std::locale::time | std::locale::ctype); +#if defined(_AIX) || defined(__MVS__) + assert(loc.name() == LOCALE_en_US_UTF_8 "," LOCALE_fr_FR_UTF_8 "," LOCALE_en_US_UTF_8 "," LOCALE_en_US_UTF_8 + "," LOCALE_en_US_UTF_8 "," LOCALE_fr_FR_UTF_8); +#else + assert(loc.name() == "LC_CTYPE=" LOCALE_fr_FR_UTF_8 ";" + "LC_NUMERIC=" LOCALE_en_US_UTF_8 ";" + "LC_TIME=" LOCALE_fr_FR_UTF_8 ";" + "LC_COLLATE=" LOCALE_en_US_UTF_8 ";" + "LC_MONETARY=" LOCALE_en_US_UTF_8 ";" + "LC_MESSAGES=" LOCALE_en_US_UTF_8 ";" + "LC_PAPER=" LOCALE_en_US_UTF_8 ";" + "LC_NAME=" LOCALE_en_US_UTF_8 ";" + "LC_ADDRESS=" LOCALE_en_US_UTF_8 ";" + "LC_TELEPHONE=" LOCALE_en_US_UTF_8 ";" + "LC_MEASUREMENT=" LOCALE_en_US_UTF_8 ";" + "LC_IDENTIFICATION=" LOCALE_en_US_UTF_8); +#endif + } + { + std::locale loc5(loc2, loc1, std::locale::ctype | std::locale::monetary); + std::locale loc(loc1, loc5, std::locale::ctype | std::locale::monetary); + assert(loc.name() == LOCALE_en_US_UTF_8); + } + return 0; +}