[libc-commits] [libc] 36991d8 - [libc] add scanf entrypoints

Michael Jones via libc-commits libc-commits at lists.llvm.org
Thu Nov 17 15:37:56 PST 2022


Author: Michael Jones
Date: 2022-11-17T15:37:52-08:00
New Revision: 36991d8342ca74b68cf74a148af2d1e87823159a

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

LOG: [libc] add scanf entrypoints

This patch adds scanf, sscanf, and fscanf entrypoints. It also adds unit
tests for sscanf and a basic test to fscanf. The scanf function is
basically impossible to test in an automated fashion due to it recieving
user input.

Reviewed By: sivachandra, lntue

Differential Revision: https://reviews.llvm.org/D138076

Added: 
    libc/src/stdio/fscanf.cpp
    libc/src/stdio/fscanf.h
    libc/src/stdio/scanf.cpp
    libc/src/stdio/scanf.h
    libc/src/stdio/scanf_core/vfscanf_internal.cpp
    libc/src/stdio/scanf_core/vfscanf_internal.h
    libc/src/stdio/sscanf.cpp
    libc/src/stdio/sscanf.h
    libc/test/src/stdio/fscanf_test.cpp
    libc/test/src/stdio/sscanf_test.cpp

Modified: 
    libc/config/linux/x86_64/entrypoints.txt
    libc/spec/stdc.td
    libc/src/stdio/CMakeLists.txt
    libc/src/stdio/scanf_core/CMakeLists.txt
    libc/test/src/stdio/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 1512640b7c147..e9e173a831013 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -394,6 +394,9 @@ if(LLVM_LIBC_FULL_BUILD)
     libc.src.stdio.getc
     libc.src.stdio.getc_unlocked
     libc.src.stdio.printf
+    libc.src.stdio.sscanf
+    libc.src.stdio.scanf
+    libc.src.stdio.fscanf
     libc.src.stdio.putc
     libc.src.stdio.putchar
     libc.src.stdio.puts

