1 /*
2 * Copyright (c) 2018, The OpenThread Authors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. Neither the name of the copyright holder nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /**
30 * @file
31 * This file implements the command line parser.
32 */
33
34 #include "parse_cmdline.hpp"
35
36 #include <string.h>
37
38 #include "common/code_utils.hpp"
39 #include "common/numeric_limits.hpp"
40 #include "common/string.hpp"
41 #include "net/ip6_address.hpp"
42
43 namespace ot {
44 namespace Utils {
45 namespace CmdLineParser {
46
IsSeparator(char aChar)47 static bool IsSeparator(char aChar) { return (aChar == ' ') || (aChar == '\t') || (aChar == '\r') || (aChar == '\n'); }
48
IsEscapable(char aChar)49 static bool IsEscapable(char aChar) { return IsSeparator(aChar) || (aChar == '\\'); }
50
ParseCmd(char * aCommandString,Arg aArgs[],uint8_t aArgsMaxLength)51 Error ParseCmd(char *aCommandString, Arg aArgs[], uint8_t aArgsMaxLength)
52 {
53 Error error = kErrorNone;
54 uint8_t index = 0;
55 char *cmd;
56
57 for (cmd = aCommandString; *cmd; cmd++)
58 {
59 if ((*cmd == '\\') && IsEscapable(*(cmd + 1)))
60 {
61 // include the null terminator: strlen(cmd) = strlen(cmd + 1) + 1
62 memmove(cmd, cmd + 1, strlen(cmd));
63 }
64 else if (IsSeparator(*cmd))
65 {
66 *cmd = '\0';
67 }
68
69 if ((*cmd != '\0') && ((index == 0) || (*(cmd - 1) == '\0')))
70 {
71 if (index == aArgsMaxLength - 1)
72 {
73 error = kErrorInvalidArgs;
74 break;
75 }
76
77 aArgs[index++].SetCString(cmd);
78 }
79 }
80
81 while (index < aArgsMaxLength)
82 {
83 aArgs[index++].Clear();
84 }
85
86 return error;
87 }
88
ParseUint(const char * aString,UintType & aUint)89 template <typename UintType> Error ParseUint(const char *aString, UintType &aUint)
90 {
91 Error error;
92 uint64_t value;
93
94 SuccessOrExit(error = ParseAsUint64(aString, value));
95
96 VerifyOrExit(value <= NumericLimits<UintType>::kMax, error = kErrorInvalidArgs);
97 aUint = static_cast<UintType>(value);
98
99 exit:
100 return error;
101 }
102
ParseAsUint8(const char * aString,uint8_t & aUint8)103 Error ParseAsUint8(const char *aString, uint8_t &aUint8) { return ParseUint<uint8_t>(aString, aUint8); }
104
ParseAsUint16(const char * aString,uint16_t & aUint16)105 Error ParseAsUint16(const char *aString, uint16_t &aUint16) { return ParseUint<uint16_t>(aString, aUint16); }
106
ParseAsUint32(const char * aString,uint32_t & aUint32)107 Error ParseAsUint32(const char *aString, uint32_t &aUint32) { return ParseUint<uint32_t>(aString, aUint32); }
108
ParseAsUint64(const char * aString,uint64_t & aUint64)109 Error ParseAsUint64(const char *aString, uint64_t &aUint64)
110 {
111 Error error = kErrorNone;
112 uint64_t value = 0;
113 const char *cur = aString;
114 bool isHex = false;
115
116 enum : uint64_t
117 {
118 kMaxHexBeforeOverflow = (0xffffffffffffffffULL / 16),
119 kMaxDecBeforeOverflow = (0xffffffffffffffffULL / 10),
120 };
121
122 VerifyOrExit(aString != nullptr, error = kErrorInvalidArgs);
123
124 if (cur[0] == '0' && (cur[1] == 'x' || cur[1] == 'X'))
125 {
126 cur += 2;
127 isHex = true;
128 }
129
130 do
131 {
132 uint8_t digit;
133 uint64_t newValue;
134
135 SuccessOrExit(error = isHex ? ParseHexDigit(*cur, digit) : ParseDigit(*cur, digit));
136 VerifyOrExit(value <= (isHex ? kMaxHexBeforeOverflow : kMaxDecBeforeOverflow), error = kErrorInvalidArgs);
137 value = isHex ? (value << 4) : (value * 10);
138 newValue = value + digit;
139 VerifyOrExit(newValue >= value, error = kErrorInvalidArgs);
140 value = newValue;
141 cur++;
142 } while (*cur != '\0');
143
144 aUint64 = value;
145
146 exit:
147 return error;
148 }
149
ParseInt(const char * aString,IntType & aInt)150 template <typename IntType> Error ParseInt(const char *aString, IntType &aInt)
151 {
152 Error error;
153 int32_t value;
154
155 SuccessOrExit(error = ParseAsInt32(aString, value));
156
157 VerifyOrExit((NumericLimits<IntType>::kMin <= value) && (value <= NumericLimits<IntType>::kMax),
158 error = kErrorInvalidArgs);
159 aInt = static_cast<IntType>(value);
160
161 exit:
162 return error;
163 }
164
ParseAsInt8(const char * aString,int8_t & aInt8)165 Error ParseAsInt8(const char *aString, int8_t &aInt8) { return ParseInt<int8_t>(aString, aInt8); }
166
ParseAsInt16(const char * aString,int16_t & aInt16)167 Error ParseAsInt16(const char *aString, int16_t &aInt16) { return ParseInt<int16_t>(aString, aInt16); }
168
ParseAsInt32(const char * aString,int32_t & aInt32)169 Error ParseAsInt32(const char *aString, int32_t &aInt32)
170 {
171 Error error;
172 uint64_t value;
173 bool isNegative = false;
174
175 VerifyOrExit(aString != nullptr, error = kErrorInvalidArgs);
176
177 if (*aString == '-')
178 {
179 aString++;
180 isNegative = true;
181 }
182 else if (*aString == '+')
183 {
184 aString++;
185 }
186
187 SuccessOrExit(error = ParseAsUint64(aString, value));
188 VerifyOrExit(value <= (isNegative ? static_cast<uint64_t>(-static_cast<int64_t>(NumericLimits<int32_t>::kMin))
189 : static_cast<uint64_t>(NumericLimits<int32_t>::kMax)),
190 error = kErrorInvalidArgs);
191 aInt32 = static_cast<int32_t>(isNegative ? -static_cast<int64_t>(value) : static_cast<int64_t>(value));
192
193 exit:
194 return error;
195 }
196
ParseAsBool(const char * aString,bool & aBool)197 Error ParseAsBool(const char *aString, bool &aBool)
198 {
199 Error error;
200 uint32_t value;
201
202 SuccessOrExit(error = ParseAsUint32(aString, value));
203 aBool = (value != 0);
204
205 exit:
206 return error;
207 }
208 #if OPENTHREAD_FTD || OPENTHREAD_MTD
209
ParseAsIp6Address(const char * aString,otIp6Address & aAddress)210 Error ParseAsIp6Address(const char *aString, otIp6Address &aAddress)
211 {
212 return (aString != nullptr) ? otIp6AddressFromString(aString, &aAddress) : kErrorInvalidArgs;
213 }
214
ParseAsIp4Address(const char * aString,otIp4Address & aAddress)215 Error ParseAsIp4Address(const char *aString, otIp4Address &aAddress)
216 {
217 return (aString != nullptr) ? otIp4AddressFromString(aString, &aAddress) : kErrorInvalidArgs;
218 }
219
ParseAsIp6Prefix(const char * aString,otIp6Prefix & aPrefix)220 Error ParseAsIp6Prefix(const char *aString, otIp6Prefix &aPrefix)
221 {
222 return (aString != nullptr) ? otIp6PrefixFromString(aString, &aPrefix) : kErrorInvalidArgs;
223 }
224 #endif // #if OPENTHREAD_FTD || OPENTHREAD_MTD
225
226 enum HexStringParseMode
227 {
228 kModeExactSize, // Parse hex string expecting an exact size (number of bytes when parsed).
229 kModeUpToSize, // Parse hex string expecting less than or equal a given size.
230 kModeAllowPartial, // Allow parsing of partial segments.
231 };
232
ParseHexString(const char * & aString,uint16_t & aSize,uint8_t * aBuffer,HexStringParseMode aMode)233 static Error ParseHexString(const char *&aString, uint16_t &aSize, uint8_t *aBuffer, HexStringParseMode aMode)
234 {
235 Error error = kErrorNone;
236 size_t parsedSize = 0;
237 size_t stringLength;
238 size_t expectedSize;
239 bool skipFirstDigit;
240
241 VerifyOrExit(aString != nullptr, error = kErrorInvalidArgs);
242
243 stringLength = strlen(aString);
244 expectedSize = (stringLength + 1) / 2;
245
246 switch (aMode)
247 {
248 case kModeExactSize:
249 VerifyOrExit(expectedSize == aSize, error = kErrorInvalidArgs);
250 break;
251 case kModeUpToSize:
252 VerifyOrExit(expectedSize <= aSize, error = kErrorInvalidArgs);
253 break;
254 case kModeAllowPartial:
255 break;
256 }
257
258 // If number of chars in hex string is odd, we skip parsing
259 // the first digit.
260
261 skipFirstDigit = ((stringLength & 1) != 0);
262
263 while (parsedSize < expectedSize)
264 {
265 uint8_t digit;
266
267 if ((aMode == kModeAllowPartial) && (parsedSize == aSize))
268 {
269 // If partial parse mode is allowed, stop once we read the
270 // requested size.
271 ExitNow(error = kErrorPending);
272 }
273
274 if (skipFirstDigit)
275 {
276 *aBuffer = 0;
277 skipFirstDigit = false;
278 }
279 else
280 {
281 SuccessOrExit(error = ParseHexDigit(*aString, digit));
282 aString++;
283 *aBuffer = static_cast<uint8_t>(digit << 4);
284 }
285
286 SuccessOrExit(error = ParseHexDigit(*aString, digit));
287 aString++;
288 *aBuffer |= digit;
289
290 aBuffer++;
291 parsedSize++;
292 }
293
294 aSize = static_cast<uint16_t>(parsedSize);
295
296 exit:
297 return error;
298 }
299
ParseAsHexString(const char * aString,uint8_t * aBuffer,uint16_t aSize)300 Error ParseAsHexString(const char *aString, uint8_t *aBuffer, uint16_t aSize)
301 {
302 return ParseHexString(aString, aSize, aBuffer, kModeExactSize);
303 }
304
ParseAsHexString(const char * aString,uint16_t & aSize,uint8_t * aBuffer)305 Error ParseAsHexString(const char *aString, uint16_t &aSize, uint8_t *aBuffer)
306 {
307 return ParseHexString(aString, aSize, aBuffer, kModeUpToSize);
308 }
309
ParseAsHexStringSegment(const char * & aString,uint16_t & aSize,uint8_t * aBuffer)310 Error ParseAsHexStringSegment(const char *&aString, uint16_t &aSize, uint8_t *aBuffer)
311 {
312 return ParseHexString(aString, aSize, aBuffer, kModeAllowPartial);
313 }
314
315 //---------------------------------------------------------------------------------------------------------------------
316 // Arg class
317
GetLength(void) const318 uint16_t Arg::GetLength(void) const { return IsEmpty() ? 0 : static_cast<uint16_t>(strlen(mString)); }
319
operator ==(const char * aString) const320 bool Arg::operator==(const char *aString) const { return !IsEmpty() && StringMatch(mString, aString); }
321
CopyArgsToStringArray(Arg aArgs[],char * aStrings[])322 void Arg::CopyArgsToStringArray(Arg aArgs[], char *aStrings[])
323 {
324 for (uint8_t i = 0; !aArgs[i].IsEmpty(); i++)
325 {
326 aStrings[i] = aArgs[i].GetCString();
327 }
328 }
329
GetArgsLength(Arg aArgs[])330 uint8_t Arg::GetArgsLength(Arg aArgs[])
331 {
332 uint8_t length = 0;
333
334 while (!aArgs[length].IsEmpty())
335 {
336 length++;
337 }
338
339 return length;
340 }
341
342 } // namespace CmdLineParser
343 } // namespace Utils
344 } // namespace ot
345