update cmd. improve encoding

This commit is contained in:
yyc12345 2023-03-04 11:11:36 +08:00
parent e166b8ec5b
commit 79aaf6b1ea
7 changed files with 204 additions and 641 deletions

View File

@ -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<std::string> 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

View File

@ -16,13 +16,14 @@ namespace LibCmo::CK2 {
CKMinContext& operator=(const CKMinContext&) = delete;
~CKMinContext();
using PrintCallback = void (*)(std::string&);
using PrintCallback = std::function<void(const std::string&)>;
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<std::string> encoding_series);
void SetTempPath(CKSTRING u8_temp);
FILE* OpenTempFile(CKSTRING u8_filename, bool is_read);
private:
void RefetchEncodingToken(void);
std::map<CK_ID, CKObjectImplements::CKObject*> m_ObjectsList;
std::map<CK_ID, CKManagerImplements::CKBaseManager*> m_ManagersList;
@ -50,8 +49,7 @@ namespace LibCmo::CK2 {
CK_ID m_CKObjectMaxID;
std::string m_NameEncoding;
EncodingHelper::ENCODING_TOKEN m_NameEncodingToken;
std::vector<EncodingHelper::ENCODING_TOKEN> m_NameEncoding;
std::filesystem::path m_TempFolder;
PrintCallback m_PrintCallback;
};

View File

@ -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) {

View File

@ -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);

View File

@ -9,17 +9,12 @@
#include <iostream>
#include <cstdio>
#include <cstdarg>
/* 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 <functional>
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 <file path> [encoding] [temp path]\n", f);
fputs("load\n", fout);
fputs("\tDescription: Load a Virtools composition.\n", fout);
fputs("\tSyntax: load <file path>\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 <obj | mgr> [page]\n", f);
fputs("ls\n", fout);
fputs("\tDescription: List something about loaded Virtools composition.\n", fout);
fputs("\tSyntax: ls <obj | mgr> [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 <num>\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 <num>\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 <temp path>\n", fout);
fputs("exit\n", fout);
fputs("\tDescription: Exit program\n", fout);
fputs("\tSyntax: exit\n", fout);
}
void InteractiveCmd::PrintArgParseError(const std::deque<std::string>& 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<std::string>& 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<std::string>& 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<size_t>(gotten_page);
if (gotten_page <= 0) {
this->PrintCommonError("Page out of range.");
return;
}
size_t page = static_cast<size_t>(gotten_page) - 1;
// show list
if (sw == c_AllowedSwitches[0]) {
@ -396,383 +409,40 @@ namespace Unvirt::CmdHelper {
m_PageLen = static_cast<size_t>(count);
}
#pragma endregion
void InteractiveCmd::ProcEncoding(const std::deque<std::string>& cmd) {
// create list first
std::vector<std::string> 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<std::string>& 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<int>(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<char>("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<char>("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<std::string>& 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
*/
}

View File

@ -143,7 +143,6 @@ namespace Unvirt::CmdHelper {
private:
void GetCmdLine(std::string& u8cmd);
bool HasOpenedFile(void);
void PrintHelp(void);
void PrintArgParseError(const std::deque<std::string>& cmd, size_t pos);
void PrintCommonError(const char* u8_fmt, ...);
@ -153,6 +152,12 @@ namespace Unvirt::CmdHelper {
void ProcInfo(const std::deque<std::string>& cmd);
void ProcLs(const std::deque<std::string>& cmd);
void ProcItems(const std::deque<std::string>& cmd);
void ProcEncoding(const std::deque<std::string>& cmd);
void ProcTemp(const std::deque<std::string>& 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();
/// <summary>
/// Add an option
/// </summary>
/// <param name="fullname">The long name of this option. Should NOT be blank or NULL.</param>
/// <param name="shortname">A single char for the short name of this option. Leave ZERO to omit this.</param>
/// <param name="type">The value type of this option. Set to CmdArgType::NONE to indicate this is a switch (no value).</param>
/// <param name="sescription">The description of this option. This can be NULL.</param>
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<std::string, OptionDescription> mLongNameDict;
std::unordered_map<char, std::string> mShortNameMapping;
std::vector<std::string> mPositionalArgMapping;
};
struct AnyVariable {
size_t mDataBasicSize;
void* mData;
};
class VariablesMap {
private:
std::unordered_map<std::string, AnyVariable> mDataPair;
public:
VariablesMap();
VariablesMap(const VariablesMap&) = delete;
VariablesMap& operator=(const VariablesMap&) = delete;
~VariablesMap();
void Clear(void);
/// <summary>
/// Add option key value pair.
/// </summary>
/// <param name="name"></param>
/// <param name="t"></param>
/// <param name="val"></param>
/// <returns>return false when this opt is existed.</returns>
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<typename T>
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<T*>(search->second.mData);
}
};
struct CmdRegisteryEntry {
std::string mSubCmdDesc;
OptionsDescription mOptDesc;
std::function<void(OptionsDescription&, VariablesMap&)> 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<std::string>& args);
void PrintHelp(FILE* f);
void ProcExit(OptionsDescription&, VariablesMap&);
std::unordered_map<std::string, CmdRegisteryEntry> mCmdDispatcher;
CmdSplitter mCmdSplitter;
ExecEnvironment mExecEnv;
VariablesMap mVm;
std::string mBlank;
bool mExitRunFlag;
};
*/
}

View File

@ -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<LibCmo::CK2::CKFileManagerData>& 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);
}
}