Add buffered get_env variants to os2

This commit is contained in:
Jeroen van Rijn
2025-06-16 20:12:27 +02:00
parent eef07431ef
commit 6347c87b5b
7 changed files with 140 additions and 13 deletions
+2 -2
View File
@@ -41,13 +41,13 @@ lookup_env_buffer :: proc(buf: []u8, key: string) -> (value: string, err: Error)
} }
n2 := win32.GetEnvironmentVariableW(wkey, nil, 0) n2 := win32.GetEnvironmentVariableW(wkey, nil, 0)
if n2 == 0 && get_last_error() == ERROR_ENVVAR_NOT_FOUND { if n2 == 0 {
return "", .Env_Var_Not_Found return "", .Env_Var_Not_Found
} }
val_buf: [513]u16 val_buf: [513]u16
n2 = win32.GetEnvironmentVariableW(wkey, raw_data(val_buf[:]), u32(len(val_buf[:]))) n2 = win32.GetEnvironmentVariableW(wkey, raw_data(val_buf[:]), u32(len(val_buf[:])))
if n2 == 0 && get_last_error() == ERROR_ENVVAR_NOT_FOUND { if n2 == 0 {
return "", .Env_Var_Not_Found return "", .Env_Var_Not_Found
} else if int(n2) > len(buf) { } else if int(n2) > len(buf) {
return "", .Buffer_Full return "", .Buffer_Full
+27 -5
View File
@@ -3,25 +3,47 @@ package os2
import "base:runtime" import "base:runtime"
import "core:strings" 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 // 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 // To distinguish between an empty value and an unset value, use lookup_env
// NOTE: the value will be allocated with the supplied allocator // NOTE: the value will be allocated with the supplied allocator
@(require_results) @(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) value, _ := lookup_env(key, allocator)
return value 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 // 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 // Otherwise the returned value will be empty and the boolean will be false
// NOTE: the value will be allocated with the supplied allocator // NOTE: the value will be allocated with the supplied allocator
@(require_results) @(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) {
return _lookup_env(key, allocator) 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 // set_env sets the value of the environment variable named by the key
// Returns Error on failure // Returns Error on failure
set_env :: proc(key, value: string) -> Error { set_env :: proc(key, value: string) -> Error {
+18 -1
View File
@@ -41,7 +41,7 @@ _lookup :: proc(key: string) -> (value: string, idx: int) {
return "", -1 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 { if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) == 0 {
_build_env() _build_env()
} }
@@ -53,6 +53,23 @@ _lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string
return 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 { _set_env :: proc(key, v_new: string) -> Error {
if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) == 0 { if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) == 0 {
_build_env() _build_env()
+31 -1
View File
@@ -7,7 +7,7 @@ import "base:runtime"
import "core:strings" import "core:strings"
import "core:sys/posix" 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 == "" { if key == "" {
return return
} }
@@ -26,6 +26,36 @@ _lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string
return 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(ckey)
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) { _set_env :: proc(key, value: string) -> (err: Error) {
temp_allocator := TEMP_ALLOCATOR_GUARD({}) temp_allocator := TEMP_ALLOCATOR_GUARD({})
+29 -1
View File
@@ -67,7 +67,7 @@ delete_string_if_not_original :: proc(str: string) {
} }
@(require_results) @(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 { if err := build_env(); err != nil {
return return
} }
@@ -79,6 +79,34 @@ _lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string
return 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) @(require_results)
_set_env :: proc(key, value: string) -> (err: Error) { _set_env :: proc(key, value: string) -> (err: Error) {
build_env() or_return build_env() or_return
+31 -1
View File
@@ -4,7 +4,7 @@ package os2
import win32 "core:sys/windows" import win32 "core:sys/windows"
import "base:runtime" 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 == "" { if key == "" {
return return
} }
@@ -36,6 +36,36 @@ _lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string
return 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 { _set_env :: proc(key, value: string) -> Error {
temp_allocator := TEMP_ALLOCATOR_GUARD({}) temp_allocator := TEMP_ALLOCATOR_GUARD({})
k := win32_utf8_to_wstring(key, temp_allocator) or_return k := win32_utf8_to_wstring(key, temp_allocator) or_return
+2 -2
View File
@@ -28,7 +28,7 @@ General_Error :: enum u32 {
Pattern_Has_Separator, Pattern_Has_Separator,
No_HOME_Variable, No_HOME_Variable,
Wordexp_Failed, Env_Var_Not_Found,
Unsupported, Unsupported,
} }
@@ -77,7 +77,7 @@ error_string :: proc(ferr: Error) -> string {
case .Unsupported: return "unsupported" case .Unsupported: return "unsupported"
case .Pattern_Has_Separator: return "pattern has separator" case .Pattern_Has_Separator: return "pattern has separator"
case .No_HOME_Variable: return "no $HOME variable" 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: case io.Error:
switch e { switch e {