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;
|
return score;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: this omits empty elements, check usages
|
||||||
fun split (const string &s, const string &sep, vector<string> &out) {
|
fun split (const string &s, const string &sep, vector<string> &out) {
|
||||||
size_t mark = 0, p = s.find (sep);
|
size_t mark = 0, p = s.find (sep);
|
||||||
for (; p != string::npos; p = s.find (sep, (mark = p + sep.length ())))
|
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;
|
return a.filename < b.filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
fun reload () {
|
fun reload (const string &old_cwd) {
|
||||||
g.unames.clear();
|
g.unames.clear();
|
||||||
while (auto *ent = getpwent ())
|
while (auto *ent = getpwent ())
|
||||||
g.unames.emplace (ent->pw_uid, ent->pw_name);
|
g.unames.emplace (ent->pw_uid, ent->pw_name);
|
||||||
@ -757,10 +758,7 @@ fun reload () {
|
|||||||
if (!g.entries.empty ())
|
if (!g.entries.empty ())
|
||||||
anchor = g.entries[g.cursor].filename;
|
anchor = g.entries[g.cursor].filename;
|
||||||
|
|
||||||
auto old_cwd = g.cwd;
|
|
||||||
auto now = time (NULL); g.now = *localtime (&now);
|
auto now = time (NULL); g.now = *localtime (&now);
|
||||||
char buf[4096]; g.cwd = getcwd (buf, sizeof buf);
|
|
||||||
|
|
||||||
auto dir = opendir (".");
|
auto dir = opendir (".");
|
||||||
g.entries.clear ();
|
g.entries.clear ();
|
||||||
while (auto f = readdir (dir)) {
|
while (auto f = readdir (dir)) {
|
||||||
@ -792,6 +790,7 @@ fun reload () {
|
|||||||
inotify_rm_watch (g.inotify_fd, g.inotify_wd);
|
inotify_rm_watch (g.inotify_fd, g.inotify_wd);
|
||||||
|
|
||||||
// We don't show atime, so access and open are merely spam
|
// 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,
|
g.inotify_wd = inotify_add_watch (g.inotify_fd, buf,
|
||||||
(IN_ALL_EVENTS | IN_ONLYDIR | IN_EXCL_UNLINK) & ~(IN_ACCESS | IN_OPEN));
|
(IN_ALL_EVENTS | IN_ONLYDIR | IN_EXCL_UNLINK) & ~(IN_ACCESS | IN_OPEN));
|
||||||
}
|
}
|
||||||
@ -954,15 +953,88 @@ fun pop_levels () {
|
|||||||
search (to_wide (anchor));
|
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) {
|
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));
|
show_message (strerror (errno));
|
||||||
beep ();
|
beep ();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
level last {g.offset, g.cursor, g.cwd, g.entries[g.cursor].filename};
|
auto old_cwd = g.cwd;
|
||||||
reload ();
|
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)) {
|
if (is_ancestor_dir (last.path, g.cwd)) {
|
||||||
g.levels.push_back (last);
|
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) {
|
fun choose (const entry &entry) {
|
||||||
// Dive into directories and accessible symlinks to them
|
// Dive into directories and accessible symlinks to them
|
||||||
if (!S_ISDIR (entry.info.st_mode)
|
if (!S_ISDIR (entry.info.st_mode)
|
||||||
@ -1048,12 +1143,12 @@ fun handle (wint_t c) -> bool {
|
|||||||
case ACTION_SORT_LEFT:
|
case ACTION_SORT_LEFT:
|
||||||
g.sort_column = (g.sort_column + entry::COLUMNS - 1) % entry::COLUMNS;
|
g.sort_column = (g.sort_column + entry::COLUMNS - 1) % entry::COLUMNS;
|
||||||
g.sort_flash_ttl = 2;
|
g.sort_flash_ttl = 2;
|
||||||
reload ();
|
reload (g.cwd);
|
||||||
break;
|
break;
|
||||||
case ACTION_SORT_RIGHT:
|
case ACTION_SORT_RIGHT:
|
||||||
g.sort_column = (g.sort_column + entry::COLUMNS + 1) % entry::COLUMNS;
|
g.sort_column = (g.sort_column + entry::COLUMNS + 1) % entry::COLUMNS;
|
||||||
g.sort_flash_ttl = 2;
|
g.sort_flash_ttl = 2;
|
||||||
reload ();
|
reload (g.cwd);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ACTION_UP:
|
case ACTION_UP:
|
||||||
@ -1115,7 +1210,7 @@ fun handle (wint_t c) -> bool {
|
|||||||
g.editor_on_confirm = [] {
|
g.editor_on_confirm = [] {
|
||||||
auto mb = to_mb (g.editor_line);
|
auto mb = to_mb (g.editor_line);
|
||||||
rename (g.entries[g.cursor].filename.c_str (), mb.c_str ());
|
rename (g.entries[g.cursor].filename.c_str (), mb.c_str ());
|
||||||
reload ();
|
reload (g.cwd);
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1124,17 +1219,17 @@ fun handle (wint_t c) -> bool {
|
|||||||
break;
|
break;
|
||||||
case ACTION_REVERSE_SORT:
|
case ACTION_REVERSE_SORT:
|
||||||
g.reverse_sort = !g.reverse_sort;
|
g.reverse_sort = !g.reverse_sort;
|
||||||
reload ();
|
reload (g.cwd);
|
||||||
break;
|
break;
|
||||||
case ACTION_SHOW_HIDDEN:
|
case ACTION_SHOW_HIDDEN:
|
||||||
g.show_hidden = !g.show_hidden;
|
g.show_hidden = !g.show_hidden;
|
||||||
reload ();
|
reload (g.cwd);
|
||||||
break;
|
break;
|
||||||
case ACTION_REDRAW:
|
case ACTION_REDRAW:
|
||||||
clear ();
|
clear ();
|
||||||
break;
|
break;
|
||||||
case ACTION_RELOAD:
|
case ACTION_RELOAD:
|
||||||
reload ();
|
reload (g.cwd);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (c != KEY (RESIZE) && c != WEOF)
|
if (c != KEY (RESIZE) && c != WEOF)
|
||||||
@ -1448,8 +1543,8 @@ int main (int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
load_colors ();
|
load_colors ();
|
||||||
reload ();
|
g.start_dir = g.cwd = initial_cwd ();
|
||||||
g.start_dir = g.cwd;
|
reload (g.cwd);
|
||||||
pop_levels ();
|
pop_levels ();
|
||||||
update ();
|
update ();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user