cc8351bf8d
See previous commit Signed-off-by: kaguya <vpshinomiya@protonmail.com>
374 lines
9.3 KiB
C
374 lines
9.3 KiB
C
#include <stdarg.h>
|
|
#include <stdbool.h>
|
|
#include "stdio.h"
|
|
#include "debug.h"
|
|
#include "video/render.h"
|
|
#include "arch/x86_64/e9.h"
|
|
#include "mp/spinlock.h"
|
|
#include <stddef.h>
|
|
#include <limits.h>
|
|
|
|
static const uint32_t g_LogSeverityColors[] =
|
|
{
|
|
[LVL_DEBUG] = 0xAAAAAA, // light gray
|
|
[LVL_INFO] = 0xFFFFFF, // white
|
|
[LVL_WARN] = 0xFFFF00, // yellow
|
|
[LVL_ERROR] = 0xFF0000, // red
|
|
[LVL_CRITICAL] = 0xFFFFFF, // white (can do red background separately if you want)
|
|
};
|
|
|
|
static spinlock_t s_printf_lock = SPINLOCK_INIT;
|
|
|
|
typedef struct {
|
|
char* buf;
|
|
size_t size;
|
|
size_t pos;
|
|
} snprintf_ctx_t;
|
|
|
|
static void buf_putc(snprintf_ctx_t* ctx, char c)
|
|
{
|
|
if (ctx->pos + 1 < ctx->size) {
|
|
ctx->buf[ctx->pos] = c;
|
|
}
|
|
ctx->pos++;
|
|
}
|
|
|
|
static void buf_puts(snprintf_ctx_t* ctx, const char* str)
|
|
{
|
|
while (*str) {
|
|
buf_putc(ctx, *str++);
|
|
}
|
|
}
|
|
|
|
void fputc(char c)
|
|
{
|
|
putchar(c);
|
|
e9_putc(c);
|
|
}
|
|
|
|
void fputs(const char* str)
|
|
{
|
|
const char* s = str;
|
|
while (*s) fputc(*s++);
|
|
}
|
|
|
|
void fputs_colored(const char* str, uint32_t color) {
|
|
while (*str) putchar_colored(*str++, color);
|
|
while (*str) fputc(*str++);
|
|
}
|
|
|
|
const char g_HexChars[] = "0123456789abcdef";
|
|
|
|
static void buf_print_unsigned(snprintf_ctx_t* ctx, uint64_t number, int radix, int width, int zero_pad)
|
|
{
|
|
char buffer[32];
|
|
int pos = 0;
|
|
|
|
do {
|
|
buffer[pos++] = g_HexChars[number % radix];
|
|
number /= radix;
|
|
} while (number > 0);
|
|
|
|
// Apply zero-padding / width
|
|
if (zero_pad) {
|
|
while (pos < width)
|
|
buffer[pos++] = '0';
|
|
}
|
|
|
|
while (--pos >= 0)
|
|
buf_putc(ctx, buffer[pos]);
|
|
}
|
|
|
|
static void buf_print_signed(snprintf_ctx_t* ctx, int64_t number, int radix, int width, int zero_pad)
|
|
{
|
|
if (number < 0) {
|
|
buf_putc(ctx, '-');
|
|
buf_print_unsigned(ctx, (uint64_t)(-number), radix, width, zero_pad);
|
|
} else {
|
|
buf_print_unsigned(ctx, (uint64_t)number, radix, width, zero_pad);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
int snprintf(char* buf, size_t size, const char* fmt, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
int ret = vsnprintf(buf, size, fmt, args);
|
|
va_end(args);
|
|
return ret;
|
|
}
|
|
|
|
static void fprintf_unsigned(uint64_t number, int radix, int width, int zero_pad)
|
|
{
|
|
char buffer[32];
|
|
int pos = 0;
|
|
|
|
do {
|
|
buffer[pos++] = g_HexChars[number % radix];
|
|
number /= radix;
|
|
} while (number > 0);
|
|
|
|
if (zero_pad) {
|
|
while (pos < width)
|
|
buffer[pos++] = '0';
|
|
}
|
|
|
|
while (--pos >= 0)
|
|
fputc(buffer[pos]);
|
|
}
|
|
|
|
static void fprintf_signed(int64_t number, int radix, int width, int zero_pad)
|
|
{
|
|
if (number < 0) {
|
|
fputc('-');
|
|
fprintf_unsigned((uint64_t)(-number), radix, width, zero_pad);
|
|
} else {
|
|
fprintf_unsigned((uint64_t)number, radix, width, zero_pad);
|
|
}
|
|
}
|
|
|
|
|
|
void vfprintf(const char* fmt, va_list args)
|
|
{
|
|
while (*fmt) {
|
|
if (*fmt != '%') {
|
|
fputc(*fmt++);
|
|
continue;
|
|
}
|
|
fmt++; // skip %
|
|
|
|
/* flags */
|
|
int zero_pad = 0;
|
|
if (*fmt == '0') {
|
|
zero_pad = 1;
|
|
fmt++;
|
|
}
|
|
|
|
/* width */
|
|
int width = 0;
|
|
while (*fmt >= '0' && *fmt <= '9') {
|
|
width = width * 10 + (*fmt++ - '0');
|
|
}
|
|
|
|
/* precision */
|
|
int precision = -1;
|
|
if (*fmt == '.') {
|
|
fmt++;
|
|
precision = 0;
|
|
while (*fmt >= '0' && *fmt <= '9') {
|
|
precision = precision * 10 + (*fmt++ - '0');
|
|
}
|
|
}
|
|
|
|
/* length modifier */
|
|
int is_long_long = 0;
|
|
if (*fmt == 'l') {
|
|
fmt++;
|
|
if (*fmt == 'l') {
|
|
is_long_long = 1;
|
|
fmt++;
|
|
}
|
|
} else if (*fmt == 'z') { // %zu, %zx
|
|
is_long_long = 1;
|
|
fmt++;
|
|
} else if (*fmt == 'h') {
|
|
fmt++; // ignore h/hh
|
|
}
|
|
|
|
/* specifier */
|
|
switch (*fmt++) {
|
|
case 'c':
|
|
fputc((char)va_arg(args, int));
|
|
break;
|
|
|
|
case 's': {
|
|
const char* s = va_arg(args, const char*);
|
|
if (!s) s = "(null)";
|
|
if (precision >= 0) {
|
|
while (*s && precision--) fputc(*s++);
|
|
} else {
|
|
fputs(s);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 'd':
|
|
case 'i':
|
|
if (is_long_long)
|
|
fprintf_signed(va_arg(args, long long), 10, width, zero_pad);
|
|
else
|
|
fprintf_signed(va_arg(args, long), 10, width, zero_pad);
|
|
break;
|
|
|
|
case 'u':
|
|
if (is_long_long)
|
|
fprintf_unsigned(va_arg(args, unsigned long long), 10, width, zero_pad);
|
|
else
|
|
fprintf_unsigned(va_arg(args, unsigned long), 10, width, zero_pad);
|
|
break;
|
|
|
|
case 'x':
|
|
case 'X':
|
|
if (is_long_long)
|
|
fprintf_unsigned(va_arg(args, unsigned long long), 16, width, zero_pad);
|
|
else
|
|
fprintf_unsigned(va_arg(args, unsigned long), 16, width, zero_pad);
|
|
break;
|
|
|
|
case 'p':
|
|
fputs("0x");
|
|
fprintf_unsigned(va_arg(args, uint64_t), 16, 16, 1); // always full 64-bit
|
|
break;
|
|
|
|
case 'o':
|
|
if (is_long_long)
|
|
fprintf_unsigned(va_arg(args, unsigned long long), 8, width, zero_pad);
|
|
else
|
|
fprintf_unsigned(va_arg(args, unsigned long), 8, width, zero_pad);
|
|
break;
|
|
|
|
case '%':
|
|
fputc('%');
|
|
break;
|
|
|
|
default:
|
|
fputc('%');
|
|
fputc(*(fmt - 1));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
int vsnprintf(char* buf, size_t size, const char* fmt, va_list args)
|
|
{
|
|
snprintf_ctx_t ctx = { .buf = buf, .size = size, .pos = 0 };
|
|
|
|
while (*fmt) {
|
|
if (*fmt != '%') {
|
|
buf_putc(&ctx, *fmt++);
|
|
continue;
|
|
}
|
|
fmt++;
|
|
|
|
int zero_pad = 0;
|
|
if (*fmt == '0') { zero_pad = 1; fmt++; }
|
|
|
|
int width = 0;
|
|
while (*fmt >= '0' && *fmt <= '9') {
|
|
width = width * 10 + (*fmt++ - '0');
|
|
}
|
|
|
|
int precision = -1;
|
|
if (*fmt == '.') {
|
|
fmt++;
|
|
precision = 0;
|
|
while (*fmt >= '0' && *fmt <= '9') {
|
|
precision = precision * 10 + (*fmt++ - '0');
|
|
}
|
|
}
|
|
|
|
int is_long_long = 0;
|
|
if (*fmt == 'l') {
|
|
fmt++;
|
|
if (*fmt == 'l') { is_long_long = 1; fmt++; }
|
|
} else if (*fmt == 'z') {
|
|
is_long_long = 1;
|
|
fmt++;
|
|
} else if (*fmt == 'h') {
|
|
fmt++;
|
|
}
|
|
|
|
switch (*fmt++) {
|
|
case 'c':
|
|
buf_putc(&ctx, (char)va_arg(args, int));
|
|
break;
|
|
|
|
case 's': {
|
|
const char* s = va_arg(args, const char*);
|
|
if (!s) s = "(null)";
|
|
int max = (precision >= 0) ? precision : INT_MAX;
|
|
while (*s && max--) buf_putc(&ctx, *s++);
|
|
break;
|
|
}
|
|
|
|
case 'd':
|
|
case 'i':
|
|
if (is_long_long)
|
|
buf_print_signed(&ctx, va_arg(args, long long), 10, width, zero_pad);
|
|
else
|
|
buf_print_signed(&ctx, va_arg(args, long), 10, width, zero_pad);
|
|
break;
|
|
|
|
case 'u':
|
|
if (is_long_long)
|
|
buf_print_unsigned(&ctx, va_arg(args, unsigned long long), 10, width, zero_pad);
|
|
else
|
|
buf_print_unsigned(&ctx, va_arg(args, unsigned long), 10, width, zero_pad);
|
|
break;
|
|
|
|
case 'x':
|
|
case 'X':
|
|
if (is_long_long)
|
|
buf_print_unsigned(&ctx, va_arg(args, unsigned long long), 16, width, zero_pad);
|
|
else
|
|
buf_print_unsigned(&ctx, va_arg(args, unsigned long), 16, width, zero_pad);
|
|
break;
|
|
|
|
case 'p':
|
|
buf_puts(&ctx, "0x");
|
|
buf_print_unsigned(&ctx, va_arg(args, uint64_t), 16, 16, 1);
|
|
break;
|
|
|
|
case 'o':
|
|
if (is_long_long)
|
|
buf_print_unsigned(&ctx, va_arg(args, unsigned long long), 8, width, zero_pad);
|
|
else
|
|
buf_print_unsigned(&ctx, va_arg(args, unsigned long), 8, width, zero_pad);
|
|
break;
|
|
|
|
case '%':
|
|
buf_putc(&ctx, '%');
|
|
break;
|
|
|
|
default:
|
|
buf_putc(&ctx, '%');
|
|
buf_putc(&ctx, *(fmt - 1));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ctx.size > 0) {
|
|
if (ctx.pos < ctx.size)
|
|
ctx.buf[ctx.pos] = '\0';
|
|
else
|
|
ctx.buf[ctx.size - 1] = '\0';
|
|
}
|
|
|
|
return (int)ctx.pos;
|
|
}
|
|
|
|
void printf(const char* fmt, ...)
|
|
{
|
|
uint64_t flags;
|
|
spinlock_acquire_irqsave(&s_printf_lock, &flags);
|
|
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
|
|
vfprintf(fmt, args);
|
|
|
|
va_end(args);
|
|
|
|
spinlock_release_irqrestore(&s_printf_lock, flags);
|
|
}
|
|
|
|
void fprintf(const char* fmt, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
vfprintf(fmt, args);
|
|
va_end(args);
|
|
}
|