diff options
| -rw-r--r-- | src/supplemental/util/CMakeLists.txt | 8 | ||||
| -rw-r--r-- | src/supplemental/util/options.c | 124 | ||||
| -rw-r--r-- | src/supplemental/util/options.h | 48 | ||||
| -rw-r--r-- | src/supplemental/util/platform.h | 6 | ||||
| -rw-r--r-- | tests/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | tests/options.c | 252 |
6 files changed, 432 insertions, 7 deletions
diff --git a/src/supplemental/util/CMakeLists.txt b/src/supplemental/util/CMakeLists.txt index 443473d6..f94e2b45 100644 --- a/src/supplemental/util/CMakeLists.txt +++ b/src/supplemental/util/CMakeLists.txt @@ -8,8 +8,8 @@ # found online at https://opensource.org/licenses/MIT. # -set(SUPP_PLATFORM_SOURCES supplemental/util/platform.c) -set(SUPP_PLATFORM_HEADERS supplemental/util/platform.h) +set(SUPP_UTIL_SOURCES supplemental/util/options.c supplemental/util/platform.c) +set(SUPP_UTIL_HEADERS supplemental/util/options.h supplemental/util/platform.h) -set(NNG_SOURCES ${NNG_SOURCES} ${SUPP_PLATFORM_SOURCES} PARENT_SCOPE) -set(NNG_HEADERS ${NNG_HEADERS} ${SUPP_PLATFORM_HEADERS} PARENT_SCOPE) +set(NNG_SOURCES ${NNG_SOURCES} ${SUPP_UTIL_SOURCES} PARENT_SCOPE) +set(NNG_HEADERS ${NNG_HEADERS} ${SUPP_UTIL_HEADERS} PARENT_SCOPE) diff --git a/src/supplemental/util/options.c b/src/supplemental/util/options.c new file mode 100644 index 00000000..d8f78deb --- /dev/null +++ b/src/supplemental/util/options.c @@ -0,0 +1,124 @@ +// +// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2018 Capitar IT Group BV <info@capitar.com> +// +// This software is supplied under the terms of the MIT License, a +// copy of which should be located in the distribution where this +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +#include <stdlib.h> +#include <string.h> + +#include "core/nng_impl.h" +#include "supplemental/util/options.h" + +// Call with optidx set to 1 to start parsing. +int +nng_opts_parse(int argc, const char **argv, const nng_optspec *opts, int *val, + const char **optarg, int *optidx) +{ + const nng_optspec *opt; + int matches; + bool shortopt; + size_t l; + const char * arg = argv[*optidx]; + int i; + + if ((i = *optidx) >= argc) { + return (-1); + } + + if (arg[0] != '-') { + return (-1); + } + if (arg[1] == '\0') { + *optidx = i + 1; + return (-1); + } + + if ((arg[0] == '-') && (arg[1] == '-')) { + arg += 2; + shortopt = false; + for (l = 0; arg[l] != '\0'; l++) { + if ((arg[l] == '=') || (arg[l] == ':')) { + break; + } + } + } else { + arg++; + shortopt = true; + l = 1; + } + + matches = 0; + opt = NULL; + + for (int x = 0; opts[x].o_val != 0; x++) { + + if (shortopt) { + if (arg[0] == opts[x].o_short) { + matches = 1; + opt = &opts[x]; + break; + } + continue; + } + + if ((opts[x].o_name == NULL) || + (strncmp(arg, opts[x].o_name, l) != 0)) { + continue; + } + matches++; + opt = &opts[x]; + + if (strlen(opts[x].o_name) == l) { + // Perfect match. + matches = 1; + break; + } + } + + switch (matches) { + case 1: + // Exact match + break; + case 0: + // No such option + return (NNG_EINVAL); + break; + default: + // Ambiguous (not match) + return (NNG_EINVAL); + break; + } + + if (!opt->o_arg) { + // No option clustering for short options yet. + if (arg[l] != '\0') { + return (NNG_EINVAL); + } + *val = opt->o_val; + *optidx = i + 1; + return (0); + } + + if (arg[l] != '\0') { + if (shortopt) { + *optarg = arg + l; + } else { + *optarg = arg + l + 1; + } + } else { + i++; + if (i >= argc) { + return (NNG_EINVAL); + } + *optarg = argv[i]; + } + *optidx = ++i; + *val = opt->o_val; + + return (0); +} diff --git a/src/supplemental/util/options.h b/src/supplemental/util/options.h new file mode 100644 index 00000000..02b3a9d4 --- /dev/null +++ b/src/supplemental/util/options.h @@ -0,0 +1,48 @@ +// +// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2018 Capitar IT Group BV <info@capitar.com> +// +// This software is supplied under the terms of the MIT License, a +// copy of which should be located in the distribution where this +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +#ifndef NNG_SUPPLEMENTAL_UTIL_OPTIONS_H +#define NNG_SUPPLEMENTAL_UTIL_OPTIONS_H + +#include <stdbool.h> + +#ifdef __cplusplus +extern "C" { +#endif + +// This is a relatively simple "options parsing" library, used to +// parse command line options. We would use getopt(3), but there are +// two problems with getopt(3). First, it isn't available on all +// platforms (especially Win32), and second, it doesn't support long +// options. We *exclusively* support long options. POSIX style +// short option clustering is *NOT* supported. + +struct nng_optspec { + const char *o_name; // Long style name (may be NULL for short only) + int o_short; // Short option (no clustering!) + int o_val; // Value stored on a good parse (>0) + bool o_arg; // Option takes an argument if true +}; + +typedef struct nng_optspec nng_optspec; + +// Call with *optidx set to 1 to start parsing for a standard program. +// The val will store the value of the matched "o_val", optarg will be +// set to match the option string, and optidx will be increment appropriately. +// Returns -1 when the end of options is reached, 0 on success, or +// NNG_EINVAL if the option parse is invalid for any reason. +NNG_DECL int nng_opts_parse(int argc, const char **argv, + const nng_optspec *opts, int *val, const char **optarg, int *optidx); + +#ifdef __cplusplus +} +#endif + +#endif // NNG_SUPPLEMENTAL_UTIL_OPTIONS_H
\ No newline at end of file diff --git a/src/supplemental/util/platform.h b/src/supplemental/util/platform.h index 1383fe15..fd287713 100644 --- a/src/supplemental/util/platform.h +++ b/src/supplemental/util/platform.h @@ -20,13 +20,13 @@ // APIs or Windows APIs, then by all means please feel free to simply // ignore this. +#include <stddef.h> +#include <stdint.h> + #ifdef __cplusplus extern "C" { #endif -#include <stddef.h> -#include <stdint.h> - // nng_time represents an absolute time since some arbitrary point in the // past, measured in milliseconds. The values are always positive. typedef uint64_t nng_time; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6ae41ffe..51903572 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -145,6 +145,7 @@ add_nng_test(ipc 5 NNG_TRANSPORT_IPC) add_nng_test(list 5 ON) add_nng_test(message 5 ON) add_nng_test(multistress 60 ON) +add_nng_test(options 5 ON) add_nng_test(platform 5 ON) add_nng_test(pollfd 5 ON) add_nng_test(reconnect 5 ON) diff --git a/tests/options.c b/tests/options.c new file mode 100644 index 00000000..166af37c --- /dev/null +++ b/tests/options.c @@ -0,0 +1,252 @@ +// +// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2018 Capitar IT Group BV <info@capitar.com> +// +// This software is supplied under the terms of the MIT License, a +// copy of which should be located in the distribution where this +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +#include <string.h> + +#include "convey.h" + +#include "nng.h" + +#include "supplemental/util/options.h" + +static nng_optspec case1[] = { + // clang-format off + { "flag", 'f', 1, false }, + { "longflag", 0, 2, false }, + { "value", 'v', 3, true }, + { NULL, 'b', 4, false }, + { NULL, 0, 0, false }, + // clang-format on +}; + +TestMain("Option Parsing", { + + Convey("Simple works", { + + int opti = 1; + const char *av[6]; + int ac = 5; + int v; + const char *a = NULL; + + av[0] = "program"; + av[1] = "-f"; + av[2] = "-v"; + av[3] = "123"; + av[4] = "456"; + So(nng_opts_parse(ac, av, case1, &v, &a, &opti) == 0); + So(v == 1); + So(a == NULL); + So(opti == 2); + So(nng_opts_parse(ac, av, case1, &v, &a, &opti) == 0); + So(opti == 4); + So(v == 3); + So(strcmp(a, "123") == 0); + So(nng_opts_parse(ac, av, case1, &v, &a, &opti) == -1); + So(opti == 4); + So(strcmp(av[opti], "456") == 0); + }); + + Convey("Long works", { + + int opti = 1; + const char *av[6]; + int ac = 5; + int v; + const char *a = NULL; + + av[0] = "program"; + av[1] = "--flag"; + av[2] = "--value"; + av[3] = "123"; + av[4] = "456"; + So(nng_opts_parse(ac, av, case1, &v, &a, &opti) == 0); + So(v == 1); + So(a == NULL); + So(opti == 2); + So(nng_opts_parse(ac, av, case1, &v, &a, &opti) == 0); + So(opti == 4); + So(v == 3); + So(strcmp(a, "123") == 0); + So(nng_opts_parse(ac, av, case1, &v, &a, &opti) == -1); + So(opti == 4); + So(strcmp(av[opti], "456") == 0); + }); + + Convey("Attached short works", { + + int opti = 1; + const char *av[3]; + int ac = 3; + int v; + const char *a = NULL; + + av[0] = "program"; + av[1] = "-v123"; + av[2] = "456"; + So(nng_opts_parse(ac, av, case1, &v, &a, &opti) == 0); + So(opti == 2); + So(v == 3); + So(strcmp(a, "123") == 0); + So(nng_opts_parse(ac, av, case1, &v, &a, &opti) == -1); + So(opti == 2); + So(strcmp(av[opti], "456") == 0); + }); + + Convey("Attached long (=) works", { + + int opti = 1; + const char *av[3]; + int ac = 3; + int v; + const char *a = NULL; + + av[0] = "program"; + av[1] = "--value=123"; + av[2] = "456"; + So(nng_opts_parse(ac, av, case1, &v, &a, &opti) == 0); + So(opti == 2); + So(v == 3); + So(strcmp(a, "123") == 0); + So(nng_opts_parse(ac, av, case1, &v, &a, &opti) == -1); + So(opti == 2); + So(strcmp(av[opti], "456") == 0); + }); + + Convey("Attached long (:) works", { + + int opti = 1; + const char *av[3]; + int ac = 3; + int v; + const char *a = NULL; + + av[0] = "program"; + av[1] = "--value:123"; + av[2] = "456"; + So(nng_opts_parse(ac, av, case1, &v, &a, &opti) == 0); + So(opti == 2); + So(v == 3); + So(strcmp(a, "123") == 0); + So(nng_opts_parse(ac, av, case1, &v, &a, &opti) == -1); + So(opti == 2); + So(strcmp(av[opti], "456") == 0); + }); + + Convey("Negative bad short works", { + + int opti = 1; + const char *av[3]; + int ac = 3; + int v; + const char *a = NULL; + + av[0] = "program"; + av[1] = "-Z"; + av[2] = "456"; + So(nng_opts_parse(ac, av, case1, &v, &a, &opti) == NNG_EINVAL); + So(opti == 1); + }); + + Convey("Negative bad long works", { + + int opti = 1; + const char *av[3]; + int ac = 3; + int v; + const char *a = NULL; + + av[0] = "program"; + av[1] = "--something"; + av[2] = "456"; + So(nng_opts_parse(ac, av, case1, &v, &a, &opti) == NNG_EINVAL); + So(opti == 1); + }); + + Convey("Separator flag works", { + int opti = 1; + const char *av[5]; + int ac = 5; + int v; + const char *a = NULL; + + av[0] = "program"; + av[1] = "-f"; + av[2] = "-"; + av[3] = "-v"; + av[4] = "456"; + So(nng_opts_parse(ac, av, case1, &v, &a, &opti) == 0); + So(v == 1); + So(opti == 2); + So(nng_opts_parse(ac, av, case1, &v, &a, &opti) == -1); + So(opti == 3); + }); + + Convey("No options works", { + int opti = 1; + const char *av[1]; + int ac = 1; + int v; + const char *a = NULL; + + av[0] = "program"; + So(nng_opts_parse(ac, av, case1, &v, &a, &opti) == -1); + }); + + Convey("No options (but arguments) works", { + int opti = 1; + const char *av[2]; + int ac = 2; + int v; + const char *a = NULL; + + av[0] = "program"; + av[1] = "123"; + So(nng_opts_parse(ac, av, case1, &v, &a, &opti) == -1); + So(opti == 1); + }); + Convey("Mixed long and short works", { + + int opti = 1; + const char *av[7]; + int ac = 7; + int v; + const char *a = NULL; + + av[0] = "program"; + av[1] = "--value=123"; + av[2] = "-f"; + av[3] = "--longflag"; + av[4] = "-b"; + av[5] = "-vxyz"; + av[6] = "456"; + So(nng_opts_parse(ac, av, case1, &v, &a, &opti) == 0); + So(opti == 2); + So(v == 3); + So(strcmp(a, "123") == 0); + So(nng_opts_parse(ac, av, case1, &v, &a, &opti) == 0); + So(opti == 3); + So(v == 1); + So(nng_opts_parse(ac, av, case1, &v, &a, &opti) == 0); + So(opti == 4); + So(v == 2); + So(nng_opts_parse(ac, av, case1, &v, &a, &opti) == 0); + So(opti == 5); + So(v == 4); + So(nng_opts_parse(ac, av, case1, &v, &a, &opti) == 0); + So(opti == 6); + So(v == 3); + So(strcmp(a, "xyz") == 0); + So(strcmp(av[opti], "456") == 0); + So(nng_opts_parse(ac, av, case1, &v, &a, &opti) == -1); + So(opti == 6); + }); + +}); |
