diff options
| author | user <user@clank> | 2026-09-22 12:57:26 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-09-22 12:57:26 +0200 |
| commit | 68ff62e9492accd4c1c9a83edbcbd225afb1a11a (patch) | |
| tree | b744ad33cdbaa25fca333f28f13b5e7df28838dd /src | |
| parent | themes: a picker that wears them as you browse (C-a t) (diff) | |
| download | slosh-68ff62e9492accd4c1c9a83edbcbd225afb1a11a.tar.gz slosh-68ff62e9492accd4c1c9a83edbcbd225afb1a11a.tar.xz slosh-68ff62e9492accd4c1c9a83edbcbd225afb1a11a.zip | |
themes: ask the binary where it is, not the prefix it was built for
`theme_name "sl0p"` with all seven themes sitting in /usr/share/slosh/themes:
"no theme named sl0p". The lookup knew two places -- your own themes directory
and the PREFIX this build was compiled with -- and on that machine the first
did not exist and the second was /usr/local, because that is the Makefile's
default and the package was built with PREFIX=/usr. Neither was where the
themes actually were, and nothing in the search could ever have found them.
A compiled-in prefix is a guess about where the binary will end up, and the
binary is the one thing that knows where it really is. So the search now asks
it: path_self() (/proc/self/exe, _NSGetExecutablePath, GetModuleFileName --
lifted out of server.c, which was already doing this for SLOSH_BIN) gives
<bin>/../share/slosh/themes for an installed slosh wherever it was installed
to, and <repo>/contrib/themes for one being run out of a build tree, which
makes a checkout able to name a theme without installing anything first. The
compiled-in prefix stays as the last candidate, for a binary sitting somewhere
that says nothing about its data, and duplicates collapse so the usual install
does not report the same directory twice.
Your own directory is still first and still shadows everything, which is the
property that matters; the rest is about the shipped set being findable at all.
The tests that assumed a closed world of two temp themes now narrow to their
own names by typing, since the shipped ones are (correctly) always in the list
-- and the "no themes anywhere" toast lost its test, because with the shipped
set reachable that state takes a binary with nothing beside it. The case that
started this is a test of its own.
Diffstat (limited to 'src')
| -rw-r--r-- | src/app.c | 4 | ||||
| -rw-r--r-- | src/cmd.c | 4 | ||||
| -rw-r--r-- | src/config.c | 67 | ||||
| -rw-r--r-- | src/path.c | 40 |
4 files changed, 94 insertions, 21 deletions
@@ -1677,8 +1677,8 @@ bool run_action(app_t *a, action_t act) { * session started is exactly the one being looked for. */ a->nthemes = app_themes(a->themes, THEMES_MAX); if (!a->nthemes) { - char dirs[4][512]; - size_t nd = app_theme_dirs(dirs, 4); + char dirs[THEME_DIRS_MAX][512]; + size_t nd = app_theme_dirs(dirs, THEME_DIRS_MAX); char msg[600]; snprintf(msg, sizeof msg, "no themes in %s", nd ? dirs[0] @@ -524,8 +524,8 @@ static char *cmd_json(app_t *a, screen_t *s, input_parser_t *in, } char names[64][64]; size_t n = app_themes(names, 64); - char dirs[4][512]; - size_t nd = app_theme_dirs(dirs, 4); + char dirs[THEME_DIRS_MAX][512]; + size_t nd = app_theme_dirs(dirs, THEME_DIRS_MAX); json_t j; json_init(&j); json_obj_open(&j, NULL); diff --git a/src/config.c b/src/config.c index c3b62be..4e60652 100644 --- a/src/config.c +++ b/src/config.c @@ -2268,23 +2268,56 @@ static void include_path(const char *base_file, const char *ref, char *out, * being copied anywhere first, and a theme of yours with the same name wins * by being looked at first. `theme_dir` replaces the first of the two, not * both, so naming your own directory never hides the shipped set. */ +/* Append a candidate, skipping one that is already in the list: the lookup + * below names the same directory two ways on an ordinary install, and a + * "looked in" message that says it twice reads like a bug. */ +static void dir_add(char (*out)[512], size_t *n, size_t max, const char *fmt, + ...) { + if (*n >= max) return; + char path[512]; + va_list ap; + va_start(ap, fmt); + vsnprintf(path, sizeof path, fmt, ap); + va_end(ap); + for (size_t i = 0; i < *n; i++) + if (strcmp(out[i], path) == 0) return; + snprintf(out[(*n)++], 512, "%s", path); +} + size_t config_theme_dirs(const config_t *c, char (*out)[512], size_t max) { size_t n = 0; - if (n < max) { - if (c && c->theme_dir && *c->theme_dir) { - snprintf(out[n++], 512, "%s", c->theme_dir); - } else { - /* files[0] is the file this config was loaded from -- remembered before - * it was parsed, so it is right even for a file that turned out not to - * exist. Only a config nobody has loaded falls back to where one would - * have been. */ - const char *root = c && c->nfiles ? c->files[0] : config_default_path(); - char dir[512]; - snprintf(out[n++], 512, "%s/themes", path_dir(root, dir, sizeof dir)); - } + if (c && c->theme_dir && *c->theme_dir) { + dir_add(out, &n, max, "%s", c->theme_dir); + } else { + /* files[0] is the file this config was loaded from -- remembered before + * it was parsed, so it is right even for a file that turned out not to + * exist. Only a config nobody has loaded falls back to where one would + * have been. */ + const char *root = c && c->nfiles ? c->files[0] : config_default_path(); + char dir[512]; + dir_add(out, &n, max, "%s/themes", path_dir(root, dir, sizeof dir)); + } + + /* Then the shipped ones, found relative to *this binary* before anything + * compiled in. A build compiles in one PREFIX; the binary that ends up + * running may have been packaged under another (a distro building with + * PREFIX=/usr while the default is /usr/local is the common case), moved, + * or never installed at all. `<bin>/../share/slosh/themes` is true in all of + * those, and the checkout's own `contrib/themes` covers the last one, so a + * build tree can name a theme without installing anything first. */ + char self[512]; + if (path_self(self, sizeof self)) { + char bindir[512], updir[512]; + const char *up = + path_dir(path_dir(self, bindir, sizeof bindir), updir, sizeof updir); + dir_add(out, &n, max, "%s/share/slosh/themes", up); + dir_add(out, &n, max, "%s/contrib/themes", up); } #ifdef SLOSH_DATADIR - if (n < max) snprintf(out[n++], 512, "%s/themes", SLOSH_DATADIR); + /* Last, and usually a repeat of the first of those: the prefix this build + * was told it would be installed under, which is the only answer left when + * the binary sits somewhere that says nothing about its data. */ + dir_add(out, &n, max, "%s/themes", SLOSH_DATADIR); #endif return n; } @@ -2292,8 +2325,8 @@ size_t config_theme_dirs(const config_t *c, char (*out)[512], size_t max) { /* A theme's file, or false with the places that were looked in. */ static bool theme_path(const config_t *c, const char *name, char *out, size_t cap, char *tried, size_t triedcap) { - char dirs[4][512]; - size_t nd = config_theme_dirs(c, dirs, 4); + char dirs[THEME_DIRS_MAX][512]; + size_t nd = config_theme_dirs(c, dirs, THEME_DIRS_MAX); if (tried && triedcap) tried[0] = 0; for (size_t i = 0; i < nd; i++) { char path[1024]; @@ -2330,8 +2363,8 @@ static int by_theme_name(const void *a, const void *b) { size_t config_themes(const config_t *c, char (*out)[THEME_NAME_MAX], size_t max) { - char dirs[4][512]; - size_t nd = config_theme_dirs(c, dirs, 4), n = 0; + char dirs[THEME_DIRS_MAX][512]; + size_t nd = config_theme_dirs(c, dirs, THEME_DIRS_MAX), n = 0; for (size_t i = 0; i < nd && n < max; i++) { DIR *d = opendir(dirs[i]); if (!d) continue; /* a directory nobody made is the normal case */ @@ -6,6 +6,7 @@ * 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. */ +#define _GNU_SOURCE #include "slosh.h" #include <sys/stat.h> @@ -14,6 +15,14 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <unistd.h> + +#ifdef __APPLE__ +#include <mach-o/dyld.h> /* Darwin has no /proc/self/exe */ +#endif +#ifdef _WIN32 +#include "compat_win.h" +#endif /* Whether a path names a place outright, rather than one to be read against * somewhere else. @@ -149,6 +158,37 @@ const char *path_relative(const char *path, const char *base) { return *rest ? rest : "."; } +/* This binary, as a path: what the program can find *relative to itself*. + * + * Read from the kernel rather than from argv[0], which is whatever the caller + * felt like passing. It is how the shipped themes are found without being told + * where they are: a build compiles in a PREFIX, but the binary that ends up + * running may have been installed under a different one, packaged by somebody + * else, or not installed at all -- and `<the binary>/../share/slosh` is true in + * every one of those cases while a compiled-in guess is true in one. + */ +bool path_self(char *out, size_t cap) { + if (!out || cap < 2) return false; +#ifdef _WIN32 + return sl_self_exe(out, cap); +#elif defined(__APPLE__) + char raw[1024]; + uint32_t sz = sizeof raw; + /* What Darwin hands back can still hold symlinks and `..`; realpath + * finishes the job, and falls back to the raw answer rather than to + * nothing. */ + if (_NSGetExecutablePath(raw, &sz) != 0) return false; + char resolved[1024]; + snprintf(out, cap, "%s", realpath(raw, resolved) ? resolved : raw); + return true; +#else + ssize_t n = readlink("/proc/self/exe", out, cap - 1); + if (n <= 0) return false; + out[n] = 0; + return true; +#endif +} + /* Create a directory and everything above it. `mkdir` of one level is enough * on a machine where ~/.config already exists, which is most of them and not * all of them -- a fresh container has neither, and "could not write your |
