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