Merge pull request #2212 from unxed/ctrl_yo
[far2l.git] / far2l / src / Mounts.cpp
blobf37518891b6074b3a8cd0f746586a75fbc27bc7b
1 #include "headers.hpp"
3 #include <crc64.h>
4 #include <fstream>
5 #include "Mounts.hpp"
6 #include "lang.hpp"
7 #include "keys.hpp"
8 #include "help.hpp"
9 #include "vmenu.hpp"
10 #include "message.hpp"
11 #include "config.hpp"
12 #include "pathmix.hpp"
13 #include "strmix.hpp"
14 #include "dirmix.hpp"
15 #include "execute.hpp"
16 #include "manager.hpp"
17 #include "ConfigRW.hpp"
18 #include "HotkeyLetterDialog.hpp"
19 #include "MountInfo.h"
21 namespace Mounts
23 #define HOTKEYS_SECTION "MountsHotkeys"
24 #define ID_ROOT 0
25 #define ID_HOME 1
26 #define ID_ANOTHER 2
28 static wchar_t DefaultHotKey(int id, const FARString &path)
30 switch (id) {
31 case ID_ROOT:
32 return L'/';
34 case ID_HOME:
35 return L'~';
37 case ID_ANOTHER:
38 return L'-';
41 return 0;
44 static std::string SettingsKey(int id)
46 return StrPrintf("%08x", id);
49 static int GenerateIdFromPath(const FARString &path)
51 if (path == WGOOD_SLASH)
52 return ID_ROOT;
54 const std::string &path_mb = path.GetMB();
55 const uint64_t id64 = crc64(0, (unsigned char *)path_mb.c_str(), path_mb.size());
56 uint32_t out = (uint32_t)(id64 ^ (id64 >> 32));
57 if (out == ID_ROOT || out == ID_HOME || out == ID_ANOTHER) {
58 out^= 0xffffffff;
60 return (int)out;
63 /////
65 Enum::Enum(FARString &another_curdir)
67 bool has_rootfs = false;
68 if (Opt.ChangeDriveMode & DRIVE_SHOW_MOUNTS)
70 AddMounts(has_rootfs);
72 AddFavorites(has_rootfs);
74 if (!has_rootfs) {
75 emplace(begin(), Entry(WGOOD_SLASH, Msg::MountsRoot, false, ID_ROOT));
78 emplace(begin(), Entry( GetMyHome(), Msg::MountsHome, false, ID_HOME));
79 emplace(begin(), Entry( another_curdir, Msg::MountsOther, false, ID_ANOTHER));
81 ConfigReader cfg_reader(HOTKEYS_SECTION);
82 for (auto &m : *this) {
83 if (max_path < m.path.CellsCount())
84 max_path = m.path.CellsCount();
85 if (max_col3 < m.col3.CellsCount())
86 max_col3 = m.col3.CellsCount();
87 if (max_col2 < m.col2.CellsCount())
88 max_col2 = m.col2.CellsCount();
89 wchar_t def_hk[] = {DefaultHotKey(m.id, m.path), 0};
90 auto hk = cfg_reader.GetString(SettingsKey(m.id), def_hk);
91 m.hotkey = hk.IsEmpty() ? 0 : *hk.CPtr();
95 static void ExpandMountpointInfo(const Mountpoint &mp, FARString &str)
97 FARString val = L" ";
98 if (mp.bad) {
99 val = L"?";
100 } else if (mp.read_only) {
101 val = L"!";
103 ReplaceStrings(str, L"$S", val);
105 FileSizeToStr(val, mp.total, -1, COLUMN_ECONOMIC | COLUMN_FLOATSIZE | COLUMN_SHOWBYTESINDEX);
106 ReplaceStrings(str, L"$T", val);
108 FileSizeToStr(val, mp.avail, -1, COLUMN_ECONOMIC | COLUMN_FLOATSIZE | COLUMN_SHOWBYTESINDEX);
109 ReplaceStrings(str, L"$A", val);
111 FileSizeToStr(val, mp.free_, -1, COLUMN_ECONOMIC | COLUMN_FLOATSIZE | COLUMN_SHOWBYTESINDEX);
112 ReplaceStrings(str, L"$F", val);
114 FileSizeToStr(val, mp.total - mp.free_, -1, COLUMN_ECONOMIC | COLUMN_FLOATSIZE | COLUMN_SHOWBYTESINDEX);
115 ReplaceStrings(str, L"$U", val);
117 if (mp.total)
118 val.Format(L"%lld", (mp.avail * 100) / mp.total);
119 else
120 val = L"NA";
121 ReplaceStrings(str, L"$a", val);
123 if (mp.total)
124 val.Format(L"%lld", (mp.free_ * 100) / mp.total);
125 else
126 val = L"NA";
127 ReplaceStrings(str, L"$f", val);
129 if (mp.total)
130 val.Format(L"%lld", ((mp.total - mp.free_) * 100) / mp.total);
131 else
132 val = L"NA";
133 ReplaceStrings(str, L"$u", val);
135 val = mp.filesystem;
136 ReplaceStrings(str, L"$N", val);
138 val = mp.device;
139 ReplaceStrings(str, L"$D", val);
142 class Aligner
144 std::vector<size_t> _maximums;
146 static bool IsWordDiv(wchar_t c)
148 return !iswalnum(c) && c != L'.' && c != L',';
151 static size_t LeftWordLength(const FARString &str, size_t pos)
153 ssize_t out = pos;
154 while (out >= 0 && !IsWordDiv(str.At(out))) {
155 --out;
157 return ssize_t(pos) - out;
160 static size_t RightWordLength(const FARString &str, size_t pos)
162 size_t out = pos;
163 while (out < str.GetLength() && !IsWordDiv(str.At(out))) {
164 ++out;
166 return out - pos;
169 public:
170 void Analyze(const FARString &str)
172 size_t m = 0;
173 const int str_len = str.GetLength();
174 for (int i = str_len - 2; i >= 0; --i) {
175 if (str.At(i) == '$' && ((str.At(i + 1) == '<' && i >= 0)
176 || (str.At(i + 1) == '>' && i + 2 <= str_len))
178 const size_t len = (str.At(i + 1) == '>')
179 ? RightWordLength(str, i + 2) : LeftWordLength(str, i - 1);
180 if (_maximums.size() <= m) {
181 _maximums.emplace_back(len);
182 } else if (_maximums[m] < len) {
183 _maximums[m] = len;
185 ++m;
190 void Apply(FARString &str)
192 size_t m = 0;
193 const int str_len = str.GetLength();
194 for (int i = str_len - 2; i >= 0; --i) {
195 if (str.At(i) == '$' && ((str.At(i + 1) == '<' && i >= 0)
196 || (str.At(i + 1) == '>' && i + 2 <= str_len))
198 const size_t len = (str.At(i + 1) == '>')
199 ? RightWordLength(str, i + 2) : LeftWordLength(str, i - 1);
200 str.Replace(i, 2, L' ', _maximums[m] - len);
201 ++m;
207 void Enum::AddMounts(bool &has_rootfs)
209 MountInfo mi(true);
210 for (const auto &mp : mi.Enum()) {
211 emplace_back();
212 auto &e = back();
213 e.path = mp.path;
214 e.col2 = Opt.ChangeDriveColumn2;
215 e.col3 = Opt.ChangeDriveColumn3;
216 ExpandMountpointInfo(mp, e.col2);
217 ExpandMountpointInfo(mp, e.col3);
219 if (e.path == WGOOD_SLASH) {
220 has_rootfs = true;
221 } else {
222 e.unmountable = true;
224 e.id = GenerateIdFromPath(e.path);
227 // apply replace $> and $< spacers
228 Aligner al_path, al_col2, al_col3;
229 for (const auto &e : *this) {
230 al_path.Analyze(e.path);
231 al_col2.Analyze(e.col2);
232 al_col3.Analyze(e.col3);
234 for (auto &e : *this) {
235 al_path.Apply(e.path);
236 al_col2.Apply(e.col2);
237 al_col3.Apply(e.col3);
241 void Enum::AddFavorites(bool &has_rootfs)
243 std::ifstream favis(InMyConfig("favorites"));
244 if (favis.is_open()) {
245 std::string line;
246 while (std::getline(favis, line)) {
247 StrTrim(line, " \t\r\n");
248 if (line.empty() || line.front() == '#') {
249 continue;
251 Environment::ExpandString(line, true, true);
252 std::vector<std::string> sublines;
253 StrExplode(sublines, line, "\n");
254 for (const auto &subline : sublines) {
255 std::vector<std::string> parts;
256 StrExplode(parts, subline, "\t");
257 if (!parts.empty()) {
258 emplace_back();
259 auto &e = back();
260 e.path = parts.front();
261 if (parts.size() > 1) {
262 e.col3 = parts.back();
263 if (parts.size() > 2) {
264 e.col2 = parts[1];
267 e.id = GenerateIdFromPath(e.path);
268 if (e.path == WGOOD_SLASH) {
269 has_rootfs = true;
271 } else if (*e.path.CPtr() == L'/') {
272 e.unmountable = true;
279 //////
281 bool Unmount(const FARString &path, bool force)
283 std::string cmd = GetMyScriptQuoted("unmount.sh");
284 cmd+= " \"";
285 cmd+= EscapeCmdStr(Wide2MB(path));
286 cmd+= "\"";
287 if (force) {
288 cmd+= " force";
290 int r = farExecuteA(cmd.c_str(), 0);
291 if (FrameManager) {
292 auto *current_frame = FrameManager->GetCurrentFrame();
293 if (current_frame) {
294 FrameManager->RefreshFrame(current_frame);
297 return r == 0;
300 void EditHotkey(const FARString &path, int id)
302 wchar_t def_hk[] = {DefaultHotKey(id, path), 0};
303 const auto &Key = SettingsKey(id);
304 const auto &Setting = ConfigReader(HOTKEYS_SECTION).GetString(Key, def_hk);
305 WCHAR Letter[2] = {Setting.IsEmpty() ? 0 : Setting[0], 0};
306 if (HotkeyLetterDialog(Msg::LocationHotKeyTitle, path.CPtr(), Letter[0])) {
307 ConfigWriter cw(HOTKEYS_SECTION);
308 if (Letter[0])
309 cw.SetString(Key, Letter);
310 else
311 cw.RemoveKey(Key);