diff options
author | Matthias Andreas Benkard <code@mail.matthias.benkard.de> | 2025-08-13 19:22:06 +0200 |
---|---|---|
committer | Matthias Andreas Benkard <code@mail.matthias.benkard.de> | 2025-08-13 19:22:06 +0200 |
commit | 98c4176b91ee08106d7cc988f364825c7956722e (patch) | |
tree | 5c2212d427dee56de78e9f7e9ccf73e32098052d /src | |
parent | 621eaa97e417ce329c65ebf23d555ce34c39dddf (diff) |
Reformat.
Diffstat (limited to 'src')
-rw-r--r-- | src/main.zig | 179 |
1 files changed, 87 insertions, 92 deletions
diff --git a/src/main.zig b/src/main.zig index 3e2c266..f2a7726 100644 --- a/src/main.zig +++ b/src/main.zig @@ -11,7 +11,7 @@ const STDERR_BUFFER_SIZE: usize = 1024; const FileSpec = union(enum) { Con, Lpt1, - Lpt2, + Lpt2, Lpt3, Prn, Path: []const u8, @@ -19,8 +19,8 @@ const FileSpec = union(enum) { const RedirectType = enum { OutputOverwrite, // > - OutputAppend, // >> - InputFrom, // < + OutputAppend, // >> + InputFrom, // < }; const Redirect = struct { @@ -214,9 +214,9 @@ const CommandStatus = union(enum) { const Token = union(enum) { Word: []const u8, Pipe, - RedirectOut, // > + RedirectOut, // > RedirectAppend, // >> - RedirectIn, // < + RedirectIn, // < Newline, Eof, }; @@ -257,7 +257,7 @@ const Lexer = struct { fn readWord(self: *Lexer, allocator: Allocator) ![]const u8 { var word = ArrayList(u8).init(allocator); defer word.deinit(); - + var in_quotes = false; var quote_char: u8 = '"'; @@ -337,7 +337,7 @@ const Lexer = struct { pub fn tokenize(self: *Lexer, allocator: Allocator) !ArrayList(Token) { var tokens = ArrayList(Token).init(allocator); - + while (true) { const token = try self.nextToken(allocator); const is_eof = switch (token) { @@ -480,7 +480,7 @@ const Parser = struct { } else { const first_arg_upper = try std.ascii.allocUpperString(self.allocator, args.items[0]); defer self.allocator.free(first_arg_upper); - + if (std.mem.eql(u8, first_arg_upper, "ON") and args.items.len == 1) { return Command{ .Builtin = BuiltinCommand.EchoOn }; } else if (std.mem.eql(u8, first_arg_upper, "OFF") and args.items.len == 1) { @@ -592,21 +592,21 @@ fn parseAndExecute(input: []const u8, allocator: Allocator) !CommandStatus { 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; } @@ -615,30 +615,30 @@ const OutputCapture = struct { 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]); + return try allocator.dupe(u8, line[0 .. line.len - 1]); } else { return try allocator.dupe(u8, line); } @@ -648,7 +648,7 @@ const InputSource = struct { fn executeCommandWithOutput(command: Command, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus { switch (command) { .Empty => return CommandStatus{ .Code = 0 }, - + .Builtin => |builtin_cmd| { switch (builtin_cmd) { .EchoText => |echo| { @@ -705,15 +705,15 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu 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; @@ -721,10 +721,10 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu 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; @@ -732,16 +732,16 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu 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| { @@ -758,7 +758,7 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu 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| { @@ -771,9 +771,9 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu .Dir => |dir| { var output_buffer = ArrayList(u8).init(allocator); defer output_buffer.deinit(); - + try output_buffer.writer().print("Directory of {s}\n\n", .{dir.path}); - + var dir_iterator = std.fs.cwd().openDir(dir.path, .{ .iterate = true }) catch { const error_msg = "File not found\n"; if (output_capture) |capture| { @@ -784,11 +784,11 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu return CommandStatus{ .Code = 1 }; }; defer dir_iterator.close(); - + var iterator = dir_iterator.iterate(); var file_count: u32 = 0; var dir_count: u32 = 0; - + while (try iterator.next()) |entry| { switch (entry.kind) { .directory => { @@ -803,10 +803,10 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu else => {}, } } - + try output_buffer.writer().print("\n{d} File(s)\n", .{file_count}); try output_buffer.writer().print("{d} Dir(s)\n", .{dir_count}); - + if (output_capture) |capture| { try capture.write(output_buffer.items); } else { @@ -836,7 +836,7 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu }, .Path => |path| path, }; - + const file = std.fs.cwd().openFile(file_path, .{}) catch |err| { const error_msg = switch (err) { error.FileNotFound => "The system cannot find the file specified.\n", @@ -852,7 +852,7 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu return CommandStatus{ .Code = 1 }; }; defer file.close(); - + // Read and display file contents var buffer: [4096]u8 = undefined; while (true) { @@ -868,13 +868,13 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu } return CommandStatus{ .Code = 1 }; }; - + if (bytes_read == 0) break; - + // Process buffer contents for output var processed_output = ArrayList(u8).init(allocator); defer processed_output.deinit(); - + for (buffer[0..bytes_read]) |byte| { // Convert to printable characters, similar to DOS TYPE behavior if (byte >= 32 and byte <= 126) { @@ -891,17 +891,17 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu try processed_output.append('?'); } } - + if (output_capture) |capture| { try capture.write(processed_output.items); } else { print("{s}", .{processed_output.items}); } - + // If we read less than the buffer size, we're done if (bytes_read < buffer.len) break; } - + return CommandStatus{ .Code = 0 }; }, .Sort => { @@ -912,7 +912,7 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu } lines.deinit(); } - + // Read input lines if (input_source) |source| { // Read from input redirection @@ -929,28 +929,28 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu } 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 }; }, else => { @@ -965,24 +965,24 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu }, } }, - + .External => |external| { // Try to execute external command var child_args = ArrayList([]const u8).init(allocator); defer child_args.deinit(); - + try child_args.append(external.program); for (external.args.items) |arg| { try child_args.append(arg); } - + var child = std.process.Child.init(child_args.items, allocator); - + // Set up pipes for capturing output child.stdin_behavior = if (input_source != null) .Pipe else .Inherit; child.stdout_behavior = if (output_capture != null) .Pipe else .Inherit; child.stderr_behavior = if (output_capture != null) .Pipe else .Inherit; - + const spawn_result = child.spawn(); if (spawn_result) |_| { // Spawn succeeded, continue with execution @@ -990,7 +990,7 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu error.FileNotFound => { const error_msg = try std.fmt.allocPrint(allocator, "'{s}' is not recognized as an internal or external command,\noperable program or batch file.\n", .{external.program}); defer allocator.free(error_msg); - + if (output_capture) |capture| { try capture.write(error_msg); } else { @@ -1010,25 +1010,25 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu else => { const error_msg = try std.fmt.allocPrint(allocator, "Cannot execute '{s}': {}\n", .{ external.program, err }); defer allocator.free(error_msg); - + if (output_capture) |capture| { try capture.write(error_msg); } else { print("{s}", .{error_msg}); } return CommandStatus{ .Code = 1 }; - } + }, } - + // Handle input redirection if (input_source) |source| { if (child.stdin) |stdin| { const writer = stdin.writer(); - + // Reset source position for reading var temp_source = source.*; temp_source.position = 0; - + while (try temp_source.readLine(allocator)) |line| { defer allocator.free(line); try writer.print("{s}\n", .{line}); @@ -1037,7 +1037,7 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu child.stdin = null; } } - + // Handle output capture if (output_capture) |capture| { // Read stdout @@ -1049,8 +1049,8 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu try capture.write(buffer[0..bytes_read]); } } - - // Read stderr + + // Read stderr if (child.stderr) |stderr| { var buffer: [4096]u8 = undefined; while (true) { @@ -1060,7 +1060,7 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu } } } - + // Wait for process to complete const term = child.wait() catch |err| { const error_msg = switch (err) { @@ -1068,7 +1068,7 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu else => try std.fmt.allocPrint(allocator, "Error waiting for command: {}\n", .{err}), }; defer allocator.free(error_msg); - + if (output_capture) |capture| { try capture.write(error_msg); } else { @@ -1076,7 +1076,7 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu } return CommandStatus{ .Code = 1 }; }; - + // Return exit code switch (term) { .Exited => |code| return CommandStatus{ .Code = @intCast(code) }, @@ -1085,7 +1085,7 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu .Unknown => |_| return CommandStatus{ .Code = 1 }, } }, - + .Redirect => |redirect| { // Check if we have any output redirections var has_output_redirect = false; @@ -1095,15 +1095,15 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu break; } } - + var captured_output = OutputCapture.init(allocator); defer captured_output.deinit(); - + // Prepare input redirection if needed var input_data: ?[]const u8 = null; var redirect_input_source: ?InputSource = null; defer if (input_data) |data| allocator.free(data); - + // Process input redirections first for (redirect.redirects.items) |redir| { if (redir.redirect_type == .InputFrom) { @@ -1118,7 +1118,7 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu }, .Path => |path| path, }; - + // Read input file const file = std.fs.cwd().openFile(file_path, .{}) catch |err| { switch (err) { @@ -1129,7 +1129,7 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu return CommandStatus{ .Code = 1 }; }; defer file.close(); - + input_data = file.readToEndAlloc(allocator, std.math.maxInt(usize)) catch |err| { switch (err) { error.AccessDenied => print("Access is denied.\n", .{}), @@ -1137,20 +1137,15 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu } return CommandStatus{ .Code = 1 }; }; - + redirect_input_source = InputSource.init(input_data.?); break; // Only handle first input redirection } } - + // Execute the command with input and output capture (only capture output if needed) - const status = try executeCommandWithOutput( - redirect.command.*, - allocator, - if (has_output_redirect) &captured_output else null, - if (redirect_input_source) |*source| source else null - ); - + const status = try executeCommandWithOutput(redirect.command.*, allocator, if (has_output_redirect) &captured_output else null, if (redirect_input_source) |*source| source else null); + // Handle output redirections for (redirect.redirects.items) |redir| { if (redir.redirect_type == .InputFrom) continue; // Already handled @@ -1166,7 +1161,7 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu }, .Path => |path| path, }; - + // Handle different redirect types switch (redir.redirect_type) { .OutputOverwrite => { @@ -1179,7 +1174,7 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu return CommandStatus{ .Code = 1 }; }; defer file.close(); - + file.writeAll(captured_output.getContents()) catch |err| { switch (err) { error.AccessDenied => print("Access is denied.\n", .{}), @@ -1202,7 +1197,7 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu return CommandStatus{ .Code = 1 }; }; defer new_file.close(); - + new_file.writeAll(captured_output.getContents()) catch { print("Cannot write to file.\n", .{}); return CommandStatus{ .Code = 1 }; @@ -1220,13 +1215,13 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu } }; defer file.close(); - + // Seek to end for append file.seekFromEnd(0) catch { print("Cannot seek to end of file.\n", .{}); return CommandStatus{ .Code = 1 }; }; - + file.writeAll(captured_output.getContents()) catch |err| { switch (err) { error.AccessDenied => print("Access is denied.\n", .{}), @@ -1241,10 +1236,10 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu }, } } - + return status; }, - + else => { const error_msg = "Command type not implemented\n"; if (output_capture) |capture| { @@ -1264,11 +1259,11 @@ fn executeCommand(command: Command, allocator: Allocator) !CommandStatus { fn readLine(allocator: Allocator, prompt_text: []const u8) !?[]const u8 { const stdin = std.io.getStdIn().reader(); print("{s}", .{prompt_text}); - + if (try stdin.readUntilDelimiterOrEofAlloc(allocator, '\n', 4096)) |input| { // Remove trailing \r on Windows if (input.len > 0 and input[input.len - 1] == '\r') { - return input[0..input.len - 1]; + return input[0 .. input.len - 1]; } return input; } @@ -1294,13 +1289,13 @@ pub fn main() !void { const interpolated_prompt = try std.mem.replaceOwned(u8, allocator, prompt_spec, "$p", full_cwd); defer allocator.free(interpolated_prompt); - + const final_prompt = try std.mem.replaceOwned(u8, allocator, interpolated_prompt, "$g", ">"); defer allocator.free(final_prompt); if (try readLine(allocator, final_prompt)) |line| { defer allocator.free(line); - + const command_result = parseAndExecute(line, allocator) catch |err| { switch (err) { error.ExpectedWord, error.UnexpectedToken => { @@ -1319,4 +1314,4 @@ pub fn main() !void { break; // EOF } } -}
\ No newline at end of file +} |