aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/supplemental/util/CMakeLists.txt8
-rw-r--r--src/supplemental/util/options.c124
-rw-r--r--src/supplemental/util/options.h48
-rw-r--r--src/supplemental/util/platform.h6
-rw-r--r--tests/CMakeLists.txt1
-rw-r--r--tests/options.c252
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);
+ });
+
+});