Skip to content

Commit 2d6ee44

Browse files
committed
new features to window, process and environment library
1 parent cfe48ea commit 2d6ee44

10 files changed

Lines changed: 681 additions & 28 deletions

File tree

lang/libs/environment/src/environment.ch

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ using std::Option;
77
using std::Result;
88
using std::string;
99
using std::string_view;
10+
using std::vector;
11+
12+
// POSIX: the environ variable (declared in env_os.ch on Windows)
13+
comptime if(!def.windows) {
14+
@extern var environ : **char
15+
}
1016

1117
// ---------------------------------------------------------------------------
1218
// Environment variable access
@@ -107,12 +113,49 @@ public func user_name() : Option<string> {
107113
}
108114
}
109115

110-
/// Get the current working directory (PWD on POSIX).
116+
/// Get the current working directory.
111117
public func current_dir() : Option<string> {
112-
comptime if(!def.windows) {
118+
comptime if(def.windows) {
119+
unsafe var buf : [1024]char
120+
var len = GetCurrentDirectoryA(1024, &raw mut buf[0])
121+
if(len == 0 || len >= 1024) {
122+
return Option.None<string>()
123+
}
124+
return Option.Some(string.make_no_len(&raw mut buf[0]))
125+
} else {
113126
return get("PWD");
114127
}
115-
return Option.None<string>();
128+
}
129+
130+
/// Enumerate all environment variables.
131+
/// Returns a vector of "KEY=VALUE" strings.
132+
public func all() : vector<string> {
133+
var result = vector<string>()
134+
comptime if(def.windows) {
135+
var env_ptr = GetEnvironmentStringsA()
136+
if(env_ptr == null) { return result }
137+
var p = env_ptr as *char
138+
while(true) {
139+
// Each entry is null-terminated; the block ends with a double null
140+
if(p[0] as char == '\0' as char && p[1] as char == '\0' as char) { break }
141+
var entry = string.make_no_len(p)
142+
// Advance past this entry
143+
var k : size_t = 0
144+
while(p[k] != 0) { k += 1 }
145+
p = (p as *u8 + k + 1) as *char
146+
result.push(entry)
147+
}
148+
FreeEnvironmentStringsA(env_ptr)
149+
} else {
150+
var ep = environ
151+
if(ep == null) { return result }
152+
var i : int = 0
153+
while(ep[i] != null) {
154+
result.push(string.make_no_len(ep[i]))
155+
i += 1
156+
}
157+
}
158+
return result
116159
}
117160

118161
/// Get the temporary directory path.

lang/libs/environment/win/env_os.ch

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,10 @@ public namespace environment {
88
@extern
99
func GetEnvironmentVariableA(name : *char, buf : *mut char, size : u32) : u32
1010

11+
@extern
12+
func GetEnvironmentStringsA() : *u8
13+
14+
@extern
15+
func FreeEnvironmentStringsA(pEnvBlock : *u8) : int
16+
1117
} // end namespace environment

lang/libs/process/chemical.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ source "posix" if !windows
66

77
import cstd
88
import std
9+
import environment

lang/libs/process/posix/posix.ch

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,25 @@ public func posix_execute(cfg : *ProcessConfig, out : *mut ProcessResult) : bool
5656
dup2(stderr_pipe[1], 2)
5757
close(stderr_pipe[1])
5858
} else {}
59+
// stdin_data: create a temporary pipe in the child, write data, then dup2 to fd 0.
60+
// This is done in the child so the parent doesn't need a separate pipe.
61+
if(cfg.stdin_data.size() > 0) {
62+
unsafe var stdin_pipe_tmp : [2]int
63+
pipe(&raw mut stdin_pipe_tmp[0])
64+
// Write data to the pipe write end in the child, then close it.
65+
var written = write(stdin_pipe_tmp[1], cfg.stdin_data.data() as *void, cfg.stdin_data.size())
66+
close(stdin_pipe_tmp[1])
67+
dup2(stdin_pipe_tmp[0], 0)
68+
close(stdin_pipe_tmp[0])
69+
} else {}
5970
var argv = build_argv(&raw mut cfg.args);
60-
execvp(argv.ptrs[0], &raw argv.ptrs[0]);
71+
// If env vars are provided, use execve() with custom environment.
72+
if(cfg.env.size() > 0) {
73+
var envp = build_envp(&raw mut cfg.env);
74+
execve(argv.ptrs[0], &raw argv.ptrs[0], &raw envp.ptrs[0]);
75+
} else {
76+
execvp(argv.ptrs[0], &raw argv.ptrs[0]);
77+
}
6178
_exit(1);
6279
} else {}
6380

@@ -94,7 +111,28 @@ public func posix_execute(cfg : *ProcessConfig, out : *mut ProcessResult) : bool
94111
} else {}
95112

