231 lines
No EOL
5.1 KiB
C
231 lines
No EOL
5.1 KiB
C
#include <knob/block.h>
|
|
#include <knob/panic.h>
|
|
#include <knob/heap.h>
|
|
|
|
#include <pland/syscall.h>
|
|
#include <pland/pcrt.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#define FORMAT_BUF_INIT_SIZE 200
|
|
#define FORMAT_BUF_CHUNK_SIZE 50
|
|
|
|
#define BAD_SPEC "%%UNKNOWN FORMAT SPEC%%"
|
|
#define BAD_SPEC_LEN 23
|
|
|
|
static char *buf;
|
|
static uint32_t buf_s;
|
|
static char *buf_i;
|
|
|
|
static const char *const hextab = "0123456789abcdef";
|
|
|
|
static void ensure(uint32_t extra) {
|
|
const uint32_t total_len = buf_i - buf + extra;
|
|
if (total_len < buf_s)
|
|
return;
|
|
|
|
buf_s = (total_len / FORMAT_BUF_CHUNK_SIZE + 1) * FORMAT_BUF_CHUNK_SIZE;
|
|
|
|
char *const new_buf = get_block(buf_s);
|
|
if (!new_buf)
|
|
PANIC("out of memory in knob format");
|
|
blockcpy(new_buf, buf, buf_i - buf);
|
|
free_block(buf);
|
|
buf_i += new_buf - buf;
|
|
buf = new_buf;
|
|
}
|
|
|
|
struct format_spec {
|
|
uint32_t len;
|
|
enum {
|
|
UNKNOWN,
|
|
CHAR,
|
|
STRING,
|
|
UNSIGNED_DECIMAL,
|
|
HEXADECIMAL
|
|
//TODO: signed decimal
|
|
} kind;
|
|
};
|
|
|
|
static const char *get_format(const char *from, struct format_spec *format_out) {
|
|
if (*from == 'n') {
|
|
++from;
|
|
format_out->len = -1;
|
|
}
|
|
else {
|
|
uint32_t len = 0;
|
|
while ((*from >= '0') && (*from <= '9'))
|
|
len = len * 10 + *(from++) - '0';
|
|
format_out->len = len;
|
|
}
|
|
|
|
switch (*from) {
|
|
case 'c':
|
|
format_out->kind = CHAR;
|
|
break;
|
|
case 's':
|
|
format_out->kind = STRING;
|
|
break;
|
|
case 'u':
|
|
format_out->kind = UNSIGNED_DECIMAL;
|
|
break;
|
|
case 'h':
|
|
case 'x':
|
|
format_out->kind = HEXADECIMAL;
|
|
break;
|
|
default:
|
|
format_out->kind = UNKNOWN;
|
|
break;
|
|
}
|
|
|
|
return from + 1;
|
|
}
|
|
|
|
//allocates new memory
|
|
char *format_v(const char *fmt, va_list args) {
|
|
buf = get_block(FORMAT_BUF_INIT_SIZE);
|
|
if (!buf)
|
|
PANIC("out of memory in knob format");
|
|
buf_s = FORMAT_BUF_INIT_SIZE;
|
|
buf_i = buf;
|
|
|
|
while (*fmt) {
|
|
if (*fmt != '%') {
|
|
ensure(1);
|
|
*(buf_i++) = *(fmt++);
|
|
}
|
|
else if (fmt[1] == '%') {
|
|
ensure(1);
|
|
*(buf_i++) = '%';
|
|
fmt += 2;
|
|
}
|
|
else {
|
|
struct format_spec form;
|
|
fmt = get_format(fmt + 1, &form);
|
|
if (form.len == -1)
|
|
//should passing zero still have the special meaning?
|
|
form.len = va_arg(args, uint32_t);
|
|
switch (form.kind) {
|
|
case UNKNOWN:
|
|
ensure(BAD_SPEC_LEN);
|
|
blockcpy(buf_i, BAD_SPEC, BAD_SPEC_LEN);
|
|
buf_i += BAD_SPEC_LEN;
|
|
continue;
|
|
|
|
uint32_t ch;
|
|
case CHAR:
|
|
ch = va_arg(args, uint32_t);
|
|
ensure(1);
|
|
*(buf_i++) = (char)ch;
|
|
continue;
|
|
|
|
const char *str;
|
|
case STRING:
|
|
str = va_arg(args, const char *);
|
|
if (!form.len)
|
|
form.len = strlen(str);
|
|
ensure(form.len);
|
|
blockcpy(buf_i, str, form.len);
|
|
buf_i += form.len;
|
|
continue;
|
|
|
|
uint32_t k;
|
|
case UNSIGNED_DECIMAL:
|
|
k = va_arg(args, uint32_t);
|
|
if (!form.len) {
|
|
uint32_t n = 10;
|
|
++form.len;
|
|
while (k >= n) {
|
|
++form.len;
|
|
n *= 10;
|
|
}
|
|
}
|
|
ensure(form.len);
|
|
const uint32_t len_backup = form.len;
|
|
while (form.len--) {
|
|
buf_i[form.len] = (k % 10) + '0';
|
|
k /= 10;
|
|
}
|
|
buf_i += len_backup;
|
|
continue;
|
|
|
|
case HEXADECIMAL:
|
|
k = va_arg(args, uint32_t);
|
|
if (!form.len)
|
|
form.len = 8;
|
|
ensure(form.len);
|
|
const uint32_t hlen_backup = form.len;
|
|
while (form.len--) {
|
|
buf_i[form.len] = hextab[k % 16];
|
|
k >>= 4;
|
|
}
|
|
buf_i += hlen_backup;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
*buf_i = '\0';
|
|
return buf;
|
|
}
|
|
|
|
//allocates new memory
|
|
char *format(const char *fmt, ...) {
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
char *const res = format_v(fmt, args);
|
|
va_end(args);
|
|
return res;
|
|
}
|
|
|
|
void syslogf_v(const char *fmt, va_list args) {
|
|
char *const msg = format_v(fmt, args);
|
|
_system_log(msg);
|
|
free_block(msg);
|
|
}
|
|
|
|
void syslogf(const char *fmt, ...) {
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
syslogf_v(fmt, args);
|
|
va_end(args);
|
|
}
|
|
|
|
//reads a unsigned decimal terminated by either null or whitespace
|
|
//returns length of string plus length of whitespace
|
|
//returns 0 on failure
|
|
uint32_t try_swtou(const char *from, uint32_t *i_out) {
|
|
const char *const old_from = from;
|
|
uint32_t v = 0;
|
|
while (*from && (*from != '\n') && (*from != ' ')) {
|
|
if ((*from < '0') || (*from > '9'))
|
|
return 0;
|
|
v = v * 10 + *(from++) - '0';
|
|
}
|
|
*i_out = v;
|
|
while ((*from == '\n') || (*from == ' '))
|
|
++from;
|
|
return from - old_from;
|
|
}
|
|
|
|
//reads a hexadecimal terminated by either null or whitespace
|
|
//returns length of string plus length of whitespace
|
|
//returns 0 on failure
|
|
uint32_t try_swtoh(const char *from, uint32_t *i_out) {
|
|
const char *const old_from = from;
|
|
uint32_t v = 0;
|
|
while (*from && (*from != '\n') && (*from != ' ')) {
|
|
if ((*from >= '0') && (*from <= '9'))
|
|
v = v * 16 + *(from++) - '0';
|
|
else if ((*from >= 'a') && (*from <= 'f'))
|
|
v = v * 16 + *(from++) - 'a' + 10;
|
|
else if ((*from >= 'A') && (*from <= 'F'))
|
|
v = v * 16 + *(from++) - 'A' + 10;
|
|
else
|
|
return 0;
|
|
}
|
|
*i_out = v;
|
|
while ((*from == '\n') || (*from == ' '))
|
|
++from;
|
|
return from - old_from;
|
|
} |