3 * Copyright (c) 2020 Project CHIP Authors
4 * Copyright (c) 2017 Nest Labs, Inc.
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
22 * Support functions for parsing command-line arguments.
26 #ifndef __STDC_LIMIT_MACROS
27 #define __STDC_LIMIT_MACROS
29 #ifndef __STDC_FORMAT_MACROS
30 #define __STDC_FORMAT_MACROS
33 #include "CHIPArgParser.hpp"
35 #if CHIP_CONFIG_ENABLE_ARG_PARSER
47 #include <support/SafeInt.h>
49 #include <support/CHIPMem.h>
50 #include <support/CHIPMemString.h>
53 * TODO: Revisit these if and when fabric ID and node ID support has
54 * been integrated into the stack.
56 #ifndef CHIP_ARG_PARSER_PARSE_FABRIC_ID
57 #define CHIP_ARG_PARSER_PARSE_FABRIC_ID 0
58 #endif // CHIP_ARG_PARSER_PARSE_FABRIC_ID
60 #ifndef CHIP_ARG_PARSER_PARSE_NODE_ID
61 #define CHIP_ARG_PARSER_PARSE_NODE_ID 0
62 #endif // CHIP_ARG_PARSER_PARSE_NODE_ID
69 static char * MakeShortOptions(OptionSet ** optSets);
70 static struct option * MakeLongOptions(OptionSet ** optSets);
71 static int32_t SplitArgs(char * argStr, char **& argList, char * initialArg = nullptr);
72 static bool GetNextArg(char *& parsePoint);
73 static size_t CountOptionSets(OptionSet * optSets[]);
74 static size_t CountAllOptions(OptionSet * optSets[]);
75 static void FindOptionByIndex(OptionSet ** optSets, int optIndex, OptionSet *& optSet, OptionDef *& optDef);
76 static void FindOptionById(OptionSet ** optSets, int optId, OptionSet *& optSet, OptionDef *& optDef);
77 static const char ** MakeUniqueHelpGroupNamesList(OptionSet * optSets[]);
78 static void PutStringWithNewLine(FILE * s, const char * str);
79 static void PutStringWithBlankLine(FILE * s, const char * str);
80 #if CHIP_CONFIG_ENABLE_ARG_PARSER_SANTIY_CHECK
81 static bool SanityCheckOptions(OptionSet * optSets[]);
82 static bool HelpTextContainsLongOption(const char * optName, const char * helpText);
83 static bool HelpTextContainsShortOption(char optChar, const char * helpText);
84 #endif // CHIP_CONFIG_ENABLE_ARG_PARSER_SANTIY_CHECK
86 static inline bool IsShortOptionChar(int ch)
93 * The list of OptionSets passed to the currently active ParseArgs() call.
96 * This value will be NULL when no call to ParseArgs() is in progress.
98 OptionSet ** gActiveOptionSets = nullptr;
102 * Pointer to function used to print errors that occur during argument parsing.
105 * Applications should call PrintArgError() to report errors in their option and
106 * non-option argument handling functions, rather than printing directly to
109 * Defaults to a pointer to the `DefaultPrintArgError()` function.
111 void (*PrintArgError)(const char * msg, ...) = DefaultPrintArgError;
114 * @fn bool ParseArgs(const char *progName, int argc, char *argv[], OptionSet *optSets[], NonOptionArgHandlerFunct nonOptArgHandler,
115 * bool ignoreUnknown)
118 * Parse a set of command line-style arguments, calling handling functions to process each
119 * option and non-option argument.
121 * @param[in] progName The name of the program or context in which the arguments are
122 * being parsed. This string will be used to prefix error
123 * messages and warnings.
124 * @param[in] argc The number of arguments to be parsed, plus 1.
125 * @param[in] argv An array of argument strings to be parsed. The array length must
126 * be 1 greater than the value specified for argc, and
127 * argv[argc] must be set to NULL. Argument parsing begins with the *second* array element (argv[1]); element 0 is ignored.
128 * @param[in] optSets A list of pointers to `OptionSet` structures that define the legal
129 * options. The supplied list must be terminated with a NULL.
130 * @param[in] nonOptArgHandler A pointer to a function that will be called once option parsing
131 * is complete with any remaining non-option arguments . The function
132 * is called regardless of whether any arguments remain. If a NULL
133 * is passed `ParseArgs()` will report an error if any non-option
134 * arguments are present.
135 * @param[in] ignoreUnknown If true, silently ignore any unrecognized options.
137 * @return `true` if all options and non-option arguments were parsed
138 * successfully; `false` if an option was unrecognized or if one of
139 * the handler functions failed (i.e. returned false).
143 * ParseArgs() takes a list of arguments (`argv`) and parses them according to a set of supplied
144 * option definitions. The function supports both long (--opt) and short (-o) options and implements
145 * the same option syntax as the GNU getopt_long(3) function.
147 * Option definitions are passed to ParseArgs() as an array of OptionSet structures (`optSets`).
148 * Each OptionSet contains an array of option definitions and a handler function. ParseArgs()
149 * processes option arguments in the given order, calling the respective handler function for
150 * each recognized option. Once all options have been parsed, a separate non-option handler
151 * function (`nonOptArgHandler`) is called once to process any remaining arguments.
156 * An OptionSet contains a set of option definitions along with a pointer to a handler function
157 * that will be called when one of the associated options is encountered. Option sets also
158 * contain help text describing the syntax and purpose of each option (see OPTION HELP below).
159 * Option sets are designed to allow the creation of re-usable collections of related options.
160 * This simplifies the effort needed to maintain multiple applications that accept similar options
161 * (e.g. test applications).
163 * There are two patterns for defining OptionSets--one can either initialize an instance of the
164 * OptionSet struct itself, e.g. as a static global, or subclass OptionSetBase and provide a
165 * constructor. The latter uses a pure virtual `HandleOption()` function to delegate option
166 * handling to the subclass.
168 * Lists of OptionSets are passed to the ParseArgs() function as a NULL-terminated array of pointers.
171 * static OptionSet gToolOptions =
173 * HandleOption, // handler function
174 * gToolOptionDefs, // array of option definitions
175 * "GENERAL OPTIONS", // help group
176 * gToolOptionHelp // option help text
179 * static OptionSet *gOptionSets[] =
188 * int main(int argc, char *argv[])
190 * if (!ParseArgs("test-app", argc, argv, gOptionSets))
197 * ## OPTION DEFINITIONS
199 * Options are defined using the `OptionDef` structure. Option definitions are organized as an array
200 * of OptionDef elements, where each element contains: the name of the option, a integer id that is
201 * used to identify the option, and whether the option expects/allows an argument. The end of the
202 * option array is signaled by a NULL Name field. E.g.:
206 * kOpt_Listen = 1000,
211 * static OptionDef gToolOptionDefs[] =
213 * // NAME REQUIRES/ALLOWS ARG? ID/SHORT OPTION CHAR
214 * // ============================================================
215 * { "listen", kNoArgument, kOpt_Listen },
216 * { "length", kArgumentRequired, kOpt_Length },
217 * { "count", kArgumentRequired, kOpt_Count },
218 * { "num", kArgumentRequired, kOpt_Count }, // alias for --count
219 * { "debug", kArgumentOptional, 'd' },
220 * { "help", kNoArgument, 'h' },
227 * Option ids identify options to the code that handles them (the OptionHandler function). Option ids
228 * are relative to the OptionSet in which they appear, and thus may be reused across different
229 * OptionSets (however see SHORT OPTIONS below). Common convention is to start numbering option ids
230 * at 1000, however any number > 128 can be used. Alias options can be created by using the same
231 * option id with different option names.
236 * Unlike getopt_long(3), ParseArgs() does not take a separate string specifying the list of short
237 * option characters. Rather, any option whose id value falls in the range of graphical ASCII
238 * characters will allow that character to be used as a short option.
240 * ParseArgs() requires that short option characters be unique across *all* OptionSets. Because of
241 * this, the use of short options is discouraged for any OptionSets that are shared across programs
242 * due to the significant chance for collisions. Short options characters may be reused within a
243 * single OptionSet to allow for the creation of alias long option names.
248 * Each OptionSet contains an `OptionHelp` string that describes the purpose and syntax of the
249 * associated options. These strings are used by the `PrintOptionHelp()` function to generate
250 * option usage information.
252 * By convention, option help strings consist of a syntax example following by a textual
253 * description of the option. If the option has a short version, or an alias name, it is given
254 * before primary long name. For consistency, syntax lines are indented with 2 spaces, while
255 * description lines are indented with 7 spaces. A single blank line follows each option
256 * description, including the last one.
260 * static const char *const gToolOptionHelp =
262 * " Listen and respond to requests sent from another node.\n"
264 * " --length <num>\n"
265 * " Send requests with the specified number of bytes in the payload.\n"
267 * " --num, --count <num>\n"
268 * " Send the specified number of requests and exit.\n"
270 * " -d, --debug [<level>]\n"
271 * " Set debug logging to the given level. (Default: 1)\n"
274 * " Print help information.\n"
278 * ## OPTION HELP GROUPS
280 * OptionSets contain a `HelpGroupName` string which is used to group options together in the
281 * help output. The `PrintOptionHelp()` function uses the HelpGroupName as a section title in
282 * the generated usage output. If multiple OptionSets have the same HelpGroupName,
283 * PrintOptionHelp() will print the option help for the different OptionSets together under
284 * a common section title.
287 bool ParseArgs(const char * progName, int argc, char * argv[], OptionSet * optSets[], NonOptionArgHandlerFunct nonOptArgHandler,
293 char * shortOpts = nullptr;
294 struct option * longOpts = nullptr;
295 OptionSet * curOptSet;
299 // The getopt() functions do not support recursion, so exit immediately with an
300 // error if called recursively.
301 if (gActiveOptionSets != nullptr)
303 PrintArgError("INTERNAL ERROR: ParseArgs() called recursively\n", progName);
307 // The C standard mandates that argv[argc] == NULL and certain versions of getopt() require this
308 // to function properly. So fail if this is not true.
309 if (argv[argc] != nullptr)
311 PrintArgError("INTERNAL ERROR: argv[argc] != NULL\n", progName);
315 // Set gActiveOptionSets to the current option set list.
316 gActiveOptionSets = optSets;
318 #if CHIP_CONFIG_ENABLE_ARG_PARSER_SANTIY_CHECK
319 if (!SanityCheckOptions(optSets))
323 // Generate a short options string in the format expected by getopt_long().
324 shortOpts = MakeShortOptions(optSets);
325 if (shortOpts == nullptr)
327 PrintArgError("%s: Memory allocation failure\n", progName);
331 // Generate a list of long option structures in the format expected by getopt_long().
332 longOpts = MakeLongOptions(optSets);
333 if (longOpts == nullptr)
335 PrintArgError("%s: Memory allocation failure\n", progName);
339 // Force getopt() to reset its internal state.
342 // Process any option arguments...
348 // Attempt to match the current option argument (argv[optind]) against the defined long and short options.
351 id = getopt_long(argc, argv, shortOpts, longOpts, &optIndex);
353 // Stop if there are no more options.
357 // If the current option is unrecognized, fail with an error message unless ignoreUnknown == true.
363 PrintArgError("%s: Unknown option: -%c\n", progName, optopt);
365 PrintArgError("%s: Unknown option: %s\n", progName, argv[optind - 1]);
369 // If the option was recognized, but it is lacking an argument, fail with
373 // NOTE: with the way getopt_long() works, it is impossible to tell whether the option that
374 // was missing an argument was a long option or a short option.
375 PrintArgError("%s: Missing argument for %s option\n", progName, argv[optind - 1]);
379 // If a long option was matched...
383 // Locate the option set and definition using the index value returned by getopt_long().
384 FindOptionByIndex(optSets, optIndex, curOptSet, curOpt);
386 // Form a string containing the name of the option as it appears on the command line.
387 snprintf(optName, sizeof(optName), "--%s", curOpt->Name);
390 // Otherwise a short option was matched...
393 // Locate the option set and definition using the option id.
394 FindOptionById(optSets, id, curOptSet, curOpt);
396 // Form a string containing the name of the short option as it would appears on the
397 // command line if given by itself.
398 snprintf(optName, sizeof(optName), "-%c", id);
401 // Prevent handlers from inadvertently using the getopt global optarg.
405 // Call the option handler function defined for the matching option set.
406 // Exit immediately if the option handler failed.
407 handlerRes = curOptSet->OptionHandler(progName, curOptSet, id, optName, optArg);
412 // If supplied, call the non-option argument handler with the remaining arguments (if any).
413 if (nonOptArgHandler != nullptr)
415 if (!nonOptArgHandler(progName, argc - optind, argv + optind))
419 // otherwise, if there are additional arguments, fail with an error.
420 else if (optind < argc)
422 PrintArgError("%s: Unexpected argument: %s\n", progName, argv[optind]);
430 if (shortOpts != nullptr)
431 chip::Platform::MemoryFree(shortOpts);
432 if (longOpts != nullptr)
433 chip::Platform::MemoryFree(longOpts);
435 gActiveOptionSets = nullptr;
440 bool ParseArgs(const char * progName, int argc, char * argv[], OptionSet * optSets[], NonOptionArgHandlerFunct nonOptArgHandler)
442 return ParseArgs(progName, argc, argv, optSets, nonOptArgHandler, false);
445 bool ParseArgs(const char * progName, int argc, char * argv[], OptionSet * optSets[])
447 return ParseArgs(progName, argc, argv, optSets, nullptr, false);
452 * Parse a set of arguments from a given string.
454 * @param[in] progName The name of the program or context in which the arguments are
455 * being parsed. This string will be used to prefix error
456 * messages and warnings.
457 * @param[in] argStr A string containing options and arguments to be parsed.
458 * @param[in] optSets A list of pointers to `OptionSet` structures that define the legal
459 * options. The supplied list must be terminated with a NULL.
460 * @param[in] nonOptArgHandler A pointer to a function that will be called once option parsing
461 * is complete with any remaining non-option arguments . The function
462 * is called regardless of whether any arguments remain. If a NULL
463 * is passed `ParseArgs()` will report an error if any non-option
464 * arguments are present.
465 * @param[in] ignoreUnknown If true, silently ignore any unrecognized options.
467 * @return `true` if all options and non-option arguments were parsed
468 * successfully; `false` if an option was unrecognized, if one of
469 * the handler functions failed (i.e. returned false) or if an
470 * internal error occurred.
473 * ParseArgsFromString() splits a given string (`argStr`) into a set of arguments and parses the
474 * arguments using the ParseArgs() function.
476 * The syntax of the input strings is similar to unix shell command syntax, but with a simplified
477 * quoting scheme. Specifically:
479 * - Arguments are delimited by whitespace, unless the whitespace is quoted or escaped.
481 * - A backslash escapes the following character, causing it to be treated as a normal character.
482 * The backslash itself is stripped.
484 * - Single or double quotes begin/end quoted substrings. Within a substring, the only special
485 * characters are backslash, which escapes the next character, and the corresponding end quote.
486 * The begin/end quote characters are stripped.
490 * --listen --count 10 --sw-version '1.0 (DEVELOPMENT)' "--hostname=nest.com"
493 bool ParseArgsFromString(const char * progName, const char * argStr, OptionSet * optSets[],
494 NonOptionArgHandlerFunct nonOptArgHandler, bool ignoreUnknown)
496 char ** argv = nullptr;
500 chip::Platform::ScopedMemoryString argStrCopy(argStr, strlen(argStr));
503 PrintArgError("%s: Memory allocation failure\n", progName);
507 argc = SplitArgs(argStrCopy.Get(), argv, const_cast<char *>(progName));
510 PrintArgError("%s: Memory allocation failure\n", progName);
514 res = ParseArgs(progName, argc, argv, optSets, nonOptArgHandler, ignoreUnknown);
516 chip::Platform::MemoryFree(argv);
521 bool ParseArgsFromString(const char * progName, const char * argStr, OptionSet * optSets[],
522 NonOptionArgHandlerFunct nonOptArgHandler)
524 return ParseArgsFromString(progName, argStr, optSets, nonOptArgHandler, false);
527 bool ParseArgsFromString(const char * progName, const char * argStr, OptionSet * optSets[])
529 return ParseArgsFromString(progName, argStr, optSets, nullptr, false);
534 * Parse a set of arguments from a named environment variable
536 * @param[in] progName The name of the program or context in which the arguments are
537 * being parsed. This string will be used to prefix error
538 * messages and warnings.
539 * @param[in] varName The name of the environment variable.
540 * @param[in] optSets A list of pointers to `OptionSet` structures that define the legal
541 * options. The supplied list must be terminated with a NULL.
542 * @param[in] nonOptArgHandler A pointer to a function that will be called once option parsing
543 * is complete with any remaining non-option arguments . The function
544 * is called regardless of whether any arguments remain. If a NULL
545 * is passed `ParseArgs()` will report an error if any non-option
546 * arguments are present.
547 * @param[in] ignoreUnknown If true, silently ignore any unrecognized options.
549 * @return `true` if all options and non-option arguments were parsed
550 * successfully, or if the specified environment variable is not set;
551 * `false` if an option was unrecognized, if one of the handler
552 * functions failed (i.e. returned false) or if an internal error
556 * ParseArgsFromEnvVar() reads a named environment variable and passes the value to `ParseArgsFromString()`
557 * for parsing. If the environment variable is not set, the function does nothing.
560 bool ParseArgsFromEnvVar(const char * progName, const char * varName, OptionSet * optSets[],
561 NonOptionArgHandlerFunct nonOptArgHandler, bool ignoreUnknown)
563 const char * argStr = getenv(varName);
564 if (argStr == nullptr)
566 return ParseArgsFromString(progName, argStr, optSets, nonOptArgHandler, ignoreUnknown);
569 bool ParseArgsFromEnvVar(const char * progName, const char * varName, OptionSet * optSets[])
571 return ParseArgsFromEnvVar(progName, varName, optSets, nullptr, false);
574 bool ParseArgsFromEnvVar(const char * progName, const char * varName, OptionSet * optSets[],
575 NonOptionArgHandlerFunct nonOptArgHandler)
577 return ParseArgsFromEnvVar(progName, varName, optSets, nonOptArgHandler, false);
582 * Print the help text for a specified list of options to a stream.
584 * @param[in] optSets A list of pointers to `OptionSet` structures that contain the
585 * help text to print.
586 * @param[in] s The FILE stream to which the help text should be printed.
589 void PrintOptionHelp(OptionSet * optSets[], FILE * s)
591 // Get a list of the unique help group names for the given option sets.
592 const char ** helpGroupNames = MakeUniqueHelpGroupNamesList(optSets);
593 if (helpGroupNames == nullptr)
595 PrintArgError("Memory allocation failure\n");
599 // For each help group...
600 for (size_t nameIndex = 0; helpGroupNames[nameIndex] != nullptr; nameIndex++)
602 // Print the group name.
603 PutStringWithBlankLine(s, helpGroupNames[nameIndex]);
605 // Print the option help text for all options that have the same group name.
606 for (size_t optSetIndex = 0; optSets[optSetIndex] != nullptr; optSetIndex++)
607 if (strcasecmp(helpGroupNames[nameIndex], optSets[optSetIndex]->HelpGroupName) == 0)
609 PutStringWithBlankLine(s, optSets[optSetIndex]->OptionHelp);
613 chip::Platform::MemoryFree(helpGroupNames);
618 * Print an error message associated with argument parsing.
620 * @param[in] msg The message to be printed.
623 * Default function used to print error messages that arise due to the parsing
626 * Applications should call through the PrintArgError function pointer, rather
627 * than calling this function directly.
629 void DefaultPrintArgError(const char * msg, ...)
634 vfprintf(stderr, msg, ap);
639 * Parse a string as a boolean value.
641 * This function accepts the following input values (case-insensitive):
642 * "true", "yes", "t", "y", "1", "false", "no", "f", "n", "0".
644 * @param[in] str A pointer to a NULL-terminated C string representing
645 * the value to parse.
646 * @param[out] output A reference to storage for a bool to which the parsed
647 * value will be stored on success.
649 * @return true on success; otherwise, false on failure.
651 bool ParseBoolean(const char * str, bool & output)
653 if (strcasecmp(str, "true") == 0 || strcasecmp(str, "yes") == 0 ||
654 ((str[0] == '1' || str[0] == 't' || str[0] == 'T' || str[0] == 'y' || str[0] == 'Y') && str[1] == 0))
660 if (strcasecmp(str, "false") == 0 || strcasecmp(str, "no") == 0 ||
661 ((str[0] == '0' || str[0] == 'f' || str[0] == 'F' || str[0] == 'n' || str[0] == 'N') && str[1] == 0))
671 * Parse and attempt to convert a string to a 64-bit unsigned integer,
672 * applying the appropriate interpretation based on the base parameter.
674 * @param[in] str A pointer to a NULL-terminated C string representing
675 * the integer to parse.
676 * @param[out] output A reference to storage for a 64-bit unsigned integer
677 * to which the parsed value will be stored on success.
678 * @param[in] base The base according to which the string should be
679 * interpreted and parsed. If 0 or 16, the string may
680 * be hexadecimal and prefixed with "0x". Otherwise, a 0
681 * is implied as 10 unless a leading 0 is encountered in
682 * which 8 is implied.
684 * @return true on success; otherwise, false on failure.
686 bool ParseInt(const char * str, uint64_t & output, int base)
691 output = strtoull(str, &parseEnd, base);
693 return parseEnd > str && *parseEnd == 0 && (output != ULLONG_MAX || errno == 0);
697 * Parse and attempt to convert a string to a 32-bit unsigned integer,
698 * applying the appropriate interpretation based on the base parameter.
700 * @param[in] str A pointer to a NULL-terminated C string representing
701 * the integer to parse.
702 * @param[out] output A reference to storage for a 32-bit unsigned integer
703 * to which the parsed value will be stored on success.
704 * @param[in] base The base according to which the string should be
705 * interpreted and parsed. If 0 or 16, the string may
706 * be hexadecimal and prefixed with "0x". Otherwise, a 0
707 * is implied as 10 unless a leading 0 is encountered in
708 * which 8 is implied.
710 * @return true on success; otherwise, false on failure.
712 bool ParseInt(const char * str, uint32_t & output, int base)
718 v = strtoul(str, &parseEnd, base);
719 if (!CanCastTo<uint32_t>(v))
723 output = static_cast<uint32_t>(v);
725 return parseEnd > str && *parseEnd == 0 && (v != ULONG_MAX || errno == 0);
729 * Parse and attempt to convert a string to a 32-bit signed integer,
730 * applying the appropriate interpretation based on the base parameter.
732 * @param[in] str A pointer to a NULL-terminated C string representing
733 * the integer to parse.
734 * @param[out] output A reference to storage for a 32-bit signed integer
735 * to which the parsed value will be stored on success.
736 * @param[in] base The base according to which the string should be
737 * interpreted and parsed. If 0 or 16, the string may
738 * be hexadecimal and prefixed with "0x". Otherwise, a 0
739 * is implied as 10 unless a leading 0 is encountered in
740 * which 8 is implied.
742 * @return true on success; otherwise, false on failure.
744 bool ParseInt(const char * str, int32_t & output, int base)
750 v = strtol(str, &parseEnd, base);
751 if (!CanCastTo<int32_t>(v))
755 output = static_cast<int32_t>(v);
757 return parseEnd > str && *parseEnd == 0 && ((v != LONG_MIN && v != LONG_MAX) || errno == 0);
761 * Parse and attempt to convert a string interpreted as a decimal
762 * value to a 64-bit unsigned integer, applying the appropriate
763 * interpretation based on the base parameter.
765 * @param[in] str A pointer to a NULL-terminated C string representing
766 * the integer to parse.
767 * @param[out] output A reference to storage for a 64-bit unsigned integer
768 * to which the parsed value will be stored on success.
770 * @return true on success; otherwise, false on failure.
772 bool ParseInt(const char * str, uint64_t & output)
776 return ParseInt(str, output, base);
780 * Parse and attempt to convert a string interpreted as a decimal
781 * value to a 32-bit unsigned integer, applying the appropriate
782 * interpretation based on the base parameter.
784 * @param[in] str A pointer to a NULL-terminated C string representing
785 * the integer to parse.
786 * @param[out] output A reference to storage for a 32-bit unsigned integer
787 * to which the parsed value will be stored on success.
789 * @return true on success; otherwise, false on failure.
791 bool ParseInt(const char * str, uint32_t & output)
795 return ParseInt(str, output, base);
799 * Parse and attempt to convert a string interpreted as a decimal
800 * value to a 32-bit signed integer, applying the appropriate
801 * interpretation based on the base parameter.
803 * @param[in] str A pointer to a NULL-terminated C string representing
804 * the integer to parse.
805 * @param[out] output A reference to storage for a 32-bit signed integer
806 * to which the parsed value will be stored on success.
808 * @return true on success; otherwise, false on failure.
810 bool ParseInt(const char * str, int32_t & output)
814 return ParseInt(str, output, base);
818 * Parse and attempt to convert a string interpreted as a decimal
819 * value to a 16-bit unsigned integer, applying the appropriate
820 * interpretation based on the base parameter.
822 * @param[in] str A pointer to a NULL-terminated C string representing
823 * the integer to parse.
824 * @param[out] output A reference to storage for a 16-bit unsigned integer
825 * to which the parsed value will be stored on success.
827 * @return true on success; otherwise, false on failure.
829 bool ParseInt(const char * str, uint16_t & output)
832 uint32_t output32 = 0;
834 if ((ParseInt(str, output32, base)) && (output32 <= USHRT_MAX))
836 output = ((1 << 16) - 1) & output32;
844 * Parse and attempt to convert a string interpreted as a decimal
845 * value to a 16-bit signed integer, applying the appropriate
846 * interpretation based on the base parameter.
848 * @param[in] str A pointer to a NULL-terminated C string representing
849 * the integer to parse.
850 * @param[out] output A reference to storage for a 16-bit signed integer
851 * to which the parsed value will be stored on success.
853 * @return true on success; otherwise, false on failure.
855 bool ParseInt(const char * str, int16_t & output)
858 int32_t output32 = 0;
860 if ((ParseInt(str, output32, base)) && (output32 <= SHRT_MAX))
862 output = static_cast<int16_t>(UINT16_MAX & output32);
870 * Parse and attempt to convert a string interpreted as a decimal
871 * value to a 8-bit unsigned integer, applying the appropriate
872 * interpretation based on the base parameter.
874 * @param[in] str A pointer to a NULL-terminated C string representing
875 * the integer to parse.
876 * @param[out] output A reference to storage for a 8-bit unsigned integer
877 * to which the parsed value will be stored on success.
879 * @return true on success; otherwise, false on failure.
881 bool ParseInt(const char * str, uint8_t & output)
884 uint32_t output32 = 0;
886 if ((ParseInt(str, output32, base)) && (output32 <= UCHAR_MAX))
888 output = ((1 << 8) - 1) & output32;
895 #if CHIP_ARG_PARSER_PARSE_NODE_ID
897 * Parse a CHIP node id in text form.
899 * @param[in] str A pointer to a NULL-terminated C string containing
900 * the node id to parse.
901 * @param[out] output A reference to an uint64_t lvalue in which the parsed
902 * value will be stored on success.
904 * @return true if the value was successfully parsed; false if not.
907 * The ParseNodeId() function accepts either a 64-bit node id given in hex
908 * format (with or without a leading '0x'), or the words 'any' or 'all' which
909 * are interpreted as meaning the Any node id (0xFFFFFFFFFFFFFFFF).
911 bool ParseNodeId(const char * str, uint64_t & nodeId)
915 if (strcasecmp(str, "any") == 0 || strcasecmp(str, "all") == 0)
922 nodeId = strtoull(str, &parseEnd, 16);
923 return parseEnd > str && *parseEnd == 0 && (nodeId != ULLONG_MAX || errno == 0);
925 #endif // CHIP_ARG_PARSER_PARSE_NODE_ID
927 #if CHIP_ARG_PARSER_PARSE_FABRIC_ID
929 * Parse a CHIP fabric id in text form.
931 * @param[in] str A pointer to a NULL-terminated C string containing
932 * the fabric id to parse.
933 * @param[out] output A reference to an uint64_t lvalue in which the
934 * parsed value will be stored on success.
935 * @param[in] allowReserved If true, allow the parsing of fabric ids in the
938 * @return true if the value was successfully parsed; false if not.
941 * The ParseFabricId() function accepts a 64-bit fabric id given in hex format,
942 * with or without a leading '0x'.
944 bool ParseFabricId(const char * str, uint64_t & fabricId, bool allowReserved)
949 fabricId = strtoull(str, &parseEnd, 16);
950 return parseEnd > str && *parseEnd == 0 && (fabricId != ULLONG_MAX || errno == 0) &&
951 (allowReserved || fabricId < kReservedFabricIdStart);
953 #endif // CHIP_ARG_PARSER_PARSE_FABRIC_ID
956 * Parse and attempt to convert a string to a 16-bit unsigned subnet
957 * ID, interpretting the string as hexadecimal.
959 * @param[in] str A pointer to a NULL-terminated C string
960 * representing the subnet ID, formatted as a
961 * hexadecimal, to parse.
962 * @param[in,out] subnetId A reference to storage for a 16-bit unsigned
963 * integer to which the parsed subnet ID value
964 * will be stored on success.
966 * @return true on success; otherwise, false on failure.
968 bool ParseSubnetId(const char * str, uint16_t & subnetId)
974 // Reset errno per the strtoul manual page.
978 // Attempt to parse the subnet ID as a hexadecimal number.
980 temp = strtoul(str, &parseEnd, 16);
982 // Determine if the parse and conversion were valid.
984 valid = (parseEnd > str && // Parsed some valid hexadecimal digits
985 *parseEnd == 0 && // Encountered no invalid hexadecimal digits
986 (temp != ULONG_MAX || errno == 0) && // No overflow (ERANGE) or invalid base (EINVAL) errors
987 temp <= USHRT_MAX); // Parsed value is valid for the domain (subnet ID)
991 subnetId = static_cast<uint16_t>(temp);
998 * Parse a string of bytes given in hex form.
1000 * @param[in] hexStr A pointer to the string to parse.
1001 * @param[in] strLen The number of characters in hexStr to parse.
1002 * @param[in] outBuf A pointer to a buffer into which the parse bytes will
1004 * @param[in] outBufSize The size of the buffer pointed at by `outBuf`.
1005 * @param[out] outDataLen A reference to an integer that will receive the total
1006 * number of bytes parsed. In the event outBuf is not
1007 * big enough to hold the given number of bytes, `outDataLen`
1008 * will be set to UINT32_MAX.
1010 * @return true if the value was successfully parsed; false if the input data is malformed,
1011 * or if `outBuf` is too small.
1014 * ParseHexString() expects the input to be in the form of pairs of hex digits (upper or lower case).
1015 * Hex pairs can optionally be separated by any of the following characters: colon, semicolon, comma, period or dash.
1016 * Additionally, whitespace characters anywhere in the input string are ignored.
1018 bool ParseHexString(const char * hexStr, uint32_t strLen, uint8_t * outBuf, uint32_t outBufSize, uint32_t & outDataLen)
1020 bool isFirstNibble = true;
1021 uint8_t firstNibbleVal = 0;
1022 const char * p = hexStr;
1023 uint32_t dataLen = 0;
1027 for (; strLen > 0; p++, strLen--)
1034 if (c >= '0' && c <= '9')
1035 nibbleVal = static_cast<uint8_t>(c - '0');
1036 else if (c >= 'a' && c <= 'f')
1037 nibbleVal = static_cast<uint8_t>(10 + (c - 'a'));
1038 else if (c >= 'A' && c <= 'F')
1039 nibbleVal = static_cast<uint8_t>(10 + (c - 'A'));
1040 else if (isspace(c))
1042 else if (isFirstNibble && (c == ':' || c == ';' || c == ',' || c == '.' || c == '-'))
1046 outDataLen = static_cast<decltype(strLen)>(p - hexStr);
1052 firstNibbleVal = nibbleVal;
1053 isFirstNibble = false;
1057 if (outBufSize == 0)
1059 outDataLen = UINT32_MAX;
1063 *outBuf = static_cast<uint8_t>(firstNibbleVal << 4 | nibbleVal);
1069 isFirstNibble = true;
1075 outDataLen = static_cast<decltype(strLen)>(p - hexStr);
1079 outDataLen = dataLen;
1084 // ===== HelpOptions Methods =====
1086 HelpOptions::HelpOptions(const char * appName, const char * appUsage, const char * appVersion) :
1087 HelpOptions(appName, appUsage, appVersion, nullptr)
1090 HelpOptions::HelpOptions(const char * appName, const char * appUsage, const char * appVersion, const char * appDesc)
1093 static OptionDef optionDefs[] =
1095 { "help", kNoArgument, 'h' },
1096 { "version", kNoArgument, 'v' },
1100 OptionDefs = optionDefs;
1102 HelpGroupName = "HELP OPTIONS";
1104 OptionHelp = " -h, --help\n"
1105 " Print this output and then exit.\n"
1108 " Print the version and then exit.\n"
1112 AppUsage = appUsage;
1113 AppVersion = appVersion;
1118 * Print a short description of the command's usage followed by instructions on how to get more help.
1120 void HelpOptions::PrintBriefUsage(FILE * s)
1122 PutStringWithNewLine(s, AppUsage);
1123 fprintf(s, "Try `%s --help' for more information.\n", AppName);
1127 * Print the full usage information, including information on all available options.
1129 void HelpOptions::PrintLongUsage(OptionSet ** optSets, FILE * s)
1131 PutStringWithBlankLine(s, AppUsage);
1132 if (AppDesc != nullptr)
1134 PutStringWithBlankLine(s, AppDesc);
1136 PrintOptionHelp(optSets, s);
1139 void HelpOptions::PrintVersion(FILE * s)
1141 fprintf(s, "%s ", AppName);
1142 PutStringWithNewLine(s, (AppVersion != nullptr) ? AppVersion : "(unknown version)");
1145 bool HelpOptions::HandleOption(const char * progName, OptionSet * optSet, int id, const char * name, const char * arg)
1150 PrintLongUsage(gActiveOptionSets, stdout);
1154 PrintVersion(stdout);
1158 PrintArgError("%s: INTERNAL ERROR: Unhandled option: %s\n", progName, name);
1165 // ===== Private/Internal Methods =====
1167 OptionSetBase::OptionSetBase()
1169 OptionHandler = CallHandleFunct;
1172 bool OptionSetBase::CallHandleFunct(const char * progName, OptionSet * optSet, int id, const char * name, const char * arg)
1174 return static_cast<OptionSetBase *>(optSet)->HandleOption(progName, optSet, id, name, arg);
1177 static char * MakeShortOptions(OptionSet ** optSets)
1181 // Count the number of options.
1182 size_t totalOptions = CountAllOptions(optSets);
1184 // Allocate a block of memory big enough to hold the maximum possible size short option string.
1185 // The buffer needs to be big enough to hold up to 3 characters per short option plus an initial
1186 // ":" and a terminating null.
1187 size_t arraySize = 2 + (totalOptions * 3);
1188 char * shortOpts = static_cast<char *>(chip::Platform::MemoryAlloc(arraySize));
1189 if (shortOpts == nullptr)
1192 // Prefix the string with ':'. This tells getopt() to signal missing option arguments distinct
1193 // from unknown options.
1194 shortOpts[i++] = ':';
1196 // For each option set...
1197 for (; *optSets != nullptr; optSets++)
1199 // For each option in the current option set...
1200 for (OptionDef * optDef = (*optSets)->OptionDefs; optDef->Name != nullptr; optDef++)
1202 // If the option id (val) is suitable as a short option character, add it to the short
1203 // option string. Append ":" if the option requires an argument and "::" if the argument
1205 if (IsShortOptionChar(optDef->Id))
1207 shortOpts[i++] = static_cast<char>(optDef->Id);
1208 if (optDef->ArgType != kNoArgument)
1209 shortOpts[i++] = ':';
1210 if (optDef->ArgType == kArgumentOptional)
1211 shortOpts[i++] = ':';
1216 // Terminate the short options string.
1222 static struct option * MakeLongOptions(OptionSet ** optSets)
1224 size_t totalOptions = CountAllOptions(optSets);
1226 // Allocate an array to hold the list of long options.
1227 size_t arraySize = (sizeof(struct option) * (totalOptions + 1));
1228 struct option * longOpts = static_cast<struct option *>(chip::Platform::MemoryAlloc(arraySize));
1229 if (longOpts == nullptr)
1232 // For each option set...
1234 for (; *optSets != nullptr; optSets++)
1236 // Copy the option definitions into the long options array.
1237 for (OptionDef * optDef = (*optSets)->OptionDefs; optDef->Name != nullptr; optDef++)
1239 longOpts[i].name = optDef->Name;
1240 longOpts[i].has_arg = static_cast<int>(optDef->ArgType);
1241 longOpts[i].flag = nullptr;
1242 longOpts[i].val = optDef->Id;
1247 // Terminate the long options array.
1248 longOpts[i].name = nullptr;
1253 static int32_t SplitArgs(char * argStr, char **& argList, char * initialArg)
1257 InitialArgListSize = 10
1259 size_t argListSize = 0;
1260 int32_t argCount = 0;
1262 // Allocate an array to hold pointers to the arguments.
1263 argList = static_cast<char **>(chip::Platform::MemoryAlloc(sizeof(char *) * InitialArgListSize));
1264 if (argList == nullptr)
1266 argListSize = InitialArgListSize;
1268 // If an initial argument was supplied, make it the first argument in the array.
1269 if (initialArg != nullptr)
1271 argList[0] = initialArg;
1275 // Parse arguments from the input string until it is exhausted.
1278 char * nextArg = argStr;
1280 // Get the argument in the input string. Note that this modifies the string buffer.
1281 if (!GetNextArg(argStr))
1284 // Grow the arg list array if needed. Note that we reserve one slot at the end of the array
1285 // for a NULL entry.
1286 if (argListSize == static_cast<size_t>(argCount + 1))
1289 argList = static_cast<char **>(chip::Platform::MemoryRealloc(argList, argListSize));
1290 if (argList == nullptr)
1294 // Append the argument.
1295 argList[argCount++] = nextArg;
1298 // Set the last element in the array to NULL, but do not include this in the count of elements.
1299 // This is mandated by the C standard and some versions of getopt_long() depend on it.
1300 argList[argCount] = nullptr;
1305 static bool GetNextArg(char *& parsePoint)
1308 char * argEnd = parsePoint;
1310 // Skip any leading whitespace.
1311 while (*parsePoint != 0 && isspace(*parsePoint))
1314 // Return false if there are no further arguments.
1315 if (*parsePoint == 0)
1318 // Iterate over characters until we find the end of an argument.
1319 // As we iterate, we will accumulate the unquoted and unescaped
1320 // argument characters in the input buffer starting at the initial
1321 // parsePoint position.
1322 while (*parsePoint != 0)
1324 // If the current character is a backslash that is not at the end of
1325 // the string, skip the backslash but copy the following character
1326 // verbatim into the argument string.
1327 if (*parsePoint == '\\' && *(parsePoint + 1) != 0)
1332 // Otherwise, if not within a quoted substring...
1333 else if (quoteChar == 0)
1335 // Whitespace marks the end of the argument.
1336 if (isspace(*parsePoint))
1342 // If the character is a quote character, enter quoted substring mode.
1343 if (*parsePoint == '"' || *parsePoint == '\'')
1345 quoteChar = *parsePoint++;
1350 // Otherwise, the parse point is within a quoted substring, so...
1353 // A corresponding quote character marks the end of the quoted string.
1354 if (*parsePoint == quoteChar)
1362 // Copy the current character to the end of the argument string.
1363 *argEnd++ = *parsePoint++;
1366 // Terminate the argument string.
1372 static size_t CountOptionSets(OptionSet ** optSets)
1375 for (; *optSets != nullptr; optSets++)
1380 static size_t CountAllOptions(OptionSet ** optSets)
1383 for (; *optSets != nullptr; optSets++)
1384 for (OptionDef * optDef = (*optSets)->OptionDefs; optDef->Name != nullptr; optDef++)
1389 static void FindOptionByIndex(OptionSet ** optSets, int optIndex, OptionSet *& optSet, OptionDef *& optDef)
1391 for (optSet = *optSets; optSet != nullptr; optSet = *++optSets)
1392 for (optDef = (*optSets)->OptionDefs; optDef->Name != nullptr; optDef++)
1393 if (optIndex-- == 0)
1399 static void FindOptionById(OptionSet ** optSets, int optId, OptionSet *& optSet, OptionDef *& optDef)
1401 for (optSet = *optSets; optSet != nullptr; optSet = *++optSets)
1402 for (optDef = (*optSets)->OptionDefs; optDef->Name != nullptr; optDef++)
1403 if (optDef->Id == optId)
1409 static const char ** MakeUniqueHelpGroupNamesList(OptionSet * optSets[])
1411 size_t numOptSets = CountOptionSets(optSets);
1412 size_t numGroups = 0;
1414 const char ** groupNames = static_cast<const char **>(chip::Platform::MemoryAlloc(sizeof(const char *) * (numOptSets + 1)));
1415 if (groupNames == nullptr)
1418 for (size_t optSetIndex = 0; optSetIndex < numOptSets; optSetIndex++)
1420 if (optSets[optSetIndex] != nullptr && optSets[optSetIndex]->OptionDefs[0].Name != nullptr)
1422 for (size_t i = 0; i < numGroups; i++)
1423 if (strcasecmp(groupNames[i], optSets[optSetIndex]->HelpGroupName) == 0)
1425 groupNames[numGroups++] = optSets[optSetIndex]->HelpGroupName;
1430 groupNames[numGroups] = nullptr;
1435 static void PutStringWithNewLine(FILE * s, const char * str)
1437 size_t strLen = strlen(str);
1439 if (strLen == 0 || str[strLen - 1] != '\n')
1443 static void PutStringWithBlankLine(FILE * s, const char * str)
1445 size_t strLen = strlen(str);
1447 if (strLen < 1 || str[strLen - 1] != '\n')
1449 if (strLen < 2 || str[strLen - 2] != '\n')
1453 #if CHIP_CONFIG_ENABLE_ARG_PARSER_SANTIY_CHECK
1455 static bool SanityCheckOptions(OptionSet * optSets[])
1459 // Verify OptionHandler pointer
1460 for (OptionSet ** optSetP = optSets; *optSetP != nullptr; optSetP++)
1462 if ((*optSetP)->OptionHandler == nullptr)
1464 PrintArgError("INTERNAL ERROR: Null OptionHandler in OptionSet (%s)\n", (*optSetP)->HelpGroupName);
1469 // Verify that no two option sets use the same short option character.
1470 // (Re-use of the same short option character is allowed within a single option set
1471 // to allow for aliasing of long options).
1472 for (OptionSet ** optSetP = optSets; *optSetP != nullptr; optSetP++)
1473 for (OptionDef * optionDef = (*optSetP)->OptionDefs; optionDef->Name != nullptr; optionDef++)
1474 if (IsShortOptionChar(optionDef->Id))
1476 for (OptionSet ** optSetP2 = optSets; *optSetP2 != nullptr; optSetP2++)
1477 if (optSetP2 != optSetP)
1479 for (OptionDef * optionDef2 = (*optSetP2)->OptionDefs; optionDef2->Name != nullptr; optionDef2++)
1480 if (optionDef->Id == optionDef2->Id)
1482 PrintArgError("INTERNAL ERROR: Multiple command line options configured to use "
1483 "the same short option character (-%c): --%s, --%s\n",
1484 optionDef->Id, optionDef->Name, optionDef2->Name);
1490 // Fail if the option help texts do not contain a description for each option, including both
1491 // the option's long and short forms.
1492 for (OptionSet ** optSetP = optSets; *optSetP != nullptr; optSetP++)
1493 for (OptionDef * optionDef = (*optSetP)->OptionDefs; optionDef->Name != nullptr; optionDef++)
1495 if (!HelpTextContainsLongOption(optionDef->Name, (*optSetP)->OptionHelp))
1497 PrintArgError("INTERNAL ERROR: No help text defined for option: --%s\n", optionDef->Name);
1501 if (IsShortOptionChar(optionDef->Id) &&
1502 !HelpTextContainsShortOption(static_cast<char>(optionDef->Id), (*optSetP)->OptionHelp))
1504 PrintArgError("INTERNAL ERROR: No help text defined for option: -%c\n", optionDef->Id);
1512 static bool HelpTextContainsLongOption(const char * optName, const char * helpText)
1514 size_t nameLen = strlen(optName);
1516 for (const char * p = helpText; (p = strstr(p, optName)) != nullptr; p += nameLen)
1517 if ((p - helpText) >= 2 && p[-1] == '-' && p[-2] == '-' && !isalnum(p[nameLen]) && p[nameLen] != '-')
1523 static bool HelpTextContainsShortOption(char optChar, const char * helpText)
1527 optStr[1] = optChar;
1530 for (const char * p = helpText; (p = strstr(p, optStr)) != nullptr; p += 2)
1531 if ((p == helpText || (!isalnum(p[-1]) && p[-1] != '-')) && !isalnum(p[2]) && p[2] != '-')
1537 #endif // CHIP_CONFIG_ENABLE_ARG_PARSER_SANTIY_CHECK
1539 } // namespace ArgParser
1542 #endif // CHIP_CONFIG_ENABLE_ARG_PARSER