diff --git a/build_tool/build_lib.cpp b/build_tool/build_lib.cpp index 38fd2e6..ae415e1 100644 --- a/build_tool/build_lib.cpp +++ b/build_tool/build_lib.cpp @@ -202,8 +202,9 @@ int Run(Strs s) { Strs ListDir(char *dir) { Strs result = {}; - S8_List files = OS_ListDir(Perm, S8_MakeFromChar(dir), 0); - S8_For(it, files) result.add(it->string); + for (OS_FileIter it = OS_IterateFiles(Perm, S8_MakeFromChar(dir)); OS_IsValid(it); OS_Advance(&it)) { + result.add(S8_Copy(Perm, it.absolute_path)); + } return result; } diff --git a/build_tool/build_main.cpp b/build_tool/build_main.cpp index a709944..d769446 100644 --- a/build_tool/build_main.cpp +++ b/build_tool/build_main.cpp @@ -26,10 +26,9 @@ int main(int argument_count, char **arguments) { // Search for build file in the project directory S8_String build_file = {0}; { - S8_List files_current_dir = OS_ListDir(Perm, S8_Lit(".."), 0); - for (S8_Node *it = files_current_dir.first; it; it = it->next) { - if (S8_Find(it->string, S8_Lit("build_file.c"), S8_IgnoreCase)) { - build_file = it->string; + for (OS_FileIter it = OS_IterateFiles(Perm, S8_Lit("..")); OS_IsValid(it); OS_Advance(&it)) { + if (S8_Find(it.filename, S8_Lit("build_file.c"), S8_IgnoreCase)) { + build_file = it.absolute_path; } } @@ -39,9 +38,8 @@ int main(int argument_count, char **arguments) { } } - S8_String a = S8_ChopLastPeriod(build_file); - S8_String b = S8_SkipToLastSlash(a); - S8_String exe_name = S8_Format(Perm, "%.*s.exe", S8_Expand(b)); + S8_String name_no_ext = S8_GetNameNoExt(build_file); + S8_String exe_name = S8_Format(Perm, "%.*s.exe", S8_Expand(name_no_ext)); // Compile the build file only if code changed if (SRC_WasModified(build_file, exe_name)) { diff --git a/build_tool/cache.c b/build_tool/cache.c index bf1ad75..d6788f9 100644 --- a/build_tool/cache.c +++ b/build_tool/cache.c @@ -62,7 +62,7 @@ SRC_CacheEntry *SRC_FindCache(SRC_Cache *cache, uint64_t filepath_hash) { SRC_CacheEntry *SRC_HashFile(S8_String file, char *parent_file) { char *resolved_file = CL_ResolveFilepath(Perm, &SRC_SearchPaths, file.str, parent_file, false); if (!resolved_file) { - IO_Printf("Failed to resolve file: %s\n", file.str); + IO_Printf("Failed to resolve file: %.*s\n", S8_Expand(file)); return 0; } @@ -82,7 +82,7 @@ SRC_CacheEntry *SRC_HashFile(S8_String file, char *parent_file) { S8_String file_it = S8_MakeFromChar(iter.filename); SRC_CacheEntry *cache = SRC_HashFile(file_it, resolved_file); if (!cache) { - IO_Printf("Missing cache for: %s\n", file_it.str); + IO_Printf("Missing cache for: %.*s\n", S8_Expand(file_it)); continue; } diff --git a/core_library/core.c b/core_library/core.c index 16fcf68..b36a856 100644 --- a/core_library/core.c +++ b/core_library/core.c @@ -1,10 +1,8 @@ /* +- I think it's okay to say that strings being null terminated should be mostly treated as NOT terminated but leave some + leeway for big buffers and other such things. Just make sure to not relay on it because it's easier unless specified. +- Not sure if we should assume that strings should use allocators or arenas, for now it's arenas because I don't have other use cases @todo -- Rewrite OS_ListDir, no weird flags and conventions, [relative, absolute, is_dir] -- add c++ default args using macros -- Rework string flags -- String should use allocator instead of arena? -- Maybe remove completely null termination from S8_String, I think it might complicate and is error prone because one can sometimes assume it's null terminated and it isn't - Maybe create a nice C++ binding for strings, - also add String Arrays and String Builder, temp allocators hook ins for nicer api - Iterating over unicode codepoints using For diff --git a/core_library/filesystem.c b/core_library/filesystem.c index 6c17798..00bc33a 100644 --- a/core_library/filesystem.c +++ b/core_library/filesystem.c @@ -126,69 +126,72 @@ OS_API double OS_GetTime(void) { return result; } -// @todo: I think we want a list with both relative + absolute + other things -// -// Returns directories with slash at the end '/' -// By default returns absolute paths -OS_API S8_List OS_ListDir(MA_Arena *arena, S8_String path, unsigned flags) { - MA_Checkpoint scratch = MA_GetScratch1(arena); - S8_List dirs_to_read = S8_MakeEmptyList(); - S8_List result = S8_MakeEmptyList(); - S8_AddNode(scratch.arena, &dirs_to_read, path); +/* +User needs to copy particular filename to keep it. - for (S8_Node *it = dirs_to_read.first; it; it = it->next) { - wchar_t wbuff[1024]; - S8_String modified_path = S8_Format(scratch.arena, "%.*s\\*", (int)it->string.len, it->string.str); - IO_Assert(modified_path.len < MA_LENGTHOF(wbuff)); - int64_t wsize = UTF_CreateWidecharFromChar(wbuff, MA_LENGTHOF(wbuff), modified_path.str, modified_path.len); - IO_Assert(wsize); +for (OS_FileIter it = OS_IterateFiles(it); OS_IsValid(iter); OS_Advance(it)) { +} - WIN32_FIND_DATAW ffd; - HANDLE handle = FindFirstFileW(wbuff, &ffd); - if (handle == INVALID_HANDLE_VALUE) - continue; +*/ - do { +typedef struct OS_Win32_FileIter { + HANDLE handle; + WIN32_FIND_DATAW data; +} OS_Win32_FileIter; - // - // Skip '.' and '..' - // - if (ffd.cFileName[0] == '.') { - if (ffd.cFileName[1] == '.') { - if (ffd.cFileName[2] == 0) - continue; - } +OS_API bool OS_IsValid(OS_FileIter it) { + return it.is_valid; +} - if (ffd.cFileName[1] == 0) - continue; - } +OS_API void OS_Advance(OS_FileIter *it) { + while (FindNextFileW(it->w32->handle, &it->w32->data) != 0) { + WIN32_FIND_DATAW *data = &it->w32->data; - bool dir = ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; - S8_String filename = UTF_CreateStringFromWidechar(scratch.arena, ffd.cFileName, S8_WideLength(ffd.cFileName)); - S8_String is_dir = dir ? S8_Lit("/") : S8_Lit(""); - S8_String rel_abs_path = S8_Format(scratch.arena, "%.*s/%.*s%.*s", S8_Expand(it->string), S8_Expand(filename), S8_Expand(is_dir)); - if (flags & OS_RELATIVE_PATHS) { - S8_Add(arena, &result, rel_abs_path); - } - else { - S8_String abs_path = OS_GetAbsolutePath(arena, rel_abs_path); - S8_Add(arena, &result, abs_path); - } + // Skip '.' and '..' + if (data->cFileName[0] == '.' && data->cFileName[1] == '.' && data->cFileName[2] == 0) continue; + if (data->cFileName[0] == '.' && data->cFileName[1] == 0) continue; - if (dir && flags & OS_RECURSIVE) { - S8_AddNode(scratch.arena, &dirs_to_read, rel_abs_path); - } - } while (FindNextFileW(handle, &ffd) != 0); + it->is_directory = data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; + it->filename = UTF_CreateStringFromWidechar(it->arena, data->cFileName, S8_WideLength(data->cFileName)); + char *is_dir = it->is_directory ? "/" : ""; + char *separator = it->path.str[it->path.len - 1] == '/' ? "" : "/"; + it->relative_path = S8_Format(it->arena, "%.*s%s%.*s%s", S8_Expand(it->path), separator, S8_Expand(it->filename), is_dir); + it->absolute_path = OS_GetAbsolutePath(it->arena, it->relative_path); + it->is_valid = true; - DWORD error = GetLastError(); - if (error != ERROR_NO_MORE_FILES) { - // Not sure what to do here hmmm + if (it->is_directory) { + IO_Assert(it->relative_path.str[it->relative_path.len - 1] == '/'); + IO_Assert(it->absolute_path.str[it->absolute_path.len - 1] == '/'); } - FindClose(handle); + return; } - MA_ReleaseScratch(scratch); - return result; + it->is_valid = false; + DWORD error = GetLastError(); + IO_Assert(error == ERROR_NO_MORE_FILES); + FindClose(it->w32->handle); +} + +OS_API OS_FileIter OS_IterateFiles(MA_Arena *scratch_arena, S8_String path) { + OS_FileIter it = {0}; + it.arena = scratch_arena; + it.path = path; + + S8_String modified_path = S8_Format(it.arena, "%.*s\\*", S8_Expand(path)); + wchar_t *wbuff = MA_PushArray(it.arena, wchar_t, modified_path.len + 1); + int64_t wsize = UTF_CreateWidecharFromChar(wbuff, modified_path.len + 1, modified_path.str, modified_path.len); + IO_Assert(wsize); + + it.w32 = MA_PushStruct(it.arena, OS_Win32_FileIter); + it.w32->handle = FindFirstFileW(wbuff, &it.w32->data); + if (it.w32->handle == INVALID_HANDLE_VALUE) { + it.is_valid = false; + return it; + } + + IO_Assert(it.w32->data.cFileName[0] == '.' && it.w32->data.cFileName[1] == 0); + OS_Advance(&it); + return it; } OS_API OS_Result OS_MakeDir(S8_String path) { @@ -238,6 +241,9 @@ OS_API OS_Result OS_DeleteFile(S8_String path) { } OS_API OS_Result OS_DeleteDir(S8_String path, unsigned flags) { + IO_Todo(); + return OS_FAILURE; + #if 0 if (flags & OS_RECURSIVE) { MA_Checkpoint scratch = MA_GetScratch(); S8_List list = OS_ListDir(scratch.arena, path, OS_RECURSIVE); @@ -267,6 +273,7 @@ OS_API OS_Result OS_DeleteDir(S8_String path, unsigned flags) { result = OS_PATH_NOT_FOUND; return result; } + #endif } static OS_Result OS__WriteFile(S8_String path, S8_String data, bool append) { @@ -475,41 +482,39 @@ OS_API double OS_GetTime(void) { return result; } -OS_API S8_List OS_ListDir(MA_Arena *arena, S8_String path, unsigned flags) { - IO_Assert((flags & OS_RECURSIVE) == 0); - MA_Checkpoint scratch = MA_GetScratch1(arena); - path = S8_Copy(scratch.arena, path); +OS_API bool OS_IsValid(OS_FileIter it) { + return it.is_valid; +} - 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; - } +OS_API void OS_Advance(OS_FileIter *it) { + struct dirent *file = 0; + while ((file = readdir((DIR *)it->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; - if (dir->d_name[1] == 0) - continue; - } + it->is_directory = file->d_type == DT_DIR; + it->filename = S8_CopyChar(it->arena, file->d_name); - S8_String n = S8_Format(scratch.arena, "%.*s/%s", S8_Expand(path), dir->d_name); - if ((flags & OS_RELATIVE_PATHS) == 0) { - n = OS_GetAbsolutePath(scratch.arena, n); - } - if (dir->d_type == DT_DIR) { - n = S8_Format(scratch.arena, "%.*s/", S8_Expand(n)); - } - - S8_AddNode(arena, &result, S8_Copy(arena, n)); - } - closedir(d); + const char *is_dir = it->is_directory ? "/" : ""; + const char *separator = it->path.str[it->path.len - 1] == '/' ? "" : "/"; + it->relative_path = S8_Format(it->arena, "%.*s%s%s%s", S8_Expand(it->path), separator, file->d_name, is_dir); + it->absolute_path = OS_GetAbsolutePath(it->arena, it->relative_path); + it->is_valid = true; + return; } + it->is_valid = false; + closedir((DIR *)it->dir); +} - MA_ReleaseScratch(scratch); - return result; +OS_API OS_FileIter OS_IterateFiles(MA_Arena *arena, S8_String path) { + OS_FileIter it = {0}; + it.arena = arena; + it.path = path = S8_Copy(arena, path); + it.dir = (void *)opendir((char *)path.str); + if (!it.dir) return it; + + OS_Advance(&it); + return it; } OS_API OS_Result OS_MakeDir(S8_String path) { @@ -651,7 +656,7 @@ OS_API OS_Result OS_WriteFile(S8_String path, S8_String string) { 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); + IO_Printf("Executing: %.*s\n", S8_Expand(result)); fflush(stdout); int error_code = system(result.str); MA_ReleaseScratch(scratch); @@ -666,45 +671,6 @@ OS_API S8_String UTF_CreateStringFromWidechar(MA_Arena *arena, wchar_t *wstr, in return S8_Make(buffer, size); } -OS_API S8_List S8_SplitOnRegex(MA_Arena *arena, S8_String string, S8_String regex, unsigned flags) { - S8_List result = S8_MakeEmptyList(); - int64_t index = 0; - - char buff[4096]; - RE_Regex *re = RE2_Parse(buff, sizeof(buff), regex.str, regex.len); - for (RE_Match match = RE3_Find(re, string.str, string.len); match.pos != -1; match = RE3_Find(re, string.str, string.len)) { - S8_String before_match = S8_Make(string.str, match.pos); - S8_String the_match = S8_Make(string.str + match.pos, match.size); - if (before_match.len) S8_AddNode(arena, &result, before_match); - if (flags & S8_SplitFlag_SplitInclusive) { - if (the_match.len) S8_AddNode(arena, &result, the_match); - } - string = S8_Skip(string, match.pos + match.size); - } - S8_AddNode(arena, &result, string); - return result; -} - -OS_API S8_List OS_ListDirRegex(MA_Arena *arena, S8_String path, unsigned flags, char *regex) { - S8_List result = S8_MakeEmptyList(); - - char buff[4096]; - RE_Regex *re = RE1_Parse(buff, sizeof(buff), regex); - S8_List files = OS_ListDir(arena, path, flags); - for (S8_Node *it = files.first; it; it = it->next) { - if (RE3_AreEqual(re, it->string.str, it->string.len)) { - S8_AddNode(arena, &result, it->string); - } - } - return result; -} - -OS_API S8_String OS_ListDirRegexAsString(MA_Arena *arena, S8_String path, unsigned flags, char *regex) { - S8_List files = OS_ListDirRegex(arena, path, flags, regex); - S8_String files_str = S8_MergeWithSeparator(arena, files, S8_Lit(" ")); - return files_str; -} - OS_API bool OS_ExpandIncludesList(MA_Arena *arena, S8_List *out, S8_String filepath) { S8_String c = OS_ReadFile(arena, filepath); if (c.str == 0) return false; diff --git a/core_library/filesystem.h b/core_library/filesystem.h index f0ab35b..ba04b0e 100644 --- a/core_library/filesystem.h +++ b/core_library/filesystem.h @@ -8,8 +8,6 @@ #endif #endif -typedef struct OS_Date OS_Date; - typedef enum OS_Result { OS_SUCCESS, OS_ALREADY_EXISTS, @@ -23,6 +21,7 @@ enum { OS_RELATIVE_PATHS = 2, }; +typedef struct OS_Date OS_Date; struct OS_Date { uint32_t year; uint32_t month; @@ -33,6 +32,22 @@ struct OS_Date { uint32_t milliseconds; }; +typedef struct OS_FileIter OS_FileIter; +struct OS_FileIter { + bool is_valid; + bool is_directory; + S8_String absolute_path; + S8_String relative_path; + S8_String filename; + + S8_String path; + MA_Arena *arena; + union { + struct OS_Win32_FileIter *w32; + void *dir; + }; +}; + OS_API bool OS_IsAbsolute(S8_String path); OS_API S8_String OS_GetExePath(MA_Arena *arena); OS_API S8_String OS_GetExeDir(MA_Arena *arena); @@ -43,7 +58,6 @@ 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); // @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); @@ -55,9 +69,10 @@ OS_API int OS_SystemF(const char *string, ...); OS_API int64_t OS_GetFileModTime(S8_String file); OS_API OS_Date OS_GetDate(void); OS_API S8_String UTF_CreateStringFromWidechar(MA_Arena *arena, wchar_t *wstr, int64_t wsize); -OS_API S8_List S8_SplitOnRegex(MA_Arena *arena, S8_String string, S8_String regex, unsigned flags); -OS_API S8_List OS_ListDirRegex(MA_Arena *arena, S8_String path, unsigned flags, char *regex); -OS_API S8_String OS_ListDirRegexAsString(MA_Arena *arena, S8_String path, unsigned flags, char *regex); OS_API bool OS_ExpandIncludesList(MA_Arena *arena, S8_List *out, S8_String filepath); OS_API S8_String OS_ExpandIncludes(MA_Arena *arena, S8_String filepath); OS_API bool OS_EnableTerminalColors(void); + +OS_API bool OS_IsValid(OS_FileIter it); +OS_API void OS_Advance(OS_FileIter *it); +OS_API OS_FileIter OS_IterateFiles(MA_Arena *scratch_arena, S8_String path); \ No newline at end of file diff --git a/standalone_libraries/string.c b/standalone_libraries/string.c index ae1138f..010f3c6 100644 --- a/standalone_libraries/string.c +++ b/standalone_libraries/string.c @@ -111,6 +111,15 @@ S8_API S8_String S8_Copy(S8_Allocator allocator, S8_String string) { return result; } +S8_API S8_String S8_CopyChar(S8_Allocator allocator, char *s) { + int64_t len = S8_Length(s); + char *copy = (char *)S8_ALLOCATE(allocator, sizeof(char) * (len + 1)); + S8_MemoryCopy(copy, s, len); + copy[len] = 0; + S8_String result = S8_Make(copy, len); + return result; +} + S8_API void S8_NormalizePath(S8_String s) { for (int64_t i = 0; i < s.len; i++) { if (s.str[i] == '\\') @@ -169,7 +178,7 @@ S8_API S8_String S8_GetPrefix(S8_String string, int64_t len) { return result; } -S8_API S8_String S8_GetNameNoExtension(S8_String s) { +S8_API S8_String S8_GetNameNoExt(S8_String s) { return S8_SkipToLastSlash(S8_ChopLastPeriod(s)); } diff --git a/standalone_libraries/string.h b/standalone_libraries/string.h index 0e811b4..d1bc5b6 100644 --- a/standalone_libraries/string.h +++ b/standalone_libraries/string.h @@ -32,19 +32,6 @@ struct S8_String { int64_t len; }; -// #ifdef __cplusplus -// struct String : S8_String { -// String() = default; -// String(char *s) : S8_String{s, S8_Length(s)} {} -// String(char *s, int64_t l) : S8_String{s, l} {} -// String(const char *s) : S8_String{(char *)s, S8_Length((char *)s)} {} -// String(const char *s, int64_t l) : S8_String{(char *)s, l} {} -// String(S8_String s) : S8_String{s.str, s.len} {} - -// // @todo add unicode iterator -// }; -// #endif - struct S8_Node { S8_Node *next; S8_String string; diff --git a/tests/1test_string.cpp b/tests/1test_string.cpp index 7330dd2..6f1b662 100644 --- a/tests/1test_string.cpp +++ b/tests/1test_string.cpp @@ -1,4 +1,10 @@ #include "../core_library/core.c" int main() { + MA_Scratch scratch; + for (OS_FileIter it = OS_IterateFiles(scratch, "../"_s); OS_IsValid(it); OS_Advance(&it)) { + IO_Printf("is_directory: %d\n", it.is_directory); + IO_Printf("absolute_path: %.*s\n", S8_Expand(it.absolute_path)); + IO_Printf("relative_path: %.*s\n", S8_Expand(it.relative_path)); + } } \ No newline at end of file diff --git a/tests/test_filesystem.c b/tests/test_filesystem.c index f0dc9a2..4b375a3 100644 --- a/tests/test_filesystem.c +++ b/tests/test_filesystem.c @@ -74,28 +74,4 @@ int main() { IO_Assert(S8_Find(abs_path, S8_Lit("/tests/data"), 0, 0)); IO_Assert(!S8_Find(abs_path, S8_Lit("../"), 0, 0)); } - - // List dir test - { - S8_List list = OS_ListDir(&arena, S8_Lit("../tests"), 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("/tests"), 0, 0)); - IO_Assert(!S8_Find(it->string, S8_Lit("../tests"), 0, 0)); - } - IO_Assert(dir_count > 0); - - // relative - { - S8_List list = OS_ListDir(&arena, S8_Lit("../tests"), OS_RELATIVE_PATHS); - S8_For(it, list) { - IO_Assert(S8_Find(it->string, S8_Lit("../tests"), 0, 0)); - } - } - } } \ No newline at end of file