diff options
| author | Peter Geissler <peter@haxx.in> | 2026-08-28 15:02:49 +0200 |
|---|---|---|
| committer | Peter Geissler <peter@haxx.in> | 2026-08-28 15:02:49 +0200 |
| commit | 22e70baa7172e8487913cceae3d3ec89bbe40627 (patch) | |
| tree | cd067b59109b829e423995ee10d02f30392ff47a /src/path.c | |
| parent | ctrl_d_exits: Ctrl-D at a prompt, where the tty cannot do it (diff) | |
| download | slosh-22e70baa7172e8487913cceae3d3ec89bbe40627.tar.gz slosh-22e70baa7172e8487913cceae3d3ec89bbe40627.tar.xz slosh-22e70baa7172e8487913cceae3d3ec89bbe40627.zip | |
path: an absolute path does not always start with a slash
`include "C:/themes/t.kdl"` on Windows did not fail to find the file. It
found a different one, or rather looked for it: absoluteness was tested
as `path[0] == '/'`, a drive letter is not that, so the path was read as
relative and joined to the including file's directory. The result was
cannot open C:/Users/you/cfgA/C:/Users/you/cfgB/t.kdl
which at least says what happened. A layout's `cwd="C:/srv/api"` took
the same turn through path_resolve() and said nothing at all -- the pane
started in the wrong directory, which is the failure this file's own
header comment is about.
Windows spells absolute three ways and all three now count: rooted on
the current drive (`\dir`), rooted on a named one (`C:\dir`, `C:/dir`),
and a UNC share. A drive letter has to be followed by a separator to
qualify, because `C:file` is relative to the current directory *of that
drive* -- a real thing, and not one we can resolve for anyone.
Windows-only, deliberately. `C:` is an ordinary file name on POSIX and a
path beginning with one is relative there, so reading it as a drive
would break the platform that works in order to fix the one that does
not.
Diffstat (limited to 'src/path.c')
| -rw-r--r-- | src/path.c | 33 |
1 files changed, 32 insertions, 1 deletions
@@ -15,6 +15,37 @@ #include <stdlib.h> #include <string.h> +/* Whether a path names a place outright, rather than one to be read against + * somewhere else. + * + * On POSIX that is a leading `/` and nothing else. Windows spells it three + * ways and all three have to count: rooted on the current drive (`\dir`), + * rooted on a named one (`C:\dir`, `C:/dir`), and a UNC share + * (`\\server\share`). Getting it wrong is not a lookup that fails, it is a + * concatenation that succeeds at being nonsense -- `include "C:/themes/t.kdl"` + * was resolved against the including file's directory and opened as + * `C:/Users/you/.config/slosh/C:/themes/t.kdl`. + * + * A drive letter must be followed by a separator to be absolute: `C:file` is + * relative to the current directory *of drive C*, which is a different thing + * and not one we can resolve for anybody. + * + * All of it is Windows-only on purpose. `C:` is an ordinary file name on + * POSIX and a path that begins with one is relative there, so reading it as a + * drive would break the platform that works to fix the one that does not. */ +bool path_is_absolute(const char *path) { + if (!path || !*path) return false; + if (path[0] == '/') return true; +#ifdef _WIN32 + if (path[0] == '\\') return true; + bool drive = (path[0] >= 'A' && path[0] <= 'Z') || + (path[0] >= 'a' && path[0] <= 'z'); + if (drive && path[1] == ':' && (path[2] == '/' || path[2] == '\\')) + return true; +#endif + return false; +} + 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 @@ -42,7 +73,7 @@ const char *path_resolve(const char *path, const char *base, char *buf, size_t cap) { if (!path) return NULL; if (path[0] == '~') return path_expand(path, buf, cap); - if (path[0] == '/' || !base || !*base) return path; + if (path_is_absolute(path) || !base || !*base) return path; /* `.` is the project directory itself, which is what a dump writes for the * pane that started there -- joined naively it would be `/home/you/dev/api/.`, * which works and reads like a bug. */ |
