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, locale: 0)); |
21 | |
22 | // Check for errors. |
23 | if (category_mask == LC_ALL_MASK && setlocale(LC_ALL, locale: 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(category: _Cat, locale: locale) == NULL) { |
29 | setlocale(LC_ALL, locale: 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, locale: 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, locale: 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, locale: 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(c: ',') != 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(in&: ss, str&: s, delim: delimiter)) { |
103 | tokenized.push_back(x: 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 | |