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;
if (Global::debug) { Logger::loglevel = 4; Logger::debug("Starting in debug mode");}
//? Read config file if present
{ 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")) {
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();
#endif
//? Read config file if present
Config::load();
// Config::set("truecolor", false);
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){
// unordered_flat_map<int, future<string>> runners;

View file

@ -41,7 +41,7 @@ namespace Config {
atomic<bool> locked (false);
atomic<bool> writelock (false);
bool changed;
bool write_new;
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"
@ -160,7 +160,7 @@ namespace Config {
bool _locked(){
atomic_wait(writelock);
if (!changed) changed = true;
if (!write_new) write_new = true;
return locked.load();
}
}
@ -238,14 +238,71 @@ namespace Config {
}
//* Load the config file from disk
void load(){
if (conf_file.empty()) return;
else if (!fs::exists(conf_file)) { changed = true; return; }
void load(fs::path conf_file, vector<string>& load_errors){
if (conf_file.empty())
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
void write(){
if (conf_file.empty() || !changed) return;
if (conf_file.empty() || !write_new) return;
Logger::debug("Writing new config file");
std::ofstream cwrite(conf_file, std::ios::trunc);
if (cwrite.good()) {

View file

@ -193,14 +193,13 @@ namespace Draw {
int ai = 0;
for (auto value : {last, data_value}) {
if (value >= cur_high)
result[ai] = 4;
result[ai++] = 4;
else if (value <= cur_low)
result[ai] = 0;
result[ai++] = 0;
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;
}
ai++;
}
//? 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])];
@ -228,8 +227,10 @@ namespace Draw {
graphs[true].clear(); graphs[false].clear();
this->width = width; this->height = height;
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;
if (max_value == 0 && offset > 0) max_value = 100;
this->max_value = max_value;
int value_width = ceil((float)data.size() / 2);
int data_offset = 0;
if (value_width > width) data_offset = data.size() - width * 2;
@ -268,6 +269,7 @@ namespace Box {
}
namespace Proc {

View file

@ -90,10 +90,10 @@ namespace Input {
}
//* Get a key or mouse action from input
string get(){
string get(bool clear = false){
string key;
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_escapes.contains(key)) key = Key_escapes.at(key);
else if (ulen(key) > 1) key = "";
@ -105,9 +105,10 @@ namespace Input {
//* Wait until input is available
void wait(bool clear=false){
while (cin.rdbuf()->in_avail() < 1) sleep_ms(10);
if (clear) get();
if (clear) get(clear);
}
//* Clears last entered key
void 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;
namespace fs = std::filesystem;
using namespace Tools;
const auto SSmax = std::numeric_limits<streamsize>::max();
//? --------------------------------------------------- FUNCTIONS -----------------------------------------------------
@ -63,6 +62,9 @@ namespace Proc {
struct p_cache {
string name, cmd, user;
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<string, string> uid_user;
@ -92,7 +94,7 @@ namespace Proc {
};
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 {
uint pid;
string name = "", cmd = "";
@ -102,21 +104,25 @@ namespace Proc {
double cpu_p = 0.0, cpu_c = 0.0;
char state = '0';
int cpu_n = 0, p_nice = 0;
uint ppid = 0;
int ppid = -1;
string prefix = "";
};
vector<proc_info> current_procs;
//* Collects 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){
//* 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, bool tree=false){
atomic_wait_set(collecting);
ifstream pread;
auto uptime = system_uptime();
vector<proc_info> procs;
vector<uint> pid_list;
procs.reserve((numpids + 10));
pid_list.reserve(numpids + 10);
int npids = 0;
int cmult = (per_core) ? Global::coreCount : 1;
(void)tree;
//* Update uid_user map if /etc/passwd changed since last run
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])) {
npids++;
proc_info new_proc (stoul(pid_str));
pid_list.push_back(new_proc.pid);
//* Cache program name, command and username
if (!cache.contains(new_proc.pid)) {
@ -238,7 +245,7 @@ namespace Proc {
break;
}
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;
}
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) {
switch (sortint) {
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
if (++counter >= 10000 || ((int)cache.size() > npids + 100)) {
unordered_flat_map<uint, p_cache> r_cache;
r_cache.reserve(procs.size());
counter = 0;
if (filter.empty()) {
for (auto& p : procs) r_cache[p.pid] = cache[p.pid];
cache.swap(r_cache);
unordered_flat_map<uint, p_cache> r_cache;
r_cache.reserve(pid_list.size());
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;
atomic_wait(drawing);

View file

@ -38,7 +38,7 @@ tab-size = 4
#include <termios.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;
//? ------------------------------------------------- NAMESPACES ------------------------------------------------------
@ -225,8 +225,10 @@ namespace Term {
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
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, "");
return std::count_if(str.begin(), str.end(),
[](char c) { return (static_cast<unsigned char>(c) & 0xC0) != 0x80; } );
@ -247,6 +249,18 @@ namespace Tools {
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
uint64_t time_s(){
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");
}
//* Check if a string is a valid positive integer value
bool isuint(string& str){
return all_of(str.begin(), str.end(), ::isdigit);
//* Convert string to bool, returning any value not equal to "true" or "True" as false
bool stobool(string& str){
return (str == "true" || str == "True") ? true : false;
}
//* Left-trim <t_str> from <str> and return string
string ltrim(string str, string t_str = " "){
while (str.starts_with(t_str)) str.erase(0, t_str.size());
return str;
//* Check if a string is a valid integer value
bool isint(string& str){
if (str.empty()) return false;
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
string rtrim(string str, string t_str = " "){
while (str.ends_with(t_str)) str.resize(str.size() - t_str.size());
return str;
//* Left-trim <t_str> from <str> and return new string
string ltrim(const string& str, const string t_str = " "){
string_view str_v = 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
string trim(string str, string t_str = " "){
//* Right-trim <t_str> from <str> and return new string
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);
}
//* 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;
string_view str_v = str;
if (times > 0) out.reserve(times);
if (!str.empty() && !delim.empty()){
if (!str_v.empty() && !delim.empty()){
size_t pos = 0;
int x = 0;
string tmp;
while ((pos = str.find(delim)) != string::npos){
tmp = str.substr(0, pos);
if (tmp != delim && !tmp.empty()) out.push_back(tmp);
str.erase(0, pos + delim.size());
while ((pos = str_v.find(delim)) != string::npos){
if (str_v.substr(0, pos) != delim) out.emplace_back(str_v.substr(0, pos));
str_v.remove_prefix(pos + delim.size());
if (times > 0 && ++x >= times) break;
}
}
if (!ignore_remainder) out.push_back(str);
if (!ignore_remainder) out.emplace_back(str_v);
return out;
}
//* 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));
}
@ -455,12 +477,12 @@ namespace Logger {
std::atomic<bool> busy (false);
bool first = true;
string tdf = "%Y/%m/%d (%T) | ";
unordered_flat_map<uint, string> log_levels = {
{ 0, "DISABLED" },
{ 1, "ERROR" },
{ 2, "WARNING" },
{ 3, "INFO" },
{ 4, "DEBUG" }
vector<string> log_levels = {
"DISABLED",
"ERROR",
"WARNING",
"INFO",
"DEBUG"
};
}
@ -480,7 +502,7 @@ namespace Logger {
if (!ec) {
std::ofstream lwrite(logfile, std::ios::app);
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();
}
else logfile.clear();