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

via libc-commits libc-commits at lists.llvm.org
Thu Aug 22 10:41:24 PDT 2024


Author: Joseph Huber
Date: 2024-08-22T12:41:20-05:00
New Revision: 8f005f8306dc52577b3b9482d271fb463f0152a5

URL: https://github.com/llvm/llvm-project/commit/8f005f8306dc52577b3b9482d271fb463f0152a5
DIFF: https://github.com/llvm/llvm-project/commit/8f005f8306dc52577b3b9482d271fb463f0152a5.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/hdr/types/locale_t.h
    libc/include/llvm-libc-macros/locale-macros.h
    libc/include/llvm-libc-types/locale_t.h
    libc/include/llvm-libc-types/struct_lconv.h
    libc/include/locale.h.def
    libc/newhdrgen/yaml/locale.yaml
    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
    libc/src/locale/CMakeLists.txt
    libc/src/locale/duplocale.cpp
    libc/src/locale/duplocale.h
    libc/src/locale/freelocale.cpp
    libc/src/locale/freelocale.h
    libc/src/locale/locale.cpp
    libc/src/locale/locale.h
    libc/src/locale/localeconv.cpp
    libc/src/locale/localeconv.h
    libc/src/locale/newlocale.cpp
    libc/src/locale/newlocale.h
    libc/src/locale/setlocale.cpp
    libc/src/locale/setlocale.h
    libc/src/locale/uselocale.cpp
    libc/src/locale/uselocale.h
    libc/test/src/locale/CMakeLists.txt
    libc/test/src/locale/locale_test.cpp
    libc/test/src/locale/localeconv_test.cpp

Modified: 
    libc/config/gpu/entrypoints.txt
    libc/config/gpu/headers.txt
    libc/config/linux/x86_64/entrypoints.txt
    libc/config/linux/x86_64/headers.txt
    libc/hdr/types/CMakeLists.txt
    libc/include/CMakeLists.txt
    libc/include/llvm-libc-macros/CMakeLists.txt
    libc/include/llvm-libc-types/CMakeLists.txt
    libc/newhdrgen/yaml/ctype.yaml
    libc/spec/stdc.td
    libc/src/CMakeLists.txt
    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
    libc/test/src/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/libc/config/gpu/entrypoints.txt b/libc/config/gpu/entrypoints.txt
index d7f35bc1edf5a0..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
@@ -233,6 +247,15 @@ set(TARGET_LIBC_ENTRYPOINTS
     # wchar.h entrypoints
     libc.src.wchar.wctob
 
+    # locale.h entrypoints
+    libc.src.locale.localeconv
+    libc.src.locale.duplocale
+    libc.src.locale.freelocale
+    libc.src.locale.localeconv
+    libc.src.locale.newlocale
+    libc.src.locale.setlocale
+    libc.src.locale.uselocale
+
     # gpu/rpc.h entrypoints
     libc.src.gpu.rpc_host_call
 )

