Show more file information
Yet to be properly aligned.
This commit is contained in:
parent
c8498c9165
commit
5d9246a38a
160
sdn.cpp
160
sdn.cpp
@ -30,18 +30,22 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/inotify.h>
|
#include <sys/inotify.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <grp.h>
|
||||||
|
|
||||||
// Unicode is complex enough already and we might make assumptions
|
// Unicode is complex enough already and we might make assumptions
|
||||||
#ifndef __STDC_ISO_10646__
|
#ifndef __STDC_ISO_10646__
|
||||||
#error Unicode required for wchar_t
|
#error Unicode required for wchar_t
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Trailing return types make C++ syntax suck considerably less
|
||||||
|
#define fun static auto
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
// For some reason handling of encoding in C and C++ is extremely annoying
|
// For some reason handling of encoding in C and C++ is extremely annoying
|
||||||
// and C++17 ironically obsoletes C++11 additions that made it less painful
|
// and C++17 ironically obsoletes C++11 additions that made it less painful
|
||||||
static wstring
|
fun to_wide (const string &multi) -> wstring {
|
||||||
to_wide (const string &multi) {
|
|
||||||
wstring wide; wchar_t w; mbstate_t mb {};
|
wstring wide; wchar_t w; mbstate_t mb {};
|
||||||
size_t n = 0, len = multi.length () + 1;
|
size_t n = 0, len = multi.length () + 1;
|
||||||
while (auto res = mbrtowc (&w, multi.c_str () + n, len - n, &mb)) {
|
while (auto res = mbrtowc (&w, multi.c_str () + n, len - n, &mb)) {
|
||||||
@ -54,8 +58,7 @@ to_wide (const string &multi) {
|
|||||||
return wide;
|
return wide;
|
||||||
}
|
}
|
||||||
|
|
||||||
static string
|
fun to_mb (const wstring &wide) -> string {
|
||||||
to_mb (const wstring &wide) {
|
|
||||||
string mb; char buf[MB_LEN_MAX + 1]; mbstate_t mbs {};
|
string mb; char buf[MB_LEN_MAX + 1]; mbstate_t mbs {};
|
||||||
for (size_t n = 0; n <= wide.length (); n++) {
|
for (size_t n = 0; n <= wide.length (); n++) {
|
||||||
auto res = wcrtomb (buf, wide.c_str ()[n], &mbs);
|
auto res = wcrtomb (buf, wide.c_str ()[n], &mbs);
|
||||||
@ -68,8 +71,7 @@ to_mb (const wstring &wide) {
|
|||||||
return mb;
|
return mb;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
fun print (const wstring &wide, int limit) -> int {
|
||||||
print (const wstring &wide, int limit) {
|
|
||||||
int total_width = 0;
|
int total_width = 0;
|
||||||
for (wchar_t w : wide) {
|
for (wchar_t w : wide) {
|
||||||
// TODO: controls as ^X, show in inverse
|
// TODO: controls as ^X, show in inverse
|
||||||
@ -88,16 +90,14 @@ print (const wstring &wide, int limit) {
|
|||||||
return total_width;
|
return total_width;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
fun prefix (const wstring &in, const wstring &of) -> int {
|
||||||
prefix (const wstring &in, const wstring &of) {
|
|
||||||
int score = 0;
|
int score = 0;
|
||||||
for (size_t i = 0; i < of.size () && in.size () >= i && in[i] == of[i]; i++)
|
for (size_t i = 0; i < of.size () && in.size () >= i && in[i] == of[i]; i++)
|
||||||
score++;
|
score++;
|
||||||
return score;
|
return score;
|
||||||
}
|
}
|
||||||
|
|
||||||
static string
|
fun shell_escape (const string &v) -> string {
|
||||||
shell_escape (const string &v) {
|
|
||||||
string result;
|
string result;
|
||||||
for (auto c : v)
|
for (auto c : v)
|
||||||
if (c == '\'')
|
if (c == '\'')
|
||||||
@ -107,6 +107,35 @@ shell_escape (const string &v) {
|
|||||||
return "'" + result + "'";
|
return "'" + result + "'";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
using ncstring = basic_string<cchar_t>;
|
||||||
|
|
||||||
|
fun apply_attrs (const wstring &w, attr_t attrs) -> ncstring {
|
||||||
|
ncstring res;
|
||||||
|
for (auto c : w)
|
||||||
|
res.push_back ({attrs, {c}});
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
fun print_nc (const ncstring &nc, int limit) -> int {
|
||||||
|
int total_width = 0;
|
||||||
|
for (cchar_t c : nc) {
|
||||||
|
// TODO: controls as ^X, show in inverse
|
||||||
|
auto &w = c.chars[0];
|
||||||
|
if (!isprint (w))
|
||||||
|
w = L'?';
|
||||||
|
|
||||||
|
int width = wcwidth (w);
|
||||||
|
if (total_width + width > limit)
|
||||||
|
break;
|
||||||
|
|
||||||
|
add_wch (&c);
|
||||||
|
total_width += width;
|
||||||
|
}
|
||||||
|
return total_width;
|
||||||
|
}
|
||||||
|
|
||||||
// --- Application -------------------------------------------------------------
|
// --- Application -------------------------------------------------------------
|
||||||
|
|
||||||
#define CTRL 31 &
|
#define CTRL 31 &
|
||||||
@ -131,14 +160,71 @@ static struct {
|
|||||||
int inotify_fd, inotify_wd = -1;
|
int inotify_fd, inotify_wd = -1;
|
||||||
bool out_of_date;
|
bool out_of_date;
|
||||||
|
|
||||||
|
bool full_view;
|
||||||
|
|
||||||
wchar_t editor;
|
wchar_t editor;
|
||||||
wstring editor_line;
|
wstring editor_line;
|
||||||
} g;
|
} g;
|
||||||
|
|
||||||
static inline int visible_lines () { return max (0, LINES - 2); }
|
fun inline visible_lines () -> int { return max (0, LINES - 2); }
|
||||||
|
|
||||||
static void
|
fun make_mode (mode_t m) -> wstring {
|
||||||
update () {
|
auto type = L'-';
|
||||||
|
if (S_ISDIR (m)) type = L'd';
|
||||||
|
if (S_ISBLK (m)) type = L'b';
|
||||||
|
if (S_ISCHR (m)) type = L'c';
|
||||||
|
if (S_ISLNK (m)) type = L'l';
|
||||||
|
if (S_ISFIFO (m)) type = L'p';
|
||||||
|
if (S_ISSOCK (m)) type = L's';
|
||||||
|
|
||||||
|
wstring mode = {type};
|
||||||
|
mode += L"r-"[!(m & S_IRUSR)];
|
||||||
|
mode += L"w-"[!(m & S_IWUSR)];
|
||||||
|
mode += ((m & S_ISUID) ? L"sS" : L"x-")[!(m & S_IXUSR)];
|
||||||
|
mode += L"r-"[!(m & S_IRGRP)];
|
||||||
|
mode += L"w-"[!(m & S_IWGRP)];
|
||||||
|
mode += ((m & S_ISGID) ? L"sS" : L"x-")[!(m & S_IXGRP)];
|
||||||
|
mode += L"r-"[!(m & S_IROTH)];
|
||||||
|
mode += L"w-"[!(m & S_IWOTH)];
|
||||||
|
mode += ((m & S_ISVTX) ? L"tT" : L"x-")[!(m & S_IXOTH)];
|
||||||
|
return mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
fun make_row (const entry &entry) -> vector<ncstring> {
|
||||||
|
vector<ncstring> result;
|
||||||
|
const auto &info = entry.info;
|
||||||
|
result.push_back (apply_attrs (make_mode (info.st_mode), 0));
|
||||||
|
|
||||||
|
if (auto u = getpwuid (info.st_uid)) {
|
||||||
|
result.push_back (apply_attrs (to_wide (u->pw_name), 0));
|
||||||
|
} else {
|
||||||
|
result.push_back (apply_attrs (to_wstring (info.st_uid), 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto g = getgrgid (info.st_gid)) {
|
||||||
|
result.push_back (apply_attrs (to_wide (g->gr_name), 0));
|
||||||
|
} else {
|
||||||
|
result.push_back (apply_attrs (to_wstring (info.st_gid), 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: human-readable
|
||||||
|
result.push_back (apply_attrs (to_wstring (info.st_size), 0));
|
||||||
|
|
||||||
|
auto now = time (NULL);
|
||||||
|
auto now_year = localtime (&now)->tm_year;
|
||||||
|
|
||||||
|
char buf[32] = "";
|
||||||
|
auto tm = localtime (&info.st_mtime);
|
||||||
|
strftime (buf, sizeof buf,
|
||||||
|
(tm->tm_year == now_year) ? "%b %e %H:%M" : "%b %e %Y", tm);
|
||||||
|
result.push_back (apply_attrs (to_wide (buf), 0));
|
||||||
|
|
||||||
|
// TODO: symlink target and whatever formatting
|
||||||
|
result.push_back (apply_attrs (to_wide (entry.filename), 0));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
fun update () {
|
||||||
erase ();
|
erase ();
|
||||||
|
|
||||||
int available = visible_lines ();
|
int available = visible_lines ();
|
||||||
@ -149,23 +235,17 @@ update () {
|
|||||||
if (index == g.cursor)
|
if (index == g.cursor)
|
||||||
attron (A_REVERSE);
|
attron (A_REVERSE);
|
||||||
|
|
||||||
|
// TODO: use g.full_view, align properly (different columns differently)
|
||||||
|
// XXX: maybe this should return a struct instead
|
||||||
|
auto row = make_row (g.entries[index]);
|
||||||
|
|
||||||
move (available - used + i, 0);
|
move (available - used + i, 0);
|
||||||
auto &entry = g.entries[index];
|
auto limit = COLS, used = 0;
|
||||||
|
for (auto &i : row) {
|
||||||
// TODO display more information from "info"
|
used += print_nc (i, limit - used);
|
||||||
char modes[] = "- ";
|
used += print (L" ", limit - used);
|
||||||
const auto &stat = entry.info;
|
}
|
||||||
if (S_ISDIR (stat.st_mode)) modes[0] = 'd';
|
hline (' ', limit - used);
|
||||||
if (S_ISBLK (stat.st_mode)) modes[0] = 'b';
|
|
||||||
if (S_ISCHR (stat.st_mode)) modes[0] = 'c';
|
|
||||||
if (S_ISLNK (stat.st_mode)) modes[0] = 'l';
|
|
||||||
if (S_ISFIFO (stat.st_mode)) modes[0] = 'p';
|
|
||||||
if (S_ISSOCK (stat.st_mode)) modes[0] = 's';
|
|
||||||
addstr (modes);
|
|
||||||
|
|
||||||
// TODO show symbolic link target
|
|
||||||
auto width = COLS - 2;
|
|
||||||
hline (' ', width - print (to_wide (entry.filename), width));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
attrset (A_BOLD);
|
attrset (A_BOLD);
|
||||||
@ -186,8 +266,7 @@ update () {
|
|||||||
refresh ();
|
refresh ();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
fun reload () {
|
||||||
reload () {
|
|
||||||
char buf[4096];
|
char buf[4096];
|
||||||
g.cwd = getcwd (buf, sizeof buf);
|
g.cwd = getcwd (buf, sizeof buf);
|
||||||
|
|
||||||
@ -217,8 +296,7 @@ reload () {
|
|||||||
IN_ALL_EVENTS | IN_ONLYDIR);
|
IN_ALL_EVENTS | IN_ONLYDIR);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
fun search (const wstring &needle) {
|
||||||
search (const wstring &needle) {
|
|
||||||
int best = g.cursor, best_n = 0;
|
int best = g.cursor, best_n = 0;
|
||||||
for (int i = 0; i < int (g.entries.size ()); i++) {
|
for (int i = 0; i < int (g.entries.size ()); i++) {
|
||||||
auto o = (i + g.cursor) % g.entries.size ();
|
auto o = (i + g.cursor) % g.entries.size ();
|
||||||
@ -231,8 +309,7 @@ search (const wstring &needle) {
|
|||||||
g.cursor = best;
|
g.cursor = best;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
fun handle_editor (wint_t c, bool is_char) {
|
||||||
handle_editor (wint_t c, bool is_char) {
|
|
||||||
if (c == 27 || c == (CTRL L'g')) {
|
if (c == 27 || c == (CTRL L'g')) {
|
||||||
g.editor_line.clear ();
|
g.editor_line.clear ();
|
||||||
g.editor = 0;
|
g.editor = 0;
|
||||||
@ -256,8 +333,7 @@ handle_editor (wint_t c, bool is_char) {
|
|||||||
beep ();
|
beep ();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
fun handle (wint_t c, bool is_char) -> bool {
|
||||||
handle (wint_t c, bool is_char) {
|
|
||||||
// If an editor is active, let it handle the key instead and eat it
|
// If an editor is active, let it handle the key instead and eat it
|
||||||
if (g.editor) {
|
if (g.editor) {
|
||||||
handle_editor (c, is_char);
|
handle_editor (c, is_char);
|
||||||
@ -329,6 +405,10 @@ handle (wint_t c, bool is_char) {
|
|||||||
case CTRL L'e': g.offset++; break;
|
case CTRL L'e': g.offset++; break;
|
||||||
case CTRL L'y': g.offset--; break;
|
case CTRL L'y': g.offset--; break;
|
||||||
|
|
||||||
|
case ALT | L't':
|
||||||
|
g.full_view = !g.full_view;
|
||||||
|
break;
|
||||||
|
|
||||||
case ALT | L'e':
|
case ALT | L'e':
|
||||||
g.editor_line = to_wide (current.filename);
|
g.editor_line = to_wide (current.filename);
|
||||||
// Fall-through
|
// Fall-through
|
||||||
@ -368,8 +448,7 @@ handle (wint_t c, bool is_char) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
fun inotify_check () {
|
||||||
inotify_check () {
|
|
||||||
// Only provide simple indication that contents might have changed
|
// Only provide simple indication that contents might have changed
|
||||||
char buf[4096]; ssize_t len;
|
char buf[4096]; ssize_t len;
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
@ -385,8 +464,7 @@ inotify_check () {
|
|||||||
update ();
|
update ();
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int main (int argc, char *argv[]) {
|
||||||
main (int argc, char *argv[]) {
|
|
||||||
(void) argc;
|
(void) argc;
|
||||||
(void) argv;
|
(void) argv;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user