kernel/sys/snprintf.c
2020-02-03 12:39:53 +02:00

262 lines
5.8 KiB
C

// TODO: merge snprintf and debug.c
#include "sys/snprintf.h"
#include "sys/string.h"
union printfv {
int value_int;
unsigned int value_uint;
long value_long;
uint32_t value_uint32;
uint64_t value_uint64;
uintptr_t value_ptr;
const char *value_str;
};
static const char *s_print_xs_set0 = "0123456789abcdef";
static const char *s_print_xs_set1 = "0123456789ABCDEF";
static int vsnprintf_ds(int64_t x, char *res, int s, int sz) {
if (!x) {
res[0] = '0';
res[1] = 0;
return 1;
}
int c;
uint64_t v;
if (sz) {
if (s && x < 0) {
v = (uint64_t) -x;
} else {
s = 0;
v = (uint64_t) x;
}
} else {
if (s && ((int32_t) x) < 0) {
v = (uint64_t) -((int32_t) x);
} else {
s = 0;
v = (uint64_t) x;
}
}
c = 0;
while (v) {
res[c++] = '0' + v % 10;
v /= 10;
}
if (s) {
res[c++] = '-';
}
res[c] = 0;
for (int i = 0, j = c - 1; i < j; ++i, --j) {
res[i] ^= res[j];
res[j] ^= res[i];
res[i] ^= res[j];
}
return c;
}
static int vsnprintf_xs(uint64_t v, char *res, const char *set) {
if (!v) {
res[0] = '0';
res[1] = 0;
return 1;
}
int c = 0;
while (v) {
res[c++] = set[v & 0xF];
v >>= 4;
}
res[c] = 0;
for (int i = 0, j = c - 1; i < j; ++i, --j) {
res[i] ^= res[j];
res[j] ^= res[i];
res[i] ^= res[j];
}
return c;
}
int snprintf(char *buf, size_t len, const char *fmt, ...) {
va_list args;
va_start(args, fmt);
int res = vsnprintf(buf, len, fmt, args);
va_end(args);
return res;
}
int vsnprintf(char *buf, size_t len, const char *fmt, va_list args) {
union printfv val;
char cbuf[64];
size_t p = 0;
// padn > 0:
// --------vvvvv
// padn < 0:
// vvvv----------
int padn;
char padc;
int pads;
int clen;
char c;
#define __putc(ch) \
if (p == len - 1) { \
goto __end; \
} else { \
buf[p++] = ch; \
}
#define __puts(s, l) \
if (padn >= 0) { \
if ((int) l < padn) { \
size_t padd = padn - l; \
if (p + padd < len + 1) { \
memset(buf + p, padc, padd); \
p += padd; \
} else { \
memset(buf + p, padc, len - 1 - p); \
p = len - 1; \
goto __end; \
} \
} \
if (p + l < len + 1) { \
strncpy(buf + p, s, l); \
p += l; \
} else { \
strncpy(buf + p, s, len - 1 - p); \
p = len - 1; \
goto __end; \
} \
} else { \
padn = -padn; \
if (p + l < len + 1) { \
strncpy(buf + p, s, l); \
p += l; \
} else { \
strncpy(buf + p, s, len - 1 - p); \
p = len - 1; \
goto __end; \
} \
if ((int) l < padn) { \
size_t padd = padn - l; \
if (p + padd < len + 1) { \
memset(buf + p, padc, padd); \
p += padd; \
} else { \
memset(buf + p, padc, len - 1 - p); \
p = len - 1; \
goto __end; \
} \
} \
}
while ((c = *fmt)) {
switch (c) {
case '%':
c = *++fmt;
padn = 0;
padc = ' ';
pads = 1;
if (c == '0') {
padc = c;
c = *++fmt;
}
if (c == '-') {
pads = -1;
c = *++fmt;
}
while (c >= '0' && c <= '9') {
padn *= 10;
padn += c - '0';
c = *++fmt;
}
padn *= pads;
switch (c) {
case 'l':
// Not supported
return -1;
case 's':
if ((val.value_str = va_arg(args, const char *))) {
__puts(val.value_str, strlen(val.value_str));
} else {
__puts("(null)", 6);
}
break;
case 'p':
val.value_ptr = va_arg(args, uintptr_t);
__putc('0');
__putc('x');
padn = sizeof(uintptr_t) * 2;
padc = '0';
clen = vsnprintf_xs(val.value_ptr, cbuf, s_print_xs_set0);
__puts(cbuf, clen);
break;
case 'i':
case 'd':
val.value_int = va_arg(args, int);
clen = vsnprintf_ds(val.value_int, cbuf, 1, 1);
__puts(cbuf, clen);
break;
case 'u':
val.value_uint = va_arg(args, unsigned int);
clen = vsnprintf_ds(val.value_uint, cbuf, 0, 1);
__puts(cbuf, clen);
break;
case 'x':
val.value_uint32 = va_arg(args, uint32_t);
clen = vsnprintf_xs(val.value_uint32, cbuf, s_print_xs_set0);
__puts(cbuf, clen);
break;
case 'X':
val.value_uint32 = va_arg(args, uint32_t);
clen = vsnprintf_xs(val.value_uint32, cbuf, s_print_xs_set1);
__puts(cbuf, clen);
break;
case 'c':
val.value_int = va_arg(args, int);
__putc(val.value_int);
break;
default:
__putc('%');
__putc(c);
break;
}
break;
default:
__putc(c);
break;
}
++fmt;
}
#undef __puts
#undef __putc
__end:
buf[p] = 0;
return p;
}