From 7c9c3371945f8ff11c7eead5ecbace539979db89 Mon Sep 17 00:00:00 2001 From: Krzosa Karol Date: Thu, 4 Jan 2024 21:54:16 +0100 Subject: [PATCH] Port filesystem to ubuntu, add filesystem tests --- bld_lib.cpp | 5 +- build.cpp | 17 ++- build.sh | 14 ++- filesystem.c | 273 +++++++++++++++++++++++++++++++++++------ filesystem.h | 2 +- stb_sprintf.h | 2 +- test/data/append_file | 1 + test/data/read_file | 1 + test/data/write_file | 1 + test/test_filesystem.c | 105 ++++++++++++++++ 10 files changed, 374 insertions(+), 47 deletions(-) create mode 100644 test/data/append_file create mode 100644 test/data/read_file create mode 100644 test/data/write_file create mode 100644 test/test_filesystem.c diff --git a/bld_lib.cpp b/bld_lib.cpp index eebb235..3ecdd00 100644 --- a/bld_lib.cpp +++ b/bld_lib.cpp @@ -205,6 +205,7 @@ Strs operator+(Str a, Str b) { return c; } +//@todo: split on any whitespace instead! Strs Split(char *str) { Str s = S8_MakeFromChar(str); S8_List list = S8_Split(Perm, s, S8_Lit(" "), 0); @@ -240,6 +241,8 @@ Str Merge(Strs list, Str separator = " "_s) { } bool CodeWasModified(char *str) { return SRC_WasModified(S8_MakeFromChar(str)); } +bool CodeWasModified(S8_String str) { return SRC_WasModified(str); } +S8_String FilenameWithoutExt(S8_String it) { return S8_SkipToLastSlash(S8_ChopLastPeriod(it)); } Strs IfCodeWasModified(char *cfile, char *objfile) { Strs result = {}; S8_String s = S8_MakeFromChar(cfile); @@ -258,7 +261,7 @@ int Run(Strs s) { #ifndef BLD_MAIN int main() { int Main(); - SRC_InitCache(Perm, S8_Lit("project.cache")); + SRC_InitCache(Perm, S8_Lit("buildfile.cache")); int result = Main(); if (result == 0) SRC_SaveCache(); } diff --git a/build.cpp b/build.cpp index a78ce2a..8e15d1d 100644 --- a/build.cpp +++ b/build.cpp @@ -8,8 +8,8 @@ int Main() { Strs cc = "cl.exe"; Strs flags = Split("-WX -W3 -wd4200 -diagnostics:column -nologo -Z7 -FC -GF -Gm- -Oi -Zo -D_CRT_SECURE_NO_WARNINGS"); Strs link = Split("-link -incremental:no"); - Strs files = Split("../test/main_core_as_header.cpp"); - files += IfCodeWasModified("../core.c", "../core.obj"); + Strs files = Split("../test/main_core_as_header.cpp ../core.c"); + // files += IfCodeWasModified("../core.c", "../core.obj"); if (use_std) { flags += Split("-GR- -EHa-"); } @@ -24,5 +24,16 @@ int Main() { flags += Split("-Od -D_DEBUG -MTd -fsanitize=address -MTd -RTC1"); link += Split("-NODEFAULTLIB:LIBCMT"); } - return Run(cc + files + flags + link); + + Strs objs = {}; + For(files) { + if (CodeWasModified(it)) { + int error = Run(cc + it + flags + "-c" + link); + if (error != 0) return error; + } + + objs += S8_Format(Perm, "../build/%Q.obj", FilenameWithoutExt(it)); + } + int error = Run(cc + objs + flags + link); + return error; } \ No newline at end of file diff --git a/build.sh b/build.sh index 0bcad7c..2be6354 100644 --- a/build.sh +++ b/build.sh @@ -1,10 +1,16 @@ #!/usr/bin/env bash -set -e - mkdir build cd build -clang -o test_array ../test/test_array.cpp -fno-exceptions -fno-rtti -Wno-writable-strings +set -e + +gcc -o test_filesystem ../test/test_filesystem.c -Wno-write-strings +./test_filesystem +gcc -o test_array ../test/test_array.cpp -fno-exceptions -fno-rtti -Wno-write-strings ./test_array -clang -o test_table ../test/test_table.cpp -fno-exceptions -fno-rtti -Wno-writable-strings +gcc -o test_table ../test/test_table.cpp -fno-exceptions -fno-rtti -Wno-write-strings ./test_table +gcc -o compile_as_c ../test/main.c +./compile_as_c + +#-Wno-writable-strings cd .. \ No newline at end of file diff --git a/filesystem.c b/filesystem.c index 3506cdf..1ef0b90 100644 --- a/filesystem.c +++ b/filesystem.c @@ -344,16 +344,6 @@ OS_API S8_String OS_ReadFile(MA_Arena *arena, S8_String path) { return result; } -OS_API int OS_SystemF(const char *string, ...) { - MA_Checkpoint scratch = MA_GetScratch(); - S8_FORMAT(scratch.arena, string, result); - IO_Printf("Executing: %s\n", result.str); - fflush(stdout); - int error_code = system(result.str); - MA_ReleaseScratch(scratch); - return error_code; -} - OS_API int64_t OS_GetFileModTime(S8_String file) { FILETIME time = {0}; WIN32_FIND_DATAW data; @@ -387,51 +377,260 @@ OS_API OS_Date OS_GetDate(void) { } #else + #include + #include + #include + #include + #include + OS_API bool OS_EnableTerminalColors(void) { return true; } -OS_API bool OS_IsAbsolute(S8_String path) { return false; } + +OS_API bool OS_IsAbsolute(S8_String path) { + bool result = path.len >= 1 && path.str[0] == '/'; + return result; +} + OS_API S8_String OS_GetExePath(MA_Arena *arena) { - S8_String s = {0}; - return s; + char buffer[PATH_MAX] = {}; + if (readlink("/proc/self/exe", buffer, PATH_MAX) == -1) { + return S8_MakeEmpty(); + } + S8_String result = S8_Copy(arena, S8_MakeFromChar(buffer)); + return result; } + OS_API S8_String OS_GetExeDir(MA_Arena *arena) { - S8_String s = {0}; - return s; + S8_String path = OS_GetExePath(arena); + S8_String dir = S8_ChopLastSlash(path); + S8_String copy = S8_Copy(arena, dir); + return copy; } + OS_API S8_String OS_GetWorkingDir(MA_Arena *arena) { - S8_String s = {0}; - return s; + char *buffer = (char *)MA_PushSizeNonZeroed(arena, PATH_MAX); + char *cwd = getcwd(buffer, PATH_MAX); + S8_String result = S8_MakeFromChar(cwd); + return result; } -OS_API void OS_SetWorkingDir(S8_String path) {} + +OS_API void OS_SetWorkingDir(S8_String path) { + IO_Assert(path.str[path.len] == 0); + chdir(path.str); +} + OS_API S8_String OS_GetAbsolutePath(MA_Arena *arena, S8_String relative) { - S8_String s = {0}; - return s; + IO_Assert(relative.str[relative.len] == 0); + + char *buffer = (char *)MA_PushSizeNonZeroed(arena, PATH_MAX); + realpath((char *)relative.str, buffer); + S8_String result = S8_MakeFromChar(buffer); + return result; } -OS_API bool OS_FileExists(S8_String path) { return false; } -OS_API bool OS_IsDir(S8_String path) { return false; } -OS_API bool OS_IsFile(S8_String path) { return false; } -OS_API double OS_GetTime(void) { return 0.0; } + +OS_API bool OS_FileExists(S8_String path) { + IO_Assert(path.str[path.len] == 0); + + bool result = false; + if (access((char *)path.str, F_OK) == 0) { + result = true; + } + return result; +} + +OS_API bool OS_IsDir(S8_String path) { + IO_Assert(path.str[path.len] == 0); + struct stat s; + if (stat(path.str, &s) != 0) + return false; + bool result = S_ISDIR(s.st_mode); + return result; +} + +OS_API bool OS_IsFile(S8_String path) { + IO_Assert(path.str[path.len] == 0); + struct stat s; + if (stat(path.str, &s) != 0) + return false; + bool result = S_ISREG(s.st_mode); + return result; +} + +OS_API double OS_GetTime(void) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + uint64_t timeu64 = (((uint64_t)ts.tv_sec) * 1000000ull) + ((uint64_t)ts.tv_nsec) / 1000ull; + double timef = (double)timeu64; + double result = timef / 1000000.0; // Microseconds to seconds + return result; +} + OS_API S8_List OS_ListDir(MA_Arena *arena, S8_String path, unsigned flags) { - S8_List s = {0}; - return s; + IO_Assert((flags & OS_RECURSIVE) == 0); + IO_Assert(path.str[path.len] == 0); + + S8_List result = {0}; + struct dirent *dir = 0; + DIR *d = opendir((char *)path.str); + if (d) { + while ((dir = readdir(d)) != NULL) { + if (dir->d_name[0] == '.') { + if (dir->d_name[1] == '.') { + if (dir->d_name[2] == 0) + continue; + } + + if (dir->d_name[1] == 0) + continue; + } + + S8_String n = S8_Format(arena, "%Q/%s", path, dir->d_name); + if ((flags & OS_RELATIVE_PATHS) == 0) { + n = OS_GetAbsolutePath(arena, n); + } + if (dir->d_type == DT_DIR) { + n = S8_Format(arena, "%Q/", n); + } + + S8_AddNode(arena, &result, n); + } + closedir(d); + } + + return result; } -OS_API OS_Result OS_MakeDir(S8_String path) { return OS_FAILURE; } -OS_API OS_Result OS_CopyFile(S8_String from, S8_String to, bool overwrite) { return OS_FAILURE; } -OS_API OS_Result OS_DeleteFile(S8_String path) { return OS_FAILURE; } -OS_API OS_Result OS_DeleteDir(S8_String path, unsigned flags) { return OS_FAILURE; } -OS_API OS_Result OS_AppendFile(S8_String path, S8_String string) { return OS_FAILURE; } -OS_API OS_Result OS_WriteFile(S8_String path, S8_String string) { return OS_FAILURE; } -OS_API S8_String OS_ReadFile(MA_Arena *arena, S8_String path) { - S8_String s = {0}; - return s; + +OS_API OS_Result OS_MakeDir(S8_String path) { + IO_Assert(path.str[path.len]); + int error = mkdir(path.str, 0755); + return error == 0 ? OS_SUCCESS : OS_FAILURE; } -OS_API int OS_SystemF(const char *string, ...) { return 0; } -OS_API int64_t OS_GetFileModTime(S8_String file) { return 0; } + +OS_API OS_Result OS_CopyFile(S8_String from, S8_String to, bool overwrite) { + const char *ow = overwrite ? "-n" : ""; + int result = OS_SystemF("cp %s %Q %Q", ow, from, to); + return result == 0 ? OS_SUCCESS : OS_FAILURE; +} + +OS_API OS_Result OS_DeleteFile(S8_String path) { + int result = OS_SystemF("rm %Q"); + return result == 0 ? OS_SUCCESS : OS_FAILURE; +} + +OS_API OS_Result OS_DeleteDir(S8_String path, unsigned flags) { + IO_Assert(flags & OS_RECURSIVE); + int result = OS_SystemF("rm -r %Q"); + return result == 0 ? OS_SUCCESS : OS_FAILURE; +} + +OS_API int64_t OS_GetFileModTime(S8_String file) { + IO_Assert(file.str[file.len] == 0); + + struct stat attrib = {}; + stat(file.str, &attrib); + struct timespec ts = attrib.st_mtim; + int64_t result = (((int64_t)ts.tv_sec) * 1000000ll) + ((int64_t)ts.tv_nsec) / 1000ll; + return result; +} + OS_API OS_Date OS_GetDate(void) { OS_Date s = {0}; return s; } + +OS_API OS_Result OS_AppendFile(S8_String path, S8_String string) { + IO_Assert(path.str[path.len] == 0); + + OS_Result result = OS_FAILURE; + FILE *f = fopen((const char *)path.str, "a"); + if (f) { + result = OS_SUCCESS; + + size_t written = fwrite(string.str, 1, string.len, f); + if (written < string.len) { + result = OS_FAILURE; + } + + int error = fclose(f); + if (error != 0) { + result = OS_FAILURE; + } + } + return result; +} + +OS_API S8_String OS_ReadFile(MA_Arena *arena, S8_String path) { + IO_Assert(path.str[path.len] == 0); + + S8_String result = {}; + FILE *f = fopen(path.str, "rb"); + if (f) { + fseek(f, 0, SEEK_END); + result.len = ftell(f); + fseek(f, 0, SEEK_SET); + + result.str = (char *)MA_PushSizeNonZeroed(arena, result.len + 1); + fread(result.str, result.len, 1, f); + result.str[result.len] = 0; + + fclose(f); + } + return result; +} + + #if 0 + #include + #include +OS_API OS_Result OS_WriteFile(S8_String path, S8_String string) { + IO_Assert(path.str[path.len] == 0); + + OS_Result result = OS_FAILURE; + int fd = open(path.str, O_CREAT | O_WRONLY, 0644); + if (fd != -1) { + result = OS_SUCCESS; + + ssize_t written = write(fd, string.str, string.len); + if (written != string.len) result = OS_FAILURE; + + int err = close(fd); + if (err != 0) result = OS_FAILURE; + } + return OS_SUCCESS; +} + #else +OS_API OS_Result OS_WriteFile(S8_String path, S8_String string) { + IO_Assert(path.str[path.len] == 0); + + OS_Result result = OS_FAILURE; + FILE *f = fopen((const char *)path.str, "w"); + if (f) { + result = OS_SUCCESS; + + size_t written = fwrite(string.str, 1, string.len, f); + if (written < string.len) { + result = OS_FAILURE; + } + + int error = fclose(f); + if (error != 0) { + result = OS_FAILURE; + } + } + return result; +} + #endif + #endif +OS_API int OS_SystemF(const char *string, ...) { + MA_Checkpoint scratch = MA_GetScratch(); + S8_FORMAT(scratch.arena, string, result); + IO_Printf("Executing: %s\n", result.str); + fflush(stdout); + int error_code = system(result.str); + MA_ReleaseScratch(scratch); + return error_code; +} + OS_API S8_String UTF_CreateStringFromWidechar(MA_Arena *arena, wchar_t *wstr, int64_t wsize) { int64_t buffer_size = (wsize + 1) * 2; char *buffer = (char *)MA_PushSizeNonZeroed(arena, buffer_size); diff --git a/filesystem.h b/filesystem.h index 99e0171..c8cd23f 100644 --- a/filesystem.h +++ b/filesystem.h @@ -43,7 +43,7 @@ OS_API bool OS_FileExists(S8_String path); OS_API bool OS_IsDir(S8_String path); OS_API bool OS_IsFile(S8_String path); OS_API double OS_GetTime(void); -OS_API S8_List OS_ListDir(MA_Arena *arena, S8_String path, unsigned flags); +OS_API S8_List OS_ListDir(MA_Arena *arena, S8_String path, unsigned flags); // @todo: this is poor API, we want absolute, relative, bool dir OS_API OS_Result OS_MakeDir(S8_String path); OS_API OS_Result OS_CopyFile(S8_String from, S8_String to, bool overwrite); OS_API OS_Result OS_DeleteFile(S8_String path); diff --git a/stb_sprintf.h b/stb_sprintf.h index 6ae1c48..5b626c9 100644 --- a/stb_sprintf.h +++ b/stb_sprintf.h @@ -596,7 +596,7 @@ STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, case 'Q': str = va_arg(va, struct STB_STRING); if (str.str == 0 && str.len != 0) { - str.str = "null"; + str.str = (char *)"null"; str.len = 4; } pr = (int)str.len; diff --git a/test/data/append_file b/test/data/append_file new file mode 100644 index 0000000..26a0e18 --- /dev/null +++ b/test/data/append_file @@ -0,0 +1 @@ +WRITE OK APPEND OK \ No newline at end of file diff --git a/test/data/read_file b/test/data/read_file new file mode 100644 index 0000000..a0aba93 --- /dev/null +++ b/test/data/read_file @@ -0,0 +1 @@ +OK \ No newline at end of file diff --git a/test/data/write_file b/test/data/write_file new file mode 100644 index 0000000..cbbe6a6 --- /dev/null +++ b/test/data/write_file @@ -0,0 +1 @@ +WRITE2 OK \ No newline at end of file diff --git a/test/test_filesystem.c b/test/test_filesystem.c new file mode 100644 index 0000000..f89520f --- /dev/null +++ b/test/test_filesystem.c @@ -0,0 +1,105 @@ +#include "../core.c" + +int main() { + MA_Arena arena = {}; + S8_String read_file_path = S8_Lit("../test/data/read_file"); + + // Read file test + { + S8_String file = OS_ReadFile(&arena, read_file_path); + IO_Assert(S8_AreEqual(file, S8_Lit("OK"), 0)); + } + + // Write file test + { + S8_String path = S8_Lit("../test/data/write_file"); + { + S8_String data = S8_Lit("WRITE1 OK"); + OS_Result result = OS_WriteFile(path, data); + IO_Assert(result == OS_SUCCESS); + + S8_String read = OS_ReadFile(&arena, path); + IO_Assert(S8_AreEqual(read, data, 0)); + } + S8_String data = S8_Lit("WRITE2 OK"); + + OS_Result result = OS_WriteFile(path, data); + IO_Assert(result == OS_SUCCESS); + + S8_String read = OS_ReadFile(&arena, path); + IO_Assert(S8_AreEqual(read, data, 0)); + } + + // Append file test + { + S8_String path = S8_Lit("../test/data/append_file"); + { + S8_String data = S8_Lit("WRITE OK"); + OS_Result result = OS_WriteFile(path, data); + IO_Assert(result == OS_SUCCESS); + } + S8_String data = S8_Lit(" APPEND OK"); + OS_Result result = OS_AppendFile(path, data); + IO_Assert(result == OS_SUCCESS); + + S8_String read = OS_ReadFile(&arena, path); + IO_Assert(S8_AreEqual(read, S8_Lit("WRITE OK APPEND OK"), 0)); + } + + IO_Assert(OS_FileExists(read_file_path)); + IO_Assert(!OS_FileExists(S8_Lit("121ffsadasd.random"))); + + // Fetching dirs + { + S8_String exe_path = OS_GetExePath(&arena); + S8_String dir_path = OS_GetExeDir(&arena); + S8_String work_path = OS_GetWorkingDir(&arena); + S8_String abs_path = OS_GetAbsolutePath(&arena, read_file_path); + // IO_Printf("Exe path = %Q\n", exe_path); + // IO_Printf("Dir path = %Q\n", dir_path); + // IO_Printf("Working path = %Q\n", work_path); + // IO_Printf("Abs path = %Q\n", abs_path); + + IO_Assert(OS_IsDir(dir_path)); + IO_Assert(!OS_IsFile(dir_path)); + + IO_Assert(OS_IsFile(exe_path)); + IO_Assert(!OS_IsDir(exe_path)); + + IO_Assert(OS_IsAbsolute(exe_path)); + IO_Assert(OS_IsAbsolute(dir_path)); + IO_Assert(OS_IsAbsolute(work_path)); + IO_Assert(OS_IsAbsolute(abs_path)); + + IO_Assert(S8_Find(exe_path, S8_Lit("/test_filesystem"), 0, 0)); + IO_Assert(S8_Find(exe_path, S8_Lit("/build"), 0, 0)); + IO_Assert(S8_Find(dir_path, S8_Lit("/build"), 0, 0)); + IO_Assert(S8_Find(work_path, S8_Lit("/build"), 0, 0)); + IO_Assert(S8_Find(abs_path, S8_Lit("/test/data"), 0, 0)); + IO_Assert(!S8_Find(abs_path, S8_Lit("../"), 0, 0)); + } + + // List dir test + { + S8_List list = OS_ListDir(&arena, S8_Lit("../test"), 0); + IO_Assert(list.node_count > 4); + int dir_count = 0; + S8_For(it, list) { + if (OS_IsDir(it->string)) { + IO_Assert(it->string.str[it->string.len - 1] == '/'); + dir_count += 1; + } + IO_Assert(S8_Find(it->string, S8_Lit("/test"), 0, 0)); + IO_Assert(!S8_Find(it->string, S8_Lit("../test"), 0, 0)); + } + IO_Assert(dir_count > 0); + + // relative + { + S8_List list = OS_ListDir(&arena, S8_Lit("../test"), OS_RELATIVE_PATHS); + S8_For(it, list) { + IO_Assert(S8_Find(it->string, S8_Lit("../test"), 0, 0)); + } + } + } +} \ No newline at end of file