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.strlcpy libc.src.string.strlen libc.src.string.strncat libc.src.string.strncmp 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.strlcpy libc.src.string.strlen libc.src.string.strncat libc.src.string.strncmp diff --git a/libc/config/linux/api.td b/libc/config/linux/api.td --- a/libc/config/linux/api.td +++ b/libc/config/linux/api.td @@ -1,5 +1,6 @@ include "config/public_api.td" +include "spec/bsd_ext.td" include "spec/gnu_ext.td" include "spec/linux.td" include "spec/llvm_libc_ext.td" 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.strlcpy libc.src.string.strlen libc.src.string.strncat libc.src.string.strncmp 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.strlcpy libc.src.string.strlen libc.src.string.strncat libc.src.string.strncmp diff --git a/libc/spec/bsd_ext.td b/libc/spec/bsd_ext.td new file mode 100644 --- /dev/null +++ b/libc/spec/bsd_ext.td @@ -0,0 +1,15 @@ +def BsdExtensions : StandardSpec<"BSDExtensions"> { + HeaderSpec String = HeaderSpec< + "string.h", + [], // Macros + [], // Types + [], // Enumerations + [ + FunctionSpec< + "strlcpy", + RetValSpec, + [ArgSpec, ArgSpec, ArgSpec] + >, + ] + >; +} 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 @@ -126,6 +126,19 @@ libc.include.stdlib ) +add_entrypoint_object( + strlcpy + SRCS + strlcpy.cpp + HDRS + strlcpy.h + DEPENDS + .memory_utils.memcpy_implementation + .memory_utils.memset_implementation + .string_utils + libc.include.string +) + add_entrypoint_object( strlen SRCS diff --git a/libc/src/string/strlcpy.h b/libc/src/string/strlcpy.h new file mode 100644 --- /dev/null +++ b/libc/src/string/strlcpy.h @@ -0,0 +1,20 @@ +//===-- Implementation header for strlcpy -----------------------*- 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_STRLCPY_H +#define LLVM_LIBC_SRC_STRING_STRLCPY_H + +#include + +namespace __llvm_libc { + +size_t strlcpy(char *__restrict dst, const char *__restrict src, size_t size); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STRING_STRLCPY_H diff --git a/libc/src/string/strlcpy.cpp b/libc/src/string/strlcpy.cpp new file mode 100644 --- /dev/null +++ b/libc/src/string/strlcpy.cpp @@ -0,0 +1,31 @@ +//===-- Implementation of strlcpy -----------------------------------------===// +// +// 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/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" + +namespace __llvm_libc { + +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; +} + +} // 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( + strlcpy_test + SUITE + libc_string_unittests + SRCS + strlcpy_test.cpp + DEPENDS + libc.src.string.strlcpy +) + add_libc_unittest( strlen_test SUITE diff --git a/libc/test/src/string/strlcpy_test.cpp b/libc/test/src/string/strlcpy_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/string/strlcpy_test.cpp @@ -0,0 +1,30 @@ +//===-- Unittests for strlcpy ---------------------------------------------===// +// +// 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/strlcpy.h" +#include "utils/UnitTest/Test.h" +#include + +TEST(LlvmLibcStrlcpyTest, TooBig) { + const char *str = "abc"; + char buf[2]; + EXPECT_EQ(__llvm_libc::strlcpy(buf, str, 2), 3ul); + EXPECT_STREQ(buf, "a"); + + EXPECT_EQ(__llvm_libc::strlcpy(nullptr, str, 0), 3ul); +} + +TEST(LlvmLibcStrlcpyTest, Smaller) { + const char *str = "abc"; + char buf[7]{"111111"}; + + EXPECT_EQ(__llvm_libc::strlcpy(buf, str, 7), 3ul); + EXPECT_STREQ(buf, "abc"); + for (const char *p = buf + 3; p < buf + 7; p++) + EXPECT_EQ(*p, '\0'); +}