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