diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/api/libc/stdio.h | 20 | ||||
| -rw-r--r-- | lib/api/libc/stdlib.h | 9 | ||||
| -rw-r--r-- | lib/api/libc/string.h | 14 | ||||
| -rw-r--r-- | lib/api/libc/wchar.h | 6 | ||||
| -rw-r--r-- | lib/elf64.c | 6 | ||||
| -rw-r--r-- | lib/heap.c | 154 | ||||
| -rw-r--r-- | lib/kprintf.c | 653 | ||||
| -rw-r--r-- | lib/libc/memcmp.c (renamed from lib/memcmp.c) | 0 | ||||
| -rw-r--r-- | lib/libc/memcpy.c (renamed from lib/memcpy.c) | 0 | ||||
| -rw-r--r-- | lib/libc/memmove.c (renamed from lib/memmove.c) | 0 | ||||
| -rw-r--r-- | lib/libc/memset.c (renamed from lib/memset.c) | 0 | ||||
| -rw-r--r-- | lib/libc/printf.c (renamed from lib/printf.c) | 86 | ||||
| -rw-r--r-- | lib/libc/stdio.c (renamed from lib/stdio.c) | 0 | ||||
| -rw-r--r-- | lib/libc/string.c (renamed from lib/string.c) | 0 |
14 files changed, 274 insertions, 674 deletions
diff --git a/lib/api/libc/stdio.h b/lib/api/libc/stdio.h new file mode 100644 index 0000000..81ee462 --- /dev/null +++ b/lib/api/libc/stdio.h @@ -0,0 +1,20 @@ +#pragma once + +#include <libc/string.h> + +#include <stdarg.h> + +typedef struct FILE FILE; + +extern FILE *stdout; +extern FILE *stderr; + +extern int fputc(int c, FILE *f); + +int vfprintf(FILE *f, const char *restrict format, va_list arguments); +int fprintf(FILE *f, const char *restrict format, ...); +int vprintf(const char *restrict format, va_list arguments); +int printf(const char *restrict format, ...); +int vsprintf(char *s, const char *restrict format, va_list arguments); +int sprintf(char *s, const char *restrict format, ...); + diff --git a/lib/api/libc/stdlib.h b/lib/api/libc/stdlib.h new file mode 100644 index 0000000..d1f3d3b --- /dev/null +++ b/lib/api/libc/stdlib.h @@ -0,0 +1,9 @@ +#pragma once + +#include <stddef.h> + +size_t mbstowcs(wchar_t *dst, const char *src, size_t length); +void *malloc(size_t size); +void *calloc(size_t count, size_t size); +void free(void *block); + diff --git a/lib/api/libc/string.h b/lib/api/libc/string.h new file mode 100644 index 0000000..c28e17c --- /dev/null +++ b/lib/api/libc/string.h @@ -0,0 +1,14 @@ +#pragma once + +#include <stddef.h> + +void *memcpy(void *dest, const void *src, size_t size); +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/lib/api/libc/wchar.h b/lib/api/libc/wchar.h new file mode 100644 index 0000000..350c209 --- /dev/null +++ b/lib/api/libc/wchar.h @@ -0,0 +1,6 @@ +#pragma once + +#include <stddef.h> + +size_t wcslen(const wchar_t *s); +wchar_t *wcsdup(const wchar_t *s); diff --git a/lib/elf64.c b/lib/elf64.c index b5afc18..38cfc11 100644 --- a/lib/elf64.c +++ b/lib/elf64.c @@ -79,7 +79,7 @@ bool elf64_validate_fd(int fd, unsigned type, unsigned machine) return elf64_validate(&ehdr, type, machine); } -static bool is_loadable(Elf64_Phdr const *phdr) +static bool segment_is_loadable(Elf64_Phdr const *phdr) { return phdr != nullptr && phdr->p_type == PT_LOAD; } @@ -92,7 +92,7 @@ static size_t aligned_size(Elf64_Phdr const *phdr) static size_t segment_memsz(Elf64_Phdr const *phdr) { - return is_loadable(phdr) ? aligned_size(phdr) : 0; + return segment_is_loadable(phdr) ? aligned_size(phdr) : 0; } size_t elf64_size(Elf64_Ehdr const *ehdr, Elf64_Phdr *phdrs) @@ -168,7 +168,7 @@ uintptr_t elf64_dt_val(Elf64_Dyn *dyntab, unsigned long dt_type) static ssize_t load_segment(int fd, Elf64_Phdr *phdr, size_t buffer_size, char buffer[buffer_size]) { - if (!is_loadable(phdr)) + if (!segment_is_loadable(phdr)) { return 0; } @@ -1,23 +1,143 @@ #include <string.h> #include <stdlib.h> +#include <sys/mman.h> + +#include "config.h" struct heap_node { - struct heap_node *next; size_t size; + union + { + struct + { + struct heap_node *prev; + struct heap_node *next; + }; + struct + { + char block; + }; + }; }; struct heap_head { - struct heap_node *first; - size_t total_bytes; - size_t ready_bytes; + size_t allocated_size; + size_t maximum_size; + size_t minimum_alloc; + struct heap_node *root; }; +struct heap_head *heap; + +void *allocate_superblock(void) +{ + return mmap(nullptr, HEAP_SEGMENT_SIZE, 0, 0, -1, 0); +} + +static struct heap_node *try_create_heap() +{ + if (heap != nullptr) return heap->root; + + char *superblock = allocate_superblock(); + + heap = (struct heap_head *)superblock; + heap->root = (struct heap_node *)(superblock + sizeof(struct heap_head)); + + heap->allocated_size = HEAP_SEGMENT_SIZE; + heap->maximum_size = HEAP_MAX_SIZE; + heap->minimum_alloc = sizeof(heap->root); + + heap->root->next = nullptr; + heap->root->prev = nullptr; + heap->root->size = HEAP_SEGMENT_SIZE - sizeof(superblock) - sizeof(heap->root->size); + + return heap->root; +} + +static struct heap_node *try_grow_heap(struct heap_node *prev) +{ + if (heap->allocated_size >= heap->maximum_size) + { + return nullptr; + } + + struct heap_node *node = allocate_superblock(); + + if (node == nullptr) + { + return nullptr; + } + + node->size = HEAP_SEGMENT_SIZE - sizeof(node->size); + node->prev = prev; + prev->next = node; + + return node; +} + +static inline size_t get_node_size(size_t size) +{ + return size + sizeof(size); +} + +static inline bool can_divide_node(struct heap_node *parent) +{ + return parent->size <= heap->minimum_alloc + sizeof(struct heap_node); +} + +static inline void *new_node_address(struct heap_node *parent, size_t size) +{ + return &parent->block + parent->size - get_node_size(size); +} + +static inline struct heap_node *divide_node(struct heap_node *parent, size_t size) +{ + struct heap_node *child = new_node_address(parent, size); + + parent->size -= get_node_size(size); + child->size = size; + + return child; +} + +static inline struct heap_node *try_divide_node(struct heap_node *node, size_t size) +{ + return can_divide_node(node) ? divide_node(node, size) : node; +} + +static inline struct heap_node *allocate_node(size_t size) +{ + struct heap_node *node = try_create_heap(); + + while(node != nullptr) + { + if (node->size > size) + { + break; + } + + node = node->next + ? node->next + : try_grow_heap(node); + } + + return node; +} + void *malloc(size_t size) { - (void)size; - return nullptr; + // TODO: min(heap->minimum_alloc, size); + size = size >= heap->minimum_alloc + ? size + : heap->minimum_alloc; + + struct heap_node *node = try_divide_node(allocate_node(size), size); + + return node != nullptr + ? &node->block + : nullptr; } void *calloc(size_t count, size_t size) @@ -32,8 +152,28 @@ void *calloc(size_t count, size_t size) return block; } +static inline struct heap_node *block_to_node(char *block) +{ + return (struct heap_node *)(block - sizeof(size_t)); +} + void free(void *block) { - (void)block; + // TODO: insertion sort by address of node + // TODO: coalesce adjacent nodes + if (block == nullptr) + { + return; + } + + struct heap_node *node = block_to_node(block); + node->prev = nullptr; + node->next = heap->root; + heap->root = node; + + if (node->next != nullptr) + { + node->next->prev = node; + } } diff --git a/lib/kprintf.c b/lib/kprintf.c deleted file mode 100644 index 3d803f6..0000000 --- a/lib/kprintf.c +++ /dev/null @@ -1,653 +0,0 @@ -#include <lib/kstdio.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) -{ - kfputc((int)c, (FILE *)m->output); - m->count++; - return 0; -} - -static int kfp_write_wcharacter(struct method *m, wchar_t c) -{ - kfputc((int)c, (FILE *)m->output); - m->count++; - return 0; -} - -static int kfp_write_string(struct method *m, const char *c) -{ - while (*c) - { - m->write_character(m, *c++); - } - return 0; -} - -static int kfp_write_wstring(struct method *m, const wchar_t *s) -{ - while (*s) - { - m->write_wcharacter(m, *s++); - } - return 0; -} - -int kvfprintf(FILE *f, const char *restrict format, va_list arguments) -{ - struct method m = { - kfp_write_character, - kfp_write_wcharacter, - kfp_write_string, - kfp_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 kfprintf(FILE *f, const char *restrict format, ...) -{ - va_list arguments; - va_start(arguments, format); - - int count = kvfprintf(f, format, arguments); - - va_end(arguments); - - return count; -} - -int kvprintf(const char *restrict format, va_list arguments) -{ - va_list acopy; - va_copy(acopy, arguments); - - int count = kvfprintf(kstdout, format, arguments); - - va_end(acopy); - - return count; -} - -int kprintf(const char *restrict format, ...) -{ - va_list arguments; - va_start(arguments, format); - - int count = kvprintf(format, arguments); - - va_end(arguments); - - return count; -} - diff --git a/lib/memcmp.c b/lib/libc/memcmp.c index 2348afe..2348afe 100644 --- a/lib/memcmp.c +++ b/lib/libc/memcmp.c diff --git a/lib/memcpy.c b/lib/libc/memcpy.c index 58b1e40..58b1e40 100644 --- a/lib/memcpy.c +++ b/lib/libc/memcpy.c diff --git a/lib/memmove.c b/lib/libc/memmove.c index fd06bb6..fd06bb6 100644 --- a/lib/memmove.c +++ b/lib/libc/memmove.c diff --git a/lib/memset.c b/lib/libc/memset.c index 3e7025e..3e7025e 100644 --- a/lib/memset.c +++ b/lib/libc/memset.c diff --git a/lib/printf.c b/lib/libc/printf.c index 4fd14e9..fa68bcc 100644 --- a/lib/printf.c +++ b/lib/libc/printf.c @@ -568,22 +568,43 @@ static int kfp_write_character(struct method *m, char c) static int kfp_write_wcharacter(struct method *m, wchar_t c) { - (void)m; - (void)c; - return -1; + 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 kfp_write_string(struct method *m, const char *s) +static int naive_write_wstring(struct method *m, const wchar_t *s) { - m->count += fputs(s, (FILE *)m->output); + while (*s) + { + m->write_wcharacter(m, *s++); + } return 0; } -static int kfp_write_wstring(struct method *m, const wchar_t *s) +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) { - (void)m; - (void)s; - return -1; + wchar_t *s = (wchar_t *)m->output; + *s++ = c; + m->count++; + return 0; } int vfprintf(FILE *f, const char *restrict format, va_list arguments) @@ -591,8 +612,8 @@ int vfprintf(FILE *f, const char *restrict format, va_list arguments) struct method m = { kfp_write_character, kfp_write_wcharacter, - kfp_write_string, - kfp_write_wstring, + naive_write_string, + naive_write_wstring, (void *)f, 0}; va_list acopy; @@ -646,3 +667,46 @@ int printf(const char *restrict format, ...) 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/stdio.c b/lib/libc/stdio.c index 70cc685..70cc685 100644 --- a/lib/stdio.c +++ b/lib/libc/stdio.c diff --git a/lib/string.c b/lib/libc/string.c index 2da2070..2da2070 100644 --- a/lib/string.c +++ b/lib/libc/string.c |
