diff options
Diffstat (limited to 'src/main.zig')
| -rw-r--r-- | src/main.zig | 64 |
1 files changed, 51 insertions, 13 deletions
diff --git a/src/main.zig b/src/main.zig index 00eff433..b08a64da 100644 --- a/src/main.zig +++ b/src/main.zig @@ -136,6 +136,26 @@ const help_text = \\ ; +/// A MISTAKE AT THE SHELL PROMPT, said in words and nothing else. +/// +/// Every one of these used to be `return error.BadArgs` out of `main`, which +/// std prints as `error: BadArgs` with a RETURN TRACE under it. That reads as a +/// crash — it is the same shape a real panic has — for the most ordinary thing +/// a person can do, which is mistype a flag. It also said `BadArgs` and nothing +/// about WHICH argument, when the site that returned it knew exactly. +/// +/// stderr and not an `+Errors` pane, which is the answer one line down in +/// `Pardes.init`: argv is read before any core exists, and a person who typed +/// a bad flag is looking at the prompt they typed it into rather than at an +/// editor. `--attach`'s refusal three functions down already answers this way. +fn badArgs(io: std.Io, comptime fmt: []const u8, args: anytype) noreturn { + var buf: [1024]u8 = undefined; + const line = std.fmt.bufPrint(&buf, "pardes: " ++ fmt ++ "\nTry 'pardes --help'.\n", args) catch + "pardes: bad arguments\nTry 'pardes --help'.\n"; + std.Io.File.stderr().writeStreamingAll(io, line) catch {}; + std.process.exit(1); +} + /// Built at COMPTIME, because both halves are: `version` comes out of /// `build.zig.zon` through the options module and `commit` out of `git` at /// configure time, so there is nothing here to format at runtime and no buffer @@ -240,18 +260,21 @@ fn nativeMain(init: std.process.Init) !void { if (std.mem.eql(u8, a, "--tty")) { opts.tty_only = true; } else if (std.mem.startsWith(u8, a, "--tty-toggle=")) { - opts.tty_toggle = parseCtrlKey(a["--tty-toggle=".len..]) orelse return error.BadArgs; + opts.tty_toggle = parseCtrlKey(a["--tty-toggle=".len..]) orelse + badArgs(init.io, "--tty-toggle wants one letter, not '{s}'", .{a["--tty-toggle=".len..]}); } else if (std.mem.eql(u8, a, "--tty-toggle")) { i += 1; - if (i >= args.len) return error.BadArgs; - opts.tty_toggle = parseCtrlKey(args[i]) orelse return error.BadArgs; + if (i >= args.len) badArgs(init.io, "--tty-toggle needs a letter after it", .{}); + opts.tty_toggle = parseCtrlKey(args[i]) orelse + badArgs(init.io, "--tty-toggle wants one letter, not '{s}'", .{args[i]}); } else if (std.mem.eql(u8, a, "-n")) { i += 1; - if (i >= args.len) return error.BadArgs; - opts.shells = std.fmt.parseInt(u8, args[i], 10) catch return error.BadArgs; + if (i >= args.len) badArgs(init.io, "-n needs a count after it: 1 or 3", .{}); + opts.shells = std.fmt.parseInt(u8, args[i], 10) catch + badArgs(init.io, "-n takes 1 or 3, not '{s}'", .{args[i]}); } else if (std.mem.eql(u8, a, "-l")) { i += 1; - if (i >= args.len) return error.BadArgs; + if (i >= args.len) badArgs(init.io, "-l needs the path of a dump to load", .{}); opts.load_path = args[i]; } else if (std.mem.eql(u8, a, "--fs")) { opts.fs = ""; @@ -297,8 +320,12 @@ fn nativeMain(init: std.process.Init) !void { return; } else if (a.len > 0 and a[0] != '-' and positional == null) { positional = a; + } else if (a.len == 0) { + badArgs(init.io, "an empty argument names nothing", .{}); + } else if (a[0] == '-') { + badArgs(init.io, "no such option: {s}", .{a}); } else { - return error.BadArgs; + badArgs(init.io, "one file or directory at a time, and '{s}' is the second", .{a}); } } // Started INSIDE another pardes: hand it the file and get out of the way @@ -328,7 +355,8 @@ fn nativeMain(init: std.process.Init) !void { std.process.exit(1); }; var cwdbuf: [4096]u8 = undefined; - const cwd = std.c.getcwd(&cwdbuf, cwdbuf.len) orelse return error.BadArgs; + const cwd = std.c.getcwd(&cwdbuf, cwdbuf.len) orelse + badArgs(init.io, "this shell's working directory is gone; cd somewhere that exists", .{}); var realbuf: [4096]u8 = undefined; const sent = switch (@import("look.zig").resolve(word, std.mem.span(@as([*:0]u8, @ptrCast(cwd))), &realbuf)) { .dir => |d| nested.sendLook(outer_pid, d, 0), @@ -356,7 +384,8 @@ fn nativeMain(init: std.process.Init) !void { // dirs); anything else resolves as a file if (std.c.chdir(a.ptr) != 0) { var cwdbuf: [4096]u8 = undefined; - const cwd = std.c.getcwd(&cwdbuf, cwdbuf.len) orelse return error.BadArgs; + const cwd = std.c.getcwd(&cwdbuf, cwdbuf.len) orelse + badArgs(init.io, "this shell's working directory is gone; cd somewhere that exists", .{}); var realbuf: [4096]u8 = undefined; switch (@import("look.zig").resolve(a, std.mem.span(@as([*:0]u8, @ptrCast(cwd))), &realbuf)) { .file => |t| { @@ -390,7 +419,13 @@ fn nativeMain(init: std.process.Init) !void { .word = try arena.dupe(u8, a), .dir = try arena.dupe(u8, std.mem.span(@as([*:0]u8, @ptrCast(cwd)))), }, - else => return error.BadArgs, + // A URL, an `@pN` pane address, or a directory that resolves + // and `chdir` refused. None is a typo, and none is something a + // LAUNCH can act on — the first two are words to click once + // pardes is open, and the third is a permission problem. + .url => badArgs(init.io, "a URL is not something a launch can open; start pardes and click it", .{}), + .pane => badArgs(init.io, "@pN addresses a pane of a running pardes, so there is none yet", .{}), + else => badArgs(init.io, "cannot enter that directory: {s}", .{a}), } } } @@ -410,7 +445,8 @@ fn nativeMain(init: std.process.Init) !void { // with no core, so the two together are a contradiction with no useful // reading. Refused rather than resolved by declaration order, which would // silently drop whichever flag lost. - if (detach != null and attach != null) return error.BadArgs; + if (detach != null and attach != null) + badArgs(init.io, "--detach and --attach are opposites: one runs the session, the other joins one", .{}); // `--fs` mounts the acme control filesystem, and it needs a CORE to serve. // `--attach` has none — it is a terminal whose state lives in another // process — so the flag there would be parsed, stored, and served by @@ -427,13 +463,15 @@ fn nativeMain(init: std.process.Init) !void { // than the desktop shells pay. `--detach --fs` is now the configuration // that most wants a control filesystem, because it is the one whose panes // outlive every terminal that could otherwise have scripted them. - if (opts.fs != null and attach != null) return error.BadArgs; + if (opts.fs != null and attach != null) + badArgs(init.io, "--fs serves a session's own core, and --attach has none of its own", .{}); // ...and `--fs9` for exactly that reason and no other: it is the same tree // over a different transport, and an `--attach` has no core to serve it // from either. Checked separately rather than folded into the line above // so that neither flag's refusal is a side effect of the other's — they // are independent everywhere else. - if (opts.fs9 != null and attach != null) return error.BadArgs; + if (opts.fs9 != null and attach != null) + badArgs(init.io, "--fs9 serves a session's own core, and --attach has none of its own", .{}); // `--detach` replaces the frontend rather than choosing among them: the // core runs here, with no terminal, and the frontends are elsewhere on a // socket (src/detached/). It is checked before `platform` because it is not |
