Fix build on sparc64-linux-gnu.
[official-gcc.git] / libphobos / src / std / getopt.d
blob5beddcc23b4fa9bc1f4ad9fe86cb4ab0782c5a6e
1 // Written in the D programming language.
3 /**
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
11 enabled by default.
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)
29 module std.getopt;
31 import std.exception; // basicExceptionCtors
32 import std.traits;
34 /**
35 Thrown on one of the following conditions:
36 $(UL
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))));
51 /**
52 Parse and remove command line options from a string array.
54 Synopsis:
56 ---------
57 import std.getopt;
59 string data = "file.dat";
60 int length = 24;
61 bool verbose;
62 enum Color { no, yes };
63 Color color;
65 void main(string[] args)
67 auto helpInformation = getopt(
68 args,
69 "length", &length, // numeric
70 "file", &data, // string
71 "verbose", &verbose, // flag
72 "color", "Information about this color", &color); // enum
73 ...
75 if (helpInformation.helpWanted)
77 defaultGetoptPrinter("Some information about the program.",
78 helpInformation.options);
81 ---------
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)
101 is thrown.
103 Depending on the type of the pointer being bound, $(D getopt)
104 recognizes the following kinds of options:
106 $(OL
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
109 with an "=" sign:
111 ---------
112 bool verbose = false, debugging = true;
113 getopt(args, "verbose", &verbose, "debug", &debugging);
114 ---------
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
125 with an "=" sign:
127 ---------
128 uint timeout;
129 getopt(args, "timeout", &timeout);
130 ---------
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:
140 ---------
141 uint paranoid;
142 getopt(args, "paranoid+", &paranoid);
143 ---------
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:
156 ---------
157 enum Color { no, yes };
158 Color color; // default initialized to Color.no
159 getopt(args, "color", &color);
160 ---------
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
168 "=" sign:
170 ---------
171 string outputFile;
172 getopt(args, "output", &outputFile);
173 ---------
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:
184 ---------
185 string[] outputFiles;
186 getopt(args, "output", &outputFiles);
187 ---------
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:
195 ---------
196 string[] outputFiles;
197 arraySep = ","; // defaults to "", separation by whitespace
198 getopt(args, "output", &outputFiles);
199 ---------
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:
208 ---------
209 double[string] tuningParms;
210 getopt(args, "tune", &tuningParms);
211 ---------
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:
218 ---------
219 double[string] tuningParms;
220 arraySep = ","; // defaults to "", separation by whitespace
221 getopt(args, "tune", &tuningParms);
222 ---------
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.
235 $(UL
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
243 array.
245 ---------
246 void main(string[] args)
248 uint verbosityLevel = 1;
249 void myHandler(string option)
251 if (option == "quiet")
253 verbosityLevel = 0;
255 else
257 assert(option == "verbose");
258 verbosityLevel = 2;
261 getopt(args, "verbose", &myHandler, "quiet", &myHandler);
263 ---------
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
271 list.
273 ---------
274 int main(string[] args)
276 uint verbosityLevel = 1;
277 bool handlerFailed = false;
278 void myHandler(string option, string value)
280 switch (value)
282 case "quiet": verbosityLevel = 0; break;
283 case "verbose": verbosityLevel = 2; break;
284 case "shouting": verbosityLevel = verbosityLevel.max; break;
285 default :
286 stderr.writeln("Unknown verbosity level ", value);
287 handlerFailed = true;
288 break;
291 getopt(args, "verbosity", &myHandler);
292 return handlerFailed ? 1 : 0;
294 ---------
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:
305 ---------
306 bool verbose;
307 getopt(args, "verbose|loquacious|garrulous", &verbose);
308 ---------
310 Case:
311 By default options are case-insensitive. You can change that behavior
312 by passing $(D getopt) the $(D caseSensitive) directive like this:
314 ---------
315 bool foo, bar;
316 getopt(args,
317 std.getopt.config.caseSensitive,
318 "foo", &foo,
319 "bar", &bar);
320 ---------
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:
327 ---------
328 bool foo, bar;
329 getopt(args,
330 std.getopt.config.caseSensitive,
331 "foo", &foo,
332 std.getopt.config.caseInsensitive,
333 "bar", &bar);
334 ---------
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 "=":
351 ---------
352 uint timeout;
353 getopt(args, "timeout|t", &timeout);
354 ---------
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.
362 Bundling:
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:
367 ---------
368 bool foo, bar;
369 getopt(args,
370 std.getopt.config.bundling,
371 "foo|f", &foo,
372 "bar|b", &bar);
373 ---------
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).
378 Required:
379 An option can be marked as required. If that option is not present in the
380 arguments an exception will be thrown.
382 ---------
383 bool foo, bar;
384 getopt(args,
385 std.getopt.config.required,
386 "foo|f", &foo,
387 "bar|b", &bar);
388 ---------
390 Only the option directly following $(D std.getopt.config.required) is
391 required.
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):
398 ---------
399 bool foo, bar;
400 getopt(args,
401 std.getopt.config.passThrough,
402 "foo", &foo,
403 "bar", &bar);
404 ---------
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.
417 Options_Terminator:
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)
423 directive is given.
425 GetoptResult getopt(T...)(ref string[] args, T opts)
427 import std.exception : enforce;
428 enforce(args.length,
429 "Invalid arguments string passed: program name missing");
430 configuration cfg;
431 GetoptResult rslt;
433 GetOptException excep;
434 void[][string] visitedLongOpts, visitedShortOpts;
435 getoptImpl(args, cfg, rslt, excep, visitedLongOpts, visitedShortOpts, opts);
437 if (!rslt.helpWanted && excep !is null)
439 throw excep;
442 return rslt;
446 @system unittest
448 auto args = ["prog", "--foo", "-b"];
450 bool foo;
451 bool bar;
452 auto rslt = getopt(args, "foo|f", "Some information about foo.", &foo, "bar|b",
453 "Some help message about bar.", &bar);
455 if (rslt.helpWanted)
457 defaultGetoptPrinter("Some information about the program.",
458 rslt.options);
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.
468 enum config {
469 /// Turn case sensitivity on
470 caseSensitive,
471 /// Turn case sensitivity off (default)
472 caseInsensitive,
473 /// Turn bundling on
474 bundling,
475 /// Turn bundling off (default)
476 noBundling,
477 /// Pass unrecognized arguments through
478 passThrough,
479 /// Signal unrecognized arguments as errors (default)
480 noPassThrough,
481 /// Stop at first argument that does not look like an option
482 stopOnFirstNonOption,
483 /// Do not erase the endOfOptions separator from args
484 keepEndOfOptions,
485 /// Make the next option a required option
486 required
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.
500 struct 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, "|");
511 Option ret;
512 if (sp.length > 1)
514 ret.optShort = "-" ~ (sp[0].length < sp[1].length ?
515 sp[0] : sp[1]);
516 ret.optLong = "--" ~ (sp[0].length > sp[1].length ?
517 sp[0] : sp[1]);
519 else if (sp[0].length > 1)
521 ret.optLong = "--" ~ sp[0];
523 else
525 ret.optShort = "-" ~ sp[0];
528 return ret;
531 @safe unittest
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
552 follow this pattern:
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;
570 auto validator()
572 string msg;
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);
589 break;
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);
594 break;
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);
600 break;
603 static if (!isReceiver!(A[$-1]) && !is(A[$-1] == config))
605 msg = format(fmt, "last argument must be a receiver or a config",
606 A.length -1);
609 return msg;
611 enum message = validator;
612 alias optionValidator = message;
615 @safe pure unittest
617 alias P = void*;
618 alias S = string;
619 alias A = char;
620 alias C = config;
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
659 bool opt;
660 string[] args = ["program", "-a"];
661 getopt(args, config.passThrough, 'a', &opt);
662 assert(opt);
663 opt = false;
664 args = ["program", "-a"];
665 getopt(args, 'a', &opt);
666 assert(opt);
667 opt = false;
668 args = ["program", "-a"];
669 getopt(args, 'a', "help string", &opt);
670 assert(opt);
671 opt = false;
672 args = ["program", "-a"];
673 getopt(args, config.caseSensitive, 'a', "help string", &opt);
674 assert(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 .. $]);
697 else
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);
704 return;
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;
732 else
734 auto receiver = opts[1];
735 immutable lowSliceIdx = 2;
738 rslt.options ~= optionHelp;
740 bool incremental;
741 // Handle options of the form --blah+
742 if (option.length && option[$ - 1] == autoIncrementChar)
744 option = option[0 .. $ - 1];
745 incremental = true;
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 .. $]);
761 else
763 // no more options to look for, potentially some arguments left
764 for (size_t i = 1; i < args.length;)
766 auto a = args[i];
767 if (endOfOptions.length && a == endOfOptions)
769 // Consume the "--" if keepEndOfOptions is not specified
770 if (!cfg.keepEndOfOptions)
771 args = args.remove(i);
772 break;
774 if (!a.length || a[0] != optionChar)
776 // not an option
777 if (cfg.stopOnFirstNonOption) break;
778 ++i;
779 continue;
781 if (a == "--help" || a == "-h")
783 rslt.helpWanted = true;
784 args = args.remove(i);
785 continue;
787 if (!cfg.passThrough)
789 throw new GetOptException("Unrecognized option "~a, excep);
791 ++i;
794 Option helpOpt;
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
809 bool ret = false;
810 for (size_t i = 1; i < args.length; )
812 auto a = args[i];
813 if (endOfOptions.length && a == endOfOptions) break;
814 if (cfg.stopOnFirstNonOption && (!a.length || a[0] != optionChar))
816 // first non-option is end of options
817 break;
819 // Unbundle bundled arguments if necessary
820 if (cfg.bundling && a.length > 2 && a[0] == optionChar &&
821 a[1] != optionChar)
823 string[] expanded;
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".
828 if (!isAlpha(c))
830 if (c == '=')
831 j++;
832 expanded ~= a[j + 1 .. $];
833 break;
835 expanded ~= text(optionChar, c);
837 args = args[0 .. i] ~ expanded ~ args[i + 1 .. $];
838 continue;
841 string val;
842 if (!optMatch(a, option, val, cfg))
844 ++i;
845 continue;
848 ret = true;
850 // found it
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))
857 if (val.length)
859 // parse '--b=true/false'
860 *receiver = to!(typeof(*receiver))(val);
862 else
864 // no argument means set it to true
865 *receiver = true;
868 else
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 ~ ".");
882 val = args[i];
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))
891 // numeric receiver
892 if (incremental) ++*receiver;
893 else *receiver = to!(typeof(*receiver))(val);
895 else static if (is(typeof(*receiver) == string))
897 // string receiver
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
912 receiver(option);
914 else
916 static assert(is(typeof(receiver()) : void));
917 // boolean-style receiver without argument
918 receiver();
921 else static if (isArray!(typeof(*receiver)))
923 // array receiver
924 import std.range : ElementEncodingType;
925 alias E = ElementEncodingType!(typeof(*receiver));
927 if (arraySep == "")
929 *receiver ~= to!E(val);
931 else
933 foreach (elem; val.splitter(arraySep).map!(a => to!E(a))())
934 *receiver ~= elem;
937 else static if (isAssociativeArray!(typeof(*receiver)))
939 // hash 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)
960 (*receiver)[k] = v;
963 if (arraySep == "")
964 setHash(receiver, val.only);
965 else
966 setHash(receiver, val.splitter(arraySep));
968 else
969 static assert(false, "getopt does not know how to handle the type " ~ typeof(receiver).stringof);
973 return ret;
976 // 17574
977 @system unittest
979 import std.algorithm.searching : startsWith;
983 string[string] mapping;
984 immutable as = arraySep;
985 arraySep = ",";
986 scope (exit)
987 arraySep = as;
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
997 @system unittest
999 import std.conv;
1001 arraySep = ",";
1002 scope (exit) arraySep = "";
1004 string[] names;
1005 auto args = ["program.name", "-nfoo,bar,baz"];
1006 getopt(args, "name|n", &names);
1007 assert(names == ["foo", "bar", "baz"], to!string(names));
1009 names = names.init;
1010 args = ["program.name", "-n", "foo,bar,baz"];
1011 getopt(args, "name|n", &names);
1012 assert(names == ["foo", "bar", "baz"], to!string(names));
1014 names = names.init;
1015 args = ["program.name", "--name=foo,bar,baz"];
1016 getopt(args, "name|n", &names);
1017 assert(names == ["foo", "bar", "baz"], to!string(names));
1019 names = names.init;
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
1026 @system unittest
1028 import std.conv;
1030 arraySep = ",";
1031 scope (exit) arraySep = "";
1033 int[string] values;
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;
1090 mixin(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,
1097 ubyte, "", 2));
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 '-'
1110 arg = arg[1 .. $];
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];
1122 else
1124 if (!isLong && eqPos == 1)
1126 // argument looks like -o=value
1127 value = arg[2 .. $];
1128 arg = arg[0 .. 1];
1130 else
1131 if (!isLong && !cfg.bundling)
1133 // argument looks like -ovalue and there's no bundling
1134 value = arg[1 .. $];
1135 arg = arg[0 .. 1];
1137 else
1139 // argument looks like --opt, or -oxyz with bundling
1140 value = null;
1143 //writeln("Arg: ", arg, " pattern: ", optPattern, " value: ", value);
1144 // Split the option
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))
1150 return true;
1151 if (cfg.bundling && !isLong && v.length == 1
1152 && indexOf(arg, v) >= 0)
1154 //writeln("success");
1155 return true;
1158 return false;
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;
1179 @system unittest
1181 import std.conv;
1182 import std.math;
1184 uint paranoid = 2;
1185 string[] args = ["program.name", "--paranoid", "--paranoid", "--paranoid"];
1186 getopt(args, "paranoid+", &paranoid);
1187 assert(paranoid == 5, to!(string)(paranoid));
1189 enum Color { no, yes }
1190 Color color;
1191 args = ["program.name", "--color=yes",];
1192 getopt(args, "color", &color);
1193 assert(color, to!(string)(color));
1195 color = Color.no;
1196 args = ["program.name", "--color", "yes",];
1197 getopt(args, "color", &color);
1198 assert(color, to!(string)(color));
1200 string data = "file.dat";
1201 int length = 24;
1202 bool verbose = false;
1203 args = ["program.name", "--length=5", "--file", "dat.file", "--verbose"];
1204 getopt(
1205 args,
1206 "length", &length,
1207 "file", &data,
1208 "verbose", &verbose);
1209 assert(args.length == 1);
1210 assert(data == "dat.file");
1211 assert(length == 5);
1212 assert(verbose);
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");
1221 outputFiles = [];
1222 arraySep = ",";
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");
1227 arraySep = "";
1229 foreach (testArgs;
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"]])
1234 arraySep = ",";
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));
1241 arraySep = "";
1244 uint verbosityLevel = 1;
1245 void myHandler(string option)
1247 if (option == "quiet")
1249 verbosityLevel = 0;
1251 else
1253 assert(option == "verbose");
1254 verbosityLevel = 2;
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);
1264 verbosityLevel = 1;
1265 void myHandler2(string option, string value)
1267 assert(option == "verbose");
1268 verbosityLevel = 2;
1270 args = ["program.name", "--verbose", "2"];
1271 getopt(args, "verbose", &myHandler2);
1272 assert(verbosityLevel == 2);
1274 verbosityLevel = 1;
1275 void myHandler3()
1277 verbosityLevel = 2;
1279 args = ["program.name", "--verbose"];
1280 getopt(args, "verbose", &myHandler3);
1281 assert(verbosityLevel == 2);
1283 bool foo, bar;
1284 args = ["program.name", "--foo", "--bAr"];
1285 getopt(args,
1286 std.getopt.config.caseSensitive,
1287 std.getopt.config.passThrough,
1288 "foo", &foo,
1289 "bar", &bar);
1290 assert(args[1] == "--bAr");
1292 // test stopOnFirstNonOption
1294 args = ["program.name", "--foo", "nonoption", "--bar"];
1295 foo = bar = false;
1296 getopt(args,
1297 std.getopt.config.stopOnFirstNonOption,
1298 "foo", &foo,
1299 "bar", &bar);
1300 assert(foo && !bar && args[1] == "nonoption" && args[2] == "--bar");
1302 args = ["program.name", "--foo", "nonoption", "--zab"];
1303 foo = bar = false;
1304 getopt(args,
1305 std.getopt.config.stopOnFirstNonOption,
1306 "foo", &foo,
1307 "bar", &bar);
1308 assert(foo && !bar && args[1] == "nonoption" && args[2] == "--zab");
1310 args = ["program.name", "--fb1", "--fb2=true", "--tb1=false"];
1311 bool fb1, fb2;
1312 bool tb1 = true;
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"];
1319 getopt(args,
1320 std.getopt.config.keepEndOfOptions,
1321 "foo", &foo,
1322 "bar", &bar);
1323 assert(args == ["program.name", "nonoption", "--", "--baz"]);
1325 // Ensure old behavior without the keepEndOfOptions
1327 args = ["program.name", "--foo", "nonoption", "--bar", "--", "--baz"];
1328 getopt(args,
1329 "foo", &foo,
1330 "bar", &bar);
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; }
1341 string option;
1342 string 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
1363 long x = 0;
1364 string[] args = ["program", "--inc-x", "--inc-x"];
1365 getopt(args,
1366 std.getopt.config.caseSensitive,
1367 "inc-x", "Add one to x", delegate void() { x++; });
1368 assert(x == 2);
1371 @system unittest
1373 // From bugzilla 2142
1374 bool f_linenum, f_filename;
1375 string[] args = [ "", "-nl" ];
1376 getopt
1378 args,
1379 std.getopt.config.bundling,
1380 //std.getopt.config.caseSensitive,
1381 "linenum|l", &f_linenum,
1382 "filename|n", &f_filename
1384 assert(f_linenum);
1385 assert(f_filename);
1388 @system unittest
1390 // From bugzilla 6887
1391 string[] p;
1392 string[] args = ["", "-pa"];
1393 getopt(args, "p", &p);
1394 assert(p.length == 1);
1395 assert(p[0] == "a");
1398 @system unittest
1400 // From bugzilla 6888
1401 int[string] foo;
1402 auto args = ["", "-t", "a=1"];
1403 getopt(args, "t", &foo);
1404 assert(foo == ["a":1]);
1407 @system unittest
1409 // From bugzilla 9583
1410 int opt;
1411 auto args = ["prog", "--opt=123", "--", "--a", "--b", "--c"];
1412 getopt(args, "opt", &opt);
1413 assert(args == ["prog", "--a", "--b", "--c"]);
1416 @system unittest
1418 string foo, bar;
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
1425 string a;
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"];
1435 bool opt;
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"];
1441 string o;
1442 args.getopt("o", &o);
1443 assert(o == "str");
1445 args = ["main", "-o=str"];
1446 o = null;
1447 args.getopt(config.bundling, "o", &o);
1448 assert(o == "str");
1451 @system unittest // 5228
1453 import std.conv;
1454 import std.exception;
1456 auto args = ["prog", "--foo=bar"];
1457 int abc;
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;
1468 enum Foo {
1469 bar,
1473 auto args = ["prog", "--foo=barZZZ"];
1474 Foo foo;
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"];
1489 bool foo;
1490 assertThrown(getopt(args, "foo", &foo));
1491 args = ["prog", "--foo"];
1492 getopt(args, "foo", &foo);
1493 assert(foo);
1496 @system unittest
1498 bool foo;
1499 auto args = ["prog", "--foo"];
1500 getopt(args, "foo", &foo);
1501 assert(foo);
1504 @system unittest
1506 bool foo;
1507 bool bar;
1508 auto args = ["prog", "--foo", "-b"];
1509 getopt(args, config.caseInsensitive,"foo|f", "Some foo", &foo,
1510 config.caseSensitive, "bar|b", "Some bar", &bar);
1511 assert(foo);
1512 assert(bar);
1515 @system unittest
1517 bool foo;
1518 bool 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);
1523 assert(foo);
1524 assert(bar);
1527 @system unittest
1529 import std.exception;
1531 bool foo;
1532 bool bar;
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));
1539 @system unittest
1541 import std.exception;
1543 bool foo;
1544 bool bar;
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));
1549 assert(foo);
1550 assert(!bar);
1553 @system unittest
1555 bool foo;
1556 auto args = ["prog", "-f"];
1557 auto r = getopt(args, config.caseInsensitive, "help|f", "Some foo", &foo);
1558 assert(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
1570 @system unittest
1572 string[] args = ["program", "--help", "--", "something"];
1573 getopt(args);
1574 assert(args == ["program", "something"]);
1576 args = ["program", "--help", "--"];
1577 getopt(args);
1578 assert(args == ["program"]);
1580 bool b;
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
1587 @system unittest
1589 auto endOfOptionsBackup = endOfOptions;
1590 scope(exit) endOfOptions = endOfOptionsBackup;
1591 endOfOptions = "endofoptions";
1592 string[] args = ["program", "endofoptions", "--option"];
1593 bool b = false;
1594 getopt(args, "option", &b);
1595 assert(!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:
1608 ------------
1609 foreach (it; opt)
1611 writefln("%*s %*s%s%s", lengthOfLongestShortOption, it.optShort,
1612 lengthOfLongestLongOption, it.optLong,
1613 it.required ? " Required: " : " ", it.help);
1615 ------------
1617 Params:
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).
1632 Params:
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);
1644 size_t ls, ll;
1645 bool hasRequired = false;
1646 foreach (it; opt)
1648 ls = max(ls, it.optShort.length);
1649 ll = max(ll, it.optLong.length);
1651 hasRequired = hasRequired || it.required;
1654 string re = " Required: ";
1656 foreach (it; opt)
1658 output.formattedWrite("%*s %*s%*s%s\n", ls, it.optShort, ll, it.optLong,
1659 hasRequired ? re.length : 1, it.required ? re : " ", it.help);
1663 @system unittest
1665 import std.conv;
1667 import std.array;
1668 import std.string;
1669 bool a;
1670 auto args = ["prog", "--foo"];
1671 auto t = getopt(args, "foo|f", "Help", &a);
1672 string s;
1673 auto app = appender!string();
1674 defaultGetoptFormatter(app, "Some Text", t.options);
1676 string helpMsg = app.data;
1677 //writeln(helpMsg);
1678 assert(helpMsg.length);
1679 assert(helpMsg.count("\n") == 3, to!string(helpMsg.count("\n")) ~ " "
1680 ~ helpMsg);
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 "
1688 ~ "information.\n";
1689 assert(wanted == helpMsg);
1692 @system unittest
1694 import std.array ;
1695 import std.conv;
1696 import std.string;
1697 bool a;
1698 auto args = ["prog", "--foo"];
1699 auto t = getopt(args, config.required, "foo|f", "Help", &a);
1700 string s;
1701 auto app = appender!string();
1702 defaultGetoptFormatter(app, "Some Text", t.options);
1704 string helpMsg = app.data;
1705 //writeln(helpMsg);
1706 assert(helpMsg.length);
1707 assert(helpMsg.count("\n") == 3, to!string(helpMsg.count("\n")) ~ " "
1708 ~ helpMsg);
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
1723 bool a;
1724 auto args = ["prog", "--help"];
1725 GetoptResult rslt;
1728 rslt = getopt(args, config.required, "foo|f", "bool a", &a);
1730 catch (Exception e)
1732 enum errorMsg = "If the request for help was passed required options" ~
1733 "must not be set.";
1734 assert(false, errorMsg);
1737 assert(rslt.helpWanted);
1740 // throw on duplicate options
1741 @system unittest
1743 import core.exception;
1744 auto args = ["prog", "--abc", "1"];
1745 int abc, def;
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
1753 long num = 0;
1755 string[] args = ["program", "--num", "3"];
1756 getopt(args, "n|num", &num);
1757 assert(num == 3);
1759 args = ["program", "--num", "3", "--num", "5"];
1760 getopt(args, "n|num", &num);
1761 assert(num == 5);
1763 args = ["program", "--n", "3", "--num", "5", "-n", "-7"];
1764 getopt(args, "n|num", &num);
1765 assert(num == -7);
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;
1775 num = 0;
1776 args = ["program", "--add1", "--add2", "--add1", "--add", "5", "--add2", "--add", "10"];
1777 getopt(args,
1778 "add1", "Add 1 to num", &add1,
1779 "add2", "Add 2 to num", &add2,
1780 "add", "Add N to num", &addN,);
1781 assert(num == 21);
1783 bool flag = false;
1784 args = ["program", "--flag"];
1785 getopt(args, "f|flag", "Boolean", &flag);
1786 assert(flag);
1788 flag = false;
1789 args = ["program", "-f", "-f"];
1790 getopt(args, "f|flag", "Boolean", &flag);
1791 assert(flag);
1793 flag = false;
1794 args = ["program", "--flag=true", "--flag=false"];
1795 getopt(args, "f|flag", "Boolean", &flag);
1796 assert(!flag);
1798 flag = false;
1799 args = ["program", "--flag=true", "--flag=false", "-f"];
1800 getopt(args, "f|flag", "Boolean", &flag);
1801 assert(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;
1813 dest += n.to!long;
1816 return (option, value) => addN(dest, value);
1819 long x = 0;
1820 long y = 0;
1822 string[] args =
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"];
1826 getopt(args,
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),);
1834 assert(x == 17);
1835 assert(y == 50);
1838 @system unittest // Hyphens at the start of option values; Issue 17650
1840 auto args = ["program", "-m", "-5", "-n", "-50", "-c", "-", "-f", "-"];
1842 int m;
1843 int n;
1844 char c;
1845 string f;
1847 getopt(args,
1848 "m|mm", "integer", &m,
1849 "n|nn", "integer", &n,
1850 "c|cc", "character", &c,
1851 "f|file", "filename or hyphen for stdin", &f);
1853 assert(m == -5);
1854 assert(n == -50);
1855 assert(c == '-');
1856 assert(f == "-");