/src/connectedhomeip/src/lib/support/CHIPArgParser.cpp
Line | Count | Source |
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 | | static inline bool IsShortOptionChar(int ch) |
73 | 0 | { |
74 | 0 | return CanCastTo<uint8_t>(ch) && isgraph(ch); |
75 | 0 | } |
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 | | bool ParseArgs(const char * progName, int argc, char * const argv[], OptionSet * optSets[], |
275 | | NonOptionArgHandlerFunct nonOptArgHandler, bool ignoreUnknown) |
276 | 0 | { |
277 | 0 | bool res = false; |
278 | 0 | char optName[64]; |
279 | 0 | char * optArg; |
280 | 0 | char * shortOpts = nullptr; |
281 | 0 | struct option * longOpts = nullptr; |
282 | 0 | OptionSet * curOptSet; |
283 | 0 | OptionDef * curOpt; |
284 | 0 | 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 | 0 | if (gActiveOptionSets != nullptr) |
294 | 0 | { |
295 | 0 | PrintArgError("INTERNAL ERROR: ParseArgs() called recursively\n", progName); |
296 | 0 | return false; |
297 | 0 | } |
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 | 0 | if (argv[argc] != nullptr) |
302 | 0 | { |
303 | 0 | PrintArgError("INTERNAL ERROR: argv[argc] != NULL\n", progName); |
304 | 0 | return false; |
305 | 0 | } |
306 | | |
307 | | // Set gActiveOptionSets to the current option set list. |
308 | 0 | gActiveOptionSets = optSets; |
309 | |
|
310 | 0 | if (!SanityCheckOptions(optSets)) |
311 | 0 | goto done; |
312 | | |
313 | | // Generate a short options string in the format expected by getopt_long(). |
314 | 0 | shortOpts = MakeShortOptions(optSets); |
315 | 0 | if (shortOpts == nullptr) |
316 | 0 | { |
317 | 0 | PrintArgError("%s: Memory allocation failure\n", progName); |
318 | 0 | goto done; |
319 | 0 | } |
320 | | |
321 | | // Generate a list of long option structures in the format expected by getopt_long(). |
322 | 0 | longOpts = MakeLongOptions(optSets); |
323 | 0 | if (longOpts == nullptr) |
324 | 0 | { |
325 | 0 | PrintArgError("%s: Memory allocation failure\n", progName); |
326 | 0 | goto done; |
327 | 0 | } |
328 | | |
329 | | // Force getopt() to reset its internal state. |
330 | 0 | optind = 0; |
331 | | |
332 | | // Process any option arguments... |
333 | 0 | while (true) |
334 | 0 | { |
335 | 0 | int id; |
336 | 0 | int optIndex = -1; |
337 | | |
338 | | // Attempt to match the current option argument (argv[optind]) against the defined long and short options. |
339 | 0 | optarg = nullptr; |
340 | 0 | 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 | 0 | id = getopt_long(argc, argv, shortOpts, longOpts, &optIndex); |
371 | | |
372 | | // Stop if there are no more options. |
373 | 0 | if (id == -1) |
374 | 0 | break; |
375 | | |
376 | | // If the current option is unrecognized, fail with an error message unless ignoreUnknown == true. |
377 | 0 | if (id == '?') |
378 | 0 | { |
379 | 0 | if (ignoreUnknown) |
380 | 0 | 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 | 0 | if (optopt != 0) |
406 | 0 | PrintArgError("%s: Unknown option: -%c\n", progName, optopt); |
407 | 0 | else |
408 | 0 | PrintArgError("%s: Unknown option: %s\n", progName, argv[optind - 1]); |
409 | 0 | #endif // CHIP_CONFIG_NON_POSIX_LONG_OPT |
410 | 0 | goto done; |
411 | 0 | } |
412 | | |
413 | | // If the option was recognized, but it is lacking an argument, fail with |
414 | | // an error message. |
415 | 0 | if (id == ':') |
416 | 0 | { |
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 | 0 | PrintArgError("%s: Missing argument for %s option\n", progName, argv[optind - 1]); |
423 | 0 | #endif // CHIP_CONFIG_NON_POSIX_LONG_OPT |
424 | 0 | goto done; |
425 | 0 | } |
426 | | |
427 | | // If a long option was matched... |
428 | 0 | if (optIndex != -1) |
429 | 0 | { |
430 | | |
431 | | // Locate the option set and definition using the index value returned by getopt_long(). |
432 | 0 | FindOptionByIndex(optSets, optIndex, curOptSet, curOpt); |
433 | | |
434 | | // Form a string containing the name of the option as it appears on the command line. |
435 | 0 | snprintf(optName, sizeof(optName), "--%s", curOpt->Name); |
436 | 0 | } |
437 | | |
438 | | // Otherwise a short option was matched... |
439 | 0 | else |
440 | 0 | { |
441 | | // Locate the option set and definition using the option id. |
442 | 0 | 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 | 0 | snprintf(optName, sizeof(optName), "-%c", id); |
447 | 0 | } |
448 | | |
449 | | // Prevent handlers from inadvertently using the getopt global optarg. |
450 | 0 | optArg = optarg; |
451 | 0 | optarg = nullptr; |
452 | | |
453 | | // Call the option handler function defined for the matching option set. |
454 | | // Exit immediately if the option handler failed. |
455 | 0 | handlerRes = curOptSet->OptionHandler(progName, curOptSet, id, optName, optArg); |
456 | 0 | if (!handlerRes) |
457 | 0 | goto done; |
458 | 0 | } |
459 | | |
460 | | // If supplied, call the non-option argument handler with the remaining arguments (if any). |
461 | 0 | if (nonOptArgHandler != nullptr) |
462 | 0 | { |
463 | 0 | if (!nonOptArgHandler(progName, argc - optind, argv + optind)) |
464 | 0 | goto done; |
465 | 0 | } |
466 | | |
467 | | // otherwise, if there are additional arguments, fail with an error. |
468 | 0 | else if (optind < argc) |
469 | 0 | { |
470 | 0 | PrintArgError("%s: Unexpected argument: %s\n", progName, argv[optind]); |
471 | 0 | goto done; |
472 | 0 | } |
473 | | |
474 | 0 | res = true; |
475 | |
|
476 | 0 | done: |
477 | |
|
478 | 0 | if (shortOpts != nullptr) |
479 | 0 | chip::Platform::MemoryFree(shortOpts); |
480 | 0 | if (longOpts != nullptr) |
481 | 0 | chip::Platform::MemoryFree(longOpts); |
482 | |
|
483 | 0 | gActiveOptionSets = nullptr; |
484 | |
|
485 | 0 | return res; |
486 | 0 | } |
487 | | |
488 | | bool ParseArgs(const char * progName, int argc, char * const argv[], OptionSet * optSets[], |
489 | | NonOptionArgHandlerFunct nonOptArgHandler) |
490 | 0 | { |
491 | 0 | return ParseArgs(progName, argc, argv, optSets, nonOptArgHandler, false); |
492 | 0 | } |
493 | | |
494 | | bool ParseArgs(const char * progName, int argc, char * const argv[], OptionSet * optSets[]) |
495 | 0 | { |
496 | 0 | return ParseArgs(progName, argc, argv, optSets, nullptr, false); |
497 | 0 | } |
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 | | bool ParseArgsFromString(const char * progName, const char * argStr, OptionSet * optSets[], |
543 | | NonOptionArgHandlerFunct nonOptArgHandler, bool ignoreUnknown) |
544 | 0 | { |
545 | 0 | char ** argv = nullptr; |
546 | 0 | int argc; |
547 | 0 | bool res; |
548 | |
|
549 | 0 | chip::Platform::ScopedMemoryString argStrCopy(argStr, strlen(argStr)); |
550 | 0 | if (!argStrCopy) |
551 | 0 | { |
552 | 0 | PrintArgError("%s: Memory allocation failure\n", progName); |
553 | 0 | return false; |
554 | 0 | } |
555 | | |
556 | 0 | argc = SplitArgs(argStrCopy.Get(), argv, const_cast<char *>(progName)); |
557 | 0 | if (argc < 0) |
558 | 0 | { |
559 | 0 | PrintArgError("%s: Memory allocation failure\n", progName); |
560 | 0 | return false; |
561 | 0 | } |
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 | | bool ParseArgsFromString(const char * progName, const char * argStr, OptionSet * optSets[], |
571 | | NonOptionArgHandlerFunct nonOptArgHandler) |
572 | 0 | { |
573 | 0 | return ParseArgsFromString(progName, argStr, optSets, nonOptArgHandler, false); |
574 | 0 | } |
575 | | |
576 | | bool ParseArgsFromString(const char * progName, const char * argStr, OptionSet * optSets[]) |
577 | 0 | { |
578 | 0 | return ParseArgsFromString(progName, argStr, optSets, nullptr, false); |
579 | 0 | } |
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 | | bool ParseArgsFromEnvVar(const char * progName, const char * varName, OptionSet * optSets[], |
610 | | NonOptionArgHandlerFunct nonOptArgHandler, bool ignoreUnknown) |
611 | 0 | { |
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 | 0 | } |
617 | | |
618 | | bool ParseArgsFromEnvVar(const char * progName, const char * varName, OptionSet * optSets[]) |
619 | 0 | { |
620 | 0 | return ParseArgsFromEnvVar(progName, varName, optSets, nullptr, false); |
621 | 0 | } |
622 | | |
623 | | bool ParseArgsFromEnvVar(const char * progName, const char * varName, OptionSet * optSets[], |
624 | | NonOptionArgHandlerFunct nonOptArgHandler) |
625 | 0 | { |
626 | 0 | return ParseArgsFromEnvVar(progName, varName, optSets, nonOptArgHandler, false); |
627 | 0 | } |
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 | | void PrintOptionHelp(OptionSet * optSets[], FILE * s) |
639 | 0 | { |
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 | 0 | { |
644 | 0 | PrintArgError("Memory allocation failure\n"); |
645 | 0 | return; |
646 | 0 | } |
647 | | |
648 | | // For each help group... |
649 | 0 | for (size_t nameIndex = 0; helpGroupNames[nameIndex] != nullptr; nameIndex++) |
650 | 0 | { |
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 | 0 | { |
658 | 0 | PutStringWithBlankLine(s, optSets[optSetIndex]->OptionHelp); |
659 | 0 | } |
660 | 0 | } |
661 | |
|
662 | 0 | chip::Platform::MemoryFree(helpGroupNames); |
663 | 0 | } |
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 | | void ENFORCE_FORMAT(1, 2) DefaultPrintArgError(const char * msg, ...) |
679 | 0 | { |
680 | 0 | 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 | | bool ParseBoolean(const char * str, bool & output) |
701 | 0 | { |
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 | 0 | { |
705 | 0 | output = true; |
706 | 0 | return true; |
707 | 0 | } |
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 | 0 | { |
712 | 0 | output = false; |
713 | 0 | return true; |
714 | 0 | } |
715 | | |
716 | 0 | return false; |
717 | 0 | } |
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 | | bool ParseInt(const char * str, uint64_t & output, int base) |
736 | 0 | { |
737 | 0 | 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 | 0 | } |
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 | | bool ParseInt(const char * str, uint32_t & output, int base) |
762 | 0 | { |
763 | 0 | char * parseEnd; |
764 | 0 | unsigned long v; |
765 | |
|
766 | 0 | errno = 0; |
767 | 0 | v = strtoul(str, &parseEnd, base); |
768 | 0 | if (!CanCastTo<uint32_t>(v)) |
769 | 0 | { |
770 | 0 | return false; |
771 | 0 | } |
772 | 0 | output = static_cast<uint32_t>(v); |
773 | |
|
774 | 0 | return parseEnd > str && *parseEnd == 0 && (v != ULONG_MAX || errno == 0); |
775 | 0 | } |
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 | | bool ParseInt(const char * str, int32_t & output, int base) |
794 | 0 | { |
795 | 0 | char * parseEnd; |
796 | 0 | long v; |
797 | |
|
798 | 0 | errno = 0; |
799 | 0 | v = strtol(str, &parseEnd, base); |
800 | 0 | if (!CanCastTo<int32_t>(v)) |
801 | 0 | { |
802 | 0 | return false; |
803 | 0 | } |
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 | 0 | } |
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 | | bool ParseInt(const char * str, uint16_t & output, int base) |
826 | 0 | { |
827 | 0 | uint32_t v; |
828 | |
|
829 | 0 | if (!ParseInt(str, v, base) || !CanCastTo<uint16_t>(v)) |
830 | 0 | { |
831 | 0 | return false; |
832 | 0 | } |
833 | 0 | output = static_cast<uint16_t>(v); |
834 | |
|
835 | 0 | return true; |
836 | 0 | } |
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 | | bool ParseInt(const char * str, uint8_t & output, int base) |
855 | 0 | { |
856 | 0 | uint32_t v; |
857 | |
|
858 | 0 | if (!ParseInt(str, v, base) || !CanCastTo<uint8_t>(v)) |
859 | 0 | { |
860 | 0 | return false; |
861 | 0 | } |
862 | 0 | output = static_cast<uint8_t>(v); |
863 | |
|
864 | 0 | return true; |
865 | 0 | } |
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 | | bool ParseInt(const char * str, uint64_t & output) |
880 | 0 | { |
881 | 0 | const int base = 10; |
882 | |
|
883 | 0 | return ParseInt(str, output, base); |
884 | 0 | } |
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 | | bool ParseInt(const char * str, uint32_t & output) |
899 | 0 | { |
900 | 0 | const int base = 10; |
901 | |
|
902 | 0 | return ParseInt(str, output, base); |
903 | 0 | } |
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 | | bool ParseInt(const char * str, int32_t & output) |
918 | 0 | { |
919 | 0 | const int base = 10; |
920 | |
|
921 | 0 | return ParseInt(str, output, base); |
922 | 0 | } |
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 | | bool ParseInt(const char * str, uint16_t & output) |
937 | 0 | { |
938 | 0 | const int base = 10; |
939 | 0 | uint32_t output32 = 0; |
940 | |
|
941 | 0 | if ((ParseInt(str, output32, base)) && (output32 <= USHRT_MAX)) |
942 | 0 | { |
943 | 0 | output = ((1 << 16) - 1) & output32; |
944 | 0 | return true; |
945 | 0 | } |
946 | | |
947 | 0 | return false; |
948 | 0 | } |
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 | | bool ParseInt(const char * str, int16_t & output) |
963 | 0 | { |
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 | 0 | { |
969 | 0 | output = static_cast<int16_t>(output32); |
970 | 0 | return true; |
971 | 0 | } |
972 | | |
973 | 0 | return false; |
974 | 0 | } |
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 | | bool ParseInt(const char * str, uint8_t & output) |
989 | 0 | { |
990 | 0 | const int base = 10; |
991 | 0 | uint32_t output32 = 0; |
992 | |
|
993 | 0 | if ((ParseInt(str, output32, base)) && (output32 <= UCHAR_MAX)) |
994 | 0 | { |
995 | 0 | output = ((1 << 8) - 1) & output32; |
996 | 0 | return true; |
997 | 0 | } |
998 | | |
999 | 0 | return false; |
1000 | 0 | } |
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 | | bool ParseSubnetId(const char * str, uint16_t & subnetId) |
1044 | 0 | { |
1045 | 0 | char * parseEnd; |
1046 | 0 | unsigned long temp; |
1047 | 0 | 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 | 0 | temp <= USHRT_MAX); // Parsed value is valid for the domain (subnet ID) |
1063 | |
|
1064 | 0 | if (valid) |
1065 | 0 | { |
1066 | 0 | subnetId = static_cast<uint16_t>(temp); |
1067 | 0 | } |
1068 | |
|
1069 | 0 | return valid; |
1070 | 0 | } |
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 | | bool ParseHexString(const char * hexStr, uint32_t strLen, uint8_t * outBuf, uint32_t outBufSize, uint32_t & outDataLen) |
1094 | 0 | { |
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 | 0 | { |
1104 | 0 | char c = *p; |
1105 | 0 | 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 | 0 | else |
1120 | 0 | { |
1121 | 0 | outDataLen = static_cast<decltype(strLen)>(p - hexStr); |
1122 | 0 | return false; |
1123 | 0 | } |
1124 | | |
1125 | 0 | if (isFirstNibble) |
1126 | 0 | { |
1127 | 0 | firstNibbleVal = nibbleVal; |
1128 | 0 | isFirstNibble = false; |
1129 | 0 | } |
1130 | 0 | else |
1131 | 0 | { |
1132 | 0 | if (outBufSize == 0) |
1133 | 0 | { |
1134 | 0 | outDataLen = UINT32_MAX; |
1135 | 0 | return false; |
1136 | 0 | } |
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 | 0 | } |
1146 | 0 | } |
1147 | | |
1148 | 0 | if (!isFirstNibble) |
1149 | 0 | { |
1150 | 0 | outDataLen = static_cast<decltype(strLen)>(p - hexStr); |
1151 | 0 | return false; |
1152 | 0 | } |
1153 | | |
1154 | 0 | outDataLen = dataLen; |
1155 | |
|
1156 | 0 | return true; |
1157 | 0 | } |
1158 | | |
1159 | | // ===== HelpOptions Methods ===== |
1160 | | |
1161 | | HelpOptions::HelpOptions(const char * appName, const char * appUsage, const char * appVersion) : |
1162 | 0 | HelpOptions(appName, appUsage, appVersion, nullptr) |
1163 | 0 | {} |
1164 | | |
1165 | | HelpOptions::HelpOptions(const char * appName, const char * appUsage, const char * appVersion, const char * appDesc) |
1166 | 0 | { |
1167 | | // clang-format off |
1168 | 0 | static OptionDef optionDefs[] = |
1169 | 0 | { |
1170 | 0 | { "help", kNoArgument, 'h' }, |
1171 | 0 | { "version", kNoArgument, 'v' }, |
1172 | 0 | { } |
1173 | 0 | }; |
1174 | | // clang-format on |
1175 | 0 | OptionDefs = optionDefs; |
1176 | |
|
1177 | 0 | HelpGroupName = "HELP OPTIONS"; |
1178 | |
|
1179 | 0 | OptionHelp = " -h, --help\n" |
1180 | 0 | " Print this output and then exit.\n" |
1181 | 0 | "\n" |
1182 | 0 | " -v, --version\n" |
1183 | 0 | " Print the version and then exit.\n" |
1184 | 0 | "\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 | | void HelpOptions::PrintBriefUsage(FILE * s) const |
1196 | 0 | { |
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 | | void HelpOptions::PrintLongUsage(OptionSet ** optSets, FILE * s) const |
1205 | 0 | { |
1206 | 0 | PutStringWithBlankLine(s, AppUsage); |
1207 | 0 | if (AppDesc != nullptr) |
1208 | 0 | { |
1209 | 0 | PutStringWithBlankLine(s, AppDesc); |
1210 | 0 | } |
1211 | 0 | PrintOptionHelp(optSets, s); |
1212 | 0 | } |
1213 | | |
1214 | | void HelpOptions::PrintVersion(FILE * s) const |
1215 | 0 | { |
1216 | 0 | fprintf(s, "%s ", AppName); |
1217 | 0 | PutStringWithNewLine(s, (AppVersion != nullptr) ? AppVersion : "(unknown version)"); |
1218 | 0 | } |
1219 | | |
1220 | | bool HelpOptions::HandleOption(const char * progName, OptionSet * optSet, int id, const char * name, const char * arg) |
1221 | 0 | { |
1222 | 0 | switch (id) |
1223 | 0 | { |
1224 | 0 | case 'h': |
1225 | 0 | PrintLongUsage(gActiveOptionSets, stdout); |
1226 | 0 | exit(EXIT_SUCCESS); |
1227 | 0 | break; |
1228 | 0 | case 'v': |
1229 | 0 | PrintVersion(stdout); |
1230 | 0 | exit(EXIT_SUCCESS); |
1231 | 0 | break; |
1232 | 0 | default: |
1233 | 0 | PrintArgError("%s: INTERNAL ERROR: Unhandled option: %s\n", progName, name); |
1234 | 0 | return false; |
1235 | 0 | } |
1236 | | |
1237 | 0 | return true; |
1238 | 0 | } |
1239 | | |
1240 | | // ===== Private/Internal Methods ===== |
1241 | | |
1242 | | OptionSetBase::OptionSetBase() |
1243 | 0 | { |
1244 | 0 | OptionHandler = CallHandleFunct; |
1245 | 0 | } |
1246 | | |
1247 | | bool OptionSetBase::CallHandleFunct(const char * progName, OptionSet * optSet, int id, const char * name, const char * arg) |
1248 | 0 | { |
1249 | 0 | return static_cast<OptionSetBase *>(optSet)->HandleOption(progName, optSet, id, name, arg); |
1250 | 0 | } |
1251 | | |
1252 | | static char * MakeShortOptions(OptionSet ** optSets) |
1253 | 0 | { |
1254 | 0 | size_t i = 0; |
1255 | | |
1256 | | // Count the number of options. |
1257 | 0 | 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 | 0 | size_t arraySize = 2 + (totalOptions * 3); |
1263 | 0 | char * shortOpts = static_cast<char *>(chip::Platform::MemoryAlloc(arraySize)); |
1264 | 0 | 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 | 0 | shortOpts[i++] = ':'; |
1270 | | |
1271 | | // For each option set... |
1272 | 0 | for (; *optSets != nullptr; optSets++) |
1273 | 0 | { |
1274 | | // For each option in the current option set... |
1275 | 0 | for (OptionDef * optDef = (*optSets)->OptionDefs; optDef->Name != nullptr; optDef++) |
1276 | 0 | { |
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 | 0 | if (IsShortOptionChar(optDef->Id)) |
1281 | 0 | { |
1282 | 0 | shortOpts[i++] = static_cast<char>(optDef->Id); |
1283 | 0 | if (optDef->ArgType != kNoArgument) |
1284 | 0 | shortOpts[i++] = ':'; |
1285 | 0 | if (optDef->ArgType == kArgumentOptional) |
1286 | 0 | shortOpts[i++] = ':'; |
1287 | 0 | } |
1288 | 0 | } |
1289 | 0 | } |
1290 | | |
1291 | | // Terminate the short options string. |
1292 | 0 | shortOpts[i++] = 0; |
1293 | |
|
1294 | 0 | return shortOpts; |
1295 | 0 | } |
1296 | | |
1297 | | static struct option * MakeLongOptions(OptionSet ** optSets) |
1298 | 0 | { |
1299 | 0 | size_t totalOptions = CountAllOptions(optSets); |
1300 | | |
1301 | | // Allocate an array to hold the list of long options. |
1302 | 0 | size_t arraySize = (sizeof(struct option) * (totalOptions + 1)); |
1303 | 0 | struct option * longOpts = static_cast<struct option *>(chip::Platform::MemoryAlloc(arraySize)); |
1304 | 0 | if (longOpts == nullptr) |
1305 | 0 | return nullptr; |
1306 | | |
1307 | | // For each option set... |
1308 | 0 | size_t i = 0; |
1309 | 0 | for (; *optSets != nullptr; optSets++) |
1310 | 0 | { |
1311 | | // Copy the option definitions into the long options array. |
1312 | 0 | for (OptionDef * optDef = (*optSets)->OptionDefs; optDef->Name != nullptr; optDef++) |
1313 | 0 | { |
1314 | 0 | longOpts[i].name = optDef->Name; |
1315 | 0 | longOpts[i].has_arg = static_cast<int>(optDef->ArgType); |
1316 | 0 | longOpts[i].flag = nullptr; |
1317 | 0 | longOpts[i].val = optDef->Id; |
1318 | 0 | i++; |
1319 | 0 | } |
1320 | 0 | } |
1321 | | |
1322 | | // Terminate the long options array. |
1323 | 0 | longOpts[i].name = nullptr; |
1324 | |
|
1325 | 0 | return longOpts; |
1326 | 0 | } |
1327 | | |
1328 | | static int32_t SplitArgs(char * argStr, char **& argList, char * initialArg) |
1329 | 0 | { |
1330 | 0 | enum |
1331 | 0 | { |
1332 | 0 | InitialArgListSize = 10 |
1333 | 0 | }; |
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 | 0 | { |
1346 | 0 | argList[0] = initialArg; |
1347 | 0 | argCount = 1; |
1348 | 0 | } |
1349 | | |
1350 | | // Parse arguments from the input string until it is exhausted. |
1351 | 0 | while (true) |
1352 | 0 | { |
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 | 0 | { |
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 | 0 | } |
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 | 0 | } |
1379 | | |
1380 | | static bool GetNextArg(char *& parsePoint) |
1381 | 0 | { |
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 | 0 | { |
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 | 0 | { |
1404 | 0 | parsePoint++; |
1405 | 0 | } |
1406 | | |
1407 | | // Otherwise, if not within a quoted substring... |
1408 | 0 | else if (quoteChar == 0) |
1409 | 0 | { |
1410 | | // Whitespace marks the end of the argument. |
1411 | 0 | if (isspace(*parsePoint)) |
1412 | 0 | { |
1413 | 0 | parsePoint++; |
1414 | 0 | break; |
1415 | 0 | } |
1416 | | |
1417 | | // If the character is a quote character, enter quoted substring mode. |
1418 | 0 | if (*parsePoint == '"' || *parsePoint == '\'') |
1419 | 0 | { |
1420 | 0 | quoteChar = *parsePoint++; |
1421 | 0 | continue; |
1422 | 0 | } |
1423 | 0 | } |
1424 | | |
1425 | | // Otherwise, the parse point is within a quoted substring, so... |
1426 | 0 | else |
1427 | 0 | { |
1428 | | // A corresponding quote character marks the end of the quoted string. |
1429 | 0 | if (*parsePoint == quoteChar) |
1430 | 0 | { |
1431 | 0 | quoteChar = 0; |
1432 | 0 | parsePoint++; |
1433 | 0 | continue; |
1434 | 0 | } |
1435 | 0 | } |
1436 | | |
1437 | | // Copy the current character to the end of the argument string. |
1438 | 0 | *argEnd++ = *parsePoint++; |
1439 | 0 | } |
1440 | | |
1441 | | // Terminate the argument string. |
1442 | 0 | *argEnd = 0; |
1443 | |
|
1444 | 0 | return true; |
1445 | 0 | } |
1446 | | |
1447 | | static size_t CountOptionSets(OptionSet ** optSets) |
1448 | 0 | { |
1449 | 0 | size_t count = 0; |
1450 | 0 | for (; *optSets != nullptr; optSets++) |
1451 | 0 | count++; |
1452 | 0 | return count; |
1453 | 0 | } |
1454 | | |
1455 | | static size_t CountAllOptions(OptionSet ** optSets) |
1456 | 0 | { |
1457 | 0 | size_t count = 0; |
1458 | 0 | for (; *optSets != nullptr; optSets++) |
1459 | 0 | for (OptionDef * optDef = (*optSets)->OptionDefs; optDef->Name != nullptr; optDef++) |
1460 | 0 | count++; |
1461 | 0 | return count; |
1462 | 0 | } |
1463 | | |
1464 | | static void FindOptionByIndex(OptionSet ** optSets, int optIndex, OptionSet *& optSet, OptionDef *& optDef) |
1465 | 0 | { |
1466 | 0 | for (optSet = *optSets; optSet != nullptr; optSet = *++optSets) |
1467 | 0 | for (optDef = (*optSets)->OptionDefs; optDef->Name != nullptr; optDef++) |
1468 | 0 | if (optIndex-- == 0) |
1469 | 0 | return; |
1470 | 0 | optSet = nullptr; |
1471 | 0 | optDef = nullptr; |
1472 | 0 | } |
1473 | | |
1474 | | static void FindOptionById(OptionSet ** optSets, int optId, OptionSet *& optSet, OptionDef *& optDef) |
1475 | 0 | { |
1476 | 0 | for (optSet = *optSets; optSet != nullptr; optSet = *++optSets) |
1477 | 0 | for (optDef = (*optSets)->OptionDefs; optDef->Name != nullptr; optDef++) |
1478 | 0 | if (optDef->Id == optId) |
1479 | 0 | return; |
1480 | 0 | optSet = nullptr; |
1481 | 0 | optDef = nullptr; |
1482 | 0 | } |
1483 | | |
1484 | | static const char ** MakeUniqueHelpGroupNamesList(OptionSet * optSets[]) |
1485 | 0 | { |
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 | 0 | { |
1495 | 0 | if (optSets[optSetIndex] != nullptr && optSets[optSetIndex]->OptionDefs[0].Name != nullptr) |
1496 | 0 | { |
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 | 0 | } |
1503 | 0 | } |
1504 | | |
1505 | 0 | groupNames[numGroups] = nullptr; |
1506 | |
|
1507 | 0 | return groupNames; |
1508 | 0 | } |
1509 | | |
1510 | | static void PutStringWithNewLine(FILE * s, const char * str) |
1511 | 0 | { |
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 | | static void PutStringWithBlankLine(FILE * s, const char * str) |
1519 | 0 | { |
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 | | static bool SanityCheckOptions(OptionSet * optSets[]) |
1529 | 0 | { |
1530 | 0 | bool res = true; |
1531 | | |
1532 | | // Verify OptionHandler pointer |
1533 | 0 | for (OptionSet ** optSetP = optSets; *optSetP != nullptr; optSetP++) |
1534 | 0 | { |
1535 | 0 | if ((*optSetP)->OptionHandler == nullptr) |
1536 | 0 | { |
1537 | 0 | PrintArgError("INTERNAL ERROR: Null OptionHandler in OptionSet (%s)\n", (*optSetP)->HelpGroupName); |
1538 | 0 | res = false; |
1539 | 0 | } |
1540 | 0 | } |
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 | 0 | for (OptionSet ** optSetP = optSets; *optSetP != nullptr; optSetP++) |
1546 | 0 | for (OptionDef * optionDef = (*optSetP)->OptionDefs; optionDef->Name != nullptr; optionDef++) |
1547 | 0 | if (IsShortOptionChar(optionDef->Id)) |
1548 | 0 | { |
1549 | 0 | for (OptionSet ** optSetP2 = optSets; *optSetP2 != nullptr; optSetP2++) |
1550 | 0 | if (optSetP2 != optSetP) |
1551 | 0 | { |
1552 | 0 | for (OptionDef * optionDef2 = (*optSetP2)->OptionDefs; optionDef2->Name != nullptr; optionDef2++) |
1553 | 0 | if (optionDef->Id == optionDef2->Id) |
1554 | 0 | { |
1555 | 0 | PrintArgError("INTERNAL ERROR: Multiple command line options configured to use " |
1556 | 0 | "the same short option character (-%c): --%s, --%s\n", |
1557 | 0 | optionDef->Id, optionDef->Name, optionDef2->Name); |
1558 | 0 | res = false; |
1559 | 0 | } |
1560 | 0 | } |
1561 | 0 | } |
1562 | |
|
1563 | 0 | return res; |
1564 | 0 | } |
1565 | | |
1566 | | } // namespace ArgParser |
1567 | | } // namespace chip |