[libc-commits] [libc] 856dadb - [libc] Add `ctype.h` locale variants (#102711)

Joseph Huber via libc-commits libc-commits at lists.llvm.org
Thu Aug 22 11:52:11 PDT 2024


Author: Joseph Huber
Date: 2024-08-22T13:51:54-05:00
New Revision: 856dadb33c38f4e3be592f11c3d67e7337e288c7

URL: https://github.com/llvm/llvm-project/commit/856dadb33c38f4e3be592f11c3d67e7337e288c7
DIFF: https://github.com/llvm/llvm-project/commit/856dadb33c38f4e3be592f11c3d67e7337e288c7.diff

LOG: [libc] Add `ctype.h` locale variants (#102711)

Summary:
This patch adds all the libc ctype variants. These ignore the locale
ingormation completely, so they're pretty much just stubs. Because these
use locale information, which is system scope, we do not enable building
them outisde of full build mode.

Added: 
    libc/src/ctype/isalnum_l.cpp
    libc/src/ctype/isalnum_l.h
    libc/src/ctype/isalpha_l.cpp
    libc/src/ctype/isalpha_l.h
    libc/src/ctype/isblank_l.cpp
    libc/src/ctype/isblank_l.h
    libc/src/ctype/iscntrl_l.cpp
    libc/src/ctype/iscntrl_l.h
    libc/src/ctype/isdigit_l.cpp
    libc/src/ctype/isdigit_l.h
    libc/src/ctype/isgraph_l.cpp
    libc/src/ctype/isgraph_l.h
    libc/src/ctype/islower_l.cpp
    libc/src/ctype/islower_l.h
    libc/src/ctype/isprint_l.cpp
    libc/src/ctype/isprint_l.h
    libc/src/ctype/ispunct_l.cpp
    libc/src/ctype/ispunct_l.h
    libc/src/ctype/isspace_l.cpp
    libc/src/ctype/isspace_l.h
    libc/src/ctype/isupper_l.cpp
    libc/src/ctype/isupper_l.h
    libc/src/ctype/isxdigit_l.cpp
    libc/src/ctype/isxdigit_l.h
    libc/src/ctype/tolower_l.cpp
    libc/src/ctype/tolower_l.h
    libc/src/ctype/toupper_l.cpp
    libc/src/ctype/toupper_l.h

Modified: 
    libc/config/gpu/entrypoints.txt
    libc/config/linux/x86_64/entrypoints.txt
    libc/include/CMakeLists.txt
    libc/newhdrgen/yaml/ctype.yaml
    libc/spec/stdc.td
    libc/src/ctype/CMakeLists.txt
    libc/src/ctype/isalnum.cpp
    libc/src/ctype/isalpha.cpp
    libc/src/ctype/isblank.cpp
    libc/src/ctype/iscntrl.cpp
    libc/src/ctype/isdigit.cpp
    libc/src/ctype/isgraph.cpp
    libc/src/ctype/islower.cpp
    libc/src/ctype/isprint.cpp
    libc/src/ctype/ispunct.cpp
    libc/src/ctype/isspace.cpp
    libc/src/ctype/isupper.cpp
    libc/src/ctype/isxdigit.cpp
    libc/src/ctype/tolower.cpp
    libc/src/ctype/toupper.cpp

Removed: 
    


################################################################################
diff  --git a/libc/config/gpu/entrypoints.txt b/libc/config/gpu/entrypoints.txt
index 0674f23687c0a5..7b869902074d8e 100644
--- a/libc/config/gpu/entrypoints.txt
+++ b/libc/config/gpu/entrypoints.txt
@@ -4,21 +4,35 @@ set(TARGET_LIBC_ENTRYPOINTS
 
     # ctype.h entrypoints
     libc.src.ctype.isalnum
+    libc.src.ctype.isalnum_l
     libc.src.ctype.isalpha
+    libc.src.ctype.isalpha_l
     libc.src.ctype.isascii
     libc.src.ctype.isblank
+    libc.src.ctype.isblank_l
     libc.src.ctype.iscntrl
+    libc.src.ctype.iscntrl_l
     libc.src.ctype.isdigit
+    libc.src.ctype.isdigit_l
     libc.src.ctype.isgraph
+    libc.src.ctype.isgraph_l
     libc.src.ctype.islower
+    libc.src.ctype.islower_l
     libc.src.ctype.isprint
+    libc.src.ctype.isprint_l
     libc.src.ctype.ispunct
+    libc.src.ctype.ispunct_l
     libc.src.ctype.isspace
+    libc.src.ctype.isspace_l
     libc.src.ctype.isupper
+    libc.src.ctype.isupper_l
     libc.src.ctype.isxdigit
+    libc.src.ctype.isxdigit_l
     libc.src.ctype.toascii
     libc.src.ctype.tolower
+    libc.src.ctype.tolower_l
     libc.src.ctype.toupper
+    libc.src.ctype.toupper_l
 
     # string.h entrypoints
     libc.src.string.bcmp

diff  --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index e7c3c7db64abe5..bac1e3cfa85da7 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -782,6 +782,22 @@ endif()
 
 if(LLVM_LIBC_FULL_BUILD)
   list(APPEND TARGET_LIBC_ENTRYPOINTS
+    # ctype.h entrypoints
+    libc.src.ctype.isalnum_l
+    libc.src.ctype.isalpha_l
+    libc.src.ctype.isblank_l
+    libc.src.ctype.iscntrl_l
+    libc.src.ctype.isdigit_l
+    libc.src.ctype.isgraph_l
+    libc.src.ctype.islower_l
+    libc.src.ctype.isprint_l
+    libc.src.ctype.ispunct_l
+    libc.src.ctype.isspace_l
+    libc.src.ctype.isupper_l
+    libc.src.ctype.isxdigit_l
+    libc.src.ctype.tolower_l
+    libc.src.ctype.toupper_l
+
     # assert.h entrypoints
     libc.src.assert.__assert_fail
 

diff  --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
index 8e00c9f1292e81..910f9eea015f27 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -45,6 +45,7 @@ add_header_macro(
   ctype.h
   DEPENDS
     .llvm_libc_common_h
+    .llvm-libc-types.locale_t
 )
 
 add_header_macro(

diff  --git a/libc/newhdrgen/yaml/ctype.yaml b/libc/newhdrgen/yaml/ctype.yaml
index f3108a34d43377..b4823c3e53234a 100644
--- a/libc/newhdrgen/yaml/ctype.yaml
+++ b/libc/newhdrgen/yaml/ctype.yaml
@@ -1,6 +1,7 @@
 header: ctype.h
 macros: []
-types: []
+types:
+  - type_name: locale_t
 enums: []
 objects: []
 functions:
@@ -100,4 +101,101 @@ functions:
     return_type: int
     arguments:
       - type: int
-    functions: null
+  - name: isalnum_l
+    standards:
+      - stdc
+    return_type: int
+    arguments:
+      - type: int
+      - type: locale_t
+  - name: isalpha_l
+    standards:
+      - stdc
+    return_type: int
+    arguments:
+      - type: int
+      - type: locale_t
+  - name: isblank_l
+    standards:
+      - stdc
+    return_type: int
+    arguments:
+      - type: int
+      - type: locale_t
+  - name: iscntrl_l
+    standards:
+      - stdc
+    return_type: int
+    arguments:
+      - type: int
+      - type: locale_t
+  - name: isdigit_l
+    standards:
+      - stdc
+    return_type: int
+    arguments:
+      - type: int
+      - type: locale_t
+  - name: isgraph_l
+    standards:
+      - stdc
+    return_type: int
+    arguments:
+      - type: int
+      - type: locale_t
+  - name: islower_l
+    standards:
+      - stdc
+    return_type: int
+    arguments:
+      - type: int
+      - type: locale_t
+  - name: isprint_l
+    standards:
+      - stdc
+    return_type: int
+    arguments:
+      - type: int
+      - type: locale_t
+  - name: ispunct_l
+    standards:
+      - stdc
+    return_type: int
+    arguments:
+      - type: int
+      - type: locale_t
+  - name: isspace_l
+    standards:
+      - stdc
+    return_type: int
+    arguments:
+      - type: int
+      - type: locale_t
+  - name: isupper_l
+    standards:
+      - stdc
+    return_type: int
+    arguments:
+      - type: int
+      - type: locale_t
+  - name: isxdigit_l
+    standards:
+      - stdc
+    return_type: int
+    arguments:
+      - type: int
+      - type: locale_t
+  - name: tolower_l
+    standards:
+      - stdc
+    return_type: int
+    arguments:
+      - type: int
+      - type: locale_t
+  - name: toupper_l
+    standards:
+      - stdc
+    return_type: int
+    arguments:
+      - type: int
+      - type: locale_t

diff  --git a/libc/spec/stdc.td b/libc/spec/stdc.td
index f9573997c65739..402d8c335470ad 100644
--- a/libc/spec/stdc.td
+++ b/libc/spec/stdc.td
@@ -35,7 +35,9 @@ def StdC : StandardSpec<"stdc"> {
   HeaderSpec CType = HeaderSpec<
       "ctype.h",
       [], // Macros
-      [], // Types
+      [
+          LocaleT
+      ], // Types
       [], // Enumerations
       [
           FunctionSpec<
@@ -108,6 +110,76 @@ def StdC : StandardSpec<"stdc"> {
               RetValSpec<IntType>,
               [ArgSpec<IntType>]
           >,
+          FunctionSpec<
+              "isalnum_l",
+              RetValSpec<IntType>,
+              [ArgSpec<IntType>, ArgSpec<LocaleT>]
+          >,
+          FunctionSpec<
+              "isalpha_l",
+              RetValSpec<IntType>,
+              [ArgSpec<IntType>, ArgSpec<LocaleT>]
+          >,
+          FunctionSpec<
+              "isblank_l",
+              RetValSpec<IntType>,
+              [ArgSpec<IntType>, ArgSpec<LocaleT>]
+          >,
+          FunctionSpec<
+              "iscntrl_l",
+              RetValSpec<IntType>,
+              [ArgSpec<IntType>, ArgSpec<LocaleT>]
+          >,
+          FunctionSpec<
+              "isdigit_l",
+              RetValSpec<IntType>,
+              [ArgSpec<IntType>, ArgSpec<LocaleT>]
+          >,
+          FunctionSpec<
+              "isgraph_l",
+              RetValSpec<IntType>,
+              [ArgSpec<IntType>, ArgSpec<LocaleT>]
+          >,
+          FunctionSpec<
+              "islower_l",
+              RetValSpec<IntType>,
+              [ArgSpec<IntType>, ArgSpec<LocaleT>]
+          >,
+          FunctionSpec<
+              "isprint_l",
+              RetValSpec<IntType>,
+              [ArgSpec<IntType>, ArgSpec<LocaleT>]
+          >,
+          FunctionSpec<
+              "ispunct_l",
+              RetValSpec<IntType>,
+              [ArgSpec<IntType>, ArgSpec<LocaleT>]
+          >,
+          FunctionSpec<
+              "isspace_l",
+              RetValSpec<IntType>,
+              [ArgSpec<IntType>, ArgSpec<LocaleT>]
+          >,
+          FunctionSpec<
+              "isupper_l",
+              RetValSpec<IntType>,
+              [ArgSpec<IntType>, ArgSpec<LocaleT>]
+          >,
+          FunctionSpec<
+              "isxdigit_l",
+              RetValSpec<IntType>,
+              [ArgSpec<IntType>, ArgSpec<LocaleT>]
+          >,
+          FunctionSpec<
+              "tolower_l",
+              RetValSpec<IntType>,
+              [ArgSpec<IntType>, ArgSpec<LocaleT>]
+          >,
+          FunctionSpec<
+              "toupper_l",
+              RetValSpec<IntType>,
+              [ArgSpec<IntType>, ArgSpec<LocaleT>]
+          >,
       ]
   >;
 

diff  --git a/libc/src/ctype/CMakeLists.txt b/libc/src/ctype/CMakeLists.txt
index ae4eec9615dc19..8830c1bccf9eaa 100644
--- a/libc/src/ctype/CMakeLists.txt
+++ b/libc/src/ctype/CMakeLists.txt
@@ -146,3 +146,159 @@ add_entrypoint_object(
   DEPENDS
     libc.src.__support.ctype_utils
 )
+
+# Do not build the locale versions in overlay mode.
+if(NOT LLVM_LIBC_FULL_BUILD)
+  return()
+endif()
+
+add_entrypoint_object(
+  isalnum_l
+  SRCS
+    isalnum_l.cpp
+  HDRS
+    isalnum_l.h
+  DEPENDS
+    libc.include.ctype
+    libc.src.__support.ctype_utils
+    libc.hdr.types.locale_t
+)
+
+add_entrypoint_object(
+  isalpha_l
+  SRCS
+    isalpha_l.cpp
+  HDRS
+    isalpha_l.h
+  DEPENDS
+    libc.src.__support.ctype_utils
+    libc.hdr.types.locale_t
+)
+
+add_entrypoint_object(
+  isblank_l
+  SRCS
+    isblank_l.cpp
+  HDRS
+    isblank_l.h
+  DEPENDS
+    libc.hdr.types.locale_t
+)
+
+add_entrypoint_object(
+  iscntrl_l
+  SRCS
+    iscntrl_l.cpp
+  HDRS
+    iscntrl_l.h
+  DEPENDS
+    libc.hdr.types.locale_t
+)
+
+add_entrypoint_object(
+  isdigit_l
+  SRCS
+    isdigit_l.cpp
+  HDRS
+    isdigit_l.h
+  DEPENDS
+    libc.src.__support.ctype_utils
+    libc.hdr.types.locale_t
+)
+
+add_entrypoint_object(
+  isgraph_l
+  SRCS
+    isgraph_l.cpp
+  HDRS
+    isgraph_l.h
+  DEPENDS
+    libc.src.__support.ctype_utils
+    libc.hdr.types.locale_t
+)
+
+add_entrypoint_object(
+  islower_l
+  SRCS
+    islower_l.cpp
+  HDRS
+    islower_l.h
+  DEPENDS
+    libc.src.__support.ctype_utils
+    libc.hdr.types.locale_t
+)
+
+add_entrypoint_object(
+  isprint_l
+  SRCS
+    isprint_l.cpp
+  HDRS
+    isprint_l.h
+  DEPENDS
+    libc.hdr.types.locale_t
+)
+
+add_entrypoint_object(
+  ispunct_l
+  SRCS
+    ispunct_l.cpp
+  HDRS
+    ispunct_l.h
+  DEPENDS
+    libc.src.__support.ctype_utils
+    libc.hdr.types.locale_t
+)
+
+add_entrypoint_object(
+  isspace_l
+  SRCS
+    isspace_l.cpp
+  HDRS
+    isspace_l.h
+  DEPENDS
+    libc.hdr.types.locale_t
+)
+
+add_entrypoint_object(
+  isupper_l
+  SRCS
+    isupper_l.cpp
+  HDRS
+    isupper_l.h
+  DEPENDS
+    libc.src.__support.ctype_utils
+    libc.hdr.types.locale_t
+)
+
+add_entrypoint_object(
+  isxdigit_l
+  SRCS
+    isxdigit_l.cpp
+  HDRS
+    isxdigit_l.h
+  DEPENDS
+    libc.src.__support.ctype_utils
+    libc.hdr.types.locale_t
+)
+
+add_entrypoint_object(
+  tolower_l
+  SRCS
+    tolower_l.cpp
+  HDRS
+    tolower_l.h
+  DEPENDS
+    libc.src.__support.ctype_utils
+    libc.hdr.types.locale_t
+)
+
+add_entrypoint_object(
+  toupper_l
+  SRCS
+    toupper_l.cpp
+  HDRS
+    toupper_l.h
+  DEPENDS
+    libc.src.__support.ctype_utils
+    libc.hdr.types.locale_t
+)

diff  --git a/libc/src/ctype/isalnum.cpp b/libc/src/ctype/isalnum.cpp
index 382553c23a6bfb..54a3e357488790 100644
--- a/libc/src/ctype/isalnum.cpp
+++ b/libc/src/ctype/isalnum.cpp
@@ -14,8 +14,6 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-// TODO: Currently restricted to default locale.
-// These should be extended using locale information.
 LLVM_LIBC_FUNCTION(int, isalnum, (int c)) {
   return static_cast<int>(internal::isalnum(static_cast<unsigned>(c)));
 }

diff  --git a/libc/src/ctype/isalnum_l.cpp b/libc/src/ctype/isalnum_l.cpp
new file mode 100644
index 00000000000000..671d9b75c4c33a
--- /dev/null
+++ b/libc/src/ctype/isalnum_l.cpp
@@ -0,0 +1,21 @@
+//===-- Implementation of isalnum -----------------------------------------===//
+//
+// 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 "src/ctype/isalnum_l.h"
+#include "src/__support/ctype_utils.h"
+
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, isalnum_l, (int c, locale_t)) {
+  return static_cast<int>(internal::isalnum(static_cast<unsigned>(c)));
+}
+
+} // namespace LIBC_NAMESPACE_DECL

diff  --git a/libc/src/ctype/isalnum_l.h b/libc/src/ctype/isalnum_l.h
new file mode 100644
index 00000000000000..5bc892e6c8747e
--- /dev/null
+++ b/libc/src/ctype/isalnum_l.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for isalnum_l -----------------------*-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 LLVM_LIBC_SRC_CTYPE_ISALNUM_H
+#define LLVM_LIBC_SRC_CTYPE_ISALNUM_H
+
+#include "hdr/types/locale_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int isalnum_l(int c, locale_t locale);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif //  LLVM_LIBC_SRC_CTYPE_ISALNUM_H

diff  --git a/libc/src/ctype/isalpha.cpp b/libc/src/ctype/isalpha.cpp
index 1a63406780b6e0..78b26f6a486eae 100644
--- a/libc/src/ctype/isalpha.cpp
+++ b/libc/src/ctype/isalpha.cpp
@@ -14,8 +14,6 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-// TODO: Currently restricted to default locale.
-// These should be extended using locale information.
 LLVM_LIBC_FUNCTION(int, isalpha, (int c)) {
   return static_cast<int>(internal::isalpha(static_cast<unsigned>(c)));
 }

diff  --git a/libc/src/ctype/isalpha_l.cpp b/libc/src/ctype/isalpha_l.cpp
new file mode 100644
index 00000000000000..0619d979bedf22
--- /dev/null
+++ b/libc/src/ctype/isalpha_l.cpp
@@ -0,0 +1,21 @@
+//===-- Implementation of isalpha -----------------------------------------===//
+//
+// 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 "src/ctype/isalpha_l.h"
+
+#include "src/__support/common.h"
+#include "src/__support/ctype_utils.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, isalpha_l, (int c, locale_t)) {
+  return static_cast<int>(internal::isalpha(static_cast<unsigned>(c)));
+}
+
+} // namespace LIBC_NAMESPACE_DECL

diff  --git a/libc/src/ctype/isalpha_l.h b/libc/src/ctype/isalpha_l.h
new file mode 100644
index 00000000000000..3591f1175cb9a9
--- /dev/null
+++ b/libc/src/ctype/isalpha_l.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for isalpha_l -----------------------*-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 LLVM_LIBC_SRC_CTYPE_ISALPHA_H
+#define LLVM_LIBC_SRC_CTYPE_ISALPHA_H
+
+#include "hdr/types/locale_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int isalpha_l(int c, locale_t locale);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif //  LLVM_LIBC_SRC_CTYPE_ISALPHA_H

diff  --git a/libc/src/ctype/isblank.cpp b/libc/src/ctype/isblank.cpp
index a4f33d265bd2dd..e0a20829f86cee 100644
--- a/libc/src/ctype/isblank.cpp
+++ b/libc/src/ctype/isblank.cpp
@@ -13,8 +13,6 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-// TODO: Currently restricted to default locale.
-// These should be extended using locale information.
 LLVM_LIBC_FUNCTION(int, isblank, (int c)) {
   return static_cast<int>(c == ' ' || c == '\t');
 }

diff  --git a/libc/src/ctype/isblank_l.cpp b/libc/src/ctype/isblank_l.cpp
new file mode 100644
index 00000000000000..4f6b0bfac29724
--- /dev/null
+++ b/libc/src/ctype/isblank_l.cpp
@@ -0,0 +1,20 @@
+//===-- Implementation of isblank -----------------------------------------===//
+//
+// 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 "src/ctype/isblank_l.h"
+
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, isblank_l, (int c, locale_t)) {
+  return static_cast<int>(c == ' ' || c == '\t');
+}
+
+} // namespace LIBC_NAMESPACE_DECL

diff  --git a/libc/src/ctype/isblank_l.h b/libc/src/ctype/isblank_l.h
new file mode 100644
index 00000000000000..61ede30ae76775
--- /dev/null
+++ b/libc/src/ctype/isblank_l.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for isblank_l -----------------------*-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 LLVM_LIBC_SRC_CTYPE_ISBLANK_H
+#define LLVM_LIBC_SRC_CTYPE_ISBLANK_H
+
+#include "hdr/types/locale_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int isblank_l(int c, locale_t locale);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif //  LLVM_LIBC_SRC_CTYPE_ISBLANK_H

diff  --git a/libc/src/ctype/iscntrl.cpp b/libc/src/ctype/iscntrl.cpp
index fb582fd6ef0820..2218adfcc33f3b 100644
--- a/libc/src/ctype/iscntrl.cpp
+++ b/libc/src/ctype/iscntrl.cpp
@@ -13,8 +13,6 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-// TODO: Currently restricted to default locale.
-// These should be extended using locale information.
 LLVM_LIBC_FUNCTION(int, iscntrl, (int c)) {
   const unsigned ch = static_cast<unsigned>(c);
   return static_cast<int>(ch < 0x20 || ch == 0x7f);

diff  --git a/libc/src/ctype/iscntrl_l.cpp b/libc/src/ctype/iscntrl_l.cpp
new file mode 100644
index 00000000000000..83aa480299fadc
--- /dev/null
+++ b/libc/src/ctype/iscntrl_l.cpp
@@ -0,0 +1,21 @@
+//===-- Implementation of iscntrl -----------------------------------------===//
+//
+// 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 "src/ctype/iscntrl_l.h"
+
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, iscntrl_l, (int c, locale_t)) {
+  const unsigned ch = static_cast<unsigned>(c);
+  return static_cast<int>(ch < 0x20 || ch == 0x7f);
+}
+
+} // namespace LIBC_NAMESPACE_DECL

