/
Boxapp
/
yadisk-helper
Обзор
Документация
Войти
/
Boxapp
/
yadisk-helper
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
tui.cpp
259 строк
9 KB
DebianCougar
test build 2026.02-beta
30 янв 2026, 21:25
30 янв 2026, 21:25
eb3551d
Код
Авторство
О чём код?
#include <cstdlib> #include <fstream> #include <iostream> #include <ncurses.h> // NOTE compile with flag -lncurses #include <stack> #include <string> #include <unordered_map> #include <vector> #include "excl.hpp" #include "finder.hpp" #include "path.hpp" using std::cerr; using std::cout; using std::endl; using std::ifstream; using std::pair; using std::stack; using std::string; using std::unordered_map; using std::vector; struct FileInclusionState { // NOTE inclusion is better because new files are not needed by default (i.e. can be excluded) vector<string> incl_list; string path; stack<string> changes; // NOTE on undo position is returned to last toggled file unordered_map<string, pair<size_t, char>> newset; }; struct StateRecovery { size_t changes = 0; stack<FileInclusionState> history; }; void render(WINDOW *w, int color, const string &path, const vector<pair<string, char>> &files, size_t index = 0) { // FIXME non-ascii filenames if (has_colors()) wbkgd(w, COLOR_PAIR(color)); wclear(w); box(w, 0, 0); wmove(w, 4, 4); wprintw(w, "Path %s", path.c_str()); for (size_t i = 0; i < files.size(); ++i) { wmove(w, i + 6, 4); if (i == index) wattron(w, A_STANDOUT); wprintw(w, "[%c]", files[i].second); if (i == index) wattroff(w, A_STANDOUT); wprintw(w, " %s", files[i].first.c_str()); } wrefresh(w); } char stat(const vector<string> &incl_list, const string &path) { switch (included(incl_list, path)) { case EXCLUDE: return '-'; case INCLUDE: return '+'; case PARTIAL: return '*'; } return '\0'; } void doChange(StateRecovery &undo, const string &file) { if (undo.history.top().newset.find(file) == undo.history.top().newset.end()) { char before = stat(undo.history.top().incl_list, filePath(undo.history.top().path, file)); undo.history.top().newset[file] = {1, before == '+' ? '-' : '+'}; } else { ++undo.history.top().newset[file].first; undo.history.top().newset[file].second = undo.history.top().newset[file].second == '+' ? '-' : '+'; } ++undo.changes; undo.history.top().changes.push(file); } void applyChanges(StateRecovery &undo, const string &token) { string path = undo.history.top().path; vector<string> new_incl = undo.history.top().incl_list; for (const pair<const string, pair<size_t, char>> &f : undo.history.top().newset) { if (f.second.second == '+') new_incl = plus(new_incl, filePath(path, f.first)); else if (f.second.second == '-') new_incl = minus(new_incl, filePath(path, f.first), token); } undo.history.push({new_incl, path, {}, {}}); } void goForward(StateRecovery &undo, const string &dir, const string &token) { string path = undo.history.top().path; vector<string> new_incl = undo.history.top().incl_list; for (const pair<const string, pair<size_t, char>> &f : undo.history.top().newset) { if (f.second.second == '+') new_incl = plus(new_incl, filePath(path, f.first)); else if (f.second.second == '-') new_incl = minus(new_incl, filePath(path, f.first), token); } path = filePath(path, dir); // TODO check if dir undo.history.push({new_incl, path, {}, {}}); } string goBack(StateRecovery &undo, const string &token) { string path = undo.history.top().path; string file = onlyName(path); if (!file.empty()) { vector<string> new_incl = undo.history.top().incl_list; for (const pair<const string, pair<size_t, char>> &f : undo.history.top().newset) { if (f.second.second == '+') new_incl = plus(new_incl, filePath(path, f.first)); else if (f.second.second == '-') new_incl = minus(new_incl, filePath(path, f.first), token); } path = parentDir(path); undo.history.push({new_incl, path, {}, {}}); } return file; } string undoChange(StateRecovery &undo) { if (undo.changes > 0) { while (undo.history.top().changes.empty()) undo.history.pop(); if (!undo.history.top().changes.empty()) { string file = undo.history.top().changes.top(); --undo.history.top().newset[file].first; if (undo.history.top().newset[file].first > 0) undo.history.top().newset[file].second = undo.history.top().newset[file].second == '+' ? '-' : '+'; else undo.history.top().newset.erase(file); undo.history.top().changes.pop(); --undo.changes; return file; } } return ""; } vector<pair<string, char>> getListPreloaded(const vector<string> &incl_list, const string &dir, const vector<string> &fls) { vector<pair<string, char>> res; for (const string &f : fls) res.push_back({f, stat(incl_list, filePath(dir, f))}); return res; } vector<pair<string, char>> getList(const string &token, const vector<string> &incl_list, const string &dir) { vector<string> fls = contents(dir, token); return getListPreloaded(incl_list, dir, fls); } vector<string> browse(WINDOW *w, int color, const string &dir, const string &token, const vector<string> &excl_list) { int index = 0; const vector<string> incl_list = toIncludeDirs(excl_list, token); StateRecovery undo; undo.history.push({incl_list, dir, {}, {}}); string path = dir; vector<pair<string, char>> files = getList(token, undo.history.top().incl_list, path); while (true) { // TODO scrolling (not all files are loaded and shown) render(w, color, path, files, index); int filecnt = files.size(); int ch = 0; ch = wgetch(w); if (ch == KEY_UP) index = filecnt > 0 ? (index + filecnt - 1) % filecnt : 0; else if (ch == KEY_DOWN) index = filecnt > 0 ? (index + 1) % filecnt : 0; else if (ch == ' ') { if (filecnt > 0) { doChange(undo, files[index].first); files[index].second = undo.history.top().newset[files[index].first].second; } } else if (ch == KEY_ENTER || ch == KEY_RIGHT) { // FIXME reacts only to Shift+Return, not to Return if (filecnt > 0) { goForward(undo, files[index].first, token); path = undo.history.top().path; index = 0; files = getList(token, undo.history.top().incl_list, path); } } else if (ch == KEY_LEFT) { if (!goBack(undo, token).empty()) { path = undo.history.top().path; index = 0; // FIXME set to last dir files = getList(token, undo.history.top().incl_list, path); } } else if (ch == 'd') return excl_list; else if (ch == 's') { applyChanges(undo, token); return toExcludeDirs(undo.history.top().incl_list, token); } else if (ch == 'u') { if (!undoChange(undo).empty()) { path = undo.history.top().path; index = 0; // FIXME set to last dir files = getList(token, undo.history.top().incl_list, path); for (pair<string, char> &file : files) { if (undo.history.top().newset.find(file.first) != undo.history.top().newset.end()) file.second = undo.history.top().newset[file.first].second; } } } } return excl_list; } int main(int argc, char *argv[]) { string input = string(getenv("HOME")) + "/.config/yandex-disk/config.cfg"; string output; string oauth = "oauth.txt"; // FIXME think of another location (e.g. in a $HOME subfolder) bool ri = false, ro = false, save = false; for (int i = 1; i < argc; ++i) { string arg = argv[i]; if (ri) { input = arg; ri = false; } else if (ro) { output = arg; ro = false; } else if (arg == "-h" || arg == "--help") { cout << "Usage: " << argv[0] << " [-h | --help] [(-i | --input) FILE] [(-o | --output) FILE] [-s | --save]" << endl; cout << " -h | --help Print help" << endl; cout << "(-i | --input) FILE Read config from FILE (default is $HOME/.config/yandex-disk/config.cfg)" << endl; cout << "(-o | --output) FILE Write config to FILE" << endl; cout << " -s | --save Write config to same file as input" << endl; return 0; } else if (arg == "-i" || arg == "--input") ri = true; else if (arg == "-o" || arg == "--output") ro = true; else if (arg == "-s" || arg == "--save") save = true; } string token; ifstream(oauth) >> token; string path = "/"; if (!ifstream(input).good()) { cerr << "File " << input << " not found" << endl; return 1; } vector<string> excl_list = readExcludeDirs(input); // инициализация (должна быть выполнена перед использованием ncurses) initscr(); noecho(); // disable echoing of characters on the screen curs_set( 0 ); // hide the default screen cursor. cbreak(); // Измеряем размер экрана в рядах и колонках int row, col; getmaxyx(stdscr, row, col); WINDOW *w = newwin(row - 4, col - 4, 2, 2); keypad( w, TRUE ); // enable keyboard input for the window. if (has_colors()) { start_color(); init_pair(1, COLOR_WHITE, COLOR_RED); init_pair(2, COLOR_BLACK, COLOR_GREEN); init_pair(3, COLOR_BLACK, COLOR_YELLOW); init_pair(4, COLOR_WHITE, COLOR_BLUE); init_pair(5, COLOR_BLACK, COLOR_CYAN); init_pair(6, COLOR_WHITE, COLOR_MAGENTA); init_pair(7, COLOR_BLACK, COLOR_WHITE); } srand(time(0)); vector<string> res = browse(w, rand() % 8, path, token, excl_list); delwin(w); endwin(); // завершение работы с ncurses if (output.empty()) { if (save) writeExcludeDirs(res, input); else { cerr << "[ INFO ] By default, new config is printed to stdout unless explicit output is specified" << endl; cerr << "[ INFO ] To overwrite input file, run with '-s' or '--save' option" << endl; writeExcludeDirs(res, "/dev/stdout"); } } else writeExcludeDirs(res, output); return 0; }