Mercurial > projects > dang
comparison dang/OptParse.d @ 211:9e9f3e7e342b default tip
Added dang folder and Module in ast.
author | Anders Johnsen <skabet@gmail.com> |
---|---|
date | Tue, 12 Aug 2008 20:07:35 +0200 |
parents | 2168f4cb73f1 |
children |
comparison
equal
deleted
inserted
replaced
210:f4149d4f6896 | 211:9e9f3e7e342b |
---|---|
1 /* | |
2 Copyright (c) 2007 Kirk McDonald | |
3 | |
4 Permission is hereby granted, free of charge, to any person obtaining a copy of | |
5 this software and associated documentation files (the "Software"), to deal in | |
6 the Software without restriction, including without limitation the rights to | |
7 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | |
8 of the Software, and to permit persons to whom the Software is furnished to do | |
9 so, subject to the following conditions: | |
10 | |
11 The above copyright notice and this permission notice shall be included in all | |
12 copies or substantial portions of the Software. | |
13 | |
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
20 SOFTWARE. | |
21 */ | |
22 /** | |
23 * Command-line option parsing, in the style of Python's optparse. | |
24 * | |
25 * Refer to the complete docs for more information. | |
26 */ | |
27 module dang.OptParse; | |
28 | |
29 import tango.io.Stdout; | |
30 import tango.text.Util : locate, locatePrior; | |
31 import tango.text.Ascii : toUpper; | |
32 import tango.stdc.stdlib : exit, EXIT_FAILURE, EXIT_SUCCESS; | |
33 import tango.text.convert.Integer : parse, toInt, toString = toString; | |
34 import tango.text.convert.Utf : toString, toString32; | |
35 | |
36 /* | |
37 Options may be in two forms: long and short. Short options start with a single | |
38 dash and are one letter long. Long options start with two dashes and may | |
39 consist of any number of characters (so long as they don't start with a dash, | |
40 though they may contain dashes). Options are case-sensitive. | |
41 | |
42 Short options may be combined. The following are equivalent: | |
43 | |
44 $ myapp -a -b -c | |
45 $ myapp -abc | |
46 $ myapp -ab -c | |
47 | |
48 If -f and --file are aliases of the same option, which accepts an argument, | |
49 the following are equivalent: | |
50 | |
51 $ myapp -f somefile.txt | |
52 $ myapp -fsomefile.txt | |
53 $ myapp --file somefile.txt | |
54 $ myapp --file=somefile.txt | |
55 | |
56 The following are also valid: | |
57 | |
58 $ myapp -abcf somefile.txt | |
59 $ myapp -abcfsomefile.txt | |
60 $ myapp -abc --file somefile.txt | |
61 | |
62 If an option occurs multiple times, the last one is the one recorded: | |
63 | |
64 $ myapp -f somefile.txt --file otherfile.txt | |
65 | |
66 Matches 'otherfile.txt'. | |
67 */ | |
68 | |
69 bool startswith(char[] s, char[] start) { | |
70 if (s.length < start.length) return false; | |
71 return s[0 .. start.length] == start; | |
72 } | |
73 bool endswith(char[] s, char[] end) { | |
74 if (s.length < end.length) return false; | |
75 return s[$ - end.length .. $] == end; | |
76 } | |
77 | |
78 /// Thrown if client code tries to set up an improper option. | |
79 class OptionError : Exception { | |
80 this(char[] msg) { super(msg); } | |
81 } | |
82 // Thrown if client code tries to extract the wrong type from an option. | |
83 class OptionTypeError : Exception { | |
84 this(char[] msg) { super(msg); } | |
85 } | |
86 | |
87 /++ | |
88 This class represents the results after parsing the command-line. | |
89 +/ | |
90 class Options { | |
91 char[][][char[]] opts; | |
92 int[char[]] counted_opts; | |
93 /// By default, leftover arguments are placed in this array. | |
94 char[][] args; | |
95 | |
96 /// Retrieves the results of the Store and StoreConst actions. | |
97 char[] opIndex(char[] opt) { | |
98 char[][]* o = opt in opts; | |
99 if (o) { | |
100 return (*o)[0]; | |
101 } else { | |
102 return ""; | |
103 } | |
104 } | |
105 /// Retrieves the results of the Store action, when the type is Integer. | |
106 int value(char[] opt) { | |
107 char[][]* o = opt in opts; | |
108 if (o) { | |
109 return toInt((*o)[0]); | |
110 } else { | |
111 return 0; | |
112 } | |
113 } | |
114 /// Retrieves the results of the Append and AppendConst actions. | |
115 char[][] list(char[] opt) { | |
116 char[][]* o = opt in opts; | |
117 if (o) { | |
118 return *o; | |
119 } else { | |
120 return null; | |
121 } | |
122 } | |
123 /// Retrieves the results of the Append action, when the type is Integer. | |
124 int[] valueList(char[] opt) { | |
125 char[][]* o = opt in opts; | |
126 int[] l; | |
127 if (o) { | |
128 l.length = (*o).length; | |
129 foreach (i, s; *o) { | |
130 l[i] = toInt(s); | |
131 } | |
132 } | |
133 return l; | |
134 } | |
135 /// Retrieves the results of the Count action. | |
136 int count(char[] opt) { | |
137 int* c = opt in counted_opts; | |
138 if (c) { | |
139 return *c; | |
140 } else { | |
141 return 0; | |
142 } | |
143 } | |
144 /// Retrieves the results of the SetTrue and SetFalse actions. | |
145 bool flag(char[] opt) { | |
146 char[][]* o = opt in opts; | |
147 if (o) { | |
148 return (*o)[0] == "1"; | |
149 } else { | |
150 return false; | |
151 } | |
152 } | |
153 } | |
154 | |
155 // Options, args, this opt's index in args, name[, arg] | |
156 /// | |
157 alias void delegate(Options, inout char[][], inout int, char[], char[]) OptionCallbackFancyArg; | |
158 /// | |
159 alias void delegate(Options, inout char[][], inout int, char[], int) OptionCallbackFancyInt; | |
160 /// | |
161 alias void delegate(Options, inout char[][], inout int, char[]) OptionCallbackFancy; | |
162 | |
163 /// | |
164 alias void delegate(char[]) OptionCallbackArg; | |
165 /// | |
166 alias void delegate(int) OptionCallbackInt; | |
167 /// | |
168 alias void delegate() OptionCallback; | |
169 | |
170 /* | |
171 Actions: | |
172 * Store: name | |
173 * StoreConst: name, const_value | |
174 * Append: name | |
175 * AppendConst: name, const_value | |
176 * Count: name | |
177 * CallbackArg: dga | |
178 * CallbackVoid: dg | |
179 */ | |
180 /// | |
181 enum Action { /+++/Store, /+++/StoreConst, /+++/Append, /+++/AppendConst, /+++/Count, /+++/SetTrue, /+++/SetFalse, /+++/Callback, /+++/CallbackFancy, /+++/Help /+++/} | |
182 /// | |
183 enum ArgType { /+++/None, /+++/String, /+++/Integer /+++/} | |
184 | |
185 ArgType defaultType(Action action) { | |
186 switch (action) { | |
187 case Action.Store, Action.Append, Action.Callback, Action.CallbackFancy: | |
188 return ArgType.String; | |
189 break; | |
190 default: | |
191 return ArgType.None; | |
192 break; | |
193 } | |
194 } | |
195 | |
196 /++ | |
197 This class represents a single command-line option. | |
198 +/ | |
199 class Option { | |
200 char[][] shortopts, longopts; | |
201 Action action; | |
202 ArgType type; | |
203 char[] name, argname; | |
204 char[] const_value; | |
205 | |
206 char[] default_string; | |
207 int default_value; | |
208 bool default_flag, has_default; | |
209 | |
210 OptionCallbackArg callback; | |
211 OptionCallbackInt int_callback; | |
212 OptionCallback void_callback; | |
213 | |
214 OptionCallbackFancyArg fancy_callback; | |
215 OptionCallbackFancyInt fancy_int_callback; | |
216 OptionCallbackFancy fancy_void_callback; | |
217 char[] helptext; | |
218 this( | |
219 char[][] shorts, char[][] longs, ArgType type, | |
220 Action act, char[] name, char[] const_value, | |
221 OptionCallbackArg dga, OptionCallback dg, | |
222 OptionCallbackInt dgi, | |
223 OptionCallbackFancyArg fdga, | |
224 OptionCallbackFancyInt fdgi, | |
225 OptionCallbackFancy fdg | |
226 ) { | |
227 this.shortopts = shorts; | |
228 this.longopts = longs; | |
229 this.action = act; | |
230 this.type = type; | |
231 this.name = name; | |
232 this.argname = toUpper(name.dup); | |
233 this.default_string = ""; | |
234 this.default_value = 0; | |
235 this.default_flag = false; | |
236 | |
237 // Perform sanity checks. | |
238 assert (name !is null); | |
239 switch (act) { | |
240 case Action.Store, Action.Append: | |
241 assert(type != ArgType.None); | |
242 break; | |
243 case Action.StoreConst, Action.AppendConst: | |
244 assert(type == ArgType.None); | |
245 assert(const_value !is null); | |
246 break; | |
247 case Action.Callback: | |
248 //assert(type != ArgType.None); | |
249 switch (type) { | |
250 case ArgType.String: | |
251 assert(dga !is null); | |
252 break; | |
253 case ArgType.Integer: | |
254 assert(dgi !is null); | |
255 break; | |
256 case ArgType.None: | |
257 assert(dg !is null); | |
258 break; | |
259 } | |
260 break; | |
261 case Action.CallbackFancy: | |
262 switch (type) { | |
263 case ArgType.String: | |
264 assert(fdga !is null); | |
265 break; | |
266 case ArgType.Integer: | |
267 assert(fdgi !is null); | |
268 break; | |
269 case ArgType.None: | |
270 assert(fdg !is null); | |
271 break; | |
272 } | |
273 default: | |
274 break; | |
275 } | |
276 this.const_value = const_value; | |
277 this.callback = dga; | |
278 this.int_callback = dgi; | |
279 this.void_callback = dg; | |
280 this.fancy_callback = fdga; | |
281 this.fancy_int_callback = fdgi; | |
282 this.fancy_void_callback = fdg; | |
283 } | |
284 char[] toString() { | |
285 int optCount = this.shortopts.length + this.longopts.length; | |
286 char[] result; | |
287 bool printed_arg = false; | |
288 foreach(i, opt; this.shortopts ~ this.longopts) { | |
289 result ~= opt; | |
290 if (i < optCount-1) { | |
291 result ~= ", "; | |
292 } else if (this.hasArg()) { | |
293 result ~= "=" ~ toUpper(this.argname.dup); | |
294 } | |
295 } | |
296 return result; | |
297 } | |
298 //enum Action { Store, StoreConst, Append, AppendConst, Count, SetTrue, SetFalse, Callback, CallbackFancy, Help } | |
299 void issue_default(Options results) { | |
300 // Only set the default if the option doesn't already have a value. | |
301 char[][]* val = this.name in results.opts; | |
302 switch (this.action) { | |
303 case Action.Store, Action.Append: | |
304 if (val !is null) return; | |
305 if (this.type == ArgType.String) { | |
306 results.opts[name] = [default_string]; | |
307 } else { | |
308 results.opts[name] = [.toString(default_value)]; | |
309 } | |
310 break; | |
311 case Action.StoreConst, Action.AppendConst: | |
312 if (val !is null) return; | |
313 results.opts[name] = [default_string]; | |
314 break; | |
315 case Action.SetTrue, Action.SetFalse: | |
316 if (val !is null) return; | |
317 if (default_flag) { | |
318 results.opts[name] = ["1"]; | |
319 } else { | |
320 results.opts[name] = ["0"]; | |
321 } | |
322 break; | |
323 default: | |
324 return; | |
325 } | |
326 } | |
327 // Does whatever this option is supposed to do. | |
328 void performAction(OptionParser parser, Options results, inout char[][] args, inout int idx, char[] arg) { | |
329 int i; | |
330 if (this.type == ArgType.Integer) { | |
331 // Verify that it's an int. | |
332 i = parser.toOptInt(arg); | |
333 } | |
334 switch (this.action) { | |
335 case Action.Store: | |
336 results.opts[name] = [arg]; | |
337 break; | |
338 case Action.Append: | |
339 results.opts[name] ~= arg; | |
340 break; | |
341 case Action.StoreConst: | |
342 assert(arg is null, "Got unexpected argument for '"~name~"' option."); | |
343 results.opts[name] = [const_value]; | |
344 break; | |
345 case Action.AppendConst: | |
346 assert(arg is null, "Got unexpected argument for '"~name~"' option."); | |
347 results.opts[name] ~= const_value; | |
348 break; | |
349 case Action.Count: | |
350 assert(arg is null, "Got unexpected argument for '"~name~"' option."); | |
351 ++results.counted_opts[name]; | |
352 break; | |
353 case Action.SetTrue: | |
354 results.opts[name] = ["1"]; | |
355 break; | |
356 case Action.SetFalse: | |
357 results.opts[name] = ["0"]; | |
358 break; | |
359 case Action.Callback: | |
360 switch (type) { | |
361 case ArgType.String: | |
362 callback(arg); | |
363 break; | |
364 case ArgType.Integer: | |
365 int_callback(i); | |
366 break; | |
367 case ArgType.None: | |
368 void_callback(); | |
369 break; | |
370 } | |
371 break; | |
372 case Action.CallbackFancy: | |
373 switch (type) { | |
374 case ArgType.String: | |
375 fancy_callback(results, args, idx, name, arg); | |
376 break; | |
377 case ArgType.Integer: | |
378 fancy_int_callback(results, args, idx, name, i); | |
379 break; | |
380 case ArgType.None: | |
381 fancy_void_callback(results, args, idx, name); | |
382 break; | |
383 } | |
384 break; | |
385 case Action.Help: | |
386 parser.helpText(); | |
387 exit(EXIT_SUCCESS); | |
388 break; | |
389 } | |
390 } | |
391 /// Returns whether this option accepts an argument. | |
392 bool hasArg() { | |
393 return this.type != ArgType.None; | |
394 } | |
395 /// Sets the help text for this option. | |
396 Option help(char[] help) { | |
397 this.helptext = help; | |
398 return this; | |
399 } | |
400 /// Sets the name of this option's argument, if it has one. | |
401 Option argName(char[] argname) { | |
402 this.argname = argname; | |
403 return this; | |
404 } | |
405 Option def(char[] val) { | |
406 if ( | |
407 (this.type != ArgType.String || (this.action != Action.Store && this.action != Action.Append)) && | |
408 this.action != Action.StoreConst && this.action != Action.AppendConst | |
409 ) | |
410 throw new OptionError("Cannot specify string default for non-string option '"~this.name~"'"); | |
411 this.has_default = true; | |
412 this.default_string = val; | |
413 return this; | |
414 } | |
415 Option def(int val) { | |
416 if (this.type != ArgType.Integer || (this.action != Action.Store && this.action != Action.Append)) | |
417 throw new OptionError("Cannot specify integer default for non-integer option '"~this.name~"'"); | |
418 this.has_default = true; | |
419 this.default_value = val; | |
420 return this; | |
421 } | |
422 Option def(bool val) { | |
423 if (this.action != Action.SetTrue && this.action != Action.SetFalse) | |
424 throw new OptionError("Cannot specify boolean default for non-flag option '"~this.name~"'"); | |
425 this.has_default = true; | |
426 this.default_flag = val; | |
427 return this; | |
428 } | |
429 // Returns true if the passed option string matches this option. | |
430 bool matches(char[] _arg) { | |
431 dchar[] arg = toString32(_arg); | |
432 if ( | |
433 arg.length < 2 || | |
434 arg.length == 2 && (arg[0] != '-' || arg[1] == '-') || | |
435 arg.length > 2 && (arg[0 .. 2] != "--" || arg[2] == '-') | |
436 ) { | |
437 return false; | |
438 } | |
439 if (arg.length == 2) { | |
440 foreach (opt; shortopts) { | |
441 if (_arg == opt) { | |
442 return true; | |
443 } | |
444 } | |
445 } else { | |
446 foreach (opt; longopts) { | |
447 if (_arg == opt) { | |
448 return true; | |
449 } | |
450 } | |
451 } | |
452 return false; | |
453 } | |
454 } | |
455 | |
456 /++ | |
457 This class is used to define a set of options, and parse the command-line | |
458 arguments. | |
459 +/ | |
460 class OptionParser { | |
461 OptionCallbackArg leftover_cb; | |
462 /// An array of all of the options known by this parser. | |
463 Option[] options; | |
464 char[] name, desc; | |
465 /// The description of the programs arguments, as used in the Help action. | |
466 char[] argdesc; | |
467 private void delegate(char[]) error_callback; | |
468 | |
469 this(char[] desc="") { | |
470 this.name = ""; | |
471 this.desc = desc; | |
472 this.argdesc = "[options] args..."; | |
473 } | |
474 | |
475 /// Sets a callback, to override the default error behavior. | |
476 void setErrorCallback(void delegate(char[]) dg) { | |
477 error_callback = dg; | |
478 } | |
479 void unknownOptError(char[] opt) { | |
480 error("Unknown argument '"~opt~"'"); | |
481 } | |
482 void expectedArgError(char[] opt) { | |
483 error("'"~opt~"' option expects an argument."); | |
484 } | |
485 /// Displays an error message and terminates the program. | |
486 void error(char[] err) { | |
487 if (error_callback !is null) { | |
488 error_callback(err); | |
489 } else { | |
490 this.helpText(); | |
491 Stdout.formatln(err); | |
492 } | |
493 exit(EXIT_FAILURE); | |
494 } | |
495 int toOptInt(char[] s) { | |
496 int i; | |
497 uint ate; | |
498 i = .parse(s, 10u, &ate); | |
499 if (ate != s.length) | |
500 error("Could not convert '"~s~"' to an integer."); | |
501 return i; | |
502 } | |
503 | |
504 /// Displays useful "help" information about the program's options. | |
505 void helpText() { | |
506 int optWidth; | |
507 char[][] optStrs; | |
508 typedef char spacechar = ' '; | |
509 spacechar[] padding; | |
510 // Calculate the maximum width of the option lists. | |
511 foreach(i, opt; options) { | |
512 optStrs ~= opt.toString(); | |
513 if (optStrs[i].length > optWidth) { | |
514 optWidth = optStrs[i].length; | |
515 } | |
516 } | |
517 Stdout.formatln("Usage: {0} {1}", this.name, this.argdesc); | |
518 if (this.desc !is null && this.desc != "") Stdout.formatln(this.desc); | |
519 Stdout.formatln("\nOptions:"); | |
520 foreach(i, opt; options) { | |
521 padding.length = optWidth - optStrs[i].length; | |
522 Stdout.formatln(" {0}{1} {2}", optStrs[i], cast(char[])padding, opt.helptext); | |
523 } | |
524 } | |
525 | |
526 // Checks the passed arg against all the options in the parser. | |
527 // Returns null if no match is found. | |
528 Option matches(char[] arg) { | |
529 foreach(o; options) { | |
530 if (o.matches(arg)) { | |
531 return o; | |
532 } | |
533 } | |
534 return null; | |
535 } | |
536 char[] getBaseName(char[] path) { | |
537 version(Windows) { | |
538 char delimiter = '\\'; | |
539 } else { | |
540 char delimiter = '/'; | |
541 } | |
542 uint idx = locatePrior(path, delimiter); | |
543 if (idx == path.length) return path; | |
544 return path[idx+1 .. $]; | |
545 } | |
546 char[] getProgramName(char[] path) { | |
547 version(Windows) { | |
548 // (Unicode note: ".exe" only contains 4 code units, so this slice | |
549 // should Just Work.) (Although it remains to be seen how robust | |
550 // this code actually is.) | |
551 //Stdout.formatln(path); | |
552 //assert(path[$-4 .. $] == ".exe"); | |
553 //path = path[0 .. $-4]; | |
554 } | |
555 return getBaseName(path); | |
556 } | |
557 /// Parses the passed command-line arguments and returns the results. | |
558 Options parse(char[][] args) { | |
559 this.name = getProgramName(args[0]); | |
560 args = args[1 .. $]; | |
561 Options options = new Options; | |
562 /* | |
563 The issue is this: | |
564 | |
565 $ myapp -abc | |
566 | |
567 This might be three short opts, or one or two opts, the last of which | |
568 accepts an argument. In the three-opt case, we want to get: | |
569 | |
570 $ myapp -a -b -c | |
571 | |
572 In the one-opt case, we want: | |
573 | |
574 $ myapp -a bc | |
575 | |
576 In the two-opt case, we want: | |
577 | |
578 $ myapp -a -b c | |
579 | |
580 We also want to parse apart "--file=somefile" into "--file somefile" | |
581 */ | |
582 char[] opt, newopt, arg; | |
583 dchar[] opt32; | |
584 int idx; | |
585 Option match; | |
586 | |
587 for (int i=0; i<args.length; ++i) { | |
588 opt = args[i]; | |
589 // -- ends the option list, the remainder is dumped into args | |
590 if (opt == "--") { | |
591 if (this.leftover_cb !is null) { | |
592 foreach(a; args[i+1 .. $]) { | |
593 this.leftover_cb(a); | |
594 } | |
595 } else { | |
596 options.args ~= args[i+1 .. $]; | |
597 } | |
598 i = args.length; | |
599 } else if (opt.startswith("--")) { | |
600 idx = locate(opt, '='); | |
601 if (idx != opt.length) { | |
602 newopt = opt[0 .. idx]; | |
603 // Stitch out the old arg, stitch in the newopt, arg pair. | |
604 // (Unicode note: idx+1 works, since we know '=' is a | |
605 // single code unit.) | |
606 args = args[0 .. i] ~ [newopt, opt[idx+1 .. $]] ~ args[i+1 .. $]; | |
607 } else { | |
608 newopt = opt; | |
609 } | |
610 match = matches(newopt); | |
611 if (match is null) { | |
612 unknownOptError(newopt); | |
613 } | |
614 if (match.hasArg) { | |
615 if (i == args.length-1) expectedArgError(match.name); | |
616 arg = args[i+1]; | |
617 ++i; | |
618 } else { | |
619 arg = null; | |
620 } | |
621 match.performAction(this, options, args, i, arg); | |
622 } else if (opt.startswith("-")) { | |
623 if (opt.length >= 2) { | |
624 opt32 = toString32(opt[1 .. $]); | |
625 foreach (j, c; opt32) { | |
626 newopt = .toString("-" ~ [c]); | |
627 match = matches(newopt); | |
628 if (match is null) { | |
629 unknownOptError(newopt); | |
630 } | |
631 if (match.hasArg) { | |
632 // This is the last char in the group, look to the | |
633 // next element of args for the arg. | |
634 if (j == opt32.length-1) { | |
635 if (i == args.length-1) expectedArgError(match.name); | |
636 arg = args[i+1]; | |
637 ++i; | |
638 // Otherwise, consume the rest of this group for | |
639 // the arg. | |
640 } else { | |
641 arg = .toString(opt32[j+1 .. $]); | |
642 match.performAction(this, options, args, i, arg); | |
643 break; | |
644 } | |
645 } else { | |
646 arg = null; | |
647 } | |
648 match.performAction(this, options, args, i, arg); | |
649 } | |
650 } else { | |
651 unknownOptError(opt); | |
652 } | |
653 } else { | |
654 if (this.leftover_cb is null) { | |
655 options.args ~= opt; | |
656 } else { | |
657 this.leftover_cb(opt); | |
658 } | |
659 } | |
660 } | |
661 foreach (o; this.options) { | |
662 o.issue_default(options); | |
663 } | |
664 return options; | |
665 } | |
666 | |
667 /++ | |
668 Overrides the default behavior of leftover arguments, calling this callback | |
669 with them instead of adding them an array. | |
670 +/ | |
671 void leftoverCallback(OptionCallbackArg dg) { | |
672 this.leftover_cb = dg; | |
673 } | |
674 /// | |
675 Option addOption(Option option) { | |
676 this.options ~= option; | |
677 return option; | |
678 } | |
679 // options action name type const_value dga dgv dgi fdga fdgi fdg | |
680 /// | |
681 Option addOption(char[][] options ...) { | |
682 return addOption(options, Action.Store, null, defaultType(Action.Store), null, null, null, null, null, null, null); | |
683 } | |
684 /// | |
685 Option addOption(char[][] options, char[] name) { | |
686 return addOption(options, Action.Store, name, defaultType(Action.Store), null, null, null, null, null, null, null); | |
687 } | |
688 /// | |
689 Option addOption(char[][] options, Action action) { | |
690 return addOption(options, action, null, defaultType(action), null, null, null, null, null, null, null); | |
691 } | |
692 /// | |
693 Option addOption(char[][] options, ArgType type) { | |
694 return addOption(options, Action.Store, null, type, null, null, null, null, null, null, null); | |
695 } | |
696 /// | |
697 Option addOption(char[][] options, Action action, ArgType type) { | |
698 return addOption(options, action, null, type, null, null, null, null, null, null, null); | |
699 } | |
700 /// | |
701 Option addOption(char[][] options, char[] name, Action action) { | |
702 return addOption(options, action, name, defaultType(action), null, null, null, null, null, null, null); | |
703 } | |
704 /// | |
705 Option addOption(char[][] options, char[] name, Action action, ArgType type) { | |
706 return addOption(options, action, name, type, null, null, null, null, null, null, null); | |
707 } | |
708 /// | |
709 Option addOption(char[][] options, Action action, char[] const_value) { | |
710 return addOption(options, action, null, defaultType(action), const_value, null, null, null, null, null, null); | |
711 } | |
712 /// | |
713 Option addOption(char[][] options, char[] name, char[] const_value) { | |
714 return addOption(options, Action.StoreConst, name, defaultType(Action.Store), const_value, null, null, null, null, null, null); | |
715 } | |
716 /// | |
717 Option addOption(char[][] options, char[] name, Action action, char[] const_value) { | |
718 return addOption(options, action, name, defaultType(action), const_value, null, null, null, null, null, null); | |
719 } | |
720 /// | |
721 Option addOption(char[][] options, OptionCallbackArg dg) { | |
722 return addOption(options, Action.Callback, null, ArgType.String, null, dg, null, null, null, null, null); | |
723 } | |
724 /// | |
725 Option addOption(char[][] options, OptionCallback dg) { | |
726 return addOption(options, Action.Callback, null, ArgType.None, null, null, dg, null, null, null, null); | |
727 } | |
728 /// | |
729 Option addOption(char[][] options, OptionCallbackInt dg) { | |
730 return addOption(options, Action.Callback, null, ArgType.Integer, null, null, null, dg, null, null, null); | |
731 } | |
732 /// | |
733 Option addOption(char[][] options, OptionCallbackFancyArg dg) { | |
734 return addOption(options, Action.CallbackFancy, null, ArgType.String, null, null, null, null, dg, null, null); | |
735 } | |
736 /// | |
737 Option addOption(char[][] options, OptionCallbackFancy dg) { | |
738 return addOption(options, Action.CallbackFancy, null, ArgType.None, null, null, null, null, null, null, dg); | |
739 } | |
740 /// | |
741 Option addOption(char[][] options, OptionCallbackFancyInt dg) { | |
742 return addOption(options, Action.CallbackFancy, null, ArgType.Integer, null, null, null, null, null, dg, null); | |
743 } | |
744 /// | |
745 Option addOption(char[][] options, char[] name, OptionCallbackFancyArg dg) { | |
746 return addOption(options, Action.CallbackFancy, name, ArgType.String, null, null, null, null, dg, null, null); | |
747 } | |
748 /// | |
749 Option addOption(char[][] options, char[] name, OptionCallbackFancy dg) { | |
750 return addOption(options, Action.CallbackFancy, name, ArgType.None, null, null, null, null, null, null, dg); | |
751 } | |
752 /// | |
753 Option addOption(char[][] options, char[] name, OptionCallbackFancyInt dg) { | |
754 return addOption(options, Action.CallbackFancy, name, ArgType.Integer, null, null, null, null, null, dg, null); | |
755 } | |
756 // Although users certainly /can/ call this, all those overloads are there | |
757 // for a reason. | |
758 Option addOption( | |
759 char[][] options, | |
760 Action action, char[] name, ArgType type, char[] const_value, | |
761 OptionCallbackArg callback, OptionCallback vcall, | |
762 OptionCallbackInt icall, | |
763 OptionCallbackFancyArg fdga, | |
764 OptionCallbackFancyInt fdgi, | |
765 OptionCallbackFancy fdg | |
766 ) { | |
767 char[][] shortopts; | |
768 char[][] longopts; | |
769 dchar[] opt; | |
770 Option option; | |
771 foreach (_opt; options) { | |
772 // (Unicode note: We convert to dchar[] so the length checks work | |
773 // out in the event of a short opt with a >127 character.) | |
774 opt = toString32(_opt); | |
775 if (opt.length < 2) { | |
776 throw new OptionError( | |
777 "invalid option string '" ~ _opt ~ "': must be at least two characters long" | |
778 ); | |
779 } else if (opt.length > 2) { | |
780 if (opt[0 .. 2] != "--" || opt[2] == '-') | |
781 throw new OptionError( | |
782 "invalid long option string '" ~ _opt ~ "': must start with --, followed by non-dash" | |
783 ); | |
784 longopts ~= _opt; | |
785 } else { | |
786 if (opt[0] != '-' || opt[1] == '-') | |
787 throw new OptionError( | |
788 "invalid short option string '" ~ _opt ~ "': must be of the form -x, where x is non-dash" | |
789 ); | |
790 shortopts ~= _opt; | |
791 } | |
792 } | |
793 if (name is null) { | |
794 // (Unicode note: We know '-' is a single code unit, so these | |
795 // slices are okay.) | |
796 if (longopts.length > 0) | |
797 name = longopts[0][2 .. $]; | |
798 else if (shortopts.length > 0) | |
799 name = shortopts[0][1 .. 2]; | |
800 else | |
801 throw new OptionError( | |
802 "No options provided to addOption!" | |
803 ); | |
804 } | |
805 option = new Option(shortopts, longopts, type, action, name, const_value, callback, vcall, icall, fdga, fdgi, fdg); | |
806 this.options ~= option; | |
807 return option; | |
808 } | |
809 } | |
810 |