From: Mike D. Lowis Date: Thu, 19 Mar 2015 15:06:42 +0000 (-0400) Subject: Added the ability for the user to provide their own error handler X-Git-Url: https://git.mdlowis.com/?a=commitdiff_plain;h=023412664fcfbe5e81e026c8bf067c63ac061251;p=projs%2Fopts.git Added the ability for the user to provide their own error handler --- diff --git a/source/opts.c b/source/opts.c index db00d7a..02bbf57 100755 --- a/source/opts.c +++ b/source/opts.c @@ -25,27 +25,26 @@ typedef struct { opts_cfg_t* options; } StreamContext_T; -static const char* Program_Name = NULL; -static entry_t* Options = NULL; -static entry_t* Arguments = NULL; - static void opts_parse_short_option( StreamContext_T* ctx ); static void opts_parse_long_option( StreamContext_T* ctx ); static char* opts_parse_optarg(StreamContext_T* ctx, char* opt_name); static void opts_parse_argument( StreamContext_T* ctx ); static void opts_parse_error(const char* msg, char* opt_name); - static opts_cfg_t* opts_get_option_config( opts_cfg_t* opts, OptionType_T typ, char* name ); static char* opts_next_token( StreamContext_T* ctx ); static void opts_consume_ws( StreamContext_T* ctx ); static char opts_next_char( StreamContext_T* ctx ); static char* opts_append_char( char* str, char ch ); static char* strclone(const char* p_old); - static void opts_add_option(char* name, char* tag, char* arg); static void opts_add_argument(char* arg); -void opts_parse( opts_cfg_t* opts, int argc, char** argv ) { +static const char* Program_Name = NULL; +static entry_t* Options = NULL; +static entry_t* Arguments = NULL; +static opts_err_cbfn_t Error_Callback = &opts_parse_error; + +void opts_parse(opts_cfg_t* opts, opts_err_cbfn_t err_cb, int argc, char** argv) { /* Setup the stream */ StreamContext_T ctx; ctx.line_idx = 0; @@ -55,6 +54,10 @@ void opts_parse( opts_cfg_t* opts, int argc, char** argv ) { ctx.options = opts; (void)opts_next_char( &ctx ); /* Loads up the first char */ + /* Record the error handler if one was provided */ + if (NULL != err_cb) + Error_Callback = err_cb; + /* Record the program name */ Program_Name = argv[0]; @@ -114,7 +117,7 @@ static void opts_parse_short_option( StreamContext_T* ctx ) { opts_parse_short_option( ctx ); opts_add_option( opt_name, config->tag, opt_arg ); } else { - opts_parse_error("Unknown Option", opt_name); + Error_Callback("Unknown Option", opt_name); } } @@ -127,14 +130,14 @@ static void opts_parse_long_option( StreamContext_T* ctx ) { opt_arg = opts_parse_optarg( ctx, opt_name ); opts_add_option( opt_name, config->tag, opt_arg ); } else { - opts_parse_error("Unknown Option", opt_name); + Error_Callback("Unknown Option", opt_name); } } static char* opts_parse_optarg(StreamContext_T* ctx, char* opt_name) { opts_consume_ws( ctx ); if (('-' == ctx->current) || (EOF == ctx->current)) - opts_parse_error("Expected an argument, none received", opt_name); + Error_Callback("Expected an argument, none received", opt_name); return opts_next_token( ctx ); } @@ -350,3 +353,4 @@ void opts_print_help(FILE* ofile, opts_cfg_t* opts) { free(buffer); exit(1); } + diff --git a/source/opts.h b/source/opts.h index 461eb0c..a6c4166 100755 --- a/source/opts.h +++ b/source/opts.h @@ -66,6 +66,8 @@ typedef struct { char* desc; } opts_cfg_t; +typedef void (*opts_err_cbfn_t)(const char* msg, char* opt_name); + /** * Parse the command line options using the provided option definition list. * @@ -73,7 +75,7 @@ typedef struct { * @param argc The number of arguments in the vector * @param argv The vector of command line arguments */ -void opts_parse(opts_cfg_t* opts, int argc, char** argv); +void opts_parse(opts_cfg_t* opts, opts_err_cbfn_t err_cb, int argc, char** argv); /** * Resets the global state back to defaults. This includes freeing any diff --git a/tests/test_opts.c b/tests/test_opts.c index 3be93f6..e8cb461 100755 --- a/tests/test_opts.c +++ b/tests/test_opts.c @@ -39,6 +39,13 @@ void exit(int code) longjmp( Exit_Point, code); } + +static void User_Error_Cb(const char* msg, char* opt_name) { + (void)msg; + (void)opt_name; + exit(1); +} + void test_setup(void) {} //----------------------------------------------------------------------------- @@ -53,7 +60,7 @@ TEST_SUITE(Opts) { char* args[] = { "prog", (char*)"--foo" }; CHECK_DOES_NOT_EXIT() { - opts_parse( Options_Config, 2, args ); + opts_parse( Options_Config, NULL, 2, args ); CHECK(opts_is_set("foo", NULL)); } opts_reset(); @@ -65,7 +72,7 @@ TEST_SUITE(Opts) { CHECK_DOES_NOT_EXIT() { - opts_parse( Options_Config, 3, args ); + opts_parse( Options_Config, NULL, 3, args ); CHECK(opts_is_set("bar", NULL)); CHECK(0 == strcmp("baz", opts_get_value("bar", NULL))); } @@ -77,7 +84,7 @@ TEST_SUITE(Opts) { char* args[] = { "prog", (char*)"--bar=baz" }; CHECK_DOES_NOT_EXIT() { - opts_parse( Options_Config, 2, args ); + opts_parse( Options_Config, NULL, 2, args ); CHECK(opts_is_set("bar", NULL)); CHECK(0 == strcmp("baz", opts_get_value("bar", NULL))); } @@ -91,7 +98,7 @@ TEST_SUITE(Opts) { exit_code = setjmp( Exit_Point ); if( 0 == exit_code ) { - opts_parse( Options_Config, 2, args ); + opts_parse( Options_Config, NULL, 2, args ); // If we fail to call exit then this breaks our test CHECK( false ); } else { @@ -107,7 +114,7 @@ TEST_SUITE(Opts) { exit_code = setjmp( Exit_Point ); if( 0 == exit_code ) { - opts_parse( Options_Config, 2, args ); + opts_parse( Options_Config, NULL, 2, args ); // If we fail to call exit then this breaks our test CHECK( false ); } else { @@ -123,7 +130,7 @@ TEST_SUITE(Opts) { exit_code = setjmp( Exit_Point ); if( 0 == exit_code ) { - opts_parse( Options_Config, 2, args ); + opts_parse( Options_Config, NULL, 2, args ); // If we fail to call exit then this breaks our test CHECK( false ); } else { @@ -137,7 +144,7 @@ TEST_SUITE(Opts) { char* args[] = { "prog", (char*)"-a" }; CHECK_DOES_NOT_EXIT() { - opts_parse( Options_Config, 2, args ); + opts_parse( Options_Config, NULL, 2, args ); CHECK(opts_is_set("a", NULL)); } opts_reset(); @@ -148,7 +155,7 @@ TEST_SUITE(Opts) { char* args[] = { "prog", (char*)"-ac" }; CHECK_DOES_NOT_EXIT() { - opts_parse( Options_Config, 2, args ); + opts_parse( Options_Config, NULL, 2, args ); CHECK(opts_is_set("a", NULL)); CHECK(opts_is_set("c", NULL)); } @@ -160,7 +167,7 @@ TEST_SUITE(Opts) { char* args[] = { "prog", (char*)"-bbaz" }; CHECK_DOES_NOT_EXIT() { - opts_parse( Options_Config, 2, args ); + opts_parse( Options_Config, NULL, 2, args ); CHECK(opts_is_set("b", NULL)); CHECK(0 == strcmp("baz", opts_get_value("b", NULL))); } @@ -172,7 +179,7 @@ TEST_SUITE(Opts) { char* args[] = { "prog", (char*)"-b", "baz" }; CHECK_DOES_NOT_EXIT() { - opts_parse( Options_Config, 3, args ); + opts_parse( Options_Config, NULL, 3, args ); CHECK(opts_is_set("b", NULL)); CHECK(0 == strcmp("baz", opts_get_value("b", NULL))); } @@ -184,7 +191,7 @@ TEST_SUITE(Opts) { char* args[] = { "prog", (char*)"-b=baz" }; CHECK_DOES_NOT_EXIT() { - opts_parse( Options_Config, 2, args ); + opts_parse( Options_Config, NULL, 2, args ); CHECK(opts_is_set("b", NULL)); CHECK(0 == strcmp("baz", opts_get_value("b", NULL))); } @@ -199,7 +206,7 @@ TEST_SUITE(Opts) { exit_code = setjmp( Exit_Point ); if( 0 == exit_code ) { - opts_parse( Options_Config, 2, args ); + opts_parse( Options_Config, NULL, 2, args ); // If we fail to call exit then this breaks our test CHECK( false ); } else { @@ -215,7 +222,7 @@ TEST_SUITE(Opts) { exit_code = setjmp( Exit_Point ); if( 0 == exit_code ) { - opts_parse( Options_Config, 2, args ); + opts_parse( Options_Config, NULL, 2, args ); // If we fail to call exit then this breaks our test CHECK( false ); } else { @@ -231,7 +238,7 @@ TEST_SUITE(Opts) { exit_code = setjmp( Exit_Point ); if( 0 == exit_code ) { - opts_parse( Options_Config, 2, args ); + opts_parse( Options_Config, NULL, 2, args ); // If we fail to call exit then this breaks our test CHECK( false ); } else { @@ -246,7 +253,7 @@ TEST_SUITE(Opts) { CHECK_DOES_NOT_EXIT() { - opts_parse( Options_Config, 2, args ); + opts_parse( Options_Config, NULL, 2, args ); const char** args = opts_arguments(); CHECK(0 == strcmp("baz1", args[0])); CHECK(NULL == args[1]); @@ -261,7 +268,7 @@ TEST_SUITE(Opts) { CHECK_DOES_NOT_EXIT() { - opts_parse( Options_Config, 3, args ); + opts_parse( Options_Config, NULL, 3, args ); const char** args = opts_arguments(); CHECK(0 == strcmp("baz2", args[0])); CHECK(0 == strcmp("baz1", args[1])); @@ -271,12 +278,28 @@ TEST_SUITE(Opts) { opts_reset(); } + TEST(Verify_ParseOptions_Calls_the_user_defined_error_handler_if_provided) + { + int exit_code = 0; + char* args[] = { "prog", (char*)"-b" }; + + exit_code = setjmp( Exit_Point ); + if( 0 == exit_code ) { + opts_parse( Options_Config, User_Error_Cb, 2, args ); + // If we fail to call exit then this breaks our test + CHECK( false ); + } else { + CHECK( 2 == exit_code ); + } + opts_reset(); + } + TEST(Verify_Select_returns_the_matching_option_by_name) { char* args[] = { "prog", "-a", "--foo", "-c" }; CHECK_DOES_NOT_EXIT() { - opts_parse( Options_Config, 4, args ); + opts_parse( Options_Config, NULL, 4, args ); const char** opts = opts_select("c", NULL); CHECK(0 == strcmp("c", opts[0])); CHECK(NULL == opts[1]); @@ -290,7 +313,7 @@ TEST_SUITE(Opts) { char* args[] = { "prog", "-a", "--foo", "-c" }; CHECK_DOES_NOT_EXIT() { - opts_parse( Options_Config, 4, args ); + opts_parse( Options_Config, NULL, 4, args ); const char** opts = opts_select(NULL, "test_c"); CHECK(0 == strcmp("c", opts[0])); CHECK(NULL == opts[1]); @@ -304,7 +327,7 @@ TEST_SUITE(Opts) { char* args[] = { "prog", "-a", "--foo", "-c", "--baz" }; CHECK_DOES_NOT_EXIT() { - opts_parse( Options_Config, 5, args ); + opts_parse( Options_Config, NULL, 5, args ); const char** opts = opts_select("baz", "opttag"); CHECK(0 == strcmp("baz", opts[0])); CHECK(NULL == opts[1]); @@ -318,7 +341,7 @@ TEST_SUITE(Opts) { char* args[] = { "prog", "-a", "--foo", "-c", "--baz" }; CHECK_DOES_NOT_EXIT() { - opts_parse( Options_Config, 5, args ); + opts_parse( Options_Config, NULL, 5, args ); const char** opts = opts_select(NULL, "opttag"); CHECK(0 == strcmp("baz", opts[0])); CHECK(0 == strcmp("foo", opts[1]));