/// @file /// @author rfree (current maintainer in monero.cc project) /// @brief various general utils taken from (and relate to) otshell project, including loggiang/debug /* See other files here for the LICENCE that applies here. */ /* See header file .hpp for info */ #include #include #include #include #include #include #include #include "utils.hpp" #include "ccolor.hpp" #include "lib_common1.hpp" #include "runoptions.hpp" #if defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined (WIN64) #define OS_TYPE_WINDOWS #elif defined(__unix__) || defined(__posix) || defined(__linux) || defined(__darwin) || defined(__APPLE__) || defined(__clang__) #define OS_TYPE_POSIX #else #warning "Compiler/OS platform is not recognized" #warning "Just assuming it will work as POSIX then" #define OS_TYPE_POSIX #endif #if defined(OS_TYPE_WINDOWS) #include #elif defined(OS_TYPE_POSIX) #include #include #include #else #error "Compiler/OS platform detection failed - not supported" #endif namespace nOT { namespace nUtils { INJECT_OT_COMMON_USING_NAMESPACE_COMMON_1; // <=== namespaces myexception::myexception(const char * what) : std::runtime_error(what) { } myexception::myexception(const std::string &what) : std::runtime_error(what) { } void myexception::Report() const { _erro("Error: " << what()); } //myexception::~myexception() { } // ==================================================================== // text trimming // http://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring std::string & ltrim(std::string &s) { s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun(std::isspace)))); return s; } std::string & rtrim(std::string &s) { s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), s.end()); return s; } std::string & trim(std::string &s) { return ltrim(rtrim(s)); } std::string get_current_time() { std::stringstream stream; struct tm * date; std::chrono::high_resolution_clock::time_point now = std::chrono::high_resolution_clock::now(); time_t time_now; time_now = std::chrono::high_resolution_clock::to_time_t(now); date = std::localtime(& time_now); char date_buff[32]; std::strftime(date_buff, sizeof(date_buff), "%d-%b-%Y %H:%M:%S.", date); stream << date_buff; std::chrono::high_resolution_clock::duration duration = now.time_since_epoch(); int64_t micro = std::chrono::duration_cast(duration).count(); micro %= 1000000; stream << std::setfill('0') << std::setw(3) << micro; return stream.str(); } cNullstream g_nullstream; // extern a stream that does nothing (eats/discards data) std::mutex gLoggerGuard; // extern // ==================================================================== namespace nDetail { const char* DbgShortenCodeFileName(const char *s) { const char *p = s; const char *a = s; bool inc=1; while (*p) { ++p; if (inc && ('\0' != * p)) { a=p; inc=false; } // point to the current character (if valid) becasue previous one was slash if ((*p)=='/') { a=p; inc=true; } // point at current slash (but set inc to try to point to next character) } return a; } } // a workaround for MSVC compiler; e.g. see https://bugs.webkit.org/show_bug.cgi?format=multiple&id=125795 #ifndef _MSC_VER template std::unique_ptr make_unique( Args&& ...args ) { return std::unique_ptr( new T( std::forward(args)... ) ); } #else using std::make_unique; #endif // ==================================================================== char cFilesystemUtils::GetDirSeparator() { // TODO nicer os detection? #if defined(OS_TYPE_POSIX) return '/'; #elif defined(OS_TYPE_WINDOWS) return '\\'; #else #error "Do not know how to compile this for your platform." #endif } bool cFilesystemUtils::CreateDirTree(const std::string & dir, bool only_below) { const bool dbg=false; //struct stat st; const char dirch = cFilesystemUtils::GetDirSeparator(); std::istringstream iss(dir); string part, sofar=""; if (dir.size()<1) return false; // illegal name // dir[0] is valid from here if (only_below && (dir[0]==dirch)) return false; // no jumping to top (on any os) while (getline(iss,part,dirch)) { if (dbg) cout << '['<= mLevel) && (mStream)) { ostream & output = SelectOutput(level,channel); output << icon(level) << ' '; std::thread::id this_id = std::this_thread::get_id(); output << "{" << Thread2Number(this_id) << "} "; return output; } return g_nullstream; } std::string cLogger::GetLogBaseDir() const { return "log"; } void cLogger::OpenNewChannel(const std::string & channel) { size_t last_split = channel.find_last_of(cFilesystemUtils::GetDirSeparator()); // log/test/aaa // ^----- last_split string dir = GetLogBaseDir() + cFilesystemUtils::GetDirSeparator() + channel.substr(0, last_split); string basefile = channel.substr(last_split+1) + ".log"; string fname = dir + cFilesystemUtils::GetDirSeparator() + cFilesystemUtils::GetDirSeparator() + basefile; _dbg1("Starting debug to channel file: " + fname + " in directory ["+dir+"]"); bool dirok = cFilesystemUtils::CreateDirTree(dir); if (!dirok) { const string msg = "In logger failed to open directory (" + dir +")."; _erro(msg); throw std::runtime_error(msg); } std::ofstream * thefile = new std::ofstream( fname.c_str() ); *thefile << "====== (Log opened: " << fname << ") ======" << endl; mChannels.insert( std::pair(channel , thefile ) ); } std::ostream & cLogger::SelectOutput(int level, const std::string & channel) { if (channel=="") return *mStream; auto obj = mChannels.find(channel); if (obj == mChannels.end()) { // new channel OpenNewChannel(channel); return SelectOutput(level,channel); } else { // existing return * obj->second; } } void cLogger::setOutStreamFile(const string &fname) { // switch to using this file _mark("WILL SWITCH DEBUG NOW to file: " << fname); mOutfile = make_unique(fname); mStream = & (*mOutfile); _mark("Started new debug, to file: " << fname); } void cLogger::setOutStreamFromGlobalOptions() { if ( gRunOptions.getDebug() ) { if ( gRunOptions.getDebugSendToFile() ) { mOutfile = make_unique ("debuglog.txt"); mStream = & (*mOutfile); } else if ( gRunOptions.getDebugSendToCerr() ) { mStream = & std::cerr; } else { mStream = & g_nullstream; } } else { mStream = & g_nullstream; } } void cLogger::setDebugLevel(int level) { bool note_before = (mLevel > level); // report the level change before or after the change? (on higher level) if (note_before) _note("Setting debug level to "<= 100) return cc::back::red + ToStr(cc::fore::black) + ToStr("ERROR ") + ToStr(cc::fore::lightyellow) + " " ; if (level >= 90) return cc::back::lightyellow + ToStr(cc::fore::black) + ToStr("Warn ") + ToStr(cc::fore::red)+ " " ; if (level >= 80) return cc::back::lightmagenta + ToStr(cc::fore::black) + ToStr("MARK "); //+ zkr::cc::console + ToStr(cc::fore::lightmagenta)+ " "; if (level >= 75) return cc::back::lightyellow + ToStr(cc::fore::black) + ToStr("FACT ") + zkr::cc::console + ToStr(cc::fore::lightyellow)+ " "; if (level >= 70) return cc::fore::green + ToStr("Note "); if (level >= 50) return cc::fore::cyan + ToStr("info "); if (level >= 40) return cc::fore::lightwhite + ToStr("dbg "); if (level >= 30) return cc::fore::lightblue + ToStr("dbg "); if (level >= 20) return cc::fore::blue + ToStr("dbg "); return " "; } std::string cLogger::endline() const { return ToStr("") + zkr::cc::console + ToStr("\n"); // TODO replan to avoid needles converting back and forth char*, string etc } int cLogger::Thread2Number(const std::thread::id id) { auto found = mThread2Number.find( id ); if (found == mThread2Number.end()) { // new one mThread2Number_Biggest++; mThread2Number[id] = mThread2Number_Biggest; return mThread2Number_Biggest; // _info("(This is a new thread)"); // recursion! } else { return mThread2Number[id]; } } // ==================================================================== // object gCurrentLogger is defined later - in global namespace below // ==================================================================== // vector debug void DisplayStringEndl(std::ostream & out, const std::string text) { out << text; out << std::endl; } std::string SpaceFromEscape(const std::string &s) { std::ostringstream newStr; for(size_t i = 0; i < s.length();i++) { if(s[i] == '\\' && s[i+1] ==32) newStr<<""; else newStr<=32 && s[i] <= 126) newStr<= ending.length()) { return (0 == all.compare (all.length() - ending.length(), ending.length(), ending)); } else { return false; } } vector WordsThatMatch(const std::string & sofar, const vector & possib) { vector ret; for ( auto rec : possib) { // check of possibilities if (CheckIfBegins(sofar,rec)) { rec = EscapeFromSpace(rec); ret.push_back(rec); // this record matches } } return ret; } char GetLastChar(const std::string & str) { // TODO unicode? auto s = str.length(); if (s==0) throw std::runtime_error("Getting last character of empty string (" + ToStr(s) + ")" + OT_CODE_STAMP); return str.at( s - 1); } std::string GetLastCharIf(const std::string & str) { // TODO unicode? auto s = str.length(); if (s==0) return ""; // empty string signalizes ther is nothing to be returned return std::string( 1 , str.at( s - 1) ); } // ==================================================================== // ASRT - assert. Name like ASSERT() was too long, and ASS() was just... no. // Use it like this: ASRT( x>y ); with the semicolon at end, a clever trick forces this syntax :) void Assert(bool result, const std::string &stamp, const std::string &condition) { if (!result) { _erro("Assert failed at "+stamp+": ASSERT( " << condition << ")"); throw std::runtime_error("Assert failed at "+stamp+": ASSERT( " + condition + ")"); } } // ==================================================================== // advanced string const std::string GetMultiline(string endLine) { std::string result(""); // Taken from OT_CLI_ReadUntilEOF while (true) { std::string input_line(""); if (std::getline(std::cin, input_line, '\n')) { input_line += "\n"; if (input_line[0] == '~') break; result += input_line; } if (std::cin.eof() ) { std::cin.clear(); break; } if (std::cin.fail() ) { std::cin.clear(); break; } if (std::cin.bad()) { std::cin.clear(); break; } } return result; } vector SplitString(const string & str){ std::istringstream iss(str); vector vec { std::istream_iterator{iss}, std::istream_iterator{} }; return vec; } bool checkPrefix(const string & str, char prefix) { if (str.at(0) == prefix) return true; return false; } // ==================================================================== // operation on files #ifdef __unix void cEnvUtils::GetTmpTextFile() { // TODO make this name configurable (depending on project) char filename[] = "/tmp/otshellutils_text.XXXXXX"; fd = mkstemp(filename); if (fd == -1) { _erro("Can't create the file: " << filename); return; } mFilename = filename; } void cEnvUtils::CloseFile() { close(fd); unlink( mFilename.c_str() ); } void cEnvUtils::OpenEditor() { char* editor = std::getenv("OT_EDITOR"); //TODO Read editor from configuration file if (editor == NULL) editor = std::getenv("VISUAL"); if (editor == NULL) editor = std::getenv("EDITOR"); string command; if (editor != NULL) command = ToStr(editor) + " " + mFilename; else command = "/usr/bin/editor " + mFilename; _dbg3("Opening editor with command: " << command); if ( system( command.c_str() ) == -1 ) _erro("Cannot execute system command: " << command); } const string cEnvUtils::ReadFromTmpFile() { std::ifstream ifs(mFilename); string msg((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); return msg; } const string cEnvUtils::Compose() { GetTmpTextFile(); OpenEditor(); string input = ReadFromTmpFile(); CloseFile(); return input; } #endif const string cEnvUtils::ReadFromFile(const string path) { std::ifstream ifs(path); string msg((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); return msg; } void hintingToTxt(std::fstream & file, string command, vector &commands) { if(file.good()) { file< lightColors; using namespace zkr; lightColors.push_back(cc::fore::lightblue); lightColors.push_back(cc::fore::lightred); lightColors.push_back(cc::fore::lightmagenta); lightColors.push_back(cc::fore::lightgreen); lightColors.push_back(cc::fore::lightcyan); lightColors.push_back(cc::fore::lightyellow); lightColors.push_back(cc::fore::lightwhite); int sum=0; for (auto ch : hash) sum+=ch; auto color = sum%(lightColors.size()-1); return lightColors.at( color ); } // ==================================================================== // algorthms }; // namespace nUtil }; // namespace OT // global namespace const extern int _dbg_ignore = 0; // see description in .hpp std::string GetObjectName() { //static std::string * name=nullptr; //if (!name) name = new std::string("(global)"); return ""; } // ==================================================================== nOT::nUtils::cLogger gCurrentLogger;