Compare commits
5 Commits
54a92293da
...
e544e6a62d
Author | SHA1 | Date |
---|---|---|
Bananymous | e544e6a62d | |
Bananymous | 606a7cb313 | |
Bananymous | de4fdcd898 | |
Bananymous | 2c471a89d0 | |
Bananymous | a5660b95b2 |
|
@ -254,7 +254,8 @@ namespace BAN
|
||||||
if (new_data == nullptr)
|
if (new_data == nullptr)
|
||||||
return Error::from_errno(ENOMEM);
|
return Error::from_errno(ENOMEM);
|
||||||
|
|
||||||
memcpy(new_data, data(), m_size + 1);
|
if (m_size)
|
||||||
|
memcpy(new_data, data(), m_size + 1);
|
||||||
|
|
||||||
if (has_sso())
|
if (has_sso())
|
||||||
{
|
{
|
||||||
|
|
|
@ -84,6 +84,12 @@ namespace BAN
|
||||||
template<typename T> struct is_arithmetic { static constexpr bool value = is_integral_v<T> || is_floating_point_v<T>; };
|
template<typename T> struct is_arithmetic { static constexpr bool value = is_integral_v<T> || is_floating_point_v<T>; };
|
||||||
template<typename T> inline constexpr bool is_arithmetic_v = is_arithmetic<T>::value;
|
template<typename T> inline constexpr bool is_arithmetic_v = is_arithmetic<T>::value;
|
||||||
|
|
||||||
|
template<typename Base, typename Derived> struct is_base_of { static constexpr bool value = __is_base_of(Base, Derived); };
|
||||||
|
template<typename Base, typename Derived> inline constexpr bool is_base_of_v = is_base_of<Base, Derived>::value;
|
||||||
|
|
||||||
|
template<typename T, T V> struct integral_constant { static constexpr T value = V; };
|
||||||
|
template<typename T, T V > inline constexpr T integral_constant_v = integral_constant<T, V>::value;
|
||||||
|
|
||||||
namespace detail
|
namespace detail
|
||||||
{
|
{
|
||||||
template<typename T, bool = is_arithmetic_v<T>> struct is_signed { static constexpr bool value = T(-1) < T(0); };
|
template<typename T, bool = is_arithmetic_v<T>> struct is_signed { static constexpr bool value = T(-1) < T(0); };
|
||||||
|
|
|
@ -11,6 +11,7 @@ set(LIBC_SOURCES
|
||||||
malloc.cpp
|
malloc.cpp
|
||||||
printf_impl.cpp
|
printf_impl.cpp
|
||||||
pwd.cpp
|
pwd.cpp
|
||||||
|
scanf_impl.cpp
|
||||||
signal.cpp
|
signal.cpp
|
||||||
stdio.cpp
|
stdio.cpp
|
||||||
stdlib.cpp
|
stdlib.cpp
|
||||||
|
@ -48,7 +49,7 @@ add_custom_target(crtx-install
|
||||||
add_library(libc ${LIBC_SOURCES})
|
add_library(libc ${LIBC_SOURCES})
|
||||||
add_dependencies(libc headers crtx-install)
|
add_dependencies(libc headers crtx-install)
|
||||||
|
|
||||||
target_compile_options(libc PRIVATE -O2 -g -Wstack-usage=512)
|
target_compile_options(libc PRIVATE -O2 -g -Wstack-usage=512 -fno-tree-loop-distribute-patterns)
|
||||||
target_compile_options(libc PUBLIC -Wall -Wextra -Werror -Wno-error=stack-usage=)
|
target_compile_options(libc PUBLIC -Wall -Wextra -Werror -Wno-error=stack-usage=)
|
||||||
|
|
||||||
add_custom_target(libc-install
|
add_custom_target(libc-install
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
int scanf_impl(const char* format, va_list arguments, int (*getc_fun)(void*), void* data);
|
|
@ -0,0 +1,652 @@
|
||||||
|
#include <BAN/Assert.h>
|
||||||
|
#include <BAN/Math.h>
|
||||||
|
#include <BAN/Traits.h>
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <scanf_impl.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
enum class LengthModifier
|
||||||
|
{
|
||||||
|
none,
|
||||||
|
hh,
|
||||||
|
h,
|
||||||
|
l,
|
||||||
|
ll,
|
||||||
|
j,
|
||||||
|
z,
|
||||||
|
t,
|
||||||
|
L,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Conversion
|
||||||
|
{
|
||||||
|
bool suppress = false;
|
||||||
|
int field_width = -1;
|
||||||
|
bool allocate = false;
|
||||||
|
LengthModifier length = LengthModifier::none;
|
||||||
|
char conversion = '\0';
|
||||||
|
};
|
||||||
|
|
||||||
|
Conversion parse_conversion_specifier(const char*& format)
|
||||||
|
{
|
||||||
|
Conversion conversion;
|
||||||
|
|
||||||
|
if (*format == '*')
|
||||||
|
{
|
||||||
|
conversion.suppress = true;
|
||||||
|
format++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isdigit(*format))
|
||||||
|
{
|
||||||
|
conversion.field_width = 0;
|
||||||
|
while (isdigit(*format))
|
||||||
|
{
|
||||||
|
conversion.field_width = (conversion.field_width * 10) + (*format - '0');
|
||||||
|
format++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*format == 'm')
|
||||||
|
{
|
||||||
|
conversion.allocate = true;
|
||||||
|
format++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*format == 'h')
|
||||||
|
{
|
||||||
|
conversion.length = LengthModifier::h;
|
||||||
|
format++;
|
||||||
|
if (*format == 'h')
|
||||||
|
{
|
||||||
|
conversion.length = LengthModifier::hh;
|
||||||
|
format++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (*format == 'l')
|
||||||
|
{
|
||||||
|
conversion.length = LengthModifier::l;
|
||||||
|
format++;
|
||||||
|
if (*format == 'l')
|
||||||
|
{
|
||||||
|
conversion.length = LengthModifier::ll;
|
||||||
|
format++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (*format == 'j')
|
||||||
|
{
|
||||||
|
conversion.length = LengthModifier::j;
|
||||||
|
format++;
|
||||||
|
}
|
||||||
|
else if (*format == 'z')
|
||||||
|
{
|
||||||
|
conversion.length = LengthModifier::z;
|
||||||
|
format++;
|
||||||
|
}
|
||||||
|
else if (*format == 't')
|
||||||
|
{
|
||||||
|
conversion.length = LengthModifier::t;
|
||||||
|
format++;
|
||||||
|
}
|
||||||
|
else if (*format == 'L')
|
||||||
|
{
|
||||||
|
conversion.length = LengthModifier::L;
|
||||||
|
format++;
|
||||||
|
}
|
||||||
|
|
||||||
|
conversion.conversion = *format;
|
||||||
|
format++;
|
||||||
|
|
||||||
|
return conversion;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<int BASE>
|
||||||
|
using BASE_TYPE = BAN::integral_constant<int, BASE>;
|
||||||
|
|
||||||
|
template<bool UNSIGNED>
|
||||||
|
using IS_UNSIGNED = BAN::integral_constant<bool, UNSIGNED>;
|
||||||
|
|
||||||
|
int scanf_impl(const char* format, va_list arguments, int (*__getc_fun)(void*), void* data)
|
||||||
|
{
|
||||||
|
static constexpr int DONE = -1;
|
||||||
|
static constexpr int NONE = -2;
|
||||||
|
|
||||||
|
int nread = 0;
|
||||||
|
int conversions = 0;
|
||||||
|
int in = NONE;
|
||||||
|
|
||||||
|
enum class ConversionResult
|
||||||
|
{
|
||||||
|
NONE,
|
||||||
|
SUCCESS,
|
||||||
|
INPUT_FAILURE,
|
||||||
|
MATCH_FAILURE,
|
||||||
|
};
|
||||||
|
|
||||||
|
auto get_input =
|
||||||
|
[&](bool advance) -> void
|
||||||
|
{
|
||||||
|
if (in == DONE)
|
||||||
|
return;
|
||||||
|
if (advance || in == NONE)
|
||||||
|
{
|
||||||
|
in = __getc_fun(data);
|
||||||
|
nread++;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto parse_integer_internal =
|
||||||
|
[&get_input, &in]<int BASE, typename T>(BASE_TYPE<BASE>, bool negative, int width, T* out) -> ConversionResult
|
||||||
|
{
|
||||||
|
constexpr auto is_base_digit =
|
||||||
|
[](char c) -> bool
|
||||||
|
{
|
||||||
|
c = tolower(c);
|
||||||
|
if ('0' <= c && c <= '9')
|
||||||
|
return c - '0' < BASE;
|
||||||
|
if ('a' <= c && c <= 'z')
|
||||||
|
return c - 'a' + 10 < BASE;
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
constexpr auto get_base_digit = [](char c) -> T { if (c <= '9') return c - '0'; return tolower(c) - 'a' + 10; };
|
||||||
|
|
||||||
|
if (!is_base_digit(in))
|
||||||
|
return ConversionResult::MATCH_FAILURE;
|
||||||
|
|
||||||
|
*out = 0;
|
||||||
|
while (width-- && is_base_digit(in))
|
||||||
|
{
|
||||||
|
*out = (*out * BASE) + get_base_digit(in);
|
||||||
|
get_input(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (negative)
|
||||||
|
*out = -*out;
|
||||||
|
|
||||||
|
return ConversionResult::SUCCESS;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto parse_integer_typed =
|
||||||
|
[&parse_integer_internal, &arguments, &get_input, &in]<int BASE, typename T>(BASE_TYPE<BASE>, bool suppress, int width, T*) -> ConversionResult
|
||||||
|
{
|
||||||
|
T dummy;
|
||||||
|
T* out = suppress ? &dummy : va_arg(arguments, T*);
|
||||||
|
|
||||||
|
bool negative = (in == '-');
|
||||||
|
if (in == '-' || in == '+')
|
||||||
|
{
|
||||||
|
get_input(true);
|
||||||
|
if (--width == 0)
|
||||||
|
return ConversionResult::MATCH_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr(BASE == 0)
|
||||||
|
{
|
||||||
|
if (in != '0')
|
||||||
|
return parse_integer_internal(BASE_TYPE<10>{}, negative, width, out);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
get_input(true);
|
||||||
|
if (--width == 0)
|
||||||
|
{
|
||||||
|
*out = 0;
|
||||||
|
return ConversionResult::SUCCESS;
|
||||||
|
}
|
||||||
|
if ('0' <= in && in <= '7')
|
||||||
|
return parse_integer_internal(BASE_TYPE<8>{}, negative, width, out);
|
||||||
|
if (tolower(in) == 'x')
|
||||||
|
{
|
||||||
|
get_input(true);
|
||||||
|
if (--width == 0)
|
||||||
|
return ConversionResult::MATCH_FAILURE;
|
||||||
|
return parse_integer_internal(BASE_TYPE<16>{}, negative, width, out);
|
||||||
|
}
|
||||||
|
*out = 0;
|
||||||
|
return ConversionResult::SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr(BASE == 16)
|
||||||
|
{
|
||||||
|
if (in == '0')
|
||||||
|
{
|
||||||
|
get_input(true);
|
||||||
|
width--;
|
||||||
|
if (tolower(in) == 'x')
|
||||||
|
{
|
||||||
|
get_input(true);
|
||||||
|
width--;
|
||||||
|
}
|
||||||
|
if (width <= 0)
|
||||||
|
{
|
||||||
|
*out = 0;
|
||||||
|
return ConversionResult::SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return parse_integer_internal(BASE_TYPE<BASE>{}, negative, width, out);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto parse_integer =
|
||||||
|
[&parse_integer_typed, &get_input, &in]<int BASE, bool UNSIGNED>(BASE_TYPE<BASE>, IS_UNSIGNED<UNSIGNED>, bool suppress, int width, LengthModifier length) -> ConversionResult
|
||||||
|
{
|
||||||
|
get_input(false);
|
||||||
|
while (isspace(in))
|
||||||
|
get_input(true);
|
||||||
|
if (in == DONE)
|
||||||
|
return ConversionResult::INPUT_FAILURE;
|
||||||
|
|
||||||
|
if (width == -1)
|
||||||
|
width = __INT_MAX__;
|
||||||
|
|
||||||
|
switch (length)
|
||||||
|
{
|
||||||
|
case LengthModifier::none: return parse_integer_typed(BASE_TYPE<BASE>{}, suppress, width, BAN::either_or_t<UNSIGNED, unsigned int*, int*> {});
|
||||||
|
case LengthModifier::hh: return parse_integer_typed(BASE_TYPE<BASE>{}, suppress, width, BAN::either_or_t<UNSIGNED, unsigned char*, signed char*> {});
|
||||||
|
case LengthModifier::h: return parse_integer_typed(BASE_TYPE<BASE>{}, suppress, width, BAN::either_or_t<UNSIGNED, unsigned short*, short*> {});
|
||||||
|
case LengthModifier::l: return parse_integer_typed(BASE_TYPE<BASE>{}, suppress, width, BAN::either_or_t<UNSIGNED, unsigned long*, long*> {});
|
||||||
|
case LengthModifier::ll: return parse_integer_typed(BASE_TYPE<BASE>{}, suppress, width, BAN::either_or_t<UNSIGNED, unsigned long long*, long long*> {});
|
||||||
|
case LengthModifier::j: return parse_integer_typed(BASE_TYPE<BASE>{}, suppress, width, BAN::either_or_t<UNSIGNED, uintmax_t*, intmax_t*> {});
|
||||||
|
case LengthModifier::z: return parse_integer_typed(BASE_TYPE<BASE>{}, suppress, width, BAN::either_or_t<UNSIGNED, size_t*, ssize_t*> {});
|
||||||
|
case LengthModifier::t: return parse_integer_typed(BASE_TYPE<BASE>{}, suppress, width, BAN::either_or_t<UNSIGNED, size_t*, ptrdiff_t*> {});
|
||||||
|
static_assert(sizeof(size_t) == sizeof(ptrdiff_t));
|
||||||
|
default:
|
||||||
|
return ConversionResult::MATCH_FAILURE;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto parse_floating_point_internal =
|
||||||
|
[&parse_integer_internal, &get_input, &in]<int BASE, typename T>(BASE_TYPE<BASE>, bool negative, int width, T* out, bool require_start = true) -> ConversionResult
|
||||||
|
{
|
||||||
|
constexpr auto is_base_digit =
|
||||||
|
[](char c) -> bool
|
||||||
|
{
|
||||||
|
c = tolower(c);
|
||||||
|
if ('0' <= c && c <= '9')
|
||||||
|
return c - '0' < BASE;
|
||||||
|
if ('a' <= c && c <= 'z')
|
||||||
|
return c - 'a' + 10 < BASE;
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
constexpr auto get_base_digit = [](char c) -> T { if (c <= '9') return T(c - '0'); return T(tolower(c) - 'a' + 10); };
|
||||||
|
|
||||||
|
if (require_start && !is_base_digit(in))
|
||||||
|
return ConversionResult::MATCH_FAILURE;
|
||||||
|
|
||||||
|
*out = T(0);
|
||||||
|
|
||||||
|
// Parse whole part
|
||||||
|
while (width > 0 && is_base_digit(in))
|
||||||
|
{
|
||||||
|
*out = (*out * BASE) + get_base_digit(in);
|
||||||
|
get_input(true);
|
||||||
|
width--;
|
||||||
|
}
|
||||||
|
if (width == 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
// Parse fractional part
|
||||||
|
if (in == '.')
|
||||||
|
{
|
||||||
|
get_input(true);
|
||||||
|
width--;
|
||||||
|
T multiplier = T(1) / T(BASE);
|
||||||
|
while (width > 0 && is_base_digit(in))
|
||||||
|
{
|
||||||
|
*out += get_base_digit(in) * multiplier;
|
||||||
|
multiplier /= T(BASE);
|
||||||
|
get_input(true);
|
||||||
|
width--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (width == 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
// Parse exponent
|
||||||
|
static_assert(BASE == 10 || BASE == 16);
|
||||||
|
if ((BASE == 10 && tolower(in) == 'e') || (BASE == 16 && tolower(in) == 'p'))
|
||||||
|
{
|
||||||
|
get_input(true);
|
||||||
|
width--;
|
||||||
|
bool exp_negative = (in == '-');
|
||||||
|
if (in == '+' || in == '-')
|
||||||
|
{
|
||||||
|
get_input(true);
|
||||||
|
if (--width == 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
int exp;
|
||||||
|
if (parse_integer_internal(BASE_TYPE<10>{}, exp_negative, width, &exp) == ConversionResult::SUCCESS)
|
||||||
|
*out *= BAN::Math::pow<T>(BASE == 10 ? T(10) : T(2), T(exp));
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
if (negative)
|
||||||
|
*out = -*out;
|
||||||
|
|
||||||
|
return ConversionResult::SUCCESS;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto parse_floating_point_typed =
|
||||||
|
[&parse_floating_point_internal, &arguments, &get_input, &in]<typename T>(bool suppress, int width, T*) -> ConversionResult
|
||||||
|
{
|
||||||
|
T dummy;
|
||||||
|
T* out = suppress ? &dummy : va_arg(arguments, T*);
|
||||||
|
|
||||||
|
get_input(false);
|
||||||
|
while (isspace(in))
|
||||||
|
get_input(true);
|
||||||
|
if (in == DONE)
|
||||||
|
return ConversionResult::INPUT_FAILURE;
|
||||||
|
|
||||||
|
bool negative = (in == '-');
|
||||||
|
if (in == '-' || in == '+')
|
||||||
|
{
|
||||||
|
get_input(true);
|
||||||
|
if (--width == 0)
|
||||||
|
return ConversionResult::MATCH_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tolower(in) == 'i')
|
||||||
|
{
|
||||||
|
get_input(true);
|
||||||
|
if (--width == 0)
|
||||||
|
return ConversionResult::MATCH_FAILURE;
|
||||||
|
if (tolower(in) != 'n')
|
||||||
|
return ConversionResult::MATCH_FAILURE;
|
||||||
|
get_input(true);
|
||||||
|
if (--width == 0)
|
||||||
|
return ConversionResult::MATCH_FAILURE;
|
||||||
|
if (tolower(in) != 'f')
|
||||||
|
return ConversionResult::MATCH_FAILURE;
|
||||||
|
if constexpr(sizeof(T) == sizeof(float))
|
||||||
|
*out = HUGE_VALF;
|
||||||
|
else if constexpr(sizeof(T) == sizeof(double))
|
||||||
|
*out = HUGE_VAL;
|
||||||
|
else if constexpr(sizeof(T) == sizeof(long double))
|
||||||
|
*out = HUGE_VALL;
|
||||||
|
else
|
||||||
|
[]<bool flag = false>() { static_assert(flag); };
|
||||||
|
|
||||||
|
if (negative)
|
||||||
|
*out = -*out;
|
||||||
|
|
||||||
|
return ConversionResult::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tolower(in) == 'n')
|
||||||
|
{
|
||||||
|
get_input(true);
|
||||||
|
if (--width == 0)
|
||||||
|
return ConversionResult::MATCH_FAILURE;
|
||||||
|
if (tolower(in) != 'a')
|
||||||
|
return ConversionResult::MATCH_FAILURE;
|
||||||
|
get_input(true);
|
||||||
|
if (--width == 0)
|
||||||
|
return ConversionResult::MATCH_FAILURE;
|
||||||
|
if (tolower(in) != 'n')
|
||||||
|
return ConversionResult::MATCH_FAILURE;
|
||||||
|
if constexpr(sizeof(T) == sizeof(float))
|
||||||
|
*out = nanf("");
|
||||||
|
else if constexpr(sizeof(T) == sizeof(double))
|
||||||
|
*out = nan("");
|
||||||
|
else if constexpr(sizeof(T) == sizeof(long double))
|
||||||
|
*out = nanl("");
|
||||||
|
else
|
||||||
|
[]<bool flag = false>() { static_assert(flag); };
|
||||||
|
|
||||||
|
return ConversionResult::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in == '0' && width > 2)
|
||||||
|
{
|
||||||
|
get_input(true);
|
||||||
|
width--;
|
||||||
|
if (tolower(in) == 'x')
|
||||||
|
{
|
||||||
|
get_input(true);
|
||||||
|
width--;
|
||||||
|
return parse_floating_point_internal(BASE_TYPE<16>{}, negative, width, out);
|
||||||
|
}
|
||||||
|
return parse_floating_point_internal(BASE_TYPE<10>{}, negative, width, out, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parse_floating_point_internal(BASE_TYPE<10>{}, negative, width, out);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto parse_floating_point =
|
||||||
|
[&parse_floating_point_typed](bool suppress, int width, LengthModifier length) -> ConversionResult
|
||||||
|
{
|
||||||
|
if (width == -1)
|
||||||
|
width = __INT_MAX__;
|
||||||
|
|
||||||
|
switch (length)
|
||||||
|
{
|
||||||
|
case LengthModifier::none: return parse_floating_point_typed(suppress, width, static_cast<float*> (nullptr));
|
||||||
|
case LengthModifier::l: return parse_floating_point_typed(suppress, width, static_cast<double*> (nullptr));
|
||||||
|
case LengthModifier::L: return parse_floating_point_typed(suppress, width, static_cast<long double*> (nullptr));
|
||||||
|
default:
|
||||||
|
return ConversionResult::MATCH_FAILURE;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto parse_string =
|
||||||
|
[&arguments, &get_input, &in](uint8_t* mask, bool exclude, bool suppress, bool allocate, int min_len, int max_len, bool terminate) -> ConversionResult
|
||||||
|
{
|
||||||
|
char* temp_dummy;
|
||||||
|
char** outp = nullptr;
|
||||||
|
if (suppress)
|
||||||
|
;
|
||||||
|
else if (allocate)
|
||||||
|
{
|
||||||
|
outp = va_arg(arguments, char**);
|
||||||
|
*outp = (char*)malloc(BUFSIZ);
|
||||||
|
if (*outp == nullptr)
|
||||||
|
return ConversionResult::MATCH_FAILURE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
temp_dummy = va_arg(arguments, char*);
|
||||||
|
outp = &temp_dummy;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t xor_mask = exclude ? 0xFF : 0x00;
|
||||||
|
|
||||||
|
get_input(false);
|
||||||
|
if (in == DONE)
|
||||||
|
{
|
||||||
|
if (allocate)
|
||||||
|
free(*outp);
|
||||||
|
*outp = nullptr;
|
||||||
|
return ConversionResult::INPUT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int len = 0;
|
||||||
|
while (len < max_len && in != DONE && ((mask[in / 8] ^ xor_mask) & (1 << (in % 8))))
|
||||||
|
{
|
||||||
|
len++;
|
||||||
|
if (!suppress)
|
||||||
|
{
|
||||||
|
(*outp)[len - 1] = in;
|
||||||
|
if (allocate && len % BUFSIZ == 0)
|
||||||
|
{
|
||||||
|
char* newp = (char*)realloc(*outp, len + BUFSIZ);
|
||||||
|
if (newp == nullptr)
|
||||||
|
{
|
||||||
|
free(*outp);
|
||||||
|
*outp = nullptr;
|
||||||
|
return ConversionResult::MATCH_FAILURE;
|
||||||
|
}
|
||||||
|
*outp = newp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
get_input(true);
|
||||||
|
}
|
||||||
|
if (len < min_len)
|
||||||
|
{
|
||||||
|
if (allocate)
|
||||||
|
free(*outp);
|
||||||
|
*outp = nullptr;
|
||||||
|
return ConversionResult::MATCH_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!suppress && terminate)
|
||||||
|
(*outp)[len] = '\0';
|
||||||
|
|
||||||
|
return ConversionResult::SUCCESS;
|
||||||
|
};
|
||||||
|
|
||||||
|
while (isspace(*format) || isprint(*format))
|
||||||
|
{
|
||||||
|
if (*format == '%')
|
||||||
|
{
|
||||||
|
format++;
|
||||||
|
auto conversion = parse_conversion_specifier(format);
|
||||||
|
|
||||||
|
ConversionResult result = ConversionResult::NONE;
|
||||||
|
switch (conversion.conversion)
|
||||||
|
{
|
||||||
|
case 'd': result = parse_integer(BASE_TYPE<10>{}, IS_UNSIGNED<false>{}, conversion.suppress, conversion.field_width, conversion.length); break;
|
||||||
|
case 'i': result = parse_integer(BASE_TYPE<0> {}, IS_UNSIGNED<false>{}, conversion.suppress, conversion.field_width, conversion.length); break;
|
||||||
|
case 'o': result = parse_integer(BASE_TYPE<8> {}, IS_UNSIGNED<true> {}, conversion.suppress, conversion.field_width, conversion.length); break;
|
||||||
|
case 'u': result = parse_integer(BASE_TYPE<10>{}, IS_UNSIGNED<true> {}, conversion.suppress, conversion.field_width, conversion.length); break;
|
||||||
|
case 'x': result = parse_integer(BASE_TYPE<16>{}, IS_UNSIGNED<true> {}, conversion.suppress, conversion.field_width, conversion.length); break;
|
||||||
|
case 'X': result = parse_integer(BASE_TYPE<16>{}, IS_UNSIGNED<true> {}, conversion.suppress, conversion.field_width, conversion.length); break;
|
||||||
|
case 'p': result = parse_integer(BASE_TYPE<16>{}, IS_UNSIGNED<true> {}, conversion.suppress, conversion.field_width, LengthModifier::j); break;
|
||||||
|
case 'a': case 'e': case 'f': case 'g':
|
||||||
|
result = parse_floating_point(conversion.suppress, conversion.field_width, conversion.length);
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
conversion.length = LengthModifier::l;
|
||||||
|
// fall through
|
||||||
|
case 's':
|
||||||
|
{
|
||||||
|
int width = conversion.field_width;
|
||||||
|
if (width == -1)
|
||||||
|
width = __INT_MAX__;
|
||||||
|
uint8_t mask[0x100 / 8] {};
|
||||||
|
for (int i = 0; i < 0x100; i++)
|
||||||
|
if (isspace(i))
|
||||||
|
mask[i / 8] |= 1 << (i % 8);
|
||||||
|
get_input(false);
|
||||||
|
while (isspace(in))
|
||||||
|
get_input(true);
|
||||||
|
result = parse_string(mask, true, conversion.suppress, conversion.allocate, 1, width, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'C':
|
||||||
|
conversion.length = LengthModifier::l;
|
||||||
|
// fall through
|
||||||
|
case 'c':
|
||||||
|
{
|
||||||
|
int width = conversion.field_width;
|
||||||
|
if (width == -1)
|
||||||
|
width = 1;
|
||||||
|
uint8_t mask[0x100 / 8] {};
|
||||||
|
result = parse_string(mask, true, conversion.suppress, conversion.allocate, width, width, false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case '[':
|
||||||
|
{
|
||||||
|
int width = conversion.field_width;
|
||||||
|
if (width == -1)
|
||||||
|
width = __INT_MAX__;
|
||||||
|
|
||||||
|
bool exclude = (*format == '^');
|
||||||
|
if (exclude)
|
||||||
|
format++;
|
||||||
|
|
||||||
|
uint8_t mask[0x100 / 8] {};
|
||||||
|
if (*format == ']')
|
||||||
|
{
|
||||||
|
mask[']' / 8] |= 1 << (']' % 8);
|
||||||
|
format++;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool first = true;
|
||||||
|
while (*format && *format != ']')
|
||||||
|
{
|
||||||
|
if (!first && *format == '-' && *(format + 1) != ']')
|
||||||
|
{
|
||||||
|
int min = BAN::Math::min(*(format - 1), *(format + 1));
|
||||||
|
int max = BAN::Math::max(*(format - 1), *(format + 1));
|
||||||
|
for (int i = min; i <= max; i++)
|
||||||
|
mask[i / 8] |= 1 << (i % 8);
|
||||||
|
format += 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mask[*format / 8] |= 1 << (*format % 8);
|
||||||
|
format++;
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*format == ']')
|
||||||
|
result = parse_string(mask, exclude, conversion.suppress, conversion.allocate, 1, width, true);
|
||||||
|
else
|
||||||
|
result = ConversionResult::MATCH_FAILURE;
|
||||||
|
format++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'n':
|
||||||
|
if (!conversion.suppress)
|
||||||
|
*va_arg(arguments, int*) = nread - (in != NONE);
|
||||||
|
conversion.suppress = true; // Dont count this as conversion
|
||||||
|
result = ConversionResult::SUCCESS;
|
||||||
|
break;
|
||||||
|
case '%':
|
||||||
|
get_input(false);
|
||||||
|
if (in == DONE)
|
||||||
|
{
|
||||||
|
result = ConversionResult::INPUT_FAILURE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (in != '%')
|
||||||
|
{
|
||||||
|
result = ConversionResult::MATCH_FAILURE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
get_input(true);
|
||||||
|
result = ConversionResult::SUCCESS;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result = ConversionResult::MATCH_FAILURE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT(result != ConversionResult::NONE);
|
||||||
|
|
||||||
|
if (result == ConversionResult::INPUT_FAILURE && conversions == 0)
|
||||||
|
return EOF;
|
||||||
|
if (result != ConversionResult::SUCCESS)
|
||||||
|
return conversions;
|
||||||
|
if (!conversion.suppress)
|
||||||
|
conversions++;
|
||||||
|
}
|
||||||
|
else if (isspace(*format))
|
||||||
|
{
|
||||||
|
get_input(false);
|
||||||
|
while (isspace(in))
|
||||||
|
get_input(true);
|
||||||
|
format++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
get_input(false);
|
||||||
|
if (in != *format)
|
||||||
|
break;
|
||||||
|
get_input(true);
|
||||||
|
format++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return conversions;
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
#include <bits/printf.h>
|
#include <bits/printf.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <scanf_impl.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -438,8 +439,14 @@ int rename(const char*, const char*);
|
||||||
// TODO
|
// TODO
|
||||||
void rewind(FILE*);
|
void rewind(FILE*);
|
||||||
|
|
||||||
// TODO
|
int scanf(const char* format, ...)
|
||||||
int scanf(const char*, ...);
|
{
|
||||||
|
va_list arguments;
|
||||||
|
va_start(arguments, format);
|
||||||
|
int ret = vscanf(format, arguments);
|
||||||
|
va_end(arguments);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
void setbuf(FILE*, char*);
|
void setbuf(FILE*, char*);
|
||||||
|
@ -465,8 +472,14 @@ int sprintf(char* buffer, const char* format, ...)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
int sscanf(const char* s, const char* format, ...)
|
||||||
int sscanf(const char*, const char*, ...);
|
{
|
||||||
|
va_list arguments;
|
||||||
|
va_start(arguments, format);
|
||||||
|
int ret = vsscanf(s, format, arguments);
|
||||||
|
va_end(arguments);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
char* tempnam(const char*, const char*);
|
char* tempnam(const char*, const char*);
|
||||||
|
@ -485,16 +498,20 @@ int vfprintf(FILE* file, const char* format, va_list arguments)
|
||||||
return printf_impl(format, arguments, [](int c, void* file) { return fputc(c, (FILE*)file); }, file);
|
return printf_impl(format, arguments, [](int c, void* file) { return fputc(c, (FILE*)file); }, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
int vfscanf(FILE* file, const char* format, va_list arguments)
|
||||||
int vfscanf(FILE*, const char*, va_list);
|
{
|
||||||
|
return scanf_impl(format, arguments, [](void* file) { return fgetc(static_cast<FILE*>(file)); }, file);
|
||||||
|
}
|
||||||
|
|
||||||
int vprintf(const char* format, va_list arguments)
|
int vprintf(const char* format, va_list arguments)
|
||||||
{
|
{
|
||||||
return vfprintf(stdout, format, arguments);
|
return vfprintf(stdout, format, arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
int vscanf(const char* format, va_list arguments)
|
||||||
int vscanf(const char*, va_list);
|
{
|
||||||
|
return vfscanf(stdin, format, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
int vsnprintf(char* buffer, size_t max_size, const char* format, va_list arguments)
|
int vsnprintf(char* buffer, size_t max_size, const char* format, va_list arguments)
|
||||||
{
|
{
|
||||||
|
@ -548,5 +565,16 @@ int vsprintf(char* buffer, const char* format, va_list arguments)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
int vsscanf(const char* s, const char* format, va_list arguments)
|
||||||
int vsscanf(const char*, const char*, va_list);
|
{
|
||||||
|
return scanf_impl(format, arguments,
|
||||||
|
[](void* data) -> int
|
||||||
|
{
|
||||||
|
char ret = **static_cast<char**>(data);
|
||||||
|
(*static_cast<char**>(data))++;
|
||||||
|
if (ret == '\0')
|
||||||
|
return -1;
|
||||||
|
return ret;
|
||||||
|
}, &s
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ int memcmp(const void* s1, const void* s2, size_t n)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((optimize("-O0")))
|
|
||||||
void* memcpy(void* __restrict__ dstp, const void* __restrict__ srcp, size_t n)
|
void* memcpy(void* __restrict__ dstp, const void* __restrict__ srcp, size_t n)
|
||||||
{
|
{
|
||||||
unsigned char* dst = static_cast<unsigned char*>(dstp);
|
unsigned char* dst = static_cast<unsigned char*>(dstp);
|
||||||
|
@ -46,7 +45,6 @@ void* memmove(void* destp, const void* srcp, size_t n)
|
||||||
return destp;
|
return destp;
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((optimize("-O0")))
|
|
||||||
void* memset(void* s, int c, size_t n)
|
void* memset(void* s, int c, size_t n)
|
||||||
{
|
{
|
||||||
unsigned char* p = static_cast<unsigned char*>(s);
|
unsigned char* p = static_cast<unsigned char*>(s);
|
||||||
|
|
Loading…
Reference in New Issue