From 6e34f480a917a9984afee64eae4dbefe2fd3cbcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Janouch?= Date: Thu, 25 Oct 2018 17:26:36 +0200 Subject: Finish copying the GNU ls coloring algorithm --- sdn.cpp | 58 ++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 20 deletions(-) (limited to 'sdn.cpp') diff --git a/sdn.cpp b/sdn.cpp index 0acfceb..47c10d1 100644 --- a/sdn.cpp +++ b/sdn.cpp @@ -318,6 +318,7 @@ static struct { map ls_colors; ///< LS_COLORS decoded map ls_exts; ///< LS_COLORS file extensions + bool ls_symlink_as_target; ///< ln=target in dircolors // Refreshed by reload(): @@ -326,17 +327,34 @@ static struct { struct tm now; ///< Current local time for display } g; -fun ls_format (const string &filename, const struct stat &info) -> chtype { +// The coloring logic has been more or less exactly copied from GNU ls, +// simplified and rewritten to reflect local implementation specifics +fun ls_is_colored (int type) -> bool { + auto i = g.ls_colors.find (type); + return i != g.ls_colors.end () && i->second != 0; +} + +fun ls_format (const entry &e, bool for_target) -> chtype { int type = LS_ORPHAN; - auto set = [&](int t) { if (g.ls_colors.count (t)) type = t; }; - // TODO: LS_MISSING if available and this is a missing symlink target - if (S_ISREG (info.st_mode)) { + auto set = [&](int t) { if (ls_is_colored (t)) type = t; }; + + const auto &name = for_target + ? e.target_path : e.filename; + const auto &info = + (for_target || (g.ls_symlink_as_target && e.target_info.st_mode)) + ? e.target_info : e.info; + + if (for_target && info.st_mode == 0) { + // This differs from GNU ls: we use ORPHAN when MISSING is not set, + // but GNU ls colors by dirent::d_type + set (LS_MISSING); + } else if (S_ISREG (info.st_mode)) { type = LS_FILE; if (info.st_nlink > 1) set (LS_MULTIHARDLINK); if ((info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) set (LS_EXECUTABLE); - if (lgetxattr (filename.c_str (), "security.capability", NULL, 0) >= 0) + if (lgetxattr (name.c_str (), "security.capability", NULL, 0) >= 0) set (LS_CAPABILITY); if ((info.st_mode & S_ISGID)) set (LS_SETGID); @@ -351,10 +369,10 @@ fun ls_format (const string &filename, const struct stat &info) -> chtype { if ((info.st_mode & S_ISVTX) && (info.st_mode & S_IWOTH)) set (LS_STICKY_OTHER_WRITABLE); } else if (S_ISLNK (info.st_mode)) { - // TODO: LS_ORPHAN when symlink target is missing and either - // a/ "li" is "target", or - // b/ LS_ORPHAN is available type = LS_SYMLINK; + if (!e.target_info.st_mode + && (ls_is_colored (LS_ORPHAN) || g.ls_symlink_as_target)) + type = LS_ORPHAN; } else if (S_ISFIFO (info.st_mode)) { type = LS_FIFO; } else if (S_ISSOCK (info.st_mode)) { @@ -370,9 +388,9 @@ fun ls_format (const string &filename, const struct stat &info) -> chtype { if (x != g.ls_colors.end ()) format = x->second; - auto dot = filename.find_last_of ('.'); + auto dot = name.find_last_of ('.'); if (dot != string::npos && type == LS_FILE) { - const auto x = g.ls_exts.find (filename.substr (++dot)); + const auto x = g.ls_exts.find (name.substr (++dot)); if (x != g.ls_exts.end ()) format = x->second; } @@ -395,8 +413,8 @@ fun make_entry (const struct dirent *f) -> entry { e.cols[entry::USER] = e.cols[entry::GROUP] = e.cols[entry::SIZE] = e.cols[entry::MTIME] = apply_attrs (L"?", 0); - auto format = ls_format (e.filename, info); - e.cols[entry::FILENAME] = apply_attrs (to_wide (e.filename), format); + e.cols[entry::FILENAME] = + apply_attrs (to_wide (e.filename), ls_format (e, false)); return e; } @@ -407,7 +425,8 @@ fun make_entry (const struct dirent *f) -> entry { e.target_path = "?"; } else { e.target_path = buf; - (void) lstat (buf, &e.target_info); + // If a symlink links to another symlink, we follow all the way + (void) stat (buf, &e.target_info); } } @@ -441,11 +460,10 @@ fun make_entry (const struct dirent *f) -> entry { e.cols[entry::MTIME] = apply_attrs (to_wide (buf), 0); auto &fn = e.cols[entry::FILENAME] = - apply_attrs (to_wide (e.filename), ls_format (e.filename, info)); + apply_attrs (to_wide (e.filename), ls_format (e, false)); if (!e.target_path.empty ()) { fn.append (apply_attrs (to_wide (" -> "), 0)); - fn.append (apply_attrs (to_wide (e.target_path), - ls_format (e.target_path, e.target_info))); + fn.append (apply_attrs (to_wide (e.target_path), ls_format (e, true))); } return e; } @@ -767,11 +785,11 @@ fun load_ls_colors (vector colors) { auto equal = pair.find ('='); if (equal == string::npos) continue; - attrs[pair.substr (0, equal)] = - decode_ansi_sgr (split (pair.substr (equal + 1), ";")); + auto key = pair.substr (0, equal), value = pair.substr (equal + 1); + if (key != g_ls_colors[LS_SYMLINK] + || !(g.ls_symlink_as_target = value == "target")) + attrs[key] = decode_ansi_sgr (split (value, ";")); } - - // `LINK target` i.e. `ln=target` is not supported now for (int i = 0; i < LS_COUNT; i++) { auto m = attrs.find (g_ls_colors[i]); if (m != attrs.end ()) -- cgit v1.2.3