diff  --git a/libc/config/gpu/headers.txt b/libc/config/gpu/headers.txt
index 99280b7563a80f..fc952c40f4daa2 100644
--- a/libc/config/gpu/headers.txt
+++ b/libc/config/gpu/headers.txt
@@ -16,6 +16,7 @@ set(TARGET_PUBLIC_HEADERS
     libc.include.wchar
     libc.include.uchar
     libc.include.features
+    libc.include.locale
 
     # Header for RPC extensions
     libc.include.gpu_rpc

diff  --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 65c5757efe6274..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
 
@@ -982,6 +998,15 @@ if(LLVM_LIBC_FULL_BUILD)
     libc.src.time.nanosleep
     libc.src.time.time
 
+    # locale.h entrypoints
+    libc.src.locale.localeconv
+    libc.src.locale.duplocale
+    libc.src.locale.freelocale
+    libc.src.locale.localeconv
+    libc.src.locale.newlocale
+    libc.src.locale.setlocale
+    libc.src.locale.uselocale
+
     # unistd.h entrypoints
     libc.src.unistd.__llvm_libc_syscall
     libc.src.unistd._exit

diff  --git a/libc/config/linux/x86_64/headers.txt b/libc/config/linux/x86_64/headers.txt
index 77e454e64395df..881e149d9c40d3 100644
--- a/libc/config/linux/x86_64/headers.txt
+++ b/libc/config/linux/x86_64/headers.txt
@@ -33,6 +33,7 @@ set(TARGET_PUBLIC_HEADERS
     libc.include.unistd
     libc.include.wchar
     libc.include.uchar
+    libc.include.locale
 
     libc.include.arpa_inet
 

diff  --git a/libc/hdr/types/CMakeLists.txt b/libc/hdr/types/CMakeLists.txt
index 4fc28fd82e68db..f41576c07d99be 100644
--- a/libc/hdr/types/CMakeLists.txt
+++ b/libc/hdr/types/CMakeLists.txt
@@ -162,3 +162,12 @@ add_proxy_header_library(
     libc.include.llvm-libc-types.cookie_io_functions_t
     libc.include.stdio
 )
+
+add_proxy_header_library(
+  locale_t
+  HDRS
+    locale_t.h
+  FULL_BUILD_DEPENDS
+    libc.include.llvm-libc-types.locale_t
+    libc.include.locale
+)

diff  --git a/libc/hdr/types/locale_t.h b/libc/hdr/types/locale_t.h
new file mode 100644
index 00000000000000..485258b4616962
--- /dev/null
+++ b/libc/hdr/types/locale_t.h
@@ -0,0 +1,22 @@
+//===-- Definition of macros from locale_t.h ------------------------------===//
+//
+// 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_HDR_LOCALE_T_H
+#define LLVM_LIBC_HDR_LOCALE_T_H
+
+#ifdef LIBC_FULL_BUILD
+
+#include "include/llvm-libc-types/locale_t.h"
+
+#else // overlay mode
+
+#error "type not available in overlay mode"
+
+#endif // LLVM_LIBC_FULL_BUILD
+
+#endif // LLVM_LIBC_HDR_LOCALE_T_H

diff  --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
index 4e3ae7f801f4a0..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(
@@ -719,6 +720,18 @@ add_header_macro(
     .llvm-libc-types.wchar_t
 )
 
+add_header_macro(
+  locale
+  ../libc/newhdrgen/yaml/locale.yaml
+  locale.h.def
+  locale.h
+  DEPENDS
+    .llvm_libc_common_h
+    .llvm-libc-macros.locale_macros
+    .llvm-libc-types.locale_t
+    .llvm-libc-types.struct_lconv
+)
+
 if(LIBC_TARGET_OS_IS_GPU)
   file(MAKE_DIRECTORY ${LIBC_INCLUDE_DIR}/gpu)
 

diff  --git a/libc/include/llvm-libc-macros/CMakeLists.txt b/libc/include/llvm-libc-macros/CMakeLists.txt
index 60a8725f9ef63f..7b980232ba0429 100644
--- a/libc/include/llvm-libc-macros/CMakeLists.txt
+++ b/libc/include/llvm-libc-macros/CMakeLists.txt
@@ -295,3 +295,9 @@ add_macro_header(
   HDR
     elf-macros.h
 )
+
+add_macro_header(
+  locale_macros
+  HDR
+    locale-macros.h
+)

diff  --git a/libc/include/llvm-libc-macros/locale-macros.h b/libc/include/llvm-libc-macros/locale-macros.h
new file mode 100644
index 00000000000000..892f8b69f3a777
--- /dev/null
+++ b/libc/include/llvm-libc-macros/locale-macros.h
@@ -0,0 +1,32 @@
+//===-- Definition of macros from locale.h --------------------------------===//
+//
+// 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_MACROS_LOCALE_MACROS_H
+#define LLVM_LIBC_MACROS_LOCALE_MACROS_H
+
+#include "../llvm-libc-types/locale_t.h"
+
+#define LC_CTYPE 0
+#define LC_NUMERIC 1
+#define LC_TIME 2
+#define LC_COLLATE 3
+#define LC_MONETARY 4
+#define LC_MESSAGES 5
+#define LC_ALL 6
+
+#define LC_GLOBAL_LOCALE ((locale_t)(-1))
+
+#define LC_CTYPE_MASK (1 << LC_CTYPE)
+#define LC_NUMERIC_MASK (1 << LC_NUMERIC)
+#define LC_TIME_MASK (1 << LC_TIME)
+#define LC_COLLATE_MASK (1 << LC_COLLATE)
+#define LC_MONETARY_MASK (1 << LC_MONETARY)
+#define LC_MESSAGES_MASK (1 << LC_MESSAGES)
+#define LC_ALL_MASK 0x7fffffff
+
+#endif // LLVM_LIBC_MACROS_LOCALE_MACROS_H

diff  --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt
index 0fa86e0152f9ba..583b84ccaae67c 100644
--- a/libc/include/llvm-libc-types/CMakeLists.txt
+++ b/libc/include/llvm-libc-types/CMakeLists.txt
@@ -142,3 +142,5 @@ DEPENDS
   .fsblkcnt_t
   .fsfilcnt_t
 )
+add_header(locale_t HDR locale_t.h)
+add_header(struct_lconv HDR struct_lconv.h)

diff  --git a/libc/include/llvm-libc-types/locale_t.h b/libc/include/llvm-libc-types/locale_t.h
new file mode 100644
index 00000000000000..6d783001acf9f2
--- /dev/null
+++ b/libc/include/llvm-libc-types/locale_t.h
@@ -0,0 +1,22 @@
+//===-- Definition of type locale_t ---------------------------------------===//
+//
+// 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_TYPES_LOCALE_T_H
+#define LLVM_LIBC_TYPES_LOCALE_T_H
+
+#define NUM_LOCALE_CATEGORIES 6
+
+struct __locale_data;
+
+struct __locale_t {
+  struct __locale_data *data[NUM_LOCALE_CATEGORIES];
+};
+
+typedef struct __locale_t *locale_t;
+
+#endif // LLVM_LIBC_TYPES_LOCALE_T_H

diff  --git a/libc/include/llvm-libc-types/struct_lconv.h b/libc/include/llvm-libc-types/struct_lconv.h
new file mode 100644
index 00000000000000..9d69f055484dad
--- /dev/null
+++ b/libc/include/llvm-libc-types/struct_lconv.h
@@ -0,0 +1,39 @@
+//===-- Definition of type lconv ------------------------------------------===//
+//
+// 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_TYPES_LCONV_H
+#define LLVM_LIBC_TYPES_LCONV_H
+
+struct lconv {
+  char *decimal_point;
+  char *thousands_sep;
+  char *grouping;
+  char *mon_decimal_point;
+  char *mon_thousands_sep;
+  char *mon_grouping;
+  char *positive_sign;
+  char *negative_sign;
+  char *currency_symbol;
+  char frac_digits;
+  char p_cs_precedes;
+  char n_cs_precedes;
+  char p_sep_by_space;
+  char n_sep_by_space;
+  char p_sign_posn;
+  char n_sign_posn;
+  char *int_curr_symbol;
+  char int_frac_digits;
+  char int_p_cs_precedes;
+  char int_n_cs_precedes;
+  char int_p_sep_by_space;
+  char int_n_sep_by_space;
+  char int_p_sign_posn;
+  char int_n_sign_posn;
+};
+
+#endif // LLVM_LIBC_TYPES_LCONV_H

diff  --git a/libc/include/locale.h.def b/libc/include/locale.h.def
new file mode 100644
index 00000000000000..516c6e6275e681
--- /dev/null
+++ b/libc/include/locale.h.def
@@ -0,0 +1,20 @@
+//===-- C standard library header locale.h --------------------------------===//
+//
+// 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_LOCALE_H
+#define LLVM_LIBC_LOCALE_H
+
+#include "__llvm-libc-common.h"
+
+#include "llvm-libc-macros/locale-macros.h"
+#include "llvm-libc-types/locale_t.h"
+#include "llvm-libc-types/struct_lconv.h"
+
+%%public_api()
+
+#endif // LLVM_LIBC_LOCALE_H

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/newhdrgen/yaml/locale.yaml b/libc/newhdrgen/yaml/locale.yaml
new file mode 100644
index 00000000000000..7da7966ea730f6
--- /dev/null
+++ b/libc/newhdrgen/yaml/locale.yaml
@@ -0,0 +1,41 @@
+header: locale.h
+functions:
+  - name: localeconv
+    standards:
+      - stdc
+    return_type: struct lconv *
+    arguments:
+      - type: void
+  - name: duplocale
+    standards:
+      - stdc
+    return_type: locale_t
+    arguments:
+      - type: locale_t
+  - name: freelocale
+    standards:
+      - stdc
+    return_type: void
+    arguments:
+      - type: locale_t
+  - name: newlocale
+    standards:
+      - stdc
+    return_type: locale_t
+    arguments:
+      - type: int
+      - type: const char *
+      - type: locale_t
+  - name: setlocale
+    standards:
+      - stdc
+    return_type: char *
+    arguments:
+      - type: int
+      - type: const char *
+  - name: uselocale
+    standards:
+      - stdc
+    return_type: locale_t
+    arguments:
+      - type: locale_t

diff  --git a/libc/spec/stdc.td b/libc/spec/stdc.td
index 118dcce829be23..402d8c335470ad 100644
--- a/libc/spec/stdc.td
+++ b/libc/spec/stdc.td
@@ -4,6 +4,7 @@ def StdC : StandardSpec<"stdc"> {
   PtrType StructTmPtr = PtrType<StructTmType>;
   PtrType TimeTTypePtr = PtrType<TimeTType>;
   NamedType ClockT = NamedType<"clock_t">;
+  NamedType LocaleT = NamedType<"locale_t">;
 
   NamedType DivTType = NamedType<"div_t">;
   NamedType LDivTType = NamedType<"ldiv_t">;
@@ -34,7 +35,9 @@ def StdC : StandardSpec<"stdc"> {
   HeaderSpec CType = HeaderSpec<
       "ctype.h",
       [], // Macros
-      [], // Types
+      [
+          LocaleT
+      ], // Types
       [], // Enumerations
       [
           FunctionSpec<
@@ -107,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>]
+          >,
       ]
   >;
 
@@ -1591,6 +1664,61 @@ def StdC : StandardSpec<"stdc"> {
       ]
   >;
 
+  
+  NamedType StructLconv : NamedType<"struct lconv">;
+  PtrType StructLconvPtr : PtrType<StructLconv>;
+
+  HeaderSpec Locale = HeaderSpec<
+     "locale.h",
+      [], // Macros
+      [LocaleT, StructLconv], // Types
+      [], // Enumerations
+      [
+        FunctionSpec<
+          "duplocale",
+          RetValSpec<LocaleT>,
+          [
+            ArgSpec<LocaleT>
+          ]
+        >,
+        FunctionSpec<
+          "freelocale",
+          RetValSpec<VoidType>,
+          [
+            ArgSpec<LocaleT>
+          ]
+        >,
+        FunctionSpec<
+          "localeconv",
+          RetValSpec<StructLconvPtr>,
+          []
+        >,
+        FunctionSpec<
+          "newlocale",
+          RetValSpec<LocaleT>,
+          [
+            ArgSpec<IntType>,
+            ArgSpec<ConstCharPtr>,
+            ArgSpec<LocaleT>
+          ]
+        >,
+        FunctionSpec<
+          "setlocale",
+          RetValSpec<CharPtr>,
+          [
+            ArgSpec<IntType>,
+            ArgSpec<ConstCharPtr>
+          ]
+        >,
+        FunctionSpec<
+          "uselocale",
+          RetValSpec<LocaleT>,
+          [
+            ArgSpec<LocaleT>
+          ]
+        >
+      ]  // Functions
+  >;
 
   let Headers = [
     Assert,
@@ -1613,5 +1741,6 @@ def StdC : StandardSpec<"stdc"> {
     Time,
     UChar,
     WChar,
+    Locale,
   ];
 }

diff  --git a/libc/src/CMakeLists.txt b/libc/src/CMakeLists.txt
index 9597e2380172b5..d554c12fb1ec89 100644
--- a/libc/src/CMakeLists.txt
+++ b/libc/src/CMakeLists.txt
@@ -40,3 +40,4 @@ add_subdirectory(signal)
 add_subdirectory(spawn)
 add_subdirectory(threads)
 add_subdirectory(time)
+add_subdirectory(locale)

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

diff  --git a/libc/src/locale/CMakeLists.txt b/libc/src/locale/CMakeLists.txt
new file mode 100644
index 00000000000000..6aaeb2ac31488b
--- /dev/null
+++ b/libc/src/locale/CMakeLists.txt
@@ -0,0 +1,76 @@
+add_object_library(
+  locale
+  SRCS
+    locale.cpp
+  HDRS
+    locale.h
+  DEPENDS
+    libc.include.locale
+)
+
+add_entrypoint_object(
+  localeconv
+  SRCS
+    localeconv.cpp
+  HDRS
+    localeconv.h
+  DEPENDS
+    libc.include.locale
+  CXX_STANDARD
+    20 # For designated initializers
+)
+
+add_entrypoint_object(
+  newlocale
+  SRCS
+    newlocale.cpp
+  HDRS
+    newlocale.h
+  DEPENDS
+    libc.include.locale
+    .locale
+)
+
+add_entrypoint_object(
+  duplocale
+  SRCS
+    duplocale.cpp
+  HDRS
+    duplocale.h
+  DEPENDS
+    libc.include.locale
+    .locale
+)
+
+add_entrypoint_object(
+  setlocale
+  SRCS
+    setlocale.cpp
+  HDRS
+    setlocale.h
+  DEPENDS
+    libc.include.locale
+    .locale
+)
+
+add_entrypoint_object(
+  uselocale
+  SRCS
+    uselocale.cpp
+  HDRS
+    uselocale.h
+  DEPENDS
+    libc.include.locale
+    .locale
+)
+
+add_entrypoint_object(
+  freelocale
+  SRCS
+    freelocale.cpp
+  HDRS
+    freelocale.h
+  DEPENDS
+    libc.include.locale
+    .locale
+)

diff  --git a/libc/src/locale/duplocale.cpp b/libc/src/locale/duplocale.cpp
new file mode 100644
index 00000000000000..d1bd0835121fcd
--- /dev/null
+++ b/libc/src/locale/duplocale.cpp
@@ -0,0 +1,21 @@
+//===-- Implementation of duplocale ---------------------------------------===//
+//
+// 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/locale/duplocale.h"
+#include "include/llvm-libc-macros/locale-macros.h"
+#include "src/locale/locale.h"
+
+#include "src/__support/CPP/string_view.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(locale_t, duplocale, (locale_t loc)) { return loc; }
+
+} // namespace LIBC_NAMESPACE_DECL

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

diff  --git a/libc/src/locale/freelocale.cpp b/libc/src/locale/freelocale.cpp
new file mode 100644
index 00000000000000..2008995f101bf0
--- /dev/null
+++ b/libc/src/locale/freelocale.cpp
@@ -0,0 +1,21 @@
+//===-- Implementation of freelocale --------------------------------------===//
+//
+// 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/locale/freelocale.h"
+#include "include/llvm-libc-macros/locale-macros.h"
+#include "src/locale/locale.h"
+
+#include "src/__support/CPP/string_view.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(void, freelocale, (locale_t)) {}
+
+} // namespace LIBC_NAMESPACE_DECL

diff  --git a/libc/src/locale/freelocale.h b/libc/src/locale/freelocale.h
new file mode 100644
index 00000000000000..77ece304307383
--- /dev/null
+++ b/libc/src/locale/freelocale.h
@@ -0,0 +1,22 @@
+//===-- Implementation header for freelocale --------------------*- 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_LOCALE_FREELOCALE_H
+#define LLVM_LIBC_SRC_LOCALE_FREELOCALE_H
+
+#include "src/__support/macros/config.h"
+
+#include "hdr/types/locale_t.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+void freelocale(locale_t loc);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_LOCALE_FREELOCALE_H

diff  --git a/libc/src/locale/locale.cpp b/libc/src/locale/locale.cpp
new file mode 100644
index 00000000000000..18ebc33ad58234
--- /dev/null
+++ b/libc/src/locale/locale.cpp
@@ -0,0 +1,21 @@
+//===-- Implementation of locale ------------------------------------------===//
+//
+// 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/locale/locale.h"
+
+#include "include/llvm-libc-macros/locale-macros.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+__locale_t c_locale = {nullptr};
+
+LIBC_THREAD_LOCAL locale_t locale = nullptr;
+
+} // namespace LIBC_NAMESPACE_DECL

