• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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)
48 {
49     return (aChar == ' ') || (aChar == '\t') || (aChar == '\r') || (aChar == '\n');
50 }
51 
IsEscapable(char aChar)52 static bool IsEscapable(char aChar)
53 {
54     return IsSeparator(aChar) || (aChar == '\\');
55 }
56 
ParseDigit(char aDigitChar,uint8_t & aValue)57 static Error ParseDigit(char aDigitChar, uint8_t &aValue)
58 {
59     Error error = kErrorNone;
60 
61     VerifyOrExit(('0' <= aDigitChar) && (aDigitChar <= '9'), error = kErrorInvalidArgs);
62     aValue = static_cast<uint8_t>(aDigitChar - '0');
63 
64 exit:
65     return error;
66 }
67 
ParseHexDigit(char aHexChar,uint8_t & aValue)68 static Error ParseHexDigit(char aHexChar, uint8_t &aValue)
69 {
70     Error error = kErrorNone;
71 
72     if (('A' <= aHexChar) && (aHexChar <= 'F'))
73     {
74         ExitNow(aValue = static_cast<uint8_t>(aHexChar - 'A' + 10));
75     }
76 
77     if (('a' <= aHexChar) && (aHexChar <= 'f'))
78     {
79         ExitNow(aValue = static_cast<uint8_t>(aHexChar - 'a' + 10));
80     }
81 
82     error = ParseDigit(aHexChar, aValue);
83 
84 exit:
85     return error;
86 }
87 
ParseCmd(char * aCommandString,Arg aArgs[],uint8_t aArgsMaxLength)88 Error ParseCmd(char *aCommandString, Arg aArgs[], uint8_t aArgsMaxLength)
89 {
90     Error   error = kErrorNone;
91     uint8_t index = 0;
92     char *  cmd;
93 
94     for (cmd = aCommandString; *cmd; cmd++)
95     {
96         if ((*cmd == '\\') && IsEscapable(*(cmd + 1)))
97         {
98             // include the null terminator: strlen(cmd) = strlen(cmd + 1) + 1
99             memmove(cmd, cmd + 1, strlen(cmd));
100         }
101         else if (IsSeparator(*cmd))
102         {
103             *cmd = '\0';
104         }
105 
106         if ((*cmd != '\0') && ((index == 0) || (*(cmd - 1) == '\0')))
107         {
108             if (index == aArgsMaxLength - 1)
109             {
110                 error = kErrorInvalidArgs;
111                 break;
112             }
113 
114             aArgs[index++].SetCString(cmd);
115         }
116     }
117 
118     while (index < aArgsMaxLength)
119     {
120         aArgs[index++].Clear();
121     }
122 
123     return error;
124 }
125 
ParseUint(const char * aString,UintType & aUint)126 template <typename UintType> Error ParseUint(const char *aString, UintType &aUint)
127 {
128     Error    error;
129     uint64_t value;
130 
131     SuccessOrExit(error = ParseAsUint64(aString, value));
132 
133     VerifyOrExit(value <= NumericLimits<UintType>::kMax, error = kErrorInvalidArgs);
134     aUint = static_cast<UintType>(value);
135 
136 exit:
137     return error;
138 }
139 
ParseAsUint8(const char * aString,uint8_t & aUint8)140 Error ParseAsUint8(const char *aString, uint8_t &aUint8)
141 {
142     return ParseUint<uint8_t>(aString, aUint8);
143 }
144 
ParseAsUint16(const char * aString,uint16_t & aUint16)145 Error ParseAsUint16(const char *aString, uint16_t &aUint16)
146 {
147     return ParseUint<uint16_t>(aString, aUint16);
148 }
149 
ParseAsUint32(const char * aString,uint32_t & aUint32)150 Error ParseAsUint32(const char *aString, uint32_t &aUint32)
151 {
152     return ParseUint<uint32_t>(aString, aUint32);
153 }
154 
ParseAsUint64(const char * aString,uint64_t & aUint64)155 Error ParseAsUint64(const char *aString, uint64_t &aUint64)
156 {
157     Error       error = kErrorNone;
158     uint64_t    value = 0;
159     const char *cur   = aString;
160     bool        isHex = false;
161 
162     enum : uint64_t
163     {
164         kMaxHexBeforeOveflow = (0xffffffffffffffffULL / 16),
165         kMaxDecBeforeOverlow = (0xffffffffffffffffULL / 10),
166     };
167 
168     VerifyOrExit(aString != nullptr, error = kErrorInvalidArgs);
169 
170     if (cur[0] == '0' && (cur[1] == 'x' || cur[1] == 'X'))
171     {
172         cur += 2;
173         isHex = true;
174     }
175 
176     do
177     {
178         uint8_t  digit;
179         uint64_t newValue;
180 
181         SuccessOrExit(error = isHex ? ParseHexDigit(*cur, digit) : ParseDigit(*cur, digit));
182         VerifyOrExit(value <= (isHex ? kMaxHexBeforeOveflow : kMaxDecBeforeOverlow), error = kErrorInvalidArgs);
183         value    = isHex ? (value << 4) : (value * 10);
184         newValue = value + digit;
185         VerifyOrExit(newValue >= value, error = kErrorInvalidArgs);
186         value = newValue;
187         cur++;
188     } while (*cur != '\0');
189 
190     aUint64 = value;
191 
192 exit:
193     return error;
194 }
195 
ParseInt(const char * aString,IntType & aInt)196 template <typename IntType> Error ParseInt(const char *aString, IntType &aInt)
197 {
198     Error   error;
199     int32_t value;
200 
201     SuccessOrExit(error = ParseAsInt32(aString, value));
202 
203     VerifyOrExit((NumericLimits<IntType>::kMin <= value) && (value <= NumericLimits<IntType>::kMax),
204                  error = kErrorInvalidArgs);
205     aInt = static_cast<IntType>(value);
206 
207 exit:
208     return error;
209 }
210 
ParseAsInt8(const char * aString,int8_t & aInt8)211 Error ParseAsInt8(const char *aString, int8_t &aInt8)
212 {
213     return ParseInt<int8_t>(aString, aInt8);
214 }
215 
ParseAsInt16(const char * aString,int16_t & aInt16)216 Error ParseAsInt16(const char *aString, int16_t &aInt16)
217 {
218     return ParseInt<int16_t>(aString, aInt16);
219 }
220 
ParseAsInt32(const char * aString,int32_t & aInt32)221 Error ParseAsInt32(const char *aString, int32_t &aInt32)
222 {
223     Error    error;
224     uint64_t value;
225     bool     isNegavtive = false;
226 
227     VerifyOrExit(aString != nullptr, error = kErrorInvalidArgs);
228 
229     if (*aString == '-')
230     {
231         aString++;
232         isNegavtive = true;
233     }
234     else if (*aString == '+')
235     {
236         aString++;
237     }
238 
239     SuccessOrExit(error = ParseAsUint64(aString, value));
240     VerifyOrExit(value <= (isNegavtive ? static_cast<uint64_t>(-static_cast<int64_t>(NumericLimits<int32_t>::kMin))
241                                        : static_cast<uint64_t>(NumericLimits<int32_t>::kMax)),
242                  error = kErrorInvalidArgs);
243     aInt32 = static_cast<int32_t>(isNegavtive ? -static_cast<int64_t>(value) : static_cast<int64_t>(value));
244 
245 exit:
246     return error;
247 }
248 
ParseAsBool(const char * aString,bool & aBool)249 Error ParseAsBool(const char *aString, bool &aBool)
250 {
251     Error    error;
252     uint32_t value;
253 
254     SuccessOrExit(error = ParseAsUint32(aString, value));
255     aBool = (value != 0);
256 
257 exit:
258     return error;
259 }
260 #if OPENTHREAD_FTD || OPENTHREAD_MTD
261 
ParseAsIp6Address(const char * aString,otIp6Address & aAddress)262 Error ParseAsIp6Address(const char *aString, otIp6Address &aAddress)
263 {
264     return (aString != nullptr) ? otIp6AddressFromString(aString, &aAddress) : kErrorInvalidArgs;
265 }
266 
ParseAsIp6Prefix(const char * aString,otIp6Prefix & aPrefix)267 Error ParseAsIp6Prefix(const char *aString, otIp6Prefix &aPrefix)
268 {
269     enum : uint8_t
270     {
271         kMaxIp6AddressStringSize = 45,
272     };
273 
274     Error       error = kErrorInvalidArgs;
275     char        string[kMaxIp6AddressStringSize];
276     const char *prefixLengthStr;
277 
278     VerifyOrExit(aString != nullptr);
279 
280     prefixLengthStr = StringFind(aString, '/');
281     VerifyOrExit(prefixLengthStr != nullptr);
282 
283     VerifyOrExit(prefixLengthStr - aString < static_cast<int32_t>(sizeof(string)));
284 
285     memcpy(string, aString, static_cast<uint8_t>(prefixLengthStr - aString));
286     string[prefixLengthStr - aString] = '\0';
287 
288     SuccessOrExit(static_cast<Ip6::Address &>(aPrefix.mPrefix).FromString(string));
289     error = ParseAsUint8(prefixLengthStr + 1, aPrefix.mLength);
290 
291 exit:
292     return error;
293 }
294 #endif // #if OPENTHREAD_FTD || OPENTHREAD_MTD
295 
296 enum HexStringParseMode
297 {
298     kModeExtactSize,   // Parse hex string expecting an exact size (number of bytes when parsed).
299     kModeUpToSize,     // Parse hex string expecting less than or equal a given size.
300     kModeAllowPartial, // Allow parsing of partial segments.
301 };
302 
ParseHexString(const char * & aString,uint16_t & aSize,uint8_t * aBuffer,HexStringParseMode aMode)303 static Error ParseHexString(const char *&aString, uint16_t &aSize, uint8_t *aBuffer, HexStringParseMode aMode)
304 {
305     Error  error      = kErrorNone;
306     size_t parsedSize = 0;
307     size_t stringLength;
308     size_t expectedSize;
309     bool   skipFirstDigit;
310 
311     VerifyOrExit(aString != nullptr, error = kErrorInvalidArgs);
312 
313     stringLength = strlen(aString);
314     expectedSize = (stringLength + 1) / 2;
315 
316     switch (aMode)
317     {
318     case kModeExtactSize:
319         VerifyOrExit(expectedSize == aSize, error = kErrorInvalidArgs);
320         break;
321     case kModeUpToSize:
322         VerifyOrExit(expectedSize <= aSize, error = kErrorInvalidArgs);
323         break;
324     case kModeAllowPartial:
325         break;
326     }
327 
328     // If number of chars in hex string is odd, we skip parsing
329     // the first digit.
330 
331     skipFirstDigit = ((stringLength & 1) != 0);
332 
333     while (parsedSize < expectedSize)
334     {
335         uint8_t digit;
336 
337         if ((aMode == kModeAllowPartial) && (parsedSize == aSize))
338         {
339             // If partial parse mode is allowed, stop once we read the
340             // requested size.
341             ExitNow(error = kErrorPending);
342         }
343 
344         if (skipFirstDigit)
345         {
346             *aBuffer       = 0;
347             skipFirstDigit = false;
348         }
349         else
350         {
351             SuccessOrExit(error = ParseHexDigit(*aString, digit));
352             aString++;
353             *aBuffer = static_cast<uint8_t>(digit << 4);
354         }
355 
356         SuccessOrExit(error = ParseHexDigit(*aString, digit));
357         aString++;
358         *aBuffer |= digit;
359 
360         aBuffer++;
361         parsedSize++;
362     }
363 
364     aSize = static_cast<uint16_t>(parsedSize);
365 
366 exit:
367     return error;
368 }
369 
ParseAsHexString(const char * aString,uint8_t * aBuffer,uint16_t aSize)370 Error ParseAsHexString(const char *aString, uint8_t *aBuffer, uint16_t aSize)
371 {
372     return ParseHexString(aString, aSize, aBuffer, kModeExtactSize);
373 }
374 
ParseAsHexString(const char * aString,uint16_t & aSize,uint8_t * aBuffer)375 Error ParseAsHexString(const char *aString, uint16_t &aSize, uint8_t *aBuffer)
376 {
377     return ParseHexString(aString, aSize, aBuffer, kModeUpToSize);
378 }
379 
ParseAsHexStringSegment(const char * & aString,uint16_t & aSize,uint8_t * aBuffer)380 Error ParseAsHexStringSegment(const char *&aString, uint16_t &aSize, uint8_t *aBuffer)
381 {
382     return ParseHexString(aString, aSize, aBuffer, kModeAllowPartial);
383 }
384 
385 //---------------------------------------------------------------------------------------------------------------------
386 // Arg class
387 
GetLength(void) const388 uint16_t Arg::GetLength(void) const
389 {
390     return IsEmpty() ? 0 : static_cast<uint16_t>(strlen(mString));
391 }
392 
operator ==(const char * aString) const393 bool Arg::operator==(const char *aString) const
394 {
395     return !IsEmpty() && (strcmp(mString, aString) == 0);
396 }
397 
CopyArgsToStringArray(Arg aArgs[],char * aStrings[])398 void Arg::CopyArgsToStringArray(Arg aArgs[], char *aStrings[])
399 {
400     for (uint8_t i = 0; !aArgs[i].IsEmpty(); i++)
401     {
402         aStrings[i] = aArgs[i].GetCString();
403     }
404 }
405 
GetArgsLength(Arg aArgs[])406 uint8_t Arg::GetArgsLength(Arg aArgs[])
407 {
408     uint8_t length = 0;
409 
410     while (!aArgs[length].IsEmpty())
411     {
412         length++;
413     }
414 
415     return length;
416 }
417 
418 } // namespace CmdLineParser
419 } // namespace Utils
420 } // namespace ot
421