[libcxx-commits] [libcxx] [libc++][z/OS] Move z/OS to new locale API and resolve all name collisions (PR #165428)

Zibi Sarbinowski via libcxx-commits libcxx-commits at lists.llvm.org
Tue Oct 28 12:09:52 PDT 2025


https://github.com/zibi2 updated https://github.com/llvm/llvm-project/pull/165428

>From 97bb07c5546dff792dd7f7f92e56874559d35889 Mon Sep 17 00:00:00 2001
From: Zibi Sarbinowski <zibi at ca.ibm.com>
Date: Tue, 28 Oct 2025 16:12:01 +0000
Subject: [PATCH 1/3] Move z/OS to new locale API and resolve all name
 collisions

---
 libcxx/include/CMakeLists.txt                 |   2 +
 libcxx/include/__locale_dir/locale_base_api.h |   9 +-
 libcxx/include/__locale_dir/support/zos.h     | 318 ++++++++++++++
 libcxx/include/__support/ibm/vasprintf.h      |  51 +++
 libcxx/include/__undef_macros                 |  76 ++++
 libcxx/include/module.modulemap.in            |   1 +
 libcxx/include/wchar.h                        |   4 +-
 libcxx/src/support/ibm/localeconv.cpp         |  46 ++
 libcxx/src/support/ibm/mbsnrtowcs.cpp         |  18 +-
 libcxx/src/support/ibm/wcsnrtombs.cpp         |  18 +-
 libcxx/src/support/ibm/xlocale_zos.cpp        | 403 +++++++++++++-----
 ...internal.zos.locale.funcs.compile.pass.cpp |  26 ++
 .../internal.zos.mbsnrtowcs.compile.pass.cpp  |  25 ++
 .../internal.zos.nanosleep.compile.pass.cpp   |  15 +
 .../internal.zos.wcsnrtombs.compile.pass.cpp  |  25 ++
 .../locale.ctype.byname/ctype.c.pass.cpp      |  66 +++
 .../locale.ctype.byname/ctype.cxx.pass.cpp    |  71 +++
 17 files changed, 1069 insertions(+), 105 deletions(-)
 create mode 100644 libcxx/include/__locale_dir/support/zos.h
 create mode 100644 libcxx/include/__support/ibm/vasprintf.h
 create mode 100644 libcxx/src/support/ibm/localeconv.cpp
 create mode 100644 libcxx/test/libcxx/internal.zos.locale.funcs.compile.pass.cpp
 create mode 100644 libcxx/test/libcxx/internal.zos.mbsnrtowcs.compile.pass.cpp
 create mode 100644 libcxx/test/libcxx/internal.zos.nanosleep.compile.pass.cpp
 create mode 100644 libcxx/test/libcxx/internal.zos.wcsnrtombs.compile.pass.cpp
 create mode 100644 libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/ctype.c.pass.cpp
 create mode 100644 libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/ctype.cxx.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 37259a7e6e7dd..b7871e4b6ff70 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -532,6 +532,7 @@ set(files
   __locale_dir/support/no_locale/characters.h
   __locale_dir/support/no_locale/strtonum.h
   __locale_dir/support/windows.h
+  __locale_dir/support/zos.h
   __locale_dir/time.h
   __locale_dir/wbuffer_convert.h
   __locale_dir/wstring_convert.h
@@ -755,6 +756,7 @@ set(files
   __support/ibm/gettod_zos.h
   __support/ibm/locale_mgmt_zos.h
   __support/ibm/nanosleep.h
+  __support/ibm/vasprintf.h
   __support/xlocale/__nop_locale_mgmt.h
   __support/xlocale/__posix_l_fallback.h
   __support/xlocale/__strtonum_fallback.h
diff --git a/libcxx/include/__locale_dir/locale_base_api.h b/libcxx/include/__locale_dir/locale_base_api.h
index 9f3ce02a3af20..6f99e40a9dee2 100644
--- a/libcxx/include/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__locale_dir/locale_base_api.h
@@ -15,6 +15,9 @@
 #  pragma GCC system_header
 #endif
 
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
 // The platform-specific headers have to provide the following interface.
 //
 // These functions are equivalent to their C counterparts, except that __locale::__locale_t
@@ -121,13 +124,15 @@
 #    include <__locale_dir/support/fuchsia.h>
 #  elif defined(__linux__)
 #    include <__locale_dir/support/linux.h>
+#  elif defined(__MVS__)
+#    include <__locale_dir/support/zos.h>
 #  else
 
 // TODO: This is a temporary definition to bridge between the old way we defined the locale base API
 //       (by providing global non-reserved names) and the new API. As we move individual platforms
 //       towards the new way of defining the locale base API, this should disappear since each platform
 //       will define those directly.
-#    if defined(_AIX) || defined(__MVS__)
+#    if defined(_AIX) 
 #      include <__locale_dir/locale_base_api/ibm.h>
 #    elif defined(__OpenBSD__)
 #      include <__locale_dir/locale_base_api/openbsd.h>
@@ -317,4 +322,6 @@ _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP_HAS_LOCALIZATION
 
+_LIBCPP_POP_MACROS
+
 #endif // _LIBCPP___LOCALE_DIR_LOCALE_BASE_API_H
diff --git a/libcxx/include/__locale_dir/support/zos.h b/libcxx/include/__locale_dir/support/zos.h
new file mode 100644
index 0000000000000..3ab85bc0fabd9
--- /dev/null
+++ b/libcxx/include/__locale_dir/support/zos.h
@@ -0,0 +1,318 @@
+//===-----------------------------------------------------------------------===//
+//
+// 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___LOCALE_DIR_SUPPORT_IBM_H
+#define _LIBCPP___LOCALE_DIR_SUPPORT_IBM_H
+
+#include <__support/ibm/locale_mgmt_zos.h>
+#include <__support/ibm/vasprintf.h>
+
+#include "cstdlib"
+#include <clocale> // std::lconv
+#include <locale.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include <wctype.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// These functions are exported within std namespace
+// for compatibility with previous versions.
+_LIBCPP_EXPORTED_FROM_ABI int isdigit_l(int, locale_t);
+_LIBCPP_EXPORTED_FROM_ABI int isxdigit_l(int, locale_t);
+
+namespace __locale {
+struct __locale_guard {
+  _LIBCPP_HIDE_FROM_ABI __locale_guard(locale_t& __loc) : __old_loc_(std::uselocale(__loc)) {}
+
+  _LIBCPP_HIDE_FROM_ABI ~__locale_guard() {
+    // if (__old_loc_)
+    if (__old_loc_ != (locale_t)0)
+      std::uselocale(__old_loc_);
+  }
+
+  locale_t __old_loc_;
+
+  __locale_guard(__locale_guard const&)            = delete;
+  __locale_guard& operator=(__locale_guard const&) = delete;
+};
+
+//
+// Locale management
+//
+#define _LIBCPP_COLLATE_MASK LC_COLLATE_MASK
+#define _LIBCPP_CTYPE_MASK LC_CTYPE_MASK
+#define _LIBCPP_MONETARY_MASK LC_MONETARY_MASK
+#define _LIBCPP_NUMERIC_MASK LC_NUMERIC_MASK
+#define _LIBCPP_TIME_MASK LC_TIME_MASK
+#define _LIBCPP_MESSAGES_MASK LC_MESSAGES_MASK
+#define _LIBCPP_ALL_MASK LC_ALL_MASK
+#define _LIBCPP_LC_ALL LC_ALL
+
+#define _LIBCPP_CLOC std::__c_locale()
+#ifndef _LIBCPP_LC_GLOBAL_LOCALE
+#  define _LIBCPP_LC_GLOBAL_LOCALE ((locale_t) - 1)
+#endif
+
+using __locale_t _LIBCPP_NODEBUG = locale_t;
+
+#if defined(_LIBCPP_BUILDING_LIBRARY)
+using __lconv_t _LIBCPP_NODEBUG = std::lconv;
+
+inline _LIBCPP_HIDE_FROM_ABI __locale_t __newlocale(int __category_mask, const char* __locale, __locale_t __base) {
+  return newlocale(__category_mask, __locale, __base);
+}
+
+inline _LIBCPP_HIDE_FROM_ABI void __freelocale(__locale_t __loc) { freelocale(__loc); }
+
+inline _LIBCPP_HIDE_FROM_ABI char* __setlocale(int __category, char const* __locale) {
+  return ::setlocale(__category, __locale);
+}
+
+inline _LIBCPP_HIDE_FROM_ABI __lconv_t* __localeconv(__locale_t& __loc) {
+  __locale_guard __current(__loc);
+  return std::localeconv();
+}
+#endif // _LIBCPP_BUILDING_LIBRARY
+
+// The following are not POSIX routines.  These are quick-and-dirty hacks
+// to make things pretend to work
+
+//
+// Strtonum functions
+//
+inline _LIBCPP_HIDE_FROM_ABI float __strtof(const char* __nptr, char** __endptr, __locale_t __loc) {
+  __locale_guard __newloc(__loc);
+  return ::strtof(__nptr, __endptr);
+}
+
+inline _LIBCPP_HIDE_FROM_ABI double __strtod(const char* __nptr, char** __endptr, __locale_t __loc) {
+  __locale_guard __newloc(__loc);
+  return ::strtod(__nptr, __endptr);
+}
+
+inline _LIBCPP_HIDE_FROM_ABI long double __strtold(const char* __nptr, char** __endptr, __locale_t __loc) {
+  __locale_guard __newloc(__loc);
+  return ::strtold(__nptr, __endptr);
+}
+
+inline _LIBCPP_HIDE_FROM_ABI long long __strtoll(const char* __nptr, char** __endptr, int __base, __locale_t __loc) {
+  __locale_guard __newloc(__loc);
+  return ::strtoll(__nptr, __endptr, __base);
+}
+
+inline _LIBCPP_HIDE_FROM_ABI unsigned long long
+__strtoull(const char* __nptr, char** __endptr, int __base, __locale_t __loc) {
+  __locale_guard __newloc(__loc);
+  return ::strtoull(__nptr, __endptr, __base);
+}
+
+//
+// Character manipulation functions
+//
+namespace __ibm {
+_LIBCPP_HIDE_FROM_ABI int islower_l(int, __locale_t);
+_LIBCPP_HIDE_FROM_ABI int isupper_l(int, __locale_t);
+_LIBCPP_HIDE_FROM_ABI int iswalpha_l(wint_t, __locale_t);
+_LIBCPP_HIDE_FROM_ABI int iswblank_l(wint_t, __locale_t);
+_LIBCPP_HIDE_FROM_ABI int iswcntrl_l(wint_t, __locale_t);
+_LIBCPP_HIDE_FROM_ABI int iswdigit_l(wint_t, __locale_t);
+_LIBCPP_HIDE_FROM_ABI int iswlower_l(wint_t, __locale_t);
+_LIBCPP_HIDE_FROM_ABI int iswprint_l(wint_t, __locale_t);
+_LIBCPP_HIDE_FROM_ABI int iswpunct_l(wint_t, __locale_t);
+_LIBCPP_HIDE_FROM_ABI int iswspace_l(wint_t, __locale_t);
+_LIBCPP_HIDE_FROM_ABI int iswupper_l(wint_t, __locale_t);
+_LIBCPP_HIDE_FROM_ABI int iswxdigit_l(wint_t, __locale_t);
+_LIBCPP_HIDE_FROM_ABI int toupper_l(int, __locale_t);
+_LIBCPP_HIDE_FROM_ABI int tolower_l(int, __locale_t);
+_LIBCPP_HIDE_FROM_ABI wint_t towupper_l(wint_t, __locale_t);
+_LIBCPP_HIDE_FROM_ABI wint_t towlower_l(wint_t, __locale_t);
+_LIBCPP_HIDE_FROM_ABI int strcoll_l(const char*, const char*, __locale_t);
+_LIBCPP_HIDE_FROM_ABI size_t strxfrm_l(char*, const char*, size_t, __locale_t);
+_LIBCPP_HIDE_FROM_ABI size_t strftime_l(char*, size_t, const char*, const struct tm*, __locale_t);
+_LIBCPP_HIDE_FROM_ABI int wcscoll_l(const wchar_t*, const wchar_t*, __locale_t);
+_LIBCPP_HIDE_FROM_ABI size_t wcsxfrm_l(wchar_t*, const wchar_t*, size_t, __locale_t);
+_LIBCPP_HIDE_FROM_ABI int iswctype_l(wint_t, wctype_t, __locale_t);
+_LIBCPP_HIDE_FROM_ABI size_t mbsnrtowcs(wchar_t*, const char**, size_t, size_t, mbstate_t*);
+_LIBCPP_HIDE_FROM_ABI size_t wcsnrtombs(char*, const wchar_t**, size_t, size_t, mbstate_t*);
+
+// These functions are not used internally by libcxx
+// and are included for completness.
+_LIBCPP_HIDE_FROM_ABI int isalnum_l(int, __locale_t);
+_LIBCPP_HIDE_FROM_ABI int isalpha_l(int, __locale_t);
+_LIBCPP_HIDE_FROM_ABI int isblank_l(int, __locale_t);
+_LIBCPP_HIDE_FROM_ABI int iscntrl_l(int, __locale_t);
+_LIBCPP_HIDE_FROM_ABI int isgraph_l(int, __locale_t);
+_LIBCPP_HIDE_FROM_ABI int isprint_l(int, __locale_t);
+_LIBCPP_HIDE_FROM_ABI int ispunct_l(int, __locale_t);
+_LIBCPP_HIDE_FROM_ABI int isspace_l(int, __locale_t);
+_LIBCPP_HIDE_FROM_ABI int iswalnum_l(wint_t, __locale_t);
+_LIBCPP_HIDE_FROM_ABI int iswgraph_l(wint_t, __locale_t);
+} // namespace __ibm
+
+using namespace __ibm;
+
+#if defined(_LIBCPP_BUILDING_LIBRARY)
+inline _LIBCPP_HIDE_FROM_ABI int __islower(int __c, __locale_t __loc) { return islower_l(__c, __loc); }
+
+inline _LIBCPP_HIDE_FROM_ABI int __isupper(int __c, __locale_t __loc) { return isupper_l(__c, __loc); }
+#endif
+
+inline _LIBCPP_HIDE_FROM_ABI int __isdigit(int __c, __locale_t __loc) { return isdigit_l(__c, __loc); }
+
+inline _LIBCPP_HIDE_FROM_ABI int __isxdigit(int __c, __locale_t __loc) { return isxdigit_l(__c, __loc); }
+
+#if defined(_LIBCPP_BUILDING_LIBRARY)
+inline _LIBCPP_HIDE_FROM_ABI int __toupper(int __c, __locale_t __loc) { return toupper_l(__c, __loc); }
+
+inline _LIBCPP_HIDE_FROM_ABI int __tolower(int __c, __locale_t __loc) { return tolower_l(__c, __loc); }
+
+inline _LIBCPP_HIDE_FROM_ABI int __strcoll(const char* __s1, const char* __s2, __locale_t __loc) {
+  return strcoll_l(__s1, __s2, __loc);
+}
+
+inline _LIBCPP_HIDE_FROM_ABI size_t __strxfrm(char* __dest, const char* __src, size_t __n, __locale_t __loc) {
+  return strxfrm_l(__dest, __src, __n, __loc);
+}
+
+#  if _LIBCPP_HAS_WIDE_CHARACTERS
+inline _LIBCPP_HIDE_FROM_ABI int __iswctype(wint_t __c, wctype_t __type, __locale_t __loc) {
+  return iswctype_l(__c, __type, __loc);
+}
+
+inline _LIBCPP_HIDE_FROM_ABI int __iswspace(wint_t __c, __locale_t __loc) { return iswspace_l(__c, __loc); }
+
+inline _LIBCPP_HIDE_FROM_ABI int __iswprint(wint_t __c, __locale_t __loc) { return iswprint_l(__c, __loc); }
+
+inline _LIBCPP_HIDE_FROM_ABI int __iswcntrl(wint_t __c, __locale_t __loc) { return iswcntrl_l(__c, __loc); }
+
+inline _LIBCPP_HIDE_FROM_ABI int __iswupper(wint_t __c, __locale_t __loc) { return iswupper_l(__c, __loc); }
+
+inline _LIBCPP_HIDE_FROM_ABI int __iswlower(wint_t __c, __locale_t __loc) { return iswlower_l(__c, __loc); }
+
+inline _LIBCPP_HIDE_FROM_ABI int __iswalpha(wint_t __c, __locale_t __loc) { return iswalpha_l(__c, __loc); }
+
+inline _LIBCPP_HIDE_FROM_ABI int __iswblank(wint_t __c, __locale_t __loc) { return iswblank_l(__c, __loc); }
+
+inline _LIBCPP_HIDE_FROM_ABI int __iswdigit(wint_t __c, __locale_t __loc) { return iswdigit_l(__c, __loc); }
+
+inline _LIBCPP_HIDE_FROM_ABI int __iswpunct(wint_t __c, __locale_t __loc) { return iswpunct_l(__c, __loc); }
+
+inline _LIBCPP_HIDE_FROM_ABI int __iswxdigit(wint_t __c, __locale_t __loc) { return iswxdigit_l(__c, __loc); }
+
+inline _LIBCPP_HIDE_FROM_ABI wint_t __towupper(wint_t __c, __locale_t __loc) { return towupper_l(__c, __loc); }
+
+inline _LIBCPP_HIDE_FROM_ABI wint_t __towlower(wint_t __c, __locale_t __loc) { return towlower_l(__c, __loc); }
+
+inline _LIBCPP_HIDE_FROM_ABI int __wcscoll(const wchar_t* __ws1, const wchar_t* __ws2, __locale_t __loc) {
+  return wcscoll_l(__ws1, __ws2, __loc);
+}
+
+inline _LIBCPP_HIDE_FROM_ABI size_t __wcsxfrm(wchar_t* __dest, const wchar_t* __src, size_t __n, __locale_t __loc) {
+  return wcsxfrm_l(__dest, __src, __n, __loc);
+}
+#  endif // _LIBCPP_HAS_WIDE_CHARACTERS
+
+inline _LIBCPP_HIDE_FROM_ABI
+size_t __strftime(char* __s, size_t __max, const char* __format, const struct tm* __tm, __locale_t __loc) {
+  return strftime_l(__s, __max, __format, __tm, __loc);
+}
+
+//
+// Other functions
+//
+inline _LIBCPP_HIDE_FROM_ABI decltype(MB_CUR_MAX) __mb_len_max(__locale_t __loc) {
+  __locale_guard __current(__loc);
+  return MB_CUR_MAX;
+}
+
+#  if _LIBCPP_HAS_WIDE_CHARACTERS
+inline _LIBCPP_HIDE_FROM_ABI wint_t __btowc(int __c, __locale_t __loc) {
+  __locale_guard __current(__loc);
+  return std::btowc(__c);
+}
+
+inline _LIBCPP_HIDE_FROM_ABI int __wctob(wint_t __c, __locale_t __loc) {
+  __locale_guard __current(__loc);
+  return std::wctob(__c);
+}
+
+inline _LIBCPP_HIDE_FROM_ABI size_t
+__wcsnrtombs(char* __dest, const wchar_t** __src, size_t __nwc, size_t __len, mbstate_t* __ps, __locale_t __loc) {
+  __locale_guard __current(__loc);
+  return wcsnrtombs(__dest, __src, __nwc, __len, __ps); // non-standard
+}
+
+inline _LIBCPP_HIDE_FROM_ABI size_t __wcrtomb(char* __s, wchar_t __wc, mbstate_t* __ps, __locale_t __loc) {
+  __locale_guard __current(__loc);
+  return std::wcrtomb(__s, __wc, __ps);
+}
+
+inline _LIBCPP_HIDE_FROM_ABI size_t
+__mbsnrtowcs(wchar_t* __dest, const char** __src, size_t __nms, size_t __len, mbstate_t* __ps, __locale_t __loc) {
+  __locale_guard __current(__loc);
+  return mbsnrtowcs(__dest, __src, __nms, __len, __ps); // non-standard
+}
+
+inline _LIBCPP_HIDE_FROM_ABI size_t
+__mbrtowc(wchar_t* __pwc, const char* __s, size_t __n, mbstate_t* __ps, __locale_t __loc) {
+  __locale_guard __current(__loc);
+  return std::mbrtowc(__pwc, __s, __n, __ps);
+}
+
+inline _LIBCPP_HIDE_FROM_ABI int __mbtowc(wchar_t* __pwc, const char* __pmb, size_t __max, __locale_t __loc) {
+  __locale_guard __current(__loc);
+  return std::mbtowc(__pwc, __pmb, __max);
+}
+
+inline _LIBCPP_HIDE_FROM_ABI size_t __mbrlen(const char* __s, size_t __n, mbstate_t* __ps, __locale_t __loc) {
+  __locale_guard __current(__loc);
+  return std::mbrlen(__s, __n, __ps);
+}
+
+inline _LIBCPP_HIDE_FROM_ABI size_t
+__mbsrtowcs(wchar_t* __dest, const char** __src, size_t __len, mbstate_t* __ps, __locale_t __loc) {
+  __locale_guard __current(__loc);
+  return std::mbsrtowcs(__dest, __src, __len, __ps);
+}
+#  endif // _LIBCPP_BUILDING_LIBRARY
+#endif   // _LIBCPP_HAS_WIDE_CHARACTERS
+
+_LIBCPP_HIDE_FROM_ABI inline _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 4, 5) int __snprintf(
+    char* __s, size_t __n, __locale_t __loc, const char* __format, ...) {
+  va_list __va;
+  va_start(__va, __format);
+  __locale_guard __current(__loc);
+  int __res = std::vsnprintf(__s, __n, __format, __va);
+  va_end(__va);
+  return __res;
+}
+
+_LIBCPP_HIDE_FROM_ABI inline _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 3, 4) int __asprintf(
+    char** __s, __locale_t __loc, const char* __format, ...) {
+  va_list __va;
+  va_start(__va, __format);
+  __locale_guard __current(__loc);
+  int __res = std::__ibm::vasprintf(__s, __format, __va); // non-standard
+  va_end(__va);
+  return __res;
+}
+
+_LIBCPP_HIDE_FROM_ABI inline _LIBCPP_ATTRIBUTE_FORMAT(__scanf__, 3, 4) int __sscanf(
+    const char* __s, __locale_t __loc, const char* __format, ...) {
+  va_list __va;
+  va_start(__va, __format);
+  __locale_guard __current(__loc);
+  int __res = std::vsscanf(__s, __format, __va);
+  va_end(__va);
+  return __res;
+}
+} // namespace __locale
+_LIBCPP_END_NAMESPACE_STD
+#endif // _LIBCPP___LOCALE_DIR_SUPPORT_IBM_H
diff --git a/libcxx/include/__support/ibm/vasprintf.h b/libcxx/include/__support/ibm/vasprintf.h
new file mode 100644
index 0000000000000..bf42ec7963f45
--- /dev/null
+++ b/libcxx/include/__support/ibm/vasprintf.h
@@ -0,0 +1,51 @@
+//===-----------------------------------------------------------------------===//
+//
+// 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_IBMVASPRINTF_H
+#define _LIBCPP___SUPPORT_IBMVASPRINTF_H
+
+#include <cstdlib>  // malloc, realloc
+#include <stdarg.h> // va_copy, va_end
+#include <stdio.h>  // vsnprintf
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __ibm {
+
+inline _LIBCPP_HIDE_FROM_ABI
+_LIBCPP_ATTRIBUTE_FORMAT(__printf__, 2, 0) int vasprintf(char** strp, const char* fmt, va_list ap) {
+  const size_t buff_size = 256;
+  if ((*strp = (char*)malloc(buff_size)) == nullptr) {
+    return -1;
+  }
+
+  va_list ap_copy;
+  // va_copy may not be provided by the C library in C++03 mode.
+#if defined(_LIBCPP_CXX03_LANG) && __has_builtin(__builtin_va_copy)
+#  if defined(__MVS__) && !defined(_VARARG_EXT_)
+  __builtin_zos_va_copy(ap_copy, ap);
+#  else
+  __builtin_va_copy(ap_copy, ap);
+#  endif
+#else
+  va_copy(ap_copy, ap);
+#endif
+  int str_size = vsnprintf(*strp, buff_size, fmt, ap_copy);
+  va_end(ap_copy);
+
+  if ((size_t)str_size >= buff_size) {
+    if ((*strp = (char*)realloc(*strp, str_size + 1)) == nullptr) {
+      return -1;
+    }
+    str_size = vsnprintf(*strp, str_size + 1, fmt, ap);
+  }
+  return str_size;
+}
+
+} // namespace __ibm
+_LIBCPP_END_NAMESPACE_STD
+#endif // _LIBCPP___SUPPORT_IBMVASPRINTF_H
diff --git a/libcxx/include/__undef_macros b/libcxx/include/__undef_macros
index 29ab327e1c375..f02547841e621 100644
--- a/libcxx/include/__undef_macros
+++ b/libcxx/include/__undef_macros
@@ -26,3 +26,79 @@
 #ifdef erase
 #  undef erase
 #endif
+
+#ifdef __islower
+#  undef __islower
+#endif
+
+#ifdef __isupper
+#  undef __isupper
+#endif
+
+#ifdef __isdigit
+#  undef __isdigit
+#endif
+
+#ifdef __isxdigit
+#  undef __isxdigit
+#endif
+
+#ifdef __toupper
+#  undef __toupper
+#endif
+
+#ifdef __tolower
+#  undef __tolower
+#endif
+
+#ifdef __iswctype
+#  undef __iswctype
+#endif
+
+#ifdef __iswspace
+#  undef __iswspace
+#endif
+
+#ifdef __iswprint
+#  undef __iswprint
+#endif
+
+#ifdef __iswcntrl
+#  undef __iswcntrl
+#endif
+
+#ifdef __iswupper
+#  undef __iswupper
+#endif
+
+#ifdef __iswlower
+#  undef __iswlower
+#endif
+
+#ifdef __iswalpha
+#  undef __iswalpha
+#endif
+
+#ifdef __iswblank
+#  undef __iswblank
+#endif
+
+#ifdef __iswdigit
+#  undef __iswdigit
+#endif
+
+#ifdef __iswpunct
+#  undef __iswpunct
+#endif
+
+#ifdef __iswxdigit
+#  undef __iswxdigit
+#endif
+
+#ifdef __towupper
+#  undef __towupper
+#endif
+
+#ifdef __towlower
+#  undef __towlower
+#endif
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index a86d6c6a43d0e..28ea168dbb8c8 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1587,6 +1587,7 @@ module std [system] {
       textual header "__locale_dir/support/freebsd.h"
       textual header "__locale_dir/support/fuchsia.h"
       textual header "__locale_dir/support/linux.h"
+      textual header "__locale_dir/support/zos.h"
       textual header "__locale_dir/support/no_locale/characters.h"
       textual header "__locale_dir/support/no_locale/strtonum.h"
       textual header "__locale_dir/support/windows.h"
diff --git a/libcxx/include/wchar.h b/libcxx/include/wchar.h
index a932dd266b862..82984143aa6c5 100644
--- a/libcxx/include/wchar.h
+++ b/libcxx/include/wchar.h
@@ -193,14 +193,14 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD wchar_t* wmemchr(wchar_t
 }
 #      endif
 
-#      if defined(__cplusplus) && (defined(_LIBCPP_MSVCRT_LIKE) || defined(__MVS__))
+#      if defined(__cplusplus) && defined(_LIBCPP_MSVCRT_LIKE)
 extern "C" {
 size_t mbsnrtowcs(
     wchar_t* __restrict __dst, const char** __restrict __src, size_t __nmc, size_t __len, mbstate_t* __restrict __ps);
 size_t wcsnrtombs(
     char* __restrict __dst, const wchar_t** __restrict __src, size_t __nwc, size_t __len, mbstate_t* __restrict __ps);
 } // extern "C"
-#      endif // __cplusplus && (_LIBCPP_MSVCRT || __MVS__)
+#      endif // __cplusplus && _LIBCPP_MSVCRT
 #    endif   // _LIBCPP_HAS_WIDE_CHARACTERS
 #  endif     // _LIBCPP_WCHAR_H
 
diff --git a/libcxx/src/support/ibm/localeconv.cpp b/libcxx/src/support/ibm/localeconv.cpp
new file mode 100644
index 0000000000000..4ab6b7a75f618
--- /dev/null
+++ b/libcxx/src/support/ibm/localeconv.cpp
@@ -0,0 +1,46 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <__locale>
+#include <__locale_dir/support/zos.h> // __locale_guard
+#include <memory>
+#include <stdlib.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+lconv *__libcpp_localeconv_l(locale_t& __l)
+{
+  __locale::__locale_guard __current(__l);
+
+  lconv * lc = localeconv();
+  static lconv newlc;
+  newlc = *lc;
+
+  enum {max_char_num = 20};
+#define DeepCopy(mbr) \
+  static char buf_##mbr[max_char_num]; \
+  strncpy(buf_##mbr, lc->mbr, max_char_num); \
+  newlc.mbr = buf_##mbr;
+
+  DeepCopy(decimal_point);
+  DeepCopy(thousands_sep);
+  DeepCopy(grouping);
+  DeepCopy(int_curr_symbol);
+  DeepCopy(currency_symbol);
+  DeepCopy(mon_decimal_point);
+  DeepCopy(mon_thousands_sep);
+  DeepCopy(mon_grouping);
+  DeepCopy(positive_sign);
+  DeepCopy(negative_sign);
+  DeepCopy(__left_parenthesis);
+  DeepCopy(__right_parenthesis);
+
+  return &newlc;
+}
+
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/support/ibm/mbsnrtowcs.cpp b/libcxx/src/support/ibm/mbsnrtowcs.cpp
index d0006a8468aad..590ac0617a67d 100644
--- a/libcxx/src/support/ibm/mbsnrtowcs.cpp
+++ b/libcxx/src/support/ibm/mbsnrtowcs.cpp
@@ -6,11 +6,16 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include <__locale>
+#include <__locale_dir/support/zos.h> // __locale_guard
 #include <cstddef>  // size_t
 #include <cwchar>   // mbstate_t
 #include <limits.h> // MB_LEN_MAX
 #include <string.h> // wmemcpy
 
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __locale {
+namespace __ibm {
 // Returns the number of wide characters found in the multi byte sequence `src`
 // (of `src_size_bytes`), that fit in the buffer `dst` (of `max_dest_chars`
 // elements size). The count returned excludes the null terminator.
@@ -18,7 +23,7 @@
 // Returns (size_t) -1 when an invalid sequence is encountered.
 // Leaves *`src` pointing to the next character to convert or NULL
 // if a null character was converted from *`src`.
-_LIBCPP_EXPORTED_FROM_ABI size_t mbsnrtowcs(
+size_t mbsnrtowcs(
     wchar_t* __restrict dst,
     const char** __restrict src,
     size_t src_size_bytes,
@@ -95,3 +100,14 @@ _LIBCPP_EXPORTED_FROM_ABI size_t mbsnrtowcs(
 
   return dest_converted;
 }
+
+size_t __libcpp_mbsnrtowcs_l(wchar_t * dest, const char **src, size_t nms,
+                          size_t len, mbstate_t *ps, locale_t l)
+{
+  __locale::__locale_guard current(l);
+  return mbsnrtowcs(dest, src, nms, len, ps);
+}
+
+} // namespace __ibm
+} // namespace __locale
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/support/ibm/wcsnrtombs.cpp b/libcxx/src/support/ibm/wcsnrtombs.cpp
index df87b9ea07f85..437ea52ecf1ac 100644
--- a/libcxx/src/support/ibm/wcsnrtombs.cpp
+++ b/libcxx/src/support/ibm/wcsnrtombs.cpp
@@ -6,18 +6,23 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include <__locale>
+#include <__locale_dir/support/zos.h> // __locale_guard
 #include <cwchar>   // mbstate_t
 #include <limits.h> // MB_LEN_MAX
 #include <stdlib.h> // MB_CUR_MAX, size_t
 #include <string.h> // memcpy
 
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __locale {
+namespace __ibm {
 // Converts `max_source_chars` from the wide character buffer pointer to by *`src`,
 // into the multi byte character sequence buffer stored at `dst`, which must be
 // `dst_size_bytes` bytes in size. Returns the number of bytes in the sequence
 // converted from *src, excluding the null terminator.
 // Returns (size_t) -1 if an error occurs and sets errno.
 // If `dst` is NULL, `dst_size_bytes` is ignored and no bytes are copied to `dst`.
-_LIBCPP_EXPORTED_FROM_ABI size_t wcsnrtombs(
+size_t wcsnrtombs(
     char* __restrict dst,
     const wchar_t** __restrict src,
     size_t max_source_chars,
@@ -92,3 +97,14 @@ _LIBCPP_EXPORTED_FROM_ABI size_t wcsnrtombs(
 
   return dest_converted;
 }
+
+size_t __libcpp_wcsnrtombs_l(char *dest, const wchar_t **src, size_t nwc,
+                             size_t len, mbstate_t *ps, locale_t l)
+{
+  __locale::__locale_guard __current(l);
+  return wcsnrtombs(dest, src, nwc, len, ps);
+}
+
+} // namespace __ibm
+} // namespace __locale
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/support/ibm/xlocale_zos.cpp b/libcxx/src/support/ibm/xlocale_zos.cpp
index 136999ec0b02f..22bc0b0f72feb 100644
--- a/libcxx/src/support/ibm/xlocale_zos.cpp
+++ b/libcxx/src/support/ibm/xlocale_zos.cpp
@@ -6,125 +6,328 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "include/ibm_xlocale.h"
+
 #include <__assert>
-#include <__support/ibm/xlocale.h>
-#include <sstream>
-#include <vector>
+#include <__locale_dir/support/zos.h>
+#include <errno.h>
 
-#ifdef __cplusplus
-extern "C" {
-#endif // __cplusplus
+_LIBCPP_BEGIN_NAMESPACE_STD
 
-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));
+#define CategoryList(pair, sep) \
+  pair(LC_COLLATE, lc_collate) sep \
+  pair(LC_CTYPE, lc_ctype) sep \
+  pair(LC_MONETARY, lc_monetary) sep \
+  pair(LC_NUMERIC, lc_numeric) sep \
+  pair(LC_TIME, lc_time) sep \
+  pair(LC_MESSAGES, lc_messages)
+
+// check ids and masks agree
+#define check_ids_and_masks_agree(id, _) \
+  static_assert((1 << id) == id##_MASK, "id and mask do not agree for " #id); \
+  static_assert((1 << id) == _CATMASK(id), "mask does not have expected value for " #id);
+CategoryList(check_ids_and_masks_agree,)
+#undef check_ids_and_masks_agree
+
+// check that LC_ALL_MASK is defined as expected
+#define get_mask(id, _) id##_MASK
+static_assert(LC_ALL_MASK == (CategoryList(get_mask, |)), "LC_ALL_MASK does not have the expected value.  Check that its definition includes all supported categories");
+#undef get_mask
+
+
+// initialize c_locale
+#define init_clocale(id, locale_member) "C",
+static locale_struct c_locale = { LC_ALL_MASK, CategoryList(init_clocale, ) };
+#undef init_clocale
 
-  // Check for errors.
-  if (category_mask == LC_ALL_MASK && setlocale(LC_ALL, locale) == nullptr) {
+static locale_t current_locale = _LIBCPP_LC_GLOBAL_LOCALE;
+
+
+locale_t __c_locale() {
+  return &c_locale;
+}
+
+// locale
+locale_t newlocale(int category_mask, const char* locale, locale_t base) {
+  // start with some basic checks
+  if (! locale) {
     errno = EINVAL;
-    return (locale_t)0;
-  } else {
-    for (int _Cat = 0; _Cat <= _LC_MAX; ++_Cat) {
-      if ((_CATMASK(_Cat) & category_mask) != 0 && setlocale(_Cat, locale) == nullptr) {
-        setlocale(LC_ALL, current_loc_name.c_str());
-        errno = EINVAL;
-        return (locale_t)0;
-      }
-    }
+    return (locale_t) 0;
+  }
+  if (category_mask & ~LC_ALL_MASK) {
+    // then there are bits in category_mask that does not correspond
+    // to a valid category
+    errno = EINVAL;
+    return (locale_t) 0;
   }
 
-  // Create new locale.
-  locale_t newloc = new locale_struct();
+  locale_t  new_loc = new locale_struct;
+  int num_locales_not_found = 0;
+
+  if (base && base != _LIBCPP_LC_GLOBAL_LOCALE)
+    *new_loc = *base;
+
+  auto set_for_category = [&](int id, int mask, std::string &setting) {
+    const char *setting_to_apply = nullptr;
+
+    if (category_mask & mask)
+      setting_to_apply = locale;
+    else if (!base)
+      setting_to_apply = "C";
 
-  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;
+    if (setting_to_apply) {
+      // setlocale takes the id, not the mask
+      const char *saved_setting = setlocale(id, nullptr);
+      if (setlocale(id, setting_to_apply)) {
+        // then setting_to_apply is valid for this category
+        // restore the saved setting
+        setlocale(id, saved_setting);
+
+        new_loc->category_mask |= mask;
+        setting = setting_to_apply;
+      } else {
+        // unknown locale for this category
+        num_locales_not_found++;
+      }
     }
-    delete base;
-  } else {
-    newloc->category_mask = category_mask;
+  };
+
+  // now overlay values
+#define Set(id, locale_member) set_for_category(id, id##_MASK, new_loc->locale_member)
+  CategoryList(Set, ;);
+#undef Set
+
+  if (num_locales_not_found != 0) {
+    delete new_loc;
+    errno = ENOENT;
+    new_loc = (locale_t) 0;
   }
 
-  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()) == nullptr) ||
-        (newloc->category_mask & LC_CTYPE_MASK && setlocale(LC_CTYPE, newloc->lc_ctype.c_str()) == nullptr) ||
-        (newloc->category_mask & LC_MONETARY_MASK && setlocale(LC_MONETARY, newloc->lc_monetary.c_str()) == nullptr) ||
-        (newloc->category_mask & LC_NUMERIC_MASK && setlocale(LC_NUMERIC, newloc->lc_numeric.c_str()) == nullptr) ||
-        (newloc->category_mask & LC_TIME_MASK && setlocale(LC_TIME, newloc->lc_time.c_str()) == nullptr) ||
-        (newloc->category_mask & LC_MESSAGES_MASK && setlocale(LC_MESSAGES, newloc->lc_messages.c_str()) == nullptr);
-
-    if (is_error) {
-      setlocale(LC_ALL, current_loc_name.c_str());
+  return new_loc;
+}
+
+void freelocale(locale_t locobj) {
+  if (locobj != nullptr && locobj != &c_locale && locobj != _LIBCPP_LC_GLOBAL_LOCALE)
+    delete locobj;
+}
+
+locale_t uselocale(locale_t new_loc) {
+  locale_t prev_loc = current_locale;
+
+  if (new_loc == _LIBCPP_LC_GLOBAL_LOCALE) {
+    current_locale = _LIBCPP_LC_GLOBAL_LOCALE;
+  } else if (new_loc != nullptr) {
+    locale_struct  saved_locale;
+    saved_locale.category_mask = 0;
+
+    auto apply_category = [&](int id, int mask, std::string &setting, std::string &save_setting)-> bool {
+      if (new_loc->category_mask & mask) {
+        const char *old_setting = setlocale(id, setting.c_str());
+        if (old_setting) {
+          // we were able to set for this category.  Save the old setting
+          // in case a subsequent category fails, and we need to restore
+          // all earlier settings.
+          saved_locale.category_mask |= mask;
+          save_setting = old_setting;
+          return true;
+        }
+        return false;
+      }
+      return true;
+    };
+
+#define Apply(id, locale_member) apply_category(id, id##_MASK, new_loc->locale_member, saved_locale. locale_member)
+    bool is_ok = CategoryList(Apply, &&);
+#undef Apply
+
+    if (!is_ok) {
+      auto restore = [&](int id, int mask, std::string &setting) {
+        if (saved_locale.category_mask & mask)
+          setlocale(id, setting.c_str());
+      };
+#define Restore(id, locale_member) restore(id, id##_MASK, saved_locale. locale_member);
+      CategoryList(Restore,);
+#undef Restore
       errno = EINVAL;
-      return (locale_t)0;
+      return nullptr;
     }
+    current_locale = new_loc;
   }
 
-  // Construct and return previous locale.
-  locale_t previous_loc = new locale_struct();
+  return prev_loc;
+}
 
-  // 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;
+int isdigit_l(int __c, locale_t  __l) {
+  __locale::__locale_guard __newloc(__l);
+  return ::isdigit(__c);
+}
 
-    while (std::getline(ss, s, delimiter)) {
-      tokenized.push_back(s);
-    }
+int isxdigit_l(int __c, locale_t  __l) {
+  __locale::__locale_guard __newloc(__l);
+  return ::isxdigit(__c);
+}
 
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(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;
-  }
+namespace __locale {
+namespace __ibm {
+int isalnum_l(int __c, locale_t  __l) {
+  __locale::__locale_guard __newloc(__l);
+  return ::isalnum(__c);
+}
+
+int isalpha_l(int __c, locale_t  __l) {
+  __locale::__locale_guard __newloc(__l);
+  return ::isalpha(__c);
+}
+
+int isblank_l(int __c, locale_t  __l) {
+  __locale::__locale_guard __newloc(__l);
+  return ::isblank(__c);
+}
+
+int iscntrl_l(int __c, locale_t  __l) {
+  __locale::__locale_guard __newloc(__l);
+  return ::iscntrl(__c);
+}
+
+int isgraph_l(int __c, locale_t  __l) {
+  __locale::__locale_guard __newloc(__l);
+  return ::isgraph(__c);
+}
+
+int islower_l(int __c, locale_t  __l) {
+  __locale::__locale_guard __newloc(__l);
+  return ::islower(__c);
+}
+
+int isprint_l(int __c, locale_t  __l) {
+  __locale::__locale_guard __newloc(__l);
+  return ::isprint(__c);
+}
+
+int ispunct_l(int __c, locale_t  __l) {
+  __locale::__locale_guard __newloc(__l);
+  return ::ispunct(__c);
+}
+
+int isspace_l(int __c, locale_t  __l) {
+  __locale::__locale_guard __newloc(__l);
+  return ::isspace(__c);
+}
+
+int isupper_l(int __c, locale_t  __l) {
+  __locale::__locale_guard __newloc(__l);
+  return ::isupper(__c);
+}
+
+int iswalnum_l(wint_t __c, locale_t  __l) {
+  __locale::__locale_guard __newloc(__l);
+  return ::iswalnum(__c);
+}
+
+int iswalpha_l(wint_t __c, locale_t  __l) {
+  __locale::__locale_guard __newloc(__l);
+  return ::iswalpha(__c);
+}
+
+int iswblank_l(wint_t __c, locale_t  __l) {
+  __locale::__locale_guard __newloc(__l);
+  return ::iswblank(__c);
+}
+
+int iswcntrl_l(wint_t __c, locale_t  __l) {
+  __locale::__locale_guard __newloc(__l);
+  return ::iswcntrl(__c);
+}
+
+int iswdigit_l(wint_t __c, locale_t  __l) {
+  __locale::__locale_guard __newloc(__l);
+  return ::iswdigit(__c);
+}
+
+int iswgraph_l(wint_t __c, locale_t  __l) {
+  __locale::__locale_guard __newloc(__l);
+  return ::iswgraph(__c);
+}
+
+int iswlower_l(wint_t __c, locale_t  __l) {
+  __locale::__locale_guard __newloc(__l);
+  return ::iswlower(__c);
+}
+
+int iswprint_l(wint_t __c, locale_t  __l) {
+  __locale::__locale_guard __newloc(__l);
+  return ::iswprint(__c);
+}
+
+int iswpunct_l(wint_t __c, locale_t  __l) {
+  __locale::__locale_guard __newloc(__l);
+  return ::iswpunct(__c);
+}
+
+int iswspace_l(wint_t __c, locale_t  __l) {
+  __locale::__locale_guard __newloc(__l);
+  return ::iswspace(__c);
+}
+
+int iswupper_l(wint_t __c, locale_t  __l) {
+  __locale::__locale_guard __newloc(__l);
+  return ::iswupper(__c);
+}
+
+int iswxdigit_l(wint_t __c, locale_t  __l) {
+  __locale::__locale_guard __newloc(__l);
+  return ::iswxdigit(__c);
+}
+
+int toupper_l(int __c, locale_t  __l) {
+  __locale::__locale_guard __newloc(__l);
+  return ::toupper(__c);
+}
+
+int tolower_l(int __c, locale_t  __l) {
+  __locale::__locale_guard __newloc(__l);
+  return ::tolower(__c);
+}
+
+wint_t towupper_l(wint_t __c, locale_t  __l) {
+  __locale::__locale_guard __newloc(__l);
+  return ::towupper(__c);
+}
+
+wint_t towlower_l(wint_t __c, locale_t  __l) {
+  __locale::__locale_guard __newloc(__l);
+  return ::towlower(__c);
+}
+
+int strcoll_l(const char *__s1, const char *__s2, locale_t  __l) {
+  __locale::__locale_guard __newloc(__l);
+  return ::strcoll(__s1, __s2);
+}
+
+size_t strxfrm_l(char *__dest, const char *__src, size_t __n, locale_t  __l) {
+  __locale::__locale_guard __newloc(__l);
+  return ::strxfrm(__dest, __src, __n);
+}
+
+size_t strftime_l(char* __s, size_t __max, const char* __format, const struct tm* __tm, locale_t __l) {
+  __locale::__locale_guard __newloc(__l);
+  return ::strftime(__s, __max, __format, __tm);
+}
+
+int wcscoll_l(const wchar_t *__ws1, const wchar_t *__ws2, locale_t  __l) {
+  __locale::__locale_guard __newloc(__l);
+  return ::wcscoll(__ws1, __ws2);
+}
 
-  previous_loc->category_mask = LC_ALL_MASK;
-  return previous_loc;
+size_t wcsxfrm_l(wchar_t *__dest, const wchar_t *__src, size_t __n, locale_t  __l) {
+  __locale::__locale_guard __newloc(__l);
+  return ::wcsxfrm(__dest, __src, __n);
 }
 
-#ifdef __cplusplus
+int iswctype_l(wint_t __c, wctype_t __type, locale_t  __l) {
+  __locale::__locale_guard __newloc(__l);
+  return ::iswctype(__c, __type);
 }
-#endif // __cplusplus
+} // namespace __ibm
+} // namespace __locale
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/test/libcxx/internal.zos.locale.funcs.compile.pass.cpp b/libcxx/test/libcxx/internal.zos.locale.funcs.compile.pass.cpp
new file mode 100644
index 0000000000000..846cdf6862c0c
--- /dev/null
+++ b/libcxx/test/libcxx/internal.zos.locale.funcs.compile.pass.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Test that version of the POSIX functions provided outside of libc++ don't
+// cause compilation errors.
+
+struct locale_t { };
+locale_t newlocale(int category_mask, const char* locale, locale_t base);
+void freelocale(locale_t locobj);
+locale_t uselocale(locale_t newloc);
+
+#ifdef _LP64
+typedef unsigned long size_t;
+#else
+typedef unsigned int size_t;
+#endif
+typedef short mbstate_t;
+size_t mbsnrtowcs(wchar_t*, const char**, size_t, size_t, mbstate_t*);
+size_t wcsnrtombs(char*, const wchar_t**, size_t, size_t, mbstate_t*);
+
+#include <locale>
diff --git a/libcxx/test/libcxx/internal.zos.mbsnrtowcs.compile.pass.cpp b/libcxx/test/libcxx/internal.zos.mbsnrtowcs.compile.pass.cpp
new file mode 100644
index 0000000000000..fc5e5d8be790f
--- /dev/null
+++ b/libcxx/test/libcxx/internal.zos.mbsnrtowcs.compile.pass.cpp
@@ -0,0 +1,25 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Validating the following declaration of mbsnrtowcs resides in std::__locale::__ibm namespace.
+// size_t mbsnrtowcs(wchar_t*, const char**, size_t, size_t, mbstate_t*);
+
+#include <locale>
+
+int main(int, char**) {
+    const char* mb_string = "Hello, World!";
+    wchar_t w_string[20];
+    mbstate_t state;
+    size_t mb_chars = strlen(mb_string);
+    size_t w_chars = 0;
+
+    // Convert the multibyte string to a wide-character string
+    w_chars = std::__locale::__ibm::mbsnrtowcs(w_string, &mb_string, mb_chars, 13, &state);
+
+    return w_chars == 13;
+}
diff --git a/libcxx/test/libcxx/internal.zos.nanosleep.compile.pass.cpp b/libcxx/test/libcxx/internal.zos.nanosleep.compile.pass.cpp
new file mode 100644
index 0000000000000..b7a6385b496f8
--- /dev/null
+++ b/libcxx/test/libcxx/internal.zos.nanosleep.compile.pass.cpp
@@ -0,0 +1,15 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Make sure the following libcxx override can coexists with standard C function.
+// std::__ibm int nanosleep(const struct timespec* , struct timespec* );
+
+#include <time.h> // timespec
+
+int nanosleep(const struct timespec* , struct timespec* );
+#include <mutex>
diff --git a/libcxx/test/libcxx/internal.zos.wcsnrtombs.compile.pass.cpp b/libcxx/test/libcxx/internal.zos.wcsnrtombs.compile.pass.cpp
new file mode 100644
index 0000000000000..1fabc7a433c86
--- /dev/null
+++ b/libcxx/test/libcxx/internal.zos.wcsnrtombs.compile.pass.cpp
@@ -0,0 +1,25 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Validating the following declaration of wcsnrtombs resides in std::__locale::__ibm namespace.
+// size_t wcsnrtombs(char*, const wchar_t**, size_t, size_t, mbstate_t*);
+
+#include <locale>
+
+int main(int, char**) {
+    const wchar_t* w_string = L"Hello, World!";
+    mbstate_t state;
+    char mb_string[20];
+    size_t w_chars = wcslen(w_string);
+    size_t mb_chars = 0;
+
+    // Convert the wide-character string to a multibyte string
+    mb_chars = std::__locale::__ibm::wcsnrtombs(mb_string, &w_string, w_chars, 13, &state);
+
+    return mb_chars == 13;
+}
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/ctype.c.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/ctype.c.pass.cpp
new file mode 100644
index 0000000000000..cc7c9298f12ce
--- /dev/null
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/ctype.c.pass.cpp
@@ -0,0 +1,66 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <ctype.h>
+#include <wctype.h>
+#include <assert.h>
+#include <locale>
+
+// This test makes sure that various macro defined on z/OS in
+// <ctype.h> and <cwtype.h> which previously were in conflict
+// with functions defined in libc++ <locale_base_api.h> still
+// work even though they are undefined in <__undef_macros>
+// to remove name collisions.
+
+int main(int, char**) {
+    setlocale(LC_ALL, "C");
+    {
+        char upper = 'A';
+        char lower = 'a';
+        assert(islower(lower));
+        assert(tolower(upper) == lower);
+        assert(isupper(upper));
+        assert(toupper(lower) == upper);
+        assert(isdigit('1'));
+        assert(isxdigit('b'));
+    }
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+    {
+        wchar_t wspace = L' ';
+        wchar_t wprint = L'^';
+        wchar_t wcntrl = L'';
+        wchar_t wupper = L'A';
+        wchar_t wlower = L'a';
+        wchar_t walpha = L'z';
+        wchar_t wblank = L' ';
+        wchar_t wdigit = L'1';
+        wchar_t wpunct = L',';
+        wchar_t wxdigit = L'B';
+
+        assert(iswctype(L'A', wctype("alpha")));
+        assert(iswctype(L'1', wctype("digit")));
+        assert(iswspace(wspace));
+        assert(iswprint(wprint));
+        assert(iswcntrl(wcntrl));
+        assert(iswupper(wupper));
+        assert(iswlower(wlower));
+        assert(iswalpha(walpha));
+        assert(iswblank(wblank));
+        assert(iswdigit(wdigit));
+        assert(iswpunct(wpunct));
+        assert(iswxdigit(wxdigit));
+
+        assert(static_cast<wint_t>(wlower) == towlower(wupper));
+        assert(static_cast<wint_t>(wupper) == towupper(wlower));
+    }
+#endif
+
+ return 0;
+}
+
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/ctype.cxx.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/ctype.cxx.pass.cpp
new file mode 100644
index 0000000000000..fed9c683d1fbc
--- /dev/null
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/ctype.cxx.pass.cpp
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <locale>
+#include <cctype>
+#include <cwctype>
+#include <cassert>
+
+// This test makes sure that various macro defined on z/OS in
+// <ctype.h> and <cwtype.h> which previously were in conflict
+// with functions defined in libc++ <locale_base_api.h> still
+// work even though they are undefined in <__undef_macros>
+// to remove name collisions.
+
+int main(int, char**) {
+    std::locale loc("C");
+    {
+        char upper = 'A';
+        char lower = 'a';
+        char digit = '1';
+        char xdigit = 'b';
+        auto& CF = std::use_facet<std::ctype_byname<char>>(loc);
+        assert(CF.is(std::ctype_base::lower, lower));
+        assert(CF.is(std::ctype_base::upper, upper));
+        assert(CF.is(std::ctype_base::digit, digit));
+        assert(CF.is(std::ctype_base::xdigit, xdigit));
+        assert(lower == CF.tolower(upper));
+        assert(upper == CF.toupper(lower));
+    }
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+    {
+        wchar_t wctype = L'A';
+        wchar_t wspace = L' ';
+        wchar_t wprint = L'^';
+        wchar_t wcntrl = L'';
+        wchar_t wupper = L'A';
+        wchar_t wlower = L'a';
+        wchar_t walpha = L'z';
+        wchar_t wblank = L' ';
+        wchar_t wdigit = L'1';
+        wchar_t wpunct = L',';
+        wchar_t wxdigit = L'B';
+
+        assert(std::iswctype(wctype, std::wctype("alpha")));
+        assert(std::iswctype(wdigit, std::wctype("digit")));
+        assert(std::iswspace(wspace));
+        assert(std::iswprint(wprint));
+        assert(std::iswcntrl(wcntrl));
+        assert(std::iswupper(wupper));
+        assert(std::iswlower(wlower));
+        assert(std::iswalpha(walpha));
+        assert(std::iswblank(wblank));
+        assert(std::iswdigit(wdigit));
+        assert(std::iswpunct(wpunct));
+        assert(std::iswxdigit(wxdigit));
+
+        auto& WF = std::use_facet<std::ctype_byname<wchar_t>>(loc);
+        assert(wlower == WF.tolower(wupper));
+        assert(wupper == WF.toupper(wlower));
+    }
+#endif
+
+ return 0;
+}
+

>From c88a34907f54fc12e30efc95d11e2c3866d289e8 Mon Sep 17 00:00:00 2001
From: Zibi Sarbinowski <zibi at ca.ibm.com>
Date: Tue, 28 Oct 2025 17:22:38 +0000
Subject: [PATCH 2/3] fixed formatting

---
 libcxx/include/__locale_dir/locale_base_api.h |   2 +-
 libcxx/src/support/ibm/localeconv.cpp         |  13 +-
 libcxx/src/support/ibm/mbsnrtowcs.cpp         |  15 +-
 libcxx/src/support/ibm/wcsnrtombs.cpp         |  15 +-
 libcxx/src/support/ibm/xlocale_zos.cpp        | 129 +++++++++---------
 ...internal.zos.locale.funcs.compile.pass.cpp |   2 +-
 .../internal.zos.mbsnrtowcs.compile.pass.cpp  |  16 +--
 .../internal.zos.nanosleep.compile.pass.cpp   |   2 +-
 .../internal.zos.wcsnrtombs.compile.pass.cpp  |  16 +--
 .../locale.ctype.byname/ctype.c.pass.cpp      |  77 ++++++-----
 .../locale.ctype.byname/ctype.cxx.pass.cpp    |  87 ++++++------
 11 files changed, 181 insertions(+), 193 deletions(-)

diff --git a/libcxx/include/__locale_dir/locale_base_api.h b/libcxx/include/__locale_dir/locale_base_api.h
index 6f99e40a9dee2..b88a065aeffe4 100644
--- a/libcxx/include/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__locale_dir/locale_base_api.h
@@ -132,7 +132,7 @@ _LIBCPP_PUSH_MACROS
 //       (by providing global non-reserved names) and the new API. As we move individual platforms
 //       towards the new way of defining the locale base API, this should disappear since each platform
 //       will define those directly.
-#    if defined(_AIX) 
+#    if defined(_AIX)
 #      include <__locale_dir/locale_base_api/ibm.h>
 #    elif defined(__OpenBSD__)
 #      include <__locale_dir/locale_base_api/openbsd.h>
diff --git a/libcxx/src/support/ibm/localeconv.cpp b/libcxx/src/support/ibm/localeconv.cpp
index 4ab6b7a75f618..85d0b2c1e3236 100644
--- a/libcxx/src/support/ibm/localeconv.cpp
+++ b/libcxx/src/support/ibm/localeconv.cpp
@@ -13,18 +13,17 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-lconv *__libcpp_localeconv_l(locale_t& __l)
-{
+lconv* __libcpp_localeconv_l(locale_t& __l) {
   __locale::__locale_guard __current(__l);
 
-  lconv * lc = localeconv();
+  lconv* lc = localeconv();
   static lconv newlc;
   newlc = *lc;
 
-  enum {max_char_num = 20};
-#define DeepCopy(mbr) \
-  static char buf_##mbr[max_char_num]; \
-  strncpy(buf_##mbr, lc->mbr, max_char_num); \
+  enum { max_char_num = 20 };
+#define DeepCopy(mbr)                                                                                                  \
+  static char buf_##mbr[max_char_num];                                                                                 \
+  strncpy(buf_##mbr, lc->mbr, max_char_num);                                                                           \
   newlc.mbr = buf_##mbr;
 
   DeepCopy(decimal_point);
diff --git a/libcxx/src/support/ibm/mbsnrtowcs.cpp b/libcxx/src/support/ibm/mbsnrtowcs.cpp
index 590ac0617a67d..6e870cff793a2 100644
--- a/libcxx/src/support/ibm/mbsnrtowcs.cpp
+++ b/libcxx/src/support/ibm/mbsnrtowcs.cpp
@@ -23,12 +23,11 @@ namespace __ibm {
 // Returns (size_t) -1 when an invalid sequence is encountered.
 // Leaves *`src` pointing to the next character to convert or NULL
 // if a null character was converted from *`src`.
-size_t mbsnrtowcs(
-    wchar_t* __restrict dst,
-    const char** __restrict src,
-    size_t src_size_bytes,
-    size_t max_dest_chars,
-    mbstate_t* __restrict ps) {
+size_t mbsnrtowcs(wchar_t* __restrict dst,
+                  const char** __restrict src,
+                  size_t src_size_bytes,
+                  size_t max_dest_chars,
+                  mbstate_t* __restrict ps) {
   const size_t terminated_sequence = static_cast<size_t>(0);
   const size_t invalid_sequence    = static_cast<size_t>(-1);
   const size_t incomplete_sequence = static_cast<size_t>(-2);
@@ -101,9 +100,7 @@ size_t mbsnrtowcs(
   return dest_converted;
 }
 
-size_t __libcpp_mbsnrtowcs_l(wchar_t * dest, const char **src, size_t nms,
-                          size_t len, mbstate_t *ps, locale_t l)
-{
+size_t __libcpp_mbsnrtowcs_l(wchar_t* dest, const char** src, size_t nms, size_t len, mbstate_t* ps, locale_t l) {
   __locale::__locale_guard current(l);
   return mbsnrtowcs(dest, src, nms, len, ps);
 }
diff --git a/libcxx/src/support/ibm/wcsnrtombs.cpp b/libcxx/src/support/ibm/wcsnrtombs.cpp
index 437ea52ecf1ac..c9d6744d568f3 100644
--- a/libcxx/src/support/ibm/wcsnrtombs.cpp
+++ b/libcxx/src/support/ibm/wcsnrtombs.cpp
@@ -22,12 +22,11 @@ namespace __ibm {
 // converted from *src, excluding the null terminator.
 // Returns (size_t) -1 if an error occurs and sets errno.
 // If `dst` is NULL, `dst_size_bytes` is ignored and no bytes are copied to `dst`.
-size_t wcsnrtombs(
-    char* __restrict dst,
-    const wchar_t** __restrict src,
-    size_t max_source_chars,
-    size_t dst_size_bytes,
-    mbstate_t* __restrict ps) {
+size_t wcsnrtombs(char* __restrict dst,
+                  const wchar_t** __restrict src,
+                  size_t max_source_chars,
+                  size_t dst_size_bytes,
+                  mbstate_t* __restrict ps) {
   const size_t invalid_wchar = static_cast<size_t>(-1);
 
   size_t source_converted;
@@ -98,9 +97,7 @@ size_t wcsnrtombs(
   return dest_converted;
 }
 
-size_t __libcpp_wcsnrtombs_l(char *dest, const wchar_t **src, size_t nwc,
-                             size_t len, mbstate_t *ps, locale_t l)
-{
+size_t __libcpp_wcsnrtombs_l(char* dest, const wchar_t** src, size_t nwc, size_t len, mbstate_t* ps, locale_t l) {
   __locale::__locale_guard __current(l);
   return wcsnrtombs(dest, src, nwc, len, ps);
 }
diff --git a/libcxx/src/support/ibm/xlocale_zos.cpp b/libcxx/src/support/ibm/xlocale_zos.cpp
index 22bc0b0f72feb..2e4cca41fe2bb 100644
--- a/libcxx/src/support/ibm/xlocale_zos.cpp
+++ b/libcxx/src/support/ibm/xlocale_zos.cpp
@@ -14,61 +14,58 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#define CategoryList(pair, sep) \
-  pair(LC_COLLATE, lc_collate) sep \
-  pair(LC_CTYPE, lc_ctype) sep \
-  pair(LC_MONETARY, lc_monetary) sep \
-  pair(LC_NUMERIC, lc_numeric) sep \
-  pair(LC_TIME, lc_time) sep \
-  pair(LC_MESSAGES, lc_messages)
+#define CategoryList(pair, sep)                                                                                        \
+  pair(LC_COLLATE, lc_collate) sep pair(LC_CTYPE, lc_ctype)                                                            \
+  sep pair(LC_MONETARY, lc_monetary)                                                                                   \
+  sep pair(LC_NUMERIC, lc_numeric)                                                                                     \
+  sep pair(LC_TIME, lc_time)                                                                                           \
+  sep pair(LC_MESSAGES, lc_messages)
 
 // check ids and masks agree
-#define check_ids_and_masks_agree(id, _) \
-  static_assert((1 << id) == id##_MASK, "id and mask do not agree for " #id); \
+#define check_ids_and_masks_agree(id, _)                                                                               \
+  static_assert((1 << id) == id##_MASK, "id and mask do not agree for " #id);                                          \
   static_assert((1 << id) == _CATMASK(id), "mask does not have expected value for " #id);
-CategoryList(check_ids_and_masks_agree,)
+CategoryList(check_ids_and_masks_agree, )
 #undef check_ids_and_masks_agree
 
 // check that LC_ALL_MASK is defined as expected
 #define get_mask(id, _) id##_MASK
-static_assert(LC_ALL_MASK == (CategoryList(get_mask, |)), "LC_ALL_MASK does not have the expected value.  Check that its definition includes all supported categories");
+    static_assert(
+        LC_ALL_MASK == (CategoryList(get_mask, |)),
+        "LC_ALL_MASK does not have the expected value.  Check that its definition includes all supported categories");
 #undef get_mask
 
-
 // initialize c_locale
 #define init_clocale(id, locale_member) "C",
-static locale_struct c_locale = { LC_ALL_MASK, CategoryList(init_clocale, ) };
+static locale_struct c_locale = {LC_ALL_MASK, CategoryList(init_clocale, )};
 #undef init_clocale
 
 static locale_t current_locale = _LIBCPP_LC_GLOBAL_LOCALE;
 
-
-locale_t __c_locale() {
-  return &c_locale;
-}
+locale_t __c_locale() { return &c_locale; }
 
 // locale
 locale_t newlocale(int category_mask, const char* locale, locale_t base) {
   // start with some basic checks
-  if (! locale) {
+  if (!locale) {
     errno = EINVAL;
-    return (locale_t) 0;
+    return (locale_t)0;
   }
   if (category_mask & ~LC_ALL_MASK) {
     // then there are bits in category_mask that does not correspond
     // to a valid category
     errno = EINVAL;
-    return (locale_t) 0;
+    return (locale_t)0;
   }
 
-  locale_t  new_loc = new locale_struct;
+  locale_t new_loc          = new locale_struct;
   int num_locales_not_found = 0;
 
   if (base && base != _LIBCPP_LC_GLOBAL_LOCALE)
     *new_loc = *base;
 
-  auto set_for_category = [&](int id, int mask, std::string &setting) {
-    const char *setting_to_apply = nullptr;
+  auto set_for_category = [&](int id, int mask, std::string& setting) {
+    const char* setting_to_apply = nullptr;
 
     if (category_mask & mask)
       setting_to_apply = locale;
@@ -77,7 +74,7 @@ locale_t newlocale(int category_mask, const char* locale, locale_t base) {
 
     if (setting_to_apply) {
       // setlocale takes the id, not the mask
-      const char *saved_setting = setlocale(id, nullptr);
+      const char* saved_setting = setlocale(id, nullptr);
       if (setlocale(id, setting_to_apply)) {
         // then setting_to_apply is valid for this category
         // restore the saved setting
@@ -99,8 +96,8 @@ locale_t newlocale(int category_mask, const char* locale, locale_t base) {
 
   if (num_locales_not_found != 0) {
     delete new_loc;
-    errno = ENOENT;
-    new_loc = (locale_t) 0;
+    errno   = ENOENT;
+    new_loc = (locale_t)0;
   }
 
   return new_loc;
@@ -117,12 +114,12 @@ locale_t uselocale(locale_t new_loc) {
   if (new_loc == _LIBCPP_LC_GLOBAL_LOCALE) {
     current_locale = _LIBCPP_LC_GLOBAL_LOCALE;
   } else if (new_loc != nullptr) {
-    locale_struct  saved_locale;
+    locale_struct saved_locale;
     saved_locale.category_mask = 0;
 
-    auto apply_category = [&](int id, int mask, std::string &setting, std::string &save_setting)-> bool {
+    auto apply_category = [&](int id, int mask, std::string& setting, std::string& save_setting) -> bool {
       if (new_loc->category_mask & mask) {
-        const char *old_setting = setlocale(id, setting.c_str());
+        const char* old_setting = setlocale(id, setting.c_str());
         if (old_setting) {
           // we were able to set for this category.  Save the old setting
           // in case a subsequent category fails, and we need to restore
@@ -136,17 +133,17 @@ locale_t uselocale(locale_t new_loc) {
       return true;
     };
 
-#define Apply(id, locale_member) apply_category(id, id##_MASK, new_loc->locale_member, saved_locale. locale_member)
+#define Apply(id, locale_member) apply_category(id, id##_MASK, new_loc->locale_member, saved_locale.locale_member)
     bool is_ok = CategoryList(Apply, &&);
 #undef Apply
 
     if (!is_ok) {
-      auto restore = [&](int id, int mask, std::string &setting) {
+      auto restore = [&](int id, int mask, std::string& setting) {
         if (saved_locale.category_mask & mask)
           setlocale(id, setting.c_str());
       };
-#define Restore(id, locale_member) restore(id, id##_MASK, saved_locale. locale_member);
-      CategoryList(Restore,);
+#define Restore(id, locale_member) restore(id, id##_MASK, saved_locale.locale_member);
+      CategoryList(Restore, );
 #undef Restore
       errno = EINVAL;
       return nullptr;
@@ -157,154 +154,154 @@ locale_t uselocale(locale_t new_loc) {
   return prev_loc;
 }
 
-int isdigit_l(int __c, locale_t  __l) {
+int isdigit_l(int __c, locale_t __l) {
   __locale::__locale_guard __newloc(__l);
   return ::isdigit(__c);
 }
 
-int isxdigit_l(int __c, locale_t  __l) {
+int isxdigit_l(int __c, locale_t __l) {
   __locale::__locale_guard __newloc(__l);
   return ::isxdigit(__c);
 }
 
 namespace __locale {
 namespace __ibm {
-int isalnum_l(int __c, locale_t  __l) {
+int isalnum_l(int __c, locale_t __l) {
   __locale::__locale_guard __newloc(__l);
   return ::isalnum(__c);
 }
 
-int isalpha_l(int __c, locale_t  __l) {
+int isalpha_l(int __c, locale_t __l) {
   __locale::__locale_guard __newloc(__l);
   return ::isalpha(__c);
 }
 
-int isblank_l(int __c, locale_t  __l) {
+int isblank_l(int __c, locale_t __l) {
   __locale::__locale_guard __newloc(__l);
   return ::isblank(__c);
 }
 
-int iscntrl_l(int __c, locale_t  __l) {
+int iscntrl_l(int __c, locale_t __l) {
   __locale::__locale_guard __newloc(__l);
   return ::iscntrl(__c);
 }
 
-int isgraph_l(int __c, locale_t  __l) {
+int isgraph_l(int __c, locale_t __l) {
   __locale::__locale_guard __newloc(__l);
   return ::isgraph(__c);
 }
 
-int islower_l(int __c, locale_t  __l) {
+int islower_l(int __c, locale_t __l) {
   __locale::__locale_guard __newloc(__l);
   return ::islower(__c);
 }
 
-int isprint_l(int __c, locale_t  __l) {
+int isprint_l(int __c, locale_t __l) {
   __locale::__locale_guard __newloc(__l);
   return ::isprint(__c);
 }
 
-int ispunct_l(int __c, locale_t  __l) {
+int ispunct_l(int __c, locale_t __l) {
   __locale::__locale_guard __newloc(__l);
   return ::ispunct(__c);
 }
 
-int isspace_l(int __c, locale_t  __l) {
+int isspace_l(int __c, locale_t __l) {
   __locale::__locale_guard __newloc(__l);
   return ::isspace(__c);
 }
 
-int isupper_l(int __c, locale_t  __l) {
+int isupper_l(int __c, locale_t __l) {
   __locale::__locale_guard __newloc(__l);
   return ::isupper(__c);
 }
 
-int iswalnum_l(wint_t __c, locale_t  __l) {
+int iswalnum_l(wint_t __c, locale_t __l) {
   __locale::__locale_guard __newloc(__l);
   return ::iswalnum(__c);
 }
 
-int iswalpha_l(wint_t __c, locale_t  __l) {
+int iswalpha_l(wint_t __c, locale_t __l) {
   __locale::__locale_guard __newloc(__l);
   return ::iswalpha(__c);
 }
 
-int iswblank_l(wint_t __c, locale_t  __l) {
+int iswblank_l(wint_t __c, locale_t __l) {
   __locale::__locale_guard __newloc(__l);
   return ::iswblank(__c);
 }
 
-int iswcntrl_l(wint_t __c, locale_t  __l) {
+int iswcntrl_l(wint_t __c, locale_t __l) {
   __locale::__locale_guard __newloc(__l);
   return ::iswcntrl(__c);
 }
 
-int iswdigit_l(wint_t __c, locale_t  __l) {
+int iswdigit_l(wint_t __c, locale_t __l) {
   __locale::__locale_guard __newloc(__l);
   return ::iswdigit(__c);
 }
 
-int iswgraph_l(wint_t __c, locale_t  __l) {
+int iswgraph_l(wint_t __c, locale_t __l) {
   __locale::__locale_guard __newloc(__l);
   return ::iswgraph(__c);
 }
 
-int iswlower_l(wint_t __c, locale_t  __l) {
+int iswlower_l(wint_t __c, locale_t __l) {
   __locale::__locale_guard __newloc(__l);
   return ::iswlower(__c);
 }
 
-int iswprint_l(wint_t __c, locale_t  __l) {
+int iswprint_l(wint_t __c, locale_t __l) {
   __locale::__locale_guard __newloc(__l);
   return ::iswprint(__c);
 }
 
-int iswpunct_l(wint_t __c, locale_t  __l) {
+int iswpunct_l(wint_t __c, locale_t __l) {
   __locale::__locale_guard __newloc(__l);
   return ::iswpunct(__c);
 }
 
-int iswspace_l(wint_t __c, locale_t  __l) {
+int iswspace_l(wint_t __c, locale_t __l) {
   __locale::__locale_guard __newloc(__l);
   return ::iswspace(__c);
 }
 
-int iswupper_l(wint_t __c, locale_t  __l) {
+int iswupper_l(wint_t __c, locale_t __l) {
   __locale::__locale_guard __newloc(__l);
   return ::iswupper(__c);
 }
 
-int iswxdigit_l(wint_t __c, locale_t  __l) {
+int iswxdigit_l(wint_t __c, locale_t __l) {
   __locale::__locale_guard __newloc(__l);
   return ::iswxdigit(__c);
 }
 
-int toupper_l(int __c, locale_t  __l) {
+int toupper_l(int __c, locale_t __l) {
   __locale::__locale_guard __newloc(__l);
   return ::toupper(__c);
 }
 
-int tolower_l(int __c, locale_t  __l) {
+int tolower_l(int __c, locale_t __l) {
   __locale::__locale_guard __newloc(__l);
   return ::tolower(__c);
 }
 
-wint_t towupper_l(wint_t __c, locale_t  __l) {
+wint_t towupper_l(wint_t __c, locale_t __l) {
   __locale::__locale_guard __newloc(__l);
   return ::towupper(__c);
 }
 
-wint_t towlower_l(wint_t __c, locale_t  __l) {
+wint_t towlower_l(wint_t __c, locale_t __l) {
   __locale::__locale_guard __newloc(__l);
   return ::towlower(__c);
 }
 
-int strcoll_l(const char *__s1, const char *__s2, locale_t  __l) {
+int strcoll_l(const char* __s1, const char* __s2, locale_t __l) {
   __locale::__locale_guard __newloc(__l);
   return ::strcoll(__s1, __s2);
 }
 
-size_t strxfrm_l(char *__dest, const char *__src, size_t __n, locale_t  __l) {
+size_t strxfrm_l(char* __dest, const char* __src, size_t __n, locale_t __l) {
   __locale::__locale_guard __newloc(__l);
   return ::strxfrm(__dest, __src, __n);
 }
@@ -314,17 +311,17 @@ size_t strftime_l(char* __s, size_t __max, const char* __format, const struct tm
   return ::strftime(__s, __max, __format, __tm);
 }
 
-int wcscoll_l(const wchar_t *__ws1, const wchar_t *__ws2, locale_t  __l) {
+int wcscoll_l(const wchar_t* __ws1, const wchar_t* __ws2, locale_t __l) {
   __locale::__locale_guard __newloc(__l);
   return ::wcscoll(__ws1, __ws2);
 }
 
-size_t wcsxfrm_l(wchar_t *__dest, const wchar_t *__src, size_t __n, locale_t  __l) {
+size_t wcsxfrm_l(wchar_t* __dest, const wchar_t* __src, size_t __n, locale_t __l) {
   __locale::__locale_guard __newloc(__l);
   return ::wcsxfrm(__dest, __src, __n);
 }
 
-int iswctype_l(wint_t __c, wctype_t __type, locale_t  __l) {
+int iswctype_l(wint_t __c, wctype_t __type, locale_t __l) {
   __locale::__locale_guard __newloc(__l);
   return ::iswctype(__c, __type);
 }
diff --git a/libcxx/test/libcxx/internal.zos.locale.funcs.compile.pass.cpp b/libcxx/test/libcxx/internal.zos.locale.funcs.compile.pass.cpp
index 846cdf6862c0c..9ca3b157253cb 100644
--- a/libcxx/test/libcxx/internal.zos.locale.funcs.compile.pass.cpp
+++ b/libcxx/test/libcxx/internal.zos.locale.funcs.compile.pass.cpp
@@ -9,7 +9,7 @@
 // Test that version of the POSIX functions provided outside of libc++ don't
 // cause compilation errors.
 
-struct locale_t { };
+struct locale_t {};
 locale_t newlocale(int category_mask, const char* locale, locale_t base);
 void freelocale(locale_t locobj);
 locale_t uselocale(locale_t newloc);
diff --git a/libcxx/test/libcxx/internal.zos.mbsnrtowcs.compile.pass.cpp b/libcxx/test/libcxx/internal.zos.mbsnrtowcs.compile.pass.cpp
index fc5e5d8be790f..59ff3a1fc8194 100644
--- a/libcxx/test/libcxx/internal.zos.mbsnrtowcs.compile.pass.cpp
+++ b/libcxx/test/libcxx/internal.zos.mbsnrtowcs.compile.pass.cpp
@@ -12,14 +12,14 @@
 #include <locale>
 
 int main(int, char**) {
-    const char* mb_string = "Hello, World!";
-    wchar_t w_string[20];
-    mbstate_t state;
-    size_t mb_chars = strlen(mb_string);
-    size_t w_chars = 0;
+  const char* mb_string = "Hello, World!";
+  wchar_t w_string[20];
+  mbstate_t state;
+  size_t mb_chars = strlen(mb_string);
+  size_t w_chars  = 0;
 
-    // Convert the multibyte string to a wide-character string
-    w_chars = std::__locale::__ibm::mbsnrtowcs(w_string, &mb_string, mb_chars, 13, &state);
+  // Convert the multibyte string to a wide-character string
+  w_chars = std::__locale::__ibm::mbsnrtowcs(w_string, &mb_string, mb_chars, 13, &state);
 
-    return w_chars == 13;
+  return w_chars == 13;
 }
diff --git a/libcxx/test/libcxx/internal.zos.nanosleep.compile.pass.cpp b/libcxx/test/libcxx/internal.zos.nanosleep.compile.pass.cpp
index b7a6385b496f8..1f3e94a1074a4 100644
--- a/libcxx/test/libcxx/internal.zos.nanosleep.compile.pass.cpp
+++ b/libcxx/test/libcxx/internal.zos.nanosleep.compile.pass.cpp
@@ -11,5 +11,5 @@
 
 #include <time.h> // timespec
 
-int nanosleep(const struct timespec* , struct timespec* );
+int nanosleep(const struct timespec*, struct timespec*);
 #include <mutex>
diff --git a/libcxx/test/libcxx/internal.zos.wcsnrtombs.compile.pass.cpp b/libcxx/test/libcxx/internal.zos.wcsnrtombs.compile.pass.cpp
index 1fabc7a433c86..efbc1e2e1cf6d 100644
--- a/libcxx/test/libcxx/internal.zos.wcsnrtombs.compile.pass.cpp
+++ b/libcxx/test/libcxx/internal.zos.wcsnrtombs.compile.pass.cpp
@@ -12,14 +12,14 @@
 #include <locale>
 
 int main(int, char**) {
-    const wchar_t* w_string = L"Hello, World!";
-    mbstate_t state;
-    char mb_string[20];
-    size_t w_chars = wcslen(w_string);
-    size_t mb_chars = 0;
+  const wchar_t* w_string = L"Hello, World!";
+  mbstate_t state;
+  char mb_string[20];
+  size_t w_chars  = wcslen(w_string);
+  size_t mb_chars = 0;
 
-    // Convert the wide-character string to a multibyte string
-    mb_chars = std::__locale::__ibm::wcsnrtombs(mb_string, &w_string, w_chars, 13, &state);
+  // Convert the wide-character string to a multibyte string
+  mb_chars = std::__locale::__ibm::wcsnrtombs(mb_string, &w_string, w_chars, 13, &state);
 
-    return mb_chars == 13;
+  return mb_chars == 13;
 }
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/ctype.c.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/ctype.c.pass.cpp
index cc7c9298f12ce..dc2132de8ce1c 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/ctype.c.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/ctype.c.pass.cpp
@@ -18,49 +18,48 @@
 // to remove name collisions.
 
 int main(int, char**) {
-    setlocale(LC_ALL, "C");
-    {
-        char upper = 'A';
-        char lower = 'a';
-        assert(islower(lower));
-        assert(tolower(upper) == lower);
-        assert(isupper(upper));
-        assert(toupper(lower) == upper);
-        assert(isdigit('1'));
-        assert(isxdigit('b'));
-    }
+  setlocale(LC_ALL, "C");
+  {
+    char upper = 'A';
+    char lower = 'a';
+    assert(islower(lower));
+    assert(tolower(upper) == lower);
+    assert(isupper(upper));
+    assert(toupper(lower) == upper);
+    assert(isdigit('1'));
+    assert(isxdigit('b'));
+  }
 
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
-    {
-        wchar_t wspace = L' ';
-        wchar_t wprint = L'^';
-        wchar_t wcntrl = L'';
-        wchar_t wupper = L'A';
-        wchar_t wlower = L'a';
-        wchar_t walpha = L'z';
-        wchar_t wblank = L' ';
-        wchar_t wdigit = L'1';
-        wchar_t wpunct = L',';
-        wchar_t wxdigit = L'B';
+  {
+    wchar_t wspace  = L' ';
+    wchar_t wprint  = L'^';
+    wchar_t wcntrl  = L'';
+    wchar_t wupper  = L'A';
+    wchar_t wlower  = L'a';
+    wchar_t walpha  = L'z';
+    wchar_t wblank  = L' ';
+    wchar_t wdigit  = L'1';
+    wchar_t wpunct  = L',';
+    wchar_t wxdigit = L'B';
 
-        assert(iswctype(L'A', wctype("alpha")));
-        assert(iswctype(L'1', wctype("digit")));
-        assert(iswspace(wspace));
-        assert(iswprint(wprint));
-        assert(iswcntrl(wcntrl));
-        assert(iswupper(wupper));
-        assert(iswlower(wlower));
-        assert(iswalpha(walpha));
-        assert(iswblank(wblank));
-        assert(iswdigit(wdigit));
-        assert(iswpunct(wpunct));
-        assert(iswxdigit(wxdigit));
+    assert(iswctype(L'A', wctype("alpha")));
+    assert(iswctype(L'1', wctype("digit")));
+    assert(iswspace(wspace));
+    assert(iswprint(wprint));
+    assert(iswcntrl(wcntrl));
+    assert(iswupper(wupper));
+    assert(iswlower(wlower));
+    assert(iswalpha(walpha));
+    assert(iswblank(wblank));
+    assert(iswdigit(wdigit));
+    assert(iswpunct(wpunct));
+    assert(iswxdigit(wxdigit));
 
-        assert(static_cast<wint_t>(wlower) == towlower(wupper));
-        assert(static_cast<wint_t>(wupper) == towupper(wlower));
-    }
+    assert(static_cast<wint_t>(wlower) == towlower(wupper));
+    assert(static_cast<wint_t>(wupper) == towupper(wlower));
+  }
 #endif
 
- return 0;
+  return 0;
 }
-
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/ctype.cxx.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/ctype.cxx.pass.cpp
index fed9c683d1fbc..07f5cbc9f4dab 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/ctype.cxx.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/ctype.cxx.pass.cpp
@@ -18,54 +18,53 @@
 // to remove name collisions.
 
 int main(int, char**) {
-    std::locale loc("C");
-    {
-        char upper = 'A';
-        char lower = 'a';
-        char digit = '1';
-        char xdigit = 'b';
-        auto& CF = std::use_facet<std::ctype_byname<char>>(loc);
-        assert(CF.is(std::ctype_base::lower, lower));
-        assert(CF.is(std::ctype_base::upper, upper));
-        assert(CF.is(std::ctype_base::digit, digit));
-        assert(CF.is(std::ctype_base::xdigit, xdigit));
-        assert(lower == CF.tolower(upper));
-        assert(upper == CF.toupper(lower));
-    }
+  std::locale loc("C");
+  {
+    char upper  = 'A';
+    char lower  = 'a';
+    char digit  = '1';
+    char xdigit = 'b';
+    auto& CF    = std::use_facet<std::ctype_byname<char>>(loc);
+    assert(CF.is(std::ctype_base::lower, lower));
+    assert(CF.is(std::ctype_base::upper, upper));
+    assert(CF.is(std::ctype_base::digit, digit));
+    assert(CF.is(std::ctype_base::xdigit, xdigit));
+    assert(lower == CF.tolower(upper));
+    assert(upper == CF.toupper(lower));
+  }
 
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
-    {
-        wchar_t wctype = L'A';
-        wchar_t wspace = L' ';
-        wchar_t wprint = L'^';
-        wchar_t wcntrl = L'';
-        wchar_t wupper = L'A';
-        wchar_t wlower = L'a';
-        wchar_t walpha = L'z';
-        wchar_t wblank = L' ';
-        wchar_t wdigit = L'1';
-        wchar_t wpunct = L',';
-        wchar_t wxdigit = L'B';
+  {
+    wchar_t wctype  = L'A';
+    wchar_t wspace  = L' ';
+    wchar_t wprint  = L'^';
+    wchar_t wcntrl  = L'';
+    wchar_t wupper  = L'A';
+    wchar_t wlower  = L'a';
+    wchar_t walpha  = L'z';
+    wchar_t wblank  = L' ';
+    wchar_t wdigit  = L'1';
+    wchar_t wpunct  = L',';
+    wchar_t wxdigit = L'B';
 
-        assert(std::iswctype(wctype, std::wctype("alpha")));
-        assert(std::iswctype(wdigit, std::wctype("digit")));
-        assert(std::iswspace(wspace));
-        assert(std::iswprint(wprint));
-        assert(std::iswcntrl(wcntrl));
-        assert(std::iswupper(wupper));
-        assert(std::iswlower(wlower));
-        assert(std::iswalpha(walpha));
-        assert(std::iswblank(wblank));
-        assert(std::iswdigit(wdigit));
-        assert(std::iswpunct(wpunct));
-        assert(std::iswxdigit(wxdigit));
+    assert(std::iswctype(wctype, std::wctype("alpha")));
+    assert(std::iswctype(wdigit, std::wctype("digit")));
+    assert(std::iswspace(wspace));
+    assert(std::iswprint(wprint));
+    assert(std::iswcntrl(wcntrl));
+    assert(std::iswupper(wupper));
+    assert(std::iswlower(wlower));
+    assert(std::iswalpha(walpha));
+    assert(std::iswblank(wblank));
+    assert(std::iswdigit(wdigit));
+    assert(std::iswpunct(wpunct));
+    assert(std::iswxdigit(wxdigit));
 
-        auto& WF = std::use_facet<std::ctype_byname<wchar_t>>(loc);
-        assert(wlower == WF.tolower(wupper));
-        assert(wupper == WF.toupper(wlower));
-    }
+    auto& WF = std::use_facet<std::ctype_byname<wchar_t>>(loc);
+    assert(wlower == WF.tolower(wupper));
+    assert(wupper == WF.toupper(wlower));
+  }
 #endif
 
- return 0;
+  return 0;
 }
-

>From 26e4eaccc1923cd2919522cefea81018d1f718f0 Mon Sep 17 00:00:00 2001
From: Zibi Sarbinowski <zibi at ca.ibm.com>
Date: Tue, 28 Oct 2025 19:17:04 +0000
Subject: [PATCH 3/3] Exclude 2 tests with non-alphnumeric characters

---
 libcxx/utils/ci/run-buildbot | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 57ecf1e49dbf2..ad4a6700111ea 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -282,6 +282,8 @@ check-generated-output)
            --exclude 'ostream.pass.cpp' \
            --exclude 'transcoding.pass.cpp' \
            --exclude 'underflow.pass.cpp' \
+           --exclude 'ctype.cxx.pass.cpp' \
+           --exclude 'ctype.c.pass.cpp' \
            || false
 ;;
 #



More information about the libcxx-commits mailing list