Matter SDK Coverage Report
Current view: top level - lib/support - CHIPArgParser.cpp (source / functions) Coverage Total Hit
Test: SHA:b879ecb8e99e175eea0a293a888bda853da2b19c Lines: 26.7 % 420 112
Test Date: 2025-01-17 19:00:11 Functions: 19.6 % 46 9

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

Generated by: LCOV version 2.0-1