Browse Source

Get Odin to compile on Haiku

This patch makes Odin to compile on Haiku which is a good first step.
Now, all that's needed to do is to figure out how to do futexes, which
I am blaming for the program crashing.
Slendi 1 year ago
parent
commit
c178f7199d
4 changed files with 559 additions and 464 deletions
  1. 5 0
      build_odin.sh
  2. 47 2
      src/gb/gb.h
  3. 461 461
      src/path.cpp
  4. 46 1
      src/threading.cpp

+ 5 - 0
build_odin.sh

@@ -83,6 +83,11 @@ OpenBSD)
 	LDFLAGS="$LDFLAGS -liconv"
 	LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
 	;;
+Haiku)
+	CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags) -I/system/develop/headers/private/shared -I/system/develop/headers/private/kernel"
+	LDFLAGS="$LDFLAGS -liconv"
+	LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
+	;;
 *)
 	error "Platform \"$OS_NAME\" unsupported"
 	;;

+ 47 - 2
src/gb/gb.h

@@ -83,6 +83,10 @@ extern "C" {
 		#ifndef GB_SYSTEM_OPENBSD
 		#define GB_SYSTEM_OPENBSD 1
 		#endif
+	#elif defined(__HAIKU__) || defined(__haiku__)
+		#ifndef GB_SYSTEM_HAIKU
+		#define GB_SYSTEM_HAIKU 1
+		#endif
 	#else
 		#error This UNIX operating system is not supported
 	#endif
@@ -206,7 +210,7 @@ extern "C" {
 	#endif
 	#include <stdlib.h> // NOTE(bill): malloc on linux
 	#include <sys/mman.h>
-	#if !defined(GB_SYSTEM_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
+	#if !defined(GB_SYSTEM_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__HAIKU__)
 		#include <sys/sendfile.h>
 	#endif
 	#include <sys/stat.h>
@@ -247,6 +251,13 @@ extern "C" {
 	#include <pthread_np.h>
 	#define lseek64 lseek
 #endif
+
+#if defined(GB_SYSTEM_HAIKU)
+	#include <stdio.h>
+	#include <pthread.h>
+	#include <kernel/OS.h>
+	#define lseek64 lseek
+#endif
     
 #if defined(GB_SYSTEM_UNIX)
 	#include <semaphore.h>
@@ -801,6 +812,13 @@ typedef struct gbAffinity {
 	isize thread_count;
 	isize threads_per_core;
 } gbAffinity;
+#elif defined(GB_SYSTEM_HAIKU)
+typedef struct gbAffinity {
+	b32   is_accurate;
+	isize core_count;
+	isize thread_count;
+	isize threads_per_core;
+} gbAffinity;
 #else
 #error TODO(bill): Unknown system
 #endif
@@ -2984,6 +3002,8 @@ gb_inline u32 gb_thread_current_id(void) {
 	__asm__("mov %%fs:0x10,%0" : "=r"(thread_id));
 #elif defined(GB_SYSTEM_LINUX)
 	thread_id = gettid();
+#elif defined(GB_SYSTEM_HAIKU)
+	thread_id = find_thread(NULL);
 #else
 	#error Unsupported architecture for gb_thread_current_id()
 #endif
@@ -3184,7 +3204,9 @@ b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) {
 	//info.affinity_tag = cast(integer_t)index;
 	//result = thread_policy_set(thread, THREAD_AFFINITY_POLICY, cast(thread_policy_t)&info, THREAD_AFFINITY_POLICY_COUNT);
 
+#if !defined(GB_SYSTEM_HAIKU)
 	result = pthread_setaffinity_np(thread, sizeof(cpuset_t), &mn);
+#endif
 	return result == 0;
 }
 
@@ -3236,6 +3258,29 @@ b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) {
 	return true;
 }
 
+isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) {
+	GB_ASSERT(0 <= core && core < a->core_count);
+	return a->threads_per_core;
+}
+#elif defined(GB_SYSTEM_HAIKU)
+#include <unistd.h>
+
+void gb_affinity_init(gbAffinity *a) {
+	a->core_count       = sysconf(_SC_NPROCESSORS_ONLN);
+	a->threads_per_core = 1;
+	a->is_accurate      = a->core_count > 0;
+	a->core_count       = a->is_accurate ? a->core_count : 1;
+	a->thread_count     = a->core_count;
+}
+
+void gb_affinity_destroy(gbAffinity *a) {
+	gb_unused(a);
+}
+
+b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) {
+	return true;
+}
+
 isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) {
 	GB_ASSERT(0 <= core && core < a->core_count);
 	return a->threads_per_core;
@@ -5457,7 +5502,7 @@ gb_inline b32 gb_file_copy(char const *existing_filename, char const *new_filena
 		}
 	}
 	
