LCOV - code coverage report
Current view: top level - lib/support - CHIPArgParser.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 112 420 26.7 %
Date: 2024-02-15 08:20:41 Functions: 9 46 19.6 %

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

Generated by: LCOV version 1.14