xref: /freebsd/contrib/llvm-project/libcxx/src/support/ibm/xlocale_zos.cpp (revision 924226fba12cc9a228c73b956e1b7fa24c60b055)
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