diff options
| author | Ada Christine <adachristine18@gmail.com> | 2026-05-26 21:32:27 +0000 |
|---|---|---|
| committer | Ada Christine <adachristine18@gmail.com> | 2026-05-26 21:32:27 +0000 |
| commit | a10aabfcd52f702057316018cd7847ab2bfe4aa1 (patch) | |
| tree | cc4652d2b602798934e8a48b4939cfb20eda1989 /lib/libc | |
| parent | 90c29fdde317c39384011a6ba97077c138f13ad6 (diff) | |
we're bringing kjarna back and not doing the crazy stuff with trying to have task management during efi. that was a bit extra.kjarna
Diffstat (limited to 'lib/libc')
| -rw-r--r-- | lib/libc/memcmp.c | 16 | ||||
| -rw-r--r-- | lib/libc/memcpy.c | 12 | ||||
| -rw-r--r-- | lib/libc/memmove.c | 20 | ||||
| -rw-r--r-- | lib/libc/memset.c | 11 | ||||
| -rw-r--r-- | lib/libc/printf.c | 712 | ||||
| -rw-r--r-- | lib/libc/stdio.c | 30 | ||||
| -rw-r--r-- | lib/libc/string.c | 195 |
7 files changed, 996 insertions, 0 deletions
diff --git a/lib/libc/memcmp.c b/lib/libc/memcmp.c new file mode 100644 index 0000000..2348afe --- /dev/null +++ b/lib/libc/memcmp.c @@ -0,0 +1,16 @@ +/* Public domain. */ +#include <stddef.h> + +int +memcmp (const void *str1, const void *str2, size_t count) +{ + const unsigned char *s1 = str1; + const unsigned char *s2 = str2; + + while (count-- > 0) + { + if (*s1++ != *s2++) + return s1[-1] < s2[-1] ? -1 : 1; + } + return 0; +} diff --git a/lib/libc/memcpy.c b/lib/libc/memcpy.c new file mode 100644 index 0000000..58b1e40 --- /dev/null +++ b/lib/libc/memcpy.c @@ -0,0 +1,12 @@ +/* Public domain. */ +#include <stddef.h> + +void * +memcpy (void *dest, const void *src, size_t len) +{ + char *d = dest; + const char *s = src; + while (len--) + *d++ = *s++; + return dest; +} diff --git a/lib/libc/memmove.c b/lib/libc/memmove.c new file mode 100644 index 0000000..fd06bb6 --- /dev/null +++ b/lib/libc/memmove.c @@ -0,0 +1,20 @@ +/* Public domain. */ +#include <stddef.h> + +void * +memmove (void *dest, const void *src, size_t len) +{ + char *d = dest; + const char *s = src; + if (d < s) + while (len--) + *d++ = *s++; + else + { + const char *lasts = s + (len-1); + char *lastd = d + (len-1); + while (len--) + *lastd-- = *lasts--; + } + return dest; +} diff --git a/lib/libc/memset.c b/lib/libc/memset.c new file mode 100644 index 0000000..3e7025e --- /dev/null +++ b/lib/libc/memset.c @@ -0,0 +1,11 @@ +/* Public domain. */ +#include <stddef.h> + +void * +memset (void *dest, int val, size_t len) +{ + unsigned char *ptr = dest; + while (len-- > 0) + *ptr++ = val; + return dest; +} diff --git a/lib/libc/printf.c b/lib/libc/printf.c new file mode 100644 index 0000000..fa68bcc --- /dev/null +++ b/lib/libc/printf.c @@ -0,0 +1,712 @@ +#include <libc/stdio.h> +#include <stddef.h> +#include <stdint.h> +#include <stdbool.h> + +enum specifier_type +{ + INVALID_PRINT, + CHARACTER_PRINT, + STRING_PRINT, + INTEGER_PRINT, + COUNT_PRINT +}; + +enum specifier_flags +{ + INVALID_FLAGS, + LEFT_JUSTIFY_FLAG = 0x1, + EXPLICIT_SIGN_FLAG = 0x2, + PAD_SIGN_FLAG = 0x4, + SIGNED_TYPE_FLAG = 0x8, + SIGN_FLAG_MASK = 0x6, + ALTERNATE_FORM_FLAG = 0x10, + ZERO_PAD_FLAG = 0x20, +}; + +enum specifier_integer_base +{ + INVALID_BASE, + BIN_BASE = 2, + OCT_BASE = 8, + DEC_BASE = 10, + HEX_BASE = 16 +}; + +enum specifier_integer_width +{ + INVALID_WIDTH, + BYTE_WIDTH = 8, + SHORT_WIDTH = 16, + INT_WIDTH = 32, + LONG_WIDTH = 64 +}; + +struct method +{ + int (*write_character)(struct method *m, char c); + int (*write_wcharacter)(struct method *m, wchar_t c); + int (*write_string)(struct method *m, const char *s); + int (*write_wstring)(struct method *m, const wchar_t *s); + void *output; + int count; +}; + +struct specifier +{ + enum specifier_type type; + enum specifier_flags flags; + enum specifier_integer_width integer_width; + enum specifier_integer_base integer_base; + int field_width; + int field_precision; + size_t length; +}; + +static struct specifier parse_specifier(const char *format, va_list *arguments) +{ + struct specifier result = + { + INVALID_PRINT, + INVALID_FLAGS, + INVALID_WIDTH, + INVALID_BASE, + 0, + 0, + 0 + }; + + // keep a pointer to the beginning of the specifier + const char *begin = format; + + // step 1: scan for flags + bool flags_parsed = false; + + while (!flags_parsed) + { + switch (*begin) + { + case 0: + result.type = INVALID_PRINT; + return result; + case '-': + result.flags |= LEFT_JUSTIFY_FLAG; + begin++; + break; + case '+': + result.flags |= EXPLICIT_SIGN_FLAG; + begin++; + break; + case ' ': + result.flags |= PAD_SIGN_FLAG; + begin++; + break; + case '#': + result.flags |= ALTERNATE_FORM_FLAG; + begin++; + break; + case '0': + result.flags |= ZERO_PAD_FLAG; + begin++; + break; + default: + flags_parsed = true; + } + } + + // step 2: parse field width and precision + // TODO: parse field width and precision + /* procedure: + * 1. check if next character is * or . + * a. if *, field width is the value pointed to by the next item in + * arguments list + * b. if ., field width will be set to 0, proceed to 3. + * 2. if above check is false, check for numeric character + * a. if is numeric character, parse field width via strtoul, add length + * of numeric string to begin. + * b. if is not a numeric character, skip check for width and precision + * entirely + * 3. check if next character is * or numeric + * a. if *, field precision is the value pointed to by the next item + * in arguments list. + * 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) + { + case 0: // unexpected eos + result.type = INVALID_PRINT; + return result; + case 'h': + { + if (begin[0] == begin[1]) + { + result.integer_width = BYTE_WIDTH; + begin += 2; + } + else + { + result.integer_width = SHORT_WIDTH; + begin++; + } + break; + } + case 'l': + { + // TODO: deal with LLP64? idk. + if (begin[0] == begin[1]) + { + begin += 2; + } + else + { + begin++; + } + result.integer_width = LONG_WIDTH; + break; + } + case 'j': + // TODO: use INTMAX_T_WIDTH + result.integer_width = 64; + begin++; + break; + case 'z': + // TODO: use SIZE_T_WIDTH? + result.integer_width = 64; + begin++; + break; + case 't': + // TODO: use PTRDIFF_T_WIDTH? + result.integer_width = 32; + begin++; + break; + default: + // there is no width argument to be found. + result.integer_width = INT_WIDTH; + break; + } + + // step 4: parse field type + switch (*begin) + { + // unexpected EOS + case 0: + result.type = INVALID_PRINT; + return result; + case 'c': + result.type = CHARACTER_PRINT; + break; + case 's': + result.type = STRING_PRINT; + break; + case 'd': + case 'i': + result.type = INTEGER_PRINT; + result.flags |= SIGNED_TYPE_FLAG; + result.integer_base = DEC_BASE; + break; + case 'u': + result.type = INTEGER_PRINT; + result.integer_base = DEC_BASE; + break; + case 'b': + result.type = INTEGER_PRINT; + result.integer_base = BIN_BASE; + break; + case 'o': + result.type = INTEGER_PRINT; + result.integer_base = OCT_BASE; + break; + case 'x': + result.type = INTEGER_PRINT; + result.integer_base = HEX_BASE; + break; + case 'p': + // 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 = 16; + result.type = INTEGER_PRINT; + result.integer_base = HEX_BASE; + result.integer_width = LONG_WIDTH; + result.flags = ALTERNATE_FORM_FLAG|ZERO_PAD_FLAG; + break; + case 'n': + // all flags are invalid/ignored and the current count will be + // stored in the value pointed to by the argument + result.type = COUNT_PRINT; + result.flags = INVALID_FLAGS; + result.integer_width = INVALID_WIDTH; + result.field_width = 0; + result.field_precision = 0; + break; + default: + // this byte of the specifier _must_ be valid. if not, + // the procedure to print should not proceed as it might + // output garbage. + // TODO: specify somehow in the output that the format is bad? + result.type = INVALID_PRINT; + return result; + } + + begin++; + + result.length = begin - format; + return result; +} + +static char *convert_integer( + uint64_t value, + unsigned base, + int zpadding, + char *buffer, + size_t bufsz) +{ + static const char *stringdigits = "0123456789abcdef"; + + // the string will be built from the lower-to-higher value, and the + // result pointer will point to the first character of the string in + // the supplied buffer + + // cannot currently work with a base > 16 + if (base > 16) + { + return NULL; + } + + // make absolutely sure there are no excess bits + + // the string is being built backwards, so the pointer needs to be + // at the last byte of the string + char *result = buffer + bufsz - 1; + + // result >= buffer condition ensures we don't underflow + + do + { + *--result = stringdigits[value % base]; + zpadding--; + value /= base; + } + while (value > 0 && result >= buffer); + + while (zpadding-- > 0 && result >= buffer) + { + *--result = '0'; + } + + return result; +} + +static int print_character( + struct method *m, + struct specifier *spec, + va_list *arguments) +{ + (void)spec; + // all specifier flags and etc. are ignored. + return m->write_character(m, va_arg(*arguments, int)); +} + +static int print_string( + struct method *m, + struct specifier *spec, + va_list *arguments) +{ + // TODO: respect field width + if (spec->integer_width == LONG_WIDTH) + { + return m->write_wstring(m, va_arg(*arguments, const wchar_t *)); + } + return m->write_string(m, va_arg(*arguments, const char *)); +} + +static inline bool is_negative(uint64_t value, unsigned width) +{ + switch (width) + { + case BYTE_WIDTH: + return ((int8_t)value) < 0; + case SHORT_WIDTH: + return ((int16_t)value) < 0; + case INT_WIDTH: + return ((int32_t)value) < 0; + case LONG_WIDTH: + return ((int64_t)value) < 0; + default: + return false; + } +} + +static int print_integer( + struct method *m, + struct specifier *spec, + va_list *arguments) +{ + // 65 bytes is the maximum length that convert_integer will need + // i.e. conversion of uintmax_t to binary plus NUL terminator + // TODO: use UINTMAX_T_WIDTH + 1? + char buffer[65] = {0}; + char *s; + bool negative = false; + uint64_t value = 0; + int r = 0; + unsigned zpad = 0; + + switch (spec->integer_width) + { + case BYTE_WIDTH: + case SHORT_WIDTH: + case INT_WIDTH: + value = va_arg(*arguments, unsigned int); + break; + case LONG_WIDTH: + value = va_arg(*arguments, uint64_t); + break; + default: + return -1; + } + + // check if we need to bother with signs + if (spec->flags & SIGNED_TYPE_FLAG) + { + if ((negative = is_negative(value, spec->integer_width))) + { + value = ~value + 1; + } + + if (negative) + { + r = m->write_character(m, '-'); + } + + else if (!negative && (spec->flags & SIGN_FLAG_MASK)) + { + if (spec->flags & EXPLICIT_SIGN_FLAG) + { + r = m->write_character(m, '+'); + } + else + { + r = m->write_character(m, ' '); + } + } + } + + // zero all the unnecessary bits + value &= (2ULL << (spec->integer_width - 1)) - 1; + + switch (spec->integer_base) + { + case BIN_BASE: + if (spec->flags & ALTERNATE_FORM_FLAG) + { + r = m->write_string(m, "0b"); + } + break; + case OCT_BASE: + if (spec->flags & ALTERNATE_FORM_FLAG) + { + r = m->write_character(m, '0'); + } + break; + case HEX_BASE: + if (spec->flags & ALTERNATE_FORM_FLAG) + { + r = m->write_string(m, "0x"); + } + break; + default: + break; + } + + if (spec->flags & ZERO_PAD_FLAG) + { + zpad = spec->field_precision; + } + s = convert_integer(value, spec->integer_base, zpad, buffer, sizeof(buffer)); + + if (s) + { + m->write_string(m, s); + } + else + { + m->write_string(m, "(INVALID)"); + r = -1; + } + + return r; +} + +static int printf_internal( + struct method *m, + const char *restrict format, + va_list *arguments) +{ + int r = 0; + + while (*format && !r) + { + // case 1: not a format specification + if (*format != '%') + { + r = m->write_character(m, *format++); + continue; + } + + // case 2: looks like a format specification, but isn't + else if (*format == '%' && format[0] == format[1]) + { + r = m->write_character(m, '%'); + format += 2; + continue; + } + + // case 3: is a format specification. parse it + struct specifier spec = parse_specifier(++format, arguments); + + switch (spec.type) + { + case CHARACTER_PRINT: + r = print_character(m, &spec, arguments); + break; + case STRING_PRINT: + r = print_string(m, &spec, arguments); + break; + case INTEGER_PRINT: + r = print_integer(m, &spec, arguments); + break; + default: + r = m->write_string(m, "(INVALID)"); + return -1; + } + format += spec.length; + } + + return r; +} + +static int kfp_write_character(struct method *m, char c) +{ + fputc((int)c, (FILE *)m->output); + m->count++; + return 0; +} + +static int kfp_write_wcharacter(struct method *m, wchar_t c) +{ + fputc((int)c, (FILE *)m->output); + m->count++; + return 0; +} + +static int naive_write_string(struct method *m, const char *c) +{ + while (*c) + { + m->write_character(m, *c++); + } + return 0; +} + +static int naive_write_wstring(struct method *m, const wchar_t *s) +{ + while (*s) + { + m->write_wcharacter(m, *s++); + } + return 0; +} + +static int ss_write_char(struct method *m, const char c) +{ + char *s = (char *)m->output; + *s++ = c; + m->count++; + return 0; +} + +static int ss_write_wchar(struct method *m, const wchar_t c) +{ + wchar_t *s = (wchar_t *)m->output; + *s++ = c; + m->count++; + return 0; +} + +int vfprintf(FILE *f, const char *restrict format, va_list arguments) +{ + struct method m = { + kfp_write_character, + kfp_write_wcharacter, + naive_write_string, + naive_write_wstring, + (void *)f, + 0}; + va_list acopy; + va_copy(acopy, arguments); + int r = printf_internal(&m, format, &acopy); + va_end(acopy); + + if (!r) + { + return m.count; + } + else + { + return -1; + } +} + +int fprintf(FILE *f, const char *restrict format, ...) +{ + va_list arguments; + va_start(arguments, format); + + int count = vfprintf(f, format, arguments); + + va_end(arguments); + + return count; +} + +int vprintf(const char *restrict format, va_list arguments) +{ + va_list acopy; + va_copy(acopy, arguments); + + int count = vfprintf(stdout, format, arguments); + + va_end(acopy); + + return count; +} + +int printf(const char *restrict format, ...) +{ + va_list arguments; + va_start(arguments, format); + + int count = vprintf(format, arguments); + + va_end(arguments); + + return count; +} + +int vsprintf(char *s, const char *restrict format, va_list arguments) +{ + va_list acopy; + va_copy(acopy, arguments); + + struct method m = + { + ss_write_char, + ss_write_wchar, + naive_write_string, + naive_write_wstring, + (void *)s, + 0 + }; + + int r = printf_internal(&m, format, &acopy); + va_end(acopy); + + if (!r) + { + return m.count; + } + else + { + return -1; + } + + return r; +} + +int sprintf(char *s, const char *restrict format, ...) +{ + va_list arguments; + va_start(arguments, format); + + int count = vsprintf(s, format, arguments); + + va_end(arguments); + + return count; +} + + diff --git a/lib/libc/stdio.c b/lib/libc/stdio.c new file mode 100644 index 0000000..70cc685 --- /dev/null +++ b/lib/libc/stdio.c @@ -0,0 +1,30 @@ +#include <libc/stdio.h> +#include <libc/string.h> +#include <posix/unistd.h> + +struct FILE +{ + int fd; +}; + +static FILE stdin_stream = { STDIN_FILENO }; +static FILE stdout_stream = { STDOUT_FILENO }; +static FILE stderr_stream = { STDERR_FILENO }; + +FILE *stdin = &stdin_stream; +FILE *stdout = &stdout_stream; +FILE *stderr = &stderr_stream; + +int fputc(int c, FILE *f) +{ + write(f->fd, (const char *)&c, 1); + return 1; +} + +int fputs(const char *s, FILE *f) +{ + size_t slen = strlen(s); + write(f->fd, s, slen); + return (int)slen; +} + diff --git a/lib/libc/string.c b/lib/libc/string.c new file mode 100644 index 0000000..2da2070 --- /dev/null +++ b/lib/libc/string.c @@ -0,0 +1,195 @@ +#include <libc/string.h> +#include <stdbool.h> + +int strcmp(const char *s1, const char *s2) +{ + size_t s1len = strlen(s1); + size_t s2len = strlen(s2); + + return memcmp(s1, s2, s1len < s2len ? s1len : s2len); +} + +size_t strlen(const char *s) +{ + size_t l = 0; + + while (s != nullptr && *s++) l++; + + return l; +} + +size_t wcslen(const wchar_t *s) +{ + size_t l = 0; + + while (s != nullptr && *s++) l++; + + return l; +} + +size_t mbstowcs(wchar_t *dst, const char *src, size_t len) +{ + if (dst == nullptr || src == nullptr) + { + return -1; + } + + size_t i; + + for (i = 0; i < len; i++) + { + if ((dst[i] = src[i]) == 0) + { + break; + } + } + + dst[i] = L'\0'; + + return i; +} + +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; +} + |
