summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cmd.zig32
-rw-r--r--src/cmd/cls.zig22
-rw-r--r--src/cmd/date.zig68
-rw-r--r--src/cmd/echo.zig68
-rw-r--r--src/cmd/move.zig24
-rw-r--r--src/cmd/sort.zig63
-rw-r--r--src/cmd/time.zig31
-rw-r--r--src/cmd/ver.zig24
-rw-r--r--src/eval.zig186
9 files changed, 340 insertions, 178 deletions
diff --git a/src/cmd.zig b/src/cmd.zig
index c24cea5..8f4c971 100644
--- a/src/cmd.zig
+++ b/src/cmd.zig
@@ -16,6 +16,16 @@ const PathGet = @import("cmd/path.zig").PathGet;
const PathSet = @import("cmd/path.zig").PathSet;
const Dir = @import("cmd/dir.zig").Dir;
const Type = @import("cmd/type.zig").Type;
+const EchoOff = @import("cmd/echo.zig").EchoOff;
+const EchoOn = @import("cmd/echo.zig").EchoOn;
+const EchoPlain = @import("cmd/echo.zig").EchoPlain;
+const EchoText = @import("cmd/echo.zig").EchoText;
+const Cls = @import("cmd/cls.zig").Cls;
+const Ver = @import("cmd/ver.zig").Ver;
+const Date = @import("cmd/date.zig").Date;
+const Time = @import("cmd/time.zig").Time;
+const Sort = @import("cmd/sort.zig").Sort;
+const Move = @import("cmd/move.zig").Move;
pub const BuiltinCommand = union(enum) {
// File-oriented
@@ -27,12 +37,12 @@ pub const BuiltinCommand = union(enum) {
Fc,
Find,
Mkdir: Mkdir,
- Move,
+ Move: Move,
Remove: Remove,
Rename: Rename,
Replace,
Rmdir: Rmdir,
- Sort,
+ Sort: Sort,
Tree: struct {
path: []const u8,
},
@@ -46,12 +56,10 @@ pub const BuiltinCommand = union(enum) {
// Shell-oriented
Append,
Chdir: Chdir,
- EchoOff,
- EchoOn,
- EchoPlain,
- EchoText: struct {
- message: []const u8,
- },
+ EchoOff: EchoOff,
+ EchoOn: EchoOn,
+ EchoPlain: EchoPlain,
+ EchoText: EchoText,
Exit,
PathGet: PathGet,
PathSet: PathSet,
@@ -64,14 +72,14 @@ pub const BuiltinCommand = union(enum) {
value: []const u8,
},
Setver,
- Ver,
+ Ver: Ver,
// Utilities
- Date,
- Time,
+ Date: Date,
+ Time: Time,
// Screen-oriented
- Cls,
+ Cls: Cls,
More,
// Dummies
diff --git a/src/cmd/cls.zig b/src/cmd/cls.zig
new file mode 100644
index 0000000..b476563
--- /dev/null
+++ b/src/cmd/cls.zig
@@ -0,0 +1,22 @@
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const print = std.debug.print;
+
+const types = @import("./types.zig");
+const CommandStatus = types.CommandStatus;
+const OutputCapture = types.OutputCapture;
+const InputSource = types.InputSource;
+
+pub const Cls = struct {
+ pub fn eval(cls: Cls, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus {
+ _ = cls;
+ _ = allocator;
+ _ = input_source;
+
+ if (output_capture == null) {
+ // Clear screen - only works when not redirected
+ print("\x1B[2J\x1B[H", .{});
+ }
+ return CommandStatus{ .Code = 0 };
+ }
+};
diff --git a/src/cmd/date.zig b/src/cmd/date.zig
new file mode 100644
index 0000000..c6db0a2
--- /dev/null
+++ b/src/cmd/date.zig
@@ -0,0 +1,68 @@
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const print = std.debug.print;
+
+const types = @import("./types.zig");
+const CommandStatus = types.CommandStatus;
+const OutputCapture = types.OutputCapture;
+const InputSource = types.InputSource;
+
+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 {
+ _ = date;
+ _ = input_source;
+
+ const timestamp = std.time.timestamp();
+ const epoch_seconds = @as(u64, @intCast(timestamp));
+ const epoch_day = @divFloor(epoch_seconds, std.time.s_per_day);
+
+ // Calculate days since Unix epoch (1970-01-01)
+ // Unix epoch is 719163 days since year 1 AD
+ const days_since_year_1 = epoch_day + 719163;
+
+ // Simple algorithm to convert days to year/month/day
+ var year: u32 = 1;
+ var remaining_days = days_since_year_1;
+
+ // Find the year
+ while (true) {
+ const days_in_year: u64 = if (isLeapYear(year)) 366 else 365;
+ if (remaining_days < days_in_year) break;
+ remaining_days -= days_in_year;
+ year += 1;
+ }
+
+ // Days in each month (non-leap year)
+ const days_in_month = [_]u32{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+ var month: u32 = 1;
+ for (days_in_month, 1..) |days, m| {
+ var month_days = days;
+ // Adjust February for leap years
+ if (m == 2 and isLeapYear(year)) {
+ month_days = 29;
+ }
+
+ if (remaining_days < month_days) {
+ month = @intCast(m);
+ break;
+ }
+ remaining_days -= month_days;
+ }
+
+ 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| {
+ try capture.write(output);
+ } else {
+ print("{s}", .{output});
+ }
+ return CommandStatus{ .Code = 0 };
+ }
+};
diff --git a/src/cmd/echo.zig b/src/cmd/echo.zig
new file mode 100644
index 0000000..a11d748
--- /dev/null
+++ b/src/cmd/echo.zig
@@ -0,0 +1,68 @@
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const print = std.debug.print;
+
+const types = @import("./types.zig");
+const CommandStatus = types.CommandStatus;
+const OutputCapture = types.OutputCapture;
+const InputSource = types.InputSource;
+
+pub const EchoOff = struct {
+ pub fn eval(echo_off: EchoOff, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus {
+ _ = echo_off;
+ _ = allocator;
+ _ = output_capture;
+ _ = input_source;
+
+ return CommandStatus{ .Code = 0 };
+ }
+};
+
+pub const EchoOn = struct {
+ pub fn eval(echo_on: EchoOn, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus {
+ _ = echo_on;
+ _ = allocator;
+ _ = input_source;
+
+ const output = "ECHO is on\n";
+ if (output_capture) |capture| {
+ try capture.write(output);
+ } else {
+ print("{s}", .{output});
+ }
+ return CommandStatus{ .Code = 0 };
+ }
+};
+
+pub const EchoPlain = struct {
+ pub fn eval(echo_plain: EchoPlain, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus {
+ _ = echo_plain;
+ _ = allocator;
+ _ = input_source;
+
+ const output = "ECHO is on\n";
+ if (output_capture) |capture| {
+ try capture.write(output);
+ } else {
+ print("{s}", .{output});
+ }
+ return CommandStatus{ .Code = 0 };
+ }
+};
+
+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| {
+ try capture.write(output);
+ } else {
+ print("{s}", .{output});
+ }
+ return CommandStatus{ .Code = 0 };
+ }
+};
diff --git a/src/cmd/move.zig b/src/cmd/move.zig
new file mode 100644
index 0000000..ceaa6db
--- /dev/null
+++ b/src/cmd/move.zig
@@ -0,0 +1,24 @@
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const print = std.debug.print;
+
+const types = @import("./types.zig");
+const CommandStatus = types.CommandStatus;
+const OutputCapture = types.OutputCapture;
+const InputSource = types.InputSource;
+
+pub const Move = struct {
+ pub fn eval(move: Move, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus {
+ _ = move;
+ _ = allocator;
+ _ = input_source;
+
+ const error_msg = "MOVE command not yet implemented\n";
+ if (output_capture) |capture| {
+ try capture.write(error_msg);
+ } else {
+ print("{s}", .{error_msg});
+ }
+ return CommandStatus{ .Code = 1 };
+ }
+};
diff --git a/src/cmd/sort.zig b/src/cmd/sort.zig
new file mode 100644
index 0000000..fb08f0b
--- /dev/null
+++ b/src/cmd/sort.zig
@@ -0,0 +1,63 @@
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const ArrayList = std.ArrayList;
+const print = std.debug.print;
+
+const types = @import("./types.zig");
+const CommandStatus = types.CommandStatus;
+const OutputCapture = types.OutputCapture;
+const InputSource = types.InputSource;
+
+pub const Sort = struct {
+ pub fn eval(sort: Sort, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus {
+ _ = sort;
+
+ var lines = ArrayList([]const u8).init(allocator);
+ defer {
+ for (lines.items) |line| {
+ allocator.free(line);
+ }
+ lines.deinit();
+ }
+
+ // Read input lines
+ if (input_source) |source| {
+ // Read from input redirection
+ while (try source.readLine(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| {
+ try capture.write(msg);
+ } else {
+ print("{s}", .{msg});
+ }
+ return CommandStatus{ .Code = 0 };
+ }
+
+ // Sort the lines
+ std.mem.sort([]const u8, lines.items, {}, struct {
+ fn lessThan(_: void, lhs: []const u8, rhs: []const u8) bool {
+ return std.mem.order(u8, lhs, rhs) == .lt;
+ }
+ }.lessThan);
+
+ // Output sorted lines
+ var output_buffer = ArrayList(u8).init(allocator);
+ defer output_buffer.deinit();
+
+ for (lines.items) |line| {
+ try output_buffer.writer().print("{s}\n", .{line});
+ }
+
+ if (output_capture) |capture| {
+ try capture.write(output_buffer.items);
+ } else {
+ print("{s}", .{output_buffer.items});
+ }
+
+ return CommandStatus{ .Code = 0 };
+ }
+};
diff --git a/src/cmd/time.zig b/src/cmd/time.zig
new file mode 100644
index 0000000..57d58bd
--- /dev/null
+++ b/src/cmd/time.zig
@@ -0,0 +1,31 @@
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const print = std.debug.print;
+
+const types = @import("./types.zig");
+const CommandStatus = types.CommandStatus;
+const OutputCapture = types.OutputCapture;
+const InputSource = types.InputSource;
+
+pub const Time = struct {
+ pub fn eval(time: Time, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus {
+ _ = time;
+ _ = input_source;
+
+ const timestamp = std.time.timestamp();
+ const epoch_seconds = @as(u64, @intCast(timestamp));
+ const day_seconds = epoch_seconds % std.time.s_per_day;
+ const hours = day_seconds / std.time.s_per_hour;
+ 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| {
+ try capture.write(output);
+ } else {
+ print("{s}", .{output});
+ }
+ return CommandStatus{ .Code = 0 };
+ }
+};
diff --git a/src/cmd/ver.zig b/src/cmd/ver.zig
new file mode 100644
index 0000000..286f6f9
--- /dev/null
+++ b/src/cmd/ver.zig
@@ -0,0 +1,24 @@
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const print = std.debug.print;
+
+const types = @import("./types.zig");
+const CommandStatus = types.CommandStatus;
+const OutputCapture = types.OutputCapture;
+const InputSource = types.InputSource;
+
+pub const Ver = struct {
+ pub fn eval(ver: Ver, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus {
+ _ = ver;
+ _ = allocator;
+ _ = input_source;
+
+ const output = "MB-DOSE Version 6.22\n";
+ if (output_capture) |capture| {
+ try capture.write(output);
+ } else {
+ print("{s}", .{output});
+ }
+ return CommandStatus{ .Code = 0 };
+ }
+};
diff --git a/src/eval.zig b/src/eval.zig
index 9b00462..5aa43e4 100644
--- a/src/eval.zig
+++ b/src/eval.zig
@@ -34,10 +34,6 @@ const InputSource = cmdTypes.InputSource;
const STDOUT_BUFFER_SIZE: usize = 1024;
const STDERR_BUFFER_SIZE: usize = 1024;
-fn isLeapYear(year: u32) bool {
- return (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0);
-}
-
pub fn executeCommand(command: Command, allocator: Allocator) !CommandStatus {
return executeCommandWithOutput(command, allocator, null, null);
}
@@ -48,122 +44,32 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
.Builtin => |builtin_cmd| {
switch (builtin_cmd) {
- .EchoText => |echo| {
- const output = try std.fmt.allocPrint(allocator, "{s}\n", .{echo.message});
- defer allocator.free(output);
- if (output_capture) |capture| {
- try capture.write(output);
- } else {
- print("{s}", .{output});
- }
- return CommandStatus{ .Code = 0 };
+ .EchoText => |echo_text| {
+ return echo_text.eval(allocator, output_capture, input_source);
},
- .Cls => {
- if (output_capture == null) {
- // Clear screen - only works when not redirected
- print("\x1B[2J\x1B[H", .{});
- }
- return CommandStatus{ .Code = 0 };
+ .Cls => |cls| {
+ return cls.eval(allocator, output_capture, input_source);
},
.Exit => {
return CommandStatus.ExitShell;
},
- .EchoPlain => {
- const output = "ECHO is on\n";
- if (output_capture) |capture| {
- try capture.write(output);
- } else {
- print("{s}", .{output});
- }
- return CommandStatus{ .Code = 0 };
+ .EchoPlain => |echo_plain| {
+ return echo_plain.eval(allocator, output_capture, input_source);
},
- .EchoOn => {
- const output = "ECHO is on\n";
- if (output_capture) |capture| {
- try capture.write(output);
- } else {
- print("{s}", .{output});
- }
- return CommandStatus{ .Code = 0 };
+ .EchoOn => |echo_on| {
+ return echo_on.eval(allocator, output_capture, input_source);
},
- .EchoOff => {
- return CommandStatus{ .Code = 0 };
+ .EchoOff => |echo_off| {
+ return echo_off.eval(allocator, output_capture, input_source);
},
- .Ver => {
- const output = "MB-DOSE Version 6.22\n";
- if (output_capture) |capture| {
- try capture.write(output);
- } else {
- print("{s}", .{output});
- }
- return CommandStatus{ .Code = 0 };
+ .Ver => |ver| {
+ return ver.eval(allocator, output_capture, input_source);
},
- .Date => {
- const timestamp = std.time.timestamp();
- const epoch_seconds = @as(u64, @intCast(timestamp));
- const epoch_day = @divFloor(epoch_seconds, std.time.s_per_day);
-
- // Calculate days since Unix epoch (1970-01-01)
- // Unix epoch is 719163 days since year 1 AD
- const days_since_year_1 = epoch_day + 719163;
-
- // Simple algorithm to convert days to year/month/day
- var year: u32 = 1;
- var remaining_days = days_since_year_1;
-
- // Find the year
- while (true) {
- const days_in_year: u64 = if (isLeapYear(year)) 366 else 365;
- if (remaining_days < days_in_year) break;
- remaining_days -= days_in_year;
- year += 1;
- }
-
- // Days in each month (non-leap year)
- const days_in_month = [_]u32{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
-
- var month: u32 = 1;
- for (days_in_month, 1..) |days, m| {
- var month_days = days;
- // Adjust February for leap years
- if (m == 2 and isLeapYear(year)) {
- month_days = 29;
- }
-
- if (remaining_days < month_days) {
- month = @intCast(m);
- break;
- }
- remaining_days -= month_days;
- }
-
- 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| {
- try capture.write(output);
- } else {
- print("{s}", .{output});
- }
- return CommandStatus{ .Code = 0 };
+ .Date => |date| {
+ return date.eval(allocator, output_capture, input_source);
},
- .Time => {
- const timestamp = std.time.timestamp();
- const epoch_seconds = @as(u64, @intCast(timestamp));
- const day_seconds = epoch_seconds % std.time.s_per_day;
- const hours = day_seconds / std.time.s_per_hour;
- 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| {
- try capture.write(output);
- } else {
- print("{s}", .{output});
- }
- return CommandStatus{ .Code = 0 };
+ .Time => |time| {
+ return time.eval(allocator, output_capture, input_source);
},
.Dir => |dir| {
return dir.eval(allocator, output_capture, input_source);
@@ -171,54 +77,8 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
.Type => |type_cmd| {
return type_cmd.eval(allocator, output_capture, input_source);
},
- .Sort => {
- var lines = ArrayList([]const u8).init(allocator);
- defer {
- for (lines.items) |line| {
- allocator.free(line);
- }
- lines.deinit();
- }
-
- // Read input lines
- if (input_source) |source| {
- // Read from input redirection
- while (try source.readLine(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| {
- try capture.write(msg);
- } else {
- print("{s}", .{msg});
- }
- return CommandStatus{ .Code = 0 };
- }
-
- // Sort the lines
- std.mem.sort([]const u8, lines.items, {}, struct {
- fn lessThan(_: void, lhs: []const u8, rhs: []const u8) bool {
- return std.mem.order(u8, lhs, rhs) == .lt;
- }
- }.lessThan);
-
- // Output sorted lines
- var output_buffer = ArrayList(u8).init(allocator);
- defer output_buffer.deinit();
-
- for (lines.items) |line| {
- try output_buffer.writer().print("{s}\n", .{line});
- }
-
- if (output_capture) |capture| {
- try capture.write(output_buffer.items);
- } else {
- print("{s}", .{output_buffer.items});
- }
-
- return CommandStatus{ .Code = 0 };
+ .Sort => |sort| {
+ return sort.eval(allocator, output_capture, input_source);
},
.Chdir => |chdir| {
return chdir.eval(allocator, output_capture, input_source);
@@ -238,14 +98,8 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
.Rename => |rename| {
return rename.eval(allocator, output_capture, input_source);
},
- .Move => {
- const error_msg = "MOVE command not yet implemented\n";
- if (output_capture) |capture| {
- try capture.write(error_msg);
- } else {
- print("{s}", .{error_msg});
- }
- return CommandStatus{ .Code = 1 };
+ .Move => |move| {
+ return move.eval(allocator, output_capture, input_source);
},
.PathGet => |path_get| {
return path_get.eval(allocator, output_capture, input_source);