diff options
Diffstat (limited to 'src/cmd')
-rw-r--r-- | src/cmd/copy.zig | 208 | ||||
-rw-r--r-- | src/cmd/types.zig | 63 |
2 files changed, 271 insertions, 0 deletions
diff --git a/src/cmd/copy.zig b/src/cmd/copy.zig new file mode 100644 index 0000000..44eeb87 --- /dev/null +++ b/src/cmd/copy.zig @@ -0,0 +1,208 @@ +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("./types.zig"); +const CommandStatus = types.CommandStatus; +const OutputCapture = types.OutputCapture; +const InputSource = types.InputSource; + +pub const Copy = struct { + from: FileSpec, + to: FileSpec, + + pub fn eval(copy: Copy, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus { + _ = input_source; + + // Handle source file + const source_path = switch (copy.from) { + .Con => { + // COPY CON <file> - copy from console to file + const dest_path = switch (copy.to) { + .Con => { + const error_msg = "Cannot copy from CON to CON\n"; + if (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 (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 (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 (output_capture == null) { + // In interactive mode, show no prompt (DOS behavior) + } + + while (true) { + if (stdin.readUntilDelimiterOrEofAlloc(allocator, '\n', 4096)) |maybe_line| { + if (maybe_line) |line| { + defer 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(allocator, " 1 File(s) copied\n", .{}); + defer allocator.free(msg); + if (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 (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 (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 (output_capture) |capture| { + try capture.write(error_msg); + } else { + print("{s}", .{error_msg}); + } + return CommandStatus{ .Code = 1 }; + }; + if (bytes_read == 0) break; + + if (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 (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 (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 (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 (output_capture) |capture| { + try capture.write(msg); + } else { + print("{s}", .{msg}); + } + return CommandStatus{ .Code = 0 }; + } +}; diff --git a/src/cmd/types.zig b/src/cmd/types.zig new file mode 100644 index 0000000..1f0e162 --- /dev/null +++ b/src/cmd/types.zig @@ -0,0 +1,63 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const ArrayList = std.ArrayList; + +pub const CommandStatus = union(enum) { + Code: u16, + ExitShell, +}; + +pub const OutputCapture = struct { + buffer: ArrayList(u8), + + pub fn init(allocator: Allocator) OutputCapture { + return OutputCapture{ + .buffer = ArrayList(u8).init(allocator), + }; + } + + pub fn deinit(self: *OutputCapture) void { + self.buffer.deinit(); + } + + pub fn write(self: *OutputCapture, data: []const u8) !void { + try self.buffer.appendSlice(data); + } + + pub fn getContents(self: *const OutputCapture) []const u8 { + return self.buffer.items; + } +}; + +pub 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); + } + } +}; |