Get env stuff working on Windows

This commit is contained in:
gingerBill
2022-02-21 12:35:52 +00:00
parent 14cb19c2df
commit 345032f804
6 changed files with 112 additions and 86 deletions
+3 -12
View File
@@ -1,20 +1,11 @@
package os2 package os2
// get_env retrieves the value of the environment variable named by the key // get_env gets 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
get_env :: proc(key: string, allocator := context.allocator) -> string {
value, _ := lookup_env(key, allocator)
return value
}
// 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
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { get_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
return _lookup_env(key, allocator) return _get_env(key, allocator)
} }
// 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
+44 -28
View File
@@ -1,50 +1,56 @@
//+private //+private
package os2 package os2
import "core:runtime"
import "core:mem" import "core:mem"
import win32 "core:sys/windows" import win32 "core:sys/windows"
_lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { _get_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
if key == "" { if key == "" {
return return
} }
wkey := win32.utf8_to_wstring(key) wkey := win32.utf8_to_wstring(key)
b := make([dynamic]u16, 100, context.temp_allocator)
for {
n := win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b)))
if n == 0 {
err := win32.GetLastError()
if err == win32.ERROR_ENVVAR_NOT_FOUND {
return "", false
}
}
if n <= u32(len(b)) { // https://docs.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-getenvironmentvariablew
value = win32.utf16_to_utf8(b[:n], allocator) buf_len := win32.GetEnvironmentVariableW(wkey, nil, 0)
found = true if buf_len == 0 {
return return
}
resize(&b, len(b)*2)
} }
buf := make([dynamic]u16, buf_len, context.temp_allocator)
n := win32.GetEnvironmentVariableW(wkey, raw_data(buf), buf_len)
if n == 0 {
if win32.GetLastError() == win32.ERROR_ENVVAR_NOT_FOUND {
return "", false
}
value = ""
found = true
return
}
value = win32.utf16_to_utf8(buf[:n], allocator)
found = true
return
} }
_set_env :: proc(key, value: string) -> bool { _set_env :: proc(key, value: string) -> bool {
k := win32.utf8_to_wstring(key) k := win32.utf8_to_wstring(key)
v := win32.utf8_to_wstring(value) v := win32.utf8_to_wstring(value)
// https://docs.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-setenvironmentvariablew
return bool(win32.SetEnvironmentVariableW(k, v)) return bool(win32.SetEnvironmentVariableW(k, v))
} }
_unset_env :: proc(key: string) -> bool { _unset_env :: proc(key: string) -> bool {
k := win32.utf8_to_wstring(key) k := win32.utf8_to_wstring(key)
// https://docs.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-setenvironmentvariablew
return bool(win32.SetEnvironmentVariableW(k, nil)) return bool(win32.SetEnvironmentVariableW(k, nil))
} }
_clear_env :: proc() { _clear_env :: proc() {
envs := environ(context.temp_allocator) envs := environ(context.temp_allocator)
for env in envs { for env in envs {
for j in 1..<len(env) { #no_bounds_check for j in 1..<len(env) {
if env[j] == '=' { if env[j] == '=' {
unset_env(env[0:j]) unset_env(env[0:j])
break break
@@ -54,23 +60,33 @@ _clear_env :: proc() {
} }
_environ :: proc(allocator := context.allocator) -> []string { _environ :: proc(allocator := context.allocator) -> []string {
envs := win32.GetEnvironmentStringsW() envs := ([^]u16)(win32.GetEnvironmentStringsW())
if envs == nil { if envs == nil {
return nil return nil
} }
defer win32.FreeEnvironmentStringsW(envs) defer win32.FreeEnvironmentStringsW(envs)
r := make([dynamic]string, 0, 50, allocator) length := 0
for from, i, p := 0, 0, envs; true; i += 1 { n := 0
c := (^u16)(uintptr(p) + uintptr(i*2))^ count_loop: for {
if c == 0 { if envs[length] == 0 {
if i <= from { n += 1
break if envs[length+1] == 0 {
break count_loop
} }
w := mem.slice_ptr(mem.ptr_offset(p, from), i-from) }
append(&r, win32.utf16_to_utf8(w, allocator)) length += 1
from = i + 1 }
r := make([dynamic]string, 0, n, allocator)
for offset, i := 0, 0; i < length && len(r) < n; i += 1 {
c := envs[i]
if c == 0 {
wstr := envs[offset:i]
append(&r, win32.utf16_to_utf8(wstr, allocator))
i += 1
offset = i
} }
} }
+33 -23
View File
@@ -63,31 +63,41 @@ is_platform_error :: proc(ferr: Error) -> (err: i32, ok: bool) {
error_string :: proc(ferr: Error) -> string { error_string :: proc(ferr: Error) -> string {
switch ferr { @static general_error_strings := [General_Error]string{
case nil: return "" .Invalid_Argument = "invalid argument",
case .Invalid_Argument: return "invalid argument" .Permission_Denied = "permission denied",
case .Permission_Denied: return "permission denied" .Exist = "file already exists",
case .Exist: return "file already exists" .Not_Exist = "file does not exist",
case .Not_Exist: return "file does not exist" .Closed = "file already closed",
case .Closed: return "file already closed" .Timeout = "i/o timeout",
case .Timeout: return "i/o timeout"
case .EOF: return "eof"
case .Unexpected_EOF: return "unexpected eof"
case .Short_Write: return "short write"
case .Invalid_Write: return "invalid write result"
case .Short_Buffer: return "short buffer"
case .No_Progress: return "multiple read calls return no data or error"
case .Invalid_Whence: return "invalid whence"
case .Invalid_Offset: return "invalid offset"
case .Invalid_Unread: return "invalid unread"
case .Negative_Read: return "negative read"
case .Negative_Write: return "negative write"
case .Negative_Count: return "negative count"
case .Buffer_Full: return "buffer full"
} }
if errno, ok := is_platform_error(ferr); ok { @static io_error_strings := [io.Error]string{
return _error_string(errno) .None = "",
.EOF = "eof",
.Unexpected_EOF = "unexpected eof",
.Short_Write = "short write",
.Invalid_Write = "invalid write result",
.Short_Buffer = "short buffer",
.No_Progress = "multiple read calls return no data or error",
.Invalid_Whence = "invalid whence",
.Invalid_Offset = "invalid offset",
.Invalid_Unread = "invalid unread",
.Negative_Read = "negative read",
.Negative_Write = "negative write",
.Negative_Count = "negative count",
.Buffer_Full = "buffer full",
.Unknown = "unknown i/o error",
.Empty = "empty i/o error",
}
if ferr == nil {
return ""
}
switch err in ferr {
case General_Error: return general_error_strings[err]
case io.Error: return io_error_strings[err]
case Platform_Error: return _error_string(err.err)
} }
return "unknown error" return "unknown error"
+21 -10
View File
@@ -19,14 +19,25 @@ File_Mode_Char_Device :: File_Mode(1<<19)
File_Mode_Sym_Link :: File_Mode(1<<20) File_Mode_Sym_Link :: File_Mode(1<<20)
O_RDONLY :: int( 0) File_Flag :: enum u32 {
O_WRONLY :: int( 1) Read = 0,
O_RDWR :: int( 2) Write = 1,
O_APPEND :: int( 4) Append = 2,
O_CREATE :: int( 8) Create = 3,
O_EXCL :: int(16) Excl = 4,
O_SYNC :: int(32) Sync = 5,
O_TRUNC :: int(64) Trunc = 6,
}
File_Flags :: distinct bit_set[File_Flag; u32]
O_RDONLY :: File_Flags{.Read}
O_WRONLY :: File_Flags{.Write}
O_RDWR :: File_Flags{.Read, .Write}
O_APPEND :: File_Flags{.Append}
O_CREATE :: File_Flags{.Create}
O_EXCL :: File_Flags{.Excl}
O_SYNC :: File_Flags{.Sync}
O_TRUNC :: File_Flags{.Trunc}
@@ -43,8 +54,8 @@ open :: proc(name: string) -> (Handle, Error) {
return _open(name) return _open(name)
} }
open_file :: proc(name: string, flag: int, perm: File_Mode) -> (Handle, Error) { open_file :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (Handle, Error) {
return _open_file(name, flag, perm) return _open_file(name, flags, perm)
} }
close :: proc(fd: Handle) -> Error { close :: proc(fd: Handle) -> Error {
+1 -1
View File
@@ -12,7 +12,7 @@ _open :: proc(name: string) -> (Handle, Error) {
return 0, nil return 0, nil
} }
_open_file :: proc(name: string, flag: int, perm: File_Mode) -> (Handle, Error) { _open_file :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (Handle, Error) {
return 0, nil return 0, nil
} }
+10 -12
View File
@@ -5,19 +5,19 @@ import "core:strings"
user_cache_dir :: proc(allocator := context.allocator) -> (dir: string, is_defined: bool) { user_cache_dir :: proc(allocator := context.allocator) -> (dir: string, is_defined: bool) {
#partial switch ODIN_OS { #partial switch ODIN_OS {
case .Windows: case .Windows:
dir = get_env("LocalAppData") dir = get_env("LocalAppData") or_return
if dir != "" { if dir != "" {
dir = strings.clone(dir, allocator) dir = strings.clone(dir, allocator)
} }
case .Darwin: case .Darwin:
dir = get_env("HOME") dir = get_env("HOME") or_return
if dir != "" { if dir != "" {
dir = strings.concatenate({dir, "/Library/Caches"}, allocator) dir = strings.concatenate({dir, "/Library/Caches"}, allocator)
} }
case: // All other UNIX systems case: // All other UNIX systems
dir = get_env("XDG_CACHE_HOME") dir = get_env("XDG_CACHE_HOME") or_return
if dir == "" { if dir == "" {
dir = get_env("HOME") dir = get_env("HOME") or_return
if dir == "" { if dir == "" {
return return
} }
@@ -31,19 +31,19 @@ user_cache_dir :: proc(allocator := context.allocator) -> (dir: string, is_defin
user_config_dir :: proc(allocator := context.allocator) -> (dir: string, is_defined: bool) { user_config_dir :: proc(allocator := context.allocator) -> (dir: string, is_defined: bool) {
#partial switch ODIN_OS { #partial switch ODIN_OS {
case .Windows: case .Windows:
dir = get_env("AppData") dir = get_env("AppData") or_return
if dir != "" { if dir != "" {
dir = strings.clone(dir, allocator) dir = strings.clone(dir, allocator)
} }
case .Darwin: case .Darwin:
dir = get_env("HOME") dir = get_env("HOME") or_return
if dir != "" { if dir != "" {
dir = strings.concatenate({dir, "/Library/Application Support"}, allocator) dir = strings.concatenate({dir, "/Library/Application Support"}, allocator)
} }
case: // All other UNIX systems case: // All other UNIX systems
dir = get_env("XDG_CACHE_HOME") dir = get_env("XDG_CACHE_HOME") or_return
if dir == "" { if dir == "" {
dir = get_env("HOME") dir = get_env("HOME") or_return
if dir == "" { if dir == "" {
return return
} }
@@ -60,9 +60,7 @@ user_home_dir :: proc() -> (dir: string, is_defined: bool) {
case .Windows: case .Windows:
env = "USERPROFILE" env = "USERPROFILE"
} }
if v := get_env(env); v != "" { v := get_env(env) or_return
return v, true return v, true
}
return "", false
} }