1 /*
2 * Copyright (c) 2018-2021, The OSKAR Developers.
3 * See the LICENSE file at the top-level directory of this distribution.
4 */
5
6 #include "settings/extern/ezOptionParser/ezOptionParser.hpp"
7 #include "settings/oskar_option_parser.h"
8 #include <cstdarg>
9 #include <vector>
10 #include <string>
11
12 using namespace std;
13
14 namespace oskar {
15
16 struct OptionParserPrivate : public ez::ezOptionParser
17 {
18 string title, version;
19 vector<string> optional_;
20 vector<string> optionalHelp_;
21 vector<string> required_;
22 vector<string> requiredHelp_;
23 vector<const char*> input_files_;
24 const char* settings_;
25 const char* version_;
26 };
27
OptionParser(const char * title,const char * ver,const char * settings)28 OptionParser::OptionParser(const char* title, const char* ver,
29 const char* settings)
30 {
31 p = new OptionParserPrivate;
32 p->footer =
33 "\n" + string(79, '-') + "\n"
34 "OSKAR (version " + ver + ")\n"
35 "Copyright (c) 2021, The OSKAR Developers.\n"
36 "This program is free and without warranty.\n"
37 "" + string(79, '-') + "\n";
38 set_version(ver, false);
39 set_title(title);
40 set_settings(settings);
41 }
42
~OptionParser()43 OptionParser::~OptionParser()
44 {
45 delete p;
46 }
47
add_example(const char * text)48 void OptionParser::add_example(const char* text)
49 {
50 p->example += " " + string(text) + "\n";
51 }
52
53 // Wrapper to define flags with no arguments
add_flag(const char * flag1,const char * help,bool required,const char * flag2)54 void OptionParser::add_flag(const char* flag1, const char* help,
55 bool required, const char* flag2)
56 {
57 const char* defaults = "";
58 int expectedArgs = 0;
59 char delim = 0;
60 if (flag2)
61 {
62 p->add(defaults, required, expectedArgs, delim,
63 help, flag1, flag2);
64 }
65 else
66 {
67 p->add(defaults, required, expectedArgs, delim, help, flag1);
68 }
69 }
70
71 // Wrapper to define flags with arguments with default values.
add_flag(const char * flag1,const char * help,int expected_args,const char * defaults,bool required,const char * flag2)72 void OptionParser::add_flag(const char* flag1, const char* help,
73 int expected_args, const char* defaults, bool required,
74 const char* flag2)
75 {
76 char delim = 0;
77 string strHelp = help;
78 if (strlen(defaults) > 0 && expected_args == 1 && required == false)
79 {
80 strHelp += " (default = " + string(defaults) + ")";
81 }
82 if (flag2)
83 {
84 p->add(defaults, required, expected_args, delim,
85 strHelp.c_str(), flag1, flag2);
86 }
87 else
88 {
89 p->add(defaults, required, expected_args, delim,
90 strHelp.c_str(), flag1);
91 }
92 }
93
add_optional(const char * name,const char * help)94 void OptionParser::add_optional(const char* name, const char* help)
95 {
96 // TODO(BM) Do something with the help field
97 p->optional_.push_back(string(name));
98 p->optionalHelp_.push_back(string(help));
99 }
100
add_required(const char * name,const char * help)101 void OptionParser::add_required(const char* name, const char* help)
102 {
103 // TODO(BM) Do something with the help field
104 p->required_.push_back(string(name));
105 p->requiredHelp_.push_back(string(help));
106 }
107
add_settings_options()108 void OptionParser::add_settings_options()
109 {
110 add_required("settings file");
111 add_optional("key");
112 add_optional("value");
113 add_flag("--get", "Print key value in settings file.");
114 add_flag("--set", "Set key value in settings file.");
115 }
116
check_options(int argc,char ** argv)117 bool OptionParser::check_options(int argc, char** argv)
118 {
119 add_flag("--help", "Display usage instructions and exit.", false);
120 add_flag("--version", "Display the program name/version banner and exit.",
121 false);
122 add_flag("--settings", "Display settings and exit.", false);
123 p->parse(argc, argv);
124 if (is_set("--help"))
125 {
126 print_usage();
127 return false;
128 }
129 if (is_set("--version"))
130 {
131 cout << p->version_ << endl;
132 return false;
133 }
134 if (is_set("--settings"))
135 {
136 cout << string(p->settings_) << endl;
137 return false;
138 }
139 vector<string> bad_opts;
140 if (!p->gotRequired(bad_opts))
141 {
142 for (int i = 0; i < (int)bad_opts.size(); ++i)
143 {
144 error("Missing required option: %s", bad_opts[i].c_str());
145 return false;
146 }
147 }
148 if (!p->gotExpected(bad_opts))
149 {
150 for (int i = 0; i < (int)bad_opts.size(); ++i)
151 {
152 error("Got unexpected number of arguments for option: %s",
153 bad_opts[i].c_str());
154 return false;
155 }
156 }
157 int min_req_args = (int)p->required_.size();
158 if (num_args() < min_req_args)
159 {
160 error("Expected >= %i input argument(s), %i given", min_req_args,
161 num_args());
162 return false;
163 }
164 return true;
165 }
166
error(const char * format,...)167 void OptionParser::error(const char* format, ...)
168 {
169 cerr << "ERROR:\n ";
170 va_list args;
171 va_start(args, format);
172 vprintf(format, args);
173 va_end(args);
174 cerr << "\n\n";
175 print_usage();
176 }
177
get_arg(int i) const178 const char* OptionParser::get_arg(int i) const
179 {
180 vector<string*>& first = p->firstArgs;
181 vector<string*>& last = p->lastArgs;
182 if ((int)first.size() - 1 > i)
183 {
184 return first[i + 1]->c_str();
185 }
186 // Requested index is in the last argument set.
187 else if (((int)first.size() - 1 + (int)last.size()) > i)
188 {
189 return last[i - ((int)first.size() - 1)]->c_str();
190 }
191 return 0;
192 }
193
get_double(const char * name)194 double OptionParser::get_double(const char* name)
195 {
196 double val = 0.0;
197 p->get(name)->getDouble(val);
198 return val;
199 }
200
get_int(const char * name)201 int OptionParser::get_int(const char* name)
202 {
203 int val = 0;
204 p->get(name)->getInt(val);
205 return val;
206 }
207
get_string(const char * name)208 const char* OptionParser::get_string(const char* name)
209 {
210 const char* val = 0;
211 p->get(name)->getString(val);
212 return val;
213 }
214
get_input_files(int min_required,int * num_files)215 const char* const* OptionParser::get_input_files(int min_required,
216 int* num_files)
217 {
218 p->input_files_.clear();
219 vector<string*>& first = p->firstArgs;
220 vector<string*>& last = p->lastArgs;
221 // Note: min_required + 1 because firstArgs[0] is the binary name
222 if (((int)first.size() >= min_required + 1) && ((int)last.size() == 0))
223 {
224 // Note: starts at 1 as index 0 is the binary name.
225 for (int i = 1; i < (int)first.size(); ++i)
226 {
227 p->input_files_.push_back(first[i]->c_str());
228 }
229 }
230 else
231 {
232 for (int i = 0; i < (int)last.size(); ++i)
233 {
234 p->input_files_.push_back(last[i]->c_str());
235 }
236 }
237 *num_files = (int) p->input_files_.size();
238 return (p->input_files_.size() > 0) ? &(p->input_files_)[0] : 0;
239 }
240
is_set(const char * option)241 int OptionParser::is_set(const char* option)
242 {
243 return p->isSet(option);
244 }
245
print_usage()246 void OptionParser::print_usage()
247 {
248 string usage;
249 p->syntax = p->title + " [OPTIONS]";
250 for (int i = 0; i < (int)p->required_.size(); ++i)
251 {
252 p->syntax += " <" + p->required_[i] + ">";
253 }
254 for (int i = 0; i < (int)p->optional_.size(); ++i)
255 {
256 p->syntax += " [" + p->optional_[i] + "]";
257 }
258 // TODO(BM) overload here rather than editing the library header...!
259 p->getUsage(usage);
260 cout << usage;
261 }
262
num_args() const263 int OptionParser::num_args() const
264 {
265 return (int)(p->firstArgs.size() - 1 + p->lastArgs.size());
266 }
267
set_description(const char * description)268 void OptionParser::set_description(const char* description)
269 {
270 p->overview = description;
271 }
272
set_settings(const char * text)273 void OptionParser::set_settings(const char* text)
274 {
275 p->settings_ = text;
276 }
277
set_title(const char * text)278 void OptionParser::set_title(const char* text)
279 {
280 p->title = text;
281 }
282
set_version(const char * version,bool show)283 void OptionParser::set_version(const char* version, bool show)
284 {
285 if (show)
286 {
287 p->version = version;
288 }
289 p->version_ = version;
290 }
291
292 } /* namespace oskar */
293