diff --git a/picoquic/config.c b/picoquic/config.c index 4faa54bfd..113d26ec1 100644 --- a/picoquic/config.c +++ b/picoquic/config.c @@ -517,36 +517,76 @@ int picoquic_config_set_option(picoquic_quic_config_t* config, picoquic_option_e return ret; } -int picoquic_config_command_line(int opt, int * p_optind, int argc, char const ** argv, char const* optarg, picoquic_quic_config_t * config) +int picoquic_config_get_option_char_index(int opt) { - int ret = 0; int option_index = -1; - option_param_t params[5]; - int nb_params = 0; - /* Get the parameters */ for (size_t i = 0; i < option_table_size; i++) { if (option_table[i].option_letter == opt) { option_index = (int)i; break; } } + return option_index; +} - if (option_index == -1 || option_table[option_index].nb_params_required > 1) { - /* No options have more than one parameter */ - fprintf(stderr, "Unknown or incorrect option: -%c\n", opt); - ret = -1; +int picoquic_config_get_option_name_index(char const * s, size_t l) +{ + int option_index = -1; + + for (size_t i = 0; i < option_table_size; i++) { + if (strncmp(s, option_table[i].option_name, l) == 0) { + option_index = (int)i; + break; + } } - else { - if (option_table[option_index].nb_params_required > 0) { - params[0].param = optarg; - if (optarg == NULL){ - fprintf(stderr, "option -%c requires %d arguments\n", opt, option_table[option_index].nb_params_required); - ret = -1; - } - else { - params[0].length = strlen(optarg); - nb_params++; + return option_index; +} + +int picoquic_config_get_command_line_option_index(char const* opt_string) +{ + int option_index = -1; + + if (opt_string[0] == '-' && opt_string[1] != 0) { + if (opt_string[2] == 0) { + option_index = picoquic_config_get_option_char_index(opt_string[1]); + } + else if (opt_string[1] == '-' && opt_string[2] != 0) { + char const* opt_name = opt_string + 2; + option_index = picoquic_config_get_option_name_index(opt_name, strlen(opt_name)); + } + } + return option_index; +} + +int picoquic_get_command_line_option_value(int option_index, char const * opt_string, int* p_optind, const char** argv, + int argc, char const* optarg, picoquic_quic_config_t* config) +{ + int ret = 0; + option_param_t params[5]; + int nb_params = 0; + + if (option_table[option_index].nb_params_required > 0) { + params[0].param = optarg; + if (optarg == NULL) { + fprintf(stderr, "option %s requires %d arguments\n", opt_string, option_table[option_index].nb_params_required); + ret = -1; + } + else { + params[0].length = strlen(optarg); + nb_params++; + while (nb_params < option_table[option_index].nb_params_required) { + if (*p_optind + 1 > argc) { + fprintf(stderr, "option %s requires %d arguments\n", opt_string, option_table[option_index].nb_params_required); + ret = -1; + break; + } + else { + params[nb_params].param = argv[*p_optind]; + params[nb_params].length = (int)strlen(argv[*p_optind]); + nb_params++; + *p_optind += 1; + } } } } @@ -558,6 +598,43 @@ int picoquic_config_command_line(int opt, int * p_optind, int argc, char const * return ret; } +int picoquic_config_command_line(int opt, int * p_optind, int argc, char const ** argv, char const* optarg, picoquic_quic_config_t * config) +{ + int ret = 0; + int option_index = -1; + char opt_string[3] = { '-', 0, 0 }; + + opt_string[1] = (char)opt; + option_index = picoquic_config_get_option_char_index(opt); + + if (option_index == -1) { + fprintf(stderr, "Unknown option: -%c\n", opt); + ret = -1; + } + else { + ret = picoquic_get_command_line_option_value(option_index, opt_string, p_optind, + argv, argc, optarg, config); + } + return ret; +} + +int picoquic_config_command_line_ex(char const * opt_string, int* p_optind, int argc, char const** argv, char const* optarg, picoquic_quic_config_t* config) +{ + int ret = 0; + int option_index = -1; + + option_index = picoquic_config_get_command_line_option_index(opt_string); + + if (option_index == -1) { + fprintf(stderr, "Unknown option: %s\n", opt_string); + } + else { + ret = picoquic_get_command_line_option_value(option_index, opt_string, p_optind, + argv, argc, optarg, config); + } + return ret; +} + #if 0 /* Reading parameters from a file instead of the command line. * Apparently never used, also not tested. @@ -637,7 +714,6 @@ int picoquic_config_file(char const* file_name, picoquic_quic_config_t* config) return ret; } - #endif /* Create a QUIC Context based on configuration data. diff --git a/picoquic/picoquic_config.h b/picoquic/picoquic_config.h index 54dc58cd8..a5fe09d85 100644 --- a/picoquic/picoquic_config.h +++ b/picoquic/picoquic_config.h @@ -140,7 +140,15 @@ void picoquic_config_usage_file(FILE* F); void picoquic_config_usage(); int picoquic_config_set_option(picoquic_quic_config_t* config, picoquic_option_enum_t option_num, const char* opt_val); +/* picoquic_config_command_line: +* parse options, with the restriction that all options must be identified by a single character, as in '-x' + */ int picoquic_config_command_line(int opt, int* p_optind, int argc, char const** argv, char const* optarg, picoquic_quic_config_t* config); +/* picoquic_config_command_line: +* parse options, allowing both single character options (e.g. -X) +* and named option (e.g. --disable_block) + */ +int picoquic_config_command_line_ex(char const* opt_string, int* p_optind, int argc, char const** argv, char const* optarg, picoquic_quic_config_t* config); #if 0 /* It does not seem anyone uses this, and it is not tested */ diff --git a/picoquictest/config_test.c b/picoquictest/config_test.c index d98e66988..c75214545 100644 --- a/picoquictest/config_test.c +++ b/picoquictest/config_test.c @@ -22,6 +22,7 @@ #include #include +#include "picoquic.h" #include "picoquic_internal.h" #include "picoquic_utils.h" #include "picoquic_config.h" @@ -216,6 +217,29 @@ static const char* config_argv2[] = { NULL }; +static const char * config_two[] = { + "--sni", "test.example.com", + "--alpn", "test", + "--outdir", "/data/w_out", + "--root_trust_file", "data/certs/root.pem", + "--cipher_suite", "20", + "--proposed_version", "ff000020", + "--force_zero_share", + "--idle_timeout", "1234567", + "--no_disk", + "--large_client_hello", + "--disable_block", +#ifndef PICOQUIC_WITHOUT_SSLKEYLOG + "--sslkeylog", +#endif + "--cnxid_length", "5", + "--ticket_file", "/data/tickets.bin", + "--token_file", "/data/tokens.bin", + "--version_upgrade", "00000002", + "--cwin_max", "1000000", + NULL +}; + typedef struct st_config_error_test_t { int nb_args; char const* err_args[2]; @@ -433,6 +457,56 @@ static int config_parse_command_line_test(const picoquic_quic_config_t* expected return (ret); } +int config_test_parse_command_line_ex(const picoquic_quic_config_t* expected, const char** argv, int argc) +{ + int ret = 0; + int opt_ind = 0; + picoquic_quic_config_t actual; + + picoquic_config_init(&actual); + + while (opt_ind < argc && ret == 0) { + const char* x = argv[opt_ind]; + const char* optval = NULL; + + if (x == NULL) { + /* could not parse to the end! */ + DBG_PRINTF("Unexpected stop after %d arguments, expected %d", opt_ind, argc); + ret = -1; + break; + } + else if (x[0] != '-' || x[1] == 0 || + (x[2] != 0 && x[1] != '-')) { + /* could not parse to the end! */ + DBG_PRINTF("Unexpected argument: %s", x); + ret = -1; + break; + } + opt_ind++; + if (opt_ind < argc) { + optval = argv[opt_ind]; + if (optval[0] == '-') { + optval = NULL; + } + else { + opt_ind++; + } + } + ret = picoquic_config_command_line_ex(x, &opt_ind, argc, argv, optval, &actual); + if (ret != 0) { + DBG_PRINTF("Could not parse opt %s", x); + } + } + + if (ret == 0) { + ret = config_test_compare(expected, &actual); + } + + picoquic_config_clear(&actual); + + return (ret); +} + int config_set_option_test_one() { int ret = 0; @@ -465,18 +539,18 @@ int config_option_test() if (ret != 0) { DBG_PRINTF("First config option test returns %d", ret); } - - if (ret == 0){ + if (ret == 0) { ret = config_parse_command_line_test(¶m2, config_argv2, (int)(sizeof(config_argv2) / sizeof(char const*)) - 1); + if (ret != 0) { DBG_PRINTF("Second config option test returns %d", ret); } } if (ret == 0) { - ret = config_set_option_test_one(); + ret = config_test_parse_command_line_ex(¶m2, config_two, (int)(sizeof(config_two) / sizeof(char const*)) - 1); if (ret != 0) { - DBG_PRINTF("Config set option test returns %d", ret); + DBG_PRINTF("Two dash config option test returns %d", ret); } }