Procházet zdrojové kódy

Get env stuff working on Windows

gingerBill před 3 roky
rodič
revize
345032f804

+ 3 - 12
core/os/os2/env.odin

@@ -1,20 +1,11 @@
 package os2
 
-// 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
-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
+// get_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
-lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
-	return _lookup_env(key, allocator)
+get_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
+	return _get_env(key, allocator)
 }
 
 // set_env sets the value of the environment variable named by the key

+ 43 - 27
core/os/os2/env_windows.odin

@@ -1,50 +1,56 @@
 //+private
 package os2
 
+import "core:runtime"
 import "core:mem"
 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 == "" {
 		return
 	}
 	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)) {
-			value = win32.utf16_to_utf8(b[:n], allocator)
-			found = true
-			return
+	// https://docs.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-getenvironmentvariablew
+	buf_len := win32.GetEnvironmentVariableW(wkey, nil, 0)
+	if buf_len == 0 {
+		return
+	}
+	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
 		}
-
-		resize(&b, len(b)*2)
+		value = ""
+		found = true
+		return
 	}
+
+	value = win32.utf16_to_utf8(buf[:n], allocator)
+	found = true
+	return
 }
 
 _set_env :: proc(key, value: string) -> bool {
 	k := win32.utf8_to_wstring(key)
 	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))
 }
 
 _unset_env :: proc(key: string) -> bool {
 	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))
 }
 
 _clear_env :: proc() {
 	envs := environ(context.temp_allocator)
 	for env in envs {
-		for j in 1..<len(env) {
+		#no_bounds_check for j in 1..<len(env) {
 			if env[j] == '=' {
 				unset_env(env[0:j])
 				break
@@ -54,23 +60,33 @@ _clear_env :: proc() {
 }
 
 _environ :: proc(allocator := context.allocator) -> []string {
-	envs := win32.GetEnvironmentStringsW()
+	envs := ([^]u16)(win32.GetEnvironmentStringsW())
 	if envs == nil {
 		return nil
 	}
 	defer win32.FreeEnvironmentStringsW(envs)
 
-	r := make([dynamic]string, 0, 50, allocator)
-	for from, i, p := 0, 0, envs; true; i += 1 {
-		c := (^u16)(uintptr(p) + uintptr(i*2))^
-		if c == 0 {
-			if i <= from {
-				break
+	length := 0
+	n := 0
+	count_loop: for {
+		if envs[length] == 0 {
+			n += 1
+			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))
-			from = i + 1
+		length += 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
core/os/os2/errors.odin

@@ -63,31 +63,41 @@ is_platform_error :: proc(ferr: Error) -> (err: i32, ok: bool) {
 
 
 error_string :: proc(ferr: Error) -> string {
-	switch ferr {
-	case nil:                return ""
-	case .Invalid_Argument:  return "invalid argument"
-	case .Permission_Denied: return "permission denied"
-	case .Exist:             return "file already exists"
-	case .Not_Exist:         return "file does not exist"
-	case .Closed:            return "file already closed"
-	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"
+	@static general_error_strings := [General_Error]string{
+		.Invalid_Argument  = "invalid argument",
+		.Permission_Denied = "permission denied",
+		.Exist             = "file already exists",
+		.Not_Exist         = "file does not exist",
+		.Closed            = "file already closed",
+		.Timeout           = "i/o timeout",
 	}
 
-	if errno, ok := is_platform_error(ferr); ok {
-		return _error_string(errno)
+	@static io_error_strings := [io.Error]string{
+		.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"

+ 21 - 10
core/os/os2/file.odin

@@ -19,14 +19,25 @@ File_Mode_Char_Device :: File_Mode(1<<19)
 File_Mode_Sym_Link    :: File_Mode(1<<20)
 
 
-O_RDONLY :: int( 0)
-O_WRONLY :: int( 1)
-O_RDWR   :: int( 2)
-O_APPEND :: int( 4)
-O_CREATE :: int( 8)
-O_EXCL   :: int(16)
-O_SYNC   :: int(32)
-O_TRUNC  :: int(64)
+File_Flag :: enum u32 {
+	Read   = 0,
+	Write  = 1,
+	Append = 2,
+	Create = 3,
+	Excl   = 4,
+	Sync   = 5,
+	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)
 }
 
-open_file :: proc(name: string, flag: int, perm: File_Mode) -> (Handle, Error) {
-	return _open_file(name, flag, perm)
+open_file :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (Handle, Error) {
+	return _open_file(name, flags, perm)
 }
 
 close :: proc(fd: Handle) -> Error {

+ 1 - 1
core/os/os2/file_windows.odin

@@ -12,7 +12,7 @@ _open :: proc(name: string) -> (Handle, Error) {
 	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
 }
 

+ 10 - 12
core/os/os2/user.odin

@@ -5,19 +5,19 @@ import "core:strings"
 user_cache_dir :: proc(allocator := context.allocator) -> (dir: string, is_defined: bool) {
 	#partial switch ODIN_OS {
 	case .Windows:
-		dir = get_env("LocalAppData")
+		dir = get_env("LocalAppData") or_return
 		if dir != "" {
 			dir = strings.clone(dir, allocator)
 		}
 	case .Darwin:
-		dir = get_env("HOME")
+		dir = get_env("HOME") or_return
 		if dir != "" {
 			dir = strings.concatenate({dir, "/Library/Caches"}, allocator)
 		}
 	case: // All other UNIX systems
-		dir = get_env("XDG_CACHE_HOME")
+		dir = get_env("XDG_CACHE_HOME") or_return
 		if dir == "" {
-			dir = get_env("HOME")
+			dir = get_env("HOME") or_return
 			if dir == "" {
 				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) {
 	#partial switch ODIN_OS {
 	case .Windows:
-		dir = get_env("AppData")
+		dir = get_env("AppData") or_return
 		if dir != "" {
 			dir = strings.clone(dir, allocator)
 		}
 	case .Darwin:
-		dir = get_env("HOME")
+		dir = get_env("HOME") or_return
 		if dir != "" {
 			dir = strings.concatenate({dir, "/Library/Application Support"}, allocator)
 		}
 	case: // All other UNIX systems
-		dir = get_env("XDG_CACHE_HOME")
+		dir = get_env("XDG_CACHE_HOME") or_return
 		if dir == "" {
-			dir = get_env("HOME")
+			dir = get_env("HOME") or_return
 			if dir == "" {
 				return
 			}
@@ -60,9 +60,7 @@ user_home_dir :: proc() -> (dir: string, is_defined: bool) {
 	case .Windows:
 		env = "USERPROFILE"
 	}
-	if v := get_env(env); v != "" {
-		return v, true
-	}
-	return "", false
+	v := get_env(env) or_return
+	return v, true
 }