diff options
Diffstat (limited to 'src/main.zig')
| -rw-r--r-- | src/main.zig | 71 |
1 files changed, 57 insertions, 14 deletions
diff --git a/src/main.zig b/src/main.zig index 4b1910bd..1cbdf24c 100644 --- a/src/main.zig +++ b/src/main.zig @@ -112,6 +112,16 @@ const nested_text = \\ ; +/// $PARDES_PID named a live editor, so this shell IS inside one, but the Look +/// never got there. Saying so beats quietly opening the second editor that +/// $PARDES_PID exists to prevent. +const unreachable_text = + \\pardes: this shell is inside pardes, but that session did not take the + \\file. Check that it is still running, or pass --nested to start a second + \\editor in here anyway. + \\ +; + // The browser runtime calls a C main (exported below); everything else keeps // the std.process.Init entry. pub const main = if (is_emscripten) webMain else nativeMain; @@ -260,6 +270,10 @@ fn nativeMain(init: std.process.Init) !void { } } opts.tty_only = explicit_tty or session_only; + // The real bare-tty startup wants the editor half present from the first + // frame, so it names that layout; a core booted for a test names none and + // gets the raw shell by itself. + if (opts.tty_only) opts.boot = .tty; opts.mounts = mounts[0..mounts_len]; if (detach != null and attach != null) badArgs(init.io, "--detach and --attach are opposites: one runs the session, the other joins one", .{}); @@ -272,31 +286,60 @@ fn nativeMain(init: std.process.Init) !void { if (opts.mounts.len != 0 and attach != null) badArgs(init.io, "--mount configures a session's own core, and --attach has none of its own", .{}); if (!new_session and !opts.nested and detach == null and attach == null) forwarding: { - const enabled = std.c.getenv("PARDES_FORWARD_LOOK") orelse break :forwarding; - if (!std.mem.eql(u8, std.mem.span(enabled), "1")) break :forwarding; - const dial = std.mem.span(std.c.getenv("PARDES_9P") orelse break :forwarding); - ninep_io.Client.validateDial(dial) catch break :forwarding; - const pane_text = std.mem.span(std.c.getenv("PARDES_PANE") orelse break :forwarding); - for (pane_text) |byte| if (!std.ascii.isDigit(byte)) break :forwarding; - const serial = std.fmt.parseInt(u32, pane_text, 10) catch break :forwarding; - if (serial == 0) break :forwarding; - var ctl_buf: [64]u8 = undefined; - const ctl = try std.fmt.bufPrint(&ctl_buf, "/pane/{d}/ctl", .{serial}); + // Am I inside a pardes at all? That is $PARDES_PID and nothing else — + // one variable, exported by every session that means to take its + // children's Looks, so a session that could not open a socket is still + // recognisably the session this shell is inside. A pid naming no live + // process is a shell that outlived its editor (a daemon started from a + // pane, a stale environment carried into a new terminal), which is not + // nested at all. + const owner = std.mem.span(std.c.getenv("PARDES_PID") orelse break :forwarding); + const pid = std.fmt.parseInt(std.c.pid_t, owner, 10) catch break :forwarding; + if (pid <= 0 or std.c.kill(pid, @enumFromInt(0)) != 0) break :forwarding; + + // Can I reach it? That is the other two, and it is a separate answer. + const Parent = struct { dial: []const u8, serial: u32 }; + const found: ?Parent = reaching: { + const dial = std.mem.span(std.c.getenv("PARDES_9P") orelse break :reaching null); + ninep_io.Client.validateDial(dial) catch break :reaching null; + const pane_text = std.mem.span(std.c.getenv("PARDES_PANE") orelse break :reaching null); + for (pane_text) |byte| if (!std.ascii.isDigit(byte)) break :reaching null; + const serial = std.fmt.parseInt(u32, pane_text, 10) catch break :reaching null; + if (serial == 0) break :reaching null; + break :reaching .{ .dial = dial, .serial = serial }; + }; + const parent = found orelse { + try std.Io.File.stderr().writeStreamingAll(init.io, unreachable_text); + std.process.exit(1); + }; + // The pane's `look` file: one line, and the line is the clicked text + // itself, which is what a right click in that pane would have been. + var look_buf: [64]u8 = undefined; + const look = try std.fmt.bufPrint(&look_buf, "/pane/{d}/look", .{parent.serial}); const word = positional orelse { var tag_buf: [64]u8 = undefined; - const tag = try std.fmt.bufPrint(&tag_buf, "/pane/{d}/tag", .{serial}); - const contents = ninep_io.Client.read(arena, dial, tag, tag) catch break :forwarding; + const tag = try std.fmt.bufPrint(&tag_buf, "/pane/{d}/tag", .{parent.serial}); + const contents = ninep_io.Client.read(arena, parent.dial, tag, tag) catch { + try std.Io.File.stderr().writeStreamingAll(init.io, unreachable_text); + std.process.exit(1); + }; arena.free(contents); try std.Io.File.stderr().writeStreamingAll(init.io, nested_text); std.process.exit(1); }; + // A word this launch cannot turn into a path is an argument problem + // rather than a nesting one: fall through and let the session that + // starts report it, the way `pardes typo` outside pardes does. if (std.mem.indexOfAny(u8, word, "\r\n") != null) break :forwarding; const target = @import("look.zig").parsePathLine(word); var realbuf: [4096]u8 = undefined; const path = if (pardes.filesystem.isVirtual(target.path)) target.path else (pardes.filesystem.resolveOs(target.path, &realbuf) orelse break :forwarding).path; var command_buf: [8192]u8 = undefined; - const command = std.fmt.bufPrint(&command_buf, "look {s}{s}\n", .{ path, word[target.path.len..] }) catch break :forwarding; - ninep_io.Client.write(arena, dial, ctl, command) catch break :forwarding; + const command = std.fmt.bufPrint(&command_buf, "{s}{s}\n", .{ path, word[target.path.len..] }) catch break :forwarding; + ninep_io.Client.write(arena, parent.dial, look, command) catch { + try std.Io.File.stderr().writeStreamingAll(init.io, unreachable_text); + std.process.exit(1); + }; return; } if (positional) |a| { |
