diff --git a/kernel/kprintf.c b/kernel/kprintf.c index b7e0c1a..166930b 100644 --- a/kernel/kprintf.c +++ b/kernel/kprintf.c @@ -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: * */ -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)); }