summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLucas Faria Mendes <lucas.oliveira1676@etec.sp.gov.br>2025-12-05 08:57:23 +0000
committerLucas Faria Mendes <lucas.oliveira1676@etec.sp.gov.br>2025-12-05 08:57:23 +0000
commit92f501b7b282cfc8391898a67e1bd95fa06631ac (patch)
tree6fc4ac4c8a3a7bb349f822b046e1ceb0746f395e
parentc8dfd5c5899667b39a5c9e55722c1e15c220c6dd (diff)
downloadshell-zig-92f501b7b282cfc8391898a67e1bd95fa06631ac.tar.gz
shell-zig-92f501b7b282cfc8391898a67e1bd95fa06631ac.zip
codecrafters submit [skip ci]
-rw-r--r--src/builtins.zig215
1 files changed, 103 insertions, 112 deletions
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");
}