1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
|
const std = @import("std");
const path = @import("path.zig");
const BUILTINS = [_][]const u8{ "exit", "echo", "type", "pwd", "cd" };
pub const CommandResult = enum {
continue_loop,
exit_shell,
};
pub fn isBuiltin(cmd_name: []const u8) bool {
for (BUILTINS) |builtin| {
if (std.mem.eql(u8, cmd_name, builtin)) return true;
}
return false;
}
pub 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 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;
}
}
try stdout.print("{s}\n", .{unquoted.items});
} else {
try stdout.print("\n", .{});
}
}
pub fn executePwd(allocator: std.mem.Allocator, stdout: anytype) !void {
const cwd = try std.fs.cwd().realpathAlloc(allocator, ".");
defer allocator.free(cwd);
try stdout.print("{s}\n", .{cwd});
}
pub fn executeCd(allocator: std.mem.Allocator, stdout: anytype, args: ?[]const u8) !void {
if (args == null or args.?.len == 0) {
try stdout.print("cd: missing argument\n", .{});
return;
}
const arg = std.mem.trim(u8, args.?, " ");
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..] });
}
std.posix.chdir(dir) catch {
try stdout.print("cd: {s}: No such file or directory\n", .{arg});
};
}
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});
}
}
}
|