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 * This type 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 * This class implements the basic output functions.
76 *
77 */
78 class OutputImplementer
79 {
80 friend class Output;
81
82 public:
83 /**
84 * This constructor 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 * This class provides CLI output helper methods.
114 *
115 */
116 class Output
117 {
118 public:
119 typedef Utils::CmdLineParser::Arg Arg; ///< An argument
120
121 /**
122 * This structure 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 * This method 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 * This constructor 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 * This method 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 * This structure 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 * This static method 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 * This method 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 * This method 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 * This method 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 * This method 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 * This method delivered newline "\r\n" to the CLI console.
270 *
271 */
272 void OutputNewLine(void);
273
274 /**
275 * This method 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 * This method 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 * This method 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 * This method 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 * This method 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 * This method 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 * This method 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 * This method 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 * This method 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 * This method 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 * This method 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 * This method 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 * This method 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 * This method 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 * This method 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 * This method 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 * This method 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 * This method 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 * This method 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 * This structure 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 * This static method 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 * This method 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 * This method 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 * This method 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