diff options
author | Matthias Andreas Benkard <code@mail.matthias.benkard.de> | 2025-08-14 16:40:52 +0200 |
---|---|---|
committer | Matthias Andreas Benkard <code@mail.matthias.benkard.de> | 2025-08-14 16:40:52 +0200 |
commit | 14ccaa3a849c2fbf30595491e605643fbfd4f733 (patch) | |
tree | 1cffdcc80ab709ef4af81615e83927d070453be0 /src | |
parent | f40779cf8ebe81ffa802967587a83d71c74c1c7e (diff) |
Factor out CLS, DATE, ECHO, MOVE, SORT, TIME, VER.
Diffstat (limited to 'src')
-rw-r--r-- | src/cmd.zig | 32 | ||||
-rw-r--r-- | src/cmd/cls.zig | 22 | ||||
-rw-r--r-- | src/cmd/date.zig | 68 | ||||
-rw-r--r-- | src/cmd/echo.zig | 68 | ||||
-rw-r--r-- | src/cmd/move.zig | 24 | ||||
-rw-r--r-- | src/cmd/sort.zig | 63 | ||||
-rw-r--r-- | src/cmd/time.zig | 31 | ||||
-rw-r--r-- | src/cmd/ver.zig | 24 | ||||
-rw-r--r-- | src/eval.zig | 186 |
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); |