diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index af63728..2a4ca37 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -43,7 +43,8 @@ set(SOURCE_FILES tests/tests.h tests/vector tests/fatfs - tests/stdio) + tests/stdio + tests/path) if (${CMAKE_BUILD_TYPE} MATCHES "Debug") set(SOURCE_FILES ${SOURCE_FILES} debug/ubsan debug/dwarf) endif () diff --git a/kernel/div64.c b/kernel/div64.c index 4f39102..b35a64c 100644 --- a/kernel/div64.c +++ b/kernel/div64.c @@ -16,8 +16,7 @@ union overlay64 { } words; }; -uint64_t __ashldi3(uint64_t num, unsigned int shift) -{ +uint64_t __ashldi3(uint64_t num, unsigned int shift) { union overlay64 output; output.longw = num; @@ -25,8 +24,7 @@ uint64_t __ashldi3(uint64_t num, unsigned int shift) output.words.higher = output.words.lower << (shift - 32); output.words.lower = 0; } else { - if (!shift) - return num; + if (!shift) return num; output.words.higher = (output.words.higher << shift) | (output.words.lower >> (32 - shift)); output.words.lower = output.words.lower << shift; @@ -34,8 +32,7 @@ uint64_t __ashldi3(uint64_t num, unsigned int shift) return output.longw; } -uint64_t __lshrdi3(uint64_t num, unsigned int shift) -{ +uint64_t __lshrdi3(uint64_t num, unsigned int shift) { union overlay64 output; output.longw = num; @@ -43,8 +40,7 @@ uint64_t __lshrdi3(uint64_t num, unsigned int shift) output.words.lower = output.words.higher >> (shift - 32); output.words.higher = 0; } else { - if (!shift) - return num; + if (!shift) return num; output.words.lower = output.words.lower >> shift | (output.words.higher << (32 - shift)); output.words.higher = output.words.higher >> shift; @@ -54,16 +50,14 @@ uint64_t __lshrdi3(uint64_t num, unsigned int shift) #define MAX_32BIT_UINT ((((uint64_t)1) << 32) - 1) -static uint64_t _64bit_divide(uint64_t dividend, uint64_t divider, uint64_t *rem_p) -{ +static uint64_t _64bit_divide(uint64_t dividend, uint64_t divider, uint64_t *rem_p) { uint64_t result = 0; /* * If divider is zero - let the rest of the system care about the * exception. */ - if (!divider) - return 1 / (uint32_t)divider; + if (!divider) return 1 / (uint32_t) divider; /* As an optimization, let's not use 64 bit division unless we must. */ if (dividend <= MAX_32BIT_UINT) { @@ -72,9 +66,8 @@ static uint64_t _64bit_divide(uint64_t dividend, uint64_t divider, uint64_t *rem if (rem_p) *rem_p = divider; } else { - result = (uint32_t)dividend / (uint32_t)divider; - if (rem_p) - *rem_p = (uint32_t)dividend % (uint32_t)divider; + result = (uint32_t) dividend / (uint32_t) divider; + if (rem_p) *rem_p = (uint32_t) dividend % (uint32_t) divider; } return result; } @@ -92,8 +85,7 @@ static uint64_t _64bit_divide(uint64_t dividend, uint64_t divider, uint64_t *rem dividend -= locald; } - if (rem_p) - *rem_p = dividend; + if (rem_p) *rem_p = dividend; return result; } @@ -108,4 +100,39 @@ uint64_t __umoddi3(uint64_t num, uint64_t den) { uint64_t v = 0; _64bit_divide(num, den, &v); return v; +} + +// Returns the number of leading 0-bits in x, starting at the most significant bit position. +// If x is zero, the result is undefined. +_unused +int __clzsi2(unsigned x) { + // This uses a binary search (counting down) algorithm from Hacker's Delight. + unsigned y; + int n = 32; + y = x >>16; if (y != 0) {n = n -16; x = y;} + y = x >> 8; if (y != 0) {n = n - 8; x = y;} + y = x >> 4; if (y != 0) {n = n - 4; x = y;} + y = x >> 2; if (y != 0) {n = n - 2; x = y;} + y = x >> 1; if (y != 0) return n - 2; + return n - x; +} + +// Returns the number of trailing 0-bits in x, starting at the least significant bit position. +// If x is zero, the result is undefined. +_unused +int __ctzsi2(unsigned x) { + // This uses a binary search algorithm from Hacker's Delight. + int n = 1; + if ((x & 0x0000FFFF) == 0) {n = n +16; x = x >>16;} + if ((x & 0x000000FF) == 0) {n = n + 8; x = x >> 8;} + if ((x & 0x0000000F) == 0) {n = n + 4; x = x >> 4;} + if ((x & 0x00000003) == 0) {n = n + 2; x = x >> 2;} + return n - (x & 1); +} + +// Returns the index of the least significant 1-bit in x, or the value zero if x is zero. +// The least significant bit is index one. +_unused +int __ffsdi2 (unsigned x) { + return (x == 0) ? 0 : __builtin_ctz(x) + 1; } \ No newline at end of file diff --git a/kernel/drivers/vga.c b/kernel/drivers/vga.c index c845cb3..1272623 100644 --- a/kernel/drivers/vga.c +++ b/kernel/drivers/vga.c @@ -6,18 +6,6 @@ #define VIDEO_ADDRESS ((uint16_t *) 0xC00B8000) -/********************************************************** - * Private kernel functions * - **********************************************************/ - -/** - * Innermost print function for our kernel, directly accesses the video memory - * - * If 'col' and 'row' are negative, we will print at current cursor location - * If 'attr' is zero it will use 'white on black' as default - * Returns the offset of the next character - * Sets the video cursor to the returned offset - */ int vga_print_char(char c, int col, int row, char attr) { if (!attr) attr = WHITE_ON_BLACK; diff --git a/kernel/elf.c b/kernel/elf.c index 04871bf..bb790cd 100644 --- a/kernel/elf.c +++ b/kernel/elf.c @@ -54,7 +54,9 @@ bool _check_elf_header(elf_header_t *header) { } return true; } else { +#ifdef ELF_DEBUG printf("_check_elf_header: Got bad magic %04u != %04u\n", header->magic, ELF_HEADER_MAGIC_LE); +#endif return false; } } @@ -70,7 +72,10 @@ static bool _elf_read_header(elf_file_t *file) { return false; } read = fread(buf, header_size, 1, file->fd); - if (ferror(file->fd) || !read || !_check_elf_header(buf)) return false; // FIXME memory leak + if (ferror(file->fd) || !read || !_check_elf_header(buf)) { + free(buf); + return false; + } file->header = buf; return true; @@ -93,7 +98,10 @@ static bool _elf_read_section_header_table(elf_file_t *file) { return false; } read = fread(buf, sh_table_size, 1, file->fd); - if (ferror(file->fd) || !read) return false; // FIXME memory leak + if (ferror(file->fd) || !read) { + free(buf); + return false; + } file->sht_start = buf; return true; @@ -194,7 +202,8 @@ static const char *elf_section_type(elf_section_header_type_t type) { } void elf_print_sections(elf_file_t *file) { - if (!_elf_read_section_header_table(file) + if (!_elf_read_header(file) + || !_elf_read_section_header_table(file) || !_elf_read_sht_str_section(file)) return; @@ -211,21 +220,15 @@ void elf_print_sections(elf_file_t *file) { } bool elf_open(elf_file_t *file, const char *filename) { - struct stat st; - - if (fstat(filename, &st) || !st.st_size) goto fail; - file->fd = fopen(filename, "r"); - if (ferror(file->fd)) - goto fail; - - if (!_elf_read_header(file) || !_elf_read_section_header_table(file)) goto fail; + if (ferror(file->fd) + || !_elf_read_header(file) + || !_elf_read_section_header_table(file)) { + elf_close(file); + return false; + } return true; - - fail: - printf("Failed to read ELF file: %d\n", errno); - return false; } void elf_close(elf_file_t *file) { diff --git a/kernel/main.c b/kernel/main.c index 443c456..e3efcf6 100644 --- a/kernel/main.c +++ b/kernel/main.c @@ -34,17 +34,19 @@ void kernel_main(uint32_t multiboot_magic, void *multiboot_info) { // uint32_t i = UINT32_MAX / 16; // while(i--); // stall + bool fs_mounted = false; printf("Mounting drive 0... "); FATFS fs; FRESULT ret = f_mount(&fs, "", 1); if (ret == FR_OK) { printf("OK\n"); + fs_mounted = true; } else { printf("fail %d\n", ret); } #ifdef ENABLE_DWARF - dwarf_find_debug_info(); +// dwarf_find_debug_info(); #endif console_set_vga_enabled(vga_enabled); @@ -58,7 +60,7 @@ void kernel_main(uint32_t multiboot_magic, void *multiboot_info) { #ifdef KDEBUG kprint("Setting up IRQ handlers...\n"); #endif - shell_init(); + shell_init(fs_mounted); #ifdef KDEBUG kprint("Enabling maskable interrupts...\n"); diff --git a/kernel/multiboot.c b/kernel/multiboot.c index 6e23762..b6a773a 100644 --- a/kernel/multiboot.c +++ b/kernel/multiboot.c @@ -3,7 +3,7 @@ #include "console.h" #define PAGE_OFFSET 0xC0000000 -#define KERNEL_OFFSET 0x100000 // FIXME +#define KERNEL_OFFSET 0x200000 // FIXME extern void *malloc_memory_start; extern void *malloc_memory_end; @@ -13,6 +13,7 @@ void multiboot_init(uint32_t magic, void *info_ptr) { panic("multiboot_magic: Invalid magic "PRIX32"\n", magic); } + // XXX: uintptr_t hack b/c of compiler optimization which then triggers ubsan ¯\_(ツ)_/¯ struct multiboot_info *info = (struct multiboot_info *) ((uintptr_t) info_ptr + PAGE_OFFSET); printf("multiboot_info = "PRIXPTR", flags = "PRIx32"\n", info, info->flags); diff --git a/kernel/shell.c b/kernel/shell.c index 3fa9f8c..6d78cd4 100644 --- a/kernel/shell.c +++ b/kernel/shell.c @@ -8,12 +8,17 @@ #include "drivers/pci.h" #include "drivers/pci_registry.h" #include "drivers/ata.h" +#include "elf.h" + +// FIXME +#include "fatfs/ff.h" #include #include #include #include #include +#include #define KEY_BUFFER_INITIAL_SIZE 0x100 static char *key_buffer; @@ -24,6 +29,8 @@ static size_t key_buffer_printed; static vc_vector *shell_history; static size_t shell_history_offset = 0; +static bool _fs_mounted = false; + void command_lspci() { vc_vector *pci_devices = pci_get_devices(); for (pci_device_t *device = vc_vector_begin(pci_devices); @@ -49,11 +56,115 @@ void command_lsata() { for (uint8_t i = 0; i < 4; i++) { if (ide_devices[i].reserved == 1) { 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); + printf("%d: %s | %016u sectors | %s\n", i, type_str, ide_devices[i].size, ide_devices[i].model); } } } +static char curr_path[512] = "/"; + +static uint8_t command_mkdir(const char *path) { + if (!_fs_mounted) return 255; + + FRESULT fret; + char buf[512]; + + path_append(buf, curr_path, path, sizeof(buf)); + if ((fret = f_mkdir(buf)) != FR_OK) { + printf("Error mkdir %s: %d\n", buf, fret); + return 1; + } + return 0; +} + +static uint8_t command_cd(const char *path) { + if (!_fs_mounted) return 255; + + DIR dir; + FRESULT fret; + char buf[512]; + + path_append(buf, curr_path, path, sizeof(buf)); + if ((fret = f_opendir(&dir, buf)) != FR_OK) { + printf("Error opening %s: %d\n", buf, fret); + return 1; + } + if ((fret = f_closedir(&dir)) != FR_OK) { + printf("Error closing %s: %d\n", buf, fret); + return 2; + } + strncpy(curr_path, buf, sizeof(curr_path)); + return 0; +} + +static uint8_t command_ls(const char *path) { + if (!_fs_mounted) return 255; + + DIR dir; + FILINFO info; + FRESULT fret; + char buf[512]; + size_t count = 0; + + path_append(buf, curr_path, path, sizeof(buf)); + if ((fret = f_opendir(&dir, buf)) != FR_OK) { + printf("Error opening %s: %d\n", buf, fret); + return 1; + } + fret = f_readdir(&dir, &info); + while (fret == FR_OK && info.fname[0] != 0) { + count++; + printf(". %s\n", info.fname); + fret = f_readdir(&dir, &info); + } + if (fret != FR_OK && fret != FR_NO_FILE) { + printf("Error listing files: %d\n", fret); + return 2; + } + printf("total %zu\n", count); + return 0; +} + +static uint8_t command_rm(const char *path) { + if (!_fs_mounted) return 255; + + FRESULT fret; + char buf[512]; + + path_append(buf, curr_path, path, sizeof(buf)); + if ((fret = f_unlink(buf)) != FR_OK) { + printf("Error removing %s: %d\n", buf, fret); + return 1; + } + return 0; +} + +static uint8_t command_objdump(const char *path) { + if (!_fs_mounted) return 255; + + elf_file_t file = {}; + char buf[512]; + + path_append(buf, curr_path, path, sizeof(buf)); + if (!elf_open(&file, buf)) { + printf("Error opening %s: %d\n", buf, errno); + return 1; + } + + elf_print_sections(&file); + elf_close(&file); + return 0; +} + +static void print_prompt(unsigned char ret) { + if (_fs_mounted) { + printf("%d %s # ", ret, curr_path); + } else { + printf("%d # ", ret); + } + fflush(stdout); +} + static void shell_callback(char *input) { printf("\n"); @@ -67,7 +178,7 @@ static void shell_callback(char *input) { reboot(); } else if (strcmp(input, "clear") == 0) { clear_screen(); - printf("# "); + print_prompt(0); return; } else if (strncmp(input, "echo ", 5) == 0) { printf("%s\n", input + 5); @@ -95,18 +206,31 @@ static void shell_callback(char *input) { ret = (unsigned char) !fatfs_test(); } else if (strcmp(input, "test stdio") == 0) { ret = (unsigned char) !stdio_test(); + } else if (strcmp(input, "test path") == 0) { + ret = (unsigned char) !path_test(); } else if (strcmp(input, "test") == 0 || strncmp(input, "test ", 5) == 0) { - printf("Available tests:\n vector\n fatfs\n"); + printf("Available tests:\n vector\n fatfs\n stdio\n path\n"); } else if (strcmp(input, "lspci") == 0) { command_lspci(); ret = 0; } else if (strcmp(input, "lsata") == 0) { command_lsata(); ret = 0; + } else if (strncmp(input, "mkdir ", 6) == 0) { + ret = command_mkdir(input + 6); + } else if (strncmp(input, "cd ", 3) == 0) { + ret = command_cd(input + 3); + } else if (strncmp(input, "ls ", 3) == 0 || strncmp(input, "ll ", 3) == 0) { + ret = command_ls(input + 3); + } else if (strcmp(input, "ls") == 0 || strcmp(input, "ll") == 0) { + ret = command_ls(NULL); + } else if (strncmp(input, "rm ", 3) == 0) { + ret = command_rm(input + 3); + } else if (strncmp(input, "objdump ", 8) == 0) { + ret = command_objdump(input + 8); } - printf("%d # ", ret); - fflush(stdout); + print_prompt(ret); if (save && input[0] != '\0') { char *value = strdup(input); @@ -118,11 +242,11 @@ static void shell_history_free_func(void *data) { free(*(char **) data); } -void shell_init() { +void shell_init(bool fs_mounted) { + _fs_mounted = fs_mounted; shell_history = vc_vector_create(0x100, sizeof(char *), shell_history_free_func); init_keyboard(); - printf("# "); - fflush(stdout); + print_prompt((unsigned char) !fs_mounted); } void shell_read() { diff --git a/kernel/shell.h b/kernel/shell.h index 3b56c54..bb89e87 100644 --- a/kernel/shell.h +++ b/kernel/shell.h @@ -2,7 +2,7 @@ #include -void shell_init(); +void shell_init(bool fs_mounted); void shell_read(); diff --git a/kernel/tests/path.c b/kernel/tests/path.c new file mode 100644 index 0000000..b3fd681 --- /dev/null +++ b/kernel/tests/path.c @@ -0,0 +1,73 @@ +#include +#include +#include + +#define CHECK(s1, s2, len) { \ + if (strncmp(s1, s2, len) != 0) { \ + printf("path_test fail line %d: Expected: %s, actual: %s\n", __LINE__, s2, s1); \ + return false; \ + } \ +} + +bool path_test() { + char buf[512]; + size_t len = sizeof(buf); + + path_append(buf, "/test", "123", len); + CHECK(buf, "/test/123", len); + + path_append(buf, "/test/", "/123", len); + CHECK(buf, "/123", len); + + path_append(buf, "/test/", "/123/test 456/../test 567", len); + CHECK(buf, "/123/test 567", len); + + path_append(buf, "/test//", "123/", len); + CHECK(buf, "/test/123/", len); + + path_append(buf, "/test", "..", len); + CHECK(buf, "/", len); + + path_append(buf, "/", "..", len); + CHECK(buf, "/", len); + + path_append(buf, "/test/..", "123", len); + CHECK(buf, "/123", len); + + path_append(buf, "/test/", "123/..", len); + CHECK(buf, "/test/", len); + + path_append(buf, "/test/.", "123/./456", len); + CHECK(buf, "/test/123/456", len); + + path_append(buf, "test 1", "test 2", len); + CHECK(buf, "test 1/test 2", len); + + path_append(buf, "/1/2/3/4/5/6/7/8/9/../9", "10/", len); + CHECK(buf, "/1/2/3/4/5/6/7/8/9/10/", len); + + // FIXME later +// path_append(buf, "/1/2/3/4/5/./6/./7/8/9/../9", "10/.", len); +// CHECK(buf, "/1/2/3/4/5/6/7/8/9/10/", len); + + path_append(buf, "/dev/", "", len); + CHECK(buf, "/dev/", len); + + path_append(buf, "/dev/", ".null", len); + CHECK(buf, "/dev/.null", len); + + path_append(buf, "", ".config", len); + CHECK(buf, ".config", len); + + path_append(buf, "", NULL, len); + CHECK(buf, "", len); + + path_append(buf, "test", NULL, len); + CHECK(buf, "test", len); + + path_append(buf, "/test", ".", len); + CHECK(buf, "/test", len); + + printf("path_test: passed\n"); + return true; +} \ No newline at end of file diff --git a/kernel/tests/tests.h b/kernel/tests/tests.h index dea89f8..a56a87a 100644 --- a/kernel/tests/tests.h +++ b/kernel/tests/tests.h @@ -6,4 +6,6 @@ bool vc_vector_run_tests(); bool fatfs_test(); -bool stdio_test(); \ No newline at end of file +bool stdio_test(); + +bool path_test(); \ No newline at end of file diff --git a/libc/malloc.c b/libc/malloc.c index eea1a5a..3eefe9d 100644 --- a/libc/malloc.c +++ b/libc/malloc.c @@ -1,10 +1,11 @@ #include "malloc.h" #include "stdio.h" +#include "../kernel/console.h" #include #include -// #define MALLOC_DEBUG + #define MALLOC_DEBUG void *malloc_memory_start = (void *) 0x1000000; // 1 MiB, start of x86 upper memory void *malloc_memory_end = NULL; @@ -14,7 +15,7 @@ struct chunk_header { struct chunk_header *next; struct chunk_header *prev; size_t size; - bool used; + unsigned short used; }; static_assert(sizeof(struct chunk_header) % 4 == 0, "chunk_header is misaligned"); @@ -29,17 +30,18 @@ static bool try_reclaim(struct chunk_header *start, size_t size) { struct chunk_header *next_chunk = current_chunk->next; if (next_chunk == NULL) { #ifdef MALLOC_DEBUG - kprint("unused until end of chunks\n"); + printf("unused until end of chunks\n"); #endif start->next = NULL; return true; } - unused += ((void *) current_chunk) - ((void *) current_chunk->prev); + unused += (uintptr_t) current_chunk - (uintptr_t) current_chunk->prev; #ifdef MALLOC_DEBUG - kprint("found unused size "); kprint_uint32(unused); kprint("\n"); + printf("found unused size %zu\n", unused); #endif - if (unused >= size) { + // FIXME do i need ALIGN here? + if (unused >= ALIGN(size, 4) + sizeof(struct chunk_header)) { next_chunk->prev = start; start->next = next_chunk; return true; @@ -58,7 +60,7 @@ static void *find_unused_chunk(const size_t chunk_size) { // First chunk @ start of memory #ifdef MALLOC_DEBUG - kprint("new chunk @ start: "); kprint_uint32(chunk_size); kprint("\n"); + printf("new chunk @ start: %zu\n", chunk_size); #endif struct chunk_header *header = malloc_memory_start; memset(header, 0, sizeof(struct chunk_header)); @@ -71,7 +73,7 @@ static void *find_unused_chunk(const size_t chunk_size) { if (header->size >= chunk_size) { // Unused chunk is large enough, go ahead and use it #ifdef MALLOC_DEBUG - kprint("reusing large enough chunk "); kprint_uint32(header->size); kprint(" for "); kprint_uint32(chunk_size); kprint("\n"); + printf("reusing large enough chunk %zu for %zu\n", header->size, chunk_size); #endif return header; } else if (try_reclaim(header, chunk_size)) { @@ -83,7 +85,7 @@ static void *find_unused_chunk(const size_t chunk_size) { // Allocate new chunk if end if (header->next == NULL) { #ifdef MALLOC_DEBUG - kprint("alloc new chunk size: "); kprint_uint32(chunk_size); kprint("\n"); + printf("alloc new chunk size: %zu\n", chunk_size); #endif void *next = (void *) header + sizeof(struct chunk_header) + ALIGN(header->size, 4); if (next + chunk_size > malloc_memory_end) break; // Don't overcommit new chunk @@ -96,11 +98,14 @@ static void *find_unused_chunk(const size_t chunk_size) { // Find space in between chunks uintptr_t end_of_chunk = (uintptr_t) header + sizeof(struct chunk_header) + ALIGN(header->size, 4); + if (end_of_chunk > (uintptr_t) header->next) { + panic("found misaligned chunk %P. Expected end: %P, actual next: %P\n", header, end_of_chunk, header->next); + } uintptr_t unused_size = (uintptr_t) header->next - end_of_chunk; if (unused_size > chunk_size + sizeof(struct chunk_header)) { #ifdef MALLOC_DEBUG - kprint("found size between chunk: "); kprint_uint32(unused_size); kprint(" > "); kprint_uint32(chunk_size); kprint("\n"); - kprint("chunk 1: "); kprint_uint32((uintptr_t) header); kprint(" | chunk 2: "); kprint_uint32((uintptr_t) header->next); kprint("\n"); + printf("found size between chunk: %zu > %zu\n", unused_size, chunk_size); + printf(" chunk 1: %P (size %zu) | chunk 2: %P\n", header, header->size, header->next); #endif struct chunk_header *next_header = (struct chunk_header *) end_of_chunk; memset(next_header, 0, sizeof(struct chunk_header)); @@ -134,12 +139,13 @@ void free(void *ptr) { struct chunk_header *header = ptr - sizeof(struct chunk_header); header->used = false; #ifdef MALLOC_DEBUG - kprint("freeing chunk w/ size "); kprint_uint32(header->size); kprint("\n"); + printf("freeing chunk w/ size %zu\n", header->size); #endif } void *realloc(void *ptr, size_t new_size) { if (ptr == NULL) return malloc(new_size); + struct chunk_header *header = ptr - sizeof(struct chunk_header); if (header->size >= new_size || try_reclaim(header, new_size)) { header->size = new_size; @@ -173,7 +179,8 @@ static unsigned digits(uint32_t n) { 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); + extern int __clzsi2(unsigned); // FIXME + unsigned bits = sizeof(n) * CHAR_BIT - __clzsi2(n); unsigned digits = max_digits[bits]; if (n < powers[digits - 1]) --digits; return digits; @@ -201,7 +208,7 @@ void print_chunk_debug(void *ptr, bool recursive) { struct chunk_header *header = ptr - sizeof(struct chunk_header); do { char *str_size = pretty_bytes(header->size); - printf("chunk @ %p | next: %p, prev: %p, used: %d, size: %s\n", + printf("chunk @ %P | next: %P, prev: %P, used: %d, size: %s\n", header, header->next, header->prev, header->used, str_size); free(str_size); diff --git a/libc/string.c b/libc/string.c index b7d3080..4a0039c 100644 --- a/libc/string.c +++ b/libc/string.c @@ -123,7 +123,7 @@ char *strncpy(char *restrict s1, const char *restrict s2, size_t n) { return rc; } -char *strdup(char *str) { +char *strdup(const char *str) { size_t len = strlen(str) + 1; char *new = malloc(len); if (new == NULL) return NULL; @@ -152,3 +152,119 @@ char *strchr(const char *s, int c) { char *r = __strchrnul(s, c); return *(unsigned char *) r == (unsigned char) c ? r : 0; } + +size_t strlcpy(char *d, const char *s, size_t n) { + char *d0 = d; + size_t *wd; + + if (!n--) goto finish; +#ifdef __GNUC__ + typedef size_t __attribute__((__may_alias__)) word; + const word *ws; + if (((uintptr_t) s & _ALIGN) == ((uintptr_t) d & _ALIGN)) { + for (; ((uintptr_t) s & _ALIGN) && n && (*d = *s); n--, s++, d++); + if (n && *s) { + wd = (void *) d; + ws = (const void *) s; + for (; n >= sizeof(size_t) && !_HASZERO(*ws); + n -= sizeof(size_t), ws++, wd++) + *wd = *ws; + d = (void *) wd; + s = (const void *) ws; + } + } +#endif + for (; n && (*d = *s); n--, s++, d++); + *d = 0; + finish: + return d - d0 + strlen(s); +} + + +#define PATH_LOOKAHEAD(p, i, c) ((i < len) && (p[i] == c)) + +// FIXME insane crazy stupid code but it works, I guess +void path_append(char *dest, const char *path, const char *app, size_t len) { + size_t i = 0, out_idx = 0, dir_idx = 0; + + // Skip existing path if necessary + if (app != NULL && app[0] == '/') goto append; + + while (i < len && out_idx < len && path[i] != 0) { + if (path[i] == '/') { + if (PATH_LOOKAHEAD(path, i + 1, '.')) { + if (PATH_LOOKAHEAD(path, i + 2, '.') + && (PATH_LOOKAHEAD(path, i + 3, '/') + || PATH_LOOKAHEAD(path, i + 3, 0))) { + out_idx = dir_idx + 1; + i += 3; + while (PATH_LOOKAHEAD(path, i, '/')) i++; + continue; + } else if (PATH_LOOKAHEAD(path, i + 2, '/') + || PATH_LOOKAHEAD(path, i + 2, 0)) { + i += 2; + continue; + } + } else if (PATH_LOOKAHEAD(path, i + 1, '/')) { + i++; + continue; + } + dir_idx = out_idx; + } + + dest[out_idx] = path[i]; + i++; + out_idx++; + } + + i = 0; + if (app == NULL || app[0] == 0) goto close; + + // Check for leading . or .. + if (app[i] == '.') { + if (PATH_LOOKAHEAD(app, i + 1, '/')) { + i += 2; + } else if (PATH_LOOKAHEAD(app, i + 1, 0)) { + goto close; + } else if (PATH_LOOKAHEAD(app, i + 1, '.') + && (PATH_LOOKAHEAD(app, i + 2, '/') + || PATH_LOOKAHEAD(app, i + 2, 0))) { + out_idx = dir_idx + 1; + i += 2; + } + } + + if (out_idx && dest[out_idx - 1] != '/' && out_idx < len) dest[dir_idx = out_idx++] = '/'; // Add trailing slash + while (i < len && app[i] == '/') i++; // Skip leading slash(es) + + append: + while (i < len && out_idx < len && app[i] != 0) { + if (app[i] == '/') { + if (PATH_LOOKAHEAD(app, i + 1, '.')) { + if (PATH_LOOKAHEAD(app, i + 2, '.') + && (PATH_LOOKAHEAD(app, i + 3, '/') + || PATH_LOOKAHEAD(app, i + 3, 0))) { + out_idx = dir_idx + 1; + i += 3; + while (PATH_LOOKAHEAD(app, i, '/')) i++; + continue; + } else if (PATH_LOOKAHEAD(app, i + 2, '/') + || PATH_LOOKAHEAD(app, i + 2, 0)) { + i += 2; + continue; + } + } else if (PATH_LOOKAHEAD(app, i + 1, '/')) { + i++; + continue; + } + dir_idx = out_idx; + } + + dest[out_idx] = app[i]; + i++; + out_idx++; + } + + close: + dest[out_idx] = 0; +} \ No newline at end of file diff --git a/libc/string.h b/libc/string.h index 9391713..d950151 100644 --- a/libc/string.h +++ b/libc/string.h @@ -27,6 +27,10 @@ char *strcpy(char *destination, const char *source); char *strncpy(char *restrict s1, const char *restrict s2, size_t n); -char *strdup(char *str); +char *strdup(const char *str); -char *strchr(const char *s, int c); \ No newline at end of file +char *strchr(const char *s, int c); + +size_t strlcpy(char *d, const char *s, size_t n); + +void path_append(char *dest, const char *path, const char *app, size_t len); \ No newline at end of file