blob: e265c61fb1686c25ac9a848db2ff5f9cb952dde9 [file] [log] [blame]
// 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_