|
|
|
/*==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==*/
|
|
|
|
/*****************************************************************************
|
|
|
|
*
|
|
|
|
* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtCmd.cpp
|
|
|
|
*
|
|
|
|
***/
|
|
|
|
|
|
|
|
#include "../Pch.h"
|
|
|
|
#pragma hdrstop
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
*
|
|
|
|
* Private
|
|
|
|
*
|
|
|
|
***/
|
|
|
|
|
|
|
|
#define WHITESPACE L" \"\t\r\n\x1A"
|
|
|
|
#define FLAGS L"-/"
|
|
|
|
#define SEPARATORS L"=:"
|
|
|
|
#define TOGGLES L"+-"
|
|
|
|
#define ALL WHITESPACE FLAGS SEPARATORS TOGGLES
|
|
|
|
|
|
|
|
static const unsigned kMaxTokenLength = MAX_PATH;
|
|
|
|
|
|
|
|
struct CmdArgData {
|
|
|
|
CmdArgDef def;
|
|
|
|
union {
|
|
|
|
bool boolVal;
|
|
|
|
float floatVal;
|
|
|
|
int intVal;
|
|
|
|
const wchar * strVal;
|
|
|
|
unsigned unsignedVal;
|
|
|
|
} val;
|
|
|
|
wchar * buffer;
|
|
|
|
unsigned nameChars;
|
|
|
|
bool isSpecified;
|
|
|
|
|
|
|
|
~CmdArgData () {
|
|
|
|
if (buffer)
|
|
|
|
FREE(buffer);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct CmdTokState {
|
|
|
|
CCmdParser * parser;
|
|
|
|
unsigned pendingIndex;
|
|
|
|
unsigned unflaggedIndex;
|
|
|
|
};
|
|
|
|
|
|
|
|
class CICmdParser {
|
|
|
|
private:
|
|
|
|
FARRAYOBJ(CmdArgData) m_argArray;
|
|
|
|
FARRAY(unsigned) m_idLookupArray;
|
|
|
|
unsigned m_requiredCount;
|
|
|
|
FARRAY(unsigned) m_unflaggedArray;
|
|
|
|
|
|
|
|
inline bool CheckFlag (unsigned flags, unsigned flag, unsigned mask) const;
|
|
|
|
void Error (const CmdTokState * state, ECmdError errorCode, const wchar arg[], const wchar value[]) const;
|
|
|
|
bool LookupFlagged (const wchar ** name, unsigned * lastIndex) const;
|
|
|
|
bool ProcessValue (CmdTokState * state, unsigned index, const wchar str[]);
|
|
|
|
void SetDefaultValue (CmdArgData & arg);
|
|
|
|
bool TokenizeFlags (CmdTokState * state, const wchar str[]);
|
|
|
|
|
|
|
|
public:
|
|
|
|
CICmdParser (const CmdArgDef def[], unsigned defCount);
|
|
|
|
bool CheckAllRequiredArguments (CmdTokState * state);
|
|
|
|
const CmdArgData * FindArgById (unsigned id) const;
|
|
|
|
const CmdArgData * FindArgByName (const wchar name[]) const;
|
|
|
|
bool Tokenize (CmdTokState * state, const wchar str[]);
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
*
|
|
|
|
* CICmdParser implementation
|
|
|
|
*
|
|
|
|
***/
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
bool CICmdParser::CheckFlag (unsigned flags, unsigned flag, unsigned mask) const {
|
|
|
|
return ((flags & mask) == flag);
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
bool CICmdParser::CheckAllRequiredArguments (CmdTokState * state) {
|
|
|
|
bool result = (state->unflaggedIndex >= m_requiredCount);
|
|
|
|
if (!result)
|
|
|
|
Error(state, kCmdErrorTooFewArgs, nil, nil);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
CICmdParser::CICmdParser (const CmdArgDef def[], unsigned defCount) {
|
|
|
|
unsigned loop;
|
|
|
|
|
|
|
|
// Save the argument definitions
|
|
|
|
unsigned maxId = 0;
|
|
|
|
unsigned unflaggedCount = 0;
|
|
|
|
m_argArray.SetCount(defCount);
|
|
|
|
for (loop = 0; loop < defCount; ++loop) {
|
|
|
|
|
|
|
|
// Check whether this argument is flagged
|
|
|
|
bool flagged = CheckFlag(def[loop].flags, kCmdArgFlagged, kCmdMaskArg);
|
|
|
|
|
|
|
|
// Disallow names on unflagged arguments
|
|
|
|
ASSERT(flagged || !def[loop].name);
|
|
|
|
|
|
|
|
// Store the argument data
|
|
|
|
CmdArgData & arg = m_argArray[loop];
|
|
|
|
arg.def = def[loop];
|
|
|
|
arg.buffer = nil;
|
|
|
|
arg.nameChars = def[loop].name ? StrLen(def[loop].name) : 0;
|
|
|
|
arg.isSpecified = false;
|
|
|
|
SetDefaultValue(arg);
|
|
|
|
maxId = max(maxId, def[loop].id);
|
|
|
|
|
|
|
|
// Track the number of unflagged arguments
|
|
|
|
if (!flagged)
|
|
|
|
++unflaggedCount;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Build the id lookup table
|
|
|
|
unsigned idTableSize = min(maxId + 1, defCount * 2);
|
|
|
|
m_idLookupArray.SetCount(idTableSize);
|
|
|
|
m_idLookupArray.Zero();
|
|
|
|
for (loop = 0; loop < defCount; ++loop)
|
|
|
|
if (def[loop].id < idTableSize)
|
|
|
|
m_idLookupArray[def[loop].id] = loop;
|
|
|
|
|
|
|
|
// Build the unflagged array
|
|
|
|
unsigned unflaggedIndex = 0;
|
|
|
|
m_unflaggedArray.SetCount(unflaggedCount);
|
|
|
|
for (loop = 0; loop < defCount; ++loop)
|
|
|
|
if (CheckFlag(def[loop].flags, kCmdArgRequired, kCmdMaskArg))
|
|
|
|
m_unflaggedArray[unflaggedIndex++] = loop;
|
|
|
|
m_requiredCount = unflaggedIndex;
|
|
|
|
for (loop = 0; loop < defCount; ++loop)
|
|
|
|
if (!(CheckFlag(def[loop].flags, kCmdArgFlagged, kCmdMaskArg) ||
|
|
|
|
CheckFlag(def[loop].flags, kCmdArgRequired, kCmdMaskArg)))
|
|
|
|
m_unflaggedArray[unflaggedIndex++] = loop;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
void CICmdParser::Error (const CmdTokState * state, ECmdError errorCode, const wchar arg[], const wchar value[]) const {
|
|
|
|
|
|
|
|
// Compose the error text
|
|
|
|
// (This text is only provided as a shortcut for trivial applications that
|
|
|
|
// don't want to compose their own text. Normally, an application would
|
|
|
|
// compose error text using its own localized strings.)
|
|
|
|
unsigned chars = 256 + (arg ? StrLen(arg) : 0) + (value ? StrLen(value) : 0);
|
|
|
|
wchar * buffer = (wchar *)ALLOC(chars * sizeof(wchar));
|
|
|
|
switch (errorCode) {
|
|
|
|
|
|
|
|
case kCmdErrorInvalidArg:
|
|
|
|
StrPrintf(buffer, chars, L"Invalid argument: %s", arg);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case kCmdErrorInvalidValue:
|
|
|
|
StrPrintf(buffer, chars, L"Argument %s invalid value: %s", arg, value);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case kCmdErrorTooFewArgs:
|
|
|
|
StrPrintf(buffer, chars, L"Too few arguments");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case kCmdErrorTooManyArgs:
|
|
|
|
StrPrintf(buffer, chars, L"Too many arguments: %s", arg);
|
|
|
|
break;
|
|
|
|
|
|
|
|
DEFAULT_FATAL(errorCode);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Call the error handler
|
|
|
|
state->parser->OnError(buffer, errorCode, arg, value);
|
|
|
|
|
|
|
|
// Free memory
|
|
|
|
FREE(buffer);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
const CmdArgData * CICmdParser::FindArgById (unsigned id) const {
|
|
|
|
|
|
|
|
// Search for the argument with this id
|
|
|
|
unsigned index;
|
|
|
|
if (id < m_idLookupArray.Count())
|
|
|
|
index = m_idLookupArray[id];
|
|
|
|
else
|
|
|
|
for (index = 0; index < m_argArray.Count(); ++index)
|
|
|
|
if (m_argArray[index].def.id == id)
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Verify that we found the correct argument
|
|
|
|
if ( (index >= m_argArray.Count()) ||
|
|
|
|
(m_argArray[index].def.id != id) )
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
// Return the argument data
|
|
|
|
return &m_argArray[index];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
const CmdArgData * CICmdParser::FindArgByName (const wchar name[]) const {
|
|
|
|
|
|
|
|
// Search for an argument with this name
|
|
|
|
unsigned index = (unsigned)-1;
|
|
|
|
if (!LookupFlagged(&name, &index))
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
// Return the argument data
|
|
|
|
return &m_argArray[index];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
bool CICmdParser::LookupFlagged (const wchar ** name, unsigned * lastIndex) const {
|
|
|
|
unsigned argCount = m_argArray.Count();
|
|
|
|
unsigned chars = StrLen(*name);
|
|
|
|
unsigned bestIndex = (unsigned)-1;
|
|
|
|
unsigned bestChars = 0;
|
|
|
|
|
|
|
|
// Check whether this argument is a suffix to any previously
|
|
|
|
// provided prefix in this token
|
|
|
|
for (unsigned prevChars = (*lastIndex != (unsigned)-1) ? m_argArray[*lastIndex].nameChars : 0;
|
|
|
|
(prevChars != (unsigned)-1) && !bestChars;
|
|
|
|
--prevChars) {
|
|
|
|
const CmdArgData & prev = prevChars ? m_argArray[*lastIndex] : *(const CmdArgData *)nil;
|
|
|
|
|
|
|
|
// Find this argument in the list
|
|
|
|
for (unsigned index = 0; index < argCount; ++index) {
|
|
|
|
const CmdArgData & arg = m_argArray[index];
|
|
|
|
|
|
|
|
// Ignore non-flagged arguments
|
|
|
|
if (!CheckFlag(arg.def.flags, kCmdArgFlagged, kCmdMaskArg))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Ignore this argument if it wouldn't beat the previous best match
|
|
|
|
if (arg.nameChars < bestChars + prevChars)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Ignore this argument if it doesn't match the prefix
|
|
|
|
bool caseSensitive = CheckFlag(arg.def.flags, kCmdCaseSensitive, kCmdCaseSensitive);
|
|
|
|
if ( prevChars &&
|
|
|
|
( (prevChars >= arg.nameChars) ||
|
|
|
|
( caseSensitive && StrCmp(arg.def.name, prev.def.name, prevChars)) ||
|
|
|
|
(!caseSensitive && StrCmpI(arg.def.name, prev.def.name, prevChars)) ) )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Ignore this argument if it doesn't match the suffix
|
|
|
|
if ( ( caseSensitive && StrCmp(*name, arg.def.name + prevChars, arg.nameChars - prevChars)) ||
|
|
|
|
(!caseSensitive && StrCmpI(*name, arg.def.name + prevChars, arg.nameChars - prevChars)) )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Track the best match
|
|
|
|
bestIndex = index;
|
|
|
|
bestChars = arg.nameChars - prevChars;
|
|
|
|
if (bestChars == chars)
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return the result
|
|
|
|
*name += bestChars;
|
|
|
|
*lastIndex = bestIndex;
|
|
|
|
return (bestChars != 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
bool CICmdParser::ProcessValue (CmdTokState * state, unsigned index, const wchar str[]) {
|
|
|
|
CmdArgData & arg = m_argArray[index];
|
|
|
|
arg.isSpecified = true;
|
|
|
|
unsigned argType = arg.def.flags & kCmdMaskType;
|
|
|
|
switch (argType) {
|
|
|
|
|
|
|
|
case kCmdTypeBool:
|
|
|
|
if (*str == '+')
|
|
|
|
arg.val.boolVal = true;
|
|
|
|
else if (*str == '-')
|
|
|
|
arg.val.boolVal = false;
|
|
|
|
else if (!*str)
|
|
|
|
arg.val.boolVal = CheckFlag(arg.def.flags, kCmdBoolSet, kCmdMaskBool);
|
|
|
|
else
|
|
|
|
Error(state, kCmdErrorInvalidValue, arg.def.name, str);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case kCmdTypeFloat:
|
|
|
|
{
|
|
|
|
const wchar * endPtr;
|
|
|
|
arg.val.floatVal = StrToFloat(str, &endPtr);
|
|
|
|
if (*endPtr)
|
|
|
|
Error(state, kCmdErrorInvalidValue, arg.def.name, str);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case kCmdTypeInt:
|
|
|
|
{
|
|
|
|
const wchar * endPtr;
|
|
|
|
arg.val.intVal = StrToInt(str, &endPtr);
|
|
|
|
if (*endPtr)
|
|
|
|
Error(state, kCmdErrorInvalidValue, arg.def.name, str);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case kCmdTypeString:
|
|
|
|
if (arg.buffer)
|
|
|
|
FREE(arg.buffer);
|
|
|
|
arg.buffer = StrDup(str);
|
|
|
|
arg.val.strVal = arg.buffer;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case kCmdTypeUnsigned:
|
|
|
|
{
|
|
|
|
const wchar * endPtr;
|
|
|
|
arg.val.unsignedVal = StrToUnsigned(str, &endPtr, 10);
|
|
|
|
if (*endPtr)
|
|
|
|
Error(state, kCmdErrorInvalidValue, arg.def.name, str);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
DEFAULT_FATAL(argType);
|
|
|
|
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
void CICmdParser::SetDefaultValue (CmdArgData & arg) {
|
|
|
|
unsigned argType = arg.def.flags & kCmdMaskType;
|
|
|
|
switch (argType) {
|
|
|
|
|
|
|
|
case kCmdTypeBool:
|
|
|
|
arg.val.boolVal = !CheckFlag(arg.def.flags, kCmdBoolSet, kCmdMaskBool);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case kCmdTypeInt:
|
|
|
|
arg.val.intVal = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case kCmdTypeUnsigned:
|
|
|
|
arg.val.unsignedVal = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case kCmdTypeFloat:
|
|
|
|
arg.val.floatVal = 0.0f;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case kCmdTypeString:
|
|
|
|
arg.val.strVal = L"";
|
|
|
|
break;
|
|
|
|
|
|
|
|
DEFAULT_FATAL(argType);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
bool CICmdParser::Tokenize (CmdTokState * state, const wchar str[]) {
|
|
|
|
wchar buffer[kMaxTokenLength];
|
|
|
|
bool result = true;
|
|
|
|
while (result && StrTokenize(&str, buffer, arrsize(buffer), WHITESPACE)) {
|
|
|
|
|
|
|
|
// If the previous argument is awaiting a value, then use this token
|
|
|
|
// as the value
|
|
|
|
if (state->pendingIndex != (unsigned)-1) {
|
|
|
|
result = ProcessValue(state, state->pendingIndex, buffer);
|
|
|
|
state->pendingIndex = (unsigned)-1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Identify and process flagged parameters
|
|
|
|
if (StrChr(FLAGS, buffer[0]) && TokenizeFlags(state, buffer))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Process unflagged parameters
|
|
|
|
if (state->unflaggedIndex < m_unflaggedArray.Count()) {
|
|
|
|
result = ProcessValue(state, m_unflaggedArray[state->unflaggedIndex++], buffer);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Process extra parameters
|
|
|
|
if (state->parser->OnExtra(buffer))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Process invalid parameters
|
|
|
|
Error(state, kCmdErrorTooManyArgs, buffer, nil);
|
|
|
|
result = false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
bool CICmdParser::TokenizeFlags (CmdTokState * state, const wchar str[]) {
|
|
|
|
|
|
|
|
// Process each separately flagged token within the string
|
|
|
|
wchar buffer[kMaxTokenLength];
|
|
|
|
bool result = true;
|
|
|
|
while (result && StrTokenize(&str, buffer, arrsize(buffer), ALL)) {
|
|
|
|
if (!buffer[0])
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Process each flag within the token
|
|
|
|
unsigned lastIndex = (unsigned)-1;
|
|
|
|
const wchar * bufferPtr = buffer;
|
|
|
|
while (result) {
|
|
|
|
|
|
|
|
// Lookup the argument name
|
|
|
|
result = LookupFlagged(&bufferPtr, &lastIndex);
|
|
|
|
if (!result) {
|
|
|
|
Error(state, kCmdErrorInvalidArg, bufferPtr, nil);
|
|
|
|
result = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If this argument is boolean, allow it to share a common prefix
|
|
|
|
// with the next argument. In this case there is no place for
|
|
|
|
// the user to provide a value, so use the default value.
|
|
|
|
if (*bufferPtr &&
|
|
|
|
CheckFlag(m_argArray[lastIndex].def.flags, kCmdTypeBool, kCmdMaskType)) {
|
|
|
|
result = ProcessValue(state, lastIndex, L"");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!result)
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Check for an argument value provided using a separator
|
|
|
|
if (*str && StrChr(SEPARATORS, *str)) {
|
|
|
|
result = ProcessValue(state, lastIndex, str + 1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Process values for boolean arguments
|
|
|
|
if (CheckFlag(m_argArray[lastIndex].def.flags, kCmdTypeBool, kCmdMaskType)) {
|
|
|
|
|
|
|
|
// Check for a value provided with a toggle
|
|
|
|
if (*str && StrChr(TOGGLES, *str)) {
|
|
|
|
wchar tempStr[] = {*str, 0};
|
|
|
|
result = ProcessValue(state, lastIndex, tempStr);
|
|
|
|
++str;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for a default value
|
|
|
|
else {
|
|
|
|
result = ProcessValue(state, lastIndex, L"");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Process values for non-boolean arguments
|
|
|
|
else {
|
|
|
|
|
|
|
|
// Check for an argument value immediately following the name
|
|
|
|
if (*bufferPtr) {
|
|
|
|
result = ProcessValue(state, lastIndex, bufferPtr);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for an argument value in the next token
|
|
|
|
else {
|
|
|
|
state->pendingIndex = lastIndex;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
*
|
|
|
|
* CCmdParser implementation
|
|
|
|
*
|
|
|
|
***/
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
CCmdParser::CCmdParser () {
|
|
|
|
fParser = nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
CCmdParser::CCmdParser (const CmdArgDef def[], unsigned defCount) {
|
|
|
|
Initialize(def, defCount);
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
CCmdParser::~CCmdParser () {
|
|
|
|
DEL(fParser);
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
bool CCmdParser::GetBool (unsigned id) const {
|
|
|
|
return fParser->FindArgById(id)->val.boolVal;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
bool CCmdParser::GetBool (const wchar name[]) const {
|
|
|
|
return fParser->FindArgByName(name)->val.boolVal;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
float CCmdParser::GetFloat (unsigned id) const {
|
|
|
|
return fParser->FindArgById(id)->val.floatVal;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
float CCmdParser::GetFloat (const wchar name[]) const {
|
|
|
|
return fParser->FindArgByName(name)->val.floatVal;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
int CCmdParser::GetInt (unsigned id) const {
|
|
|
|
return fParser->FindArgById(id)->val.intVal;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
int CCmdParser::GetInt (const wchar name[]) const {
|
|
|
|
return fParser->FindArgByName(name)->val.intVal;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
const wchar * CCmdParser::GetString (unsigned id) const {
|
|
|
|
return fParser->FindArgById(id)->val.strVal;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
const wchar * CCmdParser::GetString (const wchar name[]) const {
|
|
|
|
return fParser->FindArgByName(name)->val.strVal;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
unsigned CCmdParser::GetUnsigned (unsigned id) const {
|
|
|
|
return fParser->FindArgById(id)->val.unsignedVal;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
unsigned CCmdParser::GetUnsigned (const wchar name[]) const {
|
|
|
|
return fParser->FindArgByName(name)->val.unsignedVal;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
void CCmdParser::Initialize (const CmdArgDef def[], unsigned defCount) {
|
|
|
|
fParser = NEW(CICmdParser)(def, defCount);
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
bool CCmdParser::IsSpecified (unsigned id) const {
|
|
|
|
if (const CmdArgData * data = fParser->FindArgById(id))
|
|
|
|
return data->isSpecified;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
bool CCmdParser::IsSpecified (const wchar name[]) const {
|
|
|
|
if (const CmdArgData * data = fParser->FindArgByName(name))
|
|
|
|
return data->isSpecified;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
void CCmdParser::OnError (const wchar str[], ECmdError errorCode, const wchar arg[], const wchar value[]) {
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
bool CCmdParser::OnExtra (const wchar str[]) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
bool CCmdParser::Parse (const wchar cmdLine[]) {
|
|
|
|
// If no command line was passed, use the application's command line,
|
|
|
|
// skipping past the program name
|
|
|
|
if (!cmdLine) {
|
|
|
|
cmdLine = AppGetCommandLine();
|
|
|
|
StrTokenize(&cmdLine, nil, 0, WHITESPACE);
|
|
|
|
while (*cmdLine == L' ')
|
|
|
|
++cmdLine;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Process the command line
|
|
|
|
CmdTokState state = {
|
|
|
|
this,
|
|
|
|
(unsigned)-1, // pending index
|
|
|
|
0 // unflagged index
|
|
|
|
};
|
|
|
|
bool result;
|
|
|
|
result = fParser->Tokenize(&state, cmdLine);
|
|
|
|
if (result)
|
|
|
|
result = fParser->CheckAllRequiredArguments(&state);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
*
|
|
|
|
* CCmdParserSimple
|
|
|
|
*
|
|
|
|
***/
|
|
|
|
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
CCmdParserSimple::CCmdParserSimple (
|
|
|
|
unsigned requiredStringCount,
|
|
|
|
unsigned optionalStringCount,
|
|
|
|
const wchar flaggedBoolNames[] // double null terminated if used
|
|
|
|
) {
|
|
|
|
|
|
|
|
// Count the number of flagged arguments
|
|
|
|
unsigned flaggedBoolCount = 0;
|
|
|
|
const wchar * curr;
|
|
|
|
if (flaggedBoolNames)
|
|
|
|
for (curr = flaggedBoolNames; *curr; curr += StrLen(curr) + 1)
|
|
|
|
++flaggedBoolCount;
|
|
|
|
|
|
|
|
// Build the argument definition array
|
|
|
|
unsigned totalCount = requiredStringCount + optionalStringCount + flaggedBoolCount;
|
|
|
|
FARRAY(CmdArgDef) argDef(totalCount);
|
|
|
|
unsigned index = 0;
|
|
|
|
for (; index < requiredStringCount; ++index) {
|
|
|
|
argDef[index].flags = kCmdArgRequired | kCmdTypeString;
|
|
|
|
argDef[index].name = nil;
|
|
|
|
argDef[index].id = index + 1;
|
|
|
|
}
|
|
|
|
for (; index < requiredStringCount + optionalStringCount; ++index) {
|
|
|
|
argDef[index].flags = kCmdArgOptional | kCmdTypeString;
|
|
|
|
argDef[index].name = nil;
|
|
|
|
argDef[index].id = index + 1;
|
|
|
|
}
|
|
|
|
for (curr = flaggedBoolNames; index < totalCount; ++index) {
|
|
|
|
argDef[index].flags = kCmdArgFlagged | kCmdTypeBool;
|
|
|
|
argDef[index].name = curr;
|
|
|
|
argDef[index].id = 0;
|
|
|
|
curr += StrLen(curr) + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initialize the parser
|
|
|
|
Initialize(argDef.Ptr(), argDef.Count());
|
|
|
|
|
|
|
|
}
|