#+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()) }