1use crate::seqstring::global_string;
9use crate::stack::{Stack, pop, push};
10use crate::value::Value;
11
12fn path_str(s: &crate::seqstring::SeqString) -> &str {
20 s.as_str_or_empty()
21}
22
23#[unsafe(no_mangle)]
32pub unsafe extern "C" fn patch_seq_getenv(stack: Stack) -> Stack {
33 unsafe {
34 let (stack, name_val) = pop(stack);
35 let name = match name_val {
36 Value::String(s) => s,
37 _ => panic!(
38 "getenv: expected String (name) on stack, got {:?}",
39 name_val
40 ),
41 };
42
43 match std::env::var(name.as_str_or_empty()) {
44 Ok(value) => {
45 let stack = push(stack, Value::String(global_string(value)));
46 push(stack, Value::Bool(true)) }
48 Err(_) => {
49 let stack = push(stack, Value::String(global_string(String::new())));
50 push(stack, Value::Bool(false)) }
52 }
53 }
54}
55
56#[unsafe(no_mangle)]
65pub unsafe extern "C" fn patch_seq_home_dir(stack: Stack) -> Stack {
66 unsafe {
67 if let Ok(home) = std::env::var("HOME") {
69 let stack = push(stack, Value::String(global_string(home)));
70 return push(stack, Value::Bool(true));
71 }
72
73 #[cfg(windows)]
75 if let Ok(home) = std::env::var("USERPROFILE") {
76 let stack = push(stack, Value::String(global_string(home)));
77 return push(stack, Value::Bool(true));
78 }
79
80 let stack = push(stack, Value::String(global_string(String::new())));
82 push(stack, Value::Bool(false))
83 }
84}
85
86#[unsafe(no_mangle)]
95pub unsafe extern "C" fn patch_seq_current_dir(stack: Stack) -> Stack {
96 unsafe {
97 match std::env::current_dir() {
98 Ok(path) => {
99 let path_str = path.to_string_lossy().into_owned();
100 let stack = push(stack, Value::String(global_string(path_str)));
101 push(stack, Value::Bool(true)) }
103 Err(_) => {
104 let stack = push(stack, Value::String(global_string(String::new())));
105 push(stack, Value::Bool(false)) }
107 }
108 }
109}
110
111#[unsafe(no_mangle)]
120pub unsafe extern "C" fn patch_seq_path_exists(stack: Stack) -> Stack {
121 unsafe {
122 let (stack, path_val) = pop(stack);
123 let path = match path_val {
124 Value::String(s) => s,
125 _ => panic!(
126 "path-exists: expected String (path) on stack, got {:?}",
127 path_val
128 ),
129 };
130
131 let exists = std::path::Path::new(path_str(&path)).exists();
132 push(stack, Value::Bool(exists))
133 }
134}
135
136#[unsafe(no_mangle)]
145pub unsafe extern "C" fn patch_seq_path_is_file(stack: Stack) -> Stack {
146 unsafe {
147 let (stack, path_val) = pop(stack);
148 let path = match path_val {
149 Value::String(s) => s,
150 _ => panic!(
151 "path-is-file: expected String (path) on stack, got {:?}",
152 path_val
153 ),
154 };
155
156 let is_file = std::path::Path::new(path_str(&path)).is_file();
157 push(stack, Value::Bool(is_file))
158 }
159}
160
161#[unsafe(no_mangle)]
170pub unsafe extern "C" fn patch_seq_path_is_dir(stack: Stack) -> Stack {
171 unsafe {
172 let (stack, path_val) = pop(stack);
173 let path = match path_val {
174 Value::String(s) => s,
175 _ => panic!(
176 "path-is-dir: expected String (path) on stack, got {:?}",
177 path_val
178 ),
179 };
180
181 let is_dir = std::path::Path::new(path_str(&path)).is_dir();
182 push(stack, Value::Bool(is_dir))
183 }
184}
185
186#[unsafe(no_mangle)]
195pub unsafe extern "C" fn patch_seq_path_join(stack: Stack) -> Stack {
196 unsafe {
197 let (stack, component_val) = pop(stack);
198 let (stack, base_val) = pop(stack);
199
200 let base = match base_val {
201 Value::String(s) => s,
202 _ => panic!(
203 "path-join: expected String (base) on stack, got {:?}",
204 base_val
205 ),
206 };
207
208 let component = match component_val {
209 Value::String(s) => s,
210 _ => panic!(
211 "path-join: expected String (component) on stack, got {:?}",
212 component_val
213 ),
214 };
215
216 let joined = std::path::Path::new(path_str(&base))
217 .join(path_str(&component))
218 .to_string_lossy()
219 .into_owned();
220
221 push(stack, Value::String(global_string(joined)))
222 }
223}
224
225#[unsafe(no_mangle)]
234pub unsafe extern "C" fn patch_seq_path_parent(stack: Stack) -> Stack {
235 unsafe {
236 let (stack, path_val) = pop(stack);
237 let path = match path_val {
238 Value::String(s) => s,
239 _ => panic!(
240 "path-parent: expected String (path) on stack, got {:?}",
241 path_val
242 ),
243 };
244
245 match std::path::Path::new(path_str(&path)).parent() {
246 Some(parent) => {
247 let parent_str = parent.to_string_lossy().into_owned();
248 let stack = push(stack, Value::String(global_string(parent_str)));
249 push(stack, Value::Bool(true)) }
251 None => {
252 let stack = push(stack, Value::String(global_string(String::new())));
253 push(stack, Value::Bool(false)) }
255 }
256 }
257}
258
259#[unsafe(no_mangle)]
268pub unsafe extern "C" fn patch_seq_path_filename(stack: Stack) -> Stack {
269 unsafe {
270 let (stack, path_val) = pop(stack);
271 let path = match path_val {
272 Value::String(s) => s,
273 _ => panic!(
274 "path-filename: expected String (path) on stack, got {:?}",
275 path_val
276 ),
277 };
278
279 match std::path::Path::new(path_str(&path)).file_name() {
280 Some(filename) => {
281 let filename_str = filename.to_string_lossy().into_owned();
282 let stack = push(stack, Value::String(global_string(filename_str)));
283 push(stack, Value::Bool(true)) }
285 None => {
286 let stack = push(stack, Value::String(global_string(String::new())));
287 push(stack, Value::Bool(false)) }
289 }
290 }
291}
292
293const EXIT_CODE_MIN: i64 = 0;
295const EXIT_CODE_MAX: i64 = 255;
296
297#[unsafe(no_mangle)]
309pub unsafe extern "C" fn patch_seq_exit(stack: Stack) -> Stack {
310 unsafe {
311 let (_stack, code_val) = pop(stack);
312 let code = match code_val {
313 Value::Int(n) => {
314 if !(EXIT_CODE_MIN..=EXIT_CODE_MAX).contains(&n) {
315 panic!(
316 "os.exit: exit code must be in range {}-{}, got {}",
317 EXIT_CODE_MIN, EXIT_CODE_MAX, n
318 );
319 }
320 n as i32
321 }
322 _ => panic!(
323 "os.exit: expected Int (exit code) on stack, got {:?}",
324 code_val
325 ),
326 };
327
328 std::process::exit(code);
329 }
330}
331
332#[unsafe(no_mangle)]
342pub unsafe extern "C" fn patch_seq_os_name(stack: Stack) -> Stack {
343 let name = if cfg!(target_os = "macos") {
344 "darwin"
345 } else if cfg!(target_os = "linux") {
346 "linux"
347 } else if cfg!(target_os = "windows") {
348 "windows"
349 } else if cfg!(target_os = "freebsd") {
350 "freebsd"
351 } else if cfg!(target_os = "openbsd") {
352 "openbsd"
353 } else if cfg!(target_os = "netbsd") {
354 "netbsd"
355 } else {
356 "unknown"
357 };
358
359 unsafe { push(stack, Value::String(global_string(name.to_owned()))) }
360}
361
362#[unsafe(no_mangle)]
372pub unsafe extern "C" fn patch_seq_os_arch(stack: Stack) -> Stack {
373 let arch = if cfg!(target_arch = "x86_64") {
374 "x86_64"
375 } else if cfg!(target_arch = "aarch64") {
376 "aarch64"
377 } else if cfg!(target_arch = "arm") {
378 "arm"
379 } else if cfg!(target_arch = "x86") {
380 "x86"
381 } else if cfg!(target_arch = "riscv64") {
382 "riscv64"
383 } else {
384 "unknown"
385 };
386
387 unsafe { push(stack, Value::String(global_string(arch.to_owned()))) }
388}
389
390#[cfg(test)]
391mod tests;