https://github.com/petrhosek updated 
https://github.com/llvm/llvm-project/pull/168931

>From b8dfdd0b29af8e7e34bdb3aef30a0a8e7e5db428 Mon Sep 17 00:00:00 2001
From: Petr Hosek <[email protected]>
Date: Thu, 20 Nov 2025 08:19:46 +0000
Subject: [PATCH 1/5] [libc] Support opaque FILE* on baremetal

This change expands the stdio support on baremetal to support opaque
FILE*. This builds on top of the existing baremetal embedding API; we
treat the standard FILE* streams as pointers that point to the cookie
symbols which are a part of the embedding API. This also allows users
to define their own FILE* streams, but we don't (yet) support the API
that return FILE* such as fopen or fopencookie.
---
 clang/cmake/caches/Fuchsia-stage2.cmake       |   6 +-
 libc/config/baremetal/aarch64/entrypoints.txt |   8 +
 libc/config/baremetal/arm/entrypoints.txt     |   8 +
 libc/config/baremetal/riscv/entrypoints.txt   |   8 +
 libc/src/__support/OSUtil/baremetal/io.cpp    |  42 +----
 libc/src/__support/OSUtil/baremetal/io.h      |  39 +++++
 libc/src/stdio/baremetal/CMakeLists.txt       | 151 +++++++++++++++---
 libc/src/stdio/baremetal/feof.cpp             |  24 +++
 libc/src/stdio/baremetal/ferror.cpp           |  23 +++
 libc/src/stdio/baremetal/fgetc.cpp            |   0
 libc/src/stdio/baremetal/fgets.cpp            |   0
 libc/src/stdio/baremetal/fprintf.cpp          |  34 ++++
 libc/src/stdio/baremetal/fputc.cpp            |  22 +++
 libc/src/stdio/baremetal/fputc_internal.h     |  29 ++++
 libc/src/stdio/baremetal/fputs.cpp            |  23 +++
 libc/src/stdio/baremetal/fputs_internal.h     |  29 ++++
 libc/src/stdio/baremetal/fwrite_internal.h    |  27 ++++
 libc/src/stdio/baremetal/printf.cpp           |  46 +-----
 libc/src/stdio/baremetal/putchar.cpp          |  13 +-
 libc/src/stdio/baremetal/puts.cpp             |  12 +-
 libc/src/stdio/baremetal/vfprintf.cpp         |  30 ++++
 libc/src/stdio/baremetal/vfprintf_internal.h  |  69 ++++++++
 libc/src/stdio/baremetal/vprintf.cpp          |  47 +-----
 23 files changed, 530 insertions(+), 160 deletions(-)
 create mode 100644 libc/src/stdio/baremetal/feof.cpp
 create mode 100644 libc/src/stdio/baremetal/ferror.cpp
 create mode 100644 libc/src/stdio/baremetal/fgetc.cpp
 create mode 100644 libc/src/stdio/baremetal/fgets.cpp
 create mode 100644 libc/src/stdio/baremetal/fprintf.cpp
 create mode 100644 libc/src/stdio/baremetal/fputc.cpp
 create mode 100644 libc/src/stdio/baremetal/fputc_internal.h
 create mode 100644 libc/src/stdio/baremetal/fputs.cpp
 create mode 100644 libc/src/stdio/baremetal/fputs_internal.h
 create mode 100644 libc/src/stdio/baremetal/fwrite_internal.h
 create mode 100644 libc/src/stdio/baremetal/vfprintf.cpp
 create mode 100644 libc/src/stdio/baremetal/vfprintf_internal.h

