d: Merge upstream dmd, druntime 26f049fb26, phobos 330d6a4fd.
[official-gcc.git] / libphobos / src / std / getopt.d
blobcb97eebe31bc3e2c0fadd66cdc50c15557b6f198
1 // Written in the D programming language.
3 /**
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
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
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)
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 `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))));
52 /**
53 Parse and remove command line options from a string array.
55 Synopsis:
57 ---------
58 import std.getopt;
60 string data = "file.dat";
61 int length = 24;
62 bool verbose;
63 enum Color { no, yes };
64 Color color;
66 void main(string[] args)
68 auto helpInformation = getopt(
69 args,
70 "length", &length, // numeric
71 "file", &data, // string
72 "verbose", &verbose, // flag
73 "color", "Information about this color", &color); // enum
74 ...
76 if (helpInformation.helpWanted)
78 defaultGetoptPrinter("Some information about the program.",
79 helpInformation.options);
82 ---------
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:
107 $(OL
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
110 with an "=" sign:
112 ---------
113 bool verbose = false, debugging = true;
114 getopt(args, "verbose", &verbose, "debug", &debugging);
115 ---------
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
121 `--debugging=false`.
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
126 with an "=" sign:
128 ---------
129 uint timeout;
130 getopt(args, "timeout", &timeout);
131 ---------
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:
141 ---------
142 uint paranoid;
143 getopt(args, "paranoid+", &paranoid);
144 ---------
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:
157 ---------
158 enum Color { no, yes };
159 Color color; // default initialized to Color.no
160 getopt(args, "color", &color);
161 ---------
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
169 "=" sign:
171 ---------
172 string outputFile;
173 getopt(args, "output", &outputFile);
174 ---------
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:
185 ---------
186 string[] outputFiles;
187 getopt(args, "output", &outputFiles);
188 ---------
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
195 one parameter.
197 ---------
198 string[] outputFiles;
199 arraySep = ","; // defaults to "", meaning one element per parameter
200 getopt(args, "output", &outputFiles);
201 ---------
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:
210 ---------
211 double[string] tuningParms;
212 getopt(args, "tune", &tuningParms);
213 ---------
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:
220 ---------
221 double[string] tuningParms;
222 arraySep = ","; // defaults to "", meaning one element per parameter
223 getopt(args, "tune", &tuningParms);
224 ---------
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.
237 $(UL
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
245 array.
247 ---------
248 void main(string[] args)
250 uint verbosityLevel = 1;
251 void myHandler(string option)
253 if (option == "quiet")
255 verbosityLevel = 0;
257 else
259 assert(option == "verbose");
260 verbosityLevel = 2;
263 getopt(args, "verbose", &myHandler, "quiet", &myHandler);
265 ---------
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
273 list.
275 ---------
276 int main(string[] args)
278 uint verbosityLevel = 1;
279 bool handlerFailed = false;
280 void myHandler(string option, string value)
282 switch (value)
284 case "quiet": verbosityLevel = 0; break;
285 case "verbose": verbosityLevel = 2; break;
286 case "shouting": verbosityLevel = verbosityLevel.max; break;
287 default :
288 stderr.writeln("Unknown verbosity level ", value);
289 handlerFailed = true;
290 break;
293 getopt(args, "verbosity", &myHandler);
294 return handlerFailed ? 1 : 0;
296 ---------
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:
307 ---------
308 bool verbose;
309 getopt(args, "verbose|loquacious|garrulous", &verbose);
310 ---------
312 Case:
313 By default options are case-insensitive. You can change that behavior
314 by passing `getopt` the `caseSensitive` directive like this:
316 ---------
317 bool foo, bar;
318 getopt(args,
319 std.getopt.config.caseSensitive,
320 "foo", &foo,
321 "bar", &bar);
322 ---------
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:
329 ---------
330 bool foo, bar;
331 getopt(args,
332 std.getopt.config.caseSensitive,
333 "foo", &foo,
334 std.getopt.config.caseInsensitive,
335 "bar", &bar);
336 ---------
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.
357 Bundling:
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:
362 ---------
363 bool foo, bar;
364 getopt(args,
365 std.getopt.config.bundling,
366 "foo|f", &foo,
367 "bar|b", &bar);
368 ---------
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`.
373 Required:
374 An option can be marked as required. If that option is not present in the
375 arguments an exception will be thrown.
377 ---------
378 bool foo, bar;
379 getopt(args,
380 std.getopt.config.required,
381 "foo|f", &foo,
382 "bar|b", &bar);
383 ---------
385 Only the option directly following `std.getopt.config.required` is
386 required.
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`:
393 ---------
394 bool foo, bar;
395 getopt(args,
396 std.getopt.config.passThrough,
397 "foo", &foo,
398 "bar", &bar);
399 ---------
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.
412 Options_Terminator:
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`
418 directive is given.
420 GetoptResult getopt(T...)(ref string[] args, T opts)
422 import std.exception : enforce;
423 enforce(args.length,
424 "Invalid arguments string passed: program name missing");
425 configuration cfg;
426 GetoptResult rslt;
428 GetOptException excep;
429 void[][string] visitedLongOpts, visitedShortOpts;
430 getoptImpl(args, cfg, rslt, excep, visitedLongOpts, visitedShortOpts, opts);
432 if (!rslt.helpWanted && excep !is null)
434 throw excep;
437 return rslt;
441 @safe unittest
443 auto args = ["prog", "--foo", "-b"];
445 bool foo;
446 bool bar;
447 auto rslt = getopt(args, "foo|f", "Some information about foo.", &foo, "bar|b",
448 "Some help message about bar.", &bar);
450 if (rslt.helpWanted)
452 defaultGetoptPrinter("Some information about the program.",
453 rslt.options);
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.
463 enum config {
464 /// Turn case sensitivity on
465 caseSensitive,
466 /// Turn case sensitivity off (default)
467 caseInsensitive,
468 /// Turn bundling on
469 bundling,
470 /// Turn bundling off (default)
471 noBundling,
472 /// Pass unrecognized arguments through
473 passThrough,
474 /// Signal unrecognized arguments as errors (default)
475 noPassThrough,
476 /// Stop at first argument that does not look like an option
477 stopOnFirstNonOption,
478 /// Do not erase the endOfOptions separator from args
479 keepEndOfOptions,
480 /// Make the next option a required option
481 required
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.
495 struct 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, "|");
506 Option ret;
507 if (sp.length > 1)
509 ret.optShort = "-" ~ (sp[0].length < sp[1].length ?
510 sp[0] : sp[1]);
511 ret.optLong = "--" ~ (sp[0].length > sp[1].length ?
512 sp[0] : sp[1]);
514 else if (sp[0].length > 1)
516 ret.optLong = "--" ~ sp[0];
518 else
520 ret.optShort = "-" ~ sp[0];
523 return ret;
526 @safe unittest
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
547 follow this pattern:
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;
564 auto validator()
566 string msg;
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);
577 else
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);
585 goto end;
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);
590 goto end;
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);
596 goto end;
600 static if (!isReceiver!(A[$-1]) && !is(A[$-1] == config))
602 msg = format(fmt, "last argument must be a receiver or a config",
603 A.length -1);
606 end:
607 return msg;
609 enum message = validator;
610 alias optionValidator = message;
613 @safe pure unittest
615 alias P = void*;
616 alias S = string;
617 alias A = char;
618 alias C = config;
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
656 @safe unittest
658 import std.exception : assertThrown;
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 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 .. $]);
698 else
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);
705 return;
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;
739 else
741 alias receiver = opts[1];
742 immutable lowSliceIdx = 2;
745 rslt.options ~= optionHelp;
747 bool incremental;
748 // Handle options of the form --blah+
749 if (option.length && option[$ - 1] == autoIncrementChar)
751 option = option[0 .. $ - 1];
752 incremental = true;
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 .. $]);
768 else
770 // no more options to look for, potentially some arguments left
771 for (size_t i = 1; i < args.length;)
773 auto a = args[i];
774 if (endOfOptions.length && a == endOfOptions)
776 // Consume the "--" if keepEndOfOptions is not specified
777 if (!cfg.keepEndOfOptions)
778 args = args.remove(i);
779 break;
781 if (a.length < 2 || a[0] != optionChar)
783 // not an option
784 if (cfg.stopOnFirstNonOption) break;
785 ++i;
786 continue;
788 if (a == "--help" || a == "-h")
790 rslt.helpWanted = true;
791 args = args.remove(i);
792 continue;
794 if (!cfg.passThrough)
796 throw new GetOptException("Unrecognized option "~a, excep);
798 ++i;
801 Option helpOpt;
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
816 bool ret = false;
817 for (size_t i = 1; i < args.length; )
819 auto a = args[i];
820 if (endOfOptions.length && a == endOfOptions) break;
821 if (cfg.stopOnFirstNonOption && (!a.length || a[0] != optionChar))
823 // first non-option is end of options
824 break;
826 // Unbundle bundled arguments if necessary
827 if (cfg.bundling && a.length > 2 && a[0] == optionChar &&
828 a[1] != optionChar)
830 string[] expanded;
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".
835 if (!isAlpha(c))
837 if (c == '=')
838 j++;
839 expanded ~= a[j + 1 .. $];
840 break;
842 expanded ~= text(optionChar, c);
844 args = args[0 .. i] ~ expanded ~ args[i + 1 .. $];
845 continue;
848 string val;
849 if (!optMatch(a, option, val, cfg))
851 ++i;
852 continue;
855 ret = true;
857 // found it
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))
864 if (val.length)
866 // parse '--b=true/false'
867 *receiver = to!(typeof(*receiver))(val);
869 else
871 // no argument means set it to true
872 *receiver = true;
875 else
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 ~ ".");
888 val = args[i];
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))
897 // numeric receiver
898 if (incremental) ++*receiver;
899 else *receiver = to!(typeof(*receiver))(val);
901 else static if (is(typeof(*receiver) == string))
903 // string receiver
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
920 receiver(option);
922 else
924 alias RType = typeof(receiver());
925 static assert(is(RType : void),
926 "Invalid receiver return type " ~ RType.stringof);
927 // boolean-style receiver without argument
928 receiver();
931 else static if (isArray!(typeof(*receiver)))
933 // array receiver
934 import std.range : ElementEncodingType;
935 alias E = ElementEncodingType!(typeof(*receiver));
937 if (arraySep == "")
939 *receiver ~= to!E(val);
941 else
943 foreach (elem; val.splitter(arraySep).map!(a => to!E(a))())
944 *receiver ~= elem;
947 else static if (isAssociativeArray!(typeof(*receiver)))
949 // hash 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)
970 (*receiver)[k] = v;
973 if (arraySep == "")
974 setHash(receiver, val.only);
975 else
976 setHash(receiver, val.splitter(arraySep));
978 else
979 static assert(false, "getopt does not know how to handle the type " ~ typeof(receiver).stringof);
983 return ret;
986 // https://issues.dlang.org/show_bug.cgi?id=17574
987 @safe unittest
989 import std.algorithm.searching : startsWith;
993 string[string] mapping;
994 immutable as = arraySep;
995 arraySep = ",";
996 scope (exit)
997 arraySep = as;
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
1007 @safe unittest
1009 import std.conv;
1011 arraySep = ",";
1012 scope (exit) arraySep = "";
1014 string[] names;
1015 auto args = ["program.name", "-nfoo,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", "-n", "foo,bar,baz"];
1021 getopt(args, "name|n", &names);
1022 assert(names == ["foo", "bar", "baz"], to!string(names));
1024 names = names.init;
1025 args = ["program.name", "--name=foo,bar,baz"];
1026 getopt(args, "name|n", &names);
1027 assert(names == ["foo", "bar", "baz"], to!string(names));
1029 names = names.init;
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
1036 @safe unittest
1038 import std.conv;
1040 arraySep = ",";
1041 scope (exit) arraySep = "";
1043 int[string] values;
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;
1103 mixin(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,
1110 ubyte, "", 2));
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 '-'
1123 arg = arg[1 .. $];
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];
1135 else
1137 if (!isLong && eqPos == 1)
1139 // argument looks like -o=value
1140 value = arg[2 .. $];
1141 arg = arg[0 .. 1];
1143 else
1144 if (!isLong && !cfg.bundling)
1146 // argument looks like -ovalue and there's no bundling
1147 value = arg[1 .. $];
1148 arg = arg[0 .. 1];
1150 else
1152 // argument looks like --opt, or -oxyz with bundling
1153 value = null;
1156 //writeln("Arg: ", arg, " pattern: ", optPattern, " value: ", value);
1157 // Split the option
1158 foreach (v; splitter(optPattern, "|"))
1160 //writeln("Trying variant: ", v, " against ", arg);
1161 if (arg == v || (!cfg.caseSensitive && icmp(arg, v) == 0))
1162 return true;
1163 if (cfg.bundling && !isLong && v.length == 1
1164 && indexOf(arg, v) >= 0)
1166 //writeln("success");
1167 return true;
1170 return false;
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;
1191 @safe unittest
1193 import std.conv;
1194 import std.math.operations : isClose;
1196 uint paranoid = 2;
1197 string[] args = ["program.name", "--paranoid", "--paranoid", "--paranoid"];
1198 getopt(args, "paranoid+", &paranoid);
1199 assert(paranoid == 5, to!(string)(paranoid));
1201 enum Color { no, yes }
1202 Color color;
1203 args = ["program.name", "--color=yes",];
1204 getopt(args, "color", &color);
1205 assert(color, to!(string)(color));
1207 color = Color.no;
1208 args = ["program.name", "--color", "yes",];
1209 getopt(args, "color", &color);
1210 assert(color, to!(string)(color));
1212 string data = "file.dat";
1213 int length = 24;
1214 bool verbose = false;
1215 args = ["program.name", "--length=5", "--file", "dat.file", "--verbose"];
1216 getopt(
1217 args,
1218 "length", &length,
1219 "file", &data,
1220 "verbose", &verbose);
1221 assert(args.length == 1);
1222 assert(data == "dat.file");
1223 assert(length == 5);
1224 assert(verbose);
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");
1233 outputFiles = [];
1234 arraySep = ",";
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");
1239 arraySep = "";
1241 foreach (testArgs;
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"]])
1246 arraySep = ",";
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));
1253 arraySep = "";
1256 uint verbosityLevel = 1;
1257 void myHandler(string option)
1259 if (option == "quiet")
1261 verbosityLevel = 0;
1263 else
1265 assert(option == "verbose");
1266 verbosityLevel = 2;
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);
1276 verbosityLevel = 1;
1277 void myHandler2(string option, string value)
1279 assert(option == "verbose");
1280 verbosityLevel = 2;
1282 args = ["program.name", "--verbose", "2"];
1283 getopt(args, "verbose", &myHandler2);
1284 assert(verbosityLevel == 2);
1286 verbosityLevel = 1;
1287 void myHandler3()
1289 verbosityLevel = 2;
1291 args = ["program.name", "--verbose"];
1292 getopt(args, "verbose", &myHandler3);
1293 assert(verbosityLevel == 2);
1295 bool foo, bar;
1296 args = ["program.name", "--foo", "--bAr"];
1297 getopt(args,
1298 std.getopt.config.caseSensitive,
1299 std.getopt.config.passThrough,
1300 "foo", &foo,
1301 "bar", &bar);
1302 assert(args[1] == "--bAr");
1304 // test stopOnFirstNonOption
1306 args = ["program.name", "--foo", "nonoption", "--bar"];
1307 foo = bar = false;
1308 getopt(args,
1309 std.getopt.config.stopOnFirstNonOption,
1310 "foo", &foo,
1311 "bar", &bar);
1312 assert(foo && !bar && args[1] == "nonoption" && args[2] == "--bar");
1314 args = ["program.name", "--foo", "nonoption", "--zab"];
1315 foo = bar = false;
1316 getopt(args,
1317 std.getopt.config.stopOnFirstNonOption,
1318 "foo", &foo,
1319 "bar", &bar);
1320 assert(foo && !bar && args[1] == "nonoption" && args[2] == "--zab");
1322 args = ["program.name", "--fb1", "--fb2=true", "--tb1=false"];
1323 bool fb1, fb2;
1324 bool tb1 = true;
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"];
1331 getopt(args,
1332 std.getopt.config.keepEndOfOptions,
1333 "foo", &foo,
1334 "bar", &bar);
1335 assert(args == ["program.name", "nonoption", "--", "--baz"]);
1337 // Ensure old behavior without the keepEndOfOptions
1339 args = ["program.name", "--foo", "nonoption", "--bar", "--", "--baz"];
1340 getopt(args,
1341 "foo", &foo,
1342 "bar", &bar);
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; }
1353 string option;
1354 string 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
1381 long x = 0;
1382 string[] args = ["program", "--inc-x", "--inc-x"];
1383 getopt(args,
1384 std.getopt.config.caseSensitive,
1385 "inc-x", "Add one to x", delegate void() { x++; });
1386 assert(x == 2);
1389 // https://issues.dlang.org/show_bug.cgi?id=2142
1390 @safe unittest
1392 bool f_linenum, f_filename;
1393 string[] args = [ "", "-nl" ];
1394 getopt
1396 args,
1397 std.getopt.config.bundling,
1398 //std.getopt.config.caseSensitive,
1399 "linenum|l", &f_linenum,
1400 "filename|n", &f_filename
1402 assert(f_linenum);
1403 assert(f_filename);
1406 // https://issues.dlang.org/show_bug.cgi?id=6887
1407 @safe unittest
1409 string[] p;
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
1417 @safe unittest
1419 int[string] foo;
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
1426 @safe unittest
1428 int opt;
1429 auto args = ["prog", "--opt=123", "--", "--a", "--b", "--c"];
1430 getopt(args, "opt", &opt);
1431 assert(args == ["prog", "--a", "--b", "--c"]);
1434 @safe unittest
1436 string foo, bar;
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
1443 string a;
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"];
1453 bool opt;
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"];
1459 string o;
1460 args.getopt("o", &o);
1461 assert(o == "str");
1463 args = ["main", "-o=str"];
1464 o = null;
1465 args.getopt(config.bundling, "o", &o);
1466 assert(o == "str");
1469 // https://issues.dlang.org/show_bug.cgi?id=5228
1470 @safe unittest
1472 import std.conv;
1473 import std.exception;
1475 auto args = ["prog", "--foo=bar"];
1476 int abc;
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
1484 @safe unittest
1486 import std.exception;
1488 enum Foo {
1489 bar,
1493 auto args = ["prog", "--foo=barZZZ"];
1494 Foo foo;
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`
1505 @safe unittest
1507 import std.exception;
1509 auto args = ["prog", "--foo=truefoobar"];
1510 bool foo;
1511 assertThrown(getopt(args, "foo", &foo));
1512 args = ["prog", "--foo"];
1513 getopt(args, "foo", &foo);
1514 assert(foo);
1517 @safe unittest
1519 bool foo;
1520 auto args = ["prog", "--foo"];
1521 getopt(args, "foo", &foo);
1522 assert(foo);
1525 @safe unittest
1527 bool foo;
1528 bool bar;
1529 auto args = ["prog", "--foo", "-b"];
1530 getopt(args, config.caseInsensitive,"foo|f", "Some foo", &foo,
1531 config.caseSensitive, "bar|b", "Some bar", &bar);
1532 assert(foo);
1533 assert(bar);
1536 @safe unittest
1538 bool foo;
1539 bool 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);
1544 assert(foo);
1545 assert(bar);
1548 @safe unittest
1550 import std.exception;
1552 bool foo;
1553 bool bar;
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));
1560 @safe unittest
1562 import std.exception;
1564 bool foo;
1565 bool bar;
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));
1570 assert(foo);
1571 assert(!bar);
1574 @safe unittest
1576 bool foo;
1577 auto args = ["prog", "-f"];
1578 auto r = getopt(args, config.caseInsensitive, "help|f", "Some foo", &foo);
1579 assert(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
1592 @safe unittest
1594 string[] args = ["program", "--help", "--", "something"];
1595 getopt(args);
1596 assert(args == ["program", "something"]);
1598 args = ["program", "--help", "--"];
1599 getopt(args);
1600 assert(args == ["program"]);
1602 bool b;
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
1610 @safe unittest
1612 auto endOfOptionsBackup = endOfOptions;
1613 scope(exit) endOfOptions = endOfOptionsBackup;
1614 endOfOptions = "endofoptions";
1615 string[] args = ["program", "endofoptions", "--option"];
1616 bool b = false;
1617 getopt(args, "option", &b);
1618 assert(!b);
1619 assert(args == ["program", "--option"]);
1622 // make std.getopt ready for DIP 1000
1623 // https://issues.dlang.org/show_bug.cgi?id=20480
1624 @safe unittest
1626 string[] args = ["test", "--foo", "42", "--bar", "BAR"];
1627 int foo;
1628 string bar;
1629 getopt(args, "foo", &foo, "bar", "bar help", &bar);
1630 assert(foo == 42);
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:
1643 ------------
1644 foreach (it; opt)
1646 writefln("%*s %*s%s%s", lengthOfLongestShortOption, it.optShort,
1647 lengthOfLongestLongOption, it.optLong,
1648 it.required ? " Required: " : " ", it.help);
1650 ------------
1652 Params:
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.
1669 Params:
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);
1682 size_t ls, ll;
1683 bool hasRequired = false;
1684 foreach (it; opt)
1686 ls = max(ls, it.optShort.length);
1687 ll = max(ll, it.optLong.length);
1689 hasRequired = hasRequired || it.required;
1692 string re = " Required: ";
1694 foreach (it; opt)
1696 output.formattedWrite(style, ls, it.optShort, ll, it.optLong,
1697 hasRequired ? re.length : 1, it.required ? re : " ", it.help);
1701 @safe unittest
1703 import std.conv;
1705 import std.array;
1706 import std.string;
1707 bool a;
1708 auto args = ["prog", "--foo"];
1709 auto t = getopt(args, "foo|f", "Help", &a);
1710 string s;
1711 auto app = appender!string();
1712 defaultGetoptFormatter(app, "Some Text", t.options);
1714 string helpMsg = app.data;
1715 //writeln(helpMsg);
1716 assert(helpMsg.length);
1717 assert(helpMsg.count("\n") == 3, to!string(helpMsg.count("\n")) ~ " "
1718 ~ helpMsg);
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 "
1726 ~ "information.\n";
1727 assert(wanted == helpMsg);
1730 @safe unittest
1732 import std.array ;
1733 import std.conv;
1734 import std.string;
1735 bool a;
1736 auto args = ["prog", "--foo"];
1737 auto t = getopt(args, config.required, "foo|f", "Help", &a);
1738 string s;
1739 auto app = appender!string();
1740 defaultGetoptFormatter(app, "Some Text", t.options);
1742 string helpMsg = app.data;
1743 //writeln(helpMsg);
1744 assert(helpMsg.length);
1745 assert(helpMsg.count("\n") == 3, to!string(helpMsg.count("\n")) ~ " "
1746 ~ helpMsg);
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
1760 @safe unittest
1762 bool a;
1763 auto args = ["prog", "--help"];
1764 GetoptResult rslt;
1767 rslt = getopt(args, config.required, "foo|f", "bool a", &a);
1769 catch (Exception e)
1771 enum errorMsg = "If the request for help was passed required options" ~
1772 "must not be set.";
1773 assert(false, errorMsg);
1776 assert(rslt.helpWanted);
1779 // throw on duplicate options
1780 @system unittest
1782 import core.exception : AssertError;
1783 import std.exception : assertNotThrown, assertThrown;
1784 auto args = ["prog", "--abc", "1"];
1785 int abc, def;
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
1800 @safe unittest
1802 long num = 0;
1804 string[] args = ["program", "--num", "3"];
1805 getopt(args, "n|num", &num);
1806 assert(num == 3);
1808 args = ["program", "--num", "3", "--num", "5"];
1809 getopt(args, "n|num", &num);
1810 assert(num == 5);
1812 args = ["program", "--n", "3", "--num", "5", "-n", "-7"];
1813 getopt(args, "n|num", &num);
1814 assert(num == -7);
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;
1824 num = 0;
1825 args = ["program", "--add1", "--add2", "--add1", "--add", "5", "--add2", "--add", "10"];
1826 getopt(args,
1827 "add1", "Add 1 to num", &add1,
1828 "add2", "Add 2 to num", &add2,
1829 "add", "Add N to num", &addN,);
1830 assert(num == 21);
1832 bool flag = false;
1833 args = ["program", "--flag"];
1834 getopt(args, "f|flag", "Boolean", &flag);
1835 assert(flag);
1837 flag = false;
1838 args = ["program", "-f", "-f"];
1839 getopt(args, "f|flag", "Boolean", &flag);
1840 assert(flag);
1842 flag = false;
1843 args = ["program", "--flag=true", "--flag=false"];
1844 getopt(args, "f|flag", "Boolean", &flag);
1845 assert(!flag);
1847 flag = false;
1848 args = ["program", "--flag=true", "--flag=false", "-f"];
1849 getopt(args, "f|flag", "Boolean", &flag);
1850 assert(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;
1862 dest += n.to!long;
1865 return (option, value) => addN(dest, value);
1868 long x = 0;
1869 long y = 0;
1871 string[] args =
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"];
1875 getopt(args,
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),);
1883 assert(x == 17);
1884 assert(y == 50);
1887 // Hyphens at the start of option values;
1888 // https://issues.dlang.org/show_bug.cgi?id=17650
1889 @safe unittest
1891 auto args = ["program", "-m", "-5", "-n", "-50", "-c", "-", "-f", "-"];
1893 int m;
1894 int n;
1895 char c;
1896 string f;
1898 getopt(args,
1899 "m|mm", "integer", &m,
1900 "n|nn", "integer", &n,
1901 "c|cc", "character", &c,
1902 "f|file", "filename or hyphen for stdin", &f);
1904 assert(m == -5);
1905 assert(n == -50);
1906 assert(c == '-');
1907 assert(f == "-");
1910 // Hyphen at the option value;
1911 // https://issues.dlang.org/show_bug.cgi?id=22394
1912 @safe unittest
1914 auto args = ["program", "-"];
1916 getopt(args);
1918 assert(args == ["program", "-"]);
1921 @safe unittest
1923 import std.conv;
1925 import std.array;
1926 import std.string;
1927 bool a;
1928 auto args = ["prog", "--foo"];
1929 auto t = getopt(args, "foo|f", "Help", &a);
1930 string s;
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;
1935 //writeln(helpMsg);
1936 assert(helpMsg.length);
1937 assert(helpMsg.count("\n") == 5, to!string(helpMsg.count("\n")) ~ " "
1938 ~ helpMsg);
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 "
1946 ~ "information.\n";
1947 assert(wanted == helpMsg);