From 479fde27d7aff1da19a3f7310ce8f0b27ebb31ac Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 25 Jan 2022 21:08:07 +0100 Subject: [PATCH] path: Make path real "work" with nonexistent paths This just goes back until it finds an existent path, resolves that, and adds the normalized rest on top. So if you try /bin/foo/bar////../baz and /bin exists as a symlink to /usr/bin, it would resolve that, and normalize the rest, giving /usr/bin/foo/baz (note: We might want to add this to realpath as well?) --- src/builtins/path.cpp | 23 ++++++++++++++++++++++- tests/checks/path.fish | 11 +++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/builtins/path.cpp b/src/builtins/path.cpp index 33ccc4ee3..82b1dd7f7 100644 --- a/src/builtins/path.cpp +++ b/src/builtins/path.cpp @@ -631,7 +631,28 @@ static int path_real(parser_t &parser, io_streams_t &streams, int argc, const wc auto real = wrealpath(*arg); if (!real) { - continue; + // The path doesn't exist, so we go up until we find + // something that does. + wcstring next = *arg; + // First add $PWD if we're relative + if (!next.empty() && next[0] != L'/') { + next = wgetcwd() + L"/" + next; + } + auto rest = wbasename(next); + while(!next.empty() && next != L"/") { + next = wdirname(next); + real = wrealpath(next); + if (real) { + next.push_back(L'/'); + next.append(rest); + real = normalize_path(next, false); + break; + } + rest = wbasename(next) + L'/' + rest; + } + if (!real) { + continue; + } } // Return 0 if we found a realpath. diff --git a/tests/checks/path.fish b/tests/checks/path.fish index f53b6c191..1a8de987b 100644 --- a/tests/checks/path.fish +++ b/tests/checks/path.fish @@ -110,3 +110,14 @@ path real bin//sh | string match -r -- 'bin/bash$' # The "//" is squashed, and the symlink is resolved. # sh here is bash # CHECK: bin/bash + +# `path real` with nonexistent paths +set -l path (path real foo/bar) +string match -rq "^"(string escape --style=regex -- $PWD)'/' -- $path +and echo It matches pwd! +# CHECK: It matches pwd! +string replace -r "^"(string escape --style=regex -- $PWD)'/' "" -- $path +# CHECK: foo/bar + +path real /banana//terracota/terracota/booooo/../pie +# CHECK: /banana/terracota/terracota/pie