From 92f501b7b282cfc8391898a67e1bd95fa06631ac Mon Sep 17 00:00:00 2001 From: Lucas Faria Mendes Date: Fri, 5 Dec 2025 05:57:23 -0300 Subject: codecrafters submit [skip ci] --- src/builtins.zig | 215 ++++++++++++++++++++++++++----------------------------- 1 file changed, 103 insertions(+), 112 deletions(-) (limited to 'src/builtins.zig') diff --git a/src/builtins.zig b/src/builtins.zig index 4f8dfb8..3608b81 100644 --- a/src/builtins.zig +++ b/src/builtins.zig @@ -3,67 +3,64 @@ const path = @import("path.zig"); const BUILTINS = [_][]const u8{ "exit", "echo", "type", "pwd", "cd", "history" }; -pub const CommandResult = enum { - continue_loop, - exit_shell, -}; +pub const CommandResult = enum { continue_loop, exit_shell }; -pub fn isBuiltin(cmd_name: []const u8) bool { - for (BUILTINS) |builtin| { +pub inline fn isBuiltin(cmd_name: []const u8) bool { + inline for (BUILTINS) |builtin| { if (std.mem.eql(u8, cmd_name, builtin)) return true; } return false; } -pub fn executeExit() CommandResult { +pub inline fn executeExit() CommandResult { return .exit_shell; } pub fn executeEcho(stdout: anytype, args: ?[]const u8) !void { - if (args) |a| { - var i: usize = 0; - var in_quote = false; - var quote_char: u8 = 0; - var unquoted = std.ArrayList(u8){}; - defer unquoted.deinit(std.heap.page_allocator); - var last_was_space = false; - - while (i < a.len) : (i += 1) { - if (!in_quote and a[i] == '\\' and i + 1 < a.len) { - i += 1; - _ = unquoted.append(std.heap.page_allocator, a[i]) catch {}; - last_was_space = false; - } else if (!in_quote and (a[i] == '\'' or a[i] == '"')) { - in_quote = true; - quote_char = a[i]; - last_was_space = false; - } else if (in_quote and a[i] == quote_char) { - in_quote = false; - last_was_space = false; - } else if (in_quote and quote_char == '"' and a[i] == '\\' and i + 1 < a.len) { - const next = a[i + 1]; - if (next == '"' or next == '\\') { - i += 1; - _ = unquoted.append(std.heap.page_allocator, a[i]) catch {}; - } else { - _ = unquoted.append(std.heap.page_allocator, a[i]) catch {}; - } - last_was_space = false; - } else if (!in_quote and a[i] == ' ') { - if (!last_was_space) { - _ = unquoted.append(std.heap.page_allocator, ' ') catch {}; - last_was_space = true; - } - } else { - _ = unquoted.append(std.heap.page_allocator, a[i]) catch {}; - last_was_space = false; + const a = args orelse { + try stdout.print("\n", .{}); + return; + }; + + var buf = std.ArrayList(u8){}; + defer buf.deinit(std.heap.page_allocator); + + var i: usize = 0; + var in_quote = false; + var quote_char: u8 = 0; + var last_space = false; + + while (i < a.len) : (i += 1) { + const c = a[i]; + + if (!in_quote and c == '\\' and i + 1 < a.len) { + i += 1; + try buf.append(std.heap.page_allocator, a[i]); + last_space = false; + } else if (!in_quote and (c == '\'' or c == '"')) { + in_quote = true; + quote_char = c; + last_space = false; + } else if (in_quote and c == quote_char) { + in_quote = false; + last_space = false; + } else if (in_quote and quote_char == '"' and c == '\\' and i + 1 < a.len) { + const next = a[i + 1]; + if (next == '"' or next == '\\') i += 1; + try buf.append(std.heap.page_allocator, a[i]); + last_space = false; + } else if (!in_quote and c == ' ') { + if (!last_space) { + try buf.append(std.heap.page_allocator, ' '); + last_space = true; } + } else { + try buf.append(std.heap.page_allocator, c); + last_space = false; } - - try stdout.print("{s}\n", .{unquoted.items}); - } else { - try stdout.print("\n", .{}); } + + try stdout.print("{s}\n", .{buf.items}); } pub fn executePwd(allocator: std.mem.Allocator, stdout: anytype) !void { @@ -73,30 +70,36 @@ pub fn executePwd(allocator: std.mem.Allocator, stdout: anytype) !void { } pub fn executeCd(allocator: std.mem.Allocator, stdout: anytype, args: ?[]const u8) !void { - if (args == null or args.?.len == 0) { + const a = args orelse { + try stdout.print("cd: missing argument\n", .{}); + return; + }; + + if (a.len == 0) { try stdout.print("cd: missing argument\n", .{}); return; } - const arg = std.mem.trim(u8, args.?, " "); + const arg = std.mem.trim(u8, a, " "); var path_buf: [512]u8 = undefined; - var dir: []const u8 = arg; - - if (std.mem.eql(u8, arg, "~")) { - const home = std.process.getEnvVarOwned(allocator, "HOME") catch { - try stdout.print("cd: HOME not set\n", .{}); - return; - }; - defer allocator.free(home); - dir = try std.fmt.bufPrint(&path_buf, "{s}", .{home}); - } else if (std.mem.startsWith(u8, arg, "~/")) { - const home = std.process.getEnvVarOwned(allocator, "HOME") catch { - try stdout.print("cd: HOME not set\n", .{}); - return; - }; - defer allocator.free(home); - dir = try std.fmt.bufPrint(&path_buf, "{s}/{s}", .{ home, arg[2..] }); - } + const dir = blk: { + if (std.mem.eql(u8, arg, "~")) { + const home = std.process.getEnvVarOwned(allocator, "HOME") catch { + try stdout.print("cd: HOME not set\n", .{}); + return; + }; + defer allocator.free(home); + break :blk try std.fmt.bufPrint(&path_buf, "{s}", .{home}); + } else if (std.mem.startsWith(u8, arg, "~/")) { + const home = std.process.getEnvVarOwned(allocator, "HOME") catch { + try stdout.print("cd: HOME not set\n", .{}); + return; + }; + defer allocator.free(home); + break :blk try std.fmt.bufPrint(&path_buf, "{s}/{s}", .{ home, arg[2..] }); + } + break :blk arg; + }; std.posix.chdir(dir) catch { try stdout.print("cd: {s}: No such file or directory\n", .{arg}); @@ -104,30 +107,25 @@ pub fn executeCd(allocator: std.mem.Allocator, stdout: anytype, args: ?[]const u } pub fn executeType(allocator: std.mem.Allocator, stdout: anytype, args: ?[]const u8) !void { - if (args) |a| { - if (isBuiltin(a)) { - try stdout.print("{s} is a shell builtin\n", .{a}); - } else if (try path.findInPath(allocator, a)) |cmd_path| { - defer allocator.free(cmd_path); - try stdout.print("{s} is {s}\n", .{ a, cmd_path }); - } else { - try stdout.print("{s}: not found\n", .{a}); - } + const a = args orelse return; + + if (isBuiltin(a)) { + try stdout.print("{s} is a shell builtin\n", .{a}); + } else if (try path.findInPath(allocator, a)) |cmd_path| { + defer allocator.free(cmd_path); + try stdout.print("{s} is {s}\n", .{ a, cmd_path }); + } else { + try stdout.print("{s}: not found\n", .{a}); } } pub fn executeHistory(stdout: anytype, history_list: []const []const u8, args: ?[]const u8) !void { - var limit: usize = history_list.len; + var limit = history_list.len; if (args) |a| { const trimmed = std.mem.trim(u8, a, " "); if (trimmed.len > 0) { - // Check if it's a -r flag for reading from file if (std.mem.startsWith(u8, trimmed, "-r ")) { - const file_path = std.mem.trim(u8, trimmed[3..], " "); - if (file_path.len == 0) { - try stdout.print("history: -r requires a file path\n", .{}); - } return; } @@ -144,22 +142,19 @@ pub fn executeHistory(stdout: anytype, history_list: []const []const u8, args: ? } } -pub fn executeHistoryRead(allocator: std.mem.Allocator, stdout: anytype, file_path: []const u8, history_list: *std.ArrayList([]const u8)) !void { - const file = std.fs.cwd().openFile(file_path, .{}) catch { - try stdout.print("history: cannot open {s}: error\n", .{file_path}); - return; - }; +fn readHistoryFile(allocator: std.mem.Allocator, file_path: []const u8, history_list: *std.ArrayList([]const u8)) !void { + const file = try std.fs.cwd().openFile(file_path, .{}); defer file.close(); const file_size = try file.getEndPos(); + if (file_size == 0) return; + const buffer = try allocator.alloc(u8, file_size); defer allocator.free(buffer); const bytes_read = try file.readAll(buffer); - if (bytes_read == 0) return; - - // Parse lines from file var line_iter = std.mem.splitScalar(u8, buffer[0..bytes_read], '\n'); + while (line_iter.next()) |line| { const trimmed = std.mem.trim(u8, line, " \r"); if (trimmed.len > 0) { @@ -169,11 +164,8 @@ pub fn executeHistoryRead(allocator: std.mem.Allocator, stdout: anytype, file_pa } } -pub fn executeHistoryWrite(stdout: anytype, file_path: []const u8, history_list: []const []const u8) !void { - const file = std.fs.cwd().createFile(file_path, .{}) catch { - try stdout.print("history: cannot write to {s}: error\n", .{file_path}); - return; - }; +fn writeHistoryFile(file_path: []const u8, history_list: []const []const u8) !void { + const file = try std.fs.cwd().createFile(file_path, .{}); defer file.close(); for (history_list) |cmd| { @@ -182,37 +174,36 @@ pub fn executeHistoryWrite(stdout: anytype, file_path: []const u8, history_list: } } +pub fn executeHistoryRead(allocator: std.mem.Allocator, stdout: anytype, file_path: []const u8, history_list: *std.ArrayList([]const u8)) !void { + readHistoryFile(allocator, file_path, history_list) catch { + try stdout.print("history: cannot open {s}: error\n", .{file_path}); + }; +} + +pub fn executeHistoryWrite(stdout: anytype, file_path: []const u8, history_list: []const []const u8) !void { + writeHistoryFile(file_path, history_list) catch { + try stdout.print("history: cannot write to {s}: error\n", .{file_path}); + }; +} + pub fn executeHistoryAppend(stdout: anytype, file_path: []const u8, history_list: []const []const u8, last_written_index: *usize) !void { - // Open file in append mode, or create if doesn't exist const file = std.fs.cwd().openFile(file_path, .{ .mode = .write_only }) catch |err| { if (err == error.FileNotFound) { - // Create the file if it doesn't exist - const new_file = std.fs.cwd().createFile(file_path, .{}) catch { + writeHistoryFile(file_path, history_list) catch { try stdout.print("history: cannot write to {s}: error\n", .{file_path}); return; }; - defer new_file.close(); - - // Write all commands to new file - for (history_list) |cmd| { - try new_file.writeAll(cmd); - try new_file.writeAll("\n"); - } last_written_index.* = history_list.len; return; - } else { - try stdout.print("history: cannot open {s}: error\n", .{file_path}); - return; } + try stdout.print("history: cannot open {s}: error\n", .{file_path}); + return; }; defer file.close(); - // Seek to end to append try file.seekFromEnd(0); - // Append only new commands (commands after last_written_index) - const start_idx = last_written_index.*; - for (history_list[start_idx..]) |cmd| { + for (history_list[last_written_index.*..]) |cmd| { try file.writeAll(cmd); try file.writeAll("\n"); } -- cgit v1.2.3