///|
#borrow(key)
extern "c" fn os_getenv(key : Bytes) -> @c.Pointer[Byte] = "moonbit_maria_os_getenv"
///|
fn buflen(buf : FixedArray[Byte]) -> Int {
for i = 0; i < FixedArray::length(buf); i = i + 1 {
if buf[i] == 0 {
return i
}
}
FixedArray::length(buf)
}
///|
pub fn getenv(key : StringView) -> String? raise {
let c_str = os_getenv(@encoding/utf8.encode(key))
if c_str.is_null() {
return None
}
let len = @c.strlen(c_str).to_int()
let buf : FixedArray[Byte] = FixedArray::make(len, 0)
for i = 0; i < len; i = i + 1 {
buf[i] = c_str[i]
}
Some(@encoding/utf8.decode(buf.unsafe_reinterpret_as_bytes()))
}
///|
#borrow(key)
extern "c" fn os_unsetenv(key : Bytes) -> Int = "moonbit_maria_os_unsetenv"
///|
pub fn unsetenv(key : StringView) -> Unit raise {
let result = os_unsetenv(@encoding/utf8.encode(key))
if result != 0 {
raise @errno.Errno(result)
}
}
///|
#borrow(key, value)
extern "c" fn os_setenv(key : Bytes, value : Bytes, overwrite : Int) -> Int = "moonbit_maria_os_setenv"
///|
pub fn setenv(
key : StringView,
value : StringView,
overwrite? : Bool = true,
) -> Unit raise {
let result = os_setenv(
@encoding/utf8.encode(key),
@encoding/utf8.encode(value),
if overwrite {
1
} else {
0
},
)
if result != 0 {
raise @errno.Errno(result)
}
}
///|
extern "c" fn os_argc() -> Int = "moonbit_maria_os_argc"
///|
extern "c" fn os_arg(index : Int) -> Bytes = "moonbit_maria_os_arg"
///|
/// Returns the command line arguments passed to the current process.
///
/// Implementations:
///
/// - macOS: `_NSGetArgc()` / `_NSGetArgv()`
/// - Linux: `/proc/self/cmdline`
pub fn args() -> Array[String] {
let argc = os_argc()
[
for i in 0.. @encoding/utf8.decode_lossy(os_arg(i))
]
}
///|
pub fn cwd() -> String raise {
@env.current_dir().unwrap_or_error(
Failure::Failure("failed to get current working directory"),
)
}
///|
extern "c" fn os_getuid() -> UInt = "moonbit_maria_os_getuid"
///|
#borrow(pwd, result)
extern "c" fn os_getpwuid_r(
uid : UInt,
pwd : FixedArray[Byte],
buf : @c.Pointer[Byte],
buf_len : UInt64,
result : Ref[@c.Pointer[Unit]],
) -> Int = "moonbit_maria_os_getpwuid_r"
///|
extern "c" fn os_passwd_sizeof() -> Int = "moonbit_maria_os_passwd_sizeof"
///|
#borrow(passwd)
extern "c" fn os_passwd_get_dir(passwd : Bytes) -> @c.Pointer[Byte] = "moonbit_maria_os_passwd_get_dir"
///|
extern "c" fn sysconf_SC_GETPW_R_SIZE_MAX() -> Int = "moonbit_maria_sysconf_SC_GETPW_R_SIZE_MAX"
///|
pub fn home() -> String raise {
if getenv("HOME") is Some(home) {
return home
}
let uid = os_getuid()
let pwd : FixedArray[Byte] = FixedArray::make(os_passwd_sizeof(), 0)
let buf_len = sysconf_SC_GETPW_R_SIZE_MAX().to_uint64()
let buf : @c.Pointer[Byte] = @c.malloc(buf_len)
let result : Ref[@c.Pointer[Unit]] = Ref(@c.Pointer::null())
let errno = os_getpwuid_r(uid, pwd, buf, buf_len, result)
if errno != 0 {
raise @errno.Errno(errno)
}
let ptr = os_passwd_get_dir(pwd.unsafe_reinterpret_as_bytes())
let len = @c.strlen(ptr).to_int()
let dir_buf : FixedArray[Byte] = FixedArray::make(len, 0)
for i = 0; i < len; i = i + 1 {
dir_buf[i] = ptr[i]
}
@c.free(buf)
@encoding/utf8.decode(dir_buf.unsafe_reinterpret_as_bytes())
}
///|
pub fn tmpdir() -> String raise {
if getenv("TMPDIR") is Some(tmpdir) {
return tmpdir
}
if getenv("TMP") is Some(temp) {
return temp
}
if getenv("TEMP") is Some(temp) {
return temp
}
if getenv("TEMPDIR") is Some(temp) {
return temp
}
return "/tmp"
}
///|
extern "c" fn sysconf_SC_HOST_NAME_MAX() -> Int64 = "moonbit_maria_sysconf_SC_HOST_NAME_MAX"
///|
#borrow(name)
extern "c" fn os_gethostname(name : FixedArray[Byte]) -> Int = "moonbit_maria_os_gethostname"
///|
pub fn gethostname() -> String raise {
let mut buf : FixedArray[Byte] = FixedArray::make(
sysconf_SC_HOST_NAME_MAX().to_int(),
0,
)
let mut result = os_gethostname(buf)
while result == @errno.enametoolong {
buf = FixedArray::make(FixedArray::length(buf) * 2, 0)
result = os_gethostname(buf)
}
if result == 0 {
let len = buflen(buf)
@encoding/utf8.decode(buf.unsafe_reinterpret_as_bytes()[0:len])
} else {
raise @errno.Errno(result)
}
}
///|
#borrow(path)
extern "c" fn os_chdir(path : Bytes) -> Int = "moonbit_maria_os_chdir"
///|
pub fn chdir(path : StringView) -> Unit raise {
let result = os_chdir(@encoding/utf8.encode(path))
if result != 0 {
raise @errno.Errno(result)
}
}
///|
extern "c" fn os_exit(code : Int) = "exit"
///|
pub fn[X] exit(code : Int) -> X {
os_exit(code)
panic()
}
///|
#borrow(buf)
extern "c" fn os_executable(buf : FixedArray[Byte]) -> Int = "moonbit_maria_os_executable"
///|
/// Returns the path of the current executable. Note that the returned path
/// may be a symbolic link and not the real file, so make sure to resolve it
/// before calling spawn.
///
/// Implementations:
///
/// - macOS: `_NSGetExecutablePath()`
/// - Linux: `readlink("/proc/self/exe")`
pub fn executable() -> String raise {
let mut buf : FixedArray[Byte] = FixedArray::make(1024, 0)
let mut size = os_executable(buf)
if size == -1 {
raise @errno.Errno(@errno.get())
}
while size > buf.length() {
buf = FixedArray::make(size, 0)
size = os_executable(buf)
if size == -1 {
raise @errno.Errno(@errno.get())
}
}
let new_buf : FixedArray[Byte] = FixedArray::make(size, 0)
new_buf.unsafe_blit(0, buf, 0, size)
@encoding/utf8.decode(new_buf.unsafe_reinterpret_as_bytes())
}
///|
/// Registers a function to be called when the program exits normally.
///
/// The registered function will be executed when the program terminates through
/// normal means (such as returning from main or calling `exit()`). Functions
/// are called in reverse order of registration (last registered, first called).
///
/// Parameters:
///
/// * `f` : The function to be called at program exit. This function takes no
/// parameters and returns no value.
pub extern "c" fn atexit(f : FuncRef[() -> Unit]) = "atexit"