Coverage Report

Created: 2026-09-24 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/connectedhomeip/src/lib/support/CHIPArgParser.cpp
Line
Count
Source
1
/*
2
 *
3
 *    Copyright (c) 2020 Project CHIP Authors
4
 *    Copyright (c) 2017 Nest Labs, Inc.
5
 *    All rights reserved.
6
 *
7
 *    Licensed under the Apache License, Version 2.0 (the "License");
8
 *    you may not use this file except in compliance with the License.
9
 *    You may obtain a copy of the License at
10
 *
11
 *        http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 *    Unless required by applicable law or agreed to in writing, software
14
 *    distributed under the License is distributed on an "AS IS" BASIS,
15
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 *    See the License for the specific language governing permissions and
17
 *    limitations under the License.
18
 */
19
20
/**
21
 *    @file
22
 *      Support functions for parsing command-line arguments.
23
 *
24
 */
25
26
#include "CHIPArgParser.hpp"
27
28
#include <climits>
29
#include <ctype.h>
30
#include <errno.h>
31
#include <getopt.h>
32
#include <inttypes.h>
33
#include <lib/support/SafeInt.h>
34
#include <limits.h>
35
#include <stdarg.h>
36
#include <stdint.h>
37
#include <string.h>
38
#include <strings.h>
39
#include <unistd.h>
40
41
#include <lib/support/CHIPMem.h>
42
#include <lib/support/CHIPMemString.h>
43
#include <lib/support/EnforceFormat.h>
44
#include <lib/support/logging/Constants.h>
45
46
/*
47
 * TODO: Revisit these if and when fabric ID and node ID support has
48
 *       been integrated into the stack.
49
 */
