diff options
| author | Jeroen van Rijn <Kelimion@users.noreply.github.com> | 2025-06-16 20:51:06 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-06-16 20:51:06 +0200 |
| commit | c4fb5ebf9b3283e0f4acfc3f6c354f30e66e8de2 (patch) | |
| tree | a832edb2577a42c8ab4147f1c987e9096882a89c | |
| parent | 03e5636abee6b806d9c0c01c9a144419b321a6f0 (diff) | |
| parent | b9809e7aa48af6c3af1fe8b53dbdb6c4547aab30 (diff) | |
Merge pull request #5348 from Kelimion/get_env_buffer
Get env buffer
| -rw-r--r-- | core/os/env_windows.odin | 41 | ||||
| -rw-r--r-- | core/os/errors.odin | 4 | ||||
| -rw-r--r-- | core/os/os2/env.odin | 32 | ||||
| -rw-r--r-- | core/os/os2/env_linux.odin | 19 | ||||
| -rw-r--r-- | core/os/os2/env_posix.odin | 32 | ||||
| -rw-r--r-- | core/os/os2/env_wasi.odin | 30 | ||||
| -rw-r--r-- | core/os/os2/env_windows.odin | 32 | ||||
| -rw-r--r-- | core/os/os2/errors.odin | 4 | ||||
| -rw-r--r-- | core/os/os_darwin.odin | 33 | ||||
| -rw-r--r-- | core/os/os_freebsd.odin | 34 | ||||
| -rw-r--r-- | core/os/os_haiku.odin | 34 | ||||
| -rw-r--r-- | core/os/os_js.odin | 25 | ||||
| -rw-r--r-- | core/os/os_linux.odin | 32 | ||||
| -rw-r--r-- | core/os/os_netbsd.odin | 34 | ||||
| -rw-r--r-- | core/os/os_openbsd.odin | 33 | ||||
| -rw-r--r-- | core/os/os_wasi.odin | 25 | ||||
| -rw-r--r-- | core/sys/windows/util.odin | 72 | ||||
| -rw-r--r-- | core/terminal/internal.odin | 11 |
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 } |