const std = @import("std"); const Allocator = std.mem.Allocator; const print = std.debug.print; const syntax = @import("../syntax.zig"); const FileSpec = syntax.FileSpec; const types = @import("./lib/types.zig"); const CommandStatus = types.CommandStatus; const CommandContext = types.CommandContext; pub const Copy = struct { from: FileSpec, to: FileSpec, pub fn eval(copy: Copy, ctx: CommandContext) !CommandStatus { // Handle source file const source_path = switch (copy.from) { .Con => { // COPY CON - copy from console to file const dest_path = switch (copy.to) { .Con => { const error_msg = "Cannot copy from CON to CON\n"; if (ctx.output_capture) |capture| { try capture.write(error_msg); } else { print("{s}", .{error_msg}); } return CommandStatus{ .Code = 1 }; }, .Lpt1, .Lpt2, .Lpt3, .Prn => { const error_msg = "Cannot copy to device\n"; if (ctx.output_capture) |capture| { try capture.write(error_msg); } else { print("{s}", .{error_msg}); } return CommandStatus{ .Code = 1 }; }, .Path => |path| path, }; // Create the destination file const dest_file = std.fs.cwd().createFile(dest_path, .{}) catch |err| { const error_msg = switch (err) { error.AccessDenied => "Access denied\n", error.PathAlreadyExists => "File already exists - use different name\n", else => "Cannot create file\n", }; if (ctx.output_capture) |capture| { try capture.write(error_msg); } else { print("{s}", .{error_msg}); } return CommandStatus{ .Code = 1 }; }; defer dest_file.close(); // Read from stdin and write to file until EOF (Ctrl+Z on DOS) const stdin = std.io.getStdIn().reader(); var line_count: u32 = 0; // Skip output redirection since we're doing interactive input if (ctx.output_capture == null) { // In interactive mode, show no prompt (DOS behavior) } 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 break; } } const msg = try std.fmt.allocPrint(ctx.allocator, " 1 File(s) copied\n", .{}); defer ctx.allocator.free(msg); if (ctx.output_capture) |capture| { try capture.write(msg); } else { print("{s}", .{msg}); } return CommandStatus{ .Code = 0 }; }, .Lpt1, .Lpt2, .Lpt3, .Prn => { const error_msg = "Cannot copy from device\n"; if (ctx.output_capture) |capture| { try capture.write(error_msg); } else { print("{s}", .{error_msg}); } return CommandStatus{ .Code = 1 }; }, .Path => |path| path, }; // Handle destination const dest_path = switch (copy.to) { .Con => { // Copy to console (display file contents) const source_file = std.fs.cwd().openFile(source_path, .{}) catch |err| { const error_msg = switch (err) { error.FileNotFound => "File not found\n", error.AccessDenied => "Access denied\n", else => "Cannot access source file\n", }; if (ctx.output_capture) |capture| { try capture.write(error_msg); } else { print("{s}", .{error_msg}); } return CommandStatus{ .Code = 1 }; }; defer source_file.close(); // Read and display file contents var buffer: [4096]u8 = undefined; while (true) { const bytes_read = source_file.readAll(&buffer) catch { const error_msg = "Error reading file\n"; if (ctx.output_capture) |capture| { try capture.write(error_msg); } else { print("{s}", .{error_msg}); } return CommandStatus{ .Code = 1 }; }; if (bytes_read == 0) break; if (ctx.output_capture) |capture| { try capture.write(buffer[0..bytes_read]); } else { print("{s}", .{buffer[0..bytes_read]}); } if (bytes_read < buffer.len) break; } const msg = " 1 File(s) copied\n"; if (ctx.output_capture) |capture| { try capture.write(msg); } else { print("{s}", .{msg}); } return CommandStatus{ .Code = 0 }; }, .Lpt1, .Lpt2, .Lpt3, .Prn => { const error_msg = "Cannot copy to device\n"; if (ctx.output_capture) |capture| { try capture.write(error_msg); } else { print("{s}", .{error_msg}); } return CommandStatus{ .Code = 1 }; }, .Path => |path| path, }; // Regular file-to-file copy std.fs.cwd().copyFile(source_path, std.fs.cwd(), dest_path, .{}) catch |err| { const error_msg = switch (err) { error.FileNotFound => "File not found\n", error.AccessDenied => "Access denied\n", error.PathAlreadyExists => "File already exists\n", else => "Cannot copy file\n", }; if (ctx.output_capture) |capture| { try capture.write(error_msg); } else { print("{s}", .{error_msg}); } return CommandStatus{ .Code = 1 }; }; const msg = " 1 File(s) copied\n"; if (ctx.output_capture) |capture| { try capture.write(msg); } else { print("{s}", .{msg}); } return CommandStatus{ .Code = 0 }; } };