|
|
|
@ -61,7 +61,7 @@ struct fmt_sequence {
|
|
|
|
|
bool uppercase;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void parse_fmt_sequence(struct fmt_sequence *sequence, const char **restrict pos);
|
|
|
|
|
static void parse_fmt_sequence(struct fmt_sequence *sequence, const char **restrict posptr);
|
|
|
|
|
|
|
|
|
|
/** @brief Write a NUL terminated string using the current `printer`. */
|
|
|
|
|
static isize write_asciz(const char *s);
|
|
|
|
@ -97,8 +97,10 @@ int kvprintf(const char *fmt, va_list _args)
|
|
|
|
|
*/
|
|
|
|
|
fmt = tmp;
|
|
|
|
|
|
|
|
|
|
if (fmt_ret < 0)
|
|
|
|
|
if (fmt_ret < 0) {
|
|
|
|
|
ret = fmt_ret;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
ret += fmt_ret;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -149,12 +151,13 @@ static isize render_percent(const struct fmt_sequence *sequence, va_list *ap);
|
|
|
|
|
* specifiers and the (imho insane) $ directive:
|
|
|
|
|
* <https://www.freebsd.org/cgi/man.cgi?query=printf&sektion=3&manpath=FreeBSD+13.0-RELEASE>
|
|
|
|
|
*/
|
|
|
|
|
void parse_fmt_sequence(struct fmt_sequence *sequence, const char **restrict pos)
|
|
|
|
|
void parse_fmt_sequence(struct fmt_sequence *sequence, const char **restrict posptr)
|
|
|
|
|
{
|
|
|
|
|
memset(sequence, 0, sizeof(*sequence));
|
|
|
|
|
|
|
|
|
|
if (**pos == '%') { /* %% */
|
|
|
|
|
if (**posptr == '%') { /* %% */
|
|
|
|
|
sequence->render = render_percent;
|
|
|
|
|
*posptr += 1;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -163,7 +166,7 @@ void parse_fmt_sequence(struct fmt_sequence *sequence, const char **restrict pos
|
|
|
|
|
*/
|
|
|
|
|
bool continue_parse_flags = true;
|
|
|
|
|
while (continue_parse_flags) {
|
|
|
|
|
switch (**pos) {
|
|
|
|
|
switch (**posptr) {
|
|
|
|
|
case '#':
|
|
|
|
|
sequence->flags.hash = true;
|
|
|
|
|
break;
|
|
|
|
@ -189,75 +192,74 @@ void parse_fmt_sequence(struct fmt_sequence *sequence, const char **restrict pos
|
|
|
|
|
continue_parse_flags = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
(*pos)++;
|
|
|
|
|
*posptr += 1;
|
|
|
|
|
}
|
|
|
|
|
(*pos)--;
|
|
|
|
|
*posptr -= 1;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* parse optional minimum digits
|
|
|
|
|
*/
|
|
|
|
|
while (**pos >= '0' && **pos <= '9') {
|
|
|
|
|
while (**posptr >= '0' && **posptr <= '9') {
|
|
|
|
|
sequence->min_width *= 10;
|
|
|
|
|
sequence->min_width += **pos - '0';
|
|
|
|
|
sequence->min_width += **posptr - '0';
|
|
|
|
|
if (sequence->max_precision > 128)
|
|
|
|
|
sequence->max_precision = 128;
|
|
|
|
|
(*pos)++;
|
|
|
|
|
*posptr += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* parse optional maximum precision
|
|
|
|
|
*/
|
|
|
|
|
if (**pos == '.') {
|
|
|
|
|
(*pos)++;
|
|
|
|
|
while (**pos >= '0' && **pos <= '9') {
|
|
|
|
|
if (**posptr == '.') {
|
|
|
|
|
*posptr += 1;
|
|
|
|
|
while (**posptr >= '0' && **posptr <= '9') {
|
|
|
|
|
sequence->max_precision *= 10;
|
|
|
|
|
sequence->max_precision += **pos - '0';
|
|
|
|
|
sequence->max_precision += **posptr - '0';
|
|
|
|
|
/* sanitize length (prevents stack overflow) */
|
|
|
|
|
if (sequence->max_precision > 128)
|
|
|
|
|
sequence->max_precision = 128;
|
|
|
|
|
(*pos)++;
|
|
|
|
|
*posptr += 1;
|
|
|
|
|
}
|
|
|
|
|
(*pos)--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* parse optional length modifier
|
|
|
|
|
*/
|
|
|
|
|
switch (**pos) {
|
|
|
|
|
switch (**posptr) {
|
|
|
|
|
case 'h':
|
|
|
|
|
case 'H':
|
|
|
|
|
if ((*pos)[1] == 'h' || (*pos)[1] == 'H') {
|
|
|
|
|
if ((*posptr)[1] == 'h' || (*posptr)[1] == 'H') {
|
|
|
|
|
sequence->length_modifier = LENGTH_HH;
|
|
|
|
|
(*pos) += 2;
|
|
|
|
|
*posptr += 2;
|
|
|
|
|
} else {
|
|
|
|
|
sequence->length_modifier = LENGTH_H;
|
|
|
|
|
(*pos) += 1;
|
|
|
|
|
*posptr += 1;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 'l':
|
|
|
|
|
case 'L':
|
|
|
|
|
if ((*pos)[1] == 'l' || (*pos)[1] == 'L') {
|
|
|
|
|
if ((*posptr)[1] == 'l' || (*posptr)[1] == 'L') {
|
|
|
|
|
sequence->length_modifier = LENGTH_LL;
|
|
|
|
|
(*pos) += 2;
|
|
|
|
|
*posptr += 2;
|
|
|
|
|
} else {
|
|
|
|
|
sequence->length_modifier = LENGTH_L;
|
|
|
|
|
(*pos) += 1;
|
|
|
|
|
*posptr += 1;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 'j':
|
|
|
|
|
case 'J':
|
|
|
|
|
sequence->length_modifier = LENGTH_J;
|
|
|
|
|
(*pos)++;
|
|
|
|
|
*posptr += 1;
|
|
|
|
|
break;
|
|
|
|
|
case 't':
|
|
|
|
|
case 'T':
|
|
|
|
|
sequence->length_modifier = LENGTH_T;
|
|
|
|
|
(*pos)++;
|
|
|
|
|
*posptr += 1;
|
|
|
|
|
break;
|
|
|
|
|
case 'z':
|
|
|
|
|
case 'Z':
|
|
|
|
|
sequence->length_modifier = LENGTH_Z;
|
|
|
|
|
(*pos)++;
|
|
|
|
|
*posptr += 1;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
@ -266,7 +268,7 @@ void parse_fmt_sequence(struct fmt_sequence *sequence, const char **restrict pos
|
|
|
|
|
/*
|
|
|
|
|
* parse type specifier
|
|
|
|
|
*/
|
|
|
|
|
switch (**pos) {
|
|
|
|
|
switch (**posptr) {
|
|
|
|
|
case 'C':
|
|
|
|
|
sequence->length_modifier = LENGTH_L;
|
|
|
|
|
/* fall through */
|
|
|
|
@ -305,7 +307,7 @@ void parse_fmt_sequence(struct fmt_sequence *sequence, const char **restrict pos
|
|
|
|
|
sequence->render = NULL;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
(*pos)++;
|
|
|
|
|
*posptr += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ssize_t render_c(const struct fmt_sequence *sequence, va_list *ap)
|
|
|
|
@ -325,87 +327,63 @@ static ssize_t render_c(const struct fmt_sequence *sequence, va_list *ap)
|
|
|
|
|
|
|
|
|
|
static ssize_t render_s(const struct fmt_sequence *sequence, va_list *ap)
|
|
|
|
|
{
|
|
|
|
|
/* yes i know i forgot the wchar_t if the length_modifier is LENGTH_L but idgaf */
|
|
|
|
|
/*
|
|
|
|
|
* the string is a wchar_t if LENGTH_L is set, but that would require
|
|
|
|
|
* a full UTF-8 encoder which i won't write in the near future. Cope.
|
|
|
|
|
*/
|
|
|
|
|
const char *s = va_arg(*ap, char *);
|
|
|
|
|
return write_asciz(s);
|
|
|
|
|
if (sequence->max_precision)
|
|
|
|
|
return write_bytes(s, strnlen(s, sequence->max_precision));
|
|
|
|
|
else
|
|
|
|
|
return write_asciz(s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void get_arg_signed(intmax_t *dest,
|
|
|
|
|
const struct fmt_sequence *sequence,
|
|
|
|
|
va_list *ap)
|
|
|
|
|
static inline intmax_t get_arg_signed(const struct fmt_sequence *sequence, va_list *ap)
|
|
|
|
|
{
|
|
|
|
|
switch (sequence->length_modifier) {
|
|
|
|
|
case LENGTH_H:
|
|
|
|
|
case LENGTH_HH:
|
|
|
|
|
case LENGTH_DEFAULT:
|
|
|
|
|
/* short and char will be promoted to int with parameter passing */
|
|
|
|
|
*dest = va_arg(*ap, int);
|
|
|
|
|
break;
|
|
|
|
|
return va_arg(*ap, int);
|
|
|
|
|
case LENGTH_L:
|
|
|
|
|
*dest = va_arg(*ap, long);
|
|
|
|
|
break;
|
|
|
|
|
return va_arg(*ap, long);
|
|
|
|
|
case LENGTH_LL:
|
|
|
|
|
*dest = va_arg(*ap, long long);
|
|
|
|
|
break;
|
|
|
|
|
return va_arg(*ap, long long);
|
|
|
|
|
case LENGTH_Z:
|
|
|
|
|
*dest = va_arg(*ap, isize);
|
|
|
|
|
break;
|
|
|
|
|
return va_arg(*ap, isize);
|
|
|
|
|
case LENGTH_J:
|
|
|
|
|
*dest = va_arg(*ap, intmax_t);
|
|
|
|
|
break;
|
|
|
|
|
return va_arg(*ap, intmax_t);
|
|
|
|
|
case LENGTH_T:
|
|
|
|
|
*dest = va_arg(*ap, intptr_t);
|
|
|
|
|
break;
|
|
|
|
|
return va_arg(*ap, intptr_t);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void get_arg_unsigned(uintmax_t *dest,
|
|
|
|
|
const struct fmt_sequence *sequence,
|
|
|
|
|
va_list *ap)
|
|
|
|
|
static inline uintmax_t get_arg_unsigned(const struct fmt_sequence *sequence, va_list *ap)
|
|
|
|
|
{
|
|
|
|
|
switch (sequence->length_modifier) {
|
|
|
|
|
case LENGTH_H:
|
|
|
|
|
case LENGTH_HH:
|
|
|
|
|
case LENGTH_DEFAULT:
|
|
|
|
|
/* short and char will be promoted to int with parameter passing */
|
|
|
|
|
*dest = va_arg(*ap, unsigned int);
|
|
|
|
|
break;
|
|
|
|
|
return va_arg(*ap, unsigned int);
|
|
|
|
|
case LENGTH_L:
|
|
|
|
|
*dest = va_arg(*ap, unsigned long);
|
|
|
|
|
break;
|
|
|
|
|
return va_arg(*ap, unsigned long);
|
|
|
|
|
case LENGTH_LL:
|
|
|
|
|
*dest = va_arg(*ap, unsigned long long);
|
|
|
|
|
break;
|
|
|
|
|
return va_arg(*ap, unsigned long long);
|
|
|
|
|
case LENGTH_Z:
|
|
|
|
|
*dest = va_arg(*ap, usize);
|
|
|
|
|
break;
|
|
|
|
|
return va_arg(*ap, usize);
|
|
|
|
|
case LENGTH_J:
|
|
|
|
|
*dest = va_arg(*ap, uintmax_t);
|
|
|
|
|
break;
|
|
|
|
|
return va_arg(*ap, uintmax_t);
|
|
|
|
|
case LENGTH_T:
|
|
|
|
|
*dest = va_arg(*ap, uintptr_t);
|
|
|
|
|
break;
|
|
|
|
|
return va_arg(*ap, uintptr_t);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *digit_table_smol = "0123456789abcdef";
|
|
|
|
|
static const char *digit_table_big = "0123456789ABCDEF";
|
|
|
|
|
|
|
|
|
|
static inline void stringify_uint(char **buf, uintmax_t val, const char *digit_table,
|
|
|
|
|
unsigned int radix, int direction)
|
|
|
|
|
{
|
|
|
|
|
do {
|
|
|
|
|
**buf = digit_table[val % radix];
|
|
|
|
|
*buf += direction;
|
|
|
|
|
} while ((val /= radix) != 0);
|
|
|
|
|
*buf -= direction;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static isize render_d(const struct fmt_sequence *sequence, va_list *ap)
|
|
|
|
|
{
|
|
|
|
|
isize ret = 0;
|
|
|
|
|
intmax_t val;
|
|
|
|
|
get_arg_signed(&val, sequence, ap);
|
|
|
|
|
intmax_t val = get_arg_signed(sequence, ap);
|
|
|
|
|
|
|
|
|
|
if (val < 0) {
|
|
|
|
|
val = -val;
|
|
|
|
@ -428,10 +406,20 @@ static isize render_d(const struct fmt_sequence *sequence, va_list *ap)
|
|
|
|
|
char *buf = alloca(len);
|
|
|
|
|
char *pos = &buf[len - 1];
|
|
|
|
|
|
|
|
|
|
stringify_uint(&pos, val, digit_table_smol, 10, -1);
|
|
|
|
|
while (sequence->min_width > len - (pos - buf))
|
|
|
|
|
*--pos = '0';
|
|
|
|
|
do {
|
|
|
|
|
*pos-- = (char)(val % 10) + '0'; /* NOLINT */
|
|
|
|
|
val /= 10;
|
|
|
|
|
} while (val > 0);
|
|
|
|
|
|
|
|
|
|
char fillchr;
|
|
|
|
|
if (sequence->flags.zero)
|
|
|
|
|
fillchr = '0';
|
|
|
|
|
else
|
|
|
|
|
fillchr = ' ';
|
|
|
|
|
while (sequence->min_width > len - (pos - buf) - 1)
|
|
|
|
|
*pos-- = fillchr;
|
|
|
|
|
|
|
|
|
|
pos += 1;
|
|
|
|
|
isize tmp = write_bytes(pos, len - (pos - buf));
|
|
|
|
|
if (tmp > 0)
|
|
|
|
|
ret += tmp;
|
|
|
|
@ -454,8 +442,7 @@ static isize render_o(const struct fmt_sequence *sequence, va_list *ap)
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uintmax_t val;
|
|
|
|
|
get_arg_unsigned(&val, sequence, ap);
|
|
|
|
|
uintmax_t val = get_arg_unsigned(sequence, ap);
|
|
|
|
|
|
|
|
|
|
usize len = 22; /* 2**64 has 22 octal digits, let's hope intmax_t isn't 128 bits */
|
|
|
|
|
if (sequence->min_width > len)
|
|
|
|
@ -463,10 +450,20 @@ static isize render_o(const struct fmt_sequence *sequence, va_list *ap)
|
|
|
|
|
char *buf = alloca(len);
|
|
|
|
|
char *pos = &buf[len - 1];
|
|
|
|
|
|
|
|
|
|
stringify_uint(&pos, val, digit_table_smol, 8, -1);
|
|
|
|
|
while (sequence->min_width > len - (pos - buf))
|
|
|
|
|
*--pos = '0';
|
|
|
|
|
do {
|
|
|
|
|
*pos-- = (char)(val % 010) + '0'; /* NOLINT */
|
|
|
|
|
val /= 010;
|
|
|
|
|
} while (val > 0);
|
|
|
|
|
|
|
|
|
|
char fillchr;
|
|
|
|
|
if (sequence->flags.zero)
|
|
|
|
|
fillchr = '0';
|
|
|
|
|
else
|
|
|
|
|
fillchr = ' ';
|
|
|
|
|
while (sequence->min_width > len - (pos - buf) - 1)
|
|
|
|
|
*pos-- = fillchr;
|
|
|
|
|
|
|
|
|
|
pos++;
|
|
|
|
|
isize tmp = write_bytes(pos, len - (pos - buf));
|
|
|
|
|
if (tmp > 0)
|
|
|
|
|
ret += tmp;
|
|
|
|
@ -475,6 +472,9 @@ static isize render_o(const struct fmt_sequence *sequence, va_list *ap)
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *const digit_table_smol = "0123456789abcdef";
|
|
|
|
|
static const char *const digit_table_big = "0123456789ABCDEF";
|
|
|
|
|
|
|
|
|
|
static isize render_p(const struct fmt_sequence *sequence, va_list *ap)
|
|
|
|
|
{
|
|
|
|
|
/* 2 hex digits per byte + 2 for 0x prefix */
|
|
|
|
@ -493,7 +493,7 @@ static isize render_p(const struct fmt_sequence *sequence, va_list *ap)
|
|
|
|
|
|
|
|
|
|
while (pos > &buf[1]) {
|
|
|
|
|
*pos-- = digit_table[ptr % 0x10];
|
|
|
|
|
ptr >>= 4;
|
|
|
|
|
ptr /= 0x10;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return write_bytes(buf, sizeof(buf));
|
|
|
|
@ -513,8 +513,7 @@ static isize render_u(const struct fmt_sequence *sequence, va_list *ap)
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uintmax_t val;
|
|
|
|
|
get_arg_unsigned(&val, sequence, ap);
|
|
|
|
|
uintmax_t val = get_arg_unsigned(sequence, ap);
|
|
|
|
|
|
|
|
|
|
usize len = 20; /* 2^64 has 20 decimal digits, let's hope intmax_t isn't 128 bits */
|
|
|
|
|
if (sequence->min_width > len)
|
|
|
|
@ -522,10 +521,20 @@ static isize render_u(const struct fmt_sequence *sequence, va_list *ap)
|
|
|
|
|
char *buf = alloca(len);
|
|
|
|
|
char *pos = &buf[len - 1];
|
|
|
|
|
|
|
|
|
|
stringify_uint(&pos, val, digit_table_smol, 10, -1);
|
|
|
|
|
while (sequence->min_width > len - (pos - buf))
|
|
|
|
|
*--pos = '0';
|
|
|
|
|
do {
|
|
|
|
|
*pos-- = (char)(val % 10) + '0'; /* NOLINT */
|
|
|
|
|
val /= 10;
|
|
|
|
|
} while (val > 0);
|
|
|
|
|
|
|
|
|
|
char fillchr;
|
|
|
|
|
if (sequence->flags.zero)
|
|
|
|
|
fillchr = '0';
|
|
|
|
|
else
|
|
|
|
|
fillchr = ' ';
|
|
|
|
|
while (sequence->min_width > len - (pos - buf) - 1)
|
|
|
|
|
*pos-- = fillchr;
|
|
|
|
|
|
|
|
|
|
pos++;
|
|
|
|
|
isize tmp = write_bytes(pos, len - (pos - buf));
|
|
|
|
|
if (tmp > 0)
|
|
|
|
|
ret += tmp;
|
|
|
|
@ -541,10 +550,9 @@ static isize render_x(const struct fmt_sequence *sequence, va_list *ap)
|
|
|
|
|
if (len < sequence->min_width)
|
|
|
|
|
len = sequence->min_width;
|
|
|
|
|
buf = alloca(len);
|
|
|
|
|
char *pos = &buf[len];
|
|
|
|
|
char *pos = &buf[len - 1];
|
|
|
|
|
|
|
|
|
|
uintmax_t val;
|
|
|
|
|
get_arg_unsigned(&val, sequence, ap);
|
|
|
|
|
uintmax_t val = get_arg_unsigned(sequence, ap);
|
|
|
|
|
|
|
|
|
|
const char *digit_table;
|
|
|
|
|
if (sequence->uppercase)
|
|
|
|
@ -552,10 +560,20 @@ static isize render_x(const struct fmt_sequence *sequence, va_list *ap)
|
|
|
|
|
else
|
|
|
|
|
digit_table = digit_table_smol;
|
|
|
|
|
|
|
|
|
|
stringify_uint(&pos, val, digit_table, 16, -1);
|
|
|
|
|
while (sequence->min_width > len - (pos - buf))
|
|
|
|
|
*--pos = '0';
|
|
|
|
|
do {
|
|
|
|
|
*pos-- = digit_table[val % 0x10];
|
|
|
|
|
val /= 0x10;
|
|
|
|
|
} while (val > 0);
|
|
|
|
|
|
|
|
|
|
char fillchr;
|
|
|
|
|
if (sequence->flags.zero)
|
|
|
|
|
fillchr = '0';
|
|
|
|
|
else
|
|
|
|
|
fillchr = ' ';
|
|
|
|
|
while (sequence->min_width > len - (pos - buf) - 1)
|
|
|
|
|
*pos-- = fillchr;
|
|
|
|
|
|
|
|
|
|
pos++;
|
|
|
|
|
return write_bytes(pos, len - (pos - buf));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|