summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-29 11:48:32 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:16 -0300
commitc3c3ef27ede2f69ef4f8c6acb10808f02ee46bb2 (patch)
tree523d03bbb555a4a6d9c991fa4ec43723223cda18 /src
parentcef529dc8cd080198f79d2b26bffbe9c7cf3e8bd (diff)
downloadpardes-c3c3ef27ede2f69ef4f8c6acb10808f02ee46bb2.tar.gz
pardes-c3c3ef27ede2f69ef4f8c6acb10808f02ee46bb2.zip
A builtin or setting this build lacks is refused GUI-only, from an exec too
On a terminal, Fonts written to an exec was no builtin there, so it ran as a shell command and ended exit 127; ctl said unknown control message. Both now say Fonts is GUI-only, invalid here (a builtin another platform's build has, not in this build). Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src')
-rw-r--r--src/builtins.zig19
-rw-r--r--src/exec.zig8
-rw-r--r--src/ninep/ctl.zig12
3 files changed, 37 insertions, 2 deletions
diff --git a/src/builtins.zig b/src/builtins.zig
index 9025da6d..b0499dc0 100644
--- a/src/builtins.zig
+++ b/src/builtins.zig
@@ -56,6 +56,22 @@ fn isEnabled(comptime T: type) bool {
return !@hasDecl(T, "enabled") or T.enabled;
}
+/// Why `word`, a builtin some build has, is none in this one: `GUI-only`
+/// for the GUI's own (`gui_only`), else `not in this build`; null for a
+/// word that is one here, or in no build.
+pub fn absentWhy(name: []const u8) ?[]const u8 {
+ @setEvalBranchQuota(20000);
+ inline for (@typeInfo(@This()).@"struct".decls) |d| {
+ if (comptime @TypeOf(@field(@This(), d.name)) == type) {
+ const T = @field(@This(), d.name);
+ if (comptime @typeInfo(T) == .@"struct" and @hasDecl(T, "run") and !isEnabled(T)) {
+ if (std.mem.eql(u8, d.name, name)) return if (@hasDecl(T, "gui_only")) "GUI-only" else "not in this build";
+ }
+ }
+ }
+ return null;
+}
+
fn manualBuiltinCount() comptime_int {
comptime {
var count = 0;
@@ -814,6 +830,7 @@ pub const Fonts = struct {
pub const scope: Scope = .session;
pub const output: OutputTraits = .{ .name = config.fonts_buffer, .steps = true, .commands = true };
pub const enabled = capabilities.font_picker;
+ pub const gui_only = true;
pub fn run(c: Ctx) void {
if (comptime enabled) apply(c) else unreachable;
}
@@ -1319,6 +1336,7 @@ pub const EffectCode = struct {
pub const takes_arg = true;
pub const requires_arg = true;
pub const enabled = capabilities.panel_transitions or capabilities.scene_shaders;
+ pub const gui_only = true;
pub const output: OutputTraits = .{ .name = config.effect_code_buffer };
pub fn run(c: Ctx) void {
if (comptime enabled)
@@ -1335,6 +1353,7 @@ pub const Pet = struct {
pub const takes_arg = true;
pub const requires_arg = true;
pub const enabled = pardes.platform == .gui;
+ pub const gui_only = true;
pub fn run(c: Ctx) void {
const name = std.mem.trim(u8, c.arg orelse return, " \t\r\n");
c.p.settings.pet = std.meta.stringToEnum(@TypeOf(c.p.settings.pet), name) orelse return;
diff --git a/src/exec.zig b/src/exec.zig
index 9ec6a426..cf4e01ab 100644
--- a/src/exec.zig
+++ b/src/exec.zig
@@ -657,6 +657,14 @@ pub fn executeFrom(p: *Pardes, id: usize, txt: []const u8, from_body: bool) ?usi
.sent => |to| return to,
.asked => return null,
};
+ // A builtin or setting this build lacks (Fonts on a terminal) is said,
+ // never run as a shell command.
+ const word = cmd[0 .. std.mem.indexOfAny(u8, cmd, " \t") orelse cmd.len];
+ if (builtins.absentWhy(word) orelse if (config.Runtime.find(word) != null) @as([]const u8, "GUI-only") else null) |why| {
+ var said: [96]u8 = undefined;
+ p.reportFailure(id, std.fmt.bufPrint(&said, "{s} is {s}, invalid here", .{ word, why }) catch "GUI-only, invalid here");
+ return null;
+ }
if (!takesCommandLine(p, id)) return runCommand(p, id, cmd);
panes.Terminal.padOutputBelowEdits(p, id);
panes.Terminal.noteCommand(pane, cmd);
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig
index 68b62186..c2a78fb8 100644
--- a/src/ninep/ctl.zig
+++ b/src/ninep/ctl.zig
@@ -388,11 +388,11 @@ fn checkBuiltin(p: *Pardes, req: Req, line: []const u8, scope: builtins.Scope) ?
const b = std.meta.stringToEnum(Builtin, word) orelse {
// A setting this build's frontend cannot show (Lift, GripWidth on a
// terminal) is known, and says so rather than sound misspelt.
- if (config.Runtime.find(word) != null) {
+ if (builtins.absentWhy(word) orelse if (config.Runtime.find(word) != null) @as([]const u8, "GUI-only") else null) |absent| {
var why: [64]u8 = undefined;
// EINVAL, a request this build cannot take, in words 9ns reads
// as that: `invalid`.
- return refuse(p, req, std.fmt.bufPrint(&why, "{s} is GUI-only, invalid here", .{word}) catch "GUI-only, invalid here", line);
+ return refuse(p, req, std.fmt.bufPrint(&why, "{s} is {s}, invalid here", .{ word, absent }) catch "GUI-only, invalid here", line);
}
return refuse(p, req, "unknown control message", line);
};
@@ -1704,6 +1704,14 @@ test "a setting this frontend cannot show says GUI-only, and DumpDir reads back
if (!builtins.capabilities.lift) {
try testing.expectEqualStrings("Lift is GUI-only, invalid here \"Lift on\"", wr(p, root_ctl, "Lift on\n").reply.ename);
try testing.expectEqualStrings("GripWidth is GUI-only, invalid here \"GripWidth 120\"", wr(p, root_ctl, "GripWidth 120\n").reply.ename);
+ // A builtin the GUI alone has, written to a ctl or an exec, never
+ // runs as a shell command.
+ if (comptime !pardes.font_picker) {
+ try testing.expectEqualStrings("Fonts is GUI-only, invalid here \"Fonts\"", wr(p, root_ctl, "Fonts\n").reply.ename);
+ const ran = wr(p, root_exec, "Fonts\n");
+ try testing.expectEqual(E.IO, ran.errno());
+ try testing.expectEqualStrings("Fonts is GUI-only, invalid here", ran.reply.ename);
+ }
}
try testing.expectEqualStrings("unknown control message \"Liftt on\"", wr(p, root_ctl, "Liftt on\n").reply.ename);
var buf: [900]u8 = undefined;