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