summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMatthias Andreas Benkard <code@mail.matthias.benkard.de>2025-08-14 17:50:42 +0200
committerMatthias Andreas Benkard <code@mail.matthias.benkard.de>2025-08-14 17:53:53 +0200
commit4dd206a5a3a32e23e05c0842ce4db7a108de4d5f (patch)
treeb83fb3daf371ed53739cefcde1db841df21ca5e4 /src
parent35a3b9ccfeb9dcd2c88c359108305b7f750622d6 (diff)
Refactor shared eval function parameters into a CommandContext struct.
Diffstat (limited to 'src')
-rw-r--r--src/cmd/chdir.zig31
-rw-r--r--src/cmd/cls.zig9
-rw-r--r--src/cmd/copy.zig41
-rw-r--r--src/cmd/date.zig12
-rw-r--r--src/cmd/dir.zig27
-rw-r--r--src/cmd/echo.zig31
-rw-r--r--src/cmd/external.zig6
-rw-r--r--src/cmd/lib/types.zig31
-rw-r--r--src/cmd/mkdir.zig11
-rw-r--r--src/cmd/move.zig11
-rw-r--r--src/cmd/path.zig31
-rw-r--r--src/cmd/pipe.zig8
-rw-r--r--src/cmd/redirect.zig10
-rw-r--r--src/cmd/remove.zig13
-rw-r--r--src/cmd/rename.zig15
-rw-r--r--src/cmd/rmdir.zig11
-rw-r--r--src/cmd/sort.zig19
-rw-r--r--src/cmd/time.zig12
-rw-r--r--src/cmd/type.zig19
-rw-r--r--src/cmd/ver.zig9
-rw-r--r--src/eval.zig57
21 files changed, 216 insertions, 198 deletions
diff --git a/src/cmd/chdir.zig b/src/cmd/chdir.zig
index 5c2c618..c9413ff 100644
--- a/src/cmd/chdir.zig
+++ b/src/cmd/chdir.zig
@@ -7,35 +7,34 @@ const formatDosPath = paths.formatDosPath;
const types = @import("./lib/types.zig");
const CommandStatus = types.CommandStatus;
-const OutputCapture = types.OutputCapture;
-const InputSource = types.InputSource;
+const CommandContext = types.CommandContext;
pub const Chdir = struct {
path: []const u8,
- pub fn eval(chdir: Chdir, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus {
- _ = input_source;
+ pub fn eval(chdir: Chdir, ctx: CommandContext) !CommandStatus {
+ _ = ctx.input_source;
if (chdir.path.len == 0) {
// No arguments - display current directory
- const cwd = std.fs.cwd().realpathAlloc(allocator, ".") catch {
+ const cwd = std.fs.cwd().realpathAlloc(ctx.allocator, ".") catch {
const error_msg = "Unable to determine current directory\n";
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(error_msg);
} else {
print("{s}", .{error_msg});
}
return CommandStatus{ .Code = 1 };
};
- defer allocator.free(cwd);
+ defer ctx.allocator.free(cwd);
- const formatted_path = try formatDosPath(allocator, cwd);
- defer allocator.free(formatted_path);
+ const formatted_path = try formatDosPath(ctx.allocator, cwd);
+ defer ctx.allocator.free(formatted_path);
- const output = try std.fmt.allocPrint(allocator, "{s}\n", .{formatted_path});
- defer allocator.free(output);
+ const output = try std.fmt.allocPrint(ctx.allocator, "{s}\n", .{formatted_path});
+ defer ctx.allocator.free(output);
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(output);
} else {
print("{s}", .{output});
@@ -50,7 +49,7 @@ pub const Chdir = struct {
// Go to parent directory
std.process.changeCurDir("..") catch {
const error_msg = "The system cannot find the path specified.\n";
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(error_msg);
} else {
print("{s}", .{error_msg});
@@ -61,7 +60,7 @@ pub const Chdir = struct {
// Go to root directory - simplified to just go to "/"
std.process.changeCurDir("/") catch {
const error_msg = "The system cannot find the path specified.\n";
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(error_msg);
} else {
print("{s}", .{error_msg});
@@ -74,7 +73,7 @@ pub const Chdir = struct {
for (target_path) |ch| {
if (ch == 0) {
const error_msg = "Invalid path: contains null character\n";
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(error_msg);
} else {
print("{s}", .{error_msg});
@@ -85,7 +84,7 @@ pub const Chdir = struct {
std.process.changeCurDir(target_path) catch {
const error_msg = "The system cannot find the path specified.\n";
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(error_msg);
} else {
print("{s}", .{error_msg});
diff --git a/src/cmd/cls.zig b/src/cmd/cls.zig
index 70c1fc4..4e7b8c6 100644
--- a/src/cmd/cls.zig
+++ b/src/cmd/cls.zig
@@ -4,16 +4,13 @@ const print = std.debug.print;
const types = @import("./lib/types.zig");
const CommandStatus = types.CommandStatus;
-const OutputCapture = types.OutputCapture;
-const InputSource = types.InputSource;
+const CommandContext = types.CommandContext;
pub const Cls = struct {
- pub fn eval(cls: Cls, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus {
+ pub fn eval(cls: Cls, ctx: CommandContext) !CommandStatus {
_ = cls;
- _ = allocator;
- _ = input_source;
- if (output_capture == null) {
+ if (ctx.output_capture == null) {
// Clear screen - only works when not redirected
print("\x1B[2J\x1B[H", .{});
}
diff --git a/src/cmd/copy.zig b/src/cmd/copy.zig
index c99560f..9f9d4bf 100644
--- a/src/cmd/copy.zig
+++ b/src/cmd/copy.zig
@@ -7,15 +7,14 @@ const FileSpec = syntax.FileSpec;
const types = @import("./lib/types.zig");
const CommandStatus = types.CommandStatus;
-const OutputCapture = types.OutputCapture;
-const InputSource = types.InputSource;
+const CommandContext = types.CommandContext;
pub const Copy = struct {
from: FileSpec,
to: FileSpec,
- pub fn eval(copy: Copy, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus {
- _ = input_source;
+ pub fn eval(copy: Copy, ctx: CommandContext) !CommandStatus {
+ _ = ctx.input_source;
// Handle source file
const source_path = switch (copy.from) {
@@ -24,7 +23,7 @@ pub const Copy = struct {
const dest_path = switch (copy.to) {
.Con => {
const error_msg = "Cannot copy from CON to CON\n";
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(error_msg);
} else {
print("{s}", .{error_msg});
@@ -33,7 +32,7 @@ pub const Copy = struct {
},
.Lpt1, .Lpt2, .Lpt3, .Prn => {
const error_msg = "Cannot copy to device\n";
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(error_msg);
} else {
print("{s}", .{error_msg});
@@ -50,7 +49,7 @@ pub const Copy = struct {
error.PathAlreadyExists => "File already exists - use different name\n",
else => "Cannot create file\n",
};
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(error_msg);
} else {
print("{s}", .{error_msg});
@@ -64,14 +63,14 @@ pub const Copy = struct {
var line_count: u32 = 0;
// Skip output redirection since we're doing interactive input
- if (output_capture == null) {
+ if (ctx.output_capture == null) {
// In interactive mode, show no prompt (DOS behavior)
}
while (true) {
- if (stdin.readUntilDelimiterOrEofAlloc(allocator, '\n', 4096)) |maybe_line| {
+ if (stdin.readUntilDelimiterOrEofAlloc(ctx.allocator, '\n', 4096)) |maybe_line| {
if (maybe_line) |line| {
- defer allocator.free(line);
+ defer ctx.allocator.free(line);
// Check for Ctrl+Z (EOF marker)
if (line.len == 1 and line[0] == 26) { // ASCII 26 = Ctrl+Z
@@ -98,9 +97,9 @@ pub const Copy = struct {
}
}
- const msg = try std.fmt.allocPrint(allocator, " 1 File(s) copied\n", .{});
- defer allocator.free(msg);
- if (output_capture) |capture| {
+ 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});
@@ -109,7 +108,7 @@ pub const Copy = struct {
},
.Lpt1, .Lpt2, .Lpt3, .Prn => {
const error_msg = "Cannot copy from device\n";
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(error_msg);
} else {
print("{s}", .{error_msg});
@@ -129,7 +128,7 @@ pub const Copy = struct {
error.AccessDenied => "Access denied\n",
else => "Cannot access source file\n",
};
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(error_msg);
} else {
print("{s}", .{error_msg});
@@ -143,7 +142,7 @@ pub const Copy = struct {
while (true) {
const bytes_read = source_file.readAll(&buffer) catch {
const error_msg = "Error reading file\n";
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(error_msg);
} else {
print("{s}", .{error_msg});
@@ -152,7 +151,7 @@ pub const Copy = struct {
};
if (bytes_read == 0) break;
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(buffer[0..bytes_read]);
} else {
print("{s}", .{buffer[0..bytes_read]});
@@ -162,7 +161,7 @@ pub const Copy = struct {
}
const msg = " 1 File(s) copied\n";
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(msg);
} else {
print("{s}", .{msg});
@@ -171,7 +170,7 @@ pub const Copy = struct {
},
.Lpt1, .Lpt2, .Lpt3, .Prn => {
const error_msg = "Cannot copy to device\n";
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(error_msg);
} else {
print("{s}", .{error_msg});
@@ -189,7 +188,7 @@ pub const Copy = struct {
error.PathAlreadyExists => "File already exists\n",
else => "Cannot copy file\n",
};
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(error_msg);
} else {
print("{s}", .{error_msg});
@@ -198,7 +197,7 @@ pub const Copy = struct {
};
const msg = " 1 File(s) copied\n";
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(msg);
} else {
print("{s}", .{msg});
diff --git a/src/cmd/date.zig b/src/cmd/date.zig
index a299667..3a7d72b 100644
--- a/src/cmd/date.zig
+++ b/src/cmd/date.zig
@@ -4,17 +4,15 @@ const print = std.debug.print;
const types = @import("./lib/types.zig");
const CommandStatus = types.CommandStatus;
-const OutputCapture = types.OutputCapture;
-const InputSource = types.InputSource;
+const CommandContext = types.CommandContext;
fn isLeapYear(year: u32) bool {
return (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0);
}
pub const Date = struct {
- pub fn eval(date: Date, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus {
+ pub fn eval(date: Date, ctx: CommandContext) !CommandStatus {
_ = date;
- _ = input_source;
const timestamp = std.time.timestamp();
const epoch_seconds = @as(u64, @intCast(timestamp));
@@ -56,9 +54,9 @@ pub const Date = struct {
const day = remaining_days + 1; // Days are 1-indexed
- const output = try std.fmt.allocPrint(allocator, "Current date is {d:0>2}/{d:0>2}/{d}\n", .{ month, day, year });
- defer allocator.free(output);
- if (output_capture) |capture| {
+ const output = try std.fmt.allocPrint(ctx.allocator, "Current date is {d:0>2}/{d:0>2}/{d}\n", .{ month, day, year });
+ defer ctx.allocator.free(output);
+ if (ctx.output_capture) |capture| {
try capture.write(output);
} else {
print("{s}", .{output});
diff --git a/src/cmd/dir.zig b/src/cmd/dir.zig
index a3b8279..5aff48b 100644
--- a/src/cmd/dir.zig
+++ b/src/cmd/dir.zig
@@ -17,6 +17,7 @@ const convertTo83 = paths.convertTo83;
const types = @import("./lib/types.zig");
const CommandStatus = types.CommandStatus;
+const CommandContext = types.CommandContext;
const OutputCapture = types.OutputCapture;
const InputSource = types.InputSource;
@@ -75,15 +76,13 @@ fn getFreeDiskSpace(path: []const u8) GetFreeDiskSpaceError!u64 {
pub const Dir = struct {
path: []const u8,
- pub fn eval(dir: Dir, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus {
- _ = input_source;
-
- var output_buffer = ArrayList(u8).init(allocator);
+ pub fn eval(dir: Dir, ctx: CommandContext) !CommandStatus {
+ var output_buffer = ArrayList(u8).init(ctx.allocator);
defer output_buffer.deinit();
// Format path in DOS style with backslashes and uppercase drive letter
- const formatted_path = try formatDosPath(allocator, dir.path);
- defer allocator.free(formatted_path);
+ const formatted_path = try formatDosPath(ctx.allocator, dir.path);
+ defer ctx.allocator.free(formatted_path);
// Get volume label (simplified - just show drive)
const drive_letter = if (formatted_path.len >= 2 and formatted_path[1] == ':')
@@ -96,7 +95,7 @@ pub const Dir = struct {
var dir_iterator = std.fs.cwd().openDir(dir.path, .{ .iterate = true }) catch {
const error_msg = "File not found\n";
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(error_msg);
} else {
print("{s}", .{error_msg});
@@ -115,12 +114,12 @@ pub const Dir = struct {
// Convert timestamp to DOS date/time format
const mtime_secs = @divFloor(stat.mtime, std.time.ns_per_s);
- const date_time = try formatDosDateTime(allocator, @intCast(mtime_secs));
- defer allocator.free(date_time);
+ const date_time = try formatDosDateTime(ctx.allocator, @intCast(mtime_secs));
+ defer ctx.allocator.free(date_time);
// Convert filename to 8.3 format
- const short_name = try convertTo83(allocator, entry.name);
- defer allocator.free(short_name);
+ const short_name = try convertTo83(ctx.allocator, entry.name);
+ defer ctx.allocator.free(short_name);
switch (entry.kind) {
.directory => {
@@ -137,8 +136,8 @@ pub const Dir = struct {
}
// Get free disk space using statvfs
- const path = try std.fs.cwd().realpathAlloc(allocator, dir.path);
- defer allocator.free(path);
+ const path = try std.fs.cwd().realpathAlloc(ctx.allocator, dir.path);
+ defer ctx.allocator.free(path);
const bytes_free = getFreeDiskSpace(path) catch |err| switch (err) {
error.AccessDenied => 0,
error.NotImplemented => 0,
@@ -147,7 +146,7 @@ pub const Dir = struct {
try output_buffer.writer().print(" {d} File(s) {d:>14} bytes\n", .{ file_count, total_file_bytes });
try output_buffer.writer().print(" {d} Dir(s) {d:>14} bytes free\n", .{ dir_count, bytes_free });
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(output_buffer.items);
} else {
print("{s}", .{output_buffer.items});
diff --git a/src/cmd/echo.zig b/src/cmd/echo.zig
index d3ef8b1..4f2731b 100644
--- a/src/cmd/echo.zig
+++ b/src/cmd/echo.zig
@@ -4,28 +4,23 @@ const print = std.debug.print;
const types = @import("./lib/types.zig");
const CommandStatus = types.CommandStatus;
-const OutputCapture = types.OutputCapture;
-const InputSource = types.InputSource;
+const CommandContext = types.CommandContext;
pub const EchoOff = struct {
- pub fn eval(echo_off: EchoOff, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus {
+ pub fn eval(echo_off: EchoOff, ctx: CommandContext) !CommandStatus {
_ = echo_off;
- _ = allocator;
- _ = output_capture;
- _ = input_source;
+ _ = ctx;
return CommandStatus{ .Code = 0 };
}
};
pub const EchoOn = struct {
- pub fn eval(echo_on: EchoOn, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus {
+ pub fn eval(echo_on: EchoOn, ctx: CommandContext) !CommandStatus {
_ = echo_on;
- _ = allocator;
- _ = input_source;
const output = "ECHO is on\n";
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(output);
} else {
print("{s}", .{output});
@@ -35,13 +30,11 @@ pub const EchoOn = struct {
};
pub const EchoPlain = struct {
- pub fn eval(echo_plain: EchoPlain, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus {
+ pub fn eval(echo_plain: EchoPlain, ctx: CommandContext) !CommandStatus {
_ = echo_plain;
- _ = allocator;
- _ = input_source;
const output = "ECHO is on\n";
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(output);
} else {
print("{s}", .{output});
@@ -53,12 +46,10 @@ pub const EchoPlain = struct {
pub const EchoText = struct {
message: []const u8,
- pub fn eval(echo_text: EchoText, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus {
- _ = input_source;
-
- const output = try std.fmt.allocPrint(allocator, "{s}\n", .{echo_text.message});
- defer allocator.free(output);
- if (output_capture) |capture| {
+ pub fn eval(echo_text: EchoText, ctx: CommandContext) !CommandStatus {
+ const output = try std.fmt.allocPrint(ctx.allocator, "{s}\n", .{echo_text.message});
+ defer ctx.allocator.free(output);
+ if (ctx.output_capture) |capture| {
try capture.write(output);
} else {
print("{s}", .{output});
diff --git a/src/cmd/external.zig b/src/cmd/external.zig
index 92bfba6..68fc758 100644
--- a/src/cmd/external.zig
+++ b/src/cmd/external.zig
@@ -5,6 +5,7 @@ const print = std.debug.print;
const types = @import("./lib/types.zig");
const CommandStatus = types.CommandStatus;
+const CommandContext = types.CommandContext;
const OutputCapture = types.OutputCapture;
const InputSource = types.InputSource;
@@ -12,7 +13,10 @@ pub const External = struct {
program: []const u8,
args: ArrayList([]const u8),
- pub fn eval(external: External, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus {
+ pub fn eval(external: External, ctx: CommandContext) !CommandStatus {
+ const allocator = ctx.allocator;
+ const output_capture = ctx.output_capture;
+ const input_source = ctx.input_source;
// Try to execute external command
var child_args = ArrayList([]const u8).init(allocator);
defer child_args.deinit();
diff --git a/src/cmd/lib/types.zig b/src/cmd/lib/types.zig
index 1f0e162..2d96295 100644
--- a/src/cmd/lib/types.zig
+++ b/src/cmd/lib/types.zig
@@ -61,3 +61,34 @@ pub const InputSource = struct {
}
}
};
+
+pub const CommandContext = struct {
+ allocator: Allocator,
+ output_capture: ?*OutputCapture,
+ input_source: ?*InputSource,
+
+ // The real type is:
+ //
+ // const ExecuteCommandFn = *const fn (Command, Allocator, ?*OutputCapture, ?*InputSource) anyerror!CommandStatus;
+ //
+ // But Command is defined in ../cmd.zig, so we can't write it.
+ execute_command: ?*const anyopaque, // Will be cast to the appropriate function type when used
+
+ pub fn init(allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) CommandContext {
+ return CommandContext{
+ .allocator = allocator,
+ .output_capture = output_capture,
+ .input_source = input_source,
+ .execute_command = null,
+ };
+ }
+
+ pub fn with_executor(allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource, execute_command: anytype) CommandContext {
+ return CommandContext{
+ .allocator = allocator,
+ .output_capture = output_capture,
+ .input_source = input_source,
+ .execute_command = @ptrCast(&execute_command),
+ };
+ }
+};
diff --git a/src/cmd/mkdir.zig b/src/cmd/mkdir.zig
index 7009fbc..59ed431 100644
--- a/src/cmd/mkdir.zig
+++ b/src/cmd/mkdir.zig
@@ -4,15 +4,14 @@ const print = std.debug.print;
const types = @import("./lib/types.zig");
const CommandStatus = types.CommandStatus;
-const OutputCapture = types.OutputCapture;
-const InputSource = types.InputSource;
+const CommandContext = types.CommandContext;
pub const Mkdir = struct {
path: []const u8,
- pub fn eval(mkdir: Mkdir, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus {
- _ = allocator;
- _ = input_source;
+ pub fn eval(mkdir: Mkdir, ctx: CommandContext) !CommandStatus {
+ _ = ctx.allocator;
+ _ = ctx.input_source;
const dir_path = mkdir.path;
@@ -24,7 +23,7 @@ pub const Mkdir = struct {
error.NotDir => "The system cannot find the path specified\n",
else => "Unable to create directory\n",
};
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(error_msg);
} else {
print("{s}", .{error_msg});
diff --git a/src/cmd/move.zig b/src/cmd/move.zig
index 9ae47b7..9787673 100644
--- a/src/cmd/move.zig
+++ b/src/cmd/move.zig
@@ -4,17 +4,16 @@ const print = std.debug.print;
const types = @import("./lib/types.zig");
const CommandStatus = types.CommandStatus;
-const OutputCapture = types.OutputCapture;
-const InputSource = types.InputSource;
+const CommandContext = types.CommandContext;
pub const Move = struct {
- pub fn eval(move: Move, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus {
+ pub fn eval(move: Move, ctx: CommandContext) !CommandStatus {
_ = move;
- _ = allocator;
- _ = input_source;
+ _ = ctx.allocator;
+ _ = ctx.input_source;
const error_msg = "MOVE command not yet implemented\n";
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(error_msg);
} else {
print("{s}", .{error_msg});
diff --git a/src/cmd/path.zig b/src/cmd/path.zig
index f8d5939..baf1c2e 100644
--- a/src/cmd/path.zig
+++ b/src/cmd/path.zig
@@ -4,19 +4,18 @@ const print = std.debug.print;
const types = @import("./lib/types.zig");
const CommandStatus = types.CommandStatus;
-const OutputCapture = types.OutputCapture;
-const InputSource = types.InputSource;
+const CommandContext = types.CommandContext;
pub const PathGet = struct {
- pub fn eval(path_get: PathGet, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus {
+ pub fn eval(path_get: PathGet, ctx: CommandContext) !CommandStatus {
_ = path_get;
- _ = input_source;
+ _ = ctx.input_source;
- const current_path = std.process.getEnvVarOwned(allocator, "PATH") catch |err| switch (err) {
+ const current_path = std.process.getEnvVarOwned(ctx.allocator, "PATH") catch |err| switch (err) {
error.EnvironmentVariableNotFound => {
// PATH not set, show empty
const output = "PATH=(not set)\n";
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(output);
} else {
print("{s}", .{output});
@@ -25,7 +24,7 @@ pub const PathGet = struct {
},
else => {
const error_msg = "Cannot access PATH environment variable\n";
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(error_msg);
} else {
print("{s}", .{error_msg});
@@ -33,11 +32,11 @@ pub const PathGet = struct {
return CommandStatus{ .Code = 1 };
},
};
- defer allocator.free(current_path);
+ defer ctx.allocator.free(current_path);
- const output = try std.fmt.allocPrint(allocator, "PATH={s}\n", .{current_path});
- defer allocator.free(output);
- if (output_capture) |capture| {
+ const output = try std.fmt.allocPrint(ctx.allocator, "PATH={s}\n", .{current_path});
+ defer ctx.allocator.free(output);
+ if (ctx.output_capture) |capture| {
try capture.write(output);
} else {
print("{s}", .{output});
@@ -49,15 +48,15 @@ pub const PathGet = struct {
pub const PathSet = struct {
value: []const u8,
- pub fn eval(path_set: PathSet, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus {
- _ = input_source;
+ pub fn eval(path_set: PathSet, ctx: CommandContext) !CommandStatus {
+ _ = ctx.input_source;
// Note: In a real DOS system, this would persist for the session
// Here we just show what would be set but don't actually set it
// since Zig's std.process doesn't provide a simple way to set env vars
- const output = try std.fmt.allocPrint(allocator, "PATH would be set to: {s}\n(Note: Environment variable setting not implemented in this shell)\n", .{path_set.value});
- defer allocator.free(output);
- if (output_capture) |capture| {
+ const output = try std.fmt.allocPrint(ctx.allocator, "PATH would be set to: {s}\n(Note: Environment variable setting not implemented in this shell)\n", .{path_set.value});
+ defer ctx.allocator.free(output);
+ if (ctx.output_capture) |capture| {
try capture.write(output);
} else {
print("{s}", .{output});
diff --git a/src/cmd/pipe.zig b/src/cmd/pipe.zig
index 4f079cb..f97561a 100644
--- a/src/cmd/pipe.zig
+++ b/src/cmd/pipe.zig
@@ -4,6 +4,7 @@ const print = std.debug.print;
const types = @import("./lib/types.zig");
const CommandStatus = types.CommandStatus;
+const CommandContext = types.CommandContext;
const OutputCapture = types.OutputCapture;
const InputSource = types.InputSource;
@@ -17,8 +18,11 @@ pub const PipeCommand = struct {
left: *Command,
right: *Command,
- pub fn eval(pipe: PipeCommand, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource, executeCommandWithOutput: ExecuteCommandFn) !CommandStatus {
- _ = input_source; // Pipe handles its own input flow
+ pub fn eval(pipe: PipeCommand, ctx: CommandContext) !CommandStatus {
+ // Cast the execute function back to its proper type
+ const executeCommandWithOutput: ExecuteCommandFn = @ptrCast(@alignCast(ctx.execute_command.?));
+ const allocator = ctx.allocator;
+ const output_capture = ctx.output_capture;
// Create output capture for the left command
var left_output = OutputCapture.init(allocator);
diff --git a/src/cmd/redirect.zig b/src/cmd/redirect.zig
index dbdeafe..4cba259 100644
--- a/src/cmd/redirect.zig
+++ b/src/cmd/redirect.zig
@@ -5,6 +5,7 @@ const print = std.debug.print;
const types = @import("./lib/types.zig");
const CommandStatus = types.CommandStatus;
+const CommandContext = types.CommandContext;
const OutputCapture = types.OutputCapture;
const InputSource = types.InputSource;
@@ -22,8 +23,13 @@ pub const RedirectCommand = struct {
command: *Command,
redirects: ArrayList(Redirect),
- pub fn eval(redirect: RedirectCommand, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource, executeCommandWithOutput: ExecuteCommandFn) !CommandStatus {
- _ = input_source; // Redirect handles its own input source
+ pub fn eval(redirect: RedirectCommand, ctx: CommandContext) !CommandStatus {
+ _ = ctx.input_source; // Redirect handles its own input source
+
+ // Cast the execute function back to its proper type
+ const executeCommandWithOutput: ExecuteCommandFn = @ptrCast(@alignCast(ctx.execute_command.?));
+ const allocator = ctx.allocator;
+ const output_capture = ctx.output_capture;
// Check if we have any output redirections
var has_output_redirect = false;
diff --git a/src/cmd/remove.zig b/src/cmd/remove.zig
index 7b85f1d..e7d2688 100644
--- a/src/cmd/remove.zig
+++ b/src/cmd/remove.zig
@@ -4,15 +4,14 @@ const print = std.debug.print;
const types = @import("./lib/types.zig");
const CommandStatus = types.CommandStatus;
-const OutputCapture = types.OutputCapture;
-const InputSource = types.InputSource;
+const CommandContext = types.CommandContext;
pub const Remove = struct {
path: []const u8,
- pub fn eval(remove: Remove, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus {
- _ = allocator;
- _ = input_source;
+ pub fn eval(remove: Remove, ctx: CommandContext) !CommandStatus {
+ _ = ctx.allocator;
+ _ = ctx.input_source;
const file_path = remove.path;
@@ -20,7 +19,7 @@ pub const Remove = struct {
if (std.mem.indexOf(u8, file_path, "*") != null or std.mem.indexOf(u8, file_path, "?") != null) {
// Simple wildcard deletion - just show error for now
const error_msg = "Wildcard deletion not yet implemented\n";
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(error_msg);
} else {
print("{s}", .{error_msg});
@@ -36,7 +35,7 @@ pub const Remove = struct {
error.IsDir => "Access denied - cannot delete directory\n",
else => "Cannot delete file\n",
};
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(error_msg);
} else {
print("{s}", .{error_msg});
diff --git a/src/cmd/rename.zig b/src/cmd/rename.zig
index 9942f4c..baca001 100644
--- a/src/cmd/rename.zig
+++ b/src/cmd/rename.zig
@@ -7,21 +7,20 @@ const FileSpec = syntax.FileSpec;
const types = @import("./lib/types.zig");
const CommandStatus = types.CommandStatus;
-const OutputCapture = types.OutputCapture;
-const InputSource = types.InputSource;
+const CommandContext = types.CommandContext;
pub const Rename = struct {
from: FileSpec,
to: FileSpec,
- pub fn eval(rename: Rename, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus {
- _ = allocator;
- _ = input_source;
+ pub fn eval(rename: Rename, ctx: CommandContext) !CommandStatus {
+ _ = ctx.allocator;
+ _ = ctx.input_source;
const from_path = switch (rename.from) {
.Con, .Lpt1, .Lpt2, .Lpt3, .Prn => {
const error_msg = "Cannot rename device\n";
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(error_msg);
} else {
print("{s}", .{error_msg});
@@ -34,7 +33,7 @@ pub const Rename = struct {
const to_path = switch (rename.to) {
.Con, .Lpt1, .Lpt2, .Lpt3, .Prn => {
const error_msg = "Cannot rename to device\n";
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(error_msg);
} else {
print("{s}", .{error_msg});
@@ -52,7 +51,7 @@ pub const Rename = struct {
error.RenameAcrossMountPoints => "Cannot rename across different drives\n",
else => "Cannot rename file\n",
};
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(error_msg);
} else {
print("{s}", .{error_msg});
diff --git a/src/cmd/rmdir.zig b/src/cmd/rmdir.zig
index cbb0b92..db41fab 100644
--- a/src/cmd/rmdir.zig
+++ b/src/cmd/rmdir.zig
@@ -4,15 +4,14 @@ const print = std.debug.print;
const types = @import("./lib/types.zig");
const CommandStatus = types.CommandStatus;
-const OutputCapture = types.OutputCapture;
-const InputSource = types.InputSource;
+const CommandContext = types.CommandContext;
pub const Rmdir = struct {
path: []const u8,
- pub fn eval(rmdir: Rmdir, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus {
- _ = allocator;
- _ = input_source;
+ pub fn eval(rmdir: Rmdir, ctx: CommandContext) !CommandStatus {
+ _ = ctx.allocator;
+ _ = ctx.input_source;
const dir_path = rmdir.path;
@@ -25,7 +24,7 @@ pub const Rmdir = struct {
error.NotDir => "The system cannot find the path specified\n",
else => "Unable to remove directory\n",
};
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(error_msg);
} else {
print("{s}", .{error_msg});
diff --git a/src/cmd/sort.zig b/src/cmd/sort.zig
index 9c04c1e..8b797d1 100644
--- a/src/cmd/sort.zig
+++ b/src/cmd/sort.zig
@@ -5,31 +5,30 @@ const print = std.debug.print;
const types = @import("./lib/types.zig");
const CommandStatus = types.CommandStatus;
-const OutputCapture = types.OutputCapture;
-const InputSource = types.InputSource;
+const CommandContext = types.CommandContext;
pub const Sort = struct {
- pub fn eval(sort: Sort, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus {
+ pub fn eval(sort: Sort, ctx: CommandContext) !CommandStatus {
_ = sort;
- var lines = ArrayList([]const u8).init(allocator);
+ var lines = ArrayList([]const u8).init(ctx.allocator);
defer {
for (lines.items) |line| {
- allocator.free(line);
+ ctx.allocator.free(line);
}
lines.deinit();
}
// Read input lines
- if (input_source) |source| {
+ if (ctx.input_source) |source| {
// Read from input redirection
- while (try source.readLine(allocator)) |line| {
+ 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";
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(msg);
} else {
print("{s}", .{msg});
@@ -45,14 +44,14 @@ pub const Sort = struct {
}.lessThan);
// Output sorted lines
- var output_buffer = ArrayList(u8).init(allocator);
+ var output_buffer = ArrayList(u8).init(ctx.allocator);
defer output_buffer.deinit();
for (lines.items) |line| {
try output_buffer.writer().print("{s}\n", .{line});
}
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(output_buffer.items);
} else {
print("{s}", .{output_buffer.items});
diff --git a/src/cmd/time.zig b/src/cmd/time.zig
index bd03cfa..c30ca47 100644
--- a/src/cmd/time.zig
+++ b/src/cmd/time.zig
@@ -4,13 +4,11 @@ const print = std.debug.print;
const types = @import("./lib/types.zig");
const CommandStatus = types.CommandStatus;
-const OutputCapture = types.OutputCapture;
-const InputSource = types.InputSource;
+const CommandContext = types.CommandContext;
pub const Time = struct {
- pub fn eval(time: Time, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus {
+ pub fn eval(time: Time, ctx: CommandContext) !CommandStatus {
_ = time;
- _ = input_source;
const timestamp = std.time.timestamp();
const epoch_seconds = @as(u64, @intCast(timestamp));
@@ -19,9 +17,9 @@ pub const Time = struct {
const minutes = (day_seconds % std.time.s_per_hour) / std.time.s_per_min;
const seconds = day_seconds % std.time.s_per_min;
- const output = try std.fmt.allocPrint(allocator, "Current time is {d:0>2}:{d:0>2}:{d:0>2}\n", .{ hours, minutes, seconds });
- defer allocator.free(output);
- if (output_capture) |capture| {
+ const output = try std.fmt.allocPrint(ctx.allocator, "Current time is {d:0>2}:{d:0>2}:{d:0>2}\n", .{ hours, minutes, seconds });
+ defer ctx.allocator.free(output);
+ if (ctx.output_capture) |capture| {
try capture.write(output);
} else {
print("{s}", .{output});
diff --git a/src/cmd/type.zig b/src/cmd/type.zig
index 9270e7b..86ccfda 100644
--- a/src/cmd/type.zig
+++ b/src/cmd/type.zig
@@ -8,19 +8,16 @@ const FileSpec = syntax.FileSpec;
const types = @import("./lib/types.zig");
const CommandStatus = types.CommandStatus;
-const OutputCapture = types.OutputCapture;
-const InputSource = types.InputSource;
+const CommandContext = types.CommandContext;
pub const Type = struct {
file: FileSpec,
- pub fn eval(type_cmd: Type, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus {
- _ = input_source;
-
+ pub fn eval(type_cmd: Type, ctx: CommandContext) !CommandStatus {
const file_path = switch (type_cmd.file) {
.Con => {
const error_msg = "Cannot TYPE from CON\n";
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(error_msg);
} else {
print("{s}", .{error_msg});
@@ -29,7 +26,7 @@ pub const Type = struct {
},
.Lpt1, .Lpt2, .Lpt3, .Prn => {
const error_msg = "Cannot TYPE from device\n";
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(error_msg);
} else {
print("{s}", .{error_msg});
@@ -46,7 +43,7 @@ pub const Type = struct {
error.AccessDenied => "Access is denied.\n",
else => "Cannot access file.\n",
};
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(error_msg);
} else {
print("{s}", .{error_msg});
@@ -63,7 +60,7 @@ pub const Type = struct {
error.AccessDenied => "Access is denied.\n",
else => "Error reading file.\n",
};
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(error_msg);
} else {
print("{s}", .{error_msg});
@@ -74,7 +71,7 @@ pub const Type = struct {
if (bytes_read == 0) break;
// Process buffer contents for output
- var processed_output = ArrayList(u8).init(allocator);
+ var processed_output = ArrayList(u8).init(ctx.allocator);
defer processed_output.deinit();
for (buffer[0..bytes_read]) |byte| {
@@ -94,7 +91,7 @@ pub const Type = struct {
}
}
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(processed_output.items);
} else {
print("{s}", .{processed_output.items});
diff --git a/src/cmd/ver.zig b/src/cmd/ver.zig
index 2d8d56b..5e99c03 100644
--- a/src/cmd/ver.zig
+++ b/src/cmd/ver.zig
@@ -4,17 +4,14 @@ const print = std.debug.print;
const types = @import("./lib/types.zig");
const CommandStatus = types.CommandStatus;
-const OutputCapture = types.OutputCapture;
-const InputSource = types.InputSource;
+const CommandContext = types.CommandContext;
pub const Ver = struct {
- pub fn eval(ver: Ver, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus {
+ pub fn eval(ver: Ver, ctx: CommandContext) !CommandStatus {
_ = ver;
- _ = allocator;
- _ = input_source;
const output = "MB-DOSE Version 6.22\n";
- if (output_capture) |capture| {
+ if (ctx.output_capture) |capture| {
try capture.write(output);
} else {
print("{s}", .{output});
diff --git a/src/eval.zig b/src/eval.zig
index a689837..7aaae4b 100644
--- a/src/eval.zig
+++ b/src/eval.zig
@@ -30,6 +30,7 @@ const cmdTypes = @import("cmd/lib/types.zig");
pub const CommandStatus = cmdTypes.CommandStatus;
const OutputCapture = cmdTypes.OutputCapture;
const InputSource = cmdTypes.InputSource;
+const CommandContext = cmdTypes.CommandContext;
const STDOUT_BUFFER_SIZE: usize = 1024;
const STDERR_BUFFER_SIZE: usize = 1024;
@@ -39,78 +40,80 @@ pub fn executeCommand(command: Command, allocator: Allocator) !CommandStatus {
}
pub fn executeCommandWithOutput(command: Command, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus {
+ const ctx = CommandContext.init(allocator, output_capture, input_source);
+
switch (command) {
.Empty => return CommandStatus{ .Code = 0 },
.Builtin => |builtin_cmd| {
switch (builtin_cmd) {
.EchoText => |echo_text| {
- return echo_text.eval(allocator, output_capture, input_source);
+ return echo_text.eval(ctx);
},
.Cls => |cls| {
- return cls.eval(allocator, output_capture, input_source);
+ return cls.eval(ctx);
},
.Exit => {
return CommandStatus.ExitShell;
},
.EchoPlain => |echo_plain| {
- return echo_plain.eval(allocator, output_capture, input_source);
+ return echo_plain.eval(ctx);
},
.EchoOn => |echo_on| {
- return echo_on.eval(allocator, output_capture, input_source);
+ return echo_on.eval(ctx);
},
.EchoOff => |echo_off| {
- return echo_off.eval(allocator, output_capture, input_source);
+ return echo_off.eval(ctx);
},
.Ver => |ver| {
- return ver.eval(allocator, output_capture, input_source);
+ return ver.eval(ctx);
},
.Date => |date| {
- return date.eval(allocator, output_capture, input_source);
+ return date.eval(ctx);
},
.Time => |time| {
- return time.eval(allocator, output_capture, input_source);
+ return time.eval(ctx);
},
.Dir => |dir| {
- return dir.eval(allocator, output_capture, input_source);
+ return dir.eval(ctx);
},
.Type => |type_cmd| {
- return type_cmd.eval(allocator, output_capture, input_source);
+ return type_cmd.eval(ctx);
},
.Sort => |sort| {
- return sort.eval(allocator, output_capture, input_source);
+ return sort.eval(ctx);
},
.Chdir => |chdir| {
- return chdir.eval(allocator, output_capture, input_source);
+ return chdir.eval(ctx);
},
.Copy => |copy| {
- return copy.eval(allocator, output_capture, input_source);
+ return copy.eval(ctx);
},
.Remove => |remove| {
- return remove.eval(allocator, output_capture, input_source);
+ return remove.eval(ctx);
},
.Mkdir => |mkdir| {
- return mkdir.eval(allocator, output_capture, input_source);
+ return mkdir.eval(ctx);
},
.Rmdir => |rmdir| {
- return rmdir.eval(allocator, output_capture, input_source);
+ return rmdir.eval(ctx);
},
.Rename => |rename| {
- return rename.eval(allocator, output_capture, input_source);
+ return rename.eval(ctx);
},
.Move => |move| {
- return move.eval(allocator, output_capture, input_source);
+ return move.eval(ctx);
},
.PathGet => |path_get| {
- return path_get.eval(allocator, output_capture, input_source);
+ return path_get.eval(ctx);
},
.PathSet => |path_set| {
- return path_set.eval(allocator, output_capture, input_source);
+ return path_set.eval(ctx);
},
else => {
- const error_msg = try std.fmt.allocPrint(allocator, "Command not implemented: {any}\n", .{builtin_cmd});
- defer allocator.free(error_msg);
- if (output_capture) |capture| {
+ const error_msg = try std.fmt.allocPrint(ctx.allocator, "Command not implemented: {any}\n", .{builtin_cmd});
+ defer ctx.allocator.free(error_msg);
+ if (ctx.output_capture) |capture| {
try capture.write(error_msg);
} else {
print("{s}", .{error_msg});
@@ -121,15 +124,17 @@ pub fn executeCommandWithOutput(command: Command, allocator: Allocator, output_c
},
.External => |external| {
- return external.eval(allocator, output_capture, input_source);
+ return external.eval(ctx);
},
.Redirect => |redirect| {
- return redirect.eval(allocator, output_capture, input_source, executeCommandWithOutput);
+ const ctx_with_executor = CommandContext.with_executor(allocator, output_capture, input_source, executeCommandWithOutput);
+ return redirect.eval(ctx_with_executor);
},
.Pipe => |pipe| {
- return pipe.eval(allocator, output_capture, input_source, executeCommandWithOutput);
+ const ctx_with_executor = CommandContext.with_executor(allocator, output_capture, input_source, executeCommandWithOutput);
+ return pipe.eval(ctx_with_executor);
},
}
}