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 @@ -276,6 +276,7 @@ libc.src.stdio.fwrite_unlocked libc.src.stdio.sprintf libc.src.stdio.snprintf + libc.src.stdio.fprintf libc.src.stdio.stderr libc.src.stdio.stdout 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 @@ -250,3 +250,16 @@ libc.src.stdio.printf_core.string_writer libc.src.stdio.printf_core.writer ) + +add_entrypoint_object( + fprintf + SRCS + fprintf.cpp + HDRS + fprintf.h + DEPENDS + libc.include.stdio + libc.src.stdio.printf_core.printf_main + libc.src.stdio.printf_core.file_writer + libc.src.stdio.printf_core.writer +) diff --git a/libc/src/stdio/fprintf.h b/libc/src/stdio/fprintf.h new file mode 100644 --- /dev/null +++ b/libc/src/stdio/fprintf.h @@ -0,0 +1,20 @@ +//===-- Implementation header of fprintf ------------------------*- 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_FPRINTF_H +#define LLVM_LIBC_SRC_STDIO_FPRINTF_H + +#include + +namespace __llvm_libc { + +int fprintf(::FILE *__restrict stream, const char *__restrict format, ...); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDIO_FPRINTF_H diff --git a/libc/src/stdio/fprintf.cpp b/libc/src/stdio/fprintf.cpp new file mode 100644 --- /dev/null +++ b/libc/src/stdio/fprintf.cpp @@ -0,0 +1,40 @@ +//===-- Implementation of fprintf -------------------------------*- 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/fprintf.h" + +#include "src/__support/arg_list.h" +#include "src/stdio/ferror.h" +#include "src/stdio/printf_core/file_writer.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, fprintf, + (::FILE *__restrict stream, 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 handling + // destruction automatically. + va_end(vlist); + printf_core::Writer writer(reinterpret_cast(stream), + printf_core::write_to_file); + + int ret_val = printf_core::printf_main(&writer, format, args); + if (__llvm_libc::ferror(stream)) + return -1; + return ret_val; +} + +} // namespace __llvm_libc diff --git a/libc/src/stdio/printf_core/CMakeLists.txt b/libc/src/stdio/printf_core/CMakeLists.txt --- a/libc/src/stdio/printf_core/CMakeLists.txt +++ b/libc/src/stdio/printf_core/CMakeLists.txt @@ -28,6 +28,14 @@ libc.src.string.memory_utils.memcpy_implementation ) +add_header_library( + file_writer + HDRS + file_writer.h + DEPENDS + libc.src.__support.File.file +) + add_object_library( writer SRCS diff --git a/libc/src/stdio/printf_core/file_writer.h b/libc/src/stdio/printf_core/file_writer.h new file mode 100644 --- /dev/null +++ b/libc/src/stdio/printf_core/file_writer.h @@ -0,0 +1,29 @@ +//===-- FILE Writer class for printf ----------------------------*- 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_PRINTF_CORE_FILE_WRITER_H +#define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_FILE_WRITER_H + +#include "src/__support/File/file.h" +#include + +namespace __llvm_libc { +namespace printf_core { + +// write_to_file treats raw_pointer as a File and calls its write +// function. +void write_to_file(void *raw_pointer, const char *__restrict to_write, + size_t len) { + __llvm_libc::File *file = reinterpret_cast<__llvm_libc::File *>(raw_pointer); + file->write(to_write, len); +} + +} // namespace printf_core +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_FILE_WRITER_H 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 @@ -83,6 +83,20 @@ libc.src.stdio.snprintf ) +add_libc_unittest( + fprintf_test + SUITE + libc_stdio_unittests + SRCS + fprintf_test.cpp + DEPENDS + libc.src.stdio.fprintf + libc.src.stdio.fclose + libc.src.stdio.ferror + libc.src.stdio.fopen + libc.src.stdio.fread +) + add_subdirectory(printf_core) add_subdirectory(testdata) diff --git a/libc/test/src/stdio/fprintf_test.cpp b/libc/test/src/stdio/fprintf_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/stdio/fprintf_test.cpp @@ -0,0 +1,68 @@ +//===-- Unittests for fprintf ---------------------------------------------===// +// +// 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/fclose.h" +#include "src/stdio/ferror.h" +#include "src/stdio/fopen.h" +#include "src/stdio/fread.h" + +#include "src/stdio/fprintf.h" + +#include "utils/UnitTest/Test.h" + +#include +#include + +TEST(LlvmLibcFPrintfTest, WriteToFile) { + constexpr char FILENAME[] = "testdata/fprintf_output.test"; + ::FILE *file = __llvm_libc::fopen(FILENAME, "w"); + ASSERT_FALSE(file == nullptr); + + int written; + + constexpr char simple[] = "A simple string with no conversions.\n"; + written = __llvm_libc::fprintf(file, simple); + EXPECT_EQ(written, 37); + + constexpr char numbers[] = "1234567890\n"; + written = __llvm_libc::fprintf(file, "%s", numbers); + EXPECT_EQ(written, 11); + + constexpr char format_more[] = "%s and more\n"; + constexpr char short_numbers[] = "1234"; + written = __llvm_libc::fprintf(file, format_more, short_numbers); + EXPECT_EQ(written, 14); + + ASSERT_EQ(0, __llvm_libc::fclose(file)); + + file = __llvm_libc::fopen(FILENAME, "r"); + ASSERT_FALSE(file == nullptr); + + char data[50]; + ASSERT_EQ(__llvm_libc::fread(data, 1, sizeof(simple) - 1, file), + sizeof(simple) - 1); + data[sizeof(simple) - 1] = '\0'; + ASSERT_STREQ(data, simple); + ASSERT_EQ(__llvm_libc::fread(data, 1, sizeof(numbers) - 1, file), + sizeof(numbers) - 1); + data[sizeof(numbers) - 1] = '\0'; + ASSERT_STREQ(data, numbers); + ASSERT_EQ(__llvm_libc::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(__llvm_libc::ferror(file), 0); + + written = + __llvm_libc::fprintf(file, "Writing to a read only file should fail."); + EXPECT_EQ(written, -1); + + ASSERT_EQ(__llvm_libc::fclose(file), 0); +}