diff --git a/clang/cmake/caches/Fuchsia-stage2.cmake 
b/clang/cmake/caches/Fuchsia-stage2.cmake
index be3d0cfa2e657..1e7b3e1b71373 100644
--- a/clang/cmake/caches/Fuchsia-stage2.cmake
+++ b/clang/cmake/caches/Fuchsia-stage2.cmake
@@ -341,7 +341,7 @@ foreach(target 
armv6m-none-eabi;armv7m-none-eabi;armv7em-none-eabi;armv8m.main-n
   foreach(lang C;CXX;ASM)
     # TODO: The preprocessor defines workaround various issues in libc and 
libc++ integration.
     # These should be addressed and removed over time.
-    set(RUNTIMES_${target}_CMAKE_${lang}_local_flags "--target=${target} 
-Wno-atomic-alignment \"-Dvfprintf(stream, format, vlist)=vprintf(format, 
vlist)\" \"-Dfprintf(stream, format, ...)=printf(format)\" \"-Dfputs(string, 
stream)=puts(string)\" -D_LIBCPP_PRINT=1")
+    set(RUNTIMES_${target}_CMAKE_${lang}_local_flags "--target=${target} 
-Wno-atomic-alignment")
     if(NOT ${target} STREQUAL "aarch64-none-elf")
       set(RUNTIMES_${target}_CMAKE_${lang}_local_flags 
"${RUNTIMES_${target}_CMAKE_${lang}_local_flags} -mthumb")
     endif()
@@ -372,6 +372,7 @@ foreach(target 
armv6m-none-eabi;armv7m-none-eabi;armv7em-none-eabi;armv8m.main-n
   set(RUNTIMES_${target}_LIBCXX_ENABLE_RTTI OFF CACHE BOOL "")
   set(RUNTIMES_${target}_LIBCXX_ENABLE_THREADS OFF CACHE BOOL "")
   set(RUNTIMES_${target}_LIBCXX_ENABLE_MONOTONIC_CLOCK OFF CACHE BOOL "")
+  set(RUNTIMES_${target}_LIBCXX_HAS_TERMINAL_AVAILABLE OFF CACHE BOOL "")
   set(RUNTIMES_${target}_LIBCXX_USE_COMPILER_RT ON CACHE BOOL "")
   set(RUNTIMES_${target}_LLVM_INCLUDE_TESTS OFF CACHE BOOL "")
   set(RUNTIMES_${target}_LLVM_ENABLE_ASSERTIONS OFF CACHE BOOL "")
@@ -406,7 +407,7 @@ foreach(target riscv32-unknown-elf)
   foreach(lang C;CXX;ASM)
     # TODO: The preprocessor defines workaround various issues in libc and 
libc++ integration.
     # These should be addressed and removed over time.
-    set(RUNTIMES_${target}_CMAKE_${lang}_FLAGS "--target=${target} 
-march=rv32imafc -mabi=ilp32f -Wno-atomic-alignment \"-Dvfprintf(stream, 
format, vlist)=vprintf(format, vlist)\" \"-Dfprintf(stream, format, 
...)=printf(format)\" \"-Dfputs(string, stream)=puts(string)\" 
-D_LIBCPP_PRINT=1" CACHE STRING "")
+    set(RUNTIMES_${target}_CMAKE_${lang}_FLAGS "--target=${target} 
-march=rv32imafc -mabi=ilp32f -Wno-atomic-alignment" CACHE STRING "")
   endforeach()
   foreach(type SHARED;MODULE;EXE)
     set(RUNTIMES_${target}_CMAKE_${type}_LINKER_FLAGS "-fuse-ld=lld" CACHE 
STRING "")
@@ -427,6 +428,7 @@ foreach(target riscv32-unknown-elf)
   set(RUNTIMES_${target}_LIBCXX_ENABLE_RTTI OFF CACHE BOOL "")
   set(RUNTIMES_${target}_LIBCXX_ENABLE_THREADS OFF CACHE BOOL "")
   set(RUNTIMES_${target}_LIBCXX_ENABLE_MONOTONIC_CLOCK OFF CACHE BOOL "")
+  set(RUNTIMES_${target}_LIBCXX_HAS_TERMINAL_AVAILABLE OFF CACHE BOOL "")
   set(RUNTIMES_${target}_LIBCXX_USE_COMPILER_RT ON CACHE BOOL "")
   set(RUNTIMES_${target}_LLVM_INCLUDE_TESTS OFF CACHE BOOL "")
   set(RUNTIMES_${target}_LLVM_ENABLE_ASSERTIONS OFF CACHE BOOL "")
diff --git a/libc/config/baremetal/aarch64/entrypoints.txt 
b/libc/config/baremetal/aarch64/entrypoints.txt
index c69ab3d0bb37c..4c6b6dfc4d669 100644
--- a/libc/config/baremetal/aarch64/entrypoints.txt
+++ b/libc/config/baremetal/aarch64/entrypoints.txt
@@ -124,8 +124,15 @@ set(TARGET_LIBC_ENTRYPOINTS
 
     # stdio.h entrypoints
     libc.src.stdio.asprintf
+    libc.src.stdio.feof
+    libc.src.stdio.ferror
+    libc.src.stdio.fprintf
+    libc.src.stdio.fputc
+    libc.src.stdio.fputs
+    libc.src.stdio.fwrite
     libc.src.stdio.getchar
     libc.src.stdio.printf
+    libc.src.stdio.putc
     libc.src.stdio.putchar
     libc.src.stdio.puts
     libc.src.stdio.remove
@@ -134,6 +141,7 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.stdio.sprintf
     libc.src.stdio.sscanf
     libc.src.stdio.vasprintf
+    libc.src.stdio.vfprintf
     libc.src.stdio.vprintf
     libc.src.stdio.vscanf
     libc.src.stdio.vsnprintf
diff --git a/libc/config/baremetal/arm/entrypoints.txt 
b/libc/config/baremetal/arm/entrypoints.txt
index c566f8ad08c8e..0847a0905a6f7 100644
--- a/libc/config/baremetal/arm/entrypoints.txt
+++ b/libc/config/baremetal/arm/entrypoints.txt
@@ -124,8 +124,15 @@ set(TARGET_LIBC_ENTRYPOINTS
 
     # stdio.h entrypoints
     libc.src.stdio.asprintf
+    libc.src.stdio.feof
+    libc.src.stdio.ferror
+    libc.src.stdio.fprintf
+    libc.src.stdio.fputc
+    libc.src.stdio.fputs
+    libc.src.stdio.fwrite
     libc.src.stdio.getchar
     libc.src.stdio.printf
+    libc.src.stdio.putc
     libc.src.stdio.putchar
     libc.src.stdio.puts
     libc.src.stdio.remove
@@ -134,6 +141,7 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.stdio.sprintf
     libc.src.stdio.sscanf
     libc.src.stdio.vasprintf
+    libc.src.stdio.vfprintf
     libc.src.stdio.vprintf
     libc.src.stdio.vscanf
     libc.src.stdio.vsnprintf
diff --git a/libc/config/baremetal/riscv/entrypoints.txt 
b/libc/config/baremetal/riscv/entrypoints.txt
index a6aef96e91698..9a5c73b81e87f 100644
--- a/libc/config/baremetal/riscv/entrypoints.txt
+++ b/libc/config/baremetal/riscv/entrypoints.txt
@@ -124,8 +124,15 @@ set(TARGET_LIBC_ENTRYPOINTS
 
     # stdio.h entrypoints
     libc.src.stdio.asprintf
+    libc.src.stdio.feof
+    libc.src.stdio.ferror
+    libc.src.stdio.fprintf
+    libc.src.stdio.fputc
+    libc.src.stdio.fputs
+    libc.src.stdio.fwrite
     libc.src.stdio.getchar
     libc.src.stdio.printf
+    libc.src.stdio.putc
     libc.src.stdio.putchar
     libc.src.stdio.puts
     libc.src.stdio.remove
@@ -134,6 +141,7 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.stdio.sprintf
     libc.src.stdio.sscanf
     libc.src.stdio.vasprintf
+    libc.src.stdio.vfprintf
     libc.src.stdio.vprintf
     libc.src.stdio.vscanf
     libc.src.stdio.vsnprintf
diff --git a/libc/src/__support/OSUtil/baremetal/io.cpp 
b/libc/src/__support/OSUtil/baremetal/io.cpp
index 2a9ef6bfa6579..2978d701017a5 100644
--- a/libc/src/__support/OSUtil/baremetal/io.cpp
+++ b/libc/src/__support/OSUtil/baremetal/io.cpp
@@ -8,49 +8,15 @@
 
 #include "io.h"
 
+#include "hdr/types/FILE.h"
 #include "src/__support/CPP/string_view.h"
 #include "src/__support/macros/config.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
-// These are intended to be provided by the vendor.
-//
-// The signature of these types and functions intentionally match `fopencookie`
-// which allows the following:
-//
-// ```
-// struct __llvm_libc_stdio_cookie { ... };
-// ...
-// struct __llvm_libc_stdio_cookie __llvm_libc_stdin_cookie;
-// cookie_io_functions_t stdin_func = { .read = __llvm_libc_stdio_read };
-// FILE *stdin = fopencookie(&__llvm_libc_stdin_cookie, "r", stdin_func);
-// ...
-// struct __llvm_libc_stdio_cookie __llvm_libc_stdout_cookie;
-// cookie_io_functions_t stdout_func = { .write = __llvm_libc_stdio_write };
-// FILE *stdout = fopencookie(&__llvm_libc_stdout_cookie, "w", stdout_func);
-// ...
-// struct __llvm_libc_stdio_cookie __llvm_libc_stderr_cookie;
-// cookie_io_functions_t stderr_func = { .write = __llvm_libc_stdio_write };
-// FILE *stderr = fopencookie(&__llvm_libc_stderr_cookie, "w", stderr_func);
-// ```
-//
-// At the same time, implementation of functions like `printf` and `scanf` can
-// use `__llvm_libc_stdio_read` and `__llvm_libc_stdio_write` directly to avoid
-// the extra indirection.
-//
-// All three symbols `__llvm_libc_stdin_cookie`, `__llvm_libc_stdout_cookie`,
-// and `__llvm_libc_stderr_cookie` must be provided, even if they don't point
-// at anything.
-
-struct __llvm_libc_stdio_cookie;
-
-extern "C" struct __llvm_libc_stdio_cookie __llvm_libc_stdin_cookie;
-extern "C" struct __llvm_libc_stdio_cookie __llvm_libc_stdout_cookie;
-extern "C" struct __llvm_libc_stdio_cookie __llvm_libc_stderr_cookie;
-
-extern "C" ssize_t __llvm_libc_stdio_read(void *cookie, char *buf, size_t 
size);
-extern "C" ssize_t __llvm_libc_stdio_write(void *cookie, const char *buf,
-                                           size_t size);
+extern "C" FILE *stdin = reinterpret_cast<FILE *>(&__llvm_libc_stdin_cookie);
+extern "C" FILE *stdout = reinterpret_cast<FILE *>(&__llvm_libc_stdout_cookie);
+extern "C" FILE *stderr = reinterpret_cast<FILE *>(&__llvm_libc_stderr_cookie);
 
 ssize_t read_from_stdin(char *buf, size_t size) {
   return __llvm_libc_stdio_read(static_cast<void *>(&__llvm_libc_stdin_cookie),
diff --git a/libc/src/__support/OSUtil/baremetal/io.h 
b/libc/src/__support/OSUtil/baremetal/io.h
index aed34ec7e62e3..2605fd45776b9 100644
--- a/libc/src/__support/OSUtil/baremetal/io.h
+++ b/libc/src/__support/OSUtil/baremetal/io.h
@@ -16,6 +16,45 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
+// These are intended to be provided by the vendor.
+//
+// The signature of these types and functions intentionally match `fopencookie`
+// which allows the following:
+//
+// ```
+// struct __llvm_libc_stdio_cookie { ... };
+// ...
+// struct __llvm_libc_stdio_cookie __llvm_libc_stdin_cookie;
+// cookie_io_functions_t stdin_func = { .read = __llvm_libc_stdio_read };
+// FILE *stdin = fopencookie(&__llvm_libc_stdin_cookie, "r", stdin_func);
+// ...
+// struct __llvm_libc_stdio_cookie __llvm_libc_stdout_cookie;
+// cookie_io_functions_t stdout_func = { .write = __llvm_libc_stdio_write };
+// FILE *stdout = fopencookie(&__llvm_libc_stdout_cookie, "w", stdout_func);
+// ...
+// struct __llvm_libc_stdio_cookie __llvm_libc_stderr_cookie;
+// cookie_io_functions_t stderr_func = { .write = __llvm_libc_stdio_write };
+// FILE *stderr = fopencookie(&__llvm_libc_stderr_cookie, "w", stderr_func);
+// ```
+//
+// At the same time, implementation of functions like `printf` and `scanf` can
+// use `__llvm_libc_stdio_read` and `__llvm_libc_stdio_write` directly to avoid
+// the extra indirection.
+//
+// All three symbols `__llvm_libc_stdin_cookie`, `__llvm_libc_stdout_cookie`,
+// and `__llvm_libc_stderr_cookie` must be provided, even if they don't point
+// at anything.
+
+struct __llvm_libc_stdio_cookie;
+
+extern "C" struct __llvm_libc_stdio_cookie __llvm_libc_stdin_cookie;
+extern "C" struct __llvm_libc_stdio_cookie __llvm_libc_stdout_cookie;
+extern "C" struct __llvm_libc_stdio_cookie __llvm_libc_stderr_cookie;
+
+extern "C" ssize_t __llvm_libc_stdio_read(void *cookie, char *buf, size_t 
size);
+extern "C" ssize_t __llvm_libc_stdio_write(void *cookie, const char *buf,
+                                           size_t size);
+
 ssize_t read_from_stdin(char *buf, size_t size);
 void write_to_stderr(cpp::string_view msg);
 void write_to_stdout(cpp::string_view msg);
diff --git a/libc/src/stdio/baremetal/CMakeLists.txt 
b/libc/src/stdio/baremetal/CMakeLists.txt
index bfeff0e2b5880..e920b4a1a30f4 100644
--- a/libc/src/stdio/baremetal/CMakeLists.txt
+++ b/libc/src/stdio/baremetal/CMakeLists.txt
@@ -1,3 +1,111 @@
+add_header_library(
+  fputc_internal
+  HDRS
+    fputc_internal.h
+  DEPENDS
+    libc.hdr.types.FILE
+    libc.src.__support.CPP.string_view
+    libc.src.__support.OSUtil.osutil
+)
+
+add_header_library(
+  fputs_internal
+  HDRS
+    fputs_internal.h
+  DEPENDS
+    libc.hdr.types.FILE
+    libc.src.__support.CPP.string_view
+    libc.src.__support.OSUtil.osutil
+)
+
+add_header_library(
+  fwrite_internal
+  HDRS
+    fwrite_internal.h
+  DEPENDS
+    libc.hdr.types.FILE
+    libc.src.__support.OSUtil.osutil
+)
+
+add_header_library(
+  vfprintf_internal
+  HDRS
+    vfprintf_internal.h
+  DEPENDS
+    libc.hdr.types.FILE
+    libc.hdr.stdio_macros
+    libc.src.__support.arg_list
+    libc.src.__support.CPP.limits
+    libc.src.__support.CPP.string_view
+    libc.src.__support.libc_errno
+    libc.src.__support.OSUtil.osutil
+    libc.src.stdio.printf_core.printf_main
+    libc.src.stdio.printf_core.writer
+    libc.src.stdio.printf_core.error_mapper
+    libc.src.stdio.printf_core.core_structs
+)
+
+add_entrypoint_object(
+  feof
+  SRCS
+    feof.cpp
+  HDRS
+    ../feof.h
+  DEPENDS
+    libc.hdr.types.FILE
+)
+
+add_entrypoint_object(
+  ferror
+  SRCS
+    ferror.cpp
+  HDRS
+    ../ferror.h
+  DEPENDS
+    libc.hdr.types.FILE
+)
+
+add_entrypoint_object(
+  fprintf
+  SRCS
+    fprintf.cpp
+  HDRS
+    ../fprintf.h
+  DEPENDS
+    .vfprintf_internal
+    libc.src.__support.arg_list
+)
+
+add_entrypoint_object(
+  fputc
+  SRCS
+    fputc.cpp
+  HDRS
+    ../fputc.h
+  DEPENDS
+    .fputc_internal
+)
+
+add_entrypoint_object(
+  fputs
+  SRCS
+    fputs.cpp
+  HDRS
+    ../fputc.h
+  DEPENDS
+    .fputs_internal
+)
+
+add_entrypoint_object(
+  fwrite
+  SRCS
+    fwrite.cpp
+  HDRS
+    ../fwrite.h
+  DEPENDS
+    .fwrite_internal
+)
+
 add_entrypoint_object(
   getchar
   SRCS
@@ -27,14 +135,8 @@ add_entrypoint_object(
   HDRS
     ../printf.h
   DEPENDS
-    libc.src.stdio.printf_core.printf_main
-    libc.src.stdio.printf_core.writer
-    libc.src.stdio.printf_core.error_mapper
-    libc.src.stdio.printf_core.core_structs
+    .vfprintf_internal
     libc.src.__support.arg_list
-    libc.src.__support.OSUtil.osutil
-    libc.src.__support.libc_errno
-    libc.src.__support.CPP.limits
 )
 
 add_entrypoint_object(
@@ -44,8 +146,17 @@ add_entrypoint_object(
   HDRS
     ../putchar.h
   DEPENDS
-    libc.src.__support.OSUtil.osutil
-    libc.src.__support.CPP.string_view
+    .fputc_internal
+)
+
+add_entrypoint_object(
+  putc
+  SRCS
+    putc.cpp
+  HDRS
+    ../putc.h
+  DEPENDS
+    .fputc_internal
 )
 
 add_entrypoint_object(
@@ -55,8 +166,7 @@ add_entrypoint_object(
   HDRS
     ../puts.h
   DEPENDS
-    libc.src.__support.OSUtil.osutil
-    libc.src.__support.CPP.string_view
+    .fputs_internal
 )
 
 add_header_library(
@@ -82,6 +192,17 @@ add_entrypoint_object(
     libc.src.__support.OSUtil.osutil
 )
 
+add_entrypoint_object(
+  vfprintf
+  SRCS
+    vfprintf.cpp
+  HDRS
+    ../vfprintf.h
+  DEPENDS
+    .vfprintf_internal
+    libc.src.__support.arg_list
+)
+
 add_entrypoint_object(
   vprintf
   SRCS
@@ -89,14 +210,8 @@ add_entrypoint_object(
   HDRS
     ../vprintf.h
   DEPENDS
-    libc.src.stdio.printf_core.printf_main
-    libc.src.stdio.printf_core.writer
-    libc.src.stdio.printf_core.error_mapper
-    libc.src.stdio.printf_core.core_structs
+    .vfprintf_internal
     libc.src.__support.arg_list
-    libc.src.__support.OSUtil.osutil
-    libc.src.__support.libc_errno
-    libc.src.__support.CPP.limits
 )
 
 add_entrypoint_object(
diff --git a/libc/src/stdio/baremetal/feof.cpp 
b/libc/src/stdio/baremetal/feof.cpp
new file mode 100644
index 0000000000000..3c241edeb47f6
--- /dev/null
+++ b/libc/src/stdio/baremetal/feof.cpp
@@ -0,0 +1,24 @@
+//===-- Implementation of feof for baremetal --------------------*- 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/feof.h"
+
+#include "hdr/types/FILE.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, feof, (::FILE * stream)) {
+  (void)stream;
+  // TODO: Shall we have an embeddeding API for feof?
+  return 0;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
+
diff --git a/libc/src/stdio/baremetal/ferror.cpp 
b/libc/src/stdio/baremetal/ferror.cpp
new file mode 100644
index 0000000000000..5af5a1efdcd9a
--- /dev/null
+++ b/libc/src/stdio/baremetal/ferror.cpp
@@ -0,0 +1,23 @@
+//===-- Implementation of ferror for baremetal ------------------*- 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/ferror.h"
+
+#include "hdr/types/FILE.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, ferror, (::FILE * stream)) {
+  (void)stream;
+  // TODO: Shall we have an embeddeding API for ferror?
+  return 0;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/baremetal/fgetc.cpp 
b/libc/src/stdio/baremetal/fgetc.cpp
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git a/libc/src/stdio/baremetal/fgets.cpp 
b/libc/src/stdio/baremetal/fgets.cpp
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git a/libc/src/stdio/baremetal/fprintf.cpp 
b/libc/src/stdio/baremetal/fprintf.cpp
new file mode 100644
index 0000000000000..fc96c56ec77ed
--- /dev/null
+++ b/libc/src/stdio/baremetal/fprintf.cpp
@@ -0,0 +1,34 @@
+//===-- Implementation of fprintf for baremetal -----------------*- 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 "hdr/types/FILE.h"
+#include "src/__support/arg_list.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/stdio/baremetal/vfprintf_internal.h"
+
+#include <stdarg.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+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);
+
+  return vfprintf_internal(stream, format, args);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/baremetal/fputc.cpp 
b/libc/src/stdio/baremetal/fputc.cpp
new file mode 100644
index 0000000000000..1b4086466ee7b
--- /dev/null
+++ b/libc/src/stdio/baremetal/fputc.cpp
@@ -0,0 +1,22 @@
+//===-- Implementation of fputc for baremetal -------------------*- 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/fputc.h"
+
+#include "hdr/types/FILE.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/stdio/baremetal/fputc_internal.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, fputc, (int c, ::FILE *stream)) {
+  return fputc_internal(c, stream);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/baremetal/fputc_internal.h 
b/libc/src/stdio/baremetal/fputc_internal.h
new file mode 100644
index 0000000000000..e578d6584bcfe
--- /dev/null
+++ b/libc/src/stdio/baremetal/fputc_internal.h
@@ -0,0 +1,29 @@
+//===-- Baremetal 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_BAREMETAL_FPUTC_INTERNAL_H
+#define LLVM_LIBC_SRC_STDIO_BAREMETAL_FPUTC_INTERNAL_H
+
+#include "hdr/types/FILE.h"
+#include "src/__support/CPP/string_view.h"
+#include "src/__support/OSUtil/io.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LIBC_INLINE int fputc_internal(int c, ::FILE *stream) {
+  char ch = static_cast<char>(c);
+  cpp::string_view str_view(&ch, 1);
+  __llvm_libc_stdio_write(stream, str_view.data(), str_view.size());
+  return 0;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDIO_BAREMETAL_FPUTC_INTERNAL_H
diff --git a/libc/src/stdio/baremetal/fputs.cpp 
b/libc/src/stdio/baremetal/fputs.cpp
new file mode 100644
index 0000000000000..daed53182686e
--- /dev/null
+++ b/libc/src/stdio/baremetal/fputs.cpp
@@ -0,0 +1,23 @@
+//===-- Implementation of fputs for baremetal -------------------*- 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/fputs.h"
+
+#include "hdr/types/FILE.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/stdio/baremetal/fputs_internal.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, fputs,
+                   (const char *__restrict str, ::FILE *__restrict stream)) {
+  return fputs_internal(str, stream);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/baremetal/fputs_internal.h 
b/libc/src/stdio/baremetal/fputs_internal.h
new file mode 100644
index 0000000000000..d4032a1088eb4
--- /dev/null
+++ b/libc/src/stdio/baremetal/fputs_internal.h
@@ -0,0 +1,29 @@
+//===-- Baremetal 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_BAREMETAL_FPUTS_INTERNAL_H
+#define LLVM_LIBC_SRC_STDIO_BAREMETAL_FPUTS_INTERNAL_H
+
+#include "hdr/types/FILE.h"
+#include "src/__support/CPP/string_view.h"
+#include "src/__support/OSUtil/io.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LIBC_INLINE int fputs_internal(const char *str, ::FILE *stream) {
+  cpp::string_view str_view(str);
+  __llvm_libc_stdio_write(stream, str_view.data(), str_view.size());
+  __llvm_libc_stdio_write(stream, "\n", 1);
+  return 0;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDIO_BAREMETAL_FPUTS_INTERNAL_H
diff --git a/libc/src/stdio/baremetal/fwrite_internal.h 
b/libc/src/stdio/baremetal/fwrite_internal.h
new file mode 100644
index 0000000000000..f4a07c382b362
--- /dev/null
+++ b/libc/src/stdio/baremetal/fwrite_internal.h
@@ -0,0 +1,27 @@
+//===-- Baremetal implementation header of fwrite ---------------*- 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_BAREMETAL_FWRITE_INTERNAL_H
+#define LLVM_LIBC_SRC_STDIO_BAREMETAL_FWRITE_INTERNAL_H
+
+#include "hdr/types/FILE.h"
+#include "src/__support/OSUtil/io.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LIBC_INLINE size_t fwrite_internal(const void *buffer, size_t size, size_t 
nmemb,
+                                   ::FILE *stream) {
+  __llvm_libc_stdio_write(stream, reinterpret_cast<const char *>(buffer), size 
* nmemb);
+  return nmemb;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDIO_BAREMETAL_FWRITE_INTERNAL_H
diff --git a/libc/src/stdio/baremetal/printf.cpp 
b/libc/src/stdio/baremetal/printf.cpp
index 5a9b19ff20471..637f8aacc935e 100644
--- a/libc/src/stdio/baremetal/printf.cpp
+++ b/libc/src/stdio/baremetal/printf.cpp
@@ -7,30 +7,16 @@
 
//===----------------------------------------------------------------------===//
 
 #include "src/stdio/printf.h"
-#include "src/__support/CPP/limits.h"
-#include "src/__support/OSUtil/io.h"
+
 #include "src/__support/arg_list.h"
-#include "src/__support/libc_errno.h"
+#include "src/__support/common.h"
 #include "src/__support/macros/config.h"
-#include "src/stdio/printf_core/core_structs.h"
-#include "src/stdio/printf_core/error_mapper.h"
-#include "src/stdio/printf_core/printf_main.h"
-#include "src/stdio/printf_core/writer.h"
+#include "src/stdio/baremetal/vfprintf_internal.h"
 
 #include <stdarg.h>
-#include <stddef.h>
 
 namespace LIBC_NAMESPACE_DECL {
 
-namespace {
-
-LIBC_INLINE int stdout_write_hook(cpp::string_view new_str, void *) {
-  write_to_stdout(new_str);
-  return printf_core::WRITE_OK;
-}
-
-} // namespace
-
 LLVM_LIBC_FUNCTION(int, printf, (const char *__restrict format, ...)) {
   va_list vlist;
   va_start(vlist, format);
@@ -38,32 +24,8 @@ LLVM_LIBC_FUNCTION(int, printf, (const char *__restrict 
format, ...)) {
                                  // and pointer semantics, as well as handling
                                  // destruction automatically.
   va_end(vlist);
-  static constexpr size_t BUFF_SIZE = 1024;
-  char buffer[BUFF_SIZE];
-
-  printf_core::WriteBuffer<printf_core::WriteMode::FLUSH_TO_STREAM> wb(
-      buffer, BUFF_SIZE, &stdout_write_hook, nullptr);
-  printf_core::Writer<printf_core::WriteMode::FLUSH_TO_STREAM> writer(wb);
-
-  auto retval = printf_core::printf_main(&writer, format, args);
-  if (!retval.has_value()) {
-    libc_errno = printf_core::internal_error_to_errno(retval.error());
-    return -1;
-  }
-
-  int flushval = wb.overflow_write("");
-  if (flushval != printf_core::WRITE_OK) {
-    libc_errno = printf_core::internal_error_to_errno(-flushval);
-    return -1;
-  }
-
-  if (retval.value() > static_cast<size_t>(cpp::numeric_limits<int>::max())) {
-    libc_errno =
-        printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
-    return -1;
-  }
 
-  return static_cast<int>(retval.value());
+  return vfprintf_internal(stdout, format, args);
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/baremetal/putchar.cpp 
b/libc/src/stdio/baremetal/putchar.cpp
index ac21e6e783b01..a50bb8c86d8c6 100644
--- a/libc/src/stdio/baremetal/putchar.cpp
+++ b/libc/src/stdio/baremetal/putchar.cpp
@@ -1,4 +1,4 @@
-//===-- Baremetal Implementation of putchar 
-------------------------------===//
+//===-- Implementation of putchar for baremetal -----------------*- C++ 
-*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -7,18 +7,15 @@
 
//===----------------------------------------------------------------------===//
 
 #include "src/stdio/putchar.h"
-#include "src/__support/CPP/string_view.h"
-#include "src/__support/OSUtil/io.h"
+
+#include "src/__support/common.h"
 #include "src/__support/macros/config.h"
+#include "src/stdio/baremetal/fputc_internal.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
 LLVM_LIBC_FUNCTION(int, putchar, (int c)) {
-  char uc = static_cast<char>(c);
-
-  write_to_stdout(cpp::string_view(&uc, 1));
-
-  return 0;
+  return fputc_internal(c, stdout);
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/baremetal/puts.cpp 
b/libc/src/stdio/baremetal/puts.cpp
index fcd3aa086b2bf..e2a8c379e9ff7 100644
--- a/libc/src/stdio/baremetal/puts.cpp
+++ b/libc/src/stdio/baremetal/puts.cpp
@@ -7,20 +7,14 @@
 
//===----------------------------------------------------------------------===//
 
 #include "src/stdio/puts.h"
-#include "src/__support/CPP/string_view.h"
-#include "src/__support/OSUtil/io.h"
+#include "src/__support/common.h"
 #include "src/__support/macros/config.h"
+#include "src/stdio/baremetal/fputs_internal.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
 LLVM_LIBC_FUNCTION(int, puts, (const char *__restrict str)) {
-  cpp::string_view str_view(str);
-
-  // TODO: Can we combine these to avoid needing two writes?
-  write_to_stdout(str_view);
-  write_to_stdout("\n");
-
-  return 0;
+  return fputs_internal(str, stdout);
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/baremetal/vfprintf.cpp 
b/libc/src/stdio/baremetal/vfprintf.cpp
new file mode 100644
index 0000000000000..27cda91cb99c8
--- /dev/null
+++ b/libc/src/stdio/baremetal/vfprintf.cpp
@@ -0,0 +1,30 @@
+//===-- Implementation of vfprintf for baremetal ----------------*- 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 "hdr/types/FILE.h"
+#include "src/__support/arg_list.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/stdio/baremetal/vfprintf_internal.h"
+
+#include <stdarg.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+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.
+  return vfprintf_internal(stream, format, args);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/baremetal/vfprintf_internal.h 
b/libc/src/stdio/baremetal/vfprintf_internal.h
new file mode 100644
index 0000000000000..5fe346ade1a6e
--- /dev/null
+++ b/libc/src/stdio/baremetal/vfprintf_internal.h
@@ -0,0 +1,69 @@
+//===-- Baremetal 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_BAREMETAL_VFPRINTF_INTERNAL_H
+#define LLVM_LIBC_SRC_STDIO_BAREMETAL_VFPRINTF_INTERNAL_H
+
+#include "hdr/types/FILE.h"
+#include "src/__support/arg_list.h"
+#include "src/__support/CPP/limits.h"
+#include "src/__support/CPP/string_view.h"
+#include "src/__support/OSUtil/io.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/stdio/printf_core/core_structs.h"
+#include "src/stdio/printf_core/error_mapper.h"
+#include "src/stdio/printf_core/printf_main.h"
+#include "src/stdio/printf_core/writer.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+namespace internal {
+
+LIBC_INLINE int write_hook(cpp::string_view str_view, void *cookie) {
+  __llvm_libc_stdio_write(cookie, str_view.data(), str_view.size());
+  return printf_core::WRITE_OK;
+}
+
+} // namespace internal
+
+LIBC_INLINE int vfprintf_internal(::FILE *__restrict stream,
+                                  const char *__restrict format,
+                                  internal::ArgList &args) {
+  static constexpr size_t BUFF_SIZE = 1024;
+  char buffer[BUFF_SIZE];
+
+  printf_core::WriteBuffer<printf_core::WriteMode::FLUSH_TO_STREAM> wb(
+      buffer, BUFF_SIZE, &internal::write_hook, stream);
+  printf_core::Writer<printf_core::WriteMode::FLUSH_TO_STREAM> writer(wb);
+
+  auto retval = printf_core::printf_main(&writer, format, args);
+  if (!retval.has_value()) {
+    libc_errno = printf_core::internal_error_to_errno(retval.error());
+    return -1;
+  }
+
+  int flushval = wb.overflow_write("");
+  if (flushval != printf_core::WRITE_OK) {
+    libc_errno = printf_core::internal_error_to_errno(-flushval);
+    return -1;
+  }
+
+  if (retval.value() > static_cast<size_t>(cpp::numeric_limits<int>::max())) {
+    libc_errno =
+        printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
+    return -1;
+  }
+
+  return static_cast<int>(retval.value());
+}
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDIO_BAREMETAL_VFPRINTF_INTERNAL_H
diff --git a/libc/src/stdio/baremetal/vprintf.cpp 
b/libc/src/stdio/baremetal/vprintf.cpp
index c172b368d15f3..6b61bc45e34cf 100644
--- a/libc/src/stdio/baremetal/vprintf.cpp
+++ b/libc/src/stdio/baremetal/vprintf.cpp
@@ -7,61 +7,22 @@
 
//===----------------------------------------------------------------------===//
 
 #include "src/stdio/vprintf.h"
-#include "src/__support/CPP/limits.h"
-#include "src/__support/OSUtil/io.h"
+
 #include "src/__support/arg_list.h"
-#include "src/__support/libc_errno.h"
+#include "src/__support/common.h"
 #include "src/__support/macros/config.h"
-#include "src/stdio/printf_core/core_structs.h"
-#include "src/stdio/printf_core/error_mapper.h"
-#include "src/stdio/printf_core/printf_main.h"
-#include "src/stdio/printf_core/writer.h"
+#include "src/stdio/baremetal/vfprintf_internal.h"
 
 #include <stdarg.h>
-#include <stddef.h>
 
 namespace LIBC_NAMESPACE_DECL {
 
-namespace {
-
-LIBC_INLINE int stdout_write_hook(cpp::string_view new_str, void *) {
-  write_to_stdout(new_str);
-  return printf_core::WRITE_OK;
-}
-
-} // namespace
-
 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.
-  static constexpr size_t BUFF_SIZE = 1024;
-  char buffer[BUFF_SIZE];
-
-  printf_core::WriteBuffer<printf_core::WriteMode::FLUSH_TO_STREAM> wb(
-      buffer, BUFF_SIZE, &stdout_write_hook, nullptr);
-  printf_core::Writer<printf_core::WriteMode::FLUSH_TO_STREAM> writer(wb);
-
-  auto retval = printf_core::printf_main(&writer, format, args);
-  if (!retval.has_value()) {
-    libc_errno = printf_core::internal_error_to_errno(retval.error());
-    return -1;
-  }
-
-  int flushval = wb.overflow_write("");
-  if (flushval != printf_core::WRITE_OK) {
-    libc_errno = printf_core::internal_error_to_errno(-flushval);
-    return -1;
-  }
-
-  if (retval.value() > static_cast<size_t>(cpp::numeric_limits<int>::max())) {
-    libc_errno =
-        printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
-    return -1;
-  }
-
-  return static_cast<int>(retval.value());
+  return vfprintf_internal(stdout, format, args);
 }
 
 } // namespace LIBC_NAMESPACE_DECL

>From 01cb39862aef2ee8a6eddf6472f435e5738d89f0 Mon Sep 17 00:00:00 2001
From: Petr Hosek <[email protected]>
Date: Thu, 20 Nov 2025 18:50:21 +0000
Subject: [PATCH 2/5] Include missing files

---
 libc/src/stdio/baremetal/fgetc.cpp |  0
 libc/src/stdio/baremetal/fgets.cpp |  0
 libc/src/stdio/baremetal/putc.cpp  | 22 ++++++++++++++++++++++
 3 files changed, 22 insertions(+)
 delete mode 100644 libc/src/stdio/baremetal/fgetc.cpp
 delete mode 100644 libc/src/stdio/baremetal/fgets.cpp
 create mode 100644 libc/src/stdio/baremetal/putc.cpp

diff --git a/libc/src/stdio/baremetal/fgetc.cpp 
b/libc/src/stdio/baremetal/fgetc.cpp
deleted file mode 100644
index e69de29bb2d1d..0000000000000
diff --git a/libc/src/stdio/baremetal/fgets.cpp 
b/libc/src/stdio/baremetal/fgets.cpp
deleted file mode 100644
index e69de29bb2d1d..0000000000000
diff --git a/libc/src/stdio/baremetal/putc.cpp 
b/libc/src/stdio/baremetal/putc.cpp
new file mode 100644
index 0000000000000..b8d17d00f9662
--- /dev/null
+++ b/libc/src/stdio/baremetal/putc.cpp
@@ -0,0 +1,22 @@
+//===-- Implementation of putc for baremetal --------------------*- 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/putc.h"
+
+#include "hdr/types/FILE.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/stdio/baremetal/fputc_internal.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, putc, (int c, ::FILE *stream)) {
+  return fputc_internal(c, stream);
+}
+
+} // namespace LIBC_NAMESPACE_DECL

>From f812a2c7febe2f8bd574b09fafcb71f3ae612b98 Mon Sep 17 00:00:00 2001
From: Petr Hosek <[email protected]>
Date: Thu, 20 Nov 2025 19:46:12 +0000
Subject: [PATCH 3/5] Formatting

---
 libc/src/stdio/baremetal/feof.cpp            |  1 -
 libc/src/stdio/baremetal/fwrite.cpp          | 27 ++++++++++++++++++++
 libc/src/stdio/baremetal/fwrite_internal.h   |  7 ++---
 libc/src/stdio/baremetal/putchar.cpp         |  4 +--
 libc/src/stdio/baremetal/vfprintf_internal.h |  4 +--
 5 files changed, 34 insertions(+), 9 deletions(-)
 create mode 100644 libc/src/stdio/baremetal/fwrite.cpp

diff --git a/libc/src/stdio/baremetal/feof.cpp 
b/libc/src/stdio/baremetal/feof.cpp
index 3c241edeb47f6..42862f244d204 100644
--- a/libc/src/stdio/baremetal/feof.cpp
+++ b/libc/src/stdio/baremetal/feof.cpp
@@ -21,4 +21,3 @@ LLVM_LIBC_FUNCTION(int, feof, (::FILE * stream)) {
 }
 
 } // namespace LIBC_NAMESPACE_DECL
-
diff --git a/libc/src/stdio/baremetal/fwrite.cpp 
b/libc/src/stdio/baremetal/fwrite.cpp
new file mode 100644
index 0000000000000..22ec90356de61
--- /dev/null
+++ b/libc/src/stdio/baremetal/fwrite.cpp
@@ -0,0 +1,27 @@
+//===-- Implementation of fwrite for baremetal ------------------*- 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/fwrite.h"
+
+#include "hdr/types/FILE.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/stdio/baremetal/fwrite_internal.h"
+#include <stddef.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(size_t, fwrite,
+                   (const void *__restrict buffer, size_t size, size_t nmemb,
+                    ::FILE *stream)) {
+  if (size == 0 || nmemb == 0)
+    return 0;
+  return fwrite_internal(buffer, size, nmemb, stream);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/baremetal/fwrite_internal.h 
b/libc/src/stdio/baremetal/fwrite_internal.h
index f4a07c382b362..3e03482a0e3d9 100644
--- a/libc/src/stdio/baremetal/fwrite_internal.h
+++ b/libc/src/stdio/baremetal/fwrite_internal.h
@@ -16,9 +16,10 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-LIBC_INLINE size_t fwrite_internal(const void *buffer, size_t size, size_t 
nmemb,
-                                   ::FILE *stream) {
-  __llvm_libc_stdio_write(stream, reinterpret_cast<const char *>(buffer), size 
* nmemb);
+LIBC_INLINE size_t fwrite_internal(const void *buffer, size_t size,
+                                   size_t nmemb, ::FILE *stream) {
+  __llvm_libc_stdio_write(stream, reinterpret_cast<const char *>(buffer),
+                          size * nmemb);
   return nmemb;
 }
 
diff --git a/libc/src/stdio/baremetal/putchar.cpp 
b/libc/src/stdio/baremetal/putchar.cpp
index a50bb8c86d8c6..d7c60a989a5ed 100644
--- a/libc/src/stdio/baremetal/putchar.cpp
+++ b/libc/src/stdio/baremetal/putchar.cpp
@@ -14,8 +14,6 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-LLVM_LIBC_FUNCTION(int, putchar, (int c)) {
-  return fputc_internal(c, stdout);
-}
+LLVM_LIBC_FUNCTION(int, putchar, (int c)) { return fputc_internal(c, stdout); }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/baremetal/vfprintf_internal.h 
b/libc/src/stdio/baremetal/vfprintf_internal.h
index 5fe346ade1a6e..c9504a24a3715 100644
--- a/libc/src/stdio/baremetal/vfprintf_internal.h
+++ b/libc/src/stdio/baremetal/vfprintf_internal.h
@@ -10,12 +10,12 @@
 #define LLVM_LIBC_SRC_STDIO_BAREMETAL_VFPRINTF_INTERNAL_H
 
 #include "hdr/types/FILE.h"
-#include "src/__support/arg_list.h"
 #include "src/__support/CPP/limits.h"
 #include "src/__support/CPP/string_view.h"
 #include "src/__support/OSUtil/io.h"
-#include "src/__support/libc_errno.h"
+#include "src/__support/arg_list.h"
 #include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/core_structs.h"
 #include "src/stdio/printf_core/error_mapper.h"

>From 91ad6187dc3f4904e34c9751572e9a3d2849b56f Mon Sep 17 00:00:00 2001
From: Petr Hosek <[email protected]>
Date: Fri, 12 Dec 2025 08:12:08 +0000
Subject: [PATCH 4/5] Input functions

---
 libc/config/baremetal/aarch64/entrypoints.txt |   5 +
 libc/config/baremetal/arm/entrypoints.txt     |   5 +
 libc/config/baremetal/riscv/entrypoints.txt   |   5 +
 libc/src/stdio/baremetal/CMakeLists.txt       | 159 +++++++++++++-----
 libc/src/stdio/baremetal/fgetc.cpp            |  31 ++++
 libc/src/stdio/baremetal/fgets.cpp            |  49 ++++++
 libc/src/stdio/baremetal/file_internal.h      |  51 ++++++
 libc/src/stdio/baremetal/fputc.cpp            |  14 +-
 libc/src/stdio/baremetal/fputc_internal.h     |  29 ----
 libc/src/stdio/baremetal/fputs.cpp            |  16 +-
 libc/src/stdio/baremetal/fputs_internal.h     |  29 ----
 libc/src/stdio/baremetal/fread.cpp            |  33 ++++
 libc/src/stdio/baremetal/fscanf.cpp           |  38 +++++
 libc/src/stdio/baremetal/fwrite.cpp           |   6 +-
 libc/src/stdio/baremetal/fwrite_internal.h    |  28 ---
 libc/src/stdio/baremetal/getc.cpp             |   0
 libc/src/stdio/baremetal/getchar.cpp          |  19 ++-
 libc/src/stdio/baremetal/printf.cpp           |   1 +
 libc/src/stdio/baremetal/putc.cpp             |  14 +-
 libc/src/stdio/baremetal/putchar.cpp          |  16 +-
 libc/src/stdio/baremetal/puts.cpp             |  24 ++-
 libc/src/stdio/baremetal/scanf.cpp            |  11 +-
 libc/src/stdio/baremetal/scanf_internal.h     |  30 ----
 libc/src/stdio/baremetal/vfscanf.cpp          |  36 ++++
 libc/src/stdio/baremetal/vfscanf_internal.h   |  58 +++++++
 libc/src/stdio/baremetal/vprintf.cpp          |   1 +
 libc/src/stdio/baremetal/vscanf.cpp           |  12 +-
 27 files changed, 525 insertions(+), 195 deletions(-)
 create mode 100644 libc/src/stdio/baremetal/fgetc.cpp
 create mode 100644 libc/src/stdio/baremetal/fgets.cpp
 create mode 100644 libc/src/stdio/baremetal/file_internal.h
 delete mode 100644 libc/src/stdio/baremetal/fputc_internal.h
 delete mode 100644 libc/src/stdio/baremetal/fputs_internal.h
 create mode 100644 libc/src/stdio/baremetal/fread.cpp
 create mode 100644 libc/src/stdio/baremetal/fscanf.cpp
 delete mode 100644 libc/src/stdio/baremetal/fwrite_internal.h
 create mode 100644 libc/src/stdio/baremetal/getc.cpp
 delete mode 100644 libc/src/stdio/baremetal/scanf_internal.h
 create mode 100644 libc/src/stdio/baremetal/vfscanf.cpp
 create mode 100644 libc/src/stdio/baremetal/vfscanf_internal.h

diff --git a/libc/config/baremetal/aarch64/entrypoints.txt 
b/libc/config/baremetal/aarch64/entrypoints.txt
index 4c6b6dfc4d669..742d96761c415 100644
--- a/libc/config/baremetal/aarch64/entrypoints.txt
+++ b/libc/config/baremetal/aarch64/entrypoints.txt
@@ -126,9 +126,13 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.stdio.asprintf
     libc.src.stdio.feof
     libc.src.stdio.ferror
+    libc.src.stdio.fgetc
+    libc.src.stdio.fgets
     libc.src.stdio.fprintf
     libc.src.stdio.fputc
     libc.src.stdio.fputs
+    libc.src.stdio.fread
+    libc.src.stdio.fscanf
     libc.src.stdio.fwrite
     libc.src.stdio.getchar
     libc.src.stdio.printf
@@ -142,6 +146,7 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.stdio.sscanf
     libc.src.stdio.vasprintf
     libc.src.stdio.vfprintf
+    libc.src.stdio.vfscanf
     libc.src.stdio.vprintf
     libc.src.stdio.vscanf
     libc.src.stdio.vsnprintf
diff --git a/libc/config/baremetal/arm/entrypoints.txt 
b/libc/config/baremetal/arm/entrypoints.txt
index 0847a0905a6f7..95cb0dea8e49e 100644
--- a/libc/config/baremetal/arm/entrypoints.txt
+++ b/libc/config/baremetal/arm/entrypoints.txt
@@ -126,9 +126,13 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.stdio.asprintf
     libc.src.stdio.feof
     libc.src.stdio.ferror
+    libc.src.stdio.fgetc
+    libc.src.stdio.fgets
     libc.src.stdio.fprintf
     libc.src.stdio.fputc
     libc.src.stdio.fputs
+    libc.src.stdio.fread
+    libc.src.stdio.fscanf
     libc.src.stdio.fwrite
     libc.src.stdio.getchar
     libc.src.stdio.printf
@@ -142,6 +146,7 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.stdio.sscanf
     libc.src.stdio.vasprintf
     libc.src.stdio.vfprintf
+    libc.src.stdio.vfscanf
     libc.src.stdio.vprintf
     libc.src.stdio.vscanf
     libc.src.stdio.vsnprintf
diff --git a/libc/config/baremetal/riscv/entrypoints.txt 
b/libc/config/baremetal/riscv/entrypoints.txt
index 9a5c73b81e87f..3fc71280f5163 100644
--- a/libc/config/baremetal/riscv/entrypoints.txt
+++ b/libc/config/baremetal/riscv/entrypoints.txt
@@ -126,9 +126,13 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.stdio.asprintf
     libc.src.stdio.feof
     libc.src.stdio.ferror
+    libc.src.stdio.fgetc
+    libc.src.stdio.fgets
     libc.src.stdio.fprintf
     libc.src.stdio.fputc
     libc.src.stdio.fputs
+    libc.src.stdio.fread
+    libc.src.stdio.fscanf
     libc.src.stdio.fwrite
     libc.src.stdio.getchar
     libc.src.stdio.printf
@@ -142,6 +146,7 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.stdio.sscanf
     libc.src.stdio.vasprintf
     libc.src.stdio.vfprintf
+    libc.src.stdio.vfscanf
     libc.src.stdio.vprintf
     libc.src.stdio.vscanf
     libc.src.stdio.vsnprintf
diff --git a/libc/src/stdio/baremetal/CMakeLists.txt 
b/libc/src/stdio/baremetal/CMakeLists.txt
index e920b4a1a30f4..a706accecf152 100644
--- a/libc/src/stdio/baremetal/CMakeLists.txt
+++ b/libc/src/stdio/baremetal/CMakeLists.txt
@@ -1,7 +1,7 @@
 add_header_library(
-  fputc_internal
+  file_internal
   HDRS
-    fputc_internal.h
+    file_internal.h
   DEPENDS
     libc.hdr.types.FILE
     libc.src.__support.CPP.string_view
@@ -9,40 +9,38 @@ add_header_library(
 )
 
 add_header_library(
-  fputs_internal
+  vfprintf_internal
   HDRS
-    fputs_internal.h
+    vfprintf_internal.h
   DEPENDS
     libc.hdr.types.FILE
+    libc.hdr.stdio_macros
+    libc.src.__support.arg_list
+    libc.src.__support.CPP.limits
     libc.src.__support.CPP.string_view
+    libc.src.__support.libc_errno
     libc.src.__support.OSUtil.osutil
+    libc.src.stdio.printf_core.printf_main
+    libc.src.stdio.printf_core.writer
+    libc.src.stdio.printf_core.error_mapper
+    libc.src.stdio.printf_core.core_structs
 )
 
 add_header_library(
-  fwrite_internal
-  HDRS
-    fwrite_internal.h
-  DEPENDS
-    libc.hdr.types.FILE
-    libc.src.__support.OSUtil.osutil
-)
-
-add_header_library(
-  vfprintf_internal
+  vfscanf_internal
   HDRS
-    vfprintf_internal.h
+    vfscanf_internal.h
   DEPENDS
     libc.hdr.types.FILE
     libc.hdr.stdio_macros
     libc.src.__support.arg_list
+    libc.src.__support.OSUtil.osutil
     libc.src.__support.CPP.limits
     libc.src.__support.CPP.string_view
     libc.src.__support.libc_errno
     libc.src.__support.OSUtil.osutil
-    libc.src.stdio.printf_core.printf_main
-    libc.src.stdio.printf_core.writer
-    libc.src.stdio.printf_core.error_mapper
-    libc.src.stdio.printf_core.core_structs
+    libc.src.stdio.scanf_core.reader
+    libc.src.stdio.scanf_core.scanf_main
 )
 
 add_entrypoint_object(
@@ -65,6 +63,32 @@ add_entrypoint_object(
     libc.hdr.types.FILE
 )
 
+add_entrypoint_object(
+  fgetc
+  SRCS
+    fgetc.cpp
+  HDRS
+    ../fgetc.h
+  DEPENDS
+    .file_internal
+    libc.hdr.stdio_macros
+    libc.hdr.types.FILE
+    libc.src.errno.errno
+)
+
+add_entrypoint_object(
+  fgets
+  SRCS
+    fgets.cpp
+  HDRS
+    ../fgets.h
+  DEPENDS
+    .file_internal
+    libc.hdr.stdio_macros
+    libc.hdr.types.FILE
+    libc.src.errno.errno
+)
+
 add_entrypoint_object(
   fprintf
   SRCS
@@ -73,6 +97,8 @@ add_entrypoint_object(
     ../fprintf.h
   DEPENDS
     .vfprintf_internal
+    libc.hdr.stdio_macros
+    libc.hdr.types.FILE
     libc.src.__support.arg_list
 )
 
@@ -83,7 +109,10 @@ add_entrypoint_object(
   HDRS
     ../fputc.h
   DEPENDS
-    .fputc_internal
+    .file_internal
+    libc.hdr.stdio_macros
+    libc.hdr.types.FILE
+    libc.src.errno.errno
 )
 
 add_entrypoint_object(
@@ -93,7 +122,33 @@ add_entrypoint_object(
   HDRS
     ../fputc.h
   DEPENDS
-    .fputs_internal
+    .file_internal
+    libc.hdr.stdio_macros
+    libc.hdr.types.FILE
+    libc.src.errno.errno
+)
+
+add_entrypoint_object(
+  fread
+  SRCS
+    fread.cpp
+  HDRS
+    ../fread.h
+  DEPENDS
+    .file_internal
+    libc.hdr.types.FILE
+    libc.src.errno.errno
+)
+
+add_entrypoint_object(
+  fscanf
+  SRCS
+    fscanf.cpp
+  HDRS
+    ../fscanf.h
+  DEPENDS
+    .vfscanf_internal
+    libc.src.__support.arg_list
 )
 
 add_entrypoint_object(
@@ -103,7 +158,10 @@ add_entrypoint_object(
   HDRS
     ../fwrite.h
   DEPENDS
-    .fwrite_internal
+    .file_internal
+    libc.hdr.stdio_macros
+    libc.hdr.types.FILE
+    libc.src.errno.errno
 )
 
 add_entrypoint_object(
@@ -113,9 +171,9 @@ add_entrypoint_object(
   HDRS
     ../getchar.h
   DEPENDS
-    libc.hdr.stdio_macros
-    libc.src.__support.OSUtil.osutil
-    libc.src.__support.CPP.string_view
+    .file_internal
+    libc.hdr.types.FILE
+    libc.src.errno.errno
 )
 
 add_entrypoint_object(
@@ -136,6 +194,7 @@ add_entrypoint_object(
     ../printf.h
   DEPENDS
     .vfprintf_internal
+    libc.hdr.stdio_macros
     libc.src.__support.arg_list
 )
 
@@ -146,7 +205,9 @@ add_entrypoint_object(
   HDRS
     ../putchar.h
   DEPENDS
-    .fputc_internal
+    .file_internal
+    libc.hdr.stdio_macros
+    libc.src.errno.errno
 )
 
 add_entrypoint_object(
@@ -156,7 +217,10 @@ add_entrypoint_object(
   HDRS
     ../putc.h
   DEPENDS
-    .fputc_internal
+    .file_internal
+    libc.hdr.stdio_macros
+    libc.hdr.types.FILE
+    libc.src.errno.errno
 )
 
 add_entrypoint_object(
@@ -166,16 +230,9 @@ add_entrypoint_object(
   HDRS
     ../puts.h
   DEPENDS
-    .fputs_internal
-)
-
-add_header_library(
-  scanf_internal
-  HDRS
-    scanf_internal.h
-  DEPENDS
-    libc.src.stdio.scanf_core.reader
-    libc.src.__support.OSUtil.osutil
+    .file_internal
+    libc.hdr.stdio_macros
+    libc.src.errno.errno
 )
 
 add_entrypoint_object(
@@ -185,11 +242,10 @@ add_entrypoint_object(
   HDRS
     ../scanf.h
   DEPENDS
-    .scanf_internal
-    libc.include.inttypes
-    libc.src.stdio.scanf_core.scanf_main
+    .vfscanf_internal
+    libc.hdr.types.FILE
+    libc.hdr.stdio_macros
     libc.src.__support.arg_list
-    libc.src.__support.OSUtil.osutil
 )
 
 add_entrypoint_object(
@@ -200,6 +256,21 @@ add_entrypoint_object(
     ../vfprintf.h
   DEPENDS
     .vfprintf_internal
+    libc.hdr.stdio_macros
+    libc.hdr.types.FILE
+    libc.src.__support.arg_list
+)
+
+add_entrypoint_object(
+  vfscanf
+  SRCS
+    vfscanf.cpp
+  HDRS
+    ../vfscanf.h
+  DEPENDS
+    .vfscanf_internal
+    libc.hdr.types.FILE
+    libc.hdr.stdio_macros
     libc.src.__support.arg_list
 )
 
@@ -211,6 +282,7 @@ add_entrypoint_object(
     ../vprintf.h
   DEPENDS
     .vfprintf_internal
+    libc.hdr.stdio_macros
     libc.src.__support.arg_list
 )
 
@@ -221,8 +293,7 @@ add_entrypoint_object(
   HDRS
     ../vscanf.h
   DEPENDS
-    .scanf_internal
-    libc.src.stdio.scanf_core.scanf_main
+    .vfscanf_internal
+    libc.hdr.stdio_macros
     libc.src.__support.arg_list
-    libc.src.__support.OSUtil.osutil
 )
diff --git a/libc/src/stdio/baremetal/fgetc.cpp 
b/libc/src/stdio/baremetal/fgetc.cpp
new file mode 100644
index 0000000000000..4f9cd71628a99
--- /dev/null
+++ b/libc/src/stdio/baremetal/fgetc.cpp
@@ -0,0 +1,31 @@
+//===-- Implementation of fgetc for baremetal -------------------*- 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/fgetc.h"
+
+#include "hdr/stdio_macros.h" // for EOF.
+#include "hdr/types/FILE.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/stdio/baremetal/file_internal.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, fgetc, (::FILE * stream)) {
+  unsigned char c;
+  auto result = read_internal(reinterpret_cast<char *>(&c), 1, stream);
+  if (result.has_error())
+    libc_errno = result.error;
+
+  if (result.value != 1)
+    return EOF;
+  return c;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/baremetal/fgets.cpp 
b/libc/src/stdio/baremetal/fgets.cpp
new file mode 100644
index 0000000000000..6e8e5076231e5
--- /dev/null
+++ b/libc/src/stdio/baremetal/fgets.cpp
@@ -0,0 +1,49 @@
+//===-- Implementation of fgets for baremetal -------------------*- 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/fgets.h"
+
+#include "hdr/stdio_macros.h" // for EOF.
+#include "hdr/types/FILE.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/stdio/baremetal/file_internal.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(char *, fgets,
+                   (char *__restrict str, int count,
+                    ::FILE *__restrict stream)) {
+  if (count < 1)
+    return nullptr;
+
+  unsigned char c = '\0';
+  // i is an int because it's frequently compared to count, which is also int.
+  int i = 0;
+
+  for (; i < (count - 1) && c != '\n'; ++i) {
+    auto result = read_internal(reinterpret_cast<char *>(&c), 1, stream);
+    if (result.has_error())
+      libc_errno = result.error;
+
+    if (result.value != 1)
+      break;
+    str[i] = c;
+  }
+
+  // If the requested read size makes no sense, an error occured, or no bytes
+  // were read due to an EOF, then return nullptr and don't write the null 
byte.
+  if (i == 0)
+    return nullptr;
+
+  str[i] = '\0';
+  return str;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/baremetal/file_internal.h 
b/libc/src/stdio/baremetal/file_internal.h
new file mode 100644
index 0000000000000..d065987fd7922
--- /dev/null
+++ b/libc/src/stdio/baremetal/file_internal.h
@@ -0,0 +1,51 @@
+//===--- Helper functions for file I/O on baremetal 
-----------------------===//
+//
+// 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_BAREMETAL_FILE_H
+#define LLVM_LIBC_SRC_STDIO_BAREMETAL_FILE_H
+
+#include "hdr/types/FILE.h"
+#include "src/__support/CPP/string_view.h"
+#include "src/__support/OSUtil/io.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+#include <stddef.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+struct FileIOResult {
+  size_t value;
+  int error;
+
+  constexpr FileIOResult(size_t val) : value(val), error(0) {}
+  constexpr FileIOResult(size_t val, int error) : value(val), error(error) {}
+
+  constexpr bool has_error() { return error != 0; }
+
+  constexpr operator size_t() { return value; }
+};
+
+LIBC_INLINE FileIOResult read_internal(char *buf, size_t size, ::FILE *stream) 
{
+  ssize_t ret = __llvm_libc_stdio_read(stream, buf, size);
+  if (ret < 0)
+    return {0, -ret};
+  return ret;
+}
+
+LIBC_INLINE FileIOResult write_internal(const char *buf, size_t size,
+                                        ::FILE *stream) {
+  ssize_t ret = __llvm_libc_stdio_write(stream, buf, size);
+  if (ret < 0)
+    return {0, -ret};
+  return ret;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDIO_BAREMETAL_FPUTS_INTERNAL_H
diff --git a/libc/src/stdio/baremetal/fputc.cpp 
b/libc/src/stdio/baremetal/fputc.cpp
index 1b4086466ee7b..e7361119222cf 100644
--- a/libc/src/stdio/baremetal/fputc.cpp
+++ b/libc/src/stdio/baremetal/fputc.cpp
@@ -8,15 +8,25 @@
 
 #include "src/stdio/fputc.h"
 
+#include "hdr/stdio_macros.h" // for EOF
 #include "hdr/types/FILE.h"
 #include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
-#include "src/stdio/baremetal/fputc_internal.h"
+#include "src/stdio/baremetal/file_internal.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
 LLVM_LIBC_FUNCTION(int, fputc, (int c, ::FILE *stream)) {
-  return fputc_internal(c, stream);
+  auto result = write_internal(reinterpret_cast<char *>(&c), 1, stream);
+  if (result.has_error())
+    libc_errno = result.error;
+  size_t written = result.value;
+  if (1 != written) {
+    // The stream should be in an error state in this case.
+    return EOF;
+  }
+  return 0;
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/baremetal/fputc_internal.h 
b/libc/src/stdio/baremetal/fputc_internal.h
deleted file mode 100644
index e578d6584bcfe..0000000000000
--- a/libc/src/stdio/baremetal/fputc_internal.h
+++ /dev/null
@@ -1,29 +0,0 @@
-//===-- Baremetal 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_BAREMETAL_FPUTC_INTERNAL_H
-#define LLVM_LIBC_SRC_STDIO_BAREMETAL_FPUTC_INTERNAL_H
-
-#include "hdr/types/FILE.h"
-#include "src/__support/CPP/string_view.h"
-#include "src/__support/OSUtil/io.h"
-#include "src/__support/common.h"
-#include "src/__support/macros/config.h"
-
-namespace LIBC_NAMESPACE_DECL {
-
-LIBC_INLINE int fputc_internal(int c, ::FILE *stream) {
-  char ch = static_cast<char>(c);
-  cpp::string_view str_view(&ch, 1);
-  __llvm_libc_stdio_write(stream, str_view.data(), str_view.size());
-  return 0;
-}
-
-} // namespace LIBC_NAMESPACE_DECL
-
-#endif // LLVM_LIBC_SRC_STDIO_BAREMETAL_FPUTC_INTERNAL_H
diff --git a/libc/src/stdio/baremetal/fputs.cpp 
b/libc/src/stdio/baremetal/fputs.cpp
index daed53182686e..9020b723a1658 100644
--- a/libc/src/stdio/baremetal/fputs.cpp
+++ b/libc/src/stdio/baremetal/fputs.cpp
@@ -8,16 +8,28 @@
 
 #include "src/stdio/fputs.h"
 
+#include "hdr/stdio_macros.h" // for EOF
 #include "hdr/types/FILE.h"
 #include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
-#include "src/stdio/baremetal/fputs_internal.h"
+#include "src/stdio/baremetal/file_internal.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
 LLVM_LIBC_FUNCTION(int, fputs,
                    (const char *__restrict str, ::FILE *__restrict stream)) {
-  return fputs_internal(str, stream);
+  cpp::string_view str_view(str);
+
+  auto result = write_internal(str_view.data(), str_view.size(), stream);
+  if (result.has_error())
+    libc_errno = result.error;
+  size_t written = result.value;
+  if (str_view.size() != written) {
+    // The stream should be in an error state in this case.
+    return EOF;
+  }
+  return 0;
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/baremetal/fputs_internal.h 
b/libc/src/stdio/baremetal/fputs_internal.h
deleted file mode 100644
index d4032a1088eb4..0000000000000
--- a/libc/src/stdio/baremetal/fputs_internal.h
+++ /dev/null
@@ -1,29 +0,0 @@
-//===-- Baremetal 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_BAREMETAL_FPUTS_INTERNAL_H
-#define LLVM_LIBC_SRC_STDIO_BAREMETAL_FPUTS_INTERNAL_H
-
-#include "hdr/types/FILE.h"
-#include "src/__support/CPP/string_view.h"
-#include "src/__support/OSUtil/io.h"
-#include "src/__support/common.h"
-#include "src/__support/macros/config.h"
-
-namespace LIBC_NAMESPACE_DECL {
-
-LIBC_INLINE int fputs_internal(const char *str, ::FILE *stream) {
-  cpp::string_view str_view(str);
-  __llvm_libc_stdio_write(stream, str_view.data(), str_view.size());
-  __llvm_libc_stdio_write(stream, "\n", 1);
-  return 0;
-}
-
-} // namespace LIBC_NAMESPACE_DECL
-
-#endif // LLVM_LIBC_SRC_STDIO_BAREMETAL_FPUTS_INTERNAL_H
diff --git a/libc/src/stdio/baremetal/fread.cpp 
b/libc/src/stdio/baremetal/fread.cpp
new file mode 100644
index 0000000000000..ce077fb1ab615
--- /dev/null
+++ b/libc/src/stdio/baremetal/fread.cpp
@@ -0,0 +1,33 @@
+//===-- Implementation of fread for baremetal -------------------*- 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/fread.h"
+
+#include "hdr/types/FILE.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/stdio/baremetal/file_internal.h"
+
+#include <stddef.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(size_t, fread,
+                   (void *__restrict buffer, size_t size, size_t nmemb,
+                    ::FILE *stream)) {
+  if (size == 0 || nmemb == 0)
+    return 0;
+  auto result =
+      read_internal(reinterpret_cast<char *>(buffer), size * nmemb, stream);
+  if (result.has_error())
+    libc_errno = result.error;
+  return result.value / size;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/baremetal/fscanf.cpp 
b/libc/src/stdio/baremetal/fscanf.cpp
new file mode 100644
index 0000000000000..eb8b2326ad6b7
--- /dev/null
+++ b/libc/src/stdio/baremetal/fscanf.cpp
@@ -0,0 +1,38 @@
+//===-- Implementation of fscanf for baremetal ------------------*- 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/fscanf.h"
+
+#include "hdr/stdio_macros.h" // for EOF.
+#include "hdr/types/FILE.h"
+#include "src/__support/arg_list.h"
+#include "src/__support/macros/config.h"
+#include "src/stdio/baremetal/vfscanf_internal.h"
+
+#include "hdr/types/FILE.h"
+#include <stdarg.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, fscanf,
+                   (::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);
+
+  int ret_val = vfscanf_internal(stream, format, args);
+  // This is done to avoid including stdio.h in the internals. On most systems
+  // EOF is -1, so this will be transformed into just "return ret_val".
+  return (ret_val == -1) ? EOF : ret_val;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/baremetal/fwrite.cpp 
b/libc/src/stdio/baremetal/fwrite.cpp
index 22ec90356de61..0fc42b89bb6e8 100644
--- a/libc/src/stdio/baremetal/fwrite.cpp
+++ b/libc/src/stdio/baremetal/fwrite.cpp
@@ -11,7 +11,8 @@
 #include "hdr/types/FILE.h"
 #include "src/__support/common.h"
 #include "src/__support/macros/config.h"
-#include "src/stdio/baremetal/fwrite_internal.h"
+#include "src/stdio/baremetal/file_internal.h"
+
 #include <stddef.h>
 
 namespace LIBC_NAMESPACE_DECL {
@@ -21,7 +22,8 @@ LLVM_LIBC_FUNCTION(size_t, fwrite,
                     ::FILE *stream)) {
   if (size == 0 || nmemb == 0)
     return 0;
-  return fwrite_internal(buffer, size, nmemb, stream);
+  return write_internal(reinterpret_cast<const char *>(buffer), size * nmemb,
+                        stream);
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/baremetal/fwrite_internal.h 
b/libc/src/stdio/baremetal/fwrite_internal.h
deleted file mode 100644
index 3e03482a0e3d9..0000000000000
--- a/libc/src/stdio/baremetal/fwrite_internal.h
+++ /dev/null
@@ -1,28 +0,0 @@
-//===-- Baremetal implementation header of fwrite ---------------*- 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_BAREMETAL_FWRITE_INTERNAL_H
-#define LLVM_LIBC_SRC_STDIO_BAREMETAL_FWRITE_INTERNAL_H
-
-#include "hdr/types/FILE.h"
-#include "src/__support/OSUtil/io.h"
-#include "src/__support/common.h"
-#include "src/__support/macros/config.h"
-
-namespace LIBC_NAMESPACE_DECL {
-
-LIBC_INLINE size_t fwrite_internal(const void *buffer, size_t size,
-                                   size_t nmemb, ::FILE *stream) {
-  __llvm_libc_stdio_write(stream, reinterpret_cast<const char *>(buffer),
-                          size * nmemb);
-  return nmemb;
-}
-
-} // namespace LIBC_NAMESPACE_DECL
-
-#endif // LLVM_LIBC_SRC_STDIO_BAREMETAL_FWRITE_INTERNAL_H
diff --git a/libc/src/stdio/baremetal/getc.cpp 
b/libc/src/stdio/baremetal/getc.cpp
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git a/libc/src/stdio/baremetal/getchar.cpp 
b/libc/src/stdio/baremetal/getchar.cpp
index 8fb7bc32537e7..6e599c9337e9c 100644
--- a/libc/src/stdio/baremetal/getchar.cpp
+++ b/libc/src/stdio/baremetal/getchar.cpp
@@ -1,4 +1,4 @@
-//===-- Baremetal implementation of getchar 
-------------------------------===//
+//===-- Implementation of getchar for baremetal -----------------*- C++ 
-*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -7,19 +7,24 @@
 
//===----------------------------------------------------------------------===//
 
 #include "src/stdio/getchar.h"
-#include "src/__support/OSUtil/io.h"
-#include "src/__support/macros/config.h"
 
 #include "hdr/stdio_macros.h" // for EOF.
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/stdio/baremetal/file_internal.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
 LLVM_LIBC_FUNCTION(int, getchar, ()) {
-  char buf[1];
-  auto result = read_from_stdin(buf, sizeof(buf));
-  if (result <= 0)
+  unsigned char c;
+  auto result = read_internal(reinterpret_cast<char *>(&c), 1, stdin);
+  if (result.has_error())
+    libc_errno = result.error;
+
+  if (result.value != 1)
     return EOF;
-  return buf[0];
+  return c;
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/baremetal/printf.cpp 
b/libc/src/stdio/baremetal/printf.cpp
index 637f8aacc935e..5010810906e24 100644
--- a/libc/src/stdio/baremetal/printf.cpp
+++ b/libc/src/stdio/baremetal/printf.cpp
@@ -8,6 +8,7 @@
 
 #include "src/stdio/printf.h"
 
+#include "hdr/stdio_macros.h"
 #include "src/__support/arg_list.h"
 #include "src/__support/common.h"
 #include "src/__support/macros/config.h"
diff --git a/libc/src/stdio/baremetal/putc.cpp 
b/libc/src/stdio/baremetal/putc.cpp
index b8d17d00f9662..e1df2db58800a 100644
--- a/libc/src/stdio/baremetal/putc.cpp
+++ b/libc/src/stdio/baremetal/putc.cpp
@@ -8,15 +8,25 @@
 
 #include "src/stdio/putc.h"
 
+#include "hdr/stdio_macros.h" // for EOF
 #include "hdr/types/FILE.h"
 #include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
-#include "src/stdio/baremetal/fputc_internal.h"
+#include "src/stdio/baremetal/file_internal.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
 LLVM_LIBC_FUNCTION(int, putc, (int c, ::FILE *stream)) {
-  return fputc_internal(c, stream);
+  auto result = write_internal(reinterpret_cast<char *>(&c), 1, stream);
+  if (result.has_error())
+    libc_errno = result.error;
+  size_t written = result.value;
+  if (1 != written) {
+    // The stream should be in an error state in this case.
+    return EOF;
+  }
+  return 0;
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/baremetal/putchar.cpp 
b/libc/src/stdio/baremetal/putchar.cpp
index d7c60a989a5ed..9753f2a0799ce 100644
--- a/libc/src/stdio/baremetal/putchar.cpp
+++ b/libc/src/stdio/baremetal/putchar.cpp
@@ -8,12 +8,24 @@
 
 #include "src/stdio/putchar.h"
 
+#include "hdr/stdio_macros.h" // for EOF
 #include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
-#include "src/stdio/baremetal/fputc_internal.h"
+#include "src/stdio/baremetal/file_internal.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
-LLVM_LIBC_FUNCTION(int, putchar, (int c)) { return fputc_internal(c, stdout); }
+LLVM_LIBC_FUNCTION(int, putchar, (int c)) {
+  auto result = write_internal(reinterpret_cast<char *>(&c), 1, stdout);
+  if (result.has_error())
+    libc_errno = result.error;
+  size_t written = result.value;
+  if (1 != written) {
+    // The stream should be in an error state in this case.
+    return EOF;
+  }
+  return 0;
+}
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/baremetal/puts.cpp 
b/libc/src/stdio/baremetal/puts.cpp
index e2a8c379e9ff7..703c783cacd06 100644
--- a/libc/src/stdio/baremetal/puts.cpp
+++ b/libc/src/stdio/baremetal/puts.cpp
@@ -7,14 +7,34 @@
 
//===----------------------------------------------------------------------===//
 
 #include "src/stdio/puts.h"
+
+#include "hdr/stdio_macros.h" // for EOF
 #include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
-#include "src/stdio/baremetal/fputs_internal.h"
+#include "src/stdio/baremetal/file_internal.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
 LLVM_LIBC_FUNCTION(int, puts, (const char *__restrict str)) {
-  return fputs_internal(str, stdout);
+  cpp::string_view str_view(str);
+  auto result = write_internal(str_view.data(), str_view.size(), stdout);
+  if (result.has_error())
+    libc_errno = result.error;
+  size_t written = result.value;
+  if (str_view.size() != written) {
+    // The stream should be in an error state in this case.
+    return EOF;
+  }
+  result = write_internal("\n", 1, stdout);
+  if (result.has_error())
+    libc_errno = result.error;
+  written = result.value;
+  if (1 != written) {
+    // The stream should be in an error state in this case.
+    return EOF;
+  }
+  return 0;
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/baremetal/scanf.cpp 
b/libc/src/stdio/baremetal/scanf.cpp
index 8d07aa1da76aa..7f939ed0fcaa2 100644
--- a/libc/src/stdio/baremetal/scanf.cpp
+++ b/libc/src/stdio/baremetal/scanf.cpp
@@ -1,4 +1,4 @@
-//===-- Implementation of scanf ---------------------------------*- C++ 
-*-===//
+//===-- Implementation of scanf for baremetal -------------------*- C++ 
-*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -9,11 +9,9 @@
 #include "src/stdio/scanf.h"
 
 #include "hdr/stdio_macros.h"
-#include "src/__support/OSUtil/io.h"
 #include "src/__support/arg_list.h"
 #include "src/__support/macros/config.h"
-#include "src/stdio/baremetal/scanf_internal.h"
-#include "src/stdio/scanf_core/scanf_main.h"
+#include "src/stdio/baremetal/vfscanf_internal.h"
 
 #include <stdarg.h>
 
@@ -27,11 +25,10 @@ LLVM_LIBC_FUNCTION(int, scanf, (const char *__restrict 
format, ...)) {
                                  // destruction automatically.
   va_end(vlist);
 
-  scanf_core::StdinReader reader;
-  int retval = scanf_core::scanf_main(&reader, format, args);
+  int ret_val = vfscanf_internal(stdin, format, args);
   // This is done to avoid including stdio.h in the internals. On most systems
   // EOF is -1, so this will be transformed into just "return retval".
-  return (retval == -1) ? EOF : retval;
+  return (ret_val == -1) ? EOF : ret_val;
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/baremetal/scanf_internal.h 
b/libc/src/stdio/baremetal/scanf_internal.h
deleted file mode 100644
index 57d4b8c8d1a61..0000000000000
--- a/libc/src/stdio/baremetal/scanf_internal.h
+++ /dev/null
@@ -1,30 +0,0 @@
-//===-- Internal implementation header of scanf -----------------*- 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/__support/OSUtil/io.h"
-#include "src/__support/macros/config.h"
-#include "src/stdio/scanf_core/reader.h"
-
-namespace LIBC_NAMESPACE_DECL {
-
-namespace scanf_core {
-
-struct StdinReader : public Reader<StdinReader> {
-  LIBC_INLINE char getc() {
-    char buf[1];
-    auto result = read_from_stdin(buf, sizeof(buf));
-    if (result <= 0)
-      return EOF;
-    return buf[0];
-  }
-  LIBC_INLINE void ungetc(int) {}
-};
-
-} // namespace scanf_core
-
-} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/baremetal/vfscanf.cpp 
b/libc/src/stdio/baremetal/vfscanf.cpp
new file mode 100644
index 0000000000000..71b81ddd98765
--- /dev/null
+++ b/libc/src/stdio/baremetal/vfscanf.cpp
@@ -0,0 +1,36 @@
+//===-- Implementation of vfscanf for baremetal -----------------*- 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/vfscanf.h"
+
+#include "hdr/stdio_macros.h" // for EOF.
+#include "hdr/types/FILE.h"
+#include "src/__support/arg_list.h"
+#include "src/__support/macros/config.h"
+#include "src/stdio/baremetal/vfscanf_internal.h"
+
+#include "hdr/types/FILE.h"
+#include <stdarg.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, vfscanf,
+                   (::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.
+  va_end(vlist);
+
+  int ret_val = vfscanf_internal(stream, format, args);
+  // This is done to avoid including stdio.h in the internals. On most systems
+  // EOF is -1, so this will be transformed into just "return ret_val".
+  return (ret_val == -1) ? EOF : ret_val;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/baremetal/vfscanf_internal.h 
b/libc/src/stdio/baremetal/vfscanf_internal.h
new file mode 100644
index 0000000000000..21474e676ba79
--- /dev/null
+++ b/libc/src/stdio/baremetal/vfscanf_internal.h
@@ -0,0 +1,58 @@
+//===-- Baremetal implementation header of vfscanf --------------*- 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_BAREMETAL_VFPRINTF_INTERNAL_H
+#define LLVM_LIBC_SRC_STDIO_BAREMETAL_VFPRINTF_INTERNAL_H
+
+#include "hdr/stdio_macros.h" // for EOF.
+#include "hdr/types/FILE.h"
+#include "src/__support/CPP/limits.h"
+#include "src/__support/CPP/string_view.h"
+#include "src/__support/OSUtil/io.h"
+#include "src/__support/arg_list.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/stdio/scanf_core/reader.h"
+#include "src/stdio/scanf_core/scanf_main.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+namespace internal {
+
+class StreamReader : public scanf_core::Reader<StreamReader> {
+  ::FILE *stream;
+
+public:
+  LIBC_INLINE StreamReader(::FILE *stream) : stream(stream) {}
+
+  LIBC_INLINE char getc() {
+    char buf[1];
+    auto result = __llvm_libc_stdio_read(stream, buf, sizeof(buf));
+    if (result <= 0)
+      return EOF;
+    return buf[0];
+  }
+  LIBC_INLINE void ungetc(int) {}
+};
+
+} // namespace internal
+
+LIBC_INLINE int vfscanf_internal(::FILE *__restrict stream,
+                                 const char *__restrict format,
+                                 internal::ArgList &args) {
+  internal::StreamReader reader(stream);
+  // This is done to avoid including stdio.h in the internals. On most systems
+  // EOF is -1, so this will be transformed into just "return retval".
+  int retval = scanf_core::scanf_main(&reader, format, args);
+  return (retval == 0) ? EOF : retval;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDIO_BAREMETAL_VFPRINTF_INTERNAL_H
diff --git a/libc/src/stdio/baremetal/vprintf.cpp 
b/libc/src/stdio/baremetal/vprintf.cpp
index 6b61bc45e34cf..41190b86bdb89 100644
--- a/libc/src/stdio/baremetal/vprintf.cpp
+++ b/libc/src/stdio/baremetal/vprintf.cpp
@@ -8,6 +8,7 @@
 
 #include "src/stdio/vprintf.h"
 
+#include "hdr/stdio_macros.h"
 #include "src/__support/arg_list.h"
 #include "src/__support/common.h"
 #include "src/__support/macros/config.h"
diff --git a/libc/src/stdio/baremetal/vscanf.cpp 
b/libc/src/stdio/baremetal/vscanf.cpp
index 249f8fd5dbe3c..dc6c4d61ecf6e 100644
--- a/libc/src/stdio/baremetal/vscanf.cpp
+++ b/libc/src/stdio/baremetal/vscanf.cpp
@@ -1,4 +1,4 @@
-//===-- Implementation of vscanf --------------------------------*- C++ 
-*-===//
+//===-- Implementation of vscanf for baremetal ------------------*- C++ 
-*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -9,11 +9,9 @@
 #include "src/stdio/vscanf.h"
 
 #include "hdr/stdio_macros.h"
-#include "src/__support/OSUtil/io.h"
 #include "src/__support/arg_list.h"
 #include "src/__support/macros/config.h"
-#include "src/stdio/baremetal/scanf_internal.h"
-#include "src/stdio/scanf_core/scanf_main.h"
+#include "src/stdio/baremetal/vfscanf_internal.h"
 
 #include <stdarg.h>
 
@@ -26,11 +24,7 @@ LLVM_LIBC_FUNCTION(int, vscanf,
                                  // destruction automatically.
   va_end(vlist);
 
-  scanf_core::StdinReader reader;
-  int retval = scanf_core::scanf_main(&reader, format, args);
-  // This is done to avoid including stdio.h in the internals. On most systems
-  // EOF is -1, so this will be transformed into just "return retval".
-  return (retval == -1) ? EOF : retval;
+  return vfscanf_internal(stdin, format, args);
 }
 
 } // namespace LIBC_NAMESPACE_DECL

>From f1a2d3493b8c95f46b71300044af19f3e5ef5269 Mon Sep 17 00:00:00 2001
From: Petr Hosek <[email protected]>
Date: Fri, 12 Dec 2025 18:46:49 +0000
Subject: [PATCH 5/5] Use signed char

---
 libc/src/stdio/baremetal/fgetc.cpp   | 4 ++--
 libc/src/stdio/baremetal/fgets.cpp   | 4 ++--
 libc/src/stdio/baremetal/getchar.cpp | 4 ++--
 3 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/libc/src/stdio/baremetal/fgetc.cpp 
b/libc/src/stdio/baremetal/fgetc.cpp
index 4f9cd71628a99..c25a780c1605d 100644
--- a/libc/src/stdio/baremetal/fgetc.cpp
+++ b/libc/src/stdio/baremetal/fgetc.cpp
@@ -18,8 +18,8 @@
 namespace LIBC_NAMESPACE_DECL {
 
 LLVM_LIBC_FUNCTION(int, fgetc, (::FILE * stream)) {
-  unsigned char c;
-  auto result = read_internal(reinterpret_cast<char *>(&c), 1, stream);
+  char c;
+  auto result = read_internal(&c, 1, stream);
   if (result.has_error())
     libc_errno = result.error;
 
diff --git a/libc/src/stdio/baremetal/fgets.cpp 
b/libc/src/stdio/baremetal/fgets.cpp
index 6e8e5076231e5..76e80449aed11 100644
--- a/libc/src/stdio/baremetal/fgets.cpp
+++ b/libc/src/stdio/baremetal/fgets.cpp
@@ -23,12 +23,12 @@ LLVM_LIBC_FUNCTION(char *, fgets,
   if (count < 1)
     return nullptr;
 
-  unsigned char c = '\0';
+  char c = '\0';
   // i is an int because it's frequently compared to count, which is also int.
   int i = 0;
 
   for (; i < (count - 1) && c != '\n'; ++i) {
-    auto result = read_internal(reinterpret_cast<char *>(&c), 1, stream);
+    auto result = read_internal(&c, 1, stream);
     if (result.has_error())
       libc_errno = result.error;
 
diff --git a/libc/src/stdio/baremetal/getchar.cpp 
b/libc/src/stdio/baremetal/getchar.cpp
index 6e599c9337e9c..5afc74da659ce 100644
--- a/libc/src/stdio/baremetal/getchar.cpp
+++ b/libc/src/stdio/baremetal/getchar.cpp
@@ -17,8 +17,8 @@
 namespace LIBC_NAMESPACE_DECL {
 
 LLVM_LIBC_FUNCTION(int, getchar, ()) {
-  unsigned char c;
-  auto result = read_internal(reinterpret_cast<char *>(&c), 1, stdin);
+  char c;
+  auto result = read_internal(&c, 1, stdin);
   if (result.has_error())
     libc_errno = result.error;
 

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to