diff options
author | Anton Kling <anton@kling.gg> | 2024-12-09 23:25:02 +0100 |
---|---|---|
committer | Anton Kling <anton@kling.gg> | 2024-12-09 23:27:48 +0100 |
commit | 4bd8a81cca44285402af2a5e26db267c44abe4f4 (patch) | |
tree | 5ca227dacc0c863c031789ae51469400b221dfb8 /kernel/libc/stdlib | |
parent | 3bb66753076f4037883b7c71ce2fb8e78f8b1194 (diff) |
kernel: Add a way to parse numbers from C strings
After making this change I am now actually doubting if using
C strings is a good idea and maybe it should just always just the
string view library that userland makes use of.
But old code and the upcoming commits rely upon this so it is a change my
future less lazy self will do.
Diffstat (limited to 'kernel/libc/stdlib')
-rw-r--r-- | kernel/libc/stdlib/parsenum.c | 83 |
1 files changed, 83 insertions, 0 deletions
diff --git a/kernel/libc/stdlib/parsenum.c b/kernel/libc/stdlib/parsenum.c new file mode 100644 index 0000000..3990c7b --- /dev/null +++ b/kernel/libc/stdlib/parsenum.c @@ -0,0 +1,83 @@ +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <stdlib.h> + +#define MAX_64 0xFFFFFFFFFFFFFFFF + +extern int errno; +int get_value(char c, long base) { + int r; + int l = tolower(c); + if (l >= '0' && l <= '9') { + r = l - '0'; + } else if (l >= 'a' && l <= 'z') { + r = (l - 'a') + 10; + } else { + return -1; + } + if (r >= base) { + return -1; + } + return r; +} + +u64 parse_u64(const char *restrict str, char **restrict endptr, int base, + int *err) { + if (err) { + *err = 0; + } + u64 ret_value = 0; + if (endptr) { + *endptr = (char *)str; + } + if (!*str) { + return ret_value; + } + + if (0 == base) { + char prefix = *str; + if ('0' == prefix) { + str++; + if ('x' == tolower(*str)) { + str++; + base = 16; + } else { + base = 8; + } + } else { + base = 10; + } + } + + if (2 <= base && 36 >= base) { + for (; *str; str++) { + int val = get_value(*str, base); + if (-1 == val) { + break; + } + if (-1 == val) { + break; + } + ret_value *= base; + if (ret_value > MAX_64 - val) { + if (err) { + *err = 1; + } + return 0; + } + + ret_value += val; + } + } else { + if (err) { + *err = 1; + } + return 0; + } + if (endptr) { + *endptr = (char *)str; + } + return ret_value; +} |