diff  --git a/libc/src/ctype/iscntrl_l.h b/libc/src/ctype/iscntrl_l.h
new file mode 100644
index 00000000000000..7dee44fcd0bebc
--- /dev/null
+++ b/libc/src/ctype/iscntrl_l.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for iscntrl_l -----------------------*-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 LLVM_LIBC_SRC_CTYPE_ISCNTRL_H
+#define LLVM_LIBC_SRC_CTYPE_ISCNTRL_H
+
+#include "hdr/types/locale_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int iscntrl_l(int c, locale_t locale);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif //  LLVM_LIBC_SRC_CTYPE_ISCNTRL_H

diff  --git a/libc/src/ctype/isdigit.cpp b/libc/src/ctype/isdigit.cpp
index 43c5f1940c7f00..1f711943861f8b 100644
--- a/libc/src/ctype/isdigit.cpp
+++ b/libc/src/ctype/isdigit.cpp
@@ -13,8 +13,6 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-// TODO: Currently restricted to default locale.
-// These should be extended using locale information.
 LLVM_LIBC_FUNCTION(int, isdigit, (int c)) {
   return static_cast<int>(internal::isdigit(static_cast<unsigned>(c)));
 }

diff  --git a/libc/src/ctype/isdigit_l.cpp b/libc/src/ctype/isdigit_l.cpp
new file mode 100644
index 00000000000000..ca981362bfe839
--- /dev/null
+++ b/libc/src/ctype/isdigit_l.cpp
@@ -0,0 +1,20 @@
+//===-- Implementation of isdigit -----------------------------------------===//
+//
+// 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 "src/ctype/isdigit_l.h"
+#include "src/__support/common.h"
+#include "src/__support/ctype_utils.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, isdigit_l, (int c, locale_t)) {
+  return static_cast<int>(internal::isdigit(static_cast<unsigned>(c)));
+}
+
+} // namespace LIBC_NAMESPACE_DECL

