/* This file is part of c++script. c++script is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. c++script is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with c++script. If not, see . */ #ifndef CPPSCRIPT_H #define CPPSCRIPT_H #include #include #include #include #include #include #include #include #include #include namespace cpps { std::string basename (std::string_view s); std::string dirname (std::string_view s); std::string reverse (std::string_view s); void select (std::string_view val, std::string_view test, std::function f); template void select (std::string_view val, std::string_view test, std::function f, TS&&... vs) { std::regex reg (test.data(),test.data()+test.size(),std::regex_constants::ECMAScript); if (regex_match(val.begin(),val.end(),reg)){ f(val); }else{ select(val,vs...); } } struct DIRCONTENT_end{}; class DIRCONTENT_iterator{ public: std::string dirname; std::string name{"empty"}; unsigned long inode=0; unsigned entry_num=0; private: std::shared_ptr sdir; struct dirent *ent = nullptr; void readnext(); public: using value_type = DIRCONTENT_iterator; using difference_type = int; DIRCONTENT_iterator(){} DIRCONTENT_iterator(std::string_view _name); DIRCONTENT_iterator(const DIRCONTENT_iterator &n); DIRCONTENT_iterator & operator = (const DIRCONTENT_iterator &n); ~DIRCONTENT_iterator(); DIRCONTENT_iterator & operator ++(); DIRCONTENT_iterator operator ++(int); DIRCONTENT_iterator & operator *() const; bool operator == (const DIRCONTENT_iterator &d) const; bool operator == (const DIRCONTENT_end &d) const; bool operator == (std::string_view v) const{ return name == v; } }; class FILESTAT{ public: std::string dirname; std::string name; unsigned long inode; off_t size=0; struct timespec created{0,0},modified{0,0},accessed{0,0}; uid_t uid=0; gid_t gid=0; mode_t mode=0; dev_t dev=0; dev_t rdev=0; nlink_t nlink=0; FILESTAT(std::string_view _dirname, std::string_view _name); bool is_dir() const; bool is_reg() const; }; class DIRCONTENT{ std::string name; public: using iterator = DIRCONTENT_iterator; using const_iterator = DIRCONTENT_iterator; using difference_type = size_t; DIRCONTENT(const std::string &_name); ~DIRCONTENT(); iterator begin() noexcept; const_iterator begin() const noexcept; DIRCONTENT_end end(); }; static_assert(std::ranges::input_range); // Taken from https://mobiarch.wordpress.com/2023/12/17/reading-a-file-line-by-line-using-c-ranges/ struct FILELINE{ std::string line; size_t line_number = 0; friend std::istream &operator>>(std::istream &s, FILELINE &fl) { std::getline(s, fl.line); ++fl.line_number; return s; } bool operator == (std::string_view v) const { return line == v; } }; class FILEPATH{ public: std::string name; FILEPATH(const std::string_view _name) : name(_name) {} std::string dirname() const{ return cpps::dirname(name); } std::string basename() const{ return cpps::basename(name); } std::string abspath() const; std::string absdir() const; bool exist() const; FILESTAT stat() const; // file type // file owner,group // file is link // file is device }; class FILENAME: public FILEPATH{ std::ifstream file; public: FILENAME(const std::string_view _name) : FILEPATH(_name){} auto cat() { if (name == "-"){ // Special trick to assign std::cin to a FILENAME return std::views::istream(std::cin); }else{ if (file.is_open()){ file.close(); file.open(name); }else{ file.open(name); } if (file.fail()){ std::cerr << std::format ("Can't open file {}\n",name); } return std::views::istream(file); } } bool mayexec() const; bool is_empty() const; int unlink() const; }; struct COMMANDEXEC_end{}; class COMMANDEXEC_line{ bool is_err = false; // Is this standard output of error public: std::string line; bool is_error(){ return is_err;} bool is_output(){ return !is_err;} friend class COMMANDEXEC_iterator; }; class COMMANDEXEC_iterator{ private: void fill_line(); int readnext() const; class POPENUSER *pop = nullptr; COMMANDEXEC_line content; public: using value_type = COMMANDEXEC_line; using difference_type = int; COMMANDEXEC_iterator(POPENUSER *_pop); COMMANDEXEC_iterator(const COMMANDEXEC_iterator &n); COMMANDEXEC_iterator & operator = (const COMMANDEXEC_iterator &n); ~COMMANDEXEC_iterator(); COMMANDEXEC_iterator & operator ++(); COMMANDEXEC_iterator operator ++(int); COMMANDEXEC_line & operator *() const; bool operator == (const COMMANDEXEC_iterator &d) const; bool operator == (const COMMANDEXEC_end &d) const; }; class COMMANDEXEC{ std::shared_ptr pop; std::string command; public: using iterator = COMMANDEXEC_iterator; using const_iterator = COMMANDEXEC_iterator; using difference_type = size_t; COMMANDEXEC(std::string_view command); COMMANDEXEC(const COMMANDEXEC &n); ~COMMANDEXEC(); iterator begin() noexcept; const_iterator begin() const noexcept; COMMANDEXEC_end end(); }; class COMMAND{ std::string command; // Shell command executed with system(). public: COMMAND(std::string_view _command): command(_command){} COMMANDEXEC run() const {return COMMANDEXEC(command); } const std::string &get() const { return command; } static auto store_error (std::vector &tb, unsigned maxlines=1000){ return std::views::filter([&tb,maxlines](auto &&line) mutable{ if (line.is_error() && tb.size() < maxlines) tb.push_back(line.line); return line.is_output(); }); } }; std::vector string_split (const std::string_view v, char delim); template inline auto trf_towords(char delim=' ',Projection proj = {}){ return std::views::transform([delim,proj](auto &parm) { auto &c = std::invoke(proj,parm); using type = std::remove_reference_t; std::vector ret; if constexpr (std::is_same_v){ ret = string_split(c,delim); }else if constexpr (std::is_same_v){ ret = string_split(c,delim); }else if constexpr (std::is_same_v){ ret = string_split(c.line,delim); }else{ static_assert (false,"cpps::trf_towords: type not supported"); } return ret; }); } auto match_or_not(auto pattern,std::regex::flag_type flags, bool reverse_logic, auto proj) { return std::views::filter([reg = std::regex(pattern,flags),reverse_logic,proj](auto &&parm){ auto &c = std::invoke(proj,parm); using type = std::remove_reference_t; bool ret = false; if constexpr (std::is_same_v){ ret = regex_match(c,reg); }else if constexpr (std::is_same_v){ ret = regex_match(c.begin(),c.end(),reg); }else if constexpr (std::is_same_v){ ret = regex_match(c.line,reg); }else if constexpr (std::is_same_v){ ret = regex_match(c.name,reg); }else if constexpr (std::is_same_v){ ret = regex_match(c.name,reg); }else if constexpr (std::is_same_v){ ret = regex_match(c.name,reg); }else if constexpr (std::is_same_v){ ret = regex_match(c.line,reg); }else{ static_assert (false,"cpps::match: type not supported"); ret = false; } return reverse_logic ? ! ret : ret; }); } template auto match (Pattern pattern,std::regex::flag_type flags = std::regex_constants::basic, Projection proj = {}) { return match_or_not(pattern,flags,false,proj); } template inline auto match (std::string_view pattern,std::regex::flag_type flags = std::regex_constants::basic, Projection proj = {}) { std::string s_pat(pattern.begin(),pattern.end()); return match_or_not(s_pat,flags,false,proj); } template auto nomatch (Pattern pattern,std::regex::flag_type flags = std::regex_constants::basic, Projection proj = {}) { return match_or_not(pattern,flags,true,proj); } template inline auto nomatch (std::string_view pattern,std::regex::flag_type flags = std::regex_constants::basic, Projection proj = {}) { std::string s_pat(pattern.begin(),pattern.end()); return nomatch(s_pat,flags,proj); } auto contain_or_not (auto pattern,std::regex::flag_type flags, bool reverse_logic, auto proj) { return std::views::filter([reg=std::regex(pattern,flags),reverse_logic,proj](auto &&parm){ auto &c = std::invoke(proj,parm); using type = std::remove_reference_t; bool ret = false; if constexpr (std::is_same_v){ ret = regex_search(c,reg); }else if constexpr (std::is_same_v){ ret = regex_search(c.begin(),c.end(),reg); }else if constexpr (std::is_same_v){ ret = regex_search(c.line,reg); }else if constexpr (std::is_same_v){ ret = regex_search(c.name,reg); }else if constexpr (std::is_same_v){ ret = regex_search(c.name,reg); }else if constexpr (std::is_same_v){ ret = regex_search(c.name,reg); }else if constexpr (std::is_same_v){ ret = regex_search(c.line,reg); }else{ static_assert (false,"cpps::contain: type not supported"); ret = false; } return reverse_logic ? !ret : ret; }); } template auto contain (auto pattern,std::regex::flag_type flags = std::regex_constants::basic, Projection proj = {}) { return contain_or_not(pattern,flags,false,proj); } template inline auto contain (std::string_view pattern,std::regex::flag_type flags = std::regex_constants::basic, Projection proj = {}) { std::string s_pat(pattern.begin(),pattern.end()); return contain_or_not(s_pat,flags,false,proj); } template auto not_contain (auto pattern,std::regex::flag_type flags = std::regex_constants::basic, Projection proj = {}) { return contain_or_not(pattern,flags,true,proj); } template inline auto not_contain (std::string_view pattern,std::regex::flag_type flags = std::regex_constants::basic, Projection proj = {}) { std::string s_pat(pattern.begin(),pattern.end()); return contain_or_not(s_pat,flags,true,proj); } class DIRNAME: public FILEPATH{ public: DIRNAME(const std::string_view _name) : FILEPATH(_name){} std::string basename() const; DIRCONTENT ls() const; auto ls(const std::string_view pattern){ return ls() | match(pattern); } static auto stat(){ return std::views::transform([](auto &c){ return FILESTAT(c.dirname,c.name);}); } auto ls_l() const{ return ls()|stat(); } auto ls_l(const std::string_view pattern){ return ls() | match(pattern) | stat(); } std::string parent() const; bool exist() const; static auto drop_hidden(){ return std::views::filter([](auto &&st){return st.name.size() > 0 && st.name[0] != '.';}); } static auto drop_dot_dirs(){ return std::views::filter([](auto &&st){return st.name != "." && st.name != "..";}); } }; class PROCESS{ public: pid_t pid; std::string exe; pid_t ppid; pid_t tgid; uid_t uid; gid_t gid; std::string name; size_t vmsize; size_t vmrss; PROCESS(std::string_view _pid); bool operator == (std::string_view v) const{ return name == v; } bool is_kernel() const; bool is_thread() const; pid_t get_parent() const; }; // Enumerate all processes in the system inline auto processes () { return DIRNAME("/proc").ls() | match ("[0-9]*") | std::views::transform([](auto &c){ return cpps::PROCESS(c.name);}); } inline auto user_processes() { return processes() | std::views::filter([](auto &&p){ return !p.is_kernel();}); } inline auto threads (pid_t pid) { std::string pid_string = std::format("{}",pid); return DIRNAME(std::format("/proc/{}/task",pid)).ls() | match ("[0-9]*") | nomatch (pid_string) | std::views::transform([](auto &c){ return cpps::PROCESS(c.name);}); } class option_base{ public: char letter; std::string longname; std::string desc; bool mandatory; bool valueneeded; bool mayrepeat; std::string defval; // Default value for the option unsigned nbseen=0; // How many time the option has been seen option_base(const char _letter, std::string_view _longname, std::string_view _desc, bool _mandatory, bool _valueneeded, bool _mayrepeat); ~option_base(); bool addval (std::string_view val); virtual bool setval (std::string_view val)=0; virtual void enable ()=0; }; extern std::vector options; bool option_setval (std::string &val, std::string_view v, char letter, const std::string &longname); bool option_setval (int &val, std::string_view v, char letter, const std::string &longname); bool option_setval (unsigned &val, std::string_view v, char letter, const std::string &longname); bool option_setval (double &val, std::string_view v, char letter, const std::string &longname); template requires std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v class option: protected option_base{ public: T val{0}; option(const char _letter, std::string_view _longname, std::string_view _desc, T _defval={0}, bool _mandatory=false) :option_base(_letter,_longname,_desc,_mandatory,!std::is_same_v,false) ,val(_defval) { if constexpr(std::is_same_v || std::is_same_v || std::is_same_v){ if (_defval != 0) defval = std::to_string(_defval); }else if constexpr(std::is_same_v){ defval = '"' + _defval + '"'; } options.push_back(this); } bool setval(std::string_view _val){ bool ret = false; if constexpr (std::is_same_v){ std::cerr << format ("Can't set the value of a the 'bool' option -{} --{}\n",letter,longname); }else{ ret = option_setval(val,_val,letter,longname); } return ret; } void enable(){ if constexpr (std::is_same_v){ val = true; }else{ std::cerr << format ("Can't enable the non bool option -{} --{}\n",letter,longname); } } }; // options accepting more than one value (stored in a vector) // Accessed using the member vals template requires std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v class optionv: protected option_base{ public: std::vector vals; optionv(const char _letter, std::string_view _longname, std::string_view _desc, bool _mandatory=false) :option_base(_letter,_longname,_desc,_mandatory,true,true) { options.push_back(this); } bool setval(std::string_view _val){ bool ret = false; if constexpr (std::is_same_v){ std::cerr << format ("Can't set the value of a the 'bool' option -{} --{}\n",letter,longname); }else{ T val; ret = option_setval(val,_val,letter,longname); vals.push_back(val); } return ret; } void enable(){ std::cerr << format ("Can't enable the non bool option -{} --{}\n",letter,longname); } }; std::string string_trim(const std::string_view v); extern std::vector args; void usage(); void endoptions(int argc, char *argv[]); void progdesc(std::string_view desc); void filesys_debug(); void processes_debug(); const std::map makeenviron(); } // namespace cpps template <> struct std::formatter : std::formatter { auto format(const struct timespec &p, format_context& ctx) const { return formatter::format( std::format("{}", cpps::string_trim(ctime(&p.tv_sec))), ctx); } }; #endif