diff --git a/libc/config/baremetal/arm/entrypoints.txt b/libc/config/baremetal/arm/entrypoints.txt index 694cd7b1993ca..fa609faa8cd85 100644 --- a/libc/config/baremetal/arm/entrypoints.txt +++ b/libc/config/baremetal/arm/entrypoints.txt @@ -206,6 +206,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.time.asctime_r libc.src.time.ctime libc.src.time.ctime_r + libc.src.time.ctime_s libc.src.time.difftime libc.src.time.gmtime libc.src.time.gmtime_r diff --git a/libc/config/baremetal/riscv/entrypoints.txt b/libc/config/baremetal/riscv/entrypoints.txt index 6dc5df830eb00..68a348ef881a1 100644 --- a/libc/config/baremetal/riscv/entrypoints.txt +++ b/libc/config/baremetal/riscv/entrypoints.txt @@ -202,6 +202,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.time.asctime_r libc.src.time.ctime libc.src.time.ctime_r + libc.src.time.ctime_s libc.src.time.difftime libc.src.time.gmtime libc.src.time.gmtime_r diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt index 00f0c6a8bfb8e..21edf9a155a62 100644 --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -1010,6 +1010,7 @@ if(LLVM_LIBC_FULL_BUILD) libc.src.time.asctime_r libc.src.time.ctime libc.src.time.ctime_r + libc.src.time.ctime_s libc.src.time.clock libc.src.time.clock_gettime libc.src.time.difftime diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt index 49a8d61b93802..5fcbf8aa18125 100644 --- a/libc/config/linux/riscv/entrypoints.txt +++ b/libc/config/linux/riscv/entrypoints.txt @@ -941,6 +941,7 @@ if(LLVM_LIBC_FULL_BUILD) libc.src.time.asctime_r libc.src.time.ctime libc.src.time.ctime_r + libc.src.time.ctime_s libc.src.time.clock libc.src.time.clock_gettime libc.src.time.difftime diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index 7e549607716c0..d9e7242acdf48 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -1095,6 +1095,7 @@ if(LLVM_LIBC_FULL_BUILD) libc.src.time.asctime_r libc.src.time.ctime libc.src.time.ctime_r + libc.src.time.ctime_s libc.src.time.clock libc.src.time.clock_gettime libc.src.time.difftime diff --git a/libc/docs/headers/time.rst b/libc/docs/headers/time.rst index de82d80a2bec4..ea5ee515a9bba 100644 --- a/libc/docs/headers/time.rst +++ b/libc/docs/headers/time.rst @@ -59,6 +59,8 @@ Implementation Status +---------------------+---------+---------+---------+-----------------+---------+---------+---------+---------+---------+---------+---------+---------+ | ctime_r | |check| | |check| | | |check| | | | | | | | | | +---------------------+---------+---------+---------+-----------------+---------+---------+---------+---------+---------+---------+---------+---------+ +| ctime_s | |check| | |check| | | |check| | | | | | | | | | ++---------------------+---------+---------+---------+-----------------+---------+---------+---------+---------+---------+---------+---------+---------+ | clock | |check| | |check| | | |check| | | | | | | | | | +---------------------+---------+---------+---------+-----------------+---------+---------+---------+---------+---------+---------+---------+---------+ | clock_getcpuclockid | | | | | | | | | | | | | diff --git a/libc/hdr/types/errno_t.h b/libc/hdr/types/errno_t.h new file mode 100644 index 0000000000000..ae3a63648e082 --- /dev/null +++ b/libc/hdr/types/errno_t.h @@ -0,0 +1,22 @@ +//===-- Proxy for errno_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_HDR_TYPES_ERRNO_T_H +#define LLVM_LIBC_HDR_TYPES_ERRNO_T_H + +#ifdef LIBC_FULL_BUILD + +#include "include/llvm-libc-types/errno_t.h" + +#else + +#define __STDC_WANT_LIB_EXT1__ 1 +#include + +#endif // LIBC_FULL_BUILD + +#endif // LLVM_LIBC_HDR_TYPES_ERRNO_T_H diff --git a/libc/hdr/types/rsize_t.h b/libc/hdr/types/rsize_t.h new file mode 100644 index 0000000000000..6b441b495ad41 --- /dev/null +++ b/libc/hdr/types/rsize_t.h @@ -0,0 +1,23 @@ +//===-- Proxy for rsize_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_HDR_TYPES_RSIZE_T_H +#define LLVM_LIBC_HDR_TYPES_RSIZE_T_H + +#ifdef LIBC_FULL_BUILD + +#include "include/llvm-libc-types/rsize_t.h" + +#else + +#define __need_rsize_t +#include +#undef __need_rsize_t + +#endif // LIBC_FULL_BUILD + +#endif // LLVM_LIBC_HDR_TYPES_RSIZE_T_H diff --git a/libc/include/llvm-libc-types/errno_t.h b/libc/include/llvm-libc-types/errno_t.h new file mode 100644 index 0000000000000..ee1cf34e1b830 --- /dev/null +++ b/libc/include/llvm-libc-types/errno_t.h @@ -0,0 +1,14 @@ +//===-- Definition of errno_t type ----------------------------------------===// +// +// 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_ERRNO_T_H +#define LLVM_LIBC_TYPES_ERRNO_T_H + +typedef int errno_t; + +#endif // LLVM_LIBC_TYPES_ERRNO_T_H diff --git a/libc/include/llvm-libc-types/rsize_t.h b/libc/include/llvm-libc-types/rsize_t.h new file mode 100644 index 0000000000000..c0ef39e452fb9 --- /dev/null +++ b/libc/include/llvm-libc-types/rsize_t.h @@ -0,0 +1,16 @@ +//===-- Definition of rsize_t type ----------------------------------------===// +// +// 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_RSIZE_T_H +#define LLVM_LIBC_TYPES_RSIZE_T_H + +#include "size_t.h" + +typedef size_t rsize_t; + +#endif // LLVM_LIBC_TYPES_RSIZE_T_H diff --git a/libc/include/time.h.def b/libc/include/time.h.def index 2355e8822fad7..c745a4317504a 100644 --- a/libc/include/time.h.def +++ b/libc/include/time.h.def @@ -12,6 +12,10 @@ #include "__llvm-libc-common.h" #include "llvm-libc-macros/time-macros.h" +#ifdef __STDC_WANT_LIB_EXT1__ +#include // errno_t +#endif + %%public_api() #endif // LLVM_LIBC_TIME_H diff --git a/libc/include/time.yaml b/libc/include/time.yaml index b71b9ab72075b..f99387f5d5ede 100644 --- a/libc/include/time.yaml +++ b/libc/include/time.yaml @@ -38,6 +38,15 @@ functions: arguments: - type: const time_t * - type: char * + - name: ctime_s + standard: + - stdc + return_type: errno_t + arguments: + - type: char * + - type: rsize_t + - type: const time_t * + guard: __STDC_WANT_LIB_EXT1__ - name: clock standard: - stdc diff --git a/libc/src/time/CMakeLists.txt b/libc/src/time/CMakeLists.txt index ef9bfe57bc4ec..92c85d760c3ee 100644 --- a/libc/src/time/CMakeLists.txt +++ b/libc/src/time/CMakeLists.txt @@ -81,6 +81,18 @@ add_entrypoint_object( libc.include.time ) +add_entrypoint_object( + ctime_s + SRCS + ctime_s.cpp + HDRS + ctime_s.h + DEPENDS + .time_utils + libc.hdr.types.time_t + libc.include.time +) + add_entrypoint_object( difftime SRCS diff --git a/libc/src/time/ctime_s.cpp b/libc/src/time/ctime_s.cpp new file mode 100644 index 0000000000000..b555ffbc18d86 --- /dev/null +++ b/libc/src/time/ctime_s.cpp @@ -0,0 +1,45 @@ +//===-- Implementation of ctime_s function --------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#define __STDC_WANT_LIB_EXT1__ 1 + +#include "ctime_s.h" +#include "hdr/errno_macros.h" +#include "hdr/types/errno_t.h" +#include "hdr/types/rsize_t.h" +#include "src/__support/CPP/limits.h" +#include "src/__support/common.h" +#include "src/__support/macros/config.h" +#include "time_utils.h" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(errno_t, ctime_s, + (char *buffer, rsize_t buffer_size, const time_t *t_ptr)) { + // TODO (https://github.com/llvm/llvm-project/issues/115907): invoke + // constraint handler + if (buffer == nullptr || t_ptr == nullptr) + return EINVAL; + + if (buffer_size < time_constants::ASCTIME_MAX_BYTES || + buffer_size > RSIZE_MAX) { + buffer[0] = '\0'; + return ERANGE; + } + + if (*t_ptr > cpp::numeric_limits::max()) + return EINVAL; + + if (time_utils::asctime(time_utils::localtime(t_ptr), buffer, buffer_size) == + nullptr) + return EINVAL; + + return 0; +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/time/ctime_s.h b/libc/src/time/ctime_s.h new file mode 100644 index 0000000000000..133f31293d98a --- /dev/null +++ b/libc/src/time/ctime_s.h @@ -0,0 +1,23 @@ +//===-- Implementation header of ctime_s ------------------------*- 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_TIME_CTIME_S_H +#define LLVM_LIBC_SRC_TIME_CTIME_S_H + +#include "hdr/types/errno_t.h" +#include "hdr/types/rsize_t.h" +#include "hdr/types/time_t.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +errno_t ctime_s(char *buffer, rsize_t buffer_size, const time_t *t_ptr); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_TIME_CTIME_S_H diff --git a/libc/test/src/time/CMakeLists.txt b/libc/test/src/time/CMakeLists.txt index 12add224f386a..1a864f3ee0ef2 100644 --- a/libc/test/src/time/CMakeLists.txt +++ b/libc/test/src/time/CMakeLists.txt @@ -72,6 +72,22 @@ add_libc_unittest( libc.hdr.types.struct_tm ) +add_libc_unittest( + ctime_s_test + SUITE + libc_time_unittests + SRCS + ctime_s_test.cpp + HDRS + TmHelper.h + TmMatcher.h + DEPENDS + libc.include.time + libc.hdr.types.time_t + libc.src.time.ctime_s + libc.src.time.time_utils +) + add_libc_test( clock_gettime_test SUITE diff --git a/libc/test/src/time/ctime_s_test.cpp b/libc/test/src/time/ctime_s_test.cpp new file mode 100644 index 0000000000000..39860b9dd3585 --- /dev/null +++ b/libc/test/src/time/ctime_s_test.cpp @@ -0,0 +1,67 @@ +//===-- Unittests for ctime_s ---------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#define __STDC_WANT_LIB_EXT1__ 1 + +#include "hdr/errno_macros.h" +#include "hdr/types/errno_t.h" +#include "hdr/types/rsize_t.h" +#include "src/errno/libc_errno.h" +#include "src/time/ctime_s.h" +#include "src/time/time_utils.h" +#include "test/UnitTest/Test.h" +#include "test/src/time/TmHelper.h" + +TEST(LlvmLibcCtimeS, Nullptr) { + errno_t result = LIBC_NAMESPACE::ctime_s(nullptr, 0, nullptr); + ASSERT_EQ(EINVAL, result); + + char buffer[LIBC_NAMESPACE::time_constants::ASCTIME_BUFFER_SIZE]; + result = LIBC_NAMESPACE::ctime_s(buffer, sizeof(buffer), nullptr); + ASSERT_EQ(EINVAL, result); + + time_t t; + result = LIBC_NAMESPACE::ctime_s(nullptr, 0, &t); + ASSERT_EQ(EINVAL, result); +} + +TEST(LlvmLibcCtimeS, InvalidBufferSize) { + char buffer[LIBC_NAMESPACE::time_constants::ASCTIME_BUFFER_SIZE]; + + time_t t = 0; + errno_t result = LIBC_NAMESPACE::ctime_s(buffer, 0, &t); + ASSERT_EQ(ERANGE, result); + + result = LIBC_NAMESPACE::ctime_s(buffer, RSIZE_MAX + 1, &t); + ASSERT_EQ(ERANGE, result); +} + +TEST(LlvmLibcCtimeS, ValidUnixTimestamp0) { + char buffer[LIBC_NAMESPACE::time_constants::ASCTIME_BUFFER_SIZE]; + // 1970-01-01 00:00:00. Test with a valid buffer size. + time_t t = 0; + errno_t result = LIBC_NAMESPACE::ctime_s(buffer, sizeof(buffer), &t); + ASSERT_STREQ("Thu Jan 1 00:00:00 1970\n", buffer); + ASSERT_EQ(0, result); +} + +TEST(LlvmLibcCtimeS, ValidUnixTimestamp32Int) { + char buffer[LIBC_NAMESPACE::time_constants::ASCTIME_BUFFER_SIZE]; + // 2038-01-19 03:14:07. Test with a valid buffer size. + time_t t = 2147483647; + errno_t result = LIBC_NAMESPACE::ctime_s(buffer, sizeof(buffer), &t); + ASSERT_STREQ("Tue Jan 19 03:14:07 2038\n", buffer); + ASSERT_EQ(0, result); +} + +TEST(LlvmLibcCtimeS, InvalidArgument) { + char buffer[LIBC_NAMESPACE::time_constants::ASCTIME_BUFFER_SIZE]; + time_t t = 2147483648; + errno_t result = LIBC_NAMESPACE::ctime_s(buffer, sizeof(buffer), &t); + ASSERT_EQ(EINVAL, result); +}