diff  --git a/libc/src/ctype/isdigit_l.h b/libc/src/ctype/isdigit_l.h
new file mode 100644
index 00000000000000..abeec3464941a0
--- /dev/null
+++ b/libc/src/ctype/isdigit_l.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for isdigit_l -----------------------*-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 LLVM_LIBC_SRC_CTYPE_ISDIGIT_H
+#define LLVM_LIBC_SRC_CTYPE_ISDIGIT_H
+
+#include "hdr/types/locale_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int isdigit_l(int c, locale_t locale);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif //  LLVM_LIBC_SRC_CTYPE_ISDIGIT_H

diff  --git a/libc/src/ctype/isgraph.cpp b/libc/src/ctype/isgraph.cpp
index a5b6e501b5813f..74bb2e75d138e6 100644
--- a/libc/src/ctype/isgraph.cpp
+++ b/libc/src/ctype/isgraph.cpp
@@ -14,8 +14,6 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-// TODO: Currently restricted to default locale.
-// These should be extended using locale information.
 LLVM_LIBC_FUNCTION(int, isgraph, (int c)) {
   return static_cast<int>(internal::isgraph(static_cast<unsigned>(c)));
 }

diff  --git a/libc/src/ctype/isgraph_l.cpp b/libc/src/ctype/isgraph_l.cpp
new file mode 100644
index 00000000000000..cbef6df148aed6
--- /dev/null
+++ b/libc/src/ctype/isgraph_l.cpp
@@ -0,0 +1,21 @@
+//===-- Implementation of isgraph -----------------------------------------===//
+//
+// 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 "src/ctype/isgraph_l.h"
+
+#include "src/__support/common.h"
+#include "src/__support/ctype_utils.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, isgraph_l, (int c, locale_t)) {
+  return static_cast<int>(internal::isgraph(static_cast<unsigned>(c)));
+}
+
+} // namespace LIBC_NAMESPACE_DECL

