kprintf: minor refactor, fix stupid offset bugs

This commit is contained in:
anna 2021-10-03 18:59:06 +02:00
parent d475429639
commit 3e43ec5491
Signed by: fef
GPG key ID: EC22E476DC2D3D84

View file

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