[cmd-parser] adding helper 'ParseAs{Type}' function (#5607)

This commit adds new helper functions under `Utils::CmdLineParser` to
parse a given ASCII string as different simple types, namely signed or
unsigned integers of different bit lengths (`uint8/16/32/64`, and
`int8/16/32`). The new functions validate that the parsed value fits
within the supported range of the expected int type. This commit also
adds different flavors of `ParseAsHexString()` to parse a hex string
into sequence of bytes (with fixed/known size or unknown size allowing
or disallowing truncation). It also moves the parse related methods
(like `ParseAsIp6Prefix`) from CLI modules in `CmdLineParser`.

The CLI modules are then updated to use the new `ParseAs{Type}`
functions simplifying the code and adding extra checks when parsing
the CLI input.

Finally, this commit adds a unit test `test_cmd_line_parser` verifying
the behavior of new `ParseAs{Type}()` functions.
This commit is contained in:
Abtin Keshavarzian
2020-10-08 10:28:38 -07:00
committed by GitHub
parent 41f7cba459
commit 4b3b37c312
14 changed files with 1241 additions and 650 deletions
+254 -363
View File
File diff suppressed because it is too large Load Diff
+1 -40
View File
@@ -126,44 +126,6 @@ public:
*/
void ProcessLine(char *aBuf, uint16_t aBufLength);
/**
* This method parses an ASCII string as a long.
*
* @param[in] aString A pointer to the ASCII string.
* @param[out] aLong A reference to where the parsed long is placed.
*
* @retval OT_ERROR_NONE Successfully parsed the ASCII string.
* @retval OT_ERROR_INVALID_ARGS @p aString is not a valid long integer.
*
*/
static otError ParseLong(char *aString, long &aLong);
/**
* This method parses an ASCII string as an unsigned long.
*
* @param[in] aString A pointer to the ASCII string.
* @param[out] aUnsignedLong A reference to where the parsed unsigned long is placed.
*
* @retval OT_ERROR_NONE Successfully parsed the ASCII string.
* @retval OT_ERROR_INVALID_ARGS @p aString is not a valid unsigned long integer.
*
*/
static otError ParseUnsignedLong(char *aString, unsigned long &aUnsignedLong);
/**
* This method converts a hex string to binary.
*
* @param[in] aHex A pointer to the hex string.
* @param[out] aBin A pointer to where the binary representation is placed.
* @param[in] aBinLength Maximum length of the binary representation.
* @param[in] aAllowTruncate TRUE if @p aBinLength may be less than what is required
* to convert @p aHex to binary representation, FALSE otherwise.
*
* @returns The number of bytes in the binary representation, or -1 if @p aHex is not a valid hex string
*
*/
static int Hex2Bin(const char *aHex, uint8_t *aBin, uint16_t aBinLength, bool aAllowTruncate = false);
/**
* This method delivers raw characters to the client.
*
@@ -296,7 +258,6 @@ private:
otError ParsePingInterval(const char *aString, uint32_t &aInterval);
static otError ParseJoinerDiscerner(char *aString, otJoinerDiscerner &aJoinerDiscerner);
static otError ParseIp6Prefix(char *aString, otIp6Prefix &aPrefix);
otError ProcessHelp(uint8_t aArgsLength, char *aArgs[]);
otError ProcessCcaThreshold(uint8_t aArgsLength, char *aArgs[]);
@@ -343,7 +304,7 @@ private:
otError ProcessContextIdReuseDelay(uint8_t aArgsLength, char *aArgs[]);
#endif
otError ProcessCounters(uint8_t aArgsLength, char *aArgs[]);
otError ProcessCsl(uint8_t aArgsLength, char *argv[]);
otError ProcessCsl(uint8_t aArgsLength, char *aArgs[]);
#if OPENTHREAD_FTD
otError ProcessDelayTimerMin(uint8_t aArgsLength, char *aArgs[]);
#endif
+10 -17
View File
@@ -39,6 +39,11 @@
#include "cli/cli.hpp"
#include "coap/coap_message.hpp"
#include "utils/parse_cmdline.hpp"
using ot::Utils::CmdLineParser::ParseAsIp6Address;
using ot::Utils::CmdLineParser::ParseAsUint32;
using ot::Utils::CmdLineParser::ParseAsUint8;
namespace ot {
namespace Cli {
@@ -298,24 +303,12 @@ otError Coap::ProcessParameters(uint8_t aArgsLength, char *aArgs[])
}
else
{
unsigned long value;
VerifyOrExit(aArgsLength >= 6, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = mInterpreter.ParseUnsignedLong(aArgs[2], value));
txParameters->mAckTimeout = static_cast<uint32_t>(value);
SuccessOrExit(error = mInterpreter.ParseUnsignedLong(aArgs[3], value));
VerifyOrExit(value <= 255, error = OT_ERROR_INVALID_ARGS);
txParameters->mAckRandomFactorNumerator = static_cast<uint8_t>(value);
SuccessOrExit(error = mInterpreter.ParseUnsignedLong(aArgs[4], value));
VerifyOrExit(value <= 255, error = OT_ERROR_INVALID_ARGS);
txParameters->mAckRandomFactorDenominator = static_cast<uint8_t>(value);
SuccessOrExit(error = mInterpreter.ParseUnsignedLong(aArgs[5], value));
VerifyOrExit(value <= 255, error = OT_ERROR_INVALID_ARGS);
txParameters->mMaxRetransmit = static_cast<uint8_t>(value);
SuccessOrExit(error = ParseAsUint32(aArgs[2], txParameters->mAckTimeout));
SuccessOrExit(error = ParseAsUint8(aArgs[3], txParameters->mAckRandomFactorNumerator));
SuccessOrExit(error = ParseAsUint8(aArgs[4], txParameters->mAckRandomFactorDenominator));
SuccessOrExit(error = ParseAsUint8(aArgs[5], txParameters->mMaxRetransmit));
VerifyOrExit(txParameters->mAckRandomFactorNumerator > txParameters->mAckRandomFactorDenominator,
error = OT_ERROR_INVALID_ARGS);
@@ -391,7 +384,7 @@ otError Coap::ProcessRequest(uint8_t aArgsLength, char *aArgs[])
// Destination IPv6 address
if (aArgsLength > 1)
{
SuccessOrExit(error = otIp6AddressFromString(aArgs[1], &coapDestinationIp));
SuccessOrExit(error = ParseAsIp6Address(aArgs[1], coapDestinationIp));
}
else
{
+8 -7
View File
@@ -39,9 +39,14 @@
#include <openthread/ip6.h>
#include "cli/cli.hpp"
#include "utils/parse_cmdline.hpp"
// header for place your x509 certificate and private key
#include "x509_cert_key.hpp"
using ot::Utils::CmdLineParser::ParseAsIp6Address;
using ot::Utils::CmdLineParser::ParseAsUint16;
namespace ot {
namespace Cli {
@@ -234,7 +239,7 @@ otError CoapSecure::ProcessRequest(uint8_t aArgsLength, char *aArgs[])
// Destination IPv6 address
if (aArgsLength > 1)
{
error = otIp6AddressFromString(aArgs[1], &coapDestinationIp);
error = ParseAsIp6Address(aArgs[1], coapDestinationIp);
}
else
{
@@ -322,17 +327,13 @@ otError CoapSecure::ProcessConnect(uint8_t aArgsLength, char *aArgs[])
// Destination IPv6 address
memset(&sockaddr, 0, sizeof(sockaddr));
SuccessOrExit(error = otIp6AddressFromString(aArgs[1], &sockaddr.mAddress));
SuccessOrExit(error = ParseAsIp6Address(aArgs[1], sockaddr.mAddress));
sockaddr.mPort = OT_DEFAULT_COAP_SECURE_PORT;
// check for port specification
if (aArgsLength > 2)
{
long value;
error = Interpreter::ParseLong(aArgs[2], value);
SuccessOrExit(error);
sockaddr.mPort = static_cast<uint16_t>(value);
SuccessOrExit(error = ParseAsUint16(aArgs[2], sockaddr.mPort));
}
SuccessOrExit(error = otCoapSecureConnect(mInterpreter.mInstance, &sockaddr, &CoapSecure::HandleConnected, this));
+58 -68
View File
@@ -34,6 +34,13 @@
#include "cli_commissioner.hpp"
#include "cli/cli.hpp"
#include "utils/parse_cmdline.hpp"
using ot::Utils::CmdLineParser::ParseAsHexString;
using ot::Utils::CmdLineParser::ParseAsIp6Address;
using ot::Utils::CmdLineParser::ParseAsUint16;
using ot::Utils::CmdLineParser::ParseAsUint32;
using ot::Utils::CmdLineParser::ParseAsUint8;
#if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
@@ -58,21 +65,19 @@ otError Commissioner::ProcessHelp(uint8_t aArgsLength, char *aArgs[])
otError Commissioner::ProcessAnnounce(uint8_t aArgsLength, char *aArgs[])
{
otError error;
long mask;
long count;
long period;
uint32_t mask;
uint8_t count;
uint16_t period;
otIp6Address address;
VerifyOrExit(aArgsLength > 4, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = Interpreter::ParseLong(aArgs[1], mask));
SuccessOrExit(error = Interpreter::ParseLong(aArgs[2], count));
SuccessOrExit(error = Interpreter::ParseLong(aArgs[3], period));
SuccessOrExit(error = otIp6AddressFromString(aArgs[4], &address));
SuccessOrExit(error = ParseAsUint32(aArgs[1], mask));
SuccessOrExit(error = ParseAsUint8(aArgs[2], count));
SuccessOrExit(error = ParseAsUint16(aArgs[3], period));
SuccessOrExit(error = ParseAsIp6Address(aArgs[4], address));
SuccessOrExit(error = otCommissionerAnnounceBegin(mInterpreter.mInstance, static_cast<uint32_t>(mask),
static_cast<uint8_t>(count), static_cast<uint16_t>(period),
&address));
SuccessOrExit(error = otCommissionerAnnounceBegin(mInterpreter.mInstance, mask, count, period, &address));
exit:
return error;
@@ -81,23 +86,21 @@ exit:
otError Commissioner::ProcessEnergy(uint8_t aArgsLength, char *aArgs[])
{
otError error;
long mask;
long count;
long period;
long scanDuration;
uint32_t mask;
uint8_t count;
uint16_t period;
uint16_t scanDuration;
otIp6Address address;
VerifyOrExit(aArgsLength > 5, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = Interpreter::ParseLong(aArgs[1], mask));
SuccessOrExit(error = Interpreter::ParseLong(aArgs[2], count));
SuccessOrExit(error = Interpreter::ParseLong(aArgs[3], period));
SuccessOrExit(error = Interpreter::ParseLong(aArgs[4], scanDuration));
SuccessOrExit(error = otIp6AddressFromString(aArgs[5], &address));
SuccessOrExit(error = ParseAsUint32(aArgs[1], mask));
SuccessOrExit(error = ParseAsUint8(aArgs[2], count));
SuccessOrExit(error = ParseAsUint16(aArgs[3], period));
SuccessOrExit(error = ParseAsUint16(aArgs[4], scanDuration));
SuccessOrExit(error = ParseAsIp6Address(aArgs[5], address));
SuccessOrExit(error = otCommissionerEnergyScan(mInterpreter.mInstance, static_cast<uint32_t>(mask),
static_cast<uint8_t>(count), static_cast<uint16_t>(period),
static_cast<uint16_t>(scanDuration), &address,
SuccessOrExit(error = otCommissionerEnergyScan(mInterpreter.mInstance, mask, count, period, scanDuration, &address,
&Commissioner::HandleEnergyReport, this));
exit:
@@ -121,8 +124,7 @@ otError Commissioner::ProcessJoiner(uint8_t aArgsLength, char *aArgs[])
}
else if ((error = Interpreter::ParseJoinerDiscerner(aArgs[2], discerner)) == OT_ERROR_NOT_FOUND)
{
VerifyOrExit(Interpreter::Hex2Bin(aArgs[2], addr.m8, sizeof(addr)) == sizeof(addr),
error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = ParseAsHexString(aArgs[2], addr.m8));
addrPtr = &addr;
}
else if (error != OT_ERROR_NONE)
@@ -134,22 +136,21 @@ otError Commissioner::ProcessJoiner(uint8_t aArgsLength, char *aArgs[])
{
VerifyOrExit(aArgsLength > 3, error = OT_ERROR_INVALID_ARGS);
// Timeout parameter is optional - if not specified, use default value.
unsigned long timeout = kDefaultJoinerTimeout;
uint32_t timeout = kDefaultJoinerTimeout;
if (aArgsLength > 4)
{
SuccessOrExit(error = Interpreter::ParseUnsignedLong(aArgs[4], timeout));
SuccessOrExit(error = ParseAsUint32(aArgs[4], timeout));
}
if (discerner.mLength)
{
SuccessOrExit(error = otCommissionerAddJoinerWithDiscerner(mInterpreter.mInstance, &discerner, aArgs[3],
static_cast<uint32_t>(timeout)));
SuccessOrExit(
error = otCommissionerAddJoinerWithDiscerner(mInterpreter.mInstance, &discerner, aArgs[3], timeout));
}
else
{
SuccessOrExit(error = otCommissionerAddJoiner(mInterpreter.mInstance, addrPtr, aArgs[3],
static_cast<uint32_t>(timeout)));
SuccessOrExit(error = otCommissionerAddJoiner(mInterpreter.mInstance, addrPtr, aArgs[3], timeout));
}
}
else if (strcmp(aArgs[1], "remove") == 0)
@@ -174,10 +175,9 @@ exit:
otError Commissioner::ProcessMgmtGet(uint8_t aArgsLength, char *aArgs[])
{
otError error;
otError error = OT_ERROR_NONE;
uint8_t tlvs[32];
long value;
int length = 0;
uint8_t length = 0;
for (uint8_t index = 1; index < aArgsLength; index++)
{
@@ -201,13 +201,12 @@ otError Commissioner::ProcessMgmtGet(uint8_t aArgsLength, char *aArgs[])
}
else if (strcmp(aArgs[index], "-x") == 0)
{
uint16_t readLength;
VerifyOrExit(++index < aArgsLength, error = OT_ERROR_INVALID_ARGS);
value = static_cast<long>(strlen(aArgs[index]) + 1) / 2;
VerifyOrExit(static_cast<size_t>(value) <= (sizeof(tlvs) - static_cast<size_t>(length)),
error = OT_ERROR_NO_BUFS);
VerifyOrExit(Interpreter::Hex2Bin(aArgs[index], tlvs + length, static_cast<uint16_t>(value)) == value,
error = OT_ERROR_INVALID_ARGS);
length += value;
readLength = static_cast<uint16_t>(sizeof(tlvs) - length);
SuccessOrExit(error = ParseAsHexString(aArgs[index], readLength, tlvs + length));
length += static_cast<uint8_t>(readLength);
}
else
{
@@ -226,8 +225,7 @@ otError Commissioner::ProcessMgmtSet(uint8_t aArgsLength, char *aArgs[])
otError error;
otCommissioningDataset dataset;
uint8_t tlvs[32];
long value;
int length = 0;
uint8_t tlvsLength = 0;
VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS);
@@ -235,48 +233,42 @@ otError Commissioner::ProcessMgmtSet(uint8_t aArgsLength, char *aArgs[])
for (uint8_t index = 1; index < aArgsLength; index++)
{
VerifyOrExit(static_cast<size_t>(length) < sizeof(tlvs), error = OT_ERROR_NO_BUFS);
if (strcmp(aArgs[index], "locator") == 0)
{
VerifyOrExit(++index < aArgsLength, error = OT_ERROR_INVALID_ARGS);
dataset.mIsLocatorSet = true;
SuccessOrExit(error = Interpreter::Interpreter::ParseLong(aArgs[index], value));
dataset.mLocator = static_cast<uint16_t>(value);
SuccessOrExit(error = ParseAsUint16(aArgs[index], dataset.mLocator));
}
else if (strcmp(aArgs[index], "sessionid") == 0)
{
VerifyOrExit(++index < aArgsLength, error = OT_ERROR_INVALID_ARGS);
dataset.mIsSessionIdSet = true;
SuccessOrExit(error = Interpreter::Interpreter::ParseLong(aArgs[index], value));
dataset.mSessionId = static_cast<uint16_t>(value);
SuccessOrExit(error = ParseAsUint16(aArgs[index], dataset.mSessionId));
}
else if (strcmp(aArgs[index], "steeringdata") == 0)
{
uint16_t length;
VerifyOrExit(++index < aArgsLength, error = OT_ERROR_INVALID_ARGS);
dataset.mIsSteeringDataSet = true;
length = static_cast<int>((strlen(aArgs[index]) + 1) / 2);
VerifyOrExit(static_cast<size_t>(length) <= OT_STEERING_DATA_MAX_LENGTH, error = OT_ERROR_NO_BUFS);
VerifyOrExit(Interpreter::Hex2Bin(aArgs[index], dataset.mSteeringData.m8, static_cast<uint16_t>(length)) ==
length,
error = OT_ERROR_INVALID_ARGS);
length = sizeof(dataset.mSteeringData.m8);
SuccessOrExit(error = ParseAsHexString(aArgs[index], length, dataset.mSteeringData.m8));
dataset.mSteeringData.mLength = static_cast<uint8_t>(length);
length = 0;
}
else if (strcmp(aArgs[index], "joinerudpport") == 0)
{
VerifyOrExit(++index < aArgsLength, error = OT_ERROR_INVALID_ARGS);
dataset.mIsJoinerUdpPortSet = true;
SuccessOrExit(error = Interpreter::Interpreter::ParseLong(aArgs[index], value));
dataset.mJoinerUdpPort = static_cast<uint16_t>(value);
SuccessOrExit(error = ParseAsUint16(aArgs[index], dataset.mJoinerUdpPort));
}
else if (strcmp(aArgs[index], "-x") == 0)
{
uint16_t length;
VerifyOrExit(++index < aArgsLength, error = OT_ERROR_INVALID_ARGS);
length = static_cast<int>((strlen(aArgs[index]) + 1) / 2);
VerifyOrExit(static_cast<size_t>(length) <= sizeof(tlvs), error = OT_ERROR_NO_BUFS);
VerifyOrExit(Interpreter::Hex2Bin(aArgs[index], tlvs, static_cast<uint16_t>(length)) == length,
error = OT_ERROR_INVALID_ARGS);
length = sizeof(tlvs);
SuccessOrExit(error = ParseAsHexString(aArgs[index], length, tlvs));
tlvsLength = static_cast<uint8_t>(length);
}
else
{
@@ -284,8 +276,7 @@ otError Commissioner::ProcessMgmtSet(uint8_t aArgsLength, char *aArgs[])
}
}
SuccessOrExit(error =
otCommissionerSendMgmtSet(mInterpreter.mInstance, &dataset, tlvs, static_cast<uint8_t>(length)));
SuccessOrExit(error = otCommissionerSendMgmtSet(mInterpreter.mInstance, &dataset, tlvs, tlvsLength));
exit:
return error;
@@ -294,18 +285,17 @@ exit:
otError Commissioner::ProcessPanId(uint8_t aArgsLength, char *aArgs[])
{
otError error;
long panid;
long mask;
uint16_t panId;
uint32_t mask;
otIp6Address address;
VerifyOrExit(aArgsLength > 3, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = Interpreter::ParseLong(aArgs[1], panid));
SuccessOrExit(error = Interpreter::ParseLong(aArgs[2], mask));
SuccessOrExit(error = otIp6AddressFromString(aArgs[3], &address));
SuccessOrExit(error = ParseAsUint16(aArgs[1], panId));
SuccessOrExit(error = ParseAsUint32(aArgs[2], mask));
SuccessOrExit(error = ParseAsIp6Address(aArgs[3], address));
SuccessOrExit(error = otCommissionerPanIdQuery(mInterpreter.mInstance, static_cast<uint16_t>(panid),
static_cast<uint32_t>(mask), &address,
SuccessOrExit(error = otCommissionerPanIdQuery(mInterpreter.mInstance, panId, mask, &address,
&Commissioner::HandlePanIdConflict, this));
exit:
+55 -99
View File
@@ -40,6 +40,14 @@
#include <openthread/dataset_ftd.h>
#include "cli/cli.hpp"
#include "utils/parse_cmdline.hpp"
using ot::Utils::CmdLineParser::ParseAsHexString;
using ot::Utils::CmdLineParser::ParseAsIp6Address;
using ot::Utils::CmdLineParser::ParseAsUint16;
using ot::Utils::CmdLineParser::ParseAsUint32;
using ot::Utils::CmdLineParser::ParseAsUint64;
using ot::Utils::CmdLineParser::ParseAsUint8;
namespace ot {
namespace Cli {
@@ -284,10 +292,7 @@ otError Dataset::ProcessActiveTimestamp(uint8_t aArgsLength, char *aArgs[])
}
else
{
long value;
SuccessOrExit(error = Interpreter::ParseLong(aArgs[0], value));
sDataset.mActiveTimestamp = static_cast<uint64_t>(value);
SuccessOrExit(error = ParseAsUint64(aArgs[0], sDataset.mActiveTimestamp));
sDataset.mComponents.mIsActiveTimestampPresent = true;
}
@@ -308,10 +313,7 @@ otError Dataset::ProcessChannel(uint8_t aArgsLength, char *aArgs[])
}
else
{
long value;
SuccessOrExit(error = Interpreter::ParseLong(aArgs[0], value));
sDataset.mChannel = static_cast<uint16_t>(value);
SuccessOrExit(error = ParseAsUint16(aArgs[0], sDataset.mChannel));
sDataset.mComponents.mIsChannelPresent = true;
}
@@ -332,10 +334,7 @@ otError Dataset::ProcessChannelMask(uint8_t aArgsLength, char *aArgs[])
}
else
{
long value;
SuccessOrExit(error = Interpreter::ParseLong(aArgs[0], value));
sDataset.mChannelMask = static_cast<uint32_t>(value);
SuccessOrExit(error = ParseAsUint32(aArgs[0], sDataset.mChannelMask));
sDataset.mComponents.mIsChannelMaskPresent = true;
}
@@ -388,10 +387,7 @@ otError Dataset::ProcessDelay(uint8_t aArgsLength, char *aArgs[])
}
else
{
long value;
SuccessOrExit(error = Interpreter::ParseLong(aArgs[0], value));
sDataset.mDelay = static_cast<uint32_t>(value);
SuccessOrExit(error = ParseAsUint32(aArgs[0], sDataset.mDelay));
sDataset.mComponents.mIsDelayPresent = true;
}
@@ -413,12 +409,7 @@ otError Dataset::ProcessExtPanId(uint8_t aArgsLength, char *aArgs[])
}
else
{
uint8_t extPanId[OT_EXT_PAN_ID_SIZE];
VerifyOrExit(Interpreter::Hex2Bin(aArgs[0], extPanId, sizeof(extPanId)) == sizeof(extPanId),
error = OT_ERROR_INVALID_ARGS);
memcpy(sDataset.mExtendedPanId.m8, extPanId, sizeof(sDataset.mExtendedPanId));
SuccessOrExit(error = ParseAsHexString(aArgs[0], sDataset.mExtendedPanId.m8));
sDataset.mComponents.mIsExtendedPanIdPresent = true;
}
@@ -440,12 +431,7 @@ otError Dataset::ProcessMasterKey(uint8_t aArgsLength, char *aArgs[])
}
else
{
uint8_t key[OT_MASTER_KEY_SIZE];
VerifyOrExit((Interpreter::Hex2Bin(aArgs[0], key, sizeof(key))) == OT_MASTER_KEY_SIZE,
error = OT_ERROR_INVALID_ARGS);
memcpy(sDataset.mMasterKey.m8, key, sizeof(sDataset.mMasterKey));
SuccessOrExit(error = ParseAsHexString(aArgs[0], sDataset.mMasterKey.m8));
sDataset.mComponents.mIsMasterKeyPresent = true;
}
@@ -473,7 +459,7 @@ otError Dataset::ProcessMeshLocalPrefix(uint8_t aArgsLength, char *aArgs[])
{
otIp6Address prefix;
SuccessOrExit(error = otIp6AddressFromString(aArgs[0], &prefix));
SuccessOrExit(error = ParseAsIp6Address(aArgs[0], prefix));
memcpy(sDataset.mMeshLocalPrefix.m8, prefix.mFields.m8, sizeof(sDataset.mMeshLocalPrefix.m8));
sDataset.mComponents.mIsMeshLocalPrefixPresent = true;
@@ -523,10 +509,7 @@ otError Dataset::ProcessPanId(uint8_t aArgsLength, char *aArgs[])
}
else
{
long value;
SuccessOrExit(error = Interpreter::ParseLong(aArgs[0], value));
sDataset.mPanId = static_cast<otPanId>(value);
SuccessOrExit(error = ParseAsUint16(aArgs[0], sDataset.mPanId));
sDataset.mComponents.mIsPanIdPresent = true;
}
@@ -547,10 +530,7 @@ otError Dataset::ProcessPendingTimestamp(uint8_t aArgsLength, char *aArgs[])
}
else
{
long value;
SuccessOrExit(error = Interpreter::ParseLong(aArgs[0], value));
sDataset.mPendingTimestamp = static_cast<uint64_t>(value);
SuccessOrExit(error = ParseAsUint64(aArgs[0], sDataset.mPendingTimestamp));
sDataset.mComponents.mIsPendingTimestampPresent = true;
}
@@ -563,9 +543,7 @@ otError Dataset::ProcessMgmtSetCommand(uint8_t aArgsLength, char *aArgs[])
otError error = OT_ERROR_NONE;
otOperationalDataset dataset;
uint8_t tlvs[128];
long value;
int length = 0;
otIp6Address prefix;
uint8_t tlvsLength = 0;
VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS);
@@ -577,85 +555,77 @@ otError Dataset::ProcessMgmtSetCommand(uint8_t aArgsLength, char *aArgs[])
{
VerifyOrExit(++index < aArgsLength, error = OT_ERROR_INVALID_ARGS);
dataset.mComponents.mIsActiveTimestampPresent = true;
SuccessOrExit(error = Interpreter::ParseLong(aArgs[index], value));
dataset.mActiveTimestamp = static_cast<uint64_t>(value);
SuccessOrExit(error = ParseAsUint64(aArgs[index], dataset.mActiveTimestamp));
}
else if (strcmp(aArgs[index], "pendingtimestamp") == 0)
{
VerifyOrExit(++index < aArgsLength, error = OT_ERROR_INVALID_ARGS);
dataset.mComponents.mIsPendingTimestampPresent = true;
SuccessOrExit(error = Interpreter::ParseLong(aArgs[index], value));
dataset.mPendingTimestamp = static_cast<uint64_t>(value);
SuccessOrExit(error = ParseAsUint64(aArgs[index], dataset.mPendingTimestamp));
}
else if (strcmp(aArgs[index], "masterkey") == 0)
{
VerifyOrExit(++index < aArgsLength, error = OT_ERROR_INVALID_ARGS);
dataset.mComponents.mIsMasterKeyPresent = true;
VerifyOrExit((length = Interpreter::Hex2Bin(aArgs[index], dataset.mMasterKey.m8,
sizeof(dataset.mMasterKey.m8))) == OT_MASTER_KEY_SIZE,
error = OT_ERROR_INVALID_ARGS);
length = 0;
SuccessOrExit(error = ParseAsHexString(aArgs[index], dataset.mMasterKey.m8));
}
else if (strcmp(aArgs[index], "networkname") == 0)
{
size_t length;
VerifyOrExit(++index < aArgsLength, error = OT_ERROR_INVALID_ARGS);
dataset.mComponents.mIsNetworkNamePresent = true;
VerifyOrExit((length = static_cast<int>(strlen(aArgs[index]))) <= OT_NETWORK_NAME_MAX_SIZE,
error = OT_ERROR_INVALID_ARGS);
VerifyOrExit((length = strlen(aArgs[index])) <= OT_NETWORK_NAME_MAX_SIZE, error = OT_ERROR_INVALID_ARGS);
memset(&dataset.mNetworkName, 0, sizeof(sDataset.mNetworkName));
memcpy(dataset.mNetworkName.m8, aArgs[index], static_cast<size_t>(length));
length = 0;
memcpy(dataset.mNetworkName.m8, aArgs[index], length);
}
else if (strcmp(aArgs[index], "extpanid") == 0)
{
VerifyOrExit(++index < aArgsLength, error = OT_ERROR_INVALID_ARGS);
dataset.mComponents.mIsExtendedPanIdPresent = true;
VerifyOrExit(Interpreter::Hex2Bin(aArgs[index], dataset.mExtendedPanId.m8,
sizeof(dataset.mExtendedPanId.m8)) == sizeof(dataset.mExtendedPanId.m8),
error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = ParseAsHexString(aArgs[index], dataset.mExtendedPanId.m8));
}
else if (strcmp(aArgs[index], "localprefix") == 0)
{
otIp6Address prefix;
VerifyOrExit(++index < aArgsLength, error = OT_ERROR_INVALID_ARGS);
dataset.mComponents.mIsMeshLocalPrefixPresent = true;
SuccessOrExit(error = otIp6AddressFromString(aArgs[index], &prefix));
SuccessOrExit(error = ParseAsIp6Address(aArgs[index], prefix));
memcpy(dataset.mMeshLocalPrefix.m8, prefix.mFields.m8, sizeof(dataset.mMeshLocalPrefix.m8));
}
else if (strcmp(aArgs[index], "delaytimer") == 0)
{
VerifyOrExit(++index < aArgsLength, error = OT_ERROR_INVALID_ARGS);
dataset.mComponents.mIsDelayPresent = true;
SuccessOrExit(error = Interpreter::ParseLong(aArgs[index], value));
dataset.mDelay = static_cast<uint32_t>(value);
SuccessOrExit(error = ParseAsUint32(aArgs[index], dataset.mDelay));
}
else if (strcmp(aArgs[index], "panid") == 0)
{
VerifyOrExit(++index < aArgsLength, error = OT_ERROR_INVALID_ARGS);
dataset.mComponents.mIsPanIdPresent = true;
SuccessOrExit(error = Interpreter::ParseLong(aArgs[index], value));
dataset.mPanId = static_cast<otPanId>(value);
SuccessOrExit(error = ParseAsUint16(aArgs[index], dataset.mPanId));
}
else if (strcmp(aArgs[index], "channel") == 0)
{
VerifyOrExit(++index < aArgsLength, error = OT_ERROR_INVALID_ARGS);
dataset.mComponents.mIsChannelPresent = true;
SuccessOrExit(error = Interpreter::ParseLong(aArgs[index], value));
dataset.mChannel = static_cast<uint16_t>(value);
SuccessOrExit(error = ParseAsUint16(aArgs[index], dataset.mChannel));
}
else if (strcmp(aArgs[index], "channelmask") == 0)
{
VerifyOrExit(++index < aArgsLength, error = OT_ERROR_INVALID_ARGS);
dataset.mComponents.mIsChannelMaskPresent = true;
SuccessOrExit(error = Interpreter::ParseLong(aArgs[index], value));
dataset.mChannelMask = static_cast<uint32_t>(value);
SuccessOrExit(error = ParseAsUint32(aArgs[index], dataset.mChannelMask));
}
else if (strcmp(aArgs[index], "-x") == 0)
{
uint16_t length;
VerifyOrExit(++index < aArgsLength, error = OT_ERROR_INVALID_ARGS);
length = static_cast<int>((strlen(aArgs[index]) + 1) / 2);
VerifyOrExit(static_cast<size_t>(length) <= sizeof(tlvs), error = OT_ERROR_NO_BUFS);
VerifyOrExit(Interpreter::Hex2Bin(aArgs[index], tlvs, static_cast<uint16_t>(length)) == length,
error = OT_ERROR_INVALID_ARGS);
length = sizeof(tlvs);
SuccessOrExit(error = ParseAsHexString(aArgs[index], length, tlvs));
tlvsLength = static_cast<uint8_t>(length);
}
else
{
@@ -665,13 +635,11 @@ otError Dataset::ProcessMgmtSetCommand(uint8_t aArgsLength, char *aArgs[])
if (strcmp(aArgs[0], "active") == 0)
{
SuccessOrExit(
error = otDatasetSendMgmtActiveSet(mInterpreter.mInstance, &dataset, tlvs, static_cast<uint8_t>(length)));
SuccessOrExit(error = otDatasetSendMgmtActiveSet(mInterpreter.mInstance, &dataset, tlvs, tlvsLength));
}
else if (strcmp(aArgs[0], "pending") == 0)
{
SuccessOrExit(
error = otDatasetSendMgmtPendingSet(mInterpreter.mInstance, &dataset, tlvs, static_cast<uint8_t>(length)));
SuccessOrExit(error = otDatasetSendMgmtPendingSet(mInterpreter.mInstance, &dataset, tlvs, tlvsLength));
}
else
{
@@ -687,8 +655,7 @@ otError Dataset::ProcessMgmtGetCommand(uint8_t aArgsLength, char *aArgs[])
otError error = OT_ERROR_NONE;
otOperationalDatasetComponents datasetComponents;
uint8_t tlvs[32];
long value;
int length = 0;
uint8_t tlvsLength = 0;
bool destAddrSpecified = false;
otIp6Address address;
@@ -698,8 +665,6 @@ otError Dataset::ProcessMgmtGetCommand(uint8_t aArgsLength, char *aArgs[])
for (uint8_t index = 1; index < aArgsLength; index++)
{
VerifyOrExit(static_cast<size_t>(length) < sizeof(tlvs), error = OT_ERROR_NO_BUFS);
if (strcmp(aArgs[index], "activetimestamp") == 0)
{
datasetComponents.mIsActiveTimestampPresent = true;
@@ -738,18 +703,17 @@ otError Dataset::ProcessMgmtGetCommand(uint8_t aArgsLength, char *aArgs[])
}
else if (strcmp(aArgs[index], "-x") == 0)
{
uint16_t length;
VerifyOrExit(++index < aArgsLength, error = OT_ERROR_INVALID_ARGS);
value = static_cast<long>(strlen(aArgs[index]) + 1) / 2;
VerifyOrExit(static_cast<size_t>(value) <= (sizeof(tlvs) - static_cast<size_t>(length)),
error = OT_ERROR_NO_BUFS);
VerifyOrExit(Interpreter::Hex2Bin(aArgs[index], tlvs + length, static_cast<uint16_t>(value)) == value,
error = OT_ERROR_INVALID_ARGS);
length += value;
length = sizeof(tlvs);
SuccessOrExit(error = ParseAsHexString(aArgs[index], length, tlvs));
tlvsLength = static_cast<uint8_t>(length);
}
else if (strcmp(aArgs[index], "address") == 0)
{
VerifyOrExit(++index < aArgsLength, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = otIp6AddressFromString(aArgs[index], &address));
SuccessOrExit(error = ParseAsIp6Address(aArgs[index], address));
destAddrSpecified = true;
}
else
@@ -760,14 +724,12 @@ otError Dataset::ProcessMgmtGetCommand(uint8_t aArgsLength, char *aArgs[])
if (strcmp(aArgs[0], "active") == 0)
{
SuccessOrExit(error = otDatasetSendMgmtActiveGet(mInterpreter.mInstance, &datasetComponents, tlvs,
static_cast<uint8_t>(length),
SuccessOrExit(error = otDatasetSendMgmtActiveGet(mInterpreter.mInstance, &datasetComponents, tlvs, tlvsLength,
destAddrSpecified ? &address : nullptr));
}
else if (strcmp(aArgs[0], "pending") == 0)
{
SuccessOrExit(error = otDatasetSendMgmtPendingGet(mInterpreter.mInstance, &datasetComponents, tlvs,
static_cast<uint8_t>(length),
SuccessOrExit(error = otDatasetSendMgmtPendingGet(mInterpreter.mInstance, &datasetComponents, tlvs, tlvsLength,
destAddrSpecified ? &address : nullptr));
}
else
@@ -793,9 +755,7 @@ otError Dataset::ProcessPskc(uint8_t aArgsLength, char *aArgs[])
}
else if (aArgsLength == 1)
{
VerifyOrExit(Interpreter::Hex2Bin(aArgs[0], sDataset.mPskc.m8, sizeof(sDataset.mPskc)) ==
sizeof(sDataset.mPskc),
error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = ParseAsHexString(aArgs[0], sDataset.mPskc.m8));
}
#if OPENTHREAD_FTD
else if (aArgsLength == 2 && !strcmp(aArgs[0], "-p"))
@@ -862,11 +822,8 @@ otError Dataset::ProcessSecurityPolicy(uint8_t aArgsLength, char *aArgs[])
}
else
{
long value;
SuccessOrExit(error = Interpreter::ParseLong(aArgs[0], value));
sDataset.mSecurityPolicy.mRotationTime = static_cast<uint16_t>(value);
sDataset.mSecurityPolicy.mFlags = 0;
SuccessOrExit(error = ParseAsUint16(aArgs[0], sDataset.mSecurityPolicy.mRotationTime));
sDataset.mSecurityPolicy.mFlags = 0;
if (aArgsLength > 1)
{
@@ -911,13 +868,12 @@ otError Dataset::ProcessSet(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
otOperationalDatasetTlvs dataset;
int tlvsLength;
uint16_t tlvsLength;
VerifyOrExit(aArgsLength == 2, error = OT_ERROR_INVALID_ARGS);
tlvsLength = Interpreter::Hex2Bin(aArgs[1], dataset.mTlvs, sizeof(dataset.mTlvs));
VerifyOrExit((0 <= tlvsLength) && (static_cast<size_t>(tlvsLength) <= sizeof(dataset.mTlvs)),
error = OT_ERROR_INVALID_ARGS);
tlvsLength = sizeof(dataset.mTlvs);
SuccessOrExit(error = ParseAsHexString(aArgs[1], tlvsLength, dataset.mTlvs));
dataset.mLength = static_cast<uint8_t>(tlvsLength);
if (strcmp(aArgs[0], "active") == 0)
+1
View File
@@ -36,6 +36,7 @@
#include <inttypes.h>
#include "cli/cli.hpp"
#include "utils/parse_cmdline.hpp"
#if OPENTHREAD_CONFIG_JOINER_ENABLE
+3 -1
View File
@@ -38,8 +38,10 @@
#include "cli/cli.hpp"
#include "common/encoding.hpp"
#include "utils/parse_cmdline.hpp"
using ot::Encoding::BigEndian::HostSwap16;
using ot::Utils::CmdLineParser::ParseAsHexString;
namespace ot {
namespace Cli {
@@ -212,7 +214,7 @@ otError NetworkData::ProcessSteeringData(uint8_t aArgsLength, char *aArgs[])
if (error == OT_ERROR_NOT_FOUND)
{
VerifyOrExit(Interpreter::Hex2Bin(aArgs[1], addr.m8, sizeof(addr)) == sizeof(addr), OT_NOOP);
SuccessOrExit(error = ParseAsHexString(aArgs[1], addr.m8));
}
else if (error != OT_ERROR_NONE)
{
+16 -35
View File
@@ -38,9 +38,14 @@
#include "cli/cli.hpp"
#include "common/encoding.hpp"
#include "utils/parse_cmdline.hpp"
using ot::Encoding::BigEndian::HostSwap16;
using ot::Utils::CmdLineParser::ParseAsHexString;
using ot::Utils::CmdLineParser::ParseAsIp6Address;
using ot::Utils::CmdLineParser::ParseAsUint16;
namespace ot {
namespace Cli {
@@ -70,19 +75,11 @@ otError UdpExample::ProcessBind(uint8_t aArgsLength, char *aArgs[])
{
otError error;
otSockAddr sockaddr;
long value;
VerifyOrExit(aArgsLength == 2, error = OT_ERROR_INVALID_ARGS);
memset(&sockaddr, 0, sizeof(sockaddr));
error = otIp6AddressFromString(aArgs[0], &sockaddr.mAddress);
SuccessOrExit(error);
error = Interpreter::ParseLong(aArgs[1], value);
SuccessOrExit(error);
sockaddr.mPort = static_cast<uint16_t>(value);
SuccessOrExit(error = ParseAsIp6Address(aArgs[0], sockaddr.mAddress));
SuccessOrExit(error = ParseAsUint16(aArgs[1], sockaddr.mPort));
error = otUdpBind(mInterpreter.mInstance, &mSocket, &sockaddr);
@@ -94,19 +91,11 @@ otError UdpExample::ProcessConnect(uint8_t aArgsLength, char *aArgs[])
{
otError error;
otSockAddr sockaddr;
long value;
VerifyOrExit(aArgsLength == 2, error = OT_ERROR_INVALID_ARGS);
memset(&sockaddr, 0, sizeof(sockaddr));
error = otIp6AddressFromString(aArgs[0], &sockaddr.mAddress);
SuccessOrExit(error);
error = Interpreter::ParseLong(aArgs[1], value);
SuccessOrExit(error);
sockaddr.mPort = static_cast<uint16_t>(value);
SuccessOrExit(error = ParseAsIp6Address(aArgs[0], sockaddr.mAddress));
SuccessOrExit(error = ParseAsUint16(aArgs[1], sockaddr.mPort));
error = otUdpConnect(mInterpreter.mInstance, &mSocket, &sockaddr);
@@ -146,14 +135,8 @@ otError UdpExample::ProcessSend(uint8_t aArgsLength, char *aArgs[])
if (aArgsLength > 2)
{
long value;
error = otIp6AddressFromString(aArgs[curArg++], &messageInfo.mPeerAddr);
SuccessOrExit(error);
error = Interpreter::ParseLong(aArgs[curArg++], value);
SuccessOrExit(error);
messageInfo.mPeerPort = static_cast<uint16_t>(value);
SuccessOrExit(error = ParseAsIp6Address(aArgs[curArg++], messageInfo.mPeerAddr));
SuccessOrExit(error = ParseAsUint16(aArgs[curArg++], messageInfo.mPeerPort));
}
if (aArgsLength == 2 || aArgsLength == 4)
@@ -162,10 +145,8 @@ otError UdpExample::ProcessSend(uint8_t aArgsLength, char *aArgs[])
if (strcmp(aArgs[typePos], "-s") == 0)
{
unsigned long value;
payloadType = kTypeAutoSize;
SuccessOrExit(error = Interpreter::ParseUnsignedLong(aArgs[curArg], value));
payloadLength = static_cast<uint16_t>(value);
SuccessOrExit(error = ParseAsUint16(aArgs[curArg], payloadLength));
}
else if (strcmp(aArgs[typePos], "-x") == 0)
{
@@ -192,14 +173,14 @@ otError UdpExample::ProcessSend(uint8_t aArgsLength, char *aArgs[])
case kTypeHexString:
{
uint8_t buf[50];
int16_t bufLen;
uint16_t bufLen;
uint16_t conversionLength = 0;
const char *hexString = aArgs[curArg];
while (payloadLength > 0)
{
bufLen = static_cast<int16_t>(Interpreter::Hex2Bin(hexString, buf, sizeof(buf), true));
bufLen = sizeof(buf);
SuccessOrExit(error = ParseAsHexString(hexString, bufLen, buf, Utils::CmdLineParser::kAllowTruncate));
VerifyOrExit(bufLen > 0, error = OT_ERROR_INVALID_ARGS);
conversionLength = static_cast<uint16_t>(bufLen * 2);
@@ -211,7 +192,7 @@ otError UdpExample::ProcessSend(uint8_t aArgsLength, char *aArgs[])
hexString += conversionLength;
payloadLength -= conversionLength;
SuccessOrExit(error = otMessageAppend(message, buf, static_cast<uint16_t>(bufLen)));
SuccessOrExit(error = otMessageAppend(message, buf, bufLen));
}
break;
}
+261 -3
View File
@@ -31,14 +31,17 @@
* This file implements the command line parser.
*/
#include <string.h>
#include "parse_cmdline.hpp"
#include <limits>
#include <string.h>
#include "common/code_utils.hpp"
#include "net/ip6_address.hpp"
namespace ot {
namespace Utils {
namespace CmdLineParser {
static bool IsSeparator(char aChar)
{
@@ -50,7 +53,38 @@ static bool IsEscapable(char aChar)
return IsSeparator(aChar) || (aChar == '\\');
}
otError CmdLineParser::ParseCmd(char *aString, uint8_t &aArgsLength, char *aArgs[], uint8_t aArgsLengthMax)
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;
@@ -80,5 +114,229 @@ 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 <= std::numeric_limits<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((std::numeric_limits<IntType>::min() <= value) && (value <= std::numeric_limits<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>(std::numeric_limits<int32_t>::min()))
: static_cast<uint64_t>(std::numeric_limits<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, OT_NOOP);
VerifyOrExit(prefixLengthStr - aString < static_cast<int32_t>(sizeof(string)), OT_NOOP);
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
+230 -17
View File
@@ -35,10 +35,13 @@
#define PARSE_CMD_LINE_HPP_
#include <stdint.h>
#include <openthread/error.h>
#include <openthread/ip6.h>
namespace ot {
namespace Utils {
namespace CmdLineParser {
/**
* @addtogroup utils-parse-cmd-line
@@ -50,32 +53,242 @@ namespace Utils {
*/
/**
* This class implements the command line parser.
* This enumeration type represents the parse mode value used as a parameter in `ParseAsHexString()`.
*
*/
class CmdLineParser
enum HexStringParseMode : uint8_t
{
public:
/**
* This function parses the command line.
*
* Note: this method may change the input @p aString, it will put a '\0' by the end of each argument,
* and @p aArgs will point to the arguments in the input @p aString. Backslash ('\') can be used
* to escape separators (' ', '\t', '\r', '\n') and the backslash itself.
*
* @param[in] aString A null-terminated input string.
* @param[out] aArgsLength The argument counter of the command line.
* @param[out] aArgs The argument vector of the command line.
* @param[in] aArgsLengthMax The maximum argument counter.
*
*/
static otError ParseCmd(char *aString, uint8_t &aArgsLength, char *aArgs[], uint8_t aArgsLengthMax);
kDisallowTruncate, // Disallow truncation of hex string.
kAllowTruncate, // Allow truncation of hex string.
};
/**
* This function parses a given command line string and breaks it into an argument list.
*
* Note: this method may change the input @p aCommandString, it will put a '\0' by the end of each argument,
* and @p aArgs will point to the arguments in the input @p aCommandString. Backslash ('\') can be used
* to escape separators (' ', '\t', '\r', '\n') and the backslash itself.
*
* @param[in] aCommandString A null-terminated input string.
* @param[out] aArgsLength The argument counter of the command line.
* @param[out] aArgs The argument vector of the command line.
* @param[in] aArgsLengthMax The maximum argument counter.
*
*/
otError ParseCmd(char *aCommandString, uint8_t &aArgsLength, char *aArgs[], uint8_t aArgsLengthMax);
/**
* This function parses a string as a `uint8_t` value.
*
* The number in string is parsed as decimal or hex format (if contains `0x` or `0X` prefix).
*
* @param[in] aString The string to parse.
* @param[out] aUint8 A reference to an `uint8_t` variable to output the parsed value.
*
* @retval OT_ERROR_NONE The string was parsed successfully.
* @retval OT_ERROR_INVALID_ARGS The string does not contain valid number (e.g., value out of range).
*
*/
otError ParseAsUint8(const char *aString, uint8_t &aUint8);
/**
* This function parses a string as a `uint16_t` value.
*
* The number in string is parsed as decimal or hex format (if contains `0x` or `0X` prefix).
*
* @param[in] aString The string to parse.
* @param[out] aUint16 A reference to an `uint16_t` variable to output the parsed value.
*
* @retval OT_ERROR_NONE The string was parsed successfully.
* @retval OT_ERROR_INVALID_ARGS The string does not contain valid number (e.g., value out of range).
*
*/
otError ParseAsUint16(const char *aString, uint16_t &aUint16);
/**
* This function parses a string as a `uint32_t` value.
*
* The number in string is parsed as decimal or hex format (if contains `0x` or `0X` prefix).
*
* @param[in] aString The string to parse.
* @param[out] aUint32 A reference to an `uint32_t` variable to output the parsed value.
*
* @retval OT_ERROR_NONE The string was parsed successfully.
* @retval OT_ERROR_INVALID_ARGS The string does not contain valid number (e.g., value out of range).
*
*/
otError ParseAsUint32(const char *aString, uint32_t &aUint32);
/**
* This function parses a string as a `uint64_t` value.
*
* The number in string is parsed as decimal or hex format (if contains `0x` or `0X` prefix).
*
* @param[in] aString The string to parse.
* @param[out] aUint64 A reference to an `uint64_t` variable to output the parsed value.
*
* @retval OT_ERROR_NONE The string was parsed successfully.
* @retval OT_ERROR_INVALID_ARGS The string does not contain valid number (e.g., value out of range).
*
*/
otError ParseAsUint64(const char *aString, uint64_t &aUint64);
/**
* This function parses a string as a `int8_t` value.
*
* The number in string is parsed as decimal or hex format (if contains `0x` or `0X` prefix). The string can start with
* `+`/`-` sign.
*
* @param[in] aString The string to parse.
* @param[out] aInt8 A reference to an `int8_t` variable to output the parsed value.
*
* @retval OT_ERROR_NONE The string was parsed successfully.
* @retval OT_ERROR_INVALID_ARGS The string does not contain valid number (e.g., value out of range).
*
*/
otError ParseAsInt8(const char *aString, int8_t &aInt8);
/**
* This function parses a string as a `int16_t` value.
*
* The number in string is parsed as decimal or hex format (if contains `0x` or `0X` prefix). The string can start with
* `+`/`-` sign.
*
* @param[in] aString The string to parse.
* @param[out] aInt16 A reference to an `int16_t` variable to output the parsed value.
*
* @retval OT_ERROR_NONE The string was parsed successfully.
* @retval OT_ERROR_INVALID_ARGS The string does not contain valid number (e.g., value out of range).
*
*/
otError ParseAsInt16(const char *aString, int16_t &aInt16);
/**
* This function parses a string as a `int32_t` value.
*
* The number in string is parsed as decimal or hex format (if contains `0x` or `0X` prefix). The string can start with
* `+`/`-` sign.
*
* @param[in] aString The string to parse.
* @param[out] aInt32 A reference to an `int32_t` variable to output the parsed value.
*
* @retval OT_ERROR_NONE The string was parsed successfully.
* @retval OT_ERROR_INVALID_ARGS The string does not contain valid number (e.g., value out of range).
*
*/
otError ParseAsInt32(const char *aString, int32_t &aInt32);
/**
* This function parses a string as a `bool` value.
*
* Zero value is treated as `false, non-zero value as `true`.
*
* @param[in] aString The string to parse.
* @param[out] aBool A reference to a `bool` variable to output the parsed value.
*
* @retval OT_ERROR_NONE The string was parsed successfully.
* @retval OT_ERROR_INVALID_ARGS The string does not contain valid number.
*
*/
otError ParseAsBool(const char *aString, bool &aBool);
#if OPENTHREAD_FTD || OPENTHREAD_MTD
/**
* This function parses a string as an IPv6 address.
*
*
* @param[in] aString The string to parse.
* @param[out] aAddress A reference to an `otIp6Address` to output the parsed IPv6 address.
*
* @retval OT_ERROR_NONE The string was parsed successfully.
* @retval OT_ERROR_INVALID_ARGS The string does not contain valid IPv6 address.
*
*/
inline otError ParseAsIp6Address(const char *aString, otIp6Address &aAddress)
{
return otIp6AddressFromString(aString, &aAddress);
}
/**
* This function parses a string as an IPv6 prefix.
*
* The string is parsed as `{IPv6Address}/{PrefixLength}`.
*
* @param[in] aString The string to parse.
* @param[out] aPrefix A reference to an `otIp6Prefix` to output the parsed IPv6 prefix.
*
* @retval OT_ERROR_NONE The string was parsed successfully.
* @retval OT_ERROR_INVALID_ARGS The string does not contain valid IPv6 prefix
*
*/
otError ParseAsIp6Prefix(const char *aString, otIp6Prefix &aPrefix);
#endif // OPENTHREAD_FTD || OPENTHREAD_MTD
/**
* This function parses a hex string into a byte array of fixed expected size.
*
* This function returns `OT_ERROR_NONE` only when the hex string contains exactly @p aSize bytes (after parsing). If
* there are fewer or more bytes in hex string that @p aSize, the parsed bytes (up to @p aSize) are copied into the
* `aBuffer` and `OT_ERROR_INVALID_ARGS` is returned.
*
* @param[in] aString The string to parse.
* @param[out] aBuffer A pointer to a buffer to output the parsed byte sequence.
* @param[in] aSize The expected size of byte sequence (number of bytes after parsing).
*
* @retval OT_ERROR_NONE The string was parsed successfully.
* @retval OT_ERROR_INVALID_ARGS The string does not contain valid hex bytes and/or not @p aSize bytes.
*
*/
otError ParseAsHexString(const char *aString, uint8_t *aBuffer, uint16_t aSize);
/**
* This template function parses a hex string into a a given fixed size array.
*
* This function returns `OT_ERROR_NONE` only when the hex string contains exactly @p kBufferSize bytes (after parsing).
* If there are fewer or more bytes in hex string that @p kBufferSize, the parsed bytes (up to @p kBufferSize) are
* copied into the `aBuffer` and `OT_ERROR_INVALID_ARGS` is returned.
*
* @tparam kBufferSize The byte array size (number of bytes).
*
* @param[in] aString The string to parse.
* @param[out] aBuffer A reference to a byte array to output the parsed byte sequence.
*
* @retval OT_ERROR_NONE The string was parsed successfully.
* @retval OT_ERROR_INVALID_ARGS The string does not contain valid hex bytes and/or not @p aSize bytes.
*
*/
template <uint16_t kBufferSize> static otError ParseAsHexString(const char *aString, uint8_t (&aBuffer)[kBufferSize])
{
return ParseAsHexString(aString, aBuffer, kBufferSize);
}
/**
* This function parses a hex string into a byte array.
*
* If @p aMode disallows truncation (`kDisallowTruncate`), this function verifies that parses hex string bytes fit in
* @p aBuffer with its given size in @aSize. Otherwise when @p aMode allows truncation, extra bytes after @p aSize bytes
* are ignored.
*
* @param[in] aString The string to parse.
* @param[inout] aSize On entry indicates the number of bytes in @p aBuffer (max size of @p aBuffer).
* On exit provides number of bytes parsed and copied into @p aBuffer
* @param[out] aBuffer A pointer to a buffer to output the parsed byte sequence.
* @param[in] aMode Indicates parsing mode whether to allow truncation or not.
*
* @retval OT_ERROR_NONE The string was parsed successfully.
* @retval OT_ERROR_INVALID_ARGS The string does not contain valid format or too many bytes (if truncation not allowed)
*
*/
otError ParseAsHexString(const char * aString,
uint16_t & aSize,
uint8_t * aBuffer,
HexStringParseMode aMode = kDisallowTruncate);
/**
* @}
*/
} // namespace CmdLineParser
} // namespace Utils
} // namespace ot
+23
View File
@@ -139,6 +139,28 @@ target_link_libraries(test-child-table
add_test(NAME test-child-table COMMAND test-child-table)
add_executable(test-cmd-line-parser
${COMMON_SOURCES}
test_cmd_line_parser.cpp
)
target_include_directories(test-cmd-line-parser
PRIVATE
${COMMON_INCLUDES}
)
target_compile_options(test-cmd-line-parser
PRIVATE
${COMMON_COMPILE_OPTIONS}
)
target_link_libraries(test-cmd-line-parser
PRIVATE
${COMMON_LIBS}
)
add_test(NAME test-cmd-line-parser COMMAND test-cmd-line-parser)
add_executable(test-flash
${COMMON_SOURCES}
test_flash.cpp
@@ -608,6 +630,7 @@ set_target_properties(
test-checksum
test-child
test-child-table
test-cmd-line-parser
test-flash
test-heap
test-hmac-sha256
+4
View File
@@ -110,6 +110,7 @@ check_PROGRAMS += \
test-checksum \
test-child \
test-child-table \
test-cmd-line-parser \
test-flash \
test-heap \
test-hmac-sha256 \
@@ -183,6 +184,9 @@ test_child_SOURCES = $(COMMON_SOURCES) test_child.cpp
test_child_table_LDADD = $(COMMON_LDADD)
test_child_table_SOURCES = $(COMMON_SOURCES) test_child_table.cpp
test_cmd_line_parser_LDADD = $(COMMON_LDADD)
test_cmd_line_parser_SOURCES = $(COMMON_SOURCES) test_cmd_line_parser.cpp
test_flash_LDADD = $(COMMON_LDADD)
test_flash_SOURCES = $(COMMON_SOURCES) test_flash.cpp
+317
View File
@@ -0,0 +1,317 @@
/*
* Copyright (c) 2020, 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.
*/
#include <string.h>
#include "test_platform.h"
#include <openthread/config.h>
#include "common/instance.hpp"
#include "utils/parse_cmdline.hpp"
#include "test_util.h"
using ot::Utils::CmdLineParser::ParseAsBool;
using ot::Utils::CmdLineParser::ParseAsHexString;
using ot::Utils::CmdLineParser::ParseAsInt16;
using ot::Utils::CmdLineParser::ParseAsInt32;
using ot::Utils::CmdLineParser::ParseAsInt8;
using ot::Utils::CmdLineParser::ParseAsIp6Address;
using ot::Utils::CmdLineParser::ParseAsIp6Prefix;
using ot::Utils::CmdLineParser::ParseAsUint16;
using ot::Utils::CmdLineParser::ParseAsUint32;
using ot::Utils::CmdLineParser::ParseAsUint64;
using ot::Utils::CmdLineParser::ParseAsUint8;
template <typename ValueType> struct TestCase
{
const char *mString;
otError mError;
ValueType mValue;
};
template <typename ValueType, otError (&Parser)(const char *aString, ValueType &aValue)>
void VerifyParser(const TestCase<ValueType> *aTestCases, const char *aParserName, const char *aPrintFormat)
{
const TestCase<ValueType> *testCase = aTestCases;
ValueType value;
otError error;
printf("----------------------------------------------------------\n");
while (true)
{
printf("%s(\"%s\") -> ", aParserName, testCase->mString);
if (testCase->mError != OT_ERROR_NONE)
{
printf("error:%s", otThreadErrorToString(testCase->mError));
}
else
{
printf(aPrintFormat, testCase->mValue);
}
printf("\n");
error = Parser(testCase->mString, value);
VerifyOrQuit(error == testCase->mError, "Parser did not return the expected error");
if (error == OT_ERROR_NONE)
{
VerifyOrQuit(value == testCase->mValue, "Parser failed");
}
if (testCase->mString[0] == '\0')
{
break;
}
testCase++;
}
}
void TestParsingInts(void)
{
TestCase<bool> kBoolTestCases[] = {
{"0", OT_ERROR_NONE, false}, // Zero as false value
{"1", OT_ERROR_NONE, true}, // Non-zero as true value
{"0x0", OT_ERROR_NONE, false}, // Zero as false value
{"0x1", OT_ERROR_NONE, true}, // Non-zero (in hex) as true value
{"10", OT_ERROR_NONE, true}, // Non-zero as true value
{"a", OT_ERROR_INVALID_ARGS, false}, // Error case: Incorrect char
{"-1", OT_ERROR_INVALID_ARGS, false}, // Error case: Negative value
{"", OT_ERROR_INVALID_ARGS, false}, // Empty string indicate end of the list
};
TestCase<uint8_t> kUint8TestCases[] = {
{"0", OT_ERROR_NONE, 0},
{"1", OT_ERROR_NONE, 1},
{"74", OT_ERROR_NONE, 74},
{"255", OT_ERROR_NONE, 255}, // Max `uint8` value (decimal format)
{"0xa", OT_ERROR_NONE, 0xa},
{"0x04", OT_ERROR_NONE, 4},
{"0x7e", OT_ERROR_NONE, 0x7e},
{"0xcd", OT_ERROR_NONE, 0xcd},
{"0x0", OT_ERROR_NONE, 0},
{"0xff", OT_ERROR_NONE, 0xff}, // Max `uint8` value (hex format)
{"0x0000ff", OT_ERROR_NONE, 0xff}, // Hex format (extra zeros)
{"0xB", OT_ERROR_NONE, 0xb},
{"0X04", OT_ERROR_NONE, 4},
{"0X7E", OT_ERROR_NONE, 0x7e},
{"0XCD", OT_ERROR_NONE, 0xcd},
{"0X0", OT_ERROR_NONE, 0},
{"0XFF", OT_ERROR_NONE, 0xff},
{"00", OT_ERROR_NONE, 0},
{"-5", OT_ERROR_INVALID_ARGS, 0}, // Error case: Negative value.
{"0y", OT_ERROR_INVALID_ARGS, 0}, // Error case: Incorrect prefix.
{"0x7g", OT_ERROR_INVALID_ARGS, 0}, // Error case: Bad hex char.
{"0xaaa", OT_ERROR_INVALID_ARGS, 0}, // Error case: Out or range.
{"256", OT_ERROR_INVALID_ARGS, 0}, // Error case: Out of range (max value + 1)
{"12e", OT_ERROR_INVALID_ARGS, 0}, // Error case: Extra char.
{"", OT_ERROR_INVALID_ARGS, 0} // Empty string indicates end of the list
};
TestCase<uint16_t> kUint16TestCases[] = {
{"0", OT_ERROR_NONE, 0},
{"1245", OT_ERROR_NONE, 1245},
{"0xa", OT_ERROR_NONE, 0xa},
{"0xab7d", OT_ERROR_NONE, 0xab7d},
{"0X1AE", OT_ERROR_NONE, 0x1ae},
{"0X7E", OT_ERROR_NONE, 0x7e},
{"65535", OT_ERROR_NONE, 65535}, // Max `uint16` value (decimal format)
{"0xffff", OT_ERROR_NONE, 0xffff}, // Max `uint16` value (hex format)
{"-1", OT_ERROR_INVALID_ARGS, 0}, // Error case: Negative value
{"0y", OT_ERROR_INVALID_ARGS, 0}, // Error case: Incorrect prefix
{"0xq", OT_ERROR_INVALID_ARGS, 0}, // Error case: Bad hex char.
{"0x12345", OT_ERROR_INVALID_ARGS, 0}, // Error case: Out of range.
{"65536", OT_ERROR_INVALID_ARGS, 0}, // Error case: Out of range (max value + 1)
{"", OT_ERROR_INVALID_ARGS, 0} // Empty string indicates end of the list
};
TestCase<uint32_t> kUint32TestCases[] = {
{"0", OT_ERROR_NONE, 0},
{"1234567", OT_ERROR_NONE, 1234567},
{"0xc", OT_ERROR_NONE, 0xc},
{"0x01234567", OT_ERROR_NONE, 0x1234567},
{"0XABCDEF09", OT_ERROR_NONE, 0xabcdef09},
{"0X54321", OT_ERROR_NONE, 0x54321},
{"4294967295", OT_ERROR_NONE, 4294967295}, // Max `uint32` value (decimal format)
{"0xffffffff", OT_ERROR_NONE, 0xffffffff}, // Max `uint32` value (hex format)
{"-1", OT_ERROR_INVALID_ARGS, 0},
{"0y", OT_ERROR_INVALID_ARGS, 0},
{"0x1234zz", OT_ERROR_INVALID_ARGS, 0}, // Error case: Bad hex char
{"0x123456789", OT_ERROR_INVALID_ARGS, 0}, // Error case: Out of range
{"4294967296", OT_ERROR_INVALID_ARGS, 0}, // Error case: Out of range (max value + 1)
{"", OT_ERROR_INVALID_ARGS, 0} // Empty string indicates end of the list.
};
TestCase<uint64_t> kUint64TestCases[] = {
{"0", OT_ERROR_NONE, 0},
{"123456789087654321", OT_ERROR_NONE, 123456789087654321},
{"0xb", OT_ERROR_NONE, 0xb},
{"0x1234567890acbdef", OT_ERROR_NONE, 0x1234567890acbdef},
{"0XFEDCBA9876543210", OT_ERROR_NONE, 0xfedcba9876543210},
{"0xffffffffffffffff", OT_ERROR_NONE, 0xffffffffffffffff}, // Max `uint64` value (hex format)
{"18446744073709551615", OT_ERROR_NONE, 18446744073709551615ull}, // Max `uint64` value (decimal format)
{"-1", OT_ERROR_INVALID_ARGS, 0},
{"0x1234567890acbdef0", OT_ERROR_INVALID_ARGS, 0}, // Error case: Out of range
{"18446744073709551616", OT_ERROR_INVALID_ARGS, 0}, // Error case: Out or range (max value + 1)
{"", OT_ERROR_INVALID_ARGS, 0} // Empty string indicates end of the list.
};
TestCase<int8_t> kInt8TestCases[] = {
{"0", OT_ERROR_NONE, 0},
{"-1", OT_ERROR_NONE, -1},
{"+74", OT_ERROR_NONE, 74},
{"-0x12", OT_ERROR_NONE, -0x12},
{"-0XB", OT_ERROR_NONE, -11},
{"127", OT_ERROR_NONE, 127}, // Max `int8` value
{"-128", OT_ERROR_NONE, -128}, // Min `int8` value
{"128", OT_ERROR_INVALID_ARGS, 0}, // Error case: Out of range (max value + 1)
{"-129", OT_ERROR_INVALID_ARGS, 0}, // Error case: Out of range (min value - 1)
{"--1", OT_ERROR_INVALID_ARGS, 0}, // Error case: Extra sign
{"+-2", OT_ERROR_INVALID_ARGS, 0}, // Error case: Extra sign
{"++1", OT_ERROR_INVALID_ARGS, 0}, // Error case: Extra sign
{"", OT_ERROR_INVALID_ARGS, 0} // Empty string indicates end of the list.
};
TestCase<int16_t> kInt16TestCases[] = {
{"-1", OT_ERROR_NONE, -1},
{"+0x1234", OT_ERROR_NONE, 0x1234},
{"-0X6E8", OT_ERROR_NONE, -0x6E8},
{"32767", OT_ERROR_NONE, 32767}, // Max `int16` value
{"0X7FFF", OT_ERROR_NONE, 0x7fff}, // Max `int16` value (hex value)
{"-32768", OT_ERROR_NONE, -32768}, // Min `int16` value
{"-0x8000", OT_ERROR_NONE, -0x8000}, // Min `int16` value (hex value)
{"32768", OT_ERROR_INVALID_ARGS, 0}, // Error case: Out of range (max + 1)
{"0X8000", OT_ERROR_INVALID_ARGS, 0}, // Error case: Out of range (max + 1)
{"-32769", OT_ERROR_INVALID_ARGS, 0}, // Error case: Out of range (min - 1)
{"-0x8001", OT_ERROR_INVALID_ARGS, 0}, // Error case: Out of range (min - 1)
{"", OT_ERROR_INVALID_ARGS, 0} // Empty string indicates end of the list.
};
TestCase<int32_t> kInt32TestCases[] = {
{"-256", OT_ERROR_NONE, -256},
{"+0x12345678", OT_ERROR_NONE, 0x12345678},
{"-0X6677aB", OT_ERROR_NONE, -0X6677aB},
{"2147483647", OT_ERROR_NONE, 2147483647},
{"0x7fffFFFF", OT_ERROR_NONE, 0x7fffffff}, // Max `int32` value
{"-2147483648", OT_ERROR_NONE, -2147483648}, // Min `int32` value
{"2147483648", OT_ERROR_INVALID_ARGS, 0}, // Error case: Out of range (max + 1)
{"0X80000000", OT_ERROR_INVALID_ARGS, 0}, // Error case: Out of range (max + 1)
{"-2147483649", OT_ERROR_INVALID_ARGS, 0}, // Error case: Out of range (min - 1)
{"-0x80000001", OT_ERROR_INVALID_ARGS, 0}, // Error case: Out of range (min - 1)
{"", OT_ERROR_INVALID_ARGS, 0} // Empty string indicates end of the list
};
VerifyParser<bool, ParseAsBool>(kBoolTestCases, "ParseAsBool", "%d");
VerifyParser<uint8_t, ParseAsUint8>(kUint8TestCases, "ParseAsUint8", "0x%02x");
VerifyParser<uint16_t, ParseAsUint16>(kUint16TestCases, "ParseAsUint16", "0x%04x");
VerifyParser<uint32_t, ParseAsUint32>(kUint32TestCases, "ParseAsUint32", "0x%08x");
VerifyParser<uint64_t, ParseAsUint64>(kUint64TestCases, "ParseAsUint64", "0x%016llx");
VerifyParser<int8_t, ParseAsInt8>(kInt8TestCases, "ParseAsInt8", "%d");
VerifyParser<int16_t, ParseAsInt16>(kInt16TestCases, "ParseAsInt16", "%d");
VerifyParser<int32_t, ParseAsInt32>(kInt32TestCases, "ParseAsInt32", "%d");
}
void TestParsingHexStrings(void)
{
const char kHexString[] = "DeadBeefCafeBabe";
const uint8_t kParsedArray[] = {0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0xba, 0xbe};
uint8_t buffer[sizeof(kParsedArray)];
uint8_t buf3[3];
uint16_t len;
// Verify `ParseAsHexString(const char *aString, uint8_t *aBuffer, uint16_t aSize)`
buffer[0] = 0xff;
SuccessOrQuit(ParseAsHexString("0", buffer, 1), "ParseAsHexString() failed");
VerifyOrQuit(buffer[0] == 0, "ParseAsHexString() parsed incorrectly");
buffer[0] = 0;
SuccessOrQuit(ParseAsHexString("7e", buffer, 1), "ParseAsHexString() failed");
VerifyOrQuit(buffer[0] == 0x7e, "ParseAsHexString() parsed incorrectly");
VerifyOrQuit(ParseAsHexString("123", buffer, 1) != OT_ERROR_NONE, "ParseAsHexString() passed with bad input");
SuccessOrQuit(ParseAsHexString("123", buffer, 2), "ParseAsHexString() failed");
VerifyOrQuit(buffer[0] == 1 && buffer[1] == 0x23, "ParseAsHexString() parsed incorrectly");
VerifyOrQuit(ParseAsHexString("123x", buffer, 2) != OT_ERROR_NONE, "ParseAsHexString() passed with bad input");
VerifyOrQuit(ParseAsHexString(" 123", buffer, 2) != OT_ERROR_NONE, "ParseAsHexString() passed with bad input");
// Verify `ParseAsHexString<kMaxSize>()`
VerifyOrQuit(ParseAsHexString("1122", buf3) != OT_ERROR_NONE, "ParseAsHexString() passed with bad input");
VerifyOrQuit(ParseAsHexString("1122334", buf3) != OT_ERROR_NONE, "ParseAsHexString() passed with bad input");
VerifyOrQuit(ParseAsHexString("11223344", buf3) != OT_ERROR_NONE, "ParseAsHexString() passed with bad input");
SuccessOrQuit(ParseAsHexString("abbade", buf3), "ParseAsHexString() failed");
VerifyOrQuit(buf3[0] == 0xab && buf3[1] == 0xba && buf3[2] == 0xde, "ParseAsHexString() parsed incorrectly");
SuccessOrQuit(ParseAsHexString(kHexString, buffer), "ParseAsHexString failed");
VerifyOrQuit(memcmp(buffer, kParsedArray, sizeof(buffer)) == 0, "ParseAsHexString() parsed incorrectly");
// Verify truncation
len = sizeof(buffer);
SuccessOrQuit(ParseAsHexString(kHexString, len, buffer), "ParseAsHexString failed");
VerifyOrQuit(len == sizeof(kParsedArray), "ParseAsHexString() parsed incorrectly");
VerifyOrQuit(memcmp(buffer, kParsedArray, len) == 0, "ParseAsHexString() parsed incorrectly");
SuccessOrQuit(ParseAsHexString(kHexString + 1, len, buffer), "ParseAsHexString failed");
VerifyOrQuit(len == sizeof(kParsedArray), "ParseAsHexString() parsed incorrectly");
VerifyOrQuit(memcmp(buffer + 1, kParsedArray + 1, len - 1) == 0, "ParseAsHexString() parsed incorrectly");
VerifyOrQuit(buffer[0] = 0xe, "ParseAsHexString() parsed incorrectly");
SuccessOrQuit(ParseAsHexString(kHexString + 2, len, buffer), "ParseAsHexString failed");
VerifyOrQuit(len == sizeof(kParsedArray) - 1, "ParseAsHexString() parsed incorrectly");
VerifyOrQuit(memcmp(buffer, kParsedArray + 1, len) == 0, "ParseAsHexString() parsed incorrectly");
len = sizeof(buffer) - 1;
VerifyOrQuit(ParseAsHexString(kHexString, len, buffer, ot::Utils::CmdLineParser::kDisallowTruncate) !=
OT_ERROR_NONE,
"ParseAsHexString passed with bad input");
len = sizeof(buffer) - 1;
SuccessOrQuit(ParseAsHexString(kHexString, len, buffer, ot::Utils::CmdLineParser::kAllowTruncate),
"ParseAsHexString failed");
VerifyOrQuit(len == sizeof(buffer) - 1, "ParseAsHexString() parsed incorrectly");
VerifyOrQuit(memcmp(buffer, kParsedArray, len) == 0, "ParseAsHexString() parsed incorrectly");
}
int main(void)
{
TestParsingInts();
TestParsingHexStrings();
printf("All tests passed\n");
return 0;
}