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