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