Turns out you can't pass a va_list to subroutines as per the C standard, even though it worked perfectly fine on ARM. Well then, the entire kprintf thing needs to be refactored anyway at some point in the future, so that more formatting options are supported.
208 lines
4.4 KiB
C
208 lines
4.4 KiB
C
/* See the end of this file for copyright and license terms. */
|
|
|
|
#include <stdarg.h> /* from clang */
|
|
#include <string.h>
|
|
|
|
#include <gay/cdefs.h>
|
|
#include <gay/config.h>
|
|
#include <gay/errno.h>
|
|
#include <gay/kprintf.h>
|
|
#include <gay/types.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 & 0xf];
|
|
ptr >>= 4;
|
|
} while (pos != &str[1]);
|
|
|
|
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;
|
|
}
|
|
|
|
int kvprintf(const char *fmt, va_list args)
|
|
{
|
|
ssize_t ret = 0;
|
|
const char *tmp = fmt;
|
|
union {
|
|
char c;
|
|
int d;
|
|
uintptr_t p;
|
|
char *s;
|
|
unsigned int u;
|
|
} val;
|
|
|
|
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 = 0;
|
|
switch (*tmp) {
|
|
case '%': /* literal percent sign */
|
|
fmt_ret = renderer->write(renderer, tmp, sizeof(*tmp));
|
|
break;
|
|
|
|
case 'c': /* char */
|
|
val.c = va_arg(args, int); /* POSIX wants an int */
|
|
fmt_ret = renderer->write(renderer, &val.c, sizeof(val.c));
|
|
break;
|
|
|
|
case 'd': /* int */
|
|
case 'i':
|
|
fmt_ret = fmt_handle_int(va_arg(args, int));
|
|
break;
|
|
|
|
case 'p': /* ptr */
|
|
fmt_ret = fmt_handle_ptr(va_arg(args, uintptr_t));
|
|
break;
|
|
|
|
case 's': /* string */
|
|
val.s = va_arg(args, char *);
|
|
fmt_ret = renderer->write(renderer, val.s, strlen(val.s));
|
|
break;
|
|
|
|
case 'u': /* unsigned int */
|
|
fmt_ret = fmt_handle_uint(va_arg(args, unsigned int));
|
|
break;
|
|
}
|
|
tmp++;
|
|
|
|
/*
|
|
* 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.
|
|
*/
|