diff options
-rw-r--r-- | src/cmd/copy.zig | 38 | ||||
-rw-r--r-- | src/cmd/external.zig | 6 | ||||
-rw-r--r-- | src/cmd/lib/types.zig | 59 | ||||
-rw-r--r-- | src/cmd/path.zig | 1 | ||||
-rw-r--r-- | src/cmd/sort.zig | 30 | ||||
-rw-r--r-- | src/eval.zig | 10 |
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 }, |