Embrace paths with symbolic links
This commit is contained in:
parent
4ce6454ebb
commit
71fbaca9e5
125
sdn.cpp
125
sdn.cpp
@ -98,6 +98,7 @@ fun prefix_length (const wstring &in, const wstring &of) -> int {
|
||||
return score;
|
||||
}
|
||||
|
||||
// TODO: this omits empty elements, check usages
|
||||
fun split (const string &s, const string &sep, vector<string> &out) {
|
||||
size_t mark = 0, p = s.find (sep);
|
||||
for (; p != string::npos; p = s.find (sep, (mark = p + sep.length ())))
|
||||
@ -742,7 +743,7 @@ fun operator< (const entry &e1, const entry &e2) -> bool {
|
||||
return a.filename < b.filename;
|
||||
}
|
||||
|
||||
fun reload () {
|
||||
fun reload (const string &old_cwd) {
|
||||
g.unames.clear();
|
||||
while (auto *ent = getpwent ())
|
||||
g.unames.emplace (ent->pw_uid, ent->pw_name);
|
||||
@ -757,10 +758,7 @@ fun reload () {
|
||||
if (!g.entries.empty ())
|
||||
anchor = g.entries[g.cursor].filename;
|
||||
|
||||
auto old_cwd = g.cwd;
|
||||
auto now = time (NULL); g.now = *localtime (&now);
|
||||
char buf[4096]; g.cwd = getcwd (buf, sizeof buf);
|
||||
|
||||
auto dir = opendir (".");
|
||||
g.entries.clear ();
|
||||
while (auto f = readdir (dir)) {
|
||||
@ -792,6 +790,7 @@ fun reload () {
|
||||
inotify_rm_watch (g.inotify_fd, g.inotify_wd);
|
||||
|
||||
// We don't show atime, so access and open are merely spam
|
||||
char buf[PATH_MAX];
|
||||
g.inotify_wd = inotify_add_watch (g.inotify_fd, buf,
|
||||
(IN_ALL_EVENTS | IN_ONLYDIR | IN_EXCL_UNLINK) & ~(IN_ACCESS | IN_OPEN));
|
||||
}
|
||||
@ -954,15 +953,88 @@ fun pop_levels () {
|
||||
search (to_wide (anchor));
|
||||
}
|
||||
|
||||
fun explode_path (const string &path, vector<string> &out) {
|
||||
size_t mark = 0, p = path.find ("/");
|
||||
for (; p != string::npos; p = path.find ("/", (mark = p + 1)))
|
||||
out.push_back (path.substr (mark, p - mark));
|
||||
if (mark < path.length ())
|
||||
out.push_back (path.substr (mark));
|
||||
}
|
||||
|
||||
fun serialize_path (const vector<string> &components) -> string {
|
||||
string result;
|
||||
for (const auto &i : components)
|
||||
result.append (i).append ("/");
|
||||
|
||||
auto n = result.find_last_not_of ('/');
|
||||
if (n != result.npos)
|
||||
return result.erase (n + 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
fun absolutize (const string &abs_base, const string &path) -> string {
|
||||
if (path[0] == '/')
|
||||
return path;
|
||||
if (!abs_base.empty () && abs_base.back () == '/')
|
||||
return abs_base + path;
|
||||
return abs_base + "/" + path;
|
||||
}
|
||||
|
||||
fun relativize (string current, const string &path) -> string {
|
||||
if (current == path)
|
||||
return ".";
|
||||
if (current.back () != '/')
|
||||
current += '/';
|
||||
if (!strncmp (current.c_str (), path.c_str (), current.length ()))
|
||||
return path.substr (current.length ());
|
||||
return path;
|
||||
}
|
||||
|
||||
// Roughly follows the POSIX description of `cd -L` because of symlinks.
|
||||
// HOME and CDPATH handling is ommitted.
|
||||
fun change_dir (const string &path) {
|
||||
if (chdir (path.c_str ())) {
|
||||
if (g.cwd[0] != '/') {
|
||||
show_message ("cannot figure out absolute path");
|
||||
beep ();
|
||||
return;
|
||||
}
|
||||
|
||||
vector<string> in, out;
|
||||
explode_path (absolutize (g.cwd, path), in);
|
||||
|
||||
// Paths with exactly two leading slashes may get special treatment
|
||||
int startempty = 1;
|
||||
if (in.size () >= 2 && in[1] == "" && (in.size () < 3 || in[2] != ""))
|
||||
startempty = 2;
|
||||
|
||||
struct stat s{};
|
||||
for (size_t i = 0; i < in.size (); i++)
|
||||
if (in[i] == "..") {
|
||||
auto parent = relativize (g.cwd, serialize_path (out));
|
||||
if (errno = 0, !stat (parent.c_str (), &s) && !S_ISDIR (s.st_mode))
|
||||
errno = ENOTDIR;
|
||||
if (errno) {
|
||||
show_message (parent + ": " + strerror (errno));
|
||||
beep ();
|
||||
return;
|
||||
}
|
||||
if (!out.back().empty ())
|
||||
out.pop_back ();
|
||||
} else if (in[i] != "." && (!in[i].empty () || i < startempty)) {
|
||||
out.push_back (in[i]);
|
||||
}
|
||||
|
||||
auto full_path = serialize_path (out);
|
||||
if (chdir (relativize (g.cwd, full_path).c_str ())) {
|
||||
show_message (strerror (errno));
|
||||
beep ();
|
||||
return;
|
||||
}
|
||||
|
||||
level last {g.offset, g.cursor, g.cwd, g.entries[g.cursor].filename};
|
||||
reload ();
|
||||
auto old_cwd = g.cwd;
|
||||
level last {g.offset, g.cursor, old_cwd, g.entries[g.cursor].filename};
|
||||
g.cwd = full_path;
|
||||
reload (old_cwd);
|
||||
|
||||
if (is_ancestor_dir (last.path, g.cwd)) {
|
||||
g.levels.push_back (last);
|
||||
@ -972,6 +1044,29 @@ fun change_dir (const string &path) {
|
||||
}
|
||||
}
|
||||
|
||||
// Roughly follows the POSIX description of the PWD environment variable
|
||||
fun initial_cwd () -> string {
|
||||
char cwd[4096] = ""; getcwd (cwd, sizeof cwd);
|
||||
const char *pwd = getenv ("PWD");
|
||||
if (!pwd || pwd[0] != '/' || strlen (pwd) >= PATH_MAX)
|
||||
return cwd;
|
||||
|
||||
// Extra slashes shouldn't break anything for us
|
||||
vector<string> components;
|
||||
explode_path (pwd, components);
|
||||
for (const auto &i : components) {
|
||||
if (i == "." || i == "..")
|
||||
return cwd;
|
||||
}
|
||||
|
||||
// Check if it "is an absolute pathname of the current working directory."
|
||||
// This particular method won't match on bind mounts, which is desired.
|
||||
char *real = realpath (pwd, nullptr);
|
||||
bool ok = real && !strcmp (cwd, real);
|
||||
free (real);
|
||||
return ok ? pwd : cwd;
|
||||
}
|
||||
|
||||
fun choose (const entry &entry) {
|
||||
// Dive into directories and accessible symlinks to them
|
||||
if (!S_ISDIR (entry.info.st_mode)
|
||||
@ -1048,12 +1143,12 @@ fun handle (wint_t c) -> bool {
|
||||
case ACTION_SORT_LEFT:
|
||||
g.sort_column = (g.sort_column + entry::COLUMNS - 1) % entry::COLUMNS;
|
||||
g.sort_flash_ttl = 2;
|
||||
reload ();
|
||||
reload (g.cwd);
|
||||
break;
|
||||
case ACTION_SORT_RIGHT:
|
||||
g.sort_column = (g.sort_column + entry::COLUMNS + 1) % entry::COLUMNS;
|
||||
g.sort_flash_ttl = 2;
|
||||
reload ();
|
||||
reload (g.cwd);
|
||||
break;
|
||||
|
||||
case ACTION_UP:
|
||||
@ -1115,7 +1210,7 @@ fun handle (wint_t c) -> bool {
|
||||
g.editor_on_confirm = [] {
|
||||
auto mb = to_mb (g.editor_line);
|
||||
rename (g.entries[g.cursor].filename.c_str (), mb.c_str ());
|
||||
reload ();
|
||||
reload (g.cwd);
|
||||
};
|
||||
break;
|
||||
|
||||
@ -1124,17 +1219,17 @@ fun handle (wint_t c) -> bool {
|
||||
break;
|
||||
case ACTION_REVERSE_SORT:
|
||||
g.reverse_sort = !g.reverse_sort;
|
||||
reload ();
|
||||
reload (g.cwd);
|
||||
break;
|
||||
case ACTION_SHOW_HIDDEN:
|
||||
g.show_hidden = !g.show_hidden;
|
||||
reload ();
|
||||
reload (g.cwd);
|
||||
break;
|
||||
case ACTION_REDRAW:
|
||||
clear ();
|
||||
break;
|
||||
case ACTION_RELOAD:
|
||||
reload ();
|
||||
reload (g.cwd);
|
||||
break;
|
||||
default:
|
||||
if (c != KEY (RESIZE) && c != WEOF)
|
||||
@ -1448,8 +1543,8 @@ int main (int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
load_colors ();
|
||||
reload ();
|
||||
g.start_dir = g.cwd;
|
||||
g.start_dir = g.cwd = initial_cwd ();
|
||||
reload (g.cwd);
|
||||
pop_levels ();
|
||||
update ();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user