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