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 definitions for the CLI util functions.
32  */
33 
34 #ifndef CLI_UTILS_HPP_
35 #define CLI_UTILS_HPP_
36 
37 #include "openthread-core-config.h"
38 
39 #include <stdarg.h>
40 
41 #include <openthread/border_router.h>
42 #include <openthread/border_routing.h>
43 #include <openthread/cli.h>
44 #include <openthread/joiner.h>
45 #include <openthread/thread.h>
46 
47 #include "cli_config.h"
48 
49 #include "common/binary_search.hpp"
50 #include "common/num_utils.hpp"
51 #include "common/string.hpp"
52 #include "common/type_traits.hpp"
53 #include "utils/parse_cmdline.hpp"
54 
55 namespace ot {
56 namespace Cli {
57 
58 /**
59  * Represents a ID number value associated with a CLI command string.
60  *
61  */
62 typedef uint64_t CommandId;
63 
64 /**
65  * This `constexpr` function converts a CLI command string to its associated `CommandId` value.
66  *
67  * @param[in] aString   The CLI command string.
68  *
69  * @returns The associated `CommandId` with @p aString.
70  *
71  */
Cmd(const char * aString)72 constexpr static CommandId Cmd(const char *aString)
73 {
74     return (aString[0] == '\0') ? 0 : (static_cast<uint8_t>(aString[0]) + Cmd(aString + 1) * 255u);
75 }
76 
77 class Utils;
78 
79 /**
80  * Implements the basic output functions.
81  *
82  */
83 class OutputImplementer
84 {
85     friend class Utils;
86 
87 public:
88     /**
89      * Initializes the `OutputImplementer` object.
90      *
91      * @param[in] aCallback           A pointer to an `otCliOutputCallback` to deliver strings to the CLI console.
92      * @param[in] aCallbackContext    An arbitrary context to pass in when invoking @p aCallback.
93      *
94      */
95     OutputImplementer(otCliOutputCallback aCallback, void *aCallbackContext);
96 
97 #if OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE
SetEmittingCommandOutput(bool aEmittingOutput)98     void SetEmittingCommandOutput(bool aEmittingOutput) { mEmittingCommandOutput = aEmittingOutput; }
99 #else
SetEmittingCommandOutput(bool)100     void SetEmittingCommandOutput(bool) {}
101 #endif
102 
103 private:
104     static constexpr uint16_t kInputOutputLogStringSize = OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_LOG_STRING_SIZE;
105 
106     void OutputV(const char *aFormat, va_list aArguments);
107 
108     otCliOutputCallback mCallback;
109     void               *mCallbackContext;
110 #if OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE
111     char     mOutputString[kInputOutputLogStringSize];
112     uint16_t mOutputLength;
113     bool     mEmittingCommandOutput;
114 #endif
115 };
116 
117 /**
118  * Provides CLI helper methods.
119  *
120  */
121 class Utils
122 {
123 public:
124     typedef ot::Utils::CmdLineParser::Arg Arg; ///< An argument
125 
126     /**
127      * Represent a CLI command table entry, mapping a command with `aName` to a handler method.
128      *
129      * @tparam Cli    The CLI module type.
130      *
131      */
132     template <typename Cli> struct CommandEntry
133     {
134         typedef otError (Cli::*Handler)(Arg aArgs[]); ///< The handler method pointer type.
135 
136         /**
137          * Compares the entry's name with a given name.
138          *
139          * @param aName    The name string to compare with.
140          *
141          * @return zero means perfect match, positive (> 0) indicates @p aName is larger than entry's name, and
142          *         negative (< 0) indicates @p aName is smaller than entry's name.
143          *
144          */
Compareot::Cli::Utils::CommandEntry145         int Compare(const char *aName) const { return strcmp(aName, mName); }
146 
147         /**
148          * This `constexpr` method compares two entries to check if they are in order.
149          *
150          * @param[in] aFirst     The first entry.
151          * @param[in] aSecond    The second entry.
152          *
153          * @retval TRUE  if @p aFirst and @p aSecond are in order, i.e. `aFirst < aSecond`.
154          * @retval FALSE if @p aFirst and @p aSecond are not in order, i.e. `aFirst >= aSecond`.
155          *
156          */
AreInOrderot::Cli::Utils::CommandEntry157         constexpr static bool AreInOrder(const CommandEntry &aFirst, const CommandEntry &aSecond)
158         {
159             return AreStringsInOrder(aFirst.mName, aSecond.mName);
160         }
161 
162         const char *mName;    ///< The command name.
163         Handler     mHandler; ///< The handler method pointer.
164     };
165 
166     static const char kUnknownString[]; // Constant string "unknown".
167 
168     /**
169      * This template static method converts an enumeration value to a string using a table array.
170      *
171      * @tparam EnumType       The `enum` type.
172      * @tparam kLength        The table array length (number of entries in the array).
173      *
174      * @param[in] aEnum       The enumeration value to convert (MUST be of `EnumType`).
175      * @param[in] aTable      A reference to the array of strings of length @p kLength. `aTable[e]` is the string
176      *                        representation of enumeration value `e`.
177      * @param[in] aNotFound   The string to return if the @p aEnum is not in the @p aTable.
178      *
179      * @returns The string representation of @p aEnum from @p aTable, or @p aNotFound if it is not in the table.
180      *
181      */
182     template <typename EnumType, uint16_t kLength>
Stringify(EnumType aEnum,const char * const (& aTable)[kLength],const char * aNotFound=kUnknownString)183     static const char *Stringify(EnumType aEnum,
184                                  const char *const (&aTable)[kLength],
185                                  const char *aNotFound = kUnknownString)
186     {
187         return (static_cast<uint16_t>(aEnum) < kLength) ? aTable[static_cast<uint16_t>(aEnum)] : aNotFound;
188     }
189 
190     /**
191      * Initializes the `Utils` object.
192      *
193      * @param[in] aInstance           A pointer to OpenThread instance.
194      * @param[in] aImplementer        An `OutputImplementer`.
195      *
196      */
Utils(otInstance * aInstance,OutputImplementer & aImplementer)197     Utils(otInstance *aInstance, OutputImplementer &aImplementer)
198         : mInstance(aInstance)
199         , mImplementer(aImplementer)
200     {
201     }
202 
203     /**
204      * Returns the pointer to OpenThread instance.
205      *
206      * @returns The pointer to the OpenThread instance.
207      *
208      */
GetInstancePtr(void)209     otInstance *GetInstancePtr(void) { return mInstance; }
210 
211     /**
212      * Represents a buffer which is used when converting a `uint64` value to string in decimal format.
213      *
214      */
215     struct Uint64StringBuffer
216     {
217         static constexpr uint16_t kSize = 21; ///< Size of a buffer
218 
219         char mChars[kSize]; ///< Char array (do not access the array directly).
220     };
221 
222     /**
223      * Converts a `uint64_t` value to a decimal format string.
224      *
225      * @param[in] aUint64  The `uint64_t` value to convert.
226      * @param[in] aBuffer  A buffer to allocate the string from.
227      *
228      * @returns A pointer to the start of the string (null-terminated) representation of @p aUint64.
229      *
230      */
231     static const char *Uint64ToString(uint64_t aUint64, Uint64StringBuffer &aBuffer);
232 
233     /**
234      * Delivers a formatted output string to the CLI console.
235      *
236      * @param[in]  aFormat  A pointer to the format string.
237      * @param[in]  ...      A variable list of arguments to format.
238      *
239      */
240     void OutputFormat(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(2, 3);
241 
242     /**
243      * Delivers a formatted output string to the CLI console (to which it prepends a given number
244      * indentation space chars).
245      *
246      * @param[in]  aIndentSize   Number of indentation space chars to prepend to the string.
247      * @param[in]  aFormat       A pointer to the format string.
248      * @param[in]  ...           A variable list of arguments to format.
249      *
250      */
251     void OutputFormat(uint8_t aIndentSize, const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(3, 4);
252 
253     /**
254      * Delivers a formatted output string to the CLI console (to which it also appends newline "\r\n").
255      *
256      * @param[in]  aFormat  A pointer to the format string.
257      * @param[in]  ...      A variable list of arguments to format.
258      *
259      */
260     void OutputLine(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(2, 3);
261 
262     /**
263      * Delivers a formatted output string to the CLI console (to which it prepends a given number
264      * indentation space chars and appends newline "\r\n").
265      *
266      * @param[in]  aIndentSize   Number of indentation space chars to prepend to the string.
267      * @param[in]  aFormat       A pointer to the format string.
268      * @param[in]  ...           A variable list of arguments to format.
269      *
270      */
271     void OutputLine(uint8_t aIndentSize, const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(3, 4);
272 
273     /**
274      * Delivered newline "\r\n" to the CLI console.
275      *
276      */
277     void OutputNewLine(void);
278 
279     /**
280      * Outputs a given number of space chars to the CLI console.
281      *
282      * @param[in] aCount  Number of space chars to output.
283      *
284      */
285     void OutputSpaces(uint8_t aCount);
286 
287     /**
288      * Outputs a number of bytes to the CLI console as a hex string.
289      *
290      * @param[in]  aBytes   A pointer to data which should be printed.
291      * @param[in]  aLength  @p aBytes length.
292      *
293      */
294     void OutputBytes(const uint8_t *aBytes, uint16_t aLength);
295 
296     /**
297      * Outputs a number of bytes to the CLI console as a hex string and at the end it also outputs newline
298      * "\r\n".
299      *
300      * @param[in]  aBytes   A pointer to data which should be printed.
301      * @param[in]  aLength  @p aBytes length.
302      *
303      */
304     void OutputBytesLine(const uint8_t *aBytes, uint16_t aLength);
305 
306     /**
307      * Outputs a number of bytes to the CLI console as a hex string.
308      *
309      * @tparam kBytesLength   The length of @p aBytes array.
310      *
311      * @param[in]  aBytes     A array of @p kBytesLength bytes which should be printed.
312      *
313      */
OutputBytes(const uint8_t (& aBytes)[kBytesLength])314     template <uint8_t kBytesLength> void OutputBytes(const uint8_t (&aBytes)[kBytesLength])
315     {
316         OutputBytes(aBytes, kBytesLength);
317     }
318 
319     /**
320      * Outputs a number of bytes to the CLI console as a hex string and at the end it also outputs newline
321      * "\r\n".
322      *
323      * @tparam kBytesLength   The length of @p aBytes array.
324      *
325      * @param[in]  aBytes     A array of @p kBytesLength bytes which should be printed.
326      *
327      */
OutputBytesLine(const uint8_t (& aBytes)[kBytesLength])328     template <uint8_t kBytesLength> void OutputBytesLine(const uint8_t (&aBytes)[kBytesLength])
329     {
330         OutputBytesLine(aBytes, kBytesLength);
331     }
332 
333     /**
334      * Outputs an Extended MAC Address to the CLI console.
335      *
336      * param[in] aExtAddress  The Extended MAC Address to output.
337      *
338      */
OutputExtAddress(const otExtAddress & aExtAddress)339     void OutputExtAddress(const otExtAddress &aExtAddress) { OutputBytes(aExtAddress.m8); }
340 
341     /**
342      * Outputs an Extended MAC Address to the CLI console and at the end it also outputs newline "\r\n".
343      *
344      * param[in] aExtAddress  The Extended MAC Address to output.
345      *
346      */
OutputExtAddressLine(const otExtAddress & aExtAddress)347     void OutputExtAddressLine(const otExtAddress &aExtAddress) { OutputBytesLine(aExtAddress.m8); }
348 
349     /**
350      * Outputs a `uint64_t` value in decimal format.
351      *
352      * @param[in] aUint64   The `uint64_t` value to output.
353      *
354      */
355     void OutputUint64(uint64_t aUint64);
356 
357     /**
358      * Outputs a `uint64_t` value in decimal format and at the end it also outputs newline "\r\n".
359      *
360      * @param[in] aUint64   The `uint64_t` value to output.
361      *
362      */
363     void OutputUint64Line(uint64_t aUint64);
364 
365     /**
366      * Outputs "Enabled" or "Disabled" status to the CLI console (it also appends newline "\r\n").
367      *
368      * @param[in] aEnabled  A boolean indicating the status. TRUE outputs "Enabled", FALSE outputs "Disabled".
369      *
370      */
371     void OutputEnabledDisabledStatus(bool aEnabled);
372 
373 #if OPENTHREAD_FTD || OPENTHREAD_MTD
374 
375     /**
376      * Outputs an IPv6 address to the CLI console.
377      *
378      * @param[in]  aAddress  A reference to the IPv6 address.
379      *
380      */
381     void OutputIp6Address(const otIp6Address &aAddress);
382 
383     /**
384      * Outputs an IPv6 address to the CLI console and at the end it also outputs newline "\r\n".
385      *
386      * @param[in]  aAddress  A reference to the IPv6 address.
387      *
388      */
389     void OutputIp6AddressLine(const otIp6Address &aAddress);
390 
391     /**
392      * Outputs an IPv6 prefix to the CLI console.
393      *
394      * @param[in]  aPrefix  A reference to the IPv6 prefix.
395      *
396      */
397     void OutputIp6Prefix(const otIp6Prefix &aPrefix);
398 
399     /**
400      * Outputs an IPv6 prefix to the CLI console and at the end it also outputs newline "\r\n".
401      *
402      * @param[in]  aPrefix  A reference to the IPv6 prefix.
403      *
404      */
405     void OutputIp6PrefixLine(const otIp6Prefix &aPrefix);
406 
407     /**
408      * Outputs an IPv6 network prefix to the CLI console.
409      *
410      * @param[in]  aPrefix  A reference to the IPv6 network prefix.
411      *
412      */
413     void OutputIp6Prefix(const otIp6NetworkPrefix &aPrefix);
414 
415     /**
416      * Outputs an IPv6 network prefix to the CLI console and at the end it also outputs newline "\r\n".
417      *
418      * @param[in]  aPrefix  A reference to the IPv6 network prefix.
419      *
420      */
421     void OutputIp6PrefixLine(const otIp6NetworkPrefix &aPrefix);
422 
423     /**
424      * Outputs an IPv6 socket address to the CLI console.
425      *
426      * @param[in] aSockAddr   A reference to the IPv6 socket address.
427      *
428      */
429     void OutputSockAddr(const otSockAddr &aSockAddr);
430 
431     /**
432      * Outputs an IPv6 socket address to the CLI console and at the end it also outputs newline "\r\n".
433      *
434      * @param[in] aSockAddr   A reference to the IPv6 socket address.
435      *
436      */
437     void OutputSockAddrLine(const otSockAddr &aSockAddr);
438 
439     /**
440      * Outputs DNS TXT data to the CLI console.
441      *
442      * @param[in] aTxtData        A pointer to a buffer containing the DNS TXT data.
443      * @param[in] aTxtDataLength  The length of @p aTxtData (in bytes).
444      *
445      */
446     void OutputDnsTxtData(const uint8_t *aTxtData, uint16_t aTxtDataLength);
447 
448     /**
449      * Represents a buffer which is used when converting an encoded rate value to percentage string.
450      *
451      */
452     struct PercentageStringBuffer
453     {
454         static constexpr uint16_t kSize = 7; ///< Size of a buffer
455 
456         char mChars[kSize]; ///< Char array (do not access the array directly).
457     };
458 
459     /**
460      * Converts an encoded value to a percentage representation.
461      *
462      * The encoded @p aValue is assumed to be linearly scaled such that `0` maps to 0% and `0xffff` maps to 100%.
463      *
464      * The resulting string provides two decimal accuracy, e.g., "100.00", "0.00", "75.37".
465      *
466      * @param[in] aValue   The encoded percentage value to convert.
467      * @param[in] aBuffer  A buffer to allocate the string from.
468      *
469      * @returns A pointer to the start of the string (null-terminated) representation of @p aValue.
470      *
471      */
472     static const char *PercentageToString(uint16_t aValue, PercentageStringBuffer &aBuffer);
473 
474 #endif // OPENTHREAD_FTD || OPENTHREAD_MTD
475 
476     /**
477      * Outputs a table header to the CLI console.
478      *
479      * An example of the table header format:
480      *
481      *    | Title1    | Title2 |Title3| Title4               |
482      *    +-----------+--------+------+----------------------+
483      *
484      * The titles are left adjusted (extra white space is added at beginning if the column is width enough). The widths
485      * are specified as the number chars between two `|` chars (excluding the char `|` itself).
486      *
487      * @tparam kTableNumColumns   The number columns in the table.
488      *
489      * @param[in] aTitles   An array specifying the table column titles.
490      * @param[in] aWidths   An array specifying the table column widths (in number of chars).
491      *
492      */
493     template <uint8_t kTableNumColumns>
OutputTableHeader(const char * const (& aTitles)[kTableNumColumns],const uint8_t (& aWidths)[kTableNumColumns])494     void OutputTableHeader(const char *const (&aTitles)[kTableNumColumns], const uint8_t (&aWidths)[kTableNumColumns])
495     {
496         OutputTableHeader(kTableNumColumns, &aTitles[0], &aWidths[0]);
497     }
498 
499     /**
500      * Outputs a table separator to the CLI console.
501      *
502      * An example of the table separator:
503      *
504      *    +-----------+--------+------+----------------------+
505      *
506      * The widths are specified as number chars between two `+` chars (excluding the char `+` itself).
507      *
508      * @tparam kTableNumColumns   The number columns in the table.
509      *
510      * @param[in] aWidths   An array specifying the table column widths (in number of chars).
511      *
512      */
OutputTableSeparator(const uint8_t (& aWidths)[kTableNumColumns])513     template <uint8_t kTableNumColumns> void OutputTableSeparator(const uint8_t (&aWidths)[kTableNumColumns])
514     {
515         OutputTableSeparator(kTableNumColumns, &aWidths[0]);
516     }
517 
518     /**
519      * Outputs the list of commands from a given command table.
520      *
521      * @tparam Cli      The CLI module type.
522      * @tparam kLength  The length of command table array.
523      *
524      * @param[in] aCommandTable   The command table array.
525      *
526      */
OutputCommandTable(const CommandEntry<Cli> (& aCommandTable)[kLength])527     template <typename Cli, uint16_t kLength> void OutputCommandTable(const CommandEntry<Cli> (&aCommandTable)[kLength])
528     {
529         for (const CommandEntry<Cli> &entry : aCommandTable)
530         {
531             OutputLine("%s", entry.mName);
532         }
533     }
534 
535     /**
536      * Clears (sets to zero) all bytes of a given object.
537      *
538      * @tparam ObjectType    The object type.
539      *
540      * @param[in] aObject    A reference to the object of type `ObjectType` to clear all its bytes.
541      *
542      */
ClearAllBytes(ObjectType & aObject)543     template <typename ObjectType> static void ClearAllBytes(ObjectType &aObject)
544     {
545         static_assert(!TypeTraits::IsPointer<ObjectType>::kValue, "ObjectType must not be a pointer");
546 
547         memset(reinterpret_cast<void *>(&aObject), 0, sizeof(ObjectType));
548     }
549 
550     // Definitions of handlers to process Get/Set/Enable/Disable.
551     template <typename ValueType> using GetHandler         = ValueType (&)(otInstance *);
552     template <typename ValueType> using SetHandler         = void (&)(otInstance *, ValueType);
553     template <typename ValueType> using SetHandlerFailable = otError (&)(otInstance *, ValueType);
554     using IsEnabledHandler                                 = bool (&)(otInstance *);
555     using SetEnabledHandler                                = void (&)(otInstance *, bool);
556     using SetEnabledHandlerFailable                        = otError (&)(otInstance *, bool);
557 
558     // Returns format string to output a `ValueType` (e.g., "%u" for `uint16_t`).
559     template <typename ValueType> static constexpr const char *FormatStringFor(void);
560 
561     /**
562      * Checks a given argument string against "enable" or "disable" commands.
563      *
564      * @param[in]  aArg     The argument string to parse.
565      * @param[out] aEnable  Boolean variable to return outcome on success.
566      *                      Set to TRUE for "enable" command, and FALSE for "disable" command.
567      *
568      * @retval OT_ERROR_NONE             Successfully parsed the @p aString and updated @p aEnable.
569      * @retval OT_ERROR_INVALID_COMMAND  The @p aString is not "enable" or "disable" command.
570      *
571      */
572     static otError ParseEnableOrDisable(const Arg &aArg, bool &aEnable);
573 
574     // General template implementation.
575     // Specializations for `uint32_t` and `int32_t` are added at the end.
ProcessGet(Arg aArgs[],GetHandler<ValueType> aGetHandler)576     template <typename ValueType> otError ProcessGet(Arg aArgs[], GetHandler<ValueType> aGetHandler)
577     {
578         static_assert(
579             TypeTraits::IsSame<ValueType, uint8_t>::kValue || TypeTraits::IsSame<ValueType, uint16_t>::kValue ||
580                 TypeTraits::IsSame<ValueType, int8_t>::kValue || TypeTraits::IsSame<ValueType, int16_t>::kValue ||
581                 TypeTraits::IsSame<ValueType, const char *>::kValue,
582             "ValueType must be an  8, 16 `int` or `uint` type, or a `const char *`");
583 
584         otError error = OT_ERROR_NONE;
585 
586         VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
587         OutputLine(FormatStringFor<ValueType>(), aGetHandler(GetInstancePtr()));
588 
589     exit:
590         return error;
591     }
592 
ProcessSet(Arg aArgs[],SetHandler<ValueType> aSetHandler)593     template <typename ValueType> otError ProcessSet(Arg aArgs[], SetHandler<ValueType> aSetHandler)
594     {
595         otError   error;
596         ValueType value;
597 
598         SuccessOrExit(error = aArgs[0].ParseAs<ValueType>(value));
599         VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
600 
601         aSetHandler(GetInstancePtr(), value);
602 
603     exit:
604         return error;
605     }
606 
ProcessSet(Arg aArgs[],SetHandlerFailable<ValueType> aSetHandler)607     template <typename ValueType> otError ProcessSet(Arg aArgs[], SetHandlerFailable<ValueType> aSetHandler)
608     {
609         otError   error;
610         ValueType value;
611 
612         SuccessOrExit(error = aArgs[0].ParseAs<ValueType>(value));
613         VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
614 
615         error = aSetHandler(GetInstancePtr(), value);
616 
617     exit:
618         return error;
619     }
620 
621     template <typename ValueType>
ProcessGetSet(Arg aArgs[],GetHandler<ValueType> aGetHandler,SetHandler<ValueType> aSetHandler)622     otError ProcessGetSet(Arg aArgs[], GetHandler<ValueType> aGetHandler, SetHandler<ValueType> aSetHandler)
623     {
624         otError error = ProcessGet(aArgs, aGetHandler);
625 
626         VerifyOrExit(error != OT_ERROR_NONE);
627         error = ProcessSet(aArgs, aSetHandler);
628 
629     exit:
630         return error;
631     }
632 
633     template <typename ValueType>
ProcessGetSet(Arg aArgs[],GetHandler<ValueType> aGetHandler,SetHandlerFailable<ValueType> aSetHandler)634     otError ProcessGetSet(Arg aArgs[], GetHandler<ValueType> aGetHandler, SetHandlerFailable<ValueType> aSetHandler)
635     {
636         otError error = ProcessGet(aArgs, aGetHandler);
637 
638         VerifyOrExit(error != OT_ERROR_NONE);
639         error = ProcessSet(aArgs, aSetHandler);
640 
641     exit:
642         return error;
643     }
644 
645     otError ProcessEnableDisable(Arg aArgs[], SetEnabledHandler aSetEnabledHandler);
646     otError ProcessEnableDisable(Arg aArgs[], SetEnabledHandlerFailable aSetEnabledHandler);
647     otError ProcessEnableDisable(Arg aArgs[], IsEnabledHandler aIsEnabledHandler, SetEnabledHandler aSetEnabledHandler);
648     otError ProcessEnableDisable(Arg                       aArgs[],
649                                  IsEnabledHandler          aIsEnabledHandler,
650                                  SetEnabledHandlerFailable aSetEnabledHandler);
651 
652     /**
653      * Parses a given argument string as a route preference comparing it against  "high", "med", or
654      * "low".
655      *
656      * @param[in]  aArg          The argument string to parse.
657      * @param[out] aPreference   Reference to a `otRoutePreference` to return the parsed preference.
658      *
659      * @retval OT_ERROR_NONE             Successfully parsed @p aArg and updated @p aPreference.
660      * @retval OT_ERROR_INVALID_ARG      @p aArg is not a valid preference string "high", "med", or "low".
661      *
662      */
663     static otError ParsePreference(const Arg &aArg, otRoutePreference &aPreference);
664 
665     /**
666      * Converts a route preference value to human-readable string.
667      *
668      * @param[in] aPreference   The preference value to convert (`OT_ROUTE_PREFERENCE_*` values).
669      *
670      * @returns A string representation @p aPreference.
671      *
672      */
673     static const char *PreferenceToString(signed int aPreference);
674 
675     /**
676      * Parses the argument as an IP address.
677      *
678      * If the argument string is an IPv4 address, this method will try to synthesize an IPv6 address using preferred
679      * NAT64 prefix in the network data.
680      *
681      * @param[in]  aInstance       A pointer to OpenThread instance.
682      * @param[in]  aArg            The argument string to parse.
683      * @param[out] aAddress        A reference to an `otIp6Address` to output the parsed IPv6 address.
684      * @param[out] aSynthesized    Whether @p aAddress is synthesized from an IPv4 address.
685      *
686      * @retval OT_ERROR_NONE           The argument was parsed successfully.
687      * @retval OT_ERROR_INVALID_ARGS   The argument is empty or does not contain a valid IP address.
688      * @retval OT_ERROR_INVALID_STATE  No valid NAT64 prefix in the network data.
689      *
690      */
691     static otError ParseToIp6Address(otInstance   *aInstance,
692                                      const Arg    &aArg,
693                                      otIp6Address &aAddress,
694                                      bool         &aSynthesized);
695 
696     /**
697      * Parses the argument as a Joiner Discerner.
698      *
699      * @param[in]  aArg            The argument string to parse.
700      * @param[out] aDiscerner      A reference to an `otJoinerDiscerner` to output the parsed discerner
701      *
702      * @retval OT_ERROR_NONE           The argument was parsed successfully.
703      * @retval OT_ERROR_INVALID_ARGS   The argument is empty or does not contain a valid joiner discerner.
704      *
705      */
706     static otError ParseJoinerDiscerner(Arg &aArg, otJoinerDiscerner &aDiscerner);
707 
708 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
709     /**
710      * Parses the argument as a Border Router configuration.
711      *
712      * @param[in]  aArg            The argument string to parse.
713      * @param[out] aConfig         A reference to an `otBorderRouterConfig` to output the configuration.
714      *
715      * @retval OT_ERROR_NONE           The argument was parsed successfully.
716      * @retval OT_ERROR_INVALID_ARGS   The argument is empty or does not contain a valid configuration.
717      *
718      */
719     static otError ParsePrefix(Arg aArgs[], otBorderRouterConfig &aConfig);
720 
721     /**
722      * Parses the argument as a External Route configuration.
723      *
724      * @param[in]  aArg            The argument string to parse.
725      * @param[out] aConfig         A reference to an `otExternalRouteConfig` to output the configuration.
726      *
727      * @retval OT_ERROR_NONE           The argument was parsed successfully.
728      * @retval OT_ERROR_INVALID_ARGS   The argument is empty or does not contain a valid configuration.
729      *
730      */
731     static otError ParseRoute(Arg aArgs[], otExternalRouteConfig &aConfig);
732 #endif
733 
734     static constexpr uint8_t kLinkModeStringSize = sizeof("rdn"); ///< Size of string buffer for a MLE Link Mode.
735 
736     /**
737      * Converts a given MLE Link Mode to flag string.
738      *
739      * The characters 'r', 'd', and 'n' are respectively used for `mRxOnWhenIdle`, `mDeviceType` and `mNetworkData`
740      * flags. If all flags are `false`, then "-" is returned.
741      *
742      * @param[in]  aLinkMode       The MLE Link Mode to convert.
743      * @param[out] aStringBuffer   A reference to an string array to place the string.
744      *
745      * @returns A pointer @p aStringBuffer which contains the converted string.
746      *
747      */
748     static const char *LinkModeToString(const otLinkModeConfig &aLinkMode, char (&aStringBuffer)[kLinkModeStringSize]);
749 
750     /**
751      * Converts an IPv6 address origin `OT_ADDRESS_ORIGIN_*` value to human-readable string.
752      *
753      * @param[in] aOrigin   The IPv6 address origin to convert.
754      *
755      * @returns A human-readable string representation of @p aOrigin.
756      *
757      */
758     static const char *AddressOriginToString(uint8_t aOrigin);
759 
760 protected:
761     void OutputFormatV(const char *aFormat, va_list aArguments);
762 
763 #if OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE
764     void LogInput(const Arg *aArgs);
765 #else
LogInput(const Arg *)766     void LogInput(const Arg *) {}
767 #endif
768 
769 private:
770     static constexpr uint16_t kInputOutputLogStringSize = OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_LOG_STRING_SIZE;
771 
772     void OutputTableHeader(uint8_t aNumColumns, const char *const aTitles[], const uint8_t aWidths[]);
773     void OutputTableSeparator(uint8_t aNumColumns, const uint8_t aWidths[]);
774 
775     otInstance        *mInstance;
776     OutputImplementer &mImplementer;
777 };
778 
779 // Specializations of `FormatStringFor<ValueType>()`
780 
FormatStringFor(void)781 template <> inline constexpr const char *Utils::FormatStringFor<uint8_t>(void) { return "%u"; }
782 
FormatStringFor(void)783 template <> inline constexpr const char *Utils::FormatStringFor<uint16_t>(void) { return "%u"; }
784 
FormatStringFor(void)785 template <> inline constexpr const char *Utils::FormatStringFor<uint32_t>(void) { return "%lu"; }
786 
FormatStringFor(void)787 template <> inline constexpr const char *Utils::FormatStringFor<int8_t>(void) { return "%d"; }
788 
FormatStringFor(void)789 template <> inline constexpr const char *Utils::FormatStringFor<int16_t>(void) { return "%d"; }
790 
FormatStringFor(void)791 template <> inline constexpr const char *Utils::FormatStringFor<int32_t>(void) { return "%ld"; }
792 
FormatStringFor(void)793 template <> inline constexpr const char *Utils::FormatStringFor<const char *>(void) { return "%s"; }
794 
795 // Specialization of ProcessGet<> for `uint32_t` and `int32_t`
796 
ProcessGet(Arg aArgs[],GetHandler<uint32_t> aGetHandler)797 template <> inline otError Utils::ProcessGet<uint32_t>(Arg aArgs[], GetHandler<uint32_t> aGetHandler)
798 {
799     otError error = OT_ERROR_NONE;
800 
801     VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
802     OutputLine(FormatStringFor<uint32_t>(), ToUlong(aGetHandler(GetInstancePtr())));
803 
804 exit:
805     return error;
806 }
807 
ProcessGet(Arg aArgs[],GetHandler<int32_t> aGetHandler)808 template <> inline otError Utils::ProcessGet<int32_t>(Arg aArgs[], GetHandler<int32_t> aGetHandler)
809 {
810     otError error = OT_ERROR_NONE;
811 
812     VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
813     OutputLine(FormatStringFor<int32_t>(), static_cast<long int>(aGetHandler(GetInstancePtr())));
814 
815 exit:
816     return error;
817 }
818 
819 } // namespace Cli
820 } // namespace ot
821 
822 #endif // CLI_UTILS_HPP_
823