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