From c178f7199d7070c5481c5f0f3077f8dcbfa90226 Mon Sep 17 00:00:00 2001 From: Slendi Date: Thu, 15 Feb 2024 15:51:28 +0200 Subject: 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. --- src/path.cpp | 922 +++++++++++++++++++++++++++++------------------------------ 1 file changed, 461 insertions(+), 461 deletions(-) (limited to 'src/path.cpp') diff --git a/src/path.cpp b/src/path.cpp index de80c9def..742bba7f8 100644 --- a/src/path.cpp +++ b/src/path.cpp @@ -1,461 +1,461 @@ -/* - Path handling utilities. -*/ -#if !defined(GB_SYSTEM_WINDOWS) -#include -#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 *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 - -gb_internal ReadDirectoryError read_directory(String path, Array *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 +#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 *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 + +gb_internal ReadDirectoryError read_directory(String path, Array *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 -- cgit v1.2.3 From b543be0d155412c2270ae9cec3f0c85d558ac07a Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Mon, 11 Mar 2024 18:09:41 -0400 Subject: Copy file names from`dirent` into `FileInfo` during `read_directory` --- src/path.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/path.cpp') diff --git a/src/path.cpp b/src/path.cpp index 742bba7f8..b07f20870 100644 --- a/src/path.cpp +++ b/src/path.cpp @@ -407,7 +407,7 @@ gb_internal ReadDirectoryError read_directory(String path, Array *fi) i64 size = dir_stat.st_size; FileInfo info = {}; - info.name = name; + info.name = copy_string(a, name); info.fullpath = path_to_full_path(a, filepath); info.size = size; array_add(fi, info); -- cgit v1.2.3 From 4558f3992a47b4597563152baf26f1d2b5684b4d Mon Sep 17 00:00:00 2001 From: Andreas T Jonsson Date: Tue, 16 Apr 2024 14:27:29 +0200 Subject: Initial commit of NetBSD port --- base/runtime/entry_unix.odin | 2 +- base/runtime/heap_allocator_unix.odin | 2 +- base/runtime/os_specific_bsd.odin | 8 +- build_odin.sh | 4 + core/os/os_netbsd.odin | 755 ++++++++++++++++++++++++++++++++++ core/os/stat_unix.odin | 2 +- core/os/stream.odin | 4 +- core/sync/futex_netbsd.odin | 166 ++++++++ core/sync/primitives_netbsd.odin | 9 + core/sys/unix/pthread_netbsd.odin | 103 +++++ core/sys/unix/pthread_unix.odin | 2 +- core/sys/unix/signal_netbsd.odin | 31 ++ core/sys/unix/time_unix.odin | 5 +- core/thread/thread_unix.odin | 2 +- core/time/time_unix.odin | 2 +- src/build_settings.cpp | 17 + src/checker.cpp | 1 + src/gb/gb.h | 43 +- src/path.cpp | 2 +- src/threading.cpp | 11 +- 20 files changed, 1154 insertions(+), 17 deletions(-) create mode 100644 core/os/os_netbsd.odin create mode 100644 core/sync/futex_netbsd.odin create mode 100644 core/sync/primitives_netbsd.odin create mode 100644 core/sys/unix/pthread_netbsd.odin create mode 100644 core/sys/unix/signal_netbsd.odin (limited to 'src/path.cpp') diff --git a/base/runtime/entry_unix.odin b/base/runtime/entry_unix.odin index e49698e6e..7d7252625 100644 --- a/base/runtime/entry_unix.odin +++ b/base/runtime/entry_unix.odin @@ -1,5 +1,5 @@ //+private -//+build linux, darwin, freebsd, openbsd, haiku +//+build linux, darwin, freebsd, openbsd, netbsd, haiku //+no-instrumentation package runtime diff --git a/base/runtime/heap_allocator_unix.odin b/base/runtime/heap_allocator_unix.odin index 2b6698885..a8a4e9169 100644 --- a/base/runtime/heap_allocator_unix.odin +++ b/base/runtime/heap_allocator_unix.odin @@ -1,4 +1,4 @@ -//+build linux, darwin, freebsd, openbsd, haiku +//+build linux, darwin, freebsd, openbsd, netbsd, haiku //+private package runtime diff --git a/base/runtime/os_specific_bsd.odin b/base/runtime/os_specific_bsd.odin index 9cd065ff6..46ce51166 100644 --- a/base/runtime/os_specific_bsd.odin +++ b/base/runtime/os_specific_bsd.odin @@ -1,4 +1,4 @@ -//+build freebsd, openbsd +//+build freebsd, openbsd, netbsd //+private package runtime @@ -9,7 +9,11 @@ foreign libc { @(link_name="write") _unix_write :: proc(fd: i32, buf: rawptr, size: int) -> int --- - __error :: proc() -> ^i32 --- + when ODIN_OS == .NetBSD { + @(link_name="__errno") __error :: proc() -> ^i32 --- + } else { + __error :: proc() -> ^i32 --- + } } _stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) { diff --git a/build_odin.sh b/build_odin.sh index c53766290..df4451060 100755 --- a/build_odin.sh +++ b/build_odin.sh @@ -69,6 +69,10 @@ FreeBSD) CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)" LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)" ;; +NetBSD) + CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)" + LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)" + ;; Linux) CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)" LDFLAGS="$LDFLAGS -ldl $($LLVM_CONFIG --libs core native --system-libs --libfiles)" diff --git a/core/os/os_netbsd.odin b/core/os/os_netbsd.odin new file mode 100644 index 000000000..c75715ac7 --- /dev/null +++ b/core/os/os_netbsd.odin @@ -0,0 +1,755 @@ +package os + +foreign import dl "system:dl" +foreign import libc "system:c" + +import "base:runtime" +import "core:strings" +import "core:sys/unix" +import "core:c" + +Handle :: distinct i32 +File_Time :: distinct u64 +Errno :: distinct i32 + +INVALID_HANDLE :: ~Handle(0) + +ERROR_NONE: Errno : 0 /* No error */ +EPERM: Errno : 1 /* Operation not permitted */ +ENOENT: Errno : 2 /* No such file or directory */ +EINTR: Errno : 4 /* Interrupted system call */ +ESRCH: Errno : 3 /* No such process */ +EIO: Errno : 5 /* Input/output error */ +ENXIO: Errno : 6 /* Device not configured */ +E2BIG: Errno : 7 /* Argument list too long */ +ENOEXEC: Errno : 8 /* Exec format error */ +EBADF: Errno : 9 /* Bad file descriptor */ +ECHILD: Errno : 10 /* No child processes */ +EDEADLK: Errno : 11 /* Resource deadlock avoided. 11 was EAGAIN */ +ENOMEM: Errno : 12 /* Cannot allocate memory */ +EACCES: Errno : 13 /* Permission denied */ +EFAULT: Errno : 14 /* Bad address */ +ENOTBLK: Errno : 15 /* Block device required */ +EBUSY: Errno : 16 /* Device busy */ +EEXIST: Errno : 17 /* File exists */ +EXDEV: Errno : 18 /* Cross-device link */ +ENODEV: Errno : 19 /* Operation not supported by device */ +ENOTDIR: Errno : 20 /* Not a directory */ +EISDIR: Errno : 21 /* Is a directory */ +EINVAL: Errno : 22 /* Invalid argument */ +ENFILE: Errno : 23 /* Too many open files in system */ +EMFILE: Errno : 24 /* Too many open files */ +ENOTTY: Errno : 25 /* Inappropriate ioctl for device */ +ETXTBSY: Errno : 26 /* Text file busy */ +EFBIG: Errno : 27 /* File too large */ +ENOSPC: Errno : 28 /* No space left on device */ +ESPIPE: Errno : 29 /* Illegal seek */ +EROFS: Errno : 30 /* Read-only file system */ +EMLINK: Errno : 31 /* Too many links */ +EPIPE: Errno : 32 /* Broken pipe */ + +/* math software */ +EDOM: Errno : 33 /* Numerical argument out of domain */ +ERANGE: Errno : 34 /* Result too large or too small */ + +/* non-blocking and interrupt i/o */ +EAGAIN: Errno : 35 /* Resource temporarily unavailable */ +EWOULDBLOCK: Errno : EAGAIN /* Operation would block */ +EINPROGRESS: Errno : 36 /* Operation now in progress */ +EALREADY: Errno : 37 /* Operation already in progress */ + +/* ipc/network software -- argument errors */ +ENOTSOCK: Errno : 38 /* Socket operation on non-socket */ +EDESTADDRREQ: Errno : 39 /* Destination address required */ +EMSGSIZE: Errno : 40 /* Message too long */ +EPROTOTYPE: Errno : 41 /* Protocol wrong type for socket */ +ENOPROTOOPT: Errno : 42 /* Protocol option not available */ +EPROTONOSUPPORT: Errno : 43 /* Protocol not supported */ +ESOCKTNOSUPPORT: Errno : 44 /* Socket type not supported */ +EOPNOTSUPP: Errno : 45 /* Operation not supported */ +EPFNOSUPPORT: Errno : 46 /* Protocol family not supported */ +EAFNOSUPPORT: Errno : 47 /* Address family not supported by protocol family */ +EADDRINUSE: Errno : 48 /* Address already in use */ +EADDRNOTAVAIL: Errno : 49 /* Can't assign requested address */ + +/* ipc/network software -- operational errors */ +ENETDOWN: Errno : 50 /* Network is down */ +ENETUNREACH: Errno : 51 /* Network is unreachable */ +ENETRESET: Errno : 52 /* Network dropped connection on reset */ +ECONNABORTED: Errno : 53 /* Software caused connection abort */ +ECONNRESET: Errno : 54 /* Connection reset by peer */ +ENOBUFS: Errno : 55 /* No buffer space available */ +EISCONN: Errno : 56 /* Socket is already connected */ +ENOTCONN: Errno : 57 /* Socket is not connected */ +ESHUTDOWN: Errno : 58 /* Can't send after socket shutdown */ +ETOOMANYREFS: Errno : 59 /* Too many references: can't splice */ +ETIMEDOUT: Errno : 60 /* Operation timed out */ +ECONNREFUSED: Errno : 61 /* Connection refused */ + +ELOOP: Errno : 62 /* Too many levels of symbolic links */ +ENAMETOOLONG: Errno : 63 /* File name too long */ + +/* should be rearranged */ +EHOSTDOWN: Errno : 64 /* Host is down */ +EHOSTUNREACH: Errno : 65 /* No route to host */ +ENOTEMPTY: Errno : 66 /* Directory not empty */ + +/* quotas & mush */ +EPROCLIM: Errno : 67 /* Too many processes */ +EUSERS: Errno : 68 /* Too many users */ +EDQUOT: Errno : 69 /* Disc quota exceeded */ + +/* Network File System */ +ESTALE: Errno : 70 /* Stale NFS file handle */ +EREMOTE: Errno : 71 /* Too many levels of remote in path */ +EBADRPC: Errno : 72 /* RPC struct is bad */ +ERPCMISMATCH: Errno : 73 /* RPC version wrong */ +EPROGUNAVAIL: Errno : 74 /* RPC prog. not avail */ +EPROGMISMATCH: Errno : 75 /* Program version wrong */ +EPROCUNAVAIL: Errno : 76 /* Bad procedure for program */ + +ENOLCK: Errno : 77 /* No locks available */ +ENOSYS: Errno : 78 /* Function not implemented */ + +EFTYPE: Errno : 79 /* Inappropriate file type or format */ +EAUTH: Errno : 80 /* Authentication error */ +ENEEDAUTH: Errno : 81 /* Need authenticator */ + +/* SystemV IPC */ +EIDRM: Errno : 82 /* Identifier removed */ +ENOMSG: Errno : 83 /* No message of desired type */ +EOVERFLOW: Errno : 84 /* Value too large to be stored in data type */ + +/* Wide/multibyte-character handling, ISO/IEC 9899/AMD1:1995 */ +EILSEQ: Errno : 85 /* Illegal byte sequence */ + +/* From IEEE Std 1003.1-2001 */ +/* Base, Realtime, Threads or Thread Priority Scheduling option errors */ +ENOTSUP: Errno : 86 /* Not supported */ + +/* Realtime option errors */ +ECANCELED: Errno : 87 /* Operation canceled */ + +/* Realtime, XSI STREAMS option errors */ +EBADMSG: Errno : 88 /* Bad or Corrupt message */ + +/* XSI STREAMS option errors */ +ENODATA: Errno : 89 /* No message available */ +ENOSR: Errno : 90 /* No STREAM resources */ +ENOSTR: Errno : 91 /* Not a STREAM */ +ETIME: Errno : 92 /* STREAM ioctl timeout */ + +/* File system extended attribute errors */ +ENOATTR: Errno : 93 /* Attribute not found */ + +/* Realtime, XSI STREAMS option errors */ +EMULTIHOP: Errno : 94 /* Multihop attempted */ +ENOLINK: Errno : 95 /* Link has been severed */ +EPROTO: Errno : 96 /* Protocol error */ + +/* Robust mutexes */ +EOWNERDEAD: Errno : 97 /* Previous owner died */ +ENOTRECOVERABLE: Errno : 98 /* State not recoverable */ + +ELAST: Errno : 98 /* Must equal largest errno */ + +/* end of errno */ + +O_RDONLY :: 0x000000000 +O_WRONLY :: 0x000000001 +O_RDWR :: 0x000000002 +O_CREATE :: 0x000000200 +O_EXCL :: 0x000000800 +O_NOCTTY :: 0x000008000 +O_TRUNC :: 0x000000400 +O_NONBLOCK :: 0x000000004 +O_APPEND :: 0x000000008 +O_SYNC :: 0x000000080 +O_ASYNC :: 0x000000040 +O_CLOEXEC :: 0x000400000 + +RTLD_LAZY :: 0x001 +RTLD_NOW :: 0x002 +RTLD_GLOBAL :: 0x100 +RTLD_LOCAL :: 0x200 +RTLD_TRACE :: 0x200 +RTLD_NODELETE :: 0x01000 +RTLD_NOLOAD :: 0x02000 + +MAX_PATH :: 1024 +MAXNAMLEN :: 511 + +args := _alloc_command_line_arguments() + +Unix_File_Time :: struct { + seconds: time_t, + nanoseconds: c.long, +} + +dev_t :: u64 +ino_t :: u64 +nlink_t :: u64 +off_t :: i64 +mode_t :: u16 +pid_t :: u32 +uid_t :: u32 +gid_t :: u32 +blkcnt_t :: i64 +blksize_t :: i32 +fflags_t :: u32 +time_t :: i64 + +OS_Stat :: struct { + device_id: dev_t, + mode: mode_t, + _padding0: i16, + ino: ino_t, + nlink: nlink_t, + uid: uid_t, + gid: gid_t, + _padding1: i32, + rdev: dev_t, + + last_access: Unix_File_Time, + modified: Unix_File_Time, + status_change: Unix_File_Time, + birthtime: Unix_File_Time, + + size: off_t, + blocks: blkcnt_t, + block_size: blksize_t, + + flags: fflags_t, + gen: u32, + lspare: [2]u32, +} + +Dirent :: struct { + ino: ino_t, + reclen: u16, + namlen: u16, + type: u8, + name: [MAXNAMLEN + 1]byte, +} + +Dir :: distinct rawptr // DIR* + +// File type +S_IFMT :: 0o170000 // Type of file mask +S_IFIFO :: 0o010000 // Named pipe (fifo) +S_IFCHR :: 0o020000 // Character special +S_IFDIR :: 0o040000 // Directory +S_IFBLK :: 0o060000 // Block special +S_IFREG :: 0o100000 // Regular +S_IFLNK :: 0o120000 // Symbolic link +S_IFSOCK :: 0o140000 // Socket + +// File mode +// Read, write, execute/search by owner +S_IRWXU :: 0o0700 // RWX mask for owner +S_IRUSR :: 0o0400 // R for owner +S_IWUSR :: 0o0200 // W for owner +S_IXUSR :: 0o0100 // X for owner + +// Read, write, execute/search by group +S_IRWXG :: 0o0070 // RWX mask for group +S_IRGRP :: 0o0040 // R for group +S_IWGRP :: 0o0020 // W for group +S_IXGRP :: 0o0010 // X for group + +// Read, write, execute/search by others +S_IRWXO :: 0o0007 // RWX mask for other +S_IROTH :: 0o0004 // R for other +S_IWOTH :: 0o0002 // W for other +S_IXOTH :: 0o0001 // X for other + +S_ISUID :: 0o4000 // Set user id on execution +S_ISGID :: 0o2000 // Set group id on execution +S_ISVTX :: 0o1000 // Directory restrcted delete + +S_ISLNK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFLNK } +S_ISREG :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFREG } +S_ISDIR :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFDIR } +S_ISCHR :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFCHR } +S_ISBLK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFBLK } +S_ISFIFO :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFIFO } +S_ISSOCK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFSOCK } + +F_OK :: 0 // Test for file existance +X_OK :: 1 // Test for execute permission +W_OK :: 2 // Test for write permission +R_OK :: 4 // Test for read permission + +foreign libc { + @(link_name="__errno") __errno_location :: proc() -> ^c.int --- + + @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle --- + @(link_name="close") _unix_close :: proc(fd: Handle) -> c.int --- + @(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t --- + @(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t --- + @(link_name="lseek") _unix_seek :: proc(fd: Handle, offset: i64, whence: c.int) -> i64 --- + @(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int --- + @(link_name="stat") _unix_stat :: proc(path: cstring, stat: ^OS_Stat) -> c.int --- + @(link_name="lstat") _unix_lstat :: proc(path: cstring, sb: ^OS_Stat) -> c.int --- + @(link_name="fstat") _unix_fstat :: proc(fd: Handle, stat: ^OS_Stat) -> c.int --- + @(link_name="readlink") _unix_readlink :: proc(path: cstring, buf: ^byte, bufsiz: c.size_t) -> c.ssize_t --- + @(link_name="access") _unix_access :: proc(path: cstring, mask: c.int) -> c.int --- + @(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring --- + @(link_name="chdir") _unix_chdir :: proc(buf: cstring) -> c.int --- + @(link_name="rename") _unix_rename :: proc(old, new: cstring) -> c.int --- + @(link_name="unlink") _unix_unlink :: proc(path: cstring) -> c.int --- + @(link_name="rmdir") _unix_rmdir :: proc(path: cstring) -> c.int --- + @(link_name="mkdir") _unix_mkdir :: proc(path: cstring, mode: mode_t) -> c.int --- + + @(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir --- + @(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int --- + @(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) --- + @(link_name="readdir_r") _unix_readdir_r :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int --- + + @(link_name="malloc") _unix_malloc :: proc(size: c.size_t) -> rawptr --- + @(link_name="calloc") _unix_calloc :: proc(num, size: c.size_t) -> rawptr --- + @(link_name="free") _unix_free :: proc(ptr: rawptr) --- + @(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr --- + + @(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring --- + @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr --- + @(link_name="sysctlbyname") _sysctlbyname :: proc(path: cstring, oldp: rawptr, oldlenp: rawptr, newp: rawptr, newlen: int) -> c.int --- + + @(link_name="exit") _unix_exit :: proc(status: c.int) -> ! --- +} + +foreign dl { + @(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: c.int) -> rawptr --- + @(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr --- + @(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> c.int --- + @(link_name="dlerror") _unix_dlerror :: proc() -> cstring --- +} + +// NOTE(phix): Perhaps share the following functions with FreeBSD if they turn out to be the same in the end. + +is_path_separator :: proc(r: rune) -> bool { + return r == '/' +} + +get_last_error :: proc "contextless" () -> int { + return int(__errno_location()^) +} + +open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + cstr := strings.clone_to_cstring(path, context.temp_allocator) + handle := _unix_open(cstr, c.int(flags), c.int(mode)) + if handle == -1 { + return INVALID_HANDLE, Errno(get_last_error()) + } + return handle, ERROR_NONE +} + +close :: proc(fd: Handle) -> Errno { + result := _unix_close(fd) + if result == -1 { + return Errno(get_last_error()) + } + return ERROR_NONE +} + +// We set a max of 1GB to keep alignment and to be safe. +@(private) +MAX_RW :: 1 << 30 + +read :: proc(fd: Handle, data: []byte) -> (int, Errno) { + to_read := min(c.size_t(len(data)), MAX_RW) + bytes_read := _unix_read(fd, &data[0], to_read) + if bytes_read == -1 { + return -1, Errno(get_last_error()) + } + return int(bytes_read), ERROR_NONE +} + +write :: proc(fd: Handle, data: []byte) -> (int, Errno) { + if len(data) == 0 { + return 0, ERROR_NONE + } + + to_write := min(c.size_t(len(data)), MAX_RW) + bytes_written := _unix_write(fd, &data[0], to_write) + if bytes_written == -1 { + return -1, Errno(get_last_error()) + } + return int(bytes_written), ERROR_NONE +} + +seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { + res := _unix_seek(fd, offset, c.int(whence)) + if res == -1 { + return -1, Errno(get_last_error()) + } + return res, ERROR_NONE +} + +file_size :: proc(fd: Handle) -> (i64, Errno) { + s, err := fstat(fd) + if err != ERROR_NONE { + return -1, err + } + return s.size, ERROR_NONE +} + +rename :: proc(old_path, new_path: string) -> Errno { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator) + new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator) + res := _unix_rename(old_path_cstr, new_path_cstr) + if res == -1 { + return Errno(get_last_error()) + } + return ERROR_NONE +} + +remove :: proc(path: string) -> Errno { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + path_cstr := strings.clone_to_cstring(path, context.temp_allocator) + res := _unix_unlink(path_cstr) + if res == -1 { + return Errno(get_last_error()) + } + return ERROR_NONE +} + +make_directory :: proc(path: string, mode: mode_t = 0o775) -> Errno { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + path_cstr := strings.clone_to_cstring(path, context.temp_allocator) + res := _unix_mkdir(path_cstr, mode) + if res == -1 { + return Errno(get_last_error()) + } + return ERROR_NONE +} + +remove_directory :: proc(path: string) -> Errno { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + path_cstr := strings.clone_to_cstring(path, context.temp_allocator) + res := _unix_rmdir(path_cstr) + if res == -1 { + return Errno(get_last_error()) + } + return ERROR_NONE +} + +is_file_handle :: proc(fd: Handle) -> bool { + s, err := _fstat(fd) + if err != ERROR_NONE { + return false + } + return S_ISREG(s.mode) +} + +is_file_path :: proc(path: string, follow_links: bool = true) -> bool { + s: OS_Stat + err: Errno + if follow_links { + s, err = _stat(path) + } else { + s, err = _lstat(path) + } + if err != ERROR_NONE { + return false + } + return S_ISREG(s.mode) +} + +is_dir_handle :: proc(fd: Handle) -> bool { + s, err := _fstat(fd) + if err != ERROR_NONE { + return false + } + return S_ISDIR(s.mode) +} + +is_dir_path :: proc(path: string, follow_links: bool = true) -> bool { + s: OS_Stat + err: Errno + if follow_links { + s, err = _stat(path) + } else { + s, err = _lstat(path) + } + if err != ERROR_NONE { + return false + } + return S_ISDIR(s.mode) +} + +is_file :: proc {is_file_path, is_file_handle} +is_dir :: proc {is_dir_path, is_dir_handle} + +// NOTE(bill): Uses startup to initialize it + +stdin: Handle = 0 +stdout: Handle = 1 +stderr: Handle = 2 + +last_write_time :: proc(fd: Handle) -> (File_Time, Errno) { + s, err := _fstat(fd) + if err != ERROR_NONE { + return 0, err + } + modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds + return File_Time(modified), ERROR_NONE +} + +last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) { + s, err := _stat(name) + if err != ERROR_NONE { + return 0, err + } + modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds + return File_Time(modified), ERROR_NONE +} + +@private +_stat :: proc(path: string) -> (OS_Stat, Errno) { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + cstr := strings.clone_to_cstring(path, context.temp_allocator) + s: OS_Stat = --- + result := _unix_lstat(cstr, &s) + if result == -1 { + return s, Errno(get_last_error()) + } + return s, ERROR_NONE +} + +@private +_lstat :: proc(path: string) -> (OS_Stat, Errno) { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + cstr := strings.clone_to_cstring(path, context.temp_allocator) + + // deliberately uninitialized + s: OS_Stat = --- + res := _unix_lstat(cstr, &s) + if res == -1 { + return s, Errno(get_last_error()) + } + return s, ERROR_NONE +} + +@private +_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) { + s: OS_Stat = --- + result := _unix_fstat(fd, &s) + if result == -1 { + return s, Errno(get_last_error()) + } + return s, ERROR_NONE +} + +@private +_fdopendir :: proc(fd: Handle) -> (Dir, Errno) { + dirp := _unix_fdopendir(fd) + if dirp == cast(Dir)nil { + return nil, Errno(get_last_error()) + } + return dirp, ERROR_NONE +} + +@private +_closedir :: proc(dirp: Dir) -> Errno { + rc := _unix_closedir(dirp) + if rc != 0 { + return Errno(get_last_error()) + } + return ERROR_NONE +} + +@private +_rewinddir :: proc(dirp: Dir) { + _unix_rewinddir(dirp) +} + +@private +_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) { + result: ^Dirent + rc := _unix_readdir_r(dirp, &entry, &result) + + if rc != 0 { + err = Errno(get_last_error()) + return + } + err = ERROR_NONE + + if result == nil { + end_of_stream = true + return + } + + return +} + +@private +_readlink :: proc(path: string) -> (string, Errno) { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator) + + path_cstr := strings.clone_to_cstring(path, context.temp_allocator) + + bufsz : uint = MAX_PATH + buf := make([]byte, MAX_PATH) + for { + rc := _unix_readlink(path_cstr, &(buf[0]), bufsz) + if rc == -1 { + delete(buf) + return "", Errno(get_last_error()) + } else if rc == int(bufsz) { + bufsz += MAX_PATH + delete(buf) + buf = make([]byte, bufsz) + } else { + return strings.string_from_ptr(&buf[0], rc), ERROR_NONE + } + } + + return "", Errno{} +} + +absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) { + return "", Errno(ENOSYS) +} + +absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) { + rel := rel + if rel == "" { + rel = "." + } + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator) + + rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator) + + path_ptr := _unix_realpath(rel_cstr, nil) + if path_ptr == nil { + return "", Errno(get_last_error()) + } + defer _unix_free(path_ptr) + + path_cstr := transmute(cstring)path_ptr + path = strings.clone( string(path_cstr) ) + + return path, ERROR_NONE +} + +access :: proc(path: string, mask: int) -> (bool, Errno) { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + + cstr := strings.clone_to_cstring(path, context.temp_allocator) + result := _unix_access(cstr, c.int(mask)) + if result == -1 { + return false, Errno(get_last_error()) + } + return true, ERROR_NONE +} + +lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) + + path_str := strings.clone_to_cstring(key, context.temp_allocator) + cstr := _unix_getenv(path_str) + if cstr == nil { + return "", false + } + return strings.clone(string(cstr), allocator), true +} + +get_env :: proc(key: string, allocator := context.allocator) -> (value: string) { + value, _ = lookup_env(key, allocator) + return +} + +get_current_directory :: proc() -> string { + // NOTE(tetra): I would use PATH_MAX here, but I was not able to find + // an authoritative value for it across all systems. + // The largest value I could find was 4096, so might as well use the page size. + page_size := get_page_size() + buf := make([dynamic]u8, page_size) + #no_bounds_check for { + cwd := _unix_getcwd(cstring(&buf[0]), c.size_t(len(buf))) + if cwd != nil { + return string(cwd) + } + if Errno(get_last_error()) != ERANGE { + delete(buf) + return "" + } + resize(&buf, len(buf)+page_size) + } + unreachable() +} + +set_current_directory :: proc(path: string) -> (err: Errno) { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + cstr := strings.clone_to_cstring(path, context.temp_allocator) + res := _unix_chdir(cstr) + if res == -1 do return Errno(get_last_error()) + return ERROR_NONE +} + +exit :: proc "contextless" (code: int) -> ! { + runtime._cleanup_runtime_contextless() + _unix_exit(c.int(code)) +} + +current_thread_id :: proc "contextless" () -> int { + return cast(int) unix.pthread_self() +} + +dlopen :: proc(filename: string, flags: int) -> rawptr { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + cstr := strings.clone_to_cstring(filename, context.temp_allocator) + handle := _unix_dlopen(cstr, c.int(flags)) + return handle +} + +dlsym :: proc(handle: rawptr, symbol: string) -> rawptr { + assert(handle != nil) + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + cstr := strings.clone_to_cstring(symbol, context.temp_allocator) + proc_handle := _unix_dlsym(handle, cstr) + return proc_handle +} + +dlclose :: proc(handle: rawptr) -> bool { + assert(handle != nil) + return _unix_dlclose(handle) == 0 +} + +dlerror :: proc() -> string { + return string(_unix_dlerror()) +} + +get_page_size :: proc() -> int { + // NOTE(tetra): The page size never changes, so why do anything complicated + // if we don't have to. + @static page_size := -1 + if page_size != -1 do return page_size + + page_size = int(_unix_getpagesize()) + return page_size +} + +@(private) +_processor_core_count :: proc() -> int { + count : int = 0 + count_size := size_of(count) + if _sysctlbyname("hw.logicalcpu", &count, &count_size, nil, 0) == 0 { + if count > 0 { + return count + } + } + + return 1 +} + +_alloc_command_line_arguments :: proc() -> []string { + res := make([]string, len(runtime.args__)) + for arg, i in runtime.args__ { + res[i] = string(arg) + } + return res +} diff --git a/core/os/stat_unix.odin b/core/os/stat_unix.odin index 5e83c0e16..3bd62dfc7 100644 --- a/core/os/stat_unix.odin +++ b/core/os/stat_unix.odin @@ -1,4 +1,4 @@ -//+build linux, darwin, freebsd, openbsd, haiku +//+build linux, darwin, freebsd, openbsd, netbsd, haiku package os import "core:time" diff --git a/core/os/stream.odin b/core/os/stream.odin index 25f31218c..4e73284f0 100644 --- a/core/os/stream.odin +++ b/core/os/stream.odin @@ -32,7 +32,7 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, } case .Read_At: - when !(ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Haiku) { + when !(ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Haiku) { n_int, os_err = read_at(fd, p, offset) n = i64(n_int) if n == 0 && os_err == 0 { @@ -46,7 +46,7 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, err = .EOF } case .Write_At: - when !(ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Haiku) { + when !(ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Haiku) { n_int, os_err = write_at(fd, p, offset) n = i64(n_int) if n == 0 && os_err == 0 { diff --git a/core/sync/futex_netbsd.odin b/core/sync/futex_netbsd.odin new file mode 100644 index 000000000..f82af77d6 --- /dev/null +++ b/core/sync/futex_netbsd.odin @@ -0,0 +1,166 @@ +//+private +package sync + +import "core:c" +import "core:time" +import "core:sys/unix" +import "base:runtime" + +@(private="file") +Wait_Node :: struct { + thread: unix.pthread_t, + futex: ^Futex, + prev, next: ^Wait_Node, +} +@(private="file") +atomic_flag :: distinct bool +@(private="file") +Wait_Queue :: struct { + lock: atomic_flag, + list: Wait_Node, +} +@(private="file") +waitq_lock :: proc "contextless" (waitq: ^Wait_Queue) { + for cast(bool)atomic_exchange_explicit(&waitq.lock, atomic_flag(true), .Acquire) { + cpu_relax() // spin... + } +} +@(private="file") +waitq_unlock :: proc "contextless" (waitq: ^Wait_Queue) { + atomic_store_explicit(&waitq.lock, atomic_flag(false), .Release) +} + +// FIXME: This approach may scale badly in the future, +// possible solution - hash map (leads to deadlocks now). +@(private="file") +g_waitq: Wait_Queue + +@(init, private="file") +g_waitq_init :: proc() { + g_waitq = { + list = { + prev = &g_waitq.list, + next = &g_waitq.list, + }, + } +} + +@(private="file") +get_waitq :: #force_inline proc "contextless" (f: ^Futex) -> ^Wait_Queue { + _ = f + return &g_waitq +} + +_futex_wait :: proc "contextless" (f: ^Futex, expect: u32) -> (ok: bool) { + waitq := get_waitq(f) + waitq_lock(waitq) + defer waitq_unlock(waitq) + + head := &waitq.list + waiter := Wait_Node{ + thread = unix.pthread_self(), + futex = f, + prev = head, + next = head.next, + } + + waiter.prev.next = &waiter + waiter.next.prev = &waiter + + old_mask, mask: unix.sigset_t + unix.sigemptyset(&mask) + unix.sigaddset(&mask, unix.SIGCONT) + unix.pthread_sigmask(unix.SIG_BLOCK, &mask, &old_mask) + + if u32(atomic_load_explicit(f, .Acquire)) == expect { + waitq_unlock(waitq) + defer waitq_lock(waitq) + + sig: c.int + unix.sigwait(&mask, &sig) + errno := unix.errno() + ok = errno == unix.ERROR_NONE + } + + waiter.prev.next = waiter.next + waiter.next.prev = waiter.prev + + unix.pthread_sigmask(unix.SIG_SETMASK, &old_mask, nil) + + // FIXME: Add error handling! + return +} + +_futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expect: u32, duration: time.Duration) -> (ok: bool) { + if duration <= 0 { + return false + } + waitq := get_waitq(f) + waitq_lock(waitq) + defer waitq_unlock(waitq) + + head := &waitq.list + waiter := Wait_Node{ + thread = unix.pthread_self(), + futex = f, + prev = head, + next = head.next, + } + + waiter.prev.next = &waiter + waiter.next.prev = &waiter + + old_mask, mask: unix.sigset_t + unix.sigemptyset(&mask) + unix.sigaddset(&mask, unix.SIGCONT) + unix.pthread_sigmask(unix.SIG_BLOCK, &mask, &old_mask) + + if u32(atomic_load_explicit(f, .Acquire)) == expect { + waitq_unlock(waitq) + defer waitq_lock(waitq) + + info: unix.siginfo_t + ts := unix.timespec{ + tv_sec = i64(duration / 1e9), + tv_nsec = i64(duration % 1e9), + } + unix.sigtimedwait(&mask, &info, &ts) + errno := unix.errno() + ok = errno == unix.EAGAIN || errno == unix.ERROR_NONE + } + + waiter.prev.next = waiter.next + waiter.next.prev = waiter.prev + + unix.pthread_sigmask(unix.SIG_SETMASK, &old_mask, nil) + + // FIXME: Add error handling! + return +} + +_futex_signal :: proc "contextless" (f: ^Futex) { + waitq := get_waitq(f) + waitq_lock(waitq) + defer waitq_unlock(waitq) + + head := &waitq.list + for waiter := head.next; waiter != head; waiter = waiter.next { + if waiter.futex == f { + unix.pthread_kill(waiter.thread, unix.SIGCONT) + break + } + } +} + +_futex_broadcast :: proc "contextless" (f: ^Futex) { + waitq := get_waitq(f) + waitq_lock(waitq) + defer waitq_unlock(waitq) + + head := &waitq.list + for waiter := head.next; waiter != head; waiter = waiter.next { + if waiter.futex == f { + unix.pthread_kill(waiter.thread, unix.SIGCONT) + } + } +} diff --git a/core/sync/primitives_netbsd.odin b/core/sync/primitives_netbsd.odin new file mode 100644 index 000000000..2d5b6b7fe --- /dev/null +++ b/core/sync/primitives_netbsd.odin @@ -0,0 +1,9 @@ +//+build netbsd +//+private +package sync + +import "core:sys/unix" + +_current_thread_id :: proc "contextless" () -> int { + return cast(int) unix.pthread_self() +} diff --git a/core/sys/unix/pthread_netbsd.odin b/core/sys/unix/pthread_netbsd.odin new file mode 100644 index 000000000..c334fa56c --- /dev/null +++ b/core/sys/unix/pthread_netbsd.odin @@ -0,0 +1,103 @@ +//+build netbsd +package unix + +import "core:c" + +pthread_t :: distinct u64 + +SEM_T_SIZE :: 8 + +PTHREAD_CONDATTR_T_SIZE :: 16 +PTHREAD_MUTEXATTR_T_SIZE :: 16 +PTHREAD_RWLOCKATTR_T_SIZE :: 16 +PTHREAD_BARRIERATTR_T_SIZE :: 16 + +PTHREAD_COND_T_SIZE :: 40 +PTHREAD_MUTEX_T_SIZE :: 48 +PTHREAD_RWLOCK_T_SIZE :: 64 +PTHREAD_BARRIER_T_SIZE :: 48 +PTHREAD_ATTR_T_SIZE :: 16 + +pthread_cond_t :: struct #align(16) { + _: [PTHREAD_COND_T_SIZE] c.char +} + +pthread_mutex_t :: struct #align(16) { + _: [PTHREAD_MUTEX_T_SIZE] c.char +} + +pthread_rwlock_t :: struct #align(16) { + _: [PTHREAD_RWLOCK_T_SIZE] c.char +} + +pthread_barrier_t :: struct #align(16) { + _: [PTHREAD_BARRIER_T_SIZE] c.char +} + +pthread_attr_t :: struct #align(16) { + _: [PTHREAD_ATTR_T_SIZE] c.char +} + +pthread_condattr_t :: struct #align(16) { + _: [PTHREAD_CONDATTR_T_SIZE] c.char +} + +pthread_mutexattr_t :: struct #align(16) { + _: [PTHREAD_MUTEXATTR_T_SIZE] c.char +} + +pthread_rwlockattr_t :: struct #align(16) { + _: [PTHREAD_RWLOCKATTR_T_SIZE] c.char +} + +pthread_barrierattr_t :: struct #align(16) { + _: [PTHREAD_BARRIERATTR_T_SIZE] c.char +} + +PTHREAD_MUTEX_NORMAL :: 0 +PTHREAD_MUTEX_ERRORCHECK :: 1 +PTHREAD_MUTEX_RECURSIVE :: 2 + +PTHREAD_CREATE_JOINABLE :: 0 +PTHREAD_CREATE_DETACHED :: 1 +PTHREAD_INHERIT_SCHED :: 0 +PTHREAD_EXPLICIT_SCHED :: 1 +PTHREAD_PROCESS_PRIVATE :: 0 +PTHREAD_PROCESS_SHARED :: 1 + +SCHED_NONE :: -1 +SCHED_OTHER :: 0 +SCHED_FIFO :: 1 +SCHED_RR :: 3 + +sched_param :: struct { + sched_priority: c.int, +} + +sem_t :: struct #align(16) { + _: [SEM_T_SIZE] c.char +} + +PTHREAD_CANCEL_ENABLE :: 0 +PTHREAD_CANCEL_DISABLE :: 1 +PTHREAD_CANCEL_DEFERRED :: 0 +PTHREAD_CANCEL_ASYNCHRONOUS :: 1 + +foreign import "system:pthread" + +@(default_calling_convention="c") +foreign pthread { + sem_open :: proc(name: cstring, flags: c.int) -> ^sem_t --- + + sem_init :: proc(sem: ^sem_t, pshared: c.int, initial_value: c.uint) -> c.int --- + sem_destroy :: proc(sem: ^sem_t) -> c.int --- + sem_post :: proc(sem: ^sem_t) -> c.int --- + sem_wait :: proc(sem: ^sem_t) -> c.int --- + sem_trywait :: proc(sem: ^sem_t) -> c.int --- + + pthread_yield :: proc() --- + + pthread_setcancelstate :: proc (state: c.int, old_state: ^c.int) -> c.int --- + pthread_setcanceltype :: proc (type: c.int, old_type: ^c.int) -> c.int --- + pthread_cancel :: proc (thread: pthread_t) -> c.int --- +} diff --git a/core/sys/unix/pthread_unix.odin b/core/sys/unix/pthread_unix.odin index 4fe3c8dfa..5760560ee 100644 --- a/core/sys/unix/pthread_unix.odin +++ b/core/sys/unix/pthread_unix.odin @@ -1,4 +1,4 @@ -//+build linux, darwin, freebsd, openbsd, haiku +//+build linux, darwin, freebsd, openbsd, netbsd, haiku package unix foreign import "system:pthread" diff --git a/core/sys/unix/signal_netbsd.odin b/core/sys/unix/signal_netbsd.odin new file mode 100644 index 000000000..6655a6d38 --- /dev/null +++ b/core/sys/unix/signal_netbsd.odin @@ -0,0 +1,31 @@ +package unix + +import "core:c" + +foreign import libc "system:c" + +ERROR_NONE :: 0 +EAGAIN :: 35 + +SIGCONT :: 19 + +SIG_BLOCK :: 1 +SIG_UNBLOCK :: 2 +SIG_SETMASK :: 3 + +siginfo_t :: struct { si_pad: [128]c.char } +sigset_t :: struct { bits: [4]c.uint } + +foreign libc { + sigemptyset :: proc(set: ^sigset_t) -> c.int --- + sigaddset :: proc(set: ^sigset_t, _signal: c.int) -> c.int --- + + sigtimedwait :: proc(set: ^sigset_t, info: ^siginfo_t, timeout: ^timespec) -> c.int --- + sigwait :: proc(set: ^sigset_t, _signal: ^c.int) -> c.int --- + + @(private="file", link_name="__errno") get_error_location :: proc() -> ^c.int --- +} + +errno :: #force_inline proc "contextless" () -> int { + return int(get_error_location()^) +} diff --git a/core/sys/unix/time_unix.odin b/core/sys/unix/time_unix.odin index 088dc378b..6e7386ae3 100644 --- a/core/sys/unix/time_unix.odin +++ b/core/sys/unix/time_unix.odin @@ -1,4 +1,4 @@ -//+build linux, darwin, freebsd, openbsd, haiku +//+build linux, darwin, freebsd, openbsd, netbsd, haiku package unix when ODIN_OS == .Darwin { @@ -65,7 +65,8 @@ seconds_since_boot :: proc "c" () -> f64 { return f64(ts_boottime.tv_sec) + f64(ts_boottime.tv_nsec) / 1e9 } - +// TODO(phix): 'nanosleep' here might refere to the wrong version on NetBSD. Need to investigate this further. +// Normally this is solved by just including 'time.h'. So I need to understand the macro-magic going on in there. inline_nanosleep :: proc "c" (nanoseconds: i64) -> (remaining: timespec, res: i32) { s, ns := nanoseconds / 1e9, nanoseconds % 1e9 requested := timespec{tv_sec=s, tv_nsec=ns} diff --git a/core/thread/thread_unix.odin b/core/thread/thread_unix.odin index c75710873..3640251dd 100644 --- a/core/thread/thread_unix.odin +++ b/core/thread/thread_unix.odin @@ -1,4 +1,4 @@ -// +build linux, darwin, freebsd, openbsd, haiku +// +build linux, darwin, freebsd, openbsd, netbsd, haiku // +private package thread diff --git a/core/time/time_unix.odin b/core/time/time_unix.odin index 1c46b5994..f3e0d6640 100644 --- a/core/time/time_unix.odin +++ b/core/time/time_unix.odin @@ -1,5 +1,5 @@ //+private -//+build linux, darwin, freebsd, openbsd, haiku +//+build linux, darwin, freebsd, openbsd, netbsd, haiku package time import "core:sys/unix" diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 30d6f0b3c..cd57816ab 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -18,6 +18,7 @@ enum TargetOsKind : u16 { TargetOs_essence, TargetOs_freebsd, TargetOs_openbsd, + TargetOs_netbsd, TargetOs_haiku, TargetOs_wasi, @@ -79,6 +80,7 @@ gb_global String target_os_names[TargetOs_COUNT] = { str_lit("essence"), str_lit("freebsd"), str_lit("openbsd"), + str_lit("netbsd"), str_lit("haiku"), str_lit("wasi"), @@ -549,6 +551,14 @@ gb_global TargetMetrics target_openbsd_amd64 = { str_lit("e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"), }; +gb_global TargetMetrics target_netbsd_amd64 = { + TargetOs_netbsd, + TargetArch_amd64, + 8, 8, 8, 16, + str_lit("x86_64-unknown-netbsd-elf"), + str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"), +}; + gb_global TargetMetrics target_haiku_amd64 = { TargetOs_haiku, TargetArch_amd64, @@ -655,6 +665,7 @@ gb_global NamedTargetMetrics named_targets[] = { { str_lit("freebsd_amd64"), &target_freebsd_amd64 }, { str_lit("openbsd_amd64"), &target_openbsd_amd64 }, + { str_lit("netbsd_amd64"), &target_netbsd_amd64 }, { str_lit("haiku_amd64"), &target_haiku_amd64 }, { str_lit("freestanding_wasm32"), &target_freestanding_wasm32 }, @@ -1410,6 +1421,8 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta metrics = &target_freebsd_amd64; #elif defined(GB_SYSTEM_OPENBSD) metrics = &target_openbsd_amd64; + #elif defined(GB_SYSTEM_NETBSD) + metrics = &target_netbsd_amd64; #elif defined(GB_SYSTEM_HAIKU) metrics = &target_haiku_amd64; #elif defined(GB_CPU_ARM) @@ -1523,6 +1536,9 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta case TargetOs_openbsd: bc->link_flags = str_lit("-arch x86-64 "); break; + case TargetOs_netbsd: + bc->link_flags = str_lit("-arch x86-64 "); + break; case TargetOs_haiku: bc->link_flags = str_lit("-arch x86-64 "); break; @@ -2002,6 +2018,7 @@ gb_internal bool init_build_paths(String init_filename) { case TargetOs_essence: case TargetOs_freebsd: case TargetOs_openbsd: + case TargetOs_netbsd: case TargetOs_haiku: gb_printf_err("-no-crt on unix systems requires either -default-to-nil-allocator or -default-to-panic-allocator to also be present because the default allocator requires crt\n"); return false; diff --git a/src/checker.cpp b/src/checker.cpp index 244e7efbd..77d894624 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1010,6 +1010,7 @@ gb_internal void init_universal(void) { {"FreeBSD", TargetOs_freebsd}, {"Haiku", TargetOs_haiku}, {"OpenBSD", TargetOs_openbsd}, + {"NetBSD", TargetOs_netbsd}, {"WASI", TargetOs_wasi}, {"JS", TargetOs_js}, {"Freestanding", TargetOs_freestanding}, diff --git a/src/gb/gb.h b/src/gb/gb.h index 868e11a16..4e05a3a0a 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -83,6 +83,10 @@ extern "C" { #ifndef GB_SYSTEM_OPENBSD #define GB_SYSTEM_OPENBSD 1 #endif + #elif defined(__NetBSD__) + #ifndef GB_SYSTEM_NETBSD + #define GB_SYSTEM_NETBSD 1 + #endif #elif defined(__HAIKU__) || defined(__haiku__) #ifndef GB_SYSTEM_HAIKU #define GB_SYSTEM_HAIKU 1 @@ -208,7 +212,7 @@ extern "C" { #endif #include // NOTE(bill): malloc on linux #include - #if !defined(GB_SYSTEM_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__HAIKU__) + #if !defined(GB_SYSTEM_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__HAIKU__) #include #endif #include @@ -250,6 +254,11 @@ extern "C" { #define lseek64 lseek #endif +#if defined(GB_SYSTEM_NETBSD) + #include + #define lseek64 lseek +#endif + #if defined(GB_SYSTEM_HAIKU) #include #include @@ -817,6 +826,13 @@ typedef struct gbAffinity { isize thread_count; isize threads_per_core; } gbAffinity; +#elif defined(GB_SYSTEM_NETBSD) +typedef struct gbAffinity { + b32 is_accurate; + isize core_count; + isize thread_count; + isize threads_per_core; +} gbAffinity; #elif defined(GB_SYSTEM_HAIKU) typedef struct gbAffinity { b32 is_accurate; @@ -3267,6 +3283,31 @@ 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_NETBSD) +#include + +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; +} + #elif defined(GB_SYSTEM_HAIKU) #include diff --git a/src/path.cpp b/src/path.cpp index b07f20870..2f016a578 100644 --- a/src/path.cpp +++ b/src/path.cpp @@ -341,7 +341,7 @@ gb_internal ReadDirectoryError read_directory(String path, Array *fi) return ReadDirectory_None; } -#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD) || defined(GB_SYSTEM_HAIKU) +#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD) || defined(GB_SYSTEM_NETBSD) || defined(GB_SYSTEM_HAIKU) #include diff --git a/src/threading.cpp b/src/threading.cpp index fbe8997d1..77aa8edf7 100644 --- a/src/threading.cpp +++ b/src/threading.cpp @@ -627,8 +627,12 @@ gb_internal void thread_set_name(Thread *t, char const *name) { #elif defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD) pthread_set_name_np(t->posix_handle, name); #else - // TODO(bill): Test if this works - pthread_setname_np(t->posix_handle, name); + #ifdef GB_SYSTEM_NETBSD + // TODO(phix): Could be that libs are to old on NetBSD? Just ignore for now. + #else + // TODO(bill): Test if this works + pthread_setname_np(t->posix_handle, name); + #endif #endif } @@ -901,10 +905,11 @@ gb_internal void futex_wait(Futex *f, Footex val) { } while (f->load() == val); } -#elif defined(GB_SYSTEM_HAIKU) +#elif defined(GB_SYSTEM_HAIKU) || defined(GB_SYSTEM_NETBSD) // Futex implementation taken from https://tavianator.com/2023/futex.html +#include #include #include -- cgit v1.2.3 From 971201182abab765bbb99a671eb331b6bedf4bec Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sun, 12 May 2024 19:52:36 -0400 Subject: Fix `read_directory()` skipping directories on UNIX-likes --- src/path.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'src/path.cpp') diff --git a/src/path.cpp b/src/path.cpp index b07f20870..93f6f5000 100644 --- a/src/path.cpp +++ b/src/path.cpp @@ -400,16 +400,13 @@ gb_internal ReadDirectoryError read_directory(String path, Array *fi) continue; } - if (S_ISDIR(dir_stat.st_mode)) { - continue; - } - i64 size = dir_stat.st_size; FileInfo info = {}; info.name = copy_string(a, name); info.fullpath = path_to_full_path(a, filepath); info.size = size; + info.is_dir = S_ISDIR(dir_stat.st_mode); array_add(fi, info); } -- cgit v1.2.3 From cc73e06a46cbefd2afa4caa7ef95ba6d9ab64eb9 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 10 Jul 2024 15:09:13 +0200 Subject: Allow precompiled resources with -resource:foo.res And add them to the magic new feature. --- src/cached.cpp | 11 +++++++++++ src/linker.cpp | 11 +++-------- src/path.cpp | 7 +++++++ 3 files changed, 21 insertions(+), 8 deletions(-) (limited to 'src/path.cpp') diff --git a/src/cached.cpp b/src/cached.cpp index 02cb1ae96..36927496d 100644 --- a/src/cached.cpp +++ b/src/cached.cpp @@ -200,6 +200,17 @@ gb_internal bool try_cached_build(Checker *c, Array const &args) { } } + // Add Windows resource file to file list, if applicable + if (build_context.has_resource) { + String res_path = {}; + if (build_context.build_paths[BuildPath_RC].basename == "") { + res_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RES]); + } else { + res_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RC]); + } + array_add(&files, res_path); + } + for (auto const &entry : c->info.load_file_cache) { auto *cache = entry.value; if (!cache || !cache->exists) { diff --git a/src/linker.cpp b/src/linker.cpp index ac3e32851..046e72d0e 100644 --- a/src/linker.cpp +++ b/src/linker.cpp @@ -305,18 +305,13 @@ gb_internal i32 linker_stage(LinkerData *gen) { defer (gb_free(heap_allocator(), windows_sdk_bin_path.text)); if (!build_context.use_lld) { // msvc - String temp_res_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RES]); - String temp_rc_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RC]); - defer (gb_free(heap_allocator(), temp_res_path.text)); - defer (gb_free(heap_allocator(), temp_rc_path.text)); - - String res_path = concatenate3_strings(heap_allocator(), str_lit("\""), temp_res_path, str_lit("\"")); - String rc_path = concatenate3_strings(heap_allocator(), str_lit("\""), temp_rc_path, str_lit("\"")); + String res_path = quote_path(heap_allocator(), build_context.build_paths[BuildPath_RES]); + String rc_path = quote_path(heap_allocator(), build_context.build_paths[BuildPath_RC]); defer (gb_free(heap_allocator(), res_path.text)); defer (gb_free(heap_allocator(), rc_path.text)); if (build_context.has_resource) { - if (temp_rc_path == "") { + if (build_context.build_paths[BuildPath_RC].basename == "") { debugf("Using precompiled resource %.*s\n", LIT(res_path)); } else { debugf("Compiling resource %.*s\n", LIT(res_path)); diff --git a/src/path.cpp b/src/path.cpp index 26ccb7cbf..2c08ddd98 100644 --- a/src/path.cpp +++ b/src/path.cpp @@ -152,6 +152,13 @@ gb_internal String path_to_string(gbAllocator a, Path path) { return res; } +gb_internal String quote_path(gbAllocator a, Path path) { + String temp = path_to_string(a, path); + String quoted = concatenate3_strings(a, str_lit("\""), temp, str_lit("\"")); + gb_free(a, temp.text); + return quoted; +} + // 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); -- cgit v1.2.3