Merge branch 'master' into os2-process-linux

This commit is contained in:
jasonKercher
2024-07-24 10:14:59 -04:00
committed by GitHub
16 changed files with 437 additions and 165 deletions
+62 -3
View File
@@ -1,21 +1,80 @@
package os2
import "base:runtime"
import "core:slice"
read_directory :: proc(f: ^File, n: int, allocator: runtime.Allocator) -> (fi: []File_Info, err: Error) {
return _read_directory(f, n, allocator)
@(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_GUARD()
it := read_directory_iterator_create(f) or_return
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
}
append(&dfi, file_info_clone(fi, allocator) or_return)
}
return slice.clone(dfi[:], allocator)
}
@(require_results)
read_all_directory :: proc(f: ^File, allocator: runtime.Allocator) -> (fi: []File_Info, err: Error) {
return read_directory(f, -1, allocator)
}
@(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)
}
@(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,
impl: Read_Directory_Iterator_Impl,
}
@(require_results)
read_directory_iterator_create :: proc(f: ^File) -> (Read_Directory_Iterator, Error) {
return _read_directory_iterator_create(f)
}
read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
_read_directory_iterator_destroy(it)
}
// NOTE(bill): `File_Info` does not need to deleted on each iteration. Any copies must be manually copied with `file_info_clone`
@(require_results)
read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
return _read_directory_iterator(it)
}
+15 -3
View File
@@ -1,8 +1,20 @@
//+private
package os2
import "base:runtime"
Read_Directory_Iterator_Impl :: struct {
@(private)
_read_directory :: proc(f: ^File, n: int, allocator: runtime.Allocator) -> (files: []File_Info, err: Error) {
}
@(require_results)
_read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
return
}
@(require_results)
_read_directory_iterator_create :: proc(f: ^File) -> (Read_Directory_Iterator, Error) {
return {}, nil
}
_read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
}
+87 -77
View File
@@ -1,67 +1,92 @@
//+private
package os2
import "base:runtime"
import "core:time"
import win32 "core:sys/windows"
@(private)
_read_directory :: proc(f: ^File, n: int, allocator: runtime.Allocator) -> (files: []File_Info, err: Error) {
find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
// Ignore "." and ".."
if d.cFileName[0] == '.' && d.cFileName[1] == 0 {
return
}
if d.cFileName[0] == '.' && d.cFileName[1] == '.' && d.cFileName[2] == 0 {
return
}
path := concatenate({base_path, `\`, win32_utf16_to_utf8(d.cFileName[:], temp_allocator()) or_else ""}, allocator) or_return
fi.fullpath = path
fi.name = basename(path)
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
@(private="file")
find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
// Ignore "." and ".."
if d.cFileName[0] == '.' && d.cFileName[1] == 0 {
return
}
if d.cFileName[0] == '.' && d.cFileName[1] == '.' && d.cFileName[2] == 0 {
return
}
path := concatenate({base_path, `\`, win32_utf16_to_utf8(d.cFileName[:], temp_allocator()) or_else ""}, allocator) or_return
if d.dwFileAttributes & win32.FILE_ATTRIBUTE_READONLY != 0 {
fi.mode |= 0o444
} else {
fi.mode |= 0o666
}
is_sym := false
if d.dwFileAttributes & win32.FILE_ATTRIBUTE_REPARSE_Point == 0 {
is_sym = false
} else {
is_sym = d.dwReserved0 == win32.IO_REPARSE_TAG_SYMLINK || d.dwReserved0 == win32.IO_REPARSE_TAG_MOUNT_POINT
}
fi.fullpath = path
fi.name = basename(path)
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
if is_sym {
fi.type = .Symlink
} else if d.dwFileAttributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
fi.type = .Directory
fi.mode |= 0o111
}
fi.type, fi.mode = _file_type_mode_from_file_attributes(d.dwFileAttributes, nil, d.dwReserved0)
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
// fi.inode = u128(u64(d.nFileIndexHigh)<<32 + u64(d.nFileIndexLow))
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
return
}
Read_Directory_Iterator_Impl :: struct {
find_data: win32.WIN32_FIND_DATAW,
find_handle: win32.HANDLE,
path: string,
prev_fi: File_Info,
no_more_files: bool,
index: int,
}
@(require_results)
_read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
if it.f == nil {
return
}
if f == nil {
return nil, .Invalid_File
for !it.impl.no_more_files {
err: Error
file_info_delete(it.impl.prev_fi, file_allocator())
it.impl.prev_fi = {}
fi, err = find_data_to_file_info(it.impl.path, &it.impl.find_data, file_allocator())
if err != nil {
return
}
if fi.name != "" {
it.impl.prev_fi = fi
ok = true
index = it.impl.index
it.impl.index += 1
}
if !win32.FindNextFileW(it.impl.find_handle, &it.impl.find_data) {
e := _get_platform_error()
if pe, _ := is_platform_error(e); pe == i32(win32.ERROR_NO_MORE_FILES) {
it.impl.no_more_files = true
}
it.impl.no_more_files = true
}
if ok {
return
}
}
return
}
TEMP_ALLOCATOR_GUARD()
@(require_results)
_read_directory_iterator_create :: proc(f: ^File) -> (it: Read_Directory_Iterator, err: Error) {
if f == nil {
return
}
it.f = f
impl := (^File_Impl)(f.impl)
if !is_directory(impl.name) {
return nil, .Invalid_Dir
}
n := n
size := n
if n <= 0 {
n = -1
size = 100
err = .Invalid_Dir
return
}
wpath: []u16
@@ -73,46 +98,31 @@ _read_directory :: proc(f: ^File, n: int, allocator: runtime.Allocator) -> (file
wpath = impl.wname[:i]
}
TEMP_ALLOCATOR_GUARD()
wpath_search := make([]u16, len(wpath)+3, context.temp_allocator)
wpath_search := make([]u16, len(wpath)+3, temp_allocator())
copy(wpath_search, wpath)
wpath_search[len(wpath)+0] = '\\'
wpath_search[len(wpath)+1] = '*'
wpath_search[len(wpath)+2] = 0
find_data := &win32.WIN32_FIND_DATAW{}
find_handle := win32.FindFirstFileW(raw_data(wpath_search), find_data)
if find_handle == win32.INVALID_HANDLE_VALUE {
return nil, _get_platform_error()
it.impl.find_handle = win32.FindFirstFileW(raw_data(wpath_search), &it.impl.find_data)
if it.impl.find_handle == win32.INVALID_HANDLE_VALUE {
err = _get_platform_error()
return
}
defer win32.FindClose(find_handle)
path := _cleanpath_from_buf(wpath, temp_allocator()) or_return
dfi := make([dynamic]File_Info, 0, size, allocator)
defer if err != nil {
for fi in dfi {
file_info_delete(fi, allocator)
}
delete(dfi)
}
for n != 0 {
fi: File_Info
fi = find_data_to_file_info(path, find_data, allocator) or_return
if fi.name != "" {
append(&dfi, fi)
n -= 1
}
if !win32.FindNextFileW(find_handle, find_data) {
e := _get_platform_error()
if pe, _ := is_platform_error(e); pe == i32(win32.ERROR_NO_MORE_FILES) {
break
}
return dfi[:], e
}
win32.FindClose(it.impl.find_handle)
}
return dfi[:], nil
it.impl.path = _cleanpath_from_buf(wpath, file_allocator()) or_return
return
}
_read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
if it.f == nil {
return
}
file_info_delete(it.impl.prev_fi, file_allocator())
win32.FindClose(it.impl.find_handle)
}
+1 -1
View File
@@ -106,7 +106,7 @@ open :: proc(name: string, flags := File_Flags{.Read}, perm := 0o777) -> (^File,
@(require_results)
new_file :: proc(handle: uintptr, name: string) -> ^File {
return _new_file(handle, name)
return _new_file(handle, name) or_else panic("Out of memory")
}
@(require_results)
+8 -5
View File
@@ -102,21 +102,24 @@ _open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Err
return nil, _get_platform_error(errno)
}
return _new_file(uintptr(fd), name), nil
return _new_file(uintptr(fd), name)
}
_new_file :: proc(fd: uintptr, _: string = "") -> ^File {
impl := new(File_Impl, file_allocator())
_new_file :: proc(fd: uintptr, _: string = "") -> (f: ^File, err: Error) {
impl := new(File_Impl, file_allocator()) or_return
defer if err != nil {
free(impl, file_allocator())
}
impl.file.impl = impl
impl.fd = linux.Fd(fd)
impl.allocator = file_allocator()
impl.name = _get_full_path(impl.fd, impl.allocator)
impl.name = _get_full_path(impl.fd, file_allocator()) or_return
impl.file.stream = {
data = impl,
procedure = _file_stream_proc,
}
impl.file.fstat = _fstat
return &impl.file
return &impl.file, nil
}
_destroy :: proc(f: ^File_Impl) -> Error {
+12 -8
View File
@@ -94,7 +94,7 @@ _open_internal :: proc(name: string, flags: File_Flags, perm: int) -> (handle: u
create_mode = win32.TRUNCATE_EXISTING
}
attrs: u32 = win32.FILE_ATTRIBUTE_NORMAL
attrs: u32 = win32.FILE_ATTRIBUTE_NORMAL|win32.FILE_FLAG_BACKUP_SEMANTICS
if perm & S_IWRITE == 0 {
attrs = win32.FILE_ATTRIBUTE_READONLY
if create_mode == win32.CREATE_ALWAYS {
@@ -126,20 +126,24 @@ _open_internal :: proc(name: string, flags: File_Flags, perm: int) -> (handle: u
_open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) {
flags := flags if flags != nil else {.Read}
handle := _open_internal(name, flags, perm) or_return
return _new_file(handle, name), nil
return _new_file(handle, name)
}
_new_file :: proc(handle: uintptr, name: string) -> ^File {
_new_file :: proc(handle: uintptr, name: string) -> (f: ^File, err: Error) {
if handle == INVALID_HANDLE {
return nil
return
}
impl := new(File_Impl, file_allocator())
impl := new(File_Impl, file_allocator()) or_return
defer if err != nil {
free(impl, file_allocator())
}
impl.file.impl = impl
impl.allocator = file_allocator()
impl.fd = rawptr(handle)
impl.name, _ = clone_string(name, impl.allocator)
impl.wname, _ = win32_utf8_to_wstring(name, impl.allocator)
impl.name = clone_string(name, impl.allocator) or_return
impl.wname = win32_utf8_to_wstring(name, impl.allocator) or_return
handle := _handle(&impl.file)
kind := File_Impl_Kind.File
@@ -157,7 +161,7 @@ _new_file :: proc(handle: uintptr, name: string) -> ^File {
}
impl.file.fstat = _fstat
return &impl.file
return &impl.file, nil
}
_fd :: proc(f: ^File) -> uintptr {
-4
View File
@@ -17,7 +17,3 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, runtime.Allocator_Error) {
return _heap_allocator_proc(allocator_data, mode, size, alignment, old_memory, old_size, loc)
}
@(private)
error_allocator := heap_allocator
+4 -5
View File
@@ -188,7 +188,7 @@ _set_working_directory :: proc(dir: string) -> Error {
return _get_platform_error(linux.chdir(dir_cstr))
}
_get_full_path :: proc(fd: linux.Fd, allocator: runtime.Allocator) -> string {
_get_full_path :: proc(fd: linux.Fd, allocator: runtime.Allocator) -> (fullpath: string, err: Error) {
PROC_FD_PATH :: "/proc/self/fd/"
buf: [32]u8
@@ -196,10 +196,9 @@ _get_full_path :: proc(fd: linux.Fd, allocator: runtime.Allocator) -> string {
strconv.itoa(buf[len(PROC_FD_PATH):], int(fd))
fullpath: string
err: Error
if fullpath, err = _read_link_cstr(cstring(&buf[0]), allocator); err != nil || fullpath[0] != '/' {
return ""
delete(fullpath, allocator)
fullpath = ""
}
return fullpath
return
}
+14 -1
View File
@@ -1,21 +1,34 @@
package os2
import "core:time"
import "base:runtime"
import "core:path/filepath"
import "core:strings"
import "core:time"
Fstat_Callback :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error)
File_Info :: struct {
fullpath: string,
name: string,
inode: u128, // might be zero if cannot be determined
size: i64,
mode: int,
type: File_Type,
creation_time: time.Time,
modification_time: time.Time,
access_time: time.Time,
}
@(require_results)
file_info_clone :: proc(fi: File_Info, allocator: runtime.Allocator) -> (cloned: File_Info, err: runtime.Allocator_Error) {
cloned = fi
cloned.fullpath = strings.clone(fi.fullpath) or_return
cloned.name = filepath.base(cloned.fullpath)
return
}
file_info_slice_delete :: proc(infos: []File_Info, allocator: runtime.Allocator) {
for i := len(infos)-1; i >= 0; i -= 1 {
file_info_delete(infos[i], allocator)
+12 -10
View File
@@ -11,7 +11,7 @@ _fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) {
return _fstat_internal(impl.fd, allocator)
}
_fstat_internal :: proc(fd: linux.Fd, allocator: runtime.Allocator) -> (File_Info, Error) {
_fstat_internal :: proc(fd: linux.Fd, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
s: linux.Stat
errno := linux.fstat(fd, &s)
if errno != .NONE {
@@ -28,20 +28,22 @@ _fstat_internal :: proc(fd: linux.Fd, allocator: runtime.Allocator) -> (File_Inf
case linux.S_IFSOCK: type = .Socket
}
mode := int(0o7777 & transmute(u32)s.mode)
// TODO: As of Linux 4.11, the new statx syscall can retrieve creation_time
fi := File_Info {
fullpath = _get_full_path(fd, allocator),
name = "",
size = i64(s.size),
mode = mode,
type = type,
fi = File_Info {
fullpath = _get_full_path(fd, allocator) or_return,
name = "",
inode = u128(u64(s.ino)),
size = i64(s.size),
mode = mode,
type = type,
modification_time = time.Time {i64(s.mtime.time_sec) * i64(time.Second) + i64(s.mtime.time_nsec)},
access_time = time.Time {i64(s.atime.time_sec) * i64(time.Second) + i64(s.atime.time_nsec)},
creation_time = time.Time{i64(s.ctime.time_sec) * i64(time.Second) + i64(s.ctime.time_nsec)}, // regular stat does not provide this
access_time = time.Time {i64(s.atime.time_sec) * i64(time.Second) + i64(s.atime.time_nsec)},
creation_time = time.Time{i64(s.ctime.time_sec) * i64(time.Second) + i64(s.ctime.time_nsec)}, // regular stat does not provide this
}
fi.creation_time = fi.modification_time
fi.name = filepath.base(fi.fullpath)
return fi, nil
return
}
// NOTE: _stat and _lstat are using _fstat to avoid a race condition when populating fullpath
+2 -1
View File
@@ -262,7 +262,8 @@ _file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HA
fi: File_Info
fi.fullpath = path
fi.name = basename(path)
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
fi.inode = u128(u64(d.nFileIndexHigh)<<32 + u64(d.nFileIndexLow))
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
type, mode := _file_type_mode_from_file_attributes(d.dwFileAttributes, nil, 0)
fi.type = type
fi.mode |= mode