summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMatthias Andreas Benkard <code@mail.matthias.benkard.de>2025-08-14 16:55:23 +0200
committerMatthias Andreas Benkard <code@mail.matthias.benkard.de>2025-08-14 16:55:23 +0200
commit63f7353b630c3eb5a6c60118418923b4b358e670 (patch)
tree09a66bbb081d2cffcbb3dfc477e6293b1d1a7693 /src
parent6141cdd85c5f01a001a1dc795226cd085a561383 (diff)
Factor out redirect handling.
Diffstat (limited to 'src')
-rw-r--r--src/cmd.zig6
-rw-r--r--src/cmd/redirect.zig180
-rw-r--r--src/eval.zig154
3 files changed, 184 insertions, 156 deletions
diff --git a/src/cmd.zig b/src/cmd.zig
index 25c0711..6fec627 100644
--- a/src/cmd.zig
+++ b/src/cmd.zig
@@ -27,6 +27,7 @@ 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;
+const RedirectCommand = @import("cmd/redirect.zig").RedirectCommand;
pub const BuiltinCommand = union(enum) {
// File-oriented
@@ -152,10 +153,7 @@ pub const Command = union(enum) {
left: *Command,
right: *Command,
},
- Redirect: struct {
- command: *Command,
- redirects: ArrayList(Redirect),
- },
+ Redirect: RedirectCommand,
External: External,
Builtin: BuiltinCommand,
Empty,
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;
+ }
+};
diff --git a/src/eval.zig b/src/eval.zig
index 1568411..9399c75 100644
--- a/src/eval.zig
+++ b/src/eval.zig
@@ -38,7 +38,7 @@ pub fn executeCommand(command: Command, allocator: Allocator) !CommandStatus {
return executeCommandWithOutput(command, allocator, null, null);
}
-fn executeCommandWithOutput(command: Command, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus {
+pub fn executeCommandWithOutput(command: Command, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus {
switch (command) {
.Empty => return CommandStatus{ .Code = 0 },
@@ -125,157 +125,7 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
},
.Redirect => |redirect| {
- // 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 null, 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;
+ return redirect.eval(allocator, output_capture, input_source, executeCommandWithOutput);
},
else => {