summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian>2026-01-30 00:34:33 +0000
committerZuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian>2026-01-30 00:34:33 +0000
commitb27b128f97596236a4ce6a3d9b40ef3dfad84d06 (patch)
tree00d17aeee6f84fb3641d0b84e1931f9a88649cac
parentee090168fd7f678e40150b3699e335625b90d947 (diff)
New standard lib (std/process.zc).
-rw-r--r--README.md1
-rw-r--r--README_ES.md1
-rw-r--r--README_ZH_CN.md1
-rw-r--r--README_ZH_TW.md1
-rw-r--r--docs/std/README.md1
-rw-r--r--docs/std/process.md57
-rw-r--r--examples/process/exec.zc20
-rw-r--r--std.zc1
-rw-r--r--std/process.zc133
-rw-r--r--tests/std/test_process.zc26
10 files changed, 242 insertions, 0 deletions
diff --git a/README.md b/README.md
index 2745aba..75c4624 100644
--- a/README.md
+++ b/README.md
@@ -1106,6 +1106,7 @@ Zen C includes a standard library (`std`) covering essential functionality.
| **`std/json.zc`** | JSON parsing and serialization. | [Docs](docs/std/json.md) |
| **`std/stack.zc`** | LIFO Stack `Stack<T>`. | [Docs](docs/std/stack.md) |
| **`std/set.zc`** | Generic Hash Set `Set<T>`. | [Docs](docs/std/set.md) |
+| **`std/process.zc`** | Process execution and management. | [Docs](docs/std/process.md) |
---
diff --git a/README_ES.md b/README_ES.md
index 2735497..c5655e3 100644
--- a/README_ES.md
+++ b/README_ES.md
@@ -1106,6 +1106,7 @@ Zen C incluye una biblioteca estándar (`std`) que cubre las funcionalidades ese
| **`std/json.zc`** | Parseo y serialización de JSON. | [Docs](docs/std/json.md) |
| **`std/stack.zc`** | Pila LIFO `Stack<T>`. | [Docs](docs/std/stack.md) |
| **`std/set.zc`** | Conjunto Hash Genérico `Set<T>`. | [Docs](docs/std/set.md) |
+| **`std/process.zc`** | Ejecución y gestión de procesos. | [Docs](docs/std/process.md) |
---
diff --git a/README_ZH_CN.md b/README_ZH_CN.md
index 52acb04..6fce2d2 100644
--- a/README_ZH_CN.md
+++ b/README_ZH_CN.md
@@ -1106,6 +1106,7 @@ Zen C 包含一个涵盖基本功能的标准库 (`std`)。
| **`std/json.zc`** | JSON 解析与序列化。 | [文档](docs/std/json.md) |
| **`std/stack.zc`** | 后进先出栈 `Stack<T>`。 | [文档](docs/std/stack.md) |
| **`std/set.zc`** | 泛型哈希集合 `Set<T>`。 | [文档](docs/std/set.md) |
+| **`std/process.zc`** | 进程执行与管理。 | [文档](docs/std/process.md) |
---
diff --git a/README_ZH_TW.md b/README_ZH_TW.md
index b9b5511..fc9cef5 100644
--- a/README_ZH_TW.md
+++ b/README_ZH_TW.md
@@ -1106,6 +1106,7 @@ Zen C 包含一個涵蓋基本功能的標準庫 (`std`)。
| **`std/json.zc`** | JSON 解析與序列化。 | [文檔](docs/std/json.md) |
| **`std/stack.zc`** | 後進先出棧 `Stack<T>`。 | [文檔](docs/std/stack.md) |
| **`std/set.zc`** | 泛型哈希集合 `Set<T>`。 | [文檔](docs/std/set.md) |
+| **`std/process.zc`** | 進程執行與管理。 | [文檔](docs/std/process.md) |
---
diff --git a/docs/std/README.md b/docs/std/README.md
index 16ffc74..3cbf8f8 100644
--- a/docs/std/README.md
+++ b/docs/std/README.md
@@ -8,6 +8,7 @@
- [Networking (Net)](./net.md) - TCP networking.
- [Option](./option.md) - Optional values (Some/None).
- [Path](./path.md) - File path manipulation.
+- [Process](./process.md) - Process execution and management.
- [Result](./result.md) - Error handling (Ok/Err).
- [Queue](./queue.md) - FIFO queue (Ring Buffer).
- [Set](./set.md) - Hash set implementation.
diff --git a/docs/std/process.md b/docs/std/process.md
new file mode 100644
index 0000000..31485ee
--- /dev/null
+++ b/docs/std/process.md
@@ -0,0 +1,57 @@
+# Standard Library: Process (`std/process.zc`)
+
+The process module allows you to spawn and interact with child processes.
+
+## Usage
+
+```zc
+import "std/process.zc"
+
+fn main() {
+ let output = Command::new("echo")
+ .arg("hello")
+ .output();
+
+ if (output.exit_code == 0) {
+ output.stdout.print();
+ // Or access raw C string: output.stdout.c_str()
+ }
+}
+```
+
+## Structs
+
+### Command
+
+A builder for spawning a process.
+
+```zc
+struct Command {
+ program: String;
+ args: Vec<String>;
+}
+```
+
+#### Methods
+
+| Method | Signature | Description |
+| :--- | :--- | :--- |
+| **new** | `Command::new(program: char*) -> Command` | Creates a new Command for the given program. |
+| **arg** | `arg(self, arg: char*) -> Command*` | Adds an argument to the command. Returns the command pointer for chaining. |
+| **output** | `output(self) -> Output` | Executes the command as a child process, waiting for it to finish and collecting all of its stdout. |
+| **status** | `status(self) -> int` | Executes the command as a child process and returns the exit status code. Does not capture output (output goes to stdout/stderr). |
+
+### Output
+
+The output of a finished process.
+
+```zc
+struct Output {
+ stdout: String;
+ exit_code: int;
+}
+```
+
+#### Methods
+
+`Output` implements `Drop` to automatically free the captured `stdout` string.
diff --git a/examples/process/exec.zc b/examples/process/exec.zc
new file mode 100644
index 0000000..712a356
--- /dev/null
+++ b/examples/process/exec.zc
@@ -0,0 +1,20 @@
+
+import "std/process.zc";
+import "std/string.zc";
+
+fn main() {
+ "Executing 'ls -la' using std/process...";
+
+ let output = Command::new("ls")
+ .arg("-l")
+ .arg("-a")
+ .output();
+
+ if output.exit_code == 0 {
+ "--- Output ---";
+ output.stdout.print();
+ "--------------";
+ } else {
+ !"Command failed with exit code: {output.exit_code}";
+ }
+}
diff --git a/std.zc b/std.zc
index 03bcd45..4793c11 100644
--- a/std.zc
+++ b/std.zc
@@ -18,4 +18,5 @@ import "./std/stack.zc"
import "./std/queue.zc"
import "./std/env.zc"
import "./std/slice.zc"
+import "./std/process.zc"
diff --git a/std/process.zc b/std/process.zc
new file mode 100644
index 0000000..d0b09a9
--- /dev/null
+++ b/std/process.zc
@@ -0,0 +1,133 @@
+
+import "./core.zc";
+import "./vec.zc";
+import "./mem.zc";
+import "./string.zc";
+import "./option.zc";
+
+raw {
+ void *_z_popen(char *command, char *type) {
+ return (void *)popen(command, type);
+ }
+
+ int _z_pclose(void *stream) {
+ return pclose((FILE *)stream);
+ }
+
+ char *_z_fgets(char *s, int size, void *stream) {
+ return fgets(s, size, (FILE *)stream);
+ }
+
+ int _z_system(char *command) {
+ return system(command);
+ }
+}
+
+extern fn _z_popen(command: char*, type: char*) -> void*;
+extern fn _z_pclose(stream: void*) -> int;
+extern fn _z_fgets(s: char*, size: int, stream: void*) -> char*;
+extern fn _z_system(command: char*) -> int;
+
+struct Output {
+ stdout: String;
+ exit_code: int;
+}
+
+struct Command {
+ program: String;
+ args: Vec<String>;
+}
+
+impl Command {
+ fn new(program: char*) -> Command {
+ return Command {
+ program: String::from(program),
+ args: Vec<String>::new()
+ };
+ }
+
+ fn arg(self, arg: char*) -> Command* {
+ self.args.push(String::from(arg));
+ return self;
+ }
+
+ fn _build_cmd(self) -> String {
+ let cmd_str = self.program.substring(0, self.program.length());
+
+ for arg in &self.args {
+ let space = String::from(" ");
+ cmd_str.append(&space);
+ space.free();
+
+ cmd_str.append(arg);
+ }
+
+ return cmd_str;
+ }
+
+ fn output(self) -> Output {
+ let cmd_str = self._build_cmd();
+ let cmd_c = cmd_str.c_str();
+
+ let fp = _z_popen(cmd_c, "r");
+
+ if (fp == 0) {
+ cmd_str.free();
+ // TODO: Better error handling...
+ return Output {
+ stdout: String::from(""),
+ exit_code: -1
+ };
+ }
+
+ let out = String::from("");
+ let buf_size: usize = 1024;
+ let buf = (char*)malloc(buf_size);
+
+ while (true) {
+ let res = _z_fgets(buf, (int)buf_size, fp);
+ if (res == 0) break;
+
+ let chunk = String::from(buf);
+ out.append(&chunk);
+ chunk.free();
+ }
+
+ let code = _z_pclose(fp);
+ free(buf);
+ cmd_str.free();
+
+ return Output {
+ stdout: out,
+ exit_code: code
+ };
+ }
+
+ fn status(self) -> int {
+ let cmd_str = self._build_cmd();
+ let code = _z_system(cmd_str.c_str());
+ cmd_str.free();
+ return code;
+ }
+
+ fn free(self) {
+ self.program.free();
+
+ for s in &self.args {
+ s.free();
+ }
+ self.args.free();
+ }
+}
+
+impl Drop for Command {
+ fn drop(self) {
+ self.free();
+ }
+}
+
+impl Drop for Output {
+ fn drop(self) {
+ self.stdout.free();
+ }
+}
diff --git a/tests/std/test_process.zc b/tests/std/test_process.zc
new file mode 100644
index 0000000..a3ba6ce
--- /dev/null
+++ b/tests/std/test_process.zc
@@ -0,0 +1,26 @@
+
+import "std/process.zc";
+import "std/string.zc";
+
+test "process output" {
+ let cmd = Command::new("echo");
+ cmd.arg("hello");
+
+ let out = cmd.output();
+
+ assert(out.exit_code == 0);
+ // echo usually outputs newline
+ assert(out.stdout.contains('h'));
+ assert(out.stdout.contains('e'));
+ assert(out.stdout.contains('l'));
+ assert(out.stdout.contains('o'));
+
+ // out is dropped automatically
+ // cmd is dropped automatically
+}
+
+test "process status" {
+ let cmd = Command::new("true"); // true command returns 0
+ let status = cmd.status();
+ assert(status == 0);
+}