diff  --git a/libc/src/locale/locale.h b/libc/src/locale/locale.h
new file mode 100644
index 00000000000000..6d6db2bcacad3f
--- /dev/null
+++ b/libc/src/locale/locale.h
@@ -0,0 +1,36 @@
+//===-- Implementation header for the locale --------------------*- 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_LOCALE_LOCALECONV_H
+#define LLVM_LIBC_SRC_LOCALE_LOCALECONV_H
+
+#include "src/__support/macros/attributes.h"
+#include "src/__support/macros/config.h"
+
+#include "hdr/types/locale_t.h"
+
+#include <stddef.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+// We only support the "C" locale right now.
+static constexpr size_t MAX_LOCALE_NAME_SIZE = 2;
+
+struct __locale_data {
+  char name[MAX_LOCALE_NAME_SIZE];
+};
+
+// The pointer to the default "C" locale.
+extern __locale_t c_locale;
+
+// The global locale instance.
+LIBC_THREAD_LOCAL extern locale_t locale;
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_LOCALE_LOCALECONV_H

diff  --git a/libc/src/locale/localeconv.cpp b/libc/src/locale/localeconv.cpp
new file mode 100644
index 00000000000000..e4d7536bf1ffb7
--- /dev/null
+++ b/libc/src/locale/localeconv.cpp
@@ -0,0 +1,49 @@
+//===-- Implementation of localeconv --------------------------------------===//
+//
+// 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/locale/localeconv.h"
+
+#include "src/__support/CPP/limits.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+static char DOT_STRING[] = ".";
+static char EMPTY_STRING[] = "";
+
+static struct lconv C_LCONV = {
+    .decimal_point = DOT_STRING,
+    .thousands_sep = EMPTY_STRING,
+    .grouping = EMPTY_STRING,
+    .mon_decimal_point = EMPTY_STRING,
+    .mon_thousands_sep = EMPTY_STRING,
+    .mon_grouping = EMPTY_STRING,
+    .positive_sign = EMPTY_STRING,
+    .negative_sign = EMPTY_STRING,
+    .currency_symbol = EMPTY_STRING,
+    .frac_digits = CHAR_MAX,
+    .p_cs_precedes = CHAR_MAX,
+    .n_cs_precedes = CHAR_MAX,
+    .p_sep_by_space = CHAR_MAX,
+    .n_sep_by_space = CHAR_MAX,
+    .p_sign_posn = CHAR_MAX,
+    .n_sign_posn = CHAR_MAX,
+    .int_curr_symbol = EMPTY_STRING,
+    .int_frac_digits = CHAR_MAX,
+    .int_p_cs_precedes = CHAR_MAX,
+    .int_n_cs_precedes = CHAR_MAX,
+    .int_p_sep_by_space = CHAR_MAX,
+    .int_n_sep_by_space = CHAR_MAX,
+    .int_p_sign_posn = CHAR_MAX,
+    .int_n_sign_posn = CHAR_MAX,
+};
+
+LLVM_LIBC_FUNCTION(struct lconv *, localeconv, ()) { return &C_LCONV; }
+
+} // namespace LIBC_NAMESPACE_DECL

