From b063797bc4255f0ffdb355e76dc6ac3aec05fc2d Mon Sep 17 00:00:00 2001 From: Luke Street Date: Sun, 30 Sep 2018 14:56:07 -0400 Subject: [PATCH] ELF support; printf impl; replacing kprint with printf --- README | 10 +- kernel/CMakeLists.txt | 1 + kernel/console.c | 108 +++++-- kernel/console.h | 15 +- kernel/debug/dwarf.c | 57 +++- kernel/debug/dwarf.h | 5 +- kernel/drivers/vga.c | 4 +- kernel/elf.c | 47 ++- kernel/elf.h | 33 +- kernel/fatfs/integer.h | 18 +- kernel/isr.c | 10 +- kernel/main.c | 14 +- kernel/multiboot.c | 90 ++---- kernel/shell.c | 112 ++----- kernel/tests/fatfs.c | 2 +- libc/CMakeLists.txt | 3 +- libc/common.h | 4 +- libc/ctype.h | 29 ++ libc/errno.c | 5 + libc/errno.h | 9 + libc/malloc.c | 70 ++++- libc/math.c | 57 ++++ libc/math.h | 48 ++- libc/stdio.c | 72 +++++ libc/stdio.h | 102 ++++++ libc/stdio/fprintf.c | 11 + libc/stdio/printf.c | 11 + libc/stdio/snprintf.c | 11 + libc/stdio/vfprintf.c | 692 +++++++++++++++++++++++++++++++++++++++++ libc/stdio/vsnprintf.c | 52 ++++ libc/string.c | 25 +- libc/string.h | 4 + libc/wchar.c | 27 ++ libc/wchar.h | 11 + mkhd.sh | 30 +- 35 files changed, 1552 insertions(+), 247 deletions(-) create mode 100644 libc/ctype.h create mode 100644 libc/errno.c create mode 100644 libc/errno.h create mode 100644 libc/math.c create mode 100644 libc/stdio.c create mode 100644 libc/stdio.h create mode 100644 libc/stdio/fprintf.c create mode 100644 libc/stdio/printf.c create mode 100644 libc/stdio/snprintf.c create mode 100644 libc/stdio/vfprintf.c create mode 100644 libc/stdio/vsnprintf.c create mode 100644 libc/wchar.c create mode 100644 libc/wchar.h diff --git a/README b/README index 5fa0a7d..03b79af 100644 --- a/README +++ b/README @@ -1,8 +1,12 @@ -qemu-system-i386 -kernel kernel.bin -device isa-debug-exit,iobase=0xf4,iosize=0x04 +# directly +qemu-system-i386 -kernel kernel.bin # via Grub: ./mkiso.sh kernel.bin -qemu-system-i386 -cdrom os.iso -device isa-debug-exit,iobase=0xf4,iosize=0x04 +qemu-system-i386 -cdrom os.iso + +# support exit +-device isa-debug-exit,iobase=0xf4,iosize=0x04 # remote gdb (port 1234): -s @@ -11,4 +15,4 @@ qemu-system-i386 -cdrom os.iso -device isa-debug-exit,iobase=0xf4,iosize=0x04 -serial stdio # IDE drive: --drive file=fat.img,if=ide \ No newline at end of file +-drive file=hd.img,if=ide \ No newline at end of file diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index e994a45..ccedeb5 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -32,6 +32,7 @@ set(SOURCE_FILES drivers/ata drivers/pci drivers/pci_registry +# drivers/i825xx fatfs/diskio fatfs/ff fatfs/ffconf.h diff --git a/kernel/console.c b/kernel/console.c index 26e3e39..00c167e 100644 --- a/kernel/console.c +++ b/kernel/console.c @@ -2,6 +2,9 @@ #include "drivers/vga.h" #include "drivers/serial.h" +#include +#include + static bool vga_enabled = false; static bool serial_enabled = false; @@ -25,29 +28,30 @@ void console_set_serial_enabled(bool enabled) { * Print a message on the specified location * If col, row, are negative, we will use the current offset */ - -void kprint(const char *message) { +static void knprint(const char *str, size_t len, uint8_t vga_color) { if (vga_enabled) { int offset = vga_get_cursor_offset(); int row = vga_get_offset_row(offset); int col = vga_get_offset_col(offset); - int i = 0; - while (message[i] != 0) { - offset = vga_print_char(message[i++], col, row, vga_entry_color(VGA_COLOR_WHITE, VGA_COLOR_BLACK)); + for (size_t i = 0; i < len; ++i) { + offset = vga_print_char(str[i], col, row, vga_color); row = vga_get_offset_row(offset); col = vga_get_offset_col(offset); } } if (serial_enabled) { - int i = 0; - while (message[i] != 0) { - serial_write(message[i++]); + for (size_t i = 0; i < len; ++i) { + serial_write(str[i]); } } } +void kprint(const char *message) { + knprint(message, strlen(message), WHITE_ON_BLACK); +} + void kprint_char(char c) { if (vga_enabled) { int offset = vga_get_cursor_offset(); @@ -59,20 +63,6 @@ void kprint_char(char c) { } } -void kprint_backspace() { - if (vga_enabled) { - int offset = vga_get_cursor_offset() - 1; - vga_print_char(' ', vga_get_offset_col(offset), vga_get_offset_row(offset), WHITE_ON_BLACK); - vga_set_cursor_offset(offset); - } - - if (serial_enabled) { - serial_write(0x8); - serial_write(' '); - serial_write(0x8); - } -} - void clear_screen() { if (vga_enabled) { vga_clear_screen(); @@ -135,8 +125,13 @@ void kprint_uint8(uint8_t val) { } _noreturn -void panic(char *str) { - if (str != NULL) kprint(str); +void panic(char *str, ...) { + va_list args; + va_start(args, str); + if (str != NULL) vfprintf(stderr, str, args); + va_end(args); + fflush(stderr); + __asm__("cli"); while (1) __asm__("hlt"); } @@ -148,9 +143,70 @@ void panic(char *str) { #endif _unused - uintptr_t __stack_chk_guard = STACK_CHK_GUARD; +uintptr_t __stack_chk_guard = STACK_CHK_GUARD; _noreturn _unused void __stack_chk_fail() { panic("Stack smashing detected"); -} \ No newline at end of file +} + +// --- stdio + +int errno; + +size_t __stdout_write(FILE *f, const char *str, size_t len) { + size_t rem = f->wpos - f->wbase; + if (rem) knprint(f->wbase, rem, WHITE_ON_BLACK); + if (len) knprint(str, len, WHITE_ON_BLACK); + + f->wend = f->buf + f->buf_size; + f->wpos = f->wbase = f->buf; + return len; +} + +size_t __stderr_write(FILE *f, const char *str, size_t len) { + size_t rem = f->wpos - f->wbase; + if (rem) knprint(f->wbase, rem, RED_ON_WHITE); + if (len) knprint(str, len, RED_ON_WHITE); + + f->wend = f->buf + f->buf_size; + f->wpos = f->wbase = f->buf; + return len; +} + +off_t __stdio_seek(FILE *file, const off_t offset, int origin) { + return 0; // TODO +} + +int __stdio_close(FILE *file) { + return 0; // TODO +} + +#define BUFSIZ 1024 +#define UNGET 8 + +static char __stdout_buf[BUFSIZ + UNGET]; +FILE *stdout = &(FILE) { + .buf = __stdout_buf + UNGET, + .buf_size = BUFSIZ, + .fd = 1, + .flags = F_PERM | F_NORD, + .lbf = '\n', + .write = __stdout_write, + .seek = __stdio_seek, + .close = __stdio_close, + .lock = -1, +}; + +static char __stderr_buf[BUFSIZ + UNGET]; +FILE *stderr = &(FILE) { + .buf = __stderr_buf + UNGET, + .buf_size = BUFSIZ, + .fd = 1, + .flags = F_PERM | F_NORD, + .lbf = '\n', + .write = __stderr_write, + .seek = __stdio_seek, + .close = __stdio_close, + .lock = -1, +}; \ No newline at end of file diff --git a/kernel/console.h b/kernel/console.h index ae0530e..9dc0a67 100644 --- a/kernel/console.h +++ b/kernel/console.h @@ -10,13 +10,12 @@ void console_set_serial_enabled(bool enabled); void clear_screen(); -void kprint(const char *message); -void kprint_char(char c); -void kprint_uint64(uint64_t val); -void kprint_uint32(uint32_t val); -void kprint_uint16(uint16_t val); -void kprint_uint8(uint8_t val); -void kprint_backspace(); +//void kprint(const char *message); +//void kprint_char(char c); +//void kprint_uint64(uint64_t val); +//void kprint_uint32(uint32_t val); +//void kprint_uint16(uint16_t val); +//void kprint_uint8(uint8_t val); _noreturn -void panic(char *str); \ No newline at end of file +void panic(char *str, ...); \ No newline at end of file diff --git a/kernel/debug/dwarf.c b/kernel/debug/dwarf.c index 8e738fc..6a44893 100644 --- a/kernel/debug/dwarf.c +++ b/kernel/debug/dwarf.c @@ -1,5 +1,7 @@ #include #include +#include +#include #include "dwarf.h" #include "../elf.h" #include "../console.h" @@ -7,12 +9,18 @@ #define KERNEL_BASE 0xC0100000 +void dwarf_find_file(uintptr_t address) { + FILE *file; +} + void *dwarf_find_debug_info(FATFS *fs) { FRESULT ret; FILINFO f_info; FIL file; void *header_ptr = NULL; void *sh_table_ptr = NULL; + void *sh_table_str_ptr = NULL; + void *debug_line_ptr = NULL; uint32_t read = 0; ret = f_stat("kernel.bin", &f_info); @@ -23,6 +31,8 @@ void *dwarf_find_debug_info(FATFS *fs) { size_t header_size = sizeof(elf_header_t); header_ptr = malloc(header_size); + if (header_ptr == NULL) goto fail; + ret = f_read(&file, header_ptr, header_size, &read); if (ret != FR_OK || read != header_size) goto fail; @@ -36,11 +46,52 @@ void *dwarf_find_debug_info(FATFS *fs) { header->section_header_num_entries; kprint("sh_table_size = "); kprint_uint16(sh_table_size); kprint_char('\n'); sh_table_ptr = malloc(sh_table_size); + if (sh_table_ptr == NULL) goto fail; + ret = f_read(&file, sh_table_ptr, sh_table_size, &read); if (ret != FR_OK || read != sh_table_size) goto fail; - elf_section_header_t *section = elf_find_section(header, sh_table_ptr, ELF_SHT_STRTAB); - kprint_uint32((uintptr_t) section); + elf_section_header_t *sh_table_str_section = elf_get_section(header, sh_table_ptr, header->section_header_section_names_idx); + if (sh_table_str_section == NULL || sh_table_str_section->size > UINT16_MAX) goto fail; + + ret = f_lseek(&file, sh_table_str_section->offset); + if (ret != FR_OK) goto fail; + + uint16_t sh_table_str_size = (uint16_t) sh_table_str_section->size; + kprint("sh_table_str_size = "); kprint_uint16(sh_table_str_size); kprint_char('\n'); + sh_table_str_ptr = malloc(sh_table_str_size); + if (sh_table_str_ptr == NULL) goto fail; + + ret = f_read(&file, sh_table_str_ptr, sh_table_str_size, &read); + if (ret != FR_OK || read != sh_table_str_size) goto fail; + +// elf_print_sections(header, sh_table_ptr, sh_table_str_ptr); + + elf_section_header_t *debug_line_section = NULL; + for (uint16_t i = 0; i < header->section_header_num_entries; ++i) { + elf_section_header_t *section_header = sh_table_ptr + header->section_header_entry_size * i; + char *name = sh_table_str_ptr + section_header->name; + if (strcmp(name, ".debug_line") == 0) { + debug_line_section = section_header; + break; + } + } + if (debug_line_section == NULL || debug_line_section->size > UINT16_MAX) goto fail; + + ret = f_lseek(&file, debug_line_section->offset); + if (ret != FR_OK) goto fail; + + uint16_t debug_line_section_size = (uint16_t) debug_line_section->size; + kprint("debug_line_section_size = "); kprint_uint16(debug_line_section_size); kprint_char('\n'); + debug_line_ptr = malloc(debug_line_section_size); + if (debug_line_ptr == NULL) goto fail; + + ret = f_read(&file, debug_line_ptr, debug_line_section_size, &read); + if (ret != FR_OK || read != debug_line_section_size) goto fail; + + dwarf_debug_line_header_t *debug_line_header = debug_line_ptr; + kprint_uint32(debug_line_header->length); + kprint_char('\n'); goto end; @@ -55,5 +106,7 @@ void *dwarf_find_debug_info(FATFS *fs) { f_close(&file); free(header_ptr); free(sh_table_ptr); + free(sh_table_str_ptr); + free(debug_line_ptr); return NULL; } \ No newline at end of file diff --git a/kernel/debug/dwarf.h b/kernel/debug/dwarf.h index 0439cc9..5a79425 100644 --- a/kernel/debug/dwarf.h +++ b/kernel/debug/dwarf.h @@ -2,7 +2,7 @@ #include -typedef struct _packed { +struct _packed dwarf_debug_line_header { uint32_t length; uint16_t version; uint32_t header_length; @@ -12,4 +12,5 @@ typedef struct _packed { uint8_t line_range; uint8_t opcode_base; uint8_t std_opcode_lengths[12]; -} dwarf_debug_line_header; \ No newline at end of file +}; +typedef struct dwarf_debug_line_header dwarf_debug_line_header_t; \ No newline at end of file diff --git a/kernel/drivers/vga.c b/kernel/drivers/vga.c index 308b3c0..a45213d 100644 --- a/kernel/drivers/vga.c +++ b/kernel/drivers/vga.c @@ -30,7 +30,9 @@ int vga_print_char(char c, int col, int row, char attr) { if (col >= 0 && row >= 0) offset = vga_get_offset(col, row); else offset = vga_get_cursor_offset(); - if (c == '\n') { + if (c == '\b') { + offset--; + } else if (c == '\n') { row = vga_get_offset_row(offset); offset = vga_get_offset(0, row + 1); } else { diff --git a/kernel/elf.c b/kernel/elf.c index fcaace7..30f6e94 100644 --- a/kernel/elf.c +++ b/kernel/elf.c @@ -1,7 +1,8 @@ #include "elf.h" #include "console.h" -#include +#include +#include #define ELF_DEBUG @@ -68,12 +69,50 @@ elf_section_header_t *elf_find_section(elf_header_t *header, elf_section_header_t *sht_start, elf_section_header_type_t type) { uint16_t num_entries = header->section_header_num_entries; - kprint("starting scan @ "); kprint_uint32((uintptr_t) sht_start); kprint_char('\n'); for (uint16_t i = 0; i < num_entries; ++i) { elf_section_header_t *section_header = (void *) sht_start + header->section_header_entry_size * i; - kprint("scanning sect @ "); kprint_uint32((uintptr_t) section_header); - kprint(", type "); kprint_uint32(section_header->type); kprint_char('\n'); if (section_header->type == type) return section_header; } return NULL; +} + +elf_section_header_t *elf_get_section(elf_header_t *header, + elf_section_header_t *sht_start, + uint16_t index) { + uint16_t num_entries = header->section_header_num_entries; + if (index > num_entries - 1) return NULL; + return (void *) sht_start + header->section_header_entry_size * index; +} + +static const char* elf_section_type(elf_section_header_type_t type) { + switch (type) { + case ELF_SHT_NULL: return "SHT_NULL"; + case ELF_SHT_PROGBITS: return "SHT_PROGBITS"; + case ELF_SHT_SYMTAB: return "SHT_SYMTAB"; + case ELF_SHT_STRTAB: return "SHT_STRTAB"; + case ELF_SHT_RELA: return "SHT_RELA"; + case ELF_SHT_HASH: return "SHT_HASH"; + case ELF_SHT_DYNAMIC: return "SHT_DYNAMIC"; + case ELF_SHT_NOTE: return "SHT_NOTE"; + case ELF_SHT_NOBITS: return "SHT_NOBITS"; + case ELF_SHT_REL: return "SHT_REL"; + case ELF_SHT_SHLIB: return "SHT_SHLIB"; + case ELF_SHT_DYNSYM: return "SHT_DYNSYM"; + case ELF_SHT_LOPROC: return "SHT_LOPROC"; + case ELF_SHT_HIPROC: return "SHT_HIPROC"; + case ELF_SHT_LOUSER: return "SHT_LOUSER"; + case ELF_SHT_HIUSER: return "SHT_HIUSER"; + default: return "UNKNOWN"; + } +} + +void elf_print_sections(elf_header_t *header, elf_section_header_t *sht_start, void *shstrtab_ptr) { + uint16_t num_entries = header->section_header_num_entries; + for (uint16_t i = 0; i < num_entries; ++i) { + elf_section_header_t *section_header = (void *) sht_start + header->section_header_entry_size * i; + if (section_header->type == ELF_SHT_NULL) continue; // Skip null header + printf("Section %d %s: offset "PRIx32", size "PRIx32", type %s\n", + i, (char *) shstrtab_ptr + section_header->name, section_header->offset, + section_header->size, elf_section_type(section_header->type)); + } } \ No newline at end of file diff --git a/kernel/elf.h b/kernel/elf.h index 63fb4cd..fca5a3b 100644 --- a/kernel/elf.h +++ b/kernel/elf.h @@ -1,6 +1,7 @@ #pragma once #include +#include #define ELF_HEADER_MAGIC_LE ((uint32_t) 0x7F | 'E' << 8 | 'L' << 16 | 'F' << 24) @@ -42,14 +43,29 @@ typedef enum elf_machine_type elf_machine_type_t; _Static_assert(sizeof(elf_machine_type_t) == sizeof(uint16_t), "elf_machine_type incorrect size"); +enum elf_obj_type { + ELF_ET_NONE = 0, + ELF_ET_REL = 1, + ELF_ET_EXEC = 2, + ELF_ET_DYN = 3, + ELF_ET_CORE = 4, + ELF_ET_LOPROC = 0xFF00, + ELF_ET_HIPROC = 0xFFFF +}; +typedef enum elf_obj_type elf_obj_type_t; + +_Static_assert(sizeof(elf_obj_type_t) == sizeof(uint16_t), + "elf_obj_type incorrect size"); + struct _packed elf_header { uint32_t magic; elf_header_arch_bits_t arch_bits; elf_header_endianness_t endianness; uint8_t elf_version; uint8_t __padding[9]; - uint16_t elf_type; // 1 = relocatable, 2 = executable, 3 = shared, 4 = core + elf_obj_type_t obj_type; elf_machine_type_t machine_type; + uint32_t version; // ????? uint32_t program_entry_offset; uint32_t program_header_offset; uint32_t section_header_offset; @@ -63,7 +79,7 @@ struct _packed elf_header { }; typedef struct elf_header elf_header_t; -_Static_assert(sizeof(elf_header_t) == 48, +_Static_assert(sizeof(elf_header_t) == 52, "elf_header incorrect size"); enum elf_section_header_type { @@ -103,6 +119,13 @@ struct _packed elf_section_header { }; typedef struct elf_section_header elf_section_header_t; +struct elf_file { + FILE fd; + elf_header_t *header; + elf_section_header_t *sht_start; +}; +typedef struct elf_file elf_file_t; + //_Static_assert(sizeof(elf_section_header_t) == 32, // "elf_section_header incorrect size"); @@ -113,3 +136,9 @@ elf_header_t *read_elf_header(void *ptr); elf_section_header_t *elf_find_section(elf_header_t *header, elf_section_header_t *sht_start, elf_section_header_type_t type); + +elf_section_header_t *elf_get_section(elf_header_t *header, + elf_section_header_t *sht_start, + uint16_t index); + +void elf_print_sections(elf_header_t *header, elf_section_header_t *sht_start, void *shstrtab_ptr); \ No newline at end of file diff --git a/kernel/fatfs/integer.h b/kernel/fatfs/integer.h index b3c70ca..0339d32 100644 --- a/kernel/fatfs/integer.h +++ b/kernel/fatfs/integer.h @@ -5,6 +5,8 @@ #ifndef FF_INTEGER #define FF_INTEGER +#include + #ifdef _WIN32 /* FatFs development platform */ #include @@ -13,20 +15,20 @@ typedef unsigned __int64 QWORD; #else /* Embedded platform */ /* These types MUST be 16-bit or 32-bit */ -typedef int INT; -typedef unsigned int UINT; +typedef int32_t INT; +typedef uint32_t UINT; /* This type MUST be 8-bit */ -typedef unsigned char BYTE; +typedef uint8_t BYTE; /* These types MUST be 16-bit */ -typedef short SHORT; -typedef unsigned short WORD; -typedef unsigned short WCHAR; +typedef int16_t SHORT; +typedef uint16_t WORD; +typedef uint16_t WCHAR; /* These types MUST be 32-bit */ -typedef long LONG; -typedef unsigned long DWORD; +typedef int32_t LONG; +typedef uint32_t DWORD; /* This type MUST be 64-bit (Remove this for ANSI C (C89) compatibility) */ typedef unsigned long long QWORD; diff --git a/kernel/isr.c b/kernel/isr.c index 590c47b..3bb5e39 100644 --- a/kernel/isr.c +++ b/kernel/isr.c @@ -1,3 +1,4 @@ +#include #include "isr.h" #include "console.h" #include "drivers/ports.h" @@ -10,14 +11,7 @@ void register_interrupt_handler(uint8_t n, isr_t handler) { _unused void isr_handler(registers_t regs) { - kprint("Received interrupt: "); - kprint_uint32(regs.int_no); - kprint(" (err: "); - kprint_uint32(regs.err_code); - kprint(") @ "); - kprint_uint32(regs.eip); - kprint_char('\n'); - panic(NULL); + panic("Received interrupt: %lu (err: %lu) @ %p\n", regs.int_no, regs.err_code, (void *) regs.eip); } _unused diff --git a/kernel/main.c b/kernel/main.c index 7e01491..744841f 100644 --- a/kernel/main.c +++ b/kernel/main.c @@ -10,7 +10,7 @@ #include "fatfs/ff.h" #include -#include +#include // #define KDEBUG @@ -25,16 +25,16 @@ void kernel_main(uint32_t multiboot_magic, void *multiboot_info) { pci_init(); ata_init(); - uint32_t i = UINT32_MAX / 2; + uint32_t i = UINT32_MAX / 16; while(i--); // stall - kprint("Mounting drive 0... "); + printf("Mounting drive 0... "); FATFS fs; FRESULT ret = f_mount(&fs, "", 1); if (ret == FR_OK) { - kprint("OK\n"); + printf("OK\n"); } else { - kprint("fail\n"); + printf("fail %d\n", ret); } #ifdef ENABLE_DWARF @@ -42,7 +42,7 @@ void kernel_main(uint32_t multiboot_magic, void *multiboot_info) { dwarf_find_debug_info(&fs); #endif - clear_screen(); +// clear_screen(); #ifdef KDEBUG kprint("Initializing timer...\n"); @@ -70,4 +70,4 @@ void kernel_main(uint32_t multiboot_magic, void *multiboot_info) { shell_read(); key_buffer_print(); } -} \ No newline at end of file +} diff --git a/kernel/multiboot.c b/kernel/multiboot.c index f1d3c81..5318481 100644 --- a/kernel/multiboot.c +++ b/kernel/multiboot.c @@ -1,3 +1,4 @@ +#include #include "multiboot.h" #include "console.h" @@ -7,94 +8,56 @@ extern void *malloc_memory_start; extern void *malloc_memory_end; -struct multiboot_color -{ - uint8_t red; - uint8_t green; - uint8_t blue; -}; - void multiboot_init(uint32_t magic, void *info_ptr) { if (magic != MULTIBOOT_MAGIC) { - panic("multiboot_magic: Invalid magic.\n"); + panic("multiboot_magic: Invalid magic "PRIX32"\n", magic); } struct multiboot_info *info = (struct multiboot_info *) (info_ptr + PAGE_OFFSET); - kprint("multiboot_info = "); kprint_uint32((uintptr_t) info); - kprint("\nflags = "); kprint_uint32(info->flags); kprint_char('\n'); + printf("multiboot_info = "PRIXPTR", flags = "PRIx32"\n", info, info->flags); if (CHECK_FLAG(info->flags, MULTIBOOT_INFO_MEMORY)) { malloc_memory_start = (void *) 0x100000 + PAGE_OFFSET + KERNEL_OFFSET; // 1 MiB + (info->mem_upper * 1 KiB) malloc_memory_end = malloc_memory_start + (info->mem_upper * 0x400) - KERNEL_OFFSET; - kprint("upper_memory_end = "); kprint_uint32((uintptr_t) malloc_memory_end); - kprint_char('\n'); + printf("upper_memory_end = "PRIXPTR"\n", malloc_memory_end); } else { panic("multiboot: Memory information required"); } if (CHECK_FLAG(info->flags, MULTIBOOT_INFO_BOOTDEV)) { - kprint("boot_device = "); kprint_uint32(info->boot_device); - kprint_char('\n'); + printf("boot_device = "PRIx32"h\n", info->boot_device); } if (CHECK_FLAG(info->flags, MULTIBOOT_INFO_CMDLINE)) { - kprint("cmdline = "); kprint((char *) (info->cmdline + PAGE_OFFSET)); - kprint_char('\n'); + printf("cmdline = %s\n", (char *) (info->cmdline + PAGE_OFFSET)); } if (CHECK_FLAG(info->flags, MULTIBOOT_INFO_MODS)) { struct multiboot_mod_list *mod; int i; - kprint("mods_count = "); kprint_uint32(info->mods_count); - kprint(", mods_addr = "); kprint_uint32(info->mods_addr); - kprint_char('\n'); + printf("mods_count = "PRIu32", mods_addr = "PRIXUPTR"\n", info->mods_count, info->mods_addr); for (i = 0, mod = (struct multiboot_mod_list *) (info->mods_addr + PAGE_OFFSET); i < info->mods_count; i++, mod++) { - kprint(" mod_start = "); kprint_uint32(mod->mod_start); - kprint(", mod_end = "); kprint_uint32(mod->mod_end); - kprint(", cmdline = "); kprint((char *) (mod->cmdline + PAGE_OFFSET)); - kprint_char('\n'); + printf(" mod_start = "PRIXUPTR", mod_end = "PRIXUPTR", cmdline = %s\n", + mod->mod_start, mod->mod_end, (char *) (mod->cmdline + PAGE_OFFSET)); } } - /* Bits 4 and 5 are mutually exclusive! */ - if (CHECK_FLAG(info->flags, MULTIBOOT_INFO_AOUT_SYMS) && - CHECK_FLAG(info->flags, MULTIBOOT_INFO_ELF_SHDR)) { - panic("multiboot_info->flags: Both bits 4 and 5 are set.\n"); - } - - /* Is the symbol table of a.out valid? */ - if (CHECK_FLAG(info->flags, MULTIBOOT_INFO_AOUT_SYMS)) { - struct multiboot_aout_symbol_table *aout_sym = &info->u.aout_sym; - - kprint("multiboot_aout_symbol_table: tabsize = "); kprint_uint32(aout_sym->tabsize); - kprint(", strsize = "); kprint_uint32(aout_sym->strsize); - kprint(", addr = "); kprint_uint32(aout_sym->addr); - kprint_char('\n'); - } - /* Is the section header table of ELF valid? */ if (CHECK_FLAG(info->flags, MULTIBOOT_INFO_ELF_SHDR)) { struct multiboot_elf_section_header_table *elf_sec = &info->u.elf_sec; - - kprint("multiboot_elf_sec: num = "); kprint_uint32(elf_sec->num); - kprint(", size = "); kprint_uint32(elf_sec->size); - kprint(", addr = "); kprint_uint32(elf_sec->addr); - kprint(", shndx = "); kprint_uint32(elf_sec->shndx); - kprint_char('\n'); + printf("multiboot_elf_sec: num = "PRIu32", size = "PRIu32", addr = "PRIXUPTR", shndx = "PRIu32"\n", + elf_sec->num, elf_sec->size, elf_sec->addr, elf_sec->shndx); } /* Are mmap_* valid? */ if (CHECK_FLAG(info->flags, MULTIBOOT_INFO_MEM_MAP)) { + printf("mmap_addr = "PRIXUPTR", mmap_length = "PRIx32"\n", info->mmap_addr, info->mmap_length); + struct multiboot_mmap_entry *mmap; - - kprint("mmap_addr = "); kprint_uint32(info->mmap_addr); - kprint(", mmap_length = "); kprint_uint32(info->mmap_length); - kprint_char('\n'); - struct multiboot_mmap_entry *largest_available_entry = NULL; for (mmap = (struct multiboot_mmap_entry *) (info->mmap_addr + PAGE_OFFSET); (unsigned long) mmap < info->mmap_addr + PAGE_OFFSET + info->mmap_length; @@ -105,28 +68,21 @@ void multiboot_init(uint32_t magic, void *info_ptr) { largest_available_entry = mmap; } - kprint(" size = "); kprint_uint32(mmap->size); - kprint(", base_addr = "); kprint_uint64(mmap->addr); - kprint(", length = "); kprint_uint64(mmap->len); - kprint(", type = "); kprint_uint32(mmap->type); - kprint_char('\n'); + printf(" size = "PRIx32", base_addr = "PRIXUPTR64S", length = "PRIX64S", type = "PRIu32"\n", + mmap->size, mmap->addr, mmap->len, mmap->type); } if (largest_available_entry != NULL) { - // malloc_memory_start = (void *) (uint32_t) largest_available_entry->addr + PAGE_OFFSET + KERNEL_OFFSET; - // malloc_memory_end = malloc_memory_start + largest_available_entry->len - KERNEL_OFFSET; - kprint("malloc_memory_start = "); kprint_uint32((uintptr_t) malloc_memory_start); - kprint(", end = "); kprint_uint32((uintptr_t) malloc_memory_end); - kprint_char('\n'); + malloc_memory_start = (void *) (uint32_t) largest_available_entry->addr + PAGE_OFFSET + KERNEL_OFFSET; + malloc_memory_end = malloc_memory_start + largest_available_entry->len - KERNEL_OFFSET; + printf("malloc_memory_start = "PRIXPTR", end = "PRIXPTR"\n", malloc_memory_start, malloc_memory_end); } } /* Check VGA framebuffer. */ if (CHECK_FLAG (info->flags, MULTIBOOT_INFO_FRAMEBUFFER_INFO)) { - kprint("framebuffer addr = "); kprint_uint64(info->framebuffer_addr); - kprint(", type = "); kprint_uint8(info->framebuffer_type); - kprint(", bpp = "); kprint_uint8(info->framebuffer_bpp); - kprint_char('\n'); + printf("framebuffer_addr = "PRIXUPTR64", type = %d, bpp = %d\n", + info->framebuffer_addr, info->framebuffer_type, info->framebuffer_bpp); switch (info->framebuffer_type) { case MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT: @@ -134,11 +90,11 @@ void multiboot_init(uint32_t magic, void *info_ptr) { break; default: - break; - } + break; + } } else { console_set_vga_enabled(true); // FIXME } - kprint("Multiboot info loaded.\n"); + printf("Multiboot info loaded.\n"); } \ No newline at end of file diff --git a/kernel/shell.c b/kernel/shell.c index 764323d..c93fb68 100644 --- a/kernel/shell.c +++ b/kernel/shell.c @@ -13,6 +13,7 @@ #include #include #include +#include #define KEY_BUFFER_INITIAL_SIZE 0x100 static char *key_buffer; @@ -28,80 +29,34 @@ void command_lspci() { for (pci_device_t *device = vc_vector_begin(pci_devices); device != vc_vector_end(pci_devices); device = vc_vector_next(pci_devices, device)) { - kprint_uint8(device->loc.bus); - kprint_char(':'); - kprint_uint8(device->loc.device); - kprint_char('.'); - kprint_uint8(device->loc.function); - kprint_char(' '); - kprint_uint16(device->class); - kprint_char('.'); - kprint_uint8(device->prog_if); - kprint(": "); - kprint_uint16(device->vendor_id); - kprint_char(':'); - kprint_uint16(device->device_id); - kprint_char(' '); - kprint(pci_class_name(device->class, device->revision_id)); - if (device->revision_id) { - kprint(" (rev "); - kprint_uint8(device->revision_id); - kprint_char(')'); - } - kprint_char('\n'); + printf("%d:%d.%d %04X.%02X: %04X:%04X | %s", + device->loc.bus, device->loc.device, device->loc.function, + device->class, device->prog_if, device->vendor_id, device->device_id, + pci_class_name(device->class, device->revision_id)); + if (device->revision_id) printf(" (rev %d)\n", device->revision_id); + else printf("\n"); - if (device->bar0) { - kprint(" BAR0 = "); - kprint_uint32(device->bar0); - kprint_char('\n'); - } - if (device->bar1) { - kprint(" BAR1 = "); - kprint_uint32(device->bar1); - kprint_char('\n'); - } - if (device->bar2) { - kprint(" BAR2 = "); - kprint_uint32(device->bar2); - kprint_char('\n'); - } - if (device->bar3) { - kprint(" BAR3 = "); - kprint_uint32(device->bar3); - kprint_char('\n'); - } - if (device->bar4) { - kprint(" BAR4 = "); - kprint_uint32(device->bar4); - kprint_char('\n'); - } - if (device->bar5) { - kprint(" BAR5 = "); - kprint_uint32(device->bar5); - kprint_char('\n'); - } + if (device->bar0) printf(" BAR0 = "PRIXUPTR"\n", device->bar0); + if (device->bar1) printf(" BAR1 = "PRIXUPTR"\n", device->bar1); + if (device->bar2) printf(" BAR2 = "PRIXUPTR"\n", device->bar2); + if (device->bar3) printf(" BAR3 = "PRIXUPTR"\n", device->bar3); + if (device->bar4) printf(" BAR4 = "PRIXUPTR"\n", device->bar4); + if (device->bar5) printf(" BAR5 = "PRIXUPTR"\n", device->bar5); } - kprint_char('\n'); } void command_lsata() { for (uint8_t i = 0; i < 4; i++) { if (ide_devices[i].reserved == 1) { - kprint_uint8(i); - kprint(": "); - kprint((const char *[]) {"ATA", "ATAPI"}[ide_devices[i].type]); - kprint(" Drive "); - kprint_uint32(ide_devices[i].size / 1024 / 1024 / 2); - kprint("GB - "); - kprint(ide_devices[i].model); - kprint_char('\n'); + const char *type_str = ((const char *[]) {"ATA ", "ATAPI"}[ide_devices[i].type]); + printf("%d: %s | %016lu sectors | %s\n", i, type_str, ide_devices[i].size, ide_devices[i].model); } } - kprint_char('\n'); } static void shell_callback(char *input) { - kprint_char('\n'); + printf("\n"); + unsigned char ret = 1; bool save = true; if (strcmp(input, "exit") == 0 || @@ -112,11 +67,10 @@ static void shell_callback(char *input) { reboot(); } else if (strcmp(input, "clear") == 0) { clear_screen(); - kprint("# "); + printf("# "); return; } else if (strncmp(input, "echo ", 5) == 0) { - kprint(input + 5); - kprint_char('\n'); + printf("%s\n", input + 5); ret = 0; } else if (strcmp(input, "memdbg") == 0) { print_chunk_debug(NULL, true); @@ -132,8 +86,7 @@ static void shell_callback(char *input) { for (char **i = vc_vector_begin(shell_history); i != vc_vector_end(shell_history); i = vc_vector_next(shell_history, i)) { - kprint(*i); - kprint_char('\n'); + printf("%s\n", *i); } ret = 0; } else if (strcmp(input, "test vector") == 0) { @@ -142,9 +95,7 @@ static void shell_callback(char *input) { ret = (unsigned char) !fatfs_test(); } else if (strcmp(input, "test") == 0 || strncmp(input, "test ", 5) == 0) { - kprint("Available tests:\n"); - kprint(" vector\n"); - kprint(" fatfs\n"); + printf("Available tests:\n vector\n fatfs\n"); } else if (strcmp(input, "lspci") == 0) { command_lspci(); ret = 0; @@ -152,9 +103,8 @@ static void shell_callback(char *input) { command_lsata(); ret = 0; } - (void) (ret); -// kprint_uint32(ret); - kprint("# "); + printf("%d # ", ret); + fflush(stdout); if (save && input[0] != '\0') { char *value = strdup(input); @@ -169,7 +119,8 @@ static void shell_history_free_func(void *data) { void shell_init() { shell_history = vc_vector_create(0x100, sizeof(char *), shell_history_free_func); init_keyboard(); - kprint("# "); + printf("# "); + fflush(stdout); } void shell_read() { @@ -241,23 +192,26 @@ void key_buffer_clear() { void key_buffer_set(char *input) { while (key_buffer_used--) { - kprint_backspace(); + printf("\b \b"); } key_buffer_used = strlen(input); - key_buffer = realloc(key_buffer, max(key_buffer_used + 1, KEY_BUFFER_INITIAL_SIZE)); + key_buffer = realloc(key_buffer, MAX(key_buffer_used + 1, KEY_BUFFER_INITIAL_SIZE)); if (key_buffer == NULL) return; // return error of some sort? strncpy(key_buffer, input, key_buffer_used + 1); key_buffer_printed = 0; + key_buffer_print(); } void key_buffer_print() { - while (key_buffer_printed < key_buffer_used) { - kprint_char(key_buffer[key_buffer_printed++]); + if (key_buffer_printed < key_buffer_used) { + fwrite(key_buffer + key_buffer_printed, key_buffer_used - key_buffer_printed, 1, stdout); + key_buffer_printed = key_buffer_used; } while (key_buffer_printed > key_buffer_used) { - kprint_backspace(); + printf("\b \b"); key_buffer_printed--; } + fflush(stdout); } void key_buffer_return() { diff --git a/kernel/tests/fatfs.c b/kernel/tests/fatfs.c index 3f9c59a..e4bddd2 100644 --- a/kernel/tests/fatfs.c +++ b/kernel/tests/fatfs.c @@ -46,7 +46,7 @@ bool fatfs_test() { kprint_char('\n'); end: - if (buff != NULL) free(buff); + free(buff); f_close(&file); f_unmount(""); return ret == FR_OK; diff --git a/libc/CMakeLists.txt b/libc/CMakeLists.txt index c4e0d1c..370695c 100644 --- a/libc/CMakeLists.txt +++ b/libc/CMakeLists.txt @@ -11,4 +11,5 @@ if (CMAKE_HOST_APPLE) endif () include_directories(${CMAKE_SOURCE_DIR}/libc) -add_library(c STATIC string malloc math vector byteswap) \ No newline at end of file +add_library(c STATIC string malloc math vector byteswap ctype.h errno wchar stdio.c stdio.h + stdio/printf stdio/fprintf stdio/snprintf stdio/vfprintf stdio/vsnprintf) \ No newline at end of file diff --git a/libc/common.h b/libc/common.h index 7ab56a1..ce3d788 100644 --- a/libc/common.h +++ b/libc/common.h @@ -9,4 +9,6 @@ #ifdef __GNUC__ #define _unused __attribute__((unused)) #define _packed __attribute__((packed)) -#endif \ No newline at end of file +#endif + +#define off_t uint64_t \ No newline at end of file diff --git a/libc/ctype.h b/libc/ctype.h new file mode 100644 index 0000000..1a49b33 --- /dev/null +++ b/libc/ctype.h @@ -0,0 +1,29 @@ +#pragma once + +int isalnum(int); +int isalpha(int); +int isblank(int); +int iscntrl(int); +int isdigit(int); +int isgraph(int); +int islower(int); +int isprint(int); +int ispunct(int); +int isspace(int); +int isupper(int); +int isxdigit(int); +int tolower(int); +int toupper(int); + +static __inline int __isspace(int _c) +{ + return _c == ' ' || (unsigned)_c-'\t' < 5; +} + +#define isalpha(a) (0 ? isalpha(a) : (((unsigned)(a)|32)-'a') < 26) +#define isdigit(a) (0 ? isdigit(a) : ((unsigned)(a)-'0') < 10) +#define islower(a) (0 ? islower(a) : ((unsigned)(a)-'a') < 26) +#define isupper(a) (0 ? isupper(a) : ((unsigned)(a)-'A') < 26) +#define isprint(a) (0 ? isprint(a) : ((unsigned)(a)-0x20) < 0x5f) +#define isgraph(a) (0 ? isgraph(a) : ((unsigned)(a)-0x21) < 0x5e) +#define isspace(a) __isspace(a) \ No newline at end of file diff --git a/libc/errno.c b/libc/errno.c new file mode 100644 index 0000000..989a353 --- /dev/null +++ b/libc/errno.c @@ -0,0 +1,5 @@ +#include "errno.h" + +char *strerror(int errno) { + return "ERROR"; // FIXME +} \ No newline at end of file diff --git a/libc/errno.h b/libc/errno.h new file mode 100644 index 0000000..ecb9a35 --- /dev/null +++ b/libc/errno.h @@ -0,0 +1,9 @@ +#pragma once + +#define EINVAL 22 +#define EOVERFLOW 75 +#define EILSEQ 84 + +extern int errno; // FIXME for threading + +char *strerror(int errno); diff --git a/libc/malloc.c b/libc/malloc.c index 2d459b2..7b87caf 100644 --- a/libc/malloc.c +++ b/libc/malloc.c @@ -1,12 +1,13 @@ #include "malloc.h" -#include "../kernel/console.h" +#include "stdio.h" #include +#include // #define MALLOC_DEBUG void *malloc_memory_start = (void *) 0x1000000; // 1 MiB, start of x86 upper memory -void *malloc_memory_end = NULL; +void *malloc_memory_end = NULL; static bool initial_alloc = true; struct chunk_header { @@ -151,21 +152,59 @@ void *realloc(void *ptr, size_t new_size) { return new; } +// FIXME move everything below + +const char *suffixes[7] = { + "", + "KB", + "MB", + "GB", + "TB", + "PB", + "EB" +}; + +static unsigned digits(uint32_t n) { + static uint32_t powers[10] = { + 0, 10, 100, 1000, 10000, 100000, 1000000, + 10000000, 100000000, 1000000000, + }; + static unsigned max_digits[33] = { + 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, + 5, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, + }; + unsigned bits = sizeof(n) * CHAR_BIT - __builtin_clz(n); + unsigned digits = max_digits[bits]; + if (n < powers[digits - 1]) --digits; + return digits; +} + +static char *pretty_bytes(uint64_t bytes) { + uint8_t s = 0; + uint64_t count = bytes; + while (count >= 1024 && s < 7) { + s++; + count /= 1024; + } + + uint32_t len = digits((uint32_t) count) + 2; + char *buf = malloc(len + 1); + buf[snprintf(buf, len, "%llu%s", count, suffixes[s])] = 0; + return buf; +} + void print_chunk_debug(void *ptr, bool recursive) { - kprint("memory_start = "); kprint_uint32((uintptr_t) malloc_memory_start); - kprint(", end = "); kprint_uint32((uintptr_t) malloc_memory_end); - kprint_char('\n'); + printf("memory_start = %p, end = %p\n", malloc_memory_start, malloc_memory_end); uint32_t used = 0; if (ptr == NULL) ptr = malloc_memory_start + sizeof(struct chunk_header); struct chunk_header *header = ptr - sizeof(struct chunk_header); do { - kprint("chunk @ "); kprint_uint32((uintptr_t) header); - kprint(" | next: "); kprint_uint32((uintptr_t) header->next); - kprint(", prev: "); kprint_uint32((uintptr_t) header->prev); - kprint(", used: "); kprint_uint32((uint32_t) header->used); - kprint(", size: "); kprint_uint32((uint32_t) header->size); - kprint_char('\n'); + char *str_size = pretty_bytes(header->size); + printf("chunk @ %p | next: %p, prev: %p, used: %d, size: %s\n", + header, header->next, header->prev, header->used, str_size); + free(str_size); + if (header->used) { if (header->next != NULL) { used += (uintptr_t) header->next - (uintptr_t) header; @@ -177,8 +216,11 @@ void print_chunk_debug(void *ptr, bool recursive) { } while (header->next != NULL); if (recursive) { - kprint("used = "); kprint_uint32(used); - kprint(", est. free = "); kprint_uint32((uintptr_t) malloc_memory_end - (uintptr_t) malloc_memory_start - used); - kprint_char('\n'); + uint32_t est_free = (uintptr_t) malloc_memory_end - (uintptr_t) malloc_memory_start - used; + char *str_used = pretty_bytes(used); + char *str_free = pretty_bytes(est_free); + printf("used = %s, est. free = %s\n", str_used, str_free); + free(str_used); + free(str_free); } } \ No newline at end of file diff --git a/libc/math.c b/libc/math.c new file mode 100644 index 0000000..3dfc861 --- /dev/null +++ b/libc/math.c @@ -0,0 +1,57 @@ +#include "math.h" + +#include + +union ldshape { + long double f; + struct { + uint64_t m; + uint16_t se; + } i; +}; + +long double frexpl(long double x, int *e) { + union ldshape u = {x}; + int ee = u.i.se & 0x7fff; + + if (!ee) { + if (x) { + x = frexpl(x * 0x1p120, e); + *e -= 120; + } else *e = 0; + return x; + } else if (ee == 0x7fff) { + return x; + } + + *e = ee - 0x3ffe; + u.i.se &= 0x8000; + u.i.se |= 0x3ffe; + return u.f; +} + +int __signbitl(long double x) { + union ldshape u = {x}; + return u.i.se >> 15; +} + +int __fpclassifyl(long double x) { + union ldshape u = {x}; + int e = u.i.se & 0x7fff; + int msb = (int) (u.i.m >> 63); + if (!e && !msb) + return u.i.m ? FP_SUBNORMAL : FP_ZERO; + if (e == 0x7fff) { + /* The x86 variant of 80-bit extended precision only admits + * one representation of each infinity, with the mantissa msb + * necessarily set. The version with it clear is invalid/nan. + * The m68k variant, however, allows either, and tooling uses + * the version with it clear. */ + if (/*__BYTE_ORDER == __LITTLE_ENDIAN &&*/ !msb) + return FP_NAN; + return u.i.m << 1 ? FP_NAN : FP_INFINITE; + } + if (!msb) + return FP_NAN; + return FP_NORMAL; +} \ No newline at end of file diff --git a/libc/math.h b/libc/math.h index 9b077a4..bc222f5 100644 --- a/libc/math.h +++ b/libc/math.h @@ -1,11 +1,55 @@ #pragma once -#define max(a, b) \ +#define MAX(a, b) \ ({ __typeof__ (a) _a = (a); \ __typeof__ (b) _b = (b); \ _a > _b ? _a : _b; }) -#define min(a, b) \ +#define MIN(a, b) \ ({ __typeof__ (a) _a = (a); \ __typeof__ (b) _b = (b); \ _a < _b ? _a : _b; }) + +#define FP_NAN 0 +#define FP_INFINITE 1 +#define FP_ZERO 2 +#define FP_SUBNORMAL 3 +#define FP_NORMAL 4 + +int __fpclassify(double); +int __fpclassifyf(float); +int __fpclassifyl(long double); + +static inline unsigned __FLOAT_BITS(float __f) { + union { + float __f; + unsigned __i; + } __u; + __u.__f = __f; + return __u.__i; +} + +static inline unsigned long long __DOUBLE_BITS(double __f) { + union { + double __f; + unsigned long long __i; + } __u; + __u.__f = __f; + return __u.__i; +} + +#define isfinite(x) ( \ + sizeof(x) == sizeof(float) ? (__FLOAT_BITS(x) & 0x7fffffff) < 0x7f800000 : \ + sizeof(x) == sizeof(double) ? (__DOUBLE_BITS(x) & -1ULL>>1) < 0x7ffULL<<52 : \ + __fpclassifyl(x) > FP_INFINITE) + +int __signbit(double); +int __signbitf(float); +int __signbitl(long double); + +#define signbit(x) ( \ + sizeof(x) == sizeof(float) ? (int)(__FLOAT_BITS(x)>>31) : \ + sizeof(x) == sizeof(double) ? (int)(__DOUBLE_BITS(x)>>63) : \ + __signbitl(x) ) + +long double frexpl(long double x, int *e); \ No newline at end of file diff --git a/libc/stdio.c b/libc/stdio.c new file mode 100644 index 0000000..206c382 --- /dev/null +++ b/libc/stdio.c @@ -0,0 +1,72 @@ +#include "stdio.h" +#include "string.h" + +int __towrite(FILE *f) { + f->mode |= f->mode - 1; + if (f->flags & F_NOWR) { + f->flags |= F_ERR; + return EOF; + } + + /* Clear read buffer (easier than summoning nasal demons) */ + f->rpos = f->rend = 0; + + /* Activate write through the buffer. */ + f->wpos = f->wbase = f->buf; + f->wend = f->buf + f->buf_size; + + return 0; +} + +size_t __fwritex(const char *restrict s, size_t l, FILE *restrict f) { + size_t i = 0; + + if (!f->wend && __towrite(f)) return 0; + + if (l > f->wend - f->wpos) return f->write(f, s, l); + + if (f->lbf >= 0) { + /* Match /^(.*\n|)/ */ + for (i = l; i && s[i - 1] != '\n'; i--); + if (i) { + if (f->write(f, s, i) < i) + return i; + s += i; + l -= i; + } + } + + memcpy(f->wpos, s, l); + f->wpos += l; + return l + i; +} + +size_t fwrite(const void *restrict src, size_t size, size_t count, FILE *restrict f) { + size_t k, l = size * count; + if (!l) return l; + k = __fwritex(src, l, f); + return k == l ? count : k / size; +} + +int fflush(FILE *f) { + /* If writing, flush output */ + if (f->wpos != f->wbase) { + f->write(f, 0, 0); + if (!f->wpos) { + return EOF; + } + } + + /* If reading, sync position, per POSIX */ + if (f->rpos != f->rend) f->seek(f, f->rpos - f->rend, SEEK_CUR); + + /* Clear read and write modes */ + f->wpos = f->wbase = f->wend = 0; + f->rpos = f->rend = 0; + + return 0; +} + +FILE *fopen(const char *filename, const char *mode) { + +} \ No newline at end of file diff --git a/libc/stdio.h b/libc/stdio.h new file mode 100644 index 0000000..e6f36ac --- /dev/null +++ b/libc/stdio.h @@ -0,0 +1,102 @@ +#pragma once + +#include +#include + +#define EOF (-1) + +#define F_PERM 1 +#define F_NORD 4 +#define F_NOWR 8 +#define F_EOF 16 +#define F_ERR 32 +#define F_SVB 64 +#define F_APP 128 + +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 + + + +// Unsigned numbers +#define PRIu32 "%lu" +#define PRIu64 "%llu" + +// Numbers as hex (ex. uint32_t) +#define PRIx32 "%lXh" +#define PRIx64 "%llXh" + +// Numbers as zero-padded hex (ex. uint32_t) +#define PRIX32 "%08lXh" +#define PRIX64 "%016llXh" +#define PRIX64S "%08llXh" // 64-bit w/ 32-bit padding + +// Numerical pointers as zero-padded hex (ex. uintptr_t) +#define PRIXUPTR "0x%08lX" +#define PRIXUPTR64 "0x%016llX" +#define PRIXUPTR64S "0x%08llX" // 64-bit w/ 32-bit padding + +// Pointer value as zero-padded hex (ex. void *) +#define PRIXPTR "%010P" + +typedef struct _IO_FILE FILE; + +struct _IO_FILE { + unsigned flags; + char *rpos, *rend; + char *wend, *wpos, *wbase; + + size_t (*read)(FILE *, char *, size_t); + + size_t (*write)(FILE *, const char *, size_t); + + off_t (*seek)(FILE *, off_t, int); + + int (*close)(FILE *); + + char *buf; + size_t buf_size; + FILE *prev, *next; + int fd; + signed char mode; // 'r' or 'w' I guess + signed char lbf; // Line buffer (format?) + int lock; + void *cookie; + off_t off; +}; + +// TODO flesh out +struct stat { + off_t st_size; +}; + +extern FILE *stdin; +extern FILE *stdout; +extern FILE *stderr; + +// --- Internal + +int __towrite(FILE *f); + +size_t __fwritex(const char *restrict s, size_t l, FILE *restrict f); + +// --- + +size_t fwrite(const void *restrict src, size_t size, size_t nmemb, FILE *restrict f); + +int fflush(FILE *f); + +FILE *fopen(const char *filename, const char *mode); + +int fstat(int fd, struct stat *st); + +int printf(const char *restrict fmt, ...); + +int fprintf(FILE *restrict f, const char *restrict fmt, ...); + +int snprintf(char *restrict s, size_t n, const char *restrict fmt, ...); + +int vfprintf(FILE *restrict f, const char *restrict fmt, va_list ap); + +int vsnprintf(char *restrict s, size_t n, const char *restrict fmt, va_list ap); \ No newline at end of file diff --git a/libc/stdio/fprintf.c b/libc/stdio/fprintf.c new file mode 100644 index 0000000..8d2a9bb --- /dev/null +++ b/libc/stdio/fprintf.c @@ -0,0 +1,11 @@ +#include +#include + +int fprintf(FILE *restrict f, const char *restrict fmt, ...) { + int ret; + va_list ap; + va_start(ap, fmt); + ret = vfprintf(f, fmt, ap); + va_end(ap); + return ret; +} \ No newline at end of file diff --git a/libc/stdio/printf.c b/libc/stdio/printf.c new file mode 100644 index 0000000..417bffa --- /dev/null +++ b/libc/stdio/printf.c @@ -0,0 +1,11 @@ +#include +#include + +int printf(const char *restrict fmt, ...) { + int ret; + va_list ap; + va_start(ap, fmt); + ret = vfprintf(stdout, fmt, ap); + va_end(ap); + return ret; +} \ No newline at end of file diff --git a/libc/stdio/snprintf.c b/libc/stdio/snprintf.c new file mode 100644 index 0000000..ea76092 --- /dev/null +++ b/libc/stdio/snprintf.c @@ -0,0 +1,11 @@ +#include +#include + +int snprintf(char *restrict s, size_t n, const char *restrict fmt, ...) { + int ret; + va_list ap; + va_start(ap, fmt); + ret = vsnprintf(s, n, fmt, ap); + va_end(ap); + return ret; +} \ No newline at end of file diff --git a/libc/stdio/vfprintf.c b/libc/stdio/vfprintf.c new file mode 100644 index 0000000..21c9f75 --- /dev/null +++ b/libc/stdio/vfprintf.c @@ -0,0 +1,692 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Convenient bit representation for modifier flags, which all fall + * within 31 codepoints of the space character. */ + +#define ALT_FORM (1U<<('#'-' ')) +#define ZERO_PAD (1U<<('0'-' ')) +#define LEFT_ADJ (1U<<('-'-' ')) +#define PAD_POS (1U<<(' '-' ')) +#define MARK_POS (1U<<('+'-' ')) +#define GROUPED (1U<<('\''-' ')) + +#define FLAGMASK (ALT_FORM|ZERO_PAD|LEFT_ADJ|PAD_POS|MARK_POS|GROUPED) + +#define NL_ARGMAX 9 + +/* State machine to accept length modifiers + conversion specifiers. + * Result is 0 on failure, or an argument type to pop on success. */ + +enum { + BARE, LPRE, LLPRE, HPRE, HHPRE, BIGLPRE, + ZTPRE, JPRE, + STOP, + PTR, INT, UINT, ULLONG, + LONG, ULONG, + SHORT, USHORT, CHAR, UCHAR, + LLONG, SIZET, IMAX, UMAX, PDIFF, UIPTR, + DBL, LDBL, + NOARG, + MAXSTATE +}; + +#define S(x) [(x)-'A'] + +static const unsigned char states[]['z'-'A'+1] = { + { /* 0: bare types */ + S('d') = INT, S('i') = INT, + S('o') = UINT, S('u') = UINT, S('x') = UINT, S('X') = UINT, + S('e') = DBL, S('f') = DBL, S('g') = DBL, S('a') = DBL, + S('E') = DBL, S('F') = DBL, S('G') = DBL, S('A') = DBL, + S('c') = CHAR, S('C') = INT, + S('s') = PTR, S('S') = PTR, S('p') = UIPTR, S('P') = UIPTR, + S('n') = PTR, + S('m') = NOARG, + S('l') = LPRE, S('h') = HPRE, S('L') = BIGLPRE, + S('z') = ZTPRE, S('j') = JPRE, S('t') = ZTPRE, + }, { /* 1: l-prefixed */ + S('d') = LONG, S('i') = LONG, + S('o') = ULONG, S('u') = ULONG, S('x') = ULONG, S('X') = ULONG, + S('e') = DBL, S('f') = DBL, S('g') = DBL, S('a') = DBL, + S('E') = DBL, S('F') = DBL, S('G') = DBL, S('A') = DBL, + S('c') = INT, S('s') = PTR, S('n') = PTR, + S('l') = LLPRE, + }, { /* 2: ll-prefixed */ + S('d') = LLONG, S('i') = LLONG, + S('o') = ULLONG, S('u') = ULLONG, + S('x') = ULLONG, S('X') = ULLONG, + S('n') = PTR, + }, { /* 3: h-prefixed */ + S('d') = SHORT, S('i') = SHORT, + S('o') = USHORT, S('u') = USHORT, + S('x') = USHORT, S('X') = USHORT, + S('n') = PTR, + S('h') = HHPRE, + }, { /* 4: hh-prefixed */ + S('d') = CHAR, S('i') = CHAR, + S('o') = UCHAR, S('u') = UCHAR, + S('x') = UCHAR, S('X') = UCHAR, + S('n') = PTR, + }, { /* 5: L-prefixed */ + S('e') = LDBL, S('f') = LDBL, S('g') = LDBL, S('a') = LDBL, + S('E') = LDBL, S('F') = LDBL, S('G') = LDBL, S('A') = LDBL, + S('n') = PTR, + }, { /* 6: z- or t-prefixed (assumed to be same size) */ + S('d') = PDIFF, S('i') = PDIFF, + S('o') = SIZET, S('u') = SIZET, + S('x') = SIZET, S('X') = SIZET, + S('n') = PTR, + }, { /* 7: j-prefixed */ + S('d') = IMAX, S('i') = IMAX, + S('o') = UMAX, S('u') = UMAX, + S('x') = UMAX, S('X') = UMAX, + S('n') = PTR, + } +}; + +#define OOB(x) ((unsigned)(x)-'A' > 'z'-'A') + +union arg +{ + uintmax_t i; + long double f; + void *p; +}; + +static void pop_arg(union arg *arg, int type, va_list *ap) +{ + switch (type) { + case PTR: arg->p = va_arg(*ap, void *); + break; case INT: arg->i = va_arg(*ap, int); + break; case UINT: arg->i = va_arg(*ap, unsigned int); + break; case LONG: arg->i = va_arg(*ap, long); + break; case ULONG: arg->i = va_arg(*ap, unsigned long); + break; case ULLONG: arg->i = va_arg(*ap, unsigned long long); + break; case SHORT: arg->i = (short)va_arg(*ap, int); + break; case USHORT: arg->i = (unsigned short)va_arg(*ap, int); + break; case CHAR: arg->i = (signed char)va_arg(*ap, int); + break; case UCHAR: arg->i = (unsigned char)va_arg(*ap, int); + break; case LLONG: arg->i = va_arg(*ap, long long); + break; case SIZET: arg->i = va_arg(*ap, size_t); + break; case IMAX: arg->i = va_arg(*ap, intmax_t); + break; case UMAX: arg->i = va_arg(*ap, uintmax_t); + break; case PDIFF: arg->i = va_arg(*ap, ptrdiff_t); + break; case UIPTR: arg->i = (uintptr_t)va_arg(*ap, void *); + break; case DBL: arg->f = va_arg(*ap, double); + break; case LDBL: arg->f = va_arg(*ap, long double); + } +} + +static void out(FILE *f, const char *s, size_t l) +{ + if (!(f->flags & F_ERR)) __fwritex((void *)s, l, f); +} + +static void pad(FILE *f, char c, int w, int l, int fl) +{ + char pad[256]; + if (fl & (LEFT_ADJ | ZERO_PAD) || l >= w) return; + l = w - l; + memset(pad, c, l>sizeof pad ? sizeof pad : l); + for (; l >= sizeof pad; l -= sizeof pad) + out(f, pad, sizeof pad); + out(f, pad, l); +} + +static const char xdigits[16] = { + "0123456789ABCDEF" +}; + +static char *fmt_x(uintmax_t x, char *s, int lower) +{ + for (; x; x>>=4) *--s = xdigits[(x&15)]|lower; + return s; +} + +static char *fmt_o(uintmax_t x, char *s) +{ + for (; x; x>>=3) *--s = '0' + (x&7); + return s; +} + +static char *fmt_u(uintmax_t x, char *s) +{ + unsigned long y; + for ( ; x>ULONG_MAX; x/=10) *--s = '0' + x%10; + for (y=x; y; y/=10) *--s = '0' + y%10; + return s; +} + +/* Do not override this check. The floating point printing code below + * depends on the float.h constants being right. If they are wrong, it + * may overflow the stack. */ +#if LDBL_MANT_DIG == 53 +typedef char compiler_defines_long_double_incorrectly[9-(int)sizeof(long double)]; +#endif + +static int fmt_fp(FILE *f, long double y, int w, int p, int fl, int t) +{ + uint32_t big[(LDBL_MANT_DIG+28)/29 + 1 // mantissa expansion + + (LDBL_MAX_EXP+LDBL_MANT_DIG+28+8)/9]; // exponent expansion + uint32_t *a, *d, *r, *z; + int e2=0, e, i, j, l; + char buf[9+LDBL_MANT_DIG/4], *s; + const char *prefix="-0X+0X 0X-0x+0x 0x"; + int pl; + char ebuf0[3*sizeof(int)], *ebuf=&ebuf0[3*sizeof(int)], *estr; + + pl=1; + if (signbit(y)) { + y=-y; + } else if (fl & MARK_POS) { + prefix+=3; + } else if (fl & PAD_POS) { + prefix+=6; + } else prefix++, pl=0; + + if (!isfinite(y)) { + char *s = (t&32)?"inf":"INF"; + if (y!=y) s=(t&32)?"nan":"NAN"; + pad(f, ' ', w, 3+pl, fl&~ZERO_PAD); + out(f, prefix, pl); + out(f, s, 3); + pad(f, ' ', w, 3+pl, fl^LEFT_ADJ); + return MAX(w, 3+pl); + } + + y = frexpl(y, &e2) * 2; + if (y) e2--; + + if ((t|32)=='a') { + long double round = 8.0; + int re; + + if (t&32) prefix += 9; + pl += 2; + + if (p<0 || p>=LDBL_MANT_DIG/4-1) re=0; + else re=LDBL_MANT_DIG/4-1-p; + + if (re) { + round *= 1<<(LDBL_MANT_DIG%4); + while (re--) round*=16; + if (*prefix=='-') { + y=-y; + y-=round; + y+=round; + y=-y; + } else { + y+=round; + y-=round; + } + } + + estr=fmt_u(e2<0 ? -e2 : e2, ebuf); + if (estr==ebuf) *--estr='0'; + *--estr = (e2<0 ? '-' : '+'); + *--estr = t+('p'-'a'); + + s=buf; + do { + int x=y; + *s++=xdigits[x]|(t&32); + y=16*(y-x); + if (s-buf==1 && (y||p>0||(fl&ALT_FORM))) *s++='.'; + } while (y); + + if (p > INT_MAX-2-(ebuf-estr)-pl) + return -1; + if (p && s-buf-2 < p) + l = (p+2) + (ebuf-estr); + else + l = (s-buf) + (ebuf-estr); + + pad(f, ' ', w, pl+l, fl); + out(f, prefix, pl); + pad(f, '0', w, pl+l, fl^ZERO_PAD); + out(f, buf, s-buf); + pad(f, '0', l-(ebuf-estr)-(s-buf), 0, 0); + out(f, estr, ebuf-estr); + pad(f, ' ', w, pl+l, fl^LEFT_ADJ); + return MAX(w, pl+l); + } + if (p<0) p=6; + + if (y) y *= 0x1p28, e2-=28; + + if (e2<0) a=r=z=big; + else a=r=z=big+sizeof(big)/sizeof(*big) - LDBL_MANT_DIG - 1; + + do { + *z = y; + y = 1000000000*(y-*z++); + } while (y); + + while (e2>0) { + uint32_t carry=0; + int sh=MIN(29,e2); + for (d=z-1; d>=a; d--) { + uint64_t x = ((uint64_t)*d<a && !z[-1]) z--; + e2-=sh; + } + while (e2<0) { + uint32_t carry=0, *b; + int sh=MIN(9,-e2), need=1+(p+LDBL_MANT_DIG/3U+8)/9; + for (d=a; d>sh) + carry; + carry = (1000000000>>sh) * rm; + } + if (!*a) a++; + if (carry) *z++ = carry; + /* Avoid (slow!) computation past requested precision */ + b = (t|32)=='f' ? r : a; + if (z-b > need) z = b+need; + e2+=sh; + } + + if (a=i; i*=10, e++); + else e=0; + + /* Perform rounding: j is precision after the radix (possibly neg) */ + j = p - ((t|32)!='f')*e - ((t|32)=='g' && p); + if (j < 9*(z-r-1)) { + uint32_t x; + /* We avoid C's broken division of negative numbers */ + d = r + 1 + ((j+9*LDBL_MAX_EXP)/9 - LDBL_MAX_EXP); + j += 9*LDBL_MAX_EXP; + j %= 9; + for (i=10, j++; j<9; i*=10, j++); + x = *d % i; + /* Are there any significant digits past j? */ + if (x || d+1!=z) { + long double round = 2/LDBL_EPSILON; + long double small; + if ((*d/i & 1) || (i==1000000000 && d>a && (d[-1]&1))) + round += 2; + if (x 999999999) { + *d--=0; + if (d=i; i*=10, e++); + } + } + if (z>d+1) z=d+1; + } + for (; z>a && !z[-1]; z--); + + if ((t|32)=='g') { + if (!p) p++; + if (p>e && e>=-4) { + t--; + p-=e+1; + } else { + t-=2; + p--; + } + if (!(fl&ALT_FORM)) { + /* Count trailing zeros in last place */ + if (z>a && z[-1]) for (i=10, j=0; z[-1]%i==0; i*=10, j++); + else j=9; + if ((t|32)=='f') + p = MIN(p,MAX(0,9*(z-r-1)-j)); + else + p = MIN(p,MAX(0,9*(z-r-1)+e-j)); + } + } + if (p > INT_MAX-1-(p || (fl&ALT_FORM))) + return -1; + l = 1 + p + (p || (fl&ALT_FORM)); + if ((t|32)=='f') { + if (e > INT_MAX-l) return -1; + if (e>0) l+=e; + } else { + estr=fmt_u(e<0 ? -e : e, ebuf); + while(ebuf-estr<2) *--estr='0'; + *--estr = (e<0 ? '-' : '+'); + *--estr = t; + if (ebuf-estr > INT_MAX-l) return -1; + l += ebuf-estr; + } + + if (l > INT_MAX-pl) return -1; + pad(f, ' ', w, pl+l, fl); + out(f, prefix, pl); + pad(f, '0', w, pl+l, fl^ZERO_PAD); + + if ((t|32)=='f') { + if (a>r) a=r; + for (d=a; d<=r; d++) { + char *s = fmt_u(*d, buf+9); + if (d!=a) while (s>buf) *--s='0'; + else if (s==buf+9) *--s='0'; + out(f, s, buf+9-s); + } + if (p || (fl&ALT_FORM)) out(f, ".", 1); + for (; d0; d++, p-=9) { + char *s = fmt_u(*d, buf+9); + while (s>buf) *--s='0'; + out(f, s, MIN(9,p)); + } + pad(f, '0', p+9, 9, 0); + } else { + if (z<=a) z=a+1; + for (d=a; d=0; d++) { + char *s = fmt_u(*d, buf+9); + if (s==buf+9) *--s='0'; + if (d!=a) while (s>buf) *--s='0'; + else { + out(f, s++, 1); + if (p>0||(fl&ALT_FORM)) out(f, ".", 1); + } + out(f, s, MIN(buf+9-s, p)); + p -= buf+9-s; + } + pad(f, '0', p+18, 18, 0); + out(f, estr, ebuf-estr); + } + + pad(f, ' ', w, pl+l, fl^LEFT_ADJ); + + return MAX(w, pl+l); +} + +static int getint(char **s) { + int i; + for (i=0; isdigit(**s); (*s)++) { + if (i > INT_MAX/10U || **s-'0' > INT_MAX-10*i) i = -1; + else i = 10*i + (**s-'0'); + } + return i; +} + +static int printf_core(FILE *f, const char *fmt, va_list *ap, union arg *nl_arg, int *nl_type) +{ + char *a, *z, *s=(char *)fmt; + unsigned l10n=0, fl; + int w, p, xp; + union arg arg; + int argpos; + unsigned st, ps; + int cnt=0, l=0; + size_t i; + char buf[sizeof(uintmax_t)*3+3+LDBL_MANT_DIG/4]; + const char *prefix; + int t, pl; + wchar_t wc[2], *ws; + char mb[4]; + + for (;;) { + /* This error is only specified for snprintf, but since it's + * unspecified for other forms, do the same. Stop immediately + * on overflow; otherwise %n could produce wrong results. */ + if (l > INT_MAX - cnt) goto overflow; + + /* Update output count, end loop when fmt is exhausted */ + cnt += l; + if (!*s) break; + + /* Handle literal text and %% format specifiers */ + for (a=s; *s && *s!='%'; s++); + for (z=s; s[0]=='%' && s[1]=='%'; z++, s+=2); + if (z-a > INT_MAX-cnt) goto overflow; + l = z-a; + if (f) out(f, a, l); + if (l) continue; + + if (isdigit(s[1]) && s[2]=='$') { + l10n=1; + argpos = s[1]-'0'; + s+=3; + } else { + argpos = -1; + s++; + } + + /* Read modifier flags */ + for (fl=0; (unsigned)*s-' '<32 && (FLAGMASK&(1U<<*(s-' '))); s++) + fl |= 1U<<*(s-' '); + + /* Read field width */ + if (*s=='*') { + if (isdigit(s[1]) && s[2]=='$') { + l10n=1; + nl_type[s[1]-'0'] = INT; + w = nl_arg[s[1]-'0'].i; + s+=3; + } else if (!l10n) { + w = f ? va_arg(*ap, int) : 0; + s++; + } else goto inval; + if (w<0) fl|=LEFT_ADJ, w=-w; + } else if ((w=getint(&s))<0) goto overflow; + + /* Read precision */ + if (*s=='.' && s[1]=='*') { + if (isdigit(s[2]) && s[3]=='$') { + nl_type[s[2]-'0'] = INT; + p = nl_arg[s[2]-'0'].i; + s+=4; + } else if (!l10n) { + p = f ? va_arg(*ap, int) : 0; + s+=2; + } else goto inval; + xp = (p>=0); + } else if (*s=='.') { + s++; + p = getint(&s); + xp = 1; + } else { + p = -1; + xp = 0; + } + + /* Format specifier state machine */ + st=0; + do { + if (OOB(*s)) goto inval; + ps=st; + st=states[st]S(*s++); + } while (st-1=0) goto inval; + } else { + if (argpos>=0) nl_type[argpos]=st, arg=nl_arg[argpos]; + else if (f) pop_arg(&arg, st, ap); + else return 0; + } + + if (!f) continue; + + z = buf + sizeof(buf); + prefix = "-+ 0x0x"; + pl = 0; + t = s[-1]; + + /* Transform ls,lc -> S,C */ + if (ps && (t&15)==3) t&=~32; + + /* - and 0 flags are mutually exclusive */ + if (fl & LEFT_ADJ) fl &= ~ZERO_PAD; + + switch(t) { + case 'n': + switch(ps) { + case BARE: *(int *)arg.p = cnt; break; + case LPRE: *(long *)arg.p = cnt; break; + case LLPRE: *(long long *)arg.p = cnt; break; + case HPRE: *(unsigned short *)arg.p = cnt; break; + case HHPRE: *(unsigned char *)arg.p = cnt; break; + case ZTPRE: *(size_t *)arg.p = cnt; break; + case JPRE: *(uintmax_t *)arg.p = cnt; break; + } + continue; + case 'p': case 'P': + p = MAX(p, 2*sizeof(void*)); + t += 8; // 'x' or 'X' + fl |= ALT_FORM; + case 'x': case 'X': + a = fmt_x(arg.i, z, t&32); + if (arg.i && (fl & ALT_FORM)) prefix+=(t>>4), pl=2; + fl |= ZERO_PAD; // FIXME + if (0) { + case 'o': + a = fmt_o(arg.i, z); + if ((fl&ALT_FORM) && pINTMAX_MAX) { + arg.i=-arg.i; + } else if (fl & MARK_POS) { + prefix++; + } else if (fl & PAD_POS) { + prefix+=2; + } else pl=0; + case 'u': + a = fmt_u(arg.i, z); + } + if (xp && p<0) goto overflow; + if (xp) fl &= ~ZERO_PAD; + if (!arg.i && !p) { + a=z; + break; + } + p = MAX(p, z-a + !arg.i); + break; + case 'c': + *(a=z-(p=1))=arg.i; + fl &= ~ZERO_PAD; + break; + case 'm': + if (1) a = strerror(errno); else + case 's': + a = arg.p ? arg.p : "(null)"; + z = a + strnlen(a, p<0 ? INT_MAX : p); + if (p<0 && *z) goto overflow; + p = z-a; + fl &= ~ZERO_PAD; + break; + case 'C': + wc[0] = arg.i; + wc[1] = 0; + arg.p = wc; + p = -1; + case 'S': + ws = arg.p; + for (i=l=0; i

