#include #include #include #include #include #include #include #include #include typedef void (*log_fn_t)(void* ctx, const char* buf, size_t len); static void print_pad(log_fn_t callback, void* ctx, char c, size_t count) { for (size_t i = 0; i < count; i++) callback(ctx, &c, 1); } static void do_print(log_fn_t callback, void* ctx, const char* restrict fmt, va_list args) { // Amount of bytes written. int32_t written = 0; while (*fmt != '\0') { size_t maxrem = INT32_MAX - written; if (fmt[0] != '%' || fmt[1] == '%') { if (fmt[0] == '%') fmt++; size_t amount = 1; while (fmt[amount] && fmt[amount] != '%') amount++; if (maxrem < amount) { return; } callback(ctx, fmt, amount); fmt += amount; written += amount; continue; } const char* format_begun_at = fmt++; bool force_sign = false; bool blank_sign = false; bool write_prefix = false; bool pad_zero = false; bool left_align = false; size_t width = 0; bool has_precision = false; size_t precision = 0; size_t size = sizeof(uint32_t); char number[32]; check_fmt: memset(number, 0, sizeof(number)); switch (*fmt) { // Flags case '+': { force_sign = true; fmt++; goto check_fmt; } case ' ': { blank_sign = true; fmt++; goto check_fmt; } case '#': { write_prefix = true; fmt++; goto check_fmt; } case '0': { pad_zero = true; fmt++; goto check_fmt; } case '-': { left_align = true; fmt++; goto check_fmt; } // Width case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { width = 0; while (*fmt >= '0' && *fmt <= '9') { width = width * 10 + (*fmt - '0'); fmt++; } goto check_fmt; } // Precision case '.': { has_precision = true; fmt++; precision = 0; if (*fmt == '*') { const int32_t arg = va_arg(args, int32_t); if (arg < 0) has_precision = false; else precision = arg; fmt++; } else { while (*fmt >= '0' && *fmt <= '9') { precision = precision * 10 + (*fmt - '0'); fmt++; } } goto check_fmt; } // Length case 'h': { if (fmt[1] == 'h') { size = sizeof(uint8_t); fmt++; } else size = sizeof(uint16_t); fmt++; goto check_fmt; } case 'l': { // There is no difference between `long int` and `long long int`. if (fmt[1] == 'l') fmt++; fmt++; size = sizeof(uint64_t); goto check_fmt; } case 'z': { size = sizeof(size_t); fmt++; goto check_fmt; } // Character case 'c': { char c = (char)va_arg(args, int32_t); const size_t pad = width > 1 ? width - 1 : 0; if (!left_align) print_pad(callback, ctx, ' ', pad); callback(ctx, &c, sizeof(c)); if (left_align) print_pad(callback, ctx, ' ', pad); written += 1 + pad; fmt++; break; } // String of characters case 's': { const char* str = va_arg(args, const char*); if (!str) str = "(null)"; size_t len = 0; if (has_precision) len = strnlen(str, precision); else len = strlen(str); const size_t pad = width > len ? width - len : 0; if (!left_align) print_pad(callback, ctx, ' ', pad); callback(ctx, str, len); if (left_align) print_pad(callback, ctx, ' ', pad); written += len + pad; fmt++; break; } // Signed decimal integer case 'i': case 'd': { switch (size) { case sizeof(int8_t): ltoa((int8_t)va_arg(args, int32_t), number, 10); break; case sizeof(int16_t): ltoa((int16_t)va_arg(args, int32_t), number, 10); break; case sizeof(int32_t): ltoa((int32_t)va_arg(args, int32_t), number, 10); break; default: ltoa(va_arg(args, int64_t), number, 10); break; } goto print_num; } // Unsigned decimal integer case 'u': { switch (size) { case sizeof(uint8_t): lutoa((uint8_t)va_arg(args, uint32_t), number, 10); break; case sizeof(uint16_t): lutoa((uint16_t)va_arg(args, uint32_t), number, 10); break; case sizeof(uint32_t): lutoa(va_arg(args, uint32_t), number, 10); break; case sizeof(uint64_t): lutoa(va_arg(args, uint64_t), number, 10); break; } goto print_num; } // Unsigned hexadecimal integer case 'X': case 'x': { switch (size) { case sizeof(uint8_t): lutoa((uint8_t)va_arg(args, uint32_t), number, 16); break; case sizeof(uint16_t): lutoa((uint16_t)va_arg(args, uint32_t), number, 16); break; case sizeof(uint32_t): lutoa(va_arg(args, uint32_t), number, 16); break; case sizeof(uint64_t): lutoa(va_arg(args, uint64_t), number, 16); break; } print_num: const char* digits = number; char sign = 0; if (digits[0] == '-') { sign = '-'; digits++; } else if (force_sign) { sign = '+'; } else if (blank_sign) { sign = ' '; } const size_t len = strlen(digits); size_t zeros = (has_precision && precision > len) ? precision - len : 0; const size_t total = (sign ? 1 : 0) + (write_prefix ? 2 : 0) + zeros + len; size_t pad = width > total ? width - total : 0; if (pad_zero && !left_align && !has_precision) { zeros += pad; pad = 0; } if (!left_align) print_pad(callback, ctx, ' ', pad); if (sign) callback(ctx, &sign, 1); if (write_prefix) callback(ctx, "0x", 2); print_pad(callback, ctx, '0', zeros); callback(ctx, digits, len); if (left_align) print_pad(callback, ctx, ' ', pad); written += width > total ? width : total; fmt++; break; } // Pointer address case 'p': { const size_t num = va_arg(args, size_t); const size_t buf_size = sizeof(size_t) * 2 + 1; char str[buf_size]; lutoa(num, number, 0x10); const size_t len = strlen(number); // Get the length of the final number. for (size_t i = 0; i < buf_size; i++) str[i] = '0'; size_t offset = sizeof(str) - len - 1; callback(ctx, str, offset); callback(ctx, number, len); fmt++; written += len; break; } // The number of characters written so far is stored in the pointed location. case 'n': { int32_t* const ptr = va_arg(args, int32_t*); *ptr = written; fmt++; break; } // No format, just normal text. default: { size_t len = fmt - format_begun_at; if (*fmt) len++; callback(ctx, format_begun_at, len); written += len; fmt = format_begun_at + len; break; } } } } static void printf_console(void*, const char* msg, size_t len) { console_write(msg, len); } void kprintf(const char* message, ...) { va_list args; va_start(args, message); do_print(printf_console, nullptr, message, args); va_end(args); } void kvprintf(const char* message, va_list args) { do_print(printf_console, nullptr, message, args); }