mirror of
https://github.com/SerenityOS/serenity
synced 2026-04-25 17:15:42 +02:00
LibC: Support width specifiers for scanf
This commit is contained in:
@@ -15,6 +15,7 @@ typedef long long longlong;
|
||||
typedef unsigned long long unsignedlonglong;
|
||||
typedef unsigned long unsignedlong;
|
||||
typedef char charstar[32];
|
||||
typedef void* voidstar;
|
||||
|
||||
template<typename T>
|
||||
constexpr static Array<unsigned char, 32> to_value_t(T x)
|
||||
@@ -128,6 +129,7 @@ DECL_WITH_TYPE(double);
|
||||
DECL_WITH_TYPE(longdouble);
|
||||
DECL_WITH_TYPE(unsignedlong);
|
||||
DECL_WITH_TYPE(unsignedlonglong);
|
||||
DECL_WITH_TYPE(voidstar);
|
||||
|
||||
#undef DECL_WITH_TYPE
|
||||
|
||||
@@ -151,9 +153,9 @@ TestSuite const test_suites[] {
|
||||
{ "%d", "", 0, 0, {}, {} },
|
||||
{ "%x", "0x519", 1, 1, { unsignedarg0 }, { to_value_t(0x519) } },
|
||||
{ "%x", "0x51g", 1, 1, { unsignedarg0 }, { to_value_t(0x51u) } },
|
||||
{ "%06x", "0xabcdef", 1, 1, { unsignedarg0 }, { to_value_t(0xabcdefu) } },
|
||||
{ "%08x", "0xabcdef", 1, 1, { unsignedarg0 }, { to_value_t(0xabcdefu) } },
|
||||
{ "%X", "0xCAFEBABE", 1, 1, { unsignedarg0 }, { to_value_t(0xcafebabe) } },
|
||||
{ "%04X", "0x5E4E", 1, 1, { unsignedarg0 }, { to_value_t(0x5e4e) } },
|
||||
{ "%06X", "0x5E4E", 1, 1, { unsignedarg0 }, { to_value_t(0x5e4e) } },
|
||||
{ "%X", "0x51Eg", 1, 1, { unsignedarg0 }, { to_value_t(0x51e) } },
|
||||
{ "\"%%%d#", "\"%42#", 1, 1, { intarg0 }, { to_value_t(42) } },
|
||||
{ " %d", "42", 1, 1, { intarg0 }, { to_value_t(42) } },
|
||||
@@ -163,6 +165,7 @@ TestSuite const test_suites[] {
|
||||
{ "%f", "42", 1, 1, { floatarg0 }, { to_value_t(42.0f) } },
|
||||
{ "%lf", "42", 1, 1, { doublearg0 }, { to_value_t(42.0) } },
|
||||
{ "%s", "42", 1, 1, { charstararg0 }, { str_to_value_t("42") } },
|
||||
{ "%s", " abc def", 1, 1, { charstararg0 }, { str_to_value_t("abc") } },
|
||||
{ "%d%s", "42yoinks", 2, 2, { intarg0, charstararg0 }, { to_value_t(42), str_to_value_t("yoinks") } },
|
||||
{ "%[^\n]", "aaaa\n", 1, 1, { charstararg0 }, { str_to_value_t("aaaa") } },
|
||||
{ "%u.%u.%u", "3.19", 2, 3, { unsignedarg0, unsignedarg1, unsignedarg2 }, { to_value_t(3u), to_value_t(19u) } },
|
||||
@@ -180,6 +183,21 @@ TestSuite const test_suites[] {
|
||||
{ "%*d", " 42", 0, 0, {}, {} },
|
||||
{ "%d%*1[:/]%d", "24/7", 2, 2, { intarg0, intarg1 }, { to_value_t(24), to_value_t(7) } },
|
||||
{ " %[^a]", " b", 1, 1, { charstararg0 }, { str_to_value_t("b") } },
|
||||
// Width specifiers
|
||||
{ "%1s", "ABC", 1, 1, { charstararg0 }, { str_to_value_t("A") } },
|
||||
{ "%2s", " ABCD", 1, 1, { charstararg0 }, { str_to_value_t("AB") } },
|
||||
{ "%3s%s", "ABCDE", 2, 2, { charstararg0, charstararg1 }, { str_to_value_t("ABC"), str_to_value_t("DE") } },
|
||||
{ "%4[^\n]", "ABCDE", 1, 1, { charstararg0 }, { str_to_value_t("ABCD") } },
|
||||
{ "%4[^\n]", " A BCDE\n", 1, 1, { charstararg0 }, { str_to_value_t(" A ") } },
|
||||
{ "%1d", "42", 1, 1, { intarg0 }, { to_value_t(4) } },
|
||||
{ "%1d", " 42", 1, 1, { intarg0 }, { to_value_t(4) } },
|
||||
{ "%1x%1x", "1a", 2, 2, { unsignedarg0, unsignedarg1 }, { to_value_t(0x1), to_value_t(0xa) } },
|
||||
{ "%2x%2x", "3a2b", 2, 2, { unsignedarg0, unsignedarg1 }, { to_value_t(0x3a), to_value_t(0x2b) } },
|
||||
{ "%1x", "0x123", 1, 1, { unsignedarg0 }, { to_value_t(0x0) } },
|
||||
{ "%1X", " ABC", 1, 1, { unsignedarg0 }, { to_value_t(0xA) } },
|
||||
// Pointer
|
||||
{ "%p", " 0xaabbccdd", 1, 1, { voidstararg0 }, { to_value_t(0xaabbccdd) } },
|
||||
{ "%8p", " 0xaabbccdd", 1, 1, { voidstararg0 }, { to_value_t(0xaabbcc) } },
|
||||
};
|
||||
|
||||
bool g_any_failed = false;
|
||||
@@ -251,3 +269,53 @@ TEST_CASE(scanf)
|
||||
for (auto& test : test_suites)
|
||||
do_one_test(test);
|
||||
}
|
||||
|
||||
TEST_CASE(scanf_char)
|
||||
{
|
||||
// %c should not put a null terminator
|
||||
char buf[5] = "xxxx";
|
||||
sscanf("abc", "%c", buf);
|
||||
EXPECT_EQ(strcmp(buf, "axxx"), 0);
|
||||
sscanf(" a bc", "%3c", buf);
|
||||
EXPECT_EQ(strcmp(buf, " a x"), 0);
|
||||
}
|
||||
|
||||
struct TestSuiteFloat {
|
||||
char const* format;
|
||||
char const* input;
|
||||
double expected;
|
||||
};
|
||||
|
||||
TestSuiteFloat const test_suites_float[] {
|
||||
{ "%f", "42.3456", 42.3456 },
|
||||
{ "%lf", "42.3456", 42.3456 },
|
||||
{ "%Lf", "42.3456", 42.3456 },
|
||||
// Width specifiers
|
||||
{ "%3f", "4.234", 4.2 },
|
||||
{ "%3f", " 4.234", 4.2 },
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
static void do_one_test_float(TestSuiteFloat const& test)
|
||||
{
|
||||
printf("Testing '%s' against '%s'...\n", test.input, test.format);
|
||||
T actual;
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
|
||||
sscanf(test.input, test.format, &actual);
|
||||
#pragma GCC diagnostic pop
|
||||
EXPECT_APPROXIMATE(test.expected, actual);
|
||||
}
|
||||
|
||||
TEST_CASE(scanf_float)
|
||||
{
|
||||
for (auto& test : test_suites_float) {
|
||||
char const* fmt = test.format;
|
||||
if (strchr(fmt, 'L'))
|
||||
do_one_test_float<long double>(test);
|
||||
else if (strchr(fmt, 'l'))
|
||||
do_one_test_float<double>(test);
|
||||
else
|
||||
do_one_test_float<float>(test);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
#include <AK/Assertions.h>
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/Format.h>
|
||||
#include <AK/GenericLexer.h>
|
||||
#include <ctype.h>
|
||||
@@ -57,13 +58,31 @@ struct ReadElementConcrete {
|
||||
}
|
||||
};
|
||||
|
||||
static char const* get_remaining_substr(GenericLexer& lexer, size_t max_width, ByteString& buf)
|
||||
{
|
||||
auto remaining = lexer.remaining();
|
||||
char const* nptr = remaining.characters_without_null_termination();
|
||||
if (max_width < remaining.length()) {
|
||||
// Create a null terminated substring for strto*
|
||||
buf = ByteString(nptr, max_width);
|
||||
if (buf.is_empty())
|
||||
return nullptr;
|
||||
nptr = buf.characters();
|
||||
}
|
||||
return nptr;
|
||||
}
|
||||
|
||||
template<typename ApT, ReadKind kind>
|
||||
struct ReadElementConcrete<int, ApT, kind> {
|
||||
bool operator()(GenericLexer& lexer, va_list* ap, bool suppress_assignment)
|
||||
bool operator()(GenericLexer& lexer, va_list* ap, size_t width_specifier, bool suppress_assignment)
|
||||
{
|
||||
long value = 0;
|
||||
char* endptr = nullptr;
|
||||
auto nptr = lexer.remaining().characters_without_null_termination();
|
||||
ByteString buf;
|
||||
auto nptr = get_remaining_substr(lexer, width_specifier, buf);
|
||||
if (!nptr)
|
||||
return false;
|
||||
|
||||
if constexpr (kind == ReadKind::Normal)
|
||||
value = strtol(nptr, &endptr, 10);
|
||||
if constexpr (kind == ReadKind::Octal)
|
||||
@@ -73,10 +92,7 @@ struct ReadElementConcrete<int, ApT, kind> {
|
||||
if constexpr (kind == ReadKind::Infer)
|
||||
value = strtol(nptr, &endptr, 0);
|
||||
|
||||
if (!endptr)
|
||||
return false;
|
||||
|
||||
if (endptr == nptr)
|
||||
if (!endptr || endptr == nptr)
|
||||
return false;
|
||||
|
||||
auto diff = endptr - nptr;
|
||||
@@ -91,31 +107,17 @@ struct ReadElementConcrete<int, ApT, kind> {
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ApT, ReadKind kind>
|
||||
struct ReadElementConcrete<char, ApT, kind> {
|
||||
bool operator()(GenericLexer& lexer, va_list* ap, bool suppress_assignment)
|
||||
{
|
||||
static_assert(kind == ReadKind::Normal, "Can't read a non-normal character");
|
||||
|
||||
if (lexer.is_eof())
|
||||
return false;
|
||||
|
||||
auto ch = lexer.consume();
|
||||
if (!suppress_assignment) {
|
||||
auto* ptr = va_arg(*ap, ApT*);
|
||||
*ptr = ch;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ApT, ReadKind kind>
|
||||
struct ReadElementConcrete<unsigned, ApT, kind> {
|
||||
bool operator()(GenericLexer& lexer, va_list* ap, bool suppress_assignment)
|
||||
bool operator()(GenericLexer& lexer, va_list* ap, size_t width_specifier, bool suppress_assignment)
|
||||
{
|
||||
unsigned long value = 0;
|
||||
char* endptr = nullptr;
|
||||
auto nptr = lexer.remaining().characters_without_null_termination();
|
||||
ByteString buf;
|
||||
auto nptr = get_remaining_substr(lexer, width_specifier, buf);
|
||||
if (!nptr)
|
||||
return false;
|
||||
|
||||
if constexpr (kind == ReadKind::Normal)
|
||||
value = strtoul(nptr, &endptr, 10);
|
||||
if constexpr (kind == ReadKind::Octal)
|
||||
@@ -125,10 +127,7 @@ struct ReadElementConcrete<unsigned, ApT, kind> {
|
||||
if constexpr (kind == ReadKind::Infer)
|
||||
value = strtoul(nptr, &endptr, 0);
|
||||
|
||||
if (!endptr)
|
||||
return false;
|
||||
|
||||
if (endptr == nptr)
|
||||
if (!endptr || endptr == nptr)
|
||||
return false;
|
||||
|
||||
auto diff = endptr - nptr;
|
||||
@@ -145,11 +144,15 @@ struct ReadElementConcrete<unsigned, ApT, kind> {
|
||||
|
||||
template<typename ApT, ReadKind kind>
|
||||
struct ReadElementConcrete<long long, ApT, kind> {
|
||||
bool operator()(GenericLexer& lexer, va_list* ap, bool suppress_assignment)
|
||||
bool operator()(GenericLexer& lexer, va_list* ap, size_t width_specifier, bool suppress_assignment)
|
||||
{
|
||||
long long value = 0;
|
||||
char* endptr = nullptr;
|
||||
auto nptr = lexer.remaining().characters_without_null_termination();
|
||||
ByteString buf;
|
||||
auto nptr = get_remaining_substr(lexer, width_specifier, buf);
|
||||
if (!nptr)
|
||||
return false;
|
||||
|
||||
if constexpr (kind == ReadKind::Normal)
|
||||
value = strtoll(nptr, &endptr, 10);
|
||||
if constexpr (kind == ReadKind::Octal)
|
||||
@@ -159,10 +162,7 @@ struct ReadElementConcrete<long long, ApT, kind> {
|
||||
if constexpr (kind == ReadKind::Infer)
|
||||
value = strtoll(nptr, &endptr, 0);
|
||||
|
||||
if (!endptr)
|
||||
return false;
|
||||
|
||||
if (endptr == nptr)
|
||||
if (!endptr || endptr == nptr)
|
||||
return false;
|
||||
|
||||
auto diff = endptr - nptr;
|
||||
@@ -179,11 +179,15 @@ struct ReadElementConcrete<long long, ApT, kind> {
|
||||
|
||||
template<typename ApT, ReadKind kind>
|
||||
struct ReadElementConcrete<unsigned long long, ApT, kind> {
|
||||
bool operator()(GenericLexer& lexer, va_list* ap, bool suppress_assignment)
|
||||
bool operator()(GenericLexer& lexer, va_list* ap, size_t width_specifier, bool suppress_assignment)
|
||||
{
|
||||
unsigned long long value = 0;
|
||||
char* endptr = nullptr;
|
||||
auto nptr = lexer.remaining().characters_without_null_termination();
|
||||
ByteString buf;
|
||||
auto nptr = get_remaining_substr(lexer, width_specifier, buf);
|
||||
if (!nptr)
|
||||
return false;
|
||||
|
||||
if constexpr (kind == ReadKind::Normal)
|
||||
value = strtoull(nptr, &endptr, 10);
|
||||
if constexpr (kind == ReadKind::Octal)
|
||||
@@ -193,10 +197,7 @@ struct ReadElementConcrete<unsigned long long, ApT, kind> {
|
||||
if constexpr (kind == ReadKind::Infer)
|
||||
value = strtoull(nptr, &endptr, 0);
|
||||
|
||||
if (!endptr)
|
||||
return false;
|
||||
|
||||
if (endptr == nptr)
|
||||
if (!endptr || endptr == nptr)
|
||||
return false;
|
||||
|
||||
auto diff = endptr - nptr;
|
||||
@@ -213,20 +214,22 @@ struct ReadElementConcrete<unsigned long long, ApT, kind> {
|
||||
|
||||
template<typename ApT, ReadKind kind>
|
||||
struct ReadElementConcrete<float, ApT, kind> {
|
||||
bool operator()(GenericLexer& lexer, va_list* ap, bool suppress_assignment)
|
||||
bool operator()(GenericLexer& lexer, va_list* ap, size_t width_specifier, bool suppress_assignment)
|
||||
{
|
||||
double value = 0;
|
||||
char* endptr = nullptr;
|
||||
auto nptr = lexer.remaining().characters_without_null_termination();
|
||||
ByteString buf;
|
||||
auto nptr = get_remaining_substr(lexer, width_specifier, buf);
|
||||
if (!nptr)
|
||||
return false;
|
||||
|
||||
// FIXME: Use strtold for %Lf
|
||||
if constexpr (kind == ReadKind::Normal)
|
||||
value = strtod(nptr, &endptr);
|
||||
else
|
||||
return false;
|
||||
|
||||
if (!endptr)
|
||||
return false;
|
||||
|
||||
if (endptr == nptr)
|
||||
if (!endptr || endptr == nptr)
|
||||
return false;
|
||||
|
||||
auto diff = endptr - nptr;
|
||||
@@ -243,127 +246,126 @@ struct ReadElementConcrete<float, ApT, kind> {
|
||||
|
||||
template<typename T, ReadKind kind>
|
||||
struct ReadElement {
|
||||
bool operator()(LengthModifier length_modifier, GenericLexer& input_lexer, va_list* ap, bool suppress_assignment)
|
||||
bool operator()(LengthModifier length_modifier, GenericLexer& input_lexer, va_list* ap, size_t width_specifier, bool suppress_assignment)
|
||||
{
|
||||
input_lexer.ignore_while([](char c) { return isspace(c); });
|
||||
|
||||
switch (length_modifier) {
|
||||
default:
|
||||
case LengthModifier::None:
|
||||
VERIFY_NOT_REACHED();
|
||||
case LengthModifier::Default:
|
||||
return ReadElementConcrete<T, T, kind> {}(input_lexer, ap, suppress_assignment);
|
||||
return ReadElementConcrete<T, T, kind> {}(input_lexer, ap, width_specifier, suppress_assignment);
|
||||
case LengthModifier::Char:
|
||||
return ReadElementConcrete<T, char, kind> {}(input_lexer, ap, suppress_assignment);
|
||||
return ReadElementConcrete<T, char, kind> {}(input_lexer, ap, width_specifier, suppress_assignment);
|
||||
case LengthModifier::Short:
|
||||
return ReadElementConcrete<T, short, kind> {}(input_lexer, ap, suppress_assignment);
|
||||
return ReadElementConcrete<T, short, kind> {}(input_lexer, ap, width_specifier, suppress_assignment);
|
||||
case LengthModifier::Long:
|
||||
if constexpr (IsSame<T, int>)
|
||||
return ReadElementConcrete<T, long, kind> {}(input_lexer, ap, suppress_assignment);
|
||||
return ReadElementConcrete<T, long, kind> {}(input_lexer, ap, width_specifier, suppress_assignment);
|
||||
if constexpr (IsSame<T, unsigned>)
|
||||
return ReadElementConcrete<T, unsigned long, kind> {}(input_lexer, ap, suppress_assignment);
|
||||
return ReadElementConcrete<T, unsigned long, kind> {}(input_lexer, ap, width_specifier, suppress_assignment);
|
||||
if constexpr (IsSame<T, float>)
|
||||
return ReadElementConcrete<int, double, kind> {}(input_lexer, ap, suppress_assignment);
|
||||
return ReadElementConcrete<float, double, kind> {}(input_lexer, ap, width_specifier, suppress_assignment);
|
||||
return false;
|
||||
case LengthModifier::LongLong:
|
||||
if constexpr (IsSame<T, int>)
|
||||
return ReadElementConcrete<long long, long long, kind> {}(input_lexer, ap, suppress_assignment);
|
||||
return ReadElementConcrete<long long, long long, kind> {}(input_lexer, ap, width_specifier, suppress_assignment);
|
||||
if constexpr (IsSame<T, unsigned>)
|
||||
return ReadElementConcrete<unsigned long long, unsigned long long, kind> {}(input_lexer, ap, suppress_assignment);
|
||||
if constexpr (IsSame<T, float>)
|
||||
return ReadElementConcrete<long long, double, kind> {}(input_lexer, ap, suppress_assignment);
|
||||
return ReadElementConcrete<unsigned long long, unsigned long long, kind> {}(input_lexer, ap, width_specifier, suppress_assignment);
|
||||
return false;
|
||||
case LengthModifier::IntMax:
|
||||
return ReadElementConcrete<T, intmax_t, kind> {}(input_lexer, ap, suppress_assignment);
|
||||
return ReadElementConcrete<T, intmax_t, kind> {}(input_lexer, ap, width_specifier, suppress_assignment);
|
||||
case LengthModifier::Size:
|
||||
return ReadElementConcrete<T, size_t, kind> {}(input_lexer, ap, suppress_assignment);
|
||||
return ReadElementConcrete<T, size_t, kind> {}(input_lexer, ap, width_specifier, suppress_assignment);
|
||||
case LengthModifier::PtrDiff:
|
||||
return ReadElementConcrete<T, ptrdiff_t, kind> {}(input_lexer, ap, suppress_assignment);
|
||||
return ReadElementConcrete<T, ptrdiff_t, kind> {}(input_lexer, ap, width_specifier, suppress_assignment);
|
||||
case LengthModifier::LongDouble:
|
||||
return ReadElementConcrete<T, long double, kind> {}(input_lexer, ap, suppress_assignment);
|
||||
return ReadElementConcrete<T, long double, kind> {}(input_lexer, ap, width_specifier, suppress_assignment);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ReadElement<char*, ReadKind::Normal> {
|
||||
ReadElement(StringView scan_set = {}, bool invert = false)
|
||||
: scan_set(scan_set.is_null() ? " \t\n\f\r"sv : scan_set)
|
||||
, invert(scan_set.is_null() ? true : invert)
|
||||
ReadElement(size_t width_specifier, StringView scan_set = {}, bool invert = true)
|
||||
: m_count(0)
|
||||
, m_max_count(width_specifier)
|
||||
, m_scan_set(scan_set)
|
||||
, m_invert(invert)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator()(LengthModifier length_modifier, GenericLexer& input_lexer, va_list* ap, bool suppress_assignment)
|
||||
bool operator()(LengthModifier length_modifier, GenericLexer& input_lexer, va_list* ap, ConversionSpecifier conversion_specifier, bool suppress_assignment)
|
||||
{
|
||||
// FIXME: Implement wide strings and such.
|
||||
if (length_modifier != LengthModifier::Default)
|
||||
return false;
|
||||
|
||||
if (conversion_specifier == ConversionSpecifier::String)
|
||||
input_lexer.ignore_while([](char c) { return isspace(c); });
|
||||
|
||||
auto str = input_lexer.consume_while([this](auto c) { return this->matches(c); });
|
||||
if (str.is_empty())
|
||||
return false;
|
||||
|
||||
if (!suppress_assignment) {
|
||||
auto* ptr = va_arg(*ap, char*);
|
||||
memcpy(ptr, str.characters_without_null_termination(), str.length());
|
||||
ptr[str.length()] = 0;
|
||||
size_t length = str.length();
|
||||
memcpy(ptr, str.characters_without_null_termination(), length);
|
||||
if (conversion_specifier != ConversionSpecifier::Character)
|
||||
ptr[length] = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool matches(char c) const
|
||||
bool matches(char c)
|
||||
{
|
||||
return invert ^ scan_set.contains(c);
|
||||
if (m_max_count <= m_count)
|
||||
return false;
|
||||
++m_count;
|
||||
return m_invert ^ m_scan_set.contains(c);
|
||||
}
|
||||
|
||||
StringView const scan_set;
|
||||
bool invert { false };
|
||||
size_t m_count;
|
||||
size_t m_max_count;
|
||||
StringView const m_scan_set;
|
||||
bool m_invert;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ReadElement<void*, ReadKind::Normal> {
|
||||
bool operator()(LengthModifier length_modifier, GenericLexer& input_lexer, va_list* ap, bool suppress_assignment)
|
||||
bool operator()(LengthModifier length_modifier, GenericLexer& input_lexer, va_list* ap, size_t width_specifier, bool suppress_assignment)
|
||||
{
|
||||
if (length_modifier != LengthModifier::Default)
|
||||
return false;
|
||||
|
||||
auto str = input_lexer.consume_while([this](auto c) { return this->should_consume(c); });
|
||||
input_lexer.ignore_while([](char c) { return isspace(c); });
|
||||
|
||||
if (count != 8) {
|
||||
fail:;
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
input_lexer.retreat();
|
||||
return false;
|
||||
}
|
||||
|
||||
char buf[9] { 0 };
|
||||
memcpy(buf, str.characters_without_null_termination(), 8);
|
||||
buf[8] = 0;
|
||||
unsigned long long value = 0;
|
||||
char* endptr = nullptr;
|
||||
auto value = strtoull(buf, &endptr, 16);
|
||||
ByteString buf;
|
||||
auto nptr = get_remaining_substr(input_lexer, width_specifier, buf);
|
||||
if (!nptr)
|
||||
return false;
|
||||
|
||||
if (endptr != &buf[8])
|
||||
goto fail;
|
||||
value = strtoull(nptr, &endptr, 16);
|
||||
|
||||
if (!endptr || endptr == nptr)
|
||||
return false;
|
||||
|
||||
auto diff = endptr - nptr;
|
||||
VERIFY(diff > 0);
|
||||
input_lexer.ignore((size_t)diff);
|
||||
|
||||
if (!suppress_assignment) {
|
||||
auto* ptr = va_arg(*ap, void**);
|
||||
memcpy(ptr, &value, sizeof(value));
|
||||
void** ptr = va_arg(*ap, void**);
|
||||
*ptr = reinterpret_cast<void*>(static_cast<uintptr_t>(value));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool should_consume(char c)
|
||||
{
|
||||
if (count == 8)
|
||||
return false;
|
||||
if (!isxdigit(c))
|
||||
return false;
|
||||
|
||||
++count;
|
||||
return true;
|
||||
}
|
||||
size_t count { 0 };
|
||||
};
|
||||
|
||||
extern "C" int vsscanf(char const* input, char const* format, va_list ap)
|
||||
@@ -407,11 +409,10 @@ extern "C" int vsscanf(char const* input, char const* format, va_list ap)
|
||||
}
|
||||
|
||||
// Parse width specification
|
||||
[[maybe_unused]] int width_specifier = 0;
|
||||
size_t width_specifier = -1;
|
||||
if (format_lexer.next_is(isdigit)) {
|
||||
auto width_digits = format_lexer.consume_while([](char c) { return isdigit(c); });
|
||||
width_specifier = width_digits.to_number<int>().value();
|
||||
// FIXME: Actually use width specifier
|
||||
width_specifier = width_digits.to_number<size_t>().value();
|
||||
}
|
||||
|
||||
bool invert_scanlist = false;
|
||||
@@ -544,61 +545,73 @@ extern "C" int vsscanf(char const* input, char const* format, va_list ap)
|
||||
dbgln("Invalid conversion specifier {} in scanf!", (int)conversion_specifier);
|
||||
VERIFY_NOT_REACHED();
|
||||
case ConversionSpecifier::Decimal:
|
||||
if (!ReadElement<int, ReadKind::Normal> {}(length_modifier, input_lexer, ©, suppress_assignment))
|
||||
if (!ReadElement<int, ReadKind::Normal> {}(
|
||||
length_modifier, input_lexer, ©, width_specifier, suppress_assignment))
|
||||
format_lexer.consume_all();
|
||||
else if (!suppress_assignment)
|
||||
++elements_matched;
|
||||
break;
|
||||
case ConversionSpecifier::Integer:
|
||||
if (!ReadElement<int, ReadKind::Infer> {}(length_modifier, input_lexer, ©, suppress_assignment))
|
||||
if (!ReadElement<int, ReadKind::Infer> {}(
|
||||
length_modifier, input_lexer, ©, width_specifier, suppress_assignment))
|
||||
format_lexer.consume_all();
|
||||
else if (!suppress_assignment)
|
||||
++elements_matched;
|
||||
break;
|
||||
case ConversionSpecifier::Octal:
|
||||
if (!ReadElement<unsigned, ReadKind::Octal> {}(length_modifier, input_lexer, ©, suppress_assignment))
|
||||
if (!ReadElement<unsigned, ReadKind::Octal> {}(
|
||||
length_modifier, input_lexer, ©, width_specifier, suppress_assignment))
|
||||
format_lexer.consume_all();
|
||||
else if (!suppress_assignment)
|
||||
++elements_matched;
|
||||
break;
|
||||
case ConversionSpecifier::Unsigned:
|
||||
if (!ReadElement<unsigned, ReadKind::Normal> {}(length_modifier, input_lexer, ©, suppress_assignment))
|
||||
if (!ReadElement<unsigned, ReadKind::Normal> {}(
|
||||
length_modifier, input_lexer, ©, width_specifier, suppress_assignment))
|
||||
format_lexer.consume_all();
|
||||
else if (!suppress_assignment)
|
||||
++elements_matched;
|
||||
break;
|
||||
case ConversionSpecifier::Hex:
|
||||
if (!ReadElement<unsigned, ReadKind::Hex> {}(length_modifier, input_lexer, ©, suppress_assignment))
|
||||
if (!ReadElement<unsigned, ReadKind::Hex> {}(
|
||||
length_modifier, input_lexer, ©, width_specifier, suppress_assignment))
|
||||
format_lexer.consume_all();
|
||||
else if (!suppress_assignment)
|
||||
++elements_matched;
|
||||
break;
|
||||
case ConversionSpecifier::Floating:
|
||||
if (!ReadElement<float, ReadKind::Normal> {}(length_modifier, input_lexer, ©, suppress_assignment))
|
||||
if (!ReadElement<float, ReadKind::Normal> {}(
|
||||
length_modifier, input_lexer, ©, width_specifier, suppress_assignment))
|
||||
format_lexer.consume_all();
|
||||
else if (!suppress_assignment)
|
||||
++elements_matched;
|
||||
break;
|
||||
case ConversionSpecifier::String:
|
||||
if (!ReadElement<char*, ReadKind::Normal> {}(length_modifier, input_lexer, ©, suppress_assignment))
|
||||
if (!ReadElement<char*, ReadKind::Normal> { width_specifier, " \t\n\f\r"sv }(
|
||||
length_modifier, input_lexer, ©, conversion_specifier, suppress_assignment))
|
||||
format_lexer.consume_all();
|
||||
else if (!suppress_assignment)
|
||||
++elements_matched;
|
||||
break;
|
||||
case ConversionSpecifier::UseScanList:
|
||||
if (!ReadElement<char*, ReadKind::Normal> { scanlist, invert_scanlist }(length_modifier, input_lexer, ©, suppress_assignment))
|
||||
if (!ReadElement<char*, ReadKind::Normal> { width_specifier, scanlist, invert_scanlist }(
|
||||
length_modifier, input_lexer, ©, conversion_specifier, suppress_assignment))
|
||||
format_lexer.consume_all();
|
||||
else if (!suppress_assignment)
|
||||
++elements_matched;
|
||||
break;
|
||||
case ConversionSpecifier::Character:
|
||||
if (!ReadElement<char, ReadKind::Normal> {}(length_modifier, input_lexer, ©, suppress_assignment))
|
||||
if (width_specifier == static_cast<size_t>(-1))
|
||||
width_specifier = 1;
|
||||
if (!ReadElement<char*, ReadKind::Normal> { width_specifier }(
|
||||
length_modifier, input_lexer, ©, conversion_specifier, suppress_assignment))
|
||||
format_lexer.consume_all();
|
||||
else if (!suppress_assignment)
|
||||
++elements_matched;
|
||||
break;
|
||||
case ConversionSpecifier::Pointer:
|
||||
if (!ReadElement<void*, ReadKind::Normal> {}(length_modifier, input_lexer, ©, suppress_assignment))
|
||||
if (!ReadElement<void*, ReadKind::Normal> {}(
|
||||
length_modifier, input_lexer, ©, width_specifier, suppress_assignment))
|
||||
format_lexer.consume_all();
|
||||
else if (!suppress_assignment)
|
||||
++elements_matched;
|
||||
|
||||
Reference in New Issue
Block a user