diff --git a/LibCmo/CKMinContext.cpp b/LibCmo/CKMinContext.cpp index fdf9f4b..ec6597a 100644 --- a/LibCmo/CKMinContext.cpp +++ b/LibCmo/CKMinContext.cpp @@ -14,8 +14,7 @@ namespace LibCmo::CK2 { #pragma region Ctor Dtor CKMinContext::CKMinContext() : - m_NameEncoding(), m_NameEncodingToken(EncodingHelper::ENCODING_TOKEN_DEFAULT), - m_TempFolder(), + m_NameEncoding(), m_TempFolder(), m_PrintCallback(nullptr), m_CKObjectMaxID(0u), // register CKObjects @@ -106,6 +105,15 @@ namespace LibCmo::CK2 { } } + void CKMinContext::ClearCKObject(void) { + // free all created objects + for (const auto& [key, value] : this->m_ObjectsList) { + delete value; + } + // clear list + this->m_ObjectsList.clear(); + } + CK_ID CKMinContext::GetObjectMaxID(void) { return this->m_CKObjectMaxID; } @@ -122,17 +130,45 @@ namespace LibCmo::CK2 { #pragma region Misc Funcs - void CKMinContext::GetUtf8String(std::string& native_name, std::string& u8_name) { - EncodingHelper::GetUtf8VirtoolsName(native_name, u8_name, this->m_NameEncodingToken); + void CKMinContext::GetUtf8String(const std::string& native_name, std::string& u8_name) { + bool success = false; + for (const auto& token : this->m_NameEncoding) { + success = LibCmo::EncodingHelper::GetUtf8VirtoolsName(native_name, u8_name, token); + if (success) break; + } + + // fallback + if (!success) { + u8_name = native_name; + this->Printf("Error when converting to UTF8 string."); + } } - void CKMinContext::GetNativeString(std::string& u8_name, std::string& native_name) { - EncodingHelper::GetNativeVirtoolsName(u8_name, native_name, this->m_NameEncodingToken); + void CKMinContext::GetNativeString(const std::string& u8_name, std::string& native_name) { + bool success = false; + for (const auto& token : this->m_NameEncoding) { + success = LibCmo::EncodingHelper::GetNativeVirtoolsName(u8_name, native_name, token); + if (success) break; + } + + // fallback + if (!success) { + native_name = u8_name; + this->Printf("Error when converting to native string."); + } } - void CKMinContext::SetEncoding(CKSTRING encoding) { - this->m_NameEncoding = encoding; - this->RefetchEncodingToken(); + void CKMinContext::SetEncoding(const std::vector encoding_series) { + // free all current series + for (const auto& encoding : this->m_NameEncoding) { + LibCmo::EncodingHelper::DestroyEncodingToken(encoding); + } + this->m_NameEncoding.clear(); + + // add new encoding + for (const auto& encoding_str : encoding_series) { + this->m_NameEncoding.push_back(LibCmo::EncodingHelper::CreateEncodingToken(encoding_str)); + } } void CKMinContext::SetTempPath(CKSTRING u8_temp) { @@ -147,10 +183,6 @@ namespace LibCmo::CK2 { return EncodingHelper::OpenStdPathFile(realfile, is_read); } - void CKMinContext::RefetchEncodingToken(void) { - EncodingHelper::DestroyEncodingToken(this->m_NameEncodingToken); - this->m_NameEncodingToken = EncodingHelper::CreateEncodingToken(this->m_NameEncoding); - } #pragma endregion diff --git a/LibCmo/CKMinContext.hpp b/LibCmo/CKMinContext.hpp index 69c8a18..508e571 100644 --- a/LibCmo/CKMinContext.hpp +++ b/LibCmo/CKMinContext.hpp @@ -16,13 +16,14 @@ namespace LibCmo::CK2 { CKMinContext& operator=(const CKMinContext&) = delete; ~CKMinContext(); - using PrintCallback = void (*)(std::string&); + using PrintCallback = std::function; void Printf(CKSTRING fmt, ...); void SetPrintCallback(PrintCallback cb); CKObjectImplements::CKObject* CreateCKObject(CK_ID id, CK_CLASSID cls, CKSTRING name); CKObjectImplements::CKObject* GetCKObject(CK_ID id); void DestroyCKObject(CK_ID id); + void ClearCKObject(void); //CKManagerImplements::CKBaseManager* CreateCKManager(CKGUID guid); //CKManagerImplements::CKBaseManager* GetCKManager(CK_ID guid); @@ -31,17 +32,15 @@ namespace LibCmo::CK2 { CK_ID GetObjectMaxID(void); void SetObjectMaxID(CK_ID id); - void GetUtf8String(std::string& native_name, std::string& u8_name); - void GetNativeString(std::string& u8_name, std::string& native_name); + void GetUtf8String(const std::string& native_name, std::string& u8_name); + void GetNativeString(const std::string& u8_name, std::string& native_name); - void SetEncoding(CKSTRING encoding); + void SetEncoding(const std::vector encoding_series); void SetTempPath(CKSTRING u8_temp); FILE* OpenTempFile(CKSTRING u8_filename, bool is_read); private: - void RefetchEncodingToken(void); - std::map m_ObjectsList; std::map m_ManagersList; @@ -50,8 +49,7 @@ namespace LibCmo::CK2 { CK_ID m_CKObjectMaxID; - std::string m_NameEncoding; - EncodingHelper::ENCODING_TOKEN m_NameEncodingToken; + std::vector m_NameEncoding; std::filesystem::path m_TempFolder; PrintCallback m_PrintCallback; }; diff --git a/LibCmo/VTEncoding.cpp b/LibCmo/VTEncoding.cpp index 877f87b..a0e2baf 100644 --- a/LibCmo/VTEncoding.cpp +++ b/LibCmo/VTEncoding.cpp @@ -25,7 +25,7 @@ namespace LibCmo::EncodingHelper { } #undef LIBCMO_STR_EQUAL - bool WcharToChar(const wchar_t* src, std::string& dest, UINT codepage) { + bool WcharToChar(const wchar_t* src, std::string& dest, const UINT codepage) { int count, write_result; //converter to CHAR @@ -38,11 +38,11 @@ namespace LibCmo::EncodingHelper { return true; } - bool WcharToChar(std::wstring& src, std::string& dest, UINT codepage) { + bool WcharToChar(const std::wstring& src, std::string& dest, const UINT codepage) { return WcharToChar(src.c_str(), dest, codepage); } - bool CharToWchar(const char* src, std::wstring& dest, UINT codepage) { + bool CharToWchar(const char* src, std::wstring& dest, const UINT codepage) { int wcount, write_result; // convert to WCHAR @@ -55,17 +55,17 @@ namespace LibCmo::EncodingHelper { return true; } - bool CharToWchar(std::string& src, std::wstring& dest, UINT codepage) { + bool CharToWchar(const std::string& src, std::wstring& dest, const UINT codepage) { return CharToWchar(src.c_str(), dest, codepage); } - bool CharToChar(const char* src, std::string& dest, UINT src_codepage, UINT dest_codepage) { + bool CharToChar(const char* src, std::string& dest, const UINT src_codepage, const UINT dest_codepage) { std::wstring intermediary; if (!CharToWchar(src, intermediary, src_codepage)) return false; if (!WcharToChar(intermediary, dest, dest_codepage)) return false; return true; } - bool CharToChar(std::string& src, std::string& dest, UINT src_codepage, UINT dest_codepage) { + bool CharToChar(const std::string& src, std::string& dest, const UINT src_codepage, const UINT dest_codepage) { return CharToChar(src.c_str(), dest, src_codepage, dest_codepage); } @@ -144,7 +144,7 @@ namespace LibCmo::EncodingHelper { #if defined(LIBCMO_OS_WIN32) - ENCODING_TOKEN CreateEncodingToken(std::string& token_string) { + ENCODING_TOKEN CreateEncodingToken(const std::string& token_string) { ENCODING_TOKEN token = new(std::nothrow) UINT(); if (token == nullptr) return ENCODING_TOKEN_DEFAULT; @@ -153,34 +153,20 @@ namespace LibCmo::EncodingHelper { } return token; } - void DestroyEncodingToken(ENCODING_TOKEN token) { + void DestroyEncodingToken(const ENCODING_TOKEN& token) { if (token != ENCODING_TOKEN_DEFAULT) { delete token; } } - void GetUtf8VirtoolsName(std::string& native_name, std::string& u8_name, ENCODING_TOKEN token) { - if (token == ENCODING_TOKEN_DEFAULT) { - u8_name = native_name.c_str(); - return; - } - - // convert with fallback - if (!CharToChar(native_name, u8_name, *token, CP_UTF8)) { - u8_name = native_name.c_str(); - } + bool GetUtf8VirtoolsName(const std::string& native_name, std::string& u8_name, const ENCODING_TOKEN& token) { + if (token == ENCODING_TOKEN_DEFAULT) return false; + return CharToChar(native_name, u8_name, *token, CP_UTF8); } - void GetNativeVirtoolsName(std::string& u8_name, std::string& native_name, ENCODING_TOKEN token) { - if (token == ENCODING_TOKEN_DEFAULT) { - native_name = u8_name.c_str(); - return; - } - - // convert with fallback - if (!CharToChar(u8_name, native_name, CP_UTF8, *token)) { - native_name = u8_name.c_str(); - } + bool GetNativeVirtoolsName(const std::string& u8_name, std::string& native_name, const ENCODING_TOKEN& token) { + if (token == ENCODING_TOKEN_DEFAULT) return false; + return CharToChar(u8_name, native_name, CP_UTF8, *token); } void SetStdPathFromU8Path(std::filesystem::path& stdpath, const char* u8_path) { diff --git a/LibCmo/VTEncoding.hpp b/LibCmo/VTEncoding.hpp index d4b486e..c92b718 100644 --- a/LibCmo/VTEncoding.hpp +++ b/LibCmo/VTEncoding.hpp @@ -19,14 +19,14 @@ namespace LibCmo::EncodingHelper { bool GetWindowsCodePage(const char* u8_encoding_spec, UINT* result); - bool WcharToChar(const wchar_t* src, std::string& dest, UINT codepage); - bool WcharToChar(std::wstring& src, std::string& dest, UINT codepage); + bool WcharToChar(const wchar_t* src, std::string& dest, const UINT codepage); + bool WcharToChar(const std::wstring& src, std::string& dest, const UINT codepage); - bool CharToWchar(const char* src, std::wstring& dest, UINT codepage); - bool CharToWchar(std::string& src, std::wstring& dest, UINT codepage); + bool CharToWchar(const char* src, std::wstring& dest, const UINT codepage); + bool CharToWchar(const std::string& src, std::wstring& dest, const UINT codepage); - bool CharToChar(const char* src, std::string& dest, UINT src_codepage, UINT dest_codepage); - bool CharToChar(std::string& src, std::string& dest, UINT src_codepage, UINT dest_codepage); + bool CharToChar(const char* src, std::string& dest, const UINT src_codepage, const UINT dest_codepage); + bool CharToChar(const std::string& src, std::string& dest, const UINT src_codepage, const UINT dest_codepage); #else @@ -52,11 +52,11 @@ namespace LibCmo::EncodingHelper { #endif - ENCODING_TOKEN CreateEncodingToken(std::string& token_string); - void DestroyEncodingToken(ENCODING_TOKEN token); + ENCODING_TOKEN CreateEncodingToken(const std::string& token_string); + void DestroyEncodingToken(const ENCODING_TOKEN& token); - void GetUtf8VirtoolsName(std::string& native_name, std::string& u8_name, ENCODING_TOKEN token); - void GetNativeVirtoolsName(std::string& u8_name, std::string& native_name, ENCODING_TOKEN token); + bool GetUtf8VirtoolsName(const std::string& native_name, std::string& u8_name, const ENCODING_TOKEN& token); + bool GetNativeVirtoolsName(const std::string& u8_name, std::string& native_name, const ENCODING_TOKEN& token); void SetStdPathFromU8Path(std::filesystem::path& stdpath, const char* u8_path); FILE* OpenStdPathFile(std::filesystem::path& u8_filepath, bool is_read); diff --git a/Unvirt/CmdHelper.cpp b/Unvirt/CmdHelper.cpp index afe74bc..a50dca0 100644 --- a/Unvirt/CmdHelper.cpp +++ b/Unvirt/CmdHelper.cpp @@ -9,17 +9,12 @@ #include #include #include - -/* TODO: -do not re-allocated ctx and file for each loading in future. -this will be implemented by free all objects within doc. - -split encoding and temp folder setting -and load command only have file name arg. -*/ +#include namespace Unvirt::CmdHelper { + static FILE* fout = stdout; + #pragma region CmdSplitter CmdSplitter::CmdSplitter() : @@ -42,7 +37,11 @@ namespace Unvirt::CmdHelper { // split for (auto it = u8cmd.begin(); it != u8cmd.end(); ++it) { mCmdChar = (*it); - if (!std::isprint(mCmdChar)) continue; // skip all invalid characters + + // skip all invalid characters, \0 and etc. + // mCmdChar >= 0 to ensure all non-ASCII UTF8 char can be accepted directly. + if (mCmdChar >= 0 && (!std::isprint(mCmdChar))) + continue; switch (mState) { case StateType::SPACE: @@ -135,20 +134,54 @@ namespace Unvirt::CmdHelper { #pragma endregion -#pragma region InteractiveCmd +#pragma region InteractiveCmd Misc InteractiveCmd::InteractiveCmd() : m_CmdSplitter(), m_PageLen(10), m_Ctx(nullptr), m_File(nullptr), m_Doc(nullptr) { + // create context and file + m_Ctx = new LibCmo::CK2::CKMinContext(); + m_File = new LibCmo::CK2::CKFile(m_Ctx); + + // bind callback + m_Ctx->SetPrintCallback(std::bind(&InteractiveCmd::PrintMinContextMsg, this, std::placeholders::_1)); + } InteractiveCmd::~InteractiveCmd() { + // delete doc if necessary if (m_Doc != nullptr) delete m_Doc; - if (m_File != nullptr) delete m_File; - if (m_Ctx != nullptr) delete m_Ctx; + // delete file and ctx + delete m_File; + delete m_Ctx; } + bool InteractiveCmd::HasOpenedFile(void) { + return m_Doc != nullptr; + } + + void InteractiveCmd::ClearDocument(void) { + if (m_Doc == nullptr) return; + + // clear doc + delete m_Doc; + m_Doc = nullptr; + + // clear all loaded objects + m_Ctx->ClearCKObject(); + } + + void InteractiveCmd::PrintMinContextMsg(const std::string& msg) { + fputs(UNVIRT_TERMCOL_LIGHT_YELLOW(("[CKMinContext] ")), fout); + fputs(msg.c_str(), fout); + fputc('\n', fout); + } + +#pragma endregion + +#pragma region InteractiveCmd Dispatch + void InteractiveCmd::Run(void) { std::string u8cmd; @@ -174,6 +207,8 @@ namespace Unvirt::CmdHelper { else if (subcmd == "info") this->ProcInfo(cmds); else if (subcmd == "ls") this->ProcLs(cmds); else if (subcmd == "items") this->ProcItems(cmds); + else if (subcmd == "encoding") this->ProcEncoding(cmds); + else if (subcmd == "temp") this->ProcTemp(cmds); else if (subcmd == "help") this->PrintHelp(); else if (subcmd == "exit") break; else { @@ -185,7 +220,7 @@ namespace Unvirt::CmdHelper { } void InteractiveCmd::GetCmdLine(std::string& u8cmd) { - fputs("Unvirt> ", stdout); + fputs("Unvirt> ", fout); #if defined(LIBCMO_OS_WIN32) std::wstring wcmd; std::getline(std::wcin, wcmd); @@ -195,45 +230,48 @@ namespace Unvirt::CmdHelper { #endif } - bool InteractiveCmd::HasOpenedFile(void) { - return (m_Ctx != nullptr || m_File != nullptr || m_Doc != nullptr); - } - void InteractiveCmd::PrintHelp(void) { - FILE* f = stdout; - fputs(UNVIRT_TERMCOL_LIGHT_YELLOW(("Allowed Subcommands: \n")), f); + fputs(UNVIRT_TERMCOL_LIGHT_YELLOW(("Allowed Subcommands: \n")), fout); - fputs("load\n", f); - fputs("\tDescription: Load a Virtools composition.\n", f); - fputs("\tSyntax: load [encoding] [temp path]\n", f); + fputs("load\n", fout); + fputs("\tDescription: Load a Virtools composition.\n", fout); + fputs("\tSyntax: load \n", fout); - fputs("unload\n", f); - fputs("\tDescription: Release loaded Virtools composition.\n", f); - fputs("\tSyntax: unload\n", f); + fputs("unload\n", fout); + fputs("\tDescription: Release loaded Virtools composition.\n", fout); + fputs("\tSyntax: unload\n", fout); - fputs("info\n", f); - fputs("\tDescription: Show the header info of loaded Virtools composition.\n", f); - fputs("\tSyntax: info\n", f); + fputs("info\n", fout); + fputs("\tDescription: Show the header info of loaded Virtools composition.\n", fout); + fputs("\tSyntax: info\n", fout); - fputs("ls\n", f); - fputs("\tDescription: List something about loaded Virtools composition.\n", f); - fputs("\tSyntax: ls [page]\n", f); + fputs("ls\n", fout); + fputs("\tDescription: List something about loaded Virtools composition.\n", fout); + fputs("\tSyntax: ls [page]\n", fout); - fputs("items\n", f); - fputs("\tDescription: Set up how many items should be listed in one page when using \"ls\" command.\n", f); - fputs("\tSyntax: items \n", f); + fputs("items\n", fout); + fputs("\tDescription: Set up how many items should be listed in one page when using \"ls\" command.\n", fout); + fputs("\tSyntax: items \n", fout); - fputs("exit\n", f); - fputs("\tDescription: Exit program\n", f); - fputs("\tSyntax: exit\n", f); + fputs("encoding\n", fout); + fputs("\tDescription: Set the encoding series for CKMinContext.\n", fout); + fputs("\tSyntax: encoding [encoding name1] [encoding name2] [encoding name3] ...\n", fout); + + fputs("temp\n", fout); + fputs("\tDescription: Set the Temp path for CKMinContext.\n", fout); + fputs("\tSyntax: temp \n", fout); + + fputs("exit\n", fout); + fputs("\tDescription: Exit program\n", fout); + fputs("\tSyntax: exit\n", fout); } void InteractiveCmd::PrintArgParseError(const std::deque& cmd, size_t pos) { if (pos >= cmd.size()) { - fprintf(stdout, UNVIRT_TERMCOL_LIGHT_RED(("Lost argument at position %zu.\n")), pos); + fprintf(fout, UNVIRT_TERMCOL_LIGHT_RED(("Lost argument at position %zu.\n")), pos); } else { - fprintf(stdout, UNVIRT_TERMCOL_LIGHT_RED(("Unexpected argument \"%s\".\n")), cmd[pos].c_str()); + fprintf(fout, UNVIRT_TERMCOL_LIGHT_RED(("Unexpected argument \"%s\".\n")), cmd[pos].c_str()); } // arg error always print help @@ -243,17 +281,17 @@ namespace Unvirt::CmdHelper { void InteractiveCmd::PrintCommonError(const char* u8_fmt, ...) { va_list argptr; va_start(argptr, u8_fmt); - std::fputs(UNVIRT_TERMCOLHDR_LIGHT_RED, stdout); - std::vfprintf(stdout, u8_fmt, argptr); - std::fputs(UNVIRT_TERMCOLTAIL, stdout); + std::fputs(UNVIRT_TERMCOLHDR_LIGHT_RED, fout); + std::vfprintf(fout, u8_fmt, argptr); + std::fputs(UNVIRT_TERMCOLTAIL, fout); va_end(argptr); - std::fputc('\n', stdout); + std::fputc('\n', fout); } #pragma endregion -#pragma region Command Processors +#pragma region InteractiveCmd Processors void InteractiveCmd::ProcLoad(const std::deque& cmd) { // check pre-requirement @@ -269,25 +307,8 @@ namespace Unvirt::CmdHelper { this->PrintArgParseError(cmd, pos); return; } - ++pos; - std::string encoding; - if (!ArgParser::ParseString(cmd, pos, encoding)) { - this->PrintArgParseError(cmd, pos); - return; - } - ++pos; - std::string temppath; - if (!ArgParser::ParseString(cmd, pos, temppath)) { - this->PrintArgParseError(cmd, pos); - return; - } // proc - m_Ctx = new LibCmo::CK2::CKMinContext(); - m_Ctx->SetEncoding(encoding.c_str()); - m_Ctx->SetTempPath(temppath.c_str()); - - m_File = new LibCmo::CK2::CKFile(m_Ctx); m_Doc = new LibCmo::CK2::CKFileDocument(); LibCmo::CK2::CKERROR err = m_File->DeepLoad(filepath.c_str(), &m_Doc); if (err != LibCmo::CK2::CKERROR::CKERR_OK) { @@ -297,13 +318,7 @@ namespace Unvirt::CmdHelper { Unvirt::AccessibleValue::GetCkErrorDescription(err).c_str() ); - if (m_Doc != nullptr) delete m_Doc; - if (m_File != nullptr) delete m_File; - if (m_Ctx != nullptr) delete m_Ctx; - - m_Doc = nullptr; - m_File = nullptr; - m_Ctx = nullptr; + this->ClearDocument(); } } @@ -315,13 +330,7 @@ namespace Unvirt::CmdHelper { } // free all - if (m_Doc != nullptr) delete m_Doc; - if (m_File != nullptr) delete m_File; - if (m_Ctx != nullptr) delete m_Ctx; - - m_Doc = nullptr; - m_File = nullptr; - m_Ctx = nullptr; + this->ClearDocument(); } void InteractiveCmd::ProcInfo(const std::deque& cmd) { @@ -356,10 +365,14 @@ namespace Unvirt::CmdHelper { } ++pos; int32_t gotten_page; - if (!ArgParser::ParseInt(cmd, pos, gotten_page) || gotten_page <= 0) { - gotten_page = 0; // asssume as zero + if (!ArgParser::ParseInt(cmd, pos, gotten_page)) { + gotten_page = 1; // asssume as 1 } - size_t page = static_cast(gotten_page); + if (gotten_page <= 0) { + this->PrintCommonError("Page out of range."); + return; + } + size_t page = static_cast(gotten_page) - 1; // show list if (sw == c_AllowedSwitches[0]) { @@ -396,383 +409,40 @@ namespace Unvirt::CmdHelper { m_PageLen = static_cast(count); } -#pragma endregion + void InteractiveCmd::ProcEncoding(const std::deque& cmd) { + // create list first + std::vector encoding_list; - - /* - -#pragma region OptionsDescription - - OptionsDescription::OptionsDescription() : - mLongNameDict(), mShortNameMapping(), mPositionalArgMapping() { - ; - } - OptionsDescription::~OptionsDescription() { ; } - - void OptionsDescription::AddOption( - const char* fullname, char shortname, CmdArgType type, const char* description) { - // pre-check - if (fullname == nullptr || - fullname[0] == '\0' || - fullname[0] == '-') - throw std::invalid_argument("Invalid Option Long Name."); - - // construct data - std::string sfullname(fullname); - OptionDescription data{ - fullname, shortname, type, (description != nullptr ? description : "") - }; - - // check requirement - if (mLongNameDict.contains(sfullname)) throw std::invalid_argument("Duplicated Option Long Name."); - if (shortname != '\0') - if (mShortNameMapping.contains(shortname)) throw std::invalid_argument("Duplicated Option Short Name."); - - // add them - mShortNameMapping.emplace(shortname, sfullname); - mLongNameDict.emplace(sfullname, std::move(data)); - } - - void OptionsDescription::AddPositionalOption(const char* corresponding_longname) { - // pre-check - if (corresponding_longname == nullptr) throw std::invalid_argument("Invalid Option Long Name."); - - // construct data - std::string fullname(corresponding_longname); - - // check requirement - if (!mLongNameDict.contains(fullname)) throw std::invalid_argument("No Matched Option."); - if (!mPositionalArgMapping.empty()) { - for (const auto& i : mPositionalArgMapping) { - if (i == fullname)throw std::invalid_argument("Duplicated Option."); - } + // get list item + size_t pos = 0u; + while (true) { + std::string pending; + if (!ArgParser::ParseString(cmd, pos, pending)) { + break; // no more encoding, break } - // set value - mPositionalArgMapping.push_back(std::move(fullname)); + // add and move to next + ++pos; + encoding_list.push_back(std::move(pending)); } - 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(size_t 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) { - fprintf(f, "--%s\t%s\n", value.mLongName.c_str(), value.mDescription.c_str()); - } - - if (!mPositionalArgMapping.empty()) { - fputs(UNVIRT_TERMCOL_LIGHT_YELLOW(("\nPositional Options: \n")), f); - for (const auto& i : mPositionalArgMapping) { - fprintf(f, "[%s] ", i.c_str()); - } - } + // apply list + this->m_Ctx->SetEncoding(encoding_list); + } + + void InteractiveCmd::ProcTemp(const std::deque& cmd) { + // check requirement + size_t pos = 0u; + std::string temppath; + if (!ArgParser::ParseString(cmd, pos, temppath)) { + this->PrintArgParseError(cmd, pos); + return; } + // assign + m_Ctx->SetTempPath(temppath.c_str()); + } #pragma endregion -#pragma region VariablesMap - - VariablesMap::VariablesMap() : mDataPair() { - ; - } - VariablesMap::~VariablesMap() { - this->Clear(); - } - - void VariablesMap::Clear(void) { - for (const auto& [key, value] : mDataPair) { - if (value.mData != nullptr) free(value.mData); - } - mDataPair.clear(); - } - - bool VariablesMap::AddPair(const std::string& name, CmdArgType t, const 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)); - return true; - } - -#pragma endregion - -#pragma region ExecEnvironment - - ExecEnvironment::ExecEnvironment() : - mVtFile(nullptr), mVtFileEnv(nullptr) { - ; - } - - ExecEnvironment::~ExecEnvironment() { - if (mVtFile != nullptr) delete mVtFile; - if (mVtFileEnv != nullptr) delete mVtFileEnv; - } - - void ExecEnvironment::ProcLoad(OptionsDescription& od, VariablesMap& vm) { - if (mVtFile != nullptr || mVtFileEnv != nullptr) { - printf(UNVIRT_TERMCOL_LIGHT_RED(("Please close current opened Vrtools file first.\n"))); - return; - } - - const char* filename = vm.GetData("file"); - if (filename == nullptr) { - printf(UNVIRT_TERMCOL_LIGHT_RED(("You should specify a file first.\n"))); - od.PrintHelp(stdout); - return; - } - - mVtFileEnv = new LibCmo::Utils::VirtoolsContext(); - const char* enc = vm.GetData("encoding"); - mVtFileEnv->NameEncoding = enc == nullptr ? "" : enc; - - mVtFile = new LibCmo::CKFile(*mVtFileEnv); - mVtFile->Load(filename, LibCmo::CK_LOAD_FLAGS::CK_LOAD_DEFAULT); - } - - void ExecEnvironment::ProcInfo(OptionsDescription& od, VariablesMap& vm) { - printf(UNVIRT_TERMCOL_LIGHT_RED(("Sorry. This feature is not supported now.\n"))); - } - - void ExecEnvironment::ProcClear(OptionsDescription& od, VariablesMap& vm) { - if (mVtFile == nullptr && mVtFileEnv == nullptr) { - printf(UNVIRT_TERMCOL_LIGHT_RED(("Virtools file already is empty.\n"))); - return; - } - - if (mVtFile != nullptr) delete mVtFile; - if (mVtFileEnv != nullptr) delete mVtFileEnv; - - mVtFile = nullptr; - mVtFileEnv = nullptr; - } - - void ExecEnvironment::ProcExportSql(OptionsDescription& od, VariablesMap& vm) { - printf(UNVIRT_TERMCOL_LIGHT_RED(("Sorry. This feature is not supported now.\n"))); - } - - -#pragma endregion - -#pragma region InteractiveCmd - - InteractiveCmd::InteractiveCmd() : - mCmdDispatcher(), mExecEnv(), mVm(), mBlank(), mExitRunFlag(false), mCmdSplitter() { - // add load subcommand - CmdRegisteryEntry entryLoad; - const std::string entryLoadName = "load"; - 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)); - - CmdRegisteryEntry entryExit; - entryExit.mSubCmdDesc = "Exit this interactive commander."; - entryExit.mBindProc = std::bind(&InteractiveCmd::ProcExit, this, std::placeholders::_1, std::placeholders::_2); - //mCmdDispatcher.emplace("exit", std::move(entryExit)); - - } - - InteractiveCmd::~InteractiveCmd() { - ; - } - - void InteractiveCmd::Run(void) { - std::string u8cmd; - - mExitRunFlag = false; - while (!mExitRunFlag) { - // get command - GetCmdLine(u8cmd); - - // split cmd and parse it - CmdParser(mCmdSplitter.Convert(u8cmd)); - } - } - - void InteractiveCmd::GetCmdLine(std::string& u8cmd) { -#if defined(LIBCMO_OS_WIN32) - std::wstring wcmd; - std::getline(std::wcin, wcmd); - LibCmo::Encoding::WcharToChar(wcmd, u8cmd, CP_UTF8); -#else - std::getline(std::cin, u8cmd); -#endif - } - - 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; - } - optDesc = 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 - bool add_success = true; - switch (optDesc->mType) { - case CmdArgType::NONE: - // just a switch - add_success = mVm.AddPair(optDesc->mLongName, optDesc->mType, this->mBlank); - ++arg; - if (!add_success) { - fprintf(f, UNVIRT_TERMCOL_LIGHT_RED(("Error! Duplicated option \"%s\"! \n")), opt.c_str()); - optsDesc.PrintHelp(f); - return; - } - 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; - } - add_success = mVm.AddPair(optDesc->mLongName, optDesc->mType, *arg); - ++arg; - if (!add_success) { - fprintf(f, UNVIRT_TERMCOL_LIGHT_RED(("Error! Duplicated option \"%s\"! \n")), opt.c_str()); - optsDesc.PrintHelp(f); - return; - } - 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()); - } - } - - void InteractiveCmd::ProcExit(OptionsDescription&, VariablesMap&) { - mExitRunFlag = true; - } - -#pragma endregion - - -*/ - } diff --git a/Unvirt/CmdHelper.hpp b/Unvirt/CmdHelper.hpp index 4133fb1..f923fc6 100644 --- a/Unvirt/CmdHelper.hpp +++ b/Unvirt/CmdHelper.hpp @@ -143,7 +143,6 @@ namespace Unvirt::CmdHelper { private: void GetCmdLine(std::string& u8cmd); - bool HasOpenedFile(void); void PrintHelp(void); void PrintArgParseError(const std::deque& cmd, size_t pos); void PrintCommonError(const char* u8_fmt, ...); @@ -153,6 +152,12 @@ namespace Unvirt::CmdHelper { void ProcInfo(const std::deque& cmd); void ProcLs(const std::deque& cmd); void ProcItems(const std::deque& cmd); + void ProcEncoding(const std::deque& cmd); + void ProcTemp(const std::deque& cmd); + + bool HasOpenedFile(void); + void ClearDocument(void); + void PrintMinContextMsg(const std::string& msg); CmdSplitter m_CmdSplitter; size_t m_PageLen; @@ -161,132 +166,4 @@ namespace Unvirt::CmdHelper { LibCmo::CK2::CKFileDocument* m_Doc; }; - - /* - - enum class CmdArgType { - NONE, - STRING, - INT - }; - - struct OptionDescription { - std::string mLongName; - char mShortName; - CmdArgType mType; - std::string mDescription; - }; - - class OptionsDescription { - public: - OptionsDescription(); - OptionsDescription(const OptionsDescription&) = delete; - OptionsDescription& operator=(const OptionsDescription&) = delete; - ~OptionsDescription(); - - /// - /// Add an option - /// - /// The long name of this option. Should NOT be blank or NULL. - /// A single char for the short name of this option. Leave ZERO to omit this. - /// The value type of this option. Set to CmdArgType::NONE to indicate this is a switch (no value). - /// The description of this option. This can be NULL. - 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(size_t pos); - - void PrintHelp(FILE* f); - private: - std::unordered_map mLongNameDict; - std::unordered_map mShortNameMapping; - std::vector mPositionalArgMapping; - }; - - struct AnyVariable { - size_t mDataBasicSize; - void* mData; - }; - - class VariablesMap { - private: - std::unordered_map mDataPair; - - 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(const std::string& name, CmdArgType t, const std::string& val); - 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: - LibCmo::CKFile* mVtFile; - LibCmo::Utils::VirtoolsContext* mVtFileEnv; - }; - - class InteractiveCmd { - public: - InteractiveCmd(); - InteractiveCmd(const InteractiveCmd&) = delete; - InteractiveCmd& operator=(const InteractiveCmd&) = delete; - ~InteractiveCmd(); - - private: - void GetCmdLine(std::string& u8cmd); - void CmdParser(const std::vector& args); - void PrintHelp(FILE* f); - - void ProcExit(OptionsDescription&, VariablesMap&); - - std::unordered_map mCmdDispatcher; - CmdSplitter mCmdSplitter; - ExecEnvironment mExecEnv; - VariablesMap mVm; - std::string mBlank; - bool mExitRunFlag; - }; - -*/ } \ No newline at end of file diff --git a/Unvirt/StructFormatter.cpp b/Unvirt/StructFormatter.cpp index 2ad56ab..5d57061 100644 --- a/Unvirt/StructFormatter.cpp +++ b/Unvirt/StructFormatter.cpp @@ -78,13 +78,14 @@ namespace Unvirt::StructFormatter { fullpage = fulllen / pageitems; // print header - fputs("Index\tCK ID\tCKObject\tCKStateChunk\tName\n", fout); + fputs("CK ID\tType\tCKObject\tCKStateChunk\tName\n", fout); // print body for (size_t counter = startpos; counter < fulllen && (counter - startpos) < pageitems; ++counter) { const auto& obj = ls[counter]; - fprintf(stdout, "%zu\t%" PRIu32 "\t", counter, obj.ObjectId); + fprintf(fout, "%" PRIu32 "\t", obj.ObjectId); + fprintf(fout, "%s\t", Unvirt::AccessibleValue::GetClassIdName(obj.ObjectCid).c_str()); PrintPointer(obj.ObjPtr); fputc('\t', fout); PrintPointer(obj.Data); @@ -93,7 +94,7 @@ namespace Unvirt::StructFormatter { fputc('\n', fout); } - fprintf(fout, "Page %zu of %zu\n", page, fullpage); + fprintf(fout, "Page %zu of %zu\n", page + 1, fullpage + 1); } void PrintManagerList(const LibCmo::CK2::XArray& ls, size_t page, size_t pageitems) { @@ -104,13 +105,12 @@ namespace Unvirt::StructFormatter { fullpage = fulllen / pageitems; // print header - fputs("Index\tCKGUID\tCKBaseManager\tCKStateChunk\n", fout); + fputs("CKGUID\tCKBaseManager\tCKStateChunk\n", fout); // print body for (size_t counter = startpos; counter < fulllen && (counter - startpos) < pageitems; ++counter) { const auto& mgr = ls[counter]; - fprintf(stdout, "%zu\t", counter); PrintCKGUID(mgr.Manager); fputc('\t', fout); PrintPointer(mgr.MgrPtr); @@ -119,7 +119,7 @@ namespace Unvirt::StructFormatter { fputc('\n', fout); } - fprintf(fout, "Page %zu of %zu\n", page, fullpage); + fprintf(fout, "Page %zu of %zu\n", page + 1, fullpage + 1); } }