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
14extern "C" {
15#endif // __cplusplus
16
17locale_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
67void freelocale(locale_t locobj) {
68  delete locobj;
69}
70
71locale_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