lib/format: support asterisk with format specifier

used by some libraries, e.g:

printf("%*.s", 5, "Sun");     -> '  Sun'
printf("%.*s", 2, "Sun");     -> 'Su'
printf("%*.*s", 5, 2, "Sun"); -> '   Su'
printf("%.*d", 3, 10);        -> ' 10'

Issue #5234
This commit is contained in:
Alexander Boettcher 2024-06-03 16:32:33 +02:00 committed by Norman Feske
parent 0aafec038d
commit 25717df15f

View File

@ -32,19 +32,26 @@ class Format_command
enum Type { INT, UINT, STRING, CHAR, PTR, PERCENT, INVALID };
enum Length { DEFAULT, LONG, SIZE_T, LONG_LONG };
struct Decimal { int value; bool star; };
private:
/**
* Read decimal value from string
*/
int decode_decimal(const char *str, int *consumed)
Decimal decode_decimal(const char *str, int *consumed)
{
if (str[*consumed] == '*') {
(*consumed) ++;
return { .value = 0, .star = true };
}
int res = 0;
while (1) {
char c = str[*consumed];
if (!c || c < '0' || c > '0' + 9)
return res;
return { .value = res, .star = false };
res = (res * 10) + c - '0';
(*consumed)++;
@ -53,15 +60,15 @@ class Format_command
public:
Type type = INVALID; /* format argument type */
Length length = DEFAULT; /* format argument length */
int padding = 0; /* min number of characters to print */
int precision = 0; /* max number of characters to print */
int base = 10; /* base of numeric arguments */
bool lalign = false; /* align left */
bool zeropad = false; /* pad with zero instead of space */
bool uppercase = false; /* use upper case for hex numbers */
int consumed = 0; /* nb of consumed format string chars */
Type type = INVALID; /* format argument type */
Length length = DEFAULT; /* format argument length */
Decimal padding { }; /* min number of characters to print */
Decimal precision { }; /* max number of characters to print */
int base = 10; /* base of numeric arguments */
bool lalign = false; /* align left */
bool zeropad = false; /* pad with zero instead of space */
bool uppercase = false; /* use upper case for hex numbers */
int consumed = 0; /* nb of consumed format string chars */
/**
* Constructor
@ -182,6 +189,13 @@ void Console::vprintf(const char *format, va_list list)
Format_command cmd(format);
/* read numeric argument from va_list */
if (cmd.padding.star)
cmd.padding.value = va_arg(list, int);
if (cmd.precision.star)
cmd.precision.value = va_arg(list, int);
long long numeric_arg = 0;
if (cmd.numeric()) {
switch (cmd.length) {
@ -226,7 +240,8 @@ void Console::vprintf(const char *format, va_list list)
case Format_command::UINT:
if (cmd.length == Format_command::LONG_LONG) {
out_unsigned<unsigned long long>(numeric_arg, cmd.base, cmd.padding,
out_unsigned<unsigned long long>(numeric_arg, cmd.base,
cmd.padding.value,
[&] (char c) { _out_char(c); });
break;
}
@ -235,7 +250,8 @@ void Console::vprintf(const char *format, va_list list)
case Format_command::PTR:
out_unsigned<unsigned long>((long)numeric_arg, cmd.base, cmd.padding,
out_unsigned<unsigned long>((long)numeric_arg, cmd.base,
cmd.padding.value,
[&] (char c) { _out_char(c); });
break;
@ -246,12 +262,12 @@ void Console::vprintf(const char *format, va_list list)
case Format_command::STRING:
if (cmd.precision) {
if (cmd.precision.value) {
int p = 0;
char const *a = va_arg(list, const char *);
for (; a && *a && p < cmd.precision; ++a, ++p)
for (; a && *a && p < cmd.precision.value; ++a, ++p)
_out_char(*a);
for (; p < cmd.padding; ++p)
for (; p < cmd.padding.value; ++p)
_out_char(' ');
} else {
_out_string(va_arg(list, const char *));