diff  --git a/libc/src/ctype/isgraph_l.h b/libc/src/ctype/isgraph_l.h
new file mode 100644
index 00000000000000..d96a4608655092
--- /dev/null
+++ b/libc/src/ctype/isgraph_l.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for isgraph_l -----------------------*-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 LLVM_LIBC_SRC_CTYPE_ISGRAPH_H
+#define LLVM_LIBC_SRC_CTYPE_ISGRAPH_H
+
+#include "hdr/types/locale_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int isgraph_l(int c, locale_t locale);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif //  LLVM_LIBC_SRC_CTYPE_ISGRAPH_H

diff  --git a/libc/src/ctype/islower.cpp b/libc/src/ctype/islower.cpp
index 61ccbcc1db413b..831aad32d3a22e 100644
--- a/libc/src/ctype/islower.cpp
+++ b/libc/src/ctype/islower.cpp
@@ -14,8 +14,6 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-// TODO: Currently restricted to default locale.
-// These should be extended using locale information.
 LLVM_LIBC_FUNCTION(int, islower, (int c)) {
   return static_cast<int>(internal::islower(static_cast<unsigned>(c)));
 }

diff  --git a/libc/src/ctype/islower_l.cpp b/libc/src/ctype/islower_l.cpp
new file mode 100644
index 00000000000000..b9be6acc81c992
--- /dev/null
+++ b/libc/src/ctype/islower_l.cpp
@@ -0,0 +1,21 @@
+//===-- Implementation of islower -----------------------------------------===//
+//
+// 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 "src/ctype/islower_l.h"
+#include "src/__support/ctype_utils.h"
+
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, islower_l, (int c, locale_t)) {
+  return static_cast<int>(internal::islower(static_cast<unsigned>(c)));
+}
+
+} // namespace LIBC_NAMESPACE_DECL

