1 #include <algorithm>
2 #include <stdexcept>
3
4 #include <unistd.h>
5 #include <getopt.h>
6
7 #include <kms++util/opts.h>
8
9 using namespace std;
10
Option(const string & str,function<void ()> func)11 Option::Option(const string& str, function<void()> func)
12 : m_void_func(func)
13 {
14 parse(str);
15 }
16
Option(const string & str,function<void (const string)> func)17 Option::Option(const string& str, function<void(const string)> func)
18 : m_func(func)
19 {
20 parse(str);
21 }
22
parse(const string & str)23 void Option::parse(const string& str)
24 {
25 auto iend = str.end();
26 if (*(iend - 1) == '=') {
27 iend--;
28 m_has_arg = 1;
29 } else if (*(iend - 1) == '?') {
30 iend--;
31 m_has_arg = 2;
32 } else {
33 m_has_arg = 0;
34 }
35
36 auto isplit = find(str.begin(), iend, '|');
37
38 if (isplit != str.begin())
39 m_short = str[0];
40 else
41 m_short = 0;
42
43 if (isplit != iend)
44 m_long = string(isplit + 1, iend);
45 }
46
OptionSet(initializer_list<Option> il)47 OptionSet::OptionSet(initializer_list<Option> il)
48 : m_opts(il)
49 {
50 }
51
parse(int argc,char ** argv)52 void OptionSet::parse(int argc, char** argv)
53 {
54 string shortopts = ":";
55 vector<struct option> longopts;
56
57 for (unsigned opt_idx = 0; opt_idx < m_opts.size(); ++opt_idx) {
58 const Option& o = m_opts[opt_idx];
59
60 if (o.m_short != 0) {
61 shortopts.push_back(o.m_short);
62 if (o.m_has_arg == 1)
63 shortopts.push_back(':');
64 else if (o.m_has_arg == 2)
65 shortopts.append("::");
66 }
67
68 if (!o.m_long.empty()) {
69 struct option copt;
70 copt.name = o.m_long.c_str();
71 copt.has_arg = o.m_has_arg;
72 copt.flag = 0;
73 copt.val = opt_idx + 1000;
74 longopts.push_back(copt);
75 }
76 }
77
78 longopts.push_back(option{});
79
80 while (1) {
81 int long_idx = 0;
82 int c = getopt_long(argc, argv, shortopts.c_str(),
83 longopts.data(), &long_idx);
84 if (c == -1)
85 break;
86
87 if (c == '?')
88 throw std::invalid_argument(string("Unrecognized option ") + argv[optind - 1]);
89
90 if (c == ':') {
91 const Option& o = find_opt(optopt);
92 if (optopt < 256)
93 throw std::invalid_argument(string("Missing argument to -") + o.m_short);
94 else
95 throw std::invalid_argument(string("Missing argument to --") + o.m_long);
96 }
97
98 string sarg = { optarg ?: "" };
99
100 const Option& opt = find_opt(c);
101
102 if (opt.m_func)
103 opt.m_func(sarg);
104 else
105 opt.m_void_func();
106 }
107
108 for (int i = optind; i < argc; ++i)
109 m_params.push_back(argv[i]);
110 }
111
find_opt(int c)112 const Option& OptionSet::find_opt(int c)
113 {
114 if (c < 256)
115 return *find_if(m_opts.begin(), m_opts.end(), [c](const Option& o) { return o.m_short == c; });
116 else
117 return m_opts[c - 1000];
118 }
119