blob: 52f77ddedd257c5246161a502326121e0e7775a4 [file] [log] [blame]
/*
* Copyright (c) 2018, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file implements the command line parser.
*/
#include "parse_cmdline.hpp"
#include <string.h>
#include "common/code_utils.hpp"
#include "common/numeric_limits.hpp"
#include "common/string.hpp"
#include "net/ip6_address.hpp"
namespace ot {
namespace Utils {
namespace CmdLineParser {
static bool IsSeparator(char aChar)
{
return (aChar == ' ') || (aChar == '\t') || (aChar == '\r') || (aChar == '\n');
}
static bool IsEscapable(char aChar)
{
return IsSeparator(aChar) || (aChar == '\\');
}
static Error ParseDigit(char aDigitChar, uint8_t &aValue)
{
Error error = kErrorNone;
VerifyOrExit(('0' <= aDigitChar) && (aDigitChar <= '9'), error = kErrorInvalidArgs);
aValue = static_cast<uint8_t>(aDigitChar - '0');
exit:
return error;
}
static Error ParseHexDigit(char aHexChar, uint8_t &aValue)
{
Error error = kErrorNone;
if (('A' <= aHexChar) && (aHexChar <= 'F'))
{
ExitNow(aValue = static_cast<uint8_t>(aHexChar - 'A' + 10));
}
if (('a' <= aHexChar) && (aHexChar <= 'f'))
{
ExitNow(aValue = static_cast<uint8_t>(aHexChar - 'a' + 10));
}
error = ParseDigit(aHexChar, aValue);
exit:
return error;
}
Error ParseCmd(char *aCommandString, Arg aArgs[], uint8_t aArgsMaxLength)
{
Error error = kErrorNone;
uint8_t index = 0;
char * cmd;
for (cmd = aCommandString; *cmd; cmd++)
{
if ((*cmd == '\\') && IsEscapable(*(cmd + 1)))
{
// include the null terminator: strlen(cmd) = strlen(cmd + 1) + 1
memmove(cmd, cmd + 1, strlen(cmd));
}
else if (IsSeparator(*cmd))
{
*cmd = '\0';
}
if ((*cmd != '\0') && ((index == 0) || (*(cmd - 1) == '\0')))
{
if (index == aArgsMaxLength - 1)
{
error = kErrorInvalidArgs;
break;
}
aArgs[index++].SetCString(cmd);
}
}
while (index < aArgsMaxLength)
{
aArgs[index++].Clear();
}
return error;
}
template <typename UintType> Error ParseUint(const char *aString, UintType &aUint)
{
Error error;
uint64_t value;
SuccessOrExit(error = ParseAsUint64(aString, value));
VerifyOrExit(value <= NumericLimits<UintType>::kMax, error = kErrorInvalidArgs);
aUint = static_cast<UintType>(value);
exit:
return error;
}
Error ParseAsUint8(const char *aString, uint8_t &aUint8)
{
return ParseUint<uint8_t>(aString, aUint8);
}
Error ParseAsUint16(const char *aString, uint16_t &aUint16)
{
return ParseUint<uint16_t>(aString, aUint16);
}
Error ParseAsUint32(const char *aString, uint32_t &aUint32)
{
return ParseUint<uint32_t>(aString, aUint32);
}
Error ParseAsUint64(const char *aString, uint64_t &aUint64)
{
Error error = kErrorNone;
uint64_t value = 0;
const char *cur = aString;
bool isHex = false;
enum : uint64_t
{
kMaxHexBeforeOveflow = (0xffffffffffffffffULL / 16),
kMaxDecBeforeOverlow = (0xffffffffffffffffULL / 10),
};
VerifyOrExit(aString != nullptr, error = kErrorInvalidArgs);
if (cur[0] == '0' && (cur[1] == 'x' || cur[1] == 'X'))
{
cur += 2;
isHex = true;
}
do
{
uint8_t digit;
uint64_t newValue;
SuccessOrExit(error = isHex ? ParseHexDigit(*cur, digit) : ParseDigit(*cur, digit));
VerifyOrExit(value <= (isHex ? kMaxHexBeforeOveflow : kMaxDecBeforeOverlow), error = kErrorInvalidArgs);
value = isHex ? (value << 4) : (value * 10);
newValue = value + digit;
VerifyOrExit(newValue >= value, error = kErrorInvalidArgs);
value = newValue;
cur++;
} while (*cur != '\0');
aUint64 = value;
exit:
return error;
}
template <typename IntType> Error ParseInt(const char *aString, IntType &aInt)
{
Error error;
int32_t value;
SuccessOrExit(error = ParseAsInt32(aString, value));
VerifyOrExit((NumericLimits<IntType>::kMin <= value) && (value <= NumericLimits<IntType>::kMax),
error = kErrorInvalidArgs);
aInt = static_cast<IntType>(value);
exit:
return error;
}
Error ParseAsInt8(const char *aString, int8_t &aInt8)
{
return ParseInt<int8_t>(aString, aInt8);
}
Error ParseAsInt16(const char *aString, int16_t &aInt16)
{
return ParseInt<int16_t>(aString, aInt16);
}
Error ParseAsInt32(const char *aString, int32_t &aInt32)
{
Error error;
uint64_t value;
bool isNegavtive = false;
VerifyOrExit(aString != nullptr, error = kErrorInvalidArgs);
if (*aString == '-')
{
aString++;
isNegavtive = true;
}
else if (*aString == '+')
{
aString++;
}
SuccessOrExit(error = ParseAsUint64(aString, value));
VerifyOrExit(value <= (isNegavtive ? static_cast<uint64_t>(-static_cast<int64_t>(NumericLimits<int32_t>::kMin))
: static_cast<uint64_t>(NumericLimits<int32_t>::kMax)),
error = kErrorInvalidArgs);
aInt32 = static_cast<int32_t>(isNegavtive ? -static_cast<int64_t>(value) : static_cast<int64_t>(value));
exit:
return error;
}
Error ParseAsBool(const char *aString, bool &aBool)
{
Error error;
uint32_t value;
SuccessOrExit(error = ParseAsUint32(aString, value));
aBool = (value != 0);
exit:
return error;
}
#if OPENTHREAD_FTD || OPENTHREAD_MTD
Error ParseAsIp6Address(const char *aString, otIp6Address &aAddress)
{
return (aString != nullptr) ? otIp6AddressFromString(aString, &aAddress) : kErrorInvalidArgs;
}
Error ParseAsIp6Prefix(const char *aString, otIp6Prefix &aPrefix)
{
enum : uint8_t
{
kMaxIp6AddressStringSize = 45,
};
Error error = kErrorInvalidArgs;
char string[kMaxIp6AddressStringSize];
const char *prefixLengthStr;
VerifyOrExit(aString != nullptr);
prefixLengthStr = StringFind(aString, '/');
VerifyOrExit(prefixLengthStr != nullptr);
VerifyOrExit(prefixLengthStr - aString < static_cast<int32_t>(sizeof(string)));
memcpy(string, aString, static_cast<uint8_t>(prefixLengthStr - aString));
string[prefixLengthStr - aString] = '\0';
SuccessOrExit(static_cast<Ip6::Address &>(aPrefix.mPrefix).FromString(string));
error = ParseAsUint8(prefixLengthStr + 1, aPrefix.mLength);
exit:
return error;
}
#endif // #if OPENTHREAD_FTD || OPENTHREAD_MTD
enum HexStringParseMode
{
kModeExtactSize, // Parse hex string expecting an exact size (number of bytes when parsed).
kModeUpToSize, // Parse hex string expecting less than or equal a given size.
kModeAllowPartial, // Allow parsing of partial segments.
};
static Error ParseHexString(const char *&aString, uint16_t &aSize, uint8_t *aBuffer, HexStringParseMode aMode)
{
Error error = kErrorNone;
size_t parsedSize = 0;
size_t stringLength;
size_t expectedSize;
bool skipFirstDigit;
VerifyOrExit(aString != nullptr, error = kErrorInvalidArgs);
stringLength = strlen(aString);
expectedSize = (stringLength + 1) / 2;
switch (aMode)
{
case kModeExtactSize:
VerifyOrExit(expectedSize == aSize, error = kErrorInvalidArgs);
break;
case kModeUpToSize:
VerifyOrExit(expectedSize <= aSize, error = kErrorInvalidArgs);
break;
case kModeAllowPartial:
break;
}
// If number of chars in hex string is odd, we skip parsing
// the first digit.
skipFirstDigit = ((stringLength & 1) != 0);
while (parsedSize < expectedSize)
{
uint8_t digit;
if ((aMode == kModeAllowPartial) && (parsedSize == aSize))
{
// If partial parse mode is allowed, stop once we read the
// requested size.
ExitNow(error = kErrorPending);
}
if (skipFirstDigit)
{
*aBuffer = 0;
skipFirstDigit = false;
}
else
{
SuccessOrExit(error = ParseHexDigit(*aString, digit));
aString++;
*aBuffer = static_cast<uint8_t>(digit << 4);
}
SuccessOrExit(error = ParseHexDigit(*aString, digit));
aString++;
*aBuffer |= digit;
aBuffer++;
parsedSize++;
}
aSize = static_cast<uint16_t>(parsedSize);
exit:
return error;
}
Error ParseAsHexString(const char *aString, uint8_t *aBuffer, uint16_t aSize)
{
return ParseHexString(aString, aSize, aBuffer, kModeExtactSize);
}
Error ParseAsHexString(const char *aString, uint16_t &aSize, uint8_t *aBuffer)
{
return ParseHexString(aString, aSize, aBuffer, kModeUpToSize);
}
Error ParseAsHexStringSegment(const char *&aString, uint16_t &aSize, uint8_t *aBuffer)
{
return ParseHexString(aString, aSize, aBuffer, kModeAllowPartial);
}
//---------------------------------------------------------------------------------------------------------------------
// Arg class
uint16_t Arg::GetLength(void) const
{
return IsEmpty() ? 0 : static_cast<uint16_t>(strlen(mString));
}
bool Arg::operator==(const char *aString) const
{
return !IsEmpty() && (strcmp(mString, aString) == 0);
}
void Arg::CopyArgsToStringArray(Arg aArgs[], char *aStrings[])
{
for (uint8_t i = 0; !aArgs[i].IsEmpty(); i++)
{
aStrings[i] = aArgs[i].GetCString();
}
}
uint8_t Arg::GetArgsLength(Arg aArgs[])
{
uint8_t length = 0;
while (!aArgs[length].IsEmpty())
{
length++;
}
return length;
}
} // namespace CmdLineParser
} // namespace Utils
} // namespace ot