diff  --git a/libc/src/ctype/islower_l.h b/libc/src/ctype/islower_l.h
new file mode 100644
index 00000000000000..7d3e2f139602b9
--- /dev/null
+++ b/libc/src/ctype/islower_l.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for islower_l -----------------------*-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 LLVM_LIBC_SRC_CTYPE_ISLOWER_H
+#define LLVM_LIBC_SRC_CTYPE_ISLOWER_H
+
+#include "hdr/types/locale_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int islower_l(int c, locale_t locale);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif //  LLVM_LIBC_SRC_CTYPE_ISLOWER_H

diff  --git a/libc/src/ctype/isprint.cpp b/libc/src/ctype/isprint.cpp
index 42ab9cc8d238a1..349aefe1c17bbd 100644
--- a/libc/src/ctype/isprint.cpp
+++ b/libc/src/ctype/isprint.cpp
@@ -13,8 +13,6 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-// TODO: Currently restricted to default locale.
-// These should be extended using locale information.
 LLVM_LIBC_FUNCTION(int, isprint, (int c)) {
   const unsigned ch = static_cast<unsigned>(c);
   return static_cast<int>((ch - ' ') < 95);

diff  --git a/libc/src/ctype/isprint_l.cpp b/libc/src/ctype/isprint_l.cpp
new file mode 100644
index 00000000000000..8f51f7f0e3e94b
--- /dev/null
+++ b/libc/src/ctype/isprint_l.cpp
@@ -0,0 +1,21 @@
+//===-- Implementation of isprint -----------------------------------------===//
+//
+// 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 "src/ctype/isprint_l.h"
+
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, isprint_l, (int c, locale_t)) {
+  const unsigned ch = static_cast<unsigned>(c);
+  return static_cast<int>((ch - ' ') < 95);
+}
+
+} // namespace LIBC_NAMESPACE_DECL

