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

Generated by: LCOV version 2.0-1