diff --git a/lib/libc/src/printf.c b/lib/libc/src/printf.c index db71742..a0d94d1 100644 --- a/lib/libc/src/printf.c +++ b/lib/libc/src/printf.c @@ -50,6 +50,14 @@ typedef struct { } sign_character; } flags_t; +/// man 3 printf: Precision +typedef struct { + bool present; // This field is optional + bool invalid; + int precision; + int argument; +} precision_t; + /// man 3 printf: Length modifier typedef struct { bool invalid; @@ -64,7 +72,7 @@ typedef struct { flags_t flags; length_modifier_t length; unsigned int field_with; ///< man 3 printf: Field width - // Missing: precision + precision_t precision; } printf_ctx_t; static inline bool __attribute__((always_inline)) isdigit(char c) @@ -225,6 +233,59 @@ static unsigned int printf_field_width(const char *format, int *index) return width; } +static precision_t printf_precision(const char *format, int *index) +{ + precision_t precision = { + .present = false, + .invalid = false, + .precision = 0, + }; + + if (format[*index] == '.') + return precision; + + precision.present = true; + *index += 1; + + // A negative precision is taken as if the precision were omitted + bool ignore = false; + + switch (format[*index]) { + + case '*': + // the actual logic is not implemented, but we need to parse it anyway + precision.present = false; + + *index += 1; + if (isdigit(format[*index])) { + precision.argument = printf_field_width(format, index); + if (format[*(index++)] != '$') + precision.invalid = true; + } + break; + + case '-': + ignore = true; + *index += 1; + // cannot have '%.-d' for example + if (!isdigit(format[*index])) { + precision.invalid = true; + return precision; + } + + __attribute__((fallthrough)); + + default: + precision.precision = printf_field_width(format, index); + break; + } + + if (ignore) + precision.precision = 0; + + return precision; +} + static length_modifier_t printf_length_modifiers(const char *format, int *index) { length_modifier_t length = {0}; @@ -374,20 +435,22 @@ int vprintf(const char *format, va_list parameters) // After the delimiter, the argument is of the following format: // - // %[flag_characters][field_with][length_modifier]conversion_specifier + // %[flag_characters][field_with][.precision][length_modifier]conversion_specifier // // Refer to man 3 printf and the corresponding parsers for more // detailed explanations. const flags_t flags = printf_flags_characters(format, &i); const unsigned int width = printf_field_width(format, &i); + const precision_t precision = printf_precision(format, &i); const length_modifier_t length = printf_length_modifiers(format, &i); const printf_ctx_t ctx = (printf_ctx_t){ - .invalid = flags.invalid || length.invalid, + .invalid = flags.invalid || length.invalid || precision.invalid, .flags = flags, .length = length, .field_with = width, + .precision = precision, }; if (ctx.invalid) {