diff  --git a/libc/spec/stdc.td b/libc/spec/stdc.td
index 4d1295313e5ae..f439a1a6ba2c4 100644
--- a/libc/spec/stdc.td
+++ b/libc/spec/stdc.td
@@ -633,6 +633,26 @@ def StdC : StandardSpec<"stdc"> {
               RetValSpec<IntType>,
               [ArgSpec<FILERestrictedPtr>, ArgSpec<CharRestrictedPtr>, ArgSpec<IntType>, ArgSpec<SizeTType>]
           >,
+          FunctionSpec<
+              "sscanf",
+              RetValSpec<IntType>,
+              [ArgSpec<ConstCharRestrictedPtr>,
+               ArgSpec<ConstCharRestrictedPtr>,
+               ArgSpec<VarArgType>]
+          >,
+          FunctionSpec<
+              "scanf",
+              RetValSpec<IntType>,
+              [ArgSpec<ConstCharRestrictedPtr>,
+               ArgSpec<VarArgType>]
+          >,
+          FunctionSpec<
+              "fscanf",
+              RetValSpec<IntType>,
+              [ArgSpec<FILERestrictedPtr>,
+               ArgSpec<ConstCharRestrictedPtr>,
+               ArgSpec<VarArgType>]
+          >,
           FunctionSpec<
               "sprintf",
               RetValSpec<IntType>,

diff  --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt
index d4b39767473e5..1a8308e50c2a8 100644
--- a/libc/src/stdio/CMakeLists.txt
+++ b/libc/src/stdio/CMakeLists.txt
@@ -403,6 +403,41 @@ add_entrypoint_object(
     libc.src.__support.File.platform_file
 )
 
+add_entrypoint_object(
+  sscanf
+  SRCS
+    sscanf.cpp
+  HDRS
+    sscanf.h
+  DEPENDS
+    libc.src.__support.arg_list
+    libc.src.stdio.scanf_core.string_reader
+    libc.src.stdio.scanf_core.reader
+    libc.src.stdio.scanf_core.scanf_main
+)
+
+add_entrypoint_object(
+  fscanf
+  SRCS
+    fscanf.cpp
+  HDRS
+    fscanf.h
+  DEPENDS
+    libc.src.__support.arg_list
+    libc.src.stdio.scanf_core.vfscanf_internal
+)
+
+add_entrypoint_object(
+  scanf
+  SRCS
+    scanf.cpp
+  HDRS
+    scanf.h
+  DEPENDS
+    libc.src.__support.arg_list
+    libc.src.stdio.scanf_core.vfscanf_internal
+)
+
 add_entrypoint_object(
   sprintf
   SRCS

diff  --git a/libc/src/stdio/fscanf.cpp b/libc/src/stdio/fscanf.cpp
new file mode 100644
index 0000000000000..4fe0a3e57dcaa
--- /dev/null
+++ b/libc/src/stdio/fscanf.cpp
@@ -0,0 +1,35 @@
+//===-- Implementation of fscanf --------------------------------*- 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 "src/__support/File/file.h"
+#include "src/__support/arg_list.h"
+#include "src/stdio/scanf_core/vfscanf_internal.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+
+namespace __llvm_libc {
+
+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 = 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 __llvm_libc

diff  --git a/libc/src/stdio/fscanf.h b/libc/src/stdio/fscanf.h
new file mode 100644
index 0000000000000..cc36cd7c9af8e
--- /dev/null
+++ b/libc/src/stdio/fscanf.h
@@ -0,0 +1,20 @@
+//===-- Implementation header of fscanf -------------------------*- 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_FSCANF_H
+#define LLVM_LIBC_SRC_STDIO_FSCANF_H
+
+#include <stdio.h>
+
+namespace __llvm_libc {
+
+int fscanf(::FILE *__restrict stream, const char *__restrict format, ...);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_STDIO_FSCANF_H

diff  --git a/libc/src/stdio/scanf.cpp b/libc/src/stdio/scanf.cpp
new file mode 100644
index 0000000000000..60e77895edcc3
--- /dev/null
+++ b/libc/src/stdio/scanf.cpp
@@ -0,0 +1,34 @@
+//===-- Implementation 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/stdio/scanf.h"
+
+#include "src/__support/File/file.h"
+#include "src/__support/arg_list.h"
+#include "src/stdio/scanf_core/vfscanf_internal.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(int, scanf, (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 = scanf_core::vfscanf_internal(
+      reinterpret_cast<::FILE *>(__llvm_libc::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 __llvm_libc

diff  --git a/libc/src/stdio/scanf.h b/libc/src/stdio/scanf.h
new file mode 100644
index 0000000000000..6b4ece12a37f5
--- /dev/null
+++ b/libc/src/stdio/scanf.h
@@ -0,0 +1,18 @@
+//===-- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_STDIO_SCANF_H
+#define LLVM_LIBC_SRC_STDIO_SCANF_H
+
+namespace __llvm_libc {
+
+int scanf(const char *__restrict format, ...);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_STDIO_SCANF_H

diff  --git a/libc/src/stdio/scanf_core/CMakeLists.txt b/libc/src/stdio/scanf_core/CMakeLists.txt
index 940e9f0d083f3..ef8ec0ab6f0fb 100644
--- a/libc/src/stdio/scanf_core/CMakeLists.txt
+++ b/libc/src/stdio/scanf_core/CMakeLists.txt
@@ -89,3 +89,16 @@ add_object_library(
     libc.src.__support.CPP.string_view
     libc.src.__support.CPP.limits
 )
+
+add_object_library(
+  vfscanf_internal
+  SRCS
+    vfscanf_internal.cpp
+  HDRS
+    vfscanf_internal.h
+  DEPENDS
+    .reader
+    .file_reader
+    .scanf_main
+    libc.src.__support.arg_list
+)

diff  --git a/libc/src/stdio/scanf_core/vfscanf_internal.cpp b/libc/src/stdio/scanf_core/vfscanf_internal.cpp
new file mode 100644
index 0000000000000..af2f6fa01ad47
--- /dev/null
+++ b/libc/src/stdio/scanf_core/vfscanf_internal.cpp
@@ -0,0 +1,29 @@
+//===-- Internal 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/scanf_core/vfscanf_internal.h"
+
+#include "src/__support/arg_list.h"
+#include "src/stdio/scanf_core/file_reader.h"
+#include "src/stdio/scanf_core/reader.h"
+#include "src/stdio/scanf_core/scanf_main.h"
+
+#include <stdio.h>
+
+namespace __llvm_libc {
+namespace scanf_core {
+
+int vfscanf_internal(::FILE *__restrict stream, const char *__restrict format,
+                     internal::ArgList &args) {
+  FileReader file_reader(stream);
+  scanf_core::Reader reader(&file_reader);
+  return scanf_core::scanf_main(&reader, format, args);
+}
+
+} // namespace scanf_core
+} // namespace __llvm_libc

diff  --git a/libc/src/stdio/scanf_core/vfscanf_internal.h b/libc/src/stdio/scanf_core/vfscanf_internal.h
new file mode 100644
index 0000000000000..456d1ff92056b
--- /dev/null
+++ b/libc/src/stdio/scanf_core/vfscanf_internal.h
@@ -0,0 +1,24 @@
+//===-- Internal 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_SCANF_CORE_VFSCANF_INTERNAL_H
+#define LLVM_LIBC_SRC_STDIO_SCANF_CORE_VFSCANF_INTERNAL_H
+
+#include "src/__support/arg_list.h"
+
+#include <stdio.h>
+
+namespace __llvm_libc {
+namespace scanf_core {
+
+int vfscanf_internal(::FILE *__restrict stream, const char *__restrict format,
+                     internal::ArgList &args);
+} // namespace scanf_core
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_STDIO_SCANF_CORE_VFSCANF_INTERNAL_H

diff  --git a/libc/src/stdio/sscanf.cpp b/libc/src/stdio/sscanf.cpp
new file mode 100644
index 0000000000000..205b58fb3390e
--- /dev/null
+++ b/libc/src/stdio/sscanf.cpp
@@ -0,0 +1,38 @@
+//===-- Implementation of sscanf --------------------------------*- 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/sscanf.h"
+
+#include "src/__support/arg_list.h"
+#include "src/stdio/scanf_core/reader.h"
+#include "src/stdio/scanf_core/scanf_main.h"
+#include "src/stdio/scanf_core/string_reader.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(int, sscanf,
+                   (const char *__restrict buffer,
+                    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);
+  scanf_core::StringReader string_reader(buffer);
+  scanf_core::Reader reader(&string_reader);
+  int ret_val = 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 ret_val".
+  return (ret_val == -1) ? EOF : ret_val;
+}
+
+} // namespace __llvm_libc

diff  --git a/libc/src/stdio/sscanf.h b/libc/src/stdio/sscanf.h
new file mode 100644
index 0000000000000..7f63638e4c1d9
--- /dev/null
+++ b/libc/src/stdio/sscanf.h
@@ -0,0 +1,18 @@
+//===-- Implementation header of sscanf -------------------------*- 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_SSCANF_H
+#define LLVM_LIBC_SRC_STDIO_SSCANF_H
+
+namespace __llvm_libc {
+
+int sscanf(const char *__restrict buffer, const char *__restrict format, ...);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_STDIO_SSCANF_H

diff  --git a/libc/test/src/stdio/CMakeLists.txt b/libc/test/src/stdio/CMakeLists.txt
index f27d7bba1c562..f74fa46a5e77d 100644
--- a/libc/test/src/stdio/CMakeLists.txt
+++ b/libc/test/src/stdio/CMakeLists.txt
@@ -159,6 +159,31 @@ add_libc_unittest(
     libc.src.stdio.printf
 )
 
+add_libc_unittest(
+  fscanf_test
+  SUITE
+    libc_stdio_unittests
+  SRCS
+    fscanf_test.cpp
+  DEPENDS
+    libc.src.stdio.fscanf
+    libc.src.stdio.fclose
+    libc.src.stdio.ferror
+    libc.src.stdio.fopen
+    libc.src.stdio.fwrite
+    libc.src.__support.CPP.string_view
+)
+
+add_libc_unittest(
+  sscanf_test
+  SUITE
+    libc_stdio_unittests
+  SRCS
+    sscanf_test.cpp
+  DEPENDS
+    libc.src.stdio.sscanf
+)
+
 add_libc_unittest(
   puts_test
   SUITE

diff  --git a/libc/test/src/stdio/fscanf_test.cpp b/libc/test/src/stdio/fscanf_test.cpp
new file mode 100644
index 0000000000000..e73b6a6e7fb71
--- /dev/null
+++ b/libc/test/src/stdio/fscanf_test.cpp
@@ -0,0 +1,73 @@
+//===-- Unittests for fscanf ----------------------------------------------===//
+//
+// 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"
+#include "src/stdio/fclose.h"
+#include "src/stdio/ferror.h"
+#include "src/stdio/fopen.h"
+#include "src/stdio/fwrite.h"
+
+#include "src/stdio/fscanf.h"
+
+#include "utils/UnitTest/Test.h"
+
+#include <errno.h>
+#include <stdio.h>
+
+TEST(LlvmLibcFScanfTest, WriteToFile) {
+  constexpr char FILENAME[] = "testdata/fscanf_output.test";
+  ::FILE *file = __llvm_libc::fopen(FILENAME, "w");
+  ASSERT_FALSE(file == nullptr);
+
+  int read;
+
+  constexpr char simple[] = "A simple string with no conversions.\n";
+
+  ASSERT_EQ(sizeof(simple) - 1,
+            __llvm_libc::fwrite(simple, 1, sizeof(simple) - 1, file));
+
+  constexpr char numbers[] = "1234567890\n";
+
+  ASSERT_EQ(sizeof(numbers) - 1,
+            __llvm_libc::fwrite(numbers, 1, sizeof(numbers) - 1, file));
+
+  constexpr char numbers_and_more[] = "1234 and more\n";
+
+  ASSERT_EQ(sizeof(numbers_and_more) - 1,
+            __llvm_libc::fwrite(numbers_and_more, 1,
+                                sizeof(numbers_and_more) - 1, file));
+
+  read =
+      __llvm_libc::fscanf(file, "Reading from a write-only file should fail.");
+  EXPECT_LT(read, 0);
+
+  ASSERT_EQ(0, __llvm_libc::fclose(file));
+
+  file = __llvm_libc::fopen(FILENAME, "r");
+  ASSERT_FALSE(file == nullptr);
+
+  char data[50];
+  read = __llvm_libc::fscanf(file, "%[A-Za-z .\n]", data);
+  ASSERT_EQ(read, 1);
+  ASSERT_STREQ(simple, data);
+
+  read = __llvm_libc::fscanf(file, "%s", data);
+  ASSERT_EQ(read, 1);
+  ASSERT_EQ(__llvm_libc::cpp::string_view(numbers, 10),
+            __llvm_libc::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 = __llvm_libc::fscanf(file, " %50c", data);
+  ASSERT_EQ(read, 1);
+  ASSERT_EQ(__llvm_libc::cpp::string_view(numbers_and_more),
+            __llvm_libc::cpp::string_view(data, sizeof(numbers_and_more) - 1));
+
+  ASSERT_EQ(__llvm_libc::ferror(file), 0);
+  ASSERT_EQ(__llvm_libc::fclose(file), 0);
+}

diff  --git a/libc/test/src/stdio/sscanf_test.cpp b/libc/test/src/stdio/sscanf_test.cpp
new file mode 100644
index 0000000000000..70f652361b7d3
--- /dev/null
+++ b/libc/test/src/stdio/sscanf_test.cpp
@@ -0,0 +1,30 @@
+//===-- Unittests for sscanf ----------------------------------------------===//
+//
+// 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/sscanf.h"
+
+#include "utils/UnitTest/Test.h"
+
+TEST(LlvmLibcSScanfTest, SimpleStringConv) {
+  int ret_val;
+  char buffer[10];
+  char buffer2[10];
+  ret_val = __llvm_libc::sscanf("abc123", "abc %s", buffer);
+  ASSERT_EQ(ret_val, 1);
+  ASSERT_STREQ(buffer, "123");
+
+  ret_val = __llvm_libc::sscanf("abc123", "%3s %3s", buffer, buffer2);
+  ASSERT_EQ(ret_val, 2);
+  ASSERT_STREQ(buffer, "abc");
+  ASSERT_STREQ(buffer2, "123");
+
+  ret_val = __llvm_libc::sscanf("abc 123", "%3s%3s", buffer, buffer2);
+  ASSERT_EQ(ret_val, 2);
+  ASSERT_STREQ(buffer, "abc");
+  ASSERT_STREQ(buffer2, "123");
+}


        


More information about the libc-commits mailing list