diff --git a/libc/config/darwin/arm/entrypoints.txt b/libc/config/darwin/arm/entrypoints.txt --- a/libc/config/darwin/arm/entrypoints.txt +++ b/libc/config/darwin/arm/entrypoints.txt @@ -35,6 +35,7 @@ libc.src.string.strcmp libc.src.string.strcpy libc.src.string.strcspn + libc.src.string.strlcat libc.src.string.strlcpy libc.src.string.strlen libc.src.string.strncat diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -40,6 +40,7 @@ libc.src.string.strcmp libc.src.string.strcpy libc.src.string.strcspn + libc.src.string.strlcat libc.src.string.strlcpy libc.src.string.strlen libc.src.string.strncat diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -40,6 +40,7 @@ libc.src.string.strcmp libc.src.string.strcpy libc.src.string.strcspn + libc.src.string.strlcat libc.src.string.strlcpy libc.src.string.strlen libc.src.string.strncat diff --git a/libc/config/windows/entrypoints.txt b/libc/config/windows/entrypoints.txt --- a/libc/config/windows/entrypoints.txt +++ b/libc/config/windows/entrypoints.txt @@ -35,6 +35,7 @@ libc.src.string.strcmp libc.src.string.strcpy libc.src.string.strcspn + libc.src.string.strlcat libc.src.string.strlcpy libc.src.string.strlen libc.src.string.strncat diff --git a/libc/spec/bsd_ext.td b/libc/spec/bsd_ext.td --- a/libc/spec/bsd_ext.td +++ b/libc/spec/bsd_ext.td @@ -5,6 +5,11 @@ [], // Types [], // Enumerations [ + FunctionSpec< + "strlcat", + RetValSpec, + [ArgSpec, ArgSpec, ArgSpec] + >, FunctionSpec< "strlcpy", RetValSpec, diff --git a/libc/src/string/CMakeLists.txt b/libc/src/string/CMakeLists.txt --- a/libc/src/string/CMakeLists.txt +++ b/libc/src/string/CMakeLists.txt @@ -6,6 +6,8 @@ string_utils.h DEPENDS libc.src.__support.CPP.bitset + .memory_utils.memcpy_implementation + .memory_utils.memset_implementation ) add_entrypoint_object( @@ -126,6 +128,17 @@ libc.include.stdlib ) +add_entrypoint_object( + strlcat + SRCS + strlcat.cpp + HDRS + strlcat.h + DEPENDS + .string_utils + libc.include.string +) + add_entrypoint_object( strlcpy SRCS @@ -133,8 +146,6 @@ HDRS strlcpy.h DEPENDS - .memory_utils.memcpy_implementation - .memory_utils.memset_implementation .string_utils libc.include.string ) diff --git a/libc/src/string/string_utils.h b/libc/src/string/string_utils.h --- a/libc/src/string/string_utils.h +++ b/libc/src/string/string_utils.h @@ -11,6 +11,8 @@ #include "src/__support/CPP/Bitset.h" #include "src/__support/common.h" +#include "src/string/memory_utils/memcpy_implementations.h" +#include "src/string/memory_utils/memset_implementations.h" #include // size_t namespace __llvm_libc { @@ -85,6 +87,17 @@ return token; } +static inline size_t strlcpy(char *__restrict dst, const char *__restrict src, + size_t size) { + size_t len = internal::string_length(src); + if (!size) + return len; + size_t n = len < size - 1 ? len : size - 1; + inline_memcpy(dst, src, n); + inline_memset(dst + n, 0, size - n); + return len; +} + } // namespace internal } // namespace __llvm_libc diff --git a/libc/src/string/strlcat.h b/libc/src/string/strlcat.h new file mode 100644 --- /dev/null +++ b/libc/src/string/strlcat.h @@ -0,0 +1,20 @@ +//===-- Implementation header for strlcat -----------------------*- 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_STRING_STRLCAT_H +#define LLVM_LIBC_SRC_STRING_STRLCAT_H + +#include + +namespace __llvm_libc { + +size_t strlcat(char *__restrict dst, const char *__restrict src, size_t size); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STRING_STRLCAT_H diff --git a/libc/src/string/strlcpy.cpp b/libc/src/string/strlcat.cpp copy from libc/src/string/strlcpy.cpp copy to libc/src/string/strlcat.cpp --- a/libc/src/string/strlcpy.cpp +++ b/libc/src/string/strlcat.cpp @@ -1,4 +1,4 @@ -//===-- Implementation of strlcpy -----------------------------------------===// +//===-- Implementation of strlcat -----------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,26 +6,22 @@ // //===----------------------------------------------------------------------===// -#include "src/string/strlcpy.h" -#include "src/string/bzero.h" -#include "src/string/memory_utils/memcpy_implementations.h" -#include "src/string/memory_utils/memset_implementations.h" +#include "src/string/strlcat.h" #include "src/string/string_utils.h" #include "src/__support/common.h" namespace __llvm_libc { -LLVM_LIBC_FUNCTION(size_t, strlcpy, +LLVM_LIBC_FUNCTION(size_t, strlcat, (char *__restrict dst, const char *__restrict src, size_t size)) { - size_t len = internal::string_length(src); - if (!size) - return len; - size_t n = len < size - 1 ? len : size - 1; - inline_memcpy(dst, src, n); - inline_memset(dst + n, 0, size - n); - return len; + char *new_dst = reinterpret_cast(internal::find_first_character( + reinterpret_cast(dst), 0, size)); + if (!new_dst) + return size + internal::string_length(src); + size_t first_len = new_dst - dst; + return first_len + internal::strlcpy(new_dst, src, size - first_len); } } // namespace __llvm_libc diff --git a/libc/src/string/strlcpy.cpp b/libc/src/string/strlcpy.cpp --- a/libc/src/string/strlcpy.cpp +++ b/libc/src/string/strlcpy.cpp @@ -7,9 +7,6 @@ //===----------------------------------------------------------------------===// #include "src/string/strlcpy.h" -#include "src/string/bzero.h" -#include "src/string/memory_utils/memcpy_implementations.h" -#include "src/string/memory_utils/memset_implementations.h" #include "src/string/string_utils.h" #include "src/__support/common.h" @@ -19,13 +16,7 @@ LLVM_LIBC_FUNCTION(size_t, strlcpy, (char *__restrict dst, const char *__restrict src, size_t size)) { - size_t len = internal::string_length(src); - if (!size) - return len; - size_t n = len < size - 1 ? len : size - 1; - inline_memcpy(dst, src, n); - inline_memset(dst + n, 0, size - n); - return len; + return internal::strlcpy(dst, src, size); } } // namespace __llvm_libc diff --git a/libc/test/src/string/CMakeLists.txt b/libc/test/src/string/CMakeLists.txt --- a/libc/test/src/string/CMakeLists.txt +++ b/libc/test/src/string/CMakeLists.txt @@ -123,6 +123,16 @@ libc.src.string.strdup ) +add_libc_unittest( + strlcat_test + SUITE + libc_string_unittests + SRCS + strlcat_test.cpp + DEPENDS + libc.src.string.strlcat +) + add_libc_unittest( strlcpy_test SUITE diff --git a/libc/test/src/string/strlcat_test.cpp b/libc/test/src/string/strlcat_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/string/strlcat_test.cpp @@ -0,0 +1,37 @@ +//===-- Unittests for strlcat ---------------------------------------------===// +// +// 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/string/strlcat.h" +#include "utils/UnitTest/Test.h" +#include + +TEST(LlvmLibcStrlcatTest, TooBig) { + const char *str = "cd"; + char buf[4]{"ab"}; + EXPECT_EQ(__llvm_libc::strlcat(buf, str, 3), size_t(4)); + EXPECT_STREQ(buf, "ab"); + EXPECT_EQ(__llvm_libc::strlcat(buf, str, 4), size_t(4)); + EXPECT_STREQ(buf, "abc"); +} + +TEST(LlvmLibcStrlcatTest, Smaller) { + const char *str = "cd"; + char buf[7]{"ab"}; + + EXPECT_EQ(__llvm_libc::strlcat(buf, str, 7), size_t(4)); + EXPECT_STREQ(buf, "abcd"); +} + +TEST(LlvmLibcStrlcatTest, No0) { + const char *str = "cd"; + char buf[7]{"ab"}; + EXPECT_EQ(__llvm_libc::strlcat(buf, str, 1), size_t(3)); + EXPECT_STREQ(buf, "ab"); + EXPECT_EQ(__llvm_libc::strlcat(buf, str, 2), size_t(4)); + EXPECT_STREQ(buf, "ab"); +}