[libc-commits] [libc] Reland libgen (PR #205352)

Jeff Bailey via libc-commits libc-commits at lists.llvm.org
Tue Jun 23 07:40:57 PDT 2026


https://github.com/kaladron updated https://github.com/llvm/llvm-project/pull/205352

>From 95499a28a294b080280903cc3b846fb5a28687a5 Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jbailey at raspberryginger.com>
Date: Tue, 23 Jun 2026 13:54:31 +0100
Subject: [PATCH 1/2] Reapply "[libc] Implement basename and dirname in
 libgen.h (#204554)" (#204856)

This reverts commit ae60782c82682d20764f888321efb5d53df18a4a.
---
 libc/config/linux/aarch64/entrypoints.txt    |  4 ++
 libc/config/linux/arm/entrypoints.txt        |  4 ++
 libc/config/linux/riscv/entrypoints.txt      |  4 ++
 libc/config/linux/x86_64/entrypoints.txt     |  4 ++
 libc/docs/CMakeLists.txt                     |  1 +
 libc/docs/headers/index.rst                  |  1 +
 libc/include/CMakeLists.txt                  |  8 +++
 libc/include/libgen.yaml                     | 20 ++++++
 libc/src/CMakeLists.txt                      |  1 +
 libc/src/__support/CPP/string_view.h         |  9 +++
 libc/src/libgen/CMakeLists.txt               | 23 ++++++
 libc/src/libgen/basename.cpp                 | 42 +++++++++++
 libc/src/libgen/basename.h                   | 30 ++++++++
 libc/src/libgen/dirname.cpp                  | 48 +++++++++++++
 libc/src/libgen/dirname.h                    | 30 ++++++++
 libc/test/src/CMakeLists.txt                 |  1 +
 libc/test/src/libgen/CMakeLists.txt          | 43 ++++++++++++
 libc/test/src/libgen/basename_death_test.cpp | 22 ++++++
 libc/test/src/libgen/basename_test.cpp       | 62 ++++++++++++++++
 libc/test/src/libgen/dirname_death_test.cpp  | 22 ++++++
 libc/test/src/libgen/dirname_test.cpp        | 74 ++++++++++++++++++++
 libc/utils/docgen/libgen.yaml                |  5 ++
 22 files changed, 458 insertions(+)
 create mode 100644 libc/include/libgen.yaml
 create mode 100644 libc/src/libgen/CMakeLists.txt
 create mode 100644 libc/src/libgen/basename.cpp
 create mode 100644 libc/src/libgen/basename.h
 create mode 100644 libc/src/libgen/dirname.cpp
 create mode 100644 libc/src/libgen/dirname.h
 create mode 100644 libc/test/src/libgen/CMakeLists.txt
 create mode 100644 libc/test/src/libgen/basename_death_test.cpp
 create mode 100644 libc/test/src/libgen/basename_test.cpp
 create mode 100644 libc/test/src/libgen/dirname_death_test.cpp
 create mode 100644 libc/test/src/libgen/dirname_test.cpp
 create mode 100644 libc/utils/docgen/libgen.yaml

diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 5cddf3dc89799..3072c3d22aa5f 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -109,6 +109,10 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.inttypes.strtoimax
     libc.src.inttypes.strtoumax
 
+    # libgen.h entrypoints
+    libc.src.libgen.basename
+    libc.src.libgen.dirname
+
     # stdbit.h entrypoints
     libc.src.stdbit.stdc_bit_ceil_uc
     libc.src.stdbit.stdc_bit_ceil_ui
diff --git a/libc/config/linux/arm/entrypoints.txt b/libc/config/linux/arm/entrypoints.txt
index c4ac53c4925a3..805738a3a5756 100644
--- a/libc/config/linux/arm/entrypoints.txt
+++ b/libc/config/linux/arm/entrypoints.txt
@@ -73,6 +73,10 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.inttypes.strtoimax
     libc.src.inttypes.strtoumax
 
+    # libgen.h entrypoints
+    libc.src.libgen.basename
+    libc.src.libgen.dirname
+
     # stdbit.h entrypoints
     libc.src.stdbit.stdc_bit_ceil_uc
     libc.src.stdbit.stdc_bit_ceil_ui
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index a57efbb8e464d..bcdcf2320f7bd 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -109,6 +109,10 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.inttypes.strtoimax
     libc.src.inttypes.strtoumax
 
+    # libgen.h entrypoints
+    libc.src.libgen.basename
+    libc.src.libgen.dirname
+
     # stdbit.h entrypoints
     libc.src.stdbit.stdc_bit_ceil_uc
     libc.src.stdbit.stdc_bit_ceil_ui
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index ce88a6749d9dc..f19da5902bba1 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -125,6 +125,10 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.inttypes.wcstoimax
     libc.src.inttypes.wcstoumax
 
+    # libgen.h entrypoints
+    libc.src.libgen.basename
+    libc.src.libgen.dirname
+
     # stdbit.h entrypoints
     libc.src.stdbit.stdc_bit_ceil_uc
     libc.src.stdbit.stdc_bit_ceil_ui
diff --git a/libc/docs/CMakeLists.txt b/libc/docs/CMakeLists.txt
index cf54edeae66de..ded99393f9390 100644
--- a/libc/docs/CMakeLists.txt
+++ b/libc/docs/CMakeLists.txt
@@ -54,6 +54,7 @@ if (SPHINX_FOUND)
       float
       glob
       inttypes
+      libgen
       locale
       nl_types
       net/if
diff --git a/libc/docs/headers/index.rst b/libc/docs/headers/index.rst
index e818e1549c0d1..893ddf839cccd 100644
--- a/libc/docs/headers/index.rst
+++ b/libc/docs/headers/index.rst
@@ -20,6 +20,7 @@ Implementation Status
    float
    glob
    inttypes
+   libgen
    locale
    math/index.rst
    net/if
diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
index 549dbd9e4c3f8..e8168687109b0 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -130,6 +130,14 @@ add_header_macro(
     .llvm-libc-macros.float_macros
 )
 
+add_header_macro(
+  libgen
+  ../libc/include/libgen.yaml
+  libgen.h
+  DEPENDS
+    .llvm_libc_common_h
+)
+
 add_header_macro(
   limits
   ../libc/include/limits.yaml
diff --git a/libc/include/libgen.yaml b/libc/include/libgen.yaml
new file mode 100644
index 0000000000000..c79ab79259be6
--- /dev/null
+++ b/libc/include/libgen.yaml
@@ -0,0 +1,20 @@
+header: libgen.h
+standards:
+  - posix
+macros: []
+types: []
+enums: []
+objects: []
+functions:
+  - name: basename
+    standards:
+      - posix
+    return_type: char *
+    arguments:
+      - type: char *
+  - name: dirname
+    standards:
+      - posix
+    return_type: char *
+    arguments:
+      - type: char *
diff --git a/libc/src/CMakeLists.txt b/libc/src/CMakeLists.txt
index 9db314f54723b..56085c9632f59 100644
--- a/libc/src/CMakeLists.txt
+++ b/libc/src/CMakeLists.txt
@@ -7,6 +7,7 @@ add_subdirectory(dlfcn)
 add_subdirectory(errno)
 add_subdirectory(fenv)
 add_subdirectory(inttypes)
+add_subdirectory(libgen)
 add_subdirectory(link)
 add_subdirectory(math)
 add_subdirectory(netinet)
diff --git a/libc/src/__support/CPP/string_view.h b/libc/src/__support/CPP/string_view.h
index 6991fd46a4ace..7b98b7e5fb6c8 100644
--- a/libc/src/__support/CPP/string_view.h
+++ b/libc/src/__support/CPP/string_view.h
@@ -205,6 +205,15 @@ class string_view {
     return npos;
   }
 
+  LIBC_INLINE constexpr size_t find_last_not_of(const char c,
+                                                size_t end = npos) const {
+    end = end >= size() ? size() : end + 1;
+    for (; end > 0; --end)
+      if ((*this)[end - 1] != c)
+        return end - 1;
+    return npos;
+  }
+
   // Finds the first character not equal to c in this view, starting at
   // position From.
   LIBC_INLINE constexpr size_t find_first_not_of(const char c,
diff --git a/libc/src/libgen/CMakeLists.txt b/libc/src/libgen/CMakeLists.txt
new file mode 100644
index 0000000000000..9c315a4e7b41d
--- /dev/null
+++ b/libc/src/libgen/CMakeLists.txt
@@ -0,0 +1,23 @@
+add_entrypoint_object(
+  basename
+  SRCS
+    basename.cpp
+  HDRS
+    basename.h
+  DEPENDS
+    libc.src.__support.CPP.string_view
+    libc.src.__support.common
+    libc.src.__support.macros.config
+)
+
+add_entrypoint_object(
+  dirname
+  SRCS
+    dirname.cpp
+  HDRS
+    dirname.h
+  DEPENDS
+    libc.src.__support.CPP.string_view
+    libc.src.__support.common
+    libc.src.__support.macros.config
+)
diff --git a/libc/src/libgen/basename.cpp b/libc/src/libgen/basename.cpp
new file mode 100644
index 0000000000000..acf53f2d446e9
--- /dev/null
+++ b/libc/src/libgen/basename.cpp
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation of basename.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/libgen/basename.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 *, basename, (char *path)) {
+  if (path == nullptr || path[0] == '\0')
+    return const_cast<char *>(".");
+
+  cpp::string_view sv(path);
+  size_t last_non_slash = sv.find_last_not_of('/');
+
+  if (last_non_slash == cpp::string_view::npos)
+    return const_cast<char *>("/");
+
+  size_t last_slash = sv.substr(0, last_non_slash).find_last_of('/');
+
+  size_t start = (last_slash == cpp::string_view::npos) ? 0 : last_slash + 1;
+  size_t end = last_non_slash + 1;
+
+  if (end < sv.size())
+    path[end] = '\0';
+
+  return path + start;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/libgen/basename.h b/libc/src/libgen/basename.h
new file mode 100644
index 0000000000000..15239888aa97a
--- /dev/null
+++ b/libc/src/libgen/basename.h
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Header for basename.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_LIBGEN_BASENAME_H
+#define LLVM_LIBC_SRC_LIBGEN_BASENAME_H
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+/// Return the last component of a pathname.
+///
+/// \param path Pointer to the null-terminated pathname string.
+/// \return Pointer to the last component of path, or "." if path is null or
+/// empty, or "/" if path is all slashes.
+char *basename(char *path);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_LIBGEN_BASENAME_H
diff --git a/libc/src/libgen/dirname.cpp b/libc/src/libgen/dirname.cpp
new file mode 100644
index 0000000000000..9dd958b63ce9f
--- /dev/null
+++ b/libc/src/libgen/dirname.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation of dirname.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/libgen/dirname.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 *, dirname, (char *path)) {
+  if (path == nullptr || path[0] == '\0')
+    return const_cast<char *>(".");
+
+  cpp::string_view sv(path);
+  size_t last_non_slash = sv.find_last_not_of('/');
+
+  if (last_non_slash == cpp::string_view::npos)
+    return const_cast<char *>("/");
+
+  size_t last_slash = sv.substr(0, last_non_slash).find_last_of('/');
+
+  if (last_slash == cpp::string_view::npos)
+    return const_cast<char *>(".");
+
+  cpp::string_view dir_sv = sv.substr(0, last_slash);
+  size_t dir_last_non_slash = dir_sv.find_last_not_of('/');
+
+  if (dir_last_non_slash == cpp::string_view::npos) {
+    path[1] = '\0';
+    return path;
+  }
+
+  path[dir_last_non_slash + 1] = '\0';
+  return path;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/libgen/dirname.h b/libc/src/libgen/dirname.h
new file mode 100644
index 0000000000000..4909b2eb222ad
--- /dev/null
+++ b/libc/src/libgen/dirname.h
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Header for dirname.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_LIBGEN_DIRNAME_H
+#define LLVM_LIBC_SRC_LIBGEN_DIRNAME_H
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+/// Return the directory component of a pathname.
+///
+/// \param path Pointer to the null-terminated pathname string.
+/// \return Pointer to the directory component of path, or "." if path is null
+/// or empty, or "/" if path is all slashes.
+char *dirname(char *path);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_LIBGEN_DIRNAME_H
diff --git a/libc/test/src/CMakeLists.txt b/libc/test/src/CMakeLists.txt
index dd232b29a7a7b..45815c9bba8ca 100644
--- a/libc/test/src/CMakeLists.txt
+++ b/libc/test/src/CMakeLists.txt
@@ -63,6 +63,7 @@ add_subdirectory(complex)
 add_subdirectory(ctype)
 add_subdirectory(errno)
 add_subdirectory(fenv)
+add_subdirectory(libgen)
 add_subdirectory(link)
 add_subdirectory(math)
 add_subdirectory(netinet)
diff --git a/libc/test/src/libgen/CMakeLists.txt b/libc/test/src/libgen/CMakeLists.txt
new file mode 100644
index 0000000000000..d3ee13a9f1cab
--- /dev/null
+++ b/libc/test/src/libgen/CMakeLists.txt
@@ -0,0 +1,43 @@
+add_custom_target(libc-libgen-tests)
+
+add_libc_test(
+  basename_test
+  SUITE
+    libc-libgen-tests
+  SRCS
+    basename_test.cpp
+  DEPENDS
+    libc.src.libgen.basename
+)
+
+add_libc_test(
+  dirname_test
+  SUITE
+    libc-libgen-tests
+  SRCS
+    dirname_test.cpp
+  DEPENDS
+    libc.src.libgen.dirname
+)
+
+add_libc_test(
+  basename_death_test
+  UNIT_TEST_ONLY
+  SUITE
+    libc-libgen-tests
+  SRCS
+    basename_death_test.cpp
+  DEPENDS
+    libc.src.libgen.basename
+)
+
+add_libc_test(
+  dirname_death_test
+  UNIT_TEST_ONLY
+  SUITE
+    libc-libgen-tests
+  SRCS
+    dirname_death_test.cpp
+  DEPENDS
+    libc.src.libgen.dirname
+)
diff --git a/libc/test/src/libgen/basename_death_test.cpp b/libc/test/src/libgen/basename_death_test.cpp
new file mode 100644
index 0000000000000..24100dc48aa1b
--- /dev/null
+++ b/libc/test/src/libgen/basename_death_test.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Death tests for basename.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/libgen/basename.h"
+#include "test/UnitTest/Test.h"
+
+#ifdef ENABLE_SUBPROCESS_TESTS
+TEST(LlvmLibcBasenameTest, ModifyReturnValue) {
+  char *r = LIBC_NAMESPACE::basename(nullptr);
+  ASSERT_DEATH([r]() { r[0] = 'a'; }, WITH_SIGNAL(-1));
+}
+#endif
diff --git a/libc/test/src/libgen/basename_test.cpp b/libc/test/src/libgen/basename_test.cpp
new file mode 100644
index 0000000000000..2e8feef715137
--- /dev/null
+++ b/libc/test/src/libgen/basename_test.cpp
@@ -0,0 +1,62 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Unittests for basename.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/libgen/basename.h"
+#include "test/UnitTest/Test.h"
+
+TEST(LlvmLibcBasenameTest, NullPointer) {
+  ASSERT_STREQ(LIBC_NAMESPACE::basename(nullptr), ".");
+}
+
+TEST(LlvmLibcBasenameTest, EmptyString) {
+  char path[] = "";
+  ASSERT_STREQ(LIBC_NAMESPACE::basename(path), ".");
+}
+
+TEST(LlvmLibcBasenameTest, RegularPath) {
+  char path[] = "/usr/lib";
+  ASSERT_STREQ(LIBC_NAMESPACE::basename(path), "lib");
+}
+
+TEST(LlvmLibcBasenameTest, TrailingSlash) {
+  char path[] = "/usr/";
+  ASSERT_STREQ(LIBC_NAMESPACE::basename(path), "usr");
+  ASSERT_STREQ(path, "/usr");
+}
+
+TEST(LlvmLibcBasenameTest, SingleSlash) {
+  char path[] = "/";
+  ASSERT_STREQ(LIBC_NAMESPACE::basename(path), "/");
+}
+
+TEST(LlvmLibcBasenameTest, MultipleSlashes) {
+  char path[] = "///";
+  ASSERT_STREQ(LIBC_NAMESPACE::basename(path), "/");
+}
+
+TEST(LlvmLibcBasenameTest, SimpleName) {
+  char path[] = "a";
+  ASSERT_STREQ(LIBC_NAMESPACE::basename(path), "a");
+}
+
+TEST(LlvmLibcBasenameTest, SimpleNameTrailingSlash) {
+  char path[] = "a/";
+  ASSERT_STREQ(LIBC_NAMESPACE::basename(path), "a");
+  ASSERT_STREQ(path, "a");
+}
+
+TEST(LlvmLibcBasenameTest, ComplexPath) {
+  char path[] = "///a///";
+  ASSERT_STREQ(LIBC_NAMESPACE::basename(path), "a");
+  ASSERT_STREQ(path, "///a");
+}
diff --git a/libc/test/src/libgen/dirname_death_test.cpp b/libc/test/src/libgen/dirname_death_test.cpp
new file mode 100644
index 0000000000000..e135a4ce60cd6
--- /dev/null
+++ b/libc/test/src/libgen/dirname_death_test.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Death tests for dirname.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/libgen/dirname.h"
+#include "test/UnitTest/Test.h"
+
+#ifdef ENABLE_SUBPROCESS_TESTS
+TEST(LlvmLibcDirnameTest, ModifyReturnValue) {
+  char *r = LIBC_NAMESPACE::dirname(nullptr);
+  ASSERT_DEATH([r]() { r[0] = 'a'; }, WITH_SIGNAL(-1));
+}
+#endif
diff --git a/libc/test/src/libgen/dirname_test.cpp b/libc/test/src/libgen/dirname_test.cpp
new file mode 100644
index 0000000000000..afd718fb6e559
--- /dev/null
+++ b/libc/test/src/libgen/dirname_test.cpp
@@ -0,0 +1,74 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Unittests for dirname.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/libgen/dirname.h"
+#include "test/UnitTest/Test.h"
+
+TEST(LlvmLibcDirnameTest, NullPointer) {
+  ASSERT_STREQ(LIBC_NAMESPACE::dirname(nullptr), ".");
+}
+
+TEST(LlvmLibcDirnameTest, EmptyString) {
+  char path[] = "";
+  ASSERT_STREQ(LIBC_NAMESPACE::dirname(path), ".");
+}
+
+TEST(LlvmLibcDirnameTest, RegularPath) {
+  char path[] = "/usr/lib";
+  ASSERT_STREQ(LIBC_NAMESPACE::dirname(path), "/usr");
+  ASSERT_STREQ(path, "/usr");
+}
+
+TEST(LlvmLibcDirnameTest, TrailingSlash) {
+  char path[] = "/usr/";
+  ASSERT_STREQ(LIBC_NAMESPACE::dirname(path), "/");
+  ASSERT_STREQ(path, "/");
+}
+
+TEST(LlvmLibcDirnameTest, SingleSlash) {
+  char path[] = "/";
+  ASSERT_STREQ(LIBC_NAMESPACE::dirname(path), "/");
+}
+
+TEST(LlvmLibcDirnameTest, MultipleSlashes) {
+  char path[] = "///";
+  ASSERT_STREQ(LIBC_NAMESPACE::dirname(path), "/");
+}
+
+TEST(LlvmLibcDirnameTest, SimpleName) {
+  char path[] = "a";
+  ASSERT_STREQ(LIBC_NAMESPACE::dirname(path), ".");
+}
+
+TEST(LlvmLibcDirnameTest, SimpleNameTrailingSlash) {
+  char path[] = "a/";
+  ASSERT_STREQ(LIBC_NAMESPACE::dirname(path), ".");
+}
+
+TEST(LlvmLibcDirnameTest, ComplexPath) {
+  char path[] = "///a///b///";
+  ASSERT_STREQ(LIBC_NAMESPACE::dirname(path), "///a");
+  ASSERT_STREQ(path, "///a");
+}
+
+TEST(LlvmLibcDirnameTest, SlashA) {
+  char path[] = "/a";
+  ASSERT_STREQ(LIBC_NAMESPACE::dirname(path), "/");
+  ASSERT_STREQ(path, "/");
+}
+
+TEST(LlvmLibcDirnameTest, MultipleSlashesA) {
+  char path[] = "///a";
+  ASSERT_STREQ(LIBC_NAMESPACE::dirname(path), "/");
+  ASSERT_STREQ(path, "/");
+}
diff --git a/libc/utils/docgen/libgen.yaml b/libc/utils/docgen/libgen.yaml
new file mode 100644
index 0000000000000..07aad5f1be55c
--- /dev/null
+++ b/libc/utils/docgen/libgen.yaml
@@ -0,0 +1,5 @@
+functions:
+  basename:
+    in-latest-posix: ''
+  dirname:
+    in-latest-posix: ''

>From 998932bc61643e03a6bc7cc7c2b330c7afac30d9 Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jbailey at raspberryginger.com>
Date: Tue, 23 Jun 2026 13:54:49 +0100
Subject: [PATCH 2/2] [libc] Disable libgen death tests under sanitizers

Death tests (which write to read-only memory to trigger SIGSEGV) are
not compatible with sanitizers (like ASan) because the sanitizer
intercepts the signal and exits, causing the death test to fail.

Disable these death tests when LLVM_USE_SANITIZER is enabled.

Assisted-by: Automated tooling, human reviewed.
---
 libc/test/src/libgen/CMakeLists.txt | 42 +++++++++++++++--------------
 1 file changed, 22 insertions(+), 20 deletions(-)

diff --git a/libc/test/src/libgen/CMakeLists.txt b/libc/test/src/libgen/CMakeLists.txt
index d3ee13a9f1cab..85c28b0764f5c 100644
--- a/libc/test/src/libgen/CMakeLists.txt
+++ b/libc/test/src/libgen/CMakeLists.txt
@@ -20,24 +20,26 @@ add_libc_test(
     libc.src.libgen.dirname
 )
 
-add_libc_test(
-  basename_death_test
-  UNIT_TEST_ONLY
-  SUITE
-    libc-libgen-tests
-  SRCS
-    basename_death_test.cpp
-  DEPENDS
-    libc.src.libgen.basename
-)
+if (NOT LLVM_USE_SANITIZER)
+  add_libc_test(
+    basename_death_test
+    UNIT_TEST_ONLY
+    SUITE
+      libc-libgen-tests
+    SRCS
+      basename_death_test.cpp
+    DEPENDS
+      libc.src.libgen.basename
+  )
 
-add_libc_test(
-  dirname_death_test
-  UNIT_TEST_ONLY
-  SUITE
-    libc-libgen-tests
-  SRCS
-    dirname_death_test.cpp
-  DEPENDS
-    libc.src.libgen.dirname
-)
+  add_libc_test(
+    dirname_death_test
+    UNIT_TEST_ONLY
+    SUITE
+      libc-libgen-tests
+    SRCS
+      dirname_death_test.cpp
+    DEPENDS
+      libc.src.libgen.dirname
+  )
+endif()



More information about the libc-commits mailing list