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 
newlocale(int category_mask,const char * locale,locale_t base)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 
freelocale(locale_t locobj)68 void freelocale(locale_t locobj) { delete locobj; }
69 
uselocale(locale_t newloc)70 locale_t uselocale(locale_t newloc) {
71   // Maintain current locale name(s).
72   std::string current_loc_name(setlocale(LC_ALL, 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, 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(',') != 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(ss, s, delimiter)) {
103       tokenized.push_back(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