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 @@ -269,6 +269,7 @@ libc.src.stdio.fwrite libc.src.stdio.fwrite_unlocked libc.src.stdio.sprintf + libc.src.stdio.snprintf # signal.h entrypoints # TODO: Enable signal.h entrypoints after fixing signal.h diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -539,6 +539,27 @@ ArgSpec, ArgSpec] >, + FunctionSpec< + "snprintf", + RetValSpec, + [ArgSpec, + ArgSpec, + ArgSpec, + ArgSpec] + >, + FunctionSpec< + "printf", + RetValSpec, + [ArgSpec, + ArgSpec] + >, + FunctionSpec< + "fprintf", + RetValSpec, + [ArgSpec, + ArgSpec, + ArgSpec] + >, ] >; diff --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt --- a/libc/src/stdio/CMakeLists.txt +++ b/libc/src/stdio/CMakeLists.txt @@ -211,7 +211,18 @@ HDRS sprintf.h DEPENDS - libc.include.stdio + libc.src.stdio.printf_core.printf_main + libc.src.stdio.printf_core.string_writer + libc.src.stdio.printf_core.writer +) + +add_entrypoint_object( + snprintf + SRCS + snprintf.cpp + HDRS + snprintf.h + DEPENDS libc.src.stdio.printf_core.printf_main libc.src.stdio.printf_core.string_writer libc.src.stdio.printf_core.writer diff --git a/libc/src/stdio/snprintf.h b/libc/src/stdio/snprintf.h new file mode 100644 --- /dev/null +++ b/libc/src/stdio/snprintf.h @@ -0,0 +1,21 @@ +//===-- Implementation header of snprintf -----------------------*- 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_STDIO_SNPRINTF_H +#define LLVM_LIBC_SRC_STDIO_SNPRINTF_H + +#include + +namespace __llvm_libc { + +int snprintf(char *__restrict buffer, size_t buffsz, + const char *__restrict format, ...); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDIO_SNPRINTF_H diff --git a/libc/src/stdio/snprintf.cpp b/libc/src/stdio/snprintf.cpp new file mode 100644 --- /dev/null +++ b/libc/src/stdio/snprintf.cpp @@ -0,0 +1,40 @@ +//===-- Implementation of snprintf ------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "src/stdio/snprintf.h" + +#include "src/__support/arg_list.h" +#include "src/stdio/printf_core/printf_main.h" +#include "src/stdio/printf_core/string_writer.h" +#include "src/stdio/printf_core/writer.h" + +#include +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, snprintf, + (char *__restrict buffer, size_t buffsz, + const char *__restrict format, ...)) { + va_list vlist; + va_start(vlist, format); + internal::ArgList args(vlist); // This holder class allows for easier copying + // and pointer semantics, as well as handing + // destruction automatically. + va_end(vlist); + printf_core::StringWriter str_writer(buffer, (buffsz > 0 ? buffsz - 1 : 0)); + printf_core::Writer writer(reinterpret_cast(&str_writer), + printf_core::write_to_string); + + int ret_val = printf_core::printf_main(&writer, format, args); + if (buffsz > 0) // if the buffsz is 0 the buffer may be a null pointer. + str_writer.terminate(); + return ret_val; +} + +} // namespace __llvm_libc diff --git a/libc/test/src/stdio/CMakeLists.txt b/libc/test/src/stdio/CMakeLists.txt --- a/libc/test/src/stdio/CMakeLists.txt +++ b/libc/test/src/stdio/CMakeLists.txt @@ -73,6 +73,16 @@ libc.src.stdio.sprintf ) +add_libc_unittest( + snprintf_test + SUITE + libc_stdio_unittests + SRCS + snprintf_test.cpp + DEPENDS + libc.src.stdio.snprintf +) + add_subdirectory(printf_core) add_subdirectory(testdata) diff --git a/libc/test/src/stdio/snprintf_test.cpp b/libc/test/src/stdio/snprintf_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/stdio/snprintf_test.cpp @@ -0,0 +1,46 @@ +//===-- Unittests for snprintf --------------------------------------------===// +// +// 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/stdio/snprintf.h" + +#include "utils/UnitTest/Test.h" + +// The sprintf test cases cover testing the shared printf functionality, so +// these tests will focus on snprintf exclusive features. + +TEST(LlvmLibcSNPrintfTest, CutOff) { + char buff[64]; + int written; + + written = + __llvm_libc::snprintf(buff, 16, "A simple string with no conversions."); + EXPECT_EQ(written, 36); + ASSERT_STREQ(buff, "A simple string"); + + written = __llvm_libc::snprintf(buff, 5, "%s", "1234567890"); + EXPECT_EQ(written, 10); + ASSERT_STREQ(buff, "1234"); + + // passing null as the output pointer is allowed as long as buffsz is 0. + written = __llvm_libc::snprintf(nullptr, 0, "%s and more", "1234567890"); + EXPECT_EQ(written, 19); +} + +TEST(LlvmLibcSNPrintfTest, NoCutOff) { + char buff[64]; + int written; + + written = + __llvm_libc::snprintf(buff, 37, "A simple string with no conversions."); + EXPECT_EQ(written, 36); + ASSERT_STREQ(buff, "A simple string with no conversions."); + + written = __llvm_libc::snprintf(buff, 20, "%s", "1234567890"); + EXPECT_EQ(written, 10); + ASSERT_STREQ(buff, "1234567890"); +}