blob: 684cb6b98fca70c5f0f94f839eab7f84cc6566de [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 "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 otError ParseDigit(char aDigitChar, uint8_t &aValue)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(('0' <= aDigitChar) && (aDigitChar <= '9'), error = OT_ERROR_INVALID_ARGS);
aValue = static_cast<uint8_t>(aDigitChar - '0');
exit:
return error;
}
static otError ParseHexDigit(char aHexChar, uint8_t &aValue)
{
otError error = OT_ERROR_NONE;
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;
}
otError ParseCmd(char *aString, uint8_t &aArgsLength, char *aArgs[], uint8_t aArgsLengthMax)
{
otError error = OT_ERROR_NONE;
char * cmd;
aArgsLength = 0;
for (cmd = aString; *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') && ((aArgsLength == 0) || (*(cmd - 1) == '\0')))
{
VerifyOrExit(aArgsLength < aArgsLengthMax, error = OT_ERROR_INVALID_ARGS);
aArgs[aArgsLength++] = cmd;
}
}
exit:
return error;
}
template <typename UintType> otError ParseUint(const char *aString, UintType &aUint)
{
otError error;
uint64_t value;
SuccessOrExit(error = ParseAsUint64(aString, value));
VerifyOrExit(value <= NumericLimits<UintType>::Max(), error = OT_ERROR_INVALID_ARGS);
aUint = static_cast<UintType>(value);
exit:
return error;
}
otError ParseAsUint8(const char *aString, uint8_t &aUint8)
{
return ParseUint<uint8_t>(aString, aUint8);
}
otError ParseAsUint16(const char *aString, uint16_t &aUint16)
{
return ParseUint<uint16_t>(aString, aUint16);
}
otError ParseAsUint32(const char *aString, uint32_t &aUint32)
{
return ParseUint<uint32_t>(aString, aUint32);
}
otError ParseAsUint64(const char *aString, uint64_t &aUint64)
{
otError error = OT_ERROR_NONE;
uint64_t value = 0;
const char *cur = aString;
bool isHex = false;
enum : uint64_t
{
kMaxHexBeforeOveflow = (0xffffffffffffffffULL / 16),
kMaxDecBeforeOverlow = (0xffffffffffffffffULL / 10),
};
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 = OT_ERROR_INVALID_ARGS);
value = isHex ? (value << 4) : (value * 10);
newValue = value + digit;
VerifyOrExit(newValue >= value, error = OT_ERROR_INVALID_ARGS);
value = newValue;
cur++;
} while (*cur != '\0');
aUint64 = value;
exit:
return error;
}
template <typename IntType> otError ParseInt(const char *aString, IntType &aInt)
{
otError error;
int32_t value;
SuccessOrExit(error = ParseAsInt32(aString, value));
VerifyOrExit((NumericLimits<IntType>::Min() <= value) && (value <= NumericLimits<IntType>::Max()),
error = OT_ERROR_INVALID_ARGS);
aInt = static_cast<IntType>(value);
exit:
return error;
}
otError ParseAsInt8(const char *aString, int8_t &aInt8)
{
return ParseInt<int8_t>(aString, aInt8);
}
otError ParseAsInt16(const char *aString, int16_t &aInt16)
{
return ParseInt<int16_t>(aString, aInt16);
}
otError ParseAsInt32(const char *aString, int32_t &aInt32)
{
otError error;
uint64_t value;
bool isNegavtive = false;
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>::Min()))
: static_cast<uint64_t>(NumericLimits<int32_t>::Max())),
error = OT_ERROR_INVALID_ARGS);
aInt32 = isNegavtive ? -static_cast<int32_t>(value) : static_cast<int32_t>(value);
exit:
return error;
}
otError ParseAsBool(const char *aString, bool &aBool)
{
otError error;
uint32_t value;
SuccessOrExit(error = ParseAsUint32(aString, value));
aBool = (value != 0);
exit:
return error;
}
#if OPENTHREAD_FTD || OPENTHREAD_MTD
otError ParseAsIp6Prefix(const char *aString, otIp6Prefix &aPrefix)
{
enum : uint8_t
{
kMaxIp6AddressStringSize = 45,
};
otError error = OT_ERROR_INVALID_ARGS;
char string[kMaxIp6AddressStringSize];
const char *prefixLengthStr;
prefixLengthStr = strchr(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
otError ParseAsHexString(const char *aString, uint8_t *aBuffer, uint16_t aSize)
{
otError error;
uint16_t readSize = aSize;
SuccessOrExit(error = ParseAsHexString(aString, readSize, aBuffer, kDisallowTruncate));
VerifyOrExit(readSize == aSize, error = OT_ERROR_INVALID_ARGS);
exit:
return error;
}
otError ParseAsHexString(const char *aString, uint16_t &aSize, uint8_t *aBuffer, HexStringParseMode aMode)
{
otError error = OT_ERROR_NONE;
uint8_t byte = 0;
uint16_t readBytes = 0;
const char *hex = aString;
size_t hexLength = strlen(aString);
uint8_t numChars;
if (aMode == kDisallowTruncate)
{
VerifyOrExit((hexLength + 1) / 2 <= aSize, error = OT_ERROR_INVALID_ARGS);
}
// Handle the case where number of chars in hex string is odd.
numChars = hexLength & 1;
while (*hex != '\0')
{
uint8_t digit;
SuccessOrExit(error = ParseHexDigit(*hex, digit));
byte |= digit;
hex++;
numChars++;
if (numChars >= 2)
{
numChars = 0;
*aBuffer++ = byte;
byte = 0;
readBytes++;
if (readBytes == aSize)
{
ExitNow();
}
}
else
{
byte <<= 4;
}
}
aSize = readBytes;
exit:
return error;
}
} // namespace CmdLineParser
} // namespace Utils
} // namespace ot