summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Andreas Benkard <code@mail.matthias.benkard.de>2025-08-13 19:14:55 +0200
committerMatthias Andreas Benkard <code@mail.matthias.benkard.de>2025-08-13 19:14:55 +0200
commit5f4c1846d7964f70716f2fdf05d4f9ea6e7d74c1 (patch)
treef9792d0f97acb3eadd93703d377551e8dc1fcfc7
parentc362799fb932b8530c5d949399bf93904b24faf8 (diff)
Add input redirection.
-rw-r--r--src/main.zig143
1 files changed, 136 insertions, 7 deletions
diff --git a/src/main.zig b/src/main.zig
index 4cfc581..51ebf3c 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -513,6 +513,8 @@ const Parser = struct {
}
const file_spec = parseFilespec(args.items[0]);
return Command{ .Builtin = BuiltinCommand{ .Type = .{ .file = file_spec } } };
+ } else if (std.mem.eql(u8, cmd_upper, "SORT")) {
+ return Command{ .Builtin = BuiltinCommand.Sort };
} else {
// External command
return Command{ .External = .{ .program = command_name, .args = args } };
@@ -610,7 +612,40 @@ const OutputCapture = struct {
}
};
-fn executeCommandWithOutput(command: Command, allocator: Allocator, output_capture: ?*OutputCapture) !CommandStatus {
+const InputSource = struct {
+ data: []const u8,
+ position: usize,
+
+ pub fn init(data: []const u8) InputSource {
+ return InputSource{
+ .data = data,
+ .position = 0,
+ };
+ }
+
+ pub fn readLine(self: *InputSource, allocator: Allocator) !?[]const u8 {
+ if (self.position >= self.data.len) {
+ return null; // EOF
+ }
+
+ var line_end = self.position;
+ while (line_end < self.data.len and self.data[line_end] != '\n') {
+ line_end += 1;
+ }
+
+ const line = self.data[self.position..line_end];
+ self.position = if (line_end < self.data.len) line_end + 1 else self.data.len;
+
+ // Remove trailing \r if present (DOS line endings)
+ if (line.len > 0 and line[line.len - 1] == '\r') {
+ return try allocator.dupe(u8, line[0..line.len - 1]);
+ } else {
+ return try allocator.dupe(u8, line);
+ }
+ }
+};
+
+fn executeCommandWithOutput(command: Command, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus {
switch (command) {
.Empty => return CommandStatus{ .Code = 0 },
@@ -869,6 +904,55 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
return CommandStatus{ .Code = 0 };
},
+ .Sort => {
+ var lines = ArrayList([]const u8).init(allocator);
+ defer {
+ for (lines.items) |line| {
+ allocator.free(line);
+ }
+ lines.deinit();
+ }
+
+ // Read input lines
+ if (input_source) |source| {
+ // Read from input redirection
+ while (try source.readLine(allocator)) |line| {
+ try lines.append(line);
+ }
+ } else {
+ // Read from stdin (simplified - just show message)
+ const msg = "SORT: Use input redirection (< file.txt) to sort file contents\n";
+ if (output_capture) |capture| {
+ try capture.write(msg);
+ } else {
+ print("{s}", .{msg});
+ }
+ return CommandStatus{ .Code = 0 };
+ }
+
+ // Sort the lines
+ std.mem.sort([]const u8, lines.items, {}, struct {
+ fn lessThan(_: void, lhs: []const u8, rhs: []const u8) bool {
+ return std.mem.order(u8, lhs, rhs) == .lt;
+ }
+ }.lessThan);
+
+ // Output sorted lines
+ var output_buffer = ArrayList(u8).init(allocator);
+ defer output_buffer.deinit();
+
+ for (lines.items) |line| {
+ try output_buffer.writer().print("{s}\n", .{line});
+ }
+
+ if (output_capture) |capture| {
+ try capture.write(output_buffer.items);
+ } else {
+ print("{s}", .{output_buffer.items});
+ }
+
+ return CommandStatus{ .Code = 0 };
+ },
else => {
const error_msg = try std.fmt.allocPrint(allocator, "Command not implemented: {any}\n", .{builtin_cmd});
defer allocator.free(error_msg);
@@ -897,11 +981,56 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
var captured_output = OutputCapture.init(allocator);
defer captured_output.deinit();
- // Execute the command with output capture
- const status = try executeCommandWithOutput(redirect.command.*, allocator, &captured_output);
+ // 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);
- // Handle redirections
+ // 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
+ const status = try executeCommandWithOutput(redirect.command.*, allocator, &captured_output, 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
@@ -984,8 +1113,8 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
};
},
.InputFrom => {
- print("Input redirection not implemented\n", .{});
- return CommandStatus{ .Code = 1 };
+ // Input redirection already handled above
+ continue;
},
}
}
@@ -1006,7 +1135,7 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
}
fn executeCommand(command: Command, allocator: Allocator) !CommandStatus {
- return executeCommandWithOutput(command, allocator, null);
+ return executeCommandWithOutput(command, allocator, null, null);
}
fn readLine(allocator: Allocator, prompt_text: []const u8) !?[]const u8 {