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