diff  --git a/libc/src/locale/localeconv.h b/libc/src/locale/localeconv.h
new file mode 100644
index 00000000000000..a8f7599b572bf8
--- /dev/null
+++ b/libc/src/locale/localeconv.h
@@ -0,0 +1,22 @@
+//===-- Implementation header for localeconv --------------------*- 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_LOCALE_LOCALECONV_H
+#define LLVM_LIBC_SRC_LOCALE_LOCALECONV_H
+
+#include "src/__support/macros/config.h"
+
+#include "include/llvm-libc-types/struct_lconv.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+struct lconv *localeconv();
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_LOCALE_LOCALECONV_H

diff  --git a/libc/src/locale/newlocale.cpp b/libc/src/locale/newlocale.cpp
new file mode 100644
index 00000000000000..379e7e6385d09f
--- /dev/null
+++ b/libc/src/locale/newlocale.cpp
@@ -0,0 +1,28 @@
+//===-- Implementation of newlocale ---------------------------------------===//
+//
+// 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/locale/newlocale.h"
+#include "include/llvm-libc-macros/locale-macros.h"
+#include "src/locale/locale.h"
+
+#include "src/__support/CPP/string_view.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(locale_t, newlocale,
+                   (int category_mask, const char *locale_name, locale_t)) {
+  cpp::string_view name(locale_name);
+  if (category_mask > LC_ALL || (!name.empty() && name != "C"))
+    return nullptr;
+
+  return &c_locale;
+}
+
+} // namespace LIBC_NAMESPACE_DECL

