[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