summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Andreas Benkard <code@mail.matthias.benkard.de>2025-08-13 19:18:50 +0200
committerMatthias Andreas Benkard <code@mail.matthias.benkard.de>2025-08-13 19:18:50 +0200
commit354dec925d6089133d1a0632ecdab4bd96d6c8ef (patch)
tree571b186000c9514d00a2f449da28ef498943489f
parent5f4c1846d7964f70716f2fdf05d4f9ea6e7d74c1 (diff)
Add support for external commands.
-rw-r--r--src/main.zig121
1 files changed, 115 insertions, 6 deletions
diff --git a/src/main.zig b/src/main.zig
index 51ebf3c..ef537ea 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -967,14 +967,123 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
},
.External => |external| {
- const error_msg = try std.fmt.allocPrint(allocator, "'{s}' is not recognized as an internal or external command,\noperable program or batch file.\n", .{external.program});
- defer allocator.free(error_msg);
+ // Try to execute external command
+ var child_args = ArrayList([]const u8).init(allocator);
+ defer child_args.deinit();
+
+ try child_args.append(external.program);
+ for (external.args.items) |arg| {
+ try child_args.append(arg);
+ }
+
+ var child = std.process.Child.init(child_args.items, allocator);
+
+ // Set up pipes for capturing output
+ child.stdin_behavior = if (input_source != null) .Pipe else .Inherit;
+ child.stdout_behavior = if (output_capture != null) .Pipe else .Inherit;
+ child.stderr_behavior = if (output_capture != null) .Pipe else .Inherit;
+
+ const spawn_result = child.spawn();
+ if (spawn_result) |_| {
+ // Spawn succeeded, continue with execution
+ } else |err| switch (err) {
+ error.FileNotFound => {
+ const error_msg = try std.fmt.allocPrint(allocator, "'{s}' is not recognized as an internal or external command,\noperable program or batch file.\n", .{external.program});
+ defer allocator.free(error_msg);
+
+ if (output_capture) |capture| {
+ try capture.write(error_msg);
+ } else {
+ print("{s}", .{error_msg});
+ }
+ return CommandStatus{ .Code = 1 };
+ },
+ error.AccessDenied => {
+ const error_msg = "Access is denied.\n";
+ if (output_capture) |capture| {
+ try capture.write(error_msg);
+ } else {
+ print("{s}", .{error_msg});
+ }
+ return CommandStatus{ .Code = 1 };
+ },
+ else => {
+ const error_msg = try std.fmt.allocPrint(allocator, "Cannot execute '{s}': {}\n", .{ external.program, err });
+ defer allocator.free(error_msg);
+
+ if (output_capture) |capture| {
+ try capture.write(error_msg);
+ } else {
+ print("{s}", .{error_msg});
+ }
+ return CommandStatus{ .Code = 1 };
+ }
+ }
+
+ // Handle input redirection
+ if (input_source) |source| {
+ if (child.stdin) |stdin| {
+ const writer = stdin.writer();
+
+ // Reset source position for reading
+ var temp_source = source.*;
+ temp_source.position = 0;
+
+ while (try temp_source.readLine(allocator)) |line| {
+ defer allocator.free(line);
+ try writer.print("{s}\n", .{line});
+ }
+ child.stdin.?.close();
+ child.stdin = null;
+ }
+ }
+
+ // Handle output capture
if (output_capture) |capture| {
- try capture.write(error_msg);
- } else {
- print("{s}", .{error_msg});
+ // Read stdout
+ if (child.stdout) |stdout| {
+ var buffer: [4096]u8 = undefined;
+ while (true) {
+ const bytes_read = stdout.read(&buffer) catch break;
+ if (bytes_read == 0) break;
+ try capture.write(buffer[0..bytes_read]);
+ }
+ }
+
+ // Read stderr
+ if (child.stderr) |stderr| {
+ var buffer: [4096]u8 = undefined;
+ while (true) {
+ const bytes_read = stderr.read(&buffer) catch break;
+ if (bytes_read == 0) break;
+ try capture.write(buffer[0..bytes_read]);
+ }
+ }
+ }
+
+ // Wait for process to complete
+ const term = child.wait() catch |err| {
+ const error_msg = switch (err) {
+ error.FileNotFound => try std.fmt.allocPrint(allocator, "'{s}' is not recognized as an internal or external command,\noperable program or batch file.\n", .{external.program}),
+ else => try std.fmt.allocPrint(allocator, "Error waiting for command: {}\n", .{err}),
+ };
+ defer allocator.free(error_msg);
+
+ if (output_capture) |capture| {
+ try capture.write(error_msg);
+ } else {
+ print("{s}", .{error_msg});
+ }
+ return CommandStatus{ .Code = 1 };
+ };
+
+ // Return exit code
+ switch (term) {
+ .Exited => |code| return CommandStatus{ .Code = @intCast(code) },
+ .Signal => |_| return CommandStatus{ .Code = 1 },
+ .Stopped => |_| return CommandStatus{ .Code = 1 },
+ .Unknown => |_| return CommandStatus{ .Code = 1 },
}
- return CommandStatus{ .Code = 1 };
},
.Redirect => |redirect| {