Skip to content

Commit

Permalink
feat(libc: printf): add support for precision specifier
Browse files Browse the repository at this point in the history
The logs issued by uACPI make plenty use of this field, it is
now necessary to support it.
  • Loading branch information
d4ilyrun committed Jun 12, 2024
1 parent 7b60276 commit 9582ed8
Showing 1 changed file with 66 additions and 3 deletions.
69 changes: 66 additions & 3 deletions lib/libc/src/printf.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)
Expand Down Expand Up @@ -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};
Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit 9582ed8

Please sign in to comment.