summaryrefslogtreecommitdiff
path: root/src/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd')
-rw-r--r--src/cmd/copy.zig208
-rw-r--r--src/cmd/types.zig63
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);
+ }
+ }
+};