new repo for codebase
This commit is contained in:
654
src/os/os.c
Normal file
654
src/os/os.c
Normal file
@@ -0,0 +1,654 @@
|
||||
#include "os.h"
|
||||
#include "core/core_string16.h"
|
||||
#include "core/core_platform.h"
|
||||
#include "core/core_log.h"
|
||||
|
||||
///////////////////////////////////
|
||||
#if PLATFORM_WINDOWS
|
||||
#define NOMINMAX
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <DbgHelp.h>
|
||||
#include <TlHelp32.h>
|
||||
#include <Shlobj.h>
|
||||
///////////////////////////////
|
||||
fn os_date_t os_local_time(void) {
|
||||
SYSTEMTIME lt;
|
||||
GetLocalTime(<);
|
||||
os_date_t result = {0};
|
||||
result.ms = lt.wMilliseconds;
|
||||
result.sec = lt.wSecond;
|
||||
result.min = lt.wMinute;
|
||||
result.hour = lt.wHour;
|
||||
result.day = lt.wDay;
|
||||
result.month = lt.wMonth;
|
||||
result.year = lt.wYear;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn os_date_t os_universal_time(void) {
|
||||
SYSTEMTIME lt;
|
||||
GetSystemTime(<);
|
||||
os_date_t result = {0};
|
||||
result.ms = lt.wMilliseconds;
|
||||
result.sec = lt.wSecond;
|
||||
result.min = lt.wMinute;
|
||||
result.hour = lt.wHour;
|
||||
result.day = lt.wDay;
|
||||
result.month = lt.wMonth;
|
||||
result.year = lt.wYear;
|
||||
return result;
|
||||
}
|
||||
|
||||
static int64_t win32_counts_per_second;
|
||||
fn f64 os_seconds(void) {
|
||||
if (win32_counts_per_second == 0) {
|
||||
LARGE_INTEGER freq;
|
||||
QueryPerformanceFrequency(&freq);
|
||||
win32_counts_per_second = freq.QuadPart;
|
||||
}
|
||||
|
||||
LARGE_INTEGER time;
|
||||
QueryPerformanceCounter(&time);
|
||||
f64 result = (f64)time.QuadPart / (f64)win32_counts_per_second;
|
||||
return result;
|
||||
}
|
||||
|
||||
// @todo: revise these conversions
|
||||
fn f64 os_get_microseconds(void) {
|
||||
f64 secs = os_seconds();
|
||||
f64 result = secs * 1000000.0;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn f64 os_milliseconds(void) {
|
||||
f64 secs = os_seconds();
|
||||
f64 result = secs * 1000.0;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn void os_sleep(u32 milliseconds) {
|
||||
Sleep(milliseconds);
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
// files
|
||||
fn b32 os_copy(s8_t from, s8_t to, b32 overwrite) {
|
||||
BOOL fail_if_exists = true;
|
||||
if (overwrite) fail_if_exists = false;
|
||||
ma_temp_t scratch = ma_begin_scratch();
|
||||
s16_t from16 = s16_from_s8(scratch.arena, from);
|
||||
s16_t to16 = s16_from_s8(scratch.arena, to);
|
||||
BOOL success = CopyFileW(from16.str, to16.str, fail_if_exists);
|
||||
ma_end_scratch(scratch);
|
||||
return success ? true : false;
|
||||
}
|
||||
|
||||
fn b32 os_delete(s8_t path) {
|
||||
ma_temp_t scratch = ma_begin_scratch();
|
||||
s16_t path16 = s16_from_s8(scratch.arena, path);
|
||||
BOOL success = DeleteFileW(path16.str);
|
||||
ma_end_scratch(scratch);
|
||||
return success ? true : false;
|
||||
}
|
||||
|
||||
fn os_mkdir_t os_mkdir(s8_t path) {
|
||||
os_mkdir_t result = os_mkdir_success;
|
||||
ma_temp_t scratch = ma_begin_scratch();
|
||||
s16_t path16 = s16_from_s8(scratch.arena, path);
|
||||
BOOL success = CreateDirectoryW(path16.str, NULL);
|
||||
if (success == 0) {
|
||||
DWORD error = GetLastError();
|
||||
if (error == ERROR_ALREADY_EXISTS) result = os_mkdir_file_exists;
|
||||
else if (error == ERROR_PATH_NOT_FOUND) result = os_mkdir_path_not_found;
|
||||
else result = os_mkdir_other_error;
|
||||
}
|
||||
ma_end_scratch(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn os_write_t os_write(s8_t path, s8_t content) {
|
||||
os_write_t result = os_write_other_error;
|
||||
ma_temp_t scratch = ma_begin_scratch();
|
||||
s16_t path16 = s16_from_s8(scratch.arena, path);
|
||||
HANDLE handle = CreateFileW(path16.str, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (handle != INVALID_HANDLE_VALUE) {
|
||||
assert_expr(content.len == (DWORD)content.len);
|
||||
DWORD bytes_written = 0;
|
||||
BOOL error = WriteFile(handle, content.str, (DWORD)content.len, &bytes_written, NULL);
|
||||
if (error == TRUE) {
|
||||
if (bytes_written == content.len) {
|
||||
result = os_write_success;
|
||||
}
|
||||
}
|
||||
CloseHandle(handle);
|
||||
} else result = os_write_path_not_found;
|
||||
ma_end_scratch(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
// @todo: improve to return error codes
|
||||
fn s8_t os_read(ma_arena_t *arena, s8_t path) {
|
||||
s8_t result = {0};
|
||||
ma_temp_t scratch = ma_begin_scratch1(arena);
|
||||
s16_t path16 = s16_from_s8(scratch.arena, path);
|
||||
HANDLE handle = CreateFileW(path16.str, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (handle != INVALID_HANDLE_VALUE) {
|
||||
LARGE_INTEGER file_size;
|
||||
if (GetFileSizeEx(handle, &file_size)) {
|
||||
if (file_size.QuadPart != 0) {
|
||||
result.len = file_size.QuadPart;
|
||||
result.str = ma_push_array(arena, char, result.len + 1);
|
||||
assert(result.len == (i64)(DWORD)result.len);
|
||||
DWORD read;
|
||||
if (ReadFile(handle, result.str, (DWORD)result.len, &read, NULL)) { // @todo: can only read 32 byte size files?
|
||||
if (read == result.len) {
|
||||
result.str[result.len] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
CloseHandle(handle);
|
||||
}
|
||||
ma_end_scratch(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
typedef struct w32_file_iter_t w32_file_iter_t;
|
||||
struct w32_file_iter_t {
|
||||
HANDLE handle;
|
||||
WIN32_FIND_DATAW data;
|
||||
};
|
||||
|
||||
fn void os_advance(os_iter_t *it) {
|
||||
while (FindNextFileW(it->w32->handle, &it->w32->data) != 0) {
|
||||
WIN32_FIND_DATAW *data = &it->w32->data;
|
||||
|
||||
// Skip '.' and '..'
|
||||
if (data->cFileName[0] == '.' && data->cFileName[1] == '.' && data->cFileName[2] == 0) continue;
|
||||
if (data->cFileName[0] == '.' && data->cFileName[1] == 0) continue;
|
||||
|
||||
it->is_directory = data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
|
||||
it->name = s8_from_s16(it->arena, s16_make(data->cFileName, str16_len(data->cFileName)));
|
||||
|
||||
char *separator = it->path.str[it->path.len - 1] == '/' ? "" : "/";
|
||||
it->rel = s8_printf(it->arena, "%S%s%S", it->path, separator, it->name);
|
||||
it->abs = os_abs(it->arena, it->rel);
|
||||
it->is_valid = true;
|
||||
s8_normalize_path_unsafe(it->rel);
|
||||
s8_normalize_path_unsafe(it->abs);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
it->is_valid = false;
|
||||
DWORD error = GetLastError();
|
||||
assert(error == ERROR_NO_MORE_FILES);
|
||||
FindClose(it->w32->handle);
|
||||
}
|
||||
|
||||
fn os_iter_t *os_iter(ma_arena_t *arena, s8_t path) {
|
||||
os_iter_t *it = ma_push_type(arena, os_iter_t);
|
||||
it->w32 = ma_push_type(arena, w32_file_iter_t);
|
||||
it->arena = arena;
|
||||
it->path = path;
|
||||
it->is_valid = true;
|
||||
|
||||
ma_temp_t scratch = ma_begin_scratch1(arena);
|
||||
s8_t mod_path = s8_printf(scratch.arena, "%S\\*", path);
|
||||
s16_t mod_path16 = s16_from_s8(scratch.arena, mod_path);
|
||||
|
||||
it->w32->handle = FindFirstFileW(mod_path16.str, &it->w32->data);
|
||||
if (it->w32->handle == INVALID_HANDLE_VALUE) {
|
||||
it->is_valid = false;
|
||||
}
|
||||
|
||||
if (it->is_valid) {
|
||||
assert(it->w32->data.cFileName[0] == '.' && it->w32->data.cFileName[1] == 0);
|
||||
os_advance(it);
|
||||
}
|
||||
|
||||
ma_end_scratch(scratch);
|
||||
return it;
|
||||
}
|
||||
|
||||
fn i64 os_mod_time(s8_t file) {
|
||||
WIN32_FIND_DATAW data;
|
||||
ma_temp_t scratch = ma_begin_scratch();
|
||||
s16_t wpath = s16_from_s8(scratch.arena, file);
|
||||
HANDLE handle = FindFirstFileW(wpath.str, &data);
|
||||
i64 result = -1;
|
||||
if (handle != INVALID_HANDLE_VALUE) {
|
||||
FindClose(handle);
|
||||
FILETIME time = data.ftLastWriteTime;
|
||||
result = (i64)time.dwHighDateTime << 32 | time.dwLowDateTime;
|
||||
}
|
||||
ma_end_scratch(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s8_t os_abs(ma_arena_t *arena, s8_t rel) {
|
||||
const int buffer_size = 2048;
|
||||
ma_temp_t scratch = ma_begin_scratch1(arena);
|
||||
s16_t rel16 = s16_from_s8(scratch.arena, rel);
|
||||
wchar_t *buffer = ma_push_array(scratch.arena, wchar_t, buffer_size);
|
||||
DWORD written = GetFullPathNameW(rel16.str, buffer_size, buffer, 0);
|
||||
assert(written != 0);
|
||||
assert((i64)written < (i64)buffer_size);
|
||||
s8_t result = s8_from_s16(arena, s16_make(buffer, written));
|
||||
ma_end_scratch(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s8_t os_exe(ma_arena_t *arena) {
|
||||
const int buffer_size = 2048;
|
||||
ma_temp_t scratch = ma_begin_scratch1(arena);
|
||||
wchar_t *buffer = ma_push_array(scratch.arena, wchar_t, buffer_size);
|
||||
DWORD wsize = GetModuleFileNameW(0, buffer, buffer_size);
|
||||
assert(wsize != 0);
|
||||
s8_t result = s8_from_s16(arena, s16_make(buffer, wsize));
|
||||
s8_normalize_path_unsafe(result);
|
||||
ma_end_scratch(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s8_t os_exe_dir(ma_arena_t *arena) {
|
||||
ma_temp_t scratch = ma_begin_scratch1(arena);
|
||||
s8_t exe = os_exe(scratch.arena);
|
||||
s8_t path = s8_chop_last_slash(exe);
|
||||
s8_t result = s8_printf(arena, "%S", path);
|
||||
ma_end_scratch(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 os_is_dir(s8_t path) {
|
||||
ma_temp_t scratch = ma_begin_scratch();
|
||||
s16_t path16 = s16_from_s8(scratch.arena, path);
|
||||
DWORD dwAttrib = GetFileAttributesW(path16.str);
|
||||
ma_end_scratch(scratch);
|
||||
return dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY);
|
||||
}
|
||||
|
||||
fn b32 os_is_file(s8_t path) {
|
||||
ma_temp_t scratch = ma_begin_scratch();
|
||||
s16_t path16 = s16_from_s8(scratch.arena, path);
|
||||
DWORD dwAttrib = GetFileAttributesW(path16.str);
|
||||
ma_end_scratch(scratch);
|
||||
b32 is_file = (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) == 0;
|
||||
return dwAttrib != INVALID_FILE_ATTRIBUTES && is_file;
|
||||
}
|
||||
|
||||
fn b32 os_is_abs(s8_t path) {
|
||||
b32 result = path.len > 3 && char_is_alphabetic(path.str[0]) && path.str[1] == ':' && path.str[2] == '/';
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 os_exists(s8_t path) {
|
||||
ma_temp_t scratch = ma_begin_scratch();
|
||||
s16_t path16 = s16_from_s8(scratch.arena, path);
|
||||
DWORD attribs = GetFileAttributesW(path16.str);
|
||||
ma_end_scratch(scratch);
|
||||
return attribs == INVALID_FILE_ATTRIBUTES ? false : true;
|
||||
}
|
||||
|
||||
fn s8_t os_cwd(ma_arena_t *arena) {
|
||||
ma_temp_t scratch = ma_begin_scratch1(arena);
|
||||
const u32 buffer_size = 1024;
|
||||
wchar_t *buffer = ma_push_array(scratch.arena, wchar_t, buffer_size);
|
||||
DWORD wsize = GetCurrentDirectoryW(buffer_size, buffer);
|
||||
assert(wsize != 0);
|
||||
assert(wsize <= buffer_size);
|
||||
s8_t path = s8_from_s16(scratch.arena, s16_make(buffer, wsize));
|
||||
s8_normalize_path_unsafe(path);
|
||||
s8_t result = s8_printf(arena, "%S", path);
|
||||
ma_end_scratch(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn void os_set_cwd(s8_t path) {
|
||||
ma_temp_t scratch = ma_begin_scratch();
|
||||
s16_t path16 = s16_from_s8(scratch.arena, path);
|
||||
SetCurrentDirectoryW(path16.str);
|
||||
ma_end_scratch(scratch);
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
// threading
|
||||
|
||||
typedef struct w32_thread_t w32_thread_t;
|
||||
struct w32_thread_t {
|
||||
HANDLE handle;
|
||||
DWORD id;
|
||||
};
|
||||
|
||||
fn os_thread_t os_thread_create(ma_arena_t *arena, os_thread_fn_t *func, void *user_data) {
|
||||
assert(func);
|
||||
|
||||
LPSECURITY_ATTRIBUTES security_attribs = NULL;
|
||||
SIZE_T stack_size = 0;
|
||||
DWORD creation_flags = 0;
|
||||
DWORD thread_id = 0;
|
||||
HANDLE thread_handle = CreateThread(security_attribs, stack_size, (LPTHREAD_START_ROUTINE)func, user_data, creation_flags, &thread_id);
|
||||
if (thread_handle == NULL) {
|
||||
debugf("%s.CreatedThread failed: %d", __FUNCTION__, GetLastError());
|
||||
return (os_thread_t){.exited = true};
|
||||
}
|
||||
|
||||
os_thread_t result = {0};
|
||||
result.w32 = ma_push_type(arena, w32_thread_t);
|
||||
result.w32->handle = thread_handle;
|
||||
result.w32->id = thread_id;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn i32 os_thread_join(os_thread_t *thread) {
|
||||
DWORD return_value_indicates_event = WaitForSingleObject(thread->w32->handle, INFINITE);
|
||||
if (return_value_indicates_event != WAIT_OBJECT_0) {
|
||||
debugf("%s.WaitForSingleObject failed: %d", __FUNCTION__, GetLastError());
|
||||
}
|
||||
|
||||
DWORD exit_code = 0;
|
||||
BOOL if_fails_then_zero = GetExitCodeThread(thread->w32->handle, &exit_code);
|
||||
if (if_fails_then_zero == 0) {
|
||||
debugf("%s.GetExitCodeThread failed: %d", __FUNCTION__, GetLastError());
|
||||
} else {
|
||||
thread->exit_code = exit_code;
|
||||
}
|
||||
|
||||
CloseHandle(thread->w32->handle);
|
||||
thread->exited = true;
|
||||
return thread->exit_code;
|
||||
}
|
||||
|
||||
// @todo:
|
||||
// This will probably create 16 arenas or more XD
|
||||
// fn i32 os__thread_log(void *data) {
|
||||
// os_core_init();
|
||||
// debugf("testing");
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
// fn void os_test_threads(void) {
|
||||
// os_thread_t threads[16];
|
||||
// ma_temp_t scratch = ma_begin_scratch();
|
||||
// for (int i = 0; i < lengthof(threads); i += 1) {
|
||||
// threads[i] = os_thread_create(scratch.arena, os__thread_log, NULL);
|
||||
// }
|
||||
// for (int i = 0; i < lengthof(threads); i += 1) {
|
||||
// os_thread_join(&threads[i]);
|
||||
// }
|
||||
// ma_end_scratch(scratch);
|
||||
// }
|
||||
|
||||
fn s8_t os_appdata(ma_arena_t *arena, s8_t name) {
|
||||
assert(name.len != 0);
|
||||
assert(name.str);
|
||||
assert(arena);
|
||||
|
||||
wchar_t *out_path = NULL;
|
||||
HRESULT hr = SHGetKnownFolderPath(&FOLDERID_RoamingAppData, KF_FLAG_CREATE, NULL, &out_path);
|
||||
if (!SUCCEEDED(hr)) {
|
||||
debugf("%s.SHGetKnownFolderPath failed with hr: %d", __FUNCTION__, hr);
|
||||
return s8_null;
|
||||
}
|
||||
|
||||
s16_t appdata_path = s16_from_str16((u16 *)out_path);
|
||||
|
||||
ma_temp_t scratch = ma_begin_scratch1(arena);
|
||||
s8_t tmp = s8_from_s16(scratch.arena, appdata_path);
|
||||
s8_normalize_path_unsafe(tmp);
|
||||
s8_t path = s8_printf(arena, "%S/%S", tmp, name);
|
||||
os_mkdir_t mkdir_status = os_mkdir(path);
|
||||
if (mkdir_status != os_mkdir_success && mkdir_status != os_mkdir_file_exists) {
|
||||
debugf("%s.os_mkdir failed with status: %d, for: %S", __FUNCTION__, mkdir_status, path);
|
||||
path = s8_null;
|
||||
}
|
||||
|
||||
CoTaskMemFree(out_path);
|
||||
ma_end_scratch(scratch);
|
||||
return path;
|
||||
}
|
||||
|
||||
///////////////////////////////////
|
||||
#elif PLATFORM_POSIX
|
||||
///////////////////////////////////
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
fn os_date_t os_local_time(void) {
|
||||
os_date_t result = {0};
|
||||
time_t t = time(NULL);
|
||||
struct tm *lt = localtime(&t);
|
||||
result.sec = lt->tm_sec;
|
||||
result.min = lt->tm_min;
|
||||
result.hour = lt->tm_hour;
|
||||
result.day = lt->tm_mday;
|
||||
result.month = lt->tm_mon;
|
||||
result.year = lt->tm_year;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn os_date_t os_universal_time(void) {
|
||||
os_date_t result = {0};
|
||||
time_t t = time(NULL);
|
||||
struct tm u = {0};
|
||||
gmtime_r(&t, &u);
|
||||
result.sec = u.tm_sec;
|
||||
result.min = u.tm_min;
|
||||
result.hour = u.tm_hour;
|
||||
result.day = u.tm_mday;
|
||||
result.month = u.tm_mon;
|
||||
result.year = u.tm_year;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn f64 os_get_microseconds(void) {
|
||||
struct timespec t;
|
||||
clock_gettime(CLOCK_MONOTONIC, &t);
|
||||
u64 result = t.tv_sec*million(1) + (t.tv_nsec/thousand(1));
|
||||
return (f64)result;
|
||||
}
|
||||
|
||||
fn f64 os_get_milliseconds(void) {
|
||||
u64 micros = os_get_microseconds();
|
||||
f64 result = (f64)micros / 1000.0;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s8_t os_exe(ma_arena_t *arena) {
|
||||
const i32 buffer_size = 2048;
|
||||
ma_temp_t temp = ma_begin_scratch1(arena);
|
||||
char *buffer = ma_push_array(arena, char, buffer_size);
|
||||
ssize_t size = readlink("/proc/self/exe", buffer, buffer_size);
|
||||
assert(size < buffer_size); // @todo:?
|
||||
assert(size != -1);
|
||||
s8_t string = s8_copy(arena, s8_make(buffer, size));
|
||||
ma_end_scratch(temp);
|
||||
return string;
|
||||
}
|
||||
|
||||
fn s8_t os_exe_dir(ma_arena_t *arena) {
|
||||
ma_temp_t temp = ma_begin_scratch1(arena);
|
||||
s8_t exe = os_exe(temp.arena);
|
||||
s8_t dir = s8_chop_last_slash(exe);
|
||||
s8_t result = s8_copy(arena, dir);
|
||||
ma_end_scratch(temp);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s8_t os_cwd(ma_arena_t *arena) {
|
||||
const i32 buffer_size = 2048;
|
||||
ma_temp_t temp = ma_begin_scratch1(arena);
|
||||
char *buffer = ma_push_array(arena, char, buffer_size);
|
||||
char *ok = getcwd(buffer, buffer_size);
|
||||
assert(ok);
|
||||
s8_t result = s8_copy(arena, s8_from_char(buffer));
|
||||
ma_end_scratch(temp);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn void os_set_cwd(s8_t path) {
|
||||
ma_temp_t temp = ma_begin_scratch();
|
||||
chdir(s8_copy(temp.arena, path).str);
|
||||
ma_end_scratch(temp);
|
||||
}
|
||||
|
||||
fn b32 os_exists(s8_t path) {
|
||||
ma_temp_t temp = ma_begin_scratch();
|
||||
s8_t copy = s8_copy(temp.arena, path);
|
||||
int ok = access(copy.str, F_OK);
|
||||
ma_end_scratch(temp);
|
||||
return ok == 0 ? true : false;
|
||||
}
|
||||
|
||||
fn b32 os_is_dir(s8_t path) {
|
||||
b32 result = false;
|
||||
ma_temp_t temp = ma_begin_scratch();
|
||||
s8_t copy = s8_copy(temp.arena, path);
|
||||
DIR *dir = opendir(copy.str);
|
||||
if (dir) {
|
||||
result = true;
|
||||
closedir(dir);
|
||||
}
|
||||
ma_end_scratch(temp);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 os_is_file(s8_t path) {
|
||||
ma_temp_t temp = ma_begin_scratch();
|
||||
s8_t copy = s8_copy(temp.arena, path);
|
||||
struct stat path_stat;
|
||||
stat(copy.str, &path_stat);
|
||||
ma_end_scratch(temp);
|
||||
b32 result = S_ISREG(path_stat.st_mode);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 os_is_abs(s8_t path) {
|
||||
b32 result = path.len && path.str[0] == '/';
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s8_t os_abs(ma_arena_t *arena, s8_t path) {
|
||||
ma_temp_t temp = ma_begin_scratch1(arena);
|
||||
s8_t null_term_path = s8_copy(temp.arena, path);
|
||||
char buffer[PATH_MAX];
|
||||
char *resolved_path = realpath(null_term_path.str, buffer);
|
||||
s8_t result = s8_copy(arena, s8_from_char(resolved_path));
|
||||
ma_end_scratch(temp);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s8_t os_read(ma_arena_t *arena, s8_t path) {
|
||||
s8_t result = {0};
|
||||
ma_temp_t temp = ma_begin_scratch1(arena);
|
||||
s8_t copy_path = s8_copy(temp.arena, path);
|
||||
FILE *fd = fopen(copy_path.str, "rb");
|
||||
if (fd) {
|
||||
fseek(fd, 0, SEEK_END);
|
||||
long size = ftell(fd);
|
||||
fseek(fd, 0, SEEK_SET);
|
||||
char *string = malloc(size + 1);
|
||||
fread(string, size, 1, fd);
|
||||
fclose(fd);
|
||||
|
||||
result = s8_make(string, size);
|
||||
result.str[result.len] = 0;
|
||||
}
|
||||
ma_end_scratch(temp);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn os_write_t os_write(s8_t path, s8_t content) {
|
||||
os_write_t result = os_write_path_not_found;
|
||||
ma_temp_t scratch = ma_begin_scratch();
|
||||
s8_t copy_path = s8_copy(scratch.arena, path);
|
||||
FILE *fd = fopen(copy_path.str, "w");
|
||||
if (fd) {
|
||||
size_t size_written = fwrite(content.str, 1, content.len, fd);
|
||||
assert((int64_t)size_written == content.len);
|
||||
fclose(fd);
|
||||
result = os_write_success;
|
||||
}
|
||||
ma_end_scratch(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 os_copy(s8_t src_path, s8_t dst_path, b32 overwrite) {
|
||||
if (overwrite == false && os_exists(dst_path)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ma_temp_t temp = ma_begin_scratch();
|
||||
s8_t src_data = os_read(temp.arena, src_path);
|
||||
os_write_t write_result = os_write(dst_path, src_data);
|
||||
b32 result = write_result == os_write_success;
|
||||
ma_end_scratch(temp);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 os_delete(s8_t path) {
|
||||
ma_temp_t scratch = ma_begin_scratch();
|
||||
s8_t copy_path = s8_copy(scratch.arena, path);
|
||||
int rc = unlink(copy_path.str);
|
||||
ma_end_scratch(scratch);
|
||||
return (b32)(rc == 0);
|
||||
}
|
||||
|
||||
fn void os_advance(os_iter_t *it) {
|
||||
struct dirent *file = NULL;
|
||||
while ((file = readdir((DIR *)it->unix_dir)) != NULL) {
|
||||
if (file->d_name[0] == '.' && file->d_name[1] == '.' && file->d_name[2] == 0) {
|
||||
continue;
|
||||
}
|
||||
if (file->d_name[0] == '.' && file->d_name[1] == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
it->is_directory = file->d_type == DT_DIR;
|
||||
it->name = s8_copy(it->arena, s8_from_char(file->d_name));
|
||||
|
||||
const char *separator = it->path.str[it->path.len - 1] == '/' ? "" : "/";
|
||||
it->rel = s8_printf(it->arena, "%S%s%s", it->path, separator, file->d_name);
|
||||
it->abs = os_abs(it->arena, it->rel);
|
||||
it->is_valid = true;
|
||||
return;
|
||||
}
|
||||
|
||||
it->is_valid = false;
|
||||
closedir((DIR *)it->unix_dir);
|
||||
}
|
||||
|
||||
fn os_iter_t *os_iter(ma_arena_t *arena, s8_t path) {
|
||||
os_iter_t *it = ma_push_type(arena, os_iter_t);
|
||||
it->arena = arena;
|
||||
it->path = s8_copy(arena, path);
|
||||
it->unix_dir = opendir(s8_copy(arena, it->path).str);
|
||||
if (it->unix_dir) {
|
||||
it->is_valid = true;
|
||||
os_advance(it);
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
///////////////////////////////////
|
||||
#endif // PLATFORM_POSIX
|
||||
///////////////////////////////////
|
||||
|
||||
fn s8_t os_format_date(ma_arena_t *arena, os_date_t date) {
|
||||
s8_t result = s8_printf(arena, "%04u-%02u-%02u %02u:%02u:%02u.%03u", date.year, date.month, date.day, date.hour, date.min, date.sec, date.ms);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
#if PLATFORM_WINDOWS || PLATFORM_POSIX
|
||||
fn int os_systemf(const char *string, ...) {
|
||||
ma_temp_t scratch = ma_begin_scratch();
|
||||
S8_FMT(scratch.arena, string, result);
|
||||
int error_code = system(result.str);
|
||||
ma_end_scratch(scratch);
|
||||
return error_code;
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user