From c5404c657304ff7d2a1c97dd213644e7f2adcd36 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Mon, 25 Sep 2023 16:08:27 +0200 Subject: [PATCH] get_env for Win32, @pierrec's get_config_dir and get_home_dir --- lib/std/io/path.c3 | 11 ++++ lib/std/io/stream.c3 | 2 +- lib/std/os/env.c3 | 104 +++++++++++++++++++++++++++++------- lib/std/os/win32/process.c3 | 10 +++- test/unit/stdlib/os/env.c3 | 17 ++++++ 5 files changed, 123 insertions(+), 21 deletions(-) create mode 100644 test/unit/stdlib/os/env.c3 diff --git a/lib/std/io/path.c3 b/lib/std/io/path.c3 index beb123f5a..9ac651dd6 100644 --- a/lib/std/io/path.c3 +++ b/lib/std/io/path.c3 @@ -448,6 +448,17 @@ fn void Path.free(self) free(self.path_string.ptr); } + +fn usz! Path.to_format(&self, Formatter* formatter) @dynamic +{ + return formatter.print(self.str_view()); +} + +fn String Path.to_string(&self, Allocator* using = mem::heap()) @dynamic +{ + return self.str_view().copy(using); +} + const bool[256] RESERVED_PATH_CHAR_POSIX = { [0] = true, ['/'] = true, diff --git a/lib/std/io/stream.c3 b/lib/std/io/stream.c3 index 34d293cf6..27da70f92 100644 --- a/lib/std/io/stream.c3 +++ b/lib/std/io/stream.c3 @@ -430,7 +430,7 @@ macro usz! Stream.write_varint(&self, x) { var $Type = $typeof(x); const MAX = MAX_VARS[$Type.sizeof]; - char[MAX] buffer; + char[MAX] buffer @noinit; usz i; while (x >= 0x80) { diff --git a/lib/std/os/env.c3 b/lib/std/os/env.c3 index cf63339e0..e23997fac 100644 --- a/lib/std/os/env.c3 +++ b/lib/std/os/env.c3 @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Christoffer Lerno. All rights reserved. +// Copyright (c) 2021-2023 Christoffer Lerno. All rights reserved. // Use of this source code is governed by the MIT license // a copy of which can be found in the LICENSE_STDLIB file. module std::os::env; @@ -7,53 +7,119 @@ import libc; /** * @param [in] name * @require name.len > 0 + * @return! SearchResult.MISSING **/ -fn String! get_var(String name) +fn String! get_var(String name, Allocator* using = mem::heap()) { - $if env::LIBC && !env::WIN32: - @pool() + @pool(using) { + $switch + $case env::LIBC && !env::WIN32: ZString val = libc::getenv(name.zstr_tcopy()); - return val ? val.str_view() : SearchResult.MISSING?; + return val ? val.copy(using) : SearchResult.MISSING?; + $case env::WIN32: + // https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getenvironmentvariable + const usz BUFSIZE = 1024; + WString buff = (WString)tcalloc(BUFSIZE * 2 + 2); + WString wstr = name.to_wstring(mem::temp())!; + usz len = win32::getEnvironmentVariableW(wstr, buff, BUFSIZE); + if (len == 0) return SearchResult.MISSING?; + if (len > BUFSIZE) + { + buff = (WString)tmalloc(len * 2 + 2); + win32::getEnvironmentVariableW(wstr, buff, (Win32_DWORD)len); + } + return string::from_wstring(buff, using); + $default: + return ""; + $endswitch }; - $else - return ""; - $endif } + /** * @param [in] name * @param [in] value * @require name.len > 0 **/ -fn void set_var(String name, String value, bool overwrite = true) +fn bool set_var(String name, String value, bool overwrite = true) { - $if env::LIBC && !env::WIN32: @pool() { - if (libc::setenv(name.zstr_tcopy(), value.zstr_copy(), (int)overwrite)) + $switch + $case env::WIN32: + WString wname = name.to_wstring(mem::temp())!!; + if (!overwrite) { - unreachable(); + Char16[8] buff; + if (win32::getEnvironmentVariableW(wname, &buff, 8) > 0) return true; } + // https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setenvironmentvariable + return (win32::setEnvironmentVariableW(wname, value.to_wstring(mem::temp())) ?? 1) == 0; + $case env::LIBC && !env::WIN32: + return libc::setenv(name.zstr_tcopy(), value.zstr_copy(), (int)overwrite) == 0; + $default: + return false; + $endswitch }; + +} + +/** + * Returns the current user's home directory. + **/ +fn String! get_home_dir(Allocator* using = mem::heap()) +{ + String home; + $if !env::WIN32: + home = "HOME"; + $else + home = "USERPROFILE"; $endif + return get_var(home, using); } +/** + * Returns the current user's config directory. + **/ +fn Path! get_config_dir(Allocator* using = mem::heap()) +{ + @pool(using) + { + $if env::WIN32: + return path::new(get_var("AppData", .using = mem::temp()), .using = using); + $else + $if env::DARWIN: + String s = get_var("HOME", .using = mem::temp())!; + const DIR = "Library/Application Support"; + $else + String s = get_var("XDG_CONFIG_HOME", .using = mem::temp()) ?? get_var("HOME", .using = mem::temp())!; + const DIR = ".config"; + $endif + return path::new(s, .using = mem::temp()).append(DIR, .using = using); + $endif + }; +} + + /** * @param [in] name * @require name.len > 0 **/ -fn void clear_var(String name) +fn bool clear_var(String name) { - $if env::LIBC && !env::WIN32: @pool() { - if (libc::unsetenv(name.zstr_tcopy())) - { - unreachable(); - } + $switch + $case env::WIN32: + WString wname = name.to_wstring(mem::temp())!!; + return win32::setEnvironmentVariableW(wname, null) == 0; + $case env::LIBC && !env::WIN32: + return libc::unsetenv(name.zstr_tcopy()) == 0; + $default: + return false; + $endswitch }; - $endif } fn String! executable_path(Allocator *using = mem::heap()) diff --git a/lib/std/os/win32/process.c3 b/lib/std/os/win32/process.c3 index f9912c057..e25d42cbb 100644 --- a/lib/std/os/win32/process.c3 +++ b/lib/std/os/win32/process.c3 @@ -83,7 +83,15 @@ extern fn Win32_BOOL getOverlappedResult( Win32_LPDWORD lpNumberOfBytesTransferred, Win32_BOOL bWait ) @extern("GetOverlappedResult"); - +extern fn Win32_DWORD getEnvironmentVariableW( + Win32_LPCWSTR lpName, + Win32_LPWSTR lpBuffer, + Win32_DWORD nSize +) @extern("GetEnvironmentVariableW"); +extern fn Win32_BOOL setEnvironmentVariableW( + Win32_LPCWSTR lpName, + Win32_LPCWSTR lpValue +) @extern("SetEnvironmentVariableW"); struct SystemInfo { diff --git a/test/unit/stdlib/os/env.c3 b/test/unit/stdlib/os/env.c3 new file mode 100644 index 000000000..d288c6b0c --- /dev/null +++ b/test/unit/stdlib/os/env.c3 @@ -0,0 +1,17 @@ +module std::os::env @test; + + fn void! set_get_unset() + { + const NAME = "C3_TEST_ENVVAR"; + const VALUE = "foobar"; + + env::set_var(NAME, VALUE); + String v = env::get_var(NAME)!; + assert(v == VALUE, "got %s; want %s", v, VALUE); + + env::clear_var(NAME); + if (try env::get_var(NAME)) + { + assert(false, "environment variable should no longer exist"); + } + } \ No newline at end of file