aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeroen van Rijn <Kelimion@users.noreply.github.com>2025-06-16 20:51:06 +0200
committerGitHub <noreply@github.com>2025-06-16 20:51:06 +0200
commitc4fb5ebf9b3283e0f4acfc3f6c354f30e66e8de2 (patch)
treea832edb2577a42c8ab4147f1c987e9096882a89c
parent03e5636abee6b806d9c0c01c9a144419b321a6f0 (diff)
parentb9809e7aa48af6c3af1fe8b53dbdb6c4547aab30 (diff)
Merge pull request #5348 from Kelimion/get_env_buffer
Get env buffer
-rw-r--r--core/os/env_windows.odin41
-rw-r--r--core/os/errors.odin4
-rw-r--r--core/os/os2/env.odin32
-rw-r--r--core/os/os2/env_linux.odin19
-rw-r--r--core/os/os2/env_posix.odin32
-rw-r--r--core/os/os2/env_wasi.odin30
-rw-r--r--core/os/os2/env_windows.odin32
-rw-r--r--core/os/os2/errors.odin4
-rw-r--r--core/os/os_darwin.odin33
-rw-r--r--core/os/os_freebsd.odin34
-rw-r--r--core/os/os_haiku.odin34
-rw-r--r--core/os/os_js.odin25
-rw-r--r--core/os/os_linux.odin32
-rw-r--r--core/os/os_netbsd.odin34
-rw-r--r--core/os/os_openbsd.odin33
-rw-r--r--core/os/os_wasi.odin25
-rw-r--r--core/sys/windows/util.odin72
-rw-r--r--core/terminal/internal.odin11
18 files changed, 480 insertions, 47 deletions
diff --git a/core/os/env_windows.odin b/core/os/env_windows.odin
index efd002342..ef658b0a1 100644
--- a/core/os/env_windows.odin
+++ b/core/os/env_windows.odin
@@ -8,7 +8,7 @@ import "base:runtime"
// Otherwise the returned value will be empty and the boolean will be false
// NOTE: the value will be allocated with the supplied allocator
@(require_results)
-lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
+lookup_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
if key == "" {
return
}
@@ -29,17 +29,54 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
return
}
+// This version of `lookup_env` doesn't allocate and instead requires the user to provide a buffer.
+// Note that it is limited to environment names and values of 512 utf-16 values each
+// due to the necessary utf-8 <> utf-16 conversion.
+@(require_results)
+lookup_env_buffer :: proc(buf: []u8, key: string) -> (value: string, err: Error) {
+ key_buf: [513]u16
+ wkey := win32.utf8_to_wstring(key_buf[:], key)
+ if wkey == nil {
+ return "", .Buffer_Full
+ }
+
+ n2 := win32.GetEnvironmentVariableW(wkey, nil, 0)
+ if n2 == 0 {
+ return "", .Env_Var_Not_Found
+ }
+
+ val_buf: [513]u16
+ n2 = win32.GetEnvironmentVariableW(wkey, raw_data(val_buf[:]), u32(len(val_buf[:])))
+ if n2 == 0 {
+ return "", .Env_Var_Not_Found
+ } else if int(n2) > len(buf) {
+ return "", .Buffer_Full
+ }
+
+ value = win32.utf16_to_utf8(buf, val_buf[:n2])
+
+ return value, nil
+}
+lookup_env :: proc{lookup_env_alloc, lookup_env_buffer}
// get_env retrieves the value of the environment variable named by the key
// It returns the value, which will be empty if the variable is not present
// To distinguish between an empty value and an unset value, use lookup_env
// NOTE: the value will be allocated with the supplied allocator
@(require_results)
-get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
+get_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string) {
value, _ = lookup_env(key, allocator)
return
}
+@(require_results)
+get_env_buf :: proc(buf: []u8, key: string) -> (value: string) {
+ value, _ = lookup_env(buf, key)
+ return
+}
+get_env :: proc{get_env_alloc, get_env_buf}
+
+
// set_env sets the value of the environment variable named by the key
set_env :: proc(key, value: string) -> Error {
k := win32.utf8_to_wstring(key)
diff --git a/core/os/errors.odin b/core/os/errors.odin
index 691397f4b..bf4cf27ff 100644
--- a/core/os/errors.odin
+++ b/core/os/errors.odin
@@ -35,6 +35,9 @@ General_Error :: enum u32 {
File_Is_Pipe,
Not_Dir,
+
+ // Environment variable not found.
+ Env_Var_Not_Found,
}
@@ -82,6 +85,7 @@ error_string :: proc "contextless" (ferr: Error) -> string {
case .Pattern_Has_Separator: return "pattern has separator"
case .File_Is_Pipe: return "file is pipe"
case .Not_Dir: return "file is not directory"
+ case .Env_Var_Not_Found: return "environment variable not found"
}
case io.Error:
switch e {
diff --git a/core/os/os2/env.odin b/core/os/os2/env.odin
index 7d42b040d..310d45af1 100644
--- a/core/os/os2/env.odin
+++ b/core/os/os2/env.odin
@@ -3,25 +3,47 @@ package os2
import "base:runtime"
import "core:strings"
-// get_env retrieves the value of the environment variable named by the key
+// `get_env` retrieves the value of the environment variable named by the key
// It returns the value, which will be empty if the variable is not present
// To distinguish between an empty value and an unset value, use lookup_env
// NOTE: the value will be allocated with the supplied allocator
@(require_results)
-get_env :: proc(key: string, allocator: runtime.Allocator) -> string {
+get_env_alloc :: proc(key: string, allocator: runtime.Allocator) -> string {
value, _ := lookup_env(key, allocator)
return value
}
-// lookup_env gets the value of the environment variable named by the key
+// `get_env` retrieves the value of the environment variable named by the key
+// It returns the value, which will be empty if the variable is not present
+// To distinguish between an empty value and an unset value, use lookup_env
+// NOTE: this version takes a backing buffer for the string value
+@(require_results)
+get_env_buf :: proc(buf: []u8, key: string) -> string {
+ value, _ := lookup_env(buf, key)
+ return value
+}
+
+get_env :: proc{get_env_alloc, get_env_buf}
+
+// `lookup_env` gets the value of the environment variable named by the key
// If the variable is found in the environment the value (which can be empty) is returned and the boolean is true
// Otherwise the returned value will be empty and the boolean will be false
// NOTE: the value will be allocated with the supplied allocator
@(require_results)
-lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
- return _lookup_env(key, allocator)
+lookup_env_alloc :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
+ return _lookup_env_alloc(key, allocator)
}
+// This version of `lookup_env` doesn't allocate and instead requires the user to provide a buffer.
+// Note that it is limited to environment names and values of 512 utf-16 values each
+// due to the necessary utf-8 <> utf-16 conversion.
+@(require_results)
+lookup_env_buf :: proc(buf: []u8, key: string) -> (value: string, err: Error) {
+ return _lookup_env_buf(buf, key)
+}
+
+lookup_env :: proc{lookup_env_alloc, lookup_env_buf}
+
// set_env sets the value of the environment variable named by the key
// Returns Error on failure
set_env :: proc(key, value: string) -> Error {
diff --git a/core/os/os2/env_linux.odin b/core/os/os2/env_linux.odin
index 2ed43dd91..e003b0717 100644
--- a/core/os/os2/env_linux.odin
+++ b/core/os/os2/env_linux.odin
@@ -41,7 +41,7 @@ _lookup :: proc(key: string) -> (value: string, idx: int) {
return "", -1
}
-_lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
+_lookup_env_alloc :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) == 0 {
_build_env()
}
@@ -53,6 +53,23 @@ _lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string
return
}
+_lookup_env_buf :: proc(buf: []u8, key: string) -> (value: string, err: Error) {
+ if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) == 0 {
+ _build_env()
+ }
+
+ if v, idx := _lookup(key); idx != -1 {
+ if len(buf) >= len(v) {
+ copy(buf, v)
+ return string(buf[:len(v)]), nil
+ }
+ return "", .Buffer_Full
+ }
+ return "", .Env_Var_Not_Found
+}
+
+_lookup_env :: proc{_lookup_env_alloc, _lookup_env_buf}
+
_set_env :: proc(key, v_new: string) -> Error {
if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) == 0 {
_build_env()
diff --git a/core/os/os2/env_posix.odin b/core/os/os2/env_posix.odin
index 13682f76b..84500d139 100644
--- a/core/os/os2/env_posix.odin
+++ b/core/os/os2/env_posix.odin
@@ -7,7 +7,7 @@ import "base:runtime"
import "core:strings"
import "core:sys/posix"
-_lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
+_lookup_env_alloc :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
if key == "" {
return
}
@@ -26,6 +26,36 @@ _lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string
return
}
+_lookup_env_buf :: proc(buf: []u8, key: string) -> (value: string, error: Error) {
+ if key == "" {
+ return
+ }
+
+ if len(key) + 1 > len(buf) {
+ return "", .Buffer_Full
+ } else {
+ copy(buf, key)
+ }
+
+ cval := posix.getenv(cstring(raw_data(buf)))
+ if cval == nil {
+ return
+ }
+
+ if value = string(cval); value == "" {
+ return "", .Env_Var_Not_Found
+ } else {
+ if len(value) > len(buf) {
+ return "", .Buffer_Full
+ } else {
+ copy(buf, value)
+ return string(buf[:len(value)]), nil
+ }
+ }
+}
+
+_lookup_env :: proc{_lookup_env_alloc, _lookup_env_buf}
+
_set_env :: proc(key, value: string) -> (err: Error) {
temp_allocator := TEMP_ALLOCATOR_GUARD({})
diff --git a/core/os/os2/env_wasi.odin b/core/os/os2/env_wasi.odin
index faa54e36b..b126d014e 100644
--- a/core/os/os2/env_wasi.odin
+++ b/core/os/os2/env_wasi.odin
@@ -67,7 +67,7 @@ delete_string_if_not_original :: proc(str: string) {
}
@(require_results)
-_lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
+_lookup_env_alloc :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
if err := build_env(); err != nil {
return
}
@@ -79,6 +79,34 @@ _lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string
return
}
+_lookup_env_buf :: proc(buf: []u8, key: string) -> (value: string, error: Error) {
+ if key == "" {
+ return
+ }
+
+ if len(key) + 1 > len(buf) {
+ return "", .Buffer_Full
+ } else {
+ copy(buf, key)
+ }
+
+ sync.shared_guard(&g_env_mutex)
+
+ val, ok := g_env[key]
+
+ if !ok {
+ return "", .Env_Var_Not_Found
+ } else {
+ if len(val) > len(buf) {
+ return "", .Buffer_Full
+ } else {
+ copy(buf, val)
+ return string(buf[:len(val)]), nil
+ }
+ }
+}
+_lookup_env :: proc{_lookup_env_alloc, _lookup_env_buf}
+
@(require_results)
_set_env :: proc(key, value: string) -> (err: Error) {
build_env() or_return
diff --git a/core/os/os2/env_windows.odin b/core/os/os2/env_windows.odin
index 6bfde34bb..55b2bb5ee 100644
--- a/core/os/os2/env_windows.odin
+++ b/core/os/os2/env_windows.odin
@@ -4,7 +4,7 @@ package os2
import win32 "core:sys/windows"
import "base:runtime"
-_lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
+_lookup_env_alloc :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
if key == "" {
return
}
@@ -36,6 +36,36 @@ _lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string
return
}
+// This version of `lookup_env` doesn't allocate and instead requires the user to provide a buffer.
+// Note that it is limited to environment names and values of 512 utf-16 values each
+// due to the necessary utf-8 <> utf-16 conversion.
+@(require_results)
+_lookup_env_buf :: proc(buf: []u8, key: string) -> (value: string, err: Error) {
+ key_buf: [513]u16
+ wkey := win32.utf8_to_wstring(key_buf[:], key)
+ if wkey == nil {
+ return "", .Buffer_Full
+ }
+
+ n2 := win32.GetEnvironmentVariableW(wkey, nil, 0)
+ if n2 == 0 {
+ return "", .Env_Var_Not_Found
+ }
+
+ val_buf: [513]u16
+ n2 = win32.GetEnvironmentVariableW(wkey, raw_data(val_buf[:]), u32(len(val_buf[:])))
+ if n2 == 0 {
+ return "", .Env_Var_Not_Found
+ } else if int(n2) > len(buf) {
+ return "", .Buffer_Full
+ }
+
+ value = win32.utf16_to_utf8(buf, val_buf[:n2])
+
+ return value, nil
+}
+_lookup_env :: proc{_lookup_env_alloc, _lookup_env_buf}
+
_set_env :: proc(key, value: string) -> Error {
temp_allocator := TEMP_ALLOCATOR_GUARD({})
k := win32_utf8_to_wstring(key, temp_allocator) or_return
diff --git a/core/os/os2/errors.odin b/core/os/os2/errors.odin
index a73aee9a6..1cf7d765c 100644
--- a/core/os/os2/errors.odin
+++ b/core/os/os2/errors.odin
@@ -28,7 +28,7 @@ General_Error :: enum u32 {
Pattern_Has_Separator,
No_HOME_Variable,
- Wordexp_Failed,
+ Env_Var_Not_Found,
Unsupported,
}
@@ -77,7 +77,7 @@ error_string :: proc(ferr: Error) -> string {
case .Unsupported: return "unsupported"
case .Pattern_Has_Separator: return "pattern has separator"
case .No_HOME_Variable: return "no $HOME variable"
- case .Wordexp_Failed: return "posix.wordexp was unable to expand"
+ case .Env_Var_Not_Found: return "environment variable not found"
}
case io.Error:
switch e {
diff --git a/core/os/os_darwin.odin b/core/os/os_darwin.odin
index bbffc46d7..b247b1000 100644
--- a/core/os/os_darwin.odin
+++ b/core/os/os_darwin.odin
@@ -1055,9 +1055,10 @@ flush :: proc(fd: Handle) -> Error {
}
@(require_results)
-lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
+lookup_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
path_str := strings.clone_to_cstring(key, context.temp_allocator)
+ // NOTE(tetra): Lifetime of 'cstr' is unclear, but _unix_free(cstr) segfaults.
cstr := _unix_getenv(path_str)
if cstr == nil {
return "", false
@@ -1066,11 +1067,39 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
}
@(require_results)
-get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
+lookup_env_buffer :: proc(buf: []u8, key: string) -> (value: string, err: Error) {
+ if len(key) + 1 > len(buf) {
+ return "", .Buffer_Full
+ } else {
+ copy(buf, key)
+ }
+
+ if value = string(_unix_getenv(cstring(raw_data(buf)))); value == "" {
+ return "", .Env_Var_Not_Found
+ } else {
+ if len(value) > len(buf) {
+ return "", .Buffer_Full
+ } else {
+ copy(buf, value)
+ return string(buf[:len(value)]), nil
+ }
+ }
+}
+lookup_env :: proc{lookup_env_alloc, lookup_env_buffer}
+
+@(require_results)
+get_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string) {
value, _ = lookup_env(key, allocator)
return
}
+@(require_results)
+get_env_buf :: proc(buf: []u8, key: string) -> (value: string) {
+ value, _ = lookup_env(buf, key)
+ return
+}
+get_env :: proc{get_env_alloc, get_env_buf}
+
set_env :: proc(key, value: string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
key_cstring := strings.clone_to_cstring(key, context.temp_allocator)
diff --git a/core/os/os_freebsd.odin b/core/os/os_freebsd.odin
index f2ee6a496..a4c3c819e 100644
--- a/core/os/os_freebsd.odin
+++ b/core/os/os_freebsd.odin
@@ -827,10 +827,10 @@ access :: proc(path: string, mask: int) -> (bool, Error) {
}
@(require_results)
-lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
+lookup_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
-
path_str := strings.clone_to_cstring(key, context.temp_allocator)
+ // NOTE(tetra): Lifetime of 'cstr' is unclear, but _unix_free(cstr) segfaults.
cstr := _unix_getenv(path_str)
if cstr == nil {
return "", false
@@ -839,12 +839,40 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
}
@(require_results)
-get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
+lookup_env_buffer :: proc(buf: []u8, key: string) -> (value: string, err: Error) {
+ if len(key) + 1 > len(buf) {
+ return "", .Buffer_Full
+ } else {
+ copy(buf, key)
+ }
+
+ if value = string(_unix_getenv(cstring(raw_data(buf)))); value == "" {
+ return "", .Env_Var_Not_Found
+ } else {
+ if len(value) > len(buf) {
+ return "", .Buffer_Full
+ } else {
+ copy(buf, value)
+ return string(buf[:len(value)]), nil
+ }
+ }
+}
+lookup_env :: proc{lookup_env_alloc, lookup_env_buffer}
+
+@(require_results)
+get_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string) {
value, _ = lookup_env(key, allocator)
return
}
@(require_results)
+get_env_buf :: proc(buf: []u8, key: string) -> (value: string) {
+ value, _ = lookup_env(buf, key)
+ return
+}
+get_env :: proc{get_env_alloc, get_env_buf}
+
+@(require_results)
get_current_directory :: proc(allocator := context.allocator) -> string {
context.allocator = allocator
// NOTE(tetra): I would use PATH_MAX here, but I was not able to find
diff --git a/core/os/os_haiku.odin b/core/os/os_haiku.odin
index 4ce726965..82ead06ab 100644
--- a/core/os/os_haiku.odin
+++ b/core/os/os_haiku.odin
@@ -463,9 +463,10 @@ access :: proc(path: string, mask: int) -> (bool, Error) {
}
@(require_results)
-lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
+lookup_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
path_str := strings.clone_to_cstring(key, context.temp_allocator)
+ // NOTE(tetra): Lifetime of 'cstr' is unclear, but _unix_free(cstr) segfaults.
cstr := _unix_getenv(path_str)
if cstr == nil {
return "", false
@@ -474,11 +475,40 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
}
@(require_results)
-get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
+lookup_env_buffer :: proc(buf: []u8, key: string) -> (value: string, err: Error) {
+ if len(key) + 1 > len(buf) {
+ return "", .Buffer_Full
+ } else {
+ copy(buf, key)
+ }
+
+ if value = string(_unix_getenv(cstring(raw_data(buf)))); value == "" {
+ return "", .Env_Var_Not_Found
+ } else {
+ if len(value) > len(buf) {
+ return "", .Buffer_Full
+ } else {
+ copy(buf, value)
+ return string(buf[:len(value)]), nil
+ }
+ }
+}
+lookup_env :: proc{lookup_env_alloc, lookup_env_buffer}
+
+@(require_results)
+get_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string) {
value, _ = lookup_env(key, allocator)
return
}
+@(require_results)
+get_env_buf :: proc(buf: []u8, key: string) -> (value: string) {
+ value, _ = lookup_env(buf, key)
+ return
+}
+get_env :: proc{get_env_alloc, get_env_buf}
+
+
@(private, require_results)
_processor_core_count :: proc() -> int {
info: haiku.system_info
diff --git a/core/os/os_js.odin b/core/os/os_js.odin
index adb0f8061..1870218d3 100644
--- a/core/os/os_js.odin
+++ b/core/os/os_js.odin
@@ -250,11 +250,26 @@ current_thread_id :: proc "contextless" () -> int {
return 0
}
-lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
+@(require_results)
+lookup_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
return "", false
}
-get_env :: proc(key: string, allocator := context.allocator) -> string {
- value, _ := lookup_env(key, allocator)
- return value
-} \ No newline at end of file
+@(require_results)
+lookup_env_buffer :: proc(buf: []u8, key: string) -> (value: string, err: Error) {
+ return "", .Env_Var_Not_Found
+}
+lookup_env :: proc{lookup_env_alloc, lookup_env_buffer}
+
+@(require_results)
+get_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string) {
+ value, _ = lookup_env(key, allocator)
+ return
+}
+
+@(require_results)
+get_env_buf :: proc(buf: []u8, key: string) -> (value: string) {
+ value, _ = lookup_env(buf, key)
+ return
+}
+get_env :: proc{get_env_alloc, get_env_buf} \ No newline at end of file
diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin
index 84a7f7b32..c14f573bf 100644
--- a/core/os/os_linux.odin
+++ b/core/os/os_linux.odin
@@ -946,7 +946,7 @@ access :: proc(path: string, mask: int) -> (bool, Error) {
}
@(require_results)
-lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
+lookup_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
path_str := strings.clone_to_cstring(key, context.temp_allocator)
// NOTE(tetra): Lifetime of 'cstr' is unclear, but _unix_free(cstr) segfaults.
@@ -958,11 +958,39 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
}
@(require_results)
-get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
+lookup_env_buffer :: proc(buf: []u8, key: string) -> (value: string, err: Error) {
+ if len(key) + 1 > len(buf) {
+ return "", .Buffer_Full
+ } else {
+ copy(buf, key)
+ }
+
+ if value = string(_unix_getenv(cstring(raw_data(buf)))); value == "" {
+ return "", .Env_Var_Not_Found
+ } else {
+ if len(value) > len(buf) {
+ return "", .Buffer_Full
+ } else {
+ copy(buf, value)
+ return string(buf[:len(value)]), nil
+ }
+ }
+}
+lookup_env :: proc{lookup_env_alloc, lookup_env_buffer}
+
+@(require_results)
+get_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string) {
value, _ = lookup_env(key, allocator)
return
}
+@(require_results)
+get_env_buf :: proc(buf: []u8, key: string) -> (value: string) {
+ value, _ = lookup_env(buf, key)
+ return
+}
+get_env :: proc{get_env_alloc, get_env_buf}
+
set_env :: proc(key, value: string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
key_cstring := strings.clone_to_cstring(key, context.temp_allocator)
diff --git a/core/os/os_netbsd.odin b/core/os/os_netbsd.odin
index 40b41b133..d1e030e8e 100644
--- a/core/os/os_netbsd.odin
+++ b/core/os/os_netbsd.odin
@@ -874,10 +874,10 @@ access :: proc(path: string, mask: int) -> (bool, Error) {
}
@(require_results)
-lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
+lookup_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
-
path_str := strings.clone_to_cstring(key, context.temp_allocator)
+ // NOTE(tetra): Lifetime of 'cstr' is unclear, but _unix_free(cstr) segfaults.
cstr := _unix_getenv(path_str)
if cstr == nil {
return "", false
@@ -886,12 +886,40 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
}
@(require_results)
-get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
+lookup_env_buffer :: proc(buf: []u8, key: string) -> (value: string, err: Error) {
+ if len(key) + 1 > len(buf) {
+ return "", .Buffer_Full
+ } else {
+ copy(buf, key)
+ }
+
+ if value = string(_unix_getenv(cstring(raw_data(buf)))); value == "" {
+ return "", .Env_Var_Not_Found
+ } else {
+ if len(value) > len(buf) {
+ return "", .Buffer_Full
+ } else {
+ copy(buf, value)
+ return string(buf[:len(value)]), nil
+ }
+ }
+}
+lookup_env :: proc{lookup_env_alloc, lookup_env_buffer}
+
+@(require_results)
+get_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string) {
value, _ = lookup_env(key, allocator)
return
}
@(require_results)
+get_env_buf :: proc(buf: []u8, key: string) -> (value: string) {
+ value, _ = lookup_env(buf, key)
+ return
+}
+get_env :: proc{get_env_alloc, get_env_buf}
+
+@(require_results)
get_current_directory :: proc(allocator := context.allocator) -> string {
context.allocator = allocator
// NOTE(tetra): I would use PATH_MAX here, but I was not able to find
diff --git a/core/os/os_openbsd.odin b/core/os/os_openbsd.odin
index d90e51e13..993bff252 100644
--- a/core/os/os_openbsd.odin
+++ b/core/os/os_openbsd.odin
@@ -787,9 +787,10 @@ access :: proc(path: string, mask: int) -> (bool, Error) {
}
@(require_results)
-lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
+lookup_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
path_str := strings.clone_to_cstring(key, context.temp_allocator)
+ // NOTE(tetra): Lifetime of 'cstr' is unclear, but _unix_free(cstr) segfaults.
cstr := _unix_getenv(path_str)
if cstr == nil {
return "", false
@@ -798,12 +799,40 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
}
@(require_results)
-get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
+lookup_env_buffer :: proc(buf: []u8, key: string) -> (value: string, err: Error) {
+ if len(key) + 1 > len(buf) {
+ return "", .Buffer_Full
+ } else {
+ copy(buf, key)
+ }
+
+ if value = string(_unix_getenv(cstring(raw_data(buf)))); value == "" {
+ return "", .Env_Var_Not_Found
+ } else {
+ if len(value) > len(buf) {
+ return "", .Buffer_Full
+ } else {
+ copy(buf, value)
+ return string(buf[:len(value)]), nil
+ }
+ }
+}
+lookup_env :: proc{lookup_env_alloc, lookup_env_buffer}
+
+@(require_results)
+get_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string) {
value, _ = lookup_env(key, allocator)
return
}
@(require_results)
+get_env_buf :: proc(buf: []u8, key: string) -> (value: string) {
+ value, _ = lookup_env(buf, key)
+ return
+}
+get_env :: proc{get_env_alloc, get_env_buf}
+
+@(require_results)
get_current_directory :: proc(allocator := context.allocator) -> string {
context.allocator = allocator
buf := make([dynamic]u8, MAX_PATH)
diff --git a/core/os/os_wasi.odin b/core/os/os_wasi.odin
index a0938e860..4039d247b 100644
--- a/core/os/os_wasi.odin
+++ b/core/os/os_wasi.odin
@@ -240,11 +240,26 @@ exit :: proc "contextless" (code: int) -> ! {
wasi.proc_exit(wasi.exitcode_t(code))
}
-lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
+@(require_results)
+lookup_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
return "", false
}
-get_env :: proc(key: string, allocator := context.allocator) -> string {
- value, _ := lookup_env(key, allocator)
- return value
-} \ No newline at end of file
+@(require_results)
+lookup_env_buffer :: proc(buf: []u8, key: string) -> (value: string, err: Error) {
+ return "", .Env_Var_Not_Found
+}
+lookup_env :: proc{lookup_env_alloc, lookup_env_buffer}
+
+@(require_results)
+get_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string) {
+ value, _ = lookup_env(key, allocator)
+ return
+}
+
+@(require_results)
+get_env_buf :: proc(buf: []u8, key: string) -> (value: string) {
+ value, _ = lookup_env(buf, key)
+ return
+}
+get_env :: proc{get_env_alloc, get_env_buf} \ No newline at end of file
diff --git a/core/sys/windows/util.odin b/core/sys/windows/util.odin
index 2db55c4fe..30eecf8a1 100644
--- a/core/sys/windows/util.odin
+++ b/core/sys/windows/util.odin
@@ -75,7 +75,7 @@ LANGIDFROMLCID :: #force_inline proc "contextless" (lcid: LCID) -> LANGID {
return LANGID(lcid)
}
-utf8_to_utf16 :: proc(s: string, allocator := context.temp_allocator) -> []u16 {
+utf8_to_utf16_alloc :: proc(s: string, allocator := context.temp_allocator) -> []u16 {
if len(s) < 1 {
return nil
}
@@ -101,14 +101,42 @@ utf8_to_utf16 :: proc(s: string, allocator := context.temp_allocator) -> []u16 {
}
return text[:n]
}
-utf8_to_wstring :: proc(s: string, allocator := context.temp_allocator) -> wstring {
+
+utf8_to_utf16_buf :: proc(buf: []u16, s: string) -> []u16 {
+ n1 := MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, raw_data(s), i32(len(s)), nil, 0)
+ if n1 == 0 {
+ return nil
+ } else if int(n1) > len(buf) {
+ return nil
+ }
+
+ n1 = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, raw_data(s), i32(len(s)), raw_data(buf[:]), n1)
+ if n1 == 0 {
+ return nil
+ } else if int(n1) > len(buf) {
+ return nil
+ }
+ return buf[:n1]
+}
+utf8_to_utf16 :: proc{utf8_to_utf16_alloc, utf8_to_utf16_buf}
+
+utf8_to_wstring_alloc :: proc(s: string, allocator := context.temp_allocator) -> wstring {
if res := utf8_to_utf16(s, allocator); len(res) > 0 {
return raw_data(res)
}
return nil
}
-wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator) -> (res: string, err: runtime.Allocator_Error) {
+utf8_to_wstring_buf :: proc(buf: []u16, s: string) -> wstring {
+ if res := utf8_to_utf16(buf, s); len(res) > 0 {
+ return raw_data(res)
+ }
+ return nil
+}
+
+utf8_to_wstring :: proc{utf8_to_wstring_alloc, utf8_to_wstring_buf}
+
+wstring_to_utf8_alloc :: proc(s: wstring, N: int, allocator := context.temp_allocator) -> (res: string, err: runtime.Allocator_Error) {
context.allocator = allocator
if N == 0 {
@@ -142,13 +170,49 @@ wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator)
return string(text[:n]), nil
}
-utf16_to_utf8 :: proc(s: []u16, allocator := context.temp_allocator) -> (res: string, err: runtime.Allocator_Error) {
+wstring_to_utf8_buf :: proc(buf: []u8, s: wstring) -> (res: string) {
+ n := WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, s, -1, nil, 0, nil, nil)
+ if n == 0 {
+ return
+ } else if int(n) > len(buf) {
+ return
+ }
+
+ n2 := WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, s, -1, raw_data(buf), n, nil, nil)
+ if n2 == 0 {
+ return
+ } else if int(n2) > len(buf) {
+ return
+ }
+
+ for i in 0..<n2 {
+ if buf[i] == 0 {
+ n2 = i
+ break
+ }
+ }
+ return string(buf[:n2])
+}
+
+wstring_to_utf8 :: proc{wstring_to_utf8_alloc, wstring_to_utf8_buf}
+
+utf16_to_utf8_alloc :: proc(s: []u16, allocator := context.temp_allocator) -> (res: string, err: runtime.Allocator_Error) {
if len(s) == 0 {
return "", nil
}
return wstring_to_utf8(raw_data(s), len(s), allocator)
}
+utf16_to_utf8_buf :: proc(buf: []u8, s: []u16) -> (res: string) {
+ if len(s) == 0 {
+ return
+ }
+ return wstring_to_utf8(buf, raw_data(s))
+}
+
+utf16_to_utf8 :: proc{utf16_to_utf8_alloc, utf16_to_utf8_buf}
+
+
// AdvAPI32, NetAPI32 and UserENV helpers.
allowed_username :: proc(username: string) -> bool {
diff --git a/core/terminal/internal.odin b/core/terminal/internal.odin
index 485f6868d..44007e14f 100644
--- a/core/terminal/internal.odin
+++ b/core/terminal/internal.odin
@@ -11,17 +11,17 @@ import "core:strings"
// - [[ https://invisible-island.net/ncurses/terminfo.src.html ]]
get_no_color :: proc() -> bool {
- if no_color, ok := os.lookup_env("NO_COLOR"); ok {
- defer delete(no_color)
+ buf: [128]u8
+ if no_color, err := os.lookup_env(buf[:], "NO_COLOR"); err == nil {
return no_color != ""
}
return false
}
get_environment_color :: proc() -> Color_Depth {
+ buf: [128]u8
// `COLORTERM` is non-standard but widespread and unambiguous.
- if colorterm, ok := os.lookup_env("COLORTERM"); ok {
- defer delete(colorterm)
+ if colorterm, err := os.lookup_env(buf[:], "COLORTERM"); err == nil {
// These are the only values that are typically advertised that have
// anything to do with color depth.
if colorterm == "truecolor" || colorterm == "24bit" {
@@ -29,8 +29,7 @@ get_environment_color :: proc() -> Color_Depth {
}
}
- if term, ok := os.lookup_env("TERM"); ok {
- defer delete(term)
+ if term, err := os.lookup_env(buf[:], "TERM"); err == nil {
if strings.contains(term, "-truecolor") {
return .True_Color
}