1 // Written in the D programming language.
4 Processing of command line options.
6 The getopt module implements a `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
17 $(HTTPS perldoc.perl.org/Getopt/Long.html, Getopt::Long) module. The syntax of
18 D's `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 `std.getopt.config.passThrough` was not present.)
39 $(LI A command-line option was not found, and
40 `std.getopt.config.required` was present.)
41 $(LI A callback option is missing a value.)
44 class GetOptException
: Exception
46 mixin basicExceptionCtors
;
49 static assert(is(typeof(new GetOptException("message"))));
50 static assert(is(typeof(new GetOptException("message", Exception
.init
))));
53 Parse and remove command line options from a string array.
60 string data = "file.dat";
63 enum Color { no, yes };
66 void main(string[] args)
68 auto helpInformation = getopt(
70 "length", &length, // numeric
71 "file", &data, // string
72 "verbose", &verbose, // flag
73 "color", "Information about this color", &color); // enum
76 if (helpInformation.helpWanted)
78 defaultGetoptPrinter("Some information about the program.",
79 helpInformation.options);
84 The `getopt` function takes a reference to the command line
85 (as received by `main`) as its first argument, and an
86 unbounded number of pairs of strings and pointers. Each string is an
87 option meant to "fill" the value referenced by the pointer to its
88 right (the "bound" pointer). The option string in the call to
89 `getopt` should not start with a dash.
91 In all cases, the command-line options that were parsed and used by
92 `getopt` are removed from `args`. Whatever in the
93 arguments did not look like an option is left in `args` for
94 further processing by the program. Values that were unaffected by the
95 options are not touched, so a common idiom is to initialize options
96 to their defaults and then invoke `getopt`. If a
97 command-line argument is recognized as an option with a parameter and
98 the parameter cannot be parsed properly (e.g., a number is expected
99 but not present), a `ConvException` exception is thrown.
100 If `std.getopt.config.passThrough` was not passed to `getopt`
101 and an unrecognized command-line argument is found, or if a required
102 argument is missing a `GetOptException` is thrown.
104 Depending on the type of the pointer being bound, `getopt`
105 recognizes the following kinds of options:
108 $(LI $(I Boolean options). A lone argument sets the option to `true`.
109 Additionally $(B true) or $(B false) can be set within the option separated
113 bool verbose = false, debugging = true;
114 getopt(args, "verbose", &verbose, "debug", &debugging);
117 To set `verbose` to `true`, invoke the program with either
118 `--verbose` or `--verbose=true`.
120 To set `debugging` to `false`, invoke the program with
124 $(LI $(I Numeric options.) If an option is bound to a numeric type, a
125 number is expected as the next option, or right within the option separated
130 getopt(args, "timeout", &timeout);
133 To set `timeout` to `5`, invoke the program with either
134 `--timeout=5` or $(D --timeout 5).
137 $(LI $(I Incremental options.) If an option name has a "+" suffix and is
138 bound to a numeric type, then the option's value tracks the number of times
139 the option occurred on the command line:
143 getopt(args, "paranoid+", ¶noid);
146 Invoking the program with "--paranoid --paranoid --paranoid" will set $(D
147 paranoid) to 3. Note that an incremental option never expects a parameter,
148 e.g., in the command line "--paranoid 42 --paranoid", the "42" does not set
149 `paranoid` to 42; instead, `paranoid` is set to 2 and "42" is not
150 considered as part of the normal program arguments.
153 $(LI $(I Enum options.) If an option is bound to an enum, an enum symbol as
154 a string is expected as the next option, or right within the option
155 separated with an "=" sign:
158 enum Color { no, yes };
159 Color color; // default initialized to Color.no
160 getopt(args, "color", &color);
163 To set `color` to `Color.yes`, invoke the program with either
164 `--color=yes` or $(D --color yes).
167 $(LI $(I String options.) If an option is bound to a string, a string is
168 expected as the next option, or right within the option separated with an
173 getopt(args, "output", &outputFile);
176 Invoking the program with "--output=myfile.txt" or "--output myfile.txt"
177 will set `outputFile` to "myfile.txt". If you want to pass a string
178 containing spaces, you need to use the quoting that is appropriate to your
179 shell, e.g. --output='my file.txt'.
182 $(LI $(I Array options.) If an option is bound to an array, a new element
183 is appended to the array each time the option occurs:
186 string[] outputFiles;
187 getopt(args, "output", &outputFiles);
190 Invoking the program with "--output=myfile.txt --output=yourfile.txt" or
191 "--output myfile.txt --output yourfile.txt" will set `outputFiles` to
192 $(D [ "myfile.txt", "yourfile.txt" ]).
194 Alternatively you can set $(LREF arraySep) to allow multiple elements in
198 string[] outputFiles;
199 arraySep = ","; // defaults to "", meaning one element per parameter
200 getopt(args, "output", &outputFiles);
203 With the above code you can invoke the program with
204 "--output=myfile.txt,yourfile.txt", or "--output myfile.txt,yourfile.txt".)
206 $(LI $(I Hash options.) If an option is bound to an associative array, a
207 string of the form "name=value" is expected as the next option, or right
208 within the option separated with an "=" sign:
211 double[string] tuningParms;
212 getopt(args, "tune", &tuningParms);
215 Invoking the program with e.g. "--tune=alpha=0.5 --tune beta=0.6" will set
216 `tuningParms` to [ "alpha" : 0.5, "beta" : 0.6 ].
218 Alternatively you can set $(LREF arraySep) as the element separator:
221 double[string] tuningParms;
222 arraySep = ","; // defaults to "", meaning one element per parameter
223 getopt(args, "tune", &tuningParms);
226 With the above code you can invoke the program with
227 "--tune=alpha=0.5,beta=0.6", or "--tune alpha=0.5,beta=0.6".
229 In general, the keys and values can be of any parsable types.
232 $(LI $(I Callback options.) An option can be bound to a function or
233 delegate with the signature $(D void function()), $(D void function(string
234 option)), $(D void function(string option, string value)), or their
235 delegate equivalents.
238 $(LI If the callback doesn't take any arguments, the callback is
239 invoked whenever the option is seen.
242 $(LI If the callback takes one string argument, the option string
243 (without the leading dash(es)) is passed to the callback. After that,
244 the option string is considered handled and removed from the options
248 void main(string[] args)
250 uint verbosityLevel = 1;
251 void myHandler(string option)
253 if (option == "quiet")
259 assert(option == "verbose");
263 getopt(args, "verbose", &myHandler, "quiet", &myHandler);
269 $(LI If the callback takes two string arguments, the option string is
270 handled as an option with one argument, and parsed accordingly. The
271 option and its value are passed to the callback. After that, whatever
272 was passed to the callback is considered handled and removed from the
276 int main(string[] args)
278 uint verbosityLevel = 1;
279 bool handlerFailed = false;
280 void myHandler(string option, string value)
284 case "quiet": verbosityLevel = 0; break;
285 case "verbose": verbosityLevel = 2; break;
286 case "shouting": verbosityLevel = verbosityLevel.max; break;
288 stderr.writeln("Unknown verbosity level ", value);
289 handlerFailed = true;
293 getopt(args, "verbosity", &myHandler);
294 return handlerFailed ? 1 : 0;
301 Options_with_multiple_names:
302 Sometimes option synonyms are desirable, e.g. "--verbose",
303 "--loquacious", and "--garrulous" should have the same effect. Such
304 alternate option names can be included in the option specification,
305 using "|" as a separator:
309 getopt(args, "verbose|loquacious|garrulous", &verbose);
313 By default options are case-insensitive. You can change that behavior
314 by passing `getopt` the `caseSensitive` directive like this:
319 std.getopt.config.caseSensitive,
324 In the example above, "--foo" and "--bar" are recognized, but "--Foo", "--Bar",
325 "--FOo", "--bAr", etc. are rejected.
326 The directive is active until the end of `getopt`, or until the
327 converse directive `caseInsensitive` is encountered:
332 std.getopt.config.caseSensitive,
334 std.getopt.config.caseInsensitive,
338 The option "--Foo" is rejected due to $(D
339 std.getopt.config.caseSensitive), but not "--Bar", "--bAr"
340 etc. because the directive $(D
341 std.getopt.config.caseInsensitive) turned sensitivity off before
342 option "bar" was parsed.
344 Short_versus_long_options:
345 Traditionally, programs accepted single-letter options preceded by
346 only one dash (e.g. `-t`). `getopt` accepts such parameters
347 seamlessly. When used with a double-dash (e.g. `--t`), a
348 single-letter option behaves the same as a multi-letter option. When
349 used with a single dash, a single-letter option is accepted.
351 To set `timeout` to `5`, use either of the following: `--timeout=5`,
352 `--timeout 5`, `--t=5`, `--t 5`, `-t5`, or `-t 5`. Forms such as
353 `-timeout=5` will be not accepted.
355 For more details about short options, refer also to the next section.
358 Single-letter options can be bundled together, i.e. "-abc" is the same as
359 $(D "-a -b -c"). By default, this option is turned off. You can turn it on
360 with the `std.getopt.config.bundling` directive:
365 std.getopt.config.bundling,
370 In case you want to only enable bundling for some of the parameters,
371 bundling can be turned off with `std.getopt.config.noBundling`.
374 An option can be marked as required. If that option is not present in the
375 arguments an exception will be thrown.
380 std.getopt.config.required,
385 Only the option directly following `std.getopt.config.required` is
388 Passing_unrecognized_options_through:
389 If an application needs to do its own processing of whichever arguments
390 `getopt` did not understand, it can pass the
391 `std.getopt.config.passThrough` directive to `getopt`:
396 std.getopt.config.passThrough,
401 An unrecognized option such as "--baz" will be found untouched in
402 `args` after `getopt` returns.
404 Help_Information_Generation:
405 If an option string is followed by another string, this string serves as a
406 description for this option. The `getopt` function returns a struct of type
407 `GetoptResult`. This return value contains information about all passed options
408 as well a $(D bool GetoptResult.helpWanted) flag indicating whether information
409 about these options was requested. The `getopt` function always adds an option for
410 `--help|-h` to set the flag if the option is seen on the command line.
413 A lone double-dash terminates `getopt` gathering. It is used to
414 separate program options from other parameters (e.g., options to be passed
415 to another program). Invoking the example above with $(D "--foo -- --bar")
416 parses foo but leaves "--bar" in `args`. The double-dash itself is
417 removed from the argument array unless the `std.getopt.config.keepEndOfOptions`
420 GetoptResult
getopt(T
...)(ref string
[] args
, T opts
)
422 import std
.exception
: enforce
;
424 "Invalid arguments string passed: program name missing");
428 GetOptException excep
;
429 void[][string
] visitedLongOpts
, visitedShortOpts
;
430 getoptImpl(args
, cfg
, rslt
, excep
, visitedLongOpts
, visitedShortOpts
, opts
);
432 if (!rslt
.helpWanted
&& excep
!is null)
443 auto args
= ["prog", "--foo", "-b"];
447 auto rslt
= getopt(args
, "foo|f", "Some information about foo.", &foo
, "bar|b",
448 "Some help message about bar.", &bar
);
452 defaultGetoptPrinter("Some information about the program.",
458 Configuration options for `getopt`.
460 You can pass them to `getopt` in any position, except in between an option
461 string and its bound pointer.
464 /// Turn case sensitivity on
466 /// Turn case sensitivity off (default)
470 /// Turn bundling off (default)
472 /// Pass unrecognized arguments through
474 /// Signal unrecognized arguments as errors (default)
476 /// Stop at first argument that does not look like an option
477 stopOnFirstNonOption
,
478 /// Do not erase the endOfOptions separator from args
480 /// Make the next option a required option
484 /** The result of the `getopt` function.
486 `helpWanted` is set if the option `--help` or `-h` was passed to the option parser.
488 struct GetoptResult
{
489 bool helpWanted
; /// Flag indicating if help was requested
490 Option
[] options
; /// All possible options
493 /** Information about an option.
496 string optShort
; /// The short symbol for this option
497 string optLong
; /// The long symbol for this option
498 string help
; /// The description of this option
499 bool required
; /// If a option is required, not passing it will result in an error
502 private pure Option
splitAndGet(string opt
) @trusted nothrow
504 import std
.array
: split
;
505 auto sp
= split(opt
, "|");
509 ret.optShort
= "-" ~ (sp
[0].length
< sp
[1].length ?
511 ret.optLong
= "--" ~ (sp
[0].length
> sp
[1].length ?
514 else if (sp
[0].length
> 1)
516 ret.optLong
= "--" ~ sp
[0];
520 ret.optShort
= "-" ~ sp
[0];
528 auto oshort
= splitAndGet("f");
529 assert(oshort
.optShort
== "-f");
530 assert(oshort
.optLong
== "");
532 auto olong
= splitAndGet("foo");
533 assert(olong
.optShort
== "");
534 assert(olong
.optLong
== "--foo");
536 auto oshortlong
= splitAndGet("f|foo");
537 assert(oshortlong
.optShort
== "-f");
538 assert(oshortlong
.optLong
== "--foo");
540 auto olongshort
= splitAndGet("foo|f");
541 assert(olongshort
.optShort
== "-f");
542 assert(olongshort
.optLong
== "--foo");
546 This function verifies that the variadic parameters passed in getOpt
549 [config override], option, [description], receiver,
551 - config override: a config value, optional
552 - option: a string or a char
553 - description: a string, optional
554 - receiver: a pointer or a callable
556 private template optionValidator(A
...)
558 import std
.format
: format
;
560 enum fmt
= "getopt validator: %s (at position %d)";
561 enum isReceiver(T
) = is(T
== U
*, U
) ||
(is(T
== function)) ||
(is(T
== delegate));
562 enum isOptionStr(T
) = isSomeString
!T || isSomeChar
!T
;
567 static if (A
.length
> 0)
569 static if (isReceiver
!(A
[0]))
571 msg
= format(fmt
, "first argument must be a string or a config", 0);
573 else static if (!isOptionStr
!(A
[0]) && !is(A
[0] == config
))
575 msg
= format(fmt
, "invalid argument type: " ~ A
[0].stringof
, 0);
579 static foreach (i
; 1 .. A
.length
)
581 static if (!isReceiver
!(A
[i
]) && !isOptionStr
!(A
[i
]) &&
582 !(is(A
[i
] == config
)))
584 msg
= format(fmt
, "invalid argument type: " ~ A
[i
].stringof
, i
);
587 else static if (isReceiver
!(A
[i
]) && !isOptionStr
!(A
[i
-1]))
589 msg
= format(fmt
, "a receiver can not be preceeded by a receiver", i
);
592 else static if (i
> 1 && isOptionStr
!(A
[i
]) && isOptionStr
!(A
[i
-1])
593 && isSomeString
!(A
[i
-2]))
595 msg
= format(fmt
, "a string can not be preceeded by two strings", i
);
600 static if (!isReceiver
!(A
[$-1]) && !is(A
[$-1] == config
))
602 msg
= format(fmt
, "last argument must be a receiver or a config",
609 enum message
= validator
;
610 alias optionValidator
= message
;
619 alias F
= void function();
621 static assert(optionValidator
!(S
,P
) == "");
622 static assert(optionValidator
!(S
,F
) == "");
623 static assert(optionValidator
!(A
,P
) == "");
624 static assert(optionValidator
!(A
,F
) == "");
626 static assert(optionValidator
!(C
,S
,P
) == "");
627 static assert(optionValidator
!(C
,S
,F
) == "");
628 static assert(optionValidator
!(C
,A
,P
) == "");
629 static assert(optionValidator
!(C
,A
,F
) == "");
631 static assert(optionValidator
!(C
,S
,S
,P
) == "");
632 static assert(optionValidator
!(C
,S
,S
,F
) == "");
633 static assert(optionValidator
!(C
,A
,S
,P
) == "");
634 static assert(optionValidator
!(C
,A
,S
,F
) == "");
636 static assert(optionValidator
!(C
,S
,S
,P
) == "");
637 static assert(optionValidator
!(C
,S
,S
,P
,C
,S
,F
) == "");
638 static assert(optionValidator
!(C
,S
,P
,C
,S
,S
,F
) == "");
640 static assert(optionValidator
!(C
,A
,P
,A
,S
,F
) == "");
641 static assert(optionValidator
!(C
,A
,P
,C
,A
,S
,F
) == "");
643 static assert(optionValidator
!(P
,S
,S
) != "");
644 static assert(optionValidator
!(P
,P
,S
) != "");
645 static assert(optionValidator
!(P
,F
,S
,P
) != "");
646 static assert(optionValidator
!(C
,C
,S
) != "");
647 static assert(optionValidator
!(S
,S
,P
,S
,S
,P
,S
) != "");
648 static assert(optionValidator
!(S
,S
,P
,P
) != "");
649 static assert(optionValidator
!(S
,S
,S
,P
) != "");
651 static assert(optionValidator
!(C
,A
,S
,P
,C
,A
,F
) == "");
652 static assert(optionValidator
!(C
,A
,P
,C
,A
,S
,F
) == "");
655 // https://issues.dlang.org/show_bug.cgi?id=15914
658 import std
.exception
: assertThrown
;
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 import std
.uni
: toLower
;
689 static if (opts
.length
)
691 static if (is(typeof(opts
[0]) : config
))
693 // it's a configuration flag, act on it
694 setConfig(cfg
, opts
[0]);
695 return getoptImpl(args
, cfg
, rslt
, excep
, visitedLongOpts
,
696 visitedShortOpts
, opts
[1 .. $]);
700 // it's an option string
701 auto option
= to
!string(opts
[0]);
702 if (option
.length
== 0)
704 excep
= new GetOptException("An option name may not be an empty string", excep
);
707 Option optionHelp
= splitAndGet(option
);
708 optionHelp
.required
= cfg
.required
;
710 if (optionHelp
.optLong
.length
)
712 auto name
= optionHelp
.optLong
;
713 if (!cfg
.caseSensitive
)
714 name
= name
.toLower();
715 assert(name
!in visitedLongOpts
,
716 "Long option " ~ optionHelp
.optLong
~ " is multiply defined");
718 visitedLongOpts
[optionHelp
.optLong
] = [];
721 if (optionHelp
.optShort
.length
)
723 auto name
= optionHelp
.optShort
;
724 if (!cfg
.caseSensitive
)
725 name
= name
.toLower();
726 assert(name
!in visitedShortOpts
,
727 "Short option " ~ optionHelp
.optShort
728 ~ " is multiply defined");
730 visitedShortOpts
[optionHelp
.optShort
] = [];
733 static if (is(typeof(opts
[1]) : string
))
735 alias receiver
= opts
[2];
736 optionHelp
.help
= opts
[1];
737 immutable lowSliceIdx
= 3;
741 alias receiver
= opts
[1];
742 immutable lowSliceIdx
= 2;
745 rslt
.options
~= optionHelp
;
748 // Handle options of the form --blah+
749 if (option
.length
&& option
[$ - 1] == autoIncrementChar
)
751 option
= option
[0 .. $ - 1];
755 bool optWasHandled
= handleOption(option
, receiver
, args
, cfg
, incremental
);
757 if (cfg
.required
&& !optWasHandled
)
759 excep
= new GetOptException("Required option "
760 ~ option
~ " was not supplied", excep
);
762 cfg
.required
= false;
764 getoptImpl(args
, cfg
, rslt
, excep
, visitedLongOpts
,
765 visitedShortOpts
, opts
[lowSliceIdx
.. $]);
770 // no more options to look for, potentially some arguments left
771 for (size_t i
= 1; i
< args
.length
;)
774 if (endOfOptions
.length
&& a
== endOfOptions
)
776 // Consume the "--" if keepEndOfOptions is not specified
777 if (!cfg
.keepEndOfOptions
)
778 args
= args
.remove(i
);
781 if (a
.length
< 2 || a
[0] != optionChar
)
784 if (cfg
.stopOnFirstNonOption
) break;
788 if (a
== "--help" || a
== "-h")
790 rslt
.helpWanted
= true;
791 args
= args
.remove(i
);
794 if (!cfg
.passThrough
)
796 throw new GetOptException("Unrecognized option "~a
, excep
);
802 helpOpt
.optShort
= "-h";
803 helpOpt
.optLong
= "--help";
804 helpOpt
.help
= "This help information.";
805 rslt
.options
~= helpOpt
;
809 private bool handleOption(R
)(string option
, R receiver
, ref string
[] args
,
810 ref configuration cfg
, bool incremental
)
812 import std
.algorithm
.iteration
: map
, splitter
;
813 import std
.ascii
: isAlpha
;
814 import std
.conv
: text
, to
;
815 // Scan arguments looking for a match for this option
817 for (size_t i
= 1; i
< args
.length
; )
820 if (endOfOptions
.length
&& a
== endOfOptions
) break;
821 if (cfg
.stopOnFirstNonOption
&& (!a
.length || a
[0] != optionChar
))
823 // first non-option is end of options
826 // Unbundle bundled arguments if necessary
827 if (cfg
.bundling
&& a
.length
> 2 && a
[0] == optionChar
&&
831 foreach (j
, dchar c
; a
[1 .. $])
833 // If the character is not alpha, stop right there. This allows
834 // e.g. -j100 to work as "pass argument 100 to option -j".
839 expanded
~= a
[j
+ 1 .. $];
842 expanded
~= text(optionChar
, c
);
844 args
= args
[0 .. i
] ~ expanded
~ args
[i
+ 1 .. $];
849 if (!optMatch(a
, option
, val
, cfg
))
858 // from here on, commit to eat args[i]
859 // (and potentially args[i + 1] too, but that comes later)
860 args
= args
[0 .. i
] ~ args
[i
+ 1 .. $];
862 static if (is(typeof(*receiver
) == bool))
866 // parse '--b=true/false'
867 *receiver
= to
!(typeof(*receiver
))(val
);
871 // no argument means set it to true
877 import std
.exception
: enforce
;
878 // non-boolean option, which might include an argument
879 enum isCallbackWithLessThanTwoParameters
=
880 (is(typeof(receiver
) == delegate) ||
is(typeof(*receiver
) == function)) &&
881 !is(typeof(receiver("", "")));
882 if (!isCallbackWithLessThanTwoParameters
&& !(val
.length
) && !incremental
)
884 // Eat the next argument too. Check to make sure there's one
885 // to be eaten first, though.
886 enforce
!GetOptException(i
< args
.length
,
887 "Missing value for argument " ~ a
~ ".");
889 args
= args
[0 .. i
] ~ args
[i
+ 1 .. $];
891 static if (is(typeof(*receiver
) == enum))
893 *receiver
= to
!(typeof(*receiver
))(val
);
895 else static if (is(typeof(*receiver
) : real))
898 if (incremental
) ++*receiver
;
899 else *receiver
= to
!(typeof(*receiver
))(val
);
901 else static if (is(typeof(*receiver
) == string
))
904 *receiver
= to
!(typeof(*receiver
))(val
);
906 else static if (is(typeof(receiver
) == delegate) ||
907 is(typeof(*receiver
) == function))
909 static if (is(typeof(receiver("", "")) : void))
911 // option with argument
912 receiver(option
, val
);
914 else static if (is(typeof(receiver("")) : void))
916 alias RType
= typeof(receiver(""));
917 static assert(is(RType
: void),
918 "Invalid receiver return type " ~ RType
.stringof
);
919 // boolean-style receiver
924 alias RType
= typeof(receiver());
925 static assert(is(RType
: void),
926 "Invalid receiver return type " ~ RType
.stringof
);
927 // boolean-style receiver without argument
931 else static if (isArray
!(typeof(*receiver
)))
934 import std
.range
: ElementEncodingType
;
935 alias E
= ElementEncodingType
!(typeof(*receiver
));
939 *receiver
~= to
!E(val
);
943 foreach (elem
; val
.splitter(arraySep
).map
!(a
=> to
!E(a
))())
947 else static if (isAssociativeArray
!(typeof(*receiver
)))
950 alias K
= typeof(receiver
.keys
[0]);
951 alias V
= typeof(receiver
.values
[0]);
953 import std
.range
: only
;
954 import std
.string
: indexOf
;
955 import std
.typecons
: Tuple
, tuple
;
957 static Tuple
!(K
, V
) getter(string input
)
959 auto j
= indexOf(input
, assignChar
);
960 enforce
!GetOptException(j
!= -1, "Could not find '"
961 ~ to
!string(assignChar
) ~ "' in argument '" ~ input
~ "'.");
962 auto key
= input
[0 .. j
];
963 auto value
= input
[j
+ 1 .. $];
964 return tuple(to
!K(key
), to
!V(value
));
967 static void setHash(Range
)(R receiver
, Range range
)
969 foreach (k
, v
; range
.map
!getter
)
974 setHash(receiver
, val
.only
);
976 setHash(receiver
, val
.splitter(arraySep
));
979 static assert(false, "getopt does not know how to handle the type " ~ typeof(receiver
).stringof
);
986 // https://issues.dlang.org/show_bug.cgi?id=17574
989 import std
.algorithm
.searching
: startsWith
;
993 string
[string
] mapping
;
994 immutable as
= arraySep
;
998 string
[] args
= ["testProgram", "-m", "a=b,c=\"d,e,f\""];
999 args
.getopt("m", &mapping
);
1000 assert(false, "Exception not thrown");
1002 catch (GetOptException goe
)
1003 assert(goe
.msg
.startsWith("Could not find"));
1006 // https://issues.dlang.org/show_bug.cgi?id=5316 - arrays with arraySep
1012 scope (exit
) arraySep
= "";
1015 auto args
= ["program.name", "-nfoo,bar,baz"];
1016 getopt(args
, "name|n", &names
);
1017 assert(names
== ["foo", "bar", "baz"], to
!string(names
));
1020 args
= ["program.name", "-n", "foo,bar,baz"];
1021 getopt(args
, "name|n", &names
);
1022 assert(names
== ["foo", "bar", "baz"], to
!string(names
));
1025 args
= ["program.name", "--name=foo,bar,baz"];
1026 getopt(args
, "name|n", &names
);
1027 assert(names
== ["foo", "bar", "baz"], to
!string(names
));
1030 args
= ["program.name", "--name", "foo,bar,baz"];
1031 getopt(args
, "name|n", &names
);
1032 assert(names
== ["foo", "bar", "baz"], to
!string(names
));
1035 // https://issues.dlang.org/show_bug.cgi?id=5316 - associative arrays with arraySep
1041 scope (exit
) arraySep
= "";
1044 values
= values
.init
;
1045 auto args
= ["program.name", "-vfoo=0,bar=1,baz=2"];
1046 getopt(args
, "values|v", &values
);
1047 assert(values
== ["foo":0, "bar":1, "baz":2], to
!string(values
));
1049 values
= values
.init
;
1050 args
= ["program.name", "-v", "foo=0,bar=1,baz=2"];
1051 getopt(args
, "values|v", &values
);
1052 assert(values
== ["foo":0, "bar":1, "baz":2], to
!string(values
));
1054 values
= values
.init
;
1055 args
= ["program.name", "--values=foo=0,bar=1,baz=2"];
1056 getopt(args
, "values|t", &values
);
1057 assert(values
== ["foo":0, "bar":1, "baz":2], to
!string(values
));
1059 values
= values
.init
;
1060 args
= ["program.name", "--values", "foo=0,bar=1,baz=2"];
1061 getopt(args
, "values|v", &values
);
1062 assert(values
== ["foo":0, "bar":1, "baz":2], to
!string(values
));
1066 The option character (default '-').
1068 Defaults to '-' but it can be assigned to prior to calling `getopt`.
1070 dchar optionChar
= '-';
1073 The string that conventionally marks the end of all options (default '--').
1075 Defaults to "--" but can be assigned to prior to calling `getopt`. Assigning an
1076 empty string to `endOfOptions` effectively disables it.
1078 string endOfOptions
= "--";
1081 The assignment character used in options with parameters (default '=').
1083 Defaults to '=' but can be assigned to prior to calling `getopt`.
1085 dchar assignChar
= '=';
1088 When set to "", parameters to array and associative array receivers are
1089 treated as an individual argument. That is, only one argument is appended or
1090 inserted per appearance of the option switch. If `arraySep` is set to
1091 something else, then each parameter is first split by the separator, and the
1092 individual pieces are treated as arguments to the same option.
1094 Defaults to "" but can be assigned to prior to calling `getopt`.
1096 string arraySep
= "";
1098 private enum autoIncrementChar
= '+';
1100 private struct configuration
1102 import std
.bitmanip
: bitfields
;
1104 bool, "caseSensitive", 1,
1105 bool, "bundling", 1,
1106 bool, "passThrough", 1,
1107 bool, "stopOnFirstNonOption", 1,
1108 bool, "keepEndOfOptions", 1,
1109 bool, "required", 1,
1113 private bool optMatch(string arg
, scope string optPattern
, ref string value
,
1114 configuration cfg
) @safe
1116 import std
.algorithm
.iteration
: splitter
;
1117 import std
.string
: indexOf
;
1118 import std
.uni
: icmp
;
1119 //writeln("optMatch:\n ", arg, "\n ", optPattern, "\n ", value);
1120 //scope(success) writeln("optMatch result: ", value);
1121 if (arg
.length
< 2 || arg
[0] != optionChar
) return false;
1122 // yank the leading '-'
1124 immutable isLong
= arg
.length
> 1 && arg
[0] == optionChar
;
1125 //writeln("isLong: ", isLong);
1126 // yank the second '-' if present
1127 if (isLong
) arg
= arg
[1 .. $];
1128 immutable eqPos
= indexOf(arg
, assignChar
);
1129 if (isLong
&& eqPos
>= 0)
1131 // argument looks like --opt=value
1132 value
= arg
[eqPos
+ 1 .. $];
1133 arg
= arg
[0 .. eqPos
];
1137 if (!isLong
&& eqPos
== 1)
1139 // argument looks like -o=value
1140 value
= arg
[2 .. $];
1144 if (!isLong
&& !cfg
.bundling
)
1146 // argument looks like -ovalue and there's no bundling
1147 value
= arg
[1 .. $];
1152 // argument looks like --opt, or -oxyz with bundling
1156 //writeln("Arg: ", arg, " pattern: ", optPattern, " value: ", value);
1158 foreach (v
; splitter(optPattern
, "|"))
1160 //writeln("Trying variant: ", v, " against ", arg);
1161 if (arg
== v ||
(!cfg
.caseSensitive
&& icmp(arg
, v
) == 0))
1163 if (cfg
.bundling
&& !isLong
&& v
.length
== 1
1164 && indexOf(arg
, v
) >= 0)
1166 //writeln("success");
1173 private void setConfig(ref configuration cfg
, config option
) @safe pure nothrow @nogc
1175 final switch (option
)
1177 case config
.caseSensitive
: cfg
.caseSensitive
= true; break;
1178 case config
.caseInsensitive
: cfg
.caseSensitive
= false; break;
1179 case config
.bundling
: cfg
.bundling
= true; break;
1180 case config
.noBundling
: cfg
.bundling
= false; break;
1181 case config
.passThrough
: cfg
.passThrough
= true; break;
1182 case config
.noPassThrough
: cfg
.passThrough
= false; break;
1183 case config
.required
: cfg
.required
= true; break;
1184 case config
.stopOnFirstNonOption
:
1185 cfg
.stopOnFirstNonOption
= true; break;
1186 case config
.keepEndOfOptions
:
1187 cfg
.keepEndOfOptions
= true; break;
1194 import std
.math
.operations
: isClose
;
1197 string
[] args
= ["program.name", "--paranoid", "--paranoid", "--paranoid"];
1198 getopt(args
, "paranoid+", ¶noid
);
1199 assert(paranoid
== 5, to
!(string
)(paranoid
));
1201 enum Color
{ no
, yes
}
1203 args
= ["program.name", "--color=yes",];
1204 getopt(args
, "color", &color
);
1205 assert(color
, to
!(string
)(color
));
1208 args
= ["program.name", "--color", "yes",];
1209 getopt(args
, "color", &color
);
1210 assert(color
, to
!(string
)(color
));
1212 string data
= "file.dat";
1214 bool verbose
= false;
1215 args
= ["program.name", "--length=5", "--file", "dat.file", "--verbose"];
1220 "verbose", &verbose
);
1221 assert(args
.length
== 1);
1222 assert(data
== "dat.file");
1223 assert(length
== 5);
1227 string
[] outputFiles
;
1228 args
= ["program.name", "--output=myfile.txt", "--output", "yourfile.txt"];
1229 getopt(args
, "output", &outputFiles
);
1230 assert(outputFiles
.length
== 2
1231 && outputFiles
[0] == "myfile.txt" && outputFiles
[1] == "yourfile.txt");
1235 args
= ["program.name", "--output", "myfile.txt,yourfile.txt"];
1236 getopt(args
, "output", &outputFiles
);
1237 assert(outputFiles
.length
== 2
1238 && outputFiles
[0] == "myfile.txt" && outputFiles
[1] == "yourfile.txt");
1242 [["program.name", "--tune=alpha=0.5", "--tune", "beta=0.6"],
1243 ["program.name", "--tune=alpha=0.5,beta=0.6"],
1244 ["program.name", "--tune", "alpha=0.5,beta=0.6"]])
1247 double[string
] tuningParms
;
1248 getopt(testArgs
, "tune", &tuningParms
);
1249 assert(testArgs
.length
== 1);
1250 assert(tuningParms
.length
== 2);
1251 assert(isClose(tuningParms
["alpha"], 0.5));
1252 assert(isClose(tuningParms
["beta"], 0.6));
1256 uint verbosityLevel
= 1;
1257 void myHandler(string option
)
1259 if (option
== "quiet")
1265 assert(option
== "verbose");
1269 args
= ["program.name", "--quiet"];
1270 getopt(args
, "verbose", &myHandler
, "quiet", &myHandler
);
1271 assert(verbosityLevel
== 0);
1272 args
= ["program.name", "--verbose"];
1273 getopt(args
, "verbose", &myHandler
, "quiet", &myHandler
);
1274 assert(verbosityLevel
== 2);
1277 void myHandler2(string option
, string value
)
1279 assert(option
== "verbose");
1282 args
= ["program.name", "--verbose", "2"];
1283 getopt(args
, "verbose", &myHandler2
);
1284 assert(verbosityLevel
== 2);
1291 args
= ["program.name", "--verbose"];
1292 getopt(args
, "verbose", &myHandler3
);
1293 assert(verbosityLevel
== 2);
1296 args
= ["program.name", "--foo", "--bAr"];
1298 std
.getopt
.config
.caseSensitive
,
1299 std
.getopt
.config
.passThrough
,
1302 assert(args
[1] == "--bAr");
1304 // test stopOnFirstNonOption
1306 args
= ["program.name", "--foo", "nonoption", "--bar"];
1309 std
.getopt
.config
.stopOnFirstNonOption
,
1312 assert(foo
&& !bar
&& args
[1] == "nonoption" && args
[2] == "--bar");
1314 args
= ["program.name", "--foo", "nonoption", "--zab"];
1317 std
.getopt
.config
.stopOnFirstNonOption
,
1320 assert(foo
&& !bar
&& args
[1] == "nonoption" && args
[2] == "--zab");
1322 args
= ["program.name", "--fb1", "--fb2=true", "--tb1=false"];
1325 getopt(args
, "fb1", &fb1
, "fb2", &fb2
, "tb1", &tb1
);
1326 assert(fb1
&& fb2
&& !tb1
);
1328 // test keepEndOfOptions
1330 args
= ["program.name", "--foo", "nonoption", "--bar", "--", "--baz"];
1332 std
.getopt
.config
.keepEndOfOptions
,
1335 assert(args
== ["program.name", "nonoption", "--", "--baz"]);
1337 // Ensure old behavior without the keepEndOfOptions
1339 args
= ["program.name", "--foo", "nonoption", "--bar", "--", "--baz"];
1343 assert(args
== ["program.name", "nonoption", "--baz"]);
1345 // test function callbacks
1347 static class MyEx
: Exception
1349 this() { super(""); }
1350 this(string option
) { this(); this.option
= option
; }
1351 this(string option
, string value
) { this(option
); this.value
= value
; }
1357 static void myStaticHandler1() { throw new MyEx(); }
1358 args
= ["program.name", "--verbose"];
1359 try { getopt(args
, "verbose", &myStaticHandler1
); assert(0); }
1360 catch (MyEx ex
) { assert(ex
.option
is null && ex
.value
is null); }
1362 static void myStaticHandler2(string option
) { throw new MyEx(option
); }
1363 args
= ["program.name", "--verbose"];
1364 try { getopt(args
, "verbose", &myStaticHandler2
); assert(0); }
1365 catch (MyEx ex
) { assert(ex
.option
== "verbose" && ex
.value
is null); }
1367 static void myStaticHandler3(string option
, string value
) { throw new MyEx(option
, value
); }
1368 args
= ["program.name", "--verbose", "2"];
1369 try { getopt(args
, "verbose", &myStaticHandler3
); assert(0); }
1370 catch (MyEx ex
) { assert(ex
.option
== "verbose" && ex
.value
== "2"); }
1372 // check that GetOptException is thrown if the value is missing
1373 args
= ["program.name", "--verbose"];
1374 try { getopt(args
, "verbose", &myStaticHandler3
); assert(0); }
1375 catch (GetOptException e
) {}
1376 catch (Exception e
) { assert(0); }
1379 @safe unittest // @safe std.getopt.config option use
1382 string
[] args
= ["program", "--inc-x", "--inc-x"];
1384 std
.getopt
.config
.caseSensitive
,
1385 "inc-x", "Add one to x", delegate void() { x
++; });
1389 // https://issues.dlang.org/show_bug.cgi?id=2142
1392 bool f_linenum
, f_filename
;
1393 string
[] args
= [ "", "-nl" ];
1397 std
.getopt
.config
.bundling
,
1398 //std.getopt.config.caseSensitive,
1399 "linenum|l", &f_linenum
,
1400 "filename|n", &f_filename
1406 // https://issues.dlang.org/show_bug.cgi?id=6887
1410 string
[] args
= ["", "-pa"];
1411 getopt(args
, "p", &p
);
1412 assert(p
.length
== 1);
1413 assert(p
[0] == "a");
1416 // https://issues.dlang.org/show_bug.cgi?id=6888
1420 auto args
= ["", "-t", "a=1"];
1421 getopt(args
, "t", &foo
);
1422 assert(foo
== ["a":1]);
1425 // https://issues.dlang.org/show_bug.cgi?id=9583
1429 auto args
= ["prog", "--opt=123", "--", "--a", "--b", "--c"];
1430 getopt(args
, "opt", &opt
);
1431 assert(args
== ["prog", "--a", "--b", "--c"]);
1437 auto args
= ["prog", "-thello", "-dbar=baz"];
1438 getopt(args
, "t", &foo
, "d", &bar
);
1439 assert(foo
== "hello");
1440 assert(bar
== "bar=baz");
1442 // From https://issues.dlang.org/show_bug.cgi?id=5762
1444 args
= ["prog", "-a-0x12"];
1445 getopt(args
, config
.bundling
, "a|addr", &a
);
1446 assert(a
== "-0x12", a
);
1447 args
= ["prog", "--addr=-0x12"];
1448 getopt(args
, config
.bundling
, "a|addr", &a
);
1449 assert(a
== "-0x12");
1451 // From https://issues.dlang.org/show_bug.cgi?id=11764
1452 args
= ["main", "-test"];
1454 args
.getopt(config
.passThrough
, "opt", &opt
);
1455 assert(args
== ["main", "-test"]);
1457 // From https://issues.dlang.org/show_bug.cgi?id=15220
1458 args
= ["main", "-o=str"];
1460 args
.getopt("o", &o
);
1463 args
= ["main", "-o=str"];
1465 args
.getopt(config
.bundling
, "o", &o
);
1469 // https://issues.dlang.org/show_bug.cgi?id=5228
1473 import std
.exception
;
1475 auto args
= ["prog", "--foo=bar"];
1477 assertThrown
!GetOptException(getopt(args
, "abc", &abc
));
1479 args
= ["prog", "--abc=string"];
1480 assertThrown
!ConvException(getopt(args
, "abc", &abc
));
1483 // https://issues.dlang.org/show_bug.cgi?id=7693
1486 import std
.exception
;
1493 auto args
= ["prog", "--foo=barZZZ"];
1495 assertThrown(getopt(args
, "foo", &foo
));
1496 args
= ["prog", "--foo=bar"];
1497 assertNotThrown(getopt(args
, "foo", &foo
));
1498 args
= ["prog", "--foo", "barZZZ"];
1499 assertThrown(getopt(args
, "foo", &foo
));
1500 args
= ["prog", "--foo", "baz"];
1501 assertNotThrown(getopt(args
, "foo", &foo
));
1504 // Same as https://issues.dlang.org/show_bug.cgi?id=7693 only for `bool`
1507 import std
.exception
;
1509 auto args
= ["prog", "--foo=truefoobar"];
1511 assertThrown(getopt(args
, "foo", &foo
));
1512 args
= ["prog", "--foo"];
1513 getopt(args
, "foo", &foo
);
1520 auto args
= ["prog", "--foo"];
1521 getopt(args
, "foo", &foo
);
1529 auto args
= ["prog", "--foo", "-b"];
1530 getopt(args
, config
.caseInsensitive
,"foo|f", "Some foo", &foo
,
1531 config
.caseSensitive
, "bar|b", "Some bar", &bar
);
1540 auto args
= ["prog", "-b", "--foo", "-z"];
1541 getopt(args
, config
.caseInsensitive
, config
.required
, "foo|f", "Some foo",
1542 &foo
, config
.caseSensitive
, "bar|b", "Some bar", &bar
,
1543 config
.passThrough
);
1550 import std
.exception
;
1554 auto args
= ["prog", "-b", "-z"];
1555 assertThrown(getopt(args
, config
.caseInsensitive
, config
.required
, "foo|f",
1556 "Some foo", &foo
, config
.caseSensitive
, "bar|b", "Some bar", &bar
,
1557 config
.passThrough
));
1562 import std
.exception
;
1566 auto args
= ["prog", "--foo", "-z"];
1567 assertNotThrown(getopt(args
, config
.caseInsensitive
, config
.required
,
1568 "foo|f", "Some foo", &foo
, config
.caseSensitive
, "bar|b", "Some bar",
1569 &bar
, config
.passThrough
));
1577 auto args
= ["prog", "-f"];
1578 auto r
= getopt(args
, config
.caseInsensitive
, "help|f", "Some foo", &foo
);
1580 assert(!r
.helpWanted
);
1583 @safe unittest // implicit help option without config.passThrough
1585 string
[] args
= ["program", "--help"];
1586 auto r
= getopt(args
);
1587 assert(r
.helpWanted
);
1590 // std.getopt: implicit help option breaks the next argument
1591 // https://issues.dlang.org/show_bug.cgi?id=13316
1594 string
[] args
= ["program", "--help", "--", "something"];
1596 assert(args
== ["program", "something"]);
1598 args
= ["program", "--help", "--"];
1600 assert(args
== ["program"]);
1603 args
= ["program", "--help", "nonoption", "--option"];
1604 getopt(args
, config
.stopOnFirstNonOption
, "option", &b
);
1605 assert(args
== ["program", "nonoption", "--option"]);
1608 // std.getopt: endOfOptions broken when it doesn't look like an option
1609 // https://issues.dlang.org/show_bug.cgi?id=13317
1612 auto endOfOptionsBackup
= endOfOptions
;
1613 scope(exit
) endOfOptions
= endOfOptionsBackup
;
1614 endOfOptions
= "endofoptions";
1615 string
[] args
= ["program", "endofoptions", "--option"];
1617 getopt(args
, "option", &b
);
1619 assert(args
== ["program", "--option"]);
1622 // make std.getopt ready for DIP 1000
1623 // https://issues.dlang.org/show_bug.cgi?id=20480
1626 string
[] args
= ["test", "--foo", "42", "--bar", "BAR"];
1629 getopt(args
, "foo", &foo
, "bar", "bar help", &bar
);
1631 assert(bar
== "BAR");
1634 /** This function prints the passed `Option`s and text in an aligned manner on `stdout`.
1636 The passed text will be printed first, followed by a newline, then the short
1637 and long version of every option will be printed. The short and long version
1638 will be aligned to the longest option of every `Option` passed. If the option
1639 is required, then "Required:" will be printed after the long version of the
1640 `Option`. If a help message is present it will be printed next. The format is
1641 illustrated by this code:
1646 writefln("%*s %*s%s%s", lengthOfLongestShortOption, it.optShort,
1647 lengthOfLongestLongOption, it.optLong,
1648 it.required ? " Required: " : " ", it.help);
1653 text = The text to printed at the beginning of the help output.
1654 opt = The `Option` extracted from the `getopt` parameter.
1656 void defaultGetoptPrinter(string text
, Option
[] opt
) @safe
1658 import std
.stdio
: stdout
;
1659 // stdout global __gshared is trusted with a locked text writer
1660 auto w
= (() @trusted => stdout
.lockingTextWriter())();
1662 defaultGetoptFormatter(w
, text
, opt
);
1665 /** This function writes the passed text and `Option` into an output range
1666 in the manner described in the documentation of function
1667 `defaultGetoptPrinter`, unless the style option is used.
1670 output = The output range used to write the help information.
1671 text = The text to print at the beginning of the help output.
1672 opt = The `Option` extracted from the `getopt` parameter.
1673 style = The manner in which to display the output of each `Option.`
1675 void defaultGetoptFormatter(Output
)(Output output
, string text
, Option
[] opt
, string style
= "%*s %*s%*s%s\n")
1677 import std
.algorithm
.comparison
: min
, max
;
1678 import std
.format
.write
: formattedWrite
;
1680 output
.formattedWrite("%s\n", text
);
1683 bool hasRequired
= false;
1686 ls
= max(ls
, it
.optShort
.length
);
1687 ll
= max(ll
, it
.optLong
.length
);
1689 hasRequired
= hasRequired || it
.required
;
1692 string re
= " Required: ";
1696 output
.formattedWrite(style
, ls
, it
.optShort
, ll
, it
.optLong
,
1697 hasRequired ? re
.length
: 1, it
.required ? re
: " ", it
.help
);
1708 auto args
= ["prog", "--foo"];
1709 auto t
= getopt(args
, "foo|f", "Help", &a
);
1711 auto app
= appender
!string();
1712 defaultGetoptFormatter(app
, "Some Text", t
.options
);
1714 string helpMsg
= app
.data
;
1716 assert(helpMsg
.length
);
1717 assert(helpMsg
.count("\n") == 3, to
!string(helpMsg
.count("\n")) ~ " "
1719 assert(helpMsg
.indexOf("--foo") != -1);
1720 assert(helpMsg
.indexOf("-f") != -1);
1721 assert(helpMsg
.indexOf("-h") != -1);
1722 assert(helpMsg
.indexOf("--help") != -1);
1723 assert(helpMsg
.indexOf("Help") != -1);
1725 string wanted
= "Some Text\n-f --foo Help\n-h --help This help "
1727 assert(wanted
== helpMsg
);
1736 auto args
= ["prog", "--foo"];
1737 auto t
= getopt(args
, config
.required
, "foo|f", "Help", &a
);
1739 auto app
= appender
!string();
1740 defaultGetoptFormatter(app
, "Some Text", t
.options
);
1742 string helpMsg
= app
.data
;
1744 assert(helpMsg
.length
);
1745 assert(helpMsg
.count("\n") == 3, to
!string(helpMsg
.count("\n")) ~ " "
1747 assert(helpMsg
.indexOf("Required:") != -1);
1748 assert(helpMsg
.indexOf("--foo") != -1);
1749 assert(helpMsg
.indexOf("-f") != -1);
1750 assert(helpMsg
.indexOf("-h") != -1);
1751 assert(helpMsg
.indexOf("--help") != -1);
1752 assert(helpMsg
.indexOf("Help") != -1);
1754 string wanted
= "Some Text\n-f --foo Required: Help\n-h --help "
1755 ~ " This help information.\n";
1756 assert(wanted
== helpMsg
, helpMsg
~ wanted
);
1759 // https://issues.dlang.org/show_bug.cgi?id=14724
1763 auto args
= ["prog", "--help"];
1767 rslt
= getopt(args
, config
.required
, "foo|f", "bool a", &a
);
1771 enum errorMsg
= "If the request for help was passed required options" ~
1773 assert(false, errorMsg
);
1776 assert(rslt
.helpWanted
);
1779 // throw on duplicate options
1782 import core
.exception
: AssertError
;
1783 import std
.exception
: assertNotThrown
, assertThrown
;
1784 auto args
= ["prog", "--abc", "1"];
1786 assertThrown
!AssertError(getopt(args
, "abc", &abc
, "abc", &abc
));
1787 assertThrown
!AssertError(getopt(args
, "abc|a", &abc
, "def|a", &def
));
1788 assertNotThrown
!AssertError(getopt(args
, "abc", &abc
, "def", &def
));
1790 // https://issues.dlang.org/show_bug.cgi?id=23940
1791 assertThrown
!AssertError(getopt(args
,
1792 "abc", &abc
, "ABC", &def
));
1793 assertThrown
!AssertError(getopt(args
, config
.caseInsensitive
,
1794 "abc", &abc
, "ABC", &def
));
1795 assertNotThrown
!AssertError(getopt(args
, config
.caseSensitive
,
1796 "abc", &abc
, "ABC", &def
));
1799 // https://issues.dlang.org/show_bug.cgi?id=17327 repeated option use
1804 string
[] args
= ["program", "--num", "3"];
1805 getopt(args
, "n|num", &num
);
1808 args
= ["program", "--num", "3", "--num", "5"];
1809 getopt(args
, "n|num", &num
);
1812 args
= ["program", "--n", "3", "--num", "5", "-n", "-7"];
1813 getopt(args
, "n|num", &num
);
1816 void add1() { num
++; }
1817 void add2(string option
) { num
+= 2; }
1818 void addN(string option
, string value
)
1820 import std
.conv
: to
;
1821 num
+= value
.to
!long;
1825 args
= ["program", "--add1", "--add2", "--add1", "--add", "5", "--add2", "--add", "10"];
1827 "add1", "Add 1 to num", &add1
,
1828 "add2", "Add 2 to num", &add2
,
1829 "add", "Add N to num", &addN
,);
1833 args
= ["program", "--flag"];
1834 getopt(args
, "f|flag", "Boolean", &flag
);
1838 args
= ["program", "-f", "-f"];
1839 getopt(args
, "f|flag", "Boolean", &flag
);
1843 args
= ["program", "--flag=true", "--flag=false"];
1844 getopt(args
, "f|flag", "Boolean", &flag
);
1848 args
= ["program", "--flag=true", "--flag=false", "-f"];
1849 getopt(args
, "f|flag", "Boolean", &flag
);
1853 @system unittest // Delegates as callbacks
1855 alias TwoArgOptionHandler
= void delegate(string option
, string value
) @safe;
1857 TwoArgOptionHandler
makeAddNHandler(ref long dest
)
1859 void addN(ref long dest
, string n
)
1861 import std
.conv
: to
;
1865 return (option
, value
) => addN(dest
, value
);
1872 ["program", "--x-plus-1", "--x-plus-1", "--x-plus-5", "--x-plus-n", "10",
1873 "--y-plus-n", "25", "--y-plus-7", "--y-plus-n", "15", "--y-plus-3"];
1876 "x-plus-1", "Add one to x", delegate void() { x
+= 1; },
1877 "x-plus-5", "Add five to x", delegate void(string option
) { x
+= 5; },
1878 "x-plus-n", "Add NUM to x", makeAddNHandler(x
),
1879 "y-plus-7", "Add seven to y", delegate void() { y
+= 7; },
1880 "y-plus-3", "Add three to y", delegate void(string option
) { y
+= 3; },
1881 "y-plus-n", "Add NUM to x", makeAddNHandler(y
),);
1887 // Hyphens at the start of option values;
1888 // https://issues.dlang.org/show_bug.cgi?id=17650
1891 auto args
= ["program", "-m", "-5", "-n", "-50", "-c", "-", "-f", "-"];
1899 "m|mm", "integer", &m
,
1900 "n|nn", "integer", &n
,
1901 "c|cc", "character", &c
,
1902 "f|file", "filename or hyphen for stdin", &f
);
1910 // Hyphen at the option value;
1911 // https://issues.dlang.org/show_bug.cgi?id=22394
1914 auto args
= ["program", "-"];
1918 assert(args
== ["program", "-"]);
1928 auto args
= ["prog", "--foo"];
1929 auto t
= getopt(args
, "foo|f", "Help", &a
);
1931 auto app
= appender
!string();
1932 defaultGetoptFormatter(app
, "Some Text", t
.options
, "\t\t%*s %*s%*s\n%s\n");
1934 string helpMsg
= app
.data
;
1936 assert(helpMsg
.length
);
1937 assert(helpMsg
.count("\n") == 5, to
!string(helpMsg
.count("\n")) ~ " "
1939 assert(helpMsg
.indexOf("--foo") != -1);
1940 assert(helpMsg
.indexOf("-f") != -1);
1941 assert(helpMsg
.indexOf("-h") != -1);
1942 assert(helpMsg
.indexOf("--help") != -1);
1943 assert(helpMsg
.indexOf("Help") != -1);
1945 string wanted
= "Some Text\n\t\t-f --foo \nHelp\n\t\t-h --help \nThis help "
1947 assert(wanted
== helpMsg
);