summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/exec.zig34
-rw-r--r--src/ninep/ctl.zig10
-rw-r--r--src/pardes.zig3
3 files changed, 39 insertions, 8 deletions
diff --git a/src/exec.zig b/src/exec.zig
index ac6efcdd..8a24969d 100644
--- a/src/exec.zig
+++ b/src/exec.zig
@@ -982,9 +982,30 @@ pub fn executeBuiltinLine(p: *Pardes, id: usize, txt: []const u8) bool {
pub fn applyStartupConfig(p: *Pardes) void {
p.announce = false;
defer p.announce = true;
- var lines = std.mem.splitScalar(u8, p.opts.startup_config orelse return, '\n');
+ const text = p.opts.startup_config orelse return;
+ // A UTF-8 BOM an editor left at its start is no part of its first word.
+ const body = if (std.mem.startsWith(u8, text, "\xEF\xBB\xBF")) text[3..] else text;
+ var lines = std.mem.splitScalar(u8, body, '\n');
+ var number: usize = 0;
while (lines.next()) |line| {
- _ = executeBuiltinLine(p, p.active, line);
+ number += 1;
+ const word = std.mem.trim(u8, line, " \t\r");
+ // `#` starts a comment only at a line's start: a value may hold one
+ // (`#ff0000`).
+ if (word.len == 0 or isComment(word)) continue;
+ const failures = p.fs.failures;
+ const known = executeBuiltinLine(p, p.active, line);
+ if (known and p.fs.failures == failures) continue;
+ // Never silent: a word it has not, or one that failed, is a notice
+ // on the message row and an err in the log, with its line.
+ var why_buf: [320]u8 = undefined;
+ const why: []const u8 = if (!known)
+ std.fmt.bufPrint(&why_buf, "unknown word \"{s}\"", .{word[0..@min(word.len, 64)]}) catch "unknown word"
+ else if (p.fs.said_len > 0) p.fs.said[0..p.fs.said_len] else "failed";
+ var said: [400]u8 = undefined;
+ const line_said = std.fmt.bufPrint(&said, "init file line {d}: {s}", .{ number, why }) catch "init file: a line failed";
+ if (!known) p.reportFailure(p.active, line_said);
+ pardes.ctlfs.events.notePath(p, "err -", line_said);
}
}
@@ -1027,9 +1048,8 @@ pub fn applySettingBuiltin(p: *Pardes, setting: config.Runtime.Setting, arg: ?[]
.theme => {
const want = std.mem.trim(u8, arg orelse return, " \t\r\n");
if (colors.themeIndex(want)) |i| return colors.setThemeIndex(p, i);
- // A config line is not answered on screen (applyStartupConfig
- // runs with announce off); a click or a ctl write is.
- if (!p.announce) return;
+ // A failure is said, the init file's too (applyStartupConfig
+ // logs it with its line): never silent.
// All of it within the 128 bytes a 9P error carries, so a write
// failing with it is cut nowhere: the names get what is left.
const shown = want[0..@min(want.len, 40)];
@@ -1064,10 +1084,10 @@ pub fn applySettingBuiltin(p: *Pardes, setting: config.Runtime.Setting, arg: ?[]
return p.reportFailure(p.active, std.fmt.bufPrint(&text, "Shell: {s}", .{refused}) catch "Shell: no such shell");
}
}
- if (!p.settings.apply(setting, want) and p.announce) p.reportFailure(p.active, "Shell: takes " ++ comptime config.Runtime.takes(.shell).?);
+ if (!p.settings.apply(setting, want)) p.reportFailure(p.active, "Shell: takes " ++ comptime config.Runtime.takes(.shell).?);
},
// Said with what it takes: its words, its range.
- else => if (!p.settings.apply(setting, arg) and p.announce) {
+ else => if (!p.settings.apply(setting, arg)) {
var text: [160]u8 = undefined;
const why = if (config.Runtime.takes(setting.action)) |values| std.fmt.bufPrint(&text, "{s}: takes {s}", .{ setting.word, values }) else std.fmt.bufPrint(&text, "{s}: does not take that value", .{setting.word});
p.reportFailure(p.active, why catch "does not take that value");
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig
index 697ef594..2fa0cd7e 100644
--- a/src/ninep/ctl.zig
+++ b/src/ninep/ctl.zig
@@ -4467,3 +4467,13 @@ test "a Mount of a socket path that is not there says no such socket, ENOENT" {
try testing.expectEqual(@as(usize, 0), p.fs.mounts.items.len);
try testing.expectEqual(E.INVAL, wr(p, @intFromEnum(tree.TopFile.ctl), "Mount peer rel/x.sock\n").errno());
}
+
+test "the init file reads past a BOM, and every line it fails is a notice and an err, never silent" {
+ const p = try Pardes.init(testing.allocator, .{ .tty_only = true, .startup_config = "\xEF\xBB\xBFTheme dusk\nnosuchword 1\nTheme nosuchtheme\n# a comment\nInactiveDim 30 # not a comment, a bad value\n" });
+ defer p.deinit();
+ try testing.expectEqualStrings("dusk", p.theme().name);
+ try testing.expect(th.logHas(p, "err - init file line 2: unknown word \"nosuchword 1\""));
+ try testing.expect(th.logHas(p, "err - init file line 3: "));
+ try testing.expect(!th.logHas(p, "line 4"));
+ try testing.expect(th.logHas(p, "err - init file line 5: "));
+}
diff --git a/src/pardes.zig b/src/pardes.zig
index 803686e0..c6850451 100644
--- a/src/pardes.zig
+++ b/src/pardes.zig
@@ -620,7 +620,8 @@ test "startup config runs builtin lines in order and isolates bad lines" {
defer p.deinit();
try std.testing.expectEqualStrings("acme", p.theme().name);
- try std.testing.expect(!p.animationActive());
+ // The theme lands without a tween (the chrome is the theme's at once);
+ // the bad lines are said, so a notice may be animating.
try std.testing.expectEqual(ChromeTheme.fromTheme(p.theme()), p.chromeTheme().*);
try std.testing.expect(!p.quit);
for (0..p.effects_len) |i| switch (p.effects[(p.effects_head + i) % p.effects.len]) {