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 @@ -124,6 +124,8 @@ libc.src.stdio.remove libc.src.stdio.sprintf libc.src.stdio.snprintf + libc.src.stdio.vsprintf + libc.src.stdio.vsnprintf # sys/mman.h entrypoints libc.src.sys.mman.madvise @@ -405,10 +407,13 @@ libc.src.stdio.funlockfile libc.src.stdio.fwrite libc.src.stdio.fwrite_unlocked - libc.src.stdio.fprintf libc.src.stdio.getchar libc.src.stdio.getchar_unlocked + #TODO: Look into if fprintf can be enabled for overlay on aarch64 + libc.src.stdio.fprintf libc.src.stdio.printf + libc.src.stdio.vfprintf + libc.src.stdio.vprintf libc.src.stdio.putc libc.src.stdio.putchar libc.src.stdio.puts diff --git a/libc/config/linux/riscv64/entrypoints.txt b/libc/config/linux/riscv64/entrypoints.txt --- a/libc/config/linux/riscv64/entrypoints.txt +++ b/libc/config/linux/riscv64/entrypoints.txt @@ -128,6 +128,10 @@ libc.src.stdio.snprintf libc.src.stdio.fprintf libc.src.stdio.printf + libc.src.stdio.vsprintf + libc.src.stdio.vsnprintf + libc.src.stdio.vfprintf + libc.src.stdio.vprintf # sys/mman.h entrypoints libc.src.sys.mman.madvise 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 @@ -128,6 +128,10 @@ libc.src.stdio.snprintf libc.src.stdio.fprintf libc.src.stdio.printf + libc.src.stdio.vsprintf + libc.src.stdio.vsnprintf + libc.src.stdio.vfprintf + libc.src.stdio.vprintf # sys/mman.h entrypoints libc.src.sys.mman.madvise diff --git a/libc/include/stdio.h.def b/libc/include/stdio.h.def --- a/libc/include/stdio.h.def +++ b/libc/include/stdio.h.def @@ -13,6 +13,8 @@ #include #include +#include + %%public_api() #endif // LLVM_LIBC_STDIO_H diff --git a/libc/spec/spec.td b/libc/spec/spec.td --- a/libc/spec/spec.td +++ b/libc/spec/spec.td @@ -37,6 +37,7 @@ // Builtin types. def VarArgType : NamedType<"...">; +def VaListType : NamedType<"va_list">; def VoidType : NamedType<"void">; def IntType : NamedType<"int">; def UnsignedIntType : NamedType<"unsigned int">; diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -698,6 +698,34 @@ ArgSpec, ArgSpec] >, + FunctionSpec< + "vsprintf", + RetValSpec, + [ArgSpec, + ArgSpec, + ArgSpec] + >, + FunctionSpec< + "vsnprintf", + RetValSpec, + [ArgSpec, + ArgSpec, + ArgSpec, + ArgSpec] + >, + FunctionSpec< + "vprintf", + RetValSpec, + [ArgSpec, + ArgSpec] + >, + FunctionSpec< + "vfprintf", + RetValSpec, + [ArgSpec, + ArgSpec, + ArgSpec] + >, FunctionSpec< "ungetc", RetValSpec, 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 @@ -542,6 +542,53 @@ ${printf_copts} ) +add_entrypoint_object( + vsprintf + SRCS + vsprintf.cpp + HDRS + vsprintf.h + DEPENDS + libc.src.stdio.printf_core.printf_main + libc.src.stdio.printf_core.writer +) + +add_entrypoint_object( + vsnprintf + SRCS + vsnprintf.cpp + HDRS + vsnprintf.h + DEPENDS + libc.src.stdio.printf_core.printf_main + libc.src.stdio.printf_core.writer +) + +add_entrypoint_object( + vprintf + SRCS + vprintf.cpp + HDRS + vprintf.h + DEPENDS + ${printf_deps} + COMPILE_OPTIONS + ${printf_copts} +) + +add_entrypoint_object( + vfprintf + SRCS + vfprintf.cpp + HDRS + vfprintf.h + DEPENDS + libc.src.__support.arg_list + libc.src.stdio.printf_core.vfprintf_internal + COMPILE_OPTIONS + ${printf_copts} +) + add_entrypoint_object( ftell SRCS diff --git a/libc/src/stdio/vfprintf.h b/libc/src/stdio/vfprintf.h new file mode 100644 --- /dev/null +++ b/libc/src/stdio/vfprintf.h @@ -0,0 +1,22 @@ +//===-- Implementation header of vfprintf -----------------------*- 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_VFPRINTF_H +#define LLVM_LIBC_SRC_STDIO_VFPRINTF_H + +#include +#include + +namespace __llvm_libc { + +int vfprintf(::FILE *__restrict stream, const char *__restrict format, + va_list vlist); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDIO_VFPRINTF_H diff --git a/libc/src/stdio/vfprintf.cpp b/libc/src/stdio/vfprintf.cpp new file mode 100644 --- /dev/null +++ b/libc/src/stdio/vfprintf.cpp @@ -0,0 +1,30 @@ +//===-- Implementation of vfprintf ------------------------------*- 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/vfprintf.h" + +#include "src/__support/File/file.h" +#include "src/__support/arg_list.h" +#include "src/stdio/printf_core/vfprintf_internal.h" + +#include +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, vfprintf, + (::FILE *__restrict stream, const char *__restrict format, + va_list vlist)) { + internal::ArgList args(vlist); // This holder class allows for easier copying + // and pointer semantics, as well as handling + // destruction automatically. + int ret_val = printf_core::vfprintf_internal(stream, format, args); + return ret_val; +} + +} // namespace __llvm_libc diff --git a/libc/src/stdio/vprintf.h b/libc/src/stdio/vprintf.h new file mode 100644 --- /dev/null +++ b/libc/src/stdio/vprintf.h @@ -0,0 +1,21 @@ +//===-- Implementation header of vprintf ------------------------*- 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_VPRINTF_H +#define LLVM_LIBC_SRC_STDIO_VPRINTF_H + +#include +#include + +namespace __llvm_libc { + +int vprintf(const char *__restrict format, va_list vlist); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDIO_VPRINTF_H diff --git a/libc/src/stdio/vprintf.cpp b/libc/src/stdio/vprintf.cpp new file mode 100644 --- /dev/null +++ b/libc/src/stdio/vprintf.cpp @@ -0,0 +1,36 @@ +//===-- Implementation of vprintf -------------------------------*- 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/vprintf.h" + +#include "src/__support/File/file.h" +#include "src/__support/arg_list.h" +#include "src/stdio/printf_core/vfprintf_internal.h" + +#include +#include + +#ifndef LIBC_COPT_PRINTF_USE_SYSTEM_FILE +#define PRINTF_STDOUT __llvm_libc::stdout +#else // LIBC_COPT_PRINTF_USE_SYSTEM_FILE +#define PRINTF_STDOUT ::stdout +#endif // LIBC_COPT_PRINTF_USE_SYSTEM_FILE + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, vprintf, + (const char *__restrict format, va_list vlist)) { + internal::ArgList args(vlist); // This holder class allows for easier copying + // and pointer semantics, as well as handling + // destruction automatically. + int ret_val = printf_core::vfprintf_internal( + reinterpret_cast<::FILE *>(PRINTF_STDOUT), format, args); + return ret_val; +} + +} // namespace __llvm_libc diff --git a/libc/src/stdio/vsnprintf.h b/libc/src/stdio/vsnprintf.h new file mode 100644 --- /dev/null +++ b/libc/src/stdio/vsnprintf.h @@ -0,0 +1,22 @@ +//===-- Implementation header of vsnprintf ----------------------*- 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_VSNPRINTF_H +#define LLVM_LIBC_SRC_STDIO_VSNPRINTF_H + +#include +#include + +namespace __llvm_libc { + +int vsnprintf(char *__restrict buffer, size_t buffsz, + const char *__restrict format, va_list vlist); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDIO_VSNPRINTF_H diff --git a/libc/src/stdio/vsnprintf.cpp b/libc/src/stdio/vsnprintf.cpp new file mode 100644 --- /dev/null +++ b/libc/src/stdio/vsnprintf.cpp @@ -0,0 +1,35 @@ +//===-- Implementation of vsnprintf -----------------------------*- 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/vsnprintf.h" + +#include "src/__support/arg_list.h" +#include "src/stdio/printf_core/printf_main.h" +#include "src/stdio/printf_core/writer.h" + +#include +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, vsnprintf, + (char *__restrict buffer, size_t buffsz, + const char *__restrict format, va_list vlist)) { + internal::ArgList args(vlist); // This holder class allows for easier copying + // and pointer semantics, as well as handling + // destruction automatically. + printf_core::WriteBuffer wb(buffer, (buffsz > 0 ? buffsz - 1 : 0)); + printf_core::Writer writer(&wb); + + 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. + wb.buff[wb.buff_cur] = '\0'; + return ret_val; +} + +} // namespace __llvm_libc diff --git a/libc/src/stdio/vsprintf.h b/libc/src/stdio/vsprintf.h new file mode 100644 --- /dev/null +++ b/libc/src/stdio/vsprintf.h @@ -0,0 +1,21 @@ +//===-- Implementation header of vsprintf -----------------------*- 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_VSPRINTF_H +#define LLVM_LIBC_SRC_STDIO_VSPRINTF_H + +#include + +namespace __llvm_libc { + +int vsprintf(char *__restrict buffer, const char *__restrict format, + va_list vlist); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDIO_VSPRINTF_H diff --git a/libc/src/stdio/vsprintf.cpp b/libc/src/stdio/vsprintf.cpp new file mode 100644 --- /dev/null +++ b/libc/src/stdio/vsprintf.cpp @@ -0,0 +1,35 @@ +//===-- Implementation of vsprintf ------------------------------*- 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/vsprintf.h" + +#include "src/__support/CPP/limits.h" +#include "src/__support/arg_list.h" +#include "src/stdio/printf_core/printf_main.h" +#include "src/stdio/printf_core/writer.h" + +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, vsprintf, + (char *__restrict buffer, const char *__restrict format, + va_list vlist)) { + internal::ArgList args(vlist); // This holder class allows for easier copying + // and pointer semantics, as well as handling + // destruction automatically. + + printf_core::WriteBuffer wb(buffer, cpp::numeric_limits::max()); + printf_core::Writer writer(&wb); + + int ret_val = printf_core::printf_main(&writer, format, args); + wb.buff[wb.buff_cur] = '\0'; + 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 @@ -135,9 +135,6 @@ libc.src.stdio.snprintf ) -list(APPEND fprintf_test_deps - libc.src.stdio.fprintf -) if(LLVM_LIBC_FULL_BUILD) # In fullbuild mode, fprintf's tests use the internal FILE for other functions. list(APPEND fprintf_test_deps @@ -158,6 +155,7 @@ SRCS fprintf_test.cpp DEPENDS + libc.src.stdio.fprintf ${fprintf_test_deps} COMPILE_OPTIONS ${fprintf_test_copts} @@ -173,6 +171,50 @@ libc.src.stdio.printf ) +add_fp_unittest( + vsprintf_test + UNIT_TEST_ONLY + SUITE + libc_stdio_unittests + SRCS + vsprintf_test.cpp + DEPENDS + libc.src.stdio.vsprintf +) + +add_libc_unittest( + vsnprintf_test + SUITE + libc_stdio_unittests + SRCS + vsnprintf_test.cpp + DEPENDS + libc.src.stdio.vsnprintf +) + +add_libc_unittest( + vfprintf_test + SUITE + libc_stdio_unittests + SRCS + vfprintf_test.cpp + DEPENDS + libc.src.stdio.vfprintf + ${fprintf_test_deps} + COMPILE_OPTIONS + ${fprintf_test_copts} +) + +add_libc_unittest( + vprintf_test + SUITE + libc_stdio_unittests + SRCS + vprintf_test.cpp + DEPENDS + libc.src.stdio.vprintf +) + add_libc_unittest( fscanf_test SUITE diff --git a/libc/test/src/stdio/vfprintf_test.cpp b/libc/test/src/stdio/vfprintf_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/stdio/vfprintf_test.cpp @@ -0,0 +1,97 @@ +//===-- Unittests for vfprintf --------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// These tests are copies of the non-v variants of the printf functions. This is +// because these functions are identical in every way except for how the varargs +// are passed. + +#ifndef LIBC_COPT_PRINTF_USE_SYSTEM_FILE +#include "src/stdio/fclose.h" +#include "src/stdio/ferror.h" +#include "src/stdio/fopen.h" +#include "src/stdio/fread.h" +#endif // LIBC_COPT_PRINTF_USE_SYSTEM_FILE + +#include "src/stdio/vfprintf.h" + +#include "test/UnitTest/Test.h" + +#include + +namespace printf_test { +#ifndef LIBC_COPT_PRINTF_USE_SYSTEM_FILE +using __llvm_libc::fclose; +using __llvm_libc::ferror; +using __llvm_libc::fopen; +using __llvm_libc::fread; +#else // defined(LIBC_COPT_PRINTF_USE_SYSTEM_FILE) +using ::fclose; +using ::ferror; +using ::fopen; +using ::fread; +#endif // LIBC_COPT_PRINTF_USE_SYSTEM_FILE +} // namespace printf_test + +int call_vfprintf(::FILE *__restrict stream, const char *__restrict format, + ...) { + va_list vlist; + va_start(vlist, format); + int ret = __llvm_libc::vfprintf(stream, format, vlist); + va_end(vlist); + return ret; +} + +TEST(LlvmLibcVFPrintfTest, WriteToFile) { + const char *FILENAME = "fprintf_output.test"; + auto FILE_PATH = libc_make_test_file_path(FILENAME); + + ::FILE *file = printf_test::fopen(FILE_PATH, "w"); + ASSERT_FALSE(file == nullptr); + + int written; + + constexpr char simple[] = "A simple string with no conversions.\n"; + written = call_vfprintf(file, simple); + EXPECT_EQ(written, 37); + + constexpr char numbers[] = "1234567890\n"; + written = call_vfprintf(file, "%s", numbers); + EXPECT_EQ(written, 11); + + constexpr char format_more[] = "%s and more\n"; + constexpr char short_numbers[] = "1234"; + written = call_vfprintf(file, format_more, short_numbers); + EXPECT_EQ(written, 14); + + ASSERT_EQ(0, printf_test::fclose(file)); + + file = printf_test::fopen(FILE_PATH, "r"); + ASSERT_FALSE(file == nullptr); + + char data[50]; + ASSERT_EQ(printf_test::fread(data, 1, sizeof(simple) - 1, file), + sizeof(simple) - 1); + data[sizeof(simple) - 1] = '\0'; + ASSERT_STREQ(data, simple); + ASSERT_EQ(printf_test::fread(data, 1, sizeof(numbers) - 1, file), + sizeof(numbers) - 1); + data[sizeof(numbers) - 1] = '\0'; + ASSERT_STREQ(data, numbers); + ASSERT_EQ(printf_test::fread( + data, 1, sizeof(format_more) + sizeof(short_numbers) - 4, file), + sizeof(format_more) + sizeof(short_numbers) - 4); + data[sizeof(format_more) + sizeof(short_numbers) - 4] = '\0'; + ASSERT_STREQ(data, "1234 and more\n"); + + ASSERT_EQ(printf_test::ferror(file), 0); + + written = call_vfprintf(file, "Writing to a read only file should fail."); + EXPECT_LT(written, 0); + + ASSERT_EQ(printf_test::fclose(file), 0); +} diff --git a/libc/test/src/stdio/vprintf_test.cpp b/libc/test/src/stdio/vprintf_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/stdio/vprintf_test.cpp @@ -0,0 +1,41 @@ +//===-- Unittests for vprintf --------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// These tests are copies of the non-v variants of the printf functions. This is +// because these functions are identical in every way except for how the varargs +// are passed. + +#include "src/stdio/vprintf.h" + +#include "test/UnitTest/Test.h" + +int call_vprintf(const char *__restrict format, ...) { + va_list vlist; + va_start(vlist, format); + int ret = __llvm_libc::vprintf(format, vlist); + va_end(vlist); + return ret; +} + +TEST(LlvmLibcVPrintfTest, PrintOut) { + int written; + + constexpr char simple[] = "A simple string with no conversions.\n"; + written = call_vprintf(simple); + EXPECT_EQ(written, static_cast(sizeof(simple) - 1)); + + constexpr char numbers[] = "1234567890\n"; + written = call_vprintf("%s", numbers); + EXPECT_EQ(written, static_cast(sizeof(numbers) - 1)); + + constexpr char format_more[] = "%s and more\n"; + constexpr char short_numbers[] = "1234"; + written = call_vprintf(format_more, short_numbers); + EXPECT_EQ(written, + static_cast(sizeof(format_more) + sizeof(short_numbers) - 4)); +} diff --git a/libc/test/src/stdio/vsnprintf_test.cpp b/libc/test/src/stdio/vsnprintf_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/stdio/vsnprintf_test.cpp @@ -0,0 +1,69 @@ +//===-- 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 +// +//===----------------------------------------------------------------------===// + +// These tests are copies of the non-v variants of the printf functions. This is +// because these functions are identical in every way except for how the varargs +// are passed. + +#include "src/stdio/vsnprintf.h" + +#include "test/UnitTest/Test.h" + +int call_vsnprintf(char *__restrict buffer, size_t buffsz, + const char *__restrict format, ...) { + va_list vlist; + va_start(vlist, format); + int ret = __llvm_libc::vsnprintf(buffer, buffsz, format, vlist); + va_end(vlist); + return ret; +} + +// The sprintf test cases cover testing the shared printf functionality, so +// these tests will focus on snprintf exclusive features. + +TEST(LlvmLibcVSNPrintfTest, CutOff) { + char buff[100]; + int written; + + written = call_vsnprintf(buff, 16, "A simple string with no conversions."); + EXPECT_EQ(written, 36); + ASSERT_STREQ(buff, "A simple string"); + + written = call_vsnprintf(buff, 5, "%s", "1234567890"); + EXPECT_EQ(written, 10); + ASSERT_STREQ(buff, "1234"); + + written = call_vsnprintf(buff, 67, "%-101c", 'a'); + EXPECT_EQ(written, 101); + ASSERT_STREQ(buff, "a " + " " // Each of these is 8 spaces, and there are 8. + " " // In total there are 65 spaces + " " // 'a' + 65 spaces + '\0' = 67 + " " + " " + " " + " " + " "); + + // passing null as the output pointer is allowed as long as buffsz is 0. + written = call_vsnprintf(nullptr, 0, "%s and more", "1234567890"); + EXPECT_EQ(written, 19); +} + +TEST(LlvmLibcVSNPrintfTest, NoCutOff) { + char buff[64]; + int written; + + written = call_vsnprintf(buff, 37, "A simple string with no conversions."); + EXPECT_EQ(written, 36); + ASSERT_STREQ(buff, "A simple string with no conversions."); + + written = call_vsnprintf(buff, 20, "%s", "1234567890"); + EXPECT_EQ(written, 10); + ASSERT_STREQ(buff, "1234567890"); +} diff --git a/libc/test/src/stdio/vsprintf_test.cpp b/libc/test/src/stdio/vsprintf_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/stdio/vsprintf_test.cpp @@ -0,0 +1,66 @@ +//===-- Unittests for vsprintf --------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// These tests are shortened copies of the non-v variants of the printf +// functions. This is because these functions are identical in every way except +// for how the varargs are passed. + +#include "src/stdio/vsprintf.h" + +#include "test/UnitTest/Test.h" + +int call_vsprintf(char *__restrict buffer, const char *__restrict format, ...) { + va_list vlist; + va_start(vlist, format); + int ret = __llvm_libc::vsprintf(buffer, format, vlist); + va_end(vlist); + return ret; +} + +TEST(LlvmLibcVSPrintfTest, SimpleNoConv) { + char buff[64]; + int written; + + written = call_vsprintf(buff, "A simple string with no conversions."); + EXPECT_EQ(written, 36); + ASSERT_STREQ(buff, "A simple string with no conversions."); +} + +TEST(LlvmLibcVSPrintfTest, PercentConv) { + char buff[64]; + int written; + + written = call_vsprintf(buff, "%%"); + EXPECT_EQ(written, 1); + ASSERT_STREQ(buff, "%"); + + written = call_vsprintf(buff, "abc %% def"); + EXPECT_EQ(written, 9); + ASSERT_STREQ(buff, "abc % def"); + + written = call_vsprintf(buff, "%%%%%%"); + EXPECT_EQ(written, 3); + ASSERT_STREQ(buff, "%%%"); +} + +TEST(LlvmLibcVSPrintfTest, CharConv) { + char buff[64]; + int written; + + written = call_vsprintf(buff, "%c", 'a'); + EXPECT_EQ(written, 1); + ASSERT_STREQ(buff, "a"); + + written = call_vsprintf(buff, "%3c %-3c", '1', '2'); + EXPECT_EQ(written, 7); + ASSERT_STREQ(buff, " 1 2 "); + + written = call_vsprintf(buff, "%*c", 2, '3'); + EXPECT_EQ(written, 2); + ASSERT_STREQ(buff, " 3"); +} diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel --- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel @@ -2642,6 +2642,19 @@ ], ) +libc_support_library( + name = "vfprintf_internal", + hdrs = ["src/stdio/printf_core/vfprintf_internal.h"], + defines = PRINTF_COPTS, + deps = [ + ":__support_arg_list", + ":__support_file_file", + ":__support_macros_attributes", + ":printf_main", + ":printf_writer", + ], +) + libc_function( name = "sprintf", srcs = ["src/stdio/sprintf.cpp"], @@ -2669,23 +2682,63 @@ ], ) -libc_support_library( - name = "vfprintf_internal", - hdrs = ["src/stdio/printf_core/vfprintf_internal.h"], +libc_function( + name = "printf", + srcs = ["src/stdio/printf.cpp"], + hdrs = ["src/stdio/printf.h"], defines = PRINTF_COPTS, deps = [ ":__support_arg_list", ":__support_file_file", - ":__support_macros_attributes", + ":errno", + ":vfprintf_internal", + ], +) + +libc_function( + name = "fprintf", + srcs = ["src/stdio/fprintf.cpp"], + hdrs = ["src/stdio/fprintf.h"], + defines = PRINTF_COPTS, + deps = [ + ":__support_arg_list", + ":__support_file_file", + ":errno", + ":vfprintf_internal", + ], +) + +libc_function( + name = "vsprintf", + srcs = ["src/stdio/vsprintf.cpp"], + hdrs = ["src/stdio/vsprintf.h"], + defines = PRINTF_COPTS, + deps = [ + ":__support_arg_list", + ":__support_cpp_limits", + ":errno", ":printf_main", ":printf_writer", ], ) libc_function( - name = "printf", - srcs = ["src/stdio/printf.cpp"], - hdrs = ["src/stdio/printf.h"], + name = "vsnprintf", + srcs = ["src/stdio/vsnprintf.cpp"], + hdrs = ["src/stdio/vsnprintf.h"], + defines = PRINTF_COPTS, + deps = [ + ":__support_arg_list", + ":errno", + ":printf_main", + ":printf_writer", + ], +) + +libc_function( + name = "vprintf", + srcs = ["src/stdio/vprintf.cpp"], + hdrs = ["src/stdio/vprintf.h"], defines = PRINTF_COPTS, deps = [ ":__support_arg_list", @@ -2696,9 +2749,9 @@ ) libc_function( - name = "fprintf", - srcs = ["src/stdio/fprintf.cpp"], - hdrs = ["src/stdio/fprintf.h"], + name = "vfprintf", + srcs = ["src/stdio/vfprintf.cpp"], + hdrs = ["src/stdio/vfprintf.h"], defines = PRINTF_COPTS, deps = [ ":__support_arg_list", diff --git a/utils/bazel/llvm-project-overlay/libc/test/src/stdio/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/test/src/stdio/BUILD.bazel --- a/utils/bazel/llvm-project-overlay/libc/test/src/stdio/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/libc/test/src/stdio/BUILD.bazel @@ -89,3 +89,35 @@ "//libc:fprintf", ], ) + +libc_test( + name = "vsprintf_test", + srcs = ["vsprintf_test.cpp"], + libc_function_deps = [ + "//libc:vsprintf", + ], +) + +libc_test( + name = "vsnprintf_test", + srcs = ["vsnprintf_test.cpp"], + libc_function_deps = [ + "//libc:vsnprintf", + ], +) + +libc_test( + name = "vprintf_test", + srcs = ["vprintf_test.cpp"], + libc_function_deps = [ + "//libc:vprintf", + ], +) + +libc_test( + name = "vfprintf_test", + srcs = ["vfprintf_test.cpp"], + libc_function_deps = [ + "//libc:vfprintf", + ], +)