diff --git a/Unvirt/CmdHelper.cpp b/Unvirt/CmdHelper.cpp index 0ad12e9..fe2efed 100644 --- a/Unvirt/CmdHelper.cpp +++ b/Unvirt/CmdHelper.cpp @@ -1,11 +1,10 @@ #include "CmdHelper.hpp" #include "TerminalHelper.hpp" -#include namespace Unvirt { namespace CmdHelper { -#pragma region cmd splitter +#pragma region CmdSplitter CmdSplitter::CmdSplitter() : mCmdChar(0), mBuffer(nullptr), mResult(nullptr), @@ -69,8 +68,7 @@ namespace Unvirt { #pragma endregion -#pragma region interactive cmd helper classes - +#pragma region OptionsDescription OptionsDescription::OptionsDescription() : mLongNameDict(), mShortNameMapping(), mPositionalArgMapping() { @@ -121,6 +119,21 @@ namespace Unvirt { mPositionalArgMapping.push_back(std::move(fullname)); } + OptionDescription* OptionsDescription::GetDescByLongName(const std::string& longname) { + const auto search = mLongNameDict.find(longname); + if (search == mLongNameDict.end()) return nullptr; + return &(*search).second; + } + OptionDescription* OptionsDescription::GetDescByShortName(const char shortname) { + const auto search = mShortNameMapping.find(shortname); + if (search == mShortNameMapping.end()) return nullptr; + return GetDescByLongName((*search).second); + } + OptionDescription* OptionsDescription::GetDescByPosition(int pos) { + if (pos >= mPositionalArgMapping.size()) return nullptr; + return GetDescByLongName(mPositionalArgMapping[pos]); + } + void OptionsDescription::PrintHelp(FILE* f) { fputs(UNVIRT_TERMCOL_LIGHT_YELLOW(("Allowed Options: \n")), f); for (const auto& [key, value] : mLongNameDict) { @@ -136,19 +149,124 @@ namespace Unvirt { } - VariablesMap::VariablesMap() { +#pragma endregion + +#pragma region VariablesMap + + VariablesMap::VariablesMap() : mDataPair() { + ; + } + VariablesMap::~VariablesMap() { + this->Clear(); } - VariablesMap::~VariablesMap() { + void VariablesMap::Clear(void) { + for (const auto& [key, value] : mDataPair) { + if (value.mData != nullptr) free(value.mData); + } + mDataPair.clear(); + } + + bool VariablesMap::AddPair(std::string& name, CmdArgType t, std::string& val) { + if (mDataPair.contains(name)) return false; + + AnyVariable var; + switch (t) { + case Unvirt::CmdHelper::CmdArgType::NONE: + { + var.mDataBasicSize = 1; + var.mData = nullptr; + break; + } + case Unvirt::CmdHelper::CmdArgType::STRING: + { + var.mDataBasicSize = sizeof(char); + var.mData = malloc(val.size() + 1); + if (var.mData == nullptr) break; + + memcpy(var.mData, val.c_str(), val.size() + 1); + break; + } + case Unvirt::CmdHelper::CmdArgType::INT: + { + var.mDataBasicSize = sizeof(int); + var.mData = malloc(sizeof(int)); + if (var.mData == nullptr) break; + + char* pend = nullptr; + errno = 0; + int64_t v = std::strtoll(val.c_str(), &pend, 10); + + if (pend == val.c_str() || errno == ERANGE) v = INT64_C(0); + *((int*)var.mData) = static_cast(v); + break; + } + default: + throw std::invalid_argument("Invalid Option Type."); + } + + mDataPair.emplace(name, std::move(var)); + } + +#pragma endregion + +#pragma region ExecEnvironment + + ExecEnvironment::ExecEnvironment() { + } + + ExecEnvironment::~ExecEnvironment() { + } + + void ExecEnvironment::ProcLoad(OptionsDescription& od, VariablesMap& vm) { + } + + void ExecEnvironment::ProcInfo(OptionsDescription& od, VariablesMap& vm) { + } + + void ExecEnvironment::ProcClear(OptionsDescription& od, VariablesMap& vm) { + } + + void ExecEnvironment::ProcExportSql(OptionsDescription& od, VariablesMap& vm) { } #pragma endregion -#pragma region interactive cmd +#pragma region InteractiveCmd - InteractiveCmd::InteractiveCmd() { + InteractiveCmd::InteractiveCmd() : + mCmdDispatcher(), mExecEnv(), mVm(), mBlank() + { + // add load subcommand + CmdRegisteryEntry entryLoad; + entryLoad.mSubCmdDesc = "Load Virtools file."; + entryLoad.mOptDesc.AddOption("file", 'f', CmdArgType::STRING, "The loaded Virtools file."); + entryLoad.mOptDesc.AddPositionalOption("file"); + entryLoad.mOptDesc.AddOption("encoding", 'e', CmdArgType::STRING, "The encoding to decode Virtools string."); + entryLoad.mOptDesc.AddPositionalOption("encoding"); + entryLoad.mOptDesc.AddOption("temp", 't', CmdArgType::STRING, "The temp folder used by engine."); + entryLoad.mOptDesc.AddPositionalOption("temp"); + entryLoad.mBindProc = std::bind(&ExecEnvironment::ProcLoad, &this->mExecEnv, std::placeholders::_1, std::placeholders::_2); + mCmdDispatcher.emplace("load", std::move(entryLoad)); + + CmdRegisteryEntry entryInfo; + entryInfo.mSubCmdDesc = "Show loaded Virtools file header info."; + entryInfo.mBindProc = std::bind(&ExecEnvironment::ProcInfo, &this->mExecEnv, std::placeholders::_1, std::placeholders::_2); + mCmdDispatcher.emplace("info", std::move(entryInfo)); + + CmdRegisteryEntry entryClear; + entryClear.mSubCmdDesc = "Clear current loaded Virtools file."; + entryClear.mBindProc = std::bind(&ExecEnvironment::ProcClear, &this->mExecEnv, std::placeholders::_1, std::placeholders::_2); + mCmdDispatcher.emplace("clear", std::move(entryClear)); + + CmdRegisteryEntry entryExportSql; + entryExportSql.mSubCmdDesc = "Export loaded Virtools file as a SQList database file."; + entryExportSql.mOptDesc.AddOption("file", 'f', CmdArgType::STRING, "The exported SQL file."); + entryExportSql.mOptDesc.AddPositionalOption("file"); + entryExportSql.mBindProc = std::bind(&ExecEnvironment::ProcExportSql, &this->mExecEnv, std::placeholders::_1, std::placeholders::_2); + mCmdDispatcher.emplace("sql", std::move(entryExportSql)); } @@ -160,8 +278,93 @@ namespace Unvirt { } -#pragma endregion + void InteractiveCmd::CmdParser(const std::vector& args) { + FILE* f = stdout; + if (args.size() == 0) { + fputs(UNVIRT_TERMCOL_LIGHT_RED(("Error! Fail to get subcommand token.\n")), f); + PrintHelp(f); + return; + } + + auto arg = args.begin(); + auto subcmd = mCmdDispatcher.find(*arg); + if (subcmd == mCmdDispatcher.end()) { + fprintf(f, UNVIRT_TERMCOL_LIGHT_RED(("Error! No such subcommand \"%s\"! \n")), arg->c_str()); + PrintHelp(f); + return; + } + + // analyze options + ++arg; + auto& optsDesc = subcmd->second.mOptDesc; + mVm.Clear(); + int position_counter = 0; + while (true) { + if (arg == args.end()) break; + + const std::string& opt = *arg; + OptionDescription* optDesc; + if (opt.starts_with("--")) { + // long name + optDesc = optsDesc.GetDescByLongName(opt.substr(2)); + } else if (opt.starts_with("-")) { + // short name + if (opt.size() != 2u) { + // invalid short name + fprintf(f, UNVIRT_TERMCOL_LIGHT_RED(("Error! Invalid short name option \"%s\"! \n")), opt.c_str()); + optsDesc.PrintHelp(f); + return; + } + optsDesc.GetDescByShortName(opt[1]); + } else { + // position + optDesc = optsDesc.GetDescByPosition(position_counter++); + } + + // invalid option + if (optDesc == nullptr) { + fprintf(f, UNVIRT_TERMCOL_LIGHT_RED(("Error! Invalid option \"%s\"! \n")), opt.c_str()); + optsDesc.PrintHelp(f); + return; + } + + // get value + switch (optDesc->mType) { + case CmdArgType::NONE: + // just a switch + mVm.AddPair(optDesc->mLongName, optDesc->mType, this->mBlank); + ++arg; + break; + case CmdArgType::INT: + case CmdArgType::STRING: + // check next value + ++arg; + if (arg == args.end()) { + fprintf(f, UNVIRT_TERMCOL_LIGHT_RED(("Error! Option \"%s\" lost parameter! \n")), opt.c_str()); + optsDesc.PrintHelp(f); + return; + } + mVm.AddPair(optDesc->mLongName, optDesc->mType, *arg); + ++arg; + break; + default: + throw std::invalid_argument("Invalid Option Type."); + } + } + + // execute proc + subcmd->second.mBindProc(optsDesc, mVm); + } + + void InteractiveCmd::PrintHelp(FILE* f) { + fputs(UNVIRT_TERMCOL_LIGHT_YELLOW(("Allowed Subcommands: \n")), f); + for (const auto& [key, value] : mCmdDispatcher) { + fprintf(f, "%s\t- %s\n", key.c_str(), value.mSubCmdDesc.c_str()); + } + } + +#pragma endregion } diff --git a/Unvirt/CmdHelper.hpp b/Unvirt/CmdHelper.hpp index e5e7519..efbcfa5 100644 --- a/Unvirt/CmdHelper.hpp +++ b/Unvirt/CmdHelper.hpp @@ -5,6 +5,7 @@ #include #include #include +#include namespace Unvirt { namespace CmdHelper { @@ -151,6 +152,10 @@ namespace Unvirt { void AddOption(const char* fullname, char shortname, CmdArgType type, const char* sescription); void AddPositionalOption(const char* corresponding_longname); + OptionDescription* GetDescByLongName(const std::string& longname); + OptionDescription* GetDescByShortName(char shortname); + OptionDescription* GetDescByPosition(int pos); + void PrintHelp(FILE* f); private: std::unordered_map mLongNameDict; @@ -159,7 +164,7 @@ namespace Unvirt { }; struct AnyVariable { - CmdArgType mType; + size_t mDataBasicSize; void* mData; }; @@ -169,35 +174,72 @@ namespace Unvirt { public: VariablesMap(); + VariablesMap(const VariablesMap&) = delete; + VariablesMap& operator=(const VariablesMap&) = delete; ~VariablesMap(); void Clear(void); + /// + /// Add option key value pair. + /// + /// + /// + /// + /// return false when this opt is existed. bool AddPair(std::string& name, CmdArgType t, std::string& val); - bool Contain(const char* probe); + bool Contain(const char* opt) { + if (opt == nullptr) throw std::invalid_argument("Invalid Option Name."); + return mDataPair.contains(opt); + } template T* GetData(const char* opt) { - ; + if (opt == nullptr) throw std::invalid_argument("Invalid Option Name."); + const auto search = mDataPair.find(opt); + if (search == mDataPair.end()) return nullptr; + + if (sizeof(T) > search->second.mDataBasicSize) throw std::invalid_argument("Memory Violation."); + return reinterpret_cast(search->second.mData); } }; struct CmdRegisteryEntry { + std::string mSubCmdDesc; OptionsDescription mOptDesc; std::function mBindProc; }; + class ExecEnvironment { + public: + ExecEnvironment(); + ExecEnvironment(const ExecEnvironment&) = delete; + ExecEnvironment& operator=(const ExecEnvironment&) = delete; + ~ExecEnvironment(); + + void ProcLoad(OptionsDescription&, VariablesMap&); + void ProcInfo(OptionsDescription&, VariablesMap&); + void ProcClear(OptionsDescription&, VariablesMap&); + void ProcExportSql(OptionsDescription&, VariablesMap&); + private: + + }; + class InteractiveCmd { public: InteractiveCmd(); + InteractiveCmd(const InteractiveCmd&) = delete; + InteractiveCmd& operator=(const InteractiveCmd&) = delete; ~InteractiveCmd(); void Run(void); private: - bool AnalyzeCmd(std::vector& args); + void CmdParser(const std::vector& args); void PrintHelp(FILE* f); + std::unordered_map mCmdDispatcher; - - + ExecEnvironment mExecEnv; + VariablesMap mVm; + std::string mBlank; }; }