131 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include <__assert>10#include <__support/ibm/xlocale.h>11#include <sstream>12#include <vector>13 14#ifdef __cplusplus15extern "C" {16#endif // __cplusplus17 18locale_t newlocale(int category_mask, const char* locale, locale_t base) {19 // Maintain current locale name(s) to restore later.20 std::string current_loc_name(setlocale(LC_ALL, 0));21 22 // Check for errors.23 if (category_mask == LC_ALL_MASK && setlocale(LC_ALL, locale) == nullptr) {24 errno = EINVAL;25 return (locale_t)0;26 } else {27 for (int _Cat = 0; _Cat <= _LC_MAX; ++_Cat) {28 if ((_CATMASK(_Cat) & category_mask) != 0 && setlocale(_Cat, locale) == nullptr) {29 setlocale(LC_ALL, current_loc_name.c_str());30 errno = EINVAL;31 return (locale_t)0;32 }33 }34 }35 36 // Create new locale.37 locale_t newloc = new locale_struct();38 39 if (base) {40 if (category_mask != LC_ALL_MASK) {41 // Copy base when it will not be overwritten.42 memcpy(newloc, base, sizeof(locale_struct));43 newloc->category_mask = category_mask | base->category_mask;44 }45 delete base;46 } else {47 newloc->category_mask = category_mask;48 }49 50 if (category_mask & LC_COLLATE_MASK)51 newloc->lc_collate = locale;52 if (category_mask & LC_CTYPE_MASK)53 newloc->lc_ctype = locale;54 if (category_mask & LC_MONETARY_MASK)55 newloc->lc_monetary = locale;56 if (category_mask & LC_NUMERIC_MASK)57 newloc->lc_numeric = locale;58 if (category_mask & LC_TIME_MASK)59 newloc->lc_time = locale;60 if (category_mask & LC_MESSAGES_MASK)61 newloc->lc_messages = locale;62 63 // Restore current locale.64 setlocale(LC_ALL, current_loc_name.c_str());65 return (locale_t)newloc;66}67 68void freelocale(locale_t locobj) { delete locobj; }69 70locale_t uselocale(locale_t newloc) {71 // Maintain current locale name(s).72 std::string current_loc_name(setlocale(LC_ALL, 0));73 74 if (newloc) {75 // Set locales and check for errors.76 bool is_error =77 (newloc->category_mask & LC_COLLATE_MASK && setlocale(LC_COLLATE, newloc->lc_collate.c_str()) == nullptr) ||78 (newloc->category_mask & LC_CTYPE_MASK && setlocale(LC_CTYPE, newloc->lc_ctype.c_str()) == nullptr) ||79 (newloc->category_mask & LC_MONETARY_MASK && setlocale(LC_MONETARY, newloc->lc_monetary.c_str()) == nullptr) ||80 (newloc->category_mask & LC_NUMERIC_MASK && setlocale(LC_NUMERIC, newloc->lc_numeric.c_str()) == nullptr) ||81 (newloc->category_mask & LC_TIME_MASK && setlocale(LC_TIME, newloc->lc_time.c_str()) == nullptr) ||82 (newloc->category_mask & LC_MESSAGES_MASK && setlocale(LC_MESSAGES, newloc->lc_messages.c_str()) == nullptr);83 84 if (is_error) {85 setlocale(LC_ALL, current_loc_name.c_str());86 errno = EINVAL;87 return (locale_t)0;88 }89 }90 91 // Construct and return previous locale.92 locale_t previous_loc = new locale_struct();93 94 // current_loc_name might be a comma-separated locale name list.95 if (current_loc_name.find(',') != std::string::npos) {96 // Tokenize locale name list.97 const char delimiter = ',';98 std::vector<std::string> tokenized;99 std::stringstream ss(current_loc_name);100 std::string s;101 102 while (std::getline(ss, s, delimiter)) {103 tokenized.push_back(s);104 }105 106 _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(tokenized.size() >= _NCAT, "locale-name list is too short");107 108 previous_loc->lc_collate = tokenized[LC_COLLATE];109 previous_loc->lc_ctype = tokenized[LC_CTYPE];110 previous_loc->lc_monetary = tokenized[LC_MONETARY];111 previous_loc->lc_numeric = tokenized[LC_NUMERIC];112 previous_loc->lc_time = tokenized[LC_TIME];113 // Skip LC_TOD.114 previous_loc->lc_messages = tokenized[LC_MESSAGES];115 } else {116 previous_loc->lc_collate = current_loc_name;117 previous_loc->lc_ctype = current_loc_name;118 previous_loc->lc_monetary = current_loc_name;119 previous_loc->lc_numeric = current_loc_name;120 previous_loc->lc_time = current_loc_name;121 previous_loc->lc_messages = current_loc_name;122 }123 124 previous_loc->category_mask = LC_ALL_MASK;125 return previous_loc;126}127 128#ifdef __cplusplus129}130#endif // __cplusplus131