diff  --git a/libc/src/locale/newlocale.h b/libc/src/locale/newlocale.h
new file mode 100644
index 00000000000000..08a0071cb7aeaa
--- /dev/null
+++ b/libc/src/locale/newlocale.h
@@ -0,0 +1,22 @@
+//===-- Implementation header for setlocale ---------------------*- 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_LOCALE_SETLOCALE_H
+#define LLVM_LIBC_SRC_LOCALE_SETLOCALE_H
+
+#include "src/__support/macros/config.h"
+
+#include "hdr/types/locale_t.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+locale_t newlocale(int category_mask, const char *locale_name, locale_t base);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_LOCALE_SETLOCALE_H

diff  --git a/libc/src/locale/setlocale.cpp b/libc/src/locale/setlocale.cpp
new file mode 100644
index 00000000000000..0950ad73cbe2cf
--- /dev/null
+++ b/libc/src/locale/setlocale.cpp
@@ -0,0 +1,28 @@
+//===-- Implementation of setlocale ---------------------------------------===//
+//
+// 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/locale/setlocale.h"
+#include "include/llvm-libc-macros/locale-macros.h"
+#include "src/locale/locale.h"
+
+#include "src/__support/CPP/string_view.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(char *, setlocale, (int category, const char *locale_name)) {
+  cpp::string_view name(locale_name);
+  if (category > LC_ALL || (!name.empty() && name != "C"))
+    return nullptr;
+
+  static char locale_str[] = "C";
+  return locale_str;
+}
+
+} // namespace LIBC_NAMESPACE_DECL

