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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include <__assert> 10 #include <__support/ibm/xlocale.h> 11 #include <sstream> 12 #include <vector> 13 14 #ifdef __cplusplus 15 extern "C" { 16 #endif // __cplusplus 17 18 locale_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) == NULL) { 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) == NULL) { 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 68 void freelocale(locale_t locobj) { delete locobj; } 69 70 locale_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()) == NULL) || 78 (newloc->category_mask & LC_CTYPE_MASK && setlocale(LC_CTYPE, newloc->lc_ctype.c_str()) == NULL) || 79 (newloc->category_mask & LC_MONETARY_MASK && setlocale(LC_MONETARY, newloc->lc_monetary.c_str()) == NULL) || 80 (newloc->category_mask & LC_NUMERIC_MASK && setlocale(LC_NUMERIC, newloc->lc_numeric.c_str()) == NULL) || 81 (newloc->category_mask & LC_TIME_MASK && setlocale(LC_TIME, newloc->lc_time.c_str()) == NULL) || 82 (newloc->category_mask & LC_MESSAGES_MASK && setlocale(LC_MESSAGES, newloc->lc_messages.c_str()) == NULL); 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 __cplusplus 129 } 130 #endif // __cplusplus 131