diff --git a/libc/printf_impl.cpp b/libc/printf_impl.cpp index 367edc803..76791dabd 100644 --- a/libc/printf_impl.cpp +++ b/libc/printf_impl.cpp @@ -15,6 +15,19 @@ written++; \ } while (false) +enum class length_t +{ + none, + hh, + h, + l, + ll, + j, + z, + t, + L, +}; + struct format_options_t { bool alternate_form { false }; @@ -24,6 +37,7 @@ struct format_options_t bool show_plus_sign { false }; int width { -1 }; int percision { -1 }; + length_t length { length_t::none }; }; template @@ -308,45 +322,157 @@ extern "C" int printf_impl(const char* format, va_list arguments, int (*putc_fun options.percision = percision; } - // TODO: Lenght modifier + // PARSE LENGTH + if (*format == 'h') + { + if (*(format + 1) == 'h') + { + format++; + options.length = length_t::hh; + } + else + options.length = length_t::h; + } + else if (*format == 'l') + { + if (*(format + 1) == 'l') + { + format++; + options.length = length_t::ll; + } + else + options.length = length_t::l; + } + else if (*format == 'j') + options.length = length_t::j; + else if (*format == 'z') + options.length = length_t::z; + else if (*format == 't') + options.length = length_t::t; + else if (*format == 'L') + options.length = length_t::L; + else + format--; + format++; char conversion[128]; const char* string = nullptr; int length = -1; +#define PARSE_INT_CASE(length, type) \ + case length_t::length: integer_to_string(conversion, va_arg(arguments, type), BASE_, UPPER_, options); break + +#define PARSE_INT_CASE_CAST(length, cast, type) \ + case length_t::length: integer_to_string(conversion, va_arg(arguments, type), BASE_, UPPER_, options); break + +#define PARSE_INT_DEFAULT(type) \ + default: integer_to_string(conversion, va_arg(arguments, type), BASE_, UPPER_, options); break + switch (*format) { case 'd': case 'i': { - int value = va_arg(arguments, int); - integer_to_string(conversion, value, 10, false, options); + switch (options.length) + { +#define BASE_ 10 +#define UPPER_ false + PARSE_INT_CASE_CAST(hh, signed char, int); + PARSE_INT_CASE_CAST(h, short, int); + PARSE_INT_CASE(l, long); + PARSE_INT_CASE(ll, long long); + PARSE_INT_CASE(j, intmax_t); + PARSE_INT_CASE(z, ssize_t); + PARSE_INT_CASE(t, ptrdiff_t); + PARSE_INT_DEFAULT(int); +#undef BASE_ +#undef UPPER_ + } string = conversion; format++; break; } case 'o': { - unsigned int value = va_arg(arguments, unsigned int); - integer_to_string(conversion, value, 8, false, options); + switch (options.length) + { +#define BASE_ 8 +#define UPPER_ false + PARSE_INT_CASE_CAST(hh, unsigned char, unsigned int); + PARSE_INT_CASE_CAST(h, unsigned short, unsigned int); + PARSE_INT_CASE(l, unsigned long); + PARSE_INT_CASE(ll, unsigned long long); + PARSE_INT_CASE(j, uintmax_t); + PARSE_INT_CASE(z, size_t); + PARSE_INT_CASE(t, uintptr_t); + PARSE_INT_DEFAULT(unsigned int); +#undef BASE_ +#undef UPPER_ + } string = conversion; format++; break; } case 'u': { - unsigned int value = va_arg(arguments, unsigned int); - integer_to_string(conversion, value, 10, false, options); + switch (options.length) + { +#define BASE_ 10 +#define UPPER_ false + PARSE_INT_CASE_CAST(hh, unsigned char, unsigned int); + PARSE_INT_CASE_CAST(h, unsigned short, unsigned int); + PARSE_INT_CASE(l, unsigned long); + PARSE_INT_CASE(ll, unsigned long long); + PARSE_INT_CASE(j, uintmax_t); + PARSE_INT_CASE(z, size_t); + PARSE_INT_CASE(t, uintptr_t); + PARSE_INT_DEFAULT(unsigned int); +#undef BASE_ +#undef UPPER_ + } string = conversion; format++; break; } case 'x': + { + switch (options.length) + { +#define BASE_ 16 +#define UPPER_ false + PARSE_INT_CASE_CAST(hh, unsigned char, unsigned int); + PARSE_INT_CASE_CAST(h, unsigned short, unsigned int); + PARSE_INT_CASE(l, unsigned long); + PARSE_INT_CASE(ll, unsigned long long); + PARSE_INT_CASE(j, uintmax_t); + PARSE_INT_CASE(z, size_t); + PARSE_INT_CASE(t, uintptr_t); + PARSE_INT_DEFAULT(unsigned int); +#undef BASE_ +#undef UPPER_ + } + string = conversion; + format++; + break; + } case 'X': { - unsigned int value = va_arg(arguments, unsigned int); - integer_to_string(conversion, value, 16, *format == 'X', options); + switch (options.length) + { +#define BASE_ 16 +#define UPPER_ true + PARSE_INT_CASE_CAST(hh, unsigned char, unsigned int); + PARSE_INT_CASE_CAST(h, unsigned short, unsigned int); + PARSE_INT_CASE(l, unsigned long); + PARSE_INT_CASE(ll, unsigned long long); + PARSE_INT_CASE(j, uintmax_t); + PARSE_INT_CASE(z, size_t); + PARSE_INT_CASE(t, uintptr_t); + PARSE_INT_DEFAULT(unsigned int); +#undef BASE_ +#undef UPPER_ + } string = conversion; format++; break; @@ -355,8 +481,11 @@ extern "C" int printf_impl(const char* format, va_list arguments, int (*putc_fun case 'e': case 'E': { - double value = va_arg(arguments, double); - floating_point_to_exponent_string(conversion, value, *format == 'E', options); + switch (options.length) + { + case length_t::L: floating_point_to_exponent_string (conversion, va_arg(arguments, long double), *format == 'E', options); break; + default: floating_point_to_exponent_string (conversion, va_arg(arguments, double), *format == 'E', options); break; + } string = conversion; format++; break; @@ -364,13 +493,15 @@ extern "C" int printf_impl(const char* format, va_list arguments, int (*putc_fun case 'f': case 'F': { - double value = va_arg(arguments, double); - floating_point_to_string(conversion, value, *format == 'F', options); + switch (options.length) + { + case length_t::L: floating_point_to_string (conversion, va_arg(arguments, long double), *format == 'F', options); break; + default: floating_point_to_string (conversion, va_arg(arguments, double), *format == 'F', options); break; + } string = conversion; format++; break; } -#endif case 'g': case 'G': // TODO @@ -379,6 +510,7 @@ extern "C" int printf_impl(const char* format, va_list arguments, int (*putc_fun case 'A': // TODO break; +#endif case 'c': { conversion[0] = va_arg(arguments, int); @@ -416,8 +548,17 @@ extern "C" int printf_impl(const char* format, va_list arguments, int (*putc_fun } case 'n': { - int* target = va_arg(arguments, int*); - *target = written; + switch (options.length) + { + case length_t::hh: *va_arg(arguments, signed char*) = written; break; + case length_t::h: *va_arg(arguments, short*) = written; break; + case length_t::l: *va_arg(arguments, long*) = written; break; + case length_t::ll: *va_arg(arguments, long long*) = written; break; + case length_t::j: *va_arg(arguments, intmax_t*) = written; break; + case length_t::z: *va_arg(arguments, ssize_t*) = written; break; + case length_t::t: *va_arg(arguments, ptrdiff_t*) = written; break; + default: *va_arg(arguments, int*) = written; break; + } format++; break; }