summaryrefslogtreecommitdiff
path: root/src/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd')
-rw-r--r--src/cmd/redirect.zig180
1 files changed, 180 insertions, 0 deletions
diff --git a/src/cmd/redirect.zig b/src/cmd/redirect.zig
new file mode 100644
index 0000000..dbdeafe
--- /dev/null
+++ b/src/cmd/redirect.zig
@@ -0,0 +1,180 @@
+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;
+
+const syntax = @import("../syntax.zig");
+const RedirectType = syntax.RedirectType;
+const Redirect = syntax.Redirect;
+
+const cmd = @import("../cmd.zig");
+const Command = cmd.Command;
+
+// Function type for executing commands with output capture
+const ExecuteCommandFn = *const fn (Command, Allocator, ?*OutputCapture, ?*InputSource) anyerror!CommandStatus;
+
+pub const RedirectCommand = struct {
+ command: *Command,
+ redirects: ArrayList(Redirect),
+
+ pub fn eval(redirect: RedirectCommand, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource, executeCommandWithOutput: ExecuteCommandFn) !CommandStatus {
+ _ = input_source; // Redirect handles its own input source
+
+ // Check if we have any output redirections
+ var has_output_redirect = false;
+ for (redirect.redirects.items) |redir| {
+ if (redir.redirect_type == .OutputOverwrite or redir.redirect_type == .OutputAppend) {
+ has_output_redirect = true;
+ break;
+ }
+ }
+
+ var captured_output = OutputCapture.init(allocator);
+ defer captured_output.deinit();
+
+ // Prepare input redirection if needed
+ var input_data: ?[]const u8 = null;
+ var redirect_input_source: ?InputSource = null;
+ defer if (input_data) |data| allocator.free(data);
+
+ // Process input redirections first
+ for (redirect.redirects.items) |redir| {
+ if (redir.redirect_type == .InputFrom) {
+ const file_path = switch (redir.target) {
+ .Con => {
+ print("Input redirection from CON not supported\n", .{});
+ return CommandStatus{ .Code = 1 };
+ },
+ .Lpt1, .Lpt2, .Lpt3, .Prn => {
+ print("Cannot redirect input from device\n", .{});
+ return CommandStatus{ .Code = 1 };
+ },
+ .Path => |path| path,
+ };
+
+ // Read input file
+ const file = std.fs.cwd().openFile(file_path, .{}) catch |err| {
+ switch (err) {
+ error.FileNotFound => print("The system cannot find the file specified.\n", .{}),
+ error.AccessDenied => print("Access is denied.\n", .{}),
+ else => print("Cannot open input file.\n", .{}),
+ }
+ return CommandStatus{ .Code = 1 };
+ };
+ defer file.close();
+
+ input_data = file.readToEndAlloc(allocator, std.math.maxInt(usize)) catch |err| {
+ switch (err) {
+ error.AccessDenied => print("Access is denied.\n", .{}),
+ else => print("Cannot read input file.\n", .{}),
+ }
+ return CommandStatus{ .Code = 1 };
+ };
+
+ redirect_input_source = InputSource.init(input_data.?);
+ break; // Only handle first input redirection
+ }
+ }
+
+ // Execute the command with input and output capture (only capture output if needed)
+ const status = try executeCommandWithOutput(redirect.command.*, allocator, if (has_output_redirect) &captured_output else output_capture, if (redirect_input_source) |*source| source else null);
+
+ // Handle output redirections
+ for (redirect.redirects.items) |redir| {
+ if (redir.redirect_type == .InputFrom) continue; // Already handled
+ const file_path = switch (redir.target) {
+ .Con => {
+ // Redirect to console - just print normally
+ print("{s}", .{captured_output.getContents()});
+ continue;
+ },
+ .Lpt1, .Lpt2, .Lpt3, .Prn => {
+ print("Cannot redirect to device\n", .{});
+ return CommandStatus{ .Code = 1 };
+ },
+ .Path => |path| path,
+ };
+
+ // Handle different redirect types
+ switch (redir.redirect_type) {
+ .OutputOverwrite => {
+ // Write to file, overwriting existing content
+ const file = std.fs.cwd().createFile(file_path, .{}) catch |err| {
+ switch (err) {
+ error.AccessDenied => print("Access is denied.\n", .{}),
+ else => print("Cannot create file.\n", .{}),
+ }
+ return CommandStatus{ .Code = 1 };
+ };
+ defer file.close();
+
+ file.writeAll(captured_output.getContents()) catch |err| {
+ switch (err) {
+ error.AccessDenied => print("Access is denied.\n", .{}),
+ else => print("Cannot write to file.\n", .{}),
+ }
+ return CommandStatus{ .Code = 1 };
+ };
+ },
+ .OutputAppend => {
+ // Append to file
+ const file = std.fs.cwd().openFile(file_path, .{ .mode = .write_only }) catch |err| {
+ switch (err) {
+ error.FileNotFound => {
+ // Create new file if it doesn't exist
+ const new_file = std.fs.cwd().createFile(file_path, .{}) catch |create_err| {
+ switch (create_err) {
+ error.AccessDenied => print("Access is denied.\n", .{}),
+ else => print("Cannot create file.\n", .{}),
+ }
+ return CommandStatus{ .Code = 1 };
+ };
+ defer new_file.close();
+
+ new_file.writeAll(captured_output.getContents()) catch {
+ print("Cannot write to file.\n", .{});
+ return CommandStatus{ .Code = 1 };
+ };
+ continue;
+ },
+ error.AccessDenied => {
+ print("Access is denied.\n", .{});
+ return CommandStatus{ .Code = 1 };
+ },
+ else => {
+ print("Cannot open file.\n", .{});
+ return CommandStatus{ .Code = 1 };
+ },
+ }
+ };
+ defer file.close();
+
+ // Seek to end for append
+ file.seekFromEnd(0) catch {
+ print("Cannot seek to end of file.\n", .{});
+ return CommandStatus{ .Code = 1 };
+ };
+
+ file.writeAll(captured_output.getContents()) catch |err| {
+ switch (err) {
+ error.AccessDenied => print("Access is denied.\n", .{}),
+ else => print("Cannot write to file.\n", .{}),
+ }
+ return CommandStatus{ .Code = 1 };
+ };
+ },
+ .InputFrom => {
+ // Input redirection already handled above
+ continue;
+ },
+ }
+ }
+
+ return status;
+ }
+};