summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Andreas Benkard <code@mail.matthias.benkard.de>2025-08-16 13:02:47 +0200
committerMatthias Andreas Benkard <code@mail.matthias.benkard.de>2025-08-16 13:02:47 +0200
commit3e53d3ae08d41b21d9edcd0f9bbd9e0edbdfb138 (patch)
tree613aafbc1bfd848d739a60452199fbed28e877b6
parent9edc1338b92d1ead4c5f2fad8c0516037963f7b6 (diff)
Unify input handling.
-rw-r--r--src/cmd/copy.zig38
-rw-r--r--src/cmd/external.zig6
-rw-r--r--src/cmd/lib/types.zig59
-rw-r--r--src/cmd/path.zig1
-rw-r--r--src/cmd/sort.zig30
-rw-r--r--src/eval.zig10
6 files changed, 99 insertions, 45 deletions
diff --git a/src/cmd/copy.zig b/src/cmd/copy.zig
index 1c3c3ba..733dc47 100644
--- a/src/cmd/copy.zig
+++ b/src/cmd/copy.zig
@@ -46,36 +46,20 @@ pub const Copy = struct {
};
defer dest_file.close();
- // Read from stdin and write to file until EOF (Ctrl+Z on DOS)
- const stdin = std.io.getStdIn().reader();
+ // Read from input source and write to file until EOF (Ctrl+Z on DOS)
var line_count: u32 = 0;
+ var reader = ctx.input_reader;
while (true) {
- if (stdin.readUntilDelimiterOrEofAlloc(ctx.allocator, '\n', 4096)) |maybe_line| {
- if (maybe_line) |line| {
- defer ctx.allocator.free(line);
-
- // Check for Ctrl+Z (EOF marker)
- if (line.len == 1 and line[0] == 26) { // ASCII 26 = Ctrl+Z
- break;
- }
-
- // Remove trailing \r if present (Windows line endings)
- const clean_line = if (line.len > 0 and line[line.len - 1] == '\r')
- line[0 .. line.len - 1]
- else
- line;
-
- // Write line to file with DOS line ending
- try dest_file.writeAll(clean_line);
- try dest_file.writeAll("\r\n");
- line_count += 1;
- } else {
- // EOF reached
- break;
- }
- } else |_| {
- // Error reading input
+ if (try reader.readLine(ctx.allocator)) |line| {
+ defer ctx.allocator.free(line);
+
+ // Write line to file with DOS line ending
+ try dest_file.writeAll(line);
+ try dest_file.writeAll("\r\n");
+ line_count += 1;
+ } else {
+ // EOF reached
break;
}
}
diff --git a/src/cmd/external.zig b/src/cmd/external.zig
index 0f4e65f..3aaa7af 100644
--- a/src/cmd/external.zig
+++ b/src/cmd/external.zig
@@ -14,7 +14,11 @@ pub const External = struct {
pub fn eval(external: External, ctx: CommandContext) !CommandStatus {
const allocator = ctx.allocator;
- const input_source = ctx.input_source;
+ // Extract input_source from the input_reader for external command logic
+ const input_source = switch (ctx.input_reader) {
+ .source => |source| source,
+ .stdin => null,
+ };
// Check if we need to capture output (not going to stdout)
const needs_capture = switch (ctx.output_writer) {
diff --git a/src/cmd/lib/types.zig b/src/cmd/lib/types.zig
index f663a64..f6a32c0 100644
--- a/src/cmd/lib/types.zig
+++ b/src/cmd/lib/types.zig
@@ -72,6 +72,59 @@ pub const InputSource = struct {
}
};
+pub const StdinInputReader = struct {
+ pub fn readLine(self: *StdinInputReader, allocator: Allocator) !?[]const u8 {
+ _ = self;
+ const stdin = std.io.getStdIn().reader();
+
+ // Read line from stdin with proper DOS handling
+ const line = stdin.readUntilDelimiterOrEofAlloc(allocator, '\n', 4096) catch |err| switch (err) {
+ error.StreamTooLong => {
+ // Line too long, skip to next line
+ while (true) {
+ const ch = stdin.readByte() catch return null;
+ if (ch == '\n') break;
+ }
+ return try allocator.dupe(u8, "");
+ },
+ else => return null,
+ };
+
+ if (line) |input| {
+ // Check for Ctrl+Z (EOF marker) - ASCII 26 or ^Z text
+ if ((input.len == 1 and input[0] == 26) or
+ (input.len == 2 and input[0] == '^' and input[1] == 'Z'))
+ {
+ allocator.free(input);
+ return null; // EOF
+ }
+
+ // Remove trailing \r if present (DOS line endings)
+ if (input.len > 0 and input[input.len - 1] == '\r') {
+ const trimmed = try allocator.dupe(u8, input[0 .. input.len - 1]);
+ allocator.free(input);
+ return trimmed;
+ } else {
+ return input;
+ }
+ } else {
+ return null; // EOF
+ }
+ }
+};
+
+pub const InputReader = union(enum) {
+ source: *InputSource,
+ stdin: *StdinInputReader,
+
+ pub fn readLine(self: *InputReader, allocator: Allocator) !?[]const u8 {
+ switch (self.*) {
+ .source => |source| return source.readLine(allocator),
+ .stdin => |stdin| return stdin.readLine(allocator),
+ }
+ }
+};
+
pub const OutputWriter = union(enum) {
capture: *OutputCapture,
stdout: *StdoutOutputCapture,
@@ -89,14 +142,14 @@ pub const ExecuteCommandFn = *const fn (Command, Allocator, ?*OutputCapture, ?*I
pub const CommandContext = struct {
allocator: Allocator,
output_writer: OutputWriter,
- input_source: ?*InputSource,
+ input_reader: InputReader,
execute_command: ExecuteCommandFn,
- pub fn init(allocator: Allocator, output_writer: OutputWriter, input_source: ?*InputSource, execute_command: ExecuteCommandFn) CommandContext {
+ pub fn init(allocator: Allocator, output_writer: OutputWriter, input_reader: InputReader, execute_command: ExecuteCommandFn) CommandContext {
return CommandContext{
.allocator = allocator,
.output_writer = output_writer,
- .input_source = input_source,
+ .input_reader = input_reader,
.execute_command = execute_command,
};
}
diff --git a/src/cmd/path.zig b/src/cmd/path.zig
index 009621f..e0c549f 100644
--- a/src/cmd/path.zig
+++ b/src/cmd/path.zig
@@ -8,7 +8,6 @@ const CommandContext = types.CommandContext;
pub const PathGet = struct {
pub fn eval(path_get: PathGet, ctx: CommandContext) !CommandStatus {
_ = path_get;
- _ = ctx.input_source;
const current_path = std.process.getEnvVarOwned(ctx.allocator, "PATH") catch |err| switch (err) {
error.EnvironmentVariableNotFound => {
diff --git a/src/cmd/sort.zig b/src/cmd/sort.zig
index 0f73940..79616e9 100644
--- a/src/cmd/sort.zig
+++ b/src/cmd/sort.zig
@@ -18,18 +18,24 @@ pub const Sort = struct {
lines.deinit();
}
- // Read input lines
- if (ctx.input_source) |source| {
- // Read from input redirection
- while (try source.readLine(ctx.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";
- var writer = ctx.output_writer;
- try writer.write(msg);
- return CommandStatus{ .Code = 0 };
+ // Read input lines from unified input reader
+ var reader = ctx.input_reader;
+
+ // Check if we're reading from stdin vs file redirection for DOS-authentic behavior
+ switch (ctx.input_reader) {
+ .stdin => {
+ // Reading from interactive stdin - show usage message (DOS behavior)
+ const msg = "SORT: Use input redirection (< file.txt) to sort file contents\n";
+ var writer = ctx.output_writer;
+ try writer.write(msg);
+ return CommandStatus{ .Code = 0 };
+ },
+ .source => {
+ // Read from input redirection
+ while (try reader.readLine(ctx.allocator)) |line| {
+ try lines.append(line);
+ }
+ },
}
// Sort the lines
diff --git a/src/eval.zig b/src/eval.zig
index 3381302..1b9858e 100644
--- a/src/eval.zig
+++ b/src/eval.zig
@@ -28,6 +28,8 @@ const OutputCapture = cmdTypes.OutputCapture;
const StdoutOutputCapture = cmdTypes.StdoutOutputCapture;
const OutputWriter = cmdTypes.OutputWriter;
const InputSource = cmdTypes.InputSource;
+const StdinInputReader = cmdTypes.StdinInputReader;
+const InputReader = cmdTypes.InputReader;
const CommandContext = cmdTypes.CommandContext;
const STDOUT_BUFFER_SIZE: usize = 1024;
@@ -44,7 +46,13 @@ pub fn executeCommandWithOutput(command: Command, allocator: Allocator, output_c
else
OutputWriter{ .stdout = &stdout_capture };
- const ctx = CommandContext.init(allocator, output_writer, input_source, executeCommandWithOutput);
+ var stdin_reader = StdinInputReader{};
+ const input_reader = if (input_source) |source|
+ InputReader{ .source = source }
+ else
+ InputReader{ .stdin = &stdin_reader };
+
+ const ctx = CommandContext.init(allocator, output_writer, input_reader, executeCommandWithOutput);
switch (command) {
.Empty => return CommandStatus{ .Code = 0 },