libkmstest: add Option & OptionSet
authorTomi Valkeinen <tomi.valkeinen@ti.com>
Sun, 25 Oct 2015 19:10:14 +0000 (21:10 +0200)
committerTomi Valkeinen <tomi.valkeinen@ti.com>
Thu, 26 Nov 2015 20:45:18 +0000 (22:45 +0200)
libkmstest/opts.cpp [new file with mode: 0644]
libkmstest/opts.h [new file with mode: 0644]

diff --git a/libkmstest/opts.cpp b/libkmstest/opts.cpp
new file mode 100644 (file)
index 0000000..4ea31f8
--- /dev/null
@@ -0,0 +1,117 @@
+#include <algorithm>
+
+#include <unistd.h>
+#include <getopt.h>
+
+#include "opts.h"
+
+using namespace std;
+
+Option::Option(const string& str, function<void()> func)
+       : m_void_func(func)
+{
+       parse(str);
+}
+
+Option::Option(const string& str, function<void(const string)> func)
+       : m_func(func)
+{
+       parse(str);
+}
+
+void Option::parse(const string& str)
+{
+       auto iend = str.end();
+       if (*(iend - 1) == '=') {
+               iend--;
+               m_has_arg = 1;
+       } else if (*(iend - 1) == '?') {
+               iend--;
+               m_has_arg = 2;
+       } else {
+               m_has_arg = 0;
+       }
+
+       auto isplit = find(str.begin(), iend, '|');
+
+       if (isplit != str.begin())
+               m_short = str[0];
+       else
+               m_short = 0;
+
+       if (isplit != iend)
+               m_long = string(isplit + 1, iend);
+}
+
+OptionSet::OptionSet(initializer_list<Option> il)
+       : m_opts(il)
+{
+}
+
+void OptionSet::parse(int argc, char** argv)
+{
+       string shortopts = ":";
+       vector<struct option> longopts;
+
+       for (unsigned opt_idx = 0; opt_idx < m_opts.size(); ++opt_idx) {
+               const Option& o = m_opts[opt_idx];
+
+               if (o.m_short != 0) {
+                       shortopts.push_back(o.m_short);
+                       if (o.m_has_arg == 1)
+                               shortopts.push_back(':');
+                       else if (o.m_has_arg == 2)
+                               shortopts.append("::");
+               }
+
+               if (!o.m_long.empty()) {
+                       struct option copt;
+                       copt.name = o.m_long.c_str();
+                       copt.has_arg = o.m_has_arg;
+                       copt.flag = 0;
+                       copt.val = opt_idx + 1000;
+                       longopts.push_back(copt);
+               }
+       }
+
+       longopts.push_back(option {});
+
+       while (1) {
+               int long_idx = 0;
+               int c = getopt_long(argc, argv, shortopts.c_str(),
+                                   longopts.data(), &long_idx);
+               if (c == -1)
+                       break;
+
+               if (c == '?')
+                       throw std::invalid_argument(string("Unrecognized option ") + argv[optind - 1]);
+
+               if (c == ':') {
+                       const Option& o = find_opt(optopt);
+                       if (optopt < 256)
+                               throw std::invalid_argument(string("Missing argument to -") + o.m_short);
+                       else
+                               throw std::invalid_argument(string("Missing argument to --") + o.m_long);
+               }
+
+               string sarg = { optarg ?: "" };
+
+               const Option& opt = find_opt(c);
+
+               if (opt.m_func)
+                       opt.m_func(sarg);
+               else
+                       opt.m_void_func();
+       }
+
+       for (int i = optind; i < argc; ++i)
+               m_params.push_back(argv[i]);
+}
+
+const Option& OptionSet::find_opt(int c)
+{
+       if (c < 256)
+               return *find_if(m_opts.begin(), m_opts.end(), [c](const Option& o) { return o.m_short == c; });
+       else
+               return m_opts[c - 1000];
+}
diff --git a/libkmstest/opts.h b/libkmstest/opts.h
new file mode 100644 (file)
index 0000000..1b0fd22
--- /dev/null
@@ -0,0 +1,38 @@
+#pragma once
+
+#include <string>
+#include <vector>
+#include <functional>
+
+class Option
+{
+       friend class OptionSet;
+public:
+       Option(const std::string& str, std::function<void()> func);
+       Option(const std::string& str, std::function<void(const std::string)> func);
+
+private:
+       void parse(const std::string& str);
+
+       char m_short;
+       std::string m_long;
+       int m_has_arg;
+       std::function<void()> m_void_func;
+       std::function<void(const std::string)> m_func;
+};
+
+class OptionSet
+{
+public:
+       OptionSet(std::initializer_list<Option> il);
+
+       void parse(int argc, char** argv);
+
+       const std::vector<std::string> params() const { return m_params; }
+
+private:
+       const Option& find_opt(int c);
+
+       const std::vector<Option> m_opts;
+       std::vector<std::string> m_params;
+};