use crate::seqstring::global_string;
use crate::stack::{Stack, pop, push};
use crate::value::Value;
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_getenv(stack: Stack) -> Stack {
unsafe {
let (stack, name_val) = pop(stack);
let name = match name_val {
Value::String(s) => s,
_ => panic!(
"getenv: expected String (name) on stack, got {:?}",
name_val
),
};
match std::env::var(name.as_str()) {
Ok(value) => {
let stack = push(stack, Value::String(global_string(value)));
push(stack, Value::Bool(true)) }
Err(_) => {
let stack = push(stack, Value::String(global_string(String::new())));
push(stack, Value::Bool(false)) }
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_home_dir(stack: Stack) -> Stack {
unsafe {
if let Ok(home) = std::env::var("HOME") {
let stack = push(stack, Value::String(global_string(home)));
return push(stack, Value::Bool(true));
}
#[cfg(windows)]
if let Ok(home) = std::env::var("USERPROFILE") {
let stack = push(stack, Value::String(global_string(home)));
return push(stack, Value::Bool(true));
}
let stack = push(stack, Value::String(global_string(String::new())));
push(stack, Value::Bool(false))
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_current_dir(stack: Stack) -> Stack {
unsafe {
match std::env::current_dir() {
Ok(path) => {
let path_str = path.to_string_lossy().into_owned();
let stack = push(stack, Value::String(global_string(path_str)));
push(stack, Value::Bool(true)) }
Err(_) => {
let stack = push(stack, Value::String(global_string(String::new())));
push(stack, Value::Bool(false)) }
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_path_exists(stack: Stack) -> Stack {
unsafe {
let (stack, path_val) = pop(stack);
let path = match path_val {
Value::String(s) => s,
_ => panic!(
"path-exists: expected String (path) on stack, got {:?}",
path_val
),
};
let exists = std::path::Path::new(path.as_str()).exists();
push(stack, Value::Bool(exists))
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_path_is_file(stack: Stack) -> Stack {
unsafe {
let (stack, path_val) = pop(stack);
let path = match path_val {
Value::String(s) => s,
_ => panic!(
"path-is-file: expected String (path) on stack, got {:?}",
path_val
),
};
let is_file = std::path::Path::new(path.as_str()).is_file();
push(stack, Value::Bool(is_file))
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_path_is_dir(stack: Stack) -> Stack {
unsafe {
let (stack, path_val) = pop(stack);
let path = match path_val {
Value::String(s) => s,
_ => panic!(
"path-is-dir: expected String (path) on stack, got {:?}",
path_val
),
};
let is_dir = std::path::Path::new(path.as_str()).is_dir();
push(stack, Value::Bool(is_dir))
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_path_join(stack: Stack) -> Stack {
unsafe {
let (stack, component_val) = pop(stack);
let (stack, base_val) = pop(stack);
let base = match base_val {
Value::String(s) => s,
_ => panic!(
"path-join: expected String (base) on stack, got {:?}",
base_val
),
};
let component = match component_val {
Value::String(s) => s,
_ => panic!(
"path-join: expected String (component) on stack, got {:?}",
component_val
),
};
let joined = std::path::Path::new(base.as_str())
.join(component.as_str())
.to_string_lossy()
.into_owned();
push(stack, Value::String(global_string(joined)))
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_path_parent(stack: Stack) -> Stack {
unsafe {
let (stack, path_val) = pop(stack);
let path = match path_val {
Value::String(s) => s,
_ => panic!(
"path-parent: expected String (path) on stack, got {:?}",
path_val
),
};
match std::path::Path::new(path.as_str()).parent() {
Some(parent) => {
let parent_str = parent.to_string_lossy().into_owned();
let stack = push(stack, Value::String(global_string(parent_str)));
push(stack, Value::Bool(true)) }
None => {
let stack = push(stack, Value::String(global_string(String::new())));
push(stack, Value::Bool(false)) }
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_path_filename(stack: Stack) -> Stack {
unsafe {
let (stack, path_val) = pop(stack);
let path = match path_val {
Value::String(s) => s,
_ => panic!(
"path-filename: expected String (path) on stack, got {:?}",
path_val
),
};
match std::path::Path::new(path.as_str()).file_name() {
Some(filename) => {
let filename_str = filename.to_string_lossy().into_owned();
let stack = push(stack, Value::String(global_string(filename_str)));
push(stack, Value::Bool(true)) }
None => {
let stack = push(stack, Value::String(global_string(String::new())));
push(stack, Value::Bool(false)) }
}
}
}
const EXIT_CODE_MIN: i64 = 0;
const EXIT_CODE_MAX: i64 = 255;
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_exit(stack: Stack) -> Stack {
unsafe {
let (_stack, code_val) = pop(stack);
let code = match code_val {
Value::Int(n) => {
if !(EXIT_CODE_MIN..=EXIT_CODE_MAX).contains(&n) {
panic!(
"os.exit: exit code must be in range {}-{}, got {}",
EXIT_CODE_MIN, EXIT_CODE_MAX, n
);
}
n as i32
}
_ => panic!(
"os.exit: expected Int (exit code) on stack, got {:?}",
code_val
),
};
std::process::exit(code);
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_os_name(stack: Stack) -> Stack {
let name = if cfg!(target_os = "macos") {
"darwin"
} else if cfg!(target_os = "linux") {
"linux"
} else if cfg!(target_os = "windows") {
"windows"
} else if cfg!(target_os = "freebsd") {
"freebsd"
} else if cfg!(target_os = "openbsd") {
"openbsd"
} else if cfg!(target_os = "netbsd") {
"netbsd"
} else {
"unknown"
};
unsafe { push(stack, Value::String(global_string(name.to_owned()))) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_os_arch(stack: Stack) -> Stack {
let arch = if cfg!(target_arch = "x86_64") {
"x86_64"
} else if cfg!(target_arch = "aarch64") {
"aarch64"
} else if cfg!(target_arch = "arm") {
"arm"
} else if cfg!(target_arch = "x86") {
"x86"
} else if cfg!(target_arch = "riscv64") {
"riscv64"
} else {
"unknown"
};
unsafe { push(stack, Value::String(global_string(arch.to_owned()))) }
}
#[cfg(test)]
mod tests;