50
#ifndef CHIP_ARG_PARSER_PARSE_FABRIC_ID
51
#define CHIP_ARG_PARSER_PARSE_FABRIC_ID 0
52
#endif // CHIP_ARG_PARSER_PARSE_FABRIC_ID
53
54
namespace chip {
55
namespace ArgParser {
56
57
using namespace chip;
58
59
static char * MakeShortOptions(OptionSet ** optSets);
60
static struct option * MakeLongOptions(OptionSet ** optSets);
61
static int32_t SplitArgs(char * argStr, char **& argList, char * initialArg = nullptr);
62
static bool GetNextArg(char *& parsePoint);
63
static size_t CountOptionSets(OptionSet * optSets[]);
64
static size_t CountAllOptions(OptionSet * optSets[]);
65
static void FindOptionByIndex(OptionSet ** optSets, int optIndex, OptionSet *& optSet, OptionDef *& optDef);
66
static void FindOptionById(OptionSet ** optSets, int optId, OptionSet *& optSet, OptionDef *& optDef);
67
static const char ** MakeUniqueHelpGroupNamesList(OptionSet * optSets[]);
68
static void PutStringWithNewLine(FILE * s, const char * str);
69
static void PutStringWithBlankLine(FILE * s, const char * str);
70
static bool SanityCheckOptions(OptionSet * optSets[]);
71
72
static inline bool IsShortOptionChar(int ch)
73
0
{
74
0
    return CanCastTo<uint8_t>(ch) && isgraph(ch);
75
0
}
76
77
/**
78
 * @brief
79
 * The list of OptionSets passed to the currently active ParseArgs() call.
80
 *
81
 * @details
82
 * This value will be NULL when no call to ParseArgs() is in progress.
83
 */
84
OptionSet ** gActiveOptionSets = nullptr;
85
86
/**
87
 * @brief
88
 * Pointer to function used to print errors that occur during argument parsing.
89
 *
90
 * @details
91
 * Applications should call PrintArgError() to report errors in their option and
92
 * non-option argument handling functions, rather than printing directly to
93
 * stdout/stderr.
94
 *
95
 * Defaults to a pointer to the `DefaultPrintArgError()` function.
96
 */
97
void (*PrintArgError)(const char * msg, ...) = DefaultPrintArgError;
98
99
/**
100
 * @fn bool ParseArgs(const char *progName, int argc, char * const argv[], OptionSet *optSets[],
101
 * NonOptionArgHandlerFunct nonOptArgHandler, bool ignoreUnknown)
102
 *
103
 * @brief
104
 * Parse a set of command line-style arguments, calling handling functions to process each
105
 * option and non-option argument.
106
 *
107
 * @param[in]  progName             The name of the program or context in which the arguments are
108
 *                                  being parsed.  This string will be used to prefix error
109
 *                                  messages and warnings.
110
 * @param[in]  argc                 The number of arguments to be parsed, plus 1.
111
 * @param[in]  argv                 An array of argument strings to be parsed.  The array length must
112
 *                                  be 1 greater than the value specified for argc, and
113
 *                                  argv[argc] must be set to NULL.  Argument parsing begins with the
114
 *                                  *second* array element (argv[1]); element 0 is ignored.
115
 * @param[in]  optSets              A list of pointers to `OptionSet` structures that define the legal
116
 *                                  options.  The supplied list must be terminated with a NULL.
117
 * @param[in]  nonOptArgHandler     A pointer to a function that will be called once option parsing
118
 *                                  is complete with any remaining non-option arguments .  The function
119
 *                                  is called regardless of whether any arguments remain.  If a NULL
120
 *                                  is passed `ParseArgs()` will report an error if any non-option
121
 *                                  arguments are present.
122
 * @param[in]  ignoreUnknown        If true, silently ignore any unrecognized options.
123
 *
124
 * @return                          `true` if all options and non-option arguments were parsed
125
 *                                  successfully; `false` if an option was unrecognized or if one of
126
 *                                  the handler functions failed (i.e. returned false).
127
 *
128
 *
129
 * @details
130
 * ParseArgs() takes a list of arguments (`argv`) and parses them according to a set of supplied
131
 * option definitions.  The function supports both long (--opt) and short (-o) options and implements
132
 * the same option syntax as the GNU getopt_long(3) function.
133
 *
134
 * Option definitions are passed to ParseArgs() as an array of OptionSet structures (`optSets`).
135
 * Each OptionSet contains an array of option definitions and a handler function. ParseArgs()
136
 * processes option arguments in the given order, calling the respective handler function for
137
 * each recognized option.  Once all options have been parsed, a separate non-option handler
138
 * function (`nonOptArgHandler`) is called once to process any remaining arguments.
139
 *
140
 *
141
 * ## OPTION SETS
142
 *
143
 * An OptionSet contains a set of option definitions along with a pointer to a handler function
144
 * that will be called when one of the associated options is encountered.  Option sets also
145
 * contain help text describing the syntax and purpose of each option (see OPTION HELP below).
146
 * Option sets are designed to allow the creation of re-usable collections of related options.
147
 * This simplifies the effort needed to maintain multiple applications that accept similar options
148
 * (e.g. test applications).
149
 *
150
 * There are two patterns for defining OptionSets--one can either initialize an instance of the
151
 * OptionSet struct itself, e.g. as a static global, or subclass OptionSetBase and provide a
152
 * constructor.  The latter uses a pure virtual `HandleOption()` function to delegate option
153
 * handling to the subclass.
154
 *
155
 * Lists of OptionSets are passed to the ParseArgs() function as a NULL-terminated array of pointers.
156
 * E.g.:
157
 *
158
 *     static OptionSet gToolOptions =
159
 *     {
160
 *         HandleOption,        // handler function
161
 *         gToolOptionDefs,  // array of option definitions
162
 *         "GENERAL OPTIONS",   // help group
163
 *         gToolOptionHelp   // option help text
164
 *     };
165
 *
166
 *     static OptionSet *gOptionSets[] =
167
 *     {
168
 *         &gToolOptions,
169
 *         &gNetworkOptions,
170
 *         &gTestingOptions,
171
 *         &gHelpOptions,
172
 *         NULL
173
 *     };
174
 *
175
 *     int main(int argc, char *argv[])
176
 *     {
177
 *         if (!ParseArgs("test-app", argc, argv, gOptionSets))
178
 *         {
179
 *             ...
180
 *         }
181
 *     }
182
 *
183
 *
184
 * ## OPTION DEFINITIONS
185
 *
186
 * Options are defined using the `OptionDef` structure. Option definitions are organized as an array
187
 * of OptionDef elements, where each element contains: the name of the option, a integer id that is
188
 * used to identify the option, and whether the option expects/allows an argument.  The end of the
189
 * option array is signaled by a NULL Name field.  E.g.:
190
 *
191
 *     enum
192
 *     {
193
 *         kOpt_Listen = 1000,
194
 *         kOpt_Length,
195
 *         kOpt_Count,
196
 *     };
197
 *
198
 *     static OptionDef gToolOptionDefs[] =
199
 *     {
200
 *         // NAME         REQUIRES/ALLOWS ARG?  ID/SHORT OPTION CHAR
201
 *         // ============================================================
202
 *         {  "listen",    kNoArgument,          kOpt_Listen     },
203
 *         {  "length",    kArgumentRequired,    kOpt_Length     },
204
 *         {  "count",     kArgumentRequired,    kOpt_Count      },
205
 *         {  "num",       kArgumentRequired,    kOpt_Count      }, // alias for --count
206
 *         {  "debug",     kArgumentOptional,    'd'             },
207
 *         {  "help",      kNoArgument,          'h'             },
208
 *         {  NULL }
209
 *     };
210
 *
211
 *
212
 * ## OPTION IDS
213
 *
214
 * Option ids identify options to the code that handles them (the OptionHandler function). Option ids
215
 * are relative to the OptionSet in which they appear, and thus may be reused across different
216
 * OptionSets (however see SHORT OPTIONS below).  Common convention is to start numbering option ids
217
 * at 1000, however any number > 128 can be used.  Alias options can be created by using the same
218
 * option id with different option names.
219
 *
220
 *
221
 * ## SHORT OPTIONS
222
 *
223
 * Unlike getopt_long(3), ParseArgs() does not take a separate string specifying the list of short
224
 * option characters.  Rather, any option whose id value falls in the range of graphical ASCII
225
 * characters will allow that character to be used as a short option.
226
 *
227
 * ParseArgs() requires that short option characters be unique across *all* OptionSets.  Because of
228
 * this, the use of short options is discouraged for any  OptionSets that are shared across programs
229
 * due to the significant chance for collisions.  Short options characters may be reused within a
230
 * single OptionSet to allow for the creation of alias long option names.
231
 *
232
 *
233
 * ## OPTION HELP
234
 *
235
 * Each OptionSet contains an `OptionHelp` string that describes the purpose and syntax of the
236
 * associated options.  These strings are used by the `PrintOptionHelp()` function to generate
237
 * option usage information.
238
 *
239
 * By convention, option help strings consist of a syntax example following by a textual
240
 * description of the option.  If the option has a short version, or an alias name, it is given
241
 * before primary long name.  For consistency, syntax lines are indented with 2 spaces, while
242
 * description lines are indented with 7 spaces.  A single blank line follows each option
243
 * description, including the last one.
244
 *
245
 * E.g.:
246
 *
247
 *     static const char *const gToolOptionHelp =
248
 *         "  --listen\n"
249
 *         "       Listen and respond to requests sent from another node.\n"
250
 *         "\n"
251
 *         "  --length <num>\n"
252
 *         "       Send requests with the specified number of bytes in the payload.\n"
253
 *         "\n"
254
 *         "  --num, --count <num>\n"
255
 *         "       Send the specified number of requests and exit.\n"
256
 *         "\n"
257
 *         "  -d, --debug [<level>]\n"
258
 *         "       Set debug logging to the given level. (Default: 1)\n"
259
 *         "\n"
260
 *         "  -h, --help\n"
261
 *         "       Print help information.\n"
262
 *         "\n";
263
 *
264
 *
265
 * ## OPTION HELP GROUPS
266
 *
267
 * OptionSets contain a `HelpGroupName` string which is used to group options together in the
268
 * help output.  The `PrintOptionHelp()` function uses the HelpGroupName as a section title in
269
 * the generated usage output.  If multiple OptionSets have the same HelpGroupName,
270
 * PrintOptionHelp() will print the option help for the different OptionSets together under
271
 * a common section title.
272
 *
273
 */
274
bool ParseArgs(const char * progName, int argc, char * const argv[], OptionSet * optSets[],
275
               NonOptionArgHandlerFunct nonOptArgHandler, bool ignoreUnknown)
276
0
{
277
0
    bool res = false;
278
0
    char optName[64];
279
0
    char * optArg;
280
0
    char * shortOpts         = nullptr;
281
0
    struct option * longOpts = nullptr;
282
0
    OptionSet * curOptSet;
283
0
    OptionDef * curOpt;
284
0
    bool handlerRes;
285
#if CHIP_CONFIG_NON_POSIX_LONG_OPT
286
    int lastOptIndex    = 0;
287
    int subOptIndex     = 0;
288
    int currentOptIndex = 0;
289
#endif // CHIP_CONFIG_NON_POSIX_LONG_OPT
290
291
    // The getopt() functions do not support recursion, so exit immediately with an
292
    // error if called recursively.
293
0
    if (gActiveOptionSets != nullptr)
294
0
    {
295
0
        PrintArgError("INTERNAL ERROR: ParseArgs() called recursively\n", progName);
296
0
        return false;
297
0
    }
298
299
    // The C standard mandates that argv[argc] == NULL and certain versions of getopt() require this
300
    // to function properly.  So fail if this is not true.
301
0
    if (argv[argc] != nullptr)
302
0
    {
303
0
        PrintArgError("INTERNAL ERROR: argv[argc] != NULL\n", progName);
304
0
        return false;
305
0
    }
306
307
    // Set gActiveOptionSets to the current option set list.
308
0
    gActiveOptionSets = optSets;
309
310
0
    if (!SanityCheckOptions(optSets))
311
0
        goto done;
312
313
    // Generate a short options string in the format expected by getopt_long().
314
0
    shortOpts = MakeShortOptions(optSets);
315
0
    if (shortOpts == nullptr)
316
0
    {
317
0
        PrintArgError("%s: Memory allocation failure\n", progName);
318
0
        goto done;
319
0
    }
320
321
    // Generate a list of long option structures in the format expected by getopt_long().
322
0
    longOpts = MakeLongOptions(optSets);
323
0
    if (longOpts == nullptr)
324
0
    {
325
0
        PrintArgError("%s: Memory allocation failure\n", progName);
326
0
        goto done;
327
0
    }
328
329
    // Force getopt() to reset its internal state.
330
0
    optind = 0;
331
332
    // Process any option arguments...
333
0
    while (true)
334
0
    {
335
0
        int id;
336
0
        int optIndex = -1;
337
338
        // Attempt to match the current option argument (argv[optind]) against the defined long and short options.
339
0
        optarg = nullptr;
340
0
        optopt = 0;
341
#if CHIP_CONFIG_NON_POSIX_LONG_OPT
342
        // to check if index has changed
343
        lastOptIndex = currentOptIndex;
344
        // optind will not increment on error, this is why we need to keep track of the current option
345
        // this is for use when getopt_long fails to find the option and we need to print the error
346
        currentOptIndex = optind;
347
        // if it's the first run, optind is not set and we need to find the first option ourselves
348
        if (!currentOptIndex)
349
        {
350
            while (currentOptIndex < argc)
351
            {
352
                currentOptIndex++;
353
                if (*argv[currentOptIndex] == '-')
354
                {
355
                    break;
356
                }
357
            }
358
        }
359
        // similarly we need to keep track of short opts index for groups like "-fba"
360
        // if the index has not changed that means we are still analysing the same group
361
        if (lastOptIndex != currentOptIndex)
362
        {
363
            subOptIndex = 0;
364
        }
365
        else
366
        {
367
            subOptIndex++;
368
        }
369
#endif // CHIP_CONFIG_NON_POSIX_LONG_OPT
370
0
        id = getopt_long(argc, argv, shortOpts, longOpts, &optIndex);
371
372
        // Stop if there are no more options.
373
0
        if (id == -1)
374
0
            break;
375
376
        // If the current option is unrecognized, fail with an error message unless ignoreUnknown == true.
377
0
        if (id == '?')
378
0
        {
379
0
            if (ignoreUnknown)
380
0
                continue;
381
#if CHIP_CONFIG_NON_POSIX_LONG_OPT
382
            // getopt_long doesn't tell us if the option which failed to match is long or short so check
383
            bool isLongOption = false;
384
            if (strlen(argv[currentOptIndex]) > 2 && argv[currentOptIndex][1] == '-')
385
            {
386
                isLongOption = true;
387
            }
388
            if (optopt == 0 || isLongOption)
389
            {
390
                // getopt_long function incorrectly treats unknown long option as short opt group
391
                if (subOptIndex == 0)
392
                {
393
                    PrintArgError("%s: Unknown option: %s\n", progName, argv[currentOptIndex]);
394
                }
395
            }
396
            else if (optopt == '?')
397
            {
398
                PrintArgError("%s: Unknown option: -%c\n", progName, argv[currentOptIndex][subOptIndex + 1]);
399
            }
400
            else
401
            {
402
                PrintArgError("%s: Unknown option: -%c\n", progName, optopt);
403
            }
404
#else
405
0
            if (optopt != 0)
406
0
                PrintArgError("%s: Unknown option: -%c\n", progName, optopt);
407
0
            else
408
0
                PrintArgError("%s: Unknown option: %s\n", progName, argv[optind - 1]);
409
0
#endif // CHIP_CONFIG_NON_POSIX_LONG_OPT
410
0
            goto done;
411
0
        }
412
413
        // If the option was recognized, but it is lacking an argument, fail with
414
        // an error message.
415
0
        if (id == ':')
416
0
        {
417
            // NOTE: with the way getopt_long() works, it is impossible to tell whether the option that
418
            // was missing an argument was a long option or a short option.
419
#if CHIP_CONFIG_NON_POSIX_LONG_OPT
420
            PrintArgError("%s: Missing argument for %s option\n", progName, argv[currentOptIndex]);
421
#else
422
0
            PrintArgError("%s: Missing argument for %s option\n", progName, argv[optind - 1]);
423
0
#endif // CHIP_CONFIG_NON_POSIX_LONG_OPT
424
0
            goto done;
425
0
        }
426
427
        // If a long option was matched...
428
0
        if (optIndex != -1)
429
0
        {
430
431
            // Locate the option set and definition using the index value returned by getopt_long().
432
0
            FindOptionByIndex(optSets, optIndex, curOptSet, curOpt);
433
434
            // Form a string containing the name of the option as it appears on the command line.
435
0
            snprintf(optName, sizeof(optName), "--%s", curOpt->Name);
436
0
        }
437
438
        // Otherwise a short option was matched...
439
0
        else
440
0
        {
441
            // Locate the option set and definition using the option id.
442
0
            FindOptionById(optSets, id, curOptSet, curOpt);
443
444
            // Form a string containing the name of the short option as it would appears on the
445
            // command line if given by itself.
446
0
            snprintf(optName, sizeof(optName), "-%c", id);
447
0
        }
448
449
        // Prevent handlers from inadvertently using the getopt global optarg.
450
0
        optArg = optarg;
451
0
        optarg = nullptr;
452
453
        // Call the option handler function defined for the matching option set.
454
        // Exit immediately if the option handler failed.
455
0
        handlerRes = curOptSet->OptionHandler(progName, curOptSet, id, optName, optArg);
456
0
        if (!handlerRes)
457
0
            goto done;
458
0
    }
459
460
    // If supplied, call the non-option argument handler with the remaining arguments (if any).
461
0
    if (nonOptArgHandler != nullptr)
462
0
    {
463
0
        if (!nonOptArgHandler(progName, argc - optind, argv + optind))
464
0
            goto done;
465
0
    }
466
467
    // otherwise, if there are additional arguments, fail with an error.
468
0
    else if (optind < argc)
469
0
    {
470
0
        PrintArgError("%s: Unexpected argument: %s\n", progName, argv[optind]);
471
0
        goto done;
472
0
    }
473
474
0
    res = true;
475
476
0
done:
477
478
0
    if (shortOpts != nullptr)
479
0
        chip::Platform::MemoryFree(shortOpts);
480
0
    if (longOpts != nullptr)
481
0
        chip::Platform::MemoryFree(longOpts);
482
483
0
    gActiveOptionSets = nullptr;
484
485
0
    return res;
486
0
}
487
488
bool ParseArgs(const char * progName, int argc, char * const argv[], OptionSet * optSets[],
489
               NonOptionArgHandlerFunct nonOptArgHandler)
490
0
{
491
0
    return ParseArgs(progName, argc, argv, optSets, nonOptArgHandler, false);
492
0
}
493
494
bool ParseArgs(const char * progName, int argc, char * const argv[], OptionSet * optSets[])
495
0
{
496
0
    return ParseArgs(progName, argc, argv, optSets, nullptr, false);
497
0
}
498
499
/**
500
 * @brief
501
 * Parse a set of arguments from a given string.
502
 *
503
 * @param[in]  progName             The name of the program or context in which the arguments are
504
 *                                  being parsed.  This string will be used to prefix error
505
 *                                  messages and warnings.
506
 * @param[in]  argStr               A string containing options and arguments to be parsed.
507
 * @param[in]  optSets              A list of pointers to `OptionSet` structures that define the legal
508
 *                                  options.  The supplied list must be terminated with a NULL.
509
 * @param[in]  nonOptArgHandler     A pointer to a function that will be called once option parsing
510
 *                                  is complete with any remaining non-option arguments .  The function
511
 *                                  is called regardless of whether any arguments remain.  If a NULL
512
 *                                  is passed `ParseArgs()` will report an error if any non-option
513
 *                                  arguments are present.
514
 * @param[in]  ignoreUnknown        If true, silently ignore any unrecognized options.
515
 *
516
 * @return                          `true` if all options and non-option arguments were parsed
517
 *                                  successfully; `false` if an option was unrecognized, if one of
518
 *                                  the handler functions failed (i.e. returned false) or if an
519
 *                                  internal error occurred.
520
 *
521
 * @details
522
 * ParseArgsFromString() splits a given string (`argStr`) into a set of arguments and parses the
523
 * arguments using the ParseArgs() function.
524
 *
525
 * The syntax of the input strings is similar to unix shell command syntax, but with a simplified
526
 * quoting scheme.  Specifically:
527
 *
528
 * - Arguments are delimited by whitespace, unless the whitespace is quoted or escaped.
529
 *
530
 * - A backslash escapes the following character, causing it to be treated as a normal character.
531
 * The backslash itself is stripped.
532
 *
533
 * - Single or double quotes begin/end quoted substrings.  Within a substring, the only special
534
 * characters are backslash, which escapes the next character, and the corresponding end quote.
535
 * The begin/end quote characters are stripped.
536
 *
537
 * E.g.:
538
 *
539
 *     --listen --count 10 --sw-version '1.0 (DEVELOPMENT)' "--hostname=nest.com"
540
 *
541
 */
542
bool ParseArgsFromString(const char * progName, const char * argStr, OptionSet * optSets[],
543
                         NonOptionArgHandlerFunct nonOptArgHandler, bool ignoreUnknown)
544
0
{
545
0
    char ** argv = nullptr;
546
0
    int argc;
547
0
    bool res;
548
549
0
    chip::Platform::ScopedMemoryString argStrCopy(argStr, strlen(argStr));
550
0
    if (!argStrCopy)
551
0
    {
552
0
        PrintArgError("%s: Memory allocation failure\n", progName);
553
0
        return false;
554
0
    }
555
556
0
    argc = SplitArgs(argStrCopy.Get(), argv, const_cast<char *>(progName));
557
0
    if (argc < 0)
558
0
    {
559
0
        PrintArgError("%s: Memory allocation failure\n", progName);
560
0
        return false;
561
0
    }
562
563
0
    res = ParseArgs(progName, argc, argv, optSets, nonOptArgHandler, ignoreUnknown);
564
565
0
    chip::Platform::MemoryFree(argv);
566
567
0
    return res;
568
0
}
569
570
bool ParseArgsFromString(const char * progName, const char * argStr, OptionSet * optSets[],
571
                         NonOptionArgHandlerFunct nonOptArgHandler)
572
0
{
573
0
    return ParseArgsFromString(progName, argStr, optSets, nonOptArgHandler, false);
574
0
}
575
576
bool ParseArgsFromString(const char * progName, const char * argStr, OptionSet * optSets[])
577
0
{
578
0
    return ParseArgsFromString(progName, argStr, optSets, nullptr, false);
579
0
}
580
581
/**
582
 * @brief
583
 * Parse a set of arguments from a named environment variable
584
 *
585
 * @param[in]  progName             The name of the program or context in which the arguments are
586
 *                                  being parsed.  This string will be used to prefix error
587
 *                                  messages and warnings.
588
 * @param[in]  varName              The name of the environment variable.
589
 * @param[in]  optSets              A list of pointers to `OptionSet` structures that define the legal
590
 *                                  options.  The supplied list must be terminated with a NULL.
591
 * @param[in]  nonOptArgHandler     A pointer to a function that will be called once option parsing
592
 *                                  is complete with any remaining non-option arguments .  The function
593
 *                                  is called regardless of whether any arguments remain.  If a NULL
594
 *                                  is passed `ParseArgs()` will report an error if any non-option
595
 *                                  arguments are present.
596
 * @param[in]  ignoreUnknown        If true, silently ignore any unrecognized options.
597
 *
598
 * @return                          `true` if all options and non-option arguments were parsed
599
 *                                  successfully, or if the specified environment variable is not set;
600
 *                                  `false` if an option was unrecognized, if one of the handler
601
 *                                  functions failed (i.e. returned false) or if an internal error
602
 *                                  occurred.
603
 *
604
 * @details
605
 * ParseArgsFromEnvVar() reads a named environment variable and passes the value to `ParseArgsFromString()`
606
 * for parsing.  If the environment variable is not set, the function does nothing.
607
 */
608
609
bool ParseArgsFromEnvVar(const char * progName, const char * varName, OptionSet * optSets[],
610
                         NonOptionArgHandlerFunct nonOptArgHandler, bool ignoreUnknown)
611
0
{
612
0
    const char * argStr = getenv(varName);
613
0
    if (argStr == nullptr)
614
0
        return true;
615
0
    return ParseArgsFromString(progName, argStr, optSets, nonOptArgHandler, ignoreUnknown);
616
0
}
617
618
bool ParseArgsFromEnvVar(const char * progName, const char * varName, OptionSet * optSets[])
619
0
{
620
0
    return ParseArgsFromEnvVar(progName, varName, optSets, nullptr, false);
621
0
}
622
623
bool ParseArgsFromEnvVar(const char * progName, const char * varName, OptionSet * optSets[],
624
                         NonOptionArgHandlerFunct nonOptArgHandler)
625
0
{
626
0
    return ParseArgsFromEnvVar(progName, varName, optSets, nonOptArgHandler, false);
627
0
}
628
629
/**
630
 * @brief
631
 * Print the help text for a specified list of options to a stream.
632
 *
633
 * @param[in]  optSets              A list of pointers to `OptionSet` structures that contain the
634
 *                                  help text to print.
635
 * @param[in]  s                    The FILE stream to which the help text should be printed.
636
 *
637
 */
638
void PrintOptionHelp(OptionSet * optSets[], FILE * s)
639
0
{
640
    // Get a list of the unique help group names for the given option sets.
641
0
    const char ** helpGroupNames = MakeUniqueHelpGroupNamesList(optSets);
642
0
    if (helpGroupNames == nullptr)
643
0
    {
644
0
        PrintArgError("Memory allocation failure\n");
645
0
        return;
646
0
    }
647
648
    // For each help group...
649
0
    for (size_t nameIndex = 0; helpGroupNames[nameIndex] != nullptr; nameIndex++)
650
0
    {
651
        // Print the group name.
652
0
        PutStringWithBlankLine(s, helpGroupNames[nameIndex]);
653
654
        // Print the option help text for all options that have the same group name.
655
0
        for (size_t optSetIndex = 0; optSets[optSetIndex] != nullptr; optSetIndex++)
656
0
            if (strcasecmp(helpGroupNames[nameIndex], optSets[optSetIndex]->HelpGroupName) == 0)
657
0
            {
658
0
                PutStringWithBlankLine(s, optSets[optSetIndex]->OptionHelp);
659
0
            }
660
0
    }
661
662
0
    chip::Platform::MemoryFree(helpGroupNames);
663
0
}
664
665
/**
666
 * @brief
667
 * Print an error message associated with argument parsing.
668
 *
669
 * @param[in]  msg   The message to be printed.
670
 *
671
 * @details
672
 * Default function used to print error messages that arise due to the parsing
673
 * of arguments.
674
 *
675
 * Applications should call through the PrintArgError function pointer, rather
676
 * than calling this function directly.
677
 */
678
void ENFORCE_FORMAT(1, 2) DefaultPrintArgError(const char * msg, ...)
679
0
{
680
0
    va_list ap;
681
682
0
    va_start(ap, msg);
683
0
    vfprintf(stderr, msg, ap);
684
0
    va_end(ap);
685
0
}
686
687
/**
688
 * Parse a string as a boolean value.
689
 *
690
 * This function accepts the following input values (case-insensitive):
691
 * "true", "yes", "t", "y", "1", "false", "no", "f", "n", "0".
692
 *
693
 * @param[in]  str    A pointer to a NULL-terminated C string representing
694
 *                    the value to parse.
695
 * @param[out] output A reference to storage for a bool to which the parsed
696
 *                    value will be stored on success.
697
 *
698
 * @return true on success; otherwise, false on failure.
699
 */
700
bool ParseBoolean(const char * str, bool & output)
701
0
{
702
0
    if (strcasecmp(str, "true") == 0 || strcasecmp(str, "yes") == 0 ||
703
0
        ((str[0] == '1' || str[0] == 't' || str[0] == 'T' || str[0] == 'y' || str[0] == 'Y') && str[1] == 0))
704
0
    {
705
0
        output = true;
706
0
        return true;
707
0
    }
708
709
0
    if (strcasecmp(str, "false") == 0 || strcasecmp(str, "no") == 0 ||
710
0
        ((str[0] == '0' || str[0] == 'f' || str[0] == 'F' || str[0] == 'n' || str[0] == 'N') && str[1] == 0))
711
0
    {
712
0
        output = false;
713
0
        return true;
714
0
    }
715
716
0
    return false;
717
0
}
718
719
/**
720
 * Parse and attempt to convert a string to a 64-bit unsigned integer,
721
 * applying the appropriate interpretation based on the base parameter.
722
 *
723
 * @param[in]  str    A pointer to a NULL-terminated C string representing
724
 *                    the integer to parse.
725
 * @param[out] output A reference to storage for a 64-bit unsigned integer
726
 *                    to which the parsed value will be stored on success.
727
 * @param[in]  base   The base according to which the string should be
728
 *                    interpreted and parsed. If 0 or 16, the string may
729
 *                    be hexadecimal and prefixed with "0x". Otherwise, a 0
730
 *                    is implied as 10 unless a leading 0 is encountered in
731
 *                    which 8 is implied.
732
 *
733
 * @return true on success; otherwise, false on failure.
734
 */
735
bool ParseInt(const char * str, uint64_t & output, int base)
736
0
{
737
0
    char * parseEnd;
738
739
0
    errno  = 0;
740
0
    output = strtoull(str, &parseEnd, base);
741
742
0
    return parseEnd > str && *parseEnd == 0 && (output != ULLONG_MAX || errno == 0);
743
0
}
744
745
/**
746
 * Parse and attempt to convert a string to a 32-bit unsigned integer,
747
 * applying the appropriate interpretation based on the base parameter.
748
 *
749
 * @param[in]  str    A pointer to a NULL-terminated C string representing
750
 *                    the integer to parse.
751
 * @param[out] output A reference to storage for a 32-bit unsigned integer
752
 *                    to which the parsed value will be stored on success.
753
 * @param[in]  base   The base according to which the string should be
754
 *                    interpreted and parsed. If 0 or 16, the string may
755
 *                    be hexadecimal and prefixed with "0x". Otherwise, a 0
756
 *                    is implied as 10 unless a leading 0 is encountered in
757
 *                    which 8 is implied.
758
 *
759
 * @return true on success; otherwise, false on failure.
760
 */
761
bool ParseInt(const char * str, uint32_t & output, int base)
762
0
{
763
0
    char * parseEnd;
764
0
    unsigned long v;
765
766
0
    errno = 0;
767
0
    v     = strtoul(str, &parseEnd, base);
768
0
    if (!CanCastTo<uint32_t>(v))
769
0
    {
770
0
        return false;
771
0
    }
772
0
    output = static_cast<uint32_t>(v);
773
774
0
    return parseEnd > str && *parseEnd == 0 && (v != ULONG_MAX || errno == 0);
775
0
}
776
777
/**
778
 * Parse and attempt to convert a string to a 32-bit signed integer,
779
 * applying the appropriate interpretation based on the base parameter.
780
 *
781
 * @param[in]  str    A pointer to a NULL-terminated C string representing
782
 *                    the integer to parse.
783
 * @param[out] output A reference to storage for a 32-bit signed integer
784
 *                    to which the parsed value will be stored on success.
785
 * @param[in]  base   The base according to which the string should be
786
 *                    interpreted and parsed. If 0 or 16, the string may
787
 *                    be hexadecimal and prefixed with "0x". Otherwise, a 0
788
 *                    is implied as 10 unless a leading 0 is encountered in
789
 *                    which 8 is implied.
790
 *
791
 * @return true on success; otherwise, false on failure.
792
 */
793
bool ParseInt(const char * str, int32_t & output, int base)
794
0
{
795
0
    char * parseEnd;
796
0
    long v;
797
798
0
    errno = 0;
799
0
    v     = strtol(str, &parseEnd, base);
800
0
    if (!CanCastTo<int32_t>(v))
801
0
    {
802
0
        return false;
803
0
    }
804
0
    output = static_cast<int32_t>(v);
805
806
0
    return parseEnd > str && *parseEnd == 0 && ((v != LONG_MIN && v != LONG_MAX) || errno == 0);
807
0
}
808
809
/**
810
 * Parse and attempt to convert a string to a 16-bit unsigned integer,
811
 * applying the appropriate interpretation based on the base parameter.
812
 *
813
 * @param[in]  str    A pointer to a NULL-terminated C string representing
814
 *                    the integer to parse.
815
 * @param[out] output A reference to storage for a 16-bit unsigned integer
816
 *                    to which the parsed value will be stored on success.
817
 * @param[in]  base   The base according to which the string should be
818
 *                    interpreted and parsed. If 0 or 16, the string may
819
 *                    be hexadecimal and prefixed with "0x". Otherwise, a 0
820
 *                    is implied as 10 unless a leading 0 is encountered in
821
 *                    which 8 is implied.
822
 *
823
 * @return true on success; otherwise, false on failure.
824
 */
825
bool ParseInt(const char * str, uint16_t & output, int base)
826
0
{
827
0
    uint32_t v;
828
829
0
    if (!ParseInt(str, v, base) || !CanCastTo<uint16_t>(v))
830
0
    {
831
0
        return false;
832
0
    }
833
0
    output = static_cast<uint16_t>(v);
834
835
0
    return true;
836
0
}
837
838
/**
839
 * Parse and attempt to convert a string to a 8-bit unsigned integer,
840
 * applying the appropriate interpretation based on the base parameter.
841
 *
842
 * @param[in]  str    A pointer to a NULL-terminated C string representing
843
 *                    the integer to parse.
844
 * @param[out] output A reference to storage for a 8-bit unsigned integer
845
 *                    to which the parsed value will be stored on success.
846
 * @param[in]  base   The base according to which the string should be
847
 *                    interpreted and parsed. If 0 or 16, the string may
848
 *                    be hexadecimal and prefixed with "0x". Otherwise, a 0
849
 *                    is implied as 10 unless a leading 0 is encountered in
850
 *                    which 8 is implied.
851
 *
852
 * @return true on success; otherwise, false on failure.
853
 */
854
bool ParseInt(const char * str, uint8_t & output, int base)
855
0
{
856
0
    uint32_t v;
857
858
0
    if (!ParseInt(str, v, base) || !CanCastTo<uint8_t>(v))
859
0
    {
860
0
        return false;
861
0
    }
862
0
    output = static_cast<uint8_t>(v);
863
864
0
    return true;
865
0
}
866
867
/**
868
 * Parse and attempt to convert a string interpreted as a decimal
869
 * value to a 64-bit unsigned integer, applying the appropriate
870
 * interpretation based on the base parameter.
871
 *
872
 * @param[in]  str    A pointer to a NULL-terminated C string representing
873
 *                    the integer to parse.
874
 * @param[out] output A reference to storage for a 64-bit unsigned integer
875
 *                    to which the parsed value will be stored on success.
876
 *
877
 * @return true on success; otherwise, false on failure.
878
 */
879
bool ParseInt(const char * str, uint64_t & output)
880
0
{
881
0
    const int base = 10;
882
883
0
    return ParseInt(str, output, base);
884
0
}
885
886
/**
887
 * Parse and attempt to convert a string interpreted as a decimal
888
 * value to a 32-bit unsigned integer, applying the appropriate
889
 * interpretation based on the base parameter.
890
 *
891
 * @param[in]  str    A pointer to a NULL-terminated C string representing
892
 *                    the integer to parse.
893
 * @param[out] output A reference to storage for a 32-bit unsigned integer
894
 *                    to which the parsed value will be stored on success.
895
 *
896
 * @return true on success; otherwise, false on failure.
897
 */
898
bool ParseInt(const char * str, uint32_t & output)
899
0
{
900
0
    const int base = 10;
901
902
0
    return ParseInt(str, output, base);
903
0
}
904
905
/**
906
 * Parse and attempt to convert a string interpreted as a decimal
907
 * value to a 32-bit signed integer, applying the appropriate
908
 * interpretation based on the base parameter.
909
 *
910
 * @param[in]  str    A pointer to a NULL-terminated C string representing
911
 *                    the integer to parse.
912
 * @param[out] output A reference to storage for a 32-bit signed integer
913
 *                    to which the parsed value will be stored on success.
914
 *
915
 * @return true on success; otherwise, false on failure.
916
 */
917
bool ParseInt(const char * str, int32_t & output)
918
0
{
919
0
    const int base = 10;
920
921
0
    return ParseInt(str, output, base);
922
0
}
923
924
/**
925
 * Parse and attempt to convert a string interpreted as a decimal
926
 * value to a 16-bit unsigned integer, applying the appropriate
927
 * interpretation based on the base parameter.
928
 *
929
 * @param[in]  str    A pointer to a NULL-terminated C string representing
930
 *                    the integer to parse.
931
 * @param[out] output A reference to storage for a 16-bit unsigned integer
932
 *                    to which the parsed value will be stored on success.
933
 *
934
 * @return true on success; otherwise, false on failure.
935
 */
936
bool ParseInt(const char * str, uint16_t & output)
937
0
{
938
0
    const int base    = 10;
939
0
    uint32_t output32 = 0;
940
941
0
    if ((ParseInt(str, output32, base)) && (output32 <= USHRT_MAX))
942
0
    {
943
0
        output = ((1 << 16) - 1) & output32;
944
0
        return true;
945
0
    }
946
947
0
    return false;
948
0
}
949
950
/**
951
 * Parse and attempt to convert a string interpreted as a decimal
952
 * value to a 16-bit signed integer, applying the appropriate
953
 * interpretation based on the base parameter.
954
 *
955
 * @param[in]  str    A pointer to a NULL-terminated C string representing
956
 *                    the integer to parse.
957
 * @param[out] output A reference to storage for a 16-bit signed integer
958
 *                    to which the parsed value will be stored on success.
959
 *
960
 * @return true on success; otherwise, false on failure.
961
 */
962
bool ParseInt(const char * str, int16_t & output)
963
0
{
964
0
    const int base   = 10;
965
0
    int32_t output32 = 0;
966
967
0
    if ((ParseInt(str, output32, base)) && (output32 <= SHRT_MAX && output32 >= SHRT_MIN))
968
0
    {
969
0
        output = static_cast<int16_t>(output32);
970
0
        return true;
971
0
    }
972
973
0
    return false;
974
0
}
975
976
/**
977
 * Parse and attempt to convert a string interpreted as a decimal
978
 * value to a 8-bit unsigned integer, applying the appropriate
979
 * interpretation based on the base parameter.
980
 *
981
 * @param[in]  str    A pointer to a NULL-terminated C string representing
982
 *                    the integer to parse.
983
 * @param[out] output A reference to storage for a 8-bit unsigned integer
984
 *                    to which the parsed value will be stored on success.
985
 *
986
 * @return true on success; otherwise, false on failure.
987
 */
988
bool ParseInt(const char * str, uint8_t & output)
989
0
{
990
0
    const int base    = 10;
991
0
    uint32_t output32 = 0;
992
993
0
    if ((ParseInt(str, output32, base)) && (output32 <= UCHAR_MAX))
994
0
    {
995
0
        output = ((1 << 8) - 1) & output32;
996
0
        return true;
997
0
    }
998
999
0
    return false;
1000
0
}
1001
1002
#if CHIP_ARG_PARSER_PARSE_FABRIC_ID
1003
/**
1004
 * Parse a CHIP fabric id in text form.
1005
 *
1006
 * @param[in]  str              A pointer to a NULL-terminated C string containing
1007
 *                              the fabric id to parse.
1008
 * @param[out] output           A reference to an uint64_t lvalue in which the
1009
 *                              parsed value will be stored on success.
1010
 * @param[in]  allowReserved    If true, allow the parsing of fabric ids in the
1011
 *                              reserved range.
1012
 *
1013
 * @return true if the value was successfully parsed; false if not.
1014
 *
1015
 * @details
1016
 * The ParseFabricId() function accepts a 64-bit fabric id given in hex format,
1017
 * with or without a leading '0x'.
1018
 */
1019
bool ParseFabricId(const char * str, uint64_t & fabricId, bool allowReserved)
1020
{
1021
    char * parseEnd;
1022
1023
    errno    = 0;
1024
    fabricId = strtoull(str, &parseEnd, 16);
1025
    return parseEnd > str && *parseEnd == 0 && (fabricId != ULLONG_MAX || errno == 0) &&
1026
        (allowReserved || fabricId < kReservedFabricIdStart);
1027
}
1028
#endif // CHIP_ARG_PARSER_PARSE_FABRIC_ID
1029
1030
/**
1031
 * Parse and attempt to convert a string to a 16-bit unsigned subnet
1032
 * ID, interpretting the string as hexadecimal.
1033
 *
1034
 * @param[in]     str       A pointer to a NULL-terminated C string
1035
 *                          representing the subnet ID, formatted as a
1036
 *                          hexadecimal, to parse.
1037
 * @param[in,out] subnetId  A reference to storage for a 16-bit unsigned
1038
 *                          integer to which the parsed subnet ID value
1039
 *                          will be stored on success.
1040
 *
1041
 * @return true on success; otherwise, false on failure.
1042
 */
1043
bool ParseSubnetId(const char * str, uint16_t & subnetId)
1044
0
{
1045
0
    char * parseEnd;
1046
0
    unsigned long temp;
1047
0
    bool valid;
1048
1049
    // Reset errno per the strtoul manual page.
1050
1051
0
    errno = 0;
1052
1053
    // Attempt to parse the subnet ID as a hexadecimal number.
1054
1055
0
    temp = strtoul(str, &parseEnd, 16);
1056
1057
    // Determine if the parse and conversion were valid.
1058
1059
0
    valid = (parseEnd > str &&                    // Parsed some valid hexadecimal digits
1060
0
             *parseEnd == 0 &&                    // Encountered no invalid hexadecimal digits
1061
0
             (temp != ULONG_MAX || errno == 0) && // No overflow (ERANGE) or invalid base (EINVAL) errors
1062
0
             temp <= USHRT_MAX);                  // Parsed value is valid for the domain (subnet ID)
1063
1064
0
    if (valid)
1065
0
    {
1066
0
        subnetId = static_cast<uint16_t>(temp);
1067
0
    }
1068
1069
0
    return valid;
1070
0
}
1071
1072
/**
1073
 * Parse a string of bytes given in hex form.
1074
 *
1075
 * @param[in]  hexStr           A pointer to the string to parse.
1076
 * @param[in]  strLen           The number of characters in hexStr to parse.
1077
 * @param[in]  outBuf           A pointer to a buffer into which the parse bytes will
1078
 *                              be stored.
1079
 * @param[in]  outBufSize       The size of the buffer pointed at by `outBuf`.
1080
 * @param[out] outDataLen       A reference to an integer that will receive the total
1081
 *                              number of bytes parsed.  In the event outBuf is not
1082
 *                              big enough to hold the given number of bytes, `outDataLen`
1083
 *                              will be set to UINT32_MAX.
1084
 *
1085
 * @return true if the value was successfully parsed; false if the input data is malformed,
1086
 * or if `outBuf` is too small.
1087
 *
1088
 * @details
1089
 * ParseHexString() expects the input to be in the form of pairs of hex digits (upper or lower case).
1090
 * Hex pairs can optionally be separated by any of the following characters: colon, semicolon, comma, period or dash.
1091
 * Additionally, whitespace characters anywhere in the input string are ignored.
1092
 */
1093
bool ParseHexString(const char * hexStr, uint32_t strLen, uint8_t * outBuf, uint32_t outBufSize, uint32_t & outDataLen)
1094
0
{
1095
0
    bool isFirstNibble     = true;
1096
0
    uint8_t firstNibbleVal = 0;
1097
0
    const char * p         = hexStr;
1098
0
    uint32_t dataLen       = 0;
1099
1100
0
    outDataLen = 0;
1101
1102
0
    for (; strLen > 0; p++, strLen--)
1103
0
    {
1104
0
        char c = *p;
1105
0
        uint8_t nibbleVal;
1106
1107
0
        if (c == 0)
1108
0
            break;
1109
0
        if (c >= '0' && c <= '9')
1110
0
            nibbleVal = static_cast<uint8_t>(c - '0');
1111
0
        else if (c >= 'a' && c <= 'f')
1112
0
            nibbleVal = static_cast<uint8_t>(10 + (c - 'a'));
1113
0
        else if (c >= 'A' && c <= 'F')
1114
0
            nibbleVal = static_cast<uint8_t>(10 + (c - 'A'));
1115
0
        else if (isspace(c))
1116
0
            continue;
1117
0
        else if (isFirstNibble && (c == ':' || c == ';' || c == ',' || c == '.' || c == '-'))
1118
0
            continue;
1119
0
        else
1120
0
        {
1121
0
            outDataLen = static_cast<decltype(strLen)>(p - hexStr);
1122
0
            return false;
1123
0
        }
1124
1125
0
        if (isFirstNibble)
1126
0
        {
1127
0
            firstNibbleVal = nibbleVal;
1128
0
            isFirstNibble  = false;
1129
0
        }
1130
0
        else
1131
0
        {
1132
0
            if (outBufSize == 0)
1133
0
            {
1134
0
                outDataLen = UINT32_MAX;
1135
0
                return false;
1136
0
            }
1137
1138
0
            *outBuf = static_cast<uint8_t>(firstNibbleVal << 4 | nibbleVal);
1139
1140
0
            outBuf++;
1141
0
            outBufSize--;
1142
0
            dataLen++;
1143
1144
0
            isFirstNibble = true;
1145
0
        }
1146
0
    }
1147
1148
0
    if (!isFirstNibble)
1149
0
    {
1150
0
        outDataLen = static_cast<decltype(strLen)>(p - hexStr);
1151
0
        return false;
1152
0
    }
1153
1154
0
    outDataLen = dataLen;
1155
1156
0
    return true;
1157
0
}
1158
1159
// ===== HelpOptions Methods =====
1160
1161
HelpOptions::HelpOptions(const char * appName, const char * appUsage, const char * appVersion) :
1162
0
    HelpOptions(appName, appUsage, appVersion, nullptr)
1163
0
{}
1164
1165
HelpOptions::HelpOptions(const char * appName, const char * appUsage, const char * appVersion, const char * appDesc)
1166
0
{
1167
    // clang-format off
1168
0
    static OptionDef optionDefs[] =
1169
0
    {
1170
0
        { "help",      kNoArgument, 'h' },
1171
0
        { "version",   kNoArgument, 'v' },
1172
0
        { }
1173
0
    };
1174
    // clang-format on
1175
0
    OptionDefs = optionDefs;
1176
1177
0
    HelpGroupName = "HELP OPTIONS";
1178
1179
0
    OptionHelp = "  -h, --help\n"
1180
0
                 "       Print this output and then exit.\n"
1181
0
                 "\n"
1182
0
                 "  -v, --version\n"
1183
0
                 "       Print the version and then exit.\n"
1184
0
                 "\n";
1185
1186
0
    AppName    = appName;
1187
0
    AppUsage   = appUsage;
1188
0
    AppVersion = appVersion;
1189
0
    AppDesc    = appDesc;
1190
0
}
1191
1192
/**
1193
 * Print a short description of the command's usage followed by instructions on how to get more help.
1194
 */
1195
void HelpOptions::PrintBriefUsage(FILE * s) const
1196
0
{
1197
0
    PutStringWithNewLine(s, AppUsage);
1198
0
    fprintf(s, "Try `%s --help' for more information.\n", AppName);
1199
0
}
1200
1201
/**
1202
 * Print the full usage information, including information on all available options.
1203
 */
1204
void HelpOptions::PrintLongUsage(OptionSet ** optSets, FILE * s) const
1205
0
{
1206
0
    PutStringWithBlankLine(s, AppUsage);
1207
0
    if (AppDesc != nullptr)
1208
0
    {
1209
0
        PutStringWithBlankLine(s, AppDesc);
1210
0
    }
1211
0
    PrintOptionHelp(optSets, s);
1212
0
}
1213
1214
void HelpOptions::PrintVersion(FILE * s) const
1215
0
{
1216
0
    fprintf(s, "%s ", AppName);
1217
0
    PutStringWithNewLine(s, (AppVersion != nullptr) ? AppVersion : "(unknown version)");
1218
0
}
1219
1220
bool HelpOptions::HandleOption(const char * progName, OptionSet * optSet, int id, const char * name, const char * arg)
1221
0
{
1222
0
    switch (id)
1223
0
    {
1224
0
    case 'h':
1225
0
        PrintLongUsage(gActiveOptionSets, stdout);
1226
0
        exit(EXIT_SUCCESS);
1227
0
        break;
1228
0
    case 'v':
1229
0
        PrintVersion(stdout);
1230
0
        exit(EXIT_SUCCESS);
1231
0
        break;
1232
0
    default:
1233
0
        PrintArgError("%s: INTERNAL ERROR: Unhandled option: %s\n", progName, name);
1234
0
        return false;
1235
0
    }
1236
1237
0
    return true;
1238
0
}
1239
1240
// ===== Private/Internal Methods =====
1241
1242
OptionSetBase::OptionSetBase()
1243
0
{
1244
0
    OptionHandler = CallHandleFunct;
1245
0
}
1246
1247
bool OptionSetBase::CallHandleFunct(const char * progName, OptionSet * optSet, int id, const char * name, const char * arg)
1248
0
{
1249
0
    return static_cast<OptionSetBase *>(optSet)->HandleOption(progName, optSet, id, name, arg);
1250
0
}
1251
1252
static char * MakeShortOptions(OptionSet ** optSets)
1253
0
{
1254
0
    size_t i = 0;
1255
1256
    // Count the number of options.
1257
0
    size_t totalOptions = CountAllOptions(optSets);
1258
1259
    // Allocate a block of memory big enough to hold the maximum possible size short option string.
1260
    // The buffer needs to be big enough to hold up to 3 characters per short option plus an initial
1261
    // ":" and a terminating null.
1262
0
    size_t arraySize = 2 + (totalOptions * 3);
1263
0
    char * shortOpts = static_cast<char *>(chip::Platform::MemoryAlloc(arraySize));
1264
0
    if (shortOpts == nullptr)
1265
0
        return nullptr;
1266
1267
    // Prefix the string with ':'.  This tells getopt() to signal missing option arguments distinct
1268
    // from unknown options.
1269
0
    shortOpts[i++] = ':';
1270
1271
    // For each option set...
1272
0
    for (; *optSets != nullptr; optSets++)
1273
0
    {
1274
        // For each option in the current option set...
1275
0
        for (OptionDef * optDef = (*optSets)->OptionDefs; optDef->Name != nullptr; optDef++)
1276
0
        {
1277
            // If the option id (val) is suitable as a short option character, add it to the short
1278
            // option string. Append ":" if the option requires an argument and "::" if the argument
1279
            // is optional.
1280
0
            if (IsShortOptionChar(optDef->Id))
1281
0
            {
1282
0
                shortOpts[i++] = static_cast<char>(optDef->Id);
1283
0
                if (optDef->ArgType != kNoArgument)
1284
0
                    shortOpts[i++] = ':';
1285
0
                if (optDef->ArgType == kArgumentOptional)
1286
0
                    shortOpts[i++] = ':';
1287
0
            }
1288
0
        }
1289
0
    }
1290
1291
    // Terminate the short options string.
1292
0
    shortOpts[i++] = 0;
1293
1294
0
    return shortOpts;
1295
0
}
1296
1297
static struct option * MakeLongOptions(OptionSet ** optSets)
1298
0
{
1299
0
    size_t totalOptions = CountAllOptions(optSets);
1300
1301
    // Allocate an array to hold the list of long options.
1302
0
    size_t arraySize         = (sizeof(struct option) * (totalOptions + 1));
1303
0
    struct option * longOpts = static_cast<struct option *>(chip::Platform::MemoryAlloc(arraySize));
1304
0
    if (longOpts == nullptr)
1305
0
        return nullptr;
1306
1307
    // For each option set...
1308
0
    size_t i = 0;
1309
0
    for (; *optSets != nullptr; optSets++)
1310
0
    {
1311
        // Copy the option definitions into the long options array.
1312
0
        for (OptionDef * optDef = (*optSets)->OptionDefs; optDef->Name != nullptr; optDef++)
1313
0
        {
1314
0
            longOpts[i].name    = optDef->Name;
1315
0
            longOpts[i].has_arg = static_cast<int>(optDef->ArgType);
1316
0
            longOpts[i].flag    = nullptr;
1317
0
            longOpts[i].val     = optDef->Id;
1318
0
            i++;
1319
0
        }
1320
0
    }
1321
1322
    // Terminate the long options array.
1323
0
    longOpts[i].name = nullptr;
1324
1325
0
    return longOpts;
1326
0
}
1327
1328
static int32_t SplitArgs(char * argStr, char **& argList, char * initialArg)
1329
0
{
1330
0
    enum
1331
0
    {
1332
0
        InitialArgListSize = 10
1333
0
    };
1334
0
    size_t argListSize = 0;
1335
0
    int32_t argCount   = 0;
1336
1337
    // Allocate an array to hold pointers to the arguments.
1338
0
    argList = static_cast<char **>(chip::Platform::MemoryAlloc(sizeof(char *) * InitialArgListSize));
1339
0
    if (argList == nullptr)
1340
0
        return -1;
1341
0
    argListSize = InitialArgListSize;
1342
1343
    // If an initial argument was supplied, make it the first argument in the array.
1344
0
    if (initialArg != nullptr)
1345
0
    {
1346
0
        argList[0] = initialArg;
1347
0
        argCount   = 1;
1348
0
    }
1349
1350
    // Parse arguments from the input string until it is exhausted.
1351
0
    while (true)
1352
0
    {
1353
0
        char * nextArg = argStr;
1354
1355
        // Get the argument in the input string.  Note that this modifies the string buffer.
1356
0
        if (!GetNextArg(argStr))
1357
0
            break;
1358
1359
        // Grow the arg list array if needed. Note that we reserve one slot at the end of the array
1360
        // for a NULL entry.
1361
0
        if (argListSize == static_cast<size_t>(argCount + 1))
1362
0
        {
1363
0
            argListSize *= 2;
1364
0
            argList = static_cast<char **>(chip::Platform::MemoryRealloc(argList, argListSize));
1365
0
            if (argList == nullptr)
1366
0
                return -1;
1367
0
        }
1368
1369
        // Append the argument.
1370
0
        argList[argCount++] = nextArg;
1371
0
    }
1372
1373
    // Set the last element in the array to NULL, but do not include this in the count of elements.
1374
    // This is mandated by the C standard and some versions of getopt_long() depend on it.
1375
0
    argList[argCount] = nullptr;
1376
1377
0
    return argCount;
1378
0
}
1379
1380
static bool GetNextArg(char *& parsePoint)
1381
0
{
1382
0
    char quoteChar = 0;
1383
0
    char * argEnd  = parsePoint;
1384
1385
    // Skip any leading whitespace.
1386
0
    while (*parsePoint != 0 && isspace(*parsePoint))
1387
0
        parsePoint++;
1388
1389
    // Return false if there are no further arguments.
1390
0
    if (*parsePoint == 0)
1391
0
        return false;
1392
1393
    // Iterate over characters until we find the end of an argument.
1394
    // As we iterate, we will accumulate the unquoted and unescaped
1395
    // argument characters in the input buffer starting at the initial
1396
    // parsePoint position.
1397
0
    while (*parsePoint != 0)
1398
0
    {
1399
        // If the current character is a backslash that is not at the end of
1400
        // the string, skip the backslash but copy the following character
1401
        // verbatim into the argument string.
1402
0
        if (*parsePoint == '\\' && *(parsePoint + 1) != 0)
1403
0
        {
1404
0
            parsePoint++;
1405
0
        }
1406
1407
        // Otherwise, if not within a quoted substring...
1408
0
        else if (quoteChar == 0)
1409
0
        {
1410
            // Whitespace marks the end of the argument.
1411
0
            if (isspace(*parsePoint))
1412
0
            {
1413
0
                parsePoint++;
1414
0
                break;
1415
0
            }
1416
1417
            // If the character is a quote character, enter quoted substring mode.
1418
0
            if (*parsePoint == '"' || *parsePoint == '\'')
1419
0
            {
1420
0
                quoteChar = *parsePoint++;
1421
0
                continue;
1422
0
            }
1423
0
        }
1424
1425
        // Otherwise, the parse point is within a quoted substring, so...
1426
0
        else
1427
0
        {
1428
            // A corresponding quote character marks the end of the quoted string.
1429
0
            if (*parsePoint == quoteChar)
1430
0
            {
1431
0
                quoteChar = 0;
1432
0
                parsePoint++;
1433
0
                continue;
1434
0
            }
1435
0
        }
1436
1437
        // Copy the current character to the end of the argument string.
1438
0
        *argEnd++ = *parsePoint++;
1439
0
    }
1440
1441
    // Terminate the argument string.
1442
0
    *argEnd = 0;
1443
1444
0
    return true;
1445
0
}
1446
1447
static size_t CountOptionSets(OptionSet ** optSets)
1448
0
{
1449
0
    size_t count = 0;
1450
0
    for (; *optSets != nullptr; optSets++)
1451
0
        count++;
1452
0
    return count;
1453
0
}
1454
1455
static size_t CountAllOptions(OptionSet ** optSets)
1456
0
{
1457
0
    size_t count = 0;
1458
0
    for (; *optSets != nullptr; optSets++)
1459
0
        for (OptionDef * optDef = (*optSets)->OptionDefs; optDef->Name != nullptr; optDef++)
1460
0
            count++;
1461
0
    return count;
1462
0
}
1463
1464
static void FindOptionByIndex(OptionSet ** optSets, int optIndex, OptionSet *& optSet, OptionDef *& optDef)
1465
0
{
1466
0
    for (optSet = *optSets; optSet != nullptr; optSet = *++optSets)
1467
0
        for (optDef = (*optSets)->OptionDefs; optDef->Name != nullptr; optDef++)
1468
0
            if (optIndex-- == 0)
1469
0
                return;
1470
0
    optSet = nullptr;
1471
0
    optDef = nullptr;
1472
0
}
1473
1474
static void FindOptionById(OptionSet ** optSets, int optId, OptionSet *& optSet, OptionDef *& optDef)
1475
0
{
1476
0
    for (optSet = *optSets; optSet != nullptr; optSet = *++optSets)
1477
0
        for (optDef = (*optSets)->OptionDefs; optDef->Name != nullptr; optDef++)
1478
0
            if (optDef->Id == optId)
1479
0
                return;
1480
0
    optSet = nullptr;
1481
0
    optDef = nullptr;
1482
0
}
1483
1484
static const char ** MakeUniqueHelpGroupNamesList(OptionSet * optSets[])
1485
0
{
1486
0
    size_t numOptSets = CountOptionSets(optSets);
1487
0
    size_t numGroups  = 0;
1488
1489
0
    const char ** groupNames = static_cast<const char **>(chip::Platform::MemoryAlloc(sizeof(const char *) * (numOptSets + 1)));
1490
0
    if (groupNames == nullptr)
1491
0
        return nullptr;
1492
1493
0
    for (size_t optSetIndex = 0; optSetIndex < numOptSets; optSetIndex++)
1494
0
    {
1495
0
        if (optSets[optSetIndex] != nullptr && optSets[optSetIndex]->OptionDefs[0].Name != nullptr)
1496
0
        {
1497
0
            for (size_t i = 0; i < numGroups; i++)
1498
0
                if (strcasecmp(groupNames[i], optSets[optSetIndex]->HelpGroupName) == 0)
1499
0
                    goto skipDup;
1500
0
            groupNames[numGroups++] = optSets[optSetIndex]->HelpGroupName;
1501
0
        skipDup:;
1502
0
        }
1503
0
    }
1504
1505
0
    groupNames[numGroups] = nullptr;
1506
1507
0
    return groupNames;
1508
0
}
1509
1510
static void PutStringWithNewLine(FILE * s, const char * str)
1511
0
{
1512
0
    size_t strLen = strlen(str);
1513
0
    fputs(str, s);
1514
0
    if (strLen == 0 || str[strLen - 1] != '\n')
1515
0
        fputs("\n", s);
1516
0
}
1517
1518
static void PutStringWithBlankLine(FILE * s, const char * str)
1519
0
{
1520
0
    size_t strLen = strlen(str);
1521
0
    fputs(str, s);
1522
0
    if (strLen < 1 || str[strLen - 1] != '\n')
1523
0
        fputs("\n", s);
1524
0
    if (strLen < 2 || str[strLen - 2] != '\n')
1525
0
        fputs("\n", s);
1526
0
}
1527
1528
static bool SanityCheckOptions(OptionSet * optSets[])
1529
0
{
1530
0
    bool res = true;
1531
1532
    // Verify OptionHandler pointer
1533
0
    for (OptionSet ** optSetP = optSets; *optSetP != nullptr; optSetP++)
1534
0
    {
1535
0
        if ((*optSetP)->OptionHandler == nullptr)
1536
0
        {
1537
0
            PrintArgError("INTERNAL ERROR: Null OptionHandler in OptionSet (%s)\n", (*optSetP)->HelpGroupName);
1538
0
            res = false;
1539
0
        }
1540
0
    }
1541
1542
    // Verify that no two option sets use the same short option character.
1543
    // (Re-use of the same short option character is allowed within a single option set
1544
    // to allow for aliasing of long options).
1545
0
    for (OptionSet ** optSetP = optSets; *optSetP != nullptr; optSetP++)
1546
0
        for (OptionDef * optionDef = (*optSetP)->OptionDefs; optionDef->Name != nullptr; optionDef++)
1547
0
            if (IsShortOptionChar(optionDef->Id))
1548
0
            {
1549
0
                for (OptionSet ** optSetP2 = optSets; *optSetP2 != nullptr; optSetP2++)
1550
0
                    if (optSetP2 != optSetP)
1551
0
                    {
1552
0
                        for (OptionDef * optionDef2 = (*optSetP2)->OptionDefs; optionDef2->Name != nullptr; optionDef2++)
1553
0
                            if (optionDef->Id == optionDef2->Id)
1554
0
                            {
1555
0
                                PrintArgError("INTERNAL ERROR: Multiple command line options configured to use "
1556
0
                                              "the same short option character (-%c): --%s, --%s\n",
1557
0
                                              optionDef->Id, optionDef->Name, optionDef2->Name);
1558
0
                                res = false;
1559
0
                            }
1560
0
                    }
1561
0
            }
1562
1563
0
    return res;
1564
0
}
1565
1566
} // namespace ArgParser
1567
} // namespace chip