diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/executor.zig | 39 | ||||
| -rw-r--r-- | src/main.zig | 2 | ||||
| -rw-r--r-- | src/parser.zig | 39 | ||||
| -rw-r--r-- | src/shell.zig | 60 |
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; } |