/* See the end of this file for copyright and license terms. */ #include /* from clang */ #include #include #include #include #include #include #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 . * * 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 ; 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. */