aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgingerBill <gingerBill@users.noreply.github.com>2025-01-24 13:09:10 +0000
committerGitHub <noreply@github.com>2025-01-24 13:09:10 +0000
commit3a13c598e2efb46f12121bb532f8f3616d2cf482 (patch)
tree8f354b7327af45881cdb67b5dc6550e2c1621def
parent13a2a29b90727be9e47442104e6bd78b35e5f75f (diff)
parentd54de6704a0acb95e8123480121f0ddba62f1516 (diff)
Merge pull request #4733 from laytan/get-executable-path
os/os2: add get_executable_path and get_executable_directory
-rw-r--r--core/os/os2/errors_posix.odin13
-rw-r--r--core/os/os2/path.odin12
-rw-r--r--core/os/os2/path_darwin.odin17
-rw-r--r--core/os/os2/path_freebsd.odin29
-rw-r--r--core/os/os2/path_linux.odin22
-rw-r--r--core/os/os2/path_netbsd.odin24
-rw-r--r--core/os/os2/path_openbsd.odin57
-rw-r--r--core/os/os2/path_wasi.odin33
-rw-r--r--core/os/os2/path_windows.odin20
-rw-r--r--tests/core/os/os2/path.odin22
10 files changed, 244 insertions, 5 deletions
diff --git a/core/os/os2/errors_posix.odin b/core/os/os2/errors_posix.odin
index 0b5876c0b..8a9ca07df 100644
--- a/core/os/os2/errors_posix.odin
+++ b/core/os/os2/errors_posix.odin
@@ -10,8 +10,12 @@ _error_string :: proc(errno: i32) -> string {
return string(posix.strerror(posix.Errno(errno)))
}
-_get_platform_error :: proc() -> Error {
- #partial switch errno := posix.errno(); errno {
+_get_platform_error_from_errno :: proc() -> Error {
+ return _get_platform_error_existing(posix.errno())
+}
+
+_get_platform_error_existing :: proc(errno: posix.Errno) -> Error {
+ #partial switch errno {
case .EPERM:
return .Permission_Denied
case .EEXIST:
@@ -32,3 +36,8 @@ _get_platform_error :: proc() -> Error {
return Platform_Error(errno)
}
}
+
+_get_platform_error :: proc{
+ _get_platform_error_existing,
+ _get_platform_error_from_errno,
+}
diff --git a/core/os/os2/path.odin b/core/os/os2/path.odin
index 254950d68..9231307f5 100644
--- a/core/os/os2/path.odin
+++ b/core/os/os2/path.odin
@@ -2,6 +2,8 @@ package os2
import "base:runtime"
+import "core:path/filepath"
+
Path_Separator :: _Path_Separator // OS-Specific
Path_Separator_String :: _Path_Separator_String // OS-Specific
Path_List_Separator :: _Path_List_Separator // OS-Specific
@@ -39,3 +41,13 @@ setwd :: set_working_directory
set_working_directory :: proc(dir: string) -> (err: Error) {
return _set_working_directory(dir)
}
+
+get_executable_path :: proc(allocator: runtime.Allocator) -> (path: string, err: Error) {
+ return _get_executable_path(allocator)
+}
+
+get_executable_directory :: proc(allocator: runtime.Allocator) -> (path: string, err: Error) {
+ path = _get_executable_path(allocator) or_return
+ path, _ = filepath.split(path)
+ return
+}
diff --git a/core/os/os2/path_darwin.odin b/core/os/os2/path_darwin.odin
new file mode 100644
index 000000000..65aaf1e95
--- /dev/null
+++ b/core/os/os2/path_darwin.odin
@@ -0,0 +1,17 @@
+package os2
+
+import "base:runtime"
+
+import "core:sys/darwin"
+import "core:sys/posix"
+
+_get_executable_path :: proc(allocator: runtime.Allocator) -> (path: string, err: Error) {
+ buffer: [darwin.PIDPATHINFO_MAXSIZE]byte = ---
+ ret := darwin.proc_pidpath(posix.getpid(), raw_data(buffer[:]), len(buffer))
+ if ret > 0 {
+ return clone_string(string(buffer[:ret]), allocator)
+ }
+
+ err = _get_platform_error()
+ return
+}
diff --git a/core/os/os2/path_freebsd.odin b/core/os/os2/path_freebsd.odin
new file mode 100644
index 000000000..577108eca
--- /dev/null
+++ b/core/os/os2/path_freebsd.odin
@@ -0,0 +1,29 @@
+package os2
+
+import "base:runtime"
+
+import "core:sys/freebsd"
+import "core:sys/posix"
+
+_get_executable_path :: proc(allocator: runtime.Allocator) -> (path: string, err: Error) {
+ req := []freebsd.MIB_Identifier{.CTL_KERN, .KERN_PROC, .KERN_PROC_PATHNAME, freebsd.MIB_Identifier(-1)}
+
+ size: uint
+ if ret := freebsd.sysctl(req, nil, &size, nil, 0); ret != .NONE {
+ err = _get_platform_error(posix.Errno(ret))
+ return
+ }
+ assert(size > 0)
+
+ buf := make([]byte, size, allocator) or_return
+ defer if err != nil { delete(buf, allocator) }
+
+ assert(uint(len(buf)) == size)
+
+ if ret := freebsd.sysctl(req, raw_data(buf), &size, nil, 0); ret != .NONE {
+ err = _get_platform_error(posix.Errno(ret))
+ return
+ }
+
+ return string(buf[:size]), nil
+}
diff --git a/core/os/os2/path_linux.odin b/core/os/os2/path_linux.odin
index bfdb645ef..e3e7f8a7c 100644
--- a/core/os/os2/path_linux.odin
+++ b/core/os/os2/path_linux.odin
@@ -1,9 +1,10 @@
#+private
package os2
+import "base:runtime"
+
import "core:strings"
import "core:strconv"
-import "base:runtime"
import "core:sys/linux"
_Path_Separator :: '/'
@@ -171,6 +172,25 @@ _set_working_directory :: proc(dir: string) -> Error {
return _get_platform_error(linux.chdir(dir_cstr))
}
+_get_executable_path :: proc(allocator: runtime.Allocator) -> (path: string, err: Error) {
+ TEMP_ALLOCATOR_GUARD()
+
+ buf := make([dynamic]byte, 1024, temp_allocator()) or_return
+ for {
+ n, errno := linux.readlink("/proc/self/exe", buf[:])
+ if errno != .NONE {
+ err = _get_platform_error(errno)
+ return
+ }
+
+ if n < len(buf) {
+ return clone_string(string(buf[:n]), allocator)
+ }
+
+ resize(&buf, len(buf)*2) or_return
+ }
+}
+
_get_full_path :: proc(fd: linux.Fd, allocator: runtime.Allocator) -> (fullpath: string, err: Error) {
PROC_FD_PATH :: "/proc/self/fd/"
diff --git a/core/os/os2/path_netbsd.odin b/core/os/os2/path_netbsd.odin
new file mode 100644
index 000000000..f56a91fd6
--- /dev/null
+++ b/core/os/os2/path_netbsd.odin
@@ -0,0 +1,24 @@
+package os2
+
+import "base:runtime"
+
+import "core:sys/posix"
+
+_get_executable_path :: proc(allocator: runtime.Allocator) -> (path: string, err: Error) {
+ TEMP_ALLOCATOR_GUARD()
+
+ buf := make([dynamic]byte, 1024, temp_allocator()) or_return
+ for {
+ n := posix.readlink("/proc/curproc/exe", raw_data(buf), len(buf))
+ if n < 0 {
+ err = _get_platform_error()
+ return
+ }
+
+ if n < len(buf) {
+ return clone_string(string(buf[:n]), allocator)
+ }
+
+ resize(&buf, len(buf)*2) or_return
+ }
+}
diff --git a/core/os/os2/path_openbsd.odin b/core/os/os2/path_openbsd.odin
new file mode 100644
index 000000000..f56c1a61b
--- /dev/null
+++ b/core/os/os2/path_openbsd.odin
@@ -0,0 +1,57 @@
+package os2
+
+import "base:runtime"
+
+import "core:strings"
+import "core:sys/posix"
+
+_get_executable_path :: proc(allocator: runtime.Allocator) -> (path: string, err: Error) {
+ // OpenBSD does not have an API for this, we do our best below.
+
+ if len(runtime.args__) <= 0 {
+ err = .Invalid_Path
+ return
+ }
+
+ real :: proc(path: cstring, allocator: runtime.Allocator) -> (out: string, err: Error) {
+ real := posix.realpath(path)
+ if real == nil {
+ err = _get_platform_error()
+ return
+ }
+ defer posix.free(real)
+ return clone_string(string(real), allocator)
+ }
+
+ arg := runtime.args__[0]
+ sarg := string(arg)
+
+ if len(sarg) == 0 {
+ err = .Invalid_Path
+ return
+ }
+
+ if sarg[0] == '.' || sarg[0] == '/' {
+ return real(arg, allocator)
+ }
+
+ TEMP_ALLOCATOR_GUARD()
+
+ buf := strings.builder_make(temp_allocator())
+
+ paths := get_env("PATH", temp_allocator())
+ for dir in strings.split_iterator(&paths, ":") {
+ strings.builder_reset(&buf)
+ strings.write_string(&buf, dir)
+ strings.write_string(&buf, "/")
+ strings.write_string(&buf, sarg)
+
+ cpath := strings.to_cstring(&buf)
+ if posix.access(cpath, {.X_OK}) == .OK {
+ return real(cpath, allocator)
+ }
+ }
+
+ err = .Invalid_Path
+ return
+}
diff --git a/core/os/os2/path_wasi.odin b/core/os/os2/path_wasi.odin
index 17efbff62..2f8a3c8c6 100644
--- a/core/os/os2/path_wasi.odin
+++ b/core/os/os2/path_wasi.odin
@@ -4,6 +4,7 @@ package os2
import "base:runtime"
import "core:path/filepath"
+import "core:sync"
import "core:sys/wasm/wasi"
_Path_Separator :: '/'
@@ -74,11 +75,39 @@ _remove_all :: proc(path: string) -> (err: Error) {
return remove(path)
}
+g_wd: string
+g_wd_mutex: sync.Mutex
+
_get_working_directory :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
- return ".", .Unsupported
+ sync.guard(&g_wd_mutex)
+
+ return clone_string(g_wd if g_wd != "" else "/", allocator)
}
_set_working_directory :: proc(dir: string) -> (err: Error) {
- err = .Unsupported
+ sync.guard(&g_wd_mutex)
+
+ if dir == g_wd {
+ return
+ }
+
+ if g_wd != "" {
+ delete(g_wd, file_allocator())
+ }
+
+ g_wd = clone_string(dir, file_allocator()) or_return
return
}
+
+_get_executable_path :: proc(allocator: runtime.Allocator) -> (path: string, err: Error) {
+ if len(args) <= 0 {
+ return clone_string("/", allocator)
+ }
+
+ arg := args[0]
+ if len(arg) > 0 && (arg[0] == '.' || arg[0] == '/') {
+ return clone_string(arg, allocator)
+ }
+
+ return concatenate({"/", arg}, allocator)
+}
diff --git a/core/os/os2/path_windows.odin b/core/os/os2/path_windows.odin
index 3e92cb6f3..041a4d1e3 100644
--- a/core/os/os2/path_windows.odin
+++ b/core/os/os2/path_windows.odin
@@ -136,6 +136,26 @@ _set_working_directory :: proc(dir: string) -> (err: Error) {
return
}
+_get_executable_path :: proc(allocator: runtime.Allocator) -> (path: string, err: Error) {
+ TEMP_ALLOCATOR_GUARD()
+
+ buf := make([dynamic]u16, 512, temp_allocator()) or_return
+ for {
+ ret := win32.GetModuleFileNameW(nil, raw_data(buf), win32.DWORD(len(buf)))
+ if ret == 0 {
+ err = _get_platform_error()
+ return
+ }
+
+ if ret == win32.DWORD(len(buf)) && win32.GetLastError() == win32.ERROR_INSUFFICIENT_BUFFER {
+ resize(&buf, len(buf)*2) or_return
+ continue
+ }
+
+ return win32_utf16_to_utf8(buf[:ret], allocator)
+ }
+}
+
can_use_long_paths: bool
@(init)
diff --git a/tests/core/os/os2/path.odin b/tests/core/os/os2/path.odin
new file mode 100644
index 000000000..b91f43368
--- /dev/null
+++ b/tests/core/os/os2/path.odin
@@ -0,0 +1,22 @@
+package tests_core_os_os2
+
+import os "core:os/os2"
+import "core:log"
+import "core:path/filepath"
+import "core:testing"
+import "core:strings"
+
+@(test)
+test_executable :: proc(t: ^testing.T) {
+ path, err := os.get_executable_path(context.allocator)
+ defer delete(path)
+
+ log.infof("executable path: %q", path)
+
+ // NOTE: some sanity checks that should always be the case, at least in the CI.
+
+ testing.expect_value(t, err, nil)
+ testing.expect(t, len(path) > 0)
+ testing.expect(t, filepath.is_abs(path))
+ testing.expectf(t, strings.contains(path, filepath.base(os.args[0])), "expected the executable path to contain the base of os.args[0] which is %q", filepath.base(os.args[0]))
+}