|
@@ -1,20 +1,101 @@
|
|
|
#+private
|
|
|
package os2
|
|
|
|
|
|
-Read_Directory_Iterator_Impl :: struct {
|
|
|
+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) {
|
|
|
- return {}, .Unsupported
|
|
|
+ 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())
|
|
|
}
|