diff  --git a/libc/src/ctype/isprint_l.h b/libc/src/ctype/isprint_l.h
new file mode 100644
index 00000000000000..bd2ea9354c36a0
--- /dev/null
+++ b/libc/src/ctype/isprint_l.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for isprint_l -----------------------*-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 LLVM_LIBC_SRC_CTYPE_ISPRINT_H
+#define LLVM_LIBC_SRC_CTYPE_ISPRINT_H
+
+#include "hdr/types/locale_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int isprint_l(int c, locale_t locale);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif //  LLVM_LIBC_SRC_CTYPE_ISPRINT_H

diff  --git a/libc/src/ctype/ispunct.cpp b/libc/src/ctype/ispunct.cpp
index c1906e3acdd80e..0635294220b9c3 100644
--- a/libc/src/ctype/ispunct.cpp
+++ b/libc/src/ctype/ispunct.cpp
@@ -14,8 +14,6 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-// TODO: Currently restricted to default locale.
-// These should be extended using locale information.
 LLVM_LIBC_FUNCTION(int, ispunct, (int c)) {
   const unsigned ch = static_cast<unsigned>(c);
   return static_cast<int>(!internal::isalnum(ch) && internal::isgraph(ch));

diff  --git a/libc/src/ctype/ispunct_l.cpp b/libc/src/ctype/ispunct_l.cpp
new file mode 100644
index 00000000000000..e825fbe2001b08
--- /dev/null
+++ b/libc/src/ctype/ispunct_l.cpp
@@ -0,0 +1,22 @@
+//===-- Implementation of ispunct -----------------------------------------===//
+//
+// 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 "src/ctype/ispunct_l.h"
+
+#include "src/__support/common.h"
+#include "src/__support/ctype_utils.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, ispunct_l, (int c, locale_t)) {
+  const unsigned ch = static_cast<unsigned>(c);
+  return static_cast<int>(!internal::isalnum(ch) && internal::isgraph(ch));
+}
+
+} // namespace LIBC_NAMESPACE_DECL

