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