Skip to content

Commit

Permalink
[SystemZ][z/OS] Missing locale functions libc++
Browse files Browse the repository at this point in the history
The aim is to add the missing z/OS specific locale functions for libc++ (newlocale, freelocale and uselocale).

Reviewed By: ldionne, #libc, curdeius

Differential Revision: https://reviews.llvm.org/D98044
  • Loading branch information
muiez authored and arichardson committed Mar 30, 2021
2 parents 91d393f + ebe6161 commit 141d21c
Show file tree
Hide file tree
Showing 5 changed files with 196 additions and 0 deletions.
1 change: 1 addition & 0 deletions libcxx/include/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ set(files
__support/ibm/gettod_zos.h
__support/ibm/limits.h
__support/ibm/locale_mgmt_aix.h
__support/ibm/locale_mgmt_zos.h
__support/ibm/nanosleep.h
__support/ibm/support.h
__support/ibm/xlocale.h
Expand Down
53 changes: 53 additions & 0 deletions libcxx/include/__support/ibm/locale_mgmt_zos.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

#ifndef _LIBCPP_SUPPORT_IBM_LOCALE_MGMT_ZOS_H
#define _LIBCPP_SUPPORT_IBM_LOCALE_MGMT_ZOS_H

#if defined(__MVS__)
#include <locale.h>
#include <string>

#ifdef __cplusplus
extern "C" {
#endif

#define _LC_MAX LC_MESSAGES /* highest real category */
#define _NCAT (_LC_MAX + 1) /* maximum + 1 */

#define _CATMASK(n) (1 << (n))
#define LC_COLLATE_MASK _CATMASK(LC_COLLATE)
#define LC_CTYPE_MASK _CATMASK(LC_CTYPE)
#define LC_MONETARY_MASK _CATMASK(LC_MONETARY)
#define LC_NUMERIC_MASK _CATMASK(LC_NUMERIC)
#define LC_TIME_MASK _CATMASK(LC_TIME)
#define LC_MESSAGES_MASK _CATMASK(LC_MESSAGES)
#define LC_ALL_MASK (_CATMASK(_NCAT) - 1)

typedef struct locale_struct {
int category_mask;
std::string lc_collate;
std::string lc_ctype;
std::string lc_monetary;
std::string lc_numeric;
std::string lc_time;
std::string lc_messages;
} * locale_t;

// z/OS does not have newlocale, freelocale and uselocale.
// The functions below are workarounds in single thread mode.
locale_t newlocale(int category_mask, const char* locale, locale_t base);
void freelocale(locale_t locobj);
locale_t uselocale(locale_t newloc);

#ifdef __cplusplus
}
#endif
#endif // defined(__MVS__)
#endif // _LIBCPP_SUPPORT_IBM_LOCALE_MGMT_ZOS_H
1 change: 1 addition & 0 deletions libcxx/include/__support/ibm/xlocale.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#define _LIBCPP_SUPPORT_IBM_XLOCALE_H

#include <__support/ibm/locale_mgmt_aix.h>
#include <__support/ibm/locale_mgmt_zos.h>

#include "cstdlib"

Expand Down
4 changes: 4 additions & 0 deletions libcxx/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "SunOS")
support/solaris/wcsnrtombs.inc
support/solaris/xlocale.cpp
)
elseif(ZOS)
list(APPEND LIBCXX_SOURCES
support/ibm/xlocale_zos.cpp
)
endif()

if (LIBCXX_ENABLE_FILESYSTEM)
Expand Down
137 changes: 137 additions & 0 deletions libcxx/src/support/ibm/xlocale_zos.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
//===----------------------------------------------------------------------===//
//
// 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 <__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(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

0 comments on commit 141d21c

Please sign in to comment.