diff  --git a/libc/src/ctype/ispunct_l.h b/libc/src/ctype/ispunct_l.h
new file mode 100644
index 00000000000000..862daf4836f788
--- /dev/null
+++ b/libc/src/ctype/ispunct_l.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for ispunct_l -----------------------*-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 LLVM_LIBC_SRC_CTYPE_ISPUNCT_H
+#define LLVM_LIBC_SRC_CTYPE_ISPUNCT_H
+
+#include "hdr/types/locale_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int ispunct_l(int c, locale_t locale);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif //  LLVM_LIBC_SRC_CTYPE_ISPUNCT_H

diff  --git a/libc/src/ctype/isspace.cpp b/libc/src/ctype/isspace.cpp
index f8908493787841..005bf460fc1032 100644
--- a/libc/src/ctype/isspace.cpp
+++ b/libc/src/ctype/isspace.cpp
@@ -14,8 +14,6 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-// TODO: Currently restricted to default locale.
-// These should be extended using locale information.
 LLVM_LIBC_FUNCTION(int, isspace, (int c)) {
   return static_cast<int>(internal::isspace(static_cast<unsigned>(c)));
 }

diff  --git a/libc/src/ctype/isspace_l.cpp b/libc/src/ctype/isspace_l.cpp
new file mode 100644
index 00000000000000..5c46dd68051261
--- /dev/null
+++ b/libc/src/ctype/isspace_l.cpp
@@ -0,0 +1,21 @@
+//===-- Implementation of isspace -----------------------------------------===//
+//
+// 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 "src/ctype/isspace_l.h"
+#include "src/__support/ctype_utils.h"
+
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, isspace_l, (int c, locale_t)) {
+  return static_cast<int>(internal::isspace(static_cast<unsigned>(c)));
+}
+
+} // namespace LIBC_NAMESPACE_DECL

diff  --git a/libc/src/ctype/isspace_l.h b/libc/src/ctype/isspace_l.h
new file mode 100644
index 00000000000000..61bbf127956da7
--- /dev/null
+++ b/libc/src/ctype/isspace_l.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for isspace_l -----------------------*-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 LLVM_LIBC_SRC_CTYPE_ISSPACE_H
+#define LLVM_LIBC_SRC_CTYPE_ISSPACE_H
+
+#include "hdr/types/locale_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int isspace_l(int c, locale_t locale);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif //  LLVM_LIBC_SRC_CTYPE_ISSPACE_H

diff  --git a/libc/src/ctype/isupper.cpp b/libc/src/ctype/isupper.cpp
index 8f929ea1a009e4..965fa336b28b4d 100644
--- a/libc/src/ctype/isupper.cpp
+++ b/libc/src/ctype/isupper.cpp
@@ -14,8 +14,6 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-// TODO: Currently restricted to default locale.
-// These should be extended using locale information.
 LLVM_LIBC_FUNCTION(int, isupper, (int c)) {
   return static_cast<int>(internal::isupper(static_cast<unsigned>(c)));
 }

diff  --git a/libc/src/ctype/isupper_l.cpp b/libc/src/ctype/isupper_l.cpp
new file mode 100644
index 00000000000000..358990261d603f
--- /dev/null
+++ b/libc/src/ctype/isupper_l.cpp
@@ -0,0 +1,21 @@
+//===-- Implementation of isupper -----------------------------------------===//
+//
+// 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 "src/ctype/isupper_l.h"
+#include "src/__support/ctype_utils.h"
+
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, isupper_l, (int c, locale_t)) {
+  return static_cast<int>(internal::isupper(static_cast<unsigned>(c)));
+}
+
+} // namespace LIBC_NAMESPACE_DECL

diff  --git a/libc/src/ctype/isupper_l.h b/libc/src/ctype/isupper_l.h
new file mode 100644
index 00000000000000..9bee7ef8c09f59
--- /dev/null
+++ b/libc/src/ctype/isupper_l.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for isupper_l -----------------------*-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 LLVM_LIBC_SRC_CTYPE_ISUPPER_H
+#define LLVM_LIBC_SRC_CTYPE_ISUPPER_H
+
+#include "hdr/types/locale_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int isupper_l(int c, locale_t locale);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif //  LLVM_LIBC_SRC_CTYPE_ISUPPER_H

