[libc-commits] [libc] b8f134f - [libc] Implement 'vfscanf' and 'vscanf' (#105293)

via libc-commits libc-commits at lists.llvm.org
Mon Aug 26 07:00:14 PDT 2024


Author: Joseph Huber
Date: 2024-08-26T09:00:10-05:00
New Revision: b8f134faba3a41f47d2d05125118ea1acf512cb3

URL: https://github.com/llvm/llvm-project/commit/b8f134faba3a41f47d2d05125118ea1acf512cb3
DIFF: https://github.com/llvm/llvm-project/commit/b8f134faba3a41f47d2d05125118ea1acf512cb3.diff

LOG: [libc] Implement 'vfscanf' and 'vscanf' (#105293)

Summary:
These are simply forwarding the vlist to the existing helper.

Added: 
    libc/src/stdio/vfscanf.cpp
    libc/src/stdio/vfscanf.h
    libc/src/stdio/vscanf.cpp
    libc/src/stdio/vscanf.h
    libc/test/src/stdio/vfscanf_test.cpp

Modified: 
    libc/config/linux/aarch64/entrypoints.txt
    libc/config/linux/riscv/entrypoints.txt
    libc/config/linux/x86_64/entrypoints.txt
    libc/newhdrgen/yaml/stdio.yaml
    libc/spec/stdc.td
    libc/src/stdio/CMakeLists.txt
    libc/test/src/stdio/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index d22bd1153598eb..60aa7f5ccb319a 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -211,10 +211,12 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.stdio.fileno
     libc.src.stdio.fprintf
     libc.src.stdio.fscanf
+    libc.src.stdio.vfscanf
     libc.src.stdio.printf
     libc.src.stdio.remove
     libc.src.stdio.rename
     libc.src.stdio.scanf
+    libc.src.stdio.vscanf
     libc.src.stdio.snprintf
     libc.src.stdio.sprintf
     libc.src.stdio.asprintf

diff  --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 1a647737ec455a..9a2746dcb86f87 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -210,10 +210,12 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.stdio.fileno
     libc.src.stdio.fprintf
     libc.src.stdio.fscanf
+    libc.src.stdio.vfscanf
     libc.src.stdio.printf
     libc.src.stdio.remove
     libc.src.stdio.rename
     libc.src.stdio.scanf
+    libc.src.stdio.vscanf
     libc.src.stdio.snprintf
     libc.src.stdio.sprintf
     libc.src.stdio.asprintf

diff  --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index bac1e3cfa85da7..141dc70463d64a 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -210,10 +210,12 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.stdio.fileno
     libc.src.stdio.fprintf
     libc.src.stdio.fscanf
+    libc.src.stdio.vfscanf
     libc.src.stdio.printf
     libc.src.stdio.remove
     libc.src.stdio.rename
     libc.src.stdio.scanf
+    libc.src.stdio.vscanf
     libc.src.stdio.snprintf
     libc.src.stdio.sprintf
     libc.src.stdio.asprintf

diff  --git a/libc/newhdrgen/yaml/stdio.yaml b/libc/newhdrgen/yaml/stdio.yaml
index 43438699b58409..fd116bbc00895d 100644
--- a/libc/newhdrgen/yaml/stdio.yaml
+++ b/libc/newhdrgen/yaml/stdio.yaml
@@ -178,6 +178,14 @@ functions:
       - type: FILE *__restrict
       - type: const char *__restrict
       - type: '...'
+  - name: vfscanf
+    standards:
+      - stdc
+    return_type: int
+    arguments:
+      - type: FILE *__restrict
+      - type: const char *__restrict
+      - type: va_list
   - name: fseek
     standards:
       - stdc
@@ -284,6 +292,13 @@ functions:
     arguments:
       - type: const char *__restrict
       - type: '...'
+  - name: vscanf
+    standards:
+      - stdc
+    return_type: int
+    arguments:
+      - type: const char *__restrict
+      - type: va_list
   - name: setbuf
     standards:
       - stdc

diff  --git a/libc/spec/stdc.td b/libc/spec/stdc.td
index 6d8be9f8e4016d..026cc72b458a77 100644
--- a/libc/spec/stdc.td
+++ b/libc/spec/stdc.td
@@ -1042,6 +1042,12 @@ def StdC : StandardSpec<"stdc"> {
               [ArgSpec<ConstCharRestrictedPtr>,
                ArgSpec<VarArgType>]
           >,
+          FunctionSpec<
+              "vscanf",
+              RetValSpec<IntType>,
+              [ArgSpec<ConstCharRestrictedPtr>,
+               ArgSpec<VaListType>]
+          >,
           FunctionSpec<
               "fscanf",
               RetValSpec<IntType>,
@@ -1049,6 +1055,13 @@ def StdC : StandardSpec<"stdc"> {
                ArgSpec<ConstCharRestrictedPtr>,
                ArgSpec<VarArgType>]
           >,
+          FunctionSpec<
+              "vfscanf",
+              RetValSpec<IntType>,
+              [ArgSpec<FILERestrictedPtr>,
+               ArgSpec<ConstCharRestrictedPtr>,
+               ArgSpec<VaListType>]
+          >,
           FunctionSpec<
               "sprintf",
               RetValSpec<IntType>,

diff  --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt
index 372b8fc8192455..b9bc904471df9a 100644
--- a/libc/src/stdio/CMakeLists.txt
+++ b/libc/src/stdio/CMakeLists.txt
@@ -143,6 +143,16 @@ add_entrypoint_object(
     ${scanf_deps}
 )
 
+add_entrypoint_object(
+  vfscanf
+  SRCS
+    vfscanf.cpp
+  HDRS
+    vfscanf.h
+  DEPENDS
+    ${scanf_deps}
+)
+
 add_entrypoint_object(
   scanf
   SRCS
@@ -153,6 +163,16 @@ add_entrypoint_object(
     ${scanf_deps}
 )
 
+add_entrypoint_object(
+  vscanf
+  SRCS
+    vscanf.cpp
+  HDRS
+    vscanf.h
+  DEPENDS
+    ${scanf_deps}
+)
+
 add_entrypoint_object(
   sprintf
   SRCS

diff  --git a/libc/src/stdio/vfscanf.cpp b/libc/src/stdio/vfscanf.cpp
new file mode 100644
index 00000000000000..220576522d0fdb
--- /dev/null
+++ b/libc/src/stdio/vfscanf.cpp
@@ -0,0 +1,34 @@
+//===-- Implementation 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/stdio/vfscanf.h"
+
+#include "src/__support/File/file.h"
+#include "src/__support/arg_list.h"
+#include "src/__support/macros/config.h"
+#include "src/stdio/scanf_core/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 = scanf_core::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/vfscanf.h b/libc/src/stdio/vfscanf.h
new file mode 100644
index 00000000000000..1a0a12d9eb4cd3
--- /dev/null
+++ b/libc/src/stdio/vfscanf.h
@@ -0,0 +1,24 @@
+//===-- 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_VFSCANF_H
+#define LLVM_LIBC_SRC_STDIO_VFSCANF_H
+
+#include "hdr/types/FILE.h"
+#include "src/__support/macros/config.h"
+
+#include <stdarg.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+int vfscanf(::FILE *__restrict stream, const char *__restrict format,
+            va_list vlist);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDIO_VFSCANF_H

diff  --git a/libc/src/stdio/vscanf.cpp b/libc/src/stdio/vscanf.cpp
new file mode 100644
index 00000000000000..64f5cc1d6962a1
--- /dev/null
+++ b/libc/src/stdio/vscanf.cpp
@@ -0,0 +1,40 @@
+//===-- Implementation of vscanf --------------------------------*- 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/vscanf.h"
+
+#include "src/__support/File/file.h"
+#include "src/__support/arg_list.h"
+#include "src/__support/macros/config.h"
+#include "src/stdio/scanf_core/vfscanf_internal.h"
+
+#include "hdr/types/FILE.h"
+#include <stdarg.h>
+
+#ifndef LIBC_COPT_STDIO_USE_SYSTEM_FILE
+#define SCANF_STDIN LIBC_NAMESPACE::stdin
+#else // LIBC_COPT_STDIO_USE_SYSTEM_FILE
+#define SCANF_STDIN ::stdin
+#endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, vscanf,
+                   (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 = scanf_core::vfscanf_internal(
+      reinterpret_cast<::FILE *>(SCANF_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 ret_val".
+  return (ret_val == -1) ? EOF : ret_val;
+}
+
+} // namespace LIBC_NAMESPACE_DECL

diff  --git a/libc/src/stdio/vscanf.h b/libc/src/stdio/vscanf.h
new file mode 100644
index 00000000000000..5c59b91128ea32
--- /dev/null
+++ b/libc/src/stdio/vscanf.h
@@ -0,0 +1,23 @@
+//===-- Implementation header of vscanf -------------------------*- 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_VSCANF_H
+#define LLVM_LIBC_SRC_STDIO_VSCANF_H
+
+#include "hdr/types/FILE.h"
+#include "src/__support/macros/config.h"
+
+#include <stdarg.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+int vscanf(const char *__restrict format, va_list vlist);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDIO_VSCANF_H

diff  --git a/libc/test/src/stdio/CMakeLists.txt b/libc/test/src/stdio/CMakeLists.txt
index 8b05b928a02695..d7b88186b5704a 100644
--- a/libc/test/src/stdio/CMakeLists.txt
+++ b/libc/test/src/stdio/CMakeLists.txt
@@ -286,6 +286,20 @@ add_libc_test(
     ${use_system_file}
 )
 
+add_libc_test(
+  vfscanf_test
+  SUITE
+    libc_stdio_unittests
+  SRCS
+    vfscanf_test.cpp
+  DEPENDS
+    libc.src.stdio.vfscanf
+    ${fscanf_test_deps}
+    libc.src.__support.CPP.string_view
+  COMPILE_OPTIONS
+    ${use_system_file}
+)
+
 if(LIBC_CONF_SCANF_DISABLE_FLOAT)
   list(APPEND sscanf_test_copts "-DLIBC_COPT_SCANF_DISABLE_FLOAT")
 endif()

diff  --git a/libc/test/src/stdio/vfscanf_test.cpp b/libc/test/src/stdio/vfscanf_test.cpp
new file mode 100644
index 00000000000000..7a9cbf7f123880
--- /dev/null
+++ b/libc/test/src/stdio/vfscanf_test.cpp
@@ -0,0 +1,98 @@
+//===-- Unittests for vfscanf ---------------------------------------------===//
+//
+// 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/CPP/string_view.h"
+
+#ifndef LIBC_COPT_STDIO_USE_SYSTEM_FILE
+#include "src/stdio/fclose.h"
+#include "src/stdio/ferror.h"
+#include "src/stdio/fopen.h"
+#include "src/stdio/fwrite.h"
+#endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE
+
+#include "src/stdio/vfscanf.h"
+
+#include "test/UnitTest/Test.h"
+
+#include <stdio.h>
+
+namespace scanf_test {
+#ifndef LIBC_COPT_STDIO_USE_SYSTEM_FILE
+using LIBC_NAMESPACE::fclose;
+using LIBC_NAMESPACE::ferror;
+using LIBC_NAMESPACE::fopen;
+using LIBC_NAMESPACE::fwrite;
+#else  // defined(LIBC_COPT_STDIO_USE_SYSTEM_FILE)
+using ::fclose;
+using ::ferror;
+using ::fopen;
+using ::fwrite;
+#endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE
+} // namespace scanf_test
+
+static int call_vfscanf(::FILE *stream, const char *__restrict format, ...) {
+  va_list vlist;
+  va_start(vlist, format);
+  int ret = LIBC_NAMESPACE::vfscanf(stream, format, vlist);
+  va_end(vlist);
+  return ret;
+}
+
+TEST(LlvmLibcFScanfTest, WriteToFile) {
+  const char *FILENAME = "fscanf_output.test";
+  auto FILE_PATH = libc_make_test_file_path(FILENAME);
+  ::FILE *file = scanf_test::fopen(FILE_PATH, "w");
+  ASSERT_FALSE(file == nullptr);
+
+  int read;
+
+  constexpr char simple[] = "A simple string with no conversions.\n";
+
+  ASSERT_EQ(sizeof(simple) - 1,
+            scanf_test::fwrite(simple, 1, sizeof(simple) - 1, file));
+
+  constexpr char numbers[] = "1234567890\n";
+
+  ASSERT_EQ(sizeof(numbers) - 1,
+            scanf_test::fwrite(numbers, 1, sizeof(numbers) - 1, file));
+
+  constexpr char numbers_and_more[] = "1234 and more\n";
+
+  ASSERT_EQ(sizeof(numbers_and_more) - 1,
+            scanf_test::fwrite(numbers_and_more, 1,
+                               sizeof(numbers_and_more) - 1, file));
+
+  read = call_vfscanf(file, "Reading from a write-only file should fail.");
+  EXPECT_LT(read, 0);
+
+  ASSERT_EQ(0, scanf_test::fclose(file));
+
+  file = scanf_test::fopen(FILE_PATH, "r");
+  ASSERT_FALSE(file == nullptr);
+
+  char data[50];
+  read = call_vfscanf(file, "%[A-Za-z .\n]", data);
+  ASSERT_EQ(read, 1);
+  ASSERT_STREQ(simple, data);
+
+  read = call_vfscanf(file, "%s", data);
+  ASSERT_EQ(read, 1);
+  ASSERT_EQ(LIBC_NAMESPACE::cpp::string_view(numbers, 10),
+            LIBC_NAMESPACE::cpp::string_view(data));
+
+  // The format string starts with a space to handle the fact that the %s leaves
+  // a trailing \n and %c doesn't strip leading whitespace.
+  read = call_vfscanf(file, " %50c", data);
+  ASSERT_EQ(read, 1);
+  ASSERT_EQ(
+      LIBC_NAMESPACE::cpp::string_view(numbers_and_more),
+      LIBC_NAMESPACE::cpp::string_view(data, sizeof(numbers_and_more) - 1));
+
+  ASSERT_EQ(scanf_test::ferror(file), 0);
+  ASSERT_EQ(scanf_test::fclose(file), 0);
+}


        


More information about the libc-commits mailing list