diff --git a/libc/config/linux/api.td b/libc/config/linux/api.td --- a/libc/config/linux/api.td +++ b/libc/config/linux/api.td @@ -153,6 +153,7 @@ 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 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -324,6 +324,7 @@ 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 diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -496,6 +496,7 @@ Macro<"_IOFBF">, Macro<"_IOLBF">, Macro<"_IONBF">, + Macro<"EOF">, ], // Macros [ // Types SizeTType, @@ -534,6 +535,12 @@ [ArgSpec, ArgSpec] >, + FunctionSpec< + "fwrite", + RetValSpec, + [ArgSpec, + ArgSpec] + >, FunctionSpec< "fread", 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 @@ -184,6 +184,19 @@ 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( fseek SRCS diff --git a/libc/src/stdio/fputs.h b/libc/src/stdio/fputs.h new file mode 100644 --- /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/fputs.cpp b/libc/src/stdio/fputs.cpp new file mode 100644 --- /dev/null +++ b/libc/src/stdio/fputs.cpp @@ -0,0 +1,31 @@ +//===-- 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/File/file.h" + +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, fputs, + (const char *__restrict str, ::FILE *__restrict stream)) { + size_t str_len = 0; + for (; str[str_len] != '\0'; ++str_len) { + ; + } + size_t written = + reinterpret_cast<__llvm_libc::File *>(stream)->write(str, str_len); + if (str_len != written) { + // The stream should be in an error state in this case. + return EOF; + } + return static_cast(str_len); +} + +} // 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 @@ -15,6 +15,7 @@ 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 diff --git a/libc/test/src/stdio/fileop_test.cpp b/libc/test/src/stdio/fileop_test.cpp --- 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,9 +67,38 @@ 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 positive value on + // success. + ASSERT_LE(0, __llvm_libc::fputs(CONTENT, file)); + __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); }