Files
Odin/core/os/os2/dir_linux.odin
jason fa7ef28acf Implement _read_directory_iterator in os2.
Also, fix minor bug in linux.dirent_name.
2025-01-10 20:54:09 -05:00

102 lines
2.5 KiB
Odin

#+private
package os2
import "core:sys/linux"
Read_Directory_Iterator_Impl :: struct {
prev_fi: File_Info,
dirent_backing: []u8,
dirent_buflen: int,
dirent_off: int,
index: int,
}
@(require_results)
_read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
scan_entries :: proc(dfd: linux.Fd, entries: []u8, offset: ^int) -> (fd: linux.Fd, file_name: string) {
for d in linux.dirent_iterate_buf(entries, offset) {
file_name = linux.dirent_name(d)
if file_name == "." || file_name == ".." {
continue
}
file_name_cstr := cstring(raw_data(file_name))
entry_fd, errno := linux.openat(dfd, file_name_cstr, {.NOFOLLOW, .PATH})
if errno == .NONE {
return entry_fd, file_name
}
}
return -1, ""
}
index = it.impl.index
it.impl.index += 1
dfd := linux.Fd(_fd(it.f))
entries := it.impl.dirent_backing[:it.impl.dirent_buflen]
entry_fd, file_name := scan_entries(dfd, entries, &it.impl.dirent_off)
for entry_fd == -1 {
if len(it.impl.dirent_backing) == 0 {
it.impl.dirent_backing = make([]u8, 512, file_allocator())
}
loop: for {
buflen, errno := linux.getdents(linux.Fd(dfd), it.impl.dirent_backing[:])
#partial switch errno {
case .EINVAL:
delete(it.impl.dirent_backing, file_allocator())
n := len(it.impl.dirent_backing) * 2
it.impl.dirent_backing = make([]u8, n, file_allocator())
continue
case .NONE:
if buflen == 0 {
return
}
it.impl.dirent_off = 0
it.impl.dirent_buflen = buflen
entries = it.impl.dirent_backing[:buflen]
break loop
case: // error
return
}
}
entry_fd, file_name = scan_entries(dfd, entries, &it.impl.dirent_off)
}
defer linux.close(entry_fd)
file_info_delete(it.impl.prev_fi, file_allocator())
fi, _ = _fstat_internal(entry_fd, file_allocator())
it.impl.prev_fi = fi
ok = true
return
}
@(require_results)
_read_directory_iterator_create :: proc(f: ^File) -> (Read_Directory_Iterator, Error) {
if f == nil || f.impl == nil {
return {}, .Invalid_File
}
stat: linux.Stat
errno := linux.fstat(linux.Fd(fd(f)), &stat)
if errno != .NONE {
return {}, _get_platform_error(errno)
}
if (stat.mode & linux.S_IFMT) != linux.S_IFDIR {
return {}, .Invalid_Dir
}
return {f = f}, nil
}
_read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
if it == nil {
return
}
delete(it.impl.dirent_backing, file_allocator())
file_info_delete(it.impl.prev_fi, file_allocator())
}