This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
portland-os/src/user/knob/format.c
2021-01-24 12:00:11 -05:00

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;
}