6 changed files with 1323 additions and 0 deletions
@ -0,0 +1,617 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools |
||||
Copyright (C) 2011 Cyan Worlds, Inc. |
||||
|
||||
This program is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU General Public License |
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7 |
||||
|
||||
If you modify this Program, or any covered work, by linking or |
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, |
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent |
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK |
||||
(or a modified version of those libraries), |
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, |
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG |
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the |
||||
licensors of this Program grant you additional |
||||
permission to convey the resulting work. Corresponding Source for a |
||||
non-source form of such a combination shall include the source code for |
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered |
||||
work. |
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com |
||||
or by snail mail at: |
||||
Cyan Worlds, Inc. |
||||
14617 N Newport Hwy |
||||
Mead, WA 99021 |
||||
|
||||
*==LICENSE==*/ |
||||
|
||||
#include "plCmdParser.h" |
||||
|
||||
#include <vector> |
||||
|
||||
#define WHITESPACE " \"\t\r\n\x1A" |
||||
#define FLAGS "-/" |
||||
#define SEPARATORS "=:" |
||||
#define TOGGLES "+-" |
||||
#define ALL WHITESPACE FLAGS SEPARATORS TOGGLES |
||||
|
||||
#define hsCheckFlagBits(f,c,m) ((f & m)==c) |
||||
|
||||
|
||||
struct plCmdArgData |
||||
{ |
||||
plCmdArgDef def; |
||||
|
||||
union { |
||||
bool boolVal; |
||||
float floatVal; |
||||
int32_t intVal; |
||||
const char* strVal; |
||||
uint32_t uintVal; |
||||
} val; |
||||
|
||||
plString buffer; |
||||
size_t nameChars; |
||||
bool isSpecified; |
||||
}; |
||||
|
||||
struct plCmdTokenState { |
||||
size_t fPendingIndex; |
||||
size_t fUnflaggedIndex; |
||||
}; |
||||
|
||||
|
||||
class plCmdParserImpl |
||||
{ |
||||
protected: |
||||
plString fProgramName; |
||||
std::vector<plCmdArgData> fArgArray; |
||||
std::vector<uint32_t> fLookupArray; |
||||
std::vector<uint32_t> fUnflaggedArray; |
||||
uint32_t fRequiredCount; |
||||
CmdError fError; |
||||
|
||||
void SetDefaultValue(plCmdArgData& arg); |
||||
bool ProcessValue(plCmdTokenState* state, size_t index, const plString& str); |
||||
bool TokenizeFlags(plCmdTokenState* state, const plString& str); |
||||
bool LookupFlagged(plString& name, size_t* lastIndex, bool force=false) const; |
||||
|
||||
public: |
||||
plCmdParserImpl(const plCmdArgDef* defs, size_t defCount); |
||||
|
||||
bool Tokenize(plCmdTokenState* state, std::vector<plString>& strs); |
||||
const plCmdArgData* FindArgByName(const plString& name) const; |
||||
const plCmdArgData* FindArgById(size_t id) const; |
||||
bool CheckAllRequiredArguments(plCmdTokenState* state); |
||||
|
||||
const plString GetProgramName() const { return fProgramName; } |
||||
|
||||
CmdError GetError() const { return fError; } |
||||
}; |
||||
|
||||
|
||||
plCmdParserImpl::plCmdParserImpl(const plCmdArgDef* defs, size_t defCount) |
||||
{ |
||||
size_t loop; |
||||
fError = kCmdErrorSuccess; |
||||
|
||||
// Save the argument definitions
|
||||
size_t maxId = 0; |
||||
size_t unflaggedCount = 0; |
||||
|
||||
fArgArray.resize(defCount); |
||||
|
||||
for (loop = 0; loop < defCount; ++loop) { |
||||
plCmdArgDef def = defs[loop]; |
||||
|
||||
// Check whether this argument is flagged
|
||||
bool flagged = hsCheckFlagBits(def.flags, |
||||
kCmdArgFlagged, |
||||
kCmdArgMask); |
||||
|
||||
// Disallow names on unflagged arguments
|
||||
ASSERT(flagged || !def.name.IsEmpty()); |
||||
|
||||
// Store the argument data
|
||||
plCmdArgData& arg = fArgArray[loop]; |
||||
arg.def = def; |
||||
arg.buffer = ""; |
||||
arg.nameChars = def.name.GetSize(); |
||||
arg.isSpecified = false; |
||||
|
||||
SetDefaultValue(arg); |
||||
maxId = std::max(maxId, def.id); |
||||
|
||||
// Track the number of unflagged arguments
|
||||
if (!flagged) { |
||||
++unflaggedCount; |
||||
} |
||||
} |
||||
|
||||
|
||||
// Build the id lookup table
|
||||
size_t idTableSize = std::min(maxId + 1, defCount * 2); |
||||
fLookupArray.resize(idTableSize); |
||||
|
||||
for (loop = 0; loop < defCount; ++loop) { |
||||
if (defs[loop].id < idTableSize) { |
||||
fLookupArray[defs[loop].id] = loop; |
||||
} |
||||
} |
||||
|
||||
|
||||
// Build the unflagged array
|
||||
size_t unflaggedIndex = 0; |
||||
fUnflaggedArray.resize(unflaggedCount); |
||||
|
||||
for (loop = 0; loop < defCount; ++loop) { |
||||
bool req = hsCheckFlagBits(defs[loop].flags, |
||||
kCmdArgRequired, |
||||
kCmdArgMask); |
||||
|
||||
if (req) { |
||||
fUnflaggedArray[unflaggedIndex++] = loop; |
||||
} |
||||
} |
||||
|
||||
fRequiredCount = unflaggedIndex; |
||||
|
||||
for (loop = 0; loop < defCount; ++loop) { |
||||
bool flagged = hsCheckFlagBits(defs[loop].flags, |
||||
kCmdArgFlagged, |
||||
kCmdArgMask); |
||||
|
||||
bool req = hsCheckFlagBits(defs[loop].flags, |
||||
kCmdArgRequired, |
||||
kCmdArgMask); |
||||
|
||||
if (!flagged && !req) { |
||||
fUnflaggedArray[unflaggedIndex++] = loop; |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
void plCmdParserImpl::SetDefaultValue(plCmdArgData& arg) |
||||
{ |
||||
uint32_t argType = arg.def.flags & kCmdTypeMask; |
||||
|
||||
switch (argType) { |
||||
case kCmdTypeBool: |
||||
arg.val.boolVal = !hsCheckFlagBits(arg.def.flags, |
||||
kCmdBoolSet, |
||||
kCmdBoolMask); |
||||
break; |
||||
|
||||
case kCmdTypeInt: |
||||
arg.val.intVal = 0; |
||||
break; |
||||
|
||||
case kCmdTypeUint: |
||||
arg.val.uintVal = 0; |
||||
break; |
||||
|
||||
case kCmdTypeFloat: |
||||
arg.val.floatVal = 0.0f; |
||||
break; |
||||
|
||||
case kCmdTypeString: |
||||
arg.val.strVal = ""; |
||||
break; |
||||
|
||||
DEFAULT_FATAL(argType); |
||||
} |
||||
} |
||||
|
||||
|
||||
bool plCmdParserImpl::Tokenize(plCmdTokenState* state, std::vector<plString>& strs) |
||||
{ |
||||
bool result = true; |
||||
|
||||
for (auto it = strs.begin(); result && it != strs.end(); ++it) { |
||||
if (fProgramName.IsEmpty()) { |
||||
fProgramName = *it; |
||||
continue; |
||||
} |
||||
|
||||
// If the previous argument is awaiting a value, then use this token
|
||||
// as the value
|
||||
if (state->fPendingIndex != size_t(-1)) { |
||||
result = ProcessValue(state, state->fPendingIndex, *it); |
||||
state->fPendingIndex = size_t(-1); |
||||
continue; |
||||
} |
||||
|
||||
// Identify and process flagged parameters
|
||||
if ((*it).REMatch("[" FLAGS "].+") && TokenizeFlags(state, *it)) { |
||||
continue; |
||||
} |
||||
|
||||
// Process unflagged parameters
|
||||
if (state->fUnflaggedIndex < fUnflaggedArray.size()) { |
||||
result = ProcessValue(state, fUnflaggedArray[state->fUnflaggedIndex++], *it); |
||||
continue; |
||||
} |
||||
|
||||
// Process invalid parameters
|
||||
if (!fError) { |
||||
fError = kCmdErrorTooManyArgs; |
||||
} |
||||
result = false; |
||||
break; |
||||
} |
||||
|
||||
return result; |
||||
} |
||||
|
||||
|
||||
bool plCmdParserImpl::ProcessValue(plCmdTokenState* state, size_t index, const plString& str) |
||||
{ |
||||
plCmdArgData& arg = fArgArray[index]; |
||||
arg.isSpecified = true; |
||||
uint32_t argType = arg.def.flags & kCmdTypeMask; |
||||
|
||||
switch (argType) { |
||||
case kCmdTypeBool: |
||||
if (str.CompareI("true") == 0) |
||||
arg.val.boolVal = true; |
||||
else if (str.CompareI("false") == 0) |
||||
arg.val.boolVal = false; |
||||
else if (str.IsEmpty()) |
||||
arg.val.boolVal = hsCheckFlagBits(arg.def.flags, |
||||
kCmdBoolSet, |
||||
kCmdBoolMask); |
||||
else |
||||
fError = kCmdErrorInvalidValue; |
||||
break; |
||||
|
||||
case kCmdTypeFloat: |
||||
arg.val.floatVal = str.ToFloat(); |
||||
break; |
||||
|
||||
case kCmdTypeInt: |
||||
arg.val.intVal = str.ToInt(); |
||||
break; |
||||
|
||||
case kCmdTypeString: |
||||
arg.buffer = str; |
||||
arg.val.strVal = arg.buffer.c_str(); |
||||
break; |
||||
|
||||
case kCmdTypeUint: |
||||
arg.val.uintVal = str.ToUInt(10); |
||||
break; |
||||
|
||||
DEFAULT_FATAL(argType); |
||||
|
||||
} |
||||
return true; |
||||
} |
||||
|
||||
bool plCmdParserImpl::TokenizeFlags(plCmdTokenState* state, const plString& str) |
||||
{ |
||||
bool result = true; |
||||
std::vector<plString> tokens = str.Tokenize(ALL); |
||||
|
||||
for (auto it = tokens.begin(); result && it != tokens.end(); ++it) { |
||||
size_t lastIndex = size_t(-1); |
||||
plString buffer = *it; |
||||
|
||||
if (buffer.IsEmpty()) { |
||||
continue; |
||||
} |
||||
|
||||
while (result) { |
||||
// Lookup the argument name
|
||||
result = LookupFlagged(buffer, &lastIndex); |
||||
if (!result) { |
||||
fError = kCmdErrorInvalidArg; |
||||
result = false; |
||||
} |
||||
|
||||
break; |
||||
} |
||||
|
||||
if (!result) { |
||||
break; |
||||
} |
||||
|
||||
// Check for an argument value provided using a separator
|
||||
if (str.REMatch(".+[" SEPARATORS "].+") && !(*(++it)).IsEmpty()) { |
||||
result = ProcessValue(state, lastIndex, *it); |
||||
break; |
||||
} |
||||
|
||||
bool isBool = hsCheckFlagBits(fArgArray[lastIndex].def.flags, |
||||
kCmdTypeBool, |
||||
kCmdTypeMask); |
||||
|
||||
// Process values for boolean arguments
|
||||
if (isBool) { |
||||
result = ProcessValue(state, lastIndex, plString::Null); |
||||
continue; |
||||
} |
||||
|
||||
// Process values for non-boolean arguments
|
||||
else { |
||||
// Check for an argument value immediately following the name
|
||||
if (!buffer.IsEmpty()) { |
||||
result = ProcessValue(state, lastIndex, buffer); |
||||
break; |
||||
} |
||||
|
||||
// Check for an argument value in the next token
|
||||
else { |
||||
state->fPendingIndex = lastIndex; |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
return result; |
||||
} |
||||
|
||||
bool plCmdParserImpl::LookupFlagged(plString& name, size_t* lastIndex, bool force) const |
||||
{ |
||||
size_t argCount = fArgArray.size(); |
||||
size_t chars = name.GetSize(); |
||||
size_t bestIndex = size_t(-1); |
||||
size_t bestChars = 0; |
||||
|
||||
|
||||
size_t prevChars = 0; |
||||
if (*lastIndex != size_t(-1)) { |
||||
prevChars = fArgArray[*lastIndex].def.name.GetSize(); |
||||
} |
||||
|
||||
for (; prevChars != size_t(-1) && !bestChars; --prevChars) { |
||||
// Find this argument in the list
|
||||
for (size_t index = 0; index < argCount; ++index) { |
||||
const plCmdArgData& arg = fArgArray[index]; |
||||
|
||||
// Ignore non-flagged arguments
|
||||
bool flagged = hsCheckFlagBits(arg.def.flags, |
||||
kCmdArgFlagged, |
||||
kCmdArgMask); |
||||
if (!flagged && !force) |
||||
continue; |
||||
|
||||
// Ignore this arg if it wouldn't beat the previous best match
|
||||
if (arg.def.name.GetSize() < bestChars + prevChars) |
||||
continue; |
||||
|
||||
// Ignore this argument if it doesn't match the prefix
|
||||
bool caseSensitive = hsCheckBits(arg.def.flags, |
||||
kCmdCaseSensitive); |
||||
|
||||
if (prevChars) { |
||||
const plCmdArgData& prev = fArgArray[*lastIndex]; |
||||
|
||||
if (prevChars >= arg.def.name.GetSize()) |
||||
continue; |
||||
|
||||
if (caseSensitive && |
||||
arg.def.name.CompareN(prev.def.name, prevChars)) |
||||
continue; |
||||
|
||||
if (!caseSensitive && |
||||
arg.def.name.CompareNI(prev.def.name, prevChars)) |
||||
continue; |
||||
} |
||||
|
||||
// Ignore this argument if it doesn't match the suffix
|
||||
plString suffix = arg.def.name.Substr(prevChars); |
||||
if (caseSensitive && suffix.CompareN(name, std::min(name.GetSize(), suffix.GetSize()))) |
||||
continue; |
||||
|
||||
if (!caseSensitive && suffix.CompareNI(name, std::min(name.GetSize(), suffix.GetSize()))) |
||||
continue; |
||||
|
||||
// Track the best match
|
||||
bestIndex = index; |
||||
bestChars = arg.def.name.GetSize() - prevChars; |
||||
if (bestChars == chars) |
||||
break; |
||||
} |
||||
} |
||||
|
||||
// Return the result
|
||||
name = name.Substr(bestChars); |
||||
*lastIndex = bestIndex; |
||||
return bestChars != 0; |
||||
} |
||||
|
||||
const plCmdArgData* plCmdParserImpl::FindArgByName(const plString& name) const |
||||
{ |
||||
// Search for an argument with this name
|
||||
size_t index = size_t(-1); |
||||
plString arg = name; |
||||
if (!LookupFlagged(arg, &index, true)) { |
||||
return nullptr; |
||||
} |
||||
|
||||
// Return the argument data
|
||||
return &fArgArray[index]; |
||||
} |
||||
|
||||
const plCmdArgData* plCmdParserImpl::FindArgById(size_t id) const |
||||
{ |
||||
// Search for the argument with this id
|
||||
size_t index; |
||||
if (id < fLookupArray.size()) { |
||||
index = fLookupArray[id]; |
||||
} else { |
||||
for (index = 0; index < fArgArray.size(); ++index) { |
||||
if (fArgArray[index].def.id == id) { |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Verify that we found the correct argument
|
||||
if ((index >= fArgArray.size()) || (fArgArray[index].def.id != id)) { |
||||
return nullptr; |
||||
} |
||||
|
||||
// Return the argument data
|
||||
return &fArgArray[index]; |
||||
} |
||||
|
||||
bool plCmdParserImpl::CheckAllRequiredArguments(plCmdTokenState* state) |
||||
{ |
||||
bool result = (state->fUnflaggedIndex >= fRequiredCount); |
||||
|
||||
if (!result) { |
||||
fError = kCmdErrorTooFewArgs; |
||||
} |
||||
|
||||
return result; |
||||
} |
||||
|
||||
|
||||
|
||||
plCmdParser::plCmdParser(const plCmdArgDef* defs, size_t defCount) |
||||
{ |
||||
Initialize(defs, defCount); |
||||
} |
||||
|
||||
plCmdParser::~plCmdParser() |
||||
{ |
||||
delete fParser; |
||||
} |
||||
|
||||
void plCmdParser::Initialize(const plCmdArgDef* defs, size_t defCount) |
||||
{ |
||||
fParser = new plCmdParserImpl(defs, defCount); |
||||
} |
||||
|
||||
bool plCmdParser::Parse(const plString& cmdLine) |
||||
{ |
||||
// Process the command line
|
||||
plCmdTokenState state = { |
||||
size_t(-1), // pending index
|
||||
0 // unflagged index
|
||||
}; |
||||
|
||||
std::vector<plString> tokens = cmdLine.Tokenize(WHITESPACE); |
||||
|
||||
bool result = fParser->Tokenize(&state, tokens); |
||||
|
||||
if (result) { |
||||
result = fParser->CheckAllRequiredArguments(&state); |
||||
} |
||||
|
||||
return result; |
||||
} |
||||
|
||||
bool plCmdParser::Parse(std::vector<plString>& argv) |
||||
{ |
||||
// Process the command line
|
||||
plCmdTokenState state = { |
||||
size_t(-1), // pending index
|
||||
0 // unflagged index
|
||||
}; |
||||
|
||||
bool result = fParser->Tokenize(&state, argv); |
||||
|
||||
if (result) { |
||||
result = fParser->CheckAllRequiredArguments(&state); |
||||
} |
||||
|
||||
return result; |
||||
} |
||||
|
||||
|
||||
const plString plCmdParser::GetProgramName() const |
||||
{ |
||||
return fParser->GetProgramName(); |
||||
} |
||||
|
||||
|
||||
bool plCmdParser::GetBool(size_t id) const |
||||
{ |
||||
return fParser->FindArgById(id)->val.boolVal; |
||||
} |
||||
|
||||
bool plCmdParser::GetBool(const plString& name) const |
||||
{ |
||||
return fParser->FindArgByName(name)->val.boolVal; |
||||
} |
||||
|
||||
float plCmdParser::GetFloat(size_t id) const |
||||
{ |
||||
return fParser->FindArgById(id)->val.floatVal; |
||||
} |
||||
|
||||
float plCmdParser::GetFloat(const plString& name) const |
||||
{ |
||||
return fParser->FindArgByName(name)->val.floatVal; |
||||
} |
||||
|
||||
int32_t plCmdParser::GetInt(size_t id) const |
||||
{ |
||||
return fParser->FindArgById(id)->val.intVal; |
||||
} |
||||
|
||||
int32_t plCmdParser::GetInt(const plString& name) const |
||||
{ |
||||
return fParser->FindArgByName(name)->val.intVal; |
||||
} |
||||
|
||||
const plString plCmdParser::GetString(size_t id) const |
||||
{ |
||||
return fParser->FindArgById(id)->val.strVal; |
||||
} |
||||
|
||||
const plString plCmdParser::GetString(const plString& name) const |
||||
{ |
||||
return fParser->FindArgByName(name)->val.strVal; |
||||
} |
||||
|
||||
uint32_t plCmdParser::GetUint(size_t id) const |
||||
{ |
||||
return fParser->FindArgById(id)->val.uintVal; |
||||
} |
||||
|
||||
uint32_t plCmdParser::GetUint(const plString& name) const |
||||
{ |
||||
return fParser->FindArgByName(name)->val.uintVal; |
||||
} |
||||
|
||||
bool plCmdParser::IsSpecified(size_t id) const |
||||
{ |
||||
if (const plCmdArgData* data = fParser->FindArgById(id)) { |
||||
return data->isSpecified; |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
bool plCmdParser::IsSpecified(const plString& name) const |
||||
{ |
||||
if (const plCmdArgData* data = fParser->FindArgByName(name)) { |
||||
return data->isSpecified; |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
CmdError plCmdParser::GetError() const |
||||
{ |
||||
return fParser->GetError(); |
||||
} |
@ -0,0 +1,136 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools |
||||
Copyright (C) 2011 Cyan Worlds, Inc. |
||||
|
||||
This program is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU General Public License |
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7 |
||||
|
||||
If you modify this Program, or any covered work, by linking or |
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, |
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent |
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK |
||||
(or a modified version of those libraries), |
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, |
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG |
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the |
||||
licensors of this Program grant you additional |
||||
permission to convey the resulting work. Corresponding Source for a |
||||
non-source form of such a combination shall include the source code for |
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered |
||||
work. |
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com |
||||
or by snail mail at: |
||||
Cyan Worlds, Inc. |
||||
14617 N Newport Hwy |
||||
Mead, WA 99021 |
||||
|
||||
*==LICENSE==*/ |
||||
|
||||
#ifndef _plCmdParser_h_ |
||||
#define _plCmdParser_h_ |
||||
|
||||
#include "plString.h" |
||||
|
||||
enum CmdArg |
||||
{ |
||||
kCmdArgFlagged = 0x0, // default
|
||||
kCmdArgOptional = 0x1, |
||||
kCmdArgRequired = 0x2, |
||||
kCmdArgMask = 0xF |
||||
}; |
||||
|
||||
enum CmdType |
||||
{ |
||||
kCmdTypeBool = 0x0 << 4, // default
|
||||
kCmdTypeInt = 0x1 << 4, |
||||
kCmdTypeUint = 0x2 << 4, |
||||
kCmdTypeFloat = 0x3 << 4, |
||||
kCmdTypeString = 0x4 << 4, |
||||
kCmdTypeMask = 0xF << 4 |
||||
}; |
||||
|
||||
enum CmdBool |
||||
{ |
||||
kCmdBoolSet = 0x0 << 8, // default
|
||||
kCmdBoolUnset = 0x1 << 8, |
||||
kCmdBoolMask = 0xF << 8 |
||||
}; |
||||
|
||||
enum CmdCase |
||||
{ |
||||
kCmdCaseSensitive = 0x1 << 28 |
||||
}; |
||||
|
||||
// Error codes
|
||||
enum CmdError |
||||
{ |
||||
kCmdErrorSuccess, |
||||
kCmdErrorInvalidArg, |
||||
kCmdErrorInvalidValue, |
||||
kCmdErrorTooFewArgs, |
||||
kCmdErrorTooManyArgs, |
||||
kNumCmdErrors |
||||
}; |
||||
|
||||
|
||||
struct plCmdArgDef |
||||
{ |
||||
uint32_t flags; |
||||
plString name; // must be compile-time constant
|
||||
size_t id; |
||||
}; |
||||
|
||||
|
||||
class plCmdParser |
||||
{ |
||||
protected: |
||||
class plCmdParserImpl* fParser; |
||||
|
||||
plCmdParser() : fParser(nullptr) { } |
||||
void Initialize(const plCmdArgDef* defs, size_t defCount); |
||||
|
||||
public: |
||||
plCmdParser(const plCmdArgDef* defs, size_t defCount); |
||||
virtual ~plCmdParser(); |
||||
|
||||
bool Parse(const plString& cmdLine); |
||||
bool Parse(std::vector<plString>& argv); |
||||
|
||||
const plString GetProgramName() const; |
||||
|
||||
bool GetBool(size_t id) const; |
||||
bool GetBool(const plString& name) const; |
||||
|
||||
float GetFloat(size_t id) const; |
||||
float GetFloat(const plString& name) const; |
||||
|
||||
int32_t GetInt(size_t id) const; |
||||
int32_t GetInt(const plString& name) const; |
||||
|
||||
const plString GetString(size_t id) const; |
||||
const plString GetString(const plString& name) const; |
||||
|
||||
uint32_t GetUint(size_t id) const; |
||||
uint32_t GetUint(const plString& name) const; |
||||
|
||||
bool IsSpecified(size_t id) const; |
||||
bool IsSpecified(const plString& name) const; |
||||
|
||||
CmdError GetError() const; |
||||
}; |
||||
|
||||
#endif //_plCmdParser_h_
|
@ -1 +1,2 @@
|
||||
add_subdirectory(pnEncryptionTest) |
||||
add_subdirectory(pnUtilsTest) |
||||
|
@ -0,0 +1,16 @@
|
||||
include_directories(${GTEST_INCLUDE_DIR}) |
||||
include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) |
||||
include_directories(../../../Plasma/CoreLib) |
||||
include_directories(../../../Plasma/NucleusLib) |
||||
|
||||
set(pnUtilsTest_SOURCES |
||||
test_plCmdParser.cpp |
||||
) |
||||
|
||||
add_executable(test_pnUtils ${pnUtilsTest_SOURCES}) |
||||
target_link_libraries(test_pnUtils gtest gtest_main) |
||||
target_link_libraries(test_pnUtils pnUtils) |
||||
|
||||
add_test(NAME test_pnUtils COMMAND test_pnUtils) |
||||
add_dependencies(check test_pnUtils) |
||||
|
@ -0,0 +1,547 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools |
||||
Copyright (C) 2011 Cyan Worlds, Inc. |
||||
|
||||
This program is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU General Public License |
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7 |
||||
|
||||
If you modify this Program, or any covered work, by linking or |
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, |
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent |
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK |
||||
(or a modified version of those libraries), |
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, |
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG |
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the |
||||
licensors of this Program grant you additional |
||||
permission to convey the resulting work. Corresponding Source for a |
||||
non-source form of such a combination shall include the source code for |
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered |
||||
work. |
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com |
||||
or by snail mail at: |
||||
Cyan Worlds, Inc. |
||||
14617 N Newport Hwy |
||||
Mead, WA 99021 |
||||
|
||||
*==LICENSE==*/ |
||||
|
||||
#include <cstring> |
||||
#include <gtest/gtest.h> |
||||
#include "pnUtils/plCmdParser.h" |
||||
|
||||
TEST(plCmdParser, basic_parsing) |
||||
{ |
||||
const plCmdArgDef cmds[] = { |
||||
{ kCmdArgRequired | kCmdTypeString, "path", 0} |
||||
}; |
||||
|
||||
plCmdParser parser(cmds, arrsize(cmds)); |
||||
bool success = parser.Parse("plCmdParser ~/.plasma/config.dat"); |
||||
|
||||
plString prog = parser.GetProgramName(); |
||||
plString path = parser.GetString(0); |
||||
|
||||
EXPECT_EQ(success, true); |
||||
EXPECT_STREQ(prog.c_str(), "plCmdParser"); |
||||
EXPECT_STREQ(path.c_str(), "~/.plasma/config.dat"); |
||||
EXPECT_EQ(parser.GetError(), kCmdErrorSuccess); |
||||
} |
||||
|
||||
TEST(plCmdParser, argv_parsing) |
||||
{ |
||||
const plCmdArgDef cmds[] = { |
||||
{ kCmdArgRequired | kCmdTypeString, "path", 0} |
||||
}; |
||||
|
||||
plCmdParser parser(cmds, arrsize(cmds)); |
||||
|
||||
const char* args[] = {"plCmdParser", "~/.plasma/config.dat"}; |
||||
int argc = 2; |
||||
|
||||
std::vector<plString> tokens(args, args+argc); |
||||
|
||||
bool success = parser.Parse(tokens); |
||||
|
||||
plString prog = parser.GetProgramName(); |
||||
plString path = parser.GetString(0); |
||||
|
||||
EXPECT_EQ(success, true); |
||||
EXPECT_STREQ(prog.c_str(), "plCmdParser"); |
||||
EXPECT_STREQ(path.c_str(), "~/.plasma/config.dat"); |
||||
EXPECT_EQ(parser.GetError(), kCmdErrorSuccess); |
||||
} |
||||
|
||||
TEST(plCmdParser, argv_preserving_spaces) |
||||
{ |
||||
const plCmdArgDef cmds[] = { |
||||
{ kCmdArgRequired | kCmdTypeString, "path", 0} |
||||
}; |
||||
|
||||
plCmdParser parser(cmds, arrsize(cmds)); |
||||
|
||||
const char* args[] = {"plCmdParser", "~/.plasma/Uru Live/config.dat"}; |
||||
int argc = 2; |
||||
|
||||
std::vector<plString> tokens(args, args+argc); |
||||
|
||||
bool success = parser.Parse(tokens); |
||||
|
||||
plString prog = parser.GetProgramName(); |
||||
plString path = parser.GetString(0); |
||||
|
||||
EXPECT_EQ(success, true); |
||||
EXPECT_STREQ(prog.c_str(), "plCmdParser"); |
||||
EXPECT_STREQ(path.c_str(), "~/.plasma/Uru Live/config.dat"); |
||||
EXPECT_EQ(parser.GetError(), kCmdErrorSuccess); |
||||
} |
||||
|
||||
TEST(plCmdParser, wchar_argv_parsing) |
||||
{ |
||||
const plCmdArgDef cmds[] = { |
||||
{ kCmdArgRequired | kCmdTypeString, "path", 0} |
||||
}; |
||||
|
||||
plCmdParser parser(cmds, arrsize(cmds)); |
||||
|
||||
const wchar_t* args[] = {L"plCmdParser", L"~/.plasma/config.dat"}; |
||||
int argc = 2; |
||||
|
||||
std::vector<plString> tokens(argc); |
||||
for (int i = 0; i < argc; i++) { |
||||
tokens.push_back(plString::FromWchar(args[i])); |
||||
} |
||||
|
||||
bool success = parser.Parse(tokens); |
||||
|
||||
plString prog = parser.GetProgramName(); |
||||
plString path = parser.GetString(0); |
||||
|
||||
EXPECT_EQ(success, true); |
||||
EXPECT_STREQ(prog.c_str(), "plCmdParser"); |
||||
EXPECT_STREQ(path.c_str(), "~/.plasma/config.dat"); |
||||
EXPECT_EQ(parser.GetError(), kCmdErrorSuccess); |
||||
} |
||||
|
||||
|
||||
TEST(plCmdParser, flagged_int) |
||||
{ |
||||
const plCmdArgDef cmds[] = { |
||||
{ kCmdTypeInt, "size", 0} |
||||
}; |
||||
|
||||
plCmdParser parser(cmds, arrsize(cmds)); |
||||
parser.Parse("plCmdParser --size 5"); |
||||
|
||||
int32_t size = parser.GetInt(0); |
||||
|
||||
EXPECT_EQ(size, 5); |
||||
} |
||||
|
||||
TEST(plCmdParser, flagged_int_short) |
||||
{ |
||||
const plCmdArgDef cmds[] = { |
||||
{ kCmdTypeInt, "size", 0} |
||||
}; |
||||
|
||||
plCmdParser parser(cmds, arrsize(cmds)); |
||||
parser.Parse("plCmdParser -s 5"); |
||||
|
||||
int32_t size = parser.GetInt(0); |
||||
|
||||
EXPECT_EQ(size, 5); |
||||
} |
||||
|
||||
TEST(plCmdParser, flagged_int_assign) |
||||
{ |
||||
const plCmdArgDef cmds[] = { |
||||
{ kCmdTypeInt, "size", 0} |
||||
}; |
||||
|
||||
plCmdParser parser(cmds, arrsize(cmds)); |
||||
parser.Parse("plCmdParser --size=5"); |
||||
|
||||
int32_t size = parser.GetInt(0); |
||||
|
||||
EXPECT_EQ(size, 5); |
||||
} |
||||
|
||||
TEST(plCmdParser, flagged_int_slash) |
||||
{ |
||||
const plCmdArgDef cmds[] = { |
||||
{ kCmdTypeInt, "size", 0} |
||||
}; |
||||
|
||||
plCmdParser parser(cmds, arrsize(cmds)); |
||||
parser.Parse("plCmdParser /size -5"); |
||||
|
||||
int32_t size = parser.GetInt(0); |
||||
|
||||
EXPECT_EQ(size, -5); |
||||
} |
||||
|
||||
TEST(plCmdParser, flagged_int_bystring) |
||||
{ |
||||
const plCmdArgDef cmds[] = { |
||||
{ kCmdTypeInt, "size", 0} |
||||
}; |
||||
|
||||
plCmdParser parser(cmds, arrsize(cmds)); |
||||
parser.Parse("plCmdParser --size 5"); |
||||
|
||||
int32_t size = parser.GetInt("size"); |
||||
|
||||
EXPECT_EQ(size, 5); |
||||
} |
||||
|
||||
|
||||
TEST(plCmdParser, flagged_uint) |
||||
{ |
||||
const plCmdArgDef cmds[] = { |
||||
{ kCmdTypeUint, "size", 0} |
||||
}; |
||||
|
||||
plCmdParser parser(cmds, arrsize(cmds)); |
||||
parser.Parse("plCmdParser --size 5"); |
||||
|
||||
uint32_t size = parser.GetUint(0); |
||||
|
||||
EXPECT_EQ(size, 5); |
||||
} |
||||
|
||||
TEST(plCmdParser, flagged_uint_bystring) |
||||
{ |
||||
const plCmdArgDef cmds[] = { |
||||
{ kCmdTypeUint, "size", 0} |
||||
}; |
||||
|
||||
plCmdParser parser(cmds, arrsize(cmds)); |
||||
parser.Parse("plCmdParser --size 5"); |
||||
|
||||
uint32_t size = parser.GetUint("size"); |
||||
|
||||
EXPECT_EQ(size, 5); |
||||
} |
||||
|
||||
|
||||
TEST(plCmdParser, flagged_float) |
||||
{ |
||||
const plCmdArgDef cmds[] = { |
||||
{ kCmdTypeFloat, "volume", 0} |
||||
}; |
||||
|
||||
plCmdParser parser(cmds, arrsize(cmds)); |
||||
parser.Parse("plCmdParser --volume 0.5"); |
||||
|
||||
float vol = parser.GetFloat(0); |
||||
|
||||
EXPECT_EQ(vol, 0.5); |
||||
} |
||||
|
||||
TEST(plCmdParser, flagged_float_bystring) |
||||
{ |
||||
const plCmdArgDef cmds[] = { |
||||
{ kCmdTypeFloat, "volume", 0} |
||||
}; |
||||
|
||||
plCmdParser parser(cmds, arrsize(cmds)); |
||||
parser.Parse("plCmdParser --volume 0.5"); |
||||
|
||||
float vol = parser.GetFloat("volume"); |
||||
|
||||
EXPECT_EQ(vol, 0.5); |
||||
} |
||||
|
||||
|
||||
TEST(plCmdParser, flagged_string) |
||||
{ |
||||
const plCmdArgDef cmds[] = { |
||||
{ kCmdTypeString, "path", 0} |
||||
}; |
||||
|
||||
plCmdParser parser(cmds, arrsize(cmds)); |
||||
parser.Parse("plCmdParser --path foo"); |
||||
|
||||
plString path = parser.GetString(0); |
||||
|
||||
EXPECT_STREQ(path.c_str(), "foo"); |
||||
} |
||||
|
||||
TEST(plCmdParser, flagged_string_bystring) |
||||
{ |
||||
const plCmdArgDef cmds[] = { |
||||
{ kCmdTypeString, "path", 0} |
||||
}; |
||||
|
||||
plCmdParser parser(cmds, arrsize(cmds)); |
||||
parser.Parse("plCmdParser --path foo"); |
||||
|
||||
plString path = parser.GetString("path"); |
||||
|
||||
EXPECT_STREQ(path.c_str(), "foo"); |
||||
} |
||||
|
||||
|
||||
TEST(plCmdParser, flagged_bool_default) |
||||
{ |
||||
const plCmdArgDef cmds[] = { |
||||
{ kCmdTypeBool, "verbose", 0} |
||||
}; |
||||
|
||||
plCmdParser parser(cmds, arrsize(cmds)); |
||||
parser.Parse("plCmdParser --verbose"); |
||||
|
||||
bool verbose = parser.GetBool(0); |
||||
|
||||
EXPECT_EQ(verbose, true); |
||||
} |
||||
|
||||
TEST(plCmdParser, flagged_bool_true) |
||||
{ |
||||
const plCmdArgDef cmds[] = { |
||||
{ kCmdTypeBool, "verbose", 0} |
||||
}; |
||||
|
||||
plCmdParser parser(cmds, arrsize(cmds)); |
||||
parser.Parse("plCmdParser --verbose=TRUE"); |
||||
|
||||
bool verbose = parser.GetBool(0); |
||||
|
||||
EXPECT_EQ(verbose, true); |
||||
} |
||||
|
||||
TEST(plCmdParser, flagged_bool_false) |
||||
{ |
||||
const plCmdArgDef cmds[] = { |
||||
{ kCmdTypeBool, "verbose", 0} |
||||
}; |
||||
|
||||
plCmdParser parser(cmds, arrsize(cmds)); |
||||
parser.Parse("plCmdParser --verbose=FALSE"); |
||||
|
||||
bool verbose = parser.GetBool(0); |
||||
|
||||
EXPECT_EQ(verbose, false); |
||||
} |
||||
|
||||
TEST(plCmdParser, flagged_bool_invalid) |
||||
{ |
||||
const plCmdArgDef cmds[] = { |
||||
{ kCmdTypeBool, "verbose", 0} |
||||
}; |
||||
|
||||
plCmdParser parser(cmds, arrsize(cmds)); |
||||
parser.Parse("plCmdParser --verbose=foo"); |
||||
|
||||
bool verbose = parser.GetBool(0); |
||||
|
||||
EXPECT_EQ(verbose, false); |
||||
EXPECT_EQ(parser.GetError(), kCmdErrorInvalidValue); |
||||
} |
||||
|
||||
TEST(plCmdParser, flagged_bool_bystring) |
||||
{ |
||||
const plCmdArgDef cmds[] = { |
||||
{ kCmdTypeBool, "verbose", 0} |
||||
}; |
||||
|
||||
plCmdParser parser(cmds, arrsize(cmds)); |
||||
parser.Parse("plCmdParser --verbose"); |
||||
|
||||
bool verbose = parser.GetBool("verbose"); |
||||
|
||||
EXPECT_EQ(verbose, true); |
||||
} |
||||
|
||||
|
||||
TEST(plCmdParser, optional_unspecified) |
||||
{ |
||||
const plCmdArgDef cmds[] = { |
||||
{ kCmdTypeInt | kCmdArgOptional, "speed", 0} |
||||
}; |
||||
|
||||
plCmdParser parser(cmds, arrsize(cmds)); |
||||
parser.Parse("plCmdParser"); |
||||
|
||||
bool specified = parser.IsSpecified(0); |
||||
int32_t speed = parser.GetInt(0); |
||||
|
||||
EXPECT_EQ(specified, false); |
||||
EXPECT_EQ(speed, 0); |
||||
} |
||||
|
||||
TEST(plCmdParser, optional_specified) |
||||
{ |
||||
const plCmdArgDef cmds[] = { |
||||
{ kCmdTypeInt | kCmdArgOptional, "speed", 0} |
||||
}; |
||||
|
||||
plCmdParser parser(cmds, arrsize(cmds)); |
||||
parser.Parse("plCmdParser 1"); |
||||
|
||||
bool specified = parser.IsSpecified(0); |
||||
int32_t speed = parser.GetInt(0); |
||||
|
||||
EXPECT_EQ(specified, true); |
||||
EXPECT_EQ(speed, 1); |
||||
} |
||||
|
||||
TEST(plCmdParser, optional_specified_bystring) |
||||
{ |
||||
const plCmdArgDef cmds[] = { |
||||
{ kCmdTypeInt | kCmdArgOptional, "speed", 0} |
||||
}; |
||||
|
||||
plCmdParser parser(cmds, arrsize(cmds)); |
||||
parser.Parse("plCmdParser 1"); |
||||
|
||||
bool specified = parser.IsSpecified("speed"); |
||||
int32_t speed = parser.GetInt("speed"); |
||||
|
||||
EXPECT_EQ(specified, true); |
||||
EXPECT_EQ(speed, 1); |
||||
} |
||||
|
||||
|
||||
TEST(plCmdParser, specified_invalid) |
||||
{ |
||||
const plCmdArgDef cmds[] = { |
||||
{ kCmdTypeInt | kCmdArgOptional, "speed", 0} |
||||
}; |
||||
|
||||
plCmdParser parser(cmds, arrsize(cmds)); |
||||
parser.Parse("plCmdParser 1"); |
||||
|
||||
bool specified = parser.IsSpecified(1); |
||||
|
||||
EXPECT_EQ(specified, false); |
||||
} |
||||
|
||||
TEST(plCmdParser, specified_invalid_bystring) |
||||
{ |
||||
const plCmdArgDef cmds[] = { |
||||
{ kCmdTypeInt | kCmdArgOptional, "speed", 0} |
||||
}; |
||||
|
||||
plCmdParser parser(cmds, arrsize(cmds)); |
||||
parser.Parse("plCmdParser 1"); |
||||
|
||||
bool specified = parser.IsSpecified("path"); |
||||
|
||||
EXPECT_EQ(specified, false); |
||||
} |
||||
|
||||
|
||||
TEST(plCmdParser, flagged_weird_id) |
||||
{ |
||||
const plCmdArgDef cmds[] = { |
||||
{ kCmdTypeInt, "size", 10} |
||||
}; |
||||
|
||||
plCmdParser parser(cmds, arrsize(cmds)); |
||||
parser.Parse("plCmdParser --size 5"); |
||||
|
||||
int32_t size = parser.GetInt(10); |
||||
|
||||
EXPECT_EQ(size, 5); |
||||
} |
||||
|
||||
|
||||
TEST(plCmdParser, fake_flag) |
||||
{ |
||||
const plCmdArgDef cmds[] = { |
||||
{ kCmdTypeInt, "size", 0} |
||||
}; |
||||
|
||||
plCmdParser parser(cmds, arrsize(cmds)); |
||||
bool success = parser.Parse("plCmdParser --speed 5"); |
||||
|
||||
EXPECT_EQ(success, false); |
||||
EXPECT_EQ(parser.GetError(), kCmdErrorInvalidArg); |
||||
} |
||||
|
||||
|
||||
TEST(plCmdParser, too_many_args) |
||||
{ |
||||
const plCmdArgDef cmds[] = { |
||||
{ kCmdTypeInt, "size", 0} |
||||
}; |
||||
|
||||
plCmdParser parser(cmds, arrsize(cmds)); |
||||
bool success = parser.Parse("plCmdParser --size 10 foo"); |
||||
|
||||
EXPECT_EQ(success, false); |
||||
EXPECT_EQ(parser.GetError(), kCmdErrorTooManyArgs); |
||||
} |
||||
|
||||
|
||||
TEST(plCmdParser, missing_required) |
||||
{ |
||||
const plCmdArgDef cmds[] = { |
||||
{ kCmdArgRequired | kCmdTypeString, "path", 0} |
||||
}; |
||||
|
||||
plCmdParser parser(cmds, arrsize(cmds)); |
||||
bool success = parser.Parse("plCmdParser"); |
||||
|
||||
EXPECT_EQ(success, false); |
||||
EXPECT_EQ(parser.GetError(), kCmdErrorTooFewArgs); |
||||
} |
||||
|
||||
|
||||
TEST(plCmdParser, combined_assign) |
||||
{ |
||||
const plCmdArgDef cmds[] = { |
||||
{ kCmdTypeInt, "size", 0} |
||||
}; |
||||
|
||||
plCmdParser parser(cmds, arrsize(cmds)); |
||||
parser.Parse("plCmdParser --size5"); |
||||
|
||||
int32_t size = parser.GetInt(0); |
||||
|
||||
EXPECT_EQ(size, 5); |
||||
} |
||||
|
||||
|
||||
TEST(plCmdParser, case_sensitive_nomatch) |
||||
{ |
||||
const plCmdArgDef cmds[] = { |
||||
{ kCmdTypeInt | kCmdCaseSensitive, "Size", 0} |
||||
}; |
||||
|
||||
plCmdParser parser(cmds, arrsize(cmds)); |
||||
parser.Parse("plCmdParser -s 5"); |
||||
|
||||
bool specified = parser.IsSpecified(0); |
||||
|
||||
EXPECT_EQ(specified, false); |
||||
} |
||||
|
||||
TEST(plCmdParser, case_sensitive_match) |
||||
{ |
||||
const plCmdArgDef cmds[] = { |
||||
{ kCmdTypeInt | kCmdCaseSensitive, "Size", 0} |
||||
}; |
||||
|
||||
plCmdParser parser(cmds, arrsize(cmds)); |
||||
parser.Parse("plCmdParser -S 5"); |
||||
|
||||
bool specified = parser.IsSpecified(0); |
||||
|
||||
EXPECT_EQ(specified, true); |
||||
} |
Loading…
Reference in new issue