aboutsummaryrefslogtreecommitdiff
path: root/core/os/dir.odin
diff options
context:
space:
mode:
authorgingerBill <gingerBill@users.noreply.github.com>2026-02-11 17:53:16 +0000
committerGitHub <noreply@github.com>2026-02-11 17:53:16 +0000
commit355b8a8c83badc64d23dbb34ea00bb89ac9245db (patch)
tree1103d5bde2dd10c28a81f2104d9c17fa0f4c3166 /core/os/dir.odin
parentb5bf28dc47dd1c32a45ba0bbedcbcef80409b798 (diff)
parent1a37f4eb0cc792783784fca216c79e3f02a3234e (diff)
Merge pull request #6245 from odin-lang/new_os
`core:os/os2` -> `core:os` integration
Diffstat (limited to 'core/os/dir.odin')
-rw-r--r--core/os/dir.odin262
1 files changed, 262 insertions, 0 deletions
diff --git a/core/os/dir.odin b/core/os/dir.odin
new file mode 100644
index 000000000..a2fba81e4
--- /dev/null
+++ b/core/os/dir.odin
@@ -0,0 +1,262 @@
+package os
+
+import "base:runtime"
+import "core:slice"
+import "core:strings"
+
+read_dir :: read_directory
+
+/*
+ Reads the file `f` (assuming it is a directory) and returns the unsorted directory entries.
+ This returns up to `n` entries OR all of them if `n <= 0`.
+*/
+@(require_results)
+read_directory :: proc(f: ^File, n: int, allocator: runtime.Allocator) -> (files: []File_Info, err: Error) {
+ if f == nil {
+ return nil, .Invalid_File
+ }
+
+ n := n
+ size := n
+ if n <= 0 {
+ n = -1
+ size = 100
+ }
+
+ temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator })
+
+ it := read_directory_iterator_create(f)
+ defer _read_directory_iterator_destroy(&it)
+
+ dfi := make([dynamic]File_Info, 0, size, temp_allocator)
+ defer if err != nil {
+ for fi in dfi {
+ file_info_delete(fi, allocator)
+ }
+ }
+
+ for fi, index in read_directory_iterator(&it) {
+ if n > 0 && index == n {
+ break
+ }
+
+ _ = read_directory_iterator_error(&it) or_break
+
+ append(&dfi, file_info_clone(fi, allocator) or_return)
+ }
+
+ _ = read_directory_iterator_error(&it) or_return
+
+ return slice.clone(dfi[:], allocator)
+}
+
+
+/*
+ Reads the file `f` (assuming it is a directory) and returns all of the unsorted directory entries.
+*/
+@(require_results)
+read_all_directory :: proc(f: ^File, allocator: runtime.Allocator) -> (fi: []File_Info, err: Error) {
+ return read_directory(f, -1, allocator)
+}
+
+/*
+ Reads the named directory by path (assuming it is a directory) and returns the unsorted directory entries.
+ This returns up to `n` entries OR all of them if `n <= 0`.
+*/
+@(require_results)
+read_directory_by_path :: proc(path: string, n: int, allocator: runtime.Allocator) -> (fi: []File_Info, err: Error) {
+ f := open(path) or_return
+ defer close(f)
+ return read_directory(f, n, allocator)
+}
+
+/*
+ Reads the named directory by path (assuming it is a directory) and returns all of the unsorted directory entries.
+*/
+@(require_results)
+read_all_directory_by_path :: proc(path: string, allocator: runtime.Allocator) -> (fi: []File_Info, err: Error) {
+ return read_directory_by_path(path, -1, allocator)
+}
+
+
+
+Read_Directory_Iterator :: struct {
+ f: ^File,
+ err: struct {
+ err: Error,
+ path: [dynamic]byte,
+ },
+ index: int,
+ impl: Read_Directory_Iterator_Impl,
+}
+
+/*
+Creates a directory iterator with the given directory.
+
+For an example on how to use the iterator, see `read_directory_iterator`.
+*/
+read_directory_iterator_create :: proc(f: ^File) -> (it: Read_Directory_Iterator) {
+ read_directory_iterator_init(&it, f)
+ return
+}
+
+/*
+Initialize a directory iterator with the given directory.
+
+This procedure may be called on an existing iterator to reuse it for another directory.
+
+For an example on how to use the iterator, see `read_directory_iterator`.
+*/
+read_directory_iterator_init :: proc(it: ^Read_Directory_Iterator, f: ^File) {
+ it.err.err = nil
+ it.err.path.allocator = file_allocator()
+ clear(&it.err.path)
+
+ it.f = f
+ it.index = 0
+
+ _read_directory_iterator_init(it, f)
+}
+
+/*
+Destroys a directory iterator.
+*/
+read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
+ if it == nil {
+ return
+ }
+
+ delete(it.err.path)
+
+ _read_directory_iterator_destroy(it)
+}
+
+/*
+Retrieve the last error that happened during iteration.
+*/
+@(require_results)
+read_directory_iterator_error :: proc(it: ^Read_Directory_Iterator) -> (path: string, err: Error) {
+ return string(it.err.path[:]), it.err.err
+}
+
+@(private)
+read_directory_iterator_set_error :: proc(it: ^Read_Directory_Iterator, path: string, err: Error) {
+ if err == nil {
+ return
+ }
+
+ resize(&it.err.path, len(path))
+ copy(it.err.path[:], path)
+
+ it.err.err = err
+}
+
+/*
+Returns the next file info entry for the iterator's directory.
+
+The given `File_Info` is reused in subsequent calls so a copy (`file_info_clone`) has to be made to
+extend its lifetime.
+
+Example:
+ package main
+
+ import "core:fmt"
+ import "core:os"
+
+ main :: proc() {
+ f, oerr := os.open("core")
+ ensure(oerr == nil)
+ defer os.close(f)
+
+ it := os.read_directory_iterator_create(f)
+ defer os.read_directory_iterator_destroy(&it)
+
+ for info in os.read_directory_iterator(&it) {
+ // Optionally break on the first error:
+ // Supports not doing this, and keeping it going with remaining items.
+ // _ = os.read_directory_iterator_error(&it) or_break
+
+ // Handle error as we go:
+ // Again, no need to do this as it will keep going with remaining items.
+ if path, err := os.read_directory_iterator_error(&it); err != nil {
+ fmt.eprintfln("failed reading %s: %s", path, err)
+ continue
+ }
+
+ // Or, do not handle errors during iteration, and just check the error at the end.
+
+
+ fmt.printfln("%#v", info)
+ }
+
+ // Handle error if one happened during iteration at the end:
+ if path, err := os.read_directory_iterator_error(&it); err != nil {
+ fmt.eprintfln("read directory failed at %s: %s", path, err)
+ }
+ }
+*/
+@(require_results)
+read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
+ if it.f == nil {
+ return
+ }
+
+ if it.index == 0 && it.err.err != nil {
+ return
+ }
+
+ return _read_directory_iterator(it)
+}
+
+// Recursively copies a directory to `dst` from `src`
+copy_directory_all :: proc(dst, src: string, dst_perm := 0o755) -> Error {
+ when #defined(_copy_directory_all_native) {
+ return _copy_directory_all_native(dst, src, dst_perm)
+ } else {
+ return _copy_directory_all(dst, src, dst_perm)
+ }
+}
+
+@(private)
+_copy_directory_all :: proc(dst, src: string, dst_perm := 0o755) -> Error {
+ err := make_directory(dst, dst_perm)
+ if err != nil && err != .Exist {
+ return err
+ }
+
+ temp_allocator := TEMP_ALLOCATOR_GUARD({})
+
+ abs_src := get_absolute_path(src, temp_allocator) or_return
+ abs_dst := get_absolute_path(dst, temp_allocator) or_return
+
+ dst_buf := make([dynamic]byte, 0, len(abs_dst) + 256, temp_allocator) or_return
+
+ w: Walker
+ walker_init_path(&w, src)
+ defer walker_destroy(&w)
+
+ for info in walker_walk(&w) {
+ _ = walker_error(&w) or_break
+
+ rel := strings.trim_prefix(info.fullpath, abs_src)
+
+ non_zero_resize(&dst_buf, 0)
+ reserve(&dst_buf, len(abs_dst) + len(Path_Separator_String) + len(rel)) or_return
+ append(&dst_buf, abs_dst)
+ append(&dst_buf, Path_Separator_String)
+ append(&dst_buf, rel)
+
+ if info.type == .Directory {
+ err = make_directory(string(dst_buf[:]), dst_perm)
+ if err != nil && err != .Exist {
+ return err
+ }
+ } else {
+ copy_file(string(dst_buf[:]), info.fullpath) or_return
+ }
+ }
+
+ _ = walker_error(&w) or_return
+
+ return nil
+}