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