diff options
| -rw-r--r-- | sfm.cpp | 72 |
1 files changed, 59 insertions, 13 deletions
@@ -42,6 +42,7 @@ enum { CP_OVERLAY_TITLE, CP_BUTTON_YES, CP_BUTTON_NO, + CP_ROOT_WARN, }; #define ATTR_DIR (COLOR_PAIR(CP_DIR) | A_BOLD) @@ -54,6 +55,7 @@ enum { #define ATTR_MARKER COLOR_PAIR(CP_MARKER) #define ATTR_INFO COLOR_PAIR(CP_INFO) #define ATTR_OVERLAY_BORDER COLOR_PAIR(CP_OVERLAY_BORDER) +#define ATTR_ROOT_WARN (COLOR_PAIR(CP_ROOT_WARN) | A_BOLD) // ─── Entry data ───────────────────────────────────────────────────────────── enum class EntryType { Directory, File, Symlink }; @@ -154,6 +156,7 @@ private: bool show_details_ = false; bool show_preview_ = false; bool searching_ = false; + bool is_root_ = false; std::string filter_; std::string info_msg_; @@ -321,6 +324,7 @@ void FileManager::init_colors() { init_pair(CP_OVERLAY_TITLE, COLOR_CYAN, -1); init_pair(CP_BUTTON_YES, COLOR_GREEN, -1); init_pair(CP_BUTTON_NO, COLOR_RED, -1); + init_pair(CP_ROOT_WARN, COLOR_RED, -1); } // ─── directory loading ────────────────────────────────────────────────────── @@ -554,13 +558,22 @@ void FileManager::draw_botbar() { right = " press ? for help "; } - int total = static_cast<int>(left.size() + right.size()); + std::string root_tag = is_root_ ? "[root] " : ""; + int total = static_cast<int>(root_tag.size() + left.size() + right.size()); int pad = cols_ - total; if (pad < 0) pad = 0; + int x = 0; + if (is_root_) { + attron(ATTR_ROOT_WARN); + mvprintw(rows_ - 1, 0, "%s", root_tag.c_str()); + attroff(ATTR_ROOT_WARN); + x = static_cast<int>(root_tag.size()); + } + attron(ATTR_TOPBAR); - mvprintw(rows_ - 1, 0, "%s", left.c_str()); - for (int i = 0; i < pad; ++i) mvaddch(rows_ - 1, static_cast<int>(left.size()) + i, ' '); + mvprintw(rows_ - 1, x, "%s", left.c_str()); + for (int i = 0; i < pad; ++i) mvaddch(rows_ - 1, x + static_cast<int>(left.size()) + i, ' '); attroff(ATTR_TOPBAR); attron(ATTR_INFO); @@ -711,11 +724,10 @@ void FileManager::draw_preview() { if (!e) return; // Draw vertical divider - for (int r = 1; r < rows_ - 1; ++r) { - attron(ATTR_DIVIDER); - mvaddch(r, list_cols_, '|'); - attroff(ATTR_DIVIDER); - } + attron(ATTR_DIVIDER); + for (int r = 1; r < rows_ - 1; ++r) + mvaddch(r, list_cols_, ACS_VLINE); + attroff(ATTR_DIVIDER); // Clear entire preview area of previous content (stale lines from last preview) for (int r = 1; r < rows_ - 1; ++r) { @@ -1598,6 +1610,12 @@ void FileManager::do_help() { void FileManager::do_rename() { const Entry *e = get_sel(); if (!e) return; + + if (access(cwd_.c_str(), W_OK) != 0) { + flash_msg("permission denied: " + cwd_); + return; + } + // Strip trailing / or @ std::string name = e->name; if (!name.empty() && (name.back() == '/' || name.back() == '@')) @@ -1612,11 +1630,16 @@ void FileManager::do_rename() { if (std::rename(src.c_str(), dst.c_str()) == 0) { load_entries(); } else { - flash_msg("rename failed"); + flash_msg("rename failed: " + std::string(std::strerror(errno))); } } void FileManager::do_mkdir() { + if (access(cwd_.c_str(), W_OK) != 0) { + flash_msg("permission denied: " + cwd_); + return; + } + std::string name; if (!read_line(name, " Directory name: ")) { need_full_redraw_ = true; return; } if (name.empty()) { flash_msg("cancelled"); return; } @@ -1625,11 +1648,16 @@ void FileManager::do_mkdir() { if (fs::create_directory(path)) { load_entries(); } else { - flash_msg("mkdir failed"); + flash_msg("mkdir failed: " + std::string(std::strerror(errno))); } } void FileManager::do_newfile() { + if (access(cwd_.c_str(), W_OK) != 0) { + flash_msg("permission denied: " + cwd_); + return; + } + std::string name; if (!read_line(name, " File name: ")) { need_full_redraw_ = true; return; } if (name.empty()) { flash_msg("cancelled"); return; } @@ -1640,11 +1668,16 @@ void FileManager::do_newfile() { f.close(); load_entries(); } else { - flash_msg("cannot create file"); + flash_msg("cannot create file: " + std::string(std::strerror(errno))); } } void FileManager::do_delete() { + if (access(cwd_.c_str(), W_OK) != 0) { + flash_msg("permission denied: " + cwd_); + return; + } + // Gather items to delete std::vector<std::string> targets; if (!selected_.empty()) { @@ -1709,6 +1742,12 @@ void FileManager::do_delete() { void FileManager::do_trash() { const Entry *e = get_sel(); if (!e) return; + + if (access(cwd_.c_str(), W_OK) != 0) { + flash_msg("permission denied: " + cwd_); + return; + } + std::string name = e->name; if (!name.empty() && name.back() == '/') name.pop_back(); if (!name.empty() && name.back() == '@') name.pop_back(); @@ -1743,7 +1782,7 @@ void FileManager::do_trash() { sel_ = std::max(0, static_cast<int>(entries_.size()) - 2); load_entries(); } else { - flash_msg("trash failed"); + flash_msg("trash failed: " + std::string(std::strerror(errno))); } } @@ -1805,7 +1844,7 @@ void FileManager::do_chmod_x(bool set) { flash_msg(set ? "chmod +x: " + e->name : "chmod -x: " + e->name); load_entries(); } else { - flash_msg("chmod failed"); + flash_msg("chmod failed: " + std::string(std::strerror(errno))); } } @@ -2021,6 +2060,11 @@ void FileManager::do_paste() { return; } + if (access(cwd_.c_str(), W_OK) != 0) { + flash_msg("permission denied: " + cwd_); + return; + } + for (const auto &src : clipboard_paths_) { std::string name = fs::path(src).filename().string(); std::string dst = join_path(cwd_, name); @@ -2291,6 +2335,8 @@ void FileManager::run() { if (can_color()) init_colors(); load_entries(); + is_root_ = (geteuid() == 0); + while (true) { draw(); int key = getch(); |