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