Bläddra i källkod

LibC: Use 'long long' specialisations of scanf's read_element_concrete

...for 'long long' and 'unsigned long long', instead of reading them as
'long's and 'unsigned long's.
Also add a test for values that can only fit in (unsigned) long long.
Fixes #6096.
AnotherTest 4 år sedan
förälder
incheckning
143c68755b
2 ändrade filer med 44 tillägg och 4 borttagningar
  1. 38 4
      Userland/Libraries/LibC/scanf.cpp
  2. 6 0
      Userland/Tests/LibC/scanf.cpp

+ 38 - 4
Userland/Libraries/LibC/scanf.cpp

@@ -163,6 +163,40 @@ struct read_element_concrete<unsigned, ApT, kind> {
     }
 };
 
+template<typename ApT, ReadKind kind>
+struct read_element_concrete<long long, ApT, kind> {
+    bool operator()(GenericLexer& lexer, va_list* ap)
+    {
+        lexer.ignore_while(isspace);
+
+        auto* ptr = va_arg(*ap, ApT*);
+        long long value = 0;
+        char* endptr = nullptr;
+        auto nptr = lexer.remaining().characters_without_null_termination();
+        if constexpr (kind == ReadKind::Normal)
+            value = strtoll(nptr, &endptr, 10);
+        if constexpr (kind == ReadKind::Octal)
+            value = strtoll(nptr, &endptr, 8);
+        if constexpr (kind == ReadKind::Hex)
+            value = strtoll(nptr, &endptr, 16);
+        if constexpr (kind == ReadKind::Infer)
+            value = strtoll(nptr, &endptr, 0);
+
+        if (!endptr)
+            return false;
+
+        if (endptr == nptr)
+            return false;
+
+        auto diff = endptr - nptr;
+        VERIFY(diff > 0);
+        lexer.ignore((size_t)diff);
+
+        *ptr = value;
+        return true;
+    }
+};
+
 template<typename ApT, ReadKind kind>
 struct read_element_concrete<unsigned long long, ApT, kind> {
     bool operator()(GenericLexer& lexer, va_list* ap)
@@ -248,15 +282,15 @@ struct read_element {
             if constexpr (IsSame<T, unsigned>::value)
                 return read_element_concrete<T, unsigned, kind> {}(input_lexer, ap);
             if constexpr (IsSame<T, float>::value)
-                return read_element_concrete<T, double, kind> {}(input_lexer, ap);
+                return read_element_concrete<int, double, kind> {}(input_lexer, ap);
             return false;
         case LongLong:
             if constexpr (IsSame<T, int>::value)
-                return read_element_concrete<T, long long, kind> {}(input_lexer, ap);
+                return read_element_concrete<long long, long long, kind> {}(input_lexer, ap);
             if constexpr (IsSame<T, unsigned>::value)
-                return read_element_concrete<T, unsigned long long, kind> {}(input_lexer, ap);
+                return read_element_concrete<unsigned long long, unsigned long long, kind> {}(input_lexer, ap);
             if constexpr (IsSame<T, float>::value)
-                return read_element_concrete<T, double, kind> {}(input_lexer, ap);
+                return read_element_concrete<long long, double, kind> {}(input_lexer, ap);
             return false;
         case IntMax:
             return read_element_concrete<T, intmax_t, kind> {}(input_lexer, ap);

+ 6 - 0
Userland/Tests/LibC/scanf.cpp

@@ -31,6 +31,7 @@
 
 typedef long double longdouble;
 typedef long long longlong;
+typedef unsigned long long unsignedlonglong;
 typedef unsigned long unsignedlong;
 typedef char charstar[32];
 
@@ -145,6 +146,7 @@ DECL_WITH_TYPE(float);
 DECL_WITH_TYPE(double);
 DECL_WITH_TYPE(longdouble);
 DECL_WITH_TYPE(unsignedlong);
+DECL_WITH_TYPE(unsignedlonglong);
 
 #undef DECL_WITH_TYPE
 
@@ -184,6 +186,10 @@ const TestSuite test_suites[] {
     // GCC failure tests
     { "%d.%d.%d", "10.2.0", 3, 3, { intarg0, intarg1, intarg2 }, { to_value_t(10), to_value_t(2), to_value_t(0) } },
     { "%lu", "3054       ", 1, 1, { unsignedlongarg0 }, { to_value_t(3054ul) } },
+    // "actual" long long and unsigned long long, from #6096
+    // Note: '9223372036854775806' is the max value for 'long long'.
+    { "%lld", "9223372036854775805", 1, 1, { longlongarg0 }, { to_value_t(9223372036854775805LL) } },
+    { "%llu", "9223372036854775810", 1, 1, { unsignedlonglongarg0 }, { to_value_t(9223372036854775810ULL) } },
 };
 
 bool g_any_failed = false;