From f47b42e519d1cb44e9e6f6a35934e486346828f6 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 15 Mar 2017 10:16:42 +0100 Subject: [PATCH] fix test case, implement base32 argument parser logic --- src/include/gnunet_getopt_lib.h | 46 +++++++++++++- src/util/getopt.c | 56 ++++++++++------- src/util/getopt_helpers.c | 105 +++++++++++++++++++++++++++++++- src/util/test_getopt.c | 30 ++++++--- 4 files changed, 205 insertions(+), 32 deletions(-) diff --git a/src/include/gnunet_getopt_lib.h b/src/include/gnunet_getopt_lib.h index cd546905e..0acf15679 100644 --- a/src/include/gnunet_getopt_lib.h +++ b/src/include/gnunet_getopt_lib.h @@ -135,6 +135,12 @@ struct GNUNET_GETOPT_CommandLineOption */ GNUNET_GETOPT_CommandLineOptionProcessor processor; + /** + * Function to call on @e scls to clean up after processing all + * the arguments. Can be NULL. + */ + void (*cleaner)(void *cls); + /** * Specific closure to pass to the processor. */ @@ -206,6 +212,42 @@ GNUNET_GETOPT_OPTION_FILENAME (char shortName, char **str); +/** + * Allow user to specify a binary value using Crockford + * Base32 encoding. + * + * @param shortName short name of the option + * @param name long name of the option + * @param argumentHelp help text for the option argument + * @param description long help text for the option + * @param[out] val binary value decoded from Crockford Base32-encoded argument + * @param val_size size of @a val in bytes + */ +struct GNUNET_GETOPT_CommandLineOption +GNUNET_GETOPT_OPTION_SET_BASE32_FIXED_SIZE (char shortName, + const char *name, + const char *argumentHelp, + const char *description, + void *val, + size_t val_size); + + +/** + * Allow user to specify a binary value using Crockford + * Base32 encoding where the size of the binary value is + * automatically determined from its type. + * + * @param shortName short name of the option + * @param name long name of the option + * @param argumentHelp help text for the option argument + * @param description long help text for the option + * @param[out] val binary value decoded from Crockford Base32-encoded argument; + * size is determined by type (sizeof (*val)). + */ +#define GNUNET_GETOPT_OPTION_SET_BASE32_AUTO(shortName,name,argumentHelp,description,val) \ + GNUNET_GETOPT_OPTION_SET_BASE32_FIXED_SIZE(shortName,name,argumentHelp,description,val,sizeof(*val)) + + /** * Allow user to specify a flag (which internally means setting * an integer to 1/#GNUNET_YES/#GNUNET_OK. @@ -307,7 +349,7 @@ GNUNET_GETOPT_OPTION_LOGLEVEL (char **level); * @param[out] level set to the verbosity level */ struct GNUNET_GETOPT_CommandLineOption -GNUNET_GETOPT_OPTION_VERBOSE (int *level); +GNUNET_GETOPT_OPTION_VERBOSE (unsigned int *level); /** @@ -332,7 +374,7 @@ GNUNET_GETOPT_OPTION_CFG_FILE (char **fn); * Marker for the end of the list of options. */ #define GNUNET_GETOPT_OPTION_END \ - { '\0', NULL, NULL, NULL, 0, NULL, NULL } + { '\0', NULL, NULL, NULL, 0, NULL, NULL, NULL } /** diff --git a/src/util/getopt.c b/src/util/getopt.c index ff62dba9b..85f67500c 100644 --- a/src/util/getopt.c +++ b/src/util/getopt.c @@ -26,7 +26,7 @@ USA. This code was heavily modified for GNUnet. -Copyright Copyright (C) 2006 Christian Grothoff +Copyright Copyright (C) 2006, 2017 Christian Grothoff */ /** @@ -845,9 +845,13 @@ GN_getopt_internal (int argc, char *const *argv, const char *optstring, } } + static int -GNgetopt_long (int argc, char *const *argv, const char *options, - const struct GNoption *long_options, int *opt_index) +GNgetopt_long (int argc, + char *const *argv, + const char *options, + const struct GNoption *long_options, + int *opt_index) { return GN_getopt_internal (argc, argv, options, long_options, opt_index, 0); } @@ -867,12 +871,12 @@ GNgetopt_long (int argc, char *const *argv, const char *options, int GNUNET_GETOPT_run (const char *binaryOptions, const struct GNUNET_GETOPT_CommandLineOption *allOptions, - unsigned int argc, char *const *argv) + unsigned int argc, + char *const *argv) { struct GNoption *long_options; struct GNUNET_GETOPT_CommandLineProcessorContext clpc; int count; - int i; char *shorts; int spos; int cont; @@ -885,13 +889,13 @@ GNUNET_GETOPT_run (const char *binaryOptions, clpc.allOptions = allOptions; clpc.argv = argv; clpc.argc = argc; - count = 0; - while (allOptions[count].name != NULL) - count++; - long_options = GNUNET_malloc (sizeof (struct GNoption) * (count + 1)); + for (count = 0; NULL != allOptions[count].name; count++) ; + + long_options = GNUNET_new_array (count + 1, + struct GNoption); shorts = GNUNET_malloc (count * 2 + 1); spos = 0; - for (i = 0; i < count; i++) + for (unsigned i = 0; i < count; i++) { long_options[i].name = allOptions[i].name; long_options[i].has_arg = allOptions[i].require_argument; @@ -907,13 +911,17 @@ GNUNET_GETOPT_run (const char *binaryOptions, long_options[count].val = '\0'; shorts[spos] = '\0'; cont = GNUNET_OK; + /* main getopt loop */ - while (cont == GNUNET_OK) + while (GNUNET_OK == cont) { int option_index = 0; + unsigned int i; - c = GNgetopt_long (argc, argv, shorts, long_options, &option_index); - + c = GNgetopt_long (argc, argv, + shorts, + long_options, + &option_index); if (c == GNUNET_SYSERR) break; /* No more flags to process */ @@ -922,25 +930,31 @@ GNUNET_GETOPT_run (const char *binaryOptions, clpc.currentArgument = GNoptind - 1; if ((char) c == allOptions[i].shortName) { - cont = - allOptions[i].processor (&clpc, allOptions[i].scls, - allOptions[i].name, GNoptarg); + cont = allOptions[i].processor (&clpc, + allOptions[i].scls, + allOptions[i].name, + GNoptarg); break; } } if (i == count) { - FPRINTF (stderr, _("Use %s to get a list of options.\n"), "--help"); + FPRINTF (stderr, + _("Use %s to get a list of options.\n"), + "--help"); cont = GNUNET_SYSERR; } } - GNUNET_free (shorts); GNUNET_free (long_options); - if (cont != GNUNET_OK) - { + + /* call cleaners, if available */ + for (count = 0; NULL != allOptions[count].name; count++) + if (NULL != allOptions[count].cleaner) + allOptions[count].cleaner (allOptions[count].scls); + + if (GNUNET_OK != cont) return cont; - } return GNoptind; } diff --git a/src/util/getopt_helpers.c b/src/util/getopt_helpers.c index 234f5371f..9f6f4c764 100644 --- a/src/util/getopt_helpers.c +++ b/src/util/getopt_helpers.c @@ -266,7 +266,7 @@ GNUNET_GETOPT_OPTION_INCREMENT_VALUE (char shortName, * @param[out] level set to the verbosity level */ struct GNUNET_GETOPT_CommandLineOption -GNUNET_GETOPT_OPTION_VERBOSE (int *level) +GNUNET_GETOPT_OPTION_VERBOSE (unsigned int *level) { struct GNUNET_GETOPT_CommandLineOption clo = { .shortName = 'V', @@ -710,4 +710,107 @@ GNUNET_GETOPT_OPTION_SET_UINT (char shortName, } +/** + * Closure for #set_base32(). + */ +struct Base32Context +{ + /** + * Value to initialize (already allocated) + */ + void *val; + + /** + * Number of bytes expected for @e val. + */ + size_t val_size; +}; + + +/** + * Set an option of type 'unsigned int' from the command line. + * A pointer to this function should be passed as part of the + * 'struct GNUNET_GETOPT_CommandLineOption' array to initialize options + * of this type. It should be followed by a pointer to a value of + * type 'unsigned int'. + * + * @param ctx command line processing context + * @param scls additional closure (will point to the 'unsigned int') + * @param option name of the option + * @param value actual value of the option as a string. + * @return #GNUNET_OK if parsing the value worked + */ +static int +set_base32 (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx, + void *scls, + const char *option, + const char *value) +{ + struct Base32Context *bc = scls; + + if (GNUNET_OK != + GNUNET_STRINGS_string_to_data (value, + strlen (value), + bc->val, + bc->val_size)) + { + fprintf (stderr, + _("Argument `%s' malformed. Expected base32 (Crockford) encoded value.\n"), + option); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Helper function to clean up after + * #GNUNET_GETOPT_OPTION_SET_BASE32_FIXED_SIZE. + * + * @param cls value to GNUNET_free() + */ +static void +free_bc (void *cls) +{ + GNUNET_free (cls); +} + + +/** + * Allow user to specify a binary value using Crockford + * Base32 encoding. + * + * @param shortName short name of the option + * @param name long name of the option + * @param argumentHelp help text for the option argument + * @param description long help text for the option + * @param[out] val binary value decoded from Crockford Base32-encoded argument + * @param val_size size of @a val in bytes + */ +struct GNUNET_GETOPT_CommandLineOption +GNUNET_GETOPT_OPTION_SET_BASE32_FIXED_SIZE (char shortName, + const char *name, + const char *argumentHelp, + const char *description, + void *val, + size_t val_size) +{ + struct Base32Context *bc = GNUNET_new (struct Base32Context); + struct GNUNET_GETOPT_CommandLineOption clo = { + .shortName = shortName, + .name = name, + .argumentHelp = argumentHelp, + .description = description, + .require_argument = 1, + .processor = &set_base32, + .cleaner = &free_bc, + .scls = (void *) bc + }; + + bc->val = val; + bc->val_size = val_size; + return clo; +} + + /* end of getopt_helpers.c */ diff --git a/src/util/test_getopt.c b/src/util/test_getopt.c index 8e578640d..faa6a07a1 100644 --- a/src/util/test_getopt.c +++ b/src/util/test_getopt.c @@ -170,21 +170,35 @@ testFlagNum () unsigned long long lnum = 0; const struct GNUNET_GETOPT_CommandLineOption logoptionlist[] = { - {'f', "--flag", NULL, "helptext", 0, &GNUNET_GETOPT_set_one, - (void *) &flag}, - {'n', "--num", "ARG", "helptext", 1, &GNUNET_GETOPT_set_uint, - (void *) &num}, - {'N', "--lnum", "ARG", "helptext", 1, &GNUNET_GETOPT_set_ulong, - (void *) &lnum}, + GNUNET_GETOPT_OPTION_SET_ONE ('f', + "--flag", + "helptext", + &flag), + GNUNET_GETOPT_OPTION_SET_UINT ('n', + "--num", + "ARG", + "helptext", + &num), + GNUNET_GETOPT_OPTION_SET_ULONG ('N', + "--lnum", + "ARG", + "helptext", + &lnum), GNUNET_GETOPT_OPTION_END }; - if (6 != GNUNET_GETOPT_run ("test_getopt", logoptionlist, 6, myargv)) + if (6 != + GNUNET_GETOPT_run ("test_getopt", + logoptionlist, + 6, + myargv)) { GNUNET_break (0); return 1; } - if ((1 != flag) || (42 != num) || (42 != lnum)) + if ( (1 != flag) || + (42 != num) || + (42 != lnum)) { GNUNET_break (0); return 1; -- 2.25.1