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 @@ -44,6 +44,8 @@ libc.src.string.strcmp libc.src.string.strcpy libc.src.string.strcspn + libc.src.string.strerror + libc.src.string.strerror_r libc.src.string.strlcat libc.src.string.strlcpy libc.src.string.strlen @@ -61,7 +63,6 @@ # string.h entrypoints that depend on malloc libc.src.string.strdup libc.src.string.strndup - libc.src.string.strerror # inttypes.h entrypoints libc.src.inttypes.imaxabs diff --git a/libc/spec/gnu_ext.td b/libc/spec/gnu_ext.td --- a/libc/spec/gnu_ext.td +++ b/libc/spec/gnu_ext.td @@ -63,6 +63,11 @@ RetValSpec, [ArgSpec, ArgSpec, ArgSpec] >, + FunctionSpec< + "strerror_r", + RetValSpec, + [ArgSpec, ArgSpec, ArgSpec] + >, ] >; diff --git a/libc/src/__support/error_to_string.h b/libc/src/__support/error_to_string.h --- a/libc/src/__support/error_to_string.h +++ b/libc/src/__support/error_to_string.h @@ -16,6 +16,8 @@ cpp::string_view get_error_string(int err_num); +cpp::string_view get_error_string(int err_num, cpp::string_view buffer); + } // namespace __llvm_libc #endif // LLVM_LIBC_SRC_SUPPORT_ERROR_TO_STRING diff --git a/libc/src/__support/error_to_string.cpp b/libc/src/__support/error_to_string.cpp --- a/libc/src/__support/error_to_string.cpp +++ b/libc/src/__support/error_to_string.cpp @@ -223,18 +223,19 @@ } } - cpp::string_view get_str(int err_num) const { + cpp::string_view get_str(int err_num, cpp::string_view buffer) const { if (err_num >= 0 && static_cast(err_num) < ERR_ARRAY_SIZE && err_offsets[err_num] != -1) { return const_cast(string_array + err_offsets[err_num]); } else { // if the buffer can't hold "Unknown error" + ' ' + num_str, then just // return "Unknown error". - if (BUFFER_SIZE < + if (buffer.size() < (sizeof("Unknown error") + 1 + IntegerToString::dec_bufsize())) return const_cast("Unknown error"); - cpp::StringStream buffer_stream({error_buffer, BUFFER_SIZE}); + cpp::StringStream buffer_stream( + {const_cast(buffer.data()), buffer.size()}); buffer_stream << "Unknown error" << ' ' << err_num << '\0'; return buffer_stream.str(); } @@ -246,6 +247,11 @@ } // namespace internal cpp::string_view get_error_string(int err_num) { - return internal::error_mapper.get_str(err_num); + return internal::error_mapper.get_str( + err_num, {internal::error_buffer, internal::BUFFER_SIZE}); +} + +cpp::string_view get_error_string(int err_num, cpp::string_view buffer) { + return internal::error_mapper.get_str(err_num, buffer); } } // namespace __llvm_libc 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 @@ -136,7 +136,16 @@ strerror.h DEPENDS libc.src.__support.error_to_string - libc.src.__support.CPP.span +) + +add_entrypoint_object( + strerror_r + SRCS + strerror_r.cpp + HDRS + strerror_r.h + DEPENDS + libc.src.__support.error_to_string ) add_entrypoint_object( diff --git a/libc/src/string/strerror.cpp b/libc/src/string/strerror.cpp --- a/libc/src/string/strerror.cpp +++ b/libc/src/string/strerror.cpp @@ -7,7 +7,6 @@ //===----------------------------------------------------------------------===// #include "src/string/strerror.h" -#include "src/__support/CPP/span.h" #include "src/__support/common.h" #include "src/__support/error_to_string.h" diff --git a/libc/src/__support/error_to_string.h b/libc/src/string/strerror_r.h copy from libc/src/__support/error_to_string.h copy to libc/src/string/strerror_r.h --- a/libc/src/__support/error_to_string.h +++ b/libc/src/string/strerror_r.h @@ -1,4 +1,4 @@ -//===-- Definition of a class for mapping errors to strings -----*- C++ -*-===// +//===-- Implementation header for strerror_r --------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,16 +6,15 @@ // //===----------------------------------------------------------------------===// -#include "src/__support/CPP/string_view.h" -#include "src/__support/integer_to_string.h" +#ifndef LLVM_LIBC_SRC_STRING_STRERROR_R_H +#define LLVM_LIBC_SRC_STRING_STRERROR_R_H -#ifndef LLVM_LIBC_SRC_SUPPORT_ERROR_TO_STRING -#define LLVM_LIBC_SRC_SUPPORT_ERROR_TO_STRING +#include namespace __llvm_libc { -cpp::string_view get_error_string(int err_num); +char *strerror_r(int err_num, char *buf, size_t buflen); } // namespace __llvm_libc -#endif // LLVM_LIBC_SRC_SUPPORT_ERROR_TO_STRING +#endif // LLVM_LIBC_SRC_STRING_STRERROR_R_H diff --git a/libc/src/string/strerror.cpp b/libc/src/string/strerror_r.cpp copy from libc/src/string/strerror.cpp copy to libc/src/string/strerror_r.cpp --- a/libc/src/string/strerror.cpp +++ b/libc/src/string/strerror_r.cpp @@ -1,4 +1,4 @@ -//===-- Implementation of strerror ----------------------------------------===// +//===-- Implementation of strerror_r --------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,15 +6,18 @@ // //===----------------------------------------------------------------------===// -#include "src/string/strerror.h" -#include "src/__support/CPP/span.h" +#include "src/string/strerror_r.h" #include "src/__support/common.h" #include "src/__support/error_to_string.h" +#include + namespace __llvm_libc { -LLVM_LIBC_FUNCTION(char *, strerror, (int err_num)) { - return const_cast(get_error_string(err_num).data()); +// This is the gnu version of strerror_r. The XSI version may be added later. +LLVM_LIBC_FUNCTION(char *, strerror_r, + (int err_num, char *buf, size_t buflen)) { + return const_cast(get_error_string(err_num, {buf, buflen}).data()); } } // 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 @@ -133,6 +133,17 @@ libc.src.string.strerror ) + +add_libc_unittest( + strerror_r_test + SUITE + libc_string_unittests + SRCS + strerror_r_test.cpp + DEPENDS + libc.src.string.strerror_r +) + add_libc_unittest( strlcat_test SUITE diff --git a/libc/test/src/string/strerror_r_test.cpp b/libc/test/src/string/strerror_r_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/string/strerror_r_test.cpp @@ -0,0 +1,28 @@ +//===-- Unittests for strerror --------------------------------------------===// +// +// 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/strerror_r.h" +#include "utils/UnitTest/Test.h" + +#include + +// This tests the gnu variant of strerror_r (which returns a char*). +TEST(LlvmLibcStrErrorRTest, GnuVariantTests) { + const size_t BUFF_SIZE = 128; + char buffer[BUFF_SIZE]; + buffer[0] = '\0'; + // If strerror_r returns a constant string, then it shouldn't affect the + // buffer. + ASSERT_STREQ(__llvm_libc::strerror_r(0, buffer, BUFF_SIZE), "Success"); + ASSERT_EQ(buffer[0], '\0'); + + // Else it should write the result to the provided buffer. + ASSERT_STREQ(__llvm_libc::strerror_r(-1, buffer, BUFF_SIZE), + "Unknown error -1"); + ASSERT_STREQ(buffer, "Unknown error -1"); +}