96113
var status : int = 0;
97-
waitpid(pid, &raw mut status, 0);
114+
var timed_out = false
115+
if(cfg.timeout_ms > 0) {
116+
// Poll with timeout: sleep in 10ms increments
117+
var elapsed : int = 0
118+
while(elapsed < cfg.timeout_ms) {
119+
var ret = waitpid(pid, &raw mut status, 1) // WNOHANG
120+
if(ret != 0) { break }
121+
usleep(10000) // 10ms
122+
elapsed += 10
123+
}
124+
if(elapsed >= cfg.timeout_ms) {
125+
// Check one more time
126+
var ret = waitpid(pid, &raw mut status, 1)
127+
if(ret == 0) {
128+
timed_out = true
129+
kill(pid, 9) // SIGKILL
130+
waitpid(pid, &raw mut status, 0)
131+
}
132+
}
133+
} else {
134+
waitpid(pid, &raw mut status, 0)
135+
}
98136

99137
var exit_code : int = 0;
100138
var signaled : bool = false;
@@ -108,7 +146,7 @@ public func posix_execute(cfg : *ProcessConfig, out : *mut ProcessResult) : bool
108146
out.status.code = exit_code;
109147
out.status.signaled = signaled;
110148
out.status.signal = signal_no;
111-
out.success = (exit_code == 0 && !signaled);
149+
out.success = (exit_code == 0 && !signaled && !timed_out);
112150
return true
113151
}
114152

@@ -151,7 +189,12 @@ public func posix_spawn(cfg : *ProcessConfig, child : *mut ChildProcess) : bool
151189
// Child reads from stdin_pipe[0]; parent writes to stdin_pipe[1].
152190
close(stdin_pipe[1]); dup2(stdin_pipe[0], 0); close(stdin_pipe[0]);
153191
var argv = build_argv(&raw mut cfg.args);
154-
execvp(argv.ptrs[0], &raw argv.ptrs[0]);
192+
if(cfg.env.size() > 0) {
193+
var envp = build_envp(&raw mut cfg.env);
194+
execve(argv.ptrs[0], &raw argv.ptrs[0], &raw envp.ptrs[0]);
195+
} else {
196+
execvp(argv.ptrs[0], &raw argv.ptrs[0]);
197+
}
155198
_exit(1);
156199
} else {}
157200

@@ -230,6 +273,18 @@ func build_argv(args : *vector<string>) : ArgvBuffer {
230273
return buf;
231274
}
232275

276+
func build_envp(env : *vector<string>) : ArgvBuffer {
277+
unsafe var buf : ArgvBuffer;
278+
buf.count = env.size();
279+
var i : size_t = 0;
280+
while(i < env.size()) {
281+
buf.ptrs[i] = env.get_ptr(i).data()
282+
i += 1;
283+
}
284+
buf.ptrs[i] = null;
285+
return buf;
286+
}
287+
233288
func read_all_fd(fd : int, data : *mut vector<u8>) : bool {
234289
unsafe var buf : [4096]u8;
235290
while(true) {
@@ -252,6 +307,7 @@ func read_all_fd(fd : int, data : *mut vector<u8>) : bool {
252307
@extern public func fork() : int
253308
@extern public func dup2(oldfd : int, newfd : int) : int
254309
@extern public func execvp(file : *char, argv : **char) : int
310+
@extern public func execve(file : *char, argv : **char, envp : **char) : int
255311
@extern public func waitpid(pid : int, status : *mut int, options : int) : int
256312
@extern public func _exit(status : int)
257313
@extern public func close(fd : int) : int
@@ -260,6 +316,7 @@ func read_all_fd(fd : int, data : *mut vector<u8>) : bool {
260316
@extern public func getpid() : int
261317
@extern public func usleep(usec : int) : int
262318
@extern public func chdir(path : *char) : int
319+
@extern public func kill(pid : int, sig : int) : int
263320

264321
const _WIFEXITED_MASK = 0x7f;
265322
func WIFEXITED(status : int) : bool { return (status & _WIFEXITED_MASK) == 0; }

lang/libs/process/src/process.ch

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ public struct ProcessConfig {
5353
var capture_stderr : bool;
5454
var merge_stdout_stderr : bool;
5555
var stdin_data : vector<u8>;
56+
var timeout_ms : int;
5657

5758
func default() : ProcessConfig {
5859
return ProcessConfig {
@@ -62,7 +63,8 @@ public struct ProcessConfig {
6263
capture_stdout = true,
6364
capture_stderr = true,
6465
merge_stdout_stderr = false,
65-
stdin_data = vector<u8>()
66+
stdin_data = vector<u8>(),
67+
timeout_ms = 0
6668
}
6769
}
6870
}

0 commit comments

Comments
 (0)