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 @@ -358,6 +358,7 @@ libc.src.stdio.ferror_unlocked libc.src.stdio.fflush libc.src.stdio.fopen + libc.src.stdio.fputc libc.src.stdio.fputs libc.src.stdio.fopencookie libc.src.stdio.fread @@ -368,6 +369,8 @@ libc.src.stdio.fwrite_unlocked libc.src.stdio.fprintf libc.src.stdio.printf + libc.src.stdio.putc + libc.src.stdio.putchar libc.src.stdio.puts libc.src.stdio.stderr libc.src.stdio.stdout diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -535,6 +535,24 @@ [ArgSpec, ArgSpec] >, + FunctionSpec< + "fputc", + RetValSpec, + [ArgSpec, + ArgSpec] + >, + FunctionSpec< + "putc", + RetValSpec, + [ArgSpec, + ArgSpec] + >, + FunctionSpec< + "putchar", + RetValSpec, + [ArgSpec, + ArgSpec] + >, FunctionSpec< "fputs", 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,42 @@ libc.src.__support.File.platform_file ) +add_entrypoint_object( + fputc + SRCS + fputc.cpp + HDRS + fputc.h + DEPENDS + libc.include.stdio + libc.src.__support.File.file + libc.src.__support.File.platform_file +) + +add_entrypoint_object( + putc + SRCS + putc.cpp + HDRS + putc.h + DEPENDS + libc.include.stdio + libc.src.__support.File.file + libc.src.__support.File.platform_file +) + +add_entrypoint_object( + putchar + SRCS + putchar.cpp + HDRS + putchar.h + DEPENDS + libc.include.stdio + libc.src.__support.File.file + libc.src.__support.File.platform_file +) + add_entrypoint_object( fputs SRCS diff --git a/libc/src/stdio/fputc.h b/libc/src/stdio/fputc.h new file mode 100644 --- /dev/null +++ b/libc/src/stdio/fputc.h @@ -0,0 +1,20 @@ +//===-- Implementation header of fputc --------------------------*- 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_FPUTC_H +#define LLVM_LIBC_SRC_STDIO_FPUTC_H + +#include + +namespace __llvm_libc { + +int fputc(int c, ::FILE *stream); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDIO_FPUTC_H diff --git a/libc/src/stdio/fputc.cpp b/libc/src/stdio/fputc.cpp new file mode 100644 --- /dev/null +++ b/libc/src/stdio/fputc.cpp @@ -0,0 +1,26 @@ +//===-- Implementation of fputc -------------------------------------------===// +// +// 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/fputc.h" +#include "src/__support/File/file.h" + +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, fputc, (int c, ::FILE *stream)) { + unsigned char uc = static_cast(c); + size_t written = reinterpret_cast<__llvm_libc::File *>(stream)->write(&uc, 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/putc.h b/libc/src/stdio/putc.h new file mode 100644 --- /dev/null +++ b/libc/src/stdio/putc.h @@ -0,0 +1,20 @@ +//===-- Implementation header of putc ---------------------------*- 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_PUTC_H +#define LLVM_LIBC_SRC_STDIO_PUTC_H + +#include + +namespace __llvm_libc { + +int putc(int c, ::FILE *stream); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDIO_PUTC_H diff --git a/libc/src/stdio/putc.cpp b/libc/src/stdio/putc.cpp new file mode 100644 --- /dev/null +++ b/libc/src/stdio/putc.cpp @@ -0,0 +1,26 @@ +//===-- Implementation of putc --------------------------------------------===// +// +// 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/putc.h" +#include "src/__support/File/file.h" + +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, putc, (int c, ::FILE *stream)) { + unsigned char uc = static_cast(c); + size_t written = reinterpret_cast<__llvm_libc::File *>(stream)->write(&uc, 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/putchar.h b/libc/src/stdio/putchar.h new file mode 100644 --- /dev/null +++ b/libc/src/stdio/putchar.h @@ -0,0 +1,20 @@ +//===-- Implementation header of putchar ------------------------*- 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_PUTCHAR_H +#define LLVM_LIBC_SRC_STDIO_PUTCHAR_H + +#include + +namespace __llvm_libc { + +int putchar(int c); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDIO_PUTCHAR_H diff --git a/libc/src/stdio/putchar.cpp b/libc/src/stdio/putchar.cpp new file mode 100644 --- /dev/null +++ b/libc/src/stdio/putchar.cpp @@ -0,0 +1,26 @@ +//===-- Implementation of putchar -----------------------------------------===// +// +// 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/putchar.h" +#include "src/__support/File/file.h" + +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, putchar, (int c)) { + unsigned char uc = static_cast(c); + size_t written = __llvm_libc::stdout->write(&uc, 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/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 @@ -121,6 +121,20 @@ libc.src.stdio.puts ) +add_libc_unittest( + putc_test + SUITE + libc_stdio_unittests + SRCS + putc_test.cpp + DEPENDS + libc.src.stdio.putc + libc.src.stdio.fclose + libc.src.stdio.ferror + libc.src.stdio.fopen + libc.src.stdio.fread +) + if(${LIBC_TARGET_OS} STREQUAL "linux") add_libc_unittest( remove_test diff --git a/libc/test/src/stdio/putc_test.cpp b/libc/test/src/stdio/putc_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/stdio/putc_test.cpp @@ -0,0 +1,47 @@ +//===-- Unittests for putc ---------------------------------------------===// +// +// 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/putc.h" + +#include "utils/UnitTest/Test.h" + +#include +#include + +TEST(LlvmLibcPutcTest, WriteToFile) { + constexpr char FILENAME[] = "testdata/putc_output.test"; + ::FILE *file = __llvm_libc::fopen(FILENAME, "w"); + ASSERT_FALSE(file == nullptr); + + constexpr char simple[] = "simple letters"; + for (size_t i = 0; i < sizeof(simple); ++i) { + ASSERT_EQ(__llvm_libc::putc(simple[i], file), 0); + } + + 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::ferror(file), 0); + EXPECT_LT(__llvm_libc::putc('L', file), 0); + + ASSERT_EQ(__llvm_libc::fclose(file), 0); +}