diff  --git a/libc/src/locale/setlocale.h b/libc/src/locale/setlocale.h
new file mode 100644
index 00000000000000..a9213cf409a7b6
--- /dev/null
+++ b/libc/src/locale/setlocale.h
@@ -0,0 +1,22 @@
+//===-- Implementation header for setlocale ---------------------*- 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_LOCALE_SETLOCALE_H
+#define LLVM_LIBC_SRC_LOCALE_SETLOCALE_H
+
+#include "src/__support/macros/config.h"
+
+#include "hdr/types/locale_t.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+char *setlocale(int category, const char *locale_name);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_LOCALE_SETLOCALE_H

diff  --git a/libc/src/locale/uselocale.cpp b/libc/src/locale/uselocale.cpp
new file mode 100644
index 00000000000000..d6fdad248f12b2
--- /dev/null
+++ b/libc/src/locale/uselocale.cpp
@@ -0,0 +1,23 @@
+//===-- Implementation of uselocale ---------------------------------------===//
+//
+// 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/locale/uselocale.h"
+#include "src/locale/locale.h"
+
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(locale_t, uselocale, (locale_t newloc)) {
+  if (!newloc)
+    return locale;
+  return locale = newloc;
+}
+
+} // namespace LIBC_NAMESPACE_DECL

