diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index 8035b964d1a5d..edad30634b6da 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -384,6 +384,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.wchar.wcscat libc.src.wchar.wcsstr libc.src.wchar.wcsncat + libc.src.wchar.wcslcat libc.src.wchar.wcscpy libc.src.wchar.wcslcpy libc.src.wchar.wmemchr diff --git a/libc/include/wchar.yaml b/libc/include/wchar.yaml index 7d17d84d16c11..c6488fa937885 100644 --- a/libc/include/wchar.yaml +++ b/libc/include/wchar.yaml @@ -166,6 +166,14 @@ functions: arguments: - type: wchar_t *__restrict - type: const wchar_t *__restrict + - name: wcslcat + standards: + - stdc + return_type: size_t + arguments: + - type: wchar_t *__restrict + - type: const wchar_t *__restrict + - type: size_t - name: wcsstr standards: - stdc diff --git a/libc/src/wchar/CMakeLists.txt b/libc/src/wchar/CMakeLists.txt index 2dc14db998766..e3bd357a8fd1f 100644 --- a/libc/src/wchar/CMakeLists.txt +++ b/libc/src/wchar/CMakeLists.txt @@ -307,6 +307,18 @@ add_entrypoint_object( libc.src.string.string_utils ) +add_entrypoint_object( + wcslcat + SRCS + wcslcat.cpp + HDRS + wcslcat.h + DEPENDS + libc.hdr.types.size_t + libc.hdr.wchar_macros + libc.src.string.string_utils +) + add_entrypoint_object( wmemchr SRCS diff --git a/libc/src/wchar/wcslcat.cpp b/libc/src/wchar/wcslcat.cpp new file mode 100644 index 0000000000000..eb318e066f7a0 --- /dev/null +++ b/libc/src/wchar/wcslcat.cpp @@ -0,0 +1,39 @@ +//===-- Implementation of wcslcat -----------------------------------------===// +// +// 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/wchar/wcslcat.h" + +#include "hdr/types/size_t.h" +#include "hdr/types/wchar_t.h" +#include "src/__support/common.h" +#include "src/__support/macros/config.h" +#include "src/string/string_utils.h" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(size_t, wcslcat, + (wchar_t *__restrict dst, const wchar_t *__restrict src, + size_t dstsize)) { + const size_t dstlen = internal::string_length(dst); + const size_t srclen = internal::string_length(src); + int limit = static_cast(dstsize - dstlen - 1); + size_t returnval = (dstsize < dstlen ? dstsize : dstlen) + srclen; + if (limit < 0) + return returnval; + int i = 0; + for (; i < limit && src[i] != L'\0'; ++i) { + dst[dstlen + i] = src[i]; + } + + // appending null terminator if there is room + if (dstlen + i < dstlen + dstsize) + dst[dstlen + i] = L'\0'; + return returnval; +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/wchar/wcslcat.h b/libc/src/wchar/wcslcat.h new file mode 100644 index 0000000000000..fd964e1af49d5 --- /dev/null +++ b/libc/src/wchar/wcslcat.h @@ -0,0 +1,23 @@ +//===-- Implementation header for wcslcat ---------------------------------===// +// +// 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_WCHAR_WCSLCAT_H +#define LLVM_LIBC_SRC_WCHAR_WCSLCAT_H + +#include "hdr/types/size_t.h" +#include "hdr/types/wchar_t.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +size_t wcslcat(wchar_t *__restrict dst, const wchar_t *__restrict src, + size_t dstsize); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_WCHAR_WCSLCAT_H diff --git a/libc/test/src/wchar/CMakeLists.txt b/libc/test/src/wchar/CMakeLists.txt index aea520feee504..dc233fca59741 100644 --- a/libc/test/src/wchar/CMakeLists.txt +++ b/libc/test/src/wchar/CMakeLists.txt @@ -283,6 +283,17 @@ add_libc_test( libc.src.wchar.wcsncat ) +add_libc_test( + wcslcat_test + SUITE + libc_wchar_unittests + SRCS + wcslcat_test.cpp + DEPENDS + libc.src.wchar.wcslcat + libc.hdr.types.size_t +) + add_libc_test( wcscpy_test SUITE diff --git a/libc/test/src/wchar/wcslcat_test.cpp b/libc/test/src/wchar/wcslcat_test.cpp new file mode 100644 index 0000000000000..bad37496384e2 --- /dev/null +++ b/libc/test/src/wchar/wcslcat_test.cpp @@ -0,0 +1,57 @@ +//===-- Unittests for wcslcat ---------------------------------------------===// +// +// 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 "hdr/types/size_t.h" +#include "hdr/types/wchar_t.h" +#include "src/wchar/wcslcat.h" +#include "test/UnitTest/Test.h" + +TEST(LlvmLibcWCSLCatTest, TooBig) { + const wchar_t *src = L"cd"; + wchar_t dst[4]{L"ab"}; + size_t res = LIBC_NAMESPACE::wcslcat(dst, src, 3); + ASSERT_TRUE(dst[0] == L'a'); + ASSERT_TRUE(dst[1] == L'b'); + ASSERT_TRUE(dst[2] == L'\0'); + // Should still return src length + dst length + ASSERT_EQ(res, size_t(4)); + // Not enough space to copy d + res = LIBC_NAMESPACE::wcslcat(dst, src, 4); + ASSERT_TRUE(dst[0] == L'a'); + ASSERT_TRUE(dst[1] == L'b'); + ASSERT_TRUE(dst[2] == L'c'); + ASSERT_TRUE(dst[3] == L'\0'); + ASSERT_EQ(res, size_t(4)); +} + +TEST(LlvmLibcWCSLCatTest, Smaller) { + const wchar_t *src = L"cd"; + wchar_t dst[7]{L"ab"}; + size_t res = LIBC_NAMESPACE::wcslcat(dst, src, 7); + ASSERT_TRUE(dst[0] == L'a'); + ASSERT_TRUE(dst[1] == L'b'); + ASSERT_TRUE(dst[2] == L'c'); + ASSERT_TRUE(dst[3] == L'd'); + ASSERT_TRUE(dst[4] == L'\0'); + ASSERT_EQ(res, size_t(4)); +} + +TEST(LlvmLibcWCSLCatTest, SmallerNoOverwriteAfter0) { + const wchar_t *src = L"cd"; + wchar_t dst[8]{L"ab\0\0efg"}; + size_t res = LIBC_NAMESPACE::wcslcat(dst, src, 8); + ASSERT_TRUE(dst[0] == L'a'); + ASSERT_TRUE(dst[1] == L'b'); + ASSERT_TRUE(dst[2] == L'c'); + ASSERT_TRUE(dst[3] == L'd'); + ASSERT_TRUE(dst[4] == L'\0'); + ASSERT_TRUE(dst[5] == L'f'); + ASSERT_TRUE(dst[6] == L'g'); + ASSERT_TRUE(dst[7] == L'\0'); + ASSERT_EQ(res, size_t(4)); +} diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel index 99e7764794706..b13a909770e58 100644 --- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel @@ -5829,6 +5829,19 @@ libc_function( ], ) +libc_function( + name = "wcslcat", + srcs = ["src/wchar/wcslcat.cpp"], + hdrs = ["src/wchar/wcslcat.h"], + deps = [ + ":__support_common", + ":__support_macros_config", + ":string_utils", + ":types_size_t", + ":types_wchar_t", + ], +) + libc_function( name = "wcslcpy", srcs = ["src/wchar/wcslcpy.cpp"], diff --git a/utils/bazel/llvm-project-overlay/libc/test/src/wchar/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/test/src/wchar/BUILD.bazel index 431a7ecb8bfe8..b6d6b8f778a4a 100644 --- a/utils/bazel/llvm-project-overlay/libc/test/src/wchar/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/libc/test/src/wchar/BUILD.bazel @@ -83,6 +83,17 @@ libc_test( ], ) + +libc_test( + name = "wcslcat_test", + srcs = ["wcslcat_test.cpp"], + deps = [ + "//libc:types_size_t", + "//libc:types_wchar_t", + "//libc:wcslcat", + ], +) + libc_test( name = "wcslcpy_test", srcs = ["wcslcpy_test.cpp"],