summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--kc/api/lib.h4
-rw-r--r--kc/core/Makefile2
-rw-r--r--kc/core/kprint.c91
-rw-r--r--lib/strtoull.c148
4 files changed, 239 insertions, 6 deletions
diff --git a/kc/api/lib.h b/kc/api/lib.h
index 3bf0eca..c28e17c 100644
--- a/kc/api/lib.h
+++ b/kc/api/lib.h
@@ -7,4 +7,8 @@ void *memmove(void *dest, const void *src, size_t size);
void *memset(void *dest, int val, size_t size);
int memcmp(const void *str1, const void *str2, size_t count);
size_t strlen(const char *s);
+unsigned long long strtoull(
+ const char *restrict begin,
+ char **restrict end,
+ int base);
diff --git a/kc/core/Makefile b/kc/core/Makefile
index 9b8983a..2c7303e 100644
--- a/kc/core/Makefile
+++ b/kc/core/Makefile
@@ -14,7 +14,7 @@ GOBJS := entry_x86_64.o reloc_x86_64.o kprint.o serial.o port.o mmu.o \
kc_main.o memory.o memset.o memcpy.o memmove.o memcmp.o cpu.o \
vm_tree.o exceptions.o panic.o msr.o task.o cpu_task.o irq.o \
kcc_memory.o string.o pic8259.o pic8259_isr.o pit8253.o \
- page_early.o page_stack.o
+ page_early.o page_stack.o strtoull.o
IOBJS :=
# __attribute__((interrupt)) requires -mgeneral-regs-only
diff --git a/kc/core/kprint.c b/kc/core/kprint.c
index 762f99f..4acbddd 100644
--- a/kc/core/kprint.c
+++ b/kc/core/kprint.c
@@ -82,7 +82,6 @@ struct specifier
static struct specifier parse_specifier(const char *format, va_list *arguments)
{
- (void)arguments;
struct specifier result =
{
INVALID_PRINT,
@@ -150,6 +149,76 @@ static struct specifier parse_specifier(const char *format, va_list *arguments)
* b. if is numeric character, parse field precision via strtoul, add
* length of numeric string to begin.
*/
+ bool field_width_parsed = false;
+ bool field_precision_parsed = false;
+
+ while (!field_width_parsed || !field_precision_parsed)
+ {
+ switch (*begin)
+ {
+ case '.':
+ if (!field_width_parsed)
+ {
+ result.field_width = 0;
+ field_width_parsed = true;
+ begin++;
+ }
+ else
+ {
+ // the specifier is invalid!
+ result.type = INVALID_PRINT;
+ }
+ break;
+ case '*':
+ {
+ int w = *va_arg(*arguments, int *);
+ if (!field_width_parsed)
+ {
+ result.field_width = w;
+ field_width_parsed = true;
+ }
+ else
+ {
+ result.field_precision = w;
+ field_precision_parsed = true;
+ }
+ begin++;
+ }
+ break;
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '0':
+ {
+ char *next;
+ unsigned long long w = strtoull(begin, &next, 10);
+ if (!field_width_parsed)
+ {
+ result.field_width = (int)w;
+ field_width_parsed = true;
+ }
+ else if (!field_precision_parsed)
+ {
+ result.field_precision = (int)w;
+ field_precision_parsed = true;
+ }
+ if (next > begin)
+ {
+ begin = next;
+ }
+ }
+ break;
+ default:
+ field_width_parsed = true;
+ field_precision_parsed = true;
+ }
+ }
// step 3: check for type width arguments
switch (*begin)
@@ -245,7 +314,7 @@ static struct specifier parse_specifier(const char *format, va_list *arguments)
// TODO: use UINTPTR_T_WIDTH here?
// pointer type overrides all flags
// i can do what i want it says "implementation-defined" in the spec
- result.field_precision = 8;
+ result.field_precision = 16;
result.type = INTEGER_PRINT;
result.integer_base = HEX_BASE;
result.integer_width = LONG_WIDTH;
@@ -277,8 +346,9 @@ static struct specifier parse_specifier(const char *format, va_list *arguments)
static char *convert_integer(
uint64_t value,
- unsigned base,
- char *buffer,
+ unsigned base,
+ int zpadding,
+ char *buffer,
size_t bufsz)
{
static const char *stringdigits = "0123456789abcdef";
@@ -304,10 +374,16 @@ static char *convert_integer(
do
{
*--result = stringdigits[value % base];
+ zpadding--;
value /= base;
}
while (value > 0 && result >= buffer);
+ while (zpadding-- > 0 && result >= buffer)
+ {
+ *--result = '0';
+ }
+
return result;
}
@@ -361,6 +437,7 @@ static int print_integer(
bool negative = false;
uint64_t value = 0;
int r = 0;
+ unsigned zpad = 0;
switch (spec->integer_width)
{
@@ -429,7 +506,11 @@ static int print_integer(
break;
}
- s = convert_integer(value, spec->integer_base, buffer, sizeof(buffer));
+ if (spec->flags & ZERO_PAD_FLAG)
+ {
+ zpad = spec->field_precision;
+ }
+ s = convert_integer(value, spec->integer_base, zpad, buffer, sizeof(buffer));
if (s)
{
diff --git a/lib/strtoull.c b/lib/strtoull.c
new file mode 100644
index 0000000..bb71c24
--- /dev/null
+++ b/lib/strtoull.c
@@ -0,0 +1,148 @@
+#include <stdbool.h>
+
+static long long valueof(int c, int base)
+{
+ long long result = 0;
+
+ if (base < 2 || base > 36)
+ {
+ // bases less than 2 or greater than 36 are invalid.
+ return -1;
+ }
+
+ // check if value is an ascii numeric character
+ if (c >= '0' && c <= '9')
+ {
+ // simple as
+ result = (long long)(c ^ 0x30);
+ }
+
+ // check if value is an uppercase ASCII alphabetical character
+ else if (
+ (c >= 'A' && c <= 'Z') ||
+ (c >= 'a' && c <= 'z'))
+ {
+ // mask in uppercase bit
+ c |= 0x20;
+ // value is 10 plus the alphabetical order of the character
+ result = (long long)(10 + c - 'a');
+ }
+ else
+ {
+ // character has no valid interpretation under base 36
+ return -1LL;
+ }
+
+ if (result > (base - 1))
+ {
+ // character is not valid under given base
+ return -1LL;
+ }
+
+ return result;
+}
+
+unsigned long long strtoull(
+ const char *restrict begin,
+ char **restrict end,
+ int base)
+{
+ long result = 0;
+ const char *current = begin;
+
+ // check for empty string
+ if (*current == 0)
+ {
+ if (end)
+ {
+ *end = (char *)begin;
+ return result;
+ }
+ }
+
+ // skip initial whitespace
+ bool whitespace = true;
+ while (whitespace)
+ {
+ switch (*current)
+ {
+ case 0x9:
+ case 0xa:
+ case 0xb:
+ case 0xc:
+ case 0xd:
+ case 0x20:
+ current++;
+ break;
+ default:
+ whitespace = false;
+ }
+ }
+
+ bool negative = false;
+ // detect if negative sign is used
+ if (*current == '-')
+ {
+ negative = true;
+ current++;
+ }
+
+ // detect base from input
+ if (base == 0)
+ {
+ // base is non-decimal
+ if (current[0] == 0)
+ {
+ if (current[1] == 'b')
+ {
+ base = 2;
+ current += 2;
+ }
+ else if (current[1] == 'x')
+ {
+ base = 16;
+ current += 2;
+ }
+ else
+ {
+ base = 8;
+ current++;
+ }
+ }
+ // base is decimal otherwise
+ else
+ {
+ base = 10;
+ }
+ }
+ // do the actual conversion now
+ while (*current)
+ {
+ long long value = valueof(*current, base);
+
+ // we've reached the end of the conversion
+ if (value == -1)
+ {
+ break;
+ }
+
+ result *= base;
+ result += value;
+ current++;
+ }
+
+ // store the end pointer if needed
+ if (end)
+ {
+ *end = (char *)current;
+ }
+
+ // flip the result if we're meant to
+ if (negative)
+ {
+ result = -result;
+ }
+
+ return result;
+}
+