Show more file information

Yet to be properly aligned.
This commit is contained in:
Přemysl Eric Janouch 2017-06-30 01:39:49 +02:00
parent c8498c9165
commit 5d9246a38a
Signed by: p
GPG Key ID: B715679E3A361BE6

160
sdn.cpp
View File

@ -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;