-	gb_free(buf);
+	gb_mfree(buf);
 	close(new_fd);
 	close(existing_fd);
 

+ 461 - 461
src/path.cpp

@@ -1,461 +1,461 @@
-/*
-	Path handling utilities.
-*/
-#if !defined(GB_SYSTEM_WINDOWS)
-#include <unistd.h>
-#endif
-
-gb_internal String remove_extension_from_path(String const &s) {
-	if (s.len != 0 && s.text[s.len-1] == '.') {
-		return s;
-	}
-	for (isize i = s.len-1; i >= 0; i--) {
-		if (s[i] == '.') {
-			return substring(s, 0, i);
-		}
-	}
-	return s;
-}
-
-gb_internal String remove_directory_from_path(String const &s) {
-	isize len = 0;
-	for (isize i = s.len-1; i >= 0; i--) {
-		if (s[i] == '/' ||
-		    s[i] == '\\') {
-			break;
-		}
-		len += 1;
-	}
-	return substring(s, s.len-len, s.len);
-}
-
-
-// NOTE(Mark Naughton): getcwd as String
-#if !defined(GB_SYSTEM_WINDOWS)
-gb_internal String get_current_directory(void) {
-	char cwd[256];
-	getcwd(cwd, 256);
-
-	return make_string_c(cwd);
-}
-
-#else
-gb_internal String get_current_directory(void) {
-	gbAllocator a = heap_allocator();
-
-	wchar_t cwd[256];
-	GetCurrentDirectoryW(256, cwd);
-
-	String16 wstr = make_string16_c(cwd);
-
-	return string16_to_string(a, wstr);
-}
-#endif
-
-gb_internal bool path_is_directory(String path);
-
-gb_internal String directory_from_path(String const &s) {
-	if (path_is_directory(s)) {
-		return s;
-	}
-
-	isize i = s.len-1;
-	for (; i >= 0; i--) {
-		if (s[i] == '/' ||
-		    s[i] == '\\') {
-			break;
-		}
-	}
-	if (i >= 0) {
-		return substring(s, 0, i);	
-	}
-	return substring(s, 0, 0);
-}
-
-#if defined(GB_SYSTEM_WINDOWS)
-	gb_internal bool path_is_directory(String path) {
-		gbAllocator a = heap_allocator();
-		String16 wstr = string_to_string16(a, path);
-		defer (gb_free(a, wstr.text));
-
-		i32 attribs = GetFileAttributesW(wstr.text);
-		if (attribs < 0) return false;
-
-		return (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0;
-	}
-
-#else
-	gb_internal bool path_is_directory(String path) {
-		gbAllocator a = heap_allocator();
-		char *copy = cast(char *)copy_string(a, path).text;
-		defer (gb_free(a, copy));
-
-		struct stat s;
-		if (stat(copy, &s) == 0) {
-			return (s.st_mode & S_IFDIR) != 0;
-		}
-		return false;
-	}
-#endif
-
-
-gb_internal String path_to_full_path(gbAllocator a, String path) {
-	gbAllocator ha = heap_allocator();
-	char *path_c = gb_alloc_str_len(ha, cast(char *)path.text, path.len);
-	defer (gb_free(ha, path_c));
-
-	char *fullpath = gb_path_get_full_name(a, path_c);
-	String res = string_trim_whitespace(make_string_c(fullpath));
-#if defined(GB_SYSTEM_WINDOWS)
-	for (isize i = 0; i < res.len; i++) {
-		if (res.text[i] == '\\') {
-			res.text[i] = '/';
-		}
-	}
-#endif
-	return copy_string(a, res);
-}
-
-struct Path {
-	String basename;
-	String name;
-	String ext;
-};
-
-// NOTE(Jeroen): Naively turns a Path into a string.
-gb_internal String path_to_string(gbAllocator a, Path path) {
-	if (path.basename.len + path.name.len + path.ext.len == 0) {
-		return make_string(nullptr, 0);
-	}
-
-	isize len = path.basename.len + 1 + path.name.len + 1;
-	if (path.ext.len > 0) {
-		 len += path.ext.len + 1;
-	}
-
-	u8 *str = gb_alloc_array(a, u8, len);
-
-	isize i = 0;
-	gb_memmove(str+i, path.basename.text, path.basename.len); i += path.basename.len;
-	
-	gb_memmove(str+i, "/", 1);                                i += 1;
-	
-	gb_memmove(str+i, path.name.text,     path.name.len);     i += path.name.len;
-	if (path.ext.len > 0) {
-		gb_memmove(str+i, ".", 1);                            i += 1;
-		gb_memmove(str+i, path.ext.text,  path.ext.len);      i += path.ext.len;
-	}
-	str[i] = 0;
-
-	String res = make_string(str, i);
-	res        = string_trim_whitespace(res);
-	return res;
-}
-
-// NOTE(Jeroen): Naively turns a Path into a string, then normalizes it using `path_to_full_path`.
-gb_internal String path_to_full_path(gbAllocator a, Path path) {
-	String temp = path_to_string(heap_allocator(), path);
-	defer (gb_free(heap_allocator(), temp.text));
-
-	return path_to_full_path(a, temp);
-}
-
-// NOTE(Jeroen): Takes a path like "odin" or "W:\Odin", turns it into a full path,
-// and then breaks it into its components to make a Path.
-gb_internal Path path_from_string(gbAllocator a, String const &path) {
-	Path res = {};
-
-	if (path.len == 0) return res;
-
-	String fullpath = path_to_full_path(a, path);
-	defer (gb_free(heap_allocator(), fullpath.text));
-
-	res.basename = directory_from_path(fullpath);	
-	res.basename = copy_string(a, res.basename);
-
-	if (path_is_directory(fullpath)) {
-		// It's a directory. We don't need to tinker with the name and extension.
-		// It could have a superfluous trailing `/`. Remove it if so.
-		if (res.basename.len > 0 && res.basename.text[res.basename.len - 1] == '/') {
-			res.basename.len--;
-		}
-		return res;
-	}
-
-	// Note(Dragos): Is the copy_string required if it's a substring?
-	isize name_start = (res.basename.len > 0) ? res.basename.len + 1 : res.basename.len;
-	res.name         = substring(fullpath, name_start, fullpath.len);
-	res.name         = remove_extension_from_path(res.name);
-	res.name         = copy_string(a, res.name);
-
-	res.ext          = path_extension(fullpath, false); // false says not to include the dot.
-	res.ext          = copy_string(a, res.ext);
-	return res;
-}
-
-// NOTE(Jeroen): Takes a path String and returns the last path element.
-gb_internal String last_path_element(String const &path) {
-	isize count = 0;
-	u8 * start = (u8 *)(&path.text[path.len - 1]);
-	for (isize length = path.len; length > 0 && path.text[length - 1] != '/'; length--) {
-		count++;
-		start--;
-	}
-	if (count > 0) {
-		start++; // Advance past the `/` and return the substring.
-		String res = make_string(start, count);
-		return res;
-	}
-	// Must be a root path like `/` or `C:/`, return empty String.
-	return STR_LIT("");
-}
-
-gb_internal bool path_is_directory(Path path) {
-	String path_string = path_to_full_path(heap_allocator(), path);
-	defer (gb_free(heap_allocator(), path_string.text));
-
-	return path_is_directory(path_string);
-}
-
-struct FileInfo {
-	String name;
-	String fullpath;
-	i64    size;
-	bool   is_dir;
-};
-
-enum ReadDirectoryError {
-	ReadDirectory_None,
-
-	ReadDirectory_InvalidPath,
-	ReadDirectory_NotExists,
-	ReadDirectory_Permission,
-	ReadDirectory_NotDir,
-	ReadDirectory_Empty,
-	ReadDirectory_Unknown,
-
-	ReadDirectory_COUNT,
-};
-
-gb_internal i64 get_file_size(String path) {
-	char *c_str = alloc_cstring(heap_allocator(), path);
-	defer (gb_free(heap_allocator(), c_str));
-
-	gbFile f = {};
-	gbFileError err = gb_file_open(&f, c_str);
-	defer (gb_file_close(&f));
-	if (err != gbFileError_None) {
-		return -1;
-	}
-	return gb_file_size(&f);
-}
-
-
-#if defined(GB_SYSTEM_WINDOWS)
-gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
-	GB_ASSERT(fi != nullptr);
-
-
-	while (path.len > 0) {
-		Rune end = path[path.len-1];
-		if (end == '/') {
-			path.len -= 1;
-		} else if (end == '\\') {
-			path.len -= 1;
-		} else {
-			break;
-		}
-	}
-
-	if (path.len == 0) {
-		return ReadDirectory_InvalidPath;
-	}
-	{
-		char *c_str = alloc_cstring(temporary_allocator(), path);
-		gbFile f = {};
-		gbFileError file_err = gb_file_open(&f, c_str);
-		defer (gb_file_close(&f));
-
-		switch (file_err) {
-		case gbFileError_Invalid:    return ReadDirectory_InvalidPath;
-		case gbFileError_NotExists:  return ReadDirectory_NotExists;
-		// case gbFileError_Permission: return ReadDirectory_Permission;
-		}
-	}
-
-	if (!path_is_directory(path)) {
-		return ReadDirectory_NotDir;
-	}
-
-
-	gbAllocator a = heap_allocator();
-	char *new_path = gb_alloc_array(a, char, path.len+3);
-	defer (gb_free(a, new_path));
-
-	gb_memmove(new_path, path.text, path.len);
-	gb_memmove(new_path+path.len, "/*", 2);
-	new_path[path.len+2] = 0;
-
-	String np = make_string(cast(u8 *)new_path, path.len+2);
-	String16 wstr = string_to_string16(a, np);
-	defer (gb_free(a, wstr.text));
-
-	WIN32_FIND_DATAW file_data = {};
-	HANDLE find_file = FindFirstFileW(wstr.text, &file_data);
-	if (find_file == INVALID_HANDLE_VALUE) {
-		return ReadDirectory_Unknown;
-	}
-	defer (FindClose(find_file));
-
-	array_init(fi, a, 0, 100);
-
-	do {
-		wchar_t *filename_w = file_data.cFileName;
-		u64 size = cast(u64)file_data.nFileSizeLow;
-		size |= (cast(u64)file_data.nFileSizeHigh) << 32;
-		String name = string16_to_string(a, make_string16_c(filename_w));
-		if (name == "." || name == "..") {
-			gb_free(a, name.text);
-			continue;
-		}
-
-		String filepath = {};
-		filepath.len = path.len+1+name.len;
-		filepath.text = gb_alloc_array(a, u8, filepath.len+1);
-		defer (gb_free(a, filepath.text));
-		gb_memmove(filepath.text, path.text, path.len);
-		gb_memmove(filepath.text+path.len, "/", 1);
-		gb_memmove(filepath.text+path.len+1, name.text, name.len);
-
-		FileInfo info = {};
-		info.name = name;
-		info.fullpath = path_to_full_path(a, filepath);
-		info.size = cast(i64)size;
-		info.is_dir = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
-		array_add(fi, info);
-	} while (FindNextFileW(find_file, &file_data));
-
-	if (fi->count == 0) {
-		return ReadDirectory_Empty;
-	}
-
-	return ReadDirectory_None;
-}
-#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD)
-
-#include <dirent.h>
-
-gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
-	GB_ASSERT(fi != nullptr);
-
-	gbAllocator a = heap_allocator();
-
-	char *c_path = alloc_cstring(a, path);
-	defer (gb_free(a, c_path));
-
-	DIR *dir = opendir(c_path);
-	if (!dir) {
-		switch (errno) {
-		case ENOENT:
-			return ReadDirectory_NotExists;
-		case EACCES:
-			return ReadDirectory_Permission;
-		case ENOTDIR:
-			return ReadDirectory_NotDir;
-		default:
-			// ENOMEM: out of memory
-			// EMFILE: per-process limit on open fds reached
-			// ENFILE: system-wide limit on total open files reached
-			return ReadDirectory_Unknown;
-		}
-		GB_PANIC("unreachable");
-	}
-
-	array_init(fi, a, 0, 100);
-
-	for (;;) {
-		struct dirent *entry = readdir(dir);
-		if (entry == nullptr) {
-			break;
-		}
-
-		String name = make_string_c(entry->d_name);
-		if (name == "." || name == "..") {
-			continue;
-		}
-
-		String filepath = {};
-		filepath.len = path.len+1+name.len;
-		filepath.text = gb_alloc_array(a, u8, filepath.len+1);
-		defer (gb_free(a, filepath.text));
-		gb_memmove(filepath.text, path.text, path.len);
-		gb_memmove(filepath.text+path.len, "/", 1);
-		gb_memmove(filepath.text+path.len+1, name.text, name.len);
-		filepath.text[filepath.len] = 0;
-
-
-		struct stat dir_stat = {};
-
-		if (stat((char *)filepath.text, &dir_stat)) {
-			continue;
-		}
-
-		if (S_ISDIR(dir_stat.st_mode)) {
-			continue;
-		}
-
-		i64 size = dir_stat.st_size;
-
-		FileInfo info = {};
-		info.name = name;
-		info.fullpath = path_to_full_path(a, filepath);
-		info.size = size;
-		array_add(fi, info);
-	}
-
-	if (fi->count == 0) {
-		return ReadDirectory_Empty;
-	}
-
-	return ReadDirectory_None;
-}
-
-
-#else
-#error Implement read_directory
-#endif
-
-#if !defined(GB_SYSTEM_WINDOWS)
-gb_internal bool write_directory(String path) {
-	char const *pathname = (char *) path.text;
-
-	if (access(pathname, W_OK) < 0) {
-		return false;
-	}
-
-	return true;
-}
-#else
-gb_internal bool write_directory(String path) {
-	String16 wstr = string_to_string16(heap_allocator(), path);
-	LPCWSTR wdirectory_name = wstr.text;
-
-	HANDLE directory = CreateFileW(wdirectory_name,
-			GENERIC_WRITE,
-			0,
-			NULL,
-			OPEN_EXISTING,
-			FILE_FLAG_BACKUP_SEMANTICS,
-			NULL);
-
-	if (directory == INVALID_HANDLE_VALUE) {
-		DWORD error_code = GetLastError();
-		if (error_code == ERROR_ACCESS_DENIED) {
-			return false;
-		}
-	}
-
-	CloseHandle(directory);
-	return true;
-}
-#endif
+/*
+	Path handling utilities.
+*/
+#if !defined(GB_SYSTEM_WINDOWS)
+#include <unistd.h>
+#endif
+
+gb_internal String remove_extension_from_path(String const &s) {
+	if (s.len != 0 && s.text[s.len-1] == '.') {
+		return s;
+	}
+	for (isize i = s.len-1; i >= 0; i--) {
+		if (s[i] == '.') {
+			return substring(s, 0, i);
+		}
+	}
+	return s;
+}
+
+gb_internal String remove_directory_from_path(String const &s) {
+	isize len = 0;
+	for (isize i = s.len-1; i >= 0; i--) {
+		if (s[i] == '/' ||
+		    s[i] == '\\') {
+			break;
+		}
+		len += 1;
+	}
+	return substring(s, s.len-len, s.len);
+}
+
+
+// NOTE(Mark Naughton): getcwd as String
+#if !defined(GB_SYSTEM_WINDOWS)
+gb_internal String get_current_directory(void) {
+	char cwd[256];
+	getcwd(cwd, 256);
+
+	return make_string_c(cwd);
+}
+
+#else
+gb_internal String get_current_directory(void) {
+	gbAllocator a = heap_allocator();
+
+	wchar_t cwd[256];
+	GetCurrentDirectoryW(256, cwd);
+
+	String16 wstr = make_string16_c(cwd);
+
+	return string16_to_string(a, wstr);
+}
+#endif
+
+gb_internal bool path_is_directory(String path);
+
+gb_internal String directory_from_path(String const &s) {
+	if (path_is_directory(s)) {
+		return s;
+	}
+
+	isize i = s.len-1;
+	for (; i >= 0; i--) {
+		if (s[i] == '/' ||
+		    s[i] == '\\') {
+			break;
+		}
+	}
+	if (i >= 0) {
+		return substring(s, 0, i);	
+	}
+	return substring(s, 0, 0);
+}
+
+#if defined(GB_SYSTEM_WINDOWS)
+	gb_internal bool path_is_directory(String path) {
+		gbAllocator a = heap_allocator();
+		String16 wstr = string_to_string16(a, path);
+		defer (gb_free(a, wstr.text));
+
+		i32 attribs = GetFileAttributesW(wstr.text);
+		if (attribs < 0) return false;
+
+		return (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0;
+	}
+
+#else
+	gb_internal bool path_is_directory(String path) {
+		gbAllocator a = heap_allocator();
+		char *copy = cast(char *)copy_string(a, path).text;
+		defer (gb_free(a, copy));
+
+		struct stat s;
+		if (stat(copy, &s) == 0) {
+			return (s.st_mode & S_IFDIR) != 0;
+		}
+		return false;
+	}
+#endif
+
+
+gb_internal String path_to_full_path(gbAllocator a, String path) {
+	gbAllocator ha = heap_allocator();
+	char *path_c = gb_alloc_str_len(ha, cast(char *)path.text, path.len);
+	defer (gb_free(ha, path_c));
+
+	char *fullpath = gb_path_get_full_name(a, path_c);
+	String res = string_trim_whitespace(make_string_c(fullpath));
+#if defined(GB_SYSTEM_WINDOWS)
+	for (isize i = 0; i < res.len; i++) {
+		if (res.text[i] == '\\') {
+			res.text[i] = '/';
+		}
+	}
+#endif
+	return copy_string(a, res);
+}
+
+struct Path {
+	String basename;
+	String name;
+	String ext;
+};
+
+// NOTE(Jeroen): Naively turns a Path into a string.
+gb_internal String path_to_string(gbAllocator a, Path path) {
+	if (path.basename.len + path.name.len + path.ext.len == 0) {
+		return make_string(nullptr, 0);
+	}
+
+	isize len = path.basename.len + 1 + path.name.len + 1;
+	if (path.ext.len > 0) {
+		 len += path.ext.len + 1;
+	}
+
+	u8 *str = gb_alloc_array(a, u8, len);
+
+	isize i = 0;
+	gb_memmove(str+i, path.basename.text, path.basename.len); i += path.basename.len;
+	
+	gb_memmove(str+i, "/", 1);                                i += 1;
+	
+	gb_memmove(str+i, path.name.text,     path.name.len);     i += path.name.len;
+	if (path.ext.len > 0) {
+		gb_memmove(str+i, ".", 1);                            i += 1;
+		gb_memmove(str+i, path.ext.text,  path.ext.len);      i += path.ext.len;
+	}
+	str[i] = 0;
+
+	String res = make_string(str, i);
+	res        = string_trim_whitespace(res);
+	return res;
+}
+
+// NOTE(Jeroen): Naively turns a Path into a string, then normalizes it using `path_to_full_path`.
+gb_internal String path_to_full_path(gbAllocator a, Path path) {
+	String temp = path_to_string(heap_allocator(), path);
+	defer (gb_free(heap_allocator(), temp.text));
+
+	return path_to_full_path(a, temp);
+}
+
+// NOTE(Jeroen): Takes a path like "odin" or "W:\Odin", turns it into a full path,
+// and then breaks it into its components to make a Path.
+gb_internal Path path_from_string(gbAllocator a, String const &path) {
+	Path res = {};
+
+	if (path.len == 0) return res;
+
+	String fullpath = path_to_full_path(a, path);
+	defer (gb_free(heap_allocator(), fullpath.text));
+
+	res.basename = directory_from_path(fullpath);	
+	res.basename = copy_string(a, res.basename);
+
+	if (path_is_directory(fullpath)) {
+		// It's a directory. We don't need to tinker with the name and extension.
+		// It could have a superfluous trailing `/`. Remove it if so.
+		if (res.basename.len > 0 && res.basename.text[res.basename.len - 1] == '/') {
+			res.basename.len--;
+		}
+		return res;
+	}
+
+	// Note(Dragos): Is the copy_string required if it's a substring?
+	isize name_start = (res.basename.len > 0) ? res.basename.len + 1 : res.basename.len;
+	res.name         = substring(fullpath, name_start, fullpath.len);
+	res.name         = remove_extension_from_path(res.name);
+	res.name         = copy_string(a, res.name);
+
+	res.ext          = path_extension(fullpath, false); // false says not to include the dot.
+	res.ext          = copy_string(a, res.ext);
+	return res;
+}
+
+// NOTE(Jeroen): Takes a path String and returns the last path element.
+gb_internal String last_path_element(String const &path) {
+	isize count = 0;
+	u8 * start = (u8 *)(&path.text[path.len - 1]);
+	for (isize length = path.len; length > 0 && path.text[length - 1] != '/'; length--) {
+		count++;
+		start--;
+	}
+	if (count > 0) {
+		start++; // Advance past the `/` and return the substring.
+		String res = make_string(start, count);
+		return res;
+	}
+	// Must be a root path like `/` or `C:/`, return empty String.
+	return STR_LIT("");
+}
+
+gb_internal bool path_is_directory(Path path) {
+	String path_string = path_to_full_path(heap_allocator(), path);
+	defer (gb_free(heap_allocator(), path_string.text));
+
+	return path_is_directory(path_string);
+}
+
+struct FileInfo {
+	String name;
+	String fullpath;
+	i64    size;
+	bool   is_dir;
+};
+
+enum ReadDirectoryError {
+	ReadDirectory_None,
+
+	ReadDirectory_InvalidPath,
+	ReadDirectory_NotExists,
+	ReadDirectory_Permission,
+	ReadDirectory_NotDir,
+	ReadDirectory_Empty,
+	ReadDirectory_Unknown,
+
+	ReadDirectory_COUNT,
+};
+
+gb_internal i64 get_file_size(String path) {
+	char *c_str = alloc_cstring(heap_allocator(), path);
+	defer (gb_free(heap_allocator(), c_str));
+
+	gbFile f = {};
+	gbFileError err = gb_file_open(&f, c_str);
+	defer (gb_file_close(&f));
+	if (err != gbFileError_None) {
+		return -1;
+	}
+	return gb_file_size(&f);
+}
+
+
+#if defined(GB_SYSTEM_WINDOWS)
+gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
+	GB_ASSERT(fi != nullptr);
+
+
+	while (path.len > 0) {
+		Rune end = path[path.len-1];
+		if (end == '/') {
+			path.len -= 1;
+		} else if (end == '\\') {
+			path.len -= 1;
+		} else {
+			break;
+		}
+	}
+
+	if (path.len == 0) {
+		return ReadDirectory_InvalidPath;
+	}
+	{
+		char *c_str = alloc_cstring(temporary_allocator(), path);
+		gbFile f = {};
+		gbFileError file_err = gb_file_open(&f, c_str);
+		defer (gb_file_close(&f));
+
+		switch (file_err) {
+		case gbFileError_Invalid:    return ReadDirectory_InvalidPath;
+		case gbFileError_NotExists:  return ReadDirectory_NotExists;
+		// case gbFileError_Permission: return ReadDirectory_Permission;
+		}
+	}
+
+	if (!path_is_directory(path)) {
+		return ReadDirectory_NotDir;
+	}
+
+
+	gbAllocator a = heap_allocator();
+	char *new_path = gb_alloc_array(a, char, path.len+3);
+	defer (gb_free(a, new_path));
+
+	gb_memmove(new_path, path.text, path.len);
+	gb_memmove(new_path+path.len, "/*", 2);
+	new_path[path.len+2] = 0;
+
+	String np = make_string(cast(u8 *)new_path, path.len+2);
+	String16 wstr = string_to_string16(a, np);
+	defer (gb_free(a, wstr.text));
+
+	WIN32_FIND_DATAW file_data = {};
+	HANDLE find_file = FindFirstFileW(wstr.text, &file_data);
+	if (find_file == INVALID_HANDLE_VALUE) {
+		return ReadDirectory_Unknown;
+	}
+	defer (FindClose(find_file));
+
+	array_init(fi, a, 0, 100);
+
+	do {
+		wchar_t *filename_w = file_data.cFileName;
+		u64 size = cast(u64)file_data.nFileSizeLow;
+		size |= (cast(u64)file_data.nFileSizeHigh) << 32;
+		String name = string16_to_string(a, make_string16_c(filename_w));
+		if (name == "." || name == "..") {
+			gb_free(a, name.text);
+			continue;
+		}
+
+		String filepath = {};
+		filepath.len = path.len+1+name.len;
+		filepath.text = gb_alloc_array(a, u8, filepath.len+1);
+		defer (gb_free(a, filepath.text));
+		gb_memmove(filepath.text, path.text, path.len);
+		gb_memmove(filepath.text+path.len, "/", 1);
+		gb_memmove(filepath.text+path.len+1, name.text, name.len);
+
+		FileInfo info = {};
+		info.name = name;
+		info.fullpath = path_to_full_path(a, filepath);
+		info.size = cast(i64)size;
+		info.is_dir = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
+		array_add(fi, info);
+	} while (FindNextFileW(find_file, &file_data));
+
+	if (fi->count == 0) {
+		return ReadDirectory_Empty;
+	}
+
+	return ReadDirectory_None;
+}
+#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD) || defined(GB_SYSTEM_HAIKU)
+
+#include <dirent.h>
+
+gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
+	GB_ASSERT(fi != nullptr);
+
+	gbAllocator a = heap_allocator();
+
+	char *c_path = alloc_cstring(a, path);
+	defer (gb_free(a, c_path));
+
+	DIR *dir = opendir(c_path);
+	if (!dir) {
+		switch (errno) {
+		case ENOENT:
+			return ReadDirectory_NotExists;
+		case EACCES:
+			return ReadDirectory_Permission;
+		case ENOTDIR:
+			return ReadDirectory_NotDir;
+		default:
+			// ENOMEM: out of memory
+			// EMFILE: per-process limit on open fds reached
+			// ENFILE: system-wide limit on total open files reached
+			return ReadDirectory_Unknown;
+		}
+		GB_PANIC("unreachable");
+	}
+
+	array_init(fi, a, 0, 100);
+
+	for (;;) {
+		struct dirent *entry = readdir(dir);
+		if (entry == nullptr) {
+			break;
+		}
+
+		String name = make_string_c(entry->d_name);
+		if (name == "." || name == "..") {
+			continue;
+		}
+
+		String filepath = {};
+		filepath.len = path.len+1+name.len;
+		filepath.text = gb_alloc_array(a, u8, filepath.len+1);
+		defer (gb_free(a, filepath.text));
+		gb_memmove(filepath.text, path.text, path.len);
+		gb_memmove(filepath.text+path.len, "/", 1);
+		gb_memmove(filepath.text+path.len+1, name.text, name.len);
+		filepath.text[filepath.len] = 0;
+
+
+		struct stat dir_stat = {};
+
+		if (stat((char *)filepath.text, &dir_stat)) {
+			continue;
+		}
+
+		if (S_ISDIR(dir_stat.st_mode)) {
+			continue;
+		}
+
+		i64 size = dir_stat.st_size;
+
+		FileInfo info = {};
+		info.name = name;
+		info.fullpath = path_to_full_path(a, filepath);
+		info.size = size;
+		array_add(fi, info);
+	}
+
+	if (fi->count == 0) {
+		return ReadDirectory_Empty;
+	}
+
+	return ReadDirectory_None;
+}
+
+
+#else
+#error Implement read_directory
+#endif
+
+#if !defined(GB_SYSTEM_WINDOWS)
+gb_internal bool write_directory(String path) {
+	char const *pathname = (char *) path.text;
+
+	if (access(pathname, W_OK) < 0) {
+		return false;
+	}
+
+	return true;
+}
+#else
+gb_internal bool write_directory(String path) {
+	String16 wstr = string_to_string16(heap_allocator(), path);
+	LPCWSTR wdirectory_name = wstr.text;
+
+	HANDLE directory = CreateFileW(wdirectory_name,
+			GENERIC_WRITE,
+			0,
+			NULL,
+			OPEN_EXISTING,
+			FILE_FLAG_BACKUP_SEMANTICS,
+			NULL);
+
+	if (directory == INVALID_HANDLE_VALUE) {
+		DWORD error_code = GetLastError();
+		if (error_code == ERROR_ACCESS_DENIED) {
+			return false;
+		}
+	}
+
+	CloseHandle(directory);
+	return true;
+}
+#endif

