1 /*
2  *  Copyright (c) 2021, 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 contains implementation of the CLI output module.
32  */
33 
34 #include "cli_utils.hpp"
35 
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 
40 #if OPENTHREAD_FTD || OPENTHREAD_MTD
41 #include <openthread/dns.h>
42 #endif
43 #include <openthread/logging.h>
44 
45 #include "cli/cli.hpp"
46 #include "common/string.hpp"
47 
48 namespace ot {
49 namespace Cli {
50 
51 const char Utils::kUnknownString[] = "unknown";
52 
OutputImplementer(otCliOutputCallback aCallback,void * aCallbackContext)53 OutputImplementer::OutputImplementer(otCliOutputCallback aCallback, void *aCallbackContext)
54     : mCallback(aCallback)
55     , mCallbackContext(aCallbackContext)
56 #if OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE
57     , mOutputLength(0)
58     , mEmittingCommandOutput(true)
59 #endif
60 {
61 }
62 
OutputFormat(const char * aFormat,...)63 void Utils::OutputFormat(const char *aFormat, ...)
64 {
65     va_list args;
66 
67     va_start(args, aFormat);
68     OutputFormatV(aFormat, args);
69     va_end(args);
70 }
71 
OutputFormat(uint8_t aIndentSize,const char * aFormat,...)72 void Utils::OutputFormat(uint8_t aIndentSize, const char *aFormat, ...)
73 {
74     va_list args;
75 
76     OutputSpaces(aIndentSize);
77 
78     va_start(args, aFormat);
79     OutputFormatV(aFormat, args);
80     va_end(args);
81 }
82 
OutputLine(const char * aFormat,...)83 void Utils::OutputLine(const char *aFormat, ...)
84 {
85     va_list args;
86 
87     va_start(args, aFormat);
88     OutputFormatV(aFormat, args);
89     va_end(args);
90 
91     OutputNewLine();
92 }
93 
OutputLine(uint8_t aIndentSize,const char * aFormat,...)94 void Utils::OutputLine(uint8_t aIndentSize, const char *aFormat, ...)
95 {
96     va_list args;
97 
98     OutputSpaces(aIndentSize);
99 
100     va_start(args, aFormat);
101     OutputFormatV(aFormat, args);
102     va_end(args);
103 
104     OutputNewLine();
105 }
106 
OutputNewLine(void)107 void Utils::OutputNewLine(void) { OutputFormat("\r\n"); }
108 
OutputSpaces(uint8_t aCount)109 void Utils::OutputSpaces(uint8_t aCount) { OutputFormat("%*s", aCount, ""); }
110 
OutputBytes(const uint8_t * aBytes,uint16_t aLength)111 void Utils::OutputBytes(const uint8_t *aBytes, uint16_t aLength)
112 {
113     for (uint16_t i = 0; i < aLength; i++)
114     {
115         OutputFormat("%02x", aBytes[i]);
116     }
117 }
118 
OutputBytesLine(const uint8_t * aBytes,uint16_t aLength)119 void Utils::OutputBytesLine(const uint8_t *aBytes, uint16_t aLength)
120 {
121     OutputBytes(aBytes, aLength);
122     OutputNewLine();
123 }
124 
Uint64ToString(uint64_t aUint64,Uint64StringBuffer & aBuffer)125 const char *Utils::Uint64ToString(uint64_t aUint64, Uint64StringBuffer &aBuffer)
126 {
127     char *cur = &aBuffer.mChars[Uint64StringBuffer::kSize - 1];
128 
129     *cur = '\0';
130 
131     if (aUint64 == 0)
132     {
133         *(--cur) = '0';
134     }
135     else
136     {
137         for (; aUint64 != 0; aUint64 /= 10)
138         {
139             *(--cur) = static_cast<char>('0' + static_cast<uint8_t>(aUint64 % 10));
140         }
141     }
142 
143     return cur;
144 }
145 
OutputUint64(uint64_t aUint64)146 void Utils::OutputUint64(uint64_t aUint64)
147 {
148     Uint64StringBuffer buffer;
149 
150     OutputFormat("%s", Uint64ToString(aUint64, buffer));
151 }
152 
OutputUint64Line(uint64_t aUint64)153 void Utils::OutputUint64Line(uint64_t aUint64)
154 {
155     OutputUint64(aUint64);
156     OutputNewLine();
157 }
158 
OutputEnabledDisabledStatus(bool aEnabled)159 void Utils::OutputEnabledDisabledStatus(bool aEnabled) { OutputLine(aEnabled ? "Enabled" : "Disabled"); }
160 
161 #if OPENTHREAD_FTD || OPENTHREAD_MTD
162 
OutputIp6Address(const otIp6Address & aAddress)163 void Utils::OutputIp6Address(const otIp6Address &aAddress)
164 {
165     char string[OT_IP6_ADDRESS_STRING_SIZE];
166 
167     otIp6AddressToString(&aAddress, string, sizeof(string));
168 
169     return OutputFormat("%s", string);
170 }
171 
OutputIp6AddressLine(const otIp6Address & aAddress)172 void Utils::OutputIp6AddressLine(const otIp6Address &aAddress)
173 {
174     OutputIp6Address(aAddress);
175     OutputNewLine();
176 }
177 
OutputIp6Prefix(const otIp6Prefix & aPrefix)178 void Utils::OutputIp6Prefix(const otIp6Prefix &aPrefix)
179 {
180     char string[OT_IP6_PREFIX_STRING_SIZE];
181 
182     otIp6PrefixToString(&aPrefix, string, sizeof(string));
183 
184     OutputFormat("%s", string);
185 }
186 
OutputIp6PrefixLine(const otIp6Prefix & aPrefix)187 void Utils::OutputIp6PrefixLine(const otIp6Prefix &aPrefix)
188 {
189     OutputIp6Prefix(aPrefix);
190     OutputNewLine();
191 }
192 
OutputIp6Prefix(const otIp6NetworkPrefix & aPrefix)193 void Utils::OutputIp6Prefix(const otIp6NetworkPrefix &aPrefix)
194 {
195     OutputFormat("%x:%x:%x:%x::/64", (aPrefix.m8[0] << 8) | aPrefix.m8[1], (aPrefix.m8[2] << 8) | aPrefix.m8[3],
196                  (aPrefix.m8[4] << 8) | aPrefix.m8[5], (aPrefix.m8[6] << 8) | aPrefix.m8[7]);
197 }
198 
OutputIp6PrefixLine(const otIp6NetworkPrefix & aPrefix)199 void Utils::OutputIp6PrefixLine(const otIp6NetworkPrefix &aPrefix)
200 {
201     OutputIp6Prefix(aPrefix);
202     OutputNewLine();
203 }
204 
OutputSockAddr(const otSockAddr & aSockAddr)205 void Utils::OutputSockAddr(const otSockAddr &aSockAddr)
206 {
207     char string[OT_IP6_SOCK_ADDR_STRING_SIZE];
208 
209     otIp6SockAddrToString(&aSockAddr, string, sizeof(string));
210 
211     return OutputFormat("%s", string);
212 }
213 
OutputSockAddrLine(const otSockAddr & aSockAddr)214 void Utils::OutputSockAddrLine(const otSockAddr &aSockAddr)
215 {
216     OutputSockAddr(aSockAddr);
217     OutputNewLine();
218 }
219 
OutputDnsTxtData(const uint8_t * aTxtData,uint16_t aTxtDataLength)220 void Utils::OutputDnsTxtData(const uint8_t *aTxtData, uint16_t aTxtDataLength)
221 {
222     otDnsTxtEntry         entry;
223     otDnsTxtEntryIterator iterator;
224     bool                  isFirst = true;
225 
226     otDnsInitTxtEntryIterator(&iterator, aTxtData, aTxtDataLength);
227 
228     OutputFormat("[");
229 
230     while (otDnsGetNextTxtEntry(&iterator, &entry) == OT_ERROR_NONE)
231     {
232         if (!isFirst)
233         {
234             OutputFormat(", ");
235         }
236 
237         if (entry.mKey == nullptr)
238         {
239             // A null `mKey` indicates that the key in the entry is
240             // longer than the recommended max key length, so the entry
241             // could not be parsed. In this case, the whole entry is
242             // returned encoded in `mValue`.
243 
244             OutputFormat("[");
245             OutputBytes(entry.mValue, entry.mValueLength);
246             OutputFormat("]");
247         }
248         else
249         {
250             OutputFormat("%s", entry.mKey);
251 
252             if (entry.mValue != nullptr)
253             {
254                 OutputFormat("=");
255                 OutputBytes(entry.mValue, entry.mValueLength);
256             }
257         }
258 
259         isFirst = false;
260     }
261 
262     OutputFormat("]");
263 }
264 
PercentageToString(uint16_t aValue,PercentageStringBuffer & aBuffer)265 const char *Utils::PercentageToString(uint16_t aValue, PercentageStringBuffer &aBuffer)
266 {
267     uint32_t     scaledValue = aValue;
268     StringWriter writer(aBuffer.mChars, sizeof(aBuffer.mChars));
269 
270     scaledValue = (scaledValue * 10000) / 0xffff;
271     writer.Append("%u.%02u", static_cast<uint16_t>(scaledValue / 100), static_cast<uint16_t>(scaledValue % 100));
272 
273     return aBuffer.mChars;
274 }
275 
276 #endif // OPENTHREAD_FTD || OPENTHREAD_MTD
277 
OutputFormatV(const char * aFormat,va_list aArguments)278 void Utils::OutputFormatV(const char *aFormat, va_list aArguments) { mImplementer.OutputV(aFormat, aArguments); }
279 
OutputV(const char * aFormat,va_list aArguments)280 void OutputImplementer::OutputV(const char *aFormat, va_list aArguments)
281 {
282 #if OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE
283     va_list args;
284     int     charsWritten;
285     bool    truncated = false;
286 
287     va_copy(args, aArguments);
288 #endif
289 
290     mCallback(mCallbackContext, aFormat, aArguments);
291 
292 #if OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE
293     VerifyOrExit(mEmittingCommandOutput);
294 
295     charsWritten = vsnprintf(&mOutputString[mOutputLength], sizeof(mOutputString) - mOutputLength, aFormat, args);
296 
297     VerifyOrExit(charsWritten >= 0, mOutputLength = 0);
298 
299     if (static_cast<uint32_t>(charsWritten) >= sizeof(mOutputString) - mOutputLength)
300     {
301         truncated     = true;
302         mOutputLength = sizeof(mOutputString) - 1;
303     }
304     else
305     {
306         mOutputLength += charsWritten;
307     }
308 
309     while (true)
310     {
311         char *lineEnd = strchr(mOutputString, '\r');
312 
313         if (lineEnd == nullptr)
314         {
315             break;
316         }
317 
318         *lineEnd = '\0';
319 
320         if (lineEnd > mOutputString)
321         {
322             otLogCli(OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_LEVEL, "Output: %s", mOutputString);
323         }
324 
325         lineEnd++;
326 
327         while ((*lineEnd == '\n') || (*lineEnd == '\r'))
328         {
329             lineEnd++;
330         }
331 
332         // Example of the pointers and lengths.
333         //
334         // - mOutputString = "hi\r\nmore"
335         // - mOutputLength = 8
336         // - lineEnd       = &mOutputString[4]
337         //
338         //
339         //   0    1    2    3    4    5    6    7    8    9
340         // +----+----+----+----+----+----+----+----+----+---
341         // | h  | i  | \r | \n | m  | o  | r  | e  | \0 |
342         // +----+----+----+----+----+----+----+----+----+---
343         //                       ^                   ^
344         //                       |                   |
345         //                    lineEnd    mOutputString[mOutputLength]
346         //
347         //
348         // New length is `&mOutputString[8] - &mOutputString[4] -> 4`.
349         //
350         // We move (newLen + 1 = 5) chars from `lineEnd` to start of
351         // `mOutputString` which will include the `\0` char.
352         //
353         // If `lineEnd` and `mOutputString[mOutputLength]` are the same
354         // the code works correctly as well  (new length set to zero and
355         // the `\0` is copied).
356 
357         mOutputLength = static_cast<uint16_t>(&mOutputString[mOutputLength] - lineEnd);
358         memmove(mOutputString, lineEnd, mOutputLength + 1);
359     }
360 
361     if (truncated)
362     {
363         otLogCli(OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_LEVEL, "Output: %s ...", mOutputString);
364         mOutputLength = 0;
365     }
366 
367 exit:
368     va_end(args);
369 #endif // OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE
370 }
371 
372 #if OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE
LogInput(const Arg * aArgs)373 void Utils::LogInput(const Arg *aArgs)
374 {
375     String<kInputOutputLogStringSize> inputString;
376 
377     for (bool isFirst = true; !aArgs->IsEmpty(); aArgs++, isFirst = false)
378     {
379         inputString.Append(isFirst ? "%s" : " %s", aArgs->GetCString());
380     }
381 
382     otLogCli(OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_LEVEL, "Input: %s", inputString.AsCString());
383 }
384 #endif
385 
OutputTableHeader(uint8_t aNumColumns,const char * const aTitles[],const uint8_t aWidths[])386 void Utils::OutputTableHeader(uint8_t aNumColumns, const char *const aTitles[], const uint8_t aWidths[])
387 {
388     for (uint8_t index = 0; index < aNumColumns; index++)
389     {
390         const char *title       = aTitles[index];
391         uint8_t     width       = aWidths[index];
392         size_t      titleLength = strlen(title);
393 
394         if (titleLength + 2 <= width)
395         {
396             // `title` fits in column width so we write it with extra space
397             // at beginning and end ("| Title    |").
398 
399             OutputFormat("| %*s", -static_cast<int>(width - 1), title);
400         }
401         else
402         {
403             // Use narrow style (no space at beginning) and write as many
404             // chars from `title` as it can fit in the given column width
405             // ("|Title|").
406 
407             OutputFormat("|%*.*s", -static_cast<int>(width), width, title);
408         }
409     }
410 
411     OutputLine("|");
412     OutputTableSeparator(aNumColumns, aWidths);
413 }
414 
OutputTableSeparator(uint8_t aNumColumns,const uint8_t aWidths[])415 void Utils::OutputTableSeparator(uint8_t aNumColumns, const uint8_t aWidths[])
416 {
417     for (uint8_t index = 0; index < aNumColumns; index++)
418     {
419         OutputFormat("+");
420 
421         for (uint8_t width = aWidths[index]; width != 0; width--)
422         {
423             OutputFormat("-");
424         }
425     }
426 
427     OutputLine("+");
428 }
429 
ParseEnableOrDisable(const Arg & aArg,bool & aEnable)430 otError Utils::ParseEnableOrDisable(const Arg &aArg, bool &aEnable)
431 {
432     otError error = OT_ERROR_NONE;
433 
434     if (aArg == "enable")
435     {
436         aEnable = true;
437     }
438     else if (aArg == "disable")
439     {
440         aEnable = false;
441     }
442     else
443     {
444         error = OT_ERROR_INVALID_COMMAND;
445     }
446 
447     return error;
448 }
449 
ProcessEnableDisable(Arg aArgs[],SetEnabledHandler aSetEnabledHandler)450 otError Utils::ProcessEnableDisable(Arg aArgs[], SetEnabledHandler aSetEnabledHandler)
451 {
452     otError error = OT_ERROR_NONE;
453     bool    enable;
454 
455     if (ParseEnableOrDisable(aArgs[0], enable) == OT_ERROR_NONE)
456     {
457         aSetEnabledHandler(GetInstancePtr(), enable);
458     }
459     else
460     {
461         error = OT_ERROR_INVALID_COMMAND;
462     }
463 
464     return error;
465 }
466 
ProcessEnableDisable(Arg aArgs[],SetEnabledHandlerFailable aSetEnabledHandler)467 otError Utils::ProcessEnableDisable(Arg aArgs[], SetEnabledHandlerFailable aSetEnabledHandler)
468 {
469     otError error = OT_ERROR_NONE;
470     bool    enable;
471 
472     if (ParseEnableOrDisable(aArgs[0], enable) == OT_ERROR_NONE)
473     {
474         error = aSetEnabledHandler(GetInstancePtr(), enable);
475     }
476     else
477     {
478         error = OT_ERROR_INVALID_COMMAND;
479     }
480 
481     return error;
482 }
483 
ProcessEnableDisable(Arg aArgs[],IsEnabledHandler aIsEnabledHandler,SetEnabledHandler aSetEnabledHandler)484 otError Utils::ProcessEnableDisable(Arg               aArgs[],
485                                     IsEnabledHandler  aIsEnabledHandler,
486                                     SetEnabledHandler aSetEnabledHandler)
487 {
488     otError error = OT_ERROR_NONE;
489 
490     if (aArgs[0].IsEmpty())
491     {
492         OutputEnabledDisabledStatus(aIsEnabledHandler(GetInstancePtr()));
493     }
494     else
495     {
496         error = ProcessEnableDisable(aArgs, aSetEnabledHandler);
497     }
498 
499     return error;
500 }
501 
ProcessEnableDisable(Arg aArgs[],IsEnabledHandler aIsEnabledHandler,SetEnabledHandlerFailable aSetEnabledHandler)502 otError Utils::ProcessEnableDisable(Arg                       aArgs[],
503                                     IsEnabledHandler          aIsEnabledHandler,
504                                     SetEnabledHandlerFailable aSetEnabledHandler)
505 {
506     otError error = OT_ERROR_NONE;
507 
508     if (aArgs[0].IsEmpty())
509     {
510         OutputEnabledDisabledStatus(aIsEnabledHandler(GetInstancePtr()));
511     }
512     else
513     {
514         error = ProcessEnableDisable(aArgs, aSetEnabledHandler);
515     }
516 
517     return error;
518 }
519 
ParseJoinerDiscerner(Arg & aArg,otJoinerDiscerner & aDiscerner)520 otError Utils::ParseJoinerDiscerner(Arg &aArg, otJoinerDiscerner &aDiscerner)
521 {
522     otError error;
523     char   *separator;
524 
525     VerifyOrExit(!aArg.IsEmpty(), error = OT_ERROR_INVALID_ARGS);
526 
527     separator = strstr(aArg.GetCString(), "/");
528 
529     VerifyOrExit(separator != nullptr, error = OT_ERROR_NOT_FOUND);
530 
531     SuccessOrExit(error = ot::Utils::CmdLineParser::ParseAsUint8(separator + 1, aDiscerner.mLength));
532     VerifyOrExit(aDiscerner.mLength > 0 && aDiscerner.mLength <= 64, error = OT_ERROR_INVALID_ARGS);
533     *separator = '\0';
534     error      = aArg.ParseAsUint64(aDiscerner.mValue);
535 
536 exit:
537     return error;
538 }
539 
ParsePreference(const Arg & aArg,otRoutePreference & aPreference)540 otError Utils::ParsePreference(const Arg &aArg, otRoutePreference &aPreference)
541 {
542     otError error = OT_ERROR_NONE;
543 
544     if (aArg == "high")
545     {
546         aPreference = OT_ROUTE_PREFERENCE_HIGH;
547     }
548     else if (aArg == "med")
549     {
550         aPreference = OT_ROUTE_PREFERENCE_MED;
551     }
552     else if (aArg == "low")
553     {
554         aPreference = OT_ROUTE_PREFERENCE_LOW;
555     }
556     else
557     {
558         error = OT_ERROR_INVALID_ARGS;
559     }
560 
561     return error;
562 }
563 
PreferenceToString(signed int aPreference)564 const char *Utils::PreferenceToString(signed int aPreference)
565 {
566     const char *str = "";
567 
568     switch (aPreference)
569     {
570     case OT_ROUTE_PREFERENCE_LOW:
571         str = "low";
572         break;
573 
574     case OT_ROUTE_PREFERENCE_MED:
575         str = "med";
576         break;
577 
578     case OT_ROUTE_PREFERENCE_HIGH:
579         str = "high";
580         break;
581 
582     default:
583         break;
584     }
585 
586     return str;
587 }
588 
589 #if OPENTHREAD_FTD || OPENTHREAD_MTD
ParseToIp6Address(otInstance * aInstance,const Arg & aArg,otIp6Address & aAddress,bool & aSynthesized)590 otError Utils::ParseToIp6Address(otInstance *aInstance, const Arg &aArg, otIp6Address &aAddress, bool &aSynthesized)
591 {
592     Error error = OT_ERROR_NONE;
593 
594     VerifyOrExit(!aArg.IsEmpty(), error = OT_ERROR_INVALID_ARGS);
595     error        = aArg.ParseAsIp6Address(aAddress);
596     aSynthesized = false;
597 
598     if (error != OT_ERROR_NONE)
599     {
600         // It might be an IPv4 address, let's have a try.
601         otIp4Address ip4Address;
602 
603         // Do not touch the error value if we failed to parse it as an IPv4 address.
604         SuccessOrExit(aArg.ParseAsIp4Address(ip4Address));
605         SuccessOrExit(error = otNat64SynthesizeIp6Address(aInstance, &ip4Address, &aAddress));
606         aSynthesized = true;
607     }
608 
609 exit:
610     return error;
611 }
612 
613 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
ParsePrefix(Arg aArgs[],otBorderRouterConfig & aConfig)614 otError Utils::ParsePrefix(Arg aArgs[], otBorderRouterConfig &aConfig)
615 {
616     otError error = OT_ERROR_NONE;
617 
618     ClearAllBytes(aConfig);
619 
620     SuccessOrExit(error = aArgs[0].ParseAsIp6Prefix(aConfig.mPrefix));
621     aArgs++;
622 
623     for (; !aArgs->IsEmpty(); aArgs++)
624     {
625         otRoutePreference preference;
626 
627         if (ParsePreference(*aArgs, preference) == OT_ERROR_NONE)
628         {
629             aConfig.mPreference = preference;
630         }
631         else
632         {
633             for (char *arg = aArgs->GetCString(); *arg != '\0'; arg++)
634             {
635                 switch (*arg)
636                 {
637                 case 'p':
638                     aConfig.mPreferred = true;
639                     break;
640 
641                 case 'a':
642                     aConfig.mSlaac = true;
643                     break;
644 
645                 case 'd':
646                     aConfig.mDhcp = true;
647                     break;
648 
649                 case 'c':
650                     aConfig.mConfigure = true;
651                     break;
652 
653                 case 'r':
654                     aConfig.mDefaultRoute = true;
655                     break;
656 
657                 case 'o':
658                     aConfig.mOnMesh = true;
659                     break;
660 
661                 case 's':
662                     aConfig.mStable = true;
663                     break;
664 
665                 case 'n':
666                     aConfig.mNdDns = true;
667                     break;
668 
669 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
670                 case 'D':
671                     aConfig.mDp = true;
672                     break;
673 #endif
674                 case '-':
675                     break;
676 
677                 default:
678                     ExitNow(error = OT_ERROR_INVALID_ARGS);
679                 }
680             }
681         }
682     }
683 
684 exit:
685     return error;
686 }
687 
ParseRoute(Arg aArgs[],otExternalRouteConfig & aConfig)688 otError Utils::ParseRoute(Arg aArgs[], otExternalRouteConfig &aConfig)
689 {
690     otError error = OT_ERROR_NONE;
691 
692     ClearAllBytes(aConfig);
693 
694     SuccessOrExit(error = aArgs[0].ParseAsIp6Prefix(aConfig.mPrefix));
695     aArgs++;
696 
697     for (; !aArgs->IsEmpty(); aArgs++)
698     {
699         otRoutePreference preference;
700 
701         if (ParsePreference(*aArgs, preference) == OT_ERROR_NONE)
702         {
703             aConfig.mPreference = preference;
704         }
705         else
706         {
707             for (char *arg = aArgs->GetCString(); *arg != '\0'; arg++)
708             {
709                 switch (*arg)
710                 {
711                 case 's':
712                     aConfig.mStable = true;
713                     break;
714 
715                 case 'n':
716                     aConfig.mNat64 = true;
717                     break;
718 
719                 case 'a':
720                     aConfig.mAdvPio = true;
721                     break;
722 
723                 case '-':
724                     break;
725 
726                 default:
727                     ExitNow(error = OT_ERROR_INVALID_ARGS);
728                 }
729             }
730         }
731     }
732 
733 exit:
734     return error;
735 }
736 #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
737 #endif // #if OPENTHREAD_FTD || OPENTHREAD_MTD
738 
LinkModeToString(const otLinkModeConfig & aLinkMode,char (& aStringBuffer)[kLinkModeStringSize])739 const char *Utils::LinkModeToString(const otLinkModeConfig &aLinkMode, char (&aStringBuffer)[kLinkModeStringSize])
740 {
741     char *flagsPtr = &aStringBuffer[0];
742 
743     if (aLinkMode.mRxOnWhenIdle)
744     {
745         *flagsPtr++ = 'r';
746     }
747 
748     if (aLinkMode.mDeviceType)
749     {
750         *flagsPtr++ = 'd';
751     }
752 
753     if (aLinkMode.mNetworkData)
754     {
755         *flagsPtr++ = 'n';
756     }
757 
758     if (flagsPtr == &aStringBuffer[0])
759     {
760         *flagsPtr++ = '-';
761     }
762 
763     *flagsPtr = '\0';
764 
765     return aStringBuffer;
766 }
767 
AddressOriginToString(uint8_t aOrigin)768 const char *Utils::AddressOriginToString(uint8_t aOrigin)
769 {
770     static const char *const kOriginStrings[4] = {
771         "thread", // 0, OT_ADDRESS_ORIGIN_THREAD
772         "slaac",  // 1, OT_ADDRESS_ORIGIN_SLAAC
773         "dhcp6",  // 2, OT_ADDRESS_ORIGIN_DHCPV6
774         "manual", // 3, OT_ADDRESS_ORIGIN_MANUAL
775     };
776 
777     static_assert(0 == OT_ADDRESS_ORIGIN_THREAD, "OT_ADDRESS_ORIGIN_THREAD value is incorrect");
778     static_assert(1 == OT_ADDRESS_ORIGIN_SLAAC, "OT_ADDRESS_ORIGIN_SLAAC value is incorrect");
779     static_assert(2 == OT_ADDRESS_ORIGIN_DHCPV6, "OT_ADDRESS_ORIGIN_DHCPV6 value is incorrect");
780     static_assert(3 == OT_ADDRESS_ORIGIN_MANUAL, "OT_ADDRESS_ORIGIN_MANUAL value is incorrect");
781 
782     return Stringify(aOrigin, kOriginStrings);
783 }
784 
785 } // namespace Cli
786 } // namespace ot
787