summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMatthias Andreas Benkard <code@mail.matthias.benkard.de>2025-08-14 16:51:17 +0200
committerMatthias Andreas Benkard <code@mail.matthias.benkard.de>2025-08-14 16:51:17 +0200
commit6141cdd85c5f01a001a1dc795226cd085a561383 (patch)
tree13496bd6e1400d1d829fa2c91594c77d39a3a769 /src
parentfa2069cc7e80d491cf19bb7ad9e11d97356882b9 (diff)
Factor out the external command case.
Diffstat (limited to 'src')
-rw-r--r--src/cmd.zig6
-rw-r--r--src/cmd/external.zig134
-rw-r--r--src/eval.zig118
3 files changed, 137 insertions, 121 deletions
diff --git a/src/cmd.zig b/src/cmd.zig
index 8f4c971..25c0711 100644
--- a/src/cmd.zig
+++ b/src/cmd.zig
@@ -26,6 +26,7 @@ const Date = @import("cmd/date.zig").Date;
const Time = @import("cmd/time.zig").Time;
const Sort = @import("cmd/sort.zig").Sort;
const Move = @import("cmd/move.zig").Move;
+const External = @import("cmd/external.zig").External;
pub const BuiltinCommand = union(enum) {
// File-oriented
@@ -155,10 +156,7 @@ pub const Command = union(enum) {
command: *Command,
redirects: ArrayList(Redirect),
},
- External: struct {
- program: []const u8,
- args: ArrayList([]const u8),
- },
+ External: External,
Builtin: BuiltinCommand,
Empty,
diff --git a/src/cmd/external.zig b/src/cmd/external.zig
new file mode 100644
index 0000000..92bfba6
--- /dev/null
+++ b/src/cmd/external.zig
@@ -0,0 +1,134 @@
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const ArrayList = std.ArrayList;
+const print = std.debug.print;
+
+const types = @import("./lib/types.zig");
+const CommandStatus = types.CommandStatus;
+const OutputCapture = types.OutputCapture;
+const InputSource = types.InputSource;
+
+pub const External = struct {
+ program: []const u8,
+ args: ArrayList([]const u8),
+
+ pub fn eval(external: External, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus {
+ // Try to execute external command
+ var child_args = ArrayList([]const u8).init(allocator);
+ defer child_args.deinit();
+
+ try child_args.append(external.program);
+ for (external.args.items) |arg| {
+ try child_args.append(arg);
+ }
+
+ var child = std.process.Child.init(child_args.items, allocator);
+
+ // Set up pipes for capturing output
+ child.stdin_behavior = if (input_source != null) .Pipe else .Inherit;
+ child.stdout_behavior = if (output_capture != null) .Pipe else .Inherit;
+ child.stderr_behavior = if (output_capture != null) .Pipe else .Inherit;
+
+ const spawn_result = child.spawn();
+ if (spawn_result) |_| {
+ // Spawn succeeded, continue with execution
+ } else |err| switch (err) {
+ error.FileNotFound => {
+ const error_msg = try std.fmt.allocPrint(allocator, "'{s}' is not recognized as an internal or external command,\noperable program or batch file.\n", .{external.program});
+ defer allocator.free(error_msg);
+
+ if (output_capture) |capture| {
+ try capture.write(error_msg);
+ } else {
+ print("{s}", .{error_msg});
+ }
+ return CommandStatus{ .Code = 1 };
+ },
+ error.AccessDenied => {
+ const error_msg = "Access is denied.\n";
+ if (output_capture) |capture| {
+ try capture.write(error_msg);
+ } else {
+ print("{s}", .{error_msg});
+ }
+ return CommandStatus{ .Code = 1 };
+ },
+ else => {
+ const error_msg = try std.fmt.allocPrint(allocator, "Cannot execute '{s}': {}\n", .{ external.program, err });
+ defer allocator.free(error_msg);
+
+ if (output_capture) |capture| {
+ try capture.write(error_msg);
+ } else {
+ print("{s}", .{error_msg});
+ }
+ return CommandStatus{ .Code = 1 };
+ },
+ }
+
+ // Handle input redirection
+ if (input_source) |source| {
+ if (child.stdin) |stdin| {
+ const writer = stdin.writer();
+
+ // Reset source position for reading
+ var temp_source = source.*;
+ temp_source.position = 0;
+
+ while (try temp_source.readLine(allocator)) |line| {
+ defer allocator.free(line);
+ try writer.print("{s}\n", .{line});
+ }
+ child.stdin.?.close();
+ child.stdin = null;
+ }
+ }
+
+ // Handle output capture
+ if (output_capture) |capture| {
+ // Read stdout
+ if (child.stdout) |stdout| {
+ var buffer: [4096]u8 = undefined;
+ while (true) {
+ const bytes_read = stdout.read(&buffer) catch break;
+ if (bytes_read == 0) break;
+ try capture.write(buffer[0..bytes_read]);
+ }
+ }
+
+ // Read stderr
+ if (child.stderr) |stderr| {
+ var buffer: [4096]u8 = undefined;
+ while (true) {
+ const bytes_read = stderr.read(&buffer) catch break;
+ if (bytes_read == 0) break;
+ try capture.write(buffer[0..bytes_read]);
+ }
+ }
+ }
+
+ // Wait for process to complete
+ const term = child.wait() catch |err| {
+ const error_msg = switch (err) {
+ error.FileNotFound => try std.fmt.allocPrint(allocator, "'{s}' is not recognized as an internal or external command,\noperable program or batch file.\n", .{external.program}),
+ else => try std.fmt.allocPrint(allocator, "Error waiting for command: {}\n", .{err}),
+ };
+ defer allocator.free(error_msg);
+
+ if (output_capture) |capture| {
+ try capture.write(error_msg);
+ } else {
+ print("{s}", .{error_msg});
+ }
+ return CommandStatus{ .Code = 1 };
+ };
+
+ // Return exit code
+ switch (term) {
+ .Exited => |code| return CommandStatus{ .Code = @intCast(code) },
+ .Signal => |_| return CommandStatus{ .Code = 1 },
+ .Stopped => |_| return CommandStatus{ .Code = 1 },
+ .Unknown => |_| return CommandStatus{ .Code = 1 },
+ }
+ }
+};
diff --git a/src/eval.zig b/src/eval.zig
index 8676516..1568411 100644
--- a/src/eval.zig
+++ b/src/eval.zig
@@ -121,123 +121,7 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
},
.External => |external| {
- // Try to execute external command
- var child_args = ArrayList([]const u8).init(allocator);
- defer child_args.deinit();
-
- try child_args.append(external.program);
- for (external.args.items) |arg| {
- try child_args.append(arg);
- }
-
- var child = std.process.Child.init(child_args.items, allocator);
-
- // Set up pipes for capturing output
- child.stdin_behavior = if (input_source != null) .Pipe else .Inherit;
- child.stdout_behavior = if (output_capture != null) .Pipe else .Inherit;
- child.stderr_behavior = if (output_capture != null) .Pipe else .Inherit;
-
- const spawn_result = child.spawn();
- if (spawn_result) |_| {
- // Spawn succeeded, continue with execution
- } else |err| switch (err) {
- error.FileNotFound => {
- const error_msg = try std.fmt.allocPrint(allocator, "'{s}' is not recognized as an internal or external command,\noperable program or batch file.\n", .{external.program});
- defer allocator.free(error_msg);
-
- if (output_capture) |capture| {
- try capture.write(error_msg);
- } else {
- print("{s}", .{error_msg});
- }
- return CommandStatus{ .Code = 1 };
- },
- error.AccessDenied => {
- const error_msg = "Access is denied.\n";
- if (output_capture) |capture| {
- try capture.write(error_msg);
- } else {
- print("{s}", .{error_msg});
- }
- return CommandStatus{ .Code = 1 };
- },
- else => {
- const error_msg = try std.fmt.allocPrint(allocator, "Cannot execute '{s}': {}\n", .{ external.program, err });
- defer allocator.free(error_msg);
-
- if (output_capture) |capture| {
- try capture.write(error_msg);
- } else {
- print("{s}", .{error_msg});
- }
- return CommandStatus{ .Code = 1 };
- },
- }
-
- // Handle input redirection
- if (input_source) |source| {
- if (child.stdin) |stdin| {
- const writer = stdin.writer();
-
- // Reset source position for reading
- var temp_source = source.*;
- temp_source.position = 0;
-
- while (try temp_source.readLine(allocator)) |line| {
- defer allocator.free(line);
- try writer.print("{s}\n", .{line});
- }
- child.stdin.?.close();
- child.stdin = null;
- }
- }
-
- // Handle output capture
- if (output_capture) |capture| {
- // Read stdout
- if (child.stdout) |stdout| {
- var buffer: [4096]u8 = undefined;
- while (true) {
- const bytes_read = stdout.read(&buffer) catch break;
- if (bytes_read == 0) break;
- try capture.write(buffer[0..bytes_read]);
- }
- }
-
- // Read stderr
- if (child.stderr) |stderr| {
- var buffer: [4096]u8 = undefined;
- while (true) {
- const bytes_read = stderr.read(&buffer) catch break;
- if (bytes_read == 0) break;
- try capture.write(buffer[0..bytes_read]);
- }
- }
- }
-
- // Wait for process to complete
- const term = child.wait() catch |err| {
- const error_msg = switch (err) {
- error.FileNotFound => try std.fmt.allocPrint(allocator, "'{s}' is not recognized as an internal or external command,\noperable program or batch file.\n", .{external.program}),
- else => try std.fmt.allocPrint(allocator, "Error waiting for command: {}\n", .{err}),
- };
- defer allocator.free(error_msg);
-
- if (output_capture) |capture| {
- try capture.write(error_msg);
- } else {
- print("{s}", .{error_msg});
- }
- return CommandStatus{ .Code = 1 };
- };
-
- // Return exit code
- switch (term) {
- .Exited => |code| return CommandStatus{ .Code = @intCast(code) },
- .Signal => |_| return CommandStatus{ .Code = 1 },
- .Stopped => |_| return CommandStatus{ .Code = 1 },
- .Unknown => |_| return CommandStatus{ .Code = 1 },
- }
+ return external.eval(allocator, output_capture, input_source);
},
.Redirect => |redirect| {