aboutsummaryrefslogtreecommitdiff
path: root/libcxx/src/support/ibm/xlocale_zos.cpp
blob: 4c20997b4eb793a1cf553051241930e0215835a4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include <__assert>
#include <__support/ibm/xlocale.h>
#include <sstream>
#include <vector>

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

locale_t newlocale(int category_mask, const char* locale, locale_t base) {
  // Maintain current locale name(s) to restore later.
  std::string current_loc_name(setlocale(LC_ALL, 0));

  // Check for errors.
  if (category_mask == LC_ALL_MASK && setlocale(LC_ALL, locale) == NULL) {
    errno = EINVAL;
    return (locale_t)0;
  } else {
    for (int _Cat = 0; _Cat <= _LC_MAX; ++_Cat) {
      if ((_CATMASK(_Cat) & category_mask) != 0 && setlocale(_Cat, locale) == NULL) {
        setlocale(LC_ALL, current_loc_name.c_str());
        errno = EINVAL;
        return (locale_t)0;
      }
    }
  }

  // Create new locale.
  locale_t newloc = new locale_struct();

  if (base) {
    if (category_mask != LC_ALL_MASK) {
      // Copy base when it will not be overwritten.
      memcpy(newloc, base, sizeof(locale_struct));
      newloc->category_mask = category_mask | base->category_mask;
    }
    delete base;
  } else {
    newloc->category_mask = category_mask;
  }

  if (category_mask & LC_COLLATE_MASK)
    newloc->lc_collate = locale;
  if (category_mask & LC_CTYPE_MASK)
    newloc->lc_ctype = locale;
  if (category_mask & LC_MONETARY_MASK)
    newloc->lc_monetary = locale;
  if (category_mask & LC_NUMERIC_MASK)
    newloc->lc_numeric = locale;
  if (category_mask & LC_TIME_MASK)
    newloc->lc_time = locale;
  if (category_mask & LC_MESSAGES_MASK)
    newloc->lc_messages = locale;

  // Restore current locale.
  setlocale(LC_ALL, current_loc_name.c_str());
  return (locale_t)newloc;
}

void freelocale(locale_t locobj) { delete locobj; }

locale_t uselocale(locale_t newloc) {
  // Maintain current locale name(s).
  std::string current_loc_name(setlocale(LC_ALL, 0));

  if (newloc) {
    // Set locales and check for errors.
    bool is_error =
        (newloc->category_mask & LC_COLLATE_MASK && setlocale(LC_COLLATE, newloc->lc_collate.c_str()) == NULL) ||
        (newloc->category_mask & LC_CTYPE_MASK && setlocale(LC_CTYPE, newloc->lc_ctype.c_str()) == NULL) ||
        (newloc->category_mask & LC_MONETARY_MASK && setlocale(LC_MONETARY, newloc->lc_monetary.c_str()) == NULL) ||
        (newloc->category_mask & LC_NUMERIC_MASK && setlocale(LC_NUMERIC, newloc->lc_numeric.c_str()) == NULL) ||
        (newloc->category_mask & LC_TIME_MASK && setlocale(LC_TIME, newloc->lc_time.c_str()) == NULL) ||
        (newloc->category_mask & LC_MESSAGES_MASK && setlocale(LC_MESSAGES, newloc->lc_messages.c_str()) == NULL);

    if (is_error) {
      setlocale(LC_ALL, current_loc_name.c_str());
      errno = EINVAL;
      return (locale_t)0;
    }
  }

  // Construct and return previous locale.
  locale_t previous_loc = new locale_struct();

  // current_loc_name might be a comma-separated locale name list.
  if (current_loc_name.find(',') != std::string::npos) {
    // Tokenize locale name list.
    const char delimiter = ',';
    std::vector<std::string> tokenized;
    std::stringstream ss(current_loc_name);
    std::string s;

    while (std::getline(ss, s, delimiter)) {
      tokenized.push_back(s);
    }

    _LIBCPP_ASSERT_UNCATEGORIZED(tokenized.size() >= _NCAT, "locale-name list is too short");

    previous_loc->lc_collate  = tokenized[LC_COLLATE];
    previous_loc->lc_ctype    = tokenized[LC_CTYPE];
    previous_loc->lc_monetary = tokenized[LC_MONETARY];
    previous_loc->lc_numeric  = tokenized[LC_NUMERIC];
    previous_loc->lc_time     = tokenized[LC_TIME];
    // Skip LC_TOD.
    previous_loc->lc_messages = tokenized[LC_MESSAGES];
  } else {
    previous_loc->lc_collate  = current_loc_name;
    previous_loc->lc_ctype    = current_loc_name;
    previous_loc->lc_monetary = current_loc_name;
    previous_loc->lc_numeric  = current_loc_name;
    previous_loc->lc_time     = current_loc_name;
    previous_loc->lc_messages = current_loc_name;
  }

  previous_loc->category_mask = LC_ALL_MASK;
  return previous_loc;
}

#ifdef __cplusplus
}
#endif // __cplusplus