|
@@ -0,0 +1,88 @@
|
|
|
+package filepath
|
|
|
+
|
|
|
+import "core:os"
|
|
|
+import "core:sort"
|
|
|
+
|
|
|
+// Walk_Proc is the type of the procedure called for each file or directory visited by 'walk'
|
|
|
+// The 'path' parameter contains the parameter to walk as a prefix (this is the same as info.fullpath except on 'root')
|
|
|
+// The 'info' parameter is the os.File_Info for the named path
|
|
|
+//
|
|
|
+// If there was a problem walking to the file or directory named by path, the incoming error will describe the problem
|
|
|
+// and the procedure can decide how to handle that error (and walk will not descend into that directory)
|
|
|
+// In the case of an error, the info argument will be 0
|
|
|
+// If an error is returned, processing stops
|
|
|
+// The sole exception is if 'skip_dir' is returned as true:
|
|
|
+// when 'skip_dir' is invoked on a directory. 'walk' skips directory contents
|
|
|
+// when 'skip_dir' is invoked on a non-directory. 'walk' skips the remaining files in the containing directory
|
|
|
+Walk_Proc :: #type proc(info: os.File_Info, in_err: os.Errno) -> (err: os.Errno, skip_dir: bool);
|
|
|
+
|
|
|
+// walk walks the file tree rooted at 'root', calling 'walk_proc' for each file or directory in the tree, including 'root'
|
|
|
+// All errors that happen visiting files and directories are filtered by walk_proc
|
|
|
+// The files are walked in lexical order to make the output deterministic
|
|
|
+// NOTE: Walking large directories can be inefficient due to the lexical sort
|
|
|
+// NOTE: walk does not follow symbolic links
|
|
|
+// NOTE: os.File_Info uses the 'context.temp_allocator' to allocate, and will delete when it is done
|
|
|
+walk :: proc(root: string, walk_proc: Walk_Proc) -> os.Errno {
|
|
|
+ info, err := os.lstat(root, context.temp_allocator);
|
|
|
+ defer os.file_info_delete(info, context.temp_allocator);
|
|
|
+
|
|
|
+ skip_dir: bool;
|
|
|
+ if err != 0 {
|
|
|
+ err, skip_dir = walk_proc(info, err);
|
|
|
+ } else {
|
|
|
+ err, skip_dir = _walk(info, walk_proc);
|
|
|
+ }
|
|
|
+ return 0 if skip_dir else err;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+@(private)
|
|
|
+_walk :: proc(info: os.File_Info, walk_proc: Walk_Proc) -> (err: os.Errno, skip_dir: bool) {
|
|
|
+ if !info.is_dir {
|
|
|
+ if info.fullpath == "" && info.name == "" {
|
|
|
+ // ignore empty things
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ return walk_proc(info, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ fis: []os.File_Info;
|
|
|
+ err1: os.Errno;
|
|
|
+ fis, err = read_dir(info.fullpath, context.temp_allocator);
|
|
|
+ defer os.file_info_slice_delete(fis, context.temp_allocator);
|
|
|
+
|
|
|
+ err1, skip_dir = walk_proc(info, err);
|
|
|
+ if err != 0 || err1 != 0 || skip_dir {
|
|
|
+ err = err1;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ for fi in fis {
|
|
|
+ err, skip_dir = _walk(fi, walk_proc);
|
|
|
+ if err != 0 || skip_dir {
|
|
|
+ if !fi.is_dir || !skip_dir {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return;
|
|
|
+}
|
|
|
+
|
|
|
+@(private)
|
|
|
+read_dir :: proc(dir_name: string, allocator := context.temp_allocator) -> ([]os.File_Info, os.Errno) {
|
|
|
+ f, err := os.open(dir_name);
|
|
|
+ if err != 0 {
|
|
|
+ return nil, err;
|
|
|
+ }
|
|
|
+ fis: []os.File_Info;
|
|
|
+ fis, err = os.read_dir(f, -1, allocator);
|
|
|
+ os.close(f);
|
|
|
+ if err != 0 {
|
|
|
+ return nil, err;
|
|
|
+ }
|
|
|
+ sort.quick_sort_proc(fis, proc(a, b: os.File_Info) -> int {
|
|
|
+ return sort.compare_strings(a.name, b.name);
|
|
|
+ });
|
|
|
+ return fis, 0;
|
|
|
+}
|