+ 46 - 1
src/threading.cpp

@@ -831,8 +831,53 @@ gb_internal void futex_wait(Futex *f, Footex val) {
 		WaitOnAddress(f, (void *)&val, sizeof(val), INFINITE);
 	} while (f->load() == val);
 }
+#elif defined(GB_SYSTEM_HAIKU)
+
+#include <pthread.h>
+#include <unordered_map>
+#include <memory>
+
+struct MutexCond {
+    pthread_mutex_t mutex;
+    pthread_cond_t cond;
+};
+
+std::unordered_map<Futex*, std::unique_ptr<MutexCond>> futex_map;
+
+MutexCond* get_mutex_cond(Futex* f) {
+	if (futex_map.find(f) == futex_map.end()) {
+		futex_map[f] = std::make_unique<MutexCond>();
+		pthread_mutex_init(&futex_map[f]->mutex, NULL);
+		pthread_cond_init(&futex_map[f]->cond, NULL);
+	}
+	return futex_map[f].get();
+}
+
+void futex_signal(Futex *f) {
+	MutexCond* mc = get_mutex_cond(f);
+	pthread_mutex_lock(&mc->mutex);
+	pthread_cond_signal(&mc->cond);
+	pthread_mutex_unlock(&mc->mutex);
+}
+
+void futex_broadcast(Futex *f) {
+	MutexCond* mc = get_mutex_cond(f);
+	pthread_mutex_lock(&mc->mutex);
+	pthread_cond_broadcast(&mc->cond);
+	pthread_mutex_unlock(&mc->mutex);
+}
+
+void futex_wait(Futex *f, Footex val) {
+	MutexCond* mc = get_mutex_cond(f);
+	pthread_mutex_lock(&mc->mutex);
+	while (f->load() == val) {
+		pthread_cond_wait(&mc->cond, &mc->mutex);
+	}
+	pthread_mutex_unlock(&mc->mutex);
+}
+
 #endif
 
 #if defined(GB_SYSTEM_WINDOWS)
 	#pragma warning(pop)
-#endif
+#endif