Files
KirkOS/src/stdio.c
T
2026-04-15 13:33:18 -04:00

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