diff  --git a/libc/src/locale/uselocale.h b/libc/src/locale/uselocale.h
new file mode 100644
index 00000000000000..15403490d2f8cc
--- /dev/null
+++ b/libc/src/locale/uselocale.h
@@ -0,0 +1,22 @@
+//===-- Implementation header for uselocale ---------------------*- 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_LOCALE_USELOCALE_H
+#define LLVM_LIBC_SRC_LOCALE_USELOCALE_H
+
+#include "src/__support/macros/config.h"
+
+#include "hdr/types/locale_t.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+locale_t uselocale(locale_t newloc);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_LOCALE_USELOCALE_H

diff  --git a/libc/test/src/CMakeLists.txt b/libc/test/src/CMakeLists.txt
index 60ea7e6a90d715..ddc6a5c7f6965f 100644
--- a/libc/test/src/CMakeLists.txt
+++ b/libc/test/src/CMakeLists.txt
@@ -82,6 +82,7 @@ add_subdirectory(setjmp)
 add_subdirectory(signal)
 add_subdirectory(spawn)
 add_subdirectory(time)
+add_subdirectory(locale)
 
 if(${LIBC_TARGET_OS} STREQUAL "linux")
   add_subdirectory(pthread)

diff  --git a/libc/test/src/locale/CMakeLists.txt b/libc/test/src/locale/CMakeLists.txt
new file mode 100644
index 00000000000000..3192004db26dd6
--- /dev/null
+++ b/libc/test/src/locale/CMakeLists.txt
@@ -0,0 +1,25 @@
+add_custom_target(libc-locale-tests)
+
+add_libc_test(
+  locale_test
+  SUITE
+    libc-locale-tests
+  SRCS
+    locale_test.cpp
+  DEPENDS
+    libc.include.locale
+    libc.src.locale.newlocale
+    libc.src.locale.uselocale
+    libc.src.locale.freelocale
+)
+
+add_libc_test(
+  localeconv_test
+  SUITE
+    libc-locale-tests
+  SRCS
+    localeconv_test.cpp
+  DEPENDS
+    libc.include.locale
+    libc.src.locale.localeconv
+)

