Coverage Report

Created: 2026-01-10 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openthread/src/cli/cli_utils.cpp
Line
Count
Source
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 implementation of the CLI output module.
32
 */
33
34
#include "cli_utils.hpp"
35
36
#include <stdio.h>
37
#include <stdlib.h>
38
#include <string.h>
39
40
#if OPENTHREAD_FTD || OPENTHREAD_MTD
41
#include <openthread/dns.h>
42
#endif
43
#include <openthread/logging.h>
44
45
#include "cli/cli.hpp"
46
#include "common/string.hpp"
47
48
namespace ot {
49
namespace Cli {
50
51
const char Utils::kUnknownString[] = "unknown";
52
53
OutputImplementer::OutputImplementer(otCliOutputCallback aCallback, void *aCallbackContext)
54
7.40k
    : mCallback(aCallback)
55
7.40k
    , mCallbackContext(aCallbackContext)
56
#if OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE
57
7.40k
    , mOutputLength(0)
58
7.40k
    , mEmittingCommandOutput(true)
59
#endif
60
7.40k
{
61
7.40k
}
62
63
void Utils::OutputFormat(const char *aFormat, ...)
64
106k
{
65
106k
    va_list args;
66
67
106k
    va_start(args, aFormat);
68
106k
    OutputFormatV(aFormat, args);
69
106k
    va_end(args);
70
106k
}
71
72
void Utils::OutputFormat(uint8_t aIndentSize, const char *aFormat, ...)
73
538
{
74
538
    va_list args;
75
76
538
    OutputSpaces(aIndentSize);
77
78
538
    va_start(args, aFormat);
79
538
    OutputFormatV(aFormat, args);
80
538
    va_end(args);
81
538
}
82
83
void Utils::OutputLine(const char *aFormat, ...)
84
16.2k
{
85
16.2k
    va_list args;
86
87
16.2k
    va_start(args, aFormat);
88
16.2k
    OutputFormatV(aFormat, args);
89
16.2k
    va_end(args);
90
91
16.2k
    OutputNewLine();
92
16.2k
}
93
94
void Utils::OutputLine(uint8_t aIndentSize, const char *aFormat, ...)
95
3.04k
{
96
3.04k
    va_list args;
97
98
3.04k
    OutputSpaces(aIndentSize);
99
100
3.04k
    va_start(args, aFormat);
101
3.04k
    OutputFormatV(aFormat, args);
102
3.04k
    va_end(args);
103
104
3.04k
    OutputNewLine();
105
3.04k
}
106
107
20.6k
void Utils::OutputNewLine(void) { OutputFormat("\r\n"); }
108
109
3.57k
void Utils::OutputSpaces(uint8_t aCount) { OutputFormat("%*s", aCount, ""); }
110
111
void Utils::OutputBytes(const uint8_t *aBytes, uint16_t aLength)
112
241
{
113
4.59k
    for (uint16_t i = 0; i < aLength; i++)
114
4.35k
    {
115
4.35k
        OutputFormat("%02x", aBytes[i]);
116
4.35k
    }
117
241
}
118
119
void Utils::OutputBytesLine(const uint8_t *aBytes, uint16_t aLength)
120
156
{
121
156
    OutputBytes(aBytes, aLength);
122
156
    OutputNewLine();
123
156
}
124
125
const char *Utils::Uint64ToString(uint64_t aUint64, Uint64StringBuffer &aBuffer)
126
1.11k
{
127
1.11k
    char *cur = &aBuffer.mChars[Uint64StringBuffer::kSize - 1];
128
129
1.11k
    *cur = '\0';
130
131
1.11k
    if (aUint64 == 0)
132
1.03k
    {
133
1.03k
        *(--cur) = '0';
134
1.03k
    }
135
77
    else
136
77
    {
137
429
        for (; aUint64 != 0; aUint64 /= 10)
138
352
        {
139
352
            *(--cur) = static_cast<char>('0' + static_cast<uint8_t>(aUint64 % 10));
140
352
        }
141
77
    }
142
143
1.11k
    return cur;
144
1.11k
}
145
146
void Utils::OutputUint64(uint64_t aUint64)
147
152
{
148
152
    Uint64StringBuffer buffer;
149
150
152
    OutputFormat("%s", Uint64ToString(aUint64, buffer));
151
152
}
152
153
void Utils::OutputUint64Line(uint64_t aUint64)
154
152
{
155
152
    OutputUint64(aUint64);
156
152
    OutputNewLine();
157
152
}
158
159
61
void Utils::OutputEnabledDisabledStatus(bool aEnabled) { OutputLine(aEnabled ? "Enabled" : "Disabled"); }
160
161
#if OPENTHREAD_FTD || OPENTHREAD_MTD
162
163
void Utils::OutputIp6Address(const otIp6Address &aAddress)
164
790
{
165
790
    char string[OT_IP6_ADDRESS_STRING_SIZE];
166
167
790
    otIp6AddressToString(&aAddress, string, sizeof(string));
168
169
790
    return OutputFormat("%s", string);
170
790
}
171
172
void Utils::OutputIp6AddressLine(const otIp6Address &aAddress)
173
634
{
174
634
    OutputIp6Address(aAddress);
175
634
    OutputNewLine();
176
634
}
177
178
void Utils::OutputIp6Prefix(const otIp6Prefix &aPrefix)
179
219
{
180
219
    char string[OT_IP6_PREFIX_STRING_SIZE];
181
182
219
    otIp6PrefixToString(&aPrefix, string, sizeof(string));
183
184
219
    OutputFormat("%s", string);
185
219
}
186
187
void Utils::OutputIp6PrefixLine(const otIp6Prefix &aPrefix)
188
79
{
189
79
    OutputIp6Prefix(aPrefix);
190
79
    OutputNewLine();
191
79
}
192
193
void Utils::OutputIp6Prefix(const otIp6NetworkPrefix &aPrefix)
194
3
{
195
3
    OutputFormat("%x:%x:%x:%x::/64", (aPrefix.m8[0] << 8) | aPrefix.m8[1], (aPrefix.m8[2] << 8) | aPrefix.m8[3],
196
3
                 (aPrefix.m8[4] << 8) | aPrefix.m8[5], (aPrefix.m8[6] << 8) | aPrefix.m8[7]);
197
3
}
198
199
void Utils::OutputIp6PrefixLine(const otIp6NetworkPrefix &aPrefix)
200
3
{
201
3
    OutputIp6Prefix(aPrefix);
202
3
    OutputNewLine();
203
3
}
204
205
void Utils::OutputSockAddr(const otSockAddr &aSockAddr)
206
26
{
207
26
    char string[OT_IP6_SOCK_ADDR_STRING_SIZE];
208
209
26
    otIp6SockAddrToString(&aSockAddr, string, sizeof(string));
210
211
26
    return OutputFormat("%s", string);
212
26
}
213
214
void Utils::OutputSockAddrLine(const otSockAddr &aSockAddr)
215
26
{
216
26
    OutputSockAddr(aSockAddr);
217
26
    OutputNewLine();
218
26
}
219
220
void Utils::OutputDnsTxtData(const uint8_t *aTxtData, uint16_t aTxtDataLength)
221
0
{
222
0
    OutputDnsTxtData(/* aKeyValuePerLine */ false, 0, aTxtData, aTxtDataLength);
223
0
}
224
225
void Utils::OutputDnsTxtData(uint8_t aIndentSize, const uint8_t *aTxtData, uint16_t aTxtDataLength)
226
0
{
227
0
    OutputDnsTxtData(/* aKeyValuePerLine */ true, aIndentSize, aTxtData, aTxtDataLength);
228
0
}
229
230
void Utils::OutputDnsTxtData(bool           aKeyValuePerLine,
231
                             uint8_t        aIndentSize,
232
                             const uint8_t *aTxtData,
233
                             uint16_t       aTxtDataLength)
234
0
{
235
0
    otDnsTxtEntry         entry;
236
0
    otDnsTxtEntryIterator iterator;
237
0
    bool                  isFirst = true;
238
239
0
    otDnsInitTxtEntryIterator(&iterator, aTxtData, aTxtDataLength);
240
241
0
    if (!aKeyValuePerLine)
242
0
    {
243
0
        OutputFormat("[");
244
0
    }
245
246
0
    while (otDnsGetNextTxtEntry(&iterator, &entry) == OT_ERROR_NONE)
247
0
    {
248
0
        if (aKeyValuePerLine)
249
0
        {
250
0
            OutputSpaces(aIndentSize);
251
0
        }
252
0
        else if (!isFirst)
253
0
        {
254
0
            OutputFormat(", ");
255
0
        }
256
257
0
        if (entry.mKey == nullptr)
258
0
        {
259
            // A null `mKey` indicates that the key in the entry is
260
            // longer than the recommended max key length, so the entry
261
            // could not be parsed. In this case, the whole entry is
262
            // returned encoded in `mValue`.
263
264
0
            OutputFormat("[");
265
0
            OutputBytes(entry.mValue, entry.mValueLength);
266
0
            OutputFormat("]");
267
0
        }
268
0
        else
269
0
        {
270
0
            OutputFormat("%s", entry.mKey);
271
272
0
            if (entry.mValue != nullptr)
273
0
            {
274
0
                OutputFormat("=");
275
0
                OutputBytes(entry.mValue, entry.mValueLength);
276
0
            }
277
0
        }
278
279
0
        isFirst = false;
280
281
0
        if (aKeyValuePerLine)
282
0
        {
283
0
            OutputNewLine();
284
0
        }
285
0
    }
286
287
0
    if (!aKeyValuePerLine)
288
0
    {
289
0
        OutputFormat("]");
290
0
    }
291
0
}
292
293
const char *Utils::PercentageToString(uint16_t aValue, PercentageStringBuffer &aBuffer)
294
0
{
295
0
    uint32_t     scaledValue = aValue;
296
0
    StringWriter writer(aBuffer.mChars, sizeof(aBuffer.mChars));
297
298
0
    scaledValue = (scaledValue * 10000) / 0xffff;
299
0
    writer.Append("%u.%02u", static_cast<uint16_t>(scaledValue / 100), static_cast<uint16_t>(scaledValue % 100));
300
301
0
    return aBuffer.mChars;
302
0
}
303
304
#endif // OPENTHREAD_FTD || OPENTHREAD_MTD
305
306
126k
void Utils::OutputFormatV(const char *aFormat, va_list aArguments) { mImplementer.OutputV(aFormat, aArguments); }
307
308
void OutputImplementer::OutputV(const char *aFormat, va_list aArguments)
309
126k
{
310
126k
#if OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE
311
126k
    va_list args;
312
126k
    int     charsWritten;
313
126k
    bool    truncated = false;
314
315
126k
    va_copy(args, aArguments);
316
126k
#endif
317
318
126k
    mCallback(mCallbackContext, aFormat, aArguments);
319
320
126k
#if OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE
321
126k
    VerifyOrExit(mEmittingCommandOutput);
322
323
111k
    charsWritten = vsnprintf(&mOutputString[mOutputLength], sizeof(mOutputString) - mOutputLength, aFormat, args);
324
325
111k
    VerifyOrExit(charsWritten >= 0, mOutputLength = 0);
326
327
111k
    if (static_cast<uint32_t>(charsWritten) >= sizeof(mOutputString) - mOutputLength)
328
0
    {
329
0
        truncated     = true;
330
0
        mOutputLength = sizeof(mOutputString) - 1;
331
0
    }
332
111k
    else
333
111k
    {
334
111k
        mOutputLength += charsWritten;
335
111k
    }
336
337
132k
    while (true)
338
132k
    {
339
132k
        char *lineEnd = strchr(mOutputString, '\r');
340
341
132k
        if (lineEnd == nullptr)
342
111k
        {
343
111k
            break;
344
111k
        }
345
346
20.6k
        *lineEnd = '\0';
347
348
20.6k
        if (lineEnd > mOutputString)
349
20.5k
        {
350
20.5k
            otLogCli(OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_LEVEL, "Output: %s", mOutputString);
351
20.5k
        }
352
353
20.6k
        lineEnd++;
354
355
41.4k
        while ((*lineEnd == '\n') || (*lineEnd == '\r'))
356
20.7k
        {
357
20.7k
            lineEnd++;
358
20.7k
        }
359
360
        // Example of the pointers and lengths.
361
        //
362
        // - mOutputString = "hi\r\nmore"
363
        // - mOutputLength = 8
364
        // - lineEnd       = &mOutputString[4]
365
        //
366
        //
367
        //   0    1    2    3    4    5    6    7    8    9
368
        // +----+----+----+----+----+----+----+----+----+---
369
        // | h  | i  | \r | \n | m  | o  | r  | e  | \0 |
370
        // +----+----+----+----+----+----+----+----+----+---
371
        //                       ^                   ^
372
        //                       |                   |
373
        //                    lineEnd    mOutputString[mOutputLength]
374
        //
375
        //
376
        // New length is `&mOutputString[8] - &mOutputString[4] -> 4`.
377
        //
378
        // We move (newLen + 1 = 5) chars from `lineEnd` to start of
379
        // `mOutputString` which will include the `\0` char.
380
        //
381
        // If `lineEnd` and `mOutputString[mOutputLength]` are the same
382
        // the code works correctly as well  (new length set to zero and
383
        // the `\0` is copied).
384
385
20.6k
        mOutputLength = static_cast<uint16_t>(&mOutputString[mOutputLength] - lineEnd);
386
20.6k
        memmove(mOutputString, lineEnd, mOutputLength + 1);
387
20.6k
    }
388
389
111k
    if (truncated)
390
0
    {
391
0
        otLogCli(OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_LEVEL, "Output: %s ...", mOutputString);
392
0
        mOutputLength = 0;
393
0
    }
394
395
126k
exit:
396
126k
    va_end(args);
397
126k
#endif // OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE
398
126k
}
399
400
#if OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE
401
void Utils::LogInput(const Arg *aArgs)
402
6.98k
{
403
6.98k
    String<kInputOutputLogStringSize> inputString;
404
405
35.2k
    for (bool isFirst = true; !aArgs->IsEmpty(); aArgs++, isFirst = false)
406
28.3k
    {
407
28.3k
        inputString.Append(isFirst ? "%s" : " %s", aArgs->GetCString());
408
28.3k
    }
409
410
6.98k
    otLogCli(OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_LEVEL, "Input: %s", inputString.AsCString());
411
6.98k
}
412
#endif
413
414
void Utils::OutputTableHeader(uint8_t aNumColumns, const char *const aTitles[], const uint8_t aWidths[])
415
694
{
416
4.56k
    for (uint8_t index = 0; index < aNumColumns; index++)
417
3.87k
    {
418
3.87k
        const char *title       = aTitles[index];
419
3.87k
        uint8_t     width       = aWidths[index];
420
3.87k
        size_t      titleLength = strlen(title);
421
422
3.87k
        if (titleLength + 2 <= width)
423
3.49k
        {
424
            // `title` fits in column width so we write it with extra space
425
            // at beginning and end ("| Title    |").
426
427
3.49k
            OutputFormat("| %*s", -static_cast<int>(width - 1), title);
428
3.49k
        }
429
378
        else
430
378
        {
431
            // Use narrow style (no space at beginning) and write as many
432
            // chars from `title` as it can fit in the given column width
433
            // ("|Title|").
434
435
378
            OutputFormat("|%*.*s", -static_cast<int>(width), width, title);
436
378
        }
437
3.87k
    }
438
439
694
    OutputLine("|");
440
694
    OutputTableSeparator(aNumColumns, aWidths);
441
694
}
442
443
void Utils::OutputTableSeparator(uint8_t aNumColumns, const uint8_t aWidths[])
444
694
{
445
4.56k
    for (uint8_t index = 0; index < aNumColumns; index++)
446
3.87k
    {
447
3.87k
        OutputFormat("+");
448
449
55.7k
        for (uint8_t width = aWidths[index]; width != 0; width--)
450
51.8k
        {
451
51.8k
            OutputFormat("-");
452
51.8k
        }
453
3.87k
    }
454
455
694
    OutputLine("+");
456
694
}
457
458
otError Utils::ParseEnableOrDisable(const Arg &aArg, bool &aEnable)
459
296
{
460
296
    otError error = OT_ERROR_NONE;
461
462
296
    if (aArg == "enable")
463
9
    {
464
9
        aEnable = true;
465
9
    }
466
287
    else if (aArg == "disable")
467
8
    {
468
8
        aEnable = false;
469
8
    }
470
279
    else
471
279
    {
472
279
        error = OT_ERROR_INVALID_COMMAND;
473
279
    }
474
475
296
    return error;
476
296
}
477
478
otError Utils::ProcessEnableDisable(Arg aArgs[], SetEnabledHandler aSetEnabledHandler)
479
291
{
480
291
    otError error = OT_ERROR_NONE;
481
291
    bool    enable;
482
483
291
    if (ParseEnableOrDisable(aArgs[0], enable) == OT_ERROR_NONE)
484
16
    {
485
16
        aSetEnabledHandler(GetInstancePtr(), enable);
486
16
    }
487
275
    else
488
275
    {
489
275
        error = OT_ERROR_INVALID_COMMAND;
490
275
    }
491
492
291
    return error;
493
291
}
494
495
otError Utils::ProcessEnableDisable(Arg aArgs[], SetEnabledHandlerFailable aSetEnabledHandler)
496
1
{
497
1
    otError error = OT_ERROR_NONE;
498
1
    bool    enable;
499
500
1
    if (ParseEnableOrDisable(aArgs[0], enable) == OT_ERROR_NONE)
501
0
    {
502
0
        error = aSetEnabledHandler(GetInstancePtr(), enable);
503
0
    }
504
1
    else
505
1
    {
506
1
        error = OT_ERROR_INVALID_COMMAND;
507
1
    }
508
509
1
    return error;
510
1
}
511
512
otError Utils::ProcessEnableDisable(Arg               aArgs[],
513
                                    IsEnabledHandler  aIsEnabledHandler,
514
                                    SetEnabledHandler aSetEnabledHandler)
515
99
{
516
99
    otError error = OT_ERROR_NONE;
517
518
99
    if (aArgs[0].IsEmpty())
519
30
    {
520
30
        OutputEnabledDisabledStatus(aIsEnabledHandler(GetInstancePtr()));
521
30
    }
522
69
    else
523
69
    {
524
69
        error = ProcessEnableDisable(aArgs, aSetEnabledHandler);
525
69
    }
526
527
99
    return error;
528
99
}
529
530
otError Utils::ProcessEnableDisable(Arg                       aArgs[],
531
                                    IsEnabledHandler          aIsEnabledHandler,
532
                                    SetEnabledHandlerFailable aSetEnabledHandler)
533
2
{
534
2
    otError error = OT_ERROR_NONE;
535
536
2
    if (aArgs[0].IsEmpty())
537
1
    {
538
1
        OutputEnabledDisabledStatus(aIsEnabledHandler(GetInstancePtr()));
539
1
    }
540
1
    else
541
1
    {
542
1
        error = ProcessEnableDisable(aArgs, aSetEnabledHandler);
543
1
    }
544
545
2
    return error;
546
2
}
547
548
otError Utils::ParseJoinerDiscerner(Arg &aArg, otJoinerDiscerner &aDiscerner)
549
11
{
550
11
    otError error;
551
11
    char   *separator;
552
553
11
    VerifyOrExit(!aArg.IsEmpty(), error = OT_ERROR_INVALID_ARGS);
554
555
11
    separator = strstr(aArg.GetCString(), "/");
556
557
11
    VerifyOrExit(separator != nullptr, error = OT_ERROR_NOT_FOUND);
558
559
9
    SuccessOrExit(error = ot::Utils::CmdLineParser::ParseAsUint8(separator + 1, aDiscerner.mLength));
560
7
    VerifyOrExit(aDiscerner.mLength > 0 && aDiscerner.mLength <= 64, error = OT_ERROR_INVALID_ARGS);
561
5
    *separator = '\0';
562
5
    error      = aArg.ParseAsUint64(aDiscerner.mValue);
563
564
11
exit:
565
11
    return error;
566
5
}
567
568
otError Utils::ParsePreference(const Arg &aArg, otRoutePreference &aPreference)
569
236
{
570
236
    otError error = OT_ERROR_NONE;
571
572
236
    if (aArg == "high")
573
0
    {
574
0
        aPreference = OT_ROUTE_PREFERENCE_HIGH;
575
0
    }
576
236
    else if (aArg == "med")
577
1
    {
578
1
        aPreference = OT_ROUTE_PREFERENCE_MED;
579
1
    }
580
235
    else if (aArg == "low")
581
1
    {
582
1
        aPreference = OT_ROUTE_PREFERENCE_LOW;
583
1
    }
584
234
    else
585
234
    {
586
234
        error = OT_ERROR_INVALID_ARGS;
587
234
    }
588
589
236
    return error;
590
236
}
591
592
const char *Utils::PreferenceToString(signed int aPreference)
593
241
{
594
241
    const char *str = "";
595
596
241
    switch (aPreference)
597
241
    {
598
171
    case OT_ROUTE_PREFERENCE_LOW:
599
171
        str = "low";
600
171
        break;
601
602
70
    case OT_ROUTE_PREFERENCE_MED:
603
70
        str = "med";
604
70
        break;
605
606
0
    case OT_ROUTE_PREFERENCE_HIGH:
607
0
        str = "high";
608
0
        break;
609
610
0
    default:
611
0
        break;
612
241
    }
613
614
241
    return str;
615
241
}
616
617
#if OPENTHREAD_FTD || OPENTHREAD_MTD
618
otError Utils::ParseToIp6Address(otInstance *aInstance, const Arg &aArg, otIp6Address &aAddress, bool &aSynthesized)
619
1.32k
{
620
1.32k
    Error error = OT_ERROR_NONE;
621
622
1.32k
    VerifyOrExit(!aArg.IsEmpty(), error = OT_ERROR_INVALID_ARGS);
623
1.32k
    error        = aArg.ParseAsIp6Address(aAddress);
624
1.32k
    aSynthesized = false;
625
626
1.32k
    if (error != OT_ERROR_NONE)
627
25
    {
628
        // It might be an IPv4 address, let's have a try.
629
25
        otIp4Address ip4Address;
630
631
        // Do not touch the error value if we failed to parse it as an IPv4 address.
632
25
        SuccessOrExit(aArg.ParseAsIp4Address(ip4Address));
633
8
        SuccessOrExit(error = otNat64SynthesizeIp6Address(aInstance, &ip4Address, &aAddress));
634
8
        aSynthesized = true;
635
8
    }
636
637
1.32k
exit:
638
1.32k
    return error;
639
1.32k
}
640
641
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
642
otError Utils::ParsePrefix(Arg aArgs[], otBorderRouterConfig &aConfig)
643
101
{
644
101
    otError error = OT_ERROR_NONE;
645
646
101
    ClearAllBytes(aConfig);
647
648
101
    SuccessOrExit(error = aArgs[0].ParseAsIp6Prefix(aConfig.mPrefix));
649
97
    aArgs++;
650
651
228
    for (; !aArgs->IsEmpty(); aArgs++)
652
138
    {
653
138
        otRoutePreference preference;
654
655
138
        if (ParsePreference(*aArgs, preference) == OT_ERROR_NONE)
656
0
        {
657
0
            aConfig.mPreference = preference;
658
0
        }
659
138
        else
660
138
        {
661
2.17k
            for (char *arg = aArgs->GetCString(); *arg != '\0'; arg++)
662
2.03k
            {
663
2.03k
                switch (*arg)
664
2.03k
                {
665
204
                case 'p':
666
204
                    aConfig.mPreferred = true;
667
204
                    break;
668
669
207
                case 'a':
670
207
                    aConfig.mSlaac = true;
671
207
                    break;
672
673
200
                case 'd':
674
200
                    aConfig.mDhcp = true;
675
200
                    break;
676
677
198
                case 'c':
678
198
                    aConfig.mConfigure = true;
679
198
                    break;
680
681
204
                case 'r':
682
204
                    aConfig.mDefaultRoute = true;
683
204
                    break;
684
685
198
                case 'o':
686
198
                    aConfig.mOnMesh = true;
687
198
                    break;
688
689
195
                case 's':
690
195
                    aConfig.mStable = true;
691
195
                    break;
692
693
206
                case 'n':
694
206
                    aConfig.mNdDns = true;
695
206
                    break;
696
697
0
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
698
201
                case 'D':
699
201
                    aConfig.mDp = true;
700
201
                    break;
701
0
#endif
702
219
                case '-':
703
219
                    break;
704
705
7
                default:
706
7
                    ExitNow(error = OT_ERROR_INVALID_ARGS);
707
2.03k
                }
708
2.03k
            }
709
138
        }
710
138
    }
711
712
101
exit:
713
101
    return error;
714
97
}
715
716
otError Utils::ParseRoute(Arg aArgs[], otExternalRouteConfig &aConfig)
717
94
{
718
94
    otError error = OT_ERROR_NONE;
719
720
94
    ClearAllBytes(aConfig);
721
722
94
    SuccessOrExit(error = aArgs[0].ParseAsIp6Prefix(aConfig.mPrefix));
723
71
    aArgs++;
724
725
156
    for (; !aArgs->IsEmpty(); aArgs++)
726
94
    {
727
94
        otRoutePreference preference;
728
729
94
        if (ParsePreference(*aArgs, preference) == OT_ERROR_NONE)
730
0
        {
731
0
            aConfig.mPreference = preference;
732
0
        }
733
94
        else
734
94
        {
735
987
            for (char *arg = aArgs->GetCString(); *arg != '\0'; arg++)
736
902
            {
737
902
                switch (*arg)
738
902
                {
739
202
                case 's':
740
202
                    aConfig.mStable = true;
741
202
                    break;
742
743
248
                case 'n':
744
248
                    aConfig.mNat64 = true;
745
248
                    break;
746
747
245
                case 'a':
748
245
                    aConfig.mAdvPio = true;
749
245
                    break;
750
751
198
                case '-':
752
198
                    break;
753
754
9
                default:
755
9
                    ExitNow(error = OT_ERROR_INVALID_ARGS);
756
902
                }
757
902
            }
758
94
        }
759
94
    }
760
761
94
exit:
762
94
    return error;
763
71
}
764
#endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
765
#endif // #if OPENTHREAD_FTD || OPENTHREAD_MTD
766
767
const char *Utils::LinkModeToString(const otLinkModeConfig &aLinkMode, char (&aStringBuffer)[kLinkModeStringSize])
768
84
{
769
84
    char *flagsPtr = &aStringBuffer[0];
770
771
84
    if (aLinkMode.mRxOnWhenIdle)
772
84
    {
773
84
        *flagsPtr++ = 'r';
774
84
    }
775
776
84
    if (aLinkMode.mDeviceType)
777
84
    {
778
84
        *flagsPtr++ = 'd';
779
84
    }
780
781
84
    if (aLinkMode.mNetworkData)
782
84
    {
783
84
        *flagsPtr++ = 'n';
784
84
    }
785
786
84
    if (flagsPtr == &aStringBuffer[0])
787
0
    {
788
0
        *flagsPtr++ = '-';
789
0
    }
790
791
84
    *flagsPtr = '\0';
792
793
84
    return aStringBuffer;
794
84
}
795
796
const char *Utils::AddressOriginToString(uint8_t aOrigin)
797
303
{
798
303
    static const char *const kOriginStrings[4] = {
799
303
        "thread", // 0, OT_ADDRESS_ORIGIN_THREAD
800
303
        "slaac",  // 1, OT_ADDRESS_ORIGIN_SLAAC
801
303
        "dhcp6",  // 2, OT_ADDRESS_ORIGIN_DHCPV6
802
303
        "manual", // 3, OT_ADDRESS_ORIGIN_MANUAL
803
303
    };
804
805
303
    static_assert(0 == OT_ADDRESS_ORIGIN_THREAD, "OT_ADDRESS_ORIGIN_THREAD value is incorrect");
806
303
    static_assert(1 == OT_ADDRESS_ORIGIN_SLAAC, "OT_ADDRESS_ORIGIN_SLAAC value is incorrect");
807
303
    static_assert(2 == OT_ADDRESS_ORIGIN_DHCPV6, "OT_ADDRESS_ORIGIN_DHCPV6 value is incorrect");
808
303
    static_assert(3 == OT_ADDRESS_ORIGIN_MANUAL, "OT_ADDRESS_ORIGIN_MANUAL value is incorrect");
809
810
303
    return Stringify(aOrigin, kOriginStrings);
811
303
}
812
813
const char *Utils::BorderRoutingStateToString(otBorderRoutingState aState)
814
25
{
815
25
    static const char *const kStateStrings[] = {
816
25
        "uninitialized", // (0) OT_BORDER_ROUTING_STATE_UNINITIALIZED
817
25
        "disabled",      // (1) OT_BORDER_ROUTING_STATE_DISABLED
818
25
        "stopped",       // (2) OT_BORDER_ROUTING_STATE_STOPPED
819
25
        "running",       // (3) OT_BORDER_ROUTING_STATE_RUNNING
820
25
    };
821
822
25
    static_assert(0 == OT_BORDER_ROUTING_STATE_UNINITIALIZED, "STATE_UNINITIALIZED value is incorrect");
823
25
    static_assert(1 == OT_BORDER_ROUTING_STATE_DISABLED, "STATE_DISABLED value is incorrect");
824
25
    static_assert(2 == OT_BORDER_ROUTING_STATE_STOPPED, "STATE_STOPPED value is incorrect");
825
25
    static_assert(3 == OT_BORDER_ROUTING_STATE_RUNNING, "STATE_RUNNING value is incorrect");
826
827
25
    return Stringify(aState, kStateStrings);
828
25
}
829
830
} // namespace Cli
831
} // namespace ot