mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-05 15:18:49 +00:00
Merge pull request #4675 from jasonKercher/os2-dir-linux
Implement os2 dir linux
This commit is contained in:
@@ -1,20 +1,101 @@
|
|||||||
#+private
|
#+private
|
||||||
package os2
|
package os2
|
||||||
|
|
||||||
|
import "core:sys/linux"
|
||||||
|
|
||||||
Read_Directory_Iterator_Impl :: struct {
|
Read_Directory_Iterator_Impl :: struct {
|
||||||
|
prev_fi: File_Info,
|
||||||
|
dirent_backing: []u8,
|
||||||
|
dirent_buflen: int,
|
||||||
|
dirent_off: int,
|
||||||
|
index: int,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@(require_results)
|
@(require_results)
|
||||||
_read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
|
_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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@(require_results)
|
@(require_results)
|
||||||
_read_directory_iterator_create :: proc(f: ^File) -> (Read_Directory_Iterator, Error) {
|
_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) {
|
_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())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,22 +86,18 @@ dirent_iterate_buf :: proc "contextless" (buf: []u8, offs: ^int) -> (d: ^Dirent,
|
|||||||
/// The lifetime of the string is bound to the lifetime of the provided dirent structure
|
/// The lifetime of the string is bound to the lifetime of the provided dirent structure
|
||||||
dirent_name :: proc "contextless" (dirent: ^Dirent) -> string #no_bounds_check {
|
dirent_name :: proc "contextless" (dirent: ^Dirent) -> string #no_bounds_check {
|
||||||
str := ([^]u8)(&dirent.name)
|
str := ([^]u8)(&dirent.name)
|
||||||
// Note(flysand): The string size calculated above applies only to the ideal case
|
// Dirents are aligned to 8 bytes, so there is guaranteed to be a null
|
||||||
// we subtract 1 byte from the string size, because a null terminator is guaranteed
|
// terminator in the last 8 bytes.
|
||||||
// to be present. But! That said, the dirents are aligned to 8 bytes and the padding
|
str_size := int(dirent.reclen) - cast(int)offset_of(Dirent, name)
|
||||||
// between the null terminator and the start of the next struct may be not initialized
|
|
||||||
// which means we also have to scan these garbage bytes.
|
trunc := min(str_size, 8)
|
||||||
str_size := int(dirent.reclen) - 1 - cast(int)offset_of(Dirent, name)
|
str_size -= trunc
|
||||||
// This skips *only* over the garbage, since if we're not garbage we're at nul terminator,
|
for _ in 0..<trunc {
|
||||||
// which skips this loop
|
str_size += 1
|
||||||
for str[str_size] != 0 {
|
if str[str_size] == 0 {
|
||||||
str_size -= 1
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for str[str_size-1] == 0 {
|
|
||||||
str_size -= 1
|
|
||||||
}
|
|
||||||
// Oh yeah btw i could also just `repne scasb` this thing, but honestly I started doing
|
|
||||||
// it the painful way, might as well finish doing it that way
|
|
||||||
return string(str[:str_size])
|
return string(str[:str_size])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user