DCL 3.7.4
Loading...
Searching...
No Matches
Arguments.cpp
Go to the documentation of this file.
1#include <dcl/Config.h>
2
3#include <wctype.h>
4
5#include <dcl/Charset.h>
6#include <dcl/Writer.h>
7#include <dcl/Arguments.h>
8
9#if __DCL_HAVE_THIS_FILE__
10#undef __THIS_FILE__
11static const char_t __UNUSED__ __THIS_FILE__[] = __T("dcl/Arguments.cpp");
12#endif
13
14__DCL_BEGIN_NAMESPACE
15
17
18Arguments::Arguments(
19 Writer& _output, Writer& _errout,
20 const wchar_t* _program_version,
21 const wchar_t* _program_bug_address,
22 const wchar_t* _arg_doc,
23 const wchar_t* _doc,
24 const Option _options[]
25) : __output(_output), __errout(_errout)
26{
27 __program_version = _program_version;
28 __program_bug_address = _program_bug_address;
29 __arg_doc = _arg_doc;
30 __doc = _doc;
31 __options = _options;
32}
33
34#define KEY_USAGE -1
35static Arguments::Option __options__[] =
36{
37 { L"help", L'?', NULL, 0, L"Give this help list" },
38 { L"usage", KEY_USAGE, NULL, 0, L"Give a short usage message" },
39 { L"version", L'V', NULL, 0, L"Print program version" },
40 { NULL }
41};
42
43void Arguments::onOption(int _key, const String& _arg)
45{
46}
47
49{
50 return String();
51}
52
53static void __add_help(StringBuilder& _sb, const Arguments::Option* _options)
54{
55 while (_options->name || _options->doc) {
56 if (_options->name) {
57 StringBuilder sb = L" ";
58 if (iswprint(_options->key)) {
59 sb += L'-';
60 sb += (wchar_t)_options->key;
61 sb += L", ";
62 }
63 else {
64 sb += L" ";
65 }
66
67 sb += L"--";
68 sb += _options->name;
69
70 if (_options->arg) {
71 sb += L'=';
72 sb += _options->arg;
73 }
74
75 if (_options->doc) {
76 if (sb.length() < 28) {
77 String pad(L' ', 28 - sb.length());
78 sb += pad;
79 }
80 else {
81 sb += L'\n';
82 String pad(L' ', 28);
83 sb += pad;
84 }
85 sb += _options->doc;
86 }
87
88 _sb += L'\n';
89 _sb += sb.toString();
90 }
91 else {
92 _sb += L"\n\n ";
93 _sb += _options->doc;
94 }
95
96 _options++;
97 }
98}
99
100String Arguments::help() const
101{
102 StringBuilder sb = shortUsage();
103 if (__doc) {
104 sb += L'\n';
105 sb += __doc;
106 }
107 sb += L'\n';
108
109 __add_help(sb, __options);
110 sb += L'\n';
111 __add_help(sb, __options__);
112
113 if (__program_bug_address) {
114 sb += L"\n\n";
115 sb += L"Report bugs to ";
116 sb += __program_bug_address;
117 }
118 return sb;
119}
120
122{
123 StringBuilder sb = L"Usage: ";
124 sb += __value0;
125 sb += L" [OPTION...] ";
126 if (__arg_doc) {
127 sb += __arg_doc;
128 }
129 return sb;
130}
131
133{
134 StringBuilder sb = L"Try `";
135 sb += __value0;
136 sb += L" --help' or `";
137 sb += __value0;
138 sb += L" --usage' for more information.";
139 return sb;
140}
141
142String Arguments::argRequired(const String& _option) const
143{
144 StringBuilder sb = __value0;
145 sb += L": option '";
146 sb += _option;
147 sb += L"' requires an argument";
148 return sb;
149}
150
151String __key_options(const Arguments::Option* _options)
152{
153 StringBuilder sb;
154 while (_options->name || _options->doc) {
155 if (iswprint(_options->key)) {
156 sb += (wchar_t)_options->key;
157 }
158 _options++;
159 }
160 return sb;
161}
162
163static void __add_key_arg_options(
164 StringArray& _a,
165 const Arguments::Option* _options
166)
167{
168 while (_options->name || _options->doc) {
169 if (iswprint(_options->key) && _options->arg) {
170 StringBuilder sb = L"-";
171 sb += (wchar_t)_options->key;
172 sb += L" ";
173 sb += _options->arg;
174 _a.add(sb);
175 }
176 _options++;
177 }
178}
179
180static void __add_name_options(
181 StringArray& _a,
182 const Arguments::Option* _options
183)
184{
185 while (_options->name || _options->doc) {
186 if (_options->name && _options->arg == NULL) {
187 StringBuilder sb;
188 sb += L"--";
189 sb += _options->name;
190 _a.add(sb);
191 }
192 _options++;
193 }
194}
195
196static void __add_name_arg_options(
197 StringArray& _a,
198 const Arguments::Option* _options
199)
200{
201 while (_options->name || _options->doc) {
202 if (_options->name && _options->arg) {
203 StringBuilder sb;
204 sb += L"--";
205 sb += _options->name;
206 sb += L"=";
207 sb += _options->arg;
208 _a.add(sb);
209 }
210 _options++;
211 }
212}
213
214String Arguments::usage() const
215{
216 StringArray a;
217 {
218 String s = __key_options(__options)
219 + __key_options(__options__)
220 ;
221 if (!s.isEmpty()) {
222 a.add(L"-" + s);
223 }
224 }
225 __add_key_arg_options(a, __options);
226 __add_name_arg_options(a, __options);
227 __add_name_options(a, __options);
228 __add_name_options(a, __options__);
229
230 StringBuilder sb = L"Usage: ";
231 sb += __value0;
232 String padding(L' ', 10);
233 size_t n = sb.length();
234 for (size_t i = 0; i < a.size(); i++) {
235 const String& s = a[i];
236 if ((n + s.length() + 3) > 80) {
237 sb += L"\n";
238 sb += padding;
239 n = padding.length();
240 }
241 n += s.length() + 3;
242 sb += L" [";
243 sb += s;
244 sb += L"]";
245 }
246
247 if (__arg_doc) {
248 if (n + String::length(__arg_doc) + 1 > 80) {
249 sb += L"\n";
250 sb += padding;
251 }
252 sb += L" ";
253 sb += __arg_doc;
254 }
255
256 return sb;
257}
258
259String Arguments::version() const
260{
261 StringBuilder sb; // = L"Version: ";
262 if (__program_version) {
263 sb += __program_version;
264 }
265
266 return sb;
267}
268
269static const Arguments::Option* __find_key(
270 const String& _s,
271 const Arguments::Option* _options
272)
273{
274 if (_s.length() > 2 && _s.startsWith(L"--")) {
275 String name = _s.substring(2);
276 while (_options->name || _options->doc) {
277 if (_options->name && name.compare(_options->name,
278 String::length(_options->name)) == 0) {
279 return _options;
280 }
281 _options++;
282 }
283 }
284 else if (_s.length() > 1) {
285 int key = _s[1];
286 while (_options->name || _options->doc) {
287 if (_options->key == key) {
288 return _options;
289 }
290 _options++;
291 }
292 }
293 return NULL;
294}
295
296bool Arguments::parse(
297 int _argc, char* _argv[], bool _argv_required
298)
299{
300 LocaleDecoder decoder;
301 StringArray argv(_argc);
302 for (int i = 0; i < _argc; i++) {
303 argv[i] = decoder.decode(_argv[i]);
304 }
305
306 __value0 = argv[0];
307 argv.erase((size_t)0);
308
309 for (size_t i = 0; i < argv.size(); i++) {
310 const String& s = argv[i];
311 if (s.startsWith(L"-")) {
312 const Option* option = __find_key(s, __options__);
313 if (option != NULL) {
314 switch (option->key) {
315 case L'?':
316 __output << help() << endl;
317 break;
318 case KEY_USAGE:
319 __output << usage() << endl;
320 break;
321 case L'V':
322 __output << version() << endl;
323 }
324 return false;
325 }
326 }
327 }
328
329 for (size_t i = 0; i < argv.size(); ) {
330 // __DCL_TRACE3(L"[%zd][%zd][%ls]\n", i, argv.size(), argv.toString().data());
331 // argv[i]는 erase 될 수 있으므로
332 // 참조가 아닌 복사본 이어야 한다.
333 const String key = argv[i];
334 if (key.startsWith(L"-")) {
335 const Option* option = __find_key(key, __options);
336 if (option == NULL) {
337 __errout << __value0 + L": unrecognized option '"
338 << key << L"'" << endl
339 << tryUsage() << endl;
340 return false;
341 }
342
343 argv.erase(i);
344 String arg;
345 if (option->arg) {
346 if (key.startsWith(L"--")) {
347 size_t index = key.indexOf(L'=');
348 if (index != (size_t)-1) {
349 arg = key.substring(index + 1);
350 }
351 }
352 else {
353 if (key.length() > 2) {
354 arg = key.substring(2);
355 }
356 }
357 if (arg.isEmpty()) {
358 if (i < argv.size()) {
359 arg = argv[i];
360 argv.erase(i);
361 }
362 }
363 // __DCL_TRACE2(L"[%ls][%ls]\n", key.data(), arg.data());
364 if (arg.isEmpty()) {
365 __errout << argRequired(key) << endl
366 << tryUsage() << endl;
367 return false;
368 }
369 }
370
371 try {
372 onOption(option->key, arg);
373 }
374 catch (Exception* e) {
375 __errout << e->toStringAll() << endl;
376 e->destroy();
377 return false;
378 }
379 }
380 else {
381 i++;
382 }
383 }
384
385 try {
386 String msg = onValidate();
387 if (!msg.isEmpty()) {
388 __errout << __value0 << L": " << msg << endl
389 << tryUsage() << endl;
390 return false;
391 }
392 }
393 catch (Exception* e) {
394 __errout << e->toStringAll() << endl;
395 e->destroy();
396 return false;
397 }
398
399 if (_argv_required && argv.isEmpty()) {
400 __errout << shortUsage() << endl
401 << tryUsage() << endl;
402 return false;
403 }
404
405 __values = argv;
406 return true;
407}
408
409__DCL_END_NAMESPACE
#define __THIS_FILE__
Definition _trace.h:14
String __key_options(const Arguments::Option *_options)
#define KEY_USAGE
Definition Arguments.cpp:34
#define NULL
Definition Config.h:312
#define __UNUSED__
Definition Config.h:341
wchar_t char_t
Definition Config.h:247
#define __DCL_THROWS1(e)
Definition Config.h:152
#define IMPLEMENT_CLASSINFO(class_name, base_class_name)
Definition Object.h:245
#define __T(str)
Definition Object.h:60
DCLCVAR const struct __endl endl
String usage() const
String argRequired(const String &_option) const
String version() const
virtual String onValidate()
Definition Arguments.cpp:48
String tryUsage() const
String shortUsage() const
String help() const
virtual void onOption(int _key, const String &_arg) __DCL_THROWS1(Exception *)
Definition Arguments.cpp:43
virtual void destroy()
Definition Exception.cpp:74
String toStringAll() const
Definition Exception.cpp:45