diff  --git a/libc/src/ctype/isxdigit.cpp b/libc/src/ctype/isxdigit.cpp
index 391c5c53cee1e1..6b730c354db083 100644
--- a/libc/src/ctype/isxdigit.cpp
+++ b/libc/src/ctype/isxdigit.cpp
@@ -14,8 +14,6 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-// TODO: Currently restricted to default locale.
-// These should be extended using locale information.
 LLVM_LIBC_FUNCTION(int, isxdigit, (int c)) {
   const unsigned ch = static_cast<unsigned>(c);
   return static_cast<int>(internal::isdigit(ch) || (ch | 32) - 'a' < 6);

diff  --git a/libc/src/ctype/isxdigit_l.cpp b/libc/src/ctype/isxdigit_l.cpp
new file mode 100644
index 00000000000000..8a5c7d4d28ab1c
--- /dev/null
+++ b/libc/src/ctype/isxdigit_l.cpp
@@ -0,0 +1,22 @@
+//===-- Implementation of isxdigit ----------------------------------------===//
+//
+// 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 "src/ctype/isxdigit_l.h"
+#include "src/__support/ctype_utils.h"
+
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, isxdigit_l, (int c, locale_t)) {
+  const unsigned ch = static_cast<unsigned>(c);
+  return static_cast<int>(internal::isdigit(ch) || (ch | 32) - 'a' < 6);
+}
+
+} // namespace LIBC_NAMESPACE_DECL

diff  --git a/libc/src/ctype/isxdigit_l.h b/libc/src/ctype/isxdigit_l.h
new file mode 100644
index 00000000000000..ee847eda4eae9a
--- /dev/null
+++ b/libc/src/ctype/isxdigit_l.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for isxdigit_l ----------------------*-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 LLVM_LIBC_SRC_CTYPE_ISXDIGIT_H
+#define LLVM_LIBC_SRC_CTYPE_ISXDIGIT_H
+
+#include "hdr/types/locale_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int isxdigit_l(int c, locale_t locale);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif //  LLVM_LIBC_SRC_CTYPE_ISXDIGIT_H

diff  --git a/libc/src/ctype/tolower.cpp b/libc/src/ctype/tolower.cpp
index e230428eef2b14..3ecad7bc5d5d54 100644
--- a/libc/src/ctype/tolower.cpp
+++ b/libc/src/ctype/tolower.cpp
@@ -14,8 +14,6 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-// TODO: Currently restricted to default locale.
-// These should be extended using locale information.
 LLVM_LIBC_FUNCTION(int, tolower, (int c)) { return internal::tolower(c); }
 
 } // namespace LIBC_NAMESPACE_DECL

diff  --git a/libc/src/ctype/tolower_l.cpp b/libc/src/ctype/tolower_l.cpp
new file mode 100644
index 00000000000000..7ccf31617e5925
--- /dev/null
+++ b/libc/src/ctype/tolower_l.cpp
@@ -0,0 +1,21 @@
+//===-- Implementation of tolower -----------------------------------------===//
+//
+// 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 "src/ctype/tolower_l.h"
+#include "src/__support/ctype_utils.h"
+
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, tolower_l, (int c, locale_t)) {
+  return internal::tolower(c);
+}
+
+} // namespace LIBC_NAMESPACE_DECL

diff  --git a/libc/src/ctype/tolower_l.h b/libc/src/ctype/tolower_l.h
new file mode 100644
index 00000000000000..6099b8c813469c
--- /dev/null
+++ b/libc/src/ctype/tolower_l.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for tolower_l -----------------------*-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 LLVM_LIBC_SRC_CTYPE_TOLOWER_H
+#define LLVM_LIBC_SRC_CTYPE_TOLOWER_H
+
+#include "hdr/types/locale_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int tolower_l(int c, locale_t locale);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif //  LLVM_LIBC_SRC_CTYPE_TOLOWER_H

diff  --git a/libc/src/ctype/toupper.cpp b/libc/src/ctype/toupper.cpp
index 97c1ac2c02b8c0..b5a23fc7f588bd 100644
--- a/libc/src/ctype/toupper.cpp
+++ b/libc/src/ctype/toupper.cpp
@@ -14,8 +14,6 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-// TODO: Currently restricted to default locale.
-// These should be extended using locale information.
 LLVM_LIBC_FUNCTION(int, toupper, (int c)) {
   if (internal::islower(c))
     return c - ('a' - 'A');

diff  --git a/libc/src/ctype/toupper_l.cpp b/libc/src/ctype/toupper_l.cpp
new file mode 100644
index 00000000000000..f536ff36236160
--- /dev/null
+++ b/libc/src/ctype/toupper_l.cpp
@@ -0,0 +1,23 @@
+//===-- Implementation of toupper_l ---------------------------------------===//
+//
+// 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 "src/ctype/toupper_l.h"
+#include "src/__support/ctype_utils.h"
+
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, toupper_l, (int c, locale_t)) {
+  if (internal::islower(c))
+    return c - ('a' - 'A');
+  return c;
+}
+
+} // namespace LIBC_NAMESPACE_DECL

diff  --git a/libc/src/ctype/toupper_l.h b/libc/src/ctype/toupper_l.h
new file mode 100644
index 00000000000000..8877c35d492bd8
--- /dev/null
+++ b/libc/src/ctype/toupper_l.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for toupper_l -----------------------*-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 LLVM_LIBC_SRC_CTYPE_TOUPPER_H
+#define LLVM_LIBC_SRC_CTYPE_TOUPPER_H
+
+#include "hdr/types/locale_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int toupper_l(int c, locale_t locale);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif //  LLVM_LIBC_SRC_CTYPE_TOUPPER_H


        


More information about the libc-commits mailing list