diff --git a/core/os/env_windows.odin b/core/os/env_windows.odin index efd002342..6c7759abe 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,77 @@ 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 + n1 := win32.MultiByteToWideChar(win32.CP_UTF8, win32.MB_ERR_INVALID_CHARS, raw_data(key), i32(len(key)), nil, 0) + if n1 == 0 { + return "", nil + } + + n1 = win32.MultiByteToWideChar(win32.CP_UTF8, win32.MB_ERR_INVALID_CHARS, raw_data(key), i32(len(key)), raw_data(key_buf[:]), n1) + if n1 == 0 { + return "", nil + } + + n2 := win32.GetEnvironmentVariableW(raw_data(key_buf[:]), nil, 0) + if n2 == 0 && get_last_error() == ERROR_ENVVAR_NOT_FOUND { + return "", .Env_Var_Not_Found + } + + val_buf: [513]u16 + n2 = win32.GetEnvironmentVariableW(raw_data(key_buf[:]), raw_data(val_buf[:]), u32(len(val_buf[:]))) + if n2 == 0 && get_last_error() == ERROR_ENVVAR_NOT_FOUND { + return "", .Env_Var_Not_Found + } else if int(n2) > len(buf) { + return "", .Buffer_Full + } + + n3 := win32.WideCharToMultiByte(win32.CP_UTF8, win32.WC_ERR_INVALID_CHARS, raw_data(val_buf[:]), -1, nil, 0, nil, nil) + if n3 == 0 { + return + } else if int(n3) > len(buf) { + return "", .Buffer_Full + } + + n4 := win32.WideCharToMultiByte(win32.CP_UTF8, win32.WC_ERR_INVALID_CHARS, raw_data(val_buf[:]), -1, raw_data(buf), n3, nil, nil) + if n4 == 0 { + return + } else if int(n4) > len(buf) { + return "", .Buffer_Full + } + + for i in 0.. (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/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,11 +839,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} + @(require_results) get_current_directory :: proc(allocator := context.allocator) -> string { context.allocator = allocator 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,11 +886,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} + @(require_results) get_current_directory :: proc(allocator := context.allocator) -> string { context.allocator = allocator 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/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 }