diff --git a/libc/src/stdio/scanf_core/CMakeLists.txt b/libc/src/stdio/scanf_core/CMakeLists.txt --- a/libc/src/stdio/scanf_core/CMakeLists.txt +++ b/libc/src/stdio/scanf_core/CMakeLists.txt @@ -80,6 +80,7 @@ string_converter.cpp int_converter.cpp float_converter.cpp + ptr_converter.cpp HDRS converter.h converter_utils.h @@ -87,6 +88,7 @@ string_converter.h float_converter.h current_pos_converter.h + ptr_converter.h DEPENDS .reader .core_structs diff --git a/libc/src/stdio/scanf_core/converter.cpp b/libc/src/stdio/scanf_core/converter.cpp --- a/libc/src/stdio/scanf_core/converter.cpp +++ b/libc/src/stdio/scanf_core/converter.cpp @@ -17,6 +17,7 @@ #endif // LLVM_LIBC_SCANF_DISABLE_FLOAT #include "src/stdio/scanf_core/current_pos_converter.h" #include "src/stdio/scanf_core/int_converter.h" +#include "src/stdio/scanf_core/ptr_converter.h" #include "src/stdio/scanf_core/string_converter.h" #include @@ -63,11 +64,11 @@ #endif // LLVM_LIBC_SCANF_DISABLE_FLOAT case 'n': return convert_current_pos(reader, to_conv); - // case 'p': - // ret_val = raw_match(reader, " "); - // if (ret_val != READ_OK) - // return ret_val; - // return convert_pointer(reader, to_conv); + case 'p': + ret_val = raw_match(reader, " "); + if (ret_val != READ_OK) + return ret_val; + return convert_pointer(reader, to_conv); default: return raw_match(reader, to_conv.raw_string); } diff --git a/libc/src/stdio/scanf_core/converter_utils.h b/libc/src/stdio/scanf_core/converter_utils.h --- a/libc/src/stdio/scanf_core/converter_utils.h +++ b/libc/src/stdio/scanf_core/converter_utils.h @@ -35,8 +35,14 @@ if ((to_conv.flags & NO_WRITE) != 0) { return; } - LengthModifier lm = to_conv.length_modifier; void *output_ptr = to_conv.output_ptr; + // The %p conversion uses this function, and is always void*. + if (to_conv.conv_name == 'p') { + *reinterpret_cast(output_ptr) = + reinterpret_cast(output_val); + return; + } + LengthModifier lm = to_conv.length_modifier; switch (lm) { case (LengthModifier::hh): *reinterpret_cast(output_ptr) = diff --git a/libc/src/stdio/scanf_core/int_converter.cpp b/libc/src/stdio/scanf_core/int_converter.cpp --- a/libc/src/stdio/scanf_core/int_converter.cpp +++ b/libc/src/stdio/scanf_core/int_converter.cpp @@ -79,7 +79,7 @@ is_signed = true; } else if (to_conv.conv_name == 'o') { base = 8; - } else if (to_lower(to_conv.conv_name) == 'x') { + } else if (to_lower(to_conv.conv_name) == 'x' || to_conv.conv_name == 'p') { base = 16; } else if (to_conv.conv_name == 'd') { base = 10; diff --git a/libc/src/stdio/scanf_core/ptr_converter.h b/libc/src/stdio/scanf_core/ptr_converter.h new file mode 100644 --- /dev/null +++ b/libc/src/stdio/scanf_core/ptr_converter.h @@ -0,0 +1,25 @@ +//===-- Pointer specifier converter for 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_CORE_PTR_CONVERTER_H +#define LLVM_LIBC_SRC_STDIO_SCANF_CORE_PTR_CONVERTER_H + +#include "src/stdio/scanf_core/core_structs.h" +#include "src/stdio/scanf_core/reader.h" + +#include + +namespace __llvm_libc { +namespace scanf_core { + +int convert_pointer(Reader *reader, const FormatSection &to_conv); + +} // namespace scanf_core +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDIO_SCANF_CORE_PTR_CONVERTER_H diff --git a/libc/src/stdio/scanf_core/ptr_converter.cpp b/libc/src/stdio/scanf_core/ptr_converter.cpp new file mode 100644 --- /dev/null +++ b/libc/src/stdio/scanf_core/ptr_converter.cpp @@ -0,0 +1,43 @@ +//===-- Int type specifier converters for 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_core/ptr_converter.h" + +#include "src/stdio/scanf_core/converter_utils.h" +#include "src/stdio/scanf_core/core_structs.h" +#include "src/stdio/scanf_core/int_converter.h" +#include "src/stdio/scanf_core/reader.h" + +#include + +namespace __llvm_libc { +namespace scanf_core { +int convert_pointer(Reader *reader, const FormatSection &to_conv) { + static const char nullptr_string[] = "(nullptr)"; + + // Check if it's exactly the nullptr string, if so then it's a nullptr. + char cur_char = reader->getc(); + size_t i = 0; + for (; i < sizeof(nullptr_string) && to_lower(cur_char) == nullptr_string[i]; + ++i) { + cur_char = reader->getc(); + } + if (i == (sizeof(nullptr_string) - 1)) { + *reinterpret_cast(to_conv.output_ptr) = nullptr; + return READ_OK; + } else if (i > 0) { + return MATCHING_FAILURE; + } + + reader->ungetc(cur_char); + + // Else treat it as a hex int + return convert_int(reader, to_conv); +} +} // namespace scanf_core +} // namespace __llvm_libc diff --git a/libc/test/src/stdio/sscanf_test.cpp b/libc/test/src/stdio/sscanf_test.cpp --- a/libc/test/src/stdio/sscanf_test.cpp +++ b/libc/test/src/stdio/sscanf_test.cpp @@ -629,6 +629,45 @@ EXPECT_EQ(result, 320); } +TEST(LlvmLibcSScanfTest, PointerConvCombined) { + int ret_val; + void *result; + + ret_val = __llvm_libc::sscanf("(nullptr)", "%p", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_EQ(result, static_cast(nullptr)); + + ret_val = __llvm_libc::sscanf("(NuLlPtR)", "%p", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_EQ(result, static_cast(nullptr)); + + ret_val = __llvm_libc::sscanf("(NULLPTR)", "%p", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_EQ(result, static_cast(nullptr)); + + ret_val = __llvm_libc::sscanf("(null)", "%p", &result); + EXPECT_EQ(ret_val, 0); + + ret_val = __llvm_libc::sscanf("(nullptr2", "%p", &result); + EXPECT_EQ(ret_val, 0); + + ret_val = __llvm_libc::sscanf("0", "%p", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_EQ(result, reinterpret_cast(0)); + + ret_val = __llvm_libc::sscanf("100", "%p", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_EQ(result, reinterpret_cast(0x100)); + + ret_val = __llvm_libc::sscanf("-1", "%p", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_EQ(result, reinterpret_cast(-1)); + + ret_val = __llvm_libc::sscanf("0xabcDEFG", "%p", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_EQ(result, reinterpret_cast(0xabcdef)); +} + TEST(LlvmLibcSScanfTest, CombinedConv) { int ret_val; int result = 0;