kprintf: minor refactor, fix stupid offset bugs

main
anna 3 years ago
parent d475429639
commit 3e43ec5491
Signed by: fef
GPG Key ID: EC22E476DC2D3D84

@ -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));
}

Loading…
Cancel
Save