Added config file loader

This commit is contained in:
aristocratos 2021-06-12 18:49:27 +02:00
parent 63a286d6a1
commit c4b55c7dfd
6 changed files with 177 additions and 62 deletions

View file

@ -256,8 +256,19 @@ int main(int argc, char **argv){
} }
} }
Global::debug = true; //? Read config file if present
if (Global::debug) { Logger::loglevel = 4; Logger::debug("Starting in debug mode");} { vector<string> load_errors;
Config::load(Config::conf_file, load_errors);
if (Global::debug) Logger::loglevel = 4;
else Logger::loglevel = v_index(Logger::log_levels, Config::getS("log_level"));
if (Logger::loglevel == 4) Logger::debug("Starting with logger set to debug.");
if (!load_errors.empty()) {
for (auto& err_str : load_errors) Logger::error(err_str);
}
}
if (!string(getenv("LANG")).ends_with("UTF-8") && !string(getenv("LANG")).ends_with("utf-8")) { if (!string(getenv("LANG")).ends_with("UTF-8") && !string(getenv("LANG")).ends_with("utf-8")) {
string err_msg = "No UTF-8 locale was detected! Symbols might not look as intended.\n" string err_msg = "No UTF-8 locale was detected! Symbols might not look as intended.\n"
@ -279,8 +290,7 @@ int main(int argc, char **argv){
Proc::init(); Proc::init();
#endif #endif
//? Read config file if present
Config::load();
// Config::set("truecolor", false); // Config::set("truecolor", false);
auto thts = time_ms(); auto thts = time_ms();
@ -433,6 +443,23 @@ int main(int argc, char **argv){
} }
if (false) {
cout << Config::getS("log_level") << endl;
vector<string> vv = {"hej", "vad", "du"};
vector<int> vy;
cout << v_contains(vv, "vad"s) << endl;
cout << v_index(vv, "hej"s) << endl;
cout << v_index(vv, "du"s) << endl;
cout << v_index(vv, "kodkod"s) << endl;
cout << v_index(vy, 4) << endl;
exit(0);
}
// if (thread_test){ // if (thread_test){
// unordered_flat_map<int, future<string>> runners; // unordered_flat_map<int, future<string>> runners;

View file

@ -41,7 +41,7 @@ namespace Config {
atomic<bool> locked (false); atomic<bool> locked (false);
atomic<bool> writelock (false); atomic<bool> writelock (false);
bool changed; bool write_new;
vector<array<string, 2>> descriptions = { vector<array<string, 2>> descriptions = {
{"color_theme", "#* Color theme, looks for a .theme file in \"/usr/[local/]share/bpytop/themes\" and \"~/.config/bpytop/themes\", \"Default\" for builtin default theme.\n" {"color_theme", "#* Color theme, looks for a .theme file in \"/usr/[local/]share/bpytop/themes\" and \"~/.config/bpytop/themes\", \"Default\" for builtin default theme.\n"
@ -160,7 +160,7 @@ namespace Config {
bool _locked(){ bool _locked(){
atomic_wait(writelock); atomic_wait(writelock);
if (!changed) changed = true; if (!write_new) write_new = true;
return locked.load(); return locked.load();
} }
} }
@ -238,14 +238,71 @@ namespace Config {
} }
//* Load the config file from disk //* Load the config file from disk
void load(){ void load(fs::path conf_file, vector<string>& load_errors){
if (conf_file.empty()) return; if (conf_file.empty())
else if (!fs::exists(conf_file)) { changed = true; return; } return;
else if (!fs::exists(conf_file)) {
write_new = true;
return;
}
std::ifstream cread(conf_file);
if (cread.good()) {
unordered_flat_map<string, int> valid_names;
for (auto &n : descriptions)
valid_names[n[0]] = 0;
string v_string;
getline(cread, v_string, '\n');
if (!v_string.ends_with(Global::Version))
write_new = true;
while (!cread.eof()) {
cread >> std::ws;
if (cread.peek() == '#') {
cread.ignore(SSmax, '\n');
continue;
}
string name, value;
getline(cread, name, '=');
if (!valid_names.contains(name)) {
cread.ignore(SSmax, '\n');
continue;
}
if (bools.contains(name)) {
cread >> value;
if (!isbool(value))
load_errors.push_back("Got an invalid bool value for config name: " + name);
else
bools.at(name) = stobool(value);
}
else if (ints.contains(name)) {
cread >> value;
if (!isint(value))
load_errors.push_back("Got an invalid integer value for config name: " + name);
else
ints.at(name) = stoi(value);
}
else if (strings.contains(name)) {
cread >> std::ws;
if (cread.peek() == '"') {
cread.ignore(1);
getline(cread, value, '"');
}
else cread >> value;
if (name == "log_level" && !v_contains(Logger::log_levels, value)) load_errors.push_back("Invalid log_level: " + value);
else strings.at(name) = value;
}
cread.ignore(SSmax, '\n');
}
cread.close();
if (!load_errors.empty()) write_new = true;
}
} }
//* Write the config file to disk //* Write the config file to disk
void write(){ void write(){
if (conf_file.empty() || !changed) return; if (conf_file.empty() || !write_new) return;
Logger::debug("Writing new config file"); Logger::debug("Writing new config file");
std::ofstream cwrite(conf_file, std::ios::trunc); std::ofstream cwrite(conf_file, std::ios::trunc);
if (cwrite.good()) { if (cwrite.good()) {

View file

@ -193,14 +193,13 @@ namespace Draw {
int ai = 0; int ai = 0;
for (auto value : {last, data_value}) { for (auto value : {last, data_value}) {
if (value >= cur_high) if (value >= cur_high)
result[ai] = 4; result[ai++] = 4;
else if (value <= cur_low) else if (value <= cur_low)
result[ai] = 0; result[ai++] = 0;
else { else {
result[ai] = round((float)(value - cur_low) * 4 / (cur_high - cur_low) + mod); result[ai++] = round((float)(value - cur_low) * 4 / (cur_high - cur_low) + mod);
if (no_zero && horizon == height - 1 && i != -1 && result[ai] == 0) result[ai] = 1; if (no_zero && horizon == height - 1 && i != -1 && result[ai] == 0) result[ai] = 1;
} }
ai++;
} }
//? Generate braille symbol from 5x5 2D vector //? Generate braille symbol from 5x5 2D vector
graphs[current][horizon] += (height == 1 && result[0] + result[1] == 0) ? Mv::r(1) : graph_symbol[(result[0] * 5 + result[1])]; graphs[current][horizon] += (height == 1 && result[0] + result[1] == 0) ? Mv::r(1) : graph_symbol[(result[0] * 5 + result[1])];
@ -228,8 +227,10 @@ namespace Draw {
graphs[true].clear(); graphs[false].clear(); graphs[true].clear(); graphs[false].clear();
this->width = width; this->height = height; this->width = width; this->height = height;
this->invert = invert; this->offset = offset; this->invert = invert; this->offset = offset;
this->no_zero = no_zero; this->max_value = max_value; this->no_zero = no_zero;
this->color_gradient = color_gradient; this->color_gradient = color_gradient;
if (max_value == 0 && offset > 0) max_value = 100;
this->max_value = max_value;
int value_width = ceil((float)data.size() / 2); int value_width = ceil((float)data.size() / 2);
int data_offset = 0; int data_offset = 0;
if (value_width > width) data_offset = data.size() - width * 2; if (value_width > width) data_offset = data.size() - width * 2;
@ -268,6 +269,7 @@ namespace Box {
} }
namespace Proc { namespace Proc {

View file

@ -90,10 +90,10 @@ namespace Input {
} }
//* Get a key or mouse action from input //* Get a key or mouse action from input
string get(){ string get(bool clear = false){
string key; string key;
while (cin.rdbuf()->in_avail() > 0 && key.size() < 100) key += cin.get(); while (cin.rdbuf()->in_avail() > 0 && key.size() < 100) key += cin.get();
if (!key.empty()){ if (!clear && !key.empty()){
if (key.substr(0,2) == Fx::e) key.erase(0, 1); if (key.substr(0,2) == Fx::e) key.erase(0, 1);
if (Key_escapes.contains(key)) key = Key_escapes.at(key); if (Key_escapes.contains(key)) key = Key_escapes.at(key);
else if (ulen(key) > 1) key = ""; else if (ulen(key) > 1) key = "";
@ -105,9 +105,10 @@ namespace Input {
//* Wait until input is available //* Wait until input is available
void wait(bool clear=false){ void wait(bool clear=false){
while (cin.rdbuf()->in_avail() < 1) sleep_ms(10); while (cin.rdbuf()->in_avail() < 1) sleep_ms(10);
if (clear) get(); if (clear) get(clear);
} }
//* Clears last entered key
void clear(){ void clear(){
last.clear(); last.clear();
} }

View file

@ -44,7 +44,6 @@ using std::string, std::vector, std::array, std::ifstream, std::atomic, std::num
using std::cout, std::flush, std::endl; using std::cout, std::flush, std::endl;
namespace fs = std::filesystem; namespace fs = std::filesystem;
using namespace Tools; using namespace Tools;
const auto SSmax = std::numeric_limits<streamsize>::max();
//? --------------------------------------------------- FUNCTIONS ----------------------------------------------------- //? --------------------------------------------------- FUNCTIONS -----------------------------------------------------
@ -63,6 +62,9 @@ namespace Proc {
struct p_cache { struct p_cache {
string name, cmd, user; string name, cmd, user;
uint64_t cpu_t = 0, cpu_s = 0; uint64_t cpu_t = 0, cpu_s = 0;
string prefix = "";
size_t depth = 0;
bool collapsed = false;
}; };
unordered_flat_map<uint, p_cache> cache; unordered_flat_map<uint, p_cache> cache;
unordered_flat_map<string, string> uid_user; unordered_flat_map<string, string> uid_user;
@ -92,7 +94,7 @@ namespace Proc {
}; };
unordered_flat_map<string, int> sort_map; unordered_flat_map<string, int> sort_map;
//* proc_info: pid, name, cmd, threads, user, mem, cpu_p, cpu_c, state, cpu_n, p_nice, ppid //* Container for process information
struct proc_info { struct proc_info {
uint pid; uint pid;
string name = "", cmd = ""; string name = "", cmd = "";
@ -102,21 +104,25 @@ namespace Proc {
double cpu_p = 0.0, cpu_c = 0.0; double cpu_p = 0.0, cpu_c = 0.0;
char state = '0'; char state = '0';
int cpu_n = 0, p_nice = 0; int cpu_n = 0, p_nice = 0;
uint ppid = 0; int ppid = -1;
string prefix = "";
}; };
vector<proc_info> current_procs; vector<proc_info> current_procs;
//* Collects process information from /proc, saves to and returns reference to Proc::current_procs; //* Collects and sorts process information from /proc, saves to and returns reference to Proc::current_procs;
auto& collect(string sorting="pid", bool reverse=false, string filter="", bool per_core=true){ auto& collect(string sorting="pid", bool reverse=false, string filter="", bool per_core=true, bool tree=false){
atomic_wait_set(collecting); atomic_wait_set(collecting);
ifstream pread; ifstream pread;
auto uptime = system_uptime(); auto uptime = system_uptime();
vector<proc_info> procs; vector<proc_info> procs;
vector<uint> pid_list;
procs.reserve((numpids + 10)); procs.reserve((numpids + 10));
pid_list.reserve(numpids + 10);
int npids = 0; int npids = 0;
int cmult = (per_core) ? Global::coreCount : 1; int cmult = (per_core) ? Global::coreCount : 1;
(void)tree;
//* Update uid_user map if /etc/passwd changed since last run //* Update uid_user map if /etc/passwd changed since last run
if (!passwd_path.empty() && fs::last_write_time(passwd_path) != passwd_time) { if (!passwd_path.empty() && fs::last_write_time(passwd_path) != passwd_time) {
@ -160,6 +166,7 @@ namespace Proc {
if (d.is_directory() && isdigit(pid_str[0])) { if (d.is_directory() && isdigit(pid_str[0])) {
npids++; npids++;
proc_info new_proc (stoul(pid_str)); proc_info new_proc (stoul(pid_str));
pid_list.push_back(new_proc.pid);
//* Cache program name, command and username //* Cache program name, command and username
if (!cache.contains(new_proc.pid)) { if (!cache.contains(new_proc.pid)) {
@ -238,7 +245,7 @@ namespace Proc {
break; break;
} }
case 1: { //? Process parent pid case 1: { //? Process parent pid
new_proc.ppid = stoul(instr.substr(s_pos, c_pos - s_pos)); new_proc.ppid = stoi(instr.substr(s_pos, c_pos - s_pos));
break; break;
} }
case 11: { //? Process utime case 11: { //? Process utime
@ -297,7 +304,7 @@ namespace Proc {
} }
//* Sort processes vector //* Sort processes
std::ranges::sort(procs, [sortint = sort_map.at(sorting), &reverse](proc_info& a, proc_info& b) { std::ranges::sort(procs, [sortint = sort_map.at(sorting), &reverse](proc_info& a, proc_info& b) {
switch (sortint) { switch (sortint) {
case 0: return (reverse) ? a.pid < b.pid : a.pid > b.pid; case 0: return (reverse) ? a.pid < b.pid : a.pid > b.pid;
@ -326,14 +333,13 @@ namespace Proc {
//* Clear dead processes from cache at a regular interval //* Clear dead processes from cache at a regular interval
if (++counter >= 10000 || ((int)cache.size() > npids + 100)) { if (++counter >= 10000 || ((int)cache.size() > npids + 100)) {
unordered_flat_map<uint, p_cache> r_cache;
r_cache.reserve(procs.size());
counter = 0; counter = 0;
if (filter.empty()) { unordered_flat_map<uint, p_cache> r_cache;
for (auto& p : procs) r_cache[p.pid] = cache[p.pid]; r_cache.reserve(pid_list.size());
cache.swap(r_cache); for (auto& p : pid_list) {
if (cache.contains(p)) r_cache[p] = cache.at(p);
} }
else cache.clear(); cache.swap(r_cache);
} }
old_cputimes = cputimes; old_cputimes = cputimes;
atomic_wait(drawing); atomic_wait(drawing);

View file

@ -38,7 +38,7 @@ tab-size = 4
#include <termios.h> #include <termios.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
using std::string, std::vector, std::array, std::regex, std::max, std::to_string, std::cin, std::atomic, robin_hood::unordered_flat_map; using std::string, std::string_view, std::vector, std::array, std::regex, std::max, std::to_string, std::cin, std::atomic, robin_hood::unordered_flat_map;
namespace fs = std::filesystem; namespace fs = std::filesystem;
//? ------------------------------------------------- NAMESPACES ------------------------------------------------------ //? ------------------------------------------------- NAMESPACES ------------------------------------------------------
@ -225,8 +225,10 @@ namespace Term {
namespace Tools { namespace Tools {
const auto SSmax = std::numeric_limits<std::streamsize>::max();
//* Return number of UTF8 characters in a string with option to disregard escape sequences //* Return number of UTF8 characters in a string with option to disregard escape sequences
size_t ulen(string str, bool escape=false){ size_t ulen(string str, const bool escape=false){
if (escape) str = std::regex_replace(str, Fx::escape_regex, ""); if (escape) str = std::regex_replace(str, Fx::escape_regex, "");
return std::count_if(str.begin(), str.end(), return std::count_if(str.begin(), str.end(),
[](char c) { return (static_cast<unsigned char>(c) & 0xC0) != 0x80; } ); [](char c) { return (static_cast<unsigned char>(c) & 0xC0) != 0x80; } );
@ -247,6 +249,18 @@ namespace Tools {
return str; return str;
} }
//* Check if vector <vec> contains value <find_val>
template <typename T>
bool v_contains(vector<T>& vec, T find_val) {
return std::ranges::find(vec, find_val) != vec.end();
}
//* Return index of <find_val> from vector <vec>, returns size of <vec> if <find_val> is not present
template <typename T>
size_t v_index(vector<T>& vec, T find_val) {
return std::ranges::distance(vec.begin(), std::ranges::find(vec, find_val));
}
//* Return current time since epoch in seconds //* Return current time since epoch in seconds
uint64_t time_s(){ uint64_t time_s(){
return std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count(); return std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
@ -267,49 +281,57 @@ namespace Tools {
return (str == "true") || (str == "false") || (str == "True") || (str == "False"); return (str == "true") || (str == "false") || (str == "True") || (str == "False");
} }
//* Check if a string is a valid positive integer value //* Convert string to bool, returning any value not equal to "true" or "True" as false
bool isuint(string& str){ bool stobool(string& str){
return all_of(str.begin(), str.end(), ::isdigit); return (str == "true" || str == "True") ? true : false;
} }
//* Left-trim <t_str> from <str> and return string //* Check if a string is a valid integer value
string ltrim(string str, string t_str = " "){ bool isint(string& str){
while (str.starts_with(t_str)) str.erase(0, t_str.size()); if (str.empty()) return false;
return str; size_t offset = (str[0] == '-' ? 1 : 0);
return all_of(str.begin() + offset, str.end(), ::isdigit);
} }
//* Right-trim <t_str> from <str> and return string //* Left-trim <t_str> from <str> and return new string
string rtrim(string str, string t_str = " "){ string ltrim(const string& str, const string t_str = " "){
while (str.ends_with(t_str)) str.resize(str.size() - t_str.size()); string_view str_v = str;
return str; while (str_v.starts_with(t_str)) str_v.remove_prefix(t_str.size());
return (string)str_v;
} }
//* Left-right-trim <t_str> from <str> and return string //* Right-trim <t_str> from <str> and return new string
string trim(string str, string t_str = " "){ string rtrim(const string& str, const string t_str = " "){
string_view str_v = str;
while (str_v.ends_with(t_str)) str_v.remove_suffix(t_str.size());
return (string)str_v;
}
//* Left-right-trim <t_str> from <str> and return new string
string trim(const string& str, const string t_str = " "){
return ltrim(rtrim(str, t_str), t_str); return ltrim(rtrim(str, t_str), t_str);
} }
//* Split <string> at <delim> <time> number of times (0 for unlimited) and return vector //* Split <string> at <delim> <time> number of times (0 for unlimited) and return vector
vector<string> ssplit(string str, string delim = " ", int times = 0, bool ignore_remainder=false){ vector<string> ssplit(const string& str, const string delim = " ", const int times = 0, const bool ignore_remainder=false){
vector<string> out; vector<string> out;
string_view str_v = str;
if (times > 0) out.reserve(times); if (times > 0) out.reserve(times);
if (!str.empty() && !delim.empty()){ if (!str_v.empty() && !delim.empty()){
size_t pos = 0; size_t pos = 0;
int x = 0; int x = 0;
string tmp; while ((pos = str_v.find(delim)) != string::npos){
while ((pos = str.find(delim)) != string::npos){ if (str_v.substr(0, pos) != delim) out.emplace_back(str_v.substr(0, pos));
tmp = str.substr(0, pos); str_v.remove_prefix(pos + delim.size());
if (tmp != delim && !tmp.empty()) out.push_back(tmp);
str.erase(0, pos + delim.size());
if (times > 0 && ++x >= times) break; if (times > 0 && ++x >= times) break;
} }
} }
if (!ignore_remainder) out.push_back(str); if (!ignore_remainder) out.emplace_back(str_v);
return out; return out;
} }
//* Put current thread to sleep for <ms> milliseconds //* Put current thread to sleep for <ms> milliseconds
void sleep_ms(uint ms) { void sleep_ms(const uint& ms) {
std::this_thread::sleep_for(std::chrono::milliseconds(ms)); std::this_thread::sleep_for(std::chrono::milliseconds(ms));
} }
@ -455,12 +477,12 @@ namespace Logger {
std::atomic<bool> busy (false); std::atomic<bool> busy (false);
bool first = true; bool first = true;
string tdf = "%Y/%m/%d (%T) | "; string tdf = "%Y/%m/%d (%T) | ";
unordered_flat_map<uint, string> log_levels = { vector<string> log_levels = {
{ 0, "DISABLED" }, "DISABLED",
{ 1, "ERROR" }, "ERROR",
{ 2, "WARNING" }, "WARNING",
{ 3, "INFO" }, "INFO",
{ 4, "DEBUG" } "DEBUG"
}; };
} }
@ -480,7 +502,7 @@ namespace Logger {
if (!ec) { if (!ec) {
std::ofstream lwrite(logfile, std::ios::app); std::ofstream lwrite(logfile, std::ios::app);
if (first) { first = false; lwrite << "\n" << strf_time(tdf) << "===> btop++ v." << Global::Version << "\n";} if (first) { first = false; lwrite << "\n" << strf_time(tdf) << "===> btop++ v." << Global::Version << "\n";}
lwrite << strf_time(tdf) << log_levels[level] << ": " << msg << "\n"; lwrite << strf_time(tdf) << log_levels.at(level) << ": " << msg << "\n";
lwrite.close(); lwrite.close();
} }
else logfile.clear(); else logfile.clear();