1 // Written in the D programming language.
4 Processing of command line options.
6 The getopt module implements a $(D getopt) function, which adheres to
7 the POSIX syntax for command line options. GNU extensions are
8 supported in the form of long options introduced by a double dash
9 ("--"). Support for bundling of command line options, as was the case
10 with the more traditional single-letter approach, is provided but not
13 Copyright: Copyright Andrei Alexandrescu 2008 - 2015.
14 License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
15 Authors: $(HTTP erdani.org, Andrei Alexandrescu)
16 Credits: This module and its documentation are inspired by Perl's $(HTTP
17 perldoc.perl.org/Getopt/Long.html, Getopt::Long) module. The syntax of
18 D's $(D getopt) is simpler than its Perl counterpart because $(D
19 getopt) infers the expected parameter types from the static types of
20 the passed-in pointers.
21 Source: $(PHOBOSSRC std/_getopt.d)
24 Copyright Andrei Alexandrescu 2008 - 2015.
25 Distributed under the Boost Software License, Version 1.0.
26 (See accompanying file LICENSE_1_0.txt or copy at
27 http://www.boost.org/LICENSE_1_0.txt)
31 import std
.exception
; // basicExceptionCtors
35 Thrown on one of the following conditions:
37 $(LI An unrecognized command-line argument is passed, and
38 $(D std.getopt.config.passThrough) was not present.)
39 $(LI A command-line option was not found, and
40 $(D std.getopt.config.required) was present.)
43 class GetOptException
: Exception
45 mixin basicExceptionCtors
;
48 static assert(is(typeof(new GetOptException("message"))));
49 static assert(is(typeof(new GetOptException("message", Exception
.init
))));
52 Parse and remove command line options from a string array.
59 string data = "file.dat";
62 enum Color { no, yes };
65 void main(string[] args)
67 auto helpInformation = getopt(
69 "length", &length, // numeric
70 "file", &data, // string
71 "verbose", &verbose, // flag
72 "color", "Information about this color", &color); // enum
75 if (helpInformation.helpWanted)
77 defaultGetoptPrinter("Some information about the program.",
78 helpInformation.options);
83 The $(D getopt) function takes a reference to the command line
84 (as received by $(D main)) as its first argument, and an
85 unbounded number of pairs of strings and pointers. Each string is an
86 option meant to "fill" the value referenced by the pointer to its
87 right (the "bound" pointer). The option string in the call to
88 $(D getopt) should not start with a dash.
90 In all cases, the command-line options that were parsed and used by
91 $(D getopt) are removed from $(D args). Whatever in the
92 arguments did not look like an option is left in $(D args) for
93 further processing by the program. Values that were unaffected by the
94 options are not touched, so a common idiom is to initialize options
95 to their defaults and then invoke $(D getopt). If a
96 command-line argument is recognized as an option with a parameter and
97 the parameter cannot be parsed properly (e.g., a number is expected
98 but not present), a $(D ConvException) exception is thrown.
99 If $(D std.getopt.config.passThrough) was not passed to $(D getopt)
100 and an unrecognized command-line argument is found, a $(D GetOptException)
103 Depending on the type of the pointer being bound, $(D getopt)
104 recognizes the following kinds of options:
107 $(LI $(I Boolean options). A lone argument sets the option to $(D true).
108 Additionally $(B true) or $(B false) can be set within the option separated
112 bool verbose = false, debugging = true;
113 getopt(args, "verbose", &verbose, "debug", &debugging);
116 To set $(D verbose) to $(D true), invoke the program with either
117 $(D --verbose) or $(D --verbose=true).
119 To set $(D debugging) to $(D false), invoke the program with
120 $(D --debugging=false).
123 $(LI $(I Numeric options.) If an option is bound to a numeric type, a
124 number is expected as the next option, or right within the option separated
129 getopt(args, "timeout", &timeout);
132 To set $(D timeout) to $(D 5), invoke the program with either
133 $(D --timeout=5) or $(D --timeout 5).
136 $(LI $(I Incremental options.) If an option name has a "+" suffix and is
137 bound to a numeric type, then the option's value tracks the number of times
138 the option occurred on the command line:
142 getopt(args, "paranoid+", ¶noid);
145 Invoking the program with "--paranoid --paranoid --paranoid" will set $(D
146 paranoid) to 3. Note that an incremental option never expects a parameter,
147 e.g., in the command line "--paranoid 42 --paranoid", the "42" does not set
148 $(D paranoid) to 42; instead, $(D paranoid) is set to 2 and "42" is not
149 considered as part of the normal program arguments.
152 $(LI $(I Enum options.) If an option is bound to an enum, an enum symbol as
153 a string is expected as the next option, or right within the option
154 separated with an "=" sign:
157 enum Color { no, yes };
158 Color color; // default initialized to Color.no
159 getopt(args, "color", &color);
162 To set $(D color) to $(D Color.yes), invoke the program with either
163 $(D --color=yes) or $(D --color yes).
166 $(LI $(I String options.) If an option is bound to a string, a string is
167 expected as the next option, or right within the option separated with an
172 getopt(args, "output", &outputFile);
175 Invoking the program with "--output=myfile.txt" or "--output myfile.txt"
176 will set $(D outputFile) to "myfile.txt". If you want to pass a string
177 containing spaces, you need to use the quoting that is appropriate to your
178 shell, e.g. --output='my file.txt'.
181 $(LI $(I Array options.) If an option is bound to an array, a new element
182 is appended to the array each time the option occurs:
185 string[] outputFiles;
186 getopt(args, "output", &outputFiles);
189 Invoking the program with "--output=myfile.txt --output=yourfile.txt" or
190 "--output myfile.txt --output yourfile.txt" will set $(D outputFiles) to
191 $(D [ "myfile.txt", "yourfile.txt" ]).
193 Alternatively you can set $(LREF arraySep) as the element separator:
196 string[] outputFiles;
197 arraySep = ","; // defaults to "", separation by whitespace
198 getopt(args, "output", &outputFiles);
201 With the above code you can invoke the program with
202 "--output=myfile.txt,yourfile.txt", or "--output myfile.txt,yourfile.txt".)
204 $(LI $(I Hash options.) If an option is bound to an associative array, a
205 string of the form "name=value" is expected as the next option, or right
206 within the option separated with an "=" sign:
209 double[string] tuningParms;
210 getopt(args, "tune", &tuningParms);
213 Invoking the program with e.g. "--tune=alpha=0.5 --tune beta=0.6" will set
214 $(D tuningParms) to [ "alpha" : 0.5, "beta" : 0.6 ].
216 Alternatively you can set $(LREF arraySep) as the element separator:
219 double[string] tuningParms;
220 arraySep = ","; // defaults to "", separation by whitespace
221 getopt(args, "tune", &tuningParms);
224 With the above code you can invoke the program with
225 "--tune=alpha=0.5,beta=0.6", or "--tune alpha=0.5,beta=0.6".
227 In general, the keys and values can be of any parsable types.
230 $(LI $(I Callback options.) An option can be bound to a function or
231 delegate with the signature $(D void function()), $(D void function(string
232 option)), $(D void function(string option, string value)), or their
233 delegate equivalents.
236 $(LI If the callback doesn't take any arguments, the callback is
237 invoked whenever the option is seen.
240 $(LI If the callback takes one string argument, the option string
241 (without the leading dash(es)) is passed to the callback. After that,
242 the option string is considered handled and removed from the options
246 void main(string[] args)
248 uint verbosityLevel = 1;
249 void myHandler(string option)
251 if (option == "quiet")
257 assert(option == "verbose");
261 getopt(args, "verbose", &myHandler, "quiet", &myHandler);
267 $(LI If the callback takes two string arguments, the option string is
268 handled as an option with one argument, and parsed accordingly. The
269 option and its value are passed to the callback. After that, whatever
270 was passed to the callback is considered handled and removed from the
274 int main(string[] args)
276 uint verbosityLevel = 1;
277 bool handlerFailed = false;
278 void myHandler(string option, string value)
282 case "quiet": verbosityLevel = 0; break;
283 case "verbose": verbosityLevel = 2; break;
284 case "shouting": verbosityLevel = verbosityLevel.max; break;
286 stderr.writeln("Unknown verbosity level ", value);
287 handlerFailed = true;
291 getopt(args, "verbosity", &myHandler);
292 return handlerFailed ? 1 : 0;
299 Options_with_multiple_names:
300 Sometimes option synonyms are desirable, e.g. "--verbose",
301 "--loquacious", and "--garrulous" should have the same effect. Such
302 alternate option names can be included in the option specification,
303 using "|" as a separator:
307 getopt(args, "verbose|loquacious|garrulous", &verbose);
311 By default options are case-insensitive. You can change that behavior
312 by passing $(D getopt) the $(D caseSensitive) directive like this:
317 std.getopt.config.caseSensitive,
322 In the example above, "--foo" and "--bar" are recognized, but "--Foo", "--Bar",
323 "--FOo", "--bAr", etc. are rejected.
324 The directive is active until the end of $(D getopt), or until the
325 converse directive $(D caseInsensitive) is encountered:
330 std.getopt.config.caseSensitive,
332 std.getopt.config.caseInsensitive,
336 The option "--Foo" is rejected due to $(D
337 std.getopt.config.caseSensitive), but not "--Bar", "--bAr"
338 etc. because the directive $(D
339 std.getopt.config.caseInsensitive) turned sensitivity off before
340 option "bar" was parsed.
342 Short_versus_long_options:
343 Traditionally, programs accepted single-letter options preceded by
344 only one dash (e.g. $(D -t)). $(D getopt) accepts such parameters
345 seamlessly. When used with a double-dash (e.g. $(D --t)), a
346 single-letter option behaves the same as a multi-letter option. When
347 used with a single dash, a single-letter option is accepted. If the
348 option has a parameter, that must be "stuck" to the option without
349 any intervening space or "=":
353 getopt(args, "timeout|t", &timeout);
356 To set $(D timeout) to $(D 5), use either of the following: $(D --timeout=5),
357 $(D --timeout 5), $(D --t=5), $(D --t 5), or $(D -t5). Forms such as $(D -t 5)
358 and $(D -timeout=5) will be not accepted.
360 For more details about short options, refer also to the next section.
363 Single-letter options can be bundled together, i.e. "-abc" is the same as
364 $(D "-a -b -c"). By default, this option is turned off. You can turn it on
365 with the $(D std.getopt.config.bundling) directive:
370 std.getopt.config.bundling,
375 In case you want to only enable bundling for some of the parameters,
376 bundling can be turned off with $(D std.getopt.config.noBundling).
379 An option can be marked as required. If that option is not present in the
380 arguments an exception will be thrown.
385 std.getopt.config.required,
390 Only the option directly following $(D std.getopt.config.required) is
393 Passing_unrecognized_options_through:
394 If an application needs to do its own processing of whichever arguments
395 $(D getopt) did not understand, it can pass the
396 $(D std.getopt.config.passThrough) directive to $(D getopt):
401 std.getopt.config.passThrough,
406 An unrecognized option such as "--baz" will be found untouched in
407 $(D args) after $(D getopt) returns.
409 Help_Information_Generation:
410 If an option string is followed by another string, this string serves as a
411 description for this option. The $(D getopt) function returns a struct of type
412 $(D GetoptResult). This return value contains information about all passed options
413 as well a $(D bool GetoptResult.helpWanted) flag indicating whether information
414 about these options was requested. The $(D getopt) function always adds an option for
415 `--help|-h` to set the flag if the option is seen on the command line.
418 A lone double-dash terminates $(D getopt) gathering. It is used to
419 separate program options from other parameters (e.g., options to be passed
420 to another program). Invoking the example above with $(D "--foo -- --bar")
421 parses foo but leaves "--bar" in $(D args). The double-dash itself is
422 removed from the argument array unless the $(D std.getopt.config.keepEndOfOptions)
425 GetoptResult
getopt(T
...)(ref string
[] args
, T opts
)
427 import std
.exception
: enforce
;
429 "Invalid arguments string passed: program name missing");
433 GetOptException excep
;
434 void[][string
] visitedLongOpts
, visitedShortOpts
;
435 getoptImpl(args
, cfg
, rslt
, excep
, visitedLongOpts
, visitedShortOpts
, opts
);
437 if (!rslt
.helpWanted
&& excep
!is null)
448 auto args
= ["prog", "--foo", "-b"];
452 auto rslt
= getopt(args
, "foo|f", "Some information about foo.", &foo
, "bar|b",
453 "Some help message about bar.", &bar
);
457 defaultGetoptPrinter("Some information about the program.",
463 Configuration options for $(D getopt).
465 You can pass them to $(D getopt) in any position, except in between an option
466 string and its bound pointer.
469 /// Turn case sensitivity on
471 /// Turn case sensitivity off (default)
475 /// Turn bundling off (default)
477 /// Pass unrecognized arguments through
479 /// Signal unrecognized arguments as errors (default)
481 /// Stop at first argument that does not look like an option
482 stopOnFirstNonOption
,
483 /// Do not erase the endOfOptions separator from args
485 /// Make the next option a required option
489 /** The result of the $(D getopt) function.
491 $(D helpWanted) is set if the option `--help` or `-h` was passed to the option parser.
493 struct GetoptResult
{
494 bool helpWanted
; /// Flag indicating if help was requested
495 Option
[] options
; /// All possible options
498 /** Information about an option.
501 string optShort
; /// The short symbol for this option
502 string optLong
; /// The long symbol for this option
503 string help
; /// The description of this option
504 bool required
; /// If a option is required, not passing it will result in an error
507 private pure Option
splitAndGet(string opt
) @trusted nothrow
509 import std
.array
: split
;
510 auto sp
= split(opt
, "|");
514 ret.optShort
= "-" ~ (sp
[0].length
< sp
[1].length ?
516 ret.optLong
= "--" ~ (sp
[0].length
> sp
[1].length ?
519 else if (sp
[0].length
> 1)
521 ret.optLong
= "--" ~ sp
[0];
525 ret.optShort
= "-" ~ sp
[0];
533 auto oshort
= splitAndGet("f");
534 assert(oshort
.optShort
== "-f");
535 assert(oshort
.optLong
== "");
537 auto olong
= splitAndGet("foo");
538 assert(olong
.optShort
== "");
539 assert(olong
.optLong
== "--foo");
541 auto oshortlong
= splitAndGet("f|foo");
542 assert(oshortlong
.optShort
== "-f");
543 assert(oshortlong
.optLong
== "--foo");
545 auto olongshort
= splitAndGet("foo|f");
546 assert(olongshort
.optShort
== "-f");
547 assert(olongshort
.optLong
== "--foo");
551 This function verifies that the variadic parameters passed in getOpt
554 [config override], option, [description], receiver,
556 - config override: a config value, optional
557 - option: a string or a char
558 - description: a string, optional
559 - receiver: a pointer or a callable
561 private template optionValidator(A
...)
563 import std
.format
: format
;
564 import std
.typecons
: staticIota
;
566 enum fmt
= "getopt validator: %s (at position %d)";
567 enum isReceiver(T
) = isPointer
!T ||
(is(T
== function)) ||
(is(T
== delegate));
568 enum isOptionStr(T
) = isSomeString
!T || isSomeChar
!T
;
573 static if (A
.length
> 0)
575 static if (isReceiver
!(A
[0]))
577 msg
= format(fmt
, "first argument must be a string or a config", 0);
579 else static if (!isOptionStr
!(A
[0]) && !is(A
[0] == config
))
581 msg
= format(fmt
, "invalid argument type: " ~ A
[0].stringof
, 0);
583 else foreach (i
; staticIota
!(1, A
.length
))
585 static if (!isReceiver
!(A
[i
]) && !isOptionStr
!(A
[i
]) &&
586 !(is(A
[i
] == config
)))
588 msg
= format(fmt
, "invalid argument type: " ~ A
[i
].stringof
, i
);
591 else static if (isReceiver
!(A
[i
]) && !isOptionStr
!(A
[i
-1]))
593 msg
= format(fmt
, "a receiver can not be preceeded by a receiver", i
);
596 else static if (i
> 1 && isOptionStr
!(A
[i
]) && isOptionStr
!(A
[i
-1])
597 && isSomeString
!(A
[i
-2]))
599 msg
= format(fmt
, "a string can not be preceeded by two strings", i
);
603 static if (!isReceiver
!(A
[$-1]) && !is(A
[$-1] == config
))
605 msg
= format(fmt
, "last argument must be a receiver or a config",
611 enum message
= validator
;
612 alias optionValidator
= message
;
621 alias F
= void function();
623 static assert(optionValidator
!(S
,P
) == "");
624 static assert(optionValidator
!(S
,F
) == "");
625 static assert(optionValidator
!(A
,P
) == "");
626 static assert(optionValidator
!(A
,F
) == "");
628 static assert(optionValidator
!(C
,S
,P
) == "");
629 static assert(optionValidator
!(C
,S
,F
) == "");
630 static assert(optionValidator
!(C
,A
,P
) == "");
631 static assert(optionValidator
!(C
,A
,F
) == "");
633 static assert(optionValidator
!(C
,S
,S
,P
) == "");
634 static assert(optionValidator
!(C
,S
,S
,F
) == "");
635 static assert(optionValidator
!(C
,A
,S
,P
) == "");
636 static assert(optionValidator
!(C
,A
,S
,F
) == "");
638 static assert(optionValidator
!(C
,S
,S
,P
) == "");
639 static assert(optionValidator
!(C
,S
,S
,P
,C
,S
,F
) == "");
640 static assert(optionValidator
!(C
,S
,P
,C
,S
,S
,F
) == "");
642 static assert(optionValidator
!(C
,A
,P
,A
,S
,F
) == "");
643 static assert(optionValidator
!(C
,A
,P
,C
,A
,S
,F
) == "");
645 static assert(optionValidator
!(P
,S
,S
) != "");
646 static assert(optionValidator
!(P
,P
,S
) != "");
647 static assert(optionValidator
!(P
,F
,S
,P
) != "");
648 static assert(optionValidator
!(C
,C
,S
) != "");
649 static assert(optionValidator
!(S
,S
,P
,S
,S
,P
,S
) != "");
650 static assert(optionValidator
!(S
,S
,P
,P
) != "");
651 static assert(optionValidator
!(S
,S
,S
,P
) != "");
653 static assert(optionValidator
!(C
,A
,S
,P
,C
,A
,F
) == "");
654 static assert(optionValidator
!(C
,A
,P
,C
,A
,S
,F
) == "");
657 @system unittest // bugzilla 15914
660 string
[] args
= ["program", "-a"];
661 getopt(args
, config
.passThrough
, 'a', &opt
);
664 args
= ["program", "-a"];
665 getopt(args
, 'a', &opt
);
668 args
= ["program", "-a"];
669 getopt(args
, 'a', "help string", &opt
);
672 args
= ["program", "-a"];
673 getopt(args
, config
.caseSensitive
, 'a', "help string", &opt
);
676 assertThrown(getopt(args
, "", "forgot to put a string", &opt
));
679 private void getoptImpl(T
...)(ref string
[] args
, ref configuration cfg
,
680 ref GetoptResult rslt
, ref GetOptException excep
,
681 void[][string
] visitedLongOpts
, void[][string
] visitedShortOpts
, T opts
)
683 enum validationMessage
= optionValidator
!T
;
684 static assert(validationMessage
== "", validationMessage
);
686 import std
.algorithm
.mutation
: remove
;
687 import std
.conv
: to
;
688 static if (opts
.length
)
690 static if (is(typeof(opts
[0]) : config
))
692 // it's a configuration flag, act on it
693 setConfig(cfg
, opts
[0]);
694 return getoptImpl(args
, cfg
, rslt
, excep
, visitedLongOpts
,
695 visitedShortOpts
, opts
[1 .. $]);
699 // it's an option string
700 auto option
= to
!string(opts
[0]);
701 if (option
.length
== 0)
703 excep
= new GetOptException("An option name may not be an empty string", excep
);
706 Option optionHelp
= splitAndGet(option
);
707 optionHelp
.required
= cfg
.required
;
709 if (optionHelp
.optLong
.length
)
711 assert(optionHelp
.optLong
!in visitedLongOpts
,
712 "Long option " ~ optionHelp
.optLong
~ " is multiply defined");
714 visitedLongOpts
[optionHelp
.optLong
] = [];
717 if (optionHelp
.optShort
.length
)
719 assert(optionHelp
.optShort
!in visitedShortOpts
,
720 "Short option " ~ optionHelp
.optShort
721 ~ " is multiply defined");
723 visitedShortOpts
[optionHelp
.optShort
] = [];
726 static if (is(typeof(opts
[1]) : string
))
728 auto receiver
= opts
[2];
729 optionHelp
.help
= opts
[1];
730 immutable lowSliceIdx
= 3;
734 auto receiver
= opts
[1];
735 immutable lowSliceIdx
= 2;
738 rslt
.options
~= optionHelp
;
741 // Handle options of the form --blah+
742 if (option
.length
&& option
[$ - 1] == autoIncrementChar
)
744 option
= option
[0 .. $ - 1];
748 bool optWasHandled
= handleOption(option
, receiver
, args
, cfg
, incremental
);
750 if (cfg
.required
&& !optWasHandled
)
752 excep
= new GetOptException("Required option "
753 ~ option
~ " was not supplied", excep
);
755 cfg
.required
= false;
757 getoptImpl(args
, cfg
, rslt
, excep
, visitedLongOpts
,
758 visitedShortOpts
, opts
[lowSliceIdx
.. $]);
763 // no more options to look for, potentially some arguments left
764 for (size_t i
= 1; i
< args
.length
;)
767 if (endOfOptions
.length
&& a
== endOfOptions
)
769 // Consume the "--" if keepEndOfOptions is not specified
770 if (!cfg
.keepEndOfOptions
)
771 args
= args
.remove(i
);
774 if (!a
.length || a
[0] != optionChar
)
777 if (cfg
.stopOnFirstNonOption
) break;
781 if (a
== "--help" || a
== "-h")
783 rslt
.helpWanted
= true;
784 args
= args
.remove(i
);
787 if (!cfg
.passThrough
)
789 throw new GetOptException("Unrecognized option "~a
, excep
);
795 helpOpt
.optShort
= "-h";
796 helpOpt
.optLong
= "--help";
797 helpOpt
.help
= "This help information.";
798 rslt
.options
~= helpOpt
;
802 private bool handleOption(R
)(string option
, R receiver
, ref string
[] args
,
803 ref configuration cfg
, bool incremental
)
805 import std
.algorithm
.iteration
: map
, splitter
;
806 import std
.ascii
: isAlpha
;
807 import std
.conv
: text
, to
;
808 // Scan arguments looking for a match for this option
810 for (size_t i
= 1; i
< args
.length
; )
813 if (endOfOptions
.length
&& a
== endOfOptions
) break;
814 if (cfg
.stopOnFirstNonOption
&& (!a
.length || a
[0] != optionChar
))
816 // first non-option is end of options
819 // Unbundle bundled arguments if necessary
820 if (cfg
.bundling
&& a
.length
> 2 && a
[0] == optionChar
&&
824 foreach (j
, dchar c
; a
[1 .. $])
826 // If the character is not alpha, stop right there. This allows
827 // e.g. -j100 to work as "pass argument 100 to option -j".
832 expanded
~= a
[j
+ 1 .. $];
835 expanded
~= text(optionChar
, c
);
837 args
= args
[0 .. i
] ~ expanded
~ args
[i
+ 1 .. $];
842 if (!optMatch(a
, option
, val
, cfg
))
851 // from here on, commit to eat args[i]
852 // (and potentially args[i + 1] too, but that comes later)
853 args
= args
[0 .. i
] ~ args
[i
+ 1 .. $];
855 static if (is(typeof(*receiver
) == bool))
859 // parse '--b=true/false'
860 *receiver
= to
!(typeof(*receiver
))(val
);
864 // no argument means set it to true
870 import std
.exception
: enforce
;
871 // non-boolean option, which might include an argument
872 //enum isCallbackWithOneParameter = is(typeof(receiver("")) : void);
873 enum isCallbackWithLessThanTwoParameters
=
874 (is(typeof(receiver
) == delegate) ||
is(typeof(*receiver
) == function)) &&
875 !is(typeof(receiver("", "")));
876 if (!isCallbackWithLessThanTwoParameters
&& !(val
.length
) && !incremental
)
878 // Eat the next argument too. Check to make sure there's one
879 // to be eaten first, though.
880 enforce(i
< args
.length
,
881 "Missing value for argument " ~ a
~ ".");
883 args
= args
[0 .. i
] ~ args
[i
+ 1 .. $];
885 static if (is(typeof(*receiver
) == enum))
887 *receiver
= to
!(typeof(*receiver
))(val
);
889 else static if (is(typeof(*receiver
) : real))
892 if (incremental
) ++*receiver
;
893 else *receiver
= to
!(typeof(*receiver
))(val
);
895 else static if (is(typeof(*receiver
) == string
))
898 *receiver
= to
!(typeof(*receiver
))(val
);
900 else static if (is(typeof(receiver
) == delegate) ||
901 is(typeof(*receiver
) == function))
903 static if (is(typeof(receiver("", "")) : void))
905 // option with argument
906 receiver(option
, val
);
908 else static if (is(typeof(receiver("")) : void))
910 static assert(is(typeof(receiver("")) : void));
911 // boolean-style receiver
916 static assert(is(typeof(receiver()) : void));
917 // boolean-style receiver without argument
921 else static if (isArray
!(typeof(*receiver
)))
924 import std
.range
: ElementEncodingType
;
925 alias E
= ElementEncodingType
!(typeof(*receiver
));
929 *receiver
~= to
!E(val
);
933 foreach (elem
; val
.splitter(arraySep
).map
!(a
=> to
!E(a
))())
937 else static if (isAssociativeArray
!(typeof(*receiver
)))
940 alias K
= typeof(receiver
.keys
[0]);
941 alias V
= typeof(receiver
.values
[0]);
943 import std
.range
: only
;
944 import std
.string
: indexOf
;
945 import std
.typecons
: Tuple
, tuple
;
947 static Tuple
!(K
, V
) getter(string input
)
949 auto j
= indexOf(input
, assignChar
);
950 enforce
!GetOptException(j
!= -1, "Could not find '"
951 ~ to
!string(assignChar
) ~ "' in argument '" ~ input
~ "'.");
952 auto key
= input
[0 .. j
];
953 auto value
= input
[j
+ 1 .. $];
954 return tuple(to
!K(key
), to
!V(value
));
957 static void setHash(Range
)(R receiver
, Range range
)
959 foreach (k
, v
; range
.map
!getter
)
964 setHash(receiver
, val
.only
);
966 setHash(receiver
, val
.splitter(arraySep
));
969 static assert(false, "getopt does not know how to handle the type " ~ typeof(receiver
).stringof
);
979 import std
.algorithm
.searching
: startsWith
;
983 string
[string
] mapping
;
984 immutable as
= arraySep
;
988 string
[] args
= ["testProgram", "-m", "a=b,c=\"d,e,f\""];
989 args
.getopt("m", &mapping
);
990 assert(false, "Exception not thrown");
992 catch (GetOptException goe
)
993 assert(goe
.msg
.startsWith("Could not find"));
996 // 5316 - arrays with arraySep
1002 scope (exit
) arraySep
= "";
1005 auto args
= ["program.name", "-nfoo,bar,baz"];
1006 getopt(args
, "name|n", &names
);
1007 assert(names
== ["foo", "bar", "baz"], to
!string(names
));
1010 args
= ["program.name", "-n", "foo,bar,baz"];
1011 getopt(args
, "name|n", &names
);
1012 assert(names
== ["foo", "bar", "baz"], to
!string(names
));
1015 args
= ["program.name", "--name=foo,bar,baz"];
1016 getopt(args
, "name|n", &names
);
1017 assert(names
== ["foo", "bar", "baz"], to
!string(names
));
1020 args
= ["program.name", "--name", "foo,bar,baz"];
1021 getopt(args
, "name|n", &names
);
1022 assert(names
== ["foo", "bar", "baz"], to
!string(names
));
1025 // 5316 - associative arrays with arraySep
1031 scope (exit
) arraySep
= "";
1034 values
= values
.init
;
1035 auto args
= ["program.name", "-vfoo=0,bar=1,baz=2"];
1036 getopt(args
, "values|v", &values
);
1037 assert(values
== ["foo":0, "bar":1, "baz":2], to
!string(values
));
1039 values
= values
.init
;
1040 args
= ["program.name", "-v", "foo=0,bar=1,baz=2"];
1041 getopt(args
, "values|v", &values
);
1042 assert(values
== ["foo":0, "bar":1, "baz":2], to
!string(values
));
1044 values
= values
.init
;
1045 args
= ["program.name", "--values=foo=0,bar=1,baz=2"];
1046 getopt(args
, "values|t", &values
);
1047 assert(values
== ["foo":0, "bar":1, "baz":2], to
!string(values
));
1049 values
= values
.init
;
1050 args
= ["program.name", "--values", "foo=0,bar=1,baz=2"];
1051 getopt(args
, "values|v", &values
);
1052 assert(values
== ["foo":0, "bar":1, "baz":2], to
!string(values
));
1056 The option character (default '-').
1058 Defaults to '-' but it can be assigned to prior to calling $(D getopt).
1060 dchar optionChar
= '-';
1063 The string that conventionally marks the end of all options (default '--').
1065 Defaults to "--" but can be assigned to prior to calling $(D getopt). Assigning an
1066 empty string to $(D endOfOptions) effectively disables it.
1068 string endOfOptions
= "--";
1071 The assignment character used in options with parameters (default '=').
1073 Defaults to '=' but can be assigned to prior to calling $(D getopt).
1075 dchar assignChar
= '=';
1078 The string used to separate the elements of an array or associative array
1079 (default is "" which means the elements are separated by whitespace).
1081 Defaults to "" but can be assigned to prior to calling $(D getopt).
1083 string arraySep
= "";
1085 private enum autoIncrementChar
= '+';
1087 private struct configuration
1089 import std
.bitmanip
: bitfields
;
1091 bool, "caseSensitive", 1,
1092 bool, "bundling", 1,
1093 bool, "passThrough", 1,
1094 bool, "stopOnFirstNonOption", 1,
1095 bool, "keepEndOfOptions", 1,
1096 bool, "required", 1,
1100 private bool optMatch(string arg
, string optPattern
, ref string value
,
1101 configuration cfg
) @safe
1103 import std
.array
: split
;
1104 import std
.string
: indexOf
;
1105 import std
.uni
: toUpper
;
1106 //writeln("optMatch:\n ", arg, "\n ", optPattern, "\n ", value);
1107 //scope(success) writeln("optMatch result: ", value);
1108 if (arg
.length
< 2 || arg
[0] != optionChar
) return false;
1109 // yank the leading '-'
1111 immutable isLong
= arg
.length
> 1 && arg
[0] == optionChar
;
1112 //writeln("isLong: ", isLong);
1113 // yank the second '-' if present
1114 if (isLong
) arg
= arg
[1 .. $];
1115 immutable eqPos
= indexOf(arg
, assignChar
);
1116 if (isLong
&& eqPos
>= 0)
1118 // argument looks like --opt=value
1119 value
= arg
[eqPos
+ 1 .. $];
1120 arg
= arg
[0 .. eqPos
];
1124 if (!isLong
&& eqPos
== 1)
1126 // argument looks like -o=value
1127 value
= arg
[2 .. $];
1131 if (!isLong
&& !cfg
.bundling
)
1133 // argument looks like -ovalue and there's no bundling
1134 value
= arg
[1 .. $];
1139 // argument looks like --opt, or -oxyz with bundling
1143 //writeln("Arg: ", arg, " pattern: ", optPattern, " value: ", value);
1145 const variants
= split(optPattern
, "|");
1146 foreach (v
; variants
)
1148 //writeln("Trying variant: ", v, " against ", arg);
1149 if (arg
== v ||
!cfg
.caseSensitive
&& toUpper(arg
) == toUpper(v
))
1151 if (cfg
.bundling
&& !isLong
&& v
.length
== 1
1152 && indexOf(arg
, v
) >= 0)
1154 //writeln("success");
1161 private void setConfig(ref configuration cfg
, config option
) @safe pure nothrow @nogc
1163 final switch (option
)
1165 case config
.caseSensitive
: cfg
.caseSensitive
= true; break;
1166 case config
.caseInsensitive
: cfg
.caseSensitive
= false; break;
1167 case config
.bundling
: cfg
.bundling
= true; break;
1168 case config
.noBundling
: cfg
.bundling
= false; break;
1169 case config
.passThrough
: cfg
.passThrough
= true; break;
1170 case config
.noPassThrough
: cfg
.passThrough
= false; break;
1171 case config
.required
: cfg
.required
= true; break;
1172 case config
.stopOnFirstNonOption
:
1173 cfg
.stopOnFirstNonOption
= true; break;
1174 case config
.keepEndOfOptions
:
1175 cfg
.keepEndOfOptions
= true; break;
1185 string
[] args
= ["program.name", "--paranoid", "--paranoid", "--paranoid"];
1186 getopt(args
, "paranoid+", ¶noid
);
1187 assert(paranoid
== 5, to
!(string
)(paranoid
));
1189 enum Color
{ no
, yes
}
1191 args
= ["program.name", "--color=yes",];
1192 getopt(args
, "color", &color
);
1193 assert(color
, to
!(string
)(color
));
1196 args
= ["program.name", "--color", "yes",];
1197 getopt(args
, "color", &color
);
1198 assert(color
, to
!(string
)(color
));
1200 string data
= "file.dat";
1202 bool verbose
= false;
1203 args
= ["program.name", "--length=5", "--file", "dat.file", "--verbose"];
1208 "verbose", &verbose
);
1209 assert(args
.length
== 1);
1210 assert(data
== "dat.file");
1211 assert(length
== 5);
1215 string
[] outputFiles
;
1216 args
= ["program.name", "--output=myfile.txt", "--output", "yourfile.txt"];
1217 getopt(args
, "output", &outputFiles
);
1218 assert(outputFiles
.length
== 2
1219 && outputFiles
[0] == "myfile.txt" && outputFiles
[1] == "yourfile.txt");
1223 args
= ["program.name", "--output", "myfile.txt,yourfile.txt"];
1224 getopt(args
, "output", &outputFiles
);
1225 assert(outputFiles
.length
== 2
1226 && outputFiles
[0] == "myfile.txt" && outputFiles
[1] == "yourfile.txt");
1230 [["program.name", "--tune=alpha=0.5", "--tune", "beta=0.6"],
1231 ["program.name", "--tune=alpha=0.5,beta=0.6"],
1232 ["program.name", "--tune", "alpha=0.5,beta=0.6"]])
1235 double[string
] tuningParms
;
1236 getopt(testArgs
, "tune", &tuningParms
);
1237 assert(testArgs
.length
== 1);
1238 assert(tuningParms
.length
== 2);
1239 assert(approxEqual(tuningParms
["alpha"], 0.5));
1240 assert(approxEqual(tuningParms
["beta"], 0.6));
1244 uint verbosityLevel
= 1;
1245 void myHandler(string option
)
1247 if (option
== "quiet")
1253 assert(option
== "verbose");
1257 args
= ["program.name", "--quiet"];
1258 getopt(args
, "verbose", &myHandler
, "quiet", &myHandler
);
1259 assert(verbosityLevel
== 0);
1260 args
= ["program.name", "--verbose"];
1261 getopt(args
, "verbose", &myHandler
, "quiet", &myHandler
);
1262 assert(verbosityLevel
== 2);
1265 void myHandler2(string option
, string value
)
1267 assert(option
== "verbose");
1270 args
= ["program.name", "--verbose", "2"];
1271 getopt(args
, "verbose", &myHandler2
);
1272 assert(verbosityLevel
== 2);
1279 args
= ["program.name", "--verbose"];
1280 getopt(args
, "verbose", &myHandler3
);
1281 assert(verbosityLevel
== 2);
1284 args
= ["program.name", "--foo", "--bAr"];
1286 std
.getopt
.config
.caseSensitive
,
1287 std
.getopt
.config
.passThrough
,
1290 assert(args
[1] == "--bAr");
1292 // test stopOnFirstNonOption
1294 args
= ["program.name", "--foo", "nonoption", "--bar"];
1297 std
.getopt
.config
.stopOnFirstNonOption
,
1300 assert(foo
&& !bar
&& args
[1] == "nonoption" && args
[2] == "--bar");
1302 args
= ["program.name", "--foo", "nonoption", "--zab"];
1305 std
.getopt
.config
.stopOnFirstNonOption
,
1308 assert(foo
&& !bar
&& args
[1] == "nonoption" && args
[2] == "--zab");
1310 args
= ["program.name", "--fb1", "--fb2=true", "--tb1=false"];
1313 getopt(args
, "fb1", &fb1
, "fb2", &fb2
, "tb1", &tb1
);
1314 assert(fb1
&& fb2
&& !tb1
);
1316 // test keepEndOfOptions
1318 args
= ["program.name", "--foo", "nonoption", "--bar", "--", "--baz"];
1320 std
.getopt
.config
.keepEndOfOptions
,
1323 assert(args
== ["program.name", "nonoption", "--", "--baz"]);
1325 // Ensure old behavior without the keepEndOfOptions
1327 args
= ["program.name", "--foo", "nonoption", "--bar", "--", "--baz"];
1331 assert(args
== ["program.name", "nonoption", "--baz"]);
1333 // test function callbacks
1335 static class MyEx
: Exception
1337 this() { super(""); }
1338 this(string option
) { this(); this.option
= option
; }
1339 this(string option
, string value
) { this(option
); this.value
= value
; }
1345 static void myStaticHandler1() { throw new MyEx(); }
1346 args
= ["program.name", "--verbose"];
1347 try { getopt(args
, "verbose", &myStaticHandler1
); assert(0); }
1348 catch (MyEx ex
) { assert(ex
.option
is null && ex
.value
is null); }
1350 static void myStaticHandler2(string option
) { throw new MyEx(option
); }
1351 args
= ["program.name", "--verbose"];
1352 try { getopt(args
, "verbose", &myStaticHandler2
); assert(0); }
1353 catch (MyEx ex
) { assert(ex
.option
== "verbose" && ex
.value
is null); }
1355 static void myStaticHandler3(string option
, string value
) { throw new MyEx(option
, value
); }
1356 args
= ["program.name", "--verbose", "2"];
1357 try { getopt(args
, "verbose", &myStaticHandler3
); assert(0); }
1358 catch (MyEx ex
) { assert(ex
.option
== "verbose" && ex
.value
== "2"); }
1361 @safe unittest // @safe std.getopt.config option use
1364 string
[] args
= ["program", "--inc-x", "--inc-x"];
1366 std
.getopt
.config
.caseSensitive
,
1367 "inc-x", "Add one to x", delegate void() { x
++; });
1373 // From bugzilla 2142
1374 bool f_linenum
, f_filename
;
1375 string
[] args
= [ "", "-nl" ];
1379 std
.getopt
.config
.bundling
,
1380 //std.getopt.config.caseSensitive,
1381 "linenum|l", &f_linenum
,
1382 "filename|n", &f_filename
1390 // From bugzilla 6887
1392 string
[] args
= ["", "-pa"];
1393 getopt(args
, "p", &p
);
1394 assert(p
.length
== 1);
1395 assert(p
[0] == "a");
1400 // From bugzilla 6888
1402 auto args
= ["", "-t", "a=1"];
1403 getopt(args
, "t", &foo
);
1404 assert(foo
== ["a":1]);
1409 // From bugzilla 9583
1411 auto args
= ["prog", "--opt=123", "--", "--a", "--b", "--c"];
1412 getopt(args
, "opt", &opt
);
1413 assert(args
== ["prog", "--a", "--b", "--c"]);
1419 auto args
= ["prog", "-thello", "-dbar=baz"];
1420 getopt(args
, "t", &foo
, "d", &bar
);
1421 assert(foo
== "hello");
1422 assert(bar
== "bar=baz");
1424 // From bugzilla 5762
1426 args
= ["prog", "-a-0x12"];
1427 getopt(args
, config
.bundling
, "a|addr", &a
);
1428 assert(a
== "-0x12", a
);
1429 args
= ["prog", "--addr=-0x12"];
1430 getopt(args
, config
.bundling
, "a|addr", &a
);
1431 assert(a
== "-0x12");
1433 // From https://d.puremagic.com/issues/show_bug.cgi?id=11764
1434 args
= ["main", "-test"];
1436 args
.getopt(config
.passThrough
, "opt", &opt
);
1437 assert(args
== ["main", "-test"]);
1439 // From https://issues.dlang.org/show_bug.cgi?id=15220
1440 args
= ["main", "-o=str"];
1442 args
.getopt("o", &o
);
1445 args
= ["main", "-o=str"];
1447 args
.getopt(config
.bundling
, "o", &o
);
1451 @system unittest // 5228
1454 import std
.exception
;
1456 auto args
= ["prog", "--foo=bar"];
1458 assertThrown
!GetOptException(getopt(args
, "abc", &abc
));
1460 args
= ["prog", "--abc=string"];
1461 assertThrown
!ConvException(getopt(args
, "abc", &abc
));
1464 @system unittest // From bugzilla 7693
1466 import std
.exception
;
1473 auto args
= ["prog", "--foo=barZZZ"];
1475 assertThrown(getopt(args
, "foo", &foo
));
1476 args
= ["prog", "--foo=bar"];
1477 assertNotThrown(getopt(args
, "foo", &foo
));
1478 args
= ["prog", "--foo", "barZZZ"];
1479 assertThrown(getopt(args
, "foo", &foo
));
1480 args
= ["prog", "--foo", "baz"];
1481 assertNotThrown(getopt(args
, "foo", &foo
));
1484 @system unittest // same bug as 7693 only for bool
1486 import std
.exception
;
1488 auto args
= ["prog", "--foo=truefoobar"];
1490 assertThrown(getopt(args
, "foo", &foo
));
1491 args
= ["prog", "--foo"];
1492 getopt(args
, "foo", &foo
);
1499 auto args
= ["prog", "--foo"];
1500 getopt(args
, "foo", &foo
);
1508 auto args
= ["prog", "--foo", "-b"];
1509 getopt(args
, config
.caseInsensitive
,"foo|f", "Some foo", &foo
,
1510 config
.caseSensitive
, "bar|b", "Some bar", &bar
);
1519 auto args
= ["prog", "-b", "--foo", "-z"];
1520 getopt(args
, config
.caseInsensitive
, config
.required
, "foo|f", "Some foo",
1521 &foo
, config
.caseSensitive
, "bar|b", "Some bar", &bar
,
1522 config
.passThrough
);
1529 import std
.exception
;
1533 auto args
= ["prog", "-b", "-z"];
1534 assertThrown(getopt(args
, config
.caseInsensitive
, config
.required
, "foo|f",
1535 "Some foo", &foo
, config
.caseSensitive
, "bar|b", "Some bar", &bar
,
1536 config
.passThrough
));
1541 import std
.exception
;
1545 auto args
= ["prog", "--foo", "-z"];
1546 assertNotThrown(getopt(args
, config
.caseInsensitive
, config
.required
,
1547 "foo|f", "Some foo", &foo
, config
.caseSensitive
, "bar|b", "Some bar",
1548 &bar
, config
.passThrough
));
1556 auto args
= ["prog", "-f"];
1557 auto r
= getopt(args
, config
.caseInsensitive
, "help|f", "Some foo", &foo
);
1559 assert(!r
.helpWanted
);
1562 @safe unittest // implicit help option without config.passThrough
1564 string
[] args
= ["program", "--help"];
1565 auto r
= getopt(args
);
1566 assert(r
.helpWanted
);
1569 // Issue 13316 - std.getopt: implicit help option breaks the next argument
1572 string
[] args
= ["program", "--help", "--", "something"];
1574 assert(args
== ["program", "something"]);
1576 args
= ["program", "--help", "--"];
1578 assert(args
== ["program"]);
1581 args
= ["program", "--help", "nonoption", "--option"];
1582 getopt(args
, config
.stopOnFirstNonOption
, "option", &b
);
1583 assert(args
== ["program", "nonoption", "--option"]);
1586 // Issue 13317 - std.getopt: endOfOptions broken when it doesn't look like an option
1589 auto endOfOptionsBackup
= endOfOptions
;
1590 scope(exit
) endOfOptions
= endOfOptionsBackup
;
1591 endOfOptions
= "endofoptions";
1592 string
[] args
= ["program", "endofoptions", "--option"];
1594 getopt(args
, "option", &b
);
1596 assert(args
== ["program", "--option"]);
1599 /** This function prints the passed $(D Option)s and text in an aligned manner on $(D stdout).
1601 The passed text will be printed first, followed by a newline, then the short
1602 and long version of every option will be printed. The short and long version
1603 will be aligned to the longest option of every $(D Option) passed. If the option
1604 is required, then "Required:" will be printed after the long version of the
1605 $(D Option). If a help message is present it will be printed next. The format is
1606 illustrated by this code:
1611 writefln("%*s %*s%s%s", lengthOfLongestShortOption, it.optShort,
1612 lengthOfLongestLongOption, it.optLong,
1613 it.required ? " Required: " : " ", it.help);
1618 text = The text to printed at the beginning of the help output.
1619 opt = The $(D Option) extracted from the $(D getopt) parameter.
1621 void defaultGetoptPrinter(string text
, Option
[] opt
)
1623 import std
.stdio
: stdout
;
1625 defaultGetoptFormatter(stdout
.lockingTextWriter(), text
, opt
);
1628 /** This function writes the passed text and $(D Option) into an output range
1629 in the manner described in the documentation of function
1630 $(D defaultGetoptPrinter).
1633 output = The output range used to write the help information.
1634 text = The text to print at the beginning of the help output.
1635 opt = The $(D Option) extracted from the $(D getopt) parameter.
1637 void defaultGetoptFormatter(Output
)(Output output
, string text
, Option
[] opt
)
1639 import std
.algorithm
.comparison
: min
, max
;
1640 import std
.format
: formattedWrite
;
1642 output
.formattedWrite("%s\n", text
);
1645 bool hasRequired
= false;
1648 ls
= max(ls
, it
.optShort
.length
);
1649 ll
= max(ll
, it
.optLong
.length
);
1651 hasRequired
= hasRequired || it
.required
;
1654 string re
= " Required: ";
1658 output
.formattedWrite("%*s %*s%*s%s\n", ls
, it
.optShort
, ll
, it
.optLong
,
1659 hasRequired ? re
.length
: 1, it
.required ? re
: " ", it
.help
);
1670 auto args
= ["prog", "--foo"];
1671 auto t
= getopt(args
, "foo|f", "Help", &a
);
1673 auto app
= appender
!string();
1674 defaultGetoptFormatter(app
, "Some Text", t
.options
);
1676 string helpMsg
= app
.data
;
1678 assert(helpMsg
.length
);
1679 assert(helpMsg
.count("\n") == 3, to
!string(helpMsg
.count("\n")) ~ " "
1681 assert(helpMsg
.indexOf("--foo") != -1);
1682 assert(helpMsg
.indexOf("-f") != -1);
1683 assert(helpMsg
.indexOf("-h") != -1);
1684 assert(helpMsg
.indexOf("--help") != -1);
1685 assert(helpMsg
.indexOf("Help") != -1);
1687 string wanted
= "Some Text\n-f --foo Help\n-h --help This help "
1689 assert(wanted
== helpMsg
);
1698 auto args
= ["prog", "--foo"];
1699 auto t
= getopt(args
, config
.required
, "foo|f", "Help", &a
);
1701 auto app
= appender
!string();
1702 defaultGetoptFormatter(app
, "Some Text", t
.options
);
1704 string helpMsg
= app
.data
;
1706 assert(helpMsg
.length
);
1707 assert(helpMsg
.count("\n") == 3, to
!string(helpMsg
.count("\n")) ~ " "
1709 assert(helpMsg
.indexOf("Required:") != -1);
1710 assert(helpMsg
.indexOf("--foo") != -1);
1711 assert(helpMsg
.indexOf("-f") != -1);
1712 assert(helpMsg
.indexOf("-h") != -1);
1713 assert(helpMsg
.indexOf("--help") != -1);
1714 assert(helpMsg
.indexOf("Help") != -1);
1716 string wanted
= "Some Text\n-f --foo Required: Help\n-h --help "
1717 ~ " This help information.\n";
1718 assert(wanted
== helpMsg
, helpMsg
~ wanted
);
1721 @system unittest // Issue 14724
1724 auto args
= ["prog", "--help"];
1728 rslt
= getopt(args
, config
.required
, "foo|f", "bool a", &a
);
1732 enum errorMsg
= "If the request for help was passed required options" ~
1734 assert(false, errorMsg
);
1737 assert(rslt
.helpWanted
);
1740 // throw on duplicate options
1743 import core
.exception
;
1744 auto args
= ["prog", "--abc", "1"];
1746 assertThrown
!AssertError(getopt(args
, "abc", &abc
, "abc", &abc
));
1747 assertThrown
!AssertError(getopt(args
, "abc|a", &abc
, "def|a", &def
));
1748 assertNotThrown
!AssertError(getopt(args
, "abc", &abc
, "def", &def
));
1751 @system unittest // Issue 17327 repeated option use
1755 string
[] args
= ["program", "--num", "3"];
1756 getopt(args
, "n|num", &num
);
1759 args
= ["program", "--num", "3", "--num", "5"];
1760 getopt(args
, "n|num", &num
);
1763 args
= ["program", "--n", "3", "--num", "5", "-n", "-7"];
1764 getopt(args
, "n|num", &num
);
1767 void add1() { num
++; }
1768 void add2(string option
) { num
+= 2; }
1769 void addN(string option
, string value
)
1771 import std
.conv
: to
;
1772 num
+= value
.to
!long;
1776 args
= ["program", "--add1", "--add2", "--add1", "--add", "5", "--add2", "--add", "10"];
1778 "add1", "Add 1 to num", &add1
,
1779 "add2", "Add 2 to num", &add2
,
1780 "add", "Add N to num", &addN
,);
1784 args
= ["program", "--flag"];
1785 getopt(args
, "f|flag", "Boolean", &flag
);
1789 args
= ["program", "-f", "-f"];
1790 getopt(args
, "f|flag", "Boolean", &flag
);
1794 args
= ["program", "--flag=true", "--flag=false"];
1795 getopt(args
, "f|flag", "Boolean", &flag
);
1799 args
= ["program", "--flag=true", "--flag=false", "-f"];
1800 getopt(args
, "f|flag", "Boolean", &flag
);
1804 @safe unittest // Delegates as callbacks
1806 alias TwoArgOptionHandler
= void delegate(string option
, string value
) @safe;
1808 TwoArgOptionHandler
makeAddNHandler(ref long dest
)
1810 void addN(ref long dest
, string n
)
1812 import std
.conv
: to
;
1816 return (option
, value
) => addN(dest
, value
);
1823 ["program", "--x-plus-1", "--x-plus-1", "--x-plus-5", "--x-plus-n", "10",
1824 "--y-plus-n", "25", "--y-plus-7", "--y-plus-n", "15", "--y-plus-3"];
1827 "x-plus-1", "Add one to x", delegate void() { x
+= 1; },
1828 "x-plus-5", "Add five to x", delegate void(string option
) { x
+= 5; },
1829 "x-plus-n", "Add NUM to x", makeAddNHandler(x
),
1830 "y-plus-7", "Add seven to y", delegate void() { y
+= 7; },
1831 "y-plus-3", "Add three to y", delegate void(string option
) { y
+= 3; },
1832 "y-plus-n", "Add NUM to x", makeAddNHandler(y
),);
1838 @system unittest // Hyphens at the start of option values; Issue 17650
1840 auto args
= ["program", "-m", "-5", "-n", "-50", "-c", "-", "-f", "-"];
1848 "m|mm", "integer", &m
,
1849 "n|nn", "integer", &n
,
1850 "c|cc", "character", &c
,
1851 "f|file", "filename or hyphen for stdin", &f
);