2023-02-13 10:57:10 +08:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <unordered_map>
|
|
|
|
#include <string>
|
|
|
|
#include <functional>
|
|
|
|
#include <vector>
|
2023-02-13 16:52:00 +08:00
|
|
|
#include <unordered_map>
|
2023-02-13 10:57:10 +08:00
|
|
|
|
|
|
|
namespace Unvirt {
|
|
|
|
namespace CmdHelper {
|
|
|
|
|
|
|
|
class CmdSplitter {
|
|
|
|
public:
|
|
|
|
CmdSplitter();
|
|
|
|
CmdSplitter(const CmdSplitter&) = delete;
|
|
|
|
CmdSplitter& operator=(const CmdSplitter&) = delete;
|
|
|
|
~CmdSplitter();
|
|
|
|
|
2023-02-13 16:52:00 +08:00
|
|
|
std::vector<std::string> Convert(const std::string& u8cmd);
|
2023-02-13 10:57:10 +08:00
|
|
|
private:
|
|
|
|
char mCmdChar;
|
|
|
|
std::string* mBuffer;
|
|
|
|
std::vector<std::string>* mResult;
|
|
|
|
|
|
|
|
enum class StateType : int {
|
|
|
|
SPACE,
|
|
|
|
SINGLE,
|
|
|
|
DOUBLE,
|
|
|
|
ESCAPE,
|
|
|
|
NORMAL
|
|
|
|
};
|
|
|
|
StateType mState, mPreState;
|
|
|
|
|
|
|
|
inline void ProcSpace(void) {
|
|
|
|
switch (mCmdChar) {
|
|
|
|
case '\'':
|
|
|
|
mState = StateType::SINGLE;
|
|
|
|
break;
|
|
|
|
case '"':
|
|
|
|
mState = StateType::DOUBLE;
|
|
|
|
break;
|
|
|
|
case '\\':
|
|
|
|
mState = StateType::ESCAPE;
|
|
|
|
mPreState = StateType::NORMAL;
|
|
|
|
break;
|
|
|
|
case ' ':
|
|
|
|
break; // skip blank
|
|
|
|
default:
|
|
|
|
mBuffer->push_back(mCmdChar);
|
|
|
|
mState = StateType::NORMAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
inline void ProcSingle(void) {
|
|
|
|
switch (mCmdChar) {
|
|
|
|
case '\'':
|
|
|
|
mState = StateType::NORMAL;
|
|
|
|
break;
|
|
|
|
case '"':
|
|
|
|
mBuffer->push_back('"');
|
|
|
|
break;
|
|
|
|
case '\\':
|
|
|
|
mState = StateType::ESCAPE;
|
|
|
|
mPreState = StateType::SINGLE;
|
|
|
|
break;
|
|
|
|
case ' ':
|
|
|
|
mBuffer->push_back(' ');
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
mBuffer->push_back(mCmdChar);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
inline void ProcDouble(void) {
|
|
|
|
switch (mCmdChar) {
|
|
|
|
case '\'':
|
|
|
|
mBuffer->push_back('\'');
|
|
|
|
break;
|
|
|
|
case '"':
|
|
|
|
mState = StateType::NORMAL;
|
|
|
|
break;
|
|
|
|
case '\\':
|
|
|
|
mState = StateType::ESCAPE;
|
|
|
|
mPreState = StateType::DOUBLE;
|
|
|
|
break;
|
|
|
|
case ' ':
|
|
|
|
mBuffer->push_back(' ');
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
mBuffer->push_back(mCmdChar);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
inline void ProcEscape(void) {
|
|
|
|
// add itself
|
|
|
|
mBuffer->push_back(mCmdChar);
|
|
|
|
// restore state
|
|
|
|
mState = mPreState;
|
|
|
|
}
|
|
|
|
inline void ProcNormal(void) {
|
|
|
|
switch (mCmdChar) {
|
|
|
|
case '\'':
|
|
|
|
mBuffer->push_back('\'');
|
|
|
|
break;
|
|
|
|
case '"':
|
|
|
|
mBuffer->push_back('"');
|
|
|
|
break;
|
|
|
|
case '\\':
|
|
|
|
mState = StateType::ESCAPE;
|
|
|
|
mPreState = StateType::NORMAL;
|
|
|
|
break;
|
|
|
|
case ' ':
|
|
|
|
mResult->push_back(*mBuffer);
|
|
|
|
mBuffer->clear();
|
|
|
|
mState = StateType::SPACE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
mBuffer->push_back(mCmdChar);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-02-13 16:52:00 +08:00
|
|
|
enum class CmdArgType {
|
|
|
|
NONE,
|
|
|
|
STRING,
|
|
|
|
INT
|
|
|
|
};
|
|
|
|
|
|
|
|
struct OptionDescription {
|
|
|
|
std::string mLongName;
|
|
|
|
char mShortName;
|
|
|
|
CmdArgType mType;
|
|
|
|
std::string mDescription;
|
|
|
|
};
|
|
|
|
|
|
|
|
class OptionsDescription {
|
2023-02-13 10:57:10 +08:00
|
|
|
public:
|
2023-02-13 16:52:00 +08:00
|
|
|
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);
|
|
|
|
|
|
|
|
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 {
|
|
|
|
CmdArgType mType;
|
|
|
|
void* mData;
|
|
|
|
};
|
2023-02-13 10:57:10 +08:00
|
|
|
|
2023-02-13 16:52:00 +08:00
|
|
|
class VariablesMap {
|
2023-02-13 10:57:10 +08:00
|
|
|
private:
|
2023-02-13 16:52:00 +08:00
|
|
|
std::unordered_map<std::string, AnyVariable> mDataPair;
|
|
|
|
|
|
|
|
public:
|
|
|
|
VariablesMap();
|
|
|
|
~VariablesMap();
|
|
|
|
|
|
|
|
void Clear(void);
|
|
|
|
bool AddPair(std::string& name, CmdArgType t, std::string& val);
|
|
|
|
bool Contain(const char* probe);
|
|
|
|
template<typename T>
|
|
|
|
T* GetData(const char* opt) {
|
|
|
|
;
|
|
|
|
}
|
|
|
|
};
|
2023-02-13 10:57:10 +08:00
|
|
|
|
2023-02-13 16:52:00 +08:00
|
|
|
struct CmdRegisteryEntry {
|
|
|
|
OptionsDescription mOptDesc;
|
|
|
|
std::function<void(OptionsDescription&, VariablesMap&)> mBindProc;
|
2023-02-13 10:57:10 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
class InteractiveCmd {
|
|
|
|
public:
|
|
|
|
InteractiveCmd();
|
|
|
|
~InteractiveCmd();
|
|
|
|
|
|
|
|
void Run(void);
|
|
|
|
|
|
|
|
private:
|
2023-02-13 16:52:00 +08:00
|
|
|
bool AnalyzeCmd(std::vector<std::string>& args);
|
|
|
|
void PrintHelp(FILE* f);
|
|
|
|
std::unordered_map<std::string, CmdRegisteryEntry> mCmdDispatcher;
|
2023-02-13 10:57:10 +08:00
|
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|