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) { 69 delete locobj; 70 } 71 72 locale_t uselocale(locale_t newloc) { 73 // Maintain current locale name(s). 74 std::string current_loc_name(setlocale(LC_ALL, 0)); 75 76 if (newloc) { 77 // Set locales and check for errors. 78 bool is_error = 79 (newloc->category_mask & LC_COLLATE_MASK && 80 setlocale(LC_COLLATE, newloc->lc_collate.c_str()) == NULL) || 81 (newloc->category_mask & LC_CTYPE_MASK && 82 setlocale(LC_CTYPE, newloc->lc_ctype.c_str()) == NULL) || 83 (newloc->category_mask & LC_MONETARY_MASK && 84 setlocale(LC_MONETARY, newloc->lc_monetary.c_str()) == NULL) || 85 (newloc->category_mask & LC_NUMERIC_MASK && 86 setlocale(LC_NUMERIC, newloc->lc_numeric.c_str()) == NULL) || 87 (newloc->category_mask & LC_TIME_MASK && 88 setlocale(LC_TIME, newloc->lc_time.c_str()) == NULL) || 89 (newloc->category_mask & LC_MESSAGES_MASK && 90 setlocale(LC_MESSAGES, newloc->lc_messages.c_str()) == NULL); 91 92 if (is_error) { 93 setlocale(LC_ALL, current_loc_name.c_str()); 94 errno = EINVAL; 95 return (locale_t)0; 96 } 97 } 98 99 // Construct and return previous locale. 100 locale_t previous_loc = new locale_struct(); 101 102 // current_loc_name might be a comma-separated locale name list. 103 if (current_loc_name.find(',') != std::string::npos) { 104 // Tokenize locale name list. 105 const char delimiter = ','; 106 std::vector<std::string> tokenized; 107 std::stringstream ss(current_loc_name); 108 std::string s; 109 110 while (std::getline(ss, s, delimiter)) { 111 tokenized.push_back(s); 112 } 113 114 _LIBCPP_ASSERT(tokenized.size() >= _NCAT, "locale-name list is too short"); 115 116 previous_loc->lc_collate = tokenized[LC_COLLATE]; 117 previous_loc->lc_ctype = tokenized[LC_CTYPE]; 118 previous_loc->lc_monetary = tokenized[LC_MONETARY]; 119 previous_loc->lc_numeric = tokenized[LC_NUMERIC]; 120 previous_loc->lc_time = tokenized[LC_TIME]; 121 // Skip LC_TOD. 122 previous_loc->lc_messages = tokenized[LC_MESSAGES]; 123 } else { 124 previous_loc->lc_collate = current_loc_name; 125 previous_loc->lc_ctype = current_loc_name; 126 previous_loc->lc_monetary = current_loc_name; 127 previous_loc->lc_numeric = current_loc_name; 128 previous_loc->lc_time = current_loc_name; 129 previous_loc->lc_messages = current_loc_name; 130 } 131 132 previous_loc->category_mask = LC_ALL_MASK; 133 return previous_loc; 134 } 135 136 #ifdef __cplusplus 137 } 138 #endif // __cplusplus 139