diff options
author | Matthias Andreas Benkard <code@mail.matthias.benkard.de> | 2025-08-14 16:51:17 +0200 |
---|---|---|
committer | Matthias Andreas Benkard <code@mail.matthias.benkard.de> | 2025-08-14 16:51:17 +0200 |
commit | 6141cdd85c5f01a001a1dc795226cd085a561383 (patch) | |
tree | 13496bd6e1400d1d829fa2c91594c77d39a3a769 /src | |
parent | fa2069cc7e80d491cf19bb7ad9e11d97356882b9 (diff) |
Factor out the external command case.
Diffstat (limited to 'src')
-rw-r--r-- | src/cmd.zig | 6 | ||||
-rw-r--r-- | src/cmd/external.zig | 134 | ||||
-rw-r--r-- | src/eval.zig | 118 |
3 files changed, 137 insertions, 121 deletions
diff --git a/src/cmd.zig b/src/cmd.zig index 8f4c971..25c0711 100644 --- a/src/cmd.zig +++ b/src/cmd.zig @@ -26,6 +26,7 @@ const Date = @import("cmd/date.zig").Date; const Time = @import("cmd/time.zig").Time; const Sort = @import("cmd/sort.zig").Sort; const Move = @import("cmd/move.zig").Move; +const External = @import("cmd/external.zig").External; pub const BuiltinCommand = union(enum) { // File-oriented @@ -155,10 +156,7 @@ pub const Command = union(enum) { command: *Command, redirects: ArrayList(Redirect), }, - External: struct { - program: []const u8, - args: ArrayList([]const u8), - }, + External: External, Builtin: BuiltinCommand, Empty, diff --git a/src/cmd/external.zig b/src/cmd/external.zig new file mode 100644 index 0000000..92bfba6 --- /dev/null +++ b/src/cmd/external.zig @@ -0,0 +1,134 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const ArrayList = std.ArrayList; +const print = std.debug.print; + +const types = @import("./lib/types.zig"); +const CommandStatus = types.CommandStatus; +const OutputCapture = types.OutputCapture; +const InputSource = types.InputSource; + +pub const External = struct { + program: []const u8, + args: ArrayList([]const u8), + + pub fn eval(external: External, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus { + // 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| { + // 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 }, + } + } +}; diff --git a/src/eval.zig b/src/eval.zig index 8676516..1568411 100644 --- a/src/eval.zig +++ b/src/eval.zig @@ -121,123 +121,7 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu }, .External => |external| { - // 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| { - // 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 external.eval(allocator, output_capture, input_source); }, .Redirect => |redirect| { |