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 output.
32  */
33 
34 #ifndef CLI_OUTPUT_HPP_
35 #define CLI_OUTPUT_HPP_
36 
37 #include "openthread-core-config.h"
38 
39 #include <stdarg.h>
40 
41 #include <openthread/cli.h>
42 
43 #include "cli_config.h"
44 
45 #include "common/binary_search.hpp"
46 #include "common/num_utils.hpp"
47 #include "common/string.hpp"
48 #include "utils/parse_cmdline.hpp"
49 
50 namespace ot {
51 namespace Cli {
52 
53 /**
54  * Represents a ID number value associated with a CLI command string.
55  *
56  */
57 typedef uint64_t CommandId;
58 
59 /**
60  * This `constexpr` function converts a CLI command string to its associated `CommandId` value.
61  *
62  * @param[in] aString   The CLI command string.
63  *
64  * @returns The associated `CommandId` with @p aString.
65  *
66  */
Cmd(const char * aString)67 constexpr static CommandId Cmd(const char *aString)
68 {
69     return (aString[0] == '\0') ? 0 : (static_cast<uint8_t>(aString[0]) + Cmd(aString + 1) * 255u);
70 }
71 
72 class Output;
73 
74 /**
75  * Implements the basic output functions.
76  *
77  */
78 class OutputImplementer
79 {
80     friend class Output;
81 
82 public:
83     /**
84      * Initializes the `OutputImplementer` object.
85      *
86      * @param[in] aCallback           A pointer to an `otCliOutputCallback` to deliver strings to the CLI console.
87      * @param[in] aCallbackContext    An arbitrary context to pass in when invoking @p aCallback.
88      *
89      */
90     OutputImplementer(otCliOutputCallback aCallback, void *aCallbackContext);
91 
92 #if OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE
SetEmittingCommandOutput(bool aEmittingOutput)93     void SetEmittingCommandOutput(bool aEmittingOutput) { mEmittingCommandOutput = aEmittingOutput; }
94 #else
SetEmittingCommandOutput(bool)95     void SetEmittingCommandOutput(bool) {}
96 #endif
97 
98 private:
99     static constexpr uint16_t kInputOutputLogStringSize = OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_LOG_STRING_SIZE;
100 
101     void OutputV(const char *aFormat, va_list aArguments);
102 
103     otCliOutputCallback mCallback;
104     void               *mCallbackContext;
105 #if OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE
106     char     mOutputString[kInputOutputLogStringSize];
107     uint16_t mOutputLength;
108     bool     mEmittingCommandOutput;
109 #endif
110 };
111 
112 /**
113  * Provides CLI output helper methods.
114  *
115  */
116 class Output
117 {
118 public:
119     typedef Utils::CmdLineParser::Arg Arg; ///< An argument
120 
121     /**
122      * Represent a CLI command table entry, mapping a command with `aName` to a handler method.
123      *
124      * @tparam Cli    The CLI module type.
125      *
126      */
127     template <typename Cli> struct CommandEntry
128     {
129         typedef otError (Cli::*Handler)(Arg aArgs[]); ///< The handler method pointer type.
130 
131         /**
132          * Compares the entry's name with a given name.
133          *
134          * @param aName    The name string to compare with.
135          *
136          * @return zero means perfect match, positive (> 0) indicates @p aName is larger than entry's name, and
137          *         negative (< 0) indicates @p aName is smaller than entry's name.
138          *
139          */
Compareot::Cli::Output::CommandEntry140         int Compare(const char *aName) const { return strcmp(aName, mName); }
141 
142         /**
143          * This `constexpr` method compares two entries to check if they are in order.
144          *
145          * @param[in] aFirst     The first entry.
146          * @param[in] aSecond    The second entry.
147          *
148          * @retval TRUE  if @p aFirst and @p aSecond are in order, i.e. `aFirst < aSecond`.
149          * @retval FALSE if @p aFirst and @p aSecond are not in order, i.e. `aFirst >= aSecond`.
150          *
151          */
AreInOrderot::Cli::Output::CommandEntry152         constexpr static bool AreInOrder(const CommandEntry &aFirst, const CommandEntry &aSecond)
153         {
154             return AreStringsInOrder(aFirst.mName, aSecond.mName);
155         }
156 
157         const char *mName;    ///< The command name.
158         Handler     mHandler; ///< The handler method pointer.
159     };
160 
161     static const char kUnknownString[]; // Constant string "unknown".
162 
163     /**
164      * This template static method converts an enumeration value to a string using a table array.
165      *
166      * @tparam EnumType       The `enum` type.
167      * @tparam kLength        The table array length (number of entries in the array).
168      *
169      * @param[in] aEnum       The enumeration value to convert (MUST be of `EnumType`).
170      * @param[in] aTable      A reference to the array of strings of length @p kLength. `aTable[e]` is the string
171      *                        representation of enumeration value `e`.
172      * @param[in] aNotFound   The string to return if the @p aEnum is not in the @p aTable.
173      *
174      * @returns The string representation of @p aEnum from @p aTable, or @p aNotFound if it is not in the table.
175      *
176      */
177     template <typename EnumType, uint16_t kLength>
Stringify(EnumType aEnum,const char * const (& aTable)[kLength],const char * aNotFound=kUnknownString)178     static const char *Stringify(EnumType aEnum,
179                                  const char *const (&aTable)[kLength],
180                                  const char *aNotFound = kUnknownString)
181     {
182         return (static_cast<uint16_t>(aEnum) < kLength) ? aTable[static_cast<uint16_t>(aEnum)] : aNotFound;
183     }
184 
185     /**
186      * Initializes the `Output` object.
187      *
188      * @param[in] aInstance           A pointer to OpenThread instance.
189      * @param[in] aImplementer        An `OutputImplementer`.
190      *
191      */
Output(otInstance * aInstance,OutputImplementer & aImplementer)192     Output(otInstance *aInstance, OutputImplementer &aImplementer)
193         : mInstance(aInstance)
194         , mImplementer(aImplementer)
195     {
196     }
197 
198     /**
199      * Returns the pointer to OpenThread instance.
200      *
201      * @returns The pointer to the OpenThread instance.
202      *
203      */
GetInstancePtr(void)204     otInstance *GetInstancePtr(void) { return mInstance; }
205 
206     /**
207      * Represents a buffer which is used when converting a `uint64` value to string in decimal format.
208      *
209      */
210     struct Uint64StringBuffer
211     {
212         static constexpr uint16_t kSize = 21; ///< Size of a buffer
213 
214         char mChars[kSize]; ///< Char array (do not access the array directly).
215     };
216 
217     /**
218      * Converts a `uint64_t` value to a decimal format string.
219      *
220      * @param[in] aUint64  The `uint64_t` value to convert.
221      * @param[in] aBuffer  A buffer to allocate the string from.
222      *
223      * @returns A pointer to the start of the string (null-terminated) representation of @p aUint64.
224      *
225      */
226     static const char *Uint64ToString(uint64_t aUint64, Uint64StringBuffer &aBuffer);
227 
228     /**
229      * Delivers a formatted output string to the CLI console.
230      *
231      * @param[in]  aFormat  A pointer to the format string.
232      * @param[in]  ...      A variable list of arguments to format.
233      *
234      */
235     void OutputFormat(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(2, 3);
236 
237     /**
238      * Delivers a formatted output string to the CLI console (to which it prepends a given number
239      * indentation space chars).
240      *
241      * @param[in]  aIndentSize   Number of indentation space chars to prepend to the string.
242      * @param[in]  aFormat       A pointer to the format string.
243      * @param[in]  ...           A variable list of arguments to format.
244      *
245      */
246     void OutputFormat(uint8_t aIndentSize, const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(3, 4);
247 
248     /**
249      * Delivers a formatted output string to the CLI console (to which it also appends newline "\r\n").
250      *
251      * @param[in]  aFormat  A pointer to the format string.
252      * @param[in]  ...      A variable list of arguments to format.
253      *
254      */
255     void OutputLine(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(2, 3);
256 
257     /**
258      * Delivers a formatted output string to the CLI console (to which it prepends a given number
259      * indentation space chars and appends newline "\r\n").
260      *
261      * @param[in]  aIndentSize   Number of indentation space chars to prepend to the string.
262      * @param[in]  aFormat       A pointer to the format string.
263      * @param[in]  ...           A variable list of arguments to format.
264      *
265      */
266     void OutputLine(uint8_t aIndentSize, const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(3, 4);
267 
268     /**
269      * Delivered newline "\r\n" to the CLI console.
270      *
271      */
272     void OutputNewLine(void);
273 
274     /**
275      * Outputs a given number of space chars to the CLI console.
276      *
277      * @param[in] aCount  Number of space chars to output.
278      *
279      */
280     void OutputSpaces(uint8_t aCount);
281 
282     /**
283      * Outputs a number of bytes to the CLI console as a hex string.
284      *
285      * @param[in]  aBytes   A pointer to data which should be printed.
286      * @param[in]  aLength  @p aBytes length.
287      *
288      */
289     void OutputBytes(const uint8_t *aBytes, uint16_t aLength);
290 
291     /**
292      * Outputs a number of bytes to the CLI console as a hex string and at the end it also outputs newline
293      * "\r\n".
294      *
295      * @param[in]  aBytes   A pointer to data which should be printed.
296      * @param[in]  aLength  @p aBytes length.
297      *
298      */
299     void OutputBytesLine(const uint8_t *aBytes, uint16_t aLength);
300 
301     /**
302      * Outputs a number of bytes to the CLI console as a hex string.
303      *
304      * @tparam kBytesLength   The length of @p aBytes array.
305      *
306      * @param[in]  aBytes     A array of @p kBytesLength bytes which should be printed.
307      *
308      */
OutputBytes(const uint8_t (& aBytes)[kBytesLength])309     template <uint8_t kBytesLength> void OutputBytes(const uint8_t (&aBytes)[kBytesLength])
310     {
311         OutputBytes(aBytes, kBytesLength);
312     }
313 
314     /**
315      * Outputs a number of bytes to the CLI console as a hex string and at the end it also outputs newline
316      * "\r\n".
317      *
318      * @tparam kBytesLength   The length of @p aBytes array.
319      *
320      * @param[in]  aBytes     A array of @p kBytesLength bytes which should be printed.
321      *
322      */
OutputBytesLine(const uint8_t (& aBytes)[kBytesLength])323     template <uint8_t kBytesLength> void OutputBytesLine(const uint8_t (&aBytes)[kBytesLength])
324     {
325         OutputBytesLine(aBytes, kBytesLength);
326     }
327 
328     /**
329      * Outputs an Extended MAC Address to the CLI console.
330      *
331      * param[in] aExtAddress  The Extended MAC Address to output.
332      *
333      */
OutputExtAddress(const otExtAddress & aExtAddress)334     void OutputExtAddress(const otExtAddress &aExtAddress) { OutputBytes(aExtAddress.m8); }
335 
336     /**
337      * Outputs an Extended MAC Address to the CLI console and at the end it also outputs newline "\r\n".
338      *
339      * param[in] aExtAddress  The Extended MAC Address to output.
340      *
341      */
OutputExtAddressLine(const otExtAddress & aExtAddress)342     void OutputExtAddressLine(const otExtAddress &aExtAddress) { OutputBytesLine(aExtAddress.m8); }
343 
344     /**
345      * Outputs a `uint64_t` value in decimal format.
346      *
347      * @param[in] aUint64   The `uint64_t` value to output.
348      *
349      */
350     void OutputUint64(uint64_t aUint64);
351 
352     /**
353      * Outputs a `uint64_t` value in decimal format and at the end it also outputs newline "\r\n".
354      *
355      * @param[in] aUint64   The `uint64_t` value to output.
356      *
357      */
358     void OutputUint64Line(uint64_t aUint64);
359 
360     /**
361      * Outputs "Enabled" or "Disabled" status to the CLI console (it also appends newline "\r\n").
362      *
363      * @param[in] aEnabled  A boolean indicating the status. TRUE outputs "Enabled", FALSE outputs "Disabled".
364      *
365      */
366     void OutputEnabledDisabledStatus(bool aEnabled);
367 
368 #if OPENTHREAD_FTD || OPENTHREAD_MTD
369 
370     /**
371      * Outputs an IPv6 address to the CLI console.
372      *
373      * @param[in]  aAddress  A reference to the IPv6 address.
374      *
375      */
376     void OutputIp6Address(const otIp6Address &aAddress);
377 
378     /**
379      * Outputs an IPv6 address to the CLI console and at the end it also outputs newline "\r\n".
380      *
381      * @param[in]  aAddress  A reference to the IPv6 address.
382      *
383      */
384     void OutputIp6AddressLine(const otIp6Address &aAddress);
385 
386     /**
387      * Outputs an IPv6 prefix to the CLI console.
388      *
389      * @param[in]  aPrefix  A reference to the IPv6 prefix.
390      *
391      */
392     void OutputIp6Prefix(const otIp6Prefix &aPrefix);
393 
394     /**
395      * Outputs an IPv6 prefix to the CLI console and at the end it also outputs newline "\r\n".
396      *
397      * @param[in]  aPrefix  A reference to the IPv6 prefix.
398      *
399      */
400     void OutputIp6PrefixLine(const otIp6Prefix &aPrefix);
401 
402     /**
403      * Outputs an IPv6 network prefix to the CLI console.
404      *
405      * @param[in]  aPrefix  A reference to the IPv6 network prefix.
406      *
407      */
408     void OutputIp6Prefix(const otIp6NetworkPrefix &aPrefix);
409 
410     /**
411      * Outputs an IPv6 network prefix to the CLI console and at the end it also outputs newline "\r\n".
412      *
413      * @param[in]  aPrefix  A reference to the IPv6 network prefix.
414      *
415      */
416     void OutputIp6PrefixLine(const otIp6NetworkPrefix &aPrefix);
417 
418     /**
419      * Outputs an IPv6 socket address to the CLI console.
420      *
421      * @param[in] aSockAddr   A reference to the IPv6 socket address.
422      *
423      */
424     void OutputSockAddr(const otSockAddr &aSockAddr);
425 
426     /**
427      * Outputs an IPv6 socket address to the CLI console and at the end it also outputs newline "\r\n".
428      *
429      * @param[in] aSockAddr   A reference to the IPv6 socket address.
430      *
431      */
432     void OutputSockAddrLine(const otSockAddr &aSockAddr);
433 
434     /**
435      * Outputs DNS TXT data to the CLI console.
436      *
437      * @param[in] aTxtData        A pointer to a buffer containing the DNS TXT data.
438      * @param[in] aTxtDataLength  The length of @p aTxtData (in bytes).
439      *
440      */
441     void OutputDnsTxtData(const uint8_t *aTxtData, uint16_t aTxtDataLength);
442 
443     /**
444      * Represents a buffer which is used when converting an encoded rate value to percentage string.
445      *
446      */
447     struct PercentageStringBuffer
448     {
449         static constexpr uint16_t kSize = 7; ///< Size of a buffer
450 
451         char mChars[kSize]; ///< Char array (do not access the array directly).
452     };
453 
454     /**
455      * Converts an encoded value to a percentage representation.
456      *
457      * The encoded @p aValue is assumed to be linearly scaled such that `0` maps to 0% and `0xffff` maps to 100%.
458      *
459      * The resulting string provides two decimal accuracy, e.g., "100.00", "0.00", "75.37".
460      *
461      * @param[in] aValue   The encoded percentage value to convert.
462      * @param[in] aBuffer  A buffer to allocate the string from.
463      *
464      * @returns A pointer to the start of the string (null-terminated) representation of @p aValue.
465      *
466      */
467     static const char *PercentageToString(uint16_t aValue, PercentageStringBuffer &aBuffer);
468 
469 #endif // OPENTHREAD_FTD || OPENTHREAD_MTD
470 
471     /**
472      * Outputs a table header to the CLI console.
473      *
474      * An example of the table header format:
475      *
476      *    | Title1    | Title2 |Title3| Title4               |
477      *    +-----------+--------+------+----------------------+
478      *
479      * The titles are left adjusted (extra white space is added at beginning if the column is width enough). The widths
480      * are specified as the number chars between two `|` chars (excluding the char `|` itself).
481      *
482      * @tparam kTableNumColumns   The number columns in the table.
483      *
484      * @param[in] aTitles   An array specifying the table column titles.
485      * @param[in] aWidths   An array specifying the table column widths (in number of chars).
486      *
487      */
488     template <uint8_t kTableNumColumns>
OutputTableHeader(const char * const (& aTitles)[kTableNumColumns],const uint8_t (& aWidths)[kTableNumColumns])489     void OutputTableHeader(const char *const (&aTitles)[kTableNumColumns], const uint8_t (&aWidths)[kTableNumColumns])
490     {
491         OutputTableHeader(kTableNumColumns, &aTitles[0], &aWidths[0]);
492     }
493 
494     /**
495      * Outputs a table separator to the CLI console.
496      *
497      * An example of the table separator:
498      *
499      *    +-----------+--------+------+----------------------+
500      *
501      * The widths are specified as number chars between two `+` chars (excluding the char `+` itself).
502      *
503      * @tparam kTableNumColumns   The number columns in the table.
504      *
505      * @param[in] aWidths   An array specifying the table column widths (in number of chars).
506      *
507      */
OutputTableSeparator(const uint8_t (& aWidths)[kTableNumColumns])508     template <uint8_t kTableNumColumns> void OutputTableSeparator(const uint8_t (&aWidths)[kTableNumColumns])
509     {
510         OutputTableSeparator(kTableNumColumns, &aWidths[0]);
511     }
512 
513     /**
514      * Outputs the list of commands from a given command table.
515      *
516      * @tparam Cli      The CLI module type.
517      * @tparam kLength  The length of command table array.
518      *
519      * @param[in] aCommandTable   The command table array.
520      *
521      */
OutputCommandTable(const CommandEntry<Cli> (& aCommandTable)[kLength])522     template <typename Cli, uint16_t kLength> void OutputCommandTable(const CommandEntry<Cli> (&aCommandTable)[kLength])
523     {
524         for (const CommandEntry<Cli> &entry : aCommandTable)
525         {
526             OutputLine("%s", entry.mName);
527         }
528     }
529 
530 protected:
531     void OutputFormatV(const char *aFormat, va_list aArguments);
532 
533 #if OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE
534     void LogInput(const Arg *aArgs);
535 #else
LogInput(const Arg *)536     void LogInput(const Arg *) {}
537 #endif
538 
539 private:
540     static constexpr uint16_t kInputOutputLogStringSize = OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_LOG_STRING_SIZE;
541 
542     void OutputTableHeader(uint8_t aNumColumns, const char *const aTitles[], const uint8_t aWidths[]);
543     void OutputTableSeparator(uint8_t aNumColumns, const uint8_t aWidths[]);
544 
545     otInstance        *mInstance;
546     OutputImplementer &mImplementer;
547 };
548 
549 } // namespace Cli
550 } // namespace ot
551 
552 #endif // CLI_OUTPUT_HPP_
553