Primitive VFS & commands; debugging malloc issues

This commit is contained in:
Luke Street
2018-10-02 00:22:36 -04:00
parent 82c0fe012f
commit 1bbf6aeeaf
13 changed files with 423 additions and 75 deletions
+2 -1
View File
@@ -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 ()
+44 -17
View File
@@ -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;
}
-12
View File
@@ -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;
+18 -15
View File
@@ -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) {
+4 -2
View File
@@ -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");
+2 -1
View File
@@ -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);
+132 -8
View File
@@ -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 <string.h>
#include <malloc.h>
#include <vector.h>
#include <math.h>
#include <stdio.h>
#include <errno.h>
#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() {
+1 -1
View File
@@ -2,7 +2,7 @@
#include <common.h>
void shell_init();
void shell_init(bool fs_mounted);
void shell_read();
+73
View File
@@ -0,0 +1,73 @@
#include <common.h>
#include <string.h>
#include <stdio.h>
#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;
}
+3 -1
View File
@@ -6,4 +6,6 @@ bool vc_vector_run_tests();
bool fatfs_test();
bool stdio_test();
bool stdio_test();
bool path_test();
+21 -14
View File
@@ -1,10 +1,11 @@
#include "malloc.h"
#include "stdio.h"
#include "../kernel/console.h"
#include <string.h>
#include <limits.h>
// #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);
+117 -1
View File
@@ -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;
}
+6 -2
View File
@@ -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);
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);