• main.cpp
  • #include "ChargeCheck.h"
    #include "Functional.h"
    #include <cstddef>
    #include <cstring>
    #include <iostream>
    #include <sched.h>
    #include <sstream>
    #include <string>
    #include <fcntl.h>
    #include <sys/stat.h>
    #include <thread>
    #include <type_traits>
    
    std::string program_name; 
    
    bool isNum(std::string str) { 
        for(char ch : str) 
            if(ch < '0' || ch > '9') 
                return false; 
        return true; 
    }
    
    std::string operator""_str(const char * str, size_t) {
        return {str}; 
    }
    
    std::string fullem_all(std::string fmt, std::string fill) {
        std::string full = "";
        std::size_t start_pos = 0;
        while ((start_pos = fmt.find("{}", start_pos)) != std::string::npos) {
            full += fmt.substr(0, start_pos);  
            full += fill;  
            fmt.erase(0, start_pos + 2);  
            start_pos = 0;  
        }
        full += fmt;  
        return full;
    }
    
    std::string help(std::string programname) {
        return fullem_all(
            "Help:\n"
            "{} -ll/--low-level <value> : Set the low battery threshold level\n"
            "    -ll <value> : Sets the level at which low battery notifications will be sent\n"
            "{} -cl/--critical-level <value> : Set the critical battery threshold level\n"
            "    -cl <value> : Sets the level at which critical battery notifications will be sent\n"
            "{} -fl/--full-level <value> : Set the full battery threshold level\n"
            "    -fl <value> : Sets the level at which full battery notifications will be sent\n"
            "{} -h/--help : Print this help text\n"
            "    -h : Prints detailed information about available command-line options\n", programname);
    }
    
    std::string help_dispatch(std::string dispatchname) {
        return fullem_all(
            "Help:\n"
            "{} get <sub: charge level | low level | critical level> : Get current or threshold battery levels\n"
            "    {} get charge level : Prints the current battery charge level\n"
            "    {} get low level : Prints the threshold level for low battery notifications\n"
            "    {} get critical level : Prints the threshold level for critical battery notifications\n"
            "    {} get full level : Prints the threshold level for full battery notifications\n"
            "{} set <sub: low level | critical level> <value> : Set threshold battery levels\n"
            "    {} set low level <value> : Sets the threshold level for low battery notifications\n"
            "    {} set critical level <value> : Sets the threshold level for critical battery notifications\n"
            "    {} set full level <value> : Sets the threshold level for full battery notifications\n"
            "{} is <sub: charging | full> : Check battery status\n"
            "    {} is charging : Prints the current charging status\n"
            "    {} is full : Prints whether the battery is fully charged\n"
            "{} stop : stops the daemon\n"
            , dispatchname
        );
    }
    
    template <typename... Args>
    std::string command_call(FunctionWrapper<std::string, Args...> fp) {
        return std::string(fp) + "\n";
    }
    
    template <class Ty>
    std::string command_call(Ty arg) {
        if constexpr (std::is_same_v<std::string, Ty> or std::is_same_v<const char *, Ty>) {
            return std::string(arg) + "\n"; 
        } else {
            return std::to_string(arg);
        }
    }
    
    template<typename... Args>
    std::string command_call(std::string (*fp)(Args...), Args... arg) {
        return fp(arg...);
    }
    
    std::string set(std::string val, auto fp, std::string sub) {
        auto ss = "ok"_str; 
        if(val.size() < "xx"_str.size()) {
            ss = "You can't give empty value.\nUsage: set " + sub + " level xx\n i.e:set " + sub + " level 25\n";
            return ss;
        }
        if(!isNum(val)) {
            ss = "You can't enter non-numerical value as " + sub + " battery level.\n";
            return ss; 
        }
        fp();
        return ss;
    }
    
    std::string handle_command(std::string inp) {
        std::stringstream ss; 
        if(inp.substr(0, 4) == "help") {
            std::string res = inp.substr(5); 
            return command_call(FunctionWrapper<std::string, std::string>(help_dispatch, std::move(res)));
        }
        if(inp == "get charge level") {
            return command_call(ChargeCheck::variables->charge); 
        }
        if(inp == "get critical level") {
            return command_call(ChargeCheck::variables->critical_level);
        }
        if(inp == "get low level") {
            return command_call(ChargeCheck::variables->low_level); 
        }
        if(inp == "get full level") {
            return command_call(ChargeCheck::variables->full_level); 
        }
        if(inp == "is charging") {
            auto res = ChargeCheck::is_charging();
            return res.substr(0, res.find("ing") + 3) + "\n";
        }
        if(inp == "is full") {
            return command_call(ChargeCheck::variables->is_full ? "Yes"_str : "No"_str);
        }
    #ifdef DEBUG
        if(inp == "get status cnotified") {
            ss << (ChargeCheck::variables->cnotified ? "Yes" : "No") << std::endl;
            return ss.str();
        }
        if(inp == "get status lnotified") {
            ss << (ChargeCheck::variables->lnotified ? "Yes" : "No") << std::endl;
            return ss.str();
        }
        if(inp == "get status inotified") {
            ss << (ChargeCheck::variables->inotified ? "Yes" : "No") << std::endl;
            return ss.str();
        }
    #endif
        if(inp == "stop") {
            FunctionWrapper<std::string, int> stop([](int dummy){
                ChargeCheck::variables->stop = true;
                std::cout << "Stop requested. Dying in 10 seconds." << std::endl; 
                return "Waiting for the subprocess to die.\n"_str;
            }, 1);
            return stop();
        }
        if(inp.find("set critical level") != -1) {
            auto val = inp.substr(19);
            return set(val, [val]() {
                ChargeCheck::variables->critical_level = std::stoi(val);
            }, "critical");
        }
        if(inp.find("set low level") != -1) {
            auto val = inp.substr(14);
            return set(val, [val]() {
                ChargeCheck::variables->low_level = std::stoi(val); 
            }, "low"); 
        }
        if(inp.find("set full level") != -1) {
            auto val = inp.substr(15); 
            return set(val, [val]() {
                ChargeCheck::variables->full_level = std::stoi(val); 
            }, "full");
        }
        return "Command \"" + inp +  "\" not found.\n";
    }
    
    
    void get() {
        int fd;
        std::string fs = "/tmp/charger"; 
        mkfifo(fs.c_str(), 0666);
        char arr[1024];
        int len = strlen(arr);
        std::string inp; 
        while(!ChargeCheck::variables->stop) {
            fd = open(fs.c_str(), O_RDONLY);
            read(fd, arr, 1024);
            close(fd);
    
            inp = std::string(arr); 
    
            auto res = handle_command(inp);
    
            fd = open(fs.c_str(), O_WRONLY);
            write(fd, res.c_str(), res.size() + 1); 
            close(fd);
            if(inp == "stop") {
                exit(1);
            }
        }
    }
    
    ChargeCheckVars arghandler(int argc, char ** argv) {
        program_name = argv[0]; 
        std::vector<std::string> argList; 
        unsigned short cl = 0, ll = 0, fl = 0; 
        if(argc < 2) {
            return {20, 25, 100, ChargeCheck::is_charging_bool()}; 
        }
        for(int i = 1; i < argc; i++) {
            argList.emplace_back(argv[i]); 
        }
    
        for(int i = 0; i < argList.size(); i++) {
            if((argList[i] == "-cl" || argList[i] == "--critical-level") && argc > i+1 && isNum(argList[i+1])) {
                cl = std::stoi(argList[i+1]); 
                continue;
            }
            if((argList[i] == "-ll" || argList[i] == "--low-level")&& argc > i+1 && isNum(argList[i+1])) {
                ll = std::stoi(argList[i+1]);
                continue;
            }
            if((argList[i] == "-fl" || argList[i] == "--full-level")&& argc > i+1 && isNum(argList[i+1])) {
                fl = std::stoi(argList[i+1]);
                continue;
            }
            if((argList[i] == "-h" || argList[i] == "--help")) {
                std::cout << help(program_name);
                exit(0);
                continue;
            }
            // todo: refactor this part
            if(argList[i] == std::to_string(ll) or argList[i] == std::to_string(cl) or argList[i] == std::to_string(fl)) {
                continue;
            }
            std::cout << "Unknow argument " << argList[i]; 
            exit(1);
        }
        if(ll == 0) {
            ll = 25; 
        }
        if(cl == 0) {
            cl = 20;
        }
        if(fl == 0) {
            fl = 100; 
        }
        return  ChargeCheckVars { cl, ll, fl, ChargeCheck::is_charging_bool()}; 
    }
    
    int main(int argc, char **argv) {
        pid_t f = fork(); 
        if(f == -1) {
            std::cerr << "fork" << std::endl; 
            exit(1); 
        }
        if(f == 0) {
            ChargeCheckVars c = arghandler(argc, argv); 
            ChargeCheck::variables = &c; 
            std::thread t(ChargeCheck::update);
            std::thread t2(get);
            t.join();
            t2.join();
        } else {
            exit(0);
        }
        return 0;
    }