aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authoruser <user@clank>2026-08-14 22:06:27 +0200
committeruser <user@clank>2026-08-14 22:06:27 +0200
commit360ee83a9bae4d06db1ab7747ef984cd7292883d (patch)
treedf0c65da1753f288d875483538b3f6517628d289 /src
parentsl0ppty: walk the row, not screen_at() (diff)
downloadslosh-360ee83a9bae4d06db1ab7747ef984cd7292883d.tar.gz
slosh-360ee83a9bae4d06db1ab7747ef984cd7292883d.tar.xz
slosh-360ee83a9bae4d06db1ab7747ef984cd7292883d.zip
sl0ppty: expand ~ in a path that a config file wrote
`cwd="~/dev/api"` is in the README, in config/layout.example.kdl, and did nothing. `~` is a shell thing, and no shell has touched a path that arrives from a layout file -- so it went to chdir() literally, failed, and left the pane in whatever directory the session happened to be started from. The pane still ran, so nothing looked wrong until you typed `ls`. path_expand() handles `~` and `~/...` against $HOME and returns everything else untouched, including `~user`, which needs the password database and is not worth linking it in for -- returned unchanged rather than half-expanded into something wrong. Applied where a layout's cwd is read, which is one place because a pane inherits its tab's. Found while documenting a shader directory that was about to have exactly the same bug.
Diffstat (limited to 'src')
-rw-r--r--src/app.c7
-rw-r--r--src/path.c25
-rw-r--r--src/sl0ppty.h6
3 files changed, 37 insertions, 1 deletions
diff --git a/src/app.c b/src/app.c
index ce95175..be707c7 100644
--- a/src/app.c
+++ b/src/app.c
@@ -1583,7 +1583,12 @@ uint32_t app_current_tab_id(app_t *a) { return a->ntabs ? cur(a)->id : 0; }
*/
static node_t *build_pane(app_t *a, const kdl_node_t *node, const char *cwd) {
- const char *node_cwd = kdl_prop(node, "cwd", cwd);
+ /* Expanded once here, so both the pane spawned from this node and every
+ * child that inherits the value get the same real directory. The buffer
+ * outlives the recursion below it: children finish before we return. */
+ char cwdbuf[1024];
+ const char *node_cwd =
+ path_expand(kdl_prop(node, "cwd", cwd), cwdbuf, sizeof cwdbuf);
/* a split: children, in order, in one direction */
size_t kids = 0;
diff --git a/src/path.c b/src/path.c
new file mode 100644
index 0000000..adf824b
--- /dev/null
+++ b/src/path.c
@@ -0,0 +1,25 @@
+/* Paths as people write them.
+ *
+ * `~` is a shell thing: by the time a path reaches us from a config or layout
+ * file no shell has touched it, so `cwd="~/dev/api"` was being handed to
+ * chdir() literally, failing, and leaving the pane in whatever directory the
+ * session happened to be in. Silently \u2014 which is the worst way for a path to
+ * be wrong, because the pane still starts and still works.
+ */
+#include "sl0ppty.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+const char *path_expand(const char *path, char *buf, size_t cap) {
+ if (!path) return NULL;
+ /* `~` and `~/...` only. `~user` needs the password database and is not
+ * worth linking it in for; it is returned unchanged, which is exactly what
+ * it did before, rather than being half-expanded into something wrong. */
+ if (path[0] != '~' || (path[1] && path[1] != '/')) return path;
+ const char *home = getenv("HOME");
+ if (!home || !*home) return path;
+ snprintf(buf, cap, "%s%s", home, path + 1);
+ return buf;
+}
diff --git a/src/sl0ppty.h b/src/sl0ppty.h
index 2b7d905..96f8542 100644
--- a/src/sl0ppty.h
+++ b/src/sl0ppty.h
@@ -95,6 +95,12 @@ char *screen_dump(screen_t *s);
/* The same screen as JSON: rows of text, style runs, cursor, hit-list. */
char *screen_dump_json(screen_t *s);
+/* ---- paths -------------------------------------------------------------- */
+
+/* Expand a leading `~`, using `buf` when it has to. Returns `path` itself when
+ * there is nothing to do, so `buf` only has to outlive the result's use. */
+const char *path_expand(const char *path, char *buf, size_t cap);
+
/* ---- pty --------------------------------------------------------------- */
typedef struct {