diff options
author | Matthias Andreas Benkard <code@mail.matthias.benkard.de> | 2025-08-13 19:18:50 +0200 |
---|---|---|
committer | Matthias Andreas Benkard <code@mail.matthias.benkard.de> | 2025-08-13 19:18:50 +0200 |
commit | 354dec925d6089133d1a0632ecdab4bd96d6c8ef (patch) | |
tree | 571b186000c9514d00a2f449da28ef498943489f | |
parent | 5f4c1846d7964f70716f2fdf05d4f9ea6e7d74c1 (diff) |
Add support for external commands.
-rw-r--r-- | src/main.zig | 121 |
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| { |