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