Add piping, Add type builtin, Add command -v builtin
This commit is contained in:
+17
-16
@@ -1,25 +1,24 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const parser = @import("../shell/parser.zig");
|
const parser = @import("../shell/parser.zig");
|
||||||
|
|
||||||
const cd = @import("cmds/cd.zig");
|
|
||||||
const echo = @import("cmds/echo.zig");
|
|
||||||
const exit = @import("cmds/exit.zig");
|
|
||||||
const pwd = @import("cmds/pwd.zig");
|
|
||||||
const run = @import("cmds/run.zig");
|
|
||||||
|
|
||||||
const types = @import("types.zig");
|
const types = @import("types.zig");
|
||||||
|
|
||||||
const CommandFn = *const fn (types.BuiltinCommandContext) anyerror!void;
|
const CommandFn = *const fn (types.CommandContext) anyerror!void;
|
||||||
|
|
||||||
const commands = std.StaticStringMap(CommandFn).initComptime(.{
|
const commands = std.StaticStringMap(CommandFn).initComptime(.{
|
||||||
.{ "cd", cd.execute },
|
.{ "cd", @import("cmds/cd.zig").execute },
|
||||||
.{ "pwd", pwd.execute },
|
.{ "pwd", @import("cmds/pwd.zig").execute },
|
||||||
.{ "run", run.execute },
|
.{ "type", @import("cmds/type.zig").execute },
|
||||||
.{ "echo", echo.execute },
|
.{ "command", @import("cmds/command.zig").execute },
|
||||||
.{ "exit", exit.execute },
|
.{ "run", @import("cmds/run.zig").execute },
|
||||||
|
.{ "echo", @import("cmds/echo.zig").execute },
|
||||||
|
.{ "exit", @import("cmds/exit.zig").execute },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
pub fn find(name: []const u8) ?CommandFn {
|
||||||
|
return commands.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn execute(
|
pub fn execute(
|
||||||
io: std.Io,
|
io: std.Io,
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
@@ -29,9 +28,7 @@ pub fn execute(
|
|||||||
if (command.argv.len == 0)
|
if (command.argv.len == 0)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
const name = command.argv[0];
|
const execute_fn = commands.get(command.argv[0]) orelse
|
||||||
|
|
||||||
const execute_fn = commands.get(name) orelse
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
try execute_fn(.{
|
try execute_fn(.{
|
||||||
@@ -39,6 +36,10 @@ pub fn execute(
|
|||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.shell = shell,
|
.shell = shell,
|
||||||
.argv = command.argv,
|
.argv = command.argv,
|
||||||
|
|
||||||
|
.stdin = std.Io.File.stdin(),
|
||||||
|
.stdout = std.Io.File.stdout(),
|
||||||
|
.stderr = std.Io.File.stderr(),
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ const std = @import("std");
|
|||||||
|
|
||||||
const types = @import("../types.zig");
|
const types = @import("../types.zig");
|
||||||
|
|
||||||
pub fn execute(ctx: types.BuiltinCommandContext) !void {
|
pub fn execute(ctx: types.CommandContext) !void {
|
||||||
const path = if (ctx.argv.len >= 2)
|
const path = if (ctx.argv.len >= 2)
|
||||||
ctx.argv[1]
|
ctx.argv[1]
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const resolver = @import("../resolver.zig");
|
||||||
|
const types = @import("../types.zig");
|
||||||
|
|
||||||
|
pub fn execute(ctx: types.CommandContext) !void {
|
||||||
|
if (ctx.argv.len < 2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!std.mem.eql(u8, ctx.argv[1], "-v"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ctx.argv.len < 3) {
|
||||||
|
std.debug.print("xsh: command: -v: missing command name\n", .{});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = ctx.argv[2];
|
||||||
|
|
||||||
|
const result = try resolver.resolve(ctx, name) orelse {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (result) {
|
||||||
|
.builtin => {
|
||||||
|
std.debug.print("{s}\n", .{name});
|
||||||
|
},
|
||||||
|
|
||||||
|
.alias => |value| {
|
||||||
|
std.debug.print("{s}\n", .{value});
|
||||||
|
},
|
||||||
|
|
||||||
|
.executable => |path| {
|
||||||
|
defer ctx.allocator.free(path);
|
||||||
|
|
||||||
|
std.debug.print("{s}\n", .{path});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,14 +2,14 @@ const std = @import("std");
|
|||||||
|
|
||||||
const types = @import("../types.zig");
|
const types = @import("../types.zig");
|
||||||
|
|
||||||
pub fn execute(ctx: types.BuiltinCommandContext) !void {
|
pub fn execute(ctx: types.CommandContext) !void {
|
||||||
for (ctx.argv[1..], 0..) |argument, index| {
|
for (ctx.argv[1..], 0..) |argument, index| {
|
||||||
if (index != 0) {
|
if (index != 0) {
|
||||||
try std.Io.File.stdout().writeStreamingAll(ctx.io, " ");
|
try ctx.stdout.writeStreamingAll(ctx.io, " ");
|
||||||
}
|
}
|
||||||
|
|
||||||
try std.Io.File.stdout().writeStreamingAll(ctx.io, argument);
|
try ctx.stdout.writeStreamingAll(ctx.io, argument);
|
||||||
}
|
}
|
||||||
|
|
||||||
try std.Io.File.stdout().writeStreamingAll(ctx.io, "\n");
|
try ctx.stdout.writeStreamingAll(ctx.io, "\n");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
const types = @import("../types.zig");
|
const types = @import("../types.zig");
|
||||||
|
|
||||||
pub fn execute(ctx: types.BuiltinCommandContext) !void {
|
pub fn execute(ctx: types.CommandContext) !void {
|
||||||
ctx.shell.running = false;
|
ctx.shell.running = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ const std = @import("std");
|
|||||||
|
|
||||||
const types = @import("../types.zig");
|
const types = @import("../types.zig");
|
||||||
|
|
||||||
pub fn execute(ctx: types.BuiltinCommandContext) !void {
|
pub fn execute(ctx: types.CommandContext) !void {
|
||||||
const path = try std.process.currentPathAlloc(ctx.io, ctx.allocator);
|
const path = try std.process.currentPathAlloc(ctx.io, ctx.allocator);
|
||||||
defer ctx.allocator.free(path);
|
defer ctx.allocator.free(path);
|
||||||
|
|
||||||
try std.Io.File.stdout().writeStreamingAll(ctx.io, path);
|
try ctx.stdout.writeStreamingAll(ctx.io, path);
|
||||||
try std.Io.File.stdout().writeStreamingAll(ctx.io, "\n");
|
try ctx.stdout.writeStreamingAll(ctx.io, "\n");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ const script = @import("../../script/script.zig");
|
|||||||
|
|
||||||
const types = @import("../types.zig");
|
const types = @import("../types.zig");
|
||||||
|
|
||||||
pub fn execute(ctx: types.BuiltinCommandContext) !void {
|
pub fn execute(ctx: types.CommandContext) !void {
|
||||||
if (ctx.argv.len < 2) {
|
if (ctx.argv.len < 2) {
|
||||||
std.debug.print("xsh: run: missing script path\n", .{});
|
std.debug.print("xsh: run: missing script path\n", .{});
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const resolver = @import("../resolver.zig");
|
||||||
|
const types = @import("../types.zig");
|
||||||
|
|
||||||
|
pub fn execute(ctx: types.CommandContext) !void {
|
||||||
|
if (ctx.argv.len < 2) {
|
||||||
|
std.debug.print("xsh: type: missing command name\n", .{});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx.argv.len > 2) {
|
||||||
|
std.debug.print("xsh: type: too many arguments\n", .{});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = ctx.argv[1];
|
||||||
|
|
||||||
|
const result = try resolver.resolve(
|
||||||
|
ctx,
|
||||||
|
name,
|
||||||
|
) orelse {
|
||||||
|
std.debug.print("xsh: type: {s}: not found\n", .{name});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (result) {
|
||||||
|
.builtin => {
|
||||||
|
std.debug.print("{s} is a shell builtin\n", .{name});
|
||||||
|
},
|
||||||
|
|
||||||
|
.alias => |value| {
|
||||||
|
std.debug.print(
|
||||||
|
"{s} is aliased to '{s}'\n",
|
||||||
|
.{ name, value },
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
.executable => |path| {
|
||||||
|
defer ctx.allocator.free(path);
|
||||||
|
|
||||||
|
std.debug.print(
|
||||||
|
"{s} is {s}\n",
|
||||||
|
.{ name, path },
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const builtin = @import("builtin.zig");
|
||||||
|
const types = @import("types.zig");
|
||||||
|
|
||||||
|
pub fn resolve(
|
||||||
|
ctx: types.CommandContext,
|
||||||
|
name: []const u8,
|
||||||
|
) !?types.ResolvedCommand {
|
||||||
|
if (builtin.find(name) != null)
|
||||||
|
return .builtin;
|
||||||
|
|
||||||
|
if (std.mem.indexOfAny(u8, name, "/\\") != null)
|
||||||
|
return .{
|
||||||
|
.executable = try ctx.allocator.dupe(u8, name),
|
||||||
|
};
|
||||||
|
|
||||||
|
const path = ctx.shell.environ.get("PATH") orelse
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (@import("builtin").os.tag == .windows) {
|
||||||
|
const pathext = ctx.shell.environ.get("PATHEXT") orelse
|
||||||
|
".COM;.EXE;.BAT;.CMD";
|
||||||
|
|
||||||
|
var dirs = std.mem.splitScalar(u8, path, ';');
|
||||||
|
|
||||||
|
while (dirs.next()) |dir| {
|
||||||
|
if (dir.len == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Try the exact name first.
|
||||||
|
{
|
||||||
|
const candidate = try std.fs.path.join(
|
||||||
|
ctx.allocator,
|
||||||
|
&.{ dir, name },
|
||||||
|
);
|
||||||
|
|
||||||
|
if (std.Io.Dir.accessAbsolute(
|
||||||
|
ctx.io,
|
||||||
|
candidate,
|
||||||
|
.{},
|
||||||
|
)) {
|
||||||
|
return .{ .executable = candidate };
|
||||||
|
} else |_| {
|
||||||
|
ctx.allocator.free(candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then try PATHEXT.
|
||||||
|
var extensions = std.mem.splitScalar(u8, pathext, ';');
|
||||||
|
|
||||||
|
while (extensions.next()) |ext| {
|
||||||
|
const candidate_name = try std.fmt.allocPrint(
|
||||||
|
ctx.allocator,
|
||||||
|
"{s}{s}",
|
||||||
|
.{ name, ext },
|
||||||
|
);
|
||||||
|
defer ctx.allocator.free(candidate_name);
|
||||||
|
|
||||||
|
const candidate = try std.fs.path.join(
|
||||||
|
ctx.allocator,
|
||||||
|
&.{ dir, candidate_name },
|
||||||
|
);
|
||||||
|
|
||||||
|
if (std.Io.Dir.accessAbsolute(
|
||||||
|
ctx.io,
|
||||||
|
candidate,
|
||||||
|
.{},
|
||||||
|
)) {
|
||||||
|
return .{ .executable = candidate };
|
||||||
|
} else |_| {
|
||||||
|
ctx.allocator.free(candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var dirs = std.mem.splitScalar(u8, path, ':');
|
||||||
|
|
||||||
|
while (dirs.next()) |dir| {
|
||||||
|
if (dir.len == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const candidate = try std.fs.path.join(
|
||||||
|
ctx.allocator,
|
||||||
|
&.{ dir, name },
|
||||||
|
);
|
||||||
|
|
||||||
|
if (std.Io.Dir.accessAbsolute(
|
||||||
|
ctx.io,
|
||||||
|
candidate,
|
||||||
|
.{},
|
||||||
|
)) {
|
||||||
|
return .{ .executable = candidate };
|
||||||
|
} else |_| {
|
||||||
|
ctx.allocator.free(candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
+19
-1
@@ -6,9 +6,27 @@ pub const Shell = struct {
|
|||||||
environ: *const std.process.Environ.Map,
|
environ: *const std.process.Environ.Map,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const BuiltinCommandContext = struct {
|
pub const CommandContext = struct {
|
||||||
io: std.Io,
|
io: std.Io,
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
shell: *Shell,
|
shell: *Shell,
|
||||||
argv: []const []const u8,
|
argv: []const []const u8,
|
||||||
|
|
||||||
|
stdin: std.Io.File,
|
||||||
|
stdout: std.Io.File,
|
||||||
|
stderr: std.Io.File,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const ResolvedCommand = union(enum) {
|
||||||
|
builtin,
|
||||||
|
alias: []const u8,
|
||||||
|
executable: []const u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Command = struct {
|
||||||
|
argv: []const []const u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Pipeline = struct {
|
||||||
|
commands: []const Command,
|
||||||
};
|
};
|
||||||
|
|||||||
+8
-3
@@ -56,15 +56,20 @@ pub fn main(init: std.process.Init) !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn runPipeline(allocator: std.mem.Allocator, io: std.Io, shell: *builtin_types.Shell, source: []const u8) !void {
|
fn runPipeline(
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
io: std.Io,
|
||||||
|
shell: *builtin_types.Shell,
|
||||||
|
source: []const u8,
|
||||||
|
) !void {
|
||||||
const tokens = try lexer.lex(allocator, source);
|
const tokens = try lexer.lex(allocator, source);
|
||||||
const command = try parser.parse(allocator, tokens.items);
|
const pipeline = try parser.parse(allocator, tokens.items);
|
||||||
|
|
||||||
try execute.run(
|
try execute.run(
|
||||||
io,
|
io,
|
||||||
allocator,
|
allocator,
|
||||||
shell,
|
shell,
|
||||||
command,
|
pipeline,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+79
-6
@@ -9,17 +9,90 @@ pub fn run(
|
|||||||
io: std.Io,
|
io: std.Io,
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
shell: *builtin_types.Shell,
|
shell: *builtin_types.Shell,
|
||||||
command: parser.Command,
|
pipeline: parser.Pipeline,
|
||||||
) !void {
|
) !void {
|
||||||
if (try builtin.execute(io, allocator, shell, command))
|
if (pipeline.commands.len == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var child = try std.process.spawn(
|
if (pipeline.commands.len == 1) {
|
||||||
|
const command = pipeline.commands[0];
|
||||||
|
|
||||||
|
if (try builtin.execute(
|
||||||
io,
|
io,
|
||||||
.{
|
allocator,
|
||||||
|
shell,
|
||||||
|
command,
|
||||||
|
))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var child = try std.process.spawn(io, .{
|
||||||
.argv = command.argv,
|
.argv = command.argv,
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
|
||||||
_ = try child.wait(io);
|
_ = try child.wait(io);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pipeline.commands.len != 2)
|
||||||
|
return error.UnsupportedPipeline;
|
||||||
|
|
||||||
|
const left = pipeline.commands[0];
|
||||||
|
const right = pipeline.commands[1];
|
||||||
|
|
||||||
|
var left_child = try std.process.spawn(io, .{
|
||||||
|
.argv = left.argv,
|
||||||
|
.stdout = .pipe,
|
||||||
|
});
|
||||||
|
defer {
|
||||||
|
if (left_child.stdout) |stdout|
|
||||||
|
stdout.close(io);
|
||||||
|
}
|
||||||
|
|
||||||
|
var right_child = try std.process.spawn(io, .{
|
||||||
|
.argv = right.argv,
|
||||||
|
.stdin = .pipe,
|
||||||
|
});
|
||||||
|
defer {
|
||||||
|
if (right_child.stdin) |stdin|
|
||||||
|
stdin.close(io);
|
||||||
|
}
|
||||||
|
|
||||||
|
const left_stdout = left_child.stdout orelse
|
||||||
|
return error.MissingPipe;
|
||||||
|
|
||||||
|
const right_stdin = right_child.stdin orelse
|
||||||
|
return error.MissingPipe;
|
||||||
|
|
||||||
|
var buffer: [4096]u8 = undefined;
|
||||||
|
|
||||||
|
var buffers = [_][]u8{
|
||||||
|
&buffer,
|
||||||
|
};
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const n = left_stdout.readStreaming(
|
||||||
|
io,
|
||||||
|
&buffers,
|
||||||
|
) catch |err| switch (err) {
|
||||||
|
error.EndOfStream => break,
|
||||||
|
else => return err,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (n == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
try right_stdin.writeStreamingAll(
|
||||||
|
io,
|
||||||
|
buffer[0..n],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
right_stdin.close(io);
|
||||||
|
right_child.stdin = null;
|
||||||
|
|
||||||
|
left_stdout.close(io);
|
||||||
|
left_child.stdout = null;
|
||||||
|
|
||||||
|
_ = try left_child.wait(io);
|
||||||
|
_ = try right_child.wait(io);
|
||||||
}
|
}
|
||||||
|
|||||||
+58
-7
@@ -9,18 +9,69 @@ pub fn lex(
|
|||||||
source: []const u8,
|
source: []const u8,
|
||||||
) !std.ArrayList(Token) {
|
) !std.ArrayList(Token) {
|
||||||
var tokens: std.ArrayList(Token) = .empty;
|
var tokens: std.ArrayList(Token) = .empty;
|
||||||
|
var current: std.ArrayList(u8) = .empty;
|
||||||
|
|
||||||
var iterator = std.mem.tokenizeAny(
|
var quote: ?u8 = null;
|
||||||
u8,
|
|
||||||
source,
|
|
||||||
" \t\r\n",
|
|
||||||
);
|
|
||||||
|
|
||||||
while (iterator.next()) |word| {
|
for (source) |char| {
|
||||||
|
if (quote) |q| {
|
||||||
|
if (char == q) {
|
||||||
|
quote = null;
|
||||||
|
} else {
|
||||||
|
try current.append(allocator, char);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (char) {
|
||||||
|
'\'', '"' => {
|
||||||
|
quote = char;
|
||||||
|
},
|
||||||
|
|
||||||
|
' ', '\t', '\r', '\n' => {
|
||||||
|
if (current.items.len != 0) {
|
||||||
try tokens.append(
|
try tokens.append(
|
||||||
allocator,
|
allocator,
|
||||||
.{
|
.{
|
||||||
.text = word,
|
.text = try current.toOwnedSlice(allocator),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'|' => {
|
||||||
|
if (current.items.len != 0) {
|
||||||
|
try tokens.append(
|
||||||
|
allocator,
|
||||||
|
.{
|
||||||
|
.text = try current.toOwnedSlice(allocator),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try tokens.append(
|
||||||
|
allocator,
|
||||||
|
.{
|
||||||
|
.text = "|",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
else => {
|
||||||
|
try current.append(allocator, char);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (quote != null)
|
||||||
|
return error.UnterminatedQuote;
|
||||||
|
|
||||||
|
if (current.items.len != 0) {
|
||||||
|
try tokens.append(
|
||||||
|
allocator,
|
||||||
|
.{
|
||||||
|
.text = try current.toOwnedSlice(allocator),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+38
-9
@@ -6,24 +6,53 @@ pub const Command = struct {
|
|||||||
argv: []const []const u8,
|
argv: []const []const u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const Pipeline = struct {
|
||||||
|
commands: []const Command,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn parse(
|
pub fn parse(
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
tokens: []const lexer.Token,
|
tokens: []const lexer.Token,
|
||||||
) !Command {
|
) !Pipeline {
|
||||||
if (tokens.len == 0) {
|
if (tokens.len == 0)
|
||||||
return error.EmptyCommand;
|
return error.EmptyCommand;
|
||||||
}
|
|
||||||
|
|
||||||
const argv = try allocator.alloc(
|
var commands: std.ArrayList(Command) = .empty;
|
||||||
[]const u8,
|
var argv: std.ArrayList([]const u8) = .empty;
|
||||||
tokens.len,
|
|
||||||
|
for (tokens) |token| {
|
||||||
|
if (std.mem.eql(u8, token.text, "|")) {
|
||||||
|
if (argv.items.len == 0)
|
||||||
|
return error.EmptyCommand;
|
||||||
|
|
||||||
|
try commands.append(
|
||||||
|
allocator,
|
||||||
|
.{
|
||||||
|
.argv = try argv.toOwnedSlice(allocator),
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
for (tokens, 0..) |token, index| {
|
argv = .empty;
|
||||||
argv[index] = token.text;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try argv.append(
|
||||||
|
allocator,
|
||||||
|
token.text,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argv.items.len == 0)
|
||||||
|
return error.EmptyCommand;
|
||||||
|
|
||||||
|
try commands.append(
|
||||||
|
allocator,
|
||||||
|
.{
|
||||||
|
.argv = try argv.toOwnedSlice(allocator),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.argv = argv,
|
.commands = try commands.toOwnedSlice(allocator),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user