feat: add option for custom shell

This commit is contained in:
LordMZTE 2023-07-06 21:36:48 +02:00
parent 5c20bd47f2
commit d295666572
Signed by: LordMZTE
GPG key ID: B64802DC33A64FF6
5 changed files with 62 additions and 14 deletions

View file

@ -107,6 +107,13 @@ Colours can be configured individually in the \fBcolours\fR section.
Defaults to default.
.RE
.
.P
\fBshell\fR = <shell command>;
.RS
The shell command used to execute commands. Defaults to /bin/sh -c.
The value is treated as space-separated argv for the shell.
Must be an absolut path, no $PATH-lookups are performed.
.RE
.
.SH SECTION: keybinds
.P

View file

@ -347,6 +347,7 @@ size_format: SizeFormat = .iec,
navmode: NavMode = .descend,
mouse_support: bool = false,
theme: Theme = themes.default,
shell: []const [:0]const u8 = &.{ "/bin/sh", "-c" },
pub fn init(self: *Self) !void {
// This arena will hold all configuration values. Note that we don't need
@ -587,6 +588,37 @@ fn assignMain(self: *Self, variable: []const u8, value: []const u8, path: []cons
}
log.err("{s}:{}: Unknown theme: '{s}'", .{ path, line, value });
return error.BadConfig;
} else if (mem.eql(u8, variable, "shell")) {
// We do simple split-based parsing of the arguments here, this should be good
// enough for the shell command. This employs an appraoch where the split is performed
// twice to count the number of elements for allocation.
// count args
var arg_count: usize = 0;
{
var iter = mem.tokenize(u8, value, " ");
while (iter.next()) |_| {
arg_count += 1;
}
}
if (arg_count == 0 or arg_count > 32) {
log.err("{s}:{}: 'shell' option must not be empty or have more than 32 arguments", .{ path, line });
return error.BadConfig;
}
const args = try arena.alloc([:0]const u8, arg_count);
// collect args
{
var iter = mem.tokenize(u8, value, " ");
var i: usize = 0;
while (iter.next()) |arg| : (i += 1) {
args[i] = try arena.dupeZ(u8, arg);
}
}
self.shell = args;
} else {
// TODO maybe try to find the closes approximation and recommend it to the user?
log.err("{s}:{}: No variable '{s}' in section 'main'", .{ path, line, variable });

View file

@ -212,7 +212,7 @@ pub fn run(self: *Self, command: [:0]const u8, comptime name: []const u8) !void
try self.ui.stop();
const ret = system.execForeground(command);
const ret = system.execForeground(self.config.shell, command);
self.titlebar.setNormal();
try self.ui.start();
@ -253,7 +253,7 @@ pub fn open(self: *Self, dir: []const u8, file: []const u8) !void {
}
const cmd = try fmt.allocPrintZ(self.alloc, "{s} \"{s}/{s}\"", .{ self.config.opener.?, dir, file });
defer self.alloc.free(cmd);
try system.execBackground(cmd);
try system.execBackground(self.config.shell, cmd);
}
/// Return the name of the item currently selected with the cursor, if any.
@ -556,11 +556,11 @@ fn handleReturnUserInput(self: *Self) !void {
.command => {
if (self.titlebar.getSelectedHistoryItem()) |cmd| {
try self.titlebar.appendHistoryItem(cmd);
try self.run(cmd, "/bin/sh or subprocess");
try self.run(cmd, "shell or subprocess");
} else if (user_input.buffer.toOwnedSliceZ()) |cmd| {
defer self.alloc.free(cmd);
try self.titlebar.appendHistoryItem(cmd);
try self.run(cmd, "/bin/sh or subprocess");
try self.run(cmd, "shell or subprocess");
}
},
.search, .select, .keep, .discard => {

View file

@ -86,9 +86,9 @@ fn run(op: *const KeyOperation, foreground: bool) !void {
const cmd = (try getFormattedBuffer(op)) orelse return;
defer context.alloc.free(cmd);
if (foreground) {
try context.run(cmd, "/bin/sh or subprocess");
try context.run(cmd, "shell or subprocess");
} else {
try system.execBackground(cmd);
try system.execBackground(context.config.shell, cmd);
}
}

View file

@ -71,14 +71,19 @@ fn handleSigWinch(_: c_int) callconv(.C) void {
context.ui.render() catch {};
}
/// Wrapper around a single fork exec of /bin/sh. Will wait until the forked
/// Wrapper around a single fork exec of the given shell. Will wait until the forked
/// process terminates before returning.
pub fn execForeground(cmd: [:0]const u8) !void {
pub fn execForeground(shell: []const [:0]const u8, cmd: [:0]const u8) !void {
log.debug("running foreground command: '{s}'", .{cmd});
const args = [_:null]?[*:0]const u8{ "/bin/sh", "-c", cmd, null };
var args = [_:null]?[*:0]const u8{null} ** 64;
for (shell, 0..) |arg , i|
args[i] = arg.ptr;
args[shell.len] = cmd.ptr;
const pid = std.posix.fork() catch try std.posix.fork();
if (pid == 0) {
std.posix.execveZ("/bin/sh", &args, @as([*:null]?[*:0]u8, @ptrCast(os.environ.ptr))) catch std.posix.exit(1);
std.posix.execveZ(shell[0].ptr, &args, @as([*:null]?[*:0]u8, @ptrCast(os.environ.ptr))) catch std.posix.exit(1);
unreachable;
} else {
child_pid = pid;
@ -93,10 +98,14 @@ pub fn execForeground(cmd: [:0]const u8) !void {
}
}
/// Wrapper around a daemonized exec of /bin/sh.
pub fn execBackground(cmd: [:0]const u8) !void {
/// Wrapper around a daemonized exec of the given shell.
pub fn execBackground(shell: []const [:0]const u8, cmd: [:0]const u8) !void {
log.debug("running background command: '{s}'", .{cmd});
const args = [_:null]?[*:0]const u8{ "/bin/sh", "-c", cmd, null };
var args = [_:null]?[*:0]const u8{null} ** 64;
for (shell, 0..) |arg, i|
args[i] = arg.ptr;
args[shell.len] = cmd;
// Create first child.
const pid = std.posix.fork() catch try std.posix.fork();
@ -124,7 +133,7 @@ pub fn execBackground(cmd: [:0]const u8) !void {
}
// Execute the command, or die should we fail.
std.posix.execveZ("/bin/sh", &args, @as([*:null]?[*:0]u8, @ptrCast(os.environ.ptr))) catch std.posix.exit(1);
std.posix.execveZ(shell[0].ptr, &args, @as([*:null]?[*:0]u8, @ptrCast(os.environ.ptr))) catch std.posix.exit(1);
unreachable;
} else {
// Exit first child immediately.