diff  --git a/libc/test/src/locale/locale_test.cpp b/libc/test/src/locale/locale_test.cpp
new file mode 100644
index 00000000000000..bc48bb851f4e4c
--- /dev/null
+++ b/libc/test/src/locale/locale_test.cpp
@@ -0,0 +1,27 @@
+//===-- Unittests for locale ----------------------------------------------===//
+//
+// 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/locale/freelocale.h"
+#include "src/locale/newlocale.h"
+#include "src/locale/uselocale.h"
+
+#include "test/UnitTest/Test.h"
+
+#include "include/llvm-libc-macros/locale-macros.h"
+
+TEST(LlvmLibcLocale, DefaultLocale) {
+  locale_t new_locale = LIBC_NAMESPACE::newlocale(LC_ALL, "C", nullptr);
+  EXPECT_NE(new_locale, static_cast<locale_t>(nullptr));
+
+  locale_t old_locale = LIBC_NAMESPACE::uselocale(new_locale);
+  EXPECT_NE(old_locale, static_cast<locale_t>(nullptr));
+
+  LIBC_NAMESPACE::freelocale(new_locale);
+
+  LIBC_NAMESPACE::uselocale(old_locale);
+}

diff  --git a/libc/test/src/locale/localeconv_test.cpp b/libc/test/src/locale/localeconv_test.cpp
new file mode 100644
index 00000000000000..79264276dec354
--- /dev/null
+++ b/libc/test/src/locale/localeconv_test.cpp
@@ -0,0 +1,17 @@
+//===-- Unittests for localeconv ------------------------------------------===//
+//
+// 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 "include/llvm-libc-macros/locale-macros.h"
+#include "src/locale/localeconv.h"
+
+#include "test/UnitTest/Test.h"
+
+TEST(LlvmLibcLocale, DefaultLocale) {
+  struct lconv *conv = LIBC_NAMESPACE::localeconv();
+  EXPECT_STREQ(conv->decimal_point, ".");
+}


        


More information about the libc-commits mailing list