diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -156,6 +156,7 @@ __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 diff --git a/libcxx/include/__support/ibm/locale_mgmt_zos.h b/libcxx/include/__support/ibm/locale_mgmt_zos.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__support/ibm/locale_mgmt_zos.h @@ -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 +#include + +#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 diff --git a/libcxx/include/__support/ibm/xlocale.h b/libcxx/include/__support/ibm/xlocale.h --- a/libcxx/include/__support/ibm/xlocale.h +++ b/libcxx/include/__support/ibm/xlocale.h @@ -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" diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt --- a/libcxx/src/CMakeLists.txt +++ b/libcxx/src/CMakeLists.txt @@ -90,6 +90,10 @@ support/solaris/wcsnrtombs.inc support/solaris/xlocale.cpp ) +elseif(ZOS) + list(APPEND LIBCXX_SOURCES + support/ibm/xlocale_zos.cpp + ) endif() if (LIBCXX_ENABLE_FILESYSTEM) diff --git a/libcxx/src/support/ibm/xlocale_zos.cpp b/libcxx/src/support/ibm/xlocale_zos.cpp new file mode 100644 --- /dev/null +++ b/libcxx/src/support/ibm/xlocale_zos.cpp @@ -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 +#include + +#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 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