From a9e0dbefdd1ae5d46ae4a462d71d6352d2f1c105 Mon Sep 17 00:00:00 2001 From: Michael Jones Date: Tue, 20 Sep 2022 16:58:05 -0700 Subject: [PATCH] [libc] add fputs and puts add fputs, puts, and the EOF macro that they use. Reviewed By: sivachandra Differential Revision: https://reviews.llvm.org/D134328 --- libc/config/linux/api.td | 1 + libc/config/linux/x86_64/entrypoints.txt | 2 ++ libc/docs/stdio.rst | 4 +-- libc/spec/stdc.td | 12 +++++++++ libc/src/__support/File/file.cpp | 2 +- libc/src/stdio/CMakeLists.txt | 25 ++++++++++++++++++ libc/src/stdio/fputs.cpp | 30 ++++++++++++++++++++++ libc/src/stdio/fputs.h | 20 +++++++++++++++ libc/src/stdio/puts.cpp | 32 ++++++++++++++++++++++++ libc/src/stdio/puts.h | 20 +++++++++++++++ libc/test/src/stdio/CMakeLists.txt | 12 ++++++++- libc/test/src/stdio/fileop_test.cpp | 30 ++++++++++++++++++++++ libc/test/src/stdio/puts_test.cpp | 28 +++++++++++++++++++++ 13 files changed, 214 insertions(+), 4 deletions(-) create mode 100644 libc/src/stdio/fputs.cpp create mode 100644 libc/src/stdio/fputs.h create mode 100644 libc/src/stdio/puts.cpp create mode 100644 libc/src/stdio/puts.h create mode 100644 libc/test/src/stdio/puts_test.cpp diff --git a/libc/config/linux/api.td b/libc/config/linux/api.td index 463f40fe220e..97eed64c9f3a 100644 --- a/libc/config/linux/api.td +++ b/libc/config/linux/api.td @@ -153,6 +153,7 @@ def StdIOAPI : PublicAPI<"stdio.h"> { SimpleMacroDef<"_IOFBF", "0">, SimpleMacroDef<"_IOLBF", "1">, SimpleMacroDef<"_IONBF", "2">, + SimpleMacroDef<"EOF", "-1">, ]; let Types = ["size_t", "FILE", "cookie_io_functions_t"]; } diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index fe39ad8a9872..187165a2b8a5 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -324,6 +324,7 @@ if(LLVM_LIBC_FULL_BUILD) libc.src.stdio.ferror_unlocked libc.src.stdio.fflush libc.src.stdio.fopen + libc.src.stdio.fputs libc.src.stdio.fopencookie libc.src.stdio.fread libc.src.stdio.fread_unlocked @@ -333,6 +334,7 @@ if(LLVM_LIBC_FULL_BUILD) libc.src.stdio.fwrite_unlocked libc.src.stdio.fprintf libc.src.stdio.printf + libc.src.stdio.puts libc.src.stdio.stderr libc.src.stdio.stdout diff --git a/libc/docs/stdio.rst b/libc/docs/stdio.rst index 5467aac5060c..14067eb40958 100644 --- a/libc/docs/stdio.rst +++ b/libc/docs/stdio.rst @@ -72,7 +72,7 @@ These functions operate on files on the host's system, without using the ============= ========= Function_Name Available ============= ========= -remove +remove YES rename tmpnam ============= ========= @@ -91,7 +91,7 @@ fgets getchar fread YES (f)putc -(f)puts +(f)puts YES putchar fwrite YES ungetc diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td index fbd3cfa8a589..11d21ad831a3 100644 --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -496,6 +496,7 @@ def StdC : StandardSpec<"stdc"> { Macro<"_IOFBF">, Macro<"_IOLBF">, Macro<"_IONBF">, + Macro<"EOF">, ], // Macros [ // Types SizeTType, @@ -534,6 +535,17 @@ def StdC : StandardSpec<"stdc"> { [ArgSpec, ArgSpec] >, + FunctionSpec< + "fputs", + RetValSpec, + [ArgSpec, + ArgSpec] + >, + FunctionSpec< + "puts", + RetValSpec, + [ArgSpec] + >, FunctionSpec< "fread", RetValSpec, diff --git a/libc/src/__support/File/file.cpp b/libc/src/__support/File/file.cpp index e9766dbeb68f..b5d00b7a876d 100644 --- a/libc/src/__support/File/file.cpp +++ b/libc/src/__support/File/file.cpp @@ -131,7 +131,7 @@ size_t File::write_unlocked_fbf(const uint8_t *data, size_t len) { size_t File::write_unlocked_lbf(const uint8_t *data, size_t len) { constexpr uint8_t NEWLINE_CHAR = '\n'; size_t last_newline = len; - for (size_t i = len; i > 1; --i) { + for (size_t i = len; i >= 1; --i) { if (data[i - 1] == NEWLINE_CHAR) { last_newline = i - 1; break; diff --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt index 3aa096cf1c1c..e928847409d3 100644 --- a/libc/src/stdio/CMakeLists.txt +++ b/libc/src/stdio/CMakeLists.txt @@ -184,6 +184,31 @@ add_entrypoint_object( libc.src.__support.File.platform_file ) +add_entrypoint_object( + fputs + SRCS + fputs.cpp + HDRS + fputs.h + DEPENDS + libc.include.stdio + libc.src.__support.File.file + libc.src.__support.File.platform_file +) + + +add_entrypoint_object( + puts + SRCS + puts.cpp + HDRS + puts.h + DEPENDS + libc.include.stdio + libc.src.__support.File.file + libc.src.__support.File.platform_file +) + add_entrypoint_object( fseek SRCS diff --git a/libc/src/stdio/fputs.cpp b/libc/src/stdio/fputs.cpp new file mode 100644 index 000000000000..aee15c1a2ac1 --- /dev/null +++ b/libc/src/stdio/fputs.cpp @@ -0,0 +1,30 @@ +//===-- Implementation of fputs -------------------------------------------===// +// +// 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/fputs.h" +#include "src/__support/CPP/string_view.h" +#include "src/__support/File/file.h" + +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, fputs, + (const char *__restrict str, ::FILE *__restrict stream)) { + cpp::string_view str_view(str); + + size_t written = reinterpret_cast<__llvm_libc::File *>(stream)->write( + str, str_view.size()); + if (str_view.size() != written) { + // The stream should be in an error state in this case. + return EOF; + } + return 0; +} + +} // namespace __llvm_libc diff --git a/libc/src/stdio/fputs.h b/libc/src/stdio/fputs.h new file mode 100644 index 000000000000..2419fe8eeac7 --- /dev/null +++ b/libc/src/stdio/fputs.h @@ -0,0 +1,20 @@ +//===-- Implementation header of fputs --------------------------*- 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_FPUTS_H +#define LLVM_LIBC_SRC_STDIO_FPUTS_H + +#include + +namespace __llvm_libc { + +int fputs(const char *__restrict str, ::FILE *__restrict stream); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDIO_FPUTS_H diff --git a/libc/src/stdio/puts.cpp b/libc/src/stdio/puts.cpp new file mode 100644 index 000000000000..93132da8bed3 --- /dev/null +++ b/libc/src/stdio/puts.cpp @@ -0,0 +1,32 @@ +//===-- Implementation of puts --------------------------------------------===// +// +// 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/puts.h" +#include "src/__support/CPP/string_view.h" +#include "src/__support/File/file.h" + +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, puts, (const char *__restrict str)) { + cpp::string_view str_view(str); + size_t written = __llvm_libc::stdout->write(str, str_view.size()); + if (str_view.size() != written) { + // The stream should be in an error state in this case. + return EOF; + } + written = __llvm_libc::stdout->write("\n", 1); + if (1 != written) { + // The stream should be in an error state in this case. + return EOF; + } + return 0; +} + +} // namespace __llvm_libc diff --git a/libc/src/stdio/puts.h b/libc/src/stdio/puts.h new file mode 100644 index 000000000000..fa5d6f77356a --- /dev/null +++ b/libc/src/stdio/puts.h @@ -0,0 +1,20 @@ +//===-- Implementation header of puts ---------------------------*- 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_PUTS_H +#define LLVM_LIBC_SRC_STDIO_PUTS_H + +#include + +namespace __llvm_libc { + +int puts(const char *__restrict str); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDIO_PUTS_H diff --git a/libc/test/src/stdio/CMakeLists.txt b/libc/test/src/stdio/CMakeLists.txt index 961077d2a94c..962fa270204e 100644 --- a/libc/test/src/stdio/CMakeLists.txt +++ b/libc/test/src/stdio/CMakeLists.txt @@ -15,6 +15,7 @@ add_libc_unittest( libc.src.stdio.ferror libc.src.stdio.fflush libc.src.stdio.fopen + libc.src.stdio.fputs libc.src.stdio.fread libc.src.stdio.fseek libc.src.stdio.fwrite @@ -108,7 +109,16 @@ add_libc_unittest( printf_test.cpp DEPENDS libc.src.stdio.printf - libc.src.fenv.fesetround +) + +add_libc_unittest( + puts_test + SUITE + libc_stdio_unittests + SRCS + puts_test.cpp + DEPENDS + libc.src.stdio.puts ) add_libc_unittest( diff --git a/libc/test/src/stdio/fileop_test.cpp b/libc/test/src/stdio/fileop_test.cpp index ab98fa5eefc1..399cc799a938 100644 --- a/libc/test/src/stdio/fileop_test.cpp +++ b/libc/test/src/stdio/fileop_test.cpp @@ -12,6 +12,7 @@ #include "src/stdio/ferror.h" #include "src/stdio/fflush.h" #include "src/stdio/fopen.h" +#include "src/stdio/fputs.h" #include "src/stdio/fread.h" #include "src/stdio/fseek.h" #include "src/stdio/fwrite.h" @@ -66,10 +67,39 @@ TEST(LlvmLibcFILETest, SimpleFileOperations) { ASSERT_NE(errno, 0); errno = 0; + __llvm_libc::clearerr(file); + + // Should be an error to puts. + ASSERT_EQ(EOF, __llvm_libc::fputs(CONTENT, file)); + ASSERT_NE(__llvm_libc::ferror(file), 0); + ASSERT_NE(errno, 0); + errno = 0; + __llvm_libc::clearerr(file); ASSERT_EQ(__llvm_libc::ferror(file), 0); ASSERT_EQ(__llvm_libc::fclose(file), 0); + + // Now try puts. + file = __llvm_libc::fopen(FILENAME, "w"); + ASSERT_FALSE(file == nullptr); + // fputs returns a negative value on error (EOF) or any non-negative value on + // success. This assert checks that the return value is non-negative. + ASSERT_GE(__llvm_libc::fputs(CONTENT, file), 0); + + __llvm_libc::clearerr(file); + ASSERT_EQ(__llvm_libc::ferror(file), 0); + + ASSERT_EQ(0, __llvm_libc::fclose(file)); + + file = __llvm_libc::fopen(FILENAME, "r"); + ASSERT_FALSE(file == nullptr); + + ASSERT_EQ(__llvm_libc::fread(read_data, 1, sizeof(CONTENT) - 1, file), + sizeof(CONTENT) - 1); + read_data[sizeof(CONTENT) - 1] = '\0'; + ASSERT_STREQ(read_data, CONTENT); + ASSERT_EQ(__llvm_libc::fclose(file), 0); } TEST(LlvmLibcFILETest, FFlush) { diff --git a/libc/test/src/stdio/puts_test.cpp b/libc/test/src/stdio/puts_test.cpp new file mode 100644 index 000000000000..c32fdff3bcda --- /dev/null +++ b/libc/test/src/stdio/puts_test.cpp @@ -0,0 +1,28 @@ +//===-- Unittests for puts ---------------------------------------------===// +// +// 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/puts.h" + +#include "utils/UnitTest/Test.h" + +TEST(LlvmLibcPutsTest, PrintOut) { + int result; + + constexpr char simple[] = "A simple string"; + result = __llvm_libc::puts(simple); + EXPECT_GE(result, 0); + + // check that it appends a second newline at the end. + constexpr char numbers[] = "1234567890\n"; + result = __llvm_libc::puts(numbers); + EXPECT_GE(result, 0); + + constexpr char more[] = "1234 and more\n6789 and rhyme"; + result = __llvm_libc::puts(more); + EXPECT_GE(result, 0); +}