summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLucas Faria Mendes <lucas.oliveira1676@etec.sp.gov.br>2025-12-05 06:31:01 +0000
committerLucas Faria Mendes <lucas.oliveira1676@etec.sp.gov.br>2025-12-05 06:31:01 +0000
commit6f1c762e789cd746e20cd7597aab0316d2c2fcd9 (patch)
tree36ee2dd94e54eea37230f61f574a99054aefdf88
parent5a676cada89ccdd33de496e2a1a1a130a26894f6 (diff)
downloadshell-zig-6f1c762e789cd746e20cd7597aab0316d2c2fcd9.tar.gz
shell-zig-6f1c762e789cd746e20cd7597aab0316d2c2fcd9.zip
codecrafters submit [skip ci]
-rw-r--r--src/executor.zig39
-rw-r--r--src/main.zig2
-rw-r--r--src/parser.zig39
-rw-r--r--src/shell.zig60
4 files changed, 134 insertions, 6 deletions
diff --git a/src/executor.zig b/src/executor.zig
index 839fcda..a53ade8 100644
--- a/src/executor.zig
+++ b/src/executor.zig
@@ -27,3 +27,42 @@ pub fn runExternalProgram(allocator: std.mem.Allocator, program_path: []const u8
_ = std.posix.waitpid(pid, 0);
}
}
+
+pub fn runExternalProgramWithRedirect(allocator: std.mem.Allocator, program_path: []const u8, argv: []const []const u8, output_file: []const u8) !void {
+ const argv_z = try allocator.allocSentinel(?[*:0]const u8, argv.len, null);
+ defer allocator.free(argv_z);
+
+ for (argv, 0..) |arg, i| {
+ argv_z[i] = (try allocator.dupeZ(u8, arg)).ptr;
+ }
+ defer {
+ for (argv_z[0..argv.len]) |arg_ptr| {
+ if (arg_ptr) |ptr| allocator.free(std.mem.span(ptr));
+ }
+ }
+
+ const program_path_z = try allocator.dupeZ(u8, program_path);
+ defer allocator.free(program_path_z);
+
+ const output_file_z = try allocator.dupeZ(u8, output_file);
+ defer allocator.free(output_file_z);
+
+ const pid = try std.posix.fork();
+
+ if (pid == 0) {
+ const cwd = std.fs.cwd();
+ const fd = cwd.createFile(output_file, .{}) catch {
+ std.posix.exit(1);
+ };
+ defer fd.close();
+
+ try std.posix.dup2(fd.handle, 1);
+
+ _ = std.posix.execveZ(program_path_z, argv_z, std.c.environ) catch {
+ std.posix.exit(1);
+ };
+ unreachable;
+ } else {
+ _ = std.posix.waitpid(pid, 0);
+ }
+}
diff --git a/src/main.zig b/src/main.zig
index a0959ed..91ae759 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -20,7 +20,7 @@ pub fn main() !void {
const command = try stdin.takeDelimiter('\n');
if (command) |cmd| {
const parsed = parser.parseCommand(cmd);
- const result = try shell.executeCommand(allocator, stdout, parsed.name, parsed.args);
+ const result = try shell.executeCommand(allocator, stdout, parsed.name, parsed.args, parsed.output_redirect);
if (result == .exit_shell) break;
} else {
diff --git a/src/parser.zig b/src/parser.zig
index 13c3b42..680eadb 100644
--- a/src/parser.zig
+++ b/src/parser.zig
@@ -3,6 +3,7 @@ const std = @import("std");
pub const ParsedCommand = struct {
name: []const u8,
args: ?[]const u8,
+ output_redirect: ?[]const u8,
};
pub fn parseCommand(input: []const u8) ParsedCommand {
@@ -13,7 +14,7 @@ pub fn parseCommand(input: []const u8) ParsedCommand {
while (i < input.len and input[i] == ' ') : (i += 1) {}
if (i >= input.len) {
- return .{ .name = "", .args = null };
+ return .{ .name = "", .args = null, .output_redirect = null };
}
if (input[i] == '\'' or input[i] == '"') {
@@ -36,10 +37,42 @@ pub fn parseCommand(input: []const u8) ParsedCommand {
while (i < input.len and input[i] == ' ') : (i += 1) {}
+ var redirect_pos: ?usize = null;
+ var j = i;
+ while (j < input.len) : (j += 1) {
+ if (input[j] == '>' or (input[j] == '1' and j + 1 < input.len and input[j + 1] == '>')) {
+ redirect_pos = j;
+ break;
+ }
+ }
+
const cmd_name = cmd_buf.toOwnedSlice(std.heap.page_allocator) catch "";
- const args = if (i < input.len) input[i..] else null;
+ var args: ?[]const u8 = null;
+ var output_redirect: ?[]const u8 = null;
+
+ if (redirect_pos) |pos| {
+ if (pos > i) args = input[i..pos];
+
+ var k = pos;
+ if (input[k] == '1') k += 1;
+ if (k < input.len and input[k] == '>') k += 1;
+
+ while (k < input.len and input[k] == ' ') : (k += 1) {}
+
+ if (k < input.len) {
+ var redir_buf = std.ArrayList(u8){};
+ defer redir_buf.deinit(std.heap.page_allocator);
+
+ while (k < input.len and input[k] != ' ') : (k += 1) {
+ _ = redir_buf.append(std.heap.page_allocator, input[k]) catch {};
+ }
+ output_redirect = redir_buf.toOwnedSlice(std.heap.page_allocator) catch null;
+ }
+ } else if (i < input.len) {
+ args = input[i..];
+ }
- return .{ .name = cmd_name, .args = args };
+ return .{ .name = cmd_name, .args = args, .output_redirect = output_redirect };
}
pub fn parseArgs(allocator: std.mem.Allocator, cmd_name: []const u8, args_str: ?[]const u8) ![]const []const u8 {
diff --git a/src/shell.zig b/src/shell.zig
index e42c131..c95d9d0 100644
--- a/src/shell.zig
+++ b/src/shell.zig
@@ -9,11 +9,63 @@ pub fn executeCommand(
stdout: anytype,
cmd_name: []const u8,
args: ?[]const u8,
+ output_redirect: ?[]const u8,
) !builtins.CommandResult {
if (std.mem.eql(u8, cmd_name, "exit")) return builtins.executeExit();
if (std.mem.eql(u8, cmd_name, "echo")) {
- try builtins.executeEcho(stdout, args);
+ if (output_redirect) |file| {
+ const fd = try std.fs.cwd().createFile(file, .{});
+ defer fd.close();
+
+ if (args) |a| {
+ var i: usize = 0;
+ var in_quote = false;
+ var quote_char: u8 = 0;
+ var unquoted = std.ArrayList(u8){};
+ defer unquoted.deinit(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(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(allocator, a[i]) catch {};
+ } else {
+ _ = unquoted.append(allocator, a[i]) catch {};
+ }
+ last_was_space = false;
+ } else if (!in_quote and a[i] == ' ') {
+ if (!last_was_space) {
+ _ = unquoted.append(allocator, ' ') catch {};
+ last_was_space = true;
+ }
+ } else {
+ _ = unquoted.append(allocator, a[i]) catch {};
+ last_was_space = false;
+ }
+ }
+
+ try fd.writeAll(unquoted.items);
+ try fd.writeAll("\n");
+ } else {
+ try fd.writeAll("\n");
+ }
+ } else {
+ try builtins.executeEcho(stdout, args);
+ }
return .continue_loop;
}
@@ -38,7 +90,11 @@ pub fn executeCommand(
const argv = try parser.parseArgs(allocator, cmd_name, args);
defer allocator.free(argv);
- try executor.runExternalProgram(allocator, program_path, argv);
+ if (output_redirect) |file| {
+ try executor.runExternalProgramWithRedirect(allocator, program_path, argv, file);
+ } else {
+ try executor.runExternalProgram(allocator, program_path, argv);
+ }
return .continue_loop;
}