New list dir api

This commit is contained in:
Krzosa Karol
2024-01-13 14:49:38 +01:00
parent 2cb4160497
commit 3408f3a1ff
10 changed files with 138 additions and 182 deletions

View File

@@ -202,8 +202,9 @@ int Run(Strs s) {
Strs ListDir(char *dir) { Strs ListDir(char *dir) {
Strs result = {}; Strs result = {};
S8_List files = OS_ListDir(Perm, S8_MakeFromChar(dir), 0); for (OS_FileIter it = OS_IterateFiles(Perm, S8_MakeFromChar(dir)); OS_IsValid(it); OS_Advance(&it)) {
S8_For(it, files) result.add(it->string); result.add(S8_Copy(Perm, it.absolute_path));
}
return result; return result;
} }

View File

@@ -26,10 +26,9 @@ int main(int argument_count, char **arguments) {
// Search for build file in the project directory // Search for build file in the project directory
S8_String build_file = {0}; S8_String build_file = {0};
{ {
S8_List files_current_dir = OS_ListDir(Perm, S8_Lit(".."), 0); for (OS_FileIter it = OS_IterateFiles(Perm, S8_Lit("..")); OS_IsValid(it); OS_Advance(&it)) {
for (S8_Node *it = files_current_dir.first; it; it = it->next) { if (S8_Find(it.filename, S8_Lit("build_file.c"), S8_IgnoreCase)) {
if (S8_Find(it->string, S8_Lit("build_file.c"), S8_IgnoreCase)) { build_file = it.absolute_path;
build_file = it->string;
} }
} }
@@ -39,9 +38,8 @@ int main(int argument_count, char **arguments) {
} }
} }
S8_String a = S8_ChopLastPeriod(build_file); S8_String name_no_ext = S8_GetNameNoExt(build_file);
S8_String b = S8_SkipToLastSlash(a); S8_String exe_name = S8_Format(Perm, "%.*s.exe", S8_Expand(name_no_ext));
S8_String exe_name = S8_Format(Perm, "%.*s.exe", S8_Expand(b));
// Compile the build file only if code changed // Compile the build file only if code changed
if (SRC_WasModified(build_file, exe_name)) { if (SRC_WasModified(build_file, exe_name)) {

View File

@@ -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) { SRC_CacheEntry *SRC_HashFile(S8_String file, char *parent_file) {
char *resolved_file = CL_ResolveFilepath(Perm, &SRC_SearchPaths, file.str, parent_file, false); char *resolved_file = CL_ResolveFilepath(Perm, &SRC_SearchPaths, file.str, parent_file, false);
if (!resolved_file) { 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; return 0;
} }
@@ -82,7 +82,7 @@ SRC_CacheEntry *SRC_HashFile(S8_String file, char *parent_file) {
S8_String file_it = S8_MakeFromChar(iter.filename); S8_String file_it = S8_MakeFromChar(iter.filename);
SRC_CacheEntry *cache = SRC_HashFile(file_it, resolved_file); SRC_CacheEntry *cache = SRC_HashFile(file_it, resolved_file);
if (!cache) { if (!cache) {
IO_Printf("Missing cache for: %s\n", file_it.str); IO_Printf("Missing cache for: %.*s\n", S8_Expand(file_it));
continue; continue;
} }

View File

@@ -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 @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, - Maybe create a nice C++ binding for strings,
- also add String Arrays and String Builder, temp allocators hook ins for nicer api - also add String Arrays and String Builder, temp allocators hook ins for nicer api
- Iterating over unicode codepoints using For - Iterating over unicode codepoints using For

View File

@@ -126,69 +126,72 @@ OS_API double OS_GetTime(void) {
return result; return result;
} }
// @todo: I think we want a list with both relative + absolute + other things /*
// User needs to copy particular filename to keep it.
// 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);
for (S8_Node *it = dirs_to_read.first; it; it = it->next) { for (OS_FileIter it = OS_IterateFiles(it); OS_IsValid(iter); OS_Advance(it)) {
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);
typedef struct OS_Win32_FileIter {
HANDLE handle;
WIN32_FIND_DATAW data;
} OS_Win32_FileIter;
OS_API bool OS_IsValid(OS_FileIter it) {
return it.is_valid;
}
OS_API void OS_Advance(OS_FileIter *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->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;
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] == '/');
}
return;
}
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); IO_Assert(wsize);
WIN32_FIND_DATAW ffd; it.w32 = MA_PushStruct(it.arena, OS_Win32_FileIter);
HANDLE handle = FindFirstFileW(wbuff, &ffd); it.w32->handle = FindFirstFileW(wbuff, &it.w32->data);
if (handle == INVALID_HANDLE_VALUE) if (it.w32->handle == INVALID_HANDLE_VALUE) {
continue; it.is_valid = false;
return it;
do {
//
// Skip '.' and '..'
//
if (ffd.cFileName[0] == '.') {
if (ffd.cFileName[1] == '.') {
if (ffd.cFileName[2] == 0)
continue;
} }
if (ffd.cFileName[1] == 0) IO_Assert(it.w32->data.cFileName[0] == '.' && it.w32->data.cFileName[1] == 0);
continue; OS_Advance(&it);
} return it;
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);
}
if (dir && flags & OS_RECURSIVE) {
S8_AddNode(scratch.arena, &dirs_to_read, rel_abs_path);
}
} while (FindNextFileW(handle, &ffd) != 0);
DWORD error = GetLastError();
if (error != ERROR_NO_MORE_FILES) {
// Not sure what to do here hmmm
}
FindClose(handle);
}
MA_ReleaseScratch(scratch);
return result;
} }
OS_API OS_Result OS_MakeDir(S8_String path) { 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) { OS_API OS_Result OS_DeleteDir(S8_String path, unsigned flags) {
IO_Todo();
return OS_FAILURE;
#if 0
if (flags & OS_RECURSIVE) { if (flags & OS_RECURSIVE) {
MA_Checkpoint scratch = MA_GetScratch(); MA_Checkpoint scratch = MA_GetScratch();
S8_List list = OS_ListDir(scratch.arena, path, OS_RECURSIVE); 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; result = OS_PATH_NOT_FOUND;
return result; return result;
} }
#endif
} }
static OS_Result OS__WriteFile(S8_String path, S8_String data, bool append) { 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; return result;
} }
OS_API S8_List OS_ListDir(MA_Arena *arena, S8_String path, unsigned flags) { OS_API bool OS_IsValid(OS_FileIter it) {
IO_Assert((flags & OS_RECURSIVE) == 0); return it.is_valid;
MA_Checkpoint scratch = MA_GetScratch1(arena); }
path = S8_Copy(scratch.arena, path);
S8_List result = {0}; OS_API void OS_Advance(OS_FileIter *it) {
struct dirent *dir = 0; struct dirent *file = 0;
DIR *d = opendir((char *)path.str); while ((file = readdir((DIR *)it->dir)) != NULL) {
if (d) { if (file->d_name[0] == '.' && file->d_name[1] == '.' && file->d_name[2] == 0) continue;
while ((dir = readdir(d)) != NULL) { if (file->d_name[0] == '.' && file->d_name[1] == 0) continue;
if (dir->d_name[0] == '.') {
if (dir->d_name[1] == '.') {
if (dir->d_name[2] == 0)
continue;
}
if (dir->d_name[1] == 0) it->is_directory = file->d_type == DT_DIR;
continue; it->filename = S8_CopyChar(it->arena, file->d_name);
}
S8_String n = S8_Format(scratch.arena, "%.*s/%s", S8_Expand(path), dir->d_name); const char *is_dir = it->is_directory ? "/" : "";
if ((flags & OS_RELATIVE_PATHS) == 0) { const char *separator = it->path.str[it->path.len - 1] == '/' ? "" : "/";
n = OS_GetAbsolutePath(scratch.arena, n); 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);
if (dir->d_type == DT_DIR) { it->is_valid = true;
n = S8_Format(scratch.arena, "%.*s/", S8_Expand(n)); return;
} }
it->is_valid = false;
closedir((DIR *)it->dir);
}
S8_AddNode(arena, &result, S8_Copy(arena, n)); OS_API OS_FileIter OS_IterateFiles(MA_Arena *arena, S8_String path) {
} OS_FileIter it = {0};
closedir(d); it.arena = arena;
} it.path = path = S8_Copy(arena, path);
it.dir = (void *)opendir((char *)path.str);
if (!it.dir) return it;
MA_ReleaseScratch(scratch); OS_Advance(&it);
return result; return it;
} }
OS_API OS_Result OS_MakeDir(S8_String path) { 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, ...) { OS_API int OS_SystemF(const char *string, ...) {
MA_Checkpoint scratch = MA_GetScratch(); MA_Checkpoint scratch = MA_GetScratch();
S8_FORMAT(scratch.arena, string, result); S8_FORMAT(scratch.arena, string, result);
IO_Printf("Executing: %s\n", result.str); IO_Printf("Executing: %.*s\n", S8_Expand(result));
fflush(stdout); fflush(stdout);
int error_code = system(result.str); int error_code = system(result.str);
MA_ReleaseScratch(scratch); 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); 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) { OS_API bool OS_ExpandIncludesList(MA_Arena *arena, S8_List *out, S8_String filepath) {
S8_String c = OS_ReadFile(arena, filepath); S8_String c = OS_ReadFile(arena, filepath);
if (c.str == 0) return false; if (c.str == 0) return false;

View File

@@ -8,8 +8,6 @@
#endif #endif
#endif #endif
typedef struct OS_Date OS_Date;
typedef enum OS_Result { typedef enum OS_Result {
OS_SUCCESS, OS_SUCCESS,
OS_ALREADY_EXISTS, OS_ALREADY_EXISTS,
@@ -23,6 +21,7 @@ enum {
OS_RELATIVE_PATHS = 2, OS_RELATIVE_PATHS = 2,
}; };
typedef struct OS_Date OS_Date;
struct OS_Date { struct OS_Date {
uint32_t year; uint32_t year;
uint32_t month; uint32_t month;
@@ -33,6 +32,22 @@ struct OS_Date {
uint32_t milliseconds; 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 bool OS_IsAbsolute(S8_String path);
OS_API S8_String OS_GetExePath(MA_Arena *arena); OS_API S8_String OS_GetExePath(MA_Arena *arena);
OS_API S8_String OS_GetExeDir(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_IsDir(S8_String path);
OS_API bool OS_IsFile(S8_String path); OS_API bool OS_IsFile(S8_String path);
OS_API double OS_GetTime(void); 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_MakeDir(S8_String path);
OS_API OS_Result OS_CopyFile(S8_String from, S8_String to, bool overwrite); OS_API OS_Result OS_CopyFile(S8_String from, S8_String to, bool overwrite);
OS_API OS_Result OS_DeleteFile(S8_String path); 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 int64_t OS_GetFileModTime(S8_String file);
OS_API OS_Date OS_GetDate(void); 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_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 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 S8_String OS_ExpandIncludes(MA_Arena *arena, S8_String filepath);
OS_API bool OS_EnableTerminalColors(void); 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);

View File

@@ -111,6 +111,15 @@ S8_API S8_String S8_Copy(S8_Allocator allocator, S8_String string) {
return result; 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) { S8_API void S8_NormalizePath(S8_String s) {
for (int64_t i = 0; i < s.len; i++) { for (int64_t i = 0; i < s.len; i++) {
if (s.str[i] == '\\') if (s.str[i] == '\\')
@@ -169,7 +178,7 @@ S8_API S8_String S8_GetPrefix(S8_String string, int64_t len) {
return result; 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)); return S8_SkipToLastSlash(S8_ChopLastPeriod(s));
} }

View File

@@ -32,19 +32,6 @@ struct S8_String {
int64_t len; 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 { struct S8_Node {
S8_Node *next; S8_Node *next;
S8_String string; S8_String string;

View File

@@ -1,4 +1,10 @@
#include "../core_library/core.c" #include "../core_library/core.c"
int main() { 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));
}
} }

View File

@@ -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("/tests/data"), 0, 0));
IO_Assert(!S8_Find(abs_path, S8_Lit("../"), 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));
}
}
}
} }