| // Copyright 2019 The Fuchsia Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #ifndef SRC_GRAPHICS_LIB_COMPUTE_TESTS_COMMON_ARGPARSE_H_ | |
| #define SRC_GRAPHICS_LIB_COMPUTE_TESTS_COMMON_ARGPARSE_H_ | |
| #include <stdbool.h> | |
| #include <stdint.h> | |
| #ifdef __cplusplus | |
| extern "C" { | |
| #endif | |
| // clang-format off | |
| // An easy-to-use command-line parsing facility for C programs. Inspired by | |
| // the Python argparse library: | |
| // | |
| // - Simplify option list declarations and value collection. | |
| // | |
| // - Automatically handle parsing errors (e.g. missing parameter, unknown | |
| // option, or integer overflow/underflow) for you. | |
| // | |
| // - Automically handle printing a decent help message with --help or -? | |
| // is used on the command-line. | |
| // | |
| // Usage is the following: | |
| // | |
| // 1) Define a macro that must take a single |param| parameter, and which | |
| // consists in a series of ARGPARSE_OPTION_XXX() calls, where each call | |
| // corresponds to a command-line option, and should take 5 arguments | |
| // which are: | |
| // | |
| // |param| is the single parameter of your custom list macro. | |
| // |name| is the option's result struct member name. | |
| // |char| is a single char for short-format options, or 0 if not needed. | |
| // |long| is a string for long-format options, or NULL if not needed. | |
| // |description| is a short description for the option. | |
| // | |
| // For example: | |
| // | |
| // #define MY_OPTIONS(param) \ | |
| // ARGPARSE_OPTION_COUNTER(param, verbose, 'v', "verbose", "Increment verbosity.") \ | |
| // ARGPARSE_OPTION_COUNTER(param, quiet, 'q', "quiet", "Decrement verbosity.") \ | |
| // ARGPARSE_OPTION_FLAG(param, dry_run, 'n', "dry-run", "Dry-run.") \ | |
| // ARGPARSE_OPTION_STRING(param, output, 'o', "output", "Output file path.") \ | |
| // | |
| // There are several ARGPARSE_OPTION_XXX() macros defined below, use the one | |
| // corresponding to the option types you need. | |
| // | |
| // 2) Call ARGPARSE_DEFINE_OPTIONS_AND_PARSE_ARGS(my_options,MY_OPTIONS,...) | |
| // this will do all these things for you: | |
| // | |
| // - Define a struct variable named |my_options|, whose members match the | |
| // names of your ARGPARSE_OPTION_XXX() calls. | |
| // | |
| // - Pass |argc| and |argv| to the command-line parser, and removes all | |
| // processed options from them (so you can inspect additional arguments). | |
| // | |
| // - In case of error, print an error message to stderr, then call | |
| // exit(EXIT_FAILURE). | |
| // | |
| // - If needed, print the help message to stdout and exit(EXIT_SUCCESS). | |
| // This happens if --help or -? are used on the command-line. | |
| // | |
| // After the call, the option values can be accessed directly through your | |
| // struct variable, e.g. |my_options.verbose|, |my_options.output|, etc. | |
| // | |
| // The C type of these members corresponds their ARGPARSE_OPTION_XXX() | |
| // macro. E.g. it will be a bool for ARGPARSE_OPTION_FLAG(). See the | |
| // ARGPARSE_OPTION_XXX() documentation below for details. | |
| // | |
| // Complete example: | |
| // | |
| // #define MY_OPTIONS(param) \ | |
| // ARGPARSE_OPTION_COUNTER(param, verbose, 'v', "verbose", "Increment verbosity.") \ | |
| // ARGPARSE_OPTION_COUNTER(param, quiet, 'q', "quiet", "Decrement verbosity.") \ | |
| // ARGPARSE_OPTION_FLAG(param, dry_run, 'n', "dry-run", "Dry-run.") \ | |
| // ARGPARSE_OPTION_STRING(param, output, 'o', "output", "Output file path.") \ | |
| // | |
| // int main(int argc, char** argv) { | |
| // ARGPARSE_DEFINE_OPTIONS_AND_PARSE_ARGS( | |
| // my_options, | |
| // MY_OPTIONS, | |
| // argc, | |
| // argv, | |
| // "myprogram", | |
| // "My program short description"); | |
| // | |
| // // Voila! Simply use members from |my_options| here. | |
| // ... use options.<name> variable now. | |
| // } | |
| // | |
| // Alternatively, you can perform these steps separately if you prefer by using | |
| // the following calls: | |
| // | |
| // 1) Call ARGPARSE_DEFINE_OPTIONS_STRUCT(my_options, MY_OPTIONS) to define a | |
| // variable that is a struct holding your options values, e.g.: | |
| // | |
| // ARGPARSE_DEFINE_OPTIONS_STRUCT(my_options, MY_OPTIONS); | |
| // | |
| // It expands to a struct variable declaration like this one: | |
| // | |
| // struct { | |
| // int verbose; | |
| // int quiet; | |
| // bool dry_run; | |
| // const char* output; | |
| // bool help_needed; // automatically added by argparse!! | |
| // } my_options = {} | |
| // | |
| // Note that |help_needed| is added automatically by the parser. One cannot | |
| // change its name, and it will be only true if the help message should be | |
| // printed (see below for exact conditions). | |
| // | |
| // 2) Call ARGPARSE_PARSE_ARGS(argc, argv, my_options, MY_OPTIONS) to parse | |
| // the command-line from |argc| and |argv| and fill |my_options| with the | |
| // appropriate values. | |
| // | |
| // On success, this returns true and removes all processed options from | |
| // |argc| and |argv|. Note that '--' is always treated as a parser stop | |
| // marker, so anything that appears after it in the input is left as-is | |
| // in the output, e.g. | |
| // | |
| // myprogram --verbose other_program -- --verbose argument | |
| // => myprogram other_program --verbose argument | |
| // | |
| // If the --help or -? option appears on the command line, and is not a | |
| // parameter to a previous known option, and does not appear after --, then | |
| // the function returns false, but sets the 'help_needed' options member to | |
| // true. The caller should print the help message (e.g. using | |
| // ARGPARSE_PRINT_HELP()), then should exit. | |
| // | |
| // If the parser detected an error, e.g. missing parameter or an unknown | |
| // option, if prints an error message to stderr, then returns false. | |
| // | |
| // 3) ARGPARSE_PRINT_HELP() can be used to print a help message to stdout. | |
| // The caller has the option of printing more text after the options | |
| // description text, but then should exit immediately, possibly with | |
| // exit(0); | |
| // | |
| // ARGPARSE_PRINT_HELP("myprogram", "My program description", MY_OPTIONS) | |
| // will print something like: | |
| // | |
| // Usage: myprogram [options] ... | |
| // | |
| // My program description | |
| // | |
| // -v, --verbose Increment verbosity. | |
| // -q, --quiet Decrement verbosity. | |
| // -n, --dry_run Dry run, do not write output. | |
| // -o, --output OUTPUT Output file path. | |
| // | |
| // Complete example: | |
| // | |
| // #define MY_OPTIONS(param) \ | |
| // ARGPARSE_OPTION_COUNTER(param, verbose, 'v', "--verbose", "Increment verbosity.") \ | |
| // ARGPARSE_OPTION_COUNTER(param, quiet, 'q', "--quiet", "Decrement verbosity.") \ | |
| // ARGPARSE_OPTION_FLAG(param, dry_run, 'n', "--dry-run", "Dry-run.") \ | |
| // ARGPARSE_OPTION_STRING(param, output, 'o', "--output", "Output file path.") \ | |
| // | |
| // int main(int argc, char** argv) { | |
| // ARGPARSE_DEFINE_OPTIONS_STRUCT(options); | |
| // if (!ARGPARSE_PARSE_ARGS(options, MY_OPTIONS)) { | |
| // if (options.help) { | |
| // ARGPARSE_PRINT_HELP("myprogram", "my description", MY_OPTIONS); | |
| // } | |
| // exit(options.help ? EXIT_SUCCESS : EXIT_FAILURE); | |
| // } | |
| // // Process options and remaining parameters. | |
| // } | |
| // | |
| // clang-format on | |
| // ARGPARSE_OPTION_XXX() macros, each one corresponds to a single option type | |
| // supported by the library, and take the same parameter list, which is: | |
| // |param| is the generic parameter taken by your cursomt options list macro. | |
| // |name| is the option's name, used as a member name in your options struct. | |
| // |chr| is the flag's single character for the command line, or 0 if not used. | |
| // |string| is the flag's long name for the command line, or NULL if not used. | |
| // |description| is a small string to describe the option. | |
| // Used to declare an optional boolean flag. The corresponding struct | |
| // member is a bool, initialized to false, and set to true if the flag | |
| // is parsed any number of times. | |
| #define ARGPARSE_OPTION_FLAG(macro, name, chr, string, description) \ | |
| macro(ARGPARSE_OPTION_TYPE_FLAG, name, chr, string, description) | |
| // Used to declare an optional string option. The corresponding struct | |
| // member is a const char*, initialized to NULL, and set to the option's | |
| // parameter if used. If parsed multiple times, this will always store the | |
| // last parameter. | |
| #define ARGPARSE_OPTION_STRING(macro, name, chr, string, description) \ | |
| macro(ARGPARSE_OPTION_TYPE_STRING, name, chr, string, description) | |
| // Used to declare an optional integer option. The corresponding options | |
| // member is an argparse_int value, which contains a |used| boolean flag, | |
| // and a |value| int32_t value that is only valid if |used == true|. | |
| #define ARGPARSE_OPTION_INT(macro, name, chr, string, description) \ | |
| macro(ARGPARSE_OPTION_TYPE_INT, name, chr, string, description) | |
| // Used to declare an optional counting flag. The corresponding options member | |
| // is an int, initialized to 0, and incremented each time the flag is used. | |
| #define ARGPARSE_OPTION_COUNTER(macro, name, chr, string, description) \ | |
| macro(ARGPARSE_OPTION_TYPE_COUNTER, name, chr, string, description) | |
| // Used to declare an optional double option. The corresponding options | |
| // member is an argparse_double value, which contains a |used| boolean flag, | |
| // and a |value| double value that is only valid if |used == true|. | |
| #define ARGPARSE_OPTION_DOUBLE(macro, name, chr, string, description) \ | |
| macro(ARGPARSE_OPTION_TYPE_DOUBLE, name, chr, string, description) | |
| // A small type used to store the value of an optional integer. | |
| // If the option appears, |used| will be true, and |value| will be the | |
| // value that appears on the command-line. Otherwise |used| is false and | |
| // |value| will be default-initialized to 0. | |
| struct argparse_int | |
| { | |
| bool used; | |
| int32_t value; | |
| }; | |
| // Same as argparse_int, but for a double value. | |
| struct argparse_double | |
| { | |
| bool used; | |
| double value; | |
| }; | |
| // Call this macro to perform everything for you at the start of your main(): | |
| // | |
| // 1) It will define an struct variable named |options| containing the values | |
| // of all your options. | |
| // | |
| // 2) It will parse |argc| and |argv| to process the options in MY_OPTIONS | |
| // and will also remove them, and their parameters, from the array. | |
| // | |
| // 3) If needed, print the help message to stdout and call exit(EXIT_SUCCESS). | |
| // | |
| // 4) On error, print an error message to stderr and call exit(EXIT_FAILURE). | |
| // | |
| // |options| is the name of your struct variable holding option values. | |
| // |options_list| is a custom macro list of ARGPARSE_OPTION_XXX() calls, one per option. | |
| // |argc| and |argv| are you main() function's command-line. | |
| // |progname| is a short program name for the help message. | |
| // |progdesc| is a short program description for the help message too. | |
| // | |
| #define ARGPARSE_DEFINE_OPTIONS_AND_PARSE_ARGS(options, \ | |
| options_list, \ | |
| argc, \ | |
| argv, \ | |
| progname, \ | |
| progdesc) \ | |
| ARGPARSE_DEFINE_OPTIONS_STRUCT(options, options_list); \ | |
| if (!ARGPARSE_PARSE_ARGS(argc, argv, options, options_list)) \ | |
| { \ | |
| if (options.help_needed) \ | |
| { \ | |
| ARGPARSE_PRINT_HELP(progname, progdesc, options_list); \ | |
| } \ | |
| exit(options.help_needed ? EXIT_SUCCESS : EXIT_FAILURE); \ | |
| } | |
| // Call this macro to define a struct variable named |options| containing | |
| // the option values defined through your custom |options_list| macro. | |
| // All values in |options| will be designated-initialized. | |
| #define ARGPARSE_DEFINE_OPTIONS_STRUCT(options, options_list) \ | |
| struct \ | |
| { \ | |
| ARGPARSE_LIST_APPLY(options_list, ARGPARSE_OPTION_TO_STRUCT_MEMBER_DEFINITION) \ | |
| } options = {} | |
| // Parse the command-line identified by |argc| and |argv| according to your | |
| // custom |options_list| macro, placing the options value into |options|, | |
| // which must have been declared previously with ARGPARSE_DEFINE_OPTIONS_STRUCT | |
| // and the same |options| and |options_list| arguments. | |
| // | |
| // Return an argparse_status value.If not ARGPARSE_STATUS_OK (0), the caller | |
| // should either print the help with ARGPARSE_PRINT_HELP(), or immediately | |
| // exit. | |
| #define ARGPARSE_PARSE_ARGS(argc, argv, options, options_list) \ | |
| argparse_parse_args(&(argc), \ | |
| (argv), \ | |
| ARGPARSE_LIST_TO_LAYOUT_ARRAY_LITERAL(options_list), \ | |
| ARGPARSE_LIST_TO_POINTER_ARRAY(options_list, options)) | |
| // Print the help description for |program_name|, where |program_description| | |
| // is NULL or a string describing the program's purpose. |options_list| is your | |
| // custom options macro list. Everything is printed to stdout. | |
| #define ARGPARSE_PRINT_HELP(program_name, program_description, options_list) \ | |
| argparse_print_help((program_name), \ | |
| (program_description), \ | |
| ARGPARSE_LIST_TO_LAYOUT_ARRAY_LITERAL(options_list)) | |
| // Internal structure used to describe the layout/properties of a given option. | |
| struct argparse_option_layout; | |
| // NOTE: Do not call this directly, use ARGPARSE_PARSE_ARGS() instead. | |
| // | |
| // Parse the command-line identified by |*p_argc| and |*p_argv|. | |
| // On success, remove the options from |*p_argc| and |*p_argv|. | |
| // |option_layout| and |option_ptrs| are two parallel arrays of the same size, | |
| // each item corresponding to an option description and the pointer to its | |
| // option struct member, respectively. The last item in |option_layout| | |
| // should have the special value ARGPARSE_OPTION_LAYOUT_END, and the one | |
| // in |option_ptrs| should be NULL. | |
| // | |
| // Return true on success, false on error/help needed. | |
| bool | |
| argparse_parse_args(int * const p_argc, | |
| const char ** const argv, | |
| const struct argparse_option_layout * const option_layout, | |
| void * const * const option_ptrs); | |
| // NOTE: Do not call this directly, use ARGPARSE_PRINT_HELP() instead. | |
| // | |
| // Print a help message for program |progname|, using a small optional | |
| // |description|, and the corresponding |option_layout| array, whose | |
| // last item should have the special value ARGPARSE_OPTION_LAYOUT_END. | |
| void | |
| argparse_print_help(const char * progname, | |
| const char * description, | |
| const struct argparse_option_layout * const option_layout); | |
| // | |
| // Internal data types definitions. | |
| // | |
| // WARNING: All that appears below is subject to change, so clients should | |
| // never rely on them in their code (except unit-tests which need | |
| // that to get a finer control of the parser). | |
| // | |
| typedef void (*argparse_print_func)(void *, const char * fmt, ...); | |
| // Same as argparse_print_help(), but allows passing a custom output function. | |
| // Only used by unit-tests. | |
| void | |
| argparse_print_help_internal(const char * progname, | |
| const char * description, | |
| const struct argparse_option_layout * const option_layout, | |
| argparse_print_func print_func, | |
| void * print_param); | |
| // Expand |x| through the pre-processor. This is useful because using # or ## | |
| // in an expression disables expansion. | |
| #define ARGPARSE_EVAL(x) ARGPARSE_EVAL_(x) | |
| #define ARGPARSE_EVAL_(x) x | |
| #define ARGPARSE_OPTION_HELP(macro) \ | |
| macro(ARGPARSE_OPTION_TYPE_HELP, help_needed, '?', "help", "Print help") | |
| // A enum listing the supported option types. Must match the types | |
| // used in ARGPARSE_OPTION_XXX(). | |
| // | |
| enum argparse_option_type | |
| { | |
| ARGPARSE_OPTION_TYPE_FLAG = 0, | |
| ARGPARSE_OPTION_TYPE_INT, | |
| ARGPARSE_OPTION_TYPE_STRING, | |
| ARGPARSE_OPTION_TYPE_COUNTER, | |
| ARGPARSE_OPTION_TYPE_DOUBLE, | |
| ARGPARSE_OPTION_TYPE_HELP, // Must always be last. | |
| }; | |
| #define ARGPARSE_OPTION_TYPE_FLAG_CTYPE bool | |
| #define ARGPARSE_OPTION_TYPE_INT_CTYPE struct argparse_int | |
| #define ARGPARSE_OPTION_TYPE_STRING_CTYPE char * | |
| #define ARGPARSE_OPTION_TYPE_COUNTER_CTYPE int | |
| #define ARGPARSE_OPTION_TYPE_DOUBLE_CTYPE struct argparse_double | |
| #define ARGPARSE_OPTION_TYPE_HELP_CTYPE bool | |
| #define ARGPARSE_OPTION_TYPE_CTYPE(type) ARGPARSE_EVAL(type##_CTYPE) | |
| // ARGPARSE_OPTION_TO_STRUCT_MEMBER_DEFINITION is used to expand an options list | |
| // into a list of struct member declarations with default initializers. | |
| #define ARGPARSE_OPTION_TO_STRUCT_MEMBER_DEFINITION(type, name, chr, string, description) \ | |
| ARGPARSE_OPTION_TYPE_CTYPE(type) name; | |
| // A small struct used internally to describe a given option to the parser. | |
| // |opt_ptr| is the address of the option's value in the final object. | |
| struct argparse_option_layout | |
| { | |
| enum argparse_option_type opt_type; | |
| char opt_char; | |
| const char * opt_long; | |
| const char * opt_description; | |
| }; | |
| // ARGPARSE_OPTION_TO_LAYOUT_LITERAL is used to expand an options list into | |
| // a literal array of struct argparse_option_layout literal values. | |
| // NOTE: This assumes that |options| is already defined in the expanding context!! | |
| #define ARGPARSE_OPTION_TO_LAYOUT_LITERAL(type, name, chr, string, description) \ | |
| { \ | |
| .opt_type = type, \ | |
| .opt_char = chr, \ | |
| .opt_long = string, \ | |
| .opt_description = description, \ | |
| }, | |
| // Add the sentinel help option layout at the hend of |options_list| then | |
| // invoke the result with |param|. | |
| #define ARGPARSE_LIST_APPLY(options_list, param) options_list(param) ARGPARSE_OPTION_HELP(param) | |
| #define ARGPARSE_LIST_TO_LAYOUT_ARRAY_LITERAL(options_list) \ | |
| (const struct argparse_option_layout[]) \ | |
| { \ | |
| ARGPARSE_LIST_APPLY(options_list, ARGPARSE_OPTION_TO_LAYOUT_LITERAL) \ | |
| } | |
| #define ARGPARSE_OPTION_TO_MEMBER_POINTER(type, name, chr, string, description) &(options).name, | |
| #define ARGPARSE_LIST_TO_POINTER_ARRAY(options_list, options) \ | |
| (void * const[]) \ | |
| { \ | |
| ARGPARSE_LIST_APPLY(options_list, ARGPARSE_OPTION_TO_MEMBER_POINTER) \ | |
| } | |
| #ifdef __cplusplus | |
| } | |
| #endif | |
| #endif // SRC_GRAPHICS_LIB_COMPUTE_TESTS_COMMON_ARGPARSE_H_ |