kernel: add kprintf() api
parent
2bcb3242da
commit
2af5fad52f
@ -0,0 +1,37 @@
|
||||
/* See the end of this file for copyright and license terms. */
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @file Header for the `printf()` family of functions.
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <gay/types.h>
|
||||
#include <gay/toolchain.h>
|
||||
|
||||
__attribute__(( format(printf, 1, 2) ))
|
||||
int kprintf(const char *__restrict fmt, ...);
|
||||
|
||||
int kvprintf(const char *__restrict fmt, va_list args);
|
||||
|
||||
struct kprintf_renderer {
|
||||
ssize_t (*write)(struct kprintf_renderer *renderer, const void *buf, size_t size);
|
||||
ssize_t (*flush)(struct kprintf_renderer *renderer);
|
||||
};
|
||||
int kprintf_set_renderer(struct kprintf_renderer *renderer);
|
||||
|
||||
/*
|
||||
* This file is part of GayBSD.
|
||||
* Copyright (c) 2021 fef <owo@fef.moe>.
|
||||
*
|
||||
* GayBSD is nonviolent software: you may only use, redistribute, and/or
|
||||
* modify it under the terms of the Cooperative Nonviolent Public License
|
||||
* (CNPL) as found in the LICENSE file in the source code root directory
|
||||
* or at <https://git.pixie.town/thufie/npl-builder>; either version 7
|
||||
* of the license, or (at your option) any later version.
|
||||
*
|
||||
* GayBSD comes with ABSOLUTELY NO WARRANTY, to the extent
|
||||
* permitted by applicable law. See the CNPL for details.
|
||||
*/
|
@ -0,0 +1,24 @@
|
||||
# See the end of this file for copyright and license terms.
|
||||
|
||||
add_library(gay_kernel STATIC)
|
||||
target_include_directories(gay_kernel PRIVATE ${GAY_INCLUDE_DIRS})
|
||||
target_compile_definitions(gay_kernel INTERFACE ${GAY_KERNEL_DIFINITIONS})
|
||||
|
||||
target_link_libraries(gay_kernel PRIVATE c)
|
||||
|
||||
target_sources(gay_kernel PRIVATE
|
||||
kprintf.c
|
||||
main.c
|
||||
)
|
||||
|
||||
# This file is part of GayBSD.
|
||||
# Copyright (c) 2021 fef <owo@fef.moe>.
|
||||
#
|
||||
# GayBSD is nonviolent software: you may only use, redistribute, and/or
|
||||
# modify it under the terms of the Cooperative Nonviolent Public License
|
||||
# (CNPL) as found in the LICENSE file in the source code root directory
|
||||
# or at <https://git.pixie.town/thufie/npl-builder>; either version 7
|
||||
# of the license, or (at your option) any later version.
|
||||
#
|
||||
# GayBSD comes with ABSOLUTELY NO WARRANTY, to the extent
|
||||
# permitted by applicable law. See the CNPL for details.
|
@ -0,0 +1,231 @@
|
||||
/* See the end of this file for copyright and license terms. */
|
||||
|
||||
#include <stdarg.h> /* from clang */
|
||||
#include <string.h>
|
||||
|
||||
#include <gay/config.h>
|
||||
#include <gay/errno.h>
|
||||
#include <gay/kprintf.h>
|
||||
#include <gay/types.h>
|
||||
#include <gay/util.h>
|
||||
|
||||
#if __SIZEOF_INT__ == 2
|
||||
/* 5 decimal digits of 65535 (2 ** 16 - 1) */
|
||||
# define PRINTF_UINT_BUFSZ 5
|
||||
#elif __SIZEOF_INT__ == 4
|
||||
/* 10 decimal digits of 4294967295 (2 ** 32 - 1) */
|
||||
# define PRINTF_UINT_BUFSZ 10
|
||||
#elif __SIZEOF_INT__ == 8
|
||||
/* 20 decimal digits of 18446744073709551616 (2 ** 64 - 1) */
|
||||
# define PRINTF_UINT_BUFSZ 20
|
||||
#else
|
||||
# error "Unsupported int size"
|
||||
#endif
|
||||
|
||||
static struct kprintf_renderer *renderer = NULL;
|
||||
|
||||
int kprintf_set_renderer(struct kprintf_renderer *new)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (renderer != NULL)
|
||||
ret = renderer->flush(renderer);
|
||||
|
||||
renderer = new;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fmt_handle_ptr(uintptr_t ptr)
|
||||
{
|
||||
static const char table[] = {
|
||||
'0', '1', '2', '3',
|
||||
'4', '5', '6', '7',
|
||||
'8', '9', 'a', 'b',
|
||||
'c', 'd', 'e', 'f',
|
||||
};
|
||||
|
||||
int ret;
|
||||
/* 2 chars per byte, plus 2 for the "0x" hex prefix */
|
||||
char str[2 * sizeof(uintptr_t) + 2];
|
||||
char *pos = &str[2 * sizeof(uintptr_t) + 1];
|
||||
|
||||
str[0] = '0';
|
||||
str[1] = 'x';
|
||||
|
||||
do {
|
||||
*pos-- = table[ptr % ARRAY_SIZE(table)];
|
||||
ptr >>= 4;
|
||||
} while (pos != &str[2]);
|
||||
|
||||
ret = renderer->write(renderer, str, 2 * sizeof(uintptr_t) + 2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fmt_handle_uint(unsigned int u)
|
||||
{
|
||||
char str[PRINTF_UINT_BUFSZ];
|
||||
char *pos = &str[PRINTF_UINT_BUFSZ - 1];
|
||||
|
||||
do {
|
||||
/* stupid big endian humans, forcing us to do the whole thing in reverse */
|
||||
*pos-- = (char)(u % 10) + '0'; /* convert to ASCII */
|
||||
u /= 10;
|
||||
} while (u != 0);
|
||||
pos++;
|
||||
|
||||
return (int)renderer->write(renderer, pos, PRINTF_UINT_BUFSZ - (pos - str));
|
||||
}
|
||||
|
||||
static inline int fmt_handle_int(int i)
|
||||
{
|
||||
int ret = 0;
|
||||
char minus = '-';
|
||||
|
||||
if (i < 0) {
|
||||
ret = renderer->write(renderer, &minus, sizeof(minus));
|
||||
i = -i;
|
||||
}
|
||||
|
||||
if (ret >= 0) {
|
||||
int uint_ret = fmt_handle_uint((unsigned int)i);
|
||||
if (uint_ret < 0)
|
||||
ret = uint_ret;
|
||||
else if (ret < 0)
|
||||
ret += uint_ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parse a formatting escape sequence, fetch the parameter from the `va_arg`
|
||||
* stack, and print the resulting string to the standard serial console.
|
||||
*
|
||||
* @param pos A reference to the pointer to the fmt sequence beginner (`%`).
|
||||
* This is updated to point to the first character *after* the entire
|
||||
* escape sequence.
|
||||
* @param args A pointer to the varargs list. Will be manipulated.
|
||||
* @returns The amount of bytes written, or a negative POSIX error code.
|
||||
*/
|
||||
static inline int fmt_handle(const char **pos, va_list args)
|
||||
{
|
||||
int ret = 0;
|
||||
union {
|
||||
char c;
|
||||
int d;
|
||||
uintptr_t p;
|
||||
char *s;
|
||||
unsigned int u;
|
||||
} val;
|
||||
|
||||
switch (**pos) {
|
||||
case '%': /* literal percent sign */
|
||||
ret = renderer->write(renderer, *pos, sizeof(**pos));
|
||||
break;
|
||||
|
||||
case 'c': /* char */
|
||||
val.c = va_arg(args, int); /* POSIX says chars are cast to an int */
|
||||
ret = renderer->write(renderer, &val.c, sizeof(val.c));
|
||||
break;
|
||||
|
||||
case 'd': /* int */
|
||||
val.d = va_arg(args, typeof(val.d));
|
||||
ret = fmt_handle_int(val.d);
|
||||
break;
|
||||
|
||||
case 'p': /* ptr */
|
||||
val.p = va_arg(args, typeof(val.p));
|
||||
ret = fmt_handle_ptr(val.p);
|
||||
break;
|
||||
|
||||
case 's': /* string */
|
||||
val.s = va_arg(args, typeof(val.s));
|
||||
ret = (int)strlen(val.s);
|
||||
ret = renderer->write(renderer, val.s, (size_t)ret);
|
||||
break;
|
||||
|
||||
case 'u': /* unsigned int */
|
||||
val.u = va_arg(args, typeof(val.u));
|
||||
ret = fmt_handle_uint(val.u);
|
||||
break;
|
||||
}
|
||||
|
||||
(*pos)++;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int kvprintf(const char *fmt, va_list args)
|
||||
{
|
||||
ssize_t ret = 0;
|
||||
const char *tmp = fmt;
|
||||
|
||||
while (*tmp != '\0') {
|
||||
if (*tmp++ == '%') {
|
||||
/* flush out everything we have so far (minus one char for %) */
|
||||
ssize_t write_ret = renderer->write(
|
||||
renderer,
|
||||
fmt,
|
||||
(size_t)tmp - (size_t)fmt - 1
|
||||
);
|
||||
if (write_ret < 0) {
|
||||
ret = write_ret;
|
||||
break;
|
||||
}
|
||||
ret += write_ret;
|
||||
|
||||
ssize_t fmt_ret = fmt_handle(&tmp, args);
|
||||
/*
|
||||
* act as if the current position were the beginning in
|
||||
* order to make the first step of this if block easier
|
||||
*/
|
||||
fmt = tmp;
|
||||
|
||||
if (fmt_ret < 0)
|
||||
break;
|
||||
ret += fmt_ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (tmp != fmt && ret >= 0) {
|
||||
ssize_t render_ret = renderer->write(renderer, fmt, (size_t)tmp - (size_t)fmt);
|
||||
if (render_ret < 0)
|
||||
ret = render_ret;
|
||||
else
|
||||
ret += render_ret;
|
||||
}
|
||||
|
||||
ssize_t flush_ret = renderer->flush(renderer);
|
||||
if (flush_ret < 0)
|
||||
ret = flush_ret;
|
||||
else
|
||||
ret += flush_ret;
|
||||
|
||||
return (int)ret;
|
||||
}
|
||||
|
||||
int kprintf(const char *fmt, ...)
|
||||
{
|
||||
int ret;
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
ret = kvprintf(fmt, args);
|
||||
va_end(args);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* This file is part of GayBSD.
|
||||
* Copyright (c) 2021 fef <owo@fef.moe>.
|
||||
*
|
||||
* GayBSD is nonviolent software: you may only use, redistribute, and/or
|
||||
* modify it under the terms of the Cooperative Nonviolent Public License
|
||||
* (CNPL) as found in the LICENSE file in the source code root directory
|
||||
* or at <https://git.pixie.town/thufie/npl-builder>; either version 7
|
||||
* of the license, or (at your option) any later version.
|
||||
*
|
||||
* GayBSD comes with ABSOLUTELY NO WARRANTY, to the extent
|
||||
* permitted by applicable law. See the CNPL for details.
|
||||
*/
|
Loading…
Reference in New Issue