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