=0 && l<=p-i; i+=l); + if (l<0) return -1; + if (i > INT_MAX) goto overflow; + p = i; + pad(f, ' ', w, p, fl); + ws = arg.p; + for (i=0; i<0U+p && *ws && i+(l=wctomb(mb, *ws++))<=p; i+=l) + out(f, mb, l); + pad(f, ' ', w, p, fl^LEFT_ADJ); + l = w>p ? w : p; + continue; + case 'e': case 'f': case 'g': case 'a': + case 'E': case 'F': case 'G': case 'A': + if (xp && p<0) goto overflow; + l = fmt_fp(f, arg.f, w, p, fl, t); + if (l<0) goto overflow; + continue; + } + + if (p < z-a) p = z-a; + if (p > INT_MAX-pl) goto overflow; + if (w < pl+p) w = pl+p; + if (w > INT_MAX-cnt) goto overflow; + + pad(f, ' ', w, pl+p, fl); + out(f, prefix, pl); + pad(f, '0', w, pl+p, fl^ZERO_PAD); + pad(f, '0', p, z-a, 0); + out(f, a, z-a); + pad(f, ' ', w, pl+p, fl^LEFT_ADJ); + + l = w; + } + + if (f) return cnt; + if (!l10n) return 0; + + for (i=1; i<=NL_ARGMAX && nl_type[i]; i++) + pop_arg(nl_arg+i, nl_type[i], ap); + for (; i<=NL_ARGMAX && !nl_type[i]; i++); + if (i<=NL_ARGMAX) goto inval; + return 1; + +inval: + errno = EINVAL; + return -1; +overflow: + errno = EOVERFLOW; + return -1; +} + +int vfprintf(FILE *restrict f, const char *restrict fmt, va_list ap) +{ + va_list ap2; + int nl_type[NL_ARGMAX+1] = {0}; + union arg nl_arg[NL_ARGMAX+1]; + char internal_buf[80], *saved_buf = 0; + int olderr; + int ret; + + /* the copy allows passing va_list* even if va_list is an array */ + va_copy(ap2, ap); + if (printf_core(0, fmt, &ap2, nl_arg, nl_type) < 0) { + va_end(ap2); + return -1; + } + +// FLOCK(f); + olderr = f->flags & F_ERR; + if (f->mode < 1) f->flags &= ~F_ERR; + if (!f->buf_size) { + saved_buf = f->buf; + f->buf = internal_buf; + f->buf_size = sizeof internal_buf; + f->wpos = f->wbase = f->wend = 0; + } + if (!f->wend && __towrite(f)) ret = -1; + else ret = printf_core(f, fmt, &ap2, nl_arg, nl_type); + if (saved_buf) { + f->write(f, 0, 0); + if (!f->wpos) ret = -1; + f->buf = saved_buf; + f->buf_size = 0; + f->wpos = f->wbase = f->wend = 0; + } + if (f->flags & F_ERR) ret = -1; + f->flags |= olderr; +// FUNLOCK(f); + va_end(ap2); + return ret; +} diff --git a/libc/stdio/vsnprintf.c b/libc/stdio/vsnprintf.c new file mode 100644 index 0000000..fd584e8 --- /dev/null +++ b/libc/stdio/vsnprintf.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include + +struct cookie { + char *s; + size_t n; +}; + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +static size_t sn_write(FILE *f, const char *s, size_t l) { + struct cookie *c = f->cookie; + size_t k = MIN(c->n, f->wpos - f->wbase); + if (k) { + memcpy(c->s, f->wbase, k); + c->s += k; + c->n -= k; + } + k = MIN(c->n, l); + if (k) { + memcpy(c->s, s, k); + c->s += k; + c->n -= k; + } + *c->s = 0; + f->wpos = f->wbase = f->buf; + /* pretend to succeed, even if we discarded extra data */ + return l; +} + +int vsnprintf(char *restrict s, size_t n, const char *restrict fmt, va_list ap) { + char buf[1]; + char dummy[1]; + struct cookie c = {.s = n ? s : dummy, .n = n ? n - 1 : 0}; + FILE f = { + .lbf = EOF, + .write = sn_write, + .lock = -1, + .buf = buf, + .cookie = &c, + }; + + if (n > INT_MAX) { + errno = EOVERFLOW; + return -1; + } + + *c.s = 0; + return vfprintf(&f, fmt, ap); +} \ No newline at end of file diff --git a/libc/string.c b/libc/string.c index df20710..cf50a6e 100644 --- a/libc/string.c +++ b/libc/string.c @@ -1,5 +1,5 @@ #include "string.h" -#include "../kernel/console.h" +#include "stdio.h" #include #include @@ -8,6 +8,7 @@ #define _ONES ((size_t) - 1 / UCHAR_MAX) #define _HIGHS (_ONES * (UCHAR_MAX / 2 + 1)) #define _HASZERO(x) (((x) - _ONES) & ~(x) & _HIGHS) +#define _SS (sizeof(size_t)) // One measly byte at a time... void *memcpy(void *restrict destination, const void *restrict source, size_t num) { @@ -57,6 +58,23 @@ int memcmp(const void *const s1, const void *const s2, size_t n) { return 0; } +void *memchr(const void *src, int c, size_t n) { + const unsigned char *s = src; + c = (unsigned char) c; +#ifdef __GNUC__ + for (; ((uintptr_t) s & _ALIGN) && n && *s != c; s++, n--); + if (n && *s != c) { + typedef size_t __attribute__((__may_alias__)) word; + const word *w; + size_t k = _ONES * c; + for (w = (const void *) s; n >= _SS && !_HASZERO(*w ^ k); w++, n -= _SS); + s = (const void *) w; + } +#endif + for (; n && *s != c; s++, n--); + return n ? (void *) s : 0; +} + size_t strlen(const char *str) { const char *a = str; const size_t *w; @@ -66,6 +84,11 @@ size_t strlen(const char *str) { return str - a; } +size_t strnlen(const char *s, size_t n) { + const char *p = memchr(s, 0, n); + return p ? p - s : n; +} + int strcmp(const char *const str1, const char *const str2) { const unsigned char *p1 = (unsigned char *) str1; const unsigned char *p2 = (unsigned char *) str2; diff --git a/libc/string.h b/libc/string.h index 8573e6a..056d6f6 100644 --- a/libc/string.h +++ b/libc/string.h @@ -13,8 +13,12 @@ void *memset(void *ptr, int value, size_t num); int memcmp(const void *s1, const void *s2, size_t n); +void *memchr(const void *src, int c, size_t n); + size_t strlen(const char *str); +size_t strnlen(const char *s, size_t n); + int strcmp(const char *str1, const char *str2); int strncmp(const char *str1, const char *str2, size_t num); diff --git a/libc/wchar.c b/libc/wchar.c new file mode 100644 index 0000000..f428ceb --- /dev/null +++ b/libc/wchar.c @@ -0,0 +1,27 @@ +#include "wchar.h" +#include "stdio.h" +#include "errno.h" + +#define MB_CUR_MAX 1 // UTF-8 + +size_t wcrtomb(char *restrict s, wchar_t wc) { + if (!s) return 1; + if ((unsigned) wc < 0x80) { + *s = (char) wc; + return 1; + } else if (MB_CUR_MAX == 1) { + if (!IS_CODEUNIT(wc)) { + errno = EILSEQ; + return (size_t) -1; + } + *s = (char) wc; + return 1; + } else { + // utf-8 shit... + } +} + +int wctomb(char *s, wchar_t wc) { + if (!s) return 0; + return (int) wcrtomb(s, wc); +} \ No newline at end of file diff --git a/libc/wchar.h b/libc/wchar.h new file mode 100644 index 0000000..e070923 --- /dev/null +++ b/libc/wchar.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +#define IS_CODEUNIT(c) ((unsigned)(c)-0xdf80 < 0x80) + +typedef __WCHAR_TYPE__ wchar_t; + +int wctomb(char *s, wchar_t wc); + +size_t wcrtomb(char *__restrict, wchar_t); \ No newline at end of file diff --git a/mkhd.sh b/mkhd.sh index 9d9eec6..d2c9baa 100755 --- a/mkhd.sh +++ b/mkhd.sh @@ -4,13 +4,23 @@ if [ ! -f "$1" ]; then exit 1 fi -rm hd.img -truncate -s 100M hd.img -mkfs -t exfat hd.img -LDEV="$(udisksctl loop-setup -f hd.img | cut -d' ' -f5 | head -c-2)" -MPAT="$(udisksctl mount -b "$LDEV" | cut -d' ' -f4 | head -c-2)" -cp "$1" "$MPAT/kernel.bin" -cp "README" "$MPAT/README" -sync -udisksctl unmount -b "$LDEV" -udisksctl loop-delete -b "$LDEV" \ No newline at end of file +rm -f hd.img hd.dmg +if [ "$(uname)" == "Darwin" ]; then + mkdir tmp + cp "$1" tmp/kernel.bin + cp "README" tmp/README + + hdiutil create -size 10m -fs exfat -srcfolder tmp hd + qemu-img convert hd.dmg -O qcow2 hd.img + rm -r hd.dmg tmp +else + truncate -s 100M hd.img + mkfs -t exfat hd.img + LDEV="$(udisksctl loop-setup -f hd.img | cut -d' ' -f5 | head -c-2)" + MPAT="$(udisksctl mount -b "$LDEV" | cut -d' ' -f4 | head -c-2)" + cp "$1" "$MPAT/kernel.bin" + cp "README" "$MPAT/README" + sync + udisksctl unmount -b "$LDEV" + udisksctl loop-delete -b "$LDEV" +fi \ No newline at end of file