Add support for BSD derivatives, fix macOS
All checks were successful
Alpine 3.20 Success
Arch Linux AUR Success

This commit is contained in:
Přemysl Eric Janouch 2024-12-21 07:37:06 +01:00
parent 3607757554
commit 52a28f01c8
Signed by: p
GPG Key ID: A0420B94F92B9493
3 changed files with 78 additions and 25 deletions

View File

@ -8,11 +8,13 @@ if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")
endif () endif ()
find_package (PkgConfig REQUIRED) find_package (PkgConfig REQUIRED)
pkg_check_modules (NCURSESW QUIET ncursesw) pkg_check_modules (NCURSESW REQUIRED ncursesw)
pkg_check_modules (ACL libacl)
add_executable (${PROJECT_NAME} ${PROJECT_NAME}.cpp) add_executable (${PROJECT_NAME} ${PROJECT_NAME}.cpp)
target_include_directories (${PROJECT_NAME} PUBLIC ${NCURSESW_INCLUDE_DIRS}) target_include_directories (${PROJECT_NAME} PUBLIC ${NCURSESW_INCLUDE_DIRS})
target_link_libraries (${PROJECT_NAME} PUBLIC ${NCURSESW_LIBRARIES} acl) target_link_libraries (${PROJECT_NAME}
PUBLIC ${NCURSESW_LIBRARIES} ${ACL_LIBRARIES})
target_compile_features (${PROJECT_NAME} PUBLIC cxx_std_14) target_compile_features (${PROJECT_NAME} PUBLIC cxx_std_14)
target_compile_definitions (${PROJECT_NAME} PUBLIC target_compile_definitions (${PROJECT_NAME} PUBLIC
-DPROJECT_NAME=\"${PROJECT_NAME}\" -DPROJECT_VERSION=\"${PROJECT_VERSION}\") -DPROJECT_NAME=\"${PROJECT_NAME}\" -DPROJECT_VERSION=\"${PROJECT_VERSION}\")

View File

@ -11,8 +11,8 @@ commands. It enables you to:
can be simply forwarded if it is to be edited. What's more, it will always can be simply forwarded if it is to be edited. What's more, it will always
be obvious whether the navigator is running. be obvious whether the navigator is running.
The only supported platform is Linux. I wanted to try a different, simpler 'sdn' runs on Linux and all BSD derivatives. I wanted to try a different,
approach here, and the end result is very friendly to tinkering. simpler approach here, and the end result is very friendly to tinkering.
image::sdn.png[align="center"] image::sdn.png[align="center"]

93
sdn.cpp
View File

@ -45,9 +45,13 @@
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
#ifdef __linux__
#include <acl/libacl.h> #include <acl/libacl.h>
#include <ncurses.h>
#include <sys/inotify.h> #include <sys/inotify.h>
#else
#include <sys/event.h>
#endif
#include <ncurses.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <sys/xattr.h> #include <sys/xattr.h>
@ -57,9 +61,10 @@
#include <term.h> #include <term.h>
#undef CTRL // term.h -> termios.h -> sys/ttydefaults.h, too simplistic #undef CTRL // term.h -> termios.h -> sys/ttydefaults.h, too simplistic
// Unicode is complex enough already and we might make assumptions
#ifndef __STDC_ISO_10646__ #ifndef __STDC_ISO_10646__
#error Unicode required for wchar_t // Unicode is complex enough already and we might make assumptions,
// though macOS doesn't define this despite using UCS-4,
// and we won't build on Windows that seems to be the only one to use UTF-16.
#endif #endif
// Trailing return types make C++ syntax suck considerably less // Trailing return types make C++ syntax suck considerably less
@ -303,7 +308,21 @@ fun xdg_config_write (const string &suffix) -> unique_ptr<fstream> {
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
using ncstring = basic_string<cchar_t>; // This should be basic_string, however that crashes on macOS
using ncstring = vector<cchar_t>;
fun operator+ (const ncstring &lhs, const ncstring &rhs) -> ncstring {
ncstring result;
result.reserve (lhs.size () + rhs.size ());
result.insert (result.end (), lhs.begin (), lhs.end ());
result.insert (result.end (), rhs.begin (), rhs.end ());
return result;
}
fun operator+= (ncstring &lhs, const ncstring &rhs) -> ncstring & {
lhs.insert (lhs.end (), rhs.begin (), rhs.end ());
return lhs;
}
fun cchar (chtype attrs, wchar_t c) -> cchar_t { fun cchar (chtype attrs, wchar_t c) -> cchar_t {
cchar_t ch {}; wchar_t ws[] = {c, 0}; cchar_t ch {}; wchar_t ws[] = {c, 0};
@ -538,7 +557,7 @@ static struct {
bool no_chdir; ///< Do not tell the shell to chdir bool no_chdir; ///< Do not tell the shell to chdir
bool quitting; ///< Whether we should quit already bool quitting; ///< Whether we should quit already
int inotify_fd, inotify_wd = -1; ///< File watch int watch_fd, watch_wd = -1; ///< File watch (inotify/kqueue)
bool out_of_date; ///< Entries may be out of date bool out_of_date; ///< Entries may be out of date
const wchar_t *editor; ///< Prompt string for editing const wchar_t *editor; ///< Prompt string for editing
@ -597,8 +616,10 @@ fun ls_format (const entry &e, bool for_target) -> chtype {
set (LS_MULTIHARDLINK); set (LS_MULTIHARDLINK);
if ((info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) if ((info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
set (LS_EXECUTABLE); set (LS_EXECUTABLE);
#ifdef __linux__
if (lgetxattr (name.c_str (), "security.capability", NULL, 0) >= 0) if (lgetxattr (name.c_str (), "security.capability", NULL, 0) >= 0)
set (LS_CAPABILITY); set (LS_CAPABILITY);
#endif
if ((info.st_mode & S_ISGID)) if ((info.st_mode & S_ISGID))
set (LS_SETGID); set (LS_SETGID);
if ((info.st_mode & S_ISUID)) if ((info.st_mode & S_ISUID))
@ -692,11 +713,13 @@ fun make_entry (const struct dirent *f) -> entry {
} }
auto mode = decode_mode (info.st_mode); auto mode = decode_mode (info.st_mode);
#ifdef __linux__
// We're using a laughably small subset of libacl: this translates to // We're using a laughably small subset of libacl: this translates to
// two lgetxattr() calls, the results of which are compared with // two lgetxattr() calls, the results of which are compared with
// specific architecture-dependent constants. Linux-only. // specific architecture-dependent constants. Linux-only.
if (acl_extended_file_nofollow (f->d_name) > 0) if (acl_extended_file_nofollow (f->d_name) > 0)
mode += L"+"; mode += L"+";
#endif
e.cols[entry::MODES] = apply_attrs (mode, 0); e.cols[entry::MODES] = apply_attrs (mode, 0);
auto usr = g.unames.find (info.st_uid); auto usr = g.unames.find (info.st_uid);
@ -726,8 +749,8 @@ fun make_entry (const struct dirent *f) -> entry {
auto &fn = e.cols[entry::FILENAME] = auto &fn = e.cols[entry::FILENAME] =
apply_attrs (to_wide (e.filename), ls_format (e, false)); apply_attrs (to_wide (e.filename), ls_format (e, false));
if (!e.target_path.empty ()) { if (!e.target_path.empty ()) {
fn.append (apply_attrs (L" -> ", 0)); fn += apply_attrs (L" -> ", 0);
fn.append (apply_attrs (to_wide (e.target_path), ls_format (e, true))); fn += apply_attrs (to_wide (e.target_path), ls_format (e, true));
} }
return e; return e;
} }
@ -797,8 +820,8 @@ fun update () {
print (info, info_width); print (info, info_width);
} }
auto start = sanitize (prompt + line.substr (0, g.editor_cursor)); line.resize (g.editor_cursor);
move (LINES - 1, compute_width (start)); move (LINES - 1, compute_width (sanitize (prompt + line)));
curs_set (1); curs_set (1);
} else if (!g.message.empty ()) { } else if (!g.message.empty ()) {
move (LINES - 1, 0); move (LINES - 1, 0);
@ -921,12 +944,25 @@ readfail:
g.cursor = max (0, min (g.cursor, int (g.entries.size ()) - 1)); g.cursor = max (0, min (g.cursor, int (g.entries.size ()) - 1));
g.offset = max (0, min (g.offset, int (g.entries.size ()) - 1)); g.offset = max (0, min (g.offset, int (g.entries.size ()) - 1));
if (g.inotify_wd != -1) #ifdef __linux__
inotify_rm_watch (g.inotify_fd, g.inotify_wd); if (g.watch_wd != -1)
inotify_rm_watch (g.watch_fd, g.watch_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
g.inotify_wd = inotify_add_watch (g.inotify_fd, ".", g.watch_wd = inotify_add_watch (g.watch_fd, ".",
(IN_ALL_EVENTS | IN_ONLYDIR | IN_EXCL_UNLINK) & ~(IN_ACCESS | IN_OPEN)); (IN_ALL_EVENTS | IN_ONLYDIR | IN_EXCL_UNLINK) & ~(IN_ACCESS | IN_OPEN));
#else
if (g.watch_wd != -1)
close (g.watch_wd);
if ((g.watch_wd = open (".", O_RDONLY | O_DIRECTORY | O_CLOEXEC)) >= 0) {
// At least the macOS kqueue doesn't report anything too specific
struct kevent ev {};
EV_SET (&ev, g.watch_wd, EVFILT_VNODE, EV_ADD | EV_CLEAR,
NOTE_WRITE | NOTE_LINK, 0, nullptr);
(void) kevent (g.watch_fd, &ev, 1, nullptr, 0, nullptr);
}
#endif
} }
fun run_program (initializer_list<const char *> list, const string &filename) { fun run_program (initializer_list<const char *> list, const string &filename) {
@ -1550,19 +1586,27 @@ fun handle (wint_t c) -> bool {
return !g.quitting; return !g.quitting;
} }
fun inotify_check () { fun watch_check () {
// Only provide simple indication that contents might have changed
char buf[4096]; ssize_t len;
bool changed = false; bool changed = false;
while ((len = read (g.inotify_fd, buf, sizeof buf)) > 0) { // Only provide simple indication that contents might have changed,
// if only because kqueue can't do any better
#ifdef __linux__
char buf[4096]; ssize_t len;
while ((len = read (g.watch_fd, buf, sizeof buf)) > 0) {
const inotify_event *e; const inotify_event *e;
for (char *ptr = buf; ptr < buf + len; ptr += sizeof *e + e->len) { for (char *ptr = buf; ptr < buf + len; ptr += sizeof *e + e->len) {
e = (const inotify_event *) buf; e = (const inotify_event *) buf;
if (e->wd == g.inotify_wd) if (e->wd == g.watch_wd)
changed = g.out_of_date = true; changed = true;
} }
} }
if (changed) #else
struct kevent ev {};
struct timespec timeout {};
if (kevent (g.watch_fd, nullptr, 0, &ev, 1, &timeout) > 0)
changed = ev.filter == EVFILT_VNODE && (ev.fflags & NOTE_WRITE);
#endif
if ((g.out_of_date = changed))
update (); update ();
} }
@ -1884,10 +1928,17 @@ int main (int argc, char *argv[]) {
// So that the neither us nor our children stop on tcsetpgrp() // So that the neither us nor our children stop on tcsetpgrp()
signal (SIGTTOU, SIG_IGN); signal (SIGTTOU, SIG_IGN);
if ((g.inotify_fd = inotify_init1 (IN_NONBLOCK)) < 0) { #ifdef __linux__
if ((g.watch_fd = inotify_init1 (IN_NONBLOCK)) < 0) {
cerr << "cannot initialize inotify" << endl; cerr << "cannot initialize inotify" << endl;
return 1; return 1;
} }
#else
if ((g.watch_fd = kqueue ()) < 0) {
cerr << "cannot initialize kqueue" << endl;
return 1;
}
#endif
locale::global (locale ("")); locale::global (locale (""));
load_bindings (); load_bindings ();
@ -1924,7 +1975,7 @@ int main (int argc, char *argv[]) {
wint_t c; wint_t c;
while (!read_key (c) || handle (c)) { while (!read_key (c) || handle (c)) {
inotify_check (); watch_check ();
if (g.sort_flash_ttl && !--g.sort_flash_ttl) if (g.sort_flash_ttl && !--g.sort_flash_ttl)
update (); update ();
if (g.message_ttl && !--g.message_ttl) { if (g.message_ttl && !--g.message_ttl) {