#ifndef CTOOL_BASE #define CTOOL_BASE #include #include #include #include #include #ifndef _WIN32 #include #include #else #include #endif #include "hecl/Database.hpp" #include "logvisor/logvisor.hpp" extern logvisor::Module LogModule; struct ToolPassInfo { hecl::SystemString pname; hecl::SystemString cwd; std::vector args; std::vector flags; hecl::SystemString output; hecl::Database::Project* project = nullptr; unsigned verbosityLevel = 0; bool force = false; bool yes = false; bool gui = false; }; #define RED "\033[0;31m" #define GREEN "\033[0;32m" #define YELLOW "\033[0;33m" #define BLUE "\033[0;34m" #define MAGENTA "\033[0;35m" #define CYAN "\033[0;36m" #define BOLD "\033[1m" #define NORMAL "\033[0m" #define HIDE_CURSOR "\033[?25l" #define SHOW_CURSOR "\033[?25h" #define WRAP_INDENT 4 extern bool XTERM_COLOR; class ToolBase { protected: const ToolPassInfo& m_info; bool m_good = false; bool continuePrompt() { if (!m_info.yes) { if (XTERM_COLOR) hecl::Printf(_S("\n" BLUE BOLD "Continue?" NORMAL " (Y/n) ")); else hecl::Printf(_S("\nContinue? (Y/n) ")); fflush(stdout); int ch; #ifndef _WIN32 struct termios tioOld, tioNew; tcgetattr(0, &tioOld); tioNew = tioOld; tioNew.c_lflag &= ~ICANON; tcsetattr(0, TCSANOW, &tioNew); while ((ch = getchar())) #else while ((ch = getch())) #endif { if (ch == 'n' || ch == 'N') return false; if (ch == 'y' || ch == 'Y' || ch == '\r' || ch == '\n') break; } #ifndef _WIN32 tcsetattr(0, TCSANOW, &tioOld); #endif } hecl::Printf(_S("\n")); return true; } public: ToolBase(const ToolPassInfo& info) : m_info(info) { hecl::VerbosityLevel = info.verbosityLevel; hecl::GuiMode = info.gui; } virtual ~ToolBase() {} virtual hecl::SystemString toolName() const=0; virtual int run()=0; inline operator bool() const {return m_good;} }; class HelpOutput { public: typedef void(*HelpFunc)(HelpOutput&); private: FILE* m_sout; HelpFunc m_helpFunc; int m_lineWidth; hecl::SystemString m_wrapBuffer; void _wrapBuf(hecl::SystemString& string) { int counter; hecl::SystemString::iterator it = string.begin(); while (it != string.end()) { std::ptrdiff_t v = it - string.begin(); /* copy string until the end of the line is reached */ for (counter=WRAP_INDENT ; counter < m_lineWidth ; ++counter) { if (it >= string.end()) return; if (*it == _S('\n')) { counter = WRAP_INDENT; ++it; } if (counter == WRAP_INDENT) { for (int i=0 ; i= string.end()) return; if (*it != _S('\n')) ++it; } /* check for whitespace */ if (isspace(*it)) { *it = _S('\n'); counter = WRAP_INDENT; ++it; } else { /* check for nearest whitespace back in string */ for (hecl::SystemString::iterator k=it ; k!=string.begin() ; --k) { if (isspace(*k)) { counter = WRAP_INDENT; if (k - string.begin() < v) k = string.insert(it, _S('\n')); else *k = _S('\n'); it = k + 1; break; } } } } } public: HelpOutput(HelpFunc helpFunc) : m_sout(NULL), m_helpFunc(helpFunc), m_lineWidth(hecl::GuiMode ? 120 : hecl::ConsoleWidth()) {} void go() { #if _WIN32 m_sout = stdout; m_helpFunc(*this); #else m_sout = popen("less -R", "w"); if (m_sout) { m_helpFunc(*this); pclose(m_sout); } else { m_sout = stdout; m_helpFunc(*this); } #endif } void print(const hecl::SystemChar* str) { hecl::FPrintf(m_sout, _S("%s"), str); } void printBold(const hecl::SystemChar* str) { if (XTERM_COLOR) hecl::FPrintf(m_sout, _S("" BOLD "%s" NORMAL ""), str); else hecl::FPrintf(m_sout, _S("%s"), str); } void secHead(const hecl::SystemChar* headName) { if (XTERM_COLOR) hecl::FPrintf(m_sout, _S("" BOLD "%s" NORMAL "\n"), headName); else hecl::FPrintf(m_sout, _S("%s\n"), headName); } void optionHead(const hecl::SystemChar* flag, const hecl::SystemChar* synopsis) { if (XTERM_COLOR) hecl::FPrintf(m_sout, _S("" BOLD "%s" NORMAL " (%s)\n"), flag, synopsis); else hecl::FPrintf(m_sout, _S("%s (%s)\n"), flag, synopsis); } void beginWrap() { m_wrapBuffer.clear(); } void wrap(const hecl::SystemChar* str) { m_wrapBuffer += str; } void wrapBold(const hecl::SystemChar* str) { if (XTERM_COLOR) m_wrapBuffer += _S("" BOLD ""); m_wrapBuffer += str; if (XTERM_COLOR) m_wrapBuffer += _S("" NORMAL ""); } void endWrap() { _wrapBuf(m_wrapBuffer); m_wrapBuffer += _S('\n'); hecl::FPrintf(m_sout, _S("%s"), m_wrapBuffer.c_str()); m_wrapBuffer.clear(); } }; static hecl::SystemString MakePathArgAbsolute(const hecl::SystemString& arg, const hecl::SystemString& cwd) { #if _WIN32 if (arg.size() >= 2 && iswalpha(arg[0]) && arg[1] == _S(':')) return arg; if (arg[0] == _S('\\') || arg[0] == _S('/')) return arg; return cwd + _S('\\') + arg; #else if (arg[0] == _S('/') || arg[0] == _S('\\')) return arg; if (cwd.back() == _S('/') || cwd.back() == _S('\\')) return cwd + arg; return cwd + _S('/') + arg; #endif } static bool g_HasLastProgTime = false; static std::chrono::steady_clock::time_point g_LastProgTime; void ToolPrintProgress(const hecl::SystemChar* message, const hecl::SystemChar* submessage, int lidx, float factor, int& lineIdx) { if (g_HasLastProgTime) { std::chrono::steady_clock::time_point newPoint = std::chrono::steady_clock::now(); std::chrono::milliseconds::rep delta = std::chrono::duration_cast(newPoint - g_LastProgTime).count(); if (delta < 50) return; g_LastProgTime = newPoint; } else { g_HasLastProgTime = true; g_LastProgTime = std::chrono::steady_clock::now(); } auto lk = logvisor::LockLog(); bool blocks = factor >= 0.0; factor = std::max(0.0f, std::min(1.0f, factor)); int iFactor = factor * 100.0; if (XTERM_COLOR) hecl::Printf(_S("" HIDE_CURSOR "")); if (lidx > lineIdx) { hecl::Printf(_S("\n ")); lineIdx = lidx; } else hecl::Printf(_S(" ")); int width = (hecl::GuiMode ? 120 : std::max(80, hecl::ConsoleWidth())); int half; if (blocks) half = width / 2 - 2; else half = width - 4; if (!message) message = _S(""); int messageLen = hecl::StrLen(message); if (!submessage) submessage = _S(""); int submessageLen = hecl::StrLen(submessage); if (half - messageLen < submessageLen-2) submessageLen = 0; if (submessageLen) { if (messageLen > half-submessageLen-1) hecl::Printf(_S("%.*s... %s "), half-submessageLen-4, message, submessage); else { hecl::Printf(_S("%s"), message); for (int i=half-messageLen-submessageLen-1 ; i>=0 ; --i) hecl::Printf(_S(" ")); hecl::Printf(_S("%s "), submessage); } } else { if (messageLen > half) hecl::Printf(_S("%.*s... "), half-3, message); else { hecl::Printf(_S("%s"), message); for (int i=half-messageLen ; i>=0 ; --i) hecl::Printf(_S(" ")); } } if (blocks) { if (XTERM_COLOR) { int blocks = half - 7; int filled = blocks * factor; int rem = blocks - filled; hecl::Printf(_S("" BOLD "%3d%% ["), iFactor); for (int b=0 ; b