aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaytan Laats <laytanlaats@hotmail.com>2024-08-01 21:31:30 +0200
committerLaytan Laats <laytanlaats@hotmail.com>2024-08-14 01:44:37 +0200
commita4d459f6517d23e6dc2129112e99a14ef158a45e (patch)
tree554fe29447aa9fad0874e71b406b371a47a7f4a9
parentff0ca0bd5388450b357549e33a81489cd2b75158 (diff)
os2: initial implementation for Darwin&BSDs, process API is only thing incomplete
-rw-r--r--core/os/os2/allocators.odin1
-rw-r--r--core/os/os2/dir_posix.odin71
-rw-r--r--core/os/os2/env_posix.odin78
-rw-r--r--core/os/os2/errors_posix.odin30
-rw-r--r--core/os/os2/file_posix.odin446
-rw-r--r--core/os/os2/heap_posix.odin97
-rw-r--r--core/os/os2/internal_util.odin2
-rw-r--r--core/os/os2/path_posix.odin127
-rw-r--r--core/os/os2/pipe_posix.odin37
-rw-r--r--core/os/os2/process.odin22
-rw-r--r--core/os/os2/process_posix.odin69
-rw-r--r--core/os/os2/process_posix_darwin.odin251
-rw-r--r--core/os/os2/process_posix_other.odin15
-rw-r--r--core/os/os2/stat.odin4
-rw-r--r--core/os/os2/stat_linux.odin2
-rw-r--r--core/os/os2/stat_posix.odin113
-rw-r--r--core/os/os2/stat_windows.odin8
-rw-r--r--core/os/os2/temp_file.odin2
-rw-r--r--core/os/os2/temp_file_posix.odin20
-rw-r--r--core/sys/darwin/proc.odin142
-rw-r--r--core/sys/posix/unistd.odin9
-rw-r--r--core/sys/unix/sysctl_darwin.odin4
-rw-r--r--core/sys/unix/sysctl_freebsd.odin2
23 files changed, 1535 insertions, 17 deletions
diff --git a/core/os/os2/allocators.odin b/core/os/os2/allocators.odin
index ef73809b1..0ab3adfb2 100644
--- a/core/os/os2/allocators.odin
+++ b/core/os/os2/allocators.odin
@@ -61,4 +61,3 @@ TEMP_ALLOCATOR_GUARD :: #force_inline proc(loc := #caller_location) -> (runtime.
global_default_temp_allocator_index = (global_default_temp_allocator_index+1)%MAX_TEMP_ARENA_COUNT
return tmp, loc
}
-
diff --git a/core/os/os2/dir_posix.odin b/core/os/os2/dir_posix.odin
new file mode 100644
index 000000000..69cf7930f
--- /dev/null
+++ b/core/os/os2/dir_posix.odin
@@ -0,0 +1,71 @@
+//+private
+//+build darwin, netbsd, freebsd, openbsd
+package os2
+
+import "base:runtime"
+
+import "core:sys/posix"
+
+@(private)
+_read_directory :: proc(f: ^File, n: int, allocator: runtime.Allocator) -> (files: []File_Info, err: Error) {
+ if f == nil || f.impl == nil {
+ err = .Invalid_File
+ return
+ }
+
+ n := n
+ if n == 0 {
+ return
+ }
+
+ impl := (^File_Impl)(f)
+
+ dir := posix.fdopendir(impl.fd)
+ if dir == nil {
+ err = _get_platform_error()
+ return
+ }
+ defer posix.closedir(dir)
+
+ dfiles: [dynamic]File_Info
+ dfiles.allocator = allocator
+ defer if err != nil {
+ file_info_slice_delete(dfiles[:], allocator)
+ }
+
+ for {
+ posix.set_errno(.NONE)
+ entry := posix.readdir(dir)
+ if entry == nil {
+ if errno := posix.errno(); errno != .NONE {
+ err = _get_platform_error()
+ return
+ } else {
+ break
+ }
+ }
+
+ cname := cstring(raw_data(entry.d_name[:]))
+ if cname == "." || cname == ".." {
+ continue
+ }
+
+ stat: posix.stat_t
+ if posix.fstatat(posix.dirfd(dir), cname, &stat, { .SYMLINK_NOFOLLOW }) != .OK {
+ err = _get_platform_error()
+ return
+ }
+
+ fullpath := concatenate({impl.name, "/", string(cname)}, allocator) or_return
+ fi := internal_stat(stat, fullpath)
+ append(&dfiles, fi) or_return
+
+ n -= 1
+ if n == 0 {
+ break
+ }
+ }
+
+ files = dfiles[:]
+ return
+}
diff --git a/core/os/os2/env_posix.odin b/core/os/os2/env_posix.odin
new file mode 100644
index 000000000..d7dc6d55a
--- /dev/null
+++ b/core/os/os2/env_posix.odin
@@ -0,0 +1,78 @@
+//+private
+//+build darwin, netbsd, freebsd, openbsd
+package os2
+
+import "base:runtime"
+
+import "core:strings"
+import "core:sys/posix"
+
+_lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
+ if key == "" {
+ return
+ }
+
+ assert(!is_temp(allocator))
+ TEMP_ALLOCATOR_GUARD()
+
+ ckey := strings.clone_to_cstring(key, temp_allocator())
+ cval := posix.getenv(ckey)
+ if cval == nil {
+ return
+ }
+
+ found = true
+ value = strings.clone(string(cval), allocator) // NOTE(laytan): what if allocation fails?
+
+ return
+}
+
+_set_env :: proc(key, value: string) -> (ok: bool) {
+ TEMP_ALLOCATOR_GUARD()
+
+ ckey := strings.clone_to_cstring(key, temp_allocator())
+ cval := strings.clone_to_cstring(key, temp_allocator())
+
+ ok = posix.setenv(ckey, cval, true) == .OK
+ return
+}
+
+_unset_env :: proc(key: string) -> (ok: bool) {
+ TEMP_ALLOCATOR_GUARD()
+
+ ckey := strings.clone_to_cstring(key, temp_allocator())
+
+ ok = posix.unsetenv(ckey) == .OK
+ return
+}
+
+// NOTE(laytan): clearing the env is weird, why would you ever do that?
+
+_clear_env :: proc() {
+ for i, entry := 0, posix.environ[0]; entry != nil; i, entry = i+1, posix.environ[i] {
+ key := strings.truncate_to_byte(string(entry), '=')
+ _unset_env(key)
+ }
+}
+
+_environ :: proc(allocator: runtime.Allocator) -> (environ: []string) {
+ n := 0
+ for entry := posix.environ[0]; entry != nil; n, entry = n+1, posix.environ[n] {}
+
+ err: runtime.Allocator_Error
+ if environ, err = make([]string, n, allocator); err != nil {
+ // NOTE(laytan): is the environment empty or did allocation fail, how does the user know?
+ return
+ }
+
+ for i, entry := 0, posix.environ[0]; entry != nil; i, entry = i+1, posix.environ[i] {
+ if environ[i], err = strings.clone(string(entry), allocator); err != nil {
+ // NOTE(laytan): is the entire environment returned or did allocation fail, how does the user know?
+ return
+ }
+ }
+
+ return
+}
+
+
diff --git a/core/os/os2/errors_posix.odin b/core/os/os2/errors_posix.odin
new file mode 100644
index 000000000..7143557a6
--- /dev/null
+++ b/core/os/os2/errors_posix.odin
@@ -0,0 +1,30 @@
+//+private
+//+build darwin, netbsd, freebsd, openbsd
+package os2
+
+import "core:sys/posix"
+
+_error_string :: proc(errno: i32) -> string {
+ return string(posix.strerror(posix.Errno(errno)))
+}
+
+_get_platform_error :: proc() -> Error {
+ #partial switch errno := posix.errno(); errno {
+ case .EPERM:
+ return .Permission_Denied
+ case .EEXIST:
+ return .Exist
+ case .ENOENT:
+ return .Not_Exist
+ case .ETIMEDOUT:
+ return .Timeout
+ case .EPIPE:
+ return .Broken_Pipe
+ case .EBADF:
+ return .Invalid_File
+ case .ENOMEM:
+ return .Out_Of_Memory
+ case:
+ return Platform_Error(errno)
+ }
+}
diff --git a/core/os/os2/file_posix.odin b/core/os/os2/file_posix.odin
new file mode 100644
index 000000000..b77824843
--- /dev/null
+++ b/core/os/os2/file_posix.odin
@@ -0,0 +1,446 @@
+//+private
+//+build darwin, netbsd, freebsd, openbsd
+package os2
+
+import "base:runtime"
+
+import "core:io"
+import "core:c"
+import "core:time"
+import "core:sys/posix"
+
+// Most implementations will EINVAL at some point when doing big writes.
+// In practice a read/write call would probably never read/write these big buffers all at once,
+// which is why the number of bytes is returned and why there are procs that will call this in a
+// loop for you.
+// We set a max of 1GB to keep alignment and to be safe.
+MAX_RW :: 1 << 30
+
+File_Impl :: struct {
+ file: File,
+ name: string,
+ cname: cstring,
+ fd: posix.FD,
+}
+
+@(init)
+init_std_files :: proc() {
+ // NOTE: is this (paths) also the case on non darwin?
+ stdin = _new_file(posix.STDIN_FILENO, "/dev/stdin")
+ stdout = _new_file(posix.STDOUT_FILENO, "/dev/stdout")
+ stderr = _new_file(posix.STDERR_FILENO, "/dev/stdout")
+}
+
+_open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) {
+ if name == "" {
+ err = .Invalid_Path
+ return
+ }
+
+ sys_flags := posix.O_Flags{.NOCTTY, .CLOEXEC}
+
+ if .Write in flags {
+ if .Read in flags {
+ sys_flags += {.RDWR}
+ } else {
+ sys_flags += {.WRONLY}
+ }
+ }
+
+ if .Append in flags { sys_flags += {.APPEND} }
+ if .Create in flags { sys_flags += {.CREAT} }
+ if .Excl in flags { sys_flags += {.EXCL} }
+ if .Sync in flags { sys_flags += {.DSYNC} }
+ if .Trunc in flags { sys_flags += {.TRUNC} }
+ if .Inheritable in flags { sys_flags -= {.CLOEXEC} }
+
+ TEMP_ALLOCATOR_GUARD()
+ cname := temp_cstring(name)
+
+ fd := posix.open(cname, sys_flags, transmute(posix.mode_t)posix._mode_t(perm))
+ if fd < 0 {
+ err = _get_platform_error()
+ return
+ }
+
+ return _new_file(uintptr(fd), name), nil
+}
+
+_new_file :: proc(handle: uintptr, name: string) -> ^File {
+ if name == "" || handle == ~uintptr(0) {
+ return nil
+ }
+
+ TEMP_ALLOCATOR_GUARD()
+ cname := temp_cstring(name)
+
+ crname := posix.realpath(cname, nil)
+ assert(crname != nil)
+ rname := string(crname)
+
+ f := __new_file(posix.FD(handle))
+ impl := (^File_Impl)(f.impl)
+ impl.name = rname
+ impl.cname = crname
+
+ return f
+}
+
+__new_file :: proc(handle: posix.FD) -> ^File {
+ impl := new(File_Impl, file_allocator())
+ impl.file.impl = impl
+ impl.fd = posix.FD(handle)
+ impl.file.stream = {
+ data = impl,
+ procedure = _file_stream_proc,
+ }
+ impl.file.fstat = _fstat
+ return &impl.file
+}
+
+_close :: proc(f: ^File_Impl) -> (err: Error) {
+ if f == nil { return nil }
+
+ if posix.close(f.fd) != .OK {
+ err = _get_platform_error()
+ }
+
+ delete(f.cname, file_allocator())
+ posix.free(f.cname)
+ return
+}
+
+_fd :: proc(f: ^File) -> uintptr {
+ return uintptr(__fd(f))
+}
+
+__fd :: proc(f: ^File) -> posix.FD {
+ if f != nil && f.impl != nil {
+ return (^File_Impl)(f.impl).fd
+ }
+ return -1
+}
+
+_name :: proc(f: ^File) -> string {
+ if f != nil && f.impl != nil {
+ return (^File_Impl)(f.impl).name
+ }
+ return ""
+}
+
+_sync :: proc(f: ^File) -> Error {
+ if posix.fsync(__fd(f)) != .OK {
+ return _get_platform_error()
+ }
+ return nil
+}
+
+_truncate :: proc(f: ^File, size: i64) -> Error {
+ if posix.ftruncate(__fd(f), posix.off_t(size)) != .OK {
+ return _get_platform_error()
+ }
+ return nil
+}
+
+_remove :: proc(name: string) -> Error {
+ TEMP_ALLOCATOR_GUARD()
+ cname := temp_cstring(name)
+ if posix.remove(cname) != 0 {
+ return _get_platform_error()
+ }
+ return nil
+}
+
+_rename :: proc(old_path, new_path: string) -> Error {
+ TEMP_ALLOCATOR_GUARD()
+ cold := temp_cstring(old_path)
+ cnew := temp_cstring(new_path)
+ if posix.rename(cold, cnew) != 0 {
+ return _get_platform_error()
+ }
+ return nil
+}
+
+_link :: proc(old_name, new_name: string) -> Error {
+ TEMP_ALLOCATOR_GUARD()
+ cold := temp_cstring(old_name)
+ cnew := temp_cstring(new_name)
+ if posix.link(cold, cnew) != .OK {
+ return _get_platform_error()
+ }
+ return nil
+}
+
+_symlink :: proc(old_name, new_name: string) -> Error {
+ TEMP_ALLOCATOR_GUARD()
+ cold := temp_cstring(old_name)
+ cnew := temp_cstring(new_name)
+ if posix.symlink(cold, cnew) != .OK {
+ return _get_platform_error()
+ }
+ return nil
+}
+
+_read_link :: proc(name: string, allocator: runtime.Allocator) -> (s: string, err: Error) {
+ assert(!is_temp(allocator))
+ TEMP_ALLOCATOR_GUARD()
+ cname := temp_cstring(name)
+
+ buf: [dynamic]byte
+ buf.allocator = allocator
+ defer if err != nil { delete(buf) }
+
+ // Loop this because the file might've grown between lstat() and readlink().
+ for {
+ stat: posix.stat_t
+ if posix.lstat(cname, &stat) != .OK {
+ err = _get_platform_error()
+ return
+ }
+
+ bufsiz := int(stat.st_size + 1 if stat.st_size > 0 else posix.PATH_MAX)
+
+ if bufsiz == len(buf) {
+ bufsiz *= 2
+ }
+
+ // Overflow.
+ if bufsiz <= 0 {
+ err = Platform_Error(posix.Errno.E2BIG)
+ return
+ }
+
+ resize(&buf, bufsiz) or_return
+
+ size := posix.readlink(cname, raw_data(buf), uint(bufsiz))
+ if size < 0 {
+ err = _get_platform_error()
+ return
+ }
+
+ // File has probably grown between lstat() and readlink().
+ if size == bufsiz {
+ continue
+ }
+
+ s = string(buf[:size])
+ return
+ }
+}
+
+_chdir :: proc(name: string) -> Error {
+ TEMP_ALLOCATOR_GUARD()
+ cname := temp_cstring(name)
+ if posix.chdir(cname) != .OK {
+ return _get_platform_error()
+ }
+ return nil
+}
+
+_fchdir :: proc(f: ^File) -> Error {
+ if posix.fchdir(__fd(f)) != .OK {
+ return _get_platform_error()
+ }
+ return nil
+}
+
+_fchmod :: proc(f: ^File, mode: int) -> Error {
+ if posix.fchmod(__fd(f), transmute(posix.mode_t)posix._mode_t(mode)) != .OK {
+ return _get_platform_error()
+ }
+ return nil
+}
+
+_chmod :: proc(name: string, mode: int) -> Error {
+ TEMP_ALLOCATOR_GUARD()
+ cname := temp_cstring(name)
+ if posix.chmod(cname, transmute(posix.mode_t)posix._mode_t(mode)) != .OK {
+ return _get_platform_error()
+ }
+ return nil
+}
+
+_fchown :: proc(f: ^File, uid, gid: int) -> Error {
+ if posix.fchown(__fd(f), posix.uid_t(uid), posix.gid_t(gid)) != .OK {
+ return _get_platform_error()
+ }
+ return nil
+}
+
+_chown :: proc(name: string, uid, gid: int) -> Error {
+ TEMP_ALLOCATOR_GUARD()
+ cname := temp_cstring(name)
+ if posix.chown(cname, posix.uid_t(uid), posix.gid_t(gid)) != .OK {
+ return _get_platform_error()
+ }
+ return nil
+}
+
+_lchown :: proc(name: string, uid, gid: int) -> Error {
+ TEMP_ALLOCATOR_GUARD()
+ cname := temp_cstring(name)
+ if posix.lchown(cname, posix.uid_t(uid), posix.gid_t(gid)) != .OK {
+ return _get_platform_error()
+ }
+ return nil
+}
+
+_chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
+ times := [2]posix.timeval{
+ {
+ tv_sec = posix.time_t(atime._nsec/1e9), /* seconds */
+ tv_usec = posix.suseconds_t(atime._nsec%1e9/1000), /* microseconds */
+ },
+ {
+ tv_sec = posix.time_t(mtime._nsec/1e9), /* seconds */
+ tv_usec = posix.suseconds_t(mtime._nsec%1e9/1000), /* microseconds */
+ },
+ }
+
+ TEMP_ALLOCATOR_GUARD()
+ cname := temp_cstring(name)
+
+ if posix.utimes(cname, &times) != .OK {
+ return _get_platform_error()
+ }
+ return nil
+}
+
+_fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
+ times := [2]posix.timespec{
+ {
+ tv_sec = posix.time_t(atime._nsec/1e9), /* seconds */
+ tv_nsec = c.long(atime._nsec%1e9), /* nanoseconds */
+ },
+ {
+ tv_sec = posix.time_t(mtime._nsec/1e9), /* seconds */
+ tv_nsec = c.long(mtime._nsec%1e9), /* nanoseconds */
+ },
+ }
+
+ if posix.futimens(__fd(f), &times) != .OK {
+ return _get_platform_error()
+ }
+ return nil
+}
+
+_exists :: proc(path: string) -> bool {
+ TEMP_ALLOCATOR_GUARD()
+ cpath := temp_cstring(path)
+ return posix.access(cpath) == .OK
+}
+
+_file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
+ f := (^File_Impl)(stream_data)
+ fd := f.fd
+
+ switch mode {
+ case .Read:
+ if len(p) <= 0 {
+ return
+ }
+
+ to_read := uint(min(len(p), MAX_RW))
+ n = i64(posix.read(fd, raw_data(p), to_read))
+ switch {
+ case n == 0:
+ err = .EOF
+ case n < 0:
+ err = .Unknown
+ }
+ return
+
+ case .Read_At:
+ if len(p) <= 0 {
+ return
+ }
+
+ if offset < 0 {
+ err = .Invalid_Offset
+ return
+ }
+
+ to_read := uint(min(len(p), MAX_RW))
+ n = i64(posix.pread(fd, raw_data(p), to_read, posix.off_t(offset)))
+ switch {
+ case n == 0:
+ err = .EOF
+ case n < 0:
+ err = .Unknown
+ }
+ return
+
+ case .Write:
+ p := p
+ for len(p) > 0 {
+ to_write := uint(min(len(p), MAX_RW))
+ if _n := i64(posix.write(fd, raw_data(p), to_write)); n <= 0 {
+ err = .Unknown
+ return
+ } else {
+ p = p[_n:]
+ n += _n
+ }
+ }
+ return
+
+ case .Write_At:
+ p := p
+ offset := offset
+
+ if offset < 0 {
+ err = .Invalid_Offset
+ return
+ }
+
+ for len(p) > 0 {
+ to_write := uint(min(len(p), MAX_RW))
+ if _n := i64(posix.pwrite(fd, raw_data(p), to_write, posix.off_t(offset))); n <= 0 {
+ err = .Unknown
+ return
+ } else {
+ p = p[_n:]
+ n += _n
+ offset += _n
+ }
+ }
+ return
+
+ case .Seek:
+ #assert(int(posix.Whence.SET) == int(io.Seek_From.Start))
+ #assert(int(posix.Whence.CUR) == int(io.Seek_From.Current))
+ #assert(int(posix.Whence.END) == int(io.Seek_From.End))
+
+ n = i64(posix.lseek(fd, posix.off_t(offset), posix.Whence(whence)))
+ if n < 0 {
+ err = .Unknown
+ }
+ return
+
+ case .Size:
+ stat: posix.stat_t
+ if posix.fstat(fd, &stat) != .OK {
+ err = .Unknown
+ return
+ }
+
+ n = i64(stat.st_size)
+ return
+
+ case .Flush:
+ ferr := _sync(&f.file)
+ err = error_to_io_error(ferr)
+ return
+
+ case .Close, .Destroy:
+ ferr := _close(f)
+ err = error_to_io_error(ferr)
+ return
+
+ case .Query:
+ return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Flush, .Close, .Query})
+
+ case:
+ return 0, .Empty
+ }
+}
diff --git a/core/os/os2/heap_posix.odin b/core/os/os2/heap_posix.odin
new file mode 100644
index 000000000..eb543d383
--- /dev/null
+++ b/core/os/os2/heap_posix.odin
@@ -0,0 +1,97 @@
+//+private
+//+build darwin, netbsd, freebsd, openbsd
+package os2
+
+import "base:intrinsics"
+
+import "core:mem"
+import "core:sys/posix"
+
+
+_heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
+ size, alignment: int,
+ old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, mem.Allocator_Error) {
+ //
+ // NOTE(tetra, 2020-01-14): The heap doesn't respect alignment.
+ // Instead, we overallocate by `alignment + size_of(rawptr) - 1`, and insert
+ // padding. We also store the original pointer returned by heap_alloc right before
+ // the pointer we return to the user.
+ //
+
+ aligned_alloc :: proc(size, alignment: int, zero_memory: bool, old_ptr: rawptr = nil) -> ([]byte, mem.Allocator_Error) {
+ assert(size > 0)
+
+ a := max(alignment, align_of(rawptr))
+ space := size + a - 1
+
+ allocated_mem: rawptr
+ if old_ptr != nil {
+ original_old_ptr := mem.ptr_offset((^rawptr)(old_ptr), -1)^
+ allocated_mem = posix.realloc(original_old_ptr, uint(space)+size_of(rawptr))
+ } else if zero_memory {
+ allocated_mem = posix.calloc(1, uint(space)+size_of(rawptr))
+ } else {
+ allocated_mem = posix.malloc(uint(space)+size_of(rawptr))
+ }
+ aligned_mem := rawptr(mem.ptr_offset((^u8)(allocated_mem), size_of(rawptr)))
+
+ ptr := uintptr(aligned_mem)
+ aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a)
+ diff := int(aligned_ptr - ptr)
+ if (size + diff) > space || allocated_mem == nil {
+ return nil, .Out_Of_Memory
+ }
+
+ aligned_mem = rawptr(aligned_ptr)
+ mem.ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem
+
+ return mem.byte_slice(aligned_mem, size), nil
+ }
+
+ aligned_free :: proc(p: rawptr) {
+ if p != nil {
+ posix.free(mem.ptr_offset((^rawptr)(p), -1)^)
+ }
+ }
+
+ aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int, zero_memory: bool) -> (new_memory: []byte, err: mem.Allocator_Error) {
+ assert(p != nil)
+
+ new_memory = aligned_alloc(new_size, new_alignment, false, p) or_return
+
+ if zero_memory && new_size > old_size {
+ new_region := raw_data(new_memory[old_size:])
+ intrinsics.mem_zero(new_region, new_size - old_size)
+ }
+ return
+ }
+
+ switch mode {
+ case .Alloc, .Alloc_Non_Zeroed:
+ return aligned_alloc(size, alignment, mode == .Alloc)
+
+ case .Free:
+ aligned_free(old_memory)
+
+ case .Free_All:
+ return nil, .Mode_Not_Implemented
+
+ case .Resize, .Resize_Non_Zeroed:
+ if old_memory == nil {
+ return aligned_alloc(size, alignment, mode == .Resize)
+ }
+ return aligned_resize(old_memory, old_size, size, alignment, mode == .Resize)
+
+ case .Query_Features:
+ set := (^mem.Allocator_Mode_Set)(old_memory)
+ if set != nil {
+ set^ = {.Alloc, .Free, .Resize, .Query_Features}
+ }
+ return nil, nil
+
+ case .Query_Info:
+ return nil, .Mode_Not_Implemented
+ }
+
+ return nil, nil
+}
diff --git a/core/os/os2/internal_util.odin b/core/os/os2/internal_util.odin
index f7a38f3f1..ed65ab7e0 100644
--- a/core/os/os2/internal_util.odin
+++ b/core/os/os2/internal_util.odin
@@ -43,7 +43,7 @@ clone_to_cstring :: proc(s: string, allocator: runtime.Allocator) -> (res: cstri
}
@(require_results)
-temp_cstring :: proc(s: string) -> (cstring, runtime.Allocator_Error) {
+temp_cstring :: proc(s: string) -> (cstring, runtime.Allocator_Error) #optional_allocator_error {
return clone_to_cstring(s, temp_allocator())
}
diff --git a/core/os/os2/path_posix.odin b/core/os/os2/path_posix.odin
new file mode 100644
index 000000000..1e59afdc1
--- /dev/null
+++ b/core/os/os2/path_posix.odin
@@ -0,0 +1,127 @@
+//+private
+//+build darwin, netbsd, freebsd, openbsd
+package os2
+
+import "base:runtime"
+import "core:path/filepath"
+
+import "core:sys/posix"
+
+_Path_Separator :: '/'
+_Path_Separator_String :: "/"
+_Path_List_Separator :: ':'
+
+_is_path_separator :: proc(c: byte) -> bool {
+ return c == _Path_Separator
+}
+
+_mkdir :: proc(name: string, perm: int) -> Error {
+ TEMP_ALLOCATOR_GUARD()
+ cname := temp_cstring(name)
+ if posix.mkdir(cname, transmute(posix.mode_t)posix._mode_t(perm)) != .OK {
+ return _get_platform_error()
+ }
+ return nil
+}
+
+_mkdir_all :: proc(path: string, perm: int) -> Error {
+ if path == "" {
+ return .Invalid_Path
+ }
+
+ TEMP_ALLOCATOR_GUARD()
+
+ if exists(path) {
+ return .Exist
+ }
+
+ clean_path := filepath.clean(path, temp_allocator())
+ return internal_mkdir_all(clean_path, perm)
+
+ internal_mkdir_all :: proc(path: string, perm: int) -> Error {
+ a, _ := filepath.split(path)
+ if a != path {
+ if len(a) > 1 && a[len(a)-1] == '/' {
+ a = a[:len(a)-1]
+ }
+ internal_mkdir_all(a, perm) or_return
+ }
+
+ err := _mkdir(path, perm)
+ if err == .Exist { err = nil }
+ return err
+ }
+}
+
+_remove_all :: proc(path: string) -> Error {
+ TEMP_ALLOCATOR_GUARD()
+ cpath := temp_cstring(path)
+
+ dir := posix.opendir(cpath)
+ if dir == nil {
+ return _get_platform_error()
+ }
+
+ for {
+ posix.set_errno(.NONE)
+ entry := posix.readdir(dir)
+ if entry == nil {
+ if errno := posix.errno(); errno != .NONE {
+ return _get_platform_error()
+ } else {
+ break
+ }
+ }
+
+ cname := cstring(raw_data(entry.d_name[:]))
+ if cname == "." || cname == ".." {
+ continue
+ }
+
+ fullpath, _ := concatenate({path, "/", string(cname), "\x00"}, temp_allocator())
+ if entry.d_type == .DIR {
+ _remove_all(fullpath[:len(fullpath)-1])
+ } else {
+ if posix.unlink(cstring(raw_data(fullpath))) != .OK {
+ return _get_platform_error()
+ }
+ }
+ }
+
+ if posix.rmdir(cpath) != .OK {
+ return _get_platform_error()
+ }
+ posix.closedir(dir)
+ return nil
+}
+
+_get_working_directory :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
+ assert(!is_temp(allocator))
+ TEMP_ALLOCATOR_GUARD()
+
+ buf: [dynamic]byte
+ buf.allocator = temp_allocator()
+ size := uint(posix.PATH_MAX)
+
+ cwd: cstring
+ for ; cwd == nil; size *= 2 {
+ resize(&buf, size)
+
+ cwd = posix.getcwd(raw_data(buf), len(buf))
+ if cwd == nil && posix.errno() != .ERANGE {
+ err = _get_platform_error()
+ return
+ }
+ }
+
+ return clone_string(string(cwd), allocator)
+}
+
+_set_working_directory :: proc(dir: string) -> (err: Error) {
+ TEMP_ALLOCATOR_GUARD()
+ cdir := temp_cstring(dir)
+ if posix.chdir(cdir) != .OK {
+ err = _get_platform_error()
+ }
+ return
+}
diff --git a/core/os/os2/pipe_posix.odin b/core/os/os2/pipe_posix.odin
new file mode 100644
index 000000000..62d6fc9fb
--- /dev/null
+++ b/core/os/os2/pipe_posix.odin
@@ -0,0 +1,37 @@
+//+private
+//+build darwin, netbsd, freebsd, openbsd
+package os2
+
+import "core:sys/posix"
+import "core:strings"
+
+_pipe :: proc() -> (r, w: ^File, err: Error) {
+ fds: [2]posix.FD
+ if posix.pipe(&fds) != .OK {
+ err = _get_platform_error()
+ return
+ }
+
+ r = __new_file(fds[0])
+ ri := (^File_Impl)(r.impl)
+
+ rname := strings.builder_make(file_allocator())
+ // TODO(laytan): is this on all the posix targets?
+ strings.write_string(&rname, "/dev/fd/")
+ strings.write_int(&rname, int(fds[0]))
+ ri.name = strings.to_string(rname)
+ ri.cname = strings.to_cstring(&rname)
+
+ w = __new_file(fds[1])
+ wi := (^File_Impl)(w.impl)
+
+ wname := strings.builder_make(file_allocator())
+ // TODO(laytan): is this on all the posix targets?
+ strings.write_string(&wname, "/dev/fd/")
+ strings.write_int(&wname, int(fds[1]))
+ wi.name = strings.to_string(wname)
+ wi.cname = strings.to_cstring(&wname)
+
+ return
+}
+
diff --git a/core/os/os2/process.odin b/core/os/os2/process.odin
index 3f3e64668..f7a542276 100644
--- a/core/os/os2/process.odin
+++ b/core/os/os2/process.odin
@@ -14,7 +14,7 @@ TIMEOUT_INFINITE :: time.MIN_DURATION // Note(flysand): Any negative duration wi
*/
args := get_args()
-@(private="file", require_results)
+@(private="file")
get_args :: proc() -> []string {
result := make([]string, len(runtime.args__), heap_allocator())
for rt_arg, i in runtime.args__ {
@@ -131,6 +131,8 @@ Process_Info_Field :: enum {
Working_Dir,
}
+ALL_INFO :: Process_Info_Fields{.Executable_Path, .PPid, .Priority, .Command_Line, .Command_Args, .Environment, .Username, .Working_Dir}
+
/*
Contains information about the process as obtained by the `process_info()`
procedure.
@@ -166,8 +168,8 @@ Process_Info :: struct {
a process given by `pid`.
Use `free_process_info` to free the memory allocated by this procedure. In
- case the function returns an error all temporary allocations would be freed
- and as such, calling `free_process_info()` is not needed.
+ case the function returns an error it may only have been an error for one part
+ of the information and you would still need to call it to free the other parts.
**Note**: The resulting information may or may contain the fields specified
by the `selection` parameter. Always check whether the returned
@@ -187,8 +189,8 @@ process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator:
the `process` parameter.
Use `free_process_info` to free the memory allocated by this procedure. In
- case the function returns an error, all temporary allocations would be freed
- and as such, calling `free_process_info` is not needed.
+ case the function returns an error it may only have been an error for one part
+ of the information and you would still need to call it to free the other parts.
**Note**: The resulting information may or may contain the fields specified
by the `selection` parameter. Always check whether the returned
@@ -206,9 +208,9 @@ process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields,
This procedure obtains the information, specified by `selection` parameter
about the currently running process.
- Use `free_process_info` to free the memory allocated by this function. In
- case this function returns an error, all temporary allocations would be
- freed and as such calling `free_process_info()` is not needed.
+ Use `free_process_info` to free the memory allocated by this procedure. In
+ case the function returns an error it may only have been an error for one part
+ of the information and you would still need to call it to free the other parts.
**Note**: The resulting information may or may contain the fields specified
by the `selection` parameter. Always check whether the returned
@@ -239,12 +241,16 @@ process_info :: proc {
free_process_info :: proc(pi: Process_Info, allocator: runtime.Allocator) {
delete(pi.executable_path, allocator)
delete(pi.command_line, allocator)
+ for a in pi.command_args {
+ delete(a, allocator)
+ }
delete(pi.command_args, allocator)
for s in pi.environment {
delete(s, allocator)
}
delete(pi.environment, allocator)
delete(pi.working_dir, allocator)
+ delete(pi.username, allocator)
}
/*
diff --git a/core/os/os2/process_posix.odin b/core/os/os2/process_posix.odin
new file mode 100644
index 000000000..8a27efce4
--- /dev/null
+++ b/core/os/os2/process_posix.odin
@@ -0,0 +1,69 @@
+//+private
+//+build darwin, netbsd, freebsd, openbsd
+package os2
+
+import "base:runtime"
+import "core:time"
+
+import "core:sys/posix"
+
+_exit :: proc "contextless" (code: int) -> ! {
+ posix.exit(i32(code))
+}
+
+_get_uid :: proc() -> int {
+ return int(posix.getuid())
+}
+
+_get_euid :: proc() -> int {
+ return int(posix.geteuid())
+}
+
+_get_gid :: proc() -> int {
+ return int(posix.getgid())
+}
+
+_get_egid :: proc() -> int {
+ return int(posix.getegid())
+}
+
+_get_pid :: proc() -> int {
+ return int(posix.getpid())
+}
+
+_get_ppid :: proc() -> int {
+ return int(posix.getppid())
+}
+
+_process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
+ return _process_info_by_pid(process.pid, selection, allocator)
+}
+
+_current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
+ return _process_info_by_pid(_get_pid(), selection, allocator)
+}
+
+_process_open :: proc(pid: int, flags: Process_Open_Flags) -> (process: Process, err: Error) {
+ err = .Unsupported
+ return
+}
+
+_Sys_Process_Attributes :: struct {}
+
+_process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
+ err = .Unsupported
+ return
+}
+
+_process_wait :: proc(process: Process, timeout: time.Duration) -> (process_state: Process_State, err: Error) {
+ err = .Unsupported
+ return
+}
+
+_process_close :: proc(process: Process) -> Error {
+ return .Unsupported
+}
+
+_process_kill :: proc(process: Process) -> Error {
+ return .Unsupported
+}
diff --git a/core/os/os2/process_posix_darwin.odin b/core/os/os2/process_posix_darwin.odin
new file mode 100644
index 000000000..f5816d693
--- /dev/null
+++ b/core/os/os2/process_posix_darwin.odin
@@ -0,0 +1,251 @@
+//+private
+package os2
+
+import "base:runtime"
+import "base:intrinsics"
+
+import "core:bytes"
+import "core:sys/darwin"
+import "core:sys/posix"
+import "core:sys/unix"
+
+foreign import lib "system:System.framework"
+
+foreign lib {
+ sysctl :: proc(
+ name: [^]i32, namelen: u32,
+ oldp: rawptr, oldlenp: ^uint,
+ newp: rawptr, newlen: uint,
+ ) -> posix.result ---
+}
+
+_process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
+ info.pid = pid
+
+ get_pidinfo :: proc(pid: int, selection: Process_Info_Fields) -> (ppid: u32, nice: Maybe(i32), uid: posix.uid_t, ok: bool) {
+ // Short info is enough and requires less permissions if the priority isn't requested.
+ if .Priority in selection {
+ pinfo: darwin.proc_bsdinfo
+ ret := darwin.proc_pidinfo(posix.pid_t(pid), .BSDINFO, 0, &pinfo, size_of(pinfo))
+ if ret > 0 {
+ assert(ret == size_of(pinfo))
+ ppid = pinfo.pbi_ppid
+ nice = pinfo.pbi_nice
+ uid = pinfo.pbi_uid
+ ok = true
+ return
+ }
+ }
+
+ // Try short info, requires less permissions, but doesn't give a `nice`.
+ psinfo: darwin.proc_bsdshortinfo
+ ret := darwin.proc_pidinfo(posix.pid_t(pid), .SHORTBSDINFO, 0, &psinfo, size_of(psinfo))
+ if ret > 0 {
+ assert(ret == size_of(psinfo))
+ ppid = psinfo.pbsi_ppid
+ uid = psinfo.pbsi_uid
+ ok = true
+ }
+
+ return
+ }
+
+ // Thought on errors is: allocation failures return immediately (also why the non-allocation stuff is done first),
+ // other errors usually mean other parts of the info could be retrieved though, so in those cases we keep trying to get the other information.
+
+ pidinfo: {
+ if selection >= {.PPid, .Priority, .Username } {
+ ppid, mnice, uid, ok := get_pidinfo(pid, selection)
+ if !ok {
+ if err == nil {
+ err = _get_platform_error()
+ }
+ break pidinfo
+ }
+
+ if .PPid in selection {
+ info.ppid = int(ppid)
+ info.fields += {.PPid}
+ }
+
+ if nice, has_nice := mnice.?; has_nice && .Priority in selection {
+ info.priority = int(nice)
+ info.fields += {.Priority}
+ }
+
+ if .Username in selection {
+ pw := posix.getpwuid(uid)
+ if pw == nil {
+ if err == nil {
+ err = _get_platform_error()
+ }
+ break pidinfo
+ }
+
+ info.username = clone_string(string(pw.pw_name), allocator) or_return
+ info.fields += {.Username}
+ }
+ }
+ }
+
+ if .Working_Dir in selection {
+ pinfo: darwin.proc_vnodepathinfo
+ ret := darwin.proc_pidinfo(posix.pid_t(pid), .VNODEPATHINFO, 0, &pinfo, size_of(pinfo))
+ if ret > 0 {
+ assert(ret == size_of(pinfo))
+ info.working_dir = clone_string(string(cstring(raw_data(pinfo.pvi_cdir.vip_path[:]))), allocator) or_return
+ info.fields += {.Working_Dir}
+ } else if err == nil {
+ err = _get_platform_error()
+ }
+ }
+
+ if .Executable_Path in selection {
+ buffer: [darwin.PIDPATHINFO_MAXSIZE]byte = ---
+ ret := darwin.proc_pidpath(posix.pid_t(pid), raw_data(buffer[:]), len(buffer))
+ if ret > 0 {
+ info.executable_path = clone_string(string(buffer[:ret]), allocator) or_return
+ info.fields += {.Executable_Path}
+ } else if err == nil {
+ err = _get_platform_error()
+ }
+ }
+
+ args: if selection >= { .Command_Line, .Command_Args, .Environment } {
+ mib := []i32{
+ unix.CTL_KERN,
+ unix.KERN_PROCARGS2,
+ i32(pid),
+ }
+ length: uint
+ if sysctl(raw_data(mib), 3, nil, &length, nil, 0) != .OK {
+ if err == nil {
+ err = _get_platform_error()
+ }
+ break args
+ }
+
+ buf := make([]byte, length, temp_allocator())
+ if sysctl(raw_data(mib), 3, raw_data(buf), &length, nil, 0) != .OK {
+ if err == nil {
+ err = _get_platform_error()
+ }
+ break args
+ }
+
+ buf = buf[:length]
+
+ if len(buf) < 4 {
+ break args
+ }
+
+ // Layout isn't really documented anywhere, I deduced it to be:
+ // i32 - argc
+ // cstring - command name (skipped)
+ // [^]byte - couple of 0 bytes (skipped)
+ // [^]cstring - argv (up to argc entries)
+ // [^]cstring - key=value env entries until the end (many intermittent 0 bytes and entries without `=` we skip here too)
+
+ argc := (^i32)(raw_data(buf))^
+ buf = buf[size_of(i32):]
+
+ {
+ command_line: [dynamic]byte
+ command_line.allocator = allocator
+
+ argv: [dynamic]string
+ argv.allocator = allocator
+
+ defer if err != nil {
+ for arg in argv { delete(arg, allocator) }
+ delete(argv)
+ delete(command_line)
+ }
+
+ _, _ = bytes.split_iterator(&buf, {0})
+ buf = bytes.trim_left(buf, {0})
+
+ first_arg := true
+ for arg in bytes.split_iterator(&buf, {0}) {
+ if .Command_Line in selection {
+ if !first_arg {
+ append(&command_line, ' ') or_return
+ }
+ append(&command_line, ..arg) or_return
+ }
+
+ if .Command_Args in selection {
+ sarg := clone_string(string(arg), allocator) or_return
+ append(&argv, sarg) or_return
+ }
+
+ first_arg = false
+ argc -= 1
+ if argc == 0 {
+ break
+ }
+ }
+
+ if .Command_Line in selection {
+ info.command_line = string(command_line[:])
+ info.fields += {.Command_Line}
+ }
+ if .Command_Args in selection {
+ info.command_args = argv[:]
+ info.fields += {.Command_Args}
+ }
+ }
+
+ if .Environment in selection {
+ environment: [dynamic]string
+ environment.allocator = allocator
+
+ defer if err != nil {
+ for entry in environment { delete(entry, allocator) }
+ delete(environment)
+ }
+
+ for entry in bytes.split_iterator(&buf, {0}) {
+ if bytes.index_byte(entry, '=') > -1 {
+ sentry := clone_string(string(entry), allocator) or_return
+ append(&environment, sentry) or_return
+ }
+ }
+
+ info.environment = environment[:]
+ info.fields += {.Environment}
+ }
+ }
+
+ // Fields were requested that we didn't add.
+ if err == nil && selection - info.fields != {} {
+ err = .Unsupported
+ }
+
+ return
+}
+
+_process_list :: proc(allocator: runtime.Allocator) -> (list: []int, err: Error) {
+ ret := darwin.proc_listallpids(nil, 0)
+ if ret < 0 {
+ err = _get_platform_error()
+ return
+ }
+
+ assert(!is_temp(allocator))
+ TEMP_ALLOCATOR_GUARD()
+
+ buffer := make([]i32, ret, temp_allocator())
+ ret = darwin.proc_listallpids(raw_data(buffer), ret*size_of(i32))
+ if ret < 0 {
+ err = _get_platform_error()
+ return
+ }
+
+ list = make([]int, ret, allocator) or_return
+ #no_bounds_check for &entry, i in list {
+ entry = int(buffer[i])
+ }
+
+ return
+}
diff --git a/core/os/os2/process_posix_other.odin b/core/os/os2/process_posix_other.odin
new file mode 100644
index 000000000..ce84b1012
--- /dev/null
+++ b/core/os/os2/process_posix_other.odin
@@ -0,0 +1,15 @@
+//+private
+//+build netbsd, openbsd, freebsd
+package os2
+
+import "base:runtime"
+
+_process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
+ err = .Unsupported
+ return
+}
+
+_process_list :: proc(allocator: runtime.Allocator) -> (list: []int, err: Error) {
+ err = .Unsupported
+ return
+}
diff --git a/core/os/os2/stat.odin b/core/os/os2/stat.odin
index b3ca47be3..b53ebb3ab 100644
--- a/core/os/os2/stat.odin
+++ b/core/os/os2/stat.odin
@@ -30,8 +30,8 @@ file_info_clone :: proc(fi: File_Info, allocator: runtime.Allocator) -> (cloned:
}
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)
+ #reverse for info in infos {
+ file_info_delete(info, allocator)
}
delete(infos, allocator)
}
diff --git a/core/os/os2/stat_linux.odin b/core/os/os2/stat_linux.odin
index 6ccac1be0..2355a09f0 100644
--- a/core/os/os2/stat_linux.odin
+++ b/core/os/os2/stat_linux.odin
@@ -48,6 +48,7 @@ _fstat_internal :: proc(fd: linux.Fd, allocator: runtime.Allocator) -> (fi: File
// NOTE: _stat and _lstat are using _fstat to avoid a race condition when populating fullpath
_stat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
+ assert(!is_temp(allocator))
TEMP_ALLOCATOR_GUARD()
name_cstr := temp_cstring(name) or_return
@@ -60,6 +61,7 @@ _stat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, err
}
_lstat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
+ assert(!is_temp(allocator))
TEMP_ALLOCATOR_GUARD()
name_cstr := temp_cstring(name) or_return
diff --git a/core/os/os2/stat_posix.odin b/core/os/os2/stat_posix.odin
new file mode 100644
index 000000000..3ffdefc6a
--- /dev/null
+++ b/core/os/os2/stat_posix.odin
@@ -0,0 +1,113 @@
+//+private
+//+build darwin, netbsd, freebsd, openbsd
+package os2
+
+import "base:runtime"
+import "core:path/filepath"
+import "core:sys/posix"
+import "core:time"
+
+internal_stat :: proc(stat: posix.stat_t, fullpath: string) -> (fi: File_Info) {
+ fi.fullpath = fullpath
+ fi.name = filepath.base(fi.fullpath)
+
+ fi.inode = u64(stat.st_ino)
+ fi.size = i64(stat.st_size)
+
+ fi.mode = int(transmute(posix._mode_t)(stat.st_mode - posix._S_IFMT))
+
+ fi.type = .Undetermined
+ switch {
+ case posix.S_ISBLK(stat.st_mode):
+ fi.type = .Block_Device
+ case posix.S_ISCHR(stat.st_mode):
+ fi.type = .Character_Device
+ case posix.S_ISDIR(stat.st_mode):
+ fi.type = .Directory
+ case posix.S_ISFIFO(stat.st_mode):
+ fi.type = .Named_Pipe
+ case posix.S_ISLNK(stat.st_mode):
+ fi.type = .Symlink
+ case posix.S_ISREG(stat.st_mode):
+ fi.type = .Regular
+ case posix.S_ISSOCK(stat.st_mode):
+ fi.type = .Socket
+ }
+
+ fi.creation_time = timespec_time(stat.st_birthtimespec)
+ fi.modification_time = timespec_time(stat.st_mtim)
+ fi.access_time = timespec_time(stat.st_atim)
+
+ timespec_time :: proc(t: posix.timespec) -> time.Time {
+ return time.Time{_nsec = i64(t.tv_sec) * 1e9 + i64(t.tv_nsec)}
+ }
+
+ return
+}
+
+_fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
+ if f == nil || f.impl == nil {
+ err = .Invalid_File
+ return
+ }
+
+ impl := (^File_Impl)(f.impl)
+
+ stat: posix.stat_t
+ if posix.fstat(impl.fd, &stat) != .OK {
+ err = _get_platform_error()
+ return
+ }
+
+ fullpath := clone_string(impl.name, allocator) or_return
+ return internal_stat(stat, fullpath), nil
+}
+
+// NOTE: _stat and _lstat are using _fstat to avoid a race condition when populating fullpath
+_stat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
+ if name == "" {
+ err = .Invalid_Path
+ return
+ }
+
+ assert(!is_temp(allocator))
+ TEMP_ALLOCATOR_GUARD()
+ cname := temp_cstring(name) or_return
+
+ rcname := posix.realpath(cname)
+
+ stat: posix.stat_t
+ if posix.stat(rcname, &stat) != .OK {
+ err = _get_platform_error()
+ return
+ }
+
+ fullpath := clone_string(string(rcname), allocator) or_return
+ return internal_stat(stat, fullpath), nil
+}
+
+_lstat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
+ if name == "" {
+ err = .Invalid_Path
+ return
+ }
+
+ assert(!is_temp(allocator))
+ TEMP_ALLOCATOR_GUARD()
+ cname := temp_cstring(name) or_return
+
+ rcname := posix.realpath(cname)
+
+ stat: posix.stat_t
+ if posix.lstat(rcname, &stat) != .OK {
+ err = _get_platform_error()
+ return
+ }
+
+ fullpath := clone_string(string(rcname), allocator) or_return
+ return internal_stat(stat, fullpath), nil
+}
+
+_same_file :: proc(fi1, fi2: File_Info) -> bool {
+ return fi1.fullpath == fi2.fullpath
+}
diff --git a/core/os/os2/stat_windows.odin b/core/os/os2/stat_windows.odin
index 5e66507be..d0b7c45a7 100644
--- a/core/os/os2/stat_windows.odin
+++ b/core/os/os2/stat_windows.odin
@@ -44,6 +44,8 @@ full_path_from_name :: proc(name: string, allocator: runtime.Allocator) -> (path
if name == "" {
name = "."
}
+
+ assert(!is_temp(allocator))
TEMP_ALLOCATOR_GUARD()
p := win32_utf8_to_utf16(name, temp_allocator()) or_return
@@ -127,7 +129,10 @@ _cleanpath_from_handle :: proc(f: ^File, allocator: runtime.Allocator) -> (strin
if n == 0 {
return "", _get_platform_error()
}
+
+ assert(!is_temp(allocator))
TEMP_ALLOCATOR_GUARD()
+
buf := make([]u16, max(n, 260)+1, temp_allocator())
n = win32.GetFinalPathNameByHandleW(h, raw_data(buf), u32(len(buf)), 0)
return _cleanpath_from_buf(buf[:n], allocator)
@@ -143,7 +148,10 @@ _cleanpath_from_handle_u16 :: proc(f: ^File) -> ([]u16, Error) {
if n == 0 {
return nil, _get_platform_error()
}
+
+ assert(!is_temp(allocator))
TEMP_ALLOCATOR_GUARD()
+
buf := make([]u16, max(n, 260)+1, temp_allocator())
n = win32.GetFinalPathNameByHandleW(h, raw_data(buf), u32(len(buf)), 0)
return _cleanpath_strip_prefix(buf[:n]), nil
diff --git a/core/os/os2/temp_file.odin b/core/os/os2/temp_file.odin
index 467775e89..5ca4e1453 100644
--- a/core/os/os2/temp_file.odin
+++ b/core/os/os2/temp_file.odin
@@ -10,7 +10,7 @@ MAX_ATTEMPTS :: 1<<13 // Should be enough for everyone, right?
// Opens the file for reading and writing, with 0o666 permissions, and returns the new `^File`.
// The filename is generated by taking a pattern, and adding a randomized string to the end.
// If the pattern includes an "*", the random string replaces the last "*".
-// If `dir` is an empty tring, `temp_directory()` will be used.
+// If `dir` is an empty string, `temp_directory()` will be used.
//
// The caller must `close` the file once finished with.
@(require_results)
diff --git a/core/os/os2/temp_file_posix.odin b/core/os/os2/temp_file_posix.odin
new file mode 100644
index 000000000..67ec4d3e8
--- /dev/null
+++ b/core/os/os2/temp_file_posix.odin
@@ -0,0 +1,20 @@
+//+private
+//+build darwin, netbsd, freebsd, openbsd
+package os2
+
+import "base:runtime"
+
+@(require)
+import "core:sys/posix"
+
+_temp_dir :: proc(allocator: runtime.Allocator) -> (string, runtime.Allocator_Error) {
+ if tmp, ok := _lookup_env("TMPDIR", allocator); ok {
+ return tmp, nil
+ }
+
+ when #defined(posix.P_tmpdir) {
+ return clone_string(posix.P_tmpdir, allocator)
+ }
+
+ return clone_string("/tmp/", allocator)
+}
diff --git a/core/sys/darwin/proc.odin b/core/sys/darwin/proc.odin
new file mode 100644
index 000000000..a49383670
--- /dev/null
+++ b/core/sys/darwin/proc.odin
@@ -0,0 +1,142 @@
+package darwin
+
+import "base:intrinsics"
+
+import "core:sys/posix"
+
+foreign import lib "system:System.framework"
+
+// Incomplete bindings to the proc API on MacOS, add to when needed.
+
+foreign lib {
+ proc_pidinfo :: proc(pid: posix.pid_t, flavor: PID_Info_Flavor, arg: i64, buffer: rawptr, buffersize: i32) -> i32 ---
+ proc_pidpath :: proc(pid: posix.pid_t, buffer: [^]byte, buffersize: u32) -> i32 ---
+ proc_listallpids :: proc(buffer: [^]i32, buffersize: i32) -> i32 ---
+}
+
+MAXCOMLEN :: 16
+
+proc_bsdinfo :: struct {
+ pbi_flags: PBI_Flags,
+ pbi_status: u32,
+ pbi_xstatus: u32,
+ pbi_pid: u32,
+ pbi_ppid: u32,
+ pbi_uid: posix.uid_t,
+ pbi_gid: posix.gid_t,
+ pbi_ruid: posix.uid_t,
+ pbi_rgid: posix.gid_t,
+ pbi_svuid: posix.uid_t,
+ pbi_svgid: posix.gid_t,
+ rfu_1: u32,
+ pbi_comm: [MAXCOMLEN]byte `fmt:"s,0"`,
+ pbi_name: [2 * MAXCOMLEN]byte `fmt:"s,0"`,
+ pbi_nfiles: u32,
+ pbi_pgid: u32,
+ pbi_pjobc: u32,
+ e_tdev: u32,
+ e_tpgid: u32,
+ pbi_nice: i32,
+ pbi_start_tvsec: u64,
+ pbi_start_tvusec: u64,
+}
+
+proc_bsdshortinfo :: struct {
+ pbsi_pid: u32,
+ pbsi_ppid: u32,
+ pbsi_pgid: u32,
+ pbsi_status: u32,
+ pbsi_comm: [MAXCOMLEN]byte `fmt:"s,0"`,
+ pbsi_flags: PBI_Flags,
+ pbsi_uid: posix.uid_t,
+ pbsi_gid: posix.gid_t,
+ pbsi_ruid: posix.uid_t,
+ pbsi_rgid: posix.gid_t,
+ pbsi_svuid: posix.uid_t,
+ pbsi_svgid: posix.gid_t,
+ pbsi_rfu: u32,
+}
+
+proc_vnodepathinfo :: struct {
+ pvi_cdir: vnode_info_path,
+ pvi_rdir: vnode_info_path,
+}
+
+vnode_info_path :: struct {
+ vip_vi: vnode_info,
+ vip_path: [posix.PATH_MAX]byte,
+}
+
+vnode_info :: struct {
+ vi_stat: vinfo_stat,
+ vi_type: i32,
+ vi_pad: i32,
+ vi_fsid: fsid_t,
+}
+
+vinfo_stat :: struct {
+ vst_dev: u32,
+ vst_mode: u16,
+ vst_nlink: u16,
+ vst_ino: u64,
+ vst_uid: posix.uid_t,
+ vst_gid: posix.gid_t,
+ vst_atime: i64,
+ vst_atimensec: i64,
+ vst_mtime: i64,
+ vst_mtimensec: i64,
+ vst_ctime: i64,
+ vst_ctimensec: i64,
+ vst_birthtime: i64,
+ vst_birthtimensec: i64,
+ vst_size: posix.off_t,
+ vst_blocks: i64,
+ vst_blksize: i32,
+ vst_flags: u32,
+ vst_gen: u32,
+ vst_rdev: u32,
+ vst_qspare: [2]i64,
+}
+
+fsid_t :: distinct [2]i32
+
+PBI_Flag_Bits :: enum u32 {
+ SYSTEM = intrinsics.constant_log2(0x0001),
+ TRACED = intrinsics.constant_log2(0x0002),
+ INEXIT = intrinsics.constant_log2(0x0004),
+ PWAIT = intrinsics.constant_log2(0x0008),
+ LP64 = intrinsics.constant_log2(0x0010),
+ SLEADER = intrinsics.constant_log2(0x0020),
+ CTTY = intrinsics.constant_log2(0x0040),
+ CONTROLT = intrinsics.constant_log2(0x0080),
+ THCWD = intrinsics.constant_log2(0x0100),
+ PC_THROTTLE = intrinsics.constant_log2(0x0200),
+ PC_SUSP = intrinsics.constant_log2(0x0400),
+ PC_KILL = intrinsics.constant_log2(0x0600),
+ PA_THROTTLE = intrinsics.constant_log2(0x0800),
+ PA_SUSP = intrinsics.constant_log2(0x1000),
+ PA_PSUGID = intrinsics.constant_log2(0x2000),
+ EXEC = intrinsics.constant_log2(0x4000),
+}
+PBI_Flags :: bit_set[PBI_Flag_Bits; u32]
+
+PID_Info_Flavor :: enum i32 {
+ LISTFDS = 1,
+ TASKALLINFO,
+ BSDINFO,
+ TASKINFO,
+ THREADINFO,
+ LISTTHREADS,
+ REGIONINFO,
+ REGIONPATHINFO,
+ VNODEPATHINFO,
+ THREADPATHINFO,
+ PATHINFO,
+ WORKQUEUEINFO,
+ SHORTBSDINFO,
+ LISTFILEPORTS,
+ THREADID64INFO,
+ RUSAGE,
+}
+
+PIDPATHINFO_MAXSIZE :: 4*posix.PATH_MAX
diff --git a/core/sys/posix/unistd.odin b/core/sys/posix/unistd.odin
index 8e08860b4..6ed9e5d11 100644
--- a/core/sys/posix/unistd.odin
+++ b/core/sys/posix/unistd.odin
@@ -211,7 +211,7 @@ foreign lib {
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/_exit.html ]]
*/
- _exit :: proc(status: c.int) ---
+ _exit :: proc(status: c.int) -> ! ---
/*
The exec family of functions shall replace the current process image with a new process image.
@@ -416,8 +416,11 @@ foreign lib {
}
cwd = posix.getcwd(raw_data(buf), len(buf))
- if errno := posix.errno(); cwd == nil && errno != .ERANGE {
- fmt.panicf("getcwd failure: %v", posix.strerror(errno))
+ if cwd == nil {
+ errno := posix.errno()
+ if errno != .ERANGE {
+ fmt.panicf("getcwd failure: %v", posix.strerror(errno))
+ }
}
}
diff --git a/core/sys/unix/sysctl_darwin.odin b/core/sys/unix/sysctl_darwin.odin
index 14d3c113a..92222bdfe 100644
--- a/core/sys/unix/sysctl_darwin.odin
+++ b/core/sys/unix/sysctl_darwin.odin
@@ -67,6 +67,8 @@ CTL_KERN :: 1
KERN_VERSION :: 4 // Darwin Kernel Version 21.5.0: Tue Apr 26 21:08:22 PDT 2022; root:darwin-8020.121.3~4/RELEASE_X86_64
KERN_OSRELDATE :: 26 // i32: OS release date
KERN_OSVERSION :: 65 // Build number, e.g. 21F79
+ KERN_PROCARGS :: 38
+ KERN_PROCARGS2 :: 49
CTL_VM :: 2
CTL_VFS :: 3
CTL_NET :: 4
@@ -82,4 +84,4 @@ CTL_HW :: 6
HW_AVAILCPU :: 25 /* int: number of available CPUs */
CTL_MACHDEP :: 7
-CTL_USER :: 8 \ No newline at end of file
+CTL_USER :: 8
diff --git a/core/sys/unix/sysctl_freebsd.odin b/core/sys/unix/sysctl_freebsd.odin
index 35c5db02c..8ca40ef1b 100644
--- a/core/sys/unix/sysctl_freebsd.odin
+++ b/core/sys/unix/sysctl_freebsd.odin
@@ -23,6 +23,8 @@ CTL_KERN :: 1
KERN_OSRELEASE :: 2
KERN_OSREV :: 3
KERN_VERSION :: 4
+ KERN_PROC :: 14
+ KERN_PROC_PATHNAME :: 12
CTL_VM :: 2
CTL_VFS :: 3
CTL_NET :: 4